neutron-12.1.1/0000775000175000017500000000000013553660157013323 5ustar zuulzuul00000000000000neutron-12.1.1/bindep.txt0000664000175000017500000000202413553660046015320 0ustar zuulzuul00000000000000# This file contains runtime (non-python) dependencies # More info at: http://docs.openstack.org/infra/bindep/readme.html # tools/misc-sanity-checks.sh validates .po[t] files gettext [test] # cffi (required by oslo.privsep) and PyNaCL (required by paramiko) libffi-dev [platform:dpkg] libffi-devel [platform:rpm] # MySQL and PostgreSQL databases since some jobs are set up in # OpenStack infra that need these like # periodic-neutron-py27-with-oslo-master and # periodic-neutron-py35-with-neutron-lib-master. haproxy libmysqlclient-dev [platform:dpkg test] mysql [platform:rpm test] mysql-client [platform:dpkg test] mysql-devel [platform:rpm test] mysql-server [test] postgresql [test] postgresql-client [platform:dpkg test] postgresql-devel [platform:rpm test] postgresql-server [platform:rpm test] # Neutron's test-requirements requires tempest which requires paramiko # which requires cryptography which requires ssl. libssl-dev [platform:dpkg] openssl-devel [platform:rpm !platform:suse] libopenssl-devel [platform:suse !platform:rpm] neutron-12.1.1/PKG-INFO0000664000175000017500000000302713553660157014422 0ustar zuulzuul00000000000000Metadata-Version: 1.1 Name: neutron Version: 12.1.1 Summary: OpenStack Networking Home-page: https://docs.openstack.org/neutron/latest/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/neutron.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on Welcome! ======== To learn more about neutron: * Documentation: https://docs.openstack.org * Features: https://specs.openstack.org/openstack/neutron-specs * Defects: https://launchpad.net/neutron Get in touch via `email `_. Use [Neutron] in your subject. To learn how to contribute: CONTRIBUTING.rst Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 neutron-12.1.1/README.rst0000664000175000017500000000114213553660047015006 0ustar zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/neutron.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on Welcome! ======== To learn more about neutron: * Documentation: https://docs.openstack.org * Features: https://specs.openstack.org/openstack/neutron-specs * Defects: https://launchpad.net/neutron Get in touch via `email `_. Use [Neutron] in your subject. To learn how to contribute: CONTRIBUTING.rst neutron-12.1.1/bin/0000775000175000017500000000000013553660156014072 5ustar zuulzuul00000000000000neutron-12.1.1/bin/neutron-rootwrap-xen-dom00000775000175000017500000001252613553660046021016 0ustar zuulzuul00000000000000#!/usr/bin/env python # Copyright (c) 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Neutron root wrapper for dom0. Executes networking commands in dom0. The XenAPI plugin is responsible determining whether a command is safe to execute. """ from __future__ import print_function from six.moves import configparser as ConfigParser from oslo_serialization import jsonutils as json import os import select import sys from os_xenapi.client import XenAPI RC_UNAUTHORIZED = 99 RC_NOCOMMAND = 98 RC_BADCONFIG = 97 RC_XENAPI_ERROR = 96 def parse_args(): # Split arguments, require at least a command exec_name = sys.argv.pop(0) # argv[0] required; path to conf file if len(sys.argv) < 2: sys.stderr.write("%s: No command specified" % exec_name) sys.exit(RC_NOCOMMAND) config_file = sys.argv.pop(0) user_args = sys.argv[:] return exec_name, config_file, user_args def _xenapi_section_name(config): sections = [sect for sect in config.sections() if sect.lower() == "xenapi"] if len(sections) == 1: return sections[0] sys.stderr.write("Multiple [xenapi] sections or no [xenapi] section found!") sys.exit(RC_BADCONFIG) def load_configuration(exec_name, config_file): config = ConfigParser.RawConfigParser() config.read(config_file) try: exec_dirs = config.get("DEFAULT", "exec_dirs").split(",") filters_path = config.get("DEFAULT", "filters_path").split(",") section = _xenapi_section_name(config) url = config.get(section, "xenapi_connection_url") username = config.get(section, "xenapi_connection_username") password = config.get(section, "xenapi_connection_password") except ConfigParser.Error: sys.stderr.write("%s: Incorrect configuration file: %s" % (exec_name, config_file)) sys.exit(RC_BADCONFIG) if not url or not password: msg = ("%s: Must specify xenapi_connection_url, " "xenapi_connection_username (optionally), and " "xenapi_connection_password in %s") % (exec_name, config_file) sys.stderr.write(msg) sys.exit(RC_BADCONFIG) return dict( filters_path=filters_path, url=url, username=username, password=password, exec_dirs=exec_dirs, ) def filter_command(exec_name, filters_path, user_args, exec_dirs): # Add ../ to sys.path to allow running from branch possible_topdir = os.path.normpath(os.path.join(os.path.abspath(exec_name), os.pardir, os.pardir)) if os.path.exists(os.path.join(possible_topdir, "neutron", "__init__.py")): sys.path.insert(0, possible_topdir) from oslo_rootwrap import wrapper # Execute command if it matches any of the loaded filters filters = wrapper.load_filters(filters_path) filter_match = wrapper.match_filter( filters, user_args, exec_dirs=exec_dirs) if not filter_match: sys.stderr.write("Unauthorized command: %s" % ' '.join(user_args)) sys.exit(RC_UNAUTHORIZED) def run_command(url, username, password, user_args, cmd_input): try: session = XenAPI.Session(url) session.login_with_password(username, password) try: host = session.xenapi.session.get_this_host(session.handle) result = session.xenapi.host.call_plugin( host, 'netwrap.py', 'run_command', {'cmd': json.dumps(user_args), 'cmd_input': json.dumps(cmd_input)}) result_dict = json.loads(result) returncode = result_dict.get('returncode') captured_stdout = result_dict.get('out') captured_stderr = result_dict.get('err') sys.stdout.write(captured_stdout) sys.stderr.write(captured_stderr) sys.exit(returncode) finally: session.xenapi.session.logout() except Exception as e: sys.stderr.write("Failed to execute command in Dom0, %s" % e) sys.exit(RC_XENAPI_ERROR) def main(): # Deprecated: This script is deprecated and will be deleted in next release sys.stderr.write("Deprecated: neutron-rootwrap-xen-dom0 is deprecated, " "will be deleted in next release.") exec_name, config_file, user_args = parse_args() config = load_configuration(exec_name, config_file) filter_command(exec_name, config['filters_path'], user_args, config['exec_dirs']) # If data is available on the standard input, we need to pass it to the # command executed in dom0 cmd_input = None if select.select([sys.stdin,],[],[],0.0)[0]: cmd_input = "".join(sys.stdin) return run_command(config['url'], config['username'], config['password'], user_args, cmd_input) if __name__ == '__main__': main() neutron-12.1.1/.coveragerc0000664000175000017500000000013313553660046015436 0ustar zuulzuul00000000000000[run] branch = True source = neutron omit = neutron/tests/* [report] ignore_errors = True neutron-12.1.1/tools/0000775000175000017500000000000013553660157014463 5ustar zuulzuul00000000000000neutron-12.1.1/tools/abandon_old_reviews.sh0000775000175000017500000001001413553660047021020 0ustar zuulzuul00000000000000#!/usr/bin/env bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # WARNING! # Please do not run this script without talking to the Neutron PTL. Auto # abandoning people's changes is a good thing, but must be done with care. # # before you run this modify your .ssh/config to create a # review.openstack.org entry: # # Host review.openstack.org # User # Port 29418 # # Note: due to gerrit bug somewhere, this double posts messages. :( # first purge all the reviews that are more than 4w old and blocked by a core -2 if [ "$1" = "--dry-run" ]; then echo "Enabling dry run mode" DRY_RUN=1 else DRY_RUN=0 fi set -o errexit DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" function abandon_review { local gitid=$1 shift local msg=$@ # echo ssh review.openstack.org gerrit review $gitid --abandon --message \"$msg\" unassign_and_new_bug $gitid if [ $DRY_RUN -eq 1 ]; then echo "Would abandon $gitid" else echo "Abandoning $gitid" ssh review.openstack.org gerrit review $gitid --abandon --message \"$msg\" fi } function unassign_and_new_bug { # unassign current assignee and set bug to 'new' status local gitid=$1 cm=$(ssh review.openstack.org "gerrit query $gitid --current-patch-set --format json" | jq .commitMessage) for closes in $(echo -e $cm | grep -i "closes" | grep -i "bug" | grep -o -E '[0-9]+'); do if [ $DRY_RUN -eq 1 ]; then echo "Would unassign and tag 'timeout-abandon' $closes" else echo "Attempting to change status of bug $closes to New" python "$DIR/unassign_bug.py" $closes fi done } PROJECTS="($( python - < 4 weeks without comment and currently blocked by a core reviewer with a -2. We are abandoning this for now. Feel free to reactivate the review by pressing the restore button and contacting the reviewer with the -2 on this review to ensure you address their concerns. EOF ) # For testing, put in a git rev of something you own and uncomment # blocked_reviews="b6c4218ae4d75b86c33fa3d37c27bc23b46b6f0f" for review in $blocked_reviews; do # echo ssh review.openstack.org gerrit review $review --abandon --message \"$msg\" echo "Blocked review $review" abandon_review $review $blocked_msg done # then purge all the reviews that are > 4w with no changes and Jenkins has -1ed failing_reviews=$(ssh review.openstack.org "gerrit query --current-patch-set --format json $PROJECTS status:open age:4w NOT label:Verified>=1,jenkins" | jq .currentPatchSet.revision | grep -v null | sed 's/"//g') failing_msg=$(cat < 4 weeks without comment, and failed Jenkins the last time it was checked. We are abandoning this for now. Feel free to reactivate the review by pressing the restore button and leaving a 'recheck' comment to get fresh test results. EOF ) for review in $failing_reviews; do echo "Failing review $review" abandon_review $review $failing_msg done neutron-12.1.1/tools/generate_config_file_samples.sh0000775000175000017500000000144013553660047022661 0ustar zuulzuul00000000000000#!/bin/sh # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. set -e GEN_CMD=oslo-config-generator if ! type "$GEN_CMD" > /dev/null; then echo "ERROR: $GEN_CMD not installed on the system." exit 1 fi for file in `ls etc/oslo-config-generator/*`; do $GEN_CMD --config-file=$file done set -x neutron-12.1.1/tools/split.sh0000775000175000017500000000567013553660047016163 0ustar zuulzuul00000000000000#!/bin/sh # # This script has been shamelessly copied and tweaked from original copy: # # https://github.com/openstack/oslo-incubator/blob/master/tools/graduate.sh # # Use this script to export a Neutron module to a separate git repo. # # You can call this script Call script like so: # # ./split.sh # # The file should be a text file like the one below: # # /path/to/file/file1 # /path/to/file/file2 # ... # /path/to/file/fileN # # Such a list can be generated with a command like this: # # find $path -type f # path is the base dir you want to list files for set -e if [ $# -lt 2 ]; then echo "Usage $0 " exit 1 fi set -x file_list_path="$1" project_name="$2" files_to_keep=$(cat $file_list_path) # Build the grep pattern for ignoring files that we want to keep keep_pattern="\($(echo $files_to_keep | sed -e 's/^/\^/' -e 's/ /\\|\^/g')\)" # Prune all other files in every commit pruner="git ls-files | grep -v \"$keep_pattern\" | git update-index --force-remove --stdin; git ls-files > /dev/stderr" # Find all first commits with listed files and find a subset of them that # predates all others roots="" for file in $files_to_keep; do file_root=$(git rev-list --reverse HEAD -- $file | head -n1) fail=0 for root in $roots; do if git merge-base --is-ancestor $root $file_root; then fail=1 break elif !git merge-base --is-ancestor $file_root $root; then new_roots="$new_roots $root" fi done if [ $fail -ne 1 ]; then roots="$new_roots $file_root" fi done # Purge all parents for those commits set_roots=" if [ 1 -eq 0 $(for root in $roots; do echo " -o \"\$GIT_COMMIT\" = '$root' "; done) ]; then echo ''; else cat; fi" # Enhance git_commit_non_empty_tree to skip merges with: # a) either two equal parents (commit that was about to land got purged as well # as all commits on mainline); # b) or with second parent being an ancestor to the first one (just as with a) # but when there are some commits on mainline). # In both cases drop second parent and let git_commit_non_empty_tree to decide # if commit worth doing (most likely not). skip_empty=$(cat << \EOF if [ $# = 5 ] && git merge-base --is-ancestor $5 $3; then git_commit_non_empty_tree $1 -p $3 else git_commit_non_empty_tree "$@" fi EOF ) # Filter out commits for unrelated files echo "Pruning commits for unrelated files..." git filter-branch \ --index-filter "$pruner" \ --parent-filter "$set_roots" \ --commit-filter "$skip_empty" \ --tag-name-filter cat \ -- --all # Generate the new .gitreview file echo "Generating new .gitreview file..." cat > .gitreview <&2 echo "Usage: $0 /path/to/neutron /path/to/target/etc /path/to/target/bin Deploy Neutron's rootwrap configuration. Warning: Any existing rootwrap files at the specified etc path will be removed by this script. Optional: set OS_SUDO_TESTING=1 to deploy the filters required by Neutron's functional testing suite." exit 1 fi OS_SUDO_TESTING=${OS_SUDO_TESTING:-0} neutron_path=$1 target_etc_path=$2 target_bin_path=$3 fullstack_path=$neutron_path/neutron/tests/fullstack/cmd src_conf_path=${neutron_path}/etc src_conf=${src_conf_path}/rootwrap.conf src_rootwrap_path=${src_conf_path}/neutron/rootwrap.d dst_conf_path=${target_etc_path}/neutron dst_conf=${dst_conf_path}/rootwrap.conf dst_rootwrap_path=${dst_conf_path}/rootwrap.d if [[ -d "$dst_rootwrap_path" ]]; then rm -rf ${dst_rootwrap_path} fi mkdir -p -m 755 ${dst_rootwrap_path} cp -p ${src_rootwrap_path}/* ${dst_rootwrap_path}/ cp -p ${src_conf} ${dst_conf} sed -i "s:^filters_path=.*$:filters_path=${dst_rootwrap_path}:" ${dst_conf} sed -i "s:^exec_dirs=\(.*\)$:exec_dirs=${target_bin_path},${fullstack_path},\1:" ${dst_conf} if [[ "$OS_SUDO_TESTING" = "1" ]]; then sed -i 's/use_syslog=False/use_syslog=True/g' ${dst_conf} sed -i 's/syslog_log_level=ERROR/syslog_log_level=DEBUG/g' ${dst_conf} cp -p ${neutron_path}/neutron/tests/contrib/testing.filters \ ${dst_rootwrap_path}/ fi neutron-12.1.1/tools/coding-checks.sh0000775000175000017500000000252713553660046017526 0ustar zuulzuul00000000000000#!/bin/sh set -eu usage () { echo "Usage: $0 [OPTION]..." echo "Run Neutron's coding check(s)" echo "" echo " -Y, --pylint [] Run pylint check on the entire neutron module or just files changed in basecommit (e.g. HEAD~1)" echo " -h, --help Print this usage message" echo exit 0 } process_options () { i=1 while [ $i -le $# ]; do eval opt=\$$i case $opt in -h|--help) usage;; -Y|--pylint) pylint=1;; *) scriptargs="$scriptargs $opt" esac i=$((i+1)) done } run_pylint () { local target="${scriptargs:-all}" if [ "$target" = "all" ]; then files="neutron" else case "$target" in *HEAD~[0-9]*) files=$(git diff --diff-filter=AM --name-only $target -- "*.py");; *) echo "$target is an unrecognized basecommit"; exit 1;; esac fi echo "Running pylint..." echo "You can speed this up by running it on 'HEAD~[0-9]' (e.g. HEAD~1, this change only)..." if [ -n "${files}" ]; then pylint --rcfile=.pylintrc --output-format=colorized ${files} else echo "No python changes in this commit, pylint check not required." exit 0 fi } scriptargs= pylint=1 process_options $@ if [ $pylint -eq 1 ]; then run_pylint exit 0 fi neutron-12.1.1/tools/install_venv_common.py0000664000175000017500000001350713553660046021114 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Provides methods needed by installation script for OpenStack development virtual environments. Since this script is used to bootstrap a virtualenv from the system's Python environment, it should be kept strictly compatible with Python 2.6. Synced in from openstack-common """ from __future__ import print_function import optparse import os import subprocess import sys class InstallVenv(object): def __init__(self, root, venv, requirements, test_requirements, py_version, project): self.root = root self.venv = venv self.requirements = requirements self.test_requirements = test_requirements self.py_version = py_version self.project = project def die(self, message, *args): print(message % args, file=sys.stderr) sys.exit(1) def check_python_version(self): if sys.version_info < (2, 6): self.die("Need Python Version >= 2.6") def run_command_with_code(self, cmd, redirect_output=True, check_exit_code=True): """Runs a command in an out-of-process shell. Returns the output of that command. Working directory is self.root. """ if redirect_output: stdout = subprocess.PIPE else: stdout = None proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) output = proc.communicate()[0] if check_exit_code and proc.returncode != 0: self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) return (output, proc.returncode) def run_command(self, cmd, redirect_output=True, check_exit_code=True): return self.run_command_with_code(cmd, redirect_output, check_exit_code)[0] def get_distro(self): if (os.path.exists('/etc/fedora-release') or os.path.exists('/etc/redhat-release')): return Fedora( self.root, self.venv, self.requirements, self.test_requirements, self.py_version, self.project) else: return Distro( self.root, self.venv, self.requirements, self.test_requirements, self.py_version, self.project) def check_dependencies(self): self.get_distro().install_virtualenv() def create_virtualenv(self, no_site_packages=True): """Creates the virtual environment and installs PIP. Creates the virtual environment and installs PIP only into the virtual environment. """ if not os.path.isdir(self.venv): print('Creating venv...', end=' ') if no_site_packages: self.run_command(['virtualenv', '-q', '--no-site-packages', self.venv]) else: self.run_command(['virtualenv', '-q', self.venv]) print('done.') else: print("venv already exists...") pass def pip_install(self, *args): self.run_command(['tools/with_venv.sh', 'pip', 'install', '--upgrade'] + list(args), redirect_output=False) def install_dependencies(self): print('Installing dependencies with pip (this can take a while)...') # First things first, make sure our venv has the latest pip and # setuptools and pbr self.pip_install('pip>=1.4') self.pip_install('setuptools') self.pip_install('pbr') self.pip_install('-r', self.requirements, '-r', self.test_requirements) def parse_args(self, argv): """Parses command-line arguments.""" parser = optparse.OptionParser() parser.add_option('-n', '--no-site-packages', action='store_true', help="Do not inherit packages from global Python " "install.") return parser.parse_args(argv[1:])[0] class Distro(InstallVenv): def check_cmd(self, cmd): return bool(self.run_command(['which', cmd], check_exit_code=False).strip()) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if self.check_cmd('easy_install'): print('Installing virtualenv via easy_install...', end=' ') if self.run_command(['easy_install', 'virtualenv']): print('Succeeded') return else: print('Failed') self.die('ERROR: virtualenv not found.\n\n%s development' ' requires virtualenv, please install it using your' ' favorite package management tool' % self.project) class Fedora(Distro): """This covers all Fedora-based distributions. Includes: Fedora, RHEL, CentOS, Scientific Linux """ def check_pkg(self, pkg): return self.run_command_with_code(['rpm', '-q', pkg], check_exit_code=False)[1] == 0 def install_virtualenv(self): if self.check_cmd('virtualenv'): return if not self.check_pkg('python-virtualenv'): self.die("Please install 'python-virtualenv'.") super(Fedora, self).install_virtualenv() neutron-12.1.1/tools/misc-sanity-checks.sh0000775000175000017500000000437013553660047020522 0ustar zuulzuul00000000000000#! /bin/sh # Copyright (C) 2014 VA Linux Systems Japan K.K. # Copyright (C) 2014 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. TMPDIR=`mktemp -d /tmp/${0##*/}.XXXXXX` || exit 1 export TMPDIR trap "rm -rf $TMPDIR" EXIT FAILURES=$TMPDIR/failures check_no_symlinks_allowed () { # Symlinks break the package build process, so ensure that they # do not slip in, except hidden symlinks. if [ $(find . -type l ! -path '*/.*' | wc -l) -ge 1 ]; then echo "Symlinks are not allowed!" >>$FAILURES fi } check_pot_files_errors () { # The job neutron-propose-translation-update does not update from # transifex since our po files contain duplicate entries where # obsolete entries duplicate normal entries. Prevent obsolete # entries to slip in find neutron -type f -regex '.*\.pot?' \ -print0|xargs -0 -n 1 msgfmt --check-format \ -o /dev/null if [ "$?" -ne 0 ]; then echo "PO files syntax is not correct!" >>$FAILURES fi } check_identical_policy_files () { # For unit tests, we maintain their own policy.json file to make test suite # independent of whether it's executed from the neutron source tree or from # site-packages installation path. We don't want two copies of the same # file to diverge, so checking that they are identical diff etc/policy.json neutron/tests/etc/policy.json 2>&1 > /dev/null if [ "$?" -ne 0 ]; then echo "policy.json files must be identical!" >>$FAILURES fi } # Add your checks here... check_no_symlinks_allowed check_pot_files_errors check_identical_policy_files # Fail, if there are emitted failures if [ -f $FAILURES ]; then cat $FAILURES exit 1 fi neutron-12.1.1/tools/unassign_bug.py0000664000175000017500000000342413553660046017521 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Unassigns assignee from neutron/network bug, adds message and tag. If you get the following exception, you need X11 and python-dbus installed: RuntimeError: No recommended backend was available. Install the keyrings.alt package if you want to use the non-recommended backends. See README.rst for details. """ import sys from launchpadlib.launchpad import Launchpad MSG_BODY = "\ This bug has had a related patch abandoned and has been automatically \ un-assigned due to inactivity. Please re-assign yourself if you are \ continuing work or adjust the state as appropriate if it is no longer valid." def unassign(bug_num): launchpad = Launchpad.login_with('neutron', 'production') b = launchpad.bugs[bug_num] for task in b.bug_tasks: if ('neutron' not in task.bug_target_name and 'network' not in task.bug_target_name): # try not to interfere with non-neutron projects too much continue task.assignee = None if task.status == "In Progress": task.status = 'New' task.lp_save() b.tags = b.tags + ['timeout-abandon'] b.newMessage(content=MSG_BODY, subject='auto-abandon-script') b.lp_save() if __name__ == '__main__': unassign(int(sys.argv[1])) neutron-12.1.1/tools/ostestr_compat_shim.sh0000775000175000017500000000025713553660047021112 0ustar zuulzuul00000000000000#!/bin/sh # preserve old behavior of using an arg as a regex when '--' is not present case $@ in (*--*) ostestr $@;; ('') ostestr;; (*) ostestr --regex "$@" esac neutron-12.1.1/tools/check_unit_test_structure.sh0000775000175000017500000000445213553660046022317 0ustar zuulzuul00000000000000#!/usr/bin/env bash # This script identifies the unit test modules that do not correspond # directly with a module in the code tree. See TESTING.rst for the # intended structure. neutron_path=$(cd "$(dirname "$0")/.." && pwd) base_test_path=neutron/tests/unit test_path=$neutron_path/$base_test_path test_files=$(find ${test_path} -iname 'test_*.py') ignore_regexes=( # The following test is required for oslo.versionedobjects "^objects/test_objects.py$" # The following open source plugin tests are not actually unit # tests and are ignored pending their relocation to the functional # test tree. "^plugins/ml2/drivers/mech_sriov/mech_driver/test_mech_sriov_nic_switch.py$" "^plugins/ml2/test_security_group.py$" "^plugins/ml2/test_port_binding.py$" "^plugins/ml2/test_extension_driver_api.py$" "^plugins/ml2/test_ext_portsecurity.py$" "^plugins/ml2/test_agent_scheduler.py$" "^plugins/ml2/test_tracked_resources.py$" "^plugins/ml2/drivers/openvswitch/agent/test_agent_scheduler.py$" "^plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py$" ) error_count=0 ignore_count=0 total_count=0 for test_file in ${test_files[@]}; do relative_path=${test_file#$test_path/} expected_path=$(dirname $neutron_path/neutron/$relative_path) test_filename=$(basename "$test_file") expected_filename=${test_filename#test_} # Module filename (e.g. foo/bar.py -> foo/test_bar.py) filename=$expected_path/$expected_filename # Package dir (e.g. foo/ -> test_foo.py) package_dir=${filename%.py} if [ ! -f "$filename" ] && [ ! -d "$package_dir" ]; then for ignore_regex in ${ignore_regexes[@]}; do if [[ "$relative_path" =~ $ignore_regex ]]; then ignore_count=$((ignore_count + 1)) continue 2 fi done echo "Unexpected test file: $base_test_path/$relative_path" error_count=$((error_count + 1)) fi total_count=$((total_count + 1)) done if [ "$ignore_count" -ne 0 ]; then echo "$ignore_count unmatched test modules were ignored" fi if [ "$error_count" -eq 0 ]; then echo 'Success! All test modules match targets in the code tree.' exit 0 else echo "Failure! $error_count of $total_count test modules do not match targets in the code tree." exit 1 fi neutron-12.1.1/tools/with_venv.sh0000775000175000017500000000153513553660046017034 0ustar zuulzuul00000000000000#!/usr/bin/env bash # Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. tools_path=${tools_path:-$(dirname $0)} venv_path=${venv_path:-${tools_path}} venv_dir=${venv_name:-/../.venv} TOOLS=${tools_path} VENV=${venv:-${venv_path}/${venv_dir}} source $VENV/bin/activate && "$@" neutron-12.1.1/tools/configure_for_func_testing.sh0000775000175000017500000002306313553660047022423 0ustar zuulzuul00000000000000#!/usr/bin/env bash # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. set -e # Control variable used to determine whether to execute this script # directly or allow the gate_hook to import. IS_GATE=${IS_GATE:-False} USE_CONSTRAINT_ENV=${USE_CONSTRAINT_ENV:-True} if [[ "$IS_GATE" != "True" ]] && [[ "$#" -lt 1 ]]; then >&2 echo "Usage: $0 /path/to/devstack [-i] Configure a host to run Neutron's functional test suite. -i Install Neutron's package dependencies. By default, it is assumed that devstack has already been used to deploy neutron to the target host and that package dependencies need not be installed. Warning: This script relies on devstack to perform extensive modification to the underlying host. It is recommended that it be invoked only on a throw-away VM." exit 1 fi # Skip the first argument OPTIND=2 while getopts ":i" opt; do case $opt in i) INSTALL_BASE_DEPENDENCIES=True ;; esac done # Default to environment variables to permit the gate_hook to override # when sourcing. VENV=${VENV:-dsvm-functional} DEVSTACK_PATH=${DEVSTACK_PATH:-$1} PROJECT_NAME=${PROJECT_NAME:-neutron} REPO_BASE=${GATE_DEST:-$(cd $(dirname "$0")/../.. && pwd)} INSTALL_MYSQL_ONLY=${INSTALL_MYSQL_ONLY:-False} # The gate should automatically install dependencies. INSTALL_BASE_DEPENDENCIES=${INSTALL_BASE_DEPENDENCIES:-$IS_GATE} if [ ! -f "$DEVSTACK_PATH/stack.sh" ]; then >&2 echo "Unable to find devstack at '$DEVSTACK_PATH'. Please verify that the specified path points to a valid devstack repo." exit 1 fi set -x function _init { # Subsequently-called devstack functions depend on the following variables. HOST_IP=127.0.0.1 FILES=$DEVSTACK_PATH/files TOP_DIR=$DEVSTACK_PATH source $DEVSTACK_PATH/stackrc # Allow the gate to override values set by stackrc. DEST=${GATE_DEST:-$DEST} STACK_USER=${GATE_STACK_USER:-$STACK_USER} GetDistro source $DEVSTACK_PATH/tools/fixup_stuff.sh fixup_uca } function _install_base_deps { echo_summary "Installing base dependencies" INSTALL_TESTONLY_PACKAGES=True PACKAGES=$(get_packages general,neutron,q-agt,q-l3,openvswitch) # Do not install 'python-' prefixed packages other than # python-dev*. Neutron's functional testing relies on deployment # to a tox env so there is no point in installing python # dependencies system-wide. PACKAGES=$(echo $PACKAGES | perl -pe 's|python-(?!dev)[^ ]*||g') install_package $PACKAGES } function _install_rpc_backend { echo_summary "Installing rabbitmq" RABBIT_USERID=${RABBIT_USERID:-stackrabbit} RABBIT_HOST=${RABBIT_HOST:-$SERVICE_HOST} RABBIT_PASSWORD=${RABBIT_HOST:-secretrabbit} source $DEVSTACK_PATH/lib/rpc_backend enable_service rabbit install_rpc_backend restart_rpc_backend } # _install_databases [install_pg] function _install_databases { local install_pg=${1:-True} echo_summary "Installing databases" # Avoid attempting to configure the db if it appears to already # have run. The setup as currently defined is not idempotent. if mysql openstack_citest > /dev/null 2>&1 < /dev/null; then echo_summary "DB config appears to be complete, skipping." return 0 fi MYSQL_PASSWORD=${MYSQL_PASSWORD:-secretmysql} DATABASE_PASSWORD=${DATABASE_PASSWORD:-secretdatabase} source $DEVSTACK_PATH/lib/database enable_service mysql initialize_database_backends install_database configure_database_mysql if [[ "$install_pg" == "True" ]]; then enable_service postgresql initialize_database_backends install_database configure_database_postgresql fi # Set up the 'openstack_citest' user and database in each backend tmp_dir=$(mktemp -d) trap "rm -rf $tmp_dir" EXIT cat << EOF > $tmp_dir/mysql.sql CREATE DATABASE openstack_citest; CREATE USER 'openstack_citest'@'localhost' IDENTIFIED BY 'openstack_citest'; CREATE USER 'openstack_citest' IDENTIFIED BY 'openstack_citest'; GRANT ALL PRIVILEGES ON *.* TO 'openstack_citest'@'localhost'; GRANT ALL PRIVILEGES ON *.* TO 'openstack_citest'; FLUSH PRIVILEGES; EOF /usr/bin/mysql -u root < $tmp_dir/mysql.sql if [[ "$install_pg" == "True" ]]; then cat << EOF > $tmp_dir/postgresql.sql CREATE USER openstack_citest WITH CREATEDB LOGIN PASSWORD 'openstack_citest'; CREATE DATABASE openstack_citest WITH OWNER openstack_citest; EOF # User/group postgres needs to be given access to tmp_dir setfacl -m g:postgres:rwx $tmp_dir sudo -u postgres /usr/bin/psql --file=$tmp_dir/postgresql.sql fi } function _install_agent_deps { echo_summary "Installing agent dependencies" ENABLED_SERVICES=q-agt,q-dhcp,q-l3 source $DEVSTACK_PATH/lib/neutron install_neutron_agent_packages } # Set up the rootwrap sudoers for neutron to target the rootwrap # configuration deployed in the venv. function _install_rootwrap_sudoers { echo_summary "Installing rootwrap sudoers file" PROJECT_VENV=$REPO_BASE/$PROJECT_NAME/.tox/$VENV ROOTWRAP_SUDOER_CMD="$PROJECT_VENV/bin/neutron-rootwrap $PROJECT_VENV/etc/neutron/rootwrap.conf *" ROOTWRAP_DAEMON_SUDOER_CMD="$PROJECT_VENV/bin/neutron-rootwrap-daemon $PROJECT_VENV/etc/neutron/rootwrap.conf" TEMPFILE=$(mktemp) SECURE_PATH="$PROJECT_VENV/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" if [[ "$VENV" =~ "dsvm-fullstack" ]]; then SECURE_PATH="$REPO_BASE/$PROJECT_NAME/neutron/tests/fullstack/cmd:$SECURE_PATH" fi cat << EOF > $TEMPFILE # A bug in oslo.rootwrap [1] prevents commands executed with 'ip netns # exec' from being automatically qualified with a prefix from # rootwrap's configured exec_dirs. To work around this problem, add # the venv bin path to a user-specific secure_path. # # While it might seem preferable to set a command-specific # secure_path, this would only ensure the correct path for 'ip netns # exec' and the command targeted for execution in the namespace would # not inherit the path. # # 1: https://bugs.launchpad.net/oslo.rootwrap/+bug/1417331 # Defaults:$STACK_USER secure_path="$SECURE_PATH" $STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_SUDOER_CMD $STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_DAEMON_SUDOER_CMD EOF chmod 0440 $TEMPFILE sudo chown root:root $TEMPFILE # Name the functional testing rootwrap to ensure that it will be # loaded after the devstack rootwrap (50_stack_sh if present) so # that the functional testing secure_path (a superset of what # devstack expects) will not be overwritten. sudo mv $TEMPFILE /etc/sudoers.d/60-neutron-func-test-rootwrap } function _install_post_devstack { echo_summary "Performing post-devstack installation" _install_databases _install_rootwrap_sudoers if is_ubuntu; then install_package isc-dhcp-client install_package nmap elif is_fedora; then install_package dhclient install_package nmap-ncat elif is_suse; then install_package dhcp-client # NOTE(armax): no harm in allowing 'other' to read and # execute the script. This is required in fullstack # testing and avoids quite a bit of rootwrap pain sudo chmod o+rx /sbin/dhclient-script install_package ncat else exit_distro_not_supported "installing dhclient and ncat packages" fi # Installing python-openvswitch from packages is a stop-gap while # python-openvswitch remains unavailable from pypi. This also # requires that sitepackages=True be set in tox.ini to allow the # venv to use the installed package. Once python-openvswitch # becomes available on pypi, this will no longer be required. # # NOTE: the package name 'python-openvswitch' is common across # supported distros. install_package python-openvswitch enable_kernel_bridge_firewall } function _configure_iptables_rules { # For linuxbridge agent fullstack tests we need to add special rules to # iptables for connection of agents to rabbitmq: CHAIN_NAME="openstack-INPUT" sudo iptables -n --list $CHAIN_NAME 1> /dev/null 2>&1 || CHAIN_NAME="INPUT" sudo iptables -I $CHAIN_NAME -s 240.0.0.0/8 -p tcp -m tcp -d 240.0.0.0/8 --dport 5672 -j ACCEPT } function _enable_ipv6 { sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0 } function configure_host_for_func_testing { echo_summary "Configuring host for functional testing" if [[ "$INSTALL_BASE_DEPENDENCIES" == "True" ]]; then # Installing of the following can be achieved via devstack by # installing neutron, so their installation is conditional to # minimize the work to do on a devstack-configured host. _install_base_deps _install_agent_deps _install_rpc_backend fi _install_post_devstack } _init if [[ "$IS_GATE" != "True" ]]; then if [[ "$INSTALL_MYSQL_ONLY" == "True" ]]; then _install_databases nopg else configure_host_for_func_testing fi fi if [[ "$VENV" =~ "dsvm-fullstack" ]]; then _enable_ipv6 _configure_iptables_rules sudo modprobe ip_conntrack_proto_sctp fi echo "Phew, we're done!" neutron-12.1.1/tools/install_venv.py0000664000175000017500000000464213553660046017544 0ustar zuulzuul00000000000000#!/usr/bin/env python # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2010 OpenStack Foundation. # Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Installation script for Neutron's development virtualenv """ from __future__ import print_function import os import sys import install_venv_common as install_venv def print_help(): help = """ Neutron development environment setup is complete. Neutron development uses virtualenv to track and manage Python dependencies while in development and testing. To activate the Neutron virtualenv for the extent of your current shell session you can run: $ . .venv/bin/activate Or, if you prefer, you can run commands in the virtualenv on a case by case basis by running: $ tools/with_venv.sh Also, make test will automatically use the virtualenv. """ print(help) def main(argv): if 'tools_path' in os.environ: root = os.environ['tools_path'] else: root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) if 'venv' in os.environ: venv = os.environ['venv'] else: venv = os.path.join(root, '.venv') pip_requires = os.path.join(root, 'requirements.txt') test_requires = os.path.join(root, 'test-requirements.txt') py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) project = 'Neutron' install = install_venv.InstallVenv(root, venv, pip_requires, test_requires, py_version, project) options = install.parse_args(argv) install.check_python_version() install.check_dependencies() install.create_virtualenv(no_site_packages=options.no_site_packages) install.install_dependencies() print_help() if __name__ == '__main__': main(sys.argv) neutron-12.1.1/tools/generate_dhclient_script_for_fullstack.sh0000775000175000017500000000160013553660046024762 0ustar zuulzuul00000000000000#!/bin/bash MAKE_RESOLV_CONF_FUNCTION=make_resolv_conf USAGE="$0 The script takes existing dhclient-script and makes $MAKE_RESOLV_CONF_FUNCTION function a noop function. " if [ $# -lt 1 ]; then echo "Path to virtual environment directory is a required parameter." echo $USAGE exit 2 fi VENV_DIR=$1 DHCLIENT_SCRIPT_NAME=dhclient-script DHCLIENT_PATH=$(which $DHCLIENT_SCRIPT_NAME) FULLSTACK_DHCLIENT_SCRIPT=$VENV_DIR/bin/fullstack-dhclient-script if [ -n "$DHCLIENT_PATH" ]; then # Return from make_resolv_conf function immediately. This will cause # that /etc/resolv.conf will not be updated by fake fullstack machines. sed "/^$MAKE_RESOLV_CONF_FUNCTION()/a\ return" $DHCLIENT_PATH > $FULLSTACK_DHCLIENT_SCRIPT chmod +x $FULLSTACK_DHCLIENT_SCRIPT else echo "$DHCLIENT_SCRIPT_NAME not found." exit 1 fi neutron-12.1.1/tools/list_moved_globals.py0000775000175000017500000000235513553660046020712 0ustar zuulzuul00000000000000#!/usr/bin/env python # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Check for globals that are now available in neutron-lib """ from __future__ import print_function def check_globals(things, nmod, lmod): core = vars(nmod)['_mg__my_globals'] lib = vars(lmod) moved_things = [] for thing in core: if thing.startswith('__') or thing == '_': continue if thing in lib: moved_things.append(thing) if moved_things: print("\nThese %s have moved to neutron-lib:" % things) for moved_thing in sorted(moved_things): print(" %s" % moved_thing) def main(): """Currently no globals are deprecated.""" if __name__ == '__main__': main() neutron-12.1.1/test-requirements.txt0000664000175000017500000000156713553660047017573 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 bandit>=1.1.0,<1.5.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 mock>=2.0.0 # BSD python-subunit>=1.0.0 # Apache-2.0/BSD sphinx!=1.6.6,>=1.6.2 # BSD openstackdocstheme>=1.18.1 # Apache-2.0 oslosphinx>=4.7.0 # Apache-2.0 testtools>=2.2.0 # MIT testresources>=2.0.0 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD WebTest>=2.0.27 # MIT oslotest>=3.2.0 # Apache-2.0 os-testr>=1.0.0 # Apache-2.0 ddt>=1.0.1 # MIT pylint==1.4.5 # GPLv2 reno>=2.5.0 # Apache-2.0 # Needed to run DB commands in virtualenvs PyMySQL>=0.7.6 # MIT License bashate>=0.5.1 # Apache-2.0 neutron-12.1.1/.stestr.conf0000664000175000017500000000010513553660046015565 0ustar zuulzuul00000000000000[DEFAULT] test_path=${OS_TEST_PATH:-./neutron/tests/unit} top_dir=./ neutron-12.1.1/doc/0000775000175000017500000000000013553660156014067 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/0000775000175000017500000000000013553660156015367 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/contributor/0000775000175000017500000000000013553660156017741 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/contributor/policies/0000775000175000017500000000000013553660156021550 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/contributor/policies/release-checklist.rst0000664000175000017500000001225413553660047025674 0ustar zuulzuul00000000000000Pre-release check list ====================== This page lists things to cover before a Neutron release and will serve as a guide for next release managers. Server ------ Major release ~~~~~~~~~~~~~ Major release is cut off once per development cycle and has an assigned name (Liberty, Mitaka, ...) Prior to major release, #. consider blocking all patches that are not targeted for the new release; #. consider blocking trivial patches to keep the gate clean; #. revise the current list of blueprints and bugs targeted for the release; roll over anything that does not fit there, or won't make it (note that no new features land master after so called feature freeze is claimed by release team; there is a feature freeze exception (FFE) process described in release engineering documentation in more details: http://docs.openstack.org/project-team-guide/release-management.html); #. start collecting state for targeted features from the team. For example, propose a postmortem patch for neutron-specs as in: https://review.openstack.org/#/c/286413/ #. revise deprecation warnings collected in latest Jenkins runs: some of them may indicate a problem that should be fixed prior to release (see deprecations.txt file in those log directories); also, check whether any Launchpad bugs with the 'deprecation' tag need a clean-up or a follow-up in the context of the release planned; #. check that release notes and sample configuration files render correctly, arrange clean-up if needed. #. ensure all doc links are valid by running ``tox -e linkcheck`` and addressing any broken links. New major release process contains several phases: #. master branch is blocked for patches that are not targeted for the release; #. the whole team is expected to work on closing remaining pieces targeted for the release; #. once the team is ready to release the first release candidate (RC1), either PTL or one of release liaisons proposes a patch for openstack/releases repo. For example, see: https://review.openstack.org/#/c/292445/ #. once the openstack/releases patch land, release team creates a new stable branch using hash values specified in the patch; #. at this point, master branch is open for patches targeted to the next release; PTL unblocks all patches that were blocked in step 1; #. if additional patches are identified that are critical for the release and must be shipped in the final major build, corresponding bugs are tagged with -rc-potential in Launchpad, fixes are prepared and land in master branch, and are then backported to the newly created stable branch; #. if patches landed in the release stable branch as per the previous step, a new release candidate that would include those patches should be requested by PTL in openstack/releases repo. #. eventually, the latest release candidate requested by PTL becomes the final major release of the project. Release candidate (RC) process allows for stabilization of the final release. The following technical steps should be taken before the final release is cut off: #. the latest alembic scripts are tagged with a milestone label. For example, see: https://review.openstack.org/#/c/288212/ In the new stable branch, you should make sure that: #. .gitreview file points to the new branch; #. if the branch uses constraints to manage gated dependency versions, the default constraints file name points to corresponding stable branch in openstack/requirements repo; #. if the branch fetches any other projects as dependencies, f.e. by using tox_install.sh as an install_command in tox.ini, git repository links point to corresponding stable branches of those dependency projects. Note that some of those steps may be covered by OpenStack release team. In the opened master branch, you should: #. update CURRENT_RELEASE in neutron.db.migration.cli to point to the next release name. While preparing the next release and even in the middle of development, it's worth keeping the infrastructure clean. Consider using those tools to declutter the project infrastructure: #. declutter Gerrit:: /tools/abandon_old_reviews.sh #. declutter Launchpad:: /pre_expire_bugs.py neutron --day Minor release ~~~~~~~~~~~~~ Minor release is a release created from existing stable branch after the initial major release, and that usually contains bug fixes and small improvements only. The following steps should be taken before claiming a successful minor release: #. a patch for openstack/releases repo is proposed and merged. Client ------ Most tips from the Server section apply to client releases too. Several things to note though: #. when preparing for a major release, pay special attention to client bits that are targeted for the release. Global openstack/requirements freeze happens long before first RC release of server components. So if you plan to land server patches that depend on a new client, make sure you don't miss the requirements freeze. After the freeze is in action, there is no easy way to land more client patches for the planned target. All this may push an affected feature to the next development cycle. neutron-12.1.1/doc/source/contributor/policies/bugs.rst0000664000175000017500000011677613553660047023263 0ustar zuulzuul00000000000000Neutron Bugs ============ Neutron (client, core, FwaaS, LBaaS, VPNaaS) maintains all of its bugs in the following Launchpad projects: * `Launchpad Neutron `_ * `Launchpad python-neutronclient `_ Neutron Bugs Team In Launchpad ------------------------------ The `Neutron Bugs `_ team in Launchpad is used to allow access to the projects above. Members of the above group have the ability to set bug priorities, target bugs to releases, and other administrative tasks around bugs. The administrators of this group are the members of the `neutron-drivers-core `_ gerrit group. Non administrators of this group include anyone who is involved with the Neutron project and has a desire to assist with bug triage. If you would like to join this Launchpad group, it's best to reach out to a member of the above mentioned neutron-drivers-core team in #openstack-neutron on Freenode and let them know why you would like to be a member. The team is more than happy to add additional bug triage capability, but it helps to know who is requesting access, and IRC is a quick way to make the connection. As outlined below the bug deputy is a volunteer who wants to help with defect management. Permissions will have to be granted assuming that people sign up on the deputy role. The permission won't be given freely, a person must show some degree of prior involvement. Neutron Bug Deputy ------------------ Neutron maintains the notion of a "bug deputy". The bug deputy plays an important role in the Neutron community. As a large project, Neutron is routinely fielding many bug reports. The bug deputy is responsible for acting as a "first contact" for these bug reports and performing initial screening/triaging. The bug deputy is expected to communicate with the various Neutron teams when a bug has been triaged. In addition, the bug deputy should be reporting "High" and "Critical" priority bugs. To avoid burnout, and to give a chance to everyone to gain experience in defect management, the Neutron bug deputy is a rotating role. The rotation will be set on a period (typically one or two weeks) determined by the team during the weekly Neutron IRC meeting and/or according to holidays. During the Neutron IRC meeting we will expect a volunteer to step up for the period. Members of the Neutron core team are invited to fill in the role, however non-core Neutron contributors who are interested are also encouraged to take up the role. This contributor is going to be the bug deputy for the period, and he/she will be asked to report to the team during the subsequent IRC meeting. The PTL will also work with the team to assess that everyone gets his/her fair share at fulfilling this duty. It is reasonable to expect some imbalance from time to time, and the team will work together to resolve it to ensure that everyone is 100% effective and well rounded in their role as _custodian_ of Neutron quality. Should the duty load be too much in busy times of the release, the PTL and the team will work together to assess whether more than one deputy is necessary in a given period. The presence of a bug deputy does not mean the rest of the team is simply off the hook for the period, in fact the bug deputy will have to actively work with the Lieutenants/Drivers, and these should help in getting the bug report moving down the resolution pipeline. During the period a member acts as bug deputy, he/she is expected to watch bugs filed against the Neutron projects (as listed above) and do a first screening to determine potential severity, tagging, logstash queries, other affected projects, affected releases, etc. From time to time bugs will be filed and auto-assigned by members of the core team to get them to a swift resolution. Obviously, the deputy is exempt from screening these. Finally, the PTL will work with the deputy to produce a brief summary of the issues of the week to be shared with the larger team during the weekly IRC meeting and tracked in the meeting notes. If for some reason the deputy is not going to attend the team meeting to report, the deputy should consider sending a brief report to the openstack-dev@ mailing list in advance of the meeting. Getting Ready to Serve as the Neutron Bug Deputy ------------------------------------------------ If you are interested in serving as the Neutron bug deputy, there are several steps you will need to follow in order to be prepared. * Request to be added to the `neutron-bugs team in Launchpad `_. This request will be approved when you are assigned a bug deputy slot. * Read this page in full. Keep this document in mind at all times as it describes the duties of the bug deputy and how to triage bugs particularly around setting the importance and tags of bugs. * Sign up for neutron bug emails from LaunchPad. * Navigate to the `LaunchPad Neutron bug list `_. * On the right hand side, click on "Subscribe to bug mail". * In the pop-up that is displayed, keep the recipient as "Yourself", and your subscription something useful like "Neutron Bugs". You can choose either option for how much mail you get, but keep in mind that getting mail for all changes - while informative - will result in several dozen emails per day at least. * Do the same for the `LaunchPad python-neutronclient bug list `_. * Configure the information you get from `LaunchPad `_ to make visible additional information, especially the 'age' of the bugs. You accomplish that by clicking the little gear on the left hand side of the screen at the top of the bugs list. This provides an overview of information for each bug on a single page. * Optional: Set up your mail client to highlight bug email that indicates a new bug has been filed, since those are the ones you will be wanting to triage. Filter based on email from "@bugs.launchpad.net" with "[NEW]" in the subject line. * Volunteer during the course of the Neutron team meeting, when volunteers to be bug deputy are requested (usually towards the beginning of the meeting). * View your scheduled week on the `Neutron Meetings page `_. * During your shift, if it is feasible for your timezone, plan on attending the Neutron Drivers meeting. That way if you have tagged any bugs as RFE, you can be present to discuss them. Plugin and Driver Repositories ------------------------------ Many plugins and drivers have backend code that exists in another repository. These repositories may have their own Launchpad projects for bugs. The teams working on the code in these repos assume full responsibility for bug handling in those projects. For this reason, bugs whose solution would exist solely in the plugin/driver repo should not have Neutron in the affected projects section. However, you should add Neutron (Or any other project) to that list only if you expect that a patch is needed to that repo in order to solve the bug. It's also worth adding that some of these projects are part of the so called Neutron `stadium `_. Because of that, their release is managed centrally by the Neutron release team; requests for releases need to be funnelled and screened properly before they can happen. Release request process is described :ref:`here `. .. _guidelines: Bug Screening Best Practices ---------------------------- When screening bug reports, the first step for the bug deputy is to assess how well written the bug report is, and whether there is enough information for anyone else besides the bug submitter to reproduce the bug and come up with a fix. There is plenty of information on the `OpenStack wiki `_ on how to write a good bug `report `_ and to learn how to tell a good bug report from a bad one. Should the bug report not adhere to these best practices, the bug deputy's first step would be to redirect the submitter to this section, invite him/her to supply the missing information, and mark the bug report as 'Incomplete'. For future submissions, the reporter can then use the template provided below to ensure speedy triaging. Done often enough, this practice should (ideally) ensure that in the long run, only 'good' bug reports are going to be filed. Bug Report Template ~~~~~~~~~~~~~~~~~~~ The more information you provide, the higher the chance of speedy triaging and resolution: identifying the problem is half the solution. To this aim, when writing a bug report, please consider supplying the following details and following these suggestions: * Summary (Bug title): keep it small, possibly one line. If you cannot describe the issue in less than 100 characters, you are probably submitting more than one bug at once. * Further information (Bug description): conversely from other bug trackers, Launchpad does not provide a structured way of submitting bug-related information, but everything goes in this section. Therefore, you are invited to break down the description in the following fields: * High level description: provide a brief sentence (a couple of lines) of what are you trying to accomplish, or would like to accomplish differently; the 'why' is important, but can be omitted if obvious (not to you of course). * Pre-conditions: what is the initial state of your system? Please consider enumerating resources available in the system, if useful in diagnosing the problem. Who are you? A regular user or a super-user? Are you describing service-to-service interaction? * Step-by-step reproduction steps: these can be actual neutron client commands or raw API requests; Grab the output if you think it is useful. Please, consider using `paste.o.o `_ for long outputs as Launchpad poorly format the description field, making the reading experience somewhat painful. * Expected output: what did you hope to see? How would you have expected the system to behave? A specific error/success code? The output in a specific format? Or more than a user was supposed to see, or less? * Actual output: did the system silently fail (in this case log traces are useful)? Did you get a different response from what you expected? * Version: * OpenStack version (Specific stable branch, or git hash if from trunk); * Linux distro, kernel. For a distro, it's also worth knowing specific versions of client and server, not just major release; * Relevant underlying processes such as openvswitch, iproute etc; * DevStack or other _deployment_ mechanism? * Environment: what services are you running (core services like DB and AMQP broker, as well as Nova/hypervisor if it matters), and which type of deployment (clustered servers); if you are running DevStack, is it a single node? Is it multi-node? Are you reporting an issue in your own environment or something you encountered in the OpenStack CI Infrastructure, aka the Gate? * Perceived severity: what would you consider the `importance `_ to be? * Tags (Affected component): try to use the existing tags by relying on auto-completion. Please, refrain from creating new ones, if you need new "official" tags_, please reach out to the PTL. If you would like a fix to be backported, please add a backport-potential tag. This does not mean you are gonna get the backport, as the stable team needs to follow the `stable branch policy `_ for merging fixes to stable branches. * Attachments: consider attaching logs, truncated log snippets are rarely useful. Be proactive, and consider attaching redacted configuration files if you can, as that will speed up the resolution process greatly. Bug Triage Process ~~~~~~~~~~~~~~~~~~ The process of bug triaging consists of the following steps: * Check if a bug was filed for a correct component (project). If not, either change the project or mark it as "Invalid". * For bugs that affect documentation proceed like this. If documentation affects: * the ReST API, add the "api-ref" tag to the bug. * the OpenStack manuals, like the Networking Guide or the Configuration Reference, create a patch for the affected files in the documentation directory in this repository. For a layout of the how the documentation directory is structured see the `effective neutron guide <../effective_neutron.html>`_ * developer documentation (devref), set the bug to "Confirmed" for the project Neutron, otherwise set it to "Invalid". * Check if a similar bug was filed before. Rely on your memory if Launchpad is not clever enough to spot a duplicate upon submission. You may also check already verified bugs for `Neutron `_ and `python-neutronclient `_ to see if the bug has been reported. If so, mark it as a duplicate of the previous bug. * Check if the bug meets the requirements of a good bug report, by checking that the guidelines_ are being followed. Omitted information is still acceptable if the issue is clear nonetheless; use your good judgement and your experience. Consult another core member/PTL if in doubt. If the bug report needs some love, mark the bug as 'Incomplete', point the submitter to this document and hope he/she turns around quickly with the missing information. If the bug report is sound, move next: * Revise tags as recommended by the submitter. Ensure they are 'official' tags. If the bug report talks about deprecating features or config variables, add a deprecation tag to the list. * As deputy one is usually excused not to process RFE bugs which are the responsibility of the drivers team members. * Depending on ease of reproduction (or if the issue can be spotted in the code), mark it as 'Confirmed'. If you are unable to assess/triage the issue because you do not have access to a repro environment, consider reaching out the :ref:`Lieutenant `, go-to person for the affected component; he/she may be able to help: assign the bug to him/her for further screening. If the bug already has an assignee, check that a patch is in progress. Sometimes more than one patch is required to address an issue, make sure that there is at least one patch that 'Closes' the bug or document/question what it takes to mark the bug as fixed. * If the bug indicates test or gate failure, look at the failures for that test over time using `OpenStack Health `_ or `OpenStack Logstash `_. This can help to validate whether the bug identifies an issue that is occurring all of the time, some of the time, or only for the bug submitter. * If the bug is the result of a misuse of the system, mark the bug either as 'Won't fix', or 'Opinion' if you are still on the fence and need other people's input. * Assign the importance after reviewing the proposed severity. Bugs that obviously break core and widely used functionality should get assigned as "High" or "Critical" importance. The same applies to bugs that were filed for gate failures. * Choose a milestone, if you can. Targeted bugs are especially important close to the end of the release. * (Optional). Add comments explaining the issue and possible strategy of fixing/working around the bug. Also, as good as some are at adding all thoughts to bugs, it is still helpful to share the in-progress items that might not be captured in a bug description or during our weekly meeting. In order to provide some guidance and reduce ramp up time as we rotate, tagging bugs with 'needs-attention' can be useful to quickly identify what reports need further screening/eyes on. Check for Bugs with the 'timeout-abandon' tag: * Search for any bugs with the timeout abandon tag: `Timeout abandon `_. This tag indicates that the bug had a patch associated with it that was automatically abandoned after a timing out with negative feedback. * For each bug with this tag, determine if the bug is still valid and update the status accordingly. For example, if another patch fixed the bug, ensure it's marked as 'Fix Released'. Or, if that was the only patch for the bug and it's still valid, mark it as 'Confirmed'. * After ensuring the bug report is in the correct state, remove the 'timeout-abandon' tag. You are done! Iterate. Bug Expiration Policy and Bug Squashing --------------------------------------- More can be found at this `Launchpad page `_. In a nutshell, in order to make a bug report expire automatically, it needs to be unassigned, untargeted, and marked as Incomplete. The OpenStack community has had `Bug Days `_ but they have not been wildly successful. In order to keep the list of open bugs set to a manageable number (more like <100+, rather than closer to 1000+), at the end of each release (in feature freeze and/or during less busy times), the PTL with the help of team will go through the list of open (namely new, opinion, in progress, confirmed, triaged) bugs, and do a major sweep to have the Launchpad Janitor pick them up. This gives 60 days grace period to reporters/assignees to come back and revive the bug. Assuming that at regime, bugs are properly reported, acknowledged and fix-proposed, losing unaddressed issues is not going to be a major issue, but brief stats will be collected to assess how the team is doing over time. .. _tags: Tagging Bugs ------------ Launchpad's Bug Tracker allows you to create ad-hoc groups of bugs with tagging. In the Neutron team, we have a list of agreed tags that we may apply to bugs reported against various aspects of Neutron itself. The list of approved tags used to be available on the `wiki `_, however the section has been moved here, to improve collaborative editing, and keep the information more current. By using a standard set of tags, each explained on this page, we can avoid confusion. A bug report can have more than one tag at any given time. Proposing New Tags ~~~~~~~~~~~~~~~~~~ New tags, or changes in the meaning of existing tags (or deletion), are to be proposed via patch to this section. After discussion, and approval, a member of the bug team will create/delete the tag in Launchpad. Each tag covers an area with an identified go-to contact or :ref:`Lieutenant `, who can provide further insight. Bug queries are provided below for convenience, more will be added over time if needed. +-------------------------------+-----------------------------------------+----------------------+ | Tag | Description | Contact | +===============================+=========================================+======================+ | access-control_ | A bug affecting RBAC and policy.json | Miguel Lavalle | +-------------------------------+-----------------------------------------+----------------------+ | api_ | A bug affecting the API layer | Akihiro Motoki | +-------------------------------+-----------------------------------------+----------------------+ | api-ref_ | A bug affecting the API reference | Akihiro Motoki | +-------------------------------+-----------------------------------------+----------------------+ | auto-allocated-topology_ | A bug affecting get-me-a-network | Armando Migliaccio | +-------------------------------+-----------------------------------------+----------------------+ | baremetal_ | A bug affecting Ironic support | Sukhdev Kapur | +-------------------------------+-----------------------------------------+----------------------+ | db_ | A bug affecting the DB layer | Ann Taraday | +-------------------------------+-----------------------------------------+----------------------+ | deprecation_ | To track config/feature deprecations | Neutron PTL/drivers | +-------------------------------+-----------------------------------------+----------------------+ | dns_ | A bug affecting DNS integration | Miguel Lavalle | +-------------------------------+-----------------------------------------+----------------------+ | doc_ | A bug affecting in-tree doc | Boden Russell | +-------------------------------+-----------------------------------------+----------------------+ | fullstack_ | A bug in the fullstack subtree | Jakub Libosvar | +-------------------------------+-----------------------------------------+----------------------+ | functional-tests_ | A bug in the functional tests subtree | Jakub Libosvar | +-------------------------------+-----------------------------------------+----------------------+ | fwaas_ | A bug affecting neutron-fwaas | Sridar K. | +-------------------------------+-----------------------------------------+----------------------+ | gate-failure_ | A bug affecting gate stability | Armando Migliaccio | +-------------------------------+-----------------------------------------+----------------------+ | ipv6_ | A bug affecting IPv6 support | Brian Haley | +-------------------------------+-----------------------------------------+----------------------+ | l2-pop_ | A bug in L2 Population mech driver | Miguel Lavalle | +-------------------------------+-----------------------------------------+----------------------+ | l3-bgp_ | A bug affecting neutron-dynamic-routing | Vikram Choudhary | +-------------------------------+-----------------------------------------+----------------------+ | l3-dvr-backlog_ | A bug affecting distributed routing | Swami V./ | | | | Brian Haley | +-------------------------------+-----------------------------------------+----------------------+ | l3-ha_ | A bug affecting L3 HA (vrrp) | Brian Haley | +-------------------------------+-----------------------------------------+----------------------+ | l3-ipam-dhcp_ | A bug affecting L3/DHCP/metadata | Miguel Lavalle | +-------------------------------+-----------------------------------------+----------------------+ | lib_ | An issue affecting neutron-lib | Boden Russell | +-------------------------------+-----------------------------------------+----------------------+ | linuxbridge_ | A bug affecting ML2/linuxbridge | N/A | +-------------------------------+-----------------------------------------+----------------------+ | loadimpact_ | Performance penalty/improvements | Miguel Lavalle | +-------------------------------+-----------------------------------------+----------------------+ | logging_ | An issue with logging guidelines | Matt Riedemann | +-------------------------------+-----------------------------------------+----------------------+ | low-hanging-fruit_ | Starter bugs for new contributors | Miguel Lavalle | +-------------------------------+-----------------------------------------+----------------------+ | metering_ | A bug affecting the metering layer | ? | +-------------------------------+-----------------------------------------+----------------------+ | needs-attention_ | A bug that needs further screening | PTL/Bug Deputy | +-------------------------------+-----------------------------------------+----------------------+ | opnfv_ | Reported by/affecting OPNFV initiative | Drivers team | +-------------------------------+-----------------------------------------+----------------------+ | ops_ | Reported by or affecting operators | Drivers Team | +-------------------------------+-----------------------------------------+----------------------+ | oslo_ | An interop/cross-project issue | N/A | +-------------------------------+-----------------------------------------+----------------------+ | ovs_ | A bug affecting ML2/OVS | Jakub Libosvar | +-------------------------------+-----------------------------------------+----------------------+ | ovs-fw_ | A bug affecting OVS firewall | Jakub Libosvar | +-------------------------------+-----------------------------------------+----------------------+ | ovs-lib_ | A bug affecting OVS Lib | Terry Wilson | +-------------------------------+-----------------------------------------+----------------------+ | py35_ | Issues affecting the Python 3 porting | Jakub Libosvar | +-------------------------------+-----------------------------------------+----------------------+ | qos_ | A bug affecting ML2/QoS | Slawek Kaplonski | +-------------------------------+-----------------------------------------+----------------------+ | rfe_ | Feature enhancements being screened | Drivers Team | +-------------------------------+-----------------------------------------+----------------------+ | rfe-confirmed_ | Confirmed feature enhancements | Drivers Team | +-------------------------------+-----------------------------------------+----------------------+ | rfe-triaged_ | Triaged feature enhancements | Drivers Team | +-------------------------------+-----------------------------------------+----------------------+ | rfe-approved_ | Approved feature enhancements | Drivers Team | +-------------------------------+-----------------------------------------+----------------------+ | rfe-postponed_ | Postponed feature enhancements | Drivers Team | +-------------------------------+-----------------------------------------+----------------------+ | sg-fw_ | A bug affecting security groups | Brian Haley | +-------------------------------+-----------------------------------------+----------------------+ | sriov-pci-pt_ | A bug affecting Sriov/PCI PassThrough | Moshe Levi | +-------------------------------+-----------------------------------------+----------------------+ | tempest_ | A bug in tempest subtree tests | Jakub Libosvar | +-------------------------------+-----------------------------------------+----------------------+ | troubleshooting_ | An issue affecting ease of debugging | Boden Russell | +-------------------------------+-----------------------------------------+----------------------+ | unittest_ | A bug affecting the unit test subtree | Jakub Libosvar | +-------------------------------+-----------------------------------------+----------------------+ | usability_ | UX, interoperability, feature parity | PTL/Drivers Team | +-------------------------------+-----------------------------------------+----------------------+ | xxx-backport-potential_ | Cherry-pick request for stable team | Ihar Hrachyshka/ | | | | Brian Haley | +-------------------------------+-----------------------------------------+----------------------+ .. _access-control: Access Control ++++++++++++++ * `Access Control - All bugs `_ * `Access Control - In progress `_ .. _api: API +++ * `API - All bugs `_ * `API - In progress `_ .. _api-ref: API Reference +++++++++++++ * `API Reference - All bugs `_ * `API Reference - In progress `_ .. _auto-allocated-topology: Auto Allocated Topology +++++++++++++++++++++++ * `Auto Allocated Topology - All bugs `_ * `Auto Allocated Topology - In progress `_ .. _baremetal: Baremetal +++++++++ * `Baremetal - All bugs `_ * `Baremetal - In progress `_ .. _db: DB ++ * `DB - All bugs `_ * `DB - In progress `_ .. _deprecation: Deprecation +++++++++++ * `Deprecation - All bugs `_ * `DeprecationB - In progress `_ .. _dns: DNS +++ * `DNS - All bugs `_ * `DNS - In progress `_ .. _doc: DOC +++ * `DOC - All bugs `_ * `DOC - In progress `_ .. _fullstack: Fullstack +++++++++ * `Fullstack - All bugs `_ * `Fullstack - In progress `_ .. _functional-tests: Functional Tests ++++++++++++++++ * `Functional tests - All bugs `_ * `Functional tests - In progress `_ .. _fwaas: FWAAS +++++ * `FWaaS - All bugs `_ * `FWaaS - In progress `_ .. _gate-failure: Gate Failure ++++++++++++ * `Gate failure - All bugs `_ * `Gate failure - In progress `_ .. _ipv6: IPV6 ++++ * `IPv6 - All bugs `_ * `IPv6 - In progress `_ .. _l2-pop: L2 Population +++++++++++++ * `L2 Pop - All bugs `_ * `L2 Pop - In progress `_ .. _l3-bgp: L3 BGP ++++++ * `L3 BGP - All bugs `_ * `L3 BGP - In progress `_ .. _l3-dvr-backlog: L3 DVR Backlog ++++++++++++++ * `L3 DVR - All bugs `_ * `L3 DVR - In progress `_ .. _l3-ha: L3 HA +++++ * `L3 HA - All bugs `_ * `L3 HA - In progress `_ .. _l3-ipam-dhcp: L3 IPAM DHCP ++++++++++++ * `L3 IPAM DHCP - All bugs `_ * `L3 IPAM DHCP - In progress `_ .. _lbaas: LBAAS +++++ * `LBaaS - All bugs `_ * `LBaaS - In progress `_ .. _lib: Lib +++ * `Lib - All bugs `_ .. _linuxbridge: LinuxBridge +++++++++++ * `LinuxBridge - All bugs `_ * `LinuxBridge - In progress `_ .. _loadimpact: Load Impact +++++++++++ * `Load Impact - All bugs `_ * `Load Impact - In progress `_ .. _logging: Logging +++++++ * `Logging - All bugs `_ * `Logging - In progress `_ .. _low-hanging-fruit: Low hanging fruit +++++++++++++++++ * `Low hanging fruit - All bugs `_ * `Low hanging fruit - In progress `_ .. _metering: Metering ++++++++ * `Metering - All bugs `_ * `Metering - In progress `_ .. _needs-attention: Needs Attention +++++++++++++++ * `Needs Attention - All bugs `_ .. _opnfv: OPNFV +++++ * `OPNFV - All bugs `_ .. _ops: Operators/Operations (ops) ++++++++++++++++++++++++++ * `Ops - All bugs `_ .. _oslo: OSLO ++++ * `Oslo - All bugs `_ * `Oslo - In progress `_ .. _ovs: OVS +++ * `OVS - All bugs `_ * `OVS - In progress `_ .. _ovs-fw: OVS Firewall ++++++++++++ * `OVS Firewall - All bugs `_ * `OVS Firewall - In progress `_ .. _ovs-lib: OVS Lib +++++++ * `OVS Lib - All bugs `_ * `OVS Lib - In progress `_ .. _py35: PY35 ++++ * `Py35 - All bugs `_ * `Py35 - In progress `_ .. _qos: QoS +++ * `QoS - All bugs `_ * `QoS - In progress `_ .. _rfe: RFE +++ * `RFE - All bugs `_ * `RFE - In progress `_ .. _rfe-confirmed: RFE-Confirmed +++++++++++++ * `RFE-Confirmed - All bugs `_ .. _rfe-triaged: RFE-Triaged +++++++++++ * `RFE-Triaged - All bugs `_ .. _rfe-approved: RFE-Approved ++++++++++++ * `RFE-Approved - All bugs `_ * `RFE-Approved - In progress `_ .. _rfe-postponed: RFE-Postponed +++++++++++++ * `RFE-Postponed - All bugs `_ * `RFE-Postponed - In progress `_ .. _sriov-pci-pt: SRIOV-PCI PASSTHROUGH +++++++++++++++++++++ * `SRIOV/PCI-PT - All bugs `_ * `SRIOV/PCI-PT - In progress `_ .. _sg-fw: SG-FW +++++ * `Security groups - All bugs `_ * `Security groups - In progress `_ .. _tempest: Tempest +++++++ * `Tempest - All bugs `_ * `Tempest - In progress `_ .. _troubleshooting: Troubleshooting +++++++++++++++ * `Troubleshooting - All bugs `_ * `Troubleshooting - In progress `_ .. _unittest: Unit test +++++++++ * `Unit test - All bugs `_ * `Unit test - In progress `_ .. _usability: Usability +++++++++ * `UX - All bugs `_ * `UX - In progress `_ .. _vpnaas: VPNAAS ++++++ * `VPNaaS - All bugs `_ * `VPNaaS - In progress `_ .. _xxx-backport-potential: Backport/RC potential +++++++++++++++++++++ List of all ``Backport/RC potential`` bugs for stable releases can be found on launchpad. Pointer to Launchpad's page with list of such bugs for any stable release can be built by using link: https://bugs.launchpad.net/neutron/+bugs?field.tag={STABLE_BRANCH}-backport-potential where ``STABLE_BRANCH`` is always name of one of the 3 latest releases. neutron-12.1.1/doc/source/contributor/policies/gate-failure-triage.rst0000664000175000017500000001141313553660047026117 0ustar zuulzuul00000000000000Neutron Gate Failure Triage =========================== This page provides guidelines for spotting and assessing neutron gate failures. Some hints for triaging failures are also provided. Spotting Gate Failures ---------------------- This can be achieved using several tools: * `Grafana dashboard `_ * `logstash `_ For checking gate failures with logstash the following query will return failures for a specific job: > build_status:FAILURE AND message:Finished AND build_name:"check-tempest-dsvm-neutron" AND build_queue:"gate" And divided by the total number of jobs executed: > message:Finished AND build_name:"check-tempest-dsvm-neutron" AND build_queue:"gate" It will return the failure rate in the selected period for a given job. It is important to remark that failures in the check queue might be misleading as the problem causing the failure is most of the time in the patch being checked. Therefore it is always advisable to work on failures occurred in the gate queue. However, these failures are a precious resource for assessing frequency and determining root cause of failures which manifest in the gate queue. The step above will provide a quick outlook of where things stand. When the failure rate raises above 10% for a job in 24 hours, it's time to be on alert. 25% is amber alert. 33% is red alert. Anything above 50% means that probably somebody from the infra team has already a contract out on you. Whether you are relaxed, in alert mode, or freaking out because you see a red dot on your chest, it is always a good idea to check on daily bases the elastic-recheck pages. Under the `gate pipeline `_ tab, you can see gate failure rates for already known bugs. The bugs in this page are ordered by decreasing failure rates (for the past 24 hours). If one of the bugs affecting Neutron is among those on top of that list, you should check that the corresponding bug is already assigned and somebody is working on it. If not, and there is not a good reason for that, it should be ensured somebody gets a crack at it as soon as possible. The other part of the story is to check for `uncategorized `_ failures. This is where failures for new (unknown) gate breaking bugs end up; on the other hand also infra error causing job failures end up here. It should be duty of the diligent Neutron developer to ensure the classification rate for neutron jobs is as close as possible to 100%. To this aim, the diligent Neutron developer should adopt the procedure outlined in the following sections. .. _troubleshooting-tempest-jobs: Troubleshooting Tempest jobs ---------------------------- 1. Open logs for failed jobs and look for logs/testr_results.html.gz. 2. If that file is missing, check console.html and see where the job failed. 1. If there is a failure in devstack-gate-cleanup-host.txt it's likely to be an infra issue. 2. If the failure is in devstacklog.txt it could a devstack, neutron, or infra issue. 3. However, most of the time the failure is in one of the tempest tests. Take note of the error message and go to logstash. 4. On logstash, search for occurrences of this error message, and try to identify the root cause for the failure (see below). 5. File a bug for this failure, and push an :ref:`Elastic Recheck Query ` for it. 6. If you are confident with the area of this bug, and you have time, assign it to yourself; otherwise look for an assignee or talk to the Neutron's bug czar to find an assignee. Troubleshooting functional/fullstack job ---------------------------------------- 1. Go to the job link provided by Jenkins CI. 2. Look at logs/testr_results.html.gz for which particular test failed. 3. More logs from a particular test are stored at logs/dsvm-functional-logs/ (or dsvm-fullstack-logs for fullstack job). 4. Find the error in the logs and search for similar errors in existing launchpad bugs. If no bugs were reported, create a new bug report. Don't forget to put a snippet of the trace into the new launchpad bug. If the log file for a particular job doesn't contain any trace, pick the one from testr_results.html.gz. 5. Create an :ref:`Elastic Recheck Query ` Root Causing a Gate Failure --------------------------- Time-based identification, i.e. find the naughty patch by log scavenging. .. _elastic-recheck-query: Filing An Elastic Recheck Query ------------------------------- The `elastic recheck `_ page has all the current open ER queries. To file one, please see the `ER Wiki `_. neutron-12.1.1/doc/source/contributor/policies/code-reviews.rst0000664000175000017500000001131013553660047024671 0ustar zuulzuul00000000000000Neutron Code Reviews ==================== Code reviews are a critical component of all OpenStack projects. Neutron accepts patches from many diverse people with diverse backgrounds, employers, and experience levels. Code reviews provide a way to enforce a level of consistency across the project, and also allow for the careful on boarding of contributions from new contributors. Neutron Code Review Practices ----------------------------- Neutron follows the `code review guidelines `_ as set forth for all OpenStack projects. It is expected that all reviewers are following the guidelines set forth on that page. In addition to that, the following rules are to follow: * Any change that requires a new feature from Neutron runtime dependencies requires special review scrutiny to make sure such a change does not break a supported platform (examples of those platforms are latest Ubuntu LTS or CentOS). Runtime dependencies include but are not limited to: kernel, daemons and tools as defined in ``oslo.rootwrap`` filter files, runlevel management systems, as well as other elements of Neutron execution environment. .. note:: For some components, the list of supported platforms can be wider than usual. For example, Open vSwitch agent is expected to run successfully in Win32 runtime environment. #. All such changes must be tagged with ``UpgradeImpact`` in their commit messages. #. Reviewers are then advised to make an effort to check if the newly proposed runtime dependency is fulfilled on supported platforms. #. Specifically, reviewers and authors are advised to use existing gate and experimental platform specific jobs to validate those patches. To trigger experimental jobs, use the usual protocol (posting ``check experimental`` comment in Gerrit). CI will then execute and report back a baseline of Neutron tests for platforms of interest and will provide feedback on the effect of the runtime change required. #. If review identifies that the proposed change would break a supported platform, advise to rework the patch so that it's no longer breaking the platform. One of the common ways of achieving that is gracefully falling back to alternative means on older platforms, another is hiding the new code behind a conditional, potentially controlled with a ``oslo.config`` option. .. note:: Neutron team retains the right to remove any platform conditionals in future releases. Platform owners are expected to accommodate in due course, or otherwise see their platforms broken. The team also retains the right to discontinue support for unresponsive platforms. #. The change should also include a new `sanity check `_ that would help interested parties to identify their platform limitation in timely manner. .. _spec-review-practices: Neutron Spec Review Practices ----------------------------- In addition to code reviews, Neutron also maintains a BP specification git repository. Detailed instructions for the use of this repository are provided `here `_. It is expected that Neutron core team members are actively reviewing specifications which are pushed out for review to the specification repository. In addition, there is a neutron-drivers team, composed of a handful of Neutron core reviewers, who can approve and merge Neutron specs. Some guidelines around this process are provided below: * Once a specification has been pushed, it is expected that it will not be approved for at least 3 days after a first Neutron core reviewer has reviewed it. This allows for additional cores to review the specification. * For blueprints which the core team deems of High or Critical importance, core reviewers may be assigned based on their subject matter expertise. * Specification priority will be set by the PTL with review by the core team once the specification is approved. Tracking Review Statistics -------------------------- Stackalytics provides some nice interfaces to track review statistics. The links are provided below. These statistics are used to track not only Neutron core reviewer statistics, but also to track review statistics for potential future core members. * `30 day review stats `_ * `60 day review stats `_ * `90 day review stats `_ * `180 day review stats `_ neutron-12.1.1/doc/source/contributor/policies/blueprints.rst0000664000175000017500000004214213553660047024473 0ustar zuulzuul00000000000000Blueprints and Specs ==================== The Neutron team uses the `neutron-specs `_ repository for its specification reviews. Detailed information can be found on the `wiki `_. Please also find additional information in the reviews.rst file. The Neutron team does not enforce deadlines for specs. These can be submitted throughout the release cycle. The drivers team will review this on a regular basis throughout the release, and based on the load for the milestones, will assign these into milestones or move them to the backlog for selection into a future release. Please note that we use a `template `_ for spec submissions. It is not required to fill out all sections in the template. Review of the spec may require filling in information left out by the submitter. Sub-Projects and Specs ---------------------- The `neutron-specs `_ repository is only meant for specs from Neutron itself, and the advanced services repositories as well. This includes FWaaS, LBaaS, and VPNaaS. Other sub-projects are encouraged to fold their specs into their own devref code in their sub-project gerrit repositories. Please see additional comments in the Neutron teams :ref:`section ` for reviewer requirements of the neutron-specs repository. .. _request-for-feature-enhancement: Neutron Request for Feature Enhancements ---------------------------------------- In Liberty the team introduced the concept of feature requests. Feature requests are tracked as Launchpad bugs, by tagging them with a set of tags starting with `rfe`, enabling the submission and review of feature requests before code is submitted. This allows the team to verify the validity of a feature request before the process of submitting a neutron-spec is undertaken, or code is written. It also allows the community to express interest in a feature by subscribing to the bug and posting a comment in Launchpad. The 'rfe' tag should not be used for work that is already well-defined and has an assignee. If you are intending to submit code immediately, a simple bug report will suffice. Note the temptation to game the system exists, but given the history in Neutron for this type of activity, it will not be tolerated and will be called out as such in public on the mailing list. RFEs can be submitted by anyone and by having the community vote on them in Launchpad, we can gauge interest in features. The drivers team will evaluate these on a weekly basis along with the specs. RFEs will be evaluated in the current cycle against existing project priorities and available resources. The workflow for the life an RFE in Launchpad is as follows: * The bug is submitted and will by default land in the "New" state. Anyone can make a bug an RFE by adding the `rfe` tag. * As soon as a member of the neutron-drivers team acknowledges the bug, the `rfe` tag will be replaced with the `rfe-confirmed` tag. No assignee, or milestone is set at this time. The importance will be set to 'Wishlist' to signal the fact that the report is indeed a feature or enhancement and there is no severity associated to it. * A member of the neutron-drivers team replaces the `rfe-confirmed` tag with the `rfe-triaged` tag when he/she thinks it's ready to be discussed in the drivers meeting. The bug will be in this state while the discussion is ongoing. * The neutron-drivers team will evaluate the RFE and may advise the submitter to file a spec in neutron-specs to elaborate on the feature request, in case the RFE requires extra scrutiny, more design discussion, etc. * The PTL will work with the Lieutenant for the area being identified by the RFE to evaluate resources against the current workload. * A member of the Neutron release team (or the PTL) will register a matching Launchpad blueprint to be used for milestone tracking purposes, and for identifying the responsible assignee and approver. If the RFE has a spec the blueprint will have a pointer to the spec document, which will become available on `specs.o.o. `_ once it is approved and merged. The blueprint will then be linked to the original RFE bug report as a pointer to the discussion that led to the approval of the RFE. The blueprint submitter will also need to identify the following: * Priority: there will be only two priorities to choose from, High and Low. It is worth noting that priority is not to be confused with `importance `_, which is a property of Launchpad Bugs. Priority gives an indication of how promptly a work item should be tackled to allow it to complete. High priority is to be chosen for work items that must make substantial progress in the span of the targeted release, and deal with the following aspects: * OpenStack cross-project interaction and interoperability issues; * Issues that affect the existing system's usability; * Stability and testability of the platform; * Risky implementations that may require complex and/or pervasive changes to API and the logical model; Low priority is to be chosen for everything else. RFEs without an associated blueprint are effectively equivalent to low priority items. Bear in mind that, even though staffing should take priorities into account (i.e. by giving more resources to high priority items over low priority ones), the open source reality is that they can both proceed at their own pace and low priority items can indeed complete faster than high priority ones, even though they are given fewer resources. * Drafter: who is going to submit and iterate on the spec proposal; he/she may be the RFE submitter. * Assignee: who is going to develop the bulk of the code, or the go-to contributor, if more people are involved. Typically this is the RFE submitter, but not necessarily. * Approver: a member of the Neutron team who can commit enough time during the ongoing release cycle to ensure that code posted for review does not languish, and that all aspects of the feature development are taken care of (client, server changes and/or support from other projects if needed - tempest, nova, openstack-infra, devstack, etc.), as well as comprehensive testing. This is typically a core member who has enough experience with what it takes to get code merged, but other resources amongst the wider team can also be identified. Approvers are volunteers who show a specific interest in the blueprint specification, and have enough insight in the area of work so that they can make effective code reviews and provide design feedback. An approver will not work in isolation, as he/she can and will reach out for help to get the job done; however he/she is the main point of contact with the following responsibilities: * Pair up with the drafter/assignee in order to help skip development blockers. * Review patches associated with the blueprint: approver and assignee should touch base regularly and ping each other when new code is available for review, or if review feedback goes unaddressed. * Reach out to other reviewers for feedback in areas that may step out of the zone of her/his confidence. * Escalate issues, and raise warnings to the release team/PTL if the effort shows slow progress. Approver and assignee are key parts to land a blueprint: should the approver and/or assignee be unable to continue the commitment during the release cycle, it is the Approver's responsibility to reach out the release team/PTL so that replacements can be identified. * Provide a status update during the Neutron IRC meeting, if required. Approver `assignments `_ must be carefully identified to ensure that no-one overcommits. A Neutron contributor develops code himself/herself, and if he/she is an approver of more than a couple of blueprints in a single cycle/milestone (depending on the complexity of the spec), it may mean that he/she is clearly oversubscribed. The Neutron team will review the status of blueprints targeted for the milestone during their weekly meeting to ensure a smooth progression of the work planned. Blueprints for which resources cannot be identified will have to be deferred. * In either case (a spec being required or not), once the discussion has happened and there is positive consensus on the RFE, the report is 'approved', and its tag will move from `rfe-triaged` to `rfe-approved`. * An RFE can be occasionaly marked as 'rfe-postponed' if the team identifies a dependency between the proposed RFE and other pending tasks that prevent the RFE from being worked on immediately. * Once an RFE is approved, it needs volunteers. Approved RFEs that do not have an assignee but sound relatively simple or limited in scope (e.g. the addition of a new API with no ramification in the plugin backends), should be promoted during team meetings or the ML so that volunteers can pick them up and get started with neutron development. The team will regularly scan `rfe-approved` or `rfe-postponed` RFEs to see what their latest status is and mark them incomplete if no assignees can be found, or they are no longer relevant. * As for setting the milestone (both for RFE bugs or blueprints), the current milestone is always chosen, assuming that work will start as soon as the feature is approved. Work that fails to complete by the defined milestone will roll over automatically until it gets completed or abandoned. * If the code fails to merge, the bug report may be marked as incomplete, unassigned and untargeted, and it will be garbage collected by the Launchpad Janitor if no-one takes over in time. Renewed interest in the feature will have to go through RFE submission process once again. In summary: +------------+-----------------------------------------------------------------------------+ |State | Meaning | +============+=============================================================================+ |New | This is where all RFE's start, as filed by the community. | +------------+-----------------------------------------------------------------------------+ |Incomplete | Drivers/LTs - Move to this state to mean, "more needed before proceeding" | +------------+-----------------------------------------------------------------------------+ |Confirmed | Drivers/LTs - Move to this state to mean, "yeah, I see that you filed it" | +------------+-----------------------------------------------------------------------------+ |Triaged | Drivers/LTs - Move to this state to mean, "discussion is ongoing" | +------------+-----------------------------------------------------------------------------+ |Won't Fix | Drivers/LTs - Move to this state to reject an RFE. | +------------+-----------------------------------------------------------------------------+ Once the triaging (discussion is complete) and the RFE is approved, the tag goes from 'rfe' to 'rfe-approved', and at this point the bug report goes through the usual state transition. Note, that the importance will be set to 'wishlist', to reflect the fact that the bug report is indeed not a bug, but a new feature or enhancement. This will also help have RFEs that are not followed up by a blueprint standout in the Launchpad `milestone dashboards `_. The drivers team will be discussing the following bug reports during their IRC meeting: * `New RFE's `_ * `Incomplete RFE's `_ * `Confirmed RFE's `_ * `Triaged RFE's `_ RFE Submission Guidelines ------------------------- Before we dive into the guidelines for writing a good RFE, it is worth mentioning that depending on your level of engagement with the Neutron project and your role (user, developer, deployer, operator, etc.), you are more than welcome to have a preliminary discussion of a potential RFE by reaching out to other people involved in the project. This usually happens by posting mails on the relevant mailing lists (e.g. `openstack-dev `_ - include [neutron] in the subject) or on #openstack-neutron IRC channel on Freenode. If current ongoing code reviews are related to your feature, posting comments/questions on gerrit may also be a way to engage. Some amount of interaction with Neutron developers will give you an idea of the plausibility and form of your RFE before you submit it. That said, this is not mandatory. When you submit a bug report on https://bugs.launchpad.net/neutron/+filebug, there are two fields that must be filled: 'summary' and 'further information'. The 'summary' must be brief enough to fit in one line: if you can't describe it in a few words it may mean that you are either trying to capture more than one RFE at once, or that you are having a hard time defining what you are trying to solve at all. The 'further information' section must be a description of what you would like to see implemented in Neutron. The description should provide enough details for a knowledgeable developer to understand what is the existing problem in the current platform that needs to be addressed, or what is the enhancement that would make the platform more capable, both for a functional and a non-functional standpoint. To this aim it is important to describe 'why' you believe the RFE should be accepted, and motivate the reason why without it Neutron is a poorer platform. The description should be self contained, and no external references should be necessary to further explain the RFE. In other words, when you write an RFE you should ask yourself the following questions: * What is that I (specify what user - a user can be a human or another system) cannot do today when interacting with Neutron? On the other hand, is there a Neutron component X that is unable to accomplish something? * Is there something that you would like Neutron handle better, ie. in a more scalable, or in a more reliable way? * What is that I would like to see happen after the RFE is accepted and implemented? * Why do you think it is important? Once you are happy with what you wrote, add 'rfe' as tag, and submit. Do not worry, we are here to help you get it right! Happy hacking. Missing your target ------------------- There are occasions when a spec will be approved and the code will not land in the cycle it was targeted at. For these cases, the work flow to get the spec into the next release is as follows: * During the RC window, the PTL will create a directory named '' under the 'backlog' directory in the neutron specs repo, and he/she will move all specs that did not make the release to this directory. * Anyone can propose a patch to neutron-specs which moves a spec from the previous release into the new release directory. The specs which are moved in this way can be fast-tracked into the next release. Please note that it is required to re-propose the spec for the new release. Documentation ------------- The above process involves two places where any given feature can start to be documented - namely in the RFE bug, and in the spec - and in addition to those Neutron has a substantial :doc:`developer reference guide ` (aka 'devref'), and user-facing docs such as the :doc:`networking guide `. So it might be asked: * What is the relationship between all of those? * What is the point of devref documentation, if everything has already been described in the spec? The answers have been beautifully expressed in an `openstack-dev post `_: 1. RFE: "I want X" 2. Spec: "I plan to implement X like this" 3. devref: "How X is implemented and how to extend it" 4. OS docs: "API and guide for using X" Once a feature X has been implemented, we shouldn't have to go to back to its RFE bug or spec to find information on it. The devref may reuse a lot of content from the spec, but the spec is not maintained and the implementation may differ in some ways from what was intended when the spec was agreed. The devref should be kept current with refactorings, etc., of the implementation. Devref content should be added as part of the implementation of a new feature. Since the spec is not maintained after the feature is implemented, the devref should include a maintained version of the information from the spec. If a feature requires OS docs (4), the feature patch shall include the new, or updated, documentation changes. If the feature is purely a developer facing thing, (4) is not needed. neutron-12.1.1/doc/source/contributor/policies/index.rst0000664000175000017500000000212013553660046023402 0ustar zuulzuul00000000000000.. Copyright 2014 Hewlett-Packard Development Company, L.P. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Neutron Policies ================ In the Policies Guide, you will find documented policies for developing with Neutron. This includes the processes we use for blueprints and specs, bugs, contributor onboarding, core reviewer memberships, and other procedural items. .. toctree:: :maxdepth: 3 blueprints bugs contributor-onboarding neutron-teams gate-failure-triage code-reviews release-checklist thirdparty-ci gerrit-recheck neutron-12.1.1/doc/source/contributor/policies/thirdparty-ci.rst0000664000175000017500000001356313553660047025074 0ustar zuulzuul00000000000000Neutron Third-party CI ====================== What Is Expected of Third Party CI System for Neutron ----------------------------------------------------- As of the Liberty summit, Neutron no longer *requires* a third-party CI, but it is strongly encouraged, as internal neutron refactoring can break external plugins and drivers at any time. Neutron expects any Third Party CI system that interacts with gerrit to follow the requirements set by the Infrastructure team [1]_ as well as the Neutron Third Party CI guidelines below. Please ping the PTL in #openstack-neutron or send an email to the openstack-dev ML (with subject [neutron]) with any questions. Be aware that the Infrastructure documentation as well as this document are living documents and undergo changes. Track changes to the infrastructure documentation using this url [2]_ (and please review the patches) and check this doc on a regular basis for updates. What Changes to Run Against --------------------------- If your code is a neutron plugin or driver, you should run against every neutron change submitted, except for docs, tests, tools, and top-level setup files. You can skip your CI runs for such exceptions by using ``skip-if`` and ``all-files-match-any`` directives in Zuul. You can see a programmatic example of the exceptions here [3]_. If your code is in a neutron-\*aas repo, you should run against the tests for that repo. You may also run against every neutron change, if your service driver is using neutron interfaces that are not provided by your service plugin (e.g. loadbalancer/plugin.py). If you are using only plugin interfaces, it should be safe to test against only the service repo tests. What Tests To Run ----------------- Network API tests (git link). Network scenario tests (The test_network_* tests here). Any tests written specifically for your setup. http://git.openstack.org/cgit/openstack/tempest/tree/tempest/api/network Run with the test filter: 'network'. This will include all neutron specific tests as well as any other tests that are tagged as requiring networking. An example tempest setup for devstack-gate:: export DEVSTACK_GATE_NEUTRON=1 export DEVSTACK_GATE_TEMPEST_REGEX='(?!.*\[.*\bslow\b.*\])((network)|(neutron))' An example setup for LBaaS:: export DEVSTACK_GATE_NEUTRON=1 export DEVSTACK_GATE_TEMPEST_REGEX='(?!.*\[.*\bslow\b.*\])(alancer|SimpleReadOnlyNeutron|tempest.api.network)' Third Party CI Voting --------------------- The Neutron team encourages you to NOT vote -1 with a third-party CI. False negatives are noisy to the community, and have given -1 from third-party CIs a bad reputation. Really bad, to the point of people ignoring them all. Failure messages are useful to those doing refactors, and provide you feedback on the state of your plugin. If you insist on voting, by default, the infra team will not allow voting by new 3rd party CI systems. The way to get your 3rd party CI system to vote is to talk with the Neutron PTL, who will let infra know the system is ready to vote. The requirements for a new system to be given voting rights are as follows: * A new system must be up and running for a month, with a track record of voting on the sandbox system. * A new system must correctly run and pass tests on patches for the third party driver/plugin for a month. * A new system must have a logfile setup and retention setup similar to the below. Once the system has been running for a month, the owner of the third party CI system can contact the Neutron PTL to have a conversation about getting voting rights upstream. The general process to get these voting rights is outlined here. Please follow that, taking note of the guidelines Neutron also places on voting for it's CI systems. A third party system can have it's voting rights removed as well. If the system becomes unstable (stops running, voting, or start providing inaccurate results), the Neutron PTL or any core reviewer will make an attempt to contact the owner and copy the openstack-dev mailing list. If no response is received within 2 days, the Neutron PTL will remove voting rights for the third party CI system. If a response is received, the owner will work to correct the issue. If the issue cannot be addressed in a reasonable amount of time, the voting rights will be temporarily removed. Log & Test Results Filesystem Layout ------------------------------------ Third-Party CI systems MUST provide logs and configuration data to help developers troubleshoot test failures. A third-party CI that DOES NOT post logs should be a candidate for removal, and new CI systems MUST post logs before they can be awarded voting privileges. Third party CI systems should follow the filesystem layout convention of the OpenStack CI system. Please store your logs as viewable in a web browser, in a directory structure. Requiring the user to download a giant tarball is not acceptable, and will be reason to not allow your system to vote from the start, or cancel it's voting rights if this changes while the system is running. At the root of the results - there should be the following: * console.html.gz - contains the output of stdout of the test run * local.conf / localrc - contains the setup used for this run * logs - contains the output of detail test log of the test run The above "logs" must be a directory, which contains the following: * Log files for each screen session that DevStack creates and launches an OpenStack component in * Test result files * testr_results.html.gz * tempest.txt.gz List of existing plugins and drivers ------------------------------------ https://wiki.openstack.org/wiki/Neutron_Plugins_and_Drivers#Existing_Plugin_and_Drivers References ---------- .. [1] http://ci.openstack.org/third_party.html .. [2] https://review.openstack.org/#/q/status:open+project:openstack-infra/system-config+branch:master+topic:third-party,n,z .. [3] https://github.com/openstack-infra/project-config/blob/master/zuul/layout.yaml neutron-12.1.1/doc/source/contributor/policies/contributor-onboarding.rst0000664000175000017500000000464013553660047026777 0ustar zuulzuul00000000000000Contributor Onboarding ====================== For new contributors, the following are useful onboarding information. Contributing to Neutron ----------------------- Work within Neutron is discussed on the openstack-dev mailing list, as well as in the #openstack-neutron IRC channel. While these are great channels for engaging Neutron, the bulk of discussion of patches and code happens in gerrit itself. With regards to gerrit, code reviews are a great way to learn about the project. There is also a list of `low or wishlist `_ priority bugs which are ideal for a new contributor to take on. If you haven't done so you should setup a Neutron development environment so you can actually run the code. Devstack is the usual convenient environment to setup such an environment. See `devstack.org `_ or `NeutronDevstack `_ for more information on using Neutron with devstack. Helping with documentation can also be a useful first step for a newcomer. Here is a list of tagged documentation and API reference bugs: * `Documentation bugs `_ * `Api-ref bugs `_ IRC Information and Etiquette ----------------------------- The main IRC channel for Neutron is #openstack-neutron. We also utilize #openstack-lbaas for LBaaS specific discussions. The weekly meeting is documented in the `list of meetings `_ wiki page. neutron-12.1.1/doc/source/contributor/policies/gerrit-recheck.rst0000664000175000017500000000306013553660046025175 0ustar zuulzuul00000000000000Recheck Failed CI jobs in Neutron ================================= This document provides guidelines on what to do in case your patch fails one of the Jenkins CI jobs. In order to discover potential bugs hidden in the code or tests themselves, it's very helpful to check failed scenarios to investigate the cause of the failure. Sometimes the failure will be caused by the patch being tested, while other times the failure can be caused by a previously untracked bug. Such failures are usually related to tests that interact with a live system, like functional, fullstack and tempest jobs. Before issuing a recheck on your patch, make sure that the gate failure is not caused by your patch. Failed job can be also caused by some infra issue, for example unable to fetch things from external resources like git or pip due to outage. Such failures outside of OpenStack world are not worth tracking in launchpad and you can recheck leaving couple of words what went wrong. Data about gate stability is collected and visualized via `Grafana `_. Please, do not recheck without providing the bug number for the failed job. For example, do not just put an empty "recheck" comment but find the related bug number and put a "recheck bug ######" comment instead. If a bug does not exist yet, create one so other team members can have a look. It helps us maintain better visibility of gate failures. You can find how to troubleshoot gate failures in the :ref:`Gate Failure Triage ` documentation. neutron-12.1.1/doc/source/contributor/policies/neutron-teams.rst0000664000175000017500000004146013553660047025107 0ustar zuulzuul00000000000000Neutron Core Reviewers ====================== The `Neutron Core Reviewer Team `_ is responsible for many things related to Neutron. A lot of these things include mundane tasks such as the following: * Ensuring the bug count is low * Curating the gate and triaging failures * Working on integrating shared code from projects such as Oslo * Ensuring documentation is up to date and remains relevant * Ensuring the level of testing for Neutron is adequate and remains relevant as features are added * Helping new contributors with questions as they peel back the covers of Neutron * Answering questions and participating in mailing list discussions * Interfacing with other OpenStack teams and ensuring they are going in the same parallel direction * Reviewing and merging code into the neutron tree In essence, core reviewers share the following common ideals: 1. They share responsibility in the project's success. 2. They have made a long-term, recurring time investment to improve the project. 3. They spend their time doing what needs to be done to ensure the projects success, not necessarily what is the most interesting or fun. A core reviewer's responsibility doesn't end up with merging code. The above lists are adding context around these responsibilities. .. _core-review-hierarchy: Core Review Hierarchy --------------------- As Neutron has grown in complexity, it has become impossible for any one person to know enough to merge changes across the entire codebase. Areas of expertise have developed organically, and it is not uncommon for existing cores to defer to these experts when changes are proposed. Existing cores should be aware of the implications when they do merge changes outside the scope of their knowledge. It is with this in mind we propose a new system built around Lieutenants through a model of trust. In order to scale development and responsibility in Neutron, we have adopted a Lieutenant system. The PTL is the leader of the Neutron project, and ultimately responsible for decisions made in the project. The PTL has designated Lieutenants in place to help run portions of the Neutron project. The Lieutenants are in charge of their own areas, and they can propose core reviewers for their areas as well. The core reviewer addition and removal polices are in place below. The Lieutenants for each system, while responsible for their area, ultimately report to the PTL. The PTL may opt to have regular one on one meetings with the lieutenants. The PTL will resolve disputes in the project that arise between areas of focus, core reviewers, and other projects. Please note Lieutenants should be leading their own area of focus, not doing all the work themselves. As was mentioned in the previous section, a core's responsibilities do not end with merging code. They are responsible for bug triage and gate issues among other things. Lieutenants have an increased responsibility to ensure gate and bug triage for their area of focus is under control. The following are the current Neutron Lieutenants. +------------------------+---------------------------+----------------------+ | Area | Lieutenant | IRC nick | +========================+===========================+======================+ | API | Akihiro Motoki | amotoki | +------------------------+---------------------------+----------------------+ | DB | Ann Taraday | ataraday | +------------------------+---------------------------+----------------------+ | Built-In Control Plane | Miguel Lavalle | mlavalle | +------------------------+---------------------------+----------------------+ | Client | Akihiro Motoki | amotoki | | +---------------------------+----------------------+ | | Abhishek Raut | abhiraut | +------------------------+---------------------------+----------------------+ | Docs | Boden Russell | boden | +------------------------+---------------------------+----------------------+ | Infra | Ihar Hrachyshka | ihrachys | | +---------------------------+----------------------+ | | Armando Migliaccio | armax | | +---------------------------+----------------------+ | | YAMAMOTO Takashi | yamamoto | +------------------------+---------------------------+----------------------+ | L3 | Brian Haley | haleyb | + +---------------------------+----------------------+ | | Miguel Lavalle | mlavalle | +------------------------+---------------------------+----------------------+ | Testing | Jakub Libosvar | jlibosva | +------------------------+---------------------------+----------------------+ Some notes on the above: * "Built-In Control Plane" means the L2 agents, DHCP agents, SGs, metadata agents and ML2. * The client includes commands installed server side. * L3 includes the L3 agent, DVR, Dynamic routing and IPAM. * Services includes FWaaS, LBaaS, and VPNaaS. * Note these areas may change as the project evolves due to code refactoring, new feature areas, and libification of certain pieces of code. * Infra means interactions with infra from a neutron perspective Neutron also consists of several plugins, drivers, and agents that are developed effectively as sub-projects within Neutron in their own git repositories. Lieutenants are also named for these sub-projects to identify a clear point of contact and leader for that area. The Lieutenant is also responsible for updating the core review team for the sub-project's repositories. +------------------------+---------------------------+----------------------+ | Area | Lieutenant | IRC nick | +========================+===========================+======================+ | networking-bgpvpn | Mathieu Rohon | matrohon | | networking-bagpipe +---------------------------+----------------------+ | | Thomas Morin | tmorin | +------------------------+---------------------------+----------------------+ | net...-dynamic-routing | Ryan Tidwell | tidwellr | | +---------------------------+----------------------+ | | Vikram Choudhary | vikram | +------------------------+---------------------------+----------------------+ | neutron-fwaas | Sridar Kandaswamy | SridarK | +------------------------+---------------------------+----------------------+ | networking-midonet | Ryu Ishimoto | ryu25 | | +---------------------------+----------------------+ | | YAMAMOTO Takashi | yamamoto | +------------------------+---------------------------+----------------------+ | networking-odl | Isaku Yamahata | yamahata | +------------------------+---------------------------+----------------------+ | networking-ovn | Russell Bryant | russellb | +------------------------+---------------------------+----------------------+ | networking-sfc | Cathy Zhang | cathy | +------------------------+---------------------------+----------------------+ Existing Core Reviewers ----------------------- Existing core reviewers have been reviewing code for a varying degree of cycles. With the new plan of Lieutenants and ownership, it's fair to try to understand how they fit into the new model. Existing core reviewers seem to mostly focus in particular areas and are cognizant of their own strengths and weaknesses. These members may not be experts in all areas, but know their limits, and will not exceed those limits when reviewing changes outside their area of expertise. The model is built on trust, and when that trust is broken, responsibilities will be taken away. Lieutenant Responsibilities --------------------------- In the hierarchy of Neutron responsibilities, Lieutenants are expected to partake in the following additional activities compared to other core reviewers: * Ensuring feature requests for their areas have adequate testing and documentation coverage. * Gate triage and resolution. Lieutenants are expected to work to keep the Neutron gate running smoothly by triaging issues, filing elastic recheck queries, and closing gate bugs. * Triaging bugs for the specific areas. Neutron Teams ============= Given all of the above, Neutron has a number of core reviewer teams with responsibility over the areas of code listed below: Neutron Core Reviewer Team -------------------------- `Neutron core reviewers `_ have merge rights to the following git repositories: * `openstack/neutron `_ * `openstack/python-neutronclient `_ Please note that as we adopt to the system above with core specialty in particular areas, we expect this broad core team to shrink as people naturally evolve into an area of specialization. Neutron Core Reviewer Teams for Plugins and Drivers --------------------------------------------------- The plugin decomposition effort has led to having many drivers with code in separate repositories with their own core reviewer teams. For each one of these repositories in the following repository list, there is a core team associated with it: * `Neutron project team `_ These teams are also responsible for handling their own specs/RFEs/features if they choose to use them. However, by choosing to be a part of the Neutron project, they submit to oversight and veto by the Neutron PTL if any issues arise. .. _specs-core-reviewer-team: Neutron Specs Core Reviewer Team -------------------------------- Neutron `specs core reviewers `_ have +2 rights to the following git repositories: * `openstack/neutron-specs `_ The Neutron specs core reviewer team is responsible for reviewing specs targeted to all Neutron git repositories (Neutron + Advanced Services). It is worth noting that specs reviewers have the following attributes which are potentially different than code reviewers: * Broad understanding of cloud and networking technologies * Broad understanding of core OpenStack projects and technologies * An understanding of the effect approved specs have on the teams development capacity for each cycle Specs core reviewers may match core members of the above mentioned groups, but the group can be extended to other individuals, if required. Drivers Team ------------ The `drivers team `_ is the group of people who have full rights to the specs repo. This team, which matches `Launchpad Neutron Drivers team `_, is instituted to ensure a consistent architectural vision for the Neutron project, and to continue to disaggregate and share the responsibilities of the Neutron PTL. The team is in charge of reviewing and commenting on :ref:`RFEs `, and working with specification contributors to provide guidance on the process that govern contributions to the Neutron project as a whole. The team `meets regularly `_ to go over RFE's and discuss the project roadmap. Anyone is welcome to join and/or read the meeting notes. Release Team ------------ The `release team `_ is a group of people with some additional gerrit permissions primarily aimed at allowing release management of Neutron sub-projects. These permissions include: * Ability to push signed tags to sub-projects whose releases are managed by the Neutron release team as opposed to the OpenStack release team. * Ability to push merge commits for Neutron or other sub-projects. * Ability to approve changes in all Neutron git repositories. This is required as the team needs to be able to quickly unblock things if needed, especially at release time. Code Merge Responsibilities =========================== While everyone is encouraged to review changes for these repositories, members of the Neutron core reviewer group have the ability to +2/-2 and +A changes to these repositories. This is an extra level of responsibility not to be taken lightly. Correctly merging code requires not only understanding the code itself, but also how the code affects things like documentation, testing, and interactions with other projects. It also means you pay attention to release milestones and understand if a patch you're merging is marked for the release, especially critical during the feature freeze. The bottom line here is merging code is a responsibility Neutron core reviewers have. Adding or Removing Core Reviewers --------------------------------- A new Neutron core reviewer may be proposed at anytime on the openstack-dev mailing list. Typically, the Lieutenant for a given area will propose a new core reviewer for their specific area of coverage, though the Neutron PTL may propose new core reviewers as well. The proposal is typically made after discussions with existing core reviewers. Once a proposal has been made, three existing Neutron core reviewers from the Lieutenant's area of focus must respond to the email with a +1. If the member is being added by a Lieutenant from an area of focus with less than three members, a simple majority will be used to determine if the vote is successful. Another Neutron core reviewer from the same area of focus can vote -1 to veto the proposed new core reviewer. The PTL will mediate all disputes for core reviewer additions. The PTL may remove a Neutron core reviewer at any time. Typically when a member has decreased their involvement with the project through a drop in reviews and participation in general project development, the PTL will propose their removal and remove them. Please note there is no voting or vetoing of core reviewer removal. Members who have previously been a core reviewer may be fast-tracked back into a core reviewer role if their involvement picks back up and the existing core reviewers support their re-instatement. Neutron Core Reviewer Membership Expectations --------------------------------------------- Neutron core reviewers have the following expectations: * Reasonable attendance at the weekly Neutron IRC meetings. * Participation in Neutron discussions on the mailing list, as well as in-channel in #openstack-neutron. * Participation in Neutron related design summit sessions at the OpenStack Summits. Please note in-person attendance at design summits, mid-cycles, and other code sprints is not a requirement to be a Neutron core reviewer. The Neutron team will do its best to facilitate virtual attendance at all events. Travel is not to be taken lightly, and we realize the costs involved for those who partake in attending these events. In addition to the above, code reviews are the most important requirement of Neutron core reviewers. Neutron follows the documented OpenStack `code review guidelines `_. We encourage all people to review Neutron patches, but core reviewers are required to maintain a level of review numbers relatively close to other core reviewers. There are no hard statistics around code review numbers, but in general we use 30, 60, 90 and 180 day stats when examining review stats. * `30 day review stats `_ * `60 day review stats `_ * `90 day review stats `_ * `180 day review stats `_ There are soft-touch items around being a Neutron core reviewer as well. Gaining trust with the existing Neutron core reviewers is important. Being able to work together with the existing Neutron core reviewer team is critical as well. Being a Neutron core reviewer means spending a significant amount of time with the existing Neutron core reviewers team on IRC, the mailing list, at Summits, and in reviews. Ensuring you participate and engage here is critical to becoming and remaining a core reviewer. neutron-12.1.1/doc/source/contributor/contribute.rst0000664000175000017500000006543313553660047022663 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Contributing new extensions to Neutron ====================================== Introduction ------------ Neutron has a pluggable architecture, with a number of extension points. This documentation covers aspects relevant to contributing new Neutron v2 core (aka monolithic) plugins, ML2 mechanism drivers, and L3 service plugins. This document will initially cover a number of process-oriented aspects of the contribution process, and proceed to provide a how-to guide that shows how to go from 0 LOC's to successfully contributing new extensions to Neutron. In the remainder of this guide, we will try to use practical examples as much as we can so that people have working solutions they can start from. This guide is for a developer who wants to have a degree of visibility within the OpenStack Networking project. If you are a developer who wants to provide a Neutron-based solution without interacting with the Neutron community, you are free to do so, but you can stop reading now, as this guide is not for you. Plugins and drivers for non-reference implementations are known as "third-party" code. This includes code for supporting vendor products, as well as code for supporting open-source networking implementations. Before the Kilo release these plugins and drivers were included in the Neutron tree. During the Kilo cycle the third-party plugins and drivers underwent the first phase of a process called decomposition. During this phase, each plugin and driver moved the bulk of its logic to a separate git repository, while leaving a thin "shim" in the neutron tree together with the DB models and migrations (and perhaps some config examples). During the Liberty cycle the decomposition concept was taken to its conclusion by allowing third-party code to exist entirely out of tree. Further extension mechanisms have been provided to better support external plugins and drivers that alter the API and/or the data model. In the Mitaka cycle we will **require** all third-party code to be moved out of the neutron tree completely. 'Outside the tree' can be anything that is publicly available: it may be a repo on git.openstack.org for instance, a tarball, a pypi package, etc. A plugin/drivers maintainer team self-governs in order to promote sharing, reuse, innovation, and release of the 'out-of-tree' deliverable. It should not be required for any member of the core team to be involved with this process, although core members of the Neutron team can participate in whichever capacity is deemed necessary to facilitate out-of-tree development. This guide is aimed at you as the maintainer of code that integrates with Neutron but resides in a separate repository. Contribution Process -------------------- If you want to extend OpenStack Networking with your technology, and you want to do it within the visibility of the OpenStack project, follow the guidelines and examples below. We'll describe best practices for: * Design and Development; * Testing and Continuous Integration; * Defect Management; * Backport Management for plugin specific code; * DevStack Integration; * Documentation; Once you have everything in place you may want to add your project to the list of Neutron sub-projects. See :ref:`add-remove-projects-to-stadium` for details. Design and Development ---------------------- Assuming you have a working repository, any development to your own repo does not need any blueprint, specification or bugs against Neutron. However, if your project is a part of the Neutron Stadium effort, you are expected to participate in the principles of the Four Opens, meaning your design should be done in the open. Thus, it is encouraged to file documentation for changes in your own repository. If your code is hosted on git.openstack.org then the gerrit review system is automatically provided. Contributors should follow the review guidelines similar to those of Neutron. However, you as the maintainer have the flexibility to choose who can approve/merge changes in your own repo. It is recommended (but not required, see :doc:`policies `) that you set up a third-party CI system. This will provide a vehicle for checking the third-party code against Neutron changes. See `Testing and Continuous Integration`_ below for more detailed recommendations. Design documents can still be supplied in form of Restructured Text (RST) documents, within the same third-party library repo. If changes to the common Neutron code are required, an :ref:`RFE ` may need to be filed. However, every case is different and you are invited to seek guidance from Neutron core reviewers about what steps to follow. Testing and Continuous Integration ---------------------------------- The following strategies are recommendations only, since third-party CI testing is not an enforced requirement. However, these strategies are employed by the majority of the plugin/driver contributors that actively participate in the Neutron development community, since they have learned from experience how quickly their code can fall out of sync with the rapidly changing Neutron core code base. * You should run unit tests in your own external library (e.g. on git.openstack.org where Jenkins setup is for free). * Your third-party CI should validate third-party integration with Neutron via functional testing. The third-party CI is a communication mechanism. The objective of this mechanism is as follows: * it communicates to you when someone has contributed a change that potentially breaks your code. It is then up to you maintaining the affected plugin/driver to determine whether the failure is transient or real, and resolve the problem if it is. * it communicates to a patch author that they may be breaking a plugin/driver. If they have the time/energy/relationship with the maintainer of the plugin/driver in question, then they can (at their discretion) work to resolve the breakage. * it communicates to the community at large whether a given plugin/driver is being actively maintained. * A maintainer that is perceived to be responsive to failures in their third-party CI jobs is likely to generate community goodwill. It is worth noting that if the plugin/driver repository is hosted on git.openstack.org, due to current openstack-infra limitations, it is not possible to have third-party CI systems participating in the gate pipeline for the repo. This means that the only validation provided during the merge process to the repo is through unit tests. Post-merge hooks can still be exploited to provide third-party CI feedback, and alert you of potential issues. As mentioned above, third-party CI systems will continue to validate Neutron core commits. This will allow them to detect when incompatible changes occur, whether they are in Neutron or in the third-party repo. Defect Management ----------------- Bugs affecting third-party code should *not* be filed in the Neutron project on launchpad. Bug tracking can be done in any system you choose, but by creating a third-party project in launchpad, bugs that affect both Neutron and your code can be more easily tracked using launchpad's "also affects project" feature. Security Issues ~~~~~~~~~~~~~~~ Here are some answers to how to handle security issues in your repo, taken from `this openstack-dev mailing list message `_: - How should security your issues be managed? The OpenStack Vulnerability Management Team (VMT) follows a `documented process `_ which can basically be reused by any project-team when needed. - Should the OpenStack security team be involved? The OpenStack VMT directly oversees vulnerability reporting and disclosure for a `subset of OpenStack source code repositories `_. However, they are still quite happy to answer any questions you might have about vulnerability management for your own projects even if they're not part of that set. Feel free to reach out to the VMT in public or in private. Also, the VMT is an autonomous subgroup of the much larger `OpenStack Security project-team `_. They're a knowledgeable bunch and quite responsive if you want to get their opinions or help with security-related issues (vulnerabilities or otherwise). - Does a CVE need to be filed? It can vary widely. If a commercial distribution such as Red Hat is redistributing a vulnerable version of your software, then they may assign one anyway even if you don't request one yourself. Or the reporter may request one; the reporter may even be affiliated with an organization who has already assigned/obtained a CVE before they initiate contact with you. - Do the maintainers need to publish OSSN or equivalent documents? OpenStack Security Advisories (OSSA) are official publications of the OpenStack VMT and only cover VMT-supported software. OpenStack Security Notes (OSSN) are published by editors within the OpenStack Security project-team on more general security topics and may even cover issues in non-OpenStack software commonly used in conjunction with OpenStack, so it's at their discretion as to whether they would be able to accommodate a particular issue with an OSSN. However, these are all fairly arbitrary labels, and what really matters in the grand scheme of things is that vulnerabilities are handled seriously, fixed with due urgency and care, and announced widely -- not just on relevant OpenStack mailing lists but also preferably somewhere with broader distribution like the `Open Source Security mailing list `_. The goal is to get information on your vulnerabilities, mitigating measures and fixes into the hands of the people using your software in a timely manner. - Anything else to consider here? The OpenStack VMT is in the process of trying to reinvent itself so that it can better scale within the context of the "Big Tent." This includes making sure the policy/process documentation is more consumable and reusable even by project-teams working on software outside the scope of our charter. It's a work in progress, and any input is welcome on how we can make this function well for everyone. Backport Management Strategies ------------------------------ This section applies only to third-party maintainers who had code in the Neutron tree during the Kilo and earlier releases. It will be obsolete once the Kilo release is no longer supported. If a change made to out-of-tree third-party code needs to be back-ported to in-tree code in a stable branch, you may submit a review without a corresponding master branch change. The change will be evaluated by core reviewers for stable branches to ensure that the backport is justified and that it does not affect Neutron core code stability. DevStack Integration Strategies ------------------------------- When developing and testing a new or existing plugin or driver, the aid provided by DevStack is incredibly valuable: DevStack can help get all the software bits installed, and configured correctly, and more importantly in a predictable way. For DevStack integration there are a few options available, and they may or may not make sense depending on whether you are contributing a new or existing plugin or driver. If you are contributing a new plugin, the approach to choose should be based on `Extras.d Hooks' externally hosted plugins `_. With the extra.d hooks, the DevStack integration is co-located with the third-party integration library, and it leads to the greatest level of flexibility when dealing with DevStack based dev/test deployments. One final consideration is worth making for third-party CI setups: if `Devstack Gate `_ is used, it does provide hook functions that can be executed at specific times of the devstack-gate-wrap script run. For example, the `Neutron Functional job `_ uses them. For more details see `devstack-vm-gate-wrap.sh `_. Documentation ------------- For a layout of the how the documentation directory is structured see the `effective neutron guide `_ Project Initial Setup --------------------- The how-to below assumes that the third-party library will be hosted on git.openstack.org. This lets you tap in the entire OpenStack CI infrastructure and can be a great place to start from to contribute your new or existing driver/plugin. The list of steps below are summarized version of what you can find on http://docs.openstack.org/infra/manual/creators.html. They are meant to be the bare minimum you have to complete in order to get you off the ground. * Create a public repository: this can be a personal git.openstack.org repo or any publicly available git repo, e.g. ``https://github.com/john-doe/foo.git``. This would be a temporary buffer to be used to feed the one on git.openstack.org. * Initialize the repository: if you are starting afresh, you may *optionally* want to use cookiecutter to get a skeleton project. You can learn how to use cookiecutter on https://git.openstack.org/cgit/openstack-dev/cookiecutter. If you want to build the repository from an existing Neutron module, you may want to skip this step now, build the history first (next step), and come back here to initialize the remainder of the repository with other files being generated by the cookiecutter (like tox.ini, setup.cfg, setup.py, etc.). * Create a repository on git.openstack.org. For this you need the help of the OpenStack infra team. It is worth noting that you only get one shot at creating the repository on git.openstack.org. This is the time you get to choose whether you want to start from a clean slate, or you want to import the repo created during the previous step. In the latter case, you can do so by specifying the upstream section for your project in project-config/gerrit/project.yaml. Steps are documented on the `Repository Creator's Guide `_. * Ask for a Launchpad user to be assigned to the core team created. Steps are documented in `this section `_. * Fix, fix, fix: at this point you have an external base to work on. You can develop against the new git.openstack.org project, the same way you work with any other OpenStack project: you have pep8, docs, and python27 CI jobs that validate your patches when posted to Gerrit. For instance, one thing you would need to do is to define an entry point for your plugin or driver in your own setup.cfg similarly as to how it is done in the `setup.cfg for ODL `_. * Define an entry point for your plugin or driver in setup.cfg * Create third-party CI account: if you do not already have one, follow instructions for `third-party CI `_ to get one. Internationalization support ---------------------------- OpenStack is committed to broad international support. Internationalization (I18n) is one of important areas to make OpenStack ubiquitous. Each project is recommended to support i18n. This section describes how to set up translation support. The description in this section uses the following variables: * repository : ``openstack/${REPOSITORY}`` (e.g., ``openstack/networking-foo``) * top level python path : ``${MODULE_NAME}`` (e.g., ``networking_foo``) oslo.i18n ~~~~~~~~~ * Each subproject repository should have its own oslo.i18n integration wrapper module ``${MODULE_NAME}/_i18n.py``. The detail is found at https://docs.openstack.org/oslo.i18n/latest/usage.html. .. note:: **DOMAIN** name should match your **module** name ``${MODULE_NAME}``. * Import ``_()`` from your ``${MODULE_NAME}/_i18n.py``. .. warning:: Do not use ``_()`` in the builtins namespace which is registered by **gettext.install()** in ``neutron/__init__.py``. It is now deprecated as described in oslo.18n documentation. Setting up translation support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You need to create or edit the following files to start translation support: * setup.cfg * babel.cfg We have a good example for an oslo project at https://review.openstack.org/#/c/98248/. Add the following to ``setup.cfg``:: [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = ${MODULE_NAME}/locale/${MODULE_NAME}.pot [compile_catalog] directory = ${MODULE_NAME}/locale domain = ${MODULE_NAME} [update_catalog] domain = ${MODULE_NAME} output_dir = ${MODULE_NAME}/locale input_file = ${MODULE_NAME}/locale/${MODULE_NAME}.pot Note that ``${MODULE_NAME}`` is used in all names. Create ``babel.cfg`` with the following contents:: [python: **.py] Enable Translation ~~~~~~~~~~~~~~~~~~ To update and import translations, you need to make a change in project-config. A good example is found at https://review.openstack.org/#/c/224222/. After doing this, the necessary jobs will be run and push/pull a message catalog to/from the translation infrastructure. Integrating with the Neutron system ----------------------------------- Configuration Files ~~~~~~~~~~~~~~~~~~~ The ``data_files`` in the ``[files]`` section of ``setup.cfg`` of Neutron shall not contain any third-party references. These shall be located in the same section of the third-party repo's own ``setup.cfg`` file. * Note: Care should be taken when naming sections in configuration files. When the Neutron service or an agent starts, oslo.config loads sections from all specified config files. This means that if a section [foo] exists in multiple config files, duplicate settings will collide. It is therefore recommended to prefix section names with a third-party string, e.g. [vendor_foo]. Since Mitaka, configuration files are not maintained in the git repository but should be generated as follows:: ``tox -e genconfig`` If a 'tox' environment is unavailable, then you can run the following script instead to generate the configuration files:: ./tools/generate_config_file_samples.sh It is advised that subprojects do not keep their configuration files in their respective trees and instead generate them using a similar approach as Neutron does. **ToDo: Inclusion in OpenStack documentation?** Is there a recommended way to have third-party config options listed in the configuration guide in docs.openstack.org? Database Models and Migrations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A third-party repo may contain database models for its own tables. Although these tables are in the Neutron database, they are independently managed entirely within the third-party code. Third-party code shall **never** modify neutron core tables in any way. Each repo has its own *expand* and *contract* `alembic migration branches `_. A third-party repo's alembic migration branches may operate only on tables that are owned by the repo. * Note: Care should be taken when adding new tables. To prevent collision of table names it is **required** to prefix them with a vendor/plugin string. * Note: A third-party maintainer may opt to use a separate database for their tables. This may complicate cases where there are foreign key constraints across schemas for DBMS that do not support this well. Third-party maintainer discretion advised. The database tables owned by a third-party repo can have references to fields in neutron core tables. However, the alembic branch for a plugin/driver repo shall never update any part of a table that it does not own. **Note: What happens when a referenced item changes?** * **Q:** If a driver's table has a reference (for example a foreign key) to a neutron core table, and the referenced item is changed in neutron, what should you do? * **A:** Fortunately, this should be an extremely rare occurrence. Neutron core reviewers will not allow such a change unless there is a very carefully thought-out design decision behind it. That design will include how to address any third-party code affected. (This is another good reason why you should stay actively involved with the Neutron developer community.) The ``neutron-db-manage`` alembic wrapper script for neutron detects alembic branches for installed third-party repos, and the upgrade command automatically applies to all of them. A third-party repo must register its alembic migrations at installation time. This is done by providing an entrypoint in setup.cfg as follows: For a third-party repo named ``networking-foo``, add the alembic_migrations directory as an entrypoint in the ``neutron.db.alembic_migrations`` group:: [entry_points] neutron.db.alembic_migrations = networking-foo = networking_foo.db.migration:alembic_migrations **ToDo: neutron-db-manage autogenerate** The alembic autogenerate command needs to support branches in external repos. Bug #1471333 has been filed for this. DB Model/Migration Testing ~~~~~~~~~~~~~~~~~~~~~~~~~~ Here is a :doc:`template functional test ` third-party maintainers can use to develop tests for model-vs-migration sync in their repos. It is recommended that each third-party CI sets up such a test, and runs it regularly against Neutron master. Entry Points ~~~~~~~~~~~~ The `Python setuptools `_ installs all entry points for packages in one global namespace for an environment. Thus each third-party repo can define its package's own ``[entry_points]`` in its own ``setup.cfg`` file. For example, for the ``networking-foo`` repo:: [entry_points] console_scripts = neutron-foo-agent = networking_foo.cmd.eventlet.agents.foo:main neutron.core_plugins = foo_monolithic = networking_foo.plugins.monolithic.plugin:FooPluginV2 neutron.service_plugins = foo_l3 = networking_foo.services.l3_router.l3_foo:FooL3ServicePlugin neutron.ml2.type_drivers = foo_type = networking_foo.plugins.ml2.drivers.foo:FooType neutron.ml2.mechanism_drivers = foo_ml2 = networking_foo.plugins.ml2.drivers.foo:FooDriver neutron.ml2.extension_drivers = foo_ext = networking_foo.plugins.ml2.drivers.foo:FooExtensionDriver * Note: It is advisable to include ``foo`` in the names of these entry points to avoid conflicts with other third-party packages that may get installed in the same environment. API Extensions ~~~~~~~~~~~~~~ Extensions can be loaded in two ways: #. Use the ``append_api_extensions_path()`` library API. This method is defined in ``neutron/api/extensions.py`` in the neutron tree. #. Leverage the ``api_extensions_path`` config variable when deploying. See the example config file ``etc/neutron.conf`` in the neutron tree where this variable is commented. Service Providers ~~~~~~~~~~~~~~~~~ If your project uses service provider(s) the same way VPNAAS and LBAAS do, you specify your service provider in your ``project_name.conf`` file like so:: [service_providers] # Must be in form: # service_provider=::[:default][,...] In order for Neutron to load this correctly, make sure you do the following in your code:: from neutron.db import servicetype_db service_type_manager = servicetype_db.ServiceTypeManager.get_instance() service_type_manager.add_provider_configuration( YOUR_SERVICE_TYPE, pconf.ProviderConfiguration(YOUR_SERVICE_MODULE)) This is typically required when you instantiate your service plugin class. Interface Drivers ~~~~~~~~~~~~~~~~~ Interface (VIF) drivers for the reference implementations are defined in ``neutron/agent/linux/interface.py``. Third-party interface drivers shall be defined in a similar location within their own repo. The entry point for the interface driver is a Neutron config option. It is up to the installer to configure this item in the ``[default]`` section. For example:: [default] interface_driver = networking_foo.agent.linux.interface.FooInterfaceDriver **ToDo: Interface Driver port bindings.** ``VIF_TYPE_*`` constants in ``neutron_lib/api/definitions/portbindings.py`` should be moved from neutron core to the repositories where their drivers are implemented. We need to provide some config or hook mechanism for VIF types to be registered by external interface drivers. For Nova, selecting the VIF driver can be done outside of Neutron (using the new `os-vif python library `_?). Armando and Akihiro to discuss. Rootwrap Filters ~~~~~~~~~~~~~~~~ If a third-party repo needs a rootwrap filter for a command that is not used by Neutron core, then the filter shall be defined in the third-party repo. For example, to add a rootwrap filters for commands in repo ``networking-foo``: * In the repo, create the file: ``etc/neutron/rootwrap.d/foo.filters`` * In the repo's ``setup.cfg`` add the filters to data_files:: [files] data_files = etc/neutron/rootwrap.d = etc/neutron/rootwrap.d/foo.filters Extending python-neutronclient ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The maintainer of a third-party component may wish to add extensions to the Neutron CLI client. Thanks to https://review.openstack.org/148318 this can now be accomplished. See `Client Command Extensions `_. Other repo-split items ~~~~~~~~~~~~~~~~~~~~~~ (These are still TBD.) * Splitting policy.json? **ToDo** Armando will investigate. * Generic instructions (or a template) for installing an out-of-tree plugin or driver for Neutron. Possibly something for the networking guide, and/or a template that plugin/driver maintainers can modify and include with their package. neutron-12.1.1/doc/source/contributor/client_command_extensions.rst0000664000175000017500000000227513553660046025732 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Client command extension support ================================ The client command extension adds support for extending the neutron client while considering ease of creation. The full document can be found in the python-neutronclient repository: https://docs.openstack.org/python-neutronclient/latest/contributor/client_command_extensions.html neutron-12.1.1/doc/source/contributor/internals/0000775000175000017500000000000013553660156021740 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/contributor/internals/external_dns_integration.rst0000664000175000017500000000346413553660046027570 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Integration with external DNS services ====================================== Since the Mitaka release, neutron has an interface defined to interact with an external DNS service. This interface is based on an abstract driver that can be used as the base class to implement concrete drivers to interact with various DNS services. The reference implementation of such a driver integrates neutron with `OpenStack Designate `_. This integration allows users to publish *dns_name* and *dns_domain* attributes associated with floating IP addresses, ports, and networks in an external DNS service. Changes to the neutron API -------------------------- To support integration with an external DNS service, the *dns_name* and *dns_domain* attributes were added to floating ips, ports and networks. The *dns_name* specifies the name to be associated with a corresponding IP address, both of which will be published to an existing domain with the name *dns_domain* in the external DNS service. Specifically, floating ips, ports and networks are extended as follows: * Floating ips have a *dns_name* and a *dns_domain* attribute. * Ports have a *dns_name* attribute. * Networks have a *dns_domain* attributes. neutron-12.1.1/doc/source/contributor/internals/address_scopes.rst0000664000175000017500000002673313553660046025504 0ustar zuulzuul00000000000000Subnet Pools and Address Scopes =============================== This page discusses subnet pools and address scopes Subnet Pools ------------ Learn about subnet pools by watching the summit talk given in Vancouver [#]_. .. [#] http://www.youtube.com/watch?v=QqP8yBUUXBM&t=6m12s Subnet pools were added in Kilo. They are relatively simple. A SubnetPool has any number of SubnetPoolPrefix objects associated to it. These prefixes are in CIDR format. Each CIDR is a piece of the address space that is available for allocation. Subnet Pools support IPv6 just as well as IPv4. The Subnet model object now has a subnetpool_id attribute whose default is null for backward compatibility. The subnetpool_id attribute stores the UUID of the subnet pool that acted as the source for the address range of a particular subnet. When creating a subnet, the subnetpool_id can be optionally specified. If it is, the 'cidr' field is not required. If 'cidr' is specified, it will be allocated from the pool assuming the pool includes it and hasn't already allocated any part of it. If 'cidr' is left out, then the prefixlen attribute can be specified. If it is not, the default prefix length will be taken from the subnet pool. Think of it this way, the allocation logic always needs to know the size of the subnet desired. It can pull it from a specific CIDR, prefixlen, or default. A specific CIDR is optional and the allocation will try to honor it if provided. The request will fail if it can't honor it. Subnet pools do not allow overlap of subnets. Subnet Pool Quotas ~~~~~~~~~~~~~~~~~~ A quota mechanism was provided for subnet pools. It is different than other quota mechanisms in Neutron because it doesn't count instances of first class objects. Instead it counts how much of the address space is used. For IPv4, it made reasonable sense to count quota in terms of individual addresses. So, if you're allowed exactly one /24, your quota should be set to 256. Three /26s would be 192. This mechanism encourages more efficient use of the IPv4 space which will be increasingly important when working with globally routable addresses. For IPv6, the smallest viable subnet in Neutron is a /64. There is no reason to allocate a subnet of any other size for use on a Neutron network. It would look pretty funny to set a quota of 4611686018427387904 to allow one /64 subnet. To avoid this, we count IPv6 quota in terms of /64s. So, a quota of 3 allows three /64 subnets. When we need to allocate something smaller in the future, we will need to ensure that the code can handle non-integer quota consumption. Allocation ~~~~~~~~~~ Allocation is done in a way that aims to minimize fragmentation of the pool. The relevant code is here [#]_. First, the available prefixes are computed using a set difference: pool - allocations. The result is compacted [#]_ and then sorted by size. The subnet is then allocated from the smallest available prefix that is large enough to accommodate the request. .. [#] neutron/ipam/subnet_alloc.py (_allocate_any_subnet) .. [#] http://pythonhosted.org/netaddr/api.html#netaddr.IPSet.compact Address Scopes -------------- Before subnet pools or address scopes, it was impossible to tell if a network address was routable in a certain context because the address was given explicitly on subnet create and wasn't validated against any other addresses. Address scopes are meant to solve this by putting control over the address space in the hands of an authority: the address scope owner. It makes use of the already existing SubnetPool concept for allocation. Address scopes are "the thing within which address overlap is not allowed" and thus provide more flexible control as well as decoupling of address overlap from tenancy. Prior to the Mitaka release, there was implicitly only a single 'shared' address scope. Arbitrary address overlap was allowed making it pretty much a "free for all". To make things seem somewhat sane, normal users are not able to use routers to cross-plug networks from different projects and NAT was used between internal networks and external networks. It was almost as if each project had a private address scope. The problem is that this model cannot support use cases where NAT is not desired or supported (e.g. IPv6) or we want to allow different projects to cross-plug their networks. An AddressScope covers only one address family. But, they work equally well for IPv4 and IPv6. Routing ~~~~~~~ The reference implementation honors address scopes. Within an address scope, addresses route freely (barring any FW rules or other external restrictions). Between scopes, routing is prevented unless address translation is used. For now, floating IPs are the only place where traffic crosses scope boundaries. When a floating IP is associated to a fixed IP, the fixed IP is allowed to access the address scope of the floating IP by way of a 1:1 NAT rule. That means the fixed IP can access not only the external network, but also any internal networks that are in the same address scope as the external network. This is diagrammed as follows:: +----------------------+ +---------------------------+ | address scope 1 | | address scope 2 | | | | | | +------------------+ | | +------------------+ | | | internal network | | | | external network | | | +-------------+----+ | | +--------+---------+ | | | | | | | | +-------+--+ | | +------+------+ | | | fixed ip +----------------+ floating IP | | | +----------+ | | +--+--------+-+ | +----------------------+ | | | | | +------+---+ +--+-------+ | | | internal | | internal | | | +----------+ +----------+ | +---------------------------+ Due to the asymmetric route in DVR, and the fact that DVR local routers do not know the information of the floating IPs that reside in other hosts, there is a limitation in the DVR multiple hosts scenario. With DVR in multiple hosts, when the destination of traffic is an internal fixed IP in a different host, the fixed IP with a floating IP associated can't cross the scope boundary to access the internal networks that are in the same address scope of the external network. See https://bugs.launchpad.net/neutron/+bug/1682228 RPC ~~~ The L3 agent in the reference implementation needs to know the address scope for each port on each router in order to map ingress traffic correctly. Each subnet from the same address family on a network is required to be from the same subnet pool. Therefore, the address scope will also be the same. If this were not the case, it would be more difficult to match ingress traffic on a port with the appropriate scope. It may be counter-intuitive but L3 address scopes need to be anchored to some sort of non-L3 thing (e.g. an L2 interface) in the topology in order to determine the scope of ingress traffic. For now, we use ports/networks. In the future, we may be able to distinguish by something else like the remote MAC address or something. The address scope id is set on each port in a dict under the 'address_scopes' attribute. The scope is distinct per address family. If the attribute does not appear, it is assumed to be null for both families. A value of null means that the addresses are in the "implicit" address scope which holds all addresses that don't have an explicit one. All subnets that existed in Neutron before address scopes existed fall here. Here is an example of how the json will look in the context of a router port:: "address_scopes": { "4": "d010a0ea-660e-4df4-86ca-ae2ed96da5c1", "6": null }, To implement floating IPs crossing scope boundaries, the L3 agent needs to know the target scope of the floating ip. The fixed address is not enough to disambiguate because, theoretically, there could be overlapping addresses from different scopes. The scope is computed [#]_ from the floating ip fixed port and attached to the floating ip dict under the 'fixed_ip_address_scope' attribute. Here's what the json looks like (trimmed):: { ... "floating_ip_address": "172.24.4.4", "fixed_ip_address": "172.16.0.3", "fixed_ip_address_scope": "d010a0ea-660e-4df4-86ca-ae2ed96da5c1", ... } .. [#] neutron/db/l3_db.py (_get_sync_floating_ips) Model ~~~~~ The model for subnet pools and address scopes can be found in neutron/db/models_v2.py and neutron/db/address_scope_db.py. This document won't go over all of the details. It is worth noting how they relate to existing Neutron objects. The existing Neutron subnet now optionally references a single subnet pool:: +----------------+ +------------------+ +--------------+ | Subnet | | SubnetPool | | AddressScope | +----------------+ +------------------+ +--------------+ | subnet_pool_id +------> | address_scope_id +------> | | | | | | | | | | | | | | | | | | | | +----------------+ +------------------+ +--------------+ L3 Agent ~~~~~~~~ The L3 agent is limited in its support for multiple address scopes. Within a router in the reference implementation, traffic is marked on ingress with the address scope corresponding to the network it is coming from. If that traffic would route to an interface in a different address scope, the traffic is blocked unless an exception is made. One exception is made for floating IP traffic. When traffic is headed to a floating IP, DNAT is applied and the traffic is allowed to route to the private IP address potentially crossing the address scope boundary. When traffic flows from an internal port to the external network and a floating IP is assigned, that traffic is also allowed. Another exception is made for traffic from an internal network to the external network when SNAT is enabled. In this case, SNAT to the router's fixed IP address is applied to the traffic. However, SNAT is not used if the external network has an explicit address scope assigned and it matches the internal network's. In that case, traffic routes straight through without NAT. The internal network's addresses are viable on the external network in this case. The reference implementation has limitations. Even with multiple address scopes, a router implementation is unable to connect to two networks with overlapping IP addresses. There are two reasons for this. First, a single routing table is used inside the namespace. An implementation using multiple routing tables has been in the works but there are some unresolved issues with it. Second, the default SNAT feature cannot be supported with the current Linux conntrack implementation unless a double NAT is used (one NAT to get from the address scope to an intermediate address specific to the scope and a second NAT to get from that intermediate address to an external address). Single NAT won't work if there are duplicate addresses across the scopes. Due to these complications the router will still refuse to connect to overlapping subnets. We can look in to an implementation that overcomes these limitations in the future. neutron-12.1.1/doc/source/contributor/internals/l3_agent_extensions.rst0000664000175000017500000000241113553660046026441 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) L3 agent extensions =================== L3 agent extensions are part of a generalized L2/L3 extension framework. See :doc:`agent extensions `. L3 agent extension API ---------------------- The L3 agent extension API object includes several methods that expose router information to L3 agent extensions:: #. get_routers_in_project #. get_router_hosting_port #. is_router_in_namespace #. get_router_info neutron-12.1.1/doc/source/contributor/internals/retries.rst0000664000175000017500000001532713553660047024156 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Retrying Operations =================== Inside of the neutron.db.api module there is a decorator called 'retry_if_session_inactive'. This should be used to protect any functions that perform DB operations. This decorator will capture any deadlock errors, RetryRequests, connection errors, and unique constraint violations that are thrown by the function it is protecting. This decorator will not retry an operation if the function it is applied to is called within an active session. This is because the majority of the exceptions it captures put the session into a partially rolled back state so it is no longer usable. It is important to ensure there is a decorator outside of the start of the transaction. The decorators are safe to nest if a function is sometimes called inside of another transaction. If a function is being protected that does not take context as an argument the 'retry_db_errors' decorator function may be used instead. It retries the same exceptions and has the same anti-nesting behavior as 'retry_if_session_active', but it does not check if a session is attached to any context keywords. ('retry_if_session_active' just uses 'retry_db_errors' internally after checking the session) Idempotency on Failures ----------------------- The function that is being decorated should always fully cleanup whenever it encounters an exception so its safe to retry the operation. So if a function creates a DB object, commits, then creates another, the function must have a cleanup handler to remove the first DB object in the case that the second one fails. Assume any DB operation can throw a retriable error. You may see some retry decorators at the API layers in Neutron; however, we are trying to eliminate them because each API operation has many independent steps that makes ensuring idempotency on partial failures very difficult. Argument Mutation ----------------- A decorated function should not mutate any complex arguments which are passed into it. If it does, it should have an exception handler that reverts the change so it's safe to retry. The decorator will automatically create deep copies of sets, lists, and dicts which are passed through it, but it will leave the other arguments alone. Retrying to Handle Race Conditions ---------------------------------- One of the difficulties with detecting race conditions to create a DB record with a unique constraint is determining where to put the exception handler because a constraint violation can happen immediately on flush or it may not happen all of the way until the transaction is being committed on the exit of the session context manager. So we would end up with code that looks something like this: :: def create_port(context, ip_address, mac_address): _ensure_mac_not_in_use(context, mac_address) _ensure_ip_not_in_use(context, ip_address) try: with context.session.begin(): port_obj = Port(ip=ip_address, mac=mac_address) do_expensive_thing(...) do_extra_other_thing(...) return port_obj except DBDuplicateEntry as e: # code to parse columns if 'mac' in e.columns: raise MacInUse(mac_address) if 'ip' in e.columns: raise IPAddressInUse(ip) def _ensure_mac_not_in_use(context, mac): if context.session.query(Port).filter_by(mac=mac).count(): raise MacInUse(mac) def _ensure_ip_not_in_use(context, ip): if context.session.query(Port).filter_by(ip=ip).count(): raise IPAddressInUse(ip) So we end up with an exception handler that has to understand where things went wrong and convert them into appropriate exceptions for the end-users. This distracts significantly from the main purpose of create_port. Since the retry decorator will automatically catch and retry DB duplicate errors for us, we can allow it to retry on this race condition which will give the original validation logic to be re-executed and raise the appropriate error. This keeps validation logic in one place and makes the code cleaner. :: from neutron.db import api as db_api @db_api.retry_if_session_inactive() def create_port(context, ip_address, mac_address): _ensure_mac_not_in_use(context, mac_address) _ensure_ip_not_in_use(context, ip_address) with context.session.begin(): port_obj = Port(ip=ip_address, mac=mac_address) do_expensive_thing(...) do_extra_other_thing(...) return port_obj def _ensure_mac_not_in_use(context, mac): if context.session.query(Port).filter_by(mac=mac).count(): raise MacInUse(mac) def _ensure_ip_not_in_use(context, ip): if context.session.query(Port).filter_by(ip=ip).count(): raise IPAddressInUse(ip) Nesting ------- Once the decorator retries an operation the maximum number of times, it will attach a flag to the exception it raises further up that will prevent decorators around the calling functions from retrying the error again. This prevents an exponential increase in the number of retries if they are layered. Usage ----- Here are some usage examples: :: from neutron.db import api as db_api @db_api.retry_if_session_inactive() def create_elephant(context, elephant_details): .... @db_api.retry_if_session_inactive() def atomic_bulk_create_elephants(context, elephants): with context.session.begin(): for elephant in elephants: # note that if create_elephant throws a retriable # exception, the decorator around it will not retry # because the session is active. The decorator around # atomic_bulk_create_elephants will be responsible for # retrying the entire operation. create_elephant(context, elephant) # sample usage when session is attached to a var other than 'context' @db_api.retry_if_session_inactive(context_var_name='ctx') def some_function(ctx): ... neutron-12.1.1/doc/source/contributor/internals/network_ip_availability.rst0000664000175000017500000001356213553660046027412 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Network IP Availability Extension ================================= This extension is an information-only API that allows a user or process to determine the amount of IPs that are consumed across networks and their subnets' allocation pools. Each network and embedded subnet returns with values for **used_ips** and **total_ips** making it easy to determine how much of your network's IP space is consumed. This API provides the ability for network administrators to periodically list usage (manual or automated) in order to preemptively add new network capacity when thresholds are exceeded. **Important Note:** This API tracks a network's "consumable" IPs. What's the distinction? After a network and its subnets are created, consumable IPs are: * Consumed in the subnet's allocations (derives used IPs) * Consumed from the subnet's allocation pools (derives total IPs) This API tracks consumable IPs so network administrators know when their subnet's IP pools (and ultimately a network's) IPs are about to run out. This API does not account reserved IPs such as a subnet's gateway IP or other reserved or unused IPs of a subnet's cidr that are consumed as a result of the subnet creation itself. API Specification ----------------- Availability for all networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GET /v2.0/network-ip-availabilities :: Request to url: v2.0/network-ip-availabilities headers: {'content-type': 'application/json', 'X-Auth-Token': 'SOME_AUTH_TOKEN'} Example response :: Response: HTTP/1.1 200 OK Content-Type: application/json; charset=UTF-8 .. code:: { "network_ip_availabilities": [ { "network_id": "f944c153-3f46-417b-a3c2-487cd9a456b9", "network_name": "net1", "subnet_ip_availability": [ { "cidr": "10.0.0.0/24", "ip_version": 4, "subnet_id": "46b1406a-8373-454c-8eb8-500a09eb77fb", "subnet_name": "", "total_ips": 253, "used_ips": 3 } ], "tenant_id": "test-project", "total_ips": 253, "used_ips": 3 }, { "network_id": "47035bae-4f29-4fef-be2e-2941b72528a8", "network_name": "net2", "subnet_ip_availability": [], "tenant_id": "test-project", "total_ips": 0, "used_ips": 0 }, { "network_id": "2e3ea0cd-c757-44bf-bb30-42d038687e3f", "network_name": "net3", "subnet_ip_availability": [ { "cidr": "40.0.0.0/24", "ip_version": 4, "subnet_id": "aab6b35c-16b5-489c-a5c7-fec778273495", "subnet_name": "", "total_ips": 253, "used_ips": 2 } ], "tenant_id": "test-project", "total_ips": 253, "used_ips": 2 } ] } Availability by network ID ~~~~~~~~~~~~~~~~~~~~~~~~~~ GET /v2.0/network-ip-availabilities/{network\_uuid} :: Request to url: /v2.0/network-ip-availabilities/aba3b29b-c119-4b45-afbd-88e500acd970 headers: {'content-type': 'application/json', 'X-Auth-Token': 'SOME_AUTH_TOKEN'} Example response :: Response: HTTP/1.1 200 OK Content-Type: application/json; charset=UTF-8 .. code:: { "network_ip_availability": { "network_id": "f944c153-3f46-417b-a3c2-487cd9a456b9", "network_name": "net1", "subnet_ip_availability": [ { "cidr": "10.0.0.0/24", "ip_version": 4, "subnet_name": "", "subnet_id": "46b1406a-8373-454c-8eb8-500a09eb77fb", "total_ips": 253, "used_ips": 3 } ], "tenant_id": "test-project", "total_ips": 253, "used_ips": 3 } } Supported Query Filters ~~~~~~~~~~~~~~~~~~~~~~~ This API currently supports the following query parameters: * **network_id**: Returns availability for the network matching the network ID. Note: This query (?network_id={network_id_guid})is roughly equivalent to *Availability by network ID* section except it returns the plural response form as a list rather than as an item. * **network_name**: Returns availability for network matching the provided name * **tenant_id**: Returns availability for all networks owned by the provided project ID. * **ip_version**: Filters network subnets by those supporting the supplied ip version. Values can be either 4 or 6. Query filters can be combined to further narrow results and what is returned will match all criteria. When a parameter is specified more than once, it will return results that match both. Examples: :: # Fetch IPv4 availability for a specific project uuid GET /v2.0/network-ip-availabilities?ip_version=4&tenant_id=example-project-uuid # Fetch multiple networks by their ids GET /v2.0/network-ip-availabilities?network_id=uuid_sample_1&network_id=uuid_sample_2 neutron-12.1.1/doc/source/contributor/internals/l2_agent_extensions.rst0000664000175000017500000000351613553660046026447 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) L2 agent extensions =================== L2 agent extensions are part of a generalized L2/L3 extension framework. See :doc:`agent extensions `. Open vSwitch agent API ~~~~~~~~~~~~~~~~~~~~~~ * neutron.plugins.ml2.drivers.openvswitch.agent.ovs_agent_extension_api Open vSwitch agent API object includes two methods that return wrapped and hardened bridge objects with cookie values allocated for calling extensions:: #. request_int_br #. request_tun_br Bridge objects returned by those methods already have new default cookie values allocated for extension flows. All flow management methods (add_flow, mod_flow, ...) enforce those allocated cookies. Linuxbridge agent API ~~~~~~~~~~~~~~~~~~~~~~ * neutron.plugins.ml2.drivers.linuxbridge.agent.linuxbridge_agent_extension_api The Linux bridge agent extension API object includes a method that returns an instance of the IptablesManager class, which is used by the L2 agent to manage security group rules:: #. get_iptables_manager neutron-12.1.1/doc/source/contributor/internals/quality_of_service.rst0000664000175000017500000004763513553660047026404 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Quality of Service ================== Quality of Service advanced service is designed as a service plugin. The service is decoupled from the rest of Neutron code on multiple levels (see below). QoS extends core resources (ports, networks) without using mixins inherited from plugins but through an ml2 extension driver. Details about the DB models, API extension, and use cases can be found here: `qos spec `_ . Service side design ------------------- * neutron.extensions.qos: base extension + API controller definition. Note that rules are subattributes of policies and hence embedded into their URIs. * neutron.extensions.qos_fip: base extension + API controller definition. Adds qos_policy_id to floating IP, enabling users to set/update the binding QoS policy of a floating IP. * neutron.services.qos.qos_plugin: QoSPlugin, service plugin that implements 'qos' extension, receiving and handling API calls to create/modify policies and rules. * neutron.services.qos.drivers.manager: the manager that passes object actions down to every enabled QoS driver and issues RPC calls when any of the drivers require RPC push notifications. * neutron.services.qos.drivers.base: the interface class for pluggable QoS drivers that are used to update backends about new {create, update, delete} events on any rule or policy change, including precommit events that some backends could need for synchronization reason. The drivers also declare which QoS rules, VIF drivers and VNIC types are supported. * neutron.core_extensions.base: Contains an interface class to implement core resource (port/network) extensions. Core resource extensions are then easily integrated into interested plugins. We may need to have a core resource extension manager that would utilize those extensions, to avoid plugin modifications for every new core resource extension. * neutron.core_extensions.qos: Contains QoS core resource extension that conforms to the interface described above. * neutron.plugins.ml2.extensions.qos: Contains ml2 extension driver that handles core resource updates by reusing the core_extensions.qos module mentioned above. In the future, we would like to see a plugin-agnostic core resource extension manager that could be integrated into other plugins with ease. QoS plugin implementation guide ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The neutron.extensions.qos.QoSPluginBase class uses method proxies for methods relating to QoS policy rules. Each of these such methods is generic in the sense that it is intended to handle any rule type. For example, QoSPluginBase has a create_policy_rule method instead of both create_policy_dscp_marking_rule and create_policy_bandwidth_limit_rule methods. The logic behind the proxies allows a call to a plugin's create_policy_dscp_marking_rule to be handled by the create_policy_rule method, which will receive a QosDscpMarkingRule object as an argument in order to execute behavior specific to the DSCP marking rule type. This approach allows new rule types to be introduced without requiring a plugin to modify code as a result. As would be expected, any subclass of QoSPluginBase must override the base class's abc.abstractmethod methods, even if to raise NotImplemented. Supported QoS rule types ~~~~~~~~~~~~~~~~~~~~~~~~ Each QoS driver has a property called supported_rule_types, where the driver exposes the rules it's able to handle. For a list of all rule types, see: neutron.services.qos.qos_consts.VALID_RULE_TYPES. The list of supported QoS rule types exposed by neutron is calculated as the common subset of rules supported by all active QoS drivers. Note: the list of supported rule types reported by core plugin is not enforced when accessing QoS rule resources. This is mostly because then we would not be able to create rules while at least one of the QoS driver in gate lacks support for the rules we're trying to test. Database models ~~~~~~~~~~~~~~~ QoS design defines the following two conceptual resources to apply QoS rules for a port, a network or a floating IP: * QoS policy * QoS rule (type specific) Each QoS policy contains zero or more QoS rules. A policy is then applied to a network or a port, making all rules of the policy applied to the corresponding Neutron resource. When applied through a network association, policy rules could apply or not to neutron internal ports (like router, dhcp, load balancer, etc..). The QosRule base object provides a default should_apply_to_port method which could be overridden. In the future we may want to have a flag in QoSNetworkPolicyBinding or QosRule to enforce such type of application (for example when limiting all the ingress of routers devices on an external network automatically). Each project can have at most one default QoS policy, although is not mandatory. If a default QoS policy is defined, all new networks created within this project will have assigned this policy, as long as no other QoS policy is explicitly attached during the creation process. If the default QoS policy is unset, no change to existing networks will be made. From database point of view, following objects are defined in schema: * QosPolicy: directly maps to the conceptual policy resource. * QosNetworkPolicyBinding, QosPortPolicyBinding, QosFIPPolicyBinding: define attachment between a Neutron resource and a QoS policy. * QosPolicyDefault: defines a default QoS policy per project. * QosBandwidthLimitRule: defines the rule to limit the maximum egress bandwidth. * QosDscpMarkingRule: defines the rule that marks the Differentiated Service bits for egress traffic. * QosMinimumBandwidthRule: defines the rule that creates a minimum bandwidth constraint. All database models are defined under: * neutron.db.qos.models QoS versioned objects ~~~~~~~~~~~~~~~~~~~~~ For QoS, the following neutron objects are implemented: * QosPolicy: directly maps to the conceptual policy resource, as defined above. * QosPolicyDefault: defines a default QoS policy per project. * QosBandwidthLimitRule: defines the instance bandwidth limit rule type, characterized by a max kbps and a max burst kbits. This rule has also a direction parameter to set the traffic direction, from the instance's point of view. * QosDscpMarkingRule: defines the DSCP rule type, characterized by an even integer between 0 and 56. These integers are the result of the bits in the DiffServ section of the IP header, and only certain configurations are valid. As a result, the list of valid DSCP rule types is: 0, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 46, 48, and 56. * QosMinimumBandwidthRule: defines the minimum assured bandwidth rule type, characterized by a min_kbps parameter. This rule has also a direction parameter to set the traffic direction, from the instance point of view. The only direction now implemented is egress. Those are defined in: * neutron.objects.qos.policy * neutron.objects.qos.rule For QosPolicy neutron object, the following public methods were implemented: * get_network_policy/get_port_policy/get_fip_policy: returns a policy object that is attached to the corresponding Neutron resource. * attach_network/attach_port/attach_floatingip: attach a policy to the corresponding Neutron resource. * detach_network/detach_port/detach_floatingip: detach a policy from the corresponding Neutron resource. In addition to the fields that belong to QoS policy database object itself, synthetic fields were added to the object that represent lists of rules that belong to the policy. To get a list of all rules for a specific policy, a consumer of the object can just access the corresponding attribute via: * policy.rules Implementation is done in a way that will allow adding a new rule list field with little or no modifications in the policy object itself. This is achieved by smart introspection of existing available rule object definitions and automatic definition of those fields on the policy class. Note that rules are loaded in a non lazy way, meaning they are all fetched from the database on policy fetch. For QosRule objects, an extendable approach was taken to allow easy addition of objects for new rule types. To accommodate this, fields common to all types are put into a base class called QosRule that is then inherited into type-specific rule implementations that, ideally, only define additional fields and some other minor things. Note that the QosRule base class is not registered with oslo.versionedobjects registry, because it's not expected that 'generic' rules should be instantiated (and to suggest just that, the base rule class is marked as ABC). QoS objects rely on some primitive database API functions that are added in: * neutron.db.api: those can be reused to fetch other models that do not have corresponding versioned objects yet, if needed. * neutron.db.qos.api: contains database functions that are specific to QoS models. RPC communication ~~~~~~~~~~~~~~~~~ Details on RPC communication implemented in reference backend driver are discussed in `a separate page `_. The flow of updates is as follows: * if a port that is bound to the agent is attached to a QoS policy, then ML2 plugin detects the change by relying on ML2 QoS extension driver, and notifies the agent about a port change. The agent proceeds with the notification by calling to get_device_details() and getting the new port dict that contains a new qos_policy_id. Each device details dict is passed into l2 agent extension manager that passes it down into every enabled extension, including QoS. QoS extension sees that there is a new unknown QoS policy for a port, so it uses ResourcesPullRpcApi to fetch the current state of the policy (with all the rules included) from the server. After that, the QoS extension applies the rules by calling into QoS driver that corresponds to the agent. * For floating IPs, a ``fip_qos`` L3 agent extension was implemented. This extension receives and processes router updates. For each update, it goes over each floating IP associated to the router. If a floating IP has a QoS policy associated to it, the extension uses ResourcesPullRpcApi to fetch the policy details from the Neutron server. If the policy includes ``bandwidth_limit`` rules, the extension applies them to the appropriate router device by directly calling the l3_tc_lib. * on existing QoS policy update (it includes any policy or its rules change), server pushes the new policy object state through ResourcesPushRpcApi interface. The interface fans out the serialized (dehydrated) object to any agent that is listening for QoS policy updates. If an agent have seen the policy before (it is attached to one of the ports/floating IPs it maintains), then it goes with applying the updates to the port/floating IP. Otherwise, the agent silently ignores the update. Agent side design ----------------- Reference agents implement QoS functionality using an `L2 agent extension <./l2_agent_extensions.html>`_. * neutron.agent.l2.extensions.qos defines QoS L2 agent extension. It receives handle_port and delete_port events and passes them down into QoS agent backend driver (see below). The file also defines the QosAgentDriver interface. Note: each backend implements its own driver. The driver handles low level interaction with the underlying networking technology, while the QoS extension handles operations that are common to all agents. For L3 agent: * neutron.agent.l3.extensions.fip_qos defines QoS L3 agent extension. It implements the L3 agent side of floating IP rate limit. For all routers, if floating IP has QoS ``bandwidth_limit`` rules, the corresponding TC filters will be added to the appropriate router device, depending on the router type. Agent backends ~~~~~~~~~~~~~~ At the moment, QoS is supported by Open vSwitch, SR-IOV and Linux bridge ml2 drivers. Each agent backend defines a QoS driver that implements the QosAgentDriver interface: * Open vSwitch (QosOVSAgentDriver); * SR-IOV (QosSRIOVAgentDriver); * Linux bridge (QosLinuxbridgeAgentDriver). Table of Neutron backends, supported rules and traffic direction (from the VM point of view) :: +----------------------+----------------+----------------+----------------+ | Rule \ Backend | Open vSwitch | SR-IOV | Linux Bridge | +----------------------+----------------+----------------+----------------+ | Bandwidth Limit | Egress/Ingress | Egress (1) | Egress/Ingress | +----------------------+----------------+----------------+----------------+ | Minimum Bandwidth | - | Egress | - | +----------------------+----------------+----------------+----------------+ | DSCP Marking | Egress | - | Egress | +----------------------+----------------+----------------+----------------+ (1) Max burst parameter is skipped because it's not supported by ip tool. Open vSwitch ++++++++++++ Open vSwitch implementation relies on the new ovs_lib OVSBridge functions: * get_egress_bw_limit_for_port * create_egress_bw_limit_for_port * delete_egress_bw_limit_for_port * get_ingress_bw_limit_for_port * update_ingress_bw_limit_for_port * delete_ingress_bw_limit_for_port An egress bandwidth limit is effectively configured on the port by setting the port Interface parameters ingress_policing_rate and ingress_policing_burst. That approach is less flexible than linux-htb, Queues and OvS QoS profiles, which we may explore in the future, but which will need to be used in combination with openflow rules. An ingress bandwidth limit is effectively configured on the port by setting Queue and OvS QoS profile with linux-htb type for port. The Open vSwitch DSCP marking implementation relies on the recent addition of the ovs_agent_extension_api OVSAgentExtensionAPI to request access to the integration bridge functions: * add_flow * mod_flow * delete_flows * dump_flows_for The DSCP markings are in fact configured on the port by means of openflow rules. SR-IOV ++++++ SR-IOV bandwidth limit implementation relies on the new pci_lib function: * set_vf_max_rate As the name of the function suggests, the limit is applied on a Virtual Function (VF). ip link interface has the following limitation for bandwidth limit: it uses Mbps as units of bandwidth measurement, not kbps, and does not support float numbers. So in case the limit is set to something less than 1000 kbps, it's set to 1 Mbps only. If the limit is set to something that does not divide to 1000 kbps chunks, then the effective limit is rounded to the nearest integer Mbps value. Linux bridge ~~~~~~~~~~~~ The Linux bridge implementation relies on the new tc_lib functions. For egress bandwidth limit rule: * set_filters_bw_limit * update_filters_bw_limit * delete_filters_bw_limit The egress bandwidth limit is configured on the tap port by setting traffic policing on tc ingress queueing discipline (qdisc). Details about ingress qdisc can be found on `lartc how-to `__. The reason why ingress qdisc is used to configure egress bandwidth limit is that tc is working on traffic which is visible from "inside bridge" perspective. So traffic incoming to bridge via tap interface is in fact outgoing from Neutron's port. This implementation is the same as what Open vSwitch is doing when ingress_policing_rate and ingress_policing_burst are set for port. For ingress bandwidth limit rule: * set_tbf_bw_limit * update_tbf_bw_limit * delete_tbf_bw_limit The ingress bandwidth limit is configured on the tap port by setting a simple `tc-tbf `_ queueing discipline (qdisc) on the port. It requires a value of HZ parameter configured in kernel on the host. This value is necessary to calculate the minimal burst value which is set in tc. Details about how it is calculated can be found in `here `_. This solution is similar to Open vSwitch implementation. The Linux bridge DSCP marking implementation relies on the linuxbridge_extension_api to request access to the IptablesManager class and to manage chains in the ``mangle`` table in iptables. QoS driver design ----------------- QoS framework is flexible enough to support any third-party vendor. To integrate a third party driver (that just wants to be aware of the QoS create/update/delete API calls), one needs to implement 'neutron.services.qos.drivers.base', and register the driver during the core plugin or mechanism driver load, see neutron.services.qos.drivers.openvswitch.driver register method for an example. .. note:: All the functionality MUST be implemented by the vendor, neutron's QoS framework will just act as an interface to bypass the received QoS API request and help with database persistence for the API operations. .. note:: L3 agent ``fip_qos`` extension does not have a driver implementation, it directly uses the ``l3_tc_lib`` for all types of routers. Configuration ------------- To enable the service, the following steps should be followed: On server side: * enable qos service in service_plugins; * for ml2, add 'qos' to extension_drivers in [ml2] section; * for L3 floating IP QoS, add 'qos' and 'router' to service_plugins. On agent side (OVS): * add 'qos' to extensions in [agent] section. On L3 agent side: * For for floating IPs QoS support, add 'fip_qos' to extensions in [agent] section. Testing strategy ---------------- All the code added or extended as part of the effort got reasonable unit test coverage. Neutron objects ~~~~~~~~~~~~~~~ Base unit test classes to validate neutron objects were implemented in a way that allows code reuse when introducing a new object type. There are two test classes that are utilized for that: * BaseObjectIfaceTestCase: class to validate basic object operations (mostly CRUD) with database layer isolated. * BaseDbObjectTestCase: class to validate the same operations with models in place and database layer unmocked. Every new object implemented on top of one of those classes is expected to either inherit existing test cases as is, or reimplement it, if it makes sense in terms of how those objects are implemented. Specific test classes can obviously extend the set of test cases as they see needed (f.e. you need to define new test cases for those additional methods that you may add to your object implementations on top of base semantics common to all neutron objects). Functional tests ~~~~~~~~~~~~~~~~ Additions to ovs_lib to set bandwidth limits on ports are covered in: * neutron.tests.functional.agent.test_ovs_lib New functional tests for tc_lib to set bandwidth limits on ports are in: * neutron.tests.functional.agent.linux.test_tc_lib New functional tests for test_l3_tc_lib to set TC filters on router floating IP related device are covered in: * neutron.tests.functional.agent.linux.test_l3_tc_lib New functional tests for L3 agent floating IP rate limit: * neutron.tests.functional.agent.l3.extensions.test_fip_qos_extension API tests ~~~~~~~~~ API tests for basic CRUD operations for ports, networks, policies, and rules were added in: * neutron.tests.tempest.api.test_qos neutron-12.1.1/doc/source/contributor/internals/service_extensions.rst0000664000175000017500000000676613553660047026427 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Service Extensions ================== Historically, Neutron supported the following advanced services: #. **FWaaS** (*Firewall-as-a-Service*): runs as part of the L3 agent. #. **LBaaS** (*Load-Balancer-as-a-Service*): implemented purely inside neutron-server, does not interact directly with agents. #. **VPNaaS** (*VPN-as-a-Service*): derives from L3 agent to add VPNaaS functionality. Starting with the Kilo release, these services are split into separate repositories, and more extensions are being developed as well. Service plugins are a clean way of adding functionality in a cohesive manner and yet, keeping them decoupled from the guts of the framework. The aforementioned features are developed as extensions (also known as service plugins), and more capabilities are being added to Neutron following the same pattern. For those that are deemed 'orthogonal' to any network service (e.g. tags, timestamps, auto_allocate, etc), there is an informal `mechanism `_ to have these loaded automatically at server startup. If you consider adding an entry to the dictionary, please be kind and reach out to your PTL or a member of the drivers team for approval. #. http://git.openstack.org/cgit/openstack/neutron-fwaas/ #. http://git.openstack.org/cgit/openstack/neutron-lbaas/ #. http://git.openstack.org/cgit/openstack/neutron-vpnaas/ Calling the Core Plugin from Services ------------------------------------- There are many cases where a service may want to create a resource managed by the core plugin (e.g. ports, networks, subnets). This can be achieved by importing the plugins directory and getting a direct reference to the core plugin: .. code:: python from neutron_lib.plugins import directory plugin = directory.get_plugin() plugin.create_port(context, port_dict) However, there is an important caveat. Calls to the core plugin in almost every case should not be made inside of an ongoing transaction. This is because many plugins (including ML2), can be configured to make calls to a backend after creating or modifying an object. If the call is made inside of a transaction and the transaction is rolled back after the core plugin call, the backend will not be notified that the change was undone. This will lead to consistency errors between the core plugin and its configured backend(s). ML2 has a guard against certain methods being called with an active DB transaction to help prevent developers from accidentally making this mistake. It will raise an error that says explicitly that the method should not be called within a transaction. neutron-12.1.1/doc/source/contributor/internals/policy.rst0000664000175000017500000003576513553660047024010 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Authorization Policy Enforcement ================================== As most OpenStack projects, Neutron leverages oslo_policy [#]_. However, since Neutron loves to be special and complicate every developer's life, it also "augments" oslo_policy capabilities by: * A wrapper module with its own API: neutron.policy; * The ability of adding fine-grained checks on attributes for resources in request bodies; * The ability of using the policy engine to filter out attributes in responses; * Adding some custom rule checks beyond those defined in oslo_policy; This document discusses Neutron-specific aspects of policy enforcement, and in particular how the enforcement logic is wired into API processing. For any other information please refer to the developer documentation for oslo_policy [#]_. Authorization workflow ----------------------- The Neutron API controllers perform policy checks in two phases during the processing of an API request: * Request authorization, immediately before dispatching the request to the plugin layer for ``POST``, ``PUT``, and ``DELETE``, and immediately after returning from the plugin layer for ``GET`` requests; * Response filtering, when building the response to be returned to the API consumer. Request authorization ~~~~~~~~~~~~~~~~~~~~~~ The aim of this step is to authorize processing for a request or reject it with an error status code. This step uses the ``neutron.policy.enforce`` routine. This routine raises ``oslo_policy.PolicyNotAuthorized`` when policy enforcement fails. The Neutron REST API controllers catch this exception and return: * A 403 response code on a ``POST`` request or an ``PUT`` request for an object owned by the project submitting the request; * A 403 response for failures while authorizing API actions such as ``add_router_interface``; * A 404 response for ``DELETE``, ``GET`` and all other ``PUT`` requests. For ``DELETE`` operations the resource must first be fetched. This is done invoking the same ``_item`` [#]_ method used for processing ``GET`` requests. This is also true for ``PUT`` operations, since the Neutron API implements ``PATCH`` semantics for ``PUTs``. The criteria to evaluate are built in the ``_build_match_rule`` [#]_ routine. This routine takes in input the following parameters: * The action to be performed, in the ``_`` form, ``e.g.: create_network`` * The data to use for performing checks. For ``POST`` operations this could be a partial specification of the object, whereas it is always a full specification for ``GET``, ``PUT``, and ``DELETE`` requests, as resource data are retrieved before dispatching the call to the plugin layer. * The collection name for the resource specified in the previous parameter; for instance, for a network it would be the "networks". The ``_build_match_rule`` routine returns a ``oslo_policy.RuleCheck`` instance built in the following way: * Always add a check for the action being performed. This will match a policy like create_network in ``policy.json``; * Return for ``GET`` operations; more detailed checks will be performed anyway when building the response; * For each attribute which has been explicitly specified in the request create a rule matching policy names in the form ``_:`` rule, and link it with the previous rule with an 'And' relationship (using ``oslo_policy.AndCheck``); this step will be performed only if the enforce_policy flag is set to True in the resource attribute descriptor (usually found in a data structure called ``RESOURCE_ATTRIBUTE_MAP``); * If the attribute is a composite one then further rules will be created; These will match policy names in the form ``_: :``. An 'And' relationship will be used in this case too. As all the rules to verify are linked by 'And' relationships, all the policy checks should succeed in order for a request to be authorized. Rule verification is performed by ``oslo_policy`` with no "customization" from the Neutron side. Response Filtering ~~~~~~~~~~~~~~~~~~~ Some Neutron extensions, like the provider networks one, add some attribute to resources which are however not meant to be consumed by all clients. This might be because these attributes contain implementation details, or are meant only to be used when exchanging information between services, such as Nova and Neutron; For this reason the policy engine is invoked again when building API responses. This is achieved by the ``_exclude_attributes_by_policy`` [#]_ method in ``neutron.api.v2.base.Controller``; This method, for each attribute in the response returned by the plugin layer, first checks if the ``is_visible`` flag is True. In that case it proceeds to checking policies for the attribute; if the policy check fails the attribute is added to a list of attributes that should be removed from the response before returning it to the API client. The neutron.policy API ------------------------ The ``neutron.policy`` module exposes a simple API whose main goal if to allow the REST API controllers to implement the authorization workflow discussed in this document. It is a bad practice to call the policy engine from within the plugin layer, as this would make request authorization dependent on configured plugins, and therefore make API behaviour dependent on the plugin itself, which defies Neutron tenet of being backend agnostic. The neutron.policy API exposes the following routines: * ``init`` Initializes the policy engine loading rules from the json policy (files). This method can safely be called several times. * ``reset`` Clears all the rules currently configured in the policy engine. It is called in unit tests and at the end of the initialization of core API router [#]_ in order to ensure rules are loaded after all the extensions are loaded. * ``refresh`` Combines init and reset. Called when a SIGHUP signal is sent to an API worker. * ``set_rules`` Explicitly set policy engine's rules. Used only in unit tests. * ``check`` Perform a check using the policy engine. Builds match rules as described in this document, and then evaluates the resulting rule using oslo_policy's policy engine. Returns True if the checks succeeds, false otherwise. * ``enforce`` Operates like the check routine but raises if the check in oslo_policy fails. * ``check_is_admin`` Enforce the predefined context_is_admin rule; used to determine the is_admin property for a neutron context. * ``check_is_advsvc`` Enforce the predefined context_is_advsvc rule; used to determine the is_advsvc property for a neutron context. Neutron specific policy rules ------------------------------ Neutron provides two additional policy rule classes in order to support the "augmented" authorization capabilities it provides. They both extend ``oslo_policy.RuleCheck`` and are registered using the ``oslo_policy.register`` decorator. OwnerCheck: Extended Checks for Resource Ownership ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This class is registered for rules matching the ``tenant_id`` keyword and overrides the generic check performed by oslo_policy in this case. It uses for those cases where neutron needs to check whether the project submitting a request for a new resource owns the parent resource of the one being created. Current usages of ``OwnerCheck`` include, for instance, creating and updating a subnet. The check, performed in the ``__call__`` method, works as follows: * verify if the target field is already in the target data. If yes, then simply verify whether the value for the target field in target data is equal to value for the same field in credentials, just like ``oslo_policy.GeneriCheck`` would do. This is also the most frequent case as the target field is usually ``tenant_id``; * if the previous check failed, extract a parent resource type and a parent field name from the target field. For instance ``networks:tenant_id`` identifies the ``tenant_id`` attribute of the ``network`` resource; * if no parent resource or target field could be identified raise a ``PolicyCheckError`` exception; * Retrieve a 'parent foreign key' from the ``RESOURCE_FOREIGN_KEYS`` data structure in ``neutron.api.v2.attributes``. This foreign key is simply the attribute acting as a primary key in the parent resource. A ``PolicyCheckError`` exception will be raised if such 'parent foreign key' cannot be retrieved; * Using the core plugin, retrieve an instance of the resource having 'parent foreign key' as an identifier; * Finally, verify whether the target field in this resource matches the one in the initial request data. For instance, for a port create request, verify whether the ``tenant_id`` of the port data structure matches the ``tenant_id`` of the network where this port is being created. FieldCheck: Verify Resource Attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This class is registered with the policy engine for rules matching the 'field' keyword, and provides a way to perform fine grained checks on resource attributes. For instance, using this class of rules it is possible to specify a rule for granting every project read access to shared resources. In policy.json, a FieldCheck rules is specified in the following way:: > field: := This will result in the initialization of a FieldCheck that will check for ```` in the target resource data, and return ``True`` if it is equal to ```` or return ``False`` is the ```` either is not equal to ```` or does not exist at all. Guidance for API developers ---------------------------- When developing REST APIs for Neutron it is important to be aware of how the policy engine will authorize these requests. This is true both for APIs served by Neutron "core" and for the APIs served by the various Neutron "stadium" services. * If an attribute of a resource might be subject to authorization checks then the ``enforce_policy`` attribute should be set to ``True``. While setting this flag to ``True`` for each attribute is a viable strategy, it is worth noting that this will require a call to the policy engine for each attribute, thus consistently increasing the time required to complete policy checks for a resource. This could result in a scalability issue, especially in the case of list operations retrieving a large number of resources; * Some resource attributes, even if not directly used in policy checks might still be required by the policy engine. This is for instance the case of the ``tenant_id`` attribute. For these attributes the ``required_by_policy`` attribute should always set to ``True``. This will ensure that the attribute is included in the resource data sent to the policy engine for evaluation; * The ``tenant_id`` attribute is a fundamental one in Neutron API request authorization. The default policy, ``admin_or_owner``, uses it to validate if a project owns the resource it is trying to operate on. To this aim, if a resource without a tenant_id is created, it is important to ensure that ad-hoc authZ policies are specified for this resource. * There is still only one check which is hardcoded in Neutron's API layer: the check to verify that a project owns the network on which it is creating a port. This check is hardcoded and is always executed when creating a port, unless the network is shared. Unfortunately a solution for performing this check in an efficient way through the policy engine has not yet been found. Due to its nature, there is no way to override this check using the policy engine. * It is strongly advised to not perform policy checks in the plugin or in the database management classes. This might lead to divergent API behaviours across plugins. Also, it might leave the Neutron DB in an inconsistent state if a request is not authorized after it has already been dispatched to the backend. Notes ----------------------- * No authorization checks are performed for requests coming from the RPC over AMQP channel. For all these requests a neutron admin context is built, and the plugins will process them as such. * For ``PUT`` and ``DELETE`` requests a 404 error is returned on request authorization failures rather than a 403, unless the project submitting the request own the resource to update or delete. This is to avoid conditions in which an API client might try and find out other projects' resource identifiers by sending out ``PUT`` and ``DELETE`` requests for random resource identifiers. * There is no way at the moment to specify an ``OR`` relationship between two attributes of a given resource (eg.: ``port.name == 'meh' or port.status == 'DOWN'``), unless the rule with the or condition is explicitly added to the policy.json file. * ``OwnerCheck`` performs a plugin access; this will likely require a database access, but since the behaviour is implementation specific it might also imply a round-trip to the backend. This class of checks, when involving retrieving attributes for 'parent' resources should be used very sparingly. * In order for ``OwnerCheck`` rules to work, parent resources should have an entry in ``neutron.api.v2.attributes.RESOURCE_FOREIGN_KEYS``; moreover the resource must be managed by the 'core' plugin (ie: the one defined in the core_plugin configuration variable) References ---------- .. [#] `Oslo policy module `_ .. [#] `Oslo policy developer `_ .. [#] API controller item_ method .. _item: http://git.openstack.org/cgit/openstack/neutron/tree/neutron/api/v2/base.py?id=2015.1.1#n282 .. [#] Policy engine's build_match_rule_ method .. _build_match_rule: http://git.openstack.org/cgit/openstack/neutron/tree/neutron/policy.py?id=2015.1.1#n187 .. [#] exclude_attributes_by_policy_ method .. _exclude_attributes_by_policy: http://git.openstack.org/cgit/openstack/neutron/tree/neutron/api/v2/base.py?id=2015.1.1#n132 .. [#] Policy reset_ in neutron.api.v2.router .. _reset: http://git.openstack.org/cgit/openstack/neutron/tree/neutron/api/v2/router.py?id=2015.1.1#n122 neutron-12.1.1/doc/source/contributor/internals/images/0000775000175000017500000000000013553660156023205 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/contributor/internals/images/live-mig.png0000664000175000017500000035071313553660046025433 0ustar zuulzuul00000000000000‰PNG  IHDRñв0t)tEXtcopyleftGenerated by http://plantuml.com09JzTXtplantumlxœÕX]oÛ6}àÿpÑÛ@=XjÓ,ÆZ4M24EÒIš= ƒ@K´L@&‘Rù、d}Y²•::¿ØÖ%ïÏ=<öG©H¬’U8°–b*¤²”NV,ˆ‰b‚Ã=SK©œp¯HQ˜0.RòÇ䧉ŠqãŠÆÄÓ;¤v¡W汈pe–ºžà~â)·ÚVQ¢¨Ýmr¦,ª+iœÒ¦ÇÜ:. (W[׿fеùM±{`Õ €É‡zÚØ Ý37ëXº)~jVWßÕp㠛뫓ß<†ø1Š©[õ:wøuê~ã·ÖíøìæÖ¤)•‰XÕ½5ºÙØÝÌs`ù´so[2"5€Ü”-¤Nbëj»ªžÄÀ b‘DØ(a–PðÓð¼˜ô,À×fß;ú“D>FÊ:4œ3î3Ì0À‚…tøþq˜O\%†³áRHå ŸÆY”Ξ˜6{Ú@š½>~©F Â±Ž±n¦âfiôç®X…—ÌîS‰ºÌ\àV¡éX _¯ªÕ¾_PYѬˆò– ›=§Ü7žûVì¼HÅή’œ2ë+áC §w×9hÚŠhAz3îÖa±;ݬÁñ²©)Ò¯nÝD½ö²yÊbKÂýJPË&i·yÅœ`/ÍìÔdlÞÞmF«FÌz†I„5¥Ì£ÛN¾`àö‘+¹. ÊÍÜI|W„…Ò5T…º üJýµ}”¿ã®tEÿvÂéÓÏgÝË–sü©Âs¾Éj2%¯ëà ØYâRÂÆU­f†4ŽP5¨D“¸¬fõ”`’qšµ¥M¯FÚ˜‹á¢D»§®¬"Ÿ˜ ªÞÀµ­t#ÛÞDvN˜Ù0Ý]– ‚à‰8¦2æ>Øþ"jScì_ÜóÞ`·÷BJ‘Yåt³“þõ÷×q_¨u³^cÅóжƖ¥]çU›Æ˜ÁW¹"šÝhm¼Ò…±hÃæ]¨ð®~&Ï£>õë,¼UüÙæâCÆ«H>ãoç® û…žê-2¥jéÍů}óUûìÐ`ö Òªàz,—>/—ù…Âz blöøôôL Ûå=}µË¶ØPi¥©EÎÔGÇ ]Ó©nÍñ^Ò_ y»ï†ýËqžù»á¿(J¦Ï“Ñ:¹Q"ÌÀ„GÞ& Pž„(ít2&// _B”ìÏß¾_œö&âÿ±lÙÖ„ã“Ûó»³—îÂÞwQ–V·ô¹ éfôX|Äoæë*$\}»¼LWêÿl~Ÿ¾=Ý.8NpÀ>œÍÞLáDÓ®3µß­ÑñÕñÉgLW$1æëã)ÆlžhÒ[_HJF·—c¸9ƒë„+¶¢pÆQ) ¾ÂT¬óO—ðå…’}'ïÞNe|´ˆ¦í{zèLÝ©£›ë#øóê`:¶.O¾ÿVS:cÓ¶#Ë€IDATxÚìݘUw} ü©ã8Ž)]œ¥Óé« FDLK1$Rw‘F^§³ï,OÄy)ï“'åÍF¤Ô¬k²–¤¸#M)e³y L‚H0Ž /Iœ5®†Ò˜ÀŽ & ¤8òŠ”÷'GÏ{sÿœ¹ó‡Ëýóù<çá9÷ÜsÏùß÷þ¾÷Üïέº”‚*]%AMJƒš>”5}( júPÔô 4¨é@iPÓ€Ò ¦¥AMJƒš>”5} ìÎo.Ëœ¿ê)§} ¡W½y¥ø.­7åñFÒl€b?eÒ@¹ßTFM?sËEu°EؼR|É›bNÃßãU|çHC9‰Ò@¹ßTFI¨ÈS©®ð‘ÕÃlɵ͛`('Qº(·ó›lW‹O:5ÌlÛ¶-^-̇%Ó¦M‹îÙ³gÆŒuuu555S¦LéììpkÖ¬inn®­­½þú뻺º>ó™ÏÔ×ׇ‡a_/¾øbfc‚7F/¹öÚk×­[—ÙÔ-[¶Üxã¡ñòU«V…ùêêê1cÆÜ~ûí§OŸŽWŽeÝ×ÓO?}ÓM7Õ]fÂôöoݺ5ì+4&¬ð¡}¨§§'ëÁ†¦†•÷íÛ=lhh‡Í¿ð á©w¼ãi Hh^ž;Mhr(;6þüÑ£G‡8†»í¶Ûº»»“ã˜Ð¤û0-^ù¿1rEöRb•3zê‘G A  {Y¿~}ò;?ë³ÉoÅ„5²Ý8à¨ÌÕE™BÜW¬XFnB;óa°æpŽ%!Q$ôyˆÈò#5ró}Yۖϑ晬"ác40'MšmíŠæ±A¥€ûΫ €r;¿ÉVz衇ÂÌœ9sâÕÂ|X–‡ù]»vUWWèCêëë»páÂ'>ñ‰ðÔºuë’w1þü°òÓO?úðoÿöoÃ|\[LmLWWW˜{9uêÔéÓ§g̘‘ÙÔ[n¹åÈ‘#©ûêè航T[¶l +„¶en9sÉŽ;Âü¬Y³ÂŽÎœ9fÂð0uÍo¼1ìëâÅ‹Q%î†nÈz°wÝuWxvõêÕa~ß¾}Ñk_xá…K¿,áÝyç =ŸÖ¼=Ìï{ß‹†™Ì¦HERÓTd“û°¤kú ¡Lè¢Ñ£GG5ý»îºkh5ýðªðÚÁÖôLeSÓÏ'%柬®JM?!”üw^]”ÛùMîZUôs¯6lÿNœ81^Ý¢««k»¸”wM?í>{÷î°’›ZÒ:xð`þEºh_ñ}'nÒ’°÷؃>Ý>;ê½M›6Ewcÿ†§òéùÁî4¹ý ¡Œ]¸paûöí™·CÉ¿IƒíÃA½1Ù´-äÓ¤èZ鸊úÒK/åÿV°—F°B™ÐE!¸[·n3gÎÐî½ÖÞsçÎ Ûɼ÷N®cINÉ}žçûGîpjúÉG:Ød5Ì{ï ³7òI%öWåv~“»VÕÑÑŽ7.¾#|ä;ßùN]]Ýĉ÷ïßž>}zÆ Ó§OÏg—ò®é?ùä“aþÖ[oíïïÏõ¹i;š2eJô#¨çÏŸ.–׉ޢ¯¯/ë~£h9sfê©f½ñ}ÂÞc‡Žo¾ýªj|{ðTÖ$7oÀ&·?!”á%Û¶m Ýué—×€O›6-Ï8³õÆHˆì€UÚ´&eýÜ©S§†ù•+W†ù#GŽDµÑ<ߊöÒvcB(º(Ú¿fÍš¸ýÍÍÍ íŒn£„õëBc{,ɉ"¹Ïó#;ró¯ég¶-ùH›¬¢ßÈ0aBT?vìØwÜq¥óØ R@‰}çÕ@¹ßä®dE?ÂÕ¦£ŸåŒíÛ·¯µµ5º§Ç¸qãæÏŸßÝÝÏ..å]Ó6mÚ4aÂ„ššš‰'®_¿~À[Tïß¿ÿ†në455EËÇë¬[·n̘1 ûzúé§§OŸ^[[vwÓM7íØ±#Wƒ/åQ·.¦Ž‹b7ÞxcæÏªyî4¡ý ¡ Q›7o^ØuXþmooO¸‰ö€MTê‘Ù«´<òHxÿ„&…÷RxeÝþáÇgΜZv¢¶uëÖüߊöÒvcB(º(SˆòŠ+’o®2yòä°NòMÕ<–„D‘ÜçùŒˆ‘¹ù¾Ì¶%é`“ե˗؇-D{¹þúë;;;¯tT*(±ï¼ºàªˆ.~¿öÚkuùž»ã®÷ÞŠP&ß t@ÁÌ›7/º‰yooïìÙ³«ªªR¯³†ÎÝG®¦ï­¥ú½@̶mÛ¦NZ[[[WW÷¡}(<Ô'ä¯ö2oE¨djúPÔô 4¨é@iPÓ€Ò ¦¥AMJCöšþ?½ú“Wžù¾éªLÿôý†M€Š<@04†¶¡ H¤Ò& ÍšL"+²"ëüy(5ý°›MUSMWe:ñì÷ ›y€`h mCH¥M@š5)2˜DVdEÖùóÐkú?|à ¯<³ÉT°é‡ù…A ]*ÚÁp¾#Ú†6 ‘J›€4kRd0‰¬ÈЬóç¡ÔôÃ.ÿùŸŸ7lê{zÓ †®m€`8ß‘ mCH¥M@š5)2˜DVdEÖù³š~Éø›ùpww÷Þ½{8püøñ3gÎPÉ|G2´‰TÚ¤Y“"ƒIdM"kºçÏjúEàÎ{W<ñÄ;wîüîw¿b|òäI*¹ïH†6 ‘J›€4kRd0‰¬IdMWâüYM¿èüåOß¿qãÆ­[·†ïÝ»÷øñãTrß‘ m@"•6iÖ¤È`Y“Èš®Äù³š~ÑxýŸÝ÷È#„?ñÄÝÝ݇ ’ øŽdh©´ H³&E“ÈšDÖt%ΟÕô‹.À›ïùBð—¾ô¥ÎÎÎ;w8p@€J.@à;’¡ H¤Ò& ÍšL"kYÓ•8VÓ7tM¾Áâ;’¡mhƒDj’6iV‘AdEÖ$²&5ý’šªªª ]“o°øŽdhÚ ‘š¤M ÒÒl1DVdEVdEVM¿ oš‘zúW+ ßþö#Ó¦Mð%¾Áâ;R í;š1ãwkkß4fÌ5‹µýä'{ mH}åPÓJë|µ`™6ÞQþçW÷48Ïvm‘Adº"Ÿ…ÈÆ; ÝÕÖ6sôèQ55oljjX¼øcÿðϳ"[ºcVM_M_áoA ‰5Œ@5}ÔôËihßzëžzêÁŸýì{áliá¹·Ýö‡†6H¤jújú€š~òŽò?‡¼º§Áy¶S}°ä"›gBdãMŸþ¾¯|åþ¨DþÊ+;/þØ-·üŽ1+²¥;f¯`M?41ôé 7L¬©yc]]mkë¿=?ûðß0á×ÃSáßÿöß>–üã?î;ö­ÿôOÿ#^'ÌsÍ?üóG~³¥åÂFÂú³fý~êv²N_ûÚS¦\ý}fíÚ{všOSÚ›Ç×Ö¾éïh ¯Š»>3©K2ÛPõz6é¯ÿziCÿ¨®~ú¥ ¿ù›? +‡§®¿þí»w¯ûò——…p„‡a;ÿëmW [ËC°BȾô¥?OHÑ’¬A Í›<ùa#õõ¿v÷Ý<„ å,Wñ;’¡=ä¡{XÁЉ4!‘æJ&ùg¤ÌÓ¼¡£fN?üáÖð=0ÊÛ!‘nÞüù|š7&×ÇAž/—6ü«H¥›i³~L>‡Œ¦„Sëж¬5‡ä®ÈÚ쬧Áù´s¤êƒ"[øÈ¦¦_f‘¦äà³"[äcöÊÖôÃ!=÷Ü—ÃüO~²çŸøhè²è©G]zçïÿ¾3Ì?ÿü£!Š_þò²0?gÎ-¡#â-„…sç~0Ì\ýÛ¿ýíG~ö³ï…wóÒ¥íííÿ[ÂÁýëÝÔÔÖóÇ?µ`Áœä&7õñÇ¿¿*ü^•OM?WÒ^’ܤpìÿø»¯ÜÐ-­Íœùþ0êÂ.þó¾=ŒŸ0êâ‡3fün¼Zj°ÂüW¿ºbÀ`¥=õÍo®ž6mRh@˜ùåá?ûÙO¨éSZ¥(C{hC;L?þqW8Û0´A"Í•H’Iþ)í4ohç¨Y§‰+|‰êòaSóæÍʧÙqcr}äùriT©D3mÖ/ƒÉçÑ”ëÔzóæÏÇmûÁóñ.’»"W³¾®&´sëƒ"[àȬ¦_N‘ý‡xöî»ÿ8ù…ƬÈù˜½²5ýC‡žŒ¾öÚsñßIBG<óÌé÷Šš:õÝÑ=fÎ|¼<̇%i› >zô¨„ƒŸ>ý}O=õ`æò\;TS¿õ­µùÔôsµ!í%ÉMНQ½r5ý PHdñöÃÃãÇŸŠÖÕÕÆ«¥릛Þ;Øtš÷?ÿçWã‡!ïd&P5}мehmh‡iþüÜwߟÚ ‘æJ¤ ÉdPßmROó†vŽšë¢¤“'ŸÉš¢š7&×ÇAž/—6AU‘J4Ófý2˜|™9¥žZ‡¶…óÛ¬5‡ä®ÈÕ섯« íÁú È8²«é—Gdãë¦'Nü­\%³"[cöÊÖôs-©©ycèµÔŒj7a¦¾þ×¢šNø7ÌG«…Þ™={zX' OÖÿÛOiϵ<­`”Só©éçjCÚKÕ¤+QÓ/Ñåzf†â´§ÂØ‹§Ð¤¬‡£¦O‘—¢ í¡ í¿ù›?»õÖÚ ‘&ä–„d2¨ï6Ã?GÍ:Ý}÷‡/ŸøÄG}tùÑ£ßÌ'¦n0×ÇÁ ŽZÚò¬"•h¦Í|vÀsÈhÊujPs‘®È³#XÙG¶`5ý²‰lµ¹s?8þGŒY‘-Ý1[\5ý0Ýyç¼èoPñÿ×¢EmÑ©Sßýç~Çk¯=—ÏÁ‡þ½ê5ý\m(õš~‘èŠþ°L½“ìÐ~ñÃ7XJ«¦oh‡é/ÿòS3füîÿûÿ~ÇЉ4!·$$“bøn¦¿û»ÍõWKæÍ›U_ÿk_øÂ'Û쬃:jif©È3mÚ³ùœC&ŸZ'ÔF¤+òlgêƒ"{%"[ 5ýÒŠl<ýô§Ýñ6Y‘-Å1{ujú¡ûRÿ«E˜ÿ«ÅóÏ?:qâoE·7úÿã+qßÅ+‡…É?}úû¾ùÍÕYc–k§ M½é¦÷æº÷Nmí›âw@t—«Ôÿ”‘µ i—…ªI…¬éy€ ¹nБ¬´ „æ¥þ§š>åTŠ2´s5þÑG—‡m†Ï~C$Òﯘ+™äŸ‘Ò¶?´sÔ§—_Þ‘ú_§s5;mƒY?ò¹´ ¿ŠTä™6õÙ<Ï!“O­ÓîDæóéŠä2ÜÎu PÙl‘ÔôK(²©Óɓόs1+²¥;f¯NMÿ+_¹„_ÿ»¿Û]LÔÜ<>õ÷¸ÂWˆ¿ú«%“&ývêï}E?…|èГ7Ü01ùàS§ñèÑoÞ~{ë€;Mhjô¹ñ«R#·µõ_/\8÷'?Ù½ R–« ãÇ¿í¿ÿ÷õñ^Õ¤BÖô‹<@ …¿8Xÿ÷×^ÛÿfB°Ò‚²sçC ÿ"Äýg?û^˜vï^ý:œš>ePŠ2´³íðQN˜¢Ú ‘&'Ò„d’FJÛþÐÎQ³N³gOÿÖ·ÖFm _ÜyïŠ/úþõv_Ø·éŠO_\?`€¨ÈCchÚ€D*mÒ¬I‘Á$²"+²ÎŸ‡RÓOÕÝÝýÄOlܸñ (txèöÐù‡ ’ ¡mh©´ H³(2 ²"+²ÎŸ‡RÓß»wïÎ;·nÝvù% "tuèðÐí¡ó?.@% CÛÐ$RifQd@dEVd?¥¦àÀï~÷»agO<ñD'º:txèöÐù'Ož ’ ¡mh©´ H³(2 ²"+²ÎŸ‡RÓ?~üxØÍÞ½{»»»w–Ž/þŸŸÞY²BW‡Ý:ÿÌ™3TÒ‚¡1´ m ¢i)æOi¤ÙR<_-ã³Ù /2ˆ¬ÈЬÈVÔùóÀ5ý°ƒ“'O†=:tè@éXYó¾%+tuèðÐí¡ó_{í5*éÁÐÚ†6PQ‰´ó§´ Òl)ž¯–ñÙl…DVdEVd+êü¹ª\O/6UMuŽ%@€¡  ‹¬NYD¶œ¨é#@€¡  ‹,"‹È–5}0´äOdc‘EdÙÒP¶5ýîù¿EW€C@þ@6Y ²ˆl9©ÒPÔô 4¨é@iPÓ€Òà7r ÀÐ?E‘EdKCÙÖô7UM] mùÙXdu‚È"²åDM mùÙXdYD¶4¨é#@€¡  ‹,"‹È–5}0´äOdc‘EdÙÒà7r ÀÐ?E‘EdKCU%sU•ÀPr*º¦_øâ¾?'0d®Ów”5}‡@i¨ÄßÈM½÷Nÿ¸qãΜ9?–Ô××GKî¾ûî±cÇÖÖÖΛ7/,OêǪªÕ«W755ÕÔÔ\ýõ{öìY·nÝ„ ÂÃ)S¦¼øâ‹i»>þ|{{{ØrØWGGGj“V­ZÕÐÐP]];v¬¥¥¥®®.lgÖ¬Y'NœˆÖ‰Å È¿©Å  ,s/ò'€lŒÈ"²ˆìð•mMSÕÔœÇüúûéßyçñ³a~ñâÅaæÞ{ï3gΩS§.\¸°páÂE‹%õcUU[[[__ßÅ‹W®\Y__ŸúðÆoLÛõÒ¥KçÎÛßßæÌ™–––Ô&ÍŸ??.Ê_ýõÝÝÝa#¡ á%íííiÛ‰ ª©Å  ,s/ò'€lŒÈ"²ˆìð©é_:xð`SSSüì„ Ž;f9-<{öì˜1c’úñò%ÿÑüÅ‹ÓÖÔÔ¤íº¡¡!ÚË¥Ëã§6éÔ©SYw¶3zôè´íDÕTƒ0´äOdc‘EdÙ¥¦ÿs·ÞzëÖ­[ÃLøwÁ‚ñ³©¢›á ¸Í䇙3ÉË»»»§OŸ^WW—ֆ̗çßTƒ0´äOdc‘EdÙ¥¦ÿs»víºé¦›ÂLø7¾÷}ccctÿú¼úq5ýÔëô9’«¦Ú°}ûö .„ùðoÂjù7Õà mùÙXdYD¶DUúoäÆ 'Ož¼fÍš™3gÆK–/_>{öì¨ò~ðàÁø^öÉÛL~˜z?ý–––þËZ[[sëÇŽ»cÇŽ0sâĉÛn»-~vÔ¨Q}}}Ckjñ(ËÜ €ü #²ˆ,";|U•xÌÙ èkÖ¬©­­ÝµkWêšË—/onn®®®žÛL~Ïœ;wnÁ‚555cÇŽíèèȼá~¤««k„ ¡MMM«W¯ŽŸ}àBkSWο©#ëìÑWzþÚ¹—OKWZ•.¸êzzzš››K·ý½]Ý›ª¦>>êæWžù¾h\9júWÍ’%KúûûOŸ>=gΜ0_ÒÇòãGŸÚT55LOüÆì—:6¼vòUñqjúƒS›ÍÐ6µzõêúúúºººööösçΕzÏôíx®óM¿¿©jêÖ·Íì¬ýýoÿÑŸ¹l`dUâoär…Äeý0uÖþþWÇ~(á²}¹ù@6FdYDv°Ê¶¦U–ME2ylç€ëP¹W'ÈŸ²1"‹È"²WN•Ø3Rúv<÷Ø[¦o®žÕè{óM[Çý›¿¿û¿F×é'GD¼@î?YD‘š>#ãý7LÛ\ýþÍoúýÓ?ñò7ÿû?_øYž/{äOdcD‘Ed¤¦Ïøñ£OE׿§^˜?¨ˆ„gï»ï¾µk×îÞ½[‚Ü  #²ˆ¬È’•ßÈe¸z»ºþ£¸oº1íÂüAÆðì=÷ܳråÊ;vèR{äOdcD‘Y²ªÒ ÇÙ£¯¼Ô±á§‡z\SM`˜Ôô)5}€aRÓ§@Ôô†IMŸQÓ&¿‘K¨éC%{äOÙ‘EdÙ+ªlkúÉdŠ-"jú ÷ÈŸÈÆˆ,"‹ÈHMŸ¢ˆˆš>Ƚò'²1"‹È"²RÓ§("¢¦r/€ü €lŒÈ"²ˆì€Ôô)Šˆ¨éƒÜ  #²ˆ,"; ¿‘KQŒÆ"¯éWUU¥Í\õ–T‚eË–µ¶¶Fóaæþûï7Žä^ùÙ‘EdE¶ÂUé CM¿Ô[Rà=ž?~ܸq‡ŽöôôÔ×ׇ…†•LMŸrM¿ê—êêêf̘ñâ‹/f.okk;qâDü’ýû÷Ï;wÔ¨Q555Ó¦MÛºuëpljëô ~ëׯaM]ÒÒÒ²aÃC €J¦¦O §¦Í\¸paùòå“'ON[~æÌ™ ´´´D{zzšššÖ¬Y]Ó½wïÞyóæ wœ¨éüÚÚÚ¶lÙ’º¤³³3­Ê•FMŸ~M?RSS“¹üìÙ³µµµÑ|{{ûêÕ«óiÒÒ¥KÇŒ3zôèU«VEKŽ;ÖÒÒRWWö2kÖ¬øÚÿkúY—‡…+V¬¨¯¯m ­:wî\¼<챡¡¡ºº:Zr÷Ýw;6¬6oÞ¼þþþ¤›Ò’°æ¸qãΜ9?–„ÝEKµÍÐcMMMᨯ¿þú={ö¬[·n„ áá”)SRÿcD4sþüùp8aËa_©MJ=®¬Y•"n@Ö¦†öôöö¦¶³¯¯/,4”¨d~#—‘ëô—-[6eÊ”´å—^_Ó3fLj™;—Ï~ö³³fÍ:uêÔùóç—.]-¼þúë»»»/^¼ö¶··§ík°5ý¹sçž¹,ÌÄ{ ËçÏŸ¯ï½÷Þ9sæ„–„.\¸pÑ¢EI#öõ-¹óÎ;;::âgÃüâÅ‹‡°Í¶¶¶¾¾¾pà!õõõ©o¼ñÆ´]‡ ‡ÚŽ«¥¥%µI©Ç5`g&~üÇ›Tq”‘{äOdcDVdu‚ÈV¦²­é'W)¶ˆäs?ý ©©éÈ‘#ñòh&íÞ;YkÁ™ÆÿþjV/^=ztÚ¾uë›°rÜÚ°¯†††xù©S§âÕãÕΞ=;f̘äm¦Î×é÷õõÍœ9sÛ¶mñòHmmmêoäæy~Öê|ww÷ôéÓëêê¢-Ç÷ÆrM?õaÖ»]zý-Rwš¼ÍxæÖ[o~8ü»`Á‚ál3ùa®Nȵ|ÀÎLnjsssÚ½wÂC÷Þ‘{äOdcD‘Ù §¦OQD$Ïûé÷ööŽ?>º1}®òz{{{|üY¯Óollܾ}û… .]¾ÕÏ€åìFWîëôÓvÿA"Ÿm¦Íìڵ릛n 3áßøÞ÷CÛfòì×é‡ÌÕ9vfrSÛÚÚ:;;_÷&Ù´Éoäʽò'²1"‹ÈŠl…SÓ§("’ÿoäÞvÛm=ôÐ¥Üåõžžž¦¦¦°NTM~á…ÂK2·ÝOÿĉ©÷Ó;vlÔ€°<¼*ÿš~Âýôû/K»Ÿ~êjË—/Ÿ={vT%?xð`|ßùä¥ndòäÉkÖ¬™9sæ0·™ü0õ~ú---Ñqµ¶¶æêœ\9jÔ¨¾¾¾›ºqãÆÐi© ;ݰaƒ¡$÷ÈŸÈÆˆ,"+²•ÌoäR£1ÿšþÓO?=mÚ´K‰—Ìïß¿¿¥¥¥®®®ººú†nˆnM“¹µ%K–Œ5jôèÑñuý]]]&L¯jjjZ½zõðkúcÇŽ­­­]¸páùóçs­¼|ùòæææ°ßÉ“'§]œžkG©Y³fMØÅ®]»†¹Íä‡ñ̹sç,XPSS-`®{ åêÌx ´6uå¬M½páB}}}OOOô0Ì„‡qr¥=úJÏÃ_;÷ò)¹À¹+€lŒÈ"²ˆlQ©ÒÆkú%<ºªÊ|õôô477_¡/_¾<¾ÙN˜éèè0Ž ©·«; Ìǯ¹ù•g¾¯7Š„š>¢¦_N–,YÒßßúôé9sæ„yoïruhý7ÂØ Óößú·/ulxíä«úàêRÓ§@*°¦_[[;œ×f~{Fj›«W¯®¯¯¯««koo~²˜rÕûä·7×ܸù ¿÷õßú·Õ} »í?ºlà*RÓ§@*°¦å!*ëGìo÷o¾Vÿa—í\-~#—°¦o2™JeÚü†ß‹fŽ<¶sÀ•e?箲1"‹È"²#¨lkú I¥×éCÑêÛñÜc£nî|Ó/.Õß\ýþ-¿:ãùÿ°"ºNÀ¡­œ»Èƈ,"‹ÈŽ 5}Š""júPœ¢‚þæ7¾ÿçÕü7¾ÿ©ßùøñ'výó…Ÿå?´õ!€sWÙ‘EdÙ¤¦OQDDMŠÐ}*º6?õÂüÁíûî»oíÚµ»wïÖŸÎ]dcD‘Ed‡OMŸ¢ˆˆš>›Þ®î00;knL»0ßÐpî #²ˆ,"{ù\Šb4*üAQ9{ô•—:6üôP¯¡ àÜ@6FdYD¶¨Té Cá m†IMŸQøC€aRÓ§@þÀÐ`˜Ôô)…?0´&¿‘K(ü¡ €sWÙ‘EdÙa*Ûš~r™‰b‹ˆÂÚÒ)²1"‹È"²RÓ§("¢ð†6€t €lŒÈ"²ˆì€Ôô)Šˆ(ü¡  #²ˆ,"; 5}Š"" `h_…Àªâý\¶lYkkk4fî¿ÿ~o6ð½Ù‘EdE–K~#—"jú`h_…À_Öô‹­¸þüùqãÆ>|8zØÓÓS__z¿Asî #²ˆ,"›§*]@a¨éƒ¡ýºŸ_ª««›1cÆ‹/¾˜¶<-?sæÌ]wÝÕÔÔTSS3zôè–––={öd]?ÿ}žk âŸ¶‹õë×·µµ¥. Ç»aÃï7@MŸQÓCûu?¿¬b_¸paùòå“'ON[žfÖ¬Y‹/îíí½tù2ö-[¶ÜrË-Y78ˆÀb­é·µµ…L]ÒÙÙ™Vå*“š>¢¦†öë>~^_Å®©©Éº›yï®®®I“&ÕÖÖ677¯[·îÒëÿ@òöW¬XQ__^ÛÞÞ~îܹhy˜¹ýöÛG]fâåaýU«V544TWWg©)úëE¬¯¯/,ô~Ôô)5}0´_÷ñ“rþ²e˦L™’¶<ÍŒ3,XÐÝÝ«²?"5ýqãÆEM=qâÄ¢E‹òÙr¼ÎܹsÏ\f–.]-_²dÉœ9sâåáa¼þüùóûûû³î"þ GªÚÚZï7ÀoäR jú`h¿îã'ESSÓ‘#G2—§^º~úôéÏ|æ3“&Mª««kll\¼xqX’¶ÁAfÔôCKV­Z•v|ž5ýø>ÜÐÐ͇™ø§nÃL}}}¼þ©S§ríBM*sWÙ‘EdÙ<•mM?¹ÌD±EDM*mhÇUì¾¾¾™3gnÛ¶-my‚ÇßqÇ3fÌȺÁA|fÔô÷íÛ×ÚÚ:f̘k¯½¶««+ÿ-çy+¡\ËÓ677§ý]!TÚÐN­b÷ööŽ?>º×|þ¥ù´‹ÙG¤¦ÛµkW|­ý0¯ÓO]žz~BãÛÚÚ:;;_ד›6ù\ð½Ù‘EdE–KjúIDÔô¡Ò†vZû¶Ûn{衇.%ÞOëÖ­QÝÿÔ©SK–,™>}z“w—¶0žioon•³k×®±cÇF GÕ××7À§éåûé÷_–z?ý0Ýg?,Ÿ3gNêýôS_ž¶‹7†W¥®ÐÒÒ²aÃï7ð½Ù‘EdE5}Š""júPiC;­¨ýôÓOO›6íRîûéïÙ³§µµµ®®®ººº¡¡áöÛo?qâD“w—¶0žéììœ8qbMMͤI“â{ï<ðÀµµµn¿££cìØ±aÍ… ž?>Z~îܹE‹Õ^vÇwÄËÓ¶–¶‹ .Ô××÷ôôDÃLx¿(fg¾Òóð×ν|ʹ+€J"‹È"²WˆßÈ¥(F£š>Ú¥ýiZ5Ÿ§Ë—/o¶f:::¼Ù Tôvu‡ì÷ø57¿òÌ÷»¨$ ²ˆ,";âªt…¡ð†v9šVù<þ?Z»-$À0}ýís_êØðÚÉWõ 0RÔ (…?0´Ë@m6Ñro Õ±­Ïl®~ÿãuøæ¤y½ezwÛÔeû¹¨éS jú`hT”¨¬]°ÿä;[·ýúG\¶ Ÿš>¢ð•9´M&“ÉO¿ez4sä±®ì#ÈÊoäR,…?5}0´ÊIߎçýÁ-oýà/Êô¿ò{ºù;¿÷Ü˧òÉŸ:@%‘EdE–¬Ê¶¦ï‹PiEDá m€rô«»|aþ¦u½ë?¼¡ëŸ/ü,ÿü©TYDVdÉJMŸ¢ˆˆÂÚeãÇ>õ‹›í¤\˜?Øüyß}÷­]»v÷îÝú@%‘EdE–TjúEDþÀÐ(½]Ý!ûuÖ¼?íÂ|ù@%‘EdÙ¡¦OQDÄW0´ÊÀÙ£¯¼Ô±á§‡zåO€ò;¹EdYD¶Hø\Šb4úâ †6€ü)\]* "‹È"²%¡Jà‹+`hÈŸ@IPÓÇWÀÐ?€Ò ¦/®ÀUÚ¯|õð—¿þÕ“€üéÔȇš>¾¸WahŸÜýwO4~$¬ÐÛÕ­ùÓ©'¿‘‹/®@á†ök'_ÝÿÀ£]ïþ÷á©0\ý˜>äO§F* ˆ,"‹Èæ¯lkúÉ_“ðÅ(ðÐ>¹ûïžkÿÜ–13žúoë¦7ü^ÏÚm:?¨$ ²ˆ,";(júøâ \Á¡ý¹%Ÿ~xîí5Ï~â7fÿ?ïÿãÇÇÌøùúo˜¦ ÈŸÉÏÞwß}k׮ݽ{·¾PI@dY‘%•š>ÅòÅUMÊrhGSç¸Åó&“ÉdÊg çEÏ?ÿ¼Ï•D‘YR©éS,…?5}(Ë¡½ì?üé#ïûw›ßôûq•êkÿòÿõƒÇ·»òàçî»ï¾pôÙwýë{~), 'EëׯçEû÷ï×E* ˆ,"+²¤ò¹ÅhTÓ‡²Ú=o{òÚÖM¿òó²þ–_¡¬Y»vmÈ“ŸŸûÇ+),éìì ™óùçŸ?vì˜.PI@dY‘%U•. 0ÔôÁÐ~zøåîÿ™ÇÞüèšý}q»>*ÜîÝ»w¼^XòüóÏïß¿ÿرcgΜÑE@*5} DM íT=oÛrù÷r{»ºu#@žÔô)5}0´3½vòÕ«:Ï}EOäCMŸQÓC€aò¹ˆÂÚ8wYD‘¦²­é'—™(¶ˆ(ü¡  #²ˆ,"; 5}Š"" `hH§Èƈ,"‹ÈHMŸ¢ˆˆÂÚÒ)²1"‹È"²RÓ§("¢ð†6€t €lŒÈ"²ˆì€üF.E1þÀШdÎ]dcD‘EdóT¥ ( …?0´&5} Dá í¼>–ª ÷Á´k×®ÆÆÆä=.[¶¬µµ5š3÷ß¿ W‘š>¢¦†v^KW¾¦ïbÚ´iÏ>ûlšçÏŸ7nÜáÇ£‡===õõõa¡¸W‹š>¢¦†v^K¼N¿¶¶6y…õë×·µµ¥.iiiÙ°aƒ¸W‹ßÈ¥@ÔôÁÐÎëc©€5ý÷ÕÖÖ¶eË–Ô%iU~箲1"‹È"²…T¶5ýä2Å5}¨´¡]UUµbÅŠúúúÚÚÚööösçÎÅËÓfÒž?>¬^^ÛÑÑ‘«.ßßß?nܸ3gΤ. / K¢—T¥»ºº&Mš6ÛÜܼnݺè%MMM½½½©›íëë Åpî #²ˆ,"{µ¨éSQÓ‡JÚUUUsçÎ=sY˜Yºti¼àÜ@6FdYD¶Ø¨éSQÓ‡JÚiµø¸z>`M?×ò¬n½õÖ­[·†™ðï‚ 6µoß¾ÖÖÖ1cÆ\{íµ]]]ÑÂæææ´*xèÞ;€sWÙ‘EdÙ«ÈoäR£QM*mhx~uuõÅ‹£ùþþþ¬×é‡-$×ôwíÚuÓM7…™ðï‹/¾˜¶‹¬¯ /‰ÓÖÖÖÙÙùº#Ú´ÉoäÎ]dcD‘Ed¯¢*]@a¨éƒ¡ýºŸË÷Óï¿,ëýôo¸á†ð‹/ž8qbÞ¼y©÷Óoii‰^ØÚÚš\Ó&Ož¼fÍš™3g¦î:m&hoo?|øð¥Ë5ý±cÇF 7nÜÚ–ºµ°ë 6ˆ;pµ¨éS jú`h¿î㧪ª££cìØ±µµµ .<þ|¼<šÙ·oß”)Sª««›šš|ðÁxù¹sç,XPSS^¶õ–÷©Ö¬Yv±k×®Ô]§Í'N [›4iR|ï .Ô××÷ôôDÃLx7 ðÔô)5}0´_÷ñS5@===ÍÍÍWô—/_ßl'Ìttt:p©éS jú`h¿îãg5ý%K–ô÷÷Ÿ>}zΜ9a^€Êá7r)5}0´SÕÖÖy§«W¯®¯¯¯««koo?wî\´µL¢8w@6FdE‘-?e[ÓO.3QlQÓC@:@6FdYDv@júEDþÀÐNYD‘š>E…?(ס}u'œ» #²"‹È–5}Š""jú`hû ¤SdcD‘Edä7r)ŠÑ¨¦†¶“ ’9wYD‘ÍS•. 0ÔôÁÐ.ª¶”"5} DM í¢j@)RÓ§@ÔôÁÐ.ª¶”"5} DM í¢j@)ò¹ˆš>ÚEÕ6ç®ÈÆ"‹È"²¥¨lkúJ9¥5}¨Ì¡ýÚÉWùá_€tªdcD‘Edó¡¦OQDDM*mh÷~}ÏWÇþAX¡·«Û€” #²ˆ,"›'5}Š""júP!Cûµ“¯~ÿŽŽ-cf„§Âô£/n÷A eÈÆˆ,"‹ÈæOMŸ¢ˆˆš>”ýÐîýúž§~§}Sõ´°°óÍxì-8¾}·) @6FdYDvPüF.E1Õô¡\‡öç–|ú‹øØæQÓ7ýÊÔèÚüǯ¹ùñÑ·\邾“À¹+²1"+²ˆlYªÒ†š>TæÐ¾º“eFMŸQÓ‡ÊÚŸ[òé‡çÞþØoÜúµ5ëÉkÿ]Tjß\ýþÃ_ù¦Þ,5} DM*|h¿òÌ÷ŸkÿÜco™Þõî¶-oý ²>À¨éS jú`h¯|uÿ~ã—ìXµYäÏoäR jú`h§zå™ï?Ñø‘°BoW·npî #²ˆ,"›§²­éûiÄÒŠˆš>TæÐ~íä«Vuž=úŠž¤S #²ˆ,"›5}Š""jú`hH§Èƈ,"‹ÈHMŸ¢ˆˆÂÚÒ)²1"‹È"²RÓ§("¢ð†6€t €lŒÈ"²ˆì€üF.E1ó¹éöá/#ü«'¡œ†6Î]dcD‘Ed¥JPC.üõ~}ÏWÇþAX¡·«[7BÙ m†@MŸláﵓ¯~ÿŽŽ-cf„§Âô£/nׇPC€áPÓ§@ò/üõ~}ÏS¿Ó¾©zZXØùæ<ö–ß¾[B‰íûî»oíÚµ»wÅ#@MŸ°ð÷¹%Ÿþâ>¶yÔôM¿25º6ÿñkn~|ô- úPÒC{åÊ•Ï?ÿ¼¾>¿‘K Xø‹¦Ío™Ï›L¦2˜Ö¯_¿cÇŽýû÷KƒÎ]dcD‘Ed‡¯lkúÉdŠ-"ÑuúϽý±ß¸õkÿjÖ“×þ»_”ø«ßø+ßÔ{PüÖ®]»råÊ?ÿµŸ_˜ K:;;wìØñüóÏ;vL8wYD‘>5}Š""©÷Óå™ï?×þ¹ÇÞ2½ëÝm[ÞúAe}( »wïãweÍûvüRXòüóÏïß¿ÿرcgΜÑEÎ]dcD‘Ed‡OMŸ¢ˆHjM?ZòÚÉW÷?ðè7~yÁþU›õ!Ƚò'²1"‹ÈŠl…SÓ§("’YÓ½òÌ÷ŸhüHX¡·«[7‚Ü  #²ˆ¬ÈV2¿‘KQŒÆ„š~䵓¯XÕyöè+zä^ùÙ‘EdE¶bUé c˜5}Ôô)5}€aRÓ§@Ôô†IMŸQÓ&¿‘K¨éC%{äOÙ‘EdÙ+ªlkúÉdŠ-"jú ÷ÈŸÈÆˆ,"‹ÈHMŸ¢ˆˆš>Ƚò'²1"‹È"²RÓ§("¢¦r/€ü €lŒÈ"²ˆì€Ôô‹ÔwÜñ›åå]U£oÎ-<û¶·½mܸq ¿ )&Ož|3¥ãª~]'ÈŸ²1"‹È"²Ã±råÊJ¬é—úo)üæoþfæ–[n©Äš~©‹kúK¡REC ¹¹ù¨¿ú«¿ª¦_Ú5ý¥K—ž†J•õï¹ç €JpóÍ7«é—ª2¨éGY—îö+Jqv¦š>%ªé¿÷½ïíîîÞ»wïŽ?~æÌ™ÔuÔô‹TÉÕô3‹ÂUÓ/­?0>Xjú0 ¨¦ýõ×?ñÄ;wîüîw¿{àÀ“'O¦®ã7r‹TÔô+ªŒ^ê5}÷ÞAî?E‘Ed¯º¨¦?qâÄ7nݺuçÎ{÷î=~üxê:e[ÓßT5µ¤Û?„š~T¨]½zõoÿöo×ÖÖŽ?þ¾ûîK{¶£££¾¾¾ºº:ZøŸþÓjll¬©© +‡ùÔ­}þóŸë‡5ßúÖ·.X°àG?úQüTØÅ{ÞóžèU«V­Š7KÝãÑ£Gßüæ7‡í8p ZfÂððÇ?þqxùö·¿=4¸¡¡áž{îI8À°fhmX3`Øo>ÛÏzÔ™–g·¤•Âã‡Y?áÐlUÖNÎõ„ÌÄ„`Å/LîÐ¶æææ°Çë®»®««ëÊÕôÿäOþDM_î?YDVd+GTÓ¿îºëyä‘7>ñÄÝÝ݇J]GM¿H ¹¦?gΜ£G?~ü£ýhx—õ£g.\xâĉ¸n–|ò“Ÿ óŸúÔ§Â|jõösŸûÜóÏ?f¾ô¥/…§>þñ§–‰ÿðÿðÇ?þñË/¿|çwx;—°Ó0³lÙ²¸~⟈öæ?ö±:uêž{îI]-M´ßèè‚0“Ïö3:k§åÙ-¹júY?áÐlUr'§¾0¹s1ùÞ;öÀ‚ B¶nÝæ›››¯PA?¤ªhwjúr/€ü €lŒÈ"²"[!â{ï<òÈ#_úÒ—:;;wîÜyàÀÔuÔô‹Ôkú/½ôRôpÿþýááÛßþöÔg?¯?~üø°$ºv;º¤=,ɵåk®¹&šonn_|ñÅüoÑþÜsÏ…™w½ë]Ñòw¿ûÝáaX曚šâ6Ÿ8q"¡Fí7>º0“Ïö3:k³óì–0_[[›gM?áÐlUr'§¾0ÿL brM?¹‚è?XDsý?ƒáôß÷¾÷E‡“öwHä^ùÙ‘EdE¶\©é—°!×ôÓ–ÔÔÔäù³¨©+?ùä“7ÞxctO›ä›´äù³«Ó§Oóßþö·wïÞff̘‘ºNª\5âál8¿›Ö‡ù×ôm­Â^Ä„gÞEWè¾üqAÿºë®SЗ{äOdcD‘ÙÊQÑ5ý üÜ|®ÓO]¿¡¡!¾æ:írìúúúððÉ'Ÿ ó§NJ}mt ù /¼0¨âøæÍ›Ãü¢ËÂÌc=–z™y|Ýw‚„ëô¶?„êyB·¤VÌ=šÏuúYmÀVåßÉ {Ibr°’{àJ×ô:4uêÔ°ÙÆÆF}¹@þ@6FdY‘­(]Ó/uC®éô£=zôèË/¿üGôGáá_üÅ_ä*¿þéŸþiXò©O}*Ìò“ŸL½múÛÞö¶ðð[ßúÖ©S§n¿ýöÔ×F·zÿÈG>ò£ý(õVïoyË[Âòýû÷ç*ø¾ýío¯¿,þ3CšÝ¾¯¯/ìkÛ¶mþð‡³]GGGttÇûM½Ÿ~Âö‡PÓOè–¨Ô¾uëÖÐÔü㩯Í<ü„C°U¹:9ó… {Ibr°zàJ×ôS¯Ð饗ä*Šš~ rMõêÕï|ç;«««â‚~®òë§>õ©è¢ìñãǧþ@îöíÛ¯»îº°‘ÆÆÆ+V¤½öÁ|Ï{Þž­¯¯_µjU\s¿æšk.~º6øüç?ŸÚ†°…w¿ûÝakµµµþð‡·mÛ–ëÃá„Ö†5Æ6ä³ý¡Ýå&W·tuuEÝÒÜÜú9õµ™‡Ÿphù”³vrÖæÚKB V®¸¢5ýC‡½ë]ï |ï{ß{òäII€J£¦_ÂFä~úP*~øÃ¾ç=ï‰~÷èÑ£2HM¿„©éSQýè }÷Р’ùÜ6„š~íe Ä”–üàï|ç;£{è™6mÚ³Ï>[€†=øàƒcÆŒ©®®.ª÷O¾-¯V“BhB€V¯^º°§§§¾¾>ŒŸG@Á¨é—|ðÔô/].óEêêêf̘ñâ‹/¦-EËÏœ9s×]w555ÕÔÔŒ=º¥¥eÏž=Y×/XqÀ]Åêjڮׯ_ßÖÖ–ùlY^§_[[[˜`…÷aü¾½ŠpE÷›çƳ®6²o³´<ýôÓ!o„X‡(,X°àĉYWÛ¿ÿ¨Q£Ò6²Ç† |ã7rK>xjú—RJo.\X¾|ùäɓӖ§™5kÖâÅ‹{{{/]¾ê|Ë–-iÝXøŠj ÕôÛÚÚBe>[–5ýäúòB‘ÜÞ§05ýäÜ[øšþ­·ÞºcÇŽ‹/ž;wî®»îš1cF®}e. I)õO\Î]dcD‘Ed¯´Š®é'WK%xjú—2 m555Y—§®pñâÅ„}%× ó©ó¦– W¬XQ___[[ÛÞÞ~îܹhùùóçÃð0<ÕÑÑ¯ìØ±–––ºººÐÈY³fE— gýwß}÷رcÃæÍ›×ßߟÜà¬m3·ß~û¨ËÂL¼<¬¿jÕª†††êêêÌ]755EÉóHSWX³fMôß#¦L™²oß¾ä®ò ólU|Œ¹º"íØ³–•³Fauç´»î¤îkõêÕѱ‡L½gÏžuëÖM˜0!êŠÔÿ’2`g¦ožo³á„ óM‹FzÂÛ/k?$·$×Öººº&MšZØÜܺ.×hŠ…Ì@2—ôõõ…Æ8—œ»Èƈ,"‹ÈŒš~ÉOMÿÒë¯Ó_¶lÙ”)SÒ–§™1cÆ‚ º»»sUöG¶¦?wîÜ3—…™¥K—FËÃLêòxý0£†…c ë´··gÝþ½÷Þ;gΜS§N…Õ.\¸hÑ¢ägmÃ’%KÂFâåáa¼þüùósU¨ãŠgžGšºÂ¼yóB›ÃÑ­X±bêÔ©É]‘ç C;à [ZZrÕ‚sµ*õº"ë6“£ºf>7tÚ±cGccc®^mkkëëë ǾråÊúúúÔ‡7ÞxcÚú¹ú$íxó|› ' c$éCëó\-ɵµqãÆ…î 3'NœÈ P¦®®®Ì^?>ÚZª¬7hpî #²ˆ,"{…¨é—|ðÔô/½¾rÚÔÔtäÈ‘Ì婵ÔÓ§Oæ3Ÿ™4iR]]]ccãâŋÒ´ ¶ñ Åи=‡nhhˆæÇŸº<×%ãGκýÐìøågÏž3fLrÛ²¶!ÌÄ?ufêëëãõO:•ëÐÒjúî%µ+â«§S¯†ÎÕ¾0ìâØ±cÑ|˜Éõ·–\­J=Æ„®ÈÓ!D!!:µµµ7nÌõl\ˆÇžö0óŠò\}’v¼y¾ÍF$™[‹Fz®>°»rµ$ëÖBBXµjUêÿ,Ià/¼ð„ âkÿ3mذ!ì1íåjú€sWÙ‘EdÙBRÓ/ùà©é_J©ÐõõõÍœ9sÛ¶miË>|øŽ;îˆï ÿ sµ!íaž÷ŠvwwOŸ>½®®.ú#D|K–ÌõS¥Ý¹%¹m¹Ú0`Û"ÍÍÍiÒ<·–ge­éê…C8ö|VËÚ°ü£`ãÆ×^{í ÞWÃì«üßfÃAæ³ÑHÏóoEÃhûöíkmm3fLèá®®®„vîÚµ+¼·CÏ$ì=l$í¯/a,¸÷àÜ@6FdYD¶üFnÉOMÿÒë+t½½½ãÇç®sÝO&ÕÕÕñ|úûû‡|~ccãöíÛ/\¸péò­„r•,ÃjÑ=Ðó‘p~êò¬§_Êö¹!ùïeÀúo>×ég}aêEâa ƒ½N?uµ|º"ëuúÉQÈóÞ;©×›'¼·/åQÑÎÕ'™ïŸ|Þf#‚ÌWE¹7WŸøfÎÕ’ä­íÚµ+Wôƒ­[·655íÝ»7ÿaþ‹¼´i“ßÈœ»Èƈ,"‹ÈRE×ôË#xjú—2*t·ÝvÛC=t)ñ~ú[·nêþ§NZ²dÉôéÓ6˜¼»È 7ܰråÊ‹/ž8qbÞ¼yi÷sï¿,ó~úÑò9sæÄë;6¾ýw8xù¨Q£úúúâÝ-_¾|öìÙQ9õàÁƒñýÐs58¡ gΜ‰Úõ†æ™»Þ¸qcxUþ{°.œ«+òyaKKKôÂÖÖÖ„ûé'´*ÿ®ÈlÏ ¢,×[n°5ý\}’öÂ<ßfà A¦´çêóänI~en-%º'Ï®]»ÂQgmÉêÕ«_zé¥!„)þ† |£¦_òÁSÓ¿”Qh{úé§§M›v)÷ýô÷ìÙÓÚÚZWWW]]ÝÐÐpûí·§]m=„šþ¾}û¦L™6ØÔÔôàƒ¦C;::ÆŽ[[[»páÂóçÏGËÃLxXSSž +ÄëwuuM˜0!ÚÎêÕ«ãå<ð@ØBê®—/_ÞÜÜÖœ]‘µ=ùG!Y®ûö ¶¦Ÿ«OÒ^˜çÛl8!È”¶ñ\}ž<ÖZ’uk!('N ›4iR|ïÌÃLþ¹ÂFA Y[p…¨é—|ðÔô‹ÜnãSümX¾|y±Ýo¤§§§¹¹¹8û? ÉwrÁ>)pÊFPÚ-}Â(èèèða ÖÙ£¯ô<üµs/ŸÒÀ¨é—|ðÔô‹\YÖô‹Ç’%KúûûOŸ>ÿÍ[ŠSHÁ µµµè“‡ <„Є­[·ÎG0"z»ºÃ‰Íco¾éå¿}Noƒâ7rK>xjúEnDª´ùï+SÛP`«W¯®¯¯¯««koo~ á*ö©ôÉ•Þ]®÷aÙä^€ñãGŸ ç6aÚò«3ö.YõÚÉWËþÜ@%‘EdÙQÑ5ýä r©OM{JQߎçusç§…ĸ¹zÚŽßû?’/Û—?œÍ"²ˆ,"{IM¿ ‚§¦Ƚ%êeý7Ý]³¿¹úý —íËŸÎfYD‘½¤¦_Á«¨š¾Éd2™L&SyO›ßð{ÑÌ‘Çv¸²ïr* ˆ,"+²jújújúE]Ów>Ƚå¤÷Éoo®ùÅEú[Çý›¯ýËYÛëßþÏ¿x$ºNÀS#àl‘EdE¶ùÜ’žš> ÷”¢¨ ¿ù Ó¶7Ïyüš[¾Ý²´oÇsÿ|ágùŸéCg³ˆ,"+²¨¢kúå<5}€’shý7¢ËóS/Ìì©ÑO<ÑÝÝ}èÐ!ý •CM¿äƒ§¦PZz»ºÃ‰Íco¾)íÂ|§FÀ€ÔôK>xjú%äìÑW^êØðÓC½N€!PÓ/ùà©é”%§F@&¿‘[òÁSÓä^€²äÔÀÙ,"‹È"²™*º¦Ÿü5©T‚§¦Ƚ˜08›EdY‘­Ljú%<5}@î¨À„éÔÀÙ,"‹ÈŠleRÓ/ùà©ér/@&L§FÎfYDVd+“š~ÉOM{*0a:5p6‹È"²"[™üFnÉOM{*ð‹ŠS#g³ˆ,"+²•©¢kúå<5}€²T §FË–-kmmæwíÚÕØØÎo ߌІûï¿¿ÌÞ?#Ø“©aÒóWš~ÉOM ,•ý©ÑùóçÇwøðáèá´iÓž}öÙ«Ò’žžžúúúОrzÿŒTM?-LzàªSÓ/ùà©é”¥!ŸUýR]]ÝŒ3^|ñÅÌåmmm'Nœˆ_²ÿþ¹sçŽ5ª¦¦fÚ´i[·n½G”Ve^¿~}hFü°¶¶¶`}›YïniiÙ°aÃUl@áw‘gÒÂTê=PÔôK>xjúei85ýhæÂ… Ë—/ŸMZºté˜1cF½jÕªhɱcÇZZZêêêÂ^fÍš_ûßÕÕ5iÒ¤°‹æææuëÖ]Jù_q3šššz{{ã¶¥>›k³áÙ5kÖ„†åS¦LÙ·o_´üܹs·ß~û¨ËÂLx˜µ+¢‡™- úúúÂf³uÖÚwX¸bÅŠúúúpŒ¡ã=&´$tZCCCuuuÖ ¸ßøa®]Ÿ?>< ÃSñúY;3kî¾ûî±cdž-Ì›7¯¿¿?3LWâÀzœÍ"²ˆ,"›UE×ô“¿&•JðÔô¹ fž×é/[¶lÊ”)iË/½¾¦?f̘3gÎ ØžÏ~ö³³fÍ:uêÔùóç—.]- _$º»»/^¼ö¶··GËÇ·cÇŽ0sâĉE‹e6àRÊ2ŸÍµÙ°Î¼yóBÂS+V¬˜:õ]´dÉ’9s朹lîܹáaÖ=¦–Å30×ÍrÕôÃŽâ=Æ’Ð’ùóçÇ…ò|®‘Oh|Ö]‡™Ôåñú ™ºý{ï½7´<ômXmáÂ…qÔ2Ã4â^ÈÛ.³YDVdÙ2 ¦_òÁSÓä^€ L˜ùÜO?hjj:räH¼<šI»÷NZÑ6—ñãÇ'ÿPêÅ‹G͇ý®Zµ*õúîKƒ©éçÚlX'¾<,·ÐÐз-ÌÔ××gÝæÐjúY…-Äö0`KN:5àÁ&tHjã³î:(uyÖ]¤ufêSñËÏž=;f̘\añWÓÇÙ,"‹È"²ƒ¢¦_òÁSÓä^€ L˜ù\§ß××7sæÌmÛ¶ÅË#µµµ©¿‘›çuúY«±ÝÝÝÓ§O¯««‹¶\]]-ß·o_kkkØòµ×^ÛÕÕ•u ÍÍÍi7up³yÖè³ÞnèRbM?´dPw€És¶$ÿ] ù`óïÌTñj a‘lσ³YD‘EdÕôK>xjú€Ü P 3Ïûé÷ööŽ?>º¶=W¹½½=¾?~‚¬×é766nß¾ýÂ… —.ßê's»víŠ/åÎüÜpY›k³¹ Äa©Ç‰WWW_¼x1šïïïO¨éoÚ´iP¿Ôšp¹zÖ– ¡¦ŸÐøA]§Ÿgg†Õâ?ó$‡id|°=ÎfYD‘õ¹%<5}@î¨À/*ùÿFîm·ÝöÐC]Ê]Gîééijj ëDeß^x!¼$skÑýôOœ8‘z?ý±cÇÆ÷ͯŠWnooþ°k×®°N´pÔ¨Q}}}ñ–7nÜ8wîܬÍεÙ\âøVòýýýsæÌ‰oæ~à 7¬\¹òâÅ‹a;óæÍ‹×OkIÐÒÒ²aƬý“p?ýþ˲ÞÔ>­%iÉl@¦\OÞu´<ì:^?Wg¦µaùòå³gÏ>vìX˜?xð`|ÛýÌ0ì'ô<8›EdYD6«Š®é—GðÔôàÿcï^ ¤ªï|Ñ£m§mÛ¶%ÁCH&QTÅ5šÑ _<&*†p‰'Db ¢oÆäd<Â"..—å1<¤"ƒ¶Dœq¢ÞtNúÌØÇG&J‚M”b­aõâ°€åñzÿ±&{*õêê¦ijïýù¬½XÕ»«öëWõïßþºÝ‰4P™þOúÓ &¼[öÚðW_}uòäÉõõõ555çœsÎúõë‹.mÞ¼y Ñuý7n<ñÄëZZZ–-[=9lÛ)§œR[[{úé§G÷Þ¹ÿþûëêêr¿Â·¹¹yëÖ­…+*µØR™þ¾}ûæÌ™S÷žÙ³g8p ;ÿ…^8묳²Ëyà¢ççmI؆°%Ñ«ÊÏhæ¢E‹†–sóÍ7G¯-µ%y ÉÛ€¢Jm|©U‡áÇpÌï¢ç—:˜…Û°`Á‚Ñ£G‡gŽ?>º6¿°L¸ãå<EÉôc_<™>@"¥¡5Z°`A5Üw%lâE‹úô’Jnžsˆ þªsË4°kïÇ‘’dï¿ßúýÇöýÛ›EŸÈôc_<™>@"iªYª2ýêY;<;6¶‡ÎöÇõþ~ó¿8’éǾx2}€DÒU³ººº\T¡ÁYõáÝq€¬ß=òO¡¹ Óúa—¿ôÍïïïÞ혔ç;rc_<™>`ìH$­€n•EeSbçÓ¿üñ1­9ò¼55篩=ó§g—¿lßwä¦7Ó/š—âÉôc/@ L­€n•Ee“äßcýš Ùkö\÷É2—í§¼²2ýØO¦{R8`fOuL&“Éd2™L¦ÄO¯ÿxS¯Ï‘éËôeú2}ÀØ  5@7«²¨,ƒdçÓ¿\û¾ ²ýÚº þáýÕöá«þõÛ+²×é÷Ú§êXÉôc_<™>`ìHဩ5ÐÍ¢²¨lbDþúa—ÿø˜‹ž»jn˜óÿ½ý¿+ïSu¸|Gnì‹'ÓŒ½)<ÕèfQYT6~÷È?e/ÏϽ0¿¯½q[[[{{{WWWŽXª3ýdO¦HZ#oÇÆö÷¾÷‚¼ óõÆeÈôc_<™>@"iH¶½oüþ׋Vý¯®zã>‘éǾx2}€DÒ€Þ¸L?öÅ“é8o½±L?â;r«ºx2}ÀØ à¼Ý,*‹Êêeú‘!é|+Ä¥x2}ÀØ à¼Ý,*‹Êêeú™~UO¦{œ· ›EeQY½±L?"Ó¯êâÉôc/€ót³¨,*«7–éGdúU]<™>`ìpÞ€n•EeõÆ2ýˆïÈ­êâÉôc/€ót³¨,*«7–éG†xÓTsñdúÎ[@o,ÓÈô«ºx2}ç- 7–éGdúU]<™>€óÐËô#2ýª.žLÀy èeúß‘[ÕÅ“éÆ^ç-èfQYTVo,Ó Iç[!.Å“éÆ^ç-èfQYTVo,ÓÈô«ºx2}ÀØ à¼Ý,*‹Ê&ɾßÿáçSÿvÿ®?ô©7þÕ/þ9÷UI%Ó}ñdú€± …¦Ö@7‹Ê¢² öó©Ê·ñ´ë³}%½ñÓëÚÿؤðøçsO²ŽL?öÅ“éÆ^€˜Z#Ý,*‹Ê&Øþ]ØxÚõQ¬ßko¼æû+}ÕŸ?î×é¿ë;r«¼x2}ÀØ ÂSP­€n•Ee“-7Öï=Óÿð•áßÇOšœø@ÿÝ”gúÉ(žL ‘´F¤\뇩TX¿ï÷È>áÑ1Wÿëÿç4™~ì‹'ÓH$­äÝ„'ï·û~ÿ§ÐÿÃW>½îñ”ôÆ2ýØO¦HZ#x÷½X?{%~^¬ÿ~èZ‘žÞX¦ûâÉôIkQ÷›wµ~èowCÚzcß‘ûâÉôc/@RÏ[´FºYT•%ÛýæÞ„§ç__‹ýìUü2ý´dúåO“âR<™>`ìHဩ5ÐÍ¢²¨lÚJÅú?®ûdègÛÖÖÖÞÞÞÕÕ•†£!Ó}ñdú€± …¦Ö@7‹Ê¢²©*eù)4Æ™L& GC¦ûâÉôc/@ L­€n•EeÓfßïÿ°¶å¯CY9â¼ðïš_¹ö¡mmm¡+îèèqwwwŽƒL?öÅ“éÆ^€˜Z#Ý,*‹Ê¦Jô¥¸Ž¹záñç>:úª?>}Õÿûä?uvv†®8“Éôôô¤áPøŽÜØO¦{Rx ª5ÐÍ¢²¨lzDþã'Mþ×ÿþÏ?¹}~ø÷ñMúãôOš¼íÅíîîîééÙ¿ŽFª3ýdO¦HZ#x7'о7+úÊÜðoîüÄ“éǾx2}€DÒ@©@?+±¾L?öÅ“é$’Ö€”+èg¥0Ö—éǾx2}€DÒf•úYi‹õ}Gnì‹'ÓŒ½‰¤5ÐÍ¢²¨lj•ô +›ªX?Õ™~ùÓ¤¸O¦{R8`jt³¨,*›`?Ÿú·e®Ð/ZÙ(ÖÿùßÜ“ìƒ#Ó}ñdú€± …¦Ö@7‹Ê¢² ¶×~þ7÷”ºâ¾TeË¿*1dú±/žL0ö¤pÀÔèfQYTVeÓI¦ûâÉôc/@ L­€n•EeU6|Gnì‹'ÓŒ½)@²ÏÄ´Fô¯“ì5Ñ–.½¼¥å¸ÚÚ#O=uØ3ÏL{è¡+ÇŒ9>üxæ™|ñÅ/æ¥Û{÷Þ9}úéuu5ÍÍÇ,\xIîr–,¹løð†šš#ÂÛ¶Ý:iÒÉõõG…åLœxâŽs²Ï‰ä-6ìØ1'šÙ§LÕªU½Æú2ý“é$ûLLk@ÿ:ÉJn/ñÜ•÷cmí‘yqùðá Û¶Ýš}ä†ò;w~¹è*Ârß×k¦]üž»ÞR««$Ó¯ýíoge‡ÍÍÇôz4FŒ8öµ×þã%¹{]tQ#G6FÏïéùjSS]öqKËqK–\öúë³û}gžåË—·¶¶>ùä“]]]===…õ•éǘL ÙgbZ#ú×Iöõ–ñe2÷ Ÿž{î¦ /üp}ýQÙ›ádoÈSùË{ß}‰þ;A?–PjQCþ\´›Ï??sÊ”±MMu'ôþ ¦ö#ÓÏé_~ùåîîîÂúúŽÜ“éÆ^€dŸ‰it³¨,*Û¿NrÀ3ýÜ ç_{mV©Ô{äÈÆÇ›²oß¼ð8üÛïì¾Ôê*¼N?÷¢ûƒ¼N¿è¢Ânf¿* Ô´yó´ðÚþÝ{gùòåÙ;ðtttd2™Âú¦:Ó/šTýdú€± µ¦Ö@7‹Ê¢²ev|À3ýyó&LštòîÝw„iÊ”±¥Bù¡CŽn(?mÚiÑoj·o¿­òL¿ÔêŠNy nŽ^{õÕ‹n‚_fŠnߟ}IÞ·.jþü‹?ûÙfÿ«Ã+¯|)úÚ€ð {¯žÍ›§…CÑLõêÕË—/øá‡×­[÷Ì3ÏtuuÖW¦c2}ÀØ ÚSk ›EeQÙ2;>à™þž=sgÌ8£¶öÈ¡C^¸ð’Âîg§ ¦Žs|MÍ--Ç-]zyôÛÅ‹?]WWSù-}J­®è”·ððÚÙ³Ï sÂ4kÖÙ{÷ÞÙëÑÏ™9s|´ºJ5þÅ£F5…==ãŒæG¹&;3<;öa9ãÆÐ¿{ï¬Y³¦LRŸ%Ó1™>`ìH퀩5ÐÍ¢²¨l™ïßw´V8mÙr˨QM‡t‡qu‡q’é'ÿS-ÓŒ½©0µFºYT•-³ã‡"qž;÷¼Ý»ïصëö oh¯ÕÉôãÁwä:qŒ½Z#t³¨¬Ê&¯“<‰óÒ¥—77S_Ôôé§ïÙ3÷PÜ…«ËÞ'o’é§+Ó;™>@²ÏÄ´Fô¯“LÃUí®Ó—éÇL ÙgbZ#ú×IÆ=ÝÎûîÜ~?G¦Ou‘é$ûLLk@ÿ:I™¾LŸj$ÓHö™˜Ö€þu’ƒ“ªÞ¼~@¶°ªþÀL¿w¾#׉+`ìР›EeU6y¤L_¦ŸÆÓ¤ê'ÓŒ½©0µFºYT•-³ãEÃë¼¢¥å¸ÚÚ#Ï<óƒÏ??3;Ïž¹·ÜrVCCm˜ÂƒðcöÉ‘¢‹Zºôòì¢N=uØ3ÏL{è¡+ÇŒ9>»ä_üböiÛ¶Ý:iÒÉõõG…ù'ž¸cÇœRKž7oBSS]cãû–,¹¬üÖö{´aÃÔqãN¨««5ª)lp¯»)Ó÷©x2}ÀØ ÚSk ›EeQÙ2;^4¿á†Swîüòw-ZtéÇ?þÙùsçžwõÕ{ë­¯„éÚkO ?öz{øÕç>wÊöí·…EÝÿ_57“ûãùç(û´SOöÜs7…™ûöÍ›7oÂôé§]ò׿~Áĉ'† Û»÷Îð´ò[{0{4lXýSO}.<رcÎìÙç¸Nß§ú0éÆ^€Ô˜Z#Ý,*‹Ê–Ùñ¢ xtÅúwÕÖ™}<|xÃo;+û8¦’L÷î;¢Eåý-9w óßWtÉ#Fm@¯[{0{ÔÒrÜ’%—½þúl÷Þñ©>ldú€± µ¦Ö@7‹Ê¢²ev¼×ÇG?æÍ’ñò™~%?>÷ÜM^øáúú£²7·©©9¢’—üsJíÑóÏÏœ2elSSÝI'½Æ©2ýXò¹N\c/€ÖÝ,*«²Éë$+OÀ‡oxíµ>_§_É#G6>öØ”}ûæ…ÇáßR™{©ëôûé—Ú£hÚ¼yZxŽLŸÃ@¦ì31­ýë$+OÀçÍ›píµ'½õÖWvï¾ãê«?Ý}¾¡¡vûöÛ&Ó:ôèèþõÓ¦ÍÏ[rö~úá9y÷Óïw¦_j¦O?=û6ož¶­×Ý”é3ðdúÉ>ÓпN²ò|Ïž¹³gŸSWW¦Y³ÎÞ»÷ÎìüÅ‹?æôãÆ8Ñ6L3æøšš#ZZŽ[ºôòh~á’çÎ=¯¡¡¶±ñ}K–\vð™~©=zä‘kÆŽý@mí‘ãÆÝ{§ÌnÊôx2}€dŸ‰iè_'Y=÷“1ÉôÉ/žL ©gbZ#ú×IÊÇeúÉä;r¸Æ^­ºYTVe“×IÊÇeúi`ìH퀩5ÐÍ¢²¨l™—Ëô}ª«‘L0ö¤vÀÔèfQYT¶ÌŽËÇeú>ÕÕH¦{R;`jt³¨,*[fÇåã2ýdò¹N\c/€ÖÝ,*«²Éë$M1dú 'ÓHö™˜Ö€ƒÚÅÐ4®\¹rÅŠkˆ ™~’ÉôLkÀAjoookkkmm]A¬„’…Â…òuuu–U¦c2}€Óp:;;CǸ~ýúÖÖÖ•ÄD(V(Y(\(_&“),«L?Ædú ¦5à …^±££#4mmmk‰‰P¬P²P¸P¾îîî²úŽÜ“éÆ^€ÓèfQYTö e2™Ð.vvv¶··oJå·üí¦ä Å % … åëéé),kª3ýò§IÕO¦{R;`jt³¨,*Û«žžžîîîL&ÓÕÕµ%A–Ôž½%¹B±BÉBáBùöïß_XV™~ŒÉôc/@jL­€n•EeU6dú±/žL0ö¤pÀÔèfQYTVeÓI¦ûâÉôc/@ L­€n•EeU6|Gnì‹'ÓŒ½)ë]ºtiîõÝïö%Ó/µØðœèð0?ZÂðáãm š››‹.³™~Qa Ñ k Ðë–¼ù曽îl™’»ñEW ”;¿è*òfî¯F޽|ïÞ½MMM¥Ê4à;.Óè™~ì‹'ÓH¤ƒ¿NçΟùÌgüñh~V]]]îwäVx~Ñ4¶½½ý¢‹.ª¯¯Ï.¹¦¦&;ÿ…^˜:ujXòÉ'Ÿ¼qãÆ¢K=ztÞM]z]l…}ÑÛ ½[6Ó[Ò§;ÀT¸Æ^·¤òUô{g+?˜¹¢§•)Ó€ìx_<2ýØO¦Hr?ý;vŒ1"{m{©yÆŒÑýñË(zþÈ‘#Ÿx≷ß~ûÝ÷nõS¸ŠgŸ}6º”»ð;rÃ.ÝìR‹-‡Uä^<]$^SSóÎ;ïdïÙ³§L¦¿zõê>}Sk™ËÕ‹nI?2ý2ß§ëô+<˜áiÑæ)_¦Ýñ¾ydú±/žL ‘ê;roºé¦ï}ï{ï–Α·nÝÚÒÒž“}_zé¥ð’Â¥eï§¿k×®Üûé:4ºo~xUôä3fdÿÀ³Ï>ž“ÙÐаsçÎhÉ­­­×]w]ÑÍ.µØRqt+ù={ö\sÍ5ÑÍÜÏ9çœ%K–¼óÎ;a97Þxcôü¼- &Ož¼jÕª¢Ç§Ìýô÷¼§èMíó¶$o!…P¨ÔÆ—_uv~XuôüR3o,XpÕUWmß¾=<þÍo~Ýv¿°L»ãeŽ<EùŽÜØO¦{i 2ýŸþô§&Lx·ìµá¯¾úêäÉ“ëëëkjjÎ9çœõë×]Ú¼yó£ëú7nÜxâ‰'†Wµ´´,[¶,zrضSN9¥¶¶öôÓOî½sÿý÷×ÕÕå~…ossóÖ­[ WTj±¥2ý}ûöÍ™3§î=³gÏ>pà@vþ /¼pÖYge—óÀDÏÏÛ’° aK¢W•?žÑÌE‹ :4,çæ›oŽ^[jKò’·E•ÚøR«Âᘇ_…'DÏ/u0 ·aÁ‚£GÏ?~|tm~a™pÇËyÐÍ¢²Dö¾ñû­ßlß¿½©²¨ì»)ÏôËŸ&Å¥x2}ÀØ Â3­Ñ‚ ªá¾+a-ZÔ§—TróœCdðW[¦]{?Ž<èfQÙÔÚ±±=”éÑc/þýæQYR^Y™~ì‹'ÓŒ½)0µF‡Wª2ýêY;èfQÙ”ëzøÉP©0=1æÚ_/Zµ¿{·Êú̦“L?öÅ“éÆ^€˜Z£Ã«®®nUhpV}xwt³¨,ý°ã'?_Sû‰5Gž·a̵?®¿°ýúÿ³üeû*ë3›H2ýØO¦{R8`jt³¨l:ecýìûë‡]þXóÄ2—í«¬Ïl"ùŽÜØO¦{Rx¢’=5™L&“Éd2­9ò¼ìƒ×¼©×'k2e 2}dú2}­À Ùùô/ÜpñÚ÷ýû¥úkjÎ_wü¥Ïeqö:ý^;(éǾx2}€DÒäÉúkŽ:ÿiþQçÿÓ_~!Óöìÿ÷öÿ®¼ƒr I™~ì‹'ÓH$­@®ß=òOÙkós/ÌïkÕÖÖÖÞÞÞÕÕåx_2ýØO¦HZ#€ÈŽíìj?‘wa¾Šò¹±/žL0ö$’Ö@7‹Ê’µ÷ßÿzѪÿÕµC…Ïì»)Ïôã~ -™>`ìH퀩5ÐÍ¢²è :dú±/žL0ö8#@7‹Ê¢ƒRè”éǾx2}ÀØ àŒÝ,*‹J¡SB¦ûâÉôc/€3Rt³¨,:(…N ß‘ûâÉôc/€3Rt³¨,:(ŸÙ”Hu¦ŸŒâÉôœ‘ ƒ"%dú±/žLÀ):(RB¦ûâÉôœ‘ ƒ"%dú±/žLÀ):(RÂw䯾x2}ÀØ àŒÝ,*‹Êg6%Ré—ÿÇ¥x2}ÀØ àŒÝ,*‹J¡SB¦ûâÉôc/€3Rt³¨,:(…N ™~ì‹'ÓŒ½ÎHÐÍ¢²è :%dú±/žL0ö8#@7‹Ê¢ƒRè”ð¹±/žL0ö8#@7‹Ê¢ƒò™M‰TgúÉ(žLÀ):(RB¦ûâÉôœ‘ ƒ"%dú±/žLÀ):(RB¦ûâÉôœ‘ ƒ"%|Gnì‹'ÓŒ½ÎHÐÍ¢²è |fS"Õ™~ùy\Š'ÓŒ½ÎHÐÍ¢²è :%dú±/žL0ö8#@7‹Ê¢ƒRè”éǾx2}ÀØ àŒÝ,*‹J¡SB¦ûâÉôc/€3Rt³¨,•tPmmmííí]]]Ž•Ïl|ùŽÜØO¦{ÒyFª5ÐÍ¢²öHå§Ð>uvvf2ÇÊg6¾Ré'£x2}€¤ž‘jú§­­míŸ sBïÔÑÑÚ§îîn‡ˆø’éǾx2}€DÒô[{{û¦?ætvv†Þ)“Éôôô8DÄ—L?öÅ“é$’֠ߺºº¶ü¹0'“Étww÷ôôìß¿ß!"¾dú±/žL ‘´F@!ß‘ûâÉôc/@"it³¨,*‹ÊJu¦_þ4).Å“éÆ^€˜Z#Ý,*‹Êªl:Éôc_<™>`ìHဩ5ÐÍ¢²¨¬Ê¦“L?öÅ“éÆ^€˜Z#Ý,*‹Êªl:Éôc_<™>`ìHဩ5ÐÍ¢²¨¬Ê¦“ïÈ}ñdú€± …'*Z#Ý,*‹Êªl:¥:ÓOFñdú‰¤5 Éôc_<™>@"i€B2ýØO¦HZ# ûGßdðiÇÆv®ø’éǾx2}€¤žÀk€ðGÿí·ï6™p oªuÇ:óÄs>_1å;rc_<™>`ìHê ¼Ö@7‹ÊÊôM‡"Óßñ cëûŽÜôfúåO“âR<™>`ìHဩ5ÐÍ’’ÊÊôM‡"Óÿþëš{Ö6~êË~ÔÕÕÕÝݽÿ~ŸÙ¸éǾx2}ÀØ ÂSk ›%%••é›Q¦¦VÍ[sìÅ?[²"´”ñŠõeú2ýxO¦{R8`jt³¤¤²2}Ó¡ËôÃôüò¯®n¸hÓw¯X_¦/Ówñdú€± …¦Ö@7KJ*+Ó7ÒL?Lÿüƒ;þë/ú~Œb}™¾ïÈwñdú€± …á‚Ö@7KJ*›ŒLÈ!’ôªÍôÿxµþʘÅú¾#7½™~2Š'ÓH$­ðnõeúýKçeúUžé¿ë5vWë§–L?öÅ“é$õ^köL@âøÃ›é÷cíCÞSSsD}ýQgœÑäO /¹dÔ‹/~± +RêMõï±~Üî­ŸB2ýØO¦Ôx­ Ó?\™~öÁw½øâï¹çÍÍǼöÚ¬0gÚ´Ó–.½<ïù ^rË-gå¾pß¾yóç_|ÆÍՙ闟ÚÛÛÅúÕL¦ûâÉô’z¯5ŠfúC† Y´èÒææcêêj¦O?}Ïž¹Ñü%K.>¼¡¦æˆðc˜Ë-g54Ô†)<ˆž¶mÛ­“&\_Tmí‘'ž¸cÇœhÉóæMhjªkl|_XNt¹zV´ŠÝ»ï6¬þ­·¾½*Ì “óµ¯?tèÑaÃn¸áÔ0?7†¾øâ–G¹&zÕë¯Ï1âØè9¥¶¡p¿J­¥è~îB©——ÏÍçÏ¿8êðàÅ¿xÒIïÏýÕwÕ´eË-…/ [Rtá•”)oQÙ+߃VyâßÖÖ&Ö¯f¾#7öÅ“éÆ^€¤žÀkt³¨l©LÿÚkOzë­¯„)<˜7oB4ÿ󟥺sçžwõÕ‹ž~ÌÎ?õÔaÏ=wÓwíÛ7/¼6U‡éë_¿`âÄwîüòÞ½wæ.³0Yž3ç/.¼$÷*õ;îøãvÞ{ï'ÃÃÂ’gÎ?{ö9¹¯Ú¼yÚ¸q'D¯š5ëìÜ…”߆Üý*µ–Rû•· ¥^^>Óǰ±ñ}ÙÇa ÿá&G¿Z¹òª)SÆæ½0,ü[ߺèÌ3?Xtá•”©è‘¯|wòÖÒ§LÕªUUëûŽÜôfúåO“âR<™>`ìHဩ5ÐÍ’’Ê–Êô³÷ÉÞá}øð†hþÎ_Žžæ‡ßFOkn>¦pQÜEÕ#F=¿Tºýñ•W¾ÔÒr\4s̘ã·m»5<9²1Ú°žž¯65Õå-ä¼óFüèGW‡[¶Ü–°wïy«+µ ¹ûUj-¥ö+o*yyÑûÛDÝ?÷ÜMçŸÿ¡hþ™g~ð—¿üBîø³ÂF+*³;¥ÊTa¦_æ˜ç®¥O™þòåË[[[Ÿ|òÉŽŽŽ®®®žžŸÙª"Ó}ñdú€± …¦Ö@7KJ*[*Ó/š5—J #é /üp}ýQÑ7Á– ²K-óÊ+?ò裓ƒðïŒg&ÚE—üä“3vìƒéÓOn­Ók˜^¸ E×Rá~•zyùÍØ¹ó˹éÿŒ «ËþÏ_ÜRøÂíÛo»üò1¹—ó÷zH+¬f…»ÓïÛ\nRüòË/wwwûÌV™~ì‹'ÓŒ½)0µFºYRRÙ¾^§Ÿû´0?÷iÑà#G6>öØ”}ûæeo½ªòëô³YöŒÌ¦Û/¾øÅhɹwç/º3Ïüà7¿yá¨QMÜUøÌJ¶¡ÔZJíW…//ŸéßwßÅÑÍ|²ÿqbâij÷áyúéо0û…ÑýñK-¼T™jjŽˆÑîÝwôuw&Ó_µjÕòå˳wàéèèÈd2>³UE¦ûâÉôc/@ L­€n–”T¶Ìýôwï¾#Ly÷ÓÏû²Ùìm÷ÃÓ®¾úcÑÚ‡=ú©§>ìØ1gÚ´Ó¢WeïefæÞ˾¡¡vûöÛŠ&ÅgœÑüàƒW\~ù˜Ü/’ýìg?š½Ï+¯|©èí×®½.üøƒüuÑ@¹è6äíW©µ”Ú¯¼](õò¢øw½üòÍ÷Üó‰Üä=ú>:)ïŽùy›6ã>S>m/U¦³Ï~ÿý6 ìÎ 7œÚ×Ý9˜LõêÕË—/øá‡×­[÷Ì3ÏtuuùÌVß‘ûâÉôc/@ í€n–”T¶T¦¿pá%C‡]WW3sæøè®ôy1îž=sgÏ>'<'L³f=mÆ©cÆ_SsDKËqK—^žûª¹sÏkh¨ml|_tcœÅ‹?^^ô"ñ¼"üjóæiyû¨QMaágœÑüÈ#×¾êÑG'eo¿Sj*܆ÂxºèZJíWÞ.”zyÑÚ„žyæçÍ›Px-üÊ•W55Õ…ËäõO?}Ãyç(Ÿé—*ÓóÏÏ «Îîβe}ÞƒÉô׬YS&,ö™=ìRé'£x2}€DÒïVv?ýxM“&¼víuñÝþÄO±ÈôSN¦ûâÉô’z¯5–éÿèGWw‚Ü\¦ÏÁéǾx2}€¤žÀk€¢™~]]MÃâ°Ù--Çýò—_›Ëô92ýØO¦Ôx­P4Ó7™dúiæ;rc_<™>`ìHê ¼Ö@7‹ÊÊôM2}£qžTgúåO“âR<™>`ìHဩ5ÐÍ’’ÊÊôM2}£q™~ì‹'ÓŒ½)0µFºYRRY™¾I¦o4Î#Ó}ñdú€± …¦Ö@7KJ*¶ÍdäI¦_ådú±/žL0ö¤pÀÔèfI[eÃßýð×åÊ•+V¬XC…V¯v úG¦_Í|Gnì‹'ÓŒ½) ´FºYÒVÙööö¶¶¶ÖÖÖTì{ß¹ßAè·ðf o¹ðÆëêêò™­*©Îô“Q<™>@"i€\áOÿúõë[[[WÂ!ÞfáÍÞrá—Éd|«ŠL?öÅ“é$’ÖÈþèwtt„¿þmmmká o³ðf o¹ðÆëîîö¬*2ýØO¦HZ# W&“ ÷;;;ÛÛÛ7Á!ÞfáÍÞrá×ÓÓãXUdú±/žL ‘´F@®žžžîîîL&ÓÕÕµ±ð6 o¶ð– o¼ýû÷ûVß‘ûâÉôc/@"it³¨,*‹ÊJu¦_þ4).Å“éÆ^€˜Z#Ý,*‹Êªl:Éôc_<™>`ìHဩ5ÐÍ¢²¨¬Ê¦“L?öÅ“éÆ^€˜Z#Ý,*‹Êªl:Éôc_<™~d÷îmÿíÉð¯q Œ½Z#t³¨,*«²‰ä;rc_<™~ÐõÃ'þ¡éÒð„Û j`ìHyk€n•EeU6ÁRé'£xiÎôÿ×¶k¿áë?>æ¢ð«0½¶ü ï µ­2ýØ/™~ןxò”¿Y}Äya溦K|ÌE™'žó–’D¦’éǾxéÊô—=ôØ¥ÿiíÑf/ÌÓcœøèq—ô€äéµ5jkkkooïêêr¬ =dú±/^ª2ýì´î„Ë£Ç&“Éd2™L©BãÞÙÙ™Éd4Æ ÓOE¦ï;rc—é¯ýÁÊ ·~뱯mûðUOŸÿÅGÞ»“þ‘¶þàqf0ö$U[[[hÓÿï[ï^û'aNèÚ;::BãÞÝÝíèfQYTVeÓ#Õ™~ù9.ÅKçýô¿ù_~9ã[6\üôyÿÇcq¥XŒ½ ÖÞÞZ ¿ÿ/ÌÏ s:;;CS”Édzzz"Ý,*‹ÊªlzÈôc_¼tfúÙ9û»w¿zÿ#OŽý›ìÿ~¾eé#{’§««+ô?KjÏÞò'aN&“éîîîééÙ¿¿C ›EeQY•M™~ì‹—æL?òûÍÿÒ6ò³á ;6¶ÔÀØ `üÀhŒÊ¢²*›T2ýØO¦Ùß½{ËÒµ{ßø½q Œ½ÆOŒÆ¨,*«²‰ä;rc_<™>`ì0~`4FeQY•M‰TgúÉ(ÞñÇqRLò¡o”~ûñüœsÎ9ï¼ó.HŸã?^¦ïL€T‘éÇÒ’%K>õ'gžyf¨â)§œ2vìØÓbkÊ}³´ðÛðœ“Þs@Z͘1C¦oííímmm­­­+â¬×ûéÏ;÷®»îš?þ €tkmmmkkkooïêêJE¦Ÿ¤ïRèììÜ´iÓúõëCWÆV¯™þm·Ývûí·ÿ—ÿò_V±µêÚÙ€ñÀhŒÊ¢²¨ìAjmm]¿~ý¦M›:;;3™L*2ýò r¼lÙ²¥££#Ô¯­­mmlõšéßzë­_þò—ÿþïÿ~-çOºƒ`ü0£²¨,*{ÚÚÚ6mÚÔÑѱeË–îîn™~Ìd2™P¹ÎÎÎöööMN8É €IDAT±UÉuúsçÎýîw¿» ˆó'ÝA0~QYT•=Hííí[¶lÉd2===2ý˜ 5ëîîÅëêêÚ[½fúwÞyç7¾ñ•+WnâüIwŒŸFcT•EeRWWW&“éîîîééÙ¿¿LŸª«Høí7¾ñ%K–<ýôÓŽ{ŒŸQYTVe)ÊwäRŸF™>{ŒŸQYT•íÕ‡€Á!Ó8H2}‰Là Éô$2}€ƒ$Ó·ÕCÎ5™L&S…Sæ‰çüábÍwäÆÛê!ç¾ýöÝ&“Édêu æ£Ç~R¬è]0«,*‹ÊÆZb3ýòwzIÒnÊéL&“©ÂL?óÔZwüÅ;6¶ûóè]0«,*‹ÊÆ”L?ö»)§3™L¦ 3ýðïcý÷_ô‹ÿç‘®®®îîîýû÷k½+Fc•EeQÙ‘éÇ~7åt&“ÉTy¦¦76Üüãã.üÙ’[¶lëzWŒÆ*‹Ê¢²ñ"ÓýnÊéL&“©O™~öjý÷ÉMßýXл`4VYT•ß‘û·¸œîPOC† éuΡ^ãÀ.vÀ—0 <ÔÓd*šé‹õ½+Fc•EeQÙ˜âÄšL03ýþeâýȬәéf¸¨°)™~6Ö_ÛøÉM‹¾/ÖâB¦o2ýø^§_f!ÕŸéç-!µ×é ñãžéÿÇÕúb} &dúñ&Ó—éËôeú¦ƒÉôÅú@¼ÈôãM¦¸î½³hÑ¥ÍÍÇÔÕÕLŸ~úž=swï¾cذú·ÞúJôª0'÷ÜM^øáúú£²÷{ÉÞ¦Ìó{?äÏE |þù™S¦Œmjª;é¤÷oØ0µ¯÷Óïuùå·ªÔa,º¨Rw¿©üTrèòÈþ··ã¯ÎLùòå­­­O>ùdGGGWWWOO.лQYT•­62ýØï¦$®J®ÓÓæÍÓ.¸`dxþ-¼uûÁgú#G6>öØ”}ûæ…Çáß^—\ÉuúÑMù‹Na¢½ëG¦ßëò‹Ÿ¢‡±’Eõ#ÓϽN?«R‡´¯G>,6÷Èç^§/Ó¯æLÅŠ+W®\»ví¦M›^~ùåîîn] w0£²¨,*[mdú±ßMIÜẟþîÝw„)ºŸ~v:ãŒæ¼âòËÇôzívCCíöí·õ)˜:ôèè6÷Ó¦Ök²Ý,>LW_ý±ÂçÏŸñg?ûÑl¨ýÊ+_Šndo³yó´°ÒÂ-ÌÛøRPjù½^Û^x+YT?2ýpˆ&M:9{ˆ¦L[ê–:ò¥ŽCôMÙ#Ÿ{?}™~•ß{gùòåÙ;ðtttd2] w0£²¨,*[m|GnìßⒸÒé/\xÉСG×ÕÕÌœ9~ïÞ;£'?øàaææÍÓzÍô/þtxfÑT·Tò»aÃÔ1cޝ©9¢¥å¸¥K/ï5Ó6¯¶öȰ©aƒ‹>þü‹Gj Ë<ãŒæG¹&;3<;öá…ãÆPôÞ;y_&ª.ºü^3ýÂÃXÉ¢ú‘éïÙ3wÆŒ3¢CTê&9¥Ž|©ã;{ö9áWaš5ëìè"Ó¯òLõêÕË—/øá‡×­[÷Ì3Ïtuué½+€Ñ•EeQÙj3Ä!ˆ5™¾É4PÓ–-·ŒÕä8¤9Ó_³fMîíw¶lÙ⯠Pmdúñ&Ó7™rš;÷¼Ý»ïصëöÜ›ä˜dú2} :ÉôãM¦o2ä´téåÍÍÇÔ×5}úé{öÌu@dú2} šÉôãM¦o2™L2} =|Gn¼ÉôM&“I¦è]0£²*‹Ê¦Gb3ýÕCÎMCýdú&“É$Óô®QY•EeÓC¦ûݔęL&“Lл`4FeU•M ™~ìwSg2™L2}@ï €Ñ•UYT6%dú±ßM“Éd2 È$Óô®FcAeQYT¶úùŽÜ„Ø´iÓÚµkW®\¹bÅŠ5Õiõêò¿]]þ @,Äüs,Óô®FcT•Ee«Ü‡ ÚÛÛÛÚÚZ[[WT«%óî-ÿÛï|ç;‹/^¶lÙ €Ã' ¤a8 ƒjWW—?.@µ‘é'Dggç¦M›Ö¯_ßÚÚº²*Ý÷ן/ÿÛ»îºëë_ÿúw¾ó•‡IBÃ@†Ó0¨f2\€j#ÓOˆ-[¶tttlÚ´©­­mmUº÷ÔËÊÿöÖ[o;wî‚ Ö&a iNàÚÝÝí Pmdú ‘Éd¶lÙÒÙÙÙÞÞ¾©*ýýûÏ-ÿÛ[n¹eîܹ÷ßÿ&€Ã$ ¡a ÃiT{zzüqªïÈMˆžžžîîîL&ÓÕÕµ¥*-©=»üoo¿ýö{ï½wåÊ•[€ØúÉíóc½ýa iNàºÿ~] w0£²¨,*[m›é¯r®êƨ"á·ßøÆ7–,YòôÓO;V`ì0~`4FeQY•¥(™>UQ™>{ŒŸQYT•í•LŸª¨ˆLŒ½ÆOŒÆ¨,*‹ÊöJ¦OUTD¦Æ^ã'FcT•Ee{å;r©ŠO£LŒ½ÆOŒÆ¨,*‹ÊöjˆCÀàé$™>ƒD¦pdú ™>ÀA’é3HdúÄÔìÙ³GÁá3~üø‹àÐ §äNdú‡“ïRéÀ5jÔ€¤ûÔ§>%#BeQY™~õ&ÈÈô¡¯™þÝ0¸²o¼Ñ£G H)C>ä —ã?>Ù™¾ŒHeQY™¾Ú#Ó'u™þÝwßý\ÙX?Œ>† SÀh|H]|ñÅ2}T•™>UQ™>U+Ö™~öBïôlOµí¯Lлc!›éŸyæ™ííí[¶lÉd2===*‹Ê¢²ÕI¦OUTD¦OÕ:¼™þA†Ô2ý\Ú¦M›¾ð…/Œ=º¶¶öè£'<ßúÖ·Þ|óM™> w0Ç]6Ó?í´ÓÚÚÚBßÛÑѱeË–îîn•EeQÙêä;r©ŠO£LŸª%Ó—égM˜0áþûï饗Âãßýîw_øÂÂo?ÿùÏËô½+€Ñ8î²™þ)§œÒÚÚº~ýúM›6uvvf2•EeQÙê4Ä!`pÈô‰©~dúÙ,xÙ²eýèGëêêFŒñ_ÿëÍ}ÂßýÝß9²¶¶6ü*<Î{á¢E‹š››kjj†ü¹~¬+÷…y ÉûñÛßþvؤ°„°œ¥K—öé·aùÈGÂo‡>ȹ›~Uæ…yÊ–°£G‹;vìÆË¢Ï,µüŸüä'çž{îÑG^rÑE=þøãÑ¢Êü7ß|3Ì/9t™þ¬Y³dú ‚l¦úç+V´¶¶¶µµµ··wuu92ÕI¦Ï ‘éSýÎô¯¹æš7Þx#“ÉLš4)üEí÷w~üêW¿ßyçáq”/g_xóÍ7ïÚµ«Â Ï˯«ÂLÑ¢Eáñg?ûÙ°ßýîwW]uUå¿ýÖ·¾•½\ýÍ7ß Ÿâðø¾ûîËþjá…ѶáA™Ýéõ°Ìœ93–õëׇǣG.s4Š>³Ìò‡~|â‰'Âã0…¬äàÿä'?)³%ïþÏÿ™Ý™>‡Ztï+V¬\¹ríÚµ›6mÚ²e‹#Pdú ™>1ÕïLÿ׿þuöÇW_}5üø‘|$ûãˆ#¯½öZö.áq˜“ûÂL&SùÍdʯ+/ÓϽ¨<÷W£Gõ«_e *ÿmKKK´»víʹ³/Œ¶-<(³;½–03ú±¦¦¦ÌÑ(úÌ2Ë?æ˜cjkkŸy晼›ã—ÙÚp>úц߮[·îPú/¿üröÀŽ9ÒåQj2}€x‘é3HdúÄT¿3ý¼9µµµ¥®—/õ«Ê3ýJÖU&Ó/¿ÞJ~›+ŠÑû´;•–>-¤Ìÿ¦-éÒ¥áÈd·üÜsÏÍÞ{§ÌŠ–-[vÜqÇ…'ÿà?8¤þèÑ£ú ™>@¼øŽ\‰LŸ˜ðëô³wzÉ^K^ô‚ô~dú^§¥ío¼ñFá•øÑB^zé¥Ê›MŸ£Kãsõé:ýÊKÿ2ý2ËÏÞÿÿñ³7 úÀ>PjEáð^yå•aæÙgŸý‹_üâÝr' ôó›ßøBzè]ŒÆ‡Qâ3}g}fQÙ„Il¦_>A¦Ú*"Ó§jõ;ÓŸ4iÒo¼ñoÿöoS¦L ?~ûÛßÎþö®»î ?ÞyçáñW¿úÕÂÇç.ê˜cŽ s^}õÕþ­«0š_¿~ý›o¾ù…/|¡ðŽùa!a …7¾/ÿÛ°®ìýôwîÜ–üøãOœ81ï…™L&¼¶üýô+?,ýËôË,?´ŸÿüçáÁÿøÿ#{»›¢?œÛœp ÇwÜâÅ‹Ý=ôO<ñD>è”0²ÄgúþÎúÌ¢² #Ó§**"Ó§jõ;Ó_¶lÙI'TSS3|øð(dϺóÎ;³—1"J–‹¦Õ‹-:öØc{M±K­+÷…7n;vlxÎèÑ£Ãóó–^6&ü6,çÈ»g}ùß.]ºtܸqaN]]Ýĉ£{×d_6)÷…eþ·ƒ Kÿ2ý2Ë_·nÝE]T÷žO|âa*zð‡”0à¾{èƒN £ñ “飲¨l¼Èô©ŠŠÈô©Zr?ýCçP¬ë…^ËüèG?ÚßâKq½+€Ñ8vdú¨,*/2}ª¢"2}ªVJ2ý)S¦doÿë_ÿzâĉa™<ð@…¿Å—âzW£q¬ÉôQYT6^|G.Uñi”éSµú‘égïâ28qð@­ëG?úÑÙgŸuôÑG‡†>üXùoñ¥¸€ÞÀhk¾#•EeãeˆCÀàéSÙL?ëÃÀš5kVöÝ%Ðà0J|¦02}‰LŸ˜Š2}8DÜC€ÃK¦/2}‰LŸ˜?~|ö2êoÀ¡!Ðàð’éÄ‹LŸA"Ó'ÖÝí§>õ)‡HðYL .|G.ƒD¦O¬»[™¾±Àø `4NöYïÈEeQÙ¸Hl¦_>A¦Ú*"Ó§Ê»[™¾±Àø `4NöYO‚3}g}fQÙ„‘éS‘éSåÝm™L?¼{M)œvll÷éô®Fã$õÈôQYT6.dúTEEdúTyw[>Óûí»M©šBÑ×Iæ‰ç|@½+€Ñ81g=2}T• ™>UQ™>UÞÝÊôMy™~æ©ÛÄú€ÞÀhœ¤³™>*‹ÊÆ…ïÈ¥*>2}ª¼»•é›ò2ýðïë?™µ¶ñâ_,ûQWWWww÷þýû}^½+€Ñ8¾g=¾#•EeãbˆCÀàéëîV¦o*ÌôÃô» ÿymãE?[²"œðˆõˆõYO‚3}€„‘é3Hdú廕雊fúaÚöø-k޽hÓw Ö Ög=2}€¸é3Hdúĺ»ík¦^ÒëÓÁO‡ë¨æ½ë±[Ö{á¦Eßëß³™>@\Èô$2}bÝÝö;Ó/|PÍQuJ2ýƒ?¼…Eÿݱ>ñ>ë‘éÄ…ïÈeÈô‰uw[%×érÖ?P«;t›]%×é‹õ½+€Ñ8g=¾#•Eeã"±™~ù™j«ˆLŸ*ïneú2ý^3ýÿˆõÝ[лcxÖ“àLßßYŸYT6adúTEEdúTyw; ÷ÞY´èÒææcêêj¦O?}Ïž¹»wß1lXý[o}%zU˜žæä¾jɒˆo¨©9bHŽ¢ò¼yššêß^Røò윯}íü¡CÛpà §†Õýû·¼n»uÒ¤“ë몭=râÄw옓}máꊾ|Æ©ãÆfŽÕôÐCWмå]]ÑCT¸{÷Þ~žž¶pá%e¶­’Ã[ô •ÚͼL¿üÔÞÞ.Öô®FãxõÈôQYT6.dúTEEdúTyw; ™þµ×žôÖ[_ Sx0oÞ„0sΜ¿\¸ð’èUáñwœ›÷ªÏ~\+—¹,ýë_¿`âÄwîüòÞ½wf^øò{ïýäÕW,'ZrÑmëõð=h¥v³OWñ·µµ‰õ½+€Ñ8^g=2}T• ™>UQ™>UÞÝH¦ÿÚk³²ûÛYÇ7„¯¼ò¥––ã¢Wsü¶m·æ½jçÎ/Wr«™#Ž ‹-܌ܗÙmCOÏW›šê —sàÀ]ï+ººR/»°dÉe¯¿>»wÈÉ[]á!*ÜÍÜçDK.ºm½Þ¢­’£Ôk¦¿jÕ*±> w0Çë¬G¦Ê¢²qá;r©ŠO£LŸ*ïn$ÓÏ}Bmí‘ÙW^ù‘G„gÌ8£ü«Ê„ãEUøò\Ñ yž{î¦ /üp}ýQyó+|ùóÏÏœ2elSSÝI'½Æ©½nF…«‹Qù=*¿m}:¼åÕ§Lùòå­­­O>ùdGGGWWWOO w0WùYïÈEeQÙ¸â08dúĺ»=D×é‡ióæi\02<ÿ¾øâûé—ºN?ï ôèæõyó{lʾ}óÂãðo©µ—zy4…})zq}árJ­î`®Ó/ºmåo©ëôËïf%™~î¹ÐË/¿ÜÝÝí£@•Ÿõ$8ÓH™>ƒD¦O¬»ÛºŸ~ö»[ónÆÍ>xÅå—)óòìÔÐP»}ûmeî§¿cÇœ¼ûéç>gþü‹?ûÙfï?óÊ+_Šnd?tèÑÑ ñ§M;-zUÞêJ½<<È&ã›7O ‹*ܶ¼å”Z]™C”w?ýìs®¾úcÑkKm[ùÃ[ô •YTŸî½³|ùòìx:::2™ŒU~Ö#Óˆ ™>ƒD¦O¬»ÛÉô.¼dèУëêjfοwïÑ“|ðŠ0sóæi½fú‹:<³ÔÕúsçž×ÐPÛØø¾%K.+u]ÿüùÕTSsÄg4?òÈ5Ù™6L3æø0³¥å¸¥K/^U¸º¢/ÆŽý@mí‘ãÆPôÞ;yË)µº2‡(šÂÌð«°®ð´ðä^·­üá-zÐÊ,ªòLõêÕË—/øá‡×­[÷Ì3Ïtuuù(Påg=2}€¸é3Hdúĺ»ík¦oêÇTæÎBñšÂ[bÍš5N‡ˆ×YL .|G.ƒD¦O¬»[™¾L_¦è]ŒÆÉ>ëñ¹¨,*‰ÍôË'ÈT[EdúTyw+Ó„©®®F¦è”0®³žgúþÎúÌ¢² #Ó§**"Ó§Ê»[™¾I¦è]ŒÆÉ>ë‘飲¨l\Èô©ŠŠÈô©òîV¦o’ézW£q²Ïzdú¨,*2}ª¢"2}ª¼»•é›dú€ÞÀhœì³™>*‹ÊÆ…ïÈ¥*>2}ª¼»•é›dú€ÞÀhœì³ß‘‹Ê¢²q1Ä!`pÈô‰uw+Ó7ÉôHöYO‚3}€„‘é3Hdúĺ»-Ÿé›Ly“L€ØõÈôâB¦Ï ‘éëî¶L¦ ]oè}Cúà5¤žÓ!âuÖ#Óˆ ™>ƒD¦O¬»ÛJ2ýööö¶¶¶ÖÖÖðžðfo‰ðÆèêêòQ ÊÏzdúqá;r$2}bÝÝV’éwvv†Æwýúõ­­­+Óêÿºë+yOx„7CxK„7F&“ñQô®Fã*?ëñ¹¨,*‰ÍôË'ÈT[EdúTyw[I¦ZÞŽŽŽÐû¶µµ­M«{O½l-ï oƒðfo‰ðÆèîîöQô®Fã*?ëIp¦ïï¬Ï,*›02}ª¢"2}ª¼»­$ÓÏd2¡ëíììlooß”Vÿþs7ñžð6o†ð–oŒžž%@ï `4®ò³™>*‹ÊÆ…LŸª¨ˆLŸ*ïn+Éô{zzº»»3™LWW×–´ZR{öÞÞáÍÞá±ÿ~%@ï `4®ò³™>*‹ÊÆ…LŸª¨ˆLŸ*ïn+Éô1ö?ŒÆñ=ë‘飲¨l\øŽ\ªâÓ(Ó§Ê»[™¾±Àø `4NöYïÈEeQÙ¸â08dú廕éÉ>ëIp¦02}‰LŸXw·2} Ùg=2}€¸é3Hdú廕éÉ>ë‘éÄ…LŸA"Ó'ÖÝ­LHöYL .|G.ƒD¦O¬»[™¾±Àø `4NöYïÈEeQÙ¸Hl¦_>A¦Ú*"Ó§Ê»[™¾±Àø `4NöYO‚3}g}fQÙ„‘éS‘éSåÝ­Lߨ `ü¬ôcȼÕ¶a±8z‡qÕs ÊoɳÏ>;räÈ ·¶¯;Õ§çWûï¾û¦Nßwþ` jþüùºÙ¢g=2}T•MË­öTCEdúTyw+Ó7ö?+=ÁHV¦?È{1P«;˜*TOËoÀ„ ~ö³Ÿ¢}„Lï† ¶mÛ¶X¿ó³aá[·nmnnM7[xÖ#ÓGeQÙØ´ÜjO5TD¦O•w·2}c/@,ÆÏ(ºª¯¯¿ôÒKõ«_οþúëwíÚ½äÕW_½îºëjkk'L˜°~ýúƒß†¼‡ä4&ΗŸÌFÒlºO/„ãY~uuu‡î8Äë:ý‡~8|®“ýÎ?ø¥åΜyòäúúú°–+®¸"ºö¿×L¿èü„ ÖÜܼhÑ¢ÜçÜsÏ=C‡ ¿ºñÆ÷ìÙónÎÿvP>¬|™Á°aÃzzz¢'„9áUaNî« BÑÍ 6nÜxú駇™£Gþá8 YtÇ+¯Bå›TÉk+,A™Í{衇ZZZÂü³Î:ë…^èõhndîôéÝX´d•¯º ßZáHîØ±#.ïü^÷ì‹/[öeß¾}ÙùáÁ¬Y³ÞDóÃóö >¼¦¦¦Ô›3÷Ç;w†ƒæOFáYO‚3}€¤µýƒC¦O¬»[™>ñhîs®Ó¿ï¾ûÎ:물ùïþy¦ßÔÔ”ç•rï½÷^qÅo¾ùæî¾ûîìÌÓN;­½½ýwÞ ë 3g̘‘·®>eúa ×]wÝž={ÂöLž<9zÎ7¿ùÍk®¹&¬:¬åæ›ož3gNù….³ç=áAùeÞ~ûí‹-Š^Ï;7wEEB©Í6lX¶­ÝµkW4óà7²pÇ+¯Bå›Ták+)A™Í»ñÆÃÞ…_-^¼øÜsÏ-4z}#U~JØ>­ºÚÞZѽ‹Å;¿ÂL?w¢õΛ7/¬7š~Œž?}úôè?-T²Š>Ý»)=g=2}€Ø´ýƒC¦O¬»[™>ñhîs´´´¼þúëÑü샼{ïäE¥Œ1¢Ô×of½óÎ;yëêÓÍ4†¾}ûöìãð zíÈ‘#£½Ø»woSSSå ›½6lùeþæ7¿É½n÷ÄOÌnOôª¢¡Ôæ…E-]º4÷ÒéÙÈò;^¾ •oR…¯íëÍRò6/ºÂ:ÌÞ‡¥ŽFùíìÓq(u`û´êj{kå}cñÎ/_Íðcî.„ñ!(¢ 𛛣ç¿ùæ›åß2ýJÎzdú±iû‡LŸXw·2}âÑÜÿ)´Ú¹sçg>ó™Ç<šŸUWW—û¹^§_4 koo¿è¢‹êëë³K®©©É{òÁÜ‘ùä7º¯¼ ýؤò¯­¤½n^ùƒ\y¦_ùq¨ðÀöé?'ö·ÖèÑ£scôX¼ó+<¤YEo#Vf~¯™~8\î½Sô¬G¦›¶?©;滪LŸXw·2}c/@,ÆÏ¼ÐjĈÙ«¡K”3fÌȽAv)¥.Ô}â‰'Þ~ûíwß»ÕOÑð´ò-ϽNÿõ×_Ͻ²8ú/¥ö´Ìf—ºZ¹è2Ÿ}öÙO~ò“áAø÷W¿úUÞŠJ„¢‹Ê]ft‰ñ€ldÞŽ÷£ ½nR…¯­¤½n^áAîßuú•‡Rv ®Ó?,o­ë¯¿~íÚµñzç—c—¹N?w~îuú½Ž¹3W¯^í;r‹žõøŽ\T•‹Äfúådª­"2}ª¼»•é{b1~æ%Y7ÝtÓ÷¾÷½wKǯ[·nmii ÏɆ¡/½ôRxIáÒ²7ÔÞµkWî µ‡Ý8;¼ªòL¿Ôýô'OžœýÒΩS§FÏY°`ÁUW]•ûó›ßD÷Iohhعsgù£Ý£?¸æškz]f0~üø‡zè3ŸùL…¡Ô¢Âƒl úì³Ï†5€™·ã•W¡òMªðµ•” ×ÍËû±ÔÑèõTùq(u`û´êj{kµ¶¶†í‰Ë;¿òûégw!÷~úÑWdw-÷~ú¹//úæÌ}NmV­Z¥›-<ëIp¦ïQ\(æÌ™S÷±Ù³gÇÓóÞ^ðËÿvTØ]ñ{õfsÏzdúhY´lµé“Š‘é“òÞ­L_íHIýìÜøË>{}˜Ǻ¶¬îmÛ¶5Ê·J7þüb7“© }zžÁ®"쨖–½Ù‚g=2}´,Z¶jÚž4´ˆLŸ”÷neúj/@ÿÖÏ{¶<òÌšQ“œaxû‰5ÙÛ óæÍÛ»wï‡~˜{K ` ¨û½èÇòfúy ×›M8ë‘é£eѲÕÂ3rIÅo£LŸ”÷neúj/@ÕÏοüÙÔï®:îògO˜ºL«Ž½´cÍÆLî„%K–455Õ××Ϙ1#z´o‰ê IÛ§KáF–q“Žê§ëÇ]W_­LêÇý칞‘‹–EËV…Av•!Ó§ª{·2}*,¾0ÿÙ† «>yItyþªã.Ïj @¿Ÿõd8ÓÈ™>"Ó§ª{·2}ÊÞ52 Cú‡uÖ#Ó¨2}*wâšüªLŸ4÷neúT¸k]§ß:âÆg&¬8æb×éôK5Pg=2}€j!Ó'-]%™>iîÝÊô¨p×(Ã/~w?ýc/ýÑñ—‹õú«„³™>@µðŒ\ÒÒU’é“æÞ­L_í¨pרµµµ­­­½½=š_¶]°ÿökìC€ÊWãlŸõxF.Z-[-2›éœ?§g¦«$Ó'ͽ[™¾Ú Pá®Q±<åý ¿xá³×‡v¬k³ú«gò¬'Ù¾óg hÙŒ‘é“–®’LŸ4÷neúj/@…»FÉyÊÎ=[¯Ú÷Þûö$@?VãŒõÈôѲhÙj!Ó'-]%™>iîÝÊôÕ^€ wÜË@5®ðYL-‹–­2}ÒÒU’é“æÞ­L_í¨p×H¦ Wø¬G¦–EËV ÏÈ%-]%™>iîÝÊôÕ^€ wdúªq…Ïz<#-‹–­ƒìRÒU’é“æÞ­L€ wdú‡mß{ïoûÛç÷ÿÛnÕ¸Og=Îô2F¦OZN\eú¤¹w+Ó Â]#y À‘ر®-ÔÒg&¼¿áªq‰g=2}€j!Ó'-'®2}ÒÜ»•éPᮑ<àýö™ å4 ­|ã-ËtîQ“ÏzdúÕB¦OZN\eú¤¹w+Ó Â]#y À‘ÛùÒÏW{Y(ª«O¾vUÝe?›ò¼ËöUãܳ™>@µðŒ\Òrâ*Ó'ͽ[™¾Ú Pá®Qkkk[[[{{»}p$âXÿw}Ýeÿ0äK¹—íËôsÏz<#-‹–­™Íô“̤ðÄU¦Oš{·2}µ ìÓ`0 ý;¼û£õ2ýܳž gúÎSœ¢e3F¦O*ZD¦OÊ{·2}µà(imm]µjÕg_½ê÷”õë×oÚ´iëÖ­vÀ‘ØùÒÏtü+k.Žrü÷…ÕC¯ùÕwÿ×é÷<ë‘é£eѲÕB¦O*ZD¦OÊ{·2}µà(ikk[¿~ýúÔøõ¿¦lÞ¼yëÖ­]]]vÀaûßþ1¯¬¹då±—­¿â«ÿö“ù_ÿgé'ª2}ç)hY´l ÉôIE‹ÈôIyïV¦¯ö%ííí[·n]T{ÁÖß S::::;;»ºº8`žß>óOѵù¹æ÷õDU¦ï<-‹–M!ÏÈ%¿2}RÞ»•髽ê'@Ù±®íw‰ü±—æ]˜ß×U™¾ã,Z-›Bƒì*C¦OU÷neúP-ö½÷þ-ËþGûŽ#?Q•éB2}*D¦OU÷neú0OTeú¤LŸ´t•dú¤¹w+Ó€x¢*Ó …dú¤¥«$Ó'ͽ[™> ÀU™>)乤¥«$Ó'ͽ[™¾Ú  ~ ÀU™¾ã,Z-›B™Íô“̤°«$Ó'ͽ[™¾Ú  ~ ÀU™¾ã,Z-›B2}ÒÒU’é“æÞ­L_íP?à‰ªLßq-‹–M!™>ié*ÉôIsïV¦¯ö¨ŸðDU¦ï8‹–E˦LŸ´t•dú¤¹w+ÓW{ÔO€x¢*ÓwœEË¢eSÈ3rIKWI¦Oš{·2}µ@ý,õcР¼‘J®4cë*ï<ôÐCS§NMç‡Ê¤°·~øaû¡ÚOTeú޳hY´l»Üv)é*ÉôIsïV¦¥ž`ÈôòÞîîî:tè;3ÎUÉæ‹T`áÛ¶mkjj {^Y¨êU™>iìÒØ¤¤«$Ó'ͽ[™>Õѹÿ½úúú«®ºêõ×_ï9ý¶ÛnÛµkWü–-[¶ÜrË- µµµ_|ñêÕ«|òF*óÁp†2®«ß?lÁÙž~úéÐô©ýPiÛ{G²´Ü‰“'O^¶l™ÒTÕ'ª2}ÒØ¥± HIWI¦Oš{·2}ª£sÿû0ñàÁƒóçÏ7n\Þô®®®™3gNž<9úqÛ¶mÍÍÍK—..%Þ¼yó´iÓʵ 2ýþú°g»í¶Ûž{î¹Ô~¨´í½#YZîÄU«V%ü)…ª8Q•éÆ.]@JºJ2}ÒÜ»•éPû?Lkkk{Nß·o_]]]4>cÆŒ%K–”²äûï¿¿±±qðàÁ‹/ަlß¾}òäÉõõõa-'NŒ¯ýï5Óï5Í[ÈÂ… ›ššÂ6‡­Ý¿4½»»;ü&†—ZZZâù nÕ ñ*¾ûÝï2$,aÚ´i{÷îMøìÅÖÕs!ÁСC»ººâ”ð®0%÷]=wfŸ¶§ç^·nÝØ±cÃ{GõøãüÈÍÍÍ;vìHÇ깵%~Ÿ{ýVÞ7­à¢ÂȬY³>Fâéaþð‡ VSSSð{•·Š;w†=¯4Uõ‰ªL€4vû³úÁFⵇ‘Üéñü½nUäÁœ4iRøDa¶»îºkΜ9 ¿Øº .ä›ßüfKKKüÞ0>wîÜÜ (¸3û´==÷ðСC£^ô®]»â÷ûO¿¨‚[[ÊW¥×oÅá}Ó .jÞ¼yaããéáÇxþéÓ§ÇŸ(eñ¯Uz¢*ÓwœEË¢eS(³™~ò™v•dú¤¹w+ÓW{ª¢~æ^8ÜÜÜüî»ïÆÓ£‘¼{ïäå¼Å >¼Ø³U#‡õÔS·oßž»wfŸ¶§çk\¼xqîeøõ–é÷ã‡*¸µeùVÞ7­à¢ÂHü‰ÂHSSS<ÿîÝ»‹-­àD™~µŸ¨ÊôgѲhÙ’é“–®’LŸ4÷neúj/@UÔÏ8Lܹsçµ×^û /ÄÓ#uuu¹ÏÈ-ñ:ý‚Áe[[ÛW\Q__-¹¦¦&oæreú¹Ó ÞM(÷Ç^·*o‡äÍÖ×m+¸n¸!zÔpøwæÌ™¥ì–ÃÛžxäÕW_:ujhÍ3ÏdÈø®èá]¥gú§_xá…¡+xèС°´iÓ¦åÝ9=zRkÏûéGÓ'MšÏ_l«vîܯnþüù7Þxct™·Þz+¾í~AÅÖ•°qãÆ-]ºôÚk¯-qgöi{zîá0©¿üòËaüÈË—/Ÿ" ªàÖÔ×oE)ß´‚÷Ó/öëêêŠöOîýôsßž·“{Î3yòäeË–)qU}¢*ÓwœEË¢eSÈ3rIKWI¦Oš{·2}µ Uõó@çžwþþÅðo~çþÇþç¾øâ‹?J¼d~Ë–-“'O®¯¯¯©©¹ð £û«ô\Ú¼yó_׿nݺSO=5¼«¹¹yÉ’%G˜é¿úê«çŸ~´´G}4w!---C† ©««»ë®»º»»£éa$üX[[^ 3ÄóÛªGy$,!wÕóçÏ5jT˜sܸq¹°÷Tl] YºtiXÝË/¿\âÎìÓöôÜÃaþ³Î:+láØ±cã»Ùä}äƒ655mÛ¶­ß?TÁ­-¨¯ßŠR¾i=3ý‚‹Ú¿ÿœ9sê>6{öìxzÞÛ{~¯rç {;ìób›Gµœ¨Êô§ eѲ)4È. %]%™>iîÝÊôH‰Î¿lý£BßiǺ¶qº2È KÙÌŸ??÷9’oE)7Ï)׿…½ÝÒÒâ‹Wí'ª2}ÒØ² HIWI¦Oš{·2}ú×Î=[yfíi“C¯) Ûþî…rº"Ó§¬ßŠºß;_°¼…“U™>iìÙ¤¤«$Ó'ͽ[™>ý¥sã/ÿ߯<ø£ã¯x¾éº•Ç\¼²ö’íÏÿtà|üJÆ£u…ôïgOÕö¤ç“–q?Èß‘éçžõÈôª…LŸ´t•dú¤¹w+Ó Ââ óŸ;éªg1º<Õ±— ¨@ ßOTeú¤gä’–®’LŸ4÷neúj/@Ù»F% Ç\\꜃Á`8¬!¹VËô§ eѲ)”ÙL?ùÀLÚZD¦OÊ{·2}µ Â]£{Þ˜ÿô󟙸ªî²8xúÑñWt¬ÙhïT¬Ëô§ eѲ)$Ó'-]%™>iîÝÊôÕ^€ wâñ÷7üâåîYY{ÉÊO^"Ö¨p5–é;OAË¢eSH¦OZºJ2}ÒÜ»•髽îµ¶¶¶µµµ··GSâËö£ öß~b}Pj,Ówž‚–E˦LŸ´t•dú¤¹w+ÓW{*Ü5*–§¼¿áÏ7]fر®Ínè¯jœÉ³™>Z-[-<#—´t•dú¤¹w+ÓW{*Ü5JÎStîÙºxÕ¾÷Þ·'ú±gì¬Ç3rѲhÙj1È. %]%™>iîÝÊô¨p×h€¤HªqzÎz2œédŒLŸ´t•dú¤¹w+Ó Â]#y €j\á³™>@µé“–®’LŸ4÷neúT¸k$OP+|Ö#Ó¨2}ÒÒU’é“æÞ­L€ wä)å²ï½÷·ýíóûÿm·jœ|Ö#Ó¨ž‘KZN\eú¤¹w+ÓW{*Ü5’§”ÑŽum¡´>{„÷7üB5.vÖã¹hY´lµÈl¦Ÿ|`&…'®2}ÒÜ»•髽îÉôÊ«ýéCu ÚSn~£eÙÎ=ªqÞYO†3}ç)Î@Ѳ#Ó'-'®2}ÒÜ»•髽îÉôÊnǶ²öÒ•Ç\´ö”›TyÛmÿQ5Î=ë‘é£eѲÕB¦OZN\eú¤¹w+ÓW{*Ü5jmmmkkkoo·¯Ê(Šõ£ öW½&ï²ý[eúhY´lu‘é“–W™>iîÝÊôÕ^€²LƒÁ`0¤X¿~ýæÍ›;::ÂYL-‹–­ž‘K*~eú¤¼w+ÓW{Ž’ÖÖÖU«Výß_»Õï…)ëׯߴiÓÖ­[;;;í"€2ÚùÒÏÔ0aÕ±ÿûRý•5—T¯ëÚÂYçªÚKã óéyÖ#Ó¨2}*D¦OU÷neúP¥ö½÷þ-ËþGû»"ù¬G¦P-<#— ‘éSÕ½[™¾Ú  ~¨ÆÙ>ëñŒ\´,Z¶Zd6ÓONI[‹ÈôIyïV¦¯ö¨Ÿªq¶Ïz2œé;ÎúEËfŒLŸT´ˆLŸ”÷neúj/€ú  gû¬G¦–EËV ™>©h™>)ïÝÊôÕ^õ@5ÎöYL-‹–­2}RÑ"2}RÞ»•髽ê'€jœí³™>Z-[-<#—Tü6ÊôIyïV¦¯ö¨Ÿªq¶Ïz<#-‹–­ƒì*C¦OU÷neú@¶Ïz2œédŒLŸ ‘éSÕ½[™>@å{†*v¬k󍯳™>@µéS¹3óäWeú¤¹w+Ó¨|ÏáàÁû U7„†{î¤?íX³Ñw êÎzdúÕB¦OåÎÌ“_•é“æÞ­L ò=ùx•fú;þñA±>@5žõÈôª…gäR¹3óäWeú¤¹w+ÓW{Ê÷äãUšé‡·<{ïªÁþeÉÛÛÛ;;;8à+ èͦÿ¬Ç3rѲhÙj‘ÙL?9A&m-"Ó'å½[™¾Ú T¾žÈÇ«7ÓÃÿ·òž•'Løo‹žÜºu«XЛ­Š³ž gúÎSü΢e3F¦O*ZD¦OÊ{·2}µ¨|=‘Wu¦†×–}cå W¬ÿ/'Öôf«â¬G¦–EËV ™>©h™>)ïÝÊôÕ^ òõD>^í™þDZþœ W¬où[±> 7›þ³™>Z-[-dú¤¢Edú¤¼w+ÓW{Ê×ùx2ý0üú™oˆõ½Ùª8ë‘é£eѲÕÂ3rIÅo£LŸ”÷neúj/PùžÃ@KÃÃá&“™þï®Ö_.Öôf«à¬Ç3rѲhÙj1È. bgæÉ¯ÊôIsïV¦PùžC¿‡æ%Ο,þ¨fúÿë»·>@ŠÏz2œédŒLŸÊ™'¿*Ó'ͽ[™>@å{2ý*Íô“‡¶¶6±>@:ÏzdúÕB¦OåÎÌ“_•é“æÞ­L ò=‡‚éyKËUMMÇ×ÕÕLŸ>vïÞ¹Ñô0r÷Ýç74Ô†!ŒÄÓÃü‹]=lXCMÍ'åè¹äµk§ŽsrXìÈ‘K—^½7oþwÞùÚ­·žY_ÿÉÚÚc®»îÔ;æœ-oùñ=WQìo¥ÌùØc››O [rÞyŸyå•™ñKßùÎ%C†Þ{ûígïÙsO†­ÿàƒoÅ3„)aFSòfî¹ÓŽFâßÚÚ*ÖHçYL ZÈô©Ü™yò«2}ÒÜ»•éT¾çP0˾ùæ3>øà[a#óæ]MŸ;÷¢›n:=ž~ŒçÿÊWÆä¦ÕŲæ¡Cëò“? #;vÌ™=û‚óŸ}öÐïìî¾oÿþyaÕÓ§Mñó~,¸ŠÃžóöÛÏÞ¹óacZZ®úüç?Mà/„ý¦‡-œ9s\ôÞ9sþdÁ‚+ã÷†ñ{î_læž;­ì™þ²eËÄúé<ë‘éT ÏÈ¥rgæÉ¯ÊôIsïV¦¯ö•ï9̲ß~{V4þ›ßÌ6¬!#áÇxzSÓññü;w~£”›ä47Ÿ¸hÑÕï¾;»Ä›êtwß7xð±}Êô ®â°çŒÿ/BØ’ÚÚc¢ñ#Çû§«ëÛuaäÍ7¿¿÷”SNz篛¹çN+{¦ÿÄO,_¾üÅ_Ü´iS{{{WW—o; 7›’³ÏÈEË¢e«Ef3ýä™´µˆLŸ”÷neúj/PùzÒëëã,»Äé ý+¯Ìœ2etccÝg|jíÚ©ç߸ñÎË/ÿãúúOFwÚ‰oMSb¦_pG8gÞƒþP¼…×_ÿ¹gŸ½5Œ„gÌ87yæ£úx€Ð¬¹iѯýëÎÎNßv@o6%g=Îô§øEËfŒLŸT´ˆLŸ”÷neúj/PùzÒ§ëôs§ç^§ßׇÙnØpG¼Ø¼ùGŒüüóSöïŸÆÃ¿Ån _Só‰îîûâ›×÷\iî*’7¯Ä9ãÃFwù﹜Ë.F¿¿úÕ_$Ï|´3ýeË–=ñÄÑx6mÚÔÑÑáÛèͦä¬G¦–EËV ™>©h™>)ïÝÊôÕ^ òõ¤Øýô£G¿æÞO?ŒD÷ÙÓoºéôÜûéç¾½¡¡vûö¯Ìš§OݽgÆ;† 9®àüaz|›û;î8'^xÞl\0ì‘G¾ÔÝ}_˜íöÛÏŽg+¸Š‚azésæýøðþüåÓ¢[ë¼ùæWã;þ‡áÜs›{lâ5לO)6óÑÎôW¬XñÄO<ýôÓÏ=÷ÜOúÓöövßv@o6%g=2}´,Z¶ZÈôIE‹ÈôIyïV¦¯ö•¯'³ì ®2为ºš™3ÇíÛwo4}ïÞ¹³g_&†aÖ¬ âéyñôÂ…f(˜Y?ó̤ѣ?][{̘1'Ç·»É›?L?唓jj>ÑÜ|ââÅ×ÄÓóf{å•™ç÷™h¶%Kþ}¶‚«(Í—>gÏ~xÂÈ‘aíçžÛ–O챉a#7l¸#÷g>Ú™þÊ•+=€ЛMçYL-‹–­ž‘K*~eú¤¼w+ÓW{Ê÷*|[C™> 7›æ³ÏÈEË¢e«Å »€Š™'¿*Ó'ͽ[™>@å{2}™>•<ëÉp¦12}*wfžüªLŸ4÷neú•ï9ôL„ëêjÄâ2}ŽÒYL ZÈô©Ü™yò«2}ÒÜ»•éT¾ç —éPɳ™>@µéS¹3óäWeú¤¹w+Ó¨|ϡĘ8%7äq_ ™>@µŸõÈôª…gäR¹3óäWeú¤¹w+ÓW{Ê÷²šéäô_¦èͦù¬Ç3rѲhÙj‘ÙL?9A&m-"Ó'å½[™¾Ú T¾ž¸N?{A¦èͦù¬'Ù¾ó¿³hÙŒ‘铊‘é“òÞ­L_í*_Odú2}½ÙJžõÈôѲhÙj!Ó'-"Ó'å½[™¾Ú T¾ž …çÍ»¸±±nðàc-º:N¢{lbs󉵵ǜwÞg^yef4ýw¾vë­gÖ×2L¿îºSw옓<ÿ¾}÷NŸ>¶®®¦©éø ®Ì ¸¿óK† 9.¼tûígïÙsO¯óÇÃÚµSÇŒ99Ì3rdãÒ¥×çEçŶ¤àÇ,¶y¡|KËUa{©h™>)ïÝÊôÕ^ òõ¤g"ü½ï]vÝu§îÜù}ûî7ïâ8˜¾ýö³ÃÄîîûZZ®úüç?M?ûì¡7Þ&îß?/Ì<}úØäùÃ<7ß|Æž=÷|ðÁ·n½õÌ8Ú~à/ÜtÓéaþ°œ™3ÇÍž}aîüaæ0„‘‚QøÐ¡õ?ùÉŸ…‘;æÄoÌÍô nIÁYl3ò2ýÜMŠß;wîEá½ñôðc<ÿW¾2&þó€ëôdú2}´,Z¶*xF.©øm”é“òÞ­L_í*ßsè™~Âo~3«g_xÞÝ}_mí1=ߦ|lòüÆ5¼óÎ×âküã€{ĈÁo¿ý¿WÚÕõíÆÆºxcâéa« âÍÍ'.Ztõ»ïÎ.x‹›b[RðcÛŒ¼%çnRøDñG‹FššŽçß¹óî½ 7ë¹hY´ludP±3óäWeú¤¹w+Ó¨|ϡľçMŒܸñÎË/ÿãúúOF÷“©©ùDòü ÓsõºœÜá•WfN™2º±±îŒ3>µvíÔÒ×XðcÜŒ„mˆÿHPât™>À?ëÉp¦12}*wfžüªLŸ4÷neú•ï9”~~ÁGŒüüóSöïŸÆÃ¿½&é¹×é¿ýö¬ÜåÄ÷âÏÛ˜^¯Ó‡ šïuKŠ]§_p3J¼N?wzîuú2}>’éT™>•;3O~U¦Oš{·2}€Ê÷ŠÝOÇŽ9y÷Ó/Lr\|/û;î8§×$=,ðÖ[Ïܳçž0L™2:žþðþüåÓ¢¸ÿÍ7¿ß—?¾ÿ~nºéô‚x˜9Jç7l¸#lO‰™~ÁYl3zÞO?Ú¤ÜûéÇ·þ65÷~ú¹ooh¨Ý¾ýë2}€{Ö#Ó¨2}*wfžüªLŸ4÷neú•ï9 …çν¨¡¡vðàc-º:9_»vê)§œTS󉿿/¾¦×$}ïÞ¹3fœ[[{Ì!Ç-Xpeî}ù~xÂÈ‘aQçžÛôÌ3“¢‰ûöÝ;sæ¸xþ‚™~˜yôèO‡yÆŒ9¹ô{ïü˜Å6#o aKÂöÔÕÕ„m [´Ù³/ Ã0kÖñô¼ X¸ðOà Gõj}™>@šÏzdúÕÂ3r©Ü™yò«2}ÒÜ»•髽@å{Gû>0 ÃÖ­wÙØpxCnž#Óôf3|Öã¹hY´lµÈl¦Ÿœ “¶‘é“òÞ­L_í*_O*Ÿ8Ï{Ñž=÷ìÚõÍÜÔÈôeú€Þì9ëÉp¦ï<Åï,Z6cdú¤¢Edú¤¼w+ÓW{ÊדÊ'΋_ÓÔt|}ý'§O»wïܪËôëêjdúz³‡}Ö#ÓGË¢e«…LŸT´ˆLŸ”÷neúj/PùzRu‘ºA¦èÍVõYL-‹–­2}RÑ"2}RÞ»•髽@åëÉÀ¼¡M¹V—ÎûðÈô½Ù4ŸõÈôѲhÙjṤâ·Q¦OÊ{·2}µ¨|Ï!%9µL_¦èͳÏÈEË¢e«Å »€Š™'¿*Ó'ͽ[™>@å{)IÒÓéÆ6”e³óòÒK·_yåȺºšÁƒ1ãÜ;æÈô²tÖ“áL cdúTîÌ<ùU™>iîÝÊô*ßsé§-Ó¿þúÏýä'ÖÝ}ßÞ½s¿ùÍÏ_yåH™>@–ÎzdúÕB¦OåÎÌ“_•é“æÞ­L ò=‡„ˆyíÚ©cÆœ\WW3rdãҥלó±Ç&67ŸX[{Ìyç}æ•WfFÓ÷î{÷Ýç74Ô†!Œ„£™cÕÒrUSÓñauÓ§ÞRlQ=sðøÇb›´oß½a±aáa \Ùszn^±Uö(¸K“wKw÷}aÉ2}€,õÈôª…LŸÊ™'¿*Ó'ͽ[™>@å{ ™þСõ?ùÉŸ…‘;æÌž}aÁ9o¿ýì;¿ÑÝ}_KËUŸÿüg£ésç^tÓM§ðÁ·ÂpóÍg„K¹N?Ì¿eÞ¼‹û´¨ÜL¿à&…æ.¿”ëô‹­úHö@Á]š°[Ö®zÉ%$ÓÈÒYL ZxF.•;3O~U¦Oš{·2}µ¨|Ï!!Únn>qÑ¢«ß}wvB_ž{Eù°a ¿ùͬh<Œ45_J¦ÿöÛÿþ–°„>-*7Ó/¸IÇŸ»üR2ýb«>’=Pp—Û-¿üåÌSN9)¾ö_¦èÍfã¬Ç3rѲhÙj‘ÙL?9A&m-"Ó'å½[™¾Ú T¾ž$DÛ¯¼2sÊ”Ñugœñ©µk§ö‚çë¹Ó㤻ôûé{K±éÅVÝëôR>NÞªdÜ¥7fÆ;FŽlܸñÎøA¿LЛMóYO†3}ç)~gѲ#Ó'-"Ó'å½[™¾Ú T¾ž”òÜ× îˆ/œ/%Ñ3ç^„×é\TMÍ'º»ï‹Æ÷ì¹§×ìþð®Ó/¸ê#ÙwiÏyöÙ[››Oüïÿýÿ8¼‡îÊô½Ù4ŸõÈôѲhÙj!Ó'-"Ó'å½[™¾Ú T¾ž$äÔÓ§n ³aÃC†Wz¢ß¼~Ïž{nºéôønò µÛ·=á~úaþ0äÞO¿Ø¢.¸`Ø#|©»û¾;æÜ~ûÙ½fúÑr¢å‡åÌôó6¯ØªdÜ¥yë]¼øš#ÿú×w^ /ÓôfS~Ö#ÓGË¢e«…LŸT´ˆLŸ”÷neúj/Pùz’S?ó̤ѣ?][{̘1'÷éÞ;{÷Î=ûººš0ÌšuÁ¾}÷FÓ.üÓ0¥Ø5ò \9dÈqa†™3ÇÅo)¶¨W^™yÞyŸ©©ùDsó‰K–\Ók¦Þ>KXEXQÁmÈÛ¼b«>’=Pp—æ­wP2}@o6Kg=2}´,Z¶ZxF.©øm”é“òÞ­L_í*ßs8ì‹Á ©dú€ÞlšÏz<#-‹–­ƒì*vfžüªLŸ4÷neú•ï9HÀeúTò¬'Ù>@ÆÈô©Ü™yò«2}ÒÜ»•éT¾ç —éPɳ™>@µéS¹3óäWeú¤¹w+Ó¨|ÏA.Ó ’g=2}€j!Ó§rgæÉ¯ÊôIsïV¦Pùžƒ\¦@%ÏzdúÕÂ3r©Ü™yò«2}ÒÜ»•髽@å{p™>€Þl%Ïz<#-‹–­™Íô“dÒÖ"2}RÞ»•髽@åë‰\¦ 7[ɳž gúÎSü΢e3F¦O*ZD¦OÊ{·2}µ¨|=1dréz³©=ë‘é£eѲÕB¦O*ZD¦OÊ{·2}µè/ëׯ_µjÕSO=õä“O®Ì°+V2}@o6g=2}´,Z¶ZÈôIE‹ÈôIyïV¦¯öý¥­­­µµuùòåOfÚ¢y<9„ Í·½½Ý—ЛMÉYL-‹–­ž‘K*~eú¤¼w+ÓW{þ²yóæõëׯ^½zùòåOe×ßÜ÷ý§ŒÐ”¡AC³†Æíèèð%ôfSrÖã¹hY´lµdP2}ªºw+Óè/[·nÝ´iÓúõë[[[W‘ ¡)Cƒ†f ÛÙÙéK’³ž gú#Ó§BdúTuïV¦Ð_:::¶nݺyóæ¶¶¶õdBhÊР¡YCãvuuù’¤ä¬G¦P-dúTˆLŸªîÝÊôúKWWWgggGGG{{ûV2!4ehÐЬ¡q8àK’³™>@µéS!2}ªºw+Ó²}Ö#Ó¨ž‘K…Èô©êÞ­L_íP?TãlŸõxF.Z-[-2›é''Ȥ­Edú¤¼w{ÒI'M Sý‘ ~¨ÆÕ%œïd;Ó—Iÿв2}mLŸ—éd›L-‹–•ék{dúdAøZ~ñ÷Î;ï¼ÐÍ=묳F}…LôGv€ú  W£3fÈôѲhY™¾¶G¦O¦´µµµ¶¶._¾üIŠ¿Ëv€ú  W¯p¾Îz¹O{{»Œ-‹–•éW”g)Èô¡ì6oÞ¼~ýúÕ«W‡nîS²ìæÙv€ú  W©p¦ÎwÂYO8÷éèè¡eѲ2}dú2}ªÛÖ­[7mÚ:¸­­­«²%œé„ópÖÎ}:;;Èô‘éËô©n¡k»yóæ¶¶¶õÙÎtÂùN8ë ç>]]]ÎdúÈôeúT·Ð©íìì ½Ûööö­ÙÎtÂùN8ë ç>p ÓG¦/Ó8|ž‘K…Èôa P{ÔOÕ-‹–EËU™Íô“dÒÖ"2}P{ÔOTc´,Z-Û+™>©h™>¨½ê'ª1Z-‹–í•LŸT´ˆLÔ^õÕ-‹–EËöJ¦O*ZD¦j/€ú €jŒ–EË¢e{å¹…Íž={$euö ÁŠ ¯ž|òÉC‡6l˜}uØÆ7úÕÄ‘cíõ@5F˦ǢE‹¤hY™þ€0räÈAP;øÅ/ ú@¦?°2ýû¡D_×Q£F}à‹_<餓dú Óp™þý÷ßÿ!Tƒ(Öÿþ÷¿ï—&L˜ Ó‡L’é63ýèBï³=iû¼2} Œ¢Lÿ¼óÎkkkÛ¼yóÖ­[;::ºººì¨vž‘[Xÿfúý7Ëô˾´Ü»×Éô <Ç@ýPѲ)eúçœsNkkëúõë7mÚ´uëÖÎÎN-‹–­v™ÍôW $o—éËô˵4™>ÊÖ^õÕ-[.Q¦ÖYg-_¾|õêÕëׯ߼ysGG‡–EËV;™~a‡‘éGÑí’%KN;í´ºººáÇÿçÿüŸsgøË¿üË#FÔÖÖ†—ÂxÞZšššjjjòžN^JLœûc¼£F ›1zôèuëÖ•²…=Rluý×>HXBXÎâÅ‹ûôjXãç>÷¹ðê°aþÿýïç~¨ðRÂó$ïÌ‚¿X{œ³Øòüã?þ¸ãŽ o¹âŠ+^xá…¼ëñ‹]­´3ýY³fÉôqÜP?PѲ±(Ó=zô“O>¹|ùòÖÖÖ¶¶¶ööv-‹–­v2ýÂ;ÓŸ4iÒ{ï½×ÑÑqë­·†ãÐü/ÿò/Ãßþö·Ãø½÷ÞÆã¤8zã]wݵk×®Sà^3ý™3g†¥­^½:Œ5ª”-,1Óoii ã_þò—ÃB~ûÛßÞx㥿úWõWaü+_ùÊîÝ»¿ÿýï‡ñ‡z(ziÁ‚ñ¶a$a'ôº3 ~ü‚û°àœ Ë6lXøqÍš5aü¥—^ 0 ×é¿ùæ›ÑZdú8¨ÆhÙH|ï'Ÿ|ò©§žZµjÕúõë·nݪeѲÕN¦_Øagúo¼ñFôã–-[ŸûÜ碇~|ûí·ÃøoûÛ0¦ä¾±£££ô8¼ZWW—é‡UÄ?ÖÔÔ”²…y )¶üQ£F…ñ×_=ú1Œ”þjsss¼»víÊÑ£7ÆÛFvB¯;³àÇ/Ø^çLXþñÇ_[[ûÓŸþt÷îÝ)¹÷N{{û¹çžíÌ,ý±Ç]õÕ-{$dúhÙ¬òŒÜÂ;ÓÏ›R[[[ìÊ÷b/•%Ó/奄Í(}ù‡ñj®¼¿7^Jž¼ŸûþE—¿xñâ°g¢-?~|tï~ÌôÛÛÛ/¸à>™©½ê'ª1Z¶\2Ÿéûõ;;` ² *ûuúÑ=[¢«Â ^ZÞ×L?NÃß{ï½>eú%^§_lùyÔ¿öÚk¥¿]§_Ÿ«O×é—¾3/ÓOX~°{÷îüÇŒnôéOº3ý7ß|3 ôÇŒ#Ðre>Ó‡K¦_Øagú·Þzë{ï½÷oÿöoS¦L ?þõ_ÿuôê}÷Ý~¼÷Þ{Ãø·¿ýíž·€Ï]ÔñǦlÙ²¥Øº¢|õêÕ»wïþó?ÿó>eúŶ°g4_pùÑóÃBÂzÞø>ùÕ°®è~ú;wî K~á…®»îº¼7vtt„÷&ßO¿ôyx™~ÂòÃNûÙÏ~Fþõ_ÿ5L1bD¯Mv”2ýööö3Î8# ô;;;ýιdúU2ýÂ;Ó_²dÉgœQSS3lذ8.Ü{ï½ÑàÇ3â‚™oKKË 'œ¯[·nôèÑa-£F kìS¦_l sçLX~͇^ ËyôÑGóîYŸüêâŋnj¦ÔÕÕ]wÝuñ½k¢7†MÊ}cB^âÎ<¼L?aùÏ=÷ÜW\Q÷±K/½ô¥—^Jh²ž÷*û-wFŒá } '™>d•L¿°²ÜO?mŽÆ¾úê«a™§vÚa¼Ê‘úo½õ–ßV '™>d•gä&ÓO0eÊ”ù—‰îzÝu×…e>úè£%¾J¹ýÑ£G‡=¬„‘±Ú  ~ £eËÅ3rѲY•ÙLÅ ñGòöÃÈô£û±¤9.×þð‡?¼à‚ ¢Ž;î¸px?–þ*e ôÏ;ï<÷Ð'“µ@ý@5FË–Kæ3}¿³~g,™~aQ¦Åú_ÿú×£ïäi§&ÐÇq@ý@5FË&“é£e³J¦_XœéCªŒ5Ê=ôqÜP?PѲ½’é£e³J¦_ظqã¢üôû”É”A”üêÔ©S§OŸ>gÎû*A{{»²…ã.€ú €jŒ–í•L-›Už‘›Tõ¾øÅ/úŠTæ·1¼êñë ö¨Ÿ¨ÆhÙrñŒ\´lV ² ªžL¿Œdú“ùL,™~RÕ“é—‘L bdúU2ý¤ª—é¯4Þ`8ÃŽum~€#$Ó‡¬’é'U½äLÿàÁû †òá{õÜIWv¬Ùèw82}È*ÏÈMªz2}Cå3ýŽŸÜ-ÖgÀÖ^õÕ-[.ž‘‹–ͪÌfúÉwo/±êÉô •ÏôÿÛ×Ýý£¯ø—%?looïìì‘é£e³Ê3r“ªÞagú=G*pËô3–é|ž¯Šõ8µ@ý@5FË–‹gä¢e³j]PõRr~é 9¼ÕU2XÏ[×á­ú7¿™5|ø û÷ÏË›ÞÕõíææwíúfXì¹ç6å½zÞyŸIÿŸ ~¯Þ{ñ«¿» X(Yæ3}°dúIUO¦ŸÎL? wÜqÎâÅ×äM\°àÊ»ï>?ZìE ÿÉOþ,~iíÚ©aJ•fúÿë»·>P™>d•L?©ê•åÞ;--W55_WW3}úؽ{çîÙsÏСõ|ð­ø]aJ˜!LÉ}×¢EWÖPSó‰A9z®qß¾{ÃbÃÂÃ,¸2wžï|ç’!CŽ /Ý~ûÙaq´=fÌÉaâÈ‘K—^­+où¹k?†m¾ûîójÃFÂaâ„ ÍÏ<3)^×»ïÎ>ü„°–žËÏÛ?=×õØc››O¬­=æ¼ó>óÊ+3“·?~õ«¿8ãŒOåNéî¾/¬tëÖ»£Å>ÿü”°‘ñ«—]6âþarUdúÉC[[›Xè•L²J¦ŸTõÊ’éß|ó|ð­0„‘yó.çÌù“ ®Ì½ºüž{Æç½ë+_Ù ItX`îòã9xà 7ÝtúÎߨ¿ÞÌ™ãfϾ0š>th}tõúŽsâ‰=¯Ï]ûܹ…EÅ«?†‰6Ü1fÌÉñ[fͺ úD—Ÿ|þí·Ÿ¶³»û¾––«>ÿùÏ&oîpÝu§þÃ?LŽ|ê©§L»–3ÎøÔÏþçadãÆ;£?Tûíû÷®µµU¬ôJ¦Yå¹IU¯,™þÛoÏŠï?lXCyóͯ67Ÿ¿ë”SNzç¯å½kçÎo”rƒšáÃOÈ]~<爃ãé]]ßnl¬‹ÆÃz-ºúÝwg'çì¹kÛ–¯¢©éøhü¢‹†ÿð‡7…‘­[ï‹Ý·ïÞbËO^Wtát¡}mí1ÉÛŸ;lÜxç%—üQîíò£?^Ë’%×D)ÿ—¿|ZüŸª=Ó_¶l™XŸ ×^õÕ-[.ž‘‹–ͪÌfú+?òªW–L?w†8³¾þúÏ=ûì­a$ü;cƹÉïJH¢‹Í9èEwÑ Ã+¯Ìœ2etccÝg|jíÚ©¥¬®ØGxñÅÿ0zô§ÃÈôéc-º:aù¥/¼×íÏ.»lÄÆwFÿo ÷N;Ñrº»ï1bð?üÃäðoÏF¦ÿÄO,_¾üÅ_Ü´iS{{{WW—*F–j/€ú €jŒ–-—Ìgú~gýÎX2ý¤ªw”®Ó2èË.¥Ò¿úÕ_v¦ŸpþŽsÒá°ñÆ$¯.Ì–»Šø:ýèÒø¼|äÈÆ(1/¶üÃÈô{Ýþøï ×]wjtž—^º½çr,¸²¡¡¶¥åªÊ? ø(eú¹‡á_ÿúתŽ»ê'ª1Z¶'™>Z6«dúIU¯\÷Óß³çž0Ä÷Ó†sÏmz챉×\sJÂÛ£¡¡¡vûö¯'ÜO?ZþM7¿ñá‡'|ù˧E·ôyóͯNŸ>6šF¢élØpÇ!Ç\~ÞÚã[öG«ˆî§ «VÝfþ»¿»!žRpù Ÿ¥X¦_lû{ç÷™gŸ½5ü[Ê_A²qï'žx"ºϦM›:::T1wÔOTc´lO2}´lVÉô“ª^Y2ý ®2为ºš™3ÇE7†Ç›&nØpG¯™þÂ…æ,˜G‡†ÅÖÖVV”;ÏÃO9²±¦æçžÛôÌ3“¢‰adôèO‡ùÇŒ99¾7NÞòóV´wïÜÙ³/ 3„aÖ¬ r?³ÏÞÝ~' .?á³$\¶_pû{O=uccc]øw€dú+V¬xâ‰'ž~úéçž{î§?ýi{{»*†ã.€ú €jŒ–íI¦–Í*ÏÈMªz}ÍôÚpë­g®Zu‹ýPÉLåÊ•žVO†k/€ú €jŒ–-ÏÈEËfÕ » ¡êÉô†þð¦1cN–³Ëô€Ê|¦–L?©êÉô‹ uu5ÍÍ'þüç.g—é)$Ó‡¬’é'U=™¾A¦T#™>d•L?©êU8Ó¯ö'¸fé3¦s;eú@‰dúUž‘›TõdúGï3¦üÃÞæí%Ó'óµ@ý@5FË–‹gä¢e³*³™þŠAã¼êÉôSþÞs>ôKíP?PѲå’ùLßï¬ßÙK¦ŸTõdú2}™>8ª1Z¶ÉôѲY%ÓOªz}Íôßyçk·Þzf}ý'kk¹îºSwì˜Ä=6±¹ùÄ0ý¼ó>óÊ+3£éûöÝ;}úغºš¦¦ã,¸²XŽÑóÇ0|ç;— r\Xãí·Ÿ½gÏ=Ñĵk§Žsr˜8rdãÒ¥×Ç‹ji¹*lX˜62^EªûôsGoç ÊÑëþ/øYr‡„Õåí´0 ZÿÁߊgS»”Üwõlµbû_¦Ú €ú  £eËN¦–Í*™~RÕëk¦öÙC7n¼³»û¾ýûçÍ›wñôécãLùöÛÏÞ¹óᥖ–«>ÿùÏÆ±ïÍ7ŸñÁß C)˜éï{—]wÝ©á½ûöÝæ&Î{ÑM7¿1ü¯è+_‡Åy?>ðÀÂ»Â¢ÂæÍœ9nöì £éC‡Öÿä'Fvì˜O ïÍݶRVݧϘ;’ðưñá·ÞzfÁ“71aÿü,y8)¸wÚœ9²`Á•ñ{Ãø=÷ŒÏÝž‚­VlÿËôQ{P?Tc´lÙÉôѲYå¹IUïHî½ÓÝ}ßàÁÇÆ™r|mx˜^[{L4>|ø o¿=+ÿÍofŒ­Ã<᥼‰Ã†5ÄÃHSÓññŠvîüFnäû㈃ãÕuu}»±±.on>qÑ¢«ß}wv^\ž»ma½®ºOŸ1w¤àÊÞyçkñø¥dú û¿àgÉÛÏ·³àN{óͯ†¿÷”SNŠ65~WÁV+¶ÿeú¨½¨Ÿª1Z¶ì<#-›Uƒì‚„ª××LãÆ;/¿üëë?Ý&¾ãM^ôÜ3×NH¨K™çàÉ ô‡âÍ{å•™S¦Œnl¬;ãŒO­];µO«èuÕ½N/ãÎ)qÿÇÛœ¼¯’wÚõ×îÙgo #áß3ÎMÞøäEÉô€²Ë|¦–L?©êõ5Ó1bðóÏOÙ¿^ÿöOÉuú¹o̽X>!§›ßb¾à°aÃñ5ì ×é÷iÕ¥\§_ð¹×é‡%”’é'ìÿ#¹N¿àN ûê²ËF„‘ðï¯~õ=?uÁëô“÷¿L(™>d•L?©êõ5Ó2䏸ÆôwÜqN¯±u|Ëø0ÜtÓé ÷Ó ̽3{|ó÷è¹7µOˆ¼~x—¿|Z”¿ùæWãÛ͇‘(€Þ°áŽðrïAm[î=èûºêbŸ±”së­gFoœ2etÁÓÐP»}û×KÙÿ?KÏûé÷ÜÎb;- çžÛôØc¯¹æ”ž_°Õ%ÓÊK¦Y%ÓOªz}Íô×®zÊ)'ÕÔ|¢¹ùÄÅ‹¯é5¶Þ·ïÞ™3ÇÕÖ3dÈq \Yìîðsç^ÔÐP;xð±‹]MÙ»wîìÙÖÕÕ„aÖ¬ ÂrJ¼_ÍÃO9²1lá¹ç6=ó̤hb=úÓa3ÆŒ99÷Þ;a“†…U„ŒWÑ×UûŒ½îœ°¢3ÎßXð†9 þiØŒø- û¿àgÉÚ¢àN ÃcM ܰᎂ;¼g«%,J¦”—L²Ê3r“ªÞ‘<#·Ú‡„ÇÏV~غõî‘#³ñYÜ{‡lóõ@5F˦„gä¢e³*³™þŠAã¼êÉôûw˜;÷¢={îٵ뛹7ù‘éËôÉpíP?PѲå’ùLßï¬ßÙK¦ŸTõr¦_WWÓïÛ°xñ5MMÇ××rúô±{÷έêÏ"ÓÇqõ@5FËV’L-›U2ý¤ª73}ƒLwÔOÕ-[½dúhÙ¬’é'U=™¾A¦Ž»ê'€jŒ–­F2}´lVyFnRÕ“édúPÉÚ  ~ £eËÅ3rѲY5È.H¨z2}ƒL¨F™ÏôaÀ’é'U=™¾A¦T#™>d•L?©ê%gúCå™>P ™>d•L?©ê%dú±P CM •1ÔÇ•$X±âð_%‡#1Ð+™>d•gä&U½R2ý¶¶¶ÖÖÖåË—?I¢EóH~õoþæo–,YòØcÙW½ ß·ð­ ß½öövUŒ,Õ^õÕ-[.ž‘‹–ͪÌfú+?òªWJ¦¿yóæPW¯^½|ùò§(’üêC=ÔÒÒ²dÉû*Yø¦…ï[øÖ…ï^GG‡*F–j/€ú €jŒ–-—Ìgú~gýÎX2ý¤ªWJ¦Já¦M›BMlmm]Eqœ}uò«ßÿþ÷,Xðè£ÚWÉÂ7-|ß·.|÷:;;U1wÔOTc´lO2}´lVÉô“ª^)™~GGG¨†›7onkk[OqÿéSã“_}衇-Z´lÙ2û*Yø¦…ï[øÖ…ï^WW—*†ã.€ú €jŒ–íI¦–Í*™~RÕ+%ÓïêêêìììèèhooßJq‹j/H~uñâÅÿ÷ÿÒK/ÙWÉÂ7-|ß·.|÷8 Šá¸  ~ £e{’é£e³Ê3r“ª^)™>eùm ¯zü:¨½ê'ª1Z¶\<#-›Uƒì‚„ª'Ó/#™>@Åd>Ó‡K¦ŸTõdúe$Ó¨™>d•L?©êÉôËH¦P12}È*™~RÕ“é—‘L bdúUž‘›Tõdúe$Ó‡ÀslÔOÕ-›ž‘‹–ͪÌfúÉ r‰UO¦_±‘éƒÚ  ~ £eË(ó™¾ßY¿³–L?©êÉô+Ö"2}P{ÔOTc´lÉôѲY%ÓOªz2ýеˆLÔ^õ3#'ƒåTr¥[Wy7ࡇš:uj:?TUìù  ôðÃ+‰ª1Z¶\dúhÙ¬’é'U=™~ÅZD¦j/€ú™‘ ™þQÞÂÃÛÃÝÝÝC‡}çwÒù¡R¸ºÜõF*°ðmÛ¶555…ÆRUc´lYÈôѲYå¹IUO¦_±ßF™>¨½êg:÷¿W__ÕUW½þúë=§ßvÛm»víŠß²eË–[n¹¥¡¡¡¶¶öâ‹/^½zõ‘oCÞH%Îjz[×шÔûí®´ È›íé§ŸMŸÚ•æ=_Þõ\ZîÄÉ“'/[¶LUÔ›EË–…gä¢e³j]Põdúe$Ó8êûß'ƒœ?þ¸qãò¦wuuÍœ9sòäÉÑÛ¶mknn^ºtit]ðæÍ›§M›V®mé÷ׇ-8Ûm·ÝöÜsÏ¥öC¥yÏW8Ó§E }è“Ìgú0p»ývABÕ“é—‘Là¨wîÿ0.¬­­í9}ß¾}uuuÑøŒ3–,YRÊ’ï¿ÿþÆÆÆÁƒ/^¼8š²}ûöÉ“'××ׇµLœ81¾ö¿×L¿×L3o! .ljj Û¶vÿþýÑôîîîðc˜^jii‰ç/¸UƒrÄ«øîw¿;dȰ„iÓ¦íÝ»7á³[WÏ…C‡íêêŠgS»”ÜwõÜ™}Úžž{xݺucÇŽ ï5jÔã?^ð#777ïØ±# ªçÖöuχ¯Á¬Y³>F¢oÅ™gžùÆoüï“‹+¢‘0åôÓOvÈÒ¥KÃN_ŒóÏ?ÿÕW_=’/gÁïdÁ­Šæûdذa555¿Šy«Ø¹sgØNÕ ( ™>d¶Ûo$T=™~ÉôŽzç>ç:ý‡zèüóÏÏ›þÑfú¹Ym1<ðÀĉwïÞÝÝÝ}ÿý÷GÏ9眶¶¶C‡…u…‰3fÌÈ[W¹2ý[n¹¥ëca$^{ÉÏßëVE|ðÁI“&…Of»ë®»æÌ™“ðñ‹­«àB¾ùÍo¶´´Äï ãsçÎÍÝ€‚;³OÛÓs:ô¥—^ #»víŠß[ì<ýþ¡ nmŸöü¼yóÂ’ãéáÇh#£¿ìر£¾¾>ú¯'K—.VÞ;mÚ´°1á»±páÂñãÇÉ—³àw²àVEóOŸ>=þ“F)«ˆCŽL2Ûí· ªžL¿ŒdúG½sŸ£¹¹ùÝwß§G#y÷ÞÉËy‹>|x±g«F:4xðà¼uõéŽ% ™~ü)Â6 6,Þ¤Üé×Up«"#FŒˆß¾oß¾ÆÆÆä_p]òÖ[oå^d}ê©§nß¾=w îÌ>mOÏ=Ö¸xñâÜËð?ê-ÓïÇUpkû´çÃ× ^]ijj #kÖ¬‰îõÈ#„-ŒòýÛn»-L65¾p>|1JüæûrüNܪhþÝ»w[ZÁ‰2} \dúÙnV?˜gä¦Lϱèßú'ƒ;wî¼öÚk_xá…xz¤®®.÷¹%^§_0…lkk»âŠ+êëë£%×ÔÔäÍ\®L?wzÁ» åþØëVåí¼Ùúºmrà 7DÿΜ9³”ÝrxÛ¼úê«S§N ­yæ™g®[·®àŠF•£÷ã‡*¸µGþ­8xðàˆ#Âȸqã^zé¥/|á }üw…0=aQGòåLþNöú]-614“{ïèÍ¢eËÅ3rѲY•ÙL?9A.±êÉô+Ö"2}P{ÔÏ2tîÿ0>|xtmr±sÆŒ¹w?/¦ØUØkÖ¬‰ÓðoϼµO±iMMÍ¡C‡¢ñ½{÷öuú½nU<[ü‡R>~±u\ÈË/¿Êáß×_=oŠíÌÒ·'a‡UÇ»¨ç3rC;U*wkû´çûr§ÇWÄ_{íµ?þñ/½ôÒ0þ]·nÝ 7ÜPpoa¦_ì:ý‚[Õ×LÅŠž‘«7‹–-—Ìgú~gýÎX2ý¤ª'Ó¯X‹ÈôAíP?ËйÿøðÎ;ïüÁ~ðQñsÛ¶mÍÍÍaž(íµ×Â[z.-º[ú®]»rï–>dÈø®èá]¥gú§_xá…‹-:tèPXÚ´iÓòî§=©µçýô£é“&MŠç/¶U ;wîŒW7þüo¼1ºÌ[o½ßv¿ bëJXȸqã–.]zíµ×–¸3û´==÷p˜?ŠÔ_~ùå° ~äåË—‡O‘†Upkû´çãûìGÓã;×/\¸ðôÓOôÑGÃxø÷ÔSO}ä‘G ~ëŽða ßÉž[•÷ö¼vé9ÏäÉ“—-[¦*êÍ¢eËB¦–Í*™~RÕ“éW¬Edú ö¨Ÿ¥;йç¿1ü›ß¹ÿÃôðŸÿùŸ/¾øâ¯JÞ²eËäÉ“ëëëkjj.¼ðÂèþ*=—6oÞ¼†††ÁƒÇ×õ¯[·îÔSO ïjnn^²dÉfú¯¾úêùçŸ-íÑGÍ]HKKË!Cêêêîºë®èÁ§A ?ÖÖÖ†— ñüŶê‘G KÈ]õüùóGæ7n\îì=[WÂB–.]V÷òË/—¸3û´==÷p˜ÿ¬³Î [8vìØøn6yùàÁƒMMMÛ¶më÷Upkû´ç÷ïß?gΜºÍž=;þV¼þúëaæ?ü0Œ‡Ãz{þ—‚Ãørö|oÁïd±­Ê{{ϯbî<¡B3ÅïEo-{„dúhÙ¬’é'U=™~ÅZD¦j/€úYŠÎ¿|á³×‡ùw¬k§+ƒùz”ËüùóÝÔåÈ¿„Gã;/34PKK‹}®7‹–-™>Z6³ý“¬~0ÏÈ­®ßF™>¨½êg‚{¶<òÌšQ“B¯) o?±f œ®Èôéou¿w4¾“y Go-[^ž‘‹–Íl'Ù.H¨z2ý2’é†Î¿üÙÔï®:îògO˜ð».Ó±—v¬Ù8p>~%³ÎºBú÷³§j{|ÒÊ'ŽPæ3}°dúIUO¦_F2}€ÒÅæ?Û0aÕ'/‰.Ï_uÜå*ÐŽ„L²J¦ŸTõdúe$ÓÈíü ƒÁ`è¯a@¥[2}È™~RÕ“é—÷Ä5ùU‡@×(~5ºN¿uÄÏ6LXqÌŮӀ2gÈ~éCVyFnRÕ“éWò€êÐj/€®QÏWßßð‹ßÝOÿØKtüåb}½YÊ{œÍ6ÏÈEËfUf3ý#,Ð2ýÊPeú ö(˜ñ«­­­mmmíííÑ”ø²ýè‚ý·ŸXcèÍR®ãlVe>Ó÷;«X2ý¤ª'Ó¯äU¦j/€‚Ùk×èý ¿xá³×‡v¬k³ôf)ïq6cdúhÙ¬’é'U=™¾*ภ®ÑÎ=[¯Ú÷Þûö$€Þ,e?Îf†L-›U2ý¤ª'Ów@wtgg«‘L-›Už‘›Tõdú¨@%k/€®‘®€Þ,޳åâ¹hÙ¬d$T=™¾*€®8ÎV£Ìgú0`Éô“ªžLß@×g«‘L²J¦ŸTõdú¨ºFà8[dúU2ý¤ª'Ów@Ð5ÇÙj$Ó‡¬òŒÜ¤ª'Ów@*Y{ttôfqœ-ÏÈEËfUf3ýäò]bÕ“é; •¬½ ¦®€Þ,޳å’ùLßï¬j<`Éô“ªžLßpÜÈR×(tqíüÃóÐCM:µß7#lÃÃ?¬9@o–tgÓF¦–Í*™~RÕ“é; Ž»YêÉôOww÷СCßyç~ß’mÛ¶555…íÑ( 7K ³i#ÓGËf•L?©êÉôPÇ]€,uzÍôû1ô?ìU—øÆ#ùhO?ýôm·Ý–»¨ ¦¦¦¾¾~ܸqóæÍÛ¹sgîü¯½öÚ 7ÜPÿ±‰'†ÃÄ®®®¦¦¦C‡ųM›6-Ó‡ öᇆ%‡÷&lüäÉ“—-[æ—ôfAôJ¦–Í*ÏÈMªz2}T ’µ@×èhwdú‡ç¶Ûn{î¹çz.êСC¯¿þú÷¾÷½¦¦¦wß}7š¸eË–aÆ-]ºôàÇüñðê[o½õÑÇwΉ—ÓÝÝ]SS³ÿþèÇ0ý–[n‰>cÆŒüàÅ6>|7rÿÀèÍ"‚(Æ3rѲYåÿÞ&U=™¾*@ªºFyÁtücY¸paSSS]]ÝŒ3⤸»»;ü&†—ZZZâù·oß>yòäúúúÚÚÚ‰'îÚµë£ß_~‰WñÝï~wÈ!a Ó¦MÛ»woÒ©E‘m#³fÍjøX‰§‡ù/^d•L?©êÉôPRÕ5JÈôã{¿¼óÎ;Æ ‹Æ‡ž;½`n~èС8ΛaĈñÛ÷íÛÓEO-ŠlC‰Ÿ.Fšššâù£t»àª jnn^¼xqî%óÉoLøhÅ~,¸Š^3ýÜy’_7o^X~¹öÚk_{íµ/}éKa|áÂ…¹êˆFxà|°à2eú"ˆRÈô!«dúIUO¦ï€ ª®QB¦Ÿ;½X¾ÿØÖÖvÅWÔ××Ç{-6®x¶Â§¥mC¯Û–àÕW_:ujccã™gž¹nݺ‚o,ý£ü±à*F•|ïÝ»wÇð#]]]¹¯îÝ»7~õõ×_?~ü¡C‡Î:ë¬ðcø7Œ;6zŽîGx³þ0ç¦M›òֶĽwD¥éCVyFnRÕ“é; •¬½ºF½vjjj¢;´|ôqR|Ø×é1bÍš5 ãáßb³E÷£/éÔ¢øuú¹Ós¯ÓÏ{{é;ðå—_Ž—ßs›KùhÅödÁUÜvÛm¡E6õá‡.å~ú‘ñãÇ?ùä“3gÎŒf^¼xñ…^Xpá[¶l7nܾ}ûr'®X±Â3rAoD)<#-›U™Íô“Ëw‰UO¦ï€ T²ö(˜½v.¼ðÂE‹:th×®]Ó¦MË»ŸþÞõ¼Ÿ~4}Ò¤IñüC† ‰ïçwÆÓvîܯnþüù7ÞxãöíÛÃø[o½•L8µH܆®®®hzÞd¦àª ÝÆçå—_¡àKühÅödÁU,_¾<|„ÜO„·¿ñÆßûÞ÷rÿn±eË–áÇ?þøã 3„‘ðcîo/^ò—¾ô¥ø¾:‘?ü0,?z–ÀŽ;Âxî3u{îo¼1ž¶!lI¼½YD dúhÙ¬’é'U=™¾*ภP]£>ݸæhZ ÊòúæÏŸŸ†;Þ„mhiiñz³Tø8[¥dúhÙ¬’é'U=™¾*ภP]#™>€Þ,Gï8[¥dúhÙ¬òŒÜ¤ª'Ów@*Y{t»kTWWW±¯+¤¼ÛPlz³ôËq¶JyF.Z6«\J“Tõdú¨ºFà8[2ŸéÀ%ÓOªz2}T€~éõï Ad€L²J¦ŸTõdú¨­k$ÓÀq6dúU2ý¤ª'Ów@h]#™>Ž³Ù Ó‡¬òŒÜ¤ª'Ów@*Y{tÒÐ5’éz³8Îfƒgä¢e³*³™þžŠÈôPÊ×^3 ]#ÅPœqœÍ†Ìgú~gUãK¦ŸTõdú¨€ã.À@ë)æ€âŒãl6ÈôѲY%ÓOªz2}TÀq` us@qÆq6dúhÙ¬’é'U=™¾*ภ0кFŠ9 8ã8› 2}´lVyFnRÕ“é; •¬½ºF%vtîyçï_ ÿ:‰Л¥ìÇÙÌðŒ\´lV ² ªžLß U]£Î¿lñå0ÃŽumý²máãlÆd>Ó‡K¦ŸTõdú¨ièèܳå‘g^<ë?„—Âð›ÇV÷×¶@ö޳Y%Ó‡¬’é'U=™¾*@ÿv:7þòç3þêÙ¾¸fÔ¤UÇ^¶²æâ÷žÛÐÛY:Îf{?Èô!«dúIUO¦ï€ Ð?]£¿{jí×þêùÏݼzè5Ïæºèòü•µ—í@ÿ#™>á8+Óª™gä&U=™¾*PÉÚ  k¿Ú¿ƒôfAd€gä¢e³*³™þžŠÈôPÊ×^óß»F÷Të÷?{ÒU+k.ŽröUÇ^ú£† k6Ú{z³éqV¦ïw-[ÍdúIUO¦ï€ 8îôo×hçýמðEÉþÊO^"ÖЛ¥ŒÇÙlï™>Z6«dúIUO¦ï€ 8kt sϯþ㣫?uutÍþÛO¬±ôf)×q6«dúhÙ¬’é'U=™¾*ภª®ÑÎÿú¯«?ý»dǺ6»@o–òg3F¦–Í*ÏÈMªz2}T ’µ@רĮÑÎ=[¯Ú÷Þûö$€Þ,e?Îf†gä¢e³j]Põdú¨ºFà8[2ŸéÀ%ÓOªz2}T]#pœ­F2}È*™~RÕ“é; è€ãl5’éCVÉô“ªžLß@×Rhß{ïoûÛç÷ÿÛnÇÙbdúUž‘›TõdúN\JÖ^]#]#½YJ·c][8h>{„÷7üÂq¶'ÏÈEËfUf3ýäò]bÕ“é;q*Y{L]#½Yú¤ýéCÃ…aÍ)7¿Ñ²ì@çÇÙXæ3}¿³ªñ€%ÓOªz2}'®€ã.€®€âLšíøñÏVÖ^ºò˜‹Öžróê/o»í?:ÎFdúhÙ¬’é'U=™¾WÀq _ºF­­ÿ?{w$U}ç Œã8Áqv'YvîH¸‰ïˆÄ«¢1š£_ŒóѰS³ìæ!„Gs½–ñÞk(¬)Öâᡬ¸ø—E™„‡ÝLžõÈ3{—[ !‰IÀ \s'€RSs©aŠËåù/Çœm{ºÏô¼5MÏçS]ÔéÓ§Ïۯϯ»¿s8½¡³³s÷îÝö€O³ *Šõ£ö××Þ’qÚþ¸}Ÿ•飲¥J¦ŸÔõdú…üâ*Ó½`\5L777777·SuÛ¸qã¶mÛöïß/Ó÷ ••é¿‘+ÓN»Þ 0mذ!| ú¿¿üHû„1áCÑ–-[Â碃ÚE>Í2¨î×ÿé;U7¶ŸõÞ©úkË®]wÞM[¿¶ìû«_Ïï³~#•-Uì‚„®'ÓE2}€:;;7¾_³mÛ¶ð¡hÿþý===v$‹ýµ¸ö_Òü\ûÿîÏöoØô¿ý/ï³%Ÿéø%ÓOêz2ýQ$Óh÷îÝ;ß/ŒÙ¿ÿÁƒ{zzŽ=j@‚ÿþò?DçæG'æg\@œ¿ÏÊô¡TÉô“ºžLÉô€QÔÕÑù/yBùuñ‰ù¤“éC©’é'u=™þ(’é£åÈï~ÿë¶Õÿsw—]‘‹LJ•ßÈMêz2ýQ$Ó‡ñÀïØèŸº1*[$üF.*[ªJ6ÓONóìz2ý‚UD¦z/€þ €nŒÊŽ¢’Ïô³ŽÙqK¦ŸÔõdú«ˆLô^ýÝ•E2}T¶TÉô“ºžL¿`‘éƒÞ   £²£H¦Ê–*™~Rדé¬"2}Ð{ôOtcTvÉôQÙRå7r“ºžL¿`G£Lô^ýÝ•E~#•-Uì‚„®'ÓE2}€‚)ùLÆ-™~Rדé"™>ø0àæ–qëêèthÀ‘éC©’é'u=™þè~O~Ô[ ”ü‡cÇqs‹oá%±î¼Ïìu³£Æ‚LJ•L?©ëÉôG÷k|ò£ÞZ ä? H±Ý22ý®ÿ÷ ±>Œ™>”*¿‘›Ôõdú£û5>ùQo- ÷%ÿa@Ší–‘é‡w|÷¡ö‰7þãʿ۽{÷Áƒ=ê`|šEeG…ßÈEeKUÉfúÉ rž]O¦_°ŠÈôAïÆC‹b» ÌôÃíWk\ûÁ¼üÅðQP¬ø4‹ÊŽ–’Ïô³ŽÙqK¦ŸÔõdú«ˆLô^`<´)¶[ÖL?Ü~±ú«k?xÃÆ¿~N¬ø4‹ÊŽ™>*[ªdúI]O¦_°ŠÈôAïÆC‹b»åÊôOÆú‹^©ºacÛ߈õŸfQÙQ!ÓGeK•L?©ëÉô V™>è½ÀxhRìp 2­FÖL?Ü~ùòWÅú€O³¨ìh‘飲¥Êoä&u=™~ÁŽF™>è½Àxø0 Зé'gúÿr¶þ±>àÓ,*;:üF.*[ª&Ø ]O¦?º_ã“•é@É8åÁtžÓiÞ]Ì™~×-×Kâ½Xßµõ`dJ>Ó‡qK¦ŸÔõdú£û5>ùQo-Pòdú2ýŒL?ùÖÙÙ)Ö€a“éC©’é'u=™þè~O~Ô[ ”ü‡¬!r[ÛM©Ô9eóæMíí]_úX™¼ €IDATÒôªªòp ñø0ýò埭««*+;cBšs~íµ¦Ë.ûP˜íùçW¯Zõ¹è¹ÓïÙóåÏþÂÊÊ”—ŸyÛmSººe,cþñÝ‹xkmQ]]1qâYaµã§?ûì̆†sÃB¯¸âÃ[·¶$¬LÂôGŽ<öXXzØ{O=õéô•üúׯ­©9;üà ÓÇ ÊØð\U(Xâ¿añ> ›LJ•L?©ëÉôG÷k|ò£ÞZ ä? dÍôï¾û‚wÞùZ¸…ÖÖÑøÅ‹¯¹ë®ÅãÃÝxú/~ñ²8§N8¯¼¶¶ò‡?üBèêZ´pá•Y§¿ä’ÚÍ›ïï︯¯5,zÞ¼©É!~ÆÝ¬‹H¿=öØ'n»mJw÷Wy(Þ´ðô{ï½$Œ Ëmk»éª«þdЕÉ:}˜&ì™°+Â.úüç/Œ×êñǯ».LæÓÒ2-^±húx—æsž~®*,Ó_½zµX†M¦¥Êoä&u=™þè~O~Ô[ è½@ÉÈ"¿ýö‚hø·¿]PWW ‡p7ŸJOßÝýÕ|®ÓÐpîòåŸÝ»waž×–éïxâij†”ég]DúmÒ¤Æ[‘þôø„÷°Ðòò3]™¬Ó‡]´gÏ—ãsü㵪¯ŸïÒžž¿ª®®ˆW&}Wç“éçªBÁ2ý^xaÍš5?øÁ¶lÙ²{÷îžžÇàÓ,*›?¿‘‹Ê–ª’Íô“ä<»žL¿`‘éƒÞ Œ‡1hˆÖyŽOÈè·nm¹çž‹ª«+.¸à^{­)ëô›7ßÿÉOþ›ÊÊDœ)+;cH™~ÖE õšõñÝ¡®LÂøtynT>ÓdýóØfúéÄ/ù˃:ŽŸfQÙü•|¦ï˜uÌŽ[2ý¤®'Ó/XEdú ÷ã¡E é<ýôñéçéõ7]ßxã¾x¶Ó××OüÞ÷îéëk Ãáß\qyYÙýýGÇ?8p¡é‹Èç<ý¬wó\™ønúyúa_¥Ï'¾ÆÊ ã<ý¬U(äµw^xá…è <[¶lÙ¿¿ãði•ÍŸL•-U2ý¤®'Ó/XEdú ÷ã¡E亞þáÆ[úõôã‹¿‡ñwÝõ±ôëé§?½ªª|ß¾¯dM„çÍ›åéo¼q_MÍÙY§ãã âßwߥñÌ3&ûøÇëž~úæþþ‡Ãd÷Þ{I*[ªdúI]O¦_°ŠÈôAïÆC‹Èšé?õÔ§kjή¨(ki™väÈCÑøÞÞÅ ^F†Û‚Çg$ÑË–}&L5ž~ùåY]ôÇååg^vÙ‡â ãdLÆä#ç••ÑÐpÄã3&ÛºµåŠ+>M¶rå¿N–u·Å‹¯©ª*Ÿ8ñ¬åË?›œÑçZ™\Ó‡]ÔÜ|yXzØ{a¦_gÉ’Ï?¿:ÌêòËSa%£‘a†=OŸu§elx®*,Ó_»v­ßô|šEe‡M¦Ê–*¿‘›Ôõdú;eú ÷ãáÃ@žWœwêmçÎ/~u‰m”Lði•!¿‘‹Ê–ª vABדéî×øäG}U€’ÿ0 ÓÝÛâÅ×>üàþÂ82}(~%Ÿéø%ÓOêz2ýÑýŸü¨·(ùsÛŠŠ2Ñü°o+VÜ’JSYùyó¦öö.–éédúPªdúI]O¦?º_ã“õÖ%ÿa@ ï&Ó€‚‘éC©’é'u=™þè~O~Ô[ ”ü‡9uñÜ |Õ£a,N¦#$Ó‡Rå7r“ºžLt¿Æ'?ê­ô^ ä? H½‹geú>ͪlÉó¹¨l©*ÙL?9AγëÉô V™>è½ÀxhY£Þ ¬ìŒÊÊ\~yjñâköíûJú?ûYËç>÷oãávÛmSÂÝ0òw¾–JÓßÿp<Ù½÷^‡ñuuU<æž[z¿Ê;*› ÓðiVeK^ÉgúŽYÇì¸%ÓOêz2ý‚UD¦z/0ZDBÔÛßÿðÏþç>z]*uÎÛo/ˆFþêWQWWõì³3ûúZÃmժυGó›¿ ÝsÏEíí³£ÉŽy¨¬ìŒøGbÃø»ï¾ šù¼ySŸyæV™¾LÀ§Y•‡dú¨l©’é'u=™~Á*"Ó½-"Ÿ¨wÉ’çÍ› ‡§Ÿ¾9ýÑp7zôµ×šî¸ã£ÑÈï}ïž²²3¿ÑÝ0~Æ{¢™>üà%—ÔFH—ÃÜ.»ìCeçŸ_½jÕç2¦ Ï>;³¡áÜòò3¯¸âÃ[·¶ÄOlmQ]]1qâYË—6ùõ¯_[Ssv˜Û½÷^V ëV·µÝ”J¦ ›ÿ5" |éKÓ«ªÊÃ- ÄãÃôaþuuUa3'¤IØŸY·h„ëuA¹vΑ#…Ù†™‡E<õÔ§eú€O³¨láÉôQÙR%ÓOêz2ý‚UD¦z/0ZD>™þ;ï|mâij¢ášš³x ãÑêêŠè¼þI“>ØÝýÕ(C¿çž‹Z[g„á®®Ea|tYžhæ›7ßÕU’œé×ÖVþð‡_ˆž¾pá•cë{ï½$,+̶­í¦xn=ö‰Ûn›Æ9òP´ôp{üñëïºëcad__kKË´xn[}÷Ý„m ·0?wñâkÂsãñán<ý¿xYüç„|<~(ëpreúYwN˜aúüeú€O³¨láÉôQÙRå7r“ºžL¿`G£Lô^`<|Èó’,åågæóèâÅ×DgÇßrËG~ö³–Ï|frnk»i` ýØcŸxüñëæÙÐpn˜ÕÞ½ sÅÖñéêýýÇ+0iÒûÛ³ª¯Ÿ_;¨§ç¯¢¿@ Üêxš0‡ººªh8 Ä3 ©Ô9ñôÑ_/òÏô³nÑ×a¨;'}þ2}À§YT¶ðüF.*[ª&Ø ]O¦?º_ã“õU JþÃ@>™~w÷Wã< ¼óÎ×Ò=|øÁøÑŸÿüϯºêOúû¾è¢?wÿaø²Ë>ýŽnÆÅúÔÿøórâ[·¶ÜsÏEaÎ\ðG¯½Ö40¶ÎgçúÕßXYÙƒnu®¿aäŸO¦Ÿu‹Æb†:^¦Sò™>Œ[2ý¤®'ÓݯñÉzk€’ÿ0O¦ÿä“y]O?º]uÕŸ<÷ÜíÍÍ—G/_þÙ¼.ëÌõ«¿¸üòTOÏ_%‡Ëo¼q_|Æú ñt®óô»º úƒ±¹Î‘O?è9òùìÏô-É:”•]Ñ(úËJ>;ÇyúpjÉô¡TÉô“ºžLt¿Æ'?ê­JþÃ@BÝßÿð/9ÿÑG¯KÏ”õ«¿˜4郫V}®¯¯5LÂÝô¼]¾ü³õõÛÛg‡á¿û»»jk+Ó«6#G~æ™[¿ô¥éYÃåyó¦FéüoÜWSsvž™~t=ý®®Eé×Ó_²äÆ;îøèž=_ÃaUÓÿ‘q-ûÇ ·ôkÙÇ× ãïºëc¹®e_UU¾oßW’3ý¬[4’uøøÇëž~úæP…°½÷Þ{É ;'šO4ÿ0™>žLJ•L?©ëÉôG÷k|ò£ÞZ ä? $\©¦¢¢ìŠ+>ÜÚ:#ã$÷ŸÿüÏo»mJx4Ü>ó™Éñuu¢Û”—Ÿ]k~ïÞ…a8ý7uæÈwÜñѬáòË/Ϻè¢?O¿ì²åíèšþUUå'ž•þ·„%Kn<ÿüê²²3.¿<æœu«ŸzêÓ55g‡ji™väÈCÑøÞÞÅ ^mì‚Çg¬À²eŸ äºòOÂd¶nm  ÕÐpîÊ•· ºsÂÃlà „E„Éô ðdúPªüFnRדéî×øäG½µ€Þ ”ü‡¡¦º¥zFÀ=o2}À§YTv„üF.*[ªJ6ÓONóìz2ý‚UD¦z/0Z„œZ¦/Ó|šEe ¦ä3}ǬcvÜ’é'u=™~Á*"Ó½-BNÝ**Êì™>àÓ,*;Ödú¨l©’é'u=™~Á*"Ó½-BNí&Ó|šEe F¦Ê–*™~Rדé¬"2}Ð{ñÐ"\¸ÆM¦ø4‹ÊŒL•-U~#7©ëÉô v4ÊôAïÆÃ‡™¾›Lði•-¿‘‹Ê–ª vABדéî×øäG}U€’ÿ00*ý¨dúg2á„é2}( %Ÿéø%ÓOêz2ýÑýŸü¨·(ùÅœéûO2}(12}(U2ý¤®'ÓݯñÉzk€’ÿ0ëÔø8Iïí]ü¥/M¯ª*·0îfÝŸ}vfCùååg^qŇ·nmI~îk¯5]vÙ‡**ÊÎ?¿zÕªÏe]®L_¦¥G¦¥J¦ŸÔõdú£û5>ùQo-Pò=/~ñâkîºëcï¼óµp»ûî ÂݬO¹÷ÞKº»¿Úßÿp[ÛMW]õ'ÉÏ­­­üῺº-\x¥óôeú0NÈô¡Tùܤ®'ÓݯñÉzk½(ùƒfèuuU¿ýí‚h8 ¤Rçd}J|~ÿÃååg&?·¡áÜåË?»wïB×Þ‘éø4«²ãŠßÈEeKUÉfúÉ rž]O¦_°ŠÈôAïÆC‹4Cϸçõù<%×s·nm¹çž‹ª«+.¸à^{­I¦/ÓðiVelj’Ïô³ŽÙqK¦ŸÔõdú«ˆLô^`<´ˆ|ÎÓûíÁÏÓÏzwÐç¾ñÆ}a™¾LÀ§Y•'dú¨l©’é'u=™~Á*"Ó½-b`n[UU¾oßWâ»­­3î¾û‚wÞùÚáÃÞu×Çr]O?ëÝ\Ï7ojtMž7Þ¸¯¦æì¬Ë•éËô|šUÙÒ#ÓGeK•L?©ëÉô V™>è½ÀxhsÛeË>SQQ'é½½‹.¼2Œ · >~äÈCùgú¹žûò˳.ºèËËϼì²Å×ÞÉXn4Ÿt"u™>àÓ,*{º“飲¥Êoä&u=™~ÁŽF™>è½Àxø0 §v“é>Í¢²ã7rQÙR5Á.Hèz2ýÑýŸü¨¯jPòäÔn2}(˜’ÏôaÜ’é'u=™þè~O~Ô[ ”ü‡9µ›L F¦¥J¦ŸÔõdú£û5>ùQo-PòäÔn2}(™>”*™~Rדéî×øäG½µ@ÉS»Éô `dúPªüFnRדéî×øäG½µ€Þ ”ü‡9µ›Lði•-¿‘‹Ê–ª’Íô“ä<»žL¿`‘éƒÞ Œ‡!§v“é>Í¢²Sò™¾cÖ1;nÉô“ºžL¿`‘éƒÞ Œ‡áæ6¤›Lði• ™>*[ªdúI]O¦_°ŠÈôAïÆðŽÞ÷ÃËðõr-‘W^±²’é>Í¢²Ã&ÓGeK•L?©ëÉô V™>è½ÀøÑÙÙ¹aÆ5kÖ¼È,o}ÜNÈ%¼T &¼lvïÞíð|šEeó'ÓGeK•ßÈMêz2ý‚2}Ð{ñcÛ¶máMýúõkÖ¬ù6'ý_ÃNÈ*¼HÂK%¼`ÂËfÿþýÀ§YT6~#•-Uì‚„®'ÓE2} Þî·lÙÞ÷7lØÐ‰Â‹$¼T &¼l<èð€ü•|¦ã–L?©ëÉôG‘Lˆìß¿?¼ãoÛ¶­³³s#$ /’ðR /˜ð²éééqø@þdúPªdúI]O¦?Šdú@¤§§çàÁƒû÷ïß½{÷NH^$á¥^0áesôèQ‡äO¦¥J¦ŸÔõdú£H¦P02}(U~#7©ëÉôG‘LÆ¿c èƨl‘ð¹¨l©*ÙL?9AγëwÞy72Jî™ð§ßÈ-Òíý@7Fe‹Gss³L••é»ÚwvvnذaÍš5/2ƒ^OñâÅ?üð’%Kì+8­t;@ÿÐQÙb³fÍš 6tvvîÞ½[ú‡ÊÊô‹Ô(þ–¶mÛ6nܸ~ýúÐþ¾Íp šéå+_yàþãüöœ¾Vß½ÐNÐ?tcT¶¨¬Y³fýúõ7nܶmÛþýû¥¨¬L¿ôíܹsË–-¡ñmذ¡á4Óÿò—¿üÕ¯~õ?ÿçÿl_Œ– 6lܸqË–-;wîEQ™>è½ú'º1*‹Ê¢²ƒ’éS‘éƒÞ   £²¨,*;(™~æ³Ü½uutŽzEdúà}@ÿ@7FeQYTvP~#7ósìØ#n ·°‹Ö÷©ý¯nÝ£Q¦ã¹÷ èƨ,*‹Êæi‚]N¦ŸO¦¿ÿ‡1ŒX_¦0B2ý÷‘éç“é‡÷ÿð/¾sî ÿ¸òïvïÞ}ðàÁ£Gæ³o“•é$“é¿L?ÏL?ŠõÛ'~òÇË_ܹsg>±¾L`„dúï#ÓÏ?Ó·ß½6¿}â'7þõsùÄú2}€ò¹ï“5ÓŸ0a‚(?k¦ÿ¯±~Ûß ëËôa<ð;6ú'€nŒÊ¢²¨ì˜*ÙL?9ANxVB¦_°pŒ4*³¸‹¢‹ð ëËôa<^ï@ÿÐQYT•Í“L?óY¥tžþX¬yÖ]”O¬/Óï»èŸº1*‹Ê¢²#$ÓÏ|–L™þ¿Æú¹¯­/Óï»èŸº1*‹Ê¢²#$ÓÏ|Vòµw~°¶¶òw¾?ƤRçDc¾þõkkjή¨(»÷ÞKÂø¬³Z±â–††sËËϼä’ÚŸüä¾U«>÷‘œî^qŇþó?ÏXâ‘#Í›75Ì0,â©§>¾&Ë—¶®®ª¬ìŒpwÏž/þóVV~ Ìç¶Û¦tu-Ц‰e̶·wñ—¾4½ªª<ÜÂ@¸Oðì³3£Õ ë³ukKÖL?ùÖÙÙ™5Ö—éƒ÷]ôOÝ•EeQÙò¹™¯˜A¯§¿hÑ¿{ê©Odžáü—g=þøõwÝõ±îî¯öõµ¶´L[¸ðʬ³úÂ.Þ·ï+ýý?ýôÍ©Ô9éw¯½öO3–ØÚ:ãî»/8|øÁwÞùÚç?aúš|ñ‹—Å6¸ä’ÚÍ›ï3 ‹O™7ojÖóô㻋_V5Ì3ÜÂüÃÝx‚{ï½$lB˜U[ÛMW]õ'Ã8‹Æ Yc}™>Œ~Ç@ÿÐQYT•Sì‚tùdú¿ùÍ_64œ?ú‘œ·gÏ—Ã@}ýÄ·ß^ìéù«êꊬ³Šƒøþþ‡3î–—Ÿ™±ÄººªhæÑÉøékÒÝýÕ¬©z˜Ïĉg%gúa¶¿ýí{«R©sâ âsöÓ×gH™þêÕ«³Æú2}€’é¿O>™~¸}îsÿö»ßý|ÿ67_?š.º*NòîsÝ8<~óæû?ùÉSYùŒEçùôKöåøÃ|á…Ö¬Yóƒü`Ë–-»wïîéé‰÷mòž—é$“é¿Ož™þoÜ÷‰OÔ‡ðo|üúú‰Ñ…ìóÿÑÚA3÷ôóôß~{A®P>,ú{ß»§¯¯5 ‡ý“@˜mü_ 2ÎÓy¦ÿâ‹/~ûÛßnoo߸qã/ù˃Æû6yÏËô’Éôß'ÏL?Ü.¿<õì³3o¹å#ñ˜%Kn¼ãŽFüo~ó—ñEíG’é·¶Îøüç/<|øÁp»çž‹r…õ55gÿð‡_]]‹î»ï񿄻ªò}û¾2pþÑeúßyçka¶wÝõ±ôëé<Ó_½zõ /¼]gË–-û÷ï÷mòž—é$ó¹ï“¦ÿì³3+*ÊÞxã¾ô)—,¹ñüó«ËÊθüòÔË/Ïy¦ßÛ»¸¹ùòòò3kjÎ~ê©OçºHÎk¯5}ä#ç…å64œ»bÅ-ñ£Ë–}&¬dÖÙ.\xex(Ü,øø‘#b¦ÿÊ+¯¼ð /½ôÒºuë~ò“ŸìÞ½;Þ·É{^¦ã¶÷ èƨ,*‹Êæ©d3ýä9áYCM± vÛ¹óKçŸ_]´«gúk×®M¿üÎÎ;ó©ˆLÆsï@ÿÐQYT•Í“L?óYÅ–’/^|ÍáÃ8ð@úErdú€÷]ýÝ•UYTv’ég>«ØRò+nI¥Î©¬üÀ¼yS{{Ëôï»ú'º1*‹Êªì¸%ÓÏ|V‘‡æ2}Àû.€þ €nŒÊ¢²*;nùÜÌWL±¥äCý¡Úaü°­L8µ½ý@7FeQYT6Oì‚t2}™>@Ñ’é¿L_¦P´dúï#Ó—é-™þûdÍô_{­é²Ë>TQQvþùÕ«V}.ktž~·µuFuuÅĉg-_þÙä‘_ÿúµ55g‡9ß{ï%‡?@)ñ¹ï“5Ó¯­­üῺº-\xer¦ÿØcŸ¸í¶)ÝÝ_=rä¡ÖÖ #üú»îúXÙ××ÚÒ2-žs˜àî»/x篅[ˆç<ÔéeúÀéÒ{Ð?tcT•EeóT²™~r‚œð¬9uCù˗vïÞ… —¸‰ïNšôÁßþvAÆ²Ž¬¯ŸøöÛïìéù«êêŠxâx|xV<ç¡N/ÓN—Þ €þ  £²¨,*›'™~æ³æÔ[·¶ÜsÏEÕÕ\ðG¯½Ö”œégÔsLWVvÆ sÒô2}Àû.€þ €nŒÊª,*[bdú™ÏJ¬ßx㾺ºªh¸¬ìŒþþ‡£áÇÞyú]]‹.%á<ý!M/Ó¼ïèŸèƨ¬Ê¢²%F¦Ÿù¬9õ¼yS£Dþ7î«©9;ùñ×=ýôÍýýwu-º÷ÞK2®§F¼ž~ÆÈ%Kn¼ãŽîÙóå0ü›ßüeXJúõñ~0Üîºëcñœ‡:½Lð¾   £²*‹Ê–¿‘›ùŠ˜S¿üò¬‹.úãòò3/»ìCñµw¶nm¹âŠ—•ÑÐpîÊ•·¤'é‹_SUU>qâYË—6yä’%7ž~u˜Éå—§ÂR¢‘GŽ<ÔÒ2-,®¦æì§žútúœ‡:½L8-z/ú'€nŒÊ¢²¨lž&Øé’¯½ã&Ó8…dúï#Ó—é-™þûÈôeúEK¦ÿ>2}™>@Ñò¹ï#Ó—é…ï½èŸº1*‹Ê¢²y*ÙL?9ANx–\^¦¸÷ èƨ,*‹ÊæI¦Ÿù,¹¼Lð¾  èÆ¨,*‹Ê'™~æ³äò2}Àû.€þ  £²¨,*[œdú™ÏráM¦ÞwÐ?tcT•EeLjßÈÍnãÆíííßþö·_|ñŵ ‘LÆ-¿c èƨ,*‹ÊŽ© vAV6lX³fÍ‹ KØua†Ý¸{÷îh—ÊôFH¦ŸÝ¶mÛ6nܸ~ýú5kÖ|›! ;-캰ÃnÜ¿´Keú#$ÓÏnçÎ[¶lÙ¸qㆠÚ¢°Ó® ;0ìÆƒF»T¦0B2ýìöïß¿sçÎmÛ¶uvvndˆÂN ».ìÀ°{zz¢]*Ó!¿‘›]OOÏÁƒ÷ïß¿{÷î QØia×…vãÑ£G£]*Ó‡ñÀïØèŸº1*‹Ê¢²cªd3ýä™b«ˆLô^ýÝ•EeQÙAÉô)ŠŠÈôAïÐ?ÐQYT•”LŸ¢¨ˆLô^ýÝ•EeQÙAÉô)ŠŠÈôAïÐ?ÐQYT•”ßÈ¥(ŽF™>è½ú'º1*‹Ê¢²ƒš`P2}€’éS 2}€’éS 2}€’éS ÅŸéO˜àpŠšßÈ¥@N£L¿ðáþ-Ñ_)Ð{ôOtcTVeQÙS²‘_r‚L±Ud¬3ý|ÒíHÀ…øè½ú'º1*«²¨li“éS‘él3AïÐ?ÐU•EeO_2}Š¢" ™þ„ –-[–J¥***š››ûúú¢ña`Á‚U'…x|GGÇÔ©SÃÄ“'O~þùç£9Ä’†´kïôööÖÖÖöôôĆ1a¢1>úhMMMXÄܹsÃøäy®\¹²¡¡¡¼¼üÒK/ýéOViÊ”)áîôéÓß|óÍŒE÷÷÷‡m sËjkkK_¥+VÔÕÕ•••…»ûöíkll¬¬¬ ó™9sæ²nf^Ÿ#GŽTWW'Ï3â?žq·¼¼mÚ´U«VÝzë­ñ˜¥K—ÞyçQòþÖ[oÅײOžgòÝôëé766FÛÔÔ”+¬ÛÿfÀý÷ß?𱙗鏝dúpzáQ #ÉôÛÚÚjjj***æÏŸßßßïëë[´hQÅI .ŒÇ···_|ñÅåååS§N¯½óôÓO‡É’3ë¬úªU«Â7mÚ”>åÒ¥K'Ož\VV6mÚ´°¸|æ™|7ÕÒÒV>loØê\É Û5eÊ”° +W®ŒÍØÌôÙfÝW2}8½ø\ d´®§?~ìÚµkòäÉ^9è½ú'º1*‹Êª,±’MK“dŠ­"2ýXkkkooï»ï¾›~‘Ð{ôOtcT•UYNÈô)’Š$dú£»&ÙÏ ÷èŸèƨ,*«²DdúEE£§ö¦@ ÷蟺± ²¨,*[üüF.Eq4žÚóôu Ð{ôOtc•EeQÙÓ»€ÂéŒLŸ‘éŒLŸ‘éŒLŸ‘éŒßÈ¥@dú0è½ú'€nŒÊ¢²¨ì˜*ÙL_J{zUD¦z/€þ €nŒÊ¢²¨ì dúEEÍô<¼çoþõj½@ÿ@7FeQY•·dúEE2ýƒ›¶áOotutzµ€Þ   £²¨¬ÊŽg2}Š¢"3ý£ïxúå×>Ú ·]Ï}ß«ô^ýÝ•EeUvœó¹ÅјžéÜü³ÿúÅ'¾sÎ ßKݶöÌk˯Ý÷½Ÿè ÷èŸèƨ,*«²L° (ŒA3ýÿÔúïÿfö‚ïüéÌuçÝô݉ŸŠNÏo?ëcèŸé§ ™>2h¦jo ?™>’Ïyú«îüR{ÍgÚ+>Gíß9ç†ý¯n¶÷NÈô)˜ü¯§ÿû7þyÓí®-¿ví®ëÄüF.’¦9zð𯗾ô½ÏŒNØû…WíCÐ{ôOtcT•UÙq®d3}WH?½*20Óýþþ^êÖ0AWG§Ýz/€þ €nŒÊ¢²*;žÉô)ŠŠ$dú‘£ï\Ñ~äw¿·'AïÐ?ÐQYTVeÇ-™>EQ‘A3}@ïÐ?ÐQYTVe‘éS‘éƒÞ   £²¨,*;(¿‘KQ2}Ð{ôOtcT•Ee5Á. 0dú#$Ó§@dú#$Ó§@dú#$Ó§@dú#ä7r)™>Œz/€þ  £²¨,*;¦J6ÓON)¶Š ÓŸ0¡p¯öM›6Õ××'/ñÉ'ŸljjІÃÀ’%K¼HÐ{ôOtcT•UY;!™LŸ¢¨Hidúñ"f̘ñãÿ8aÊþþþÚÚÚ={öDwwíÚ•J¥ÂH¯ô^ýÝ•EeU–2}Š¢"%vž~EEEò/½ôÒœ9sÒÇ466®^½Úë½@ÿ@7FeQY•%LŸ¢¨H‰eúƒ.kΜ9ëÖ­KÓÞÞž‘òƒÞ   £²¨¬Ê’ÁoäRGcB¦Ÿ‘ÇwÃÀªU«ÊË˧OŸ¾}ûöxü²eËR©TEEEsss__ßÀ'fa˜><+<·­­-W.ßÛÛ[[[ÛÓÓ“>&<%Œ‰ž2!M¸ÛÑÑ1uêÔ0ÛÉ“'?ÿüóÑSÂjwuu¥Ï¶»»;Œô:AïÐ?ÐQYTVeI0Á. 0Æ"ÓŸ;wî¡C‡Ž?¾lÙ²«¯¾:?{öìž“ÂÀ#<2ð‰Yg¦ Ó÷öö†'666&œkÿÀ´µµÅwÃðâÅ‹s-¢¶¶6Ú¨,Z´(Y^^>p¶ƒ^±çdúÈXdúñ9øÇSò0~ïÞ½Ñðž={êêê>1ë Ôûö틆Ã@B¦ÿÖ[o¥ŸS?eÊ”è‰Y¦\±bEÆYù2}`dúÈXdúùŒOÏú‡7ìn¿ýöõëׇðoKKK¬¶oßÞÔÔT]]}á…vttD#'Ožœ‘ò‡»®½$“éS ÃÎôËÊÊŽ? ÷ööæÍ'Ÿ§Ÿk†éçé‡9$gú›6mºþúëÃ@ø÷Í7߸YŸ¯Ìœ9sÚÛÛß·^yÅoäÉüF.2ìLÿÊ+¯ ã?~àÀ¹sçæ“éG—Ų^O?× Ã”Ñ›šš’3ý`Ú´i«V­ºõÖ[³®C<²¹¹yÏž='Nfú555ÑÈ5kÖ„uKŸ[XôêÕ«½NÐ{ôOtcT•UY”l¦Ÿœ SlIÈô·oß>}úô²²²†††gžy&ŸL¿­­­¦¦¦¢¢bþüùýýýäša___KKKyyyxn˜CÖKÞ§[µjUXĦM›²®C<²½½ýâ‹/s›:uj|ícÇŽ¥R©]»vEwÃ@¸¯*è½ú'º1*‹Êª,YÉô)ŠŠ$dúC~MO…Wõ®]»&Ož<¦;déÒ¥ñÅvÂ@[[› z/€þ €nŒÊ¢²*k'$“éS)’L¿µµµ··÷Ýwß5kVV5Ð{ôOÝ•EeQÙ¢"Ó§(*2Š™~EEŰŸ»råÊT*UYYÙÜÜÜ××Ím Õ½@ÿÐQYT•=%üF.Eq4Žb¦è½ú'º1*«²¨l©š`Pƒfú§ö¦@@ñ“éS Å|ž¾L8-Èô)™>ÀÉô)™>Àù\ D¦ãÞ  èƨ,*‹ÊŽ©’Íô¥´§WEdú ÷èŸèƨ,*‹ÊJ¦OQTD¦z/€þ €nŒÊ¢²¨ì dúEEdú ÷èŸèƨ,*‹ÊJ¦OQTdÐLÿèÁÃ{þöá_¯Ð{ôOtcT•UÙqËoäRGcB¦póÏ6Ôß&èêèÔ)@ïÐ?ÐQYTVedz v…1ÔLÿèÁÃ;ž~ùÿá¡pûí³ëOÕº ™>’¦póÏþ©ù?}÷ƒŸzuò¬ö³>±¶lÆïÖ½q ×  HÈô)A3ýÿÔúïÿfö‚ï4ܾ¾ö–ï}ø¶èôüµå×u B¦œ&dúÈ ™þ©½)PüüF.’ÏyúÏßòçí?µ¶lF”³·ŸuÝwªnÜÿêf{ô^ýÝ•EeU–%œé;óúôªHúõô»ÿËûÑÿg”ì¯ýÀµb}Ð{ôOtcT•UY"2}Š¢"é™~4æèÁÃ?ÿϬÿ£ÏFçì¿ý«ö!è½ú'º1*‹Êªì8'Ó§(*20Óuÿ—ÿ¶þÿ%Ùïêè´AïÐ?ÐQYTVeÇ3™>EQ‘„L?rôàá+Úüî÷ö$è½ú'º1*‹Êªì¸å7r)Š£qÐLÐ{ôOtcT•UY&؆L`„dúˆL`„dúˆL`„dúˆL`„üF."Ó‡ñ@ïÐ?tcT•EeÇTÉfúÉ 2ÅV™>è½ú'º1*‹Ê¢²ƒ’éS‘éƒÞ   £²¨,*;(™>EQ™þHä ~îš'Ÿ|²©©)K–,Ñ{|vÐí•EeQÙâ'Ó§(*"Óé‘ü‡ ¹ð‰ó-±h£óQY±ÑÚºaϧ¿¿¿¶¶vÏž=ÑÝ]»v¥R©0RïðÙ@7FeQYT¶Èù\ŠâhLÏô'üAeeåM7Ýôæ›o¾÷bM?gΜÄOß±cÇìÙ³«ªªÊËËg̘±~ýú19ZNQÊœÏrKàäñS² ÃXèðÖ󔇸^zé¥p¥ill\½zµÞ à³+€nŒÊ¢²¨l‘s½ dH™~4òرcK—.6mÚ{/Ö?ŒïééiiiillŒîîÚµ«¡¡aÕªUÑYÆÛ¶m›;wî˜-2ýS½™Å°ÐÒÈôçÌ™³nݺô1ííí)?EH¦O #Ó”——¿÷bMäÈ‘ŠŠŠh¸¹¹yåÊ•ù¬Ã#p3ã\û*L°jÕªhõÂúlß¾=a[ò\hV¹¶+×Î̧ˆéY·b¬÷RÓÕÕ•¾™ÝÝÝa¤NPädúÈðÎÓòÉ'§OŸþÞ‹5G¦_]]ž_çòøãÏœ9óСCýýý<òH4òÒK/íìì<~üxXVÙÜܯ­­®ìàÀE‹ \,ÇRŽLöìÙ='…x¹­­­³f͊LJ»Ã^nÆ4ÑÀ<ÐÖÖ?†/^žx≰ܰÂöΟ??^D®yΙ3§»»;ìŸPšT*•~÷ºë®ËXtغ°-½½½a£ÓWiÞ¼yqäkŸglf|7×¾ Ì;7lK˜Õ²eË®¾:é–çB³Š¶+^ø)¹vf>ELß9Y·b¬÷Rü§²tñ1@Ñ’éS øž~ÐÐаwïÞ÷^¬9®½“5hÒ¤Iñ/‚fuüøñ‰'FÃa¹+V¬È8‘yx×i‰×?,½®®.ñÊ„T*5Âåfdúo½õVú9×S¦LÙ·o_¨¯¯×çÈ‘#ÕÕÕÉóŒƒø°s2îüÿa£¢¥œ8yšyú*:thÐ}ž+­Îµ¯ÂñÙèéë3¨„…æzå¤1~J®™OÓwΠ[1{I¦pšò¹È0ÎÓïîî¾õÖ[¿ÿýï¿÷býƒŠŠŠôßÈÍó<ý¬émggç 7ÜPYYÍ9º8L°}ûö¦¦¦0ç /¼°££#aCZhÖë¥ör^ŒåöÛo~+8üÛÒÒ’±3¶7Ÿ•OH¥óœ yŸçùô\û0y/å¹Ð!í‡\;3Ÿ"ºÓÆz/Mž<9ã¯áî¨\{GïÐ?tcT•EeÇTÉfúÉ 2ÅV‘\×Óïêêš4iRt¢q®øµ¹¹9¾>~‚¬çé××׿úê«ÇŽ;qòR?±iÓ¦øäúÑ=O?}||Võ°—;0Û s¸þúëÃ@ø7¾ö}ØÞøo!ùÏ3ùnÖóôÃÖ力síó„3гî«!eúy.4×+'×yúÉ;3¡ˆƒnõXï¥9sæ´··¿ï|å•Qù\½@ÿÐQYT•S2}Š¢" ¿‘{ÿý÷ë[ß:‘;~ݵkWCCC˜& @ñ‹_„§üëKüÏŠ®§àÀôëé×ÔÔÄ—>ÏŠ'nnnŽþ°iÓ¦0M4²ªªª»»;ç±”ûzú½'¥_O?¾D{?kÖ¬øêçÃXnÆÒÓWcÚ´i«V­ºõÖ[ã1K—.½óÎ;£äý­·ÞŠ¯ÒžÏ šJ‡jllŒ6¶©©)W kŸglfÆeúî«!eúy.4«øw¢ˆŸ›kgæSÄA3ú±ÞKkÖ¬ Ó§?j·zõj½ÀgWÝ•EeQÙ"'Ó§(*’éÿèG?š1cƉÄÐvÇŽ•••eeeW^yetÍ™÷^âiÏjmm­ªªš8qb|^GGÇ”)S³V®\OÜÞÞ~ñÅ———O:5¾|ÊÓO?]QQ‘k5reúmmm555á‰óçÏïïïÆ÷õõ-Z´¨â¤… Æã‡±ÜŒ¥§O¶jÕªðÄM›6¥O¹téÒÉ“'‡Mž6mZÆ™ÚÉ[4h¦6ª¥¥%¬|ØÞ°Õ¹.ÿ’kŸglfúl³î«!eúy.4«°ÄP»x»Ò'κ3ó)â ™þXï¥cÇŽ¥R©]»vEwÃ@¸?+8ò»ßïú›ïõýCz/€Ï®º1*‹Ê¢²EE¦OQT$=Ó/©lÂ8ýê]»vMž<Ù˾˜-]º4¾ØNhkk˘ «£3˜ßýà¿ãŸõ^Ÿ]tcT•Ee‹„ßÈ¥(ŽF™~ihmmííí}÷ÝwÓ/ÿÂék÷K?Çf¸½ú‘»ݶúèÁÃz/€Ï®º1*‹Ê¢²§Ö»€ÂŸ™~EEŨÏp â™çÊ•+S©TeeesssôËÆ…ßáÃÞ–±Ø·% ëïÿ¿µå×­=óš×>r÷w*?Ù9ç? é´}F—LŸ4Óÿæ7¿ùÜsÏmÞ¼Ù¾‚¢ÅúÑ ûëkoù^ê¶üOÛ`tÉô)(tss+ÛÚ3¯‰ö~gã ë~£H¦O¡}ó›ßüÆû…1Ë—/饗^ýõ;vØEPTº_ÿ§ïTÝØ~Ö{§ê¯-»vÝy7mýÚ²è<ýAÿ Ž0ŠüF.…öÜsÏ-_¾ü©Ù¾ü˜ööö×_}ëÖ­ûöí³‹ xDþÚ\û/iþ®ý‡÷gû7lúßÇþWèÆ¨,*‹ÊŽ® vÃ&Ó($™>Ã'Ó($™>Ã'Ó($™>Ã'Ó($¿‘‹Ã'ÓðÑ@7FeQYT¶J6ÓOŽ™P  Ç²LÀG#Ý•EeQÙÑ%ÓG€±:–K;ÓŸ0átz}òÉ'›šš ¼i£»‹NùÏsŠó…Qœ5¡M›6Õ××g¬Ò©]Ãp”-Y²ÄG#_TQYT•Û¯xjct,Ëô‹Dmmíž={2Æ?óÌ3ÕÕÕeeeynÚP79ž~TöUÁvø7ÿôÊôOmMGhÆŒ?þñGwÅa8ŒY¹reÆ”YÇïÚµ+•J…#ÎG#_TQYT•ïxjct,'dúþ ²²ò¦›nzóÍ73ÆÇ¢ñ===>ø`CCCyyùĉúÓŸfþ”'¿ùOP°u{饗æÌ™3p²°'ã=b âÚ"}úöíÛ“wѰŸ˜Ïssí“ŽŽŽ©S§†yNž<ùùçŸÏU”„Æ ›s"[h>Öë„A—Ó¨¦¹Ö*~=çÚu{)ku²qY×$×A1h‡8¾»»;ì ïÀØ‘é0|£ržþ“O>9}úôŒñnºé¦–––ÎÎÎ\Éþèfú³gÏî9) <òÈ#Ñø0>>žþÒK/V,lK˜¦¹¹9ëüŸxâ‰Y³f:t(L6þüE‹%¯pÖuhmm 3‰Ç‡»ñôóæÍËšZžHû“IºP”úúú!íô æÎ¶%lõ²eË®¾úêä]”çÃú‡'666æÊ³>7×>©­­^xˆ÷v>§™ÇÓ º9'rdúcºž×]w]ž›Sü5͵Vé¯ç„—}B!rq ™þÀƒ"6iÒ¤¬¿ ’u|Ö‹Œ™>Ã7òëé {÷î8>ý<åwß}÷±Ç›:ujeee}}ýâŋØŒù-0w¦¯Ïž={êêê¢áI“&¥Ïuõĉ³Î?¬vüô#GŽTWW'¯[ÖuñO݆T*OèС\›60ÓTTT¬Y³fHKOßEñ)Õé§çÚEƒ>1,bß¾}ÑpÈõ7˜\ÏͺOÂëjÅŠÿAaH™~>/ðzþ⿘2eJ|.ü^íÅYÓ\k•þzNxÙg=~‡qÄ zP¬^½:lÚÀ­È:^¦Œ)™>Ã7òóô»»»o½õÖïÿûãìÙ³gáÂ…ñ%Åób®uȸ›çuâ»7ÜpCeeeôGˆøÚ§O—ëCZ‡A×-2yòä×ÞY³fÍ…^8¼¥ç¹ë²æ¿CzâHêµ}ûö¦¦¦êêê°™ù¿N†´V…\ÏM›6…R†×Ûp>ð5Íóõœu²¬+–ÿ7èAÆgûŽÇškïcÊoä¢@ÀðÊõô»ºº&Mš_#;ÏEgœ{>ŒL¿¬¬,¾’Oooï°ÏÓ¯¯¯õÕW;vâ䥄reša²èRûy½=ç>O?}|Ö–OdûÜöööŒE¤ŸRçÒkó9§;ëÓÏésR¦ŸkŸÄ6mÚ4p’w~òæäùôÑ]Ïõë×744lÛ¶m˜øŠµ¦Ék•ÿË>ëyúùqƒéí"yü+¯¼â7r|QEeQYTvL•l¦Ÿ3¡@@Žåü#÷þûïÿÖ·¾u"ñzúëׯrÿC‡µ¶¶Þpà 3L^\äÊ+¯ ëvüøñÌ;7ãß½' ¼ž~4~Ö¬Yñô555ñõÐÆÄ㫪ªº»»ãÅ-]ºôÎ;ïŒRηÞz+¾ì~®NX‡žžžh²^X|à¢×¬Yž•çnIXú 1n®]”Ï£'655%gµY:pŸ„=]°eÓ¦M¡FY÷Lò«%׿$ïñXÏ•+WÖ××ÿú׿ꋼøkš¼Vù¿ì®O>Gܠ寸°Õ«W¯öÑÀUT•EeÇŽLÆêXÎ?ÓÿÑ~4cÆŒ¹¯§ÿÓŸþ´©©©²²²¬¬¬®®nÁ‚gà#Óß¾}ûôéÓà žyæ™ôL°­­­¦¦¦¢¢bþüùýýýÑø0î–——‡‡ÂñôS¦L‰æ³råÊxüÓO?æ¾è¥K—Nž<9L9mÚ´'Îg¬pÖuèëë[´hQÅI .ŒÇgl`Æ¢;–J¥víÚ•±”\W#ɵôAcÜ\»hÐ'†íjii‰Ÿ˜~®t>ÏͺO¾øâ‹Ã¬¦N_Óf`Q^-¹6'ù¥5ë™ë¸ˆmÙ²%ã¯\§KM“×*ÿ—}Öõôˆ¸'syŽGÙ‡jkóÌwûþÇ!|QEeQYTvŒÈôQ `¬Žå„L¿¨ß'L(½uXºtéÀëÔÕÕ ïâìcj×®]“'Ovp ÉÍ7ß\„¥´¦Åp¬åsP„1¯•”u|8ÊÚÚÚº::C÷ûîoüýÿ죀/ª¨,*‹ÊŽ~h ö(0FDzL¿È×áÅ_¬«««¨¨(†ÝÞÚÚÚÛÛûî»ï¦_\…ÓÚ 5-ÂLàA†Ã˜çŸ>cÊ\ã#o?÷ýÐÃíµ;û×m«<죀/ª¨,*‹ÊŽZhPªæ·8åo´§i¦_ÈŒ»"›¯C‘X¹re*•ª¬¬lnnŽ~8áTí| VÓÒÞáûÖ¿±¶ìÚïV~ò‡Sç~çœ:çü‡äÓö}4ðE•EeQÙÃ7h¦ïææææß¾{Î ÑÀÞïltbo1@V2}†Ïyú¹t¿þOßøéuç~ú½˜þŒk¾[uãý³'úþÇ¡|ú§d%Ó`ødúYEþw*Ož˜挎K¾°guÇÿ>ö¿òïŸö!•ßÈE€á“é ôß_þ‡÷.¶“vbþPûç7¿ùÍçž{nóæÍö'€/ª¨,*«²¤+ÙLßÉM œòcY¦ŒC]¡ûµ—_›qb¾þ à‹**‹Ê¢²£B¦cu,ˤ€ñæÈï~ÿë¶Õÿsw—þ à‹**‹Ê¢²cD¦cu,ˤôO_TQYT•]2}«cY& ø¢ŠÊ¢²¨ìèò¹(0Vo´2)ýÀUT•EeG×»€a“IèŸ@!Éô>™€þ ’L€á“IèŸ@!Éô>™€þ ’ßÈE€á“I蟾¨¢²¨,*[H%›é'MB€Ë2)€bëŸ&øºÃôä“O655òÕë°dÉå_TQYTVeÇ3™> ŒÕ±,Ó(¶þ)ÓžþþþÚÚÚ={öœò5ÙµkW*• ë£(à‹**‹Êªì¸%ÓG€±:–eúÅÖ?ÍôOaè?ìEçùÄ‘lÚK/½4gΜôYeee•••Ó¦MkmmíîîNŸþ¿øÅí·ß^yÒÌ™3ÃÝ0²§§'•J?~<žlîܹñp_WW÷î»ï†9‡ç&¬|ccãêÕ«Dà‹**‹Êªì¸%ÓG€±:–eúÅÖ?eúÃ3gΜuëÖ œÕñãÇß|óÍÇ{,•JíÝ»7¹cÇŽºººU«V;éù矾õÖ['N^9'žOYYY___t7ŒŸ={v4óæææo}ë[¹V¾½½=ý €/ª¨¬Ê¢²ãßÈE€±z£•éŒzÿ̦ã»a`Ù²e©Tª¢¢¢¹¹9NŠûûûÃÝ02<ÔÖÖO¿oß¾ÆÆÆÊÊÊòòò™3g8pàÄN?Ä‹xôÑGkjjÂæÎÛÛÛ›ôÕ"Ç:„ Tâñaú+VÔÕÕ•••e]ô@S§N óŸ]~üøÅ_î†ÃðÔ©S£ßÑ=ñþ‹õ‡)·lÙ’±Ä°&®½0žÉô>™>@ûgYYYt…–'“âaŸ§___ÿꫯ;v, ‡sým L]>¯¯¹ÏÓOŸ~ž~ÆÓóß›6mŠç?póÙ´\{2ë"æÌ™ÓÞÞž°ªK–,Éçzú‘«¯¾úÅ_lii‰&^±bÅ•W^™uæ;vì˜6mÚ‘#GÒG¾òÊ+~#`<ó¹(0|2}€÷Ï+¯¼2Œ?~üøæÎ›q=ýÞ“^O??kÖ¬xúšššøªñ÷ß<¾ªªª»»;^ÜÒ¥Kï¼óÎ}ûö…á·Þz+=˜ÎòÕ"qzzz¢ux‘™¬‹Î*¬@tŸM›6…MÈúÄ<7-מ̺ˆ5kÖ„MHßÒh <ý׿þõc=–þw‹;vLš4éùçŸ?vìX˜ „»é?x»bÅŠúúúuëÖ8Ð×ÖÖFWãɺ[¾õ­o-X° }dccãêÕ«Dà‹**‹Êªì¸U²™~ò×$(À±,Óõþ¹}ûöéÓ§—••544<óÌ3é™~[[[MMMEEÅüùóûûû£ña Ü-//… âé;::¦L™ÍgåÊ•ñø§Ÿ~:Ì!=A^ºtéäɓÔӦMK?W=ËW‹ëÐ××·hÑ¢Š“.\ϯ.z °_|qØœ©S§ÆÆÉxbž›–kOf]ıcÇR©Ô®]»â5„†™<òÈ#ÿ›áÍ7ßœ9sf´É7ß|s|]È»ï¾æý–@WWWNÿMÝ{àÎ;ïŒG†ukïCÀUTVeQÙqH¦cu,Ëô Ö?‡táš±új1¡”/ì¹téÒb¸âMX‡¶¶6Gø¢ŠÊ¢²*;žÉôQ `¬Že™>@Áú§LÀUTVeQÙqB¦cu,Ëô Ö?+** ¶òÙŒî:äZ€/ª¨,*«²ø\«7Z™>€þ à‹**‹Ê¢²£Ë`ødRú'PH2}†O&0FýóÔÞŠ–L€á“é”^ÿ”é@1“é0|2}€ÒëŸ2}(f~#†O¦PzýS¦ø¢ŠÊ¢²¨l1+ÙLßWNù±,Ó8û§Ïi€æŒÊ¢²¨l1“é£@ÀXË2}€Ó±úœhΨ,*‹Ê3™> ŒÕ±,Ó8û§Ïi€æŒÊ¢²¨l1“é£@ÀXË2}€Ó±úœhΨ,*‹Ê3¿‘‹cõF+Ó£þyôàá=ûƒð¯/Q¾¨¢²*‹ÊŽ7ì†M¦PàþÙõÚOÿŸšÏ„ º::OɺÀÿÏÞý@GuÞwÂW¢ª²,cU[¢J­óÇvm‚)CÜ'Iâ"“%Äuµ,»}Y–M7¡ñ›Ú>YL”£Ãë×ËËñëÿ£,ƲYlp¬6¬í6ê&mLxêG†˜?–倫å}ÌÝÜw<®F#Íhfôùœ9>£;wî}îó›ûܹ_®ï0±dúN¦Pšñóì‰7²¢k[Ó á¥ðxõáÕ6`bÉô(œL Øãçñgþë÷~¿ckíÜ0±û¢=qñÇŽíÜ3m&–L€ÂÉôŠ4~~sÕ×þØ=Þ8oë;®®Íò’럜òñbúçeúPÞüF. N¦P¤ñsb 8QEeQYT¶lUm¦ïTD€ ß—eúŸß\õõï,\þÄï|ú©wÏÿîå_ˆ¢öÇk?rø?ý•Þp¢ŠÊ¢²*;™ÉôQ  Xû²L`ìãç/üäGß|âây=W/Þvé'ÄúNTQYTVe'9™> k_–éŒ×øyöÄ›î}ìÙ_]°pýãúÀ‰**«²:Ae''™> k_–éŒûøùÆ ?Ù1ã3a†ã=½ºÀ‰**«²¨ì$ä7rQ  XZ™>@‘ÆÏ³'Þ<¸¾ûô‘7ô$€UTVeQÙɦFP0™>€ñ(%™>…“I?€R’éP8™€ñ(%™>…“I?€Rò¹(P8™€ñÀ‰**‹Ê¢²¥Tµ™~òi ”`_–I?œ¨¢²¨,*;¾dú(P¬}Y&`üp¢ŠÊ¢²¨ìø’é£@@±öe™Àd?׬Y³hÑ¢èùîÝ»g̘QS3g¡ k×®­¶³¸ñëÉÔ2éyp¢ª²¨,*[ßÕŠ´/Ëô&íø9444uêÔÇGÎ;÷ûßÿþ„´¤¯¯¯¥¥%´§ªÎâÆ)ÓO+“ž'ª*‹Ê¢²•ñm°Z7Ìo)(0áZ™>À¸Ÿ5¿ÒÐÐpà 7ìß¿?súâÅ‹â·8p`áÂ…uuusçÎݾ}{QÎ+Þž2oÚ´)4#þ³¾¾¾tg8yw{{ûæÍ›'°¥_EžmH+S¥÷<8QEeU•$jt“é”xüŒcÓsçÎuvvΚ5+múàààÒ¥KÛÛÛ£?ûúúZ[[7nÜ].½wïÞ%K–å¼âíyîâÅ‹·mÛ–ëÕâžád¬«»»{“ëPúUäÙ†´2UzÏL2} 'Ó(ñø™›ÖÕÕeN?}út|]|GGdž òiÒêÕ«›šš¦L™²~ýúhÊÑ£GÛÛÛÂZæÏŸ_ûßÓÓ3sæÌ°Š¶¶¶‡zè|Êÿ%7£µµõøñãqÛR_͵ØðêÆÃÃôÙ³gïÛ·/š~æÌ™åË—7^ž„?³vEôgfK‚þþþ°Øì§CÙ²ï0qݺu---aCÆkLhIè´iÓ¦ÕÖÖfmÀˆëÿ̵ꡡ¡ðg˜^êêêŠçÏÚ™YÛpÇw477‡%,Y²äÔ©S™e*Ɔ'ô<“éP8™>@‰ÇÏÔëô׬Y3{öì´éçßžé755 ŽØž;ï¼sþüù'OžZ½zu4ñª«®êííë ;::¢éS§N600°råÊÌœOùdžÌWs-6̳dɒІðÒºu뮽öuѪU«,X0xÁÂ… ßYט‹gn`®›ÿäÊôÊâ5Æ’Ð’Ûn»-Êó¹F>¡ñYWž¤NçOèÌÔåß}÷Ý¡å¡oÃlË–-‹«–Y¦qßðRÞv `’éP8™>@‰ÇÏÔ«¡[[[_{íµxzô$íÞ;i¡m.Ó§OOþ¡Ôááá)S¦DÏÃzׯ_Ÿz}÷ùÑdú¹æ‰/Óã%L›6-n[xÒÒÒ’u™…eúÙÏ‘jj⎠k ±%'Ožqc:$µñYW ”:=ë*Ò:3õ¥3fÄo?}útSSS®2û†ËôÆßÈE€ÂÉôJ<~Ʊiÿ7ÞøôÓOÇÓ#õõõ©¿‘›çuúYÓØÞÞÞyóæ544DK®­­¦ïÛ·oÑ¢EaÉ—_~yOOOÖ%´µµ¥ÝÔeÄÅæ™Ñg½ÝÐùÄL?´dTw€És#¶$ÿU¼±ùwfªx¶„2ˆ¶çÁ‰**‹Ê¢²y}_œ§I(P‚}Y¦0îãgjlzüøñéÓ§G×¶çÊ‘;::âûã'ÈzþŒ3vîÜyîܹónõ“¹ŠÝ»wÇ—rgþFnwwwÖfçZl®€8¬"õâñø"ñÚÚÚáááèù©S§2ý­[·Žê—Z.WÏÚ’2ý„Æê:ý<;3Ìÿ3Or™ÆwÃGÛóàD•EeQÙ¼¾¯ª= i_–éŒûø™›Þzë­<ðÀùÜ9r___kkk˜'Š}_zé¥ð–Ì¥E÷ÓH½Ÿ~sss|ßüð®x掎ŽèvïÞæ‰&666ö÷÷ÇKÞ²eËÂ… ³6;×bsÄñ­äO:µ`Á‚øfîsæÌ ]4<<–³dÉ’xþ´–ííí›7oÎ~:”û~ú§.ÈzSû´–¤-$³™r5>yÕÑô°êxþ\™Ö†ÎÎΛo¾ùèÑ£áù+¯¼ßv?³Lã»á =NTQYT•-˜Lе/ËôÆ}üLKQŸþù¹sçžO¼6üÀííí µµµsæÌÙ¾}{Ö¥­Zµª±±qÊ”)ñuý===—]vYxWkkë† â™»»»¯¼òʺºº™3gÆ÷Þ¹÷Þ{ëëëS·¥¥¥¯¯/sE¹›+Ó?sæÌÊ•+ë/X±bÅÐÐP4}ß¾}³gÏŽ–sÿý÷Çó§µ$´!´$~WrÆ»ººš››Ãr–-[¿7WKÒ’Ö€¬r5>תÓðgèóðR˜!ž?Wgf¶¡³³³­­-Ì9kÖ¬øÚüÌ2ã†'÷<8QEeQYT¶`2}(Ö¾,Ó˜Ìãgggg9Üw%´¡««ktçH5v–TúU§–i|×^@σU’>òFßwž:óúI•EeUv’ó¹(P¬­LÀøY‘çH“)Ó/ŸµƒUòq¼§7%»ëÿàµîçT•UÙÉû}UP0™€ñ³úÔ××ã¢2•fÕ»áÅó‹Ç¾”áñdãõ?ZvÏÙoê`²‘éP8™€ñJ¬מœò‰'>öV¸ÿÎ÷\½xT—íT:™>…“I? ô¢XÛ¥Ÿˆ®ÙüvÙ>0yÈô(œL  H㧇‡‡‡‡‡Ç¨O^‹ÊV´ªÍô Ð Lø¾,“0~8QeÜEþ óz®þâ“S>ñÃ[þ¬×þç¹ÿ‘ÿqVÚgQÙŠ&ÓG€bíË2)ã'€UÆ×«>]žŸzaþh³÷Üsσ>¸gÏýiŸEe+‘Lе/ˤŒŸNTGÇ{zC™ž¸è£iæ;ÎÚgQÙIE¦ÅÚ—}W0~8Qe¼œ>òÆË]›ÿñÐqÇYì³*;Éù\(ÖÖweã'€Ug±Ï¢²ã«Fà»2€ñg*‚Lß•ŒŸà8 Pdúø® `üÇY€Ê ÓÀweã'8ÎT¿‘‹¾+?p¢ê8‹}•­ U›é'ß(à»2€ñ'ªŽ³ØgQÙŠ#ÓGß•ŒŸ8QuœÅ>‹ÊV™> ø® `üÀ‰ªã,öYT¶2ÈôQ Àweã'NTg±Ï¢²•Áoä¢@€ïÊÆOœ¨:ÎbŸEe+C.Àweã'8ÎT™>¾+?Àq 2Èôð]Àø ޳•A¦€ïÊÆO¨`gÞøå}ýìÀ/Guœýë'žJ}@¥ð¹(P8™€ñÀ‰*¾Žž=W-Žú|޳ÿqÍ·Ÿl»9<ÿáîÐöYT¶²Tm¦Ÿ<|£@@ öe™€ñÀ‰*%pvà—=W-Žcý³ßüê×7Oûä[ó_ýE×éÛgQÙŠ#ÓG€bíË2)ã'€UJ#5Öñ8ûè»nÿ}¢íf¾}•­D2}(Ö¾,“0~8Q¥dâX?‹ÊV(™> k_–I?œ¨RJ™~jeÅúöYT¶:ø\(ÖV&`üp¢JÉd ôÓ*+Ö·Ï¢²U FP0™€ñÊA®@?“X¨t2} '“0~À„Ë?ЈõŠ&Ó p2)ã'L¸.úzþ~$Žõø…;t PYdúN&`ü€ wvà—?ü£½â¾°wL8¿‘‹…“I?œ¨¢²¨,*[JU›é'Ÿ&¡@@ öe™€ñÀ‰**‹Ê¢²ãK¦ÅÚ—eRÆO'ªÕÔ?ãõ8ÞÓëeŸEe &ÓG€bíË2)ã'€ÕjêŸsçV{ŒýzrÛo|âØÎ=>TöYT¶02}(Ö¾,“0~8Q­¦þÇW¦ì¯þD¬oŸEe æ7rQ  XZ™€ñÀ‰j5¼Äñã•é‡ÿíù“'.÷7þÓ¡C‡Nœ8qöìYŸ1û,*›§]ÀX¾Ö'¿*“0~P5/qü8fúQ¬ß=eÞ÷ï{äàÁƒb} 2}Æôµ>ùU™€ñ€ª9x‰ãÇ7Ó_ìøWÝS>öÜÿù XÈŸL€1}­O~U&`ü j^š¡×ÔÔ”ÕªÓzò;þÕã—|ì¹®ïˆõ<ÉôÓ×úäWeRÆOªæà%Ó/F¦á&<ÿZ¬äÏoä¢@À˜¾Ö'¿*“0~8Q­šƒW9§í ‹—µ¶<3ýð8òì¿~ë&«²*;™> k_–I?œ¨VSÿdͬ»ºnhi¹¸¾¾ö¶Ûfž:u{4=<ù“?™ÝØXáI<=Ìß}Ÿœ6­±¶ö5)².ù/þb~kë¥uuï¼æšßzñÅ¥ KÇE…Ç3Ï,ºúêw…-zÏ{š6nüT®åÿÙŸ}¤¹ù¢0Û¿ø»o¾ù§O5ôC˜úäÛßþD®L?ùÑÛÛ+ַϪ, dú(P¬}Y&`üp¢ZMý“5.ÿÜç>ðÿð§áž¬Z57š~ûíþìgßOÆóÿÑ]'àÉ×ñ‹¿Ûßÿ¾ÖÕuÇ>ôî—<^‹š:µá¯þꟇ'ǯ\±bNÖåßyçGÃ{Ã2ÏœYµté¬x¶Ð©RÀÕý¡ŸwìØ!ַϪ, dú(P¬}Y&`üp¢ZMý“5.õÕåÑóŸÿ|ù´iÑóð$üOoi¹8ž¿¿ÿßåyÜøÂù¡¡¯ÕÕ½sÄ%×¢Z[/½ï¾O¾öÚŠ„»è̘1%ÞðÁÁ¯45ÕGϧO¿$µC Ëô7oÞ,ַϪ, üF. ë@+“0~8Q­¦ƒ×ˆ÷‹ãò<§çüøÏ<—<–E½øâÒ[n¹¢©©þø'Ï<³(×BRÕÖ¾c´˜é?üðÃ[¶lyöÙgüã:thppÐÇÏ>«²¤ªÑŒåk}ò«2)ã'UsðÕuú©Ós]M_@Ÿç’Dz¨øñ _Š·(ó:ýãÇWf®k\®Óä‘G}ôÑîîîçž{îg?ûÙ‰'|ü€T2}Æôµ>ùU™€ñ€ª9x废þ›oþûðH½Ÿ~|[ù0ý³Ÿ}®»Þ76Ö=úoGÄçZò8.ê¶ÛfF÷äyá…/57_”uùk×^ÿ™Ï¼ïðážÿ÷ÿþ¯Ã[R—uHXfÁ÷Þyøá‡£;ðüøÇ?>vì˜J¦À˜¾Ö'¿*“0~P5¯¬qù·¿ý‰ææ‹êëk—.uúôW£é§NݾbÅœ01<–/ÿ½xzZƽn݆²ß¹‚ø\KÇE=öØ‚+®øÍººw^}õ»â{ïd.íÚëßóž¦ÚÚw|ðƒ-á-ÑİÐὡOBÏ–éoݺõá‡Þ´iÓ¶mÛ~ðƒ:tÈÇH%Ó`L_ë“_•I?¨šƒ×ˆq¹ÇØ¡ŸüñÔÛïùÚk+Ü{˜äüF. Œék}ò«2)ã'€Õª9xǹ|Úô¬ {®yr-óÅ—ÞrËMMõøÀ?yæ™E2}ì³*;iUm¦Ÿ|š„%Ø—eRÆO'ªÕÔ?Y¯ÓõÕì×éœéçZfüxá…/…ydúØgUvÒ’é£@@±öe™€ñÀ‰j5õOf½jÕÜÏ}îÿðúæ›ÿþ³Ÿ}êýô Îôs-ó¶ÛfF÷äyá…/57_Mll¬;zôßÊôí³¨ì¤"ÓG€bíË2)ã'€ÕjêŸÌúÔ©ÛW¬˜S__Ë—ÿÞéÓ_{¦Ÿk™=¶àŠ+~³®îW_ý®øÞ;ëÖýa˜-uQ5o'Ó·Ï¢²ÕG¦ÅÚ—eRÆO'ªÕÔ?zå»Lß>‹ÊV¿‘‹Å:ÐʤŒŸNT«éà%p—éÛgQÙrP£ Ë×úäWeRÆOªæà%p—éå@¦À˜¾Ö'¿*“0~P5/»L(2}Æôµ>ùU™€ñ€ª9x Üeú@9é0¦¯õɯʤŒŸTÍÁKà.ÓÊßÈE€1}­O~U&`üp¢Z5/»Lß>‹Ê–ƒªÍô“O“P  û²L Àø àDµšúGà.Ó·Ï¢²å@¦ÅÚ—eRÆO'ªÕÔ?¥yÈôí³*«’ÉôQ  Xû²L Àø àDµú<÷ÜsÝÝÝ>úè#<òxEغõñJ#ӷϪ, dú(P¬}Y&`üp¢Z}z{{wìØ±eË–G*Ä}«î|¤…ýzûСC>uöY•%•ßÈE€bheRÆO'ªÕgïÞ½Ï=÷ÜöíÛ·lÙòh%ø_»ëÑJú6ôpèçÐÛÇŽó©³Ïª,©jt“I?˜l<øãÿø¹çžÛ±cG7Åú6ôpèçÐÛ'Nœð©RÉô(œL Àø ÀdsìØ±ƒîÝ»···÷9Š#ômèáÐÏ¡·}ê€T2} '“0~0Ù ž8qâØ±c‡:Hq„¾ =ú9ôöÙ³g}ê€T2} '“0~¥ä7rQ  p2)ã'€UT•EeK©j3ýäÓ$(Á¾,“0~8QEeQYTv|ÉôQ  Xû²L Àø àD•EeQÙñ%ÓG€bíË2)ã'€UT•EeÇ—Lе/ˤŒŸNTQYT•_~#Šu •I?œ¨¢²¨,*;¾jt“I?€R’éP8™€ñ3—5kÖ,Z´(z¾{÷î3fÔÔLÀÙGhÃÚµk«í,nüz2µLz 2¾ ê &Ó0~f5444uêÔÇGÎ;÷ûßÿþ„´¤¯¯¯¥¥%´§ªÎâÆ)ÓO+“ž¨Œoƒº€‚ÉôJ<~ÖüJCCà 7ܰÿþÌé‹/ˆßràÀ… 666ÖÕÕÍ;wûöíE9¯x{ʼiӦЌøÏúúúÒádäÝííí›7ožÀ”~y¶!­L•Þó“„ßÈE€ÂÉôJ<~Ʊé¹sç:;;gÍš•6}pppéÒ¥íííÑŸ}}}­­­7nŒ.—Þ»wï’%KŠr^ñö k_–éŒûøǦýýý7ÞxãÓO?OÔ××§þFnž×égMc{{{çÍ›×ÐÐ-¹¶¶6š¾oß¾E‹…%_~ùå===Y—ÐÖÖ–vS—›gFŸõvCç3ýÐ’QÝ&Ï5ŽØ’üWQðÆæß™©âÙÊ4.>Úž'ª¨,*‹Êæõ}µZ7Ìo)(0áZ™>À¸Ÿ©±éñãǧOŸ]Ûž+GîèèˆïŸ ëuú3fÌØ¹sç¹sçÎ_¸ÕOæ*vïÞ_Êù¹ÝÝÝY›k±¹â°ŠÔ‹Çã‹Äkkk‡‡‡£ç§NJÈô·nÝ:ª_jM¸\=kK Èô?ªëôóìÌ0[üÏ<Éeß mσUT•Eeóú¾ª (˜L ÄãgZlzë­·>ðÀçsçÈ}}}­­­až(ö}饗Â[2—ÝO`` õ~úÍÍÍñ}óûâ™;::¢ؽ{w˜'𨨨Øßß/yË–- .ÌÚì\‹ÍÇ·’?uêÔ‚ ⛹ϙ3'tÑððpXÎ’%KâùÓZ´··oÞ¼9ûéPîû韺 ëMíÓZ’¶ÌdÊÕøäUGÓêãùsufZ:;;o¾ùæ£G†ç¯¼òJ|ÛýÌ2ï†'ô<“éP8™>@‰ÇÏ´õù矟;wîùÄkÃ8ÐÞÞÞÐÐP[[;gΜíÛ·g]ÚªU«§L™_×ßÓÓsÙe—…wµ¶¶nذ!ž¹»»ûÊ+¯¬««›9sf|ï{ï½·¾¾>õ'|[ZZúúú2W”k±¹2ý3gά\¹²þ‚+V EÓ÷íÛ7{öìh9÷ß<ZKBBKâw%÷g<±«««¹¹9,gÙ²eñ{sµ$m!i È*Wãs­:< †>/…âùsuff:;;ÛÚÚœ³fÍŠ¯ÍÏ,Ó8nxrÏCQ>òFßwž:óúI]@U’éP8™>€ñ3—ÎÎÎr¸ïJhCWW×èΑj&ì,©ô«N-Óø®½€ž‡qt¼§7Œ¥O6Î{ã…Ÿè ªŒL€ÂÉôŒŸUxŽ4™2ýòY;Œ»_<ö½0œ†ÇSïžÿ÷ßÞtöÄ›ú€*ù¾Z­æ·(™€ñ³úÔ××ã¢2•fÕ»áP&úwý¨û¢]¸`ÿúî_¿nÏgoO¾l߉jµRY•Ee«LÕfúɧI(P‚}Y&`ü˜Xq¬=ž¼äã —í;Qœ‡]T•­82}(Ö¾,“(xüôðððð(öãµ'žq,*‹Ê–!™> 1“’é?&PÖëôöŒ®Óq4Ö“á°‹Ê¢²G¦ÅÚ—eRÆO€ ô¶ûé_ô±Üôå0åžûùÆúp2vQYT¶âø\(ÖV&`ü˜(¿xì{ѵù©æv4¾çž{|ðÁ={öèÏŠ&‚PYT¶ÊÔè &“0~”¡ã=½a,}¢þÒ.Ì7PdúÎY€ñ Üœ>òÆË]›ÿñÐq£1UI¦@áœ?ŒÆPJ2}œ?ŒÆFc*ƒßÈEgAÆO£±Ñ¸j‰ T•­2U›é'˜Q `B΂Ξxóð_>þ«ëãç¨ÆOŒÆ©Ð¨,*[qdú(Pг {~ºã·?¦ïéÕoR$£1eRhT•­82}(âYÐ7W}ý; —?ñÛóÃóðè{ðip^Š`4¦l Ê¢²G¦EÙ—OìùixuKÝG¶L™·õ~¼î#GŸúÈç» ÀhLÉ Ê¢²Çoä¢@ÀxhÏžxóÀ½=ó¾öm¿qCtmþ[ºëúùŸ¨„Wï¹çž|pÏž=ú `Gc™~uA¨,*[ejtc9 òðððð(Æã¾ûîëîî~ñÅkŠúm6ùU™>eH¦À8¸çž{îºà›«¾þÿ·ÛþSãÇûµ¹q2õøE;¶Ó¥¦Iãg,L¹ï¾û6mÚ´k×®è"€â‘éP‰dúŒƒ|ð¾·{äkkÿ½/n­ûxí[áþÏëä3~†)ÝÝÝ»vízñÅ=ª‹ŠG¦@%’é0öìÙ³ëí”_|qÿû»ÿvÇúí-7Fì¿úðN}ÏøyàÀ£Gê"€â‘éP‰üF. ”b×~ã…ŸüÊôgQYT¶<ÉôQ À® `ü0üfyU¦ï8‹Ê¢²eH¦vmã'€á7Ë«2}ÇYT•-C2}°k? ¿Y^•é;΢²¨lò¹(`×0~LF2}ÇYT•­D5º˜„dúT"™>0Éô¨D2}`2’éP‰dúÀd$Ó ù\°kE·bÅŠ÷ÛûšZt0QfÍšuýduKÍoß•[xõw÷w¯¸âŠk®¹f2ôÆ}÷Ýç<•Ee+BÕfúÉÿØŽvm ”Þóž÷Ôeìãÿ¸óT•­2}°kEgú«(ÑÈÜÖÖöñÉê–šß¾;·ðê‡>ô¡ßÿýߟ;wnu÷ÃoüÆoÈôQYT¶‚ÈôQ À® ]”é¯^½ú—”(Ö¿ë®»|}Íõjww÷sÏ=wðàÁêî‡ë¯¿^¦Ê¢²D¦vm èdú2ýŠûú:Ù2ýk®¹¦··wïÞ½a{;688è<•EeË“ßÈE»6Pt“-Óng!.÷Þ›$[æ›™Ú¼šZ¶[79w[™¾L?eúW]uÕŽ;ÂöþøÇ?›|âÄ ç)¨,*[žjtÅ6±™~飺<×(úŸ ™þˆmΜ¡°Í,Yç”U¦_ðÒÆ«Û+}à’éËô#Q¦å•WnÙ²eûöía“÷îÝ{ìØ1_`Ê“L€¢“éËôeú2}™~tùòå2ýäW'U¦ÅW<òÈ#[¶lÙ±cGooï¡C‡|(O2}Š®€L?ʳ6lØð¾÷½¯¾¾~úôé÷ÜsOê þç>cÆŒºººðRxžöÆ®®®–––ÚÚÚš·Ë?AË .þ /…Ö¯_Ÿ¶¨oûÛáÏИK/½téÒ¥¯¾új¼Ì̆…E½÷½ï ‹š6mÚ]wÝUXÿ¤uBra9üࣗBûGlÌw¿ûÝk¯½ö¢‹. /Í›7ïé§ŸNžž¼]ɽ—ÏVïÛ·/læÅ_Üßß϶=Ìüâ‹/f.'kE"ßúÖ·“k+↵µµ…W¯¸âŠžžž„*§½1m†\K+à㔹®´OEB]rõRfSGÕÿÉ[—ëSšõ£•u«>„céöqÙ1‹1pÅOúÓhù2ý„W'Û½wyä‘G}t’l5@å’éPtgú ,8räȱcÇ>ÿùχ?ãtìÏÿüÏß_ùÊWÂó¯~õ«áyœŽEo\¶lÙÀÀÀX®šÏ ûr5&Š>£Wƒð$mQßüæ7£dóÑG Óÿøÿ8WÜaÊýÑõ‹_üâõ×_ÿò—¿ó™°üСý©IØŠh¶¥K—†VÝÖÖ6Æëô³.mT§¬+JýT$ÞòYQü<ÿþOغ„Oi®VæVçšs,Ý>^;f1®‚ýìg?kmm «˜1cÆd¾[¦‘éT¿‘‹vm è Îô_~ùåèÏ„?ßûÞ÷FNŸ>=ü]8ü‹_ü"<SRßxìØ±î„S__ŸéçjL[[[ê«áIr*}É%—äjX±E‹H sóïŸÌNH豨ýû÷ïO[EBc.¾øâºººüà'OžL}K®é ‹mïåÚê={ö„ç×\sMôÒ—¾ô¥ðçw¿ûÝ|êW$­+Â“ÔÆ$lE4[èØøÏøBø‚3ý¬KÕÇ)ëŠR?£ú¼e]Qü<ÿþOغ„Oi®VæVçšs,Ý>^;f1®1ú¡d¯¿þúd><Éô#UŸé;OqŠÊV™ªÍô“Ì(`×J©àL?mJ]]]®kês½4Ž™~žI›òÝï~÷ºë®»è¢‹Rï§‘üÆT anÁMqæ³~ýúÐWÑ”k¯½6¾½I®é ‹U½’·:ʤžþùÐáüà³.¤€ŠŒv+FuÇù|ŠUØÇ)ÏeݢɼN?ëV”,ÓÕÇiÄ%lQž+Jû÷†|ú?aë>¥¹>ZY·:ׇ³àn¯³Wþe—]ý¯“ü ý|¾¾Êô§ ²¨ly’é£@€](º‚3ýÏþóGŽyýõ×o¹å–ðç·¾õ­èÕ¯}íkáϯ~õ«áùW¾ò•ÌÛR§Ý‹#L9pà@ò£ ص¢+8Óß°aÃ>ðÚÚÚiÓ¦¥F·Ñ/LF½NŸ>=ÎŲFc]]]—\rɈ½öôô\qÅa]mmma½™ÁeBcŸabx)Ìpÿý÷§¾wçÎÑbg̘±nݺ´9³aëׯ¿úê«Ãüõõõ7ÝtSòCr5)ëÆæê± ´ùƒü`XNKKKhÀˆÙ¶mÛ¼yóê/¸îºëvíÚ•<=y»zo´ŸŠè×\ƒ»îº+W¡*5&ôOjcRï²’k+âà?~™3$,m´§¯ûεE +Jhj>ýŸ°u ŸÒ\­Ì­NøÜíãµccà*àúýü¿¾Êô§ ²¨lyò¹(`׊n\î§?ʪ1eÛ¤2q饗^|ñÅGŽû¢öíÛ:ù}ï{Ÿ^þ¯De»cÆþW\ñòË/;*åÉô§ ²¨lyªÑ›L_¦_Û·oÝß>¨·ÜrËßüÍßDwö¿é¦›î O1ú_¦_ ñâ¶µµ9rÄ!)•L?Rõ™>@•‘éPtQ¦Åúyª½`uÔf3QãV¬&Å%—\úäw~çwþôOÿ´à…´··¿ûÝïËùµ_ûµ¶¶¶ð§Ž-eÿWº2Ü1ãŸC˜1cÆ¡C‡ÒÈô#2}€Ê"Ó èâL€Òkkk{å•WŒ2Éô#2}€Ê"Ó èfÍš…JwPr®ÐÏE¦‘éT¿‘‹vm è¢°àãÿ¸®0~”™~êaÚo䢲¨l¥¨ÚL?ùÀŒvm ôaLßø PAïLßq•EeË“LìÚ@‰Â™¾ñ ‚†_™¾ã,*‹Ê–'™> ص…2}ã'@ ¿2}ÇYT•-O2}°k% dú“aü …N{Rn «ˆÞ›À–OG%·d÷îÝ3fÌȳµ£Ý¨QÍ_=¶fÍšE‹Uî'¿”BG­]»6ÏáW¦ï8‹Ê¢²eú¥±Z7Ìo)(`×Ê-,éO†ñ³Ê2ýoÅx­n,U(Ÿ &7`îܹßÿþ÷‹´-%ÈôDZ{‡††¦NzøðáŠþäGJ°ð¾¾¾–––ÐiÑŸ2ýÔôßÈEeQÙŠùÊ­ (6™~‰¾ÜÿJCCà 7ܰÿþÌé‹/ˆßràÀ… 666ÖÕÕÍ;wûöícoCÚ“"mi Þ2!ß­(^G• ?“WQ___¼~¨¬ëô7mÚöëêþä}i©ÛÛÛ7oÞ=—駦«8Ó¨¶¯ýº€b“é—èËý¯B«sçÎuvvΚ5+múàààÒ¥KÛÛÛ£?ûúúZ[[7nÜ]²ºwïÞ%K–ŒWdúÅØ.™~a ¨îLñâÅÛ¶m«îOþØ—–:±»»;þW™~êaZ¦P1_ûuÅ&Ó/Ñ—û·'Yuuu™ÓOŸ>_àÜÑѱaÆ|–¼zõꦦ¦)S¦¬_¿>šrôèÑööö†††°–ùóçÇ×þ˜ég>44ÖÒÒÒÕÕ•:ÏwÜÑÜÜ^Z²dÉ©S§Î§üoÉ9`þË ¦N:88Ϧ„w…)©ïÊì„¬Í zzzfΜ&¶µµ=ôÐCãÒȬžòoR>ïͳ ÍÛ¸qckkk˜>{öì}ûöØ™LmÀ¨>YK–ÿªËð£zòøñã•òÉ1pþ\·n]hIXTØ–3gÎDÓÓåË—7^žÄÓÃü¡mÓ¦M«­­ÍõáLý³¿¿?tZô\¦Ÿz˜–éTÌ×~]@±ÉôKôå>å:ý5kÖÌž=;múù·gúMMM©q^.wÞyçüùóOž<944´zõêhâUW]ÕÛÛ;<<Ö&vtt¤­kT™~XÂÂ… O:ÚÓÞÞÏs÷Ýw/X° ¬:¬eÙ²e+W®L^xæ2/O’—ùå/¹««+~ox~ûí·§®(k'äjÞÔ©SwíÚž ÄÇÞÈÌ Ï¿ ù7)Ï÷æS‚„æ-Y²$l]xiݺu×^{mroŒøAÊ¿ruì¨V]n­ø_ï*ⓟg¦Ÿº ñzW­ZÖOÆóßvÛmñ?-䳊x”駦eúóµ¿Z7Ìo)(`×ʇL¿4ãgꪭ­­¯½öZ<=z’vï´(0—éÓ§çúùÍÈððð”)SÒÖ5ª›iL›6íèÑ£Ñóð$~ïŒ3â­8}útSSSþ ÍŽßÚŸ¼ÌW^y%¾n7¸ì²Ë¢öÄïÊÚ ¹šµ~ýúÔK§Ç¥‘Éž\…ü›”ç{G{³”´æÅWX‡éñç0Wo$·sTý«cGµêrûh¥íÈñÉO®fø3uÂøqc“–––xþ“'O&6dúù¦ýF.*‹ÊVŠªÍô“Ì(`×JÈô‹=~Æ¡Uÿ7ÞøôÓOÇÓ#õõõ©¿‘›çuúY²ÞÞÞyóæ544DK®­­M›y,w$O]HªÌµŒã2?ýéOG?þ»téÒ|6'×¢öíÛ·hѢн—_~yOOOñ6<ÿ*Фä÷æS‚›—ÜÉùgúù÷Cž;ªN˜ðV[[[jŒ^Ÿü<»4’õ6b ÓGÌôCw¹÷NÖÃtgúÎS|ƒBe«ŒLìÚ@‰Â™~±ÇÏ´ÐjúôéÑÕйÊŽŽŽÔdç’ëBÝ;wž;wîü…[ýd Oóoyêuú¯½öZê•Åñ¿@äÚÒ„fçºZ9ë2wïÞýÑ~4< ÿݿڊruBÖE¥.3¾Äx\™¶áTaÄ&åùÞ|J0bó2;¹°ëôóï‡\;^×éOÈGkñâÅÝÝÝ•õÉOþ`'\§Ÿ:=õ:ýLJԉ[·nõ¹YÓ2}T•­2}°k% dúÅ?Ó’¬[o½õ8Ÿ;~íëëkmm óDa芧“LãIDATK/½Þ’¹´è†Ú©7ÔnnnŽoœÞ•¦Ÿë~úíííÑv.Z´(ž§³³óæ›oŽâþW^y%¾OzcccroÄ÷è,X0â2ƒY³fmܸñÆo̳r-*<‰bÐÝ»w‡ŽÇF¦mxþUÈ¿Iy¾7ŸŒØ¼´?sõƈ¤üû!WÇŽjÕåöÑÚ²eKhO¥|òó¿Ÿ~´ ©÷Ó* Ú´Ôûé§¾=ë‡3už0ÚlÞ¼9ŸáW¦ï8‹Ê¢²åI¦vm DaL¼ÆÏ³'Þ<ü—φÿ¦¹{°õüóÏÏ;÷|â%Õhooohh¨­­3gNt ŽÌ¥­Zµª±±qÊ”)ñuý===—]vYxWkkë† ƘéŸ9sféÒ¥uuuÍÍÍ]]]©÷ïììlkk +š5kV|1ò½÷Þ[__Ÿ¼ -[¶,^fêÌY—lܸ1,v÷îÝyvB®E…'W^yeXõÌ™3“ï@2ÚF¦mxþUÈ¿Iy¾7ŸŒØ¼´?z#ùƒ4ªOcÖŽÕªËí£uîܹ–––¾¾¾Šøäç™é‡–‡ö‡V…m [+W®¬¿`ÅŠñô´·gýpÆ†Ž Ý¿W¦Ÿz˜–飲¨l¥ð¹(`×JÈôÇ>~žØóÓ3>ÎsŽ÷ôVkoôõõµµµùT@þ:;;ã›ÉT¢QýžÁW:ª««+ž.ÓO=Lû\T•­˜C§. ØdúctöÄ›î}ìÙ+¿°µæÚðøù_l¯¾m\µjÕ©S§~ùË_¦ÞR˜ ê%ús|3ý´…§‘駦«8Ó¨22}ŠN¦_°{~ú£Žo>yÉÇw¶-èþõ?x¼vî‘m/Tå–nذ¡¥¥¥¡¡¡££#úiß<ÕgSn[W†Ç&uë&°ë*â£U•JÙÏ2ýÔôL RÈô(:™þhÅæoŸúÏžú­›¢Ë󯻮Z}€ !ÓO=LËô*…L€¢“駉2úÝ¿þyÎéááááQØ#y¬–éP†üF. ص¢“é§1E:{âÍÿ÷ÿ¸û?ùäãµs•ï_÷DãõÇvîÑ{%eúÎSPYT¶ Um¦Ÿ|`F»6Pú°@¦Ÿç€™újÿùÛç¯ÿߣdÿñ_ûˆX ”£±Lßy *‹Ê–!™> ص…2ý<ÌðêŽ;z{{:M‰/Û®Ùõáú £±Lßy *‹Ê–!™> ص…2ý<Ì„©ÿ¿üíöß|+Ù?ÞÓ«&j4®ÊôL•Ee+…LìÚ@‰Â™~žæˆ)ÒÙo\ß}úÈz`Gã*;LËôQYT¶Rø\°k% dúyž¨Lž Àh\>‡i¿‘‹Ê¢²•¢FPl2ý4R$£q¹¦«8Ó¨22}ŠN¦ŸFŠ`4.·Ã´L RÈô(:™~)€Ñ¸ÜÓ2}€J!Ó èdúi¤HFãr;LËô*…ßÈE»6Pt2ý4R$£q¹¦ýF.*‹ÊVŠªÍô“Ì(`×JÈôó0eúFãÒ¦«8Ówžâ •­22}°k% dúy˜2}£qéÓ2}T•­2}°k% dúy˜ÅK‘Bt~aÖ¬Y³hÑ¢ l@XûÚµkªc4.ÏôL•Ee+…LìÚ@‰Â™~ž¦L¿Ü M:õðáÃ؆¾¾¾–––Ðå€*Ëó0-ÓGeQÙJá7rQ À® ”(,éçy¢2™þ†þ¯:Ï7ŽeÓ6mÚ´xñâÔEÅ.\ÇýY×OÌ|’6[P[[–9kÖ¬U«Võ÷÷§ÎÐÞÞ¾yóf»TÁh\ž‡i¿‘‹Ê¢²•Âu:L?LW]‚LñâÅÛ¶m˺¨Ó§Oãߘ5kVÂZòÏô£'ÃÃÃû÷ï‹miiyíµ×â§"õŸ€ÊËó0]Å™>@•‘éPt2ý4§HiYpjF¼nݺ–––úúúŽŽŽ3gÎDÓ‡††ÂŸabx©««+žÿèÑ£ííí uuuóçÏ8ÿöËÏãUÜqÇÍÍÍa K–,9uêTÒ©EŽ6„'Ë—/o¼ <‰§‡ùׯ_?mÚ´ÚÚÚ¬«ÎÔÓÓ3sæÌ°ü¶¶¶‡z(k›óÜ´\=™¹Š µµõøñ㹪„ùs½t~ô™~¬³³3ôdügh‰ÝÊa4®ÊôL RÈô(:™~šbdú .¼ »wï›õynZ®žÌºŠ-[¶„MHÝÒøù¹sçÖ®];{öì¸ÇæÎûw÷wÑŸ{÷î½îºëÂÄ´7&gú¡U/¿üò7¾ñÔ ÚÛÛ7oÞl÷r«ò0-ÓGeQÙJ!ÓG»6P¢°@¦Ÿç€™"íÛ·oöìÙµµµ­­­÷ßjFÜÕÕÕÜÜ\__¿lÙ²¡¡¡hzxþ¬«« /…âù{zz.»ì²h96lˆ§ß{ï½a ©‰sggg[[[˜sÖ¬Y©×ªg9µÈц3gά\¹²þ‚+VÄÓÓríÌUg ¸òÊ+ÃæÌœ93¾1NÚóÜ´\=™uçÎkiiéëë‹[ ˼ùæ›ã—‚õëׇ÷Ö]ž„?S»(s ‘Ô‰a™¡m«W¯NýŸ$Â*BâÞ&v4®ÊôL•Ee+…LìÚ@‰Â™~žf)Ò¨n\S¬S‹šš*.YggçÄÞ÷&¬½««Ë¾e>WôaZ¦Ê¢²•Âoä¢@€](QX ÓÏóDE¦P¡£qE¦ýF.*‹ÊV _»(:™~šqO‘âßh-úlÆ· ¹VPæ£qE¦«8Ó¨22}ŠN¦ŸFŠ`4.·Ã´L RÈô(:™~)€Ñ¸ÜÓ2}€J!Ó èdúiFL‘&ö¡@€Ñø¼L_¦P®üF. ص¢“é§)çI¦Ëa4.ýaÚo䢲¨l¥¨ÚLß©ˆvm Ü™~ž¦LÀh\úÃtgúmÎ@QÙ*#ÓG»6P¢°@¦Ÿç€)Ó0—þ0-ÓGeQÙJ!ÓG»6P¢°@¦Ÿç€)Ó0—þ0-ÓGeQÙJ!ÓG»6P¢°@¦Ÿç€)Ó0—þ0-ÓGeQÙJá7rQ À® ”(,éçy¢"Ó0—þ0í7rQYT¶RÔèŠM¦ŸF¦`4.·ÃtgúUF¦@ÑÉôÓŒ1E:{âÍÃùløoéÛ`4®ÖôL RÈô(:™~š‚S¤CíüÏM7„Ž÷ôNHÛŒÆÕz˜–éT ™>E'ÓO3Úé¿ÞûÅo&nkºá‰‹çÛ¹gÛ09GãÉp˜ö¹¨,*[)ª6Ów*¢@€](·°@¦Ÿç€ùVŠ´aãS7ü«î‹>]˜OýÖMO^ú‰búsÀhü¶ÑX¦ï<•EeËLìÚ@‰Â™~žfœãOÔC£ñy™¾L•EeË•LìÚ@‰Â™~žæ[)Òƒ>óo¾ùÔeŸÛñ;7ïúÈ¿|²ñÂôß9·ïÁ§õ@éFc™¾óT•-?2}°k% dúy˜©)Ò/üäGß|²ñú]þO½ûSb}€ 'ÃaZ¦Ê¢²•Âoä¢@€](QX ÓÏóD%3E:{âÍ÷>öì_ˆîspýãú ô£qu¦ýF.*‹ÊVŠ]@±ÉôÓœ"½ñÂOvÌøL˜áxO¯n˜¨Ñ¸*ÓUœéT™>E'ÓO3Æéì‰7®ï>}ä = 0£q•¦eú•B¦@ÑÉôÓH‘ŒÆåv˜–éT ™>E'ÓO#E0—ÛaZ¦P)üF. ص¢“é§‘"Ëí0í7rQYT¶RTm¦Ÿ|`F»6Pú°@¦Ÿç€)Ó‹ÓGÞèûÎSg^?i4ÕaºŠ3}ç)Î@QÙ*#ÓG»6P¢°@¦Ÿç€)Ó£ã=½o¥¿~ÝÏÿŸÿl4Îó0-ÓGeQÙJ!ÓG»6P¢°@¦Ÿç€)Ó»_<ö½0œ¾5¢ÖÿÁýüê<üºÑ8ù0-ÓGeQÙJ!ÓG»6P¢°@¦Ÿç€^ݱcGooï¡C‡ô@ÁúwýèÉK?±­éo…ûï¸vç?ý\Úeû2ýÔôL•Ee+…ßÈE»6P¢°@¦Ÿç‰Jxõ¹çžÛ»wï±cÇôÀXD±þS-7F×ì§]¶/ÓO=Lû\T•­5º€b“駉£%?ž~÷üèÉkO<'ÓO=LWq¦PedúL?—ÞÞÞçÞ.LÙ»wïÁƒ;688¨‹Æâðæž­ïœ»µæÚ'çíºö_qÑGÓ.ÌO#ÓO=Lû\T•­U›é'˜Q À® ”>,é?Jàô‘7^îÚü‡Žqø•é;΢²¨ly’é£@€](QX Ó7~TÐð+ÓwœEeQÙò$ÓG»6P¢°@¦oü¨ áW¦ï8‹Ê¢²åI¦vm DaLßø PAïLßq•EeË“ßÈE»6P¢°@¦oü(+2ýÔôßÈEeQÙJQ£ (6™>eH¦Ÿz˜®âL ÊÈô(:™>eH¦Ÿz˜–éT ™>E'Ó  ÉôSÓ2}€J!Ó èdú“裦&íI)WZeë߬Y³fÑ¢Eå¹QU)ôöÚµkõC™“駦eúó•»Z7Ìo)(`×ʇLòŒŸ2ýb·°°š:uêáÇËs£JY¾H Þ×××ÒÒzÞWÎdú©‡i¿‘‹Ê¢²ó•fr˜Q À® ”>,é{üŒ#ņ††n¸aÿþý™Ó/^<00¿åÀ .lll¬««›;wîöíÛÇÞ†´'¥8«i]ňÔ'ì.¿¤Í¶iÓ¦Pú²Ý¨rë½±,-ub{{ûæÍ› q•;üÊôgQYT¶L¿Ò¨= ص҄2ýbŸq˜xîܹÎÎÎY³f¥M\ºti{{{ôg___kkëÆ£K‰÷îÝ»dÉ’ñjƒL¢66ël‹/Þ¶m[ÙnT¹õÞX––:±»»;áŸR(ÿáW¦ï8‹Ê¢²eú•FíQ À® ”&,é{üLKëêê2§Ÿ>}º¾¾>zÞÑѱaÆ|–¼zõꦦ¦)S¦¬_¿>šrôèÑööö†††°–ùóçÇ×þ˜éƒ¦-dݺu---¡Í¡µgΜ‰¦ …?ÃÄðRWWW<ÖVÕ¤ˆWqÇw477‡%,Y²äÔ©S Ûžk]™ ¦N:88Ϧ„w…)©ïÊìÌQµ'³‡{zzfΜÞÛÖÖöÐCeÝäÖÖÖãÇ—ÃFe¶6ÏÏ󈟊Â>iYž,_¾¼ñ‚ð$žæ8mÚ´ÚÚÚ¬Ÿ«´Uô÷÷‡ž7ÄUîð+ÓwœEeQÙò$ÓG»6P¢°@¦_ìñ3õ:ý5kÖÌž=;múù·gúMMM©Ym.wÞyçüùóOž<944´zõêhâUW]ÕÛÛ;<<Ö&vtt¤­k¼2ý… ^žÄkOR§ÇóتÈÝwß½`Á‚°Ea¶eË–­\¹2aós­+ëB¾üå/wuuÅï Ïo¿ýöÔdíÌQµ'³‡§Nºk×®ðd`` ~o®à™ðÊÚÚ|>*#~* û¤e]ÔªU«BããéáÏxþÛn»-þ÷‰|VïnTâð+ÓwœEeQÙòä7rQ À® ”(,é{üL½p¸µµõµ×^‹§GOÒ–óæ2}úô\¿­ž2eJÚºFu““„ô6ފІiÓ¦ÅMJžu]Y[™1cFüöÓ§O755%o~Öue]È+¯¼’z]öe—]vôèÑÔdíÌQµ'³‡Ãׯ_Ÿzþù‘2ý ܨ¬­—OEaŸ´¬‹ Oâ- OZZZâùOž<™kiY'ÊôËœL?õ0í7rQYT¶RÔèŠM¦_¢/÷¿ ûûûo¼ñƧŸ~:ž©¯¯OýÜ<¯ÓÏ\öööΛ7¯¡¡!ZrmmmÚÌã•é§NÏz7¡Ô?GlUZ‡¤Í6Ú¶e]ȧ?ýé视×.]šO·ÖžøÉ¾}û-Zªyùå—÷ôôd]Q[[[jŒ>•µµãò©Ë'-ù6â/×ÄÐçî½Sædú©‡é*Îôªík¿. Ødú%úrÿö0qúôéÑ}ÀsÅë©w?Ï%×UØ;wîw¼Þ×××ÚÚæ‰Bð—^z)¼%siÑÝÒRï–ÞÜÜß=¼+ÿL?ëô9sæÜwß}ÃÃÃaiK–,I»szôK­™÷Ó¦/X° ž?W«ûûûãÕuvvÞ|óÍÑ d^y啸¶ûYåZWÂBfÍšµqãÆo¼1ÏÎU{2{8ÌEê»wï=u“·lÙ¶¢6*kk³í§"ŸOZÖûéçú€ Fý“z?ýÔ·§uræ<ííí›7o64•3™~êaZ¦P1_ûuÅ&Ó/Ñ—û·ŽÏ?ÿüܹsÏ'^2àÀööö†††ÚÚÚ9sæD÷WÉ\ÚªU«§L™_×ßÓÓsÙe—…wµ¶¶nذaŒ™þ¾}ûfÏž-íþûïO]HWWWsss}}ý²eˆ††¢éáIø³®®.¼fˆçÏÕª{ï½7,!uÕmmmaÎY³f¥^Àž)׺²qãÆ°ºÝ»wçÙ™£jOf‡ù¯¼òÊЙ3gÆw³IÛäsçε´´ôõõMøFemmV£ýTäóIËÌô³.êÌ™3+W®¬¿`ÅŠñô´·g~®Rç ½ú[:Žý G¦Ÿz˜–飲¨l¥é£@€](QX Ó/`ü<ôÐÎg¯üÂÖw|8̳­é†'.žwlç=P‚¯¯2}ç)¨,*[ždú(`×JÈôó?Ó.̧~ë¦'/ý„@ d__eúÎSPYT¶<ù\°k% dú©'*ù<žz÷§òœÓÃÃÃã°GòX-Ówž‚Ê¢²eÈONPt2ý4#¦HgO¼yàÞÇž½â ;~çæ]ù—O6^¸`ÿsû|Zï”l4–éP†dúL?͈)Rüü~ò£Žo>Ùxý®ÿ‹·.Ûë”p4–éP†dúL?͈)ÒŽ;z{{:M‰/Ûîqpýãú £±L€2$Ó èdúi N‘Þxá';f|&Ìp¼§W7LÔh\•‡i™>@¥ð¹(`׊N¦ŸfŒ)ÒÙo\ß}úÈz`Gã*;Lû\T•­U›é'˜Q À® ”>,éç9`Nž Àh\>‡é*Îô§8Ee«ŒLìÚ@‰Â™~ž¦LÀh\úôL•Ee+…LìÚ@‰Â™~ž¦LÀh\úôL•Ee+…LìÚ@‰Â™~ž¦LÀh\úôL•Ee+…ßÈE»6P¢°@¦Ÿç‰ŠLÀh\úôßÈEeQÙJQ£ (6™~)€Ñ¸ÜÓUœéT™>E'ÓO#E0—ÛaZ¦P)dúL?ÍD¥H¡ :¿0kÖ¬Y´hQY5)´gíÚµJ•8—çaZ¦P)|§ èdúidú•ehhhêÔ©‡.«Võõõµ´´„¶)TÜh\ž‡i™>@¥ð¹(`׊N¦Ÿ¦l3ý ý ^užo˦mÚ´iñâÅi×­[×ÐÐþ›6ýÀ .lll¬««›;wîöíÛ£µgЦ>|xúôéçÎK[ÎéÓ§[[[ùË_f}c¤½½}óæÍv(¨¸Ñ¸<Ó~#•Ee+EÕfúÉf°k¥ dúy˜2ýb¼q,›¶xñâmÛ¶¥M¼üòËxà÷¿ÿý©ûúúZ[[7nÜ]>¿wïÞ%K–$4#úóÖ[oݰaCÚò»ºº–/_žÜòð9ÉüÇ üGãòü›ßüfÆmÎó£eÛ“Ýß"hllܼysêòÏ>ûì©§ž&&Nœ¸nݺx~øàÝÓù+øÿø#GŽLÿþûï‡ xýõ×?È™éoÙ²%l›¥+ò0-ÓGeQÙ¤é£@€® ”(,éç9`–é_qÅ] ·ß~{4?L¤Î—?öØc[ZZÞÿý÷Þ{/,3cÆŒŒëÿüç?Ùe—ýêW¿ ‹Ýpà sæÌÉui‘eæÍ›VÏ?ÆËOŸ>=þw‚|¾GØa‡EúÖ­[ãI{až-Ûߢºº:mK.¹ä’Ç{,L<þøã]tQ<¿¾¾>|ÌÜ{)ã“'OþÞ÷¾Ï«É›{ÏÔÔÔèPPúѸ"Ó2}T•M ™> èÚ@‰Â™~žfa™þ›o¾M¿ñƃަ‡ ’:?c:üþûï80ãú‡¿|ÇŽõõõ¹.-²lC˜ˆŸ.&âåõ«_eûh566.Z´(í+ó9^˜ã£eû1ã[¤eúác†=V½EêìžþçYÁ–––è‹ÿ‘ñãÇ¿øâ‹ñ2Ùî§ÿLúh4®ÈôL•Ee“Â3rQ @×JÈôó¼P),ÓO'Ë9ä3Î8£¶¶6ʈ«ªª²-Ÿ*^,ó¥E~ÛÐã¶åðÓŸþtêÔ©õõõ#GŽ\³fMÆæÿÑ2þ˜ñ-†žšòßu×]i{æÎ;ïŒ~Uð÷ôƒÓO?=l|˜xþùçS;KŽ=¶Ê½w OFãŠT´¶3f,Z´(÷^Êöã~ðƒÉ“'ðá}xž}öÙ|öÌO<á¹Ð'£qE¦+8Ó¨02}ŠN¦Ÿ¦ài„ .|ÿý÷·nÝzíµ×¦ÝOû‡ºßO?šÙe—ÅË4(¾küõ×_ϯ««Û²eKüv÷Þ{ï%—\òöÛo‡éú§ŠïMŸùÒ"ç6tuuEÛz?ýÔ—§½uFa¢»Ü<ÿüóá#d|až-ÛžÌøK—. !š~üñǯ¼òÊ´ ›-¦Ïv?ý°=a«âý ”r4®ÈôL )dúL?Í^O‘zuãšb]Z ¨ä‹‹{ï½·Üîr¶gÁ‚z”ÕhœèôL )<#tm èdúidú9'ú0í¹¨,*›{ÚûÀŒº6Pú°@¦Ÿç€Y@ŠTSSS²¯ÉdïnC¶·(óÑ8чé Îô]§¸Ee+ŒLtm DaL?ϳÿ¤HFãò9LËôQYT6)dú( k% dúy˜2}£qéÓ2}T•M ™> èÚ@‰Â™~žfømß6ŒÆÈôeú¨,*[®<#tm DaL?Ï •¾M‘\DFãrK˜öŒ\T•MŠvÅ&ÓO#Ó0—Ûaº‚3}€ #Ó èdúidúFãr;LËô’B¦@ÑÉôÓÈôŒÆåv˜–é$…L€¢“é§‘éËí0-ÓH ÏÈE](:™~™>€Ñ¸ÜÓž‘‹Ê¢²IQ±™¾Ktm Ü™~ž¦LÀh\úÃtgúm®@QÙ #ÓG](QX ÓÏsÀì1EÚÕ¹ío=þ4˜ôáh\a‡i™>*‹Ê&…Ltm DaL?Ï3GŠ´õ¹M«;7,°yM‹Á ¯FãŠYwf±ýdú€Ñ8u4–éP~dúL?M)Rß6ŒÆÈôeúåJ¦@ÑÉôÓäó=ýÕŸþó§†_ºê°óV5\EíË«O}ë;ëí=€ÒÆ2}ÊL€¢“é§É?EzgýO^˜ñçOÖ~âé#._¾ß)b}€>ûÃaZ¦ž‘‹º6Pt2ý4½M‘vunûùý˾ô•Ñö_{h•}PúѸ²Óž‘‹Ê¢²IQ±™¾Û€* kåÈôó0s¤Hï¬ÿIÓЋÃ›×´Ø}5Wäaº‚3}×)®@QÙ #ÓG](QX ÓÏsÀì1EÚÕ¹­mÑŠo½cOôáh\a‡i™>*‹Ê&…Ltm DaL?ϳÿ¤HFãò9LËôQYT6)dú( k% dúy˜2}£qéÓ2}T•M ÏÈE](QX ÓÏóBE¦`4.ýaÚ3rQYT6)Ø›L?  dv¼õÎë?µó—¿2ç>LWp¦PadúL?  ”6¯i C드þ˼`4Îv˜–é$…L€¢“é§‘"”Ø?/ûa]CûÎ!“Zç-ÚÕ¹Íhœv˜–é$…L€¢“é§‘"”Þ–u/ èÚ@‰Â™~wMMM+V¬¸û˜sWü^˜ÓÜܼqãÆ¶¶¶ÎÎN»`/Úüý/¯þ÷/é¯:ì¼§>:ùé#.ÿÙ_.þÞãOôçÑX¦Ê¢²É"ÓG](QX Óﮥ¥¥¹¹ù ‡þÿ¯‚FœÖÖÖ¶¶¶ŽŽŽ®®.»`o‰ýåûN|zøe+:ëÇWÞ¾eÝ ÿöÞïŒÆ2}T•M™> èÚ@‰Â™~wííímmm «Ohû½0§£££³³³««k×®]vÀÞo{&úz~ôÅü´è÷óÑX¦Ê¢²Éâ¹( k% dúÆO€>±yMËNzò€Óã/滦=#•Ee“b€]@±Éôè+;Þzçÿ.xü_Ú7Û¹ÓœéT™>E'Ó€2?LËô’B¦@ÑÉô ÌÓ2}€¤éPt2}(óôL )<#tm èdúÆO£q™¦=#•Ee“¢b3ý'œ¤º èÚ@Y…2}ã'€Ñ¸lÓœé;Î골l…‘é£@€® ”(,é?ŒÆe{˜–飲¨lRÈôQ @×JÈôŸFã²=LËôQYT6)dú( k% rdúaÑ*²u<½ÁßÀÙl"Ó2}T•M ÏÈE](QX;Óï½Ûµ k¡¬ß9äìÍkZtÀÙlù¦=#•Ee“b€]@±Éôûm¦ß±ö¿‹õq˜®àL ÂÈô(:™~¿Íôßo>}㓟ñw³¬½½½³³s×®]z@¦eúI!Ó èdúý9Óíí57®xÆs ·µµ‰õÊó0-ÓH ™>E'Óïç™~ho=s㊟hþò#b}€òàl¶ Óž‘‹Ê¢²IQ±™~¸~P]tm ¬Â™~?Ïô£Xyíib}ÀÙl¦+8Ów¢Ï¢²F¦º6P¢° Ä™~oƒé<—/jì^Ι~Û–±¬b}ÀÙly¦eú¨,*›2}е…2}™¾Xp6[¶‡i™>*‹Ê&…Ltm DaAo3ýð’ &54XSS5}úØíÛçFóÃÄM7¯««-LÄóÃò ž;xp]UÕ>Rt_óêÕSÇŒ9<¬vذúo|ãÂèµiË¿ñƧ§LY[»_uõ¾\päæÍs2.–¶þøÇîoѽ͛7±¾¾fàÀýÃfÇ/è¡É‡776Í̱19–ß±ãÖ°Ç»‡½7þÙ©yǧ t@øÕ´iÇlÛvKËÇo”öÁ³U!-ÓÏÝZZZÄú€³Ù29LËôQYT6)<#tm DaA™þå—x÷ÝÏ…&æÍ›ÍŸ;÷äK/=:ž~Œ—ÿä'ÇÄ9uŽï•vXíÚµW‡‰Í›çÌž=!ãòÇs؆ ×ïÞ}ÛÎóÂ[OŸ>6wˆŸöcÆ·HmwÝuÚ¹eËgwì¸5þháåÓ¦f†÷]°`Ò‰'þA“qù°LØ3aW„]4eÊÈx«î¾ûô°ëÂòa=3goX´|¼Kóùž~¶*ôê[üMMMb}ÀÙl™¦=#•Ee“b€]@±œéÿâ³¢é×^›5xp]4&Âñü††ãå·lùl>÷Šilì¢7Þøtüÿx«†ïÒ®®?­¯¯‰7&uWç“ég«B¯2ýÇ\¬P&‡é Îô*ŒL€¢+8ÓOý1¬óœŸ#£ß´iæUWª¯¯1âÐÕ«§f\~Æë?ñ‰ÿR[»_tÙªª}z•ég|‹ÞÞ³>þ±·“c~ªËdüç‡3ýG}téÒ¥Ï<óÌÆÛÛÛ»ººt€¾:LËô’B¦@Ñíõïé§ÎOýž~oŸéº~ýuñjÓ–:tàSO]µsç¼0þÌ—WUí³{÷mÑô¶m·tÓÔ·Èç{úÌscâS¿§öUêzâ{ñ§mLßÓÏX…^eú©áÑ+¯¼ÒÙÙ©³ôÕaZ¦2}ŠnOî§¿mÛ-¡¥ÞO?¾ù{˜é¥G§ÞO?õåuuÕo¿ý™Œiòôéc£<}ýúë : ãòa~|Cüë®;6^yÚb'œ0øþûÿh÷îÛÂbÓ¦/–ñ-ºßO?¼*í~úcôl“mù°Â)SFF»îª«FÅóï¹çÌ‹/>*Šû_}õÆø¾üñý÷£]š1ÓOûàÙªÐÛ{ï<úè£Ñx6nÜØÑÑ¡³ôÕaZ¦ž‘‹º6PtgúóçŸ=hÐ55U3g¿cÇ­ÑüíÛçΞ=!Ì mÖ¬âùiIô}÷ÈO/[vÙ¨Q©®Þw̘Ããã¤-æqÄ!UUû46¼hÑyñü´Å6mš9nÜG£Åxà?ËøimîÜ“ëêªÜáÂssgôÙ6&Ûòa͘q\x÷°÷Â>L½1Î=÷œ9lX}XÕqÇ5„Œf†}öp¼|Æ–öÁ³U¡W™þO<ñè£>öØcßùÎw~ô£µ··ë,€³Ù¾:L{F.*‹Ê&EÅfúá Auе² öð~úZa­­í¦aÃêËpÃBÑ—/_î;¡€³Ù29LWp¦ï:EŸEe+ŒLtm DaL¿”mîÜ“·m»eëÖ› »1ŽLp6ÛßÓ2}T•M ™> èÚ@‰Â‚Þfú55U¢ù‚Û¢Eç54X[»ßôéc·oŸ+Óp6›û0-ÓGeQÙ¤é£@€® ”(,èm¦¯Uv“éÎfËê0-ÓGeQÙ¤ðŒ\е…2}M¦8›-Ûôg䢲¨lR ° (6™¾&Ó(óÃtgúF¦@ÑUv¦ïY¾2}€ 8LËô’B¦@ÑíI¦¿`Á¤ÚÚýŸióö³?¹üòuuÕÕÕûž|ò•+§DñzwÑü×^›5dÈA;wÎK[OWן66¼uëÍ_˜èVæA¦PV‡i™>@RÈô(º=ÉôGŒ8ôÁÏ?ê¨A©3ÛÚnjl<ø¡‡&ïØqkøñÅÿxÚ´crÄÙÑ×]wì¢E祭þü³oºi|E~Ý^¦@þ‡i™>@RxF. èÚ@Ñœé¯[7í”S>&N>yÈÚµWÇó§OÛ=ï1ÓùåOqhêüÝ»o6¬¾­í¦<ðÕ«§ŽsxMMUxÕ7¾qaÚ{…‰‡šÜØxpuõ¾ãÆ}tÓ¦™ñ çÍ›X__3pàþ žϼãŽS : ¬mÚ´c¶m»%ã§X°`RCÃa™ð‘·oŸÍ7Ý4¾®®:´0Ïˇõ\WUµOùÿ‡™>àl¶¬Óž‘‹Ê¢²IQ±™~¸BP]tm ¬Â‚2ý‹/>êÑG/K–\rá…ç×××¼ûîçz›é‡vÁG~÷»WÆóÃj¯ºjTþßj?ì°ÚèŸ6ož3{ö„î™þ´iÇlÙòÙÝ»o[°`Ò‰'þA4ÿ®»N ïæïØqë¼y£™wß}ú¥—fîÜ9oæÌããµ¥möå—Ÿ4´0¿vîÜ“ÃkãùáÇxùO~rLüϾ§àl6ÿÃtgú®SôYT¶ÂÈôQ @×Jô6ÓÿÅ/f :p÷îÛ¢/Ô\÷Úk³¢_UWïÛ«ÛÎÄ?nØp}ôÅÿ¨÷Ñ^øoñ2=ÞO¿±ñà… Ï}óÍÙW&â¯Ì‡ Ž7rȃâ-[øháÆ÷ô¯¯¯Éø)âeˆ¦SwE˜hh80^~˖Ϻ÷€³ÙÓ2}T•M ™> èÚ@‰Â‚ÞfúwÞyjZÈ~ǧìá÷ôC;í´¡6\&Ö¯¿îÌ3{•€oÚ4óª«F…w1âÐÕ«§vÏô³eý·0UUÕ>=~Šø òœ/Óp6›ÿaZ¦Ê¢²I!ÓG](QXЫLçÎyƒ×ÅßR¾¶ßÐp`ôµýéÓǦޘ¾W™þ3Ïü× .82ºϺuÓ KÀׯ¿.þÖ|™~¶ïéoÞ<§Çÿmí{ú©óS¿§/Óp6[ØaZ¦Ê¢²Iá¹( k% z•é/YrÉ”)#Óf^pÁ‘‡iÏÈEeQÙ¤`Pldú§œò±æækÓf®]{õÉ'‰¦ö³?™2edmí~UUûœpÂà•+§äŸé/YrI}}Mø3ÇÍp2FüË–]6jÔGª«÷3æðüï½=Õ¶®®zàÀýSÿ{Á=÷œ9lX}ØþãŽkkΘéÏŸö AÔÔTÍœyüŽ·Fó·oŸ;{ö„03´Y³Nˆç§mÀ}÷(ÛoëËôÊê0]Á™>@…‘éPt…ÝO_+ó›çÈô*é0-ÓH ™>E'Ó—éËôÊü0-ÓH ™>E'Ó/¬ÕÔTÉô(ÍaZ¦ž‘‹º6Pt2}M¦8›-óôg䢲¨lRTl¦®TW](«° q™~‰o}³·Þ.)wì‘éÎfËê0]Á™¾ë}•­02}е…½Íôû<›–éËôg³ýç0-ÓGeQÙ¤é£@€® ”(,ð=ýÞ®€mØ+›¶’uë¦}ö°ššª÷Ÿ1ã¸Í›çÈôg³v˜–飲¨lRÈôQ @×JÈô“›é_xáÇ×®½z÷îÛ¶oŸ{óÍ'ž}ö0™>àl¶ÂÓ2}T•M ÏÈE](QXPð½wV¯ž:fÌá55UÆÕãf\ò¡‡&76\]½ï¸qÝ´if4ûö¹7Ý4¾®®:´0~ 3GŒ8ô•Wnˆøö·/&œ£ŽÔ}µ Ljh80¼õôéc£—g[m÷<þ1ÛæíØqkXmXyx‹ùóÏîžÅH‘û­ Þwo÷÷Mm»wßÖ,ÓœÍVØaÚ3rQYT6)ØÛfú‡V»víÕabóæ9³gOȸä´iÇlÙòÙÝ»o[°`Ò‰'þA4îÜ“/½ôèwßý\h—_>"üfΙó‡Qrý曳kk÷Û±ãÖ0ýÐC“»¯9¬6¼*~ù¼ys¬6w¦Ÿqó SןÏ÷ô³½uÁ{#ÛîÍñeÿÕ«§žrÊÇdúv˜®àL ÂÈô(º=Ìô^¸ðÜ7ßœãF1ñ·ÎS¿E>xpÝk¯ÍЦÃDCÃaâ©§®š6í˜0qß}çqÄ!Q¾õÕ£Ãüî«ýÅ/þãåam9V›;ÓϸyC†”ºþ|2ýlo]ðÞȶ{³eú/½43ì´ø»ÿ2}€Š9LËô’B¦@Ñía¦¿iÓÌ«®U__3bÄ¡«WOí1øN ÓSçGéöÎó†&Ž;®aíÚ«O;mh˜sÂüÜ«Ãñ<çgÛŒççóÑÒ޺དm÷fܘõë¯6¬~Æë÷Êódúeu˜–é$…L€¢ÛÃL?5SŽ¿,ŸOŠNý"|üÍôóÎ;¢©éªè2áÏÕ«§^xáÇ3®6Û÷ô3®¶ªjŸÝ»o‹¦·m»¥Çì¾°ïég|ë=Ùwo÷Y¹rJcãÁ/¾øÇ{ë™À2}€²:LËô’Â3rQ @׊n3ýéÓÇF7Y¿þºAƒÈ?ÅŽoX¿mÛ-—^zt|ù &uÔ 8/L‡?8âûî;'ãjÃËÃkCK½Ÿ~¶ÕžpÂàûïÿ£Ý»oÛ¼yδiÇô˜éGë‰ÖÖ“1Ó¯««~ûíÏÄ?f{ë=ÙwoÚû.ZtÞСãg Ëôg³•w˜öŒ\T•MŠŠÍôÂê* keœé/[vÙ¨Q©®Þw̘Ã{uïíÛçΞ=¡¦¦*´Y³Nˆ‡ÚË/*¬mëÖ›Ãtø³ªjŸ0'ãjçÏ?{РÂËgÎ<>~y¶ÕnÚ4sܸ†µ56üÀçõ˜é‡†Õ†- oÞ(c¦ß}ç„wéñíÉÞȸ{ÓÞw@72}ÀÙl…¦+8Ów¢Ï¢²F¦º6P¢° ·™¾VÙM¦8›-«Ã´L•Ee“B¦º6P¢°@¦¯Éôg³e{˜–飲¨lRÈôQ @×JÈô5™>àl¶lÓ2}T•M ÏÈE](QX Ó×dú€³Ù²=L{F.*‹Ê&Å»€b“ék2}€2?LWp¦PadúL_“é”ùaZ¦2}ŠN¦¯ÉôÊü0-ÓH ™>E—O¦¯õÃ&Ó(ŸÃ´L )<#tm èzÌôcÍÍÍ+V¬X²dÉâÅ‹—÷O,ï·äG€³Ù29L{F.*‹Ê&EÅfúO 8Iuе² òÉô[ZZššš–.]º¸?Y8ïîÅýX(w(z(}{{»Î8›í«Ãtgú®SôYT¶ÂÈôQ @×Jä“é·¶¶677¯ZµjéÒ¥Kú¿¸è“Kú«PèPîPôPúŽŽp6ÛW‡i™>*‹Ê&…Ltm DaA>™~[[ÛÆ››››ššVôwsîŠþ*:”;=”¾³³SgœÍöÕaZ¦Ê¢²I!ÓG](QXO¦ßÑÑÑÖÖÖÚÚÚÒÒÒÜo|áГšû«PèPîPôPú®®.p6ÛW‡i™>*‹Ê&…gä¢@€® ”(,È'Óïêêêìììèèhoooë7¾ó=mýU(t(w(z(ý®]»tÀÙl_¦=#•Ee“b€]@±åŸé}r˜®àL ÂÈô(º(,8äCÎÊL8@ËôD¦@ÑE™>P¶dúI!Ó è.\xÖï7îØc=zô¨Q£ŽÊÃŒ3dú‰à¹( k%ÕÒÒÒÔÔ´téÒÅüÞã—϶ŒÆå  Ãa:¬ÛÛÛ]§ ²¨¬L¿¤žp’ê* ke¨µµµ¹¹yÕªUK—.]Â‡Âøi'û\84‡t8L‡ƒuGG‡ëT••é«= º6Àmmm7nlnnnjjZÁ‡Âøi'û\84‡t8L‡ƒugg§ëT••é«= º6Àmmm­­­---Í|(ŒŸv€Ñ¸Ï…Cs8@‡Ãt8Xwuu¹NAeQY™¾Ú£@ k|ÐÕÕÕÙÙÙÑÑÑÞÞÞÆ‡Âøi'û\84‡t8L‡ƒõ®]»\§ ²¨¬L¿¤$ƒgä¢@€® `üÀh¬²¨,*› ›é?1à$ÕU @×0~`4VY;AeQÙJ"ÓG]Àø €ÑXeQYT62gú¿Ýö›wÖÿ$Ñ-Ô>¡[þÛ_oï±l Tæ‚ÂèÚº6ÐoÒÄŸ†M0ÌVdKîÙ¬AeUVeU¶_?gÎô£}§õIÛúÜOz,›•y 0º¶® H ›€aV2h*«²*ëü¹ðLÿÕû¿ôÎú'´’µW¿ü¥^u]*ÛÁž\#éÚº6` 5l†YMÈ ©¬Êª¬óçB2ýð–ÿöo›´’µ-Ï>Ñ«®«@e[ Ø“k$][× ¤†MÀ0« 4•UY•uþ,ÓOL×~ùá–––ÖÖÖ¶¶¶ŽŽŽ®®.J\À5’® H ›€aV2h*«©¬VŒóg™~ÙxÅçïkjjjnnÞ¸qc¨qgg§%®@àI× ¤†MÀ0« 4•ÕTV+Æù³L¿ì ü­ÿqÏÒ¥KW­ZjÜÚÚÚÑÑ¡@‰+¸FÒµ©a0ÌjBMe5•ÕŠqþ,Ó/»?vÇ/^jÜÔÔÔÒÒÒÞÞ®@‰+¸FÒµ©a0ÌjBMe5•ÕŠqþ,Ó/»/ÿß_ ^²dÉŠ+š››ÛÚÚ(q×Hº6` 5l†YMÈ ©¬¦²Z1Οeúº®æ ×Hº¶® RͰ f… *«²šÊj2}M×ÕÁ5’® H ›€aV2h*«©¬ÊÊô÷z0`€®«¹‚Å5’®­kƒT3lým˜-‡@@È ²*«²*«²2ý’þ¥Ù[átݾ*è¼xâı=¾Ä,®‘Ôµ››¿>iÒ‰55û××4gÎ5¿ùÍßêÚ` uÉ!Ó’u¾Z²‘6~£üÏ!ûö48Ïí,ÛAesìŠ| •ß(ì®k®9àÀºêêýÏûÉwß}NŸUÙäöY™¾L_ð׋¢„5ô@™>2ýJêÚ]ô‰þðÁßýîÅp¶tà W\ý…º6Heú2}@¦Ÿûò?‡ìÛÓà<·S>˜¸ÊæP¨lüFgœq·¿}O‘¿óNóܹŸ<ë¬?ÔgU6¹}¶ˆ™~ØÄ°O'L]]½_mmÍÔ©ç†=ÿöá‡ï>òÈ…_…?ÿæoî s~ýë ƒüÛßþ}¼L˜®¯?èÝwŸ{ë­µW^yNXIX~òäÓRד±=õÔýãÇŠþ}æ‘G>ŸãMóÙÔ°†áÇÔÔìôÑáUñ®ï^ƒÔ9Ý·aÀÖã&}õ«·ü‘ªª}‹Ôu“U ¯}펰pøÕ±Ç~|Æo~ë[Ê~ ëyíµ§ãÅÂÂüP¬P²%K¾£#Es2%lÞñÇ+ih8ôÎ;?UÀå –>¼FÒµ îÚÑg èÚ` Í1fLò‘ºŸævŽÚ½½úêªpÛa ]¾|~>›oL¶ÃAž/7lù§HÉi3^ æ>‡ŒZŽSë°m3‡Ü»"ãfg< Îg;÷V>¨²¥¯li2ý «lÔrWŸUÙ2ï³ÅÍôÃGzá…o…éßüæoo¼qJØeѯ–-»7ì—_^¦7mZªø­oýE˜¾ì²³ÂŽˆ×f^qÅÙaâØc?þã/þÝï^ ›o¿}ÆŒ—æøð«Wµ±qpX>LwtüpæÌËr¿iîM]¹òKñ«ÂŸáUùdúÙ¶!í%¹7)|ö_ÿzCñºn² tþù§„^ÞâÏþlVè?¡×Å?Nštb¼Xj±Âôw¿{_ÅJûÕÚµLœ86l@˜þå/×…|÷Ý7ÊôIV¥kÖµCûç^Î6tm0fHs &ùHi§y…£fl£G.E¢\>¬êÚk'ç³ÙñÆd;äùrÃ&Ы)¡#mÆ‹ÁÜçQËvj½|ùüxÛ^yee˜Žß"÷®È¶Ù9.Wslç^ÌU¶Ä•-Y¦_I•}÷Ýçî¼óS¹_¨Ïªl™÷ÙâfúííßÜµë…øßIÂŽX¿þáÔ{EtÒ˜è=Î?ÿ”x~˜sÒVvúÀu9>ügœðÃ>Ø}~¶7íÕ¦þèGä“égÛ†´—äÞ¤ø;ªÅËôT 0Åë?vtü0þ±¶¶&^,­X§Ÿ>®·ÃMؼŸýì»ñaÜé>€Êô)ó(J×.¬k‡6}úÅ_üâgtm0fHs &½º¶I=Í+ì5Û—’:;×g¢slv¼1Ùy¾Ü° ô*EJèH›ñb0÷9d÷–zj¶-œßfÌrïŠl›ãr5ÇvîÅ|PeK\Ù’eú•QÙø{Ó£G‘íK ú¬Ê&¢Ï7ÓÏ6§ºz¿°×R÷`”Ý„‰††C£L'ü¦£ÅÂÞ¹ä’3Â2Qy2þßÞ¸¥­<Ûü´À(ÿMÍ'Ó϶ i/éÕ&#ÓOh²ý& (qÚ¯Bß‹[ؤŒG¦O™GQºva]ûk_»ã¢‹>¡kƒ4ÇØ’c0éյ͞Ÿ£flwÞù©pqãS–-»÷­·Öæ3¦®0Ûá WŸÚ° ä™"%t¤íþÛÏ!£–íÔ:Gæ°WvEžÛ¹óA•-qeK–éWLe£ª]qÅÙÓ§_¬Ïªlrûlyeú¡Ý|óµÑ¿Aýå_~vΜk¢™'4æ _˜½k× ù|ø°û<Ó϶ IÏôˤ@E þB·L½“laOüpK²2}];´/ùÖI“Nü×ý?º6HsŒ-9“r¸¶ í¥—–å+ó®½vrCá_úÒŸöv³3zõ© ›À¦He>Ò¦ý6ŸsÈܧÖ92‡½²+òÜÎäƒ*[ŒÊ–C¦Ÿ¬ÊÆí_þ¥%^§>«²Iì³}“é‡Ý—ú_-Âtü_-6mZ6zôÑíþþï¿ï»xá03÷‡?ãŒÖ®} cͲ½iŽM=ýôqÙî½SS³ü7 ºËUêÊȸ i_ íÕ&•2Ó/óåþ²Ý #G±ÒŠ6/õ œ2}*)ŠÒµ³mü²e÷†u†c¿® Òﯘm0ÉDJ[aç¨=¶_þr]êζÙi+Ìx8Èÿå†M`ÏS¤2iS›ç9dîSë´;Q„é|vEsÝäƒ*»×+[&™~‚*›Ú:;××פϪlrûlßdúßþö=Gù±—^Z}™høð!©Ïã —_ùʼ±cJ}ÞWô(äööïO˜0:÷‡O}Nã[o­5kjošcS£g䯝J}FîÔ©çÞpÿùÍßF R7,Û6 røßýÝcñ»ôj“J™é—yrq±^~yÅÈ‘Ããiæ(VZQš›¿>xðGBÝ÷»CÛ°á›ÑÓádúT@¥kgìÚáPN˜¢•èÚ` Í=æLò‘ÒÖ_Ø9jÆvÉ%güèGDÛ.<ÂÀÞãfw_a÷ÃA¯^nØö0E*ó‘6þmþç¹O­£Ì!z*ct®›Ï®È±Ùi»"Ïí,A>¨²{·²å“é'¥²×^;9¼côEìW^Yyþù§Üu× ú¬Ê&·ÏöM¦ÚÃßåãáÏ0ºØ¿ø™úúƒþê¯>Ï » \WTUíÛØ8øk_»£ÇDõ»ß½ïøãGDË?òÈç{|ÓÜ›ú裖k þõ_ßÿêw𝹿üššýïÆµlÙ½©¯Ê¸ Ë—ÏnØÔÛM*e¦_æÊü…n+õßÐr«{QBçœ4éÄè>Ya"õ«Çþ3W°$+ŠÒµ3víAƒNëÚ©ß8еÁ@š6'Û`’ÿˆÔ}ý…£voacÎ:ëÃÔÖÖ\yå9©O$˶ÙÝWØýpЫ—6=L‘Ê|¤›ÿ9d§Öa{ÂÌ8s¨®Þ¯€á7Û®Ès;KªìÞ­lž…ÊÆ¿ [oäI'ùêWoÏ}y«Ïªl™÷Ù"fúZñÚÞêºÖòÿßèåS (Æ5’®­kRÃ&`˜Mb{å••#G2¨¬Êª¬Êª¬L_¦/øs‹k$][לÄ6Ãl9¶™3/koÿ~!Mœ86zV¹AeUVeUVe+3Óþ¿CZsÔŸ T>\ÁRÁ×Hº¶® Ò>oI9 6l‚a6¹)RÁ#m/\¶ìÞ‘#‡‡ÅŽ<òc}!õ«|PeUVeU¶2*›àLß—žüsœo¥áIwÓµ©a0ÌjBMeUVe?Ëôu]Í,®‘4] ¤ša0̱UVe5•Õdúš®«@àI× ¤†MÀ0« 4•ÕTV“é+°¹‚Å5’¦kRÃ&`˜Õ„ šÊª¬Ê:–é뺚+X\#éÚº6H5Ã&`˜2¨¬Êj*«Éô5]W×Hº6` 5l†YMÈ ©¬¦²*[²LÿÕ/)¼¥V²vx¯º®•m`O®‘tm]06ì&dÐTVeUÖùs!™¾Ö'-ÿ®«•m`O®‘4]06ì&dÐTVeUÖùsï2ýßþzûÖç~µõ<öÔ=‹–ý¯¿zìŽ/j%hK|(xéÒ¥MMM---ííí ”¸Aatm]06ì&dÐTVeUÖùs!™~ªÖÖÖæææU«V…·\BI„]vxØíaçwtt(P¢ …ѵumÀ@jØ ³PY•UYçÏ…dúmmm7n oÖÔÔ´‚’»:ìð°ÛÃÎïììT D £këÚ€Ô° f2 ²*«²ÎŸ Éô;::ÂÛ´¶¶¶´´4SaW‡v{Øù]]] ”èAatm]06Ã,BTVeUÖùs!™~xƒÎÎÎðNííím”DØÕa‡‡Ývþ®]»(Ñ‚ÂèÚº6` 5l†Y„ ¨¬Êª¬óçB2} Èô dú 2}H™>$Ãÿ›˜ öØyIEND®B`‚neutron-12.1.1/doc/source/contributor/internals/images/live-mig-ovs-hybrid.txt0000664000175000017500000001140113553660046027536 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Generated using plantuml. @startuml title live-migration with ovs-hybrid plug : nova<->neutron interactions participant nova_conductor participant nova_compute1 participant nova_compute2 participant neutron_server participant neutron_l2_agent2 participant neutron_l2_agent1 participant neutron_l3_agent2 participant neutron_l3_agent1 nova_conductor -> nova_compute1 : live_migrate activate nova_compute1 nova_compute1 -> nova_compute2 : RPC.call : pre_live_migrate() activate nova_compute2 nova_compute2 -> neutron_server : REST : list_port() activate neutron_server neutron_server -> nova_compute2 deactivate neutron_server group port plugged on host2 nova_compute2 -> nova_compute2 : plug_vifs() activate neutron_l2_agent2 neutron_l2_agent2 -> neutron_server : RPC.call : get_devices_details_list_and_failed_devices(devices : [port]) activate neutron_server neutron_server -> neutron_l2_agent2 deactivate neutron_server neutron_l2_agent2 -> neutron_server : RPC.call : update_device_list(devices_up : [port]) activate neutron_server neutron_server -> neutron_l2_agent2 deactivate neutron_server note over neutron_server port status is never changed since port is not bound to host2 end note deactivate neutron_l2_agent2 end nova_compute2 -> nova_compute1 deactivate nova_compute2 group proactive dvr router creation nova_compute1 -> neutron_server : REST : update_port('binding:profile'={'migrating_to':'host2'}) activate neutron_server neutron_server -> neutron_l3_agent1: RPC.cast(fanout) : port_update(port) activate neutron_l3_agent1 destroy neutron_l3_agent1 note over neutron_l3_agent1 "migrating_to" does not match host end note neutron_server -> neutron_l3_agent2: RPC.cast(fanout) : port_update(port) activate neutron_l3_agent2 note over neutron_l3_agent2 proactively create DVR router end note deactivate neutron_l3_agent2 neutron_server -> nova_compute1 deactivate neutron_server end note over nova_compute1, nova_compute2 libvirt handles the live-migration end note note left of nova_compute1 live migration succeeded end note nova_compute1 -> nova_compute1 : post_live_migration group port unplugged on host1 nova_compute1 -> nova_compute1 : unplug_vifs() activate neutron_l2_agent1 neutron_l2_agent1 -> neutron_server : RPC.call : update_device_list(devices_down : [port]) activate neutron_server neutron_server -> neutron_server : update_port_status(DOWN) neutron_server -> neutron_l2_agent1 deactivate neutron_l2_agent1 note over neutron_server port status changed to DOWN since port is bound to host1 end note deactivate neutron_server end nova_compute1 -> nova_compute2 : RPC.cast : post_live_migration_at_destination() deactivate nova_compute1 activate nova_compute2 nova_compute2 -> neutron_server : REST : update_port({'binding:host_id':'host2', 'binding:profile':{}}) activate neutron_server neutron_server -> neutron_server : update_port_status(DOWN) neutron_server -> neutron_l2_agent1 : RPC.cast(fanout) : port_update(port) activate neutron_l2_agent1 destroy neutron_l2_agent1 note over neutron_l2_agent1 port not hosted on host1 end note neutron_server -> neutron_l2_agent2 : RPC.cast(fanout) : port_update(port) activate neutron_l2_agent2 neutron_server -> nova_compute2 deactivate nova_compute2 deactivate neutron_server group port_update processed by agent that really hosts the port neutron_l2_agent2 -> neutron_server : RPC.call : get_devices_details_list_and_failed_devices(devices : [port]) activate neutron_server neutron_server -> neutron_server : update_port_status(BUILD) neutron_server -> neutron_l2_agent2 deactivate neutron_server neutron_l2_agent2 -> neutron_server : RPC.call : update_device_list(devices_up : [port]) activate neutron_server neutron_server -> neutron_server : update_port_status(ACTIVE) neutron_server -> neutron_l2_agent2 deactivate neutron_server note over neutron_server port status changed to ACTIVE since port is now bound to host2 end note deactivate neutron_l2_agent2 end group @enduml neutron-12.1.1/doc/source/contributor/internals/images/under-the-hood-scenario-1-ovs-network.png0000664000175000017500000050465713553660046033007 0ustar zuulzuul00000000000000‰PNG  IHDR¼È¬á9 pHYs  šœÕiTXtXML:com.adobe.xmp 5 2 1 m ž@IDATxì`EÇÿדKï!•Jè¡÷ÞTTTD±°~~ŠègÅ^»¢Ø;*JgÞII ½·+¹ýÞ»ËAÀ€¡§¼Ñp{»³S~³7ûß7ogT HB@! „€õ”€ºžÖKª%„€B@! ìDðÊ… „€B@!P¯ ˆà­×Í+•B@! „€Á+×€B@! „@½& ‚·^7¯TN! „€B@¯\B@! „€õš€ÞzݼR9! „€B@¼r ! „€B@Ôk"xëuóJå„€B@! DðÊ5 „€B@!P¯ ˆà­×Í+•B@! „€Á+×€B@! „@½& ‚·^7¯TN! „€B@¯\B@! „€õš€ÞzݼR9! „€B@¼r ! . üô:’«R}¢9iÉH:–MmÈHMBRJ&mÕïP–Ÿƒ‡Qj©ï5­ßí(µu™€J¡P—+ eB á¨(ÎÁŒ×^ÂÖ‚ hU€Vo€ Lf3¸k³i=qýÄÇÑ3ƳlX=ëCüº*6«6î) 7ÿPtèÚÃv‡›æß’Q0íÚNxãh¬üû;DûP¡N åxf@+üà=[¿¾w]Ýò{bÓúáuR¼3}1aÖ§`ÅÞ\\}×}èÝ<äxdsA*>ë„]ù Fv?¾ÿtæ²|dåZÍ©E=ÝIç°ñkqóô%ø`öj\ÕÎ÷RS„€çG@{~§ËÙB@ËG@©° ;9G +ળbÝܹH±z ïÞð%lÕx“U±¦åS°qÎ ¼ýÙQ ÝZV¼ØõýL¼úܳ¸aÊL|ýÔ va}¦­ÈEYQ úꤋ-æ¢bXÔž¸ò›È15‚Û™üDZ2Ìÿâc|¼vænÌÃòEÓ¤wD²–daÖo".fTïÖEᑟlXòõ“øW-ÿrÔ|‡J]ˆâÂ<¨ÅÀ[shS J@ïÅ)‰ !p) h½‚1é«PQ¡@­"ëêõmðâþ|üù/d]T* Êóà÷oÄ®ä <ýÑ®cOôëÖª§‚Vk"àÓ/¾D°›C°–fíÃã·ÀG¯¼ƒ'ïσK±!C‹A{ÃÛ ‚ÍTŒµóæÂÑýãÃíyf$¬^€Y»¶¡Øæ‚N}†`@Ǧv4*•šâ¨É­†»‡;,”†SÚ¸ Ë6lEN± Q-;`øžpÓJTW­^îÁ8°j&¦¼? ï>r¥ÆõUAï èµ'¼ÕRv¬Ç²›’^ˆ Ðhôê?MÃ=¾c¾šù vî ÄLâã¥Q#,¾z4ó§”,X»l1RË\0°_ø–òü$ü9+:öˆè`7ìX1K7lG‘Eƒ°è8 8a~.(ÈØ‡…Ž Eco,]¼¯½j­jõ‰2Y R0gþjhC[`p6øGO­²|B@œ/vi „€¨¦ÝÐLñiy…’˜ã¨MIÆ.åænÑ 4.Ê ‘×*=Z‡+0(?\TMu+”÷îí¦ |¬r´ääÃ?M¯@Õ\Y“]¡|~k[EÒYÙ’b±G*ÏÜ© õƒÒå¾öïSol­h]]?¥÷€ÁJS£^1øF*3–'Òq“2¹Ÿ¯Òxè$¥´$K¹ªW¤Òò&…³ÛðÇ[J¨Q§øÄvVzukFæe2êñJY…=Ù*ÿä+:·P:¸Q¹otKÅŧ¥²t¾ýxéÑåŠ0(ÿýf•ýûþ5ß(1^®ŠÞ-N}ÕP%P%$v„²%µDYýÙ J¸Št·_¸Ò­OwEo0*]ƼïÈ'¯Ò'ÎÊ«ü±Åsã×ÇPå× ‡•3ŸTü4”VëÊèaý²P+m\¯ì+R”­³Ÿ£ó\•Ð`?ú4*STVL»]ñòn¤ü™@,Ê£×ÇSZ¾Ê뿬W«TO6…€À‰GîóUÎr¾Bà2p¾‘àüÜ0û|½6/}·Ïú‹—.Ç ­]ñå+“°7¯âŸ¥%‰ƒ 6§É•¾f¥ìÄ_­|B讆ÖÅî®.Ç}^Ud¹tu'i§s ˜©ÈÒl-SaÂÔYX8÷/,]þ+¢LGðÚ´ŸÉnª¶[b«`pq…+\aÍÁ»“žDi»ñX¿a9-Ý€iÆÆ_¿ÇÁÂúd¨”2‘Óíƒ/½ƒhe/žxîM”P¢êãåæ >˜< ¹ƒ±r÷ü:k.¶,ÿ ®Gç๡Ûm“1é®.hÜíf,Z° o¬} Anûv!ó˜.†llßµÒR°döJD¶ï„¸À"¼:éexz ëÖ/ÆÏÎÃß>‰]‹Æ´oÖAçÆej9G²Ò0¡o,+YyupÑâ½§îÆ[³ÓðâWá±k:“ϵ! „ÀÅ' ‚÷â3–„€¸,lY¼¾í®ÁMƒ{BGCö¿(\?¬;* 2±ÿHñ?J¥ÒÐàzê"\7l õ@n ýÑ·ß |µ(&?Ž(Ú'ƒë?ΫºÃf)‡{TŒ»¢?ô:B:ôD×¾qÈÝ–€"r^8!Jg©Ô¥nÄü}*Ü~Ó5hâã½Áw?ÿ%–Íû±žÕ ø“ëB…É„àfðÆäÛ°þ»—ðÚw[apu8óªÔtNi–ìËE£l_ú¾ 7ù ¡#AŸ0o>ÊÕZ*¹ÐÛyj½F oŒÌ8”XŒ];W#5ä ÜxUæ­Ý ¥4swf¡YË!ð,Þ5鮘x÷ w5Ø…lïëF#:À©›¶ Ôîºà‰ÏÞ†poؽ‚êXQœ‡Wï‡ÿ¼»OOÿOíV›l ! .*y¸¾¨x%q! .'â‚b¸û{‘OëqÓ'”5ô$< ºûN*£¢CxTcxëiÌÞfC\|o¼uå5äkÚºŠu¶ê'|py¯BÒàm$¡W™¾bµÏ¡ÕºØ}Uÿ)—Ud5“V Ó°:»z¢é¿L.a¢ü†OøFýò¦¿0 W·~dHeg^2ð– ˆÔuÉž øjFø?ÎÛ·y7Ä·¶o;ÊBi²ê¶< ®wý†-›`Z5¯xWµHÆ-/­Ã®±!HÏθ+‡B]2åp»‘R« 6+‰ZÐC…Ž|£Ki'w=!Ôí>ËåEX²f)M¥o_ç™ò)„€¸$Dð^Ì’‰—ž€ ^þ¾ÈLÈD ½ÔF*ÐQ£•\ è¯âŸS(6rˆ>ÿ ~§ÿbA ²kIsШ”˜“¿«Hd–—‘%˜¿Q K*(/‹RN¹/…ýý²\½áO3BäRB•¡(ç0v¥ i»®ö¬¾«VX»Gâí7¦ Oß xö(%7 Øè€‹/<Ë´¹õ?øîµ PYLÐjHŽ–•ÒËy®`¹ª¿PFh\Â;Ó”a%˜»ä[”þž‹u@çØ`D—Ý‹O¿U¡¨$ÃûFB›èI³JãhA¾³¨l·¦,‰©ÍDâž9Ó”pÌ©2(TÐ ½;þóÜÈŸ÷<ž¹kãñÌãWàïϧcÉÞ2èÕ”³k8†µÀ¼_~ÆÒ­GÈWØÇö.Ç×ÂK?®­<Õ†ÃÛ÷áÐÑB(* 3 s>ý+•`ÄÇ5Cpóf ²âÝgÂuèõháM‘íÑ+øä½Ï‘˜MÖ\[)f}ñ)m—£uß¾pµ±h?Q6{F$Àµnžzݘúñ ™÷àÞ±“‘TðOÿdgäS!p! ˆà½4%-! .+«Mç˜ò«R v¸jÆ ŒÅ{Cß>ýÐwÀ5X½× ϼø:"+§;Q`id%3¬­Š˜þú ´ Ó£˜Õ-+MC¦w1áIw ÿ¾rí=H2vÓŽ&K¨Žëaf\ÊCGW-¹1¨Õ>xtÚÓN™+‡P9ûÀ›ßnÁ¸G^B×°Sg饅5Ø*Kî'Šé‚›}ýš†Ð~§5ؼûÚvà–«Rš}0àÊ[°d·­[ÇpQáÝêä¿qí×cþšcèÜuɯ7²u$Z6á7ñ"Ñ1¬)4ä'qí°v fèýšáÉwmóLòsî‹~}úàžW¾ÁÀ۞ĮmK™•,È4ý½¼ç Ü.< [A~!¼šÃ'ï>м=_ã‘Wg¢¼’›3®| ! .YiíbP•4…€¸,mY†YZ èÛî4¿-ki6–/œƒu[AñˆÑ×¢]t`5åS´}¶ÑaÈÐn0:}pÿSÁÞu‹ñ÷òÕ°è0rôh()[çÕ½Z‡cÿ†eH²x!ƽ Í^ˆµ;†‡N±d%Á»kÕ¤¨#0¸kl[» ÙJú÷lcŸ8eï&ü=w1²Š€¶}‡bXï¶ÕÌQ[m«ø<_ôêOþÈ'Bò¶5Øp ­»öA\˜·ý@þÑøó·ßÉ[¿ˆ–~ÅPÄ9|hM…øã§_q¸ÌW^3±Þf,Yº ðŠA_*Kg{š‡rѵwoDøŸp*Nܶs­DF‘Í:ôÅèa½i~` 8c?®<€.ôÂ_ˆ7¹WPÈ:¸ k÷C§^ÑÈ›S-ÃÚ… ‘^€áºžÞŸÚ~¶ü#„€8"xÏŸ¡¤ „€B@!P‹ ˆKC-n)šB@! „ÀùÁ{þ %! „€B@ZL@o-n)šB@! „ÀùÁ{þ %! „€B@ZL@o-n)šB@! „ÀùÁ{þ %! „€B@ZL@o-n)šB@! „Àùp.Ésþ)I uŠ@~~, ­ü䘜¿N^ +jú ™ÌfXÌ´L.­ V«-ͦ×ëè–«8±Z­.²NÔV´À7t´”¸··c‘—ÚZN)—ƒ€Þx%$$$à?ÿyÙÙÙTûZ~ƒn€í#U®[Ô*5JÊJ‘C¿'E±Õê«H{{yÃÃÓnÕ¢xkucIáêþþ˜:u*âããë@yvEð6ÀößJ‚w劕0°"Âý`³Ébö ð2*_$ -fæ-8­Ñ-º Ñ[;…$‹ÝÝ– °è(zö ‚Þ@Ý--ë…hIC\LjµGRr°xñrlݺUïÅ„}ÒÁ{@Ö¥dø‡j4º`òäaèÛ·-†b%!pT(.)G¯^o@Ð ·>ÿ =@žC2—à5½±ñÁÇ9+ðÉ'·ÀÕèJ¹ÖNq~ pHBà< è°lÙ6¬]{|O•Pû ÔHðþõ×_øðÃaµZÅ"p¾mJV5Ýyºté‚x~~~ç›â9Ÿoµò™­»bá=gˆrb' ¢~±Ân(ew† ê"m¶Ú)"µÊárAV]‹¥®öß}í,k¿¨¤úu‚€š~ûµôé¶Nð»ô…¬‘àýðƒ°héR„ÇÆB«ÑŠï×9·“㆓O¾~ɇãšk®¹¬‚÷œ«!' ! „€B ¨‘àeËnxL,ÞøåWxûùBžiέ…ùõ0SYf1›æÎkù¹a”³„@ƒ'Àþ¸j«¿pƒo  „@­#P#ÁË¥Öèt𠀯ÞslF¾E•——ÃÍÓjL|Žå4!P/ ¨54I uÖÓ»¨È-A­RPV\@#m:¸¸A_k­E½l(©”u’@/;©Ù**ìbW,¼çÖÖ,xmôF‹Â>~§¿§[âr–u˜€yÉPluC£Æ$j«© ©a[é1Ì™öV­ÝLÄö™ˆë&<O£¦Ö¾,WMMd—Bà’3ã%G. ! ØbËsa«U¥X3ãa|üÖ0Û–Þ“‘ØÕi 1ï­›1{Ñ }øCÜôУØýÇdÌ|ç-˜•jÎ9)ù"„€hØDð6ìö—Ú !p©hu¸-8Ì·4 D£?jÂìÞà j‹+<¸k—®@çñbðÈÁh?b"Æß{3¶Ïý ‡’r¡!— B@!P=¼Õs‘½B@ F@­U¡¢4GîD^A 4ôÝ’³ ó~ùY9Ũ Ñkpu!Ѫƒ¥8Gw£¤”„pe­¡i>íÞH¾»aè>¤7,¥ Lå@LßX|™é #¯! „€8 šûðž&îc«N¹Ìþ½—ÓÇ—ïúã¥hJL B@ËJ@Eâ¶`çßøùÓÒ¡ÒV£çÝo kγX:{–¡×¸—ÑÄà %©óñû4»9Ù(QuÂ]¯¿?òÏåÀmeTFpwÓÛ­À¼OïްàB¤g£×,â.k=%s! „@m&p^‚—ÅnIz"öìN‹› M¹eF£¸Ö ò¾<ïdYÍ8²mæÎ^Œv#îC÷Ža²†Xm¾ú¤lB ¾ ¿½ª‹¾x{S}Õž^þ--FòLtê{¶ ÇÏOE€¹Û6Ó‹gžè~çhâO¹GÓËëvœ÷¹üw<ðµ ê“÷?,B@!à pÞ. <]Yñ¾˜rõ5Ø´/ƒ†àΜ$=©Ã¾€-Á>oJ…ûæ­ÀÁCäÓvÓ®iRöûÏE¬cMË!ñ„€¨%¬e(¬0bÄCÿC—·aì«`è¨^лè¡5 ÓëÉ•ú.³]êE>½îðò4œè+Ù¯Wç ›: …Å–ã³8˜ “‘rÔNòù­%5—b! j 3«Ó)&ϬåêŽ>× EPH4ºŽ°O²6l:f×]å6}!†ôÝ»PBÛzþNÎãüÉ÷±Påóy Hç6ŸÃûœ…æxœ‡óO­Ñ#¸iôÚZëÙËjgÞœ®3/Þæà,«ó³ê~Þæ2ò9åÙ™ÈÍ̯8Èû$!а ¨4®Ðêò°fî"Àè UÑaìÛš9Ýæ9ŒòârXL&Øh‰bKy)L¹/XhŸ™ú‡ƒ/YÒª+ÜÜaíï‹ qWÁÅØ·`&²¼¢Ð($D¦:lØ—™Ô^!pA4™ÕlµO|n5U㬻–þ…ƒ9¥¨ÈÏE±Ù®¸P7X÷>|zÚÝ6ñú Úß„Õó¢¤@.ýѹ}ŠS·á÷ßw <Œ†øL!|E[¬ùé{Xý{ðÜ"Ú¢Ïð>ðq%¿¸£‡°fÑ ”Xôð @ó=àKЏÂFå8Ë`++ÁÁ +±|Á¸Ä´‡¾ Z߯è2x0"‚ÈI;„-K7¢œT¾Ñ¿Úöê C9RönÅÚu‰ð÷3ªñ#k÷Zì;œ…¸AÃÑ¡s{„†úeI$ºõ†Í_n†Cnz3_}ïOœ7%oŸ‚¨æáðU¥`ÉŒgÕ®,i`JOÁžM›⚌‚R'¬Aë˜Á„Cèžè×w~úò>½_ìÇOŸ×|€¨(OÉl‚ „€Õ¸ ‚×™°J£EÎÁ%xá¾{‘Úãn€½¿¼ŽmÍxã­[‘¹s3¦EdjrCbÙkO!÷ºE<ü8ôOÁúIàËe{Õ½¢› €[ér¼ñôtÄŽº½›(˜=å¿Ðü‚Þ±…˜þü«ðhÜ~ÊV|òÒ<ÜñÃbôm~ÂßÍY®š|ª Èyõ¬ùðí©GǦ.Øüջظf'Mý3«ÞžŽò˜èïs>ÇÖÍ 9¤+¾ýÏD,9”¨NÝвeWh²÷"õX9‚³òPVίÎIB !°‘u6°Ý ¸zO¤§¥Ã3¬)üý=¡X*pÃË_"7Ÿú‹¨XJz!þj- W5¹84Æ-o÷¢8jZ¨†ä.-§f±¸¢ßcŸCãû ~ò(, ÆŒ‘ãæ2ãéÌ$! „@õ.¨àUhÜÍ;4‡ „ªÛÜ9¦#6ùæã«yY°jµˆ9Í—dâº'î‡ùý~³-რÈ,°¢ë .ðñ Çïšÿâñ/~D«` ¯GÇî‰ûÌd´s-†’•‰âü,$mØ„"üÔý°e¢x¹P¬ç>/ƒÆè°–íÐyÈt™øzD(ØÒ& 3Þÿ +–û"E§Ç­÷ÜŽVåððVðÅ´¹Hz%Ýv Jæ¦bÂ'¯!ÊÛ†Åß}… r/ {-Œj™)¢úëNö EÀFË»ú…!&0Œ-BYXjà¿H^N˜D­»§ƒ â£Îyx[nÉZ CúNüÝnŸJ³2Ð q4•;oÙDí:ØÉ¿B@Ó8oÁk÷±ÕëhBu ´ôiÐ{À¯QJu,öTÐzxB£/´ûåšiÈÍJ¢ÔBóGª©óÖ†tÂ-O=‰v4;°ÈlB–'\¼}àÃkÄ«¸ûÂÝÓ½†Ò£;€Ö“ÒÓi ”Z)O~©ƒÒ¤»…©Ô½|å(>®¡òœ¯BéTдȕW ›^ · Fðòð rP~$ØUT† šS££›ŒBÞ¼4¼‡Ÿ7Œ^Åðpå²”ÙçÓ´’åÆF74Οÿøæ%A†M€…ëÉÞVô„®sGåO¬êú ^–œ£é¨¯ãÀß«‹g?(ÿ! „Àqç-xM%yHÚ²YG“´{»âXÒné‘“×Iû•nBN¡n¤þrsö"aí.4¶•Á-s ^h2n¸®;Žì܉Ø^£a<–†œ”ÃHÞ—¿V¡(<|™©p$é0LÈ<¼ðKC\”’çLÇ×Íèæ#ËðÇß«¡i¿ašVHJ8ƒ{*Êl-ì·×ô† ¢Éß‹¤cÿúh 7lž?º°VèÖ©r¶nÀæUëàÓÁë/‚Ùþjì\’„ŒÃiHKJƒg¬Yjò’”‚„[Ñ:.þ$þI'KB@\0 [{%! „@ œ—àeëeiF2v'–⊇î†ùÐnì*2" Õ ø»[qøHÊtѳ‡ Ù%hÞ¨®Ô »þø.#nÀƒ}†§ŠÙ¿å£ã€«Ð¬™;ö0âîÈÜ·™AžÈK)F§!ƒ`=–ˆ£f âz@c´Á«Ý(Üûd6®Ýßèxàµdæ ##~úÁèWŠœ< 5·€ÙbÊÃŽ•¿£ø€Æ xŒ»uB½Up7¨°bþü¸SFÝqïmá¥Êùp ûФØàhÞ¦ Ž&/ÄèÅ?„Ô¸1$¢B@! „À…'p^‚—m žQí0jB¼Ý•ÀFÒ’gѱ¯OÇ*È ÑªC[»;ƒ•¶U$(¯}êUXÈ­AK.løüïŒA4«¹7ÐÚñœ^ï[î¶§UAoj°S@Hø´ÆnŽ—2ZödwàaÀð& ç Ž)ÃX|ó>>«û0þFÛ” ï«I •ëí. ^›¡Û“Ñ)Ê…ÜÈUƒNæ!ĨöýÙ®7¹dT8æÍ¤}øaè]q•õ£e@é?¿Np+ n® ÏIÌ%ám B ~àÉødè‚– rÞ¢¾MM®OäêO} £ÿ©míÍS¤³{; Üõó$Ò;h'fÁ½½09ÁD¶„@ý!p^‚׎:OîÞîg´M*wüÇ7|̹ÍTCb×Ùµðq‰]Çù´MßíÛ”ó¶á­”6íp¦Íç9_QsžËû8ŽÃ¡2{Ç—3üËù˜r2±gÙBlÞ¼YSß‚ËѦEÀñrÙË@7 ­žüy)>çí¨¹³~Ž›¿=ïJ¡ËY:ËÂÛ„@} À/K-^´‹ïuü`ëC¥ÎµÔy˜L6Ë(„: ~~ûô ];õjêS÷'ÐJoùxâÉY0¸P÷_;‹z®­q^繺è0~|74nÌ£r55•œW–r²—Àù ÞKXØ‹•÷ùZß`ô½ï)ô¾÷)6ÑÐ qÚãb÷bå+é ºG@S¹/>?«WD€Û¹MX÷ê}úóü…Þ8+(Û‹y3^:}ÄZpDE†w½?~º^Ä®³=è¡ÅBïodÓ\ì.&=1‚Žˆàuâ‘O!P_ˆà­lI¾iñ<Â2H[_.m©ÇÅ"ÀL“ÅŠ^£ðÙU#/V6u*ÝrâQF‹î8G²jkáùe7WrÕrÑ5Ü®ŸGô öY|x‹mïÏÊÂUß}OF6°«G} \?çØh}­£ÔKTO áözÕó½B@Ô€»Ah_߯®çÑè¹N…ZêvqÑR;ÙhË„ì ”ÑCŠ=Ð4:GË‹áéáŠÔä<,[¼ƒæ8>ÙQëRd@mîíkDÛø0û{6—"KÉCÔ&5¼ÔŸÛ­ Tú:Öµ×ÞÌýèìb­i)ȹ°OÅo©Ju…½?2kß>Üù×lXiRdîÙæÉ³Ã7߬ǷôWßžøvSA¿U¿wüùç´mטöˆ¥— Hh@j,xòq²˜Í°”•‰wÓ9^ Üé˜ËËi•%«}ÂøsLFNB@s!@7³¸et/{dì 4‹jD‹ð³Û!HüÚÕ¯SŸKµó5¹°,Y»¿-ÛŒ‚Â2*¤X\jgKI©.& ^ž^+#5ï<òZ͌浭o¿“pÕ´©±YmHÞ¿ ßO#Q5 ÙB@s&ÀRÏ@ptÿvèÔ¥;¤ŸsZuæDš/VýceB­÷3¯3L¥ uŽ@ïèÑ£QRR‚‚#‡Eì^€&ö¢‡†v]ºÀßßÿ¤&I! „ÀÙ0™É²K3ŽÀÜ/Á1Óò„@C&P#Á{çwâšk®iÈœ.xÝ]]]Á„€B@! .. ^5¹4øùù]Ü’HêB@úJÀþ²*¦“(/)A! .)™vö’â–Ì„€hxT°šË°7ý(6däÂL+ÕI¨£x}f£V*:‡6¤"`$_ZZYT‚—ž€ÞKÏ\rB ! ÕÍÊ‹2ðÀŸaÔÜõȳ’àÍS÷®ª¹ixwê¯ø{W-XqHþCÛðÔó°/Ç$¢·îµ¾”¸Á[Qª „@-&@‹¸ûE`B| $|¤Ó­Åmu¦¢‘à­°”á»ùk±:©äì/-nQx4 Ó¿X€ýùŽiÐΔ•B਑ï…ÏVRB@4 j |<}¡Ðøñmwúô’X±ÑüÜ*c,žú±ºéI8 iÑiE\Ç6è\{¢Îs8]^¤ºóòóª*-7loö½æÀ4ËA@TÜÚ9é:ÚÁË3óŠl4Õ¤}ŠInfÎ>Úζá†â@m;´BÏ& Ðrûh)}5Åã™èßãq~ E®àUï×r»Ò~Þæt9 çõÃirÞ5˜‰Á~݀ϭ,[•lÞ&s ¥Ýk$xmÔyZ-ûﲡ€¹ØõÔP‡Ê¼z! ê?^Õ+-'ïo^‹Üœ4”ëƒpO§öˆ§å^ ‹²ñÊÂåð ˆDnn*šÅuÆMB¡¢ÕÀŽBO>rßìJFžÊ†°&¸»y(ÖìÝ…d3­¢å鉯-¶då@gðÀЦM¤=E8OL6NK€ºäÃûv`úG1¬W#$¥äÀÅ'#zÄÁË ÀB>Ø¥…¹X8;©fwŒìÛêüLlM˃ÞÅ¡ÞL̓« zÆÒÔ“•*zèÑhmX½v+²·Ú(M ëÞîF-R·oÁü4W4ÖA>q>UD/H1aíʽH*² $<Ã\°nÏ1:×O23sasqGçáÐó’q§ <‡~*•-99ƒŠÕ0¦c; {³øúºÁÛÛ¢TùîÙ_ç ÔHðîØ¾ óçüIkÛÄõì<›œ»"îøb›ÅaÈÐapwç›! ê;î=½È×< aÁžxsÑ< ß›Žeã‡àÇ¿ãÕGHðæ 'Ý„=JÊ¡ÒP÷ÌÏÃüPLÇvã–ïbÄ ‘¸Ý¿ 7þô=¬Ú[ÐSoÁäßç 2º+^îä·ÖnÃC‡ÃÀD gO€Lði‰Iøvöbd©û ]€ŸÌüŸíºK'u‡^S‚Ù̓ÿÀ¶Ø¼lVdªñr?<5ù ¸Ž·¯öÃäæãª±×¡gëFduµ÷úö¦Tl¤ç• ʨƧï~‚Ù‡nÄ”ž¸í±™Ø òD\ }¹£Oû š8—-|®?LûÓ»`â ¼þÆLÜ~ÏÕXòãQ‡¯§tÁ Ÿoý·õCÇ8ŠÎ×L5AMLeefL˜ð ô`T©Ã«‰Ùv9Ú¤cÇH|üñÍhÔÈ‹*-–ÞúÞò5¼S^| Ìú ¾öá6éFÏí²à~ˆ;™âR3ÂG#®EK´hA+ýHµ‚ ‹Ú‡:¹0ägh¿ÙòUËßö«—>–þ×ÌBŒß<çáU†åÀCÁ¼Ÿ;‰ Nƒ·Ó6µóg\Þ® ùÇQ5.çýxÈ–ÿ88ãò¶3ªq«¦Q]~5)=”÷=¨š×—CÕüN*[u,8C…ª]7Ÿ0 mO<ïëUèùÙϘ›ÓõÇ÷ûþÀ•½Gâù¶ÁPÓˆÚîÔCHµ¨ #Ž~^~ØJ–ÜC veïÇî#¥ˆŽ€¯N‡n-z`6Å3g5n[à‡ÇÜŒ‘¡ŽÄN*[%c.¿SLW­Ç¿qãóœ,ªÆ­šFµùU¶?}œž[Õ²9¯¡*í_5¿êÚ´¦e£ö« 2V^ŽëMÏT8v eߡҢS»h´m™…çïº ‘dMí¤ÃÕïmBÚ]`µêÐÈ<>± vú[ðì¶l4j> “oléé6„ú5ÂØ!=1¶G .Ø zE ß ô ¦G•®ÙCÚx¢³§ ƒ§nij×ÝŽÑ#`)ŽÅ¯ ‡[Y Ö.ߌœ2ZÝ[ºãó5‰ðŽn”Ô"„5n ÿÀ|ðò]Èÿï'xø øøå»1¬ƒ/P¹&Põ´ä1dpK„‡ûRuS8Í õw·š~ßë×'bé’}H! ~£FdQ?qUÔߊ7ðšÕHð!&<?y3¼¼Œtoã\ÂY NÕL«úü¼(+w§“»UåÍü¬’„À…&@b2)X¾ß!J†´Bhîí"z9gÞ.  &+ÈmHò*hJ¢¡Cq!°y# øR\+‰…C艮˜îð4uSL@OÓ0Q¼ÄC”.Ý`…AÁŽÂ'òóÉRDÅnn@)åsˆÊÀ¾ˆ^”_t¬#nZ hÜÖ!z9?º¹›éæ¾o­–EËtó".M›;ŽçæÐ˜t2 :54 ¤H¨U)—©IS*•‘ú6¤ü8‘UËÇîHCé°Ÿ$—Í$å·s»CôqÙGQÄíX‘îØ ühÛJÃÅUËÖ<ôû/Ë…jë*§‚F^Z´vEYý³óq= w÷)8 ƒÊ¡®ÀÊ-kpðHÜMåkÙ %(G³¦]0³-Õ‰¦8³?l„Ò­Ö­i<ÞZ·?+F\•Ç?ÚQžŒcÀQŠË‚.’¹Q;±`ܽã·b¬#&Ì-刣©B*Y0ŸBjCû±ÄY·¤DG›ûR[D4¶#½˜å`Aùqûó·ÓÞÄÏ Û"‰?¨dQ{¦R»2oféKܸöîv\®.ÄžÊÆ×RALmÂ÷žÀj×HG9×VGq©Îœ_)]³|½ÑCWSÚ´Ÿ…xÊa{›6§k)˜|lfRJ×Ίºèºó¡²uoM,(zV9|ŠÕp¡¬QF—K³ „»&Òe©¢¢ëHÉb†„“«žÒ¶ØÐ£_¼ñòÌ\©C@hSxØ ±ug" ·[c:zØ›ÁÆå*·!""!n‰Ð¸¸!4À|½”_>öìOFb^ F´k­‡&=|5zEÒqn?2šÀÅwôŽÄÏïmÅAšîVªë‚ØéIÜ?øP?téB¿oP 6ðþô9Øšp„Ú„® ‚õ`5 *é—ß,ž~Ô)ðNÂÙ –©Ü‚õ;“¡ÛŸ}öçËBࢠ›ö.+ÿûÃ!*bH$†Ð_. 7€$1Ô…„ÃðVvËn'*Ǹì,`ñb€\sàO"„à¶X$ãC‚ "‚nÊ$ZXH-¢xlAéÑ‹Deˆ£Ù½سÔÝÄ)/C%$–—.£{1ÝŒcHh6iæ¨1 ›õ$®Y(áÈã¬]KéÓÍ>˜ÎgÁËÇ33åâ3  ´I¼™©l›7“ÀJ%!íáX®”oaÁ‰¸;SIL²yzÏn`•Ë@i{‘˜`a½d‰CäE“@c‘ÆBŠ…Øê5޼‡ ¦üH°’H=^6^´§i3²,jYš5+í–ÄܦAØW¬Âu.Ô '$aby1¼·Q»ÆS¾n¸«mw¨“fÙ±Á`Â/#V/Ù‚ÖLt:LÜL¥X<ð èÜi»×¡7=;\«+Æâ¹ ¡„Ü&Þ$LYÀ.[î¼£F:Ä?÷ßNn¼¼9?@ï)2Ó©~ÔNl±ïÞ ¨lS„ÉÉáÉ,*ù!f¥[FõlEâ‰E, ‡ÄD€„7}q´·)?(¬^M¢•ΉnìH—rÒ¨-øúaÁ{•-€¸Y©Mù!ê(•…¹q{Ÿ*r¨¿\¼ÄqÝtêHe¦k‘Љ;èÁb¿C„P]øA¤„ïŠÑÜŒ:?4ñu±ŸâmÞ‚NdioL/œñ „()¾]Dœ²€Tß.-ìuÖ,<€A`OF1´e˜ñã&xÐKg!ê2¤¤ãXR¹„ +«).È+1ÃÄù°°ùxùËõØøCo¸Ñ¥óØ×P!)0›Ìdº„ò°!) }¨Z?ÿ™}xü4&$§!3?é…r÷Åí7_eGh?Ï”Ob¸÷¿ô>¾§3ÒÀƒ[ ÞKÄ¢gcükïÁÕð0ní -ógêikØÙݾ7ùˆà­´ScQ§WAü*ÄB~*ù~Ù Pçß‹ÄÁßPIè†M–oŠäkŠ/o%±GÛ»À3(˜Gq’yo {Ù(.[˜}[|mÛQ~UÊÆÂëÆ±Ž8ludQÇñ[µÝºÈeãúq8¿ªe£:ºPç–ã¾ð8ôñ¬À'{£eë!¸6؈_f¼Eåò1záš)Ó<î,œ¯ã`Eæa…¼ù;\»·ÝÝt°¸ Y«3WÌÅ›K7àŠŽ½0D›‹‰K· à¯9xwøptˆ#ÇVg.+‹A'ϑĭ©Ì-Ývn±79ò3’¨å}ÜNý8D+Ï<À?Ý5tjÙ8. ajï¬#©(âüù¯·XÓœ/çül´M/1•úá¿ãúÛ}¨½½Ü鲦éàȲúôCM¡¢vbCx³1C0–.C+•ÿØDlLS0¶_¬#-ÎÓè¸Ú+“îu…y¥ûì| ÛLaxôÞHZÇB‰\#ô„ë¤@e ˆjŽ÷_ˆFI9ù{ zXiÞ8’~‹ŠÝ«þÅÿÄÐ¥ÃâšëqÒÙòEJ"xåR žÝu3ódžµ$ kW"â¼kògkS¥š¥3 s „ªüHØý#T#ÙoЧÄ>›¸|* ”SÃiÓ8›¸,ª ÕåW]=øÔSâ†D œô™\1˜?¹œ|®Z‡Ö!á|Àx¿ý¾ž’†»GnêX)It±Ok«ˆÆ'Σ­({&Ž´kX6{§åV ‹³‰Ë‰ŸR3æw6q««ß™ÊFBQCçðõ{¼ U‡¶Yøª,øÑÕ‚w•<,œ>Sî öŸ ¹q5ìÓÍxÉm_]ØŽZ­Þ„žýüZãçqôÐWÍï€}Ei¢jo=|½èËNÿ«ù 2«]þÉØêLVl7#·——ò´ß½i!{|…4»=‘SÏ”ïB@T¨‚×ù;®®#¦Bà< ÐŒ_‚™·“\h¨ºRÔžg¢rú©X ž®«N¸z>ç4NÕí§žË‚O¹°³Ó`Ø7aÐí ¬eVx{‘¸ªZU¼ömúÇjÀã¯Þ•ÞFCÈV[güSVMóÔcÎï§¶kÕsªn;ã˧'¨‚—’hÜÇFw -!]êÀb[:“KM]ò»ÔxlÕÜÜÈò$A4dÔß{:JÏ}Ôù·ˆ"´ã_ï*¸y’+ G¬*Ž2G©»¨ej·à¥§¢¼›v%#ßæ‰]" ¹„¢×FOÔ<÷·Ž&"§$ B ž “a(ù¬.{”ü$YðVua¨§U–j5xvµU]ØÀÁËú~9‡\èËkN>þ¯Ô8½té#P«VóÒ@r—Ÿ@í¼ÜÙP¿c¥¹çÎ[€M¦æèÓ-šü¯hèõ_Ÿ¶Ï–;#>ßùYmT²*W˜±cû.ü´9 7]7 -|Èv ÌÕOv M€ßÀà—´ø"?uÌüBg&é ËOÀLó›L4;ÏÐ@ iYð öƒÞÝW÷oÄ•etœ|ªè\^“^MñÎ6ðÏó|owˆ^²Ü:Ó¡4Ùm‚¿ÚŸüiˆ×ÓÛMBÍð ‘8œ”?•…ë!AÔ}t}ó¢»ÓÛôôÖ|( _¹¸ë~»J ª%@—{!-Ýüà›?ÂÇÝÕ.t9O©"C ÛZº§Ñ¼¿ô¹vÂ;õâ— ¦J¥fäÓÏœFoœ÷¼jáÈN!P \Áë°–&Î@1‰M½‹;Üh­òÌB¨]Œ$(=q8é’3Mð¦‰ÆÛ6õCâŽ]xÖ>\3¶'šUÑ|älÝs€ñ)EÓ˜H4 ¡ibjì+E® ærl^¿ÛJthdDn‰#¡e¸Ÿ]¼æçæá@J6Ê­jššÑaa¦ù+‡½áB¯ãäcç®$XÜÐÓj;ÑM#ìFøD\ä_Ýuìæxç§3;þ.áÜ T½pé 0'¸å ÐÌõÀ£Ã)ÙwkàÏÊWÅϽr¦¸”蚃ÛÛ·§©ÁÊèAÏaà(¤EE–&%ÑÂnhF«ªm ‹°—Ê£Æ÷˜KY‰sÈ‹Dn\p†ÐÐÍãx:¹æ›sHMNu’ÀżÔÁX-¥˜?)¦þ´Cïºãâ4xö•ßÑúê!\ŠçíEZ»|É·+1fü0ä-_‹Y›²PJh_9jbhÞ•í;÷âÝK¡/ËA¦&3žº´¼yÍÝ—ËŠ ðë¯ËðC:­%Þ&…ÙéÈ1¹áéÿ^X—"|õÛrìϬ€­<)Ùx`ÂÕ°¦îÁë2ñнW¢hë*<1s% 4ßfdh&ÜM‚؃æÞ¼„>Å5«l]Œå¯üÉæwçwÂ[aB­öUPXF«YiµÐ ºž*hxŽ:쪚­.Vû2–YCó#ii’}NMS“êáž[ Ÿ‚2ÙìÇËmÀÛUÿè«3ÐîlZ¥jmr²sOýþdqßP,cõ¼®ü vg|üñîCE£yIdðØš†›oîŠgþw%@òuO¡êŽ=uú_nZÇo›Ç;%†Eàâ ^šFGOëßq÷•È<šRš½e´ : ˆgolW'¿Š:4 7Ò"7Z\?¢Vgà½gÇÀ–¥Ü²ZADTc¼ùØ ÐäîÅ#o¯EfY‚x óÊ~éÌͦ‚+­!ÅðÖ(9芩 AöÁ½øðë…ø{Õ.ô4ãˆ.Ó§ @VÒ>|ùý:Øh½tÿOx»å  m²ø6‹ FûÞq÷ÀXxòª<"vÏŒýŒGYØòåg#![†âb3RSriõ×ÃØ½;ÉÉÙȦQ€üüû±2­PG¼+Hñšðü'áÜ °Ûs²ðUÓ]Í0fÁsêBø-ÙÐ0o4iˆøøp´hBk+¸ÐÊ¿4á=Åtq±ÑÂhF,Ü´þùæÜ Qδë~rÇ¢~Ìb±TNî_ ~nedñ§£ÅؽÌá{vnéÔú³ÊÏ^Lnâ r£+¥ÕÛ¼xQvW{ñO‡.|’V}«·Ö׫F”¾³F˜$R½#pñ/#£›…Nï…ýZáÑ_c¶µ9ÚµkBÏf˜h5£±× Á­iþOrе’Iß•B¦‚“aŸZz»”&Üðñ‚¿‡yY*êµv?Þ³i ^ÙDFA^¥FG›;ûç6FB‘…¬… ¼ÝÝìKÚh•- \-uúa¡Ahäv*šáÛ^èiD8´q½ËnžM $®ƒ ] rh}Æ#ؼ) Ë—ï§%îS—WBLO/xxÃ;Ð Þ´ÊWñv÷vƒ+ùÛéô:è¨}ø`; çF€×‘·Ð ;V~q§Ì„â‚•”¡0§‰Çr‘°ïòØŒrÚ§#§÷æÍƒÑ³W,ý5¡ßnÞxs4Ö¬'ÿG[½~øPSß3gÎvÌ[°CLJo€¿ý%þs£^»ÏâEr³²1ïû0tpS Þ†Ú¶ }“t1h1rT[º_Ñ‹j+öS7E@¼µ»Ý¤tB@ÔŒÀ%¼4PMB²E—Öˆýn!ÞXí‡?‡÷%ÑjB¨—_ý¼±î½¡Ë?†äBOtWU˜ý‡³áGËcÙ_2È**‡‰nÀEåÅ((-§É+ç=¬A]¹ÏR“…0•Ö<Ϥ¡Ṳ̂4ìK-D—ÞmàZp»7mÇŸ±^È¥uÎÿ\w>-2áákBåYXl‚+ù{—"‡†~KÊ-pãÎPÂY°·¶ï8‚O>Y9& -½ˆü¸ ˆiÙ˜\]F¢U· „‰[÷J‘kÔÉÉÐtüçÈ·%œ/z”´'ÁŸÎ­r”£„®óbòY/&ëzan$Ķ;ñóœýødÆ:zÑG‹.]¢1ñþþ6¼¥Á–ßú*Œt($+×Ãÿ{-¢£àx§ÿ|é×¾óÙClwbVÎþ=»GãÞ{†ÐžúZÛêøs¯B¾ë,ò}iÙc~6— „@½!pi/ã"KÁèq×ôFDQ8Â}(kz1üºq£‘8c>^ød6ZÆ„ãæ‘Íàj@ç =øöÕˆoU­Y¢ÇÆi(-E ½h–œ”10¨©sª¡úá‘©¤ƒ‡ðåtƒ6•¡e·.¸²s$Js šYŒù‹6ÂÏ—æûí…ƒ{BêŽÀF¾HOLƒJSF¾»¡°eg`wªÚ7ö¥Äªf¿JhíøiÓáÅ)Á@ÖôNC:â†Ð劎ð ô³K.¶Èfx\ಓpé°ôu£3Ý©Ta)Ü¡w<ÆŠ^Åî7Î#DåÔ÷”R-ë«dÁËu亲¯¼½ƒ®·µ¥ê.ð⥫Gë;Ãé*-û…@ý'pé/±d£hoòÝíd¦ žß„j`DÞxâä“ÕÔÓ®<˜ÍˆW¾y&^®°TÄ@KÃ×òßԪȶs(d­Õ©j.víÓ Q:õj‹[‡Ç“÷¨üo³Â=0÷Ý< åehÉ⨥tM&^o- iÚìk»«Ð§;çK/¶‘ŸÛ¹L‹Vÿ/§êkÈ+ŸNºÏ¿07=~=†ß6ôp£!Âfº¡²Àå@N,Õ' {/“-é'gË/¹uêÓÝútEΤ\lY²ÓOd8@IDATÿ£¯þ >ŒÈH^–XO¦&ßê¹1!•Å®¯7u®U¤ÀB༠\RÁË¥Õhu Q»•fYjZÜßÏÕ±„,WOòÙä Š§Ó9Ö9ÓÛM¹Ô9ƒ3 ç÷Ó}R¼’ôc˜»2 ²4˜ˆÛGÆÙÅ®Ã:ÌâV £½ºc/–®.U×V«ŠÉ^øå?]ž²¿’€Š|s‹ñËÏ[Ð}d7Ü÷âÝvak¥¡CzH(Õ1äõKíF‰äv2xô ¸Ð~“®x ‹íÁíwô¥Úˆà­cM*Å­Ž/-Ì!&Àñ)ÿ !Pç TUr—¦2Õ‰TÞwêþ“Þ¯*Œªn×°Èôâ›§_¼ï:L¤SÔZèÿáŽ@éʽº†@Ï&ÍÃLmi¥?<}=1Í´ –ܳX+ãr;òC‹‹Ñ…\•ô4Ï5[ç«<ŒÖÊRK¡„@ ðÔÿ~§]Ï_ßáø¬ÁiEÚMàÒ ÞËÁƒú-­ÁH M ×' jÇnù÷âàQBE˜_<À—)eö÷dC½¸:^¦l/<òÖ–Y`.[IU\&g%xíKÿê\8Eä^Ž:Pž2•V»p¤ÀBà’àW6ùz*{ËkÝq7ÅÎUÎ}´y|›¤ø¯ÒÉêø¹Î¸NøªçÿûúyœCCD½O3¼ ý2ú×;5¼dÂáΗ½]%œ+šÀž(Š5ìlø9®»S'";Ó^–Ž›>_±§ §¦xºxÎý´Hý§;Ï!q"ð~ž6Í1·Ä‰ý§n©I˜¨ìîUÏ>5VM¿«)W¾š¸4'‡Ó•óäXò­vP‘¥üèþ(¢…wt´Þm©ÅÍÚ6AIÚ!¤e¦ˆEM‰èâMKhÖ“‚r„´nO[vî97=reÀÅ·Üè凒"+Â[·ƒKùQ:˜NKºk`Õz"Šf½©ñz=µ Ñ¥+ [v'óòÚNý}9öÊ¿B@Ô=5¼¥ef$ìO·Í}Ë>NΞ)ÝrZ1ìHzžciܳO¡Áa³’§(- bpáånOHM+­ŠDærZ¨âį†e&¾:ŠËbÐFq,ä;l™¨BŽGá5z-45²¤ÔL%Ø·#Æð„¹ÚÅ/-rL‹§”Ñþ#ðˆhŒF~†*%¬’! bk~&öìÏA£MàënŸÿ£j„ãÛ,WY³ìé„1‹ýÌ}P¤x ªyëü=’¿´¹ ‰ Ið‰‹…¯'¿æyºTŽg)µ€_£E‡·ãÉÏ¢Ð-÷¼:Í>¢®˜J±é‡w°h½7N†Ýo‚yÔ¸ëÙ§IðÒui³ cëlÌübz\;iMÆêr+F]ÑŠd`YÀJñã¤OQÞ w?ÜÑn5ÕÒ‘¥Ž_~JÁÈWÅÕ]Ü1›¾ÏúùÍ¡ÌËçÒuK>Ä6R»üÝÐň¡•R:N"Ò9=šãÛlbQCå0Ó’ÖoÞü :<ûîŠ ¶#¢w7ØÒñúí/¡óc/ࡱMhÖà :ëdk/-ÖŠ‚ëñÜøðÀ/ïc`¼MÇÆR”íýœ³ã ¼6ç§aÝú|tÒŠÎbÑË18Û­%¤u±jÊ4,-ŒÇÔ?'Òb¿¦ÊIXç¥á£O Çâšᔂc’ç;°#gÏ\gÝOßr¤¦œR‰ã3wv-`ê¼ßùxÆß«º°¬j1xî<¸ “§mCp Í1Nû‚bâàçFsƒß<WŽé­=±¼ýD¸…4A Ù„¢q`¢zÝ»ÿs'ŽvõÆúASàEçñz .ô ¦1 åˆqÅÀ6öEð !}S;ØÊ͈‰ä#øZ&7S9ŠK+`ô1ÚE‰©¤˜æ|VӼЮtTAYQ>l®¡xò—×ኌƒ;ðá?áÖï[—gÐÑÜÍF£ÙÙÈ)5Ã;4FÙ,}9ÝÞí{ã•Y-ÜÔå6².©€»‡9i™°¨ô ñµ×%qþ_øvF¢»“ ¡ã:J%;#eeV}}ÉjK*‡RÔзžh1ç#'+®þ~ð£)ÂÌtmi]Ézm÷›a©m¥ó³P^®À38Ú¦ó)ò³³PZj…æ¿öõf2Qœ½ÁÎãîPSw­Ç®m©0xy¢<+a} MT0ŽXÛÂ… jCÚtliwOpfÇ3¾¶¹ù.„=7 æn@³[{¢,?;v¤cÄ]ìW“_³ôàç‹ߟ…¡=‚›­+WoCüàIv1Ú~$†ôz ß0½Þ¿zkæm(@Ç»;³‘Ï#À‚wV‚Ç÷Þ>ާ–;GŽ !Pë ÔHð¶nÝü'A\ZduUÔˆmÙJÉ~|ôôßxqúI†ê]H º:liì°}Öߨ£k ¯‡â‹w—bOÚ0´ŒïˆÆñì;›UÏü ï.1ìºATÉH‡/®šìhé«ÿÄ[ïîÀ]MF‡àr|>ùMl;ê§¾™„mÞ{ô}˜›C¸QW^‹”Yß`÷~û‹ÐN¿…ì¿åX:m:V=—‰ì\búõÃ>¼AìýC¹±¥:oç:|òür\ÿÖ“ˆVvâ…;‚1@#»RPFeèvçÍ7"Ÿ?õöfèðìU_᥮ļ7>łٻQ\l†Gh(®é^ŒÐ’HÑ qá L² Ç’sáÝwN{]c6D-˜¢²ä`æ£Ó±pá^—Ð".M¢0îõ‰èÛ!‹¦¾‡ï¾J@^>-›íç‹®W݈»ŸîE2øô®—¶í/Tnlce&·ãÝñ RýÃ;Çãàü¤GƒJ·™S]cñNüËiòÂï0åá/1ôÉaØ7S'ÿ„Éë6Á¸àcZ‘n:í_¾ˆÕE=ñÓÜwàIç8KÃ_–¸þ¡žxkÚLŒßù[þ@fÄ´ ÕÒã½ ¦vÃÀ;ïÆ¬qï`ã‘û¯ÞŽ´£ÅÐ-Ö~­i\ü1èÖ[°à¿ïbûó㑾êp´iÕÈ~œ’PÔ¿È, 5%q„@Ý!P#Á[wª#%­o*h9[ÿm0î†Nxì†o±ð¦®¸®Ëg aW‘¿>Ù‚¦GaämñXñÂ,[²-nŽ'ùÆÊdÓ$ÿ^»?påw§ÈàÁ|ï˜@ì[¹Dl.Z{”b;Y×¶¦ù"%©þniرhz<Ñë~Ü Ã@O ¹1s7˜qûk÷ u4-ÇZT‚l«Ï}õ lÛþÆ3üUã¯ÄýI ³ä%Ku^öm9ˆÂbZb›ŠŸ¶f+´ƒÇà…9b߬ñæÿÞCב¡{ÿ8dnôÀÃ]˾­XõÇ zñ1Œìé‹™‚÷&ÍEŸM-I„YÈB›ƒë§<ƒž€çF<‰ï¦ÎBë†Pndu6(Øüã¯øöË]¸÷»çЫ >¼ó9|0ñ[4ÿy¦½¶½&OÂcšbÍÇbÞÚÈ(î‰pwövÒq2®kŸ,V¹kS¡¸¨)©ÈÉ)À¶­©Ø³7II9ÈÊ.FaA ­¨HËhXiñ²Œëh%977ø¥?4Ä 1±èÜ9šÎÏ#UJ®!´Â"/†Ã-êœ6=º,ýsBny7Ý4‚rïˆU¿o†º< ?~þ=âïzG·ÅáΡș²Vs1Y‚7 §¤®Ê€={À—¬ó]Fß÷n¢„)ÀwÛ0øŽí#œ'‹Þؾ#É%ê ü5ó/øµ§%ÐcÆ¡1͸h¢c§Åð«áýÌû˜óí| ö߂жW!˜\àe‘n‚S“@íŒiia¾Œjìç_“„%Ž—“€ÞËI_òþW<2o.5!nô͸þŠeøø¾è¾å!Ø—Š¦³y‰è¤Õ˜ƒÑ¯tG ·úßÑ ?¼µ7Ž‹‡§ÓPwšœhI ¸E¶Á X+·ïG’G9J ÍÑ9¾»·†ŸÏ~d+qèÕ-ëÉjªÑéáÑ ôÆ»§k¹;xaàCw g›(”FöAдÕ4m>)G•†Þ¾7èèþIÂÊÕ^~ž=å:´lB>˜ãG`Þg °}O1âÃ=á¶Ï‘MàÙÎôÀ½‡±ðÛ½ÈW¹A)2“¨!çƒRqúâªñ]áEßo»½ ¦ÍMF^¶Å5—b×Öý°#¡+>Š„5ryðBáÎ-H/(7 ¶ÿö'æ’uù^ xÚZ-/&Q—Å®Ãâ_VV޹s·’e{6oNÂÞ½Y(**£‡j»à„4 …Ÿ,b£ýàj4’ÐÕ“_79ž˜M((̧•É&á(þž³sŽ·ãËîE·Ñçê«iÔ:è LÀŽ À¢Ð²äfn#]ßž¡Á°™ŠQnQÁÝÏÝ·Èœ‹b²«­&JXƒ‡Ki1O„uê o¼Íû£o¯vøæŽ±hÞm&µodºÇsq‰À-7 Ä+_<‹6tÀ-ŸŠ%›öÐŒ Û4¨‚hƧ°á—Þxñù´³»y{èÙ€ìÉöLÈÆzF¡Í¢Ò î´Æ¶}6J‡GÍ­f²DSÚöŨT©›—âå1_‘‘ˆmŽ"šÝCq µÐ+$ô .Þ$ºìsE&F\¦Ê9 ½Ya9Œy3H´UлBƒ¦ÝÚ@ëçþœ„·'‡y|…¯Ÿ.@·{nÃ#ÏŽ‚›žmÞu-0l HÇwß­ÇW_­Æ‘#yðöA\\+Ü}wW²Ôö$¿ùæpqq…^o ‘«#¯£¶«l+ÆÍÊÖ^ž„ů™„knn6Ö“p^‹-›ÖãËW^ÃÇ/NA‡Þ}pýý sß¾ð"h‡uÕmâ{âÛGŸÄïÝ¢Ð>´‡hfnîôpä/¿FÅC±üÃ×Qêr 4î~ußÿŽ?b°´bö•Wßy¾ô†<> ~õI‚—ÅtüØôÜç8ÔeÚ4rùÇñ®ãÆWÓú!sèˆö —é gA “¬ú=Ÿò¯už€Þ:ß„ £ôâ—_óî¸ïƒñÒ³Sá©3¢ig YÀÜÏÐgÒŒì 3McfЕãë;ßÁâ/þÆð^÷Úßv?%š¸ ±ÝºÂôö·øimz?5qM}ðíg?â·Òôa,\]H ±Šä@Â’-̲ØjI0ñ~»v­Á¿|N>r³K©l!(µYQ^JK/{iäœ$‰_’óØ·z#2}»àÛUO!‚$Í7ÿý/6-diG~¥êÿ³w€UTKû»ÉMïÒ{„^¥7AzGAQAT|–'*v}úðá½!¢X¨H/Ò{/!$¤‘ÞûÍÿÍÞ\¸„I ˜ÄØìÝÝSæÌîžóíœ93%ÔZ&h™QÃ[J9”Ю™ÚJò$lJ€“ò2.ÄóêŒÿ­ž gãÌŒD¤çÖ9ˆ7iŠ—} MA*6|úþ÷.AÝGmoˆ5ËlH× ©©™˜3g ¾øb3j †gŸ‹–-Û"(È_¹…òq"(BÆ{Ão9¯ÕRºÜ¬¬ÄÅñ= DFRczï}”w SC¾}û&üðýB<1l(šµoɳf¡×ðá¼ tœøo<–]†Õs_Ä™vQÈÕÑö¶Ìãÿ¬-Á]'ÑrèCH\w´¦@)]1T–¶ZÏN#pσGÑ¡kOaã2’&ع…cØÌÐt»k½¦ÙHÊs l‘O<—! §Õ}u% ïଥzs†OîVͪ+75*z.ðÖóôOgOWZŒ’bñ Æt™q/º.݆•¥"¸¬»?_‚ãåÞxøá!èìcÇ4Ô–ò_Ù£ÛðÌœ-8s'ZùÓ~± ¨Â÷ñ•ªWîap¶Iž3N˜Ò&>ž–0Ë:ƒc™~xŒ ‚4%ÇPTXÈ©i½2X#÷äYüþùr” öRüýñ¼ØÎŠ­gQQ1ŠÄÿïÅ›ÇÅwœ¾Vx Ϻö+×`ÁýŸÁñÝþ8ñëœÈöÆŒvžÈ9`‚¸ã»ñËwë`YÎzbNaý÷k ‹Þ…_改?þÚ±ñÉE8±j=æ½…ޡŸdþ„M|„^h/ÌéûBkÜÖ÷6ü0÷c¼õœ'nïâ„_^ù)M¢ðÌ3áxuÜ|4ð>ôèà‰¤³I´ƒ¶…5‰xð"çõû‡)6o9G^„cÇR1dÈ(<òÈL„…E(^3øíÃ{Q»ƒ`Ë%§ææfèØ±£²Ýsσ4›X‚ߟƒ'hâ0zút<úßÙ°wpÀÈÇß@¿r`gUŽ¼ã§©MÎÃâ7ŸÂ¢ÛðàÄصôS¸µ@wc|\®Æž­zÿm"p‹Ë´·†äôEBíïÛü°Ÿ%WR±©†¾ð]ò©ÚÝ+¥S3±é*Э†˜Ô$ª’TÀÛîÖ?ŒW‰#&Þ: *%„³†2˜Z6ÁÄž…åGÚÆ9§›à¾Z ÄÛŠÞy m™ø­m>q FžZŽ‚Â|æ²Fç©ýaÝÑSÍ•Å(“øæNMqÇŒñð9oŠ @h­Ý1è‘ñhQà ?+*dÑkÂ@xFÐ.3´¦<Ø{7îÀ©#Ð{To¸…‰ž•ÒÌÐodØðØ@¥°õÂÀ‰ýáéiR"(S3WÜ6° V¾³Îxò»ûÙ„ë±ÃÐûP¶ÿ´÷¾6“Ïý€õŸü†&‘axeÉËØýÍØ¿œ>€{ÞŽ‰íJP»‹~OAûI÷áîY`nš†öÂÇÍ]âÅoÊðË·[±`c9ü;÷Àø‡G"”ZܯÓÎuù.|³*ö>~xüÖïK·flCà -~ýu/5¯ h—„¯¿^€>´¯ *µ  êVˆqY‡$é*›È5A :æó†Ê$Š?€¶³³ÃÝwß‹ý‡â£þï¼û:ÎÆà•/ÀÕÝæ*æXóîÓØ¹}RçýŽ©ÓŸã†-Ø·kì¢Æà±1#Á€i×”¶)Ánl(lÉy­™Þf¹ª4†ëU]«Ü.õ¸²xcZûè”K_­•©ÇªT 40 0¢¥±.£q¯²[+ ,ür!fÌxK–NE_:£×¯ý®UQÕÈd‚””lôê5~[â¹Ïg^‚‚×-€#¶ ÚÆàD­b«ª½ÆT>áRiVAà„Ž>$¯Œ[×üE?+zäK¤Ïct^aD®K ˆ2jýèÏ–ÓßJÉÆ V…¦„ž7Ñh Òwá‰Ns1iÓ'4! Èä%+Ú“Š«4Ử¬DáÛŒçÄÃDq1ë17ç"( µ}„¤eLES Sš3pIŸ½ÏŒ6ÍZwÑpKp=;\ ÇTE¥ ‡A'iÌx]ôår¾XDzJè¡€‹¶,¸ ïZÑÝXÑ$íÙ²Oš…ÿ{s8¦=Ø—i._¬wE¦›rB‹Íôª1pà{ˆjÞ |ø|0z­ÞLîg#Þå—у¥–f y´s¦-øRMsˆl˜Ù:Ó?¶Ì™˜æ¼×%SÞ~Ù~üñ{<þ¯ûÐþö~˜óÓ0§iIVJ25ò G­ã‚AwÅ´Fž|Ñ2ˆ·™Y¨O$OÓñÃG0µg/<õXg<÷<=T©_®O\×/´§WˆUjŒ0Ç»ïü—_^†ÕkCûöAld Æ%E$føóσ9rÞÿc~|OlŒ‚jTmªÖÛ,+˜K8§g •nqcdà5å()Ûõ|{ÞbÖê_uV…âí"o¢ë¹ <ÕËïr * Ek+àQ£Ñ?µ±ìÚW_†q*=¾5*[A‘rVBV0¨ƒ™˜Èq% ªB^¹D«þHÇ ªKi»«åâ))Oï>MÏ· =:H‰¢Ñ–²ÍÌõà]%å¹Ñ×JÈ­\7·°Ë²xMHÀ®¡ôÀ³¦²8KyÓK/Â9oBkÂ@BrÜ0È„À4=öšºàþ!@õb`ës/bÉL:†ÅŸ~†½\ØgøhjŸ~YŠbÓfè×Ë;i¦nÙ¯¾õšÑ——kçEˇ„h~ÇŽGß,Ìœ9 KçNMúC°oâqñ©“§Ä`]Q }ÕK·Dæz [ÂZ‰*Uµ@µïÙ³g±wï^jޤÛVéF% €ÄÃÃC‰^gEU*][U=uF0ôª™/OSU)Wf­œêòcã#ãÒÏK™•õõÈY1Ó°öÅs[fÃ5XlŽ ñØôiä¯qÉ•®¬/áRiWæ0\»¼ÃÙú¾×býúcìÎヾ¦f×ëªæ •["f ¾Í»bpï5X?¯ƒG†e/_‚Íf¡˜2}†vk‹ ãîÄ«ŸwÁϯŒ@Y5€´t‹bã{×]÷bÝÚåøöÝ·1tÒ}°¤Ë³ú¦Å­,õ¸ È ]s@?­Ô7R?=TE2õ”*U KÕ¼o¼ñ~üþ{8œUÒe5¬ÖþÍÜônùT ††báÂ…\`ö7sU÷ÕËø!^ LÄ÷¨òÕ}õ·†rzw°†_$}ê*šâúËéõ8£.™j}R3/×+ùjשÕ//ÆÚµGàææ‹AƒFWK³k\šD 3·´…¹ ̨ö¶°°‚-û5 S3ž3CXûþèÕ ;bÏ£”/¬á5.£ªß¢åµ··À°awaÝ“°oótëûEzUyÔsõTÒaÍYÍÉÞý>zà[OYUÙR% J ú¨à‡3}z¾Øµ+liKÈî@¥ZJ „`wMt4åæÒæÐ0ÉYËÂH6KÚKº8[!úÐYF·Ê†W²WØz6L-ã ¾aC]ºòá"û{N¢(¿MšØR(uß3ˆŸÜè3©4cð#X墮ZUy)“2kÅ—¼T:°çöü%ûÎcЬNÐÒö &o¨˜74mêN MWi‰‰\*Ù°¢›‰qØ3ëMbnôWó«P% J ~I Z€W:@ŒŒäêclµeêWËÿn(ÇbÎ}fÒ©}tBÂ?Ds@ÿ²ö6xü‰~¸çîÏñ`÷Ç0júôÓ vNv°4±T¦ðeÁ–üS©þI@€­¸z“r púP4~|g 6-ÝŠaC[bÀQd¼î퀥/²¶±ÀùøÜ›((¶ÐLƒƒÛ×cçÎS˜ñêBÜ=¼= Ö¬ ‰H[XT@ó†"lüåg¤?K;í†cÔ`JÙ¦$]@NV–b—\³Ö7¢Ô”ž oüVI•€*F!j^i©Dq*¤ ÃVŒÕTÀ[ë›/.©J*"tÕº—±Œ+YÛÁñw¼úò¯x÷Ñ÷0ï…/Ðáövè0 ¼ƒ¼àäÎEHM ©èA™ì—¿—þ5¸&×€á¿CË- ¶*’óòO¯¿Õ› ” îôy$žIÄñ½§°yÉE³ëçë„gõÇc÷…,ò©kpÇørZstîˆåËW!:ú$Bi$þvkBz &[Y! öanâŠ1“ƈ;5Ôº³›«ikDÃ{ðÐ^%BÛ‘mëqx뺫H¹&ÜÞº´¢÷‘¸7±BHhSþº¤ ¿u\ÔƒšäÁè#pB5|¸ô™Ô¿ªT ÔC TðÖCÞU–”Êлw$n»-{vÇ`É’}XöÛlü~-}ëZrE»#šú4O'¼B¼àÃÍ#Ð.tçdi®ØÿJô0+†­A¡‰ªîñ„Ø5[ÑŽTË¢=Õ»«; !`Vv1ƒcÈt¾²Ñ¾ZG´¦c°Œ’’R\8ŸŠ¸qˆ=ÇýyÄžN@j| òÒ³`©5Ám]Cðì÷¢g¯pF=`$  ¦ð°*TïÜí·7Çk¯þFW@oâÃ?§· Ö^ÍêËÈ$?7›šØl*‘Îò‹ §KÓ»wÓ˜ÕX³+\ÓÒ‹Á/NbþgïÑŽ·Þxc$%]M¦*5ÛDDÌ%¡«ÉðüW>oœæFË“§å3äÊ_ÿ`°ÇgC! uˆÔ Bý«J áK@}›þ=l@-(…¥¥ºt çŠÙoŽÄ¡CñØ´ñ$súôœÜ´Û–lDvv¡̤qæ\Tdëh+j͸°HK§f²XÊ ¢k@¸ŒUNk4뉰Ö!ð –Û/ÒÖ¦²º_hCôÜB7C ,`WGßÁ‹þo1ö­? ÜRÎØ#;-›~c³‘›+t‘&pr°ââ0[„ú¹`p·6èØ)€þ”Ci§ê@Ñ‹;3áM\£ÝJ*eÈ`_FÖëÃçgÚ¶éˆÉS¦ÒTˆÜ\ޝ`JÖ Ú·¿lˆE§BäX‚níÜlîçœÝøvéFÜ9¤/}"³¬|sØMKKÅ“OÞOšÿ¾ñ0""}XmLt~“‹ŠÊ¨5+`#´3¦)‰%ÝÕ®l%{5ÿˆ0 uW3KcI&î<žÿEß¿°jðýLc¹/j;T Ü TÀ{ƒT³×T2ˆê5GfŒÕ¦M·`žÓp ÏGrrý¬æÓ§i!òr ‘˜”…Ä„,å|.‹ŠJ9ð3Â÷5B%5eóV¤'`OMÍÀïïý€ïòJ`nkWj¹ƒ›¢e÷æhÕ³\Üœamo +F{ã?Ñß­3A_nfNì:쌜‹­l×ÖFwEóæžðõs†••9íõ-ÐÄÕMp¹œ‰›8Ù.ÝCü ¤Ã /Á¹s˜õÜ ¤g¤áᇞ„ £›]˯€X€˜öJgXÓ·q^NgLñòì¯a¦Õ! “®•«ýXIÀ q£|ìØq<þøìß¿ _,˜L°ëE™ÔÐX‘¢–À9“Àù'<ð@Wtéγ-«)’’²ñÒKËðôÌÛÙÌ×èš’YýsÓ$ÀåÀy½—yÔùΨ¤J@•@×€ xþ=là-0€(N¥Òˆ——37¶Éx”‘Qǰš+Ç Ÿ„eg`Ë–3رÞ;ÆáÈÎýØðÃ:~”Qëë…¨.ÍЂ›w°7ü"|àæáΆ ü-°ìõÿ®' I¥Ñj0}öTŒ{r4mq·bïú}8¸ùvï9Çútpt´Bp°;Ú·óƒ† û’¹BMü\“½^kkK|ôñÚÛšböìYØ·o'þõ¯g1©£ò¨ˆ5@e3ybl]`ÇGKänW±þV& ”c^¯dEp£’V6º™™9ŒV¸oÍy•ueáÛo§aôèvRÊù®B‹ó4%¹çî8z,³fÝÁ,ÆÏ¸Ža©päpFþß|3™~¼™Æˆ¯_ƒš¢à¬ ÙUR% J I@¼èf6ü¦Èo<È7ü]¯žhÖ0hP+nm™¼‰‰™Ü²päH<þ\s ;¶íÇÖÖ¢Œö¥ÖÎöð öBD‡P´ëÛ-acg [[ê`µŠþ÷zvÀ“›8aÌ´‘>m¨² íÄÞ“XõÕŸxeöŸxõµåòwFÿ;Z`ü„ö q§§ q;&$«>Ü£2‚skÌ›w/ºuÅóÏ-Áˆ‘¢ÿíC0é¾Ñ¢ek^·Sì{ÅÛ‚,(³‡šÂQÀå÷˜ä"99+Vüˆ >¥‹´ÃЇŠ7ßœŠfÍ|XCm¨œÃ¤û¿d@XtèàÏ>G–eÌi9í„-Ü?ü°‹vÂâ£&`èPyfêË=!+Ćúl Ÿs±ƒQI•€*F!›xE;RÊÎAVv›ÖëþAÃ!Dô\úxö7÷²ÔrN˳P õ[on jiUòDÊ“£<=ŒÂçÈÍ™¦þ¸çžÎÔ>êpüx6o:âŽâðç‚ß±xÎ÷°´µFh›š@D!ˆ¦>aÞð;`s½°AûkØ$(šaˆðÕÀ7ØGÙúŒí…¤¸dlúi v¬ÙO¿ØŽ¹o¯A{š;ŒÝ]º…¢sG†)–PÆOáýï"vùþû»£¼÷ÞŸøþûøí·ïЪUG 0 ­[·ep—ðövWÞKƒÖWú,Ù*“¼»B²—M°Onn)Ž9‚“§ŽR¿+%üpz*ºu Ä‹/LÆøñí¹øPºR¹5%3–y÷ßÿNJR2{ñþÛðæJ³ˆr.tVÒÄÇgâÞ{¿`´¹BL˜pÏ 8®¢AJjõO%ÀE´zªÍ=­qmjUªnnðÊ ï¬.xÉË後 ¸ØÙÀ‰}|2¹òö>”QæaïGsÓË&ί7ÛW"À ŒS¨ZñBzm’ÕÞ‡RÒájï„@úž•õð*©¨™䙑M¥žÄ+EDµº~<¡¡–1ññYHHÈÀÆ '±Ûª— '·z¼p¤ÝohË ´ìÖmú´‚o;g;ؘڰTaÙ%;`Ârl w7Üýø]ýøÄŸLÀáíGÍï ÿY3Ýr„¹`ÐÐÖ7®-šÀÖÎŽY…׿ p‰¬J ]ðÿ7žf ý°yó |þù|üÑ‹(*ÖÂή ¯M5Bÿ€ x¸{ÁÅ¥‰¼Bd*¤#ÎÉÉBJJbccpöì).¤¾â!¡**Gx¸˜³è)33“'…óqé˜ùô@t)óÒscH§îk(y¤öžÑñ´ñ­af5¹*UõUµ¼ì[ϧœÃ§{ÃÞÅQNÖ8’‡ãyV˜Þ¡ Z1²–iU*”¿C») ødÇQÎ;¢¤0%>˜ÅÕñö‚b¥ƒ«‚Êt%8™‡Õç²0ªC+øÒ^PæDi Y1D^ž)5' ïÓþ²sx L‹´åpv•‚/Ϧ©¨†ÈèBÉ‚²à`7eëÞ=œ ¸ÊÁ.ý;bpøðyj`ëÒM(¡°{€'¢º6CóN‘´öB`ËúÍÓÓíÚ5Addúÿ B¡ô’@G½ ]º''ª¹`ªÁgŸ­ãb·‘—w¹}tpˆÜ›ªäW®,*4á4»!”sQQ žyv)µÐÅ\À7‹ eqaUy«ÉššŒâã}}ñ7½9ï«f ê3¡J ‘H v€—DzF4ûm9Â[ôŔ֡ =¬AWo|´j5]¹ GõG0g>óÙ÷Z²ý‡h ” +€p©ôËrNúé¤ù_œqøaz9_5°”ÔUË•" $Å–³Œ"])¶Ÿ>ŒÃåø¤c[äe'cÑátä Øi®²2ù„7¹lÂvH¹e,'Oü—š˜ÁÞ\K xAÚ&nœ\íœÐÉÍ öÌ£DX2)g[jØóê^•ÀU% <—]µ§pÿ-¹µáyúÒ½I­a.ìúu'ð×¶ÃØA·[Åe˜sÆ;ÐÍo‹@»~­á['[8pÖCü‹Æ×ðÏP‰€ßÉO+‹Ý£±oÃ~¬úz-žyqÌËCx˜FŒj‹á#ZÁÏß6Öâ:Ká%í±¡¬ºÝKzò¥¦tʽxЛ ò yäÒ3C>ݰåç!5%¹›ÅôøaJß³â2ÏÉÙN´ ¶àoFt“E|b^ =—¼é†?kIâaäÿûƒ Ы„þ#ÆP”€ÙæÍ=xx©†kr.0Еk+deå_:Í_oüw¥âÉdÎ[#›p}uYõ & 'Õ~·&SÓª¨ÿ¨à%Ԗ⇭Ûq¤ÌŸ´og ‚M;+GLïÕk¾ùßœŠÀC~Öøýd*ºpê35;—qÚmFÍ’‹È(ÈÁ¹¬váfðwu#Ú§f$âP¶>6eH.,C€kSxS›U=ðYŽÌü\d—rµ%èz(« <_@ÿ£Ç/Äà·_ â´íÔ¶ÎÔ@#6‹Æ àÅ¡~yšÒ×-—`çѾÑÔ>M¼1ÞÁÖšrÄg¦`õ™h8¸£»‡l©ðMÊÌBÍ:Ì´Öð¦[$3ßìÂ\¹H¼l_GG¸Ðy9›¬’*:”€€`—²i_¹M›:R3黯wæ95¿ñŠøÀ¾Xå÷º…+ñÃÜaMÜ:-è-0ÜOñáßÌ–fzM¦|FÊ?ÑüjiÚ#à×?ÔçÁɧ±å×mسn?^zu9^~yúô ÇàÁ-Ћɢ¢ÄE—|T †?o hÔJáÛÝv™î5y…_¡¹úrä¯Fñu,æ 4±4GX´hNœHV’ˆö^o¶`¨ûRNùeÁ¾Ä› Ú €W”Faô¨´gfÿG3Y©Ò H@”´YWoM—¨RͪJ@•@ÝK æ€W:ÚªnHO§{¤ÎhbÆŽ¹¤¢s¦ œ…… º6±$àKņÜT¼´õ4îH €†rR&†t€˜»áOظ¡<3ZŸx2Âï¬úKSM00¬ N'&Â9 =>ëÑÚòêh‰4(Öcý‰}xoç1úÛlƒi-ÜðÝŸëQìˆIá¡è»ÏÿúÖ†·ÂŒvQ°(LÇ[›7!Ù¢ †xà·#‡aáà ¡ÞXòr,ÝÐÇ¥;’J0µS;œ;{Ÿl?Ÿl\Hó«IVǧÃÝÉçR‹1¼e+–daK<§•ãÌq¡@‹ž-p/ÙSÛ«Œ÷uOÕþñ÷±â4’Eóæ>Ôú+g ó¨Ì¥p:Ö­=‰MkŽbý«_c)5Œe x`ïâ€ÐVAhÓ«%ZöˆR¼:8Я­Ö–ÐÙ`¬CXËP4oÙ ÙOç él"v¬Ú5‹Ö㉧…%Áwd3OŒ½«† Žâ{ãÌþA4¿ò> ˆ¼•TµLn%úºÊi;l‡ wwá¡ í‚óèua‚‚šÒ.;CùP±·wpWÞ?É/Zèà¦8r4ö»]h¿}J&¢pß$ºcS´Ð"Ûª´Ã’[¥jI@:½_ER™sTI•€*Æ šÞŠVk9ׯ£K™Òý1é¨15ÑZ¢k@Zž)ÀÝú#ÈN‡ï×­ÂÛ´ùílbßi_7š4eHÈÎAiyS˜èL…ÿ 쀃û¶àå“ÙÈ£vVÜÞW‡,µЪ=µÔ®F8;#, ·ÓEQ„ƒ9Z¸:£ãþ=˜¿k/žÊÌÄ«}{ãžáøøT&ZúÃ¥4k²¬ÑÎËe¹9pñ …æm!uùŒîe…HŸ` ¾PŒ.»ÃO—‚÷·F·6=p—#’Y^1;Êó±fhç…‰áþØpôâ ÁŠÚe{ ƒ–«:-QÓ¨¨ \ÒTZ2\³,ôòós¥mjž»¯r»ÎÁ¡Þá8N»àû¢±uk4æ=õ 9cââ銂åVÁŠ Tl‚½ü= Ëdag ¬ù~†r ÀØBôá³Ø·ñþZ±O=µÏÑδ#=<ôë׌IÕà‚/ÑvêIZÕ¯"A#ÛI[H™)`766ž%¦ŽóX±ü0µè•{TCóË9se†ÐP7Å{Çüù“h¼QYô¶wO Ú´ `Bì¤ucû[ýAvcܪ¹U ¨¸¾jxÙY›Y£3µ»ÏE#® %|iuÀeüÊ-ÊAL¶QŽÐšäÐTÀ î6ôJ€|›Ÿ7¾‹;äüûµÁ-šÁ4#0£‡3´gÐ[j†4pµ´‚«yŽÞua\¼~{`mb‰na!X¼z~wÒpzÏ >v8~&Žžî˜Ü½¸?ž_¿ ûsJ0Â+ç6ãû“çE­Ó™„cXvÎæ–M1’SÂq:Oø%ÆÐ~׿lÌŠI#îÇdñÝ3y2Ì­9®³ Í raI VSÚBºRcÍôV:ÕWC5n›šä–K@^XÙ„¸çój;¶-:÷ Gçþí©2ìĨ_9ÈHÏcPºA[s»wEcÍgG°´¡‡-,àÄÅp´îp{[ø„úÀ¡‰œœ`ÁÍÕmà¤HŒId ÍØôëV¼òúøßìhÓ.€ÞÚ£¶¿¿ Ažh5þ“[)ŠBøº'è>ÍGÙ|}œ¨±•Ïpoøó2Ò0Os.¨M¹† ô:ÐlÄ“ÑÝ‘‘ o.Sl'ª)Ck [ŒŒj†ô³¥hÝĦ¥¹Ôˆ8âdÌ1¼S–§ÒB¸x£Ÿ§,اµðöÇÐÄlxøD¢U6–ÄÑ–`7 ÇR3QÀ))7þ.ð´6Ãîè0áïàð0,=zs¶¦ÂÓŽ&.®È,(A¡6i‹œÉðNY¥%HÏË£<.ãÕl†šL•À-”€0ñÑgÐV œš"€öî”ÃJ[ó6Ô ¹jRU7(Î>¤eSû íáÀ–,ïj€I±lâ1À¬"*œÃáá©™éî+‡Æ3`ÃìÙsÛ¶žÁ¼™ó8éQ g½p`ó„sV¨e÷˜1û!L}?Žm?‰ƒ[aÝ1‘ÄÄ5X×.ôøÐœ!ƒi6¥cžA¨!k~é&‘3?¯Ò£Å† 'ðÄã}Ù™ý¹š¼¥½×#~\Ó;Ã'Ÿn‚³³ ^WÂâ^/zýª`ר͑Ç}¹JªT 4 ÔðJû9…/Žj]Ý(ý‚S9GYJ@| ñ5žiØ“š‡ñ\Äe'`Wì£H&´Û piªÏCïâFÌÅ`º IäëZIÊ?5¼ÒQ™j,h;Ìr*ê»[g[ÚÚÑ5‘”G-µLÙÁ9H(Oq8^n sá“vŠŒçbC¦ õsï`mšaÈu9oÇãp[{}^žr¦ø")mPq©Œ‹Õªê‹øÿ‹ÞœáÛ)cXM QÓ«P%Pß$PG€·¢™ÆÜúÖr•Uª.I€ö¸hÈca7ð^ªâÒ/)__‡íâ]\l­ÛâßÿfÄü=’ˆ3gR;àM›NáÇÕ;i¬Cߦ°s¢Û@F+aXÞ¥Ëã»Å;¹Î·q¡Û€QtÛÕLñ~ ×üJ{dû»I£DG{äáïГ }òändèf]CÛ’}îGmP"·}ðÁ]uP‡¡®F¾§M´j¿ÛÈï±Ú¼œêðþãÄ©6X•@•Ý">•®èWññz³ÁØÕäb¨Göz`jcc…ö‚¹…âλ:R \€íÛiLÕbq>«é©9(Ì+P wi²-^¼KYH7`@3Œ ÉCË–¾ôZ š_7Ôw5~êê¼ ~úi/¾ùvƒoDÔQ%åœY3£ t >üp=µßa5ª=ëºQá:b·ÞK-y0ý÷’^a^o9US% J ú¨àUÝ´T_²jJU FtG–”ŒŸÏ­ðp?rþw‚¤Ëµ²6´-¶±±$€uæ&® 11)8uJ¶$lÙ|²(NÜ£•ÐdàüùtÌŸ¿YÙÂÂܹ +D‰îÖ¿$í[-™ßà~íVj~˱ý¯h†XnI iCÝÔ­Ñè0yÊmøæ›8B-ù¨Q¬J¥šI@ìÙgÑïžBç{P3¶ÕÔªT \[Õ¼´kÒp—Nì›dS©æ†Ê‹àj^JÏ!œAm"ƒ‰p o¿íCè¡3=o{ÎÛÎ0Û5Œ¼‰ŸØBò´d'óän\O`ÅXÓ,3ø“õ•±†îE3_æcÝKwÑŒ SÏ›”AÔ8O³‚‡ÉeÑŒ²èÉ´¤µD™qê_qjÍ©}¹ô_Y_ ÛcÌÛÙ Àr–M“D0]8·‹àR*®¯$@N66ÓÔ”ž ¬Ð<ʇ›Ïèí€E,!¶ñ>mÞr†âÎã½÷Ö*[Hˆúð^¼ôÒ`zKp`Þ›:"?r¥Ë´µå³§PÝÕe(ßٙϩ”ý,^Ó‡+Ö_Uÿ^Gr³æ¯e"¾ÛSºr÷Ïꩯ#õ²*+j^Gì,(À#kÖp U_þ¹Û¢OÌäGƒ‹LµÕÿTWòÀ ýž kÁŸø{EGPy lÎj=ˆ IÛCeáª}|°^¯}lA`ê㦩ﭲ€þÝS)•Aðú }{_DÀKÐ+ æ³-€ñz@(àÏš@à8ãìUÔî=À­›€Jòöó>È•½ LOáím~‘_©OÀøÖÓÀèëkN`êKÞ Yÿ+ ¹¥Ó²\Kk}ý¯>xÞo@IDAT¯däÇņ@6€ùÖ8í¬€f«ÝC( /4Ìf¹ núE/Ö)´ŒÀvñn½ÜZùð|‹6V‘A³ŒÖŠ Éû®½,Dkë˲ƒÈ‹”ÇÈ^ h6æP¼¾\jþ0šu68­ åªìå £‡5Ú·jåÏ-îËZ£÷&‹ ‘Äxó–ÓØ·/k×Ç}÷v"àå}®3ÀkŠ^øI‰9´«½–V|îo¥§çb„¸ëÎ æÁç\õ)[=É à•¾@L&SnêW=¹©©T Ôs h¨­5ŒWeõÔ©Sؽg¢Ù½nâ«–¢^ Hß)›§§'£Su€••Aë#Wo -ür!fÌxK–NEß¾ÔÖõ@XFBûJXrêœ+í‘ÅÐÆÄÇ¥ø}åj{èI:Y0å%„”œM ™«_Ö¿??O×ï¯C½Úà$`Žwßù/¿¼ «×<†öíƒØ‚Ò¶Â þy#GÎÃûïŒ{ïXÃüjò[-ްקȦ’*šK€`”áœ1ñKjdý€·&„]é`- #|*Š–k×Ý|Ý` °IZqažü-†Ç†óθÎ<2*#P¦è+¥(še…ŒÒz0zè*TQŸðæÅ3R†!-ëuP]Ì <ˆö¨HkÄ›ës0ÔWQ®ÔàveZ.ÎBDåú˜Ì‹rð"`WÚ,|°|ä!דEE}ÊF²°ãWs?jÎ |Þ«7ihíÏò¥ŽÆDr?„ ƒšnò|Õi;M—–ÔÈÏx´7Á®€m¹·‚Ê•o¼1OÏ\‚””ì À{+ênàuÈ,æsùÊs¯Îh6𛩲¯Jà’ªx/%W©¨…;‰V7—v¤Ê€_yÐ0b"Æå¦ñ9ù]9¿œ«IÚšÔ'i몾ªÊ­I}5I+2ºZ},çäàÞ/©ÝhF"ÚŽÆx¥ýÆ$²“­®H>Ê0vl{Å ™“âê­*ù×UýRnºt f@Ǻ¬©Q•- —.ñôTÒ¨š¦6F•À?YTI©¤J .%@P!š-±íÀŠêdÔe;yÙ%üà1yP´Ù¼½uÚ< J¸è055[©ÅÉÉ–û¿ç¹×Ò„Gì™åæÂ…,Z¶4ö™›tcShæ“ÊM%UªTÀÛhne}mz±½}c,0©;™TÜúw§xìx¼iï+‹Üþ&pVÿäR[ŽÌ°†^9úöyG ’ñ÷~@ÐÖ`õª#èÑc.NౘV¨tU ˆ ïs¿0¼ð¯úÀ«&T/¨P%Ð$ Þ†t·,¯2èÊÖH¦ªekLÄ^€î/\ ó€|”Te^Ò˜Ú[—mÑ ;+ ¯ÿg#žÁO#þíÏ~9ƒš !!/½ðŠÅå]£{†oò=•…ª²5Z2ôc­/k´7LmØM€jÃ{„¨q- °CåÀÏÖêAÕˆ¶L\´¼†_¾ù ¾áœ´GºðiëÀnØWÎ#ÇÆeòñôeɹúDäUü"GrÁ,n»)Mä!šDƒœêS{ë’ ÌhÂãG³žGGñ²!¶»¯V58ØîÑ!66‹çäsÁãM¹Ï,¦Þ’¼¿µ´›à²@¶A¡ß~7ó"C_&çŒû&96ä‘ßR–¡<92¤7ìËÒ§PÿªhPoC¸K šGv¦Pâ«í@§ºûiw [cÜ‘~WìË‹‘D·TééyÈÎ.DŽl àPH^‹iÏšOž•UÀsEŠF¬´T‡ºB+eèZ!±ÔjM`ffJ€£…]¤98ZÁÆÊ‚Çf°¢«1[š ØÙYÀA/\œmÐÔÍŽ‹¾é>ìŠDJ4 &òûV¸xj³¦}£–1­7¨­–WäkJY1ÐC,ò”…Š·º=s}ÁCÓ»Ó[ž Ãþ›™©¨žmss‹ÙáçãСì߇hFy;Oÿ¾)\P”™Y€BŸ(-$åVF@[. ·*J0טÒcš¹2(˜r4å ª• ¤RúÔ-£oÛ2î–p0a$9ùWÎú%™ 5}Z ˜üZؘÁ‘‰[¸3ÒY£¶µˆòäæÍÆšˆ97ÚÑ*š7œX×`fêÚ™M¥ïÞÕG+| ëÛXóòL”EQ_~¹‘AÖâ,CëÊ ƒuÍKkÐ9¤Ù†±¿¾4Äp+ê_u Ÿr ßÖÖ]»„R³=Q-üXK <.hØ_)Tƒ<9n|'7Jú1ÙLK Ù‡¡€ JcbR±ÿÀy;’¤¸¼KJË@Zz>r³‹QÂõéÇè—[W*7¹\¤ZöGæô.}™ôaJ_F oÂNJÇ>Kú0YÌXÆß…ÅÅ @É:•¾G£<Â&ÄÒiÇÜ’˜ñòïáæú%oá–ŒX)æ;V˜ –MM¥/2ôcÿ€‡Nß`õo=–€ xëñÍi¬±£“ ¯gÀú£½© NÃfBmm&Là´m‰Ç®çpäpâÅÕò–hjEM«5¬íàîêWj*œnØ•@šð·+Û[P›Q±YEÃ3àÃ}1î‹Â5«¨ˆ‹‘ÅðÇiŒ¦w!/éùü]€þNÊÎFÒÑìÚŒåùû™žA&H®MìAs‚íýå?Óhΰ®Md:\j1L!×(9o"Éô­lŠICmÊ•@ÅxòÉñÉ'жmWL›ö œEÃVYrµ)_Í£J z¥lII1Nž:•+Áú>oáãîÆè1X@5f.$@ÌúC|lYPϰ[ðøÊûaØBuE8Êþëä‰ ˆ>—‚]»b°oobÎ¥ÒûGÌLµìǬÑÄÚnÖ6ˆ´÷BS[¸°ïrá9Ù7•~ÍŠ3Læú¾ÌÖÌœáR‡ž.ýºü»LǶç²Ë&ðÍæ>£ òóš—¯ôcÒ—]Èa?–‹ØSIØ]póØ×•èÊ`N°ëOÐÛ¶­/:v`?抈p7„qãœ7鿌7=/ê_U·J*à½U’þÇÖÃŽSeúð½ÒÙUcÀ¹¦¬¤«Ö?¶ÙY98ŸŽ+Ž`Íšc8z:i‰¹(/ÐÁÑÒaM]q_hZ÷tGˆ³ bx^ö–Üh“Àþ—ûW²ßîC‰–ÁOœ,Ùw9¢s‡` Üm[û)¦]&&¢íù­šÅæUú§K@¼ÿô'àV´_:Þ †Æà+‘½jE2Ín¾¼Û¶žÀÆÍ'ð$Ì8õéâŠÞî>hÝü½ÐÚÃC±ASª2 XÆ‚„Ú•íf’ñs•r­Š­8@¹Ù`’¯v>>ÕBI]L³ˆýIÉøëüyìOÀ½ÉxiÍoxæé%h×ÁÆ´¤k©0Üv[§ e“Áâf ,ÃQǾz ¢¼šÊÅ Û¶ÃgŸmÀäûg0lõ¿ K¥¶JªþV „…†à½÷¾ÀØqw`Ö¬% ëOS[àðjÜ™iD¯–¬æç^J_¦A\ìlÚ| ËWÁË##3^¶öhÙÔ C"ƒÐÞÓ|¼áaghçEP+ P{SɸŸ¼JÁbáHPìH²§^ÁÁúÔÌ›™‰íçã €ãq 1 KîÆ'o@Sw Ø wôB·nAðð/&Ò‡Éý¸Î=Ñ—®þU%Pk ¨€·Ö¢S3VOìܳ‰|¦| ´ò^¤iC4ˆ¢Ñ )1K—îÇ‚…[qäH´ ÜÖÑßóA__xÚÚ‘›ÒùŠM³²¯ot P,vv|}ÐÁßOÑÒd1&Ps²=6‹Åœ—VâuË•ˆhæ†ûîî‚1cÚT 2PÔ¤ †ƒp=il;ª“Y¦ «;øÐί¼?|¿‹hO<ñ䋊Øoö·„1·êoUÕ•-Œ¨aôÂ Ï¿Ž‰‡bÆÔ4¶fök¼/òü¯>Òí\sÕ­ïêé¤03ºªËÇÞ½1øàƒøsÓ1ä$ÀßÉ“#[aXx(;ËÊÖÖÐ]£Ï¸z}u|å: Ø×É ¾..ÛºmŠ‹p>'gÓÓñÓÑcXùÓq,úr'œúh#ÎrÁÆm^>x±sŒŠŒ@ˆ«ë¥Ò¤¦É@ƒ'#ͳb~A-JD“&˜Ô¶-¢ÓÒ”cùñxì_ßcÎÿ­ÁÃuÇ„ñ¸xDd!ƒxuª±¤øQBwU3~x»oÞÉ‹Õxòó ±gï94kÞ®®.7]qnÌ©ú[•@M% ¯Txxœœ°uëÞ6×.Bi®Œ¯]ŠÑUS~–aõûðÁGðûɵÃÈÐ0 ëŽÛé!¡bA¯ÜÞl­­7·ä§€õ …ƒ,’óst„ApÏ  Þ^„å§NcÙñãøuÑ^|±pÆŒnƒiS»£M¸ôð›!÷[ÒRµ’$ð6 ›Õ0Y%£û.´ñ‚š² ÕdZzX(À—_lÄf/GF|.†GEâÛÉÃÐ’¦ Ö2&à¶>jpoæM2 ~eRc2³WO<Ú©#Òôáƒ;ð곿ãí÷×áÙÀ”)]9]+ k ü9ÀçQF×EŠ·EµUý†èèÙ¢¨°”6’¶ÊLkõsª)U Ô½ä52/–ЧƒjÕÈÅY Y¼5T+W¥DòÑn‚½{Îb#·­[w¡ö.ødÈ` ‡§ƒƒ>½ôeyJ¤R?fIp?*ª9F5o†Xšº-=v ï.ÿ KÙ‡¡4Ûzåå!ˆŒäxQëøJ·A=T%P!ðªBK€£ ÝÔàóIõ\ÏÌ@‹ƒbñð˰eë)ÜÓ¢%þ5¤3ÚzzêóKçÙ嵕¸|¶Ý’ ä:жï+š?<Ñ%ïÿµ?þ=ÿ° p7Z·ñc 5½”©¸“ûä* ãx\S’±½ÙjZš^•ÀH@Óë’¼gÏÿB¬ÊÔÿ­ß_7Så¦(¦=Åë¯-ÇÜw×Â×ÒóŘfÍ裛S÷Ò5†©ÊͮαQÛ}©ùý×m·á¾Ö­ñÝÁƒ˜½z+º®{ ³ž€?Ù¥‰½óõÆŒêTª¦Q% sÆ*©¨s È0#¶¸‚ˆ®Õyi±îÏ#¸oÊB'â›Ñ£1®ys:SàcZ›ÁAËiBé/mp#BcF“V^ž˜7|(ú‡…àñe+1xøû˜÷ñÝ4¨•4œ[uˆ÷DB ÿÀEkþbQÝ|Õ)[M£J Jàp<ûé½áÁ)}¥/ª±6—ý'ƒÙÐI¸>#êÊ ³tb¾õ`§Nè‚gV¯Á33—Ðw*æþß8XÐÿ¯ú5]›çPÍSY*à­,õø&K€u!§ËÞN^Náõ‰`ùUõØZlÞx c G|;ínÓvU™ê«1Øeš2üux;œ<Ÿfx¼}845.ç&‹¢.Šc›Ä•ÐØ¨(®êöÂ=?ý„ ÷ÌÇO‹§¡ïíQ¬±:à•÷ƒâÐW¼EÈɵ>Jê¢u[¦˜d Éw‚ò›ûªž@}ª[ûWø¾ª¢úÆkU<6Îs¼)òágxpjÔH r$âαŸaÝšã˜7t&µ¡Í°”E/,5&ÉW–ëþÂV‡Û©ú{838D£"Ѫs pvÆ·£G¡ãvo<ùáJt¸ùóÅÕ›·¶QIþÕ˜Ú~Âþ£„¤6öF$À›ÑÌðÎZ€6Zz•kåòL/ Lûž&6øéÎqz°+õjh r— ²1;Ï3'O`uLÊeN×/KÚDF”• ‹ÇEˆ¥3¦?òÕ_`ëªóŠ3MR&pï|`?Lmü­Œ(â©àa”&k­X«‚eúègØTýc$3Ôbo||YEîº=%XFø©Šxé¦ñjB™¸j½~“4ŒË¢Ü ¦­ŽG.Yg%ŠÇk•[UÛêý9iü›£è‹wdõqYƒLñöÛ«±šQ ß|&µk§¿*€®6$ï6];[cýÁÃ8œI;ûF'p#ÁPû­eðD×.˜;`~±ïq‚:m$#õg­%PѰօ«U TO¦øßÿV!îd >> >†©¿êe¾2•2 ˜À×; ¶ ‚åÅèaÈ4U±¯Œ¬$Ÿœ3íåXÎ@+×ø“éªz}䚤¯Ø Ü]ÌÏóƤÔWQGe~ŒÓUçwY)¼)»Ïiâx:³g¯d.©¯RW”Åú/ä‹w›NñªL!Ö= È2-/Äá]bÁçÒ_ê»øî×UH`¨TS×ãúzŠ8ެÁŒFaâƒÓ±jÛ<9m4®Ø ÁÝ–½ú¯u]˶Ù<÷Oš‚£)y—¸cDkäĭÔIc±lûQÁ;µ&‚ýøÓ±nÃV;aÐë*-CqGðןá÷>ÂÏlF½Ô]|mª¨Ý¤,»·¬ÃŸ›w+ë•W®ŠtuqÊðJÖEÙËt¢GÙjDZœ8vo½ý'–vÂýÙM€n­>Ú+*Vú$ toÞÂÜ/¥œçÍTöý‡1¯Wô7Lkœìe2ä¯êÁkÆ›>Ç¥¾O®èb9¼Hýµ%‘åöe8<4³ç¬BÌÙ$–vkú¥Ú²­æ«ÿ¸§²þ7Nå°>H€—¬t~a0¾#ª¬é0ArR:–­8ˆ´CíÈ@ µ²×54UPW÷ž¹„c)iȤæS£@éðuˆKÉVúÍ”Ì ÄŠ`CÇÌ ¼¬'’’p.+åÒsewZV£Ÿ•£¸01Y(,ã+C€’˜Ì”òKýR7ëHÍÉE5EŒFT <–SXÃI\X1­)åsš.!=û’RÏ)Ê é‰H/c9Fãˆ[#â´i .ðŽßWF"ýW«@²©Ü±¼$¢ÊŒÝƒûÇwÃQƒ0ç·°`Á;xnúôî×sÚ‚^Éø,ÀŒ¢¼\D.² UÅ›ANÆ2WÒJ;¥-ÜWn·iY~xïi¬9{E’÷¦ÑÁàйg‚ÕCif1¦µo«‡g7v)¼Ò’ML@tF¶ÒGP,Š0ËK‹q>ÎÈÙ×g´Æ¤š‰ å=f¾‚ÂasÁpÞ´gR²XƒŒœLÄfå2=,úÐŽ£Üø\jŽo„žýar®>lp}é–”ËM5AFV:'§"³D:+’¼xN³Ï=”’Iž q6# å†VŸ¬F)73¶ã¹žÝ‘’˜õO0»Òú£&V%`,>Õ*©¨K °¶àc6´-+‘Þ¸¢“¼X%Í’²ާî@|Ä»0;³~_\gø™åáËýgÐ<<ˆ…ê°~Ï:Íb1î×ÍðquCQYSíÕÌÃ>‹OFAmºav·Ö°Ô”#.ñfnØ;Îϧgg"ŸjÊgA`îQ<ò×I2dð¶sxwÜPt°/Á»ËV`g¹5š9[bΆD$—`ΨûᬽqÐ9," ‹—FE.H»&±>†]Åíá@[?EV×L~£9niòÏá¹c°|{.¦Ì|÷ ïG¹™ vßj¼ñŸñßG?ýs¶óP¦öbO#)-6ÎîÔÜ{Á‚c©¸w.ÉËA¾N [sÄž>Žœb-<½ýábo¢ü4ÄD'ï׽xé…×áã¬Å‚ïºpœ7ËÊ8~!É©pl7{S¤ÓþÒÎÁ&Œè—Á ['Xi\ƒ<—æç"—¸ÂÞÞ–&âÅÈ¢ë<[;K$DGÃÔÖ>žŽÐp NNˆARjœ=áãݔ煕bZ‘tþ .äÂ= æ–âÃZ±Á®,R Áˆ ìib`ެ çéƒ:v®~ðã⢜<äÈØ::A”¿òVÉß>sEkÊ‚ˆÔ@Dš&Å…(&ï•ñ‚h’uéÑxó—‘mÖ3í½-±ä“§ñÔ[‹°rãƒË)yãW–rÐêJ°vå·8žcí)|³ôOôo{Ÿ¡FE®¥yˆ‰;¹ü¼=Þ`n[+S=ø%ÐJˆ;‹~\º6ñ§<£¬§LWЬôÊØ y|'cSaëä o/h)Û´„ƒøþ³÷ѹét mÊM–Õ2Ú¹È\u~ˆVö©Ÿø€°±ËÕﯛOÃåÅØ³?ÎN3¬?¸n¾«$ °Œ‰9„™ëöÁ?ÐÈŠÆ¢s)x¡=}úÏ[û þïXZ{;àlvrKmðÌ ¡˜èïŒóq‡ðÒæãŒæ¨ÁæÔbüßá(8»֞Š 'ÈHÂ…\Sô E #¢M¿€ÔröUý‡á¾`w>J:l8¼ïì…»q¹Y°ç{÷áýqøÐV¼s&^e¹8™ï„o'ÜÛÌÌZ¹ù.ÞðÒà¹Ø$X{âþ}au…‚ã*í­ê4ïCÃÃð9ß¼å4&Ý×¥ªTê9UÕ–À |‚U»5á?]¢å8³…DUP)µ›%ÜĈ°ÖÄÁ Eøïªß±SçŠWûöÆS=z¢¿‹£Rbqaï?ˆ‚ÔŽðåè1ç¬ÁO[Ž ˆš„˜øh|{6£;öÄë‘O-…UŒù öp¶xþŽ»ðM¯6Xqp? lÜñÕ„ ˜ÓÎ öïCt^9J ’1eù&xµÆkwôôpWì‹!vߘ2[<Ù¯F»"WWŒ%œ^û\:þÝ«žíÞ#ÝÍp*‡.ÇnÆIy;жœÚe Þ×%¦ñ¢œ–<Üß•©«³Ðíº…^5–·yß²ùøm{ F=>¯ÍšŠæ¡ðööC¯;À[ÿñƒûÀÕÎ e…©xÿù»Ð³_<´é ôë…y+N*v·¢`ËOÚ‹ú†aÚûKÀy±þÐôjŒµw†‹òÁ ±ÃØ»ÇËÿ™ƒþ­àÔÄãÆŒ¢vR|+‹ñrMmVÒaüðÃ2t»÷9<8¡?6?‡ÒÊ`ÆgWªI;³SǵBÿ‘ýq7 ÜûÐ$Lº«#^X¸bœ{áþ}Wgôâ;úÈô1èÛ-¾øòXvnÌrôÜO?7 }ú÷ÄýJ»£ðÂÇ ¼øÝUMŶ”,xc"¦>÷22ùè^Ëôârîkp$33W±¯¾Z):]»¹b†çÃ"‚ª-1oIþ<ºìx„´Ã«=ºcV·îˆ43‡ŽNO?ïŸF2Ÿ1úâ§‘Cà—›ŒŸ÷£rAƒ­ è°=×õ„CAM¬©Î9Ù)Èu¤÷›‰˜Ñ_þµm›µÃ÷ÆclSþ·÷µ¸Z$ÅÄ´ Ç1¡köe}ÐÕ*;¨-.+-Š;Qfï‹×õCKú5.-Äë,Åf=^éÝ›}YODYd#±X£|”ÖVÆùlùæåÒÆ¦âÓÎøšú[•@M$p3†×šÔ§¦ýÇI€˜tVý|¼‘­¯üȕÎþ_­8µŸXû‚£~qf2–$`8mçÜ9°š›Û£W[—s 2·uÃ3Ý:¡{&·†Ÿƒ+úPÓë É¦ùA9ü›ú"È4ã¾]ˆŸ³œñ@¨µN¾èàÙ¶ë„Nî.èŽ^.nɨAöèN:^€Ê8¶=ƒL¸à¥ö¡p³´Aï°H:9P YŽ6~aH>¾ƒþ{·ôvÑâÇs‰nÖ­,©4Cÿˆæœr·äËöF‰<ífüz+úÖ áõ 6@¦'OPþ)´å­¬ ¼Q~*å73)Çží{¨M÷ÇÈ‘wÂ’UKXY£˜G0Ö{Þÿ|>z7wÅêùOáUÃ~SgcÁW?â­çŸ@úæxtîW`”pêvßæUˆ6iç-›Ï<‚âÃkðÁ/»Ðoä“錰.£ñæì×ÑÎÇqñ‰Èãã˜rlfÿ÷] r4>\ð^˜<,ùÑq³Kè ž–ŒB ÈAñ~)&øNOKU¼C•QËs"k÷DãÉÿ|†)[âûÙS0wñ.Lxü|õõ¼6ãlþößx‹¶Ñq›ñì«oÀ®õ¼?ÿ{¼;û!˜¤œ¯$ãCj~³q(&¾<Ÿú%z6æ<7En‘p2IÁï?† ÊNÌ¢w~µqeÖµMµ X¯˜Ø¹áΟDŽ­p!ñŽíÝŠ—ÿû6Êí}èçKM·1O¬‹ø|ÿªyؑ슻†Ä°ÛGÁǼ>…]âZlÙw¥E%ä­ ^ˆ¯ÿÊÇŒW¾À×_ÿ„™ãûá—O&€?M›îb¤ÇíÁ7+÷âÁç>Ãü¢g¤9¼7§‹Ý1uʃh Œ}èU<÷аgwR­oºË›qí#¹Ù“¨M¼ï¶ôGœ‚çWQHpšd"ÛØnæÚµ]y•}Ù¹¸ØgLj¦Ì9Ú»£ƒ—Þ”ÀÅ3O´@wߌñb_Oôò°†®”Zü2S´õ @f úöd9D¢¿‡=Zúú!‚3O·oÍp®ÂH•Aâogô „Y _@Þðõ‡s¬Æ„º¡‰•FÐŒ-mp4f–èá€ÕÛVbÄЧY+ø”¦áû4¶é7â|­™-FF*æ•+ÛY3ùlïÙ´ „…Ró|ÅØQÔ$ªŒ$P}]Rª¸I(e×·'8Ì9˜\F:ø2,îm]‚ð󡣸Àé¹ËlÉ.K{RE ÊPZZ‚\.Þ*਌„¬Oz^Ë ¥<ðÅâÒ‡£}‰Ì‰“ŸrIgé„ycÆã¡ü²}%î^½—3¬è¨>*—QŸi4ˆÜ©UÌdÆü&\MdÏz˸ó¸H¦¹KX+̦&¹Høà¡ƒ[0–Þ=ýœuxqù7xïX"\©U>Ÿ‹<%é……g9¼"/ùyø–Úì.”©'§Ù¥ük¥"¡…‡||²‰Ii‚R‡D©2~F‰bàÕÄšš£Ë+“cŠ&E´í^µ^ÍàÙGîGTx$†Lzïê‚CKÇ9bs3jËM]Úàégg£g綸cÔTt‹²á´x ¼B£ìm[~ÌtèÜM$° CMy?£OlÙ\k<>ó9ôíØ£§ýSu†–÷PO4 XD;j óšÈI’œ¦Ò ƒÇ¿YÞƒ¶¾eøeÙøµé„aZÓþÐ mûŒDË@®®ÿcvmùñe6¸{ê è×±º˜Šé“FðÖTj¼¡2Þ³âbSŒ›ú4¦ŒêŽœ xlÊÿÏÞUFulѳw%BH $hpww+”b¥-R£ÐRù¥N½´¥Ÿúo)U ¥@q-w‡QB ân›ÝîÛ,„` šÐØìîÛ7vgÞ3w®À6ï0¦z`ò N8¶{-ŸÌ¢d5 ü &Ýйy]eÎ],æF85d¯£.HÀÌ©ýЃ§?¯ÜŽFG¾(6CÊ‘G)'?Î_Á~öA»¨Óº7Ú4tƆ_":SUæY‚Ý„v†'xÜݲÓ@Ì|õmÔ P.¡D²0ý4Vn8ˆ¡gû:P™: Cÿár×aÝŠuȧÂrI±)Æ?ù &팶ºbÄ ~0§t²Pk‰õÁð n‡Ð†ùøf÷HpÅï2¸ ×Q|]þ+n»ò‚;!•›¥e'àã3~sI…\FzÓ‘O‰-€¢OCF¢·E`‰äeÔ²áæ¸„* z^&,V¹ÕsVŽ}Ííux{ùbÌ9‘HÝm>uæäe˜ǚ;Óä+¼PGµ3 ‡õL Xg^1uvÉ”xF„ŒžÎ1›œÄ5iЫ†ö€§6 ãþü«“ ÀùÌœNŒg0r>,_a«7×ñËri9ßó­Ð¤}ûr·g¼—ÑÇø¥òÐsïÊç3æ0R ‚ '–Y̺¯ ?”_žtJÈϧŸèŠsšl|°m' P–ªˆLÜÎÅ ¬Ì°hßN„eRBWOc Ž‘!g §ç(Œ<ƒâÄbM ²5^ÈùÌw_MÀ”nƒñAodž¿€LêæsáI¦yYҨ˛Y„†ñ{FnŽ~ó(¹rwr…&;ïì?Äœt|¿Τç 6å<ÖØ…uiVxóÑ:˜ûÓµV§&’£vãý}gRX€ˆ”4.0\U*Ûï²ÄaÞ"öåã;•—'¦t†õUh^6“|æê)F+ñU§ë¿—¿å6~',âæCMÚdPgVŽäË& `Tªb Y3ŸWg×zp¤º+ÉD=Iì=¡&“=M ¥÷6NÎTA¶R4ftÔauvwg Zð'4h©sÊáç:¯Ÿ{¢g›SÆ ü])5£[A¾§ƒ TÉ3Ti[yàÃ2Ì,MP?ˆjÜ¥'#×ÎGîÂóTgx챘øÌT„g¨`_|ñR)m·¯g ðXqÉêèæÁ¢Ë?J8P¿7ÐÇ—¤67æf°2áMc^£…KVVî:ˆôÈCØp< -»Œ¤ž²è’ʸñ»tËD­A‰™+ÆOy òÆ÷ï„Èå4"\µý²½¨uœÙ»ÛO% ¸0ó¾yïÏýšÏ §ïÀî£Él[’µfðp¬­è‹“•CM8’Èq|Az<ùKøö_1uâHL$é$XÀ*?‘†YìûííêAàË~³/Žv5àXú\hø,ky‡–¡|t¯I½÷üwÈ‘ƒ¼*•ŠÑ¡C]tî„·¶lAXb"7Ë7±Äò®EIªkn2¾áÆ5‰4%'G1xÍ!ÍádΤñ«€ÒL¶±ˆ¼,‡<³Òñ<‚ÖÇ`õàÓ¡#ð°‡qi™äe4¢¥îwùY1ùZ\V6Rs‹yÚQL^X„T^—rr‹´â=u¿…ŸGbj<>:pœQÇóp"Ž›;‘`Yß?8žÖ*DÛâalØ¿ ð„(….Æq·š´Ü,ìLJÛwbðàP4n\‹EVbbßjŒùïK ÜYQÎ}I2c§*G.è"YûvL©²AÝ©½zÕÇóÓ{Ò=ÙZÔ xy¦M.îåÐùÊ\ ´¦Ž˜Õ³3¦lÜ…‰+V"ÔÁš‚Ë$–QSzœ“Áȹ%˜¿/ Ô³ÇfJ¤¼´Xq&Ã\ݾk ^ÝL?¹ö<ÑlŒ3á§kçˆ]1DZ‡:'…u Gì‰>‰VNõ°žqÞæØƒ¶-ñFÛÌ ÛG"x´I0Ó÷?‰é”„­ß»ªBX[×Ęº>èH£ºw2r0ïà&‹u‡I=5P®=.ÓyýÇ.z_íßwîÄ´iݸP4åDI¢\)ÆN«;#”–hi½]'Èù‹·aãŽÝhW»#.§ŠÚTG—\§°tý6„¶lZ‹§r#Ľ‰~/ ÷ÑÒœòOE’)R/‘Ðëu•I="ºË7JÄÛLQ"/XJ@°Z$ùÊg}n)EE€©`-þ.ºl’ßµŠŒí¦dÒŒb¶€F]ðúK3ø¹ÀLM/f°wõBÜÚ×¹ñ"0!ÀPŒ×YV± ßë&êÑò˜YiÛ¥¦Gé_~~!¼êõÆ .5±ì÷åhKÉqa¡† é S÷µdÆå«’~å§áèþ°ñkƒ]ú(ýØ»Îu ÀŠõ»1y@'˜ó>VËù›‡ +Bš©Zº˜àÀ–­J_ÌkÔƒkâ.ü±Œ‘þ^jÂ9,§'LùH =Ù"¡·ï™RïÁŒ»ÐžcñÔƒh€% ’Rw¡““/¬Šÿa>Ù¬°JÇ¥îò›)ì&€dy\ë»ÌŸÙëXÿ¡ ß+”()e—Ù=€^}>ã˗㗡CQ‹þ±M„€M쯃s>íÑ ³ìÅ#ÔÙ­mB/œ_áa 1îÖŒbÒ*¿‹A§œQ;Â’ê›ÏfRZ·s³R\éŽWׇ텇«’yk=˜/.*, ‡…§ÖD§ÁCgƒ¿©Ò3´I{LOÁ¼­«ñ;Ç«QfHŽˆÂ³©èæf…¹;6ã|m´ö®T swïƒè‚õødÓJüźϟO€¥l:o.i¸I8–˜€‰Ëÿ‚[-;Ìzs é*²üràÍoÌõ/¦€ðþ‹ÿ®u] KJx-¦¥ÃË/÷CjZ.ÞúþÄfdâµ. PmõìY0¸ÖóoŒ•£ýq–’]G;Ú¹"±P 7J:MëøSß‘Ë/AŠ9ø>è?f\…iÒ[ºûbøÅU™ƒ]sÔ¤õ}~3†*€JM1d³N½1ž*r´gA V§÷ <ÍU¹XHÃÛðH¸q. ÕÔð`»só²P¨6‡ ½T4ó DB^1ü[Ö€“ Á,Sº£óŠ dè:D£ÿºCÊ_©q!mE¥"•Vøïq‘û–€wì¸Öxãõ\§+ºÈrL|©úð57%!î¬þZcT©–]óf çÐzèX„~÷;~™ýÔœ^M”±ÉIÁWï=Œ¹ËãåïþF+olݽ GN¤ Kd]ˆÁá­»`0 ŽT/Qa97ÊN峡ëÊ»á‹CÚÂÕÕÖªXl;p]x<žMIüq›“Êðèx6­Ø5”œQßvSmu>ûe(UN‰M|êZ%åÁÙ§>ù˜!Ÿîž~üu\ AÃÚATøÀàV]©¬œ½[þaY²Å1”t±h庩*ûìAæ°æp±ÔáDØ1¤–XÃËÃ&6è;d,?ó_¼yÞŸ¦Õ½OGÊ–¡ä”i@¢h ó)§ê /«4eÈÖœøƒøÏ¤Á°j÷æ5N|ÒbJu+{ëR°OHJ|™»K–lGãAŸà—ž¤·´bœ”ÙÏœ¥sqøñE¦7Œ“Ñû(Õ·Ÿ=6wPý„RôFœWVîAhlo‡ÓçòáØ~ÎY‰”/\D]ë1¨!Ž+$•!‰Œ§2ÎÊuË#—®ÿR¹ ’ÎÞ‰%ŒnàÐèŸÓÊ!7 6ôÅßÇ”'~Åàßb6½tö÷'ÏQ++éÆ 7uý[tFó:MV,s{ÆÒKy…õò‡Ö©£4OK÷!&<™?²Õo9&f°V7Æ<êë&òtÄ·Y ò4ëLÛÚW™‘÷½:hyãØUK~o˜ó—@K×*ªž¼Ü4&òKebAÛ ¤5oAn +UÚd°M*<ÙÚ•úÅœLf^xØz{H‡ÖÔ ±ÑÛñv‰ÖÉíѦæ„WËw±ý.¶Öö¨o#‹•,È–°“E´¬Ô¯TŒ*æ\WU‚ˆ†tK¦ä#¨µ’ðTWKòÔð~ Jõ$w)ßE¤åÀE”YŸ†í°…­²ZëxÄë'¹Î› mábåEƒ÷¢|lº€têʤÁK°‡}…ÖXÁ-…T¯Ø|6ïmÛŽÔsœñBO̜ٗ{"‘þW$‘®6”ìnMü y*š¯"e_y×UØùvÀkoÍÄÓ/¼‰gÆw§EþPÔö°ÀµËè.½ûãz¶FŠíXÌß>¯¾òÆ èŽÈ¿aÙ)žž=î$¼L-Y´/µ˜_t…б Ô, ¼h?*3Gæ+/æÔnØíë¸bþûazþQÄߌ=h–¶5i±³{-‚b{¬£g…ÕѰgCU'Aåî§è(*襡déXjTxhêD¬îMJקЛD;œÞ¿ ¬>€Ñ³:Ò8o0Ú5¦1Ûœ—až;꘿ñë–:ù°m‚ .O¢kiJ«ü]¿¿‚WìòÐÈ)?}ù=j…ŽF·&ŠJóCâûvÆä`Ò{ýaKPZ$“¢L’é§¡44';Ÿ½5‰ÀÙD¡‰µmK¼6c$ºP¢û¿¥ïàñ§ãÐ.Ø›þøÇ55ðZ×°ä4â‰7ۢŶÅÿéBg¼3´/œèΠßkN@Ô¯Ç|¾ôüºêÙÏþ/ÏtFc·ü¾f éJ¢£`áƒqÏNÀã¯}‰§f``Û`ìý{!Vo‹ÆsAÃÑŠÞñÔf¼ô1`&ù.}Q›XÃÆB‡¿~šWm4ô&wñ‘*Óõ[ûHtÑ¡s¥K*A¯Þ°àWkLyê<°pžm×ãš4£#Ì¥#7L¼‡ÓUáB™pæÎ  ®ž×”Ëϳ™Ò$¿îTÏq'+3ð; aX&É9Ž$…wñ]ÏéJá€Â7UÜ|p·"…±ng;~V®›Â§a~’YÑ’[Ø6eÿô@“›… t%w!Ót9רž%ß ¿¬ç5*Äpƒøõ}øv½G´©…ÿ~ò š„JM|ÈÉHÛ@“7˜nC9Æ"ªŽ>‚5kVaäƒÍP»¶HôȰîXâ*œEñÎØyŒæEÝ®>MX“°¸«%9VV£# ?šÑì?‡¢ðÍ–½t•7[k¸2x€š’U5%WB„2å ƒU^¼fø\æçk~4Ü{­æ]3c™ eÈ%ùlHe¯®•¾§e&aý¹l¹Q¿“œP7'å(¸ÜmÊW‘‚ˆD7hcO|þ³éoÌÞ½N4Æí߯!õµø|õ¬<¦l¶jÙ;pCj®ðºëò1©·,ß(ûùFmªÌ½×*K)£ôGùlH×*›G ÑIçp8p¢iSÔw2ˆì ™Kß>¦æfU‹x÷ù‚6/®[‡]Ô~zjW|F°[; o.·ƒ+WÌÍ5ÁžÝزåŒr"æí͹VéuÐQôó½hÑÕõG“&o¾9Æœw…T]+;“ïJÆJî1~œÿ#ž~ú ,Y: Ý» ó§äåŽ%®6é´òí=håüw4kº“eÀ™™YøßwÛñÃüˆŽHAW?<Ô !ÚøøPZêH#Z+_úÞ±^Ý΂e)Q ¹H*1¬’Þ—_…b¢í(^,vÒxdщãØ_:’7¦- Ô:‘H/ )³8ñÊEdÇbï¶¾x˜Y*:'TÈÎÎG—.³ࢠ¾£Ë-FU4‰ËRÚ”Ñ>ˆFjl¶ ƒuˆ0J ’ å£ÑHŠ’YÕH@+:+OÜ ¡*Ù]Ü„H|ûù‡ÐÔ¤¯ÒñÝš²ñÖ(ú4µÆ+w ý"K±EÔа"sŠ:E•*µÊت¼¬z~!ŽÔç!ÈQóxÙÒ’ œyr¯Fi)õ†©‹iF ¼¥§ìöu“Ч˜*9E¬XÍSÉSB:èP€ÓÛbì¤éh1ú[|ýöƒâIíª¤ ¶¸¦ËÔ&j°"œ£Aé[û*qѧ%6VŒÆ—æµÙlJ*¿j(—Iò–Ÿßê°|Œyd*Xáય0fâLŒ›»¯Žh­ÐPhQHÃ+©OúE!±~Ì¥ )_©åÊ?RØBêB«9jÇß(‰)À¹¸h К>†àÓ9£™…ƒr½¤c&I¥’fý—›ù+åè°iSþûùߨü÷i¸›[a|£PtáÉUwÎêKUBÔRúÞLMU!Œ›Þ³ OVø…Cu)ñ;55΢¡±ÝþäóX{ú œqûÂÃÎŽQØ)…ç ¹)øÖ*¿ë¹eœ »!ÙÉ•"ñ¦’ÈñYXugÂq"5™†²&2¬ ÆmM×”µ –’èi&E“‘·“FÀ{;©i,ë*à’$Š€ãÛ•þVºÊ^åÎ+/ÉrVŒštåõøãݸƒnƒõëOaÉ_°uW4.;N=N[tòõC??øSê[®ÉlmhÈAc.ò®0á+ ¯2W¤— BTće]b,úl?‹-qg‘Ⱥž>NhÝ·6ÞÐ}ú—º“n ØÍv‰•ò­€†Þ,¤2ct³u^žO€ÌõÒ~¿^Þò¿ xUÛÕÄG_/A׿áPä9åøcÔsc1¤k;˜pï` ÀÍÔ{½ls//4àâaC阵•S‚¥¦TIÖ1éµHP ïwr¼DïVû"L噽¸“ y”âžHNft´å=ü| ’ŠrQH2OtRýú5@ƒoJvEçZäÀ"ѽ]‹Ë‘ ¥5Ï–o¢\"–cÖj’Dªiáè‹g`”“AlªDÒ(ãSU“€us÷`¼õéÊ,àÔQTªL{I»ªÔoKÆŽýïa"IIµH¸ïe’§Pž÷ %ª[(Œ¢B7Wô&*Z !|–ß|ÝGø™8uê<¶mǪÕÇññ‰Ý(ÚÃàÔ¯eÏ ¼{ 4¦_é–>^ô4c«ð0ò2QýQSWã"“¾±x…ÓÜ¡ùkà_Ê;ëG^ÜŠšN^)?Kâf}ýçŠÁq}OGg2êϬ&¼Q¼÷Ü´n€ºhý^™²½¼Ç“£¢Ch¼¯ZSàfÐÇ=í°¨^ò°>U‘+>,?¥¢/ZP`e]/?™©HJÉàä%zs¢G'r—K‘¨®Åe™!‡Ê…ï­¥%wòt`¾ŠÔyµöHzifíÚnŠÁ]ÿþÙùŠÃ¶m‘t_‹¨Èì:ŠÏèÒIbÈ{ÒEX€£3ê¹:#€>1­­àÀ£C++EÂÑÒJ1èQ1F+[{qQT>K‡KÅIò½lºØcÞ£ÌÃÒåvY t<{ͧ®Z:ǧ1ÔhšZ Q&¼ŸIMCxz¢2ÓO'ð%t-äèdïšN”~ÔÂè?´oïO㽚4.’ãl‘€HB;¢žÛžXvºPÓ×Sñ $™-ÃgÐ…—>oyJU¼¬»y§1 fQíÛŸWµ[-:Øòª Iá"î rsi¼ggðKp–I†n!zFpǦ²<ÇETÛ²¡Îj _˜NýÕøøtlù'ÉÏNÑ?øæ¨sX¸ý8%ø%p²°&v@mJƒ¹©÷äg{ªr9Qé]ÏËÈ×øYM0¬ðkÙг/Jäý|L(aàeÌ¡|“ï Oc>y/¡J¦ð/¾R8ùÒIÏ,FCI¤EU" lÏÒ¾ ƒ¼ÎœB /Gø5rÁƒ!AmìKÉvÜhl)z×z +£ÇÂoÆd¤À¦@•¼håUBàPDÑKvv6Á #ÍädãܹsHˆO@zz:²ù]ó4 EFà{ý)#^Ž3VºÐñÛo¶cúӤñµ”0LZd[PñÐÞÎ..Œ‚DfæååÄ#u›ÐºÜŽ>kÅ¡¼a­H¤¬K‰ ”Ñ{@éÚøðv¼ôÓ-}ºÔfKKs´iÈWKÔ"=- ‰YHIα”šòuêL–ÄF +º€Ñ ´<¶¦®!#XQgÌ’:s6 #å`MfV–J°‘ ÛИƆVHæ¦Ô!5%íä%I¢¢IXOÅ*"©Í¡”#—‘r¹0är¥Ïfp€¬Ü|~§g¾”`“.%BJºì­P“* :×£å¸'4ðæ‚àúXu¡…»^Š+}ZÊòs'% Óô€‘ß6fô­D}Œ‹Å „„†þâóýˆˆGPPݱ³c2Rà®P@M»vm!àÍU€×%xwêÉó0£WéeyÙ5î¿¥ËR¾¾úý­É@ãÆºó%ÞOò‰ääzHÆ‘£Ô3?„#Ñ)Ô¡1#z+QÑ® ý!›À††’¶½ÂËl †­yªeM«O;IÊ —¹œrÔŠ®µ¬"ô(&Òp÷WBž–/¼‹k©x‘Ó§nس p³ò é_œ§Räy…âÚb´äe–Ü »ÓÿY@‹èÔy+* n½h³!ëÂ%þ%|ìß¾%²3)p¨r€WÀm1Aƒ€×Ó§OãÌ™3|Ø£p”îk΄ŸA-Ô³©Ï¨—2ÒÅÚœ;ÊÒã>Ä«Œéz©ààÍP °ëÈùëݬßåËÆƒ–áb]”/®è'“ŒÒÅÉ>>ö¤³uO2:7‚7ÔªåNFgBÃ’RI°8p÷¥Û—v×­ëæF*‹†0S%¥ô…Kêú*tê¬ü&!iÃçFǤ!Žï²˜H ‹¬ì¨éèGJpܺ8ÛR=Á5I??gº,rR$!2zpk˜¸Ò; n¥õåëf(fŠ¥;&oþ¨õåïºÖwyGŒhï¿ßŽ7Þ˜Ï>ûA» é#RiC¿®•ÛxÝH;C}à•büóÄ>žE½÷†hÙ²6+ÓÌëÖ*›tIz[Aýç»òWêÕ×-ëZ­Zz~*^FŽÔÿ&ÑýÒèù&&&±±éˆKÈ`t³lº^Ë'Ëá«Y|ÏÏVõq/Ä^@lX,bNöƒñøsU¥ ÅŒ{o†@Z6wëZý4$C¬¡W™~þ`©þîíÝá èÔRm˜œ/J$¨C–•UÈÓ€i!Çà’d‘cÚ3 ž3±j哊êçÉË’ †^Aq8IJ^ú…KÊÑ_»½4Ñ—[™¿l¥ÜŠ~5õ¤õmªL~ BCkáÝw‡aúôExä‘xêéç\¯!ŸK¯V¦¨{x¯ÌIfcº„VU™N²¦äQuhíº?ñùܨþdGÐ;œ¼H$Ž7xÞ„á}¿•¬œÜAÿ~zÜ™Ÿe"^ÙVY넯¹º: ys˜†'LÞER+ꌨð1 Q‚«1ÁG­ÂÂ…{ñÓ @ZB6+*ì§làÅ«‚ OŸÄŠz^F©1OòÔŒ¸¦´†‡CÞ |ÌpÍð?“‘U÷ðŠªBRRÝN­Âï¿ÿŽ]{6”;Æ€Ð@ôèÑu›!¤U\¼]øXóÁTdä×?äCNVAâÞM*Kw¡=õ ˆuHˆŒÇÉݔȌÀ©ƒ§ñÁœÍøð£ tÇSW‘þuëUŸÑzl©‰z%¿> Ì\¿À ø,¡„ƒ ÀOEdT uß2©»›L]Þs8q2Îgâ“OÀ“Oödñ)ƒ0f£._«J¨VâTI—ê-ç¥ï¦˜0¡-}ì'ÄžM§IñilÎCÝ—rU½Ol+%6˜AZµ¯ÃæUvŒ¤GZ<öXª½XâwV`ò¤a<êô½ƒsé“Zõz]¾E²™‘9Íu_Izé{ù»þÝß/©Œ‘PG@oU¤“œ&%Åq›~}àõסNÙèVdn³S+Ž&u¸„'«ÔÐ ‘¾\Ù³Ù3,ô¥$z¸Pôƒýý]I· 7¢zÀzé®Ë?Iù†$Ÿ«ˆB¶¡IÆw#n‚÷ ð ã̤»’ùóç㫯¾B|r<‚Bë`Æ×Ó pCàAÉ ½…áw§|ñ@YYŒJ®ò€ßD¿Yn# Ô ¬‰ÚþPQ!=+çc“±wí>lXô¦<¾€¾&]1õ¥>hÖ·!ÓnÄlEjkx À¥áWF.²)¹MLHÇÞ½gqèH"ÏœÇêéR˜A]î”PßL|X:8XáÙg»cøðfìeEµÁÞ+F Î]‚¶ùŒ4Ö•¾6Õj‘rT·Ä6;SþÚHJz…^Ð^™~Ýt1²:t¨ƒÝ{¢°–çéi9•)äžÝ+ mëÖpÄœMcÔ¤¨ájË9XÇòÎ’Pèt66›7‡Ó Ò ©{~ ßÙº+Zº€;Sêät¤JS%j£™D/©ÌÚA5¤JjöT´ywá>ý³x©"ª>$ãÀþ³48”¼òãÐqP{˜S·ˆ»N‘æSõ¦€Hã ,ÕœQÐz=Ôõ¿_‰³4{è³-ÈNÏÃìÙ£r© |AŒÆòM€»go ŽI@ cjZ>cГô›ëTÃ~u}0´o'ÔkYî¾î°§[u‰×ÿ´›þ gk5^ýd$ÆŒnM×Crœw·$%˜8±æ1 ÜÚµ'¨B!€·º%*ðQ%“~&¶'à d #x3}¹•¼7Sß­æÑ£šwÞ¢è=ZÓ6 úmZn•ÏïL£§¥Ë‡­­l d¬ecy%¼mƒJ;t·øÈ¤Ÿ>´±Œ[´ðgE÷ÙxÝIÒ˾¯(pOï¢E‹°uûV¼¹àUôÞUºvéþ£€hùR„{¾O+4 ôÁ #‘˜ÿùÅ£¸Û9z4^R7_"vc˜\ŸæÑ&Ð>^hìšõ|aª?Q‡›ªùô¹gÃüòÞÄÄ0ª.üç徨[—ÒIeÃt7ç’ yQ²\óæí øåæÍW¶Ú 'xzGQÖAº:ª¢J‹wˆš*8æ+^Eœ$Úœ¢q\Ýû"ÍuŠõð Î7'ŒÐNŒ›Äêÿ¾J<-R½¯T÷$§gÖ‡¡On©ªa¼Õ}Hí¿I Ü.µjÅ*ê|ú"´kE¦k4<»ÉÑ«Ù$òO‰ξ6–ê*¦è×ȨÛûÓâãpóróè69õš×Q$¹ÖŽ6°§K1KX(*"ñ¿¨ÁM0~2¿½¿{VïA³&>ølÙ“ô­̈Nb­|3º§·JDú¢´´Æ£´Ãø‡ç3ôñIôï/Gùwtßz`O“`qcaÿ = bæÌ¿èÝ£_5 æŠ~ù­Òóß_l0òðШyèM•ž'ŸìFýõûätNt·ÿ³To´ö¿qú÷j;¤jºõLÄIª…Íš5PáUÕ‹7U[Â^)pO¯¸ 3¥D@Kÿ¦OúcÇê»Kûï¸[u_gŒè×R£.†ÌZü:¬ñÈÅCïy£l.ÙüÈ¿<†j$^!TÔëMKNÃâ¹K±fÞZ:U7Áì‡aô˜V zaûd¡½—‹m ºu†7uWùy7õä+nËîÝx å*“H;ÁÀ‚‰€‹!ÜgeòW×{MèÕ#ß}·&:ìÊý½œGÕ‰Ž:JuÐÀÞœ^9V3ôuCºþb$Å[R…©BýOʪæ@×@KSlØp %ä¡ûð¢Û~7¾)ðï¡À=¼Š›Ja?Ê ‹µPñ8òÀV*sM¶¹m77fw¹íã6Ÿ K¾‹ô&]]:Iù¢y1Q Ç<¿Y‚šnH¡ ‹__e Fno½ì.Ç»ˆaƒ÷mÜŸßùqaÑ<¨ ^~¥\xónÙ0Ý ©îe UÚáAðÞ§O,Yr˜ÆkI4^«¨ ¤òeÝ£ïô¿ FGbX&6à.ÏÑ{Ôe©VÔjš6­I¯]ÿUý¾$·¤~þÓOwe0—,JSB}ß7€·!Õ£„_W÷ÄGy?½3¸¸Z£~}á™U_Vw¢Û_])p‡!Э’Eaó߉âÇO×ñ ØTd~¥ë¿¼Ã5Ã=e5üf¸Vþ{E¯—/»l9—ÿ&mUecýÜÏñëŸaŠa}-eóê-ÿn¸çò2õ»tÃo×Ê#w©‘›xsÿ Ç£2Kë6ä»Z™R–á÷k—{ù/r¿š#¤ÆÞ¯Äoßm# ½l@ °šÁ*Üè©Ái }[ò»^MáZ’4…bt«Cõsß}ä}¼5j\Ì4X´h2~üù‚]ÑÕÆ]™Þ~G““é¤>ƒ#V®dåj•ÈÎe0´ðÿ€v²å÷d|—)&¾IµŠdrÍš§•èwUk>ÝerÜTuÅ çí5kŸR¼£H ƒû"‰zÔ‡ï¯æ WMwŽØGcà&Mj2t¹è]W%žy_Ìc'ªª4àU‘ª ¹ü2JL¾=ضüaCO_¥äREÌ£.â»H~ ‰Ï³\Sñ‚r¿é6‘ñÆåY—ëÊg/}~ÞWF %e©”üåÊ•û©ï§ÔWn“¬´ÓP.Û¦/ßÐ æcÕÙ©ÙÈ£¤“%èÛ^,mäo%×þ¦ô9X¯ˆ––É~èÛÈ÷²}'Mä^}¹B©«Yi™((–êL¥ô‘²…>ÒV¥ûåhª¾¬]¼åª¤ŒÝ~Y*#¾GoÞŽÝ¢øs9ÀDÓÓ‚†j —û²‚ä 77lWÖ…L,þòO¼Ðï?Ûr/½Ø Kÿz‚º‚¡Ê=UóØY‹à/tí\ó¾ßI‡÷œ¸Õ&qžÐÏ1¶œŸc«¯5'«M‡nÐP£pâlLŠâÏŽ^=ªbð„t¢Jü¬&8”èe>**Y‰>X%v« †¼ªuR1|zÂÓÑ€ŒÉH;Ê!“ªB{œÙ¾‡öÅÁ­~0 Ô&°RÜàäãèÆƒ°«ãŽÄý§›”‡€¶MѤ©L´…ˆ9p GÄ@cjƒÀ Р‘²â¢]'»Ø×ú Ь¥;ŽoØØD ê÷nznP34nܱ8¼;¹…&lŠÆ¡~ ³Hkäóñ8°þ.¤Â¥v-„vlG{J2‹Ùžõ»v:ÞBà¨NA†Êíº˜%“ôS`j¦ƒz¢ÐÞ Sƒäˆp܆^ŸÆÁhÜ2VTÕ¸„»U(ÉÏÀñGÎà ¦Ž.hÖ»%jzÛA'm<*mŒB±Úþ-²¾të¦CFüYìgž´l-<ê¢Iû`X9ù Ï”þ¨écu¦Ï<‚C»£aS˵}MéSÓ »ÖFĖð¨éŒ°pDPìKO ÍÚÀÂD‡üäóØ¿é#õä¡FP Zt¯O'ê`àĦ]8q,>MC ±2§±™è3”í C]rAŒyÿ1”Pß–á$®2É8ÞëšÂbìÚ°¿~°‘‡N+ê /¾Ø×dÉ'»˜ªšôÆkcÇ·ÂÃãçcÍêã2´«ìVªj£KÛÅñ©–lQ/ó~O:n€ÿq®ñÖcéÒ)¤±ž1ÝL±sg8&Ñ­Ý' ßÛ§¯lLËIn¡ô»žUƒÿ¹‡Ïï ^#‹jšöìŽaxt £@Ög®Æ{«iÇŒÍ6Rà&(P%¯¨0œ\ú^Ÿ¾ΰ۸ {OÁ­UJ4ó°úÅ÷p(Ç><ÚÎKM¼ÿnÆÛk߆Sļþø ¸¹Ã¤( _ûS–€Z ðþÿÀ¹^l­ ñÞŸðmìE¯æÈOŒÇÏ?Ç—›^‚ÉîÕxqòϰ¯ík].Íþ £¾ùt5Ü_ET#j8Z %r!Ö·†w~ƒã?ÎÃ/m„±ùª-8¹?õ&øp=œœ¬àä,Fz—6h·Rò¿7¯ÞÞNJ$ÄO?ý»„0L·,-Õ”®xi|ª^‰ÖX}ñ.VqãÌ`2Þ:ܘŒø—S  ^5LuXñý˜ùwƧ›_€]N8^èð<’$%½:m\[À[ó†êÂ>ÿYð2Úø[cûÊ-HÉç‘`™‰X\”³šùÖP {¨)Îï]—F}ŒãOÂjí.˜9`êo"бëýv&H=}»Ï­™Ž-½pêŸ5ˆ,pE Öu¹%0)ÉÅö¥#Ó>sÖ½—œhÌèþ"Ó‹ÉÏ9 tY°iÐ3> m$ž©;q'âqèÜA¬ß^ŒY»ÞFÇ 7ìY<o>µGŸnŠeßþÏv1ç)@Ê~<Ñx&û~)u%Í’“¡£¿ÎG’HÞš——§XôÉb䧦ãùg»aÒäNdÐμG¨R]À®ô©Þ>®4° ,܇˜1^éaÙÑ•ûªZâ@ˆ¿Ñ/æ°ÈøUõöÞ ýD¡¡¡>C/ÎÎâgµ:Háo¥Ïw:o‰b°6ó•¾8t0–j òÌVÁ¥¥2d¿ÂÕÚhME·q¹ØC!ĈÍ`Oç÷÷s]™Á5Þûo¥@äJÜNë ‘®-APŸ¶p ò¨Î®&ZPé~íYYˆyD^섃šÂª…N®¨åj-÷¶n€ß~Ƴ­ŸB«¡ѺWs4¬íгÇKèã5ÛPmA•;ú­[§9 ¡ÔHsv‚£Z®yž­àpz+¾œô.̉VEgÁ³`ëŒÁm]ñÇS/ãpûÖh7°Zõj [“td&2(ϳ é5¶VÞ~hEõ€íEW9Óòz­ŽmQ'h?Þé;º·Dû¾­Ð~X=Â?íEyˆÊÜušÖƶ¥1kÌ ’#Y…R]¥ì—Õ?bF§©hͼmzµB‹.Þ0I­nMVá뇞ήmÑn@K´î٦ɻH3‚K:ÍÎÏA@÷~p£ËUµ¥?ºv«‡¤5ÖK—aÅ6íÝ5„p„¿—sÑ×fF&Š‹s±î“yجf‰Äd<'D¥#ÛÊ û´†5]ˆi]ë¢E½8Ip[6éDb™‰zã?@N³@D¿=j·h µ8¶ÿ~}ï7¦:EgJg΋ví•öV/ [¶ÇZ<òH[|÷ýV¬^uOO­Çåœ[ùÔ9ÞŸy²CÕT:Wv(®òYœðËfjÞ¼q”¾‹®²ì^…L7qIC•†öÔåÕ2… ”’V[Miø¤Žd›|çÿê™DÍ$))9hÉ€j ŠŒ€·zޤ±Õ·UðJçh¼¤0=àU¤XajÆJ8¾ÓI8×0ÓíWÝÞ½1ë_Ü~ÇÖïÆ_/Á„Ÿ?Bkž+ë´Åœ: æbY(ù_À*˜«ò±èµÏ°l½ýp(kPlZQ™Zø`ò¢×ÑtÓDî=3>Ī¥½ñî·@mfƒ;a±ú..`󯡩¥·ÇàÎxé×8´ù(Nm=„ÏG½†úc&aÖ÷ƒ)Ѱçô¬}zö}†`ظn@z"÷Ÿ"(-†oÛ^xkqMÞ†cváíËÑïÍ—ðìKí0}ùØ»á"÷Ã7ßĺFáùgY¦0nùËl£BE.H:X¨„ ±IeÒGOSBàRR+y]ÝÒ±lø+(¥n7a0‚jYaƒ`[Ž‹rß5*ÉTš‘Ÿ.&êçš%§Ã„†Qbd—LмpöbløeÜ-ñÅçáÁQ-`E7GÕèz«EH}O‰ÔÇ÷4^›DÏ J0ƒ«ÐÅåž¿sD¥Ã¿Ñ‡ž3†-ºÿ€ {„Œ‡mX[Ë\«ÊcrÏ'E¥`B>+¯ 2Îðáuëz’íTC Ðíݸ´ÿÕé„©ì©°cG$,©“ߨ¡/fmLF ü») "Ž*—T*s؈Y¿i¹b4ûPHH¨àà2-v**Wj]ö._‚#‰æ4~ fþôú¶·ÇÝgyƒäºã%d£V\B>êиlôàîðõ2A2·VÇ÷èÝXòËJœ{bÒÏáå÷ú#ñäÐùœÜK°oÝQdçi‘ƒÛb¨, ²|¢âÕQOl݈µ+Ρãˆaxæ‹ÿ`ô¸ Än?†\]i¥€ÇüŒ,e›£÷´èE)µ*/™iÅ0£›„]ËVbï‘bô÷^üy&úut$ÇÙ“Û°|ñI„êÇß›3;!æðidæ”°d‚[Sê/[!rã$f"+1›6†#Wu%MLréhÈäàä‹´Btí€GG›VþÐäÁ™GàVEœØpyjd=‰í'“éø¡|ßY7}¼fµm€¢Fˆ ÆË_Ãß?­Æ¨áM°ný4L˜Ð‘`Wv Õuq);Ö:ê/Úà¡ÑÍq‚àjõêü±Šî+Ë6»˜§òÊ¡¤÷Ч¬ìÕõ3í¨¢Ó¯ÿX°`?;Q~žV×~U¥v U3¢×Œù}3xCuKédîòª–‰^q(yÙ·7ZÙÜÕ“Š÷µšRµ$c£ïªàJ,’BôÝûfü‰×J%ue£ÏeÂ/È”à–Ò_Ss¾¤H0-®Õ4l2·4AÖñp|ûÍìlí³âDÓaàJ8KÒaÆßy3sè`fn-3Er¬%ƒ)-lmÐÐß?­ø/fí¥+/ª>x¸!nëj,óÁί·Rßv/¼jX ùt$jw`d-OtÝ›f,Æ™aÐ¥ÇáD‚.£*k!"WSGBÇv¥ÄbÙ;Ké¥áØ[é³' †÷„’´NvâvîT¥°Ä‚§ÞÃ./3dÆåÒ;‚ VÌZˆŸ\ü³jö,¦ENG¡ÇkM`‘s«ßø»WƒžB½áàh ûkn¶ƒºaÉoŸáQoÃZUˆ YØ; ¸L™pJ•6ð ÍdŸÐ¨{ôn³†o¯aM3’Ú·Ä·ڎÔ|çÝexulTiñHcxV_ê—MRz Ý>EÌy –jsœúq¢ŽFaîGbòãÝx«¨ÜoÒDZE3òš9,Z¸ƒ‡RÒu£MWYªÝíÏuŽ¿'õY-îú›Ã»Ý¶ÛQŸžösælBzZZ4÷c¡r͘n?tŒ4Ø€F§[ñÛo{1mZOV!Ïx5Jr0k™"çȇÕѯ II™8|$žú»Í©Î Òý L¨FsÈØÔ*IË YUi¡† ¯áØñ¶·?öïd”˜à¾xx–ò´vT=°À€ž…-i¨yÊb2g6ѯ÷4¸¶8€°#qV™£ÿŒfmU ¹q*Lú$®æZBD |ròè€ÿ¬Ýýñè7O¡–»#š½ÿ4ÜÚîF|’u;5F}?5v­> ûÐ&8¢'öm>ŽÔ ùh6 ZÒ'¬½5úØx¼íHi^j6€µ °—@ÒR«¬lÑïÅéÈqòGhý6ø¸Nm Gf¾ ÇC‹ÎÁ”7‰:@XøÔÁ³¿¼ŠÝëOBgE—d[Óð.‡¤¢yߦè=æ4Žì:‹|9úL€¦mj“.!˜ý·öm;CipÚ ï‡½¨† ËÄ3¿MƒM;XÖÅcoMD6ñ¥Wp"~ÿ «þ–:-Ðóͧ¡ö÷çgþ¨²Bß÷ŸE@M˜Ð%ÚÔï`ç’Ý8Ÿæ”€·aß]¬ÔpšúÞ ÆÑCçáÙt&+±½j–q8+„ ÜZÖÿ¥rõm”ˆfÒn郴ðÔJºz{æ/t|q<ꙜÇWtÛVkôSx÷ÃnüU(bè“Fúöêéw‰ªWëû¥œÒŸ²ãÉvÒSDÅÿ~žø)æ<æ>1K—L¦oÈF¬õ~j ?èÖ­ßÇÇôM:uªHº.m„ø¥j%aâEB Ó˜ó~JZ­¾?jÀÈܼë,ï~"gû"܉¦¸$·èñV+]^1Êxò7i8ðÅCÕøšàÅcÞ;pøð«ŠË¸ûS°PÁ©xÕÛÌñÙœµxãX¿aZ´à]•åÏfØH¿÷C‡~‹¹s¿Âøñã®Z“ñbÕ¡@•”ðêÉ£‚zß´—¬üµ²ßåsÙï’S€\Ù©|ùï¢ ÀTËÖV\¼ÉRye’üRº0øxÕ­ƒzînW,©eë4@Ø+˺tE¡]r½,Œ,[Zù<—¾ë?éû'¾ü{tň Qر|#ÂùcÓqbÌ̲,±ôËA@Ùôô»Zï }/_ç¥ï à¦/úØ-êLCÆâçõßtŠÿË^=i¼ö×&u‚¥¨ÕTÉD@x>xa Ð+ÝŽ­,;÷ªd£¯Ó(y ó™aÉOÄ*jE„Ƙî´ô´³gO ||áë+neLdl$ÆGÿ­Jýïô3®¤j(åãéM›ÂЮ] ÝîÙ°U˜ÖUjà¹ß)P…ou#==IP–ÚvÂx´-•ÅV­°ZPý㕗п$â.×ÚLüÿ ¤¿Ã ‘’-=ˆÞOÒÑ€5F=Ôþ»vEð¨WŽ«¢D› ü…l€¾ƒ9)x;²Õðhè4nTQ’…>++O<ñ ͱvíTê3Š´÷ÏwÖ`L*FqÌ¥Aê|´láæOPÜÀ¥¦dÁ‚QmmÅKFNælŸ‚ͯ¶Ñ¯ÂíæÚ}Ti;®5ç=õî$²1)`¤€r’}“Á M¸{]TÓ‚âòPª”uUñqê¿©rEC·ˆžÈLEÖ”ŸËʯï@Y¤(W”lœ›1Ƀ;GU·äVè¢A·nÁ¨AãµeËŽÒGiU‘|VDA„·Òç{™WØsi4¤\‹Â"™g&øçŸSØ·/†ÇŽ¡¥¾H`÷ÒE{ô`òÅÀ‘#g9ÿÍð5ÿù'’MGM¢Þ³/ØËWµ›.&8ÈàEô Ó¨¡W%°±YF Ü Ta®sëQÊën&ñs`à‘q‰8OkYѽ}Iú¤ARL<Òi§×ä­\éÒÂòR]ñv!¯ÛŸX½S¤ 섬fè‡×@Û_SÕ+Q g ¡—†E‹ö!6ö›x'h|«='8t¦$ˆà¡¾,¬ºnJTˆO÷ßnÅüy[QH_ݵýkàÕW`Ô¨–ì׿iîÝꜸÕü´U :ÀôéÝ0óå>|°™¾Ç?ùd#Áo, ¯ŠÏAiŸð¾³xo §Lu›3:ð::2PÓšì¨Û“‘F ª0×¹µS³óg¢q>5¯ ð-ÿºZåï)˜ ¿_/¯¿k§¿ƒ¹¯¬å2+QËÊ—S6¿¡Ì²ïò{ù<i€–Ox ¿/‹eÉ2„†|ú2/å2\7\)ûÝðYr«wŽ€,Ÿî„ž©¦Y0-â±ù=OZHC 4¸Úûíj¨ŽÇºm”ãÝåË”Öy»Ê¾]åàúÐKÃÒÇGÛ³ÐÊ.’W£ŸáÚíjcÅÊ9~, Åxþù?ñÖ[Ëè]Ï ÓŸíÃwKú&­nà¥b}®ü]†±)ÿ^ù’®•C3òó5ððtÅø‡ÛbÙ’Ã4ꙇ´ô\DE¥^+[º^v® ©<ÍÊ~7Üs¯ÞÉÇÓ³°eË„22©££R]7¯÷ІÆzïg ܧJ•„»9ð׬/à2ò1 ê]¦<®ÕæS¿/ŸG·¦°±µe¬²}õì&Ÿ+*ÈGnC¦©Í`Ëä¦fr0OXX¬ANNŠy\jF¿½¶V Î Œ‘¿ÓAvNžàÁÎÆŒAáM¿4¦(bHß|æ3·±†µäQ]΄$àYaaòèÇV%m³±] ³(¡ÔÕ‚n¼„­jø]£³`P~ÖCMWKy R‘Åã+k{öGÍhl…(ÒÐõ¥+9Ù¹ô7Ì ôƒK-EÚ­! r²I•ììm`bΰ º±ó‹ïíÖ “žì3ȸ]I1ZËÊEÀÔ/ i½0á»ÄZ\ÿ–¦ãZÆ’ ƒ-hyQ""«¸w©”™)½hpÔñŸ+§üj޳ªÜ¸U¬Z¯y3¼g-üðÃn<þxg†^•Ç®ì‚Z±’îè]…¹'w{ú¬#ø½FûÄãÐRè(Æ1BG†täKÂö–¥£)éhªÐ‘ÁMHÜ[£cEz¯Â©ÓIÊÙ|~ß}w5Òrý`/Ø:PŸzd3ôÀS±¾¿Fÿ*RKõ¹§ü¼§ “VCàO#Uªô=$̲½s¨ˆÆ„ÌB®‰gNcºœ_ݸÿjJ£)ißN×XÙ¸pìXüÅlgÏ à•1¨¢IæÇ¨VНv­–¾ß Þ •ºO#;þ ²÷%è·Gƒ!=0êñ.HZ±†)v!×1 6%ÖxøùÖœÀäò·%qáÔè`s4E.SÍøº I”ˆ”I¤z%%ˆˆ¸@ vRS³‘˜Ž¸¸L¤Ò¨&;«€› úé X€fIƒGK¸ºÚ¢¦ŸjÔp„[ [øÕr¦¥y .j&\ܨªÄ¾®h¥ñ7:Æ…âÅ—`ûötíZՌ׈jbÏý?¡‡†Y²X¤¼¥t,¢GŽ’"ÄD'ã,阒’ÍÅ5±gÓIÇn$…Ž$ºÐHèè`o©X‰ûù9ÃÝÑ4µA-Ò±fM7WŽŽ›4ÑÑ)—ÝÅÉññ;¦µbµ~ƒ]ýFDá“yŸ››…3g.°ÿYŠäï,Ç*)I>ç"/—ÚüÜ( ð437¡q“œ¬)‘µçø8ó³-¼¼íPƒ’BŽ—€:ÁBÞÍ{7öV¼3üúë.Jz/7ÔLJÊ@vv6ìì¬*P–Ôww’žgÐÎÍ-¡í34üŠã"<#Axƒ ÏÈÉ6ð ž\qCgeeBžaá5àææ@?Üö¨Åyïåíª<æ`èÝâ݈v·Ö×Û#•ÍgçNA·V1·‘÷!îKÀ+ÑÓ¼›ÕÎ zµ#óµÁ/½…¿ÂÍ1}îdX&ŸÀì'¿ÊÙ›ÜíÈò ÉÂéÛ·8?Lú‡MBðĬ)È?µ¿0vµÐÅç¼ÿBžx £Z8`ù¬Ïñî4k,ø{"V̘‹…[21íËIМø_¿65Z + ¢ž@‡Oa|ï\¼?þk¬nÐ!ÓÛ²Î"Ö)* ÅXûêGØ›Œ©oOF^øA|õÎ Ô EêÑ#ØcŽ)3º‘i‰g°g?¥hO¢„Rã0VL›3æ±»ðዟþ~CôòHÅš/þ@ËÉc1ꕱØúálÌ{w1Úw¬…uïÎŲuÖxö«‰0¹p Ÿ>÷4.ß².\ÌmàÖ²ZuªÍV•×ð½Å™Ï¶xAã)®‰îlÒ–¢\ñ““ièÐ9,ûëŽå‚•…œ\.dJÌ-¬•EÜ ¹­¿›S"Né8óe @ÈÌLDFÆ)^+âï%\œÕ hÑÜ5Fݺnprr(7’€i0|xs|ôáz,]z;×»+‹_Å)M“ËÂr8É2™ÍD‘ÖSú’’cGã°båq:C:¬ð6 éhÆ£'''88޶®¤“,-L Œ5[… ¸ÊÊJ!À '¸*¤d»öv*xÓMU³fþè߯!Bê{Ã…!‘.ªå¨ã¦ŸâÂBÄÇe\V‚xgxõÕ~xöÙžlŸˆ+ï,ฬò»öE¥ŒGQQ>ÎK§®ìi¬_ÆM^"Ÿƒäå P5ãÉ‘­2gíí"–I[M̬ .,@rjt½û‰–ý+©@îUS)ÒIZ´Ÿ2Ãv [óúï÷#Œ ]×1t~ ðØÌQháãûì£Ø2ë,utcqhÕ4xh:Ü(y3³ BíºÖØñç1L=~nVP5j€Ð–¾„ÿyJ·ã€g ÕBÂ~™ KêQª~^{;н¢ 9fIíÑXÍ~.úã0¬,.¸^¨W/mÚ„ #ÜÕ©L)cm.ø²Ø п Ê1¯á•žžJ?g(%;Iðpa'Ãð˯ÇðÅ—ÛР'FŽ Eï^ XǃãD‘û5-¥Ã®Jˆá?þ8ˆÏõàBXã:÷Zsß úÁy¡s´¢¬† 7«HÇ?ÿƒ€À—IseÎ …õÒn¡ é"ÿ9÷EU*))žóý”2^gÎpÞ‡Çonàkºu Âðš*¡³œd£"%]®rMKž.ð¢Ôø™g~ǹ¸Te#•žž‡Dn¬BBü$ó=KÂ3Dß{O¼V®<Œ?—¡‘q<Ü}02; ¾ž>Ð=?uêÖ‡_MnêlI£kÏu¡ŸðŒÈ¨Ó8EšEG‡ãø‰tÉv s>ûMC=1bdsôêUþþÂ3d¢_v7Cª°qGàÀY´o/þwå¹¼ úfÚfÌc¤@Õ À} x…´ZêY‰D¡¤„η µ0çqªWS/²€ÊT¹¶;˜!ÿ,udyŸŠRØŽŽú²fìùc"ÿ¦ßXsç[û"ÈÒ©áÇñÛó k¸û¸#76Z‡VPSÁ˄Ƿö5]X6ýÛšQ…`X_¢Ø*2µ«"Å-â¯ÂàÔ°¤J’¥†!?x)/}ù/ü—zÁ¦pkØÓ?£è”^Zý™Ïp2ÈŽ©©{ëáíBð\À2h$GÉ¢´¯¬Ù&jóR§Xôò¤Fêé“Z¶_lß@Ý`ú ε@ÍÚ"ß–°ü'ú}·M•¡´‹òÆúu¦Ô]†²:–ùáv|T)GaañŠøj†‚Ö2uwn x€ *^^~”n‰î©H&eNÈX_½î²ØÍ’O7tìØ^†Œ ‘äDÓÊü–,]@0µsæl¸qmyRБ–è.”Ä_k‘ÑáÑÇÚâÛÿmÅÒe‡ Âz±+Ëq±Þ?ìÕÛt篒 ”ºÏ}QÔGœóì¯XBWR……–èÔ© À"x !`òg¸XÁæí†—Ðôj©,]]\àîÖ†*m”“ŠŒŒb…ŽaaǨŇnÃW_ýƒ#šã駺R•Ä4a%•J&ŠÑe:Õ¤nOO¼÷ÞPŽM;–"e]cÀ+UGÕ¹YxUa!O‡ÖÐóÁœ ”¾'òÝž†®Ýú¢¦¯?¥|¢:Â9/ãÅy/cuµy¯p$²¡›HéýýksCSýúõUîOJJåxÅbëÖu<¡XÌ *‹¸qtÂsÏ  nL ÈÍòUõÛ…æ ÜLQmyzêBêòÆ)í>^¿1¹7åùZ‘Šm‰!ÏØ„uëN°ýèÑ£?û3ÜÈ5yií,þàhhH7¡ŸÐNa¥å]v®xFçN èCš¦Òh:!!îò¥‹ðÊ+ðé§ðÈ„xlb{Ž™ÓuxF¹ŠÊ}}bà¢?Ìðˆ8t(œý\Ú ø³n¹VÙg¨\ƯF Üg¸o¯2NdPÂrUtŒ«Í*ÄJbKºûðb²Óu°u´„©b•AnÆ;U4L*.±Aß™Ó0fd b4–u! vdJ;¾™Ãñørï˨çjŠåoÎÂé•4JàŠRB‰ØÙÃqÐŒ ¦¾ê,ÿ}/¼›„RrKƒ0F’6HC‡‹_ô—ƒ KÕxäë÷ñh! –>¿ z´„ƒ5¥nbĤ).BÒ‰‚s½n––\8Uªb¨ 1w´†©®P‘¢XW’a‘#÷ÕñؾÃô)xêáFì'išC°Îcy.Jbp$°˜ â}ú¬·ë¯ª¨Ž;O@íæJE6¥–ÛT´Hh4øå—=˜5k% І Ÿ‚‡F=¬4Yp„äÎònBh-A^¢Ó'ÉÒÒ’:Xy úöþŸ½ë€‹êxÂßqôŽ"‚€{ï½÷ÞKì±ÄcIµDcÔXbbLì%Ñ{ï½÷Ž(*vAEQ@éÿ7ï8=ø‹‚Þò;îÝ+[fçíÎÎÎ|sú–.]ˆ?ÿÜ„œ4'ýÒŠæ Ÿˇ^Kq(D fåJnX²˜ÎkýkÓn8«èTÕªUIØe³–S^{8ÝODQÐÙL-ï¨Q›áÿ4†6ßÝЭ[_jáJ(BªT@èHebªSb:šp—¤@ʧyó¶¸à~†4Y€ÅÿlÀž=×0aB 4iR’tLNkž¸*ÚÓ^4J±;ùWg4lT\jÍχ£kâZ¾‹ß"äøú>ÃäÉ»0oÞÒ±8¡×†Q°üŒšx;EÐ’¾¾å«—ª¤}_äymÊÊ‹ƒCV”¦™U¯^ƒ©E^Çwn¾øbd9~|k ÉÙ’1I‰Aõ…ø®ôæ3ÿ*!·ïÝ[ëÑ/­îßÿœ`½·S `…οd€˜žÜ ̯`¨Ó“þYõˆ¾Ë7SZÕļnAû}Y,ʧ}»î @s@¡Ýä)›°}ÇeLûµ *V*@¡5µ´ ¿ m‹÷3¢Z«Ö¥96+ŽF\W:¨ÁÐÔöyñèÄEœ

°ioFô¨§OqlÑ¿8Ds‹#ÿmÆ¥{Ü"dXX#ÎrŠÇ¼vü¤À,¶˜ÙP¢a ø}-N¾…›‡Žã—6?bñJ/B¨±<‹(\;uš[pwYCÙÄËz¦ýH´ÏêÐäúñd_¾KÑ8§=7Ý'U ìѨQ›¨\…¢ÅjÑ#…¥©DE(¢hre®LâºIlEuAó-Ç u,@±œô½aÜ@IDAT„݇x¬~%_ɳZµê˜9s -ZCäŽìhß~°ÌÄB½Æ™ÈÂÒœ÷”ŵk¾Ø±ãµK»ƒý sN|¢‚Óù§8›-|Ô®‹ÐÄÌ‹—mÆÔ_g¢xñ¯´YÚ¯›ÞDGµÐ‘Zµ [è>p¬KG¬Ê•-3æcé©Aή]ÿ¦Æ÷ù6QÇ%‘×ËSñ´ õGáB9°|yŸaW³}yÏÇp¤æ–ùtí¶ žâNÁHòý6ôíןö¡v/„\¡«.ßËbþõþ¾íì/ù–bRIòÁYøÞÌÌ‚ýÓËÉ#£GO¢À{—šù¹ *ƒhßœbÈO®X³¦/5©…(œ=d~´O’CÞœËÿwEÆŒ(|óÍ:Út¯C•ʰbåNüôÓ/а+cMÍ©ÑGð÷“òݧYt©_3HO¡ù\3ÞÐt]^MpíÚuÈÛKiγ’Ž{Öô˜MŒî“\L¾øolp<͉LøžîÁ¯VÑìÄ›=®Ót%;J”Èżé\'HCo¬¬þ‚ž‚©†—& –YP½g=nÍîÃj=>ûs"Fþ‹Å_OCœ)Ê÷ï„¶]Êp8ÐF£Ðc쀞ó†aÁØ5˜ÛÿWšÓ«¼QmT®ZfyâQ{ûb,ò'lrº ÓO}à±þ¶Ì=‚~ãáYüB¬ùaÃ÷Z ùƒÑ¬nN9X…lÙÑ¢åUÁ¡\ Xr+P„kÍðÆoµZ|YËçÁŒ^G†´,л5ê5.cßÔÙñ/–~û'ìÝ\Q«3m„ äP‚•«V9ã,°úÛé G•®Ÿ£EWD¸gEÉ*…( Ë?ÙœPš  F–6hùÃ@<ú‹¿šFÁÙN…j M«B¼ËUº7ÆåYç°nn ›Õ6ÇïPG 3)…n•ì ¾£$“ðôé{)0 Îç‚ÛOà`IÁóÕ‰þµâˆ4pïö-„ÓQ…&ÑŠpjhD˜&:­„…† ,< ¦VŽ´ÛË +š²è ‰ó¢”"$Ô¯_ŸZ¶‚ÙïO$†u´Y´åDVš×4í}þ<”pÜÖµD¹ò¹éøb†¯†¬¢–.v°ä‡—ââÔ˜ó÷!ü3n3öGDÁ²|%<¯\1Ôˆ¿±íìÎØˆ`Üòö&h¦Bè¥x•©(8ÅR* ¡¦52 VÙr!_˜Ò<(™&Š0%,R¹rU q²í;ß}·JAËèÖ½:5vö[Rll$énC¦7«\¼[„Ý-I¸Þ`.ðVâ¢{ ~ÿmíi;)ýN¤Á7%k|ïâñ³HFXäˆ'‡†´­g›è0B0’ïcÕfÈ·(Ñ]¸ÓÌk*•Vf°wʆ,„K" ©¢Ÿ•ÿ†ôìÏ’Ý–Á#Ôt£Í¬¥¬è &+hÉÛ.»½âK_geÊ5&ì1µÚdÌÙÁ˜mâÃû•¼)´ê–)uV›Y².ö ºóTSØ”aŒyB4Å®³9ÚQ4–:ð 5·æne’“¤"t”™%…}þ–üâ)ìgqdÛ²Ù¼h›ÜGZØ8ØQû( ƒw™XKÒÁ·w3Ö®Œxô¸Æú&:Y£ý1c/í])HŽä¤›aWÓH.(ÌQ¾zK”/E‡´¼UѨiMt¤ýž]„ò–k„ÞƒÇ`⨞pß¿i—mDº§$‰–ÒÙ9;5“ãèàNÏì|Lú:žÎqg0séѤ8‰Æ²F´IN”ƒ3\ÔÝ峯q¯f(&DC÷¾x^cʰ`Á!:°æ!rÄpšÜ¼AØ•*Q·7Ö†!žï¬­ka:ÀÖ"ä^ÔnÜ [tD3B8Fæ ýlWŒ™úêˆÄó—ƒúˆ'3\\ñýw?ã™â‹ðvÚéf¢ªè&çEx1¢1é«{E¬§€ž䕨Rµ ÞòÅ]Ï;´a•&}>z5V³šQHó[D×äqfå)Í]¯Þ§ÍM{VóûÕ¼¥<í˜'åÉŸ6%þ­=/ß/ó~ù¼ö¼¶ÆºÏk¥¼WÛ#9½,S“óËßòœæ‰WïzYƒ—÷êÖïÿ=V3z],·ÿ¡Vô&6hŠÜ¹]Òä˜#¸Ë*‰&Ê@JjQ[òGM\ —ƒ=Õ/Š6¬)[½•+W&dYb¡zRû&^’AŒ2 ýûo/zÏÛ¿XŒHÛ#"Ø{ŠvR~½ß$Û·œãöj+¸Õ¨Ú®‡wð ä-ÕQäPFæ"C)Ûêò-BŒbÊ¡ŠDî¢åaE V°JSóŠ‹­h±¢%P»f¢8œeÿŠd—’ŽH ?½¥‘æ²î}ðÐ7œ6³Ý-뀵—{ãhcK‹z qÒ_Ê ôU<½´¬²¹!OŽœˆ§v]^‰”P[2—|EÈâR…ð^—ø­ÙÑ’ëoNﳯ²ÎǨY³VÊÂTx,¹$޶*šø(„PhÇwÈâi!ÖÐGMSžXÁ+¶²Bþ‚Å`ÈH›¢·N)í¤|z+W®B3œÒضõé)ÔOyÊ–ML^&Çì6˜2¥µ‚÷«Tôå%ý‘žŸ<>ˆÀ;`À”*^ S{OÇÙÃçW—šIzk†ŠÔ Ÿ|ÿepH_ÒV–Nkncë×ìùo?2|qþüN¼Æ™7ÕIÂgqÛ;–Þø^S•Î4;¹ë4·*cä×ß"®P-Ta´(Ê¿©Jb®’7o^8˜™^Nъл`a78ëàÀû#ð ƒà9J–(G[@ )Œ~¥¯ÿ#ÐÚóü~¬üg6FO˜Ž<õÛ¢@KQš¥8‰%Ú©’¥Êâ>M`$®>ÅÓdæ5åÆŽÊ*dÿ/MT$rDˆ/önÛ€i?öÆÎÛ*´êÐb·Ê.Hqá1g®<ÔÌÛ)vé/y>ÅY¤û! ÛþüyŠQ°ˆ±7&i8%øð-”Ï›q@‹‰}Žcû¶aÖäï0wý94ëÖòRC;qjËšÕ¹råÅ#† 8™”OËÙS(MkDký㘦Ըæ‰ä©¹_ÿ_OO)³Þ!e²dÉB<ÒùÈí”?¶‡¿',Áýëέ®£V~‡ë³zÐöq£ …«Ç.!lÉn<ÝxV'®«`ЦUˆ‘(N³^¥8˜‰p”ö$ue’/ò…M„= Aþ}ðïì)(Â`4¹Mu  U\ÔêįW =Ô‹aʯ­…I³ÍR²eŸêJ¼õx˜Ò]ô ÑD?ð…]ÿv0[¹€vèo}øõHgK+<#nÝ.0ÊpØ3(Gj•ØÒ¯ÁŒøeBM»ú¤R §â(!…„H4¼ÿ?É«cDXÂxjú㬊cÚìѳ–¢Þ¢ýL\²tO·7b)IŠyQêD¾Ä¹¥Ïoy ù'³·ª`¹Û#;áû±2Ióžì”Y’?Õ‰µÇàŸaÄgURµ°Ó¶TœéÂé,+Nq"´¦œ~ñ„‹Û‘ÑUhP¿(>'&¸^ØUH¢ÿ§§Àkx›±Õk¼«%K–Äúõë1ý÷éX8g!v/Û‹ Ê¡FËj(Zµ(½‰éßE­“¨zÄ ü•‚_ýõÊ%ý÷HW'1 º’˜Á1tè ÁémgpxËqÜ>~qt¤‡çïÖ½ ZA¿GÞ Rˆ€ÀûðxŽ@jdC¢ƒpßç&ŸÓ)”4ŒMCWÑ,bÇöõðôbØá'Á„0 óÆÝ»~ˆ JŽB»d²|Óª8¡mݺ†cÆ].„+*å½éÞ¤Î;9Z+;^cÆ4KšIRyèÏé)ð©Pàƒixu \ªT),ú{=UoâÈá#åßó÷`ÍïëaKT„´÷tÍç çÜüäϧ|NÈæbÏ-* ¨Š[PÚ•·n¾úã¤) [èò'aŽS”8ËX'ê:>+ZI¿{ðèÆ}Ü¿ù÷ï>¦IÊ}øÞ|€Ð Æ¢w°ASÁhР#,å§aÚdO[Á¥MƒôZ%c)0ÖÖ-—0æÇ¡˜Gïh‰ÜKlÝd5½Òܘœ9±1NUÇ2ÇŽœ…Iª·n £@wœô(„ºò§jkRȨVÇ`÷6í'ÔoP˜Î1%9!r% Pîµð„¾ÑÞ>xB¼°0Yɽ Ú$UÖëçLh³¬f€nÝ*a‘FÌùùšwDÁVÝ4‹Ê×yq†¯¢žûáè‰ãp,ÓÆÏo0˜‰%"#Ъ[SøyÁg'T(ä¤/LÁ‘Q,NŸ9Ë(Xß¡Bù\hذ8i# ‚÷G›·USÌ@Œˆgû~“,ô¬§Õ‚P| Å7Š4úÛàf²©’l’5æƒkçpá©5‹áÔ¾Cˆ)îjcëP“~àð´j\ælS²ïO¢R~L‚GÌœ5 [·­&Œ_'’QÓ_‰nþ@?e^0¥}ºÀs VO #é)Y)ðApxßF¬pÚƒ=yò„aOàÐÁCð¼ìI' _<çÊ:’!qã9x™W×ÚÆš°`–0g+#†+5ýûøò8<’ž`ÒÏLÑ$÷€x œV =–C‚ RNûÌ%Cš˜1ü«­)Ñð¢L.Ô­[%JºOÒ’“ŒhæDuÄ-ºësþæÖÛ»IFØ·ï'ÿytø(ÉÉ:Ê–-ÍI‡pmÉùlODd8ÔÆf0`HæhªfdÁdD-bƒÄГÝTöhS˜$‚U<±·lÙ‚q㾡è!*Ñ$ÇÁÑ–ù%7 4›šýÎÖ*BÝ¥¼ÌV-ÉÛ„6&*õ«ŒZµ ó¾g×Zu>aöraÒ„¿PµBy¾GÉÝpÖt176¥ÀEG·X BGcÒ.::œb¾1aœ¡—\_èÔPÌLââhÆpè FŽü’ »(S¾ Ñ<ì)”ˆºýÿØÒU|,º|VœH åø+…Óiçÿ¨ÂøŸ·QàÝΠ½èd,²gÏÆw4yÝEtT8∨aLÑH ¹ÄjU©Œ©4ˆg?i€‚³²ù’ Jt¶b€Ï›÷'fü9‘kæ¨Q« Nýƒåµz ?©#» ÆiÍÖ¿!Lwté2Ÿ¡~+0*ã_Ô˜3ðŽî˜!lF~³ÔAxŸÍ\E'7b™sWQÆ…(¢ÍÈû*ða²ð‰äXbdlΨ‚)çOáõ˜˜lÚ¼cFáNÓsT«Yœ”V¬KêxJÌ¡Âãi>eÎw-uϾF²dNÄG‘ïK¡Ýãûd*—¦KzÞ4‘-“?”!Þ¤hêͨNòAØÿ©?±NïÓkùéÄB[Î("iØ“Kª°øœ Ö}ÂËË %Š;SûjI-¹RÅ‚ … ¶v¶pv±aT+;d±·à6¦5£…ÙSÀ[2ô%/Ý'@:‚¡ÕDæ~íÈëi°àS¯'†Þ{ƒ.£Í°zöú ;õf›¬)L2€ÈÂuŠv%¹ô¶yCž—0­±4x¼wï>C…NÃÚ5K¹‘ ®yóñ<áÍR¨îç;)/='+m[)âp‚Ž$4ÜišSÔ –q^ͲNíöÀÖÎ p*Þ Ez B¯Ïû±O³²Œ’FÁ!)yt³8 Ͼ¾1êO¬X¾‘î,‘+¥L¡eFIÒ§WÎGƒ:y°eë—¨ZXi0}ú.ò¶"_¾R “;µj5 &S)\ô3½ÆÃäÙäØþµûµNúZÓß±ÊX{úôqj&Çã¼ûi¸,kû¬\×~(ÌD•åOÁù}üð|oߤyÌ— rS’gÅ6VmÛܹC´‚ 3+¡Švíº)Úóc‰atõ‚’itÚ´³áÿ/í$33b¸ø¿sçfÏúë6,G6gî\ææ˜‘6xB©™ì¸¤VPV—Âq¬Û•óçѨ^> éSøTF¿M/ðfôJú%¯HÓ˜gΜ9–’“N’ (š˜8"è*Ûôoµužý”W,_ÁxòÃ0~Bkn£á옌“„2ÑÑV—‚™xQ‹®¢Äá;„" ‰›Œ+s¡„»Šáê]¦Ô«Wœ”aÌØMøsÆbY®¥Ýçͬ‰$Q»cÑ´iÎ%Yœa-1kÀæÍ«Ð­k_†ý®Ê1Ã…ÚoCD—.Íç(L J_i'õÐŽ¡¡Ñ :s!€7+cF(MA: ‚Žƒ¿‚q|ußFy.£$áû(j±‡4mœü<‘Q*¬¯‡žÉP Ó¼IµAVñ&&&Ê'©ëúsISÀÔ„ñ@™,,Œ¸iÆ£Ô²AjWδý‚l…Q¨JÛ´+µ}SŠFá"ÎX¾¼¶oó ¶õ ~úé+jž]éá_ ÕªÕB¥J5ù;»"üÊ6¤úG„)Nhѵµk’V4 Ã µV‚! +Ÿ{ ÿ¹ §NÁ÷3 AíÖ­Ñ®_”b0I2q½ÈF9“qþÉÅÌ*„ íbE[7Ö8Œ‹â”vêZÙºUÅ\j'Løšš/'šŠT" k–u¸}îD¿à—tÔä¢iµÐ2):ŠYL¤Ð‘;1|àð‘Ý´û=ŽsçNpK÷ª6j„oÎAùÚµ•¥”¶´5üÐßÒºˆ(†çÃÕž„:ñhÙª,ÊWpÃêÕ§0wîAj+7£@þD©‚êÕë¡dÉò°0³€!ûJúK ¹¤’â{M¿iPKD«.|/1/»ìéŽ#GöàôÉc¸âu Öv¶èúõp´ø¼\òP;ùÁéñzĘÊDâ'™¢iÊàŠµkÑa̳fÒŒfT?:¶åA¹rU”E^}~ÛØØ"ÊÂ’& /y=)ÚiŠñXzåÕ1ãö:òÛ‹“'Ž0pȆD].Ú÷ë‡â*ˆ,¡Ç iSx¤Qß'£ÑAÿ_O MÔJ:º1úÊ¥Œ2(KÒl£k&Ï”=™–»˜¿íS‰Ô Ié¡ËˆUìéZ¶*O‡±¢8yò6þý÷8½±wÓÎwµ²fÔÜä%dR )R‚ႱŠ#"ÃàïÏ~ºŒ«äùë×=i³3úKä)TƒÆÿŒÚíÚÃÑ9‡"àeTHF™äÍ„bibHs†JDo(N´—X¶ì$¡·Ò±m5²Ó†=[6{ü]¾&Š~.Î2f˜)f#Æ´ÿÚiÆ ¡y]Æ Ú÷ŠoÄ­[^Dq‡içûà> è_âÌ]’f={ I·î ¹S¦K»Ì7f|~×—©§ÀÿG½ÀûÿÑOÿtJ)Í!]‹éæå+SF40cÔ©S„Ÿb´ õÇ¥KpõÊ9zçÏo¦Fg)íådK]<íé´Ç‰_E{_ÑËÊB@„^ñ< ­«¨Ã )„9çÊ…¼Ê¢mÕ!p+Tn%J" ÌdbÕ¸ø3&¶ÂÅXÖ ³!u™Š0P­z!T«^„vóððx@ÁÿN¿Mßn"R¬$Hs!;Ÿ,D¦Ð’ZDeáƒ(qö¡FKLÔÆFpr͉¼¥‹£ùÀ¾ÈË…‚[‰ÈÆ 4Z:~(p¶LØa:UÖ,ìq«WïüT%¿ûâš—/µç÷¸q‡•gðìÖØbæ ›¹…‚mu¡ú¢)ðFEkúKœ-mlhž5Û·CñŠ»`AäeS}/%jJÕ©F¦<ÔŒ––& dY£F% ?ö—/ÝG¡ëqïÑC¸»o!ý–*6²2fíŒ M8fíˆßÍ1BxQ3"¹`dDͲ³› U­„NU«!wüÈWª4l,-^ðzæ3”¦êÿé)©( x3UweÆÊR7FáqÁÆ¥ICë2l„L4é•$oÍ”ìDÇ:''{£g„7 ±Ñ£-ñí;Oé@MV15£©Ý¹N(Ó‹§S’¯ð½PTE~rªª,û‰‚m¥C©y|J¿¸~/Mz~®ÀÊS†D0率5í¦³::Á%_>ØÒ MΛ™›oC³°“Ü?NAM;fpÝG´W{€»D®Œ˜rüûÆÄ.ޤ£™fÌðç˜Ì1˨3K–Cõ¦ÍP®z ˜[[Ã.»yÝ Ù]\èäk¬áõvÌ 3蓞™Œz7“uXæ«.^qV[|¨ìFWàœÞ—~H#° Í$±|¬­ÍQ”…vz²I-É‹íaT¯{hÕ·*’Kô42ê~äÎw!äjK•ü$IÚsò[{,çÓr]žK}¢øù è³”ýS ôjc"Þh“è±5­728&CÚ~›Ò.Ú^Ù Ÿ2uj4oŽn½{)´“§´u—oÝ\K«Ð¤¥6oÉK{N޵)-×uŸÑæ“9¿¥%/ûÎŒpwò±%ºJžÜ™øF§WÆ C;ykÖºÓ†¿ ºôì-â¶. ¥^i3t۞ܱ”¡KOíïÄÏÈyIº÷¾íXó„þ¿žôïÇÓ—·%Dx€#xY‰+ɇJÚá]Ê׸ ç9C‡*™ÜÓ*IÉ%U¬ ‰21©DûCûɶ†Ü.VŽÅë‹[¥b/,0^ÀÆé^çÔ%!••°ÛZè.Á'¢cRêŸ tÜ®+TqÑÿ_ryèÒQãÜ$åÅЩOh—Ö >Ù:‹G•ÐIè"‰t“#1PÐ&¤Jr"Ñu-íäqåRÂu…îò¼–öi¦3ÉðIÛ_šo¯~Šâö{”ŽÀûî›Á­~î¨ÐI4¡×”"”Z(ZOšé\—óJŸÊ]rf±ÔL«¹¸â9«¤—ÏkϤӷ[»$Ð38FDiZ¾wc[HͲÂ믤ƒÂ¿âhÊ¥ýÚóìGal1A‰'¶¯2NÈ5~?]±!Óž2)÷&‘'óUY¥,Ĺ1ȪÏVOI½Àû!©ÿI”ÍWœÖ~kŸ ðj'O¢ñ/©¢îóÆ©“ð{NÛIN+¦Ž(Q¶|.ŸÃ£'¡ Ÿk ;S„ø†@mAÛ@B€å¯R 1>—q÷ž/bÕ´“5³FLxKšpÞÊîV NVÁp?±8­œó¡HÙÂÊtª',™(DDAÔÈ@Ók…È @xž:ŽpÂË© MáV¡:­Ux|ÛƒNU¾ˆ¢y…D€R›Ó£Þ’öÖUao‹‡×=pãö#Ä0t³‘© ¨á71φËÃÖ87=.yÃ&6¹P¦j ÚfŠ·>½ ¨bŸ`ÕOËP y5˜3HE4ÅÙØP†0ŽFžŠµá`|ÿý´ %Û×€Q¡%IøXÁSçw¾ªuaq +&ïFÙŽ5 ¦m1A>C‡°h. V©;³tì+‘07žQD´Ñàð¾ š¤$Ø_¸;È8SFÍ ïëá–“mN7†ÜÇ“8çtAàÝ é #†·²wBVËXšùÑVÞÙ]ð”ײ六*÷nù †AGÄé.;Í´ÂÞ$ K,¬étó¢àÈüƒ}®#ZÆ¥ªe`&òuJ*¬¿GOLDäT9™¨úªfh P˃ÒnœÉr°š™{e¤*UùI ¸|ë~ä´îoí±œ57WáØ?£ñ]Ç/póI,£Ô»4è¦ýWnùÃÆÖ÷O­Æðfí°ÿðÂ*Q€3Á–_bË^OzÊ[!Æÿfôl†™óör²3¥É­N­…ù3Öq#[c›PµT|Qšp¤ýn*@B|.}%BI/i£ùý¦s¢% ¾µ“;4ÇÆíg`B˜("CÃ(ÖsFMƒÇÍ`<¿ºãÛ4ÇÎ#×hºB­7¤UÞøó»?pÿi$Ÿ^[´ÄwLOZEû_ÄïßÏ‚/¯2ò˜>%MÝ~JL%ÝkÚ§ù¶ãÙå£8ê±7<®À×ÇÏý`CÿÎ;t4|Ÿ…Âïôf»r7ܯ⑞?¾‡eÝÚ`Ü÷à ߣëxý n_¼†G 2èã……ìÿÉã¦ã9#‹¥ëÄ%ïâãÀ~Ò0\)4áîƒ.m´ÇÚo¡•öX÷Û˜+YÏÍaT»ŸDgÐ''Và‡Ï¾Â]ŸP\ Ãí¦íW(¸a÷_ópáÎUüÝç ,{fªìšø5&ŽØFΈÙcæâiXBî^ĺ»qýôjŒl9‚Õ½² c:õ…»G nYODÓ›Œptî\ñôUÌùµ}©ÿÖSàc¢€^Ãû1õfFm øÐ}™S¡-M2iRsËï™÷m<‰5G>jQT²ÝJ“Ù‚T’ló\<5R‘ ×*r]<ºUÆ&p-UCgÌÀ£ÚíqéäYD7-Š«'.¢ñ7“Ñw@S%‹ü¿LÇ“w°oïÿÐAŒød–w0†O[Z„”,Êè·ñóï;qçÑ0ZŠÜBP´3F/ýÅr˜+&©Ÿ£Ù?Œ˜‡Ù=8 ‹°ûêö­R±wôO™Ü©å‹áv¬+Û´ÔnñG4i¦æÖ­È´”vÈÇÈÜ LÓ;Þ˜¿éî džp?t•ºÆa­f¡ µZ÷±fÓ><ð9²Äâü¡£h4ì"Ô*º.<Ï]ÃÑõ{Ñå‹–°1 Áñ½§Ññç_оmÙ4ÒMðq'¥hŠ+*Vþ`$bİÏ:&2ŠAÈÛ4©QÑãHC õ :ÀªDc´èÒð…“bz‘VŸ¯žŠz÷CQþ“)—³d÷äz,ʸãÛ²åé'P¥Yeâ|rËÆŒ‚·±+ª5®êë Ðóù/SB#Ä™YVÔú¼9î®Z€-G=Q¸beùÜ‚:kn4éÜ v&0sª†ÁsÆa`ûñ˜t*Ë"øê»¦Š° Ó!T¶è;q2®Ôo‰É_ªak‡®cS`rÅ>VêQíËñ¨u¸9þè?%óZ£LïÑ(AaWœíÒ–˜«DÂ;|`˜htb6ºâKÚrMü”Ô‘ÏqjóføøÑÌ€¼ß­G¨:p £îaó²ppˉ ps4íÙV´µˆ¤Þª5'ÿ£;ëàqs0²O>Ùƒþ·R9:ŽþGk4ÄŸS—àËFÄa¶ÄÐêh¸Íȃ§LÀ€Ýñï’æhèp~ñ•0¤}Y¯Ä5þ´K©¸8¹zh<.ùÁÌÂÏü§^sT(`uÔ2Ú+ "ÔéÓ.Öjeo@ø8G¹àÛNÚÆãñõÃÿíŸpk1ŸµÑ,.ò7hI»ØËS8¼°ã¿ûe{.@Ë…[ðÂMÚ°Çy‹ÌÇVà—1PhP%gú÷—˜÷|Q¥3)L«9Lɹ=èÞ%Ì>fk¢rÝF¨Z³8®Ü+×¼ sHH8»\¨Ó®NÍšŒÃ~Æ(ž7;¯×6wI4ìÐŽ…Ê3ÐKV윿jþ„V}Úã»ÁÃÑdÂNs4äâá²Ëñˆ¨Ú»;,ù GÎ>@‹úá2v&3ÂßçúsQù×ö{¡ô¨.0³ŽF­±qîlôl?-¾ú;|‹à!+ðy~S\Ý &ή°áK§}çRÒ^ý=z d& ¤ëÎPf"„¾®éENâ¨õ8 K¯BÞK¾ÑáÏà}× ¡Šd©ÆÝ£Ëñûäù°Ì‘±÷cΓ©Bt°7Ž¬Ù‡þQpÍ—û&~‹¿mQ´R" ªõº6ÌÍ‹–!OÃpd°;EØå5Y ˜»V°qýprù<䯂Â.æ/–2Å9àËi“¡>·»}³£~¼ÿ§³‡ï§@—Eø8EaAï8²‡~‹Ó÷­±ŒUw°væ?xâ‡uS¿Ãüi¿bÇêõ¸}ã"U±0¤¡š5èB#ûøz›&bìw«ÐpüHPÎWh#t³p)ᓆÁséHLùíZMüv”ªäš|²oŠac{aÇÄþ˜»ò6:ÿ9Åä—´ç¡>éP@Î;û—bÚ` ¶K 7-’¶Ï˜ïûA8±â7Ì6kfÎÃuÏ›Ž2€!w6¤¿ÔÑ1ŠÉðjlÈ Ìù¢?â †Ó{½ÐŠ`(×£ü=ñGÏÁ°®ÕßM`ˆbž“¾–ë’BœÂo=¾C®ßcð÷õ•gÒ] —ᇕ§"ÉÝ‘AáÃh‚‘‘'³óþÄâe{‘Ð|ÍÇÂI+6ÍÁV{Í^<1‡kvc¬òÖn8ÁkÖhÖ£ îØ‚ >‘¨Ô¨œ¬hs냸 \¹á¼ÅŠ+µ²Ë_ dú¶ ÎÌmZV‚±½£Ä™!À}Ù—E^[BIù†Ý; üÎ÷ôGÉ:ÍQÀÍÙ’ø«—¯Àµx9HÜÍt§o*è©¿UOwI½†÷]RSŸWàð)Á&ªæ£¤—×3çp*µ¶ËUêTD`þ6èÔ¦  l‡†£x\ ³âÈ¡ß8ízy¯úÝ¿é g>ççƒÉ«Ž!pP;dçŒæw Ox¥xå|Øø7ªV-‡Ø Ù°eÙ)Ä; mÿ.Øå… 6 Xv#:åœÁº}RÖœ…PµauEÊÛà ´m¶לê£r~›W}jKµ„fëVâYåf(“Óò•ëâW_¥Çp”\º®ÍZÃ[ã‚,¡OIS@ôµŽn‚u«hܸ5ŠeРÕ&:˜™ fF¤×zÂr´©âˆÐgw±må^²PÃò1p&²0fþ:Yx*ắLJúaÑp˶&æ+²i5¾1 x"á~Óu$aØp%Y¤YFÞ?—’•Q°da”îÐ-*;ã—vߢÊPÒ©šŠe{„+ãÎÑÌÉ ùª—EÕctÐÄFá“+Xµûºu®†. è5¼WfÀÖpZ²dLûÉ­^²M˜6‘,c4L"±E#<,‚f»Fè$ñ ghÑ €gˆeD1z¤K¨V^U`‚d‚¶ÈîKû«iž Š Ɔß~‚UÝþ˜±b,ïn¤ÑsiQ@ûUægÌüN®˜‰EðÛÆµhX$ÓÅí't¬âuCÚ>:¿+Vyâ›1”˜¹«G|…ý|xMã(“zZ±I#£­1Tmú-JhûI;ÅG×oš™S*†$¦—9‘“`biI“ø° ½£4@ì¡ÙžxÚŽ²Ù/„ƒ85ìs¹Â*‹…B_åÞ„‹d¨2E¹nc®iJ¸&_rÝÄØÖNٕ늱°Îuýáë0¢í¹ÿ­»£}µ*Ö¦ N!vé&–ŒÅ"‹¥Ò_âD(ŽgŸÉ+®æà±y.æ.öDß?æ¡b~[D“wc‚½°jæ>òz4N.û ÿ®}„¡\Ô•ta2^{ä5sŽÀ46 {çǺ½qø~Þ_(ÍˆÅÆÐ|â4Ö/<‘N{ í#òÑ7±IÂ5©Kb®Œ¡Ñ¤9”4‰!Œ™Œ¾ØUÊ»*ôˆ%b‹MšZ99ÁŠ>„§ Ír¢ÙÐØ=w:6í¸‚N“~Gœ×Nj‡g!{£6 ‹©Âϲˆ+Ò¨)\r<ÃÂÇ# _oÙi€ÿ†«j{*œëÅ»#æRÍ¿i‰«cùÒ½h:v.ÂÎa­ë¶€½¼r©kªþn=2ôo¦ê®LZYe§¦Ä*œL¸mk /ó8wÞÆÙ²ÀcáDlÞºKfÌÄ«×qr×ÄR{~í&®]~ˆ€^ظx*µ¨•ï ¬ýík¬Ý ÔjR¶.åѵgu\\òÌÝŒ;7®ãÈê9˜?q3ª7oDaÂíF …ß¹-˜þí¯ðôô‚çñm˜ _üR­Š‹‚Ï xxënÑ‘çæ]¹±i/Rl8î^9 ÿOàuòîøø3,ì‹«úƒDˆ¥[ªf+øsg­d¨âƒ8¹Ûp†ð»w¡AA¸íqOƒ¸³Í‰†Ýú Cÿ~hѱ9¢ï£ùXä¬Ý’Žk†ð¼èŽ Ç`Åøq¸@üÝ'·àÁÓP E{:L…ᯟ;²ÿMž@AM ŸS+0{ÔB”íÚ9ÍüpÑ×mÇS§â&Ç.ÏÒ/‰zåp•Ÿ4ð‡ÿ~ðiíì àyò½€¢P¾}wØX‡áþã˜ùE ÆWQ¶ÃÞ×<ášßEѺéÊyo'ûC4¼õˆ!æ'餅,ÖêƒFct6WÚ³ áErÁ,KĆàI¨=:}ÁˆU·<ð$¿+r™š¿6«âÂqûÚ ä¨ÒѦ¸sí®b)»ã GQ ½qõòÕý¹•ë÷x=‡³$×ãé0çuåªw®hïÝ}{Å_yþí„ú¤îíaÎê0q•!¶®Ø…qÅ`â˜qQ¡ð½†f½†À8ø|ž¸¡ˆµÃ‹e’Øþ†„Ä¡ú7caKmºïÕkÊNHé—¥$Z×/gAgÑxÜDdur€·ç5ÄÅHÈêç0v,‡æµ àá}´œø XÞ+r=Š;+°q«ŠÕò¥¿)Jî¬tD•w!õI(›tîC¤•³ð¼• Í~š«%ãòÅëhðõ8½pñÜÉP‚I<é ËCŒâ*\ÔmPDyû„­óWD'BæïÜRáïš]‡Á°†•b—®û~ËqÉϾA_3oäwµ DYyôý5rÓüJáû„&ȱ™c´4võºp4ʵ„9ŸÃÁæU”„Gô_z |TÐ ¼UwfÄÆpÒ n&~ÛTÊM·+™>Äôn½ÔÚµL# -Ý€“’"(¹òG&íV‰Àáûœ^ó\EмO?d7e(^ÂÉdS´q+”h"±”[©E’ïÊû¡¿%ÅpïÞ€“ ä+Z&:Ì©Ó%ê*—_œBÊí¤Ê¹*Z–©¦<#ç¤ùN]b‰~Ï4[¸õ *òñôé£Xµ%ªu„*ÙÖpo\Ù~1†æ¨ÜæsTf©ëI 3’jCœÚ ¥·E9Nh§­¡öÞx#ThÕQÉG¹.Äà|IR™;¢j‡n/¶ÃµÏk¯kîÒÿ×¥]Q¬våÇ *K¼O1xD, ×l‰"µ4´^Õ¥¡ÐÕ©D-|Μ×öô‰ð¶æ\Nä­©=æI¦W®ëŒÂ Þ|]„ñtK"ˆNk§É> B/uP¦eo”jÁ–Ó4GÚÜᇟ:Ȼ߼°q8µz5œÊÕTv2ÌYbL4¡•––1*+´ýá¥â'ëR­¸ó‚žÊ…„ßÙ ÕBOêäù˜¢Ùð”|÷EQ7ÞxØ· ­©†€=û¨k ÍsÚrµùê¿õøØ( ï¢>é)¾Og ~ oú–”î¹+'1i’Ëä.…|+ÇÑ8¾áB"cñ˜yx*Sœæ^Ò>#ÇJ& Ïɳ¢ñÑæ¥½&·ióÖžSÊ‘ûyMþiŸIû„ÅaàQ4þ>ì»ÊL5uæAº$¥­ŒµeÆTøq[üÔÚÝôT×%ËiÖ›’´YKäî‘g_\O"#9õ‚žIe ?÷”þ¢u|Õ œ¼xWví…÷3M‡ ­^~í) åº6iùT{NòÕË=©½®Í7]¾Í‰ÿ+Ÿ4&¥]b‡Î$ÇÒViŸPMŽ#ŸÝÆé½Wðäê!,ùm=‰d#×壛äY-äšöX÷9N|MîKœ—öÝ{uµ×õßz |¬Ðkx?ÖžÍ0íâ*ÂîÐ:šào²3L…Ó\EØ2²C›ñ¡-ãÑÇRF-¥ÅIÈ^i.'­êÖA–_ˆ–¼"%0ˆh·^œ[Iº9jî}ýLÒyÄÓž·Ñ·3ÐäC:#QÛ›ŽtzÓÄŸtÍôg“¢€hzËuú庌¤$E<ƒ©ˆà&)¥}®¹ûýÿ×䩨¥0Ì™›š†UÌ“D…__”¥"wM~¶yñÅÜ¥èËccã•qBŠMu>IÔN{JÏ÷ZJè¿õÐP@/ðê9!)Àa×”Îjm+±‚Tyé\êûÌ^;I©ˆÎ a‡½•@MôÄÎ(IꥦÑL«"ÄÄHÍ´¹Ùi­.÷DËçfI?¥DË˾ì%PRÓ ]óK9õÖÔ“Ël¯)‰Ï¦WЧT|‚&<½Êøò5PxGZªá á…÷ø>#Ñ@j¨b¿Kzו³Éý#ÚÊÔdj¢-¬(9$º™niZ¾g©å{mfÊ»ÂWM'Þ1ÿ ¿KûåM–>é) §]TôDÐSà½P€P^ÊÈn’‘è½Ôìÿ.$ž“UhXnxxàÙ“'v†sÄ@†xމŠÄÍ[þزå!‘ Ìx-^ãD¹ùLŠ&I•Jk×0®»ŸÇž­ö„WÊ8Ó«X–Š€Ÿ«@A¸äÎCSÓµëÿfˆO ãdÂßß7É÷á¡¡²ŒÊ0-7"³_;sF©ÏÑ£·Cx;­N:ùjÊ¢µœO bp|vÓye±¤û„M._}¬œò:w{Õ–ÑøÞŒæyK”€½}V†éÖ;¤éöŸþøÓ¥€^àýtûþ=µœîóP`à2 xà‡f,÷M–hï©Jo)&¥Ó¶2éSØ=¼s'–üò ¼oÞ„Z¢×wÄ)@IDATÊ¥4ƒ·ÔãÝ_fÅÄÁˆZçã'2i•R„ˆ¨‚ñ›+"ÏŒŒðÔÈ8ÅšÚxæghhŠý«×âÀÚõÌ)ã¼Ò¸X¶KÍ ¿ÁgÐï'âÛØ¤©†¶K¥‘iLÒ¦´´Kž‰ ÀÆE ±âÏ µZ1…IcEÒå1ÚIJïŒL1sæqjyO¤¸i_S ÌÂÉÛ¬Mò9á{cCcì[½û×®ã=ï­²Øâ³A_¢mß¾013KÓ¨›þH’`ú“z d èÞ Ð }D¾½æÇPeæ<ȸChlt4bÂÃ_‰Îõ¦¾‘Vˆ®zÿ6ââöW'W ð-]ÞèÄó¦¼Þçy©·J&sÎÏ2iKŠ#™­ïC4e@Œ«•jà|{BE¥tçö. !y%d§ä™þ‰r\l$Ž9„µ³çPÛk„cǼØêMM…nѲíÌFRŽBxxÊ´…©)#ýïeÝú)šÚÙhòzj[¡ðóX¿p±¡¿EÕÊ5Ѭo+XZÙeÈþsV—Ÿ”ò³¦âøŠÒÙI¦ŒÍ÷!!ψû» °¤ÏÛSèåÀ”j±\á{òŠQÞ'}ÒS 3S@/ðfæÞË,uɰpvbz1JSLá„M‹ vs Aí·2¶|Ü'9Êsð£tlÇ”/S üñ\]³'L®:÷e‚ÃxšX^ð„õ„oPÖÑ;}FìäLPñTQ&é¶í>GgWü1}"îߺs+KNà)x8Ñ-q´Uõ¹}ÆQèÛ÷ßDW3ÁOãô™{$ ǯ_}Ccv|jé@zÆ0êÚá-›Ðºåg˜úëØÚš+‹€L@WQ© :Ïhr™1‰¼ÞŽ|?jÔü5bäÉÿGE—À÷òÎdv2$ײۡÁ¤NM±­g5­hš‘&¾'¯DÅDÔ€âsgüžÔ×ð]P@/ð¾ *êóH†xè2üe;…Ý\üäå½S¢RÂùŠ:0%6¼¼GœAR“T*B€)ZnÍSÊãñ¬ÌÔäóÆ{òÓàêÞ§R‘æñ4×`ù%Pb/-MTOŸÂbþTDW¨†È-`ÀöI>º-TòMmÉçÅÃâÌ"ó«÷¾¸œlK¼R6¤°eq’Ò>Naž™î¶Tòí+í˰|OŒ“©WS²|ÿê­Ê”y9ž˜ÒAãgk®JTE¯d‘ðù^Ú!òè½LÜŒ¤~§™ïÙnA:yù¾'•»þœž™ƒz7sôS殥LÌà5…nyÀ뾿"ô )Ô†fpÍSÎŽYåÿúdýÆN§ÐE§#ÁÆW’D*{ês ×nû¡XÅ*°!ò‚–ÜÜ”ÄÅÓ'`’½¬¢Ã/. ŠÌ£„.¡¬ C¿°œ÷ñTCàW¢8Ü=½Q x98S+/ò¡A ¼ÜÏÀ?Ú%K‡9󗀋—½³h%8XRÖÊk±ÄûSØÈú:§•çÒòO¥ŽCtH.]<s×’(è’õ“æ©´Ðð}?#|ï{Ó×èò½9\Ý høžBdÊùž»E†ÔVjŸ¾âs^wž’ï+ÃF‡×Tª0\u:èÛŸnàìbÌÙ¤g ^–ò.DŸt«lºeL_'œÛô3¾únfÌšŠY³§`ÊØ~h^¿!–lºHpxMÑ*ÐDò-ºÔ„“¢ O #›ñ|ðMÌüu î…Å Aî}çðôÒ^ ïßëOzÃT›χ>8/uÇž3·qfÓXüºl­½&ˆ6©0B4¿¦ÁXùë÷èÕ«9ælÜG•wð>c£p¬™þ >ïÞ›OßRÊa#ìÁiŒÛ§ïÃXg414ŒÂêÙ°ËÃ&¬ƒ$©¿´M-mÓ)Ô\DiÚ¬‹ <À:QЖc lõÜÛSG†NÝ[bÉb«JÝô)CS@øþÌÆŸ4|?SÃ÷“Çö%ß7ÆÒM©ãûäû_á˜ï/îÁ0òýÆ“÷_áû ;0ˆ|¿÷ì-œÞ8¿._÷ßk‰gbå¿G¯ÞÍ1ó>^÷‚Ñõëäû0¬šþ5zï·ž½­8@È;uÿ$ù¾ù>4ßG`ÅÌ 8y3àí|O†$ùžc¤Â÷d|CZ¯™Òú ÃÚMk0{Ê@ôè×W}Ã8hk¯ÿÖS@O”P€S–>é)ž €kNé«om"ÂnÆ4gHO hóŽ$üA®Jmñ׸o`nHÿ›˜:¸'±hÜt'œU!8y`+NyÞ†‰µ*×l€’œñüÁMœ¹äC›áçp¿ Õ ¬Ý¸j·âèØ¨œì-M¥B5#¯Ý8l]³]kO€'Lu<öïÞÿȬ¨]£nyE"*š^ö"E&¬=ÄÊ"ÎÚ±%ÊâIÜ3l;wEŠ®«Ñ·c3ä´äÌÊÔQ1ˆ½?™„Ê%æ"_# £jBºEpêNȌ٪)´^?»þ· y² ¿cä'"Ý™£ûpîÊ òƒ=ªÕmŽnŽ|:oºcÏáC5tD¥²ñàÞS”­Þ†Øwp/²¬‡ª…ì°nÃTœ–  Ù"Œ°QÒ†—¥j©¬ÿÎH6‹$šIžÊí1sÜp˜‘Ù"ýo`ò—=0ë×QhÜdœÈ÷'öoÁé+w`bã„*äûùñìÁ œ½|6&Â÷qpÁ1òýFå-†ÏÈ÷ŽYÍ5|_«1ܬÆ–µ йöÏ/ø~×êUˆÎ®ð½—'ù>êU¾×ÒIn7cûùã(R¤vÎ[‰„ÑåÀa ç+|r3~™ŠJ+g!¯­!ùÞPáûxòº6©¹(õ:½3—/C…h'8XwF>»8œ>²ç¯ÞRø¾:ù¾xò=ï}tóö:Œ0c'T*S÷ïù£|°7Àþƒû`_¸! ÚÜÀßËw£Ñ 9˜øe+<8³][ ÁQÏ»(êJ§;œjÉ®ÿÖS ÅÐÑɤøýz ¤žÜ‚§¤•úç>¢'D83'Z…½-²ÙÙ!oÙ2¨Z¬T1>ˆ ¸ûš_ ß?ã¤ûIØ: Ý{tÀÑ[Ït÷Æôo…¯†þ„#=pfï(•ÓQŠ9¢ˆ!:I`ÉräFÐÊ8˜Ë ·ªbÜ£`¶kŽz1¢”(y¹lÊÈMFÁîÉL[´NÌví«ŠvŽ/³6REáºÇ1<~þ÷Ýwã¶ß#¬ýsú ãçO`ÿú™èøyœ¡v,êÉ ëÒóWn¡Ðó¾èÖ‹›~1ˆº‹…Ʀ3ÞÊömÅ–?`Á³PÚ™Ây aü¢íúƒ7P€;¯ð}YTå‚JÃ÷‘X9¥ú¡V”|¿óLtëÑÇo?Çó['ñ#ƒZ !ß=x‘|˜|ÿ‡wìã§á/øÞÈÚ|_ç)XzÝ gä? Ùw°õÐ!¸Õ鮯ˆ”ŒW¶^VÕˆ‹Ï“û×áNxòýHd‰ØŠ+7:ÞÉ÷YÐiàhØe ¨á¦Ë÷'vlÃÚs§`“£.ŒÉ÷Óçî@Ë^Ó°^áû¥Ès «w†š¿ñêp´üf!þ#ßwð\íòbÂì¿P® ßsW±õ`¸…]ĆîŠÀëå¾'½,ѧû[øžÌôkæ¯BåÎmÉ÷MQ¹d!l¡M/ßAŸ¾ÏR°9¾ëß;æŒÁÏñåzïa†F-ºÀ•|?hô¿hW:'L]JcÀw3°@ø~ÒDäÚ‡3wáâö¿ámŽŸg“«6 G½‚ä{†`Wø¾Fþ²ÝkäFt¼) *K ‚ß”‘CàŸ»ªÉùþ´» …WüLÃ}úŠê)ð’\ 듞éIN Q”˜ö^¥á›D/zešHÏ‚3\ÞFƦ¸zd5†úœæV>Qfj[#ÇŽ„Mø»¸¡võj0 bÉÔ¶ªwÄŸûˆ ·‡¹Y~4lXA±Ùã/Þäâ=«ãô#Îe9Ë¢}«J˜³t%†wªˆ}K—À¢dæ•SÑ'IŽjïÛÈÒ½9¬oÇ –ÂõÖ]»`aooÙjöü-Ê‹Qp<Â#ãШçH4Ù°“¦ÌÄ?_WW Ýç/*gvu|4踖;o\Ú¶ýzΆÃýú†ÄRsl€À§ÏÓ©M7,(ÔÍ›µÅÌCÿÑùv¼Æ–(S¡ŠÈ½g Ì/N¯ÙMLê ÿÛÈHø~%†xŸ„š|Ç•™©]uŒúi¬C Ø5/ù¾ Tä{3Û‚¨^ îù ª”-,Ì ïË'ð=1 ¾'ß¿G„ï³ç*‡¶-*bÁÒÖ¡öþ·ŽuÚ¡Z—WøþåS²©i{ýà 츇ö4­¾7f丿 "P»B'4u1ßÕˆˆx4í9 MÖoÇ䩳±hH壺 |/¶ðqÑQˆ7´Ež<ùá¹}ù~ù>ø>Àÿ9r9 ß›¿àûY¤Q<_n•ÉK¾gL:·Æ"àöEü4fŽß5Á”™¡hóTm˜iÛÍ}6*­ùK×»T·‰y¯r¯ »Ð%R›Sâ3úo¡Ð[QM/}út) x?ݾ-('k†y2{efÀ’bhCX°æg˜2¼?uAU&ÈÁÉÞ–¤ ¸DyŽ’„ñTdX |1ô7¢gŠFÁBY®ÅkO‘þ tM ¤½ËãÌQ¿I,Øù ÖínŒ ç}Q½ÇO £ÉiˆÂ'Íe‘!•-Zup Ô÷î¡jkL>ô/Üw„ÃÄÔ ¦Ïï`ËÞƒhT®©æfÖÑÐÂ_ùíz~ß72#±mø{×U±µ¿MvÓ{ï!!!„Þ{ ½* RD° –ߊņQ±ñÔgWPô‰X@T¢ ½IïH%}K²ÿwîfCI @îÀfïÎræÌ¹w¾9sæL9¡ –Ô„Rá…‡&ÀÜî<_ö&~^Ú ¶Á)#XFîeÉ*÷RŒ®0 ?ýð´.Zì]9[þ¹w€Cúüö÷*ôi3ÀR-iÔ¹†à¡g`ä]OâÝz”K¶ü$€Ò†èÏÀóß›÷RîÇR›ˆã;×СE~eò¦È=ÅÛHd[RŽ­roK–vt#{`ŽÙ´Ãgß¾‡ö‘¾Škó"†TêK´³é< ò›;‘I›{²¡’A#AxBzÒðö[¿W2_íJ&|îØ1½z7¬]„©Ô\S¨€÷š²».VÆ7-üù9¢Š U]Ý ¢½qñôG4í9Ë ¦ÊIUTQ« /š,[ùzµŒ¬„ÝX³- çNq®¯ZV~‹?ca:’Ï܉J _ô†ú¡qÄLÌšö$ìœ#1öæ^Ô²3™€C©7?i)iÈ5Hpе¡}¥cn*´ÌÛfØH,zë]˜©Ñ²ã`ûÃ̱˜ú¿ÿá©{ãŠkSì†nÁcC~Ä3ïssœ»/1oiÂj¸¡È€ÔÌ dee#ïœ#FMÂMB°ô«é8“f@Þo/$&-ÅægÑ·qü2Y9‚[aºcÚºa+ÜÃZ!*ÄYééœäÐÞÙcn&RRÒ©µq‹£ I­j¨mm½k‘Ü»Q‹åžrä@¹÷L‹Ç²¿V¢g‹ÈLص”ûF]DîÓŠÅ^.DvéH9—Éw ±)–HŠ·lÚìÐÃÞ¦Ü?ApÁ=Z˽†3£²r¯urAAüfüùÏNŒœò^»½=òÔò¹Øÿ`fsógü=ÝøØXd[ÎÏiÔù<2h>ž£ÜÛyøSþÊ@GiíñÓ)÷™ÙiÈÏÒ`ÔèIÚ?K¾xgÒ)÷Üüi‘û¿°…^LzÇRzŸ°¹ÏÏÄÖÿÂ#²-7½eá­©“°!½¾#Øv·Gb"M‰Ý)÷•¾íypÄÒ#Gðø’%ðrw†=O–$Ü®´¨hœ´È=gÀG¬®tžÚ”095 í;ÖCû<}Îr`Pm¢O¥åÚp òO̵¡G­åF䀢áõ±¨Vªð’½ÑX!›Ë´ÔØš888†–\wæ²þ”nÁóß½Œ ÛçÃH ÓiŸ.˜6 l“þ€½#õäæÑ¹ù„ÀÍ9¯=:†i³Ð¯UT±öVNÁ¸wxŒáKô¸û9´Ñ)!QšÙ;9âèâ©1ÚD¯_‚" aßâVÜæ·{›bÂÐ[áLm™¸#£+Qô8‘óÅ¢õÜAÏcJõ¢UeÇŒ:Œ||þÞ¹«\Y„y¥X‹?Ð/ï/ÿ¹ùãF£V¡øzÆ$lùÚY)¹o…ß~ѓǡ³Ïßxã™±øÁW‡ôŒtæõ¥Ë1ŒÉûðæ Ñðž1cx¾˜|6PKuê07»ü/þoëè6aîОZÀMZnŒöˆXØp2T,÷ü]Jîƒ:Pî‡áÅÿMCÂöïa ç’3þÝñÊ€.°=µ%äÞÝ'®Ng0ýÊý˳Чy}N€ŠøDìfvÁ¤aq¸ã¥9ˆ»gúÙ*r/ŠX;gGY:Ÿr¿¡XîZÂ@m8kÓã†t‚5»Š{@"ò~ôÙ›AÏ K¸aÒž6ôr¦…"÷&;Œ~bVî\…uIʽ[¢¹õåëqrÔýhÔ"_½6g{ 35õšÖçæÔPòXtò\…ק܆ï}l‘–q:'?j¹)÷©»ñ:å¾Ùý‹0¹ån,ÞxnnxïÅ;xdbÖw×tÜÛ¿M•ä>¶ÈöÜúÆ##кIÍ.¬Ì»´¬=ÖJ"áÁõD»ýø»ó‘žÃƒ¢KªÑ¯·†¨ô^1TÀ{Å,T ¨&ŽFt9D$Ô÷còëñµYa+/™@N²m;ô9Àž#HÙ÷®±@‹Aýáí—cÓî}°u‰îôUZ?À™öñì۳艶–}czâÕ·æbÇaÚúz[LJÔ.¦ ­‡MÁç~ýÞªèöV™f í†<ƒ7›%tp Uò)ÚyÕG¨S,t-î…sËÜÆ>3 ñô+ê Ô¥¸çíż…ÐzFá±ç?€[ýz0Ø8 Ûø{•g¢S;bëèóŽ¡mrݧ½]»/EæÚßü<ÂÌAàü邾2Qî‡<üêu\ŽÍ»÷ÃÖåVôèM7cÎÈÔvÁ³o…Òå˜V‘{¿Ø8¼:s.vуCØEä¾íˆgðyÀ@ÔkÝti­È¸BÃMÏâÍ¥åÞÞ+ !Ž1ø°µz;('Š+¦Á(…iÛ&þÁÞh÷ÀÓ ºµ„Ü7Çëï/ÁîãYhF[ÚÒrŽ—fŠÜïCxÛ.hÜÿ¬ i„ÞÖ íDî³öaÃŽC”û(Üö4åþLž"÷î©Pî—Ñ^·:h<þÂp"ϸ‚ó7ÔI“Q1d§™ïÓ°—'÷¶Ô 7 óGÓ&៥:¨pñ¦]vz27ª¡Ns@¼uºû¯Eã©ÉÎÿ‰ëåaÀŒ‘¬ÔŠŒ®Eýµ£e!M:!Œ`—¦¸B~Þ/„šuˆ–]*D‹÷1wpò F·žÁŠ7)§€À­»ßÌ”e1 è%ƒ¤±§mïAÁʆ/YB– ñA±íÖ¤ ðž‘…V?~…ü‚30tèaé"¦7Û¹£=ý¢SU¢ gkyÒÿ¨¶ÐÀÒ¦’š;ѸF´èŠèÖ]-\9£&ÄÊY–ö´Æ˜F­‘vp9yy êu›\Mxë?Ÿ"8x¢‚ìP¨³G§¸þJÝRg›^ƒ”El¡*n,eµ¯(ZýªE™ mÒá¤I1Ç.C›Ü7ÛØ£y§AhÕ•vâ’Î*÷Þ!”ûRrߦçÍhßË’Æ*‡Ö"¹w AïÁ!¥ä^ä2˜r^Vî/÷b)G¢ìä¥åÅ3'¿.ɸԣ̗•{Êv@t;„ÄXd¹¬ÜG¶ì†mºÉ}]‘Ñõ Wžû€6Ó¸ Rö/ÅïLAýNw`ˆ«o½÷%BC‡è‹ÜÛ—{³;âú’†áJåÞ(/ ñO(Õ…@À«nv­ ]qUÀ[1ÔWÊQ›dp v6U†7RàÈ£ ’Öµü Ú& °"l&cPÙqHr®F–  .5\)y–Ë¡AƺR&°ÚSÇáôÚTpt†¾ µcÖ42P[ë.‡ørË+*\–›‹—œ'Úé’!Ÿ"áÞÏ?ñæ.\Ÿnƒ{à ˜2y üí4Ï %ê,¦£d!êõ5ç€YÐ[B…r/2Vr_Ô«¨7‰Ïµ¡Y å']J—ûÐ^©Ü»GôÂóOÆ·‹þ¦Üo…Gã xfòSðãžTE‘[BîË>ûÅtVúÂúžªt5¡Ê’*à½!»µ65Š£™œ9{ssªühÇ{¡n³6[%ZœéƒÖ¨Ï§v¨ ’«R)5ŸXà‹†£ª†ö±š\.û±»®U( ±pܰwÓ<=ÍGg¢o‚ŸJšVšLqe ºÜ‘}¦†+〠ϱ6pöR@MaYG!WVríÉm{&Y!¦ÐÓ÷ª%rßkăèuóƒÈ¥ÐÕ{éñú¢ç&UUî¯J?ª…^_¸†CÛõÅ•Úêâá”3­÷žå2ýØö,TàUÉ@íïRkœ|— Å_^Ú²)¥ˆŠ´d°þ¶~ÓˆµkX8ò²³ð矋8øôŠæ«øSÔ*…äóñÅ-*N'÷ÊO[\}%Ó*eW&mÙúØ'….®(ˆˆDAp»è<½ ,SÑê—ÍX©¬ø·5½%ÞšÖò]qZwƸ.¬ãî"™;鎭tÙ)ƒÑ’®¢ú46سw;Ò’Ï¢Mp°ä²i§µ?­qò]^<“–ÊKkMX6Ï%Ó–Il¥Ëú]\æEh–û奵æ+û]Ù´’®dZÊ}‡Ð0äœËÀºõ«h?Nõq°’òäBê._,J<Ë/%G–´Ö>µÜ“„ ¥ÒI>‰#yeã•´çÓ×W‚†â²ŠÒ—Á¡bïúò£p}u²¥íÅåKb†RåX¢Ê‹—d¥Ó²ì¢(‰×‹Ü“·v£ŠÜË’Mq]¥ÓžWаü)Ž¢8¥=f#Ö¯[…ÌôTt /Y_ì#¬ŸEÇIyWX¶â÷÷JÊPóª¨&¨Þjb¤ZLÐsá0›kØÜ4wncÏ ²ÛCL$¸[ÝKe1NN! §¸9q½yd—T:me©OÜ›‰«3 r˜Ež º¼veœœƒ+cE5•²YNǼܰ I§~Á_RŸƒhûÌp俯´²ænÜå;õ‰½›¨J˜žš-eí•Z]I×ÑÓw7nŒY³¦Ãļ÷¿ ô;käK>‹;¾ ˜Ï‘ZEç¢5É\æÏ³ãÉdÌëJ_˜:®‹2M¦½# ¸“ÝŽÚº4ÒpÀͧ_.?œõzØs'š™i³ì칡F ér%6¢¹´Ñ"›õ‰åˆ3µE\ûär Íc¶ä¤Õ²>íKÒæ$š]¦ÍdÙ¯~;WwØ9®8ö7Ò•‘ž ÔžêVm´¤ÛÈþËgýB‹=óÚ)m ›4zŸÐ³}Z¦³çš¬–t™˜VÏx#Ó;Ò§“NúPºyóí„®¦eÙŒ71Î@ÞINäܳe}Òk¾3Ø›,e¿&Ò–Çö)4RŸÐ¦g]…[Æ[Òjè>bÛáC˜öÒ“hÏN½ü‚5E…¿L±­PÖ‘É5ùM+;I‡ÒߤAñ¹*é•8ÊiSҊ挴SfQK‹\[ËlQUK»Ù6H~‘I+køÂ IkÏúä·¤y“4ä’^âd— Û§”AYQÀˆÐ,r%`EÊ•ôäOH°ÔG¾+ío‘k)ƒI9«°Ä MJZ©OÒJ}¼GYSèÚ(WJ[%­Ø”0m—À މÁfLE ÓôêÒ§ëÑm•ÐAzÈ#‘ ©Pd%ŸåŠlÛ3¿ÈôµÈfisd$^‚ž}šÏçÆƹ"—Š ±<éS‘ Æ9°:îRäBž%qÑçÌö‰ É‘’V‘ –)²e•Ù<-mcÙ7ŽÅ´±>–)iE¶ØO™%Íl£kb"ÛÊç8é”_ÈZ¥…¤GäRÚ(†&¶!¿X>,‹~†¥-Lkâñ¿6œÈÉóhCú X®ÔÇŒ ?ì™:øŒ°íìuEžEö¥ž<Ê¡<Ó¶,SÚ.Ýg`™ò<ȵÂOÊ‹ð nƤýrH„´ÑƦ‹þù /¾ôDE£{h¾ON†»”WÒè8“ïI‘9ªØ…ïI)˜›A‘O9`(t²§ç Òl Ò¸ÊÊljz&² ¶õwUÚWÙ¬j:•Wƒ|£©AåÀUæý­â£•Àoô2æ¼}+¡ ÿؽ§€GçYÔ‰]€qIÊY+€û™†/à™·Ð³Cpú 0i.Q@·ÀËÃ-D·øßFÖeÑ"wkDôF?RîÉ4 Šç³Û™–÷ÿ |@:dHz8Žfm9‚ä!nÃarÐk¸…å„…qw¤Gü³Àï¿Y,Ëžƒ~ÛvÜ5ŤٲEüm9€½Úªôt´ðÄðùÛˆæ˜ÂêÑÀ0¤òþ-Ù¸?9I¡u¡·/>uõ†Ø¿y6yyÈã`÷lP8Žó4²îúLå+ƒ÷jO¼Eߥ¢ðy5-mØ&àwB°ÑÞLùx31Ž49;ã9ß`dH<‘qýéÚKÆ¥ùÞ~øÊÍ›m3âí¤xÌœ"¸z"0gèþèîs©¸-ídXâå.ž˜˜ž ïœ,eÀÞí䌷˜ÿ¡Ì4´ÎÉ&¡QGg|êádÒòdFüóèê‡í<áè„7ߛ˧CéƒÔ‹ .‹|\êì†Eôw:=ý,dÁp†åÎt÷F Ë»exëóËSè¶:9±>¼“ž„PòF&ÙN˜åá%½‡3RáÃxÐ' Äžô ÀÓç’Ñ‚¦=¤a¶»Ž<•‘‚ Ö'»ÛÏåÐ š›Þô‡·ôi&ýS£fMhÊÒ²e@j Gwrͧ€ôîÃß©ÀöuÓ’¿ 6]»-ØŸß<ïèD™%íXõËͲ€F?`ð˜×®æØ1"‚Eò­[r—_ð7ÓÒ‘¿V]?|$°ƒ²¹w'd,ƒmF£X 3ë[·–çCd ®¤mÐ`KK—gÏP¬ 2#",4ïÝl$Íì¸0m‡ö|v¢Ù¹ðù9m©/ 0Ðò›>ŸÁ~UhkA›U‘o‚$=N^ð9tç4Pêp½ìONø,;4j„™ýà¯ÿ}ƒÈ§îáÑ×®ÈgŸ¾ÆvŸåäë…ŒdÔÏÊ"À´Á–ñ–›Fq%dåÈ…ý”Îçé3š l xŸÊ´­ùŒ x<@š_e|åâÉÔ3œèåCÏgi©«f»z⡬4ô:—ÆIUÉÏ^¾Êö×é)gàË6çóÙÛäÆç†2;!çn¦¼ØlÊ„r¦/NÐÕÝË©gQ?7[”{ÝÜñ2åmŸ»±LëBPŸÎçã+–ë‘|:ʎǘÞhOùJåsú¦»YFŸ§WøÜð]ËvBÚhަoè¾lgÁãÛ|ÙÚË»=_ ì{ó9=Làøª‡iÖ``~6F‰l1|J™]gÇC^ÏLAŒø¯f}O{ú!Ÿ“·æÆ'qì¿*^%m |‡ø¾H¦¯ß|l£«³Ö!!xgÐ@h÷íA÷m›1‹Ÿà\ö'Ÿ_ª˜ùr™C¹¥¼È;öÊ!Ý·aå6`Þß owwn‰zãúÀ]4ù‚†ù¿Ü ñòÒ±™šré·÷k|ȫ掇7ß{jP9P“qD *®.Dcå©€ÝPO¾l¥:¾(~õ}yÉkO‚ëÓŸƒµÄ‹&X€¬ÄËwãò 4äÔ6kZ/‚qu&§}‰ÖVâåeʺ$NêSãE[%õ1Â×rÁs&‘¡F¥™€ m[ÏüQBÞRËt ¨p"-B'A-A&ñöÁG&{4@à&ú¶% ³ç`ՀLjfòT°.‘Ú Pa^ojqäÂ×âWKà&Žæësr¦á*÷£Ó-“Å·§5A±¦c¼„/e_ÒÊêG}> väŸD¶‹+”oWN¤¬š²Oº˜Vò…²ÌF”u;^Ë3'‹RŽºB›L 'h–úTQs+e0 yj©Ï¡¨>wòÔ[hc¹nœ„OiÞ­ÛPþ([g‘éè‚6h*²Çº” âûÐôû‰L’0a(ùº.ËJKÅoÇRpwÿu%‰"½ƒ©˜`2å](e0òýbгOÈŒ¢8åyrò@§Hw,<˜Ë4ÌÄçQ)ßšFäQ‚üfÛÊ B“ëý¢Ÿú%^¹[É?ÖÌ’œ×ü_¥ì•¬EMV{9À±Î*Iµ—H•²êåÀœÙsðÐCàç_&¡wïf,\fïW;È+Úúz±~K%_BÖøòâª#myeØáãOÿÂÌÇâÛoG×­›i‘Am3µÂ^8€(d•GE^Àe‰S Ër¦RK‰µµU%ã­Ü+>*•å^*mÉüÂË’dˆsu Ör•üS^|yq’^J ÌQS+B[aQErŸÜP,M¤½Œ·ÔÊHI+õK^‰·~K}奕\ª>)SÊà0ª„K¥-K›•fÉ(õ+m.j‡Â8Æ)Aâ”k©I㕸’×eÓòž5ŸµL)C‰0aÀì 0iË)|=~F‡pˆ]²k>¥eaÍW¦þŒO<†ßO0ªU <ŶԚVÉfÍw‰2. ­DÚRe”©[hSª+ª£‚´R$·Ê±ð^J”8 Š\ð—Òtþ./­¤+/^â¤,鿲²e­Od«äÛ¥<9´`+‹,_@ó[ëQhbŠäCê™—<Öúä¶Ô)AêUd‹ßK+ü°–_\ãDž%(ò]T_¹i™ÆZ_É´«Ïú¬ÉdZaºÐÊÉÃ'Ôþ?±|)þ˜õ º¶µhxˆs>U};A¸ö‘÷ÍÕÛ×îâ)rœhÒ?°«½—ÿnÚ€=ÆHŒììG-µ…WÒ.©óèªßpçW‰˜ýöhØsÒåîæÌy˜V1ÛJ9›“Ö™“ #œ9A²“ÆIÈ=WQ22r9ϲ眎nNÂÄu°-iÖréK/“NpìŠxgÉXÎ_¦ùôÇ8š’‚+&s>É Mp>úïŸxùåEXºì1´m[Ÿ«ªÖaùò>üS¼ÿþG˜0a|9•«Qµ‰¢8QƒÊkÀ6Ê Ö7\É{åÅÉýòâË‹»XÚòãEÃ)ïZùV4]bO©$-*[â­¡äuɸ¢x+”P"]qœÜ(“N¹WÉ´U)WI[N}%Ë(EWQe@åuñ=Ò&ƒº5XïK;Н­7'ù¬y­ßK+Ù¬e”L[²>kÑ•Ik-Kê+U[ù^\žÄYCÉk«Œ)·‹Ò”¼_Ñ5µ]à!ƒÇ dOÝ»UPFq}eèá ½'é0ÞX›ÞMÀSY¼(“FèW¢äíIië)šK¥Úk¥¹8mQËãË)W)»œxkž÷¥ï”þ+ºgå}É>-Ns‘´RÜå–aí÷K×'5œ—‹K¦U’ZÚ.é¬å—äiq\%ÒZù!I+*£*i+¤M&ëÖÀ¾‘÷[©òåžhtËÑÆŽo\½Ÿÿ“ˆ7šP-Ø™Žmï}ƒã§xbH4f?3Y‡·bòŒŸ‘ì][·6Ásÿ×CÑæ[ä’ÒÈ#êNŸ>Œ7?Y„Œ¤DdÙûᧆÃ=á &ÿw ô^žÜLjÄ3<=o`3®(äù¡uf2f}õ;µÒ4Ÿ:’ŽÞãú#8ó^ül ·D÷`=¾^wwOˆ±íÂHn‰¶–m¸‡äd|žzò'.ÄȺ-¶m? WW'påá*'—u£q@¼7Zªí¹|ȀݾߎÔxs ³äàtù…ª9ëh¾B;R'ÚªŠ¶Í@à!ŽïŤÃò· ΫÄtD†jÑR•RT1²oÓžØK“.4€=!WŽ•2L"«Ì §ö‰†±€;òÿÜ}€‡vD¡‰¯í\å~Ýã¼Úâjà€"M26î>„MÇhVE¡ÝägŸò<1íαð8µÃgl‰4„„¡¹tCûbj`š:”mB…œˆXH¹ô¨g ÷ìxÜþÀûxyO’‹Ì®}ñð‰‹Á¤[ÃëËMº²©˜Ï‰˜½¬ùí|A@;õž0'žÄ/÷µÅÐÕ{ñÑö#ðÒ`Ø-Cpk»  Á®<;&>Y´UþçŸCÊ«]â$L¸£š4 åÕµXåTªTÿÔ TÀ[ƒÌW«®…àF/ËIÖÇZH£JR­ç€,7çó±ôè^d$äá`– ]¢aHD07ãñÏîu˜o‡8?#öéðxgÚZ*з¨i3sS1ÿ` F6kú²>z|7><ÉJÞ.X}ò…/hßa…ذ¦,ÝŒá]5HÊöB×Ð`8ËŽG5¨¨*d•‚¶ùŒìŒ5ûVò˜swDh ûµf´ r§‡ŒP´ó߯S9Qsó@xˆ îžð¦‚ö»¯Äo{¹x|ò(SêRùWì®Áá˜Ð­>Þ;’‚v“{¡×kѬw'Ü>0 gÖ®Äÿ½·4Fã–Íàš‹v=âмžZ†Þ ??/*~ðÐc°ãÁWñÅ&Wœ|,”žg*a†@üîD3Š˜˜ÌŸ?‰&Êôh#ÿøŒºŠgŸ*›2T•¡júÚÂðÖ–žP騠ÏJeƒl M‡T\(;âeÂD—wáA8”°wÌÛŽOÇLD˂økÉ*Äœ°Ÿ9Ý]|‘ÁqÛ]VVEÝKլƜY¿.¢Ç O mÙ¹™ ø¿?—ay¢ñÜŒÔÜÍßm]Ž<7?¼×Ô ¿>ŒÜ}¿+é85Ê&´ ‚³lM°TT™…Èâ„âSH:ºŠt£›G5¥4 §1eÕBÖrè2-íl& ðÂÀÁƒÐµ·‰¯Nxû;#aÓs‚g¤æVé9¡‡›+ sõ `6ÐZÝUzG6Á«EYöºÑ;ÏâO·áD<½µ„u€òqðp<éŠ2ñP<\Â[ Kþ~<ÿízÌ×­W¬œU;š2„r³£læ´ªx•oõ©²x\§TÀ{vœJöUà€Üõk¨Z£†âæa4kà㡆«Àè¿HÑÙÐ}Ü-MZc@„Ô DúûŸ`îþý×7mÇ✠|7¶/<˜¶@ŸŠÍ©ôÍ̱׆kÈÑþ>ˆk‰ï·d( @çì‰~¡HvñÇ·ÃûÑY‚¾•SðÍéTØ´m€§:¶Ãÿö­ÂãqÑŇÈYÌrTÙ½ñíj´›Ýòéycùš}8Co$ÿ¬Û‰¿nCï4-Vïm ûä“8•’‡Í»Ðއ{:`íÚøÂž»¢aP‘9_§:nüÍ+Hǜ߷ $? ¸bê´¦8°óNÓa…v×.l w@»úžwè¡¢ÿÐ.øvê< šŠfD4h]&ÿäO´è?Ccíñs— Qˆ?&õªW±Y‹•ÇÁ$¾Ù•Y¥ rɈ:TÀ[çº\mð%9KŸ©¹9—L¢ÞT9P)p„ÕÓ¬â³×Ö½Ò/2»Óok]áy9{#\Ù0¬Ý¿Óöœ„7 9ØÑ-ýø6 ð† ÝHÉÑ:ú^mM¼0ŸÈ2.5Çîž·ULéI€6ÁâFËHw<Å༫R„ª‰T”àåÖ†Ú†-Úá‹v”%ªyµôy8ÈßgÜérОf ¯Ô£üÑÅí–Û‡Ãíßð¦ó(wÚ.o +€gDc|ú|càq'²ƒðÅ»½æm‡“Çüðúô;÷~¶º¢•j-îÕ"›bî[žX¾‹]ÿôkŒ´¤3xúžap¥‹KtÂü&méþPŠó•hƒz©r ¨€·¦¨Qu”¢ã†úµúª¬£¬P›}å kz¡4Q(¤ïÔíÉ™ð tâAÖkæ‡ÚôËà€8ã?‚ÜÏ·nàñ¹áØ{h~Î Àw­šâäÉ}X™šŽDã!ÌÚD³‡èDºØÑLAÜ10ðf¤&à‹=‡D÷L_ì;†ñ!:,<|ÇSt˜{0ŒX:‡[œÀß§£ÐÖÎ ~¦L,ص«é j OM‹rQMr, UÿV™½Ê1×’Q@ï£-EÈÑ×eƒR"GÇ—äž„²ùÊþ¶¤*ýWÒ”LWÒÍZé”ê/••†x+Å&5QြèçÊ·¼èÕ r ªà.w3^Œ Ozâ?ÌcŠ=ü`QÇhB{Ǥ³Î¸¯ç Øó0‰<3OÖâib¥ím™ŸG?·‹i…N1¬ÜÞI9ékp«ÌŸŽÔ´9댘Ðin£˜ú3¿»G0¦éõ™ÔF€SÙ2«ÚˆšIo$@ÒÉêŠúìÕL\´Ö’šÕ‹&Ro¨¨ÕPo­î•¸kÎÝ;hÃËÝÍ[r÷^¾jP9PàÕy`tSzú`èWœ—²DU€O0ú–˜L•Õ\QæÜéÇw€{Ñòq‘ öõ(ý;̧¨ ¹ÏO/Ú\ö*J«œX\ïõqaæóöíŽíÚ ^rL²µ-×ùוШðO ¼(ñ2ùS…?7râÅÍq|/^oý¢Nž.Ú½uí† xëZ«í½8äŸÿpîo‹‹§S﨨¡ÛL#€¡"ÐP^š²yÊþ.¹ü[!}µ,Ÿ½ä,ºbÛ° Ü­sllÅ<ªeM¸^ÈÛnq¬²O¡"¢‹@±î×µ0{ºÂ×J2V”«ÖÝW1o­ë’!H¼5ÂvµÒZË ž¯•*®!lyÔ+žÝu& ÀMóXZnfRCµq@|ÑŒ&Ì[²[÷Æ+þu+S¸ 7…õ9“<úË]5o¹²q²2ùjKi÷¾cg`ï¦ÂÚÒ'5E‡*5ÅyµÞÚÇјuàÑÂùzõháÚ×;*E70Ì\6ÿqï>nò/Äú“'‘žŽwÝ¥‚Þêéuò1ÈÕþÎ.øqÉfÙRYùr™týñµúæÏÊç«E)å°ŒÞmc¡{y5ÔY¨€·Îv½Úðr9À ?ŠÍš:ЖË5Rå@µs€¸³ÙÙX|è Rôá´4l9}!ž´VŸÃêa7Í]úFFâ—Ñ£‘G-oU–ø Ì{ï’ßáåŽ_V=ô\ãR i^áÃSÖdõŽŠ 5ÔI¨€·Nv»Úè‹r€/¨Í€ªWeT¸hê •*.É ±ˆf ‰YYJ2Ñòþ¼w/†ÆÄЗ°ª‹:š´ æ„¾*ï@3߇aöðñtDÇŽâ6äz â"M5“¹^{¯:èVoupQ-ãÆà€Ü kxДz´ðѯ—ÙŠüýÝ«äe²¯Ülv]vâýÄ:™ä·É`À¯¼…%6á-=zg8ù ä2|…üÊ­I¼€Â_ñ¸P•ÀþÉãÁäåÂ6UK“lå´5¨¸9 Þë°ÓT’¯"22 ziPCÝåù }>ž_½RÓ”ÓÌÔ!þÊÄA‘eY¹_xißÅÁïÁ³g±þÔ©Rœå¤S´¾“Ú·¯:H+U’úãŠ9@ Üˆ ·0_¹VƒÊë—Åïë· *å*ª‰¢ñ£¯S'G‹ª„Æ©šjP‹¹8@v2ëæþ»Þ^®hîO%£ªé½’®“MRÛöŸDrF6&¶lw‡bÍíòcÇ’“SªxV‹ÄÄÖ­9áàtC}KñçZÿ8¥µNƒèk]±ZŸÊjä€ x«‘™jQ7:våá\ö‡ëj¨³€&K·wÄ”{¬7ÖY^TKà ˜nî+ÞYZ“k¢æðûݻ˭bcBö%'£q@@é#fËM­F^-hh†òŠ£3¼}ÜÐC5g¸ZlV˽Pï5`²ZÅuÄ΢Ý'þ¢YRCæ€ÎÖ†n™)êZî•ɵæØ~HmŒÇΤ¤rËN¦ ïê'ÑØŸ€W 5ʳNª Fû@­üÊ9 Šð•óP-áFâÀ‰ãÀ‘C7R‹Ô¶\œ+ËéêçÊxPÎŒÁLmáôÆÃMkÊ›^ÎÛ»‡ .ªvý DøÊ³Rö[™ Ð$ßTN/^yñj *®T ïµâ´ZOíç€ht7®çÑÂܸ^OõÒPû{L¥ðjsÀŠB«[ÃM“¡>gËxnpµ·‡3O8Lâf5w-lËz3òó ðîä³hÈ]oÕMÄÕfÞQ¾™nâþ//ætñÒ`†V5k¸1:¶¶B¼u°ÓÕ&_‚Z._ÛªÅ%8¤Þªˆ¯›qâ% eþ­¨¦ËºofÉ•*›tqdeKZªõ© hÚš˜=7>Ò±#†5j{š=ô›û þ¯m[ŒiÞ¿Pû;ö½?íÛæÁ!¼Ut©uYÜQ3•Ç‚ÞBš5¨AåÀõÌj}‡]ÏŒPiW>¤ÛŒ@IDAT9  Œ-º¤‚  Ô¡•UáA¢IìU  ¹LñÉ£íjr¶ þ¾îU©¼â´FÊö™ =¼è}ÂIw©ÂÍÈÌHdž­ûð÷¾ôêß½c܈~/³QeI£¼'–?…¦AAнü^ž®&ß–ükàç‡)¾¾Ó¤ 6ŸN@®^'P¡>“eyõk¨ÿ‘žkœ]ÐXVÁÔ rà:åÀ¥Þx×i“T²U\"ëQ ® 5kå)+‹–oF|6‘[EØ ¼ûœg­YºÇ‰ÔhV×ò±ÔE yx×Fô½÷c¬:•hË#€éÄCIa6^yëc {á{¼9{ Vâa,ÕEK‘€ÄÕ GSñÀ@O .¥åHaƇyx`Dl#8ÒìA»5ôd±ß·Õa¥«}u‹@ 5H­¶®r@¼uµçÕv—ÏÚ ÂÀªÝ-Ÿ?jìÅ9ÀUï—¿®ÆIjhðÊÆ,øZ¿­¹©þͧ#ÿ @±F‡F‘ÁðÕdà '¡ŠF­$.%5+q%#­…Jz~¤®’õ×¥Ahƒúhàƒ€R–¨/¦±3iСg|ÿâ(ÄøÑ/µØ6\ ÞP.øòY¼Hk/•S½Wpd÷Û_%¨F2Õ¢T\’*à½${Ô›uŽÿnÖ®®sÍV\†‚e’•úY`„c`=ü0ý´ñÕÁÌc\“2‘“kBJJÒù]¨R[$ÛÙ«O"— ‚®D0Ã/È~~.ÈÏÍCrr&ÎåAü©€ÓüÌ4;›‹ì¬lz7`Î2HÐȣϥg"95‡ö±¼I³Ô{ñåªcÈå='w¸siºPcÂ9nKÉÌWltK•#‡lع`Ä€Ô<˜Ê4eh,An/3i.ÂÛ‹ÅW± 5yus€Ž1&†œËS÷ V7oÕò®)TÞkÊnµ²ÚʈS©ÝÕŸˆ‡MfRZÑSƒl^S5½µµË®]\>OåÉ_¥h%ª£V5ñÄ1Lÿl%nyh¢¶àŽ76 2ÖƒŽ?2‘jã‚O_¼!æSxöµyX‘í‰õëàéû#ÖÇ®XÖÌDÅú¼d¼ÿùBøhòq"Ý„ÇñÎÿ°*Ïîyih×ç&“Žõ;Âì›§ ÈßvcZ{ÃÇßi÷ᳯsѲ{'tªÇåbCº“ÍfÎ|óûfäFÚ`öÇq׸›Q|;’¸ìïpmEï&ð§ÖV ˆÆ>-› Ã¼ ¸mê'hUßZ;_¼þP(||ݵâ >ÿ¦®îvÈÒg២ƒw$¤â¸ùN$7C„+!oIdKzÀ„ê<Ü×Â[NÒ`kàÆ¸"ž[H{*¸²r§F¿Ù!šc\í×pá^5JŠZ¹Ê+ဠx¯„{jÞŒÓ~wÏ>Ìê˜öÔ0egbOB6ž~n,ÚF{##) ÷<4v6Ћ{2Ê 8Ô¹à½×îÃÒ͇cï¸ÖÑð¶#MÝâðŒýä2®¡¿š6ФvWC®C‡ß„~Ïž(€³³ ´<$Ʀj¨ˆÈåÓä&¿:õü5еÊ:ÏðÖyPPŠ©‘ã†%YZVƒÊ*s ¢ƒ¬*F¾prϪM%f-¢®(HCo —ª«¢òº¨Ý0Ìÿ×.˜ñácÝú£èÕ+mÚ„£Y3Ùì¦#øµUOÍåžÝ|íqcÔ$‡˜ôޏR *®g¨€÷zî=•öjæGô;»•)ˆãšŽðÕܵ8•ÕÄ’Záj*²âb4 òÂÖ­+±xñn¸8;ðÄo?üè‹z´yöõõPÀ¯N'Ï©ú¬VÌÓËIA¾r‚©˜¹h…ârй!óXW[nÈÆÝRï Ù­j£.›ܽ®Œöª†÷²yxÃeäÀ¦ŽmWÖ«ÔW-˜qûøŽh×>‚T–cΜuغå86o:†·f.E#º^kÓ: ]»EÇÀÛÛvvt)hKßÙÅUÝ( ØÚ iõZ¸É’eÍPªÆäʧVŽúcr]Éú+ŸýÆLIwÑ~+áöE-dæõrJ¢ xk¡ð¨$Õ >[ð¸U<Ù×¢Ù¨ARÔªk‚-ø‘Í@uᆮ¤ÍZµu¬•ßµÛ‘U HÀ;Ý»7ÀÌ™K°{÷ixÐ3…ÑÁ‹×œÄO¿ì€§‡=:wŠÂ!ÍФI""¼¨ùu Ý1!áugö Þ*Šö0Ò[‡\ ¨5óÚò­§ÅЉº¦£¶ÑXHŸÏt+G-¼¸Ð+¸Úx±Ý^²p¤wMÔ p@ÃCbNœHå£gFRÒi8pàÚM@j°lml¡)zžåÐ&13²¥;>pööÊs.ÏzÉ M­Ÿ$*à­Iî«u×2A¬¤;©³<Öôñ>¤MEmGµŒ…79Äâ«·ëÜåŠÝÚÖ4QîÒ>WŽz­:˜¼¶­­ë¾c‰ÐˆûÝ*T Fj¯€Þ÷ßÿ üŒz ½«ëáÐö#X¿h~^ø ¼=Тep4† n‚z¾Ì¯-²ûµ>ËÖï*R ‰-ï x+ÀVk?Wxù}úpÕ Ïÿй¯}‡^·Åá–‡†cÌc#q|ÿ ¬œ÷v¬ßƒÕo-ûï.EãÆA1¼ZsÓ[t´ŸâñA´­ø»fA£8Ì`ó•‰SZê9$§d#}wäðYìÙ›ˆÃGRrÓÆøü<=÷,ó=d#›ô°;è`Gí™#m™]<èÞŽö°s­šl™FˆFñfQú»ji¦uéþjh«…Àš(„& ½Â÷ñW]x£¬2Pe‚UÀ‰–ždõ”W½xäæä ù\2òNçBŸ«‡‰2ISÈ™® maoO¹õòDHH(¢¢ê#66–«81ðôô„ü|ýŠA°h«{"¯Žì5ñœ¨uÖRpÍ®ŒL²Qã‘·–Ò­’U½( fЛv£w!5%KY¾«Þò«£4vî<Ž'Ÿø 'vÅM7µa¡e¼3TG5ÕX†‚ˆ‚Nz}(÷Ù×cz5Ÿ—¤ôè‹E¿‡cöì5xó?±áM¸õ±[0äžÁhÓ±rL9Ø¿ñ öl܃ nÁ“SÀžÏs3ÔѦ]ú÷mLwoõhók§,ÇÚØPkU­¦:Ú (0òt»ŒŒ¬YsÛw$àð‚ÛcÉ8˜­enK —»—|Ã|Ô¢ÚDÁ7ÔnîÔ¹;ÃÙÃ.üvá·³›ìÈ+ƒd1] *j–²naY 儎ÿôF=r2s‘}.9éüdæ K®Ïe#•ÝÄM@ÒÑ$ìùs'f3[Yåpur¡8õ"81F‹æ-ЩS'E3lñÌÂÉŸLèd©è ‚†ˆ]}f®€×cÖ9³çࡇÀÏ¿LBïÞÍØÕ¿bq?šèò¡Ò”ôU|W½¨S}À•½`¯»´ÜÀµÏï{x÷½[q×]½YÕõðÓ´ØçZIîØ 11oQ3Û»WC Øü"é¬yôÙ`ÇŽxù¥EXòç4îÜ^‡¦­ÃÚPê–•…¤ø³XÿÛ&lXº'ÄÔ™Ð0Oôï×}ú6Bt”½©yæíx|(äarzó¹s™´ç<‹M›Žâ%û°OròhšáâDûcOG"²I=Ôoˆfõxú tö<œD4¶´ƒä"1ÛF»\~䟀¤-ßêpm•õ»vr€V|2å¯õÛú‹+ü§7Y´ÂF½‘¦:98±7Gxå±ÝÇpòH<ØIG~–v¶öôkƒ¸¸^èÚµ bÄðÔHe’*f—TÀ{9\»Îó¨€÷ŸF /åztÈ…³ÉKÔ¢ÞR9p…ÐaÍê}4è¼9sî½·Ë«ÝÞŠ,“ ™dˆ&W€qe‚-7pñüÍ4wøI©z ¸³?F=>‚K¤^0Sƒ*¥ÒZÓŽï>I›ßCظt ¶¬ø9ÙˆŠð¤Ç‡zÔÇp//Wª4àfEÑþ–¯>O[a!ƒ¦yÉÁƒ§1oþ6¬úk/víKfSlШ]C4ïÒL¸õgÂZଔn©ãüßóe«W*n4–`ÄÖoÇ6È3åáô‘$$IÄÉCñøwåvìçÄ>/+ ¢ S‡ÎyëH´mÛV1¨*ðUMn4iRÛsàƒøÒo@27­ÍŸ¤-|œT³ª¨:òUUK] ¸#w{j¢ðÚôÅøáӅؾjî|q<ÚôlEûW- 4…Ê0Õ$1Mê#nTwd¦eâß¿wbÝo±bóü´p'ÜíеK iªØÿ6hàoñø@ͯü–l•†@׌mÜ4÷Å—k±€#tܰݪ¹4Zölwo78::Æ“å—|«ÜÆ’uÖÖk1Òu-\I»/–÷bñey+é$ß+›GÉPÍ.U÷…÷,<ëD¯4)¶´Û­†¨˜HeEã¦{#›&»ÖîÅÚ_×áו¿â»yß¡[—n¸ï¾û¸‘µ;Ws*mê ÞÒüVÕuœËåš$?jP9 rà:á€(‡ó0ŠO>»ÃG´Â‹/.ÄËc§£û­Ý1îé1 „™–F‚NÑôÊ&//º7ë;:ýG÷AÒé$Þq ÛþÙŽõ¿oÂÂ?¾ƒ—»š6 B—.Ñ<¤1"èñAL%D«$¶ÆYY¹xûeÜ¿.~>¸óÕIhÑ­"†)|“ ÜM¬S>e-‡a#îÆÆ/ ¢™¶@ d¸˜¤®¬œrDgfçó*u²d1rKI/–`)³ø>mœ-¥œ/«øž”Äæ‰}¥YшŸ¿sý\]Ì*¢]á%-ßÌâ¶°ª6àì/ yf.F`Eõ+ñäuqüÅ©ÐЄF)ÃÒÍ\ÆY*¯.^FÅw*æ‹Ð æEf[«ìXJµòGm*KUÙçEÇ ™^|æzëŠÞÃz 1á v¬Ù‰ß¾ücnƒ[†ßÂgýEn‚ ©¸)La;¡R)ÕD7 vlßÁÓ‹~ǨѭéÏv•Ô¦¡Unîø ÀèрϰusH• R3¨¸Ê°å®þ|÷Ý&ŵM›HÖw!¨ºÊDÔ²âeXÕp÷w† oCžÌ[U¿¬‡“‡‚£B`G7JÄ 2´ZìdEóêBÍldt8Zön~ôüж7jíp€^–.Þ‰¯>_åË÷!ƒ›pÄ•˜ÔôÜó ñå×›1â±[1ùÃÑ’6Ä>î  ®ØÞ–Ûô¹HMÊ€ÎÙ‘;ý¢„0ˆæ¤¤!+Ç›7½éaïâ¨ÀQ%A‰?„<Ðggqse&Ëqb9E7 h ™‚|ƒNbl …†<¤'çžÇEŸ¯’@©ÐÄô©(°¡×àaÌ壳ÈÍ—h9܈T;þ3…@kü¡)ÈÇþ•kq4Ý!Aôr£pGÚs¾MÅÉ/¸¸Xš‹ÅK—ºwAEé/–Ç/ MxYÚŒMî•—Oâðò2±uÁZ˜ýü••òj./Ž~ÿüuî!Ap±c§IýoôÉô4ò÷!x‰ýP>P$¥ô q|ëvlZµùúBÚsξðv¥|+•^Œöò(*/Žù‹@y AaÂÒåʤìÀ²u8™jFPˆWqA2y2f¤`íp „3WY,¡tþâ —¸(ùœºº¹ A“(t»¥ "0÷ão±îŸõ4í''§K”b¹e¥¢Â„j‚ºË«ßÈ:ÁÇú5““e ¨j𕱥¬iÕúkd¸7Ò÷§ Þ˜97ÝÜ/qSÛ;÷½‹5¿®ÅíSÇ)&2d µêž,æŠ308¹9+v·mº´Bf^Žl?†ÿÄj‚›iÓ—‚Âàï‚C‡“ñÀ[÷aÌC·* ×À¿• bSœºw7Þ¼g6º¿ó†u dnyáн˜þ >{ämèCºaH‡T|·(}ô0|íF— „Næ<,ëCü´ #Þ~ÃzI9lYv2¾~ôÉ Å3ß<‰w&¶8ýïz¼ñô><½à>{hYžèá´0%ÁïýÇ=ЇnÆ¿ÿ»Ï/ý+o@jo=#1bêp´i€B+%„6åkfÿ€¤FcѱM°B£¦ÈE—YC>—«ÁÐ&ii£fØÒEñR<ë<_-<™V&+–â¬÷,éK×a‰+dἂÐ"õ¬[¦*,ÃLYT–ÿýÿ¬C¦{$Ú¶ "öí7ÿ)M$UL`¡ÍB«hVÉKSæ|wÿÛèñã§s9¯ÅgÙ2ï(T `ý$Z4¯ÖöÙr©áܘóæ:µo׌£øgkânn¬#Ûñæ#ËñÎß-À‰ŠÈDA­¹€IýÙc˜uÓKHjˆq÷tÃü~Fÿ—_EìÐ`ä³&¥jú#0D[´¾¤™<š¤?•öóÛÊ7Ëo¦·tÓØàÜÑ=Øø¯½niCÖ‰lsÕAá¥ô—´IlãMXýúÇØ6­ÛÇ×ùì3ÖÅ~ÏO<ŠOïš…»6~¸fäk]ümí \é`]-±ãÏAcûÃ+À O ~|ð*£»Uo¥Y]÷Šÿ½ž>ÇϤrõ9å$åi©{¬P[\Ctâ‹_|¨ÚÓ%“VË5¨¨@Ö©s ~YŠO?ùïÒüàiŽ£Ÿ~cû(öµÊr´ ÀEÁrÅËŠþ98: yÇ&hÖ±1†Ü=gO'cí ʆ·Öáè=6Ž ÄÄÁÛ ­%]ú[RûD„BWŠï/Aÿ®÷ðÔ^„† tÖ-܆Á_?ÛÔŸqüPkU:èÊ8º ¾^ƒ”L þúzúÅÝ;íâìÁ“ØEÍß÷sºáÑ»@Gàd:—†£{`(K.wÌŸØ}Éyk OMÆ©“xræð°1 q÷,þ|^¼u^üåethâSðØr;' yîQ}•ƒPò³Ò0 Gì€@ú>ÏcircpZb"ãÓaçî…°¨@>ßl!5ÉÉñ'‘”­³+B†Â•Ï¿!3Éé&x¸ÙâÔ¡DèœÖ( Ž6¹H8˜Ïúp•™Cvr ë0!(*98~à4òó5ð‰ A€Ÿ+QšgŽ'SknϺÒàâl‹åïÏAnëa¨ì?ä¦'ãÄá3ôŸ¬ƒƒPøz9äQ«ÏÁQºÌ+ÐÁÇ+î´Ï.³˜Íve™àìI°«GéÕyùÀÛÇ…€1ñ‡OñQg<ÿI+„»ë±îó%˜ó«-#áÄœÜìPŽ[Òck‡ÐFápsm¯ðrPhÀ±;q$Ñw~xº4ÏÃOó¤3Ѿ3è3Óp|_Œ&-<ÃÈÿ@OúÄM#Oõ<¨Å::8È8€Lƒ==†xQŠÌH;ÉôÎîð#Ly¹Øòí<,X熘¶õî£ô+y’K#îÔ@‡„pc7¤–¾¡íèö/áða¤qÅ£^‚½•*ìé‚Pá_äzöáñC” ½ |ë‡Âß—u•1ƒP*¯Äá…L0£É³úÁX·v]%r)äV*š¨ŽqÀ̧;!!'È,ò¥{‘šÆãv•éuc„ÚÜå€xM´³ÓÐ%/&ÞÓþf[r`ýIÉ´FIT+¯Õ0qÂdG¹rÓ‡þw_Ÿ±_Lý «~Zƒ‰/M@óNMaK—`mÜ…2%@ÖPfåð‡àÈ`ÜöØ(Œ|l8ýŠæ(¾r« v…]©í<ÂpóØ6xù½UØsd4ÚFTqKÛ–…Ëq.¨3‰Dò—Ô¿j逿 PÙö¿ß‘ìÛ÷=í_ú‡â‡¢i˜³×mÃÐc¸/V½>­»7DŸæ¾\6·…ÎQú—)9ýkyp…pA|ž:¡U\Sx0­mÏè~S;<ßÿ1Ìþà´þx¸B“h5ùéXòÚ{8;OMôÇ;“fáÈ1nüå³[¨sÄà§ÆÈ š¤V9óc×¢ŸññôÈ%ЦE5ZŒ…‡§Äáßï¿ÄçomD>±œæåÙO~;škñì”åðòsFÎñ³´ŸÖ£ÕøÛèƒ: Ÿ|Mf>‹±ƒb 5çãkj7íÁKŸwÁ×÷„Ǩ¬áØeëäŠQ3Àžîøé®ØO³ò]Ð<Ü ÖƒqçÏp…F²ÁÛ?ÃÉtz=áÄÀÎÇwÏz›:âçbÞw» £ßXwœàR~yz‹Å®ï¿Æ¼ýaxé¿ãá~n/¦ß:AÃÆ`Ê‹#`8ÌU‚ûÿD¯^XûÏYŒ¼7~üjâã5˜ý¦ÆŽ FAâ1|úÐLнÝ9ú³mvËp<öÚ-p°W™ÔØf'áÇ–áÿý:còGwà$rjM%RbÖÄÿ`Ï¡lö!ãì\0âÅÇÐ"ì8¦YŒÉOCã3O}¿ŒÅ»Ë†¿}&¾÷Ü&=€»no/5 y×vüüÙfÍuÃ'ÏDâ©·Z¦ÿ`ï!ž¸ÇžÔØ9bøôÉÖ¿>ÍcÌØ½äw¼¶ÿ/d¦×ža˜üÅĺX\‡iÄsã÷âÝû?Áøåp­›;ÆÍœŒ¸N!ÅraåaU¾ DN(«6”ßÊUÃ[.Õ¹46ô#ywÜ1‡ß™ˆ‹ÀQÑU½å$¤:ǵÁ5É´´³X¹ò/Ü~ûWxæ™~ô^žÆšl‡ZwMq@ œ‰^B1{Î ðùÆxææçqó7aÄý7Á/Ø…ŠRà…ÀW(­’üã¹R¢gã.ÄCeU¥’²rA–«[k°zÉN´z°+ôéñÇ×;÷øóä)kgX_ù^rNâ·o÷¡ñà‰è{kþ¢‡™‹÷¢Ù½˜…”jÑó‘1pNxOùm>LpPé–_ºÄši'j" ( ‡ Ña{…E£÷¨v˜õãIȶ^‹.dAzB ÒB ˆ_·W”_¢uX!¾›1'ÖQ#KÀk©™æi‡ñÕ´o¡i?ï¼ÙÛ¾‹Ï?ø»â<1gÚ/pí~?f¾ÓÛ–ãÑaáÇï»aLK3Žn<‚–3ŸÄÝ_µf[ŸÇG?¬Äí÷wBý:üùñ&ŒKÍt¶ü²¡>ƒ5ï|ƒåëóðò²×Ñ8°9Sæ¢ÃšÉ(0Ɖ øoîCLHR{ì€ÿƒwãî»cðÅObwšf,xÚ¼:ô)̾õ^oŒ¯_Y‰îï?‹{Æ6ÇÊ÷ßÁŒU ¥M\ÙJ?j¥÷¼¹ ggŒAî–]8±ç ’v!cêP$®ØÊŒÔn:Qó| Ú¦xK RÖáácÃÌÒ§/¼¿ Ö¿5 ïþ¸£&ß„h?E«nã‚ñO Å¿«~ÁøCûàd,ùº€}[ˆŸÍÁÊ-Z¼ðóëhmÆ·÷=ƒO^ÿ~ÞZýü»31.yX¿ôN󤿃ñyðp؇ջ¸·^¶<ªšТ:u®GUp<ùßaÈÚ¼œÇ\kqû§/¡s”_Ü7 ³__ý£iÁSÕÌ.ÿö:‹)=Ço?®GÄAŠiƒNgÀ¯oÌÁÚ=ÀôÅo ŠšñwGOÅì©óÐvÅSp±#’kTÀ{mø|Õ¢á&‰\LšôNœ0⣾AŸ>ý¹YA^€×Q3TRoÈŠabb fÍzn§þãtñè£ýø‚¯ë›´n˜.¾F ¡.‘Ú¦ñã» G¼ýöR|ñÁOXÿÇFÜñì8tÚ vö<¢YQ]úeg¿WB¸ÀHGÿXŒ‰/¾]‹ñtÅijw›C1q`3±¬½è;WG`u`Å<›ƒ‘]Ã`ʲA³õñÛ‡+pÛÄvð!aæjOýqçkã°aÐ0ÿç8ô ÕÑE›bTPeÒÅ"ÖÁÞ :Yé+¨=åf@Ñ z„„ÁÛÃ_?ù é€ÎwÞ†°hæ Njwy•žxi 6¸ã±~ðñpA·‰ãѰW:²â·#3Ãã^èË2áß³;Z6Z€#+¹1‹€×%ª!éÂ<Žh: -´+vÓÞɇwÇo.ÃîÄ1ðܳ³ÂqÿMXød'18¸|%ްæBš5d§Ar&5¥F'Ä=4;ÔçÁ𧹩Jgžxçâš‹ÝÇÏÑÄ;;ü‰íl•=v*é(«+²ëñP”¡máÊM]ÆFÄ+ÛÙK烼‘B›µFCí|Mz"<0[ƒZ;ÜX–Îщ^AÜAs§ð( ×¾ž.lg{hlàñ¼ ‹´˜ÔfºûÓÝ5ÿ~î4ÉH+š™phõQ´¿c<Úµ ‡=)ëyÿPÌëCMºË(Ä´ˆÀÖ¿Ž!N{ú¨¦èb—m+OÀÏcbШy0Ë‘ÖPooç7Wn`48ÂÝÓ®Û`ôã¶Øû÷2|»Ðˆ“çHœHʆ\-šôëªÔi‡ ‰ùûNs³c€rJ¥&7 {yº l°wñ_ØÏ¾€^ÃZÑhܹG\¼d¥j‚:|ñÅßX·î8>úx.ú÷ïOû'º´çêŽTÔäœõgŸÎIØ ¼ùæ ÒŒv½$G½5Õ'×o½F„…ys5šª¦ÜÔ¶¯Mx ½ÇõŨGoA½á0ÓŒÆ. |¯”fžªÖ~Ô`Ìó_lܲ{çý‹ð®Ð(–v—%`€•©O1#0žã&ºUÜT,ytS«1›rî4þþ+·u·¬€æܵ/î~`>~é+>Þ â>¬Ò„[j¶É˜ÉC;@ëO{`þ. hžàײžùâQüòùJü;ï'ÌŸ:_šŒûž¥r 44sÓÕÏ,AœÄÑ¿1Íä仄ŒÝ°Ñ:ÃÉÑFyª Qa- ½’fŽæü-ZõBÛ,`Ëh0#ºg[x:ÿu?ïDàÆu¨?´bxp´S›…c»ŽÁldÝ¡QèÚ:v…¢[·ãæCš²܈%ü°:kCçÜp•ŸCEùZ´Adž inšG{U{Á  (ôàU ´&%¹!ª½?v,ÞÍÁD´™x2~üÿò÷©gÑüáæ°7ý£Ô)<”úsÁ¢ÍJÄ´Ui§Ü»Õ¥hÚ-SžNæääÈ,B K ¿Ì&J’£Z· ç$i+Ö˜2àÙ +ÆÂ¶b³îí›Û ÐÍÚCE¥ YüˆM÷Î߯Ë÷®@ì ¶ˆæ~ éKGEYy¦sœ"G|œhÓ$èZÚø" Gwg_p\T,º…І]ÒŠ¬ç°T~•þªÞ«ÄØë³XÚÚäæá—_6¡E‹v8`ˆ²iM„^ *j’òލO<ñúöùK–ì)¼5I•Z÷õËËD©ÿ€fhÓ¶>ÿl5^‚Í˶àö§Ç¢7]“¹º¸p‡¼´ð꽩ÃEXÛ–hÖÉsŸþ…§21øƒ¾´%ÛVÙ„¥ ÅZA¾øó%¡YBÆ‘]X>ÿnz} v†± €›‘òñå­¯bå7àÿÙ» À¬ª/þÛ·îî1ÆFltw7H* i!¢ðWA %E@Q¥DBºCº» ÖÝùÿ÷ö±oc„ˆ:ô»Ê¾÷Ý{î¹ï÷{çžx©n[j)‰ÎÎÍÄîÍïcÖØ›H² Ôê yþ1… 2;‹~ñYl#=—¯ÄÚõ!è8«Ìy™p0‹¤Ñ8÷þ-:.Á#æN@jr( ‹­?nEÏ!5AE©¡M>Ÿc£Dœ?rMüœétv³ÇîEÍWJåEàü¡û¨ÖÊŸ×G)±Ž­ÿØ$'BqˆSØ¡¬ÎIé3u  -¨¶/Y†œ{±h:µì¸$iÌñ[9aðÁ«A!·.!,Åž.ÆŠ©† p¥1àä-C³NjFt¶s ¬…‘3ûÂÔ ×.E*C~‡îCê0Ü»2ö={#rÐ&WñªEX—ch‡ª•KbÚôÅ€±#^š^Š9\1ëÇ%tFs@º´ÓÝÏz¹/VáIy6CÈÑsœ*Ÿò†YØý'úv©§iC ©…u‚O^Aljc¸ÓÄ!:øàâSá 6U8~½¢AÓ¯z£¬Ÿ)fþ¸7©Án9¹Ìx§ÐG=·2“¾Ìœ hÒÓpåè%˜TnŠñsÂ2ë.¾=» g£8òJCE…Þ@25èö©\K…»}/¨ó'YŒ•'Hvž1”ÑFlh‹|ûÚyDe™ÁÍs¡ÜIÚ~ÿÚ_=àýkùû‚µn€ððDÆ÷ŒcŠÍ6°¤‡e 5ú¢ç@Qàðõ-‰~%±o_0l\ÈÒÓðBs ƒ)ˆ-1lx[4j\Ÿ}¶3OÇáMGÐcÄ«¬T9=@)Zxñ<L £‰+:0YƇ﭅M¥æhÖЋp774µ}±×nâ—Ñ?ÁÒˆ4‚OCo˜žÞ2h×½>4dÖ1&p Œ˜xׂëQ±–®„™MŸ©s ¼ñi'¼×ûgÐy?)åEìu›]¢-­´–zï ~»H±± ½xû·\GÅ>½Ð¹CYÖÐYY!ªÉ¡Ö4‡ØcG1±ÿz´Úö ÷7.ñÓP$œeÓ‹?5[bý§?Â&¡>®mÞ‚×<Ñ÷»¨Úx-V|<Faõsä.'Z`XŸ 0ˆÞDð¥Q4–¢½”èYYìWÑ`NÕ~½>~n=ðD톾´C5FÛ.õq¬ÿrLe‡ þ&Ø2uŒ*7Âçóû2|–ª•áhŒ`éj€¿¬B)ŽèÔ³6¾³S'ÅíR±zò*”èÓ~XU<aÁØ9oëO{Ù_‘m&÷DþûB"j¶(´ñÛ`T¯.<ÝìàJ§Á˜ñ;áÖ¶J0RÄý\-¨|ÀXÛØ#üð!,šïjÞ´Å%P—±‰V]¨Ì&­½²Ÿ‘Á:¼”þeŸ6ÛõtÄ/í–àÇf(]<›¾ÚÊtF󤙉™Uü °;ÂUk¸ÁΑ‘+"p*¶jÖñâ=”7Ù²f´ŠÛ»â×E%al㌄‹'°töþžÅ.†QËÖ\ÂÎݧÁµã1kâbxßÅʉèñc9X˜ò®ä}‘aj‡¯ÔʼnwWcÊ8 ”-¦ÁÆ)tlÔcf¾ ~Œè(ÉÙó_Wô‰'þ:ÞÙ–xBCÀ›€Ÿ~Ú‡*Uê2m_c~ÝÙaè ûq@VY¼!׬YN[Âd¼újõÿt‡«O<¡Ë?·-¯÷l†YrB‡áán‡M+aýÂH£vˇᯬÌ,–*ü¹þò_-/{'†Õ23¶Bã7Z¢ŒŸ½À%äWž±…#ˆ VU‰3sØØ:¢v—:¨\½kŠi€,ÎgÃÖ×AîKÒ&Ô>ÕJÃÁ†v»<ç\ÖîNæ(^¡,*Ö.ScÈ+ÀJâ³2Ô—_Íòðñ°P·„Ç2` ÁTö%ðÒÿº¡ûÛM`Ë¥l1EÐj 4Æp/G»ÐfÂÊ 7ã:ÁnÉM™é®5œ¬$毎ÉÐAuag’Œó®À¦tE¼=¹;JwA`Ý jcqnßE¤Y{£çço¢~eWÚ°¦3ª…+Ê×ðƒ¹|€P³kfï‚ j%`FóK†ýrbx¬ íš¡FMåÀ%¨4+¸ãúÑó¸v9A/µ@¯¡I‹éYFð©]†sÍPa¤Ý™&±7"ióì„ï¶GiFn¸tà¢FïèÞ¿ììP¦¾b.ßÀ«±¨Ôµ ªÕ@™zäµÉŽˆ¶Õœ HÌi—[vÌeJ»ÀÒÑ‚ =lPçåºðSÌ-D3íruÊÀ«˜ Œ’£ÍœGeªûÃÅÝÕK*vÅ ÖÙÔÚA5JÂÂL¬¤¥È¼åÀȆ| `86îj,>¬4JW*ƒRåìpïl0®Òá¼R¯—Ñw@CØÐFYønïj— ´­nâÇ„$V´6…OÃú¨Û WòæTà¶#C˜eF'!&ÎMÞj M ‚O݃C`%ôûä%ØåÄ!1Ë%*¡êKÕù‘ÂxÕ“ШwtîVFÙiÐ˜Û €c ¨Y¥qíðyܸŠZ¢×öp°x¼ÏVä¾MMNÅ–…Ûà`倞½z>±!šÄˆÒY_þKX0µcðÛª~Ôä–çй^¬C‡3'ý—èÖmSöWLrOêôøG9 €7•å]º4…»[Ö­øÒóÏvnŒ}{/2ÃÐ÷˜üeg¼M BKû–¤Eï* q•±RÇ1aÅÊßN¿ ƒûÒ¡½*ÃÈÔH5·|ožÏ ÅR»2ž_ߥÚIÊË]Š€)U7©îÓè Úvª×h5«RMRLPKÊs¯“sb«)=ÈyçþUûTÏK|bݵkÈ3ùh+Å1MÚ`¡ŽJµîÔÖ•_©/ã•öä Ù–¥mõ¸¶>m?yVŽK»BŸJ´ W©=jA J¿vj¯–bµÅjWiMå¦zµô«~dhÛP¯òVƒzan=¹JÚÞhÇ*Wæ•êy©­å¯Ð,ÚûôÁ3h£Jït>O>¨`AÕÒæoWÎÈqõ?u[=¦Bš‡k«PC jtÏ‹=¯œÍ’ÕÚ6¨PPÛ¯öWM°¡žW¯Éû«R®ö©ö!Û\忇û“ú*°¥nKkê¶ –…"Õ­MΨôåõ.mKÚ¢¥_Ž«ãUkiiÑŽOêå]-ýʾú_v.mêž:µ®:õ¸Ð¯pZ*ò~UОGM}RG»'­jG–×{ÞUr¶ð>´W©=ÊiG¦å¿z­Ð˜œºuÕ¹Õ^«¶¥ý«íAèþ¨t¨|Tûë¤á—zDÛ§vÆuû’Ò–¶Ž.]Ú>ÿê_=àý«9¬oÿ¹s@4}R´¿êÞ?ÿ÷qô<îÜ?O¹ž=ôÈÏ,%„ÙKí«`#½ú¿Y{–ng¦¶O°uÙv¤%3¯¿,ÿeú==ô(²ÐÞ";5/ a¢‘62¢Ý™±Ä¼äÒM.8}£Qâ®2“Ní¹Ä«6žÃÙb½_Jã'¦ ]…Œ7‡á‚Òi ø-УÏ+=n(3b Á»}ZãåݱîØ-jóèà&¡jÙדóJ¹LdVz,OèƒïfÍÅýèH¤%…aïþƒ‹Nf=µmƪyЮN[JÒ†NÑíKÚ×íW§ Ó*u”qPh1¬ÊɃ+ðãOK+J^¾„¶Ð[0wî \‡‘y±ç£C§ã‡Ž³MS£$,ߎˆ|_xz8‡×Í@‡.Í1dÄ`Lž4opN>üäkĤI¸r7L [¾Ã[ÌÔëq-šB®@»B˾ʸ¹_°)uLóIêJyÀGu—<àGAbæ~÷%.†%>Ôgn5ýžE„ÊÖt‰6¬¦|ÛQü°¿x4˜ŽUÿÔ_.ÕÝjóàI˜9‰Q%bïbz›8z.NqzzæñLOÀ®Ÿ`ýÆ›¼V+°¥í?ÝÖ´Çä÷銄KŽ Ãî%›qèÈMb&郮Q 7wý 6N[ŠFÌÁo?ïCDL†œaø1Á”Y½Û6œd qc{ú¢a eÍc•-ºãж¬=V°íqm=íyõ¸ƒ¯/^ЉÝg"&[uÓÖÈÿ[ðzÊr†Q‹9uZ1Ljñ€çÚ>ó· îiÏ=Ü^aµóç¶lìœ0 £?Ù¢„|S[‘ ÞÑŒ¡üÍÛ±zó]&™û;¯áiüí«Ø²h NŸ ãÙêþÏ?¢§ÙÓÞÕOSW_Gχ8 À%9ô$#ô~(’™•ÇÂÆÎŽ¶ü‚dH¾O’ãc#ÒLÉâv ¡âêÂ08¦ˆ»›·"øÒç˜4ìX›bÍÚV0·´f ~pf¥ :2Š™‡Òaëà Kãl$1·-óÝçd¤".1…ñíœ]}pSØW*{²¥ö9;%ñé™°`ÎøÐІnñ†3êd¤§ ""IÌVdmë 'j?é¥-\ÈNEèÝ»HHe '›˜3=$ƒtçÉ…¼ÒšpÛ† /a÷o!:&ÖNžpq´BrBROÑšù©¼PŠSOˆc&¦´È}D¥Sò:#6ñ©“ZõÁ_ùÊΈ½„)>C<Šá³)3PÙSƒiâçÅ3Ð¥×khSÙ-¿&œý¦F1åèÜO¥Øº³«vžA¹5˜k^Ût&’âbÃ08öpaºË¤Äd˜ZÙÁœÅUÓò)< )é ‹dçG+å5œ•™Šx¦‚²&c£ÃG•³•Ù† ?"n^ڇų¾…cíWàç a’„Oú¢ç@Qæ€Ã‰|ÓzéŸ5‰[ˆ0"}9”7²â¦Q²É©’$‡Äb¾|FüèaÃE;&œ`|]VÑPã«‘ç’G®WšæRUµ|ºç¨òSbä²¢N‘|ƾr³†NÇ–Á¨öÁ{¨Z+@¼L÷;ý•18fà…ºU±iÌT>ú:&Lïƒ¨Ûøeä,,žwµê¢J«Z°4÷§¼ÂÖ ©Ä-¦R$Û£“,0sC¯é%@•IJ%›²$M.µ†Æ„Ìr#ñŽ5ÜfÄ2µˆšïIÂ!õŒL(p…R¨±Î¤ZWÃÊɱá¸})F‘Ïà>²™9΀sf ¼‹ÈO’•M:2¹BzûÜm$3{œÞl&£È””Â"§™ÎÀ(w®¨íVrY3‹Û*ÿ ïMê)™äØŽFh¦Ü•’Ãy“¡¸UªŒj©|÷ðz‰÷+óM-»d£»{å£R¥ÒÌhŒ³lȱß8°3‡-Äa&i>f†âµ‚•Bß eí™á°˜ú¶p³$ÑÝåàĪ­¸a‚všÂ‚(+- «§l…O£òLk¢È MÎïÚ‹ÝkŽ#,"Ë£ÍëMàílŽ#+6!,‡q‰ÃoãÔñ»p-_-Þl†b¶Ù¸Âx‚i­ˆ"x200wioÜ8º©Åª¢sÖ#@Ru‰98»m'v]¶Âg'G¡©¿#Ž.™‹aý~Åéá­Q"ô3zY¢÷ :Ø~†‚!°ér>“ ‹¤ÄtÒô‰Žõþ±ÃX¼z'ÞŸ7A%lzU`&œH 9uk‚áìl€“[ÎÁȽ8u­Š[›÷âôépøÖ¯ö¯7„^ì½Ø6{ Î]ˆ„•‡êtiˆêµý ÉHfºMؽî¬üàÀwš˜„ä§DK-™’‹Í“×îy=Ô®áKÝh6î_=ƒmËn¡|Ub’"¦(IQصx޼‚´sTj[MÚW'-i8±aîfÚ#ëöeœ>zÎ+ ÓÛ-àl+¡Õò Ž–xí~[yu_m€b–ì16¬ÁÙxofŒ3ƒeŽ™@ZD1¡Äš·áN¬!*Ô)†4Σ±‘ÒSC±bÂ6önƒJ%íqò§phÔÕâvÀÀ¼`ùû¾\X@IDAT/j{ÚÛ²¨Ñ¥§çá€Æ Gö‚•W-tjÝŒ€+cü'©`“â ÐøõÏ1kÚw¨êc‚åßþ3ÖFíÎ1†_†jaÍOáË•i3f€ÈÐëX»`:î˜TÅÇÃÇ S­R8¸æK,=†v­{¡$sSú×hƒ¯v€³q<.]½Œ~ ß<°_L›cß&øhø4òÍÀ¼ŸÃõ›·G±Œ”ܼvIÉÔzPðK?6ì"n…„*"‰ÚãƒûöaAvÇÞ½ Áœñocîï!höòŒ5MJÙâ§Iý±êp¢.­ÇÈIßúT# =][ø3=æšmð ºÐ"ÇÃqöJ,z ޼ MÄï;rŠ•Á½K»±iÏd°š!ÑÞ‰ƒËpüf*êUò…(ž¦0><ÌœñÑ'ß¡WÛZ ÈMÙJmúžÕëjá_ׇš1¤jaßæÉòGïW» KƒÚ¹° ûO„˜¦Ù)Q˜=j0Ü5ÅËýG` ÞÉ?aïH¤—:ߺ˜6¶VœOCç¾#0æÓQ¨î‘…¯Ç àP,²™ôê¹MX¼ù z¼5 ¾Ó '0ãûÙHµóC«Fõ¸hÖÝû£MµŠ‚¯Õ/¥‡(ÕÐs@Ï•›6[F}Yßý[og„Ú‹ ;ï*ZÔ'q‰éôX2ñg\»•ÊçÏQ¢?LZ‡È8Ð<‹‰šëØ«§1åõ)¸x? îÅlpdÖl|1z+uÀƸµf-¾îó%‚=3x­;óggÚÜÌys>}e2&öüz|ñÝ'`|︓Àv¯`ÄÔ×áÅdÌZ¬êv‡Ëæ5*£œŸ R¸ïW»<<’qùL(ÊÔÇèE#P®¸­¢u-8>QÀÞ:t‹Æ-Æ’/—á?O^†K7 `}S>œ‹‹7¹jö@-Ëä±!˜=ì;Ìþ~/Ìl-q`ÁR n2‡NÄS‰‘ˆŸ?þ ¿‚1WÁ~êñ9–®»—b®H½~ã_„ ·“pg÷vL0ñÌ&fu+A\o¡ˆ—ÚX.ÅÝܸ ¿ßÃäªYÁ±…Ë°ä—‹ÔøòÀ ©y?ùýB|5rr˜áÌÖ0?¼ù9~Ûx›ÊàâîßñEŸ¯°÷x,ùl÷-–¯8Oh[¸þÒØ8›'-ÅÎ7ĹЖ_‡ÌÆÙ á8³r~^x™qã›_aÃîp¸º˜c¸r: ÆtÉLŠÆå{p74‘³c€F_ Âðñ­`A±hƒ_¤R8‡^¤èiýG9`@M@B\ܼàʼØZA¦%JÉÔÆ‡ß0å6V­ÞÃÌ3㳡àÀ;¯NùÒ8zöv,^ؾµS³€˜0a*ÝÞ¦X˯ÿˆHÔ~«=J³e”¯‹ömãÆ^:Yñ7aïS'v!2Ó?›‚.,‘N uåplÏõ#1 FTÀ(eùƒb@õ¦Q®¦ÔˆZ#k `œÍa}Ê!êæ1 X¹Õ;Ž~}Û2ŸzÞ8;Žö`²ƒpóÙˆ«â˜<ìs´¯á‚œºuiÃ| 3vê¨bô$"LðÆG_âžÕùÕ ÄÚ…+¶ Ác(zTw£3Ìoôj[x˜„`õÊõ(Ójj—t¿€?Th ¡ÚgÄ`ÕÜÑóó6´~í[Ô pÊŸ5Ÿº)§°táVTm7åé}žÓ½/\m( ™²t^¢YLŽK†½­…×diÜÜÞ&ÖHŠIf:\o§Æó‚Pô®hnå¥F](òÞm¶Ç¶I“1mÒ6|5ýe˜IüX‚OÒ•cë^>Bz¾ø)ù&V5Á Å£P"û®û„Ëú‰·¸ˆÍ{1lçgèØ âbÏbx­·±oÛi8_= ãR-1zÁP®Ÿ%Á"&ëIbè £çhlÑdXlxu?®Nzö‘Øùó)´>îvÉŠ‚&‡6z5jcð÷ÕQºf˜Ð×áÚ[¸°ï6Оãä»Ì¾FŒû™Òˆ…iò]œ?^¨ì”HÄ–Å«ð銎àÕ¾Õth?Ž$¸`h爙³¦ÆÔîžÃù«‘èµêô­[7N®Å»´'NOç|8”Æ®þ™q^$ý°½»-g$‚ÊöOZëoa÷¥þØÓq @j(Ý’ÓXé]›¯P›ª¡ê.#1×’ ÑÜ?öµüȤÒŒ vÎÖÀýD(gQMéêÃT˜ÄIt…©¥ ìø k@Md&C‰­SVFÓ}Rû˜û¬0õf5‘f¥ànÉ¥-‚k gøÒ6¡…‹…>2³¹öoÁôþ>àêâ˜"ñÁpØžyx»×zå«6›Ns©ÆðŒ½‚›†¡°uö€·“#Í»¹dçìÍHÙÑù†ž·£ˆe•ôâòÇÀñ93M%3EÒ‰Í/÷} óúmç& ¥f6_3ňÛÀ–õ¤þ)ÂëôØ[ønÌ{˜ùÛïhÓoF é§hӝܯŒùÒÚ·jŽD¤¡fÚUÌŸûMQR™Î2ÛW,Ç¥wÞ„'5ãa°BY/1ïC*ÙéêV VŒJ!Vfq‘·p—íe­ÿ ¯íÿ‰&`ԤС¼K‰¸TÅYÄþÞn 94r˜¥ÊÇŠ& Bç/-3?œÛz6gò­ª/zè9ðDäȲŠU)ÎÇ2YFnô·ÁuO,ÔÑÀ´íàDá tkã€ý‡n¡ìë#àjÅÇ\i‚@0=ç·ïǯ³ÖÂÊÆñ!W``×€•O,5˜N~”™À9°³6Dˆ€G*2 ˜³ÅT+_ùœð _°‘<ëüyP¤Š˜5˜Òÿ!+—vÙOOaä bj;˜êŽJÛ¬ÚˆhHšdý:ÿHn¼Í+­Sè 35†¶µè1µÁ®Úž´#f)Vqnn¢Y¦Ÿ}EìÜJP£šÅå|šèÒ„KC-I&#`˜–õ†/S§€¶¦ÖÔ@› ònlùþq«YšR2gÌP:È»NÄ©–ù‰U“7AÉêµ(öM·àèu s<гy%h"w)rÑ€fk)q·±í§ƒøyj6?Œpán,ê2=³Ѫz—ñ¥Í`¯90µ³T” ÊÉBÿ£FÏ&XüòzÜ {ï‡Kñ@T­@{iΗ”,.Ék\P’÷Qÿs(éoú|Ðô„~+nŽìIM!ó!k–/bÑÞqÖŠ Í”^Ùkø—ôÀ¼upüÒm”¬éC¡§hHƒÌ˜+g±ëì=T*eEG6¨ŒËëm«\OÉhÀ8µ|š(êX(@´a»Ä©à‰‘r£ÔM§¾IéàA„•EË)¥¤ºR”‡Ù=ûñ*ËzÆ0¤À«Ú°z´¨OaÀñ¡7¡S•[±œ^°¡g2 %ò_l,‹Z%ô¯¸HÇý:Z Ê4’™ƒTÒ\§Vgy~UË6ÀÀb#œ¼Ѱz9òâá–äˆvå‰ Ê‰c`îÚ“èþáO3èUXÊqÝñJñ÷±ló6Z{ þæq¬¼~Œ¼cÚQ{æXރ߶ÄûÕE<¨Úá ƒKðíAá“hÉy¾A›>h[½,§Ž&#ä1=‹•Eê…¼€¡†ØeÈ| !B€¶pòtUïÚÃú_=ô(„|òøødóN‹}+ågfîFRF•Ð>Wòt>®£ÒË-°¸ë.ü¾Ì¡wlзSʰ$6—CÇÞ,ݰ k7&`܆ècƒ]ß~ƒ9»®IÛ|Že)I)ªÌ4 Ò#Ž©+MC°ˆÂܳòcêæ‰w¦€’v<*¼8± *™céh‰ˆà{ˆ£ž%í_#ïF )<^~”é2diPþÈuÜ—oiÃì >5ª¢å²r‚µEÌ‚7¬Ã‚ñxcÉXÔ«êÊúZºYKÚ¤Ì×jœ•‘p\2B¡Sqjã¦øM¤Ç¥ …¸pðÎ pé† =FÍ ,i1‰Ê‡‚)CB††'Ò†Y(<\Ä~ÖÒÝU«àÐòØd{^e* lqŠdÊFöeŠùW Ú¡F-j{„bÔKE’ª´ (S:ùWKçý©G¤¦O•*(±ÛÞ{çï¡\×Þ4 ”±k¹Æ”ì4*®(ß] OÐȱs,Ò•Y´fQæNx#ΉjÑ:)Ê„¨åž(’?zÀ[$§åÅ!*+SƒÆ=ûÂeõÛøz§ðýz*Ê{SkËv _깻阴d ª9šàÀ‘½¸Ö >DÞ¼†+!°,çsùò–ç¥à3#‚Gi­srœ\í=“²'èpTÏ 1·Ï`ß•û\b+£\)Â1‡À25•kæyI÷ÎàÀM>ÌÅ•ÓêyˆÙÒ0wóGsc„%Ù¢V£¦p0× !4K×n‚}‰Êð)îäûq.ø:j÷CrÄœ?vœf: êl*šÎP?sí«¹BèÇoÝE𱬍E°ó)‹¶êã»5Ÿá–I*vœŠ CÆHÔiC»I2ÅÃ:“ÚXâK%æ£B7¦fÓ~ö‡ßÎà£/—â½—k¸§"™c244U´Ò ýè@²{£É€¥˜þ¿Và”|n†É×ÐçåæXÿË4ôªß¾&I8qñ4ÓªÑÁ% ÇîEL:5¾v®¥áG‡Žˆ G4lÞ‚sh@Þ_ÁR:}x”¦éF.Oµ¤Ë;@á²r\ŽJ¾úHÄ0êƒD±P¥©×=ô(Œ" ÌmhJ…¿lD»vÅtl‡£RMUï¦|S>š·¢ jU°s&®„gÇ—¹ŠcIðCóÊŠj2éï`æã‰â¥\wå6¬:ƒtÓšˆZ‘ÉZ­›c#˜:ºãÕÙCÂ¥$ J×ò¬SF;YçÐ9ë£Ú0Ã&ÉŠßù`FÄq†õjpùt W™ãíöŰaÚJDØTBÅ {ÄÞ½‡ðXF ¸ƒÔØTÜ:}I\úò(áÊ•5Z:9£¤“ËC¡8Þ_Ùµ(wìæc i%‡§) qñrE lëí‡bq³±šáÒ|>hˆ›»vÒÎ7oMðƒéaÆŸ³ ÏÔFiÓ{رù,’ += ï’Ælê’-Фo¬é6+Ž;Í| öŒàKZ¾Róœ‘i ײ>ðr5ÃÞyûpõf8‚b BEs!ü¾æàÙÚAhêüŠNÛÜÙuš•Áì‰ó¡±wG¯W({å†0šý ML£±nέ‰½¿ìÀ뱌R!ï¡P¬üzJwkCÇ?sܾ|[ÑšÇñÀäö]_¼3G¸¸Iô ÒW„‹ðáÉyH“/D¿ª¯`È{»1ö‡eèûêyÚ u†³e v-[„ã÷¢Ðñ?¢}ýÊp¦þšqs1z‚:Ö/ƒ=+§âB¢->}½ =E3 Þ( äá}0p>’<®Í¦–m¬hE•Ó¬gd”A © *×h ?³ñͨ¾Hëý Žl_Š`‚ECjÄ™ËÑÝÆYiXøÃhÂkbß/óq>,6¾"ùÈSóšAa“I )rÄĺz¾Ý ƒ§Ç`ûL´«Q»7ÌÆòm—0ô§fèÓ¼+¼§üÂ8·Ã¨Ž¸³1VŸŒ‰ wAuŠˆ&#ÃÅSß…‹é88ÄÆ/k¢BãÁò1#¡c—®˜½ìuÜOrÆØWZ‚«…Š8ÒmJ4Ù#DÐO›0 –Œ»K²Isì¼[áÖX¼a=­K ìò6L·VÑ<دÓeZV(®˜ŽðK~ëÊyˆÖ gçf°ed –.RÚÔ¥ºÖ«‡Á +óöûèÒª,¾™÷)&ئÀ%õ*f¯ØÅ öôôåÄÛzT@Ï^Mðù¼!f‡†e°þW†:ž‚¯Ê·E_HFF|½Ê[”Ex›E‚µ3l 1£@MÂÒ9ßÀºw4¬¨:®)µõôÐsàaðy2qA÷OÚcò¨uÙæ#j5Œ‚àÅ$3420†Cqw˜[i5p· O ©'ê7®€ëË. E§ª°àò(/ì¼Üaåh2ÞµPlÎŒiG9C‡® vMaL›Õyã—3„`1¸ZXS\Èsm w†d(B‰›ëèí!b$?°THàû`ô|FÚ‰Ez>|k4B gÆ/åüz”n€7»½‚âØå¨_5ÄÇÛ*õú¿zü·9 š¹²ºà«JÕr7޾ž°alÚt~@šÚáÍ%£`A»Ô4jc•Å%]vñ9”ø¯i aÖxè[(÷fœ…±£=ã—Ä„M%—<•¶®ðñ¶Â½«!Hј+1Ðsh¶Dk[Å@ éØAh`(ªél'Wèö©l£ÙWýQ3)‹«MÆ S©h0elól®,µœø!Ê÷½ðˆ8Ó×ÁÃÍ–­e ò¯ xßTÅÁL®å„¡…9l((žZ…¥÷,XúVÀØKÁÞÔ¶¹™¨6à5”Í’ô)0sÆ€¯þC[º¢Ñiú×Цî°›ð˜o 'ëûaäopÌœáîf‰ÄÆtÏ }-å®Væ>`‡"À62sÅ;+>GR–)ÜèÁ-€Ü!¨">\Z V(ñå öaLÞ4#ÊIÆ…7KÅõKá°örƒi•Þ¨&aáÈt¾ÁÓߢ¶9ý=8ßÂþ\9ÿ O04¥#\õœÓr0q²§#¶Ì¸! Œª´s–€Ã­& BÙד$Pt¤I†¡=4–ødÓgLÄéŒL³lôel÷T‰ÌXÂTIse VNƒ|bíBG;Ý×sÑ&VOÝóáÀ‚ù 0pàF臦M˳Q±„’bˆ`.Õ«÷%ºu€Ñ£Ç3Q„zæ‰ù™Qæ¥Ðy-)Yìt øul£Äf¥Ÿ™¨h®¥|9&Ä'Ð)€q#M-èð`¢>\Ù´ƒÍ¦mšÖ’=òÖÌ ÔÐVT^g 2¤Í¬‘L>hDj&âazå–ÐÑʱltj“¬Hü¯)¿ˆíëcí¯ËAÄ‘±ñqÔÀÚŽ $ø¦qyHú i&m¢ŒDˆç …Vv§h?Í,˜¤ÂX±OA+‘$Žp*Ÿq +[†haâ ¦…4’á™Åö3˜ÆŠÚ‚„XÆàÏWk;P'Ø¥Í_?8²f*zR{1`Ê& îQ½ps6œM;†,Ýe7¥/.Ë)Q'ÄÞ–èµ`!o5 ˜œ—j”‚’ÙˆK†|ù”b&—®L|&v,ýgâ½Ð½{Gø¸bÇìÑxsäwµì*úÕ/æšPxG^HÏ&¦VämÞ8犭çȶÏÒ¦±u–{Èv2™¬˜D§5S3KÚH?þû[æ&•ˆ¼K—¦|±Ä1bÆ@µÑÿä_cìÛ{mÚ|É_vÆÛo7!ø…¢/Œ±kÏe´m; ïÄuéבOðÓ ÊçGžd¸’X³ê$\”¬ àäS{±lÁi—ùŠ<“¶åjàåÞ4Qâã& @ N’"- ݩھè;i⻯¬2±žÚ— 9i•o_i¨?R|¾"&*t49/ý Ëk»à5”,ÎçkìïHˆ·¼ñç‡ÄÃ=­Ê ±ìkQ óœZä¨yI]|&„Ï›g¯Æ™³QŠ|Õ%‹’Ú£Y]ÖxÞXdO;~©§òƒ2Rá”SçZ†U.©HïiÑ÷°žæ)!Œ‘+jóºÕØã¥áàãh®\©Ò©^[pÌâ•!4içD¿ÜZ^¨Ûû.©úö¼nÿê-áVLD >lú1ü\KbóÖÍOìòño˜'^®¯ ç@.øü0-7•)llEoªàU%ŠAn Á¨ @2cv4Å'Ÿ×¤ë¼£ ”å^§\C„cb¢¬7) Ì˜`RŠúË Ý¦*27JÁ†5Œ'»x-vmªHÝ 8x_ƒ×^ë * ˜%†@‹O¨•5wäzJ% {nÌuiK¼bM$¦ *Ô:¹´Zæ^#ÒM@œ¶/´Ú*ãëÄ$ÀÄÄ8_ÚºòÖá)åãÁ”c'¶Sê‰pŒ¾´ã¿ù»÷ì…[¹WñJÛÊÆ-ìÇP€âƒ†óoZ~å?Ã=žãÿ¹ECÏè¼9ÒÕþÊLJ±™),hÜ8‚™ ¾Æ¡Ckàb–“Çö X½>hY‰'r5ÃbÆaÅLqJáµ>’øÕ s¤ËW#α9&¸ÝmI‹ìëÖS*éÿè9 çÀ#9 @Uþ+X 3«¤W)O݇^­Æ‡ÌÜ…™ùÌ ˜)øy¬Û^ÁÖ†VT˜WØ™üÇT`žÿXÞž¿‡Ûü5yW?ÛVþñ燀>]Þªûù©½Êu¢ÛV ý+¸açé‚bi…Dž¡œ³±¡¼S¸/ 1¯h³z¤ ?ÄAO[[Û—ZS¥ÛÎŽòË·eÁˆ@QóÀµötÁ1Ó°P{*߯î¸u·óUzvô€÷˜¤‰Ä§.|ùÜ?T <ºÇt·åbÙ—àÖ¾µ1áJ,[ºço‡Â‚aw&ÍüíÕ–L›j_¬«]¶×vü€n<ØÖžÌý}H™ªs¾à¹Gµ!—hi×»h,³ˆ£ãRQ·ã0ô§¹€CϘ~TѶó¬çµ×=±ÎQ†Æ”& óàZj¶î?ÁxËéŒé;¯÷| –Ô¸kå.^·ê§`¿²_ð˜öZý¯žz<-T@#µ9– B·’ ½X`Ï£òÚ)ôbýÁ'p@¸k†j­š¡&5…®÷é€×‚5þ8ÿ<Ñi¬Ñ»]Ñ£€e18(ì õ¸þw® Ý/ƾð¾óô÷S)÷ú RdßÝ¿&†Œ­©˜$(@’ÇtmS‹âP˜‡Ε:b骎 è“ègbµQTŠ€s#3¼Ô{0:ö¥&œ÷„@þìþ#Ä !ÿHÇúNõ(’P3ærÉ›q¸%µ{6ZS™”ç qÆ+¸„D«£¿¨Hêù…µ¼ÂcìT‚ äÐ.ïœÔÕyÞY!ï\ÞÕEe+‡Ë]~ýgСú‹r?•t!y†"ö¼t¾9ŠÒ‚¼oòO9iá½#9ªŠ¡Ì#ѰEÎ)Ó£;—Ï@þßtI០SçúnŠŒM@ÂV¥2ø·œ¥ˆC”,¥‹ÓIWÒ¿šC1¯z…n©(rEx(@\ËW‰ìöOÒ)6ˆ©©ñ01{¦·B‘㯞 =ž" Uðqeçœ8zÙ© 8ÊH*¡Ñ©¹gž¶u¶ÅH6§ÖîÂÕñÔj §Ô>ÝÊ{Idä !,±\Õ[Tµp ŒÁÉWWÎ_CXX}äS&¦'Å2“Û5\g6·¸F3ÈÖ^÷hª´gž†GÁ}iñq¼y0BÔùsØÇ„iôE)¬-]åÚ”ð»Ø»â âSuÁçãúÔÒT°µ§Û«Ü›‡`ÏÞT¸èÎ7­céØ|hë>3|§Øïêú¾ÄÞ Ç•s×pçF8åŠà‹K—`mý[C‡úÍl88˜3‹Œ._¾D#ùüÓ>zîè9ðÏr€~‹ˆŒŒÀõë7Эk##‚øÅ¶ÿ,÷ô½ÿÛ8 hD©e3bÈ“½“gá‚G”žR‹ß™‚ææÀ«¶—×^¾¡ä& È4™[4¢Òeâ‰ÕL…'•KÓ ÄV‰çrhªhóä˜\¯¨b ò”ÓrNÛÖc`^Z ެXƒßîB@Ïî0ˆ¡ÎHŸ†ýn›<˘¼H’ñšÚ Íðþh߯1—Žbú‹pÑ4ìÌÔÕ¯3‚Aµ@ŽMÛ§ ¢°~Ž+! S:Ä2ôOAͰÀJCŽM|$jŒ´‘Mí²ÊWÑ4ëŽK†¬pÑe«äöK­.ÙlÌøf·˜èbæQ˜Ú¶:ã–ç·•¤iG—GýI¨Ö¿t3ß\Š¡ «¡¢›pFKk -üOiA—ÿB7Ïe?à¿R%ßù°à°tæHNS±EmË™Õë°-¾j5ð㛞t±=qå3L‹ÁüOf!è£QÁ׆Y3 …O zhñb,úá’CX”b¾Í[cиv°1þ»ÝÖò ó©vòC÷§ºD_éßËØÚÚ I“²8zt?8DA¡>dÿÞ1ëGö"p@k6m@æÌùšžÏ©h݆™èÀû"Pÿ×ÐH–èË’MLî~õ:N8‹›!0²1¡¬6U2˜™2ü‹1APaE`S̽û¸{3’ØF^ÿmLÙ×¥5+ðÝø­LHtŸá½¢”¾E{z|?fOÚï—_Áø¥#P£X*~´¡\úZ;f6öœJÅ«S>ÆÐ)Ý´÷(–~¹‡Ë÷:Ñp±JBÉ®óO²eîÿqæýpPY¥’1å¸äx„Ü CRt$.;‡ËC•ÂT87oáÜ¡s¸Ët¾‚ò0MXðU…ßÎÝdD5.†`Ф°Pœ;pÁ—ï “éÓÍ¬Ì  {o²ÿœ4Üc½h&ÑPåÜRq#8D•fœ;æïQ–Ñ8OçžÅÙ×ÍW‚N¡&–Ç£¢“ÈÿÛ8˹¹v=œÊ)ݱåRúÈŠÆíà;H¦&Vx 5“ÃCqýv,j½ÝïØ†ÀÓ$±Ç%œ:ráQä°•Ìx/d‘æKw˜4„«¨·/à‡QK1jéHtí€]3~ÆîQ Vôá¤^ë{oè·ÉL Ð+Wž`X²!˜1cüü|ù%GQØ3üoã™VnüÆú‚̤‰NcáåLd²hѼþz”+çCêÖ^=¯!I6»””4¾(å-¯½)žWëÏ£L$$ÉKSLb2>(ƒ0 (‰€iÎ`õ¦úòìØsyó:L¾Ša™©ÑÕ±Áa(þÒ“‡Úêôô%X~$Ÿ¬ o+C$Ý?ƒO;ÍFûÏú2j õ| q˜‚ï|…SgcÒ:‘IZ|¯v*‡“faó™8X2×Aø=f@cØ«Î_Á˵L±pÌb„Ä€‰*ȗø¹C¼úEoX—(‰ÁKã΂)اÈW õ•…àC'æUßjSctú*v7ŸŒâPñõàŽÚ K3y/êÕ^ŠÓIÌç«À6s9»z gO„Áˆ±…u‹˜e¤¥â÷ ßÐ×€¡ûjº¤ç¤§0®ßbXØ[#)$š©PªN9d_ÁÝÐh:»ã£åãPÑÝÛçPöüxi¹ñg=jÒgä›7a~ “ßøWn1^1“8ÄÝ¥ít.:Û›Ìf·¬ÏXdwï÷ßoF˜…ëVãÛ/îbÐgå”ÉnvïàN|þᯙԚr¥ÕÒËïü05Jh˜ m>ÖíO„Uf¢ïE#ÃÐ ýæŽD£êl-¿L¤Ü:ƒ1½W¡ÛרÑÓ9ÉX÷ÑXœsnÊG°/©:¾ü¾-vŽŸ…KCcÁDÌŠzãr ªòYM‹½Š¯:}ƒ6sF¢IYw¼öåoÔŒöáùZ#,X|‚™óÒsgDg¸Ep3ÿR Ô“ôws ÅŠ¹`ÆôèÓw.Þx£3úõûÅ‹{S³&yÄÿåHPŽ QÿRþ»o¼Âû#ÖLNLÂÖmë°dÉ|´j„qã:0¤œ€Pe²ðëþÔQ b£0tè*\&XÅE­HŽû˜˜d$'§aæÌ=X¿î 5à?žÏ賨}kÙº,>ú¨ýžΞOŸÿ¾V¨+ÍŒÀ/Ÿ.C†k|³ì=$Ý÷[ž§Cé“d³D0F`K?LýáWœ;Š’}plåN¦QOC &S¸L}C>[QWo",$]¿û*X`ñ ±Xøù.¼Ô©3o…àê¥L _9•K¤`XýqxñQt¨ÝFœcƒl‚f¾'Ô'”Lã E¤úT­?NÈ­™”±jtBîe#™f®A%`E0*©,¬Ýaån„Û¼{5bÖÏLÇÕÛ±õd$ê .ÃX·b¬!°ÕI‘Ô`ž»Á„’ C§ð£Ut܇V-Å Sô{­3¨³=ze]>q‡@þ} |³2¾yu0v.?‚a¾BË+x¿í—8¸?þµ£ñã»ËQzôøßÿà.+ eöÒ¬Êþô&>š‰ÏöMAY«Œí0÷™¨ðodòÖÈe›:aÚ‡Ðë­Fp·H±y›`AçeW3E©¤Ñdàò¦s°ð ÄSß„Mô n> +GÑ•‘”ƒà“±·n Mb0¹ãl\}‰€×Sgðê¦UØ”wâtìÙ~M•@ö]¦Š^†&3ý½g+©-Ž¿zKçnBñ·†ã“÷k0ñOøjÿEÅŒÁÈ̉I£3ͱŒìФksÎäR#±uæ&@r@Ù@;ÎȓÈûÛèïßÎò¡ÃL4k„åËûcäÈ55j“J˜ñÅÿ°ç•Ðê%æo>éù"ÌÛ¿“ÆÔÔ$˜›cÈføhH ØÚIÍ¿Üip'$†åãp±·EY?w%³\‘â.‡oŤoC'.gòõ#K¹E¹ºpñ‰)Ê ’­íãR¡åü³´É‚tVR nòÕéÞÞLÛ jû/CÌSx“JäY׺õQÃg!öì<Ö l±iýY«Óeü-°–¦Yt s ª€n§áüÁÝX¼9Ù!¹®´Ïx´&(Ó¬>êÖ÷còdÔ)ïˆÓ9Ldî†Þ‡¨ ]6qî3LʪÚGÝ;UìRSÒÁ,Áê "—Ö¸ `Älj’4CbóJ²s›ÖcÒÿ·sôy¿*Ÿ~^I‹6žÞ([æ…¡L6bÁì`Q' òF”"5´ŸZ9ü@´.î‡&ÀÕÅÕƒÜq×Èuë–„-“9ù:3†{âC®ã¾—>éUŽvæ°mÚõk.Â…ÃWQ,ê>|^z µÊ¹û¢Y“ò¸·êq vP¹sKX÷5Ÿ‹Fs×+Ø|8¯­¬ óËŠB)+]ƒªƒ:#{ó)윹š´$‚~Æ3eŒòR2€ßKíШ†: týœHQA¼.ÛÕm&W2uGów«â3j¨£G·DÈo;ïåzMKãÌnFâa ú„Ðp~Sءõacg…&Ýšaùì}ÌTÊO$Îk×=9{ÚL‚›ñÃ)1 +?Œ%¿ÜGÙãP©¸…röáþ‹Ö=à-ZóQ„¨ÉB:¥°iÓ Úò^Ãùó÷øÒ§¨ú·ƒ@­íÛµ%EhªþͤðãÃÞÞ úsåÁ)w¤%ØU»`aH-dï¶50¬_[5ìÇ¿™ÏõØh ØcØlG†ýÕ=ý=íkWºþy(÷¦¬p˜XsÅ.›°ÇÆ…‚Å'šhœÐô­šø|ú)\lå€ë§ãÐ|qcFU#µ™hpeÏzŒï¹¾-«¡l)O¦{' 2©‰) ¤oùnQ\qÈHŠÀ–/WàžØÔjùB>Ù; U¿vÌx)WçÑ(UDGkíh„dFf]iY4¶Éq0£FVC“ˆË—bü{+P¡ÏËø`RØ›ô’"W3yOð5ìYqB§eå$Ïj¸#N òkobȘæ´mV]ݤo¡„A‰Ø†šTB¤ Sòð/ÇD3e5S©¨¹­9Lh§!ur$&#Ïg3℘ú™ÚI" áƒ!,h?m’&>iy<শH3·2A([Å~9WÓ0ö.‰*Õ¨û>wž×ÐÁ +GNÁª-i¨Ú® J¸ ˜§>[²7ç ΑÊ{þ¾iù­­ ó+óS¶IC¸ŽýÇ^Æ¥­Wá[±%ÜLp\Þé,|ï0m´1 ˆE+¬¡‚˘êY:ÿ‘ãtÅD"ƒYÝÑÜeíéd‚ÝÑèÞΟêŒètû7lrйÚ•g=àý¦åÅí"“övÆhÔ¨,ÿ=Ê.éÅ]¡”'$(°ËÍVh%ýÁ¿Ÿ¢ËùënÁqi(Pø"×)§àyýþSr€€¨P ÜS^^´ª‰s/o‹ÈH5v­âi¤Âƒ¿–VöÁÌ` ÷Û§n «CÍ+8r!.¾(¥<žy’J5i —‰ßaÉÔxĸTFƒšîÌIAPC›Ýk'."ÿÆ-øöÙQ˜Ùo?ö‡:eÒˆ€8#Gâ@>(ìÿËüŠVVìvsqwx‚U•˜­Ô 8“ˆ‚Ï2™—=Ë(Î^nH9tnuC=/;šZ\@d¸%ÊWuÅ¥m+0~ÐJÔ2ý?¨¦ÍL¥TCÀ'\³J5kŠQÍZ> F»!ö½Û¾úæÞ}ÑobØ´p¥D!yBÓƒ"q"•"ðŽ@’§Œ-,~fcI$ÆE#ìNü|la|ÑNwWÁ(kp6ˆÅ±÷›nŸ7þ k[eûæ®hܲf.Z‹_ŒãP¢IWø2áPÁg/¢qäX$ª½þ¾ÖáwwcÛO›”Ì”B²å3_"‘:C(Ð¥òãZ¦4Ê6tÆÚ‰K‘|=fÕä' A=Eª _cȨY¸v9 5‹ãöÙ눺É㤇÷C\X,Lìa¡‰Ç/ý'cí‘tŒXój–rb˜ÈTfXå}ñ·¢IÊeÒ›„¸ÈxxV~Øœ£ dÿo%±0ôÇŠ:äI! EÞ?CÅð»Kè‰A§ˆµ˜§W Í#IþLWúk_È- ¿ ^œùúË)ÍBPY7´jˆ ó6# ²?j5¯ÁpZÔ¸Jü§EwûÁÁ?µ!ºH#k74¤½æâE‹0×-ɧöávL"ܨõgKÄ¢T`¥²‘¯OÑ :û—BPs¬] -Æ€«%iO§vŠ\ sëààŒd:u-›¹·.`ÛþhhR¯`ûïÇJÐ+q®¤eù—En6MŒ-Ñzäëùú’• éØ?u.D%ãÂÁpÜ39„ããáÛ¨>6nŽšÅ7bJÿ¸ÙÜ{Z—Î}QÎ3“º­BlSô&^Ã’ —™ô&¶%ýѶocX1 ƒ|KÊãdþ+X„¶ÀÝQÞÞ¶¦RKw>x–hW4ƒŠ†”uÃ˾R‹|°›–• çRÕЦÚ,;‰=«Ò†÷w\HóFßFhÓz‹¦ÍÃ÷£àg=[‚aè[‡mh¹S*1ÓÈA¥î ‘3iN§ÙbÔÂÔœRƒ*¯XùcfNÛ^3\ܱ ‹ä þ'ï˜Û wu?>îAðw¢†7 V>^(]1ë•…Ax. GÙŽíñöа3%Í6b?t˜¦ÓZ¦T>‡fŽŽ(U¹8Í,¤—Ç3k˜P‹\ØÓkÀ¼ô 'Õô‡­…Œ<ûÒÅQª¼î¹C^”¨T >ÞÎ(Ý´L“®ì4NxmR_”çøüŠÁ»˜n¹Œd 7´èÛeªúÓdÁJDO¾kçênÛ9ÂÕÚ> ¡i›R|ÕsÔÚšØÚÓ©­2*—ó@RèmÜ ŽGÅ.mðjïª %ó’%àãag¿’(UÊ‘ü—(€KI?ø—dìäBæ[úÍâ çâN0gŒãF}š"s%×Ê9§€Ò(WÝåjø!=ü&.]ˆF¹V P½^ÊÔ„›£qQ™ð¯H-9M8íáíg‡´F…áוˆFgÞG¾þvBÍã'äŸU„d3â*[Fz"îE`ãÜ-ø~È ¤Ed`Ú÷ÓЀvìOSèL©«ÏšKôu^t,˜¿Ào«ú¡iÓòŽ|éé‹x§n#àåúÔ'­¸þ! ÏûÖó¹èsÀgOߢÏ×üj3 ïߎªžg|F¸¶+¯eyýáòg®}ŠÎø®SÊic=¢C3"MÏ´,È¥ÑîCÂåˆPüþû‡4 ø78­Ñž‘y¶çйg¸t5 UF—j¡LµRðòõ¤áG3A‚¬Tkù75ÏrS¨W‹-¥ÜYb*Ûl^Ù–{MöÓ¢ÃqëV¬b›™×i @1±w‚W1{e.U­è|UZT+]ÙV·ä8 ”öe[­Gàü`[[Sz~üx¤ÅÜ[N‡$µMCÒ,²Và²:±$UûÖ©ü`S(þóE¥(oTª™„–ÚQKO*U~«{Œ>A eÛˆ£’m©£Ž"÷¯ÝEl|m›óX8dçå gG~)ÿɧ€¶G•Ù—–TºÄNYm#¯žvÖT~ Y)Œï‚Æã}¨G)<ÊxÂBÌØ¿ü“ûFŠ.´}ʉ›,­«³WK®(؃´#s§…ºÿGÿæ§Zd’ S1‘ØÊQ‘¸zü:Nî>…k)[·já#†£xñâOÝÙ3É®§n]_QÏŠ|Âþ×D¥XTòÈ닞ÏÈÊð”ä$„G&ÃÎŶæ„%ê{æ©LŒCh\6¼Üíþ©.yêJÙÔn…†YÙÂÝžšÀGÒ•ƒèˆ(ì;z;/E£uÛfh^ÆZy =ugO]‘š(¾°%î²ÁQøS7úVÌ¢ó˜!ú¿Ó:WÁ¶­ç°`þÌ:ƒšCK¸ûy¢|Ý Tn\î>n° È·fŒScÚàŠÙ€dõR3”é¾ìŸNæÑÙÖ>£wOÂôÏv?lKJaW»)†ŒjÍ0`ù¯“ö¶¨EÀZÄòU»­=§KeÞ5¹<âçqH·ÝíçlAPV>ݾóóXbDhKÞ¶ZGÜÞ’ñûüe8¸/TùÐÑÖ”_19©úÞ;èÛ©ÕLZ^kkäQÇõCS[Cû«Ë9–~? k?˜…`6©{7)õ œÐoÞ{ô¶*p§¨ó­m+¯O>Þùf<.¹âI6Zý›ŸBEóMº5ÔâJhŤô$$*¶¹±8½çNï>ƒë ;—ÊÄ(.Ž®x£çxù•—øè.qFxÁýáÿ(”Œ5| óÙâýGy¡öã€ÈqíÛC®¤#ÓÕ³§ÑëóíþùèZÙ†o¦‚/¹GtA€»gÓ&¼ùÓ=¬[>Uìy@·íG\öÄÃB#È’b"ðÞG³`ߦæô¥Cj>'¤ÜVÄ)3c¾š‰¹û‘’–« šhÈq<bt©e|Å}hjåüô¡ÛßóÚ–IÉ€‹‹5zô¬Í5pôÈMÆE>‹cÇ®cÇœuX1e)¬¸ ìWÞ¥ªÀÍ×Ξpóq…“»3ÀÔ¼)^òÔ‰“¨kY”ÛIþh·ÕÍ'þͨOf¿ªáÃuI®ÆÈf\oW]Ç®¢?òg8  ,ÑaØ;hM{c.ˆä/|ü--yÇÈ}SðdþªO»'šU†fë»bôCZmC ksBØç×ç“hSîÝÜJhU ÌŽ_ÂÁåHtngQîDGÇ ìV˜âv'WÏ\ÃÕ×~+œ¡Ú,àïïÍ;0RO´lÙ–äß³=à}VÎé¯ûwr`Õ1Å}jц7÷Íóï©~TÏ“|³eóŸ ‡åUF5_`Ô)qPÕ¬ÈÛOþi­ÈÔ÷@áThŒQ¡”íæšµí€v-ñ?†œÒþ¸-ÛzF3’?Aų_š÷!S­ºªU ÷zÍ bp‹i|wl¿@ |›x “8.#cš›ÂÞÙ.NÁ®ðô÷„W—»¹oL8cSIlÌ8¨Æü5ƒ ±!]á•»+w>•9~HÎy£¯%½U”¸ÍJ#ª¡?þÐå`ÞÓÃLs¦V`Tµkþ¦X3OEœÿÔ3ïÁÊR>DQä¹Í»EQéiçZ¹ùÃcìB$N&ŠÓéý˜‘‘Žô44K¥cMÂ2¸‹+÷øï.îß¼ˆ;ˆ ‹A:ÃÒYìÌŒÍáéí…æu›£áȆ ‚§§'(i›þŠð>&ê›ø·p€íŠã@£4PS£:­ñ)Ö=ǂόÆû¼J¥Ë4¿Ff3LaakIxcüŸ½ëŒªxÞ_.¹Kï½÷„N齉€TéUETDÿvå‡+"vA±"UQQé½÷Þ[Ò{ïÉÿ›w9R(‚ñ.wï½-³³ûv¿)æÉŽÌ”4êØi`O›ž<Ç{†šj¦Ï§Ôȇ$MÄšt9~]hƒØœ’ÏÄÑî§±)A ºäÉIOEL¾1œLx’ÚÄ–&åé„Nvc™ßòè‚5-¯v6–0%pJ¼pKΛab˜XQ¯¦µèŸ ¹™™H'¶·±¢ƒA&”ò%Èq%úuoâKg˜‡6_íÃÛÿ·„Dge  ÿ¶£ÛOì s€ÌÓð`‘+?n¸¿¥èdlJrŽÐ»×Ù³ ÃɈ¸”„˜˜Ë8xü6ÐÒ‚*%Ì­-amoK,³¶ã‡Ž,í,aI§&æÆT¥ `f›ŠÓ5ϨØoÄ„žHˆÅ6ìm:Œ£ë·½€{1Cay.XïZ‹Î{±JC³bÅ‚c•ôÑRvÖb:j’O ?â†=ŸîØs2²yÈ- ™ÉŠÉ°Ì”LŽ}Y´¶–Å>*ýÓ€V?màèè7?tjæooÑRDóæÍùÛûo¨øçõ€÷ŸóNŸ².r€“ òî%ÉR]l„{¨NÊlWˆ?—.ÇË‹¢Ñ‡¾êÓ’²p)¥ Ï=?£î³¢$¶kV­Àž‚œŠ.Æ‹SFÂ9ü¦þz!Ý;¡·o!fü| =ꇩtûÊbd5å¨^S˜ŸˆY?®€}Y.$àåG£‹]!Þœ¹{ ­a™—Ž}á!LG»¦J ˆ>µu'>ÚƒC$ÑƒÒ Ãbú‡K±!Ý÷áWRÈh€sÇöⵈý8“–=Ä´! `HSLU'¶BÅ•»5r!‡|ê’½^¾•ƒ½ƒ%½X6æ§©r»¤¤éé¹È¤e˜ìlJÀz/Ò#Ø¥‹Éˆ‹KG"M$¦$Å!âüJç Q@)Y ·ƒK)Þ—E,nD/TA]’#ï);ÈJî·é”CÖJbùAÈ·"¾yœ/Ä©F-–.äÝ­»A‘èV®û†ÜS€,¿¸øÒh40åâÜš¶ìÝí<д™\Ý\ ëççGõ'˜Ñ¶±¥¥%lllhëŸsî zÀ{‡­/æ^àßÞ íµÀA¯Îp/4ØÝ§Q$;jst €óÚD Ò­ÝUøø½1mÞ^ »Ö> raèî‡éO´ÇOïÎÆo;"ñó“íÑhÅaX¹¸£CÃR4lœ±C¾–V@IDATtšK`!Rm[ʾ§Zã€ç& CG§b|È´¯³¿?ˆÓañH²·Á„Ýâg‡<‚%ñ¥¢¹,“‚4¼:k J›·F_#lþùÂûµB;/7¤ÓËÓǶ *5hÕÙ…F˜ø㑽{žÛ|/ô¯™äèC p@[± 0ä8cOOcö”æêBÇNò«rhç±o¤òàŽäìì|­Bö—be»¸˜‹$q1-¦­nßÖu9EÒǹ RT¼\+èÔÑûŸý¦ŠÉ©“Ñøô³Í1”N3:u¬Úlu‘1\[Šj w£ä£¡ÚèÙš˜šÀÂÂB°bÌÖÆ ¼µ±Uô4Ý=t¨Ï²ùV+úƒ•'»G’¾äÚÎêìi ¨‚„v´/jLýËdž´¦›Õ3ôºD¢¶Å€Îͨ‡f†FÖ8'ž¦461°^Ûw íMÝÐ:(e©áxæ»í h87k‡·Ú¨•mjCš&¢5…½[aÉ—á2CèYh‡] <6´=JÂÎâÿ†Ëthíà…©£‘gg—ú7—q¾ŸÑ¾~ØêfkK¸p›\ÀKQ!Zµm®fˆu³‡›Y Ý»Öv^×5ú„á7ÇtS*…ºÓ!;¼iƒŒStßÚ««¯u÷õß·—Fرã8fµhväÈ·7{}n·zÀ{ÛYªÏðžæ@6õweÖ§ž¥>è9psC=99¸Ô…5,ÅŘdXxzÁŠÖ>d :—Äh~AQ ŠÔ¢ÿV†Í›@óû|½=o<1 ^ö%˜þœ£² ®6·„&-œ»"ä3 u~ã3`lkCêËe3¯"‚éâlê [»àåLJq{•:Ác‚ÚT俥!4èÖÅّшJ1C7%Ægp[œz¿‚‘¸'^È<]<–QHIž²M~Jß,»NbýíÛÀ›Ç·¡°ªYP\ÁÕ·Û>^ÕRî±+y´ÒúÂBºÒ‡ZÏ=à­õM¤'ðÎq€(àõå@jðãxJæô{»wŽ÷÷rI¢ÃfÌÓÆ¡øiýI´0ÍÄÜ5ÉxæùÁˆ>†°¤b¤î Ew¿RœŠ+DLQ¢“ràíã‡>>fXYhÔcãIfgçréÊù†<¼TŒ…« Õ½ ‹WÄaò£Q‰s‰eȦ«×͇]г±+¬É@¤Ï“»ûbæW?âÄO¥›bò”Apu±AöÎP|·¨~ }p4¶Q¹q&Ú ÔMNHÇ‘ ÉèÕÈž‹¾JºˆÄYeÕ¢¹bµA)Jt0%”)’ÛOÞœˆZ[OÊLJï݇@7 dÒnåKSÇBMeÍ‚2eé«Jw-ï³´Ÿ;~üH´nKéÅ!¸u¥E†R›ž˜áK4d>A“†àJ}-4F°nß ÛÑ婚ù]%æ¥÷+3k<Ø¥%Ѫ^ïAÛDÿ¿ìW¡qÜ郞÷2ô€÷^n==í·Ÿõ\©@I-J9}¯zü-ØOhs2ôR"’ÓÒ¡²@ßTpà0m:¹9ÂI'yå n1ý¥HbË2ðÅ»‹±³Ìs†R¢ZdÄQ­!Ð’ñÙë5b9T; V6¶°²ÕõO‚‘ÊÒX¡WY¬¢^Ã`Ô UT‡ ^T&hÝšæ±ä$9U*ìíÊók[˜Ñ>“l®Zm0±wÅ´§‡Ñ²Ó‰Ê>üw8 caØr±£zÜÃÐÞ{¸ñô¤ßnp"ŸFƒ÷2ŸËĮߺ»Ý ®›ùæ¡ÐÌÏŒ¶FrL2œ`+n„%T£Š†.­0ÔoÐmšÔ‡£9ã^i`ªäBÐZþ-_:)p¥[Wÿd'.®f^Oò»®NÁôAÆXMšô`÷Flª{ÏØÞF ?Bg²@Ò·ÝkãÿNô€÷¿ÓÖúšÞ8©+‚/‚}Ðsào9 €À{´Cg9 &’ZÚJ½1.`SÜ?œ&ðxMkw÷o ºûîØU^À»__=ZHs(;rù7 #m ý_=j%ô€·V6‹ž¨»ÆÍ'i‡—’±~M(Ñ(—ÒÝ5bþ+(޵ĎhíÔ¯¥·/ß)Ø‹nˆÁ`«ŸÒ¾a|©%Óä—ŸìþÛ¸µƒ+ÿŠŠr!±X‘G jºÖ­º* >2 ø Ï‹¸È÷»5èZXE³lÆÆ†zÍ¥›iTYÄm;ˆ„¿CÀͤÐÇÑs Vr@xke³è‰º; ò˜¿W{h­7ué¶³ê¤|w¨ªÛ¥ÒMnv¦O[M#î±t‘z÷ÑŸxÀ[Hà¥â¢§PY÷è?ÿª²YÕTŠˆHÆããÒ#“ªŠ\ÜâfdåÓ¡B–þz§ÇPX^3  R¢jO:ëxëí~p›Ä·ÝSÿâT-K̆ð7Ò×µo é#ÖràîUúf¸5èï­ñK»®s@p¢«V×+Z[ê§B\l:~X°ê|‚ì ?îî„*€·ˆÎ!Ä_üŽ#aÐÌßÀsiú­ÜÓcÄ]ð©K±(Í/EZhÔ\Hˆõb]7­ÙE”ðRÊŸ—L»…5ÒÄjtF:6n;‡#[¡C[’P3ÀZW·ÿ­VÂ/íïÊÚ%Šká_Èur`yâuR**^Üd€¬h·ë$úÏÜ6,Ÿ+¤—Þ”n}ÝbMe7à•×ÖZêomm=]wÈG¶Ñn5Ë ¯ØïXPÿuD“Fx»g‚ž» . Æ.§§ã‘eáè‰?©ÃwŒu± q{Û¯^0¾èݦjQi¨äùÙ„<°hÆ6iŒWè¹êïú >[_t½üáxsçÖŠÂkÕ/NtR*ßZ•pt+—?JËÄp!Ý Ó&²81¡£“"Zî°U&Ï™¶2¾-U“ÅžXi §7»t[²¬ ™!44AáyDljãÇÉû[é÷ÔÊ®—îÛÈȈ;7jå£Ñh`nn~å¹ÄÑÅÓ}ëÒÞ­šëïÝâ¼¾ÜÚɾMI—HQ*‹¯¤Ö5ªŒé›Ýœƒ¦²…z—+WÏÞ  FJ^MŒéÃíà€¨x[[ÃÑìæ­Øöfl{yó47Ñ ) Î#ÈI±L¸†*êãú*mu#ÐAÝaNÒw'Hí´àU¤²ZP[L‹qò!p-*UÔ{¢¢Ò™‚¸¸L$¥d#Žp²²òM•,~ò¨ëœOgvô–Ðü^Qµ½ý€WéÖ³òó×CÚkþý¯îæ°¨òñå¬/ðÃß+ü¯Ë|!†U­WåU“4j ìÊ·ßmKKKøZZYÒºÍÚÃÕÕ^Þ^ðññ……Uš´@Yò1äû®Äò]“án½ù5Y'}Þzüsà(»—>ÜQ(²+7,w¢@Öð|«S;ŠRÄ–Z‘¥,) É_eŠc›ë¤c•ûü–xF|~e2ä¹ðÔ)Lß¹M]œÑÁÓ œáaa _kÚ6†LlJéK•Û°R9ŒrG‚P\l ¦ù)B=ÚÅÆf"5-Âqöl<Â.$"::é¼'úã*ê;khÄ~LÌLala 3s[˜Û›ÂÚÜ„÷506•çü4fÈ])y# Bî  ÆÝ©2YpHß®ãAÞQQï’ÝšýBöÕ‚¼~ ?7¹Ù9HÉHDnlïç£0·ÅŒSD/’¥\ÐÉaU[[xx¸ÃÏÏõë×Gpp0ì)`pvvæ}Ú0gÿ@,Ÿ+ïümâ«ðÞ&F곩 àÔ+‡3Ò z?ÄS^eú­ •Ó×áV9p·U+n•Þ{(~Aè¦ðpøsòó¦Ȉ×rpM‚ü-å¤WÂëª–Ž‹Ã zõ´“Ÿ G~î£ÄÈŸ8Ö†žÇ²Óg`@°çie::à>7tôö„ƒ¹Leò¤Xòו¡TcH{‰An1ò RÓr°kç<ðó±”Þ¦"!9Ù9 @µu±‡‹·3õ ‚G€œ=éRÚfVf0·4ƒ™5?V¦ÊocCcE²&ãRÅÈTùwUJŸ±žU8Pñ¶jßYù+=±€^'s3óøÉAŽ|gåò;WùNºœ„˜ 1¸|1ÛmÇÒK lfb;xzz"((Íš7Cûöíáææüê$ÁUˆ¸Å =à½E†é£×q¢•†,­ò\¯ª¾zzÜ-P™]P€¾‹ãÉV-ÑÖÛ¹ùù 9é¼8:‡ø™uàF7n U#ZMk(]jʉðÏÃñíáÃøöàADÐË]BI!ŒÕ8Ÿ“„Ÿ·Ÿ…Aa¼ ŠÛ¸º£µ§;|líáOIR1÷þEr¤Å×Úi[›ñ¿û+* b.5%ÇiqäÐÁKX¿áΞ‰E!}YÚ[ÁÞà ž­›¡S}Oø5ò…_nñZ™ÃPMPnÌm^ê…Ê?•ÿÉANùWXƒ¦Úþ]íõ©õÐrÀ€C-¬-`emI\õeÃìÁÅ(¦Ä·˜ê8ù9yˆ<Ã…àÉpDœ‰DTX4Vl9‡EKAUªB@`ºuí;¢a£†Šj„¨Oøý'Axÿ ×ôiê.\h¦ˆ“>è9 ç@ r€RÚÖܾ,æ÷‹k×Qkêÿeô.>q‹x(>+ Ö”îŒjB›ØÕ·‹)}·àÄ÷bûèˆwíÄòóçaª1ÁƒczÁ'Ø1±8±óÖ9‡…À&e”kÌ”2óyðkÿþøú8PM@C)Õ¸Ý*Ú*•Nvªû¾1JJhºŽH÷äÉh,YrÛ¶ŸCØÅTª˜ A›zýfOø7òƒ£§#\}a®6£]Ý?-Õ•@m^»|hSGŒþ[ÏÀÝBíz ÕT×Qó ¤™%»ÎhÙµ¨”ƒÜ²<$D&"12 Qç£qxëÌÿu>¾þökøxø UËVÎ…nÛ¶m}aQ{¸•Àƒ®²G¤ÿ%ÌŸ7Ï<óþüëqôèÁÉD/5¨h~n3*§?,M*îéÕ  qá|:tú…`z¯û+$y5Xª>ëªI@gõ ê“¼¢”æñeËð=¥´× ë7ÀŸÃ‡qмA`>rè믳çðÞÖ-ËËÆÀIýðÐ3ƒ`ïlÏÃ`…ˆ Çñ]'z<Ñ”"%^ŒGfR:ÌÍÔ¨_ÏíÚù!$Äž^vð·‡…¥=êP‚á®ëR×€* %Ø ßÿ°+W‡Õµiˆö}[#¤[EÒ¥!–É\¤[%бµº8åJ ÕÅzÝ ß)þi½u=úZ<»Ù¹éiHM͇½§3h°AJK|9¥jK8¹Z°ÛiûBI~6“ ààÎüˆ‡µ@‰úÏÉ1©PÛØÁÆJƒüìt¤Æçˆ ÅhŠÚÔ¶VVP›¢ìŠÊ‡¤&Åy8¾v2í¡c;–$têêsƒ>xÝ8º´’ÿµÒëžëžÉµî·¤©ªÇ¯ü¼<-M»)¨MY)êòº^:¹Ï…JN:ö,ÙÞ]àãfN té*ç/¿ËË(¿Í}dEœÄ¶ÝñhÕ¯3\¬Ù²,߀–9òâðyk,ÚökKiqɳz¾\:—!tÏ>œ=¯úþHIŠ…W«6¨çeª,°oŽÿå]ó‹4K3ŠÃueúå·-Mì8µj²­}Ѧk ¯dìeÕ|Òã±sé 4Â:ÚjxWÒTM/9Ý(èx*z¿ò±s²C—~Ѷw+ìZ±³_þ —Æ^ÂÒ?–*Vn”—<»Òåÿ.¢þ¹žuŸ|?£•†i+Ê'}ÝËY÷k®¯áíæ€ô28½“V­ÃátªÊÔØJÊ2ÀÅ„p¼¸i ¶'åþ}Yœ\Û·ìGéÊ¥šþ *æ£ÛÍŽkåGI´ ­' ¢ÚµBGx ßPº[=¡èúHwòóÅ_cGã“>½q~å!¼6àu,øh1ÒÒÐDŠ$À´Ós ÝYk¥H-º4Ä©ã0ý—7ñɺéødí ¼¾ð5´Ü aÉÅxgæfŒ}xôŸƒz}Š%¿ìÃcoǘçFÐl˜‰r L'Õ­NVõkÊO‘yî4Á¼†5[b m¤ÅhF0ÌOÂw“?À÷ŸîÅÅMËñÙ”H+Þê[¿%9X?ãs¼Úû¬ÛçQÀ²S0ÿ™ixcܧ¸œZÊ\e«Øñ‡÷à­¡ ‘Áz—ƒ`%‰‘˜=ñ-,] s^_Ú²¯õŸ†ý3ýï ›†—Ýû.U¦„Ц´G–¯ÃîÝ‘ÌSrå_J ùQ‰”ùš@™@õŸåp’ô`m4åi™^þéî+kÉ]I£{ÂkÔ]I\‰'y ìâÓ+´hÓ—çÆ8ÊS–¡fúÐÍÛqàp,…¥ÒR¦¤þJ:$­ÐF³Y%£Ù‰XúÒW8~6¼­¨§”SQKaþR'm`úR5o–¿i¹|÷.ÇÚ¥‡©[®A6ð¯ÿЏ”³ CâG]Êò xMUšø ˜5h:~[¸ñ/`Þ; qðh:Û_¨­D»ÂÉw+µ›Â£+4ñ©BsEI’GÚ…ãXóë®›ÕL­ä ðC꯭¨”aïç?aéO´Ï\Js€Ê3- ùñ‘˜ÿô78§ð§2]üÑÕêæ¾å}•ƒ›|g{îŠ×çMÅÁ#1{öì›Ê@/á½)6é#ýg8ÇcÖù²O¥züPš¡2ÁíBðù¥LBˆë‡ ¹Gů«c‹¬ƒS]•(¥Hâá+{‚-™zº6n~‡.rÒ«˜¸®Î§üÑmp`cüá×-4q𘻅›d~‡O‰õ ħ{v#—Î$tAìó¦W¶›¨‰.IÕoJ{­1©UktöñÁG;wá—waßÚ;uZvkA·‡²­©êVª£íÑ E=tÜ^±=š–˜†S{iuá`(.¾„v¾è:¢‹2‹d÷V‚Ävô÷…µiV}µý»O¦o¡@ôÁcØOëƒ}Fq¿!6*žÓ¼Àœª°©OcÊ娰mÑVôíõ0Œ™Ô*-: a§"±ø‡Ýxù¥®ÌŸ²×ìLæK¤*KÕ¼@©ñ4eÍ…™”Tœž‚”LšûlT…ˆ?}k¾[‹wG¼iËßG‡f.ÊÖ³ˆK5¦x१PlæÄ>T‚ÜÔ$\<š_…½·MM9Ð Eµ¹‹–EZR ±u€o#/:r#Håý˜°pÄDg@ma 料°¥:IAZâ’Š`kkˆ¨ÓQ(ÑÐærˆ?, sqñt"êyÁZ±^†Œ„8$'Á§¡' 2’páT4ró àäOw[‚%êt‡ÆAÍs‰ÉÔ×6ÄÆY ‘×b |Ü­áâj†¬¤„Ÿ»ÌÍ> ÜêûÂÕÙ‚äK^Î<œÞ+‚©u½+]²5=: ™¥æpçHCŠ< £3œ]¬©ÆƒKg¢`Ø ¯Ìj/ól_¸ V÷¹?¬h¥ÃÜRƒ¢ôd‹L@ž‘)|šú‘êò~J0Y’°Ã'‘b‡Ÿg_nZ€%³Œ ¡ê€Œ¹äUøÉ:'1‚=Õk||Q”—Šèˆ\x5p§c¤Ò3\jó®çȱ© I.¡ÐÒn<´-ý¬˜jNæÿŽ•û­ÔÒþA¶(HMÄÅs1ÈÉ--U‡|ýËj Sžó6`»œBBL|¸ ⬘ 4!Å9ŒœÈcú ´ÈÏÅ›K°U‡l¸ƒpkïMå^+ÀׇýÆ#Àû÷í¯ü躿o4_7‘þžu“-ú…€ö’Vd«6H×ÍJëkUc(ã¡'3ê¶ñôqiRrr aÅÓûÆÊ¶°œîÏFlž .¦*äsì·¦åч«Ê8ÑÒTW ¼‹½J š¯2¢.i^ÚE¼±%3úóÄ2Óh$—rTÏ/ÌGT‰•¦¿*Pêãî Ò–w÷z:¥±õœÐÜÍ»"#®éOÛ¼]ýünMº{%uù‘öòÓÀÑ ?€´å;}ÛvL5†uÁˆ‡ÀÛß‹Ûþ:þT¼ïÚfŠDÉÁÍ=rF¯‡ºó4y> ²ó`J;¹· v%WÙú5²òÀ€1-ñÆô=86m‚Eõ ÿÚ€l¯ŽèÝË q?R"È6ÒQX‰"ÂÝ2^°in­1ùIw|úòN„FDˆ¯s'x6ñB1®ØûÉ.ª5f.Í1eéd¨OíÁÔg×ÀÞÕExïÛ6øaÂW8ŸTL5[1éf„!Ó_ÄÀÞøã±÷p* (4t@3o3ìÙ¢ó«`gì‡aÆ#ß#±2qöŸ"S+<6k º¶0ÇÒiŸâ¿.ÀÂÆ Æê"D%•¡KÞU$™§[„E'=1ý‡G¹€¸€÷†½ ·Á£ñúû#Pº oN^‡^=ܰ}K<Ú+ïFÜe~™e#h¹äòE|=y&ÔÅHç"+¨ß ¼úÙH˜³Å©D“ˆßoF¦AV}´E#ÚÐ"ˆ81DVâi|<æ \ˆ+†±ÆÅ½¾ù"ÚFcúxiçû4éW†USÞÆÊІ˜µó¸g`Þ¨i°ÿ¿g1al¶(Ò§Obåü£ˆÌ·¢ôx'^z¿¾|âS„Ç•( 3ZC¿÷^¨AõalV†“+WáÃÐT¯ÉD®‘^˜÷:šÚi᥼;™áÇ0ó±o‘Aç̿ЀÖW>z ½»û(}µZo¼éËR.ÜåÀšŠúã7n.ÖÍ䤣ç@]àÀ˜6À“¹³¨5êBsÞí:®(*ÈÃo'áë}Ûñ貿ðîcHáÜŸ——9[×`ä›0mýJLܰ Ê–j%ª9˜Çskðýõ;ñÕ¡ƒx~ËlHÌAzÆeL]»KÂOá“]»°„‡°DeBMIÊ™ˆp¼»j Æ.ýÓöžAf¥­ËJ9 RX¢Õ¬rÿN^^KêÔÞïïW¥ÔÁ ЇÄn‹×=‚jÙdа!Ö>ò0î÷òŪV㕾¯aÅO«9IçPj&ÅWGUHR€m!A[òÉj˜YÞHo³jÚk]Ñ)šŽî §âTìXsœWä&‡cõ¢3èõdw8ѺƒN‡±zzÂ`gE`Õ¯çѸKtØMLc°qõiF•±‹)¹•Þá‰aèÞøá•…HÊçaÆk-~ªg^~]Fim1…€gúÑ‚¥‹7º nE 4%¦Œs…[°YIÙ””àò¾8µ? 濇o·Ï@ŸAÍ‘~&9T“4†(dLÿ–]aöî¯0é¥Î[¾'÷Æ¢kàÖcæìÿ3~‰Ä£k±tñ9ñ,ñåSqhÐ f혅‡‡{òPâ^;£^+lš³ùe*äR—õèêè÷`}lÿtvŸ1ÄK¿ÏÄ7;>䱌büüÚ"¤$]F²^Xüž˜7ÁVèù쓘øb0~þ߸X䉷WYß‚gÉe,™±Ñ—NâçO¢ÛkÏởcèC¾ÈÊáÆ+Œ®ÀµIÂ×GB:%Éø}! '7žFzaÂ7¥ç1#¸{X#Ž\cŸ&è?²\ÛuÇ §Ûíbdç•¡þÐÁø|ûl<>!û×ïC,%ÖÒ‡e¡d`éI ü¬\1á»W0xT =šñ>Sî™3.Xã…_>Åw»?DŘ÷ÑJ”š8ÂBuGާ cÇÁ‰H;ÎR.ò¢OcßE|ý=¹$^Y —-Щ‹ZwÃÔ#'ü2JTöx\ÚuÇèÔÄK>ÝÃ^A©r)ÿšYãño§cö†7à˜ukÿÚ‹<Žeb“Wc”å.Äá(3Lýãc|½õ4tÌÆâi¿"ƒjwr¦½“e•¿Fú/=j1äÀýÔ냞·ƒÄÜB4D{÷ŒoÕcýlðź•øŽ[¾g"Oã#Z 8’p nÚZPâE7±EL#ŸÒâ4L!H>\l‚^ @“|ÿ#è ‰š•Úõ]\àoI£–L«*ÈÄÖˆ hÞ¨%žãvðâC;q<‹ý¹ò¬|;*v;ó úÁCÔãi´SJûø—ï²Ü¦‚¬Åq…=¯=À¼-¸Õêle„¯_ø o{'i€M¥,8*!˜ë/@ôŸHv+g'àÅÄ>C©‡‹v …À<ôψ2öF÷ÞMð!d_+ˆNn؆½¸@ ÔÜ‘Rú<wóî¯7!Žê ä-!t1vÀØÆG7á—_Ž¡ÄXM •Àêµ2¿Á=#5ûÝU…ýŽ}Õ€ÒP;èÓà›Ç>Ä7ŸoE£~bÂ[mH­@^ⴒσ&w†9í·3Ÿ¬~VIÈɶÀ —»pÃ^­Û I=7Dî %xâÂ( ½¶‚w9‚º5§L‚µ4µAû‘}|Ž_ÎGô¡}+ðGß¾>¸•L½tCìýù/,øx¥¥…ÈIBr%¥fè:©'š7ö€ $˜r{ĘÒzã„Æä@EÉôæïÅ/_oFAf’“¢uö2òý‚ѽg›˜¢åÐ>ð§U¯Ù ÷hÜõÍ/ ”*"‡7‡!àÁûá…\Å‘}‘°oÒ^N¢NCE$òÍÔœº¯j ,LM¹ð*‚¹·wÚ*;AÁ]YOÚ…/ãÂãJÅ–µ):Ò‚¿É+- Ÿt mÆõ@ÓÆÎИ: Ý£}Px4 9N¦jÄ‘Íáˆã‚»¨^ctí`‡£[#Èßã0 vA½&.ìÓÊÊꘙr'I£†‰©.­CÐ÷ÑN8óëRÌyíWœÉçX%¥rO"O=Ú¡i#'ظãÁ~>ˆ §g5J_eÜÉÍFXL:`*ì\ø'}¶–*æEÈL¹„ä,­…•+u«ázÀ[à Ögq`Án`Î6NŽ•˜{¬ zrkÐl='gxYÛbX»˜ìa‰-1QhÖ¸-ž T¥±G¾°7ÞïÜ 9‰ç1viægéñs8}9)Ns§ºÛ²'aî€ÑþÞ°³sFOWêöÙcHÃhïÌe<ˆ¥¶À„¶]ñPôiÔ~´3™EfáîÓ”êÓp+wO…†¦ðMèIí–«Ý,õNE,Ï`é³Ï‡bö¬ÑÈŒ ´|И7}¢³PZÙšg•^ÐrHX…À¾½g±~éqøtè€V”³Uƒr*þñ`UQ:v.Ûœb#lxy:žêù:¶nŽGÚ壨º!jnqK(Í/„KËnxâÙ¦XùÞ\œ:™ -T¹–)é”’Eý&瞆Q{Œ©j%ôlgß°5¦Ì}-CìpiÃf¼ÚþÌ™¹0J zyZ(¥u Ù_PÁÑÁ–fbòÍ‚OÑYýàB¶•Í^I®Ùŵ>çJ¹8°UDszþ[ÂÞ¼ û–Ãá…ûP§ø=í´ü2-AFbRhõÂ2$=Çô€)u?ec]Mµ xâŠ@ûOè#ÕĪ©NŽ”Ø$8wïŒîÚðp[! ©zd –+˜Ò€NL­˜²à•|,¹má†ãkãdX,î{¸Ýåâ0%ÏØ¿š÷k Õ5tÉÊ®XSÑÞµw^¤¦¥ü¾VÐÒL(´ëbp¬Qih>`ZÉK*òIᙉ5Z´òAÁ‘ÃØ±ê4l:І­?bvľÑÔ'o  á{EPêÅ~'-wlÙ¯xïÑyˆMUÁÙÛ vTW0!J; p7jùa™lWèJJ•ìå`(+}¥-lZ¶DÏQÝ`\VÈ(ñέ’Å)K_Šžµœ|ñVÒºžÔ™´Ê¤¡–j9ézòj18 Èì!*<‰ìn…#Ô)TtSbBÛSj‚U iÙY8•”ĉ€ó‰i1¼M8ÛàÙ–¹ ȉŒYq«Ù:ÁE̯L@.%—šòn*yšPZD¿ì¾†pæõ½ÄÖîè&°‰'Îï€5Õˆ°jŒt â¥éáGº [÷@|üÑFÌûôWlûcysÚömC — ·q…„šD+Ó£E34îhE¯~ÄgcÈÔ0#(+!(SJ—®C0)Fø¨T†H =Š Ebèço oj¦ˆÏÃ^ÇŽÅk0¸Ë@e;Yh/& èòÜÓØ¶ö9|óf ìB®’Ñ*UÿCPVÄÝ®"Š0 r²ppñ¯X¾.£çµ$hùtåu•sc/œATª=žýbÁV2>ùV/Ü‚ì7:‚K2¦*£Î§)w&rè$ =xð)òôv|5u;º>Bšqt{Z j€¼ÌÅÖ±]Göïê¿+”S©m“2J±5èÙÕº°¿¢,1~Ý6†|ˆÍ¬ü1ùãÿÁ‰6bÃɳ¸\J+5ö PÕµ+,ùWXÄ…a1%š”D:ø5Ç Ÿ>sƒ|œ=v˜RGXfB~x"#RQßÖ—ÇYêðö«òЍªlвU>¥~m™™;· BêÌúv1A¾ÆÓt[ñN‚Ãòò… ás±twNAr[G™öÇ•«òºk‘¤ö¯ö–ðUÀº…!¶î åyîp7*¡.î%€ÎPLLàÞ§%r¾Ÿƒ_Ïñ èìGQߟ‡öfÿ€åEF8ë>³Eyz¥<ð, )â9ªb\:~fmzá­YOCS‰·¯á>–ÊňŠÈ©éN¦ÉTQ¨ž`è-{±à;oXÚ¹ ëä~ÌýÒÙaì ç9²žÂºõÛ—É]¤Ãû¸˜ú®êx,ßS€G&4¹†ð™ŽmŠ46Ô3î‚#Oþ†§hPÏÓf¯‚Cï¾xó¾ ‚aéw&¾ÅpgŠÒ—R[8püØq¬]»š>©[ÀæCî\w«-¸ͼ@eIÚÓ¡„©b„¹Aý£ÇšVJÉÆÜŸö ™½ º‹î&Á_Ü:.ÍOÃÒ³áÍÈDf~:PÇð²Y>ërâ#Žã§ÐHdä"2'vNpä ú+`„“œ±¹ ü5¹øæè1¬=Ÿ‹Hãa•¾~n(IIâõY\ÌIÃîø<eFaÂDd¤ÃßÕÏŸÄŠè&*åØ\ë &· ²ã1ãðE<а)Z:²ßßëjcfF=^Stðö"ÆÔÌÛÜ(=s›".bôèÖ»,@ J‚‚=0hp¬(q_N_kø‘U…w'­cP¦YC$Io·§i%;kt´'½mHÜÕîX88ÁÌ\C]OS˜Q:jfc;gwtÞMBÜS”DâJúyÀÞTׯþp¤EŸ°¡I+qil_;Þ×[zBsÅ:»©ŸÑ-²»‹)Á« ¶®VÜeÐP”&¸7ÅЗGaðÃddÆ ¥ ìB”ÚZÁ«i}´íÞÁ lw:q1¹h8ðŒ~ù~ØÒa…ö­&74E£N!p³-Aøqn§‡´Â“3†ÃËͺ4…£u!.‰€¡s ™þZ7àâ€ÜÊÙ [xÄ*Dx°uqCýf>´F˜Û;‘nk4ëO½ÜæÎ Gê#¤½Mª…ã2uN[ ë‡q/ €­bÒÄ>mêÑYwÜÕprG^B6TVŽè>±š4²CÔ‰0$¦©ÐeâP ŸØÖôDب[}ä]ŽåAÒ´yx0Útl€àv´da) µA–&Ž6°±£ã”þmHWÕf°¶sD‡‡ÚÛÖ Õ*JO4¤ô×ÃÇ…’ääP¶U ¯=P¿¹ûÛ…¼µqtAý>ÔÕ»X¾–4›×:€:ÍÜ%2³A½ûéP¥š´r£¹ºHDG¢íÄ3¡=ÛLËwGö-êF·îè cS Z±FÀýÀ«[H ¯ÝaXPŒÜBSô˜ÐÞVyˆ K…[ËÖ˜ôú8˜ä£Pe… ê÷¶ÞfÙ±ˆŒ,ÂÏŒFÿÁMi1ƒ–"h!%ˆÒn?ö¹¦­<s*±ñEh=f íW÷¥é@mO—2o5;ÈÏÍÇú©ba‡1cÇüm4Æ‘FþS˜?o>žyæ)üù×ãèÑ£ ë^aÿò?ňkUVwJ^{lûZ1ô÷n+ qá|:tú…`z¯ûïˆtï¶Váz™‰DŠÙ£4ÉàbR‚£ñÉ-¶hëá3NdYH%Öâ™êb–r¢ç$IàQ5ÈäVа¸œLσ§½3Z:‰¾.NQö]ŽA2õ8[y¸Á¨(—æ²8)rÁ`EðXFë$ tõF »ÄáÖsy?€¡Ï=7,ãÌy-à÷Ïɸ"Z±Uƒ@“«ïVSùJ⋼Z‚äX5\» mœªÏ¤n×¢§j~W•©¼os’»ŽÎŠ\´4_ë¾öIE¾š/×¢÷Z÷´¥U¦WîT¿¾^ݫǫL{ÕßÕË®z]•þª¹V…Ï’®¢ÞeTÎïz¿+bë~U-éêzëâÉwU+?¹Ùß7O×ÍæøoãU,þmNúôzÔ¼·xñw }d²¸ûÓo]`éº<€K %z©•O]Ë3ˆÊ·­º’^âë‚’7ïñp‘påZÉ“y]ù]^†.Í5¿¹=J¯d²S¬:p;–ê%Ç·ÂÚuÏc¸6Ø4-¦|ߌ8•¦µŽÓ6ÂUŠ×wœ<™“ÓÿÜ)ÈNÉàÕ?€­\ôä¤fеXÁ­ °#yDÒ©C)é»u k‚¦k7•hÍf§eÒ2Þßì¼\39u¨ss•–Ë:Þššçgd 'WB*—É'´Ö’‘šÉ>Qù¾Žpj ˆíW]ó2.M½çÑ\Zžì.UNËÞA+Jy5î¡ ¼÷PcéI½x¬ð|w­•ó;Pœ¾=ô¸8 ð¬¦Ô“~tBWÌül8r2rq&Šp÷n^9°T†5ÏNÇ×ïoBAz f=0ûO¦ß"]„<`ùÓàÿaåêK<TýxOeÀ#íUýZ׆r¿úGûLEp”…ußý…;/‘êêeèò¸Ö7PiÖµžjïU§©úµ.åõîërá&7ŸLx¿¯ºLû´·Ö¾Ft}ú§ExäÈâ!è¿Y7¦GGõõ¾•#k4/¶äÿ^Çì9GÉÕŠÅmDAÔQLþN„æ°.Ϥ hßùÒž½øíËU´°@+ÊsÆ)ÌDZ5›ðã«_cÖ«s±qíIÚ)–eÛ˜:9ô –Íù ÷ÅÞb?»^-îÌýʵ¿3%êKÑs 6s@ìÀÜâW›«soÑöïþ{«®zjïþÕaT¾4ñeoA×Ît²K›©w'P™OG9…()(£41©4•%'Ò.Ó­+¥r×{›Jéz¸„½Ê µ$Ÿ¶¨'ž—€¬Ì5å>ÚrfÅ”ú*îΘª„ç$RÞÓ>9¦”Æ¿”ʇ†E*}F©xtÞ2 Ÿ¼ð-Ö@‰SÙ«i¤Ä‘–Fª~¯0Û~^‰ó‘9Lu5Ï˨_Bi¬H¯ÐD)r %”¹´LCZ™–´å2y"­” :ÙÅyR_JÊiR+%.Ù™…× Q‰Î8”€² äª.ˆ àRš,ÊN£3†Ö‘¥²>:^R[FI«ükÅŒ+E…y¦U‘Ñåvõ·x˜“4Òqö‚Öbšz»oÔ`ôìæÁÞÉ:Q:«Ô‘ugA"=¼)Òj…f%¹XŒm_‡×†~€o^ù¡¹´^L{½¤íøÊexcôgHWÛÂV•†ÙcÞÆò ø\SËÿÄk½‹Y/|ÍÛâ•4WSZ;ïÜÊ«vÖ@O•ž·“<•\N£[÷^=°Þ΢ôyU倊Ö eÁñ¯IÕ<ïÞ•PTLLw’›-ùjðq³)ï¡xì_åþþÑî`ÿU6ÿ(±Hô²ã"°üËe8y"Þm!#£†."‰$Tå¸%NI®"ï+ÃÁ_Vádœ½ô ,è¬8/¿¼³ }ZÁ®¨ „GIÇ)áÛ²ì ’S àB/xýŸ|ôxhÁ DÓ.qJŽŠƒ#]X÷ü üÊpáØ%äÑßçiAA§Ê@ƒÚŽ¡ÃÇ®½0*h.–]MbÕ1R‘/*U†^Ò]ZŒÄÃðÛo1åÇÇáílJl¨Ešbj+æÐn¬'ø²£c…Ã;.À¡i:wõÄ¡¥ÛWˆ¦ƒºÓE;Ú•ÍÀþ?6`óòãÈ/2†_û–x`lgx:™ öðAüñízDg¢q[.èÒâ:s€,ò“#ñÇ»ëÑàéÁhäH*J°oÅœ ×À‹n‹Õ´e¬bE2¢ÏaÕ7qæl T–Žh?¼+º=й'°jáI¸›ààÊÈQY í£é5ιWBÑå )R÷Ø}Ûñ׆T }¶?\í4»ØñÃR¤:4ƒk~2 ˜– —3ë6᯻‘£±CÓ3z1„mÿ¦DœÄÏ_Aïi£àm–‹ÐMÉðÞD¬{}ˆ\úé¶zû7Ká5| ^úäQX"ªôW°ò›½Ò7ák/¢ñ¸‘°[¼ *q y=ཇKOjMs€Cð’ýt-̃k#îÓŽÚ•ÖÒ5]ú6²]&¿}q±øöØÞëú¿×øCa 'o‘Õ~Êea¬æ¤(Î)¤1êhP‰íŒævºbíâh˜«ÚAà]>–¿ô~Þ‹ñÿŒ‚ЃXº#mëÿ]ÃI}5Pg^ÆÒÏ ÑÀ.hl‡˜];0ÿ›­x­WG󚨑xê >{b6|GŒ@ç&ØðÙb¼s¡ßÿ2†Þ·Öaöo±èûÜX´êl‚ïÞ˜¤¼5%??û1N'«aE—ØRZ)-‹”š8bÚ¦h2`8BFZ`Ť½½ Ï©Xv>Ý/Çit² u«\Q}"è,s¨âŽâý§Ä”/'À×2àl@Ét8~úß"´™0í[{àÇ¿Çê¯ý0ôùàž¾s§|ƒz[;á‰;Ð÷ÕaðuÌǪ÷?ÃÙ(LŸÖ_Mü¡ÖÍ0~LS]½ NÐ>8߉ë Ápø¶58cæ3zy)XñÙB$ùD½Ú"¦äX…\,Ÿü)V%ÙbØã]±;>û1,v~ƒ†…)XIos†!1lRkœ\³Œý O¿/zF«,ÇÕÒ Þó±ñýÅðéÚƒºx!ãÒ9,˜ò:~ŒK?ÿŽ˜–îèè‡÷ÇçAƒÐ£µ þüâ$Ç›À.~óÓÒprÏyt¢ä›v1îçÿÑ:ËE¬ŸºâÊ8ULizJX>Z>ÖTq7\JÈ[¿m#ü5íâ¹¹ÿ“ÿƒ•u¦þººü=º‡jß}=à­}m¢§ènr@ÜåßM þse›™# À§è§þìÚA®#!=#yÜ6Ò˜Pjöw`äîVº¸°†´Kæ`gQ7ì7`§¸çõö±ƒ­-½ÊÝc Z‘î–fÅbËÁ´ÿ4&<ÑEe!8ñÇ)”Ü„ e±‘ÛplO¸½³‡¶†¢CÐ}Øþó^¸7 FóV.8@u…²¢"˜xøbâçÏöaìéZ8ýÜ ,XOWÕ t`âß­ žüè1¸"±«w œý`ÑÏý1“ìëºîÎÕ^¡]"«Ì)hIeÓz’ŸÄ~°û÷€W—B:Óe {Nø¬ÒÀ\yOOÇ¢…ÍñÆ«íu ,)õn„Ç)­læ\†K+7"á¾î˜0åaŸ·ÁÆ‹‘™†Ó?lBð˜Q˜4mlQ»Ò8¼9s?Î 0DÄålŒýöIŒl탎ílzø}Z¼ö¢H༙ºŒkG [ÿVOhÂÏ"ôXÆÚšÝ畞UB^´œ<þjsxx: ׿ k—üŒø¨L4õ1‚±µ†Í~#ÛùâlP)Îôÿy”~ðVR¦K“¶èÚy6.?þ]|pnÃÄ87D—®Xù» =ò!|ý^äÑcÜD¶O+[ x›&âå){PDµ×YØ™Ø@IDAT¦í0cC ˜ZÒÙ3Ç$s+ RKÈ?mU‹Ò’|ddÓ³Ÿ‘̃bK×öô‚get…¬¹µ,4²©ªQÂÚ­¼µ¿ôÞ1ð ~® újŠÖ{ð¾c¼º]•Àî-ç΋„Dš¸j¢»]åÜÙ| ¸…8sÆ*l;‡&¿ S «+”;KÉÍ”ÆCOß¿+u¾ør8Ôêº=-ˆt×ÚÆ ÐaÖ=¨¯ª²2{cOjìf¡ÈÀÁ¸Hò÷¡jë@ôê‚ßè]od?{ì=ÆO=G3êÁJô¾W”™€ÝKÖã|ôRXÛÛr[>6]}莠X [G/˜²ô<Â! 3Û“­‚Û¬øTdsøÔ­ïdwCÅŸ… ‡Ôª2˪V$¼8ºd¶ï¦JCÕ§Ì…åR =F¶÷aÒÈB³ŠñYôÍì¬aibD9qÌM,àìîBz /ªüP¢0ßA!>”s’öb˜{8A{™Tc36r„§·9¡\¬½ÝaíîHðÇÊ\3Pýй„ÜßƳga%ìǶ£(„nzéî9b§– RfÔ‰½XþÇyäÓ £i¸ Ð˜¢“fC–éácAZr©r † Ý_UõòòńƔîŠtÀöO¶ övؼàštoO””Ûø.¡]n+K7ØÛ°&9ðl «#äÕK ai­fNÒÐ’cE[I1ÇÐÀôbM•-Åo_V*ïy¤·änõÖ‘´÷B¨Û#Û½Ðzkšù‘y©ì¯wŒAn ÒÉîX±5X ®®V”¸ Iç°q´Òú–¨˜£µeëfŽëÝ×Qx½ç׺­{’ϵî—ßÛ»ü{X”d¢sç FmÕ#Ku)Hýþ)ØÕ5ÚÝà! ‹/¥47'5‡“@ŽíGÙ«Ž®¿k;5š íŃÖaý"âãí0qP0U¹rÔ<†põlÜYŠ™[ÞDSlüð|·E|~IÞü[RÁ;¹c R¡0=Ë_ù¡”Šê(»¸cÒìçáo]”X†ìx”)’`ÆT€nQGµÍ#ÑxÇÞ*z@”kxnûülLvÅ”9O!ÈU ke0Ê|øä ™Bͳ‰:… ]^R”ªÉI4ÑÅE1Áh)‹š™ìªXºâÎ,‚ÊÅ ™‰iÔPȼð–iƒÈ¤í‡ ‘³-/\‡3'. Ã¸ pQ#‚HRt¡ ¢Ná»™;Ñá…0nBKEíÆùÞó4µt’ J[JŽB»–¿å\㋚ÚhÒ³5læìÇêyškˆþÃ;ÁŒ‡ìÁ¹dKÄ\X˜ƒ|\T™ª‘z)A9À¦p_xÁCŽ0‘ÌÉ&*SV'¢~Aù®Ðmd+ÛD„Å’¥ `TVˆ„ Q(ñó…½)ämÅqCI#Œ•^»ƒðÖîöÑSw§9“#ã%·ç*©5È #y¡¯¼Ô¼–]‚r ¦üe¿—÷eôPB¥¸•óÐÅ•8•ó(-~Uâ2'7%TŽ«ËCH¹fy2‚i“)uøäµî&m•ø)GuÓ?¥Mø#ÍP™oÊ ®«³<¼]ít¾ OËÛ¯Dl²^Ey(È·äÜ"@@ÒI$R¢Líp¥ù”j”Ó©Z-oêú¡žr¾•)ýBh'.ùu3eÈ)u‘dÉV>·´M¸…­ùªñJ2¬Ò÷þ ¿™§Žv…5Ñ6RñkÐ&Õ©òI…nùb$(©ïèDj3xkJ±wÑrô}ÀÙû×bX"Z´“:Sj§Ø¸­Ú*É–†W³Õ_‚3ÿ„ÏCcPÏÕœ*Y²,€†Wª}Ø#òèAüùÛ1˜´B2M±‰„\+!Ôæ*¨„ØØÎ cNAAeÀ+dªaM•†˜3áH£Õ€ØØld¦ÇãÜ¡S°vq„»—Á–€iX9;‚¸¸Z`)΄W«6˜òj_v]êÍwM7¼)ÀW7>óY ¥©00Ep÷ ¬þi <Œ@Çl¬ûvìú€Ÿ]k›¦cÙW[Pojìš¿‘3ÐVNß]7 6xà±¼öÂ0°rÀ›}Dï•zû䣼ï ?Õf°õ°…º4¿}µééé´‘Š<ëršË›ŠÑÙvJUnPb1lê5D‹fXúæBØ…´E§ÎδGÀO ä½s²¶aý’£x¨§#~ÿnË$T&¨O‹:‰ß¾=‰ž¯ ›IΟˆGaê%dÓnpô‰0œ4Í‚g=w4Ù ³¾ú»:zÃ¥0¿Ï?„VÏO¡6o!ÂOFТDÒÒs !>yÌ.põâ.–np»n îî=ཻü×—^Û8ðÚ2 *ø~,`oÅ–Ò†pê•Rž`F·Ae²ãËŽK)üÍ ¸ÛŽ–Ú‘*‚qÓó(Öà@àÄYƒiò(}9ŸÈÑŒ#š+‡sæ+!†å$SªÂ¾à>Ë£ä$4–v9òQW ~ŽÚ¸I@,?²ßÅÁÔµTD†¨;'´I9Ê-Ÿgä¤MFP7Òæ$´1›KI|FÚxZÁ¬‡l]Ë‘jÉCtÕ$žÔEB\Hs²U)´Y6Nl8­˜õá©-m‹äL-m’NÒ;0)0,^K›@ v&íÌKøͼeûÍExÁÄ$mi¤MxQq¹½§ð-œ÷iFöÔ·äĨ„ò!Žá} y¬ãÛy–ÇCbô“«¥MÊÏyÒž$½ðMÊ»À¸Ô¯åI-¶y,¼Èdù‘ä›Ð&¼ Þšä^é¾I\æŸÏ´I›´·ÇáC A”á2Ë3ÔÀPÚ]N¼³Mm"ÎB•—‹B3 d»*z&©°ˆ»¤Ð“íìƒ[{ò˜äG‡AKÞë@Û±ÐÒÙ.¾ä!»aJ"Ì’Ø,/Ë#…–0(*†uTŒòsQlfŽ,· ”’—Æ)°ˆP@±”‘Ï2XKšœ,› ÛÍ8ÐjÖÅàÄeÒξë@~{”ó;šõ>J_ðc=…ßÂgi›BöWêŸ|â(CÛWÚÆ—õ±f>Ò§¯¼C”K›IÛ¿…‡lxÈ;dżˆà=åb›H¾ÒFùä%AÒO•¾À¼%ݬdòJúïY±-È „mÒo¥Ítm›ÎŸ$γÒ;DÝqd—÷é{B[V9mä•Ò§¥?Hˆb_H'-ò.Ý•@¸ªvÀ˜·‡á£7–aZÿÿÁ‚ºÈÁ-àîÇ~À-k×ú>°´ ï®”h¬ÝѵWkDgŸA߇ZÂŒ¢˜6c¼aådF~Qoñ<¼7ðuX:٢ݘþ8»æ æÏø‹€Ó^ÜZç2‰%¤zyÁ…c¥,欄×W‡2nÚïü`1¶_N§)/ꀜÁ·/†¢ÑØá˜øx¦Ôv¡MºÃUÁÈíÆ àm‘ó«Ê`bi Ï@WnÇKj¬¼©rÁCmßPc‚@µ‰Ÿšˆ£ÇgaîSïò5§”×¾>&O½nÁ¶1±?~Z²/ï]Ãm3´l[ëëóQHHÐý]q_ÐätBÓú–T¢ Ê‚½<‚ò`îÈÃe¾Øôñ—Ø÷<Û5ÆCC½³çÂù™ÞðiB:ÙE%•©Ü»ÁX6X®¸¸00Gïñq8tZë'¾%…*¶;ŠíáÕó pë>ù{¿·€õ‚›;s·ÉzÌñØ·òZ?;Žia˜ûúbZ£à±´ kþù7ûÍ>þ?ÜÿÄXœ=ö~¢¾tû…}§¾xøá¶0ȉÁŸ3ç"":ö–È=¹ _<¿F<Œ±“KŸ¨½Â™õá¿Äùóæã™gžÂŸ=Ž=š°êœ¼ôà°ûäB`ñAàÏ'žäM2'Èqs BãÆîÀoO\r Ýp˜´H˵·Æv àDøÌ`óY-°ýõqN–|ÅQ‰¯ÚäNÀ‹}˜Ž¿_ÿø…e °;ñ˜ûÎÖ¬ŽÀ¼ ŒËAwÎà“M¤qæ`æ×š“4'úQ?§ }8ÙozžU Û|xj±¶¼—z²NÝ8¡?Á{ÛBµàqÅÓ›4gIÛÐo .ÙƶÞêÇò8áO_ ü¸‹àŽ£éüG€ûHKËëñ™¤·#­?’f Ëûiã¯a:Ò6­/0†uäЇg1òM€Ï²§>hV^þC F&w¦“ÞŒËArÒBòôŒÜlzŽ`Ó‘|‹ÆÿD°• >ʸ†À—äÅ[XËûu"Ђ´¥e°ßh!-¼µü´0–î¦,cº2àS¦ïߊ¿Y×a¬óáH-m‹œÙNÛO‘ŽŸµ´=ÒxM&X†ç+ØÞ"õÿ…å5`þçÉ·ñó¸( ÀëÕøz4#’Ws·ï‘ÇÏÛšbI†^o|ù,/žGR£Ø1õ'X[Á{ÛJ´ÿäV¿ ‡žšŽóýFȧ§;½?.´V¡Û ntû^Øóâ×(!°«¿ôk4›÷!›ÉÛÞZ€Ø`š–ŠnÓFÂæRRƒcÛ›ó‘giŸmËÑî3ò“ þàÿÍÄù¾Ã Š®o€Óñ}ÈqöÄ–wbæÌgÐ v?~VÑ'íz*fùÞ"µ'ÿاg»ñp!æ? 4#¿#ØçÆÎÕ.tD b.ïKýÙE¾-WhÃW£È¾CôX…q?±=™¦±‡6kK`+ùýÛMƒ7Øoa¿ƒ©“³/—¿C !Èvc?b_4‡mC°3¸)ðñ&b¿y‡m»€m,‡Mg:5b›ðÂöIZù’¦'—ýæ[¾?±ïðRÞ¡Aì ™ÙÀ£ó£Ì_úér¾æÀ.öÅGæ)mƒç{OóSBÚ^Yªô…m&&x0:“>zC&$È)`¦w.ˆ³‹ô˜ª#äÂÖÛjÚŸ%ÿ­, ‘Ÿ Ú ¾²QR‰,XÒd–ÆÅ\ð¤§ÂÆÙ†ëKª”Ño|Ô6V\7#-6–&É aélw§ÿoï:£*¶ö—Ms„B/Ò”.ŠˆŠˆ`AÅÞPßCQyÖχ¨(O± ö®4AP)Ò[¨IH¯¤·Íîý¿s7›ÿ 3°Ù[æÎœùföÞoÎ=sŽ+²R2PiÃ…QN´äµ³ƒ‡— ¡Wb0’h{qßÒ™õ*¬·Y’{‚䪚ÃV^ž“ÚrmÏqàIr)ûgžheZZ„3*ĽaQ6ûÞÙî2ã"¬‚¬R¸øyÁ™ŠñB~8‹s}bhëêæÀ;‚„Á0ñx u4GjÈIV9•®ºîÂÄß  ›úIŽØ92Hë+ΦÏ]G7xsò+WÓ»¥ ¼ïýž^œÓ‹Ë³¤Riaä3%46.vtYFÕaf>—Ô×[™8¸p̸S "2þU‰ªœÈ9‡†?ЍÀh|¿úû?¬Úîs¨ {†]øPŽòc«ùp¥ n ¹mg ÒB8åx;>ïe^ÙíÊüòC Ø•|À‹¦Un:ºYo\¢ý•¼¢9ìiÉ+ù‡v´œ V É ”+îÛñ~Ũ®5”÷j Ü7„D‚u´¶sàC|Ro‹lúæF6yp[ë“ë¤.ÞÐ0–D¸¸H=BD{×` ÉëÆc’˜_ȶä·<¢•øÄ4‘Œ)‚uèX° ¹NÇ‚uv’WÒõÄ-‡¸Éjx¹VŽ‹vïžÁ¼¡s»w[ãõRßÄM´À’OÇçE¶ÛY´ÄqÒfIÌ+Þ;Äò@Ð5ÄÌËÅ*˜ÒŸäŠ&Ô›åPn);ŽíÜx™®×eãÎÕ=€QÙ\ –!“‘¯õÑ=ÌrŒ1’„6‡dœù¤/%¯<”$*_ ±Ð5ˆ’‘Ç{ð:‘M¬«÷À&¿FGWÚíÝG( ¥†Ž®Ý)Zµ?{¯»‡ÚW3ò£ãé,žEðÁ—4x½…í-­Œ=±QÙÿÒ~Ñòâ1é}œ’‘²’ý¸¼9¸¿ yó¡ãͼ¼ c8:²¿e"¨{R úoˆå áíÄ>ÕeãuÇXò‰l¢á•ãÒŸl‡þ6CÆ‚Þù<Þ3Ü2¤’Rë“71×ë2اº*æ•·ÒfAŸ¶–2d‚5²3ÛË1,o[þl)šgÿêdb;½BCá*zVÑŠrõ<¿)9ü9YLݲMÖŸ5HBj‘‡t’";QûÎrWP“NªHgC×`"ƒ>qtFh°v.[†eßá‹‘°.‰)IèðKqÝÄΰ±sB@¸‹ŽŸ®ç½ÂÝßÚfΈŠå·ô 5àô[l©Ça–‘£ð“IJ]’EfÉk#šñN1µí¢•0;Í>Á¼O×Kk°®w¼5n* okì•s,“Òðž `!ò”öa½5ËZ~Ö²/Ç%YóɶÜ`¬7{¹VÎI^9.ßr­”!©%y¥ ËmÄ’_®;Y}ÍÉvºõYe“6ˆ|’¬ííæ°¨Ÿ÷td;YÞæê“vüÍÉÖ’~ª[sýT¿}VÙ÷é©d3`úôEX¶¦3Þ\·€× y5ˆù“˜˜¥& »j#ÄÉÌnjÊæk °í£¹H3wŸIWrðÑÈóV3oËuD‰åݼ_-ziÉÇGm÷mÓÇés¬SÖÁ*mùæaáý—ÁU;Œ5…“¼%ÐÇAã± òÕÇêTí—–H^ëï¢>ÞÖßÐÙ§'“MŽ[ë;ÙØ;]Ù°î·\qé|Üùâ¸úöñ¹†WÐ=YÓ‚I±i]RÓ,„Ã9, ý‡ÄpNxó¦W] G¨C4bÓ÷Û‘ÏàM’Á 1ƒû 6œA0ôû|“§}@¼`¤ìÞÝÛ³š^ËŸ’gÇÎè×O&£2®/ì¤4¼vÿ«ÖŸäm}H[ ´>0­ûòÝ\>9.y+Q}IrsÕZãt²¼§S_syO·¾–Ê&ò7Wßɰ8¼ç ‹Ó‘ítq;UûHk¬hTm™ÕJh­£@öÉ9,s©Zø¿õWÑb/Mzk0ðeyÖ>J§åz!¼–rd]Ym¢æÑV+EÎÑ}pp q݃Šâ2½¸Ú:j3[6 b^S[†TÎrå«’‚ÒeRÃê¹ê©°¥cïtóžªoXmƒÔ\ÞSÕÇüò U¥šÁC™DrŸ¶1ö&j£›M&ÝÃäkšêÊizîlù+ê8²R‹MWbÆ çïNdnœD{ZÍÍ!iÍßܹÆåÔí‹yAX—žˆì"¿Á¦Ijlè¢~žSázªsõËh¼}ªvœi™ëøëö­ª¿®FU“B@! PX Y5ØõÕÕ²Ššï*Ix qd*":Ær¡;:_4W¥“7Wé+ä \õ.¯°k u•æ„ncŸC¿ F|öíHÕ smµñwG šz] Ñæ‹ÙŠfg¦½íé’r–!óý Ãéµ–Á+ºgþÓ'‚toUßãGË ø sQNb*3L£ntšUsBiï'gÀ°„DŸnÏÉlÙj¥1\tý$ßdÂm¦ ®eV[ÿ¬l‹]ÊZë&Ær¬®¯,oœêìsy†ù ¼ïh4¥Ð8S®;׸ìÖµßü4¢uɨ¤Q(?„¡Tfcçʱqé;øõ‡ï¹v®‚V¿Œ/Þzë¾ý )\%¯K Cz`Í{ø~ñ\lÛ¸[7‡h=2¸ºÒ¢FñRÀ‡˜Jw¤…–pMßÊ5ømÃQ˜¹ jã’ïšÃW§5X ÇÎ~H8r‚g4¸|ñâk=WS.m§Lå¥HOÊä=ë›^GÎ\QpGwÄþG¹ °²®¯èý$/9{¶$àð4TTœ>=oI«ÎEž3˜œ 1T™ …€BàBB€+¼éKuëÿžÂòíU¸âº¡ØüÑlT;ùÃ19 %i¹ô‹ZA.©NžHøÇbcàïXŒå¯ÍFPç/èY‹ëÊ­f„ŽëÒ,ï³/$/à¶Šé‹ ]FÙ2PĦWÞþ+еs|þÐk¸´cO®­smVWhC’# ¨Ìº ‹HjÌqfc.ÊÇ_GèìPÄÅèyDé'Ú<ññlÉÏw r½p"’"}ÝZÎÉà“$$«iúœ°r^þ× Tpqœ Ý=z´ïˆûæßƒ¨`GÊi½^®m¦ z(¢û9g//Ýc"e´\EºW+eå¶(’ kͧäâ1“äg›Ä;A­fÓr­Aã[–ê|üÜbÄÝŽÞ±ÞtªÖ”ÔÉ[[½.ùÙIÙ,™ØØó_òw+ñî{eè>±œ©i-« Rè6úz~â(²Q$ݟ׋†ÞZ–^`ã?,_Úc®ç‚à«Âï_}‡]½qñE¡”–e³züºœ#Xøøûx”‹]ÛÒm™ÜSäzJ¢´#øhæ›ØÈ{ÐoÍÄà~Aú»8)óï]€¤ zÕ`G;¶‰Æ ïFû{üôÖ;xïÍ­\–"žŒ¾äR<üŸ ðv:¥ä[òÿ²¯ïÿ ìªR…€Bà‚F@rÙØ¶gü‚ $' a½' (8#°#c?Oœ¦»‘M9V¯öW㲩wùäŽm½ e¥|`Ñ‘€J"<ôr’zð(ÒèÞ*€þWm=áèèHÇú¤u¤Wpôt¢£ Ydƒ²¹H<Šâ#<Û´A»˜ º/Ó“xœ3wJó‘JŸËn³íbƒaG3 >]…7¬Ë!ÌXŒÂ˜¡xbþ$ü„'Æ/ÆÚKG"ê–î´J–ÃÈøê“_)Kˆh –?÷:½ ŒÁõ“ºˆ³€Zb(´®,/ 9ܪt( n!mÝÖ)Ž"¯Ðˆ¸(ÑS…+ËÍÂá½Çi†mOìÂÚÆ—¡‡‰}XÚ~'ˆMÛ[8º9ÓÁˆ,m.Ñ ²‰ûrà×! žº§ìL ªôÕíìV­Ë.>¼R$!“> mÝÙ­¼ÙGU%'A¶>AHÙ“ÊÈŽˆìÅsµm«_³´³4'é9Õˆ ›0ñ NÀr’Ž¢ÌÆ£ÿqLn\Ë««Šóqhë1TÒ9Ø…nÃØ{šªT–àHB>‚ã#ànSŒ÷§½ˆ%v\äXˆjàEÿkÃñ°êÍÅø5Á„g>ÁÕÉø÷u/`Éâ‹ðøO¼õÂ7ðy-™=‡¾øÏþë3ü<~0&õcö]}ù[ö"¼­¡” …À…‡ÕdíêâÆLǨ¡atoFY™†üU|0•3æ Ý9ÉcÎ@×Ï^4­¤iƒ©Š¤†(qO§Ò‰€¼$ßûÕ—ø÷Ka ïW7o'äÈ@ô„†4³)8 .BŠ»çé.¹O. ¬P’²3Ç/Â5sn£W–N»UùI˜Ë¿±ÿh½Ñi¨äØñø½¸ùúømÎÿ°lk>¼|p‚~¨ *qÕ¼G0ùg,zä-$Øé …øšÍÖÖS_™Œ+ç= ÿ®M—oæ¶Cü#„É’/‘[ôFì]·Ej`'d®^2pÌËBÁ/~ÚÚ™˜<‘®ë(—$ù}¤ýºOÎZ ú¶-ËÌGÁ £ƒq,¹yðèÙ$oŽ)›1÷æEHɧ`ÖaªvÂUÏ>ˆ«Æ„aé¬×ðÞ×{áêí..|ql_ºénêê R³)u–ç¤ãí‰Ï ãÜ'pãx Á[Ž/IÊ·¥DáÚ‹-Ú[[j7Óïý‚—ƒ}šW3e™ùÎ#p8´ÿ½îm8Fz£´@|ðÁ{ØxÌ}õjúUnª-•IHö–ŸðøÃ;ðøò§è~Ü'ÒñÞ´'a;j Üw‰Œ¸[ðÔÃQx÷†ç±î@>Ü}HòM¥ŒŠF]/ý'gîþÏN^†ëç žÞõúÞx& rì¾óuM³ôƒâý_ýŽÁ÷?¾Ý#9fB1þö~x壭(¾k îþß  Ñô3î{õEðãWÆ…rßúSÃQÕúåU* ¿¶ >Ñ.ßÏ{ë¾ùk_û/6­ßƒ‹ JîDÒ®-8žp9™ z Ù|—æfЉ~>òh Ù¼æ‰JaòQéoˆ€lªrññ³ßÀ.ª^Ýôž|q,ƒHðUù*×8a"­ì:¶²÷ÅžÍiÔ)ÚaïëPÄ÷êíéOÛÀð´ŒKüätÚfºâ¦E³ñæÆÿ`äEîøô?QJ¢gªÌBzf5®~áI¼þÓ¿ÐÆœ…Ÿmcð gøÑ×nUø0¨CûùÁ=Ü Ýèí`Ú1ü¬±ªëÿ÷)vT¸ #ý6hÐ ISy~r3óÏ1^ÿ“›žûvñ¸á‘øíEøbåQ¶¤N_gÒŒH;Tˆ!÷߉«žD0ß„ìK1àžO^Âì¹—#yûA$ÊdžW–`wVf.7×?‡‹¢ ðöKkz`/¾üx-:Þzm\€IS賜¶­f±Ýh& õ Eû~X³p3*iVP–“†˶¡í¥ÝàAf%Ff­ IÓÐ}ò¼´ñu<½`¯ØŠÍ[³à@o(¹I)pë<ó^€;쇣ï~T†rÛ8É/:¨[wäÀƉÌa‡Ì¿cÝ& =.Š`ð‹" 4#yÕ*¬Øˆ«_y‹~š‹~Ý9Á(­ÔCN{…Åbì}£èF¹+ú^?€.®Y—øˆ¯I‰{Y©;¨ùÏÁ4j æ`ا%£˜&WG¢«sÚüfbÙË_¢Â>]:yqlµþ{N݈±¶V}+ …À¹E€„´Jsư[^‚“Ûìýq9‚c‡ O÷Îð¶ @ß¡›ppÓt8Žáè9ÐE™i|ˆV"a(J…ÉÖPF>·äAìäê[F=jýÏŸ†ò«½?D@^Ked³×2¬¬ #¦õî‡þ±È­GZNVP^¿¾1 æ¬ÿqFõÆ÷+÷"bÐeˆtâ‚G:¾ª4Á·SgŒ¹£nö`DJbi (ž¯r@û¡ýÑ«W(C”¡OœvÑNÕà€kcDÅFIlTŽFŽ\¾Ñ(/Áw¯¼·æ­ÃUÏ=ŒKê”Jè®D¯ð6ˆ°c€’fˆ¦ H8Ù¸Àé/(/¿¸õ¸Ù×ñèwqxxÑ52‘#!†¤­¢:®^›hoZŽÄ­é¸øö‰ˆcà "‡ASGâëvãèQ†›0zãŠz‘!ˆûó}Ä‚µ ®×ÔvWšGë_šU0äò¸AXv×wØ•r=üömÆ¡²HÜyU*?ZeAÁÆ—B¶z@ÉJœì}påýhSˆÏŸ| ‹ß=†^y×1£¶»VÝ¢Øgî>Œ»3äÊÉ@£Ý¼#‡±uc &Ì™‰&wæuͰd”Eavúj.éÒ8B¡/^c.ùéo=ØWNéÐÒ{¤ÿ\ §U‰ürÒ‘ …Ò ™eÈ`ÚD×ôrY¬;B£÷‚ŸÛJlúb‚ß„öc":Ð{t¨ çÄôÕg#Å6½‡uC £™t §3/é/’_ÚÎêHÖ¨èO>¤(™Á}†À×_|‹í{/Áú_KÑgÚPø{Š6Ù"™Ø`Û8è÷ Á–`ƒWñ`iOm¤e"I¾j­£jލùõò¨bÉ2æ=² 9dzQ8³ìÊÌ$¼}û‹X“hÀ­KžÂUÃ#t­ø—þ¡À5#½[P·"¼-IeQ(ç ªëxï6K0ˆš$[B"¬7r³5[òÕ<¬È·0>´;ôÕÖ/¯~6µýgÐàååÄÉ6ÈHÊÒýŸÚè‹›¬ÔíÏ–ªëIAĆ›á£m9 ãØŽ0Àoû eµP¬GÍ•$C©ýÐÁy~–¼XÀðÓ=qqï@šÈærБü™«‘¸ç( ñC0káÃð¢ÉÂËÛ~ ¯h¥jjùdè5°Ÿ`}üo í¯mh‹+…T×ÔÌãv ŸkGO?½´o½Š>œË†DÒžµ &ÚȈÐL#©ÕàۯLjZ]S¿ä|>¼ÿDÿn¸½÷铺.ƒe«>ü”LŸ|’õWf%cwº î\2“û·ÅÖÕoa5¨B$u¢)¿Ùz ±þÞ­u4þ-jäžj» Ÿ<þ!òþø–É]¸à.¿ÖK‹m…‹èŠìxR ÂCܱï‡Ý(/ ›:VX]YÊÅsÅð÷ç50VR×K¾e`c©Ñ.<¨‹6ÑŒF‡ö~øâñ…Ð:cï·k‘ϤTh²$)Q>M“ø5è3uð*lÚФⒶì!Fï Ž"…½§?—|UbÍ[Ÿ x»+6¬ø^N•øí£•h3>DÇQ´®R¾ÆØHÐÚV4­O"]ÜÃqù%A˜3+b¯¿ñm©]g‹éÛ¬¼’Zç‘ýá÷ôOxwÆÿp´¿7V.ÜÈŠž\h@æž xlò <´n.Ú’ñÞ¼QV‹¦ÞØî¥°¶ÿ7™ZsÅbjrÉ%±t+•‹Åó–bÿŽCð ñ¥û'>€-~ju?·Í¥?Ó6Ò(ÄЇ³‰šØTõë‡Ë'ôAl¿ˆàŠwoDìH¿¨‘ºDò©úÑÙ Nõ…‡>á"øxѧ*5¢¶ÎîhÛ¿ºôê„`ÏJ?œK{Þî¸köÕ¡ml¹É±}:#²g£}-4ÐÉ ¡´?mÅÅQ|–HõÿéŠWc%ìü‚ЮÐì‰mÌíáÈH)GHß)µ¥N\Ì)^šæ”×î€Wh[ÄõÓíXÁ>ëÕ!¡nº&ÕÕñ=¢ß{ +á2²4 ¾w ®›Ü .®îèDû_Ci6’“ÊÐsÂH Þ ±½; ˆ¾kõJáhí^Wß´ òd¤ÃáèÑ#ˆðÛè×¾-:öï¦Û׿¥q±i‘Æ?z.»4ŠîË ©EíŒvtiÖ¾_4\h’ÀÕ‚Î!þèxQ,\k^Õ×ï7}[â¹€Ø xù…âÒ›#ÔßY÷…ìèâ¶ÝÛ£}׎èÒ/ùIÉÈ.´ÇèéW {ÿ8tìºô†ÁÁt€‹©„nç²aïå‡î—vEP€ ]뺠m—vèУ#â{…!;!ev~¸vöÍÒ¿½jTÁ;"‘qœPØÂÙ•&"tiÌ~ ‹ôdË›öÎ)zµ§,“ ñmgâĉéôÄt,}s9<²våxíõ×пÿ”%¿ƒ?Ò¡·¨•é|Bà=ºI¹÷Þ»ðõ7·cèP®F•©J …ÀYDÀwÜþ6–­-Â#‹ÖÂÃÇ¯ÖÆî,VrÖŠzýËán>„5?Π»&–{¶\gMÔVTPÇj¼¿d3Þ|s=öìËDüÅ]Ð÷²>èÔ/Ž„0œä‹¯ãÅÕ—¼¿&ciˆjýÓi˜XÓŠ{2k }¢Å”¡(èk6Gç³ÞF¥òݵƒtEûÁŽÄIþ‰Íª•¨X"¬ÉžÔД¬×o\%Ér¥”FµÕÛ=ocad”ÕÕ]/óioŠ´Rº,Â’dm‡¥]–º-d¼¡õq³ ª_^Órbc.Eâî”0 Y,x;úñ‰ ý¬½Ž–´ÆZ}y¤?šKÒr‘Ú"—äânƒü”ãHI-©³­µ^̦¹†† <Ü“¹,ýS?‹ü–Òê×iíEYl&uÔÕÙ‹ºjdÒÒðœ”a¹^®¶–h½âlôcÃ2…Ü‹&×LÓf£9Ù¹\Ä{;6îÂÖ•Û`Kò;nÜ8Ìxxñ¯ä¶db¤Ò…†Ç– ¦ŒŒ"$'çp»ÎøÿBƒBµW!p.ÇJqq_Ûé4 U% VCŸ‹:ϼL.d¡ ¦ ÞŽËãjlªøÈSé°!i12ññmðÓO xñoXøÐïpóõDpd0:茮—tAhD Üý<àéáÉ Ô¼Ú’îÈ)=ÊWÇ}K°¢sò»¶†¬}ÛðÎsë’4éUÞø½úÃC³F1Hƒ…ÂÔo¥Ð%Kª±-¯ò$Ûuל$ƒ~Xʳ–}ª|gvNð¨_zýúu×ßnXWÃk„’¢—çcÅ›ŸÐ‹C©u}”å"!dn¸ô±»qE?-Ô¯]z°®´Sµ»ñ9qÑvxÃ:|´x/ oÃq!öþác¯Á}w÷Ñ©uÃÅ€¢îHãrëZZ¿ýõ·ërXØ|õki|MKö¶Ií­&â1¢¸œ¾sŠi›{»7ìÆ®uô¢±?6•6 Ç=ÓïÁµ×^Ë·ÓíZRaƒ}0èâA1b„QðL[¯ï™"w_—žžÎWpo"77ÿV‘Ùð}­¯/ƒ¸{èZÀÖ‹lë—L«`I–qPÁq…\¾–•O^A>ªLôJË9›·'Dþ$¿A‰áëj†Œõ ñÓ}Á:2<­=CÖ:‘T9ºÐVÒà{ºç²ëºïFd¸†Œ=zÖúûâlIøWcùW××bœê }z'ÚYëtLH-'TF¾)ªdxâ*F•«,—o‰Y¡GÕ+È¡éC© µœ†Lz8ÉIÍ¡6·€f t_DZíîâŽð¶áèÚ­+†ŠÎ;ëÁLÜÝÝ[,â©2*Â{*tÔ9…€B@! Pœ%JJJpèÐ!O>ŽŒÌ L8ˆ#GÒ´, é™ á[¡k¾ìhVââîWW8{ðÛÓUÿ8»9ÃY‚ÛÛÛ3\,?Ìk+n¡¬âƦgIpUŒB€ˆM´™~u -½l«ªèÊ̈*jÁ$¸åe(+a¤6z )-,Ó?å²_XJâËr£¶[¡MXDGG#66mÚ´Ñ?1±1ð÷÷?gX+Â{ΠU+ …€BàÔ£´”6ÔÈÏÏGbb"’I€32•¼¼<2l,Ér95eÕ|å]M“ñª ¯…Å.Wÿ'ßü4y~êêÕY…Ài! Úgy#$IÞ2H`m¹/DÖžæ2ŽÎœ Ñ£†‡§|ý|u mpp0Â#Âu»Û€€¾µ`WW¸¹¹Ò×±^ÉYü£ïYS¥P( s€[!Çò)//Ge%_óc42W³ é­*v.Pe^ðX ®Aˆ.Ívä-ƒ##ÒÉGH¬˜‰•ã­1)ÂÛ{EɤP( …€B@!pÖ°è¥ÏZqª …€B@! P( …@ëB@ÞÖÕJ…€B@! P( ³Œ€"¼gPUœB@! P( …@ëB@ÞÖÕJ…€B@! P( ³Œ€"¼gPUœB@! P( …@ëB@ÞÖÕJ…€B@! P( ³Œƒª¤P(þ.ˆóýS´…þ#O¶³¹«tGþNˆ/ÊS%“± &Ö"¾(OóT¥œ…sz@‹søÆ¥I»þ¨¯9×ûÕÄÍlc ;Ûs]•*_! ¸P„÷ìtÕd…ÀßÙiÈ:Q qŒÞ8i&3ÜüBêß²¸ìU%yHJË«\¥“D[{øÂÑ„NF̶~ù.6”øà¶)×À˱±§³ÏÐÆjØ qÖLÈN?Ž¥Â"ÛžîežVY„c)9𠃧«Ã $‘½l vŒ¤ÔÓ?¼¸¥Ì•XóîóïƒÛÇ ‚Ý9¬ª¥"©| …Àß xâïÕŸª5  ‹ï˜‚ù›÷ÁÎÖÆÊ TM°'9uä¾™ä±Ûôùxç®Á-ÂèÀò9¸vÆbœ]à@Â(‘¬ª+pö ÁõÎÂcûî¸`JO¼|<k—¾pÏUÕl¦êÉøð‹Ÿ0tÒ„¹¦Ö³,ÏÜ{ >ø¥wýë-Ü{m/N,ÕT$}ã_À½Ï½…/i¶îºƒUXõ釰‰á=BëŸÖ–¨Üÿ€ÁšòqKG_¬Ž}¿y®JsZ«Ì …À# n+Œ‘Ê¡PœØà¢¦â¡~Ùpt2àÀšÏðô[+qËÓó1"Æ U•Õí‰ò²RÂD­¥'Ã`::9“ 7%dUEùØ{( w=ñ†tõÆxñÙûw`ñ;óñÀÝw¡Cô ï䊊J†Ù´Eiq!l¼pÛÂ5¸Þh/wÊJ+`ë@Âm_GX+ËÊPͺ]œP]ER^^bÊd°wƒ§»3œ©u5W#y÷¼øÄóˆ6N”ÓÎÀ¼å¨(/AY¥™a<=(»ã××iok»‰ah³RRpäHž{á9 ð>z´qÓO›MØ›”„üÊ*}_3›Ø† ”–£Z³…;1qrv¦F¨,:Šç_þ:ME¿¾5ÇR'ÛËÊöð'Òš© ®à¤À“ƒÙˆòŠ–ÁÎÉn.. ?ê¨o“‘aq×–W1© \=àáh_KÌ«*Ëa¬6Ñe57±¨m«ÚP(-@@Þ€¤²(çŽD‡YSñÚgë1zôdŒï!ªV3rmÄ}7݆}iå°ãÝÏàŒ»g=‹ ýck‰–µ¥6´'…£†\6W÷ ´¾j†ö´GÜåÏa×±,tªÞ‚¾°í:Äcåw?àÊ©óÑ©t1ÞK ówij3C‡›ŸÇìëûY®7aþ-“±>tÞj*¾_ø$ÞX¾Õ¯F£݇LÄÓOÞ ÇÌõ˜þ¹8”“™Ó&bÒƒOaÚ€ üwæ?ñþ Ê®±5Þ˜òÀlÜ4¶ ¡Ujë· líоï”í]Gžÿ+_NM7‰=Í=lí`Єä›q|×j<ûÄ<$VÁ@ëc{×ÎxlÎl éì‚—§ß;"¹ôdüÒiÛqõÌ9˜6¢7Ì%™xé¾)ØXÒ ?xaäéø¾ü%î|i1~ŽYsßC1 ±YƒÔ@<ûï".ÄÛ>û^^µ‘$ø+·$áɧ_„½“½Ex¶ÅXY€EÏ?ˆo·•â¡^èxkÃÔ·B@! 8#šÜ&Ϩu‘B@! heTÊ"(¾M7VUê’ âΫ¯ÁW[R1òšqÛ¤+aL\I7݉¥'‘Þ„*j#ÍÔ˜ÊÇD ¦­ƒóÚÃÞÁ¥yÉXñù ÌyýÆwATœ·¯ÅÚŸ·ÂÙ-…é¿cáü%(4YŠ/LX‹9Ÿ­ ÕÉÛ>Çÿz¥‘¸é¶éÜÑ‹_xÏ.Þ W`ÄÇ„QKê„C‡£KˆæÏ¼Ï,ùúŽÃm·L‚Ga¸å&¬:Xجì&S5 !Ãq×íW`ý»OáÓõǨ“­K’ÞÊÂØ>m:>Û–‡KÇNÁÔkF"sÛ'˜>ý/sE‡î]ádoƒ°îƒpéЮ0Þ€w¿Ú¦—s"}þ·øG¬\¾›ˆ &üúË ¬ß–SúZ\só=HD¦Ü~'ÆèˆÍŸ¾ˆé³ ŒBä&íÁÇïŒ7VnETßÎhã 3m¬e’a¨*Æ’ÿÜç|Èã0¨=5Ë*) ?‹b¨¤P(þvìüìÍÇÝ[ûô·,½mG–?¯9Û;k³Þ]_ÛÖ]ËæiÔýj3?ÚQ{̺±ë£Ç4ç`mö›Ëµí›·j[·nÑV/]¬ìÑFsôì®­>X %®y]ó¶6öžw¬—i/NˆÒ|â¯Ô²+«µw˜ 99‡ißï/ÒÏöÉš£C¨öÍæ,-/m›¶èw´}I9ZqQ¡–½o•Ö#ÔYë?ÅRÖίæj6]´]ešfÊþUëdmÔ}¯kÅ%%ZIi©–¼n±俤Mž½¼¶îÚâ4íîZÛsµŠœÚ¨v¾Zà Û´#'ŒZEÒ·š»o;íõe‡´#?/ÔHßµ¼û›VZSîš97iph£}¸1SÓòvh}Ûh˜ ýÊŒáš[ÈõZ¶YÓ6/~@ó ÖÚùGj÷¼ô-#ò´»†Ehnú¯öÑÜÛ4G—Pí‡#¥5"™µEwÐàÒWÛœS¥­;Y3\´§ÿVsþ„vgOG-þªYÚû¯ÍÒ<==´›ŸþP+«®m‘ÚP( eÒðgg êz…€Bà¼@ 1!¯ë=ѧK|­¼mÚ#ÀÈÊ/©=V»A;[ÇŠL¼øÏ±ˆ ßÄß™hŒýBc0ûùYë‰#G«ààb‹†ÖØQˆîS7ÖPa´Å“Æà‘·¾ÇêŸ~Çð¨îøâ×_àÛmv€s1m[K’ñô7!5+•e'U~N–Û²h–5ÍŒê ;õ U¿|€i7®ý& º(•šcä݃jŒFs7sÍT G¿nxzî ¿n^xm^½‘æ l¤ö¸ÇÓ ˆÛ?~ø,Ž.s€Ù`¢ã[àl›‡ä´TG¨yo‚©Oï!p^¸ÛSб{ÙÏèÚïJ À~ü²ågdf8ã×-Y˜xWO_¿.þ½Ñµ‹~€Ý?xmÊË«©-7ÁÅ;õä1IÔîÚ;:cß·oàŽ¯rà=ìA¼4ó:Êa9­þ* ?‹@s÷È?[¦º^! P´:ĵ½Ï’,Ö½ØBiCs*O›Ê«U£Ê6½ð.ïEÌË•e m‹6÷ B€Å³¯”Û8™Œ€ÏË1¶“'V¯ý S{±mK Ƽt#|í5|öÊý˜öÜ*LºgfÜÚvÇðÈC3h6Ázjí_Y´øˆš:uí…Á½: ºº6t‘våDDÄôâ⯓'¹²×•ÓéUâüwÎÝý~8;ðÖ϶r’W÷é1:ù»pšF7hÃ0ÉÞ }.Š‚©ê^°´PRÇ^}²«W®ÂöäjD]s®ùo¾¾ÖjHpˆÃðnÑøi3“P×Xrèך­í²¥ûüêQt¨„O NìúŸüp+n×Ķº.¿ÚR(-G@Þ–c¥r*›ï04IDATçâFLþYSh»˜*>ÃŽ}1¦Ko)323 ·Ò¾~Tó6J|wÆ@Nˆëž=‚µîÖ•o="š`ý¨ü±õÇ”[Çá½G–bžó&$yôÅÔÑÝy¢›I‚½ûMÇËÿž O[²¶}ƒŠ*’]º@³AÖ_ˆbz=ð h‡H®Ls ë‡[¦M —½NäaÃoûÛ!²ºÍ*j„/ƒ½f<ý$~X3 OÎZ€ª2RQG[»EÂÕàˆ6=¯À­—·'&J2áÚGz™&)L~Yª©…õhÛñAÁøbÁ<˜+J1mx7D;äÀœð0æ-؉ÈýÑ!,‰>¡(?±ŽÁ¿ç F$íJ€­›7Üœlq‚25Žb¢¹°!`ü+qç£1ëþû¿òSô‹öi¾}R†J …€B …¨Ek-JeS(Î3HÔLt}e®Q#Æ ƒí<ðê“cÉÒuøeõçxôçQÕf&^ܾiãHÒlé̺è­iú8 ?­bt°P›L¬¯Údª¥Ú݆^‡x»}øà“1𚫯»³ƒ_Ûì^O¿X…Õ_-æÂµ'‘Un‹Ìý±+=×; ÌpK½‡ƒ˜2±/¾™÷žyã lÝ´OÝ+®œp –į̂­»vƒÚhå¯&µ&ÿ¸ËñÌ37áÐÞí(()]#¢Ûº\sÃs÷ß‚÷¾ù¿ýø-n˜8Žf OápnµN¬5ºÛðɬZ·f[?\?¸#ÒlFaeg ìì¯6½024¿oOB|Üp¸Ñ[¿á—"؃Çþ9k6mÃ×o?ƒ§þŒKn˜„ŽþtÇFo$®®é‘Q´Öºj nßóÞœÛôõ˜úÐ\¤ZZÛ¡¾ …À™ `;›éL.T×( ÖŒ@£®íM:ac®FT ýÂ: [($Ø„eŸŒ¯V®ƒSäExvî¿1¬kH“¦g%`Û±J\6æ D‡Šg†¦©$7»d¢÷¨«Ñ³­ÅÌ!içVd»Ç`Âèaô+k'wOç'£~¸óÖûÑ3F\lÙÂ?<GwnÀÊU+ðËö#4þ>L„ݰ¯8†Ä#u÷üöëϰ‰Œ¦_‹ÊŒ¬øâS|¾ô;¤;aìÍ3ðÀÔÁp¶F•°Šh*ÇÁ}[`ç×.ëV£!µA»¸N(Oß³sF^:;Æ W|;Øíøò“°tõzÀ'w<ð8®ê M²R“°ç¯ØuÈW_ ü¼Íض']ÇÜ„›/íB¯ö(/NCfyÆÝyzEzÂ#,1þø}= ýÇcíÖ#è6j2þóÔƒõtDfâA.²ÇeãÆ#܇þÌ4#öoÙ Sä ŒÒ íº!Ö·»~ûî1ƒÐ#ÊÏÚ2õ­P(ÎiíŒ`S)­ nPX\ Ww/~¨{™eª(DZ:‰™mào.:k.™Œå((ª€›<Ô Q?¯¹ºŠJàäæE÷]–:*€¢Ül€§§»¾8LòW–¡¤ÒOojMëJ(+ÈDfaƒ+x"4À›*ãJ.þÊáâ:/øz¹‘(g!—ç½ýÈBÎU!%)…².ž~ñ³ìº­[a1À…#Ãóºz©¢¬%åFxxx2‚œESy!ާeSƒk/¿øº“„Ö$Sy1R3óà@Ùƒ|<éS׈ÂüØ8{ÀÃÕQÏU©—Y o¶¯jåe"·€ íh6‡SgceJh&áFìõ ”·°€ò:éÁ7ôBi|"ÿÍ1\áéálG}+ 3B@Þ3‚M]¤P( …€B@!p¾ Po.~¾ˆ¬äT( …€B@! P´Ex[Ž•Ê©P( …€B@!p" ïyØiJd…€B@! P( –# o˱R9 …€B@! P(ÎCá=;M‰¬P( …€B@!Ðrám9V*§B@! P( …Àyˆ€"¼ça§)‘ …€B@! P(ZŽ€"¼-ÇJåT( …€B@! 8P„÷<ì4%²B@! P( …@ËP„·åX©œ …€B@! P(ç!Šðž‡¦DV( …€B@! h9Šð¶+•S! P( …€Bà 5 2 1 m ž@IDATxì]`Å=yé „¡…Þ{ï½ ¢X¾"XAÅ‚]A±`£ª4é5ôÞ{ B „B ½ï?w7’j€HòÞîìì̹wgÏܹsÇBc‚J …€B@! P( GÓ#ÔÕ…€B@! P( …€Ž€"¹J …€B@! P(9É}äDª¤P( …€B@! H®Ò…€B@! P( GEr9‘ª) …€B@! P(’«t@! P( …€Bà‘C@‘ÜGN¤ªA …€B@! P(Šä*P( …€B@! xä°zäZ¤¤P( |ŠÀÑ£Gñûï3±gϤ§gÀÂ"ŸVTUK!ðŸ!`ôïßµjÕâ3rÚñì?“¢º±B@! P<&ÄÆÆâý÷ßÇøñãùÒ6¡X±°²²†Útô1QÕÌ\! „Vááaˆ‰¹†`ôèÑ(T¨P®®¿5“²äÞŠˆú®P( ˆ@jj* D îïxæ™—1pà`¸ºàîÝBõ«§ŠRä34$&&`îÜiøúëY³fÁÁÁ!ÏõT–ÜÖâWW( …€B@!ðh" Hî£)WÕ*…€B@! Pä ™€„k2‡m2å“ð½æúäXÌSúLv®Z©2=Ž(’û8J]µY! P(ò?$r66Õ´ Ff`ì1ó±ôö6! —ÂBpìäYÄÆF#øØq\‰MÔ ï רè 8~ü4’¹Êž_o$9—G#&1õ¾ëÊ è ‚Ÿ¦æs7nþ/~à†x°Ö·ìÒtÙXSŒcÿbEry+J–9`˜ËËÿ‘lù¬:ÿHU¡ …€B@! x¸ ‘³B:vmXŒÑ<¾}š¢K—&xiÄPÌZ¼1)ÀýZ\­-Ó±tü‹hÔ¨"z z«7þ6MkaFÐ>XÚþ·pÙ±nËfŽ@ûnO!4.–ÙX®XÏ슛aÍ¡s°¼½[­­Ó4û;¼ùö0¬Ø!–’„Hg$_ÁÔ/^ż­Ç d÷ßL֬Dž{1qÜ;xö©VèÜ¥!ž|ö üøël„„Ç0ÚÀ¿Y›»ßKðºvù".FÅR1ïžÿßÊ‘ªòo5YÝG! P(ù!  'C—ðóì(X<UË!tù¯xy`s<7jâ3¹ÍbfÃ!>béë¯9™­BšäG¾Ûò¼“E2¶/™›20aìh4¯×?NžŽu* $ZÈ£ä•ë¤L)_þÊ=…ôÙ[ecæzÈy±êI^ÝòÈëÅ%B’¹~æ²²[%iíF;lì,‘‘ž†8aó²CœqùßiˆO€fa£×Ñ\žXõz³nÙæúf¿Ÿ^«ì"Ó~ûǾ‹ÓѬ¥®l„–ƒu³ÆaÝ¡`X°=’ÌmÌŽµ´×Œ‘|6'ÁÀÖ\ž¼tdâh•Y¦9¿ù¯Ü#xËttïÞï|2 ÖÞ¨^£&¬/ÁÃû¢ëÓCpèb,äz‘£Ž3Ë”:Éýtk*ÓeÀ²nÔ9SŽ2H¹š13ã$¸™e¦ëSf~ý¼ù>gÇÇ\Î?ù—UPI! P( ü‚€“‹{çá¾CÁò½1ù—ŸP¥¸»Nh⇿Œ_l‹©s¾Àš¾=л¦ÎÙ‰ek×!": G•ÍQ§R hto8¸v>¢]ýá•qk6íFŠÉ j´D‹Ú¥±iõ\ì?—×ò¶8~ E 'AJ# ©°$)‰:s+×®G­sEê¢J]HEófÍ€+ǰfë~ÔïŒ ÞÈ =²nN¤ù U㚈 Þ]§ÃQÔÍ [v@ͦO¢^u/ß¾›öîAÄÕò)ƒúM:Àß×Yßæ8.< –,Æù+‰(P —®ÆÁdõw´Èçöbê¤?q–¤¯_]tîÜa{×âHX,·ìŒÂÎ&Ö H¹U«WÁä×­«ù"-3f± l­ìáìàèøü§™˜ð^X¤ò"2H;¿L&/d-"x–êXÇ¡€±®Ùu cÓòù¸ªù¡M›š0±l!€çö`ý–C¨Óº7J¹;׬ÀîƒÁ¸–˜Oï²hÖ²Jv¾QÑ?!ãѧðÉ;oá|œ#¾ž±½[T¤%›!´Òb1çë'1bìtÌYó,4†IKÄÆ Xîq$YØÁ·DE´hÑÞî6;¸{Ï]Céâغ~¢SíX»-jû;aÕây8Cœ}J×@ûVÍáÊMNm±ë(éa…­;v"&Ý ¥Ë×CëÆu`“ÅKƒà\(õjÀDP£.ÆÚÍ'аE3\<ºóWï†uÔBÌöš6o ÛÈ“X¸n BÎ_‚sATªÚ j—ñ¹uÐ"mÿGcø©¤P( …À?€À¯¿þª999isæiW¯jÚÅ‹wÿ‰º¦iß<ÓFƒ•öÍüÓÚµhM ϼîòM Þ¬Íÿk»|6MÛ¹ôwÍ¿°›fã uêÒS+åh£Ù¹”Ò&­ Õ®F¥hƒÔ\¼¼5¿b…´†ÍÚje<­48×&/Þ£|¶›æLîäQ¤ŒV¿ã múo?ŠýO{óûmZxHˆÖµ¶/¿;iµ5×ü‹»i¾…]5ëbµÁš¶é·Ñš=¹ÊðÉ'´ëQš™®½Ñ¨ æ\e v༦ýñÁÍÑ’e;»hð(¬}=ý„¶qþÍÛÞZsv¯ªuîÐNsã½½KwÕÖJÖ"BCµÞ¥x?­I‡îZu_ÍÁÁJs,ÐP[w<]‹ÏÂíòeM[7ûKæµÑ jEÊ7ÐêÕ(Ïï¶Z‡§kS?|Ÿ­´9¢]%–‘ÄlÝŒ¯5òGíÙïvjW#³ÊŠŽNÐ^mV[+Wµ“6¨o}ÍÒ¾¨6qY¸MÌÏŸ8­u,­ï' µHÊnç²YZYo7ÍÊÓ_ÇÚÏÉV³s.¥M]±GëÓÌ_ƒKmCpºyIðHÕ>z"@ƒ}Emå‘mæÈ—5òeͧlM­mËÆ:vMžÕ¶OÓ.GdÕGî³ñ÷ ¡Z«!Óµ+1šv)³íá,÷BèUmñ̕ڎñ¼G¢öõ‹=4;+hek´ÑÚ7cýy]@§÷µc,sú›Yh)ˆMOFß¡ãPÍõ F1 —x]–°‰®i˜õÝ8•è‹i™X/\´­Îã£IÑ£cØ&œÄæ­'@Ã0R®ìÃ_KBP¿× ¨èiÂÁ+WP¯Ë,Zº.\iŸôÃñõK°ã`˜îN`®‘Üórø9ÚÒíж}[X&Ѳ› ×Âdï†zíZ¡”·bNm×ÓþDþ£±hþüúû:Lú`Ž/ú‹¶½½’R-PÀ÷˜·`fûqaˆ÷n‹å WaÎÜ%¨é”„‡N …˜ØR~Ô¯V¯Lfy+0ù*4-˜ ?ÍÂU–ã@]²§ƒÙ¶ns?náŽÁoF#wotxz4fMûGe¦áH,¾úcþœ;kÖï@÷’qøòçiˆNÔäæ&ÿ£ÉýGáU…+ …€B X¾¶övÖ°£¯#7M»)Ñ©ò}E£VÃN¨à‰´4 «ØÍÛׯµ½»pU÷èа: ²DÑ2ÕàW’d6‹¹X¶5+ã6ÐoÒJw‡cI&}:ìR,Ë ‡F°H±Bñêгa,äæ¹H‰¥CôíÞÅ<y[ÏÐ¥À'Û—þŠŸ'LÂÚÃôu%E>ºu5vìÙ{Ï´lØ ¶œš/P¸ š·m†”ñÈ)‰‚ž|¢' ;XÃÆ­ ÚÖ¯»ÔHDe”Ç‹=[`×Â8z! i×bÊ’ChÞ¯J´a¤‚[Êãý2è„ìT´Þñ*B7NÄ¿n€% »$ :—¦_¿€ G¯¢zƒNhPÞ éÄÚ·|}4ïX‘[·Ã¯Uº¦cõæÕˆ'ˆ‡ƒfâ`’ZÒuÀÑί¼3¯?ÑËû o½ý&m:F iÛ½µ2¼Ö‚ò·¡ßB'[ÜÊñ…è¦$3åv.x/âáŠ.ž€·3Íæ$µ[tƒŸ#°ëè–»EЦek8ÓáÖ»x âí„­;ÂÇÕ öîÅá_Ü„ 箲=T´dØ®Œîm:Àžù½JVG“ z—“3nòq¾ AÖÙ’þÆ €>ÒÔŸÇÏÀÊÑ WN®ÅäŸÇcÆâ5H¦y:rûZœŠæðKí_HÊ'÷_YÝB! P(¹E@£­ÔÎÖ —ƒ£p12%ŠÛ€œôF2Ydäš`¯¥‚àiçnDÐ-¾iÈ Ó£XÙäŸA†locI'ƒ,›6ÁŠ+ 4±¿ò¿™kÜL¤5¤Òº “;ì¸ KαDc“NJoTä–äŠY‰×X[ÛÁÎÁN¸Rc‘ÀO©'waA|8¹e ¾•¡BùÂHIØMK¢-ìY7¶IȯIV?Ý1 vp´eù™íH§õÕŠN Ö–¨Õåy߬=×Ëp>Õ¯6i'’CFË1¥°Éz EïEó0ëÇ7Эî4° :H©IÄÚ>v:ÖOïiDFH©CÁªèÕ¡ÞY„ žÀ’•ÛàêY ­”á c0þýN3{ÊV® oÄ\Šfó-ôEöÊÖV”Qopèøyt­PN·²šóÈ"°Ô´4Ø‘„¦&ÅSδ½;ÚCàä¥\,'rq¡ “V`+ž·Ï“é©ÌcK,÷ŸmcË…r-ÿZsTeGd‘ T4²_K“ lxãÌlFfù]àÄ@?Ä_§Ä2žž5KhA×'Vܺ"Ô/ɺSûn+L¿üÿR$÷Cª T( …À½#ža‹ºMkcìêI˜»l j¿Ò 4ºêV=±ç‰xï§yè;ä ’. \‹ ¥—”¼Æ’ÖÚô¤t¤iÉÙ¦¹³‘ ² :6Þµr&YŸ¸ }u¼%WS%‘6f!@,‡¼ÅÊÖZ·ðY“Å' ñÌJzaWüoçäÎŒ£j··ñý›O!#1…‹³xMB<]±â󈉉Ct|‰É- Oº~•VÔìÌ9«l2p~I@LR*ó“oñ«fŸNtÖ#%«5æ)?¬›þO¢H±FhZ«ˆnÍ^JöÏätHs.„‘ï}­{`ô÷ß’9›LÐÒÖžŒxq=ö´ŽµD`°” oIi” ­Ålzîƒá8uæ-™íö£f‡ßA;.í߉¹ó7 jÏq˜=æeö6ÿ<-^ÇzËÀ!+¥³~«Ó:†Å³¾Á3í&¢(Ý’IÌé‚«!;ðîK¯Á£Ç'èåëI—t\¾ÎbÖ$ßÔ“m*üü"%ß&oÊM’C “‘šÊöSS}Ù0i_ª°a^fâ 9Çÿ°H£ßAö$¤'­è&áÎEp®…kâûéAð•:‰^¦Æ#9Í ÎÎvº.g¿ôŸú,Z¢’B@! P(ùñ»­Ý}š–vÅ´á£q3qâL8®\ÃÊéßâÕ·aÛÙhx¢y…âØ³eVl yé^ƺ]N¿OÿfíàAfœ®“Ì›IÍM$W'AY ×…tN£û—ª ‡ô˜ùûL\§ÅpÛ¢I˜¹[¦Ø­uŠd¢¯CzR2N<€„´TY;¿ï §qÏL+²‘iò#›‚Ñ´\a¬ük¶¾NŇî[„gžn‡ñK °AKd\ Á¼ÅKp…ŒîÄž%ô#]Mkð¬¹rŸü9k ÎE§ 9<s‚¶#Í®JcÄO<Ñ®¢ŽMeÜß­hüäÿP„$ñÖé½åÙˆ XD‹TéŠ#žÄ®±öh ‘6…}ÑœáÙÖþ¥[ BM?}Ñ`¡Œq\¯M­÷ÿˆÄ(†õ l ¿« ñýO!Õj&^jYZ·ÖJR2œ0èÃIœò„I|†+§¢PAD_<‡X W¼üÙ éX…~³ixsø` ùlútZ 7Ûœ¼q—°qó^«\¥½ÝI4Sqxû*Ĺ”Cƒš¥ðûÐx¸ËÚ·?¸‘ v¨Ñ¸#êT.©·#-9 [‚S®áðò«Aãr8°;•ê7`…K'§²ÂIbáfOB~$éPʺ% ùN¾ƒ5[ÈÑ­ç$~kN){ùB¨Ì·’°h·LE˾ƒmYýÉÐb\pv‡{JÙ·a”yCiË­÷×ï{¬å²ìõÊ,Fÿ#Ço•Q:±—Ÿ¿KzÝH&ÿ.Ý)Ïmõ7Ë7[afùëä—¦è4ú ³VXé†lYÙ8UžÎ^e]_23Éý¸æ1+å?ëä¿óI‘Ügu…€B@! P<œ¤Yy`ø¨)èÑÿ$¢b¢*ƒ>¹•Q„ÛÅ&›e>l gøáQ±¦ý+§"¨ÀÅbÖ¬ï-ÆÏ|Xóÿ¶JB^[ šˆ€>t§ð¤kCv²úßVí¾î®Hî}Á§.V( …ÀÈZöBóèÔÄêÇYÅË”CINOK‹a~&¸RG±Û(Œ:õ –GÖY\Aæï“Xš /ï’†{‚à˜ßRÖ³”ûš)’›{¬TN…€B@! Pä ''{/nÞ@S™ø£>TI,ºw™*Ïí¹“Ë@~¬k~ªÓmî ù rò̤ҙW¡ÙÛËÎyKŠäæ /•û@àø) ü²á0_¾ §¶ 0Ú"ýµåŠ]NÙ¸¹0`y£¡¡a@èyc!D€P¸ 1Ê=Àp‘Œ[n惚•EQÜÀæÀqã:¿b@ñ"´ ð%q4„{‘GÒ!Ÿ¾bÕX.·{çje`çžçèÙ×(S¸îÄ.øgytà¯`ÔEfì:hXP¤nUÊÓeŽF¡sÓçŒëÊpô]¤--,o?Ûq=–1¹€£V•¬º>aÜOêUŠõ“‘º‹Œ2îW›yeË8î´÷°ñróf™Òn±Aâ½Î^0°,¸»ò~œÒھߨ[Ö-u–…"€“¡º!‚E1¶QÒžC¬[&nÕ*‹.óþ")«¨wGNru0q“òj¸IÝ{±&yyýr¥^§Îò3+Z•ø¸±n²Xbïè¡„\¸deÖYp„³Ì㔉´¿\iúzeìa›7‘ie–!ø]½NŒ‚‰ë&ø–-eä=ʺqqµ^ž”ëâÄû$\T­ë‡Ë ’Â.ÁlŸtÖËÒZânø3ŠœâæHêV…eÈŽÈ«À!ÞOêÀ{c?¤^1ê^'¹Ÿèฟí3×Mä$)'ýf8SìcÞÛôû<õ;ìoô›÷©Iìe‘Íõ›X\&¢ßòÜpó¥›ô[žyÎ$8“M¿‰…ÈIô{7õ;‰2Íþì‰LåÙ“tC¿)‘ÿ5ÊI¶»­SÕÀUêvC¿‹R¿} Ü‚OÓ?3ÂÀVÚ!ïÈìúí#2%n7é7Û*z|«~KÝä¸<›(Ó“gxÖíNú-XزŽ Wb }‚OA{­4G%…€B œ:Ì~0åËó¡Êcâ㬒BàñB້@×®@ï'€Œ¶ a|ú9ãøÈQYxüþ—q¬k7 h£q\ÈÅÐÆñÿ½BÂE!i׾̼,{¯“$ä³±Æñ~O“ð„뇦éu輦Çä÷Ï¿y{ö5Œ» xÊ8þÖH’Ay³2Í]’y?ÖmÑj㘅a™uøŒÔÍÈ,„º[/#ÿ/³¼RÎÇŸgÞ¯IÉŸ¤3$½úÇ¿þ1ë~¿Ì̼_¦ÃFÞÄ$âö?ãøÖ->‹EA™yÙ¾yK2+ÌK^Ý8þÜ @ôu£Œ;ž}xœí?Õ8&¿GŸî=I®IÄ$ ©ï?Ð(㓯A„Ÿ=ÏÈ+rݷ߸ŸÖ^3p~˜ÄÄKNÊq½q/){ÕZýþkĨ̺ 2ˆ°òÕ³·qüÇÉYyÇ~oëÅz —$¤Ì¬Cï}žÕæÅ‹5taÛ¤}[ØVIÑ17-Òu൷v(Ñ8¾y›qLÚ±h©qLäôî§Æñ§‰Û¹pãø1Þ·;e!y¿ùÖ8&¿¿h´Yô[G’ÂIBšõûýþk&q“ë¥n«7Ç…@¾ö†qüEÖQ²¤]{3ó2ÿìlúýÉWÆñ~”‹Y¿ÏgX¦`<†r2§Ÿ~5òödÝö“¬JŠ&yêiã¸è·y%~vý^’©ßBÒ_Ϧß))Î;÷óúLýþ-S¿eeþ·Ô_iŸèó¥Ëúít]êI}—ãÙõ{Ê ãX×ìúÍû=õ¬q\ô;!‹…«2ó²ŒyËŒrå÷ëÃŒã J X䨕xa»­0qÂWØÏ‘=*)wG@ž•àà3˜0ák4nÜw¿è–*„Ø-€¨¯>Ûø²+£XžÖ2,tB\Wo2¬~… ­8ˆÕH,¼b…«[ݰÚ‘\³…/¯(à ױ…am”üÚ­¼Žï]†¤D`YÃrºi7‰#É™X‘¤\î`I‹¡†EA ÄͱX¹Ä,IȨX%,O“º´2².I$ª‹×X !Á°$·jdXrÅr%dS¬ºr½X…T±bYsr:·Ôh!´Ð­‰¶ÖÒŠ†EY,„ëyìÜÃ"Ù©¥qÏhac;K—Ã\¿‡Xw'–@³ú€—a-]À¾ÔMpkÎãb- e[Hú%‰Å¶‚¿ñyÙZãå/Ö˶ÄBÂÜáý·#±¼”¦õ9 !¿Œ{¯—×¾¹FÜ,tËêšÍ†¥°¸/и–Q7±~î=dÜ£U#ÖYn ÊúK;®³=žî@‹†e!±m—A¦êÕȲ/_Ϻ7gÖMò:¿‹$F¶±n©ÌçG¨jÜc¯?}ÖÐ!É+ÖY!‡R7 BïãÃ2êy‡hض[£šX_ÊT¬Ú28b^!xR·– «ö¹‹Öm5âÖ©¦ÑŠn|}ë¡ÈTî':$V_x‰%Ô_äĶHÚJý!þÙõ[¬—+‰…Ô­åÖš:$IˆæÁ£†~×£~‹…_×oÖí Ëwu:43,¡¢ßk¨ßŒËª•8Ó©ß"'„ܪߋŠH¬ó¥Šó9«iÜïNú½„úÏ:Êv§­ˆ…è´XÐÅú¬ëwê7Û(Sç‚›®ß$Ñ­5ZV ý]–éÖJå k¾åíÔÁ“§©C|Ö;67d*ú-²ÖeÊ2ëfê·Xòä¤ß+ }‹´è½è÷q–¹‹ÄZR5bQ¡Œñ9»~Ës*Ví³ÁMŽà£wšÃÝÝ _~9µk×1.P¿ çþÈ‘C6ìE„„ŪU«P³ffG’ã9T$7g\ÔÑG±.Ê‹é­!ÆËçl¢j’B@!Oذa^|ñEœ:Š*Uj TÉ2ŒïÊ-re´©’Bà!B@\v„„Ê öA«¯ Îàh5,ìvíÚ OZ~øátîÜùžR$÷ž`S=Œ ÿ˜1. ´Šó~[ ê¬P<Ìœ??ýô6mÚ„K—ÂI2ææ¨º?†¹•Y™y‘5Bx4Ñ5q§wwÎxÔÆK/½D_Üò÷Œ4'^TR<ØrjÜÉÕ˜š}É0ƒ¹¿,×å?ÈŒŠä>H4UYù(®={Öˆ%››ŠNùm2üµ%Ë•`lX“¾»Qn®Sy<玟C8·‚ëÊþ¹}¹Êžzæè‰£ð-ãKù©PM^2ªÄÇ——Î^ÂÆ›Ð§O”*Uêžš¾2h%&OœŒ"eŠÀÑÙ›¨çòž€¼Ï‹Dž‘"´*Ý»wÏÓF »wKï S$÷>Å .W<8J•à6 µŒ rU*ûÞ’åJbÌÒOÎø¡%IÙ-Ÿß EÞ£z| -s÷¶\ÉΜ‰×Öj]#§Œ@ÿ©”Ÿ¸W=½×ëò3ù¯n66˜ðþdlžÃAî'ñ™täN+Ã~|µ+!EK¹ŸÒþãkͺ——~Õ|T=/×=ئÚZØbú׿cñ÷Kó\°ƒxpãÙ$)¿§|iÉMJJ¢E‹uåŠq8»Jäw@ócýôLjLÁ‰á8:uêWWÆÑz ÓàgùÉK ®cG8s*'ã?ìòRg#¯Ø--`‚%ÿq'(ý›hH#xßu¢P´Æ¸Zú±tý*)ñߥ&VÖlE2?ÜC²¶±†3·S$÷Àû.M54AàùYtSt.ÿŒo9éœqÆò “Ì×q—5^gÖ÷¡úÍ-lÀvÜ:îA$ŠÔÞ™!¢¸õa ÿ=\ItÍDͳÌìS }M§æÝYïòŸ¾Ú‚ÿìïÍåä¥g¾=€÷áë}¾$¹»víÆsÏ=‡îáh-{SªéŒûSNK¤Ë4îÓ:yòd 0àþÊ{ָ̮?¨$ãüð4^èmÒåóØð׆{‰„CaoÔêЕ*Ô‰ÀßµÄÖ¼:§¸WnDTJT €—§­ŽÂß]÷OœÓyGÜc?BC~Ê’{þ£—YPÓÒc¯`Ó_›pèà˜\ÝQµUÔ¬Sœ÷M½ãsgI2|þÀl_w˜®,q°uqC@ƒÊ¨Ý´, ˆÈü>”æmñÃY¸Þ>ÀwqV¿ú°=—’¥Æa÷¢Íعã4Ò¬Q{°×o@½»³¾ Ù:x/¶-ßðð8Øyz¢<õµFƒ²°1ýûúz?ò,Y”³œüyR¾$¹II‰úN0ýéÜÞ¤m;Ng¨Îê~”Éš$÷Ä¡ƒûú0ÄÆÆÞOQõµwÇNOt5ö°¨s—Ê[°»M¾p £Û~„ Ã—Q­U5„MœÙ_.À°¿>FÛFÅiï‹îíIlá»¶aÒg³±mÑ~Äf¸`è²ïѧmq$ñ*•K˜â.â§ÞŸà÷åÇP±y Ä\ŽY_ü…ç‰~}ªóV©·ÑUa!Kç`øãiYµëÁ‘õû—bƒîã>ÀkƒjñšœõûÁÕ]•ôø!@ÍˈÅìÁc0~Ò¯_V‘á˜õÙŸèûÃx~pSèÞÊX,i5=·a.†wý—Ó=Q¡Z„î[€©£¬ñÔ/ãÙ'krÈv»žçW|/E±ñ@)_cdzüZO©W¾$¹R1=Jq+·ÚõêòŪÒý –¶6°²²ú&›ï§æöÚ¥+€™3v-u’+ 3ÞúAG£1è÷oл[\?±w…qÏLDÀþQ‚«bs¢¤]µûöF¡J‹lÝ “–Ó”ñƒ•*íñCÀ†TtÙ˜I$¸Áèþå‡ôrC¤G·=ßÄøg@ÅzãPµ˜=‡VÙiƒL§#tÝ18”­‹/§¼†Zn¸²£»½‡åŸ/BïgkÁ‡“€9é÷ㇲjñƒBÀ†³þZ„‰$¸Už{£Æõ‚CZ$~}æL{å'ø× @«Z^Ô×ìÖiÝ䀃KÁT¬FO ÕÊÀ•½x·ñ›XðËvôêS ÎÖª–ÿ|9c‚ø> â®gÝþùûÝÏÄõ)ߦ´ÔTÝ O#; æÔ.,_ª;$äøBVx¦~Ü1N+;NþÒeC%…ÀuTÎÈàJœä¥nh¦ v«ÞšQ7…”«ç°tÞn¨Øý^n[ .>eÑkÔpJ8sH 2õÝ|øàŠ÷cýQCñãê·Q«’LÖV(R®,üÊBZr*Ò”ºfÁõú{}=5ô5{OjÖW9—=ÉñŒ¤«X÷{ÒÜ*â¹/zÀÝÞ6Î>èöÁ@xY_Æ_3öR3o¥UšÎ_„q«ßGÍJ…`kgÂe‹ÃÛ†54uÈ~¯üþ9&ˆ¤5÷aˆ9«4ò;¶ª~ {F sà£O7—{.⟹PË@rR*ÒÙc¤¦¦ 5ƒ»¤~OGrJÒ®âø‘hh© 8sì.ÒŸËž‹@â#/Ñ å¢bSõ®Ú\9¡ÃWOžE|L:ʶ©W qLÐ8`´D…zUáaœ^„¶^kæ¼=‰w˜‹·7ŠvArB]âc¸¥Çñ_ÖÔÓ4êiu4%%›ž&ÓÖšžˆ£û/Q_ÒqþÄi†ë»"K• '… âjÂM„UHCÌÅKˆC©¶õPˆSNb±•IÛ•+¡DIœ^y±¤"·¾¨D#íœàêâ ë°55|ãøÙXø×”h’‡ì¶´ÇQZªÍÔWð0Îyz:u•úš’œÌïìW“Å蓊àý¨Ëˆ8ŠÓ§"¨K60¥ÄâÔ᜿ÍïB@ÍÉIqѸ¸ë µ¬‡Ò6,eóŸg)T¨â³+öã*•ófzlÄPp*àww[Dœ?ã{`òKŸaë5t{º:­¸WˆÊÀ @ËæyˆTd†ð?ø›oÝþ,Ô-qêUä'%Ú©.aᇿ`ûþk°/TOŽhˆ£ VcÛ–3(W¿""îÂîݨZËŠ1_/røì‰JÍüqiÇ9ÄÅ\à|Qy¼5{Ê’W½D@È@ltÒ,¬àST撌׽ؿ}ÜaïbÇyÜ¿·ìK×-kÞURp~@‹Ãš¦`ÙÊó°tòE××Û!éØN,ÿk?&–ƒmÂ,š‰†- àã'ÆØ#°M ® GLdm¼1dê[¨]ÞÚ•A=ÕÈXÆñtó®XœÖX3<¾3`ïáÇB®HHã +ÍÈ. Œ‰-ΔEc'â›7­FUŒøª3-DƒUz|°à> ;fLüÙÇ¡Ùz¢Õ Î(Œ?'oA¡€Ò(äts&žF½–^8z×/Óк ’98»~1 Ñ©6xꇑhÛ¤u)]×”Ä8\e¼õ"%(B¿ÿ2€Âú৕ߢEm'¬žs=¾| ß}Œ‚Å‚?ŽRK Ý“ßi)ÉH6z›žJͨ¥º®ÊçÛ“¸å$‡ŸÅOý†ã³‘Ëе¾Xô)ÊuÒIôíW¨#*Ñ 5z÷BM¿d„\°F¥úeؤ2bÑþþð+芴¨Ëpª×c—üÀXűæ—-h6â|»öK4¬”¹Óö°s_Éž55‰ÂG©¯Ùç·Ìýwú*Ñ ¸R«ûÇñâ»ýРQIœ\6 c†.Eb†¢bÿ”n>ôÈZ1GKC²ÿÈ19—=Ñ-ñ†ºÊqQLÉc.Ã\Žœ»Ÿ$åÑVvÛýï§Luíý#0y#+÷ žü~Æþõ?øyÛq YÙpÍ8=ÎEolœÜÐúÕ>pÝ„]ÁqˆÚ±N›#Ð×ÊÃ7  zl€‚ž¨Ö¼ ŠÔ@Íú~pv+ˆÀ†•`M·1³þ ­µuq‡7AHŠËÞ¯rb,.añ‰wÔW‘ƒÌ<ØyDýnÍÑí•çðùŠ1èÖÉkÆM®×Ùk›Étþ—Ú‚UÀç?hˆKÈÿu^øÐ&ñb9ÃNvÿÁs¸y×®Fãú•HDñsè¾½8vìܶY¤^Ǿ-Ç «Š:Å]½‚+ÜpâJø9œ8p'ŽŸBwUºŸEŽ2vt7ö,ÃÅèdxߨÄ]>H9¾ H÷q:ä4°e3;¨,>x¥=ØK¥+­ótg¸Z]Á¼ŸÿÀPO4hèÍ $¤ôÙ…ƒØgeÍ®% »CM^åBU­HfoÖXé” ¸PÇ5oÞO_\Ã÷V~‡í?èèxúcPE¬ýÒÄ»»dƒ†(h‹??ŸU[Lh×£,µ”zJ_GÍQœÄU€›¨¸Ú2þ§œzÀïÜ”ãæ×8Iƒ£l­pbý.úÞK*e¡Y$}Ê/„\…gùRp`if¢a lxJnýîÌÞƒ§&}€AƒšpúØð=7“åG_ª…wC@Ü ¼ÊUEÝV…±ø«éøca šu©H}¢®˜Š¾rñsqs G;ØÛÐo7Ípú²³µÓ¹@–îi°²µ…saGœÞ°‹¡À C–èkìår‘0¸•÷ƒW7KÏ|k²’ÍX¢ôã)²"¾0Ú÷oLýŽFôõd>7?·^ŸŸ¾¯ ~þÁñA談›är;À¥÷×ïŒÂò¿à“§ûâý×>ÅŠðÕóOaýú7¬´W,Ǩî­p ,QW\iø¥ëñÝ Ú}vmÛˆYo=…ÁͺaãÞˆ{¶ÂJ¹q§w`ôs}°õØÝÊœåõN Û‹¿€Ã´ˆUY¥‹ýþQ´;¦|¨õ²ÜÆÆ«"ú÷/ïL‚k»Fð´äQ.˜ˆ‹N@lÈ%\ç"Š„k8{äb£âé’Š¤Ø’Ö8$‹…63‚ìøT¸zUT¯ä‰½.ÃÞÓר‘:BKºŒ ß–!ÉÙMºpåóeuàkUÚ£‰€×ÀÖO¼ÐûÆOE|Å@”¤)6‹ÍâcÏÅ;Q×8[‡ Á¸O+mRâ}%F×ÓT.ô‘$Ô¢@ñ2¨ÅгëVbݶ0.¨t`ß—ˆM3#<Þ Íû2émzʸ"Z46ÌÞƒÂÕª¡cŸZ|b‘ÄÅD)™?*È£©yoóÂÝ_hKÎÃY×¢(ï_P×½$q;‰È+q\¢ˆKìc¯^ˆ§Ÿ8]½«?:<‰ t§ÑýiÄ«q—¶Â¨Ó£⎮ÇÜ™‡8°â ätï‹q4"ÍûWç‘›]Ĥ&Ôvíg?áã—¦#*Öz.W%âàÆH0yÁÛ‹ý3ÿ=,‰ÁL@龜‚–?Ô\*%é R\ÛáÝ?CO"6®@LŶxrP#T+YgRÄõœ#4- {öîCLRÖÿ¹ †µ¤âåuF“=kðåLKú>†U÷ÖÓ· ¾ød*æÿ5‚ã³¼%cÆBžBˆrAD–3¹”%çÍ#<ùlViã:º[XÛÂ+†m)9'ÇÍ?ò]®5—¿òÝ\?ªtÞzþRþ]*VÚêÝZ¢ÂJ+4kTB—íµ°£8x.ƒV°³X²ø*šBq8Öv6ǰqgŽlG´­ ÂÙ+ðõ²'u ×Ú ý?é‹}½~À§ý¿Dÿçjâ̺5øëÏ04úø-Ô,ëHkB–~æ µŒRi5NUš–3FßQ!§eÚ4GýšÁhÞ2/p>ãç°}ÃY,‘€¿&nDÛ–X»ö"¼8ë°bá^T´‹ÄÙë|±gœÆ‰3ѨPÚÚ— gt}»¶oýß<5×^o‰„ã;1õ»]ðöE4©S˜}õÍ3âªwg’p9dÞm>–ÔS]Cŷ׆eŽŽõ=©ãæ÷ñ““j±{0mÚMšlE™¶µáB”ô+ضâ¼*ÚbÉ”5pèìƒs·lVÿ¹ Lع9 N|ãî݆&õ} }¥¶×í×õfoÁ¬W¿€MT8ÅžÂo/E‘Î}Ѿ­¿ô¾·A/ÖÞ¸¨sXòóD&Å i“âtõك߾݅ªƒ_CåbâG~·þø¶bÿ³Oöš6\k=¿§‡šäZØÅSc¿dX{ÒƒdŽ,ªÙÚÖŒAçß®|i12uGHp>} ÓÿšŽ‹CZ‹ájL–$ œ~°¶á4„…  ú£^ë–Ø2û¢N‚Åu ·¯v!±)qWpêxc’Z€C˜xs¹¿‰¡uN>Œ¸4[”ª-ö*;w#BŸ5â¹uˆ¦YÁç|‰ŒK*)V»3'B›˜ŽÂ¥ÊÁ» Râ£qêÈ1väžð«ìkÞ ·uÔ }ŒÉèS~òkòàP£æûÃÕMìWip,R¯ÌøP·>§sCðI—Ž0ñ¥žÁøŠ¦Ú¾¨þlod0¤“%õßÜÁJÇî׺ÆÌwÄÏ/LÂ÷ƒ¶êñûó60©Ó¿½+¾™t³vñFï—Z¡´Ÿ£ÞÉßœC}{k®•›/^_<¶îÎÔ´4F[(„~ߌ€¥5‹fT™-ykIöËÌÍJ¾êØŠ—ñ°ß5Ë'ÍEÁªMðÉRL|þ'L~í[ž/€Žï¼†gßng+sÄ…,¤E/5[W´¡{O¹°xÆêÌÖ ɵr€‡«¬}WI! ÐÆTÿ›ó,\ R_éf邎£† ‹5·”¦»‚èç+rÖ€/í4*¬••†-®Ã~Öp3÷–bv(Z#çiCÆ#Ç!ÝÂõ>ƒG÷‚«%sÜÚ³Jœ\K´õ ’éÓ;ëÇÅøj]iˆèþáëè?´ l-Å4ñ𤚀ü< )¿ò¥#ýÙoÞ’U:7 ¿H0ùùž¹Øªá…ö%¶kö G»šÞ™WÑבS]'÷ïÆ¾S;1mòr”í÷=§ Kif¦¿ý#D6õÊqŒò2ü›Ã;5ëwD%h}ç¾ñöf”@§øãg_¹„)¿-G£~ï W»Bødøçð®Ô-:øâ÷Qßãé?¹ÂÓ#_=ù*+rú#l öœ¯‡ óžÁÔ7^‚e¹ÆHرMþ‡aCÚÉby•rÀ‰Pº¨\jU¡ï³l÷%³o•ü•¼¬&7Žeɺµy’JuC³vBáBƨ^B'™¬¬aÏWtZ,f’D܆ç¸ñ]?FöÎW¿|¼‘¤{.Ó¬ ÆmÂØ¦<Çœ õNÿ–¼7.ºéC¬}ðÒ×åxT¼Øní¼oÊüH}É’”!CC‚æßÒÔìY†‚’ÈQ$a|‰d}t Ò¸#¥[¡Bl›aA•€övކo¸YOEÓ¤ýÙ=Æ-è?nhz2 ó©\ïm«ÆÑ¢cŒV*}'q‡£@IDATÌÙ¡†”ÃÆ ]ßz6«Û>ñvÍ3žAó“˜%Ýì—™ådÈ.»#ÙóªÏÿ>YR3¤šÕ³Ï¢ù¼¹f·ÊÓÍ«ˆ®QF?)ú*ôG£Ë¡§æ¿fRdüí½Yþbqu-Q/ÿ5ÿK1؇5õUœßNpyIºx¡ç¨×ÐþÍ–šÎ¡ ììít=¿Õëܸ*ÿþŽá–¾ô–#ýqwÚü[O©™Yžù»–S»¿{íJË 3Ø:{=*7툄ToÔªaƒÕó7 UÍ>ºrKÑ&[kD„Áž•»Ñôõq\åÞ‰J™ûÄý ôíG8åÐS>NŸœݳŸ#B DžX…Ÿ]ƒ–ÏähÒ×/Ţńq[Û ©î(_³ZµjVï¿íèb̤DlHjެù'¹«ÊŸ¿û”!8rÁs>Çê!h[¦9l=¬qùÔ)$>]y©oî[öhåüu0{6°‘+C} ?ضI‡kMm9$pÓ†¤¸$\»r!ûC)á.ŸDìÕX$p9j*c™É"ÍÖ\ŒãèìgZÄ +ß2EPºji¸pÇ ;'º¯Ð*%]¬yùŽÔÚõÍ¿ù›¹mÒµÒrFl“I%æó¹ûk~Yä.÷ÛK^’"C¡µ‰œ}Æ^‹ÃéCÜØàøy\>w×£bè{Çù:ã öðVÄ×ÞÑž2t‚§'Š”¦ «”†§·;_¤vÜÅË%št ~TÒ­º$´0{ÊI'ï¤KúT­¥%l,å•$¹înÙÊ©,‘¡Ì‘ÉVô¦Dbl"âzø,Î?‹ðÓ—ôç2îz7¶ ;ŸC±Xñ¾"+'W'F0qC?”ªT Þ%½açÀçÐÁºa­Ëðî5ËŽ‚ú|¿˜wÏK¦=4!ŽÏ%}¼ÏŸçsŠKg(ÏË×ÇçTÂ&¦§É”?åie [;8p†{!7x3_Å’ð¡\m)OÇ䙓¾š{Û[Û Ï/Ðh`c+ÔVrÝú4Üz…ä1d·3®iÓSnôüö’þû#ã&ò=ºïÓ©€»ë_Ÿ¿«ÁCOrÍ““Ĩ³¤â™m,2Àˆ?·Çëâ—†À›Ö;—ø¼9rο×þ4×Zp%¤Ešêt} Mø#€HW}swm¾Kέøð?t ¥š×Õ£#¤ÁÅܼ<]C|ôU”nÕƒ?Ásx†ÞÚ26ìøzGŒüòg¬ðï„XÇÆðá³bQ©Jb@i®¦gÇ\´t€^l\Q¹¼+ÏŠA…¾Ÿbа껃Aªr~8s®ëã|4Q’âôwÚƒAÈ‹tÂq,øÀΓ8¾÷8ö®Ûã;ŽãjwÌ¡Nº8»rg&78::ÃÞÞÖÖ½ó….D7…q£")ïÓˆ¹~ 1q×õº,ê‰ uÊ£j³*ð¯ZÔ’ |øï^ˆRN„àð/dʃ٠·yE†B‹Ûûáäì]»G¶E8_¢’œ);W†es¤ÿ³ehÃZ/32wÿºœx¡ ³1±×tòäBÒë_ÃU›Tå`6ekúÃeÈ}DŠL¤øï!·"CI¡ÁŒ–³ÿ$ösQÏáM‡¹Kà’n0aï ?‡NNÜhÂÁ‰…‹†HneG¿4’£«a ‹¿¬ËïzÌ5}÷A;Gº˜ú¡zÓ*úó(2ô.ì­ËÐl±íXÊÊí³•S{¤O¼[2ËSrž 9{OâÀæƒ8°kŽŸa|åtØ“,º8€³‹+ì¹–Å–äÑÊ$‰!á]H¤q!æ:®ÇFs‡¾Fó°A©Š¥P­YUʳÊÖ( o}Hu/ò¼}½—kî†×ýž—Áz^ÒÅ‹Àñ£œEÏ QÊË `ÞGƒä’&ÄpU/7SŽ»|ñŒÅh²'•LI†y á]¯è¢Åq=PºA8ZÅÜYÛðVÿH`,ÆøÄXÄqÄoçlÏÎ+ï)´Õ×× 3glj'*ÃÇê:¢ég[$…>•^^8¹ý+,€v5Š`ïú5p®ß mŸAÅšã»20vý:©Næö—Wi%J n‘¢…±ùÙØÔ©%j•qD(_¶NÅ|plìWØÙ«Ê{%bíÊ#¨Õµ-§ ìøÂ|¼RBB¼îKu÷î2 —ÕiµÏÅÄÅf¿Ó'ÝÊs‡“òB•ŽøÒ…KXñë*ý±á\k‘f‰2þeѵ}Ô¨YzQNNÎüqÑ_²vìˆ-Ù‹7ƒ=„lšÄÎ811:ƒ¸øXݻ¶`çŽÍضp]o2ϱ(Z÷k…ýšÃ‹1I¥C~Ü,J"¾°léŠt×Dňáë'Có%Šl˜»‘ZVâÔÓô…×P¬x 4ªÕµ_iD䲆 ù2uä£=-EÜzƒ$Wcß#)99I—a<+>!¸cÒÞ=;°c£¶|ò'C&À«8£ZtmŒÏ·‡¯_Q]„ðªtïÈ3(Öxö»VïÁÒ)Ëppë!$2|cÁ‚…P¹Ju<ÝëUn°×$D¤Øs"„ךë7,9Ø’+z’Â)ä9Œç3G9FEEbÿþ]ر}#¿³ÆÌ…› j4¯®Ü=« Ÿvœr–0S¼bc Œòüâás™”tç¸S"O1ÅSÛ×íÄâIK)σHˆJ‚‡‡ªT­ŽÝs³œ@º*ºÓpÀ‹£ìHxmdMNæL@gYDžII"Ï8Ê3†;•]Ñå¹}Û&,ÿ)s¾ü®…Q½Yu]žÕiHàâoÆèxœÄ©ë¿ô[¢ó¹•'!‡·/ˆt[°ÏÆ"E7õç*¿€˜­zù¥Jy¯‡Ž•¿NÀuw¤X€Å+J SÇ*8´a1öì< çJŒ´p!¥‹Øá-m¾•jãì² XËPN†g0€øe,úmº?ÝnÎì#o)•– Öo¿‡Í^ÃèAÃPµ’¯À´nZ#^[‰)ÞÀ&ßÒ(X²5w±çâ8´îÝ égýáÏåž)$仿/Bš›+‚7¬Gçb`/îoý¿×°»¡Òì0däkh»ñ>¦'Šc°ê6ÿC çÌy¼Uù¡Î½ƒþÈo¿5‚-ìå¡%²¸€†~¬]p÷gÙDkÃÅÈp¸x9ßvé„/‡Gbñ/Ë0÷ÛyH¸–ˆ:µá©áCЪu”*åGDË’aXb'Âûeû¹Q î¬hgº+ˆqÃüS§N]ôîõ¤¾ &8øV®XˆuëèöòÆDÌûzë…öOµA÷úôéòÕ/…î'¡kOkÝ:~·¦ÊËRâ`ž8y ëß–]¬ï X9=|3ÓŸçP%°=?‚÷耊«‘ÈÒÂ+3,”MvùÉçìÉ–³3NNÌç~C†5Q ;t£ ðð Xµj!Ö¯ ÂüqK0ÿç…èð\{ôz¥;|ŠûæJ$Y•òŠ€ 4Óºlãò͘5zŽn¹-U½:DóæmQ·^S’úöò™× UY\ÓͲ¼ù~t¢»‚û_“É[—£œoÙ².Ãèèh¬_¿k‚–cÃâ ,ÿu%šõn‚¾oôF…åuËn~´ÐÝÜÆûÿfI7€¨¨(ôë×O_´qëÃp—[ˆ8êjûáÌ/[~t †[VmÇï£gcgSJ/®mû£EËö¨W¯gQ8 f–'¯•ç+'yÚˆå †“üò KjÞ¼µ~͵k×±aãJ­\‚Í+Ö`Õ´ 4ah°>oôB`íJ<9P¿NcÀ³Ï>§»päVžf±÷èbàšõ[CÇŽ0räH"õ\Ö©ÿè“™w¾ë_ƒ‚‚Э[w ÿñt0@·Àþ->éɸ~-žS޲O¤îíZÀñ×¢¹¬×Ž[ñ%ÂÒ±m´™V­1|¦eðå’N_,{K¾ð’¼Ü«ïùDÜC’«Ò¯áÜÙKpàˆÓF|úÒ-áá]v<qþ ýülà[‡¯_Ã"–»$“5§@íé¡F¿¢ë°¤{Òí ¸ê® a§p=Á’¾š%À  Ü Ïœ…fç…¢ÞhÍãC~—úŠCýÞmÛðr›¶øâ³OñÒàÁw¹"ŸþöÛo1tèP†0ñ‡§‡_\wCÀhä’ŸÜHX¦Ôö¾+Z`¿Y3N.ÇP‚[bÛ²íøæÕqˆ8‰n=žÀÀCHŠª’¬2Ƨ hz`IfߨŒ…›„÷`òÄï°tÉ_(Q¹^÷*ª7ªöÈ[42“a-ßÄYndѰqi¾´2ßXƒ²äHås´~ÃIÔêÐïÏx[·~Ë%2H9¼û(~1ÖâöšÍ0xðT¯^žžŒ@Üü<¨$/Y;¾oÅ€uôèaL›ö3fÍø.…ðÂgϣ͓­©[ÆB§uÏG½¤DFDa›“°jFÊ”.‡—_y 6C±b…i]7d˜Ë®á®p I’çPHÕ©Ó§±xá\Lÿ-b“£Itû ÿÛ}õ†GyvņOÎ#'àϯÿ@ö½Î\3Û¾× °ÜÐЫ8‰1Ë>GÕº•õþKäyõê5]žË[R%ÊRžo¢I“œ óÑq§·NhÍeÝÏ_³jתO’;Ÿþ.Ft|oN޽šé–‡œ®$Ž?k†ú)_®0æÌù_¦…à€Ýh°-ý hÒè+ÊÆüÄÉJkìX³õÿ6š=¾ýîWtïц±W‰s^eh–­¼,ïD¨„I¹’·R¥ŠøòËÑ»÷¼÷ÎP|2à €#1àÍ~ %Q4îÖ® |l?ÈâÀó¡ðAïqöðy¼òÊH¼ôҴºÜßsHDé}’£D¶2H–,Q C_]ºöÁŸ¿‡_?˜ŠðÐp¼üõ`¸pé½øÌ?,Â+77üüS?”(YˆÕÎ+ ´Æ?­Á›ogM§ Á½x.q½IÈžSôÂp¼òê› J#‹Od8ÎeÁÿ9¦Ïe.äY¬XI¼üò0têÔ_Ž…iŸü‚ '/b询€§+[˜×6æX¥|yPÖÈ`åë¯{¢b¥¬#;Á\¤ýG=ဠ/Ò0hÆÇ„~OŒÇÑc Ýj@.Êú§³<$—(e½Æ² “×Å­¯Œ[Ýz>ëê¼’²î¤"f5È^ê­uÉ© ·^'×Üz,{™Óg#>æ­(Þ tlÚê…¿ùcðóGHrÅ/ìN]¤”Aº!Eg&!GK¦/ÃWƒ¾Aûv=0fÌxâªÝ¿q/˼Rã®9ñŒp@@S;ì$úäZÓŒËãéÂ]tܸ † Ÿh¾Ù¢$– IݺwGÕêu1|ØóøøÉÏ`ã`ƒÆrPôÍÆ­òÕoG^d~SüT^+2<Â…eïö…²%+á›o'“t–×ehÆ6Ç3&q<ý¦m¸¸E„$;ÍÉöžÖ$¥ œIa'ÎÑëD–ÜÖç˜Ë}2[ˆkÖ¬9sWàÓOÞÁäwd»20ðŒ¹©žn3^9ý•™qz§Ëûˆ¹ÉSæ¡uëÖ:®+C–Ìç-ÝİMŒ†‘ÈQ‡,¶‘Pe錆"k2¬\HàlÁ³ï¸ Fdhx+Vœ„m*Úué>5„²OÅ»SߢnÈ‚àlGN yÈåå9¼¹©Ò¯fa#. ‘‘Qx¿Ç‡¸t"'ývŒqO—ÚýÝÍ×g}K¥¿mªfÉh6”m9C$Rži‰\l–+>¯nnöœµ¥ÌîðXe—g‘"Eñݸ)º<‡¿Á0_ô»ÓßÖÃ|=ÊA4oòÌÀÄie°lI1ìܸ¾È{GÞ¥7¿3¥Üü’›ÀTuÈߨ;2î…'ƒp3¢F^’ø¡…†„âÇaãѰ~ |KräÌEHw%¸bià²à=+´b3âéNS¶L-X¥ÇŽ}'¸¦ÜcrÕšÄS}›qéÿì]`TÅÓÿ]Iï½÷@Bè¡…:¡÷& MQº€¨Hÿ*¢€Š ¨¢( ¢Ò¤÷Þ;Té½ç.wßÌK"&’„”Kx —{÷ʾÝùíÎÎÎÎΔ.$iÊÌš gG|±j=ƼØŸOÿ>þõaCî«ê:3ÖР¼ß<˜fÒ”¯ÈDÁTÏœ„“ ¨_ß«t-QÑ Ø†[IXNìߎ³Ç.@N6‚>.žHŽ?‡ëw`ïàNÁ]ž¤ƒÞC^Cÿn~ ú´Æ™ ­ä¼·ääã±á£ŸÑ*¸š¶iRçÍOÊ‹æ~^KaÓ•o’¦-$ë¾ßFöÓÝa¨˜Ü¤¹ýßlásï4vü¹ñ4ÑôðkKXœ»r™ž·€‹“î]¿›C1yò‹°1$ÿéO‘…]ÖX½úê’3ð¿ÞFËnÍ1ð•u~ÂùâVð+ûÓu‹×#ìJ$ ¸¿ Oß¾‚É×Sñ¤~™A wDtB"œÂÁ 篞§~nwŠ ~ƒVì¼úâÕ‰ãàdA{mJÒ"+;kiÞƒñã_!¾ƒ… g Y§f>}ˆÈW‹Ñ‰ÉžÌ*©?²l«å‰ “Hç2Ìxù&vmùvVOÓâ£qb-•ÿ²b Ù\ï¾·œlLmN±»J>¤ARMѵ †‹”\‹ÝÉBpÿ.îÞwD¼^¼öÆôi®‡¾„-g"a ±º)9ÇGgY#èà`ƒw—¬@ÊÃtlýòw2}aã1•Drl=FîÝ.‘0Bæ&õ¼ž>I¡ŒxùZfh{"?òÒt\ѽOWtnÓ‡¶€s«Á˜ñæ|43¾³¦à|Tf™£ê±¤G3?øplÌìiÜäP¨ÄBØ’jñ|ŸcMü­‹w°oã~Œ7™4¸ep™j,°º6l‹6î¹8~òÚõꆞ}ú¢àî\ˆ6ÁËZuÂÈøó«)Xðý~rW6Z³0Æ–IS^G¯žƒðã‡?#%5Uİ ä“ÓjÖ½{¡ØóãŒ5}ûö+“€+àIýÒÁ§%:5±ÂñcçÉÕbô ‹ôÔtq@Õн„ï›gn núÆŒåådš„‹Í©OZ“;1+G¸XÛ‘é ¹‹³D@ïÑÚ5q—¯‘_ùòái` Å #Ç#-‘´Ã{/’Õ°¸èýA º“-î”±·hs ¹Ú´99ù½+£bK»©\Ã¥c¥Ûž•½›×p«èõ‰)´”¥C^)²„¡ey /Gr”«”ØT4êÑ´\ƒª&fÜlsÉ>UYSÀA ø\~z2>ŒÂ¥Ýßãj¦\ÉuQ)¶cš¼ŠsÌŒë×÷Cy È$ËæfZ² –{éŸ6Ù G‡Ý‡_½–d§gñÔ¥Ëâtæc5Çvk…ßl³I@äfî~d6öü´)N°³¦8—eŸC ¯áeÛ¶m;âË/—’o6œlµd³‹DhKÚ°ñ†Nö_z5 ööŽpsó„"€eüÃýåý0d ¯2;q1p#äöQt»zã€çÁ4‡,sâ•WWOŠngŒØpò°Cá¹Ì5Ÿ 73*ˆvTU­Jì›øÞåPØÙ:ÀÙÙ­Üxr… ñ$Ï$ÌWùC' ÈV—ñ ¾Š=çnÀ¶÷+´É”p.GíÙtÁÉÁ榖ˆ}@x‚¼‚µ‡án‚V®ZÖîÆ 0åNÊ¢i‡~9†[.wIøªy ™Wø¶©w_×P™˜<œs;’“é ,°VFâv ”db÷–u8yì$:û3'?uÓÓ“ïæ~¨¤r±uëìlÿi'òUdÇPÓ‰èdbmŒVÝÉï3m¸Ó¦Ä²)X!ž•(Tò\ìÿsN9ŒfƒßÃô©ƒÈ~´ü5ð¤‰PÈù»øã§„g2)ÿkÿû "“!jÙßötëQ¡gü÷#•U…5ëàô;|½â$ÌMxÐÒ^IW»Z}¦¦¦ä\ݘ"ý„½›6UC«|°kGŽÌ€y`×!&gem]; ]¥ÌΔ#%Žü&—C~P¨aagk ³{åòyA“Çã³²*’„,Í}Ép Fæ‘»](þcg~IäàM ܽ{™)Y´ãÇò­ß–”ieœcÁ› ½ðéÞ¥°w²Ó ͤ»ŸnQ4¬ääІeU~ÍÑ?hCšêìxûvAÏÁýi©Äœ–¬¥‚w…ÜVƇÿ-ÜùóG›a¼FÆ®'ÊÊ£û`ñæ£æ=°Í¹wSoü¹z7nß¾ŠfÍš=òVñDÑËüSMBŒ¾ÜCƾ„>ƒ_‡¥ {Ð(Ý»BisлwoÑ®þTœÜqg÷\(”ÌK{ :ÎS?, %‘…&^ú×ûhÐD«VV¸MÕ÷¯‡K7“mîmøû·(ŸQI4äqNm…#§¡wÿi°¤`>RÒÊ–æ]¡¤,øãA‘'SR“qfÏy\ØY+ðdnRòO;wýtÜ¥Æ6©Þºec)&Á3* J Ïk¥Û²eKüùçŸBXNíT" ÕÏ“ (i«V­ªámÚø ZÅ“©ùO6dU[ÙZkþ,,Ø;¿|¼G§ žº+¿8x’Añ·h'*i…ZÒ—FÒr)fFKç:¤Tf×SåMd̓]»váÜÙãEÁ1:öîCNÊ!Á—÷…e¼_Nþ훯qñØ!(s+ Ö,ã{Ês›Nô~©'æü:ü±™vÐO+—+`È[Š¥¤Áeùt1ÎòàƒŸ6"¸·öà)'7”2’jO)æN»†™¯Ü‚…s+-l\Å@×J!·XùÄC‘•FÖâúÈ-ãã%"{{{¼õÍxgèbŒÕ_­ý‰Bù’@B}¼TÍ –jŠ nëÒ“?Ý}™Š¸šbªË±jÝÒ¬äB)3ã1²Ì‰Uâq8þ…^×ÇŒßBWGE|QÊ‹,åÆ3;;\ ÎÃ7kWbäÌ™è>tˆVá©`—:Z,Ìra!´ra–§%*½¢[:mÄ+uŠjì=æˆË×-1iLHQXß²Ï@iû#Úti…ùßÏŲW?ÁÁݰ`Á0ðracTºöÖE\½ÿ1óæôÑLœ}d ¾Ÿ¬MhY4•BÛþˆe-‚¡•þ÷ÓÏhÜŸJ'¦§Q@ICª“‹#–üú.XŒqcúcÎÜ÷ðâ‹àèd Ú—ùoa•±Ò5€W=ŸR³g<Ë"2†™Ì°ò°{Ïn,Z4 IqX¼núŒ©ÜR+©%X#ohdˆ9ß¾O^]Žùó_Ãõk—ðÚŒyðññ&,%ͯ"X8¸Áš0àcÆ‚'§ú!£}ceNÙLˆ1?}æ4>üpNŸ:ŒqsÞÆÔ÷—€7”–cΪ%T­¹b0žú†ú˜½f&Bwi¥ìáùúì…ðõ­'ô«’„]¦¿©3í—(O‚Wà«|ßÓ’ÏsçÎcéÇ qìè>Œš5 3>þ2º(âùo F>0%>¦ ßzì’³ DþwÕv¦|*­j+–ø"‘•MötÁŠOš!-“T0H¼\Úi@|qh%<[º`Öë0|hw¬ÿ~²²²í3LÖ*ýcð$!–^͇"Íqi.?ÏùhòKKOÃêÕ«0h@g,\0­zt÷û¢M§ŽÏ$àò<œ©ÁퟓW´'a ë7ñÆÊ¿?EïW{2úwÄŠåŸ"!>NØp¢¡9k\…D<ü^ÅpÔœ+MÀ}C…B‰-[~ÅÈ‘½ðÒøA°qwÅš½ûÈwì3aȺCþæßu9±K3sšà-úi!&/{ÛwüŒ¾})´î¸uë¦ÐoXËÊŸGAØŸ °dÁéý°—1äÕÎKèהϱãG1iÒ‹2¤3"†ãÚ=ãã¥@âÙ"îƒÏC?,Þ>OSò¾€ÓWLÆ®}[зw iêgâ:E¡ÓÐÿ_x’ôYž|¾$·8žšüNž:©SÆbðàŽ¸z“VU~Âìåˉ÷VÏâ}²îiÉ<çËÆ0")él£ÝBnÝ£ñÞ#‹(F9-céQh_ftM,$y7ôÆ¿/ÁÁ͇±qù&¼ùÖD’ÞG¿þÃ(šY?²×uƒ££+y)Hy0e­‘æSüÝ\þð`ÌÌ\óž^€¨ûQˆŠ §Ð•[±{×ïHLL@ýæÍ°ì·-èÔ¿?ôtuJŽØ.–e´ÿÚ Å?.ä nÞ‡”¼ø´…ƒ¹^©y/wm>f í]ÈüdÍl h‡Ë6‘éÂ\¬Yõ zôêK¡E‡ÁÓÓ[ÀМ:âጡG<Ÿ@7ÍÊÇ,qøå›±h:¼ýñ«à ùž7ÞX±ƒ&N„ ÙØ•¶ ÌÂãÈ×éõ%&nÊêÜ4\<~ Iù°ñlŒÆM=„{µ{è)±:e>Éæ'z†ºóÖ‹hÓ£5~Zº?nZׯEûŽ]h…e7n;;gúïbJü4ýðI 5ýPÓ¹?²0›ˆ˜˜û8wî$MR6àêå 0µµÂ°©SñâoÀÙÑQÀ¨$÷1žxp?ü/<¸¯²8:"Œ–l¬`cgQç'+TåG‰ñÔÑ×ÁÈ×_  |Kü¼t6mý?oøA1xÈ‹žöö.žÜîYA Á’¿Ë‚'OLcc“<ÏŸ?­[7àòų0²²ÀÀ‰¯âÅÙ³Éß±«Àÿ O6(é:Wˆ±ÌOÅÙÓçAÖpnÔ¾^6uН*)ºF^¦ì?Û4ÓB’(äj bªRŒ{á.ºu|ksfQÌ&+–XHÒÕ×Eßñ½Ñqh{\8xQˆ¦µþç/±æ«OàDn=o?xyyÃÝ£\Ý<àâìk°$aˆÙ`aâ )ÉIHLˆ#¡6QQሌ¸K.uB—Dqñ10·±FçƒÐeð`´ìÒ†¤a ^]"ÓäZ©”©Ø³z ˜´Áè—»–XSîøw­ÇÛ#f"×ÚЏHûcÙÆÿƒ¯«i‰y–¸nüeÓö}Ü®W[´îÙ —^Æñ¿N`ϯ¿cã¦u°¶²¢£y“™‚»‡7ð¤É‹;lÉq½……%iöt ªì‰ \G%&Æ!:š&&„cDø]„‡ÝCHÈ ÜŽ ÞzèŒqc@²¶47h\š™ ¸a§vaÏÖ‹èöÆ[ðqÐ0/N}ë´H¬˜4Û·_¥°µN»›Ž—?ÿ“_êVª`\<Ú|Ì‚ÿ«Gšù%áöÅœÚsû~ÚiÓþ";u3Ú$Z>¾ éÛK áêâ; &aii#ØhŒ²23‘Hæ#14)‰º†ˆˆ{½‡»÷n!ŒŽŠ|4ê€7V­BP¯Ô.< K³’”ÒÕó¿­Ç™PŒ˜5dªR’`ÄXÇ\Û…¯ß]‰ g¯Ã©ß|¬üò5è›ø/Á¸6cWRÙ5xzùybñ råáI:~>€éÓGÃØÈž>¨ïãGXº Ø:SŸtpp&<­ïA–V´VÈe V©B$%ÑÕAšºöÚ.CaÝÜÜH[øXȪkµ,½>4ðJ¡OÝòìÍ^pUC,Ë6uéß}Û"%!W]ù¿Ïƒ¾‹kûÎ —ì/•yä"ž¸'Ó—ÀôÈ0SÏ@‚sä!›±"_!8ÿ—Êe„‘ )̨­“3: „¶=z¢I`[Zžµ†>ñPKc”Mi‚BŸÁS QÇNüuR|êÚ^ü}˜ø&ù©V)ˆã xêФEF¤±§„_Í's†Âɬ€'Ùr³ÂÀ­zOz™Û^´_¦tžùæõÉB^(Afì]ìÚvÁ¯Í€c B®®* ?,zoéã›#‡ÑÜY¥dû¿|Úd48~tK}OíX‚á}#€¾Líp™¦Ï>Ús.•œ®^½ŠQ#†Ó,,Qȹ®¶•L¾eÇÍihD3áÕkÖ 1ëç1¨hyE%'³ŽxU9`íCýcÖÚΠ݆vA¡Á$Èä#ö~<¢Na³Ò³é“%D& »Ž›!¨O_4k׎v [ÀØÄFf¦$ÌZÁޝƒƒÐ9y™š0ãWÜô€—@ù<¬}С2ܹï.БY¢Û ~¸·'O`A|/?Ï÷qâNsß.D¦êbÑü9p57Fy¼ñ¿‰˜4ý„¼=þž¦îªÃXØe Y(1³4C@p+ÐY5âãO1ìS‹0ÌFZr ¶9Øóý>ÒècÄ”7è9K²+´ I1}[ÂÖÙ îî0 †ÆòçI yRÂøñyþp“LI¼O-ÇNÖ:ðé2šþJ€é KÞŒ·¦=ð½¹Y°íf´˜ü%úwò¥3ÀÈ·æâWÒdî^÷;z­zYÈ“ó~‡ýåddjˆ-|ѨECáwJZ b(Ôn*MB3S3‘™-ø#\w|»“l´ýÑž¼"agHSê“ÖÔ=<`¢§ûˆîŒAqá¶øÊ×øwtT™3¸Ãœü‘µé7¾g%q…f šþZî—M-À¦Ñ¦Ø2-›c9'1ñŠ'CÂÓ·¹6o@í™&äQ#6"–|8§<Â3üÆ&=LŸß쀋§,àÉý²°OZÀÊÞNžŒ§^™ñä>ó úf´g,…ï!h½ýEU£É.]ã~Èø1žŒm2MjNî:‹> ÷" ­pmæÒØÙ|"Žï;Æcé®ºŠ·`í®Oñ>ª5%‰¡%¢0ôh€æ~n4ÓfÖ.¦ŠR€ý$FƤà§]§hü^E³©åÏàã5Í)€;~߸Ž6ìè‡YTå$ˆø ½ÂR8åí@vŸÎ.NÄü˜5sh[e8~øNl?à¡Ã0lÌhár)øzqA¦ø`Ê×5I®Î¹{•”s·F¢Ð¹×ÿ _ý†¾Cú¡yÇN‚†±@™‰‹»6áö½$øµ€æ-\ŠÊÜ¿=½&ðõ1{4h{´†kÞψI&GV$ä>oé1†\óBžÃ[;Âÿb(LlÔù¸~ô6Mí0åwá†[cÈ~š?Å'&ôóQâÁ3áæœ:w j]´êÕFi·ñÍ’ÅÕ‚Î-Ш GŠ“"þÖal>x*ó&è6¤ ŒØP—Rnj8ùJ–cÀGÍé®ÂÁVÏÒ]lpãá]Á¥\Ñ­ÂýÏËÆ‘µ ‰™ Ì›™!È=‘“D°y?üë´ìܯ-X $LG †š¾X†.ôÄ~\¹÷úæŽh׳+2.oÇ—Ÿm€oçÑhÕº UB9BíÄ‘;!pð놠àÆB{á÷pâo}3²¿VÑê ê•Ç—8ÿÚžžÄÓØÄ>ëCÚXÃY i˜œˆ#[¢I›L_¸Pौ''ÆòéxQçãÂÈ­Ñ®wO(îĪ¥_Â%`Û¶‚§ã©FÔÙ¿q%ôÌÜÛ£SßVÐ%ȵôØ{ˆM²Å¸Ž~t_!0öm‡ K)b–º G·Ö¢¤ÆÆ?`.æÇbÒü×AîtiNìZ¾ˆ×Þ[½øë¿þ6R3Õp!!—SA ê\aóËgÌt y¤IÒ‡•™Q-gÂBµö ¹wÎíÁšÝ´‰ C¾YÒ9»ý`vØŒ¦S¢wC#dæÅ¡@Ç Cßú£ÚØÀ0ä(nÆÆ“ˆ]hšÀ”4lòæEMâ­0ÙdÒi¤o%¸šóâwåR@Nt?y`7d—~†žAdGo‰f]‡£åï·1rÎBøÐbHƒÐsíˆ ‹—ÁßV‰øÓ½””) ¹bÒ °VþÜ‘]È [ìÚd(w€«‹R¼ÖmbàìwÑ’|ò¦>¼©YCŒ[¸=tuöâcÒHĦ‹”Ô¼ŠC}²¸›OfjrX¶^«®rÀ¡o¨„±e>›¯k}ª+4×zB‹¬i Hñêè;?"„ jAï,‰d§]M6eÖ|€¾&Â’²Dèî\ä¿#ßYˆbaÒëÑF5ÒëèéÖ–¯ ´6È+8ˆ˜X àXèÀ>æÎ9„gzÀÑÞ\¸OüSuÈËÎFr97eÑ«F¬EÌÉO"í^íú.œ  ÉNÛ²ÐÛ…Dؘ¨û¨åê:ív:™,Dѹ&ÂyIÞ}œ¼ž £ö IÇ[Û5FUGÿÊÈ96ªuœø&¦Ž&…¼"“z¹ÔGyÿs)yR1´°´]´ Š6%Ê!ׇÝÊ eç‘—›‹6c^ÁÔé}ì¸O¦ÇÝ$ ê¹¥AíšJciq³ŒÊSeä(N)+ƒŠbµ€jœ¾d‹[÷Ì1¢_ŒµÜX¾4‚擽×ËKV@ï“•Øñݸ7îˆM½aKËl:œÂ¡­ MŸNH•: M«\DÞ‹„ivšõéœaP5³˜±¾S¼¶hÞ}1^»¼ÒôHD$YàÏ'ÂÒ°Ð#@ieÏ?xÁ­Í,ú(ÛùÉ 8h,ÍÌÐñÅ‘8pj.·“1|{ „4á¢ãÔ0jÖõô²‘šk#šˆè!xÚ;4 ï éOgc„]¿ŠöSÞÁ€®nÂdæÙJ*>]òI( þ&f'-Ç¡ ß“tk} ¶Ž$èŽê‰k{¶BÖ%d&Ö6¸%R"CðÌï<»õ†^z2ò}a ûOgúl“Ëš^íGÑø{-íýâùÊ¥D6ï3óâsñ÷æïéÞ ]FO€µ‚_ìóG~‡‘$fºä+¹dß¿ƒ 8uìµ4iY rEF;&ô¬0váÜ; oöëO¾t pûÊM _ð ZúšÕ‘g ÒÈΑÃÉ!›öwh÷´LrKë+‚¡b €§YE¯W—FŠºq^…¿ö¸aãOÞèÑéA‘[ûjÆÍÂÐÞ—‹\Ú‰­K"!¨ÈNó•Ï¿¤¥n}(P…ª]GÈIm­ä0O’†hØuà?RÓ¬ h{Dдÿa¹«þÞuÃLÿ2ü;ŠZÜjh2Žz­†Ï A’|²ˆ,ü¶?M_ÈLßRZönݳ?m¤áýÜ yÛþ’-µêñ$ÄÔµ%þ÷ûvl'gó÷d `Øëè?®ŒŠò«†ª<—¯à~$5²F¿7?B÷ù‘ÿcv)Åöžû ºääA×@Ÿ\ªÐ‘L‡ØnZMþ\=ý; x‚4`š¾¨! oY šò)|T ù÷_×5÷‰ß•OÆB¢oŽîSÞC§WÒ€7ˆ1žÝf„ òÙ¯c` íeh7°Oê#6' xzò80„S»¡X±Ó[¾ÛJîÎ ÐkÆ'îÓJX6¯½»AŠÓ\…¿h†½»œqhßNؘShGª¿¶&QÈ-%ÙZ%¤å¯Dò]ªK£Å“ܨ„g*딚DÞü/§Ž#¦Ê¥€” í+‘T# •[!7MË`—kÂŒ˜“D*'‡è…ÍUJ®`Ÿ[äMƒï{Ò³…A@“~ãЂ>œX›¡ÉK8!þ©R 0­erû0æc=L…Z$I¨áj†ædø(q†N~÷îGE“ÇGÄ©âMßÓ!—ÛZJ(P ¸tŽ=q?Ô`Xx®ä%^”œ›Á…îû!¡š“O9 ¸ÿÄ“\¼iú$¹ã,ާPDêšþÉ¿;+ß@Lÿ4P8Ï¿¹mhø6Öú”›#EF&íå¨KÅØe­§{åT€˜RôÝK:c vÝN§QèIT¼õ?­„$˜DS8×§ïBQ l]žVíº.%7LQ˜3÷2-+=Ž´«Œå+ 3â'SIçž¼§øofÀì”?âÀZœ2ÕsÌx=‰Ù“¿ŸV¾Ÿ·0†ü]Þçé1=J¢wIçÊò „Ä~XJUÝ=%aWÒ¹ÿ*ãÈöðÜ'YyPÞçé-N´ÍµqzGC_®ý5«Ûš\€É8üë˜Nð¹¢™ˆJÉNžIÓGš/[7´ò0B¶àv‹ï£OEf,ü«]Hs((5ïÓ´ þÍ…à¼ù˜î½wã*¾½ªBpÛÐa×@ÿN|kEÊÀω‰( A‡V±ôyHÇÜìâ×,e´ ÕC€ç¦¢ÕCNñ-•@ÇmòñQ%d«mYU®N×QÛh^¥åÑ$%xõÅ»PºKòRqáªJ+^áÌë¦Kí€L¡[䫎-Ø4QIxèÊÈ]ËÉÌ@Ž’Ü,Y‘ãfàÊñkH´t@Ÿ= ÉEˆ­%û1T"+ƒï“ÁÜT¯Ð…F@}ɹ tïƒð(,ìa/ÏGzžææFÐgå0 ¯ vMÂn­­´YúôGë®Jè(HèVÓnÜd˜YS\uLMõÉNïi/¯—F|…œ\ÁKƒ‚²›,0½¾RyÌå‘w‡",+/çÒj^ýçynÆ©ðû O–÷ï3ˆL!¹$EŸò¾¼¤û)„¦‚œ+?cKzIÍžc7$E©°®kÎ×îoR<UI]tP×j(àSÄc4u,bf†®x»¯p&E>¦÷㣊çIy0Ž«)̯2r­xyªæI©¦íÖžB VW—lyS5•¯`®uSÈ%}çòy¼»îòÌ\0}LkÜÚqGbrñ"í|5Œ¹Š?C2ašIÎŒ4Ç«M%˜½r 2­ìЪ >Ûš¬Tرç(þÞ–ŠDr¢?cò èÛØŠÖʸ˜DfQW¯â•Å¡¬ß^ä’$:1;àƒqå¥à‡Íûq4, ùÙYä>Ú«?‚ýGðûí<|8³?n;†EOÁü–&““þ•‹ÆÁÓ€ZV㪠æÏéc*|»ÁïuÅwkŽÒ$¦lÆòÊ%RãÓ ÈÎ'›ªg'¼.mgHç°¹”²ÒÓ‘',kÕ%PxÃFNf6M4UD»tÒ„­Œý¦B0Cͧ 0…Ö«%Üð”SŠx­Á7@IDATì<ŠsŸLø1W~öÄ"-WQÑ&E^ccÿ!ô>ûj>4Áuã(RÉD;]ÁNñÙÛÍ׬°ñŒÛ&·ÑÜÌL¡²©G]Jì#›y §ŒätÂ1…ÌY*^KÞŠ•É|³5¥´äL¡LŠg(“¦$ñ,ÆW)ŽòÉ5óÕºa”¦©!ÙêÓazrŠp"35»ÏŠ; Ô¥³Ó³+0ª©±ë°+îÜ3ÅÔ1äw^Ë=ÕM!—ÆÓúþÍ1àØE|r9þ~®È»lµ·'ÍRÑwÉtaܲ#ñÃþ³Û¶š×·…i@WLèà]Š$T‡Ô,ü¸ ¶~óvœ ¥Õ¤¬B.•Áµ¼-‘ãUŸ¼è‡kûaܪèÞÍÒS»°1Ú¿¼óžØÑ«B‘—“‹=§®âLº%ENQâÚ­»8{/ã›SÌz7XÖû—Ç]RÛŽÔ¿oгg­‘›_6;k}=†>Ä’Ц­²=ó´Z³&$;-[ÀzÓgŸáï…öiÏ=ëu~¯ ¢TƒÉ o¶yŽŒ”L|:y%tÉÛóšÚDÞŽB@Kçr“A—–i®Ò*ͼ‹èÙÊÒ$ˆ #á;oöPîr•ûµ7,ðWj5þ£ ‘wCh㸋/!Kª:¦#bæçæ môÀ¶m¸yþ¼à á?ÈQ9—GÊIÀ±rr,5&’/ðšïÿˆ_Ͷ>Ó{™‡ÄFÄÁ\?ðÆÖŠ&9PPäæà›¹ßЪ)ùž­žTj;%6 'öþÈþÔv+%ßÿ®¥F³]-ï"<3ÒÒKBüï«·ãðoGž©Ž\öøèD!H·•²'5öìuÆî¿\0nx( ¹e²&B.2•†Œ Æ7G~ƾ“¡HËP KŸ¦H¿¸ Æ-У±ä¶ø.Øõœàhk cs¸ØQ´'eÍõ0vxK8X™¢·#n%”‹¤ú:0"sˆŽíêÃÒØt@‡Wq%,†wôlLô`îãG«äÂÄoŒĽïî@¥g„ ƒÛà» I;²šÙÐûsinZ™ãt9«TÛo·µÉ¦ØãБ—M£7v»}Q“Æ®âZÈ'iF|j# 㪜œ|Xã«|ÉGE¡&¯\"3 =øøÚ?Y¤ÊÿMmÔb¼›˜ÚÂÊ’8`% 6ͽ¢OŸÆÐÕe}nY:ÆÆú˜4©öï¿E‚EYž);)x€ÈpÐEbb&MRËþ`îd±(++—0|gK¸¸Ð„½’ëSR±š6²µµ1LŒØokåÒ¯¤÷Uû9‰1¼ô‘žžS-ýëwçv,ùÍCÓ&®4q®¸ X&Zd&ŽÚ˶¶Æà×=+Œ*OO4nì {ŠY\¡•‘tí\^iO[v™ªQ曨Oz;ø -5—ú$ ØUL^î—wïÆ!)9 þÍ]¡Ã~ߪ2ž¦–Rxv¬{;aOü³â©ö4B½úöpsçà$e¹Š5ÔФfUI—§ä]G…\ª5-[èÙºá¥.Vøø«­èß¡79Ê~]iI 0¶¶…¯“.9éNDJ:ÇÏE|z-CRT,òOÉ©¢€…ŠEMZUÞ9ÆËÕå™ïð8ÄFj&ë¤ç´Ô¡ÒECOsDUàÆÍHäôp¤ï<Œ§{”d·K!=•dD¨¢÷æçÓ"//¿K3U>ÍžŸ¦xù¿( Ãä±w0î…»°¦ˆ6Oç€*ôèÙˆ> ÿ+Óg¸Fb µz«6IFƒIçÎ+P¿¾6ožH¯+;3«hÙ×­2+Èy•½ì2ÕǿԎ>­ÆSž«. å¸z5RÀpÊ”N˜;¯/•‹÷lWmª «¶ÌåϽº0ä’I1â…oB‚Ñß{gÑ :GL  SÕbX‘²«P¯žÖ¬UEµ®NšÒl4u±ùÔ|“—?k=D'¥"êÒ=tsÓÅm„#S{qÍQßœHÆ;ÊÊ’H‚ÑÉÍÃÖ-gÐÌÈGö†}‹ÖèìbŠ{›bíç{07?Qa¡ÈÈËÆòow¢¡I:bÈñô¥;1…E‘MœûŽ^ƒm—p7'«nåbªŒòaD6Í2v˜[¦TÖN_¦ÌþuSÕ ¸ÿzeµx\·Êl¯ÉKİ¢ W †-MÕ=÷¸žU÷ŽšÊùqÝ*ÒwªªÔU[–Çu®ªò×\¾ëVµ4üïJÐÎ?ðgÞÊ"de*2þû͹Z‡…\"-3Û¸{cÙ¼±hkMËœ¤‘Õ1²Á²÷'cÏé[úÔ]ZûÀÎLûô†þ™P˜:;ÂËB†W^~AØL"SIѹgO°©Ï^u9HFiÙ#6¥Pª÷BcÑ0(¯û»Aìܷ댿ì=p-6¾#z ;)™Ð‡Ž´€ÌÈÈ\f¤žM°n~säåæjy3ªHÓ«îgÔ 3#¹ýãÉ“Eå™ TwMÄ÷‰) R@¤€Hš¢@V¶.ím‘Áœ6ïWñjijֱÛ³¾ªž§¥~Ȍѩ5Ù±]%O~h7­Ž‰%úõîH¿éº’Ôíä#×ÂÎ#‡¸ݧ†›F¬$AÕÉž¼*pb¿deÕ¤’²0=!)9ð€!zwk+c"7ûÞårÐÇÕÛ ®õiÄes0^ñøßTô^>Yžw?ÎD HÍÐ-8­[·.ßsuèncC%¬-s)îºöËWÙEÓ•ª£mUå̘Ç­øqU½SÌ·ò) âVù4­É÷ËBdŸ|)€+E†esí÷T¤•B®»»;Ö¯_Oö“¢™Beva©T 3³çU…)#Cùxqð=Ø‘ ûO¡¡2©\¶¼ r¡]-üA)…·ÞE<˜=çUפ‘šäòº;¡( ÷9dã]I*åp¥<¨T/†r9…2§w×ÕÄ6úªêtƒDøq_ä>™ŸÏöŒUO[Dttªþ=ÚÐF”„'}ª+g¥é'}hµ´ºx+ó€BA,¹ª™#qaŠêçëm†^½‘Äa”«úÕ݈¥8s!'O†V9†4MA| (¦æ8w%«×#_ÏU=y`iL…ΣޭâÇÂmvî Aù0g@U·Q ‰Cܹ/®ZsºzU=üòäVk] ܾÎõÃÇýž'{÷…àÆíÄjÂS†»QÙ€® ¾þ¿‹06Ö«bÞÊ}R S)l s £ÂS‚Þ3o§riqªê^¦ÅU‹ö¼Q &ÁÉ©zðñH§™pU ¥QWŽ‹Ã0sÚPì=èšÒór™II¹øpéÞÊʲô|ˆçe¥¥ÃÕÑMš¸ÀÅÕšîÕþéú÷…¢KÞß{/ÃÌÒâß7TòÖÞ0†G…áð‘ÐJν„ìhüJKIFïîõ±eë$ê/€å³^ÃÍô³’!ñ À+–£Ck¯:æiúÉÚ×½ßìáÁ}XöÒL„¤ÑÒ~v:ÌêuÆŒUŸ  ±s­ÆS‘-ANªŒ¦ÔÚ¿Î ¹u¯o‰5*‘RŒ~ÝÛ?€5í Õæ™g‰Å¯¤“<ßæFèácEr>yÎムsÉïÐn¼ûÒ[øT×0JP"ibˆÙ<#((4ëlÿ1Ä0–ñgö`ÕÌùÈTe!_—¼w’…HbÒB ð¤¤x¿R«8ôõ<ÄuÀâ'Ã,#ëæOÅ’Ùk°õðr˜ŽÚ/Rh!¡«©HOâÉ~sö~° öíðΪI0ÎÃêWGcÕr4ù¿· ORpíÄS‚À 8˜˜+ ¯[¼W¡Ëù‘ý•“`âíµ•4ðN¡-Çׯ(lsÂR ýá¯BÒtDqé%$ð‡•Û'P]µpßI¿i§šÃÓr'3áÜôäHõafD^ÉÞµ ¡wàð`þ‘¥hׯ ²6M0áð1¬þyB^ ?Q›Kdª„DxÐ!ñîGÆ”KÈ]W¡¦ªaÚ“ðèVvåÅ?(â™pŽ\$¥&$ÂÀκÅl6ùš™_G|~æ B¶|ŠO>¾Î¹‹©2) é‡ÜɸŸÃÜ Øq(]Ú!àúèÕ´l-ôÜ¢:9YH¥èU¦æfÔ5 ;lÍFz/ü }Ìagª]eÜîö8º9‰6bSNâhýˆœ•u !ü ûÙ¿ñä>É 3žâ¡êâ&#Oâ™—ƒÔ̘RôCYQŸäˆ–½–|>6np0Ó‡<¿1û¸àp~“©¬ÔD>Œì.| [náßš(IYÞ)v›²PI¼§NP #K9¹rX[Ôÿ~¢«•¹H|í ”CO¢@zf6äzưÒÏÅ•Kñ°£ºq±ñ°r÷…›«-ض/-ö>¢îÇ@nf O/è)2phíWÈtk  æ°v°9Þ@ø‰ý7êæ\ê¨1`p/¬Þùbîg ±Us¬®¡mu}³xš‘”ˆ ³*‘ëÁÄÌÙÉÉȧÀ$–„CÒ‹ÈÙC–÷ÙR3ø4õƒ¡Ž¹é»s™ùR8Õóƒ¹.\9Œ_¾;ˆ>o½ ['²/×4‚<$›8ºÁ޾#å4ŠÖê´º)û{$P"9&–´ã´ô¬g¢{ZÙZJuakc€° 7!·´EVòèX¸ÂÛ׺äG4+9‘¡‘Pè˜ÂÝ·ÌäJ\øc.EKÑcxØ88Â@‡¢èQ¿³tõ&£JE®ìý߯?¶#–‚^%ÂYv¨Êt§”(ž‹œ|%$º05Ö§>š %M6lìÌyñ*Í푟v0vD=?oè‘V@‘™‚Ð;÷ȯÜ( …¾7lDZ+éè9²7ìœ` Ç¹KàPß©÷#påv.ý±ÛOãë+håk³_¥’Ú+ٌ뉚Ü2µµ:{SÑýÑd¿¼4q<+«ò’®„ûUøêü¹Ë¿­?ëš ë[BáJ=ÅsdUÖ}l[4 í¾þË—£•A8V/øþ3Â=n¾X~ A£;"'6wÓlðåß[`uKg|—Έ;õ'|F¬@°O4~^µjÏæ½Ñ çL#eRöæ)`æ`FKO…šBnnžnÈÏQ æE¼ò·*µ|â…²Q@Bx‘'¶béôÿA¿Ý+xãí¾Ø:}2b]ð fظèC(’`d…“»÷aØ{ñrWü²xN¦X¡¡ÝCl|èŒyËg`çgcçþh$d'!xÊ"·q„\. cÇJ?6ŸÏ‰©r( %Së—áÿ>Ý ¿©ÿÃøžX3v ôûG/﬚¿n]:ÁT_sçïaÎÖcè`—ˆÕçBáëýPàõ ¦Lðů+>Á<[Ü'hÌœùðqÒ„¡*7'6}ŽßþVÔVæÌ ’™jµ gå P¹¹U).þº _-Y×Qó1e¼?¾?†c`c¬™ó¬[ÀÖZŽÓÇ.bÆæèãS€u¯¿…(3?8¨®àyWÌžßÛϳ1ú„g(F¾µM<Œá²iVoÚ†0˜:÷D3Òæò„´ö&5þøÛ ×nZàÍi7`b¨ÝžŠx³v%Z:ÈÏÏ‚¢ —¼.äÐñ?ƒ1J¥JZÕ«Á&¤RB‘Ÿ-”-›6dd²¶†5‡¥—I"- 2ÿÓ ‡šžIMBJZZíÂGkK«F|¢!Ân›BA³ÐÚ’Xh‘›ÕÃÔ/–ÁÛQk?4lã ¿þ³0kÚ˜êêÁÆ¿3&~¶K¾þö)éHIÎÅñ_¾ÅÅèlXPà CÒF< m C“ÎèÐ/æ~ˆùïMƒ9 ¸B«$r¨É¬%$ eTÊ‚ o²:1E—7?ÁGkÖcx»æ»‹¬èÓønÝ^XZ@×ÐùñqHƒ)zŒë Ÿ þxgý×èÒÚQÔ²Wà¤a2ÿCtïæ#Kgø5ó‚kðhÌýx\-a`|…\Ö–¦KBøÙ-Øu&ÖFrZ¹ %"r»ÑíÆÌÂûŸ/7 ¸šýéRE~yc"½õ‚¦¬Â?¬„«¡(àVÆ0BŸïcà€&„6ös—xããÅðò°£>g‡~ï­Æ²uÑÍá!‰ˆ¹±¶…‰9¡±CFtòMœ<ªZ¿0ï¯Y?wcO.2ê~“fàƒß¶àóÍ_ÁKroM\†¤¬Bã¤*¨V5d©Æ¾ƒNø¿µ~ÈÎeƒ7¥´7Õ*sÖŒä¥!äÚï¸~:&öpó†_ƒæ´<[hõ tR¥h@˶ÕN{*Ÿ2/ —.lCXÔMb„M`¬›‹´„tx´…fõýK++%R£. 4A¦ÍÚ€£ä± e^f8v¬Š(½q˜;cÙ]jo#ª%“ÀË= ­ÚÄÑKí"& ¢ºf Ñ»wüþíç0kh€fÃ'ö´ÞAíà 3öJ“g G²Ÿ¥PžiÙ ô›9ƒF¢@6ºï\&̓œ„b ð²0«h¬Í‘JË©©™y°1Òò¾x‰î¥å6'ËjïFµ£=•¿”¬ïh;f~èó&þØh G¸»šÀT§ °dr⠊θ¯/ENz"ìZ·Ãø¹ÓaNŽÁoèBO.ÇR¿³y /šÕ¢ò—F|¢"PÀ½^…yKVc¯eo8µm sÂBß¿=¼|ÂÊÒŒ\ÝÙí—é)i3£¦!syÈuÈ­1}™¾>$9û!‹ ¼ŽrvÇørãEŒ\ý'¦ ŠX›—µ+Bãê|†8"º½<;_û;½2aÕ¸ñ@¢Iê7Ø +šDJàän‚d2ÉLMA½n}ðâì‰dŠBæcº:¤e—’™ñÕÂ}ÌXåUDMþO L¬`lf /o_èçEà•ažøM|ŒkíäÅÐH 3«\â?Ú-àr[ªUš\Áæ_ÇÞíówdÂÞž¤)jQ´/Qw6árÈùG5“Àƒý¯ÚDxËôìàãÝQáû¡ïØÛ…ƒq<¶|? sØ€…XšÅñ7}xBJìA¹HKYT:.#×ÓȲÚ´ê]E)Vj~†“æ[s\ü·pƒøç H1zh(¾[}Œ¼+Ô>¹, ušþ*Ô'Vcóm ´iêDÌ·™d˜A~5³²ó›•!Øêæäªaig„]ë7!ôaRï]Ãñ¿ö"CÉsÚ$DF>DJRùRåÍN€W‡0Œ>€Ý»ÏA’ŸYn~ûyä¦àIB˜v/F=³ÿd6eîÞ=ÚbÍâÏàÔ&V4pæe¥nYHKM%{Ï<Â3Yi4±±CBè)ìÙs9)I¸úÇo¸IvÔ $&E"&1É)Y%2qÆ•7'çZLšZS48=» Dcý+X¹ö(𶠠ɆÙ$ü¤ee"3-‹úU6ᙃ¬Œ2é´Åùí[qæj2ã¢qjëïˆÉPCG’‰èûQH ~˜‘/`¨V&’)Ê4›ð& jŠìʃ6¨åfçŠ8VQ á1×¥M/´u‰ÁŠ•À¯]Ж\òm›Jxf!xkí‰È$<³ [#žÛ#§ï";)g·nAX’:Ò<Œ‰B<ÙÙg²€+yW¸°íœ»rjâ«*ZuΦüä$#ê×*ýâÔ—`ú„[øá›£07æÑ¡H(yâ.mùYë(-‘êÐ Â…–ðlaaÝf¦& BRÒ(ÒºÛ×hFÑ,1ª ò‘BÚ#S dg¥CßÐz:¤éRѤ.%³iøa: 雘‰šŒÐ%ºt¬¢ûH(¥èEó3áOû#‘Áƾ!ŒM­ahlCšÅ99×§‘ìŽ ¼ÊHdHIŽƒT×’:B>ô Íáæ?“PGá‰0%U(#S CsÚ=­`} Ÿ§K•‹ü¼Ò…Ÿ²:z¦Kó‘““A‘‡™Ð¨#2ÄBŠýë¯ÙÊÑê;i4µ»Sþ«àt‚›†¡c[¿ÐÙM`¦K-)+7Bb §ÎµÓWaš} KÜ9s‡OÇÙ³ÉGãØÓ2jó¡SaHž<üšâêÁ-ؘ~}^WC85ꆡcšaÓÛ¯Á }T,XNïçÀÙH* ¹%RÁs â-ÝÆŽÀ•”[ðomOlJ‰ÐsG‘e`èóÇâÐ÷³i¹3í:Ò §á­×áÛOfâúFOXØaB/#©}a¯·Û>ûõ¡_ïÆÿ( ·n¨Õdª"¦Ê§€ZÏžLF^@Î%Ú\æj@L2póÒH­qçÔ ˜úÉ‘kÈ.žÕ¼a| ëf¾gw¸¶ж/H`W¿”»`ã‚Eˆ¶4Ùy€ðp*ÎüŽOf]ƒšVd„ž¯gŽñË>†›±Ž ©ü=ß9*åè>~$bvÉÑÀ׌úN6BΟ¡°µ¶;}.0G¢Ò 9×ÎBwèpLp¿¾ý*Ž;{ÁÁ·^NzÆüô6­úýÇ ms'"ª ×·|†_#,0|Ü`(b±û³uh>e%¼¬Û`×>êÓª¨[Ôn,Þ²8¯Ý©Ö ¹LNÙ½ªÈ6—¿Ù[Ë£¤ÎĹ#"VÚýƒàÞå¯ð×ÁßaéÔ9 ·¡0ï‰I#'â⎷q7×C½Eþ<œ Á€7Bòv? Ûú£á¡Š ¡÷Ѻû{hYß›–€½å©jŠÔÆŸ¨;»p<. {v~ »¦oÃÚxxk¶þý ôÉ(%YÃF­€$yœÎÃè—gÒ.ëmؽwl|}qi2Æ’PF÷ÒB±cÏr¤f¦ ‹vÌ›bôK‹qÿìw8|'–FPêµAŸc·BO-äswƒ—nZ!4Ò}»> ™t9ÕZÑô½æ¯Jb$1гB—‰óLÆØ¤V¢ôƼŽhD±…Þþab¢H¶q„½e¡F¶ý˜ùðn©…IÀeM†TŸB/ÿæÎK±ë§¯Q`àŒ™?nÄ A­Ù–i j}1˜]Ùµ}oyÑrõk%aêÔ¬æþÐG˜¸òvÔ²Ï 5tõMÐqÒR4è5 Yù28{»’S"âưøkW$gÑ2j=¶PŒ2¼NáØc´9cø×õb·Š‡ ÷™&#çÁ­œ»‰¾4î5ÍL ½*_¿öÃwÂ¥gb +6 WDÔúpv²ððîô2ºv@¾Ž9œÝm…~&ÑwÀ÷“¦7Wh 𩏄¼2ÈibÎ]1U>Ïz}g`v «½X?¦ ïŽÃ±8xÔ#<'~»–®¨ od ֢ͨ0(¤pqca–œ/´z‹>ó'ñØ.^N_•NxðÊï‘÷Ñ2ìûö+(Éî¾í¤Ï1zÚH°"—ß[[SbŠ)×äp´ËLË'ÓµRÈ-©aðr}AÎ\¹¶jw7#²²±Éô=N¹‡°æÿÖ >÷u4lÝ—6o†Bnˆ6íGáð•WLË-›O…oø)\;] C´ š…fž4ˆTH’BßÀ–ììÜкöžü§¯ô‚mü5D‘9…Sûyhß¾!L ²påÆV„&½q$þض Ž]¾ÆÀNMq2ã öG±ÝKÎì_Hš¸†ŽÃoaÛ™h(Ó¯aÓoŸÃ5h6Ìãpðèïˆz®&4K9âÍD…_ÿðĦ Þh{äOrµ™å‰ ðzÙvY§YX’È hUƒŒqKH|]f` WS᪦ Ëôáê×H8§a²|¯Ž¹+^xo †¼Ë¿ÈÆŒú‰ÍH U¥þQK`g¯ä¤02· d Ó»ø1cdëê!\d<4˜YºxÂ’~—„ßãÖº'Â_“ósüSÕP@¢cŠ \Ô¿ä0!<‹÷ÃâǬlpðð òO‰ ö^´ˆ’æcjæÜSVoÀ«E>wy‘óÒôc¾¿ö%–¯m„œ±ë·=dþ—KUÐPHûj£ýºæ§ÐLCZ‰º*7tk? dǪ”™ m›¡´ ¾®Ž°vlEæ ¤ùUA.ׇŒÌ]²ñÕÕ!AÌdúvè>ì;˜Åþ‚ósѰ‰? öEæO)GñËÃ"õ«{¼ë£ßðÕÿÏÞuFU¤ÿßn²ÙMï½'IHhBc‚õTìÞíÔ;õï©§žž Ë`”¢H¥K ½'”@é½n²ûÿÍK&!@ ›ð6ûöͼy3ß7óÍ7ß|a´N8°k+ºŒx.Üça è7† žèÙ÷1†Þ´DqƘ¸¢w¯žl§¼CÂ`¡ ÃJµ‹‹Ùí2ކlðî<–VVÔïI†Â9=Â"áâ5 sî}n2ƒÛ—_s´íYSñÊÅÎðûÊoÑo11mHP»×°¬pz.mëŸyrj}\‰þ~^‹7p(ò ©±{†<ñmÀsÃgæË×7| µp&¾Ežá·È7”8kˆECÏî7ünøœ¡¬üݺð6¤†8¼òZ”1à¦!^»gÀ§‚Æ7 2¸†ß†÷´×ïÂ<3d_ЀqˆŒ>µC&—P¥A…PWÐ Æ–:´Bµ² ·.AA¥Ð©e·DÅZ]9j¨¬+tmõbh’™{)- =**+¨Ï[†Z@ê‰-µóÏÇÁ¥ó4ØÕžÆºÛéoR ËkH¬_WC&[R2§ci:õ.ÎOB©¶š;Å* KGS"¿†NÅu¹ÖÖ”ò£%óm mU6RÓ2Ù¯j¶’m£ˆ’3U9RÏïGµ¶‡ü€²²j©|¥×µfèΣIF]¡ñƒl„Ö¾”>( OS×ÍÖ ×l¬œ|O†€ 2dÈh ]C 0`(UáDÐ#OíJ]A0oÕU¹ôuú .P½ 6óÏgT  Äíýå61¨-N£…£;-WS‘{vŸ¤ÌŸ˜œû²ƒ¨`4¡³gSY<ª³XûÃS°SfÐr²'| ç ?l^ý.‚F¾ NßcñÆáç²½BCÈ·“lŸ¾ê"öïü¥Åµ8ûÔŽqáø/° ‡‰£QU *™êä¤up³Ÿ7ÛjÄ;€Šòd릢W°#Ö¬x %&ââ©È-vDjv1ú›•?½Ï/,£¡]Ш50wê‡^°bá÷£õ§ ýõý™ãË%-hùmPDýÓøIe_…t^@©-Rƒ÷6¸l‹–ÜŠwÞ]¼`”ß!Càú pÙ¼ìÇõÕ'?Õ¶¸L‚ÕVøTâþ9gq÷¬DX™ Q[µ£e¨hWL®Ô%n4–˜uß2êÝÒM§…ά3úŒün<(¡¥7Š2Y3^»‡ÌÇœPZ oPU$¦SmÀDÅØÒþ˜0ï+Ä'&ÀË?Ñ}séÉ ±¥/bðŒw¡VÛÓý×½xÄkÌ,m¤¸á-y›Ã#x"îé2U’ÆÖÔÔ ¸Ë úÛs¥Û-ªì»aæCK(í-“$κZxt½ ³#Mhhâ„á3¾ƒÏ±_QB»˜Aÿ@çð\ö…±ê kÐ{Ø?èØ¥ÉK±f ”*:³žþ-â~AaµÁ]GÂÉÚ‚0iòo·RÕZSJÏ0×PBN‰{k'áú­%Þ@§DDìô–­ÖnñÍ©O"yõ‘·ôôË{5º⭓këaK£ Ò¦XèÖÕuTŸÀcGJ¢7âÄKôQôUßÂjíuj9g 6º£ÎC™xi}´ÑØFrKæ¥Àçïå:,>ëQS7'¥Ñ{UdÝŒµNe*X‰±btƒåðhWL®˜¤*3‚¨ñ•$Ê*ÞR©ºÉíS7yy_X¢Û¸öÂ@Ï^T`e§z/ Îp8cþ‡s=ah1Ó(ÞIkYo¿(齆?âý¢Ž½ n‘uí¯÷ù èDwÃo– ë=[Ú‰güÀ2ûÇoq›¨ãë‡ôô ýwê3‹L{x¯ÙÒBK~Zfp%H6öG‡¯W†`ëvO|øf,œí[WY^¨Ä\Ì,æ†H¨Ä4=ñÔñÎÊ*!îõÈJOÇÙ“ñ¨&~;RR±û…9¹ÒA„¯LW–—Ü#Ö2äuùBúe1ŒžÖ¶î0W›\‚CÃòWÖ-ÿ6@@ÏØávؾɯ\»1¡¡–Æ¿M°iÓiüýåér¦ùÝ­ ÔÅtTYY‹¯Þz KÞ{{˜Žµº `eE9U|jñÐCKaF¶¹.ê¸ð æèóÏîB@ ØðµÂoQÍÜUbÑÂ=øìó\蹚\Er)‡sdþ*+ÏãÁÁƒ›©·ýfUÐ]¡Fc‚ÓÿÛ,ƒ!õ"ÃZJæ?<óÂ[º› ¤$gqÌ-†`”Í ÅÕd„ «°á»¥ØþÓª9µ\äªHkþþ÷µ°¶6¿êfSÅêoLƈ‘Â÷r[ÌÆƒD‰Œô|<ôðb\8/6ËÍ0ÞÄg }Çge—ã⺵8¸mK‡Äg ûª -,؉ï¿;Ü,>Ñû‚—^‹©S{¶">õXµÖë×øbÚøóF難ý1¹ çÀmt-v´V®`¼IZk`~o#ÜpW+(5/†Ê´µ`%öîMÅÁC™èÙs*¬è^HšJ6¶ øù1ˆ'‘*>W—6U“±ÞĵnAªÇ&Ro«X¸ÒÓObûöHNÉ!“ë΂m±¸ê±uÛin„ʉÃé\Pil~óÑ«—è£8"m¾\ã=7þ»É™`šO´§G›#GV`ëÖÓõLnóOÜœ\%79Kgá0ˆ>iC%Æ»Éw‘ps8ìÈóP¨œ(ꯛ§JŠüJKsqð £³íO!“Ûp¹Þ›„l+e(i„]€ß¶¡Ë½høúvkŸœ‹11utG/9Ð'‡×¡éªøGõ!±¡§M%s$jÿþåˆM$“Û«©¢×ußTE•P 1>šnÃuU|’™Ü›Ô›U¥XK;èzz³@Ö ^%æÎ<‹ñ£Ïß”°¾âXÈÂÂãÇSoÚ7„D¨Á«åË&!`J ´cÇWÈÈØCÉ[Û.J¦&â¸=wÞù>Ü…Ô«ÉfË ÐV]]‚œœ½mªn"š$tÕj3Äô›‡!Cæñd¥ACåË&!@-†û>S§6KÁ,š,x‹3ˆNµ› :úNŒ=_Æg á/𙕕ŠÓ§·Ü|*0yÌyt ˇ¥Æø:™Émá ‘‹µw0”¦c\ìË$½³›ÕáªNqe&·e›ƒæ¤Þ-«¥5K‰c÷ 2“Û2¸ &W袓D[HòäyØ2ü‰R‡‚vkÒéè.T¦«×„³›““Ϊ…@°!ôÁh ÌW¶XþÝãRUxÛv.­ï ôíÞú¦|¹Ò ÖÅôC(ª¨„’Qá,Ãáî䄚ŠL\HO!~òPQ­„­q¢·g8,-ÌŸ‡Ü²–«@R s†ôÔ+,áås3äfE^Yqë¿n`ðD£[Dƒ1Ýsì\ü7ت£'?;F=nŠ|@î…½8•”ÿà`èkª¡£3ÿšÚ (ÞÓ+I‡¾@…u?¸Z›¢†óMOõ2­–Œ„}xÚ›áè¾/¡p nÔéJO7wZé°q ¥[D»&ßmL03–¶\ -´/#õJªj1Î…sµåÕ•P™9ÐG,ŸŸÇ¤nÐióP\”O|ZÀÔÌ–ë‹òó²xhh [;T”dQU;; æ^@%«56ÐX:ÁDWŽÒʆR¶…˜¿ùÙ°´aäÇÚB”UéáåÝ“ï‚ c O¸ -SƒqC2¡Q·4×øÙpcÁ«ÜŽv=bºà«ÅQB´ö˜””âžÚñ1Î]LyϦ©¸‰²§÷¿ÿ|8gRÓHkº2 ›W߇½'OÀ„¿ó.ü„…ŸÅÆûI`ɸ*˱ù—ç±þ·QCiquq–,š‰%+>C­ c‚íÂñbÙÚPPYE&Ifp›FB9™’A'’÷üg/$Iš3MAQAîH_•ˆËçáëe/¢¬Æ„0§ü—¸?ø)ÖmÛ„ŒóñÅg“±ñÐA˜¨• ¨Áµ±"Û~|g.žGƹt:¶ÆÇsa¦Ä\vuÑqüúã_œ›ÃßM´S¾Ý,”¦JT\<„ƒ»–/õ‚¸&žR»Ô³‹p a3.EfN2ioòí‡c°jÛbº4¼€=[–àì™-HK?A#ádÞûO|ðáÝ8’’mÅYÄnbþÙß0ù¹ü½óe|ôÉC8‘–#‡&^-ßn-¥¥Âh;/e}y'â²JPYx‹¿œ„}I‰(I^‹¿,D%¥í©§`߉mØóóÓø~Ã7Ðrƒzlç+øò›GQÄðÑG×ý'Î¥ÁT—†-ÛÞAâ™ÍøæË©8”œ }ùY,[4 ÛŽïGyævlXû”Ôò½ç¿ÆÆk8Gë$üMt¥ nëñÝÊüío}PRnük©ñ·° P(¿²cBÀÚª†!ÊÉ(ŽÜ·Ÿuj ,ÁSN% m­V §ÞèÑ äLô £*–V¥ByóŠGj"Ôc]xw%=úbâÌ#'w2NLÀð¡Õ(ÎJ„ç¸cÒCй ôë„‚äƒØ¸ åµ@_œ s‡7í5xÚ¨¡ Œ„®*_ý°Ùy%ð²(ÃÅ}':…=Ža‡¡*c2’‚zăž°R“pÞÚQC^@DPŽ&û¡ßˆ?ÁÆœ,9×cJBí‡ œF#^n<2“Û$h䌎%îuc‡_€³]»f¼",A”µe)8qt7´â¬ÅÄåÕè;d.LÕVHOÙ‚½»RQZ¡E§ÐqðótEIf,vÅ¥ÀÃA‰b­¢{Ä@mŒ™3þŽ÷?|ËWC_QŽÑ“^\A¤t&6qÇpêƒéXñÃßàP›è‘oÂÛNjqU«D/!ãìüôãóHÐÀ!â~D’Ámꈽc šî®ò"â)á)­Ñqs¥BiI1¢‡ÿ ¦<ªÌNÛ}±<ª,-‡çQð÷óAUÞl?| n–(¬tDïžÃà>SlÅ+^Dpৈ_·A}þ o{5ªjÔ9é_Hxg,VoY…™=ìq$Y…qwΆPû­5±Ç¸Éoâ܇³°nÇL óJÃé\OLŸ=HR“0þ%ê†QpÃ(•ZdžÛŽsé™ 1«Bey!œ:ŸÆŠ*ùˆ?¼ g«ò¡± AD·a°`ùSG¿ÆÅJ/htY𠚌.½æ‘}©'vÿ?n;‰I÷oB¨»5];vGt” ¼É4Ԥ⧯B™Å@<4…›Q>dêØ½]¨vD¦¢¶â ~Xøà23&Ì‚ŠùFÆÿÜ0¼[³‚Ö ¥1}cÐ?º7¾Þ³i%“ÝûOØøÞ|Äv}OŽ Aeþ!¤û ž§afÓàný.~Û¿;ƒ߯òEaÖ#»a˜ÎÁÖ~4U¬Ð§ÏH|²ö[œË¾Q½Æú-³°¥b:žzâièŠNãLžFõµçkMˆ´F]Œx6(žT2~‘Q2¹bW\Y^Nÿ¸òµ5†¤¡%Ï›ÍÍ͹ØÞŽZ* øz•À׳„ãî¿B—‹_¾z¹vC1¬$öüú ÎäG ïÐydÍk8/janm…”ÿÆöƒÛ1ÿþ×°ÿ»§±á\:|ƒzÀÑaz’É­á¢ép'¦ ÿŸ¬z ãïÿ ^&—â¿SÀ3ûž˜=ý%¼ñŸ‡`ýæú»]Êã¦VAFxʧ8üV öÅ÷ÁKÓbäUÕ0¡®òmbR†ß–?¸?Œ?göþ;âjÑkØŸh;VKÝK4VÈ8ó-¾Ü³>´ ?½€ÕÇ‘1ê{ëè9 :3S ó:Ž%ŒÃ—߉îý^F_êd$xæN½0õŽ¿aÁÿ^Ä—é0yÚ»pU“‹#?6ÞÃ0eÊ“øbÉãÈï:Sg¼kîñ(ø—ÓU Àý%²N­Â’ÅÿCô¤ç`xü¼öUô¿{0=…kFJxyœl¡©ÂVª¥W|ƒþŽÙø~é³(±é _/¨†ÂßÃ]=ryôýÝʈõõèªÝr¿-ôÞÉÌ*«±{ãóˆM¶ÀÏ~Hý\EŽI¯„´Ý¥Ø¶þ/8^€G| Ü‹„ï*à¼åÙ­AK{õŒA`OJT×ÏÂñSIÝ}<:Û½Œjµ+llt8µg?<:÷£)qh㇞]cýÞ…È1Q}îů»ÿ'êÒWS—>ý\6zL D ]Öût».?-Å‘ãGqÇð¡èâ€óJêíÉ;÷ÃÉ#˜ãЏ󨍒SF¥£Ä¶Mœ^¯Àˆ“‹o£Jiiixþ©§“M†„3\N7=áhEÿÃÿxõÕÛÖXVŽ%ò ¢³o T¦Æ¹Â þ»2'Ç s1÷®?#Ô°®¾ëOJøX¿àñèÕ§?ºúû#és’«BOJyfmæör¸ÒhLLñ©åquu9ý¼C°gÓt ê{J D÷%ÚTMCˆ|xûFROpÁ>T¨n\}‹ ÓáäÙåç±eço˜>d¨ááúBò× @<Ö–&`_êI Ÿ÷?t ±‚‡e9âS¿–È™PIðòÑÐ=( ^‹„ eè9ìÄ&TaúKHi®‚J¶ç&d˜&޾Ÿ®Û„^½GK<ƒ@0»Q »×7ȶÊãSýÞ"m¡½EÈú¥° œ†w[T5Èÿ½¤|u%(_eP¢ï é:ƒú##Ézº@IDAT3çâ~%SJæ–Ægfj/DEÏBËÊT,ß±ãž} až(ðxΞJ ­0ò䦣ì V-êÀG0cìì:’ƒ -¦”ˆ¥üŒúœ1zö:Dú9J›M½ž„¡¨I Îìü7Ví8Œi÷nF°‡5d*º° ´v7ätZ“–ª( è×+ûwü„!Ó)]§!e#R“ÆSŠ›àN´µ&èÞw66ïgΤÃUÁ1bY€ƒûvÁ=, …Î1p3¯Û¼¨,B9¦zãçÝ+1º·¶6ÈKÚ‚¤3)È,H€KÈD˜’FæùÝkÓŸÕZÒ%ÒÊÌŒ>¥$÷ô©SX¾f BÈ”ùr[#Èéú! $·y´´Þ´s'†q›2¹:|µ,?­ñǪ¥›i¥^N€J\Þõöf=IÉN® e%ù\èì ¶r§ÆÂinöÄ ©ãGEÊj\˜R’àÌ#kjyÒÂצ°Öh Œ%„ž§Š’¡ƒ;ßC2âÉÇ×ÿƒe+þ‰Gï} V´¸W(k·;Ndãþ§Ö`ÇÒiX¶ô8=ñüÈëø¯*÷~ؼC¦ÿmü«øïÚ‡áã±}B|É|ÕI˜n:B½ÄJ 2)U„JíHfÆànŠ9ÜI<*UNpv² ÆLiUmSs{X›[ÓÐGÔõ¤OOÅk{gXZ;ÂL:"lpÁq¡ ÷''”j dY¡Ï{)‰q£²‡‹›3ÇŒ5cq£Aþ¥‚òEcPÐB¾´,UÜM˜Ò½¥¹­+-åëY!‘§¾|5N['wh̲ˆ?k˜[Z¢ÒÂS3Tp“B%"ü²æ¯ˆË Á“}¶jê÷r™š° åÚNð¼€+^Gp¿bTLoêÕBYSŠøc+ày?T™¿0,ù‡è>â î*å+*³wòWø„ß [+YÔöZ– ›©aQwaç7oá—_´ïÿ4;ÿ…Vÿ››Æžpwµ¬S7a¬°µr!cU—„´¯ ›ý…90Ï9F#BX[:Èln P¯£8Ð?`,ÖnzvtAgœ:sÁþjÂOªê4œKK„=•Ô÷ï‹…{нôõ—…¢‚b”™Ó@°¸'¬,,%)ì–›ÑoÆbXÖ¤ÑË‚Õegñ[ìèÜýIœ\ñÑöŒ\5ikÊ‘ž¼™úܘšŽ–=ƒt«a¸#ª2SOSe©)§V#)E‹À^ìˆ,ú6oœ–¦BíìU·ÙäžÆ½óH¸Ùü {NÃß&RPs~ÿ"†¾ sÎC¡y"’^aΓµyøí£—à>oÆE;àЖ8‡™˜áB7duÅ$¶ÆÑg|,±ëèA<÷âK𷺈·ÿû*‚|•ª1¿×Yÿˆ‘|Q·ø¸Öÿê×_;h$mjºÆÉäַה̙™ð[$§ƒ¥f·¥nC°)”!£ÒÈ÷¦I¯òÄ”ûbÓÆ÷±kç2XšÖRm’>.džÇ£8¯{[@†I)³ß†§e Wۢ߀!\ÀÉzÊs#£  ýÆóú$JÝ"QC‹ßÑc_€‰"ƒqÇ7P•¡¡Ý'@Ã…:¯8UeB÷óy3©púäf˜ð¨Ô6h ,lôÈÉÏ"Ñ-BDŸÇ)]® nÙX[yÀÛ™>:å¶á@»t]«·Åè9_@³åÄîZ aC¨‘àåäK=M•–’öÏP\\…‘3ÞA p,E‡¾ƒÇ!?k? ìàæh+1²¥ùdRKìѿ׺~;€[êëÙHyz]ÒRöÂ.xœ)uL:w.”ï‹—üOuÒÏÇÂ#b6éiÎQí$¼koâ÷RSå‹& ôÚ=CfãØsp”uC-¥â´ƒÂÌÝ¢§ ôÜØ_µÏLÌ:E¹±p›gu™ÐóèD7c âÜrµ¨D"=šèÄIMi ì\úÀ‹’°"—áëçbº%+ÒášääÁ¿Ç8 "¶>S0Î>¹iÇwk«‹‘]¢C`ÑôÌæÈ¸lƒ7JKlá`F)-iœÞ̃?TýPÐu1Ôawbè`'ôð¥jI!ùuóÁÃÊѧk(ô ó2 ÍÃ‚Š»¿«‚QÁÔû?‡ü0¸[òw§ ¬³Ñ4î56 lo_ OrÉ{HÃûÆxmÔ¤4oÅì•gðÂO¦JÜ=# s§ã‘£àÊŒ[škiŽÉ3¿$ÃYÊŒ‘’¹“ºY:êV>.ÑUV–Ò’×jÍ­Q}a¿h–ÆUYøU´²ƒá$³aÀ)|®†(‡EqÊs0hµUdœÕ|†Ï‹|aü8#‚&I¾Vk˜/ jT¿. ¥ˆgz'ŒÑSro%Ýc”4Y“¨é©)È–¥¹£'~À£î*Ô@R}ó(: òiDÑg-À+TV<æ¸Ó!¬×ˆ¤ÿ0Éè–81bÙ¹ôÆ×¾¾j…A.ó ° N]ç 8¢Žœ }_!Å•æ<ÿ˜˜Ù (ânh˜Ï¸– 7Xrjjõjtíñ :‡ÝCøk±¥4žÜ¨ú`ì˜Hò»•œ§:˜[XHz™—Œ7PR ©¡¾‰Žzš^@7"EÇrj¬  9NYÉ ÍÏ¿¿D‘j ç/áGŽI¨ªL™-­ƒwu©A¾qïÙëÛÛ6_b~£´Ôr¡Wý‚ˆ›jžÐ«¬#é&,Rò]Ýp:‰w*ÍC1aj¨„SÁ÷ú‹ÄÆ\bpëÁ!»b. ü ¼Isµ>OØQð)–t]»ºçä^ 7 Ã륥—W*”Tê’À §«@u£ÉPÎyÙ”5Ü”¾ëêõHuòÁ¦ê¼ì±6üááRŽ ÿBnúŒ½¥\2ÛNò«eÜBè±ÿ+ÎÚ`ÚäJ:¹jyÄYcq³—a=…’GÝ²ÔÆÈ±öÇæ æÆTˆÁ“¾Ââ‘çÜ aü Ù{r{ß©¥—’€ˆG0?üqi©Ò0$¶` ˜s"'ã…€LK[7I©ÖÈ/R#*´Àh=z,Kr ¿;8tX¾Îy¡݈IÚŠí¢¿ ¥šÇ ¶Ðh¬aFGôrjŸP02¦j31þäÔ! RÛÂÂÜŠsÒJ:Ii}¸]Û,ÓÒÖ¼ÿý&?ÖE¥bM2îž,Ém-¼Ëõ=ÔtÛce)tVëÅ/Fßâú’†7i/€lãvÊxlc´Îëe©mëÀ±Mj‘ç`«€½´D… Ã[·Ju7µ’vÊäRI»^áÅÄè)N][Es•7©­µâ”g¢í@=æ¦ææ+WâÎÉIè•GÛ›§,ß´ÞUó­“se´o\©Ø¶½:æò\¼6;¼Œ½}×í[QúfÍIztÏ‚•4*ãµ;&·šÊ5E¥…8_Rθì*øØÚÞŒñPØÑæ– ¹°Œ‘A¬ä`Fé»jª%3\Å…`^›Kœõ•Õ¥Ø~& æ®èBÏz7‰‘n®í#On]òøÉes7ií¶ëi&ˆñÕ²]};$)ba3­ƒÓÍ"ÆÍ¼¸Ù¬–2H´“ïàhl ~®6Ö›õMÊóKàæjm“çaœí2ÖT×>Ÿü´„®^mìêºöoî»#QúÔ=k¼ãF´¯ý0¹ÄXYe>îÞ…“:kŒ ô‡¶4oîØ…>zà©îþôhD+ùÓ#'âݸ< î쌧v"À¯/Þ@ÿMÍì~TJ:‹O25X8ª;LÈÔ Ú#°_9–“júÀ|oûVxõ´Å@7:âlç×>«Z剒r5*«L(É­¢usëÁ¨´4EEõÖòM5›nœj颫#sH¢o´75¾®¸¿ÃE¨WVü~£¯D4¹’’LºX³º*«cÔ+ðÔ“Ð%6¡wŽæ’XL«µ…unÏ ðÝö©²²ž,2I›k‹˜‡Zâ¹£[sÒ˽©ÔÙð7áŽ^Ð.AÚ›¯?}sï J]QQÔ|ЏQŸÆoT|c£‡ ø”t#ØTN=Š‹/Þ´±]#<‰ðs)€cS 1‚ûÍS/#h`]èB…γßûe¶Vyᛩƒá­ §zÚ[cÂòŸQ¡ž†WÂ=$‚×UÐ’Ø™ 7K¢¤Hbúê¸ø ×<© o"_Ü»£¦‡ 5–øœŽÏI;+±°3I!5µ%øâðqÄô™Š‚]0ÌÉ ÏpAdYDn eë*mîð-BuuFF$ÃH/ª€“¥Ô쌂}Ò±°Ô^2À6ö¸·³öPWAêë}0ô¹®vù¯Àþ7+ðëfo|ùÁN¸8T¶*”4᪈¡9!ÌŒ½¥Å¢1¸‹±Bf¸$‡þm…cÄŽ‰)ýƒšÖ CÛÜâ*\*çr®r^Öûm j·âžŠ;'' Î$ZÄ,ª›£½[¸x2E£‡]dY5i¬dû½'9ù!‚šéˆ#M:•~1ý"š)wó³„oYA ·mû‡/á4lZ  Æ\y™]Þaq(ð&ü]ÛØ07MŽiŽamuaQLúuÍ«àMC¬pÁ&pº{÷ˆ?ùSóød+>k(ø‘|{Ý´VµeÅÝôOnkëL|6côÅu¥FøX¯Ì#>[{ã©Ç†mÞ8“`‹‡îM€¥¹qoôÛ“ËAžvá >+ÇG€7Š:‰¥¾þ]ñlÔQ¼ºoî ˜‚Cgâ¡Ô8Á]]ƒÌj:W÷ðfüh î:ªqêb2Ëjàhçˆ;ƯÄÞs©01·DqyL-ìÑ×Ãæ-^³„ÿÂ*œÍ.„–‹žFc [÷Ë(¯†šþLÑèÝ=»dÞý=‚ð’ ÃkæçáBYÌÍ4T±¨EÃAª¸+s6W!½¤ jsÆ1Ä4FË1¡„!3+?’‚ÉÁ]ÑÓÕ NÔ9š•…‚ªJ¨ÍìÑÇßó™]m%N0’Ñ… zzûÀÝœ¨mvajˉÚïÖ3Ò“-ìv¥ŠGkñZŒ×çS²Q^Á°­R÷‡`Ž22бyKúE¢³Ÿ«ä ¾- r³Þ)˜Õ½'Î!)-ƒ8’Ñ5ãPlœI’îêÝàî‚®]=Ù¤¶’ª)0÷®> £Ë BDÑ‚´sûEn<µÕ·K J·¿"÷œ$Ó“…aC¸ ^m3&|-wǬ;{¶aGk¥1ôøcƒ‘™YHFGòTÛh{„P¢¸¤ ?ÿœ†ÎžîèáÇ5¢i†¸ÑJŒü¦ÒT‰³)Yˆ=Æ€Á<éóô°ãÉCÓ}ÔÊž#b0ft8{Ö²9psA C'ž‚>öè†A¦šY3øäà#C§ÅÚuðtddÀîèn±qšssÛ|ójt5#‹¡Ö÷Å#4ÄÁÁ\;š¡Uz¨(iíƒÉ“#Ù¨¦ñ~í-Öcõz_¬]í‹9³’ÈäÞ<—koÛŸhL.÷æçó3Q`ç†(k!%3t„z%z¹{@—p–Ll*>ݽe6nèA&6-3ñ _lš=égváÓ¹âí‚•»âµ)SᔺóVÇ",¨3¼Yíê¤ ¼7ë~Ìp·¢RÃKšþ»¥Ú |·w>;YŒÇ&à E.žÝ´£ÆÌèØ±n=¦~{³»G㩾½Q“•‚¹«×À¯ËHÌö¨Å_6Å":lþb…g7ïADX zWÆã“ ®Øo ÃEÚ¸tW0bRT7d'Ǧjk t¬À'ãû¦s¡Õc_Â>hŠ”‘Žï}ûáëa‘”4uôc›¦QÓXŽg ºuÏ¥+®ÖœðâM:t óÀ‚OïâõÕÆ Û¶ž d" ÷O€yÓ€á°knû½gfŠçÿ½ßüºï¼;~d䯾h &JÀ®µqÓR0ê0rT?][ðç½V‹!ƒÞ‡•B…ÿ¸»ãIä¹)óØG(WUáË…÷À”¡I¯>¶èÛ‡N<1{ó­i-h« R{cû‡ã­gfP碃ÑK†CüjåNIHÅsÏÁ!‚y½­iëyÈ&^J:J,-ðêk“xçjtU‰\†AÞ»÷M Ž Áç¯Îc¤c`Ô/uæÆ/ˆÏßvÅc×1®ôǽ÷fWçᵭ;'-Ìk`kSMÛÕðbxÛ}·&—pä¡…tDOŽ÷†Ré‰G2üx¹b"ÔrÏAx>¦*²ÎbÔç«°îÜyÄ< ­c´Ô1 ¶å±$¥©A66ð²sÁS#&aˆ› ̾øg +¬œA¤[Âäò8O¥vÆ+ãÇâèÅå°3·G”]5bº Â?zwaŒj-~žç„¯öîÇ[û·á@^¶Íš‚ÿëŒW.–cjá8u&YVNˆr±ÅXÿÎx(:qGÒh¨&ú¨Æ¨pôϳÄû“&µèpG»èî èb®Ç¤BÝ™ ˆ0¼=q ÒOlÇü¸\T°ýÖ ÁÔvcÌHÞl‚{f&bæ¤d8Û1&c«+tˆÉÞ’ Ïã¦ú±%I7ÅuKÆš‘@±EÍ` ’[1/ëàr5Ø\-¿Eo¾ÁB-_~Ç!{Ç>r¿Û¡’ÀŸ¾‹O]_~Z‚£–”¹™ jy;Ç!ŸãT«7³m·¸nÑ@¦–ã°®ü-ni3¯íiI›n3ºz $—.šakg)©¦p ÓÇ'ÃŽ.9[-mÝö¶&— ˆ¯«;¼JN`[f0œ`R¡£´òøÅØ[9ÃÃB* ìÍ)–e¬x• xX¢¤¸Uf®x¸Ï@ŒsáÑ)×2¡‹2wt¶7‡Š` £èemŠÊ«Ë]ŸÀÜ sƒ=ðñáXØ8h0$¼te…Ø•‰¾!xjŒ;z;`Ö¦$–Öbd·^x÷øø<1’û°*lO<ŠUÖP:w=EtuuƒuB‘4xT<¢¨å18eÒRåBmfEé§=c {ÛÛÑ™|J³=  ÷Ô»²ŠÈrú¬­ªéL)&ü È7dÈ! C@†€ f!Ú©ú@q€ÕbÝÎfë»™™ÆßBÑ{2´î®Aøk”;lÚˆÔKZ Z2˜GNÆÂ¤L<2` É«êjkTDYîb“/$`W¹9zÁCYˆû÷âD^!v&Åcub&Š©hŸSYÉ™`šáæ”k‘_RA&¸åi—©U`|ï0¹pÿ-µÆ'[èh̰`÷,JLGYU5+st²u CMžØÁ„yáoË¿…Ö+}L/âÝCÙ¸'ܦæ44+¡zB9J«zUY¹¹H¡.o¡^ ]y>$¹<®PÐ5ÍüQ“`»?=€óîî0£Ç…#IY¸wø Ì t"3¬…™B‹=)g°Ü²'hT6#zú»Ûãÿ0Äm‹Å£[²ÐÉÊôõƉÌ4XÙ8 z¬žZSTh¨˜_Mæ×¶*!.m!òi ¦¶òÀQ¨rõ„…™°l4C¤‡#¾;´ùYŽH£qÚû£…Ì"’ÅØð–¢Ã}Ýz"IU/Ó@¸“¡-§Û™ð³ªÅñ‚bô¶v@?3,;¼:…âýðæ®£x«ø<Û§Âj*£+òµ8gsóHÃ:3=ârŠááeßñŽàZˆ’?«ÅWß…`é·Ø³k5¼]iÛê* |«|G†€ 2dtè°àëlßé†_nƒƒ]ëz*jm8µ&—Ý:”†Î2SÊŠ% &Åœ˜JfQ#zA%sõ«•L ‰@_ ïÜ®– QUƒŸ.X~§?r)õt´²Z©C™e$úwG5U„–“½È×ê˜w ®#ÜÑÌ0–ϳ1ÔÅ5UÙà…‰SñHI)²ø~—v°‘ ¨f ’­½–Ìrƒ¹Ú ‘1\¸w¡Z­±ÇÝãOT9ÐQ÷ØÌÔ„íš$µÛÕÆšFf>XጋôààbC÷>T×åS¡Ò@¦-°{_Œ¥³´Ãêh:fX_ï7a$¶K-Ý»\ïkŒê9å?tøÊ{×úÛ¨:x›4æJ]Ùí«å_Y^þm¸Þ®–o½èø­hˆ‡†×õ¼a~S×W{®±|#º—™nޤ8;hÛÁbÚ~˜\`¡DOÒBmNt×U§TO¦QÅÒ5SJjv\ÌFuùtv„ñ>dpµõ–TA0SiàaðaÊÇ,Íê|Ñš ÏÉ S½²~Ã[-¹Ö,%Äó%¶¶–Ö°•”hù»žÁ5Ôe©ž"D»ê-—YĄ̮A§V´ƒ –óѰ¬`Zk)5f| ‰ñכª®uð0XÀˆ¿ü/'”˜8*Á~…Ôß6n—'†·ì[J±Áã†ÈBø·¬Ç¹ðq*†›ž:SÜ.Ñ/4}C“ɯa9Áê =%AkÅ_ŽÇ–Šã®–óDºÇq'yyBïŠõ™HúWôˆZ?´Ä051Œ·–5V.Õ$å´JàDzŸRa&-$¼VÕÛ üŽ'"Yà…¸aÃMIEª%>%Ê'„—òýGzžã€·ù_ò#.ãPY«ü.*jôPÓiùÁyUIÚ­dÔ1¯D’6ÜÄ7Í* äs•¶ˆÀÂÇ{]>KÖã\Á9.Œ‰M¥|9ñ¼¨‰cBŒ éŽüçÆ Ð8->éÅ|0Ó‡h’~®AÂÑ%0!èfu´–Eøœáž˜c¢QN”ªå·”xŸN %ú-îˆ|Q¹¨×”×µõcHàØx’a] PZ®’<;O»oIûbr }àà©cp 7øÍ{ÎöîxgêliÕÀÆB0…Òü½Ð•ω߭™þPŸh'_ð‡ûõ/5Ü7|ÚÒØï†Mù—ÊÔ¿£©g ÷oûoú÷¾ÈO&!!È“1ëDšZª©¼õÁJ$(ƒðÕ+#`FýoÁ¹äffaÛDd—ëafa^áÁðWcÕþdú„Ö#$ÂÖå%ˆOσ–›D{7/ŒòD\|ö%åÃÆÚ ¾î–HMÉCI²…ƃ‡¡&5;N¥¡ToŽžá¡èâPOõ¯³òc\Ù¸´U–`Ág+°5ËKÞšžêpÅCnj*VîI&QÀÅË“úøBEúvhÿqN/ ©79<9"#ëå€ñ==é «{˜üb1™" Tˆ“%°vîÜ #£< ¥?îíûŽ >« QÝ»¡°™¬+h¥Œ—k‡…%‰‡öàá‡ñô³waBWª‹ Ά÷µ¹ixeQ,‚»‡ÁMCšMF6¿¸eJŒÖ¾åð·%‡Ñ»WWؘòLÎD‡œüb”hÍ0axOØeÆË?&`PßžÞ‘u¢#+·ez+L7ëß7¸×Þpù 1¥¥t‘uáô¬9˜GwO„»*°/>µ*{ô sÂñI°p÷¦/|Ÿ)„;n² Êàéëu5bÏdÁÚÑ=},°çp*|‚ü`«ÏÃá³¹dOÌ 4W#&ÒçNŸGZi5º„À$/§ó”èåŠø 0uòÇô˜@#Z±¸‡a}ïšvÂ˜ÛØ×ÒºmGãdú¬-mÐÅÙa®ð±Ò4`;Bå>Ü(´BïºRÍýA;ö‚»w÷è¼ôûò{&TYqª.ÀùÌ•£ ºÐ`ÓÕ¬yw¶§TÉ>ªlÕØºiRjÌáåÖBÒ7Џöü¼bÈ5ÄgÃkÁóXXí¶é©9T»"Ã"I\°¶w€)õïŸ~g N•RºC !4 O}1 ¿ì; PÔ^LÀÓo¯CZ÷i¥…¥)ç°ùX6B»ú¡0éž~o# tF (ñ˜ºÿbÏÁãBH€åÔ %Ñ\ƒ¹wÙ\d~=>ˆ@Ÿ–Œ¼rÊÑÅ= ïzœJLÅù< bÔ°³·¢°`á*¼±8:3%Žœ:ìbýÂj`ïh gzçÓ•øÏÉ´Tƒ«¤ÒY +k5ómarüãýøbC*t’¾^3í¿Ý³ sÐð-àqþD÷"ÑRÎE;gs¬ÿn#¾`à_g$؇¿O€‹E%¾ý%ö.Ž0«.DZÄܰÂâÅ?bå©2øx9`纵øæ`¥…ض/n°¬ÈÀîLžÐŸÃË ·¢Te'ÓükÁ÷H&NÞu€¶5¦¤Ù8›œˆ| †Ó]©¥mÿGcÆu2ö™jûæ4Û‚v°Ú7ÛþË3± ñ‘ ÷å°¹íé°tMù[_z¢axa'ûã2zΨ$ãR^\Š êT’ÐUªÔQ¡C0¸´* ôìáK5·(±+§ÎyõÃÕTçé9x Þ{t4²)]ú9ކ‰ñ'Q¨öŇόEˆ3R'7üù‰¹¸/Ü+~=€Œ¢bü°%󜉹C:ÁÝÑc¦ŽÃÇ÷tg8䃔 –bßž#°ž€·çö†·ÕäyÖÌÌ"žˆ«Š â‘-/¯@i¥%< -+GS _MÍУ‡?U›LQË(ˆ•ä“4ÖvøÓýÓñôŽßÝŒØH»ƒœ ¬:U€'›MÇ÷¾xú¡é¸¿ƒRü´|OYj ÖpSòÂã31€‘¼^|ìÌê\‹/VB97x9‰§ð›Î ¯=6ANBeʈçB3н%Yœcz#l)e@¡ŠPTTFÛ%ñ¦D%èTB¨Ã)dïáŽHJÆ…_ÚŠÊ*”WóÈ™s×Ñ˯??ãû‡"º»?.Äǹr|òú Û™À% ÿ|v2ÆÄ#:Òq"WáÏ^H‰ ¾á]ðúŸ'`$%¹½Ãݱ'öý¾áó—ÆÂƒòc&g·OM½äFi)qgçÑ/Ìé† ™gQ¢öÀãw…—òþ·ê¦M‰ÁÈ®.(¡W£q£ú¢ÿ Ü70§¶ÇÃÒÛ/>0§ã“ðÍþtŒš2Ñ]¬pî‚ ÷L„îðšrÄ&`ÐÄAÖÍ ?n؃ª <;»'ÌIÏml‚1}ïO¥›êï-º¿ç°–¯ä¼0~eãoá-BšüšŽzœˆsį«}ð곇·³\8«‹sðÙ¢ßcjC£É|ħi1}\0ÆžA••¦Ž‰DúÞÃØ“VŒ‘GÁOe†³gãñʇµH¥úA@Xž»36ä’úMÇN¤à¥ÿûÁðÆsÓàfJ÷rB±V°Ò7¼òÒ5!¦?q}† Áë#ù^æI¼)æÞ;û|€{Ÿù‘añî}`BFLfŽšFb3RU‚ÅßlÆÉbsX›#>³c‡ãä³(ÖØbüè(TŒÇ¦39è7rúÑ6-5¯òÕMrá„çç„‹µ-žb6Îÿ // Ám¼¢cXg+rÎ lbÁ€4OLAÿÇ¿Áÿ­ôGç¬4½¼¸™#cfêèÿ{b"úýu%þ×ÛÚ#1ƬÁŽ‚»®·Yh¦+·u™¤3à³_.ÀÝY3Y”¶:  ƒ>“ ·ðpLfˆÕoܽ›7ž™Niº‹¿_‡¸­dkqÿ¼qÖÅŸzï¾huˆÛþ3žüì }é1L‰¤ºç’ ÁÌ G½ì}ëÂó‹OãÅ=…aAÄ1õ‰‚;‹|ô U¿åûïðϵøÏžBon4;Zd/ö´URkÑÒÙýÐgd_x-þËvŸÅóã0Àö'¼ýëyZÂèz•ùع§ÓŸ$.‹˜>±'ÞØ¹«æ`NL_ôüt;–lÓàøã¾¨>—ˆCU–xÌ…;+oÌçŒ×~:€GGγ“"ùìüýõ?ÃÍÖ‡ݬÊ+Ž&T]2ªiªÃ’øy/†Í ñ¼pWZ' o¼µr%K’ÛÊÀ‘«ëXptªäbRBýD.6F™H(LkðÕ‚°&Ù÷Íî©}\qüÈYØú PY€M{sèG•Kô qE5IwÌ›5ŽëŒUKWáíiõªÇxä®Q°ÊÎÀ‘5Bœ®P1 ~§Sh¥øâàÉDØy9_nL@c•µž›×ÉgÎAkaJeÑÑU­‰¶®Ú„ÿî­ÀÜÙ0sˆ/Î9 ½“7"ìÊñ˶óôqå±5ÕLlÑ?“¿E{`δaxrZ7ìX»o¬K"¨u°ëÔÿº??~±j}ðØx±·Ld‚\£ðÆœ(|ùÁ·8x1¿~ed9ß^½ñÊÄNøÇÿ}¼à^˜.˜+£Z9ëúbL)¥-Ï:‡§ßÜŒ€¾=pלxW¤áHj ÷óÇ)2!tßèà„’´ôéÓ…Q/iTTcŠþ£1ÞPt¡ôÇ?؈´bÒ¥%â‰÷¶ `À8üylTÁ 0 ˜ï»˜Çþ³ý&LÅüÁ”àÑ÷ù¥|>!îûü0&Ìœ9Ñîd~dÖèÊ¿­GK?ÚrjOÜ9Ä‹:Š*¥ ¦ô£ç#šýñd$÷TŠýýàoIvŠøtê©î*¬þí$àÌ@PÁ>PS7[Ïùw"ÎÎpó}uäà˜<ŒMg‹Ö' !ÜèŠi­«-Ä/¤ãû¸p~ßz% äL¨WÞ|ÉLî•s£ÿ6Þ½Ö­º æÍLÀ?ÝÁ°¾”RãΓR#T•c{F&úSàïj‡¨žèêÊõÑzx,õ63pøT.zL˜:î¶&\çãÝÊÁÞÎè?r0æD8ãÐ Ø™™Òj·kÖC4C>»$Å⯠ ZèfŠÁ >\8óNÆJ{ÌÛŸ¼ ;¨üI)ˆ”/ŒfJr±86Lì…=?nÀG[’QËûÆÂ[1’®úÇÚJLIB·1QˆðµGH·pŒîO¦…fC³ï›€0›Bì=“‡"â{ô”¡ðw2“ðhia‰ ª êù1>8v<•L0ëãB7bì ĸÛbTïÐË7"B¨SbÒ¤ÁèA}ÍÑÑ¡"zƒf2ô©ÍñâeíŠqÑ4b¡oo9]œ¹É8ïd©ƒ:ÃÓÉ3ÆEQre‚Îáxf^8b)•/º‡}0ÇÍÂK¤ðtèéŠÎþ^xò¾± ×|¤US²Ÿþs .:uÅ_ Ir[Ã`AUÐÓk‚¶4 /¾¶µA}ðÉŸc`J ÐV£¬šÒzº’¬È½€¿¼² ÎÑCñïù= <.èyâRÊcm9]V¤¥-ÕX`üÐhÔž iN¦«hV€%;Žã—£TCà†CJb®YáÞY¤•?@ü‘ó(u4‡¾ì‹¸ çÜ# aíKüù„…bhg%£Ç‘rî\#ñ+ÕŽŽïŽGY€ :ÓB ãJ ̽# ï¼¹Öb&ãMFÍä ÐUs°ÊŸ‡’p4º¹r‹ç…»SBýó¡R5dnq#®ö:.f6&f8LÏ•Ôÿº· ZøêÈÈš¹uœ¾6øpá8“놾”ÊRçO$ájHèÖP"DíXX˜¡†|èÁM¿á—|K|ðò]x^/¬þ~¾Ý›AP2Ç4ª¨,ÍÃÇ_oAÏqÃðùs³1À:¼¿™åtAFÉR5ÿ-ÿîW¤¹³Ž¹xf° Þ| b“ËY?_ §Æ!@ØÙraŒ;˜È(„µtVç¸MÏ€5 {oÜ7Ü_/^‰ß1¤«577ucRGæ³–’[!ÕURleÁEN$njj”4Z²SÒØC¸ º2QªÂÍ‹5óMù<‡Âå‰ï7áÀ°²Q‘yªcÄ./ ÿj ê¶Wd2°P~Ý\äöAPR!­4<š »ð·Å‰ ÕîG[&]a¾À碎’X ‹Y2¸ÐšoW`Ñ9=Þf&ü”ÐR¥%/å,¾Þ‡j–ýöËï±¶È=3 ´¤¾½ 2âã±xG"ãi±àÓ%Ø­ðÆÇ 3ªŽ>åè,Û›=糜®€@+ÒRcøDuAï¼öÞrT„÷çÉI4¾úlvfW {ggNªzüsî…vB¸Í9<óÑ&E ÁÓýñÁ¢åØUè?êÁ u1ލ0od0Žä©ÍÅøàùð*=Š@L—4ð&xEçÚò§}z\ÄôÉg¹áû#%jË–5önqîh”©†Äa}J r¸›ÖÉG27„#aqœ—Gã¡gyåêwCU·£‡é &Ã’ny4ˆ)¤obÂdD]DOa…ÇõÁŒw7à%' Ì® {Nã>FÑî&Á‡O.ƒïƒsa.æ™)32µI”8­ÝŸ‹ÔSX“`Åãng¬_¶¯þo?&Ï™ŒÚòD÷펫wãÕ÷¾‡Éü1èf¯ÀÊ5¿bUœ 領ÁÌs†`Ã;ñä'¶xp¨?’÷Æ'ëðÔóý)4Ÿ Ñxuã<ýúr¼þØp ¥µðµD4"pß´¦HsL ÃbðÙóËðÄbŽUû2 †Rt¢yÄ.’+¾€ÃÌ™°åùn¾2òÓ±*6 ~åç±hóÿÑ…24eeØ}àŽd•Â|ßQD8÷@7É¿±0q®d(ð-”þœÉã÷žãðµ G˜™gÁí2¿¬°?ÇžFfÁE¬ßu.úÀßñ Õ•›vZ1%©Na˜¾/üë+\œ3*$‡Qe.1V>0)ÈŸgWSW!Ù©úC`s“Y…vÅ!ÒºKÿ· ‘}£¡È8Žg¾ƒkÏáP¦bÃÖjcåºè1l$âîÆË«ÑiØxh3S°!¹ùùX±öFM›„Øí[ñÖÆtô˜<ç°!žaèós±ü§#˜öÀl#—¥µþ[‰–þtÚ /ß×YÒ›†©þ4“¿LÃPJc#Ô–ð]´.ð°%}¿D W7Lé× oï,EÌ PŽ£Jüë§Eކ¦!?C>'zD¸ý÷LBü†iÑx“šÝB)Ö§”¾²Zœ<Ñ7»¹0Ÿ5îd”L®““ü°#+‹»¤lã†`;iô¹1²O;iqk7S‡EKƒ±|y 6ýü3<]Êù#œždh¢F ÆOt…·æ@*–öèämK{2+$Žnxåé9Έ~u ïuÛtE9¿”* >~{6™œ Äæ»à•?O¡³xS•i¡ª4ý=ˆû(‰ªaøëì¢Zô0€D˜ÝârT”™Á¡s|óv? jŸ[Çà`¼Ñ•Kw½T¢ÂÔ_½ù LÈÔÒk€Pë´$ß&§+ @<úDDbõ{æX±+‘z|ôvAýi­ðËEÐÚÍ/þy.UQèKU0¹”ÙùáÇ}PM=ÐCÔy㟳Ñ?ÔQÊ«åbX«²Æ›/Î’c¡šîâHV:Ÿ¡‘[M5LìœðîKs¨ûÇ5Yòù]Ã#oKúBþäå;(…äÉ ˜.å_Ñtùg=ˆ¥™ ^}é~tݰ²‹ÑƒÞ*.&Rj.pVk‚)wM„{¦5ìMtž¼Ð+5&Ïœˆ¬ =v<‹°C1ch(ŠÓRðÖ³÷ÀÒ’1*ÝÔRwkFD¸7,y¢òÁ‹÷Á‚:«y:#òímíqçŒaèé†Ú=>ûÇ}0g¾VäÓ‡£#æÍ‰^]©_m"ÊÈû­@K?ú×è@±:ç,'ÂûÃÇštqUS¦ïŽ7Ÿëð@N8±Á1$Ž º«œ6alzYÁߌcÃ;/?1}CÅÆÓPŽßěʚÆg/Üç žèè1þŽ PŸå#4JW‡¿êŠØ½nøò?»`o#‡õm€Ñ–]vëÖÌÈrºP*«'ä-{N.Õ4“«ÑhÐ¥ %C·i*¯$³W¨"½2BæöNÄâɈ2‘ë‰ò‹IX¹,yÅ•<á²¢_yÌÓeH8YT|{…tÅ¡õˆ®‰OoqÃ(]’ Š{Nð‘qS<,R=,„äƒc$&&²îöþŠvéЭ[(ºžuâo¨êÏÜÎ7l€€P<×µ+ª 2q|Ë^º¯x¤ê52§Œ&¬%Ý‚8cF§z|< œˆ|&súðÞ?êw€Jỷ¸µ¢?ÝÑý)ú7¤†ùÄ‘­“3ƹ¸Öç²mâwrjÜÌ™ZØ`öŒÑDG-v®ú ËO”¢˜›G"Ø# S ËúÈš ª7 ˆéUW§„CëZXúûcF€À-_J æìàÜD¾˜».6 k&_C—*–/$ˆ1~ƒ´´á<"l<0kœ§d`&^1xD?¢” î•S‰›Y'‹ÞÙ™mÞ/ôÓ³n>‹ ÒeI…!C¢YËQ-ÅÑ+³¼Y ~L]VÔH~$µÁ¡ÝÎrÔa#iWcÍ0JI®’* aaaµW¾'Cà:! @d8£wM3¥¦á®û:«»Ù ¤¶?¬ÝK.p'&a|˜3ì„gA4&.¢¦zéRž ØWKW>sey™1º"Íÿ–àU‰õëw!‡>kâ’‘ÛÏ®êúůáÓ‚™i*‰…±9ÜÜh~Sï•ï“ù ìÉD¦'žÄÊùð žtl|6&DpSñœ4§«Î=>×\úcÔ\a9ï2´-5T(ðØpù'3M¥Ëè2ñ{%Ýnø\ÃqÔhøL^{z–#¨k!Ä7C³Ú°} _m”LnÃÊ×2ZJÌžœÈO«ÃÞØw bѳÄÜîÂ\a]/ˆ ØÙ_e-lXɵ´ˆ°Z¦Ü9SæbG›2[Ä·¢&Î?¯À®øðMž¢ˆ$\ɾ:Xý_™–¶.Š”xpÞiÌ™A—“VBŸÂ¸×R™Ém]ì˵1jjˆª ÂP˸§¥ˆ$ÎÂ= #rjßLQ3BŸöݹۤõbƒ"‚¤È©B@¦¥­‰4'»J8Úê¨>gü^=d&·51/×eÄÐãçß|pà° þúð ØÙÐ÷¤Q°ºd·ÛÇ} ¸ípj¾ï¢»·Y—›H;Ë•pÇ?‡ŠŽ×¥ -HŸ-ÕubôгÈÎÑ``t6=5¡.w]u·þC2“Ûú0•k4Jè°3Ö K¿éÌ£–SõLnÛ6TbºçÑuG[]EŸÚ¶·êísÕþ:‰¿Ö£¦‡„ÐvÐyx›LÃK¸ªË ZuT|^êh›^è±ðÛ üú³vn] g{qDe¼ÔBfrÛt°È/¿•°dt;‡*ój[Ò/Œ®µ4Bøaë$gåÓ½æ-Ø ó¥JúK¤H,ÂÛF-KëÃBI¯;ЏŽyoýúoå˜iî]–§Ïgá/Ö6W¬uòø2a«§”ÀÝ­H¢o>ínÅ«Úè– ì™Ô%jK½:=œé-¡K;Ÿ*}nHKK«P^^Eœ JºäälEGÞTNf’ÈÔïZ¥9âQQ^°¶ftŸv@¯µÓ"ÀJ÷ž> 0’¯7î¹Öǯ©¼ð‡[Å€ö™™…ŒxfÁ%qx ¬š9<¢"}Ú…ÞÝ5T*¬—ÆfwŽÑ¸øL,úe÷µWÑÂ'þDèí âO­6…‹³Í-aŠ Í %­4§#ÎCCEß4æ¦ˆìæƒC‡/à«_bϺ¡«:fRÁˆƒ‚D ¾¶šs1?¿\b6ÿŸ½ëªHÚßÌæœsbÙvaÉ9g”  ˆ˜ÌÙóÔSOýï¼3+Þ™3A@¢äœ3,qaas,›sšÿ«730à.²À¸×ºÌÌ{ýº««¾×]]]]mNÅÓÖŽñÅ]ì©ì6Á;IÚ´óBp0c#ÿ!nÙ55´k0h@Ü\+aÍ“÷ZzbLë&2 ´tN¨ôÝ0˜5kÞ|ó¬_÷zõ%ÝWº£§VQð4<%¬9“t–‰ çPZb èßøÔ””V"úT:~øq7ŽN;çISºbê=àíÍ“³”“…EÛ½f‚ĺáʵ|}Åx}ʼf¢®k”"%%ÿ¼EçºoZ_މÉÀüˆì !ޝó¦5ì{-‡†€g89ñà‘›T†éiùŠÂ"«–XtQaî§üƒÜñé'ÓÈÎ&X½©Q†¢„µjå¦?û£ÑÙ Ö 3³9gK”U«k§H3šÅb[ÀøÖ©©¹X´è06o>­ œœl0bd8¦Oï'%¬õµ×yùDÕ¶æD)¸µûujãåë«ÿ®àWúuS;)#Mû§¢k±uëföNõ?Þ„wL)lÂjÕªT4=ròlPTbŽŸ2ž(Ô|3PNƒƒ=ùo#¬u°·gÏÖ¸ãÎîX²äæÍÛ‡… âÀþd<üp?ŒŸÐ ƒôô<øq2ôÈ#ý1n\'tìèõ«ÈPêû_“¥¿,m‹ÃGÝñÏWÁÉ¡¥D*ªQª’[7_Ô«7tÈȶCL´3*«š×]¡yY+V€*ñlQ£:bĈpeùí›o¶Cþfÿ²ÓïéM…·;O  ¡læh^ŠÕÚUÜè0§·'ŽUU¢5çäúFçäu¦Ÿ«¹A­’Ç4Ÿ>‚Ý»âðÛ¢Hìßw7G Š·þ~n×ö´ÄÓy2IiÎ}×™ .N‡ƒܱjEÞxñ0Z†SBýP•Üúy£Þ¹©8 AHpzÌ‚U ë×4leW–ÀAE7ǵáCÉøö»øöÛíø…Êî-·tÀã¢åŸʮ57 ©–ݦ‘ZËÍÆ3s-,,Ì ¾ñjjnÈF@±ÚVáìÙBìÜ‹ùóàÀ$ZÚË•ÍkŸÏº ´E»0o+î29¹z+qs·øz×ïä\o¯RŽ 2Ž´ì¤*¹-[>*u×Z»=z¶ÂSO ¥Uw~ÿýýw#qûí]¹¹¢ú÷¡²k s¥×hùÛ…ö©ßT4/ÌVÏÊʹy¥ÍKÈÿlí wÅ.K¬¬,Ãî݉ضõ4/ŽDjZ.BC½p×]=0‰}]?ösülE±•]jºÀ ¾7oK„£]ó‡3»@WÝßT%·n¾¨WoBÈnq}ã´´ª¨éRˆÒª÷-ëØ1Ÿ>Ï>;ŒÔöãÇ÷à÷åGѯ_žxr0úö ££•]Te÷RNª¿U\ʉèba©¥‚%J¼7jj Hd±ÚŠb›˜xŽ÷ãX¾ì0ΜáI]–fNw­O>¹]»rÚl@ÙH?¨®ZÕ/ BŠP@ƒQSoº«Ÿªúî¨Jn}œQ¯ßdÐ!ò„;âœ0nT ìlÔÙyýÖwð¡¡Þ Õ6ž.PÑÝŒÆpÇ_cÈ0Ü__ŒÎX£v GÆ’ëxý%ªwTüïr@&‚æ´äšSÙ’8Õ<œA]HjD8ˆb«S¬¶99¹Øµ3«VǺuQ¨(¯BÏ^AxõÕ1Jt„àÖåFŒ¢Üª®$W*””L;Z"<´°Y#] ½ª’{%\Róܨłå­1wNôëI%W:4u¤¹¼`õÊ®¯¯ þö· xè¡X¹ê¾þj™ñ ¼0sÆ`Œ½µ<<\èsHŽªÊîåYªÞý倖;ò-%·ªº–ò²¨« × úè@aaNžÌÀ‚ù±~ÃIdg2:‚3yx&MîÊ~Ëvvö¬[õ³½:Ô$`—;@IDAT⫟±i£V-^wçrÓrÇRUɽ:)«OÝ€°`l\kójUk°ìôV x>ã‘Á˜vWwü:ïæýºÏ=?_}íËC úâ¶Û:3(º+—Ue·Á,V¸É9 á{a¡\QnTroò&7Aóôîúèqqiز%«VÅŽ1puµçªS[Œñã;ÑÅJÂ~IR­¶z>\ý¿ùy–ÈΰA£œœ|õdÕù¤ªäÖÉõâÍÇ-&ŒMF‡vypqhùÎò-“ÿ28Ô*¡tfÌЩ܍±cG ¾øÏZz—Ñ·m#î½·rP` '-»ZFogÔ¤r@倸+ÔpO€¸,è-_ª?ûÕ¢BAÇÎry M'ܱgO¬rän‡î)˜¦(¸!!⎠VséóU?Û«å÷¥Ïu옋Š*ºà˜·| «Jî¥ÒSߤРO—lþe±}êÆ³k²tl•´ŒØàÖ[»`̘X¹ò„vì“7âçŸ÷Ðg·nçmÛúÑQ”]©±åwˆ×Æõi•õs@båÖJLÖ ýÏúsªwþÈct™$T*á¾¶òxÝå+Ž 66AAn\aê… ;ccÛj4FÕFµÚþ‘—×zE‹Ç]ò§¸)´\Wi© ×Újõy•-žÅe¨¨0ƒ‹c%•.Uáºvé-»Z†?¾ÞŽLÆ—_mÇ_lÅ¿ØÎ“պЗ·:u är­¥k÷Ú™®–pƒrÀ‚»ùkju¨¨%·e+-‡Å<¬¡ZËMdˆÏƆ Ñôµ=€èÓÜWakɰ†mðî»·ó¨Ý ÅUJ?‘V£#4¶üäØc9ÅOb?·ô¤*¹-]B*}׉µXø{+lÞæ‹OßÝ×âå¯S£›°˜**°Z%Öî÷=ƒ¹t8³gïá™ïG°„±(ïœÚC9E­wïÖŠo¢>Ö®§N6þ\H¢™*E¦ßÿüi5GKà€N‰®PK%·ªÊè®Ðèji4è±-J”e]PPˆ­[c±eó)®GaQ)zôh…¿þu &LèŒðp?6@úùS#æ44uغÇ ‰N˜6%vÖ-{eBUr›j-Í΢¢]±qmJß<ØìÔ´dtµrÔ嵸tð`‰mùŠ—_E÷…½ŠÂ;Ÿ» ÅÌ™Яo(líìhQ¿–z®‹ ^¬úú˜¿ +O¿›[Óá 4œ@豚WUÉ÷¦µ \ ŸÆÕ¦Í-¾55M!C9 B|ru´LÖP†‚û¦—¡àüjV±ª«5´ÜiëŒaÎw‘ÆAòˆÝ,,âñºkלÀ¹œxû8â>†-œ:µ»r ™™™>:‚(–̯?I‡ÙUò©iÑÛ\µé°`i0VÒh4nl"•Ü–½ÇEUr› 'j½MÌ ü|KÞ9—q]¥cTSÝÐàLL-'âoumvV‰*Ö]??L›Ö ëÖŸÄÆ§°bÅQ Ü–¾sm€Ýµè8¥Â&ÿGÇ3êí1Ž»¯­­âŠ-Ë=’ˆ½û~-S™Ê+ª°go<œ~ØLe ‰7ÑP tQvª››‹2AMà†NâãY‹ ë#>!‡Ê[ãbÐÌÌœÇf'¡¼¼sæ@ jYS&eؽ{z÷fµW*?™TU1bA22óù\ãðIØ_QQƒ¤Äì?˜ˆ}{ÈbÎß}úcÝÜÜíqà`öñ‹ø•¶ !<–ÖI¹„(Ç™_9ŸRËŸ×ζNŽW5ajêÖ«JnSs\­¯™8 ÅÃwÁÝ·ÇÁÃ¥‚44NgÝL»ŽÕj0ë³Møþû]pv°¹nƒ¿#+Ks8ÙÛÀš1BìKDäØ2vh³%ެ¢t˜ñ$ªÀ@7ôîJR®déMþ¼óÖj¬]¥ð©QÛ@¨Š•ËÖ»wÄaï®ø&{5(-+‡›‡=ê{¡ 7^Ÿ•+×X8'©yxá……HI·½­5Ëk µIO¦(q5Ôií,­ðýW;yŒv£VW'o ŠË0›²–,yŒqb¯´½f8u2•G}ÏCQa%ßWNŒ×=ɵ(-—¾p¤<ÌyÊŒ†–ö¨cJ_Q+ lô¤C^Q¦Lî†ÙsjDËu£7¤+ÐàÞ©±1$rjùþ媒ۈPP‹nYp´¯„ƒ®Ù—È[WþHÍÙœbxºâ³—§ê•Ðë8¦™ZÌIJԜICe{õÖ£øÏo›P\Ò°‰s¶Zûâƒç&]·ÉÀåø!¼Sx&lkÂ9šðiöŠÝX²åÊãõfI•Ü–—_†[ûwÂãÓ†BÇC-dv^†VQ=óøÕ·¿\޳ĬœÖTVVE‚ Ü?¶/&é-»•Lû©£©ûˆªj^øh!¤”º/¥§±Ú}c•«A·ˆè"¤j^w³+ᛪä^ —Ô<7tˆOv@V–ºwÉ¡U±‰—{o$Rrv´ÆÈ^a° å•ë†7õWN«µRRÎÂŒ~Ábikpâ3^®ŽÚ§{û«) Á56ÏÖæ8p,–“Cú®6R«ˆLþBüÜ1¸{®—ßÄ}e÷í¢m(<[Ö`^ŠÌEÙ oíC>ë<÷¦LNtÜœyh„4ZMõr '߆«;fðó.‡™¦),ìõ’ò§7T%÷OY¤f¸98À£WŽõÝ»}½KØ,µ'«O¶²¤¼¢Ü,£(¹¦z®‘mu]“r½Yórƒ]íµYï$¸¿X¶.RÿŒü© ióõ(£!õÕ•—í©’íî7iªßX‰][Å¿ºÚ=dp=Êhm¦õ 9‘¬^Kªâ†9…O•&J®ë ¡­!yMÛ!ß³>*¹bÝ_5]Žr¬o¶ïðÁü¶ÀÕI=Ö÷rÜRï©h2ÈD]+£?¥«æÒ”$sî\7nò—kµ:Ùm­ã5}*«ê²Œ%š™éhùÓ_—q_vKË/sæ•ò%UVéóJ™Ê^“|F=Á´>)WÊ—gõÉXTmXR”çÅ‚&IVSe3†ü²°Ð_»@7y±>ã.|Y’¬a”µ¥\#mU¤MJ"a¼¤S—ñKÝà Iò°Óç/.’óÌ }}RdµðI6ü›‘W’„=µä™ü²Êø[rI^ù”­Jf†2j˜¯Fù®/W)ÿT)yY/0’aZŸ9Ë5†7ÖçUDy¡>C¬ªnÚ¤\–Áêõ´‘_ò&–ˆzùf“žo Å|P®Uè6¥MÏ ßLx!4ËUsVª5òM)Á€!Þ“’u¼/ª·ä–|F^èùÆ‹Lçë;Ï7)™¼WþY\Óù¼¼/ò¨Uê!ïë ÍŒuŸ—)óS…WüGå}Ð'ñG6ÐV¾‰7d•÷æòøf^–#Ɉoý/ý¿î‚*A OFü´p2UòT\+49$^n¥pãa±‰ŽX»%@)tH¿tt Ë〬Åj'§JL½áQlªyÖ‹V¶¦ÕèÝõ,úuÏæslÞëðd.Ü•_ƒ‰£“àíV†8ñË¢PZAµmUˆq#R™W‡ƒÇ=±ÿ‡šfè€t.ý(aº,oœ|kxº•cÚ„xe ‰ŠuÁ¶]>ìd5èÛ+ =èÿT«ÓbÕf$ÐåÂÚ¦ÝËx³ÕÈ8k‹åë‚`^‹.ÏaH¯L¥M»"½qè¨óèpÇm ðt-£ß©%~]Œâ"s)´™ñØÝÈ“îØ½ß‹Š‰cG¤³C§6ÁŽøç­¡T^ááXŽI}“`Åxˆ1)NØxØÊ†½Â²Ñ;쬒wõž@Äe9Àžyî‡Jd³Á²ýä…9:åad§te?pÊ{N{(ÊÀÔqJÛ ‹,±hO IckŸBŒí–Æ'µ8ï‚ÍÇ|…Œï™‚ÿÔTš1o0²rmàA+ÂÄÞÉÜHS…„t¬>@¾2„YÛ è@^‡«÷ >Ñ´Ua|¯dò¢96X½¥5¶Dqð6O¡4E˯Ŏ>8|–<®r´Ùf¥%¦go Eì…z³íEŒQŽ£=°?Ú“Š^-†²máÄûYÁîYâ_ú©»±¥&ýÆöìC)§¾ò¾QV5ÄãªìCÙOIŸ}ÿ8ØHÿNü/Ý׊ ­²•>X&R;ö{ãûFK¶uÒ­‰zÚJ-1÷÷`”›“7äÛˆ4ö&}(±;fh*B‚ ‰B æ/e¿aO÷rLa¸-K‹œIp¦í†>´ñÍ“8%­ÞÄ>”ý»-ñ=}Bû ÒÆ>tùú ”SN¦ø>p„}(ß7sΈ¦ßÒ§³%¾ ˆ%‘阡ìCÉ«g؇îòUê¸mx*ïßµìCW³娿Á±gâ-ìCÉ‹„Tö¡|Ï”>´3ñÝCÆ Ölå»Ç1ÓÞ¶ “ÙŽBté› ‹àØvUÉUD¯þsós@‹‘Ó•?QPNžñÃûÿî¤4Ûþ**¹çx·æÎkƒý{<á\‚Û†¥ðŧB”i÷Þg^O>E%WŽÖ`Õú,š{× Eùõv+fçg…?è¨ ºÃÙ¡Ü6B¨Zlßë…Ï>Š€9.7æç $góm8âãÖ.wŽO âWÈîxŸõ‰"ùÊkG¨äfSÁ6Ãü!رÝ.•˜6. lÒ2íðá'¨(²À½9¯ä®ß䇟¾osÛZ ê™ÉN°E¥æ˜õßäeXaàà bÒAë°—Ê€ð‚{SZB+±XztøvE7ªx }pÆP©²²©Àq*=ïÿÒ‰†ÏÞ{½Ãõà‚õ!Øv؇‘*pK·T88–! ¬_#¨TXQ1ŒÅÈ.úA~ûaoÌZ¡ &ƒÚ“6÷Rä—Zà‹Åíy½÷IÃÈÎäU5ñÀ‡?uVäÔÖ«˜ƒG.yaŸÖ´EÔ)W´ ÎÇð.éHËqšJîû?vVøöøÔSЉõqwöâí­±i—}íÊ©˜Ÿe}%Hαǧs‘‘ц“‰\Ò”Šî†-þøù‡60#:Sù à ›OÞ~ù]8’cѽ6&Ž’vÐ>¦íˆø´GHŸ+|ËÖ*nÄÐÆPì§²P„áŒíI["ÛõþO”)1dyeÚž¢L—Qñ]µ9..åèÃÁ_hË¢òÙœ´¬0º?1Ô7AQ¶ñÅ׿ÑOšƒn+ßB´¦âT\aï–…!6Ù a¹Š’ ³œJ¦œXŸ$‡Çˆïâ[h[ߨúúc,e*|ËÈÑÓ&JÇ“wž BÚh Y±7KÖ…ÀÓKGeÃF™üq ýô“N(ÈF¾%WïYëñíáN^(ø¶ ¾Ãë„vmó “¸>.ø9¯þí0•\=¾ðmŒãÊÛxö…Tr3øMƒ… C°•‡¹89WpOá&Rb,ÓŸ}FŒåcÓâ J.éÙé­\§YƒzePÉ-åàoÿ~ÝYɶ>&yü(´¼—îýßÓ$¨g1ÚãwsÆÜŒ #ÆDŽ1Äñ(ïæcS¢1 ³c;ZcÃ?¸*ËVä˜Ydψÿü+ö%ICTr6ôÇ¿c|÷º´"Ƽˆ±rbli’Sˆ±®Ù˜Ð#…÷kp<ÞMÁ¿ØË|œÊÎwñRŒŒ9cÜo `Œ´=0&ƒ»Ó:=ÆVon…¼Ì÷­M6'qìC7²=ä'FØ™Ê÷ʆ eê9{|Èr+9Qš6ö$ûÆ¿õ[üðË|ߘ§'[Ò¿—š)}h~¦±M¿¸¥FTÄ~Š/íñß|†„GZ‘‡[ù®XZTâ8㵿ÿñF+ñ³/ß]ßTÖ|“6âû¶áITrË•þý³Ï: (× S¦ñ­Ã6âûóÏeç—Cz§³'¾‹ øN±ÅÐQ©>}¨ûÐÃìCßcJKAh@ •\=¾œÛ'#Ù‡†³˜¦Œu§õï&ùöøS'©äߤmÉrâ{ ñíÁ~ªg6žxàfÜÃwP\W„a-7q¥F ÔjR9pãp`Ö¬YxóÍW°~ÝsèÕ;”„SáhPÒ)³ëXZ…$û)ÖD v~š³U™ ÛXU£}HŠÑ’w*ÞIØü½Jàï]ª<—bì\kþµ •YnU•Žæ2•+ñUjÛªˆy¹¼“eË?~ÀN©•ÜÙÉÊ@ã̰9朽W#¢M>» Ë´ARš¿iK¢G©bÕ=CÚòi…’úÄò,³t¡M:& hïíQ† Æ–”œn«XvÄÊ í°ã ¼’´I}üt¡•± gû²¬œÁÙ|r†ž¶6AÅxìÑÏs$³^æV {T…pïÅ }®È ñgz|]È v®’Ng8!´X‘¶öT¾¬È79FùL–£Â O‡2Ób%¼H倗𧝝­fvVU MÑ,£¼ÒÎìÜÛx“¶ZdÓB“Íúød[*œ®T¢EN'Óh5'ßhjÇë–´>ç—X!†Vá«K)i“uÚ3´°å•Y1åD…B‚—•Y".×K×GâÓÙs±tÉ A¾[Qé³åÀ¥—©­P´Ÿ!ENŽöUTà i­ÀÈá?£º<½|BY®»¶Ó™((µb»„¶ÅzSH ´Oh â@î#|ã÷Ø,{œ+±¾ˆ¶RZ¾%¯XB]í('*´’’©Œfä m:„xÁž‚y̘—´ñ9y(‚ü”O,‚±Ù|3¯'±(ÖRáEÛnà D¸¯ž¶bþŽJsRdêÏÉ?­þÂðxZ ÏRQ³¶Õâ·óð͆L[÷4Ö](/¢Ðµå€®È”øN7â›Ê½;j‘SÜR‘“M5:RÑ•¼òÎ$¥Ùó›à›¼ nÅjkÄ·QÉ•û¾ÄŒ¿—cÄ¿X¹EÖø®ÈæÑbNŽD6ò¾yp¢Ð: ø<=©œœJYíC"ŒV*‘£¼Çä•« ý¨+ã0dð{¸µß0̘~?-¤5 >\ùnèø>EQ²2£`Œò+\>'1Ä£¸4ù80F^¡ òˆ?±ú1VRAÚ2QI9ºÓ¡l«È&‰–t½™—«N6ÄßÉ3™äwäï0^×öÖËëÒ/„ÆH›ÒOQÙ.$ƤŸ ó¡É“ó3Ðä!¼ ÆÈ‡\*Žo|6—ŸÇ°yÓ“Ê{}Š“6Á˜ eÕV±<cìduHÜVB¸ÚãâTÃÓ 0fìr<2ùV<8¹7:xr‚´™b¬•{¼ˆ1=m&Ú”¾€ã;+röc¿À5ÁX1] (ò‚ﱬ¶TÖ˜á8'iJJ¾µõ&ÆXŸLšÓråZÓò¾‰uVúÁ¿-yÑQðÏûÙÄI'²R_•S®rˆL¥?Êg?%‡bt0ô¡|7Oóº¼n¶…xøwaíi¯¿{…«QvJ_Æ>T¬˜Ò‡ž8ãL™JÊ~Šý»¾µ&ïˆ7ß\©´ ïe,‰++«wb…ÎáÄ"+ /8žø)øÖã?—"‘eßá[­Ú1ĽÈI¬µÁ ¾Ô,(øf}ò.ˆVÁwœ¾•±'”-û±ìsìCÓœü–>_h“º£bQBǼ›í8~)øf¿“dÀ7Ç@ýx"ïfžÐF|K^[Ö§O¦ ®wOû§¢kyJÝf®†:ò4ä6/ÿÕÚ¯‚×®äJ¥|ãER’xÁ_V¹&÷$Ñ´©¤+ÉkZ†,}K’2庤?+£!y¥¼ºh3-ô ¡ÍœGð~ƒ¸cg±çûç`é`-NÅ"*µÊvœJ¢ …X·•ÄÎ[p%qRš+¿åº$vªbUU­gb™Q.q°Ð{¢²XZ‡„e¢@€º|¿FY~—ò4¬KKÅPñg”k¤…ÅÐ:jð>d^)C”#Y¦Ô±>t¤£¿@Ë•ò…6;sÌ^° O~ð+–-FQr¹³FˆÕÓ©ÈŽy•Ä2ôâ§*y0Ãð¡ÃæïÕÿ}–·ØfÖ©$#/¤œúÇX¦‘ríÒ¼ò ðG¡ß…’ä7¯+m–çÙfðBªZšt…WÒ$pÉ•¥( ’Žù•û"#–¡X¨˜Wü5¥~QH¤ )P¬†JªK¦ŒBñÞ—«ðÁœØºåDt”%j™XòÙF÷B¡|©G’QòýÏð/Ìü’Žè¿*`–¯æˆ?Ë ÿïãþQ}ð¯W&ë7UÕ%ƒ ”„ùçñ¯ç«rÝ(sùqÿü~©iS¢‘XQœ§F™SŠ N«å½ ½f”—´@*]gÄoXËkŠï<©%þåÝPdÀwE|€eRxc¦4 mt ŸþÒ׈ÎÉÁ–-/ÁÑцϦr4å±ðÍû÷ÆbÔ-âÝÇÇÓ‚7(1Î(;wÅZ[:™#1½^þèàH›Äð]3nòdhÜô+¿/¤ºø&|­ëz]ü¼yë+£.êË{­´]àHKþVW+[2½*m*T´Hß)Éøiünü-ŸÆïÆ{&ŸrZâ™c¸ï/³0k{:•-sZ¸ªðñ÷ËðñŠ“¨±²AY~:ž|ë[<óùÔZYrãŒl^…—~Ú‰l*á6vØ»m+Æ<ó 6œä’­Ž(/Àç_ÏÆìHúýIˆKõ1¡¡©“‘¦ŸòýÒ?¡ËxMùN Ãf¤%žÁ¯…wÖÆ³¢¬p0«(âéYs±9¥ˆ|<‰©¯|O¶¥ÂÒF:Z¼‹rðß/æa_vN8‚É/}‹ÅÑT(¬ÙíkÍP–Š÷¿\†Ã9åÌ¥25]–¦²3f¼HV¼hÌ#÷ßå“ ®-?‡¹ #±çh,N$d")3k6lÀØg~‚2YÉÁO¿Áþã 8™”…”¬<,Z¾cžû+ãéï_…Á“‰8¥Ü?‡Ÿ.Å„¿ÌÇÆdº˜(¦J©¸ “i/÷Ýôž'¿%Lm÷ÌÆË_å»Mݹ¿aÊ;;¨ÜãŸß¬Ç®´RTçàãÙÛ±/ú(îyþìIªDÉÙdÌ|ý|±! YIQøËwPNx 'sÄâÇà±ïB-'v;—¯Â„×Ö£¤¢Ÿÿ´+£é6PSŒYßmDT ×HšƒoM(¢ÿõªLMÿë¼PÛ¯r@åÀ•r€ƒ,p‹5I,±ºš*.¡ò“ƒ¹²ôJ믎Îj\äÞŸ±¸ÔJmSÇütîå5-+~xèÁ‰ÈŠNÁ‘'p®j,³3áѺ=þñìDZm9vàÎrr|uš~sôÏ3+Dì9/úÁNÁðPú™Ñšôú wÐ*ó_¬LÅ‹÷t§B˜ ï6#ñá“Ãà öOChŸ+mÚuÍ'ƒ9ÏpUâÌÒš+<ÑϨà+‰ ¸–¼¨¾ÑªgÎï²€Z-|b’ã_Í-èëv+ò“’ñξ(œÉëî8qê4\zÃÛ „5íÂÄx|…¤ÂÞhç¦Aä©X™„7îé³²dDÇaódï_úî;‚±ÓîÆ³cÛ@#þ[Âd@iu ü‡‚P¯™àÝŒx¯ýÞ oÊPVàeQ_Á;ó™ ŠJ0ô;q÷ˆPÞС,õ4f}•€á£oÁk·£‚¿'>u/î¨ÜÏãðíOSpûäÉxv°? â¢pÏ àŽÁ~ЬRìÅß"³qÏô{0£W&ŒîMÄ?b[Üaªkdµ…ï>­ÎZ,W $i‰gÅ¥†˜×r%Fæ›5Ì_Ëü¼«¸¹…Dàå‰0sE2jÝ&ãŇFaíÛkqìD2ºv퉗ÇF óäA 2 /Ý€ì'q€+=Ήë÷as^âuxöñèÕÊ{|Zã­ñQuÀS¾<‰"+<ûÈX,8:G£Ú/Nî óÌcè5h¦F¸3^YóMáŽúOSq@Ur›ŠÓj=*np ªª,¦-QH/Õq3‚™Y¥èÑ;gÓÎ6öèÜÖ g’y¿;…¡­[56nFýË«+гw7nf³¥bg^œŠMO~…?ZßÚ*<8c2‚¶Šš!GE-n½ã6<ø ^ýd!v´5GØ-Ã1¼3'‰âËáÒÞÿzaÆ<ù žýXð¹—Ûü ®Çfd&%`õÞ4ZX5ܼV{7xДÏ#r¼¼Âb2aãê±=[!åÌ)ì«´E,\½1ºwºXb棓±iïGxõç½øz¢'ªÅ+¯ö†uýˆ56xáÉ)ØöÈçøûüÃx·¿ÖÄÛãWºÃ¬œÁÚÍðú3“°~æ7x÷÷vxÔç,öåzãáÐпXM—á€à½¢7G!“¬”ø±Ù9¥èÞ+Ù)9ÐØ:¢k[O¤D'!£œ:‡!Ô¹ ë·E£„“»rÆìÕ§3Â<‚p÷*TF«ªòð÷ÌE’u06¼0v:*Z^­p—Dú…–—d⯟ƒJ¿Nxÿ‰>°ä{áŠ;‚y¿ª%¹‰xþÍù°ëÐÿ÷`W˜&E—iÅõ½%Ê*iŠÜ…Ã)eÜœ§An~9BÂ}PžÃ0g–VŒ¨áÚ¬LDó(á€ô µÇÞíQH.ªåä¸ !í#Л|4¶7\ç}…E;ãðò¨.è³~¼ ó¿|–•ص%ÇŽ„ÖÂwN脇lFê´Î¸—G1ÿ÷±ŸðåÀÉØö„rOF£Š«?>ž¶pÒ!?íÂü-§ðÞôáˆ>X/?z ®nµø}v Âzõ£Ÿ³~2y}™£–Ö’8À7NM*T¨¸RPkãæ¥…_-Ç¿'"¤]*2¢ñɯ{QAŸÎ³—ã­Ÿ¢ áÆ–¨]‡°ú@*ý@«ðý¿á›í9n‹ÜT¼ð>x%ØB5¬|BñŸ§GcûÚ­ØkŒAí¸+×x:­AÐØãù§î‚CÊA,<œ‹;‡ÓV£WpªÖÈ«]|p_7,øm¼;wäŽs«æµà a\Ž-ÈHÀó¯.ƒª1“–.X‰­gŠQ^˜‚g_û ‘ÙÜå¯+Á¯?mF6}3OÇSo­C±½ BœñûÜølã)Å"fî€üubV¯ÁݳvaúýÃjÇ.\ö„Ð:fëJe4vÎ]Œ‡<ŠÇ?†BS,´ÌÝÚFà_õÁ¢¯çá• Ùxþ‘p¤•]µà*(ªçâ›Çæþg >YÃ8Ía(I‹Â§ópŸK\ŽÌ=³5Ží<ˆ GŠa¢¾ø÷|ü°;­Ã'šËé/~8©å”-¸µš,›· ß5Ã'¯MSüo¹>Oœ³.®€ÔÖVà—~ÃBFøïk“áoCÊj„Üçó5•Åøâ«…ØVˆÿ¾<nÜ…/Ï5iâ$íè¶]xaÖn8øÀÅ,Ÿ¿©ôš8ºmž}o;Ê,-‘—€…kN@gksVâ­¹Qðkoór¼ù¯ï°5!æ­ñà`ü¼:åÌÎ ¯Vðvaă´DDêÜÑ_s¸ÿnð¬JÁÏ;Òر#îèäOðmÇñ3ñ𠆋'm¾xtT+ü¶áΕÚà‘["è¿kÉÐnŒp’‘Œ½¥¶èĉrSó­I…¤V&à[§&•*T\!¸<‹’<Ì?ƒqŒÂ`Z¾s0::Â; Ý FÀ²ŠÕ¹Æ&˜̓ãÐѶ?G§ãŽ{G¢Wû@Ü}ÿ-0OËÂÚ(Z}eÉ·¦qô+íÖ‰–ཛ°ôÀYî@§ÅŠcºòGE79-¡aÁpÊÎÂÇËOpá¾MšªË‹‘PXƒ>á¾øuöfìOäñù“2š+Q)‰=qG\ñÌ]½Ñ¯_7<>±d-ì0îα˜ÖωÜ4çf^†Ð[GcÆÈD>„üð0̼­zôïŽ'ÆvÀÏ‹#—Ö=QüÃûõÃ]\aëÖCe©Õ86ÒÂ×cø@L µ…³{ôlËÉ‚(OJ¢Dcáø;Gc˜7èõèp?nTRyêù¼Ó]f~T<¦<4†1wÛ`æƒ&ÏþÁaÄûhËy¨ñnâÇï»í­ 0;6wÝ?=‰÷GgŽAU|*ÖŸÊMòˆÞˆ?ų/ß…Û;¹)2’ f²ô/'¤X³Y˜€·Þ¸CC·š÷u¼/6-Y…ÿ[Ÿþ> =}%úI-7PÊýzÚp½/ 5Œ÷‰àÑ8é ÇØÛaR_*§ÞxýÅñcØ®‚"-ìÜ­0iúíj‰Ù{Ž£Ûø!Ü=#§ GO'¾ZЗvìÈA¨ª©XÕX2–ª9ÃjU °[O tÊÅ{?lĹt ¥"EM#çK<@5µTj88×pð:¶iæ¯Æ‚ÏŸÁ#¬ðâûKq<›ñ@éÛWKÿݼÄhümñ ¼øòÓøôŽøö¿?cÑ¡F`0,úûU3¤Óšeë°KŠåŸÍDm žþèwdЪT+>‚Í•Ø\­…JKÊ[À¢Œðàè"±Bii2wÀ·veÛWàãyé×/˜¼¡o#7”iyŒQ­ø Š»†¹}EQ•јþºŒ®`WG[p/¡iåÓ™Û!ˆ[ïí­ÿ¨¿R¡Õ1îq+>ïFÅB ‘~…Àå’˜ÏÈ8§à]ge̓Q$Œ\%BzôBû³ø×÷›Q˜ï„ŽÁ¶ÐUÖRîâ HùûÕ´%™ÑgUlJå©qxþÓÕè1v<^×µâºÀU‰Ív ‘±^ÏÅÇóŸo¤¯ôxrdïóØhZnׯ߅ŒK¤GÀKßìÄô‡¦ãÞþ>œ ²Š¢<¬Þ¸gA é&u<™ïgNv>*èv#‹=ª+Ê`†;;;âûßVaûA†÷ âYÆ>€ï²†lБ'²@cnfÁø…‹2AÃ1 £9>øöwìѵÁ»÷õÆì_Vaá©tkćØPágµ%îžÞƒ,ÇÇ w#dÌÝìV€×>Z޲°¶h͘ÎÊkAË·kH0ÆôrĬŸVbeŽ'Þ™9k—oÂw{âЩK(,ÄEDM7=šq¸éy«6PåÀÍÇQ¸lœ1¾C æþ°+vÅð4·S<Ò´ŒûM¨ Ø9ã¾ ]±|å8 é»jŽ`önàçˆuÛ#ŠðÞm‘áꎾˆÜ¶}~ CÆv‡'—蟸g8l’âñÖ 26‹Ç&ŸÆ_Þ^çЮèìoц#‚ü_úç’ÁƒriÙZ‹wžÁÃ;ÁÝË/>2'vmÃë_mG\vQóí;£ÒÙš.!¥™xó»u8t2 K·ñHT YF©ã°>/ËÄÚt ¥U–ãxûvmP•‡m±9<™. ?3ÿ°áapK5ƒœ´LI-Bzz2N%r’aªçRÈLJÃñô"¤¥¦ð(ßi¢õèj«‘—ŠS Ót&!‰G*o>^lj6fë‚ tSøé»%Xر[öœä vÜ8E¼;¹áÞ[;añÊ}ðÜ6ô7×8º£ŸÖn=‚úCoX³9^žèïSƒ~4Ûª}1ct[$Ä'ãØ©,^±߯âqÚçðú‡‹i× ™3I8z2³o¥oéYZ”3ðâKã×÷õõÆÉÓ 8r"ßÒGuÙö\Å/ö:¶¼þ¢ˆkh­1¨KޝYÏ—â©Z±Ø~4“~äT´xò”þ8²n²:„Á—Shm0¬C"÷C*'|±‡Ža­«SRqå¤ÏÊm›ö£m§6˜>q¼‹“éÒdî­Ó׸y”òìÐc¼ybO˜Ô•“`}úú™(ù”'’÷h“{÷Ã3¤îžØ,²hõµÀ€0ZÈy ‰šn~ÈôRM*T¨¸ˆ ¥~Ñoå‡r‰CzìNhÜ·`ÅÚÝh` zÅÉàWÉãp{uÁ3wÛcJ­¸²œnáˆ7Ÿ›‚¹Žàë¹›éw§Åg/ß ÏŠ¬;SˆñãºÃ–֨̂2äñô¡ÇfŽG9—÷<ÅÚ5ØÎÎ5<¶´nUÀ%ïÐ}ðO}Û°u7R¯ é7ÝÒ‘ö]ò‘¼8Ÿ®Ý0‰8•ŽÀ!mx …Æ@UU8}: íöE'N8ŽžÊBÀ€Ö—Njdté¨À‰˜Tô56ÜÔu:A}õgHÓdl‹ÉuýW¹a¼iúÀöm¸´Æß&mVÅXÈ3Ÿ¾fÞ[°lõn„úYÜ–på$Îázö%Þ§¹bb;Þ­œñ#~Ì[_ÎÙŒ23G|þ—QµâÉTAx3™XÍÀÊ´Š2K¦_öÀ1aYRߎÝñÖ Wd0BHý¨+8JâwCFóˆÚ¢´íÙo{¸#>&‰Õ×¢œ“ÆŒ*+ ½¥=le*´Û!"1¶ÅpÍøój¤¥–IY´Àv:ßpc葨(ð…™Û/“®Ðx¶jÇî‡Ý<ù[°kŽîŸ»õûñËÂ-l¹Ï?6S:ËäNí.CâåLo ð€Oï{þž@§®0#Î'¶QëàŒ»¦ÜŠ~¶¡pbI]èÂó÷xù+LìíÏÉ ª2 þ„eJ;LbàÑùºÔ/7T%÷¦£Ú•p¼PbIïÍÃÎx²OK:tQ°hlQ’’W.2Ÿ\3Yב0`J’Ž_þä'Ç/å OVÒrGùÃ÷OÀýb á&ª}{Ò¸2+ƒl5,œüð·‡úK ²Ê˜YÜâ‡Çîòfà{ÜÀ׿VtE¨tÀ£¡ÜÝÏzkeiž. >Ý:!¢WW¥YÊÕñ¦—üe Ô»ÚãÅ»[+J›øè‰%Ó|(»1~ÖVs¹ŸÊÝà=0lŸá5T†•cb…þ‹Ú!U>cû¤ð ÉÄdÊ #ß$ŸX¤$I›¨ÐË¥‹’ÂÃãMò$°M>­­¬Êbû²¥øí\•b˜2#ï ¦”¥„1w ïÓ¿7zv¯ Î«áfN¸;_ù£¸oŸîè?PÚË JÒ6™XÈZºœÜda‰Azaˆ‰§Ñú¥Ü’øÝ‚;ÏÇ í‡Ûd)Ê€¸Oh¦`#½òYŸ¿¦ÿ"a±”‰Œ,1_„!ÃC2šíJª“o¼£è+,H¬þ’_’\3òÎJê’6ñCÊ$eóÊ ³àV2\1¾™@Îã[¡ò3Ö©ož–+ZIò¡ä©¡RëŒGï¿“_µù‰Ø½7ƒ-æ%æ,\9© ¢˜YòÓbîäEÿÜ{¼yljÞµ¢ ¯WÛà•™ãø…eÈ»"y…,ñçÿ"£7 äuÊNð(õË}Æ4VøÎkï<‹zÙ ”,â¿.?X¯’¤\c2”¡ÔÅÖí¥ÂƼí[Álhwýs›Â,&Þ(Òàï œ+V° ´`ˆ#EÃ¥ °›üËó]|><<8˜ÍvðT6øÑJÄhŒi0¾¬Ù/ûq}’c¹EÆAsì •Šf9ºlpyu|?@hË<,Û)š ÒËö»c)Hוâø/µèRt–²ni³åDžsÝ™þ‹•0ãoÛq,CRd4pè ËàÿƒÈ‹pÊ©Œ8¬"mgó¹dN^Ü1Lá›–îØ@9‘ÝÚža@÷vxÉ7áelÌlHÿíƒÈ?hóòôõ ß:R¦ý:*2Å®ãÀ òB&£‰•_ØPq˜Á‰†ç—[¾!ÄùIŸJì=Ív“w’nãó=yO¬dß;|ŸJ~=ÄÆ3‘ï²Ó‚<ò;F¾E^Kß!Øt¥œÒs å ‰qq8{"Ç-¢0¾»/¼wSΧYŽ¼Ë¦øfû¬K+I+-¸Ø? mûOêñM‘*ïzåTLYJ¢àÛžøª/Kð½žø–Ô+ŒØ0â;’¼H§µÖ ˜H¼Ñ?˜»Í@ÿ ò˜x‹hM|Gð}ŒøN¸€ïò‰~óX° æô/‡c¾ÞÊ{bôø¦xA·ô`9¦øv³Lž°Þ£‰ÄÛ-“ŸÁmˆïöÄ÷³ÃÙŸÚ²0æQS“p€T“Ê•7>8Bˆ23o?°ù -* 9PyÓêÀëkO©TjºRa˜ÚS¯PÄžË`Æ·W+|ìÀ©²õ.äÀLåÓbß)’¬e ’Áfóavà,K–jé¨(G“Ð…Ë­ÝZ»£r õÒQÄc;ØKØÑ‹‚iT$6±>ÉÓ+\Qžšw“6QjdƒÉÈúH”CQªÅRÙ†4we;¨ð0^.¢SÔU? º;³žBæå ' ªÐ:ŠeÈ1'™o;Q:{’ŽVTªKÊB[EúO¶aTæ4\úGb޾¾¬KInÃzÀ1Y`L2à¾ú15fË"14·:ow¸%—t‹ÓÖnŽÀˆî€›ùH^H›y?p@Güðé ¢ªÁâÛTD%ée:”4˜Q†y”éfbEð-“"yGtQ‰äy$ø–ú‚X_± ¾{P·õç3ÌŸ†>YçPÎ÷É‚qkI0-›¤måDßw0*‰)¾õ楫TxAú}ØE¦l7 *–NÁ¥XÌ$I]ù ]1¾ùœ+ŸWðÍúhÅkrbZ.Lþ ÓGõÃ{O'¼nä…¬!Ü@IDAT^H2Å·È_,“¢¸ ..Â7¥õñòø¦ŒF|±Ý$çb|³\Æ}¾2|3¯`Hð-“ÁŒÐ&4ŸÇiÜÐÚýôß~@\~–oz.E”‰ô›uá[¬ÏÂ{IN8r,#F|‚÷î…‡' f»ùœßò. f…o2a“÷L’‚o¶CdjŠo™ Î|h« ߢ(’ß乫`È߬ώø‘?Á·(ÎÆwýOñm|ŸÈ7éo¤_÷)—‡X³^ÿ5¾6X½ú)h ‰Ù?à›y¥?UðMÚ¼ÙŽ+Á·àÚ‹J1Ñ ^>o¦¤åÉŒ_ãTt-¶nÝ 'cÍß>…ÝÍO†JÊ•×ÌQˆ| ™.’ÓuÀ÷E2}Ÿˆ!aW8b«ÍIÛõÊ­É|É_‘Søæ ¹.©&ñg2èmbãyUXw:Q¤ëÅ·ANáÍß—Ð&u\Ä‹úøVÇ»®«‹o,¯Vo'Áw¨ìéÎs1¾‰mgÁœä%½"oQ ½Ü©¿W£€Ör;¹&|« oBoƒñÍv‹+ÓêÃt…i£7*((T¨ÿ4"T%·™«­r I9 r2•Q”dí¢d|.º&ˆRqqâÁeÐÊQzø¡×?xQqc’ë’ÄŠ"Å“r]¡˜¾wÎÙŠ9«vpÕY޽1[KøTHUþÑâƒ÷6è›aÚ–?!RÆ:I¼=Gÿå†ú—({_7[dòY_¹õ]oH’—›£ŒŠ¬ÈQŠm(¾ésþþœm˜'ø.oyø6a¦òÕˆ×Çûõb^šÑô·ã`}a¬;xÑënšµe}×ãÛ>áOÞ9÷ÜÚ‡["X&¯uöY¤^™à6sžoÌõÂëÞ ¼¶”û žÖ[¦ÕÈó\nÌ/ª’Û˜ÜUËV9Ðdàh$Kñ3¨p¹ØpÅ£W]3 à<¨J@™l¢0*WZ"GS}ôÞþv fÍÛ€Q·LÀÀƒ`eM‹Ÿ(Q-,‰«ÁÕxm). «–פ?ç0áRÀ¥ëµëÖâÉ÷æsu¹ OLa˜Ï\Ò ã¬ãÎig ­ñe ìåZÒŸ“Ò<9xHƒ,‰3IÛÊDÁ5üþSz/|/þþÍZügþŒs;ú÷H|ÛµH|Û£Wr¯ëWóŽënŽÏ¼‚³X»v ÿ׃ŋ߉o“}9 ô +ʹZájXݹôe¸ÚvP©íÀ¿  ®ªä^-'úœªä6”cj~•-’4OÈf±\Sb¨ †w sõÀì)·3ì¤UÃK㦓öîʤ*– övhx…êWËñp±á!ÎÎNïÏ#‘Ÿ„?'KÕÄ—DŒ¸Œ’ð‡[ò¾2e<„ ŠíÕ»ž\!±äÃ'ûbYB‚‚ï:Øu…©Ù›/ƒ‘ØÂÅÉ·úûà/C†Ö‰o#UŒñÀ’åpqµU&vÆë×åS6ìIä;®–(ó®K©j!ÂUÉý©·UÜP౺Šr Æ\–ÀxõhïáÁMçTrº4ÍÜ®66KÈ Å½ÿibÝiyódtŒÕT?͈oyWÔÔ²9 ¶XùÓãÛ“ø¦¢YOÒqÆbÏ ˜×_®tûÚÅg󯎂½HÁ'“õ¥^¾¨Jî50O}Tå@‹â€DCøa5Tj¸÷÷»&ÒÄrWE'CkÃFŒFK_C}65<&TðgôinQbÒ6¨æú3³l)Q§£µû’¤‘#?•5zÉ#´_f‰’åh…ÎZíE¡”¡<+Gþš$-ÛÅÃE/ëh’ýš¿ i&2áã•©ÈL0$2WSýàA *¾õìQð]Ç;U?ó®þŽFK‹»²Ì/øfˆÃj¢z|S±¼ ¾«y¡Å^aƒØÄ1äÙo‡€ÇQɽ¸Ÿ¸ÂBÔlWÁUɽ ¦©¨hy`§)áqdxÆ"½F%·)Û'«ßY §U ËÑKkn ÿÀ6ðtsP”Ç+¢G7tDÉ®,+FÌÉã°ñëÀCªœ¬4¨B5ós@ïh$ò˜YÁFðíG|{]'|çfÄ!!õøÖhʉïð"¾mÊ2‘«qCXk_˜×‡ïÔxœ ò¥øŽ#¾ thß¾¬xž¤¦Ä7jJ}d?¢yŸ­³:ué oF‘i¨¢«Þ\ÿØsU¬c1K¨>55þhÖh²ªÕŠT¨¸î“”“¤®{ÉV pØ»äM<õâLüû½7ðÞoà­¿>ˆ‰ã&b (æ^%iµ<¡Ë¬’®ÇÕŠG†r‘–K-Ÿ¾ñ4¬¹$Yš?3¾ûY ])n_C§“`í싾ý‡¢m;òÓâq,&Ž–ˆJªgÕüº` ,B#0yè ¸;Z£ŠFÓðA#á¦{KþˆÉ}_žÔ[‹Eóç"»ÜúwGÔá"ëxä§(—˜WePϋى·Áß?ˆƒøBÜwû0xZÒIAËh<65?+ÿëctÿù=ø;P³áªAa.C¾‹+ÿ1£ÞñÝKññ?`P/¼&#й‘»¶ãhl´vžè?ä„úº0{ r’¢°m×nTØø¡gÇ`¤ðØæ.½Âyؾw7ÜÛF`[ÌŸû6ödÙ ¯–oȱ·R¡šZD‚oßnãñÉφ \ÙÙh¼÷Ì |ôW1hÈRøi.àÛÊÙ½û C­~ù\8væ,­ p"±^Õ»‰ïß`)C« ‚ï0âÛCû–ßSú½fÀ·‹ÎCf‰# èèã<Õ¯ZïK- ‚ïܸÝX¹‹‚ï¥_-Àý·€—©çÊG-yÎÏ>‰ÿù!zÌþ|€ïHaQª.Á÷ Á÷Oßc@µ7<'#ȹ‘»‰ï˜h߃G¡ O`׃sÄ÷Ö]»PnãK|·Fê%øöl7–Çñ¾oŸü ÿxb’vý€ï~;¢^Eè°PÔðôà–ŸÈ,9aÒÞžü”‰ê%LËoÀ K¡ªäÞ°¢S W9p 8¡K0/JzcmË’-w@{ûzÖʡM¸†u Gäžx”WTQ©|ÿ\´þ¾>¨)ÉÁ—?ý„¯~XÇ3;ðêÌ—aFÅÀ'h,Ú`52²ÎbÕ¼%èÓ¾'Ч’KVXºµ£RÚ¯®X‡Øôîe‰Ê¼$¬Ú¶Áƒ_G—@k¡¯¥Öh»„µô_Ø»m)Kã›LÅ_Ÿ|¿|Oj¥,™VÙ¹cÊCÏáð²ñùüxÿñÑT4Y}%Ø´óÉ‚ËÇ»¶¬DZn&n˜ƒSCûáÀOÿ‡wçìCp;”ædàó¹Ë1ûç_Z{/Þ= Ö^\Ö¶Âþ}¹æá˜·`4§ðÙû/¡÷cKнU;ôšð†Þ€^Œq[QSË oøøyÃŽø¶÷ÄЮp`‡ßß½€,äŠø®-%¾ü_~ÿ\âwãÕG^‚ÖÙKÁw[ÍZâ;«ç-Fïö=ˆ *¹ÜGeùÿì]`•e~wÝÝ KjŒŽéRA%TTP1@ü-LTÄVD@JBéîîcÝÝÛÿœïî²»9t¤÷…»ûÅ›ç;÷ýž÷¼'ìê’¿›á…åkq6v"B]„¿¯`õæ­ðŽ| ½MpLø»&.)¥âBmï¦e¸Ó–ü=/Kþ~ã#9ŸPOµØÌýG>‹Ã+¾Ã§??€÷Çjø[ÿOü½k3ù;•ü½þ'œŒj‰sÞÆ[svUò÷¼e˜óã<ÔÅ!L2TNpv4ÅçïžFšA æ-\†gðÙûñøR¼Ò;,Ø„€ú°ãâQ¯nCØ[º¦ÕÁúÝõе{ÃΦd3"%õr]vâZtµ tËŽï=‘Ï-#…®bîq ¶II£jƽ7¾|ó3Sw%1qñ8´a=VQÐÒ¹5 ³vãÝá¡ïaÅo±`ö÷°Oüó×î‚!õÜŠŠÓÐyÌ,,Xú†=ñ<í‚0ý«/ѨŽí5Ge|Á·ð\ÒöcÕžcPqK÷Ô¡ÕØuR…ÇFôTtÿŠh…qT1X€&ƒ"ªsoDºcù?#‡…•À#b(žÞK>šŒíç³a$ûÔډϧPÏœê‡RߨϾ1}ê9£ØÚCÇ}„Ÿ®ÀWï3pÆ•¥Øu1 W}½¹Æxã‹ß±bÉrôŽp¤?Ö ”–ÃÄ)/L™‰~Ísd5v*üÝ ª¿Y÷æÄcñ×óÑdðÃh×¹š{aéìù ,S€q1‘´{Ä`Lzä,ýèEl;+ü] >Tð÷ÐÁÃàéí‹ Ó~B¿†.(¶ô±¼®À7¾ ‹˜eØy!‡VÝÙ*¼þÕ¬Xº}š8+ü]Fþ6v Á¤×ÈßÍ=Pj`‰ˆ¦á°±ÐGnJ4>|}"’½š Y]×{'”ÿvè3 àΓìéèÒí¡À—䊷öG†-çÚßʉîÏ’ÿ•Õ¿ÿ“ÄøËAi‰ÓÓ§P'—Ûb_ ýËÜwÛM•±9Žoœ‡q—¶‰o” Ô°>&½:–Ù›‘á€Ní"¡¢ÔÊÚ©!Ú…»bçÅhÕ5â`0º÷j Ö%|b@©”øh¿×Å Úݯzu â¹KðtïúØ<Ú @ÛPHTWI¤bÉ”\c\ œß¾«/Ã-mûÀÝÓ¿íX†Ãç' ]ˆ¼ìËQP ‡þc¹e¼"Ó?ù3‡\Ó›”z4I• õ(qÕ7¶@ÃðæHX» Ï>ù#Ê ³QTF½Þr¤ÐPÉÇ·[1¸ЯWüph-›*…¾)U,¢:¢Œô(fßE@'ŽtéŠA'Žoú ã.ï ¼ÑðwÜdXålGš§?:µ‚Š?a+ç”òºcù»0Ä–uÉßmèÖOxÓ@áoÁHÕùÛÍ·9zQïsáÜÅxªO#lš?mBÛzžÂ2×’ð¸vÒ'_ܾü]Ž!zض?Ü=ì°jÛ2:7C¥U,ò¾c¦bÉŠ6˜>ƒ’æQA5ò·¨7èñ¨ð·‘4n¸µ+ñìsî; éäoù;5! Þ>ÍÑ8ØtV¢æï«ùþþ¶¦ÚŽš¿eݦOÕ¢ìØSxûqX{¼Ó>û¡næ\àjä.?–ßgSåÉÝå}¾ºwKA®6x•˜çe|Óh>rOŽ33Éôéiü¤#++‹?¤|äp‹’/9ÎÚ¥ôæ\¦›½ïV»¹!èq¢4¤ãy}#c#†c4a-3ØÙÚáÊ•+Ê<‘ͰœÙÙ…œP9)2¿äU8Ár^–/1É좞¨o®÷`îDÊ^Š´Þh÷ÈŠ óQ·U_L?Fœô Làé G$-ç;/N¹.ã¡ÃöÒ’rª(*Ï×zÊüR)ÑRDY7›Y£k÷Áøé¥/±a[3,Þ›€^“ûÂÕŠUjôúÈ4âIª•2RŠò¬Å ñÅ[†K?†Ÿ hÜCdt«6oGÛV {•³_&¡˜8u*Œ³Üž€ó)]”Nh¥rêeÐD~Âq¼4v0C~Ãc}À(3'©÷+¢}ºJR:ÁóRˆÏâÊÚŠ‹Éׂ˜^gj¾¡¾¦Œ[É¡ûs')PRT€º-ûâ§F˜| …¿ƒÈ߆H>¦æoYiÉs>*+þ®Ö¢§y–ÉëZ8R®Éʤ€‰ã†ãdA0¾œ7m‚ÝP¨ùÍViùn=á3¦šÂcmè°—z¹ÏñnííýÔ¯Z¹%”$ ˜•Onn.’’’ðšË—/ãÂÅ‹4œˆABB<ÒÒÒ•™|ÆC,)Q)2I˯O~¿üâþ©úc¾Ÿˆ¯Ëõ)@ù>A '>ÉÂÙ¸\ffNü"©Ë§¹ ƒ‡~ {;K%:«³Üžÿ žÞT=W‘OåÅÛ°ã0Œn»Ó¹5kîàD¾Sf2¥ŸÊæ+£¢pVA²³R›ª‡‘¿ˆ!ííðÇï 5“¥¿][;+ÄÆïÇñóYhíSŒ5+—rñÏ^Ù—æàÄÑÓ°ö …‡½ òr¨8Q”"™ƒ ò¹øË!Ÿ›Rº] ü+; ;ºÝ(%[;y¡ùÛBø´:'_ƶ]»Ñ>< 9Ig°ÿX:Z¹ÓóB*gBùÇ$åø%ü-Æ…z4ªÔ… ·yF‹Ÿ`Æ´I(³òC÷ȦZü-ä"äSp¤áïÌèƒX»í(úL\„·†4Só·a)æ¿3,\„çG6WÿŠØ¾Â߆âɨÅxûƒÿÁÌÞ‘RÖêüÍ~RüšÅݘìì$ªèaù{hG{l QfJf%Ëz°¶±B|Â~Žf¢­o ~[¹ù…â9E›¿Ãhä–‰O¦>‰mIÁ˜ûÓWhèn†ä”,¨L,`¢í¶Dht×&bœf~@Sß Lsï "îZÒþMÇþÈ0+ÀV>i©iØ»o/:ˆK/!úJ4âãã©"˜ÂýVr¢· ð°u±ƒ³-¼ëxÁÖÉö<7·±€1¥u*ú>Qчœ!?üè”è˶Þ}‹Jþæéü‡o_ÛàÛ@A%ò)¦R 'мì|nw¥"[º©Ié8Ÿ†]GN##5“(Ý1‘oì¸mïâl O/øú9£A=w4mæ77…ÏT"e“­mY`Ýó‰c—Íô~÷äHD/k ¼(9åKRë‘XyµÆÄ‘ñö7“{°rá¢U¼ò@ èS¿Ï>ÈÊEœÄÿÖv¾z—ñ¿gG`Ò”÷â­€e…(DúþÓ³FOÿQ£^Fˆ«¡b.nÌŒ¹|nõ|L³›—P9,Dã¸ZÜ3úu„•­‘bgD¡Ó=ãÇUS±jßxÎ]ôÙ)óûP\fŽ‘/OŶ“]p0ÓX‘˜i°6ñ-Œ¬ÝiMŸ¯Þ~ Ñ€oˆ-¾ùßãØûƒ¢ãáìçŠnŸì‰àò-xý¹!p(Å•˜8ª7øT¡€à÷ÕçF¢Å3¿âå.ŽøjÂhäî×¥“Y(MücwÏE»‘o`D»úê¡(Ðý¹à#ç¶¾Hi`…ü-k*mþ¶ðl‰I#;áÍo'#îP8=‹Ç9‹FènƒøÜÑ"sÊÅÿÂß*ƒ+äïGþŽ õ©Æß~x¢gk<þÎBDŽü†[úZümbŒsëƳc÷^ão‹FÐÑl®5À‡ý…¿¯ñwçžCñï¯ÐãÂÓ0äNš²‰À>—™aøËÓ°åD'ìOW)]Óæock7¸à›·GáÊÃ#áj‹ïÞz~´"ÇÁÙß ßŸ†—ÆtCˆÞ&¼!üíXNX,ôüøÞ7FaÂ!Lyþ4}zÆïÇ/›NòŽ9¾~ûIª(”ð7U޾‰íï!þ–‡ÎÅ3Œ8Oë0Ímû)Þ4ȃ¢¢"EB{úôiìæêó÷?~Ç™“§¹ £?H ¬èàÚÅÓ7‚w¼‚<áæï 3SX}2d¨Êˆ@–ÛÑ*pKZ-áo˜LPñWMŠìm#‡®¡»‰z4’8ͱæ[8ƒp—s%WðË ¤¨ ‰—}æ*¢OÇ î|<÷'bÇ#øæ»Pñeáï¨ºˆjˆÀ:.pusà"‹¨¦}·»‰ 7ÒQÞ”ßÑ=4ÑFjÒý%¸èù(W€ v*)7ÆÃ/~ïæ+¹9U£Öx­[„xY"Ý  &½åo RêÖ ÓÞšE]ÙlØ[Z¨¦Ve\—£Å€×0ÃñA·é }JŠÎJšöxSB)yiËbÉÔ9~æöøø‹1¶3R Ù¤{RÁ'¢/ÞùØ6ö6}âY¸¸S’ªÆÖ^­ðþgËqèr>ê;™@6«”$@ÛºÞ|÷l=xuÂZà‘?cõPdèˆÑ/õƒQÆlßNîaÿÖtœ½˜ ¯@8doÀØÏwð'AFZÓ›øœê¹¢„J•=úÁ¥ ýFp0Å”¸#ÀÃáOã¯è…îë6R@¤¶»½Çr¯ù»”üÝÿ…¯áÙ¬’¿_éÚ¡ÞVHWµÂ¤·áce¨ð·shò÷ԕͬ‘¿…ÏZ<ô >µïŒº­«òw“î/`JpBþ6q‚¿¹#ùû Óƒ§Rå-¬ðwxo¼3ÃÖäïz£Ÿ…¾“G%{´À{ŸýŠC—rQÏÙ´ ëY…áïÌÀ–ý'Ú#f,Àoë6 ÀпØ* G¨×~Žnõ0îÍwqFøÛ3޹›0ö³­ü”’¿0æù·àXÏ &VÍðîŒï(}.eûêۨ2x8ÞCüÍy™cÆŠ#À˜HÀ“A!Ôû”·‘ÿ›MéqETí•R3!$[!ß"l^ø36­ß„“gOr奠ð@4j׈+6o8y:Á…q™íèH’É“š`yÕakõóš[×]ÕQ f hë¦iŽå›òÿkÿr *ŽÀ79: WÎÓ²yóaœÚsÙi™ð÷±G›Ö>¢6ô¤î/%÷ªd—êXzþ‰( íQ¿f‚ýíU<<ðK\؇͎€¥ ÿ›"*k¦äiÒ†˜sú,V,ß ???Jã+o×tDõCe©{ VË$‚®‰¯i/‰*ƒSÍuâ:åå,Ũ&¨ä£‘v‰™¦J)#DX+uh’ô¡ºfT*ÑšP%UÔ£õP8#ï,íúD'ÚWÒWmÉÔ!c‘¶¤Ò‘$K.‰ÂZ$[~=ˆÇ¢[3'|óîD¤Ú÷Ƽ93áÈ1*Æuc1³;URõñU¹Yq"t¸xþКJþ‡¯ña½¸q ~8y¿.ÛŠÿ¿%Ù-áoá+-þÕ<‘{…¿“Ž ?‡ðÆ¢{s|;}"’lzà§y³à¤Íßüýqc÷OéFùûò… èÉÝ›¡uüñ~×®ÉßE¬´#Ý÷™[`Íê§5·?5ü.p@ßož_¬ŸÀU?k¹ß~gú<è+œ:]†Í›7*j‚ÿˆTµ\¨Öùs pEýà‹/¾Àwß}†¨ j„§ÇŒC“ʺc¦¦&”É*º’41C!ålº¤£À­¦€ö"Iû¸+(Í“7‚}ì¯,¶zì<Ûv;VÆ1,X´½{5Ää—» ÀùIu†°—œ´«&ƒüéš&_5èÂ|Ê*µŠÎ²Lš_oS‡õíÒPi¢Æ5¬RGµz+j½–ŸoJ~ äY-ÕÔ¾d1æ ëI’À¹J—j(¯ÎQõ¯¼°jx__Ë$ôÐZ=Õt½¦|Ú夌èúVOJ´G+C—µûTCç°j@«VUʡҵ.V·,¥%»]@GLxt¬ý _Ñø®Üµ^Ÿ<N\w(8T«Í¿³VS:Tž°E—n˜”Õø;¼^:þ®J…¿ýÉߣ†àç5kðÕ1Îaν1mòT ø3×ô{­Zã_Ÿ]›×þ:Û­½+¡èœit¦ªXÑÞÚÖtµWPà†@+8p ŽŸ=ŽAÏ@d¯¶ð«çÍš>_«¥ò:S¾åX—t¸Û(@ŸÊ? ®u[nÁµëÝz·ÅÕËñضb;}²{zÏâBqÖ%oW¼û•/ÍIÅ«JQ$GÅ‹Oƒø}íeÕF™ò-ñRñMñe™Ü§ºE)E~%òÍ{ål@ò•TäU!9ËkMù¢¨2–‘O)6ʨ“™‘‡Òï·(u‰q‰:oEY£R:wW×]QŽõ)uÊøø9y2ù¹xiófJw‹uÈ-…ŠrÌJ¥nö©„}RQú™fMÑÀÕU¡ŽE‹…4Ž)¢¥ŠH3uéæ)PFo ýFME¯aÔA§Š˜©¥hïö·RÛiIÖ ù4T`ê]^cø›©ä¿–—¿KŠ‹è ¨˜Ö^:þþg ÞBú>:=‡Tð·ù›óSmn$(üÍç”ϹÈöŽò7íƒ_ª)0’gáF4]©›¦À Ü?ø‡Æôåo¢i‡&J#¢ùßxPI–À9–o]º³øwÏA ÓÔøO’«3>ýw%šà¹Î/ ¿¯àæiCpȧM §¸½ ( “hOs.€@£ú˜·L²…HÉ{ʇåˆJ=ʹðO p”CùÖ\®t&Bu …õøçHäqŵk÷”K×yQ$L¢¨)«X^2WÔ#÷*>¼Xy,Yø–—mJ9v¤X< ”â3†ÛÔ$¥&–Dò)¯|KÖ¦4ð37Gš(òñ\ÔÖÓÓ6nÀŽ›L :7à•[šútß7FQ‘Уnƒ¹™!}Œªg\!qm¥2úN[óûrßå£ÕJÔÌX[µß§õ»»áu†´Ý¹k+BBêr :þþ'O[‘Ðjø›¼.Ð¯Öø›ójYY!ù{)ÛùhGù›qµåÇ‘#q‹Lúºt;(pC ÷ÔÉSðò÷D@#ƉVËsj¥oÊë˜8CÙÚÔªQ^¶âg_·)IC51¯ØP²¦'®J^ôè’GbÕëÒ£@™8Ô¦^œÄ[¯$;GÝ/Ðã?|{×îWfZñ"nÇ®ùße›ê èÅ@®‰»;q¥øè•{Šr]®éQ!S9–¼”rŠ›©ïÚGSVò± ¥æS(€³b²g{ 8eå[¼šk×òUä©è“’ŸYõ„>¢3 ä N5¢übÄ–Ø”ÒCE:⣨¯œˆ4ñV‘”â|úfQ‘ìIÔ.3z¡°06“…9<,-áck?¸ZZÁŠ:»¶¹N¹Š¤¤b¾¹—……áƒ÷^ƒ1%çQQX£n›®6øµ6êç[D)äÒÅsñõ—`lÓ¦ˆpwWæ¸Ú¨ÿ¾®ƒïf^žŠß{¦&¦hÛ¶‡¬ãï»å¹ ’¿/üß~óÆ5m†;Ëß™yŒH™†&¤nUté¶Pà†`‚µæ•-J¾$k/QÕþsãËYË¿¹ºz®”UfV”°ÙiáÜš€¥°²¤ê÷®w]òV¿Gi\.]N™s%.·¯%*íÿ};bbKÒÄ›ñ¨ß±< ‹¤MM‰êík*‘ûuOò]ï¾Ü»™úÿ.¯Ô'ézù®wýß”Ñ[õúyÎPM¥ô—h`"¢JMÞêù¤}Ir~nËò°uõ6ù…£Y#‡Šç døW„¿ìFõo‹6½[ .T'ÍÏeñ¥tƒw”cMÍÅ?]¯¨BùRW¨]NSYÕk•VùÚ¨si¨$·•cþV 1ƒ#”18@iZÆã"Zb¥3lVŠ|²›I—kIiH¸”€Dº©J‹KCïåq¾Õ‡¥¹1¬­Œá`ƒCG³a¬§ÂÄv­PÇÁ~4"u' U¤‹;#Á4Q9»Ä1±ýk¾ü5IÀñ´ºrÞ|mæùÕ…9½hè©É¨û¾3 *Njr2’㯢p0^‰ŒR‚kÈBþ_'áI>ga ùeký<þuÕwKFTǙ֡#RV¬À¯>_¿JÛuü}·<n!%)) qBþެ=þþGƒ$¨]µx}%°øI k¡°F—n9näÞŠ^Ðébdöô¥È!ˆ.dÜúÌ}غZ+nžü"ñÜÌî ãIŸxœ0õTjy¯¼ØõŒ”E3²Ë«^¤¬R”ÙT¾$€)£±”×”¡TV­óÎ{†(˾Š5s£Åcà`¤®……99ë!ãâIÌ:F-ZÃÑ6?üo5Æ„ÃÏÑT‘g—3<¡Ò¦!û#Ò]îµ(ï}efç-‚ ¥Ÿj|£æitMúÈëz”ð]ë£4|-±Ì#/ Š©œôNNeüJ;ÜBW|ÒHEʸxS‘" U$§vb^*¯±]–ZJe,RŽ'Ò±LyÍÔ÷$SERÊð\»M¡-o‹Î§ÒIJ)%¨ƒR‡Üá3‘Žpesõ0Ç™ãî! ”rUú¡i‹wHK=Fw20ÈÁŠO碫Z5r"Éeƒ«v’†f”Ê^?©siòJ>íãë—»Ñ;B’¨"»B"Õd¨¢ Q\R„Jb ó (•-Q‚c$Ð{DÜ…x$ó;->iéÙ 2…‚\[¡d¶œ–I*ŽËÎÞîîtyÕÈ™®ÔBidç[ œaÊàæ ža‰#P¼+ŒmÒFF4ó—g©0"û!ßòù;Å9–ñµ³Ãìþá·3g°íòeû¥]WÅðt_wˆÂWmѱy:úùÃT"¸És®…$=®¤g ƒ¿×@{{ÐÃê?¨• 5–¶7‘ÉînKܱð'Ïéß«NŸÁöèËôÝ­ãï»é1…;9 sËæhïëG‡3|ñÖÿã1Šui6Uºît?þñîÍ‚w ä 8±iP_†rúË9úõGø|50øýáð´¢ÄÎÔ iç/àÊÅ$‚ÝLEJ¾;ÆŒéߺ&bÀ ‘b‘IÓ¯Æàâ©Xâiì½ÝáÃ`Ñcdo_{‚€œÝ k_/öÍ’@#ç§À#È ù‰ñˆ¾˜Êˆœ†pò󄇇= ˆ%*­ÚÓRqñx £ÂÀÔÁ¾tÏfmeDéw:¢Ï§ÃÄ´q±¥¨YÙ¸p2E°tr‚O°;,诸|ñ%A¿™‰Ñ14²J%vUÁɇé½×,¹)I¸x29¹e°qw…o]7Ö_ŠÖiÈíé¼Ä$$¦ÀÑß› FpbÔ°¨® â/\ÆÕ+iÔg5„[ˆ<Üí)=Ofô›ÕØëŽ:Áöð&ØÊOMÂ¥1ÈÍ+‡ƒ—†83¤/%þ ÷|áÈ9¤0¨“·ôUÆÜ¿u/¾JºU>‘Ú9R÷YÓsu;<#hPÎEK9ueú¢"SN`ŸSœƒ´D†¼NΤ˳lädæ"1& 4–K½š‚ŒDF$ 5à²Ë”Á ¬“²ag¦Bý;zÂ…‹F//‰çÿGØØ˜+ª†\Ìè‹_#e&£SzÀo*n°?ò)$‰ì5€+Ùn&82ó#ááÚ «ù'`çfÔå½ ˆ^µžøãsªµ/yª(/™«XSìŠC‚Ÿ©šÇo¼o̯W‚]§Î Må€Þôe­'h÷nK¤›¨é<Ñ4j¨ãï»ìùÜþþÇc$ÿo k=Fô0c-w!?ÿã±ÝÝïÈåk&v¶nN€Gqdîv?XíÉ@XÓ0êûéãÔÚåxwêjÐè‹óL'<ýåDx”\Æ—Cf@ß—Rz~9JLdEK§Úxôëî•/ÿ g“Jà‰í)Žšð† vâç>ÂÉL#Jù ‘“‘ƒ\}[<ýõ$4ó/Á÷¼wäl®â’µ ¯G=‚qã¢ppæ|,X #®F½G¬åA¼3i¨¸F ]ˆ23;<ñÕ+ðÍ=Æ0¥G\z+9cððzX0éSœŠ)¥QŸJ Ðyê8 èì‡Ù ÁÂΚ‹rœ=œŒ}îëÞà/Z eå¯òäHÞu¹Š™Oq8-,áÌ[ˆ‡ |Z7 4Ö‘àߦưù|#lÝᣇè‹GIÒ¸Hé´%u5]“Ìšm9®…T!µPdãw£D®†xOWñwù›Ÿ·±™úø;cÅqªÝlyɯðI1=€«Æ!èÄ(vœ+ÿ:•SàQ óšªþuÁwWÇßÿŽ~·ºtmó÷?î/UÚÖe¤¿ }\y×ëÒí ÀE "¿Ro7TÊÖÖýÕQà¶Q€?F`óqræ#Žv»¥Èu'@šS5‹æ™Êª/B*-)k*ñÒa*;dšD}Jè™âFq+€ +œ¤Š£~1‰4âo-Ÿ#êR™ñ7 •ä$cî‘d jÊ(Ì뺤£À]DyA”*Ëý»¨S÷Wî(È­‰¼b]^šŸ†³ÉŰ´,ÀÆÙ+8­•Äôâ:؆föxðÙHØÓÚ»nÃ@xXo ¤«}Û¢…ÑAlùöP1«”VႚY¾¬Ü Çw‚‹5 -•˜õ¥åœ$­Ì¡¢sfKG FAÒÒð$H°à֮Жvf0*à$L&-§ËK{ãÐ|d„Õu¤ã˜´Ú‹>ë×ûÁ·|ÎÒ¸çÒ–h4ë÷2ÏGãR|<îO‚È6 »€ß·üŽÏŸý-Gç'ûÂ7ȽBŠ«ôÖ®èþX$ŽîÚO|Ù1é04æ6rÅ–±‰-zLêWKœ"àÍÑ3Gò‘CøåÄaäÇæS.ž†è˜\D( WÀ—!|êÁU¾zv&šwmˆ1ÃÙ;Ì[J• sDo߆«›Aã$Ò ?ÉÔ…£_ΖÝÛ¢^'Ö`….ºaùñX>݋±};1wC)·Ïó¨’L‹Q¶TZdÿ¦ÔƒêYÈCàÐNÈ\sëf. t½€’kF#4 ŠŠ‰… TE”@!æÐE¨hÜtfã&\\_ŽÔ"cä'¼‹¼  ÒVÆïÒAÁKéÌ_[*Y'ÕÆµÊ¥æH‰n¥¸^Ÿ¶Eȧ„:?·€Û´"•¥Ê Õ®ž‹Eà §Æ¦"ƒÄ•‚ÂÜ|”Q«ÏPÄ*îØÚY¼Ú"¼•D ùÉÁ ¬L-Æv¶f0ãv¨FËxÔ 0¡³ú#×äXË€A”¨•$×uIG;AÎ9d¿,F\{á“‚LÎ݃QŸ:v@HÓ˜kl[s )]0­Ì| ÷¯C¨"Û{†H9rÓ› ¯¨–hæÒKÜv.-Rïâ)c1¦jô³Ã²(€i2S3QL ¯r# ´´\¨×¬"Ï í×/˜XcãªãØ÷ó2,xoÆ?‰zƆÐ7)AzB†Z-ÄÉ톆ÁœñÃK¹ê4L¥M:ê‹1ÿå]0ýñêÞšAÜQK*P=a¢>µ‰r,/ŸÅ¯}ŒMMѦS¾”OT(˜WI‰Bw‘‹J?ô¸}Ÿ™”IÀL—^vþèú°ÌøüÄ@PŸ* òôõè¶JÜÏݲDÂÉ‚Bôcå¹–I°âÈüÒ<¤ÆÓ+y2›ô΢D6…F^‰—©ç-`61Ùé9JÿML¨K°êàÀ…õÅ]êûÁÉÅŠú×6ðò¶WT ¹°×a"‘USO(¨p%¿e´òù»mZfÑ$ ;Ï«#ê´ô×\Õ}ë(pÛ)PNÝ\Ãâªè!ÈÆìÚEgbðKÿ.8p`^Ør6®²T¡¡±úø¹ÑUø¿Î9©WñÔ’•(©ÓëûàlÌ Lذ6UA ¦®= W×ш(ÇÜsÑH.0ÄÚóçäËŰ½%>šð¶_× ŽÕ(À÷èáàƒµ@‡@‚\Úí\{CW˪;­U Ü= —ó›2Çqb204}ƒæxê}hŒŸ‰ã{Â,8f…”U¾”ª¿ | ²päh2{?‚—_éJ×!{±î‹%ĆE‚ÕžfΓ¹T@L)K ¦ó)…+7&9ÀQq£¢Oê~Uô’2K'CÜ}ÉéÍádP„¸£”BRgí莈†øò“y0&H ‹ðG±Ÿ~œ¹&ÆþhÚØG¶ìA©S<7£2SÎâ½Þ¯aײCèÛ?˜ð¦„xúH9Í1ê9àñ™Z^€y÷ 8/M1HRzF)ÁxbbnU‰ º¿4 Ml­‘’~§N$ÓàË– X=B½ò"œÙ¾%^ 1iF7dRýãÓÑobýÚóhMà #‚ÆSÆÁߨ±±§q9¾îN‚Zó{ú" Ú(UG:b®Ðø¬‰%’Ï_B®SCŒÿä)x”fá‡s[QRTBwTjè©Dk…é8r"áCFáÅgÛàê…Xò!õ‚%2A£k!·è r)©·5©‹ †N{ žFe¸špÑ)Vœ Ò¸…ŸƒS¯¢Ÿ-âN\@ü¹$8 ö®íDr¥ÆÑ˜K\neå"îbb/Ä!ù õcéCVôcE[B·[²Ø¢o˜SíèhEýX[x·ª:4 ó¥Ju¦¯Id¹ ¯¯­+ÏE˜QÃ2êçríféÍE¦ÿÐ[t ÷fˆ§Ë[Ë(£ŠŽ…µ3©ßnæúh‰Èï–cY\3ô BÐW¿ ÞiêøWzHJOD2í ¹P31µ€«w8\,q¨b±ggk‡@+.ëuÂ'è%3Ûã~ƾ¤,ô Æ„°ó8{ÊŸôìCîâQòPË#ÒU§£À¿¤€à îÎÑ—Ù¿¬HWüf(pW€\‘]éÑbß «LÔ Ì=0dH#|1÷|é– ËâD¬ù~;ºÏxÝSŠJ}DEåPFJ‰ËÑ" ~NöØ´{ æ~ŸŠ û @Ïy‡÷a÷aÐa½¸¯â„ZÎÂú&*E:hbmMÉ[–¼³ƒŸï ?ê>ª#að²~E¾Æ¢*iÇÀ­ÇôÁÊ!Ë0û-#xÚbÃìˆ|úØ#ìz(˜µN]êÁÉÄ¡íê"练ðÕîŒZ´qõX¹! §Gµ…iI’³Ñ ܃m¨!©@okJ5 Ò°âÃØY’€ƒ‡S9άýyú¶¢„š}×£DT4„CÚDÏìÀ—Ï~…³m=p~ÓnœI1ÂÔˆ0X;¨Ô:¹Å×ýŽÙ֢뀘•d")ÁÁu½Ð¢=~ÿn>›ôš6¤Ô囑ã‚ÀVui<¦ÂÑ_6c®·Ï©ã¼ñ„ O¼Tž…Å0JþK?[#¾lvÈ‚ ÂnX½&eô|@¿·ÊÂМQ -pzã:̳¹ˆ“ÛÑk† q»vàPt·ëquÕ6Ì[ΩCƺ'gcÖä/P/È—­EN>xkz{„·ôÆÚçÁ:#Ñ[wRO˜ªT1¨X[I¤Éy°Ÿ=÷Žm:ʳbEÊjii{‚TgºÖªßÈÎN¶pu³‚§§|hìåKO¦f4Ž”À Š p‹|¤wÚŸÛ¤Ë~¨Ûå—.é(p)PÎy§H (ÍõpðD} ²èõ¦Ž« œ97tq€›5½â0Õ×»vá×4ã¢øzÕÇ;í£!¥±Gio)ÉÕÑ a’—¡_5îëÄg³:.-%ÜÉ*¦þ®e]ÒQàî¢y²±ðZwJqmØ5Þ®çsW€\•^m#1̦Öæâ”ßm_‹-غñ réNlØÌɈìÚzYñèþòøºŠ‘X Ý‚y¡çË} BléŽì1X.àVw@IDATØŠ«gRаo? {º=D©œ!"' …C¶TL€k¨‰Càæo'çp ~qΜ¥¥{¡H"$ –Ãܽ†¾h'3º[2uÀ1½àéb¿°nxu¶5v®9†Ë—èYá­Ihß'œÐ¦N›aôk…plÕŒ£(„kËÖxôcøu gUèûúX8ÕÛFÉä¤ÐÃBwÆ£mÆl±XD%üvmÕcßÌÇÁ]WPàî‹qsBô¶í¸˜–}ëô|e ¼™³–¾”büò þøy Ήc³VèûPÁ¥ÛSÿÊØNû‰ÏÁÈ+NE*u•;N‹¨ÞaÕµ—LÄú%»qöXêöìCÚÃVUJ «1Z jËR.^1¦dù9tlïã;<óz1K„©¦,è‹Ó«w ™n®¼|ÝŠ)]¦ÉH©±=F½ÿ$~_¼ 1g3ÑräP<òL6­>†TJo›êƒäÜõt©–ׇ;aÊO6ò;èš,aAû‡ÛpKÓƒ>|¶óÖÓMZ,»wG£®9°wæèjo¢ç.ÒÛc;N!„.Í^x¡+ ¾¬`ff¤HdmlLannÊç$? x•öå£Ûò-J$juܯĶEueõ½¸Ò%î$d5³BºÜ•ËÍIG½ÐD’7‹(å-æõB~Pâj\fˆÍ: w×$,µ]5–q§$Ÿå%TvýíŠ S>óšP¢(ð0Ÿò+ä·ÌÕØO\ðÉ//—õ˜+‹½;I]Û: hS€œèÆ'/ ×Þ»K»ÝñŸ)À9…³Äߤ.»àBÒy|ôÇ{°%˜¬Mp¡iš0R‘IÝÒ!9K\j@$85_•3É)Ó™ºãê{j¹¦”PO~RF-ÓRO‚’«b:Tš”ÚÕS¢¦¼@uÛš>I/¤FuŸªSKTÉ-}¾IYMÏEö{½s©EvÚ•‰™ßšú«¶¬»f$•rAM Úã—:¤Fuïe4r— ¼V™¤F©GÓŽšžê'©î¯º5¹®Nù˜õ8ŠºÁs“Ú“Ú¼#ãºKjJkÓYh¡~vÒŽº}雺f5¤nõØÕ9Õ­iÚÖ<#ɫ5õs•¥])#õkÆ"uþÛ$í%ǧàÉæOãáølæHV)¯Lé§$Íè4çê«wß_¡›¤ªÏ_}íFþêãá_)Á 6?:–TeQëÝHY]Hî~;³‘Ôÿ:ª:9cå¡}ØYèŠùÝ[ré0_¾ !1Іc­]œaF]ûÊT†Ëɱx—»O‡J\ðYÏp(¼Š§—¯§H7oGE@/å †­Ù†ºmðq§8{` Fí¸‚i"¨êe…Až0üûW[e“º#HñàÑñÇ90 ¶ÀšÕO);tµFQsË̸»)aé¦Áƒ¾Â©Óeؼy#§­ïŠ!Þ’\¡„@4š$çÜèR’@ ÊWvM9ÕÀC Z55Tý®~¯²55l«š[s¦}¯ê±„€Õ¤Ê#¹"ù´¯T=—3͸$·ö±œ«Seïä¼rìê»R‡vÒnOîUÏ/yÕ „ÊRÚy´Ë«kVzwZ5;2…ÒK lÕÔVYWõ#m¨]·v›ê2ø«>ÓΫ}¬Mª#¯Þr휫w;¥¥šŸNí´r+j!å³]ðu…uIG;BJ_íl½ Fía©*Ä^z‡qõÃÏ!Áp§¾}l™%^êÔ‘`”þ°Å¸SøµšŠA ÕÚ5h…2›Qâ[^bˆá­#•õV9}æ–ê›ãՍޔà2ŠNQ9„á©\=\ànWc/¸#×5ª£Àõ(@¨µö0ðéàóÁ@°3ÞŽ·Ùõúóß¹~×€ÜÿÉï…‘ÊBÂÝ?}j²E¯V¥¸zþŸî£žÍÚLv¾øÇ·ÿO“B7ø;HJPݼñ¤SÅÞ‘¨!Pº«x; ^A3ú2o&ê"iÕ|ªu7ÀÅ®,O ¬À–ð÷ä¹’ŸÀׯ4u@6rÄS‘íÔ÷E7¾h®V½îTGÛLòdZ.p,–nzÄ>ƒçºt[( ³Ð-Jÿî!þ»Òµ?$õ&¹º^íãÚoé¯k¼]t• {E'µª¼õzý»™ž]/ïõ®ßh›R^ó¹^™ûõ:ÇM05Ç §ï×AêÆuÏP@À)%Up¥Ïr,U%½×B k®©ïTþ•ëRF¼$H19×^•s­:¤”’_îI9~ë’Žwh¼ŒÆÞBh<íÜm¼?ûs $¹Ôa-ʧŸÃX9[Ó;2ÅÝõ ³sS ;Wz>ø‹’2Ÿ)í‘§6n‰öifJ:ÃñYÂÒ´©)ù°t°aHßÚ¨ýÆê*æ¦e ¿Ø¶ÎV¤‹Ìô·&•s«0ƒ®yÌm`,±~+R)Ý„••Ð'±Yå5¹%jÝ%yôã+‘—Šxʸ^*e^ ¬2«ÌSÎSIýK$/Å †ºt™¸c^úË­ÂIÒ&Cá2ª}ìRw›!žK•ÈJBj&SçÉPú£4qëhu½1Þ‘ëb}ãN'ãtפK: è( £€Žw ¨úÖ)˜®ý8?ÓCˆNUá¶=˜ªh¥š•`'âÃ^ïábri…¹“6<Ñ>®Þ ÝcМZ±oŸ4:u¥Œæ£.#&i)—‘[ ½j×ä©Þ†æúŸÛS׫}]“WS‡>ÃHæbþ«ã»9§Q–pÓFÍÀ±sùôïX Ò´k¨ùXSoõ»šëšö4÷µ¯38AíÖOÀãѬ&ºh—¯ZVS£ú[û^Õ; 8äó+Œ¿„{¼ƒg³¯µ$Ï!fÏ,ùa'r*üáJiÑÞ-NMÀºVÐØ$GWnÄŽÑK«WÎsëåec;ó.Ÿ· yt'$IŒåò’b°â³eع7ž;”ê§./lÙ„­[ÎsôÚ+ ºšËKǦ¹+±ÿXy¦§×ý%³~ÅòoVbù׫°nÉvœ<£D»“’we€·å¼,¦÷^ív[ZÔ5¢£€Ž: è(p#ù™jd”æ2z;âFÊéòü[ h#‡[—R^Ey韋CýŽæ¤¢Œ®L­(]*gÀ€¼RYѯ¨ß 8W-Õ32§ïWÖÀ )8ú"¢»ã’—£ˆ÷…)ÊŠŠQTD'\&&0fx×Òô+˜7yšO~ÍC)¹£ýAN>êÓ‡¬!ó˜R’ǾÐ1xQ>=4ˆÅ"ÆH·¥!U.݈QÒW’_@77ä=º†26Æ£šaY r ¤‘)ÛâÖ‚H‹SbâQ蜧DK‹g€‚œd¢ÜŒþv+ð\)Ë—ê³ £w)Ô?ìýÊ䢄[w*Z«[QGLO©g!둵úJŸ ›ErJ)e1iWDé¤8663…ŠýónŒFî6 Ü+—²Ù,[ÂîôӪǭ<=FÙÒg”·bÒX¤›…G9ƒW[’^¥Jóè*’TÙD4渥^IåìCAÇW®BqA.&2Ÿx¶P'­y¿s&¬ƒ3ûÑ)‘fY ÜÁÑ[7ã«I¿â™ÍqæûÕ¸èÑ  bš:ôpt7f=÷ ÒÌÝàÜ´!:6´b.}äÆ^ÂüW¾Eq³ ð]ñ"¼Õ4<½ìWlÐoƒVë3P PX•xB(IOƲ¾‡×ø@t ·ÅáEK°ü¨š6÷¡T7ÑÇ.Ð'§9zL…¾ÃÃ)‘¾1¥‹ŠáÞ›_¨®vÂrLÚ‹¿{s8º^ë( £€Ž÷8)G'‡®ô†·ÖÝÛódkä*ÝæËV¢Á\´ÛÏœG®5"ëÓ¬þé8}´‚/Á¦^Y>Ö1 N¾m]ô֔Ѕ•” Œ€K¿{æ,C¦…-òcbpæT Bë¡×è¸4wv®Ý4+7ØOèëL¬·gN&rÜõ»·@Tç†È9k~9sÓ\\LpÆ“S›bí·ÀÈÓ ‰{">¡þQ­Ðsx+˜08Ã¥›±sã9d–Â-,í„@'âl‚bqø/ˆ×ÀعOaÞoWÐlXO„øY(€î诿âX®ŽhB\. ƒù‹ópdÕfìXw é׎uùHG„ùXãÊñcظh®^Í‚©£#êwn‰¶ŒæS˜|ëçr,§$¤ê4oŠÎ7&ÐçV>-…F‰'O`õ[‘Hêuš¡$1½Dë4¬ž{®uLqŒ>†s(oŽÜ£}±àw,ÚŒýû/R½Í u"¡}ß–pàâòì®X9{ò ,P7ÄE†™ÒPE’ÅGp»N¨¼KfïC»p:µV€z¶Î^›¨î‚ BF8¤•‰òZZEoýn,š4‚Wf"Ö¹ mgõ$­¨ÌÀ64&É‹=€Ÿfmó/w€¹¾Št×êŠR¯W5† ¾aJ0/€½¼Ì¡ý»aÒ”n\Øä!') «¦|ˆï'΂gÄ{ˆbÐ ÚdWvç~<]Ç3qdVþøïÇêÆ¤£€Ž: ܃ ÔÚvŽÁ V+ÆäŠ{­{Í{Ï=HvvY-Æ«í¾SzX˜{›…_‹†ÐO?÷F~Žøä\l³ËW\à³1òã/â§—àRR9ÏQ—“}¹´u;¾õ;rÊ p‘ª Ÿ‡¤b{…Ú`ùßbÍoç`ja޲Bì]aªŸƒŸž~¿,»ˆÀ6àd™…OGLÇÎÃÉÜJÃÒi_cùÏ1ð pƒQYÖÍ^ŒÏ__ cŸx9äaî Ÿ`ÇÑ,¤݆OŸ]ˆ2go4lám|9ßnG{«M) ©klm€½_.·(3£d9' ^˜è„<‚6µhW$ŽçöáÓgæ"ÓÈá‘8öÃ|Ìœºyܶ_øìÛØq2õÛ4„iÁ|ýÜ$SßwÇsñåÇI»úðu–¾ô%öŸIıÅk±ô“­( Ÿýøtl9˜Š°æup–4ýbê\œ<•†¢ä,ýküôù øF„¢,‘´üG¤RŸvëG_ãÓ7ÖÂ%¬‚Ã,ñó¤øù»ÈK¹€ÏùWˬÞÄ'©rp9…Rg­%ø[0vôBÇ^ ¥l Î&qÔ†H;½¿¯KG—‘­`ÅçGÒÔ˜Dá"÷ÊI¬[—„#Fbøø†8f€‡¤|Ö€ƅ… þÑOë_ý€M»¢yUÙ¨±>ÍEíæD%UJèSlmëæ‡ß‹@ãK8¸ó‚"-Ö”¹?¿9xJõ1yðÖêûsˆµ8*áYòè>µKƒ¿zDEÔ»/C ]ÒQà¿H>(‚²ÿâàïܘµ`L-v‚™‰)úðI jçËáÆ8Öí$ZŽGT+ol]°™CâÂŽ  :jÀÆÕR6™UdŠèj/£Þ î5u8l‘„s‹¶"+±u‡4…ý‹›9º<ô÷aùo è2e Zwª‡Â&n8øóZìÙ~Á­afc†ŸŽCïÖîTU8ƒRC348£Ç=ˆü4_lXÿ£3a^ƒß¯0?X—#¶õï8G+ÐI+Ñê×Ü#½‡ÕÅ—‹÷ u\íÛ…c¥Žx©[BbFéaví†îxøÍQp©ë k2:nÁºèú„.FaZ!ì‚м":†¢ùÃùp´,AV2 ÛüƒàåÒõh 'k$QíÃPe‚üØÓ nÃ#?>Š!]ëáj3Kì‰xŸ‚URŒê&Tlèƒ1ÐÌ õ¼ pbèzd§ÄbåòãpmÚí{4£žq.,_‡Ã›vàLˆ7N¤:â£WG M€BJ°kó/ì½v¢ •Z éˆo?û»(%n0¸öÏûe¾hHUÄàÔ.Vq¬âªuÏÊíHÉ7C³nðÉj »—ÞÁŽ­—ôp¨"5Χ~uHÏ>è°„¡™§,FÓ•Ôª U;RCí—Èwjù±Ã(ÊÊ~uÍ©·}ý~]¿²{ôލ¸˜é$õô„.gd 5?Ÿ|Wå—ýWÅt÷þ†²§âme {31¬©–8§ï¹rÞÜ­ñ'ñ:°[@ºÓû›Üe sžî8Yq¨7úR»¿©r;Fwk@.Až¡¡¼ƒ¬{òafiɨzHM6£›cшÍ8qòNþ±{!ÔÍ„JBÝUáP«³Ä®~îÔÇ$($èµ¶4@)¯XÝ £S·Hº§†%Ø»`).üÁíòOJ™=im_Â|*• ü‚m £©ÓÉ{†FFð pg/E%BCÑe¥Þo~J2ö,ÿ¿}—ÇÈnNˆÙûÁB¢êýb,×dHW|ûÅLì?z9?ïƒ{P8†Ø³‹$}ee`ÿÊõHdèZkG$ïæ¾¿#ŒlÐçÑøêµŸñÚæðpAXûNjˆÃûãì¹ðî€ipõw‡˦x¤~Ö§þa”QÇ×È™/ o{Ò7Öa!hàfL_ÉBÚ¹À“!‹óxWEof¢wKÝRåê¾xô1¥ªÌ«Ôƒ÷0Fnz.Lýœáä`Ê2Þ>~p¡ ƒè+k'¹ÖAMѳƒ-Ö.Þ~í-±~Íy„t} ~ÎcÍEÉ¥M11N+Ì‰Çæ%«çˆÕÓ¿†AqÊM °áËÍèß7D_\™ÛaäëC±»û§X¼°5<ø¬ôdÅpCIÓª,¨ü’Ÿ…”øØÝN77ÔÏ[‘‰¼ÁEzÕL¹ó K5S€ 6«°Ïü±Gã“`Ê߇î}S3©nø*vâa%ŸóÒcáñZÛ¶r¡²8i.Ü/ìG=Ϋ/EF*óQeÝ‘Ž÷;ønkèÅ/*/Öj/×û}øwp|·ä„–ç!‹ÆVD-4¬’(\te¢¯ÖíÐØ~V³I›3ÐýëEò©…ZTN‘”Ím©Ï¹i®Lžw è”è_2‰X¢ÍCЭ“; ¶ i”U+?_èŸÛÂ*¹MFC-í:K+êT$¼¡O#·ŸÍÃÑ‹6˜öÓËð²ÓÇOœÃq°Ï’Ò¬fÞ–+%4Ôr i€†Í­±ú£%(?Œˆñ#a«RKqæÉ6üÁoÁÞ9xuÑd»›bŤD¬M$\$ètmÔ“—†#åB 63‹^úíBîíƒÇf½‚ì¸xX¹ó>ûÖ |`A5鯥"e4ËÎC1+z&HÀÕ´Rh0 üÊÄ+]ç¸•Ž‹fŠü¢ùùÉÍQZ\¤½Ù; àä3RV.ÒÊm è>,I‰>(˜©j…´ÛËú¯Äê9†¸Lб£šr¬ÏHèDš•që\1Z¤ñq{vc×Á´ìãÛ*6P!´Uclß´; D¸cY10tŠhÇÇïÁ'.B}ï,è‡þ©#ênI[U:Èç̵Ç,F€Ç—®Ã¾8Lô ÷iGl«Rèþ9¡^3FGÝ?ã¹%#á~]äŠM„ƒ‹5zF5䜢{áüRË>W1… ß.ߎ# ´#¨!%dfbë¥ËˆNÏÄs­ZÁH'A¯JºK÷5øN‚Ò[3%m|téöP ÖA.á&=€e§á÷o·¡îÓͰõN$çÚ£N  ®1|´9^¸-Ú#"‹@¥ÚKF¹h¥†Ü’¨%îÊ ²qvÿY„¶sAwÎì=‹»×¥p–¼³-_P™I)£©@¾YþZ½ÆDÒ@0X¦G´¶°¡ŸÑØý°÷HŒí¸­™”£6Â)å¤|1êQBüàCÍñâ ¿–x¶—?GR& \KølœlaËxÕI' ô¶Å¡ÐÕWÁÊ) 6´?ÚFÖEäHì\õ вSðûópÚ´%†lŠÈ~­°}éN¤¥ä²Ôk3¦DØz¹,\‡6ØûÕ¯¸œ—O]`é “ô¯âP¤Éå¬ìÐØÏëCFq[¸šäã··À¼UhÛä_±né>Xv÷ÆÆïV#´ÌT=‰ï¦Í¶sß[ï q -e¼e¢¹L:•#;æ Ž<ŽêKÓ6.æØôùz˜4ˆÀ„¯_‹±Ôbˆ²Ü‹xºñsøcÎV„Žà'Û“&é-íÆ=‚­«'aëúD4«_È•ç Ñ*ž£ =‹ˆûô¡(/ÈÁémû±jæ:=Э[x²½ªÏ¥ú¸î›sL*D¤á .]ŸYÍêûbÚS½iúáë“ãßÝᯀ ×õ;Nð—[äAZo§ªÂÕ¬,äãp#4÷ßQ]Wúž¢qȶ“À÷;7{þ4¶©Ž{î©ñÜ;½ñ7a sWÍä>d¹!܇"ÿØvL¾YtuÕîùGæ!J*G5‚ƒÕ4ŠŒ‚›¥x¼T¦NrdâêßFj#4_?º›W€}ØxCÏ™z¶¶.¨Iã°fÃÑþIŒþd>sÞvH¤ÆŽðöv‚*!žõ<¹ƒ+à˜/3=CxÔñ‚¥ ÏÙš¾|êzÒ} uL»`ÝÿÖâÑoÑ¥˜=ÚŒéŽëObáwûáâï,‚5}Ãbøy˜Riq~Ò£)',…×€Ö”þšðŠfo]Z+§Å'Ø^†ÏF½3„èJ=ØÃX1÷4êYcËó°õª`¼Öö 4òÆ¥suðÇ쵘¾‘ú®”ȪµÒ¦l‚+ÝbÉâ©2ñØÈÁ>°q0SèlçïÂM'0{êyååYTT†Àá0ì¹Þ°5—餒Ç*ë¹ÏŽøü1Ÿ“¨¨+ m~Ÿ ®ö‡#¤ÜRù³^Ní7u×H+»uš›êƒ-åBtÁ‰ÊåtêAoŽŽFCOÏšàpõ¢ºsî PP üNUÁ‰8¦T÷ÉøïÜ0nä*CÌ_?‘dº7m)ËšÀ’®à®À”[Á7Ô†ÜFÏÏÌÁ¥ƒ—``]<Ö˜µ àÑ$ÑúÔCX¯ðjW[žöSénÜ(…ЈÞ_Ñõ 4¬±€ýà>:s âcsáàç ›`L­_1—ÓéÂÊ‚@ÖŒºUh[ãgסtVŸåé÷ÕÔOÐêÞÈÖ–Ú§Å(7wŸ÷åÜÖæ½ðFHc¤¥ÁÊÃ.Ž&è>tDrBÉm×`Oîp¨àõÔP4)¢à²,•Û¢ãc}èîË6úW0é×ý0£þªµ_ÆÍö„­!GW û†1þ»`Øš”Â$´1^Xê‡KtMVªoLàN÷kÜ:€ÚëµgРo4 ËTp«ë‚"êéš9[(4«‘Ñw1ŸQ³g†ÂH¥´j]g¹# ˜.“G!’‘Ëi»:;6Õ,ø,ÍIszñ½ö¼%WËçžBÝ”b‚UcŒŸ^´T@«,ºôÆÔà¶0°²ä“¯,'0ØÀÞú2ïÙPóXmÆEÃ"Ån¢?dKØÙŠ/BÌÚY:õç$[®÷ö*—¢Z3?`KŸÔ:û燬»rû)@§èÔTì¾zõZÛ¿ž>ƒÇ…Ã’»zš˜k7u: ܯH”¾ŽŒœzã°ë~%Åí× QÛÑɇÏBZb*}™Ò2P"+)©žj:-ÒQCúvu¢¤SŽ«¥pâݵ(-«>^†ßîB½1O¢‘õuˆ¥)-Šî¹,­hø$ð·Ö”ê P©¨")t¡ÓZå¼ –Î.T/I°ÀÉRXð¼ž3õx_ÈÇÐÔÎt7ÆûRC9%·ŽÎ6£‹4]gOGÖ£¯Š^.Ü8~¡‘ü34·EH„=ë%u½J•†Fh\Ñæ"!½žü“ ÓÜnæÈ¡2½ÝÂÆÙ vJ…•„vÂ12~yŽ•‰¼bn w%J-f¬¯²MyB*8y»2»ôU¨²þiž›ÔiéäkaŠ$OLúö÷Izƶh$5.zÊù°Ñï=™8ö]V ºt÷RàúÓgíôYÍÒ_׿ûû𮛣œ wÓ¥KH ª‚&펽ŠSÉÉhêåÉŸžö| É¡ûÖQà~£EI„Ü!…« '¢%]º¸!ûøèDZeËÌœð½4A ëR˜j¦Hèhˤ$ ”'x!°F’XØ‹¨ÌÌÝ}?xmû1`B5è¢dä)¯aÊšÔwµÏåX[rª>×´¬Îÿg°SüT=ׂ}J⊪zЩøÊµsD¯—Ç¢yŸæÔ—•«5'[Ú÷´Ïjª_( .£]J ±¥Ê)Ñííà¸r®ÆfÁiÄÃù@sÔñ4#=ª–‘ñk÷Lj®žGÝk‘¢ÿ¤×<¢ªÏH;<m8ª}¯¦cíg]µç’»ê³©Z¾ê½›i÷šC8v´”à6/'GwÇ¢‹áhg„¾ý(µ¿©QTíÙ9#X%êÉ]iÔPƒóéÔýÙ꿇ä[>ªRÚPƒªÖ“üöŠ©†!†]W½¨7åJdENqÖ–fÔú»7Ù]ö!îûVœ=Ë_re*¤^î¢S'ÑÔÛ«ò¢îHGûšüXÐàÌ”ÛyCÞ×D¹-ƒ»!Û–.a>Ÿõ9Þ}û]Lö>ÂÚ„¡Uïð¡N¤›§+Œè†Gß”^ 5«öd©žÞ’Úy¢ëoÅ8A &µ§¾Û2ÞZj„2e• zéˉØí‹<úöE€¡ÊÊ QCz*0P¼&ˆÖé}Ñ릟^U>Tž½>”V4ŽŒ»š€Ë§.aû²8ºõ(}mññ7#î£Ð󦛻ÓÄú.Ü—½‘þyÉp§»w?´/ºóYä!kê„ÿ£¥ËŸ>}3TèÜÒO ‚óö®0Šj‹žl²I6½÷Þ :¡éRņ±£ˆ;ô«ˆlXPl‘&UªôÞB€%½·MûçÎd!Ä€ îƒÍìNyåÎÌ{çÝwï¹õ'—*¤RKº3±];„À®ZQgþå%غm?~[ƒƒùZ¼óô0´ñ£ùÛVo‰ 7Ž´Œ¢É­V%2ì6ísíæÜ¨Í­-ãïOì›Ïä€ÑH#æ ØÒtáb†Ož—Þ¢K¹’ÝÀѦMÌŸ?³gÍÆ”g§ÂÚÕ~>iЦ#áEÛXs+srOê”estE+[þ7hæþª•c×[Íç¿×™4œÓÈþ—ò`§µR†i‰8Qm¦']Z1#ª•– õDl<€ø½ HŽMB^Z&ÂBÝñþ;Ñ@3xx\ÏKIloZ¶²bWÕ°åz{«t}IÝwúànLܘŠWW-2åa«£Ö|,åb"n8LÐÇXæXòëLÞcƒí ‡;W€®xÑ@)KlÊ«phß6<óM ÏzvVÔÕU?2Ó”å$ã­iK{²ˆÑ$-1ºm‘úÕy¡—¿]@ûÛ|¾{µS\†j§Û;<œªçK_Aªñ·QׇµÖÞZ ü: %¨ÑdáÚܹK¹R<þøã>|8¶nÝŠ%‹—`ïž½X5u5æ±€,Utž"ó@`Óø†ùÀÎÑvÎö¤Ð²‡WQÍ„ù‹<¯¤ê- .‘™ˆJDú×j¨RÓôAö“QÊã¡<µå?mù„Þ´Š|Ãd+Důåå((É'×o.rÓs—S€”øS8“ˆ”CÉÈJ˵¥|íѯ[ nî?;ÃV¼$•þß›¸\Ù]¦„„)à¹Ù\{¦–`ò=W–¯Ve.b© )]qöÿNgõOŠg_EÛu-Á P ‚-ª=>7 ~ ­¢¢BdšÂ›Î«g®ÈÑÂ}Ûøb~BŸU®q"¦^§Ö§B¨ )y©¦ä¥àÎZU-£6X"µ™U,aY,ËËÍ¢[ liß †¤!Æ• (±B`&:¼?ö)˜Û†þŸlc´ÄZ…\éOÖ-_¯Çâ¸#uæTB`»†ÚÜ!Áª,Ù&c2Jà†–€¼T´ ¨1]; \È5TËÆÆ={öT>9 ‡c\’Ú¹c'öìÙƒ ?lBaq! t¶°¶·†‹-층£³˜/?®öpâw0—ÿ~Eë+Mu$%ìæ L†Z·×dì­b§ |»•ÜVЊwqA rR³‘“ž‹lj/ ßs3 p3rP”ÚÚVP‹¤#¨õ÷w%¨ B›è„»#4Ì Î|Õe}ét®Wp[뮦æ•{…IÏÉÃ邨¨ÜXÉ„¡¥ •gI…’—ѺŠ28ú‡âéž°$*,ÊÊÄÆÃô0Ç©Ì8»»#ÒÏvÞ%X¶ôìA8êâ _Wa©N| íÙÇ¥#1!‰|F]ìçÓòR8˜ˆ"šBi+JäK…<µÕc¢0”d¦f )-Ÿ}¥!\°æs»jÅ*l* Æ#7ÁGkNÐ dæd!÷h6´6ˆ b„Åš}(_&3îoåbNZ‹Ù4*ý÷[™tJ žbÚ8Ÿ¯¬Ùœ”ŒÃ™¼x>ð}Íš“ D–nÉ(_eŒºYʾOWß“>ÉÂé}o4àÌp¢ÊÈöï·÷¿Pƒ³}ï?m¬c‘GGG+Ÿ;ï¼%t4(ààxøða:tÇáôéÓHKMCv\6Žï<ÁÈZ…(ahM‰:&€ECUƒ…Îæ¤ÉÒ2Ä®–aXµäjUl| xMµï?­¡ñºëQ  ­µåetcD%=£¡éiK[L`PL{>-£ÆXŽÅ’eV:3jcÍáHfÀ@;¸·÷áÖ <é;N¤, v5d×P“Á ÀÉ’HÛ@Ú{ÑÎñ ’ßÁy¹t1'Ÿ &ry5´Kë‰ö4³¤˜œÌ—S;žlRŽ?–­Ä'«NaúÄG±wÎ"<93· A.£(¶côÃÇ I‰Á§Ó7#Ç7 úôÆxþáNp1£ù_#Ú\:eç¤báŸûPš~[ ñú«#ž‡'&.†ÞÃZ½ ^~òn hΉ5¾å0+%¯N^ ¯ˆF8±ç(‚ÜŒ‘ô,k#N¹ŸFW.^ÈÉ]>›¾®f…Ø›Xˆ÷ßx]üɺÁ|Î&©‡2a<»çоˆ[>C¬oÏ=‰‹RV&HÊÌBi9C³?×PØÂ¥[Á:È;[É{O{Ýû.‚£•N®¨Æ‹¨ ÈDíhvºj´~•(í‚Ø?ó£tîH;Ž#bóJÓ//>’åps ÀØ'n%x6^=gîÃŒ{½`ͼC[¶Åó}ƒ~*‰ä´f?hEêÆËÖ`ýñ2ŒlE CO+d¤Ò±wKômâŒNý1~ÐÌcÔíš`H¿ž¸§£c_šŠƒ)¹è æ8W/)«o¤–4wÒ©¥#rr ‘É(Í¢|áíë'klÙ“§rпûúR$$¤ádiü›»sÜçMyW¯^9%p)èÙÒ · iQ½šl7.åÊ¿9GÌèô kFjú‹ Ñß\k<ü%P¯ ÷bµ° ­¹_ås±¤,{Ԣ綻ÆxìÆ•€Lœ ƒ Ëä/&cÍšeèÚ5Íš‡°ñbj ‘ás£Ù¿¹ÏnMÙ^È­ÄÀAQèÓ·qö Sƒëà° W•rС㇜D_Nuùl10Kç6aðþ= UZK´lŒàì¸X Yْ礈œ˜˜kÉùL0LP‹ª2løs–.dafùØ­påWEƒò®J€N:5öÀÖ#%ðoŒ;„3ºbú6dìdDÃ9ûP±1´I$l H³Ù­†÷kDs†fœjéÖ@“0k ÝÍ›^¯5¹°Û4qá|®¶v:>õ8P×%2foFÓ4[ 4 ÷Æ?<„˜˜ãؼ)ÃïkDzïhtýÄTü¾ø¦LÎ}–HNNÇâÅ1ôñhËÉ©Á&¾®ŒûŒ¸vɽ9ÙSÔñ¤¾Êe_°÷Àç#©üó®z£ò®¾¤{±|®ȽX%j@cÊ¥9c2JàB¨¨F'¢mT5³7ˆ=í…|©ûeÅcÙ>öÐ|­û7½Ô«ê8<«J'_Ç¡ë~—F1sa7sÙ©ŠØt:2æ–U¡ACÊ®<·¨Ù…¥0/*E.Mjò‹å™4¥`N$ÐL+Ú} ÿ+É”«\ÇT¢DOVƒ¼Xé ±-!þAî(ÍËG.5=¹y(d9.!Q˜ôRE£nJ’Õ?Æ×k6#¦›ü,‹±û`>ðVHŸ Æ7=Ó UÔ–—##¯Ú2—”‚vÕŒ~Î.ølËEª-m} èb– Õjh"äE*¾@æoÈ«ý-X¤#QZZš"<Üaa®4CªP®9[-ã£þu Ô÷Ęf?£§~¾†áE›äªA®þõfþ*Ðà@î@æÆ&%p$@ôÀem|µŽa}i®qE WªWßüUhò?Êò²ÔRhž 'Ý´;v¤gÛ»7¥§ª›úÚÝq)°ÉO%}¢-Ž9Eµ+)Ã}±cß!ü¼0ýuC˜59‚÷¨&2g´@?_K,\±%ÙH1Å+wD"þà!˜;»#÷d"vÅÙ£K¤ËY¡ÃëÒ¿zÅÎÅk“ç"ÐE‡ví:ÐN‹Æþøs[<~ø5Îfðt5ÇVj¼Bµ(%MúÉ3È,€;¹èÏ»­¢²¢3›VKYAýõxËE{ËR•ò—r-·£”#ä·† —²©ÏÂk”jüj”@ƒ‘€Žþ tB}HŒéÚIÀr¯¬%%põ% £!'쌩ž%@dffiîݻҔƒšZjéƒ[Í5Š–Ô¤Â ŸµhJZ06ë}ë¶Ë‚…í¹O& ’¨áui†¯_m‰b:¸¤—šâѼeUÈ3 Á„·Âiá G¥†÷‘À¶r[ 'o|0îa=•¥„çtç?mx» ìï–™ an G†SÜ3šÚÜ*æ©Áó£îUØIlL%/µ gÿRóïè‚gïÔ!Ô™ TEŸg_Ù—Ú…©Ù £…æ<5ú_Ï»²rW%Ð%ÀÕÆ›"€ šŠ·æ°!Ö÷Æ©“q4¼q%ÿi ,PÛˆ;Ð\A´hÆT¯ 4%㋵¤gÁ"µ’êwjü¡üæw‚SS +D„„ ÷]-V [[Xȹ¤Vô•­ç˜gç@-üC à[3‘ÚHÃ|Cƒ ùª¥ÆÜá!~jY5/a¶ ‰šu0äI§^§€PŒáGiˆˆŽ×óVñ¯`û³zÎÛ˜Q [|1=É”âA[\²´(ï[îð S;#Ƚan¥±!ÿy ˆÇîà6ƒ £r½?×:Aîµ–¸±¼z–Àµiê¹õ”å ¡ÇÍg0Fzgp=å{ãf#ö¡ ä2>BWv“)?u캸 UÇ3JÝ`Bqe¥^7W ˜E»|˜–• h-U>Ť‘ËÉ.Âñäl$ÏDj:ÃAg‘;¾… [BÖŽb²v(4ˆ‰„¢N@q°ÁšEæ—üu#ª·¢rƒô2›¡‰Ñ+‰©‰¨žô5ôFž/åã% 9ßayIe¯€^K -®93ø¬j1nÜ8øùùÁÇǶ°²R9ì…Ç^À¯°h –ïW3AîÕ”îUÌ[ °!æäû)éI‰$mžÑììb6ÝhįsEéýOï#jtK¯ú#!³rKÒAYJ9W½Èz)Àð¶è‰:²x†Þbõ’ïUÉDÛÐßoÖ±´HOwq9ž³²0ÜƒÄØFeÄ—­¤ÚÇÕ½×Ç_E+ÙÒÁɧ°  ‰™HIÉAzZâãÓDP{òt.22 è§X© øZ sÒ-[Ag£ƒíµm<éŒo~,©)³àq­D% 9: O²F(7¯§ðú¸‘õ_Ky¼ås}(pϵŸý¼Û•òá ‚^YQУL¢ëQTXŒ¢‚BæÁ‡ÝEy…X±q…²U¢•ò ­\]\áëã‹À€@ÃÕÍ>^> ‚µ5Ÿo …?Û ù=W+ûf¹W&¿åj·)))ؼy3vîÜE°[Â>Î0@ü+Uºf… åPLÌ~.Ý•ã³ÏÖÁÍm×ä_PØâ\t" HãkýÊo<­>È$Cµ¶nãŽCàëëÂßõ‘óÕÏC4¦\>Û²?c'ÍU´bW¿ÔV‚¼Õ ò‰F\& 'Îä QsÏ 4TZÁðÄì§ä6$ÑPæåS£cNÀ¦.kŠÿ¤à6ÃÖpnCÞŠ–¶´´ EEù8s¦@ t±‘Á0öï?…ãÇ3Cþb}ík©¹rt±ƒ›—3"»‡Â3Ø >ü8y81´½Dv$˜åÖš¾„º×ØB}±h˜ ˆÀ°•'£¡? ùžëvyP†áï¹­<…eüWª/Ei1oä ð[Âï¥\‰È8‰S 'qòè)œ9žŠ[â·$¾æfp ?µ¾MšF¡SÇŽð÷÷‡‡‡‡¢ùà+ ÷J’ +ã{r%¼Æ×Š­Ë’%KðÑG1,f"œ]8ûáòô¦»“A’K%´q’¥¿ÿN»/ñAd éœÚLý]ïM”U„ÌÌTÒÂÚáõ×oF9W;¿z/®Þ24á2q žyz6öì=Qo¹ÖwFbûvšš¾S'sáksÅF³¾Ë©ÏüÄîîûÛaÔ=˜mmu•÷˜‚?×a¿õž¢µÖž=ÉødÒj89[ÁËÛ66–pt`xn'kj~äcKÛ?KžoÂÁNÃle™“LÎVdnoø¨m1<WXs’ârÅìàà¡SäNNÁ¶mä8Þs éYÔNYÁƒÖ7ÌÞþžð óæwo[O¶I«(õØ“)&ôªŠO­Rsù¦þ2BÙú|Fy] ÈDW’lÕêwÙgx’+«û}©' z>I𛂓ÇN#åèI¤&¦R#\WGW’i…è¶ÑhÞ¼95j¤˜;H”DCÔSÉ÷RÓ•AäK-Åx^½H œê‚Ù³gãé§ŸAã&-ðùç?`„q0oð+šõ"c& F,*Éùwä &~<>8S¿›û7#è¸ø²õ¿Ûˆ*H}<ñäå7`ûP-¦M]‹‰“VáãoCT³Šê†œˆÎ\ÝHƒö€«VZ@¡ €¢IW“‰â@{ðŽË ”ÚŸ’2N”´°c8b~¬¬Ì¨é±"¶†‡»ÃÂ; Àß‘+Ž\Æ×q‚ÏÊVZXSó©£ÔŒ¡Že \S,Ïk}¦ÂÂJ®¦¥cõš8Ì_°ñ iÐ3¦œg ':ÜÚÍ»7ƒ›¯Ýìáà`Ïx ¸A³*.ø*ÿ ¿˜Z5ä·¥>efÌëÆ•ÀåLÈÌivÜ8aCù^sœÿr²s‘›ž‡ÔãiØóç>Än‰ÁŸŸ¯#Õ¢‚B0dÈôêÕ ÞÞÞ|×­E×¥JӨɽTI5€óvïÞ~ýú!**“&}OOOj3@ÅŒUøÏJ@|âãbÄý·ÓÓ6Ë–= w‚‘ †#(Ñx×3ò©×Æiñé'K1î­ß±fõshÑ2˜¹_/û…VW´¸ï¾o°qãQ=ú)‘¿è,5t²*Ä’Å1X²4†Ç§+Pëé Ÿpo”Ó¤¡˜ö~ùÔŽæÐ«D±4çjŽƒƒ%œ¬øÌÙÀËÓ^yöì¬`O€ìÄý¢ –çQg¥S´ÇfÔ+a†°,|¨&}ü<4,õ;?‰Y©S9X°`~œ¹I'rÑ&-º4CónÍÜ<ˆQêh£^}Y¥¢Ç’¿×K’š_jm/çÜë¥ý—RÏÚn™âÕ­™—# xñò0œu©÷Êp~}m¥%’ê*ÿòä#-’ä_8›cƒßÝŸ„½k÷`ïÆ½8¼ýü<ýpçmÃpÇw(ØçRÖŒš\E¬ ÿ~ùå— EǸ7'P£áI°†_oc ol È$+44c_}#F ÂÚµ1lR\@›×p¤Q{9½áÔL­‰éY[sÕ,Gê{=€Ü É‘C¸ªÊ%¬yN%ªî¹·n»½:¹¿îÁÊ•±8“™Ü´l´íÛÛEÂÅÛY¡et8Í:ÓÉgz" i)H¥=ðÁi(ÊOB9—C«ˆFÍ8Óªæ ,Ú`1ðp·UÀ°ŸŸüüX¶5µÁ¦Š&X´Áò1£`Mm°h„å#|´»ö$áå—çãpb:ì€Ç¿êÿp_šØRÏ^Æhf/~Ÿ$@U]#o%#Öƒ‚ÿåv³Ì &ÁRLí|”¢™D¸«NòÍ„ö5÷©‡¸Ox”¥<~¼b"”dêAeñú¹ß†ýÕÛŠ¢2^K]£ìýW’XwV”CcʼnŒð’_râ¹|&õô“6µÛó³RV‚Q Íø|ר_WÞtd,£í·©¹êàhÂU £%^ëTY(÷ž+&:yxj$¾ã ZcCùÔ:Tã¬ó¾Ê'ÿÎë‘)®ðfÁhÜ,ƒˆãGR°ü‡•˜4e-^„·ßz;wæ{y±—D-¦ö+r^áÆ G¹¹¹Øºu Ú´é€ÈƸ¬×pêf¬É[2Ùjß® |°nÝ‚Üvÿm[_§d(Vaü­90Ëw=ÍÌhƒç¯|F=ÑóçíÁòå°`Òl¬tqB»íкGk„G…Â/È­o’”ƒ½’Wj²Ïd#ýd&Ap²R³‘O œžƒ¬ôläfäáÄá\ämMA)mÿdò C¤­ª vs³©Ö;(ÀÛÞžöÁŽ´ vµ¡æÈŽ^äåxä‘Qfa×§¿„æ]›ñjÁ£dRà¿KIe¹HØ{nÍBál{Ž:IZqêp" ªÄMpòŒAQ0ל7ô+Eˆô²èxœ’\/ÊÂÕÎT4J‰MDa¥5Bšú¨2.ÏÏÁQ¶; ™/,†ZMÔ–#i_´Ôù{ÙPSžŠ¤CiDar'Ô~;¸:sr` sK ÷îµn%Ø>wN;¶Âa<"õ4h çUuÞÆpŽì¬yÞ…ö.67\Sû’ójî«}~Íãê±r:GU¤›“!æ\]êºN½VŽTfbù'‹8lš„:²Õ†ûS³ìšeÉw©™)rãwcþïIèùÀ ø9š ˆ€Õ‚6èE§b0göQô}°?|œÌ8wQAŸz¥á/e^YŒ­ VáÀá|4j‰“I‰éÑ m"l9½¹ÔU÷šõªù]òýëïª2:D[ÚÔ Vq~¾òt옹™Žèwksþ*Wj,m,ÉHÆï“7 í¨¡p·¤t.T/)ÿâI&Œò1·4GXÍ> FÏ;ºá‹ç¿Äc=†yóç!²QäÅ3áQ#Èý[5Œ ¸Œ—‹ví¯¾OQÃh²±בLéëîá…Ó\ÊmøZÜëH°7PUE v¾9@íÆÉ€¨jA½¼ñÄèÞ¸oD²©¤`æÏÛ±jÞjü1}9ü¡Ç°îhÚ±1<}½ Õq£Cšäíâé7OW*'eñSþj RRQ¬pΖÒÔ¡”àFOv–ÜŒœIJEj µÁ'2ípò‘ll?}I)*i3¡©ä‡Ú5K’ßK†ÌœRLÞð&E…ƒ¾ãµpÑß$üBñÉd|ýÀû;OÝߌƒ¸ØYS³Wpß>û!ô¡ƒqo÷L|>íÆÎy¾ÖbÉ[3 ÀÌDz÷>Åìy'1xÂ8ÖE8…54KQQA=ËP8š•mö/\‚ ǦèÓ3•¦¼,«JÊ‘"%ŸšÚðµ*N&Jr°æ“9èÔ¶¢B”:)²) ÑгVäÕLjk­&@IDATø­:“2 J9Ù8²']†s’rx?­ÌÁ£û¢øL"fM^ŽVƒ{×6èÒÒ³2P2Ðaöd¾{p ´}o"èsÇ¢¯¡w;´°gq,_i3Ï5´Yä Ü醹”Ô‘«çýYV+CMª4HÛ» +7–ãÎÑ=É_Kè,çòå;æ­áóËBò®Dß-9Yb}©Y6áq}ö,ž0.CoF»•B§'÷NY0¨%Óê¦ýíFä!Ò•Ô´mŒùæ9Œîô4¾øì |ùÕ—ê‹ü5‚Ü‹§!2`õ‘1%Ð% áÈUqÕYbËuº œ{48X*ž‹]%Úž^ÕèÐ!Tù¤ƒÚ¢{±bE ¦¿6.þ^ˆ¾9­z´D`D¬h‡+ƒmh€j&á’µ¶%÷,?C¤÷:ž_Ñ<åfæVkƒ3‘šƒa”ª€rjÍI÷ÖjH?Dè|è€ZŽô£Çs…Ô:ùû ´i í¢Å`ä\*/Íq’¥Ck踻YCŸŸø]‡p"9‹:¶ C@  ô'p(. BfHÚŸ ½©Â;FÁ˱1«àFoÚ\³åÈ8ž„cG‹Ñ´k ’’»7™ŽŒ€[X ›úÑ ¥ [¡Š|éI™Š-÷ši Q„ šÁ„„ÛP“ž€¸)¤zÓ« 5‡Þ4eÑ ?í4ö­EA¹9¼ýÌ`FÍ¿¶†ÍL¦Òé€{ªÄ‘-ý¡­,À¾?c`ሰp/‚ÄLìÙ$÷ËwŒò€«&+g­Ä‚奆¯)'iª•| vF¾†šËöMáïë@áÉ{À1ŸZ÷ývãt‰†ÝÜ!¢&g2M&ª*+Ÿ€ƒ»“PRf —@_D´‚©> 1;2Ф{$¬ø^¤ì߇“ùöhÑ9ˆs $n݇ Ö)$ÄEyô9ØðÃB¬Ùmð¶¡hí,Þ×#¼¯Eåpô'ÍWë8Xk`Nîæ*Ú^ì[¿‰“Äl8…„ ¯Þf  æ/u.CŸ¡C¼w%%¦ðˆPï…¥®ÖƒxîùÛoâ¤æäæÀÉ­}Aâÿö|9Ár/ILÆ“Œ0JÀ(£þ¹T¸sùst¹N¬‘‘^ˆŒôÅ£uÅ®]ÇðÃŒ-ØðãR,¶¡-ÃÐóîžmL“whm8´µ™T—åï¥$g{²98@CSU,0ŠŠRþ0x©ùÔ.KYÒ·pBÿ°ò±Åر# ý:¹SÛÅeè9ËQÞ=»x#ci‚"}ZU9¶ÎXŽbß¶xùÕH¼=löÆÜ†mè„,ÔÔï‚?-Åo‹Ûá¡Û33¶‚à£.h¡Ø×8bfkïo¸Sû焨Ρ23¿Øˆ~½CUå©èK‹°ü½Iˆ~orÁøû¨…¶õ†½%5¼Iih=ü1<ùb4e%÷ŽÐœ@lýÔi˜:%^ÎÈ;“ «ÈnxcòíØôé'øiÆIx‡¹ ‚ÚδLg¼ðûXxgìÅ[#fÂ@Õ•“3‡À¾}?¼:¡'ŒùNÏæ~ÞЙè.·¿ÿ2†ösÅÌç>º˜br¹º 8ëºÕ@Nt_ıՋñérLúõi8eÆÇ¼çþÃðîW¡,f;>|iú ÀòEGñøûwcÛº䤘býâ­¸e0}J"f¼9^.VH;rVͺã­£8 ’iï\iv®<€"Ól™»¶åÍ©mæ 'o™ [1þî¯Qjã[S¤ÆŸB›'žÂÀ®Åøâþ0zýGè¬Áâ±`áÁP|µûMZe`ÆCÂoì  ñ` *Q|›—Az±Vÿ‹@Çl|üÐDäX¸ÂÙª§žF›§ŸÆS£¢a®«Àe+ðcn4e9 vR‚Ǧ¾‰®Áª`L¨ñ=½k=Þ{ôT¹{ÀÚŒl$‰éè3öY Þœ¥ÉdéŸ%ŶŸ—^*˜Q-øÏäl¼ª–dbk˜Ü¶µNù×~^¬>;ö¯UØX°Q7œªÐ¬™:u ½‚– `Ò+¬ ½z5Á?=Œ‹žÄ+cz€¡Äðù¨ñ¿»ßÅ´÷¾GìöC$EÔ6© «Zw{IeËâ¨h‚ÓI1I ¡ÃY€{I\ä$qM Ð M<ʱzáVÁEgaÁ¬dô{ø&¸i©ý”eæ:mÓbé¢x4éÑ mºv@‡Ð,›½ƒ¹ªCyEY9"‡ôÅðû£0oìħJ Ëæ¹ì-NÎò„nÐZ9#º_KdÇÑ©ëø ,ò,§³”Xc¦Å&àD|&n~ùQ³±xdìíðu±dj;ÄL£45s¿Xްá÷cÂâ÷ñÂGwÚÀòÐÖ˜ÿÕ4äa|´ä#¼ÿËHX–Æâ·éûaª3ENFz\8£ži‹CÛbQjæŠè[C°éëÈ)§6úÌqÚïE‡ûšaÝäy8êŠ'¿}ã~x =Ú;cÙÄ_p¦ˆ!5,³ kү΃¿½‘dãèòøƒxä¹pÌzoršâ…ïÇâ_F+Ÿ2üþÅ"$ÆíÇÂÙÇqËÿžÃÇ¿ÇÈ1]øR»jï“Lzü¢›£`s,N¤ ió^æÇ7Ç!#¿‡×ìc”:g„†9+6âVþM0àöH¸µé‚§Þ‹r‚WtúA|Àv>Em~ÂÞCHÏ(¥ädRÅgØÚ Ã_WÜ÷é3rWÊÈѬ¡FvÓ—3qÊ"/ÎÿÃîÀ¼Ï—£BçIõtìÞ™ŽâôcØ{¸ šÂDÄÆå¢àèÄÙ"’¶®b,Ï»[óVèÒ=m:ã…/£ŠB££é\ù<ÆÍx =ºya匽|*¨_./cp0ž™ñ6>ü}Z¹g`íÒ(¨$äç ªE!æ:ÇËñÜÔ±÷ã«è¥ÃÒOç ­DÖ#®]ª1¹v…Kº¶¸ ct¡îôrj(ùWr¦.o¾ØÅUr]ÒDÜrDâ%sõá1 ™(®ÓWVYƒŒ/8>É$ Ž".x¾œ[ãš‹žWG¾WºKÚs­Ë¼Ò:¯oȨÄèÑÝ^Ü+ïud WMû@>wßMf8Ìž¹ ›¸¼vöZRzµ@§[:!¬EiÆœ¡á2ë9›àËïùþ©×PWu[A6o |¤=Þþ|3N¾Ös×!ÇÞzGª lTSíþBË#±«¶âØ)Ò–ÙsùzuÂ=ñçOk‘ðj_4²’+î½Â}Ÿ¼ æŒÁ/_­ÇÝýÈÔQgïS]PíÍÙ‚E¿GÓÚ/›r ý/P™Úæ*Ò»¹GF"’‘î~yjYª!}a……ÅÔFiHmÄûu-S•82ÈÃSWÝ®eEŒeÝ0hfjªÏ‡J]ötuµÅw´Åí··ÀömDZjÕAÎÙNíÛ&ø6D»~Ñè8¸=ܽÝaI»LYN•T?ÀUmÕ¥þ•2£ôÏÇo`Õ¢­Hÿ=þ=ú¡Q€ !î¹N¨Š‹A-`¨²4Î^ƒ GOúé7ì'À´ “VEa2VÌEㇽùÎ2wjs-=Âðø[7cìs?ÀǤ=ÃK‡r)‰p˜ OÎ6¡r 8ÿ4v®Þ§ÈÁ°¾Šõ«™S%í.u>axò«—q`Ë!ÙzSzƒFཟn#¤QÛ#愺(È-ä– ³œšâ"²[Q grE©(IªÉ¡°^ÒbAR¢86ÉSY,ËèÝþns±íç-H>°AÝ¢äeOGAÀ‚އÁ-B¡!8n€Ž6öpщBƒíbF¢©m¹üS“jÎaåçAs—Pv»å¢M±…»'ìÌòjö98H Ê9†VÒ©Nò1$‘‰¥£ù’ƒ±ÿ·Í0Oæ²þ ÷"on"vÌÝ„ì¤,D?ÜšÚ9’r½zEžj;Õ©”†klyrY²åZS@©¤£›8Ú™³bB¡' &¶hÕ% [¿ß•¤c²oÒ ½¢1eÎfüi~îM{ÓÊHÞÔün—%mžõ >}íî? ‘AvØúS ΜRå åŠ Õ»Ë{I™™Ò9S‚°(‰÷ԔϯÎÇ !¼à½⽸ÉÁöZÑWŸg(ø*n ÷* ÷ßÎZ0JYa6¶nZ‰Í[×"ž/ŸÐƒ¸ø„"º}tíÒž¤È‘'U€Zíd_u“s帩¦œÏcðÆÔpm<Ï=Ð o½Àe 3ñPÿæ\.¬«ú[É›eŠ­]–ü®«Ü‹í—œk^cAÙÕóÞÃ;ÓÏ`Þ:.•Ù°´êB@nòÞÙxð‰¯ðÁOk0¨‡êºÊ9g+vöå !C}e§™YÖþúöæÙàÑGF+uœ…+§«×1÷ôQ,^< ·í£ÊÂÓ?½ C·èÆŠCÍüä"éã³Öã« QîÔ £GÞgj¢jqC]Ε¤~3äU×qåX ¹×¾V~ µPBÂQ¸úGÀ‘žëâmlLF \¹®æƒ$/œ¬ iÝ.„Ÿ@Üÿ@,_Kz¦m\zž‹eÓ—¡}ÿöhÛ?¡\¦µå$S´»êË~5ëv¾ädß)( mzbÑ;3`AǰÞîKeјưLÂì—š… «2¼˜h­P¿kWgãîiÿÃîô¨/gßQ†ïz[ç.CúCC€*I"«5½õNô˜»3鄦woÃcÊ¡óþ(ýa¿tHÂM¬eV”™…-?þ†µ{ÊððŒhÚªª¶J¼F®•pË)‡vcãº<Ü|W?tÐöå™X¸f? «ncÄ*9» Ö6°&5ÚŽ¥±¸‰¬){×à“7`è¸>ÐÙ`ó‚hõ`k”dÒôÎ~Þ=|`Q‘Z Œ*Yqå¨ ï50Ó~‡ýÅ%èýqgزÓtr´9œZÝÜþ ö»c#N[Ò N…Û¦†ÎQ:x.õçegÈû“†MG[T+tÐΚìX·žv½Ö4!7ó‰3tà:†`« Y· Gòô”9’*ki_%¬Ñª](–¼0ýu†3(HQ‚¾;\²aÕ U bY¾ˆ[t¸%yÈ¥IŒ¿µÓù \ŽÊÍáæìGÍK §s =–l܃cÇ;"lGþ<m¨ìYÌ¥{ ”¿û0Ã=¿„!0Ĺã>ÂJ¶õþŸE{®ºž-ŸI $ ²ò‘N‡2»–ÑzGoÚ`Çc9™sôùEùzŽC%H‰;Œ“iùp7ÉÄÁ9pvMLªd2gÙöT✰@tÿ¾ð4­ óÝdšYÃÎF¸;þ¹MîÙº^â#ȽDA]o§ À1)ÏÇüïÆâµOgÀÖ#7uï [m)ö¯š‹E¿|…Ã?Æä7…µ•Dx¥úRe9ÑŒvaZörâ-//UíoªLhäÎp­z=‰Öùj˜ó¸1—•daãŸQæ2/=9Í0楷ÀÙ:/g‡TVF:ÎÂMµŠ/ˆLÈͨE–ÊÈ£1e^Õ3@)KØ+ÍèqZI®1ƒzè9˜r™JKû²J"çrÖ£œ¦f¤®2ÓêxNõ¢&@O¯ÏrR§˜’믨8éôŒV¨PjÝÄ2‚¹Lvä%¤”Ñ—0’'f¬£–/h%ë,ÆŒràujÇÄW³œ´A&欟õ«š™–bË‚ï±(Çw?4 îÜmè·D„šò,L}÷q|ºd;º÷ŽæÁ¦x?Ç¢%0sá":AØS{Q£rl‹¹iVü4“g,#Aûoˆêrn#?›¯ 8”U ÆM¸~eNy–S^†A”GRee)eÏe:æmf.ž¸–Ê$ Šr/çNSzH—•QVŠÙ&¹?¦•È?µïŽyÃ?^„Î̉ˆÛ˜Œ¸.$ /¥:[õövÄCwÁ½ÃÛbÆ,[ƒùó×bÍ/«è¨¡03D÷o gGÎìØ·¨¤úžjíe?bbƒAwwƪ…?À¶ãthå¦hqåÍ5ãIJ,#ÓÇL‚)¨”E¿¸ž< +®HuíÙ„àK5l`czcÅÈu\Ï„…#¹i¥þ¼ŽNn÷¾>»÷‚töY¹&•d¤ G5pJG mVzß=û9-y§O!ù¨Cß~ý{…0ÇsTûK:÷i­-Q–“…¥“¾ÇþÍÛàh¯AÒvzô雳Ҭ Iiú?Üß}1 Nì@öÑTù·AëîíQ|Güøõ |tx O&#Ç%£n“”l¬`‰TسŸ³ Û†¨QEŸÜbXh¿y%>-ѳ_йl>è‘A8¸y &<2~ZÄ­?†„í6¨mrÙ>aügÂ~Ò'Ê¿ÿ2Ói{ësCqläxç‰l¸ÚV"fÝ!txöQ´¤î~ÃÂñ_#~©'NïÞ[:‡iÉV Ž Ü0‰>´GsØ:-ƒY‡HrÛBÓ¥ ,Þ^ÏÞ-áíL‡2ª™-”¤ŠÙÅÛ凗᳷fa@o;ØØ“n‹YJÝ4ë–ÕíVuêÕ÷“ÚY­DîãOÑŒ[1¬u¥F‹NO܃Eƒ?Å—£'ÂÝuܦxÜüê3ð²7…ÖŽÑø,q:Ï-›¹ÂÚE‹0Þ£Œ’`´náÎ;*yKR¤F>H_±_¼;MC€Ÿ7㣗rPžGÎi3gè’c0w椕;"?!_>=6•™ØyÜO¼ÝVf´{&»B¥Ö·>1±÷OÅø‘ïÀÓEƒCk ñýw£m¼ŸJVË¿zMÇ1]½ì9×—$Ä´iÓÖ=zô9ÕQŸ}œØ6y.MïÁ—S¦áî!· [·>èÛ¯ òެÁŸÛ#ªÇs©DÜžMø}É"lݾã ×ØÂÝÕ‘`Ø¿öWĦ!÷8ÉÙW,ÃŽ]{Qª…?‘Ýç3BÑh‚Ú¢Edc¸¹Ú!“¶Mî|‰]ìÍqüðN†z]Œ-;v %³åÙ‰ØDJ¿à`”œ>€Å+ÖÀÌ9œiKç»n¶$’Ö%È9q›±zûNä¦&céò?`áFÊÖgù2×üÍÛ·àÈÑ$-w…«t*ágH¸½`Á¯Ø´}NœÎ¤ãÀzì=Œù üϽXì#š¸?ÎÛf­£¸ ˆ?™Gz?œŒÙˆ5ÛöQë{vŒòR‘Òf£­œ1ñ@°‡ÝY+|‚fOÇ‘2;Üy×pN0«ß`¹%©±øjÚwíò &}0}¹4ꇙs¢EŸ{Ñ,€vQôÊ{)u+N;ˆ÷?z¸ ®& H.ôǾ­•JŽ—äžÆÚ?bÝŸëŸÄòʱZ‹b|¨JÖSƒ¿{à ¬Z³ ÛvnÇÑc©Ð9xÁ™œB¹©°hÅVV²kWýŽ[6!žÇí=à¨Éd'öæü¶ :wjYœàåAmÆEz$‘M9Áöœ9?ÀƦD±¬ã‘4îº, ˜2øKþüóîÞ^4â »¬Œ'ËC[ɉ°)û/ôìŽA£ýL‡#û(Û…[°yñäPkeJOu+kq.£N)vš|¨¯b˜fCŽ_¿Ð@Ú£v„¯§­3¤H3KkxG„À?¾áþð ÷C 5sþ‘!è4¤=BÃ]y®ÀÉ…ùøx#0À ^‘~ðòåÖvÖ¢-#¸ôôE£fAhB–„°Æd¨a¶`˜ÜÎìë"Ú„ÂÅ™ ˜í÷ €Ã){‡ú"²k;ÜAŽÜn}"‰ƒ¥¤‰@ÌÎÇKÑ!hÝ­:“倊 ;Dß3CjOÚªsým%•$Á­›‘Ã…<ÅUëÖ÷¾x3\–9¸}3:e9Ðä«MZâ¾×ï@¤ 9YÍhJʵư`½Í,-á€FžlµÃö.iâ‡6ƒ»¢Q˜#[KÛVo´êݘ¦¥0Ñ9¢óýƒqÛCÝ`Íó­<½Y‡ Nd²oªÐ„ÙXX)”[-o¦#_— ”1(‰ƒúŒ¾nk sKDÞÔ¶|&L­ÐóñÛѵws†o€-i¿Îu‹4p°‡OPÚÜܾ^¶ìoÉTÁú·ïß®V0·±/ïauxx³_µ†­«")ÿ°ÆÁôdy,Ç‚í ò£'¢ç¬oEáâFz¶(/R›QSêíM¾fx‘Ú«E·pº0R«¤sÂM“×xH)mi–A0ïш”b7E£I3Þs+åwÔ€ö´eç`Zcæ#mq §VÛÑžõòBç{z Q° ©ç¬Ð´wWŒxº/ü|mxÌM»µB·ûn‚Ÿ3-¬ ~<5 ]º( )g²a7õGpZöh„ªâR˜Ù8£û£·aÈðްä³tNn5ž©Kø* _Âzÿñã*R™9©aøß^eBmÕ?-ïo37žPHNNFÇŽÑ¿ÿxiÔV^]Ÿ¸1ñGȯØK¶¬A¸-µÒÕO;ÀØ·rzÝó&ù }ÈɃ“&•€ï0z=ô †xíÄ#o|±ßnÁSC›‚ bìœÿ.rV:ê«mxsh5Àê=Ðé ñú XëŽßþAÇ‚¾êA`µÉ Çâ`å† _{¾¦åسð} ù1ÆÏÝ{:†$ž»ŸÒo]ø.îùÞüu rg?‡/Öc6'm¼éµ\®Ço?Œ1_̇_Ëî0=ŽcgŠqâÄq<8a+>Þ³§¼ŠWÞ™‡àNh¢ÅŸ«×Á£Ùݘ2õSäo{=îù¡Ã`kãg]&6P ÓyÄ4LÓ?4l:Œ¦ÑÝÑ÷žgñÄ7CËö_¨£6–Ðæë¶Ûz2êYÍ2ž:×ã·(s|2iÞz{1Vþñ ZÓÞOôEÆt%àƒÊ·OV&²²ò°iSæÿ¶‹œ³ ¨Ì‰N|[ôh;;[.ÙÓg“|õ¹¿ÐÓeõáúóÈzBÊR¶xÕ×N†˜;?ò†r¬|3œ-žìbM*& çôvêõê19_ŽÔU®Hr«Y®ZK¹V$*eôŽ*ø® e䨞Cj£¶YÎQ÷Ê_u¿ìSåA­4÷êm¨Ÿá·ÜKµ\áE>'?©—A~ÙÊVöª5S[c8OöÉõ†ßÒFõ:U^j¹†Ú©×Ьk·ÏPCÛäwÍû{~ýå˜ÔR®HîçÚu~»¥>jRóSÏ3Üsƒ¤ wàüºËuÒ.y* ù~׿’–s m5´Á«úÈ®ú\ŠÔo²•VH;Ô§H®QŸ(µìóïEígPJ½ô$ÏO#>ßëeÚdðÝ]ñ·KÛéF“Ÿ>Sz&&œ†[P#´" 3q‘¢y”)MmBD#ÜnÖ%ä@D½?>›6“&~ƒiß~û¼XLünJ¨ŠÔqf™“©ÇÏNÇ·Sfà“wß…CáQ¬9PˆQ/LÂMÁfh9h&09;­Ë,*1vÃü±ãîû=¾Ÿú &¿?f´C’eryA„ãÏ’XæDIj×Á±†‹%gðr\KƒúbF¶ ïû4/Z‰›[›áëñãPäÕ_ÿ8 |ü¿†¼øß0cÑNÛùæ8/ŽÉŸNÁ7_ƒÖ^Ö4‰8מ«¥T=‚;?ˆéÓÁ·ßÎÁÐö^X6çc8·»Í=*°|åBd1ÞxÍ6ÐÌÃÒ³îìšÌ]Ry›éìдYûÐ,7âLâ6|þþ7°èVœ™×´ß•†Wês°lætX„ ÂM-šaÀ ÐgÆ`Åší`Ðèsà篆kçGéa<S¦-D¿Æöth­â½¢÷jV¾ø1¬[=„ï~ü?™Ï_‰C¦cÖ‡I"Nƒ;À¥Å0ÌøåLûú–ã]«— Ô¹^yò18ÀÏÒÓøÉ¡½av€{IB0žd”@ƒ€¼ïÒPÞɶÂWœH¯à$bø°VÈ'ñýWc&ã¡obÁ·¿ãÈ®#¤!+®¦!“H¯TŸI ZQàJî*èRiˆ>"äß_“š€.çƒ/|H†>öÜõê1Ѻʭ۹kUh¬^k(ƒæg¬­”u~Ô« @HΡ¥ñÙsÔZW(×Ê~9¢&Ÿ†Ú“˹vÊ5œo¸Ê—Z)W’šó¹«EVj}Õë ¿ m0œ©Êå\Û.Ô>©¡z‡ õ9ÿþ䫿+Ç r8¿"ÙšíV[%ÕüÎ]_SRòœœ«£¡îr•´«f~†ßr¬v2´U=_-Ï õ²_>òÏðÝpÜP/9f(ÿB2­]îÕüm¹WSºÿVÞ|ÂäaT+`“æ˜g—Ö•*±¯Íd ¯>ç86ÎB«ýÑË#4ÐBpË.hAnÆš ‡ÐD8ñšßŽþ]Û(¶¸‘mÀe ­h¡#Heþ\$³€jó)e˜’ÑáØÉCÐkC¡¥+,ª¬Úî Œ¤)¢á-“/ªXI3.9Ý>ä.„y¹¢"û¶%æÁËDZ›bæìYØÇœÖ4 KØ»»÷î„¥K#ô¢ýª-íK=ü£Ñ­GGj½/„H¥3²Á]wÞNs ÒÕP£Û»m+˜¥#W‰‡†vÇÎ¥¿ãÀñBòSîÆLÄž÷Ü?'úž^BýÏ6‹çÒ ™`»„t2ेîÇf½^cÂÝßÛÐ'ò®–âԾŘ÷çqt¾©Ìè‘kÖ­¼õ˜µpÎÒã9-{ ̨/‚1ÈÖÅCo¿öæ´}æL7;%{Ó*áåY‰«æà—¹sq8;få8¼kJ¹Ôz8ßsÇíðt`»=ýkÚG £‚%m’eƬ5·åò+dLF Üp—ŽKÓì›4ñÁûîÀ‚…£ñù§w2ô­~}ÿ'¼3üLzîslýƒ6¤iy¨ /©V솓…±AF ܸ0Ž`7â½%ˆ:f¦g$¥ãÄ™Bø3üa50S—Ð ‘Cz*;Ø"zäzY:ˆß…š¨Z4¡=©8›qÄ#·bAFÈL5 k(î·Dªbí¢äËíùÀ¯Š˜NZG蘱èÄóÙ´¢ú‘«¡‘厳©ÆWÉМNe:ÚŠ‘qåt+¦ö7;i?—ps«)e*Òª;E¸¡4·˜‘X,¨¦ãÏ7¡c…D…¹0¢X²m4”çWiGùÅ$ÏÔ̆K—Àù;òM®ÛGº¯ßqªÒ /u¥}…BånÝ©ºþ"ãšÉ¤ª1›çáÕ^ÄÓ0|ȸÛý;Ñ^I4ì†yÖ¤‹çÎ ±ýT<}öΔޑ dnZD{fœÛ–“¤^ ÖZ¹VL4] Ïøá¥ù(æ¾3·ãìc _. +GËöÝÑ4È‘¦Y,‘äètP“û)ÀØŽŽ~2W‘dÐ|( ©g+§3þ5JàÆ’€<àò©„·† kÁƒ›c÷îdÌüeþ\·,ÝŠf!èvGW´éÓîäq5¼#ÿª,䵿?áþUVƒØ/Õêv.­z‚÷ }ÿ¥]q™gI­êêHjî¯ùÝ}]û ÇÎViÿÔîó/¥†Ês$ø?½w—RH­s ãt]õ•cuíW³¸>îG­ær$5¦O|P˪,Ю{|¶ú[ü¶t1š=:ˆßô¼’¤)Âî¥ß`⬠¸oôSðµfhÀì£("W¡ µ|e¥ŒÌ^XFž=ÂOæÅÿÄ›\€’/Êwy5«¨»þú—oŠ–ÚTÐó2ƒ¤Ð¦N:r!"¿Hà¡Ú£ª¹PçL@­¼XÔtfœ¯u•s$˜ƒga㇠šô~?7‚¡"iIEö€42$8yú0<ä¯(ÈËÙÜ<˜8¡‚ ytäºP.û‹I§>øÒ¿‚àU¢ÒkÈ4QŽÀ¦]ѹu0ÖÎü%އÖ›{]ÐZ%šW ²’¶ cÁ‘óðøs/Ò£¸>{o<:G¸Ð„D–udê &©fÞ‰X,^»ÞM‡bä]ÈILóŽZãÛÏ?ÂÌYóÑi´'tšRÊ5›`ž9 '¿ “÷\½OÖ6®p @»Üù"ÆÞÓ‹ø–KKdœH§6וÚêãÛ>b¼Ÿï8‘±Üßêz¨[Yª2ì©>`Ü%pCK@¦âÐé´ôã'ññ§ tãðóÏÛðÉèOi“?O6ºAH!'ù$JLé ì­Ã™¸S°eHZ:C]^ªDZ hédêÄ>úoûõËËœgs áLº´€l54C3§lϧ‘Ï·¸€ñäè 5×Áš<Ê&B ×p£ç±r*F,è­/îKO׌q¬ÈÎdÀ½Rx20GMW´¿¯/Õ v:9—¡‹½È#õ¾ºIJÈ;™‚œ :Áù;QÒ2Ñ$÷ISGÏÀÂÕ.ôž>_â¼RîWQãÌÉTq¡1UÍ­!ý½~jÚ¤vÔ…qÐfÐÃèÞÈ ³?y ¿˜ƒGxô –ÿ2ã&ŒEÌ©|8s™ºGTî^…匧››Fg®_±'æ$štïM¦­Ùhyü¹ßðœý"ç¨Ç*L,Ü‚‹âGñë¯3‘’š‚õ‹¦cqÜqš6°ËâiZ òT²ƒKØ» §ÈȰ‹KëKIƒs6n{uþr.•²0w@Ϩlúc¶Å&“a¢ˆvsKðÊØÑXHø¦zÁ¼è8Ÿ!95•¼| ±|õfRê\¨ã—W¾‹æýŒ£§RqæÈ>Ì[¿:²=z[£ÂÆ ·ßÜY±?á×õûÐmØÔxs¬®—´Ö¤Îú‚|ÄÇìÃÞý{±_ùì¡ÄS(ËOÁ¤7_ÆÑ\{Œ|˜,Uiس7ûcöãtNÁÙ™³†Ü·y>ö/ÃíÁcŽÂ£#ŸÁ£ŽÆSO½‚^-›`çªïPê.X½ôgì:x Gcvà‡_ç ¯’Tl”¿½w:ºa%Y&$¥2:Oö®Ÿ‰^ƒ ‡Óa®˜+(wP©¾HAš$´@²—Fg;bIjî0&£þC·@o9BC=ñð#Ý1oþ㈢YCr\Š2!ü7…!®ZZ¾µoNÂäwV ´(S†¼†»ժƤùïëH7£ÊBÌþ.Œg޲–£¾ìutq9&×Ôõán* NÆÄ`æ{Ó±”œÅB)çjè'‘øçfÌxã;|þÔW˜ö.ûªÃ™|ó R·°úÜ'6ÅÎþÃñÁ3Â0‡Î4™ýåXÌ[ &áòÜ7x´WS…®ª"k毀£ nIî^Ê©z¢]eIz²¡·`Ñßbo\FŒ~ÏOø¯½˜s •Τ~!g%' :‡Œz{,^xm<^|öH_“xŒƒ˜{wøû9BgÎ0§Ô¨ª,ušXÙÃÎ÷“ß­í}àïሹŸ¿H;Ä7ðì=ýÙ•«mR*nüs $Àá‡Úxe‚§<¶ç?»× Æ" ÈÊ’ ©ñ$B¡J.¸*tõÅ%ý¢œ\–ÃÚÚŠÜáÅÐsÆ-Ú¸Ò¢B¾ûÕEU))(âq ûSÏ—¾° …Y¤å*£ySW®ªû¤"®~å°Ÿ#õ£7ÚÙ“ƒœx§¤ +jt¦ö¤çhu´å'·«°O T]åSVã e³¥NïÚ€‰ÏÎÀყQꌛ‡F+Pºàx >ääµîa#°iÊ,ŒßQ€ÉËGÃ&ý&?ü9vìL†E#:ÞŽLGXuÚaÈ^êh®Lòw/\ŠLǦèuSˆ¸˜0I{%±åtü-,`pY«²ƒ§ÙƒåWÀ蒴Ѷ¶…-iÍ”k¸&ሠ©½ÔÐ\ÏΖŒ¤“”T–_ˆ¼œBj¦íyJ8V\lÝ«’Á )O©¿d"Á•³2UÈ+åý+e0Šböó|ßÙù–rp,àjg£gZËËHœr~©È¿ŠZp:@Pþf:ÞÞ˺G>ä¼/`(c0±)©¬çø 4:Î¥Ž”s.-CW>õefŒî¦ç}.F™¬0rì’€ZúªX“ydÇì9ørÒj?’Š®­0àæ:í]Ér¯ûôj)Aü›õ'ÕWKìÛ½û'¢ˆs@£hת5¼\È<@ å1ŸM g45HNÏ‚Cÿûлçø8“o•èÀ'?E«*O>ììhØçkl}ñØóŸÂÊ7Œv·:ÜþÂè]›°³l½;bâG“Ñåì‡=5Qñ§ØQjÙ¶=6¼3G’hžÀÑ[çÙŸñ#Ön\‡4š)4‰¾¡VYHÈw ÙÞóaLðï‡f~Öì„ ÙW·º ß~ß„a;—#•U¯Á£sçît·c'ì„×>œ‰¶¿ÿ†¤ÌRrvFSk$$2‹%_|C_GiŠl¼÷#›D$¹‡ý°sýJ^C€ûlôêÍÎ\Ê« ­ÍL´è<ø>D©¡.-ni™CF¾†&ÒÙYëy¥£°qŽ ÿ¬žs*eOïT ðl5xj—¸ —T²£»éþWщ×:©v†/1(÷{ŸX5†k¨\̆á•×#PÄNÉ=¬9œ³·ã¡'Ö’9ÁL™¡7ïñ$¦y·ÁšµëM;ã¾·?Ãw]áCŽÈ ÀdžÓÙÌE¹Ÿl»ßý‚ûšBÇvë"ºá½‰_a{Ü „D©TŠg+m¨Ñõ½-fHQ=ïGCMBå#Äô4#ÊË• ,¼9 8 ×1v¶¶:dn®êeWML¦dÂño%!ËÊJØ_>Xˆ£‰¹ð¤pA2ͲBø€0É„HýV»†¢õöÌZ‚µûŠ0â£àÆPÀúüãøþ…_ÑxX/]ú^­š”aÃ/ó°~ÅaäÑÄÌžº}G E»–ÞØ3í7ĤÑd¬<‡öž!ËLú¿|¢ƒµdeÙ…ì"†‚%þ‰” u-Y.?—!î½ mø`'Ê3" áG©,д_'ò¼vYa}2[7ˆ²(.&§QÁñMêõ׼yøýå©0ïÓComÁöW a×fÌœ’„~wzÒœ5㋞Ÿ’ˆ_?[ŒÃ‡ÏÐIÙ ä:j©;«°eÎì=Í@EY)ˆßV~¸ëõ{Àqÿj&Y‰+:¶_}¸ =Ÿºí¢œ9né±ýÛéØWÕÍm“p 0m:!aåZÌ™ú'²ô ÑÌYÙz¾¿ FRœ‚¶cîCçæîÈŒ+Cï—Àg1$vÍÒþw#Èmø÷èŠj( ÔÒÎz EçÞjVª8~žåf¥OìÝ#pÛˆvvì¤Øq¢©8{I·Þ¦7ñRqþ’c:v¤hýDÓØ¼K1ñT´Žæv~è7À„êà,~Þ}íØ7½cFEþ±uøuk‚J]è Å2ÜhÒpox eÉ^êÊlà_]–³_úE)õ4T§.þM1ü±¦J§"MÚ"×J²rðÅí>£´CΕú…„ò–ŪŸMÒFÖé×/\¼Á!ìhÔ|ä}a’’â0‡š×ýÅ~˜|[?‚«Œg3©þRI;áÐæѲöéðÕ:të=Hicí3”6WW¬ÊÄí{ߢأd}ö|žcbå^ƒnål¾Ÿ?s?fÅ:âå7_Ac f̘‰ÿ³w€Q[ûK²Énzï…ô^è½wDÀ‚‚ЍÏg/ï©ÏîÓ§âï“'Ø+vTPH‘Þ;!@ é½núÿ{³i,¤ÝÍÞ;wÊ™¹3ßœ9¥Tç‡4”.m•>öì‹[ãú6Ó‰"UJ¿Ú¹…cüÄp…nB‡zút‰€0™¥h@¼ûÈÉè=Fý­Ô¥¹"—ÿE=ÆÂ¯¶c?Er,Ú-’¦u\ôOÀ–Æè?z#ÜÝÕÁÔi*xrE¨û¨8[è7 „7›õÉÉL1gM%SquÉS`ÙÎFÜ÷7¢þèVÌ{#1a*gñÔYŠ’–ÜJš@\°½fL„_³?ÿ° ·^EHÄ<ÙzEe[øÊOš1c»9`é+à•Gµø|ù](Ú³_™ëžº“n Ç÷¼‹…¶t¼ðjo¬}ï{ȵ€ûd>iäIU…†õDØð ˆk%÷/e)ê˜ïâÜBØõŠF€»–z5ð§G·(nÌõýpÏóv8ðî+ŠÎAÛ‘$ŽŽ|(ñÓ×û`)…6å+40ã‘Ueƒ¯Ÿ{xöï=4Œ_Fr&l¬)Â/4>i<¦Ï‹ož¯Ýº×?5WÐQÅë-€ÏÈ‘¸yp#Þžú*Šz ÆM÷CÞÞµxï¡yðŒ‡gÊ |øò*\7ç^D9ã‹g—¡¸±—Ñù]ê¤1§{Û*Ší½¹#¯H€»®[ç‡]ÇbpÂÙåšÊÖÝïü€ß–Ç”'§Á¾$¯?ú ª]¢ñÌÝÈ8¸ß¼q Sþs&õ ħ¿‹¯CðìcƒX'ûV|bظ9"ýçuXÑ ½âGg§`Ñì¥ðýGúy †’UkqDëWvEÖë iú“òÀÇŽ";¯7=;z9ÞˆÒ`ûêƒ\—ù¼C0þñõ¿(v(=QOE3á?ןâ´Àx-:K¬ äv–žø³ÕC&/­|â L¸. œÔ(3Õôý-\ÜÎY7§ˆÑ<²÷‡¥c"Nëñëb·£Žœ×ž#èè!j(ŽžÈ¥È‰vžAäê*s|'ÆC›TF˓ųš»Ž1ýcñì=¡MgŠì(ÌPÅ1xã3óQ%¶dš¬!\#&oƒ¦p–à|jA?¹î¡„ä°Á6اèœ)/‚?ÆÞŽ7—íBÚu¾Ø¹5]¸.ä *¬È.ËLÁ/o}‚z–ã펒£GÐ`ßK6"kG«-Z–^Í––w,`RF7eý>ê°o W& ºË@8úɼ+”@jK4t±é"6¹õTÀ­*.ã^øÊ* mNÞô”ú¥Š_dïÙo>Úºò4$¢XâäÏ ßÜ1oOCDÐGr•鑿0mµp¤Œu5Ùm[{o‚z¶Kä’I©§€~ë.nððµKll 'Š0ê•:»FÒµ0ïè ÇÝüÝak]ØFÎPù–²ýzÐ¥ñXýÃ!x'!˦ ú ŽƒyÎ*…û--NÞ° >N‚…›3|©‘šW79>dM‚‡3«Gjæh®¥ˆÉ©^5IÏO×k‡£ñ½pÖ3·>¡ÝáˆT‚X b1ÉRãw7+¶£¶î®ØÎäžË}žÐ^ºp…•´üÓLéæ åV§ÿcšn:}©”O>u<‹>é½î¬mà›Ö@›ºa±ÝÙ´á–£ôÎ p…”2GëÈ:Šœ¾ãR_‰ëLADœ}BÐÇ?DÂݦΥ¬g=£Fs¦ãÓÎCE:ˆ¢â¥Ê†ê<õºìj IOeš‡:U¨¦\ñ₩ÛgtíêÏdì…:UÅ;E<¡™J‚“´<¾çT*Àá£hLhBg §Ü޽r´s>ÇOó-‘Uâ…Û¯ c/ˆ;GMjµ%®^ÍI¶˜»þ_ˆôÔà§ç_ÂÁ_èƒLdŘFÀ¡™oiòª®² Û?^ŠCt dX—d²tõ€7­Á8‘U[ËÑU3…õõ4©èD.eÙdäWÂ‘Š±¹É(É¡ÎE¬è °LÎour¼Ä¼DAW@g#e½ r:FðàÁx:íƒØaØ»à ,ø¢3çÜŽ¸©Ì§Uhd~"ÏÅ ÞFjAÚ%Ê? *[Õå#÷D"½<©\‰²2ÚwÓA“gŽÒ´Têà`^ŽtšÝ*ÓËúܪV—­œ|1`H,¾_¼ ‹óÐ¥[ODÒ‚Oa:…¸ó3«ÊÅ÷¯‡UèuxáýI°(8LåéC¤u8š*WO&Qs=Ù'Í×­Ê2\J‹ýbâÝ]ƒ_ßþ%I9èyÿÝJˆÙIÉÓŒrØ åÈÏá8ò´AÛTž[Ìx±~DÐ_¤§þ7²—¡75öŸ2Xöcͽ™s tö`¹½‡šêgggWWWãCÁ„…^ ƒ¿37Aê(Àö4›ÎNY}©7 "tê k@“Rô%­§LîUt,’“›‰AƒCX™øN7_Òê6®Ø_®†ìLáS€@”äN=QÞ³¨ ÷ßÿ5ÆŽ‰ÆÜy7°,Í;Jptiímß¿ZŒMC]Q·5v¦ä"º'Á/_µÓC•ÚžQ±TîmÄ·oþ‚è›nA˜· ’ôì74þ³¢+*–ææ`ÛÎøá»C<} ¥e*B)œÚ–wZ;œ€,íÝqý‡óR9¥Ò&%µ‹`Ý@IDAT’]#ެÛM»åÕŠ²\±Y¶-Ý÷ð`öíó¥xÿÿ~Ŭk±bÎBT… D·HgdîÝ#å8¶7y¥ØõËœðvAl¿Hè(p+ù‹­V;m“Ý÷V„$<ƒ[D îúx¢\É §ØÐ¶‹ÍW‚ÜZ¨vê…»,zç¸Ü;'6þ†m´Jó@z†Ü¯/ÇÏ?% Ò.˾ٌZ]‚2뵪J«K±°Bÿñåçs°l¿·,z¤>ò¥p© § åïK,ôÈI>‚m‹~BzV1|Ó2hf’V øŽ©°Ü-ëmhƒ!ªÕ·ð®-¨‹3dT,þ;û{XÄáÞ±A¬‡Ð Ðùö´däâZ‹Å¯ý ÿzQNøWddÓ¡¹ÛµUé˜ÿè"ô¸Šk1<1X»"&ÅÈ#ÇÝbÖ­4£ùÑP„E»*ùµ*ºÓ]š@n§ëãrqqÁСCññÇŸbË–­èÛ·7wy¦î3N-SìÅ¢€\3³ZZ»øùù™1b‹nîå¶­¹Xë¤å\ð} y†´_Z­7‹³%TäºöÙÈyz>üÇëpòqCß }àÓÕƒøH‡]©œ¨‚>¬­ƒ‚¡lÌunyÕ0ä!ã¯íEÏ_y½ãêå3ƒ¢>Æç¾{o\ùÐ4øi–Î_‰¸ðxD6ú0gdºSž7ÌŠeâØc oQµ­ÅšÔ|V‡PB¥¯%»f"nÕ{z>ÿz#ÞØ¼ö^A¸ÿÕáE3mWìÀ’õÉfµ@H 3Ö|ü#\ããÜ;ŠNpš@4s4ìeë:lR/òƒ[“AyÂBçˆø~Q¬³ïÖÃ!$ –41©@H¢zFÁÅK­SÜöæíøìíUxã¾Õ¶¸â™[лwh‚ÇaÒ–tüöß±æ2Ý{ö„»mÌTÝ–új}ÈvŽï1c{`_©7†ðá É“/D ‰„»®š:_,ÜŒyì†_¯(ÌüǬYµk·v…OXÂ<<ØrQ)Jà‚z¥MÂTh_¦”ZÃØ>“‡¢×ú¸Ž¹‚J~tÈDÑ ïØHD—yž uúƒSðù‚˜óÀNxÒ$g¿+úÃK6?5¥ÈMÍ…žÇ˜fµeX¿h9ÒNTÂ.º ½I$ãûwRÑw7Ñî,§-ünGôKþ“–œN·¸äõ3U :„khãÎÊÊ/¼0—>×cyMƒ,†­h«´¦K.<È/àQŸlºî»ïVDFjñí"‰9Û°èÎ:ñ™#'»}úÎÆ˜±x÷éÏ“{.àð\ž=SçV=cè£õ³L'œ­.´dÔäºu‚\s§#ž ‡Ý9ÁQîøæ›;øPëþ7G6iÞ·ÏlZ3‰ÅÛïæ'”sÉ’˜¡šöDG˜ƒj{7ºùý7v_¼J‰«ªÒBä‰ ¾ M:Ò®*-»XùU’[J+Vä¶Q3µ}ÕxÌl¦£][*w™)öSki ÛF1]%\==Ý¡›ét°%w´’^‹iFÊÚÙ nZ”ЛWuƒ%´bŒÊO:Ú‘”ìÅîn=OE¾U…qÆéPMeãjÚÑ5o¢—ˆ XЦº5å‹­ÖJ 耨Fióà):U•rZ©6DdGÍxfnë@hÆ‹é`,!95w+izRgoCù_Žû Úv з±×±6”vn¢…–2¯âÔ¾ª´…´qkamwÒœ#ï åT—S”¡„vdíáìj }Y5þk•Mœ÷·V´WkKqžÚŠr…žöP8ÊÔö®¤wQk'[Ò£YùÔ°€‹+l,j‘—U+G¾—Õ„ÄZÊ2ÓV1Ó‰7¸ÚFÖXOùlÖÿ¤@ΰ«=¹ó ´‡\ [kÊQ SŒâls-M­ÙP.Yb ²séíÔ®>ÍÕ´ÑKÛÁZÊ1WWACÂÔËSèR+Üz¥O8j8¯hmuÐ1Ï3M1'Õí"D~»8¯ÿõ/tñIJߖ17+ðŒ$ê< """ðÚk¯P܇{ï½™€w=ò„Q’º°¡½JËä×yú¦ÓÔÄ0ß]à™GæºZNÐ))4³öÍGä"Õcöìɸtäñáâ6ð¸¶¸Š†ååèTlÞÍk)kW,2}\ø´Í;çiq.(£a{±Sí¬,l§Ê— ™¾Ù'²°'µAáaèêoO|z 9UÖ¦øóN€:rþüd¨ <¥ã廑ÖY;×àÃ×6*æ[.vkíúaæ]Cè ˆÎh+[øj/S¬ÀÑ^ÉO޲uÎðiÊ_Ìlœ©Œ¤Ü•¥L•ª#÷VÀ–!—Öåµ¾ÖÙÚ·¼Âs”§ä9á´ÚRÏÞUåOJù, ÈYM¦PƒLir-O[ HÓXÑÙƒV©ƒ´ÄŠN5Z~µÐBîI}´NðåG¥·ð,¥6¢øe ß`á`KkÌàäP‡ŸèV~ûŽÊ©¶­¥¼NqÓ¦áªÑ0£³ Ù¨ma}(û+õ‘ß„õtOïÙTíx‚ºPP¥²U«zšAgÇ÷4/KÿÇx8rÒbæ‚)/Ý€`[ع°C›r‘šYQéÏÐf¥L//° Ô:‘«­–G(ÎÍOÉ1ôƒä!×êX0ô’ÄvÎЮ;:g%Mµj¡À¨Q£°`ÁÌ;ß~;Ÿò¹|Åšv¼-©þ¼WÒÖªª*z¡¡Âwö–Ttêü¯ÙEì2 Ë D?ð:44Ðç¼e†ÁÃÓ–r”?‹µ¿@ I¥¢œt<;çWD]}5îȣĺŽD>{dÇVüsþ<öèd $ 0æeä‘ïG]m)æ¿»Ç<ºâ3â¹2gP={ñ¢o1{qŽRþïæïÇ›ŽL¯‚?R¼é™ Kªn\´Èlë׿©" -)xÅD–ôìhEE!”µ¹«‚›¦¨öùËocABÆî´“tÆsPÓµ/ObÏôLÛÎöWÛš·ýÕôZei¬~r[žk¼á;GcØàp§"_ûÀs uä*ý }¦&k]‘¤mK­–ßòlKP,Üå®× CPKt«+-œ¹>S‰Ò’–2äqC)­ëu2]ZÔé/M ·ÓwÑÉìÚµ+æÏŸ¤¤$ziÙ¯€¾¿ Еv.[¶ ‹~…¿ÿm‚CéN—`ËÈI•äWy|Ãc$Ü=ô‚’D¦BÎ 11>”Ëòâ/‘Ãý“\Ù4Hƒå»UÐò°¨8iù4Ú,gýT›Ó’š®åžÌ`K;Z‡ö$!›æŽ¸Kå§éÙ–$†«–u§%¦m]šÊ¬¦ÂŒ–ùZ fN燨®Øìâq¯šË—_,“¡EuZ 胲ßÖÆü—ú¸éog£€Œ+ *²õè‚㕘“ÿ¨œÉ6C 9QÓølþ}1/ZÚpr©§»wrêKC<„t‹GXûI¢©R%[ùºlWÕ³§¿ô¸9¹é±ãTg?í2T~J‰Æ9íª o[ØlÈátu1ô‡¤5>’ ¹t¦oÈíL½q–u‰ŠŠ"÷,ê,Ÿºü“—––bÉ’E˜0)æˆÂØ ¶»ßË¿…´™ßî m1ÊÄÞ8øfrÏÉD'Jèÿçí-ÈÉ­â1¯ q!iL[ž¶4¦nçAtá®8H¹·r=ÅXm­)iÈóÉJšà‘¥DøjÔ_'Ÿ‡{Ù"Å BºÐ­²å«QRNï#”ã³ÕYð€’r‚5zægN1Ò—gŸ'‰Bp­©¤5ƒºjò¨anK7œúò\¬ØWŽ!=»PÖÎñ™NÒ*©8"ò¤ZÚ¼¤NOËÚ¤_n»ñZhiŽjüží”íã}S¸l(РØ$æ(£LnÝ¢›[S+^öšgäYÐy‚9e//Là€UÆ–ŒçÖ%0^LbŠJ6ŠMàI¹¢ƒÙŒ™YRÄ¡Cö€[ç}±®Å¦.•ñªÙ6E™íìÊm䯏¡’â|Vöºgdãrö žFŠ(ÕQž×‚ã¦m‘¬K¬8  f¬k£œVÉÄ·¯/¼Y/à“÷wR瑎*6®ÅóŸÃ{ÿYˆ¯^øÿ¾ö,_Χ:¦6CQZ:ò «Ò‚¯B¥-Òr‹•6²}BW¡QKà:¡Ph!œÚj’žV#j,Ÿ¸ßÍY† ÒÇ8õù(óiÂöjö¤³Ùî'RðÝ ß"³T ƒñié'*¤6ðÓ&½Äè/õlCÿ–7_ñq¥L©¶!°Îfl_Α#8´?³ ¾ò&û»QœYP–ÿ§÷¾ÇÖí*¥¥Î¬§ym%–ÍyÏÞüÎýïß÷?üûÎ/•:w¤G Å_ªo'÷RQÞT®‰çœÑèS2<7…ó@sø¹"Æáo¥ü[mhFÐËÑæ'7!fŽêt>f&ŽCdEöl©‚ƒ‹7î™Ñ÷ý–ƒ’Š`ÄÅb|„ß}õ;JØE:ïL‰³e7Y¡kt0útu…ÿ-#1à±mh𛇅¢üD|øÌDÚ÷ÉÃ÷?þŽŒRŠ%²’Ž «È 1ÂaI{®¨¯Bâ†Ãp t!gŽæ«(òB{¢(LMÅ¡½i(§I,__Ú ¥ \ 2DY£545ÅH;Rk/Äô‰€=w€¥T HRТDÆ™9ì¹Ù;ºf~ú5 žîf(¥#Š"’“ºm¾ù2#ß~³&†bÕ+sñúK_bÜͱØòÑ2ä˜ûàáwÿËx’óhrŠÓŽPy_ñ÷„WBO<±`"’^ÿ–Óõ¯WHK<€Ò ¾˜z}wØQ9‹?};vbè#w —™"¢=(ÑcƒðH70W”¼ºë2÷'bÓê£|'d3¯æË Þ³ «a-R¾þsÿÀÓáê¤Úrruf^žõ&ìýüh^Ï GfÁ)0®’úŠdæêqÿs14\‹ï_xßývîN̳•6þxê‹Á!gfÏx Uvž|7µÈ;xefq'’H…X#óƬzz²^ƒgg_²S÷~÷-^[Š'ætWž3çéKÊÒ%xöñáà }9rŠê0ëý0±¶~õ5>û U@re12’ qÝ;Ïãºña|·Û°Ÿù¦›£.ïæÝöÆÏy7\E7ÐõEXüÐl‹½ݪ×à·¢>xûCÒüŸ¯á» ùÔpGm9]Eï/@OÊï×”¤ã£ûßÁ<‰1݃ñà‡ÏÂ#6†ã f}CáäÁΖÍÒeŒm.ƒj›ªh¢€‰'Q@P̶ãÀnr(:S`µÄðƒX²ê ŸVkb¨$¹%jʲ°&)™iiø`a"bGô€km ŽçU#ýp­,Pf-§y¹¥H-$gÇËWÆ8à—õ™ÛÛÀÔ£Gôĵ£záÊî) <¬®öǑéøtq"ýÌÇí±ÉÙÕ¨*ÌÃc¨³²ÁÀ~Ý”ç®C³yNèG×§¯¾Á£Ü…kñáÛ¿bå÷×¢<5k¶Dâ¡ ¤Pv.7µÇ²Ë¸p#·¸†×¥§n/ÇŽ #N¤cwÞô³`‹ÍVc÷Ïw\Çêz¹¤¢[‚ÚBüðÖ ØÅ ÁËKçà™×gÀ‘–4ê¹¹9}{´æˆ»ºrs±{Í1Žf ö}¿zrí¢{ðX›ŠO‘ee°s÷Çô—ïÃó_<†1ý±ì£m(#lBCÊëmqýìÇ0çÇgë]ÿîEµ¹ 9¡±è>0±üÄ ˆC€XÄ -XÁ5$ !þŽT°lbòFU’ûéäFÑ"!POçö°ö°Aöñ2ÇD"<Úutœ°}ÉX¹¿ £{P‰S•UŸ¡T 34}Ĭ¡¹›¦¾pÌþŽÞÝD3›Â;VƒÈêæçÕ áº)øï’ÙÙÙNàÚ¹Oᕯï}c9vó¸¾ReëiÈ>-àQîwiu8‹äB˜Žøáƒm¦EQ~lüq‹‚âJÔx¹"!*³Üœà§c&Š9‘¾r@Ìt˜2u Šéaè×µàD—ŸóFѦmE‚úöŽL–žY‚pz;jm^ŒÏýó¶)ðûe3v'ç .6£bÑ–îê/Š6Y8aø]ñØã›pâÅIH_ø;» ßà lû…›Ò¹²°Öœ1tb4kDÂð¾ð þ™dxjdå‚¡ÓG’âⲃòÂgÅ)‡ñÉcs±!ÉwÍ¿Q^²íhßóÆês¾ã:4s7j¹Í¤0]˜(p9S€“!'.ˆçIÊE—ª]fÐÓ\ÍSO.ÁŠ”IA·KdzøUP@W—ŠÉ¢3Tˆd­(/G½¥-†ôO@ï{ʼn8L°óõÇõj\°C l‡ÈBNžõ´;»x[®›LÙ¦…¼¹$.´æ¶¸ztF±6R!‚êsœZ–¤ï"k#ó–Ì=Ñ­(ùµÎŒ·ÈÕ1·sóÿ¼.JT–;£Ï*0ëZŠs”PŽ3ec>¦M~5k)Gh^ÇròtW®8@YÁÍàæ¬Êé@âz¶ÑŠ,Ä7^ŸŠ¡Ã¢øÄùZ„Ù 1¡é65q[ÚØy¿xìÍ"ÍšŽí‰Q§aùR3©§ Ý:KîÚ­cÝpl_&¿8ˆ€IåʼnÈÂå‹ñ÷®Ä¤§§¢w”ü”‰}…2~¤`þ§Ü­pùdÀ ¹‘óM-ø¿ýçëH%µVuѸ¸cúó·"ÈIÌT íÄn€úƒòºö(É/¡P)šS’Eî)7®žö”=.ÃÒÿ½…ùo'áªîôY ièI%l jßr¯øØA,~w3F=ón»·/A¶Ø‹Uš üQlp<Ê­'x3£B—\ gXN$˜Ñ[ È.Kœÿ7ŠÈA¥¸–iC¬HJ°³/ˆ«O´;…£Gÿlû|ÜýRàÄßÝx¢S¿gËbßÖ—âÓGç`sЦ?2¾öHZ½ÄèM5§èÛÌR)¥lNY oHºˆd¾ ë—mCꚣˆ2AÎlj’Í7—9‡ílT6ü|Æ «@\F“â’Dö`î­óáGþ,±¾`xNW“ó{OúWæS±cAÑ®Ž„K¿êt¤–¦4& ˜(pf È"ãîÌ9Qf)u²>óC6E í´nß™ -§®Š‹P&¨ [â™s¯ª¯Å§'йTÈÒwº@‚6P¤`o&\ݱ`ÉF¸ß0”ÜUƒÏQÒX´ž A@¤LÂä´Þš 3'ºÉ àx UhYD ¡Íõ꥔cxÐðÝ®.m€u»{†G ß´ÅëçO‡Â}ígÐðXëo .˜:~|tv˜äÞ¶Ÿ‰~*jªñE'­0Æ+ì‚€\¡Çh–jÑý8z4Ÿ WÁV4n]á³¼¶ —º°pOrÝSy¤žÒ«QQôjÕÿg™gÇ““3Gw´v웃¿ïFá¤4'aëÁ¸zËKÏp†j8 ìÓ!¿áë×CѽãÝHõyŽ©‚Œ” ÎP^u>IÜŒ¬:ÄL‹ë®‚㇗£º¼µ•Õ´‡­ÎJ5 %´ê CTëoéoÇ€ Ä ÀÒW¡¡´·þ_éGøËgEDBcmMQŠìÚpÝì#qh}"EœŠ¡!G½¡¾G¶ƒKL\-Š1ÿÎyH,qÆSÝ‚0w äæ@kckÊèžqðµ®Ø9]s“ÅFÚÁtf"~ó)6sC™M…¦blÃ÷f lâ˜6q$&Œ\‰/~(Û™ºyzÜu•â*ñÒ‚-°°±Fú¦•˜»ù7Íz8ÇÄâ¦Ç®ƒ ݈“„jµ 4R*wÓ H ’¦%ÍíµÕBkz*lTºŒ`ÜR-1(Жq:?¡·sPLŸE¯ÍGö¦@r‡“QЃ‡'@×¥ ®ÎÃkwÌ—ûiÞÌѧgÕ;qÚIQÏçÆ¡ü}zW–#2Æ´A/-æ™»¢–'VZ­Õ%ÇðúMÿÅ„Ï_D×úƒØžD‹¾Žøéÿ>TœÐ?¹ÇøÉ¸úÆp¶¢=•vËŠ”þ’·ª‘=å¸÷ˆøô|DEDáž{îéPž&Û!2™™(ÐÙ)À‰NŽ¡^^FÕéþxf§ª°¢\!@Ê(ò"V•\îÚ&–ˆ :P¶lÏr"¿Ôíì@³Î[’Vk¬¡ŸkäXŸÀ×JŽDI?#`¸¯€jÆWñÃåšÒ­2ù£cyõmØ]4#cÏÕóx9o½s#ž~â{j迈~“cÀ„>ˆ „9—æâAªyE5´ç|_Uy¬ÿ½³`áŠÄÝ9ðÓ)¸ÝèHSZ{LøÏßáᄚ*'k· ¬ é[Knè¨{¦ÃuP6b†Å^ñÀ™fìF(ÓgßO'Lûï]ðZLŽv‰ßt5Â\ôXûë~‚rºO¸5<=`Dʹ÷5“ÔSìä–J«ÙßÔê 1 Ì#÷9&VÂ7C7Â­Ö [öÁ[ÿ€Ï¢õHÏúÿ{$,*Ëá–ÀÓ@[L~j:‚íaWÎzß =Åêø‘ÐÀÍ`ä©(IþÀŸ¶ïŒÈ‰7ÐLcQ)†ìOÅÚoÖcǪ]èÝ­7ž{þ9šsóëP4'3¸)˜(pùP`Þ¼yxúéá·e wŸPVüâËu>jq‚ Â&¾IK·w‚*𡤤ÃFÌA„Þ ¦\× @®9r*ÊÐçý0fD<Þzâ&²…Lãçœ Á¥hË»í¿Ö:â›)×c[F i÷³oº&¸* "Qß>À¸ðp¼q啔᭥mÎJ¬>~ãÃÂà¨s\ŠÈm]•|W}óæ½53ga³Î·ç? òèõî«Ûðé§‘•_AÄcá¾1Hš€€ph­µäÒ½3eÍ„£Ü´v«­ûãmÐÈÛt•À>˜â(¡,ã¶mLWËl•ª‘t>AèÖ7:Š]?V€‘iK\  <¾æä¾a#()ùS>%A­‰”ÝP+·›ÿH:)¯uPë¬Êº¶n¡NÒ–¶­äÐà¿3•ÕºŒS_‹I59´W¹˜Së”v~©´iOï–:¨é uÚȆáÀúÝT-§¬hÛ˰öêÈÚ¦å?yÎP¾PUj ÐUr1H™Jžò1ô•\µ§½¾{—í@q-ï*Ãg”'Íl?6žÞ­˜§<ÙB?µOðžÜò´ÚR¡Œ: NîC:©å †ÑÅú‰˜OÈ„k¯¯ªæG”½)Øû{"öoI¢‹DFDòž‰k®¥6ŠJt44ï;;ú€)‰& tR ð("§ÄcHS8=dMÈÎ/Ŷ})Ü#~¡>}N¦»Â%¬ª¨F¹¾44ÁuÛ ö¶3ø£CB0Ž V8ºÂÍ-&ðÝqâçåá½;èè„ëãâÎà^´n¨ƒ»»î½o$¦LéŸ~Þ‡%ßïÂúKñËG?S©Èa=#ÛŸ6E½œáêãFûÇδpAÐËã}3ºb&ÊR€NK•Û F8NµS–“†5ß­kŸ¹’]|¢éô@k!y´Í_Xjm|u$´~æté%ݩޮSµ§£u8]¹§¾'஥ýNýëTônû” T«qxÛNlÛ’Óp²&$w¬Î±!N ÷¶mÝZçÕÞâ­ICh©¥ÄHו"ñëuHiÛM8÷O$\¹¨†|$Ï–>9UHêÊÍÎ6œ~L —¶±† ~W×êi}¥EYEÈÉÈÅþ û‘¼3Å9ôêèà†˜˜Þ>˜þÅ“†ˆ“¾Y¤’Ï_™'Ñ&B¥‚ÊåBØ×œ䫎¢Kb–LœÛÔÖÕÒé†'Žd"=9YÉÙÈNËA.•KhjR,]øÓjͨþ£1`ÀÄ'Ä#""¢M9gûÃrÏ–b¦ô& tZ pFMhY̨La F(ÀY×…Ê%„ýä&^¬ÀHMþtQ\UÇ’s++›€¸‰<^üj_"eùjœŸ¯´7b †áæ†þb‚͈ü²žqø„1|2kÌÃ¥þ–]>fèÚ5Ÿ \?¥JKõH¦ ã ëS°oo:Ý´¦aç®$¬¥|a duT|röp†»¿;<©läê þ¶ÔYQ[Ýš÷µŠ¦N®-µä‚SjY‹ü¬|«m*þo­¯›#›.8+˜Â¦À©è¯Ò¾õÝóÓ­slß´³ç¾¶Ï¡å·Ô¶i¤W²Š¤+‡åàki…¦Šv¸«*õ4¡çÉN•f«i¢0«'Žf#'5…™E(Ê-B O}tìiJÑÓÓƒ!þæx ¤Ù³.qrttì°‰°–Z¿2\ãt1Åš(p™Q€Ó»¿»‚â 4ër×Ðˬþ¯ºÂ$»†G`×`¸\‚°D õUVœ3T¼)}=¿kù늋!3<þ‡o `•9³ýýýÉÁuTÌzËorTœEI¨²˜,ž?Òú…|÷ó÷£ÛW‚>Š?X’ãÛ9·nÒ!*x·¢-h7Ú€uss@¿~áŒ7#è-CZj!2³J靿ÒÄY.RSÓ°mû>TPY¬NÞ]Ú,µ¢<¯%哵tb`MË Zb±`ÍxΖڦé!ŽäT‚,q#²)®Q@Ɖ„ËhÞamE^VwÔ‹=`ŽÓZº¯¥Â›Â™åØ­â n)²TAg0U”Ïm*KÃrZaokooôŒì‰Ð+C nðD1nŠ/T0Ü EYS¾& \l à§} ¶ 䞉öç|݆âþšn:]Y“…¹ixô÷ßi½€Çåu¸eÌèãë .´&í³žC‘…½€ÈÈÁ*‹ã¼nh¸Ùf— ïO6UZjåÌN…[Iõ™ËâxÍ/ÎáâZ C^ò1h.Åæœe ! Ý“ݦkÆ„†ÁVA# —¨ Ñ”Óû誫0‡ŠkïíØRºQÕ…y"•ÀpçÑCx{ÏNzïrÆpÊïõòñE8ݼ(ã[Ëù¹%\¨w­¥„?~eؾO•“A„Nù´ÌíÛÕþ÷©ò2Å_R pC¢Ø2—“˜Ë®ËÚSÃoù6\Ó-ß¿FŠ Î2Ü <Ú>N 5<⫤̈|%&&Òøt ²³(›C [„Rr«ª«S Y'sDkDÖÑ“Œ–¾Ð­¨ ^oÌðtà¸ð7ï/½{$5–Å{ åƒÊeM»Ünjèh¡ŽÇ‹5ùzTëËhÔ¼š›¢*4P©FêAƒÓŠ–´5A°]:9ZÓ\U//'¹".Ηõ. ¶¦â“– X@¯æ|ºå.ÑþMc£õbÿ—.¦ñè8¯]Êð?2ÕkøK¬Rª" ¼´âœ"€µÉ1–ÑúÔpü·¤™ë@P·'³anGS!aíé=»Xrþ§È§ >)pÞëâ‰/nˆ¦Áþb¬{'óÒXI#÷ÖÕÎWGE·¹·“££TÙÝ“*ßAN¥=¹´Ï Šîtð ¹Ò¹%7k¢{D"ûD.6/ÝJ.ï!¼{`/ü¬¬ÑÕÓ 6b¥€*§ˆFEE-¹M²9=U!—C¼ô¶|Láò¦_ÜÀÁ, O0è=„Íù³õë…\ˆÿxï7Ï©<‹Îó¤pf+**Èu¥!aÙã©Ç±—²];wîÄ‘#É(¡;O ²Øm@ìÈ}sö¤'—è@¸ð[ìºz»òÚ.^®taHw\tÌ„…ÇoÅ#ünîÇæ‹ÎCSM.Ô‰ˆ„s’z<(Š2"Œ/J="d_”SD_ÚÅ( ?íNd…üŸ[ˆýÔ$Ý’˜‚Ê’=¨çÎÖÁÞ¡!®ôœä‡zêà‚0SZë´¿4~®r¢ÀC£Ù Û9ÔýДe d@З:¹¬™•¥H¢ßùªzøñ8ÝŸý,‡wÅåXsBž:¤Qd ÁÛ ¶íP)·O4½U„#%å”o³C¸»3œè;>+;þzÏOŽp\,8ÞD´ŠÊ™¹8\To'7„8ÙQ¡¬ÝbÉqi­³F|¥ù|FL]š LaAxe½%*š,+ ê€r^å}:mN¸¢£æêŠ'–-Ç‚ç?Çè¿]ëï¿£¦ GIa ¶¯Ú]ë÷b3mç¥d£\ïÏ?ßʬÍêIYXorBíh@^ŽX/%NÛRÓÍ?=ø.®>Ä“¶%ÀwÑe\[|²¨ÎŸž —  «¯Ö£œÜØ””£øå×_°yÓf¤;Š‚’|Ø8X# Ìi1¬k\ hÜàD9(gW'rN4\fÃoÕ*{ úÃTäeFGrký<A?k/~r¨YÊ ¬¤¨„æRJPÊO~N¡âÁåÐŽ#øâ»ýxoþV¸Øk z]з_®ØÙÙò˜Gò98‰N\¼…]øÜOªLî7œ”ââF˜0l ›€EÓQyÛ$ŠÊuSÅš¯ ~[¥0Ôü›×"‡hE®µpH/m`ùu5ØzdV˜»có‘$¯µÃ¿ÇNÄ ›r¼øÛ¯øàH åG­q”Ê󮛊þÔýjîZ>~0e/žÛp}"ý±+m3lý»âÅ({<¹bÖP iÎ3ûGáÅ>ôVE¹ï•I;QR‡4}! ÝñÎä‰èfÇñ"Äk@$¶nä¢\“«," Q?ØN/hÂ8¸šVlxšA¹±3WAÚÁt‘îî˜?ù¼½u+þûÞ¯Hܸñ6$ôMÀ•7Žåg4²Nä ƒö7÷óÞ¶å;ñì‹Ë`EöyH€37–><ñÜ\:S1SNSDŒˆNÉLÁD‹CѨñÓ¦pÑ(pYƒ\IùÙU«Vaá¢o°nýzXè,àéƒ×õAìÀ8Dö‡­jzE5¢Ã‰_…³ÂE¡”îE#¶© ?d;ÔdSƒZ;:;rA¥Ÿö­ÀÐɃh~¥•å•8´=‰ iû!¼ÿÉv¼ûî: Šk¯îŠá#"áâbß$ëæ×¡¿b43¯œ  #¢ †z«@ñTó®A©E˜j’F8ØÂT®ù[oùmHoø®¥¸œ°Ôð]Í¡|{K ÝèVSÄ£–×R¤Vî3ßZÆK\53—g于÷»‹¼ÄéyŒ­g ¢|$6Y¥—,CJË ã¢zâ®^˜€)ó¿Äì »6ÈGÈmÕ×›#2¼®§¸SM= ÄLÛgff;”àÙ«QàJïXvðƒ?&îDeש˜€oó pÿ ¡§½TÅõ¨´ŸÍýÛØ+Ø‹1_~U9eèæp•ÞΕ¶l«˜ÿº‚&ƒäzÚÚÒZBÀÙ÷Ûî@ñ…GF4ï³+VáÅ›fcê#S0†×ÞÙ^q¸àíënCpÝ×"íp:v.ß=›±xy >_°Þžö´ÅŒýC¨ìå…`ºàåIŠ •HÀ«¾çÚlÓó& ´¥_ÜOà¦AMÌqh{Ûôë‚Qಹp·nߊٳgcã– ð ò´ǯG|ÿ8ÄÀ^g§@X²2œŒÔœw?æ¬_L_æñ&n[`¯Ú ±sé¨uDÿ1½1pL”W—#51 ‰›`ù—+qï?¾Cïn¾xì_ãЧO5Yy$N(\Qp”œ Óp­~3]ß`4Ú[¡±B€§j9D¾åY±bˆ«§©è舢€/¸C®UN©¢µÍßÕÔäÍîª*UûJ±‹(¿ Öõ|£¨-\E{ˆU<ª¯& Õë囲Ë"¿\ÓÀßÕH;NwµÅ˜òõרnPjJÙµ¬—|DìC@póÀ«_ÆÉµŠg,ñŠ%+"ùÜÜ­+F†‡Ÿ=XB×`Fƒý4úy{ãÁø <‘•IîþHÜ›pûv™ãñž ÐQå-$<½ç Ì)Ï«¡ÿÛ"=q¨Q‹›"‚(w[¾qý1ÖÆî´sä`O%ðåI»¸‚åf¼Ú\ƒqq½ÐÃMLàÖ'Ä&¥™3ïwâE“}y Ef“ ÑËÏaTãÀ;û^àx” â ±ä ?µb%>}b>ïNÆMÍ€o @39ýÔöOCtB&7ꑞ”¡€ÞmËv`ù†D|ó}"ܬh¯Ó}{bÔè(øúº(J£bõ@ÄÔƒ‚NLW…¦?—8Þû…ñÉêÊ Æÿ—GC;]-/[»wÿ^ÜrË-¨×Õáï³ÿ†Á“RãÝ–srg¹Ðš8´n°™*¤n¶„ÔCåßzÅf (ÒDöÇØ™£°qñf|ù¿…¸eÖ'xã© t#€¤E‚ÍšZ@ª‹Ä%N±ŸIî­Ö:oGZ¡ÂÓ‡«¨G`JpZEOKDõ‘újr›€i€S^W3_±ÙYË|k÷‹´Ç)eˆÔ3_ET€âµä¸YÌß(Ê™]æ<¶ 7S¬H˜Ñú„%]”Zð[ÌÌhx­±´A×Q½`NYxzl„ÓihëUÎRJ(ÆQ’W }1í–Ö¡”bG–Ùb­)Oj©ÕÁ‹ï´?Áœm®úѱ@˜‹3bÈÉ r"°#G÷Raž‹dljä¢QKKJɲzr«iÛŠö5ûDgí€A~]˜’³”….Zö)5Óœt®¸‚Š”–L_VË“%r´ë þU޹XsóXÌŸ`±ŽŸÆ&°'æÓ/‹ÓvÖYÌ}õ¡ØÂ$ºèT\ÌžË"Ï Qez?¸jæmތ׿݌ÿHÇ-ÏÝ„îºAc§QúCÝdÖ“v扦Ýhrp'ö£G¦*ìå)ÊîßwãàÎd¼5 ^k ÂÃ<0’" q ~ˆ‹õ¡…gŠUX@Kñ!E'ãR6Sù—7èDU|E“”s¨)\ \¶ wþó‘[˜ƒ¹«^EDL„Â¥mâ]Ê™J1QàÒ’Òè¯ürN䣌îë *`YZ w¹Xš‚ "ŠrwSޤ=©™ïJÙIº¶6¢¬¶•¨±ppÕê4&åç ³Â5¥9x'¹ÝÃÀ¢¡ÙUz‚Xs*‰• ˜õwuòÄTWoi?ì¨Ú"Œ¶3à «WÂAÓašj|œ†«û … 7$2hưÊLCóÇu(æÆ£¦´yzoèêJQHy`ÇÊr”s#ÂlNØRâ% ,ߎîêÕ ýþˆ¨‚±Šs¦ã˜{dÐ t¥èÊSËWâ噯àÚû¯ÁÄ¿M€ò!mç©€²‘÷ÃŒz¶8®/€¢º=’…C;’±eé6¼óÙ4|°¾î6ˆŽñŰaaèOÑww;EyÍŠ\u‘åUûÐXÅLq& £·£[’…ÛŒE1X÷üU0Ê@IDAT¥6Å? \¶ ÷ĉ xø{Â'ØG¸çc9Jcá|äm,_ãq§Ÿ@‰f„³&@†çž ˜1ž)öì)pzÚËë·:Wq3>zŒ=ep"Ê®'÷·ž\æzr€õuzdgçÑÂmOÀf§å õ@*ŽïO¥ÏòX‘Si§Ñƒšÿ~vvèMn\@XP¨õ€;•îD‹Áq"e¥<ð ¨í\[µB­ÿZ —w Ê3ðîöR””ä!0¸žẫØO{_C<ÌñÉžDLM@7N·ûj wWã€GÇFñï›ðúï«àBšô‰í‰®–(¯ðÆ(ÏCøxÛÒÅ#Üë`nï ³’TlÊóOI:wƒYq*Ž•“óH›Í ZW¯éZˈFXÎjIq¢J§ÄƪUP^šóP¦äÃÏhŠ­ˆÄÓËW`Á³Ÿ!õ`¦>t=‚"»P|Aƾ¼¤-AƯ zë`Gq˜Nˆêñ³Æ"/+»VìÁž‰Øµï(–®ú¶Zös Ѝh_*¯q“¡³Uäx-É©WÞ©–ìMW& ¡Çá‘\àËmÀ-ý@Êçš@®:ÿ(š÷<_3Îù¯Üérœ<ùZì;žˆy¿¿ šìQÞÓ=pÚ{´cÉ£„JÚV”éP| ÒXhØÒÈÿE rkÄ®£Ô¨Œ¶}ÓÁΗ»À¢bXyùЫëlÚž—¾c§rŒ¤ì :’#ÇMY“{×%&6<ª>_–GÑ걫šgËo}% ¸º„«ß†å\~N§¾¯¶ÃКF¡ ±™"Q+öGéŸ\ì–Rž8)©û#+% ¹²å4éT[YEYÚ:r_­àåí„=‰ˆ´qà £†Ã•¦œ\ÉÕó" Ó;Ü b…;{YMEB:PÌ…Ê®éÅ%hÔèÐÅ‘ò²lKÛ_GV"ÛŠw*Ñ"4:<8Þji£;‹o['WnZ òª…äÒSQMär-˜g=ç%sÒH ‚QR››]ª£1Þœ…¹Ø¾£…­_]Œy;ö£‹O$&Ñ|Ý¥1˜Û¾bà77G•¤ùK«×à…µkˆ¿¿t;ºî +Z19õXo[w‹;ܾ!+#™G³°{õnl]± 'R2a­iD€zt§ï˜(:ñV¸¼:nÜÄ&/÷¦`¢€ pQÿf#0{ðÙ,€zCʤj$ååeަ½‹¤ƒ X½z)^Ö‚ 䲸D kóv,úx5ª© "þëêh¢Hž»…ôÁÿ@ìS¯,0M숦þ“ÅŽ À€. È@MÔ¼p pn™h ‰ÔçS‹œ^ƒ¾¶f#xPl ´ YÊѦ¦¦Þñ8¾ZRŒk^¼¥}û™÷âÎ;ûr:¦¬¤–šzÓ5žU6NK޲Lª%HáMñʳ-i”Çšž•@¿Z†!]SNòSi–ÊóSÓ2B‰3¤mÉQn4çyR•ÂÔ Û<¯<¡6Mžn£J¢æv¥µ€yD-UÍ£U¹„«8º) 6aðq³j‘Th£<(•ji/Í8n wý„[gþ†/~ =‚uÍ¢- ;ë•J Cí¤¿¤© w–ò¹ äÎÖñ¼¬ª ¹©¹(ÈÌGQ^‰²è§“S–{<Åù% „-HXSéÊ‘>ÊøñPœ_DFyñ¸×[ÉsĨ9ˆªv¢s‚©*€S ’ÂZÑÓP‘Ëí[¸ÎÒû#2$†`ˆ“1wº¶J:ùèbx^áÀó‡l$(ùñÛWûßJ"#˜uë¡+?ÿ”^Ö¤Ť¯¾ H‚Ž› L¼{&Ý1´VÂi”¡Uÿœ†òvPðFù'bpuuµ8²÷v-ß…ýt@qŒ\ÞŽ_:|4 ½{!†ã=ˆ ÐÖo®m·ð(º”œãÒ2=M&Q†“m±åjÊ—”+ŠFE¥°õ¦W*•–­ô”á¤r •7¬hžç¤@SZE¤iEe-9z:ÅÔ––Dè´¢¤ÅE•Šy&GÒÚ–Öêj«TZUSƱÞÜ ®¤›Ž´V”|ZÓÚÕöŽÂ½Qi-JQ%EUpösƒŽ•¢übE!K£³aýœxôO.P~:>~üt}ø.L¨ÈÝU”³ÿÔz8³J›IÍºÒ äåñ¸™\;‚ÁFÃ7ãí“ÚY"ZAé^rgE¶W¸³•5•+å¨fÿQì9³©Ò“šM¨d{)WkiÎñädƒ.”'yôM9Z@¥;„ 펩÷N„&ë0æ?õ+\cqdó1G!jò$Ü~ç è%âóWÓAE.ÍáÛ-S™Û|ûðû@°²Ó‘•KÏEá–»ûcéÿ¾Å¡Ý…øü±oq÷+ÓàMn_3JjnúàG¤¦UÂuû&lÝì„F*¦ˆE#9¼«¿ø«ïBQYì½Ü0tÖ$„ØdaÁ›Gpç'·ÃݼKÿ3‰µ=ð¯×¯†¦,ß>ôA®-‚wà ÷OBCÚ>¼óïUp °EÚþ4”×Y¢çõ0íÖþÐ?€/^%­“óy4lŸ®Ñ˜ú0im_ˆ…÷€JÊ+¦©Å­¯Ýކ”øîÓÍ(,”réˆaÊ(\7­Ö~°ë·FÖœÏàá}'ÂÌ2°ð­e8Êþë2r¦Ü=n¥XþÊçømu2,\Ýàç!ÇÒ­ä%x‰[Š”íd ï‚ðzrgëDv–vf i[:—ò²/È§ëØŒ£™È:œüŒjžWàkàAE_'tëã ?w„„R,Ú‹&—ܹ`[6i›ËæÅP¢p¥T9U0Ã=Ãï?á7U&7X·ÿ¶'H; žWwJ0#ã€ä7¯©Â?{õÀô„F4qˆÿdÝÒÀ²¯¯yä ÄÒJÂË<"žM¥´éMCŸ½`i'>âd¬ž]P8ºœ‘EÉ2ðÿÙ»ÀªŠ¥ý¥÷Þ{/ô„šÐ{oŠ…þ@EEìí©((*bÇ.(Eð§Š ½÷jBz€$¤‘^ïý¿9'7$!xY¸¹åìÙݳ;;;óMˆüB¼Ñ~@[ÅlçØö“ˆ"4Ù©Cg0kö^|þå6š2¸ k×`"5x"¬™;ìˆ "^jˆ-ýŸ§áÏQ\ŸûÎáçBjþ‘Båñ™ièMÒêûÿŸèŸz!·’Ë¢ÍÝ“ˆ ”d¨ =œ¶ôæ=ý—câ[)ÂÖÌ'gÃÆ?š•àÐÒ °È¾^ƒ“«–`ÎK?£ÏèÖH›ÿΈic`—wŽŸ {8^{Êñ›¶âľöxöó‡Q³ 3ž]€ˆA­@)«Ýñ4Û^"±é’‘"Xº.Ù¿nó@ µ¨tz8d>{jÚOˆí½±gá|Ìÿ^ø¤¢·®Á‰ÄQhg›„ÍsöàŒI.†M×䓸}y,†4?ˆÙS·`øGO£uCSlþh¶¬9€ˆŒ®$ÃÂBìúx9ÎúbÔÔaÈÙ·3¦,€OD[ ‰ÐâÔ¾ƒ8’lƒ‰ï=?Ó,|9vRm:ã©w@ö‘møhê,X´DOOŠêÂa …)øæõ…(oÜ“ߎ@ÒÎ X»|="n€?^ø{ 0ñíÇëû$fNœ¿`⃖âÐÿm€M¯!xú¿cpfõrüøü,4ëß% 7`ÿ¾ ¶m  ãðáߣÜ5o>ãŽÄ][p­1qÆP¸˜äà§VAÓ¶{¶1Ž-šïž[ˆ0.|¡Œ(eël6tE·rÌó9¤‡âùO&ù(f¾ø Œ|1Àá0¾úx#úÏxš˜`Î Ÿª‹AÿŠ<§«”“e?+šYQ˜–#¿˜§„æ*¢vöM by¼šr6™ÚÙtäP[[VÀYEL©Ùrv²A# ¯¡½Ú1ܰ<<Z–[sž>𘗣@eDÊK&gyIþ' +I¹¹Ø}î¡­àÒ„óÇ•'÷N᎘ç–bÿ¦eØ™”ˆaaw i·…Õ¨² ºÑŒÀƒïRÌxìC }þaô}¤lm¨(ªeÿ¹tEË«Þ'h û·E§þí‘•“…´äK8Ií®`ò~;ÿOAöÀÑÞaM¼Ð½{(šÓ‰ÍPjXðxyxXŸîyð”rK4ðÁZ`î#@co¶X4ºõévs ^Ƚ‡©•)ΈŶÃ9h9:„Gûeиù!Ôµ'ŽÅ '3;k<üá(´oê oÓ‹ØðÃ<ý–#hx<Òøê,öºÞá¡°#x¹WË&f”S+ÑØÇPìÑLî:ôx:`Çš·‘¡? þ plW2ÝO¢ 0!,óÄþt”? ƒæÁhÔØvN‚T½¶–¹¶þð3ö+…¿œ³p™Â¾¡D"ú´vô¶ç' ¥ôì’%#jåïHßbH[EÚ»»Ìß@Y$?[ ­¹žüøÌ¥‰Áâ?Ò.TƒÀÞýðè”ÿµ8½}òcª¶¶ö~ðv°DyI 5/ “ko̲ÈwÖkDhŸÒ‚bDý¾?ýp6nÌk€ÔŒ<ØÑ¾W¼ÔT˜ÑFYlE!ëbÆ;É KG:2-q6ÍmafÄh`¤ÃÎ×úFg¤Wo}Rìf…tB‹QrÊË#&êå|jg‹O︨x\ˆIF*ñgósò¡!ŒèfIáÛµ•:4§óWã„<ò ¸5_Sjg-x\J¸)% ÕÒϺñ$ïºÏJ†ú?7Á‹ò€Êã-ê–ÄèéN¤ëV³©î•2øS10kÖHžTxaÆûkðÖð<òÖD2ò ‰•)Gý­÷RNõ²<|Ýà3ÎÆõCÑRÒ°ÿ8¼- [>ÞFã¸rx¹Û 5Ÿ×Þ}ÂÏÏ‘Z^;>£„¤/õ,õéžàçO®“½6âtVŸþ)Ô ¹uqZ)e¡â;g=jÛûÁù›PØ*DfBL½ÛA¼,ää+Zð„C±(§S–ð·2ÐÙ+>*AÕΦ1,.@“in‘DTƒô"iaC]w. íáC“/jd·ECn:œ¦ÓÞZ݆T¾+-ägy¯n7˯·1É8•WÇ{–Dü‘è_÷Rb» ¸¹Py—´Kæ¥/8ÝSIé Q6èÆzõÖItA}<5©»â0ùÚ«ËñÙ¤ÏðÔ}øX?š/Ørã,O‡Ü{ë’}IüMQ ¸x:3ܳ3šµoBßRœ>ƒãÛãôáh,[y¿„Õ:´@³f^„)ó¤=¼37Õ†Äåe°nìk, ·ŽÌú’n;ø¬ÑTtHTçà{ìÙ»íüûëÔ ¹WñN&9Ù‹Ó6—“¦±C zµ¶Ç–ù+ê§Ó¼,|g:O{Ý‚uÆãêÄXõ—!Imyüt>?göìÇÁå 9-.1çL£s%UN¦Ê›ü¡F—`þEù‰Ø±tlîoÛ«ð9Sš’Üb®PÎ/ ¬œ²?ÀüÏÖ¡7Oìš³ ùfФ-ÜüZÂ÷¡OgÂûXÂÒÌ&.ÞèÓÚk¬Fh€Ì ’±èåhÿֳܛPÛš‡e¯/‡ëÛíprÙ¤{¡y¸;b‰Î™—ŽÓ{àÈJò:5Žq±HHô䢠k ýþiZaI[ã 쇽釱è›]0 6þô¾³jÇh`ô?ðû.xn‚f ï¹åóÿC#/=˜fÂ6¢Ùó/b`ç¦(ød>~ýz:‡›`!LJÊnéÒÈK,ÿl9–½&ZbsêÁÆÖ ^Þhß;”‹´‡‚ÏiggA,BÑΚ³¦ÊÂÔÂ+cJ™|øw…Ja¿1`¥^À‹ëÖÞM² 0ð†IN28nJ(¼ÿ£¡_eXKªêkõëþð,|gr2ûâwnÐo”ûîº.ü¿@ûhÙ€K;¯NÂ,1_ůÿ÷¦¿û;Ì\Œèg1öíÑ € fü'™zuEWý"³r"TyE6Ña‘Ñ"2 yÜôg¦dâíæ÷þ¾¿ï8ƒÅ+¢`IMnP +ºv "ÍDk°ƒ½½-…uƒz´†«8|ü r-ƒç@¦j±qªOÿê…ÜZl–éÍ¡A0"rèkH­ž¡ úhŠßYŠÓç@Ÿ€íþ}º¡CGÚèæÆ"¬_+ £*x¸‘ šõm 'Ü?¦~^´s¦žƒ_»0LxÛÿ8ƒ Þ»=', `A¡ÏÄ!½#ádgŸ&ˆè‚ƒóþ@£v!hÁc4R P¨52AãέáIH0±ÑõëØ¦¾6pkê‡Çg<ˆu«wá»-%°tsÇÄ-qw˼Ð{|;¸e6‚Ÿ3Áè y$6²-’LÛÂÛš¢|‡>è¶5»\Š-…Ô¸zcÌ3÷QC)b¾:-{ã?uÂÚí›0w·9Âõæñ÷YìÚµ§£iûpX+ú C[/<6}<|¶‹^ÿž@ VhÚ§/zvvçÒ¢ [ëeáоvÄÆmäϦM04³FïÇG ’¶¶˜>ÙÓ–`å{?‘×fðéÙ|C^:H 8ºæaÑÔŸPj`އ>‡`FøŸ¾X´p~|;>íša›ؼî$h„䵇—é+‡K“6ùðIì™» G­mÐiü(´Ø¿ÇWmA‡žc1øÁ–ضc ¶6 ÁÈ)¡ù+–O›Í>"lVDôïî w;'Œ{*¬[ƒ¹Ûlàݾ œ´6°² ¿k¥¿ó5îxLÈ«éïÞO›=/„„ˆFGfGIÕ'Hòª4µÔ™"H¾?™2*[·n ±Iï NèeýûÝb DCW` žRÈÉÊ_Mr«lyrEP\b‰2nÐ Íl;ÎÊiâ†÷ ½Yú¥8i˜­<á7¼ánË`¯‡Ž>AÊ&ðÚ¼rѱÃG=¤äûø£õPÐ^޶½"`BŒ]U£û7:ô&ø&Q×äeLJÏwåÕip{"1Šæ 'öžÆibòΜµ}¾•Žk®ˆŒà|àA-¯7¾Vœ¦›öV:¸É?½ë¹ :ë³Ü pÓÅÀ9Øy  .(áV®·‚Æ{³Œú`uô«–v¯¥<â7&¨·ˆœBxÊZŒÂNéÃÚΊúHbVPJX&cSæcF‰>TJÜ[%ä*È'´P)m8­ˆ[K=$ñcy?µµzÄÖ4$v¢¢ù¡F¶”˜±t±5¢=mI~>xú sz·+áMuôI¾â2ÚIð$”Égañ‹1©ÌÏÏE3¡C— 5l%#.„eTÿš°>I#g塘8¹‚§kÍHQ‚L)%h5äu&ñiÙ6+:ö±tdžØ:ƒñ{g¡¡ù,ÚrÒ"¨4(¹)^KK´ÚbûÁ€Ú_[+3öO éˆfJ§3=Ú½æ_.&͉}L;½ŠB¥Ïõ‰lmoMŠU ¿>Û#ØÑ°¥}U ;Ì|S”ÆWù—?‰&÷­‡ÞFn|vîz™‹˜œHŸTçaÅË1íN¢bdá_G†ã'=•2@«Ëæÿ)·¡N==C:”ˆ¡C¿C¿ñ3Ðkô“|žoÐOuС)+@êÙ(è;Á̓'×Xÿôx‘§wf/:aNC+¢­”q¾º^Ûùœô‹xÿ?í0 § ÞŸñŸ…êÏùõî¾»®RKêèhqDË€4ÄþýçðêË¿âోÔi¹ñ·(¬¿ù/q@"¨JÀEÁô—J¨¿é/p@Ý.þ…ïí[®žÖT±ðê…§¦pSó›LVW’*Ä^ù~åSõ»®«º(Yó³”T³®šeW§º®–éà+wÕþT›ªêß«—®ÞWýjí’®|¿v®«y­¹Wž˜;™2ä_gÄq¥´ší¿ù²«×Uý³”\ûû•Ú®]þ•<ÿÆ' MtP¡ì¿Qùuê”1rõ8¹Î wÑ%Ž<žàÜ0±kTÇQòR­ÈÄZF%, Ü %O+ÌиESÄ‹éC•W‚«—Ís"„Ý÷>Z ¸„¹SŸTN…ª_¿™Ï2®eÆP_7sǽž§\Á†þô³¡}ðδßðÖ°iýÆHtØŽÊ n²o¢o›d^«^³w7‚üQ:® Y©Y8Ÿpû~;€£»O`ë¬Ð§C›'1xÛ´öE¯Þ áëã@Ó {*RŒ)øò,KÑNßB닼¸¡”Óµ÷ï'Ê£žÝÓ̰â¾|§­†ÿpóo¶:YlîÎEZlðÔEízm•\jR[y+ÛZ³äëQq­k"Rц¶I';~ºZÀ½ú¾¿Û_:š¥ä[É‹«)½u¿Nqfx¼gx QŸî°[´¥¸œ‘ŠBš¶X:x(‚éGbÏY{tïÛV ÷iÎÐÞ ž}ár.gÚ=–ÔÒ‰Š’øAkè€ÐˆN(MØ­¨ºvç´ö.¥¤BÑzŽÓ-[ú⿯-×ÏÎb°›ÓùÊ08¸9qåfæÒÛÛ|9UTN9c;Pxu¤YEXDcF/CìñDÄÑyíèŽcX½ùÑÁÃÕŠX¼®hÅ6uî±í7"Zƒ‘"ðêso/Õõ¥+J'ÀÏ…›\9AÑ=Ôõ¼¹Ý¨ro‚Ãå´ÓwVCî„ï¦Á)Ð^Åùåt¤ °~¤%8uiýi~a@Œ_-{̯sÃ5Ê©ëçŠübB»×êŠà¥•£XÚ{èk·vÒ2Ê­ÈæÊk¡ê'1¨y¼ª;Dæ£BU’E_Kûb}¶…&¸UIQŽI½\¨jתÔ)ð<¼E1S$ÄRUª,§ö=U×ï¤l·"Elg-^ÝIdÞ«´ׯJì}½rœ]ÿ¶ÐéÄ;ÀŒ8ÎVè3ælüácœÌqA^|z=ó±§õ‘~d6älÀ¥Äxص| CƆŒÛªÂ¹åct» fðªÚ®ýƒ2€ïŠQ|í6Üö+Âär†öÂO?ÅŒë1gÎf\dP•ÑoŒFƒ°Pè3`CµÎ¸í]«Q\TßîëSEÛ0<4¨ËÃq™>§iΰç·ý8r4;öl×_n¥«+ACh¢áK×^ \!ðd¦¦œÿê‡ÇµØ}k~¿\ÈоùÔäÚQQ/zݦ޸”zN_—GÜ  ›fþ€Š–ÝÑPækRÝŠ#³‚î³® ›ù­zž+B×ÕWõZÍßu÷êÞuõ^y—»6'f?¾ü>£é5ìí(îcBkÍ2‰‹3›6`Ƀp nÉзg‘ãÓ£nÈvÊAÙÕ÷Ô¬Ir\Í•=ìÿqN•zcôó½Hí\Y_öùDÄœÊBP—f°£™’zÇöйïÔŽ0óñ…yñ%dhlÑžÄâèU³ºfhJsð2 ѨCcX1†HÇš‚lDí> cWo4ིkÇ´LÖ™¤A“v´³«*M¨,Côî“0ôòB¿#RNFRÒejmT!ßÌÎ.^.°w´¢sà•–ß™ŸÈâë*‰ðslœš®t#Y$|ª¼ û]šZ;¯ÜIø2õß5Ì |åÑK’ïjçÕØPT6Ò%jªž·®ú¤ÈÑV½Œ:i“2þL}•yåM·¯©A+”ÍV Úø›„P–$p ºú*³©ZÝ’‰OGaVÿô Ýψ†f8w( y. G‰aw<üÄÃ0¡½y*µqúþè=ùäîû‹Vì Mö`BIqÆaùZn•š…òªL•Ô¨ß*Õ%øƒ^åFQɪìÞH‹ìu7èú_¾ëH–̺>­Á þ.תçå×:ùV½Œºú©z¹5òJ•©6mµ¿K¶ê÷V/óZy%¿¼ªç­^FÕ¦¸vt„þÎ`´a¸ï7ÞX‰wFNÇЗ‡¢×Ðn ¹öï@ií·8É,>"¤›ÐæÚÓ…˜¼.èz_G:¯]FìáXD‹ÅÁ‡1ã³­¦ÎwöÄãõdýˆ2¶ÙB±ã匩{­A們·Ê …w’„Š„Ìw™'”ßäwõcͼºß«å•lÒ'’n¦¾ÛNéÐ=BSmBŸüÀt-^\“6ŠZÛΟm¾Fl8/rcÇ3ÝÆCÞÕÏR±¡c.?V%ŸëSmÔ ¹µ9Rë»G{ìÆ]È7iо=¹{fä1 ©O-¤¦˜ŽLF\LDƒ&‰††è ú ˆ ƒ2µ’Z6U¹O÷ZÇòYÄ(þÿP_×#ÔÞhЍ»d™J}ò¤ñ7–/õiùù½Õ@ª—¤e`Š ¢èQ)ÊLÁÖû1xòP8¢œÕ¤n¡Ih¬F”åâ·)‹pè²ÆtµBIR)4Œú%8¼"Gè±|åÞd@';L—ÆI0 åwÒi@M@ m*#¶IhSS›Ÿß{²Ë1òy"AP ¤ŠËQ'ð鸟1dîL<ÜÝ“³†óˆ 9?ö úMNgÆúÜp¼õÑ0Þ¡“@¤n©]¥é‰øáé•j——¾ƒnmœ8e°†ôdÌžôòœÃðÖ²×áïD4 Þ³v=¾œS¯öN„E•ÐL͵6 '}‡Qcðâ‹Ý°oöR,X–€@âï2 ò/]B±¾†OŠÎÄÎÕ7d‡Ü©‰ý‹7ü‚!FŸéV SÃŽ;—P£®Øê†ð¸Œ¨Ê÷ØK°¼Ç‰ÎGƒ^€ÑùA6”<à±&Š9KDê`(9¹‰MÇK2gÉ&ðˆÒò]ê“$uI̪äÊüCè°¸ƒGqì@¡ÏLжÂ[x!å'»øBx¶iÇP¿Î0ó‹D±ƒÊò.bïÖ†ìµEü¾“ȦƴA÷6hÆÂl’ŸÂ±=1È/4€OËFoȹ€ÑÌ pdýnœ9™צAÈã'ÞõêlÀ[É'ŸŽÍáeû¶Î_‹]'ÐÙ´†Ú2†BÞ…œR;´èŠÄÃe()á¦Ì>5{Hül܆Ù–”.cãòƒh׺Ÿ2WŠnV§´ãÇñËwÛñÂË=KG‘LZµ'mµB¹Æ…ŽE@^=œfŒnÕ³~ÁǾóµ¡]cò»Žrj—û—†ðuŠ‚Ë–³êâ>¦© ­Óר“«+ÜÂG(ÄQè‹Nž\ÄE‚ôðVÀ+ýTŠ¿Ý¬:®.D¿>A <_&/¦@B¨G`æƒ2«Ä"t•ôÍ }CNøì3j¾pšMcŽ•oG2Œ%'þýñ„ÏYFIÏÄ.Àøî¼ýÌ?€Ý±z) ÍM!“BÉiæ}šõÉ6¤0e0ór¡úq7ÀèvŠsÝœQ@ÓÒDÁå…_UÁ¬-¿M:ô8Þ¶’‚4!ÂØGCHwS.rX§¯%/ÑòK^[Ò•Lš~!«¸è¼ÒÙõ•Ÿo6ŸQ7 ß2¯?Û“t7‹¯h¼7ŠAY€ù=ºv,oÀ®g>BjËN0ÍÌB›OŸ‡åÅX”\.ÅO'á=( £õºEh““ŒsaÁÈËφÕþ Ñló>,R7álŸ(¡àls>y”lchñ‡ŸÂé!Á£ÑÖM˜‘}†ßÇÉî PâÌûÒÑùí±ÔÒ•#9¢ö=û‰òøøoü!«B1ÑC¾#¼_¹,ÜÒOÏ-%ßrjóðó£l3—ƒ §®ôé+½€¡í¸€S˜e¤D ßd°öi.ðä›ôñ“?« üØöÀ³=Ù¿ìÓ¹ì§Åì' öÃâq€¯+žL`^1ýº?Œ}:PC˱¶¨‰/Úr|•3ô©l ˆ‹Oøûó•´¶òeþáfjß¼Ëþdsði^Ùoo­BÅÉ‹ÐxØ¢bîX”v¦tÓi”¿Éß¹aËЉÝ"íá¹Vå3ž\ ^Å]Q@­€GÊEqÄ!fu¢h¬øQ˜`žÕ?¬¡œ\ÉŸN¢0ÁùB‘rXÿœD/©ÄѶ¤¶ÖŠ(?þ¡¾x`Ò}H‰=O[Þ8bòžÂáÍG±nó Xr#bbc® ŸœK•M¨‰âÔ*Ÿ$y6eó+› ìDpó1Ù°ÊÆY„Nâ¼+}#CEúÁW„C„e³)›™KˆM¬‡T¬(eH"<ËÆRx,‚¯ËRŸltD0–úrùL mR¦Ž6P¡šy„¡Mê“MYm¤CèW¡ÕèhAX6tB›ÔÇ@_2¶–í4"ýPGðÍX¯%Ÿ+ yÝ–s«ƒ½\œ-áÆè—>¾öÔÆ›V Å"ËK„`iмÔN©£îÚ´Ü#ß92êÓõ9 c‹2윳¹±AÔÎdá—o7ba|â¿_„¨àLÌøbl µ8ºl%>ÿ4_lË%YtÀ4vÈNÆ}…~ ßCà G”ç`õKß Õþ„ÚƒwŸÛ¿æÞÐãâ³úã5·ä4µNÄÃ?ƒÖÓ›ô°îûh3a"&=Žíï|…íqú CáÄà!ê~S0qU© 2…¾¾t8ˆÁʃ'¹PzQ‘A“„+0mÒjÞçczuÿ6{F~ý/$ðy.ByÒzé:¢èØR\j1 5À×/s‘¤V*€Ovô)¬a¨âÖO…~ÔVL;æþ~°2)óW£ëÔðèCÍpàçExÚ&ø·hÀHB‡µ…z·­b±èŒ,=1`t3Lûò0Î%_F ”ä%cת½ðìó0BœMËùEuЍºµòíËr°}Î4|h8$à)ë‘ð|„P#©Õ–±†ŽöŪÏbK÷fÜÆY™¤Ô¼vyüÎ UuqÞ3&f®£{“‡›7¼ü¬Ýéy¬XpÞïr‡ ¹œ´DË&ÜÖ¾@¨ƉVˆž€îêB¢l8ø»³5ðp+uáhá­æã_´ d¾ÊN‰±ÎûeQy 9'mNü¢ñ#”r°ŽQÊGU‹ÇrEkÖ·)ÐÒ—‚+ëMŒÐàMÁzD~ägÙ(éhëÚðsR㹋ÖN~a\¡ `Š Áßy¬ªhPdÑr`É+‹Ø` MÔÆ)[…8þ.šÞ‘¤M2EÌzE³×ƒ‚¸Ð]6Ñ@Žh­ÒVÅ7ß)X¾ežH}¢ÉÆvÈ‚+B<u9¡<ÄKAÛ ´U{;ÒĆ—5&ˆï<˜l.4éш_3K!¬È &=FB/û8Òb㑳ø{x5A¡C…“^M#q1•Â{AÎq¡Œ0Šcž¡xÈWÖ%mº„ƒìû†=àhñÊç¥ÜÜÑýF°î2dû²Ï•¼Tx†6G¬¦œ‘µJÅPÖ¤•+p_8…Nò šxAÚ/ý$)¤r ‰]z–'×M•¬”Ì+ÚÛáÌ+6ÎaU¶ñg¿³|é{;BˆôíCo"|4eŸêÆPSÖÇ~’:\…–+*7ŠàÉßDk6˜´ŠÐàE °ER¦hþ…V–UìŒâ<†ú&ÆyaS/š~hq‰Y²VÁ©D×ïCrJM *?w*–æ}\ü¹ Ò§ +áÑ ‹8@s G˜¶õ¢r‘xâl‡1…¶¥ _BvÈPvÌï¤õ.K:šÙcJò ð€o€Ú n‹|jÆãO% jÏi®SbZÆÖJƒå’$ãI'˜Éo•?+¿Éµêy•üü±öïríß®—W®)éõI™JÁò~ƒ¼JÖ:踴IÙÂ3Ù`Šv\Ç¡¥Z’ͬØÛ bK…ì2žÊ•P8.å)M 7 9ùE¸@œ÷’ä\öÑ%ꛈ5D­µœ¾ŠoBAØZ½ó´Ì‡ó^W<ù°%î¾7kÎÎ6|ô¹GÕçK¤«UO~”¨>Ý€å\¼ÚvÆK󟆓q*¦D>‚[ãñà„®XþÄvD¿4-½‹°gáv4úܹ(ªÈ¸2®9ši> cPMÜûk8”Ò¦ô(CãZ`ÚgOÂݸ€Þ}p²/ÇöŸV#ú²>[õ"|­ °ôµ·ñ_/Æã[ 76­ûâ¿?> ÚÙJ¨\IbprÉÄÙbú‚wÐÊÓó'¿ˆï×ðÈËÄÚ™?Ã8lžûæaØj.ᛉSðã§û±dù}û`ÌFŒÄ³/àK³ÆÀE~®ÝžzÏ=ÙÉ›~Ä­C 5ñŸÏG®k^ùæ x˜añ§à—϶âÁî´=Ü ÇV}ðþ²ñ0È9‰g[½Ìàº)Uå€hB›¯_ǦÍÑh2¶îÃŽ}åxúýH¶D‡™«æ¯þW`ÒÓîÀ¦£åxbZ ´ôð€×›«±aU <Ú’YifAñ´íâòöC˜ûæ¤ -£¡m™½å<^@ò­ra¨º¦.ôß„ =éK“LѸk{xPuulÿfÄ—1Œ$ôlh[wfÃ~¤mýœ(ì9žË–ò ÔL¢-ñiÛ ÁÁ¿báks`hëŽ^tH‘0À"^K*Í£ã×ùTkEX§‰Ÿ©9¢×nC\–#¦®|›¦}&˜·8ï·y›—ïC›GÜx§Ð*&fè4q,¶ýñþp¶mï«SÈ­j¢R+j¤3Å&’;✤x¬œ¹ÙÆþ˜<¼‘Rne¶ú·zü«ø_p‹©ñŽã¢¼jÅq,X¸Ùy¥p ñF÷‘}Ñ¿<ÜaJsS=Så©3Ý¿µsnyå²=¯=SÝòJîÀÿN»¯uïµ~¯Ý|É'Iø~³÷(7Üâ?׫ûêkêJ­®‚µ 1¢_‚ˆCüѳ5ŒëK³‡|Dí<…}ÂÆ½g±lE"Ûxã?£Û C‡ØÑÔH1»¬]Ø]ò½^ȽnGQ€*ÏG.ìyä(fMúÆ4ö>™ï†ÉýÂxìU Ç–mÑd6Vì%ÈöÀN°WöW"h[Óöå?ü>{röoE ûMé°ajaˆ"z[/œ·‡Ã=aPœ‡Äd ôh„AžØñbæØ÷àN¬¸gáÿ}èE|Ncj-¼öc'{£!}øÝ‡˜ýÌLlp5¦ãW:¬ìˆÁ}ž‹ý-Ä»ãamT‚Ø#ÑþÕ–Ch:Z™iEGI:òºEßÍh§&bÊCN' q¾¡½Z—Éã°yØW˜ñÈT8Ùê#áhŽÔ[™:ãá-ðæ+Ñxü(4ô0¦ˆ+@`Z˜Ò£5ヘõt²bC(v²FÍZÀ”8†õAX"Að,Xø`hí‡n“;àë¹Gpá>'X¦É€ÚkZîÁØÙ—@îâøØŸ©i–…°VâF„‚2S@ÞY½ eöé¤4ÅK=+)¹n˜ôÄùzˆ|þg’pK´ÙÿLmõµüUð¸P×Gü |Ö}ÿ«EÞ†û„®êΕêÓx*ºeE±XƒUkŽã="b\È,FÏaѾ[· "2”¹"ÌêN´tèÕ«W ÅdRS°K«_áçJ aNtÊgÁ½½v·ñJ]åT/£ªxæ•IFÊ­•ªp‹yMÉ"§r•IúG-îêû$‹–GÏZ°»2I[ënÛõš£!˜©TèÊëå¬uš› ó$êBæ/ë—¡–ª<«µn«ñ•vßšRÒ,6ébÊÇ÷?MGëúrc¾hÄÉ•§´[Z=)mxRúAðòM%yª9Ž(“Dqd StÒ Ý‡tÆ…äTœÜs kç­ÇOýŠ>ÝñÚkýHG^•_×ëÚôÝAÿ@&š)‡Ðÿ@U·¶Š‚(F ú|ë't0g×].oeMz„¶:³ã  àÒ‰h$'!°Sk4oåAFb]ÎÆW#^BBàýDYÌ“yÚƒV#€Cùé±Í!d qÇF(§–Љ‘i\¬ˆø´ó(#\¤Ý§ üÛ5CSBæˆ9LNJ“ãjK'GøÒ®Ï™ÎW²‘ÐÝ©kíšô0åÁ)ȉMÀëŸå‘=Óÿšo}[þJ4ÄÑ£qè?à ôyl:zŽ$dOOî´Ä=*rÒ²ñá£ñà@O̘9‚$Þž9óÖ´Ý+9ÿ=óÌ/hÒ9¼1 þMüxŽD¼oþÓ ·×««¢¬ˆóÔ%Xy»ÃÊüŠ>GäìÔK((Ò‡½²(@;ûºÊðê§[,‹r²v1ö> Ǭ@C² =YÉQD8FwÎEª ÅS ­¥ÍÁ‘åW·k$´Z:5ÒúÖpr0CIA..%gÓlK5«Ð76-Í8×ÖdõÈãô¦m¸`à‡Î]•yôÊ&åjškòDºòè’?{­féê·—%Q5Å!Ø Æñ÷µî“ßéeRV€‹¶Â®C$BüìoªÏ…A*L‰Áöm hÚ«}lTC"Ë”g&aÃúXúÀ´ƒ›QåŒ~5ôÙ¿1»âôé,x…"—˜óŽMZ¡I€%GŸä¿íBÁÍ$Þ/ÎÜÈéslŠOšj–+ã/æíH×sEÛžD¢QÖ<µþ Bî]²>ý:)kgåÖ©²]y•_oòM¼Ž™¥¨;—íÆœióàïjŠùóÇÒ|Am®W®Æ=6g£5ضmKeþ›¬ø6f»k…Ü ã'`ÙÚe ò!<½½ 5R»øúð×8)È’dÀ‰"á2É^y ª×¥¶OòÓ†ÿt÷J9*ERƒ´AÄԺʺBŸ:ÅH)ºZä^¹^÷‚*•¼BOõ6©´ÖÝ›B½J…”­&]9R<¤5sÈ´§¶[¦ÚÚéJ›EÀ–œ5SmÚj^½uß”Þæ;7'/÷ ÙI0zt[ ¹‚ \ŸîDˆ| ±f,؃Àæ]àß´7¿W±¿J»Nûúwu¦„%v¬œ‹FÁ6è߿͔îÌQ¥Î…ú\XwÃ9Äü:Mqzý3a2$Ÿ%Äî§pô$Æ kÊ9C´WœÇÊ/ã›'?DÂ1ö!CÌþæ&üð_øÙˆ±TužÐÞŸJ޵3?Ç‚¹gÑãÍ—0æ¡`n‹9O”åcÉsoã#†˜ôÓkh(XºŒæ³oøã—¼€¾¢m–ùž‚ln*>ûÐõ1¼úd ÄîÙ€w',…9ñ¡EQ(Q0p@¯IƒÐ©KMÌ„ êJ`DÅÁâ¦àˆI'LÞåQ»IgC¥ûz­á\Y­x»úœ*8=B\ª-ãgV¥þÎ\Ä劔¥/ªd§%6®2€C¹‡'ˆ¢ÁÔ• ¨œeˆÓ7C©Gü6ä»4„ÿ•»¨¥V4TÈ{ì®Ý¸¨õ@»ì©}”\‚ÁË$Ò+äx…åé1ºŸ¦4¯=†Ÿ|Œ1ŒÀ)NUJRo«¬‹÷KBþ×*|à\_aŒK‡~Ç/­ÅãßOC#‹t¬ß”Š.Ã;B{öw<Ü1¡¼?E‡P ÅA›¶ƒj;ÕÒY9sðß&Ï Ñ)#&ôÀoß.BËÉS0iDݽ¹²JøyaZe›…7  OX¢¥çu…$¯ò;›¥$©#—[vlÏC‘m×I]™”~•¼r‚Êg–¤aÁ'°G‰~}œc·Z^Ó#ô]QJ^kú2º¯ü;¸“?¤Kæþ×cðˆ+ oª—«Ë«–¯ûvã÷êåToƒz§*„_«”êùÿl½×*óæ~çDV™dÂ/¡½aü™x,út .DSî슕«ÔÅL—±þýä1‘wTyÝ*E°ÍÉÉaÿkigGaáÊxù«uØ”>ç²/‰ù«EÜöû¤•"°¤1øHÏqMèímAÁ¢øOÕ+3¿•³<ü ±úóßðÐ&°áQ¿lc3£Ï`ãÖ3FÂ´à ’Î%+¸ÿWs×ig°jÞnÂ)j±ýÇÍ8(”±MÄ@‹ì”t­IÇÜkôù0%ž†‰ä“I *!‚¢.ñO¤.žM„q“þÎ@åyYHŠ%ªÚûÃàÁ½ŒèXlþy#ƒÎ¼Ïo¢O·eŠË)aFŒ‰FúND+P™A§¡ Š9‡Oš¼°ÅŒh3Wë£æ4-1’2ˆlŸF¾Jà ½î/Ä'ò÷l¢ÅXÒYÚÎÄO­(ÌD|á)L‘}žÜ6†wã8Új(Ö4¡sÛLV’—u ˜×;ÜZFÞ‹?s¨°ñp…O€+LL©ŠN–ˆÙeÏÜ Ûˆ)ŸàØ>>ðð¶@njb.RÃj‡@Â@z:R°?Œ\Ä2üp~qŒ<``mBÓ³JÉu ïr/¦ ý²|xÀX¯ 4ÔR îàB¼èBÄñ»™S(&¾G ` öÿº f§Â…Ác| èECCÒ}rO,.S ôlH8ÎJþøcŽ…¿?]‹ leeEˆ Wwj"Vp|4ÆŽyDðå$åÏ&Ùôê™8 ËÈîX1â'<’^m9KWàøoaŽ~÷1À±´ ɤ<˜µ*‘“«#Ë7 Q?“f¶`Xßßq$ê~toéÂÒI“žÚi„³«Wã÷­0´ƒDPਲjr™Z*ùn@žr­$ý`ìàŒF4«ò±æYV»–è08ï œ„y³¶ÖÉ_‰j+–ÂÄUßýÕ÷8hÑÓ^iˆo&†s‰Œ)‚u£Ç`ÌÁtVÔÈžÛ³³þ»ôÛSpR½:öijïBÌŠ…øáÃý(§ä¥O»U=‹@Lš; žyQ˜>n)l< ¹”­˜¶øôè‡g§vÂÒÇ߇ÍS1ñ±ÖY‚} cáR`ÚâAXþì—ˆJ, t³Ê(0vü(F Á†W¾Àþ”ÂTÚ ‘—Žî:‡\Fì\iå‹ÿ1ì‰s’§¸ SÉa‚ÞŒ¾\±îÓðËâSÄ0¶¢£5/V i5ù]NÚ’7®ÆÇ‹ 0mÉËðÖKìñÓQ‰isŸIò|üÜ2Dr“°C4xù¬[´ƒBw)–¹ ÿy"šìTüòÖ7ôC)AvZÜÛwÃK_Ž­‘¬á<(ÍÆ†yÛž|Ì_‹²vÐgé³âìXÌ!ÿŸ)eT1ú±ð‘o>| O†/ÀØåo¢]¨%¶¼ÿ1–ìqÅt:=;—a)Oôï‰ñ:Ëù qX3g?Îg˜ãçw·ãù­°à•/q2¶b ×)Ov|vÆŽhEA—& ¶ã‹çN£øòeä•Y`øŒgг¹Êñ›ÉK9‰ožžÃç¥,ŸJ­!ú¾6ƒ°½•ãBxò†vÉZ¶»}ûvøèãnx·œH¸»»{W6 wFúû³æ¿Ôpýû÷G‹-ðýwßcÍokðɸ/àÈbóîá‚{ +Ü=ݹ«àn†æŠ: æ½"8¨"GuâÏ úaŠéZšSåbýŸÿ!ȸº²^ÊnXùF!HŽó4Ô®ˆ3DáÍR/¤!56ÑLjó¹á(ÎÇ^€·‡7ü0žÿ‚Ѫ>ÕsÀÁÁZ²tëÖ튥Qæø¿c¦!æ ¾[Ó¦p!¶­8€­à0k~<„ÖÃÇÃßÞ Éלò)B¦bÝ‚Cðmqºê„=o,Ä&†ïØr€"`VP[Û|Ø@„[gáÇ7æ£Uë×aË#æ›NÔÊ–SÃ+  u§¦vîèÎÀ-›¦§P© Æ•µ)/5YvŸu{WÂóf¡¤#¶þ²§Nч€•ŠîL„4 é^õÁOÈódPž¯† yó|9m?N ñ'öú FüŽéß BEܼØ÷C,þ©^F“‚£‰èÕo žzªŽ~û!Þýv;2_ꇆí°à»Ý9¦ ¬Š3°wÞxö®¹«Ö\Äó+§ ²±%V¾õ9–üw>º ~“]2RrðÒé$¬/û0}ßñø áXüü«ØVSW½?»|>â¿øeÚ2bª÷Ä‚÷¶ ìÍgñÔø¶ÔtÎÆëëcj9UiàØÐé»—"95…qÔp¦¡ ý 23 Qºó8‘qŠáîf…”s) dˆ>4Â)žxwì³÷óy*Cãvðìó=pjî'˜úé.$ŸJ;je-ט8áÁÉ©íÏÄ}oO@Ÿðlý™‘ iÚqœþšõ4'[J%D˜9Ö¼1ß|±ÝúŒƒ…~ JE¤—9l=Ì„tœ:— $cOT|žþ6ìm‘CÑŽ&YQ®xæ«û¡‰&r}M{ÉË%/OÇ¢™;ðÀˆ–4Ýà9°¾!†¼ùƒÊðvï§±t-Ú…õ"o¸Y2¨À†/có®¼òý7般¿'8pÍ^æ@PlÒ‹M„YñìåXÂÈ4%EÅ(fló<ÑÖžHDÜ‘x$žKâQb:вŠ`nfŽ6mÚà•§_UÞýýýï ^Õ7âïs@<ьȫ”ª;éðï·îú%”U¾~Öë\¥¦ÐÜCÆ6ÃôŸ(ȼÄèlÛvàd¶^ÜŠî·ÔR‘Çu%Ñâ&í?„#'.¢Ãh7dŸËAhßü8‡pŽ/õB°g>ôåzæèÿòüÞñeüøáf<ù¨9§ú¤®’¯÷kyf/u×NzÔâ‹õ¬•»| Ú¿iæ íHáWzùÎyGQë& «ç!)õï÷ª&XjcùYY)Īˆø”"?¯€ÇHE ­X€üœ|NØÜ•ç 5.—â3Jàí¬KYŒ0cDÏj'x{{#²g[4oÞ]ºv¡í-m§nÁQô½Äöú¶ÔsàVp@„¢¦÷õãŒ)ØE•’%aÛ ÂZ:Q\¨¶ôsrW§tyàiù©a˜ö•[p™XèÇ¿øÇ?¥ÐA‡©Šìll\Б y„w²önŠq/uÄ´©‹±+¸Lx|\5mÜ Õ×”Òâ| 9§aâJíœh[ª ^ü&‘-ã©YÏbý‚­ˆß¹[?_„Æ#Æá…{A‚É+‰fuOCÛR9¯壃 .k“¡o` :µ‰ .&¥â&r³S{.•G[j˜%•Søóâi©¯ÿRìYr %Ù»aÞB\‘Ä,ú¦eH‹Kc4=båˆNùÂЎZ#˜Ð‰Sh‘}¦P¡W„ÔøtN Äúiؼ|a¬)§ )ípÅiŠwÑ{ 4c%ÂŽBŠòGJ1±v"jB(v¯'|y  } ›p„“¥Œ‚×døhÂKR°¬¼M"y*›þ Ènb#K?ur‹ùº²ÕίªHðèåþª9¿òŠ#g Ú´DþI(%P”¾%šGaçG±c5Ñ1=[¢_÷\Ì[q{R.ÀÖ# žŽ‚ ":wIl¡R¸†›œÛú;>œ° F;Þ•øõ¤™f *YìrCÆëd°%== 6†TH2vEØ5*FÇâ@û·ïCh§Ë(øäK¬8©E§·‡s»T3P”Èeå%JУøãçPâÝÏ}ö Ê3ñmÔ”¥p]!Ę–æ ™iñÈÈfTUÃb$¥P)ç Sò\mªÅaA;p3k<úÎ3p6Ö")æÒ‹ŒàédÂÑ¥“ä¥1ÿ{éžrkw¡©©©bß(6ŽmÛ¶ÅÈ‘#•°¶ÜÂ¥§§#!!IIIHOK§MO&.ç\Fnn®b—ŸŸ¢ìbä–æ+N%rFv…Ê.ƒ«>ÝóP¶3\\’H6Prd,±ÃÍLÌàháH/XKXÛX+p)vövpuuUNüüüàBY¹§^K{Ï“úÞ± ÆŒGêýj‹£á²_z÷ô¯ ù\S¹Qš•ƒßfÎò3#}¢šØ¤Æ ÃÖOóX»)ƒÀ:]Ê€gº`Ó“ÛU2>œ(€É´žÛ¼>;úÌD±“_‘ð*9Ã`6„Ô(Z ÆÐ+¸ˆ•Ÿ,=÷»ÙñqÄ?‹ !à¡áÍ)@«·Ž­´5  óÿì]`Ueÿ~v·Ýuw'Ûˆ1:GI)’‚؉ègwðéßîù@ABºŒ.©Ñlccݽ»û~çî²`CPT„óÂÝ=ñæóž{Îs~ï/rOôÇfcÿ}CBÝÏ„C9ð¦uƒßác5°vñE÷»ûà›/æâësÈÙ³ûŽ[c\L+ôÕ >ú®ÕgPqêgY`üƒía^½>l)y5²sŽA «$‰yXÇÛc1}ÊȲöÄ1ŒÎÉcÒeÞô/ðå‹ß¢kŒö.\\‡0¼ÝùQ’_Þ')Ñ6$S8Xcçª5XiAc{#áÙŘô¾h¸5“WÀmÀ<ÿÆ@ÄF™cɳQ™…ã¿üÊH˜rß•NÕ=oE*þÛ-µ¥Èr C ¿ -CiP–«®ÝÂýÊd‘ “r~ìœ\P–‡Ÿ§­BoÚçYÒ_.Ÿâ¬…óG•Ç-êŠu-Hk<ÇÕ5Sy/¡¤ÔœyªIr;Þ7?.™Œï_›…VQÖˆŸºac†"ÐÝvÚ–ˆtÕ`yª)b:ûÂ>ˆ«v ¼”RàŽ®ÝjÛ4 "õÛy;Ò×òAÌŸ±^öT½8„_¾ÝÙÓˆ?X ›ÂdlذY¥æÔÅNÀ¬÷g#Ô¥‹7ä#ö™.` 'Åó„NkÁ#c±{õ|ýÖL´ µÀÆo—ôMW¼Ú! v^Ýë’¡ýëéï5Or›šL!òñóóS>Må8‘WÐú]¬¦EЧ,Kð­_’òf×TAõØ5…€¿HÝ'#aËt zï¨ëIOòššXu0× ²Dì?°?îÿ?º\Šh' NbÊãv¡‘9(£1¨ðRYÆ&¿ãrs8z£3‚¬é¾¬¢–$èàwÃ@Üÿ„–n±´ˆEÕ„–ŽÊ9qéÓ9Ç“4ðp2)!L|fÐW¿ñcp_¶\ ‡ ÷ü (¥™Wós7¼8[‹ÕT<¸½þÄæ¾ÛûÁ‰ê­n]‹@^c¢2¡Eßg@áŒÈNÌE¿'oÁ‹®X³dg˜£çîFßq½`G ÷ÏxK¦®Ã©ÝgyÇíhss!B#ˆ`•— kß@ÜöÊÝ(qð…“­Ö–Á¸ã¿wÀ:*ö”̇Daìãðc /Ÿ>¸÷áBœÍȆ‰}4Ç9> pÄ9³r ¸ Ô]vâ Hm2_f ¦4âù›H^9¦¶¸ñÞ¡ðli—ÈøïL3¬_¸ »rÐî¡ÐglزÍj8bЋ·Áé0CÞ0°‘™-n~ùg¸#ÜG”Iꨦ\­ÆŽÀˆl{äÑÏvŸG‡áá KL8÷ˆ(¼>wö-Ú„â3…hɹ÷Å $ɧpì`F¾ò(†ÜÕæ59Œ>z+9Æàv7á…™.X½p'î0AÌ=ãÐçÖX8Ðc„¬N\Ïé_ âzž4uì**×>B~¬¬*­[·îºz¡:pèz÷î‘Oǃï%Íü}¿"d5FÖe º˜uy ÇëöE¾g”êÉ–‘úsðe·¶))Ö˜[JR†[{¤q9Y47ä7œiØ®ôLú'T•´×ØØùoÃYiIÚ¨ ž#½‘ëH±¡ˆä’~ŠÞ±”•2²mÈo8.=bwå¸Ô+í×WöëÎp^j¯ŸÇ0H+įC]_—–sRΘ$§a¤ÌDÖ(ã–^K>É)u †ÚcR×Cî¦÷ mFf¬A°®?††ã4öÐPŸ1Ÿaî¤%J~Ù/Ù’1Ãñ7ä«Ã¨ñ¾±vù6¶ ý2n RÆp®-õaÐS¢,g WŠaÖ!Ù’~ÕÇTFiìWýv/u[Ú--*Å}žA› ¶˜7Þ¥½ªò]—’Ü«jÔΨ¨¨¨üÅéJãfš;.ùŒ´¨~£ÄÔ@dꟑ#B2šK2f<{a»Í—¬#M²U_2'½©£ŽÆº =1æ“–ŒÛ ó‹üÙ˜÷Göë§Æçåœô¥®×õën\º5CuåbV¿º<õûaÜnÜŸ‹í^+êJÖŸ×Æ=­ËU¿TÝÜ5sýþJɺ|†zïk—ïú=Œ©>ކ|’³n6êcY×ûúýjˆ©±Þëõ[^bÔ¤" " " " " " " " "pM! ’Ükj:ÕÁ¨¨¨¨\ydñÖê¶.¿•?SöÒ[3öôÒKü9ÿ,ÿ(›ïqógŒÈ6×[9~±sÆòê÷E@%¹9µœŠ€Š€Š€ŠÀu@éæçÑÿ— 2sQ^?xÀ%#ÀðÏÙy(-åæˆÍ%WÖDFÕÒ•ÑPšî ë/Ê7‘¹‰CEŸšhF9Dsó¨onT¤h._SÇ©G\^†¼Ì¢znÊšÊw%éQBÿûy*¥æ+ݬåfåÓ œhÆ6NÑm¡utæ1–—¹ãØu5ìÝŒJ_J‹Ø>¥ºE•0³Óœ>x._Ž) ’p¤ø·%¶®,_ƒ“Ûâðöý“Û ž%˜òÐjä~óîÞÉ;×cÊë³±mã)t}êUôêDï"u†i†^©/•ä^*Rj>ë *rÎaõôå8øÛ9x¶mI i©Bìdð3:Ž"i*‰§3»wac\=+‘ÁHœªó±bÒr¸wc’>=ƒhHÀŽnÞ‚M¿îGfv œƒƒÑ÷Îþˆ qEŠU8–ek].ìL•oú?4Q¾8}ø4ŠÊéÏ—ÍŸ'¯: üÛ†!ëø¤ÁcïˆÂ–stvÕDKó…‹%zZ]¢û)ʤ¸ÍX±wžþf}dNfU·K¼<åI„²Ì…Áе‹TìèêMøù‹EXòõ¯õ>KñËä•È,±ƒyÆa|9q6N2ø²•$rÞ2†•úòd,þ圭÷Í,LùNžÓÀÊ$?Lü »— }Û*¼?a²+máék‰ÝßLǤ·6 ¬°sŸù W“†{»àЪ8$ìNƒ¹eóKñ¦kŸ\ö+æLÛ¥HèH§ÎÆŠõ© ŽÒ3 #šmÿ|f|+/˜%ã«ñaÇáBF”ËÃò·&còÄ•`Ø3Tœ=†ÆOGrV9GváDÊSÎÀÊwæbëö³Ü6GEæüøâHJ)ÀÎçã—¥'P‘}“ü;NVÀÓÅñ³Wã\bLq^ó³±{Õ>äQš+A0†~ùîy¤É1å·J<FÕ+Cú¾Äô†#£²éLlкo;”î;‰ Jœ;=^x{(l±­Vr¯L„úç! Jrÿlj!k.¥Wg`ͪ´¼i,^øð.Ê1“»ïÒ+/”þ5¿øv Ø!$Js¶cxQØ6o+ ˜7º÷ Á¢O¨>@©iÖƒ½mÃáç …»e>>›”‚"Jx5”þºµj‹G&¿€H»XQ aKB2Ê4ƒqÇÇÏ`8ãb—Ò¥} ÷àæIJ¦u#£ªIb$Цðeh_­O0%¹MÈ·4¦è_?,|íüïÛxý­þµ4—•Ròkêˆ{ÞyƒZ9Bs|¶”„à1öÑ!g öÆ~ˆsÉyÈžý+ìz ƳS…/ÃÇÅGO?³ ã\±ÿ·4 Ÿ2kƒ¬ÄÍH;ø#‰6T‹ñHÿ¯Zkwô}´7Þ~y#’ß¾^ŧ°í×dÜøÝpÐìgæÒUë{<cŠð˜¨ÎöǾõß!ñHºõ¡6µ•%¿ñ8äßãD÷©È.¨D¸‡D¤k˜Äÿ¬sdG èmƒ K÷bô°H$mˆÃq³PT&Õi5ðíʱQ[ÕáÁ.Èäòúï'RN  îoÖíBú¹.ضùüÝ _;z 4OÑ"ÕUàìÑcXµ`—¹­Q’~5¶‘¤¡”øéLàÌp³.v \±¶æ0-d)êÞV“HéDõÓÈQIžô$§"$”€/KIK6–UJ,±”=G±mGuŸ@yQ *ý1lXkC_X™äÔët°uu‚«« Ûk`gmgû@XjJ¸Mµ”û’É•éÖ!öæ5(á9ǘg¢W‚Rªh8 4Ü™å‹`ïíWwT³Þæ’¨Dô蛵ض)í ¶ãŒMºvi“{”ž™“ò²Llÿ™ªÿ[Í0¶¥H+¬€¹‘äË„‰‰ B¢œ©ÓZJh X[‰¡UHšiÔ”¡x;ÞÒ K_Ù‚´üÁØ4w‚;uEX°=¶ÕöµšFcÖÞ²âHJàÞ"v.¶|Û`{f$¯Þ6Z¼[È?âwjÙ¹8MÍøÒ!JËLˬœì¶¬Š*ÄPŽ*ÄŒÙ%·šþ*Éýsø©¥UTTT®!„XÈ¿Òì"q«ªé¹¥ÐSª$JØ.–älô¸!°š2kç¬Çé#f¸ï«•3 ËjŽnZ™SŽâ¹ŸŸFûpìù~ŽÌ¢”Wc¨[_S¥PVÉ/´Ç„K÷ºÊ"¬~e:Ð=˜‘ãJ?4¶Ž÷ᓈö6'Ù%ѱ!ûH(Ö'$—AâÚKäá†çÿ’v±Èyó°xsùq"º¶v` åFî½HâôüÈ¢¾¢Q-Fm†1}y™Q#Ÿ¸•QBkG¢[Y@#;KKXqY&•(Ì-#²N(É#=Ì-†‹¨€4“„(:µl…vQØ3k ²r~CTŸ^&¡L¢ê*@_Н_ž »®cñÌ»7À4{?Nî™FÃ9é— ÇÀ¶4T¶‰‰aKÙmâäÖ UlW:lÄÊ™kð[rº¾ÜŸä™õ°°2õ|A¨®.Aq^4^6(ÊÌ£!\¹áÅ’øŠbÖbËâ./ÂZ íð31µ†«{ RSPQÕ‘ê•H§®u¥O\¬EE#—¾²P|S2ÏËP©¯‰N«‡~•äþ@êië 3:Z#ná2 ³3;±eo*\zvW@Ћh®–ï6…ŠÈðÜÃÚ ç@kÌýð'¸ö‚˜W%á]$?º2´îîð¦>nyv"Ö.9ÈcÈíâ wV~·íúáÈ/q8u²Á$ÅÆ:/Ä’u{ ¸·#ž{p!ŽSâ}ï{Á†bƒ ”mV‘h›ÚÀ­…7œ¬«±äç-ÈÉ+DÁ¹l—‰ 5¥c†ª¥!Có ²^J¬ƒÃÑ©»æ¿û¬ü#Ñgˆ?Im±Vôj©§êsX>s|îl5?lDn6=N˜Ñð,/?}±Ýþ3 ÁÎÕ8±?žRh„W†Œ#IHðÒÁ3ÄmÇõƇŸüŠÍÂd•E3v#z̃p¶¨A²ÞÒBäå—¢8% ûÂÚÅ>âÝÃ8ò ÑR4€Jr›ÆE=ª" " " "pÝ!@š£±ÇÐ'†áøKóðùïÀ–@îáðmA]I=·Càà@·XÍ&* X8¡Ó±ØrhúïLò"Ê”aúCãf‰À¶ÐÚi¦<ü¬é:Ì¿O,L6ÀÏŸ,D´½/üBE/Sae°ôò—¥£Ð5¸øzÁ¥‰vE·sË´¥X™@RE¢kM?þ÷[„ Œ{íGbh ¹ò·išd†· SÚ¬b] 髵ÇïÍ%±µõò¥ç{æ'áÖ˜ÃÄÍÂÒíï¼ ½6}F½Þ/°ˆKòUUÎÔã ª-Ü|×Ìøq=Þº{,yίMœ-6ÕhOzÛíZÄ!Ó‰/Ý<nfãŸ0WX¸{a`lV-ý ¯m³…S‹@ чçÌÃ6¡ðk@|E”ÍQ[ÚÀ3ÊÖÖ‚¤|šb»Bs­Ñol7ÄmÏCä˜Að±¥qUDœ<áâf×Ví0Œsºü§Ùx}ƒ¥àÕŽe½tã–›‰­‹¶#ì.zgЧà§÷@~~%ÌýplÙ2$¯w¨—Æ£Ç]c°w맘?q’"ÙׄwÆö„¶*«¦ÌÃñô‚aišÄ]˜:ñ Ú ‡»w®Gï¥î6‹_Ð.ö^Ól9õ„ЀЀЀŠÀ_ˆ€ÜšL—O%X·n´Zú ºNÒCлwoŒ|r8œx/匴¶úµL‘›šŠÔ¤<ØûyÂÙ–tæpq·D^R.´®v0å’ù…¦AÁyóq…޾lí|œaEWeºŠrdŸ+„ Âè`·‰öäå„nÂh\†jJ^óaù³§N´äÂôTšSÕ€sºJKÜŸŒÉ?u‹‹JY¯ ý—#ƒ’tG?7Xj*Ic¼*йeNä÷,6.ž®°#NÕå…H>’¦\W~~p¤?`¾9)œoöÏTŒ¥ õ$lœáìjÍÙozþP®ð™RŽë‰>ÏP*ßóæÏ»Â-ü=Õ©$÷ïÁYmEE@E@Eà²PHî!(..Æz•ä^vW"³]q£e”~ Á[xzh¥ßؽ˜3u§Açµ~c4&Òú†`ä}áXkäd(o 'R£,9‹‰Xýú $нK䍯¶¤j‰xU¿~sõ·%ŸâúIH‘ôùÏ&1–“^ ª÷IL©Ä|Ê0.É+‘½„h ±—±’xÜ•œ†zD·×´&˦,Ç©¤¢#”ü”G†nmX·P.EÃV9eìx³ÞÐ4çk”¶$¯ø+–Þ’Òú&tê¥Á©ø X¾ø8_F¤‡õɤsû.1¦ ËËüçÞPºá<Û4ôIzghÇж'.ZQú%ó_‡“Ô!³$í4]F°5Ž¢^ÿÒÍk…äªê ée¢V~-#PZJ)JÙjï ×òPÕ±ýCT3“†Ä);'f¦×æíZ,ýìí¯:Iµù×8É1 —¾½‚ô{AÒ›ÂÆZ Ó.$ê ûÓ¸=J†›DÂÐO-¥¢ÞÁÔ«mÌ>9w¶Î¶Êay90èº^5sã6ùê—kˆ‹1‡ñ»iœ.^ÆXVý¾t®Í»æ¥_Í©"ð‡˜=g.æÎ_¨,O6¾_þáJÕ‚*õÈKNÅ€òrsýí“ÐQ¡ûÐ$–MßA…J6MX­ËÇR¨¸wT FGuln¡¨•õ{ÃLkïbçÖ¢îý½¨$÷ïÅ[míA †ÒµŸ©£´yÓ6„µëNkçæ]á\#CV‡ñ7# £ }[Lüm+>Ù³sZ´ç2jÓdàoîÚi΄ã+//a¨Ö8DÒ­ÕÕLrõ ¡,™S-Z_N}N­M„†ý=`j*ªˆKëɓװ¤¯W„ª¼N”[RÓTû÷úùwœ× =±¨4»|}kÅ­Ý”i,+hü~ÏEB>—›”k€×¬Fb*7H”ÉVÐ?2•‡Mj]À58-žÓ ¸vkp‚Ç9WÊ 5˜+Ö/ÅgÛS\‘5óÊÔ :u§Iþ’ŸZ“-©U®5è“Ò7¬5xg6{3(¥RjR¸"ð9WCƒ•3'u8<ë <}t‚&ü¹-ZA#>B¯dJ²™œˆ¾£¹ZÇ$š“$n߉ô Gtꈽ¿l†s·Î °ç¢ø¥*­Æ‘•ñ@H$ZµUa£FÂÔÜøå|sç^&t§U”—‹s©yÔ¦‘œ¯'œœ­›!^ ËÊž´bìÍ…gëiܧÆû’×XSã¾Ëqêê˰sÝN˜¶BL¤3‘hœ¯~{ ·Eã7ÿÌ ìÛ™…Ž£;Á†ÑPº©~Ë^ìœ1OsßÔÆÕW aóVäX‡¢[W?f4èûÊXôe¹Ø´üÂzu‚ŸÇTÃYÏ/BfN<\aKO² A,Je®Îd¡²ÆŒnÝÃ…*²ZS^…œsÈÎ,9#¶¹ÓðÑÎñú1:mߨQIîQ­âúE@C‡ä´6ã[¼Êq¯ßëàJœ—Ò“Y…_Äg'× Ã’oÿü$ÔÐÜäR¹Õ•îØ¬OTŒµüíˆD÷êK$¥ÄX$†¦ô&p`æl¬:‰èØÑXô̈üòS„Ñoi“$—Fa]õ¥z¬KcR…uoL†nÌãhÝÊ—C–à¤g"±KzE(I ļLxœ^‹,‰Ö $!Ë&1æ:³}¦¾¾YtZ@_t=æ‹ñ™ÖA¶Ø±ðg|?yó`®:GwÓ† Y³š.•ä^&`jvÆH,z!¸ª$·12êþB€ÏOÚ›aÅôt”ï7…ã°ûh7éuœê7©ðbûC5_U…„ÉoçêKœ€Ê*¤ŸN‚¸ør¢oÚ*3S†ŠåËI”¹•9™§‰$G‹²2™YßH_0:°°/¤M…–‘ºÌ)É3BK TNIÞ™é(*f˜XÒ­]å§¥Ñe˜æº2¤Ÿ¡»2'g†ûIà0TTD)\îü%`Bã-KÇ*,yó>ëŠg¦M€Cá¼9üK¬îÖáOQŠNØä’©a CÔ2îœOBǪ÷å,äxvÅmwu‚¹âKÖx‘iPY˜Í۵®!.Ù0cŸh0–“–‚¬ì2¸†øÁ×Ï™õèéò, ÉÇÏ2š—)¼ÝáÇð½Z+zT ;ªä£‰È%¯óõ£Ÿs-´Äµ¹TSU†”Cé° b Gž©$7Ùé:Tò IK¿¼·ž:ëçΜEƹ|ÆÚ°„wK¸3ü°®¼i§ó„™§2Pʰ¹>‘ps±fMƱ)ÕÖþÑ "7)iåð ÷e”6™§ä$ŸAq b'܇Ž,Iö¼<œ>|†Á‚­àîPƾp¾xMTWâÔ¡,¸GÒ%˜e5V¼<ŽàìálŽ»ŠóÀ뇄~Û쟰".Ï|ûBmóðÅ=bæ·­ñæ3áøáƒù(‰Å3oŒBêêexÿÕyXS,îì…r•æÖŸ°KÚVIî%Á¤fRPPø{ š"nö®4Á õ 7Áa×{°l6ÚM{ ‘QiGB!R@5]qÄuTÒ–8|øü<”ê´p¤¿Ú‚c‰°iÛN¸éE“—ÒÓÖÇã‹·c¢wãc‰òœãøðþIèúøÝÔ!5Wtrùö‚_þ;÷å¨Ñ ƒNƒ.Þì…ƒÓç`ÞªT¸zY!ël> Š5èÿúx`¤~6 '2hø&1”é§ÒC©%n~óVtšp'ºx‡ S—¾¹!&ìGT” ¹•N‹k*’ö@Rb!LÅùl½dJ±¬Žj0«Þš+SŒCbjhBÔ5òvâõ§—ÀÝË%gó_iŽíÃQ•”„Œ qß<ûëðª8†ÉNÁñärªpIZôjÆŒ ÅæÉßcÆ÷»HômaÏ 'ä ×ðú"ÓºIokÊò0û¾wáúÄÃxèžÎD¶;gÏü_5¸¼j’sËã+—ã‹÷Ö’¦“bWVÃ&8OM~.I˜z×W0 v%ñ.¡_Ý"Øtî7?gƒ´·®EÞj8Ï{ðÖ6à¡/"6Ú‘Çrðó“o¡ Óø¥®Ä1×›ñú;`ñK_aY| ¬í)ù®a¤:ŽWk޼3 øìþ9¸ç§×Ð9ÌA=`¹á£¾ŸÔ^;$Â5ÅØÿS<ÚßûÜ0 ô,Œ1OnÇ[ŸíAΓíqË+ÿGמˆôu`Ô´ø~ú&””ò­÷÷.¾úƒQ·Ï#Ðôvþ´º¡" " " "ðw!ÀU`êT¿LÂ:ýFQnT©G¹³+v?ü&\NFÄ/Ó”åÓ¿«O×W;¤W5…XøÁäZ†ã­åá¥÷F¼’ää^*è\ ÃP•‘„ sö“˜iqház$¦• e‡@˜UÓðŒœ³˜.áŠr5è÷äݘ8ýYôбÂì÷Ö#‡KB5%YHLÌCχÁGK_C„s1¶ÏÚŠR½NŽpsv‚‹‹œùqrv„½— ¥%ºì‹Ž]üQʨ[g-À¾r´éLºH/N¢PÝR–8›xI ?gÏ¢Ú-cž¾¿Í˜Ù 3¿‘S™AWŽÄˆq >øåÿ¢=‡½qËg¯áõÉãq0{Ë®ißcûQk<4åM|¼üUôm¯ÇÌw–àôÑX8e¼n¾/{·ÝÓD¼´V-ãÂ+LÜkYÙº#ª+Ö}‡G¨È?‹ ß®‡wŸÖp¡”•2iö±i{ÒáÕ>O{¼Ò§WíÄæÍ©”¼› ãhô]ñÚÂ÷ðØ‹½‘8sNŸ+å,]˜DBëÙ >ʼnX½ôQ3ǹ=Û°:®уP”ž…¬<¾Ø¿?ÿ¼]ŸŸ/y7ô÷‡¾´Œ*5tAæ‡Áã‡ÀÛÅŠýÓ¢ó}}ä÷Qa¨Múšræ[ Ô4|ñ^×@?X¥%£@ë„Þ· D˜¯ƒBœÄÂð…6bD¥C}©5bx9߯«ørʨyUTTT®0BBd)zÃ|êáf›àöõ IJrE!ކŸÔÎqªÿ(´žÿ5Rº Fnh›k^š+„ðïM¤?ÕÅ8SR‰ÖCû#ˆR\x¶G—¶¾Ø£û}‹|ñŒkŽ^]]±~ÍVäOh¸åûáÖ±¢BíOuñÌ¢utFôÀvH.Tà¶r0aÎj”“Ä®±¿ÌÞ‰ØGoGl_’-C¿…Ú¸yÂ+ý X+¤_ÚšR ¼)&–~Ö^T±¬BÐ¥_4%ÌæÔuCqp4ZGùÀ4?vnÐWT"eo*Z‡˜öŒ<Æv;ßÚÓ§®ArÒY”Z঱HÌíÑ~Pw„†-@Ûi:qŒktÑ?Íš½‡ó•¿ûÏÚaâÈh˜8ËbB¾5ðƒŒ¸“ˆŸ½†´´€Ã`„2zÃ]\3+'ô›ÐÎ “oÛU,Ó4YTˆµƒzßÙ_ÿ²¹ÏÅb÷ü°ˆŽF‡¾8CÐÌÍ)í=› [oôÞ6öfˆÞ³¦ï¥$¼ÖÎ>ö° ©hAK;ÕCDE§ZQG¢n…2tÑï5§ØœÄ]Ô¸e¾rOœÄâ/fa}|F¾þ8#ŽÙp~š´5Û•?ªü›†ìÊ7öÔ¨’Ü¿TµJËE@ yŽîv®0A÷á@hk’^#¯ Ÿ¹9ÑhÉëJ´™ý)â_œ™•ÁÞçr»Êó+†Wäš¶!£û;:Ni.Ù†ÿŸí&Æ¡\ˆàïµ/äÐ Ýïê…E¶`_Ü>û­=?…5Õ¤¸urÓÄcÚk ÑzT,ƒÝQ’D9%¢c*¢^SzG0Hùdü¤§ñë«K±ýÇÅ8]ƾø‘ᜅ-ú<4>úl¬š2 æž@ìC·áŽGn`È\cøÑ¥ùl&Î9«ls÷|’vÓ)Ý·'ž~ãîŠfûõÌÕxýI€ B¢7é•Fñƒf íŠQ‚tý6ã…,RQéw5ßД¼,+c0H4yŽ>µôTg¥æ’´îÝ¡-l°mþnçí€S‡vˆ ^9ÇX«¿Uäáç·¦ãX¡?zÞ 'Sj­jxÎ’í‰ß.Ò^sê~ òz¾\Ô®‰fù3a”µzÀîë騱u?¶nË@ÌÍ£áÉ— ¯+Itª$(¹•þ×È…Â1+n¸_¥—’¼ò¯v²”mÓh¬HôkK½^©Aœ‘•ÈÎæ4úÓš ïÄqÌ}yve›bÔÛcØ0–º|7k,ô§’ôWþ y7iFýO5ð7VIîß´ÚŒŠ€Š€Š@s·)*Vo ï-MÊÚçªRLC ñ¼ÐHA+ì?Å©Æ!¥ëÅb½¹zÿ­ÇííìáêäŠÔ“©(É/¡äSKB téoH$jö|¡HÜvéô¨`^v;dAfxè Yjžš µÓÿSW´õZ‹“~Ažszv `ïI~¸l-d(óTr´ýÌXøQGsεЕq;ËÞØÕ0Ÿ± Å8C7¡hÒšRPû2J}Ù%I& ¸UaûÿæàÛÏ·bèÄÇpã°H¦žE‰-Äk“÷¥6à f˜±°¡ «ÄÚO&Ã3v0Æ>Ò–¦B0ÈK/FùH’oî*à!ñ² §ÚÉß'wÄéÓ]àG]ÕÓÛÃ2ÄnŒ§Õ–Q×ü$ºqù>mÿœ;™¯‹žIMí|ÐgP$¾›·Iåhÿ츑Sfò·PCIhMQ.-AÛãžûbp8~Ìj*éž«åbdWCªÌ¾;ú{s'ÄÚ+º5Â;ØbÉ{óPXmãdÅ„*œw¥À–66¨.ÏàX’ÐÞ‡6@Qf1ß^(M/ÏGÕ'|Û…ÂÞB‡êT×TåÒ¯šzÁyÈ<“ K7+vÀ’u»pjt;¸iб{Í!xôí Wz˜;qVÆçS/øt‰pBFòYXØÙ)zÌÆ‘Ôè/ûR.§÷\R& r ð—µõWW¬’Ü¿aµ~ßC€¼cë2$îz•º\5oRxcŠ„á"`Ó´žý1²¢: ÂÖéš“æúúøâÖ±·bæœÿaÉ÷KуËÛδÞ7³4£„Q(›üû+‰‘™#zÓ hÊw›ñÓ·°ÈOF^5—ÅIN(‡ƒµ“,Ë{EÐÕ¨†¾Y¹ù¢ëˆVøfÒtùÏxúOµ$}ª¤¡—ÍéØÕŇVþ& X3óWXWdáð=¼´ùغvìµvpp1xQšÙÚÃÆ‰k”Úw?]µHvE‰æLýyÌ=ÜP“w˦S\YyvîŒÃÚ*ª‚—®™EoJ%[Ý: í]=éoÃ…qJqÍ-H²èî­Ö•™–/!¶<¦$JTíxΔ’ÙNwߌÅ÷ÎÃ/_,@#çî^x='܈PÄôm‰u?,Æ‚ŠDdîÛ*kGØØÐõ"ç³1í6Q$Ñ·ô„vƧ(´öGŸ›#˜Ÿ&fÒW+˜Ú8¢e +’wÆãgëS8²û¼ýpš>mO·‚½»U µëMµ°u³‡¹âFLÚl|I>2;+ …÷ÞÝ ïT µ£³|X;ÚÀÂÒnQ-挵“桺Ÿ?Ö½#¯ 3º ;†ÿ½ð n›õÚ¹äbÍ÷PœŸ‡J-plíFüœäc¡ûƒ·`ýßâ§ÏæÃâ;ô3©;*“OaûŽ8†Q˜º¿[èpŒ/¡ú ß°Î_Ù1ât%¾³ í•äÒÆÉ3øåËEp³wÃèÑ£¯DÿH¦ÿÇô´¬6ª"ð/F@tÔ~üñä•êÐõ¦»¸´) y—xmî†.Ò<%¿/Räm*Ûù:.RVN5—O©Wênªò‹”ûæÔÓA@.3'Ÿ?7ADW=ÜV;·ŸÁ‚?UÚÚ¡ÊÎ ­æ}R7Ýö<ÞÌ„]¤Ýú”†âÈ’‚4±7Å ¬}B\¨;iƒÀN-à@O âUt<Ϩ^!RN*G7ºÐòDÏ›ºÂÕþT…TÑ@Ì·]Â[…ÂñÙ”ôY{aÔ£7!ØÛôæß(†yR³]m¹µ ¤‹17þæEÂÛ🠦I£•=Z´ò‡õFµVÐZXP²êƒÀ`7ñUP‹l3_üÁ[QZhnÚ”¼Ü@­]½ÑÖV\R¯áøØ-”€nþþð ¦Þ3ÿ]É$3+^%äyVEWvÙ…HLHBÜ‚X:ijòõ˜8q"úôé£àr%Ûþ»ê2áà®,jWÏÕvTþAÄxdÈàA8•U§'¯a0F²ùÝŸ’•%ù”YÂÂB¤BuIÏ%¶ò’bÞÐì¡«(†©…=-¦/°©- CyqL¨‹gÁv©¦ŠKtŒ–cií ¸Ö1g›e…|èZ+Æ B^åX^ZÈç­Û¨Gδ¼(‡Æ |;к™R'aUL¢#Y]QH‰:ÖâÀßP»ú÷Ï! 8VTs>ÃÛšàáAâ@·PØèùà7­(B¿‰wÁ1ù8V|¶…>!ÿ:#4SJÔ2’OãýûzàáîÄ{x•|è/\¸ó~š‡£'Â3ÜaBØ*~¾ŒeË%|^Û”¢t% ò(ÃUûÇ)è\ ¡«¡OVù%(ÈMQ‘†};Î\ð6(QÌÌÝЪc(¬(A4üLdaßø˜5“oæ&!–€2ò+”ú ùŒchXæâ£²"TÌX¿lÿYNGÃ^5·Çè`ì‘#Ù—†=ñï`%e‘µÇ¥v“šRÝ~9ôZ èa×kRH©O»(’L+e\rª1’2B!i5¼wÉØÍÄÚŒÉ ñ”>Ê Ñ7`”—H‰oBö…í1Ÿo ¢Úx°¯†õ©«~i™3y]ó:¥MÑRi´aö8"åŸ!—¡=9fL†9‘¿rÕ¯Cô˜‰YmyÉo,UWÆXËå‘Z•«Œ×")-,£7ŽR⟃¤ƒÉ8}0'w¦¡«=»÷Ľ÷݋֭ið/NªºÂ¿xòÔ®ÿ›à Ϭ ‰›æ!ÓÄÞ D”$­üÔÝØ±n?Ú¼ )Û¾ÝmŠAŠQîüHÉŠ*³¿t),ú kïn0%‘ùGã±m;ÃKv”üˆnŸ©iެ[@ ñžˆéF×F$ÙiG¶ãÄo¿Á§Çˆõ1Hž4:dߎ[âPTDH{DôŠ P.—éP’•„cÛW!«Ä ½F‡åñÛÏ÷Pݸ 䙼?Ø·Áá.gˆH˜.^ PÕÖvØ׳ðÌHº›Š]¿ÁBu/<¯áßsV«ÕâÖ[oEŸÞ}°uëV,]²{ìÇ–ùÛiÑo Ÿ0„wl7W8º;ÂÉ‰ËÆŒš`jAJÅ¥eS¾ÄÉÏÌHë¨ßÅ1Â!eÄàF¶ëO‰ÔWšŠÍ 7 äë*#qІ#¤m¬iäo$Hu ”ÅØ™©ßh_hÈgÌc,Õxßx¼þ·Vë­æJm7ìEs{Bo IÆfÜÌÎoŸ?nJýÙrêÑî±£|içñ†I‹ŽÎö´¦‚Â…’hc”zÙÀ…3%9êf^°êšxº°=Îsw3D´öæ=UgÃ$kS®¥M!Ôõg¯~›usÒ°&Ù«½ªÎ×aD­®¼±­ Ë^üˆØI2I-UQª(üÐÑ [5ƒœ"/3yÙùH:œ„ÄýIÔ΂¾DwêI<ƒ‡ F›6mj}_¼½«ý¬Jr¯öRûwÍ ’ŠÂck±$îüÛõB°¥¶4 01©À‘ecõ:Dõê‹Þ†¯c, ¨»" ©€©Y5Nnœ†%3~€•ï!„D·‡ É ù'WbÉ×_¡Åé<ðÊ+p`Üs —÷Ìýú.®èÜ»Îlþ+fI‹ïßÐËÇIäÎ]¿Nz é¾hÅåÒĸÉ8xø8&¼ñ)l+Rèžç]lgô,“~èxó0Z‹ôäÞ†ëèúÝ5…œ `Í&ˆºpµþRÕð¹šÑº Ž»-‰3=oFztwˆqÚµ˜<<=0bä ½y(8€øÍñ8xð Žî;ŠÃk`Ÿ€³Ÿ<ƒ<©ïêBÂë@âë7'XZYsÙ—VöüXX[¤¾Âºø{bU'A¬“w0lˆ¥qO¨‹cH[<ü'­qb& ]XYY+ФTÒššF€2ES yøvÜ xÑh”‹*8Z[kRÈúòéFy.sW¨d`÷Þx4º‡‘ÖÕÀ¹3¥:¹²zõÏÏ\Ã+§þ^u—ÕCãGX¹®šÑàÊ*• •eU ¹Í/,@vZrÏ1ˆGf23è'9“Ü2ØXÙÀß×7ÅvAtL4U&zÃÕÕµ“k`K%¹×À$ªCø7 À›uµ¢F<ûec¸Ô¹A#{ó!KÉlî!ìX¹‘7~oO7æãBV­š@ý‘I^}Q Sâà; •‡ã°gÛ~øÞ̨’ô°s @æžyؼºnÎúù7¥4JGÉ–×Ó“öÒqúXhÊÓ¹lh˜R*{nßÏH8šŽ±ÎC·ŽAHkçƒ_þÇŽ¿„6viÔ¯³Fç¡£°veuÛDf¢¦?ƒ€<²›–˜ðáÜÿ˜v†c—T¯¼`%'ÐÓBÀF¡ýø1²[´A5u#EÒ{­&‰¢£|ÊÊÊššŠ””ìÙ½‡Æ™)8AŸ©•tãDïQt< …½ ¹<àäí O†˜µw±Wt-¬-H€iXFâkiÉoªþ˜ó#++òÒ¨_…×V¶jù†9õR›K•EåÍR_€€u/8¨З“°]q(éïÖ¬™¹ã=±¢Ð(UmºOéQþt [ûVä¢Z`$³Ü®â£²ª‚Þ#*2+QíÊù©$VÅÔEÎNÍFVJ Hh å­´ T‘âšéÍHjmáíí…þ]ÛÒ—q "##áááOOÏ¿tXÿdå*Éý'ÑWÛ¾®àªìü» C¯l]ºý÷‚3ýŸÚ2gJíqgl,¥eÍb"îÓ®Eb’·<û"ÎÎ8‰½¿ÎBßAí)µe\£ªrø·½þ鈛õÂcZ!*„Ok%Q7—íGÞøÚÓ¸dþž¯ÏKbÅ?köé#°± ¡tªhÕïÞ®ÜM?Rt&-èò–Wb½ëk¬_–ÙlÿÔ—Ž€¸=¾WøE&è6Lˆ˜ßWSh\» ¥¶…~¡8<æQtüúUøoY†ÓÆÊºðu‘¬¬¬Ð¢ øéÛ·/ÕUüæ§Ša‹ËKË º´…´Á(.ç‡$—ßUeŒÑÆU ZsõÂÙÙþÁð"©õòò‚ŸŸÂiŒ¤¨!Ô­`ü#ýûÚTIî¥bÍ OÇ O¬èk_ä/µäEó‰¾c —eÄRøÏ¤E*G|‘Bü‹’<˜DÚx¹ý–rb°AAË¿&ÉÛ¸ ½Úô»q¯~€ Ï£_{;l[³ öÁ7¡e¸t§›Ou ö¯žJ¯.ðw·Cç®XÿÅBœ:šö¼ù¶Ïk‰n†ºßýŽm†Õ?ÎAà‹w*ÒbE:@6këàF…Âtæ­cB‚ao¦S7:ªgórÊÌ™Öå”j””@cc ËÑ£šþ44šFi°rÝQ1 VïQŠP–³Ë¯šªƒ8>è6Ò¥XúÎ=Ý ¥.^לK±KAÆ”÷fYjåË"ø_R5¯Ùüü|ê™S¢URŠü‚|…ø ùÍÎÊF~^> P\XŒÒs4À)Íåo±ÃÄ¿*ï÷ Ùà‚?]…Ü*µ&VY ­©Uþõ_ „„*ÿø-׶ؙӳBb­­áɨkvþvptt„+=2ˆ4ÖÇÇGù È  ­­-ìíé~Ž~}¯ç¤’\ξX¼gS¤/70Iò-µ6ôhO áQy)Çq09í:ÃÍöÊÁ–r¡Ë,Љ‘\è2ð%’›“»·"ׯèÞ¤!]ÖÓ™s6ʹîêêL…ú¦š¨¡rºCÉÉ_aKŸnî®Ð6™o˜Õ(ÈËE^~!óÓ”n}ìéæÅ™þ#Í›)ÓT«ÐbçútF»–~žþß#¼ºòlF¢9 ߈6 »éÔdÕWëA!2>í’¤~‚}k–£µs8N9‡ÏÞA¿“$›ç;Þp¢Ä¦8e'vÆí‚}¸7vÍ}:KÏaÏæõhÓþ–¤ÐV^­0ø¡{0õ¯°#¶5Ö½ È®!ÆÊE}¾-#ó’˜ê²Ò-+0ºRq)àÄåay¦K9ã Xz&9¦¦ËG@n3»Ö®z€±ÏPjN¢œ¶?”D5¡ÂÁûoý_‡ð_¿Ã¾{^àäüÑ›ÉêÆU[HÔ„ø^LÏP!®œQ(,,T>e¥eôTRNÏ\æGÈ®¨÷È˵zá_µÓýïïo¬r/¯ ¾„ÔZZÐÕiêW!°’O!Âÿ&IÏ?0Cê 'í]¯ìR.$ƒ@Ut5ðŒ@ßÁáç†Ã‹¦aü”Mødîj iíxŦjÓÌ1™¢g7 6ŽBþ@Òã»—îG|è£X3ùqX5`¹UXóÓt*wÇßGÒÒ¸~êiž:ˆ_/ÂîߨËVm'Ÿ Ü4rzw ‡—é$ºº:¶V­\„S)¨P §Lá‰ØA7¡OûÍ’ãõÈNIÞã¿ðï1Ñÿw²RO"»Ú‘Aîd­ ìì²´«&ktèÚÒå"y÷2|þå 8ž†ŒÌLddg0†|<>`xÄçßû²F±ª"i©É¨¬`x¾Âúq#Š˜W$Š’*ÊJKÜr(e.#ö’tô{:1ùEÔE*+â\d#D®ÚXHÉuõþÑ×hТÿÝp(?ˆõ —£í€[àåÌGʘÅÛ­îO1æýžxß½ Ç÷mAò±ýعa3üº<ˆÛ_úã&~‹Û^ž„Ñã_„uÑ6 SÖ¼ML„³*­zßÿ2,sãDZßúéË‘† i¬ëøþí vgR/t ¯ùb£ ‚­•;—ÌFâ‘ÝØºàè½B梌8¾‡}8}š YHú-'–ó5äÿË.™Wº}ÅÚÙ¼®)%¿áv=hÜlx‘ù­Š4WOCšý·>®8¼œ?fü݈À^M***×3Èõ®G04bbé''¾‡îaT’ãá,/3Þ»/L™Œ}OާË$º ¡sþ¤#[°h*Ò2 20ƒn‚ê¤8l8Xá·‡»¬93gÇÜÅ[оß`´p2Áúµk”–Mb@gúv.hß¹/:¶ñ£5¼¼EŒŠªË‹ppçlÛ%T"·gûn±}êÊÂLldǒΡŒ$ÑÜÊ{Dç6þ2›>XͨO) •#Ù𸠳‰‡^ENvMÁ[Âçノ\Ù£…9F=6ûÏä"ŠQZê§ŒÃë°x=]K½þÞîf#sTRmeëwð劓8uú(ö­=‚®}ѵeU‚mËæàD™F»Ž„:'é0~ÛN 0ˆxXàØ–•˜¿p’uXnŒ…¾0[Ö®ÇIº>©æZ¶CŸØ®° Xf¦ÈK?Že‹R˜zKGºÈŒ®­ý©kÔïýձ͕OØûtB¯A¿#í{ˆÁ¡’—x†D#5a6%1J’ˆN9^»€.0µõG÷c!Á4«y\ÇÀ Na]ЦÏ`ä:MKxp ‹EDEÛ%ü ¸ç!¬]ºNNô‘›‡C‹§"…:)•Vá¨<‡¸´mh;öMt‹é!ãîå5¶KO¬FUizßöÝËØàLü&Ä-Ú]e!¼ƒÌ±wÙTZ¤w†wËH˜[¢å\È^ݽazÂnà·8ô¿S€œG’Þ+‘D777, ·L@»éï ¹Çü ¡þJÔ®Ö¡" " "ðïD@%¹ÆyãÒ«-ÃeŠ®‹$[kt§ÏRLšü’*Øð ¥//ÄOÓ> ¾*Es©}ã¦$Vÿ€ÞºUT˜“öx°O0K×`÷’©xø‰1õ×ÈÞüzùG´éÜ îÚJìݹÑ'ðÓ¼7±zÒsxe~ëK—PËðÔ£ÿEŽ/"|ì°kó>´ò¦}ýή…ÿ<þ9BcÐ&ÀÛã7ãûEû1÷§î¬t¹Ù?Š€°™³¦vxîí¸DOkŠ•*áLGóæ–Zêï6ÁŠÍÌáncœ¬Ó8r$ît?â`g‹A÷ÑjT õJÓqíÃïþÉøù­{Q‘{o?6ëJb±{?ÜlÝ+fáÉWcæê ¼Â¸î¡C^Ƹ4Å…U¹Ï~,]„^Ý"ðëä7ðÉw›º±Ì OâÀäéxüƒßÎŒ.ª±úçàì7W¶nŽƒçÚT¬˜÷\ZñªNœ½™ÚÝò:‚ûWÀÕß™:™”Æ Þnˆ½ïc”Ñi÷ùDòkF×/ú>}áÌ(Oç—·ItkÌÝpÃ}o¢¼Ú6ôÍÙ7ŒœXˆ ÏUט¡Õ g~s4,ƒ+@‹öÔ×lÅe1Ú‘¯øéµõö`^-¢o~>­û!3#öÞ‘ð ð' ÜÂbÐ= +‘Ÿ¨¬Ó1v¼)ý2š…Qý|_Õ& úÝùÔ88í¢nn2ÛŸ8Ⱦ@|BÖÎG›9Ÿ"£M'Tڹҥ؟¨V-ª" " "ð/F@%¹Êä‘\0ÎtZz*’̹lÌÝJÆ¥^òëVØ8ÂËÅRQð.eÔïN·àãî„Me éÑËÖîÅcÿw+"Ýg`æì¥¸#ö Xêò°ñÖ½#bѯ]8f}·%Î]ðÚ[o#„!(÷¯™‡Ù«O0z哌3nFeJÓò|ü<õìÎ÷Ä/³¾C{?k,üøÿðíÆ8ÅXßy\*ï<Ͼò´ôÐbùï`ü Sâ%„wm(m½œëQkçŽÞ}*EôUeH>º_|<!½nA{J'ï¶qÿà_0kág8¾c!bºÄ¢uˇF¡Më°smá7´Äç+v£à{‘—ßJijš‡Í[Ž£pvïe´¢°Á”š;‘\1"Knzø)¬Y>IÁ7¯ŽD9¥ãö=Âü 3^œÄ+O?ƒ·ãöv|p— Æ. ï~ý¤›S_½/OY”¢WIr ÒôÆ}¿zöINID­)Å·q!×$ÑT^DDjkj×À(EeAv•Äë‘ÿ•?¢Ó+ÄRÙ—c\ªvô •-ᵰ㠉Ǯdàs+ø´l#ï]Äðå¶®­[xµ¨7Hd5=W4Ü[t‚gKcÿ$¶»ÔiåìWÿóeŒå•v”–Õ?¿‡€À½mpö¤ Æ¿§‡\ˆ·©÷ÈÆuÉ\J2á|•ò|ÿmO¡ç;#xý"v?‹ž¿Z.¥5ЀЀŠÀ5ƒ€Jre*E]¡ò~ ÉŒw´Tìiò3±}o†Ýûolq€Êt¦6Ž5ævº‘™À;ûcoq%‰A;ÜÛŸ­f|õÌñ)Þõ‡’Ñküëðs¶‚7—Ÿ­âOàÓ§£CÛ¶ˆhÑo¼›Q­Ì±ŽäBÌÙKèþÐŽh}ÃW¸!:H¹Àn}üE´‘P†¯¬ù îô8„S{Öá0>§œ)bOú|§¦W USJ}`ó|þþ»8`î‹w_y„„\{AÍæö¡xîÓà3w öî?ˆƒÛÖb ¨*à€^C†âùçŸF羃PúÓ2É-GêšuðŽî„6&…ØË(EÅÃ)…N@·û_+%®äWJ²¡G++Jhµ6pv ûô>¤”Øáõ[‡ÂË¢j·˜øÞ—(Ô8Á¢fÊ+,1üö[Ð)Âàĺ#£µXëV(Ëøtú*= ~…`6L\ÿ˜Lq¥€FbZÿœaûb,èbç×Ô\ÞæŽ7.¯î7…€ͳðaã|òƒ6~8—Ìé¼XEÅÎhe}‘é¯×¨¨-$ÅCPÜ/h=ç3œm‹Bß°k:@D½á«›*** PI®Ÿ6$JÃÐ+âCÔÍ¿-^ón áz:>‘´Œ†cOÝ\C2¥4†"DFGÑ™ÛbÜ]cðé‰X¹~ºT­ÆÙ O|tG?E†ró½ÿEzáWØr`+lZâ Stz^e<Œ3ét4¢"yuñ®óÜ`ëæƒÖÔ×,Ä_ŒÉËŽ u«–p²·DvÊ)TÑï©©</%‰0§NUY>¶®[ˆW_yùáxï“71¢ŒÔ«GIAÊÌ|1þÙWQQ$r9HK<„ŸfOÅÔ߀oû¾øOçXø¹MA\ü^œÞžˆÐ˜Çp[P<ž\¸ {÷8`Ï9;|ÛŽµ s«M3 á3J¶ÊidWÍØÔäQÔ¿ÐE§¹"I\™ÃFKõŠÚdOwo6—ÂŒþ%ßÔí±~%Ô`0âó/é¾ÚM" s–n‚¢lº ÒcÙLÞnx˹ð÷Õ.¹œÍ©€=pàåÏ{Áî²a¦F{b„Vemƒw<‹O C«y“°í‰÷ùân¡èj7ʮ¨\Ó¨$W¦WÖfÍixöÊGˆ ·ovÂMøÔ‰ÆdÀ@òÁ⡽G`pÛ·°ð—ÿáDñfêZÞ‹!ôáª+Cn!pû ïãaä#áÐ~,œö5¾üèUô=š¡$ù¨meg7;lI8‚zsVÇÃÆÃihÛBƒ©ÓÁwè‹øì£Çáçbµ3^ê5;ÀÕ~¦ßy\šà0IÈÏg•§¬äjì];<ó>¬£†à‹·^B–^J^=EP IUö /ßžƒ;Ÿ™€p7GØ9Á?0aþ–X0c’édÝed,‚ü±ø»oi€W‚[c{¢=ÒÙŸNÂÌ9« :…óAŸi„ÒðÍ.•ÖzP°·w§US²QÓÖ&Õeؽe2«h´çK·ØT4¬ÐàZÕ ¢#S&îÁT] /—Óž»·¾ÊO]y¡¾”˜-âA¤ÀÊo×Á¥&SêwgFu¤ªÂ=ˆXô-Nõ»çÚõàoè2*¹ÔÆÔ|***W1*ÉåäÐDG­èë¹½jpõ EË XhS–¥¸H‰-‰ªsŸXü4s!æ,냞6XüÑÿá›­ù$ÄORUÂŒFqZf§a×­-ZOk$úÇõ$dYãÃÑ7ª$Û¶%±S€oT[ºÚÕ«‰:…G·Ón 29Æ»†v†«½Ê ³±eåBÛy ØÝ wÜØ:LŸ +'úî'ÜuÔ?¹ó¡ë¨áçħwUb*²bÔtbßì;Ü…^¢ÑŽdvÎÌ™èáw/Ìòãµ_FeÈ(Ìxµ#ü,«”3tO†^7²]þWïˆî®ÉѨG”Ëì÷^gþÕcU;ßï‚¶ˆ”æJ ‚ËK$Œxþñ 82÷sä…D¡’êVâyCM***× *ÉýÿöÎ.Êjýã?f†ö}AAA\Qr7-µH½7½Úr½–©ej¥–šKj¥¦Vv¯™ij¹kî᮸ (®¬²Ë¾3ëÿ9ˆ ðÇ¥9ÇÏ03ïrÞs¾ï;¾¿ó¼Ïy:Óú2¸9Ú@DV}E&hÙ;9B—f”W LÍaWjzïÆÑ¹÷Ëp1\4óžñ®ÚLl_[¬Þû_|&zj$&$"`ðëèêe†#Fö|Ü‚f÷áÅLįg>ÀêÏgሳnÜL@—o¡K×x¥_ÖÛ‚Ùù±PgÂÔ9=:âèO«ÐÙc*%ph K²¬>ªsu`âìin2¾Yô9D‚*¡Îº­ç(,ø·-Òif}+Cþå[DR63vd“Š^™ù\ºÜ/rÅèÿúÛ}5‡)U|ÔXPD†ò’|¤¦ç Ç+1²O í­ƒCzÁm4 ¼Ç&ʉ4Nè3(·ö¥ c÷2Œ:bmœ]+ š8GŸÝ}ÛC°ù >›o†¯–þSÞ}sÖîŬO’(ºBÒ(¡Å;¯Ž„4Î.N0¡œó5E(5»mUZÚš…ÏÉ;…¸…´ÆKæ9éïFã Ü÷ð¨Ñ;±IhE”Ôåò¨)è²èòÑý ×%kn£«àrœ'Ðì p‘K§°u÷Q˜'Ë…›-EV¨§xöŠÀ\ë®ð£¸¡UE†L€qÇ´„²S•T–Ûƒ Ýû ‡—M•Š1þÓeð ;‚ WãQN±M{…na=áD1u;GLÁÜ"}­ß¯¬m/|·z "OœBRf^ý^ësc1&}¶.;"“ÒÛzŒÁàÞAH¼‰Ó±©°2·ÆË“æ!TŸ²=â¢+Á·§À;õ.E2¨±á²Ç¦äaåG±~ÍðÁì(£ ­ E•ÀÕš éfèM¡­.Fv0wù÷q1 QÑqÔgô(¯?)5±•¬Š¡s§AX<ϺÎí!Ó¶IŠAã?†m—\ø÷ô­ªVbi͆ԑ¾ëH0â­O(\ñ¨(—É0tÂBØúGâLT,”bSLA­)|›1æÍŸ ÷ {ÍsöïYŸ»ÁÑŒç¬Ð¨ƒ=Ià…x4žERÑzÏðùeR;õE ùù øÅô8ù¶œ'ÐŒ ›é£¶¿fÜŸ¿¥éeù™¸LÙ7¿¿œ¨Äö¡¯‹ÀÀËóJ€ùf÷ï×ñw+1yÕA 'Öº^<¯ýåýjžÔ”)ÆáìAôžAVÝ÷ýiôä©i …Ô¶¬ä,Ûo 6Oȼ՜'Ðd t½Ðµ­¬,¦:*k7¤Oì˜Úúª0‘ªR”Ó²ÒGŽ¡¡zËŠ²QR˜CÙßX>¼š¢ey!-ÏBiqų®YÎßk°beV¶¸òêX\»·£¿Ñˆà±ƒïÖTÇß9N€hZt±²ü[X4k*ÎßQ£ï°q˜>ažÆÛànJ,6n9•X|/Ý.‹Úeí䆠áîjK˾.ä8ºs Öþ·u ìžâŒ”åe ³pu¶½O<|¼º¿Wä&bãú­Kœðʨá°5ªA›vã86ýQ„ñã_†™ôÿ#¤¡ÓñÐ1±‡•±¤îƒ=‹¥ê<|½`RE]ðãwBú,êlÁuèU¯²ð.ØÛÐpu° ì~lÔ¥C×l9nœÞb8¡•“7neïç‹0Ô¥ € Ì„5Òcáúµ;h7'ctÑ+Kxf+²ËÌѾ×PÜ=R´¥ù·q)ò&üú €‘T€òÂtÜ>ÿ *%ðï5 Fzt j…F^„„ ‘|ƒƒj!Ì\Ñ6$†]¦Ò`'*y9êÉ`çÛž~¾”û‘[ >Ô°†ÂÆ÷—ã»)¤Ør¤µï"‡V<¤X‹¾*xç9ç›@˵äÒ„Œ´¸?ðõúÃÙùÀßÇC+ 5” ¾¤¸EÅ%¨$kQm¡œð ÝÔ5Z+Sy¥¢vUõ§ÔØãøô“Yøeï8{æ4e;C” lƤÿ`òÇ_")—Y±ªêa)iËKÙ1(Ÿ½Ckøy{AŸnç ¹\›YíþÊUÔ&9»ªh ¯(CqQJJɲV-24%.íþ ¿\bj÷=cÅÃ-/+¡þ£B~î?¥ÓM¿‚/ÎÆ´É3°n÷ù¬Ê‰1ûðɼŸU\»¿š,¿¥%Å(.)…â>Ó™²2ëÌÀޓ״m®m7Øiûwßöj¥‚¶«­WAÖÂ"mßÊàÀâú*”Ç—R€•Ò1•ÊJ\‹»Œ˜Ø”ZË3­“W>ÊïÁžòou`zP§²g7ÎÅû«ÓEÓ2Ó(òn`ß·âbt JöáÐæo(>2­{XDÒwUY:ޝÿ»VÍı½‘P“xfRS@"7v'…Ø£´ÔgOÇR½´œþ÷©H?‡+¿C^1]±ò œÿuí; »Ö…¢ Úög)3bw`Ó²Ÿ˜‚ÂŒì[1 gÎ\%cd"×MÁ®?¢ ÿ.R¢¶b ~ïQ⓺zÚr—éÐJ©¡6A„4;ÞÛ×À­KÌ 'À pÏ#§°6o¥98EÿÁËÒ1ζƨ ô´q—Ï#îvTB!ŒMlàW;3yøãøE˜™[ éj4ôÝ:¢ogŸ,¿B&-IŒFL‹×ÚYjhY~6~Z5óW.ÇöWßÀ»=õ±ÿÄ5Ø[áêå´ê܇2}c ¹zewqôø9˜9ù!Ð×¹ 0Ý„¢ÏÇÝ ôìHiucp)ö&ŠÊ+H@èÃÃ'€¶õ@Ùݫظq ަÚáБ@‡v…¥ q11¸ŸŠ …RSk´  ìf–xØOC9‚Õ:"ês%Ö|±½B×#ÈÙDÛ–@WN/&JX)ÉM%hJX‘KBT3;m"[S.žø[¶ì‚§4ÈÐÖÙ©goXÐŒ¼ŠÜ;ˆ< #goû»“øÑàæÙH$©MÑ3´=ŠÓpö\²HÝÅúppñ‚ŸŸ,ebä¦ÝÀ©kyp0Vãrl<:¿BéNõ!ÖT]Âe9®EA|ŽÈÂggÁm»Ug«qÙC»6hÛ±=üY¯ ƒ Å ¡ŒÉ—öRF;FöëQÒ5`ûMˆ~2ȌۃK·åði‡['6 ;¢/lLô´BJ ¤JÛð¼|×Àц2ÝÑÀBäè ¡*¸[‰wÑ®û 8}özíàE#GÌÎePØôÆð–Á\\Œ]s‡ ò×mn3¹yJ„D|†ÃÂQ³ ‹¦¼›72àënåßéÔ8ôéV¥™>AˆïÏÝëØs(²|C(ÚC&ù¿´Uü`œ'À ü9Z¬ÈÍMˆÅ¶='èf­ÀÑ_†@_Ã4Ì_öôl½a+ÓÁåè+ð<KfM ô½Ñ˜0á-˜Y9¢4' Ýþ5á‰ÜšSdnmJÕËŠ­F ÇÊ5¿"%«… Çñú§ðrBFVþ³Àç/­Ä¼ *DœŽO&Žq—w±í¿ƒyTÐãÜY´,Óõìtc6ÝÀ#“€€¶.ÈHºŠl•V¯ý¦™‡°ãl2Š…ÅøaÍ0÷j‡;—×aÊ'k 7u…‹¥®Eǽ×|9w*œÍÌî¦C*G­gްþ#pi×±ôÛß°òãÂŒ…‹`;AÌ&§®,ÀöµóñùÚ°ñð‡±N®Ð àÅ·æaÎ[!Ø»s®ä*P~b7 /nÇÅ[Iøbg4",qóį3ú´4»šCM!V¼ÿoœ±ê‹m­M°~þøþÈ5¶í ÌOƵL5ÆÎ\€i={üGŒ|/:ºË~W‰Ï}|ÈR'€ŽZ@ò\AÓ÷cÊÄ™(°ÅŠÖ\äj/¾ÇøCbS) Cÿ—qèð Äž§X¿vPWäãÒÁM0ô6^&ÈM¤ÁÎÃÜšÃhJ½k ¤Îý1p\o¬ÿ&®\º›^þ´…Šþ™¢ëko!~ûìß¶ÿ|k„öšÒ*fJ' #o ™ºŠØoq.êZM­P+ ‘¯—çÀÜLŸ–úhŽ# N¢H2æ­ƒØÌ®ò&·Ä2J7]ó”ã^Eü{‘.® Žô›ñÙ¼ù®>PHd÷Ò“sLœ'À Í>K¿ÄÒ¥Ëð^DìZ1»/¦ÒùV£"/UrÌó8IDAT·˜òÙ—˜ðrwº©>TH 2WÑÝ ddf =#woãÔ±(” áè`ª}œ^’•„´B',új%†·AeY1Š Ka`ïƒ~v8uò²)–%•ÄKäú“^C_CIÜ$ébÌ„™Xøù|Ì›9ùÑ»±aß)xw‰¾Þ®pv >C;Iæº pÜùK°hÁ"L÷ö~¿ŒÄðmmÝü¡†“Õ¬U|06 {×|Ídq&m[Õ)ö&!ÿÖ|<ÿ;Øvz‹¾X„Å_,ÆKÁVXþé\Ì”`ìø±dÁ•bð¿ÞÇü9ïB¦*Aä¡+toÕ ê┈„H¸z )…jÈsnâhB|Ú„ ýÔÌ_{/¾9Ë–,Á²ÅóÐV?KçÍCz Ù’É=¡$í*²øzå—õ´€Rëö Bâ­S˜ÿÁˆ‡'f~ö1:xòlsœÛF~Q“ÖÜoüðvµA]ð˜(üýç5XõÍJ¬\õ ¾˜7_þ÷Úõyƒl!W±ͺˆxçM y¡;œ¬´7tÉ@²¤ ý*tScpàB"m'GäŽÍ(· ưnžpˆÓÞC+.œ;‡´%dº:äºP©± =Ê—AOß^­]¡É¸‰«7ó`ïèMª;W¯Ý†ÔÆ Æª‹ºJu?Z´Ö\$¢;XæaþçK‘œS]²˜²"Ô éìQ$—èÁÓÙ9É7p#!VŽ®@~ Ž_¹ '7SSK'øv ‡¿£bOGiEÎGžF÷þý`žMíIÌAÊ•KÈQê£û€î¸qd7TÖ?êU¸:ÙÁ£}O ïÙ åÉ×q+£"­ƒ¥ Þ˜>á=B`i¬§õëÌϾ‚ÏÞ¿ßÑ#—…âøhÇø’F kP×Á/F ýÜN$§æàú­PZùÃ?ÈŸi×V£­L¸V!ÍÔ¿yb;²) ‡NÁEœÜö*õ-zj+n%åAX=ãR!Ásà[ðq•ãÀúï‘[¬ÐúÝj«a–=1¨±³úÙ‹ÒÐ éàJò×Z¦”WeX"ÚV£(AÊ™ضbîŠ:aø;‚\\¸%· W]uˆs|ïÈñjÿ K!É%×¢ºþC«kg¾Œà8fBàcd3i÷3i&›ÈÄ&›DUXËL•°v°¹W·H×ô´^£µRæ µ>m­ê·l'šÁÁîÉ)iÉ0®Ì% óf•àÅI31dèKð2SØ+&r%p"?ߪRk²¢(?pï4Ý<cÛo‘ªÄ–:t¼m¥¸uä(/ú }3˜@­(@.YeDÕBEÖ0m¨ââB ¥T¡Dnòüï‡$ò£¤Ö¯GHEHxÐU¡º!Ú751‘Ú´Ãô÷Gc䤕X½ñ„[Š´Âƒùñæfç’DW úäd_Ó§þiPA!ÆÉVÌ,pô¢V@-g“ìLÑ¿«f»€ [t"VLSwÆàTt4Tyç 0öG÷ÖØº$–`$©½$õ¥Ô7êsÕÖjG;m;ÙW&|3âOc_¶Ìœ{ÂÃ¥ÚE¤j þ÷IWçÀap1ù§÷lDYÔ8…L‡>‰Ìê IXªéœhHE2‹+¥r$^<°2ÏžÜÄÍ,9ôd.0Ö;Œ³¿„ßa´íOn B™+¾1Ëg~Ž“ºipG׫šzìÃ&eÒÚ–¾±ß=‘™‹A‘äa”pE‰»·¯@aæN× zn~[2â ×0rÜ$8ZK¹—ñl °b•&f¸<ò=„Íz­÷mDôÈwiÚß_»óUœ'À 4 ü4:MìF*1 IVtsÍÈÌ¢Û¬“ö¦+¯ ¸žt¿ӣت»0Ý€01ÁÇÊ„9+16ð>±L!Å4’°zØüÁÂîíz–î<836mÁöMI¸RhŒù}û’Ø®ÄO߇ãW‹ñݦèä…Š´Hô:p„,Vµµ«) «Y"3‚È@Þ/¼†OßèF7}R($rs³ aéá÷àøÆú ƒðˆ‰»ÿ$Ö-_ Í@3”Úiu™Ò$<} 3C;Új]•%((T’õÕ•" \$ Ìÿ²J…’å¶dã:lýy#Òm¼ÐÑ?%”nùŽ(Ò‰‚kÏ7á&Âê-¿œ<š¶ï cáËT%¢”&® ´³ëïñg#m¡£ÐàÃÄÆcFâ—uÛ0gÉÏX={¬î V½1k$vYêY{ ]·.ØN!íô¤xm`_mø©ªËŒbÒR,ÝôkgQAc%5»hézФÀ•[J YüBƒZÑ ŠžWˆ)<Þœ—p€& e½J-`çŽÎ­³‰/ìŶ_6“ØêG–Z!Ê3’™›ƒÜ”(ÊKqý,`a ›VžhÕ½?~Ýóbƒ`).Ĺc'áöôå)øå«N¾é#áʹŽÛé*’¶v4g:™—z°bwBû#!l(Úl]…;‘K~ö‚{¿±zvä‹9N€h&jÕQ3ið³m&ÝI´ª(„•µ{[¸šI¹}®Æ§ -5 ûwý†4tôsÔÞÌÙ …«¯0 “vB„b¶5¯ k÷×Ú«îUÃ/{iõ³Ža}CRr-Ø÷„…ºÑ¶å¸K¾»2Sgš5n²‚ ݳÙ$ÐKHä’6U!'ï:b’ÉÕ¸5Ú¸YãFÔIªD01‘¡"û*6¬û™|\«ûÞk“dEcýÓ ZAñrÿ3m:ìK/`íºß¡Sx(²»‡t­¨ÏÇŠ‘ÂÈPŸ&¸ÁÏ›÷£”"I È?òz|îdÞ¥˜¥½$Lúµ[ááÙN6&h炴ñíp † ì¤Õ¯~ÝzC”y[÷ôt$Ü_²S_Åáµó°jËq“›—'$@×§J£‡á¯EÔÎÁÿ$ 9E ©܉¬`Lçüܶ8¼i9ŽÐ€ë½[sâ2œC_„·wkrMCWWL–[Cø cY^³rabï ‰¡ˆ%¡ ä•÷áÔ Ž´\WO„œK‡ptãWˆº”™¡1.ìXŽÈ=[Q,·ß$ø¸èàÄÏ ±çǯQjŒþÃRÔ±x”ëšÁÚÖ÷®ÆÁŸ¾Â‘Í+píBtÈ•‡—й֟}lÄ$hK±sErJôÁ±5¯â8æD E[r…")ì-L(V¤zö~x{Ì0,úùwÐ-¸šÈqpçt<C‚\ÒØXZBf@a•ê)}XØÙÓãTzÆ^OéJ`og}qí6tC·´&·ƒê›‹s`7·vAdbz #&“¥èÚ9Ç¢#±xñr8J —˜E¡ÂÚ"ùü1ìŽôƒ­WkÈcNã«%«1yÖ‡˜0î_˜Fqsç|¼Þˆ9¶IÖˆ¸_)V·S@ÂÜÊÊÆ2Ý{-wÇÄw^Âì•{i¢ùd*aÙ6ïŽì‡ov}y)p0ªÄ‰ƒû l5˜|wI½áîes»6á&{ƒù#nû5ø¶ï õÑ‹¬¹Þ6HS{¡+…?cÅÿ…Ñv¿¬ú‰Q’‹KY†x}Âd¸ ‘¬k;rñÝ.:03µ@±HBÒW‚MþˆB­Á¦5_#¬‹ºzZÝëÿðxèç™G_¼:ݺsí Á™´Œ“‚´–z6^Óah°Ã~Áæ0¡s¥&K ‹h( „™Ç Œzß…&U:Ârø ˆ,l´"—M`“؇bÄÔoPPª S %"'?ìîvžT©.WÍB•‰A†~H¬|1ìÃï)|_ÊUpð†µµ *‹ðÒŒÕÚXÍj­¿;—.³ ¿t>ÖùÿO<Ó¼V¾¸1d<ÚnüñáÑ܇œžÙä…à8æM@8›Jóî“·ž="52³G§`_˜Há¬õë,ÌJC>7è:t4¦NúœÌÉñIe–ìÜ .6¦u”…æ1µpDPÇN°1ª[ )­±#Bºt„5…9b®b‘Ξþè@àHêèÊàji{¯@¼òÚØi3‡‰ÐÆÛ6fF())„®‰ ÆMœ‚a½ KIÄFŽ88Œ&¢‰ ÐÕ‡ å®]Càïü,ä)Ñ:¸&NzÛ:=ä>AÍ ÉeRŠ  ;£êþ‰´–9s3²fwîŒà€ÖèÀ?´3ZÙÉ™žŽ2ŽÄ{»¥¶í.N–Z¥ad錠@ØZ[Q /„Óco+èËd‘?`Pxô öR÷z2kt"o%‘#ýN&¦.ûæ$¼>¢+ôÈçX—ü6Mm=ÜÆúll&€žØ>AðkãBuÚ¢M”ÀžfŽ;ÛTÍįîÈ3c"mÆŸè:Q!dà?H”ÕZžùÁþ– Å0¶u€¡¡¤Jà²6îIMé<8ÂÜÆ æ¶N0³u†©•Œé%3%·²øj-Õ6 x¬­G¢/Ô܆wºUO h=£Rº¶Ì¬éz¡ÊÅFæsÙ™êt„©µCUÝ–ÖäÊÀž2°c›ÀÊÍvnî’_¹Š ô¤0fÛÚ8Òö¬=Ô.;'ú‘Ï:Û‰µƒ¿êgÀÎ+9Û8·†ã¹ƒ0¿ƒ”>PIª}ߓۼ±…%ú(-ÌÇÉ? (À½{‡7vW¾'À p"@†v«áå„„¹%ª£ <°® |a§ìÞ ôGÚC"ƒT†v’OÍ:æ†@ËØMåY­›HðÀÁªŽÐpnEU½Ìí¡áíê_Ë.é'Þ¹þjï[Ã&^õï×ñw+1yÕA²0‹‰{Ëþ)±ÞK ³éÑwùŸÿ¾3Á?>5:qj²Ö·Úÿ+B–OÇÉÉK‘Øc=áb¿¡F^Ótí«(¥r ThLÒ¨"¤ÁmVrŽí‚·ÇÆ‚ µ߈à8ÆhÑî õBªG¸Õ»ý_¼¢~ËòÀÕ.zÁXçtX2†zV7ÜÆzvª^ÜP½ ïY³¶¾VÕ¬çï ‰·ý`š§õõü3ŽÁëüs°I…¢Š ÈÙ“šÿ-†éíhú¯¤ñO'4›0˯3n ˳§ý9§ˆ×Ê pO@€‹Ü'€Æwá8‡ T ,$ùy0 Œ€š:¬ûïÁ¿7-Šd’زª`p7ë±ÇblëQØÂ*?¨Ç°?ÖQøÆœ'À <.rßšàê$@î=4‰)–â® (F2¹uBj& _¤êK”Š&Þ (K/œ'À 4\ä6•3ÁÛÁ 4w¤o*É'³^–æÞ?Þþ† Ðù×a“ýxá8N ‰à"·‰œÞ Này ÀEÎópy8N€x><§àùèï'À pœ'À pœ@K'ÀEnK¿xÿŸšÅ³àO蟚"¯€à8N€x¶¸»Â³åÉkka˜¢J­$_DúÀ^¼pœ@£°¤!jŠäÐèX¼ª•oÄ pœ@-.rkYðOœÀc`±€Ë‹òqëB$„B…Íç“n ߸E`Y ²Ó¡”ËHnÓ¢ñÎsœÀSà"÷)òÝ[.;{œ¿ƒ}ß~ÌZîeÀ{þ¤È’«R((U¸VVÖOZ ßà8z ð´¾õ¢á+8† ܺu ׯ_§¸·aR|-'P?ö¤C‡°¶æB·~J| 'À < .rŸ„߇à8N€à8N IàÑšôéáã8N€à8N€x\ä> 5¾'À pœ'À pœ@“&ÀEn“>=¼qœ'À pœ'À pOB€‹Ü'¡Æ÷á8N€à8N€hÒ¸ÈmÒ§‡7Žà8N€à8NàIp‘û$Ôø>œ'À pœ'À pMš¹MúôðÆqœ'À pœ'À < .rŸ„߇à8N€à8N Iø?bÙe×Ý> IEND®B`‚neutron-12.1.1/doc/source/contributor/internals/images/under-the-hood-scenario-1-ovs-netns.png0000664000175000017500000024575613553660046032447 0ustar zuulzuul00000000000000‰PNG  IHDR„«Y¥†1 pHYs  šœÕiTXtXML:com.adobe.xmp 5 2 1 m ž@IDATxì`TUÖÇÿÓkzï½’ ½#ÕŠ *6tu]uŵ­ V\ׂнìÚWl€T¤÷ÞKBK#ô:ÉôùÎ}I0 `ä#r®†™yå¾{ïÍ{ÿ9÷žsd.*à˜`L€ 0&Ðm È»mϹãL€ 0&À˜`„|!0&À˜`L ›`AØÍ/î>`L€ 0&ÀXò5À˜`L€ 0nN€a7¿¸ûL€ 0&À˜`AÈ×`L€ 0&Àº9„Ýüàî3&À˜`L€!_L€ 0&À˜èæXvó €»Ï˜`L€ 0„| 0&À˜`L ›`AØÍ/î>`L€ 0&ÀXò5À˜`L€ 0nN€a7¿¸ûL€ 0&À˜`AÈ×`L€ 0&Àº9„Ýüàî3&À˜`L@ɘ`L }ØÍ ¨¨ªÔzx{zB¥p [£ •U5kõ0j¨©ª…Lc€”²·=aÇNüÁåt ¶²õ6;<¼|©ßªNÜn:è:XvsÉ=aÝœ€ å9Xøù§Ø^\¥\…J…˛ݧӽw†^9—¥…ü!+s]9¶¯^„ù+öÂîlÚ_¡T# ,ƒ† GZJ<ܵg¾…ݶ¯¾þ.¬±£ñÏïAb î„ãfoü¯¼ù!ô}&àªA~øä­aL¾ O?}7‚ g®»¥¢ò‚ÃøuñäÕ*1vü8ôí…&)éÂá­«±%ã(¢û GÿäH(Zv:Õ«ËúÚØ =Ü'^OµËÙ,³Ö”`Λ/aÁ^n{x®w6Õð>L€ œcm»ãœãƒruL€ 0ö à°XPQP„üÂJØk¹c'Êì*¤è ƒ F Yámm:´¥®;V|‡?݆ôƒà¥A]i!æñ)^û M÷LO¢ð 3odX,5pÙlPÈOu»5Ãj«…¼Á ÿ¨¾˜úh ”nAðÖœQºÐþÊÂ,üüÝ瘳* ‡ ëðôô©ˆökž›ñË«0Ä+ é ]æ"Ì3æ Üvëõ÷l{NhÐ|É$ÐM°šMÄÅù[ój&ÀÎSÝ¡Î×±ù8L€ 0sH@†€˜$<ôöûx€tFe^&>›9 ßæñ껡_„,g4\Y^€…s?EFn 4î~èÙ»ôíƒêÄ!Z ÙÊ•îˆH…Þ|â<á°™±}Ùl<ÿâëØ²f5ÆôK€ªþjá†äIð1(P–{rKá Õ¡ a⊪|¬[ò-–V–? 4=£Z/ýG¯J²fÈu ±æšrìß½ ;ö„F¤ôé‡ôž‰ð0¨O`¦ K¨^a„›Ì†ÕËcaß4Ü=y44¤SåJÔ²’*ä’ÕÐÞPƒCûwaóŽ=¨49Ùýû÷A¸»W¯Å¯?.B©2 zÂBB‘Ø#Iá~0‘U/#c?^Hé‘?ƒ 9G!§ Q± tsb×¶ÍØ¶ç,r¢z¢zo„úiqäÈa”ÕÂZ_Ê""Bà’) ”;Qs{÷‚{H,zG£ZZqB?ù`íK€aûòåÚ™8Ïäd‰#D‹D‡Ò!…”´LIïó3·`æC÷ã«Í¹Hê™guþU®À”G_ÄK÷^ÝÉÆ>5ž†šårýÉ!×èi¿>ˆMLÂa»¥yûñëgoã"O<ý4†&¸aïªï0ó£Ÿ1ð¶é¸!™Žn7cÙ¼±}Ýøh´8Vp!ƒ'`ÆŒ§CÕËä¢} ¶­ÃÇo½÷”«1ýWbñ'¯â¥÷Bîá ¥µ5oÜùð“¸ÿÖ+áçöÛ­ÛEíSÓÅ#‡ Á\†ïf…¤è8Œvœ¼WNÓ1Ìÿß;xvÖl˜•Þð#7»}.¿Óï»6lÁεÛQ㑇cyÛQ^aÄÍw>„g¦]‡¬íËñü¿žA¶j f<ó ®ê©ÄüÏÞœÍuøëm“aÎø¯|²nÁ‘ðuÖãHv1FL™Š'ÿ9+|Š™o~‰êšøEöƃÞ%:ÝÜ6r÷®Á‹Ï>ƒ•9rÜ7ý)Io8¿aLà¼hùVž·ò˜`ç…‰9¡çDqѪ ²Q^RŒ’£ÇÔÁâ_7`èµ÷àÛ…?S»çãÉ¿]ŽýëÖã§•û ¾r— ×Þú8ýø=þ:aT´LI–ËŠü]xëÕW°.O…ÿ7þzÕEll}nù=8~û™yʇbL€ œoõÕUÈÉ8€À¨Þ¸xÄEЫUð ðaC°êàrìË*ÈXVÍbÊŽœ=«pïÍà£WÁl®EnNd©˜6x"½’³ ™!OY„U(´HK€þ½Òàf4 eø$®ÜŠÚj6ûÒð°8Nsï­µ8œWŠ…F÷KGb˜74!#1ãåžht*ÈÙ»eëß^]N­î9þb4dîÃ7?ü€ŸÓÒÝ,å$––£ Ò½G9¶®ù ™¼ÜÔ•ç!ÿ@ªÒzB%¬«2%üÉÒŸˆ#™Çµ{2‹ª¡¦9Ž‘ä—çã`®¹ÕjDƆÃf*‚IŒ+öGByGÀ´Ñþ}J¨~/²€G§bäèÁH‰ö…­ÚLÖV öl«{w@퀿Ý?·]1:~"ývNù8Ïøëwžóᘸ0œ,Ðè} Õ5ÍÃs9epYåO´4Äüûâ‚R­EpX‚½dlS`ü¤¿bܸ±ÒÀêÃë%ëcëýZë;ZIóø”Ðé44¬ÛT¿‹Ä›Ë%§°2*`tüãvÌæZh3y5Ûè?™Ü)I:ÈÕðöl}˜ß½á\t‰¸ôÆÉØzh¾Ÿ;cµPÐq]pR64ØLÈÛ¹õ%¹¤aÅR'tA±ˆŠð¥öˆvÑ2‡r£üÃá !ä«–¡¦®}‡¤!ö:äïÍÆÎò ¥£F|j<ì9…pÈ Ð*õÍ^ÌT iÍTÒаœ˜©h^¡V©iê µ\HàêÒCÈ7¡½ 3jÉ2ÚJÿ®w¼€ 0ö&À‚°½ sýL€ t ¥ Zƒu¦ò46S›Ô$¸\©°;hH™æžXÄ€³ áq#È:÷>úFž2FÚ–”‹tŒœ&ÊÉqCH«M„¹i¬¦u!D-VZÖäQ+£9pZawÙèŽ\&5¬tF HjŽ…bõI­rYŸ›‡ “aáðq?u[¨%H4 W_¼o¶ßåéáFrdjxBpÑ Å£÷ݺû Ih§cÑ[—µ¡éXb¦LG–É@DøaÝêÃÐë"pé-}á[»?.þ¿ìuÂ?²"[ ‚ÜFqiž£h+ÙárûŽh;õÑI}”ï¥×¦> ñC0²WJlÄ—}€è Œ fUraLàB8Í@Ç…h “ 0&pŽ ÁFÿ‰bôôBBj jìÆÂE+PÛhAavþ¼N÷pôI>µÎå [EÈß9›¸Hè•æ¡¬¤%‡¶bɪµØUÜ ËHˆ9í Ø¾}+6íÊ@m}vlZƒEud ‡…¬qÑÃã…¬‡P /g V¯Ú€=GðЦ_ñĽÅ#Ͼ‰Œüòã›C]lé§Bã‹ñW_aéؾa3öäTK^½aÖ˰mÓ&ì:X@Û[°zálüã‘'ðõòmdÓ#ˤ©ž†‘óQTZ‰Ð„DøR€ìõ[¶#On„L ¢“#P]W„ûs¡ŽŽGH`8bƒBa°Vaåš-ÈÌ­@UáAl ¸ˆÇTˆLŒ‡Q)“†Õ›ÎBS‹…6ô ŽÁå·?„‡¦Þ×áx÷¿_a{nõñ.ñ&ÀÎ/¶ž_Þ|4&ÀÎa{s3ƒ°š O\•›ÒFM„å[ðÁsÿÀНBa¯-§Œ>¸ëÉ‘JCÂ'!Ô¬´·ð*i-g~ÛÈ;$1)Iøþ¯pß­[à®v‡µ¦†€H²½ÉÈHÁ±É2—¿ožyè6üÇM¢‚2Äžˆ1ÃÒ¡¯Ø›KIƒÃt:œ’,–NÚ'qØH\•½¯½?7M\¥¥ve0îøçÅH þ­b?ús(”p‘8mÖ¾ˆë‡É×_ƒYGq˜D) à"bÀ0ÜpûAÌ|ýCÜyýð÷Ò¡¸ !é—`¢¯/tî&x¹«±ã›ÏðtE¾ÿ$G&#Ò°ññ¡ˆ‹ñ‡gy‚¢| ©IôÔ pôHL:¼oò>&o^É©0¹pÅ-÷aÂÈ,Ê])y{“g‰Ônñ¯Ë¥ ØfúSbÄ„ëpK~^ûòÌž†Ð¿ß„wqÞ¸0&p> ÈhÎÈ©ïtç³|,&À˜À9&`k¬Gîá äW»Ð;-ÞnBd¸P_s ›W¯À¶ÝÙPycèEc(‹GÄïŽn·Ð|»#û‘]êDŸ>½áí®ýÝ6b©ò¶oÙ€ûÛâú¥%„SÐe+Œ4ïÐ ÈÊÊ%Y©€¥¢;wîƒÒ?#/¤¨ ˜Ê‹pàP6žA£`Òy´­Â#I Q[j±×Vlܸµ7ú´1¿‹—hª)ÃÁÙÒ¼¿¸øßÖÛLµ8™â:â“’è™ÃŠìý;°zÍzZîDy&ØžÔŠxx?ÖoØŠµ?†Y52äÀdŠ›¨0×áðª³ÖNu&Q>kC5öïÜ‚ [v¡Ö©Eï¾C1°o*¼tNä>„‚R"ãâè 'ÅrÌ=tE56DÆ'#Ô×@ó °&dz$''ÁÓíÄX‹ÒAø&ÀÚ• ÂvÅË•3&À˜`L ãà9„ÿq ™`L€ 0&ЮX¶+^®œ 0&À˜`Ÿ ÂŽޏ…L€ 0&À˜hW,Û/WΘ`L€ 0ŽO€aÇ?GÜB&À˜`L€ ´+„튗+gL€ 0&À˜@Ç'À‚°ãŸ#n!`L€ 0&ÀÚ•ÀYe*),,ÄŠ˱uë6ÊÑi‘R4µk+;yårÊ ޱcÇ¡GÐh4ªGõõõhlhJµÕ¡ÆaŒ€Hg£¾6;%‚ë1ÿ)Õ2ÔjT*‘ØX|â˜ÀÙß}•J wwwˆg~g,*0µÕjÅ{gY³^Eaa|} ÕjéÆ×»~þÚìt:PUUAIîí¸þúɘ6mº$ Ï_ N¤ââb<üð?±g÷Jhτӓâ5L  (q½©µuµpØ´CG¾9Ê T*èæ£^Oz¿ÿm8ü 8 åþöÅÌ—^¢ÌF}è»uVö¶ÓÔ}~·¹Å dAºçž{0{öç¸üòIøè£GDJ˜rhvä{ÞùáxÆ£¸è!a2ÕaùòŸðÆ/bõêÕÇ#Fœq¿ö^é¤ä©%%%X¶lK%ú÷‹¡|ªâ!Æ… 0?K@F9ƒ+*Mص«j£ÂÓ 5¸Iy”ÿl]í½½Œ,u•%È?¸¬&$§øÀhÐн\dFæÂ˜ÀŸ! §<â……•X³v23 gÏž][Θ1Ÿ}öž{îmüíoSIʤdìZwÞVüøNLŒÃ!£ñ%¹¿óÎ;±xñbDEE]P,2j˜°òNšÔO?=‰Úb¿ íáƒ3ÎK@½{óñÂó‹Pdí‰Ëî|þaQd)ìx=R) ?s øÒbk1ý±+êM eAØñη¨ãPbÝÚ úËgP(Äpqç´¶·ÉB˜ÞŸDÃM$dî•„ ÍÖñOQÇk¡½z¥à™gÞÀm·]…×_ o½õv‡h¦°6= ØBØ!N7¢SpÑ÷ÈIC&bþ ÓîM%¤×Ž7„⢖Óálj§“Ú* ˆ{ÿ;å…ǾÀäpˆgh'.mÓÌÇ9sæÀB pê½Ó%è’v8ø…ÕìTs/Ų ¥¯E›”ªö;¾Í¦ÀÀ}1jÔ¥ÒP­˜ÃÇ… 0&À˜`’@›ᆠëN&ýÏ©¶46¢Áì‚da=NÁF³Ž?PÚ4dOs_ŽïtnÞÈ\¨/ÏÇÖM;Ðà¿¡Ï}iÁ ÄÅ% ¶¶,Ï=c®‘ t72q·7.L€ 0³$Ð&A(¼‹µݹ /C÷-…³ß¿};¦Ü~2ÊlP‘À–Áú’ÍøÇ­÷`GN5Mʤ^‰mixß"…è;f2ž|ôaÔog^þ.¥UÊÑxì¶ÉÚ¨¤_æÕ9›ðÄ´Gáêw^~ø6(Åœk.L€ 0v ÙõèÇ·Œî=5Û±äó!Š‹§ÜA?pU’³JËa´MUîf,ý ŽVhpÑÝ3a-܈­Kޕ⌿æF4ô³û¦Þ´ÔǯL€ 0²]¨â‚ÝæÀ˜›ÅßÇã™Oáh= ç6w¨ÕÀºïàÍ/Vã¶ó°jÙrŒ‰“á™û"íb ‰Çð+ÄËÏß sÉ|¬É(¬ùXüãzìß<{ ja©Ø‹…[  ¶–ã­‡§A5æ,Z…W¦OÁš¯gbîÒp*­ÈؽöÀ«ðÊ̻ᩢa¥–êL<;õVl5…áî&ÂÀ£0êBáã2.I@Œ¼ˆá]é•zètÙa·Xá°:àÖ ½úÂ×›ÿÓ½Gº-Šqäæâ´5 `× ”Õ*Ðçêû1jâõsý=HéÃÛ× ;+OšÓ²=¿2&ÀþˆÀ„¢i”âIæ‰;{žyóðü›?À¥¥¡ºñ)íõØs`;Õq@Õ~,YµŽæz¡þØ*d×»!ÔKGøÁH6éi©X¹nJ÷­Æ1ßdŒèˆu3¿o=œaÁˆqb¿: wÝúwôމÁÑ·ã†aضw¥˜’! $ Îx}RhÞ YË20í®±Üœ†?ü½Ã= %ø#š¼ž 0&ð„®“ѽÏTU„²‚\Ô7˜h8Ø‚¢ŒUؽi)JËj¥¹€JúULÁ,P_QŒòâ˜ÍVi¹Øßn¦é/G `0F!:1N ¦ôGXXÜê«a*/∂px5`'¸€CÆM ±“ÊO¡xú¾Ëñ÷7žÄ¸´'a¤¼šÂÇW¡ñ_ò±hþ× EFwÂ>Ã.‡Òj’nvNŠøjW+0„¢‚o_¹+}­0ø]ŠÛ–áWbS^&¼½#&È Z/7¸{ºQL0š§Hñ·”rlúàRÒÜCOèÕrŠFsnèfÛh*DNF%üz¤B­SS¦¡ñ'&À˜ÀÙbÎe­AæòùÈØ·6˜ óAHxrÖÏþŒ„ö¾ C.¹’îq ää6‡w."QhChß«1jòMðÑÑ=ÉFETj7r†kÊî’éàé­ƒÎX‰†† ú±K"‘ŽwòÜói7ïØ@×'p-„Mw+Ò„¸äö71)¥Ï<þ m" M­v9 ö¼Ÿ,ø…²z,ÁœOçá…§^Äð8/X¬ân'\S€£GÃR³¯~µáÃFbÀÐÁ(ËýšDá>ô7žÔËšÜ|äå@©§á4¢¬Äoo7V¡•¤øD0YQÄœïÀ¼ùñ'ˆª]ŠGŸú7ªH‹Ò¡¸0&Àþ_„ ¬>º;7üŠÂ¢Rh)DBua1 CoDõ‡¡îÆ•SŸ@Þ d´Â#|Æßû\}ëuÐ8ÊPY^y\þ"‡¡Ï×ÂÛ×y{–bÓ·õ‰ ¢i/ù¨®U#oר• ,?^Gs`Ž„V§GxÏqذ[æ¿ [Ýa8J6 ## =.}‘1apQ¶%.L€ 0¶¸`‚ðt ¤|ðÒÜ—–[™˜_(£y6"“‰Z¶ÑŸ(ª¨c"TÄ/fáø!MØ––4ëš?·ˆNes¾;Õ{BiÙ°õBZæ ãÈ(B5O!l †ß3&p6ĽJ¡õAïË@LÿëPo²Á#0nnî°7FÃ'fÍ'TAïæù0§tÿ’Ó^Yj*.¢54 Z̯vØs#a9ööéÐólýúe(L€ tONŠÓ nš­ËÉŸ[Ö¼üwŸ[6lõzò6Òª“Žw\E6ïwÊ}ZÕÉo™`m% î'r…žÁÑð¦_šâG§“žBc„_tRÓP/ýÀ·%É1„Þ´~/9ÀÑJ® ï˜Á¸ì‘þ¸ø;\$Uâ/K»x8£­§ƒ·cL ™@‡„|v˜`]™€…bHWŒt´i™}Õª´þÔú}Ë&Rz:Êî¤ÔˆP]B8Ò¿Í£(-Ûð+`L -Úe$”~¨Ò/Õæ_·­Z¡$ùyÜK®Õr~˘`gO@JQ')ʳ¯ƒ÷dL {8ç‚PDÌÚ±‹¯ƒEš×Øi¯Äª¥ËQ@îºòs~Ôî}¹÷L€ 0&À˜øÿ8çCÆ ¹ «ç=Ç?Ý‚iïoÅ#צSFбZ›‰ÇïºS?^„[ÆFQ( öL–DÉ„†8(üŸäÅ« I1µè!¥44 Ò²­ö^Ãb#aqq¥EN#-éåDä‡"ÕCѤºÅ6bû¦ù;´ŽêÇ”ê¢uªÙr á ÒÒ±ßñúÄ>$rÅœQ¤ãÓ~¢ˆe¢ ´ &Ð͈/¡øßÜî\(âÝxtã’Ñ EIžp"'»£‹ˆ{š’B6Ȩ­ EsV(ÐBvŸku‹øã»z+(ü¶ w€s\DÂvJ§ÕàýWÃøA?¢W°VòÔÕhÈ[W+§¬.dglÄ?ýˆ£åuˆK‡k¯¼ >¶,*¹/ênÂæÌr ¾d Òƒ¬øæ›/Q-ÂUSîDz¨\mĺ…óðëÆ-iB1æÊë(…] ä‰5sób,Z²µNz º€ÆòýX³9A~¬ZµžÆàêknBRÅ-$Õ™·w-.\Œü:+’ÒÇà²ËÆÃ_¯ X`ؾj!~]»‘r…Fãâ ס'ÅN´›°aå|,[» u†_| Fô%ÅxŽqruL °ÙØ¿¿‹îE]ƒ¹[KBýº--­ÃÎ]Ga¢ÜÄ?2 zwÏéà!„`mYrìGC‰/¼¸îîZúÁÌâG|݆Þ=C1n|2|| øÈMcçŽ@;B;,*L¹çEd-|ϼù¾zé¯Ç •ûWbÊäË ‹‡^¡Züïù›°:ã |ñòØõÍL<3?ƒÆ\9}õ?Š øÈäìü?o«ÂêÏcÕ›÷àÁVcððá×ü„[¿þï}¹ɲ¥¸ù–iˆ<ŠRÖÁ+ˆ:óá¯pId»÷>(BzaXÿd/ÿ —ïÀ¼¯Þ‡òè¸uÒ¨)ðu|° ïÍø µç_øè™Û±öÛãÞç¾Dú!hܺ _ÎÿŸ1ÇV½†ž‹¾£FBiÚ‰|'Þ[„Éý"[*ÏÝi⚘@G$ CE…sælÅk¯,…»F n+)„”:ßHC&{)ŠínRÑp*N XaÍ4—jPr¤ºéë¶'ïÄïWÙ‚¨x„†zaðÉŠzâü‰ t=í )±³ÆÐ4̘1rOÃWãGã–ôf/8‡v­þvÿëèsÙ  ÌsørÖßñÌw›Qûü|Ú†¨þãñÞWsP¶}Æ^ƒØQÓñí+“ðëÛOâYÛPX|¯|ô.»åÌzê:8«JðË“ðýÏßCŸƒG^yä5ŒNò‘«P©‹…gUÂ|Ý1ñ…¯0íšhlþáÜõÐãX{à9\쌿>ý!ú¿ áÞ˜;ó/øòÀ*ä^‚…s>ÆÀ¿¼…OŸ¾•‡÷âYï"s×F,Ÿý Ýþ6Þ›~µG0cÚÍøpöO˜4`*d4|Ì÷Õ®÷eáýž€Ë%¾ï.¤âß”B2Éߟ¼g›çTü~ó.¿DXØ,Ôñ׬mB«jiî‹FšcÓ•kû^2rÅjÅ"méñB£Y¯n\uõEÒ°º\F’­×ß°+¼CâÝ÷ûÚÎà¹ìC;BÑ<¬fz_q;nó_¼úô“úõC4G¬.9ŒÞz˜«aúãéRÔ ªìän×I3Xìv BÈŠG›ÀI9>Ý}}Ñ+%3(—§z ¯ ³6À¬p`Í÷/aâ¦w¥¯ê¡ |½+{Ýõ˜·7Œñ“1vÜ\wUêje4Œ‰>=Âa6ÉH9B=Ô((,‡1Æ ÕùðøßgÑж‘Þ×Õ4÷Ñ„º* †öëECÁr¸‡ö¿ßù/JoÂ55Èøé\¿é?”Ù‚£¡-„…¾_”Ꭰè>衪'Qæá(oï¦ µÝ§÷§î)1é4¥»Ó9j´Zp¸ªu”©àøxZn‘9é¹"ÃöÍyh¬³Ñ|öNt>Ûxá‰EQ1> õ¦¨ Í“ñÛ¸/oÖ5 ´“ #%Øä<üÔ,¬¥ùu¯½ëIÓ•UÐ(XöíK˜ñö¼üß70¸G(Ö|> 3Wÿ6ù®%>—\ܨèÏEž$âËê"‘¦/­fè1òšû0yD$ÌN3PŽâ„Ƅ୹«pÇžíXûë7xçù¿cw‘  ×Ð omvshääY¢R¹°ø?ã¿ßfá©_C¿_,~ûa,ªtÐí@Ø*TÕTJ¢Sx½”æ ²žæJÉÕqÕݸaH,ÔtVCH§þvcéš× ÷Š üŽ€°†I“…—øãÂ::²ŠÎÝ¿3×­G¡©V€Òž2’ü£âê…xêt=Aè ïéÅ—$cæK“ç×ÑÏ·ï<h£ l’amm‹Üt¥ %¯øKðØ´¿âŽÇ߆YIs1h]mU5<(_ð„KG…;±xÅ:šw3R6Q¡õR°Õæƒ9…l9<½qû¯Â€¥UšÀÝsèèkËñѯÀoÈu0e|Ž_‹Ãqÿ7bPÿA0Úò1÷H6¬“Ñ`ÊÆÒŸ¶bÈ]°oÏF-S"!*Ë+i¾H?\r鸯ƚ{Ð6Šò…º!æüºp n›ŽºŒq×OàÊGžG`„?² ë<`ܬŸvÎ'Øï “#Ûø<ìz7˜æSÆ/L€ 0NA ÌÔ@^à.ÜqÕ0$G7ͤçL“—¦CHÏž–P§èR›i§ð_ü²UUfX,'çqmS¼Q$Ð&A¨ 6‘ü÷¸2;3 •šlš&!'ÂËŒ¿i._¾K·[É XƒÔ£aøï³¸êâMÐÐô ÿ˜aÚ´ÿëÄ*à¦U7ÿ&“Áhp'«‰'úN:zè*ÈaxþÙÇqdzpéð/ ‘5¢´x~Ü]ð6ëñë{aí÷oAã²ÁDCÍ˜<†XðŸû°y¾–&×#ñ²‡04ƇŒAÉóïbÒèáÐ’å/0u0²önóïý‚¿ýí)l|ô9\7a%R®…gÚX\sÅ¥¨1á¾Gg`Âø¹4èmEm½S_˜L–Cbó‡…µiúÊÉRÉ… 0&À. w½†¥bø $i©Ï¹ ÓÚspTñ8µÚ°ûy™W—ƒ ¹Š®B M‚0=½V¯^ƒªê*š{çÆ¾Û \rÇë°z„Á)4¤GÚüû•¹¸¹ qñ¾ð¸_~‡ýyUŽé)¡Ø³m-järŸˆKUîPsb0óßïÁ3<õu@ô ëñ|ØxøR•îWÜyqðqgdzôê31ÁîtÀû0ÁEØ•qf‹‘)ýÐ+)9ëçÂÍ+½ý>Pz=Í12î4w¢Ï„éø!µ_S{D=½^$…h¥ï c{âêæ> ‹§]ê;9¦DõÂÄxr8¡ãK±Û U*'6oÞeË~ÂĉWQXƒÐ¦Fó¿L€ 0&À˜¸@Ú$ãââ0uêTÌš5 ±±‰¸ûî(¿ìxÆŽßµ’PwÂúÞºˆ¹æbHU,–Ã&aÕ´ø,vE¼¶ì+–‹ù-ŸE"«HK9n‘kµX×"Å{9íà–†ï¼a^j)KŠ$<›Û"¶iÝQ¿øLôøq[ŽÓÒ~±(-ËÅû–6Š÷'ѹ܉mÛvàÑGï¦`§Þ˜6m:õVpaL {ß{ñ'†'»úe÷:³Ü[&Ði ´I Ñ2cÆ âÙgÆ®][qçÀß?€DŽˆÑÔ"å:.¹ÖWÜx=œu%ȯ=Ÿí“0‘ÛêÕKðÆ3)c‹Ÿ~ú)¢££;.0n`íC€î5 (0™(ÊaînÐI¿’Ûçp\k;5ý‰!®ëF[%òžŠP/VÖ .L h“ í4’ˆI§!Ü×_ |MV.?7'ê^ËòóÍ]̬®®$‹ª×\3 Ó§?†ž=iX `ÝÙƒg6ìDd ð§Ôš±”¶“-…åR ˮ…ü¬lüº)‰é=NñüôBÔÿQ!šÇ”¹;K÷Ö`İ$‡¸Q@6.LàÂh³ MÕh4xøá‡qã7bÅŠعs‡ä².®q.§' <‰ÃÂÂ1fÌ$%%IO¿5¯aL K H}ÂB12²*…ˆvÚ¥»Ûõ:Gç‹z õ5X°v7ÆÓÜö‘Þœ]LhCo•r”ææá«ïÂ'*qÁ$ùÚp¼I{øS‚°¥1ÁÁÁ¸ù曥¿–eüʘ`m# Óè Ó(8>¥M“~Q“"Ù0ZæÒÛ&‡7‘o˜êBã乆b?± 9ºè½ØL*ÒrZ!&\‹ Äg1)úäý›7ç—3 v"ì™Ä±y;ñÑîBBR,.N>o7\$ì%Ug“ÜiNìø¹ïŤôæs@?ÒzÅ¡O|9%3 Ì[bèYœ>¥¶ŒO냿¤„ÁÖP#ÅGQIÁ“Õ”9Jç2cyQ¶‘·r]ÞÈÉjä«sG< ¿±=â@)ÚIîEux<¦ôžÃÈòqB,®Ë¥5L ùjôè—„ÁéÜqä&y¢·ñ;|¸+å¡+×—KÇ5—¦bç¼ùXKç'4=“GEy{ë ¸vì`ôŽñ G’ƒ¨1Y` B¬ÜÖ—ê‘}ƒ Ä‹KŠ`Oï¡”!H…¿]Û±z2öÄvŽ*ÊÚ•èeÆÏE , ¨¬5ÀÛË Q‘јøh0žxof¯(ÅS÷_‰ C¡—Ññ<˜E@n?7 E ŒüùoŠH—»`Á.|÷ív\{m„$À¥qùóß– yD„’>› 0îK€ô†“†tE~vhUˆô÷FH½r¥ žZ-|Œ:xºQºN¥Yuøaß>l¯4Á]mDoz€›•LJMÁý}{ÂH^¯ÒóKš§ŒÄú£Ù8ÔhY,¸üy$šì”ÑC.×a–8Ú•HŒ÷DÀJr4vA©ÖÀ×Ý5½WÐ<>5 ‹möŠŸ³1¿Ö{RJ<›‡¢ Â¿F5‚C©.nvP¦—]Žð oøèê¤ìU>^:4ILz(cW ²r q°¸F/ø&ëàà…É׌ð”Š\C絑®z’O ÆšC™8ZZ‰F«?ô"$ΊAQä óðÃãÑ£Gȶìê«Èê; ªIäSÜn\Xvã“Ï]gLàÂ3•l$"\©ÀåÀÑšzTPÊNƒZM¢‚¼ÅÐbó0qt@8¦û’EŠöqÑòS)f­Zu‡s0$"#ýôÈ«,G#ÔÕ)±—’»ÇFA^Q‰víF( _&Éê!9š\¸>wÆ#ˉiUE «V^Œy+ò H€ŽÌ°%ÇjQï[ºú&4Ô¡¾Þ†ÔøhxÏÝ€Õ…U¸ä¢>ˆŽ C\B‰@tnës3h»ì/ªDb5–¬=¹»Y©Î2 k+QVV‡à°@Ü0)¸i?Ú×QS §i=Þúz%ªÇ¦Âzô44ÏPQ•‡Uõþ¸oœ sý7 7 M gž'@:–|_HP’ ê¾EÌÇug·÷»bAØ}¿Üs&À.a“pSÈŠìLèåvø¹j±<¿–† {"NaÁ÷äT²±°UÊÄ(KÅpÊ!ï)Íó{ÒH¦›®ˆ‹ÄŽu”$àÛ¯‘DÖ$¥Á}Ba+/Àú Æ%Æ#ÊiÃWûv ‘„çƒýû Å]'íÏÿ´€Ðå–†*ü´t6’ÕM{Æ&Â\R ØMõ(Ì-†ÅÝA$¸kL°’»´<|¬Añ6B!r¨ 5/ é™R‹¾}#i¿#øÏÁ úQàƒLL‡(½jH(‚(Õê#Eˆð$ ¤òÍEaôÇÓ‡wç®Ågs–“µ².&甲F„Òqb’0Ö©EUiªëÍð !o¡A¹0¶`AØJ¼ `Là\†inY¯gF]c=ÊmükL ö&/Ò\ÝsF÷pISÀ¼Œz¨EXIPü¦*$öÂ;>äLp f…ê•!¿Ú£Éüè¥Õ@„q$2U* Îäqz®úÖÕê!5(§¹xa±˜rebƒÜÈU#9ÙÉ !55‘Œ®ÂÌôO £meä˜lGI~>òjuÑ/Zæ¸|(AÞ7S¯¡‘|LÕ©…’†›VwÜuK$&;as¸hHø$  ÿ‡Å%â…i±”äĬɴlX:9¸P;ÄÕ1jPOºn\ÉFÆb°«]íÜ„í ˜«gL€ œH€æ›Q˜˜AôÖñf«NŒáéŒH6´òm×;± Ú‰""Æ? 1AáMuˆá`ÚÖ«e_±_kóŸÄð>’³Õ’ñÆ,lÝ}54_‡ ‘„¶®"é´)DHéÒb.G%>z}JB’0Ò—¤pïç¢U‘bOŠ})&¡»”ý•ÖSú;áù+Í¥zÔ´þ”EÌi¤cëÄñiQäâC˹õJ•œro^ÈNK€áiÑð &À˜@;8­@#q ¬Nm)BDž<ý«u½' ‘¶TÉÛ4 ŽJr½þ†‰¸özrþqÈáfÔJÎ$ÇõVëÓ$ÞÓùPj¼ñÏ'n†Š„£JÊ^Òz£Vt¥Å§XwŠE­öjz{òym½Oë÷¿Û‘0Ó`Axz6¼† 0&Àº1YêÜÉÓûx9Yˆ_Ñú  .~²U°õüž tD,;âYá61&À˜Ày#Ð2„Û4ú &¶–aÙ¶´ìÏlÛ–úÎÕ6Ô®¦¦nLú\ˆëélXv¶3ÆíeL€ 0sHÀ+yõš-v8•×OèA²J²ææu=Ù$#‡;M?䨔çð"êU± ì§‘;ÁÚƒÀ,%­'ì¥Ïä_IÿHOÓVkù-訜R˜Ç²ªZLç;x¹ÈrFòÄ ÃEñ!éUøm4Y ;jή] ùpÞ1$ö:» x¯.I€a—<­Ü)&pö„mÄFD‚‹3[GÈÒ`WPx 5œr%,N%ÌôG‘“Ïþà¼'8_ì2ô Ƹ¸ZäVÕÀe¢P-ôƒÆH^ÃËËQDé…`òñ&¯âŽ:üûÿ`5€ú>~\2DZC.L€° äË€ 0f4tf“cÓö¼ñ^2ÊËuÇ£iœ‘ˆðoCa^ÔUÖâže!0ªÅ>m³,žºÎ¾´Eëvá.þî ˆ>wÑþŠa¥¤siè•þóÒ™pUôR¬Íš‹½ÖR¼<ój ¥ðî‚E²ˆvÕ“ÛÏW{w‰a{æú™@'"Ð`V`×^o¬^øô7S”Ó? ]dT)¼Qb¡ÒQƒ Wu'êíY4•„‘nsRH‘êêl¨gq„µ‹´,£qSñÚ•‹œRÆÕ×y ";K÷†ÓvÉ)žP«•-Ù»h÷»¨Òï¢g«½»Å‚°½ sýL }=#cðÄ£Ù;¬Š¬†§´ØEÙª«MÈÎ-£Tl]{’ºY‡K‘{´^A¡ðñóëÚ¶¾%Ũ.-FlŒ"#|»âÈ©ôíTS^âÍ{|ñÑGîèÕ«ú÷2#­—RRCšOw¢ï07• œ-„gKŽ÷c]œé¦äB ¶Èàíí?_®n,¾’®mT!uÀ`$ôêE º®…E8UdlÛŒ›× DÿþÑMN§½:ï !Mp‡›—FNÁ µp7Z©Cââïºç¸óž1ny{`AØT¹N&Ј\®ÎnÂBh?YDEí4wÒj±tmAHBHôÓICãNò0²Ùœ]ÖB(f:ì)°B²86ú‹0§±Žw£ï7wµûNõ\˜`L€ 0&Àº1„Ýøäs×™`L€ 0& ° äë€ 0&À˜`Ýœ Ân~p÷™`L€ 0&ÀN%| 0&À:ŠýG?Õ›²è’SeÏpQÜCÉÁAÄŽ’¯ƒp{oh½äMo¥Ü»M{ºDÁVë…¯lKÊÊãâmCe­J}_ P(ƒáéyUÅ…¨©‹ƒ—¦UµëàfP±%·;_žÜw&p üd=^ÔÈ(Gn=Þ|c9ôºüç?Ÿ#99ŽæUu„¶qZðóóÆ<ާžºß|³ C†ô‚N§é‚CAsî$˽4l‹yM½9•USX÷ÄöÂê)æHŠWiY“ׇ¤Ýšöný/mCY.ŽíÛ»s¡ ƒÊ^Œ¬ƒÅˆLV&|°@IDATG½Ï<—ˆšÒ|4È|ÐoÔhxx襶ˆvˆ&¼î…#y$ Ê C ¬*|¼©~”zO†£dë”Áè¢y¶r<ƒ¼ ’9!7x#0Ô•™…(-+§Ÿÿ$L]øúÐzɧ¤u£ù=`Ýœ Ân~tÔî‹aG–#3³ £ÇÞH–Á846vÔÖvïv)r„†!)9 9Ù›h¾gâãÃ;Œ ó…t¬©iD-YѪ«M(.®EEE-ªªL¨7YhnªRÓ‰´e$¦h59\hµ*¸¹éàí£‡¯;ÊJka#‡ aîS’Ó†KN)Ïúܬ*¥KÄ£¥ÅeÅP£çð!P7äÑ6{á²ÑòBôþ HÒµ™[qðPMÞl@yA-ª©- µž¾~p÷rG@Xò wâhÎaø©iÈWk”„£8>ÉSø„‡AŸ•ƒ²üƒÐËüIôyÀÝÝ“£X¯ +c8T¹E(<’‡Ÿ'  ¢õn´®u{»÷õ˽gL ‰ B¾:$ñ|­¯·ÀfwÁÏ7°ÙšÓ!›Úí%ΕœÄ‘Ñ`„Ýî$+î…wú!=GE8¸¸hh¶–~XáÈ‘c8v¬•• ÔFY1 ðòò‚ÑèEbæéi4$òTÔ¹$òl4gÁb1ÓuX'íWS“GŸÉsW SÃbdg!*!A‘Ðë ’È’æŠCñÞAÂÌi·C©ÖÑаV›‡L“rR¶8,°’5ÏI¯5å¥(8Z •Þ‹¬F¸yzÀ#(>^ù8´{£S‘œ‚ê& JÖÍ¡~س3$rSúö…Ñ]CzQlà‚Þ#¡þFìÙ½ õ‰èÑ·ôbhºäž„¸0&Ðv,ÛΊ·¼Øq!¨ÿùcv ó$¬‹œJ°uk($¡*'˘BÈábðàh…ÀÝ̓Âê(¥ð: šã'#Ùäè!ƉɆ&Ä ; :a94%¥%ÈÏÏÅѼÚ¾»7l€—Ÿ µtÄöìI–D?¡C!Óà¦Ñ“Wð ãÐ(.ÊÉrêFÇ•+-È9’Iáaª`ÊßZ—7"Œ>ˆJŒEt/¡%Å06YøH´)5ä„ÜC‡Ä´Bøø“uï¸#AWjÉ㘭öA¹K ohv QQÈAE 1GB»û‰QŒ4WQy|½´ÿذ ä t@bŠš!'|3„®¥ˆùkb˜ G:…ù¿±Žôd=¢Ps'Ò;"f1Å ;±¾6êä„UPL58t¸ +–íѬRîõFÿþ£€À`È*è ±% žð”œIZ_5ü¤só™8gXž3”\ ±F¢‹4Y“þ?ˆÊ"ìù~-äZwøx{0ðÅ£³^GÃ/oãƒÅë¥9f‘iSñØQø×Œ'±ë`ÅŒS!˜füßóÈLôõ+Á÷?‹‘÷¾ˆ›.îêÜ#xïéGà?æä}? w£ê±'ñä“OÁ|øk¼ðß/QYo†²L »ò/xôî)ÈXô!^þð.£n¤8‹@ü¡-lA.ÔïÃÌǯ‡­¶—#/»Óº,Nd­ûwüå[ ›R…и¸çÁ§cPP äC˜ýÆ=˜c3¡Ñ®ÁÀÑ7ãþÜLiˬ8¸e>þ9u=LÕ•d™ŠÁõ·MÃ-׎…Z–:©Zp.›¬n;wæÓðp_ &1(E¤P0g¼ì°6ÖãHædÎ1<AÞn8VpE%6rä…³þŠ+ìHL‰¾½"‰áéE¡8””â¼…‡EaذÑX¶ügdìØïÀxSNLìÒ­ÉQåŒÍã•L€ 0óL yì<•ÇÎ3a‰qØqxÇbÌþ|-&Oû æ~…aQ6‘ªUxf!;?£nzÓ§ÆœY/bg¶¯½ó¿ø¶ ¼÷æ[8F¦½R²0VU7…G„1S ¹,85A¸ìú[ÒÓž}¾Ž˜õò‡è;éi›ñÔ]Wcí—obéö\Ê\Q¬œ,Ä ¿Ïϸú&JÃØôM a /S‹Å°þ×â³ï–`Ú-}ñÓ¢°~W Y¿䡪ÀÄûßÂìÿ¾Š@Å!|<çgÔ6Ø(± þñÃñöìðô}Wb/‰ÀVdÒ°&Í¡Szbì”'ñÅç³1*ɉ¹ßÌEnq#­;Ï'ãNœÓ:²fd“%Ï &†mÌÀAC°z 7w£Þ1HIJBThH;#*¹?†p6ïØˆc5æÓ·îŽÐÕâÜEFFÓÜÂ^¨#Á^˜MçËÞ$¥ ZïÁï™`ƒ@'}t xÜŠÎEÀEOú²Òl8c†bâèQ&«Ðå§ÂŸæ€9I]¨h9©ç(Üzë8ø8Ð`€+ÿŽQ©¡p7^1 u™p¸rƪI ˆ¯©ú_©ÖÐ0 †‚S 2­>$Žà@ Õ]‘ƒï¿ùûŽ”CÑP½¹$$µˆM쇿Ý¢BÜQ”µ?ÌŸƒù?|5[R{È»4¤®¿f R¢Â1üâÛCíÌÊʃM©AøÀëpýØ¡ˆ¡:†öƒF cRb²Á+(c.›‚Éø¸ÿ¥H¢Ti…2a¦ð#1©Ã0qü¥ˆKÁ°WCCN(ùe•Rû;×™lj­°jÖ×7RŒÀzÄÆÄÃÃÓK ÓÖ¾A)—S 8Š+¨¤üÁršÌ©RªIpS( JíîåKÂ.ò² wo±CŠpÞÉaá”zŽâ–K!kN7° Uò&L€ 0v'ÀCÆíŽ˜БˆXsjòèTÑC_ÄyÓèÝáIN"<‡‹¬Fj™›ÒEx“*5*xúBF¡Zäæ+7\ÖD8 ϦB€”_Öb’fq óY‚D}´žêsʹ3Jrš~{%ºQ”õ54C„g‡Ý‚ümëÉrù9¥&3")u î¸F#å«Õ‘X‰0ôŸU„Œ¡ã»h^¢‹ ïc»°RRo1 )æ±éµ:j# KÒµGMÖOáQ ¥ÀË ò˜}ö*»¨KTÐÒ•æu¦1PüyyùsÎÌlCÇ w°š²Œ*¯Ä®={àð‰„»œ>«?Q 7b®¦ö5êÖËSyÿ‰*Ïë¦M¶•'Ôy=:Œ 0óM€áù&ÎÇ»€„)i±œ”’'h¬¯y»SO>Òk$÷šÿ‰„d}ÊÚ² –©#¡£ %ÇêHÄihxòÿØûÀ8޲íGÒéî$zïÕ–-ÛroqÓ{o„N>zËO(HèøHR Ò«Ø)¶ãnÉ’U,[½—“tU÷?ïœÎVuäû´{{»³³ï̾óÌ[C¤H‘¾ÖæRhp`1ŸÌ[ƒ›¡>ú„±ëÌéøü÷Àâ™·Ö‰ò²rX’“pà¹7y6¥€¼€0,ºü“øóùñJ* 4xŽêP¦9ki¥³ cçu·Â!ö†‘»Ø–êjtShd‡ÝÚ‰€ˆî¢ÇÚÚúz™g̪a÷¸˜é" Áîj´R}ÙN©§)·•áNÃÄåxx8gûï÷Ux?âè#ªZ¯õåèÈÃ>]Íè NtF!Θ=±¡”ôr!0œ"ÀRⶮ¶×_¡™BЧNñ þH º»z¦ò™:äÖ-Õ˜d h@8É o?q AvÞJd¸ÿŒ»~ümœ7?Ï>tàlJÿ(1sP5Hi“»@/*±ß»ÿ^|ÿG¤êðÈ?7bÉ¥?Á¬Œ,¤göà©GîEpÃRlùçïPíéQº JššßÇCü W¯™ ²¬¸û{_DÍž³P³ë5<ÿv)¾wï#ˆ"¶p0-šb EBåq[ÑQ_–²øý/¿kŹ(yõ/°š’P8; ¥O–¢~×;¸ãG‘˜ÓW^Ý„Yß ‹» uÅ[p ºá½W£qï«8ÜîÁEKf£§èM”ïz6ßÁÚY±xë¹Ç•}rÒb—NÉ"àÞL@aTÙC,X¢¤ £A/‚× &¤e0°ôÒ…+e':\"p40M^s%7Ñ‘§i¨g¿ê ‘œ''G"Ü2%ÛïWÄÔÑðs h@è礛76‰^B¹dlÀô(~ä‰g°«¨ë®ø ð츨‡Í[~%.Kq#D0@@4ÎýÔŠËÃóßÀ!w(nüú½¸æŠK܇›¿ý[ýã ¼³}–^u;V;Û1of*²âñ±oÆîÃa¿·ýöQüãÁûñÚsÿE\ú,|ÿg·áœ…3p8à \q}¢ŸPÚ¦>|T9¶%6k1¾øÕ;i›Ø‡›^¥ÚrîúÜ-X”ËÅK¯Ä­I—ÀÙx;Šš±þºïá†+/@ µûìwa¥cI£Õ˜‡¯Þv3Î^‘…2Óùøú àîmÀömû³æ\{ÍM zMJ)§j‰ˆE^^Þz«¥täXpòp3G”²\ÝÍttN‡ NªýÅcÙM‰ªÝe£Í =K0ZB ·ˆý`{G;JJŠÒóÏ_ÉpCѬ‹=ìâ•jsT¨1rìro\AïØI½Çj:Õž‘v :ÞxÇæ©ÎÖ¿k h LU h@8U{N·{ØÉ3І¥g~‹×„s¬G(E{÷Ù7än¸’Žç‰òÑ–€‹oü.¸ákê^°ºO~ç·Â—cîòˉà(Q’ãÄ "M‘IóS_¿GÙöQ“©L_»ã~J¹K )± ÌY´9 ù;+“ú|EîÍD¸H™½Ÿ#x•XÃÎ[ä˜÷Z¹ÏŠ >ƒ3$h!ë”ziªè­'vnúìå5,!ø¤*  -÷[°æ,ZÇ,ò›\#Ÿ“…êóží¿…V#0??û÷×âÍ7_g¡x¤0_°ëTÆk»;›qèPºèíi*GYpÚ;£‘ ®¶²Ñ!…Hˆf‘a¹@µ—’ß]»¶£êp)–/KÜ9©Œ)ÞÏã¥Ë–ð9N'íM9‚é]®Æ‡ô!Û$žË’ƒÙȘ†#›Ckg¹ïÔRwíÙôYššÇ( á1Zè=?¤€,…†Æ¨m2±‰DÌ'íp9~Ähaàf/ óM¸r;š¤¦ƒGËÀ¬gôÇðn•0É÷ÕwœßŽà½|EÁQP0à<ßï·¾óŽ^Îó}&lÒFßïrØ!úŠìjÑŒM•]#Ï5àÙ|×t;Öý4ôvx\œÒÜsçà_ÿÞ§ÿõ8÷/ÄŒÜ|Úf -ç•ÒÆ“Á¹g-\…‚%t0R§=HOIÇüEt"  ûÊStTÞ”d°³ï¾³»÷nż9‰Xº4—€Í @ÝnŠ/âÁn³9,³¯Ý˜??“cV½ jkïÿMÞE‹|ÜdWPxŠæêŸ54¦4 œ8]A&R™{zº”dm,ŸSÍóF¤¬ÃO^€æ¥²Æò^Ó½.Á)nO;$Õ›á[†ˆ£Æ„4"ÍËKÆ—/Æÿû>žzêïX¹b-.ZBûB†¢!²÷J¹€;¶ÙÀÀÁôâV¥s¬AfîzÕÊÇŽ}pOÁ3JÅ©EH**˰eóFT×”cÙÒ,¬?s¢¢èñ= £ÜAhØÃX—ï¼sï½WAP™­¤€ï.’ÁÞ^¶m«DggV­Î‡%Œ9¯‡y¯uê}MMÓ›žÞýï·Oo0 =éÕÒ,ؼyš›ÛZ$š’–1l2ñ‘ž¶IÉLüËý‰1cø“ZU¿Ð (¯(A\l8’c•ªs¢&ý& pæÌdÜ|ólÚx€©ì^`ö’m λQ‘ÑU&&‚C~úDüËrb5èð8àAHŽcztK==ôÚ>Ì`ær¯òòˆ5ãò+¡p^ºRg ±ÞzÀoÚT‚Ío•ÀĘ–‘‘¡G¥ƒÞ¦0Î!UÈrÜAG¨W^Ù‡‚à gðØðè€ÇÓ»šš§14 <;ß¿ÝÃÉ57Ý´·Þúoüüç?Ä7¿y;ââbµ$ÏÏ:®¥¥‘R¹GQÁàÚ·ÞzbØoT&²xÁ¼¨£pÅ•K0gn¶l)!°z oQj7sÆ,ä2xurrв3 áB€ò?Y¨Å@ÿŸã6YBÙ‘‰v–6¤n:Œ´¢±±žžÍ‡q°´õ µ”š™®.K—å >.BI%‡Uõ”667wáµ×‹°mk%€vÄÅGСÃô@èÀ`Ø 0êê:(™,…ÝæÂYgÍF<¯νûÐú ¦€¦ÀiG O».Ÿ:€k¯]ŠúºvÜu÷ïðÆ/cùòEœiè¯ÅyãÔ‘L™;m+Íf‚ûðDE@ŒÍfCii *+àºëÎÂÕWo€Ëg¸x¢ Çñ¸x Tr.¡H kjZQ´¿{ö– ¸x'%kf:x„q±‹„„$nã æ¢8¦"ø¼ û#*`þ“ñ%jp›­]Ö´µ¶¢¥¹‰êà#*>¤ÃÑM‰c²²â±ný2̘‘Ø/É#häuÃ)b!´¬©i£´o/ví>Òoû ŸxøŠy È“ïáƒa ˜-ûv» ï¾{P©šÏ9g®’®<8íÑçj h œžЀðôì÷)óÔááF|ýç!7/>øíªž§ÚØ-Â]ÆzbX{ °v p\03ìÍÉŠ€¯ÔÔD|ã×ãÆ/à5Â9rœ¬¦±ûMÖ ^`€ììxÚÎ:gj ¸*ÊQ[Û†æÖFU¡»Ç¡lñÜô`%àL:Lyè¡#ôó‹áá&J¨#4“‘‡ÌÌ8Ê0uœ§ÔÑ'ÆÏÇ}@¹Ÿ€ÏÊÊf¼üò>‚Ö~¤$†† è£sË "çéH"¿‰=¡H8¥Íâ„"¶…çž;Ñ„AW鯚šš'¦€„'¦þÅ/(à¡D'×_¿×\³ˆª:k¿}š†„cÝ= ÀÜÑeÀ¿žÏÆOæãk_>‚5K;àd\ÃQK(bb û& l§ʱ`:ºää$P’—¤€[o¯“Ò4%kN.0\°ñ»Í&ûü'ác‚ 5Ãİ.Fæ6O^‘ÖÉwÁ}ò¼J’¨\·‰û*ÀõàÁ¼JÉ`)·êÄ>PÚ.afD],N:ƒ‹Ü×0˜sÌk( R®/)©SmIáÌ™‰ à¾^×ÐÐL SD÷S 8•$$99ÊOÛ7=še±ÑÆ.‰`$ñ‰nJÁ̰;N E2%àE2¼ø{Q§ûc÷˜€' j(EžÑ'…s4ŽE‘{ •vœ±j Zæ`s(—4(կ°p3߇½¶¥=&æÚöF³R_zz,ÌÏ ía8R’G¹[:À¨öE«uššÓ•Nמ–Ï%³šžÙƯkI[O mç\¤²[Åâõ¯Û=4Ð4~íûšEÂæyc_ûÐj”6H`mQí%©¸ƒkªÛXüfD%’^åÜÅ+! ¢÷÷¬ì8„Qš)¬SS£ .Ó•´p$jì÷Ñûšš§4 <}úZ?©¦€¦€ŸQ@€Ø/ Þklì¤çr+3›¤!3+Ž©øJEº9¸ˆD04ÔˆY³’œ:K×_ÞK)c¯U'ƒpäà*ôwMMM£Ѐð()ôަ€¦€¦ÀäP@$yqqá¸øâ0Ð&04ĄĄÈ~uñ¥ƒ¾н`BBVQÝ,¦bÏyÙe‹”Í HK}×é­¦€¦€¦Àñ( áñ¨¢i h h L|vŒ¢P(êbñZNK‹V-àw¼"€O¡\/ûÜйǢ¶¾óOt­ïw½ÕÐÐðQ@B%ôVS@S@S`‚) R½öö†À©EígÍJê÷*>><^ó|’@Q3bSÙÙPTTƒ³³ ÒT\Æã]§i h h ¤Ù‡.šššš“A± ”01om.aÆ‘6å2ºv(ïÒÒzlz³‡·(UòèêÔWk h œЀðtèeýŒšš~GQõJiq&IIŽVéöÄöo4Elc,{ΜTå¥ÜÂTx/ѧ–MÝúZMMéM­2žÞý«ŸNS@SÀ) ™H6l˜£ÂńБd,lþ˜ã©Èc8¦dZñ©•ý˜ºišš“L '¹ôí54NO øA|©éDº7EêIcHˆH½`Ð{/ Ç‚¾ºMéJ §kÏêçÒÐðK ˆúV2»ìÚU…ÎŽ^,\”ɜȖ1m«W"€2f>)/obʾD•ºÏç‘<¦7›Ö•‰sÏô Ì>­»L%/Ð}6’>Ö€p$TÓ×h h h ŒFœ=Þ{¯QQaXȼÉãÔä>ôúÀ:ttôt†!22l„­>/óàí÷ñ'séð ‰þ£a†Žl7ûpÓGÊpöêæ—£º ‡‡ZúÜ1 €¼¤úEBŽ¢ ß´æÛŽ¢*}é°( ÀO<‹­]6DF„ °0¹£ 6NkpX•:YÂÐdfÆ¢`N šé\ÒÕe×€pNõõõM)xæévšæ#"¼„#"›¾høðÀá Ä›ï%á‹_ZE–qøUè+Æ„IÉ=øüç‹ð™–ŽI}º’¡Q@¤ƒ>Ç‘@b3“abÒË{CÙȽE)Á°µ¤kh}&g‘Z0ócèƒM»¡Ón¢Î òö‡¶.£ €„£ ž¾tx)E}C(ššÂpŵÍÈÉèÓ­ßàáQqg3ZM­ o½C•’"M—‰¢€O^| Šk'0ùùIu{ØéIJ•6‹mmÝX¾<II㢦ž°šàù@à9‚Opë§ÿí¤|}4ýŸvüžPÂñ£­®ù8ä ;6Æ›®­ÅºmpØulôãi\1Û-Þߎò²`ªëµNe\ˆ|‚JE:ØÚÚ];£¥¥‹¶ƒãâHr‚ÛS²E9¥ƒÅ£¦È‹ÍW)ò¼ÞÈ'ºJ×Ð8( áéÔÛ~ò¬EœTÛmüЖP—‰¡€Ð]èí¡¤P—‰¥€H/lv'$æ`Fz =âØ€‰åúÄ~pÖì´ÓÛX¤•v»[ÓR•‰únšþL ý¹wtÛ44¦<|êâ”ä(\|ñ¥ÚOg’ãLlãâ,¸à‚ù´!D¿tðxgêcšš§+4 <]{^?·¦€¦À¸S@À Ät¹Üô(˜T5­¨­ƒƒE:@ ¡KS“ÉÀíÄH*ÇØúšš£¢€„£"Ÿ¾XS@S@SàÄ`Ð鯆VlÞr±qáXuFŒÆÉc» ëê:°ys c†bõª¥{<â ž˜*úMM¤€6àòÇ^ÑmÒИòðÕÛëÀž½GPUÕŒ°Pc,ÀÉ{4‘Z,Fåв_5**›4œ¼îÐwÖð+ LÞRկȠsºS@&oþçÇ0[ö%^›¯FÍ£&NÙ÷©ØÔqù£Nó^/_¤® Æ™“­÷Z/u=ž>õÝW‡–Êxé2ÿJŸ[,&,\˜‰Y³’U êÉìw{K1rHˆ‘c\œŒè.^—)O/óñ2/òõ­¯‹Å„ÁËü1½<Êg6à»Ö;ÄÞÔÇ…8}Šyyy¢Ô!cÙǧ<ñô@B=N x™¡´c”r\šÍæ„Õj§DÇÉ}= ]´³rÂIÛ/±ÿr8ÜÌ®â€ËÑ7™ [‚ü2™0D&›ôÈ-?üb0ÂÈ fc’`6Ád6ÂÌã!!üPZjVþrÿ~>«8­ìsG;-:g?dh¨ gœ1““ªwL0è#³Œ·¬¬xdg'¨±ï zb÷QÈ·ƒù9™â!Ò‡Â7´ íî±£‡’i;yšüLìEN'\äWÆ£”ãò4I¡Èõ Ü.ò4îð'ü+ö®ÂÏLäU&z¨‚ æ*×d&/#S¼Œûaaäca>>æmƒwùø—oë¿4Õ-;F ÑBïM3 ‘‰!¿€+ ³a·ÙsììêUvT ÌñÚ‰V~·‘‰Ê92I·ÓM°GÐÇÊhÏ0\aÁ¬O˜f |ç'@¤ˆäİV@¢0VŒ.n%î›9'À@Æ-`1˜HÖLcþPs0""B—1 ‘/Ô¨¨P‚G#SŽ”­o©@ç±U½ªRÿñs Èxu±@3'N‘Äù[‘‰[‚‹€ ˜‹#'~ºø8ŒúAšð±Õ_.TNsSÛÐØØI^ÖÎØ–V´µ÷ÀÚmSAÈÝNò!‚>‹|Œý+|LØ‘oÉÂUøW y™Ä¨žÆ&>ÓGÞ%’@»Î>Ö!×ÊÝyžŠä_+²/ãÆÜÏÇb¢ÂÈ€çI‰‘ˆ‰ W _ácò‘ñ/õ)ª™ÿ °A-Ñ€pAô×)JÅ8ÉØúWÌ.2Ɔv4Ôw¡¹µµµí¨©n¥z)é3 °M7r…Ëm’)Rì0™j2ÂÂt_aÜ1PªGæ'«cs 0aÍ'+ÂL‡v7j/·VÞ³ÛaçÖ‰n»v¬¡]íVÔ—µâ]G9z%QŒ@jJ4RS£Ÿx:#ÈÖDæ+¼T&rßödíпMÄÔ ³Ó†×_/RÒæ g }êÒÁ°q°´Û¶W2sJ–,ÉR Ÿçèý‰¥€zÁŸ¬! µ‘G4‘o55w¡¶¾µÕmŠ·uQ£aàI–`yùùVJpÂÃÉ×ÌrŒ¼ŒÇ-Ü%¿3‘™dK^& ‡'+²6pöQº(Úò1ÑX)iì±s+üŒ|¬ÃfS[ku/J+Û±ÝQ¡øk€!qÑaH&KO‹ñò0¾EÂ(àö˜ ^…œ¬&ò7 '’Úú^cNYy(}é^gW~EÌÆPQÙ€ö6¦Æ³¹à¢CÌH‹ŽÂòYiH·@€_™¥YÔ d”²õƒ°ÅV*Ð5`Ë# „ å„ÑJ}æPÙž°>~xL1a©€ûŠÙº¨¾q0örÅo%`l²v£º­U[q`W5\¼Ø¨$‰™±((He`ãX2ý®À ”îÈJ^Öïºø¼CȃƒëQZZ§ì-ôâï%1±Èâi÷î*¤qâNMò;àêtûÉ"B¤v» ô깘-%X?XÖ€æ–Nج DI_8A^rtÖeä 9<‘f3´äcz²`ÀǪX˜7[-½üL âýüm(O Õ˜ ©6ŽÃÂŽò1ï-dùMÐH¦øA¢<­›[ác5í8|¸›ö5À†]2",ÜŒŒÔX$=;;Qôt[V¥aa=ºL.4 œ\ú뻀2éú Ûú*ÊPÂÉ·¸¸íí݈6‡ 9"³’ÒÃO4¢È8¨ù™ÜÖkëâÝ CË¢æÿÌ—ûƒ‹´E¸0ó˜PÕ2ädz=Y>=è"c=ÒN¦ÚÖ¦@bùžz¼ûNÂè¤?+³ó“1sfÕ38¼’Ãßgð}õ÷ñ¤@€R‹IF’… ²0cf"ÄŽÐߤƒB/bª°|EM'¬Juxœa:žÄ:­ëöò1ïBQÌ ª5£” ‰¢¢ZÔSh F¯s#’™…̨(Ä”‰¶BŠ,0Ua§ùÀž€³±,CåcFYX‡„mS^\ÇW†jJùXug'ªZ[QÓÞšÒìxÿš¹yñ˜ÍEúŒ HLˆR&>ò®øxóX>‹®ëÔЀðÔ4Ògø Ø—u”ú©n¶­åØ[T[—‘F3$'aîœDÄYÂ4h cQ Â4}³o;ÉÏõf{œ6‰:»0) óS’Õ*¼“6-ÝÝ8ÐÔŒ}%5Ø¿³¡Q&Ì"(\¶,1Ê^GÛN^ÇÊ-iâf3Mœ€vEþ}[•W™K%1#óNÄ¢ÎÓ Æc+Žb»ÙDÀ;ª°{_Z©6cf|<Î_œ§µ‘Y¡ý¦*¢ p³_¦MÌ,>SAb"KnµÐmíéÁAò±½Õµx¶h¨ÉÎŒÇÊå¹Èã"*‚¡‡ã1úN\§„'¦þÅ( *1H.)jÄÛïî9ŒÀ`ÌNLÀ<¢™ ñ”´‰mŠwRFâ úb*™’ÅÑ»ü‡REq‚ÈÅ93òpˆ+îmGªQ²»;vV¡`v*V®$CÍKêψ! u*S`jµ]$6V.NzzíJ¥/ÆôS¡¨I—ím½jŽˆ0Ôê &ãÑw¾PVÕGøîn?„íÛ+àêu!+:k šˆ IÞqeß×þÆZê7Ïu²:e|¹„‘—‰LSìÃi²#Ïõy¹”v`oMŠŽÔã/Eo";'Añ±ÂyéT'‹t]É?Ov ýÛQ`jp¬1zX]ÍÔ¤€x³Õ¾òÊ~H0Ý(c.]€y”œ‰zX¼]\yŠ$p:%èGxòÌ3Ô§™RÃâú¼S~-ÞŒüÙI8kC23ã¨Âñz)OgºøÃ³ ”ðD;vR¨Ï8ccfL @.m•åÛo—¡úH3ÖŸY@S„d-ã%RÁÖ–n¼õV ¶n«@ Íƒ%ééX˜–‚š¸ˆùˆHÐä=Iàt,òTjÒÿ|³£¢‘CÓž \äVp‘ûfižzt+ÞÚ\ŠsÏžC»é4e_©®›ŽDñ£gÒ€Ð:C7åƒ0ã¤Áû–¥xõÕ}0÷áò9ó°05E­2…qªÏÛÍ|°þûÍ€cC™‚,7 RS±·¾›+*ñ§ŠMX³v&Ö­ÉgX½Êï^”±*)áŠhÂ`±˜•‡øxßs¬ê—‰Vbc¦§Ç ä@-öî­FJJ”zVwÕów=^[?vî8Œ—_Þƒ®–¥¥byf&â-ÿOàT—ޔޮþ…¼¨ÆçÑDf6ºeMxµ¬ =´…ÞïÙ8ï¼¹ˆ‰¶S—ôfúº“R@“’Gÿ8YPR‹^xaÞaØÂäd\0{ÃÕ ÁÇD†Ó>C cgÑNJ´*iÛp.öãsÕ³pB 3ceVrbbðFY96¾VŒ¶Ön\tá¥ÂôH?~”)Û4’Ÿžà&,]–£2ÄÒ{WŽM€œœx¬[_Àø—Œ·É|ËS«ýþIiácbêòÁ^yeâ a¸tá"äÅǪ8€òîÏ „±ÿè, ‘HO'ˆ‘E£€öàé1ÑxçÐ!¼²­T…ܹæêeHJŠÔ’ëqêŽ#quÕ#§€¨°žûï.:ŽTàÂYùX›—‡à~ÕððkåÌÌ8ZEÕ•ØÙÔ…¬D®ÎÓãކ6vïÌo¯g'Í—ÎCZU$^Ù]ª‚q_uåz'›ý¶íS¹a2éËe¡4#LL¥¦Ü¤å•UJ»Úz<| þ·CÑN"a}“*â7^Û¹1I8'&bh#(àG áTÍú<®^l­¬Fy§sÒ30#:Œº5­Šð1‘–ŠÄðœ™3‘‰'wíÅc½ƒë®[IP(Apt ×ESÀ(à]Q ÜN;›ó 7̘¡VÓ#—p‘c’™†“·77¡œR3aÖÓµÈDc"x^š‘Žsòòq¨´QÙ㈤bú>õäô¦ #Iqxäˆ7XðT÷Œþ^:9ÔÔ´ª îâ±>_•q4â@RZR‹-›KPˆó€Áݘïu CÑDR‚[ÝØ‚FÆ+TÎ#ªÌÿ/>&š 9T#_¿hÚj»”ʽ‡ýu h@8>tÕµŽ˜4QŠ·…jâÙñ‰X—›«j­Aq ¬¦Ä$`9s¸Š¤ÑW|± e«<û|?p+Q  )_½[9Ã[|çù¾¨F+“ª\ãûÝwý‡îÉsTJ<¹ÿ€óÖ7”}YeKl°…))X˜”Š]ôB®¬lîOå7”ô9C¡€ô‘~úé­ô­TYxhÊ1Ëóå—÷á…—v£µÕÊqxì}™²6Á :Jj¹7éŠ3éM+A¤‡-ÐnÅ3‚Œ˜™œJoäh¢æ@ã‹îã%ÇøÕ€‹¸{ô÷ãRøŽ|õñ-ïæw¾š¤õ‘ú|¹õ]?ðرvx¯Ìã\>¤]É»œÏ¸†çÍžâýµ úÞ0å$ðCzP?8I¿é~Ð º Ç( ÀïHU ¬­=t”ÈRAXGËD%ERsWêék§”ìhá½:ºíÌPX{{ÑAI0=)ò×Å´MMÖ.´ó¸œ#ŒMÒ5u;¹ru9ÐÖkcþpÁ«½­ŒØ×ÏhU’'”ê·^ãPy<Ýpòyé\Nf#è²¢“J¼…̘?tõö ¦‹íd›»{;Ñ# ¿ÿŒánŠÚ¥€FÚÁÎ@ Ã›1ò ‡Û€i~¾LúVJi$ý-Íņäuå°š²ETœbÿ˜•‡¦J+/oT6pý¯Å”}®‰n¸ð‘ªCMheÀïy‰Iˆ U$ºðœ¦.æ_ï±ÁáfæÖ(÷q1¼VsR»É¯:™JÎÊTs¾"™œä5’9¤ËAÐN“'ûº‡Ù‘:˜ŠŽ²GÑšQRjv1½æÀbà½û8À{x\$’Jí„ÐiV[º{Ыì$ÈÛÔb›üÖÚ‰Jò\.;ùd/œ¼n4¯…8Ý,`T‰äÐpìßÏØ³¶¶q`{õþÈ) mGN;}å8P@òfÖÕ·!‚êÝ”ˆÈQ†’ñÀÚÙŠ×TÀÍ4O¡žì©i@fJ'9'ʪËð,ARsnÚ\Ì7Ì#J­¾éÛ¥Œ6ƒç,ÆÅÙɨ¨«E`H nX¼ëªcgCš™IÅnëÄó%‡•Nï蘟ŠúötÓXº®©Vç3V]Z4“ÁÛq ®ïvôbeþllÈÉÆìp#Úì}”0Ž’ ¤§LNj•O騲Ãe£kÑô¹Zè*áZ$x>Ó†„Nmé ¯güEE…bΜT¤2¿±D¯äÇw†Þ…¾ë2Á2¶ˆò†rɇÎu½µ§ ¯W†92«sr±6›yŒC͔ТÃÚŒâ¦Ftxéd27Ì+@"%‚Õ4»±÷qÑ[ߌ¾YÙXœ`ñ¦†£RÂÑc…'$ÏYŒ5ÉÑ(§Mb,í¯[¸ùá¼ßЦ´µÍÕx¾²ó"ç¼Ù3‘j¦”²G˜ÛÉûBcpþœˆ &è´áµ’½h3Eªvž5#qÁÌÕN¥,LGFc$ ‘‹h—Ó­UÆÇÈ2¦{Ž)9ue£¥€ÁÄxSah#Ó‘Dé#e"Â~z¨r-ïaƒÌ DÒÞÆŠŒ˜p61aaÑ8#'3£°89– -¹1!\Û6*ž(»Zñ÷íÛPj³`q|4¢#@5.KÓ²iA6mgEÅ`F\"’43hçM·½³ ÎÀP¬OO „ÏÌsSk UD‚%½MGð×­{Ð`AžÅ€Jª#sFO`f`ÉIHåu£B?›ÓÉ•¿!aF„ŒA£íã©~½Ln¢Z­¬hRyg­´g RÓ¥ÈÂL¤0ÍÍØGäÖ•çÖehúEE†ÒôÃMêø}$Ô£_1š¸8í ŽP¡j™A&2$œRGæ æoqÑ©Xœ’†EüÄE!%2†žƒi'í¢V’?æqïh¯Ác{ö£ÝTò¹ÌXnɳV2ni³…ÌŠ‹e¦”$n£‘ÄÙy màX¦4Êë‘Jïè^ja~øT@;À`"ß²`oÉü}ÏdÓ,%ÄÍùv#ò“Ó™< fc(r’ \¹¸å»!cOTÕÝ] ïdVª‡Öú¬áP@ÂáPKŸ;îiD¥ªvÕÔ(»½‘ÜTøÄér‘!{• ÂŽÉ”û“X´(-?jBW7ò²m‹% ×.Y‚ʼnï”ìÃÓ¥µ 6W–ûÊ—p‚ „ÅÔG[C¯}Œ0> ì"¾¼r_mz|Ö[2¥Œn¹7Ï“[»hZ£îB~5wFn,œ£«OíÙ‰CÝ´Áv8ÐA;h±¥Vm;ÚP^6Š"ÒÁ:Ú—55aF~ÕãÞ4¥£¨R_z h@x¢èC“Gá#’1!wV"6—W Š6(F¦tv!÷ cpC飯@+ ªeÝtâüÀ‚ÅAÀfç11®–¼¨ª]<_œIê[ê±·±æ,•Yèmo£êƒÙl.Ú’A“=:¨êm³ ö¦ÍsÐèÚÎ}a„!F3zº›ðFU¹»±ýpªé,ÒÑÝ}Gã@§ W.]Ž%©Ñ¨éq#32 åx¥¢A¸JHÇ"LL£ÕŠwy¿Ø¤pÚ¹¥³ND6Eõ¤€"'UVåeh'Uqb‚K—érzyVI˜š…CUÍ ­ÓªÌ¦×SŽÏÓÈ311’woK#44¯Œ ìû švÊ®®Vf!â˜#(·”õr :ÉÃd1ê¤&Å®¿ü®Ž‘·É{ЇÂwzq!3<­‹3¢Îo6ò9'5=´t‹³×‰nò5©Oœ7ìuNò±>‚¾8j,Ê£˜žçÍÍØTUMGZº¨Æ.­B_h n^ºö}L†ù ¸­´ûèÔÃvuY{ÉùnŒqáÝËg~©¨fŸKšŠà@—±§ÀfÚ±o„®QSÀGá!!Fœ}Öü­®OìÜ….Y„T:˜ +µÙ޳gfSºwÙуd2­F‘ÂQòö:™7ÓÞçÀ»•5˜kD©•*j®”‹i“ì-¥xÎÓ £#s3“©>k@#Û×EuobH Æ ›÷©lªA‚1™œ]teK;V¦ÄcMF3Þ¬Ú‡#µ&ÄRÝ’îÆöêz,‹Aéáƒ1|¦,O‹CmqÎêê`dþ"Ô´ÐÎÑÙM[F£wñï#η^Þ€zzH?w Ý.bnãHªZ„¾ºŒœ2¹Qº’?‹´Š Sß§#]åY% ßšÕ³ÐMÑD:[MGà;òÑpò+ dV®È%nÁ+R gÀfà(3Ô×PIùh;¸*½[êªðAY$;êèÈQßh$Ÿ°¢–& ­¶^Ñ®0ÁèB#Ub.SÕicŠÏ:„”£¡ËGP$fÓ6´®­¶¾^ìil„‰ü­¼¡™“Þòm­08»PÜÒÊ•\ð¤çbI÷~¼¹'ǹÉ1©¨jiÃÎÖ:‰ØðÆÁTÓ«.#"™tÜ›U0ûŠñÊþÝØi¡C^Gb¹`i‘\½tˆy¾¸t´»ìòÅHˆ×©GJÏS]§á©(¤Ÿ H.Õ+¯Z†ÇþÏïnÅ5……˜™Àì"dC P@nvR¾›„N®†Môò5zœèqÓ€€/(9AT·pa¬ÂÃ\±PÒI‰ú7PEÿ¿bQ$=ð0›ÂM>‡+ŸIÎòªBhÐYˆUlHé“2Ž%¼^VÖFþ¾4o>¤ƒêž@þEB0¸ 0S휗Äð.D„YÈXÖ“ïš9K°0ÇNµ1äèÄ3å5ŠöÃY\‹jE”K›š  ÇàÄ%/Bn^âé6)>En*ãO"kÄÄXø SÒévG <ó±ì%¢º*¤XÓéµ/|A —^²ÿü÷v<½:Ü–¤§+~ ïü©©H~Bž±ŒöγR3)Ý£æƒêR'%f§$Ö¨bO(b8é—¼„ê‹©6f§1ÿoíø¬Ž>X¹!Œç;©X˜E®"—°_³bâq6¿ˆ¢Y´Ï™4i¡} «cý¬ã‚y˱–ÚÆ@ %ïê¦&ÄÌÅ*RÑÁ 6Om°Íæ».Œ¯\¼’Ç{Äs7ÄžðM„ñÞ¾ÿÞ³Z›pñÅ T^c¯ƒÓ©)wz¶±yZ Ç†Žº–q @^nnþØjüëéíx཭ RU99ˆ£*XÔ¾C™”„m˜!5y™¥Á#ßfìÓchK#_ ÂФô3êPS,æPu9?˜+|òLo‘ |ç“Éz+µ¿o}¢E4›Ô%ÒVcÿ¾ÔÁP0‘¡Þ6yÛ"U0› ÃØÉ|ËÛ•cM#c&Ÿ:åœ4KÀŠÄüÚz¤ oUV!&) ×^´3g&©g&ë2: Ô×w ±±SIËNI…Œ]…Hº$îbFFÓ¯É×夳ää(\wÍ <ÿÂ.<·»•mmX—“‹ôè(Ácý¼è$Õþ\r"Âfòå?‚-„mÇŠzÁÉ „ˆ­3¿Ë¹áf.:% "y¦œod¬£E®ññ1u=Qß½<ÍÛô•¦vAþñ€ÅdRüDnG© ¦Î% äB<6Ü >”\vZÑl÷P‚é`¸®`Å£ŽÞû8;Òtñžî¥„rw²ùy\@IDATõ¼| ¶`®ºj Á`ëŠy[uœËõ¡QR@ÂQP_>žð ““Ï'>±›Þ,Ʀ·b'Cœ‘…eLË&Þ»Â@Dbx2!𨯸 MûËÑõ}0À|by/Pg¾Šn¥Ž£— ØW pðõ¼ªH·Á^m4ÚŽO@'WÈöp3ˆgÕs´v²d2H¹Gƒ_ï©«Á–ŠCèè³cÉòœ¹~ƒ ‹}›€çéÝQ@&÷­[ËQÁ`Íg=GÂÓ®‚üI†ˆ=ô(]³&K—f«Éyð;3"ÂNó‹Ä[;–åk¯^†íYUx}ã~<ðî{X˜šŠ•Ù™H óàïlÇ'‡tûíD/4ú©O¤}Ž}¨ÏâÁßyñ@ìï[ÀJ¥âÀ2¸xyž‡jêv¸ò+›vÚ,¦ N=^>&¼ÜNÛǽõuxë`9Ê©ÂÎ/HÆçrÛ¿ùðýŽWŸ>62 h@82ºé«&ˆf"¾áâ‹bîœ ¼ú:SjQúVYVdganC¾0T‚¨N0$3›Ò,ƒ³oƒº®Î"óõ¯÷y>“HŶF~©¦ñþúlãjº+rñÀ»‚öŒ¢'Mbt=IU‘Š£Evv<+•)l`ÏŒþ>þXƒà“É€Y³RPEç’êêV£0œb§ |?h“ð1ñŒ]µjãVÆcËÛñþŽC´)> )˜Ï,™115ÒñC-à¦6³™”˜D†¬ITRPqeóExðu‡É÷fªž2žâ{Œ·XÙÖŽdÆg½þüå˜??SeÿÑ|ÌGµñÝj@8¾ôÕµ„AJÉɉǧ2×RBÓ„­ÛÊñÚž2¼Q^†tzáÍKa¬¬ÄDDQåL ¤lé8“)æ:m˜è*jøäžþÄÓN8ªxŠ—_is3öÔÖ¡¢µ}|‹ f§àªåyÈ%dò©ÄPm-'úÙ¦âý…ÒhÿÒKѦÔÀ}ãi†DB˜š…Ë.[¬p°8~i08¼‘ìU±{˜Dûè+cùò\ìÚuÛ¶W`Û6:ª©8‰(L¦ç:cü™]A$grHù¦ÚÒCx˜áÞ²/ášäy¤ˆt/µetìÛÃðbe-­*4VÓ?~ô¢•jñ!ñ]LѤÁ "Ù„üÑ€pBȬo2p'L%V$pÎ9s¹Ê®Bqq5þK©á3{÷#›©f1HjZd¤JCCj‘Ššã˜‹®¾½ ”ù_­²/"áÚ׫‘ùBk‚§´¹ åÍ­*øuCɬÞ0 æg 9)’Ž-^õ±Në4£íƒuˆg1‡ÄÙI€wrÿà9Óý›Øo%qœy¥¢^ûWyf˺ b"gIu˜šƒÕ«f¢¨¸{ö2=Ý¡Ãxý`Óv†#?>™ a¡Ýt˜ˆJ+ãOnÇ­?’ÞËÇÈÆøò^&-í =t3yY}{J›±¤¡‰ádôB¶`ö¢Te#˜žË(‚@‰y¨µCUcs¦„cCG]ËQ@&£ˆ¤*y>6œ9[å ­¬lÀ¾¢¼XQ W–zÅ LŽD:#òg$JŽä`Îì²R æÇˬ„·z™«áŽ£UŒR˜$i%÷U “ûr/ ©#qÊdku:p„L³šÆç²mabz+Äö!¦pB^vR 'ÑX=2Ò»Š°¬WÒc?ÙMLsèÂæ¼îdx3VæÖ1Ðdœu2œIqq-½ö ˜]ÀÌ”˜Ê{£ËÐ) äò½¯‘‘! .]’«2ÈZ^øØ»ÕØXU‰zG™ò…¡·ÒÈË2ÉËbèXgdn_ñì5?ãVzÀÇÇ,]½YC:Sø—wù@Ÿ“û{ã¸2Ø?ãö2žaMGyX;óÓÌlL€®?ž¡c–­ÉÅ,š¸$3u^LL¨2¾|ü}HÑ')4 SrêÊ&’ªQÄFNL99b×Gƒ÷Yô„´¡¼¼AÓ­ab÷¢¶&¼ÃàÌ”ޤJ9–«í$®Ài̪ìvB¨ž §÷œ™[­‘!¯¢ÃûDÇ›ðOÂõ]‹ü°'éø„Qvôõ°ñ= áÐIÏàF+I¬h¦$°•ù’ ÁAˆàDÅÐ&3g¦";‹± siœ.qï¨góDJå`8]Æ2q74tàõ׋è cÁ ªåOoìÃ,<4[(+k`J´åmœ“pšÓdtãO©„  ‚˜1D<’å³lYº»8|¸••¨fƘª–ì9X‡ÞA#Žé2™Š.žÛò2‹‰©)É¿Ä ØÌßCèMlb*Pmªp,O¦8õŸùRÇð1Q÷Ú…‘ /³J ln;)l"ïj #\½‹[h( _‹ð±èPÚÞ2V!Còf$1ƒ í¾’#[Âv‘7:µ4ðDŸ¤/NáõmÇŽÖÄÖDŠ0qBY¶,WÙèØínt0£„HwÚZ»PSÛ®*Ûð~S-.z%ý&»Nñ ¹³Ò4pG¼‡%c“xý ø“M²\À tRë ˆ•L'r> hÓÇz,ö òÓ‘J`GI`ƒa‡“…‘ÉÊØM^íåa^~æ{^½| h@8ù}0i- /˜+IÙŠÔ©?xÕøµ‡Ì@î‡1e-“YöE7¶Å71õõ¯º%€p\œ40ÄÂ…\#ó™Ôm”¶´ƒµ2 Óʵ÷rUΔN ‘ ¿uÙÜhuY™æ‰° PR–Éuêz1ò&(“{ ýÄáCTrND¸‰a‚à &c6Sêi@,c†š¹Š'ÓŒ"Ø‹âÄMf)¬0R©G>>XŸÔ-÷P¢ËÄS@úÙh ÂÂE™ô M`?YTL|KüëŽ2ÜÅ‹=•iÍ|‡Å¾Ò«0ô¯vNõÖø¢¨–…/„sÑ( D ÁR8/MG9ÇI-𯶶^~ºÕ¾€Gác½½s ®ßåè‹ç¹(‰ ¯Ô)¼NТr–"ýª><&‹]¹§â_b?K-L0Cæp¢C5„!d"ÂCØ‹Q|ŒRÊØ0ˆþ% ›RŸgJ[¥ª0ˆ' @£NöŸ f˜2ÑC; À„ ƒO6Þûº2™åÙ½íöÎ…ª‘§¾ôèáÜòÀ ä¹§bÑ€p½&«œ½{÷âÅ_@“nË 4ÕJCC#íVšðüó{a¥Mži¼J—Ê#Ñ\ÿOÜwO+þý˜+ѱ§™ôCttÖ®YˆÅKfóeT`màsÉ9"y–•«$}d„*-<‘žƒ’EWÅÂ@…y Øó®j Ég<  Ëñ1&_¯kÃÓ ¢½jU>V¬ÈSLÁ@&!¦ Qm˾0H)RŸÔåc˜rÌ; h5°Ðb²‹¼Îb‡" žÀGöv÷d7nï/d±,`PŠoœ"´Ûφ¦v<òÈÃØ¾}«¢×pê‹s¥,–p¬]»«W¯f£h@8 b <µ«« ?ÿùÏñË_þ’“<Õ‹–©™µ@^¤¨¨8fªÜ?ðÇ|_î%%Œ‘çßzCöúÈ0%›­‡}ó|æ3—áÖ[?Âg WÀnàm„Áz™Ø‡y*J0CŒ»B˜ä‰Šü$Æá6AŸ{î\Ž Yû¹¨’…9ø^'ªSŸ H_RSÆw¢‰Î$uJB˜BÃ÷þyrå‡w•÷¬†*ãR¬N£´pæÌDM¢Iè'áaò‘"ã–Pþ¨"c9˜æ/q,R‹è´"€^âiŠÄQÍ`°)x«õÖ-û»¬½øãÿMþú7NQ‡«ëû› î;Y ”6UO>9|†À@ÑÒ˜ðÌ3#Ç4WŠHÄÎ{øÙ=Â:FO9™û~ðƒà–[n!¸½ƒ‘’ÀJÍ…JƒÎ±1Ü=÷܃ŸýìçøØÇnÁM7ýE衃ÎÒ_'‡Þ•qww;þóŸ§ðÀ¿V+ã»îú,™–w<”vùᙜ—1ÿú˜©F™;7Ï=»¥¥ ´ÿIá½OvÍñkÒG'—ÒçÝxûí2žV%E™ÜùçÝe$æ Eû«QSÝB§1ÍÌ8zÌûK æcÒgvÚ3 —X’’“[À¤Û‹$OÚl¹Vºöž{ǽ÷>ÁtrÇõלژXŽãÊ“V¨ ¸Ývj-ŸÅOz;ßOî»ï>öuÈ@¡„Ãì·ïß¿¿ùÍ=øèG?…ýè^ŠÓ}+©aV¦OW ääd3»DžxâA2¯3édR0Ž:¦š'à ¡…6„{öT¡ €Iæu™’èê¢i¯ƒ±Ñ²•­œ¼÷º ¦€ii1jœ>ÜJ‡‡^Näx’¦ÁWêï“C‘ðµ¶vÓvº[w¯ÍŸxúžÄ‹]à¾}•T‰> οßùÎ c&M69œwýò—óÑÕÕûïÿn¼ñF¬_¿^Âñè:'=¬^|ñEUõ'?ù%ªE8wÒuކÄfT•„ãšknf=Ͱ!Ûé%:{4UáÚ%%™1#¥%µÊ›999RÙáb}ŠQ@²r\à e[¥Îñ;F¤Ob{Æ3¹Ø’Ôl†!‹ãצNŠ#]WW/Ä{YÒéùÔÍC¹ÿoìàùÜxÓ-´¿Ž oÓ¡Ðm¢Ï‘wó#ù4…!àñÇǺuë†Ô‘óê2 ˆ¤ †©vÄî.<UT¢Ê#8k™€¾òPZlÀ¾%þý°§yëÌŒj‘˜”Ì8–b)\íÔE÷è©iô¡3„¸6t™¾ê 1Ú'’a‘ÌôfY4ÖÞ¶ý½•µøx´4Èë;ñ¯nÃ{ï•«‰r¢ÆÍD>ãxÜKl …fÿýÏNeŸæ E3wÒuŽ”ÂÅãø½aÅÖSBØ ×±Ïû>lÀi[õucC•‰kˆ`Pî¨áØÐ}Rjð!ö‹Mé/¡ÔדÒ}Ó`i†³)asS”4°ôk6€@~¹+ïRo¯{vÁ~z`JH"½èZWÉ|#¡¤ˆ3Õ~:™-5ý†F¿‰:Kú£­ê;¹h<ût¢î?æ÷á;+&[â MM8ãr.”ÉP—S@ÏT#&Ýä^èAýëwíÚ‹^zˆ)Þ’ŠZ ñ'·[xwJɲ2ã•mß¾Ãè}`Ò›§p xS²‰ææç13Iõå NÖ‡Q@¼Ló™—6V’ ï$C=9"Ò$àT}¤MaÑŒ&\…£™²’>?·ÝŠ¢];Ñ(Ù[ê+±oO±Jª1áÈš„#§Ý¸^)+‘þÉÊG¤²•"L6ˆñ`ƒ ”my·~îK¨bþÛ§îþ8~zßßiÐÃëDZ8¨g…ÈqÙúŠì«úä|~NÊÀå¾{/c~5ÁHæÄ &³™™x™zÇdfÞÝØ»˜«·ÑŠÈxÄEšÀÔ“ÌgéA{s Ú{ìN@LD0ìLÞÃ(ö–Ð`æÅ¬‡; „FÆ PÊèƒ#x_ü¬­CÝÊ£mb<+¾H „nï²¢™/¢'ØLF“À|™Þ:YY{s3:{l0³ý Ìké¯4¾LnG/³»´ÀÉÖÇÄ% <$ˆYAØ^4ë—¡·‡!hˆ„çÑ‹ïý÷¡ì`=³Dýr}æ„R@ÀŒ¤ÿRYIKRÀ»¶~ŸóQMÚí.EC Š,Çu™\ HŸt1U#md%·°Ñ©®?Žs"Ø»ZÑÜÖƒP:°˜úlèé dºOJ5 b[j™'>!”tšBCTîæ ":Sh8‰4ÔU£/ȂĸH5:{»ÐÔÒI$Éã ¤Ö¦ÛÚƒ°0X;9Gu;›ˆ03O&pð<°0z;;˜æ´†ÄÅ2î0³lIݽíhéèâýcC»ÌþãÖvι¼Gd"b#ŒÌÊå‚t¶ñ~ôòJBŒ%þªV½îûü/>úcüü÷ ÃiBvn.ì¸ñ»Æ%Œšò“;¾‚ßÚsl*-.X=©jÅhpaÛÆqóÕ ª¶ñy«ñ?ßý®š‰Gî¿ <ùÔóKÎ^Œ/~ã'È1îÂwï¸aQF´´u¤›ëÄk¾ü¿ƒ9 ÒûÓB=cs«QÕ@€U»ÿ%üðî»°½¸±É™ ´"uÁøá·?ŽO߇ßüáo¨ïêCFz"×tàlJ$+Tký~|ï«7 ±®.c,νånÜvíz4íÛˆŸý걫¸ s ξø›øÂÇàÿ~ðTu9˜JµµÌYx>nÿÖw1?'‘RÄcÒSÑÈí{ý!üïýFQùæ¥OÁÕŸþ>sÕZ½òWÜûçGp ‚™_¢Sñ‘/ü»x¶>û{üö¯âàáD$åàf¿þÜ…x럿Âÿ=ò*jš•<ŸúÚÏpõú¤™¬š(üÔÅ_(7Z«ßï~p/²Ïù žxìqœ™mÅžâý°uwâÆcÏîÅöþôÓï"¤§-=Ì1É%Mz¸éÀåŸþ5þø›_ mß+xþé—qpÿkøÕÝDáeßÄßÿúfîÀ=üƒÆÅ6Tîx5AËð>ƒO]X€Çîù&¶Tµ1e›‰{Åò.Ô½ŠÿlkÀÿï!<ûÏanàA<ø§_¢®Óíoþ UÎùøÝc›ðë[?Š×îÇ3ïìD{m1ž}k?_x;þýïçqÍÂXüóaGµw¼ˆ¢¶$|ïžçðÐoîF_É3xôÙçÐCþ$@jz”eC8{v ¥§]4¶oT*šéñlÓã)d¬I~׊òì¢3‰‘qô${ƒí£é_æ~& %ˆ±8—òõûpï]ßFͶà©W7Rig®fï̹ÐV¿?û߻ѽ¿ûóS¸òŒL<öç?ã½­/ãÇ¿þz’ÏÇÿýõq\´8ÿxà!l{óQÜ}ϽˆZxþüðcX˜èÆÃzÛ6=Œ»û$¯ú,þòàß1+²þá¯h ªÍ_æ=-!£nlªq£­¶Õ±sqÇ ŸÀœÙD}úN<óøÇÑçìEyu ÒÖ|¹p%Bl¸éêPñ‡"¥âu¹B°ê‚«qÃ5«HðxIaÛÚQÎDÎüKðù~ÙqÜþ›çQRgG”»ÉŒQôÙÿ÷e,™tÃ'Àù%lÛ¹=¨néE !ys– }þÕ¸÷—ÙØ[ºÏ<ßÅ€ w/Ú_|Ó‘X°ûßzš¢ü#ˆŠ d†;¢3–âwÿ»‹à…çŸE=ÕÆ¡f'ìî¬8ÿKø^Â{¨8ðJÚšB1»“Ïç™VÉ Dm€ìf.a¢ô={3•]ªRahÀ16oËXÕÌÕÏìÙÉXµ2OIt,½‘SV4 aaF,\˜IZ*Ǒʻ ËäPÀ·ð9t¨ á´“ŠóËþvº­uØÇ,Ak.ý Ö.^€(s.úÛ‹(£J¸©fê£æâÖË®ÃÜy)Hþø˜žô›Š¨²°KË\Oîj,ÌKÄk¡ê?íè¬Ýƒ÷]¡øØ ÿƒ3ÎCÀ¬ß`æú#HH‰ÅÎðh\}þ§qɆeˆ1.ÇÇ_º›*ÊpèÀ^tP5ìtö!1w>Œ5»Ñaµà3·|‹¤aNÆ=8»ºm•¯ÃæLÄ—où$ÌMA~ÊïpaCªßy}é¸ù¦«P˜—„Ûïú+ŽP2[ùÆoa Ÿ…›>r) ³ñß=ŒÊæn„‹Ðɺ«„"Éä—",:’a/Ìp»` ]BFb ZeÒ¢52)Š`Œ’ 2Øè8 탛FyšÃhQön+±"áÁ܈²(ÏWë‹Éœó \äƒFÀÂç’Æ²‚ÜptÕá‰GÃÆÝ‡™›7ŸûñßS÷8>õŸ!:!³òòPs¸H ¢—Wþñû[ñÀK%TÎ@BXZÛ\J…]Wò&¾që—a Hà59°5´ÒN‹/¼«¯>ñSÜñ—é‰;é ñh¬ïA¶XØN³"“ctt(f2sIQq­²ßI¢-¡[{ûEOKÿH0eI1˜O¯bí5}×L¡*‰¼JTÈ>0¨B£§íÈj€Íæ@-¥l‰ñ‘ ¸nR)ÿþWÄÞˆÈøNJ´1EjJªœÌ(00G‡«@è}4- æüéµùDeàüD}™›¿ñl5öú8¯™M¢ÔZæÓ x¬Ýíþ(˦Í`¸RK¹°›3çO'Š·þ?û†Êæ²î?ÂEÉþLã8r ‰‰Çš7½ßú6Œœ“CŒPÀã–„$¬¥§ý˻ͦž™ñÚÕù1™Ùȉb~ôw0ÒSÆD;H'íïãrr‘^X9òö~Q4 ô‹nÐŽä¶šF44·#xv,º÷ã½z'ÖÝ9ÚêJÁFìЇâ²ÔõPZ'£ŸÃÛÃÁï{ǽ[¾F#Ú‹KÑ`íE^¼ï¿ñž|›ªå3â(Yl¦1¬•ª2&8w;Ñk÷ *¥Ÿ¹æ|šF¶2-Ñf<ù—§~ýσH§=ÇO>~þCû\Ww¶=ÿ.ÿíÿg5Ê·¼„¯Üò®òÄ:²o+ZRñ«§_Åš”.<ù›»ð‹§ÞåûÖݯ¿u×~¿ºãfØ㶛ߡS‹A‰éɦQ!#!Ã(œŸ†÷wBIi-êâpÒV£HÌAƒ°Œe¿{p®–ÅiO,•Ýš¦ñXÒxhu (¯¯o‡Õêd|Ô5Þ½É7[ ­ž‰8KÚêà\u¸Œáº KìjÃ{ÍèM¤Z˜ª§¶mtÔ6yÐÖPŠòVNœÿxßß¾£ï°ïÉ‚èlÙÝØŠÃUµ0Í€³~nÿé3XuÖ…¼¨‹u1› x4wÑÑÒÈ€Ý+.bîßK¾È9у°˜h´î|‚scÊ·àìÂd4oÅ=ϼ‡”¸^^sˆš»¬+HBÍžÍøõ‹{@É$Õ×ÕMv,o|ÿÝZ‡8‡m•¨krbQ°ûå§ðìÖF\÷©O#N'ƒ9'‚Þƒï¡á`ŠLêwâ2–aaÐíøÕ½w¢·z-^{èvZU8HÐ6gÆb´ýî.Üýë<,°Ôá4fµEœ/PN›/ äÛà¤72Í+0£`f¸…;゚܅Gï½ ms¿…kÖ§Ó.± ¿¸õ{0}óÁþe¶|ü`^6Â#ÃÊ…“¼hwÂ",¨9²›ªëÁQü,Þ¸±vl~o?½Ÿñþ–çñXè¼ð UÄÍ]0oÙ„„d0wUàùþ{íûh¿øWT¹b°eÓÛpÁîݹ‰’H3ö<ÿ¼t° é±Û°}ïùX>;U°í´)"øLHŒBn^v¼_…+ò”TjÚ<à|™<|9v¼ˆ^à•¸ð¢ùÈ¡!¹+cß™Mxûí2Ò7+©’/dÿ”Lý³ûKbÏyøpC«H®õpµHõG&+ã"0< k¨NýÝs¿À9nW¿Žû÷a~Â…HÉZ<Ã}øÓŸ~Î#+°é?@C{5dnJÿ\Ì ÕË}VÂ÷ÛítÒt©æÄù¸j^$ýÛ/m®Có–ûñð»N®¿¦þýï‘L'²DãÏñ±ïÎE|\,BňõH ½y™Ixð—wÂÔ~ v¼ð¼Q–Ž?Ü÷%dÄ=‚¿üâNÏÃæg~‹…øý¯AÒ‹oà¿þ ¬•KñÔŸ~Œ:ùøí—!ú¹Møý¯ŠÖ‹ ñ÷û~„®èËpÓçÈzï5Ùƒîd™ìFL¥û»8ð^zé%TTTáÚkoã|Æ0[âQ0/e;ÞĶÝû‘1w ¸Ê[s#.9o5bB\ØòòQÞ„õ^ŽÂ9…XqÆR¸::—¿ó rù6ôq%Ò…¸Y‹±zÕJž“ÅÕËF¼öö.ä¬únÿÚgå¨ÂsÿÙŒ…ëæá½×þƒ¦€t|ñ¶»pÖ¼tª)ç›>ݾ’r ÐÓ\ŽÍ7µ_øê—aé­Euw,®¿á”Ò¸öý=åXxþWpÕù‹P}  óÎÁÂl3ÞÙø*ZÜɸò“·bf¬ Õµ6œõµh9ø.¶ï,BÒükñÉ/Aב°¥bù.Æ@|.“¾”¶¶V¼ðÂÓ ûŽóÏ_9 “>U Ìâà¤$jÕðI‡©©Q“fh_×`›["‘žÖ kë¼Dð×ìÜ•ˆõëÚ›aãpê'æ€ó¦úncc^{}?Œ” ¯\™Ë­Áo˜òT§­¯ý¢*óˆ2æô>€ÈIIˆ‰¡6Â_f?_C¹ifeµ 7Å`Áü&òÌV˜Mn¼ùc‰VFcݪN¤&ÓøÀ5SaW$nÂ{$­ ‹¦%Kr”Êx$] ýùê«ÛQUÕÊx“W\Æ+µìXÒÁã ÆÌóá±ÖâíÍ›` ÉD†©úØ|\zõX¶0¥ï¿‰-Ûv#kɘ7{–¯[XòØ€ˆT,\¶T[;­è‹Jáó®ÀòµkÄH_yíÁùøŸ¯Ü‰ó–fãí=°…è©Û‰wöU㬫¿„]ybh®%s_ŸÌüÐlkIá<Ø›Kùü¯Q=7_¿íÛXµp–ÎEoc^¡3Š1e n½í›Xºh–ÎÍG}Ùv¼òÆ[Hšs.n½õkX<–ÌÍEuÉ»xmÓ»ÈXp!ný_à ªÇÇŠ ¢§§Ï<ó˜’ÌßxãJJª¾ÒÂSQh‚÷ÐV¡p͵øí²+.r[áŸTÞÁ–ÜüùŸáªOÚèðÁ¸ƒ B-6 RÎùäw”Øœž­‡âÒÏ|_§Io¸…«®*²àm\¨¨wQM‰+èš¿:Ý{Ô3ms?88=ˆÍ\€ïýä!å¡f03ö!Og®:WÅO219ú²µ×@̆r8ñú /uQm,ªìKpã缯1QÅ|¯!X»­õë/£Ý„F‰û&í?ç:Þ—ÒƒivFßÛêEÌÉNèÏ\r„ÑiÇ~Ö{N‘¨1ØYvV<òh€n±„hÛÁqè±%Œµ`ñâ,9˜ï½?3„>I•âZèÐ ¡€’’Ä6n³ ÖŽ59I#†ñ“Õˆ¤|íöß©0P!áF™Øó对Žï÷ÀÈÙvüŸ²;´ÚHÛx.Tz›UÜÛÙg^…Ï߸nFª¡½s‘ªo@›å‰3WáÖ®äÈȹÁ&Ú*¹ Òæ®Çm?Z£ÚÈã T â f-<wÌ=Ë;çMŒŸÈãœg,½?šìœhƒ9)ÒR…sû€›Mò.›©Ëp)0Þ+\ AYs¸£“ƒ¸¯—¦²Z±ˆñ©‰vR zm'¸åŠ€!‚99îûîóiÌ*£•×SšŽ>G"—?NZçŒÔs°ž( H %ˆ©QâÑðÄp "ƒÇ×Vuò"J1ˆAÏß ‘ÀH‘k¤±/JV^´­“—A+ÿóEõÅ‘¬\¥þ“—É]×Ë3Eѹ$V2öï«ñ:—9‹}•.Ky/$h¸är½ø’EJZ¥IƯ„ÞL8—šï[(|Lb¼ñ¥ÐeÜ) ôomí•A©Se©p3Š9ðÎÒo"uÏrt¾áÜ!s˜³›Ò?&k£æ/™ohö®æ?i‹4I>¾a%-ô}W/~1˜IQBŽA·ƒ(nJ}9÷˜8ߊ4SÝq‹rTQs ·œHñ§C§dc`ùÀqßœ+ÇûAŸlÕýúç;ßquñ8ü‘¾λæ…Ç¡!ÓµJ!°„©Pöz¾Ñ7+UKtÁ˜øÖ_ŸGXJ°÷Fo{t_? !ƒ¾=ONñ*fþ÷ѧ‘’¡«¼l'+¸ž'ü>p_êø}ྼѾû|àø k¤Ž±*ò28É‚}NZç’ ¦aÇv:— s ¡.ãOߤ໓ØvR$±òÂÂLÃb–¾:ôvøðpØeµ+ ‹þöÆÁórƒÁ}4üÚõ'¢€,:««Û(`D"yŽ˜¯ æ½'ºöxÇÅ´ÂÁÕýhê8^½ƒùê!Ã…_»gšK`5b|“/ò'×ÜÿÐw^3à2J\"ðé_> cTB‡æÐ1¸~¹‡”±:î­mìþ „¶SgŒr¥”+é2 ÈÄþÿÙûð8³òÜW3ÒHšÍŒú¨÷.Y’åÞ×u láÂÒ!BžB $¸\À½i$´…Ý,Ë.»ë]o³½î¶,7ÉM–Õ{ïm4ª÷ýÎhlÙ–í‘-[Åsvå™ùÿóŸòóŸóž¯.]ºm­ÍŒS[ÊSÇLžžy^-cÃŦ¦ ˜b­›M¼™–*/†Noa¬Ý$èźr¦,üÍ(jhaWO.E‚Òš«¦Ë¦d¡ˆ’Æ%g¡…n îõI{®ú:_ê•÷ÅBMÚ$¿kjÛñÜsÇTHÁ™œœçKŸb;dî‹O·“'+ñûߟ ³êfŽ‘¾ÈõQŠ=ÜïÂì¬Ð]h[SÓÆ¢&€D²$¼³;K2FY™‰hoo¢})9í"Û¹·IÞÙàÈÄD„R¢D¸2[›•‡",4ÐLE%Òd¶Ê½·äp¹tÒ5Ô×¢ºª«×¬qùýrs]&±#£ˆC·nÝŠ¤¤D|÷»_£À³IJ :âæGvê.¶$ ¡¸ ¸ÄPEÿò“@] l~hÙ¬ê;£#—ÐAu*#—ìÚU„Ë—[è¼7šâÍ„wV‹û)d  ¬´I©:ˆSð®®~œ:Y›m11< ˆÈÈ-¶¿×sF6uáL……YPTTË÷²qÔ©í§{‘æ¦^DFù+ÀâöÏ9»#!®»{€®_9Å7)ÿƒ3!Nm‘ènܘƒèèüä'ßATT õCs”ôCÆØ®Š©ç’bPÒÜÜŽÿó¾ÆC€/>ð¸dP"mvÂŽœ¼dáááøÑ~„O~ò“xê©MxÏ{ÞGÐBÀ±ðÞ ÑŸ‘ªø ÓéÄÛ 2ƒì²IŽj¨gAýCO: ÅßÕf§ÞL+öïßÇÍ¿ÿôO_`Äëƒ/‡îM|üã’Lwä’«ã6ÛßÄÂueå­hlêV:œÁAFúa‹D2PGEðþ=œì³ÝEP^LL¶lÉP®gú?úàÁË´„´#$ÄÏeÆ" Ã}ë‚”ÔÖv’Y1®ÅžbÙp¬0yŸ¬Œðñ­oý ­f‚O}ê}4ÜŠðYÇD%gnß'Ù»FFÈï£â¡·—LgÖžááµÿét^,cfÃäØÛd/¥N¾'EîR4?³Ú¯æ|ÒOO×{÷¾CPXKœòcº|Šwùýr«´tù›ˆ7mÚ„_|?øÁ°{÷NÛŒ'‘ËÞ³ŒtI@ “îînêTiéŽÀanϪã jöD߀&¿Z^ñ•Ù;ëbÓ¨{Ië匌üÅ_üz(‡@ôöœ¸›-³ÙF³E4—œ§›ƒ¶¶>¥K¸Ø€Éý ãí&‚´AÄ‘]]ä63.øqlÙšÉuáœÞj“” N œôt¯ÇKÊ—§‰aÓ²é¤]@ÊÞ=鬽JsáØ.Tëãéh7›ëÅÝÌMi‡D'ñ" 2) ~·m¡îÖ­yøå/¿I.áÿàèÑ70004÷ÌÎ11éíg€í8LÆ™‰³<öôØÔ<4›õdVÌ ŠQ§Ý.úÉCôZ ÑTÆ'w3~wò¬Œ¯Ø8$&&’ò]<úè£w×ažë9ï¤u‹ø…«W¯ÆË/¿Ì®VùüYhÝŽfii)>÷¹?ÇûÞ—ÊÏ­|±ï• ü•‘=°çP$þý?²ñw_¯ÂÒ¬^^ã›t’lúAt³àË:®]ä%âBYÌåO™lH¦ry÷¡·Ä2U¼Š™¶€yfj^׺0ÁÅJ«,.OS°äRÂ6.„XŸs¯ç§ƒ#ç £ ü©ÎV…†Ž<òÞ8è(4tÐû^€;-ôe³’zÊ+ZQßpˆÜýN0(ØÙK¢ ¹õ»6îo.çœp&}Αü‰;Œ©sYæ½ä‘ñ•gœÉ1^׿uÞ»Õgw÷ .^l@uu7Ýr.z+–›/9ùC¶ôp•²¥¾©í¸U™÷ûžÐCœj‹sgí&”~ž´W*d'ž\Oä9.ýqÒZn ¡ïS’zûúlhiéã|7ÀbñQïèlÐWÔ\òò’ñ‹_ü VZøŽ1½¼òs˜´´BnïòÂßüïd¤g6ãKŸ½@‡¬û²fß.1ôûÕ? «{ˆ¢Ö§Èµ6ð!Wžu”íÁH['OTàßx þ¹¿¡ÏÛW<£ä…ÎŽ^µ–GGG#55•ïÃ=ÜŒŠ™ãÌóé œcRĘä1†ªËÌŒR×î妬*¸Ïÿ8ÇJúe#Øj ÈðRq#*«–þè‹_R‰>ôgª7è¨×— Ï×H·a™÷ðñ€4Ø¥TadM^ƒ‚1IãüˆôçL7 M·8Ò¦”«rмwïœ;WÏ÷D ËV‡1Ùˆ9}æˆ ¦*K$"#ÔáRü¹Ž‰Ëº0›IÆ£¥¥›c=JŸ›þ|'eM›Í‡îùb'ýæ^2AǶãâ áì乯哵ãʱc92]ÖISÑ—2äùÑY§µ³–{ýé„÷šÂîò焲áÉéýܹfVãÂÅz¾¨äTXa K¡ÅcOaüBPÂøž\Ð18 ƒ³É—°¹Àööö¢­½…"¯FõW]ÝL®G[ ¢éŽ(Fqþ„C ‹Êô °—hÌÍX‡b¶KNðV¦ÏïlË\|:Ä^ÃôÎ/Æ……UÔMó1ÞgÅ*Iìw8Ãñ…“ëL.ªIE¨¹‘Žl;;'Ëì8˾¾>êO¶¢¥µ MMükÆ… g:Æ‘A¿Ü¼8Æ~¶*à|+ q+Šˆ(t€œ`„’„¾1ÜŒŸx"—ÜA+7á›Ï­Jß÷` ½šh¹{‘:ª…E5üÞÍùiPó>55aVÎù+] ¼ÉÁ.ÐfêÆ©pÇKÊ!r™:;ÑÚÖăÿZšpüx ¶.)çÞ991È̈"¨7)€'õO—äztt ð–*â‚F  …K9L¥£ ÍU0, N€ÄΗOãWÿñ_¸t¡ Ããkd6Ap–çfqͰ2ú }×ùH7¾Ò`¯4ÝI¿!»\ºNº(ãšAºÉº!ëÑ‘#eßû¥¶N`-ª$2'g+Éú#"záðZ,rÈqül•î.g±RÀ ëÈ> ý’MQN{g7xß¾ Tªî¦¸&„îFbB2Å^A€Ü¼¸ˆ)PàУšž\“‘¦p¿BzBBC±$+G¾ÞÞŠž[QZV‚óçÏ(ßvqñغ%“–¬a\„oæÖdʸ„àçܹZ–ÑÇfþE.‘Ó1u¿ÞÙs™\Ò6r8‚±rùFö-MÅ1:ŠN ãDì7å¸MnžrSD ž‘‘¥@G/bGG+9e†E8ñ(9(lØFN^¤Ã8NíÓÒtWÅÿZ¯MoFHNÇパþjì¦{f¡^S\-ÎѶ¶~ä/ÙÓÕ< ±±ñxòÉG·:† À_é &˜ Xý½›• Pô€Ñ`$÷Û„8–%œÀ!Ä¡54Ör¬Îãݽe8|ø2òcÕª$5¥ÜéÊŽà`#vìX¢Äªùùe ° 膙¢Õû ZØEAÒm<{¦5Þ¸ØYŠ¥KâñüK•lGYUÖ®Ma<[Z¡ò?ÇaâæèÊI?=9¯C¢#¢B˹h§¸¹M‡‹ÏâØ±Ë/¥o¿h®O©Ê-´çnh c&IB ŒÍuÅdÔ«kîܸ܀ðvrß_0ÅPbvî¡£s‹"Gèar2)ãf#ü?n„££®ê–\Ûõ1>ËÌW„ê&??êq³ŒOÀÚ5 ‹pòÔ1üüç¸$cûö,‚OŸiˆˆSìœææ-~ÙŠÕ±èä‰Ï<4s•Dd6d³ãÐK8<|„¢B+yô}ÈHÏVÜTÙe× (3Mã²ã‘ãêTN0RTïgL /«V®AII1NœÌÇï~W€œœFl#ƒéKr& PDeo‹Ê@vv49S¹Ê½É\Òu¦tr%¿ót±¸^YîvvSüžE¥ÿ•¿ÑäºJL™öâcæó^;Êß³Þ“ê惃CÉÌACS=NŸ*À‰‚ót8ÝB·2éôË* íÓÇ-N¡?Ã8nÝš¡Ä¨GŽ”’óîÐõt¥¿³™Ç ¤ËË[ðöÛgpø¸‰œÊ,üÅ¿…Ï|Ÿ\o/üðÿÕc¤Ñ©Æ®gÞ²9WiÇl|O¤> CþüKKÍ ‡¼…~OR—ø$-á¨y.R†©Q\nYÁ”›B×òòf¥~!ñ£›šz`S.}Ì*æ¯Ô=HŸR„û«›n?„î9°8( ^CC]p1´aÇÃïŲ¥«¨·dœ.Nøqm•„ãP}Í E a‰€ÙL§[L…ÃèLF£ p22³¹9åÉ?ÛÑ…§Ÿ^ÅÍÓt ˜‘M\’€@Ñ›eû8Šwï>ÏÍÖL+äHg±sò9ί«£‡ÜÕ<õÈ&ä-ß@Ý> Žb‘zs(œ–ë“ê©\'N'Sq²\½Þ@Ê•H!ò 7ËüüƒÜ8àÉ'–“FA×Ðñúºœ¿oŠ¡„è§­]›„GÉQÊý‹ Šž`~~…âÒùû‡ãïˆ~Ç’•þ¦ÌÏi-[ù®È¸È;smâoõ?ÿ‘±¢øTh959AŽ\ý·˜¨XDòàµdÉR>ô.^}µPÊÖ¬IV:ƒÓ‰åš¸¸Ú¼9Céâ^¸ÐH±±mj5÷ü»³ï'* ÏQÝ!k×=ŒºÆ8‚6oÒOÜ9tQ¯mŒCâÚk“¿&é*¿Æ ¢¯#šÿ¢Ü:™DüüðöÇ)qXЇöàõ×Î2Îy7¶l΄Q\€]O|çƒÓ|ÊP7©ƒðÆé4€iQs@8íâvFÄó²î8מiŠp_rSÀ Ýs`qP ££¯¼ršF ãxú¥±G†ÚÔnÅ …¤iâIñî¹JjD•h”ú?6šEk©<è‹ÇÒ™kU‚Цœ^bÇzÑ ÊßßèíÝ¢í…îááxêÉbÿ¾Ý8q¢€@ÚGIÄ‹ÌeW’ôGò?ÞHKú>BúR'³”aŲ>‚ª¢Æ"4w'7nF÷ì¸eÜ× D<¸_ 9Z ¦¤d 8Œ nÕ Y®mý8—Õô?µdÅøŒµr“=ƒQm4r²‚Quñ Úì¡ØöÄHö¹í¦%›š0ÄD¼ª¡÷þ¯ýûÞ½ˆÇß»T5ENòÍͽxë­"‡5' 6dcooïåfä©€—”!ù\Ý nÕG×ïy(W<ÏýÏq¼úŠ Ii§Ñ´vå«\iËc[GGìh¬¼€‹ç.B“8Z`7UŸCEU?âÓÒ i¢/À6D¤mÄÆõY %o½YÔpË%5MÑB@¡Ðñ}ÿk¹{·¢ÐÎá{Í ”ég"jž¶óôbõ:†…'â!‚ÁÀ@‚÷)¨i›MÚp£¿³ gOŸBï˜7âRR0ak¦¨¾Þ¦0„{¢¾¢0Åcåšµˆ õ»õd`H ŒÍ›¶cï¾qœ:U…@º÷ÉH¿9·[€¢»XÜßäOÛ—;¼(\²úBÜ˃¿$vKgµ†¢¸šèKM,!Ò4‰K¤¶þ:€pøÓÉŽnDá™X“î¯AuÙEŒø¤`Ó–uˆ nRšª@$2fbµ¼eËþš ºDõ Ítá2GO¡ø^•ƒ¸Ó’>Š+­WyHgì¢6!®„¦çÓôÔ}饷.wrS`!SÀCYRŠõkNö2µ@‰ÛW›%$)ty¢ó AjF9‹ŒÕég†§–.Û€m¼†±={Ydd®”ªöY“Ò“»’zBU´2ìá"-\ºž¤n\Œr !:²H;Ä­²98¸ˆ.U4‹™¤ ••mعó"Œ†×£\Œ\cPp3Ò›—Ä,ÁÖt¤&¦Âà­Gtj.Öo|¹ ÕWVv ô9Õáñͺ!ukI³¸¸DŠ$—£ªºÏ7+QØÍž™z]€ùýä:M­û^~sCº@IDAT—9$âpÑ=×Óº}¹câ¾ä¶Iæ]+…R¿ÐJKû€àÄ%e “À&8 –à(,[³Ö-ȀĪZZÚ*íÛÛ-/Ñ‘ËÎ^ÊùlTbÄ}‹´÷¦Ia09Ý"ÏM¾³RW¹úû'°|Ùê 2쨥Àà-Êä}rÀC#âBÚÅ .!9KráïkB€5¹«¶aÇæuì­DY]#¹°.ÒŽcçM7@+W¬…¯EÅ:©®ÒEÈçK@ò¾ÈÚ#ÏIw„£¾…Fnò郷[÷-E×v87±Ü˜—MdœÞò»¨#Cãœ\þv誹Þ\É/¼(9ýËSŽÍI®hiëg Eˆ™–©{ªMÃõ‚¡£e²èÂOx“ûÒD ãØô<é¼VDe>–£ÀÍÕ…šbÀQ§¾âýÛ EqLü Š œ®®1$¥d@KNƒˆÔgžH9!ÿ„¦Ò ÑÓôñ5R?2¾Ü¥F \Û&e&‰b¼¸Ønt¾¨©i#uœeÞ¾e²!.Æ$}—ƒ„!…„D ŒnfÜU>0!Q”ï|X~ Ð2«%€>! ò=ç\éxºHHá …‡Ó Mh”â8‹Ór)w¾$KƒôOYSÝBÝ^ºL²†»t8¹¦ýŠØœÛ\7´#Èf'Õ4ºñgß-Ý¢/¨$.ö_8…ôdÇÃb? Ìúfp ‹púCœŒ,#õÊ¡kã†T$%…̼×tØýãA¡€[dü Œô¢ì§è®3ô•§kxú—ã¢z·IE3±u¡ø|Å çp¡mkMâ ×’ŒÐ½Œ‰î>¸9ôôô«ÍÖù´ˆvd±§Ù{öœ¿rz£A‡oLg á|ö^|Ó!q[Û õŽÈ鋌E]ÃlÔ"~ñ†Ñ\_Š~¹‹ÅÔ-³.5À¨Do®Ö >âXÜŸôõQ„MŸu¢›vɸZТÉç5â \hâÉPUœ@³Ð»qôv· øÜaÔ•^Dó°ò"#àM®“«3_ZáEÕ³ÙÌ÷RÖÌÚ6;Eà ×rxxœë…?E¬³ãûPÊíë!GõB?ÊN¡u‚ârk0ttèíò¢1ÙEñéYßP¢TJä@$eß. 7ÜWï­¸ƒŽ¼JLœ“MŽ#õ3ge~Ü®îû n@¸ÐGðn¿°4@.›#6«€)áÊ]å¸ÝqdíÔjYÁ¥§‘®kžÊB|d0ÁÍÌÀ¦¬ãJœ6xS×di£–lÚ”¢Ü£œ:Y®îKþ¹X¼¥­BljqFp‘Pü}×I± µÊÛuÕÒs¢ÃéX¡d¦:cB/á 8DͳѸ»îÝœ !æiLpNÞí|¿ÒrrÅA»Í HŠ‘£“H]NWáà•’8V#$áÈͧ$ï CuCÞM¡Ýì´N¸­Zr²G¥Ä?<OÐÊ:œ!ÔD(0Ó*”Ê Ÿ»™„›µXIâfIÔNDqÝ:ñ»jàzr³'Ü×ݸ–n@x-=Ü¿$®ª?c†ŽÐ¨¡®®’ êK¸ ºªCÈÎRdæ¡6W~rQWDC^:#Ò²V"•íÊÆ;ÚÀ’xÈíÍ´Vî§O"_+‚•ÜhôUÑ3ÆèñÌ™Åñ”ý~'__ "£Ìäœô¡ªê2Poš!&ÚÉ&&Ÿ¢+)˜’›$ãÓ†Xã‘I×;´È¹üŒÁ ÅÝ=]ä61>©¿ø¸óä_KËûM¯¹¬OæÄ–9Œ"2DëV_Š(g‚n8@Ô6£NœcÎË`{Œk 7"ƒ:q¾´C ˆ&f2eümƒƒlW;tÞ §P"zÈ»0—»Z·¼ƒ~~â0Z§œ¡Ûl|W3¸ðíÊ:ÁŽIßD·XC:ù‘“¥rx”ƒÝLº-劄£‰M z­c¹æJúÊN ÓÄÚxãÆ4¦QÞÅÍ W*sçY´pë.Ú¡}:Æ— ¡„Ä24tðzŠ:x#®‹[ùðèð úºézf¨—¢¤¢aôóšXö 9v’™,ìByi—®=.\(äMKÊ”P‚½K‘ÅZícåÒ\(7÷Û[GÏþÈr3óõTV1±FT•_†F $¤kU±ãã#ìïgtÆ+¶õÂF÷%Cv›úÞ×爩êÜ$]+Ô‘K6ÄAFÇ(/+&wpT…tqœI5 .¯…s&"ÂBàÕD}¸ Èv•0+ÃCƒŒ"±¥eî3Ʊ¸Y²÷©ßœ·¢ç)zh7ÎØ[Jêç\¨®­¤úAÛ ¨Þâ™û|KÚ¢Óy"9)Œú²­¨©­âar„ÀÎ5à%0ÙN9À0–*^1i%s}ÀÞ¾NÆH—8팧{–¨? µÔ‰®Uc+Ü=WÛ%Õ ”¾åäÆÒé}¹…¢<ˆŸÇÚ]ÝÌ)àâŠ?ó‚ÝO¸)p(@ñ£X¬Y›@[qôèaS¹´Š¸­»­-}žˆŠ0¡¶¼ ÍälÐÒ22p5uÊW›«[…³¿"&¿}ùù= ˜Žf³À ,3b,‡sØ©Ÿ²d‡ZÍô³·‚ åA*HºÄšçn¿KyŠs§ÞøIÎ9DlÑäŸqqøÔ'WÒC‰ LZ?»´QŠûž^43>±Öíh¹Œ´â$¦´ÆZ1ÔQ‡f*È+gßN"¹ð)¢= ÷UTx—KÏ1úFbãBT¦§ãÝÒèNŸw‰D.ô×Õ,²É —4##’‡ ÐMÉ1T×T: n×Þã§¥¥£äÞúúíÍ5hî€)н×#·`ØEÃg›¸ÈxÕÕU‘Ó}‚Ü-¤3Ʊ^¯“×qVçòŽ“ó9ºKè†%:ÚB¿}‡PQYÆ9%QHnó¦óþÄubk`ך ×Ò}M=£µ´#(†!'zèM  ö;à^kiŒÒÞц÷Ð͈Šb$VÃdð¹D;ɧ¥´$.f6‘;hæAÞ7gŸgûó¶´rN ÷ç‚¢€[d¼ †ËÝØ)à°¦[¶,^…kÚ¿ÿmžŠÇ±nÍ&¥l?~‹ÈÔ— c¸4ú]Ÿæ%TBDxDŠï¡õ$ÝâTu£þÙ1,ŸÉÁy±˜tŒ4Añ\ÐŒïÝtB?V«…Öê‰xû­³x÷Ý·1N‡èñtÑ#Àì¦by>¨ÑéC·¸4ÅÑSzˆœ II™jŽ wKK0!u¸’¤>©åä,8ðŽ…¡axw_iR~ûyïJ%w›‡]CÖ´”†Ü‹Pbc -ù»ßÃ;ï¼/í{Û<ëÖ }ôÐzÃEÚŤªž¨.ˆ±YB‚ÐŽŽªI3qýâjR@šs½¹™1ÃßÙ¥µV¡Ýþ2Îù 5>ª¢ÛÈþI[䯮ÑiÀ&(oø,&ÒPËudY^çK¨œ³Xº»¨9¦€Îñ¸«Ÿ èia÷žI7.ûö½‰ÚÚl~h-fcž5Dì{ÃÇ4Gd“[:/Êã<ýê\Ü•Ë n"f«"—q?믪©B5Œ€ÐˆŽ®QUþm{ʵ[)Í'¸ÌÌ€å¶eKö³³{„âBZfÛFq©|ƒ}}8z¨˜ÖÐ:ÂxFñEÚ’8¼úº'N>ƈ%Òk;â ’'7þÀËšyɧÔÃ|ZT\Ëð’ú­¹ö’ñÚ¤èÈÍQô)«ª+,¯ >##`øÑõOw?ú\Ô½¶èÙÿÅŽ °–ÛÉóDxD¹v"¢›ýª¦+Qæ²Ð\œ‹¨P¯½òÊóܨ×`iÞ XLÒŸ¢|iÐu’çÄ¥’Øüˆç A'‡eÄߎ±›®Ö)ט_?{zºqš\ÁSäTÚ†‡Dÿ†4°hí¤aÉ}¢Ç”–Mÿ•`­£­bò¥[çMË[‰Þñ¬dœB¼ºóØ=Úxyˆô4ÑRZ½.Ó–åEAgÒŠŽžsž‹Næu´væ»þSÃÃ(I;ÅÍgH·|J6zÉeˆ$`ס­SN=3\Ôú!Ã-ÏÍ>áeÞˆ±Pk#]hiǤ8Õ×÷Íý{áRÀ îØ¹[>…²AŒ>xüñ\ZØYø~ù«Jäæ,EîÒe4ê‡/$ÒŠöz×4N è\Ë¿§?íW±xÕPì6HÝÆ†Zµ)^¼P¿@¼çcCBz†âÖ\¿!O[Ø}¹Èp]=q¾Â€ÔIøã/}OäãWÿU6ñê"_MÈ[‘ˆ¡¾ýxáw¿&ðXB‘íJ„Ño›Á`àÆ :f×l>xµé_{aÚ^ 7PÃÕFãˆÆš*œ=ÎÁ›7›)Ks¯€õi ˜ƒ‹"Ž«½|EÇÌAíŽ*eÌD(‘>ÄPâÀbÆÐÞ‹âK畳ꔔtáP„ÏCläSÇXF ÿq|^ý}«‰Ú§€vr}»ººPJ•ˆ3§O ­£ ™È]»ž¡PÇAÀ…±¿U]³uO€Ì0ý)~ý5"0áÔ;&Ü´èè |ä#kI»³øÃÎ hkŠåÜAY6cЧGSÏÛÑ—)¹†Žêºkó\qÒNjïëéAeu9N ÖÕ× ">[?ô4BÂ%:‰ëÆ)ͺç_åà z¯ÿö·7ÐäžWî®à¾PÀ ï ™Ý•Ü ÈBíI=šµk“(ΰR·ªùÇ Èé*@Tt ÅiɌ療Ð0n’ZåÂD68Y¡e‘V[¢Ú+Ô?rî–ú“ﲈ8ZNàvŠv›•Ž`EuièË8Æ«Ù̼e01bÃõÀó~ÐàVuH¯DÑ\œ×êt^0š¨CfôS âšç„ŽÔQË͉ŲŒ-8v¬Ç ÎãüùB‚íHF`IVÃp~«FqUBDçà8)J:èéøªø'“tT"*©¯ˆ*íÃ#hm¨£ÑH *È ljid,Xod¯[ƒŒ+":ƒŽñ¸¦süC¡øÿ.Ù\&™ó2/Ü®ììf‚ÂR†e{ ‡ïGz'qÎÇÄÄ3ζ‰cÍñçÆ.àDQUþ‘2T'ä_öÇñ¿|ð»Ì{1¢8’}½]¨¦KÇ«Šz‹ƒ4°ˆ$˜yôá"&1ÞTJœ‰j€ªöÿ#}•ñòà;ïèÔÕ Z,¾x?C"Žðö… <÷›oc÷ëùX±2Ÿ}t-¢³ÊwGè"Õ+$Ù”<Ž*$¢ä•Ãi(*µµÕ¨(/¡úC ‚:(~ŽÆö<„ÌLªfÌÐR\ê¸Iá˜àÌSÀzI±h«rÂE;´nÇ@„†š(BÎÅšÕI WË¿äßMNÀº™ …&EÉám‡À HÄ@"Fsr«„zãb"Vƒb}Ü××KëÉÆL®#G°žVÄðÖû’#bÅ–§žB"u#Å›’ÆøÜ|J²A9ö~µ{M~ç5öqº$ C€@` Û¨k•·,ÉäìÙ¼Kqî^ºÆ02*E$Å¥‘ %çÐH'ÜÞ $ 0w€mºÒ èK6ÞQn&v‰ˆÁJ Pšë-ûûûàåãƒ@r–ÖìØäì˜ø¼ˆ¨f(6›®3÷àšÇ¸*¡èüH2çÅÂ4+Kt»¬mí«j†ú+!ǰˆàÄ Á Ñ©"›X, Þ* Œ§Ì}9‘ÞÒ'9ȈúùjC694–hàœ¯cô‘6õ^-fĤ¥ %711 ýH0Ã6ˆSõù–®Ìû›4LhçA%bk˜?uбqÛhì-8~Œ'51|§°óõó¨­f–°pø*ð&N­=¹f(ñ/§ç2ÏI»aºÁž½§Ëa±¡±-MÍÕ£ðáÁ1œÔõ¿—œÁDÅq?7¨bܤ½suÙqø˜?s~®è°˜ëuÂÅ<ºp߀Hò÷÷ÃÖ­ØÀ¨ ÍÍ= ]Tzï@--ˆ»@ßvv½¨?¨Óù(î‰p½dñ0(Ö‡"n¦®$ᇅ!}y¬\ÔƒÂÃKÀ¤Ê?OŒjüŒÿqÄVp(ÀP¬×Ðánkk/éØInG']d4£àx úû³–œ‰Ç*›¤'­&•U3Óį¢l„¬EgJhëC±sP¨ ÙY#…¦¡¡âDI4÷Ö3³spRÀ055Œ¢ä0ê÷ r¬º æ:Õ¼¯®,¢ˆ÷F©CËœ—È"b`%L/IÂG8÷Å·ç(£Âhyß Ä™IyK©ç¡æ½‘GáˆMp|ç7ÜÑ×ÿ•9.ïú(û²li,>üþaض ßþa „Ÿi‚ÖÅ—páüqÎaídÍ𤮤:D’~ʨDÍs®ƒB?yüüJ ¹lóC¥H8˜Ñ_ÌþŒÞ"œó\êuÏu×ÇÊóÞQÀ ïmÝ%Ï L˜uâv%**±±\¨ãù'\¿qr«†ÐÑ>€¾þAÛÛ{Qx¦äzÅ¥¦*pc ãZƒÉD®U Œ¯ð«"œ%Jã*ܯŜlD:FЀB|?æå“޲ùÑáà¹G½¸TÒ‚3EuŽŠ"€`Œ]Ò_6Lûê 00¤™l†z?†n’3ËeH¸ÐQË„ ÀÖþþ¤§‡sœ„ëçPwèíBeeÕ)ª12჈¸rùèæ„5{èˆè×`4ÁD¿6³‰÷ôyÝ‹`_įbÆYî®Ú:åarb|„Ò3U$‚@}ÚGÉÅÒÌh”{{lŒ3Ì5ƒ‡ »}˜‡ËêR¶"<6 !QŒ×Í0‹zJ!„³hö7OÒ΋súˆ¤ˆÞEŒ¼ø(7GÓݦ™PÀ gB-wÞKáçƒÿ«äóÄ€ðFº‘ ûauH ãúú.Z¸vÑ $ëÞ¡@г ñø¿zJ20‹\tèŠ:î³LcNˆ(o1&é²€DyBõ›Ÿ"`î¿Yî¹ÒŸòrɇ392vÌ ÆäšûÒF"/¶ÉárŒ»*@Þ‹ûÃisÔ-sÍ1gØ~wôB8Š2Ý”î¥p¸U[ùœ=½ƒOBÃ0¦ÐÉA"ÇZà —ƒ&2åU¥òÁ|R³Ê+Ïñ?¡¬¤+×U%+ß É(Îç$ŸªK %íÙßYè‰TïN €n@¸ÉÝÄÅCYœ¹ÃÖÕÎŽh(‚“d0A‰Û§VnOý;ÜŠgÇË¢;t3˜Zá©»„škÐçïk½ t=âžöré(Ú¥q‡?uô|XÎb[Ñíƒ}ìg£È8,}ýBéRèéDOW YX¤‘'9:ZOo˜hLáã9ÁûíèaD¹/­§7Åxž>ð£a‘·fýŒ ÜÓockB‚ù)zaîíPMÐ;ý‡ã0:@ÓòŒÐÇd¥Þ!£ (ïéé_’NqÐVÝŒQêôŽûô-4زs¬[¨¯j¡èšóY@™Añuh æ§$Û·¨FŠ mŒ! ;Ú;)b¦n.£–Œ0„£¨xùá1fSQ”|¨s+a‡jRKoª·h'¨C/4$ÒùhyŸú½^z:ß…­¯AÓ‹º£¾të4ÂÐ{rxóòa™vªŒ³.äóð2 PÞñÚïNÜ€ðfw'ïÔ&$r#'«ÀÅJÔ¡œœ¨ÎšB:…°UïÃæMað >cññè ÂÚmy°wÑ9ó®½ð ÌÄcŸzœ ¼ -—÷âBƒ7VlÚãD? e¡Ï3›{ø¡½æc:×cé–m´˜”SþbK䚀¨*Äé£ç¾|Ölßmc°õ¶ãø¾Bés2P[‡ Eåˆ]ýVÑw¢†€o°» ù{Î" &Æár\<_‹äMO`ï3Ø úºè²e_1â–­¤‘KÈb#ÜœôG8PƒÝ Œ·|Þˆð¥³óÔ3îxmk2¶?Šè€ ÔpÞŽéB` „vĆú6lì±cë'?‰¡†³(¯DÈHu ˜ °/zw/&"’±éñÇád&˜™Çpk„Çä1“VÊœ­8ºe5:¬}| lµ8~¢Ë[žjÆ÷ ¦¯Äp”8ƒ1âèúÂ*X—¬GFº…{÷¢W“Š•kBøT!ŽÖàF{+JkZ¡jA}¹V<¹^=qdÒ7m…ÏHš¼±lu:.:Ïôðaþ7“vÏÉDsW:+pÂY!£»j‘¤Eao[L´ºÔŒŠõ,9u‫§è#ŽQ '\æ›Ðе‡0ëde¥¨Fo¶bɆŒ¼Ñ‹stî;°ŠÎ—:yú·bÛÿzþ-ÆÃC11Ú\°Û;‡¸ÚÉá²bí#[#á䢱ÊÖIs]¥ò?½º oûct?£"~Ì÷•\ƒX¸ ß”ònn=~KâÊNZ½ ýH4q„ìè ­h þH^±…ñ“GQ\Ñ€ÞeÃð1)g¿©K°|CÆúš¨ãù&*ËëÑ¿|”F-CèëîAì’<,[¿öÎT؇v¡®´ž~#sáúy?‘`0oeUh$4ŸAÆ\¼pj¬dŒ˜øˆÙELª¢œÈoÉóS¬ŽÙäSãeBÚÊäöE€ê‹h-;…røˆÜuHNMæA¨é+6ÀŸn tšq4_ÊG ¹RÑK×#˜^ºúã±!-š–¾~tcGñÞ0å· ÈŸ¾9Nó°hè~i°½39ü4Èñ2¯)ÿ•B,Îkáú±üÎè/WBVò·ÑŠŒe9hé,İV$Ò°¾i':ªàí„ôì\˜ èŠËD\Z*=v¡ÚÖ_k²—Õáôåt7Ûy/)É1è®Ez`|úp¨ï] pŠçÆ- äÖ)×C9Ôƒ ±x !>) ðf{æ+mçâXìuºábawÿf²ŽŽ  Š\Œóôo˜°&±‰0{âÒÅظÐzûú!,>Z[;.<ÅÀ³´âhǨ§q9¢O3R6<ŒŠß>‡wžÿ="ÂŒˆ^±ƒ¢ËHA1â—®AOc%޾ü{4'Ã’±q1í «949É­Ý8þæNt¦'Áƒ%©Q  ²£Ãƒh«,Gk»À¢DŠpCãbá§FÍåRhhé:1¦AlF|¼mÈ ØLoAbî245ìÂñ‡±27­ä"%oYEcc†$ç-Gó®·qüP>òR…béÛR¡å&ìm Fêòåh{c/Ž>%±¾ák±d{ü¬Ïš…_ Ð¥›à¡¹®…q¾ë›úråËÓŒ®†ZôööCÃh@ôMBQ£€{‰i Ž€‘C'¢ÞÖZ;!M#ÐШk\©ˆÃ#:êÊpâð9r ;7[YæÇ3n0GDC‡ðÕgÒñv ’×=ŒØ(+±•C¿n^R—€o€ý-xë0&hq‘œ†ØèPr÷ªÑÜÔθÇtIdEXxZ.žFˆª °Ó÷£9<± q°Ä%!Ðÿ Ê16f¢bBpèài¤?ôLÁzt½Ì¹O¢³’pñSt“†¸äxþ-Nµ†`ûÓ¤}"6uÃĨ6Æ` ¬‘Þ¨-»D§å!>3o¿²A[‘â‡ÎŠøXüèMÁ¡§ë~æåìº'ZŒ2¥{B(w¡n \¡€ìéS¯¯½=Cô7fëjÂÑ=o¡½wŒ:<Ý(|û5ÔÔ÷2£uÅ¥¨­j†ŽŽœ;K‹°ûŠyí\f‰,½ýñaÛôŸ@½GãƒtÔE]@o32Ö? ßÁ \.ãF˜•H.ƒÃÅ,Ô^>&$äúŒàÒåF„&¦ÂÛ‹wæù*.œÁQ{?jN0’ÌáRøÒW†ZQ’ ­ômxöàØ»k äÓïa†ÇµÊéµXzz*.,õÑB#‘½‚Ñ$JŽâö….]@#ýA †Ü*KX,r–¥£©p?Ž)GÄÊ•ð÷•°k¢L¯A`d<–,MF5–Ÿ8Õ€˜U+à§ã]1¸2âü1\è €(z÷º= ÷Cã勨)©DÕ…SØÿÚË8¾ÿªÊ*‘cXÅãÕÑÕ8|XaŽð@Tzôºú&¾†á$C(þU4|`{o .ËGÿ¨Yëóè#èŸä2Mö5\BÁîãÍ\ŠÌ¥ä^)ýØù;ÑÙe ÷u¢«·CÔãFõÅ|œ?wãä|·].À™CgÐ?Șâm(?_†^Û8<Çqòõ×™¯vò®ÓbÐÙÔ€–v;b“•m?3¹¥ö´Û=”K,¡‰‡N«e‚kM-Æ<ý‘”M]ØøÐ7èPg=½È¥¤²­Æñ©I°÷4Ñ7kBø„GÁBÑ»çØÚzzi)ˆú·ì€;=Pps¨ávwv¶(àI`lVšFz‘³v5:ÏQ÷Oއ·m×H'E’ýª*?:\‹FØæÍH‹02|žï¼ò6E?½ÈŠ·`Œ2Þ¾ö˜í¡ïòA_ˆ¡ã7K+¿AŠ•}ƒÂ0@ñô©“—°a59&ô“&Ëõ8ÅÑÃ6h6 +v©€:‰¯ƒ—õæï^ɶÓmÏ7´¾VÑÙqzN:zš `ðŠÃ,]’‹v›7r¶=…˜`Ò¨…gk öGÂSsÙÒq+#x¯ÜW…¸ÿqP€Èƃ@¡µ³ @"ÇËjd$¿þqúê‹3)q½g@B;aމ£‘ eöó›#Saõ<öº&z ¡Ê†’Ëh´úÒ0Ë‘¹¼¦(ìþçA¢€{̤Ñv÷uö(ÀÈaí(®i¸iqkRšLÜ<ÅuÇÈ 9…䎈› ± ÖRGHYóÑa­N~%úƒ0Nºk‹PÑ ìøìgkÀ‘7ß@K×09+´œ¤Cg[G5=W`ÙãŸÀÖMi(Ùó JÊši%H}$¹ßÓŒªË—½áqlÙ±­— pâLõ¶&93³×ã{PiE‘­—–ŽÔŸ¿x^ (ú™^ädx‰°r–ì ­€<‰x"VÚBqIâÜÚHQfÈ%qþÕDNZ[è\ÙGPWïI biÌûŠøÅiðÔ»îï7P€ô·Ò•Ñf<$ i':o¥GÆKb;;cýJØÇ1ŠxåSôiÛ«.ãâÙÄ.'€O'׊֯´dmªn¤1£©”]À…sÍäp/CfV<-‡µâA¨¾‚¢U» çíGM‡ yk×",„ãEËâ. WÕ®Ì;fà -žó jæ*=AÑñ6é!/>Ó°è–L0¦º^á„rþ)I´¶ö¥¥¯„"ÈëëŠÈªPœÂ¥†a¤Ómk¯!½Šái €7i/àzŒ Mø’Tè|pád>Li ‡¢üø4MÀ:Æž¬[Ú õõG\^,šk‹q¾¸† }%|Ç{øÜIh£àËq•V^ó:I£ÝiQSÀÍ!\ÔÃëîÜ=£WË ÑËénDSm=†úÇÐ_Vˆ“‡ý`iBaáE$úE"ÊšŠ‰þ!nx ˆô Cõ¹S°û„"Ä<ÁïÇ‘¿ï,’z~f¤¯ÊCÝs¿ÅîW±mÇjPgw½mÆ„‡Z0¢_ óÉì}ö%è>F«bý.;Œžá0lÞBÖ> âÃáÂÞðóyR’"¸qŠ[›ù·¬ gHçk"7Ä‚ò‚ƒ8m¢>•f´‚LŠÇ`O¹N½èjëD°/C‡E#+4V>z€S¢FÛ÷wµ¡½¡mteÒÙÖ‹ÙW‰(e3]_g3:wºKG}+rbý„›ÂWîѨ¯½íMê @OB£s¬ËMwºJ‚• ­7Y‡ ‚\¼sGŽ`(-ŒzmÐZ¢ûiÓM‚sÌñÇ$œªŒ@ĉ̔`°¯¾ gö¼ƒa#bƒh=ßLyªË1 ± ¼¿UGÒÎÛaþèokD§­uU•@P2F Yçi„¯Ú½×:{¨¯…±µ+˜´ Á±„³ÜqèëB[s=|<1AW2— ò1ÖS²ü´õéInžž:Ã5-芡¡Y êZúJ½Yq">æáͨI±€{È¡Md¸Ç4ô4”£ªm“„dïPò ª3E ! ùEÍÌ·ÔªÀ®ç÷ $ž‘gôŽC¢:XÑi•5&º£gЫ ÂòõYðèoDAQVS’¡zu¸¿= pÂd ÝÝœ= ¼î’1$I‘íÔ£*CtJ6¼Ç5´z6‡à¡>ÌÐVÂrÁ(ºZÊqöx &èZcÍ£é46é@W÷0Á¹èO  ‡a°ÆÆŒHÛô$öµh®*S®*³ràkñDwO¼ÉiH¦5fü0­›[ªÐË6èƒ#áïmBO ý´y{"$q5–…“1Ô‰în3B¬,gBn`ZEè+ÖBKßiut£ÑO±·Ö‡¾Ñȧ´¤”th:¨WåGਥŵCŒ.#)Ytä.‚ãzÚÚ`0D3ž²"»ÇôSA?fÝôѨ7D¨ð{‚‘GÉy H.02ÜVt|úêÃÉñr+×ü#©»E÷–n@xoéë.}±R€Ü_“Ë~¯,ÃÊHÁCcETÊRå†CÜJÈI|„ÀO£×#uÕz$P?‡» :}Oøé‘M1ó‰b=“‡)A j•X§:E,¬Bq§rׯ(˜¸fQÏPÜ$Ü©K’)ÀÊgx‰ P]#×`¾&¡›ÎÇŒä弌q¥;šQ|ª^#bÃ>]‰œ1Ù?g_„Ãèém¤(- ‘©–ß•è"$…PÓ|£S³=)¢»þ¾—ÞŒ˜ô\ÄLsßYûs 48cíîé,SÀ ªÉwq*ñ»,ÿãÃh)¾ˆFŠ2ÛÞÞ Ï-ñÔOkÚI¨žd“I}…D•Ô2}µL^S÷¹p;«‘lW@ŽzFòˆ8uòÇøP”b{í´@-;}—/µ"t„.:"ÉñAà­““&“;ç ™€¸‡ôv÷o(ðA¾@bËxÙÑt¹åçNÃîM5ý Æ%„*WzÓ’GÞþ]“Ô;rõÚÕwi2×Ôû}Ws:ï³7\¼¦†yòƒ3LµÓ!Ôž N½Ç¶6ŠÈë+êQÙØKŽ÷òÖeAâ%_3û'éw¥Ëü}åûõ=½æs9þ¿>_wïšç¦Éî¾´è)à„‹~ˆÝœ ¨Åš¡£Â²×âƒ9käÎS>—øI08mš¯u ­tú@dm~?²¶H+I'rˆn¾ãÍמ<íâ`yhtK] kÊJŽ”ŒÿŸ´-rÈTƒ#Ýß8òWz&÷´¼.1°å»øX¿>Ï•ÌÓ~a™æP¬~ꃠÖ0 L.¢ã8¨H;íSsqQÑ@˜ñT ˆ†~C5âP{$i‡†í’¹èNwN7 ¼sÚ¹ŸtS`Z ¨5ILZ‹¿½ù¤µԊõ 7~@Gë ­p‹;/жł:/G”-ÅÕ^ü.âÙÛ%é¦7FyFVe±6õ¤¥³ø[›W‰ÀeT¸-“¢ÆyÕ¶Óø S›s@C}²Ê¹ýœ¹oÝ•†’óîô‡¨¥Ò¡ŽVÌ2 œÉ‹¾;eîÊ»!s^C]<g^“XÂ}Ztöñýp>0ùéÉ©{ЃC:æ¡®o/ß-yL-ûºG¦ùy}©Ód™—„66êEÚuè§ÿÃÎ^OZ8“NSè8WÍ/ÝýôAJ½Vwºs ¸áÓÎý¤›7P@-GACƒ )ÕÒJ—ÃóêÔÚP­ç¢Æ(m8üv9ª/£§w••íØ½ûm^8wNÛ`"á;P\Ü„áy·KÜ ²ê;•QH{c#*Y¶„6›I˜:_øÓxÄ—=/Îlçž]™wठ8F«ïήv Ð:\…œ' ~Û¨8’ghBÆ+DE-ÓÛéðrJÒy¡¦j˜ï†•Ö¼õx×tžV¹Ã¨( DEI2¾ödúû½Þëñc5$¡«k%ŒoÁ×ÏÄ<ó ”°9ªEügŒÖøÒƒ;™öÒ¯1u·=M—PÀ/ž3«Ãä2ÎÝWömtTƒÒ F,_Éf¨Ï]sjÍn@¸PGn·[ÞU/¯qž.ÅGÙüêˆ7#hû–ƒb‘äâñ—ý‰…²çàô‘CÔ‹T d¾ôPQK;C¼ÕmFñÉ×qè¥aþ«ËñÊ«ç¸UøÒ€‘ ü3q€.qª.”ÐGšëË„èH‰¾XñÉ#¸túØ|éú5íð£ßœ5ë¾,OqH•ã59nÿCD„ޜÞZZkr®øp.{ëfʺ}=÷*‡pñ¤ý^^£ädÓ]‰7ûÃ?—çûdÃH ÷­¡ªœQeŽP®‚î~x˜gïµ4×iœõæ®®Ú§å8VÔF£¥1¿ø¯}øÕÏ^fÈá>ÀCÍQt4+uÃjÀ Â9ÓòÝê¨g\g•á†\÷j]*wŒã1ÁˆI¡4XË\·f Ý߸ôäµ™dH‡ì£(xã0LF-rrÒ8o\_®-möIûÖoÇŸ¬"˜ópÎ~·gµÄù3š³Ú-waó•"©ôDþi3C6ié~b~¼´Ò v¶ëPÙ”‚áâ0úýóâBê wL–žž¹1ÖWTâÌÁR„d†ƒŠ¥1½œÇçÉpPf3ÀÇ7m} mKd§Ùò+¨œ.DFPDEìÖ Ø¹óêªÉÔ›Ájtú(ÜË’Kõøû¯¿£‹èiM9“ŽeÀ—¾ò}$Óíˆ#lî}hù̪ úâ¢(þ!ø›ÄlYDÿ‚œ\Ûâ\ê…«U^Z…ÿÉ7ÑÑÖDç½âÉú÷AC°ùÓ™¶^Â.°$¢ý>û0ºÉARnaf2ÙÙWö1:2¦£ä÷=ýrŸt`ëFgså]w#)–ïÁá }g/–äÃ>úöî}'Þ~:žÔ9h†”)2À…mxX9:œáÓîìón@8ßGh‘µOï3ŠÕ¹Íó°WŽÄ¡Æ‹ðhb<¾¸v9YÜ\H½ä:ýç©ØÛÙ‚M«&žŽy eã¡4ìf†¶þ~à)ï.ô|áeñ~¨io…7`{P?¾±i“Ò»“žˆse/Z­zŠOŽ˜FÈvqŽ_Û=jðô¹óx«³¹YƒØ¶násŠ„£OŒK5‰k{»ÐÉ»îË/£}4w)þty¬zÃô'›u–¬ÆN¢äO¿ü*,ƧÚ;-. ¸áâÏÐYD”|jÞµubB£tè<;ÔâkA £~\ãú-îax)3e­fT¾ž¿€ðÝP·ÄWö£kÎÈð°DºÐÁÄ1‹µÐ©²ÈÕî4MÇf½Ó²æâ¹;añQ¯2ÔCÛ=D O­šósÑ|w3£€‰¢šH³ 9­ˆàfÅñ1îÃÐUÜiñQÀ ߘº{4 P"49Vߌv}̧ž¹þúM~Ë,¢Ì~%ˆ‰ælá Á8ò'}%I½¢3&º^‘ßÜÛoš¤ i§„v¶Mµ_ÊçSò¬³|Õ'©“¤sæ½iÁ³pÃÙq²8S®Ž´YŒ@dÜ4®Žó,´yÞq'EÂKXA¡Ÿsì]é—š“2÷&3KÕd4ÎxìnV—r Í›7Ì¿ÉwMêRÌ\~ÊÜ¿Y×%ÄH–wgª°3εLç³Î9(bæ©yoÖÆ»½.mýdIwÂÍtÐ\;¥Žânû¯óÛærgX¨pÂ…:rîv/h Œs%ïh¨@MK—ãî ˆ±y£ââB™í87¢™$Ù”œ”ì¶ã£v´5Õ££w”–´)Ðss“û’ox¨õµµó4À Ç |/n2WÊÊ™Wvœ®ÖÔ·w"<*fº¥a9ÃCh¬­‚]c@<ÛL_¾|˜V¼=hjé@`xŒÔÁd^g’º¥ŸS.9oÍü“e‰ÄjÄÖººhý¬ˆ°B;[åϼEî'nEɹÔÙڌƦF RgA€¸Žs>$< Öà%7˜:_nYœ”Ç45¿‡f MÕ•Ô‰ô@DT$ü||Ô}™wccôµIËrO¿@ZNõ`BÄ:á-¨ïº$ù{›«PÝbCDL üMõ*`܆Šâ Œêü 1 á³¶žfÔµô!8,fÆ(—ƒ–3Íæœw”eCùù³(©¬a_"°$;¡f£jÇ”jÕ»?ݘ&Ï3zÆÙM7î‚jƒîÁ‘—þŸþÓã[ÿð×ø‡o ý¹÷ãñã·;ÏÑ7œ¼ii«àKEwîmW¸Âe±ˆôñEgÝyüî·¿Amß°âjHùã,¿p÷søßûy¼STϨ$l0¯{Œ¢¬ðMüﯿùý.ìíø·gŸA?wÙܦ&©g¨·/üç×ñ©?ý8žÝ}#¼&\˜¾Ž üòŸ¾„?ûËÏã­ÂJxÑ`hb̆šs»ñÿþåË8Fç¿N.†äì©Ãî—~…縑:úæÉ>P­‰}¸Ú7©_¹á5oþÉ~-}ð'ý’gäšÇøê/çã{_y?žþćðÌÛû0";âõ˜Ú!÷÷9£€ ‹ÇøÎ|ßûæáo¾þüãw¾Œ/ÿÅÓxú©÷àÇÿñ:FÔܘÌw5/8Ö’dyñºÌ™3ý 'ð?Ͻ€Úކ„t »¼+vý7¾öÕ¿ÂÓ—1L&˜Ìáö5ž{_ýÚðÆÞýxí7_Ç3;w¢µ§_ÝwÔpõ_ñ6üæƒOx~½ë]ôØìªŒ‰‘füöG_ÂG>ý¼u²ãä,³MMç_Á¿üô8QZ©8“ªNéðH;^{î—8UÂÃ禼OêuÎùÉvKÍÒF™ïbx'y¤ŸðåÊœç5‘NüϾÌwññ£ŸþßþÛâϾôœ,©‚œ¥JwrSàn(ÀiæNn ¸)p¿) ¢6µ&,ß÷{ð׎ ½ö<~ýƒoã?ù6ny1^6ÚãË¡ŒÁšu!)ÂBŽ]-ŠK¡÷Ä¥Ò&Ø[N`ç~F1‡Á°a ‚ý ðÖû#)g,¯Ðõ;/â±å_‚7[_ÎEßÖlZ®#§a¡Âãõ‰»ËÄè+ QX´œ”0Ùyï}ˆmõvpA†PV\ˆßþìçÈIùÄDŒ8A¿gÃã’dC%GælÁnüÛ3¿AÔÚQD=‰JÎÁ™ åÐF`åªHŠ¡ó_Fvi­+Eþɰiüi­LÆŒ aqð÷ìEYY)<ƒRkîGþþçQP;„ìt+õ6i2íÞ¯Åyö[C†ó¢²‡ÿS¬K CW} ^úÙpzÿ H_šŠGW&¡êüiœ>‹!/ 6myqah©*Fs·½í5èРëòKØu¼=ô'yVÆQ'ïч¡ßù.Nž9‰•Y Š{¦õ°áÀëocHŠÌô \®ÙÉù2BN59ó×%-Y]Á8]w‰ tGvÅÃkòàŸAÐFs$:fî¯Øg~ù,R£¿ˆ¬êÓýŠ8¤¿…óÐc”ïnþ›øù Ï"©¦Úáz”æãôÅK˜ð Áªu›‘ÂwËí —Ÿ=d$EÁf÷B||"ü4ݸp©A1ÔÕ<…W÷BÞã_Äÿø)tþ÷­gPTR‰Ôøh˜hñ>;ì÷ëˆâþùÀPÀ ˜¡vwtÞQ€hIoÐ#8$A|C­>xhÃ*ün/Caµãôïÿß|n7¢##11؉Ÿýúçøþ~޶#øúg>~ïH$%­‚Å~¥•ÝÈß½K“²d Øô‚5*ëW$áwgO£´¶Kcôè¤(¬"§ ¼cY¼/Þ:äm×ÓF°ÕÐ@;Ξ@½=ŸùÄ&ì~íEì),AÊ£Ùj+õ ÃöG’Ñ[»¿zq9¾õé “‰Óf'6“Oû`7J/ ´êFLûp¼ÈŠ‰Ò·ñÓŸ½à¤h ÷uã¹–àßû>bGKñ½¿ùkœìƒ5DæºZø„.ÅWþþß°ÜP‚gÿû‡Ð¯ù¾ñÑUØøø_"sC7޼òcTO•^ß÷ïùCÎy:k DC†…a%•o瓋܆]ÏïÇ/žý5úÆ}á=aËoî#÷üï0pðüÓý#F¤Å­ÅDÏiTµôb÷Î|,_²Š‡ ú@$.(j%­cðRÁToÛ†#úyÐ:B.ZTö'‘ŠJ²Þä 2Ý Â“=öïËGŸ×&|öc:ìõC¼±gÐ#Es¯Pä5âͨf<þWÿ¿~þøÐ§ÿ«²×ãKßü{,Ëö*Ý)_K8²×ì€_W1ÃÐh7._,ÀÙ<ñÈf)–ºŽ"¯58¾÷(R7oÆ#oEuôòß|Ý,_ÄYð4"1ç1<¹i)ÿág8|±¢c:% Æh±bÝCObÍšõøÀ§¾‰nÈCpô|á;¿ÄK/“søÝo NW‰‚¢3(.<„Z¾þÓ]xé¹ßâSO¬v´”†$.Çg¾øOøÄÖ:”Ö#4"V?¹=3T¸œÒ>÷×ûO»­ú­Mhhl™#ïàãÑ­5c¤»GŽä#,ýýøåïvãÅßüþݧqèT1†<‡àåïO}õGø÷ßÿþäãaÙ²÷à[ßû<–eGC82—5¾¬Úò$<[NQ”Zɹ1†¢¢ƒ¨n3`ó†å23rÐM ˆDôÜUW‚ãç¶~=ûØg‘ìoDáÉ"´÷’IGú#ÔKH^û |xËìùÃÿ  ¸šn{œÓAKiǘw0ì¤d­ÁŸÿõ?ã½y XØ@IDATˆHYƒÏ~åßñü‹ïà™ÿû}$jËPZÃ>¿ý<*& øÒ÷ŸÁKx Ÿ~d)ü¼Éc§HÚœ¸ õÕàÉ Ka6š êñ´–á?~òct˜#‘œE_šnîàýŸÉ‹¯F7‡pñ©»G €Žpq(:ð2>õ¡êÈÑb“RV‹5ùµ¯"g0õk×Ç¢0}@ E`áx·®ö%qð3ÆcëÖ7QÜ+ J|~Œ\ÙëdsÔ›œƒ¼%QØ¿{/[b¹£GaNÊÊŒZ†TLK)âT‚ÊT^>…£Õ=ÈËòÀ¹â³ð`(¯–ª#8_Ö‰t3!#M9½ý‚±mËÇp²è+øå3ÏàOv$9Àâu%OHç˜&äƒÑ1Ñ8óê³ø“—þƒýè§/…}°M ,( KÙfÏrV¶cßå]¬k>~þHÏôW}kl‰¨G µ;- ˆ{Ÿ’ï`Ï»ûàç«#è‡_PžþøÇ;Œ³ç‚‘œFy&?xE >Ê„ŽŽv Pg4Ìš‹åÙñ0ä$#ü½1ŒrÈ¡ÃÉÙgð)Ùë÷Ž=Ž9Q(Øû.ÂxmIr$çÔÍArȹtúu\l™ÀêVœ¼T½~‡Púðv„¤2:¼gðÃýÎ}/üám|$›º„¢ x]µ97”zÑUrJv½þ<¾øÊ?Óøª5ÍíXÅòº;숊ÊFÕA|lܰZÈ%äó:c²sƒ”z‰¨˜È»ÙQ[„ïý×p¤f_þÚç°23^wò.H›¥@W•{½ù§!7ÔÓSú+‡?ÊØœœZú,ë‘cMâwš¤€º§‚›s@Ù,$ŽpÆ:r9¾ú·0‹i¢Ö¡áÑðEÅ©S“b׫ˆG\f¨uX-âÜœ¸™:‘8[¾“ØÑ!‘$™¨Ÿ·"o5N½ðþ°?„ÜÁ>d>¶QÁžõNî,OŒ6ĘCvWùÞßÓ‚Ó'ÞEk[ N¾ó3ä¿A.w$혇CìÖ˜+b7kÜFªø(¾ûÓÿÂϱ:Z}È’{}’öiØÏú’üë÷¾ŒæÀ øä'¿€Ñ¶J¼ýÆK¬z²l€l7’Ÿ[­|sERÈæïLj?ãm-óI^‘Úi˜Á½Ì;)4ÿ>…;—˜»Øô,£þ‡Ö[‰@CýP}v1ðê”gdÈØrpé'T<òçÈ"óéÚ™¦ÞK,¦ˆö§ïœÂÁƒØ]jÿÿí`\W•þ¿é3Ǫ̀÷ÞeË]n‰Kì8‡„Ȇ˟ ,»,²ô Ë.,Ø]z ˦“„æôàÄݲ-ËU²$«×éåî“dËŠlK²4š}×ÍÌ›÷nùÝ7ó¾wî=çâªÛW!'-ErRËóÉ%ù©Cµl‡Ë zñ̳[àvÃs}Ï>¬“ó=ˆ¶Ö:¼¾s/Uªšˆˆõ!µxî¸ý]øç>ŒÇÜèõ´sPË|Ôu^Äz×uøu|ó+Ÿ+sn¸ùƒHlÄÏ~óë¡›'©‡6ÍâTSÔñÔ›SV|ålròà|ýKwáX°_û×/bã²*±ŽÊÜLa2Ѥå,_–:‰ðÊñFôÊ’Eª]çO: ø¼hk©wgî¿ÿiqx‹= ¡~zW­,–yË¥0kÞvçoù\Ù#özs®ô Û÷ÔO°=% Õ5ó‘.×Ñwš% ÃRk Œ­Çñªˆ¯+/ºGñÖŽ“È»&W&»ËÎ#My…âñ8(CÂMÔ©Ød*©‹£²L”/]ÇC¿Ã/~üŸ(/_‚w\¶V†a‡²PQäB>±ÌÉ"ƃ²Æ´˜z(Þ=ؽ·ï¹ë7øÂû6Êv¹”¶Ä?»O=÷,Ö-|¯v1Uõ0X“±tõ•¸ú¹§qïoïGy\ô”+ð©$¥H]¼>?=nø$TŒNÖ¿Ý|Û¸åò<þ“­8Þ<ˆEF ¬:´´íÇκH/ÄÖWžDGG¯X;õ²Æt/Ž=£3yYNø¼~ †ì•¶K6özüâÙ¬£Ë>U ¾ˆê´Mp¦ ¤ª ççiBF-”¢Î³Õ ]G›¬µ» 'ß¹iî8|x+6¦ÃÚo‹Ã/ç|P–Ø4ˆcÊD$ÝHýtò#ñ“íÛñ‹;à+¥Q)ã &%Â6tà;÷ýe‚GD×n²,éÅkËð«_P,³)ÑU¹Y® á,w‹Ÿ«”Hòh+ã)'_¯|OYûä^Y >tóz|í¿>‹½[~"“УS_Šo_y%,ÝωÕ@Ä›ÌiÒ˾‰:š_Å¿}õSøè'?‹õ‹ªÄz8Ä5,öƒ¬ì¹˜-Áku/£dé5X”g‡Œbi¿x¿ú¤ ÝÖ½$˯I,äˆh,3·£Ù[ˆÿwÙEH” ”UÆf.‘y[ãù­¿Ä3o.—a*ÃÅFòrdãÚ;>‚·$Ìá€\š†/Îê9,±9°ô6ã—?ü2ŽlX“S‡|õ#xêGéèmoCÈ üù¿€~Ã%(6ºqϧoÄor¬hj}’¬£'C†mõoÈúÃ÷‰'õ]øÄÆ\üú[_Å3G£½õ|¦ý8¶ýyl¸áÜvÍ¥YQÊ©Ã\=Å¢®Ý2-Bú1(bÄ/7!âP®?ªšÊZ—Y¸ÖÔâ¿ûÜù7âÙÁ”Y‹uËçÃõê«r*É”ujI¿¦gçHÏÇðµ/Ü…¿ÿâ=X¿°±,«.WŸ§ä,à «+pïžzÔ.» e¹9š5.$ÊÓ fä¿þéGØ%žÍfÇHÄeÁš›àl|ƒ†J¬\&7h©ÉšmÚ˜œˆËÖ®ÀkÿýŒÜ¤, ™º…’Cäû OÌÀÍw~ÛÀÎõý%Éd'[J®X[ð?÷þê.¿Râºñ“oý=ý‘|ÿd ¸xð‡ŸüwÈ<Ù,ïN|ï‹wâgv‰åÙ®„•ÐÉoBÏÁçðoþ+n¾«Òð–xwÚðÿr'î—ï ¤Ç•·7ËM^jâ™q?×s%·L6®.ËÁ'ooç‚ÌsízÆg#­Tb.I¥¿ü£ÇÐíÔnb®þ3\a ÂÌìI`,õCj09±êÚB¾×‰56j'uA3ÛÒpý'þ¥+%„ÆÞ0'—`ý¥W¢<Ûž“ëð÷¡2KÖ"• m ×Þ„»¿šÆ.rS%ð®\¬F’ºpY“òpùm_DÁò&äÏ_:4ëGʰH`ÛMïú,r—Ÿ”‹YP«ƒND^¢Ì Lsèqñ5IX”;4IÕIgtbÑÚwáîŒ@b&WWAg/ЬšeàJj6â_¾û' èë“rÜp=Âa¥EµøÌÝ÷bGý äÏ[…âw_‹×^}Q&ßga¡ i'Z±{_2r˱ra6´ô#95 îŽ:‰÷‰×îëêJa!™?»ñÖ»‘µìäU,’ é‰CåìØ°ùsÈ_Ù — ójß<9Q™%H@ ®M(DM™L‡PyÈCݬ-¾üC¸;g#’Ä»wYi•Ìó-Ö–o##r+7á‹ßø)šº¨(/–ãT{ì™Ëñ¹/ý+¶×Aþ‚õÈ»þrì”p8ýá$Ô^| LÇPw¨E‚ÂK´€²2´´tÞUŠpã#øñ–N-GÉEøØ§2‘RP…4K ¾øõ"ôôõk£ Jš*ëjqM…ܬMíRîL°¢FæVÖHX9‘¤_2dzBo‡kN4w²œÚY4ÙR¸? Àiò£d0YP¸`5Še«rPÑI½7š¨ÝpVmúÄ/5±Ü™™µÙùÚqê¢eufaÝUïÖæþ)ceÍÂ2ù;-§—ä•ËpÚÐ…N}n´ÚQ¶øb‰W8zï¡×*eµQåÔM­_š Ã+Ö¨ØkÚ¥Tû3"üô&qb©^…Òyg¶Io’²æ¯Ü„…«åP•§SR)ÂRÒÐû\TÌ›‡ÝÎãK÷}Î’õ¸¬6Ï<ò;1µ¬À‚² °&,Y%– )[µ{ù†«°bØ\¡êªÊQy©ú¨ü™¢ˆ€tHXo’x’ó‘§Î+ÕGc:I³ºÉ9_%óók×iC ê|Vç`zq5D+ r¬ÞšŽ5—߈õr3¥–TÛçêüMÉ”s^n0´ÏFÎc 4X¼p•œ÷r~ ž‘*¨²G6Œ®›ª£Ñž'1EˆJjdUå­öWŸåöªlÁ*TÈö‘ïÖ©<åæªzùԬܠ«Š¨2•T›€lÔÈÛÃo>ˆáÌÊX»°þúa8ª6£ Ó‰ÄÔ,¬^'CëjÉxMN±ö½cŸ1oÎU–V_Él¼üNµeT~CÅÓB{®¨z­¬шß<ü$zl/²‹®Àÿæ£(O1 ÕT»•˜r5 f]ñÒ0e‚ÚŠŸ6{M©« $%¦FuáÛŽ8u9‰Ô9¯æØžJꜗþ?×) òR^Çc“& Æä?vŸÑïU>#ß/Uÿ±IÇcÏå‘}Æ~¿Ôê)£“Ê/oþUó°<õ,zäuØK®Çî7¹™š¥rt”ut:’ê7&K€‚p,¾')P×C³˜©Ôü(-ÜÄ/S(*nQ^›#×Þú)\{Ë'áË…É"ëÅ*1p®+ÿˆ¨!A¿×#Có&Í04…,æü!ÊéÁ$óöBÒ9ÊóvÄJ;çÁL€:çaHÀ•ïú®Ø|§ÄM{¾M¼ôå|m©œD–ãî:Ô7jˆY"˜ÅÚŠp\Ns}£º™H€.”€EÆ’ef¢K¼$wËÒ[ÊÑUýó19ª4‹‹ O›$ÎFxxÈpº8ª~ñË„ò}ûv¡£µ 5Y™ôJžÊÉ/JFy¦ËœU“{;Ä3¸«³uhÝi¹ªLW͉|„¿ºá Š‹ÉdÐÎy5¬Òtµ_ u9R¯ý>•%%Á&‹OfDc¨6øw¤1˜ Ÿ9´Î[æ<‡XäÇnm¡NOÅ=÷~YªOĆ ›ä]D ïÆ£âLЉYÄåÀ3Ïü ßý÷{°P¼³//+ºêFE c¬rÎ/ÍÉÁzñàýé#ÈyÄn¿yy…òš¶†hèÍ! Âë¯oÇw¾÷-dÉÒ|k ‘h:3ŒÏy몼±Å«Y›k8…ß35WÑå•SA½· 9ßœ·Pîi„‘&Îòâ“€üòfŠ'ã=—]†O<þ8>rç-(,.GRR²6<>[­R+¹ôtuI˜š&l”õ¡¿té¥ÈO–þ9Û°élž2Ñhwr5Âu:«2myɸT› X¾aøÓßÿ?ø{ ®ž«Ù6mÅ0£©PÃún±à;Ò€ÂD¾rÅ•¨ÌxŠ“¹K•iÍ ±@åUQ-‹2™$ËN†Ý]øÖ·‹;²ð³{Þ‰"‡L‰—ïÁdXDù¾„QÞA¬^ìÑH,‹ÉC·ÞЧðÂáCèìáµhèB¹ªÕ)²2’±~ÅRl’0_1b0 óJ{=pË\Èd[Úr3“…"aì[]È•'Âd.è“-j2ûK] eòŸ7nÄuUUxúÐ!Ô··Ããê–\&)&S.÷¥¹l2OâCë×âºÊ*”¦É¿ˆDÄ~ÂIV59´/~sØ€Šâ$k0ëµÅÎÕIâÅ£w¤byAÞìôÉé+ª$5Ir$ºa’ g;·Gö9Ûç#yñyÊ(§ŒŽ’ÀÛ èäÇ*K„Æû—-Ãû—ŒÏåí‡pK¤ Œ\X"a” dKG¾ñ¼LÁ]—\‚Íb•}@ûƒA7v6¶¡7d’Ø™HSqç¢åÂ(¢0Qæ{^\\Œ‹‹Š¢§^à:gv9çÕÍÄd“|OÖ]}-ÖHhÜÌè$8c—LjdkÞ‰V±ö‰È¸mûNxŠQ[• ‹vã2\˜„à))ÍAÂAÔ*1}}>Ën–2Ýß+«ÉJE&ym–ál5Ô}*©º‹oz_ï 8™™a•åòt*¸ºAR.ã*µ¿˜ü^©¸•‹º…Wë1Ë_õ$ÂW mÏåDA8—{ŸmŸêÇ1bcfjÿ¹F²oä•“šUE9hkñËõj eÑñàѺÝ8ä·ã3)©¤Xã>O^ê"yž]¦­³yÎOÊÉhÊ缦´°å™—ñT£½q¶?ú8~ð—6ääͨ¼è"|í¶µh}óU|úßžD¿]bD.^Š/|ðbd'žV²îpC¾úŸ}2u£ÖÌ"|ícW"¡å¾òë1 ËS6wp×ÇoÀ $î¨:@m|ýøŸ?<Ÿ­Mn\ë˜Ú🼉*¹ñ®°öâÉ-¸þÆ+ðžUÅHm¦AÊ9z´wßý(µ¦‘èH¯;w6bAM®8™©6«ÇÜK„s¯ÏÙb 0š-2‘Þ)«IôËuF.4Êâ¡®7šE›víQ䡽–—ZäãáJŠØJ´¥ãŸ/»J[K×(s²´cµ|ä%ÆT¾êY=$©© {Zû‘”œ†…²¢Íèá¹álùDç% Î+¿Ïl­ÃÎ@6ÜýxùC8©OÂW>z tßÂço!çEAq‰œkY(¿î ÜX›+#%bT¢nè”Oê Ì«®Ä—þáz¤xšðá»~ï=^€«mmصë+Wá}ס&× £šV¡}WtøËcÂÏ_>ÛÞ¹®æøß-Gñ­ÛcÝ+ðôþãeÛ±ù]×âÆ•b<TíUÙöô¸ñÄ»ÅȨ©Nùz„±dI>¾ußÈÌL:]iuÀJ„s¨³ÙT Y  ]ÃhìïÂcuÛQX.Jf;.-«ÀÒŒY×¶ î;‚®LaqDÊÀeeÅÈ4+‘7T_µŽïñîv´yÂ(§€ c¯4Æ·Äi ãhOòÓóquE1Rt<ôÖ›xðh7–•W‰U²¥ÉN8†/~³@€EÆ*ucbMÆÇÞµ ÷ü©UV¬Ì —TÃß“‹ªÜ…¨Ní×îOY©ÈÏKDNf:2øþ-çh/‚:>ó©Í°ÊBÔÊ:§|RÒK+p˪"<Ô:€ÕŸ\µ¯7`ÕÕaóúb´½ùþß÷v¡kÐ%ËÂÐèÆŠu± < k« ‘“•†Œ¤D|î³7ã­÷-Â?~4 Α9¶ça”›£êê,üÇ÷nAnnò©{)5„œžn—÷Ã_ºóäSÆc¯²M$@ÑE@,wzŸV½™ :<¶{+žh8ŽO®X†Þ“ûðÍ×w¢SÖbÎqر©z –"Ój²øIdîú£‡pïóE0½Ÿ¿$uÇ÷ã«Ï¿„ýƒz-ŒˆÕ߇gŽ6Á'æ i:¼Ü܈í]nøÍÈuÚfA¨~íçîµ.ºÎ‡˜ªM¹éÉð­š»’“HB‘k禚Ê«œNæä)ët¿Û‹Þ>7BÖ\üÍÍ×b³whŽ_z¶÷…D8Šƒ‰º1 Ð5à‡Å.óe %‰Ç-ñAE|z}È«\„¯¼R–æ˸ӂ?þWÞlëBuùJä&†Q°)N+:Ž·£¬zºvïǯ^¨ÃǯZ,Ðç?Ç•Ý"S. R$DRꘞ:ÌáDA8‡;ŸM'ˆ ñqF^z.®œ·›KS°ÐéÄw^Ù†íxG~ .Îíƒ!­ŸZ³%Èdýìk“¨Ür!N°ØäHÁâ¼ œ€üd‡(‘y„µ9÷2¹ŸX³¦#¸÷Õí8îò wþ<\^Þ¯Õ[Ö¬Âu¹2&ù)#Ó×qUŠˆ·A Õôüë±ç˜[ž÷ á@'vw¸°ã@¼G£a+^ßÛŠòŒ¤Z‚xùù×ájLÇu×\Œâ‰9¨N<ñt6JÐíMøÅ“o"Óuí³àÓÿT†ãŽáxÇB[÷HV¬¬LCnŽ„-R7/2æ|ÍÕ+ðè=áÆÏG¡˜_³›Zðõ_¾„K®¹ÉÕã_x v“ÜT!N"NÏ“TØ€¶âÜ€c1QŽ%Â÷$@$0ÔÄõPP„™„å(KKAMŽ5 á‘)²sSS`NNB±3z‰ÙöÄÞmøßÃbñ“è K ªÄc½Jb&ÚÑÓ–y„zä8“P&1»%`°Q-T-Ö½X2,§l8z5Ô'V˜°Z„wÊ3YÆez9ß–­^ƒe‰—±Ü,x×;pƒ «:ĺç5ÏÇ¿ß]“Õ)Â&Ü~Çf<¿£ é…ȳ`Pódµ@NÅ"Üÿy±è?fG!îÿf5Š2Ìh<êÇ¿~ùýrÏâEB‚,%©Lxʰ¨¥ òç×âç÷fà¹Ý'`OÏÁ¥‹rÑ'"õs¸‰ŽD˜KÖáçµÁh±Ëò¡#Çñy*(§BÇ À$ dX-hŸ\™ëÔåq㤠¯eËJ6V±ž(1gÏ•qÃfsâ½µkqÓ’ f$1LK.ˆzu9‡5^ ÑéäBm–g“ZOLÔ³úÌ(ÛÔeX G½ZaB Ï![”Å…‰&C@ÍéÀ¹Y‘Óï̤„›Ú¨m—óKnt2ò ðîâbt#‹1"ù¤äå£V½/Ÿ«äWp ¸$è½ÊGÝÈÈÍÍIæüeá–âRí&G9HÙduœ,ñrõ¨Nô¡ÝµEÏÇ{FF|s>„ç#ÄÏI€Hà‚¨‹T‡›âbEì´ã…†£è3å`svö5À+ ðtô#ÝaµEù(å½´µ>D(†>ì9qO:Œ=n3R32QièÅ G¢Ý˜†œ´D8Ü-ØÑxú$#vgÁ,ë¾Îø¿¿¾‚öÂb¬-.B…CÆ¢µ‹ø5†Ï5šÅî”ÉîÜ­WÖè³Y¤G¶<ä4öýÈöÑÏjŸÑû~=z?¾¾ „„“ À¹( †^†‡Kñ•µéè÷{Ð<膪EX/Aœ E£D!>·!] ,z2³ÉÐÛ°ÅC]ˆ‡\¢- ·.[ ·ÌÈʶ' |xÕZxE`fÊ{SbþqMÂ& ò¬V™€_%ÖH3{ÃÈ O¦uq ÏÕ ~F$÷(㾋Ù@ Y% «4Tgæ¢:+ïÌj ¾ùyE˜?¬‡ài!¨Љgr©LØ/Í%eû‚\õ^í«v:ó3 7§eÊ’F Ë¡ üK$@o'@Aøv&ÜB$@ÓKà\¢lt곕:ÞñjÛHýú\ÛF>ã3 Fgë± q4Ѥ݇ȟñι‰æ1Kû©)‡*"Óø(Çç­$@$@$·ÔíDSk7~ÿèëÔ\­Î1$õK S0Á¦¢@OUˆ!RAñb¾âòy0›)Æv‰Œ%Â÷$@$@$¯d¨×,!Š>¾r¥,uXïD=vEDv»Ý¸ëé¿  6 ŸøÄ¥p:&1Ô%ÇZRat$æ!Ó(ÏÀÁ7$@$00'ÑÓ’¨Ù¤ 6*ïhr>9µŠÈ‹Aèk¢²°%8´D¢–‡o‚s·X @A ½Ä:’ Ì^ŸOªÇï÷íÓ.ˆ1i™5zg)X-}f´à帾ºv³ =Rž7“@dPF†3K!ˆEbÁêôxðè¾zl9vµÕ…0´^@_ªe÷ú<ØÖt ²ßE……°Ë0$á@å¡$0 (§"³ ˆc2EÊ,a6VÕ”à_û’²¨œr·dš°ÜUߌoýÏŸ`ì=/Õ)5š‘@   ŒNbI€fŸ€A,[f“5¼9ÙùW³_ýè©B“IýÈ}ÑS3Ö„æ4 Â9Ýýl< À¤ h+‹ÐB8in#È\ANÁgˆ\Ã%zú‚5! è& Fx9ÊÝ}ÄÚ‘À PN# ¸`%Ì”ÓòXò[@þøÄ8*‹=0‘ ÄÇY‡²9$@ÑB@bŠ—rH†^œP$ÒʤSHâ¾õ ¸áëeU+,F¹‡ŸB>ãáðÂÞhƒÍ|–¼EœlëÀkÛâ˜ËˆuËçayqŠèÔiªÌxä6 ˆ  Œ(nF$07ˆŠ pøè ´ †PVœ‡»¬ýz.ý¤ u£?×é1ÐÝŠÿúyÔ¹ìøèí±ºÈ)êlVX‡ÿ ~ü¼t,€›n؈Ëj2Ĩ‚ I"j=ˆû~ò~µå’K*aKÏAmq*áT|K±L€‚0–{u'ˆ^â|²ïÀ!lmõÃ’’ާÄÚ;#\¨?%•NDžòU1ˆPéB-‰çE¢3 ¥ 8y€ZmKÄÙ©Ô®üÊbXI? ç µ¿|(oƒ)Kò2ØPYš£ƒ¢1¥peÂTùõøŠ%Ø“E4^Ž¢Â\¼th`L;N—6¥WªL& Y'@A8ë]À D#^¢¢±Wf¯N“?D\éMxǦuØ$bÏh2Á;è‚Ë+CÇb ‰È2YÌH´šööck} |ºD,«Ì†MB²T’…AöÉÈJEroŸ ïºÐÕmÐòJ°š  dW¯G3Ú,& ‰c€U#I›óç÷a`P–“`ÚŽ+t7Þ’€ý –Uç"Ñá„#qþ€Ý}.èåŸÝfÂé\T=tÈÊÎEVn.,®Nìn:t†!s¤¼©>ëÆ Ð©fÄãfœÀéLqG€‚0 ºP}/ZÄ"s¸˜æ0±^µ»áò3”zN,2$ëwáù-Û°§3„‹jËѲcyõ0‚63ú](_°»yîÆwû »-¸îÒx÷¦%¨Ì–à×êš« wâ@}=¶7¶!A€== ï¹v52\'ñ³'·£¾sŽä \·iÞ½¦%°¤Þ­ÍxüåØw¤ƒ+VW¡ÜÒƒþö%ìéÒãê˱,ߊö޼øÇ&üÒ7gFÞ{íl¬Jƒáè¡iÉS„mPÌ”g9ÏÉàܪï™Kòks¹DÈÊp:c;žØl*kw Â7]'Àl·‡å¿áÛpÃ\& ‚åúåBøû½{ðʉÆiµ„Ìe®±Üv¿r¨QX»¸xâÍSÊ \<·<†½ƒVT¥bo}ºv|øÖ =„§öô ¾Õ‡e陘WR†%9xÏóšhKbP„ &}¡ ¥XåZ›5ñøà_v`EjûëNÀŸ_„«W×`ISDh^9N/bôÅ—ÞÀsÛZQR]ŠÖvÔ‰²|i.jJJQ¼8ï»f1<-‡ðü 5Õ5¸~oÙ‡ý‡[±¦:–4…D´zE`>ßЀ½m'ÅYÆÄå'~vÍÊžÊ:è—äúî,5k³ f¥",tÆPÎZfk †0 ‹Òpç—`ï¾–¡¼Óc$±ÖÖw(Q¦ÖÝuïj²ØM4OSÉ™X_[]]7Rr²pѪùHm£¢¬P¬a=8,s r’”bGNN2¬Ù)°ñÈ#/à/»Z‘$BñF±† z˜Å:c+ZUi ÖTÃA3ª—â²óÊð¡«æaÿÎøæƒ»QßÄŠ,]!”•àâEó¹z2Ò“‘vaoVœÎd¦8p¢Õ ûäb^m)æåð–LQNú>ïÕ°¹ºùJε£|Q2¿2̡㉞]³³ŸºAó¢ÖP†›oªEB‚Xu™âŠa\u'satHOOÄg?w•d3ƒæ‘ «$Ž(îÆ×¿ú0ÚõOªä° zÄÃ’9ƒ2ó  h1ü2Lª“Ï‚:qî1a5ŸpÐ冻×Cb!®¾|Ö¯õC/V³d‡[›! øÄ:ãöø0èóAovÀ(Çy}AÅ‚é÷‡QVV…;3‹¤Ì0Ìb'|òé×P/‚1Yæ –%‹Çò€_ŽõÃå–y…!Ü^¿ŒK $ï€Ô-¤†„¥ ½³si³ ÿfÈà¸i$Ÿ±ˆ¹qRE°)KãØ|Ux—)†x1 "3•hÎÿ¬eó X#@Ak=Æú’ Àl œ-ò,—fœáŒ#f$@ñE@ Ýr¸tê}ªMOýpI$0#(g+3%ˆG!™ÓçSsù‚úèkވΊv­*óCâÀ2RÝèÉ‘ÀÜ$@A87û­&˜002çîHS~úà$X$ˆr:TŒ„pщàRhMªnMí=8ØØ%¹yÑZMÖ‹æ Â9×ål0 ÀdX­Fäæ%¡Û3ˆïüþͲuF8ñ VA«½Þl²V²Õ"KÁM¦‘ÜWIUµtÅl@fÖ<©¯Zñ"ZkA0,Šf™á,w‹'ˆfa ölǧ?}®{ç‰#ºQg|ÓÁç àá‡wâµ×á†ë—á’ å5zE–²fZ,Fäç§";Û.u®L$@³J€‚pVñ³p h'`ùnYÙÉòH‹Úªúý~ÔÕµ þ@+æ×äbíÚyR×è„§AªX:ƒ§yð Ì ÂÙcÏ’I€b†€-Ñ*\t²±_Ö3jC±a{~yÄ‚ Œ™€%¸'…®rqÏœ $   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ @ä PFž9K$   ¨"@AUÝÁÊ ÀÔètS;ŽG‘ €"`$  ³‡¿?Ûy•Éí@=RO—Ô³·×-õŒÖÚ!Ôëu0›M°X QÉ”•"¹F€‚p®õ8ÛK$0 :ôô¸ððCÛðÛß¾…@0ˆh3Ä©úÃ!47÷¢£cßúæŸñÓŸ¾Õz0,*;!ÑŒ7/ÅM7/GR’UZÝv' w%˜$@A“ÝÆJ“ DŠ@w·Oÿe?¶m=†U J`2¡M4%% Sò¡Ë×#$â0Œ®úf¥“±íÞ7¶×5!#ÃŽç‰ L]‚£wãk   Œ0pG${,"W-,Æ/ÿåƒH±Û ª+ö-56豫áîýé0e¸8ÚL®Ñ‰õ   Œ0pG$›t¢\ z= FñÅ£ œz'Š Ô©…àÔòH˜„3•Y’ Ä1e¤ðÂ:˜ü.Œ& À°33•Y’ @\PÖ=Zøâº‹Ù¸¹G€¹×çl1 Àl!¥ dºIYÉä 9@;NÕ}RÇN ±§ê¤TÞ92×öÓ! »(Ç0Æ a"ˆy„1ß…l @,‡BèÄ pˆW­]Åß;‡öݦ`О^|Ð#ÙiƒÍ4ñcGç3þë0Ü.z|°Ølâ4cßø'Â/ð£¥é$¶l;€&V/Àê’Év‚ ¿ÜJ$8dÀ* Ä!Ñ–3ñ ðû%žác/à3ß ÏÖ÷˜yBI§Ç@W;~üÓGð¹>‡]'=?ö\Œ¯ bçöøÂ÷ǯ^: oø,—ÙÿøáøúýàÎo?„ûŸØŽ½"G2:WaüŒH Ú ÐBí=Äú‘ İr¡•‡îÔ¸p¦;æ•dãP_»ØùÄ¢&B/,bìÔ.JWgh“¡Y{R2*sœèj"’ýôb! †¸„UYC‡êF2'U'U„Œõjûûƒ’‘„þ3ZŒ()ÌÁ‚¢ØŒÃPí£ÄêXOj9.19·ßr5æ•ïÁ ýCy©ý™H€bžaÌw!@$MÂá N´öÁå !ÙaÖt•%Á¢ ;íV8­Yb΃®®>è&Ø­&Ù'$« „ —°6:^Av&ƒ&y¯’A–xKÍL½·òho‡,ûfFb‚úí}n:$Z0ɾ f5»ït è—aaPö±'À7ÞÚ׌þ€µó `³Z‘$+‡¨aíîžA¸¤X»ÔÕ:z‚ Ϭ¬ldeg!ÔÓŠ·Ž¾M3ž.‘¯H€ba¬õëK$½Ä ×Úx?øÕóxõHò²蓹©ù%bY[|±¼užl›6áA…ÖÌbܰ¶ íõûñж“X±²6_/Þhôâ†+Wã¦åù0+³ž˜íô"4Ô×cWS,AliYxïu«‘6ЂŸ<± õÝ.$§dcóáÝk `P>”žžN<ùÜN¼~¬Kæ †P^’RG¿üuìéÖášM«ñÎ%Ùð{ðò–ñÚ«/¡Ç›ˆwÈö[7UÀn|F,ŽÊº(Â5¤ê5 @Ü  Œ›®dCH€¢@VFº g`À’†[6¯³· ÿöÀv<ýJÞU­]Ô.Á5•v<úB=Ž÷†±º²{›ÜÈ \3I¬p!,)ÊDÐÀ 4Ê$CË••UØxÍ:ø›ð_¾ŒGEè­HÓáÈ¡v˜ŠJqկō-vˆ¸óÃ#‚ÍhÒã¯Û÷ã7/@rq!J,n9Ö‰œš<,¬¬DuRn½f1’¼]xÅãEjnn»jv¿±'ZÐ:P‚òd¹LPüEéÅ:ÀŒ  œQ¼ÌœH`nÐ)£¬¶d”åÛQšœ|6U7ãˆg]‰ÈÎÍGÕ|Z~ä¥&À(¸’ª|lÄ¡#Ç`ÌÊAAšÇölÞߋ‹W× Õ†Y†pM:ÊJŠpQåôZP½¤—öTT†;6U¢nÛNÜûÀNÔ·‡°fEŒ!ŠÊó±jy%æeÚát:à ÷£¯«$Òœ‰@{7’ÒP›Y€Å¥Ùèn8 wŸÌq¤pn¾líœ&@A8§»Ÿ'˜v2le¡®@}ž>t•cH’L2âêË ‚2ìêÊlAÚ59’°  ýÓ6¸u,]±B¬wù¨]X€@»Ì |cËIxe. OŽpùÐï‘ 4¦$$¯XÝÞü>**«ñw2Dí‡{‚[^ÝŠWôë·ŠHMFw¯[„©½.¯ !ÂíöÃ,sÕ0°_êë?U7ýаðYDáXŸ“içÈ I€"J€‚0¢¸Y @|PêI:ƒØµÿì[L0w¶£ÝoÃå5è=z{ˆ—qê ¤»õ8ÒÔ½Ì×kî/Á¼Ò\¤7‰ƒGÊR%Ö L´Ù¬’Ÿòøõ#1Ñ€æŽv<ùâ[ØîÀ¡“\¶)žþ.4¶tÁlÄŽ†,±ð¥!=Í6„YÄéŠy¥Ø¾ç8~þÓ?àq{*ÊÊqÍ%¥H2ñÊö:< ÷"ÝBÝáNºLØ–aÆ¡ænè2bY[/*“ÓÇ–‘Ù2Ÿ1¨=â»7Ù:˜K(çRo³­$@3L@Ä›¥°Å†Ò¼lä%Éq¹8zf!ÍFG†  çÃ"žÅ‘Y½=[BϘlÖÃo°áâUHËÏG¢¼‡X톒ˆL™?X³t)î.«Bg¯x ¸¥09NºzSðÙ¿+M@j~Éâµ<â’“_„üÛqCc;Â&Tf S¼Ÿ+³1ÉĤ;­X½¬Z"áˆg³AÒœu¸Z,ŒÉvNVãÆÊ±E%¥wåuvN..Y*ÃÕ9ŽáêC& X&@A˽Ǻ“ D0úúÑÒÜ…¶>R³3±¼*Ý@ËÎË&®DYiâJÞèߵ¿{|,2ÔûÞeÉ"Õ‡£’LLL’X„I))(Q1Åò'ˆhÞÇéi©HWïUÒ„àpÞC[4Ag—yƒ‹%m‘ál%òÒ2Ò&sµ¢Ôq#ªï””<•(}ÛDBªjæ£|¾Z¶NÅ6SבrùL$S(cª»XY è&†GtÚüEÅHêõ#èÀ +î^"ÆÆ&Ñ\I2”»aÕbdˆÕ/?Y&Ž7AO‰6uøØ,ÆÛ÷meˆh d=ò™{§¬#'ö¬+¢DLG,NìxîE$}(£¯OX# X% º+S†S¯,,Tµ˜}#–¼³6ItYVa.²JóeQ{ÚR$gÝ;:>ÐŒ‚ÚŸè¨kA$pÁ(/!3 ˜N~¿x»Š§ktŒDêàv4àÓmËØ©õæNo=õJæŠ+ð©·Ú‹áÝ37¿SŸ…Fs®}ÇÍ Ö6žn ZÅ#|]ƒ>aV¢—a,µÌàézÍJEX( Ì2 ÂYîO$pš@_¯O>± ÿ÷»íZ”Ù¿Dëàr{Q¸5E9âÛ¡ƒO ‡°æºqºâ|5qÂÏ/{ûDô¿ñl=ZŽõ"Ñf’©Š‘·8ªÕVªæçàÃ]‡²2ñ¦¦(œx?rϸ#@Aw]Ê‘@¬eÝ:]xTœ+žþóÔdeÁb0Š¡0òBa4A¯8axú½8t¢?úà °YfG¼Œ®S,¿ÖÉüÃ'»Q¬M–Õó¢·¡¾Yègås¤»Ûv5aãeU(*J‡I¦o2‘À\%@A8W{ží&($ ¤ŸQBŸ¬*(À®»Ùvû¬:.(‹Q«Ë…ÿúë_ñ›Ý»qÿ¯ž-ž}ÓeöÞÄ«¤¬V£w,^Œ¯\‰¬ÄDqŽð±xn÷Õ×ñÇÆº‰Wœ{’@  ŒãÎeÓH  (­e‹µ]Ì5v‹eÊž°ÓÕö§ÿtñŸ¹¦FbýÍ®µrºÚ ù˜dî^žÃ¡‰ÁY©œc£ŠIu?+üYhÔ  Œº.a…H€5¿K‹7Ë"LÍLKeºC¦i& ¼ªg±gcÞâ4dv$0m(§ %3"ˆ[³/n™°a$@qE€®rqÕl Lžáä™ñ   ˆ+„qÕl \ºÍ\?M±J€scµçXo ˜"Òée-Ž0t²ö°a†[Ã’@¼gõR–^^Ÿ¯¸°„×Q«† x]è÷…`·Z‘b62PsLa¬, \  ãÇ£I€Hà¼/œlE]¿¥YXœ–r{&RX–Á;ÖÕmm=HOIÇŠìtØÎµÔžT¢½· Ï7Ôá×;öà^=þvÍz|~y%œçS’3ÑæI$0+8d<+ØY( ÀÜ! “¥Š}x£a¾ñÒKx²±¥gDlIYá´Æ÷¶¾ÇŽ4Ã3fYå3¹«J±óÐ~üøµíØÑÕ1dˆpè3+Åw$@³@€ÂY€Î"I€æ0ÌVÖ”–a뀬Ä2²^î¹ „Œ£w ‰UpPDœ è`¶bUq)®ép«ßçž*ª‹+q_v)Üžvüà­ƒ²jȹ*7—úŽm%¹C€‚pîô5[J$0kt0ÈÊ+:£I,vt»Ý0É侓Y–pÓC/Á™]~ÜþŒ²®¯AD£Åd„iD<×[­ëì ú1èñCo4Ãi6A/ùëlÁ Gº—Sˆ%iCƒÑrlH>óˆu²Ë”U9̰©²ÆaP–ù`c/ÔüC& ¹G€‚pîõ9[L$0 ÔºÈý®^ìmØ‹¾Î´ ºQš]‚÷̯B¦Áƒÿݶ¯µöA'V½”ä4¼[Öù½(=IÖN¾UbðèÉf<]]Åx‘X—%éðû­[ñà±.Ì/hG·§K’ET†ýØÛxÿÖy ݽ¨.¬Àí k0?ÉööÕA´u„E Îâª!³Ð%,’H` ÂQ0ø’H€fŠ€Z&-ÑdÁ²ô"Ü1¯»ìÆï؆ÞA?Ö¤‡ñPÝ^lu±² ÕN,z2¬™ûDLöô6ãW"üê<\3¿{ïÆC»z`‘§,Fk" ÓÒQœìr|ðyÑÐëÅ‚âX’€?77á…æ,”'Âj²³îkÇ+KK±*/½]ÍÐ[ìxGu9î¨]ô5âûºÜB–9ãÇRÖ˜H` PN#LfE$@ç#”!àP0(s-Èt$Àá6ˆÃ±šch€Íj¹~jè6$–ÃAÔ·wâ¤7€\GI,Ö\W¹ª)W‰5(ùènì§å–ù‰âf¬ ýêÄÂh–ágƒ6¬‡Ý`ÐæÒ]ä|=ÄÏI`n  œ›ýÎV“ D˜€òÕðè øá‘ÖÙ׺t IH±Èð°Xî|"ð| ‚a3––cIQÅP-Å9¤¡ù ^kØŠ'÷ì•!`+ÊŒ>ìév!Õi×4`Pòô˱ÊiÅ'¢0$ïƒò¬¨¼ÑÄ£Q†­ÕÐõ¹Óù>?÷Ñü”H 6 PÆf¿±Ö$@1E@V'ïᄯªÃ}îVt÷taÀ˜†÷Ï«‚»»{»Z±¯øíîÝʶÚôdXÔ\?¥ÏDÓ•¦çãæymøÞ¶=ø»#ÓnGMQ%nKÏ@¶-&÷a<ðæ«â)œ'<ØÓÑ‚ƒùÅ éêÀ®ÆãHH `w~:.ÎJ§“ñ„Ÿ¬l"ÂT§„cLñeeI€.”á…äñ$@$p>¢®2œYøÇõ—aPÂË´ ô#\Rù™éÈ’Ð1}n+>wi¾–KH†V‹¬d"iD•‰@Ó­¸fñ ,(,Å’“P–‚$™è)¯„#) mþ0 S’‘lÒãÆ…‹åx¬2­CjK«$C=œ6«æF+lô)+Áœ€šŒt¤&%j¡oFÌ×$@ñM€‚0¾û—­#ˆvñ¶KÄ)z¾8•hf¿áù~N±ð9Õg*îàˆåuÝ¥4 ƒIDATnäY¢’z¯3¢(=E™ò~øXõlµ$`q¡]ÛMÍ? &¨öWÇ mÖ^«?2|ý÷—áúë— ¨–P‹ƒk´NgÀ¾}'ñ³_¼†ž@!–_q)Pb$š’©žÁ>¼õÜC0ºöàý·­À²eEbÑß0šj|áuQÖÚÔÔD”ˆ(4HøE&˜Ë(çrï³í$eôz2³’ä‘e5»êȪ²]æ{`2T`ñ%W!5'¡À…ä9ýÇŠnÅ`WZïG°å-ÈÇEUJAc‚hOѳœ£jŸzD—@Ÿe(,~  œƒÎ&“@t¹@Gw-'^;ƒXƒÊsAøá÷àóˆ D—щ÷y2×1 «ß…dECøiCêÀqYs# Àø(Çç­$@$Ê:èí?–¶cðea#’óæ!¯f5¬î Y(¾äFTÎËEÃso!µôRT^ñ>ÙšñêÓ¯"ìsiF@5¥‰H€¦J€‚pªäx Lqú0&:P¶î"lºñFØd>¡Ï«Ç‘WÀëóÁ/µ¼šZoØd±Éº»må™h½Q¤ RƒãZÇÝ8]µf>$@qD€‚0Ž:“M!ˆA¢ÙŒöLXLÀögþ_w’!˜Ò+ óÂÓ~'¶½ŒË´5· óX3ÒZN ÑÖ…y:ºd).v£šÊ—$0íÔ·ZýåDA˽7MuW«ôöâØ±v¹¦)WfC$ ètz´¶ôÀåö«ëGwK#BAYXN–¡‹¦$Õ„«¿žÁn„Ü>œ<Ù#¿ ò› ÜW˜H€ÎF@'뉷¶öjKKžmŸXØNA ½4ƒu4 äÖˆ?<°/n9*6‚èºHÍ`Ó™5 D„€2¸=´µõÁjBÓþmLZ~z£í«& ‹Píëî€.ä—¾Ü {¢9ꪑNc!$0 Ê:80àAOFùn«@±˜(c±×¦©ÎzYå ¨¨ŸúÔ§°woœÄ1zOfC3C@'!_Bb…ïFww7‚(¶ÂËO€¾8ÉÉÉòH•›Eƒ ‰6å:3½Ä\Ià„‘––†Õ«WÃl6_XV³t´N† ùmŸ%ø,–H€H€H€¢½Œ£¡X   ˜E„³ŸE“ @4  Œ†^`H€H€H€H` PÎ"|M$@$@$@Ñ@€‚0zu    Y$@A8‹ðY4 D ÂhèÖH€H€H€f‘á,ÂgÑ$@$@$@$ þ?˜ªÜÎ\½dIEND®B`‚neutron-12.1.1/doc/source/contributor/internals/images/live-mig.txt0000664000175000017500000001163213553660046025460 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Generated using plantuml. @startuml title live-migration with ovs-normal plug : nova<->neutron interactions participant nova_conductor participant nova_compute1 participant nova_compute2 participant neutron_server participant neutron_l2_agent2 participant neutron_l2_agent1 participant neutron_l3_agent2 nova_conductor -> nova_compute1 : live_migrate activate nova_compute1 nova_compute1 -> nova_compute2 : RPC.call : pre_live_migrate() activate nova_compute2 nova_compute2 -> neutron_server : REST : list_port() activate neutron_server neutron_server -> nova_compute2 deactivate neutron_server nova_compute2 -> nova_compute2 : plug_vifs() nova_compute2 -> nova_compute1 deactivate nova_compute2 group proactive dvr router creation nova_compute1 -> neutron_server : REST : update_port('binding:profile'={'migrating_to':'host2'}) activate neutron_server neutron_server -> neutron_l3_agent1: RPC.cast(fanout) : port_update(port) activate neutron_l3_agent1 destroy neutron_l3_agent1 note over neutron_l3_agent1 "migrating_to" does not match host end note neutron_server -> neutron_l3_agent2: RPC.cast(fanout) : port_update(port) activate neutron_l3_agent2 note over neutron_l3_agent2 proactively create DVR router end note deactivate neutron_l3_agent2 neutron_server -> nova_compute1 deactivate neutron_server end note over nova_compute1, nova_compute2 libvirt handles the live-migration end note group port plugged on host2 note over nova_compute2 libvirt creates tap device end note neutron_l2_agent2 -> neutron_server : RPC.call : get_devices_details_list_and_failed_devices(devices : [port]) activate neutron_l2_agent2 activate neutron_server neutron_server -> neutron_l2_agent2 deactivate neutron_server neutron_l2_agent2 -> neutron_server : RPC.call : update_device_list(devices_up : [port]) activate neutron_server neutron_server -> neutron_l2_agent2 deactivate neutron_server note over neutron_server port status is never changed since port is not bound to host2 end note deactivate neutron_l2_agent2 end group port unplugged on host1 note over nova_compute1 libvirt destroys the VM and corresponding tap device end note neutron_l2_agent1 -> neutron_server : RPC.call : update_device_list(devices_down : [port]) activate neutron_l2_agent1 activate neutron_server neutron_server -> neutron_server : update_port_status(DOWN) neutron_server -> neutron_l2_agent1 deactivate neutron_l2_agent1 note over neutron_server port status changed to DOWN since port is bound to host1 end note deactivate neutron_server end note left of nova_compute1 live migration succeeded end note nova_compute1 -> nova_compute1 : post_live_migration nova_compute1 -> nova_compute1 : unplug_vifs() nova_compute1 -> nova_compute2 : RPC.cast : post_live_migration_at_destination() deactivate nova_compute1 activate nova_compute2 nova_compute2 -> neutron_server : REST : update_port({'binding:host_id':'host2', 'binding:profile':{}}) activate neutron_server neutron_server -> neutron_server : update_port_status(DOWN) neutron_server -> neutron_l2_agent1 : RPC.cast(fanout) : port_update(port) activate neutron_l2_agent1 destroy neutron_l2_agent1 note over neutron_l2_agent1 port not hosted on host1 end note neutron_server -> neutron_l2_agent2 : RPC.cast(fanout) : port_update(port) activate neutron_l2_agent2 neutron_server -> nova_compute2 deactivate nova_compute2 deactivate neutron_server group port_update processed by agent that really hosts the port neutron_l2_agent2 -> neutron_server : RPC.call : get_devices_details_list_and_failed_devices(devices : [port]) activate neutron_server neutron_server -> neutron_server : update_port_status(BUILD) neutron_server -> neutron_l2_agent2 deactivate neutron_server neutron_l2_agent2 -> neutron_server : RPC.call : update_device_list(devices_up : [port]) activate neutron_server neutron_server -> neutron_server : update_port_status(ACTIVE) neutron_server -> neutron_l2_agent2 deactivate neutron_server note over neutron_server port status changed to ACTIVE since port is now bound to host2 end note deactivate neutron_l2_agent2 end group @enduml neutron-12.1.1/doc/source/contributor/internals/images/live-mig-ovs-hybrid.png0000664000175000017500000033420013553660046027510 0ustar zuulzuul00000000000000‰PNG  IHDRñrGf€‘)tEXtcopyleftGenerated by http://plantuml.com09zTXtplantumlxœÕXQo›H~GòõŶTŸ q›ÆºVM“TM•\£$Í=œ*„am¯„w»FQþûÍ,Ø 6i,UåÅÀìÌÎÌ~ó͘J{±NaÇêXšëAÈS6XðYìi.Üs=™ªÁüaó¢0™Á„L½¿KtŒ«¸Ð,ö|ÒPd*B«Üç‘'´YêúR‰¯e\+[D‰fv³È©ˆ²]]Åâ”U-æ²Ðq½ºAu)®nºl×>Xiw¬r|0øPŽ “E)u³”²ŽEiJñ®|Y«bÆA3×W'ù^âm3·hµ×o°ë”í:Æn)}døìæÖ¸©´ÉX—­U’]Ñ®úÙ±Ö¨Û±f±L" M ’f,„Ï\*J5àµém% ¤æ¦|ªÈKÒØØ­pôÆbõu}ÖÉ1í,å>Sø«=*×äÆ;ÅG,å½üµþ£ ~4ù´Ì@Ñ¡Bë=ß’É_ ,‰´•ûnBZúïâ¡ü–$ $™¨“Òe ‚,¥\á²’ÔŸ{‚0¤¸ð™Y»–Ñj©a"€–E1|C[7úXˆ×ª¼—võåzX>–f ƒ _!e‚3ó›à··•j~’¦X»..fcÜ`Šàì¾ìæü-f®–ÝqׄÞ}záÉ.Io‰)„ÏÔGŸÊ]q3·ztßXœkêÌr¯ðõC“x•t½*Fû ÉÔZHXxÚŸ›ó¯;þÝ;{‰ØÙ’S@ý+áC§w×9hÚbø –ëyÛÞY®«:X¹_T]E=Y ù$åX¾X¤Aˆ$©çÕñ˜ÍÃ0–C6Õ §u¾‘&4éJ|Ÿ,(ÚÙÑImstÈæ…þiìZS"*ÍÉnªÏŠáL±]{²ëY¼¡î[±x ïÅžx|µsjÜŒ‡{§ßþý§ß¶4#«šˆgõ‚%ók¹–“_…V@À°ÿzEÞì–%´ýVšÒõs=š+•y$€4t {Ó\±E<®zEïò`Õ^ÃFû?>==sl™x¶l» P}e-ª!à2 v¨oPŠXŽž5”˜f÷ôòpœgÝ¿4ŽçÎPçA&QXY“0Û#m{° …ØŒ(‘¯‡­?hÀÞ†ÐOßÏ/N[ÓÚ<¢oKÂñÉíùÝÙ¾³ðbfÏÜjóï÷1惩*‹ød>ŠXW!þïÿ~y订ï!ÃÞí<ãdΰÇoŽÆC8!Úu†öÛ¾Õ;¾:>ù‚îÊ$F<ŘO"ý¾õÕK½ÞíenÎà:š/œ œ“¤X +Öù§Køzw—VôÓ{;:ªøh†²=9Ž;´¡ws}Ÿ¯FþuÁEòó¾¾ Gy` Z€IDATxÚìÝt÷}(z5²¢(„PZEO—rU…$uáâ–b‡—º%ÔáQ¢ðž.Ç%º”“{||y¾)¡´nêúæb‡^•º)¥¾¾ÆÄ!ƒ¢u•ÆÛÁŽš¸‰ q¸.¦*‘ÚU)«„êýÊÔóÖ«ÝÑêß2»ûùœ9œÙÙÙ™ßü¾ûûío¾fª€RP¥  $Èé@iÓ€Ò §¥ANJƒœ>”9}( rúPäô 4Èé@iÓ€Ò §”é(çŠÁóW½0å´¯àªo ‹4‚ŽS夰V©G±®ÂÀIå9Ê©Œœþà-§ê`SX¼1ŒŽœ>éŒNá{¼Šßœ’èR:ÂT@yŽr*#=”òÔÓ/Ñ’P!4U9} J© <¸0ÁŽ;¢\{íµ[·n\ÔÝ»wßpà ¡ñòM›6…ùêêêI“&Ýzë­/¿ür¼r,ç¾üñyóæÕ]fÂˬòwvv†}…„nºé¦žžžœŠV>tèPô²±±1f4ÿÜsÏ…·Þõ®we ¡xî4¡üÉ¡§ë7†ÖšPÎðnX'¬9šcIèê<¡ næcÕZ lq9ËVÈ‘ØAEB7`ÔgΜmm\û®a5€’<ÛU@yŽrr¥Šî¿ÿþ0³dÉ’xµ0–„åaþÀÕÕÕ7ÝtSooï¥K—>ñ‰O„·¶nÝš¼‹+V„•£Toüò/ÿò/Ã|œgÌ,LWWW˜{9wîÜË/¿¼`Á‚ÁE½ñÆOœ8‘¹¯ööö(cµ{÷î°B(Ûà-^òØc…ùE‹…õõõ…™ð2,Ì\ó†nûº|ùr”•‹²Õƒ}ò“Ÿ ïnÞ¼9Ì:t(úìsÏ=7ðZ:ïöÛoO¨ù¬â¸Ó„ò'‡2l0ÌGIÀ .ttt„*MŽcÎ"mÛ¶-Ì/]º4^yõêÕaÉÆãÆÅ»ùæ›ÃË}ûö%„2³B}ôÑøãAttÉéõä}ò•Hþú%4¢áVÝh—ð…?>¬ª7´Ù°Â´iÓÊÞÃ>>;ÜcIî’ë<ß»Yß1l­ƒ÷žïƒƒËVÈ‘ÞA}ûÛß®««{×»ÞuðàÁðòøñã+W®ï¾kXÍ $ÏvUPž£œ\9µ .ÔÔÔTWWŸ9s&¼ ÿ†ùÚÚÚ(Çwã7†Õâ‹=ûûû3/?Ï·‹(·uùòåÌ—Aõƒ 3þü0ÿío;zfõèѣɇ6qâÄÁ[η¯ø¿ „™ðrÞ¼y™kÆïF‡—9KôWŠ–––0¿qãÆè³Qj;, óa…„šÏ*^;M(r(£ÊÿWA!_•œE ó aGq¸©©)¼ÕÛÛ;øƒ9«7+” щþ»Crz=y_ñ|(pòNó}ýÑp«n4áKøÂ_ºt©££céÒ¥áãayÇš5kžyæ™!£Ö k677Gõ³dÉ’°°µ%¹sH®ó|­ ë»1†­uðÞó}ppÙ†<ÒauPÑž|òÉB¾]cÕw «ù”äÙ®*Ês”“'§¶råÊ0ï½÷†ùðo˜¯²„Y²’_ ©º„—™óµµµ™«Å ȹ‘HOOÏâÅ‹'Nœ]’\øEÙÑe½[WWWÈg³„r†­M˜0!Ì/\¸0ÌLž˜P½Éß„äª(|_ÑQ¸Ó¬¯ßhZÙp¿~ áKøÂ‡/[˜Ÿ:uêš5k¾ùÍo&”*ßqEÉý°…ðnô}.üX:‡„:/ð;?†­µð½~·n°ð*ú¾ÅÃX}y’?›ÐüÊälWå9ÊÉ“·zúé§ã»âÌš5+̇%Ñ[ é§«šÓRT9oôQ´œþÀkwGùæ7¿6ÛzE˜ /ÃÂÅ‹yà#ØirùByòäɶ¶¶†††8dY÷A*¼'Nœ¨®®ŽîÝq×]wE7µOø œ~_¿„ð%|á'Nœåô?ùÉOŽ,§>>;ÜœþCÙäô é ï ®JN?¡ù”ÉÙ®*Ês”“?o=îuûöíáßéÓ§ÇË£ÛDtuu`çô³î)qðàÁ!sú™é­£Gž°‹ö߃"á†- {Ýwß}Ñ­´£ÚÛ¹sgtÑzø7¼UHÍw§ÉåOeìÒ¥Kûöí|k”a)ºyýÉ“'gÏž][[Ûßߟ¹ZáÕ;0ê{ﲯèZé8‹úüóÏþõ²f†Uu#_Â>´³³sÉ’%#»÷NX?ú2,]º4lgð½wòKrç\ç~çǰµŽ&§Ÿ|¤Ãí FyïQÖF!Í $ÏvUPž£œüy«öööð²¾¾>¾#|ä›ßüf]]ÝôéÓ9^¾üòËÛ·oŸ?~!»(8§=õæ›o>þ|¾gäfíhöìÙÑQûûû£‹åãu¢£ˆnï>x Ñ“{.\˜ù`Õœ7¾OØ{ìøñãñ8¢'¬Æ·ÚoåÜHrñ†ÜirùB>²gÏž(ù]>wîÜã8xITŒè鸷ÜrKÖjYÅËùÜÚœ[ŽŸ‘ÛwEôñä,m!ûš3gN˜ÿÜç>7på?D¹Ñ¿~CÖ̰ªnÄáKøÂÇBù·lÙ—¿¹¹9¡œÑmôƒ°~øT(Ìp%¹sH®óBZÁضÖÂsúƒË–|¤Ãí ¢gäN›6-ÊΟ@±íÙ³gΜ9µµµuuu7ÝtSx©NRkÙ²e!R“'O^³fÍU,Fí¾~€œ>”9}( rúPäô 4Èé@iÓ€Ò;§ÿϯüãKO|ÇtU¦þ‡óC†M€R M[Ót¤ºM@7k’d0‰¬ÈЬñóHrúa7;«æ˜®ÊtæÉï 6Jy€`d4mMБê6ݬI’Á$²"+²ÆÏ#ÏéÿͽðÒ;ME›þæÿ`XMW€R Í9’¦­i:RÝ& ›5I2˜DVdEÖøy$9ý°Ëù—gME›zß9¬¦+@© ŒæIÓÖ´©nÐÍš$L"+²"kü,§_2þê>ÐÝÝ}ðàÁ^xáÔ©S}}}TrçHš6 #ÕmºY“$ƒIdM"kñ³œ~êÜqׯ½{÷îß¿ÿ™gž 1>{ö¬•\€À9’¦ èHu›€nÖ$É`Y“ÈšÆcü,§Ÿºñ·ïÙ±cGgggˆñÁƒO:%@% pޤi:RÝ& ›5I2˜DÖ$²¦ñ?Ëé§.ÀÛ~ëî‡z(ÄxïÞ½ÝÝÝÇŽ ’ 8GÒ´©nÐÍš$L"kYÓxŒŸåôSà]¿÷!À_øÂ:::öïßÿ /PÉœ#iÚ€ŽT· èfM’ &‘5‰¬i<ÆÏrúš®É,Α4mMt¤&Ý& ›•dY‘5‰¬IN¿¤¦ªª*M×ä çH𶦠:R“n¨´n6 I‘Y‘Y‘•Ó/ê—f¬¾pšîÕ è7¾ñÐܹ3‡üˆ3Xœ#•PÓÞ¿ÿþ ~®¶ö“&½eõêÖüǧ5mБ:åÓJk¼Z´ž6ÞQácÈ«; .°œ©M2ˆlBU’ ÙxG¡ºZ[Nœ8¡¦æš¦¦Æ5k~íïÿþImVdK·ÍÊéËéKü #(¡c -PN9ýrjÚ7ßüÁ¯}í¾ýèÛa´´jÕÒ[nùMt¤rúrú€œ~òŽ C^Ýapå”,¹È˜ ÙxGóç¿ÿÏþìž(EþÒKû׬ùµoüYmVdK·ÍŽcN?1Ôéu×M¯©¹¦®®vÙ²_ 5¿ûÀwN›öSá­ðïÿøŸKþážš<ù­ÿüϯæ'MzËßÿý“/¾øÕ––_  ë/ZôÌí䜾ò•{gÏ~wô÷™¼+a§…5l¡¹yJmíßõ®¦ð©¸êÇ sÉà2T½ÞEúã?^רø“ÕÕo§¦[Zú“?ù­°rxkÆŒw<õÔÖ/~q}Gx¶ó·»/^-l!,Á !ûÂþ[BCŠ–ä J(Þ¬Y?6ÒÐðwÜñë#衜ÁrÏ‘4í7íèØÃ š6èH:Ò|Iá=ÒàaÞÈÆ¨ƒ§¿ù›ÎpõÛ¡#ݵë÷ )v\˜|?~\· žE*Ýž6çÉ`ò2š†Ö¡l9sÉU‘³Ø9‡Á…”s¬òƒ"[üÈ'§_f‘¦äàj³"›ò6;¾9ýpHßúÖÃü?þãÓŸøÄGC•Eo=üð†P;ßû^G˜öÙ‡C¿øÅõa~É’CEÄ[ —.ý…03cÆ;¾ñ‡~ô£o‡oóºummmÿgÂÁÿùŸÿqSScX?ÌŸ:õµ•+—$ï4¹¨_úÒÄŸ ÿ†O’ÓÏW†¬$)û?üÃSã×tK+@ ^Z]ØÅý¯·†öZ]ürÁ‚Ÿ‹WË V˜ÿò—7¬¬·¾úÕÍsçÎ ó?üácáï¼órú”V*JÓYÓÓ~ÐFš6èHóu¤ Iá=RÖ0odcÔœÓôéo§"Q^>ljùòE…;.L¾Ÿƒ?®Û†•E*Ñž6çÉ`ò2šò ­wíúý¸lßÿþ—Â|¼‹äªÈWì„ÓÕ„rŽa~Pd‹Ù¢åôË)²ÿ÷OÞqǯ'P›Ù”·ÙñÍé;öhüòÕW¿ÿ$TÄO,ÉÚl¨ô‰'$üüùïÿÚ×î¼<ßN‡UÔ¯ýÁBrúùÊõ‘ä"ÅרŽ_N¿„:²xûáå©S_‹_ÖÕÕÆ«ekÞ¼÷ ·» Åû_ÿëËñËÐï î@åôIy*JÓYÓÓŠ¹ûîÿ¬iƒŽ4_GšÐ™ ëÜ&s˜7²1j¾‹’Ξ}"gPì¸0ù~ ü¸nV©D{Úœ'ƒÉcÈÁSæÐ:”-Œosæ’«"_±NWÊ9†ùA‘-rd‹–Ó/ÈÆ×MOŸþö|%h³"[mv|súù–ÔÔ\j-³£ÜM˜ihø‰(§þ óÑj¡v/ž։“óÿöÆSÖÆó-ÏJ^ÔBrúùÊõ‘aió9/¾øÕ[o]6äNŠ=#7þTæ3r—-û¥U«–þã?>} 2 –¯ S¦¼í¯þj[¼—a©˜9ý”(!ñë{ßë¸öÚæøAš ÁÊ Êþý÷76þdˆû~ôí0=õÔÖèéprú”A*JÓÎÙ´ÃOu0EÑ´AGšÜ‘&t&…÷HYÛÙ5ç´xñü¯ýÁ¨láÄ#tìC{ðÿ ëãºM`”Y¤”÷´ñ»…!“‡ÖQÎ!z*c4Ö-¤*ŠU–³ùA‘ÛȦ'§_*‘]¾|QØct!ö÷¿ÿ¥… ¯ÿô§Wi³"[ºmöêäôÃôÀwFùñðo˜Ï\íî»ÿó¤Ioùïÿý¿ÄKB„óŠêê7455þÉŸüÖÕ/yã¬Y?­ÿàƒw ¹Óä¢~þóÿ5¬¶þýÓ?½#~륗ö·¶.¬­}cxköìw?üð†ÌOå,î]¿"ݰi¸E*fN?åJHü…f+óoh Á”Ð8,ø¹è>Ya&óÒãª×sKi¥¢4íœM{òä·f5íÌ+4mБf-É×™Þ# ÞþÈÆ¨ƒ§P˜oüÙP€ººÚ––_Ì|"Y¾bÞààŸƒa}\· Œ2‹”òž6~·ð1äCëPž°0Î9ÔÔ\3‚î7_UXÎ"äEvl#[`‚BdãwC©âBΙóÞ?þãuɧ·Ú¬È¦¼ÍŽcNß4~ÓX5Ý2› ÿßèé ŒÇ9’¦­i:RÝ& ›-ÅéûßÿÒµ×6K2ˆ¬ÈŠ¬ÈŠ¬œ¾œ¾ÄŸ3Xœ#iÚš6`«Ût³iœV®\rìØ£Q iîܙѳÊ%DVdEVdE¶¬¦+@© ŒæIÓÖ´©nÐÍš$L"+²"kü<’œ¾éªL…7]Sj£9G2iÚ€ŽT· èfM’ &‘Y‘5~^NÿŸÿáü™'¿M_ýÃ:îÚøÅß¾gÛoÝöm÷éóÛ† °¥<@02𶦠èHu›€nÖ$É`Y‘Yãç‘äô3uwwïÝ»wÇŽQD¡ÂCµ‡Ê?v옕t€`d4mMБê6Ý,’ ˆ¬ÈЬñóHrúÜ¿gggØå(ŠPÕ¡ÂCµ‡Ê?uꔕt€`d4mMБê6Ý,’ ˆ¬ÈЬñóHrú/¼ðÂ3Ï<v¶wïÞŠ"Tu¨ðPí¡òÏž=+@% M[Ót¤ºM@7‹$"+²"kü<’œþ©S§Ân<ØÝݽ¿t|þ?ýöþ’ª:Tx¨öPù}}}TÒ‚‘Ñ´5m ¢:ÒRì?u› ›-Åñjf+<É ²"+²"[Qãç¡súagÏž {:vìØ ¥ãs5ï¡d…ªª=Tþ«¯¾*@% M[Ó*ª#-ÅþS· ºÙR¯–ñh¶Â“ "+²"+²5~®*×áÅΪ9ÆXhÚúOôÆ"«D‘-'rú iè?Ћ,"‹È–9}дôŸèE‘EdKCÙæôŸû½ÿ)ºhÚúOôÆ"«D‘-'UªJ‚œ>”9}( rúP<#4mý'zc‘EdÙÒP¶9ýUsDW€M@ÿ €ÞXdU‚È"²åDN4mý'zc‘EdÙÒ §š6€þ½±È"²ˆliÓG€M@ÿ €ÞXdYD¶4xF.hÚúOôÆ"‹È"²¥¡ª¹ªJà(9Ó/~rߟ1×é;Jƒœ¾C 4Tâ3r3ï½sþüùúúú¾¾¾øÝ°¤¡¡!ZrÇwLž<¹¶¶vùòåayR=VUmÞ¼¹©©©¦¦fÆŒO?ýôÖ­[§M›^Ξ=ûðáÃY»îïïokk [ûjooÏ,Ò¦M›«««ÃË“'O¶´´ÔÕÕ…í,Z´èÌ™3Ñ:±¸…5ýʲï@ÿ  7FdYDvôÊ6§¿³jNÞc~ýýôo¿ýööööøÝ0¿fÍš0s×]w-Y²äܹs—.]ZµjÕêÕ«“걪ªµµµ··÷òåËŸûÜç2_Þpà Y»^·nÝÒ¥KÏŸ?ß×××ÒÒ’Y¤+VÄIù3ftww‡„2„´µµem'2¬¢¦?@@Yö½è?ôƈ,"‹ÈŽžœþÀÑ£G›ššâw§M›vòäÉ03uêÔ'ND /\¸0iÒ¤¤z¼rÉ4ùò嬗555Y»nllŒö2påbüÌ";w.ç.Âv&Nœ˜µÈ°Šªqš6€þ½±È"²ˆl‰’ÓÿW7ß|sggg˜ ÿ®\¹2~7St3œ!·™ürðLòòîîîùóç×ÕÕe•aðÇ /ªÆ hÚúOôÆ"‹È"²%JNÿ_8p`Þ¼ya&üßû~êÔ©Ñýë ªÇaæô3¯Ó?qâD¾œ~(þ}û.]ºæÃ¿ «^TдôŸèE‘EdKT¥?#7^8kÖ¬-[¶,\¸0^²aÆŋG™÷£GÆ÷²OÞfòËÌûé·´´œ¿bÙ²eù’õ“'O~ì±ÇÂÌ™3gn¹å–øÝ &ôööެ¨éP–}/úO½1"‹È"²£WU‰Çœ+¾eË–ÚÚÚd®¹aÃ†æææêêêY³futt²Íä—ñÌÅ‹W®\YSS3yòäöööÁ7ÜtuuM›6- ©©ióæÍñ»÷Þ{o(mæÊ…ul]xñ¥ž¾rñ‡ç´%€ñV¥ ®ºžžžæææÒ-ÿé®îUs¾4áC/=ñÑ?rúWÍÚµkÏŸ?ÿòË//Y²$Ì—ô±üàá¯í¬š¦½ÿ~ñóíÛ_=ûŠøŒ99ýá©Íed›Ú¼ysCCC]]][[ÛÅ‹K½fzûVÇ?°³jNçÛvÔ~àû-—팭J|F.ã$N뇩£ö_ž|SÂeûú^ôŸzcD‘Ed‡«lsúQfÙ”’éÄ#û‡\Gk„òè{U€þ@oŒÈ"²ˆìø©{ÆJïcßzäÍówUÏrô¼i^gý/ïŽ?®ÓOŽˆx¾@ÿ €Þ‘EdÙ!Éé36þ-¡ÿ†¹»ª¯ßõÆìŸÿ‰~õ¯þåÒ Œˆx¾@ÿ €Þ‘EdÙ!Éé3~ðð×¢kó3/ÌVD»wß}÷ƒ>øÔSO©OÐ÷è?Ð#²ˆ¬È’“gä2Z§»ºÿõ¡¸o¼!ëÂüaµÆðîïýÞï}îsŸ{ì±ÇT)è{ôŸèYDVdÉ©J0^|éùöíÿtìôkÊ錒œ>E"§0Jrú‰œ>À(ÉéS$rú£ä¹‰œ>T}/€þ@oŒÈ"²ˆì¸*Ûœ~r™´EDNô½úOôƈ,"‹ÈINŸTDDNô½úOôƈ,"‹ÈINŸTDDNô½úOôƈ,"‹ÈINŸTDDNô½úOôƈ,"‹ÈÉ3rIEkLyN¿ªª*k檗¤¬_¿~Ù²eÑ|˜¹çž{´#}/€þ½1"‹ÈŠl…«R‡œ~©—¤È{ìïﯯ¯?~üxô²§§§¡¡!,Ô”¨drúɈsúU¯©««[°`ÁáÇ/omm=sæLü‘#GŽ,]ºt„ 555sçÎíììm;q~ÑaÛ¶m!¬™KZZZ¶oß®)PÉäô)’Ñäô£™K—.mذaÖ¬YYËûúúV®\ÙÒÒ½ìééijjÚ²eKtM÷Áƒ—/_>Úv"§_ôChmmݽ{wæ’ŽŽŽ¬,?T9}Šdô9ýHMMÍàå.\¨­­æÛÚÚ6oÞ\H‘Ö­[7iÒ¤‰'nÚ´)ZròäÉ–––ººº°—E‹Å×þ™ÓϹ<,ܸqcCCC([(ÕÅ‹ãåaÕÕÕÑ’;î¸còäÉaµåË—Ÿ?>©Åf”$¬Y__ß×׿–„ÝEK†µÍPcMMMá¨g̘ñôÓOoݺuÚ´iáåìÙ³3ÿcD4Óßß'l9쫽½=³H™Ç•³2«2ÄÈYÔPžÓ§Og–³··7,Ô”¨dž‘K‘ŒÉuúëׯŸ={vÖò×çô'M𔙿ÎçÎ;ï\´hѹsçúûû×­[-œ1cFww÷å˗þ¶¶¶¬} 7§¿téÒ¾+ÂL¼—°|ÅŠqòú®»îZ²dI(IØéªU«V¯^Ôb__’Ûo¿½½½=~7̯Y³fÛlmmííí BÐÐÐùò†nÈÚu8p8¡üá¸ZZZ2‹”y\CVfòáǼÉG}/€þ½1"+²*Ad+SÙæô“3Ȥ-"…ÜO?hjj:qâD¼<šÉº÷NÎ\ð`S¦L‰Ÿ¿šÓåË—'Nœ˜µ¯aÝú&¬—6쫱±1^~îܹxµ©S§Æ«]¸paÒ¤IÉÛÌœ9zôhæ¥ëÓ¦M;yòä¶'âÃQg½ü#ÂD{¸r1~f‘2kÈÊL>|9}}/€þ½1"‹ÈŠ,ƒÉ铊ˆr~ooïÂ… ÷ìÙ/ÔÖÖf>#·Àëôsfç»»»çÏŸ_WWm9¾7Έsú™/sÞ5hàõ´ÈÜiò6ã™›o¾9zpøwåÊ•£ÙfòË|•où•™\Ôæææ¬{ï„—î½£ïР7FdY‘­prú¤""ÞOÿôéÓS¦L‰nLŸ/½ÞÖÖß?AÎëô§Nºoß¾K—. \¹ÕÏéì!ZWþëô³vÿA¢mfÍ8p`Þ¼ya&üßû~dÛL~™ó:ýp€ù*gÈÊL.jkkkGGÇë¾$;wzF®¾@ÿ €Þ‘EdE¶ÂÉ铊ˆþŒÜ[n¹åþûïÈŸ^ïééijj ëDÙäçž{.|dðÖ¢ûéŸ9s&ó~ú“'OŽ –‡OžÓO¸Ÿþù+²î§Ÿ¹Ú† /^eÉ=ßw>yG™™5kÖ–-[.\8Êm&¿Ì¼Ÿ~KKKt\Ë–-ËW9ù*s„ ½½½CuÇŽ¡Ò27vº}ûvMIß  ÿ@oŒÈ"²"[É<#—T´ÆÂsú?þøÜ¹s/™?räHKKK]]]uuõu×]ÝšfðÖÖ®];a„‰'Æ×õwuuM›6-|ª©©ióæÍ£Ïé···Ož<¹¶¶vÕªUýýýùVÞ°aCsssØï¬Y³².NÏ·£ÌlÙ²%ìâÀ£ÜfòËxæâÅ‹+W®¬©© ‡0ß=…òUæ½÷ÞJ›¹r΢^ºt©¡¡¡§§'zfÂ˸o^|©ç¯\üá9}/€±+€Þ‘EdÙT©RLjsú%ܺªÊ¿}õôô477ÓÆ7lØßl'Ì´··kGÅtº«;4Ì/½åC/=ñµrú‰œ~9Y»víùóç_~ùå%K–„y_ïrulÛ_„¶¦}oÿÕçÛ·¿zöupuÉéS$˜Ó¯­­Íg}yÆj››7onhh¨««kkk‹YL¹:ýè7vÕܰë ?ÿçoÿÕGê>ØÝú;.Û¸Šäô)’ ÌéCyˆÒúÑûõ¿ü•†»làjñŒ\ŠdÈœ¾Éd*•i×~>š9ñÈþ!WÖû»èYD‘Ce›Ó—H*­ˆ¸NR«÷±o=2áCoü·KõwU_¿ûÇ<û_6F×éÙ´U €±+€Þ‘EdÙ1$§O*""§é%ôw]sý¿fó¯¹þk?ûñS{üË¥Þ´Õ!€±+€Þ‘EdÙ1$§O*""§)ôƒ‡¿]›Ÿyaþp›öÝwßýàƒ>õÔSêÀØ@oŒÈ"²ˆìèÉ铊ˆÈéCÚœîê ³£æ†¬ ó5mcW½1"‹È"²W‘gä’ŠÖ(ñ©ráÅ—žoßþOÇNkÚÆ®zcD‘EdS¥JP i0Jrú‰ÄhÚŒ’œ>E"ñš6£$§O‘Hü¦ À(yF.E"ñš6Æ®zcD‘EdG©lsúÉi&Ò‰?дt§èYD‘’œ>©ˆˆÄhÚºSôƈ,"‹ÈINŸTDDâ4mÝ)zcD‘Ed‡$§O*""ñšöUø ¬Jïàúõë—-[͇™{î¹Ç— œ· 7FdY‘eÀ3rIIk”ÓMû*ü¾–ÓO[r¿¿¿¿¾¾þøñãÑËžžž†††°Ð÷ ʘ±+€Þ‘EdÙU©ŠCN4í×ýü¼¦®®nÁ‚‡ÎZ‹–÷õõ}ò“Ÿljjª©©™8qbKKËÓO?sýÂô®Y„ŒÖ.¶mÛÖÚÚš¹$ïöíÛ}ß9}ŠDN4í×ýü¼–žtéÒ† fÍš•µ<Ë¢E‹Ö¬Ysúôé+—±ïÞ½ûÆo̹Áaü¦5§ßÚÚ0sIGGGV–¨Lrú‰œ>hÚ¯ûùy}»¦¦&çòÌ._¾œô{–˜yÏùîà{ïtuuÍœ9³¶¶¶¹¹yëÖ­¯ÿÉÛ߸qcCCCøl[[ÛÅ‹£åaæÖ[opE˜‰—‡õ7mÚÔØØX]]=xMMMÑ_/b½½½a¡ï §O‘È郦ýºŸŸŒëôׯ_?{öì¬åY,X°råÊîîî|™ý1Éé×××GE=sæÌêÕ« Ùr¼ÎÒ¥Kû®3ëÖ­‹–¯]»vÉ’%ñòð2^ÅŠçϟϹ‹ø/™jkk}ßÏÈ¥HäôAÓ~ÝÏO†¦¦¦'N ^žyéúË/¿üéOzæÌ™uuuS§N]³fMX’µÁaÿÊ釒lÚ´)ëùsúñ!?~¼±±1š3ñ£nÃLCCC¼þ¹sçòíBN*±+€Þ‘EdÙ•mN?9ÍDÚ""§•Ö´ã,vooïÂ… ÷ìÙ“µ<ÁñãÇo»í¶ äÜà0~åô:´lÙ²I“&]{íµ]]]…o¹À[ å[žõ²¹¹9ëï á¥{ï@%w§èYDVd‰É铊ˆÈéC¥5íÌ,öéÓ§§L™Ýk¾ðÔ|ÖÅìc’Ó8p ¾Ö~”×ég.ϼN?¡ð­­­¯«É;=#œ· 7FdY‘e@NŸ”DDN*­ige±o¹å–ûï¿ ñ~úQÞÿܹsk×®?~“w—µ0žikk‹n•sàÀÉ“'G 'L˜ÐÛÛ;įé•û響"ó~úa&ºÏ~X¾dÉ’Ìûég~õÔSê@&‘EdE–Lrú¤"" i”‡Ó]Ý¡÷먹>ëÂ|ý'€L"‹È"²cBNŸTDĉ+hÚeà‹/=ß¾ýŸŽÖ”ßà‘EdÙ”ðŒ\RѸ‚¦  ÿÔ\]2 "‹È"²%¡JàÄдôŸ@IÓlj+ iè?€Ò §Wàª5íWϾrü‹þU“€þÓÐ(„œ>N\«Ð´Ï>õݽS?V8ÝÕ­ý§¡P ÏÈʼn+P¼¦ýêÙWŽÜûp×{ÿCx+LG7?¢ý§¡€L"‹È"²…+Ûœ~òiN\"7í³O}÷[mŸÙ=iÁ×~öã{¦Ü¼ó ?ßóàè? dYD‘9}œ¸ãØ´?³ö·Xzë#Í‹÷þûÅÿïõ¿þ¥I þõ ý7Ì•ÐôŸÉïÞ}÷Ý>øàSO=¥®dYDVdÉ$§OZN\åô¡,›v4uÔßÏ›L&“©)Œ‹:::ž}öY¿52 ˆ,"+²d’Ó'-‰?9}(˦½þ¿üæCïÿ¿v½ñq–ê+ÿLJ¿ôÖ_8µÏ•§ÿêî»ï£ ;ßóK¿÷š°$ жmÛÆEGŽQE2 ˆ,"+²dòŒ\RÑåô¡ì›vÏ{½vÙÎû×´þî_ ­yðÁC?ùûKýs¯ K:::BÏùì³ÏžE"ñÁm·ÝöÓåå=U?”_x÷mo{[}}}ccãO“³fÍúäNɲÈ郜>¼ÎOÿôOW¤Ï7Þè” @Näô!wNWôÅknn¾^ïÇüÇåôäô¯>ÏRÓ‡Ôæô×­[÷2W”Ö=­fÆ®è³|èC*ßY‘EdËLÙæô“3Ȥ-"rúT9}äôcW½qÚD9ý÷½ï}ÝÝÝ|á…N:Õ××'²ˆ,"›Nrú¤""rúTˆ’ÎéG7o©œò¤íxåôÁy zãqåôg̘±wïÞýû÷?óÌ3/¼ðÂÙ³gE‘EdÓINŸTDDNŸ qusú£LRËéáÖ™ÒÇ?þñæææššš7½éMï{ßû>ó™Ïœ;wNNŒ”Ð_”ÓŸ>}úŽ;:;;Ã`õàÁƒ§NYD‘M'9}R9}*„œ¾œ~dîܹ÷Þ{ïsÏ=æðƒ|üãïþÚ¯ýÚøέ·Þ*§Æ®èsŠrúï~÷»zè¡;vìÝ»·»»ûرc"‹È"²é乤¢5ÊéS!FÓrÁ›7o~ç;ßY[[;eÊ”»ï¾;s…ßýÝß:ujMMMx+Ìg}°½½½¡¡¡ºººêõF°¯Ìfm$ëåg?ûÙP¤°…°M›6 ëݰÇw¼ãáÝÆÆÆÐ-d/¼•ðÁ,ÉÕޱ¹¹9l*œ·tuu%×FÎ5ómÿÑG3gΛÞô¦ð‘ùóçïÙ³'ÞTBåŸ;w.,§„þw¿ûÝh×rú`ì €Þx°øÞ;=ôÐ¾ð…ŽŽŽýû÷¿ð "‹È"²éT¥ (9}ENÉ’%/¾øâ©S§>úц—qªýw÷wÃËßøßóŸúÔ§Â|œ_Ž>¸jÕª3gÎxáyò¾ Ìé···‡ù|ä#a#?øÁ/^\ø»ŸùÌg¢ËÕÏ;ú„0¿~ýúè­ßÿýßË„™„òZV®\ª¥³³3Ì777'ÔFÎ5¶ßØØ^îÛ·/̇-`!•ÿè£&”d”¾ÿýï755…íO:µœ.¶`¬”}N ÌÈéS$rú00ŠœþóÏ?½9Éé”9}ŠDNF‘ÓÏZRSS“ïzù|ožÓ/d_ 9ýäýòn¦8>¬Ã)¼Z†µ‘„ÿ¦oÓ¦M¡f¢’Ï™3'º÷NÂŽ6oÞüÖ·¾5¬üàƒŽÇ-wâ„þÑ£G5@r’Ó(-rú‰œ> ŒÃuúÑ^¢kÉs^>‚œ~×éÇÙö_|qð•øñFž{î¹ÂßÐñ¥ñ™†u~áÕ2²œ~Âö£›ãÿå_þet³ ŸüÉŸÌ·£P½¿ò+¿¾ÿýïÿ«¿ú«ñHèO›6MB€!Éé”ÏÈ¥Häôa`9ý~ô£/¾øâøÃ}ìcáåg?ûÙèÝßüÍß /?õ©O…ùßøß|ãøÌM½ùÍoKŽ92²} NÍwvvž;wîãÿøà;懄- ¾ñ}ò»a_Ñýô{{{Ö÷ìÙóá8냧N ŸM¾Ÿ~áÕ2²œ~ÂöC¥}ãß3ý×ÝÂ>gå‡3¥·½ímo}ë[7nÜ8NÅúî¡Æ®è‡ä¹ˆ,"[ZÊ6§ŸœA&m‘Ó§BŒ8§¿yóæŸù™Ÿ©®®nllŒ“ì‘O}êSÑeãS¦L‰3Ë9³ÕíííoyË[†ÌbçÛW滺ºÞýîw‡uš››ÃúYÛ Ÿ … ï†íÜwß}Y÷¬O~wÓ¦Mï}ï{Ã’ÚÚÚøÃñ½k¢†"e~0á¿X-#Ëé'l÷îÝóçϯ½â†n}ZÎʯÊÃCqÁH ½qñ•}Nßï¬6‹È–9}R9}*ĘÜOüŒÇ¾:¶ùÎw¾sï⡸`¤€Þ¸äôYD¶´È铊ˆÈéS!*$§ÿ±},ºAüóÏ?ÿá8ló¾ûî+ð]<Œ”Йœ>"‹È–9}R9}*D”ÓÒúª¾b]QŒÕ¾ZZZþÝ¿ûwaS×\sMsssxYø»ŒØ­·Þ}»$ôÁؽñ°Èé#²ˆliñŒ\RÑåô©qNƉ{胱+zãáòŒ\D‘--Uª€âÓ‡`Ö¬YÑeÔ¿W.>VõSÉï.[¶lÅŠ«W¯ÙöCuíXþãüo}ë[“÷¸`Á‚÷¼ç=Ñ|˜ùÅ_üÅ´EDB€á*ûœ>@™‘Ó§Häô!+ßxã•Ó´Gy>ªkÜ_ÛÅܹsŸ|òÉ„5ûûûëëë?½ìééihh }±(ƒó9}€R!§O‘ÈéÀœþ~¥ªŠ÷;U[[›¼Â¶mÛZ[[3—´´´l߾݀28O‘Ó(rú‰œ> ÈéàWªˆ9ý!÷ÕÚÚº{÷îÌ%áè²²üP¢ç)rú¥Â3r)9}ÓOø5ªªÚ¸qcCCCmmm[[ÛÅ‹ãåY3Y/ûûûÃúáSá³íííùòòçÏŸ¯¯¯ïëëË\>–DÉ|ÌlxÙÕÕ5sæÌ°Ùæææ­[·Fijj:}útæf{{{ÃB_lÀØ@o\ç)ž‘‹È"²¥¢lsúÉi&Ò9}*j¬,§ŸãרªjéÒ¥}W„™uëÖÅ˳f²^†5ÃúçÏŸliiI¸ÖþöÛoooo_†ù5kÖäÛE}}}Ô9sfõêÕÑšššÁ›òŽ=Æ®zã’8O)㜾ßYm‘-3rú¤""rúTÔXYN?ǯQUÕ‰'¢ùãÇ766ÆË³f²^†5Ož<͇™„œþÑ£G3¯©Ÿ6mZôÁœ»knÚ´)ëª|9}ÀØ@o\Æç)rúˆ,"[*äôIEDäô©¨±²œ~Ž_£×çâãìù9ý|Ësºùæ›;;;ÃLøwåÊ• ›:tèвeË&Mštíµ×vuuE ›››³²üá¥{ïÆ®zãò8O‘ÓGdÙR!§O*""§OE•åôsü u~uuõåË—£ùóçÏç¼N?l!9§àÀyóæ…™ðïádzv‘ó³á#qaZ[[Ãá¼îwîôŒ\ÀØ@o\ç)rúˆ,"[*<#—T´F9}*j¬,§Ÿã×èÊýôÏ_‘ó~ú×]w]è"._¾|æÌ™åË—gÞO¿¥¥%úà²eË’súÁ¬Y³¶lÙ²páÂÌ]gÍmmmǸ’ÓŸ}zØÚÌ™3ã{ï\ºt©¡¡¡§§'zfÂ˸¨PÒç)eœÓ(3rú‰œ> Èé'üUÁïQOOOssó¸ï† â›í„™öövßjÊãE"§rúùÕÖÖŽ¸ ›7onhh¨««kkk»xñb´µÁ|ý ᩈˆœ>5V–Ó÷» Ð§í ÈéåÕ³¯ÿâ_„‹_TcW<#×3rYD¶$”mN_""§OE•åô;óä³õ¿Ö?ÝÕíw0R@o\üó”2ÎéûÕfÙ2#§O*""§OE•åôc¯ž}å{¿s_çÛ†5ÃôwŸßçw0R@o|UÎSäôYD¶TÈ铊ˆÈéSQce9ý+æ?~ã­»®¹a×ýü®k®¤nÞ©}Où]Œ”Ð_­ó9}D‘-rú¤""rúTÔX¹’súñ…ù»ªçF׿w¼ñ†G&|h¼ú~cWôÆÉç)rúˆ,"[*<#—T´F9}*j¬\±9ýÓ]ÝQÿjM¾€±+YãUÈÏÈEdÙRS¥ (ÚP)ù]9}*g¬\á×é¹÷áGßÕÒYÿË ŽÎ”vÕÜðâî'|=HÛh¶¢ÎSÊ8§PfäôIËPINŸÊ+»Ÿ~ðÒßùVÛg©ûྷÿê®k®—Ö …£ÙŠ:O‘Ó(rú¤e¨$§O匕åôcñeûÑûû?;}IHÏhvïÞ½ÝÝÝÇŽ«„ó9}€R!§OZ†JrúT9ý|^zâ;{§~$¬º«Û÷€ÒÍ–ÇyŠœ>@©ðŒ\Ò2T’Ó§Èé'{õì+/lê¸ðâK¾*@¥1v(ƒÑl©Ÿ§xF."‹È–вÍéWÎmïÊf¨$§O%ÓÀØÀh6ç)eœÓ÷;k…È–9}Ò2T’Ó§Èé`ì `4›Îó9}D‘-rú¤e¨$§O%ÓÀØÀh6ç)rúˆ,"[*äôIËPINŸJ §€±+€Ñl:ÏSäôYD¶TxF.i*ÉéS äô0v0šMçyŠgä"²ˆl©¨R¤d¨$§O%Ó£Ùtž§”qN ÌÈé“–¡’œ>•@NŒfÓyž"§P*äôIËPINŸJ §F³éÆ®F³éÆ®F³é•@NcW£Ùtž§Èé#²ˆl©Ó'-C%9}*œ>e?v ¿tY3i+XIÔÞUÜ`z**¹$˜:uj¥îA ký4ÔØúõë—-[Vºßüb uÏ=÷ÍæBN€²»–YN¿ÈG1V»MÒÁäÌ;÷É'Ÿ§c)BN «·¿¿¿¾¾þøñã%ýÍaã=== ¡ÒŒfŸ§Èé#²ˆlÉ ¹ËõÀBN€q»Æ©«ººº >|xðòÖÖÖ3gÎÄ9räÈÒ¥K'L˜PSS3wîÜÎÎÎÑ—!kf\NcJùòóÑr\sÓÃúHê3yµµµãW¥uþ¶mÛB».ïoþè·–¹°¥¥eûöíF³ƒÏS<#‘EdK…ÿ…G‘ÈéÀœ>ã=¸-iuéÒ¥ 6Ìš5+ky__ßÊ•+[ZZ¢—===MMM[¶l‰.Y=xðàòåËǪ rúãq\rú#+@yçô[[[wïÞ]ÞßüÑo-sa Æ1šÍ@¹ ûUÅ!§rúŒ÷àþõ™¬šššÁË/\¸_àÜÖÖ¶yóæB¶¼nݺI“&Mœ8qÓ¦MÑ’“'O¶´´ÔÕÕ…½,Z´(¾öÈœ~Îåýýý¡0¡` ííí™ëÜqÇ“'Oo-_¾üüùóÿí 9Xø6ƒúúú¾¾¾x…°$|*,ÉüÔàJÈY¼ ««kæÌ™aassóÖ­[Ǥ9¼ð(^¤B>[`Š·eË–¦¦¦°|öìÙ‡²62³Ãú6æ Yá»NáW+ÔäéÓ§Kå›?dÂ=z¹qãÆP’°©p,/^Œ–‡™[o½uÂa&^Öekll¬®®Î÷åÌ|ÙÛÛ*ÍhvðyŠœ>@É ûUÅ!§rúŒ÷à>ã:ýõë×Ïž=;kùÀësú“&MÊLçåsçw.Z´èܹsýýýëÖ­‹Θ1£»»ûòåËa_aa[[[Ö¾†•Ó[XºtéùóçCyZZZâuîºë®%K–„]‡½¬ZµjõêÕɼ;+ÂLò6o¿ýööööø³a~Íš5™;ÊY ùŠW__ kÏœ9/}!xáQ(¼H~¶$oùòåáèÂ[7nœ3gNrm ùE*¼òUì°v¶¯Vü×»’øæ˜ÓÏ<„x¿k×® û—‡—ñú+V¬ˆÿ´PÈ.ânÐh6ó Èé0Îc×8iÕÛÛ»páÂ={öÄË#µµµ™ÏÈ-ð:ýœ ²îîîùóç×ÕÕE[®®®ÎZy4w$ÏÜH¦Á{ÃmÞ|óÍÑ#‚ÿ+W®,äpòmêСCË–- Õ{íµ×vuuß…)ù³…„`Èâ%Wrá9ýÂë¡ÀŠÖŸ®úW«¹¹93^ßü«4’ó6b ˇÌé‡êrïœç)ž‘‹È"²¥¢lsúÉ?̤-"rúT9}Æuìš•´š2eJt5t¾e[[[æ ²óÉw¡î¾}û.]º4påV?9“§…—<ó:ý'Nd^Yÿ"ß‘&;ßÕÊ9·yàÀyóæ…™ðïádzv”¯rn*s›ñ%ÆcRȬA†,RŸ-$Cop%ì:ýÂë!_ÅŽÕuúWå«ÕÚÚF\¥õÍOþb'\§Ÿ¹<ó:ý!û‡Ì…;wîôŒÜœç)eœÓ—#2‚BdËŒœ>©ˆˆœ>BN€q»fe²n¹å–ûï¿ úµ§§§©©)¬%CŸ{î¹ð‘Á[‹n¨}æÌ™ÌjOž<9¾qvøTá9ý|÷Óoii‰Ú¹lÙ²x 6,^¼8J÷=z4¾Oú„ z{{“k#¾G°dÉ’!·Ìš5kË–- .,°òm*ÌDiЄŠÃBfxáQ(¼H~¶ Y¼¬—ùjcÈ/Ráõ¯b‡µë´}µvìØÊS*ßüÂï§BæýôãGD‡–y?ýÌçürf®z›íÛ·Í>O‘ÓGdÙR!§O*""§O…Ó`\Ç®Y‰­Ç|îܹ‰—T9r¤¥¥¥®®®ººúºë®‹nÁ1xkk×®0aÂĉãëú»ºº¦M›>ÕÔÔ´yóæQæô/^¼¸råÊšššÉ“'···gÞ|Æ ÍÍÍaG³fÍŠ/F¾÷Þ{kkk“¯ýýý«V­Š·™¹rÎm[¶l ›=pà@•oSafúôéa×3gÎL¾Ép ™uà…G¡ð"øÙBB0dñ²^&ÔFòiX߯œ;¬]§í«uéÒ¥†††žžž’øæ˜Ó%å¥ ÇŽ(î(V¯^]{Åm·Ý/ÏúxÎ/gü2TT¨®ø³F³™ç)rúˆ,"[*äôIEDäô©rúŒÉØõÕ³¯ÿâ_„˲6zzzš››}+ p6lˆo&SІõ<ƒQî"TT{{»ÑlÎó9}D‘-™ŸÎr=0ÏR(­Ö(§O…Ó`”c×3O>ÛYÿK¡ƒ=ÝÕ]f•°víÚóçÏ¿üòË™·Ô*Aík¢—c›ÓÏÚ¸ÑlÂyŠgä"²ˆl©¨R‡œ> Èé0R¯ž}å{¿s_çÛ†®5L÷ù}åwŒ›7onhh¨««kkk‹í[ Ú\Òvt),äi\î*V]I|µÊR1ëÙh6ó<¥ŒsúeFNŸ´ •äô©rú ×™'Ÿ}üÆ[w]sîûù]×\ÿHݼSûžR-F³c~ž"§P*äôIËPINŸJ §@â ówUÏ®Íïxã Lø„>€Ñì8§Èé” 9}Ò2T’Ó§Èé÷…L»®¹¾À5M&“É4²Éhv@N ÔxF.iIüÉéS äô(°ÿ|õì+Gî}øÑwµtÖÿrgÇÿ-Å_sË»ŸP{F³ãqžâ¹ˆ,"[*Ê6§ŸüÃL ‡JrúT9} ì?ãù—žøÎ·Ú>óHÝ÷½ýWw]s½´>€Ñì8§”qN_ލ2›0"[ÆäôIËPINŸJ §@ýçÞ½{»»»;-‰/Û.ØÿÛÿÙ©ŒfÇöi*ÉéS äôeÿùÒßÙ;õ#a…Ó]ݪÀhv ÏSäôYD¶TÈé“–¡’œ>•@N€1é?_=ûÊ ›:.¼ø’š0š«ó9}D‘-ž‘KZ†JrúT9}ôŸzãtž§xF."‹È–Š*U@J†JrúT9}ôŸzãtž§”qN ÌÈé“–¡’œ>•@Ný'€Þ8ç)rú¥BNŸ´ •äô©rúè?ÆÛ…_êyà+xNo<¬ó9}€R!§OZN\åô©rúè?ŠàtWwèK©ûàKO|Go\àyŠœ>@©ðŒ\Òrâ*§O%Ó@ÿ P?xøk¡; Sgý/?wׯž}Eoœ|žâ¹ˆ,"[*Ê6§ŸüÃL O\åô©rúè?Ц÷±o=òæù»Þð󻪯ßUsý¿x[ÖeûzãÌó”2ÎéËUæ€ ‘-crú¤åÄUNŸJ §@ýçÞ½{»»»;¦®FãßÒúÕs£kö©—yÙ¾ÑlæyŠœ>"‹È– 9}Òrâ*§O%Ó î!M&“Étu§ì7šÍö­Ž7~ JâwÔ~àË?qÓÞ¿ø}ö!×é>O‘ÓGdÙRṤ¢5ÊéS!äôÈÒÝÝúÉÏÿ§ßÞÿš°äàÁƒ¡ç ÈéÀø»ðâKÏ·oÿ§c§f‡už"§P*äô)9}Ó£Ù´ž§Èé” ÏÈ%-C%9}*œ>Æ®F³éÆ®F³é•@NcW£Ùtž§Èé#²ˆl©Ó'-C%9}*œ>Æ®F³éå‚QU•5S̖پƶëׯ_¶lY:ª,…Ú¾çž{ÔƒÑl §”qN Ü†Üª€” •äô©rú”ÿ †œþ8—pd5Üßß___üøñtT1Ã)ÂÆ{zzBÍëŒfKåã;¸-™xéÒ¥ 6Ìš5+ky__ßÊ•+[ZZ¢—===MMM[¶l‰.%>xðàòåËǪ rúWë`s®ÖÚÚº{÷îÔTÚjo4[Ë\B JÁh6mç)rú%3¤)×ó,…’*ÉéS äô×±kV†±¦¦fðò .ÔÖÖFómmm›7o.dËëÖ­›4iÒĉ7mÚ-9yòdKKK]]]ØË¢E‹âkÿ‡Ìé™ÍÚÈÆB™Ci/^¼-ïïï/ÃÂðV{{{¼~ÎRUeˆwqÇwLž<9laùòåçÏŸO8ö|û¼‘ ¾¾¾¯¯/^!, Ÿ K2?5¸2‡UžÁ5ÜÕÕ5sæÌðÙæææ­[·æ<䦦¦Ó§O§á —¶Àïóߊ‘}Órn*ÌÜzë­®3ñò°~8ÀÆÆÆêêêœß«¬]ôöö†š×ÅÍ–ÊyŠgä"²ˆl©(Ûœ~ò3)*ÉéS äô×±kæuúëׯŸ={vÖò×çô'Mš”™«ÍçÎ;ï\´hѹsçúûû×­[-œ1cFww÷å˗þ¶¶¶¬}UNéÒ¥}W„™xïa&sy¼þ¥ŠÜu×]K–, GV[µjÕêÕ«?ß¾rnäöÛoooo?æ×¬Y“Y€œ•9¬ò ®áúúúh}æÌ™ø³ùþÀsÕ*gi ùª ù­Ù7-ç¦Ö®] //ãõW¬Xÿ}¢]ÄÍ £ÙôŸ§”qN_ŽÈ ‘-3rú¤e¨$§O%Ó`\Ç®™7558q"^ÍdÝ{'+ϛϔ)Sò=[5rùòå‰'fíkX79IÈÞÆGÊÐØØ)syÎ}å,UdêÔ©ñÇ/\¸0iÒ¤äÃϹ¯œ9zôhæuÙÓ¦M;yòdfrVæ°Ê3¸†Ã7mÚ”yþÀP9ý«xP9K;&ߊ‘}Órn*ÌÄGfâõÏ;—ok9ÊéÍ–ÐyŠœ>"‹È– 9}Ò2T’Ó§Èé0®c×8™ØÛÛ»páÂ={öÄË#µµµ™ÏÈ-ð:ýœ‰Ëîîîùóç×ÕÕE[®®®ÎZy¬rú™ËsÞM(óå¥Êª¬Õ†[¶œ¹ù曣G ‡W®\YHµŒ¬<ñÌ¡C‡–-[¢yíµ×vuuåÜQsssfý*TÎÒŽÉ·b4ß´ä/Ø_¼| C»÷ŽÑl §Èé#²ˆl©Ó'-C%9}*œ>ã:vÍJ&N™2%ºx¾ôz[[[æÝÏóÉwö¾}û.]º4påV?ƒó­ÃÊéWWW_¾|9š?þüˆ¯Ó²Tñjñ6 9ü|ûʹ‘Ì›7/Ì„>œU€|•Yxyj8ì:®¢ÁÏÈ ?Ê©:¨ÌÒŽÉ·b¸md ñ:ýÌå™×é'lmðÂ;wzF®Ñl §Èé#²ˆl©ðŒ\Ò2T’Ó§Èé0®c׬ ã-·Ürÿý÷äO¯÷ôô455…u¢$øsÏ=>2xkÑÝÒÏœ9“y·ôÉ“'ÇwEŸ*<§Ÿsùu×]†‚—/_[[¾|yÖÓ£'µ¾Ÿ~´|É’%ñúùJ5a„ÞÞÞxw6lX¼xqt™£GÆ·ÝÏ)ß¾62kÖ¬-[¶,\¸°ÀÊVy×pX?J©8p Ô@ÎCÞ±cG8Š4TÎÒæ4ÜoE!ß´œ÷ÓÏ÷ëëë‹ê'ó~ú™ϪäÁë´´´lß¾]g4[*ç)ž‘‹È"²¥¢J’¡ÒÿÇÞÝ@WUßùÂGÓcŒ‘LSš†h«¢‘2­´¶Z™©§¾"·Š–ár]õ¡H[‹h§«·í±®,ÆÅÃâñ©¥õårQP/iãS–Z¡Íܲf`è«Øb¨’ˆ„–•Ű ‹axþõtvçeç$„pöÙŸÏÚËuÎ>ûì—ÿ/çïÝî-Ó' dúÛÁýÛÇ^xaêÔ©Gb/™ß¶mÛŒ3jkk«ªª¦L™’¹¿JþÚ-ZTWWW__]×ßÑÑqÖYg…oµ´´,_¾ü(3ý­[·Nž<9³¶x {%mmm£G®©©¹õÖ[ûûû3óË𶺺:|ˆ–/¶W÷ßXCö¦/^ÜÚÚ–œ4iRöìùŠm+f%+V¬›Û°aC‰9¨ýÉoá°üyçöpâĉÑÝlrùСCÛ·o?îUpo ì_E)iù™~ÁU8p`Á‚5o™?~4?çëùWÙË„Öm^l÷0š-Ãó” Îô*mد (“¡’LŸ4é0,öîÝñ?¿þYù§+£œ° ›Å‹WÆ}`Žæ¯¢”›ç ×î…Önkkó‡g4› ó™>@b†Cš€2*ÉôI™>G©wã¿>ÝøÉÐÁött¦âtE¦Ï°þUÔü§cñ–³rŒfwž"ÓHÌpHP&C%™>i Ó`höîýÕâGŸûסk Ó«=›’Éx´¦ã{ìeµ?ås¤ÃØòwŒf³ÏSdúIá¹”ËPI¦OÈôìØµwã¿nøÔÂ5']üDÍÅèWk.î~v£0šöóÏÈEeQÙ¤¨ØL?þ_Ì”áPI¦OÈô(±;.Ì_óV”Ÿ™ž8å>€Ñì1:O©àL_F”Ο0*[Ádú”ËPI¦OÈôˆzH“Éd2•Ãd4{D¦Ê¢²I#Ó§\‚?™>i Ó Äþó`ïÞm÷?¶îÌëž:}Úu—f‚§5']¼óé—´€Ñì±8O‘飲¨lRÈô)—¡’LŸ4éPbÿ½~óÅù?ŸùÆšš‹×žq¹XÀhöاÈôQYT6)<#—r*ÉôI™>%öŸííí]]]™9Ñeû™ ö·çm`4;¼ç)ž‘‹Ê¢²I1JP&C%™>i Óà(ûÏ7_ü—öwÿuX §£S3ÍãyJgúF¦O¹ •dú¤L€aé?öî}eÙšý¯¿©%Œf‡ëå2T’é“2}ôŸzãòi Ó@ÿ  7.ÏóÏÈEeQÙ¤¨ØL?þ_Ì”áPI¦OÈôÐŒ°ý¯¿¹ýÛOxcÞ8þ<¥‚3}Q:T¨l“éS.'®2}Ò@¦€þ`äõtt†®õɺKÞ|ñ_ôÆÅÎSdú¨,*›2}ÊåÄU¦OÈôп}ì¡w ÓÓïšþËû=Ø»Woœsž"ÓGeQÙ¤éS.'®2}Ò@¦€þàxÙµþ'kNþØ[ì_ºæ¤l¼z¡Þ8û©+Ëô0v1û_óWm+ÿ­«GožRÁ™¾ÏA¡²F¦OYTD¦OªÆÊ2}Œ]ôÆåvž"ÓGeQÙ¤éS‘铪±²LcW½q¹§ÈôQYT6)dú”EEdú¤j¬,ÓÀØ@o\nç)2}T•M ÏÈ¥,~2}R5V–é`ì  7.·óÏÈEeQÙ¤¥ 2}8"ÓÊõ<¥‚3}€ #Óg„ÈôáˆL(×ó™>@RÈô!2}8"ÓÊõžÐL?üsdz·¯©¿ôŸ–ÿ¯®®®ÞÞÞƒú“$ åž"ÓGeQÙ¤éS‘铪±²L ”îE>žÜL?L¯>ó¹Õ§^òÃ¥‡ˆõIB"ÎSdú¨,*›2}Ê¢"2}R5V–é”Ò½ÈÇé‡é7OÍ[}ê%ÏÿãwÄú€$!ç)2}T•M ÏÈ¥,~2}R5V–é”Ò½ÈÇ“žé¿ëÿ_ˆõÛ¾-Ö$ åžâ¹¨,*›£4#vfÿ©LŸôŒ•eú¥t/òñ Èôߺ Ï<±>@"ÎS*8Ó¨02}FîÌ<þS™>é+ËôJé^Ò–†‡ATd¦Ý[_¬Pæç)2}€¤é3rgæñŸÊôIÏXY¦PJ÷rÜC󗯌,þ˜fúŠõÝ[ ŒÏSdúI!ÓgäÎÌã?•铞±²L ”îE¦ŸÐL?~êììë”çyŠL )<#—‘;3ÿT¦OzÆÊ2}€Rº—‚éy[Û´ÆÆSjjªfÏž¸oßÂÌüðâ¶Û&×ÕU‡)¼ˆæ‡å—.ýDSS]UÕ £²ä¯yݺ™&œV;n\ÊWf¾›³üŽŸ»þúskkßQ]}âWœÕÓ³ àb9ëÞæo¢Ø(eÉœÞÒrZØ“ /|çæÍs£¾üå}røîM7¿wïa3¦öw¿ûb´@˜03'gáüF;‰{{»X$”çyŠg䢲¨lRTl¦3Qn‘铪±²L ”î¥`–}íµçüîw_ Sx±hÑÔÌü… /ºúê³£ùám´üg>3!;­.–5SûÜsŸ/zzÌŸ?¥àòçŸ?fãÆ[úûï:p`QØôìÙãCüœ·71ä%oºéü]»>v¦­mÚ?ø®Ìü¯}í£¡Âü°‡sçNÊ|wÁ‚¿¸ï¾Ë¢ï†×wÜñ¡b ç7Ú°gú+W®ë’„òeQ™>©+ËôJé^ fÙ¯¾:/óú7¿™×ÔT—y^„·ÑüÆÆS¢åwíú|)7Éii9méÒO¼öÚüoªÓßW}ýIƒÊô nbÈKFÿ/BØ“êê3¯››ë£öéëûRCCMxñòËŸ +Œ¾{æ™§ïØñ¹b ç7Ú°gú=ôЪU«¾ÿýïoÚ´©«««¯¯Ï_; I(“ó™>*‹Ê&…LŸ²¨ˆLŸT•eú¥t/Þ¹>ʲKœ“ÑoÞ<÷†Æ74ÔœsΟ­[7³àò7Þò±½§¶ö™;íD·¦)1Ó/¸‰£\2çí¨·‹öðÊ+ßûä“ׇáŸsæ\¿ð1}<@(kvZôóŸÿ¼··×_; I(“ó™>*‹Ê&…LŸ²¨ˆLŸT•eú¥t/ƒºN?{~öuúƒ}˜í‹/Þ­6gùææú§Ÿ¾áÀEáuøg±èWUÐßWtóúüfo"~÷J\2zö0s—ÿüõ\|qsxþùÓŸþ·ø…u¦¿råʇz(sžM›6uwwûk$ erž"ÓGeQÙ¤ðŒ\Êâ×(Ó'Uce™>@)ÝK±ûégýš}?ýð"sŸý0ÿê«ÏξŸ~ö×ëêªwî¼½`Ö<{öÄÌÝ{^|ñæÑ£O.¸|˜Ýæþæ›ß­úèSO=õÒK/uuuùk$ eržâ¹¨,*›£4#vfÿ©LŸôŒ•eú¥t/³ìûî»lôè“kjªæÎ´ÿ™ùûö-œ?J˜¦yó>Íω§—,ù˰@ÁÌú±Ç®?þÏ««Oœ0áŒèv79ˇùgžyzUÕ --§-[vy4?g±Í›ç^xá;3‹-_þ§Å n¢`4_ú’ùoï½÷ÒqãÂÖ/¸ 1¬'šÿàƒÓÃN¾øâÍÙ_,¸ð±ÎôW¯^]©@ÒÏS*8Ó¨02}FîÌ<þS™>é+ËôJé^Fø¶0¦˜dúå|ž"ÓH ™>#wfÿ©LŸôŒ•eú¥t/2}™>#yž"ÓH ™>#wfÿ©LŸôŒ•eú¥t/ù‰pMM•X\¦À1:O‘é$…gä2rgæñŸÊôIÏXY¦PJ÷"—éHFò<Å3rQYT6)*6Ó™(·ŠÈôIÕXY¦PJ÷RbL\&7äq_ ™> IHúyJgú2"¿YT¶ÂÈô)‹ŠÈôIÕXY¦PJ÷R©™~šÓ™> I(çó™>*‹Ê&…LŸ²¨ˆLŸT•eú¥t/®Ó¯¼Cé’„r>O‘飲¨lRÈô)‹ŠÈôIÕXY¦PJ÷"Ó—éHFòñ ߹yóÜÌü;>wýõçÖÖ¾#̿⊳zzÄ/¿ÿ³gO¬©©jl<å¾û.˸¿üå}røè¦›Îß»÷Ž—¦uëfN˜pFXfܸ†+®Ì‰Î‹íIÁÃ,¶9¡|[Û´°?a™°oûö-ÌÌ/n»mr]]u˜Â‹h~X>¬¿©©®ªê„QYdú@jyF®g䢲¨l"ŒÒŒØ™yü§2}Ò3V–é”Ò½ä'Â_ýêÅW\qÖ®]Ÿß¿ÿÎE‹¦FÁôM7fö÷ßÕÖ6íƒ|WfþùçÙ¸ñ–0óÀEaáÙ³'Æ/–¹öÚsöî½ãw¿ûâõןEÛ_ûÚG¯¾úì°|XÏܹ“æÏŸ’½|X8LáEÁ(|̘ÚçžûtxÑÓ³ úbv¦_pO f±ÝÈÉô³w)úîÂ……ïFóÃÛhùÏ|fBôŸ\§òó” Îô*ŒLŸ‘;3ÿT¦OzÆÊ2}€Rº—üDxìØSó›yùAvtáyÿ]ÕÕ'æ1̯¯?)~ù¦¦º;>]ãÜÍÍõ¯¾úÇöõ}©¡¡&Ú™h~Ø«‚xKËiK—~âµ×æ¼ÅM±=)x˜Åv#gÍÙ»Ž(:´h…áEcã)Ñò»v}Þ½wé$‹LŸ‘;3ÿT¦OzÆÊ2}€Rº—oøž33z»qã-ûØ{jkß‘¹ŸLUÕ ñËÇÌÏ6àz²§Í›çÞpÃø††šsÎù³uëf–¾Å‚‡Yp7bö!ú%Ηé¤üé+ËôJé^J¿N¿àÛææú§Ÿ¾áÀEáuøç€Izöuú¯¾:/{=ѽøsvfÀëô£éÅoŽ®špOŠ]§_p7J¼N?{~öuú2}ŽÈô’Æ3r¹3óøOeú¤g¬,Ó(¥{)v?ýžž9÷Ó/L}rt/û›o~ÿ€IzXáõן»wïaºá†ñÑü{ï½ôSŸz_&îùåÏF÷å«¯>»` Τó/¾xsØŸ3ý‚‡Yl7òï§ŸÙ¥ìûéG·þÏìjöýô³¿^WW½sçí2} Í<#×3rQYT6*6Ó™(·ŠÈôIÕXY¦PJ÷R0^¸ð¢ººêúú“–.ýD|2¾nÝÌ3Ï<½ªê„––Ó–-»|À$}ß¾…sæ\P]}âèÑ'ßwßeÙ÷å¿÷ÞKÇk«ºà‚ÆÇ»&3sÿþ;çÎ-_0Ó ÿça™ Î(ýÞ;³Ønä¬!ìIØŸššª°oa£C›?J˜¦yó>ÍÏÙ%Kþ2,pL¯Ö—é’„r>O©àL_Fä7‹ÊV™>eQ™>©+ËôJé^Žõ}`b¦W^¹mܸ†ã¸C›Fàæ92} Íç­ž"ÓGeQÙ¤éS‘铪±²L ”îeäç… /Ú»÷ŽÝ»¿}ƒ™¾LpÞš’ó™>*‹Ê&…LŸ²¨ˆLŸT•eú¥t/#Ÿ8/[vycã)µµï˜={â¾} —é×ÔTÉô$ C>O‘飲¨lRxF.eñk”铪±²L ”î%q‘ºI¦H}žâ¹¨,*›£4#vfÿ©LŸôŒ•eú¥t/鼡Ípm®<ïÃ#Ó(çó” Îô*ŒLŸ‘;3ÿT¦OzÆÊ2}€Rº—2É©eú2}€”œ§Èô’B¦ÏÈ™Ç*Ó'=ce™>@)ÝK™$éåéa†e·sV²~ýM—]6®¦¦ª¾þ¤9s.èéY Ó¨¤ó™>@RÈô¹3óøOeú¤g¬,Ó(¥{‘é—[¦å•ï}î¹O÷÷ßµoßÂ/|თ]6N¦PIç)2}€¤ðŒ\FîÌ<þS™>é+ËôJé^b"æuëfN˜pFMMÕ¸q +V\YpÉœÞÒrZuõ‰^øÎÍ›çfæïÛ·ð¶Û&×ÕU‡)¼o3 G ®ª­mZcã)as³gOÌ|¥Øªòsðèm±]Ú¿ÿΰڰò°‰ûî»,òw¯Ø¦‡Ü›4¾Yúûï k–é’„J:OñŒ\T•MŠŠÍôãc&Ê­"2}R5V–é”Ò½ÄdúcÆÔ>÷ܧËžžóçO)¸äM7¿k×çûûïjk›öÁ¾+3á‹®¾úìßýî‹aºöÚsÂÛR®ÓKF_Y´hê V•éÜ¥°Âìõ—r~±MM lÒ˜fY·næ‡?ün™> I¨¤ó” ÎôeD~³¨l…‘éS‘铪±²L ”î%&Úni9méÒO¼öÚü˜ >º=ûŠò¦¦ºßüf^æuxÑØxJ)™þ«¯þé+a ƒZUv¦_p—ÆŽ=5{ý¥dúÅ6}4-P°I‹5Ë¿þëÜ3Ï<=ºö_¦H*ã@)ÝKL´½yóÜnßÐPsÎ9¶nÝÌCðì`={~”t—~?ýb_)6¿Ø¦œ_ÊáälúhZ `“Ü™_¼yܸ†o úeú€$¡œÏSdú¨,*›2}Ê¢"2}R5V–é”Ò½”òÜ×_¼9ºp¾”D;,œ}QüQ^§_pUUU'ô÷ß•y½wïf÷C»N¿à¦¦ 6iþÎ<ùäõ--§ýó?ÿס=tW¦HÊù I(óóÏÈEeQÙ¤¥ ±3óøOeú¤g¬,Ó(¥{‰É©{ìšñãÿ¼ºúÄ ÎÔ½wöí[8þ”ššª0Í›÷ýûïÌÌ_²ä/Üb×Èßwße£GŸ˜;wRô•b«Ú¼yî…¾³ªê„––Ó–/¿|ÀL?|1¬6KØDØPÁ}ÈÙ½b›>š(ؤ9Û•G¦PIç)œéT™>#wfÿ©LŸôŒ•eú¥t/C¾ÜT¶“L œÏSdúI!ÓgäÎÌã?•铞±²L ”îE.Ó`$ÏSdúI!ÓgäÎÌã?•铞±²L ”îE.Ó`$ÏSdúIṌܙyü§2}Ò3V–é”Ò½HÀeú’„‘eQ™>©+ËôJé^$à2}IÂHž§ÈôQYT6)dú”EEdú¤j¬,Ó(¥{‘€Ëô$ #yž"ÓGeQÙ¤éS‘铪±²L ”îÅT‘“L$”íyŠL•Ee“Â3r)‹_£LŸT•eú¥ }HèIy䑇~xu{|uzÈôIByž§xF.*‹Ê&Å(MÀÈéÙ>Ààuvv¶··¯Zµêa*H(h(k(nWW—?r€29O©àL ÂÈô!2}8"Ó¼-[¶„ndíÚµ«V­z„ŠJ ÊŠÛÝÝí LÎSdúI!Óg„ÈôáˆL`ðB²iӦГ´··¯¡"„R†‚†²†âöööú#(“ó™>@RÈô!2}8"Ó¼îîîЇlÙ²¥³³óy*B(e(h(k(n__Ÿ?r€29O‘é$…gä2BdúpD¦0x}}}½½½ÝÝÝ]]]¯T®ï}áÞWR#”24”5÷àÁƒþÈIB™œ§xF.*‹Ê&EÅfúñ1åV™>©+Ëô0vЗÛyJgúþ=ë7‹ÊV™>eQ™>©+Ÿ~úé—VŠF½ûëÅ…O?øÁN™2墋.º€ØîT#è—p†"ÓGeQY™¾Ú#Ó‡¢™>@¹‘飲¨¬L_í‘éC®ðGþñÿtá…†Aóyç7~üø÷'Ö £Þýâ§a™sÞò~b»S 7>¾æÌ™#ÓGeQY™þñäY 2}(sííí«V­z8Éüi/\¸ð®»îº÷Þ{ ¸•×Î×zãrÎPÂyJ8[éêê’¡²¨¬L™¾LþdË–-Ï?ÿüÚµkàù‘Äð§}ûí·á _øÿã<”·pnÎPÂyJ8[éîîvÖ ÓG¦/Ó‡?yå•W6mÚ†ËíííkkÀŸöç>÷¹Ïþóÿ÷¿(oáÜ$œ¡„ó”p¶ÒÛÛë¬ @¦L_¦ÒÝÝÊ[¶léìì|>±J¹NáÂ…ÿøÿø<PÞ¹I8C ç)ál¥¯¯ÏY€L™¾Lþ$ ‘{{{ÃX¹««ë•Äð§}çw†_÷#<ò PÞ¹I8C ç)álåàÁƒÎÚdú#ʳdú€Ÿ6€±+zcTVeQÙ S±™~|ÌD¹UdÀàï`ïÞÿóûáŸZ*é§ €±+€Þ•EeQÙA‘éS‰ þz7þk{ó§Â=š*æ§ €±+€Þ•EeQÙ!éSÉþöîÝvÿcï¿1|¦_/BBü´0vУ²¨,*{4dú”EE²ƒ¿Þÿú“9÷äi—uœÿ駦­>ñ¢ÿë9 Iÿik+cW½1*‹Ê¢²GÏ3r)‹_cøôïýí·¯›÷DëUO¿kú÷ÇÏ\ýއ™««>,ЇDÿ´¿ùÍo~ç;ßÙ¸q£¶0vУ²¨,*{ôFiFÆ€Á_fZ]wIôÚd2UÀ´téÒ5kÖlÞ¼Y7pôdúŒR®Óèâ›WŸò±ÇOøcøä©—>Yÿñîg]Þ ðÍo~óëoæ,]ºôÑG]¿~ý¶mÛ4ÀÑ“é3BJ¿éöΧ_úÁ”ÙŸxQ˜¹æä‰õ!¾óï,}»0gÍš5áG½yóæ;wj"€£'Óg„ öAš{÷þó¼ûžª¿,sÍþ«=« ¡œmܸqýÛ…9›7oÞ¶mÛÎ;ûúú4ÀÑóŒ\FÈ`3ýÈΧ_úߣÿ2,ÐÓÑ©Aß  ÿ@oŒÊ¢²*›f›éÇ'È”[Eb2ýŒƒ½{_Y¶fÿëojIÐ÷è?У²¨¬Ê¦–LŸ²¨È€™> ïР7FeQY•E¦OYTD¦ú^ý'zcT•Ee$Ó§,*"Ó}/€þ½1*‹Ê¢²òŒ\Êâ×(Ó}/€þ½1*‹Ê¢²¥ 2}€£$Óg„ÈôŽ’LŸ"Ó8J2}FˆLà(yFn™š?þ¸Êrþ¨úK‹ ŸžqÆcÆŒijjY&Mšt)É1}ÜD ÿУ²¨,*{4–.]šÆL?þªðò7nܸQ¤ÌÇ?þq™~‚3ý»!­2?ÖÖÖ“7Œz·FÐèQYT•šÓO?]¦ŸìLÿî»ïþ=¤U&Öÿú׿î.iú^ý'zcT•UÙ4¸ôÒKeúI%Ó™¾¾@ÿ €Þ•EeU6U2™þ…^ØÙÙ¹eË–W^y¥»»»¯¯/{ÏÈ-Ségnœ’Üõ§Jy6¦L_ß  ÿ@oŒÊ¢²*›*™Lÿýï{{ûóÏ?¿iÓ¦W^y¥··7{™QþJÊSâ2ýüP8U™~²þÃÈK¦ÊdúçwÞªU«Ö®]ûüóÏoÙ²¥»»;{™~™ª€L?U1zÒ3ýòtûí·ËôHL¦?~üø‡~xÕªUííí]]]ÙËÈôËÔ2ýLP»|ùò÷½ï}555cÇŽýæ7¿™ói[[[cccUUUfæÿïÿ½¹¹¹ºº:,^g¯í¾ûî ˇ%O;í´¹sç¾úê«ÑGa\pAæ[Ë–-‹VÉÞâ믿~òÉ'‡õ¼òÊ+™ùáExfþö·¿ oÃN¾÷½ï ;ÜÔÔôõ¯=æÃ’aoÃ’áÃvKYÁ£Îo´›%' Þ<ü˜Cp¯ 6r±/Æ4`Á"Æ+úb| „}kmm [ KGGÇ1 ôCW•ÙœL€”ˆî½óðÃ?òÈ#kÖ¬yþùç_yå•ìedúejÈ™þ5×\óúë¯www_ýõámëg>½õÖ[wïÞå¶aΗ¾ô¥ðúÎ;ï ¯³ÓÛ¿û»¿Û¼ysxþtÂGó7“_yå•¿ýíoßxã/|á ÞÎ%l4¼¸çž{¢ :¼ýìg?›ÙJxý™Ï|fÏž=_ÿú׳Ë‘Ùnæè‚ð¢”õçuÁF+±YŠeú?æÐÜ«øFÎþb|+bü½wl¹sç†X»vmxÝÚÚzŒýÉ“'gÖŸóß! R¥:ÓOá3r3yë¯~õ«ÌÛmÛ¶…·ï}ï{³?íî;vl˜“¹v;sI{˜Slͧžzjæukkkxû‹_ü¢ô[´ÿä'? /Î?ÿüÌü &„·afxÝÒÒíóîÝ»c2âÌv££ /JYþQÜí›%¼®©©)1Ó9´÷*¾‘³¿Xzf1>Óo ó?XdÞûÿ †%Ð?~¼@_ß  ÿ@oŒÊ¢²*›©Îôõ¡Dïÿ3ýœ9ÕÕÕ%>5{áï}ï{ùÈG2÷´‰¿IK‰]½ä’KÂëÿøÇ7n /¦M›–½L¶bñѬÿhž›Ó†¥gú1‡6„½ÂVUĘOcþŠŽÅ}ù»ººþâ/þ"¬¶¹¹Y ¯ïР7FeQY•M™~‚£ëô³—ojjŠ®¹Î¹»±±1¼ýÞ÷¾^ïÙ³'û»™KÈö³Ÿ *_½zux½à-áÅO<‘}™ytÝwŒ˜ëôcÖ?„ô<¦Y²ó×_½”ëô Ú€{Uz#Çl%¦ˆñÅŠocšég_¡Jìßaú^ý'zcT•UÙT‘é'Ø3ý믿þõ×_ã7n¸á†ðöþáŠÅ¯wÝuW˜sçw†×_úÒ—²o›~Æg„·/½ôÒž={æÍ›—ýÝÌ­Þ?õ©O½úê«Ù·z?å”SÂümÛ¶ |ßûÞ÷6¾%úÏ AؽÌíàwíÚ¶õÌ3Ï\qÅ®­­-stÝÝÝa»Ù÷ÓYÿ2ý˜fÉDík×® »ú7ó7ÙßÍ?ü˜Cp¯Š5rþc¶SÄøbÅ´À1Íô»ººÎ;ï¼°Â /¼°··×¿Àô½úOôƨ,*«²i#ÓO°!gúË—/?çœsªªªššš¢@¿Xüzçwf.Ê;vlörŸ}öÙñãLJ•477/Y²$ç»<ðÀ\>mll\¶lY”¹Ÿzê©1ƒg]Üwß}ÙûÖ0a„°¶ššš+®¸â™gž)v€ápÂÞ†%Æ}(eýC»ËM±féèèÈ4Kkkkhçìïæ~Ì¡•…lä‚_,¶•˜"X¬b-pì2ý—_~yâĉ™ç¼þúëþí¥ïР7FeQY•M!ÏÈM°a¹Ÿ>$ÂË/¿œ¹Bß=ôõ½úOôƨ,*«²i>üTgúI'Ó'%~þóŸŸ}öÙ™{èÿú׿öÛ µdú –Éô3±~‰ªÞr7$Gt£ÿÖÖÖ7ÞxÀ4“é'X”éCÅkmmuËé'ؤI“2Yç×+Å £ÞÿéÌ™3gÏž½`Á‚¯“2}8â¹P¼üãS‘ø'V‡O þú^ý'zcT•UÙ”Hu¦Ÿ '¥x2}@ß  ÿ@oŒÊ¢²*›2ýÄO¦è{ôŸèQYTVeSB¦ŸøâÉô}/€þ½1*‹ÊªlJÈô_<™~e¥LÐÞÞsÏ=3gÎ}áÂ…===GÞºêü©§žÊiÆ‘OT”éßxã¡Åò?­ÈL?>_ÆC(“Ûû¤3Óÿë¿þëõë×>|øÀwÜqÇ´iÓŠm+N蔲ÿÀ±&ÓO|ñdúGò‚¶êêê‚ó³8|øp̶âsÃRrÞìrÉ’%555sæÌ9pà@f~xf†ÚÚÚ¢åwîÜ9cÆŒÚÚÚ°“Ó§OÏ\2\ðÿøÊW¾2zôè°†Y³fíÛ·/~‡ îCx1oÞ¼º·„Ñü°ü²eËšššªªªò7ÝÒÒ’ùÏ!%iö+V¬Èüï“'OÞºuk|S ù‹%îUtŒÅš"çØ ÆÊ«0„Ü9ç®;ÙÛZ¾|yæØCOý£ýè»ßýîYg•iŠìÿ%eÀÆÌ>ÞÿÌŽ¦ù„9+ùó+Øñ{Rlm'N {ØÚÚ𮨝)z†˜$ή]»ÂÎø÷0bdú‰/žLÿÈÛ¯Ó¿çž{&Ožœ3?Ç´iÓæÎÛÙÙY,ÙÞLÿºë®ë{Kxq÷Ýwgæ‡Ùó£åï1³cáXÂ2sæÌ)¸þo|ã×\sÍž={Âb·Þzë‚ âw¸à>,Z´(¬$šÞFËÏž=»XB%ž%iö³fÍ ûŽnÉ’%úЇ⛢Ä/†ý _œ1cF±,¸Ø^ecLS\g|²—,å†Nëׯonn.Öª7Þxã®]»Â±/]º´±±1ûíG>ò‘œå‹µIÎñ–øgv4%ð72´6/¶'ÅÖ6f̘мáÅîÝ»ó ”¯££#¿U#cǎͬ-[Á4#ž‘›øâÉô¼=9miiyíµ×òçgg©¿ÿýï¿úÕ¯Nœ8±¶¶¶¹¹yáÂ…aNÎ »ó1ah´?;vìhjjʼ;vlöüb— ×××\Øíèëû÷ïohhˆß·‚û^Dº /£å÷ìÙSìÐr2ý·’ÝÑÕÓÙWCkŠ¿6±sçÎÌëð¢Øk)¶WÙÇÓk:„*ÄT§¦¦fÕªUÅ>‚øpì9oó¯(/Ö&9Ç[âŸÙ°” m™¾·X›Ø\Åö¤àÚB‡°lÙ²ìÿ³$æþ³Ÿý쬳Ί®ýÏ·råʰŜ¯ËôcW½1*‹Ê¢²#)Õ™~|‚œ”âÉôd%t»víúä'?ùÌ3Ïä̱cÇŽùóçGwÐ.ý‹Åö!çm‰÷ŠÞvvv^rÉ%µµµ™ÿÝ’%ùl9wn‰ß·bû0à¾e´¶¶æ$¤%®­Ä&*˜éê‹C8öR+¸c¥W!Æö“Ç€IDATªU«Î=÷ÜAý]e[•þgvô%Èÿ4óK/ñ¿åmëÖ­3gÎlhh-ÜÑѳŸ6lÛ¡eb¶V’ó__ÂoÁ½wcW½1*‹Ê¢²#I¦ŸøâÉô¼=¡ëéé;vlt?ô7]ì~2¥«ªªŠîä³oß¾!_§ßÜÜüì³Ï:tèÈ[·*Y†Å2÷@/EÌuúÙó ^œ~¤Ð3rC!JßÊ€ùo)×éüböEâa ƒ½N?{±Rš¢àuúñU(ñÞ;Ù×›Çüm)!Ñ.Ö&ù?¥ü™ K ò¿•ù¥kóÿ˜‹íIüÚ6lØP¬úÁÚµk[ZZ¶lÙRúÏüÇòøãž‘ »èQYT•I2ýÄO¦$/¡»å–[¾õ­o‰½ŸþÚµk3¹ÿž={-ZtÉ%—Ĭ0~sS¦LYºtéáÇwïÞ=kÖ¬œû¹ï{Kþýô3󝹿šhùÑ£GG·ÿͯ««ÛµkW´¹Å‹_uÕU™8õ׿þut?ôb;³}}}™}(xCóüM¯Zµ*|«ô­ ˜ kŠR¾8cÆŒÌgΜs?ý˜½*½)ò÷gPUˆWìOn°™~±6Éùb‰fGS‚|ÑÊ3¿ôbmß,ñEùk EÉÜ“gÆ ᨠæòåË›››õ«_ ¡LáðW®\i,»èQYT•12ýÄO¦$/h{á…¦Nz¤øýôô£Íœ9³¶¶¶ªªª©©iÞ¼y9W[!Óߺuëäɓà [ZZxàì0´­­môèÑ555·Þzkf~xÞVWW‡ÂÑòguVf=Ë—/æßÿýa Ù›^¼xqkkkXrÒ¤I/œÏÞá‚ûpàÀ Ô¼eþüùÑüœÌÙô¡C‡·oß^âVÌ…‹5Å€_ û?wîÜè‹Ù—ºØþ9ë,¥) îOéUˆWì¾=ƒÍô‹µIÎKü3;šä‹Vžù¥kóøßZÌž\[(Êyçvlâĉѽwò3þ¤(V¦ð+¿…‚{`ì  7FeQYTöñŒÜÄO¦_æ†pŸò߇ŋ—ÛýF¶oßÞÚÚZží_Ц¦¦ø;¹c›Œp *¦ï ʹ¥Oø´µµHƒµÿõ7·ûéoìIÛØ@’€Ê¢²¨ì°Hu¦_œ闹ŠÌôËÇ¢E‹öíÛ÷ûßÿ¾ô›·”§Ð755ÕÔÔŒ@›Œp *C(M(Ðw¿û]ÿê†EOGç6'}ä7ÿïÿÖÀ Èô_<™~™–”¶ômåá}aË—/oll¬­­3gNæ DZý“Ò&ÇzsÅþÈöÛÇ~Æ6ÞÔ\ü£ëïþ·oh 2ýÄO¦D»ÖÿäÉÓ.{ªá²?„û'|èÙ3¯uÙ>0 ™~â‹'ÓH¨L¬ÿtã'3×ì»lgä&¾x©ÊôM&“Éd2™*{zæ]Ó3/^{âùv2 I@eQY••é§+ÓOú‰ëô}/@¢íXÙñø‰SCÇødÝ%ë?ô_×uݺ÷]ÿËxø`ïÞR†FÀh•EeU6…dú‰/žLÐ÷$Q&ÐúW¬¿hîS Ó~|×w­ÿÉú÷Ò‡FÚÀh•EeU6…dú‰/žLÐ÷$Îo|:sÿœì ó;4jooïìììêêÒžF³¨,*«²é!ÓO|ñdú€¾ Yz::CgøÄÉ͹0ßÐÀh•EeQÙyFnâ‹'Óô½ ²ÿõ7Õ¶òߺz ŒfQYT•‚Tgú•Q<™>@E24òÉô_<™>@E24òÉô_<™>@E24òÉô_<™>@E24òyFnâ‹'Óô½ÉÐÀh•EeQÙ|©ÎôãO“’R<™> ïHa‡ih`4‹Ê¢²*›N2ýÄO¦è{RØaÍ¢²¨¬Ê¦“L?ñÅ“éú^€v˜•14ºçž{fΜ™y½aÃ†æææ0¾ùÝûpï½÷VØßÏ0¶dv™´<ͪ,*‹Êw2ýÄO¦è{RØaVÀШ¿¿̘1;vìȼ:uêøÃã²'Û·ooll ûSI?Õéç”I˃Ѭʢ²¨ìq繉/žLÐ÷¤ðD%fh4ê?ÕÖÖN›6í¿øEþüo¼q÷îÝÑW¶mÛvÝu×ÕÕÕUWWO:uíÚµÇâˆrRæG}4ìFô¶¦¦fÄÚ6?ïž1cÆÊ•+ãŒü&J܇œ2%½åÁh•UYT¶¤:Ó¯ŒâÉô*ÒÑdú™‡Z¼xñ¤I“ræ÷õõÍ;wÆŒ™·Û·ooiiY±bEæré-[¶Ìš5ëXQNž{ã7>õÔSÅ>=¦ò·s“ë!ìÀÈo¢Ä}È)SÒ[ Èô_<™>@E:úL?£ºº:þþýû£ëâçÌ™³|ùòRvéî»ïnhh¨¯¯_¶lYfÎÎ;g̘Q[[¶2}úôèÚÿŽŽŽ‰'†M´¶¶~÷»ß=’õ D»ÑÒÒÒÓÓí[ö§ÅV>]±bEøb˜?yòä­[·fæ8p`Þ¼yuo /ÂÛ‚M‘y›¿'Á®]»Âj uÁì;Ì\²dIccc8ÆÐ€Ñcö$4ZSSSUUUÁp»ÑÛb›îïïoÃÌðQ[[[´|ÁÆ,¸_ùÊWFÖ0kÖ¬}ûöå—éXxLËPL?ñÅ“éT¤a¹Nÿž{î™¶fΙ3'3̘1ëׯ/vïÞ½`Á‚ü8’õò?-¶Ú°Ì¬Y³Â>„–,Yò¡ý±‰-ZtÍ5×ô½åºë® o n1;Ï?Àb7ÿ)–é‡ E[Œ$fOfÏžå¥\#³ó7^dÏ–iÌìõãß{Ú6,vë­·FUË/Ó°øHÞv  Èô_<™>@E:úûé---¯½öZ4?ó"çÞ;9¡m1cÇŽPêáÇëëë3¯Ãv—-[–}}÷‘ÁdúÅV–‰.ó£5455Eû^466\çÐ2ý‚¢† [ ;0àžìÙ³gÀƒiì/¸éP ìù7‘Ó˜Ù577G_ß¿CCC±2 ûËôÅ3r_<™> ï¨HGþ®]»>ùÉO>óÌ3ÑüŒšššìgä–x~Á4¶³³ó’K.©­­Í¬¹ªª*3ëÖ­3gÎ k>÷Üs;:: ®¡µµ5ç¦.®¶ÄŒ¾à톎ÄfúaOu˜·8àž”¾‰!lé™-Z,¦LÃràƒmy0šEeQYT6Õ™~üiRRŠ'Óô½)ì0K¼Ÿ~OOÏØ±c3×¶Ë‘çÌ™Ý?FÁëô›››Ÿ}öÙC‡yëV?ù›Ø°aCt)wþ3rÃ!Üíb«-‡Md_<]$^UUuøðáÌë}ûöÅdú?þø žÔs¹zÁ=B¦³óƒºN¿ÄÆ ‹Eÿ™'¾LÃ{àƒmy0šEeQYTV¦ŸøâÉô}/@ ;ÌÒŸ‘{Ë-·|ë[ß:RÖ3kÖ¬hùœ= f̘±råÊ‚ís?ý}o)xSûœ=ÉYIþä+¶óñ›ÎÌ›Ž–/Ö˜9û°xñâ«®ºjçÎáõ¯ýëè¶ûùeÞiy0šEeQYT¶ ™~â‹'Óô½)ì0KÏô_xá…©S§‰½6|Û¶m3f̨­­­ªªš2eÊÚµk ®mÑ¢EuuuõõõÑuýguVøVKKËòåË£…þwÞyÕÕÕ'NŒî½sÿý÷×ÔÔd?·±±qûöíù*¶Úb™þ,XPó–ùóç÷÷÷gæoݺuòäÉ™õ<ðÀÑò9{ö!ìIô­øöŒf¶µµ=:¬çÖ[o¾[lOrV’³Ûùb›/ÂÛÐæá£°@´|±ÆÌ߇ŋ·¶¶†%'Mš]›Ÿ_¦a<ðø–£YT•Ee ’é'¾x2}@ß Â³2†F‹/.‡û®„}hkkÔWJ¹yÎ12ò›Î.Óðn}-F³¨,*[Iö¿þæöo?}à=*;(ž‘›øâÉô}/@ OA ޝTeúå³u0šEeQÙÊÓÓÑF¶OœüÑ7þ¿Ÿ¨l‰RéWFñdúÉШœÕÔÔ ãªò̦ïdüö±„Ám˜ž:}Ú–EËöîÕ&ñdú‰/žL "»Öÿ䉺K×¼cjå®®šºþ¢ÿ:¨ËöÓF¦ŸøâÉô*’¡éñÇXÿ¤d®Ù_]õa—í#ÓO|ñdúiÀ¡‘Éd2™L&“ÉTÁÓê/ʼxí‰ç\X¦ï¹2}™> ï04ÀhVeQYFHÏ÷~¼ºúé¯sùÓïœþì™×þòÎ\§?àØ8Um•êL?éÅ–éú^€Ôv˜†FF³¨,*[12þê§>ÛzÍ“§~üÇ3îÞµþ'ÿqèßK§ª¹dú‰/žLÐ÷¤°Ã440šEeQÙÊÐõè÷3—çg_˜?رq{{{gggWWWZL¦ŸøâÉô}/@ ;LC#£YT•­=¡LOœüÑœ ócÈô_<™> ïHa‡ih`4‹Ê¢²I·ÿõ7Õ¶òߺzŒÅ3r_<™> ïHá)¨¡€Ñ,*‹Ê§slœêL¿2Š'ÓpÞÆÆ2ýˆL¿¬‹'ÓpÞÆÆ2ýˆL¿¬‹'ÓpÞÆÆ2ýˆL¿¬‹'ÓpÞÆÆ2ýˆgä–uñdú€¾Ày F³¨,*kl,ÓŒJçŸBRŠ'Óô½Î[0šEeQYcc™~D¦_ÖÅ“éú^ç-Í¢²¨¬±±L?"Ó/ëâÉô}/€óŒfQYTÖØX¦‘é—uñdú€¾Ày F³¨,*kl,ÓxFnYO¦è{œ·`4‹Ê¢²ÆÆ2ýÈ(4å\<™>€ó06–éGdúe]<™>€ó06–éGdúe]<™>€ó06–éGdúe]<™>€ó¨$ÞüýgþíÁÝ¿ÔØøÿôÏÙߪTž‘›øâÉô}/@E240šEeQÙÔúñÌ¿ #ÞŽ÷ߘ èK¯ªý™³¯¯ü_¾RÙ“êL?þO!)Å“éú^€v˜†FF³¨,*[Áîþ}ÇûoŒbýÇÆ«¿ýð“­Wýaù 7¹NÿˆL¿Ì‹'Óô½)ì0 ŒfQYT¶²eÇúgúï¹2üó™sfT| D¦_Å“éú^€v˜†FF³¨,*[ñ¢X?LÅÂúoþ>³À“g^ýËÿóÏih™~â‹'Óô½)ì0 ŒfQYT6 rn“óé7ÿ3ôÏ•ëŸz&%ccÏÈM|ñdú€¾ …§ †FF³¨,*›™ûéçÇú ôÃØxÅÃé§:Ó¯ŒâÉô*’¡D£ßœ«õ£@¿cÂMiËô_<™>@¥ž·@fô›}ž¾_¾ú™«øeú2ý$O¦P©ç-†F£Xÿ‰šF~æÓöööÎÎή®®4´†L?ñÅ“éTðy‹¡dnš3…ñ–-[º»»ÓО‘›øâÉô}/@¥ž·Í¢²¨,‘oþ~MË_‡‘ðcUSÃ?W¿çÊ5+noo£âM›6…qoooÚ!Õ™~üiRRŠ'Óô½)ì0 ŒfQYT6U¢‡â>yæÕ÷þ¡'[¯úÃëÖ«~ôýlÙ²%ŒŠ»»»ûúúÒÐ2ýÄO¦è{RØaÍ¢²¨lzDþ3çÌøåÿùç¥Õÿ|æìëÿp'ýsfìøé/{{{ûúú<˜†Öé'¾x2}@ß ÂÓÐÀh•EeS" ô³Š{$둹៙ù)!ÓO|ñdú€¾ …¦¡€Ñ,*‹Ê¦A~ Ÿ]ÙtÆúž‘›øâÉô}/@ OA ŒfQYT¶â ôs*›ÂX?Õ™~eO¦P‘ H³b~¾´Åú2ýÄO¦P‘ H­ÒýŒTÅú2ýÄO¦P‘ H­ÏüÛÒýŒ(ÖÿñùJe7ŽL?ñÅ“éT$C#Rëàîßÿø¿|e°WÜí[‰ã¹‰/žLÐ÷T$C#£YT•Eeó¥:Ó?MJJñdú€¾ …¦¡€Ñ,*‹Êªl:Éô_<™> ïHa‡ih`4‹Ê¢²1n*Û©§£ó(ë+ÓO0™> ïHm‡ih`4‹Ê¢²1~èÐݦ2œBiž:ý²îg7M}eú &Óô½©í0 ŒfQYT6æÀ¥çe›éw?wûQÆúž‘›`2}@ß ÚSPC#£YT•+JÏË6Óÿ|í{óÖÔ_úOËÿWWWWooïÁƒUßTgúI'Ó¨ì31C#†6’”ž—s¦¦ß®û¿ÖÔ_òÃ¥‡ñü`c}™~‚Éô*ûLÌЀ¡$¥çežé‡iÇ3·­>õ’çÿñ;ƒõeú &Ó¨ì31C#†6’”žÍ4jÔ¨ÈôÃÔõôm«OýØómßT¬/ÓO0™>@eŸ‰0´‘d™§ÛÇ.4OV¦ÿÖMxë{Fn‚Éô}/@eŸ‰Í¢²¨ìÐF’Årêã¦Óí–¸òÒ[`$3ý!Äú©ÎôãO“ÊŸLÐ÷¤¶Ã440šEeQÙ˜—é'(ÓÿS¬_Ú½õeú &Óô½©í0 ŒfQYT6æÀL´Ã‹eË.oi9­ºúÄóÏóÒK7¯Xqå™gžÞ^xá;úÓÿ–“nïßçìÙkjªO¹ï¾Ë²×³té'ššêªªNøÃs_w|îúëÏ­­}GXÏWœÕÓ³ ³L$gµáŃNÏìFØîæÍsã7Wð¸rV¾oßÂÛn›\WW¦ð"¼0Ó/¶¹b« Ó—¿üáÑ£O_¹é¦ó÷î½#3sݺ™&œfŽ×Ú³`¦?uvvëËôL¦è{RÛaÍ¢²¨lÌ—’éúÓçíÜy{ÿ]÷ßÿW§d¿ýð‡ß¾/Z4õÚkÏÙ»÷Žßýî‹×_nöz>ó™ Q¨}þùc6n¼%¬äÀEá+³gO,xå{ö×oºéü]»>¾ÒÖ6íƒ|WüæJ¹¬~á‹®¾úìðÅ0…•„·fú™ÍE_‰–)¶ª¯}í£a~Øíp˜sçNš?Jfþ˜1µÏ=÷éð¢§gA4sPWñ···ëËôL¦è{RÛaÍ¢²¨lÌ—r{™(ˆïï¿+çmuõ‰9qySSÝŽŸË¼/²Cù]»>_pa=õõ' ˜éG¿go·ØæJÉôÃwó›y™×áEcã)¶ÆØ±§¾úꟾ’}ÔWÕÜ\-ß×÷¥††šÌë––Ó–.ýÄk¯ÍòyV®\9`¬ï¹ &Óô½©=540šEeQÙ˜±â`o“¹—¸@fÚ¸ñ–}ì=µµïÈÜ 'sCžÒ¿>àü!Kôß †°†b«õvÑanÞ<÷†Æ74ÔœsΟ­[7s™þC=´jÕªïÿû›6mêêêêëë˯oª3ý¤“éTö™˜¡CI{¦Ÿ}áü«¯Î+–z77×?ýô , ¯Ã?‡œÝÛ\‰×ég_t”×é\U8ÌÌ£ŠM/¾xsøî2ýì˜þç?ÿyooo~}eú &Ó¨ì31C#†6’öLÑ¢©×_îÞ½w„醯 åG>9º¡üÍ7¿?ú´®®zçÎÛKÏô‹m®à”³òèæøá»W_}vtü˜)º}æ+9OÈ_Õ½÷^ú©O½/ó_^~ù³Ñc‹̽z^|ñæÐC»÷ÎC=”¹ϦM›º»»óë+ÓO0™>@eŸ‰0´‘ä°gúûö-œ3ç‚êêG>ù¾û.Ë¿á~fZ·næ™gž^UuBKËiË–]}ºdÉ_ÖÔT•~KŸb›+8å¬<|wþü)aN˜æÍûÀþýwØa™¹s'E›+eU÷Þ{é¸q áH/¸ ñ±Ç®ÉÌ /Æÿó°ž ÎÚ½wüñ‡zèÑG}ê©§^z饮®®üúÊôL¦PÙgb†F m$9´g´–8½òÊmãÆ5ÓMÇÍÇ)nõêÕ1I}†gä&˜LÐ÷Tö™˜¡€Ñ,*‹Êm$y,ç… /Ú»÷ŽÝ»¿Pâ m’µ9™~%œ&•?™> ïHm‡ih`4‹Ê¢²1~,çeË.ol<¥¶ö³gOÜ·oᱸó7—¹NÎ$Ó—é'‰LÐ÷¤¶Ã440šEeQÙ˜OÃUí®Ó—é'LÐ÷¤¶Ã440šEeQÙ˜—ËôýªË‘LÐ÷¤¶Ã440šEeQÙ˜Ozº=jÔ¨aYF¦_i<#׉+ ï04Àh•UÙÊIÊôeú”#™>@eŸ‰0´‘äȤêÇ7¯–=,«ÿ0 Ó¯|2}€Ê>34`h#I™¾LŸr$Ó¨ì31C#†6’,^?øàô––Óª«O¼ðÂwnÞ<73ß¾…·Ý6¹®®:LáEx›Y8RpUË–]žYÕùçy饛W¬¸òÌ3OϬù§?ýo™ÅvìøÜõן[[ûŽ0ÿŠ+ÎêéYPlÍ‹Mmh¨©¯?iéÒOÄïí(LëÖÍœ0ጚšªqãÂx˜2}†ŸL ²ÏÄ ÚH²`~ÓMçïÚõùþþ»ÚÚ¦}ðƒïÊÌ_¸ð¢«¯>ûw¿ûb˜®½öœðvÀ ØÃGŸþôy;wÞVuÿýÕØxJöÛøÝ™ÅÎ?ÌÆ·„™,Z´hêìÙ ®ù«_½øŠ+Î ;¶ÿa±ø½=š#3¦ö¹ç>^ôô,˜?ŠëôÉ3r¸ú^C#ŒfQY•­¼‘dÁ<ºb½¿ÿ®êê3¯›šê~ó›y™×áEcã)¥dú{÷Þ­*çm´æì)̯¯?©àšÇŽ=5Ú÷öhލ¥å´¥K?ñÚkóÝ{§bO“ÊŸLÐ÷¤¶Ã440šEeQÙ˜ðÆñÑÛœùQ2Ÿé—òvãÆ[>ö±÷ÔÖ¾#ss›ªªJùúÑ/Sìˆ6ož{à ãjÎ9çÏÖ­›)Ó÷«>dú€¾ µ¦¡€Ñ,*‹ÊÆxé xSSÝ«¯ú:ýRÞ67×?ýô , ¯Ã?‹eîÅ®Ór¦_숢éÅoËÈôýª™> ïHm‡ih`4‹Ê¢²1^z¾hÑÔk¯=çw¿ûâÞ½w\}õÙÑÝçëêªwî¼ýh2ýÑ£OŽî_óÍïæç¬9s?ý°LÎýô‡œé;¢Ù³'fþãÁ‹/ÞömÀÔéûU?™> ïHm‡ih`4‹Ê¢²1^z¾oßÂùó§ÔÔT…iÞ¼ìßgfþ’%æ áÆ8ÑÛuëfžyæéUU'´´œ¶lÙåÑüü5/\xQ]]u}ýIK—~âè3ýbGôØc׌ÿçÕÕ'N˜pFtï˜Ã”é—#ÏÈuâ è{ 0šEeU¶òF’ås?“LŸÜâÉô*õLÌЀ¡$åã2}Ê‘L ²ÏÄ ÚHR>.Ó§Éô*ûLÌЀ¡$åã2}Ê‘L ²ÏÄ ÚHR>.Ó¯Lž‘ëÄÐ÷`4‹Êªlå$åã2ý4ž&•?™> ïHm‡ih`4‹Ê¢²1.—éûU—#™> ïHm‡ih`4‹Ê¢²1nJè$Ó¯ð_µLÐ÷¤¶Ã440šEeQÙ…áb4>òÈ#?üðêJòøã«+—L¿’Õ2}@ß ÚÓÐÀh•EeÔÙÙÙÞÞ¾jÕª‡+ËÒE_{¸¢…’…Â…òuuuå—Õ3rL¦è{R{ jh`4‹Ê¢²Ú²eK1®]»vÕªUTÿû®¯?R¹B±BÉBáBùº»»óËšêL?édúÌЀ£ÆŠ›6m ƒÆööö5$D(V(Y(\(_ooo~Yeú &Ó¨`†F¥îîî0\ܲeKggçó$D(V(Y(\(____~Yeú &Ó¨`†F¥¾¾¾ÞÞÞîîî®®®WHˆP¬P²P¸P¾ƒæ—U¦Ÿ`2}€ fhäóŒÜÄO¦è{*’¡€Ñ,*‹Ê¢²ùRéÇŸ&%¥x2}@ß ÂÓÐÀh•EeU6dú‰/žLÐ÷¤°Ã440šEeQY•M'™~â‹'Óô½)ì0 ŒfQYTVeÓI¦ŸøâÉô}/@ ;LC#£YT•UÙtòŒÜÄO¦è{Rx¢bh`4‹Ê¢²*›N©Îô+£x2}€Šdhä“é'¾x2}€Š”†¡Ñ=÷Ü3sæÌÌë 6477‡ñíÈïF؇{ï½·Âþ~†±%³Ë¤åŽ;™~â‹'Ó¨H?4êïï3fÌŽ;2o§NúÃþð¸ìÉöíÛÃþTÒßÏpeú9eÒòÇL?ñÅ“éT¤!Fý§ÚÚÚiÓ¦ýâ¿ÈŸã7îÞ½;úʶmÛ®»îººººêêê©S§®]»öXQNÊü裆݈ÞÖÔÔŒXÛæçÝ3fÌX¹råqÜ‘ßD‰ûS¦¤·<@ðŒÜÄO¦è{*ÒÑdú™‡Z¼xñ¤I“ræ÷õõÍ;wÆŒ™·Û·ooiiY±bEæré-[¶Ìš5ëXQNž{ã7>õÔSÅ>=¦ò·s“ë!ìÀÈo¢Ä}È)SÒ[ŒfQY•Ee+@ª3ýøÓ¤¤O¦è{RØa–’égTWWçÏß¿t]üœ9s–/_^Ê.Ý}÷Ý õõõË–-ËÌÙ¹sçŒ3jkkÃV¦OŸ]ûßÑÑ1qâ݉ÖÖÖï~÷»G²þ/h7ZZZzzz¢}Ëþ´Øjç+V¬_ ó'Ož¼uëÖÌüÌ›7¯î-áEx[°)2oó÷$صkWXmÁ£.˜}‡™K–,ill Ç0ÚbÌž„Fkjjªªª*¸n7z[lÓýýýám˜>jkk‹–/ؘ÷á+_ùÊèÑ£ÃfÍšµoß¾ü2‹iy0šEeQYT¶ ™~â‹'Óô½)ì0K¼Nÿž{î™¶fΙ3'3̘1ëׯ/vïÞ½`Á‚ü8’õò?-¶Ú°Ì¬Y³Â>„–,Yò¡ý±‰-ZtÍ5×ô½åºë® o n1;Ï?Àb7ÿ)–é‡ E[Œ$fOfÏžå¥\#³ó7^dÏ–iÌìõãß{Ú6,vë­·FUË/Ó°øHÞv ŒfQY•Ee+€L?ñÅ“éú^€v˜¥ÜO?hiiyíµ×¢ù™9÷ÞÉ m‹;vlüƒR>\__Ÿy¶»lÙ²ìë» &Ó/¶Ú°Ltx˜­¡©©)ڷ𢱱±à:‡–éÖ5lØbØ÷dÏž=lLƒdï|ÁM‡eÏ/¸‰œÆÌþ¨¹¹9úúþýûŠ•iØ\¦Ñ,*‹Ê¢²ƒ"ÓO|ñdú€¾ …f)×éïڵ듟üä3Ï<ÍϨ©©É~Fn‰×éLc;;;/¹ä’ÚÚÚÌš«ªª2ó·nÝ:sæÌ°æsÏ=·£££àZ[[snê2àjKÌè ÞnèHl¦ödPw€)q‹îIé›òÁ–Þ˜Ù¢ÅbÊ4,>Ø–£YT•Ee=#7ñÅ“éú^€ž¨”x?ýžžž±cÇf®m/–#Ï™3'º?~Œ‚×é777?û쳇:òÖ­~ò7±aÆèRîügä†C(¸ÛÅV[, ›È¾x<ºH¼ªªêðáÙ×ûöí‹ÉôüñA=©5ærõ‚{2„L?fçu~‰‹þ3O|™†÷ÀÛò`4‹Ê¢²¨lª3ýÊ(žL " ×3ro¹å–o}ë[GŠçÈÛ·ooii Ëdbߟýìgá+ùkËÜO÷îÝÙ÷Ó=zttßüð­há9sædþÀ† Â2™™uuu»víŠÖ¼jժ뮻®àn[m±€8º•ü¾}û®¹æšèfîS¦LYºtéáÇÃzfÍš-Ÿ³'ÁŒ3V®\Y°}bî§¿ï-ojŸ³'9+Éß|Åv>~Ó™ùaÓÑòÅ3g/^|ÕUWíܹ3¼þõ¯Ýv?¿LÃ{à1-@A2ýÄO¦P‘†+Óá…¦Nz$öÚðm۶͘1£¶¶¶ªªjÊ”)k×®-¸¶E‹ÕÕÕÕ××G×õwttœuÖYá[---Ë—/ûvÞyçUWWOœ81º÷Îý÷ß_SS“ýßÆÆÆíÛ·ço¨Øj‹eúX°`AÍ[æÏŸßßߟ™¿uëÖÉ“'gÖóÀDËçìI؇°'Ñ·âÛ3šÙÖÖ6zôè°ž[o½5ún±=ÉYIÎTlç‹m:¼oC›‡ÂÑòÅ3/^ÜÚÚ–œ4iRtm~~™†ñÀã["û_sû·Ÿ>ðÆMGdúP<™>@EJÃÐhñâÅåpß•°mmmƒúJ)7Ï9FF~ÓÙeÞ­¡åH­žŽÎ0þy²îÒ7_ü­AÊÉô_<™>@E24*g©ÊôËgë¤ÜoûA…©ý=WýªmåÁÞ½Ú„tòŒÜÄO¦è{*’¡Q9«©©ÆUå™MߣYT–!صþ'kNº8 „ÖžñÉ55ÿø†/Ç_¶¯²~³)Õ™~üiRRŠ'Óô½)ì0 ŒfQÙtŠbý? ‡j.þߣÿ*æ²}•õ›­H2ýÄO¦è{RØafÎcM&“Éd2™LÑôÚϸŒA¦lA¦¯ö2}™>àß»†F:gT–²kýOž8å’ÕUS3ý'tí˜Ëú•ÿ'sþ€#( è7[dú‰/žLÐ÷¤°Ã440šEeSèþ‰SWW}xõI?ÉgßxîŸþãп—>‚Ò†~³À3r_<™> ïHቊ¡€Ñ,*›6¿}ì™kó³/Ì쪽½½³³³««K{úÍ&Wª3ýÊ(žL "Dz::ÿ0þ9é#9æA‘B2ýÄO¦P‘ 2ö¿þæ¯ÚVþ[W‘éW@ñdúÉÐÀ òÉô_<™>€3RþöîHªúÎþÄÉdÇÙÉ\‘ø‚(¢ñ5Q“ø_ˆ O#Ëe­Xˆh ŽæúdÕ›õ²°S,EMñxã— Yt0’½Ô¬+Þ;¹Ë>+!ƃÃ*Ã¥¦( ¦(ÊçÎæ€o¤8‚¢DÈô ¾x2}ßHpE‰pÜ‚/žLÐ{|#ÀÑ,*‹#(ïÙQÒ™~î7y¡O¦è½¾‘àh•%Ÿ#¨æææÖÖÖöövûÊ{¶pÉô ¾x2}@ï(Ío¤Í¢²ô,eî!>mݺµ££Ã¾òž-\2ý‚/žLÐ{J°a:4p4‹Ê’Csss8XzbÌ7ÖþQ˜޶lÙŸ:;;í"ïÙÂ%Ó/øâÉô½ ¦C#G³¨,9´¶¶†#¥¿ø“?ü0?¦lݺ5;utttuuÙEÞ³…Ë5r ¾x2}@ï(Á/*Í¢²äÐÞÞ“~þм¶? S::::;;»ºº:dyÏ®’Îô‹£x2}€¢äÐèI¦_ðÅ“é%‡F@O2ý‚/žL (94z’é|ñdúEÉ¡Гkä|ñdú€Þ P”8šEeQYT¶§’ÎôsM*”âÉô½ ¦C#G³¨,*«²¥I¦_ðÅ“éz/@ 6L‡FŽfQYTVeK“L¿à‹'Óô^€l˜Í¢²¨¬Ê–&™~ÁO¦è½%Ø08šEeQY•-M®‘[ðÅ“éz/@ ~Qqhàh–©løÐ7Žû°kc«÷lá*éL¿8Š'Ó(J€èCÿðáÇ †ã8„Õº3¿Ö±á ï¯%Ó/øâÉôŠõ ¼C#@¦o8™þ®¿J¬_¸dú_<™>@±~whÈô '"Óß}ñ‘µC'ürÉOÛÛÛ;;;:äíV(dú_<™>@±~whÈô '(ÓÃÿYóðšÓ'üãâåáR¬_@\#·à‹'Óô^€býïÐÀÑ,*+Ó7œ¸L? o­|pÍé×´üõO +ÖwÜÒÍôsM*”âÉô½ ¦C#G³”Heeú†šéõç¬rMKãßP¬_âÝX¦_ðÅ“éz/@ 6L‡FŽf)‘ÊG¦_VV&IOl¦†ß¼ð`aÅú2}™~aO¦è½%Ø08š¥D*›´L¿é¼L?á™þ~­¿ªb}™¾L¿°‹'Óô^€l˜ÍR"•=é™þq‰ãOn¦ßµ—U^þ©ªªO_|qíܹ—ïÜù@˜þ»ßÍ>üôƒÒæïêú~}ý{ö@Qrh|’%Ó/++kl¼¾¶ö´ÊÊòéÓÇîß?7ž¾xñ7êꆔ—*Ü Óï»ïÒ!C*ÂFâÙvì¸Ò¤ó«ª>]QqÊM7ÚµkN¼ä††+ª«+‡ýLXNüsõH¼Š}û6¬ê£¾?*L MùÁ®¬©95lØ]w ÓScè ê_xabü¨>˜=|øéñ<Ù¶¡çóʶ–ŒÏ«çSÈöðܹù¼y®#¿þõŸwÞŸ¤ÞÕÝýèYgU·µÝ×óaK2.<Ÿ2¥-*º™ÿÓé¹ÓòOü›››“ë—2™~ÁO¦P¬_àÙ2ýo}ë¼>ú^ÂHCÃñô{î¹(NuçνüöÛÏg 7£écÆ {ãïtw?zð`CxlU‡á‡?¼ê¦›FíÞýà¤.³g²êµ×î¾è¢ÏÅš5ëK© ɽ ©Ï+ÛZ²=¯´§íá¹3ý°‡ýL4¶ðïþnr|׊·Ýyçè´†…ÿèG×\rÉç3.<Ÿ2eÜóù?´µô)Ó_¹r¥X?Édú_<™>@±~whdËô£óÀDgx¯«Oß½ûÁx¶0=ÜÏV[{ZÏEuw?GÕÇŸÏŸ-ÝŽn¾óÎwëëψ'ž}ö™;vÜFFŒoXW×÷««+ÓrùåÃúÓÛÃH[Û}a <’¶ºlÛú¼²­%ÛóJ{ ù<<ãùmâÝ¿ñÆw®¼ò ñôK.ùü?ýÓŸ¦žˆ?ž`¼¢O'[™òÌôsìóÔµô)Ó_¶lÙªU«^}õÕ-[¶´··wuuy'&ŠL¿à‹'Ó(Ö/ð€l™~Ƭ9[ Ü3’þêWÿcUÕ§ã+Áæ²³-ó›ßüâ‹/N #áïŒ÷L´3.ùÕWÿ¯Ñ£?F¦OŸZ§×0½ç6d\KžÏ+ÛÃsoÆîݦ¦ÿW]5"¬.úÏ&Ô÷|àÎÜpÃÙ©?çïu—æYÍ<ŸN¿O»^r©Iño~ó›ÎÎNïÄDqÜ‚/žLÐ{Šõ ¼C#G³¨l_§Ÿ:[˜ž:[üð#†¾ôÒ6D§ˆ‰•ÿïô£,ûª«FDéö¯ýgñ’SÏΟq!—\òù§žúêYgUww?ÚsÎ|¶!ÛZ²=¯<ž;Óúé ñÉ|¢œ¸é¦QÑyx6mº+㣠ÄçÇ϶ðle*/ÿT¼‹öí{¸¯Og ™þÊ•+—-[gË–-Þ³‰RÒ™~î¯I…R<™> ÷”`Ãthàh–©lŽóéïÛ÷pÒΧŸv±Ùè´ûa¶Ûo?7>Q{MÍ©¿øÅ·ÃÈ®]sî¾ûÂøQѹìÃÄÔsÙR±sç“â‹/®}öÙ›o¸áìÔ ÉÞzë9ÑyxÞyç»Ïh¿víáæO~rKÆ@9ã6¤=¯lkÉö¼ÒžB¶‡g Ä»»ýÍoî}üñ¯¤&ïñ?N¼øâ¤´3æ§mjØŒgž¹1wÚž­L_úRÝ¢E_žÎ]wéëÓH¦¿zõêeË–=ÿüóëÖ­{ýõ×ÛÛÛ½gE¦_ðÅ“éz/@ 6L‡FŽf)‘ÊfËô,¸®¦æÔÊÊò™3ÇÅg¥O‹q÷ïŸ;{öeaž0Ìšõ¥x¶W^™röÙg–—ª¾þŒ¦¦R5wîåC†T ú™øÄ8 ~-<<ãÄŸ}öæp×k¯Ý¸ŸuVuXøÅ×¾ðÂÄžzñÅIÑéw² =·¡g?dúkÖ¬É{Ïžt2ý‚/žLÐ{J°a:4p4K‰T6ŸóéÖ0iÒùk×ÞQ¸Û_ôƒL?ùdú_<™> ÷”`Ãthàh–©l‘eú?ýéí]ô9¹¹L_7×È-øâÉô½ Ã‡FŽf)‘ÊfÌô++Ë 1,›]_Æ?ýÓŸÊÍeúºñ@”t¦_Å“é%‡FÀ'Y2}ƒ¡Ä3ý'Ó/øâÉôŠõ ¼C#@¦oé“F¦_ðÅ“éëx‡F€Lß Ó'L¿à‹'Ó(Ö/ð™¾A¦O×È-øâÉô½ X¿À;4p4‹Ê†}ƒa×ÈM¸’ÎôsM*”âÉô½ ¦C#G³”ZeÃç~øô_±bÅòåË×§Õ«íƒþIx¦_âÝX¦_ðÅ“éz/@ 6L‡FŽf)µÊ¶¶¶677¯Zµj9y[Üð„ÐoáÅ^rá…×ÞÞî=›(2ý‚/žLÐ{J°a:4p4K©UvëÖ­á£ýúõ«V­ZA~ž¾å;¡ÂË,¼ØÂK.¼ð:::¼gE¦_ðÅ“éz/@ 6L‡FŽf)µÊ†ý-[¶„Oÿæææµäç‰1ß°ú'¼Ì‹-¼ä ¯³³Ó{6Q\#·à‹'Óô^€ 8š¥Ô*ÛÑÑ>÷·nÝÚÚÚÚB~–Ý÷_ì„þ /³ðb /¹ðÂëêêòžM”’Îô‹£x2}€¢äÐHÕÕÕÕÙÙÙÑÑÑÞÞÞ'Xx™…[xÉ…Þ¡C‡¼E¦_ðÅ“é%‡F@O2ý‚/žL (94z’é|ñdúEÉ¡Гkä|ñdú±Cûvüí«á¯76è½p4‹Ê¢²*[”J:ÓÏý5©PŠ'Ó:ßøUóˆ[à »6¶jj ÷”ø¡ŽfQYTVe‹˜L¿à‹WÊ™þ¡Î}ï.zaã…SÃ]axoÉÏt4Ð{JöÐG³¨,*«²¥@¦_ðÅ+ÍL¿ó_ýÓŒ½xÆuÇ|{]õõkN¹|ÇO¡Þ P: S¦àh•EeU¶4Éô ¾x¥•éÿdÅ+÷ÿèås'½ôn~uô”5Ÿ¾2L\S~¥@ô^€Rk˜2}G³¨,*«²¥É5r ¾x%•éGÃφ^ ƒÁ`0”ìÐÜÜÜÚÚÚÞÞîK€$•EeUV¦_™~q¯Ô~§ÿòíþlȄ՟ú÷¯²/ž>áÅ¡×vlxÃë(>kZµjÕŠc…)ëׯî[·níèè°£ tÈô ¾x¥y>ý/½þ?.›¾ú”Ëÿ0ñÔ¯Šõ€"ÖÜܼöXaJ8(Ú²eË®3ÔÙi@éé|ñJ3Ó¦êÜ÷ÿÎZ°nèuÑoöß_¶Á«(>­­­-Ç S¶nÝŠ:::ºººì"(2ý‚/^)gú±/½þw5_ 3ìÚØê…™ööö¶c…)]]]‡²‹ t¸FnÁO¦;Ô¹¯­iíï z/€þ €nŒÊ¢²*[”J:ÓÏ Jñdú€Þ   £²¨¬Ê–™~ÁïÌ3ÏœP,î,û“م{¿üå/_vÙe—_~ù ßév€þ  £²¨,*Û?gžy¦L¿°3}JŠL¿ -^¼øÚ?ºä’KB/¸à‚Ñ£G_X°î,ûÂSÙ…{Ã<çu!PÈït;@ÿÐQYT•ˆ3f¸Fnakmmmnn^µjÕòBÖëùôçÎûè£Î›7o9P°V~k¶ èƨ,*‹ÊܪU«š››[[[ÛÛÛK"Ó/&[·nmiiY¿~}¨âŠ‚Õk¦ÿÀ<ôÐCÿõ¿þ×%lÕªUëׯoiiÙºukGG‡L¿À´µµmÙ²%Ô¯¹¹ymÁê5Ó¿ÿþû|ðÁ¿ø‹¿X Pš››[ZZ¶lÙÒÖÖÖÙÙ)Ó/0¡r[·nmmmm)XùüNîܹý×ÝPÂZ[[·nÝÚÖÖÖÑÑÑÕÕ%Ó/0¡f¡xííím«×Lÿ‘GyòÉ'W¬XÑPÂÚÛÛ;:::;;»ºº:乜½fúO>ùäâÅ‹7mÚd_Þ   £²¨¬Ê’QÑfú¹d’V™>è½ú'º1*‹Ê¢²½’铈ŠÈôAïÐ?ÐQYT•í•LŸDTD¦z/€þ €nŒÊ¢²¨l¯dú$¢"2}Ð{ôOtcT•Ee{å¹ÿ7 CžCdž7|ðŽ]ÐU•Ee Z™]PÐV—?|ø1ƒÁ`0ô:„†ùâéÄú@A“é6™¾Á`0äŸéïúûëÎüÚ®­>>€%Ó/l2}ƒÁ`È?ÓÿÏšÇvæõ¿üï/´··wvv:tÈG P@dú…M¦o0 }ÊôÃð«¬9}Â?.^ÞÖÖ&Ö ‹kä6™¾Á`0ô5ÓÃÖçç®rMË_ÿD¬8v@7VYT•-,E›é¯._ õ“é C?2ý0¼¹âûb}À±+º±Ê¢²¨lÁ‘éüÓ”Ó衬¬¬×)'zÇw±Ç}ùYà‰Þ™C¶Lÿh¬ÿðbýÆ¿ëŽ]ÐU•Ee …L¿àŸ¦œnÐ2ýþeâýȬK3ÓÌpÿDïaC¡dúÿÿ¯õÅú€cWtc•EeQÙ!Ó/ø§)§+ÐßéçXHò3ý´%”ìïô…øEé‹õÇ®èÆ*‹Ê¢²…Å5r þ%.§“éËôeú†<3ýÜCkk«Xpì  £²¨,*›pevAA“韬sï46^_[{Zeeùôéc÷oßÃÆU}ôÑ÷âG…)a†0%õQ‹£®nHyù§ÊRd\ã³ÏÞ\_FEÅ)—\òù7ßœMß±ãþI“ίªút˜~ÓM£víš“{þ ›62lÉ‚×eÌôðƒ+kjN óÜuט°ÍÑÄW^™rÑEŸ Ï:«zéÒoöܼ´Ï¶Ù–Ÿm÷æØù,*ZHSÓ Ñ–Œ3ìõ×ïÛöÙgFöë_ÿYÚJs좸XÙö|Æý„—Ä}÷]:dHEÂH¸ÙëŽ2$!ñonnë 'Ó/l2ý“•éë[ç}ôÑ÷ÂF®çÌùO \?*Œ?üðø´GÝsÏEqûwúwÝ5f÷î»»ml¼þË_þÑô1c†½ñÆwÂăÂJ§O›{þ0Oêvö|"Oãs™;÷ò°µñž7sï(CB2ý•+WŠõ€„“é6™þÉÊôßV4þ»ßͪ«FÞyç»õõgÄ:ûì3wì¸?íQ»w?˜ç¹wâ_vww?ZQqJÏyÂô¡C?“{þáÃOOÝΞOdĈ¡ñ ]]߯®®ŒÆÃY¼ø|0;ÿsïdÜ€lËϱ{³íÆ|ÿØ?Þ’´›ñ†Å+ µ‹–ý?[±rìùŒÏ%,6ìðxÏ×Öž–e '1Ó_¶lÙªU«^}õÕ-[¶´··wuuù”’F¦_Ødú'+ÓO!Nf¿ùÍ/¾øâ¤0þΘqqîGå>ýøæo|ç«_ýUUŸŽÎ÷&Çü½N/;V¼À7ßœy磫«+Ï;ïO^yeJ_ϧßëòsoU¶Ý˜qQÙÎ~“ÿÈg× pÏ÷ü·§ãOf¦¿|ùò+V¬]»¶¥¥å7¿ùMgg§O i\#·°Éô“ó;ý0¼öÚÝW]5"Œ„¿=OÝ>ðLĈ¡/½tçÁƒ a<üíuÉùüN?>)Æ!<£øÙõ#Óïuù÷OÆÝ˜Ï¢ú‘é§þN?ì«l»´¯{>,6uϧþN_¦Ÿðsï,[¶,:Ï–-[:::%Ž]tcT•Ee“¦h3ýÕeãK¡~2ý“x>ý}ûC|>ýh¸øâÚgŸ½ù†Îîõ·ÛC†TìÜù@Ÿ‚éššSãÓÜß}÷…½&ËñÉâÃpûíçöœÞ¼ ·ÞzNj¿óÎwãÓć‘èÔ1¯½vwXiÏ-LÛølmù½þ¶½çnÌgQýÈôÃ.š4éühÝyçèl»4۞϶â+D{>õ|ú2ý$gú«W¯^¶lÙóÏ?¿nݺ×_½½½ÝQàØ@7FeQYT6idúÿ4%q'%Ó_°àºššS++ËgÎwàÀ#ñÌÏ>{s˜øÚkw÷šé/\øµ0gÆT7[òûÊ+SÎ>ûÌòòOÕןÑÔtC¯™~ذ°y§„M œqþyó&œuVuXæÅ×¾ðÂÄhb=ú³á]ô¹ŒçÞIÛøQuÆå÷šé÷Üù,ª™þþýsg̸8ÞEÙN’“mÏgÛa±³g_î ìY_Š_!2ý„gúkÖ¬I=ýN[[›£À±+€nŒÊ¢²¨lÒÈô þiJâ †ã2´µÝwÖYÕöƒL_¦8vÐQYT•M2™~Á?MIœÁ0aîÜË÷í{xÏž‡RO’céËôÇ®º1*‹Ê¢²Éä¹ÿ—Ä ššn¨­=­ªêÓÓ§Ý¿®"Ó—éŽ]tcT•Ee“¬Ì.(h2}ƒÁ`é¥C¦_ØdúƒÁ ÓJ‡L¿°Éô ƒA¦”™~a“é ƒL(®‘[ØV—7 Ãqdú€cWÝØNPYT•M¾¢ÍôW—/©B¶´´¬]»vÅŠË—/_“L«Wç¾wuPàod™>àØ@7FeQYT6ádúE¢µµµ¹¹yÕªUË“jqùïý«¿ú«… .Y²d9P°r¿Ó Bh¤¡†¦ÚÞÞî(pì  £²¨,*›42ý"±uëÖ–––õëׯZµjE"=}Ë=¹ï}ôÑGøÃþÕ_ýÕ  `å~§'_h¡¡‘†všjGG‡£À±+€nŒÊ¢²¨lÒÈô‹D[[Û–-[ZZZš››×&Òc¾‘ûÞûï¿îܹóçÏ_ ¬Üïôä -44ÒÐNCSíììt”8vÐQYT•M×È-mmm[·nmmmmI¤¿ø“ñ¹ï½ï¾ûæÎ»hÑ¢ `-»ï¿ôö‡ih§¡©vuu9J»èƨ,*‹Ê&M™]Pººº:;;;::ÚÛÛÛiqÅ—rßûÐC=ñÄ+V¬h8IB 4´ÓÐT:äÃH™>ƒ$÷ÿš ÷>ùä“‹/Þ´i“}‘LŸA"Ó ™>ƒD¦0@®‘Ë ‘éC)Ð{ôOÝ•EeQÙªh3ýÜ 2I«ˆLô^ýÝ•EeQÙ^ÉôIDEdú ÷èŸèƨ,*‹ÊöJ¦O"*"Ó½@ÿ@7FeQYT¶W2ýôGzvml=î‘éƒÏ]ýÝ•EeQÙ^¹Fnú+æðáÇ 9†°‹ÖùµŽ oßw£LJ¹÷ èƨ,*‹Êæ©Ì.H%ÓÏ'Óÿ·ÿñÿô#Ö—é Lÿ2ý|2ýð÷WûÔÚ¡×þrÉOÛÛÛ;;;:”ϾÍ}¯L 7™þ1dúyfúaøççþï5§OøÇÅËÛÚÚò‰õeú$Ó?†L?ÿL? ¿üï®rMË_ÿ$ŸX_¦0@®‘{ ™~Ÿ2ý0´>Óð‡X¿ñozõeúP \Ç@ÿÐQYT•=¡Š6ÓÏ çxTÏ»¬¬L”Ÿ-ÓÿC¬ÿã¼b}™>”‚þõ^ôOÝ•EeQÙ<ÉôÓ•#Ó´pÿ­è¸,6ã.jýñ£½Æú2}ð¹ €þ  £²¨,*;@2ýôGÓïôOÄ–g;=Ñ¿ÿZ?û¹õeúàsý@7FeQYTv€dúé’é÷šéçZ[[3Æú2}ð¹ €þ  £²¨,*;@®‘›þŠÉ}î}û6¬ê£¾ß¦ÔÖžMùÁ®¬©9µ²²ü®»Æ„éÕÔtC}ý§Œ3ìõ×ï^ºô›gŸ}f¸yÉ%Ÿÿõ¯ÿ,m<2}úذÀ°Š ®KÝ’Å‹¿QW7¤¼üSáæŽ÷Ošt~UÕ§ÃrnºiÔ®]s¢ybi‹Ý¿î}÷]:dHEÂH¸Ïðì³7G›¶çÍ7gö#ñonnÎëËô¡¸Ž €þ  £²¨,*{B•Ù©ò9Ÿþœ9ÿiÁ‚ëâ{ÃøÃÿáQOÀÉô‘O¦ÿÎ;ß­¯?#¾÷ì³Ïܱãþ02bÄÐ÷ߟMìêú~uueÆEÅA|w÷£i7+*NI[c]ÝháÑñS·d÷î3¦êa9C‡~&w¦û»ßýû¦†‘ÚÚÓââßì§nOŸ2ýeË–­ZµêÕW_ݲeK{{{WWW¼osïy™>@n2ýcä“é‡á›ßüâ‹/N #áïŒÇ÷¦ŠÎŠ“û÷ÙnöÉ=ý7¾óÕ¯þǪªO§­:χ÷ü·„~ŸŽ?ìÀåË—¯X±bíÚµ---¿ùÍo:;;ã}›{ÏËôr“é#ÏLÿµ×î¾êªa$üO‚?bÄÐèDöù_´¶×Ì=õwúï¿?+[(VýÒKw<ØÆÃß^ÿI ,6þ/i¿Óx¦¿råÊeË–EgàÙ²eKGGG¼osïy™>@n®‘{Œ<3ý0\|qí³ÏÞ|à gÇSæÍ›pë­çDü;ï|7>©ý@2ý††+&M:ß¾‡Ãp磳…õ55§þâß#»v͹ûî ã{‡ ©Ø¹óžËNÓÿÑGß ‹½ýösSϧ?ðLõêÕË–-{þùç×­[÷ú믷··Çû6÷ž—éCÉö^ôOÝ•EeQÙ<m¦Ÿ;AÎñ¨<3ýgŸ½¹²²üµ×îNsÞ¼ gU]^þ©‹/®}á…‰Ïô÷ïŸ;cÆÅ§ÔÔœº`ÁuÙN’óÊ+SÎ>ṵ̂Þúú3ššnˆï]¸ðka#3.vöìËÂ]a˜5ëKwôOÝ•EeQÙM¦Ÿþ¨¤¥äsç^¾oßÃ{ö<”z’™>às@ÿ@7FeU•-A2ýôG%-%ojº¡¶ö´ªªOOŸ>vÿþ¹2}Àç.€þ €nŒÊ¢²*[²\#7ý“ðÐ\¦_ï@ÿÐQYT•ÍS™]*™~_/TÛ ÛÊô ‚Lÿ2}™>@bÉô!Ó—é$–Lÿ2}™>@b¹Fî12fú¯¼2墋>WYY~ÖYÕK—~3ctžz³¡áŠêêÊ¡C?³xñ7rOüÁ®¬©95,ù®»ÆìÛ÷p4ñÀG¦O&ÖÖž¶`Áu©Kîëü2}  z/ú'€nŒÊ¢²¨lžŠ6ÓÏ çxTÏœzذª_üâÛad×®9³g_–;Óÿ᯺é¦Q»w?xàÀ# Wä˜øÄWß~û¹aâÁƒ 3gŽ‹—føÖ·Îûè£ï…!ŒÄKîëü2} Pz/ú'€nŒÊ¢²¨lždúéê™S×ן±xñ7>ø`vŽSÜÄ7‡?ýw¿›•¶„ŒGŒúþûÿ>±«ëûÕÕ•ñÌñôð¨xÉ}_¦øÜÐ?ÐQY•Ee‹ŒL?ýQ=sê7ßœy磫«+Ï;ïO^yeJîL?c¤žmbªòòOõºä>Í/Ó|îèŸèƨ¬Ê¢²EF¦Ÿþ¨õk¯Ý]W7$//ÿTw÷£Ñø¾}÷ïwú»vÍé¹–¿ÓïÓü2}Àç.€þ €nŒÊª,*[d\#7ýÓ3§ž>}l”È¿öÚÝ55§F¿ô¥ºE‹¾ÞÝýè®]sîºkLÚùôÃÄžçÓO›8oÞ„[o=gÇŽûÃø;ï|7¬%õüøûö=†Ûo?7^r_ç—é…Ò{Ð?tcT•EeóTf¤Ê˜é¿ðÂÄÑ£?[QqÊE}.>÷Λoμä’Ï——ª¾þŒ%KnHMÒçν|ÈŠ¡C?³xñ7rOœ7oÂYgU‡…\|qmXK4ñÀGfÎVWSsê‚×¥.¹¯óËôЉLÿ¹Ï½céœD2ýcÈôeú‰%Ó?†L_¦X®‘{ ™¾LüÞ €þ  £²¨,*›§¢Íôs'È9%——éƒÜ{Ð?tcT•Eeó$ÓO”\^¦øÜÐ?tcT•Ee“I¦Ÿþ(¹¼Lð¹  èÆ¨,*‹Ê&“L?ýQ†2}ð¹ €þ  £²¨,*{‚¸Fnf---k×®]±bÅòåË×ÐG2}(Y®c èƨ,*‹ÊžPevAF­­­ÍÍÍ«V­ZN¿„]v`ØíííÑ.•é L?³­[·¶´´¬_¿~ÕªU+裰Ó® ;0ìÆŽŽŽh—ÊôH¦ŸY[[Û–-[ZZZš››×ÒGa§…]v`ØÑ.•é L?³ŽŽŽ¶¶¶­[·¶¶¶¶ÐGa§…]v`Ø]]]Ñ.•é käfÖÕÕÕÙÙÙÑÑÑÞÞÞF…v]Øa7:t(Ú¥2}(®c èƨ,*‹ÊžPE›éçNIZEdú ÷èŸèƨ,*‹ÊöJ¦O"*"Ó½@ÿ@7FeQYT¶W2}Q™>è½ú'º1*‹Ê¢²½’铈ŠÈôAïÐ?ÐQYT•í•kä’ˆw£Lô^ýÝ•EeQÙ^•Ù ™>ÀÉô$2}€’é3Hdú$Óg$?Ó/+óvÍ5r$”é~¸‚Öè_)Ð{ôOtcTVeQÙ"S´‘_UäDgúù¤ÛE€ ñÑ{ôOtcTVeQÙâ&Ó'‘éÚÓ½@ÿ@7VYT•-\2}Q‘™~YYÙÂ… kkk+++g̘qðàÁhz™5kÖ£ÂH<}ãÆcÇŽ 39ò¹çž‹–ËõfH9÷Îþýû‡ ÖÕÕߦ„mˆ¦<þøã555aÓ¦M Ós/sÉ’%õõõ^xáÿüŸÿ3lÒ¨Q£ÂÍK/½ôí·ßN[uwwwxŽaÉa]©›ÔÔÔTWWW^^nîܹsòäÉUUUa97ß|óž={2>Íx$Û¾ 3,]º4Ú¼°=Û¶móBEïÐ?tcT•Ee“L¦O"*’;Ó¿ãŽ;ºŽ #=öX4½¡¡aâĉñôp3š>lذh!{öì™3gN¼Þß ÇFá=ôPccc|oŸ;wnyê©§Âz÷îÝ{øðá{ï½7^E¶eN:u÷îÝGŽ Ï®¶¶6õæW¾ò•´U‡gžËþýûÓš=þ÷ƒ /¼°µµ5,$lCxÈŒ32>Íøf¶}f˜6mZx.aQ .?Þ»½@ÿÐQYT•M4×È%ïÆÜ™þ|ïØ±£®®.#áf<½¶¶6¯¯¯ojjÚµkWÚBz3›é¿÷Þ{aQñ½£FÚ¹sg1bD¼=¨®®Î½Ì8ˆ?räHÚÍŠŠŠ´U‡'­å“£?ÆOݤ½{÷f\EXÎСC3>ÍÔÅfÜWa†ø7û©Ûz/€þ  £²¨,*›LÎ¾Í H¦Ÿz³gž6}Û¶mS¦L©®®>ÿüó7nܘqæÌo†§¬¹å–[Ö¯_FÂß™3gÆ÷¦ŠN†Óë2sßì9’{zkkë5×\SUU•¶ y><Û>t:~H8ƒäDüN?uzüÛóØæÍ›ã™û—é‡%\}õÕa$üÏ}?bĈèüõy½Áú˜é§þN?<»l¡|؆ 6>|8Œ‡¿½þ“@¶}%Ó€Â"Âc ð|úûJ=Ÿ~têù®®®0}âĉñ9âg̘gfóæÍ555ÑÄ!C†ìÞ½»—7C¦d|ܸqK—.½ñÆã)óçÏ¿í¶Û¢äý½÷Þ‹ÏeŸ{™¹o¦žOòäÉÑ“2eJ¶°><¯øšßùÎwâ{ÓžfÚiú{î+™>ƒd ™~cccMMMeeå½÷ÞÛÝÝM?xðàœ9s*š={v<}íÚµ\pAEEÅØ±cãsï,Z´(Ì–;³Î /]º4 ÷èŸèƨ,*«²Ddú$¢"2}Ð{ôOtcT•Ee{å¹$âÝî=¹ƒÞ  èÆv‚Ê¢²¨lò•Ù Ž$ÿN_¦™>ƒD¦0@2}‰L`€dú ™>À¹F.ƒD¦¥@ïÐ?tcT•EeO¨¢Íô¥´…U™>è½ú'º1*‹Ê¢²½’铈ŠÈôAïÐ?ÐQYT•í•LŸDT¤×LÿPç¾ûjøëÕz/€þ €nŒÊ¢²*[²dú$¢"92ýÎ7~õRía†][½Z@ïÐ?ÐQYTVeK™kä’ˆwcÏLÿPç¾ßÎþåá·„»Âðþ² :è½ú'º1*‹Êªl‰+³ ùgúoüjó­s×~檟U^¦¯­¼ªcÃ'qÛB¦Ï é5ÓÿQÃYzÛ}k?ûõµG£ühøÙiלè@ÿ™>P dú ’^3ý“;(|2}I>¿Óÿ›;fý¬î¦ug^ÿ³!¢¨}íg®ÚùÒëöÀ'®‘Ë Éÿ|ú¿í_þ÷=O­­¼jýçnëƒÞ   £²¨¬Ê+ÚLßÙT «"©™~4åPç¾w½ðÊÙwD?Øßþ“—íCÐ{ôOtcT•UÙ'Ó'é™éÇ~ÿÚ¿4á–0î­v#è½ú'º1*‹Êªl)“铈ŠäÈô#‡:÷µ5­=ðáïíIÐ{ôOtcT•UÙ’%Ó'é5Óô^ýÝ•EeU×È%ïF™>è½ú'º1*‹Ê¢²½*³ 2}€’é3Hdú$ÓgÈôH¦Ï ‘é kä2HdúP ô^ý@7FeQYTö„*ÚL?w‚LÒ*"Ó½@ÿ@7FeQYT¶W2ý„š={öYÅeLÙÐ Ù…{?÷¹Ï 6¬®®î, Ö¸qã&PÚî,û‚ èƨlr,^¼Xú‡ÊÊôÕ~0œuÖYe0×^{­ô••é«ý fúA!ˆ^®#G޼–ÒvgÙìý@7Fe“àÌ3ϔ飲2ýBRè×Rˆ2ýÇ{ìc(Q¬ÿä“Oêª%ÎulôOÝ•Mˆ &w¦ï=«²%«Ì.H¦Ìô£z—Îö$íùÊô ˜D™þ%—\ÒÚÚºuëÖ¶¶¶ŽŽŽ®®.{ L?¡Nn¦Râf™þq_ZêéódúPR¢Lÿ /lnnniiÙ²eK[[[gg§=…N¦ŸP2}™þñZšLJP”é_pÁ«V­Z¿~}KKËÖ­[;::ì(t2ý„êG¦E·K–,9çœs*++‡þßþÛKáÏÿüÏGŒQQQî ãilll¬­­-//O»1qêÍx3FŽ6côèÑ7nÌg {.$Ûêþò/ÿ2<‘°„°œ¦¦¦>ÝÖøÅ/~1Ü[WW÷ä“O¦>©pWަɽ33>ýlõÊ8g¶åÿüç??~ü©§žrÍ5×¼üòËi¿ÇÏökýéÏš5K¦Éeú£G^¾|ùªU«š››[[[ÛÛÛí(t®‘›PýÎô'Nœøá‡vttLš4)ÜŒCó?ÿó?7¿ÿýï‡ñGy$ŒÇIqôÀ{ï½wÏž=y¦À½fú3gÎ K[¿~}9rd>[˜g¦ßØØÆo½õÖ°ý×½í¶Ûò¿÷G?úQ¿çž{öîÝûä“O†ñ§Ÿ~:ºkÁ‚ñ¶a$ÇNèugf|ú÷aÆ9s,¿®®.ÜܰaCß´iSx‚Iøþ;ï¼­E¦ëØèŸº1*›ñ¹w–/_¾bÅŠµk×¶´´´µµ©,*[èŠ6Ó_]6¾ ·¿ß™þoûÛèæ»ï¾n~ñ‹_Œn><Ü|ÿý÷Ãø¿þ뿆ñ0%õù§ÀáÞÊÊÊ™~XE|³¼¼<Ÿ-L[H¶å92Œ¿ýöÛÑÍ0’ÿ½õõõñìÙ³'5Fo[ɱzÝ™Ÿ~Æzeœ3ÇòO;í´ŠŠŠ×_}ïÞ½ 9÷N{{ûرc£é_û)ôÞ  èƨlÑ(úLß{Ö{¶dÉôªß™~Ú”ŠŠŠl¿|Ïv×qÉôó¹+Çfä¿ü~Ü›*íßú—’çÞÏý>QÆå755…=mùøñã£sïœÄL¿½½ýÒK/èãs@ÿÐQÙ¤‘飲ÅJ¦ŸPÇýwúÑ9[¢_…güiy_3ý8 ÿðÃû”éçù;ýlËOûAý[o½•ÿ½ÑïôãŸÆ§êÓïôóß™ýËôs,?Ø»wïßÿýßG' úìg?{3ýwÞy' ô/ºè">>wôOÝ•M™>*[¬dú ÕïLÒ¤I~øá¿ýÛ¿Ýyçáæ_þå_F÷>úè£áæ#<Æ¿ÿýï÷<|ê¢N;í´0åÝwßͶ®(_¿~ýÞ½{ÿôOÿ´O™~¶-ìÍg\~tÆü°°„ž'¾Ï}oXWt>ýÝ»w‡%¿üòË7ÝtSÚ;::ÂcsŸO?ÿÙ¿L?ÇòÃNû_ÿë…‘þçÓGŒÑkÉNP¦ßÞÞ~ÞyçE~gg§fŠÏ]ý@7FeE¦Ê+×ÈM¨~gúK–,9ï¼óÊËËëêêâ¸<òÈ#D?>|xœgÌ|O?ýôYðÆGÖ2räȰÆ>eúÙ¶0uÎË¢ùð½a9Ï<óLÚ9ësßÛÔÔtÑE…)•••7ÝtS|îšèa“R˜# Ïsgö/ÓϱüuëÖ]sÍ5•G}å+_Ù´iSŽ’õ<×Ðq?åΈ#üBŸbê½ú'€nŒÊ ×ÈEe‹U™]LÇå|úIs"¶pÛ¶ma™çœsN?îeàþ{ï½çÝ Tô™>”,™~BÉôs¸óÎ;ùË_Fg½¿é¦›Â2Ÿyæ™<ïåxú£G{Ø[’I¦ÅJ¦ŸPýÈô£ó±$9>^[øÓŸþôK_úRXÔ©§ž>ŸÂÍüïå¸ú—\r‰sè@’Éô¡XÉô*Êô£X’àˆ^“çœsŽ@N¦ÅÊ5r*Îô!QFŽéúqïÐ?tcT¶h¸F.*[¬Š6Ó_]6¾ ·ܸqQ~úd±¸³ì ¹ï2eÊôéÓçÌ™38Ûvï =÷ÿüŸÿógœ‘{×_ý˜1c¢ñ0òµ¯}-™uloo×7)âÞ  èƨlÑ(úLß{Ö{¶dÉôÝv¯½öÚ©H¸w?ZÂî´U\qÅÿøÿ˜cÎîîîaÃ†íØ±#º¹}ûöÚÚÚ0Q‡BïÐ?ÐQÙþ‘飲ÅJ¦Ÿè¶+Ó?/ý²Á{ñWVVæžáù矟:ujê”É“'¯\¹R‡BïÐ?ÐQÙþ‘飲ÅJ¦Ÿè¶+Ó?/ýAÌô{]×Ô©S×­[—:%ì´”ô^ýݕ͟L•-V®‘›è¶+ÓÏð’=6o†‘¥K—Ö××WTT\zé¥Û¶m‹§/\¸°¶¶¶²²rÆŒìùÀŒ ìîîó‡G…Ç666fËå÷ïß?lذ®®®Ô)á!aJôÔ Ì†›7n;vlXìÈ‘#Ÿ{î¹è!a³wíÚ•ºØÝ»w‡‰:z/€þ €nŒÊök䢲ŪÌ.HrÛ•égxÉfÏô§M›¶wïÞ#GŽ,\¸püøññô;ë¨0òØcõ|`Ɔ9Ãüû÷ïœwîÜl«6lئM›ÂÈž={æÌ™M¬¨¨è¹Ø^ÏØMÑgúP²dú‰n»2ý /Ùì™~üü#GŽÄ)y˜þÁDã;v쨫«ëùÀŒ sîܹ3#92ý÷Þ{/õ7õ£FŠ˜qaΦ¦¦´_åËô€ãK¦ÅJ¦Ÿè¶+ÓÏð’Ížéç3=5ëïß3ºå–[Ö¯_FÂß™3gæXÔ¶mÛ¦L™R]]}þùçoܸ1š8räÈ´”?Ütî ßdúP¬dú‰n»2ýžÊËË9ïß¿?Ÿh>÷ïô³-0õwúa ¹3ýÍ›7_}õÕa$ü}ûí·{nCƇÄ3uêÔðôÙ!«W»F.Ðo2}(V®‘›è¶+Óïé²Ë.[¼xñ‘#GöìÙ3mÚ´|2ýè´øAÆóég[`˜sòäÉѧL™’;ÓÆ·téÒo¼1ã6Äg̘±cÇŽOŽfú555ÑÄU«V…mK]ZXõÊ•+u(ô^ýÝ•í×ÈEe‹UÑfú¹äBi»2ýž¶mÛv饗–——×××?óÌ3ùdú555•••÷Þ{owwwÚ ÙxðàÁ™3gVTT„dž%d<å}ª¥K—†UlÞ¼9ã6ÄÃÓ¼à‚ ÂÒÆŽŸ{çðáõµµÛ·on†‘p3ÞTÐ{ôOtcT¶¯Š>Ó÷žõž-Y2ýD·]™þqx‰—‡ùöíÛGŽyB÷Ïüùóã“í„‘ÆÆFí ½@ÿ@7FeûM¦Ê+™~¢Û®Lÿ8¼Äé744ìß¿ÿã?ž8qb×/Àç.€þ  £²…B¦Ê+™~¢Û®Là*++ûýØ%K–ÔÖÖVUU͘1ãàÁƒÑÒzÒGÀç.€þ  £²I#ÓGe‹•kä&ºíÊô½@ÿ@7FeûÁ5rQÙbUf$¹í–T¦Ÿ¨Á+(hEŸéCÉ’é'ºíú~7 ùdúP¬dú‰n»2ýn*@òÉô¡XÉôÝveú ÜT€ä“éC±rÜD·]™~7(âÞ  èƨlÑp\T¶Xm¦_豬Lß‹ô^ýÝ•í·¢Ïô½g½gK–L?ÑmW¦ïÅz/€þ €nŒÊöƒL•-V2ýD·]™¾è½ú'º1*Û2}T¶XÉôÝveúÙêÜ·ão_ ½x@ïÐ?ÐQÙždú¨l±rÜD·]™~Ooüªù ·„ùwmlÕ8@ïÐ?ÐQÙŒ\#•-VevA’Û®L?v¨sß»‹^xåœÉaÎ0lÿÉË'kS’¯è3}(Y2ýD·]™þ'G˜ÿ¿ïyêg§]óRíMkN¹bMÅ•;_zý$n*@òÉô¡XÉôÝvK9Ó˜¿îÌë_zmôóüµŸ¹êDúŸÈô€Â'Ó‡b%ÓOtÛ-ÙLׯÖ(Ä?YƒW ná[ÿ'2}(^®‘›è¶[â¿Óÿíüç_úüÍk+¯Š?tvÚ5Þð¾½@ÿ@7&›^#ˆÙ®‘‹Ê«¢Íô ½AËôc¿í_6ßòðšŠ+×|úJ±>è½ú'º1)¥Lß{•-t2ýD·]™~,þÙ~ôƒý÷—mðî½@ÿ@7¦¯¥ ÷677·¶¶¶··÷~飲ÅJ¦Ÿè¶+Óïé÷¯ýËKµ7†ùwmlõ½@ÿ@7¦O¥Ì?‚(t2}T¶XÉôÝveúÙêÜ×Ö´öÀ‡¿÷½@ÿ@7&ÿRÊô½gQÙB繉n»2}@ïÐ?ÐéDÄ5rQÙbUf$¹íÊôúD)úLJ–L?ÑmW¦Ð'"ˆˆLŠ•L?ÑmW¦Ð'"ˆˆLŠ•L?ÑmW¦Ð'"ˆˆLŠ•kä&ºíÊô½@ÿ@7¦OD×ÈEe‹UÑfú¹Ûw¡´]™> ÷èŸèÆÇRÊô½gQÙB'ÓOtÛ•éz/€þ €nÌq,¥Lß{•-t2ýD·]™> ÷èŸèÆÇRÊô½gQÙB'ÓOtÛ•éz/€þ €nÌq,¥Lß{•-t®‘›è¶+Óô^ýݘ>AD\#•-VevA’Û®L OD‘¢Ïô¡dÉôÝveú}"‚ˆÈô¡XÉôÝveúØŒ²²´‘Á\i‘­ëønÀÓO?=eÊ”d>©‚Øóƒ hÞ¼yÚ œ""Ó‡¢=ä¶ ’ÜveúØ ™þ ÞÂþíáîîîaÃ†íØ±#™O*«K]od¾}ûöÚÚÚP,H‘éCÑrës\¨€Þ Pjý3Ϋªª®¿þú·ß~»çô©S§îÙ³'~È»ï¾{Çw 2¤¢¢âŠ+®X¿~ýÀ·!md0¾Õô¶®©Ÿ´¯pùm@ÚlÏ?ÿ|(}bŸT’÷üñ]oÆ¥¥Nœ ÷èŸ}8¸ÿc2xøðáùóç7.mzWW×Ì™3'OžÝܾ}{}}ýÒ¥K£ßoݺuÚ´iÇkdú'ëÉfœmêÔ©ëÖ­Kì“JòžäL?|-Êñ¯/8šEÑ'EŸé{ÏêÆ%K¦Ÿè¶+Óô^ý³÷ÇÆ…=§8p ²²2Ÿ1cÆ’%KòYòc=V]]=tèЦ¦¦hÊÎ;'Ož\UUÖróÍ7Ç¿ýï5Óï5ÓL[ÈÂ… kkkÃ6‡­=xð`4½»»;Ü Ã]ñü·ª,E¼ŠÇ¼¦¦&,aÚ´iû÷ïÏñܳ­«çB‚aÆuuuÅ3„)áQaJê£zîÌ>mOÏ=¼qãÆ±cdžǎ9ò¹çžËø”ëëëwíÚ•„'ÕskûºçÃË`Ö¬YCŽ #Ñ«âüóÏÿíoûïo¨Õ«£‘0åÜsÏvÈÒ¥KÃN/ŒK/½tÛ¶myqf|Mfܪhþ°OêêêÊËË3¾ÓV±{÷î°º¢£YDÇ1\’飲ÅG¦Ÿè¶+Óô^ý³÷)¿Óúé§/½ôҴ韛éWWW§fµÙ<ñÄ7ß|óÞ½{»»»{ì±hâ…^ØÚÚzäÈ‘°®0qÆŒië:^™þwÜÑuT‰×FR§Çó÷ºU‘§žzjâĉá…Ùî½÷Þ9sæäxúÙÖ•q!=ôPcccüØ0>wîÜÔ È¸3û´==÷ð°aÃ6mÚFöìÙ?6Û?ðœô'•qkû´çÂ’ãéáf´‘Ñ¿ìÚµ«ªª*ú¯'K—.V;mÚ´°1áµ±páÂñãÇäÅ™ñ5™q«¢ù§OŸÿ“F>«ˆß¡8šEq\Â%™>*[|dú‰n»2}@ïÐ?ûppŸ¢¾¾þƒ>ˆ§G#içÞIËy³>|x¶k«FŽ92tèдuõéŒ%92ýøY„m¨««‹7)uzÆueܪȈ#â‡8p ºº:÷Óϸ®Œ yï½÷Rd=jÔ¨;w¦n@ÆÙ§í鹇ÛššR†ÿIo™þI|R·¶O{>¼ âÕ…‘ÚÚÚ0²aÆèÌQ‹- [åûS§N Ó£M8^y¾ò³½83¾&3nU4ÿÞ½{³--ãD™¾£YDÇ‹L•-V®‘›è¶+Óô^ý³÷LwïÞ}ã7¾üòËñôHeeeê5róü~Ʋµµõšk®©ªªŠ–\^^ž6óñÊôS§g<›PêÍ^·*m‡¤ÍÖ×m˸[n¹%ºÔpø;sæÌ|vKÿ¶'Ù¶mÛ”)SB5Ï?ÿü7f\ÑÈ‘#Scô“ø¤2níÀ_‡1bD7nܦM›®¾úêOŽþ»B˜žcQyqæ~MöúZÍ61”ɹwÍ"‚8^\#•-VevA’Û®L€>Ü› ><úmr¶sÆŒ©g?Ï&Û¯°7lئáoϼµO±iyyù‘#G¢ñýû÷÷ûwú½nU<[üù<ýlëʸ͛7GrøûöÛo§m@¶™ÿöäØÃaÕñ.êyÜp¼¨'•ºµ}ÚóáQ©Óã_Äßxã?ÿùÏ¿ò•¯„ñðwãÆ·ÜrKƽ1ÀL?Ûïô3nU_3ýÕ«W»F.ˆ Ž—¢Ïô¡tûí‚$·]™>}8¸?6.üÎw¾óãÿø“ì æöíÛëëëÃ+zxTþ™~Æé—]vÙâÅ‹9–6mÚ´´óéGWjíy>ýhúĉãù³mÕ!CvïÞ¯nþüù·Ýv[t™÷Þ{/>í~FÙÖ•c!ãÆ[ºté7Þ˜çÎìÓöôÜÃaþ(Rß¼ysØŸòªU«Â³H“ʸµ}Úóñyö£éñ™ë.\xî¹ç>óÌ3a<ü5jÔ¢E‹2¾êx±‡¯Éž[•öð´ºôœgòäÉ+W®ÔÍ@qÃ%™>áa¿]ä¶+Ó §Cûvüí«áoúÁý±éá?üÃ?\qÅŸäüUò»ï¾;yò䪪ªòòòË.»,:¿JÏ¥544 2dèСñïú7nÜ8jÔ¨ð¨úúú%K– 0Óß¶mÛ¥—^-í™gžI]HcccMMMeeå½÷Þ]ø4#áfEEE¸+ÌÏŸm«-Z–ºêùóç92Ì9nܸÔ°÷”m]9²téÒ°ºÍ›7ç¹3û´==÷p˜ÿ‚ .[8vìØøl6iOùðáõµµÛ·o?éO*ãÖöiÏ™~¢Û®L€Toüªù ·„c§]âëJ™/,ÇÍüùóÔeà/ÂñšŒ— ÔØØhŸÃÀ‰ "2}(Úã“b}b®‘ëÐ{ЦêÜ÷î¢^9gr8j ÃöŸ¼\*_Wdúœl•t"^“i ÇÑ,Ç‹"⹨lÑ$—fû.”¶+Óô^€ïŸoüêßóÔÏN»æ¥Ú›ÖœrÅšŠ+w¾ôzéì™ÁÌ:+39¹Ï=QÛã™þkG³ˆ ¨è3}ïYݸdÉôÝveú€Þ Pšý3þaþº3¯qèµÑÏó×~檒 ôÍr"J)Ó÷žEe L?ÑmW¦è½ÅÚ0óN¹"ß9 ƒÁ`0¤ "ˆOdú¨lñ’é'ºíÊô½ f¸÷Pç¾ßÎþ¥Ïß¼¶òª8žøÙi×tlxÃÞp4Ë?geúÞ³¨lAsÜD·]™> ÷”à•Ô{ÿÚ¿l¾åá5W®ùô•b}G³—ÏY™¾÷,*[ÐÊì‚$·]™>@QêõШ¹¹¹µµµ½½=šÿl?úÁþûË6؇ÐïÏY™>PÐdú‰n»2}€¢ÔïC£ß¿ö//ÕÞfص±Õn€ãû9[ddúP¬dú‰n»2}€¢4ÀC£CûÚšÖøð÷ö$÷ÏÙ¢!Ó‡b%ÓOtÛ•é%‡FàsöD“éC±rÜD·]™> ÷%‡FŽfñ9{¢¹F.*[¬Š6ÓÏݾ ¥íÊô½ ¦C#G³øœ¸¢Ïô½guã’%ÓOtÛ•éz/@ 6L‡FŽfñ9;p2}T¶XÉôÝveú€Þ P‚ Ó¡€£Y|ΜL•-V2ýD·]™> ÷”`ÃthàhŸ³'ÓGe‹•kä&ºíÊô½ ¿¨84p4‹ÏÙs\T¶X•ÙIn»2}€¢äÐ|ΞhEŸéCÉ’é'ºíÊôŠÒÉ:4 G˜v~ÿ<ýôÓS¦L9é›¶aÞ¼yÊÌÏÙ¤‘éC±rLŸè¶+Ó(J2ýÂÒÝÝ=lذ;vœô-Ù¾}{mmmØEHàçlÒÈô¡X9¦OtÛ•é¥Äfú'1ôï÷ªó|à@žÚóÏ??uêÔÔEåååUUUãÆkhhؽ{wêüo½õÖ-·ÜRuÔÍ7ßn†‰]]]µµµGމg›6mZ<¦×ÕÕ}üñÇaÉá±96~òäÉ+W®ô&AôJ¦ÅÊ5rÝveú€Þ P”dúÇqÕƒéO:uݺu=uäÈ‘·ß~û‡?üammí|M|÷Ýwëêê–.]zø¨çž{.ÜûÞ{ï}rôÌ9ñrº»»ËËË<Ý Óï¸ãŽhá3fÌøñœmãÃk#õG³ˆ ²q\T¶Xm¦Ÿ»}JÛ•éz/@ 6̇FiÁt|3Œ,\¸°¶¶¶²²rÆŒqRÜÝÝn†‰á®ÆÆÆxþ;wNž<¹ªªª¢¢âæ›oÞ³gÏ'üùy$^Åã?^SS–0mÚ´ýû÷çúj‘eÂȬY³†Fâéaþ¦¦¦ºººòòòŒ«îiãÆcÇŽ Ë9räsÏ=—q›ó|jÙödÏUõõõ»víÊV…`þüùá)GãadñâÅ©÷†›Ñ½aá·Ýv[4qÆ ቇ¿ÑÍ0ýç?ÿy´ð°Ÿ/¼ðÂèŸz®q÷îÝa{¼‰ÀÑ,"ˆ^}¦ï=«—,™~¢Û®LÐ{J°aö/Ó¿ãŽ;ºŽ #=öX4=Œ¤Nç_ï[[[9røðá0OœG§-ÿ©§žš8qâÞ½{Ãl÷Þ{ïœ9sr}µÈ² a!ñôp3žúôéñ¿äó;úaÆmÚ´)ŒìÙ³'Þ˜´æùÔ²ÝÌ¸ŠŠŠŠýäèyu†×ÔÔ|üñÇi÷VWWrôwýÇûó“£ÿX2eÊ”h/…u…éÑiy¢…‡§0~üølk¬¬¬ô&G³ˆ z%ÓGe‹•L?ÑmW¦è½%Ø0û—éÇç~Ù±cG]]]4>|øðÔésó#GŽÄytÚ #FŒˆ~àÀ(˜ÎúÕ"Ë6„‘øê²a¤¶¶6ž?J·3®:£úúú¦¦¦ÔŸÌç~`ާ–ífÆUôšé§Î“ûÞ†††°ü0rã7¾õÖ[_ÿú×ÃøÂ… Sÿ©#yâ‰'žzꩌ˔郣YDù飲ÅJ¦Ÿè¶+Óô^€l˜ýËôS§gË—ã›­­­×\sMUUU|¹×ló§ŠgËüÕ"¿mèuÛrضmÛ”)Sª««Ï?ÿü7f|`þO-ãÍŒ«9rdîsïìÝ»7þ0ÒÕÕ•zïþýûã{ß~ûíñãÇ9rä‚ .7Ãß0>vìØè:ºŸ{²þ0ç–-[ÒֶĹwÀÑ,"ˆ|ÈôQÙb幉n»2}@ï(Á/*9ÊËË£3´|r4)î÷ïôGŒ±aÆÇ‡ñð7Û¿ „Ù¢óÑçõÕ"ûïôS§§þN?íáùïÀÍ›7ÇËï¹Íù<µl{2ã*¦N*’cSçÍ›—Ïùô#ãÇ_¾|ùÌ™3£™›šš.»ì²Œ ÷ÝwÇ÷ÿ±w7PRÕwžð[;¶íN'¤ÓaÙ–a'¾$<*yÓ5†$ˆ¤e‚Äa9 »y!‰ÁÖ‰ë1n¢<íéÃø°,Çc‚¯Ë ¶²tÒٸƒž'ì„cÐ ŒžPNŸž>Їaxþáš»•êªêêꮢúöçsêpªnݺ/ÿ_ÝÝûí˽}}}©{ì1÷È{³ˆ òṨlRUh‚rîveú‰Tð®ÑôéÓW¯^}üøñ,X° ízú½' ¼ž~4|Μ9ñøõõõñUão¸á†xxmmmwww<»ÖÖÖÙ³gïÛ·/<å•WRƒé ‡9—¡§§'Z†™É8ëŒÂD—ñÙ²eKX…ŒÌsÕ²µdÆYlذ!¬BêšFOÂÇ_zé¥Ûn»-õï»víš0aÂŒY2ý²îveú‰Tð®ÑÎ;§M›VYYÙÔÔtï½÷¦fúmmmõõõÕÕÕK–,éï'áeUUUx+ŒßÑÑ1yòäh:k×®‡ßsÏ=a © rkkë¤I“˜S§NM=W=áE–e8räÈòåË«OZ¶lY<<-¼8ëœwÞyau¦L™_'íƒy®Z¶–Ì8‹cÇŽ544ìÞ½;^òH˜`˜È-·Ü’ö¿^|ñÅY³fE«|å•WÆ×Õ‰¼ùæ›aúѽºººÂóÔ{êlÙ³gÇÃ2„%‰ÛD2}H*™~Yw»2}€Dñ]£!]¸¦X‡I>¸hmm-‡+Þ„ehkk³”øwv”’éCRÉô˺ەé$’LFÑïì(%Ó‡¤rܲîveú€¾ ‘F|רºººd _ÉÈ.C¶YØ›å”üÎŽRî‘‹Ê&Ub3ýÜÝ÷héveú€¾` v˜vìÍâwvøŸéÛfõÆc–L¿¬»]™> ïƒfx÷Ô>Ð9#‚H™>*›T2ý²îveú€¾` v˜§v×Hgèœñ;› 2}T6©dúeÝíÊô}/Àì0eúöfIêïl)ÉôQÙ¤rܲîveú€¾` ¨ÈôìÍ’ÔßÙRr\T6©*4A9w»2}€D’éÀØü-¥Ägú0fÉô˺ەé$’LÆæïl)Éô!©dúeÝíÊôI¦ców¶”dúT2ý²îveú‰$Ó€±ù;[J2}H*÷È-ënW¦è{i˜»FGÞûß~þ-ý²Ø›%ñ¿³‰á¹¨lR%6Óí‡"2}@ß 0f;Ì»F·þróÄÏ…º::uæ:@Föw6aŸéÛfõÆc–L¿¬»]™> ïƒæÀ]££ïºçÑŽ 懷Â㕵OèÌt€ŒÔïlRÉôQÙ¤’é—u·+Óô½c°ÃLÝ5:¸õ—¿Xôí'ßùÉŽó¿°±næã§_º÷o¨3Ð2"¿³Én™>*›T2ý²îveú€¾` v˜Ø5ºÿág¾üíïàóO½ÖÎ÷øÛ>>^ùábú:s@ç̘ø•éÛfQÙÑÌ=r˺ەéú^€1x ]`ç>°7‹"Ü#•Mª MPÎÝ®L ‘ò9Oÿû×|õ‰ÚË;í­œýÉw\þä¸Oìz«Ö€áþÎÊôÑL¦_ÖÝ®L ‘òß5Ú÷ÔO4}ác§_ú‡g|\¬#ø;›ìvéCRÉô˺ەé$ÒPwŽ<ü¿—Þ½qÜ'£sö_}ðim#õ;›T2}H*™~Yw»2}€D*x×hßS?ýïõW„º::5#ŒìïlÂÈô!©Ü#·¬»]™> ïH¤aî=xøå5í}¿]KØ›eÄgÃ=rQÙ¤Jl¦Ÿ»û-Ý®LÐ÷ŒÁÓ®€½YüÎ_â3}Û¬ÞxÌ’é—u·+Óô½c°Ã´k`o¿³Ã'ÓGe“J¦_ÖÝ®LÐ÷ŒÁÓ®€½YüΟL•M*™~Yw»2}@ß 0;L»FöfTßï_ßý½§ŽüÓ!¿³ÙÈôQÙ¤rܲîveú€¾` ¨Ø5°7K>º::Ãæ“ï¸üõçÿÁïì@î‘‹Ê&U…&(çnW¦Hv`D¼zÿ÷Ãïfx<óïæ¾Ô¶þèÁÃ~gc‰ÏôaÌ’é—u·+ÓH$»F0RömzþñÊ?YóñNYðÄ™—uÎÿO~g#2}H*™~Yw»2}€Dt×hóæÍ{öìÑV0¨(ÖNØÿ»³ç¥¶?fgeúT2ý²îveú‰…§äñÜsÏíØ±cÿþýc!\’éCò¸G®L_¦è{N™Í›7‡½ ÿúå[Úÿ( ;EÛ¶m ûEÔDöfT÷³¿xrÜ'7¾ó“o¥ö§]údíåÿß¿¿ãûoË¿³î‘‹Ê&Ub3ýÜ òhéveú€¾ Ù:;;Ã.ÐwÞý‡#aÈŽ;ÂNÑþýû{zz4€½Yr‹ý'j.ûCšúŒŽó¿°w}Ç¿û¿³‰Ïôm³zã1K¦_ÖÝ®LÐ÷$Ûž={ÂþÏꪽüGaÈþýû<ØÓÓsôèQM`o–þñÑEçæG'æù§C~gc2}T6©dúeÝíÊô}/€þ½1uutþ!O¨úp|b¾Ê¦’飲I%Ó/ënW¦è{ôŸè¨ï÷¯¿Ô¶þŸ÷t©l62}T6©Ü#·¬»]™> ïР7Fe ๨lRUh‚rîveú ñ™>ŒY2ý²îveú@¦I%Ó/ënW¦@dúT2ý²îveú@¦Iå¹eÝíÊô}/€þ½1*[÷ÈEe“*±™~îy´t»2}@ß  ÿ@oŒÊ ñ™¾mÖ6;fÉô˺ەéú^ I]„‡GÚ£«£Ó¦Ø›Ee‹D¦Ê&•L¿¬»]™> ï’ÔE;v‹‡Gü_‰ïºbÿÓ[m€½YT¶dú¨lRÉô˺ەéú^ I]„Û#-Óïúw‹õ{³¨l‘ÈôQÙ¤rÜAütë€IDAT²îveú€¾HÒ΀Û#-Óÿþæñ[ÛÇ}âï×þíž={¤)¶GÆL?<¶?øÇj/{îoîëÀH‘éCRÉô˺ەéIÚb{dËôÃãßãbý¶ï‰õ`DÈô!©dúeÝíÊô€$í H±Ã#ìãYŒŒ™þÎÖX¬#F¦Iå¹eÝíÊô}/¤¾L?w¦2Öÿ†X°7‹ÊŽ`¸ä¹¨lò$6ÓÏ –nW¦è{$u§<˜Îsü¢æÝåœé—xÙ²}%ÞŠõ][°7‹ÊŽD¸”àLß6k›³dúeÝíÊô}/¤.B¦/ÓOËôs?:;;Åú€½YTv˜á’L•M™~Yw»2}@ß $©‹È"·µÍlh8³ººráÂ)½½+¢ááÉ—¾4­¶¶*<“xxõê«k++O«H1pÊÏ<3ï ß&{ÖYuëÖ}&úlÚø{÷~ùóŸ?§¦æmUU§úÓ“»º–g-múñ˳øhi™QWW=nÜÛÃbÇÿîwg55½3Ìô¢‹Þ·}ûâ “cü¾¾›B‹…¹‡Ö»ûîO¦.ä_ÿõ‡ëëÏo]ýù‡ß8èøñŒÒV<[J–øoÞ¼Y¬Ø›Ee‡.ÉôQÙä‘é—u·+Óô½@’ºˆŒ™þµ×žýÆ_ð¤¥eF4|ÅŠK¯¹æñðð2ÿ/ÿòÂ8§Îq^ùøñ5?üá“®®åË–MÏ8þùçߺõ†þþ›i ³^¸pJî?íeÆY¤>n»í£Ÿþôäîî¯öõݯZøøõן†ù¶µÍ¼øâ÷º0Çã„– MšèóŸ?'^ªÛoÿXhº0~˜ÎâÅSã‹Æ›4Ÿóô³U¡d™þúõëÅú€½YTv˜á’L•M÷È-ënW¦è{$í d ‘_}uiôüw¿[ÚØX=OÂËxxCÙñøÝÝ_ÍçZ1MMï\½úª×^[–çµeúûo7îíCÊô3Î"õ1aÂ;âµHýx|Â{˜iUÕéƒ.LÆñCíÝûåøÿx©&N7iOÏ7êêªã…Imê|2ýlU(Y¦ÿàƒnذá?øÁ¶mÛöìÙÓÓÓc;ìÍ¢²C —Ü#•Mž MPÎÝ®LHÒÎÀ !rXç9xð í†.%8Ó‡1K¦_ÖÝ®LHÒÎÀÎÓOžzžþPïéúüó_Œ'›6þĉãžzêº#GZÂóðo¶¸¼²ò´þþ›£ç‡ß8p¦©³Èç<ýŒ/ó\˜øeêyú¡­R§_‹?ma 8O?cJyí|0º϶mÛöïßo;€¡†K2}H™~Yw»2} I;Ù®§øðá‘z=ýøâïaø5×| õzú©¯­­Ú·ï+á… §Dyúóϱ¾þŒŒã‡áññ¿øÅ ≧ö¡5ÞsÏ•ýý7‡Ñ®¿þüx´Œ³x=ýð©´ëégŒÑ³-L¶ñÃ?ÿùs¢¦»îºsãá+W^þ¹Ïýy÷ÿö·ÿ1¾.|ýý¨I3fúi+ž­ %Ëô{ì±|ð‘GÙ¸qãOúÓ={öØŽ`¨á’L’G¦_ÖÝ®LHÒÎ@ÆLÿî»?Y_FuuåâÅSûúnІ÷ö®X¶lzK—~(ž–D¯ZuE!c<ýè£sÎ=÷=UU§_xá{ã 㤆ÿÙŸ½«²ò´¦¦w®Yó©xxÚhÛ·/¾è¢÷E£­]ûFË8‹´ÇŠ—ÖÖV÷öÕ«¯ÊÑg[˜lã‡&Z´èƒaî¡õB¦^gåÊËÏ:«.Lêƒl  mZ8?c£¥­x¶*”,ÓüñÇe0ÌpI¦Éã¹eÝíÊô}/¤<¯8ï1ÔÇË/鬳ê¶R2}ÀÞ,*;"á’{䢲ɓØL?w‚ ï’ÔEÈôGö±bÅ¥‡ßxàÀ×Ja™>`o•-áR‚3}Û¬mvÌ’é—u·+Óô½@’ºˆ¹muu¥h¾àÇš5Ÿjh8³¦æm Néí]!Ó°7‹Ê —dú¨lòÈô˺ەéú^ I]„ÞC¦Ø›EeK.ÉôQÙä‘é—u·+Óô½@’º9uù7­¯ï¦ÊÊÓâ›Ä†á×^{v4ñ… §Ü{ïÕ2}™>ŒÙpI¦É#Ó/ënW¦$ig Ÿ¨wåÊË.œ=Oî¹çÊÔwÃËèÝgž™÷¹Ïýy4ð©§®«¬<-ü½ Ã7o¾.šøáÃ7žþøèÏ9Âå0µ /|ouuåYgÕ­[÷™´1Óï~wVSÓ;«ªN¿è¢÷mß¾8þ`KËŒººêqãÞ¾zõUñÀ¿þë×ן¦výõç‡ȸÖmm3Î ã„Õ‰ÿž|éKÓjk«Â#<‰‡‡ñÃôkÃjV¤ÈÑž×h˜ËqFÙ§¯ï¦0Ù0ñ0‹»ïþ¤LNU¸$Ó‡ä‘é—u·+Ó’´3O¦ÿÆ_7îíÑóúú3øZÚ»uuÕÑyý&¼£»û«Q†~Ýuç¶´ÌÏ»º–‡áÑey¢‰oÝzÃÅ¿?w¦?~|Íø…èãË–M[_ýùa^a²mm3ã©ÝvÛG?ýéÉax_ßMÑÜÃãöÛ?vÍ5iY¼xj<µ´µ¾öڳú„GxvÅŠKÃgãááe<þ_þå…ñŸräãñ[×h˜Ë-ÓÏØ8a‚©Ó—éÀ© —dú<î‘[ÖÝ®LÐ÷IÚÈó’,UU§çóFgÇêSöË_.¾âŠIáy[ÛÌ1ôm·}ôöÛ?–cšMMï “zíµeÙbëøtõþþ›ã˜0á¿ûÝÒ´IMœ8.¾vPOÏ7¢¿@ \ëxœ0…ÆÆÚèyxO0ûÝÏ<3o`l-ÎÎv×ßXeåiƒ®u¶¿adžO¦Ÿqб C.ÓìÍ¢²%—œéÛfm³c–L¿¬»]™> ï’ÔEä“éßyg^×Ó_üþûïÿì¢EŒF^½úª}¨1ãÄó›ÿðÁ6ôô|#w¸üüó_ŒÏX4žÎvž~W×òAo›íùÔპ#ŸO{¦®Ñp–¡²ò´èŠFÑ_VòiçéöfQÙr—dú¨lòÈô˺ەéú^ I]DŽ º¿ÿæ_ÿzÉ­·~$5SþÍoþÄ ïX·î3GŽ´„“ð2õ†·«W_5qâ¸öö¹áùßþí5ãÇפޫ6-G¾÷Þ«¿ô¥iÃå… §Déüóϱ¾þŒ<3ýèzú]]ËS¯§¿rååŸûÜŸïÝûåð<,jê_ Ò®eøðá‘z-ûøôaø5×| Ûµìkk«öíûJîL?ã g>ô¡Æ{î¹2T!¬ïõן?hãDÓ‰¦¦#ÓìÍ¢²§*\’飲É#Ó/ënW¦è{$u9®TS]]yÑEïki™‘v’û¯~õWŸþôäðnx\qŤøº:ÑãÀ¯UU]kþµ×–…ç©÷Ô˜#îsž1\~ôÑ9çžûžðñ /|oþ×Þ‰®é_[[5nÜÛSÿ–°råågUWYyÚ?ئœq­ï¾û“õõg„•Z¼xj_ßMÑðÞÞË–MVvéÒÅÃÓ`Õª+ÂÙ®ü“c†³ Û·/ +ÕÔôεk?5hㄆɆ³3’éöfQÙS.ÉôQÙäqܲîveú€¾HÒÎÀPSݤ> ¸ÇàC¦Ø›EeG$\r\T6y*4A9w»2} I;rj™¾LJ.%8Ó‡1K¦_ÖÝ®LHÒ΀œ:zTWWj™>”&\’éCòÈô˺ەéIÚS{Èô Äá’L’G¦_ÖÝ®LHÒ΀ ×xÈô Äá’L’Ç=r˺ەéú^ I;2}™>`o•-q¸ä¹¨lò$6ÓÏ –nW¦è{$u#ÐH¦?p"$L—éöfQÙdH|¦o›µÍŽY2ý²îveú€¾HRQΙ¾ÿ Ó°7«² #ÓGe“J¦_ÖÝ®LÐ÷Iê"²'é½½+¾ô¥iµµUáž„—c÷ï~wVSÓ;«ªN¿è¢÷mß¾8÷gŸyfÞ…¾·ººò¬³êÖ­ûLÆùÊôeúöfU6ydú¨lRÉô˺ەéú^ I]Ä çůXqé5×|à7¾×^{vx™ñ#×_~w÷Wûûonk›yñÅïÏýÙñãk~øÃ/„']]Ë—-›î<}™>€½Y•#dú¨lR¹GnYw»2}@ß $ig`Ð ½±±öw¿[=OÎÌø‘øüþþ›«ªNÏýÙ¦¦w®^}Õk¯-sí™>€½Y•SÜ#•Mª MPÎÝ®LHÒÎÀ zÚË8¯Ïç#Ù>»}ûâë®;·®®úì³ßýÌ3ódú2}SáR‚3}³dúeÝíÊô€$í äsžþ«¯~ž~Æ—ƒ~öùç¿Æ‘éËô`L…K2}H™~Yw»2} I;sÛÚÚª}û¾¿li™qíµg¿ñÆ×¾ñšk>ízú_fûìÂ…S¢kò<ÿüëëÏÈ8_™¾L’.Éô!ydúeÝíÊô€$í ÌmW­º¢ºº2NÒ{{W,[6= ¥K?Ô×wSþ™~¶Ï>úèœsÏ}OUÕé^øÞøÚ;ió¦“J¤.Ó€d„K2}H÷È-ënW¦è{$í È©=dú€½YT¶Äá’{䢲ɓØL?w‚ ï’ÔEÈ©=dú€½YT¶ÄáR‚3}Û¬mvÌ’é—u·+Óô½@’º9µ‡L°7‹Ê–8\’飲É#Ó/ënW¦è{$urj™>`o•-q¸$ÓGe“G¦_ÖÝ®LÐ÷Iê"äÔ2}ÀÞ,*[âpI¦Ê&{ä–u·+Óô½@’väÔ2}ÀÞ,*[âpÉ=rQÙä©ÐåÜíÊô€$í È©=dúPâp)Á™>ŒY2ý²îveú@’v<<†ôéÀðÃ%™>$L¿¬»]™><á?üî‡Ëpxù8ä$ƒ€a†K2}H™~Yw»2} y:;;7oÞ¼aƇ ᫾0ák³gÏ›äO¦Iå¹eÝíÊô}/<;vì?ú›6mÚ°aÃÜô_nþ–FÈ(|IÂW%|aÂ×fÿþý6ÀÞ,*;ÔpÉ=rQÙäIl¦Ÿ;A-Ý®LÐ÷É~î·mÛ~÷7oÞÜÎI·Ÿ•FÈ(|IÂW%|aÂ׿àÁƒ6ÀÞ,*;Ôp)Á™¾mÖ6;fÉô˺ەéú^ yöïß~ñwìØÑÙÙù'}çÝ—h„ŒÂ—$|UÂ&|mzzzl>€½YTv¨á’L•M™~Yw»2}@ß $OOOÏÁƒ÷ïß¿gÏž—9iuÕ‡4BFáK¾*á ¾6GµùöfQÙ¡†K2}T6ydúeÝíÊô}/€þ½1*[™>*›Tî‘[ÖÝ®LÐ÷è?У²p\T6©*4A9w»ïz×».OŠë*þÍ·² ï^|ñÅÓ§O¿ôÒK/`xÞõ®w%;Ó™>å˜é@Ádú Ó§DV¯^ý‰?ºè¢‹Bÿ{Þyç{Z×Uü›;² ï†qÎ>éF¢E‹dú Ó§Ô:;;7oÞ¼aƇF³A¯§¿bÅŠ›o¾yåÊ•0r6lذyóæÎÎÎ={öˆÚ@¦_¦’t/…;v<÷Üs›6m ýïã֠™þW¾ò•¯}íkÿù?ÿç‡QkýµË4€þ@oŒÊ–• 6lÚ´é¹çžÛ±cÇþýû¥¨¬L¿LåNG——_~yÛ¶m¡çݼysû¨5h¦ÿå/ù«_ýêw¾óv`4oé@ÿ  7FeËÊæÍ›Ÿ{î¹mÛ¶½üòË”þ¡²2}µ/ºýû÷‡>wÇŽÏZùœ§¿bÅŠ¿ù›¿yÍ[ºFÐèQÙ²ÒÙÙ¹cÇŽ—_~yÿþý===Ò?TV¦¯öEzÛƒ†nwÏž=/Zƒfú7ÝtÓ·¾õ­‡~øe`4oé@ÿ  7FeËÊž={öïßðàÁžžž£GJÿPY™¾Ú3 ï~ë[ßZ½zõ³Ï>«­@ß  ÿ@oŒÊ¢²*KFî‘KYl2}Ð÷è?У²¨,*;¨ M@iÈô†I¦O‰Èô†I¦O‰Èô†I¦O‰Èô‹»%WØ–5õÐÜyçóæÍ‹ž‡'+W®T\€òç¹”ˆL¿¸[òƒæÒ'ÎEšcÙFç#²`#µvO§¿¿üøñ{÷î^îÞ½»¡¡! Ô÷ØwУ²¨,*[æ›éçN)·Š¤fúTSS3sæÌ_|ñ­/kÊðùóç8p þø®]»æÎ[[[[UU5cÆŒM›6ek9E)s>óMÀÉã§d ˜iaËyÊCü4<òH؈R‡477¯_¿^ß `ß@oŒÊ¢²¨l™“éSIËô£ÇŽkmm:uê[_Ö?ïééY¼xqsssôr÷îÝMMMëÖ­‹Î2Þ±cÇ‚ вµÈôOõj–ÃL“‘éÏŸ?ãÆ©CÚÛÛÓR~}/€}W½1*‹Ê¢²eH¦OYT$c¦©ªªzëËš2¼¯¯¯ºº:z¾hÑ¢µk׿³ ·ÜrK]]ݸqãÖ¬Y Ù·o_sssMMM˜Ë¬Y³âsÿ;::¦L™f1iÒ¤xàDÊÿȪf®Zµª¡¡!L*,ç‘#G¢ááÉÒ¥KkO OâáÌ7mîáIooïøñã{zzâwð Ñ[o½µ¾¾>ÌbÁ‚axîi††mjj sÁüìg? ‹4yòäðrÚ´i©ÿ"zÒßßÖ1L9Ì«­­-u‘Bƒ766VVVfkó«?ÉÖVa„uëÖE‹–gçÎ9Ö%Ï™f”m½²5f>EL}’q-ŠÝJaHWWWêjvww‡ú^û®zcT•EeËœLŸ²¨H¶óôï¼óÎiÓ¦½õeÍ’é×ÕÕ¥æ×ÙÜ~ûí³fÍ:tèPÿ-·Ü ¼à‚ :;;?æ.Z´(>~üøèÊþX¾|ùÀȰ-eÉôçÎÛsRxÏ·¥¥eΜ9ñðð²àù¦=ùÚ×¾ÖÖÖ¿ž¯X±"<¹ãŽ;Â|C#„õ]²dI<‹lÓœ?~wwwhŸPš†††Ô—ùÈGÒfÖ.¬KoooX©æææÔEZ¸paygkó´ÕŒ_fk«0‚ º„I­Zµê’Kr}ÁòœiFÑzÅ $[cæSÄÔÆÉ¸Ån¥øOe©âmJß `ß@oŒÊ¢²¨lÙr\ÊbkÌx=ý ©©éµ×^{ëËšåÚ;ÓÉ&L˜ß4£ãÇ7.zæ»fÍš´™ »NK¼üaîÑóð$^˜ð¤¡¡a˜óMËô_yå•Ôs®'Ož¼oß¾ðdâĉñòôõõÕÕÕåžfćÆI{9ðÿO„•Šærâäiæ©‹tèСAÛ<[Z­­ÂñÙè©Ë3¨3ÍöÍI-bü‘l™OSgе(F+/Ó×÷è?ôƨ,*‹ÊU…& 4 8O¿»»ûꫯþþ÷¿ÿÖ—õª««Sï‘›çyúÓÛÎÎÎË.»¬¦¦&šrtq˜`çÎóæÍ S>çœs:::rLaH3Íx¡ÔáÏwàÅX>ûÙÏF÷ ÿ.^¼8­ ÓÖ7Ÿ…Ï‘Jç9Bî6ÏóãÙÚ0w+å9Ó!µC¶ÆÌ§ˆƒ6Z±[iÒ¤Iiu/GäÚ;•LŸ)ìzú]]]&LˆN4ο.Z´(¾>~ÏÓŸ8qâÓO?}ìØ±'/õ3p[¶l‰O®ÙóôS‡ÇgU<ßÙn˜ÂÇ>ö±ð$ü_û>¬oü·ü§™ûeÆóôÃÚe‹›³µyŽ3Ð3¶Õ2ýö؈Ü#€¢’éS"ß#÷†n¸ï¾ûNd_wïÞÝÔÔƉÐ^x!|äÿ|Åÿø©èzúH½ž~}}}|éóð©xäE‹EزeK'X[[ÛÝÝu[Ê~=ýÞ“R¯§_¢= Ÿ3gN|õóæ›6÷ÔŘ:uêºu뮾úêxHkkëìÙ³£äý•W^‰¯ÒžÏ šJ‡•jnnŽVvÞ¼yÙbèlmž¶ši—éØVCÊôóœiFñ}¢ˆ?›­1ó)â }±[iÆ aüÔ·BíÖ¯_¯§(s2}J¤àLÿÇ?þñŒ3Nä mwíÚÕÜÜ\SSSYY9}úôèš3o}ÅS>ÕÒÒR[[;nܸø¼þŽŽŽÉ“'‡O555­]»6¹½½ý¼óΫªªš2eJ|ù”{î¹§ºº:ÛbdËôÛÚÚêëë×,YÒßß ?räÈòåË«OZ¶lY<¼€ù¦Í=u´uëÖ…nÙ²%uÌÖÖÖI“&…Už:ujÚ™Ú¹×hÐL?¬ÔâŋÇõ kíò/ÙÚMe¥ûÙ_´¿ý£Q@ß^ýÑÿþî+7ÿÛÙ¿ùŠÎÓô¿àh@€$Ó§Ô¶nÝúìŸ C¶oß¾k×®}ûöõôôh"(q ¿iü§ž8ó²­³W„!ÿzì_âdú¥$Ó ³|ôGÑéù©'æ§qûk€R’éAWGçÉ;â~4íÄü4ƒfúßúÖ·V¯^ýì³ÏjR€ás\H×÷û×_j[ÿÏ{ºS¦`×@oŒÊ¢²¨l)%6ÓwgNù¶,Ó°k 7FeQYTvdÉôQ  XÛ²LÀ®€Þ•EeQÙ‘%ÓG€bmË2}»FzcT•EeG–Lе-ËôìèQYT•Yî‘‹Åú¡•éØ5У²¨,*;²*4“é”’L€ÂÉôJI¦@ádú¥$Ó pc9Ó¯¨M¿¡wÞyç¼yóJ¼j#ÛD§¼Áó\€òüb”gM‡iË–-'NL[¤S»„a+[¹r¥Ÿ ¸‡xI]1÷RP  dú£Bÿøñã÷îÝ›6üÞ{ï­«««¬¬ÌsÕ†ºÊñø#ÒV%kða®þèÊôOmM‡iÆŒ?ùÉOFvÁnáy²víÚ´13ß½{wCCCØâì8PEeQYT¶ˆ‡xI]±Ü1 ”`[ΑéWüQMMÍÌ™3_|ñÅ´á±hxOOÏ7ÞØÔÔTUU5nÜ¸æææŸýìgÇ?åÉoþ#”lÙyä‘ùóç-´dÜò'Š×–áyúÙHîö)ê0~üã‡Í¤ºº:”lñâÅê—0‘çé‡ñ 3m£ˆìÚµ«¶¶vàȇ‡Þiýúõv¨¢²¨,*[Ä£~µG€"m˹3ýèɱcÇZ[[§Nš6<ͬY³V¬XÑÕÕuâäYç7nüÄ'>‘q‚¥û=™þüùóC‹ :šL?ÏÏ–>Óÿìg?6¢ãÇ9räÆoœ9sæP¿„‰Ìôs¢°Uêh8¼½½=ãŸÐì8PEeQYTvÄŽ†ÔŠ´-ç“éGªªª2Oáøñã¹~φžÓåH´W­ZÕÐÐP]]½hÑ¢#GŽDÃûûûÃË00¼ÕÖÖ¿oß¾æææššš°³fÍŠÎ¡Îøÿn½õÖúúú0… ôööæ^àŒËž,]º´ö¤ð$Æ_³fMccceeåÀY755EI“vÕA[ u„uëÖEÿmbÚ´i;wîÌÝD0ŸÏfk“ŽŽŽ)S¦„iNš4éÈV”_ŒAWçD¦Ð¼ØË6„A·—QTÓlKŸ³5]Z+e¬NÆ-.ã’dÛ(íaïîî­a×À**‹Ê¢²Å#ÓG€bmËyž§çwN›6-mxš™3g.^¼¸³³3[²?²™þܹs{N On¹å–hxx’:<ÿ‚ .ˆ,¬KgÑ¢E§ÇwÌ™3çСCa´%K–,_¾<÷g\†–––0‘xxx¿páÂŒ©å‰”?™¤ E™8qâZ u„ „u k½jÕªK.¹$wåùÁ°üáƒÍÍÍÙòߌŸÍÖ&ãǾxˆ[;ŸÓÌãq]Y2ý¢.gGGÇG>ò‘ýôÓÇŽ;qòRBÙ2Í0Zt©ý¼~ž³Ÿ§Ÿ:<ã Ë'2Ý#·½½=m©§Tç9÷AãÚ|ÎéÎøÁÔsºÃ†”égk“Ø–-[®BîÆÏ½:y~|d—sÓ¦MMMM;vì(p‡¯\kš{©òÿÚg}zX¶ãÇ8p`Á‚i×øî=iàõô£ásæÌ‰Ç¯¯¯¯‡V$^[[ÛÝÝÏ®µµuöìÙQÊùÊ+¯Ä—Ý϶À9–¡§§'Z†Œ8ë 6„OåÙ,9æ>hŒ›­‰òù`sssôÁyóæåÎj3Ît`›„Ž.زeË–P£Œ-“ûÛ’mur·a1–síÚµ'N|饗†ú%/ÿšæ^ªü¿ö—'Ÿ-nÐò0<¬õúõëý:Å#Ó p#•éÿøÇ?ž1cƉì×ÓÿÙÏ~6oÞ¼šššÊÊÊÆÆÆ¥K—¦[@¦¿sçÎiÓ¦… 655Ý{ゥ™`[[[}}}uuõ’%Kúûû£ááIxYUUÞ #ÄãwttLž<9šÎÚµkãá÷ÜsO˜Bê¬[[['MšÆœ:uêÀçÓ8ã29rdùòåÕ'-[¶,ž¶‚i³>vìXCCÃîÝ»Óæ’íj$Ùæ>hŒ›­‰ý`X¯Å‹ÇL=W:ŸÏfl“ÐÂçw^˜Ô”)SâkÚ ,JŽoK¶ÕÉýÕ*ÆrfÛ.bÛ¶mKû+×h©iî¥ÊÿkŸqyÝâ¶d¶"Ïáa+{ïøñ¿½÷É#ÿtÈP$î‘‹…+8Ó/ëŸÆŠŠä-CkkëÀë466vqö¢Ú½{÷¤I“l\Crå•W–a)­i9lkùlaÈÀk%e¶²¶¶¶®ŽÎÐû=Y{ÙëÏÿƒ]#ª¨,*‹ÊŽ|hÔË3¡@@ ¶e™~™/ÃC=ÔØØX]]]ÍÞÒÒÒÛÛûæ›o¦^\…QmК–a¦?p£ÏÃx mÌlÃ#ÿøèBO½Öoî~äèÁÃv¨¢²¨,*;b¡Ú£@@‘¶åQšé—2ã®Î¤ÄËP&Ö®]ÛÐÐPSS³hÑ¢èÆ §ªñ)YM“ÝàÝÏþ¢ýŒŸ^{â¹AÇñà@•EeU–ŒÜ#ŠõCë<}`,Ëxžþ¯¿stžþ ý§p ŠÊ¢²*KFš€‚Éô2ú“ëéŸññŸ~úkaÈ¿û—üûOmd$Ó p2}€þñÑEçæ§ž˜?Ôþó®»îºÿþû·nݪ=€T2} 'ÓHÓÕÑz¿'ª?švb¾þ2} '“HÕ÷û×_j[ÿÏ{ºôŸ@‘¸G. N& ÿp ŠÊ¢²¨l)%6Ówc1Nù¶,“Ð8PEeQYTvdÉôQ  XÛ²L @ÿ à@•EeQÙ‘%ÓG€bmË2)ý'€UT•EeG–Lе-ˤôŸTQYT•Yî‘‹Åú¡•Iè?¨¢²¨,*;²*4“Iè?€R’éP8™€þ(%™>…“IŒ®þ³¢Âþî¼óÎyóæòÅ˰råJåËìÓP8™>Àèê?eú…éïï?~üÞ½{Où’ìÞ½»¡¡!,¢ŒYî‘‹…“錮þsÐLÿ†þÏ:ÏgÕyä‘ùóç§N*¨¬¬¬©©™:ujKKKww÷Ÿìë¾ðÂg?ûÙš“fÍš^†=== ÇG[°`Aü< oll|óÍ7ÔÃgs,|ssóúõëmDà@•EeUvÌJl¦Ÿû0 J°-ËôÊ­ÿ”éfþüù7n8©ãÇ¿øâ‹·Ýv[CCÃk¯½ ܵkWccãºuëŽôÀ„w_yå•'¯œO§¿¿¿²²òÈ‘#ÑË0|îܹÑÄ-Ztß}÷e[øöööÔ?0TQY•EeÇ™> k[–éŒxÿ™LÇ/ÓU«V544TWW/Z´(NŠûûûÃË00¼ÕÖÖ¿oß¾æææšššªªªY³f8pàÄO?ij¸õÖ[ëëëÃ,XÐÛÛ›ëÐ"Ë2„'K—.­=)<‰‡‡ñ׬YÓØØXYY™qÖuttL™2%LÒ¤I<ð@ÆeÎsÕ²µäÀYMMM]]]Ùª´¶¶†UŽž‡'¡v©ï†—Ñ»aâ³gÏŽ>ýôÓaÅÿÑË0üïþî‡v¾à‚ ¢? œcwwwX8PEeQY•³dú(P¬mY¦0âýgŽLîܹ='…'·ÜrK4<çÑ?>j“Ä “öÁa„ÔásóãÇÇytÚ'NŒ?Þ××ÓY-²,Cxß]6@‰ûÏéÓ§‡áÇ?pàÀ‚ Ò®§ß{ÒÀëéGÃçÌ™____5þ†nˆ‡×ÖÖvwwdzkmm={ö¾}ûÂóW^y%5˜Îph‘szzz¢ex‘™Œ³Î(,@tŸ-[¶„UÈøÁºOÆf¹ï¾û–.]š:°¹¹yýúõ6"€1K¦@ádú%î?wîÜ9mÚ´ÊÊʦ¦¦{ï½75Óokk«¯¯¯®®^²dI4<< /«ªªÂ[a„xüŽŽŽÉ“'GÓY»vm<üž{î SHM[[['MšÆœ:ujê¹ê-²,Ñ#G–/_^}Ò²eËâáiáõÀYà¼óÎ «3eÊ”øÂ8iÌsÕ²µdÆY;v¬¡¡a÷îÝñ’GÂÃDn¹å–´ÿÍðâ‹/Κ5+Zå+¯¼2¾®NäÍ7ß Óî%ÐÕÕž§ÞSw` Ìž=;–!,I܆ŒA2} 'Ó(“þsH®)Ö¡EE’.Z[[ËáŠ7aÚÚÚlAc™{ä¢@@ádúeÒÊô¨¢²*‹ÊމÝíÎ}˜„%Ø–eú%ë?«««K¶ðÕ™Œì2d›€UT•UYdú(P¬mY¦ ÿp ŠÊ¢²¨ìÈ’é£@@±¶e™@Áýç©}( sFeQYT¶lÉôQ  XÛ²L`4öŸöÓ3*‹Ê¢²åÌ=rQ  X?´2}€ÑØ:ˆ¨¢²¨,*[Î*4“é$¯ÿ”é@9“éP8™>@òúO™>”3™>…“é$¯ÿ”é@9“éP8™>@òúO™>”3÷ÈE€ÂÉô’×Êôª¨,*‹Ê–³ÄfúE8åÛ²L Hýçу‡÷þ·„í§èQY•EeÇ™> k[–éŒxÿypë/7Oü\¡«£Ó~€•UYTv ’é£@@±¶e™>ÀHõŸGÞuÏ£?8ï/Â[áñ»ïn²Ÿ DeUV#¨ìØ$ÓG€bmË2}€á÷Ÿ·þò‹¾ýä;>ñô¤9íoÿèã•3~¿ñyûi:@T•UÙ1Ë=rQ  X?´2}€‚ûÏo·|ó{s—>ÑôÙMã?õÔû>žÿxÕGŠè;ˆ¨¢²¨,*[æ*4“é©ÿ<µ€²%Ó p2}€"õŸßnùæŸú«öqŸx¼rF”³·¿ý#OÔ^¾ÿé­ZÆ2™>…“é»ÿìþŸÿëÇ—ÿßQ²ÿøÛ>,Ö€1N¦@ádú¥é?<ü«ÿtï¦w_³ÿêƒOkC›Ü# 'Ó(qÿÙý?ÿצ÷ü!ÙïêèÔŒTQY•EeÇ Äfúní¥@À)ß–eúEê?<üòšö¾ß¿®%¨¢²*‹ÊŽ52}(Ö¶,ÓÐ8PEeQYTvdÉôQ  XÛ²L @ÿ à@•EeQÙ‘%ÓG€bmË2)ý'€UT•EeG–{ä¢@@±~heRúOª¨,*‹Êެ M@ÁdRúO ”dúN& ÿJI¦@ádRúO ”dúN& ÿJÉ=rQ  p2)ýg6wÞyç¼yó¢ç[¶l™8qbEÅ)8ú˰råʤÅ\K¦–I˃U•EeQÙѱ786“P  Û²L`ÌöŸýýýãÇß»woôrÆŒ?ùÉONÉ’ìÞ½»¡¡!,O¢ŽâF(ÓO+“–ª*‹Ê¢²£coPíQ  HÛ²L`ÄûÏŠ?ª©©™9sæ‹/¾8pøüùó8d×®]sçέ­­­ªªš1cƦM›Šr\ñ§)ó#<#~Y]]]º#œywssóúõëOá”~y.CZ™F{˃UTVeQÙ1B¦ÅÚ–eú#ÞƱé±cÇZ[[§Nš6¼§§gñâÅÍÍÍÑËÝ»w755­[·.:]zÇŽ ,(ÊqÅŸæ¹óçÏ߸qc¶w‹{„3`^ííí#˜\°¥ŸEžËV¦ÑÞòà@•UYTvŒé£@@±¶e™>Àˆ÷Ÿi±iUUÕÀá}}}ñyñ‹-Z»vm>‹tË-·ÔÕÕ7nÍš5Ñ}ûö577×ÔÔ„¹Ìš5+>÷¿££cÊ”)a“&MzàN¤ü/x1šššºººâeK}7ÛdûëÖ­  çM›¶sçÎhø‘#G–.]Z{Rx^flŠèåÀ% º»»Ãd3eʾÃÀU«V544„u Ï1Ç’„Fkll¬¬¬Ì¸ƒÎ7~™mÖýýýáeÞjkk‹ÇÏØ˜—áÖ[o­¯¯SX°`AooïÀ2cÅs´<8PEeQYT¶`î‘‹Åú¡•éŒxÿ™zžþwÞ9mÚ´´á'þ4Ó¯««ëéétyn¿ýöY³f:t¨¿¿ÿ–[n‰^pÁÇó -Z ?~|´`X¾|ùÀ8‘òdžïf›lgÁ‚aÂ[«V­ºä’·š¨¥¥eΜ9='Í;7¼Ì8ÇÔX|à f»øO¶L?Ì(žcÜ 9–dáÂ…qPžÏ9ò9>ã¬Ã“Ôáñø93uúwÜqGXòжa´%K–ÄUX¦_ñR^v ¨¢²*‹ÊŽš€‚ÉôJܦž ÝÔÔôÚk¯Åã'i×ÞI m³™0aBî¥?~|ܸqÑó0ß5kÖ¤žß}b(™~¶É†qâÀÃðx ñ²…' §YX¦Ÿù©¢"nØ0ǰƒ.É¡C‡]Ù ’ºðg ”:<ã,Ò3õ­‰'Æïëë«««ËV¦_q™>Àˆ“éP8™>@‰ûÏ86íîî¾úê«¿ÿýïÇÃ#ÕÕÕ©÷ÈÍó<ýŒilggçe—]VSSM¹²²2¾sçÎyóæ…)ŸsÎ9§0iÒ¤´‹º :Ù<3úŒ—:‘3ÓK2¤+Àä9ÇA—$ÿY¼²ù7fªx´e‘jË×þª& `2}€÷Ÿ©±iWWׄ ¢sÛ³åÈ‹-НŸCÆóô'NœøôÓO;vìÄÉKý œÅ–-[âS¹Þ#·½½=ãbg›l¶€8Ì"õäñø$ñÊÊÊãÇGÏ{{{sdú=öØîÔšãtõŒKR@¦Ÿcá‡tž~žF‹ÿÌ“»L#»âCmyòÚ_ÕL¦Pâþ3-6½á†î»ï¾ÙsäÝ»w755…q¢Ø÷…^8µèzúH½ž~}}}|Ýüð©xäE‹EزeK'X[[ÛÝÝOyÆ sçÎ͸ØÙ&›- Ž/%ßÛÛ;gΜøbîÓ§OMtüøñ0 Äã§-IÐÜܼ~ýú̇CÙ¯§ß{RƋڧ-IÚD.À@Ù>÷¬£áaÖñøÙ3mZ[[gÏž½oß¾ðü•W^‰/»?°L#»â9Z€‚¹G. N¦Pâþ3-Eýñ7`™FpÅs·<8P¥}¿}÷÷ž:òO‡T•UÙ1.±™~îÃ$(Á¶,ÓËýgkkk9\w%,C[[ÛÐŽ‘*NÙQRégZ¦‘{-TTWGg(Ó“ï¸üõçÿAeQY•³dú(P¬mY¦ ÿ•ÇHc)Ó/Ÿ¹ƒUò´ç‘„J…ÇÓvíKmë<¬²¶YTvÌí¯ª= i[–Iè?G£êêêœÔ@¥™õ©]qp JQuýÝϯúÈã§_úÌŸ]ûDÍÇ;çÿ§Ü§í«¬m•M™> k[–Iè?¨R Q¬°¿iü§žjøtŽÓöUÖ6‹Ê&Œ{ä¢@@±~heR÷ŸCz<~ú¥Ñ“מxnБ튗PÙQÍ(b&%ÓÐÀˆë~öOÔ^Þþö·NÕ¼òÃß5sû×WEçéú;«QM¦@ádRúO(±(ÐümþCšÿ¶ÿèÿú÷û7où×cÿ’ÿï¬6F5™>…“Iè? ”þñÑEçæ§ž˜?ÔßÙ»îºëþûïߺu«öF#™>…“Iè? dº::ïd{ÕGÒNÌ÷; Œ)î‘‹…³¯  ÿp Jiôýþõ—ÚÖÿóž.¿³ØfUvŒKl¦ïâh œòmÙ¾2€þÀ*~g±Í¢²#K¦ö•ôŸ8Põ;‹m•dú(`_@ÿ €U¿³ØfQÙÑA¦ö•ôŸ8Põ;‹m•Ü#ì+è?p êwÛ,*;:Thì+è?Àï,À¨ ÓÀ¾2€þüÎŒ2}ì+è?Àï,Àè ÓÀ¾2€þüΌö•ôŸ8Põ;‹m•›éçî¾Q À¾2€þª~g±Í¢²£ŽLì+è?p êwÛ,*;:ÈôQ À¾2€þª~g±Í¢²£ƒLì+è?p êwÛ,*;:¸G. ØWÐà@u;òú›?Ÿ÷Í£ÞÒïìÿxâ©ÔOa›EeG‹ M@ÁdRúO8å~>ï›á׳ã‚ùQ@ŸÏïì¹óî''ÍÏþ·j@`t‘éP8™€þN¹£Þì¸`~ëú;û훾¹¾ñª?ŒáõÎÓF™>…“Iè? ¤ÆúƒþÎ>üÞ™áß'&Í裑L€ÂɤôŸP&âX?<²…õG^3aýû?õ£'ŸÒhÀhä¹(P8™€þÀ*å#í" k[¶¯  ÿp Ê))eêExz~ójèGgñûµÍ¢²£šLе-ÛWÐ8PåT•2ŽõŸ¨þXèGïÞu×]÷ßÿÖ­[µ•m•Ü#ŠõC+“Ð8På”üÎæ~„ÙöööíÛ·k+Û,*;Uh†³¯œû]™€þN‰#¯¿ùHÃáWõÑ·Íÿ>üÞ™ßiùæ]wÝ~ayä‘ð#»k×.­ŒF2} '“Ð@ŠoŠ»þýŸZ{ë·×7^õ‡çW=ðÿþ×öööð »}ûö}ûöi(`4’éP8™€þÊMèoœ<çGO>~OÿONšý‡!vÍÿzî§»víÚ·o_OO¶F#™>…“Iè? ¬Ä~|SÜH|ËÜðoêp€QÇ=rQ  p2)ý'€UÊGÆ@?®¬Xß6‹Ê&Cb3ý܇I(P‚mY& ÿp JÉd;C?µ²b}Û,*›2}(Ö¶,“Ð8P¥4²ú++Ö·Í¢²£Lе-ˤôŸT)~ÆÊŠõm³¨ì¨&ÓG€bmË2)ý'€UJàçó¾™-ÐÏVÙ8Öÿù_ܪm³¨ìèâ¹(P¬Z™€þÀ*%pôÀ›?ÿ‹[³qŸ­²¹?…m•-[š€‚ɤôŸ@)Éô(œL @ÿ ”’L€ÂɤôŸŒ/‘ztutúF“é0¬ÝúÜïʤôŸ$æÇëØ±[<†ÿ-¹ñ]Wìz«/P÷ÈE€aíÖç~W& ÿp š˜/qüHeú]ÿãn±¾m•-Xb3ý܇I(P‚mY& ÿp š¤öÇT¦þýÍã·¶ûÄ߯ýÛ={ö'™>ÃÚ­Ïý®L @ÿ @b~¼Ê9mÏ1©™KaÉ?Óÿ?±¾k냑é0¬ÝúÜïʤôŸ$æÇK¦?R™~îGgg§XÈA¦À°vës¿+“И¯Œ™u[Û̆†3««+.œÒÛ»"ž|éKÓjk«Â#<‰‡‡ñW¯¾ª±±¶²ò´Š§üÝïÎjjzgUÕé]ô¾íÛç˜òN*<žyfÞ…¾7¬ÑYgÕ­[÷™lÓÿë¿þp}ýa´ë¯?ÿðá£}}7…vC›Ü}÷' øK@hçÍ›7‹õÜ#†µ[Ÿû]™€þÀjb~¼2Æå×^{öo|=<“––Ñð+.½æšÄÃÃËxü¿üË ã<÷Éõ×_~w÷Wûûonk›yñÅïtÊ#5©ñãk~øÃ/„']]Ë—-›žqú·ßþ±ðÙ0Í#GZ/žZ µA Ëôׯ_/ַͪ,9$6ÓÏ}˜„%Ø–eRúOªIjŸŒqù«¯.žÿîwKk£çáIxoh83¿»û«y^0'>q¾¿ÿ檪ÓòHMª©é«W_õÚkËr\EgâÄqñŠ÷ô|£®®:z>aÂ;R¤°LÿÁܰaÃ~ðƒmÛ¶íÙ³§§§Ç×Ï6«²¤’é£@@±¶e™€þÀj’ÚgÐëÅÇqyžÃó¿~ü2Ï)gRÛ·/¾îºsëêªÏ>ûÝÏ<3/ÛDRUVž6ÔÌ‘é?ôÐC?üp{{ûsÏ=÷ë_ÿúàÁƒ¾~¶Y•%•Lе-ˤôŸT“Ô>C:O?ux¶³é âóœòp&?žþ‹ñ Ù®§øðá‘z=ýø²òaø5×| ÛUïkk«öíûÊ‚ølSÁI-\8%º&Ïóϱ¾þŒŒÓ_¹òòÏ}îÏ÷îýrxþÛßþÇð‘ÔiF ¦YX¦ÿØc=øàƒ<òÈÆúÓŸîÙ³Ç×Ï6«²¤r\(Ö­L @ÿ à@5I?^ãò»ïþd}ýÕÕ•‹Oíë»)ÞÛ»bÙ²éa`x,]ú¡xxZƽjÕa„ŒÁw¶ >Û”GpR>:çÜsßSUuú…¾7¾öÎÀé¯\yùYgÕUVžöÁ6„DÃDB;„φ6 -SX¦ÿøã§^~çå—_öõ³Íª,©*4ÃÙ­Ïý®L @ÿ @b~¼Ë=†ÿ郒é0¬ÝúÜïʤôŸ$æÇK¦/ÓÊL€aíÖç~W& ÿ 1?^èêêJ)¼L(1™>ÃÚ­Ïý®L @ÿ @b~¼î2} ¸G.  k·>÷»2)ý'€ÕÄüxlxíº=2}Û,*[˜Äfú¹“P  Û²L @ÿ à@5Ií#Ó—éÛfQÙr ÓG€bmË2)ý'€Õ$µOfú'RñG2}Û,*›T2}(Ö¶,“Ð8PMRûŒŠL´ÿ'™¾m•”Lе-ˤôŸT“Ô>èÞÞ_úÒ´ÚÚªðOÂË8Rÿîwg55½³ªêô‹.zßöí‹3ÆîÇÉ6Ígž™wá…ï­®®<묺uë>“zJ~Ƴõeú¶YT6©Ü#ŠõC+“Ð8PMÒ×ÀzÅŠK¯¹æo¼ñõð¸öÚ³ÃË8R¿þúó»»¿Úßs[ÛÌ‹/~ÆL?ã8Ù¦9~|Íø…ð¤«kù²eÓ§mVeǬ MÀpvës¿+“И¯tccíï~·4zž44œGêñùõýý7WUž1vÏ8N¶i65½sõê«^{m™kïcœL€aíÖç~W& ÿ 1?^ƒFçq.Ÿ6÷»2)ý'‰ùñ@·´Ì¸öÚ³ßxãë‡ßxÍ5H½ž~Á™~¶i.\8%º&Ïóϱ¾þŒh`mmÕ¾}_‘écŠ{ä¢@À°vës¿+“Ð8PMÌ×Àº·wŲeÓ««+ÃcéÒõõÝ4üL?Û4}tι羧ªêô /üÿÙ»à(Î;OøÄŠ"ËŠ¬h—h9ÂÉ:âø?Æ„‹ÿ,޽—µ‰MX,ó.&áå¸K±>6—8„œÏë¸r„(¯Š×EQœÏüe Èf±Á‰²Ka'P«ÝøÖëõ‚W†Ø€e‰ ¥â(Pq:Þ'îM¿ãÑÌh$4£éÑçSS®™VO÷ÓÏoúé™/íîÆ×ÞY½ú÷Âl©‹÷~2}û,*[~Ê6ÓÏý3 а/ˤŒŸ~¨–Sÿ$ôÌw™¾}•-32}(Ô¾,“0~ø¡ZNý#p—éÛgQÙR ÓG€BíË2)ã'€ªåÔ?w™¾}•-2}(Ô¾,“0~ø¡ZNý#p—éÛgQÙRà¹(P¨­L Àø à‡j9¼î2}û,*[ ÆéÎåk}î¿Ê¤ŒŸ”ÍÁKà.ÓJL€súZŸû¯2)ã'esð¸ËôR Óàœ¾Öçþ«L Àø @Ù¼<Šóé¹Éô8§¯õ¹ÿ*“0~PfvíÚÕÖÖöôÓO?õÔS[( ™>ƒ{ä¢@ÀðɤŒŸ~¨Ž5Û·oß´iÓS ñØÿóÈS z8ôsèíƒúÔÙgU–Te›éçþ™„EØ—eRÆO?TËÏÞ½{wíÚµmÛ¶M›6=+ïøÂÓIú6ôpèçÐÛGŽñ©³Ïª,©dú(P¨}Y&`üðCµü8pàå—_Þµk×öíÛÛ’à¡+>Ó–4¡oC‡~½ÝÓÓãSgŸUYRÉôQ  Pû²L Àø à‡jù9räÈöîÝÛÑѱ+ þÛo]»+iB߆ýz»··×§Î>«²¤’é£@@¡öe™€ñÀÕòÓÛÛÛÓÓsäÈ‘ƒH‚5•Ÿ<4¡oC‡~½}úôiŸ:û¬Ê’Ê=rQ  PZ™€ñÀUT•EeGÖ8]À°É¤ŒŸ@1Éô>™€ñ(&™>Ã'“0~Å$Ó`ødRÆO ˜Ü#†O&`üðC•EeQÙb*ÛL?÷Ï$(¾,“0~ø¡ŠÊ¢²¨ìÈ’é£@@¡öe™€ñÀUT•EeG–L µ/ˤÆòø¹råÊyóæEÏwïÞ=iÒ¤qãFá×GhêU«ÊíWÜÈõdj™ô<ø¡ª²¨,*›ŒoƒjÚ—eúcvüìëë?~ü¡C‡¢—3fÌøñ<*-éììlhhí)«_q#”é§•Iσª*‹Ê¢²Éø6X®æ^ ŒúV¦0âãç¸ß¨®®¾õÖ[_{íµÓçÏŸßÝÝ¿eÿþýsçέ©©©¬¬œ1cƶmÛ ò»âý)ó† B3â—UUUÅû…3 ïnnnÞ¸qã(6 ø«È³ ieJzσª¨¬Ê¢²cÄ8]À°ÉôŠ<~Ʊé™3gZZZ¦Nš6½··wñâÅÍÍÍÑËÎÎÎÆÆÆõë×G§KïÝ»wÁ‚ù]ñþ™>@‘ÇÏ´Ø´²²ràô“'OÆçÅ/Z´hݺuù4iÅŠuuuµµµk×®¦>|¸¹¹¹ºº:¬eÖ¬Yñ¹ÿíííS¦L «hjjzâ‰'Φü_q3=·-õ¯Ùþº~ýúðÆ0}Ú´iûö틦Ÿ:ujéÒ¥5ï OÂËŒ]½Ø’ ««+,6óÏ¡LÙw˜¸zõꆆ†°¡ã5æhIè´ &TTTdlÀ ë_f[u___x&†?µ¶¶ÆógìÌŒmxàêëëÃ,Xpâĉe*Ćçèy†M¦ÀðÉôŠ<~¦ž§¿råÊiÓ¦¥M?ûþL¿®®®··wÐö<ôÐC³fÍ:vìX__ߊ+¢‰W^yeGGGXW˜¸hÑ¢húøñ㣆uww/[¶l`ΦücÃÀ¿f[l˜gÁ‚¡ áO«W¯¾öÚé¢åË—Ï™3§÷=sçÎ /3®15¸Ù.þ“-Ó+Š×wHŽ–,\¸0Êó9G>Gã3®:~¦Å¦÷Þ{ïc=v6{ŽÜÙÙÙØØæ‰bßW_}5¼eàÒ¢ëéwww§^O¿¾¾>¾n~xW<ó¢E‹¢ؽ{w˜'šXSSÓÕÕ/yÓ¦MsçÎÍØìl‹ÍÇ—’?qâÄœ9s⋹OŸ>=tQX΂ âùÓZ477oܸ1óÏ¡ì×Ó?ñžŒµOkIÚB6` lϽêhzXu<¶ÎLkCKKËìÙ³>ž¿ñÆñe÷–id7۪Óð2ôyøS˜!ž?[glCKKKSSS˜sêÔ©ñ¹ùË4‚ž»çÁUT•Ee‡M¦…Ú—eúcyülii)…뮄6´¶¶í7Ò¸Qû•TüU§–id×>Œž‡‘ròíw;¿÷Ü©wŽù¡ŠÊª,*[–Ü# u •é?ùi,eú¥³vYGÛ;ÂXÚVuã[m»üPEeU•-·ï«º€a“I?ËOUUÕ.j â¬zt7JÁ/¿ÿWa8 gknþé’oŸî9®O(2}†O&`ü(Y];úlí-ÏVÿî¯Ãýó®k¿jþNÛ€Ò$Ó`ødRÆO€RÅú[/¼%:gË®sÚ>I'Ó`ødR?=<<<< ôxö‚™Ñ“·žÙ5èÌX” ÷ÈE€fR2}ã'À(:¼í¥-×Gý.¾kû¿žýÂÇçþÃwžŠÎÓt4ÖåA¡²¨l™)ÛLß¡W€Qß—eRÆO€QúÏTÏl¿êžgkoù뻿ѵó§ÿçÌÿÎ4Ö‡cá°‹Ê¢²‰#ÓG€BíË2)ã'Àhyóñç£ÓóSOÌêhüíoûñÇß³gþ,ãÃ.*‹Ê&ŽL µ/ˤŒŸ£âh{GKŸ9ÿ¦´óÆ»¨,*[dú(P¨}Ù¯ ã'@ñ|ûÝ×[7þ¯ƒGÆäShT•M÷ÈE€Bhý 0~u"•EeËÌ8]€_AÆO£±Ñ€DéàW€ñÀhl4 dúïWÐéžã‡þì‡á¿º0~iüÀh ™>ÅøÔýãW¶ÿL˜r´½C¿H‘ŒÆ0<î‘‹üô­åÿå‰ßÿ÷mu·„çáñæ“;tÀY)€Ñ˜"A¨,*[fÊ6ÓÏ}`F€BïËÝ?~%üõûçÍØük·TÌx¦ú¦#;öè1€|¾ I‘ŒÆ­Ð¨,*›82}É}ùtÏñ¿û¯nûèm[*fDçæoþàŒ-Õ¿+ÐÈÿ»Pøë·¿ýíÇ|σ'ÀhŽÆ2ý±PhT•M™> œÓ¾ìááááQˆÇš5kÚÚÚ^yåÇ€Ñúe*ÓA ²¨li’é£@Àøö·¿~ð|XŽLŒA¨,*[fÆéÎÝž={v¾_˜òÊ+¯¼ö·?{ùáÿ±ããs£öÿéO·é+€|ÆÏýû÷>|¸··WŽL€$’éP ï¾ôóí“î ¿‹Ž¶wè JL€$’éP<§{ŽXÛvòíwu£N¦@Éô€±H¦@¹G. صŒŸc‘Lßq•Ee“¨l3ýÜf°k? ¿¹ÿ*ÓwœEeQÙ$ÓG»6€ñÀð›á¯2}ÇYT•-A2}°k? ¿þ*ÓwœEeQÙ$ÓG»6€ñÀð›á¯2}ÇYT•-Aî‘‹vmã'ÀX$ÓwœEeQÙ$§ €1H¦@Éô(¸ûî»ï"JÒÔ©So«î÷±ofþzÅW\vÙe×\sÍXè5kÖøÆ2} .”°OúÓ¾±$‚L€‚‹3ý”ŒhdnjjúôXu÷¸=œ]øë§>õ©ûoÿíŒ3Ê»>ò‘ÈôÄ=rQ À® \”é¯X±âW”Œ(Öÿæ7¿9fOƒ^O¿­­m×®](ï~¸ùæ›Ë;Ó÷;EeQÙ2S¶™~î3 صbk™~t꫸pÄ{oŒtl‰ofjó†ÑԒݺ±¹ÛÊôeú‘(Ó¿æšk:::öîݶ÷È‘#½½½~§ ²¨li’é£@€](¸ÑÍô‹Õå¹FÑÿXÈômóÀ†·™E뜒Êô‡½´‘êö¤\2}™~$Êô¯¼òÊíÛ·‡í}ùå—Ã&÷ôôø‚Ê¢²¥I¦vm àdú2}™¾L_¦/ÓOÜ××±–é_~ùå›6mÚ¶m[Øä½{÷9rÄïT•-M2}°k7ŒL?ʳ֭[wñÅWUUMœ8ñÛßþvê ò'2iÒ¤ÊÊÊð§ð<í­­­ ãÞ/ÿm`p™£1áehLøS˜aíÚµi‹úîw¿^†Æ\xá…‹/~óÍ7ãelXXÔÇ?þñ°¨ &|ó›ß^ÿ¤uBî Âr®¾úêèO¡ýƒ6æ?øÁµ×^{þùç‡?Íœ9óùçŸÏ==÷våî½|¶zß¾}a3/¸à‚®®®xæ°íaæW^yeàr2V$òï|'Gc²mEܰ¦¦¦ð×Ë.»¬½½=G•ÓÞ˜6C¶¥ ãã4p]iŸŠuÉÖK›:¤þϽuÙ>¥?Z·:LJð\º}DvÌB \çhéÒ¥2ýÜS™~øØ?õÔS›6mÚ¾}{GGÇÁƒýNAeQÙÒä¹(`× nØ™þœ9sÞ~ûí#GŽÜu×]áeœŽýÉŸüIxùÕ¯~5<ÿÚ×¾žÇéXôÆ%K–twwŸËYóþl‰¢Ïè¯Ax’¶¨o}ë[Q²ùôÓO‡é_üâ³5,̦|á _8vìØ7¿ùÍð|åÊ•Ã蟣Ǣöö³Ÿýå/ùÎ;ï|ùË_´1&L/wìØžïܹsöìÙ¹§çXÔ ½—çVGÏyä‘hÎmÛ¶E•q9Ù*ÒÚÚ^Þyçaù¡7BûS“c+¢Ù/^:R;f!®sñ‹_ü"Z‹L?Ç_ÇÚµwžzê©°Ë—ßVûâ(*[fÆé mØ™þ믿½Ü¿xùñÓ(32} nØ™~Ú”ÊÊÊlçÔgûÓfúy6&mÊ~ðƒn¸áüóÏO½žFî7¦Êæ»IƒÎϳÿóï«Ôâæþh¥.?ÛœçÒíÚ1Gdà:Ç@ÿšk®yçwÆòáI¦‘é$‹L€‚ñóô£ËkDgÔf<Ýux™~œÓ½ýöÛ#už~tUñè„åcÇŽåa£”->SxØý“í‚${,jÿ«¯¾šñ<ýl Ûò—ù—Ñ•s~û·;÷ô‹©óô£:ºDÌ÷¾÷½ðäÑG͸IkLè“çégÜŠ¢eúCú8 º¢[”çŠÒþ½!Ÿþϱu9>¥Ù>Z·:Û‡sØÝ>R;f!®á]r'ô{zzÆøáI¦‘é$‹L€‚v¦×]w½ýöÛï¼óÎÝwß^~ç;߉þúõ¯=¼üÚ×¾žõ«_xYê´kq„)û÷ïϽÆ(ÏݶmÛ±cǾøÅ/ .³5&º{øë‘#GÂ_Ó®ÿÑ~4<®ÝŽ2þÓÀ†…eF—íîêê ó?ÿüó·ß~û0úg`'äè±(ú¼óÎ;ß|óÍÔëéçhLXÝ_ÿõ_‡'ÿóþÏèÚ¹§çXTîÞÒV×\sMUUÕÍ7ß@²¸G. ص‚v¦¿nݺK.¹¤¢¢b„ ©Ñmt‡Éè¤×‰'ƹXÆh¬µµõÃþð '½¶··_vÙea]MMMa½ƒË /ÃÄð§0ã>šúÞ;vD‹4iÒêÕ«Óä [»víUW]毪ªºýöÛs_9$[“2nl¶ B›¯¾úê°œ†††Ð€A³uëÖ™3gV½ç†nعsgîé¹·+Gï õSÝÍ5ºçg¶Bç¨HÔ˜Ð?©I½ÊJ¶­Èúñ8CŽ¥ õã4èyßÙ¶(ÇŠr45Ÿþϱu9>¥Ù>Z·:LJpØÝ>R;f!.þ9’éGÜ#•Ee“¥l3ýÜf°kÅ4"×ÓE%Õ˜’mR‰¸ð /¸à‚·ß~ûܵoß¾ÐÉ_|±^•þO¢RÞ1ãkè ôóÿú*Ó÷;•EeK“LìÚ@ÁÉôeúűmÛ¶Ð-ñ僆áî»ïþ›¿ù›èÊþ·ß~{ŽëÂSˆþ—é:пì²ËÂgÛQ)ϯ¯2}¿SPYT¶4ÉôQ À® Ü02ýèÒ…ȶª2­ÆœãV‘SM˜0!ºÜJ>÷2ÍæÏÿüÏ?ùÉO†åœþù7ß|sx©c‹ÙÿIWš;f|Sܦ¦¦·ß~Û!)ÿ¯¯2}¿SPYT¶4ÉôQ À® \”éG±>EßâxÒ¤It<Ò×W™¾ß)¨,*[šÜ#ìÚ@ÁÅ™>Å×ÔÔôÆo8 56’éû‚Ê¢²¥iœ. Ð¦N…Jߠ蜡ŸL?Rö™>@™‘éPpQXðéOZWP:dú©‡i™>@RÈô(8™>%H¦Ÿz˜–é$…L€‚“éP‚dú©‡i™>@R¸G. ص‚“éñ3:íI©5,½7Š ,ŽÊÝ’Ý»wOš4)ÏÖu£†4)ôØÊ•+çÍ›—ÜO~1…ŽZµjUüR¦Ÿz˜v\T•MÌWîrݰÜf°kÅ dúcaü,³L¿È[1R«;—*”Ns7`ÆŒ?þñ ´-EÈôG°{ûúúÆèСDò#EXxgggCCCè´|†_™¾ã,*‹Ê–èWnµG»6Pœ°@¦_èñ3Ž®ª««o½õÖ×^{màôùóçwwwÇoÙ¿ÿܹskjj*++g̘±mÛ¶soCÚ“‚üŒIòéççÒÈ‚fÓCzKú3÷*ªªª ×É:OÆ a¿.ïOþ¹/-ubssóÆó~eú޳¨,*[¢‡NµG»6Pœ°@¦_èñ3­Îœ9ÓÒÒ2uêÔ´é½½½‹/nnnŽ^vvv666®_¿>:euïÞ½ ,©6Èô ±]2ýá5 ¼3ýùóçoݺµ¼?ùç¾´Ô‰mmmñ¿‚ÈôSÓ2}T•M ™> ص"…2ýBŸiIVeeåÀé'OžŒOp^´hѺuëòYòŠ+êêêjkk×®]M9|øpsssuuuXˬY³âsÿÍô3Nïëë khhhmmM稯¯Z°`Á‰'Φüo¹sÀü—Œ?¾··7ž!L ï SRß5°26/hooŸ2eJ˜ØÔÔôÄOŒH#3nxþUÈ¿Iù¼7ÏähÞúõëÃôiÓ¦íÛ·oÐÞØÈÔ éÓ˜±dù¯º?Z¡'=š”Oþ {ôrõêÕ¡%aQa[N:MO–.]Zóžð$žæm›0aBEEE¶gêË®®®Ðiù ¿2}ÇYT•-Mî‘‹vm HaL¿Ðãgêyú+W®œ6mZÚô³ïÏôëêêRã¼lzè¡Y³f;v¬¯¯oÅŠÑÄ+¯¼²£££¿¿?¬+L\´hQÚº†”é‡%Ì;÷ĉ¡=ÍÍÍñ<?üðœ9sªÃZ–,Y²lÙ²Ü ¸ÌÞ÷„'¹—ùå/¹µµ5~ox~ÿý÷§®(c'dkÞøñãwîÜžtwwÇϽ‘7<ÿ*äߤ<ß›O r4oÁ‚aëŸV¯^}íµ×æîA?Hù÷C¶ŽÒªKí£ÿë]">ùyfú©›¯wùòåa½ñôð2žáÂ…ñ?-䳊x”駦Ý#•Ee“bœ. ÐdúEúrŸ¢±±ñ­·ÞЧGOÒ®½“f3qâÄl·ßŒô÷÷×ÖÖ¦­kHÓ˜0aÂáÇ£çáIüÞI“&Å[qòäɺººüš¿7´?÷2ßxãø¼Ý`òäÉQ{âweì„lÍ ‹Z»vmê©Ó#ÒÈÜž» ù7)Ï÷õb)i͋ϰÓãÏa¶ÞÈÝÎ!õC¶ŽÒªKí£•¶#'ⓟ»šáeê&„ñ!(âÆ„' ñüÇŽËýÙéçs˜.ãL Ü¾öë M¦_¤/÷¿ ­ºººn»í¶çŸ>ž©ªªJ½Gnžçég È:::fΜY]]-¹¢¢"mæs¹"yêBR \Ë.óŽ;îˆnþ»xñâ|6'Û¢öíÛ7o޼н—^zi{{{á6<ÿ* £I¹ß›O m^îNÎ?ÓÏ¿òìØ!ýs¨´šššRcôD|òóìÒHÆËˆå˜>h¦ºËµw2¦eú‰ùÚ¯ (4™~‘¾Ü¿?´š8qbt6t¶€rÑ¢E©ÈÎ&Û‰º;vì8sæÌÙ÷.õ“1<Í¿å©çé¿õÖ[©gÇÿ‘mKs4;ÛÙÊ—¹{÷î›nº)< ÿ}íµ×ÒV”­2.*u™ñ)Æ#ÒÈ´ FmRžïͧƒ6o`'ï<ýüû![ÇŽÔyú£òÑš?~[[[²>ù¹?Ø9ÎÓOžzžþ ãCêÄÍ›7»GnÆÃ´L 1_ûu…&Ó/Ò—û÷'Y÷Þ{ïc=v6{üÚÙÙÙØØæ‰ÂÐW_}5¼eàÒ¢ jwww§^P»¾¾>¾pvxWþ™~¶ëé777G7íœ7o^1¿í½(?zÀ’駦Ý#•Ee“bœ. Ðdúiâ˜ÞÃÃÃÃct¹Çj™>%H¦@ÁÉôÓ š"î9¾ÿ‘ï¿ðoænýÈ­ÏÔÜOmºñðs?Ñ{Eeú” ™>'ÓO3hŠ?÷¥Ÿÿín«ºqÛG_¬PäÑX¦@ ’éPp2ý4ƒ¦HÛ·oïèè8xð`4%>m?:a¿óñçõ!@Fc™>%È=rQ À® œL?ͰS¤w_úùöÝf8ÚÞ¡Fk4.Ëô{䢲¨lR”m¦ŸûÀŒvm øaL?ÏsÐétÏñkÛN¾ý®žÅѸÌÓeœéûâ(*[fdú(`׊Èôó0ÇNŠ`4.ôL•Ee“B¦vm HaL?ÏS¦`4.þaZ¦Ê¢²I!ÓG»6P¤°@¦Ÿç€)Ó0ÿ0-ÓGeQÙ¤p\°kE dúyþP‘鋘v\T•MŠqº€B“é§‘"Kí0]Æ™>@™‘éPp2ý4R$£q©¦eúI!Ó àdúi¤HFãR;LËô’B¦@ÁÉôÓH‘ŒÆ¥v˜–é$…{ä¢@€](8™~šÑJ‘Btþð¬\¹rÞ¼y£Ø€°öU«V)”Çh\š‡i÷ÈEeQÙ¤(Ûïô¹Ì(`׊Èôó0eú¥¦¯¯oüøñ‡Å6tvv644„–(”Áh\š‡é2ÎôýNñ •-32}°kE dúy˜£˜ébè?ìUçùÆsÙ´ 6ÌŸ??uQ±êêê¹sçÆqƵÄ>I›-¨¨¨Ëœ:uêòåË»ººRghnnÞ¸q£ÝÊ`4.ÍôL•Ee“B¦vm HaL?ÏS¦_ˆ7žË¦ÍŸ?ëÖ­uòäÉ|pêÔ©9Ö’¦=éïïíµ×ÂbÞzë­x†ð©Hý§ ¹£qi¦eú¨,*›2}°kE dúy˜9R¤´,85#^½zuCCCUUÕ¢E‹N:Mïëë /ÃÄð§ÖÖÖxþÇ777WWWWVVΚ5«»»ûìûO?WñÀÔ×ׇ%,X°àĉ¹~ZdiCx²téÒš÷„'ñô0ÿÚµk'L˜PQQ‘qÕµ··O™2%,¿©©é‰'žÈØæ<7-[O\EÐØØxôèÑlUÂüÙþtvè™~¬¥¥%ôdü²««+´Äî¥0—åaZ¦Ê¢²Iá¹(`׊Èôóü¡2¼Lîܹ½ï OV¬XMOR§Çó_yå•ýýýgΜ óÄÁqÚò~øá9sæ;v,̶dÉ’eË–åúi‘¥ Ë—/ ‰§‡—ñü .Œÿ ŸóèÇ¿sçÎ𤻻;nLÚóÜ´l/3®¢²²2Û{Cû|ðÁ™3gæØŠagú¡ÇjkkS§ÄÿxŒîh\–‡i÷ÈEeQÙ¤p, N¦Ÿ¦™~|‘–C‡M˜0!z>qâÄÔé£äþþþ88N›aÒ¤IñÛOž”Èh\–‡é2ÎôÊŒL€‚“é§)D¦Ÿ:=‚³ÍßÑÑ1sæÌêêêø¾¬ÙæOÏ–ù§E~m´m9ìÛ·oÞ¼yuuu—^zi{{{Æ7æ¿i_f\ESSS¶kïôõõµ´´ä>O`òÌô;–úï(¡ ®½%2—åaZ¦2} N¦ŸfØ)REEEôüĉÃ>OÒ¤I;vì8sæLxþ›-k³E×£Ïë§EöóôS§§ž§Ÿööü;p÷îÝñò¶9ŸMËÖ“W1þüP‘MOŸojjzýõ×Sÿ^ÆAüP3ýU«V¥^OóæÍî‘ %2—åaZ¦2} N¦ŸfØ)ÒôéÓ׬YÓßßßÝݽ`Á‚´ëéŸxÏÀëéGÓçÌ™Ï____5þÞ{ï§×ÔÔtuuÅ«kii™={öáÇÃó7Þx#5_ÎðÓ"gz{{£6¤^O?õíi«Î(4 ºŒÏîÝ»Ã&d|cž›–­'3®bÓ¦MaR·4~~æÌ™U«VM›6-î±3füìg?‹^îÝ»÷†nÓÞ˜;Ó­zýõ×|ðÁÔ š››7nÜh÷RËò0-ÓH ÷ÈE»6Pp2ý4ÃN‘öíÛ7mÚ´ŠŠŠÆÆÆG}45#nmm­¯¯¯ªªZ²dI___4=< /+++ß ñüííí“'OŽ–³nݺxú#<–š8·´´455…9§Nšz®z†ŸYÚpêÔ©eË–U½ç¾ûî‹§§åÚW=PhÀå—_6gÊ”)ñ…qÒÞ˜ç¦eëÉŒ«8sæLCCCgggÜòXXæìÙ³ã?k×® ï­|Ox^¦vÑÀ%DR'†e†¶­X±"õ’«mˆ{ÝѸ,Óî‘‹Ê¢²IQ¶™~î3 صâ‡2ý<Ìa¤HCºpM¡~ZŒ+çÿ ¸¥¥et¯{ÖÞÚÚjß}˜.ãLßï¿@QÙ2#ÓG»6P¤°@¦Ÿç€)ÓHèhœèôL•Ee“B¦vm HaL?Ïs)R|Ö"¨ÊddÛm%>'ú0-ÓGeQÙ¤é£@€](RX ÓÏsÀ;)€Ñ¸tÓ2}T•M ÷ÈE»6P¤°@¦Ÿç•ð×Ñ}(`4>+Ów\T•-U.y @ÁÉôÓ”rŠ$ӌƥ0ÿ0]Æ™>@™‘éPp2ý42}£q©¦eúI!Ó àdúidúFãR;LËô’B¦@ÁÉôÓÈôŒÆ¥v˜–é$…{ä¢@€](8™~™>€Ñ¸ÔÓî‘‹Ê¢²IQ¶™¾Ÿ" صR dúy˜2}£qñÓeœé;´ùŠÊ–™> ص"…2ý<L™>€Ñ¸ø‡i™>*‹Ê&…LìÚ@‘™~žæ )Òéžã‡þì‡á¿s€QËì0-ÓGeQÙ¤é£@€](RX ÓÏsÀÌ‘"õìùÅöÝf8ÚÞa0­Ñ¸,Ó2}T•M ÷ÈE»6P¤°@¦Ÿç•)Òéžãûùþ 7‡?…GçãÏûPüѸ¼Óî‘‹Ê¢²I1NPh2ý4ù§H={~ñ·_xø™ f>×pû–ófl©¼þðs?ŶŒÍÑx,¦Ë8Ó(32} N¦Ÿfðéñ§_ø£om»ès[?rë³µŸŽNÏoûÐ…ôÏÊô£qêh,Ó ôÈô(8™~šAS¤Ñ}(`4>+Ó—é”*™>'ÓO“Ïyú;þÃ7·ŽÿL[ÕqÔþÌ3ìØ£÷Š7Ëô(=î‘‹vm àdúiòO‘Þ}éç»ïøÊ–Êë·|ðz±>ÀhÆcá0í¹¨,*›e›éû_†°k¥Èôó0¦H§{Ž¿Þ²á¹ß™°ÿæ“;ô!@ñGãò>L—q¦ïwŠ_ ¨l™‘é£@€](RX ÓÏsÀÌ‘"½ûÒÏŸk¸-Ìp´½C7ŒÖh\–‡i™>*‹Ê&…LìÚ@‘™~žæ )ÒéžãÖ¶|û]= 0Š£q™¦eú¨,*›2}°kE dúy˜c'E0—ÎaZ¦Ê¢²Iá¹(`׊Èôóü¡"Ó0ÿ0í¹¨,*›ãt…&ÓO#E0—ÚaºŒ3}€2#Ó àdúi¤H…sòíw;¿÷Ü©wއt˜–é$…L€‚“é§‘"ÔÑöŽ0–>[só»/ýÜhœçaZ¦2} N¦ŸFŠPh¿üþ_…á4<¶ÿëÙ¯·n<ÝsÜhœû0-ÓH ÷ÈE»6Pp2ý4R$€"èÚùÓ¶ÝÕm½­­êÆ¿¾ûi§íSÓî‘‹Ê¢²IQ¶™~î3 صâ‡2ý<Ì_ŸUº}{GGÇÁƒõÀ¹ˆcý_ôU7þEý¿K=m_¦Ÿz˜.ãLßï¿@QÙ2#ÓG»6P¤°@¦Ÿ:`zxxxxŒîã­gvÉôSÓ2}T•M ™> ص"…2ý¶oßÞÖÖöПiû0e×®]/¿üòzzztÀ¹èÚùÓg.˜¹¥bF”ã?sþMÛÆÿþß=ðß§?ð0-ÓGeQÙ¤é£@€](RX Ó¨££c×®]ÿí·®ÝõaÊÞ½{8päÈ‘ÞÞ^]0lÿèŸ7cKÅõ[>tã®™ÿñýÍÿ9ó¿óüú*Ó÷;•EeK“{ä¢@€](RX ÓèàÁƒøÁ—Wø0åÈ‘#===½½½§OŸÖEÃóËïÿUtn~ê‰ùidú©‡i÷ÈEeQÙ¤§ (4™>Åt´½ã׉ü‡nH;1?L?õ0]Æ™>@™‘éPp2}ŠæäÛï¾Þºñ<:èœ2ýÔôL )dúœL€$ÓO=LËô’B¦@ÁÉô(A2ýÔôL )Ü#ìÚ@ÁÉôŸ%H¦Ÿz˜v\T•MвÍôs˜Q À® ?,é?4üÊôgQYT¶4ÉôQ À® ),é?4üÊôgQYT¶4ÉôQ À® ),é?4üÊôgQYT¶4ÉôQ À® ),é?4üÊôgQYT¶4¹G. ص"…2}ã'@I‘駦Ý#•Ee“bœ. ÐdúcèƸqiOйÒ2[×È6`åÊ•óæÍ+Í*K¡·W­Z¥JœL?õ0]Æ™>@¹}åÖšL ýÀ鸅Ãëá¾¾¾ñãÇ:t¨47ª˜å‹aá ¡ç ¥L¦Ÿz˜–é$æ+. ÐdúEúrÿÕÕÕ·Þzëk¯½6púüùó»»»ã·ìß¿îܹ555•••3fÌØ¶mÛ¹·!íIq6ügÁuúÆfœmÆ ¡ô%»Q¥Ö{ç²´Ô‰ÍÍÍ7n44•2™~êaZ¦˜¯4º€B“ééËýoÂÄ3gδ´´L:5mzooïâÅ‹›››£—ëׯN%Þ»wï‚ Fª 2ýÑÚØŒ³ÍŸ?ëÖ­%»Q¥Ö{ç²´Ô‰mmm9þ)…R ÓO=LËôó•¦\7̽°k¥C¦_œñ3-a¬¬¬8ýäÉ“UUUÑóE‹­[·.Ÿ%¯X±¢®®®¶¶víڵєÇ777WWW‡µÌš5+>÷ÐLÐ4m!«W¯nhhm­=uêT4½¯¯/¼ ßZ[[ãù3¶j\Šx<ð@}}}X‚ Nœ8‘cÛ³­kàB‚ñãÇ÷ööÆ3„)á]aJê»væÚ3°‡ÛÛÛ§L™ÞÛÔÔôÄOdÜäÆÆÆ£G–ÂF lmžŸçA?Ãû¤e\Tx²téÒš÷„'ñô0ØÀ &TTTdü\¥­¢««+ô¼!®”ÉôSÓî‘‹Ê¢²IQ¶™~î3 صâ‡2ýBŸ©çé¯\¹rÚ´iiÓϾ?Ó¯««KÍj³y衇fÍšuìØ±¾¾¾+VD¯¼òÊŽŽŽþþþ°®0qÑ¢Eië©Lîܹ½ï Oⵇ'©ÓãùmUäᇞ3gNØ¢0Û’%K–-[–có³­+ãB¾üå/·¶¶Æï Ïï¿ÿþÔdìÌ!µg`?~çÎáIwwwüÞlÿÀ3ꕱµù|TýT ï“–qQË—/§‡—ñü .Œÿ}"ŸUÄ»I~eú޳¨,*[šdú(`׊Èô =~¦ž8ÜØØøÖ[oÅÓ£'i×ÞIËy³™8qb¶{«FúûûkkkÓÖ5¤‹œäHoã­m˜0aBܤÔéו±U‘I“&Åo?yòd]]]îÍϸ®Œ yã7RÏËžçòIËýôƒ—mbès×ÞIôð+ÓwœEeQÙÒ$ÓG»6P¤°@¦_èñ3-Lœ8qbtðlñú¢E‹R¯~žM¶³°wìØqæÌ™³ï]êg`Þ:¤L¿¢¢¢¿¿?z~âĉaŸ§?h«âÙâØÈgó³­+ãBvïÞ}ÓM7…'`½öZZ²ufþíÉÑÃaÕq ¼Gn[[[ImTjkGäS1Ô}älÎóôS§§ž§Ÿci'nÞ¼Ù=r=üÊôgQYT¶4¹G. ص"…2ýBŸi ã½÷ÞûØcͯwvv666†y¢üÕW_ o¸´èjéÝÝÝ©WK¯¯¯¯ŠÞ•¦ŸqúôéÓ׬YÓßß–¶`Á‚´+§Gwjx=ýhúœ9sâù³µª¦¦¦««+^]KKËìÙ³£ ȼñÆñe÷3ʶ® ™:uêúõëo»í¶<;sHíØÃaþ(Rß½{w茛¼iÓ¦°¥°Q[›ÑP?ù|Ò2^O?Û¬··7êŸÔëé§¾=­“ÎÓÜܼqãFC\rc#™¾ã,*‹Ê–¦qº€B“é¬Ó=ÇýÙÃÓ¿Ü¿?p|ñÅg̘q6ç)óû÷ïonn®®®®¨¨˜>}zt}•K[¾|yMMMmmm|^{{ûäɓû×­[wŽ™þ¾}û¦M›-íÑGM]Hkkk}}}UUÕ’%Kúúú¢éáIxYYYþfˆçÏÖªGy$,!uÕ---MMMaΩS§¦žÀ>P¶uåXÈúõëÃêvïÞgg©={8Ìùå—‡N™2%¾šMÚ&Ÿ9s¦¡¡¡³³sÔ7*ck3ê§"ŸOÚÀL?ã¢N:µlÙ²ª÷Üwß}ñô´·ü\¥Îz;ôy¶æQ"dú©‡é2ÎôÊŒL€‚“锞=¿Øþ±;6»öh{ǘø¹2ΖÓÒÒR×9—†®.3ÈIDATOE>Ï©æ…ÞnmmõÁ+q2ýÔôL 1_‡t…&Ó?G§{Žïäû/\ܼyܵáÑùøócåçŠLŸýTTýF!>`i 'dú©‡i™>@b¾é M¦?l={~ñ·_xø™ f>×pû–ófl©¼þðs?;›_Ìx´*“ÑÝö’jOéléöƒü™~êaZ¦î‘‹vm àdúC?ãó·~äÖgk?žßö¡ÇT Ph2ýÔô{䢲¨lR”m¦ŸûÀŒvm øaL?uÀÌëqÞŒ|çôððððÖ#÷X-Ó÷;•EeKLìÚ@‘™~žføëéžã¯·lxîwfµUÝOÏ\0óÈŽ=z h£±LßïT•-A2}°kE dúy˜©}÷¥Ÿï¾ã+[*¯ßòÁëÅúEeú~§ ²¨l ’é£@€](RX ÓÏsÀ ݾ}{GGÇÁƒ£)ñiûÑ ûo>¹Ca4–éû‚Ê¢²%È=rQ À® ),éçùC%GŠôîK?®á¶0ÃÑöÝ0Z£qY¦Ý#•Ee“bœ. ÐdúiÎ1E:ÝsüÀÚ¶“o¿«'Fq4.³ÃtgúeF¦@ÁÉôÓH‘ŒÆ¥v˜–é$…L€‚“é§‘"Kí0-ÓH ™>'ÓO#E0—ÚaZ¦î‘‹vm àdúi¤HFãR;L»G.*‹Ê&EÙfú¹Ì(`׊Èôó0eúFãâ¦Ë8Ó÷;Å/PT¶ÌÈôQ À® ),éç9`ÊôŒÆÅ?LËôQYT6)dú(`׊Èôó0eúFãâ¦eú¨,*›2}°kE dúy˜2}£qñÓ2}T•M ÷ÈE»6P¤°@¦Ÿç•Â¥H¡ :xV®\9oÞ¼’jRhϪU«”’8—æaÚ=rQYT6)|§ àdúidúÉÒ××7~üøC‡•T«:;;BÛ7—æaºŒ3}€2ã;='ÓOS²™þ(†þÃ^užo<—MÛ°aÃüùóÓ&®^½ººº:ü7múþýûçÎ[SSSYY9cÆŒmÛ¶Ek(š~èС‰'ž9s&m9'OžlllüÕ¯~•ñ‘æææ7Ú¡ q£qi¦eúI!Ó àdúidú#¸ê"dúóçÏߺukÚÄK/½ô±ÇûÄ'>‘:±³³³±±qýúõÑéó{÷î]°`AŽfD/ï½÷ÞuëÖ¥-¿µµuéÒ¥¹[>'ÿ±(ýѸ4Ó2}€¤éPp2ý4ÃN‘2&ÂѓիW744TUU-Z´èÔ©SÑô¾¾¾ð2L jmmç?|øpsssuuueeå¬Y³º»»Ï¾ÿ\òx<ð@}}}X‚ Nœ8‘ë§E–6„'K—.­yOxOó¯]»v„ W=P{{û”)SÂò›ššžx≌mÎsÓ²õäÀUGMÿÅ_¼á†“3fìܹ3ž6|`:?h_{íµK/½4uzh@ggçÙœ™~WWWh› Š?—åaZ¦î‘‹vm àdúi ‘éÏ;·÷=áÉŠ+¢éáIêôxþ+¯¼²£££¿¿ÿÌ™3ažE‹e\þÃ?žß’7wÏTUUÙ¡ ø£qY¦Ý#•Ee“¢l3ýÜf°kÅ dúy˜ÃËôßzë­èù¡C‡&L˜=Ÿ8qbêôŒépmmmÆåOš4)~ûÉ“'ëêêrý´ÈÒ†ð$¾»lxÒÐÐÏìØ±l›–QccãÚµkÓN™ÏñÆ›–íeÆU¤eúa3CÏ„…G«HÝÀéžìèèˆNüL›6íg?ûY*‹Ê&…LìÚ@‘™~žfŽ©¢¢":=<8qâİÏÓŸ4iÒŽ;Μ9ž‡ÿfû·0[t=ú¼~Zd?O?uzêyúioÏ¿wïÞ/`›óÙ´l=™qóçωž‡e¦nÑÙ÷NÛ-mÑ¢Ek×®ÍÝKÙ^þå_þå¬Y³Î¾wž_|1ŸžÙ¼y³{䨌Æey˜–飲¨lRÈôQ À® ),éç9`æH‘¦OŸ¾fÍšþþþîîî ¤]OÿÄ{^O?š>gΜxþúúúøªñ÷Þ{o<½¦¦¦««+^]KKËìÙ³>ž¿ñÆñµé3ÿ´ÈÙ†ÞÞÞ¨ ©×ÓO}{Úª3 ˆ®r³{÷î° ߘç¦eëÉŒ«Ø´iSØ„èùÆ›››Ó6kÖ¬§žz*<éììlll|ì±Ç¢TxõÕWCÒz)ÇËiÓ¦mÛ¶-ü7Ç<©BKB{ìPPüѸ,Ó2}T•M ÷ÈE»6P¤°@¦Ÿç•)Ò¾}û¦M›VQQÑØØøè£¦fú­­­õõõUUUK–,éë닦‡'áeeeeøS˜!ž¿½½}òäÉÑrÖ­[Oä‘GÂRCä–––¦¦¦0çÔ©SãsÕ3ÿ´ÈÒ†S§N-[¶¬ê=÷Ýw_<=-ª¸êB.¿üò°9S¦L‰/Œ“öÆ<7-[Of\Å™3g:;;Ãón¸áÇ?þqZÃvîÜ9cÆŒèùþýû›››«««Ã§OŸ¾mÛ¶´^ÊñrãÆuuui1}¶ëé‡ö„VÅý s4.Ëô{䢲¨lRŒÓšL?͈§HCºpM¡~ZŒ+ç---¥v•›ÐžÖÖV{”ÔhœèÃtgúeF¦@ÁÉôÓÈôÊr4NôaZ¦¾vPp2ý4#ž"UUU­ñU™Œl²­ ÄGãD¦eúI!Ó àdúi¤HFãR;LËô’Â=rQ À® œL?Í )Òè>0Ÿ•é»G.*‹Ê–ª²ÍôýQ À® ”ZX ÓÏsÀÝÉ`Ka4.þaºŒ3}‡6¿@QÙ2#ÓG»6P¤°@¦Ÿç€)Ó0ÿ0-ÓGeQÙ¤é£@€](RX ÓÏsÀ”鋘–飲¨lRÈôQ À® ),éç9`ÊôŒÆÅ?LËôQYT6)Ü#ìÚ@‘™~ž?TdúFãâ¦Ý#•Ee“bœ. ÐdúidúFãR;L—q¦PfdúœL?LÀh\j‡i™>@RÈô(8™~šsL‘N÷?ôg? ÿ-~ÛŒÆåz˜–é$…L€‚“é§vŠÔ³çÛ'Ýf8ÚÞ1*m0—ëaZ¦î‘‹vm àdúi†š"î9¾ÿ‘ï·_9?ü)<ÞX÷Ìhµ `,Æå}˜v\T•MвÍôýQ À® ”ZX ÓÏsÀLM‘zöüâ§‹¾õì…·´_ñ‡[ënÝrÞu‡þüGs€"Æcá0]Æ™¾C›_ ¨l™‘é£@€](RX ÓÏsÀüuŠôøÓ/üÑ·žÿÄ]Ïý«Y?¼lÞ–^&n©¸¾Ð¾Á0¿o4–éû‚Ê¢²¥G¦vm HaL?Ï3ºÀÎ(>0Ÿ•éËôQYT¶TÉôQ À® ),éç9`Fçé?ÿ¹?~¦ææÍø—œýÙßülí§ìØ£÷Š7ËôýNAeQÙÒã¹(`׊Èôóü¡’š"~î'5}áæó®ûõÄóW¬0*£ñX8L»G.*‹Ê&Å8]@¡ÉôÓ 5E:ÝsügK¿»µö–èœý7ŸÜ¡Š?—÷aºŒ3}€2#Ó àdúi†"~î'Qÿ{a†£íº`´Fã²@RÈô(8™~)€Ñ¸ÔÓ2}€¤p\°k'ÓO#E0—ÚaÚ=rQYT6)Ê6ÓÏ}`F»6Pü°@¦Ÿç€)Ó0ÿ0]Æ™¾ß)~¢²eF¦vm HaL?ÏS¦0‚N¾ýnç÷ž;õÎ1£qîôL•Ee“B¦vm HaL?ÏS¦0²Ž¶w„¡õÙßüîK?7g;LËôQYT6)dú(`׊Èôó0eú#îÍÇŸ£kx¼ðñ¹¯·n<ÝsÜhœv˜–飲¨lR¸G. ص"…2ý<¨Èô áð¶—¶T\ÿlõïþhÊ‚g.˜Ù1ÿ¿SÓî‘‹Ê¢²I1NPh2ý4ƒ¦HÛ·oïèè8xð ¾AQ¬°ÿƒK楶?p4–éP‚dúœL?M'yxxxx”øcŒü «L YdúœL?›íÛ··½_˜²k×®—_~ùÀ===º`uíüé³µ·l½ð–Ií?pÝæª7ß¼hÓÿÞÓï·iÓ¦mÛ¶…yïÞ½GŽ ‡i™>@RÈô(8™~6»Þ/LÙ»wïŽ9ÒÛÛ«‹FJè?S=ó×iþy3Ú¯øÃCÛÿÏ™ÿ}vÌÿ «L YÜ#ìÚ@ÁÉô³9xðà~ðåU~#L9räHOOOooïéÓ§uÀˆøå÷ÿ*:7ÿÙš›ÿö‹ŸzçXê_£a}òKÿelþ «{䢲¨l²”m¦Ÿû¶c(`׊ÈôŸ£âh{ǯox[y}|b~šè_X×T~rlþ kÙgú޳¾A¡²eF¦vm HaLßø P|'ß~÷õÖÿëàQ£qîôL•Ee“B¦vm HaLßø `4.ÙôL•Ee“B¦vm HaLßø `4.ÙôL•Ee“Â=rQ À® ),é?ŒÆ%{˜v\T•MŠqº€B“é@‰¦Ë8Ó(32} N¦%~˜–é$…L€‚“é@‰¦eúI!Ó àdúPâ‡i™>@R¸G. ص‚4Óß<îZ²|Ù±Ççðm6‡i÷ÈEeQÙ¤(ÛL?ü~P]ìÚ@I…¹3ý3gVx”Ù#”uëG~ïh{‡]ðm¶ôÓeœéûbŸEeËŒLìÚ@‘™þÌôßù«V±>àÛl"Ó2}T•M ™> ص"…2ý1˜é‡ÿþýŸ£­öÓó?¾ðàÁžžžÓ§OÛ#ßfKð0-ÓGeQÙ¤é£@€](RX Ó›™~xìÛ¸|ˇoþñš§8 Ö|›-ÍôL•Ee“Â=rQ À® ),éÙL?<ön¸sÍÌ]ÿïãb}À·ÙÒ~|õ~ô‡áÉÑ£Ëî»ozÆù¯¸büž=÷öõ}ýÔ©åaÕ NÉ⧽̸ŠÔǃÞxûí“»ºþøäɯśÞ~Ï=W„‰a½­­·~êSÿjÐÆdœ?Ìz&tE袻îº4nÕCݺ.Ì–³xñÔ¸aÑüq—æsž~¶* )Ó߸q£Xðm¶DÓ2}T•M ™> ص"…ÃÈôß|siôüŸþié„ 5Ñóð$¼Œ§74\ÏßÕõÇù\+¦±ñÂ5k>óÖ[÷åym™¾¾¯×Ö~hH™~ÆU¤>&Nüp¼©oOx+­¬¹iÓ¦þð‡/¿üòÁƒ{{{í,€o³£u˜–飲¨lRÈôQ À® ),F¦Ÿú2¬óœž#£å•Åwß}Y]]Õ%—üÖ /ÌË8ÿž=÷þîïþëêêFœ©¨øÀ2ýŒ«ê5ëã—CmLŽé©òܨ|æÉøÏƒfú©áÑßÿýß÷ôôØYßfGë0-ÓGeQÙ¤p\°kE Fð<ýÔé©çéõž®/½ôùx±ióOšTûÜswŸ:µ<<ÿÍ—WT| ¯ïëÑóãÇ¿2p¥©«Èç<ýŒ/ólLü2õ<ýÐW©Ë‰¯ÅŸÖ˜aœ§Ÿ± C½öΓO>]çå—_>räˆðmv´Óî‘‹Ê¢²I1NPhçr=ýãÇ¿©×Ó/þ¦îsŸH½ž~êÛkj*þOÓä… §DyúK/}¾¾þüŒó‡éññ?ÿù+ã…§ÍöÉONxä‘××÷õ0Û=÷\Ï–q¯§Þ•v=ýŒ1z¶Æd›?,ð®».ºîî»/‹§¯Zuów^Åýÿøÿ1¾.|ýý¨K3fúiž­ CÊô7oÞüä“OnذaëÖ­?ùÉO =ÏŸ±ÓÒ6<[†”éoÙ²E~P"‡i™>@RÈô(¸¹ž¾Çð|颋êJ°a2}€’:LËô’B¦@ÁÉô‹ÿ¸ÿþëŽÿJw÷—‡wa™>ÀX;LËô’Â=rQ À® Üð2ýªª Ñü°k×þ~CÃÕÕ\¸pʉ÷Ëô|›Í}˜v\T•MвÍôÃ/ÕU À® ”TX0ÔLߣ¼2}À·Ù’:L—q¦ïwŠ}•-32}°kE dú2}À·Ù’=LËôQYT6)dú(`׊”k¦ïºÿ2}À·Ù28LËôQYT6)dú(`׊ /Óom½µºúƒá¿iÓÿáþÃüÁ%55•••ç]wÝÄgŸ½+Š×ЦÿÓ?-8ñçN-O[NoïW/ìîþrÆ7&úQâ› Ó|›-©Ã´L•Ee“Â=rQ À® ),^¦É%¿õ裷]|q}êÄ¾ÔØxáŸþ鬓'¿^þìgÿ÷=÷\‘#ÎŽ^~þóW®]ûûiËÿîwoùÒ—¦•åéö2}ßfó?L»G.*‹Ê&Å8]@¡ ;Óß¹óžë¯ÿXxrÝuô£?Œ§/\8e`:?h¦ÿw÷ï/¹ä·R§÷õ}ý¢‹êøRž ø /Ì»êªVUU„w­_ÿÙ´u…'ú§³/¬¬<ïšk~ç•WÇo\¾|F]]Umí‡Ö¬ùL<ñ߸¾¾þü°´{î¹âøñ¯dÜŠÖÖ[.ó„M>qâþhzxò¥/M«©© ð$žæËŸ0¡¦¢â¥ÿ?ÈôJê0]Æ™>@™‘éPpÃÎôï¼óâ'Ÿ¼30Ó¿çž+ººþ¸¯ïë­­·~êSÿ*šþàƒ7†õ†é'O~mùòÑćºésŸûD˜xêÔòÅ‹§ÆKKköüÁ%aKÃ#<‰ß{ÿý×…÷ÆÓÃËxþ/|áªøŸœ§@þ‡i™>@RÈô(¸áeúo¾¹tÒ¤Ú¾¾¯G'ÔO˜PóOÿ´4úSeåyCºìLürÏž{£ÿ£Ç5×üÎOúÅxžA¯§ßØxáš5Ÿyë­û2.<<‰O™ Ž9qâ‡ã–ǰiaãkú×ÕUeÜŠxž°„ÐÑóÔ®O.ˆçïêúc×Þ`‡i™>@RÈô(¸áeúóÍCJÀ_yeñÝw_Ö~É%¿õ ófúÙ²þŒ-LUQñA·"þG‚<§ËôÈÿ0-ÓH ÷ÈE»6PpÃÈôOZ>aBM|–ztÚ~CÃÑiû NI½0ý2ýþðÿºýöÉÑuxvî¼gx øK/}>>k~ÐL?ÛyúG.ôÿ6Èvž~êôÔóôeú¾Íï0í¹¨,*›e›é‡_ª«@€](©°`H™þÓOϾë®KÓ&Þ~ûäÇ¿#<9pàK>úèm§N-/ñ‹ÅŸÿü•yfúÑ%wž}ö®ðß¡&à N‰Òù—^ú|}ýùyfúÑõô]–z=ýU«n¾ó΋ú£ðüÿñ?†%g»žþñã_ Ôëé‡'ÑuöÃôÏ}î©×ÓO}{MMåáÃÿI¦àÛl>‡é2ÎôýN±Ï¢²eF¦vm HaÁ2ýë¯ÿØ®] Ò&þèGxÝu£çÿðÿá®».­®þ`EÅ>ùÉ Ï>{Wþ™þÓOÏ®«« ÿÍq1œŒÿ÷¿?ç²Ë~»²ò¼«®úhþ×Þ‰îj[SSY[û¡Ôÿ½`Õª›/º¨.´ÿê«Â’3fúßýî-õõçWUU,^<õäɯEÓOœ¸ÿ¾û¦‡‰á±té'ãéi X½ú÷ %{¶¾Lðm¶¤Ó2}T•M ™> ص"…C½ž¾G‰_*‹Ê&…LìÚ@‘™¾L_¦ø6[²‡i™>*‹Ê&…{ä¢@€](RX Ó꣪ªB¦àÛlqÓî‘‹Ê¢²I1NPh2}™>@‰¦Ë8Ó(32} .¡™~‘/}3R«KÊ{dú%u˜–é$…L€‚^¦?êÙ´L_¦0vÓ2}€¤éPpÎÓöê†Ñ†ivÚBvî¼ç–[.ªªª¨­ýТEW=ºL¦Pf‡i™>@R¸G. صÿ¿öîFκÎx¥,k奵ö€,”×ÖR)`{%bÊIS¡áEíq…`H­èU^c8ˆÂPÞ_¨H aKBÉ^Ím0s…TV©Ø%¤1dCÜ›–ж„˜ûŹ{nÜyvvÚ™}žé瓚ÙÙ™gž}¾ýÿžÿóëth8=ý¼÷ôÏ<óÈîî wî¼fûöåW]5û´ÓÓÓ¬f[ì4í3r‘,’Í‹–íéÇ‚t˜Ú@¦šuÿî wÜÇÛÛÇvØÄŸüäÌŠ|ðÁ…´µí5kÖ'¶lYRºûöå_ýê ûîÛ#nÄ—qç1Ç´uëå¥<öØ9¥qÏQGM¾Ù;î˜?yòGâ¥/žYzzµÍïƒ'_VÛ½÷ÞûVl66/qûí§ ïÅ+“þÒuЇwøë–;¯‰-ëéV³-všnáž¾ësɶ=}˜Ú@“šu÷ô?ö± ÝÝÆ×__¶téIyñÅŸü㿾sç5wÜ1öì(Ý¿|ù?žsÎÑÛ¶}#ƾpL|w.[öéRçúÕW—N˜°÷{ï}+n?øàÂá[ŽÍƳ’§_}õܔͦ÷ô+î^l°|ûµ¼O¿ÚK×}4ªÞ”7ûoذèä“ÑÓ¬f[ì4­§d‘l^èé# ÀÔšÔ,¨»§ßÑqÀ½÷~îÕW—¦ü¢˜ä]çåï"Ÿ2eßW^¹²t;nLžü‘¸ñÔS\|ñ'ãÆŠ§Oz`©¿á…Óãþá›ýÝïþÿé±µ”ͦ÷ô+îÞÁïW¾ýZzúÕ^ºî£QíðVëé¿øâ’8hÉ{ÿõô«Ù–9Mëé#Y$›zú0µ&5 êîéoÙ²ä‚ ¦MœØ~Ì1mذhÄÆwy3½üþRwû/¹úÐC÷ŸúÔäîî ?ó™CãvÜ÷§o6iŽ×xµÝñþZ~´!/]÷Ѩvx+îÌÆ_:ì°‰›6}y·|~€ž>`5›©Ó´ž>’E²yá3r`jMjÔÝÓ/ï)'o–¯¥‹.#|òÎô3ΘÚÕuAéÈÄŸ6,:óÌ#+n¶Úûô+nvüøíÜyMéöÛosÄÞ}}ïÓ¯øÒ»r4*Þá;óÄ_ìè8à…þuw}&°ž>`5›©Ó´ÏÈE²H6/Æ94Ú.öô/žYú¥17~iÒ¤×ÞÅN~aýÛoóœsŽN~ƒüwÌ?ê¨I@‹¦õôòBO€†«¯§o´öÐÓÈÔiZO /ôôh8=}CO ã§i=}€¼ð¹0µ†ÓÓ7ôô«ÙŒŸ¦}F.’E²yѲ=ý¸B®€SÈT³@OßÐÓ¬f3{šnáž¾ësɶ=}˜Ú@“šzú†ž>`5›ÙÓ´ž>’E²y¡§€ShR³@OßÐÓ¬f3{šÖÓG²H6/ôô`jMjèézú€ÕlfOÓzúHÉæ…ÏÈE@€© 4©YÞÓ7öÀ¡§XÍfç4í3r‘,’Í‹q6bO?Q(Ö­[·zõêU«V­e §‘Ót ÷ôZŒž> W{O¿§§§«««³³s{Œˆ;BèûûûM€±:Mëéä…ž> W{O¿···P(¬_¿¾³³s5{€:âŽÐ#úb±h²ŒÕiZO /ôôh¸Ú{ú}}}›7o. ]]]ëØDÐw„Ñ ˜,cušÖÓÈ Ÿ‘‹€Sh¸Ú{úÅb±¯¯¯···§§§°Çxø«×öTtÄ¡Gôƒƒƒ& `5;V§iŸ‘‹d‘l^´lOÿñqs¤+ ÀÔ2Õ,¨¥§?88800P,ûûûûö÷¶Ø·§Š #î=¢ß±c‡ÉXÍŽÕiº…{ú®SÌY$Ûbôô`jMjÔÒÓW?PÇä4­§d‘l^èé# ÀÔšÔ,ÐÓW?TãÌž¦õô‘,’Í =}˜Ú@“šxà?QÉãqTã±'h=}$‹dõôÇžÏR`jYëé™å3r‘,’ÕÓ€ÿuï½÷žúfÍš5cÆŒéÓ§O›6m —^zi«öôZŒž>MÕÓÓÓÕÕÕÙÙ¹ Ș8AÇi:NÖýýý-Ù¤§@Sõöö …õë×wvv®2#NÍq‚ŽÓtœ¬‹Å¢E @6ééÐT}}}›7o. ]]]ë€ÌˆSsœ ã4'ë‹€lò¹0µ¦*‹}}}½½½===þ¦kñõ€j<æâÔ'è8MÇÉzppÐu ’E²zúMõø¸9Ò`j488800P,ûûûûø›¨Ÿ€j<æâÔ'è8MÇÉzÇŽ®S,’ÕÓ—=Sõ@5F²HÉÖOO¦6€ú €j,Y$‹dóAO¦6€ú €j,Y$‹dóÁgä" ÀÔP?P%‹d‘l>Œs ôô ôô ôô *÷ôßûÝ77þ&×ãWÿrsN÷üýw¶›€2ÔÇÔ6µ=¶æ®~*› Ì¶äÈïjV“A²’•¬d÷¨õsåž~¼Ìããæc2Þzæ7#Æ& Œõ1µMm@!U6eÖÐd0$+YÉZ?×ßÓùî;ßÜø¸Ñ´ñò]wŽjê (³Á®\#™Ú¦6 *›€2kh2’•¬d­ŸëééÇKþõ¯[Œ¦?þ÷㣚ºÊl@°+×H¦¶© (¤Ê& Ìš †d%+Yëg=ýÜÜ}×ÊžžžÞÞÞ¾¾¾b±888( Ü®‘Lm@!U6eÖÐd0$kHÖhÄúYO?s¯»iEWWW¡Pؼysd<00  Ü®‘Lm@!U6eÖÐd0$kHÖhÄúYO?s?zý­ëׯŒ{{{‹Å¢€r¸F2µ…TÙ”YC“Á¬!Y£ëg=ýÌüÈu·¬Zµ*2îêêêéééïïPî×H¦6 *›€2kh2’5$k4bý¬§Ÿ¹€×þûðêÕ«×­[W(úúú”»€À5’© (¤Ê& Ìš †d ÉX?ë雺†+X\#™Ú¦6(¤†² (³š ’•¬!YCO?Wcܸq¦®á ×H¦¶© ©¡l{Z™ÍBC@“A²’•¬d%«§ßÔ¿4»ë/œ©;V>÷ܪ¹sgŽøW°¸FÊÑÔ.~<þìöö}&NÜoÙ²‹Þ}÷¦6(¤.9ôô|­W›Vi“ª} 9¶Ëà÷3³MɦŠZ’M^(×E-Øÿ}ÛÚöî蘲|ùW¶m{Æœ•l~笞¾ž¾Æß(B‰Â3PO=ýVšÚgõÙ§ŸþἫ¥Ë/?ïË_>ÓÔ…TO_OÐÓO¡Ú×c» ®q?õs—l É&/4oÞ‰=vk©Eþæ›…åË¿r꩟6g%›ß9ÛÀž~ìbÓ“NšÞÖ¶÷„ í‹}.ŽlòÝ•+o<âˆCâ[ñç~ôí¸çw6MštÀûïÿ:yLÜž8q¿mÛžyíµîóÏ?=6_¸ð3åÛ©8žzêîN˜Vú÷™‡º)åEkÙÕØÂá‡ÜÞ¾ÏÑGwij’C?<ƒò{†ïø¿7â.ÝwßµS¦|tüø½4uóÐý÷_Žo͘qä¦M?}ôÑïFñelç•Wþ3yXl!î°"²Õ«¿“2‘J÷T %vïøã‰Lž|Ð 7\VG…rË^#™ÚuOíÒÏ0µA!M)¤ÕŠIíiø2¯¾5êðñòËëã:°T·£®]{{-»ìLµÓAOW6Ú»Hù­´/Ó×¥‘²´Ž}«ØsH?w»â2¸–ýÜ]ýAÉ6?Ùæôô[,ÙÒHל•lÆçlc{úñ#=ÿü£qûÝwqÅ_ŒCVúÖš5·ÅÑyé¥uq{Ë–5‘â£~7nŸ{î©q ’-ÄçwZܘ1ãÈçž[õÁ/Äßæk¯½ôÒKÏIùá7l¸¯£cJ<>n‹O/Yrnú‹¦ïêOÜ™<+þŒgÕÒÓ¯¶Cž’¾Kñ³¿óΦÆMÝ|´`ÁÉ1ëâ%n¾ùʘ?1ë’/çÏŸ<¬<¬¸ýä“+F kÈ·º»˜;wfì@Ü~ãŸÇ|ãWè铯V”©]ßÔŽñûßÿW¬6LmPH«Ò”bR{E²Ì«oZqLŸ>5.EJ}ùØÔ%—,¬e·“©v:¨ñéÊ&0ª.RN+mÅ‹Áô5diT[Z¯]{{²o[·>·“—H?Õv;år5e?wcP²MN¶i=ýVJvÛ¶gn¸á²ô'š³’ÍøœmlO¿¿ÿgÉ—;v<Ÿü;IˆW–ÿ®¨9sŽ+ý£Ç‚''÷Çí¸gÈfã ï¿ÿ¾)?ü¼y'>ýô‡ß_íEGµ«Ï>ûP-=ýjû0ä)é»”¼Gµq=ý…,Ù~|Y,>|9aB{ò°!arʬі›Ø½ßþöÉä˨;à ¨ž>oE™ÚõMí‹þ–[¾fjƒBZ­¦“Q]Û”/óê[£V{SÒÀÀÆŠ%:e·“©v:¨ñéÊ&0ª.RN+mÅ‹Áô5äðQ¾´Ž}‹õmÅžCú¡¨¶Û)—«)û¹ûƒ’mr²Mëé·F²Éû¦§OŸZíM æ¬ds1gÛÓ¯vO[ÛÞqÔÊ`©w7&O>¨ÔÓ‰?ãvéaqtÎ>{^<¦OÅÿÛ›Œ!¯vÿ†Qí»ZKO¿Ú> yʨv©=ýœTí˸QGÄC¾s/±K=}2ÞŠ2µë›Ú÷ßÝYg}ÖÔ…4¥¶¤“Q]Ûìúµâ¸á†ËâãŠ+¾¸fÍm¯½Ö]K ,ß`µÓÁ¨~je¨±‹”ÓJ;ü»#®!K£ÚÒ:¥ç°[Eû¹ûƒ’mr²Mëé·L²¥ÔÎ;ï´Å‹?oÎJ6¿s6[=ýW]uIéß þã?¾¾lÙE¥;çÌ9î;ßYºcÇóµüðq|Ǽ§_mòÞÓÏH@ müÅ´,ÿM²õ}â‡+XòÕÓ7µcÜu×·æÏŸýç?ÿÊÔ…4¥¶¤“,\ÛÄxñŵ÷Üsõ%—,œ<ù ;ïü·ÑîvÅÓÁ¨~jeØÅ.RÆ+íïÖ²†L_Z§ôvË¡¨q?›Ð”l#’ÍBO?_É&ãOêI¶iÎJ6svlzúqøÊÿ«EÜNþ«Å–-k¦OŸZúõF¿þõcɱKw¦ÿðóæØÝý@Å̪½hÊ®žrʬj¿{§½}Ÿäo@é·\•ÿ§ŒŠû0äm¡£Ú¥föô3PJã¯Ú/èH kH(±{åŸÀ©§O+µ¢Líj;¿fÍm±Í8÷›Ú Žøû«“Ú+Òí×·Fq¼ñÆÏËÿëtµÝ²ÁЧƒÚŸ®l»ÞEÊx¥-ÿnkÈô¥õßD·k9ém¸:ÖºMèJv·'›‘ž~Ž’-'NÜÏœ•l~çìØôô{ìÖ#Ž8äÅ×–ÞLtøá—W\BÜsÏÕ3gUþy_¥BîïÿÙI'MOÿáË?§ñµ×º¯¼rш/𲫥ÏÈMžUþ¹‹}îòËÏ{÷Ý_”þ”ïXµ}8øàÿò—$¯2ª]jfO?ã¥4þ’°^ziݱÇž|fJXCB)~x!ƦM?-}:œž>-Њ2µ+Ní8UÇ‚©´SÒôBšRLj¯HC¶_ßµâ8ûìyÏ>ûPißâÂ# ûˆ»=|ƒÃO£zº² ìb)ã•6ùníkÈô¥u©çPúTÆÒZ·–C‘²ÛCEûÙ„þ dwo²Ùééç%ÙK.Y¯Xz#öÖ­O,Xpò·¿}¹9+ÙüÎÙ±ééÇX¹òÆR<þŒÛå»å–¯Mœ¸ß÷¾÷äž8q]1~ü^Sî¿ÿº;ªO>¹âøã)=þ¡‡nñEÓwõá‡oŽÇÇÖâÏüà†ä[o¾Y¸è¢ííûÄ·N8aÚš5·•?«â>¬]{{$Zú…M£Ý¥föô3PJã/¦eVù¿¡¥„5<”˜œóçÏ.ýž¬¸QþÖãqÏ,ùjE™Ú§ö¤I ™Úåï80µA!rOµbR{E¾ýúÖ¨ÃGìÌ©§~:v`„öóÏ?½üɪíöð ?ŒêéÊ&°‹]¤ŒWÚ仵¯!G\ZÇþÄIÏ¡­mï:ÊoµCQã~6¡?(ÙÝ›l É&ß½JvrΜãî»ïÚôË[sV²Ÿ³ ìé»kê¶Ø¨ý£g' hÄ5’©mj ©² (³y[·>q챇k2HV²’•¬dõôõô5þ\ÁâÉÔ6µ‹XePf³8–,9·¿ÿg¥Òܹ3KŸU®É YÉJV²’mÍž~éÿ; .ö䀲óÀ,-|dj›Ú ŽùÈË2XÙe6¿]¤º+mO\³æ¶c=<vćŒI iêJV²’•lk$›ãž¾7=ùç8ïJÃ5’éfj ©² (³†&ƒ!YÉJÖúYOßÔ5\ÁâÉ0µA!5”M@™U±%+YC²†ž¾aê \#™Ú€Bªlʬ¡É`HÖ¬¡§/`¹‚Å5’aj ©² (³†&ƒ!YÉJÖúYOßÔ5\ÁâÉÔ6µA!5”M@™Õd¬d Ézú†©k‚k$SPH•M@™54 É’•lÓzú/ßug¼¤Ñ´|TSW@™ våÉÔ6µ…TÙ”YC“Á¬d%ký\OOß“QûÔ52ìÊ5’aj ©² (³†&ƒ!YÉJÖúyt=ý÷ßÙþÖ3¿)î»V®»iÅ£×ßúÈu·Äk ?2bÀÊx@PSÛÔRePf MC²’•¬õs==ýr===]]]«h¢8àqØãà÷÷÷ (×A}LmSPH•M@™E“ÉJV²ÖÏõôô{{{ …Âúõëã%WÓq¨ã€Çaƒ_,”뀠>¦¶© (¤Ê& Ì¢É€d%+Yëçzzú}}}›7oŽëêêZGSġއ=þÀÀ€€rÔÇÔ6µ…TÙ”Y4¬d%ký\OO¿X,ÆËôöööôôhŠ8ÔqÀã°ÇÁP®‚ú˜Ú¦6 *›€2‹&’•¬d­ŸëééÇ Ä+õ÷÷÷Ñq¨ã€Çaƒ¿cÇå: ¨©mj ©² (³h2 YÉJÖú¹žž>zúzúzúÿO#Åb¸†zCIEND®B`‚neutron-12.1.1/doc/source/contributor/internals/dns_order.rst0000664000175000017500000001252713553660046024456 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Keep DNS Nameserver Order Consistency In Neutron ================================================ In Neutron subnets, DNS nameservers are given priority when created or updated. This means if you create a subnet with multiple DNS servers, the order will be retained and guests will receive the DNS servers in the order you created them in when the subnet was created. The same thing applies for update operations on subnets to add, remove, or update DNS servers. Get Subnet Details Info ----------------------- :: changzhi@stack:~/devstack$ neutron subnet-list +--------------------------------------+------+-------------+--------------------------------------------+ | id | name | cidr | allocation_pools | +--------------------------------------+------+-------------+--------------------------------------------+ | 1a2d261b-b233-3ab9-902e-88576a82afa6 | | 10.0.0.0/24 | {"start": "10.0.0.2", "end": "10.0.0.254"} | +--------------------------------------+------+-------------+--------------------------------------------+ changzhi@stack:~/devstack$ neutron subnet-show 1a2d261b-b233-3ab9-902e-88576a82afa6 +------------------+--------------------------------------------+ | Field | Value | +------------------+--------------------------------------------+ | allocation_pools | {"start": "10.0.0.2", "end": "10.0.0.254"} | | cidr | 10.0.0.0/24 | | dns_nameservers | 1.1.1.1 | | | 2.2.2.2 | | | 3.3.3.3 | | enable_dhcp | True | | gateway_ip | 10.0.0.1 | | host_routes | | | id | 1a2d26fb-b733-4ab3-992e-88554a87afa6 | | ip_version | 4 | | name | | | network_id | a404518c-800d-2353-9193-57dbb42ac5ee | | tenant_id | 3868290ab10f417390acbb754160dbb2 | +------------------+--------------------------------------------+ Update Subnet DNS Nameservers ----------------------------- :: neutron subnet-update 1a2d261b-b233-3ab9-902e-88576a82afa6 \ --dns_nameservers list=true 3.3.3.3 2.2.2.2 1.1.1.1 changzhi@stack:~/devstack$ neutron subnet-show 1a2d261b-b233-3ab9-902e-88576a82afa6 +------------------+--------------------------------------------+ | Field | Value | +------------------+--------------------------------------------+ | allocation_pools | {"start": "10.0.0.2", "end": "10.0.0.254"} | | cidr | 10.0.0.0/24 | | dns_nameservers | 3.3.3.3 | | | 2.2.2.2 | | | 1.1.1.1 | | enable_dhcp | True | | gateway_ip | 10.0.0.1 | | host_routes | | | id | 1a2d26fb-b733-4ab3-992e-88554a87afa6 | | ip_version | 4 | | name | | | network_id | a404518c-800d-2353-9193-57dbb42ac5ee | | tenant_id | 3868290ab10f417390acbb754160dbb2 | +------------------+--------------------------------------------+ As shown in above output, the order of the DNS nameservers has been updated. New virtual machines deployed to this subnet will receive the DNS nameservers in this new priority order. Existing virtual machines that have already been deployed will not be immediately affected by changing the DNS nameserver order on the neutron subnet. Virtual machines that are configured to get their IP address via DHCP will detect the DNS nameserver order change when their DHCP lease expires or when the virtual machine is restarted. Existing virtual machines configured with a static IP address will never detect the updated DNS nameserver order. neutron-12.1.1/doc/source/contributor/internals/agent_extensions.rst0000664000175000017500000001056113553660047026051 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Agent extensions ================ All reference agents utilize a common extension mechanism that allows for the introduction and enabling of a core resource extension without needing to change agent code. This mechanism allows multiple agent extensions to be run by a single agent simultaneously. The mechanism may be especially interesting to third parties whose extensions lie outside the neutron tree. Under this framework, an agent may expose its API to each of its extensions thereby allowing an extension to access resources internal to the agent. At layer 2, for instance, upon each port event the agent is then able to trigger a handle_port method in its extensions. Interactions with the agent API object are in the following order: #. The agent initializes the agent API object. #. The agent passes the agent API object into the extension manager. #. The manager passes the agent API object into each extension. #. An extension calls the new agent API object method to receive, for instance, bridge wrappers with cookies allocated. :: +-----------+ | Agent API +--------------------------------------------------+ +-----+-----+ | | +-----------+ | |1 +--+ Extension +--+ | | | +-----------+ | | +---+-+-+---+ 2 +--------------+ 3 | | 4 | | Agent +-----+ Ext. manager +-----+--+ .... +--+-----+ +-----------+ +--------------+ | | | +-----------+ | +--+ Extension +--+ +-----------+ Each extension is referenced through a stevedore entry point defined within a specific namespace. For example, L2 extensions are referenced through the neutron.agent.l2.extensions namespace. The relevant modules are: * neutron.agent.agent_extension: This module defines an abstract extension interface for all agent extensions across L2 and L3. * neutron.agent.l2.l2_agent_extension: * neutron.agent.l3.l3_agent_extension: These modules subclass neutron.agent.agent_extension.AgentExtension and define a layer-specific abstract extension interface. * neutron.agent.agent_extensions_manager: This module contains a manager that allows extensions to load themselves at runtime. * neutron.agent.l2.l2_agent_extensions_manager: * neutron.agent.l3.l3_agent_extensions_manager: Each of these modules passes core resource events to loaded extensions. Agent API object ---------------- Every agent can pass an "agent API object" into its extensions in order to expose its internals to them in a controlled way. To accommodate different agents, each extension may define a consume_api() method that will receive this object. This agent API object is part of neutron's public interface for third parties. All changes to the interface will be managed in a backwards-compatible way. At this time, on the L2 side, only the L2 Open vSwitch agent provides an agent API object to extensions. See :doc:`L2 agent extensions `. For L3, see :doc:`L3 agent extensions `. The relevant modules are: * neutron.agent.agent_extension * neutron.agent.agent_extensions_manager * neutron.agent.l2.l2_agent_extension_api * neutron.agent.l2.l2_agent_extensions_manager * neutron.agent.l3.l3_agent_extension_api * neutron.agent.l3.l3_agent_extensions_manager neutron-12.1.1/doc/source/contributor/internals/db_models.rst0000664000175000017500000000426213553660047024425 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Relocation of Database Models ============================= This document is intended to track and notify developers that db models in neutron will be centralized and moved to a new tree under neutron/db/models. This was discussed in [1]. The reason for relocating db models is to solve the cyclic import issue while implementing oslo versioned objects for resources in neutron. The reason behind this relocation is Mixin class and db models for some resources in neutron are in same module. In Mixin classes, there are methods which provide functionality of fetching, adding, updating and deleting data via queries. These queries will be replaced with use of versioned objects and definition of versioned object will be using db models. So object files will be importing models and Mixin need to import those objects which will end up in cyclic import. Structure of Model Definitions ------------------------------ We have decided to move all models definitions to neutron/db/models/ with no further nesting after that point. The deprecation method to move models has already been added to avoid breakage of third party plugins using those models. All relocated models need to use deprecate method that will generate a warning and return new class for use of old class. Some examples of relocated models [2] and [3]. In future if you define new models please make sure they are separated from mixins and are under tree neutron/db/models/ . References ~~~~~~~~~~ [1]. https://www.mail-archive.com/openstack-dev@lists.openstack.org/msg88910.html [2]. https://review.openstack.org/#/c/348562/ [3]. https://review.openstack.org/#/c/348757/ neutron-12.1.1/doc/source/contributor/internals/db_layer.rst0000664000175000017500000001147513553660046024261 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Neutron Database Layer ====================== This section contains some common information that will be useful for developers that need to do some db changes. Difference between 'default' and 'server_default' parameters for columns ------------------------------------------------------------------------ For columns it is possible to set 'default' or 'server_default'. What is the difference between them and why should they be used? The explanation is quite simple: * `default `_ - the default value that SQLAlchemy will specify in queries for creating instances of a given model; * `server_default `_ - the default value for a column that SQLAlchemy will specify in DDL. Summarizing, 'default' is useless in migrations and only 'server_default' should be used. For synchronizing migrations with models server_default parameter should also be added in model. If default value in database is not needed, 'server_default' should not be used. The declarative approach can be bypassed (i.e. 'default' may be omitted in the model) if default is enforced through business logic. Database migrations ------------------- For details on the neutron-db-manage wrapper and alembic migrations, see `Alembic Migrations `_. .. _testing-database-migrations: Tests to verify that database migrations and models are in sync --------------------------------------------------------------- .. automodule:: neutron.tests.functional.db.test_migrations .. autoclass:: _TestModelsMigrations :members: The Standard Attribute Table ---------------------------- There are many attributes that we would like to store in the database which are common across many Neutron objects (e.g. tags, timestamps, rbac entries). We have previously been handling this by duplicating the schema to every table via model mixins. This means that a DB migration is required for each object that wants to adopt one of these common attributes. This becomes even more cumbersome when the relationship between the attribute and the object is many-to-one because each object then needs its own table for the attributes (assuming referential integrity is a concern). To address this issue, the 'standardattribute' table is available. Any model can add support for this table by inheriting the 'HasStandardAttributes' mixin in neutron.db.standard_attr. This mixin will add a standard_attr_id BigInteger column to the model with a foreign key relationship to the 'standardattribute' table. The model will then be able to access any columns of the 'standardattribute' table and any tables related to it. A model that inherits HasStandardAttributes must implement the property 'api_collections', which is a list of API resources that the new object may appear under. In most cases, this will only be one (e.g. 'ports' for the Port model). This is used by all of the service plugins that add standard attribute fields to determine which API responses need to be populated. A model that supports tag mechanism must implement the property 'collection_resource_map' which is a dict of 'collection_name' and 'resource_name' for API resources. And also the model must implement 'tag_support' with a value True. The introduction of a new standard attribute only requires one column addition to the 'standardattribute' table for one-to-one relationships or a new table for one-to-many or one-to-zero relationships. Then all of the models using the 'HasStandardAttribute' mixin will automatically gain access to the new attribute. Any attributes that will apply to every neutron resource (e.g. timestamps) can be added directly to the 'standardattribute' table. For things that will frequently be NULL for most entries (e.g. a column to store an error reason), a new table should be added and joined to in a query to prevent a bunch of NULL entries in the database. neutron-12.1.1/doc/source/contributor/internals/linuxbridge_agent.rst0000664000175000017500000000365613553660046026174 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) L2 Networking with Linux Bridge =============================== This Agent uses the `Linux Bridge `_ to provide L2 connectivity for VM instances running on the compute node to the public network. A graphical illustration of the deployment can be found in `Networking Guide <../../admin/deploy-lb-provider.html#architecture>`_. In most common deployments, there is a compute and a network node. On both the compute and the network node, the Linux Bridge Agent will manage virtual switches, connectivity among them, and interaction via virtual ports with other network components such as namespaces and underlying interfaces. Additionally, on the compute node, the Linux Bridge Agent will manage security groups. Three use cases and their packet flow are documented as follows: 1. `Linux Bridge: Provider networks <../../admin/deploy-lb-provider.html>`_ 2. `Linux Bridge: Self-service networks <../../admin/deploy-lb-selfservice.html>`_ 3. `Linux Bridge: High availability using VRRP <../../admin/deploy-lb-ha-vrrp.html>`_ neutron-12.1.1/doc/source/contributor/internals/sriov_nic_agent.rst0000664000175000017500000000470613553660046025650 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) L2 Networking with SR-IOV enabled NICs ====================================== SR-IOV (Single Root I/O Virtualization) is a specification that allows a PCIe device to appear to be multiple separate physical PCIe devices. SR-IOV works by introducing the idea of physical functions (PFs) and virtual functions (VFs). Physical functions (PFs) are full-featured PCIe functions. Virtual functions (VFs) are “lightweight†functions that lack configuration resources. SR-IOV supports VLANs for L2 network isolation, other networking technologies such as VXLAN/GRE may be supported in the future. SR-IOV NIC agent manages configuration of SR-IOV Virtual Functions that connect VM instances running on the compute node to the public network. In most common deployments, there are compute and a network nodes. Compute node can support VM connectivity via SR-IOV enabled NIC. SR-IOV NIC Agent manages Virtual Functions admin state. Quality of service is partially implemented with the bandwidth limit and minimum bandwidth rules. In the future it will manage additional settings, such as additional quality of service rules, rate limit settings, spoofcheck and more. Network node will be usually deployed with either Open vSwitch or Linux Bridge to support network node functionality. Further Reading --------------- `Nir Yechiel - SR-IOV Networking – Part I: Understanding the Basics `_ `SR-IOV Passthrough For Networking `_ neutron-12.1.1/doc/source/contributor/internals/openvswitch_firewall.rst0000664000175000017500000006474713553660047026751 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Open vSwitch Firewall Driver ============================ The OVS driver has the same API as the current iptables firewall driver, keeping the state of security groups and ports inside of the firewall. Class ``SGPortMap`` was created to keep state consistent, and maps from ports to security groups and vice-versa. Every port and security group is represented by its own object encapsulating the necessary information. Note: Open vSwitch firewall driver uses register 5 for marking flow related to port and register 6 which defines network and is used for conntrack zones. Firewall API calls ------------------ There are two main calls performed by the firewall driver in order to either create or update a port with security groups - ``prepare_port_filter`` and ``update_port_filter``. Both methods rely on the security group objects that are already defined in the driver and work similarly to their iptables counterparts. The definition of the objects will be described later in this document. ``prepare_port_filter`` must be called only once during port creation, and it defines the initial rules for the port. When the port is updated, all filtering rules are removed, and new rules are generated based on the available information about security groups in the driver. Security group rules can be defined in the firewall driver by calling ``update_security_group_rules``, which rewrites all the rules for a given security group. If a remote security group is changed, then ``update_security_group_members`` is called to determine the set of IP addresses that should be allowed for this remote security group. Calling this method will not have any effect on existing instance ports. In other words, if the port is using security groups and its rules are changed by calling one of the above methods, then no new rules are generated for this port. ``update_port_filter`` must be called for the changes to take effect. All the machinery above is controlled by security group RPC methods, which mean the firewall driver doesn't have any logic of which port should be updated based on the provided changes, it only accomplishes actions when called from the controller. OpenFlow rules -------------- At first, every connection is split into ingress and egress processes based on the input or output port respectively. Each port contains the initial hardcoded flows for ARP, DHCP and established connections, which are accepted by default. To detect established connections, a flow must by marked by conntrack first with an ``action=ct()`` rule. An accepted flow means that ingress packets for the connection are directly sent to the port, and egress packets are left to be normally switched by the integration bridge. Connections that are not matched by the above rules are sent to either the ingress or egress filtering table, depending on its direction. The reason the rules are based on security group rules in separate tables is to make it easy to detect these rules during removal. Security group rules are treated differently for those without a remote group ID and those with a remote group ID. A security group rule without a remote group ID is expanded into several OpenFlow rules by the method ``create_flows_from_rule_and_port``. A security group rule with a remote group ID is expressed by three sets of flows. The first two are conjunctive flows which will be described in the next section. The third set matches on the conjunction IDs and does accept actions. Flow priorities for security group rules ---------------------------------------- The OpenFlow spec says a packet should not match against multiple flows at the same priority [1]_. The firewall driver uses 8 levels of priorities to achieve this. The method ``flow_priority_offset`` calculates a priority for a given security group rule. The use of priorities is essential with conjunction flows, which will be described later in the conjunction flows examples. .. [1] Although OVS seems to magically handle overlapping flows under some cases, we shouldn't rely on that. Uses of conjunctive flows ------------------------- With a security group rule with a remote group ID, flows that match on nw_src for remote_group_id addresses and match on dl_dst for port MAC addresses are needed (for ingress rules; likewise for egress rules). Without conjunction, this results in O(n*m) flows where n and m are number of ports in the remote group ID and the port security group, respectively. A conj_id is allocated for each (remote_group_id, security_group_id, direction, ethertype, flow_priority_offset) tuple. The class ``ConjIdMap`` handles the mapping. The same conj_id is shared between security group rules if multiple rules belong to the same tuple above. Conjunctive flows consist of 2 dimensions. Flows that belong to the dimension 1 of 2 are generated by the method ``create_flows_for_ip_address`` and are in charge of IP address based filtering specified by their remote group IDs. Flows that belong to the dimension 2 of 2 are generated by the method ``create_flows_from_rule_and_port`` and modified by the method ``substitute_conjunction_actions``, which represents the portion of the rule other than its remote group ID. Those dimension 2 of 2 flows are per port and contain no remote group information. When there are multiple security group rules for a port, those flows can overlap. To avoid such a situation, flows are sorted and fed to ``merge_port_ranges`` or ``merge_common_rules`` methods to rearrange them. Rules example with explanation: ------------------------------- The following example presents two ports on the same host. They have different security groups and there is icmp traffic allowed from first security group to the second security group. Ports have following attributes: :: Port 1 - plugged to the port 1 in OVS bridge - ip address: 192.168.0.1 - mac address: fa:16:3e:a4:22:10 - security group 1: can send icmp packets out - allowed address pair: 10.0.0.1/32, fa:16:3e:8c:84:13 Port 2 - plugged to the port 2 in OVS bridge - ip address: 192.168.0.2 - mac address: fa:16:3e:24:57:c7 - security group 2: - can receive icmp packets from security group 1 - can receive tcp packets from security group 1 - can receive tcp packets to port 80 from security group 2 - can receive ip packets from security group 3 - allowed address pair: 10.1.0.0/24, fa:16:3e:8c:84:14 ``table 0`` contains a low priority rule to continue packets processing in ``table 60`` aka TRANSIENT table. ``table 0`` is left for use to other features that take precedence over firewall, e.g. DVR. The only requirement is that after feature is done with its processing, it needs to pass packets for processing to the TRANSIENT table. This TRANSIENT table distinguishes the traffic to ingress or egress and loads to ``register 5`` value identifying port traffic. Egress flow is determined by switch port number and ingress flow is determined by destination mac address. ``register 6`` contains port tag to isolate connections into separate conntrack zones. :: table=60, priority=100,in_port=1 actions=load:0x1->NXM_NX_REG5[],load:0x284->NXM_NX_REG6[],resubmit(,71) table=60, priority=100,in_port=2 actions=load:0x2->NXM_NX_REG5[],load:0x284->NXM_NX_REG6[],resubmit(,71) table=60, priority=90,dl_vlan=0x284,dl_dst=fa:16:3e:a4:22:10 actions=load:0x1->NXM_NX_REG5[],load:0x284->NXM_NX_REG6[],resubmit(,81) table=60, priority=90,dl_vlan=0x284,dl_dst=fa:16:3e:8c:84:13 actions=load:0x1->NXM_NX_REG5[],load:0x284->NXM_NX_REG6[],resubmit(,81) table=60, priority=90,dl_vlan=0x284,dl_dst=fa:16:3e:24:57:c7 actions=load:0x2->NXM_NX_REG5[],load:0x284->NXM_NX_REG6[],resubmit(,81) table=60, priority=90,dl_vlan=0x284,dl_dst=fa:16:3e:8c:84:14 actions=load:0x2->NXM_NX_REG5[],load:0x284->NXM_NX_REG6[],resubmit(,81) table=60, priority=0 actions=NORMAL Following ``table 71`` implements arp spoofing protection, ip spoofing protection, allows traffic for obtaining ip addresses (dhcp, dhcpv6, slaac, ndp) for egress traffic and allows arp replies. Also identifies not tracked connections which are processed later with information obtained from conntrack. Notice the ``zone=NXM_NX_REG6[0..15]`` in ``actions`` when obtaining information from conntrack. It says every port has its own conntrack zone defined by value in ``register 6``. It's there to avoid accepting established traffic that belongs to different port with same conntrack parameters. The very first rule in ``table 71`` is a rule removing conntrack information for a use-case where Neutron logical port is placed directly to the hypervisor. In such case kernel does conntrack lookup before packet reaches Open vSwitch bridge. Tracked packets are sent back for processing by the same table after conntrack information is cleared. :: table=71, priority=110,ct_state=+trk actions=ct_clear,resubmit(,71) Rules below allow ICMPv6 traffic for multicast listeners, neighbour solicitation and neighbour advertisement. :: table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=130 actions=resubmit(,94) table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=131 actions=resubmit(,94) table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=132 actions=resubmit(,94) table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=135 actions=resubmit(,94) table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=136 actions=resubmit(,94) table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=130 actions=resubmit(,94) table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=131 actions=resubmit(,94) table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=132 actions=resubmit(,94) table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=135 actions=resubmit(,94) table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=136 actions=resubmit(,94) Following rules implement arp spoofing protection :: table=71, priority=95,arp,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:10,arp_spa=192.168.0.1 actions=resubmit(,94) table=71, priority=95,arp,reg5=0x1,in_port=1,dl_src=fa:16:3e:8c:84:13,arp_spa=10.0.0.1 actions=resubmit(,94) table=71, priority=95,arp,reg5=0x2,in_port=2,dl_src=fa:16:3e:24:57:c7,arp_spa=192.168.0.2 actions=resubmit(,94) table=71, priority=95,arp,reg5=0x2,in_port=2,dl_src=fa:16:3e:8c:84:14,arp_spa=10.1.0.0/24 actions=resubmit(,94) DHCP and DHCPv6 traffic is allowed to instance but DHCP servers are blocked on instances. :: table=71, priority=80,udp,reg5=0x1,in_port=1,tp_src=68,tp_dst=67 actions=resubmit(,73) table=71, priority=80,udp6,reg5=0x1,in_port=1,tp_src=546,tp_dst=547 actions=resubmit(,73) table=71, priority=70,udp,reg5=0x1,in_port=1,tp_src=67,tp_dst=68 actions=resubmit(,93) table=71, priority=70,udp6,reg5=0x1,in_port=1,tp_src=547,tp_dst=546 actions=resubmit(,93) table=71, priority=80,udp,reg5=0x2,in_port=2,tp_src=68,tp_dst=67 actions=resubmit(,73) table=71, priority=80,udp6,reg5=0x2,in_port=2,tp_src=546,tp_dst=547 actions=resubmit(,73) table=71, priority=70,udp,reg5=0x2,in_port=2,tp_src=67,tp_dst=68 actions=resubmit(,93) table=71, priority=70,udp6,reg5=0x2,in_port=2,tp_src=547,tp_dst=546 actions=resubmit(,93) Flowing rules obtain conntrack information for valid ip and mac address combinations. All other packets are dropped. :: table=71, priority=65,ip,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:10,nw_src=192.168.0.1 actions=ct(table=72,zone=NXM_NX_REG6[0..15]) table=71, priority=65,ip,reg5=0x1,in_port=1,dl_src=fa:16:3e:8c:84:13,nw_src=10.0.0.1 actions=ct(table=72,zone=NXM_NX_REG6[0..15]) table=71, priority=65,ip,reg5=0x2,in_port=2,dl_src=fa:16:3e:24:57:c7,nw_src=192.168.0.2 actions=ct(table=72,zone=NXM_NX_REG6[0..15]) table=71, priority=65,ip,reg5=0x2,in_port=2,dl_src=fa:16:3e:8c:84:14,nw_src=10.1.0.0/24 actions=ct(table=72,zone=NXM_NX_REG6[0..15]) table=71, priority=65,ipv6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:10,ipv6_src=fe80::f816:3eff:fea4:2210 actions=ct(table=72,zone=NXM_NX_REG6[0..15]) table=71, priority=65,ipv6,reg5=0x2,in_port=2,dl_src=fa:16:3e:24:57:c7,ipv6_src=fe80::f816:3eff:fe24:57c7 actions=ct(table=72,zone=NXM_NX_REG6[0..15]) table=71, priority=10,reg5=0x1,in_port=1 actions=resubmit(,93) table=71, priority=10,reg5=0x2,in_port=2 actions=resubmit(,93) table=71, priority=0 actions=drop ``table 72`` accepts only established or related connections, and implements rules defined by the security group. As this egress connection might also be an ingress connection for some other port, it's not switched yet but eventually processed by ingress pipeline. All established or new connections defined by security group rule are ``accepted``, which will be explained later. All invalid packets are dropped. In case below we allow all icmp egress traffic. :: table=72, priority=75,ct_state=+est-rel-rpl,icmp,reg5=0x1 actions=resubmit(,73) table=72, priority=75,ct_state=+new-est,icmp,reg5=0x1 actions=resubmit(,73) table=72, priority=50,ct_state=+inv+trk actions=resubmit(,93) Important on the flows below is the ``ct_mark=0x1``. Such value have flows that were marked as not existing anymore by rule introduced later. Those are typically connections that were allowed by some security group rule and the rule was removed. :: table=72, priority=50,ct_mark=0x1,reg5=0x1 actions=resubmit(,93) table=72, priority=50,ct_mark=0x1,reg5=0x2 actions=resubmit(,93) All other connections that are not marked and are established or related are allowed. :: table=72, priority=50,ct_state=+est-rel+rpl,ct_zone=644,ct_mark=0,reg5=0x1 actions=resubmit(,94) table=72, priority=50,ct_state=+est-rel+rpl,ct_zone=644,ct_mark=0,reg5=0x2 actions=resubmit(,94) table=72, priority=50,ct_state=-new-est+rel-inv,ct_zone=644,ct_mark=0,reg5=0x1 actions=resubmit(,94) table=72, priority=50,ct_state=-new-est+rel-inv,ct_zone=644,ct_mark=0,reg5=0x2 actions=resubmit(,94) In the following flows are marked established connections that weren't matched in the previous flows, which means they don't have accepting security group rule anymore. :: table=72, priority=40,ct_state=-est,reg5=0x1 actions=resubmit(,93) table=72, priority=40,ct_state=+est,reg5=0x1 actions=ct(commit,zone=NXM_NX_REG6[0..15],exec(load:0x1->NXM_NX_CT_MARK[])) table=72, priority=40,ct_state=-est,reg5=0x2 actions=resubmit(,93) table=72, priority=40,ct_state=+est,reg5=0x2 actions=ct(commit,zone=NXM_NX_REG6[0..15],exec(load:0x1->NXM_NX_CT_MARK[])) table=72, priority=0 actions=drop In following ``table 73`` are all detected ingress connections sent to ingress pipeline. Since the connection was already accepted by egress pipeline, all remaining egress connections are sent to normal flood'n'learn switching (table 94). :: table=73, priority=100,reg6=0x284,dl_dst=fa:16:3e:a4:22:10 actions=load:0x1->NXM_NX_REG5[],resubmit(,81) table=73, priority=100,reg6=0x284,dl_dst=fa:16:3e:8c:84:13 actions=load:0x1->NXM_NX_REG5[],resubmit(,81) table=73, priority=100,reg6=0x284,dl_dst=fa:16:3e:24:57:c7 actions=load:0x2->NXM_NX_REG5[],resubmit(,81) table=73, priority=100,reg6=0x284,dl_dst=fa:16:3e:8c:84:14 actions=load:0x2->NXM_NX_REG5[],resubmit(,81) table=73, priority=90,ct_state=+new-est,reg5=0x1 actions=ct(commit,zone=NXM_NX_REG6[0..15]),resubmit(,91) table=73, priority=90,ct_state=+new-est,reg5=0x2 actions=ct(commit,zone=NXM_NX_REG6[0..15]),resubmit(,91) table=73, priority=80,reg5=0x1 actions=resubmit(,94) table=73, priority=80,reg5=0x2 actions=resubmit(,94) table=73, priority=0 actions=drop ``table 81`` is similar to ``table 71``, allows basic ingress traffic for obtaining ip address and arp queries. Note that vlan tag must be removed by adding ``strip_vlan`` to actions list, prior to injecting packet directly to port. Not tracked packets are sent to obtain conntrack information. :: table=81, priority=100,arp,reg5=0x1 actions=strip_vlan,output:1 table=81, priority=100,arp,reg5=0x2 actions=strip_vlan,output:2 table=81, priority=100,icmp6,reg5=0x1,icmp_type=130 actions=strip_vlan,output:1 table=81, priority=100,icmp6,reg5=0x1,icmp_type=131 actions=strip_vlan,output:1 table=81, priority=100,icmp6,reg5=0x1,icmp_type=132 actions=strip_vlan,output:1 table=81, priority=100,icmp6,reg5=0x1,icmp_type=135 actions=strip_vlan,output:1 table=81, priority=100,icmp6,reg5=0x1,icmp_type=136 actions=strip_vlan,output:1 table=81, priority=100,icmp6,reg5=0x2,icmp_type=130 actions=strip_vlan,output:2 table=81, priority=100,icmp6,reg5=0x2,icmp_type=131 actions=strip_vlan,output:2 table=81, priority=100,icmp6,reg5=0x2,icmp_type=132 actions=strip_vlan,output:2 table=81, priority=100,icmp6,reg5=0x2,icmp_type=135 actions=strip_vlan,output:2 table=81, priority=100,icmp6,reg5=0x2,icmp_type=136 actions=strip_vlan,output:2 table=81, priority=95,udp,reg5=0x1,tp_src=67,tp_dst=68 actions=strip_vlan,output:1 table=81, priority=95,udp6,reg5=0x1,tp_src=547,tp_dst=546 actions=strip_vlan,output:1 table=81, priority=95,udp,reg5=0x2,tp_src=67,tp_dst=68 actions=strip_vlan,output:2 table=81, priority=95,udp6,reg5=0x2,tp_src=547,tp_dst=546 actions=strip_vlan,output:2 table=81, priority=90,ct_state=-trk,ip,reg5=0x1 actions=ct(table=82,zone=NXM_NX_REG6[0..15]) table=81, priority=90,ct_state=-trk,ipv6,reg5=0x1 actions=ct(table=82,zone=NXM_NX_REG6[0..15]) table=81, priority=90,ct_state=-trk,ip,reg5=0x2 actions=ct(table=82,zone=NXM_NX_REG6[0..15]) table=81, priority=90,ct_state=-trk,ipv6,reg5=0x2 actions=ct(table=82,zone=NXM_NX_REG6[0..15]) table=81, priority=80,ct_state=+trk,reg5=0x1 actions=resubmit(,82) table=81, priority=80,ct_state=+trk,reg5=0x2 actions=resubmit(,82) table=81, priority=0 actions=drop Similarly to ``table 72``, ``table 82`` accepts established and related connections. In this case we allow all icmp traffic coming from ``security group 1`` which is in this case only ``port 1``. The first four flows match on the ip addresses, and the next two flows match on the icmp protocol. These six flows define conjunction flows, and the next two define actions for them. :: table=82, priority=71,ct_state=+est-rel-rpl,ip,reg6=0x284,nw_src=192.168.0.1 actions=conjunction(18,1/2) table=82, priority=71,ct_state=+est-rel-rpl,ip,reg6=0x284,nw_src=10.0.0.1 actions=conjunction(18,1/2) table=82, priority=71,ct_state=+new-est,ip,reg6=0x284,nw_src=192.168.0.1 actions=conjunction(19,1/2) table=82, priority=71,ct_state=+new-est,ip,reg6=0x284,nw_src=10.0.0.1 actions=conjunction(19,1/2) table=82, priority=71,ct_state=+est-rel-rpl,icmp,reg5=0x2 actions=conjunction(18,2/2) table=82, priority=71,ct_state=+new-est,icmp,reg5=0x2 actions=conjunction(19,2/2) table=82, priority=71,conj_id=18,ct_state=+est-rel-rpl,ip,reg5=0x2 actions=strip_vlan,output:2 table=82, priority=71,conj_id=19,ct_state=+new-est,ip,reg5=0x2 actions=ct(commit,zone=NXM_NX_REG6[0..15]),strip_vlan,output:2,resubmit(,92) table=82, priority=50,ct_state=+inv+trk actions=resubmit(,93) There are some more security group rules with remote group IDs. Next we look at TCP related ones. Excerpt of flows that correspond to those rules are: :: table=82, priority=73,ct_state=+est-rel-rpl,tcp,reg5=0x2,tp_dst=0x60/0xffe0 actions=conjunction(22,2/2) table=82, priority=73,ct_state=+new-est,tcp,reg5=0x2,tp_dst=0x60/0xffe0 actions=conjunction(23,2/2) table=82, priority=73,ct_state=+est-rel-rpl,tcp,reg5=0x2,tp_dst=0x40/0xfff0 actions=conjunction(22,2/2) table=82, priority=73,ct_state=+new-est,tcp,reg5=0x2,tp_dst=0x40/0xfff0 actions=conjunction(23,2/2) table=82, priority=73,ct_state=+est-rel-rpl,tcp,reg5=0x2,tp_dst=0x58/0xfff8 actions=conjunction(22,2/2) table=82, priority=73,ct_state=+new-est,tcp,reg5=0x2,tp_dst=0x58/0xfff8 actions=conjunction(23,2/2) table=82, priority=73,ct_state=+est-rel-rpl,tcp,reg5=0x2,tp_dst=0x54/0xfffc actions=conjunction(22,2/2) table=82, priority=73,ct_state=+new-est,tcp,reg5=0x2,tp_dst=0x54/0xfffc actions=conjunction(23,2/2) table=82, priority=73,ct_state=+est-rel-rpl,tcp,reg5=0x2,tp_dst=0x52/0xfffe actions=conjunction(22,2/2) table=82, priority=73,ct_state=+new-est,tcp,reg5=0x2,tp_dst=0x52/0xfffe actions=conjunction(23,2/2) table=82, priority=73,ct_state=+est-rel-rpl,tcp,reg5=0x2,tp_dst=80 actions=conjunction(22,2/2),conjunction(14,2/2) table=82, priority=73,ct_state=+est-rel-rpl,tcp,reg5=0x2,tp_dst=81 actions=conjunction(22,2/2) table=82, priority=73,ct_state=+new-est,tcp,reg5=0x2,tp_dst=80 actions=conjunction(23,2/2),conjunction(15,2/2) table=82, priority=73,ct_state=+new-est,tcp,reg5=0x2,tp_dst=81 actions=conjunction(23,2/2) Only dimension 2/2 flows are shown here, as the other are similar to the previous ICMP example. There are many more flows but only the port ranges that covers from 64 to 127 are shown for brevity. The conjunction IDs 14 and 15 correspond to packets from the security group 1, and the conjunction IDs 22 and 23 correspond to those from the security group 2. These flows are from the following security group rules, :: - can receive tcp packets from security group 1 - can receive tcp packets to port 80 from security group 2 and these rules have been processed by ``merge_port_ranges`` into: :: - can receive tcp packets to port != 80 from security group 1 - can receive tcp packets to port 80 from security group 1 or 2 before translating to flows so that there is only one matching flow even when the TCP destination port is 80. The remaining is a L4 protocol agnostic rule. :: table=82, priority=70,ct_state=+est-rel-rpl,ip,reg5=0x2 actions=conjunction(24,2/2) table=82, priority=70,ct_state=+new-est,ip,reg5=0x2 actions=conjunction(25,2/2) Any IP packet that matches the previous TCP flows matches one of these flows, but the corresponding security group rules have different remote group IDs. Unlike the above TCP example, there's no convenient way of expressing ``protocol != TCP`` or ``icmp_code != 1``. So the OVS firewall uses a different priority than the previous TCP flows so as not to mix up them. The mechanism for dropping connections that are not allowed anymore is the same as in ``table 72``. :: table=82, priority=50,ct_mark=0x1,reg5=0x1 actions=resubmit(,93) table=82, priority=50,ct_mark=0x1,reg5=0x2 actions=resubmit(,93) table=82, priority=50,ct_state=+est-rel+rpl,ct_zone=644,ct_mark=0,reg5=0x1 actions=strip_vlan,output:1 table=82, priority=50,ct_state=+est-rel+rpl,ct_zone=644,ct_mark=0,reg5=0x2 actions=strip_vlan,output:2 table=82, priority=50,ct_state=-new-est+rel-inv,ct_zone=644,ct_mark=0,reg5=0x1 actions=strip_vlan,output:1 table=82, priority=50,ct_state=-new-est+rel-inv,ct_zone=644,ct_mark=0,reg5=0x2 actions=strip_vlan,output:2 table=82, priority=40,ct_state=-est,reg5=0x1 actions=resubmit(,93) table=82, priority=40,ct_state=+est,reg5=0x1 actions=ct(commit,zone=NXM_NX_REG6[0..15],exec(load:0x1->NXM_NX_CT_MARK[])) table=82, priority=40,ct_state=-est,reg5=0x2 actions=resubmit(,93) table=82, priority=40,ct_state=+est,reg5=0x2 actions=ct(commit,zone=NXM_NX_REG6[0..15],exec(load:0x1->NXM_NX_CT_MARK[])) table=82, priority=0 actions=drop Note: Conntrack zones on a single node are now based on network to which port is plugged in. That makes a difference between traffic on hypervisor only and east-west traffic. For example, if port has a VIP that was migrated to a port on different node, then new port won't contain conntrack information about previous traffic that happened with VIP. Using OpenFlow in conjunction with OVS firewall ----------------------------------------------- There are three tables where packets are sent once they get through the OVS firewall pipeline. The tables can be used by other mechanisms using OpenFlow that are supposed to work with the OVS firewall. Packets sent to ``table 91`` are considered accepted by the egress pipeline, and will be processed so that they are forwarded to their destination by being submitted to a NORMAL action that results in Ethernet flood/learn processing. Note that ``table 91`` merely resubmits to ``table 94``that contains the actual NORMAL action; this allows to have``table 91`` be a single places where the NORMAL action can be overriden by other components (currently used by ``networking-bagpipe`` driver for ``networking-bgpvpn``). Packets sent to ``table 92`` were processed by the ingress filtering pipeline. As packets from the ingress filtering pipeline were injected to its destination, ``table 92`` receives copies of those packets and therefore default action is ``drop``. Finally, packets sent to ``table 93`` were filtered by the firewall and should be dropped. Default action is ``drop`` in this table. In regard to the performance perspective, please note that only the first accepted packet of each connection session will go to ``table 91`` and ``table 92``. Future work ----------- - Create fullstack tests with tunneling enabled - During the update of firewall rules, we can use bundles to make the changes atomic Upgrade path from iptables hybrid driver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ During an upgrade, the agent will need to re-plug each instance's tap device into the integration bridge while trying to not break existing connections. One of the following approaches can be taken: 1) Pause the running instance in order to prevent a short period of time where its network interface does not have firewall rules. This can happen due to the firewall driver calling OVS to obtain information about OVS the port. Once the instance is paused and no traffic is flowing, we can delete the qvo interface from integration bridge, detach the tap device from the qbr bridge and plug the tap device back into the integration bridge. Once this is done, the firewall rules are applied for the OVS tap interface and the instance is started from its paused state. 2) Set drop rules for the instance's tap interface, delete the qbr bridge and related veths, plug the tap device into the integration bridge, apply the OVS firewall rules and finally remove the drop rules for the instance. 3) Compute nodes can be upgraded one at a time. A free node can be switched to use the OVS firewall, and instances from other nodes can be live-migrated to it. Once the first node is evacuated, its firewall driver can be then be switched to the OVS driver. neutron-12.1.1/doc/source/contributor/internals/provisioning_blocks.rst0000664000175000017500000001630613553660047026562 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Composite Object Status via Provisioning Blocks =============================================== We use the STATUS field on objects to indicate when a resource is ready by setting it to ACTIVE so external systems know when it's safe to use that resource. Knowing when to set the status to ACTIVE is simple when there is only one entity responsible for provisioning a given object. When that entity has finishing provisioning, we just update the STATUS directly to active. However, there are resources in Neutron that require provisioning by multiple asynchronous entities before they are ready to be used so managing the transition to the ACTIVE status becomes more complex. To handle these cases, Neutron has `the provisioning_blocks module `_ to track the entities that are still provisioning a resource. The main example of this is with ML2, the L2 agents and the DHCP agents. When a port is created and bound to a host, it's placed in the DOWN status. The L2 agent now has to setup flows, security group rules, etc for the port and the DHCP agent has to setup a DHCP reservation for the port's IP and MAC. Before the transition to ACTIVE, both agents must complete their work or the port user (e.g. Nova) may attempt to use the port and not have connectivity. To solve this, the provisioning_blocks module is used to track the provisioning state of each agent and the status is only updated when both complete. High Level View --------------- To make use of the provisioning_blocks module, provisioning components should be added whenever there is work to be done by another entity before an object's status can transition to ACTIVE. This is accomplished by calling the add_provisioning_component method for each entity. Then as each entity finishes provisioning the object, the provisioning_complete must be called to lift the provisioning block. When the last provisioning block is removed, the provisioning_blocks module will trigger a callback notification containing the object ID for the object's resource type with the event PROVISIONING_COMPLETE. A subscriber to this event can now update the status of this object to ACTIVE or perform any other necessary actions. A normal state transition will look something like the following: 1. Request comes in to create an object 2. Logic on the Neutron server determines which entities are required to provision the object and adds a provisioning component for each entity for that object. 3. A notification is emitted to the entities so they start their work. 4. Object is returned to the API caller in the DOWN (or BUILD) state. 5. Each entity tells the server when it has finished provisioning the object. The server calls provisioning_complete for each entity that finishes. 6. When provisioning_complete is called on the last remaining entity, the provisioning_blocks module will emit an event indicating that provisioning has completed for that object. 7. A subscriber to this event on the server will then update the status of the object to ACTIVE to indicate that it is fully provisioned. For a more concrete example, see the section below. ML2, L2 agents, and DHCP agents ------------------------------- ML2 makes use of the provisioning_blocks module to prevent the status of ports from being transitioned to ACTIVE until both the L2 agent and the DHCP agent have finished wiring a port. When a port is created or updated, the following happens to register the DHCP agent's provisioning blocks: 1. The subnet_ids are extracted from the fixed_ips field of the port and then ML2 checks to see if DHCP is enabled on any of the subnets. 2. The configuration for the DHCP agents hosting the network are looked up to ensure that at least one of them is new enough to report back that it has finished setting up the port reservation. 3. If either of the preconditions above fail, a provisioning block for the DHCP agent is not added and any existing DHCP agent blocks for that port are cleared to ensure the port isn't blocked waiting for an event that will never happen. 4. If the preconditions pass, a provisioning block is added for the port under the 'DHCP' entity. When a port is created or updated, the following happens to register the L2 agent's provisioning blocks: 1. If the port is not bound, nothing happens because we don't know yet if an L2 agent is involved so we have to wait until a port update that binds it. 2. Once the port is bound, the agent based mechanism drivers will check if they have an agent on the bound host and if the VNIC type belongs to the mechanism driver, a provisioning block is added for the port under the 'L2 Agent' entity. Once the DHCP agent has finished setting up the reservation, it calls dhcp_ready_on_ports via the RPC API with the port ID. The DHCP RPC handler receives this and calls 'provisioning_complete' in the provisioning module with the port ID and the 'DHCP' entity to remove the provisioning block. Once the L2 agent has finished setting up the reservation, it calls the normal update_device_list (or update_device_up) via the RPC API. The RPC callbacks handler calls 'provisioning_complete' with the port ID and the 'L2 Agent' entity to remove the provisioning block. On the 'provisioning_complete' call that removes the last record, the provisioning_blocks module emits a callback PROVISIONING_COMPLETE event with the port ID. A function subscribed to this in ML2 then calls update_port_status to set the port to ACTIVE. At this point the normal notification is emitted to Nova allowing the VM to be unpaused. In the event that the DHCP or L2 agent is down, the port will not transition to the ACTIVE status (as is the case now if the L2 agent is down). Agents must account for this by telling the server that wiring has been completed after configuring everything during startup. This ensures that ports created on offline agents (or agents that crash and restart) eventually become active. To account for server instability, the notifications about port wiring be complete must use RPC calls so the agent gets a positive acknowledgement from the server and it must keep retrying until either the port is deleted or it is successful. If an ML2 driver immediately places a bound port in the ACTIVE state (e.g. after calling a backend in update_port_postcommit), this patch will not have any impact on that process. neutron-12.1.1/doc/source/contributor/internals/security_group_api.rst0000664000175000017500000000630213553660047026406 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Guided Tour: The Neutron Security Group API =========================================== https://wiki.openstack.org/wiki/Neutron/SecurityGroups API Extension ------------- The API extension is the 'front' end portion of the code, which handles defining a `REST-ful API`_, which is used by projects. .. _`REST-ful API`: https://git.openstack.org/cgit/openstack/neutron/tree/neutron/extensions/securitygroup.py Database API ------------ The Security Group API extension adds a number of `methods to the database layer`_ of Neutron .. _`methods to the database layer`: https://git.openstack.org/cgit/openstack/neutron/tree/neutron/db/securitygroups_db.py Agent RPC --------- This portion of the code handles processing requests from projects, after they have been stored in the database. It involves messaging all the L2 agents running on the compute nodes, and modifying the IPTables rules on each hypervisor. * `Plugin RPC classes `_ * `SecurityGroupServerRpcMixin `_ - defines the RPC API that the plugin uses to communicate with the agents running on the compute nodes * SecurityGroupServerRpcMixin - Defines the API methods used to fetch data from the database, in order to return responses to agents via the RPC API * `Agent RPC classes `_ * The SecurityGroupServerRpcApi defines the API methods that can be called by agents, back to the plugin that runs on the Neutron controller * The SecurityGroupAgentRpcCallbackMixin defines methods that a plugin uses to call back to an agent after performing an action called by an agent. IPTables Driver --------------- * ``prepare_port_filter`` takes a ``port`` argument, which is a ``dictionary`` object that contains information about the port - including the ``security_group_rules`` * ``prepare_port_filter`` appends the port to an internal dictionary, ``filtered_ports`` which is used to track the internal state. * Each security group has a `chain `_ in Iptables. * The ``IptablesFirewallDriver`` has a method to convert security group rules into iptables statements. neutron-12.1.1/doc/source/contributor/internals/segments.rst0000664000175000017500000000444013553660046024317 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Segments extension ================== Neutron has an extension that allows CRUD operations on the ``/segments`` resource in the API, that corresponds to the ``NetworkSegment`` entity in the DB layer. The extension is implemented as a service plug-in. .. note:: The ``segments`` service plug-in is not configured by default. To configure it, add ``segments`` to the ``service_plugins`` parameter in ``neutron.conf`` Core plug-ins can coordinate with the ``segments`` service plug-in by subscribing callbacks to events associated to the ``SEGMENT`` resource. Currently, the segments plug-in notifies subscribers of the following events: * ``PRECOMMIT_CREATE`` * ``AFTER_CREATE`` * ``BEFORE_DELETE`` * ``PRECOMMIT_DELETE`` * ``AFTER_DELETE`` As of this writing, ``ML2`` and ``OVN`` register callbacks to receive events from the ``segments`` service plug-in. The ``ML2`` plug-in defines the callback ``_handle_segment_change`` to process all the relevant segments events. Segments extension relevant modules ----------------------------------- * ``neutron/extensions/segment.py`` defines the extension * ``neutron/db/models/segment.py`` defines the DB models for segments and for the segment host mapping, that is used in the implementation of routed networks. * ``neutron/db/segments_db.py`` has functions to add, retrieve and delete segments from the DB. * ``neutron/services/segments/db.py`` defines a mixin class with the methods that perform API CRUD operations for the ``segments`` plug-in. It also has a set of functions to create and maintain the mapping of segments to hosts, which is necessary in the implementation of routed networks. * ``neutron/services/segments/plugin.py`` defines the ``segments`` service plug-in. neutron-12.1.1/doc/source/contributor/internals/live_migration.rst0000664000175000017500000001765713553660046025520 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Live-migration ============== Let's consider a VM with one port migrating from host1 with nova-compute1, neutron-l2-agent1 and neutron-l3-agent1 to host2 with nova-compute2 and neutron-l2-agent2 and neutron-l3agent2. Since the VM that is about to migrate is hosted by nova-compute1, nova sends the live-migration order to nova-compute1 through RPC. Nova Live Migration consists of the following stages: * Pre-live-migration * Live-migration-operation * Post-live-migration Pre-live-migration actions -------------------------- Nova-compute1 will first ask nova-compute2 to perform pre-live-migration actions with a synchronous RPC call. Nova-compute2 will use neutron REST API to retrieve the list of VM's ports. Then, it calls its vif driver to create the VM's port (VIF) using plug_vifs(). In the case Open vSwitch Hybrid plug is used, Neutron-l2-agent2 will detect this new VIF, request the device details from the neutron server and configure it accordingly. However, port's status won't change, since this port is not bound to nova-compute2. Nova-compute1 calls setup_networks_on_hosts. This updates the Neutron ports binding:profile with the information of the target host. The port update RPC message sent out by Neutron server will be received by neutron-l3-agent2, which proactively sets up the DVR router. If pre-live-migration fails, nova rollbacks and port is removed from host2. If pre-live-migration succeeds, nova proceeds with live-migration-operation. Potential error cases related to networking ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Plugging the VIFs on host2 fails As Live migration operation was not yet started, the instance resides active on host1. .. _live_mig_operation: Live-migration-operation ------------------------ Once nova-compute2 has performed pre-live-migration actions, nova-compute1 can start the live-migration. This results in the creation of the VM and its corresponding tap interface on node 2. In the case Open vSwitch normal plug, linux bridge or MacVTap is being used, Neutron-l2-agent2 will detect this new tap device and configure it accordingly. However, port's status won't change, since this port is not bound to nova-compute2. As soon as the instance is active on host2, the original instance on host1 gets removed and with it the corresponding tap device. Assuming OVS-hybrid plug is NOT used, Neutron-l2-agent1 detects the removal and tells the neutron server to set the port's status to DOWN state with RPC messages. There is no rollback if failure happens in live-migration-operation stage. TBD: Error are handled by the post-live-migration stage. Potential error cases related to networking ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Some host devices that are specified in the instance definition are not present on the target host. Migration fails before it really started. This can happen with MacVTap agent. See bug https://bugs.launchpad.net/bugs/1550400 Post-live-migration actions --------------------------- Once live-migration succeeded, both nova-compute1 and nova-compute2 perform post-live-migration actions. Nova-compute1 which is aware of the success will send a RPC cast to nova-compute2 to tell it to perform post-live-migration actions. On host2, nova-compute2 sends a REST call "update_port(binding=host2, profile={})" to the neutron server to tell it to update the port's binding. This will clear the port binding information and move the port's status to DOWN. The ML2 plugin will then try to rebind the port according to its new host. This update_port REST call always triggers a port-update RPC fanout message to every neutron-l2-agent. Since neutron-l2-agent2 is now hosting the port, it will take this message into account and re-synchronize the port by asking the neutron server details about it through RPC messages. This will move the port from DOWN status to BUILD, and then back to ACTIVE. This update also removes the 'migrating_to' value from the portbindng dictionary. It's not clearing it totally, like indicated by {}, but just removing the 'migrating_to' key and value. On host1, nova-compute1 calls its vif driver to unplug the VM's port. Assuming, Open vSwitch Hybrid plug is used, Neutron-l2-agent1 detects the removal and tells the neutron server to set the port's status to DOWN state with RPC messages. For all other cases this happens as soon as the instance and its tap device got destroyed on host1, like described in :ref:`live_mig_operation`. If neutron didn't already processed the REST call "update_port(binding=host2)", the port status will effectively move to BUILD and then to DOWN. Otherwise, the port is bound to host2, and neutron won't change the port status since it's not bound the host that is sending RPC messages. There is no rollback if failure happens in post-live-migration stage. In the case of an error, the instance is set into "ERROR" state. Potential error cases related to networking ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Portbinding for host2 fails If this happens, the vif_type of the port is set to 'binding_failed'. When Nova tries to recreated the domain.xml on the migration target it will stumble over this invalid vif_type and fail. The instance is put into "ERROR" state. Post-Copy Migration ------------------- Usually, Live Migration is executed as pre-copy migration. The instance is active on host1 until nearly all memory has been copied to host2. If a certain threshold of copied memory is met, the instance on the source get's paused, the rest of the memory copied over and the instance started on the target. The challenge with this approach is, that migration might take a infinite amount of time, when the instance is heavily writing to memory. This issue gets solved with post-copy migration. At some point in time, the instance on host2 will be set to active, although still a huge amount of memory pages reside only on host1. The phase that starts now is called the post_copy phase. If the instance tries to access a memory page that has not yet been transferred, libvirt/qemu takes care of moving this page to the target immediately. New pages will only be written to the source. With this approach the migration operation takes a finite amount of time. Today, the rebinding of the port from host1 to host2 happens in the post_live_migration phase, after migration finished. This is fine for the pre-copy case, as the time windows between the activation of the instance on the target and the binding of the port to the target is pretty small. This becomes more problematic for the post-copy migration case. The instance becomes active on the target pretty early but the portbinding still happens after migration finished. During this time window, the instance might not be reachable via the network. This should be solved with bug https://bugs.launchpad.net/nova/+bug/1605016 Flow Diagram ------------ OVS Normal plug, Linux bridge, MacVTap, SR-IOV ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. image:: images/live-mig.png OVS-Hybrid plug ~~~~~~~~~~~~~~~ The sequence with RPC messages from neutron-l2-agent processed first is described in the following UML sequence diagram .. image:: images/live-mig-ovs-hybrid.png neutron-12.1.1/doc/source/contributor/internals/objects_usage.rst0000664000175000017500000007212713553660047025317 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Objects in neutron ================== Object versioning is a key concept in achieving rolling upgrades. Since its initial implementation by the nova community, a versioned object model has been pushed to an oslo library so that its benefits can be shared across projects. `Oslo VersionedObjects`_ (aka OVO) is a database facade, where you define the middle layer between software and the database schema. In this layer, a versioned object per database resource is created with a strict data definition and version number. With OVO, when you change the database schema, the version of the object also changes and a backward compatible translation is provided. This allows different versions of software to communicate with one another (via RPC). OVO is also commonly used for RPC payload versioning. OVO creates versioned dictionary messages by defining a strict structure and keeping strong typing. Because of it, you can be sure of what is sent and how to use the data on the receiving end. .. _Oslo VersionedObjects: https://docs.openstack.org/oslo.versionedobjects/latest/ Usage of objects ---------------- CRUD operations ~~~~~~~~~~~~~~~ Objects support CRUD operations: :code:`create()`, :code:`get_object()` and :code:`get_objects()` (equivalent of :code:`read`), :code:`update()`, :code:`delete()`, :code:`update_objects()`, and :code:`delete_objects()`. The nature of OVO is, when any change is applied, OVO tracks it. After calling :code:`create()` or :code:`update()`, OVO detects this and changed fields are saved in the database. Please take a look at simple object usage scenarios using example of DNSNameServer: .. code-block:: Python # to create an object, you can pass the attributes in constructor: dns = DNSNameServer(context, address='asd', subnet_id='xxx', order=1) dns.create() # or you can create a dict and pass it as kwargs: dns_data = {'address': 'asd', 'subnet_id': 'xxx', 'order': 1} dns = DNSNameServer(context, **dns_data) dns.create() # for fetching multiple objects: dnses = DNSNameServer.get_objects(context) # will return list of all dns name servers from DB # for fetching objects with substrings in a string field: from neutron.objects import utils as obj_utils dnses = DNSNameServer.get_objects(context, address=obj_utils.StringContains('10.0.0')) # will return list of all dns name servers from DB that has '10.0.0' in their addresses # to update fields: dns = DNSNameServer.get_object(context, address='asd', subnet_id='xxx') dns.order = 2 dns.update() # if you don't care about keeping the object, you can execute the update # without fetch of the object state from the underlying persistent layer count = DNSNameServer.update_objects( context, {'order': 3}, address='asd', subnet_id='xxx') # to remove object with filter arguments: filters = {'address': 'asd', 'subnet_id': 'xxx'} DNSNameServer.delete_objects(context, **filters) Filter, sort and paginate ~~~~~~~~~~~~~~~~~~~~~~~~~ The :code:`NeutronDbObject` class has strict validation on which field sorting and filtering can happen. When calling :code:`get_objects()`, :code:`count()`, :code:`update_objects()`, :code:`delete_objects()` and :code:`objects_exist()`, :code:`validate_filters()` is invoked, to see if it's a supported filter criterion (which is by default non-synthetic fields only). Additional filters can be defined using :code:`register_filter_hook_on_model()`. This will add the requested string to valid filter names in object implementation. It is optional. In order to disable filter validation, :code:`validate_filters=False` needs to be passed as an argument in aforementioned methods. It was added because the default behaviour of the neutron API is to accept everything at API level and filter it out at DB layer. This can be used by out of tree extensions. :code:`register_filter_hook_on_model()` is a complementary implementation in the :code:`NeutronDbObject` layer to DB layer's :code:`register_model_query_hook()`, which adds support for extra filtering during construction of SQL query. When extension defines extra query hook, it needs to be registered using the objects :code:`register_filter_hook_on_model()`, if it is not already included in the objects :code:`fields`. To limit or paginate results, :code:`Pager` object can be used. It accepts :code:`sorts` (list of :code:`(key, direction)` tuples), :code:`limit`, :code:`page_reverse` and :code:`marker` keywords. .. code-block:: Python # filtering # to get an object based on primary key filter dns = DNSNameServer.get_object(context, address='asd', subnet_id='xxx') # to get multiple objects dnses = DNSNameServer.get_objects(context, subnet_id='xxx') filters = {'subnet_id': ['xxx', 'yyy']} dnses = DNSNameServer.get_objects(context, **filters) # do not validate filters dnses = DNSNameServer.get_objects(context, validate_filters=False, fake_filter='xxx') # count the dns servers for given subnet dns_count = DNSNameServer.count(context, subnet_id='xxx') # sorting # direction True == ASC, False == DESC direction = False pager = Pager(sorts=[('order', direction)]) dnses = DNSNameServer.get_objects(context, _pager=pager, subnet_id='xxx') Defining your own object ------------------------ In order to add a new object in neutron, you have to: #. Create an object derived from :code:`NeutronDbObject` (aka base object) #. Add/reuse data model #. Define fields It is mandatory to define data model using :code:`db_model` attribute from :code:`NeutronDbObject`. Fields should be defined using :code:`oslo_versionobjects.fields` exposed types. If there is a special need to create a new type of field, you can use :code:`common_types.py` in the :code:`neutron.objects` directory. Example:: fields = { 'id': common_types.UUIDField(), 'name': obj_fields.StringField(), 'subnetpool_id': common_types.UUIDField(nullable=True), 'ip_version': common_types.IPVersionEnumField() } :code:`VERSION` is mandatory and defines the version of the object. Initially, set the :code:`VERSION` field to 1.0. Change :code:`VERSION` if fields or their types are modified. When you change the version of objects being exposed via RPC, add method :code:`obj_make_compatible(self, primitive, target_version)`. .. note:: Standard Attributes are automatically added to OVO fields in base class. Attributes [#]_ like :code:`description`, :code:`created_at`, :code:`updated_at` and :code:`revision_number` are added in [#]_. :code:`primary_keys` is used to define the list of fields that uniquely identify the object. In case of database backed objects, it's usually mapped onto SQL primary keys. For immutable object fields that cannot be changed, there is a :code:`fields_no_update` list, that contains :code:`primary_keys` by default. If there is a situation where a field needs to be named differently in an object than in the database schema, you can use :code:`fields_need_translation`. This dictionary contains the name of the field in the object definition (the key) and the name of the field in the database (the value). This allows to have a different object layer representation for database persisted data. For example in IP allocation pools:: fields_need_translation = { 'start': 'first_ip', # field_ovo: field_db 'end': 'last_ip' } The above dictionary is used in :code:`modify_fields_from_db()` and in :code:`modify_fields_to_db()` methods which are implemented in base class and will translate the software layer to database schema naming, and vice versa. It can also be used to rename :code:`orm.relationship` backed object-type fields. Most object fields are usually directly mapped to database model attributes. Sometimes it's useful to expose attributes that are not defined in the model table itself, like relationships and such. In this case, :code:`synthetic_fields` may become handy. This object property can define a list of object fields that don't belong to the object database model and that are hence instead to be implemented in some custom way. Some of those fields map to :code:`orm.relationships` defined on models, while others are completely untangled from the database layer. When exposing existing :code:`orm.relationships` as an ObjectField-typed field, you can use the :code:`foreign_keys` object property that defines a link between two object types. When used, it allows objects framework to automatically instantiate child objects, and fill the relevant parent fields, based on :code:`orm.relationships` defined on parent models. In order to automatically populate the :code:`synthetic_fields`, the :code:`foreign_keys` property is introduced. :code:`load_synthetic_db_fields()` [#]_ method from NeutronDbObject uses :code:`foreign_keys` to match the foreign key in related object and local field that the foreign key is referring to. See simplified examples: .. code-block:: Python class DNSNameServerSqlModel(model_base.BASEV2): address = sa.Column(sa.String(128), nullable=False, primary_key=True) subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id', ondelete="CASCADE"), primary_key=True) class SubnetSqlModel(model_base.BASEV2, HasId, HasProject): name = sa.Column(sa.String(attr.NAME_MAX_LEN)) allocation_pools = orm.relationship(IPAllocationPoolSqlModel) dns_nameservers = orm.relationship(DNSNameServerSqlModel, backref='subnet', cascade='all, delete, delete-orphan', lazy='subquery') class IPAllocationPoolSqlModel(model_base.BASEV2, HasId): subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id')) @obj_base.VersionedObjectRegistry.register class DNSNameServerOVO(base.NeutronDbObject): VERSION = '1.0' db_model = DNSNameServerSqlModel # Created based on primary_key=True in model definition. # The object is uniquely identified by the pair of address and # subnet_id fields. Override the default 'id' 1-tuple. primary_keys = ['address', 'subnet_id'] # Allow to link DNSNameServerOVO child objects into SubnetOVO parent # object fields via subnet_id child database model attribute. # Used during loading synthetic fields in SubnetOVO get_objects. foreign_keys = {'SubnetOVO': {'subnet_id': 'id'}} fields = { 'address': obj_fields.StringField(), 'subnet_id': common_types.UUIDField(), } @obj_base.VersionedObjectRegistry.register class SubnetOVO(base.NeutronDbObject): VERSION = '1.0' db_model = SubnetSqlModel fields = { 'id': common_types.UUIDField(), # HasId from model class 'project_id': obj_fields.StringField(nullable=True), # HasProject from model class 'subnet_name': obj_fields.StringField(nullable=True), 'dns_nameservers': obj_fields.ListOfObjectsField('DNSNameServer', nullable=True), 'allocation_pools': obj_fields.ListOfObjectsField('IPAllocationPoolOVO', nullable=True) } # Claim dns_nameservers field as not directly mapped into the object # database model table. synthetic_fields = ['allocation_pools', 'dns_nameservers'] # Rename in-database subnet_name attribute into name object field fields_need_translation = { 'name': 'subnet_name' } @obj_base.VersionedObjectRegistry.register class IPAllocationPoolOVO(base.NeutronDbObject): VERSION = '1.0' db_model = IPAllocationPoolSqlModel fields = { 'subnet_id': common_types.UUIDField() } foreign_keys = {'SubnetOVO': {'subnet_id': 'id'}} The :code:`foreign_keys` is used in :code:`SubnetOVO` to populate the :code:`allocation_pools` [#]_ synthetic field using the :code:`IPAllocationPoolOVO` class. Single object type may be linked to multiple parent object types, hence :code:`foreign_keys` property may have multiple keys in the dictionary. .. note:: :code:`foreign_keys` is declared in related object :code:`IPAllocationPoolOVO`, the same way as it's done in the SQL model :code:`IPAllocationPoolSqlModel`: :code:`sa.ForeignKey('subnets.id')` .. note:: Only single foreign key is allowed (usually parent ID), you cannot link through multiple model attributes. It is important to remember about the nullable parameter. In the SQLAlchemy model, the nullable parameter is by default :code:`True`, while for OVO fields, the nullable is set to :code:`False`. Make sure you correctly map database column nullability properties to relevant object fields. Database session activation --------------------------- By default, all objects use old ``oslo.db`` engine facade. To enable the new facade for a particular object, set ``new_facade`` class attribute to ``True``: .. code-block:: Python @obj_base.VersionedObjectRegistry.register class ExampleObject(base.NeutronDbObject): new_facade = True It will make all OVO actions - ``get_object``, ``update``, ``count`` etc. - to use new ``reader.using`` or ``writer.using`` decorators to manage database transactions. Whenever you need to open a new subtransaction in scope of OVO code, use the following database session decorators: .. code-block:: Python @obj_base.VersionedObjectRegistry.register class ExampleObject(base.NeutronDbObject): @classmethod def get_object(cls, context, **kwargs): with cls.db_context_reader(context): super(ExampleObject, cls).get_object(context, **kwargs) # fetch more data in the same transaction def create(self): with self.db_context_writer(self.obj_context): super(ExampleObject, self).create() # apply more changes in the same transaction ``db_context_reader`` and ``db_context_writer`` decorators abstract the choice of engine facade used for particular object from action implementation. Synthetic fields ---------------- :code:`synthetic_fields` is a list of fields, that are not directly backed by corresponding object SQL table attributes. Synthetic fields are not limited in types that can be used to implement them. .. code-block:: Python fields = { 'dhcp_agents': obj_fields.ObjectField('NetworkDhcpAgentBinding', nullable=True), # field that contains another single NeutronDbObject of NetworkDhcpAgentBinding type 'shared': obj_fields.BooleanField(default=False), 'subnets': obj_fields.ListOfObjectsField('Subnet', nullable=True) } # All three fields do not belong to corresponding SQL table, and will be # implemented in some object-specific way. synthetic_fields = ['dhcp_agents', 'shared', 'subnets'] :code:`ObjectField` and :code:`ListOfObjectsField` take the name of object class as an argument. Implementing custom synthetic fields ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sometimes you may want to expose a field on an object that is not mapped into a corresponding database model attribute, or its :code:`orm.relationship`; or may want to expose a :code:`orm.relationship` data in a format that is not directly mapped onto a child object type. In this case, here is what you need to do to implement custom getters and setters for the custom field. The custom method to load the synthetic fields can be helpful if the field is not directly defined in the database, OVO class is not suitable to load the data or the related object contains only the ID and property of the parent object, for example :code:`subnet_id` and property of it: :code:`is_external`. In order to implement the custom method to load the synthetic field, you need to provide loading method in the OVO class and override the base class method :code:`from_db_object()` and :code:`obj_load_attr()`. The first one is responsible for loading the fields to object attributes when calling :code:`get_object()` and :code:`get_objects()`, :code:`create()` and :code:`update()`. The second is responsible for loading attribute when it is not set in object. Also, when you need to create related object with attributes passed in constructor, :code:`create()` and :code:`update()` methods need to be overwritten. Additionally :code:`is_external` attribute can be exposed as a boolean, instead of as an object-typed field. When field is changed, but it doesn't need to be saved into database, :code:`obj_reset_changes()` can be called, to tell OVO library to ignore that. Let's see an example: .. code-block:: Python @obj_base.VersionedObjectRegistry.register class ExternalSubnet(base.NeutronDbObject): VERSION = '1.0' fields = {'subnet_id': common_types.UUIDField(), 'is_external': obj_fields.BooleanField()} primary_keys = ['subnet_id'] foreign_keys = {'Subnet': {'subnet_id': 'id'}} @obj_base.VersionedObjectRegistry.register class Subnet(base.NeutronDbObject): VERSION = '1.0' fields = {'external': obj_fields.BooleanField(nullable=True),} synthetic_fields = ['external'] # support new custom 'external=' filter for get_objects family of # objects API def __init__(self, context=None, **kwargs): super(Subnet, self).__init__(context, **kwargs) self.add_extra_filter_name('external') def create(self): fields = self.get_changes() with db_api.context_manager.writer.using(context): if 'external' in fields: ExternalSubnet(context, subnet_id=self.id, is_external=fields['external']).create() # Call to super() to create the SQL record for the object, and # reload its fields from the database, if needed. super(Subnet, self).create() def update(self): fields = self.get_changes() with db_api.context_manager.writer.using(context): if 'external' in fields: # delete the old ExternalSubnet record, if present obj_db_api.delete_objects( self.obj_context, ExternalSubnet.db_model, subnet_id=self.id) # create the new intended ExternalSubnet object ExternalSubnet(context, subnet_id=self.id, is_external=fields['external']).create() # calling super().update() will reload the synthetic fields # and also will update any changed non-synthetic fields, if any super(Subnet, self).update() # this method is called when user of an object accesses the attribute # and requested attribute is not set. def obj_load_attr(self, attrname): if attrname == 'external': return self._load_external() # it is important to call super if attrname does not match # because the base implementation is handling the nullable case super(Subnet, self).obj_load_attr(attrname) def _load_external(self, db_obj=None): # do the loading here if db_obj: # use DB model to fetch the data that may be side-loaded external = db_obj.external.is_external if db_obj.external else None else: # perform extra operation to fetch the data from DB external_obj = ExternalSubnet.get_object(context, subnet_id=self.id) external = external_obj.is_external if external_obj else None # it is important to set the attribute and call obj_reset_changes setattr(self, 'external', external) self.obj_reset_changes(['external']) # this is defined in NeutronDbObject and is invoked during get_object(s) # and create/update. def from_db_object(self, obj): super(Subnet, self).from_db_object(obj) self._load_external(obj) In the above example, the :code:`get_object(s)` methods do not have to be overwritten, because :code:`from_db_object()` takes care of loading the synthetic fields in custom way. Standard attributes ------------------- The standard attributes are added automatically in metaclass :code:`DeclarativeObject`. If adding standard attribute, it has to be added in ``neutron/objects/extensions/standardattributes.py``. It will be added to all relevant objects that use the :code:`standardattributes` model. Be careful when adding something to the above, because it could trigger a change in the object's :code:`VERSION`. For more on how standard attributes work, check [#]_. RBAC handling in objects ------------------------ The RBAC is implemented currently for resources like: Subnet(*), Network and QosPolicy. Subnet is a special case, because access control of Subnet depends on Network RBAC entries. The RBAC support for objects is defined in ``neutron/objects/rbac_db.py``. It defines new base class :code:`NeutronRbacObject`. The new class wraps standard :code:`NeutronDbObject` methods like :code:`create()`, :code:`update()` and :code:`to_dict()`. It checks if the :code:`shared` attribute is defined in the :code:`fields` dictionary and adds it to :code:`synthetic_fields`. Also, :code:`rbac_db_model` is required to be defined in Network and QosPolicy classes. :code:`NeutronRbacObject` is a common place to handle all operations on the RBAC entries, like getting the info if resource is shared or not, creation and updates of them. By wrapping the :code:`NeutronDbObject` methods, it is manipulating the 'shared' attribute while :code:`create()` and :code:`update()` methods are called. The example of defining the Network OVO: .. code-block:: Python class Network(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a v2 neutron network.""" name = sa.Column(sa.String(attr.NAME_MAX_LEN)) rbac_entries = orm.relationship(rbac_db_models.NetworkRBAC, backref='network', lazy='joined', cascade='all, delete, delete-orphan') # Note the base class for Network OVO: @obj_base.VersionedObjectRegistry.register class Network(rbac_db.NeutronRbacObject): # Version 1.0: Initial version VERSION = '1.0' # rbac_db_model is required to be added here rbac_db_model = rbac_db_models.NetworkRBAC db_model = models_v2.Network fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(nullable=True), # share is required to be added to fields 'shared': obj_fields.BooleanField(default=False), } .. note:: The :code:`shared` field is not added to the :code:`synthetic_fields`, because :code:`NeutronRbacObject` requires to add it by itself, otherwise :code:`ObjectActionError` is raised. [#]_ Extensions to neutron resources ------------------------------- One of the methods to extend neutron resources is to add an arbitrary value to dictionary representing the data by providing :code:`extend_(subnet|port|network)_dict()` function and defining loading method. From DB perspective, all the data will be loaded, including all declared fields from DB relationships. Current implementation for core resources (Port, Subnet, Network etc.) is that DB result is parsed by :code:`make__dict()` and :code:`extend__dict()`. When extension is enabled, :code:`extend__dict()` takes the DB results and declares new fields in resulting dict. When extension is not enabled, data will be fetched, but will not be populated into resulting dict, because :code:`extend__dict()` will not be called. Plugins can still use objects for some work, but then convert them to dicts and work as they please, extending the dict as they wish. For example: .. code-block:: Python class TestSubnetExtension(model_base.BASEV2): subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id', ondelete="CASCADE"), primary_key=True) value = sa.Column(sa.String(64)) subnet = orm.relationship( models_v2.Subnet, # here is the definition of loading the extension with Subnet model: backref=orm.backref('extension', cascade='delete', uselist=False)) @oslo_obj_base.VersionedObjectRegistry.register_if(False) class TestSubnetExtensionObject(obj_base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = TestSubnetExtension fields = { 'subnet_id': common_types.UUIDField(), 'value': obj_fields.StringField(nullable=True) } primary_keys = ['subnet_id'] foreign_keys = {'Subnet': {'subnet_id': 'id'}} @obj_base.VersionedObjectRegistry.register class Subnet(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' fields = { 'id': common_types.UUIDField(), 'extension': obj_fields.ObjectField(TestSubnetExtensionObject.__name__, nullable=True), } synthetic_fields = ['extension'] # when defining the extend_subnet_dict function: def extend_subnet_dict(self, session, subnet_ovo, result): value = subnet_ovo.extension.value if subnet_ovo.extension else '' result['subnet_extension'] = value The above example is the ideal situation, where all extensions have objects adopted and enabled in core neutron resources. By introducing the OVO work in tree, interface between base plugin code and registered extension functions hasn't been changed. Those still receive a SQLAlchemy model, not an object. This is achieved by capturing the corresponding database model on :code:`get_***/create/update`, and exposing it via :code:`.db_obj` Backward compatibility for tenant_id ------------------------------------ All objects can support :code:`tenant_id` and :code:`project_id` filters and fields at the same time; it is automatically enabled for all objects that have a :code:`project_id` field. The base :code:`NeutronDbObject` class has support for exposing :code:`tenant_id` in dictionary access to the object fields (:code:`subnet['tenant_id']`) and in :code:`to_dict()` method. There is a :code:`tenant_id` read-only property for every object that has :code:`project_id` in :code:`fields`. It is not exposed in :code:`obj_to_primitive()` method, so it means that :code:`tenant_id` will not be sent over RPC callback wire. When talking about filtering/sorting by :code:`tenant_id`, the filters should be converted to expose :code:`project_id` field. This means that for the long run, the API layer should translate it, but as temporary workaround it can be done at DB layer before passing filters to objects :code:`get_objects()` method, for example: .. code-block:: Python def convert_filters(result): if 'tenant_id' in result: result['project_id'] = result.pop('tenant_id') return result def get_subnets(context, filters): filters = convert_filters(**filters) return subnet_obj.Subnet.get_objects(context, **filters) The :code:`convert_filters` method is available in ``neutron.objects.utils`` [#]_. References ---------- .. [#] https://git.openstack.org/cgit/openstack/neutron/tree/neutron/objects/base.py?h=stable/ocata#n258 .. [#] https://git.openstack.org/cgit/openstack/neutron/tree/neutron/db/standard_attr.py?h=stable/ocata .. [#] https://git.openstack.org/cgit/openstack/neutron/tree/neutron/objects/base.py?h=stable/ocata#n516 .. [#] https://git.openstack.org/cgit/openstack/neutron/tree/neutron/objects/base.py?h=stable/ocata#n542 .. [#] https://docs.openstack.org/neutron/latest/contributor/internals/db_layer.html#the-standard-attribute-table .. [#] https://git.openstack.org/cgit/openstack/neutron/tree/neutron/objects/rbac_db.py?h=stable/ocata#n291 .. [#] https://git.openstack.org/cgit/openstack/neutron/tree/neutron/objects/utils.py?h=stable/ocata neutron-12.1.1/doc/source/contributor/internals/ml2_ext_manager.rst0000664000175000017500000000267513553660046025546 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ML2 Extension Manager ===================== The extension manager for ML2 was introduced in Juno (more details can be found in the approved `spec `_). The features allows for extending ML2 resources without actually having to introduce cross cutting concerns to ML2. The mechanism has been applied for a number of use cases, and extensions that currently use this frameworks are available under `ml2/extensions `_. neutron-12.1.1/doc/source/contributor/internals/quota.rst0000664000175000017500000004416113553660047023630 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Quota Management and Enforcement ================================ Most resources exposed by the Neutron API are subject to quota limits. The Neutron API exposes an extension for managing such quotas. Quota limits are enforced at the API layer, before the request is dispatched to the plugin. Default values for quota limits are specified in neutron.conf. Admin users can override those defaults values on a per-project basis. Limits are stored in the Neutron database; if no limit is found for a given resource and project, then the default value for such resource is used. Configuration-based quota management, where every project gets the same quota limit specified in the configuration file, has been deprecated as of the Liberty release. Please note that Neutron does not support both specification of quota limits per user and quota management for hierarchical multitenancy (as a matter of fact Neutron does not support hierarchical multitenancy at all). Also, quota limits are currently not enforced on RPC interfaces listening on the AMQP bus. Plugin and ML2 drivers are not supposed to enforce quotas for resources they manage. However, the subnet_allocation [#]_ extension is an exception and will be discussed below. The quota management and enforcement mechanisms discussed here apply to every resource which has been registered with the Quota engine, regardless of whether such resource belongs to the core Neutron API or one of its extensions. High Level View --------------- There are two main components in the Neutron quota system: * The Quota API extensions. * The Quota Engine. Both components rely on a quota driver. The neutron codebase currently defines two quota drivers: * neutron.db.quota.driver.DbQuotaDriver * neutron.quota.ConfDriver The latter driver is however deprecated. The Quota API extension handles quota management, whereas the Quota Engine component handles quota enforcement. This API extension is loaded like any other extension. For this reason plugins must explicitly support it by including "quotas" in the supported_extension_aliases attribute. In the Quota API simple CRUD operations are used for managing project quotas. Please note that the current behaviour when deleting a project quota is to reset quota limits for that project to configuration defaults. The API extension does not validate the project identifier with the identity service. In addition, the Quota Detail API extension complements the Quota API extension by allowing users (typically admins) the ability to retrieve details about quotas per project. Quota details include the used/limit/reserved count for the project's resources (networks, ports, etc.). Performing quota enforcement is the responsibility of the Quota Engine. RESTful API controllers, before sending a request to the plugin, try to obtain a reservation from the quota engine for the resources specified in the client request. If the reservation is successful, then it proceeds to dispatch the operation to the plugin. For a reservation to be successful, the total amount of resources requested, plus the total amount of resources reserved, plus the total amount of resources already stored in the database should not exceed the project's quota limit. Finally, both quota management and enforcement rely on a "quota driver" [#]_, whose task is basically to perform database operations. Quota Management ---------------- The quota management component is fairly straightforward. However, unlike the vast majority of Neutron extensions, it uses it own controller class [#]_. This class does not implement the POST operation. List, get, update, and delete operations are implemented by the usual index, show, update and delete methods. These method simply call into the quota driver for either fetching project quotas or updating them. The _update_attributes method is called only once in the controller lifetime. This method dynamically updates Neutron's resource attribute map [#]_ so that an attribute is added for every resource managed by the quota engine. Request authorisation is performed in this controller, and only 'admin' users are allowed to modify quotas for projects. As the neutron policy engine is not used, it is not possible to configure which users should be allowed to manage quotas using policy.json. The driver operations dealing with quota management are: * delete_tenant_quota, which simply removes all entries from the 'quotas' table for a given project identifier; * update_quota_limit, which adds or updates an entry in the 'quotas' project for a given project identifier and a given resource name; * _get_quotas, which fetches limits for a set of resource and a given project identifier * _get_all_quotas, which behaves like _get_quotas, but for all projects. Resource Usage Info ------------------- Neutron has two ways of tracking resource usage info: * CountableResource, where resource usage is calculated every time quotas limits are enforced by counting rows in the resource table and reservations for that resource. * TrackedResource, which instead relies on a specific table tracking usage data, and performs explicitly counting only when the data in this table are not in sync with actual used and reserved resources. Another difference between CountableResource and TrackedResource is that the former invokes a plugin method to count resources. CountableResource should be therefore employed for plugins which do not leverage the Neutron database. The actual class that the Neutron quota engine will use is determined by the track_quota_usage variable in the quota configuration section. If True, TrackedResource instances will be created, otherwise the quota engine will use CountableResource instances. Resource creation is performed by the create_resource_instance factory method in the neutron.quota.resource module. From a performance perspective, having a table tracking resource usage has some advantages, albeit not fundamental. Indeed the time required for executing queries to explicitly count objects will increase with the number of records in the table. On the other hand, using TrackedResource will fetch a single record, but has the drawback of having to execute an UPDATE statement once the operation is completed. Nevertheless, CountableResource instances do not simply perform a SELECT query on the relevant table for a resource, but invoke a plugin method, which might execute several statements and sometimes even interacts with the backend before returning. Resource usage tracking also becomes important for operational correctness when coupled with the concept of resource reservation, discussed in another section of this chapter. Tracking quota usage is not as simple as updating a counter every time resources are created or deleted. Indeed a quota-limited resource in Neutron can be created in several ways. While a RESTful API request is the most common one, resources can be created by RPC handlers listing on the AMQP bus, such as those which create DHCP ports, or by plugin operations, such as those which create router ports. To this aim, TrackedResource instances are initialised with a reference to the model class for the resource for which they track usage data. During object initialisation, SqlAlchemy event handlers are installed for this class. The event handler is executed after a record is inserted or deleted. As result usage data for that resource and will be marked as 'dirty' once the operation completes, so that the next time usage data is requested, it will be synchronised counting resource usage from the database. Even if this solution has some drawbacks, listed in the 'exceptions and caveats' section, it is more reliable than solutions such as: * Updating the usage counters with the new 'correct' value every time an operation completes. * Having a periodic task synchronising quota usage data with actual data in the Neutron DB. Finally, regardless of whether CountableResource or TrackedResource is used, the quota engine always invokes its count() method to retrieve resource usage. Therefore, from the perspective of the Quota engine there is absolutely no difference between CountableResource and TrackedResource. Quota Enforcement ----------------- Before dispatching a request to the plugin, the Neutron 'base' controller [#]_ attempts to make a reservation for requested resource(s). Reservations are made by calling the make_reservation method in neutron.quota.QuotaEngine. The process of making a reservation is fairly straightforward: * Get current resource usages. This is achieved by invoking the count method on every requested resource, and then retrieving the amount of reserved resources. * Fetch current quota limits for requested resources, by invoking the _get_tenant_quotas method. * Fetch expired reservations for selected resources. This amount will be subtracted from resource usage. As in most cases there won't be any expired reservation, this approach actually requires less DB operations than doing a sum of non-expired, reserved resources for each request. * For each resource calculate its headroom, and verify the requested amount of resource is less than the headroom. * If the above is true for all resource, the reservation is saved in the DB, otherwise an OverQuotaLimit exception is raised. The quota engine is able to make a reservation for multiple resources. However, it is worth noting that because of the current structure of the Neutron API layer, there will not be any practical case in which a reservation for multiple resources is made. For this reason performance optimisation avoiding repeating queries for every resource are not part of the current implementation. In order to ensure correct operations, a row-level lock is acquired in the transaction which creates the reservation. The lock is acquired when reading usage data. In case of write-set certification failures, which can occur in active/active clusters such as MySQL galera, the decorator neutron.db.api.retry_db_errors will retry the transaction if a DBDeadLock exception is raised. While non-locking approaches are possible, it has been found out that, since a non-locking algorithms increases the chances of collision, the cost of handling a DBDeadlock is still lower than the cost of retrying the operation when a collision is detected. A study in this direction was conducted for IP allocation operations, but the same principles apply here as well [#]_. Nevertheless, moving away for DB-level locks is something that must happen for quota enforcement in the future. Committing and cancelling a reservation is as simple as deleting the reservation itself. When a reservation is committed, the resources which were committed are now stored in the database, so the reservation itself should be deleted. The Neutron quota engine simply removes the record when cancelling a reservation (ie: the request failed to complete), and also marks quota usage info as dirty when the reservation is committed (ie: the request completed correctly). Reservations are committed or cancelled by respectively calling the commit_reservation and cancel_reservation methods in neutron.quota.QuotaEngine. Reservations are not perennial. Eternal reservation would eventually exhaust projects' quotas because they would never be removed when an API worker crashes whilst in the middle of an operation. Reservation expiration is currently set to 120 seconds, and is not configurable, not yet at least. Expired reservations are not counted when calculating resource usage. While creating a reservation, if any expired reservation is found, all expired reservation for that project and resource will be removed from the database, thus avoiding build-up of expired reservations. Setting up Resource Tracking for a Plugin ------------------------------------------ By default plugins do not leverage resource tracking. Having the plugin explicitly declare which resources should be tracked is a precise design choice aimed at limiting as much as possible the chance of introducing errors in existing plugins. For this reason a plugin must declare which resource it intends to track. This can be achieved using the tracked_resources decorator available in the neutron.quota.resource_registry module. The decorator should ideally be applied to the plugin's __init__ method. The decorator accepts in input a list of keyword arguments. The name of the argument must be a resource name, and the value of the argument must be a DB model class. For example: :: @resource_registry.tracked_resources(network=models_v2.Network, port=models_v2.Port, subnet=models_v2.Subnet, subnetpool=models_v2.SubnetPool) Will ensure network, port, subnet and subnetpool resources are tracked. In theory, it is possible to use this decorator multiple times, and not exclusively to __init__ methods. However, this would eventually lead to code readability and maintainability problems, so developers are strongly encourage to apply this decorator exclusively to the plugin's __init__ method (or any other method which is called by the plugin only once during its initialization). Notes for Implementors of RPC Interfaces and RESTful Controllers ------------------------------------------------------------------------------- Neutron unfortunately does not have a layer which is called before dispatching the operation from the plugin which can be leveraged both from RESTful and RPC over AMQP APIs. In particular the RPC handlers call straight into the plugin, without doing any request authorisation or quota enforcement. Therefore RPC handlers must explicitly indicate if they are going to call the plugin to create or delete any sort of resources. This is achieved in a simple way, by ensuring modified resources are marked as dirty after the RPC handler execution terminates. To this aim developers can use the mark_resources_dirty decorator available in the module neutron.quota.resource_registry. The decorator would scan the whole list of registered resources, and store the dirty status for their usage trackers in the database for those resources for which items have been created or destroyed during the plugin operation. Exceptions and Caveats ----------------------- Please be aware of the following limitations of the quota enforcement engine: * Subnet allocation from subnet pools, in particularly shared pools, is also subject to quota limit checks. However this checks are not enforced by the quota engine, but trough a mechanism implemented in the neutron.ipam.subnetalloc module. This is because the Quota engine is not able to satisfy the requirements for quotas on subnet allocation. * The quota engine also provides a limit_check routine which enforces quota checks without creating reservations. This way of doing quota enforcement is extremely unreliable and superseded by the reservation mechanism. It has not been removed to ensure off-tree plugins and extensions which leverage are not broken. * SqlAlchemy events might not be the most reliable way for detecting changes in resource usage. Since the event mechanism monitors the data model class, it is paramount for a correct quota enforcement, that resources are always created and deleted using object relational mappings. For instance, deleting a resource with a query.delete call, will not trigger the event. SQLAlchemy events should be considered as a temporary measure adopted as Neutron lacks persistent API objects. * As CountableResource instance do not track usage data, when making a reservation no write-intent lock is acquired. Therefore the quota engine with CountableResource is not concurrency-safe. * The mechanism for specifying for which resources enable usage tracking relies on the fact that the plugin is loaded before quota-limited resources are registered. For this reason it is not possible to validate whether a resource actually exists or not when enabling tracking for it. Developers should pay particular attention into ensuring resource names are correctly specified. * The code assumes usage trackers are a trusted source of truth: if they report a usage counter and the dirty bit is not set, that counter is correct. If it's dirty than surely that counter is out of sync. This is not very robust, as there might be issues upon restart when toggling the use_tracked_resources configuration variable, as stale counters might be trusted upon for making reservations. Also, the same situation might occur if a server crashes after the API operation is completed but before the reservation is committed, as the actual resource usage is changed but the corresponding usage tracker is not marked as dirty. References ---------- .. [#] Subnet allocation extension: http://git.openstack.org/cgit/openstack/neutron/tree/neutron/extensions/subnetallocation.py .. [#] DB Quota driver class: http://git.openstack.org/cgit/openstack/neutron/tree/neutron/db/quota_db.py#n33 .. [#] Quota API extension controller: http://git.openstack.org/cgit/openstack/neutron/tree/neutron/extensions/quotasv2.py#n40 .. [#] Neutron resource attribute map: http://git.openstack.org/cgit/openstack/neutron/tree/neutron/api/v2/attributes.py#n639 .. [#] Base controller class: http://git.openstack.org/cgit/openstack/neutron/tree/neutron/api/v2/base.py#n50 .. [#] http://lists.openstack.org/pipermail/openstack-dev/2015-February/057534.html neutron-12.1.1/doc/source/contributor/internals/index.rst0000664000175000017500000000333213553660046023600 0ustar zuulzuul00000000000000.. Copyright 2010-2011 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ================= Neutron Internals ================= .. toctree:: :maxdepth: 1 address_scopes agent_extensions api_extensions api_layer calling_ml2_plugin db_layer db_models dns_order external_dns_integration i18n l2_agent_extensions l2_agents l3_agent_extensions layer3 linuxbridge_agent live_migration ml2_ext_manager network_ip_availability objects_usage openvswitch_agent openvswitch_firewall ovs_vhostuser plugin-api policy provisioning_blocks quality_of_service quota retries rpc_api rpc_callbacks security_group_api segments service_extensions services_and_agents sriov_nic_agent tag upgrade neutron-12.1.1/doc/source/contributor/internals/layer3.rst0000664000175000017500000004521013553660047023672 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Layer 3 Networking in Neutron - via Layer 3 agent & OpenVSwitch =============================================================== This page discusses the usage of Neutron with Layer 3 functionality enabled. Neutron logical network setup ----------------------------- :: vagrant@precise64:~/devstack$ openstack network list +--------------------------------------+---------+----------------------------------------------------------------------------+ | ID | Name | Subnets | +--------------------------------------+---------+----------------------------------------------------------------------------+ | 6ece2847-971b-487a-9c7b-184651ebbc82 | public | 0d9c4261-4046-462f-9d92-64fb89bc3ae6, 9e90b059-da97-45b8-8cb8-f9370217e181 | | 713bae25-8276-4e0a-a453-e59a1d65425a | private | 6fa3bab9-103e-45d5-872c-91f21b52ceda, c5c9f5c2-145d-46d2-a513-cf675530eaed | +--------------------------------------+---------+----------------------------------------------------------------------------+ vagrant@precise64:~/devstack$ openstack subnet list +--------------------------------------+---------------------+--------------------------------------+--------------------+ | ID | Name | Network | Subnet | +--------------------------------------+---------------------+--------------------------------------+--------------------+ | 0d9c4261-4046-462f-9d92-64fb89bc3ae6 | public-subnet | 6ece2847-971b-487a-9c7b-184651ebbc82 | 172.24.4.0/24 | | 6fa3bab9-103e-45d5-872c-91f21b52ceda | ipv6-private-subnet | 713bae25-8276-4e0a-a453-e59a1d65425a | 2001:db8:8000::/64 | | 9e90b059-da97-45b8-8cb8-f9370217e181 | ipv6-public-subnet | 6ece2847-971b-487a-9c7b-184651ebbc82 | 2001:db8::/64 | | c5c9f5c2-145d-46d2-a513-cf675530eaed | private-subnet | 713bae25-8276-4e0a-a453-e59a1d65425a | 10.0.0.0/24 | +--------------------------------------+---------------------+--------------------------------------+--------------------+ vagrant@precise64:~/devstack$ openstack port list +--------------------------------------+------+-------------------+----------------------------------------------------------------------------------------------------+--------+ | ID | Name | MAC Address | Fixed IP Addresses | Status | +--------------------------------------+------+-------------------+----------------------------------------------------------------------------------------------------+--------+ | 420abb60-2a5a-4e80-90a3-3ff47742dc53 | | fa:16:3e:2d:5c:4e | ip_address='172.24.4.7', subnet_id='0d9c4261-4046-462f-9d92-64fb89bc3ae6' | ACTIVE | | | | | ip_address='2001:db8::1', subnet_id='9e90b059-da97-45b8-8cb8-f9370217e181' | | | b42d789d-c9ed-48a1-8822-839c4599301e | | fa:16:3e:0a:ff:24 | ip_address='10.0.0.1', subnet_id='c5c9f5c2-145d-46d2-a513-cf675530eaed' | ACTIVE | | cfff6574-091c-4d16-a54b-5b7f3eab89ce | | fa:16:3e:a0:a3:9e | ip_address='10.0.0.2', subnet_id='c5c9f5c2-145d-46d2-a513-cf675530eaed' | ACTIVE | | | | | ip_address='2001:db8:8000:0:f816:3eff:fea0:a39e', subnet_id='6fa3bab9-103e-45d5-872c-91f21b52ceda' | | | e3b7fede-277e-4c72-b66c-418a582b61ca | | fa:16:3e:13:dd:42 | ip_address='2001:db8:8000::1', subnet_id='6fa3bab9-103e-45d5-872c-91f21b52ceda' | ACTIVE | +--------------------------------------+------+-------------------+----------------------------------------------------------------------------------------------------+--------+ vagrant@precise64:~/devstack$ openstack subnet show c5c9f5c2-145d-46d2-a513-cf675530eaed +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | allocation_pools | 10.0.0.2-10.0.0.254 | | cidr | 10.0.0.0/24 | | created_at | 2016-11-08T21:55:22Z | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 10.0.0.1 | | host_routes | | | id | c5c9f5c2-145d-46d2-a513-cf675530eaed | | ip_version | 4 | | ipv6_address_mode | None | | ipv6_ra_mode | None | | name | private-subnet | | network_id | 713bae25-8276-4e0a-a453-e59a1d65425a | | project_id | 35e3820f7490493ca9e3a5e685393298 | | revision_number | 2 | | service_types | | | subnetpool_id | b1f81d96-d51d-41f3-96b5-a0da16ad7f0d | | updated_at | 2016-11-08T21:55:22Z | +-------------------+--------------------------------------+ Neutron logical router setup ---------------------------- :: vagrant@precise64:~/devstack$ openstack router list +--------------------------------------+---------+--------+-------+-------------+-------+----------------------------------+ | ID | Name | Status | State | Distributed | HA | Project | +--------------------------------------+---------+--------+-------+-------------+-------+----------------------------------+ | 82fa9a47-246e-4da8-a864-53ea8daaed42 | router1 | ACTIVE | UP | False | False | 35e3820f7490493ca9e3a5e685393298 | +--------------------------------------+---------+--------+-------+-------------+-------+----------------------------------+ vagrant@precise64:~/devstack$ openstack router show router1 +-------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ | Field | Value | +-------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | nova | | created_at | 2016-11-08T21:55:30Z | | description | | | distributed | False | | external_gateway_info | {"network_id": "6ece2847-971b-487a-9c7b-184651ebbc82", "enable_snat": true, "external_fixed_ips": [{"subnet_id": "0d9c4261-4046-462f- | | | 9d92-64fb89bc3ae6", "ip_address": "172.24.4.7"}, {"subnet_id": "9e90b059-da97-45b8-8cb8-f9370217e181", "ip_address": "2001:db8::1"}]} | | flavor_id | None | | ha | False | | id | 82fa9a47-246e-4da8-a864-53ea8daaed42 | | name | router1 | | project_id | 35e3820f7490493ca9e3a5e685393298 | | revision_number | 8 | | routes | | | status | ACTIVE | | updated_at | 2016-11-08T21:55:51Z | +-------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ vagrant@precise64:~/devstack$ openstack port list --router router1 +--------------------------------------+------+-------------------+---------------------------------------------------------------------------------+--------+ | ID | Name | MAC Address | Fixed IP Addresses | Status | +--------------------------------------+------+-------------------+---------------------------------------------------------------------------------+--------+ | 420abb60-2a5a-4e80-90a3-3ff47742dc53 | | fa:16:3e:2d:5c:4e | ip_address='172.24.4.7', subnet_id='0d9c4261-4046-462f-9d92-64fb89bc3ae6' | ACTIVE | | | | | ip_address='2001:db8::1', subnet_id='9e90b059-da97-45b8-8cb8-f9370217e181' | | | b42d789d-c9ed-48a1-8822-839c4599301e | | fa:16:3e:0a:ff:24 | ip_address='10.0.0.1', subnet_id='c5c9f5c2-145d-46d2-a513-cf675530eaed' | ACTIVE | | e3b7fede-277e-4c72-b66c-418a582b61ca | | fa:16:3e:13:dd:42 | ip_address='2001:db8:8000::1', subnet_id='6fa3bab9-103e-45d5-872c-91f21b52ceda' | ACTIVE | +--------------------------------------+------+-------------------+---------------------------------------------------------------------------------+--------+ See the `Networking Guide <../../admin/deploy-ovs-selfservice.html#create-initial-networks>`_ for more detail on the creation of networks, subnets, and routers. Neutron Routers are realized in OpenVSwitch ------------------------------------------- .. image:: images/under-the-hood-scenario-1-ovs-network.png "router1" in the Neutron logical network is realized through a port ("qr-0ba8700e-da") in OpenVSwitch - attached to "br-int":: vagrant@precise64:~/devstack$ sudo ovs-vsctl show b9b27fc3-5057-47e7-ba64-0b6afe70a398 Bridge br-int Port "qr-0ba8700e-da" tag: 1 Interface "qr-0ba8700e-da" type: internal Port br-int Interface br-int type: internal Port int-br-ex Interface int-br-ex Port "tapbb60d1bb-0c" tag: 1 Interface "tapbb60d1bb-0c" type: internal Port "qvob2044570-ad" tag: 1 Interface "qvob2044570-ad" Port "int-br-eth1" Interface "int-br-eth1" Bridge "br-eth1" Port "phy-br-eth1" Interface "phy-br-eth1" Port "br-eth1" Interface "br-eth1" type: internal Bridge br-ex Port phy-br-ex Interface phy-br-ex Port "qg-0143bce1-08" Interface "qg-0143bce1-08" type: internal Port br-ex Interface br-ex type: internal ovs_version: "1.4.0+build0" vagrant@precise64:~/devstack$ brctl show bridge name bridge id STP enabled interfaces br-eth1 0000.e2e7fc5ccb4d no br-ex 0000.82ee46beaf4d no phy-br-ex qg-39efb3f9-f0 qg-77e0666b-cd br-int 0000.5e46cb509849 no int-br-ex qr-54c9cd83-43 qvo199abeb2-63 qvo1abbbb60-b8 tap74b45335-cc qbr199abeb2-63 8000.ba06e5f8675c no qvb199abeb2-63 tap199abeb2-63 qbr1abbbb60-b8 8000.46a87ed4fb66 no qvb1abbbb60-b8 tap1abbbb60-b8 virbr0 8000.000000000000 yes Finding the router in ip/ipconfig --------------------------------- The neutron-l3-agent uses the Linux IP stack and iptables to perform L3 forwarding and NAT. In order to support multiple routers with potentially overlapping IP addresses, neutron-l3-agent defaults to using Linux network namespaces to provide isolated forwarding contexts. As a result, the IP addresses of routers will not be visible simply by running "ip addr list" or "ifconfig" on the node. Similarly, you will not be able to directly ping fixed IPs. To do either of these things, you must run the command within a particular router's network namespace. The namespace will have the name "qrouter-. .. image:: images/under-the-hood-scenario-1-ovs-netns.png For example:: vagrant@precise64:~$ openstack router list +--------------------------------------+---------+-------------------------------------------------------------------------+ | ID | Name | Status | State | Distributed | HA | Project | +--------------------------------------+---------+-------------------------------------------------------------------------+ | ad948c6e-afb6-422a-9a7b-0fc44cbb3910 | router1 | Active | UP | True | False | 35e3820f7490493ca9e3a5e685393298 | +--------------------------------------+---------+-------------------------------------------------------------------------+ vagrant@precise64:~/devstack$ sudo ip netns exec qrouter-ad948c6e-afb6-422a-9a7b-0fc44cbb3910 ip addr list 18: lo: mtu 16436 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo inet6 ::1/128 scope host valid_lft forever preferred_lft forever 19: qr-54c9cd83-43: mtu 1500 qdisc noqueue state UNKNOWN link/ether fa:16:3e:dd:c1:8f brd ff:ff:ff:ff:ff:ff inet 10.0.0.1/24 brd 10.0.0.255 scope global qr-54c9cd83-43 inet6 fe80::f816:3eff:fedd:c18f/64 scope link valid_lft forever preferred_lft forever 20: qg-77e0666b-cd: mtu 1500 qdisc noqueue state UNKNOWN link/ether fa:16:3e:1f:d3:ec brd ff:ff:ff:ff:ff:ff inet 192.168.27.130/28 brd 192.168.27.143 scope global qg-77e0666b-cd inet6 fe80::f816:3eff:fe1f:d3ec/64 scope link valid_lft forever preferred_lft forever Provider Networking ------------------- Neutron can also be configured to create `provider networks <../../admin/archives/adv-features.html#provider-networks>`_. .. include:: l3_agent_extensions.rst Further Reading --------------- * `Packet Pushers - Neutron Network Implementation on Linux `_ * `OpenStack Networking Guide <../../admin/index.html>`_ * `Neutron - Layer 3 API extension `_ * `Darragh O'Reilly - The Quantum L3 router and floating IPs `_ neutron-12.1.1/doc/source/contributor/internals/calling_ml2_plugin.rst0000664000175000017500000000403213553660046026230 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Calling the ML2 Plugin ====================== When writing code for an extension, service plugin, or any other part of Neutron you must not call core plugin methods that mutate state while you have a transaction open on the session that you pass into the core plugin method. The create and update methods for ports, networks, and subnets in ML2 all have a precommit phase and postcommit phase. During the postcommit phase, the data is expected to be fully persisted to the database and ML2 drivers will use this time to relay information to a backend outside of Neutron. Calling the ML2 plugin within a transaction would violate this semantic because the data would not be persisted to the DB; and, were a failure to occur that caused the whole transaction to be rolled back, the backend would become inconsistent with the state in Neutron's DB. To prevent this, these methods are protected with a decorator that will raise a RuntimeError if they are called with context that has a session in an active transaction. The decorator can be found at neutron.common.utils.transaction_guard and may be used in other places in Neutron to protect functions that are expected to be called outside of a transaction. neutron-12.1.1/doc/source/contributor/internals/upgrade.rst0000664000175000017500000002623313553660047024126 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) .. note:: Much of this document discusses upgrade considerations for the Neutron reference implementation using Neutron's agents. It's expected that each Neutron plugin provides its own documentation that discusses upgrade considerations specific to that choice of backend. For example, OVN does not use Neutron agents, but does have a local controller that runs on each compute node. OVN supports rolling upgrades, but information about how that works should be covered in the documentation for networking-ovn, the OVN Neutron plugin. Upgrade strategy ================ There are two general upgrade scenarios supported by Neutron: #. All services are shut down, code upgraded, then all services are started again. #. Services are upgraded gradually, based on operator service windows. The latter is the preferred way to upgrade an OpenStack cloud, since it allows for more granularity and less service downtime. This scenario is usually called 'rolling upgrade'. Rolling upgrade --------------- Rolling upgrades imply that during some interval of time there will be services of different code versions running and interacting in the same cloud. It puts multiple constraints onto the software. #. older services should be able to talk with newer services. #. older services should not require the database to have older schema (otherwise newer services that require the newer schema would not work). `More info on rolling upgrades in OpenStack `_. Those requirements are achieved in Neutron by: #. If the Neutron backend makes use of Neutron agents, the Neutron server have backwards compatibility code to deal with older messaging payloads. #. isolating a single service that accesses database (neutron-server). To simplify the matter, it's always assumed that the order of service upgrades is as following: #. first, all neutron-servers are upgraded. #. then, if applicable, neutron agents are upgraded. This approach allows us to avoid backwards compatibility code on agent side and is in line with other OpenStack projects that support rolling upgrades (specifically, nova). Server upgrade ~~~~~~~~~~~~~~ Neutron-server is the very first component that should be upgraded to the new code. It's also the only component that relies on new database schema to be present, other components communicate with the cloud through AMQP and hence do not depend on particular database state. Database upgrades are implemented with alembic migration chains. Database upgrade is split into two parts: #. ``neutron-db-manage upgrade --expand`` #. ``neutron-db-manage upgrade --contract`` Each part represents a separate alembic branch. The former step can be executed while old neutron-server code is running. The latter step requires *all* neutron-server instances to be shut down. Once it's complete, neutron-servers can be started again. .. note:: Full shutdown of neutron-server instances can be skipped depending on whether there are pending contract scripts not applied to the database:: $ neutron-db-manage has_offline_migrations Command will return a message if there are pending contract scripts. :ref:`More info on alembic scripts `. Agents upgrade ~~~~~~~~~~~~~~ .. note:: This section does not apply when the cloud does not use AMQP agents to provide networking services to instances. In that case, other backend specific upgrade instructions may also apply. Once neutron-server services are restarted with the new database schema and the new code, it's time to upgrade Neutron agents. Note that in the meantime, neutron-server should be able to serve AMQP messages sent by older versions of agents which are part of the cloud. The recommended order of agent upgrade (per node) is: #. first, L2 agents (openvswitch, linuxbridge, sr-iov). #. then, all other agents (L3, DHCP, Metadata, ...). The rationale of the agent upgrade order is that L2 agent is usually responsible for wiring ports for other agents to use, so it's better to allow it to do its job first and then proceed with other agents that will use the already configured ports for their needs. Each network/compute node can have its own upgrade schedule that is independent of other nodes. AMQP considerations +++++++++++++++++++ Since it's always assumed that neutron-server component is upgraded before agents, only the former should handle both old and new RPC versions. The implication of that is that no code that handles UnsupportedVersion oslo.messaging exceptions belongs to agent code. Notifications ''''''''''''' For notifications that are issued by neutron-server to listening agents, special consideration is needed to support rolling upgrades. In this case, a newer controller sends newer payload to older agents. Until we have proper RPC version pinning feature to enforce older payload format during upgrade (as it's implemented in other projects like nova), we leave our agents resistant against unknown arguments sent as part of server notifications. This is achieved by consistently capturing those unknown arguments with keyword arguments and ignoring them on agent side; and by not enforcing newer RPC entry point versions on server side. This approach is not ideal, because it makes RPC API less strict. That's why other approaches should be considered for notifications in the future. :ref:`More information about RPC versioning `. Interface signature ''''''''''''''''''' An RPC interface is defined by its name, version, and (named) arguments that it accepts. There are no strict guarantees that arguments will have expected types or meaning, as long as they are serializable. Message content versioning '''''''''''''''''''''''''' To provide better compatibility guarantees for rolling upgrades, RPC interfaces could also define specific format for arguments they accept. In OpenStack world, it's usually implemented using oslo.versionedobjects library, and relying on the library to define serialized form for arguments that are passed through AMQP wire. Note that Neutron has *not* adopted oslo.versionedobjects library for its RPC interfaces yet (except for QoS feature). :ref:`More information about RPC callbacks used for QoS `. Networking backends ~~~~~~~~~~~~~~~~~~~ Backend software upgrade should not result in any data plane disruptions. Meaning, e.g. Open vSwitch L2 agent should not reset flows or rewire ports; Neutron L3 agent should not delete namespaces left by older version of the agent; Neutron DHCP agent should not require immediate DHCP lease renewal; etc. The same considerations apply to setups that do not rely on agents. Meaning, f.e. OpenDaylight or OVN controller should not break data plane connectivity during its upgrade process. Upgrade testing --------------- `Grenade `_ is the OpenStack project that is designed to validate upgrade scenarios. Currently, only offline (non-rolling) upgrade scenario is validated in Neutron gate. The upgrade scenario follows the following steps: #. the 'old' cloud is set up using latest stable release code #. all services are stopped #. code is updated to the patch under review #. new database migration scripts are applied, if needed #. all services are started #. the 'new' cloud is validated with a subset of tempest tests The scenario validates that no configuration option names are changed in one cycle. More generally, it validates that the 'new' cloud is capable of running using the 'old' configuration files. It also validates that database migration scripts can be executed. The scenario does *not* validate AMQP versioning compatibility. Other projects (for example Nova) have so called 'partial' grenade jobs where some services are left running using the old version of code. Such a job would be needed in Neutron gate to validate rolling upgrades for the project. Till that time, it's all up to reviewers to catch compatibility issues in patches on review. Another hole in testing belongs to split migration script branches. It's assumed that an 'old' cloud can successfully run after 'expand' migration scripts from the 'new' cloud are applied to its database; but it's not validated in gate. .. _upgrade_review_guidelines: Review guidelines ----------------- There are several upgrade related gotchas that should be tracked by reviewers. First things first, a general advice to reviewers: make sure new code does not violate requirements set by `global OpenStack deprecation policy `_. Now to specifics: #. Configuration options: * options should not be dropped from the tree without waiting for deprecation period (currently it's one development cycle long) and a deprecation message issued if the deprecated option is used. * option values should not change their meaning between releases. #. Data plane: * agent restart should not result in data plane disruption (no Open vSwitch ports reset; no network namespaces deleted; no device names changed). #. RPC versioning: * no RPC version major number should be bumped before all agents had a chance to upgrade (meaning, at least one release cycle is needed before compatibility code to handle old clients is stripped from the tree). * no compatibility code should be added to agent side of AMQP interfaces. * server code should be able to handle all previous versions of agents, unless the major version of an interface is bumped. * no RPC interface arguments should change their meaning, or names. * new arguments added to RPC interfaces should not be mandatory. It means that server should be able to handle old requests, without the new argument specified. Also, if the argument is not passed, the old behaviour before the addition of the argument should be retained. * minimal client version must not be bumped for server initiated notification changes for at least one cycle. #. Database migrations: * migration code should be split into two branches (contract, expand) as needed. No code that is unsafe to execute while neutron-server is running should be added to expand branch. * if possible, contract migrations should be minimized or avoided to reduce the time when API endpoints must be down during database upgrade. neutron-12.1.1/doc/source/contributor/internals/api_extensions.rst0000664000175000017500000000604513553660047025526 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) API Extensions ============== API extensions is the standard way of introducing new functionality to the Neutron project, it allows plugins to determine if they wish to support the functionality or not. Examples -------- The easiest way to demonstrate how an API extension is written, is by studying an existing API extension and explaining the different layers. .. toctree:: :maxdepth: 1 security_group_api Extensions for Resources with standard attributes ------------------------------------------------- Resources that inherit from the HasStandardAttributes DB class can automatically have the extensions written for standard attributes (e.g. timestamps, revision number, etc) extend their resources by defining the 'api_collections' on their model. These are used by extensions for standard attr resources to generate the extended resources map. Any new addition of a resource to the standard attributes collection must be accompanied with a new extension to ensure that it is discoverable via the API. If it's a completely new resource, the extension describing that resource will suffice. If it's an existing resource that was released in a previous cycle having the standard attributes added for the first time, then a dummy extension needs to be added indicating that the resource now has standard attributes. This ensures that an API caller can always discover if an attribute will be available. For example, if Flavors were migrated to include standard attributes, we need a new 'flavor-standardattr' extension. Then as an API caller, I will know that flavors will have timestamps by checking for 'flavor-standardattr' and 'timestamps'. Current API resources extended by standard attr extensions: - subnets: neutron.db.models_v2.Subnet - trunks: neutron.services.trunk.models.Trunk - routers: neutron.db.l3_db.Router - segments: neutron.db.segments_db.NetworkSegment - security_group_rules: neutron.db.models.securitygroup.SecurityGroupRule - networks: neutron.db.models_v2.Network - policies: neutron.db.qos.models.QosPolicy - subnetpools: neutron.db.models_v2.SubnetPool - ports: neutron.db.models_v2.Port - security_groups: neutron.db.models.securitygroup.SecurityGroup - floatingips: neutron.db.l3_db.FloatingIP neutron-12.1.1/doc/source/contributor/internals/ovs_vhostuser.rst0000664000175000017500000000464713553660046025434 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Neutron Open vSwitch vhost-user support ======================================= Neutron supports using Open vSwitch + DPDK vhost-user interfaces directly in the OVS ML2 driver and agent. The current implementation relies on a multiple configuration values and includes runtime verification of Open vSwitch's capability to provide these interfaces. The OVS agent detects the capability of the underlying Open vSwitch installation and passes that information over RPC via the agent 'configurations' dictionary. The ML2 driver uses this information to select the proper VIF type and binding details. Platform requirements --------------------- * OVS 2.4.0+ * DPDK 2.0+ Configuration ------------- .. code-block:: ini [OVS] datapath_type=netdev vhostuser_socket_dir=/var/run/openvswitch When OVS is running with DPDK support enabled, and the ``datapath_type`` is set to ``netdev``, then the OVS ML2 driver will use the ``vhost-user`` VIF type and pass the necessary binding details to use OVS+DPDK and vhost-user sockets. This includes the ``vhostuser_socket_dir`` setting, which must match the directory passed to ``ovs-vswitchd`` on startup. What about the networking-ovs-dpdk repo? ---------------------------------------- The networking-ovs-dpdk repo will continue to exist and undergo active development. This feature just removes the necessity for a separate ML2 driver and OVS agent in the networking-ovs-dpdk repo. The networking-ovs-dpdk project also provides a devstack plugin which also allows automated CI, a Puppet module, and an OpenFlow-based security group implementation. neutron-12.1.1/doc/source/contributor/internals/services_and_agents.rst0000664000175000017500000001135113553660047026500 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Services and agents =================== A usual Neutron setup consists of multiple services and agents running on one or multiple nodes (though some exotic setups potentially may not need any agents). Each of those services provides some of the networking or API services. Among those of special interest: #. neutron-server that provides API endpoints and serves as a single point of access to the database. It usually runs on nodes called Controllers. #. Layer2 agent that can utilize Open vSwitch, Linuxbridge or other vendor specific technology to provide network segmentation and isolation for project networks. The L2 agent should run on every node where it is deemed responsible for wiring and securing virtual interfaces (usually both Compute and Network nodes). #. Layer3 agent that runs on Network node and provides East-West and North-South routing plus some advanced services such as FWaaS or VPNaaS. For the purpose of this document, we call all services, servers and agents that run on any node as just "services". Entry points ------------ Entry points for services are defined in setup.cfg under "console_scripts" section. Those entry points should generally point to main() functions located under neutron/cmd/... path. Note: some existing vendor/plugin agents still maintain their entry points in other locations. Developers responsible for those agents are welcome to apply the guideline above. Interacting with Eventlet ------------------------- Neutron extensively utilizes the eventlet library to provide asynchronous concurrency model to its services. To utilize it correctly, the following should be kept in mind. If a service utilizes the eventlet library, then it should not call eventlet.monkey_patch() directly but instead maintain its entry point main() function under neutron/cmd/eventlet/... If that is the case, the standard Python library will be automatically patched for the service on entry point import (monkey patching is done inside `python package file `_). Note: an entry point 'main()' function may just be an indirection to a real callable located elsewhere, as is done for reference services such as DHCP, L3 and the neutron-server. For more info on the rationale behind the code tree setup, see `the corresponding cross-project spec `_. Connecting to the Database -------------------------- Only the neutron-server connects to the neutron database. Agents may never connect directly to the database, as this would break the ability to do rolling upgrades. Configuration Options --------------------- In addition to database access, configuration options are segregated between neutron-server and agents. Both services and agents may load the main ```neutron.conf``` since this file should contain the oslo.messaging configuration for internal Neutron RPCs and may contain host specific configuration such as file paths. In addition ```neutron.conf``` contains the database, Keystone, and Nova credentials and endpoints strictly for neutron-server to use. In addition neutron-server may load a plugin specific configuration file, yet the agents should not. As the plugin configuration is primarily site wide options and the plugin provides the persistence layer for Neutron, agents should be instructed to act upon these values via RPC. Each individual agent may have its own configuration file. This file should be loaded after the main ```neutron.conf``` file, so the agent configuration takes precedence. The agent specific configuration may contain configurations which vary between hosts in a Neutron deployment such as the external_network_bridge for a L3 agent. If any agent requires access to additional external services beyond the Neutron RPC, those endpoints should be defined in the agent specific configuration file (e.g. nova metadata for metadata agent). neutron-12.1.1/doc/source/contributor/internals/rpc_api.rst0000664000175000017500000001447113553660046024114 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Neutron RPC API Layer ===================== Neutron uses the oslo.messaging library to provide an internal communication channel between Neutron services. This communication is typically done via AMQP, but those details are mostly hidden by the use of oslo.messaging and it could be some other protocol in the future. RPC APIs are defined in Neutron in two parts: client side and server side. Client Side ----------- Here is an example of an rpc client definition: :: import oslo_messaging from neutron.common import rpc as n_rpc class ClientAPI(object): """Client side RPC interface definition. API version history: 1.0 - Initial version 1.1 - Added my_remote_method_2 """ def __init__(self, topic): target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def my_remote_method(self, context, arg1, arg2): cctxt = self.client.prepare() return cctxt.call(context, 'my_remote_method', arg1=arg1, arg2=arg2) def my_remote_method_2(self, context, arg1): cctxt = self.client.prepare(version='1.1') return cctxt.call(context, 'my_remote_method_2', arg1=arg1) This class defines the client side interface for an rpc API. The interface has 2 methods. The first method existed in version 1.0 of the interface. The second method was added in version 1.1. When the newer method is called, it specifies that the remote side must implement at least version 1.1 to handle this request. Server Side ----------- The server side of an rpc interface looks like this: :: import oslo_messaging class ServerAPI(object): target = oslo_messaging.Target(version='1.1') def my_remote_method(self, context, arg1, arg2): return 'foo' def my_remote_method_2(self, context, arg1): return 'bar' This class implements the server side of the interface. The oslo_messaging.Target() defined says that this class currently implements version 1.1 of the interface. .. _rpc_versioning: Versioning ---------- Note that changes to rpc interfaces must always be done in a backwards compatible way. The server side should always be able to handle older clients (within the same major version series, such as 1.X). It is possible to bump the major version number and drop some code only needed for backwards compatibility. For more information about how to do that, see https://wiki.openstack.org/wiki/RpcMajorVersionUpdates. Example Change ~~~~~~~~~~~~~~ As an example minor API change, let's assume we want to add a new parameter to my_remote_method_2. First, we add the argument on the server side. To be backwards compatible, the new argument must have a default value set so that the interface will still work even if the argument is not supplied. Also, the interface's minor version number must be incremented. So, the new server side code would look like this: :: import oslo_messaging class ServerAPI(object): target = oslo_messaging.Target(version='1.2') def my_remote_method(self, context, arg1, arg2): return 'foo' def my_remote_method_2(self, context, arg1, arg2=None): if not arg2: # Deal with the fact that arg2 was not specified if needed. return 'bar' We can now update the client side to pass the new argument. The client must also specify that version '1.2' is required for this method call to be successful. The updated client side would look like this: :: import oslo_messaging from neutron.common import rpc as n_rpc class ClientAPI(object): """Client side RPC interface definition. API version history: 1.0 - Initial version 1.1 - Added my_remote_method_2 1.2 - Added arg2 to my_remote_method_2 """ def __init__(self, topic): target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def my_remote_method(self, context, arg1, arg2): cctxt = self.client.prepare() return cctxt.call(context, 'my_remote_method', arg1=arg1, arg2=arg2) def my_remote_method_2(self, context, arg1, arg2): cctxt = self.client.prepare(version='1.2') return cctxt.call(context, 'my_remote_method_2', arg1=arg1, arg2=arg2) Neutron RPC APIs ---------------- As discussed before, RPC APIs are defined in two parts: a client side and a server side. Several of these pairs exist in the Neutron code base. The code base is being updated with documentation on every rpc interface implementation that indicates where the corresponding server or client code is located. Example: DHCP ~~~~~~~~~~~~~ The DHCP agent includes a client API, neutron.agent.dhcp.agent.DhcpPluginAPI. The DHCP agent uses this class to call remote methods back in the Neutron server. The server side is defined in neutron.api.rpc.handlers.dhcp_rpc.DhcpRpcCallback. It is up to the Neutron plugin in use to decide whether the DhcpRpcCallback interface should be exposed. Similarly, there is an RPC interface defined that allows the Neutron plugin to remotely invoke methods in the DHCP agent. The client side is defined in neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.DhcpAgentNotifyAPI. The server side of this interface that runs in the DHCP agent is neutron.agent.dhcp.agent.DhcpAgent. More Info --------- For more information, see the oslo.messaging documentation: https://docs.openstack.org/oslo.messaging/latest/. neutron-12.1.1/doc/source/contributor/internals/openvswitch_agent.rst0000664000175000017500000006751113553660047026232 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Open vSwitch L2 Agent ===================== This Agent uses the `Open vSwitch`_ virtual switch to create L2 connectivity for instances, along with bridges created in conjunction with OpenStack Nova for filtering. ovs-neutron-agent can be configured to use different networking technologies to create project isolation. These technologies are implemented as ML2 type drivers which are used in conjunction with the Open vSwitch mechanism driver. VLAN Tags --------- .. image:: images/under-the-hood-scenario-1-ovs-compute.png .. _Open vSwitch: http://openvswitch.org GRE Tunnels ----------- GRE Tunneling is documented in depth in the `Networking in too much detail `_ by RedHat. VXLAN Tunnels ------------- VXLAN is an overlay technology which encapsulates MAC frames at layer 2 into a UDP header. More information can be found in `The VXLAN wiki page. `_ Geneve Tunnels -------------- Geneve uses UDP as its transport protocol and is dynamic in size using extensible option headers. It is important to note that currently it is only supported in newer kernels. (kernel >= 3.18, OVS version >=2.4) More information can be found in the `Geneve RFC document. `_ Bridge Management ----------------- In order to make the agent capable of handling more than one tunneling technology, to decouple the requirements of segmentation technology from project isolation, and to preserve backward compatibility for OVS agents working without tunneling, the agent relies on a tunneling bridge, or br-tun, and the well known integration bridge, or br-int. All VM VIFs are plugged into the integration bridge. VM VIFs on a given virtual network share a common "local" VLAN (i.e. not propagated externally). The VLAN id of this local VLAN is mapped to the physical networking details realizing that virtual network. For virtual networks realized as VXLAN/GRE tunnels, a Logical Switch (LS) identifier is used to differentiate project traffic on inter-HV tunnels. A mesh of tunnels is created to other Hypervisors in the cloud. These tunnels originate and terminate on the tunneling bridge of each hypervisor, leaving br-int unaffected. Port patching is done to connect local VLANs on the integration bridge to inter-hypervisor tunnels on the tunnel bridge. For each virtual network realized as a VLAN or flat network, a veth or a pair of patch ports is used to connect the local VLAN on the integration bridge with the physical network bridge, with flow rules adding, modifying, or stripping VLAN tags as necessary, thus preserving backward compatibility with the way the OVS agent used to work prior to the tunneling capability (for more details, please look at https://review.openstack.org/#/c/4367). Bear in mind, that this design decision may be overhauled in the future to support existing VLAN-tagged traffic (coming from NFV VMs for instance) and/or to deal with potential QinQ support natively available in the Open vSwitch. Tackling the Network Trunking use case -------------------------------------- Rationale ~~~~~~~~~ At the time the first design for the OVS agent came up, trunking in OpenStack was merely a pipe dream. Since then, lots has happened in the OpenStack platform, and many many deployments have gone into production since early 2012. In order to address the `vlan-aware-vms `_ use case on top of Open vSwitch, the following aspects must be taken into account: * Design complexity: starting afresh is always an option, but a complete rearchitecture is only desirable under some circumstances. After all, customers want solutions...yesterday. It is noteworthy that the OVS agent design is already relatively complex, as it accommodates a number of deployment options, especially in relation to security rules and/or acceleration. * Upgrade complexity: being able to retrofit the existing design means that an existing deployment does not need to go through a forklift upgrade in order to expose new functionality; alternatively, the desire of avoiding a migration requires a more complex solution that is able to support multiple modes of operations; * Design reusability: ideally, a proposed design can easily apply to the various technology backends that the Neutron L2 agent supports: Open vSwitch and Linux Bridge. * Performance penalty: no solution is appealing enough if it is unable to satisfy the stringent requirement of high packet throughput, at least in the long term. * Feature compatibility: VLAN `transparency `_ is for better or for worse intertwined with vlan awareness. The former is about making the platform not interfere with the tag associated to the packets sent by the VM, and let the underlay figure out where the packet needs to be sent out; the latter is about making the platform use the vlan tag associated to packet to determine where the packet needs to go. Ideally, a design choice to satisfy the awareness use case will not have a negative impact for solving the transparency use case. Having said that, the two features are still meant to be mutually exclusive in their application, and plugging subports into networks whose vlan-transparency flag is set to True might have unexpected results. In fact, it would be impossible from the platform's point of view discerning which tagged packets are meant to be treated 'transparently' and which ones are meant to be used for demultiplexing (in order to reach the right destination). The outcome might only be predictable if two layers of vlan tags are stacked up together, making guest support even more crucial for the combined use case. It is clear by now that an acceptable solution must be assessed with these issues in mind. The potential solutions worth enumerating are: * VLAN interfaces: in layman's terms, these interfaces allow to demux the traffic before it hits the integration bridge where the traffic will get isolated and sent off to the right destination. This solution is `proven `_ to work for both iptables-based and native ovs security rules (credit to Rawlin Peters). This solution has the following design implications: * Design complexity: this requires relative small changes to the existing OVS design, and it can work with both iptables and native ovs security rules. * Upgrade complexity: in order to employ this solution no major upgrade is necessary and thus no potential dataplane disruption is involved. * Design reusability: VLAN interfaces can easily be employed for both Open vSwitch and Linux Bridge. * Performance penalty: using VLAN interfaces means that the kernel must be involved. For Open vSwitch, being able to use a fast path like DPDK would be an unresolved issue (`Kernel NIC interfaces `_ are not on the roadmap for distros and OVS, and most likely will never be). Even in the absence of an extra bridge, i.e. when using native ovs firewall, and with the advent of userspace connection tracking that would allow the `stateful firewall driver `_ to work with DPDK, the performance gap between a pure userspace DPDK capable solution and a kernel based solution will be substantial, at least under certain traffic conditions. * Feature compatibility: in order to keep the design simple once VLAN interfaces are adopted, and yet enable VLAN transparency, Open vSwitch needs to support QinQ, which is currently lacking as of 2.5 and with no ongoing plan for integration. * Going full openflow: in layman's terms, this means programming the dataplane using OpenFlow in order to provide tenant isolation, and packet processing. This solution has the following design implications: * Design complexity: this requires a big rearchitecture of the current Neutron L2 agent solution. * Upgrade complexity: existing deployments will be unable to work correctly unless one of the actions take place: a) the agent can handle both the 'old' and 'new' way of wiring the data path; b) a dataplane migration is forced during a release upgrade and thus it may cause (potentially unrecoverable) dataplane disruption. * Design reusability: a solution for Linux Bridge will still be required to avoid widening the gap between Open vSwitch (e.g. OVS has DVR but LB does not). * Performance penalty: using Open Flow will allow to leverage the user space and fast processing given by DPDK, but at a considerable engineering cost nonetheless. Security rules will have to be provided by a `learn based firewall `_ to fully exploit the capabilities of DPDK, at least until `user space `_ connection tracking becomes available in OVS. * Feature compatibility: with the adoption of Open Flow, tenant isolation will no longer be provided by means of local vlan provisioning, thus making the requirement of QinQ support no longer strictly necessary for Open vSwitch. * Per trunk port OVS bridge: in layman's terms, this is similar to the first option, in that an extra layer of mux/demux is introduced between the VM and the integration bridge (br-int) but instead of using vlan interfaces, a combination of a new per port OVS bridge and patch ports to wire this new bridge with br-int will be used. This solution has the following design implications: * Design complexity: the complexity of this solution can be considered in between the above mentioned options in that some work is already available since `Mitaka `_ and the data path wiring logic can be partially reused. * Upgrade complexity: if two separate code paths are assumed to be maintained in the OVS agent to handle regular ports and ports participating a trunk with no ability to convert from one to the other (and vice versa), no migration is required. This is done at a cost of some loss of flexibility and maintenance complexity. * Design reusability: a solution to support vlan trunking for the Linux Bridge mech driver will still be required to avoid widening the gap with Open vSwitch (e.g. OVS has DVR but LB does not). * Performance penalty: from a performance standpoint, the adoption of a trunk bridge relieves the agent from employing kernel interfaces, thus unlocking the full potential of fast packet processing. That said, this is only doable in combination with a native ovs firewall. At the time of writing the only DPDK enabled firewall driver is the learn based one available in the `networking-ovs-dpdk repo `_; * Feature compatibility: the existing local provisioning logic will not be affected by the introduction of a trunk bridge, therefore use cases where VMs are connected to a vlan transparent network via a regular port will still require QinQ support from OVS. To summarize: * VLAN interfaces (A) are compelling because will lead to a relatively contained engineering cost at the expense of performance. The Open vSwitch community will need to be involved in order to deliver vlan transparency. Irrespective of whether this strategy is chosen for Open vSwitch or not, this is still the only viable approach for Linux Bridge and thus pursued to address Linux Bridge support for VLAN trunking. To some extent, this option can also be considered a fallback strategy for OVS deployments that are unable to adopt DPDK. * Open Flow (B) is compelling because it will allow Neutron to unlock the full potential of Open vSwitch, at the expense of development and operations effort. The development is confined within the boundaries of the Neutron community in order to address vlan awareness and transparency (as two distinct use cases, ie. to be adopted separately). Stateful firewall (based on ovs conntrack) limits the adoption for DPDK at the time of writing, but a learn-based firewall can be a suitable alternative. Obviously this solution is not compliant with iptables firewall. * Trunk Bridges (C) tries to bring the best of option A and B together as far as OVS development and performance are concerned, but it comes at the expense of maintenance complexity and loss of flexibility. A Linux Bridge solution would still be required and, QinQ support will still be needed to address vlan transparency. All things considered, as far as OVS is concerned, option (C) is the most promising in the medium term. Management of trunks and ports within trunks will have to be managed differently and, to start with, it is sensible to restrict the ability to update ports (i.e. convert) once they are bound to a particular bridge (integration vs trunk). Security rules via iptables rules is obviously not supported, and never will be. Option (A) for OVS could be pursued in conjunction with Linux Bridge support, if the effort is seen particularly low hanging fruit. However, a working solution based on this option positions the OVS agent as a sub-optminal platform for performance sensitive applications in comparison to other accelerated or SDN-controller based solutions. Since further data plane performance improvement is hindered by the extra use of kernel resources, this option is not at all appealing in the long term. Embracing option (B) in the long run may be complicated by the adoption of option (C). The development and maintenance complexity involved in Option (C) and (B) respectively poses the existential question as to whether investing in the agent-based architecture is an effective strategy, especially if the end result would look a lot like other maturing alternatives. Implementation VLAN Interfaces (Option A) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This implementation doesn't require any modification of the vif-drivers since Nova will plug the vif of the VM the same way as it does for traditional ports. Trunk port creation +++++++++++++++++++ A VM is spawned passing to Nova the port-id of a parent port associated with a trunk. Nova/libvirt will create the tap interface and will plug it into br-int or into the firewall bridge if using iptables firewall. In the external-ids of the port Nova will store the port ID of the parent port. The OVS agent detects that a new vif has been plugged. It gets the details of the new port and wires it. The agent configures it in the same way as a traditional port: packets coming out from the VM will be tagged using the internal VLAN ID associated to the network, packets going to the VM will be stripped of the VLAN ID. After wiring it successfully the OVS agent will send a message notifying Neutron server that the parent port is up. Neutron will send back to Nova an event to signal that the wiring was successful. If the parent port is associated with one or more subports the agent will process them as described in the next paragraph. Subport creation ++++++++++++++++ If a subport is added to a parent port but no VM was booted using that parent port yet, no L2 agent will process it (because at that point the parent port is not bound to any host). When a subport is created for a parent port and a VM that uses that parent port is already running, the OVS agent will create a VLAN interface on the VM tap using the VLAN ID specified in the subport segmentation id. There's a small possibility that a race might occur: the firewall bridge might be created and plugged while the vif is not there yet. The OVS agent needs to check if the vif exists before trying to create a subinterface. Let's see how the models differ when using the iptables firewall or the ovs native firewall. Iptables Firewall ''''''''''''''''' :: +----------------------------+ | VM | | eth0 eth0.100 | +-----+-----------------+----+ | | +---+---+ +-----+-----+ | tap1 |-------| tap1.100 | +---+---+ +-----+-----+ | | | | +---+---+ +---+---+ | qbr1 | | qbr2 | +---+---+ +---+---+ | | | | +-----+-----------------+----+ | port 1 port 2 | | (tag 3) (tag 5) | | br-int | +----------------------------+ Let's assume the subport is on network2 and uses segmentation ID 100. In the case of hybrid plugging the OVS agent will have to create the firewall bridge (qbr2), create tap1.100 and plug it into qbr2. It will connect qbr2 to br-int and set the subport ID in the external-ids of port 2. *Inbound traffic from the VM point of view* The untagged traffic will flow from port 1 to eth0 through qbr1. For the traffic coming out of port 2, the internal VLAN ID of network2 will be stripped. The packet will then go untagged through qbr2 where iptables rules will filter the traffic. The tag 100 will be pushed by tap1.100 and the packet will finally get to eth0.100. *Outbound traffic from the VM point of view* The untagged traffic will flow from eth0 to port1 going through qbr1 where firewall rules will be applied. Traffic tagged with VLAN 100 will leave eth0.100, go through tap1.100 where the VLAN 100 is stripped. It will reach qbr2 where iptables rules will be applied and go to port 2. The internal VLAN of network2 will be pushed by br-int when the packet enters port2 because it's a tagged port. OVS Firewall case ''''''''''''''''' :: +----------------------------+ | VM | | eth0 eth0.100 | +-----+-----------------+----+ | | +---+---+ +-----+-----+ | tap1 |-------| tap1.100 | +---+---+ +-----+-----+ | | | | | | +-----+-----------------+----+ | port 1 port 2 | | (tag 3) (tag 5) | | br-int | +----------------------------+ When a subport is created the OVS agent will create the VLAN interface tap1.100 and plug it into br-int. Let's assume the subport is on network2. *Inbound traffic from the VM point of view* The traffic will flow untagged from port 1 to eth0. The traffic going out from port 2 will be stripped of the VLAN ID assigned to network2. It will be filtered by the rules installed by the firewall and reach tap1.100. tap1.100 will tag the traffic using VLAN 100. It will then reach the VM's eth0.100. *Outbound traffic from the VM point of view* The untagged traffic will flow and reach port 1 where it will be tagged using the VLAN ID associated to the network. Traffic tagged with VLAN 100 will leave eth0.100 reach tap1.100 where VLAN 100 will be stripped. It will then reach port2. It will be filtered by the rules installed by the firewall on port 2. Then the packets will be tagged using the internal VLAN associated to network2 by br-int since port 2 is a tagged port. Parent port deletion ++++++++++++++++++++ Deleting a port that is an active parent in a trunk is forbidden. If the parent port has no trunk associated (it's a "normal" port), it can be deleted. The OVS agent doesn't need to perform any action, the deletion will result in a removal of the port data from the DB. Trunk deletion ++++++++++++++ When Nova deletes a VM, it deletes the VM's corresponding Neutron ports only if they were created by Nova when booting the VM. In the vlan-aware-vm case the parent port is passed to Nova, so the port data will remain in the DB after the VM deletion. Nova will delete the VIF of the VM (in the example tap1) as part of the VM termination. The OVS agent will detect that deletion and notify the Neutron server that the parent port is down. The OVS agent will clean up the corresponding subports as explained in the next paragraph. The deletion of a trunk that is used by a VM is not allowed. The trunk can be deleted (leaving the parent port intact) when the parent port is not used by any VM. After the trunk is deleted, the parent port can also be deleted. Subport deletion ++++++++++++++++ Removing a subport that is associated with a parent port that was not used to boot any VM is a no op from the OVS agent perspective. When a subport associated with a parent port that was used to boot a VM is deleted, the OVS agent will take care of removing the firewall bridge if using iptables firewall and the port on br-int. Implementation Trunk Bridge (Option C) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This implementation is based on this `etherpad `_. Credits to Bence Romsics. The option use_veth_interconnection=true won't be supported, it will probably be deprecated soon, see [1]. The IDs used for bridge and port names are truncated. :: +--------------------------------+ | VM | | eth0 eth0.100 | +-----+--------------------+-----+ | | +-----+--------------------------+ | tap1 | | tbr-trunk-id | | | | tpt-parent-id spt-subport-id | | (tag 100) | +-----+-----------------+--------+ | | | | | | +-----+-----------------+---------+ | tpi-parent-id spi-subport-id | | (tag 3) (tag 5) | | | | br-int | +---------------------------------+ tpt-parent-id: trunk bridge side of the patch port that implements a trunk. tpi-parent-id: int bridge side of the patch port that implements a trunk. spt-subport-id: trunk bridge side of the patch port that implements a subport. spi-subport-id: int bridge side of the patch port that implements a subport. [1] https://bugs.launchpad.net/neutron/+bug/1587296 Trunk creation ++++++++++++++ A VM is spawned passing to Nova the port-id of a parent port associated with a trunk. Neutron will pass to Nova the bridge where to plug the vif as part of the vif details. The os-vif driver creates the trunk bridge tbr-trunk-id if it does not exist in plug(). It will create the tap interface tap1 and plug it into tbr-trunk-id setting the parent port ID in the external-ids. The OVS agent will be monitoring the creation of ports on the trunk bridges. When it detects that a new port has been created on the trunk bridge, it will do the following: :: ovs-vsctl add-port tbr-trunk-id tpt-parent-id -- set Interface tpt-parent-id type=patch options:peer=tpi-parent-id ovs-vsctl add-port br-int tpi-parent-id tag=3 -- set Interface tpi-parent-id type=patch options:peer=tpt-parent-id A patch port is created to connect the trunk bridge to the integration bridge. tpt-parent-id, the trunk bridge side of the patch is not associated to any tag. It will carry untagged traffic. tpi-parent-id, the br-int side the patch port is tagged with VLAN 3. We assume that the trunk is on network1 that on this host is associated with VLAN 3. The OVS agent will set the trunk ID in the external-ids of tpt-parent-id and tpi-parent-id. If the parent port is associated with one or more subports the agent will process them as described in the next paragraph. Subport creation ++++++++++++++++ If a subport is added to a parent port but no VM was booted using that parent port yet, the agent won't process the subport (because at this point there's no node associated with the parent port). When a subport is added to a parent port that is used by a VM the OVS agent will create a new patch port: :: ovs-vsctl add-port tbr-trunk-id spt-subport-id tag=100 -- set Interface spt-subport-id type=patch options:peer=spi-subport-id ovs-vsctl add-port br-int spi-subport-id tag=5 -- set Interface spi-subport-id type=patch options:peer=spt-subport-id This patch port connects the trunk bridge to the integration bridge. spt-subport-id, the trunk bridge side of the patch is tagged using VLAN 100. We assume that the segmentation ID of the subport is 100. spi-subport-id, the br-int side of the patch port is tagged with VLAN 5. We assume that the subport is on network2 that on this host uses VLAN 5. The OVS agent will set the subport ID in the external-ids of spt-subport-id and spi-subport-id. *Inbound traffic from the VM point of view* The traffic coming out of tpi-parent-id will be stripped by br-int of VLAN 3. It will reach tpt-parent-id untagged and from there tap1. The traffic coming out of spi-subport-id will be stripped by br-int of VLAN 5. It will reach spt-subport-id where it will be tagged with VLAN 100 and it will then get to tap1 tagged. *Outbound traffic from the VM point of view* The untagged traffic coming from tap1 will reach tpt-parent-id and from there tpi-parent-id where it will be tagged using VLAN 3. The traffic tagged with VLAN 100 from tap1 will reach spt-subport-id. VLAN 100 will be stripped since spt-subport-id is a tagged port and the packet will reach spi-subport-id, where it's tagged using VLAN 5. Parent port deletion ++++++++++++++++++++ Deleting a port that is an active parent in a trunk is forbidden. If the parent port has no trunk associated, it can be deleted. The OVS agent doesn't need to perform any action. Trunk deletion ++++++++++++++ When Nova deletes a VM, it deletes the VM's corresponding Neutron ports only if they were created by Nova when booting the VM. In the vlan-aware-vm case the parent port is passed to Nova, so the port data will remain in the DB after the VM deletion. Nova will delete the port on the trunk bridge where the VM is plugged. The L2 agent will detect that and delete the trunk bridge. It will notify the Neutron server that the parent port is down. The deletion of a trunk that is used by a VM is not allowed. The trunk can be deleted (leaving the parent port intact) when the parent port is not used by any VM. After the trunk is deleted, the parent port can also be deleted. Subport deletion ++++++++++++++++ The OVS agent will delete the patch port pair corresponding to the subport deleted. Agent resync ~~~~~~~~~~~~ During resync the agent should check that all the trunk and subports are still valid. It will delete the stale trunk and subports using the procedure specified in the previous paragraphs according to the implementation. Further Reading --------------- * `Darragh O'Reilly - The Open vSwitch plugin with VLANs `_ neutron-12.1.1/doc/source/contributor/internals/tag.rst0000664000175000017500000001101313553660047023240 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Add Tags to Neutron Resources ============================= Tag service plugin allows users to set tags on their resources. Tagging resources can be used by external systems or any other clients of the Neutron REST API (and NOT backend drivers). The following use cases refer to adding tags to networks, but the same can be applicable to any other Neutron resource: 1) Ability to map different networks in different OpenStack locations to one logically same network (for Multi site OpenStack) 2) Ability to map Id's from different management/orchestration systems to OpenStack networks in mixed environments, for example for project Kuryr, map docker network id to neutron network id 3) Leverage tags by deployment tools 4) allow operators to tag information about provider networks (e.g. high-bandwidth, low-latency, etc) 5) new features like get-me-a-network or a similar port scheduler could choose a network for a port based on tags Which Resources --------------- Tag system uses standardattr mechanism so it's targeting to resources that have the mechanism. The system is provided by 'tag' extension, 'tag-ext' extension, and 'tagging' extension. The 'tag' extension supports networks only. The 'tag-ext' extension supports subnets, ports, routers, and subnet pools. The 'tagging' extension supports resources with standard attribute so it means that 'tag' and 'tag-ext' extensions are unnecessary now. These extensions will be removed. Some resources with standard attribute don't suit fit tag support usecases (e.g. security_group_rule). If new tag support resource is added, the resource model should inherit HasStandardAttributes and then it must implement the property 'api_parent' and 'tag_support'. And also the change must include a release note for API user. Current API resources extended by tag extensions: - floatingips - networks - policies - ports - routers - security_groups - subnetpools - subnets - trunks Model ----- Tag is not standalone resource. Tag is always related to existing resources. The following shows tag model:: +------------------+ +------------------+ | Network | | Tag | +------------------+ +------------------+ | standard_attr_id +------> | standard_attr_id | | | | tag | | | | | +------------------+ +------------------+ Tag has two columns only and tag column is just string. These tags are defined per resource. Tag is unique in a resource but it can be overlapped throughout. API --- The following shows basic API for tag. Tag is regarded as a subresource of resource so API always includes id of resource related to tag. Add a single tag on a network :: PUT /v2.0/networks/{network_id}/tags/{tag} Returns `201 Created`. If the tag already exists, no error is raised, it just returns the `201 Created` because the `OpenStack Development Mailing List `_ discussion told us that PUT should be no issue updating an existing tag. Replace set of tags on a network :: PUT /v2.0/networks/{network_id}/tags with request payload :: { 'tags': ['foo', 'bar', 'baz'] } Response :: { 'tags': ['foo', 'bar', 'baz'] } Check if a tag exists or not on a network :: GET /v2.0/networks/{network_id}/tags/{tag} Remove a single tag on a network :: DELETE /v2.0/networks/{network_id}/tags/{tag} Remove all tags on a network :: DELETE /v2.0/networks/{network_id}/tags PUT and DELETE for collections are the motivation of `extending the API framework `_. neutron-12.1.1/doc/source/contributor/internals/rpc_callbacks.rst0000664000175000017500000003132413553660046025256 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) .. _rpc_callbacks: Neutron Messaging Callback System ================================= Neutron already has a `callback system `_ for in-process resource callbacks where publishers and subscribers are able to publish and subscribe for resource events. This system is different, and is intended to be used for inter-process callbacks, via the messaging fanout mechanisms. In Neutron, agents may need to subscribe to specific resource details which may change over time. And the purpose of this messaging callback system is to allow agent subscription to those resources without the need to extend modify existing RPC calls, or creating new RPC messages. A few resource which can benefit of this system: * QoS policies; * Security Groups. Using a remote publisher/subscriber pattern, the information about such resources could be published using fanout messages to all interested nodes, minimizing messaging requests from agents to server since the agents get subscribed for their whole lifecycle (unless they unsubscribe). Within an agent, there could be multiple subscriber callbacks to the same resource events, the resources updates would be dispatched to the subscriber callbacks from a single message. Any update would come in a single message, doing only a single oslo versioned objects deserialization on each receiving agent. This publishing/subscription mechanism is highly dependent on the format of the resources passed around. This is why the library only allows versioned objects to be published and subscribed. Oslo versioned objects allow object version down/up conversion. [#vo_mkcompat]_ [#vo_mkcptests]_ For the VO's versioning schema look here: [#vo_versioning]_ versioned_objects serialization/deserialization with the obj_to_primitive(target_version=..) and primitive_to_obj() [#ov_serdes]_ methods is used internally to convert/retrieve objects before/after messaging. Serialized versioned objects look like:: {'versioned_object.version': '1.0', 'versioned_object.name': 'QoSPolicy', 'versioned_object.data': {'rules': [ {'versioned_object.version': '1.0', 'versioned_object.name': 'QoSBandwidthLimitRule', 'versioned_object.data': {'name': u'a'}, 'versioned_object.namespace': 'versionedobjects'} ], 'uuid': u'abcde', 'name': u'aaa'}, 'versioned_object.namespace': 'versionedobjects'} Rolling upgrades strategy ------------------------- In this section we assume the standard Neutron upgrade process, which means upgrade the server first and then upgrade the agents: :doc:`More information about the upgrade strategy `. We provide an automatic method which avoids manual pinning and unpinning of versions by the administrator which could be prone to error. Resource pull requests ~~~~~~~~~~~~~~~~~~~~~~ Resource pull requests will always be ok because the underlying resource RPC does provide the version of the requested resource id / ids. The server will be upgraded first, so it will always be able to satisfy any version the agents request. Resource push notifications ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Agents will subscribe to the neutron-vo-- fanout queue which carries updated objects for the version they know about. The versions they know about depend on the runtime Neutron versioned objects they started with. When the server upgrades, it should be able to instantly calculate a census of agent versions per object (we will define a mechanism for this in a later section). It will use the census to send fanout messages on all the version span a resource type has. For example, if neutron-server knew it has rpc-callback aware agents with versions 1.0, and versions 1.2 of resource type "A", any update would be sent to neutron-vo-A_1.0 and neutron-vo-A_1.2. TODO(mangelajo): Verify that after upgrade is finished any unused messaging resources (queues, exchanges, and so on) are released as older agents go away and neutron-server stops producing new message casts. Otherwise document the need for a neutron-server restart after rolling upgrade has finished if we want the queues cleaned up. Leveraging agent state reports for object version discovery +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ We add a row to the agent db for tracking agent known objects and version numbers. This resembles the implementation of the configuration column. Agents report at start not only their configuration now, but also their subscribed object type / version pairs, that are stored in the database and made available to any neutron-server requesting it:: 'resource_versions': {'QosPolicy': '1.1', 'SecurityGroup': '1.0', 'Port': '1.0'} There was a subset of Liberty agents depending on QosPolicy that required 'QosPolicy': '1.0' if the qos plugin is installed. We were able to identify those by the binary name (included in the report): * 'neutron-openvswitch-agent' * 'neutron-sriov-nic-agent' This transition was handled in the Mitaka version, but it's not handled anymore in Newton, since only one major version step upgrades are supported. Version discovery +++++++++++++++++ With the above mechanism in place and considering the exception of neutron-openvswitch-agent and neutron-sriov-agent requiring QoSpolicy 1.0, we discover the subset of versions to be sent on every push notification. Agents that are in down state are excluded from this calculation. We use an extended timeout for agents in this calculation to make sure we're on the safe side, specially if deployer marked agents with low timeouts. Starting at Mitaka, any agent interested in versioned objects via this API should report their resource/version tuples of interest (the resource type/ version pairs they're subscribed to). The plugins interested in this RPC mechanism must inherit AgentDbMixin, since this mechanism is only intended to be used from agents at the moment, while it could be extended to be consumed from other components if necessary. The AgentDbMixin provides:: def get_agents_resource_versions(self, tracker): ... Caching mechanism ''''''''''''''''' The version subset per object is cached to avoid DB requests on every push given that we assume that all old agents are already registered at the time of upgrade. Cached subset is re-evaluated (to cut down the version sets as agents upgrade) after neutron.api.rpc.callbacks.version_manager.VERSIONS_TTL. As a fast path to update this cache on all neutron-servers when upgraded agents come up (or old agents revive after a long timeout or even a downgrade) the server registering the new status update notifies the other servers about the new consumer resource versions via cast. All notifications for all calculated version sets must be sent, as non-upgraded agents would otherwise not receive them. It is safe to send notifications to any fanout queue as they will be discarded if no agent is listening. Topic names for every resource type RPC endpoint ------------------------------------------------ neutron-vo-- In the future, we may want to get oslo messaging to support subscribing topics dynamically, then we may want to use: neutron-vo--- instead, or something equivalent which would allow fine granularity for the receivers to only get interesting information to them. Subscribing to resources ------------------------ Imagine that you have agent A, which just got to handle a new port, which has an associated security group, and QoS policy. The agent code processing port updates may look like:: from neutron.api.rpc.callbacks.consumer import registry from neutron.api.rpc.callbacks import events from neutron.api.rpc.callbacks import resources def process_resource_updates(context, resource_type, resource_list, event_type): # send to the right handler which will update any control plane # details related to the updated resources... def subscribe_resources(): registry.register(process_resource_updates, resources.SEC_GROUP) registry.register(process_resource_updates, resources.QOS_POLICY) def port_update(port): # here we extract sg_id and qos_policy_id from port.. sec_group = registry.pull(resources.SEC_GROUP, sg_id) qos_policy = registry.pull(resources.QOS_POLICY, qos_policy_id) The relevant function is: * register(callback, resource_type): subscribes callback to a resource type. The callback function will receive the following arguments: * context: the neutron context that triggered the notification. * resource_type: the type of resource which is receiving the update. * resource_list: list of resources which have been pushed by server. * event_type: will be one of CREATED, UPDATED, or DELETED, see neutron.api.rpc.callbacks.events for details. With the underlying oslo_messaging support for dynamic topics on the receiver we cannot implement a per "resource type + resource id" topic, rabbitmq seems to handle 10000's of topics without suffering, but creating 100's of oslo_messaging receivers on different topics seems to crash. We may want to look into that later, to avoid agents receiving resource updates which are uninteresting to them. Unsubscribing from resources ---------------------------- To unsubscribe registered callbacks: * unsubscribe(callback, resource_type): unsubscribe from specific resource type. * unsubscribe_all(): unsubscribe from all resources. Sending resource events ----------------------- On the server side, resource updates could come from anywhere, a service plugin, an extension, anything that updates, creates, or destroys the resources and that is of any interest to subscribed agents. A callback is expected to receive a list of resources. When resources in the list belong to the same resource type, a single push RPC message is sent; if the list contains objects of different resource types, resources of each type are grouped and sent separately, one push RPC message per type. On the receiver side, resources in a list always belong to the same type. In other words, a server-side push of a list of heterogeneous objects will result into N messages on bus and N client-side callback invocations, where N is the number of unique resource types in the given list, e.g. L(A, A, B, C, C, C) would be fragmented into L1(A, A), L2(B), L3(C, C, C), and each list pushed separately. Note: there is no guarantee in terms of order in which separate resource lists will be delivered to consumers. The server/publisher side may look like:: from neutron.api.rpc.callbacks.producer import registry from neutron.api.rpc.callbacks import events def create_qos_policy(...): policy = fetch_policy(...) update_the_db(...) registry.push([policy], events.CREATED) def update_qos_policy(...): policy = fetch_policy(...) update_the_db(...) registry.push([policy], events.UPDATED) def delete_qos_policy(...): policy = fetch_policy(...) update_the_db(...) registry.push([policy], events.DELETED) References ---------- .. [#ov_serdes] https://github.com/openstack/oslo.versionedobjects/blob/ce00f18f7e9143b5175e889970564813189e3e6d/oslo_versionedobjects/tests/test_objects.py#L410 .. [#vo_mkcompat] https://github.com/openstack/oslo.versionedobjects/blob/ce00f18f7e9143b5175e889970564813189e3e6d/oslo_versionedobjects/base.py#L474 .. [#vo_mkcptests] https://github.com/openstack/oslo.versionedobjects/blob/ce00f18f7e9143b5175e889970564813189e3e6d/oslo_versionedobjects/tests/test_objects.py#L114 .. [#vo_versioning] https://github.com/openstack/oslo.versionedobjects/blob/ce00f18f7e9143b5175e889970564813189e3e6d/oslo_versionedobjects/base.py#L248 neutron-12.1.1/doc/source/contributor/internals/api_layer.rst0000664000175000017500000000571213553660047024443 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Neutron WSGI/HTTP API layer =========================== This section will cover the internals of Neutron's HTTP API, and the classes in Neutron that can be used to create Extensions to the Neutron API. Python web applications interface with webservers through the Python Web Server Gateway Interface (WSGI) - defined in `PEP 333 `_ Startup ------- Neutron's WSGI server is started from the `server module `_ and the entry point `serve_wsgi` is called to build an instance of the `NeutronApiService`_, which is then returned to the server module, which spawns a `Eventlet`_ `GreenPool`_ that will run the WSGI application and respond to requests from clients. .. _NeutronApiService: http://git.openstack.org/cgit/openstack/neutron/tree/neutron/service.py .. _Eventlet: http://eventlet.net/ .. _GreenPool: http://eventlet.net/doc/modules/greenpool.html WSGI Application ---------------- During the building of the NeutronApiService, the `_run_wsgi` function creates a WSGI application using the `load_paste_app` function inside `config.py`_ - which parses `api-paste.ini`_ - in order to create a WSGI app using `Paste`_'s `deploy`_. The api-paste.ini file defines the WSGI applications and routes - using the `Paste INI file format`_. The INI file directs paste to instantiate the `APIRouter`_ class of Neutron, which contains several methods that map Neutron resources (such as Ports, Networks, Subnets) to URLs, and the controller for each resource. .. _config.py: http://git.openstack.org/cgit/openstack/neutron/tree/neutron/common/config.py .. _api-paste.ini: http://git.openstack.org/cgit/openstack/neutron/tree/etc/api-paste.ini .. _APIRouter: http://git.openstack.org/cgit/openstack/neutron/tree/neutron/api/v2/router.py .. _Paste: http://pythonpaste.org/ .. _Deploy: http://pythonpaste.org/deploy/ .. _Paste INI file format: http://pythonpaste.org/deploy/#applications Further reading --------------- `Yong Sheng Gong: Deep Dive into Neutron `_ neutron-12.1.1/doc/source/contributor/internals/plugin-api.rst0000664000175000017500000000227413553660046024542 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Neutron Plugin Architecture =========================== `Salvatore Orlando: How to write a Neutron Plugin (if you really need to) `_ Plugin API ---------- .. automodule:: neutron.neutron_plugin_base_v2 .. autoclass:: NeutronPluginBaseV2 :members: neutron-12.1.1/doc/source/contributor/internals/l2_agents.rst0000664000175000017500000000023213553660046024343 0ustar zuulzuul00000000000000L2 Agent Networking ------------------- .. toctree:: :maxdepth: 3 openvswitch_agent linuxbridge_agent sriov_nic_agent l2_agent_extensions neutron-12.1.1/doc/source/contributor/internals/i18n.rst0000664000175000017500000000241713553660046023253 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Neutron Stadium i18n ==================== * Refer to oslo_i18n documentation for the general mechanisms that should be used: https://docs.openstack.org/oslo.i18n/latest/user/usage.html * Each stadium project should NOT consume _i18n module from neutron-lib or neutron. * It is recommended that you create a {package_name}/_i18n.py file in your repo, and use that. Your localization strings will also live in your repo. neutron-12.1.1/doc/source/contributor/effective_neutron.rst0000664000175000017500000006560313553660047024216 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Effective Neutron: 100 specific ways to improve your Neutron contributions ========================================================================== There are a number of skills that make a great Neutron developer: writing good code, reviewing effectively, listening to peer feedback, etc. The objective of this document is to describe, by means of examples, the pitfalls, the good and bad practices that 'we' as project encounter on a daily basis and that make us either go slower or accelerate while contributing to Neutron. By reading and collaboratively contributing to such a knowledge base, your development and review cycle becomes shorter, because you will learn (and teach to others after you) what to watch out for, and how to be proactive in order to prevent negative feedback, minimize programming errors, writing better tests, and so on and so forth...in a nutshell, how to become an effective Neutron developer. The notes below are meant to be free-form and brief by design. They are not meant to replace or duplicate `OpenStack documentation `_, or any project-wide documentation initiative like `peer-review notes `_ or the `team guide `_. For this reason, references are acceptable and should be favored, if the shortcut is deemed useful to expand on the distilled information. We will try to keep these notes tidy by breaking them down into sections if it makes sense. Feel free to add, adjust, remove as you see fit. Please do so, taking into consideration yourself and other Neutron developers as readers. Capture your experience during development and review and add any comment that you believe will make your life and others' easier. Happy hacking! Developing better software -------------------------- Plugin development ~~~~~~~~~~~~~~~~~~ Document common pitfalls as well as good practices done during plugin development. * Use mixin classes as last resort. They can be a powerful tool to add behavior but their strength is also a weakness, as they can introduce `unpredictable `_ behavior to the `MRO `_, amongst other issues. * In lieu of mixins, if you need to add behavior that is relevant for ML2, consider using the `extension manager `_. * If you make changes to the DB class methods, like calling methods that can be inherited, think about what effect that may have to plugins that have controller `backends `_. * If you make changes to the ML2 plugin or components used by the ML2 plugin, think about the `effect `_ that may have to other plugins. * When adding behavior to the L2 and L3 db base classes, do not assume that there is an agent on the other side of the message broker that interacts with the server. Plugins may not rely on `agents `_ at all. * Be mindful of required capabilities when you develop plugin extensions. The `Extension description `_ provides the ability to specify the list of required capabilities for the extension you are developing. By declaring this list, the server will not start up if the requirements are not met, thus avoiding leading the system to experience undetermined behavior at runtime. Database interaction ~~~~~~~~~~~~~~~~~~~~ Document common pitfalls as well as good practices done during database development. * `first() `_ does not raise an exception. * Do not use `delete() `_ to remove objects. A delete query does not load the object so no sqlalchemy events can be triggered that would do things like recalculate quotas or update revision numbers of parent objects. For more details on all of the things that can go wrong using bulk delete operations, see the "Warning" sections in the link above. * For PostgreSQL if you're using GROUP BY everything in the SELECT list must be an aggregate SUM(...), COUNT(...), etc or used in the GROUP BY. The incorrect variant: .. code:: python q = query(Object.id, Object.name, func.count(Object.number)).group_by(Object.name) The correct variant: .. code:: python q = query(Object.id, Object.name, func.count(Object.number)).group_by(Object.id, Object.name) * Beware of the `InvalidRequestError `_ exception. There is even a `Neutron bug `_ registered for it. Bear in mind that this error may also occur when nesting transaction blocks, and the innermost block raises an error without proper rollback. Consider if `savepoints `_ can fit your use case. * When designing data models that are related to each other, be careful to how you model the relationships' loading `strategy `_. For instance a joined relationship can be very efficient over others (some examples include `router gateways `_ or `network availability zones `_). * If you add a relationship to a Neutron object that will be referenced in the majority of cases where the object is retrieved, be sure to use the lazy='joined' parameter to the relationship so the related objects are loaded as part of the same query. Otherwise, the default method is 'select', which emits a new DB query to retrieve each related object adversely impacting performance. For example, see `patch 88665 `_ which resulted in a significant improvement since router retrieval functions always include the gateway interface. * Conversely, do not use lazy='joined' if the relationship is only used in corner cases because the JOIN statement comes at a cost that may be significant if the relationship contains many objects. For example, see `patch 168214 `_ which reduced a subnet retrieval by ~90% by avoiding a join to the IP allocation table. * When writing extensions to existing objects (e.g. Networks), ensure that they are written in a way that the data on the object can be calculated without additional DB lookup. If that's not possible, ensure the DB lookup is performed once in bulk during a list operation. Otherwise a list call for a 1000 objects will change from a constant small number of DB queries to 1000 DB queries. For example, see `patch 257086 `_ which changed the availability zone code from the incorrect style to a database friendly one. * Sometimes in code we use the following structures: .. code:: python def create(): with context.session.begin(subtransactions=True): create_something() try: _do_other_thing_with_created_object() except Exception: with excutils.save_and_reraise_exception(): delete_something() def _do_other_thing_with_created_object(): with context.session.begin(subtransactions=True): .... The problem is that when exception is raised in ``_do_other_thing_with_created_object`` it is caught in except block, but the object cannot be deleted in except section because internal transaction from ``_do_other_thing_with_created_object`` has been rolled back. To avoid this nested transactions should be used. For such cases help function ``safe_creation`` has been created in ``neutron/db/_utils.py``. So, the example above should be replaced with: .. code:: python _safe_creation(context, create_something, delete_something, _do_other_thing_with_created_object) Where nested transaction is used in _do_other_thing_with_created_object function. The ``_safe_creation function can also be passed the ``transaction=False`` argument to prevent any transaction from being created just to leverage the automatic deletion on exception logic. * Beware of ResultProxy.inserted_primary_key which returns a list of last inserted primary keys not the last inserted primary key: .. code:: python result = session.execute(mymodel.insert().values(**values)) # result.inserted_primary_key is a list even if we inserted a unique row! * Beware of pymysql which can silently unwrap a list with an element (and hide a wrong use of ResultProxy.inserted_primary_key for example): .. code:: python e.execute("create table if not exists foo (bar integer)") e.execute(foo.insert().values(bar=1)) e.execute(foo.insert().values(bar=[2])) The 2nd insert should crash (list provided, integer expected). It crashes at least with mysql and postgresql backends, but succeeds with pymysql because it transforms them into: .. code:: sql INSERT INTO foo (bar) VALUES (1) INSERT INTO foo (bar) VALUES ((2)) System development ~~~~~~~~~~~~~~~~~~ Document common pitfalls as well as good practices done when invoking system commands and interacting with linux utils. * When a patch requires a new platform tool or a new feature in an existing tool, check if common platforms ship packages with the aforementioned feature. Also, tag such a patch with ``UpgradeImpact`` to raise its visibility (as these patches are brought up to the attention of the core team during team meetings). More details in :ref:`review guidelines `. * When a patch or the code depends on a new feature in the kernel or in any platform tools (dnsmasq, ip, Open vSwitch etc.), consider introducing a new sanity check to validate deployments for the expected features. Note that sanity checks *must not* check for version numbers of underlying platform tools because distributions may decide to backport needed features into older versions. Instead, sanity checks should validate actual features by attempting to use them. Eventlet concurrent model ~~~~~~~~~~~~~~~~~~~~~~~~~ Document common pitfalls as well as good practices done when using eventlet and monkey patching. * Do not use with_lockmode('update') on SQL queries without protecting the operation with a lockutils semaphore. For some SQLAlchemy database drivers that operators may choose (e.g. MySQLdb) it may result in a temporary deadlock by yielding to another coroutine while holding the DB lock. The following wiki provides more details: https://wiki.openstack.org/wiki/OpenStack_and_SQLAlchemy#MySQLdb_.2B_eventlet_.3D_sad Mocking and testing ~~~~~~~~~~~~~~~~~~~ Document common pitfalls as well as good practices done when writing tests, any test. For anything more elaborate, please visit the testing section. * Preferring low level testing versus full path testing (e.g. not testing database via client calls). The former is to be favored in unit testing, whereas the latter is to be favored in functional testing. * Prefer specific assertions (assert(Not)In, assert(Not)IsInstance, assert(Not)IsNone, etc) over generic ones (assertTrue/False, assertEqual) because they raise more meaningful errors: .. code:: python def test_specific(self): self.assertIn(3, [1, 2]) # raise meaningful error: "MismatchError: 3 not in [1, 2]" def test_generic(self): self.assertTrue(3 in [1, 2]) # raise meaningless error: "AssertionError: False is not true" * Use the pattern "self.assertEqual(expected, observed)" not the opposite, it helps reviewers to understand which one is the expected/observed value in non-trivial assertions. The expected and observed values are also labeled in the output when the assertion fails. * Prefer specific assertions (assertTrue, assertFalse) over assertEqual(True/False, observed). * Don't write tests that don't test the intended code. This might seem silly but it's easy to do with a lot of mocks in place. Ensure that your tests break as expected before your code change. * Avoid heavy use of the mock library to test your code. If your code requires more than one mock to ensure that it does the correct thing, it needs to be refactored into smaller, testable units. Otherwise we depend on fullstack/tempest/api tests to test all of the real behavior and we end up with code containing way too many hidden dependencies and side effects. * All behavior changes to fix bugs should include a test that prevents a regression. If you made a change and it didn't break a test, it means the code was not adequately tested in the first place, it's not an excuse to leave it untested. * Test the failure cases. Use a mock side effect to throw the necessary exceptions to test your 'except' clauses. * Don't mimic existing tests that violate these guidelines. We are attempting to replace all of these so more tests like them create more work. If you need help writing a test, reach out to the testing lieutenants and the team on IRC. * Mocking open() is a dangerous practice because it can lead to unexpected bugs like `bug 1503847 `_. In fact, when the built-in open method is mocked during tests, some utilities (like debtcollector) may still rely on the real thing, and may end up using the mock rather what they are really looking for. If you must, consider using `OpenFixture `_, but it is better not to mock open() at all. Documentation ~~~~~~~~~~~~~ The documenation for Neutron that exists in this repository is broken down into the following directories based on content: * doc/source/admin/ - feature-specific configuration documentation aimed at operators. * doc/source/configuration - stubs for auto-generated configuration files. Only needs updating if new config files are added. * doc/source/contributor/internals - developer documentation for lower-level technical details. * doc/source/contributor/policies - neutron team policies and best practices. * doc/source/install - install-specific documentation for standing-up network-enabled nodes. Additional documentation resides in the neutron-lib repository: * api-ref - API reference documentation for Neutron resource and API extensions. Backward compatibility ~~~~~~~~~~~~~~~~~~~~~~ Document common pitfalls as well as good practices done when extending the RPC Interfaces. * Make yourself familiar with :ref:`Upgrade review guidelines `. Deprecation +++++++++++ Sometimes we want to refactor things in a non-backward compatible way. In most cases you can use `debtcollector `_ to mark things for deprecation. Config items have `deprecation options supported by oslo.config `_. The deprecation process must follow the `standard deprecation requirements `_. In terms of neutron development, this means: * A launchpad bug to track the deprecation. * A patch to mark the deprecated items. If the deprecation affects users (config items, API changes) then a `release note `_ must be included. * Wait at least one cycle and at least three months linear time. * A patch that removes the deprecated items. Make sure to refer to the original launchpad bug in the commit message of this patch. Scalability issues ~~~~~~~~~~~~~~~~~~ Document common pitfalls as well as good practices done when writing code that needs to process a lot of data. Translation and logging ~~~~~~~~~~~~~~~~~~~~~~~ Document common pitfalls as well as good practices done when instrumenting your code. * Make yourself familiar with `OpenStack logging guidelines `_ to avoid littering the logs with traces logged at inappropriate levels. * The logger should only be passed unicode values. For example, do not pass it exceptions or other objects directly (LOG.error(exc), LOG.error(port), etc.). See https://docs.openstack.org/oslo.log/latest/user/migration.html#no-more-implicit-conversion-to-unicode-str for more details. * Don't pass exceptions into LOG.exception: it is already implicitly included in the log message by Python logging module. * Don't use LOG.exception when there is no exception registered in current thread context: Python 3.x versions before 3.5 are known to fail on it. Project interfaces ~~~~~~~~~~~~~~~~~~ Document common pitfalls as well as good practices done when writing code that is used to interface with other projects, like Keystone or Nova. Documenting your code ~~~~~~~~~~~~~~~~~~~~~ Document common pitfalls as well as good practices done when writing docstrings. Landing patches more rapidly ---------------------------- Scoping your patch appropriately ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Do not make multiple changes in one patch unless absolutely necessary. Cleaning up nearby functions or fixing a small bug you noticed while working on something else makes the patch very difficult to review. It also makes cherry-picking and reverting very difficult. Even apparently minor changes such as reformatting whitespace around your change can burden reviewers and cause merge conflicts. * If a fix or feature requires code refactoring, submit the refactoring as a separate patch than the one that changes the logic. Otherwise it's difficult for a reviewer to tell the difference between mistakes in the refactor and changes required for the fix/feature. If it's a bug fix, try to implement the fix before the refactor to avoid making cherry-picks to stable branches difficult. * Consider your reviewers' time before submitting your patch. A patch that requires many hours or days to review will sit in the "todo" list until someone has many hours or days free (which may never happen.) If you can deliver your patch in small but incrementally understandable and testable pieces you will be more likely to attract reviewers. Nits and pedantic comments ~~~~~~~~~~~~~~~~~~~~~~~~~~ Document common nits and pedantic comments to watch out for. * Make sure you spell correctly, the best you can, no-one wants rebase generators at the end of the release cycle! * The odd pep8 error may cause an entire CI run to be wasted. Consider running validation (pep8 and/or tests) before submitting your patch. If you keep forgetting consider installing a git `hook `_ so that Git will do it for you. * Sometimes, new contributors want to dip their toes with trivial patches, but we at OpenStack *love* bike shedding and their patches may sometime stall. In some extreme cases, the more trivial the patch, the higher the chances it fails to merge. To ensure we as a team provide/have a frustration-free experience new contributors should be redirected to fixing `low-hanging-fruit bugs `_ that have a tangible positive impact to the codebase. Spelling mistakes, and docstring are fine, but there is a lot more that is relatively easy to fix and has a direct impact to Neutron users. Reviewer comments ~~~~~~~~~~~~~~~~~ * Acknowledge them one by one by either clicking 'Done' or by replying extensively. If you do not, the reviewer won't know whether you thought it was not important, or you simply forgot. If the reply satisfies the reviewer, consider capturing the input in the code/document itself so that it's for reviewers of newer patchsets to see (and other developers when the patch merges). * Watch for the feedback on your patches. Acknowledge it promptly and act on it quickly, so that the reviewer remains engaged. If you disappear for a week after you posted a patchset, it is very likely that the patch will end up being neglected. * Do not take negative feedback personally. Neutron is a large project with lots of contributors with different opinions on how things should be done. Many come from widely varying cultures and languages so the English, text-only feedback can unintentionally come across as harsh. Getting a -1 means reviewers are trying to help get the patch into a state that can be merged, it doesn't just mean they are trying to block it. It's very rare to get a patch merged on the first iteration that makes everyone happy. Code Review ~~~~~~~~~~~ * You should visit `OpenStack How To Review wiki `_ * Stay focussed and review what matters for the release. Please check out the Neutron section for the `Gerrit dashboard `_. The output is generated by this `tool `_. IRC ~~~~ * IRC is a place where you can speak with many of the Neutron developers and core reviewers. For more information you should visit `OpenStack IRC wiki `_ Neutron IRC channel is #openstack-neutron * There are weekly IRC meetings related to many different projects/teams in Neutron. A full list of these meetings and their date/time can be found in `OpenStack IRC Meetings `_. It is important to attend these meetings in the area of your contribution and possibly mention your work and patches. * When you have questions regarding an idea or a specific patch of yours, it can be helpful to find a relevant person in IRC and speak with them about it. You can find a user's IRC nickname in their launchpad account. * Being available on IRC is useful, since reviewers can contact you directly to quickly clarify a review issue. This speeds up the feedback loop. * Each area of Neutron or sub-project of Neutron has a specific lieutenant in charge of it. You can most likely find these lieutenants on IRC, it is advised however to try and send public questions to the channel rather then to a specific person if possible. (This increase the chances of getting faster answers to your questions). A list of the areas and lieutenants nicknames can be found at :doc:`Core Reviewers `. Commit messages ~~~~~~~~~~~~~~~ Document common pitfalls as well as good practices done when writing commit messages. For more details see `Git commit message best practices `_. This is the TL;DR version with the important points for committing to Neutron. * One liners are bad, unless the change is trivial. * Use ``UpgradeImpact`` when the change could cause issues during the upgrade from one version to the next. * ``APIImpact`` should be used when the api-ref in neutron-lib must be updated to reflect the change, and only as a last resort. Rather, the ideal workflow includes submitting a corresponding neutron-lib api-ref change along with the implementation, thereby removing the need to use ``APIImpact``. * Make sure the commit message doesn't have any spelling/grammar errors. This is the first thing reviewers read and they can be distracting enough to invite -1's. * Describe what the change accomplishes. If it's a bug fix, explain how this code will fix the problem. If it's part of a feature implementation, explain what component of the feature the patch implements. Do not just describe the bug, that's what launchpad is for. * Use the "Closes-Bug: #BUG-NUMBER" tag if the patch addresses a bug. Submitting a bugfix without a launchpad bug reference is unacceptable, even if it's trivial. Launchpad is how bugs are tracked so fixes without a launchpad bug are a nightmare when users report the bug from an older version and the Neutron team can't tell if/why/how it's been fixed. Launchpad is also how backports are identified and tracked so patches without a bug report cannot be picked to stable branches. * Use the "Implements: blueprint NAME-OF-BLUEPRINT" or "Partially-Implements: blueprint NAME-OF-BLUEPRINT" for features so reviewers can determine if the code matches the spec that was agreed upon. This also updates the blueprint on launchpad so it's easy to see all patches that are related to a feature. * If it's not immediately obvious, explain what the previous code was doing that was incorrect. (e.g. code assumed it would never get 'None' from a function call) * Be specific in your commit message about what the patch does and why it does this. For example, "Fixes incorrect logic in security groups" is not helpful because the code diff already shows that you are modifying security groups. The message should be specific enough that a reviewer looking at the code can tell if the patch does what the commit says in the most appropriate manner. If the reviewer has to guess why you did something, lots of your time will be wasted explaining why certain changes were made. Dealing with Zuul ~~~~~~~~~~~~~~~~~ Document common pitfalls as well as good practices done when dealing with OpenStack CI. * When you submit a patch, consider checking its `status `_ in the queue. If you see a job failures, you might as well save time and try to figure out in advance why it is failing. * Excessive use of 'recheck' to get test to pass is discouraged. Please examine the logs for the failing test(s) and make sure your change has not tickled anything that might be causing a new failure or race condition. Getting your change in could make it even harder to debug what is actually broken later on. neutron-12.1.1/doc/source/contributor/stadium/0000775000175000017500000000000013553660156021407 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/contributor/stadium/guidelines.rst0000664000175000017500000002257313553660047024301 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Sub-Project Guidelines ====================== This document provides guidance for those who maintain projects that consume main neutron or neutron advanced services repositories as a dependency. It is not meant to describe projects that are not tightly coupled with Neutron code. Code Reuse ---------- At all times, avoid using any Neutron symbols that are explicitly marked as private (those have an underscore at the start of their names). Try to avoid copy pasting the code from Neutron to extend it. Instead, rely on enormous number of different plugin entry points provided by Neutron (L2 agent extensions, API extensions, service plugins, core plugins, ML2 mechanism drivers, etc.) Requirements ------------ Neutron dependency ~~~~~~~~~~~~~~~~~~ Subprojects usually depend on neutron repositories, by using -e git://... schema to define such a dependency. The dependency *must not* be present in requirements lists though, and instead belongs to tox.ini deps section. This is because next pbr library releases do not guarantee -e git://... dependencies will work. You may still put some versioned neutron dependency in your requirements list to indicate the dependency for anyone who packages your subproject. Explicit dependencies ~~~~~~~~~~~~~~~~~~~~~ Each neutron project maintains its own lists of requirements. Subprojects that depend on neutron while directly using some of those libraries that neutron maintains as its dependencies must not rely on the fact that neutron will pull the needed dependencies for them. Direct library usage requires that this library is mentioned in requirements lists of the subproject. The reason to duplicate those dependencies is that neutron team does not stick to any backwards compatibility strategy in regards to requirements lists, and is free to drop any of those dependencies at any time, breaking anyone who could rely on those libraries to be pulled by neutron itself. Automated requirements updates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ At all times, subprojects that use neutron as a dependency should make sure their dependencies do not conflict with neutron's ones. Core neutron projects maintain their requirements lists by utilizing a so-called proposal bot. To keep your subproject in sync with neutron, it is highly recommended that you register your project in openstack/requirements:projects.txt file to enable the bot to update requirements for you. Once a subproject opts in global requirements synchronization, it should enable check-requirements jobs in project-config. For example, see `this patch `_. Stable branches --------------- Stable branches for subprojects should be created at the same time when corresponding neutron stable branches are created. This is to avoid situations when a postponed cut-off results in a stable branch that contains some patches that belong to the next release. This would require reverting patches, and this is something you should avoid. Make sure your neutron dependency uses corresponding stable branch for neutron, not master. Note that to keep requirements in sync with core neutron repositories in stable branches, you should make sure that your project is registered in openstack/requirements:projects.txt *for the branch in question*. Subproject stable branches are supervised by horizontal `neutron-stable-maint team `_. More info on stable branch process can be found on `the following page `_. Stable merge requirements ------------------------- Merges into stable branches are handled by members of the `neutron-stable-maint gerrit group `_. The reason for this is to ensure consistency among stable branches, and compliance with policies for stable backports. For sub-projects who participate in the Neutron Stadium effort and who also create and utilize stable branches, there is an expectation around what is allowed to be merged in these stable branches. The Stadium projects should be following the stable branch policies as defined by on the `Stable Branch wiki `_. This means that, among other things, no features are allowed to be backported into stable branches. .. _guideline-releases: Releases -------- It is suggested that sub-projects cut off new releases from time to time, especially for stable branches. It will make the life of packagers and other consumers of your code easier. Sub-Project Release Process ~~~~~~~~~~~~~~~~~~~~~~~~~~~ All subproject releases are managed by `global OpenStack Release Managers team `_. The `neutron-release team `_ handles only the following operations: * Make stable branches end of life To release a sub-project, follow the following steps: * For projects which have not moved to post-versioning, we need to push an alpha tag to avoid pbr complaining. A member of the neutron-release group will handle this. * A sub-project owner should modify setup.cfg to remove the version (if you have one), which moves your project to post-versioning, similar to all the other Neutron projects. You can skip this step if you don't have a version in setup.cfg. * A sub-project owner `proposes `_ a patch to openstack/releases repository with the intended git hash. `The Neutron release liaison `_ should be added in Gerrit to the list of reviewers for the patch. .. note:: New major tag versions should conform to `SemVer `_ requirements, meaning no year numbers should be used as a major version. The switch to SemVer is advised at earliest convenience for all new major releases. .. note:: Before Ocata, when releasing the very first release in a stable series, a sub-project owner would need to request a new stable branch creation during Gerrit review, but not anymore. `See the following email for more details `_. * The Neutron release liaison votes with +1 for the openstack/releases patch. * The releases will now be on PyPI. A sub-project owner should verify this by going to an URL similar to `this `_. * A sub-project owner should next go to Launchpad and release this version using the "Release Now" button for the release itself. * If a sub-project uses the "delay-release" option, a sub-project owner should update any bugs that were fixed with this release to "Fix Released" in Launchpad. This step is not necessary if the sub-project uses the "direct-release" option, which is the default. [#jeepyb_release_options]_ * The new release will be available on `OpenStack Releases `_. * A sub-project owner should add the next milestone to the Launchpad series, or if a new series is required, create the new series and a new milestone. .. note:: You need to be careful when picking a git commit to base new releases on. In most cases, you'll want to tag the *merge* commit that merges your last commit in to the branch. `This bug`__ shows an instance where this mistake was caught. Notice the difference between the `incorrect commit`__ and the `correct one`__ which is the merge commit. ``git log 6191994..22dd683 --oneline`` shows that the first one misses a handful of important commits that the second one catches. This is the nature of merging to master. .. __: https://bugs.launchpad.net/neutron/+bug/1540633 .. __: https://github.com/openstack/networking-infoblox/commit/6191994515 .. __: https://github.com/openstack/networking-infoblox/commit/22dd683e1a To make a branch end of life, follow the following steps: * A member of neutron-release will abandon all open change reviews on the branch. * A member of neutron-release will push an EOL tag on the branch. (eg. "icehouse-eol") * A sub-project owner should request the infrastructure team to delete the branch by sending an email to the infrastructure mailing list, not by bothering the infrastructure team on IRC. * A sub-project owner should tweak jenkins jobs in project-config if any. References ~~~~~~~~~~ .. [#jeepyb_release_options] http://lists.openstack.org/pipermail/openstack-dev/2015-December/081724.html neutron-12.1.1/doc/source/contributor/stadium/index.rst0000664000175000017500000000337213553660047023254 0ustar zuulzuul00000000000000.. Copyright 2014 Hewlett-Packard Development Company, L.P. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Neutron Stadium ================ This section contains information on policies and procedures for the so called Neutron Stadium. The Neutron Stadium is the list of projects that show up in the OpenStack `Governance Document `_. The list includes projects that the Neutron PTL and core team are directly involved in, and manage on a day to day basis. To do so, the PTL and team ensure that common practices and guidelines are followed throughout the Stadium, for all aspects that pertain software development, from inception, to coding, testing, documentation and more. The Stadium is not to be intended as a VIP club for OpenStack networking projects, or an upper tier within OpenStack. It is simply the list of projects the Neutron team and PTL claim responsibility for when producing Neutron deliverables throughout the release `cycles `_. For more details on the Stadium, and what it takes for a project to be considered an integral part of the Stadium, please read on. .. toctree:: :maxdepth: 3 governance guidelines neutron-12.1.1/doc/source/contributor/stadium/governance.rst0000664000175000017500000003545113553660047024277 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Stadium Governance ================== Background ---------- Neutron grew to become a big monolithic codebase, and its core team had a tough time making progress on a number of fronts, like adding new features, ensuring stability, etc. During the Kilo timeframe, a decomposition effort started, where the codebase got disaggregated into separate repos, like the `high level services `_, and the various third-party solutions for `L2 and L3 services `_, and the Stadium was officially born. These initiatives enabled the various individual teams in charge of the smaller projects the opportunity to iterate faster and reduce the time to feature. This has been due to the increased autonomy and implicit trust model that made the lack of oversight of the PTL and the Neutron drivers/core team acceptable for a small number of initiatives. When the proposed `arrangement `_ allowed projects to be `automatically `_ enlisted as a Neutron project based simply on description, and desire for affiliation, the number of projects included in the Stadium started to grow rapidly, which created a number of challenges for the PTL and the drivers team. In fact, it became harder and harder to ensure consistency in the APIs, architecture, design, implementation and testing of the overarching project; all aspects of software development, like documentation, integration, release management, maintenance, and upgrades started to being neglected for some projects and that led to some unhappy experiences. The point about uniform APIs is particularly important, because the Neutron platform is so flexible that a project can take a totally different turn in the way it exposes functionality, that it is virtually impossible for the PTL and the drivers team to ensure that good API design principles are being followed over time. In a situation where each project is on its own, that might be acceptable, but allowing independent API evolution while still under the Neutron umbrella is counterproductive. These challenges led the Neutron team to find a better balance between autonomy and consistency and lay down criteria that more clearly identify when a project can be eligible for inclusion in the `Neutron governance `_. This document describes these criteria, and document the steps involved to maintain the integrity of the Stadium, and how to ensure this integrity be maintained over time when modifications to the governance are required. When is a project considered part of the Stadium? ------------------------------------------------- In order to be considered part of the Stadium, a project must show a track record of alignment with the Neutron `core project `_. This means showing proof of adoption of practices as led by the Neutron core team. Some of these practices are typically already followed by the most mature OpenStack projects: * Exhaustive documentation: it is expected that each project will have a :doc:`developer `, :doc:`user/operator ` and `API `_ documentations available. * Exhaustive OpenStack CI coverage: unit, functional, and tempest coverage using OpenStack CI (upstream) resources so that `Grafana `_ and `OpenStack Health `_ support is available. Access to CI resources and historical data by the team is key to ensuring stability and robustness of a project. In particular, it is of paramount importance to ensure that DB models/migrations are tested functionally to prevent data inconsistency issues or unexpected DB logic errors due to schema/models mismatch. For more details, please look at the following resources: * https://review.openstack.org/#/c/346091/ * https://review.openstack.org/#/c/346272/ * https://review.openstack.org/#/c/346083/ More Database related information can be found on: * :doc:`/contributor/alembic_migrations` * :doc:`/contributor/internals/db_layer` Bear in mind that many projects have been transitioning their codebase and tests to fully support Python 3+, and it is important that each Stadium project supports Python 3+ the same way Neutron core does. For more information on how to do testing, please refer to the :doc:`Neutron testing documentation `. * Good release footprint, according to the chosen `release model `_. * Adherence to deprecation and `stable backports policies `_. * Demonstrated ability to do `upgrades `_ and/or `rolling upgrades `_, where applicable. This means having grenade support on top of the CI coverage as described above. * Client bindings and CLI developed according to the OpenStack Client `plugin model `_. On top of the above mentioned criteria, the following also are taken into consideration: * A project must use, adopt and implement open software and technologies. * A project must integrate with Neutron via one of the supported, advertised and maintained public Python APIs. REST API does not qualify (the project python-neutronclient is an exception). * It adopts neutron-lib (with related hacking rules applied), and has proof of good decoupling from Neutron core internals. * It provides an API that adopts API guidelines as set by the Neutron core team, and that relies on an open implementation. * It adopts modular interfaces to provide networking services: this means that L2/7 services are provided in the form of ML2 mech drivers and service plugins respectively. A service plugin can expose a driver interface to support multiple backend technologies, and/or adopt the flavor framework as necessary. .. _add-remove-projects-to-stadium: Adding or removing projects to the Stadium ------------------------------------------ When a project is to be considered part of the Stadium, proof of compliance to the aforementioned practices will have to be demonstrated typically for at least two OpenStack releases. Application for inclusion is to be considered only within the first milestone of each OpenStack cycle, which is the time when the PTL and Neutron team do release planning, and have the most time available to discuss governance issues. Projects part of the Neutron Stadium have typically the first milestone to get their house in order, during which time reassessment happens; if removed, because of substantial lack of meeting the criteria, a project cannot reapply within the same release cycle it has been evicted. The process for proposing a repo into openstack/ and under the Neutron governance is to propose a patch to the openstack/governance repository. For example, to propose networking-foo, one would add the following entry under Neutron in reference/projects.yaml:: - repo: openstack/networking-foo tags: - name: release:independent Typically this is a patch that the PTL, in collaboration with the project's point of contact, will shepherd through the review process. This step is undertaken once it is clear that all criteria are met. The next section provides an informal checklist that shows what steps a project needs to go through in order to enable the PTL and the TC to vote positively on the proposed inclusion. Once a project is included, it abides by the Neutron :doc:`RFE submission process `, where specifications to neutron-specs are required for major API as well as major architectural changes that may require core Neutron platform enhancements. Checklist --------- * How to integrate documentation into docs.o.o: The documentation website has a section for `project developer documentation `_. Each project in the Neutron Stadium must have an entry under the 'Networking Sub Projects' section that points to the developer documentation for the project, available at ``https://docs.openstack.org//latest/``. This is a two step process that involves the following: * Build the artefacts: this can be done by following example https://review.openstack.org/#/c/293399/. * Publish the artefacts: this can be done by following example https://review.openstack.org/#/c/216448/. More information can also be found on the `project creator guide `_. * How to integrate into Grafana: Grafana is a great tool that provides the ability to display historical series, like failure rates of OpenStack CI jobs. A few examples that added dashboards over time are: * `Neutron `_. * `Networking-OVN `_. * `Networking-Midonet `_. Any subproject must have a Grafana dashboard that shows failure rates for at least Gate and Check queues. * How to integrate into neutron-lib's CI: there are a number of steps required to integrate with neutron-lib CI and adopt neutron-lib in general. One step is to validate that neutron-lib master is working with the master of a given project that uses neutron-lib. For example `patch `_ introduced such support for the Neutron project. Any subproject that wants to do the same would need to adopt the following few lines: #. https://review.openstack.org/#/c/338603/4/jenkins/jobs/projects.yaml@4685 #. https://review.openstack.org/#/c/338603/3/zuul/layout.yaml@8501 #. https://review.openstack.org/#/c/338603/4/grafana/neutron.yaml@39 Line 1 and 2 respectively add a job to the periodic queue for the project, whereas line 3 introduced the failure rate trend for the periodic job to spot failure spikes etc. Make sure your project has the following: #. https://review.openstack.org/#/c/357086/ #. https://review.openstack.org/#/c/359143/ * How to port api-ref over to neutron-lib: to publish the subproject API reference into the `Networking API guide `_ you must contribute the API documentation into neutron-lib's api-ref directory as done in the `WADL/REST transition patch `_. Once this is done successfully, a link to the subproject API will show under the published `table of content `_. An RFE bug tracking this effort effectively initiates the request for Stadium inclusion, where all the aspects as outlined in this documented are reviewed by the PTL. * How to port API definitions over the neutron-lib: the most basic steps to port API definitions over to neutron-lib are demonstrated in the following patches: * https://review.openstack.org/#/c/353131/ * https://review.openstack.org/#/c/353132/ The `neutron-lib patch `_ introduces the elements that define the API, and testing coverage validates that the resource and actions maps use valid keywords. API reference documentation is provided alongside the definition to keep everything in one place. The `neutron patch `_ uses the Neutron extension framework to plug the API definition on top of the Neutron API backbone. The change can only merge when there is a released version of neutron-lib. * How to integrate into the openstack release: every project in the Stadium must have release notes. In order to set up release notes, please see the patches below for an example on how to set up reno: * https://review.openstack.org/#/c/320904/ * https://review.openstack.org/#/c/243085/ For release documentation related to Neutron, please check the :doc:`/contributor/policies/index`. Once, everything is set up and your project is released, make sure you see an entry on the release page (e.g. `Pike `_. Make sure you release according to the project declared release `model `_. * How to port OpenStack Client over to python-neutronclient: client API bindings and client command line interface support must be developed in python-neutronclient under `osc module `_. If your project requires one or both, consider looking at the following example on how to contribute these two python-neutronclient according to the OSC framework and guidelines: * https://review.openstack.org/#/c/340624/ * https://review.openstack.org/#/c/340763/ * https://review.openstack.org/#/c/352653/ More information on how to develop python-openstackclient plugins can be found on the following links: * https://docs.openstack.org/python-openstackclient/latest/contributor/plugins.html * https://docs.openstack.org/python-openstackclient/latest/contributor/humaninterfaceguide.html It is worth prefixing the commands being added with the keyword `network `_ to avoid potential clash with other commands with similar names. This is only required if the command object name is highly likely to have an ambiguous meaning. neutron-12.1.1/doc/source/contributor/neutron_api.rst0000664000175000017500000001014413553660046023014 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Neutron public API ================== Neutron main tree serves as a library for multiple subprojects that rely on different modules from neutron.* namespace to accommodate their needs. Specifically, advanced service repositories and open source or vendor plugin/driver repositories do it. Neutron modules differ in their API stability a lot, and there is no part of it that is explicitly marked to be consumed by other projects. That said, there are modules that other projects should definitely avoid relying on. Breakages --------- Neutron API is not very stable, and there are cases when a desired change in neutron tree is expected to trigger breakage for one or more external repositories under the neutron tent. Below you can find a list of known incompatible changes that could or are known to trigger those breakages. The changes are listed in reverse chronological order (newer at the top). * change: QoS plugin refactor - commit: I863f063a0cfbb464cedd00bddc15dd853cbb6389 - solution: implement the new abstract methods in neutron.extensions.qos.QoSPluginBase. - severity: Low (some out-of-tree plugins might be affected). * change: Consume ConfigurableMiddleware from oslo_middleware. - commit: If7360608f94625b7d0972267b763f3e7d7624fee - solution: switch to oslo_middleware.base.ConfigurableMiddleware; stop using neutron.wsgi.Middleware and neutron.wsgi.Debug. - severity: Low (some out-of-tree plugins might be affected). * change: Consume sslutils and wsgi modules from oslo.service. - commit: Ibfdf07e665fcfcd093a0e31274e1a6116706aec2 - solution: switch using oslo_service.wsgi.Router; stop using neutron.wsgi.Router. - severity: Low (some out-of-tree plugins might be affected). * change: oslo.service adopted. - commit: 6e693fc91dd79cfbf181e3b015a1816d985ad02c - solution: switch using oslo_service.* namespace; stop using ANY neutron.openstack.* contents. - severity: low (plugins must not rely on that subtree). * change: oslo.utils.fileutils adopted. - commit: I933d02aa48260069149d16caed02b020296b943a - solution: switch using oslo_utils.fileutils module; stop using neutron.openstack.fileutils module. - severity: low (plugins must not rely on that subtree). * change: Reuse caller's session in DB methods. - commit: 47dd65cf986d712e9c6ca5dcf4420dfc44900b66 - solution: Add context to args and reuse. - severity: High (mostly undetected, because 3rd party CI run Tempest tests only). * change: switches to oslo.log, removes neutron.openstack.common.log. - commit: 22328baf1f60719fcaa5b0fbd91c0a3158d09c31 - solution: a) switch to oslo.log; b) copy log module into your tree and use it (may not work due to conflicts between the module and oslo.log configuration options). - severity: High (most CI systems are affected). * change: Implements reorganize-unit-test-tree spec. - commit: 1105782e3914f601b8f4be64939816b1afe8fb54 - solution: Code affected need to update existing unit tests to reflect new locations. - severity: High (mostly undetected, because 3rd party CI run Tempest tests only). * change: drop linux/ovs_lib compat layer. - commit: 3bbf473b49457c4afbfc23fd9f59be8aa08a257d - solution: switch to using neutron/agent/common/ovs_lib.py. - severity: High (most CI systems are affected). neutron-12.1.1/doc/source/contributor/dashboards/0000775000175000017500000000000013553660156022053 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/contributor/dashboards/index.rst0000664000175000017500000001300713553660047023714 0ustar zuulzuul00000000000000CI Status Dashboards ==================== Gerrit Dashboards ----------------- - `Neutron master branch reviews `_ - `Neutron subproject reviews (master branch) `_ - `Neutron stable branch reviews `_ - `Neutron Infra reviews `_ These dashboard links can be generated by `Gerrit Dashboard Creator`_. Useful dashboard definitions are found in ``dashboards`` directory. .. _Gerrit Dashboard Creator: https://github.com/openstack/gerrit-dash-creator Grafana Dashboards ------------------ Look for neutron and networking-* dashboard by names by going to the following link: `Grafana `_ For instance: * `Neutron `_ * `Neutron-lib `_ neutron-12.1.1/doc/source/contributor/testing/0000775000175000017500000000000013553660156021416 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/contributor/testing/images/0000775000175000017500000000000013553660156022663 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/contributor/testing/images/fullstack_multinode_simulation.png0000664000175000017500000007520013553660047031710 0ustar zuulzuul00000000000000‰PNG  IHDRj{Ü:*zGIDATxÚìmpUÕ½ÿ½%bä1…ȃ¦˜‹  È ŠLhƒÆ†JŠ‘$(Q£ r¹¡¦.ŠÆ‚J)XDRQц1ÖŒ¢ä C üÁRêP/Zêàçê Îð‚¼ð/üåg×ÝÝ{Ÿ““sNNÎÃç;kΜ³ÏÚk¯½÷Ú¿Ïú­§}Á×!„ê¤.à „Bà!„Ÿ!„øD!„À'B!>B!>‘ÔÖÖ¶ !”l8pó>QÖª¹¹ù„P(//‚‚O”µzä‘GôœÏœ9³!”<•””èÉ’Š‘Ÿ(›ñùÜsÏA%O"(øŸ|"„À'Ÿ|">øDà!ð‰À'ŸO>øD|"ð‰À'B|"ð >Á'BàO>Ÿ|"ð‰øDàO„À'Ÿ|"„À'Ÿà!>øDà!ð‰À'ŸO>øD|"ð‰À'B|"ð >Ñy}øá‡)8ÊK/½4ì¼ô%‰ÉZš³gÏNV‚_~ùå§Ÿ~š‚ ¢<[æÁ'Ÿ|f˜Ä‰Ûn»­¤¤$ÇÒ-¸à¼’{/,Íd«¯¾*ž½ýöÛ)¸ ʳe|"ð‰Àg†±³ÿþIdO¦ãsÍš5–øŸ|"ð­Í6¹®[¦ãÓx>Á'Ÿ|‚Oð >øDà|‚Oð‰À'Ê)|ž>}º©©é–[n>|ø¥—^:lذÒÒÒ§žzêË/¿Œÿ¥—^òÅ_³fÍgŸ}Œ¼k×®ÛψŒ¾úê«ÚQ»hÇ‘#GÞvÛm>(š"+Ž™ïAƒÙîÚÑ"<üðÃú©Ï3çGÌN˜0AIMš4IPÆ\:Ÿ|ò‰â”——Û±ô©ÜΜ9SWÛ-)øôžÔèÑ£ï¿ÿ~=:>umu¬Ù³g+¾Ë¡.£òlû:mذAç{Í5×Xj7Þx£]`…cåÊ•ú×¥¦Ë«ôugƒçÛ)|*å»îºK©)Y]CݲÖÖÖнÜÒëëë­xèš·´´€O>Q¶áóСC²Œ„I&øOú“/þ| b…Æ—­ VG&í(kº£xô;}’AôZv}ÊL{#\tÑEŽß‚~^A"–Qqãó‹/¾¡ƒ‡èß¿¿ê ‘ð)œ¨Z){Ú×{¤ÐhÞÚŒ.`^^^¤U·èìt‡O]äÞ½{ÓÔYëÜC½mÝ©ùóç{#«Z>øDY…OÑÑGù ò”™Ç{Ìù:²òü±—mÎî»ørûäôØFqA™dC r²¼²n²øJÁYØ-[¶Äè}šeWèµÑÚÅ"(ÿ¨r•”=;)¹AŽ1>¤Å‡OqËaFÌ“‹¦ÝEn»zîÂú޵wï^o6_{É׿ãŽ;l°±¥æªzŸ÷Þ{¯;_qKIÙùº›"½Õñig¡£[>uŽî¼”~(>EkkS_ÂÁ'Ÿà³kå,ØãmªûK9_¶Þ×´ÛÔÔdTêmÅud2+ì]AGqnÊ”)Sbìût–ݲa~ÕîÝ»…%£¯ùú´-¾–dç•zs>*ÛKî»·’¡órH ž‚Ë¿çKP>±k ðå$Rß§|z»ìªL› äæ:²ÅXð)é\¼Í¿:â°aÃB3ém xúé§µ— ÉK/½Ô©CƒO>QºãS†ØQ-Ø=&“gîL¡‘rÇŽu¡ÝiÎÄ[ǤLJÇËwã™ÐY|†ö)ÊW³—.]zÖ®ÙË¡øð©Š‚å<È-IW)˜O±Ç]óÐ4]þ]cut|ºír²£ƒ04“â3ô"«²âê ¡ø Ö À'Ÿ({ðé<$×pê“,²âÈÇ2ïÁù£®Õ'y~a„ A2•——‡îåzR½>k,ø ½n"´²§œ Tч°zdzÄO‡Hçåºf½§ +©ãÊ3‹4úÆÕi¼NtïSîp©wÓuvjÈ®»È‘ör•ƒC‡ñéëZŸ|¢¬Â§ãVÐ) •ëª akrm®iב©¾¾>º¥ö¶¦Æ‚Ïýû÷wê|•%1Ïõz/{øtËEò´DÊÎN\Šî¿ÿþÐÞÊ8&®è|ÙÝå8ðÙ»wïH£v]~ä.ûð)§œ‰+|¢lƧµ=Æ>·Ï;"7’‚=‹ŽLÞݤà3úhRÁc×®]r¤[n¹eôèѾ±©ÑñéæÛe­ Îw²Ê¼õF§ð¯Ã©V!·^ó pí,>Eky´:_elæÌ™:_ßø·£ÜâH§æ.©ŸéÔÜó62§rª.øDà|v> c‰üÉ'Ÿ\Ð ?1:vqã3Êd’¥K—ºQ¬¾É-®ÒŸÞO¡Š¥QÔ†ËOaË–-ÞQÇ^¹í±ãó³Ï>2C'êÉn¤´ÛÑ%”»nvøDàO¿Ìà 31Npts*bëýJ1>•O×-g¼Ãä}®\¹²¥¥Eÿ:x$ˆO7f8ÊšæúNÁ5ϺÑ7åååÚ(/VÍõ}ƈOù‹Þi»â¥ÙÙ³g?öØcŠ)<ˆùØñé >øDàóŸú2#­.”ncïVL.>œäU¿úê«ÁS“c >Ú#ÈÒm—§Ÿ~:Ò¹›çç=…ÖÖV7AV„ ö"»1âÓÑQÕ¾Á®Ê >uÐH§æv·‹¥eÂuý>õÔSàO”[øtËåìÞ½;Ò¼Fy3r¡vìØáš"£ÙÙµkסC‡¼óüRŒO×Vœôir 2$8tÈ ¬õ ‘ 6w{OÁM›‰Ô,ÇŽOÜMû‰ô^q7U7¾‘·‘¦lºñð‰À'Ê |:âÞ{ïÎW›œP__o?ïºë®ÐønR£QNàÓë!õîÝ[ÝZêô©ïn…×ìº?TΨ›0ºk×.ÇÎH‹öu Ÿ^ÞÿýÚ×ЉŽO×6;aÂ/AwïÞm‹î:y;íâçNÓãQlB‹½ŽÆ»"¼'n?ÁÕ;æHWR>½7{J0ôN9R‰¸÷Û¸iºþÞù»­­­¾µg›ššâÀ§¹¶b¶y™û÷ïwg•_Ó.øDàå >ƒA½ó=ÄÈàRpÁøÞù”†oq¸¸ñé]ñÜ‹“èø2½(íÕ]î¤dñݪxwÜqG‚ø4/ÓKJïÕ³w¥ùp"y©&ˆ*ŽKA» r–Èðáý:tèo”¬u:îÝ»×{¾6ûÖeCpuÜõyбàSHvW[wÙ{ e/¸ øDàå>mLp–½,o¤æ>ù"Áw–ÉY¼ýöÛƒ ÅÏO>ùÄë¹åU;œ÷)Òxwt}øá‡¿üòKÌšUõ\)Ž{¡SÖ‰{ëú.WR‰‡.› Íß¼ÿùóçÛÕs´¯E]à÷âÙ5ÿ*šÕåÅ›œT¬œTóãµ%öºyŸÊ­êÞÜÚ{]B»{Á'Ÿ(·ðéº6ÅQe楗^Š´`¬âèsç%O¨‹ÞªaS!÷ïße¥Àè§#®³ËûÅ¡O?ýÔ§ÏX^®©ÓiiiqW/ƹCâŸ*º Á$_P7În_§V‡ïÔÙéŽwöF€O>Q–㡬øDà|"„À'Ÿ|">øDà!ð‰À'ŸO>øD|"ð‰À'B|"ð‰À'BàO>Ÿ|"ð‰øDàO„À'Ÿ|"„À'Ÿà!>øDà!ð‰À'ŸO>øD|"ð‰2ŸóçÏ¥½Ö®]û«óâR¤¿fΜ >Á'ÊfmÞ¼ù„Pר­­ #>Q6ô”ƪªª*((0sÜ¿û2`À€êêj.N:«¹¹ó>BÝ #GŽ”””/ÇßÞÞ®;wî5j”m,--=zô( !ð‰úF§NZ¸p¡1rÈ![·n=wîœûWß7nÜXXXhóóÏ?ç¢!>Ê]}õÕW}úôóóóΜ9Ó†w*ŽÅ\¹råÙ³g¹€O„rNÍÍÍÅÅÅæSVUU8q¢Ã]NžîT.(´ü ð™ñø\»ví'(‹tðàAÁÒÀYXXøøãÿõ¯M0MYÀHøìlùÙ¹sç´iÓ,{—_~ù† ¸eY¯ÐòƒÀ'øDé¢ÿùŸÿYºti¯^½Ì½»ûî»9Òuæ/‘òÓÔÔ4räHƒè÷¿ÿý;vpûÀ'Ÿàuƒžyæ™Ë.»Ì€T^^þÞ{ïuµùK°üÈ'–gì^Þ2sæÌ½{÷rÁ'Ÿà¥Ho¼ñ†8ƒÐèÑ£_yå•Ô˜¿¤”Ÿ£GÞwß}ò•“î1#ð >øDáêŠnÎãÓ´oß>w"ßýîwW­ZÕE'‚À'øDà3§Õuݜ݂OSkk« Ñ”†þüóÏs£Á'Ÿà%M]ÚÍÙø4=÷Üs—_~¹h*¦rÇÁ'Ÿà%¤tsv;>?9?ªhÕªUýúõ³3={öÁƒ¹ûàOð‰:­”us¦>MGŽY´h‘U¤<=z”’>øÌu|nذA‡–Aljj²/ÚøÚk¯é{¨«á–ôlx¿thÓS|ÅRÜÍ™>ø4½÷Þ{3gÎtõÎê ú¢"¡ò`_ÜÝŒ´ƒ7Zë4JV…Ö¾DOÿ•W^Y{^;wîŸ|‚Ïøe½w{÷îµÑ"6çϬÐ6Io´DôÇ?þqΜ9î– ï—è²fq7gºáÓ$D?ÞµZ+'Ö3ªò`_ÜÝ”BSðFKD;vìÐpD´¶eû)ý]»v >Ü»ì´iÓrÊ“Ÿà|&S6<$õøÔùz;>åôˆ»fþ²¸›3=ñé*nT‘4nܸÔãS‰¸C8|Šî‘ÒW±;{õêen±µ%Xe|"ð >ãÑôéÓ/ºè"}±¦9«Œ§Ÿ"“YéS§öë×/JË›bZûaWã³{»9ÓŸ†ŸúúzUô/ÿò/?ûÙÏzöìYVVÖ-øÜ¹s§’½ûî»ÿøÇ?êKMMMh§ƒþRž½mÕ_í>øŸ–ŒŽ€¤/=ô\ oÓh,øQ”s™!Ù2ý%óäm “aZ´hÑØ±c•²8íÎQ(2—N;*‚QôeÁ‚ãÇ”[{ËôsÏ=×¥·éÐÍ™æøt÷·wïÞöò–=zLžƒíêRÏž=/¼ðÂkÃäŧ•#¢•ðNк„LÇ69ásÖ¬YÁÆÛød}ŸŽÖàOð™4|ª‚Y@6ÿÏð9vìXy¾Ê»ÙDm4|Î;×5çz›@Ó ŸiØÍ™qøt·Õ½¼åâ‹/Íï;smýð >Ágºà3t¥=¢öâeÙMUóe§ÌZ»nú4Þ¦s7gæâó“óC‡† "ÏÒî»hªb`÷Ý•+Ál{ûMU }Ý=RýÆúéãÆ§ò +)¹¶9¸vøŸà3]ð)³h£vƒûîÛ·Ï»Q¦ê¹çž;v¬þ²‰wÝ‹ÏôïæÌt|ÛZ[[§Nj×ùòË/7Óði3Izè!Þzõê¥[ãKPåMÖ_n¤OøT!´›^SS“›/bŸà|¦ >ËÊÊôÝë´ÉBÊó°&2}÷Nªóνë.|fJ7gvàÓÝ÷‘#GºeÆŒcEE,ôMRzþùçÝp¡Î·¦·f>­¸ú€ >øŸÝ€Ï;vôèÑcøðá¶|¨ÈdÊ:´änZ£¨TŸ6®Òfï­[·Î¦ºÇGÐ8ð™YݜلOs+U*ܨ¢Y³f©éØà2»* 6ÅJ—­ãá×Uaûîw¿ë¦&O›6Íù¦rF’•FQ|m@¹ÓŠ >Á'øL|šU²©,îB”‹löÑý+Öº)íJÁHæ–:êR|f\7g–áÓ$L*«¶Ì‚ŠD]]]ee¥·„h/7Eµ17ÆŠÊ!CÜÊ ‚±ýe«mt(735(–M@à|&MªÑ‹¡Î™ì—þò÷ü6lØðøã QÁ—´Tr4eì‚ÿê§v€ã˜<`9ÌânÎŒÆçŽó ýK×Ë–-6~ÇÚÏ|ðÁ_þò—*?MMMÁ†t{AŠýë+'*ŠúK÷7–,Y¹ Ë& ð >Qötsf4>c¬ÖXK†4|øð¤¿Å Oð >Q§•ÝœYOÓ† ÜË[¦NêÚføŸ|¦ZÙÑÍ™#øüäü¨¢+VXz=fÏžúžv>Á'Ÿ]ؘ5Ýœ¹ƒOÓ‘#G-ZdË,ôêÕKgšSËéOð‰Àg÷(ûº9s Ÿ¦÷Þ{ÏÞác÷ÑÍZAà|"ð™dek7gnâÓôÚk¯ÙUÙÚŠ>Á'ŸÝ¬,îæÌe|šì½¡nrgŒË# ð >ÁgjjjZ›ÃZ²dIqq±ÙÖK/½TNgFŸŽ[+ éøÌèrÒØØøãÿøâ‹/¶Å¦Nú_ÿõ_¹\ìc,'à|‚Ïh^×(‹Ô£GX,cgñI9ÉÍr>Á'øìà1¨¬¬|$—´råÊn¸¡gÏž:÷¼¼¼’’’†††,8¯ÒÒÒ @gñ™MådÉ’%W^y¥Q¤   ªªê‘Sìå|‚OðÉcðjnnv­µ2'NœÈÁ>³©œìÙ³güøñV ¦L™ràÀ v|‚Oð®#GŽÈÑ4‹)ÓÙÞÞž³|JçÎÓ 2$+ëRà|¢nÀg[[Û¶¬ÓøC³’ýû÷¿óÎ;›šš²à¤|>SŠñ™åä…^¨¬¬¼ð u‚úÔ÷ì+üq—ð >Ág'ƒææfVdŠòòò¼–1•ø¤œäB9Ÿà|vâ1°D¦—WÔýûfSXX·tî÷fÓMº~Zè½K >-ò÷§ýðÖ;ïÊšpCåìY î̦3R=abÜå|‚OðÙi|>úôæ£ÿ{–ÎAív|Þ½bÕËÿï0!ƒ >Á'ŸðIŸàOø$€O>Á'|À'ŸàDOð >Á'øŸà|‚Oð >Á'Ÿà|À'øDà“> àOðIŸð‰À'ø$€Oð >Á'øŸà|‚Oð >Á'Ÿà|À'øDà“> àOðIŸð‰À'ø$€Oð >Á'øŸà|‚Oð >Á'øŸà|À'øDiŒÏu›·+fF°Öò¹é•7Ó0o¶µ(o™­øT4…•ÏmIsÒ<Õü†eµñÅWÒ$KO¼ºãþƵ–«eO>½¹møŸ(ðyiѰ Î+ýñiùœtý´tËØ«;÷õÈËSÞÞ9ôq¶âÓ.þ´›+ÒŸ:˪Óí™5‹†_qÁ?«GSÊnzúõVð >øÌi|îùàÓÁC/³¼OðéBùmÕ^jö00ÿâ‹ÝÏ^}ú®z¾ |‚O>sŸoíÿ°øŠ+MŸàÓ— !sî’\ƒíê­ÛÆLºÖ5Ɔ\ð >øÌ*|nzåÍ‚½øŸ /î;Ôÿ|ÁèÑ£Ç/~ý›à¿ã®»Þ2yË‚;Á'øDà3‡ð¹çƒOo»ÀQ³o¿þà|ºðг-SÊnŠ4˜È" ¿|‚O”%øÜÿÑÏlmÖ¾ë6oo;ü·XRÐ.Šüø³[ßÚÿa|4jÿËgvе›¶EIćσǿ”ÿ§½tèWwîK;›wìyQþ·=X½{+³æÌË5|>ÕüFݪÕÚW¨Ø²û±‰]ºæ©MïüwÜCXí QƯñùôë­6ðUŸëÿv ðyÛ½‹-w,oˆ§oÿsOÁ'øDÏ?ÿýÌ÷-s`0M-½éõ݇}»ö¯¬ÿË­{½ ˜å³ªb9¨h =¨¶‡âÐý{øÄéà^ß+.Š{ã;ª=üä¦H9™9ûÛño¶#ó^Øñ®íòƒg/s Ÿ¢‘ë½sÝ{7Ï„¨#™v™÷à2¡Â~^س§(Øáµ—¡¾ž0Ñ{P%Rú“[‚õâSÀvͤNÚ¢í.~ÓÞ½úô5’EâºâبŸE¥¯Z‚ØeòŒ%¨³Ÿàe<>Ën®üv`}^ž—ˆr°|S->ç-Zìº-§;¨Ž8⪫u,— >¯7qÂd¿MtòžµãÜu%ÓC³!ÿÕ|ÍÄɱ3OÉ*A}9õø¼rÜ¿õÿGÙu¼CI‡^^ìÃ#™@â»M±Ìuø³ÝA)xúë·Þ =¨0éÍžo¬wÞÈM?ÝYì-ÂÜ%$Å=Õé[‚#ÆŒŸàe<>aÚK¾¶·þ›óÌúöëïmÈuø4Ú)…ù÷<(pêK󮃱ãÓæJê Ž”:ô]üÜ%îkÈõZ@ëñg·¶ÿå3íòÛ7Þ³-«–ßÙ…6DË+µW¬y6‘æÜÜÁ§3ú«·nsý|b˜m?µ$”dæÞ ½ÂÀ”²›bôá>ÝAר/ÎU²¡uY«ä>ÊO]¼ºÑaXž«ÛEçš” æÂÊ=õ¡:îà|âÛî] >Á'Êx| WÁvZ7@¦ªfQŸæ º¥vþü÷31bÃÑNÎ_¸‹—¯rq(>¯3η¾ù½âáöïó¿{ÛÇ'é_<)ÊÃþ¾Ÿ1âSóµÓ *…C‡Ú¿Þ¦^’UÝUçzÚY|þë¨Ñ¾ƒŠˆÚhÿ.]óTèAËo«ö¥éH)¢{·»Å ‚«èì¬ÙÙW9ˆ;8_¼oÿ&®€O” ø Œ¸bÃJWçÕyñ)Ï/Þ8|Ê× þ+ [®äžÊ¿ âÓ H”ÿ +)§Ó×ßýc-ø4y;]À¾í,/ÿqd Š™‘ðÚ‰¸êù&ûw̤kƒ•£zÐa#¿§ëu%ç.y ˆyß_÷7®MÊ:D®ØK}ð >QãsÏŸFgƒëõ6ÞÆîq†â3Ò8Ûù÷£4öš×{aÏžrF}4iDÛƒŽ¦s1…âVž¢;DRØc³-øŸ(Ýñ9xèe‘",ä {à—­|܇ϠKg«"„‡=ÃgÁ€‘úø³[ƒ,t½ž‘”ŧKJHön·Æ^¸ËÕÁã_Fʹ·?5—ñyóÜy‘"Œ3Öâ¸É!ѧ`*š Î)tø¼é§s"ôûÓ~hq~ùòk1ÎûœvsEh;­KÊuë*Èë æA9 ÍyôY1‹V8v[•Á'øD™ŠÏ(k±äðº—ën ÊÑÅðyõ¸‰N™|øœ0ùúNáSä³!NÞ*ÂË­{¿lyß²` AyÇÙæ2>ç=¸,R„ÉÓo°8n)WG²ÐC¸îRŸœƒë¸é Ž…®Ï5n|ºög/ÛT]2Õ7…ÆIg)Ÿ5ÿ·ÈF”k>Á'Ÿ1á3ÊA£à3Ò^‘ð©PU³È×iZ½ðžàtOðÙ!>£DvXr“R2 Ÿ/î;dKèÓ:MÝÚ{¾ÖãNásËî?ŒŸZâ޵˄Wð >Q&á3Š#è ²xùªXðùÌÖfá$4¸q@†ÏW]å-¤Á׸ñé|Í[ç.°¡I6±Õ7Ýó·o¼)çÑUÈ|F!™[KÁa):>娉dÁà:cÁ§ëÈ º¼Å§÷)Ëõ¬~ê3tº§ršó`Ëö¯ßz× •ã~[*øŸ(}ñ¥28Š':>c:Ô«wïHÜ0Zï‚AqãSÁÞ‹bã‡x‹ÙðØ¯’²Œ_îà3J7¤y“r°Üx×èøìÔ² ‘â¸a´nH"øüå˯yÇ:é3‘éžÚË͈՗'^Ý÷˜#ð >Qúâ3ʪuWçš›,|F™÷:47|:oØÖb´ëyQ¾wV øŒŸ‘Áº5н+$ Ÿ ŠÄ'Œ#/ö%ã£àSÁœÅü‹/v‹ùÅ7ÝsËî?¸¹¤º& ®·>Á'Jk|zFpA° ®{—D|úFðµµúš”ÁgÛá¿Ù"G:Í— ŽthðÙá¼OkØôsÔ¾il_¶<éøü¦ã`uchóo°q5A|*ÿîUb‰L÷t­ÊÁU&À'øDÙ†Ïo,ã#Oxÿzuç>·ø­×ML">ƒË5¼¾û°á-8#3|*üàÆæt:7|ÆÏþºY"¾Œê™D|Êôuz—Gð®Ý“ >7½óßöìiGŒ{ºç/~ý·®½²:ËÅð >QfãsðÐËÌ9›0ùzAtÅšggή¶-Áµ’ˆO;Ä5'ë ?¹éÖ¹ ÞªÞÓ©÷}vˆO7I¡ã[ð!ÇñicSím'‹VÌ{p™›î)Þ¸ñ;IǧõAÊǽcyƒêƾj£ÏNŸ^Ç1z_o,KÚÆ"ð >QfãSL]/ÿÏfååóK">uPß{Çì vo âóð‰ÓΙö-¡>cÄç- îôÒÅiÀ AÁa¥ÉÂç•ãþÍ5û^œlINŸnÀ­w@oìAÞª[|"ð™Íø”Û'#n‘_ß}X$(¾âJ1uÄUWËÿ O´ÿ£/´‹Û+|êû[û?¬ªYä:oÑâHƒ˜¢´y×A‹eަ{‡LpqüDÂ3[›íÐ]z>ƒð) ®`Ó+ïo\+«pèP±í¶{‡.}Þøâ+¶W”×^Æ‚O› ºìɧ½»äèõ.^ï[Á"DZ®Ý-@8ôòâ8²­d-ýøŸ(SñÙ-Á‹ÏT†'¹¦8d>S|øLYpoeéÔ²´]À'øDà³;ñéV2Jðížà3»ñioÏNâÛ=Á'Ÿà3ƒñù翟±ƒöí×ß÷®Pð >½Xmäm¤¶€O>ÁgNàóá'7 Qw=ðs·øC¤Eà3—ñ)Sˆ*¿­Ú–Jˆô†Qð‰À'øÌ|Þqß2ï(Çâ+®L×|¦>yËIg_(>øŸ) 峪.-éÍI +Öüß$1;Ò+ÁÁg†õ¿ÛÆÙ¦ Õ­wߣG±Ó-Û >øŸ¹^ß}ø…ïFe øÌñд÷Àªç›~ñëßDšÍ>øŸð >3,€Oð‰À'|À'øDà“> àOðIŸð‰À'øŸà|‚Oð >Á'øŸà|‚Oð‰À'|À'øDà“> àOðIŸð‰ÀgâóÖ¹ DPB:‡éåÝŽÏÒŸÜ"‚Ò9|ÚÁ'øD©ÀçæÍ›/@™£¶¶¶nÁ'å$Ê øŸà³s,ã#Ù¥ÒÒRfeee–Wsss| q|RNr¡œ€Oð >sý1°+“#§™2|RNrÇP€Oð >1‹ðI9ŸàOÌ"ø¤œ€Oð‰À'f|RNÀ'Ÿà³>)'àOð‰YŸ”ð >Á'Of|‚OÊ øŸ|bÁ'å|‚O>1‹à“r>øŸ˜EðI9Ÿ|‚OÌ"ø¤œ€O>Á'f|‚OÊ v|‚OÌ"ø¤œ€Oð‰À'f|RNÀ'øDà³>)'àOð‰YŸ”ð‰À'øÄ,‚OÊ å|‚Oð >1‹à“r>Á'Ÿ˜EðI9ŸàOÌ"ø¤œ€O>Á'f|RNÀ'Ÿà³>)'à|‚Ož Ì"|RNÀ'øDà³>)'à|"ð‰YŸ”ð‰:‡Ï¶¶¶mÈ£t >³ïFTVVê4kkk³»„¤Ÿ”“¬/'à33ðÙÜÜ|úgåååuŠ IÁ'7"sKH*ñI9É…r>3ŸvG§—WÔýû&]?­³4)øäFdn I%>-ò÷§ýðÖ;ï"¤s=abÜå|f>}zóÑÿ=KP}ìF|r#2±„¤Ÿw¯Xõòÿ;LHç ‚‚Oð >Á'|À'øŸà“> àOðIŸð‰À'øä.€Oð >Á'øŸà|‚Oð >Á'øŸ”ðIŸà« > à“>øŸðIŸ|‚OøŸà|‚Oð >Á'øŸà|‚OðI Ÿð > à“> àOðIŸð‰À'ø$€Oð >Á'øŸà|‚Oð >Á'øŸà|À'øüç°ü‘'d5Þ9ô±~*¾ý|aÇ»ú¹bͳöó·o¼£ jÿËg¶Ë¿xÔ¶4ï:h[,¼µÿÃXÒY»i›ÛÅ2cá™­ÍÞú´Ÿži¦ãÓî”>SIW<ÒvVB¬Ü‚O O¿Þ*ã®ð‹_ÿ&e8q]ºæ©tÀÛ‹ûéôç.yÀr5ïÁe«žoŸ(ùø¼´hØçe4štý4ûió®~n?¯+™£ qm—òYU¶Ey¸À#¥Ùa"þû™‚Ý.^ó=kÎ<ïF3y’ržÝø´;¥ÏT"ʤ¤¦ÛªJÕ«;÷%ž”êd=òò\¹Ÿ„ »_2ñ)Õ;è´›+’’àúß¿=¥ì¦øöÕìï1Nƒ‹Š–ÿêYð‰R‡OUíí§LUÛá¿Åbƒ®™8Ùvyþwo‡âó{ÅÃ;LDûzwŸY€OQ³øŠ+“¼=|:xèeÞr >³Ÿró/¾X©Å±oùmÕÎhôêÓwʱÿ:jô…={ºw,oŸ(EøTßi[bi6|³ý‹ìµò>|Jr¢§sëÜà3ðY>«J¶Ãk;œžÊ˜j`®l€ÏnÇg㋯Œž0Qá¶{'žZáСv ݱnÕjÛQôÕe|qß!Û¾e÷nYpç·n@«·nŸ(9ø4‡À™¡éå>X®Ý´Í¶\5f\‡èŽû–Yd×ñéŧ³zÑÛoŸ8Ý·_ÿoê½{ñYU³È»Qù´ŸÊ9øLÿ9 oÝæíV6Àgúà3¹!n|½¼Øv|èÙÁA'O¿|¢äàÓñrÏŸzmܦWÞôÁLRÅ?zÏ–5©ùZz>å8Z„èí·Ïlmvñƒøt¼×mèзÍ2÷-ŸYŒÏ·öèʪä«öÏǧ<`Ûk̤kC#4í=`mÂöì >Qrði&C€tµ{/M-T/¼'¯ÑuXþàÆÞí^|Î[´¸ÃöÛ™³¿éÀ˜0ùzgp½øt¼´Úÿò™—¦¹ƒÏ—[÷.[ù¸Å4GUŸw}¬pðø—6ÜfñòU ±#GÕ KÁ×õ¨-®„è‹°­/Á>rÝ#Åw·^wÊÒT}+ölØ@!+«6æ|vˆO1cÙ“OÛÔE +Öÿþíн6·íyúõV¶zãZ°j[: :Ší¾éÿöµ[vÿÁNi*媻ê”+í1¤ø ²S°ÝýÖ»1޲½äeFŠ3bÌX‹é:€OðÙ9«m®Þ5'ÛÏ×wÖÏ‚}£ƒ=š‘°'mØÖ Ÿ¿}ã½è$–Å·6[›2Äçþ¾°†…— ÖOå#ásù¯žõAíѣǴ›+ËPHbÞäé7xã‹sñ ùl£Îè©æ7FO˜è»ûÊ[ݪա~§WÚ1–×I=ñêåä—/¿)Îà¢"K3xÀ'øŒŸª×Ë$ɶz_´«Ç}[ú#M•¹ìyQ¾"f>¯Â‹OýŒÞ~ûø³[]óo(>Í–+ã~>ð‹G•r‡®LvàS5×n9⪫u_ì²ÛEsMî>|οçAä~rS‚øœ0ùú²›+Ý¡]Ì` DâøTa(ŸUåm±ŸÑI&OKð³¶Ê5Úõ Júîu½ø”_èí/fø”;è(Þ«Oß¾ý ¼‡X¼º1)øì0«– ŽBã-JéªC ýêÛZíÜ¡dŽ#õAúð½ýÖú·lši$|ææªC^>‰šÎÛ–;î* ®íù,ˆOc§¦Û´låã×Lœìõã祩jÐ3[›­î¢œ¸±cßô]µî튑·à3’™n¨œí<­Æ_6òÛŠ×÷§ý0Ÿb­BùmÕÊÆÍsç•þä–¸'®8|ÅåÔ:¿Pnâ•ãþÍþ0hP²†E:eKVç>QJñ)Ëh^Ž ôᧃä‹Dr)|øtMÁÁö[çšoÔø¬­­Ý³*+¿ñ®æÌ™³6f•••ùŽb‰$Ÿò>ƒðsžÍ >£Ô{âÆ§µ$sâXà3Åø¼é§s|ÿÊ•tpòÎßpøŒ4l5|JA oÙýç’Šë]ÏE +œû‹? >Ág’—Zu]›ò6"uM…NôáSÁf°Ûo­åV5ª+ðÙ]J">}-´nxޫ߄âÓë•& ŸÞ6”½ÐîUð™J|öí_›ã‰Ó ># [MŸ¡ÃÄÔoçÈýób@IÇçýkÍ–’²¬ øŸÎ—Ý\É–… âÓ­èk¿µ1ngWàS¾à#1«´´ô›ó-+»?f]wÝu¾£X"ɧ# SKoò]UwËF\uuWrø ®õÖþÁg·ãÓKGß×Ú÷™t| ¿" „‚§œ\|zÙ9ïÁe,Ú‡ºŸÎ˜ÊËñ5Ù¹Q-¡íºA|†¶ßÊ=²î4ù ]‡ÏLïûŒ²ø°ëwt•˜(5[¾GBCŒø ½ÝÞQ²oÿG_Dʃwêøì,É¢¬Nä“#™o(¬K34<ñêŽXðiìO§ði£jCC”«1ÙrÇÎøjà|& ŸüâÑà¸Mg£«ÞºWŸ¡í·6øÈËfðÄg” 'îr¹c¹[ºW¤±^XFÇg‡“L:ž·y¹SÍÝà3:>£´Rºa;nZ§#Y("Ý ËàS)GÊF¤—®ÜôÓ9.N²üNð >ãnöº×rV8ÒÌËP|Ûo­åvæìê ‰Ÿà|v ŸQækº)˜nõ€,Ãç–ÝwÝõnýÛû×ò¾OÔýøTøÁ3¬\Úâ2n•·ðBŒø´õ\û­R ŽKŸA|úVtòW# 6Þ†âÓ½95R†OEŽ”‡èïŸÑñ%·z@d¡ø´E‹‚Á9¸)À§Ž)¾dýÖ»Eïp+3$÷MŸà|&Ü‚´ËV>îÆÊF„Ÿ WçÚom%Û¾ýú{»ÓÀgŸQ¹%aƒC‡b\c¨³C‡Ç'C‡ºŸ‘Ö¼mÚ{ÀºC‡Å÷’íà3Æ vº"ô%ÆEÁ'øL>ÿü÷3¶Hž½€ÅZ\å€F™† Ÿ‹—¯ræÞ¦úæ&‚ÏP’…Ž©qͺ˜ÀgÎâ3´îo\œ‹™øÜ²ûÎï¼rÜ¿¹µvÁ'J|*Ì¿çA×Ùi]¡Ñ§äG§w†ƒï Ûà3 ÉB‡h¹–[ïr†é‰Oß’Èೋ–M²ðÅ}‡`V>·%=ñl[Ž1¸þÑcÆv;Á'øL(¸wb_3qrèòl1âӵߚ‚‹å‚ÏH‹öy_§ê]R±o¿þ^ß4­ðéÞ4§íò•u¯cã øŒŸý ô.-´¹m[¸nüÔ’P’¥>ÝÈ&òºøC0¬z¾É­X·ju¤Y.bL|‚ÏäãÓ»D_,Sò£àÓµßúÜ&ð¥ïÓ<~}Ñ›σúâ¶õ9vi…O·‘S¤÷¬ÏÄñi>œX"RÞ²àÎÒŸÜâÖjzy±o ´Â§[¨S+¼O)»)ö%ÀìŸà3¡àˆho‹Ÿ®ý6ô].à3tâʺÍÛ­›Ó«ÁC/ó5}§>åkúÞ³–È5ŸÑI¶¨aEèd1“® .¡—Vø|ªù ß{Öbi‰íÕ§/øD™ÏƒÇ¿TÊöôEôÈb¤Å }ÙÙÚMÛìßà_Í»Ú_Ñç0ä>EMw …¨»ø¹h$’M/¯xøÉM¡ Ù‹¬#]ùØê˼üÈH·Ì[6‚Au»«Þc/NÞæ=¸²}q¢\çШì›6!MS¸¡r¶ouY_|ÅöŠñÝÔ‘êíOólc‡µ¬úœ»äå\A¬ŠŸ–TŒ!ÁžQð >s.d:> ©/!‡OB øŸGðIŸð >Á'ø$€OøDà|À'|"ð >¹à|‚Oð >Á'øŸà|‚Oð >Á'|À'øÄjƒOø$€O>Á'|À'Ÿà“>Á'øŸà|‚Oð >Á'øŸà|RBÀ'|‚OÌ"ø$€OøDà|À'|"ð > à|‚Oð >Á'øŸà|‚ÏŒÇç­s¸÷Zçx˜^^ÑøäFdb I=>KrK§ÞüLH}øþ´‚Ï,ÇçæÍ›/@µµµ¥ŸÜˆÌ-!©Ä'å$Ê øÌ |Úùò¨¹¹¹S—8)øÌÊQZZªÓ¬¬¬Ìî’J|òÀæB9ŸƒO” ’…Ïl½29rš)Ã'ÊCAyŸ<à“>1à|"ð >Á'ŸàOð >øDà|‚Oð‰À'Ÿà|‚O>)à“§|‚Oð‰¡ŸàOð >øŸ|‚Oð‰À'Ÿà|‚O>øŸà|"ð‰À'øŸà|b(°à“§‚Ç|‚O>Á'Ÿà|"ð >øŸàO>ÁgP§OŸ>zôèÉ“'Ï;—xjŸþ¹’:uêøÌ2|ê¶;v,î;ëÕW_}uò¼Îž=‹5Ÿàe>7oÞ}úÔÖÖŠÎÆh£¾„î^QQ¡½d-r}}}aa¡K­¸¸xãÆErÊð©lïÙ³§ÃhÊiiéÂ… sŸª]-]ºT%ÁÝÙ#F¬_¿ÞÝÙ;wjcMMM¤Ýóóó'Mšd?Oœ8QYY©-.µ’’’ööö´}TÛÚÚt Q*‹zd¬Øë©œØã>Á'ÊZ|ÊöUUUä”%+Ôe 8`Ñ–,Y¢----Áä…äåå͘1Ã~–••™)\·nR[¹r¥Œ¬¶È¸¤!>åE©Z cux•Då'gñyüøñ¢¢"»³B¦’Õ¡íΪVa¾£®’âèz†º’ª¢)²>­Ê2dÈ• •7m¬««ÓŽÚ"J¥ásj™DDaU§£ü744èʨú¨j¶¸ (øŸ( ñi\|žekk«l™L€Õ¸=ªhr‚)“ú«¹¹Yß…Û cdLåsh»X•nø”»££DǧŒ *æ!å&>U6T»Ryؾ}»¯Va姺ºÚ¶ˆúé‹fš2eŠ#«ÕE|¤TñÐ!F•¶Ïi$|Š—¾ÓÑã£-:Mð >QvâSæ@küøñ¡-«Ê•$Ó`?…@Õ©ƒíWcÇŽ•ŸjôU®Bó&{*ÓjUÞÿýmç%WGñáóĉö¯ò¬c {^[¦üïÙ³Gÿê>§då9ÚA˜w§ ˜r¤ ŠJ0´iZÛ•s¸ù¹‰O«!…V2tåÅEý« –]R}wM^çÕ{õä°êg0µŠŠ •¥(m¤¢¯Èd%A·ÆWn]1PRÌS§Nùn«RÖ_.‚Û~æÌÅÔ§"[„;wºÄujʹÕí4}Ò£¡Š¦ßzž÷ËÁ'øDÙ‰O¤µ§…š*a£¨¨ÈÛ~µuëVoóJbe¼ôSFP–¨S ƒÖ èí|½ýöÛÝišOcïÍwqÖ\»{;nÁåG’ÛTUUeMÊ&”Œ£»øN¡¾…x,Ó)x›AÌM|ZãA$ªéyË€˜¡[à‹lwPUûY[[«Ÿ7nìl ª·¯TRÕÍH°´|šTn­œ¸Ûª¢«rå"X1pªKYµ/qƒ½)}íÕÚÑð >QvâÓz=C+Ô®ÁMlŒ¥ˆ(ãURRâ`ÊÛ*k¶FvªººZöÎ<¿è;eËœm•QÓ `§)#kÜRäÈÊ:;|j£Lž\3…²bÖ’¦Kêð©øÂ§2©Ýå[¸:ö•?al±Œ7ÎY|FoSUñ°>Qûi—T(ò:…ºà¢‘Û¢êˆ AÒ­×Å—CÙa}Ë*jºÖ¡ènôÊ•+-‚þR>u¾:œèeeÛáÓ*vʤՄô©ï* Vt ŸÚ]©i_eÆ¥­N ´r~àÀÇ[Þ¢7·€Oð‰2Ÿ†º(CkjjáÈ‘#öÓš°Ü€Ù)qKˆõîòÕW_餼#oe:eM"ÙG¥¦Jº×ÚJ•••¶¯NS» ^¶™+cø4¸ZÏ«—úÊ€íbøô:Câº;‹Xú>Ág‡í6ôÌ¡Ñ[* ]¾[¬R'ÂùFÞFÉ­ªG¤·ª§¹;¢º—×¶ÂiMV¼•¸ªtÞBhíÌ6¢ÍŠ·ÍY»+o÷}úd]ÊmR&€Oð‰2ŸV…wÎ¥Y™ÆÆFûiã#"åDMç%“d&RæµÃš»\ ÙGù—òT̤Z¯›o†pî˜g •mm÷Ȩo¦VÇõú=’< wÖà3Yø«ÜOsÝÌϳJXh¯ù×ÿèËö\»«…EÖ©i]–1+'¾y/æZ»‚õñ·ÿ³† b^uh1PɉŸÖ›®"שþ ð >Q†áÓ0ãZMƒ’ÅQ¯!¹q4º"ÍRðJ»×ÕÕE™»";«œ8‡UiÊ®Ùw¯£éól£·_Ê'3¦^#4…à3鎸.ð lX·ŸÏZíªZz,PT-Íú°]—¤O---Öâêæ;|z«DÁm&TJ-Yøt³›”ɤ¯>Á'J/|Ú ×{lVÕ¿nž»Éªü²6 ^\ôþ++uéšy½Š×“Ð_2Ðòld£"×ÍÓõ5Ê™õáód˜lÈ%øL<“æâGšwdžŸ>½UlìvoݺUÿz¦Ð-Ö¿¾¶\S[[[¤ÛaeUU7¬øjmïŸv|AÇTëÕY I”6üNáS¼´ÙÒ*ºIl³Ÿà¥)>ùy¡vÁZn}²ET˶NG×-êm ²þ0ù²‘Œ¯ï(®)¯©©Idõu¯Ú`3vÖNè³›­­­ŠcN3øL<“FµÐÎ<•!U¤|-óvt_䊹nQ“*^‘¦KYw€Ä&•íì¼´›k ùlííF+“ÁÚ›®›Ž˜8>u"v¬H5Qð >Q¶áÓ,Y/d¤ljÌGÐÆ©o–%ˆC³bJÍÛ ¥ÔlÌQ¨Ãaî¬÷tZZZ\Ûš­áâä ÈšvÍØÙ˜eÆMï³QEEEnèP|Z7jŒV/—W²öÝz/&åGZskp ŠÓ¶ûîFA{[)´]õ³`jb¤ë4õJ…Í‹OÝ\WD¿þÇ@¡ÂÂBç"Ûøw£­yÁln±u«ÇˆÏHÃÈ­$ÆJøŸ(Kði†ÆæÃ;VÍu/UTT„vá#eËBçíY‹™ ¸•õ±ÅlHHh£–leŸó’ÓÕP4}wÞ§ò¦<(Kú.Ûj.ˆýt¦ÐŽ(ë©êêêÄNïÚoÑñ)s¬È¶k‡‹"å2>uºVº â¨õ‹ëê…:‹_ÿcœ¶"×®Ó=µ† sCõÝ0l3OBS³ÖQ²î¸öÒWEÊu.¨&d·^ÙS4•"ïÈ[WŠt,ínÍÑ:¨ñ¸C|ku¸àTNÕÛ¼ÓI½bÙð‰²ŸÖ'›¢§ÝÆ©Ú*Ÿnî¬î¯ÊL¨§èn«®˜§,EÍÍÍ*ºeJM”#%5«ç•••ÕtÊ"âæÍ›½ëMªk»¶444()㷻ъ¶~ýz+äʼ2æ °ÜJEö•RoÉQní âSé/Œ  Û >Á'Ê6|¦í•±e|‹£š+i|f¶Þ÷E¶þ¢¾b­üœ5à|‚Ï\ÇgIIIAA³ŒŸþ¹­R”oˆŸÉj>±ÁMΕk›——Ë„ð‰À'øÌN|¾ÿþûö*«I畟Ÿ¯ŸÑ[ƒÁgšKëW½Êõ¤Ž;6)ïôŸ|‚όħùëׯ¯=/}É›>“+Õ´Ñ… .Y²DÞgWÌ¿Ÿ|‚ÏLÂ'|b(À'øDà|‚O>Á'Ÿà|"ð‰À'øŸàO>Á'øŸ|RÀ'OÅ?ƒÚÚÚmY-[V-GN³ëð™õ0G{9Ÿà|FÔêÕ«/@Ù¥¦¦¦¤ã“r’›å|‚OðÙAïÏv]wÝu¶ŽLÖŸé3Ï<·ù‹^~r¡œäŽb,'à|‚Ï\—=í\™Dð‰øŸà|rAÀ'ŸàaþÀ'øDà|"ð >Á'Ÿ|‚Oð >øDà3ëðÙÔÔ´6ÛUVV¦+3gΜ¬?Ó×^{­‹ð™ å$wc9Ÿà|FÔ3Ï<ølR^^^,–±³ø¤œäf9Ÿà|vðTVV>’Õ*--ÍÓìºU‡²þæˆb/'à|‚Ï\ Xó6)øÄ\2H‚ò>y*À'øŸ ð >øŸàOð‰À'øŸ|"ð >Á'øDàOð™˜Îž=›Mø—,Y¢ÕÕÕÍÍÍëׯr^²•iˆÏI“&w­¥¥%//|&ŸgÏžfúôé£ÌèòZ™©¨¨HÃÇMEWŒRàkjj”yUt"ªf©PéçæÍ›Á'Ÿà³øüüóÏE¯ܳgâ¯\¹2 ñY|^ÑÝSåÜØ >“ˆOÕ«|ª««Ó–cÇŽ¥Ûã¦SˆRà=ªEPoÍ`ĈÝîLƒOð‰2 Ÿ2µµµÞ–.YÅ—1’¦öÚ¸q£Ò\·nÝ‚ÕÕåÍM9wîœ Öûï¿ï ŸÚQn¿ývïiÊð)·–¾"466ŠåÞ1ÏX_"YyÙA3Žò-ÀgñÙÜܬKꌪøQš=uëU®”må_å!˜² †î²"XñðÝVkçÎ*Š,?Ç×¥¯B»ÛQeOYµ¬ƒDT¶å›úšvÍ™îöÎrð >Q&á3(1Ïrɽ“«ª}úô‘#hN^ee¥‹ÐÚÚª¿´Ñú–Æ?vìXç2j÷êêjÛÝz[¥5kÖx/”¬¡s½-„Þ¥A#Nii©Ø`>“…Ï ¬‚©›\x³âQx^¶š«·´¾¾ÞúÚí_ûén«v·š ‰üB«¹a>JYG·÷ ¸¥b·nÝê îãé̘1C‘OŸ> >øŸÆ§œNUÛU —E+))‰4 W•};³e²8FSsU÷GeVUÆkÊ”)ú×áÓªùJÄv¿ûî»Íš—`JÓœ%nvÍù 6Þú`>“ŽÏÏ?ÿ\ ²›¥P©Ú$ì9rÄ~ê‹~êFÛOqÔD+fº¿Vå²Ut—Ù •ŠŠÎ]ÿªð¸b ^ ŸÊŒ%®È®õ5zãmPòe_©e¢¡Ÿà|v?>;æ*òK—.„O…ÜGï–;wº¬®\¹R߽͹â¨4æ ‡¾~V»2® Ð~zÇÖZG¬Ë<øìv|º[VTT©åV…GÎâúõë½mÀŽMzQK÷Ñ;iÄúVíFÛ¨ió&TlT±³bi~­wt›·õµSø”›«êšà²Ñæà|¢lçÍÓ§Õô;¬ŒË=zTÔñ,«¥¥¥r‚Žˆ1¯µµU1ëêêÚÿ!3Ùα° 奯Ï‚ÏnǧLnŸ9 XULÝhÑÑc•¾jQÁÎuaÌÝhËIsss»Gµµµ®l‹]¢ÎâSØØì¿Ÿ|‚ÏN÷}:GÁµ¼ù¬g}}½ë¶”C0vìX—ÕP¼‰©¶Ñ×/å•S¯Ÿé‰OoIPU)Òí+++­IÖä­/üäy ¤w£½·+T‘ŠYø´Nze,MØ >Á'Ê0|ÊÝ”¥ó P´®©Ð£Ë&Ú8Ž––UÞå°ZúyÊ”)AïÓMÖ”«ª˜ëÖ­;ù-]ºÔ¶Ø¨ ð™¶ø….ý+z7ZOR°VnÍn2šÞ7ntYµñ“ªwy¨fì¬{Õk7íÊÌš5Ëf&€Ï´Å§*F¡·š¥š“JyoÁÈVýòõ}k-}í8bÄï¿Ößi7ÚzÐ}sKTD­š• >-{%%%é¶ú øŸ(“ð)[“——'Ñ:räˆ ¥¬[p9Pç,©Û"OÂÆq˜­´BŠ`fNiš'ጤ6·DZµj•5ÊÙ–ñ©”e¯cY§|&ŸÞ¡=&ƒÝÐЉOŠà…Ÿw&‰UÚ„I»•ªWYw€¥o¥hüøñn&‰<Ñ‚‚Å1~ǂϥK—†žˆŽe‰§~…gð >QFâ3TfnÌ}”=’µEMe§‚-u&›µ©š»NJñeÔln‰üN‹ <Ë|e÷Nœ3÷Q·^1m>’¶„®Ô#ìÙ]®­­UæUBTlš¦µL(5»•*"™b*YïV™ÔF%RSS£˜*fÞµ¢ãSY²>WÅ ¶ X¿~”Ç|"ð >¿vÄZA®v/+#³"k('2E™?n“ðM‘•‚ÍÌSMßë—È{Pmܺu«Íáó¶÷*qE¶¼Ã޾>?šÃ›+‹¬-Ún?O:%NË£•ûý¬uèè 'ÏÐ+”«éÖË­T½G7NŸÛ·oÒ -Y²¤ô¼”‚Š„•Ã;wº8¡ ›üW?«äyo«@[WWg)()o}®þ¼¼‡ó•%huÁ >u;|À'Ÿà3Õ’q”ô6‹é»ÜßTQß•á}Ÿ¹ö¾ÏÆÆÆææfïVæëƒÇP€Oð >sŸ¶Ü¨ë“ëiM‚‘Ì"øÌM|Z[ÉV¾©-Úž5oŸàÏÎéܹsÖÅeýÖåkgŸàóøñã6 TŸ6ôLìô¶ëb(À'øD¹…OÓž={í},Ñ_h>sŸ_ŸoÕW¶9¯Í›7ç¸ß >Á'Ÿñ\ð™ƒøDà|"ð >Á'ŸàOð >øDà|‚Oð‰À'Ÿà|‚OÌ%†|‚Ož ð >Á'ŸàOð >øŸ|‚Oð‰À'Ÿà|‚O>øŸà|b.Á'å|òT€Oð >1à|"ðAgÏž=qâD{{ûìÙ³ušµµµ:Ó¶¶¶£GfåÚ§àOð‰Àg<úüóÏ·nÝZYY9bÄ{ K”––644´¶¶z_ >1— ð >y*râ18vìXccã”)S|€ìÕ»÷÷ЇOº~ÚÌÙÕ·Î]0kÎ<…Ü8ãª1ãô—7f^^^YY™Ð›Ñ^)øDà|"ð“ZZZFåååôòŠGŸÞüÖþÿòèÿžÚÿòÙ†m-w=ðsñµG^ž¥ŸŸ_WWwüøqð‰¹ÄP€OðÉS‘…A{{û¤I“Œyƒ‡^&çrÓ+o>q::2£ ôá'7]W2Ý‘¸ªªêĉàs‰¡ <€OžŠ,y Ž=:cÆ ƒ\á%ƒåkþùïgâ£f0¼ÙþAõÂ{.º(ß<Ñúúú êŸ|‚O>Ctîܹ•+W8ûöë¿låãq»›ÑÞ>•;k-º#FŒ8räøÄ\‚OÊøä©ÈÈÇ@^`EE…r+ªÝqß²ý}Ñàô†æ]¯3Î骂OÌ%ø¤<€OžŠ { Nœ8aC„  ~aÇ»] Nþü÷3óïyÐüÝÚÚZ¹¿àOÊøä©ÈŒÇ`Ïž=………ʧ|Áw}œ2vº°a[K¯ÞßÌ"•û›Î]¡àOð‰Àç·jkkË;ß9½¼¢Ã¹(]^nÝ+Çך¶>(øDà|"ðùŽ?^PP οçÁî§wP®´ºº|"ðIyŸà3Mƒ3gÎXgù¬ªng§…Wwî³VÜ•+W‚O>¹˜à|¦ÝcpîÜ9›Ü9fÜÄnl³ †v¼kZÚÚÚÀ'Ÿ|‚ÏôR}}½³m;ü·ôa§…~ñè7y+,Ág÷«µµÕ³í¢ù’}z³5á¦Ï;ÎÀ'Ÿàå(>Ï;g£m~rS:³Ó¤ë§)«ºìàO>ÁgwJ9Q~†OâKTºtQ\å¶   MPð‰À'øD¹ˆO¹žC† Q~ÖmÞžþìôŽ!J|"ð >Q.âsÏž=ß¼ 쪫3…¶‚ò,ê§ÃJ~àOð‰rŸK—.UfîzàçqÃlÿG_¼°ãÝ(áåÖ½ïú8¹ë0ˆ÷ÊvKK øDàOðÙ *..VfäÏ%²$бéÒ¢aó-NÊš ýÊ–’Ÿ|"ð >S­£G*'ƒ‡^–àŠztF}ûõÿíï%>´G^^~~~·¿Ë |"ð >QÎá³±±Q9©ªY”|_qå£Oo Ëyâ7Îp°÷ôE‚7ñÚtX|"ð >QÎásÊ”)ÊɦWÞL >']?­Ã˜½z÷¶È‰/oT÷ïÿ©t–,Y>øDà|¦TùùùÊI‚+ ÅŽO·ò»tÕ˜q âó·o¼÷ÍA'MŸ|"ð >S§3gÎXOdâo‹Ÿ¯ï>l‘{^”Ÿàq÷ô…ÒéÓ§øDàOð™:?~Ü:,3Ÿ — ¬¤º÷fàOð‰r Ÿö†²X˜—D|.[ùx²o&Ÿ_ÿV'>øDà|¦H¶Ôí¬9óR†Ï7Û?(8ÿƱd½­ìæÊn_<|"ð >QnáSyV6î¸oY²ðyÍÄÉïú8ÞÚÿá†m-ó-vÃn/-–”EˆÄþn¿’àOð‰r Ÿ–óĽÀÎ.›ð½âárC“²tß­s€O>øŸ)•­™Èj·Å爫®^þÈI|#wù¬*%ÛÜÜ >øDà|¦HIïû¼´hXÝ¿ÿ§ ó-¶…Ýmz̆m-]ôêl†!ð‰À'øLìUeוLïÒ¡C?¹©G^žEë™\|^ö½Ë™¸‚À'Ÿà3¥:vìXRÞôÙáÈÛGŸÞìÚoûUñi`îÞ·~‚O>Á'Ê-|ž>}ZÙxÉàL\©ªYdq¼Ä_·â^º¢ 3¥€O øŸ(ð)å÷ÞR€ÏƒÇ¿¼´hXrg­4ï:¨ÔÆ>øDà|¦TcÇŽUNtc\6Á;@wþ=&ŽÏ‡ŸÜôÍÛÖªªÀ'Ÿ|‚Ï”ª¡¡!ñ¹+±¯:dÓ4­ ÷ÕûÄçu%Ó•ÔöíÛÁ'Ÿ|‚Ï”êÀ‰ŠŸû?úbà%ƒÝš·þû™D^·"KgÏžŸ|"ð >SªsçÎ*3ïú85KƯۼÝ5á>ð‹Gã>èãÏnU eeeTÀ'†|‚O”%ø”jjjœOÒ)|*üàÆîeoíÿ0¾ƒN/¯P [·nŸ|"ð >»AÍÍÍ .žðfû³æÌSˆÑ›l;ü7‹¯ðð“›â8âÁã_^tQ¾²}êÔ)ð‰À'Ÿà³töìÙüüüyyÍ»&}Q½. â´.`iiifð‰¡Ÿàe>¥¥K—*?ÓË+2‚íù¬o¿þݾÔ-øDà|¢\Çç©S§ ”¥Ä'“¤ Ì[´XY­¨¨È¸>1à|¢¬Â§;‹Çþtcxkÿ‡6_åøñãàO>Ág7ëÌ™3怾°ãÝtÆgÙÍ•Êdmmm&ð‰¡Ÿàe>¥uëÖ)WW›˜Ä×Y'7lzåMå°OŸ>é0à|"ð >øüF_}õÕˆ#’òí®o¶ЫwoeO˜ÏÐ>1à|¢,Äç×çß*ßNy‹o:f—޶ý^ñpe¬¦¦&s øÄP€Oð‰²ŸRKK‹-éþrëÞ4açŸÿ~fÒõÓ”«)S¦ÈEŸ|"ð >ÓQõõõöí=|šø´÷´¥U—'øDà|"ðùO:wî\YY™rX|Å•m‡ÿÖ½~gõÂ{l¸Ð‘#G2½€O øŸ(›ñ)>}ÚÞ¤]xÉàîZKaÿG_Ø=óóó[ZZ² €O øŸ(ËñùõùµpÍíÕ»÷3[›S?ÎV¾¯Ž>dÈ÷ß?; øÄP€Oð‰²Ÿ_ŸoÅ­­­µ‘D‰¼Ñ¬³áùß½m«ÚNš4)=û;Á'ŸàϤS³×sN-½INa—‚sϟΚ3O´Ö᪫«ågSŸ ð >Qáóëó³YlI?­zá=íù¬+z:ëþý?í-žùùùÙWÀ'†|‚O”[øüúü¢¸K–,É;ïöí×ÙÊÇÿü÷3É^»bͳ…— 6WNç‰'²²€O øŸ(çði:~üøŒ3Œsß0ðÖ¹ žÿÝÛñqôð‰ÓÚW) h –––¦íìð‰À'øDà3QíÙ³güøñüCâèÌÙÕÏlmÞÿѱ,¿§˜Šo ؚƎÛÚÚšõ|b(À'øD9Oç‰666z9z~–KŸaÅÃ']?mÖœywÜ·lñòUóïyP°Ô–K‹†Yצ“öU ÇŽË‘>1à|"ðù:qâ„N¿¤¤Ä†EWaa¡b®_¿þäÉ“¹VÀ'†|‚O>ÃõÕW_‰¦íííÛ·o·Uª««õ][ÄË4\ð|"ð‰À'øLÇ+“#§ >øŸ|‚Oð‰À'øDà|‚O>øŸà|"ð‰À'øŸà|b(°à“§‚Ç|‚O>Á'Ÿà|"ð >øŸàO>Á'øŸ|"ð >Á'øÄ\b(À'øä©Ÿà|"ð >øŸàOð‰À'øŸ|"ð >Á'øDàOð >Á'æ|RÀ'OøŸàC>Á'Ÿà|"ð >øŸàO>Á'øŸ|"ð >Á'øDà“ò>Á'øŸàC>Á'OøŸàOð‰À'øŸ|‚O>Á'øDàOð >Á'Ÿ|‚Oð >Á'†»>y*x À'øDà|"ð >Á'ŸàOð >øDà|‚Oð‰À'Ÿà|‚OÌ%†‚ò>y*À'øŸ|‚O>Á'øDà|"ð >Á'Ÿ|‚Oð >øDà|‚Oð‰¹Ÿ”ðÉS>Á'øÄP€Oð‰À'øŸ|‚O>Á'øDàOð >Á'Ÿ|f3>ÛÚÚ¶e*++ušµµµYv^èF|feQÉJÅ]NÀ'øŸ±>ÍÍÍ ÌQ^^ž×2¦Ÿ•\('à|‚ÏXKazyEÝ¿ÿ'!Íäë§ùLX*ñi‘¿?퇷Þy!Ãè ã.'à|‚ÏÎáóѧ7ýß³„4"h·ãóî«^þ‡ éDPð >ø$€OøŸ|À'|"ð > à“>øŸð >Á'øŸà|‚Oð >Á'øDà|‚OøŸ|À'|"ð > à“>øŸð >Á'øŸà|‚Oð >Á'øDà|‚OøŸ|À'|"ð > à“>øŸð >Á'øŸà|‚Oð >Á'øŸà|‚OøŸ(ñ¹ÿ£/Þ9ô±ÂÁã_Â9…Ã'N¿°ãÝæ]Á§7¬z¾©pèP…y.ËY¼-]óÔ”²›†^^Ü«O_…¢áWL»¹â¿þ øDà3ñi–GÖwA&]? |zƒ aåD&>Áùë·Þ1fì4yú [vÿ|"ð >s7<ÿ»·{äåOðé ›ÛöÈã´Óï?`àM?£‹yÇò†ÒŸÜraÏž¶}̤k_Üw|"ð >s1¼°ãÝ^½{ÛÕŸàÓ…òÛªíܯ÷oB©÷¯_¾üš€jÿÎ_¶|"ð >s.¬Xó¬ùà|zÖÝ0³WŸ¾>vú®Ì°‘W‚O>Ág…w}üƒgøz³À'ø´°üWÏÚ‰O»¹"RœƒYœ{@Á'øD™Ïý}ñêÎ}¯ï>|øÄéiÿËg/·îÕŽqv}³ýƒß¾ñžH–z§³çEùv^2øñg·‚ÏXð¹tÍSã®»ÞÆâŽ3¶ê®º_¿õnè^Šo»4¾øŠíR4üŠŠš›ÞùïØþâ¾C‹W7Nž~Ãà¢";èø©%‹V4í=à‹yãZ;b¤¦Tm·=»1F|Žž01úc7ªèé×[Á'øD¹ˆO1ljéM® S\)»¹2t Gù¬ªK‹†éSß«Þc»èszy…PÚ©îF壟•`Ûá¿y£ýùïg”²Ž¨ C=Èâ+®´bì˜5gžwæìjå\é€Ïèø¼eÁSÊn Ž>íÕ§¯˜ÊãŠ"xã K1ú‰Wwˆ¸¡C^EµÕ[·ù:#Ýp¡Ô—TݪÕö—üÅNñ;:ÚíÔzôè‘àè!ð >QFâóÖ¹ ÜÀ¯Ä¶†Ç~åÛKt1Æ,^¾Ê¹o¿þ±û¬óïy0ÒL¥#gÔYwnâ†m->¸N˜|ý·Ã7îy°S§/|Šßޏà³C|æ_|±¡bÚÍòÿä“]9îß¾-*=zø\:‡Ï1“®õÞ_Ñ+FÒˆnlŽ\OÁ['2wÉî Ê ò/¿-Eý ¼½•B¸Ë|'k*?n`}Ÿ|æ">6žÿÝÛ¢ÈË­{TGP¹‰A|ÊÕ3¤]5fœ¶Èq¬ªYãqï¸o™#¥ò :ú*–ûüÈe+ÿÖŒ½ÌÛPì~õ¸‰Bi§Nß·XøìŸ†%±n»w±s½ ªŸF±PŸj~cÙ“OÇèØ y¥í~ÓOçøˆ+ˆÚ_C//öý5îºë}–ŠàšX“Ø}+<ë”-ÙÅ«Á'Ÿ9ŠO¡Ë‡G¬W]ħií¦mÎ Œ±åVž¥ën|³ýß¿ë6o·Eeß_וL·¿§E\kûnï:Ÿ±à3´ÝuüÔû×{ ‡ÏHÃV£×Ö*†FS-‚búV9ãí/CµË‰(žøM‡dÇéÑ&²lŸ9ŠÏïmwu¤”WÜhÝŸ ÓË+lw‘24ÂÌÙÕÁƒ*´þ›¼UûK±|GyÀ>ŠƒÏ.ŧ|¸Ð+ŸÛDƒV”a«Q‚ƒ“ G6_—®yÊ9ÄÊ|=Ìo^ÿû·“ÅÎÉÓopk)„ŽœŸàå>/_Á F·hqŸq@K¶&_¹ž‘âlØÖbé 9ß´øŠ+ÝÀùÍÉšÄ>;:)Žõ,ÊÑ â3¾%,Á {öŒǼLïA](ýÉ-®SÖ¾7Å=ôûÓ~èØiÒàÏŧo¨Ž oíÿ0H‡Ï×wîì_ݹÏö½fâd[­>\ëî„É×GñM]Ãr²Ö»Ÿâ3J:n|¬›þèðÊ-¹¤¡aQà k€uð³É*¡Á¡1Ø8¬l .*rå¤ü¶ê`t¬HÙˆeýÛ$²|‚O”©øŒÒqh.-Ägp/G  t,¯û‹¼õŽ÷qm¶rdC§Öx{g;L|ÆŽÏ(Ó%GO˜è›þèð:Ò5ÒM7t)‘ :£Ð9—ó\æ"„¶ëX‘ =ÇÆ_qc…†^^üTó¼° Oð™"|*Ï âÓ›yyà3•øŒ2¾ÔùdÎL >…+[è zzŸëÿ¶w²©<Ñàª@Âç²'Ÿ¶öd‚”¬™£à|¢ìÄçᧃƒo£à³íðßlù‚`XþÈÞÎÔYsæEj¼uÁ·~‚o<°ë z²…‚!´5|ÆŽÏ;–7DŠc4˜`ßg(>ÈÐ`X>ã˳ó†Ýì—*gûâèX‘²\ÏÈ5 ºÉ¾ >Á'Êx|¾Üº74Â뻿õ~pãŒXðÙaÐlßéåqdX¾¦ÍT)0Ðu‚†.EDßgWàó¦ŸÎ àœEïÐÜèøì0¸fÒ(Ãe•ò¯î®Þçf… ¢r:]R1N9Âή[õ|‚O”‘ø”K¡á±_‡æ&‚σǿt35#-Q¤dï¸oÙŠ5Ïú ®ør‚ݤ%5xèe¡KÏ.Âg¤ƒ)«îªK>ÝÐÙHÄòæÊ×CiK÷É6?ÒÅìÛ¿ ³“LVoÝæØeå[ð‰ÀgŽâóª1ã‚KöhË÷ЇÙ&‚O…©¥7EgvUÍ¢àlý´íe7Wú¦¸ ¼dp§–ÛŸq/›PQ³À÷ï/_~ÍM2ñÂ)A|Š‚¶»’ ü‘OéZe½Ì–'ê¶{'̸5ÆO-‰=JÍ ßíê·Í€Oð‰2ŸÁ&P±ÓaÌá*)ø|aÇ»nܬoašëÖ kÿ‡n»bºe彤t³?ãk ŸÅ§u"Zƒªè¢”ÝŸs– >½¯ªžç.yÀÖyèÙÿ:jt蘠›çÎ ] ÈÛ„ûQ–ãóñg·†¾qå{ÅÃËÒ&ŽOïªñ¡¯y¹ëŸ{#Ëý õƒ}M¸:¯Ã >¿î‚eV>·Å»ùÁ·ƒ%ŸnñÛ¡—û ‰(`{¾Ím{Ü›«#ù—n¦Š¸ËèYoý|"ð >ÿ/(¡BaÏŸ “‚“{/Ø¥EÃd÷Cô‘ïèöJäèÍ»Êõ”wë}ß§¶ø¦r¾Üº×w]ÉôHœ‚±ÅÑ—¸ó£Ó±D|}®à3t@ÒTð½}¥ëÂSÍo,]ó”tÕóMÁѶ™À'øD™„ÏHN˜÷`):\ê𲕸$€O>Á'|À'Ÿà“>Á'øŸà|‚Oð >Á'øŸ|‚OðIŸàOø$€O>Á'|À'Ÿà“>Á'øŸà|‚Oð >Á'øŸà|‚OðIŸàOø$€Oð‰À'|À'Ÿà“> àOð >Á'øŸà|‚Oð >Á'øŸàOø$€Oð‰ÒŸ·Î] ‚fY¨_µF!›ÎhzyE·ã³ô'·Ø‹¦³#ÜVwßÂÿøy6‘Â÷§ý|‚OÔåøÜ¼yó(£ÔÖÖÖ-ø¤¨äB9Ÿà|vâ1Y|$5eÊ”¼¼<3%ýúõ+--]¶lY¦ŸTsss| q|fGQ¹ûî»'MšÔ³gO+úò£ý(û Üå|‚OðÉcðNŸ>½nݺQ£F¹*ùŒ3ZZZÎ;—k )øÌè’°~ýú±cǺ’ Ú•ÎîìÙ³ ìøŸ<ÕÞÞ^SS“ŸŸo¦sÈ! 'OžŸY϶¶¶êêjï­¯¯¯?~ü8†|‚Oð >cÕ™3g6nÜèuAJKK›››¿úê+ð™eåäóÏ?×ÙÙÎËË«¨¨È¦†ð >øì½ÿþû .tIaaa†z$àÓ'Õ„Tš1c†«!ëES øŸ|&GgϞݼyó¤I“¼Î¨®L9£àÓ騱cK—.UMÈn¥êF555ííí”sð >øì*9r¤®®®OŸ>fy –,Y"s >Ó¿œë@ú¾qãÆ3gÎP°Á'øDÉÇgmmí6ôÏ’Öe¹âŠ+œ!Ö÷;ï¼SÛÓ6Ï•••]ŠÏt.' %%%^x¡Ý¬^½z•••=ú裔äDÊ øŸà3¢V¯^ÍŒò,SSSSÒñI9ÉÍr>Á'øì€ ÷£Žtß}÷É›¹ì²Ëœõ4hÐôéÓëêêÒ*ŸÏ<óLÜæ/zùI«r²xñâŸüä'ÅÅÅßùÎwìvôéÓgòäÉ , ¬&±œ€Oð >QÒ´k×®E‹}÷»ß5«}ÑEÍž={ÇŽ™uqà3}®ÿÝwßíÆõèÑ£¼¼\ŽÔ_ÿúW g÷–>Á'êXÿó?ÿ£úûÔ©S3:räÈU«V9r|vÑWÆ®½öZï衇þøÇ?RÁ'øDà3ó´wï^¯3$gtæÌ™¯¼ò øL–Þxã¹sçöêÕË š3gNƹûàOð‰Bô׿þuÆ Ó¦Ms¾ÑðáÃÓÙ7J|êÒ­X±B.¦»¤ãÇWÞŽ=JyŸàÏlÓ¾}ûdY† âuF☃øÔåÒEÓ¥s‹@-Z´h×®]”.ð >øÌ~gôù矟>}z=Œ—]vY}}ýÁƒÁgŒ5]:]@ùôŒ Ÿ|‚Ï\tFEM7Ýņ‰Š¬ÝŽ„ôÁ§ Âò¶{[UC—Žò>øŸ¹®¦¦&Ó9£ò±d€º‘é€Ï;w.X°À;hÖ¬Yé?ê Oð >QªuðàAùUÇ÷¶Ov‹3ÚøÓŸsæÌY‹2Aÿõ_ÿ%ˆ<ØùaW^y¥ˆòË_þ²«]VV ŸI/?+V¬øñ|É%—¸Ó1bÄí·ßÞØØHÈP…–>3X›7of!l»ÚÚÚ(?(YåÏŒ'è#(3ÕÐÐpóÍ74È™§ââ⪪ª•+WvÅáš››»¨ü,Y²¤¤¤Ä½0õ;ßùÎÕW_-¯zÕªUÜå¬QhùAà¡î”ï}Ý………ñ¾î¯¾újÛ¶m¥¥¥Þ1AëÖ­;}ú4÷!ð‰PŠtöìYù‚“&Mr4š2eŠø$J¥9ïõ¥¶¶öý÷ßç&">ê6=zTÞgAAÁI_ôSÄêöŒÉ­\¿~½w Š^àç®!>J YÓ¨øäX%ÇTîi·°ª­­­ºº:??ß­Q__üøqnBà¡4Õ±cÇ–.]ê^‘Ö§OŸºººÔ´”~þùç<òHQQ‘://¯¢¢¢¥¥åܹsÜ„À'B™áŒ677ûÆélܸñÌ™3]t¬3fx‡¯]»V4åF >ÊH?~¼¾¾Þ½¨$??áÂ…ÉrF}ž®¯©©iooç²#>Ê;w®¥¥Åë Ž5*îI#ÁA¿úÞE®-B|"Ôý:yòdCCƒ×­®®ŽÝ_TL9¯nLò=zô(!ð‰PN8£;w¨ÈËËsËÌ®]»öÔ©S¡ñµ]ÿÊauîfYYYsssÎ1E|"„º\Á±²UUUn©Ò esåÊ•ra¹tO”:É.oC駦¦¦ÿøÿ˜OBàOÄÓ…øäŸˆ§ !ðÉ>øD Ë>øDOn.øD<]O>OBà“|"ž.„À'8øDà!,l¦>àû÷ﲎÝÛo¿K"§Ï«[îˆåÿÃ?Ÿ|">S÷€+‘(K¨ë|cØ„ ’°¸ó#æÁ'ŸÏdâóÞ{ïý0LŸ}öYŒ×|"ð‰Ê9|Æâe‚Oð >Bà³ø¼ÿþûKJJ¼ˆZ³f¶ìرCnë°aÔ¤I“ôÝþýøãçÏŸ饗jûèÑ£~øa×3ÚÒÒ¢÷ï߯ϋ.ºhöìÙŸ|ò‰¾kûSO=¥ÈÚeøðá=ö˜7:Ð7Þ8pà@ý;hРÛn»ÍÑ|"ð‰øLS|þéOêÝ»·0ùÅ_èçîÝ»óòòÄ»ŸBcI˜\4qKÑ„7Tèêäb†6ÞΜ9Spݵk—ôÒKîpJÁH,>…[—à§Ÿ~*Çôšk®±Ÿòb•½/¿üÒ%xûí·kA|"ð“TçMÁQìa–ôˆæø¥H¥¬PŠãNy­<øŒŸýû÷&oÌòòrq±´´TñwìØá»&†OAQqn¼ñFïŽÚèР³k÷ѹ§&ùšîè|ð‘ÒiéÒ¥™àÏhRmtþüù²­ÙŠÏC‡M™2%•ÀŸ4Þš>ùäë}”¼&†Ïýû÷[÷¤Ï‹•79zôhw8oÎí>º~S“vñÂ[qäþ*Žn]ªàϘôÿÛ;[׺²5Œ_Q¨¨¨(TŒ¸"QQQ¨qEaDDE‡Š#…©¨DD2QQq7¢0bD`D WT**FFô_¸?òpÞYkïsvNÎI;íóˆprÎþXŸïo½ës}}ý´¶õ“Äç«W¯hÚŸ30‚ÏàÓÅ'•‹ç²âóéÓ§|¾~ýú·¶¶¶fà³ @Åçþþþ…`œÔÝÝ]§ŸQð¹ÛúIâóƒ#ø >4œHõy^OƒOƒ·Ù¯›ŽO~œ7nÜàÉþugg'øŒ‚Ïà3ø >ÿøÜÛÛã2þþùçŸ8—pÔã‘ÍÔ¡õõuœÔ7oÞøÞçÏŸ»Ë÷´øüå—_ú_oÞ¼|Fuí¢hvÜŠ®¯c*³7Ìœn[kuPoß¾õ¿Uãsé ÉO ò¢š°)"¦ì2sÚx½{÷Ž[–•ųSƒ·Ì-}‘˜¾_ë§ŠOÒppÏ[½âÅ‹ðh)¡øˆêßÝÝ] ^jn-µÎäéÓ§äÑÑÑ@åzîZŸÇÇÇÜ»¶¶&Zó¯znO>£¨vQnݺEmQ¥!y÷îÝS=uý¥K—|ý;wK³ÖGk‰ôû“¥ÐõEÔ±‡6æ›W«ËȆy–ÖœÉzòL-ãbP§êñÞ5™½™ç_|±¹¹©*½D|ò®­­-…DºvíZ/D,šè4ȹ}û¶“KI§F·Y?õYÌÅܨ1*­ ‘ŸP­‚Ïèc©]”cêÉôÍ£±é_~ùåØõ÷îÝkl«íƒƒwQgªwh«ÚTéZÜ‘ZÇõW§~ø¡ù©ŠjIU_>iI¸Ñ ÒXç—/_:`½¡¿ÿ¾~~^7;S@ V” œô}~ˆ[ôÙZ +ÁÓÒø1óÚ<–Æ–§GŠp.Ÿ„v,âÄ«)„ŽN™R/®ãyŸ>)ÿu\O¯2éªÊCÈôÊWîâªáóçÏkèuµ(êQMû’×U;ðæÍ›_Oä/$=°/„Ágt~µ‹âkƇ½½= +Mu8ç¦x¨ÕÀbµÉ×Ñ$tó!í ­iЋpÚõA8O¾7ˆ2m¦v(U|ʇà„\Û£ØÕ0&yS‘"´v¹«š†…ñùÓO?U<è]|ItìaJê-Äήp5àJß“’ ¿Y·9ˆ`sž6WÏw‘ ä¿Ö˜j›˜ŸvjÁ‰°§&…I£‚¡Maø’ 0vdåTÃçûy” ´d°/Kgħó‘ô!s‰8^‘›&sïH)Y(¼šƇ)}Ý9ïóVð-X»ìîЖoÜlŸØ†yu+çÒ´k®Ç€š¬Õ¯ªö›…û5øÔ4uÇl«ñ©.ˆúÉÚBÓ;ž4½‹Æ<8#> °üN¢Ð¿ T¸ƒº˜‰â×Uü¨—{ÊØ§6…Ñ+šÔ#wL»ÝÝÝŸB»ß…¡ ­Ýz”ŠÆ?Ј—²²~¿µµåî‡>¡4g²i$Ÿ„¿qî_¼xកÚE_#E7C¦4-›à3ø >ƒÏ©8j­Ó œ<âÎ=ám ^ ¶…´wöod+ߌdÌÅgåzí=ÑÆþb¿ÊiñifŒÍ`Ô6c=È1߆¥ÒÄN¤ÖÉMÁ'Ö_ýdâñññ`ׂ¨¾þúëà3øŒ> |$›››ƒÐ~ÇÂ>yòD­uÜ5]31öL–ÙòÚÞaÊo±kÒ„|.>ÇP‡}„Iýü û»ýí‹áS!œÑ¨åäƒKâ4SÑ}àîŽîgåŒá“8Ú ¡–ë5Mƒdp2­IÓ,“ﳸÞNvÙÆoî]ÞÆg= >i Î`¢E%´–7GŠ^ØÂ~$ >ƒÏèÃãÓÎr½}©ÁÉžYªk<Ó²Îõ˜m›N‹Ï)“>ˆPy¾Éñij}}}ìLùŒkœžn^ òl ŸîGmŽyªr‹§^3»5cÒô~pì)KYþøã°úðáÃ¥ãsF£Á€÷¬Gj°oy¢…ÅùûþƒJ}þÁgð}x|ÚNœÿ=6sPYœ»lqa|6óPzà ,¹^Þ¼y³N©ŸˆÏŽH\±g9E/^ ac<ÖˆKÀ†¾³U7,$YϹ=Ò=>q5q ¯—Ðöqçâ“ÌLs·½œZ>1±`ÏTÆ>ƒÏà3øm§V‡Ï1êcÄwvv“ð¥WÝÌÅçXÔt;'j0¨žŠuáÂ…±\X >ûx-Ÿ‡‡‡uåk]&ä©Rsñ9VÆ|£ScƒÏ(øŒV[»ÜyZï?é×y²m=|Þ¹sÇf§sccãÁƒxÃZ‹6}ìs">aïÔ‡Óƒ—îàÜ¿i.>÷÷÷ç¾½.Î[>ÝK,W›6 -F©à3ø >£OŸ^µ2¶=¦áø³×2NÙ¨e®õ_>=%çòåËcÐ,eìspšñt½}ûV}›:(±ïb›€º®+p¦héø|ýúµ\r×àbö¥}ÎØ ÜήC|FÁg´ÌÚealÉ‹@dÖÍ­fÉ|'|ÓgÏžÙ‘:g|šñcPñZÉ3âGVýÃcl×_@B0H“~±„]d(XA{ä% 'NÏ qpp@s§®'Y:>]še}AZ">›505Íg̼ >£à3ZBízõê•ÚàO˜Ô;¡ u„ƒÀàK/ ôº‘sƧ{ÿƺv½mÂÙ®ƒ (ß— †šE ¶æëëëòÕÜ|åÊ•fÌXr™–áÂï± ñ½cí`X:>#c·ø‚%â³Ù¡ÿàºÏà3 >£åÔ.ûj}·áï¿ÿ®E÷uSà rôÄõ*ÃjèƧßլğè}"Í ‚¥àÓ“oXß¡íì·aò^EuS~cicc£qsÇü-ßÒoõþ¯;õÛ&¬Âû\›D6¹kw¹ø$AšX{sŸõ|ö¥âîÝ»4§H(Ê¡ZÆ$ãÖÖ–F(´TäºcífÚÊĵµµ¦ž2̯*Ò4=zä¦ÓjFèEAŸ©*Q×®]S+ªä×ݸq£ÎH§ÉxïÞ=µÑùûàÁƒÅŽ{ >£%Ô.Œ‘MqE*áw:ak—&–ÅS›…• í{žæäéà„šøP„)$ï¨nßìf°Ò±O~õ÷¤†7Ov²/ Ÿz öZ…ˆ×4ovö>kö]>j©Gí Ljƒh¾½½My#aÕ BÂò/WR›øU=.Þh…¢ÅB¦ÞÙg(Ýj=­­è3ÁÐQBÚÛšjGâ½½=¨}­µD Â5”4m¬¢ÅígÜ8>øŒ¯]˜Úæ Šª~XtöõÔ„Æ[ŸÍ™J^:yª™·ýNËj‹³ã“ ïÅ'ƒÒµlÐݶUÞ5ƒU‡K›£Q*øAõàr‘±³eÞ¯fæ­G”×¼b…º>ç,øÄzzåFýapÁg“}µ^«LÖr¢¡%£¶Äª¹FsP›7QøÉAŸpPŸ¯Á‚)øô™hòY)ÌTL?PÓÀ³›ªµ#GG˜ ŸÑyÔ.š™Ífc¤c{°q½»z*8I?Ç7ZùÞŸWåB]ÐïºNÕ5ª!Šìµ¯;Ž”zH[¸ Õ’j&bY=Á¤w Ƕڙ-¹¶õxB5«›¼€ˆcvàiY낺«Žæ´Úì7RŸ>kÁ Ѩ¼ÍX _ª}©–e©iÖàEÓÂÖŒ Uö)ø¼uë–åýŸŸˆ„'¸ ellYðkí¾SÖ±ìƒÚ2-+\`îé"0SN•‹•¼ßú|Ò©/§JÀÓŠˆÌì!•ˆ¬VyŽh½j9 +-Í„5Ì+o\¬|žø¬ß\<Ñ¿þ*uƒ¿;‘‡tÚ<þŸJW¶‘¦Þ|ÖþM&Œ ªž_ÃéóºƒÏ(ó¢èo99óoŠO d|;$µ%>=ÿº³]ç¥áÊ.†Ï±Ú©][[ gðŸQ|ž7>ûÏk'GíJyùò¥æj¨¾9±€={¼Ç§¿áSc®ÍùRsµ›UOæŸÁgEÁç_ð©¹oõ,^«c‚¸þÂ… uøC]¬:0øúõëu¤@“È4Æ,o²í iJcøä—.]j†öñtµ3‰¦ÔQ[žÀOc'ëŸQðEÁç ñ©…ÈW®\yüø1ŸŸ={¦­E„=mr‰ÛÇ÷ZV¹^@ÕBj|MÜJ~Åqä§µµ59¯ÚØ×?ìÁN ©ŽáÓüæ›o¸žÛ···=™ˆWh‘Œ–*)$ øƒÏ(øŒ¢àóœð)2ÕYÓ@®®<æs=¿:Ö9ù ÓËmµªªNÉÆõÔè«W¯j²î |Š ¾^Þ¿ßÞ-¤¬3ÌyàÄ³ŠƒÏ(øŒ¢àsUoOóÆûÉ|Ã÷ü:¸Q¢œâŽ':öØ1é”uîÚ$ ü¤”Î-sƒÏà3Š¢à3 >£Ô®( >£à3J튢à3<øŒR»¢(øL>£à3ŠbaSÁƒÏ(øŒ¢(øLæŸQjWŸQð¥vEQð™ |F©]Q|¦‚ŸQðE±°©àÁgô ásssóßQ-O·oßþxð™ þA27øüÄõã?þ#Š¢Õè·ß~Kÿl37øü,ú}EËÖÏ?ÿœ þ9gnðEQE§VðEQEÁgEQŸQEQ|FQEQðEQEÁgEQEÁgEQŸQEQ|FQEQðEQEÁgEQEÁgEQ-Kÿ¹'I`šÓIEND®B`‚neutron-12.1.1/doc/source/contributor/testing/db_transient_failure_injection.rst0000664000175000017500000000307513553660046030400 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Transient DB Failure Injection ============================== Neutron has a service plugin to inject random delays and Deadlock exceptions into normal Neutron operations. The service plugin is called 'Loki' and is located under neutron.services.loki.loki_plugin. To enable the plugin, just add 'loki' to the list of service_plugins in your neutron-server neutron.conf file. The plugin will inject a Deadlock exception on database flushes with a 1/50 probability and a delay of 1 second with a 1/200 probability when SQLAlchemy objects are loaded into the persistent state from the DB. The goal is to ensure the code is tolerant of these transient delays/failures that will be experienced in busy production (and Galera) systems. neutron-12.1.1/doc/source/contributor/testing/template_model_sync_test.rst0000664000175000017500000001231213553660047027234 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Template for ModelMigrationSync for external repos ================================================== This section contains a template for a test which checks that the Python models for database tables are synchronized with the alembic migrations that create the database schema. This test should be implemented in all driver/plugin repositories that were split out from Neutron. What does the test do? ---------------------- This test compares models with the result of existing migrations. It is based on `ModelsMigrationsSync `_ which is provided by oslo.db and was adapted for Neutron. It compares core Neutron models and vendor specific models with migrations from Neutron core and migrations from the driver/plugin repo. This test is functional - it runs against MySQL and PostgreSQL dialects. The detailed description of this test can be found in Neutron Database Layer section - :ref:`testing-database-migrations`. Steps for implementing the test ------------------------------- 1. Import all models in one place ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Create a module ``networking_foo/db/models/head.py`` with the following content: :: from neutron_lib.db import model_base from networking_foo import models # noqa # Alternatively, import separate modules here if the models are not in one # models.py file def get_metadata(): return model_base.BASEV2.metadata 2. Implement the test module ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The test uses external.py from Neutron. This file contains lists of table names, which were moved out of Neutron: :: VPNAAS_TABLES = [...] LBAAS_TABLES = [...] FWAAS_TABLES = [...] # Arista ML2 driver Models moved to openstack/networking-arista REPO_ARISTA_TABLES = [...] # Models moved to openstack/networking-cisco REPO_CISCO_TABLES = [...] ... TABLES = (FWAAS_TABLES + LBAAS_TABLES + VPNAAS_TABLES + ... + REPO_ARISTA_TABLES + REPO_CISCO_TABLES) Also the test uses **VERSION_TABLE**, it is the name of table in database which contains revision id of head migration. It is preferred to keep this variable in ``networking_foo/db/migration/alembic_migrations/__init__.py`` so it will be easy to use in test. Create a module ``networking_foo/tests/functional/db/test_migrations.py`` with the following content: :: from oslo_config import cfg from neutron.db.migration.alembic_migrations import external from neutron.db.migration import cli as migration from neutron.tests.functional.db import test_migrations from neutron.tests.unit import testlib_api from networking_foo.db.migration import alembic_migrations from networking_foo.db.models import head # EXTERNAL_TABLES should contain all names of tables that are not related to # current repo. EXTERNAL_TABLES = set(external.TABLES) - set(external.REPO_FOO_TABLES) class _TestModelsMigrationsFoo(test_migrations._TestModelsMigrations): def db_sync(self, engine): cfg.CONF.set_override('connection', engine.url, group='database') for conf in migration.get_alembic_configs(): self.alembic_config = conf self.alembic_config.neutron_config = cfg.CONF migration.do_alembic_command(conf, 'upgrade', 'heads') def get_metadata(self): return head.get_metadata() def include_object(self, object_, name, type_, reflected, compare_to): if type_ == 'table' and (name == 'alembic' or name == alembic_migrations.VERSION_TABLE or name in EXTERNAL_TABLES): return False else: return True class TestModelsMigrationsMysql(testlib_api.MySQLTestCaseMixin, _TestModelsMigrationsFoo, testlib_api.SqlTestCaseLight): pass class TestModelsMigrationsPsql(testlib_api.PostgreSQLTestCaseMixin, _TestModelsMigrationsFoo, testlib_api.SqlTestCaseLight): pass 3. Add functional requirements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A separate file ``networking_foo/tests/functional/requirements.txt`` should be created containing the following requirements that are needed for successful test execution. :: psutil>=3.2.2 # BSD psycopg2 PyMySQL>=0.6.2 # MIT License Example implementation `in VPNaaS `_ neutron-12.1.1/doc/source/contributor/testing/testing.rst0000664000175000017500000000206313553660046023624 0ustar zuulzuul00000000000000.. Copyright 2010-2011 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) .. include:: ../../../../TESTING.rst neutron-12.1.1/doc/source/contributor/testing/index.rst0000664000175000017500000000224713553660047023263 0ustar zuulzuul00000000000000.. Copyright 2010-2011 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ======= Testing ======= .. toctree:: :maxdepth: 2 testing fullstack coverage template_model_sync_test db_transient_failure_injection neutron-12.1.1/doc/source/contributor/testing/coverage.rst0000664000175000017500000001506413553660046023747 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Test Coverage ============= The intention is to track merged features or areas of code that lack certain types of tests. This document may be used both by developers that want to contribute tests, and operators that are considering adopting a feature. Coverage -------- Note that while both API and scenario tests target a deployed OpenStack cloud, API tests are under the Neutron tree and scenario tests are under the Tempest tree. It is the expectation that API changes involve API tests, agent features or modifications involve functional tests, and Neutron-wide features involve fullstack or scenario tests as appropriate. The table references tests that explicitly target a feature, and not a job that is configured to run against a specific backend (Thereby testing it implicitly). So, for example, while the Linux bridge agent has a job that runs the API and scenario tests with the Linux bridge agent configured, it does not have functional tests that target the agent explicitly. The 'gate' column is about running API/scenario tests with Neutron configured in a certain way, such as what L2 agent to use or what type of routers to create. * V - Merged * Blank - Not applicable * X - Absent or lacking * Patch number - Currently in review * A name - That person has committed to work on an item * Implicit - The code is executed, yet no assertions are made +------------------------+------------+------------+------------+------------+------------+------------+ | Area | Unit | Functional | API | Fullstack | Scenario | Gate | +========================+============+============+============+============+============+============+ | DVR | V | L3-V OVS-X | V | X | X | V | +------------------------+------------+------------+------------+------------+------------+------------+ | L3 HA | V | V | X | 286087 | X | X | +------------------------+------------+------------+------------+------------+------------+------------+ | L2pop | V | X | | Implicit | | | +------------------------+------------+------------+------------+------------+------------+------------+ | DHCP HA | V | | | amuller | | | +------------------------+------------+------------+------------+------------+------------+------------+ | OVS ARP responder | V | X* | | Implicit | | | +------------------------+------------+------------+------------+------------+------------+------------+ | OVS agent | V | V | | V | | V | +------------------------+------------+------------+------------+------------+------------+------------+ | Linux Bridge agent | V | X | | V | | V | +------------------------+------------+------------+------------+------------+------------+------------+ | Metering | V | X | V | X | | | +------------------------+------------+------------+------------+------------+------------+------------+ | DHCP agent | V | V | | amuller | | V | +------------------------+------------+------------+------------+------------+------------+------------+ | rpc_workers | | | | | | X | +------------------------+------------+------------+------------+------------+------------+------------+ | Reference ipam driver | V | | | | | X | +------------------------+------------+------------+------------+------------+------------+------------+ | MTU advertisement | V | | | X | | | +------------------------+------------+------------+------------+------------+------------+------------+ | VLAN transparency | V | | X | X | | | +------------------------+------------+------------+------------+------------+------------+------------+ | Prefix delegation | V | X | | X | | | +------------------------+------------+------------+------------+------------+------------+------------+ * Prefix delegation doesn't have functional tests for the dibbler and pd layers, nor for the L3 agent changes. This has been an area of repeated regressions. * The functional job now compiles OVS 2.5 from source, enabling testing features that we previously could not. Missing Infrastructure ---------------------- The following section details missing test *types*. If you want to pick up an action item, please contact amuller for more context and guidance. * The Neutron team would like Rally to persist results over a window of time, graph and visualize this data, so that reviewers could compare average runs against a proposed patch. * It's possible to test RPC methods via the unit tests infrastructure. This was proposed in patch 162811. The goal is provide developers a light weight way to rapidly run tests that target the RPC layer, so that a patch that modifies an RPC method's signature could be verified quickly and locally. * Neutron currently runs a 'partial-grenade' job that verifies that an OVS version from the latest stable release works with neutron-server from master. We would like to expand this to DHCP and L3 agents as well. neutron-12.1.1/doc/source/contributor/testing/fullstack.rst0000664000175000017500000000253513553660046024143 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Full Stack Testing ================== Goals ----- * Stabilize the job: - Fix L3 HA failure - Look in to non-deterministic failures when adding a large amount of tests (Possibly bug 1486199). - Switch to kill signal 15 to terminate agents (Bug 1487548). * Convert the L3 HA failover functional test to a full stack test * Write DVR tests * Write additional L3 HA tests * Write a test that validates DVR + L3 HA integration after https://bugs.launchpad.net/neutron/+bug/1365473 is fixed. neutron-12.1.1/doc/source/contributor/modules.rst0000664000175000017500000000222213553660046022137 0ustar zuulzuul00000000000000.. Copyright 2010-2011 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Module Reference ================ .. toctree:: :maxdepth: 3 .. todo:: Add in all the big modules as automodule indexes. neutron-12.1.1/doc/source/contributor/index.rst0000664000175000017500000000434213553660047021604 0ustar zuulzuul00000000000000.. Copyright 2010-2011 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ================= Contributor Guide ================= This document describes Neutron for contributors of the project, and assumes that you are already familiar with Neutron from an :doc:`end-user perspective `. Neutron Policies ---------------- .. toctree:: :maxdepth: 2 policies/index Neutron Stadium --------------- .. toctree:: :maxdepth: 2 stadium/index Developer Guide --------------- In the Developer Guide, you will find information on Neutron's lower level programming APIs. There are sections that cover the core pieces of Neutron, including its database, message queue, and scheduler components. There are also subsections that describe specific plugins inside Neutron. Finally, the developer guide includes information about Neutron testing infrastructure. .. toctree:: :maxdepth: 2 effective_neutron development_environment contribute neutron_api client_command_extensions alembic_migrations testing/index Neutron Internals ----------------- .. toctree:: :maxdepth: 2 internals/index modules Dashboards ---------- There is a collection of dashboards to help developers and reviewers located here. .. toctree:: :maxdepth: 2 dashboards/index neutron-12.1.1/doc/source/contributor/development_environment.rst0000664000175000017500000000447313553660047025450 0ustar zuulzuul00000000000000.. Copyright 2010-2013 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Setting Up a Development Environment ==================================== This page describes how to setup a working Python development environment that can be used in developing Neutron on Ubuntu, Fedora or Mac OS X. These instructions assume you're already familiar with Git and Gerrit, which is a code repository mirror and code review toolset , however if you aren't please see `this Git tutorial`_ for an introduction to using Git and `this guide`_ for a tutorial on using Gerrit and Git for code contribution to OpenStack projects. .. _this Git tutorial: http://git-scm.com/book/en/Getting-Started .. _this guide: http://docs.openstack.org/infra/manual/developers.html#development-workflow Following these instructions will allow you to run the Neutron unit tests. If you want to be able to run Neutron in a full OpenStack environment, you can use the excellent `DevStack`_ project to do so. There is a wiki page that describes `setting up Neutron using DevStack`_. .. _DevStack: https://git.openstack.org/cgit/openstack-dev/devstack .. _setting up Neutron using Devstack: https://wiki.openstack.org/wiki/NeutronDevstack Getting the code ---------------- Grab the code:: git clone https://git.openstack.org/openstack/neutron.git cd neutron Testing Neutron --------------- See :ref:`testing_neutron`. neutron-12.1.1/doc/source/contributor/alembic_migrations.rst0000664000175000017500000004736613553660047024342 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) .. _alembic_migrations: Alembic Migrations ================== Introduction ------------ The migrations in the alembic/versions contain the changes needed to migrate from older Neutron releases to newer versions. A migration occurs by executing a script that details the changes needed to upgrade the database. The migration scripts are ordered so that multiple scripts can run sequentially to update the database. The Migration Wrapper --------------------- The scripts are executed by Neutron's migration wrapper ``neutron-db-manage`` which uses the Alembic library to manage the migration. Pass the ``--help`` option to the wrapper for usage information. The wrapper takes some options followed by some commands:: neutron-db-manage The wrapper needs to be provided with the database connection string, which is usually provided in the ``neutron.conf`` configuration file in an installation. The wrapper automatically reads from ``/etc/neutron/neutron.conf`` if it is present. If the configuration is in a different location:: neutron-db-manage --config-file /path/to/neutron.conf Multiple ``--config-file`` options can be passed if needed. Instead of reading the DB connection from the configuration file(s) the ``--database-connection`` option can be used:: neutron-db-manage --database-connection mysql+pymysql://root:secret@127.0.0.1/neutron?charset=utf8 The ``branches``, ``current``, and ``history`` commands all accept a ``--verbose`` option, which, when passed, will instruct ``neutron-db-manage`` to display more verbose output for the specified command:: neutron-db-manage current --verbose For some commands the wrapper needs to know the entrypoint of the core plugin for the installation. This can be read from the configuration file(s) or specified using the ``--core_plugin`` option:: neutron-db-manage --core_plugin neutron.plugins.ml2.plugin.Ml2Plugin When giving examples below of using the wrapper the options will not be shown. It is assumed you will use the options that you need for your environment. For new deployments you will start with an empty database. You then upgrade to the latest database version via:: neutron-db-manage upgrade heads For existing deployments the database will already be at some version. To check the current database version:: neutron-db-manage current After installing a new version of Neutron server, upgrading the database is the same command:: neutron-db-manage upgrade heads To create a script to run the migration offline:: neutron-db-manage upgrade heads --sql To run the offline migration between specific migration versions:: neutron-db-manage upgrade : --sql Upgrade the database incrementally:: neutron-db-manage upgrade --delta <# of revs> **NOTE:** Database downgrade is not supported. Migration Branches ------------------ Neutron makes use of alembic branches for two purposes. 1. Independent Sub-Project Tables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Various `sub-projects <../stadium/sub_projects.html>`_ can be installed with Neutron. Each sub-project registers its own alembic branch which is responsible for migrating the schemas of the tables owned by the sub-project. The neutron-db-manage script detects which sub-projects have been installed by enumerating the ``neutron.db.alembic_migrations`` entrypoints. For more details see the `Entry Points section of Contributing extensions to Neutron `_. The neutron-db-manage script runs the given alembic command against all installed sub-projects. (An exception is the ``revision`` command, which is discussed in the `Developers`_ section below.) 2. Offline/Online Migrations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Since Liberty, Neutron maintains two parallel alembic migration branches. The first one, called 'expand', is used to store expansion-only migration rules. Those rules are strictly additive and can be applied while neutron-server is running. Examples of additive database schema changes are: creating a new table, adding a new table column, adding a new index, etc. The second branch, called 'contract', is used to store those migration rules that are not safe to apply while neutron-server is running. Those include: column or table removal, moving data from one part of the database into another (renaming a column, transforming single table into multiple, etc.), introducing or modifying constraints, etc. The intent of the split is to allow invoking those safe migrations from 'expand' branch while neutron-server is running, reducing downtime needed to upgrade the service. For more details, see the `Expand and Contract Scripts`_ section below. Developers ---------- A database migration script is required when you submit a change to Neutron or a sub-project that alters the database model definition. The migration script is a special python file that includes code to upgrade the database to match the changes in the model definition. Alembic will execute these scripts in order to provide a linear migration path between revisions. The neutron-db-manage command can be used to generate migration scripts for you to complete. The operations in the template are those supported by the Alembic migration library. .. _neutron-db-manage-without-devstack: Running neutron-db-manage without devstack ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When, as a developer, you want to work with the Neutron DB schema and alembic migrations only, it can be rather tedious to rely on devstack just to get an up-to-date neutron-db-manage installed. This section describes how to work on the schema and migration scripts with just the unit test virtualenv and mysql. You can also operate on a separate test database so you don't mess up the installed Neutron database. Setting up the environment ++++++++++++++++++++++++++ Install mysql service ''''''''''''''''''''' This only needs to be done once since it is a system install. If you have run devstack on your system before, then the mysql service is already installed and you can skip this step. Mysql must be configured as installed by devstack, and the following script accomplishes this without actually running devstack:: INSTALL_MYSQL_ONLY=True ./tools/configure_for_func_testing.sh ../devstack Run this from the root of the neutron repo. It assumes an up-to-date clone of the devstack repo is in ``../devstack``. Note that you must know the mysql root password. It is derived from (in order of precedence): - ``$MYSQL_PASSWORD`` in your environment - ``$MYSQL_PASSWORD`` in ``../devstack/local.conf`` - ``$MYSQL_PASSWORD`` in ``../devstack/localrc`` - default of 'secretmysql' from ``tools/configure_for_func_testing.sh`` Work on a test database ''''''''''''''''''''''' Rather than using the neutron database when working on schema and alembic migration script changes, we can work on a test database. In the examples below, we use a database named ``testdb``. To create the database:: mysql -e "create database testdb;" You will often need to clear it to re-run operations from a blank database:: mysql -e "drop database testdb; create database testdb;" To work on the test database instead of the neutron database, point to it with the ``--database-connection`` option:: neutron-db-manage --database-connection mysql+pymysql://root:secretmysql@127.0.0.1/testdb?charset=utf8 You may find it convenient to set up an alias (in your .bashrc) for this:: alias test-db-manage='neutron-db-manage --database-connection mysql+pymysql://root:secretmysql@127.0.0.1/testdb?charset=utf8' Create and activate the virtualenv '''''''''''''''''''''''''''''''''' From the root of the neutron (or sub-project) repo directory, run:: tox --notest -r -e py27 source .tox/py27/bin/activate Now you can use the ``test-db-manage`` alias in place of ``neutron-db-manage`` in the script auto-generation instructions below. When you are done, exit the virtualenv:: deactivate Script Auto-generation ~~~~~~~~~~~~~~~~~~~~~~ This section describes how to auto-generate an alembic migration script for a model change. You may either use the system installed devstack environment, or a virtualenv + testdb environment as described in :ref:`neutron-db-manage-without-devstack`. Stop the neutron service. Work from the base directory of the neutron (or sub-project) repo. Check out the master branch and do ``git pull`` to ensure it is fully up to date. Check out your development branch and rebase to master. **NOTE:** Make sure you have not updated the ``CONTRACT_HEAD`` or ``EXPAND_HEAD`` yet at this point. Start with an empty database and upgrade to heads:: mysql -e "drop database neutron; create database neutron;" neutron-db-manage upgrade heads The database schema is now created without your model changes. The alembic ``revision --autogenerate`` command will look for differences between the schema generated by the upgrade command and the schema defined by the models, including your model updates:: neutron-db-manage revision -m "description of revision" --autogenerate This generates a prepopulated template with the changes needed to match the database state with the models. You should inspect the autogenerated template to ensure that the proper models have been altered. When running the above command you will probably get the following error message:: Multiple heads are present; please specify the head revision on which the new revision should be based, or perform a merge. This is alembic telling you that it does not know which branch (contract or expand) to generate the revision for. You must decide, based on whether you are doing contracting or expanding changes to the schema, and provide either the ``--contract`` or ``--expand`` option. If you have both types of changes, you must run the command twice, once with each option, and then manually edit the generated revision scripts to separate the migration operations. In rare circumstances, you may want to start with an empty migration template and manually author the changes necessary for an upgrade. You can create a blank file for a branch via:: neutron-db-manage revision -m "description of revision" --expand neutron-db-manage revision -m "description of revision" --contract **NOTE:** If you use above command you should check that migration is created in a directory that is named as current release. If not, please raise the issue with the development team (IRC, mailing list, launchpad bug). **NOTE:** The "description of revision" text should be a simple English sentence. The first 30 characters of the description will be used in the file name for the script, with underscores substituted for spaces. If the truncation occurs at an awkward point in the description, you can modify the script file name manually before committing. The timeline on each alembic branch should remain linear and not interleave with other branches, so that there is a clear path when upgrading. To verify that alembic branches maintain linear timelines, you can run this command:: neutron-db-manage check_migration If this command reports an error, you can troubleshoot by showing the migration timelines using the ``history`` command:: neutron-db-manage history Expand and Contract Scripts ~~~~~~~~~~~~~~~~~~~~~~~~~~~ The obsolete "branchless" design of a migration script included that it indicates a specific "version" of the schema, and includes directives that apply all necessary changes to the database at once. If we look for example at the script ``2d2a8a565438_hierarchical_binding.py``, we will see:: # .../alembic_migrations/versions/2d2a8a565438_hierarchical_binding.py def upgrade(): # .. inspection code ... op.create_table( 'ml2_port_binding_levels', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('host', sa.String(length=255), nullable=False), # ... more columns ... ) for table in port_binding_tables: op.execute(( "INSERT INTO ml2_port_binding_levels " "SELECT port_id, host, 0 AS level, driver, segment AS segment_id " "FROM %s " "WHERE host <> '' " "AND driver <> '';" ) % table) op.drop_constraint(fk_name_dvr[0], 'ml2_dvr_port_bindings', 'foreignkey') op.drop_column('ml2_dvr_port_bindings', 'cap_port_filter') op.drop_column('ml2_dvr_port_bindings', 'segment') op.drop_column('ml2_dvr_port_bindings', 'driver') # ... more DROP instructions ... The above script contains directives that are both under the "expand" and "contract" categories, as well as some data migrations. the ``op.create_table`` directive is an "expand"; it may be run safely while the old version of the application still runs, as the old code simply doesn't look for this table. The ``op.drop_constraint`` and ``op.drop_column`` directives are "contract" directives (the drop column more so than the drop constraint); running at least the ``op.drop_column`` directives means that the old version of the application will fail, as it will attempt to access these columns which no longer exist. The data migrations in this script are adding new rows to the newly added ``ml2_port_binding_levels`` table. Under the new migration script directory structure, the above script would be stated as two scripts; an "expand" and a "contract" script:: # expansion operations # .../alembic_migrations/versions/liberty/expand/2bde560fc638_hierarchical_binding.py def upgrade(): op.create_table( 'ml2_port_binding_levels', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('host', sa.String(length=255), nullable=False), # ... more columns ... ) # contraction operations # .../alembic_migrations/versions/liberty/contract/4405aedc050e_hierarchical_binding.py def upgrade(): for table in port_binding_tables: op.execute(( "INSERT INTO ml2_port_binding_levels " "SELECT port_id, host, 0 AS level, driver, segment AS segment_id " "FROM %s " "WHERE host <> '' " "AND driver <> '';" ) % table) op.drop_constraint(fk_name_dvr[0], 'ml2_dvr_port_bindings', 'foreignkey') op.drop_column('ml2_dvr_port_bindings', 'cap_port_filter') op.drop_column('ml2_dvr_port_bindings', 'segment') op.drop_column('ml2_dvr_port_bindings', 'driver') # ... more DROP instructions ... The two scripts would be present in different subdirectories and also part of entirely separate versioning streams. The "expand" operations are in the "expand" script, and the "contract" operations are in the "contract" script. For the time being, data migration rules also belong to contract branch. There is expectation that eventually live data migrations move into middleware that will be aware about different database schema elements to converge on, but Neutron is still not there. Scripts that contain only expansion or contraction rules do not require a split into two parts. If a contraction script depends on a script from expansion stream, the following directive should be added in the contraction script:: depends_on = ('',) Expand and Contract Branch Exceptions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In some cases, we have to have "expand" operations in contract migrations. For example, table 'networksegments' was renamed in contract migration, so all operations with this table are required to be in contract branch as well. For such cases, we use the ``contract_creation_exceptions`` that should be implemented as part of such migrations. This is needed to get functional tests pass. Usage:: def contract_creation_exceptions(): """Docstring should explain why we allow such exception for contract branch. """ return { sqlalchemy_obj_type: ['name'] # For example: sa.Column: ['subnets.segment_id'] } HEAD files for conflict management ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In directory ``neutron/db/migration/alembic_migrations/versions`` there are two files, ``CONTRACT_HEAD`` and ``EXPAND_HEAD``. These files contain the ID of the head revision in each branch. The purpose of these files is to validate the revision timelines and prevent non-linear changes from entering the merge queue. When you create a new migration script by neutron-db-manage these files will be updated automatically. But if another migration script is merged while your change is under review, you will need to resolve the conflict manually by changing the ``down_revision`` in your migration script. Applying database migration rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To apply just expansion rules, execute:: neutron-db-manage upgrade --expand After the first step is done, you can stop neutron-server, apply remaining non-expansive migration rules, if any:: neutron-db-manage upgrade --contract and finally, start your neutron-server again. If you have multiple neutron-server instances in your cloud, and there are pending contract scripts not applied to the database, full shutdown of all those services is required before 'upgrade --contract' is executed. You can determine whether there are any pending contract scripts by checking the message returned from the following command:: neutron-db-manage has_offline_migrations If you are not interested in applying safe migration rules while the service is running, you can still upgrade database the old way, by stopping the service, and then applying all available rules:: neutron-db-manage upgrade head[s] It will apply all the rules from both the expand and the contract branches, in proper order. Tagging milestone revisions ~~~~~~~~~~~~~~~~~~~~~~~~~~~ When named release (liberty, mitaka, etc.) is done for neutron or a sub-project, the alembic revision scripts at the head of each branch for that release must be tagged. This is referred to as a milestone revision tag. For example, `here `_ is a patch that tags the liberty milestone revisions for the neutron-fwaas sub-project. Note that each branch (expand and contract) is tagged. Tagging milestones allows neutron-db-manage to upgrade the schema to a milestone release, e.g.:: neutron-db-manage upgrade liberty Generation of comparable metadata with current database schema ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Directory ``neutron/db/migration/models`` contains module ``head.py``, which provides all database models at current HEAD. Its purpose is to create comparable metadata with the current database schema. The database schema is generated by alembic migration scripts. The models must match, and this is verified by a model-migration sync test in Neutron's functional test suite. That test requires all modules containing DB models to be imported by head.py in order to make a complete comparison. When adding new database models, developers must update this module, otherwise the change will fail to merge. neutron-12.1.1/doc/source/configuration/0000775000175000017500000000000013553660156020236 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/configuration/samples/0000775000175000017500000000000013553660156021702 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/configuration/samples/linuxbridge-agent.rst0000664000175000017500000000045013553660046026041 0ustar zuulzuul00000000000000============================ Sample linuxbridge_agent.ini ============================ This sample configuration can also be viewed in `the raw format <../../_static/config-samples/linuxbridge_agent.conf.sample>`_. .. literalinclude:: ../../_static/config-samples/linuxbridge_agent.conf.sample neutron-12.1.1/doc/source/configuration/samples/neutron.rst0000664000175000017500000000037113553660046024125 0ustar zuulzuul00000000000000=================== Sample neutron.conf =================== This sample configuration can also be viewed in `the raw format <../../_static/config-samples/neutron.conf.sample>`_. .. literalinclude:: ../../_static/config-samples/neutron.conf.sample neutron-12.1.1/doc/source/configuration/samples/macvtap-agent.rst0000664000175000017500000000042413553660046025161 0ustar zuulzuul00000000000000======================== Sample macvtap_agent.ini ======================== This sample configuration can also be viewed in `the raw format <../../_static/config-samples/macvtap_agent.conf.sample>`_. .. literalinclude:: ../../_static/config-samples/macvtap_agent.conf.sample neutron-12.1.1/doc/source/configuration/samples/openvswitch-agent.rst0000664000175000017500000000045013553660046026076 0ustar zuulzuul00000000000000============================ Sample openvswitch_agent.ini ============================ This sample configuration can also be viewed in `the raw format <../../_static/config-samples/openvswitch_agent.conf.sample>`_. .. literalinclude:: ../../_static/config-samples/openvswitch_agent.conf.sample neutron-12.1.1/doc/source/configuration/samples/metadata-agent.rst0000664000175000017500000000043113553660046025304 0ustar zuulzuul00000000000000========================= Sample metadata_agent.ini ========================= This sample configuration can also be viewed in `the raw format <../../_static/config-samples/metadata_agent.conf.sample>`_. .. literalinclude:: ../../_static/config-samples/metadata_agent.conf.sample neutron-12.1.1/doc/source/configuration/samples/dhcp-agent.rst0000664000175000017500000000040513553660046024443 0ustar zuulzuul00000000000000===================== Sample dhcp_agent.ini ===================== This sample configuration can also be viewed in `the raw format <../../_static/config-samples/dhcp_agent.conf.sample>`_. .. literalinclude:: ../../_static/config-samples/dhcp_agent.conf.sample neutron-12.1.1/doc/source/configuration/samples/l3-agent.rst0000664000175000017500000000037313553660046024047 0ustar zuulzuul00000000000000=================== Sample l3_agent.ini =================== This sample configuration can also be viewed in `the raw format <../../_static/config-samples/l3_agent.conf.sample>`_. .. literalinclude:: ../../_static/config-samples/l3_agent.conf.sample neutron-12.1.1/doc/source/configuration/samples/ml2-conf.rst0000664000175000017500000000037313553660046024052 0ustar zuulzuul00000000000000=================== Sample ml2_conf.ini =================== This sample configuration can also be viewed in `the raw format <../../_static/config-samples/ml2_conf.conf.sample>`_. .. literalinclude:: ../../_static/config-samples/ml2_conf.conf.sample neutron-12.1.1/doc/source/configuration/samples/metering-agent.rst0000664000175000017500000000043113553660046025336 0ustar zuulzuul00000000000000========================= Sample metering_agent.ini ========================= This sample configuration can also be viewed in `the raw format <../../_static/config-samples/metering_agent.conf.sample>`_. .. literalinclude:: ../../_static/config-samples/metering_agent.conf.sample neutron-12.1.1/doc/source/configuration/samples/sriov-agent.rst0000664000175000017500000000041213553660046024665 0ustar zuulzuul00000000000000====================== Sample sriov_agent.ini ====================== This sample configuration can also be viewed in `the raw format <../../_static/config-samples/sriov_agent.conf.sample>`_. .. literalinclude:: ../../_static/config-samples/sriov_agent.conf.sample neutron-12.1.1/doc/source/configuration/linuxbridge-agent.rst0000664000175000017500000000022613553660046024376 0ustar zuulzuul00000000000000===================== linuxbridge_agent.ini ===================== .. show-options:: :config-file: etc/oslo-config-generator/linuxbridge_agent.ini neutron-12.1.1/doc/source/configuration/neutron.rst0000664000175000017500000000016213553660046022457 0ustar zuulzuul00000000000000============ neutron.conf ============ .. show-options:: :config-file: etc/oslo-config-generator/neutron.conf neutron-12.1.1/doc/source/configuration/macvtap-agent.rst0000664000175000017500000000020613553660046023513 0ustar zuulzuul00000000000000================= macvtap_agent.ini ================= .. show-options:: :config-file: etc/oslo-config-generator/macvtap_agent.ini neutron-12.1.1/doc/source/configuration/openvswitch-agent.rst0000664000175000017500000000022613553660046024433 0ustar zuulzuul00000000000000===================== openvswitch_agent.ini ===================== .. show-options:: :config-file: etc/oslo-config-generator/openvswitch_agent.ini neutron-12.1.1/doc/source/configuration/metadata-agent.rst0000664000175000017500000000021213553660046023635 0ustar zuulzuul00000000000000================== metadata_agent.ini ================== .. show-options:: :config-file: etc/oslo-config-generator/metadata_agent.ini neutron-12.1.1/doc/source/configuration/dhcp-agent.rst0000664000175000017500000000017213553660046023000 0ustar zuulzuul00000000000000============== dhcp_agent.ini ============== .. show-options:: :config-file: etc/oslo-config-generator/dhcp_agent.ini neutron-12.1.1/doc/source/configuration/l3-agent.rst0000664000175000017500000000017113553660046022377 0ustar zuulzuul00000000000000============ l3_agent.ini ============ .. show-options:: neutron.az.agent neutron.base.agent neutron.l3.agent neutron-12.1.1/doc/source/configuration/index.rst0000664000175000017500000000227013553660047022077 0ustar zuulzuul00000000000000.. _configuring: ============================= Neutron Configuration Options ============================= This section provides a list of all configuration options for various neutron services. These are auto-generated from neutron code when this documentation is built. Configuration filenames used below are filenames usually used, but there is no restriction on configuration filename in neutron and you can use arbitrary file names. Configuration Reference ----------------------- .. toctree:: :maxdepth: 1 neutron.rst .. toctree:: :maxdepth: 1 ml2-conf.rst linuxbridge-agent.rst macvtap-agent.rst openvswitch-agent.rst sriov-agent.rst .. toctree:: :maxdepth: 1 dhcp-agent.rst l3-agent.rst metadata-agent.rst metering-agent.rst Sample Configuration Files -------------------------- .. toctree:: :maxdepth: 1 samples/neutron.rst .. toctree:: :maxdepth: 1 samples/ml2-conf.rst samples/linuxbridge-agent.rst samples/macvtap-agent.rst samples/openvswitch-agent.rst samples/sriov-agent.rst .. toctree:: :maxdepth: 1 samples/dhcp-agent.rst samples/l3-agent.rst samples/metadata-agent.rst samples/metering-agent.rst neutron-12.1.1/doc/source/configuration/ml2-conf.rst0000664000175000017500000000016213553660046022402 0ustar zuulzuul00000000000000============ ml2_conf.ini ============ .. show-options:: :config-file: etc/oslo-config-generator/ml2_conf.ini neutron-12.1.1/doc/source/configuration/metering-agent.rst0000664000175000017500000000021213553660046023667 0ustar zuulzuul00000000000000================== metering_agent.ini ================== .. show-options:: :config-file: etc/oslo-config-generator/metering_agent.ini neutron-12.1.1/doc/source/configuration/sriov-agent.rst0000664000175000017500000000017613553660046023230 0ustar zuulzuul00000000000000=============== sriov_agent.ini =============== .. show-options:: :config-file: etc/oslo-config-generator/sriov_agent.ini neutron-12.1.1/doc/source/ext/0000775000175000017500000000000013553660156016167 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/ext/support_matrix.py0000664000175000017500000004064613553660047021652 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ This provides a sphinx extension able to render the source/general_feature_support_matrix.ini file into the developer documentation. It is used via a single directive in the .rst file .. support_matrix:: """ import re from docutils import nodes from docutils.parsers import rst from six.moves import configparser RE_PATTERN = re.compile("[^a-zA-Z0-9_]") class SupportMatrix(object): """Represents the entire support matrix for Neutron drivers""" def __init__(self): self.features = [] self.targets = {} class SupportMatrixFeature(object): STATUS_IMMATURE = "immature" STATUS_MATURE = "mature" STATUS_REQUIRED = "required" STATUS_DEPRECATED = "deprecated" STATUS_ALL = [STATUS_IMMATURE, STATUS_MATURE, STATUS_REQUIRED, STATUS_DEPRECATED] def __init__(self, key, title, status=STATUS_IMMATURE, group=None, notes=None, cli=(), api=None): self.key = key self.title = title self.status = status self.group = group self.notes = notes self.cli = cli self.api = api self.implementations = {} class SupportMatrixImplementation(object): STATUS_COMPLETE = "complete" STATUS_PARTIAL = "partial" STATUS_INCOMPLETE = "incomplete" STATUS_UNKNOWN = "unknown" STATUS_ALL = [STATUS_COMPLETE, STATUS_INCOMPLETE, STATUS_PARTIAL, STATUS_UNKNOWN] def __init__(self, status=STATUS_INCOMPLETE, notes=None): self.status = status self.notes = notes STATUS_DICT = { SupportMatrixImplementation.STATUS_COMPLETE: u"\u2714", SupportMatrixImplementation.STATUS_INCOMPLETE: u"\u2716", SupportMatrixImplementation.STATUS_PARTIAL: u"\u2714", SupportMatrixImplementation.STATUS_UNKNOWN: u"?" } class SupportMatrixTarget(object): def __init__(self, key, title, driver, plugin=None, architecture=None, api=None, link=None): """:param key: Unique identifier for plugin :param title: Human readable name for plugin :param driver: name of the driver :param plugin: optional name of plugin :param architecture: optional name of architecture """ self.api = api self.key = key self.title = title self.driver = driver self.plugin = plugin self.architecture = architecture self.link = link class SupportMatrixDirective(rst.Directive): # general_feature_support_matrix.ini is the arg required_arguments = 1 def run(self): matrix = self._load_support_matrix() return self._build_markup(matrix) def _load_support_matrix(self): """Reads the support-matrix.ini file and populates an instance of the SupportMatrix class with all the data. :returns: SupportMatrix instance """ cfg = configparser.SafeConfigParser() env = self.state.document.settings.env fname = self.arguments[0] rel_fpath, fpath = env.relfn2path(fname) with open(fpath) as fp: cfg.readfp(fp) # This ensures that the docs are rebuilt whenever the # .ini file changes env.note_dependency(rel_fpath) matrix = SupportMatrix() matrix.targets = self._get_targets(cfg) matrix.features = self._get_features(cfg, matrix.targets) return matrix def _get_targets(self, cfg): # The 'target.' sections are special - they list all the # backend drivers that this file records data for targets = {} for section in cfg.sections(): if not section.startswith("target."): continue key = cfg.get(section, "label") name = key.split("-") title = cfg.get(section, "title") link = cfg.get(section, "link") target = SupportMatrixTarget(key, title, *name, link=link) targets[key] = target return targets def _get_features(self, cfg, targets): # All sections except 'targets' describe some feature of # the Neutron backend driver. features = [] for section in cfg.sections(): if section.startswith("target."): continue if not cfg.has_option(section, "title"): raise Exception( "'title' field missing in '[%s]' section" % section) title = cfg.get(section, "title") status = SupportMatrixFeature.STATUS_IMMATURE if cfg.has_option(section, "status"): # The value is a string "status(group)" where # the 'group' part is optional status = cfg.get(section, "status") offset = status.find("(") group = None if offset != -1: group = status[offset + 1:-1] status = status[0:offset] if status not in SupportMatrixFeature.STATUS_ALL: raise Exception( "'status' field value '%s' in ['%s']" "section must be %s" % (status, section, ",".join(SupportMatrixFeature.STATUS_ALL))) cli = [] if cfg.has_option(section, "cli"): cli = cfg.get(section, "cli") api = None if cfg.has_option(section, "api"): api = cfg.get(section, "api") notes = None if cfg.has_option(section, "notes"): notes = cfg.get(section, "notes") feature = SupportMatrixFeature(section, title, status, group, notes, cli, api) # Now we've got the basic feature details, we must process # the backend driver implementation for each feature for item in cfg.options(section): network_notes = "networking-notes-" if not item.startswith("networking-"): continue if item not in targets: raise Exception( "networking-'%s' in '[%s]' not declared" % (item, section)) status = cfg.get(section, item) if status not in SupportMatrixImplementation.STATUS_ALL: raise Exception( "'%s' value '%s' in '[%s]' section must be %s" % (item, status, section, ",".join(SupportMatrixImplementation.STATUS_ALL))) notes_key = network_notes + item[len(network_notes):] notes = None if cfg.has_option(section, notes_key): notes = cfg.get(section, notes_key) target = targets[item] impl = SupportMatrixImplementation(status, notes) feature.implementations[target.key] = impl for key in targets: if key not in feature.implementations: raise Exception("'%s' missing in '[%s]' section" % (target.key, section)) features.append(feature) return features def _build_markup(self, matrix): """Constructs the docutils content for the support matrix """ content = [] self._build_summary(matrix, content) self._build_details(matrix, content) self._build_notes(content) return content def _build_summary(self, matrix, content): """Constructs the docutils content for the summary of the support matrix. The summary consists of a giant table, with one row for each feature, and a column for each backend driver. It provides an 'at a glance' summary of the status of each driver """ summary_title = nodes.subtitle(text="Summary") summary = nodes.table() cols = len(matrix.targets.keys()) cols += 2 summary_group = nodes.tgroup(cols=cols) summary_body = nodes.tbody() summary_head = nodes.thead() for i in range(cols): summary_group.append(nodes.colspec(colwidth=1)) summary_group.append(summary_head) summary_group.append(summary_body) summary.append(summary_group) content.append(summary_title) content.append(summary) # This sets up all the column headers - two fixed # columns for feature name & status header = nodes.row() blank = nodes.entry() blank.append(nodes.emphasis(text="Feature")) header.append(blank) blank = nodes.entry() blank.append(nodes.emphasis(text="Status")) header.append(blank) summary_head.append(header) # then one column for each backend driver impls = matrix.targets.keys() impls.sort() for key in impls: target = matrix.targets[key] implcol = nodes.entry() header.append(implcol) if target.link: uri = target.link target_ref = nodes.reference("", refuri=uri) target_txt = nodes.inline() implcol.append(target_txt) target_txt.append(target_ref) target_ref.append(nodes.strong(text=target.title)) else: implcol.append(nodes.strong(text=target.title)) # We now produce the body of the table, one row for # each feature to report on for feature in matrix.features: item = nodes.row() # the hyperlink target name linking to details feature_id = re.sub(RE_PATTERN, "_", feature.key) # first the fixed columns for title/status key_col = nodes.entry() item.append(key_col) key_ref = nodes.reference(refid=feature_id) key_txt = nodes.inline() key_col.append(key_txt) key_txt.append(key_ref) key_ref.append(nodes.strong(text=feature.title)) status_col = nodes.entry() item.append(status_col) status_col.append(nodes.inline( text=feature.status, classes=["sp_feature_" + feature.status])) # and then one column for each backend driver impls = matrix.targets.keys() impls.sort() for key in impls: target = matrix.targets[key] impl = feature.implementations[key] impl_col = nodes.entry() item.append(impl_col) key_id = re.sub(RE_PATTERN, "_", "{}_{}".format(feature.key, key)) impl_ref = nodes.reference(refid=key_id) impl_txt = nodes.inline() impl_col.append(impl_txt) impl_txt.append(impl_ref) status = STATUS_DICT.get(impl.status, "") impl_ref.append(nodes.literal( text=status, classes=["sp_impl_summary", "sp_impl_" + impl.status])) summary_body.append(item) def _build_details(self, matrix, content): """Constructs the docutils content for the details of the support matrix. """ details_title = nodes.subtitle(text="Details") details = nodes.bullet_list() content.append(details_title) content.append(details) # One list entry for each feature we're reporting on for feature in matrix.features: item = nodes.list_item() status = feature.status if feature.group is not None: status += "({})".format(feature.group) feature_id = re.sub(RE_PATTERN, "_", feature.key) # Highlight the feature title name item.append(nodes.strong(text=feature.title, ids=[feature_id])) # Add maturity status para = nodes.paragraph() para.append(nodes.strong(text="Status: {} ".format(status))) item.append(para) # If API Alias exists add it if feature.api is not None: para = nodes.paragraph() para.append( nodes.strong(text="API Alias: {} ".format(feature.api))) item.append(para) if feature.cli: item.append(self._create_cli_paragraph(feature)) if feature.notes is not None: item.append(self._create_notes_paragraph(feature.notes)) para_divers = nodes.paragraph() para_divers.append(nodes.strong(text="Driver Support:")) # A sub-list giving details of each backend driver target impls = nodes.bullet_list() for key in feature.implementations: target = matrix.targets[key] impl = feature.implementations[key] subitem = nodes.list_item() key_id = re.sub(RE_PATTERN, "_", "{}_{}".format(feature.key, key)) subitem += [ nodes.strong(text="{}: ".format(target.title)), nodes.literal(text=impl.status, classes=["sp_impl_{}".format(impl.status)], ids=[key_id]), ] if impl.notes is not None: subitem.append(self._create_notes_paragraph(impl.notes)) impls.append(subitem) para_divers.append(impls) item.append(para_divers) details.append(item) def _build_notes(self, content): """Constructs a list of notes content for the support matrix. This is generated as a bullet list. """ notes_title = nodes.subtitle(text="Notes:") notes = nodes.bullet_list() content.append(notes_title) content.append(notes) for note in ["This document is a continuous work in progress"]: item = nodes.list_item() item.append(nodes.strong(text=note)) notes.append(item) def _create_cli_paragraph(self, feature): """Create a paragraph which represents the CLI commands of the feature The paragraph will have a bullet list of CLI commands. """ para = nodes.paragraph() para.append(nodes.strong(text="CLI commands:")) commands = nodes.bullet_list() for c in feature.cli.split(";"): cli_command = nodes.list_item() cli_command += nodes.literal(text=c, classes=["sp_cli"]) commands.append(cli_command) para.append(commands) return para def _create_notes_paragraph(self, notes): """Constructs a paragraph which represents the implementation notes The paragraph consists of text and clickable URL nodes if links were given in the notes. """ para = nodes.paragraph() para.append(nodes.strong(text="Notes: ")) # links could start with http:// or https:// link_idxs = [m.start() for m in re.finditer('https?://', notes)] start_idx = 0 for link_idx in link_idxs: # assume the notes start with text (could be empty) para.append(nodes.inline(text=notes[start_idx:link_idx])) # create a URL node until the next text or the end of the notes link_end_idx = notes.find(" ", link_idx) if link_end_idx == -1: # In case the notes end with a link without a blank link_end_idx = len(notes) uri = notes[link_idx:link_end_idx + 1] para.append(nodes.reference("", uri, refuri=uri)) start_idx = link_end_idx + 1 # get all text after the last link (could be empty) or all of the # text if no link was given para.append(nodes.inline(text=notes[start_idx:])) return para def setup(app): app.add_directive('support_matrix', SupportMatrixDirective) app.add_stylesheet('support_matrix.css') neutron-12.1.1/doc/source/conf.py0000664000175000017500000002202013553660047016661 0ustar zuulzuul00000000000000# Copyright (c) 2010 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # # Neutron documentation build configuration file, created by # sphinx-quickstart on Tue May 18 13:50:15 2010. # # This file is execfile()d with the current directory set to it's containing # dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import logging import os import sys import eventlet # module ref generation can cause partial greening resulting in thread issues # during the linkcheck builder, so initialize eventlet upfront eventlet.monkey_patch() # NOTE(amotoki): In case of oslo_config.sphinxext is enabled, # when resolving automodule neutron.tests.functional.db.test_migrations, # sphinx accesses tests/functional/__init__.py is processed, # eventlet.monkey_patch() is called and monkey_patch() tries to access # pyroute2.common.__class__ attribute. It raises pyroute2 warning and # it causes sphinx build failure due to warning-is-error = 1. # To pass sphinx build, ignore pyroute2 warning explicitly. logging.getLogger('pyroute2').setLevel(logging.ERROR) # 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. BASE_DIR = os.path.dirname(os.path.abspath(__file__)) NEUTRON_DIR = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) sys.path.insert(0, NEUTRON_DIR) sys.path.append(os.path.abspath("ext")) # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', 'sphinx.ext.graphviz', 'sphinx.ext.todo', 'openstackdocstheme', 'support_matrix', 'oslo_config.sphinxext', 'oslo_config.sphinxconfiggen', ] # openstackdocstheme options repository_name = 'openstack/neutron' bug_project = 'neutron' bug_tag = 'doc' todo_include_todos = True # Add any paths that contain templates here, relative to this directory. templates_path = [] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Neutron' copyright = u'2011-present, OpenStack Foundation.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # Version info from neutron.version import version_info as neutron_version release = neutron_version.release_string() # The short X.Y version. version = neutron_version.version_string() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. # unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. show_authors = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. modindex_common_prefix = ['neutron.'] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' html_theme = 'openstackdocs' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = ['_theme'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%Y-%m-%d %H:%M' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'neutrondoc' # -- Options for LaTeX output ------------------------------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, # documentclass [howto/manual]). latex_documents = [ ('index', 'Neutron.tex', u'Neutron Documentation', u'Neutron development team', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True # -- Options for oslo_config.sphinxconfiggen --------------------------------- _config_generator_config_files = [ 'dhcp_agent.ini', 'l3_agent.ini', 'linuxbridge_agent.ini', 'macvtap_agent.ini', 'metadata_agent.ini', 'metering_agent.ini', 'ml2_conf.ini', 'neutron.conf', 'openvswitch_agent.ini', 'sriov_agent.ini', ] def _get_config_generator_config_definition(config_file): config_file_path = '../../etc/oslo-config-generator/%s' % conf # oslo_config.sphinxconfiggen appends '.conf.sample' to the filename, # strip file extentension (.conf or .ini). output_file_path = '_static/config-samples/%s' % conf.rsplit('.', 1)[0] return (config_file_path, output_file_path) config_generator_config_file = [ _get_config_generator_config_definition(conf) for conf in _config_generator_config_files ] linkcheck_anchors_ignore = [ # skip gerrit anchors '\/q\/.*', 'q\,.*', '\/c\/.*' ] neutron-12.1.1/doc/source/cli/0000775000175000017500000000000013553660156016136 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/cli/neutron-debug.rst0000664000175000017500000002073013553660047021447 0ustar zuulzuul00000000000000.. This file is manually generated, unlike many of the other chapters. ================================= neutron-debug command-line client ================================= The :command:`neutron-debug` client is an extension to the :command:`neutron` command-line interface (CLI) for the OpenStack neutron-debug tool. This chapter documents :command:`neutron-debug` version ``2.3.0``. For help on a specific :command:`neutron-debug` command, enter: .. code-block:: console $ neutron-debug help COMMAND .. _neutron-debug_usage: neutron-debug usage ~~~~~~~~~~~~~~~~~~~ .. code-block:: console usage: neutron-debug [--version] [-v] [-q] [-h] [-r NUM] [--os-service-type ] [--os-endpoint-type ] [--service-type ] [--endpoint-type ] [--os-auth-strategy ] [--os-cloud ] [--os-auth-url ] [--os-tenant-name | --os-project-name ] [--os-tenant-id | --os-project-id ] [--os-username ] [--os-user-id ] [--os-user-domain-id ] [--os-user-domain-name ] [--os-project-domain-id ] [--os-project-domain-name ] [--os-cert ] [--os-cacert ] [--os-key ] [--os-password ] [--os-region-name ] [--os-token ] [--http-timeout ] [--os-url ] [--insecure] [--config-file CONFIG_FILE] ... Subcommands ----------- ``probe-create`` Create probe port - create port and interface within a network namespace. ``probe-list`` List all probes. ``probe-clear`` Clear all probes. ``probe-delete`` Delete probe - delete port then delete the namespace. ``probe-exec`` Execute commands in the namespace of the probe. ``ping-all`` ``ping-all`` is an all-in-one command to ping all fixed IPs in a specified network. .. _neutron-debug_optional: neutron-debug optional arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``--version`` Show program's version number and exit ``-v, --verbose, --debug`` Increase verbosity of output and show tracebacks on errors. You can repeat this option. ``-q, --quiet`` Suppress output except warnings and errors. ``-h, --help`` Show this help message and exit ``-r NUM, --retries NUM`` How many times the request to the Neutron server should be retried if it fails. ``--os-service-type `` Defaults to env[OS_NETWORK_SERVICE_TYPE] or network. ``--os-endpoint-type `` Defaults to ``env[OS_ENDPOINT_TYPE]`` or public. ``--service-type `` DEPRECATED! Use --os-service-type. ``--endpoint-type `` DEPRECATED! Use --os-endpoint-type. ``--os-auth-strategy `` DEPRECATED! Only keystone is supported. ``os-cloud `` Defaults to env[OS_CLOUD]. ``--os-auth-url `` Authentication URL, defaults to env[OS_AUTH_URL]. ``--os-tenant-name `` Authentication tenant name, defaults to env[OS_TENANT_NAME]. ``--os-project-name `` Another way to specify tenant name. This option is mutually exclusive with --os-tenant-name. Defaults to env[OS_PROJECT_NAME]. ``--os-tenant-id `` Authentication tenant ID, defaults to env[OS_TENANT_ID]. ``--os-project-id `` Another way to specify tenant ID. This option is mutually exclusive with --os-tenant-id. Defaults to env[OS_PROJECT_ID]. ``--os-username `` Authentication username, defaults to env[OS_USERNAME]. ``--os-user-id `` Authentication user ID (Env: OS_USER_ID) ``--os-user-domain-id `` OpenStack user domain ID. Defaults to env[OS_USER_DOMAIN_ID]. ``--os-user-domain-name `` OpenStack user domain name. Defaults to env[OS_USER_DOMAIN_NAME]. ``--os-project-domain-id `` Defaults to env[OS_PROJECT_DOMAIN_ID]. ``--os-project-domain-name `` Defaults to env[OS_PROJECT_DOMAIN_NAME]. ``--os-cert `` Path of certificate file to use in SSL connection. This file can optionally be prepended with the private key. Defaults to env[OS_CERT]. ``--os-cacert `` Specify a CA bundle file to use in verifying a TLS (https) server certificate. Defaults to env[OS_CACERT]. ``--os-key `` Path of client key to use in SSL connection. This option is not necessary if your key is prepended to your certificate file. Defaults to env[OS_KEY]. ``--os-password `` Authentication password, defaults to env[OS_PASSWORD]. ``--os-region-name `` Authentication region name, defaults to env[OS_REGION_NAME]. ``--os-token `` Authentication token, defaults to env[OS_TOKEN]. ``--http-timeout `` Timeout in seconds to wait for an HTTP response. Defaults to env[OS_NETWORK_TIMEOUT] or None if not specified. ``--os-url `` Defaults to env[OS_URL] ``--insecure`` Explicitly allow neutronclient to perform "insecure" SSL (https) requests. The server's certificate will not be verified against any certificate authorities. This option should be used with caution. ``--config-file CONFIG_FILE`` Config file for interface driver (You may also use l3_agent.ini) neutron-debug probe-create command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: console usage: neutron-debug probe-create NET Create probe port - create port and interface, then place it into the created network namespace. Positional arguments -------------------- ``NET ID`` ID of the network in which the probe will be created. neutron-debug probe-list command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: console usage: neutron-debug probe-list List probes. neutron-debug probe-clear command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: console usage: neutron-debug probe-clear Clear all probes. neutron-debug probe-delete command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: console usage: neutron-debug probe-delete Remove a probe. Positional arguments -------------------- ```` ID of the probe to delete. neutron-debug probe-exec command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: console usage: neutron-debug probe-exec Execute commands in the namespace of the probe neutron-debug ping-all command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: console usage: neutron-debug ping-all --timeout All-in-one command to ping all fixed IPs in a specified network. A probe creation is not needed for this command. A new probe is created automatically. It will, however, need to be deleted manually when it is no longer needed. When there are multiple networks, the newly created probe will be attached to a random network and thus the ping will take place from within that random network. Positional arguments -------------------- ```` ID of the port to use. Optional arguments ------------------ ``--timeout `` Optional ping timeout. neutron-debug example ~~~~~~~~~~~~~~~~~~~~~ .. code-block:: console usage: neutron-debug create-probe Create a probe namespace within the network identified by ``NET_ID``. The namespace will have the name of qprobe- .. note:: For the following examples to function, the security group rules may need to be modified to allow the SSH (TCP port 22) or ping (ICMP) traffic into network. .. code-block:: console usage: neutron-debug probe-exec "ssh " SSH to an instance within the network. .. code-block:: console usage: neutron-debug ping-all Ping all instances on this network to verify they are responding. .. code-block:: console usage: neutron-debug probe-exec dhcping -s Ping the DHCP server for this network using dhcping to verify it is working. neutron-12.1.1/doc/source/cli/neutron-sanity-check.rst0000664000175000017500000001771513553660047022754 0ustar zuulzuul00000000000000.. This file is manually generated, unlike many of the other chapters. ======================================== neutron-sanity-check command-line client ======================================== The :command:`neutron-sanity-check` client is a tool that checks various sanity about the Networking service. This chapter documents :command:`neutron-sanity-check` version ``10.0.0``. neutron-sanity-check usage ~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: console usage: neutron-sanity-check [-h] [--arp_header_match] [--arp_responder] [--bridge_firewalling] [--config-dir DIR] [--config-file PATH] [--debug] [--dhcp_release6] [--dibbler_version] [--dnsmasq_version] [--ebtables_installed] [--icmpv6_header_match] [--ip6tables_installed] [--ip_nonlocal_bind] [--iproute2_vxlan] [--ipset_installed] [--keepalived_ipv6_support] [--log-config-append PATH] [--log-date-format DATE_FORMAT] [--log-dir LOG_DIR] [--log-file PATH] [--noarp_header_match] [--noarp_responder] [--nobridge_firewalling] [--nodebug] [--nodhcp_release6] [--nodibbler_version] [--nodnsmasq_version] [--noebtables_installed] [--noicmpv6_header_match] [--noip6tables_installed] [--noip_nonlocal_bind] [--noiproute2_vxlan] [--noipset_installed] [--nokeepalived_ipv6_support] [--nonova_notify] [--noovs_conntrack] [--noovs_geneve] [--noovs_patch] [--noovs_vxlan] [--noovsdb_native] [--noread_netns] [--nouse-syslog] [--nova_notify] [--noverbose] [--novf_extended_management] [--novf_management] [--nowatch-log-file] [--ovs_conntrack] [--ovs_geneve] [--ovs_patch] [--ovs_vxlan] [--ovsdb_native] [--read_netns] [--state_path STATE_PATH] [--syslog-log-facility SYSLOG_LOG_FACILITY] [--use-syslog] [--verbose] [--version] [--vf_extended_management] [--vf_management] [--watch-log-file] neutron-sanity-check optional arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``-h, --help`` show this help message and exit ``--arp_header_match`` Check for ARP header match support ``--arp_responder`` Check for ARP responder support ``--bridge_firewalling`` Check bridge firewalling ``--ip_nonlocal_bind`` Check ip_nonlocal_bind kernel option works with network namespaces. ``--config-dir DIR`` Path to a config directory to pull ``*.conf`` files from. This file set is sorted, so as to provide a predictable parse order if individual options are over-ridden. The set is parsed after the file(s) specified via previous --config-file, arguments hence over-ridden options in the directory take precedence. ``--config-file PATH`` Path to a config file to use. Multiple config files can be specified, with values in later files taking precedence. Dafaults to ``None``. ``--debug, -d`` Print debugging output (set logging level to ``DEBUG`` instead of default ``INFO`` level). ``--dhcp_release6`` Check dhcp_release6 installation ``--dibbler_version`` Check minimal dibbler version ``--dnsmasq_version`` Check minimal dnsmasq version ``--ebtables_installed`` Check ebtables installation ``--icmpv6_header_match`` Check for ICMPv6 header match support ``--ip6tables_installed`` Check ip6tables installation ``--iproute2_vxlan`` Check for iproute2 vxlan support ``--ipset_installed`` Check ipset installation ``--keepalived_ipv6_support`` Check keepalived IPv6 support ``--log-config-append PATH, --log_config PATH`` The name of a logging configuration file. This file is appended to any existing logging configuration files. For details about logging configuration files, see the Python logging module documentation. Note that when logging configuration files are used then all logging configuration is set in the configuration file and other logging configuration options are ignored (for example, ``logging_context_format_string``). ``--log-date-format DATE_FORMAT`` Format string for %(asctime)s in log records. Default: None. This option is ignored if ``log_config_append`` is set. ``--log-dir LOG_DIR, --logdir LOG_DIR`` (Optional) The base directory used for relative ``log-file`` paths. This option is ignored if ``log_config_append`` is set. ``--log-file PATH, --logfile PATH`` (Optional) Name of log file to output to. If no default is set, logging will go to stderr as defined by ``use_stderr``. This option is ignored if ``log_config_append`` is set. ``--noarp_header_match`` The inverse of --arp_header_match ``--noarp_responder`` The inverse of --arp_responder ``--nobridge_firewalling`` The inverse of --bridge_firewalling ``--nodebug`` The inverse of --debug ``--nodhcp_release6`` The inverse of --dhcp_release6 ``--nodibbler_version`` The inverse of --dibbler_version ``--nodnsmasq_version`` The inverse of --dnsmasq_version ``--noebtables_installed`` The inverse of --ebtables_installed ``--noicmpv6_header_match`` The inverse of --icmpv6_header_match ``--noip6tables_installed`` The inverse of --ip6tables_installed ``--noip_nonlocal_bind`` The inverse of --ip_nonlocal_bind ``--noiproute2_vxlan`` The inverse of --iproute2_vxlan ``--noipset_installed`` The inverse of --ipset_installed ``--nokeepalived_ipv6_support`` The inverse of --keepalived_ipv6_support ``--nonova_notify`` The inverse of --nova_notify ``--noovs_conntrack`` The inverse of --ovs_conntrack ``--noovs_geneve`` The inverse of --ovs_geneve ``--noovs_patch`` The inverse of --ovs_patch ``--noovs_vxlan`` The inverse of --ovs_vxlan ``--noovsdb_native`` The inverse of --ovsdb_native ``--noread_netns`` The inverse of --read_netns ``--nouse-syslog`` The inverse of --use-syslog ``--nova_notify`` Check for nova notification support ``--noverbose`` The inverse of --verbose ``--novf_extended_management`` The inverse of --vf_extended_management ``--novf_management`` The inverse of --vf_management ``--nowatch-log-file`` The inverse of --watch-log-file ``--ovs_geneve`` Check for OVS Geneve support ``--ovs_patch`` Check for patch port support ``--ovs_vxlan`` Check for OVS vxlan support ``--ovsdb_native`` Check ovsdb native interface support ``--read_netns`` Check netns permission settings ``--state_path STATE_PATH`` Where to store Neutron state files. This directory must be writable by the agent. ``--syslog-log-facility SYSLOG_LOG_FACILITY`` Syslog facility to receive log lines. This option is ignored if ``log_config_append`` is set. ``--use-syslog`` Use syslog for logging. Existing syslog format is **DEPRECATED** and will be changed later to honor RFC5424. This option is ignored if ``log_config_append`` is set. ``--verbose, -v`` If set to ``false``, the logging level will be set to ``WARNING`` instead of the default ``INFO`` level. ``--version`` show program's version number and exit ``--vf_extended_management`` Check for VF extended management support ``--vf_management`` Check for VF management support ``--watch-log-file`` Uses logging handler designed to watch file system. When log file is moved or removed this handler will open a new log file with specified path instantaneously. It makes sense only if ``log_file`` option is specified and Linux platform is used. This option is ignored if ``log_config_append`` is set. neutron-12.1.1/doc/source/cli/index.rst0000664000175000017500000000046213553660047020000 0ustar zuulzuul00000000000000================================ Command-Line Interface Reference ================================ .. Add links to neutron, OSC and its network plugin command reference once their CLI reference is available in neutronclient repo. .. toctree:: :maxdepth: 1 neutron-debug neutron-sanity-check neutron-12.1.1/doc/source/_static/0000775000175000017500000000000013553660156017015 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/_static/support_matrix.css0000664000175000017500000000072213553660046022626 0ustar zuulzuul00000000000000 .sp_feature_required { font-weight: bold; } .sp_impl_complete { color: rgb(0, 120, 0); font-weight: normal; } .sp_impl_missing { color: rgb(120, 0, 0); font-weight: normal; } .sp_impl_partial { color: rgb(170, 170, 0); font-weight: normal; } .sp_impl_unknown { color: rgb(170, 170, 170); font-weight: normal; } .sp_impl_summary { font-size: 2em; } .sp_cli { font-family: monospace; background-color: #F5F5F5; } neutron-12.1.1/doc/source/index.rst0000664000175000017500000000432013553660047017226 0ustar zuulzuul00000000000000.. Copyright 2011-2013 OpenStack Foundation All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Welcome to Neutron's documentation! =================================== Neutron is an OpenStack project to provide "network connectivity as a service" between interface devices (e.g., vNICs) managed by other OpenStack services (e.g., nova). It implements the `Neutron API`_. .. _`Neutron API`: https://developer.openstack.org/api-ref/networking/ This documentation is generated by the Sphinx toolkit and lives in the source tree. Additional documentation on Neutron and other components of OpenStack can be found on the `OpenStack wiki`_ and the `Neutron section of the wiki`. The `Neutron Development wiki`_ is also a good resource for new contributors. .. _`OpenStack wiki`: https://wiki.openstack.org .. _`Neutron section of the wiki`: https://wiki.openstack.org/Neutron .. _`Neutron Development wiki`: https://wiki.openstack.org/NeutronDevelopment Enjoy! Installation Guide ------------------ .. toctree:: :maxdepth: 2 Installation Guide Networking Guide ---------------- .. toctree:: :maxdepth: 2 admin/index Configuration Reference ----------------------- .. toctree:: :maxdepth: 2 configuration/index CLI Reference ------------- .. toctree:: :maxdepth: 2 cli/index Neutron Feature Classification ------------------------------ .. toctree:: :maxdepth: 2 feature_classification/index Contributor Guide ----------------- .. toctree:: :maxdepth: 2 contributor/index API Extensions -------------- Go to https://developer.openstack.org/api-ref/network/ for information about the OpenStack Network API and its extensions. neutron-12.1.1/doc/source/feature_classification/0000775000175000017500000000000013553660156022075 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/feature_classification/general_feature_support_matrix.rst0000664000175000017500000000263013553660047031137 0ustar zuulzuul00000000000000======================= General Feature Support ======================= .. warning:: Please note, while this document is still being maintained, this is slowly being updated to re-group and classify features using the definitions described in here: :doc:`feature_classification_introduction`. This document covers the maturity and support of the Neutron API and its API extensions. Details about the API can be found at `Networking API v2.0 `_. When considering which capabilities should be marked as mature the following general guiding principles were applied: * **Inclusivity** - people have shown ability to make effective use of a wide range of network plugins and drivers with broadly varying feature sets. Aiming to keep the requirements as inclusive as possible, avoids second-guessing how a user wants to use their networks. * **Bootstrapping** - a practical use case test is to consider that starting point for the network deploy is an empty data center with new machines and network connectivity. Then look at what are the minimum features required of the network service, in order to get user instances running and connected over the network. * **Reality** - there are many networking drivers and plugins compatible with neutron. Each with their own supported feature set. .. support_matrix:: general_feature_support_matrix.ini neutron-12.1.1/doc/source/feature_classification/feature_classification_introduction.rst0000664000175000017500000000471413553660046032142 0ustar zuulzuul00000000000000============ Introduction ============ This document describes how features are listed in :doc:`general_feature_support_matrix` and :doc:`provider_network_support_matrix`. Goals ~~~~~ The object of this document is to inform users whether or not features are complete, well documented, stable, and tested. This approach ensures good user experience for those well maintained features. .. note:: Tests are specific to particular combinations of technologies. The plugins chosen for deployment make a big difference to whether or not features will work. Concepts ~~~~~~~~ These definitions clarify the terminology used throughout this document. Feature status ~~~~~~~~~~~~~~ * Immature * Mature * Required * Deprecated (scheduled to be removed in a future release) Immature -------- Immature features do not have enough functionality to satisfy real world use cases. An immature feature is a feature being actively developed, which is only partially functional and upstream tested, most likely introduced in a recent release, and that will take time to mature thanks to feedback from downstream QA. Users of these features will likely identify gaps and/or defects that were not identified during specification and code review. Mature ------ A feature is considered mature if it satisfies the following criteria: * Complete API documentation including concept and REST call definition. * Complete Administrator documentation. * Tempest tests that define the correct functionality of the feature. * Enough functionality and reliability to be useful in real world scenarios. * Low probability of support for the feature being dropped. Required -------- Required features are core networking principles that have been thoroughly tested and have been implemented in real world use cases. In addition they satisfy the same criteria for any mature features. .. note:: Any new drivers must prove that they support all required features before they are merged into neutron. Deprecated ---------- Deprecated features are no longer supported and only security related fixes or development will happen towards them. Deployment rating of features ----------------------------- The deployment rating shows only the state of the tests for each feature on a particular deployment. .. important:: Despite the obvious parallels that could be drawn, this list is unrelated to the DefCore effort. See `InteropWG `_ neutron-12.1.1/doc/source/feature_classification/provider_network_support_matrix.ini0000664000175000017500000000347413553660046031367 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. [target.ovs] label=networking-ovs title=Open vSwitch link= [target.linuxbridge] label=networking-linux-bridge title=Linux Bridge link= [target.odl] label=networking-odl title=Networking ODL link=https://docs.openstack.org/networking-odl/latest/ [target.midonet] label=networking-midonet title=Networking MidoNet link=https://docs.openstack.org/networking-midonet/latest/ [target.ovn] label=networking-ovn title=Networking OVN link=https://docs.openstack.org/networking-ovn/latest/ [operation.VLAN] title=VLAN provider network support status=mature networking-ovs=complete networking-linux-bridge=complete networking-odl=unknown networking-midonet=incomplete networking-ovn=complete [operation.VXLAN] title=VXLAN provider network support status=mature networking-ovs=complete networking-linux-bridge=complete networking-odl=complete networking-midonet=incomplete networking-ovn=incomplete [operation.GRE] title=GRE provider network support status=immature networking-ovs=complete networking-linux-bridge=unknown networking-odl=complete networking-midonet=incomplete networking-ovn=incomplete [operation.Geneve] title=Geneve provider network support status=immature networking-ovs=complete networking-linux-bridge=unknown networking-odl=incomplete networking-midonet=incomplete networking-ovn=complete neutron-12.1.1/doc/source/feature_classification/index.rst0000664000175000017500000000233413553660046023736 0ustar zuulzuul00000000000000.. Copyright 2010-2011 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Neutron Feature Classification ============================== .. toctree:: :maxdepth: 2 feature_classification_introduction general_feature_support_matrix provider_network_support_matrix neutron-12.1.1/doc/source/feature_classification/general_feature_support_matrix.ini0000664000175000017500000001352113553660047031107 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. [target.ovs] label=networking-ovs title=Open vSwitch link= [target.linuxbridge] label=networking-linux-bridge title=Linux Bridge link= [target.odl] label=networking-odl title=Networking ODL link=https://docs.openstack.org/networking-odl/latest/ [target.midonet] label=networking-midonet title=Networking MidoNet link=https://docs.openstack.org/networking-midonet/latest/ [target.ovn] label=networking-ovn title=Networking OVN link=https://docs.openstack.org/networking-ovn/latest/ [operation.Networks] title=Networks status=required api=core cli=openstack network * notes=The ability to create, modify and delete networks. https://developer.openstack.org/api-ref/networking/v2/#networks networking-ovs=complete networking-linux-bridge=complete networking-odl=complete networking-midonet=complete networking-ovn=complete [operation.Subnets] title=Subnets status=required api=core cli=openstack subnet * notes=The ability to create and manipulate subnets and subnet pools. https://developer.openstack.org/api-ref/networking/v2/#subnets networking-ovs=complete networking-linux-bridge=complete networking-odl=complete networking-midonet=complete networking-ovn=complete [operation.Ports] title=Ports status=required api=core cli=openstack port * notes=The ability to create and manipulate ports. https://developer.openstack.org/api-ref/networking/v2/#ports networking-ovs=complete networking-linux-bridge=complete networking-odl=complete networking-midonet=complete networking-ovn=complete [operation.Router] title=Routers status=required api=router cli=openstack router * notes=The ability to create and manipulate routers. https://developer.openstack.org/api-ref/networking/v2/#routers-routers networking-ovs=complete networking-linux-bridge=complete networking-odl=complete networking-midonet=complete networking-ovn=complete [operation.Security_Groups] title=Security Groups status=mature api=security-group cli=openstack security group * notes=Security groups are set by default, and can be modified to control ingress & egress traffic. https://developer.openstack.org/api-ref/networking/v2/#security-groups-security-groups networking-ovs=complete networking-linux-bridge=complete networking-odl=complete networking-midonet=complete networking-ovn=complete [operation.External_Nets] title=External Networks status=mature api=external-net notes=The ability to create an external network to provide internet access to and from instances using floating IP addresses and security group rules. networking-ovs=complete networking-linux-bridge=complete networking-odl=complete networking-midonet=complete networking-ovn=complete [operation.DVR] title=Distributed Virtual Routers status=immature api=dvr notes=The ability to support the distributed virtual routers. https://wiki.openstack.org/wiki/Neutron/DVR networking-ovs=complete networking-linux-bridge=incomplete networking-odl=partial networking-midonet=complete networking-ovn=partial [operation.L3_HA] title=L3 High Availability status=immature api=l3-ha notes=The ability to support the High Availability features and extensions. https://wiki.openstack.org/wiki/Neutron/L3_High_Availability_VRRP. networking-ovs=complete networking-linux-bridge=complete networking-odl=partial networking-midonet=incomplete networking-ovn=partial [operation.QoS] title=Quality of Service status=mature api=qos notes=Support for Neutron Quality of Service policies and API. https://developer.openstack.org/api-ref/networking/v2/#qos-policies-qos networking-ovs=complete networking-linux-bridge=partial networking-odl=partial networking-midonet=complete networking-ovn=complete [operation.BGP] title=Border Gateway Protocol status=immature notes=https://developer.openstack.org/api-ref/networking/v2/#bgp-mpls-vpn-interconnection networking-ovs=complete networking-linux-bridge=unknown networking-odl=unknown networking-midonet=complete networking-ovn=unknown [operation.DNS] title=DNS status=mature api=dns-integration notes=The ability to integrate with an external DNS as a Service. https://docs.openstack.org/neutron/latest/admin/config-dns-int.html networking-ovs=complete networking-linux-bridge=complete networking-odl=complete networking-midonet=incomplete networking-ovn=complete [operation.Trunk_Ports] title=Trunk Ports status=mature api=trunk notes=Neutron extension to access lots of neutron networks over a single vNIC as tagged/encapsulated traffic. https://developer.openstack.org/api-ref/networking/v2/#trunk-networking networking-ovs=complete networking-linux-bridge=complete networking-odl=incomplete networking-midonet=incomplete networking-ovn=complete [operation.Metering] title=Metering status=mature api=metering notes=Meter traffic at the L3 router levels. https://developer.openstack.org/api-ref/networking/v2/#metering-labels-and-rules-metering-labels-metering-label-rules networking-ovs=complete networking-linux-bridge=complete networking-odl=incomplete networking-midonet=incomplete networking-ovn=unknown [operations.Routed_Provider_Networks] title=Routed Provider Networks status=immature notes=The ability to present a multi-segment layer-3 network as a single entity. https://docs.openstack.org/neutron/latest/admin/config-routed-networks.html networking-ovs=partial networking-linux-bridge=partial networking-odl=incomplete networking-midonet=incomplete networking-ovn=partial neutron-12.1.1/doc/source/feature_classification/provider_network_support_matrix.rst0000664000175000017500000000244713553660046031417 0ustar zuulzuul00000000000000======================== Provider Network Support ======================== .. warning:: Please note, while this document is still being maintained, this is slowly being updated to re-group and classify features using the definitions described in here: :doc:`feature_classification_introduction`. This document covers the maturity and support for various network isolation technologies. When considering which capabilities should be marked as mature the following general guiding principles were applied: * **Inclusivity** - people have shown ability to make effective use of a wide range of network plugins and drivers with broadly varying feature sets. Aiming to keep the requirements as inclusive as possible, avoids second-guessing how a user wants to use their networks. * **Bootstrapping** - a practical use case test is to consider that starting point for the network deploy is an empty data center with new machines and network connectivity. Then look at what are the minimum features required of the network service, in order to get user instances running and connected over the network. * **Reality** - there are many networking drivers and plugins compatible with neutron. Each with their own supported feature set. .. support_matrix:: provider_network_support_matrix.ini neutron-12.1.1/doc/source/admin/0000775000175000017500000000000013553660156016457 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/admin/deploy-lb-ha-vrrp.rst0000664000175000017500000001417713553660047022466 0ustar zuulzuul00000000000000.. _deploy-lb-ha-vrrp: ========================================== Linux bridge: High availability using VRRP ========================================== .. include:: shared/deploy-ha-vrrp.txt .. warning:: This high-availability mechanism is not compatible with the layer-2 population mechanism. You must disable layer-2 population in the ``linuxbridge_agent.ini`` file and restart the Linux bridge agent on all existing network and compute nodes prior to deploying the example configuration. Prerequisites ~~~~~~~~~~~~~ Add one network node with the following components: * Three network interfaces: management, provider, and overlay. * OpenStack Networking layer-2 agent, layer-3 agent, and any dependencies. .. note:: You can keep the DHCP and metadata agents on each compute node or move them to the network nodes. Architecture ~~~~~~~~~~~~ .. image:: figures/deploy-lb-ha-vrrp-overview.png :alt: High-availability using Linux bridge with VRRP - overview The following figure shows components and connectivity for one self-service network and one untagged (flat) network. The master router resides on network node 1. In this particular case, the instance resides on the same compute node as the DHCP agent for the network. If the DHCP agent resides on another compute node, the latter only contains a DHCP namespace and Linux bridge with a port on the overlay physical network interface. .. image:: figures/deploy-lb-ha-vrrp-compconn1.png :alt: High-availability using Linux bridge with VRRP - components and connectivity - one network Example configuration ~~~~~~~~~~~~~~~~~~~~~ Use the following example configuration as a template to add support for high-availability using VRRP to an existing operational environment that supports self-service networks. Controller node --------------- #. In the ``neutron.conf`` file: * Enable VRRP. .. code-block:: ini [DEFAULT] l3_ha = True #. Restart the following services: * Server Network node 1 -------------- No changes. Network node 2 -------------- #. Install the Networking service Linux bridge layer-2 agent and layer-3 agent. #. In the ``neutron.conf`` file, configure common options: .. include:: shared/deploy-config-neutron-common.txt #. In the ``linuxbridge_agent.ini`` file, configure the layer-2 agent. .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE [vxlan] enable_vxlan = True local_ip = OVERLAY_INTERFACE_IP_ADDRESS [securitygroup] firewall_driver = iptables Replace ``PROVIDER_INTERFACE`` with the name of the underlying interface that handles provider networks. For example, ``eth1``. Replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the IP address of the interface that handles VXLAN overlays for self-service networks. #. In the ``l3_agent.ini`` file, configure the layer-3 agent. .. code-block:: ini [DEFAULT] interface_driver = linuxbridge external_network_bridge = .. note:: The ``external_network_bridge`` option intentionally contains no value. #. Start the following services: * Linux bridge agent * Layer-3 agent Compute nodes ------------- No changes. Verify service operation ------------------------ #. Source the administrative project credentials. #. Verify presence and operation of the agents. .. code-block:: console $ openstack network agent list +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | 09de6af6-c5f1-4548-8b09-18801f068c57 | Linux bridge agent | compute2 | | True | UP | neutron-linuxbridge-agent | | 188945d1-9e70-4803-a276-df924e0788a4 | Linux bridge agent | compute1 | | True | UP | neutron-linuxbridge-agent | | e76c440d-d5f6-4316-a674-d689630b629e | DHCP agent | compute1 | nova | True | UP | neutron-dhcp-agent | | e67367de-6657-11e6-86a4-931cd04404bb | DHCP agent | compute2 | nova | True | UP | neutron-dhcp-agent | | e8174cae-6657-11e6-89f0-534ac6d0cb5c | Metadata agent | compute1 | | True | UP | neutron-metadata-agent | | ece49ec6-6657-11e6-bafb-c7560f19197d | Metadata agent | compute2 | | True | UP | neutron-metadata-agent | | 598f6357-4331-4da5-a420-0f5be000bec9 | L3 agent | network1 | nova | True | UP | neutron-l3-agent | | f4734e0f-bcd5-4922-a19d-e31d56b0a7ae | Linux bridge agent | network1 | | True | UP | neutron-linuxbridge-agent | | 670e5805-340b-4182-9825-fa8319c99f23 | Linux bridge agent | network2 | | True | UP | neutron-linuxbridge-agent | | 96224e89-7c15-42e9-89c4-8caac7abdd54 | L3 agent | network2 | nova | True | UP | neutron-l3-agent | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ Create initial networks ----------------------- .. include:: shared/deploy-ha-vrrp-initialnetworks.txt Verify network operation ------------------------ .. include:: shared/deploy-ha-vrrp-verifynetworkoperation.txt Verify failover operation ------------------------- .. include:: shared/deploy-ha-vrrp-verifyfailoveroperation.txt Keepalived VRRP health check ---------------------------- .. include:: shared/keepalived-vrrp-healthcheck.txt Network traffic flow ~~~~~~~~~~~~~~~~~~~~ This high-availability mechanism simply augments :ref:`deploy-lb-selfservice` with failover of layer-3 services to another router if the master router fails. Thus, you can reference :ref:`Self-service network traffic flow ` for normal operation. neutron-12.1.1/doc/source/admin/config-address-scopes.rst0000664000175000017500000007117413553660047023404 0ustar zuulzuul00000000000000.. _config-address-scopes: ============== Address scopes ============== Address scopes build from subnet pools. While subnet pools provide a mechanism for controlling the allocation of addresses to subnets, address scopes show where addresses can be routed between networks, preventing the use of overlapping addresses in any two subnets. Because all addresses allocated in the address scope do not overlap, neutron routers do not NAT between your projects' network and your external network. As long as the addresses within an address scope match, the Networking service performs simple routing between networks. Accessing address scopes ~~~~~~~~~~~~~~~~~~~~~~~~ Anyone with access to the Networking service can create their own address scopes. However, network administrators can create shared address scopes, allowing other projects to create networks within that address scope. Access to addresses in a scope are managed through subnet pools. Subnet pools can either be created in an address scope, or updated to belong to an address scope. With subnet pools, all addresses in use within the address scope are unique from the point of view of the address scope owner. Therefore, add more than one subnet pool to an address scope if the pools have different owners, allowing for delegation of parts of the address scope. Delegation prevents address overlap across the whole scope. Otherwise, you receive an error if two pools have the same address ranges. Each router interface is associated with an address scope by looking at subnets connected to the network. When a router connects to an external network with matching address scopes, network traffic routes between without Network address translation (NAT). The router marks all traffic connections originating from each interface with its corresponding address scope. If traffic leaves an interface in the wrong scope, the router blocks the traffic. Backwards compatibility ~~~~~~~~~~~~~~~~~~~~~~~ Networks created before the Mitaka release do not contain explicitly named address scopes, unless the network contains subnets from a subnet pool that belongs to a created or updated address scope. The Networking service preserves backwards compatibility with pre-Mitaka networks through special address scope properties so that these networks can perform advanced routing: #. Unlimited address overlap is allowed. #. Neutron routers, by default, will NAT traffic from internal networks to external networks. #. Pre-Mitaka address scopes are not visible through the API. You cannot list address scopes or show details. Scopes exist implicitly as a catch-all for addresses that are not explicitly scoped. Create shared address scopes as an administrative user ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This section shows how to set up shared address scopes to allow simple routing for project networks with the same subnet pools. .. note:: Irrelevant fields have been trimmed from the output of these commands for brevity. #. Create IPv6 and IPv4 address scopes: .. code-block:: console $ openstack address scope create --share --ip-version 6 address-scope-ip6 +------------+--------------------------------------+ | Field | Value | +------------+--------------------------------------+ | headers | | | id | 28424dfc-9abd-481b-afa3-1da97a8fead7 | | ip_version | 6 | | name | address-scope-ip6 | | project_id | 098429d072d34d3596c88b7dbf7e91b6 | | shared | True | +------------+--------------------------------------+ .. code-block:: console $ openstack address scope create --share --ip-version 4 address-scope-ip4 +------------+--------------------------------------+ | Field | Value | +------------+--------------------------------------+ | headers | | | id | 3193bd62-11b5-44dc-acf8-53180f21e9f2 | | ip_version | 4 | | name | address-scope-ip4 | | project_id | 098429d072d34d3596c88b7dbf7e91b6 | | shared | True | +------------+--------------------------------------+ #. Create subnet pools specifying the name (or UUID) of the address scope that the subnet pool belongs to. If you have existing subnet pools, use the :command:`openstack subnet pool set` command to put them in a new address scope: .. code-block:: console $ openstack subnet pool create --address-scope address-scope-ip6 \ --share --pool-prefix 2001:db8:a583::/48 --default-prefix-length 64 \ subnet-pool-ip6 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | address_scope_id | 28424dfc-9abd-481b-afa3-1da97a8fead7 | | created_at | 2016-12-13T22:53:30Z | | default_prefixlen | 64 | | default_quota | None | | description | | | id | a59ff52b-0367-41ff-9781-6318b927dd0e | | ip_version | 6 | | is_default | False | | max_prefixlen | 128 | | min_prefixlen | 64 | | name | subnet-pool-ip6 | | prefixes | 2001:db8:a583::/48 | | project_id | 098429d072d34d3596c88b7dbf7e91b6 | | revision_number | 1 | | shared | True | | tags | [] | | updated_at | 2016-12-13T22:53:30Z | +-------------------+--------------------------------------+ .. code-block:: console $ openstack subnet pool create --address-scope address-scope-ip4 \ --share --pool-prefix 203.0.113.0/24 --default-prefix-length 26 \ subnet-pool-ip4 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | address_scope_id | 3193bd62-11b5-44dc-acf8-53180f21e9f2 | | created_at | 2016-12-13T22:55:09Z | | default_prefixlen | 26 | | default_quota | None | | description | | | id | d02af70b-d622-426f-8e60-ed9df2a8301f | | ip_version | 4 | | is_default | False | | max_prefixlen | 32 | | min_prefixlen | 8 | | name | subnet-pool-ip4 | | prefixes | 203.0.113.0/24 | | project_id | 098429d072d34d3596c88b7dbf7e91b6 | | revision_number | 1 | | shared | True | | tags | [] | | updated_at | 2016-12-13T22:55:09Z | +-------------------+--------------------------------------+ #. Make sure that subnets on an external network are created from the subnet pools created above: .. code-block:: console $ openstack subnet show ipv6-public-subnet +-------------------+------------------------------------------+ | Field | Value | +-------------------+------------------------------------------+ | allocation_pools | 2001:db8:a583::2-2001:db8:a583:0:ffff:ff | | | ff:ffff:ffff | | cidr | 2001:db8:a583::/64 | | created_at | 2016-12-10T21:36:04Z | | description | | | dns_nameservers | | | enable_dhcp | False | | gateway_ip | 2001:db8:a583::1 | | host_routes | | | id | b333bf5a-758c-4b3f-97ec-5f12d9bfceb7 | | ip_version | 6 | | ipv6_address_mode | None | | ipv6_ra_mode | None | | name | ipv6-public-subnet | | network_id | 05a8d31e-330b-4d96-a3fa-884b04abfa4c | | project_id | 098429d072d34d3596c88b7dbf7e91b6 | | revision_number | 2 | | segment_id | None | | service_types | | | subnetpool_id | a59ff52b-0367-41ff-9781-6318b927dd0e | | tags | [] | | updated_at | 2016-12-10T21:36:04Z | +-------------------+------------------------------------------+ .. code-block:: console $ openstack subnet show public-subnet +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | allocation_pools | 203.0.113.2-203.0.113.62 | | cidr | 203.0.113.0/26 | | created_at | 2016-12-10T21:35:52Z | | description | | | dns_nameservers | | | enable_dhcp | False | | gateway_ip | 203.0.113.1 | | host_routes | | | id | 7fd48240-3acc-4724-bc82-16c62857edec | | ip_version | 4 | | ipv6_address_mode | None | | ipv6_ra_mode | None | | name | public-subnet | | network_id | 05a8d31e-330b-4d96-a3fa-884b04abfa4c | | project_id | 098429d072d34d3596c88b7dbf7e91b6 | | revision_number | 2 | | segment_id | None | | service_types | | | subnetpool_id | d02af70b-d622-426f-8e60-ed9df2a8301f | | tags | [] | | updated_at | 2016-12-10T21:35:52Z | +-------------------+--------------------------------------+ Routing with address scopes for non-privileged users ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This section shows how non-privileged users can use address scopes to route straight to an external network without NAT. #. Create a couple of networks to host subnets: .. code-block:: console $ openstack network create network1 +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2016-12-13T23:21:01Z | | description | | | headers | | | id | 1bcf3fe9-a0cb-4d88-a067-a4d7f8e635f0 | | ipv4_address_scope | None | | ipv6_address_scope | None | | mtu | 1450 | | name | network1 | | port_security_enabled | True | | project_id | 098429d072d34d3596c88b7dbf7e91b6 | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 94 | | revision_number | 3 | | router:external | Internal | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2016-12-13T23:21:01Z | +---------------------------+--------------------------------------+ .. code-block:: console $ openstack network create network2 +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2016-12-13T23:21:45Z | | description | | | headers | | | id | 6c583603-c097-4141-9c5c-288b0e49c59f | | ipv4_address_scope | None | | ipv6_address_scope | None | | mtu | 1450 | | name | network2 | | port_security_enabled | True | | project_id | 098429d072d34d3596c88b7dbf7e91b6 | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 81 | | revision_number | 3 | | router:external | Internal | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2016-12-13T23:21:45Z | +---------------------------+--------------------------------------+ #. Create a subnet not associated with a subnet pool or an address scope: .. code-block:: console $ openstack subnet create --network network1 --subnet-range \ 198.51.100.0/26 subnet-ip4-1 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | allocation_pools | 198.51.100.2-198.51.100.62 | | cidr | 198.51.100.0/26 | | created_at | 2016-12-13T23:24:16Z | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 198.51.100.1 | | headers | | | host_routes | | | id | 66874039-d31b-4a27-85d7-14c89341bbb7 | | ip_version | 4 | | ipv6_address_mode | None | | ipv6_ra_mode | None | | name | subnet-ip4-1 | | network_id | 1bcf3fe9-a0cb-4d88-a067-a4d7f8e635f0 | | project_id | 098429d072d34d3596c88b7dbf7e91b6 | | revision_number | 2 | | service_types | | | subnetpool_id | None | | tags | [] | | updated_at | 2016-12-13T23:24:16Z | +-------------------+--------------------------------------+ .. code-block:: console $ openstack subnet create --network network1 --ipv6-ra-mode slaac \ --ipv6-address-mode slaac --ip-version 6 --subnet-range \ 2001:db8:80d2:c4d3::/64 subnet-ip6-1 +-------------------+-----------------------------------------+ | Field | Value | +-------------------+-----------------------------------------+ | allocation_pools | 2001:db8:80d2:c4d3::2-2001:db8:80d2:c4d | | | 3:ffff:ffff:ffff:ffff | | cidr | 2001:db8:80d2:c4d3::/64 | | created_at | 2016-12-13T23:28:28Z | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 2001:db8:80d2:c4d3::1 | | headers | | | host_routes | | | id | a7551b23-2271-4a88-9c41-c84b048e0722 | | ip_version | 6 | | ipv6_address_mode | slaac | | ipv6_ra_mode | slaac | | name | subnet-ip6-1 | | network_id | 1bcf3fe9-a0cb-4d88-a067-a4d7f8e635f0 | | project_id | 098429d072d34d3596c88b7dbf7e91b6 | | revision_number | 2 | | service_types | | | subnetpool_id | None | | tags | [] | | updated_at | 2016-12-13T23:28:28Z | +-------------------+-----------------------------------------+ #. Create a subnet using a subnet pool associated with an address scope from an external network: .. code-block:: console $ openstack subnet create --subnet-pool subnet-pool-ip4 \ --network network2 subnet-ip4-2 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | allocation_pools | 203.0.113.2-203.0.113.62 | | cidr | 203.0.113.0/26 | | created_at | 2016-12-13T23:32:12Z | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 203.0.113.1 | | headers | | | host_routes | | | id | 12be8e8f-5871-4091-9e9e-4e0651b9677e | | ip_version | 4 | | ipv6_address_mode | None | | ipv6_ra_mode | None | | name | subnet-ip4-2 | | network_id | 6c583603-c097-4141-9c5c-288b0e49c59f | | project_id | 098429d072d34d3596c88b7dbf7e91b6 | | revision_number | 2 | | service_types | | | subnetpool_id | d02af70b-d622-426f-8e60-ed9df2a8301f | | tags | [] | | updated_at | 2016-12-13T23:32:12Z | +-------------------+--------------------------------------+ .. code-block:: console $ openstack subnet create --ip-version 6 --ipv6-ra-mode slaac \ --ipv6-address-mode slaac --subnet-pool subnet-pool-ip6 \ --network network2 subnet-ip6-2 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | allocation_pools | 2001:db8:a583::2-2001:db8:a583:0:fff | | | f:ffff:ffff:ffff | | cidr | 2001:db8:a583::/64 | | created_at | 2016-12-13T23:31:17Z | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 2001:db8:a583::1 | | headers | | | host_routes | | | id | b599c2be-e3cd-449c-ba39-3cfcc744c4be | | ip_version | 6 | | ipv6_address_mode | slaac | | ipv6_ra_mode | slaac | | name | subnet-ip6-2 | | network_id | 6c583603-c097-4141-9c5c-288b0e49c59f | | project_id | 098429d072d34d3596c88b7dbf7e91b6 | | revision_number | 2 | | service_types | | | subnetpool_id | a59ff52b-0367-41ff-9781-6318b927dd0e | | tags | [] | | updated_at | 2016-12-13T23:31:17Z | +-------------------+--------------------------------------+ By creating subnets from scoped subnet pools, the network is associated with the address scope. .. code-block:: console $ openstack network show network2 +---------------------------+------------------------------+ | Field | Value | +---------------------------+------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | nova | | created_at | 2016-12-13T23:21:45Z | | description | | | id | 6c583603-c097-4141-9c5c- | | | 288b0e49c59f | | ipv4_address_scope | 3193bd62-11b5-44dc- | | | acf8-53180f21e9f2 | | ipv6_address_scope | 28424dfc-9abd-481b- | | | afa3-1da97a8fead7 | | mtu | 1450 | | name | network2 | | port_security_enabled | True | | project_id | 098429d072d34d3596c88b7dbf7e | | | 91b6 | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 81 | | revision_number | 10 | | router:external | Internal | | shared | False | | status | ACTIVE | | subnets | 12be8e8f-5871-4091-9e9e- | | | 4e0651b9677e, b599c2be-e3cd- | | | 449c-ba39-3cfcc744c4be | | tags | [] | | updated_at | 2016-12-13T23:32:12Z | +---------------------------+------------------------------+ #. Connect a router to each of the project subnets that have been created, for example, using a router called ``router1``: .. code-block:: console $ openstack router add subnet router1 subnet-ip4-1 $ openstack router add subnet router1 subnet-ip4-2 $ openstack router add subnet router1 subnet-ip6-1 $ openstack router add subnet router1 subnet-ip6-2 Checking connectivity --------------------- This example shows how to check the connectivity between networks with address scopes. #. Launch two instances, ``instance1`` on ``network1`` and ``instance2`` on ``network2``. Associate a floating IP address to both instances. #. Adjust security groups to allow pings and SSH (both IPv4 and IPv6): .. code-block:: console $ openstack server list +--------------+-----------+---------------------------------------------------------------------------+------------+ | ID | Name | Networks | Image Name | +--------------+-----------+---------------------------------------------------------------------------+------------+ | 97e49c8e-... | instance1 | network1=2001:db8:80d2:c4d3:f816:3eff:fe52:b69f, 198.51.100.3, 203.0.113.3| cirros | | ceba9638-... | instance2 | network2=203.0.113.3, 2001:db8:a583:0:f816:3eff:fe42:1eeb, 203.0.113.4 | centos | +--------------+-----------+---------------------------------------------------------------------------+------------+ Regardless of address scopes, the floating IPs can be pinged from the external network: .. code-block:: console $ ping -c 1 203.0.113.3 1 packets transmitted, 1 received, 0% packet loss, time 0ms $ ping -c 1 203.0.113.4 1 packets transmitted, 1 received, 0% packet loss, time 0ms You can now ping ``instance2`` directly because ``instance2`` shares the same address scope as the external network: .. note:: BGP routing can be used to automatically set up a static route for your instances. .. code-block:: console # ip route add 203.0.113.0/26 via 203.0.113.2 $ ping -c 1 203.0.113.3 1 packets transmitted, 1 received, 0% packet loss, time 0ms .. code-block:: console # ip route add 2001:db8:a583::/64 via 2001:db8::1 $ ping6 -c 1 2001:db8:a583:0:f816:3eff:fe42:1eeb 1 packets transmitted, 1 received, 0% packet loss, time 0ms You cannot ping ``instance1`` directly because the address scopes do not match: .. code-block:: console # ip route add 198.51.100.0/26 via 203.0.113.2 $ ping -c 1 198.51.100.3 1 packets transmitted, 0 received, 100% packet loss, time 0ms .. code-block:: console # ip route add 2001:db8:80d2:c4d3::/64 via 2001:db8::1 $ ping6 -c 1 2001:db8:80d2:c4d3:f816:3eff:fe52:b69f 1 packets transmitted, 0 received, 100% packet loss, time 0ms If the address scopes match between networks then pings and other traffic route directly through. If the scopes do not match between networks, the router either drops the traffic or applies NAT to cross scope boundaries. neutron-12.1.1/doc/source/admin/config-az.rst0000664000175000017500000004273213553660047021075 0ustar zuulzuul00000000000000.. _config-az: ================== Availability zones ================== An availability zone groups network nodes that run services like DHCP, L3, FW, and others. It is defined as an agent's attribute on the network node. This allows users to associate an availability zone with their resources so that the resources get high availability. Use case -------- An availability zone is used to make network resources highly available. The operators group the nodes that are attached to different power sources under separate availability zones and configure scheduling for resources with high availability so that they are scheduled on different availability zones. Required extensions ------------------- The core plug-in must support the ``availability_zone`` extension. The core plug-in also must support the ``network_availability_zone`` extension to schedule a network according to availability zones. The ``Ml2Plugin`` supports it. The router service plug-in must support the ``router_availability_zone`` extension to schedule a router according to the availability zones. The ``L3RouterPlugin`` supports it. .. code-block:: console $ openstack extension list --network -c Alias -c Name +---------------------------+---------------------------+ | Name | Alias | +---------------------------+---------------------------+ ... | Network Availability Zone | network_availability_zone | ... | Availability Zone | availability_zone | ... | Router Availability Zone | router_availability_zone | ... +---------------------------+---------------------------+ Availability zone of agents ~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``availability_zone`` attribute can be defined in ``dhcp-agent`` and ``l3-agent``. To define an availability zone for each agent, set the value into ``[AGENT]`` section of ``/etc/neutron/dhcp_agent.ini`` or ``/etc/neutron/l3_agent.ini``: .. code-block:: ini [AGENT] availability_zone = zone-1 To confirm the agent's availability zone: .. code-block:: console $ openstack network agent show 116cc128-4398-49af-a4ed-3e95494cd5fc +---------------------+---------------------------------------------------+ | Field | Value | +---------------------+---------------------------------------------------+ | admin_state_up | UP | | agent_type | DHCP agent | | alive | True | | availability_zone | zone-1 | | binary | neutron-dhcp-agent | | configurations | dhcp_driver='neutron.agent.linux.dhcp.Dnsmasq', | | | dhcp_lease_duration='86400', | | | log_agent_heartbeats='False', networks='2', | | | notifies_port_ready='True', ports='6', subnets='4 | | created_at | 2016-12-14 00:25:54 | | description | None | | heartbeat_timestamp | 2016-12-14 06:20:24 | | host | ankur-desktop | | id | 116cc128-4398-49af-a4ed-3e95494cd5fc | | started_at | 2016-12-14 00:25:54 | | topic | dhcp_agent | +---------------------+---------------------------------------------------+ $ openstack network agent show 9632309a-2aa4-4304-8603-c4de02c4a55f +---------------------+-------------------------------------------------+ | Field | Value | +---------------------+-------------------------------------------------+ | admin_state_up | UP | | agent_type | L3 agent | | alive | True | | availability_zone | zone-1 | | binary | neutron-l3-agent | | configurations | agent_mode='legacy', ex_gw_ports='2', | | | external_network_bridge='', floating_ips='0', | | | gateway_external_network_id='', | | | handle_internal_only_routers='True', | | | interface_driver='openvswitch', interfaces='4', | | | log_agent_heartbeats='False', routers='2' | | created_at | 2016-12-14 00:25:58 | | description | None | | heartbeat_timestamp | 2016-12-14 06:20:28 | | host | ankur-desktop | | id | 9632309a-2aa4-4304-8603-c4de02c4a55f | | started_at | 2016-12-14 00:25:58 | | topic | l3_agent | +---------------------+-------------------------------------------------+ Availability zone related attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following attributes are added into network and router: .. list-table:: :header-rows: 1 :widths: 25 10 10 10 50 * - Attribute name - Access - Required - Input type - Description * - availability_zone_hints - RW(POST only) - No - list of string - availability zone candidates for the resource * - availability_zones - RO - N/A - list of string - availability zones for the resource Use ``availability_zone_hints`` to specify the zone in which the resource is hosted: .. code-block:: console $ openstack network create --availability-zone-hint zone-1 \ --availability-zone-hint zone-2 net1 +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | zone-1 | | | zone-2 | | availability_zones | | | created_at | 2016-12-14T06:23:36Z | | description | | | headers | | | id | ad88e059-e7fa-4cf7-8857-6731a2a3a554 | | ipv4_address_scope | None | | ipv6_address_scope | None | | mtu | 1450 | | name | net1 | | port_security_enabled | True | | project_id | cfd1889ac7d64ad891d4f20aef9f8d7c | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 77 | | revision_number | 3 | | router:external | Internal | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2016-12-14T06:23:37Z | +---------------------------+--------------------------------------+ .. code-block:: console $ openstack router create --ha --availability-zone-hint zone-1 \ --availability-zone-hint zone-2 router1 +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | zone-1 | | | zone-2 | | availability_zones | | | created_at | 2016-12-14T06:25:40Z | | description | | | distributed | False | | external_gateway_info | null | | flavor_id | None | | ha | False | | headers | | | id | ced10262-6cfe-47c1-8847-cd64276a868c | | name | router1 | | project_id | cfd1889ac7d64ad891d4f20aef9f8d7c | | revision_number | 3 | | routes | | | status | ACTIVE | | tags | [] | | updated_at | 2016-12-14T06:25:40Z | +-------------------------+--------------------------------------+ Availability zone is selected from ``default_availability_zones`` in ``/etc/neutron/neutron.conf`` if a resource is created without ``availability_zone_hints``: .. code-block:: ini default_availability_zones = zone-1,zone-2 To confirm the availability zone defined by the system: .. code-block:: console $ openstack availability zone list +-----------+-------------+ | Zone Name | Zone Status | +-----------+-------------+ | zone-1 | available | | zone-2 | available | | zone-1 | available | | zone-2 | available | +-----------+-------------+ Look at the ``availability_zones`` attribute of each resource to confirm in which zone the resource is hosted: .. code-block:: console $ openstack network show net1 +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | zone-1 | | | zone-2 | | availability_zones | zone-1 | | | zone-2 | | created_at | 2016-12-14T06:23:36Z | | description | | | headers | | | id | ad88e059-e7fa-4cf7-8857-6731a2a3a554 | | ipv4_address_scope | None | | ipv6_address_scope | None | | mtu | 1450 | | name | net1 | | port_security_enabled | True | | project_id | cfd1889ac7d64ad891d4f20aef9f8d7c | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 77 | | revision_number | 3 | | router:external | Internal | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2016-12-14T06:23:37Z | +---------------------------+--------------------------------------+ .. code-block:: console $ openstack router show router1 +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | zone-1 | | | zone-2 | | availability_zones | zone-1 | | | zone-2 | | created_at | 2016-12-14T06:25:40Z | | description | | | distributed | False | | external_gateway_info | null | | flavor_id | None | | ha | False | | headers | | | id | ced10262-6cfe-47c1-8847-cd64276a868c | | name | router1 | | project_id | cfd1889ac7d64ad891d4f20aef9f8d7c | | revision_number | 3 | | routes | | | status | ACTIVE | | tags | [] | | updated_at | 2016-12-14T06:25:40Z | +-------------------------+--------------------------------------+ .. note:: The ``availability_zones`` attribute does not have a value until the resource is scheduled. Once the Networking service schedules the resource to zones according to ``availability_zone_hints``, ``availability_zones`` shows in which zone the resource is hosted practically. The ``availability_zones`` may not match ``availability_zone_hints``. For example, even if you specify a zone with ``availability_zone_hints``, all agents of the zone may be dead before the resource is scheduled. In general, they should match, unless there are failures or there is no capacity left in the zone requested. Availability zone aware scheduler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Network scheduler ----------------- Set ``AZAwareWeightScheduler`` to ``network_scheduler_driver`` in ``/etc/neutron/neutron.conf`` so that the Networking service schedules a network according to the availability zone: .. code-block:: ini network_scheduler_driver = neutron.scheduler.dhcp_agent_scheduler.AZAwareWeightScheduler dhcp_load_type = networks The Networking service schedules a network to one of the agents within the selected zone as with ``WeightScheduler``. In this case, scheduler refers to ``dhcp_load_type`` as well. Router scheduler ---------------- Set ``AZLeastRoutersScheduler`` to ``router_scheduler_driver`` in file ``/etc/neutron/neutron.conf`` so that the Networking service schedules a router according to the availability zone: .. code-block:: ini router_scheduler_driver = neutron.scheduler.l3_agent_scheduler.AZLeastRoutersScheduler The Networking service schedules a router to one of the agents within the selected zone as with ``LeastRouterScheduler``. Achieving high availability with availability zone ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Although, the Networking service provides high availability for routers and high availability and fault tolerance for networks' DHCP services, availability zones provide an extra layer of protection by segmenting a Networking service deployment in isolated failure domains. By deploying HA nodes across different availability zones, it is guaranteed that network services remain available in face of zone-wide failures that affect the deployment. This section explains how to get high availability with the availability zone for L3 and DHCP. You should naturally set above configuration options for the availability zone. L3 high availability -------------------- Set the following configuration options in file ``/etc/neutron/neutron.conf`` so that you get L3 high availability. .. code-block:: ini l3_ha = True max_l3_agents_per_router = 3 HA routers are created on availability zones you selected when creating the router. DHCP high availability ---------------------- Set the following configuration options in file ``/etc/neutron/neutron.conf`` so that you get DHCP high availability. .. code-block:: ini dhcp_agents_per_network = 2 DHCP services are created on availability zones you selected when creating the network. neutron-12.1.1/doc/source/admin/config-dvr-ha-snat.rst0000664000175000017500000001351313553660047022602 0ustar zuulzuul00000000000000.. _config-dvr-snat-ha-ovs: ===================================== Distributed Virtual Routing with VRRP ===================================== :ref:`deploy-ovs-ha-dvr` supports augmentation using Virtual Router Redundancy Protocol (VRRP). Using this configuration, virtual routers support both the ``--distributed`` and ``--ha`` options. Similar to legacy HA routers, DVR/SNAT HA routers provide a quick fail over of the SNAT service to a backup DVR/SNAT router on an l3-agent running on a different node. SNAT high availability is implemented in a manner similar to the :ref:`deploy-lb-ha-vrrp` and :ref:`deploy-ovs-ha-vrrp` examples where ``keepalived`` uses VRRP to provide quick failover of SNAT services. During normal operation, the master router periodically transmits *heartbeat* packets over a hidden project network that connects all HA routers for a particular project. If the DVR/SNAT backup router stops receiving these packets, it assumes failure of the master DVR/SNAT router and promotes itself to master router by configuring IP addresses on the interfaces in the ``snat`` namespace. In environments with more than one backup router, the rules of VRRP are followed to select a new master router. .. warning:: There is a known bug with ``keepalived`` v1.2.15 and earlier which can cause packet loss when ``max_l3_agents_per_router`` is set to 3 or more. Therefore, we recommend that you upgrade to ``keepalived`` v1.2.16 or greater when using this feature. .. note:: Experimental feature or incomplete documentation. Configuration example ~~~~~~~~~~~~~~~~~~~~~ The basic deployment model consists of one controller node, two or more network nodes, and multiple computes nodes. Controller node configuration ----------------------------- #. Add the following to ``/etc/neutron/neutron.conf``: .. code-block:: ini [DEFAULT] core_plugin = ml2 service_plugins = router allow_overlapping_ips = True router_distributed = True l3_ha = True l3_ha_net_cidr = 169.254.192.0/18 max_l3_agents_per_router = 3 When the ``router_distributed = True`` flag is configured, routers created by all users are distributed. Without it, only privileged users can create distributed routers by using ``--distributed True``. Similarly, when the ``l3_ha = True`` flag is configured, routers created by all users default to HA. It follows that with these two flags set to ``True`` in the configuration file, routers created by all users will default to distributed HA routers (DVR HA). The same can explicitly be accomplished by a user with administrative credentials setting the flags in the :command:`neutron router-create` command: .. code-block:: console $ neutron router-create name-of-router --distributed=True --ha=True .. note:: The *max_l3_agents_per_router* determine the number of backup DVR/SNAT routers which will be instantiated. #. Add the following to ``/etc/neutron/plugins/ml2/ml2_conf.ini``: .. code-block:: ini [ml2] type_drivers = flat,vxlan tenant_network_types = vxlan mechanism_drivers = openvswitch,l2population extension_drivers = port_security [ml2_type_flat] flat_networks = external [ml2_type_vxlan] vni_ranges = MIN_VXLAN_ID:MAX_VXLAN_ID Replace ``MIN_VXLAN_ID`` and ``MAX_VXLAN_ID`` with VXLAN ID minimum and maximum values suitable for your environment. .. note:: The first value in the ``tenant_network_types`` option becomes the default project network type when a regular user creates a network. Network nodes ------------- #. Configure the Open vSwitch agent. Add the following to ``/etc/neutron/plugins/ml2/openvswitch_agent.ini``: .. code-block:: ini [ovs] local_ip = TUNNEL_INTERFACE_IP_ADDRESS bridge_mappings = external:br-ex [agent] enable_distributed_routing = True tunnel_types = vxlan l2_population = True Replace ``TUNNEL_INTERFACE_IP_ADDRESS`` with the IP address of the interface that handles VXLAN project networks. #. Configure the L3 agent. Add the following to ``/etc/neutron/l3_agent.ini``: .. code-block:: ini [DEFAULT] ha_vrrp_auth_password = password interface_driver = openvswitch external_network_bridge = agent_mode = dvr_snat .. note:: The ``external_network_bridge`` option intentionally contains no value. Compute nodes ------------- #. Configure the Open vSwitch agent. Add the following to ``/etc/neutron/plugins/ml2/openvswitch_agent.ini``: .. code-block:: ini [ovs] local_ip = TUNNEL_INTERFACE_IP_ADDRESS bridge_mappings = external:br-ex [agent] enable_distributed_routing = True tunnel_types = vxlan l2_population = True [securitygroup] firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver #. Configure the L3 agent. Add the following to ``/etc/neutron/l3_agent.ini``: .. code-block:: ini [DEFAULT] interface_driver = openvswitch external_network_bridge = agent_mode = dvr Replace ``TUNNEL_INTERFACE_IP_ADDRESS`` with the IP address of the interface that handles VXLAN project networks. Keepalived VRRP health check ---------------------------- .. include:: shared/keepalived-vrrp-healthcheck.txt Known limitations ~~~~~~~~~~~~~~~~~ * Migrating a router from distributed only, HA only, or legacy to distributed HA is not supported at this time. The router must be created as distributed HA. The reverse direction is also not supported. You cannot reconfigure a distributed HA router to be only distributed, only HA, or legacy. * There are certain scenarios where l2pop and distributed HA routers do not interact in an expected manner. These situations are the same that affect HA only routers and l2pop. neutron-12.1.1/doc/source/admin/config-sfc.rst0000664000175000017500000002731713553660046021237 0ustar zuulzuul00000000000000.. _adv-config-sfc: ========================= Service function chaining ========================= Service function chain (SFC) essentially refers to the software-defined networking (SDN) version of policy-based routing (PBR). In many cases, SFC involves security, although it can include a variety of other features. Fundamentally, SFC routes packets through one or more service functions instead of conventional routing that routes packets using destination IP address. Service functions essentially emulate a series of physical network devices with cables linking them together. A basic example of SFC involves routing packets from one location to another through a firewall that lacks a "next hop" IP address from a conventional routing perspective. A more complex example involves an ordered series of service functions, each implemented using multiple instances (VMs). Packets must flow through one instance and a hashing algorithm distributes flows across multiple instances at each hop. Architecture ~~~~~~~~~~~~ All OpenStack Networking services and OpenStack Compute instances connect to a virtual network via ports making it possible to create a traffic steering model for service chaining using only ports. Including these ports in a port chain enables steering of traffic through one or more instances providing service functions. A port chain, or service function path, consists of the following: * A set of ports that define the sequence of service functions. * A set of flow classifiers that specify the classified traffic flows entering the chain. If a service function involves a pair of ports, the first port acts as the ingress port of the service function and the second port acts as the egress port. If both ports use the same value, they function as a single virtual bidirectional port. A port chain is a unidirectional service chain. The first port acts as the head of the service function chain and the second port acts as the tail of the service function chain. A bidirectional service function chain consists of two unidirectional port chains. A flow classifier can only belong to one port chain to prevent ambiguity as to which chain should handle packets in the flow. A check prevents such ambiguity. However, you can associate multiple flow classifiers with a port chain because multiple flows can request the same service function path. Currently, SFC lacks support for multi-project service functions. The port chain plug-in supports backing service providers including the OVS driver and a variety of SDN controller drivers. The common driver API enables different drivers to provide different implementations for the service chain path rendering. .. image:: figures/port-chain-architecture-diagram.png :alt: Port chain architecture .. image:: figures/port-chain-diagram.png :alt: Port chain model See the `networking-sfc documentation `_ for more information. Resources ~~~~~~~~~ Port chain ---------- * ``id`` - Port chain ID * ``project_id`` - Project ID * ``name`` - Readable name * ``description`` - Readable description * ``port_pair_groups`` - List of port pair group IDs * ``flow_classifiers`` - List of flow classifier IDs * ``chain_parameters`` - Dictionary of chain parameters A port chain consists of a sequence of port pair groups. Each port pair group is a hop in the port chain. A group of port pairs represents service functions providing equivalent functionality. For example, a group of firewall service functions. A flow classifier identifies a flow. A port chain can contain multiple flow classifiers. Omitting the flow classifier effectively prevents steering of traffic through the port chain. The ``chain_parameters`` attribute contains one or more parameters for the port chain. Currently, it only supports a correlation parameter that defaults to ``mpls`` for consistency with Open vSwitch (OVS) capabilities. Future values for the correlation parameter may include the network service header (NSH). Port pair group --------------- * ``id`` - Port pair group ID * ``project_id`` - Project ID * ``name`` - Readable name * ``description`` - Readable description * ``port_pairs`` - List of service function port pairs A port pair group may contain one or more port pairs. Multiple port pairs enable load balancing/distribution over a set of functionally equivalent service functions. Port pair --------- * ``id`` - Port pair ID * ``project_id`` - Project ID * ``name`` - Readable name * ``description`` - Readable description * ``ingress`` - Ingress port * ``egress`` - Egress port * ``service_function_parameters`` - Dictionary of service function parameters A port pair represents a service function instance that includes an ingress and egress port. A service function containing a bidirectional port uses the same ingress and egress port. The ``service_function_parameters`` attribute includes one or more parameters for the service function. Currently, it only supports a correlation parameter that determines association of a packet with a chain. This parameter defaults to ``none`` for legacy service functions that lack support for correlation such as the NSH. If set to ``none``, the data plane implementation must provide service function proxy functionality. Flow classifier --------------- * ``id`` - Flow classifier ID * ``project_id`` - Project ID * ``name`` - Readable name * ``description`` - Readable description * ``ethertype`` - Ethertype (IPv4/IPv6) * ``protocol`` - IP protocol * ``source_port_range_min`` - Minimum source protocol port * ``source_port_range_max`` - Maximum source protocol port * ``destination_port_range_min`` - Minimum destination protocol port * ``destination_port_range_max`` - Maximum destination protocol port * ``source_ip_prefix`` - Source IP address or prefix * ``destination_ip_prefix`` - Destination IP address or prefix * ``logical_source_port`` - Source port * ``logical_destination_port`` - Destination port * ``l7_parameters`` - Dictionary of L7 parameters A combination of the source attributes defines the source of the flow. A combination of the destination attributes defines the destination of the flow. The ``l7_parameters`` attribute is a place holder that may be used to support flow classification using layer 7 fields, such as a URL. If unspecified, the ``logical_source_port`` and ``logical_destination_port`` attributes default to ``none``, the ``ethertype`` attribute defaults to ``IPv4``, and all other attributes default to a wildcard value. Operations ~~~~~~~~~~ Create a port chain ------------------- The following example uses the ``openstack`` command-line interface (CLI) to create a port chain consisting of three service function instances to handle HTTP (TCP) traffic flows from 192.0.2.11:1000 to 198.51.100.11:80. * Instance 1 * Name: vm1 * Function: Firewall * Port pair: [p1, p2] * Instance 2 * Name: vm2 * Function: Firewall * Port pair: [p3, p4] * Instance 3 * Name: vm3 * Function: Intrusion detection system (IDS) * Port pair: [p5, p6] .. note:: The example network ``net1`` must exist before creating ports on it. #. Source the credentials of the project that owns the ``net1`` network. #. Create ports on network ``net1`` and record the UUID values. .. code-block:: console $ openstack port create p1 --network net1 $ openstack port create p2 --network net1 $ openstack port create p3 --network net1 $ openstack port create p4 --network net1 $ openstack port create p5 --network net1 $ openstack port create p6 --network net1 #. Launch service function instance ``vm1`` using ports ``p1`` and ``p2``, ``vm2`` using ports ``p3`` and ``p4``, and ``vm3`` using ports ``p5`` and ``p6``. .. code-block:: console $ openstack server create --nic port-id=P1_ID --nic port-id=P2_ID vm1 $ openstack server create --nic port-id=P3_ID --nic port-id=P4_ID vm2 $ openstack server create --nic port-id=P5_ID --nic port-id=P6_ID vm3 Replace ``P1_ID``, ``P2_ID``, ``P3_ID``, ``P4_ID``, ``P5_ID``, and ``P6_ID`` with the UUIDs of the respective ports. .. note:: This command requires additional options to successfully launch an instance. See the `CLI reference `_ for more information. Alternatively, you can launch each instance with one network interface and attach additional ports later. #. Create flow classifier ``FC1`` that matches the appropriate packet headers. .. code-block:: console $ openstack sfc flow classifier create \ --description "HTTP traffic from 192.0.2.11 to 198.51.100.11" \ --ethertype IPv4 \ --source-ip-prefix 192.0.2.11/32 \ --destination-ip-prefix 198.51.100.11/32 \ --protocol tcp \ --source-port 1000:1000 \ --destination-port 80:80 FC1 .. note:: When using the (default) OVS driver, the ``--logical-source-port`` parameter is also required #. Create port pair ``PP1`` with ports ``p1`` and ``p2``, ``PP2`` with ports ``p3`` and ``p4``, and ``PP3`` with ports ``p5`` and ``p6``. .. code-block:: console $ openstack sfc port pair create \ --description "Firewall SF instance 1" \ --ingress p1 \ --egress p2 PP1 $ openstack sfc port pair create \ --description "Firewall SF instance 2" \ --ingress p3 \ --egress p4 PP2 $ openstack sfc port pair create \ --description "IDS SF instance" \ --ingress p5 \ --egress p6 PP3 #. Create port pair group ``PPG1`` with port pair ``PP1`` and ``PP2`` and ``PPG2`` with port pair ``PP3``. .. code-block:: console $ openstack sfc port pair group create \ --port-pair PP1 --port-pair PP2 PPG1 $ openstack sfc port pair group create \ --port-pair PP3 PPG2 .. note:: You can repeat the ``--port-pair`` option for multiple port pairs of functionally equivalent service functions. #. Create port chain ``PC1`` with port pair groups ``PPG1`` and ``PPG2`` and flow classifier ``FC1``. .. code-block:: console $ openstack sfc port chain create \ --port-pair-group PPG1 --port-pair-group PPG2 \ --flow-classifier FC1 PC1 .. note:: You can repeat the ``--port-pair-group`` option to specify additional port pair groups in the port chain. A port chain must contain at least one port pair group. You can repeat the ``--flow-classifier`` option to specify multiple flow classifiers for a port chain. Each flow classifier identifies a flow. Update a port chain or port pair group -------------------------------------- * Use the :command:`openstack sfc port chain set` command to dynamically add or remove port pair groups or flow classifiers on a port chain. * For example, add port pair group ``PPG3`` to port chain ``PC1``: .. code-block:: console $ openstack sfc port chain set \ --port-pair-group PPG1 --port-pair-group PPG2 --port-pair-group PPG3 \ --flow-classifier FC1 PC1 * For example, add flow classifier ``FC2`` to port chain ``PC1``: .. code-block:: console $ openstack sfc port chain set \ --port-pair-group PPG1 --port-pair-group PPG2 \ --flow-classifier FC1 --flow-classifier FC2 PC1 SFC steers traffic matching the additional flow classifier to the port pair groups in the port chain. * Use the :command:`openstack sfc port pair group set` command to perform dynamic scale-out or scale-in operations by adding or removing port pairs on a port pair group. .. code-block:: console $ openstack sfc port pair group set \ --port-pair PP1 --port-pair PP2 --port-pair PP4 PPG1 SFC performs load balancing/distribution over the additional service functions in the port pair group. neutron-12.1.1/doc/source/admin/config-bgp-dynamic-routing.rst0000664000175000017500000013545613553660047024350 0ustar zuulzuul00000000000000.. _config-bgp-dynamic-routing: =================== BGP dynamic routing =================== BGP dynamic routing enables advertisement of self-service (private) network prefixes to physical network devices that support BGP such as routers, thus removing the conventional dependency on static routes. The feature relies on :ref:`address scopes ` and requires knowledge of their operation for proper deployment. BGP dynamic routing consists of a service plug-in and an agent. The service plug-in implements the Networking service extension and the agent manages BGP peering sessions. A cloud administrator creates and configures a BGP speaker using the CLI or API and manually schedules it to one or more hosts running the agent. Agents can reside on hosts with or without other Networking service agents. Prefix advertisement depends on the binding of external networks to a BGP speaker and the address scope of external and internal IP address ranges or subnets. .. image:: figures/bgp-dynamic-routing-overview.png :alt: BGP dynamic routing overview .. note:: Although self-service networks generally use private IP address ranges (RFC1918) for IPv4 subnets, BGP dynamic routing can advertise any IPv4 address ranges. Example configuration ~~~~~~~~~~~~~~~~~~~~~ The example configuration involves the following components: * One BGP agent. * One address scope containing IP address range 203.0.113.0/24 for provider networks, and IP address ranges 192.0.2.0/25 and 192.0.2.128/25 for self-service networks. * One provider network using IP address range 203.0.113.0/24. * Three self-service networks. * Self-service networks 1 and 2 use IP address ranges inside of the address scope. * Self-service network 3 uses a unique IP address range 198.51.100.0/24 to demonstrate that the BGP speaker does not advertise prefixes outside of address scopes. * Three routers. Each router connects one self-service network to the provider network. * Router 1 contains IP addresses 203.0.113.11 and 192.0.2.1 * Router 2 contains IP addresses 203.0.113.12 and 192.0.2.129 * Router 3 contains IP addresses 203.0.113.13 and 198.51.100.1 .. note:: The example configuration assumes sufficient knowledge about the Networking service, routing, and BGP. For basic deployment of the Networking service, consult one of the :ref:`deploy`. For more information on BGP, see `RFC 4271 `_. Controller node --------------- * In the ``neutron.conf`` file, enable the conventional layer-3 and BGP dynamic routing service plug-ins: .. code-block:: ini [DEFAULT] service_plugins = neutron_dynamic_routing.services.bgp.bgp_plugin.BgpPlugin,neutron.services.l3_router.l3_router_plugin.L3RouterPlugin Agent nodes ----------- * In the ``bgp_dragent.ini`` file: * Configure the driver. .. code-block:: ini [BGP] bgp_speaker_driver = neutron_dynamic_routing.services.bgp.agent.driver.ryu.driver.RyuBgpDriver .. note:: The agent currently only supports the Ryu BGP driver. * Configure the router ID. .. code-block:: ini [BGP] bgp_router_id = ROUTER_ID Replace ``ROUTER_ID`` with a suitable unique 32-bit number, typically an IPv4 address on the host running the agent. For example, 192.0.2.2. Verify service operation ------------------------ #. Source the administrative project credentials. #. Verify presence and operation of each BGP dynamic routing agent. .. code-block:: console $ neutron agent-list --agent-type="BGP dynamic routing agent" +--------------------------------------+---------------------------+------------+-------------------+-------+----------------+---------------------------+ | id | agent_type | host | availability_zone | alive | admin_state_up | binary | +--------------------------------------+---------------------------+------------+-------------------+-------+----------------+---------------------------+ | 37729181-2224-48d8-89ef-16eca8e2f77e | BGP dynamic routing agent | controller | | :-) | True | neutron-bgp-dragent | +--------------------------------------+---------------------------+------------+-------------------+-------+----------------+---------------------------+ Create the address scope and subnet pools ----------------------------------------- #. Create an address scope. The provider (external) and self-service networks must belong to the same address scope for the agent to advertise those self-service network prefixes. .. code-block:: console $ openstack address scope create --share --ip-version 4 bgp +------------+--------------------------------------+ | Field | Value | +------------+--------------------------------------+ | headers | | | id | f71c958f-dbe8-49a2-8fb9-19c5f52a37f1 | | ip_version | 4 | | name | bgp | | project_id | 86acdbd1d72745fd8e8320edd7543400 | | shared | True | +------------+--------------------------------------+ #. Create subnet pools. The provider and self-service networks use different pools. * Create the provider network pool. .. code-block:: console $ openstack subnet pool create --pool-prefix 203.0.113.0/24 \ --address-scope bgp provider +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | address_scope_id | f71c958f-dbe8-49a2-8fb9-19c5f52a37f1 | | created_at | 2017-01-12T14:58:57Z | | default_prefixlen | 8 | | default_quota | None | | description | | | headers | | | id | 63532225-b9a0-445a-9935-20a15f9f68d1 | | ip_version | 4 | | is_default | False | | max_prefixlen | 32 | | min_prefixlen | 8 | | name | provider | | prefixes | 203.0.113.0/24 | | project_id | 86acdbd1d72745fd8e8320edd7543400 | | revision_number | 1 | | shared | False | | tags | [] | | updated_at | 2017-01-12T14:58:57Z | +-------------------+--------------------------------------+ * Create the self-service network pool. .. code-block:: console $ openstack subnet pool create --pool-prefix 192.0.2.0/25 \ --pool-prefix 192.0.2.128/25 --address-scope bgp \ --share selfservice +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | address_scope_id | f71c958f-dbe8-49a2-8fb9-19c5f52a37f1 | | created_at | 2017-01-12T15:02:31Z | | default_prefixlen | 8 | | default_quota | None | | description | | | headers | | | id | 8d8270b1-b194-4b7e-914c-9c741dcbd49b | | ip_version | 4 | | is_default | False | | max_prefixlen | 32 | | min_prefixlen | 8 | | name | selfservice | | prefixes | 192.0.2.0/25, 192.0.2.128/25 | | project_id | 86acdbd1d72745fd8e8320edd7543400 | | revision_number | 1 | | shared | True | | tags | [] | | updated_at | 2017-01-12T15:02:31Z | +-------------------+--------------------------------------+ Create the provider and self-service networks --------------------------------------------- #. Create the provider network. .. code-block:: console $ openstack network create provider --external --provider-physical-network \ provider --provider-network-type flat Created a new network: +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2016-12-21T08:47:41Z | | description | | | headers | | | id | 190ca651-2ee3-4a4b-891f-dedda47974fe | | ipv4_address_scope | None | | ipv6_address_scope | None | | is_default | False | | mtu | 1450 | | name | provider | | port_security_enabled | True | | project_id | c961a8f6d3654657885226378ade8220 | | provider:network_type | flat | | provider:physical_network | provider | | provider:segmentation_id | 66 | | revision_number | 3 | | router:external | External | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2016-12-21T08:47:41Z | +---------------------------+--------------------------------------+ #. Create a subnet on the provider network using an IP address range from the provider subnet pool. .. code-block:: console $ neutron subnet-create --name provider --subnetpool provider \ --prefixlen 24 --allocation-pool start=203.0.113.11,end=203.0.113.254 \ --gateway 203.0.113.1 provider Created a new subnet: +-------------------+---------------------------------------------------+ | Field | Value | +-------------------+---------------------------------------------------+ | allocation_pools | {"start": "203.0.113.11", "end": "203.0.113.254"} | | cidr | 203.0.113.0/24 | | created_at | 2016-03-17T23:17:16 | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 203.0.113.1 | | host_routes | | | id | 8ed65d41-2b2a-4f3a-9f92-45adb266e01a | | ip_version | 4 | | ipv6_address_mode | | | ipv6_ra_mode | | | name | provider | | network_id | 68ec148c-181f-4656-8334-8f4eb148689d | | subnetpool_id | 3771c0e7-7096-46d3-a3bd-699c58e70259 | | tags | [] | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | | updated_at | 2016-03-17T23:17:16 | +-------------------+---------------------------------------------------+ .. note:: The IP address allocation pool starting at ``.11`` improves clarity of the diagrams. You can safely omit it. #. Create the self-service networks. .. code-block:: console $ openstack network create selfservice1 Created a new network: +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2016-12-21T08:49:38Z | | description | | | headers | | | id | 9d842606-ef3d-4160-9ed9-e03fa63aed96 | | ipv4_address_scope | None | | ipv6_address_scope | None | | mtu | 1450 | | name | selfservice1 | | port_security_enabled | True | | project_id | c961a8f6d3654657885226378ade8220 | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 106 | | revision_number | 3 | | router:external | Internal | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2016-12-21T08:49:38Z | +---------------------------+--------------------------------------+ $ openstack network create selfservice2 Created a new network: +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2016-12-21T08:50:05Z | | description | | | headers | | | id | f85639e1-d23f-438e-b2b1-f40570d86b1c | | ipv4_address_scope | None | | ipv6_address_scope | None | | mtu | 1450 | | name | selfservice2 | | port_security_enabled | True | | project_id | c961a8f6d3654657885226378ade8220 | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 21 | | revision_number | 3 | | router:external | Internal | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2016-12-21T08:50:05Z | +---------------------------+--------------------------------------+ $ openstack network create selfservice3 Created a new network: +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2016-12-21T08:50:35Z | | description | | | headers | | | id | eeccdb82-5cf4-4999-8ab3-e7dc99e7d43b | | ipv4_address_scope | None | | ipv6_address_scope | None | | mtu | 1450 | | name | selfservice3 | | port_security_enabled | True | | project_id | c961a8f6d3654657885226378ade8220 | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 86 | | revision_number | 3 | | router:external | Internal | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2016-12-21T08:50:35Z | +---------------------------+--------------------------------------+ #. Create a subnet on the first two self-service networks using an IP address range from the self-service subnet pool. .. code-block:: console $ neutron subnet-create --name selfservice1 --subnetpool selfservice \ --prefixlen 25 selfservice1 Created a new subnet: +-------------------+----------------------------------------------------+ | Field | Value | +-------------------+----------------------------------------------------+ | allocation_pools | {"start": "192.0.2.2", "end": "192.0.2.127"} | | cidr | 192.0.2.0/25 | | created_at | 2016-03-17T23:20:20 | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 198.51.100.1 | | host_routes | | | id | 8edd3dc2-df40-4d71-816e-a4586d61c809 | | ip_version | 4 | | ipv6_address_mode | | | ipv6_ra_mode | | | name | selfservice1 | | network_id | be79de1e-5f56-11e6-9dfb-233e41cec48c | | revision_number | 1 | | subnetpool_id | c7e9737a-cfd3-45b5-a861-d1cee1135a92 | | tags | [] | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | | updated_at | 2016-03-17T23:20:20 | +-------------------+----------------------------------------------------+ $ neutron subnet-create --name selfservice2 --subnetpool selfservice \ --prefixlen 25 selfservice2 Created a new subnet: +-------------------+------------------------------------------------+ | Field | Value | +-------------------+------------------------------------------------+ | allocation_pools | {"start": "192.0.2.130", "end": "192.0.2.254"} | | cidr | 192.0.2.128/25 | | created_at | 2016-03-17T23:20:20 | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 192.0.2.129 | | host_routes | | | id | 8edd3dc2-df40-4d71-816e-a4586d61c809 | | ip_version | 4 | | ipv6_address_mode | | | ipv6_ra_mode | | | name | selfservice2 | | network_id | c1fd9846-5f56-11e6-a8ac-0f998d9cc0a2 | | revision_number | 1 | | subnetpool_id | c7e9737a-cfd3-45b5-a861-d1cee1135a92 | | tags | [] | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | | updated_at | 2016-03-17T23:20:20 | +-------------------+------------------------------------------------+ #. Create a subnet on the last self-service network using an IP address range outside of the address scope. .. code-block:: console $ neutron subnet-create --name subnet3 selfservice3 198.51.100.0/24 Created a new subnet: +-------------------+----------------------------------------------------+ | Field | Value | +-------------------+----------------------------------------------------+ | allocation_pools | {"start": "198.51.100.2", "end": "198.51.100.254"} | | cidr | 198.51.100.0/24 | | created_at | 2016-03-17T23:20:20 | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 198.51.100.1 | | host_routes | | | id | cd9f9156-5f59-11e6-aeec-172ec7ee939a | | ip_version | 4 | | ipv6_address_mode | | | ipv6_ra_mode | | | name | selfservice3 | | network_id | c283dc1c-5f56-11e6-bfb6-efc30e1eb73b | | revision_number | 1 | | subnetpool_id | | | tags | [] | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | | updated_at | 2016-03-17T23:20:20 | +-------------------+----------------------------------------------------+ Create and configure the routers -------------------------------- #. Create the routers. .. code-block:: console $ openstack router create router1 +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2017-01-10T13:15:19Z | | description | | | distributed | False | | external_gateway_info | null | | flavor_id | None | | ha | False | | headers | | | id | 3f6f4ef8-63be-11e6-bbb3-2fbcef363ab8 | | name | router1 | | project_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | | revision_number | 1 | | routes | | | status | ACTIVE | | tags | [] | | updated_at | 2017-01-10T13:15:19Z | +-------------------------+--------------------------------------+ $ openstack router create router2 +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2017-01-10T13:15:19Z | | description | | | distributed | False | | external_gateway_info | null | | flavor_id | None | | ha | False | | headers | | | id | 3fd21a60-63be-11e6-9c95-5714c208c499 | | name | router2 | | project_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | | revision_number | 1 | | routes | | | status | ACTIVE | | tags | [] | | updated_at | 2017-01-10T13:15:19Z | +-------------------------+--------------------------------------+ $ openstack router create router3 +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2017-01-10T13:15:19Z | | description | | | distributed | False | | external_gateway_info | null | | flavor_id | None | | ha | False | | headers | | | id | 40069a4c-63be-11e6-9ecc-e37c1eaa7e84 | | name | router3 | | project_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | | revision_number | 1 | | routes | | | status | ACTIVE | | tags | [] | | updated_at | 2017-01-10T13:15:19Z | +-------------------------+--------------------------------------+ #. For each router, add one self-service subnet as an interface on the router. .. code-block:: console $ neutron router-interface-add router1 selfservice1 Added interface 90e3880a-5f5c-11e6-914c-9f3e20c8c151 to router router1. $ neutron router-interface-add router2 selfservice2 Added interface 91628362-5f5c-11e6-826a-7322fb03a821 to router router2. $ neutron router-interface-add router3 selfservice3 Added interface 91d51044-5f5c-11e6-bf55-ffd180541cc2 to router router3. #. Add the provider network as a gateway on each router. .. code-block:: console $ neutron router-gateway-set router1 provider Set gateway for router router1 $ neutron router-gateway-set router2 provider Set gateway for router router2 $ neutron router-gateway-set router3 provider Set gateway for router router3 Create and configure the BGP speaker ------------------------------------ The BGP speaker advertises the next-hop IP address for eligible self-service networks and floating IP addresses for instances using those networks. #. Create the BGP speaker. .. code-block:: console $ neutron bgp-speaker-create --ip-version 4 \ --local-as LOCAL_AS bgpspeaker Created a new bgp_speaker: +-----------------------------------+--------------------------------------+ | Field | Value | +-----------------------------------+--------------------------------------+ | advertise_floating_ip_host_routes | True | | advertise_tenant_networks | True | | id | 5f227f14-4f46-4eca-9524-fc5a1eabc358 | | ip_version | 4 | | local_as | 1234 | | name | bgpspeaker | | networks | | | peers | | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | +-----------------------------------+--------------------------------------+ Replace ``LOCAL_AS`` with an appropriate local autonomous system number. The example configuration uses AS 1234. #. A BGP speaker requires association with a provider network to determine eligible prefixes. The association builds a list of all virtual routers with gateways on provider and self-service networks in the same address scope so the BGP speaker can advertise self-service network prefixes with the corresponding router as the next-hop IP address. Associate the BGP speaker with the provider network. .. code-block:: console $ neutron bgp-speaker-network-add bgpspeaker provider Added network provider to BGP speaker bgpspeaker. #. Verify association of the provider network with the BGP speaker. .. code-block:: console $ neutron bgp-speaker-show bgpspeaker +-----------------------------------+--------------------------------------+ | Field | Value | +-----------------------------------+--------------------------------------+ | advertise_floating_ip_host_routes | True | | advertise_tenant_networks | True | | id | 5f227f14-4f46-4eca-9524-fc5a1eabc358 | | ip_version | 4 | | local_as | 1234 | | name | bgpspeaker | | networks | 68ec148c-181f-4656-8334-8f4eb148689d | | peers | | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | +-----------------------------------+--------------------------------------+ #. Verify the prefixes and next-hop IP addresses that the BGP speaker advertises. .. code-block:: console $ neutron bgp-speaker-advertiseroute-list bgpspeaker +-----------------+--------------+ | destination | next_hop | +-----------------+--------------+ | 192.0.2.0/25 | 203.0.113.11 | | 192.0.2.128/25 | 203.0.113.12 | +-----------------+--------------+ #. Create a BGP peer. .. code-block:: console $ neutron bgp-peer-create --peer-ip 192.0.2.1 \ --remote-as REMOTE_AS bgppeer Created a new bgp_peer: +-----------+--------------------------------------+ | Field | Value | +-----------+--------------------------------------+ | auth_type | none | | id | 35c89ca0-ac5a-4298-a815-0b073c2362e9 | | name | bgppeer | | peer_ip | 192.0.2.1 | | remote_as | 4321 | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | +-----------+--------------------------------------+ Replace ``REMOTE_AS`` with an appropriate remote autonomous system number. The example configuration uses AS 4321 which triggers EBGP peering. .. note:: The host containing the BGP agent must have layer-3 connectivity to the provider router. #. Add a BGP peer to the BGP speaker. .. code-block:: console $ neutron bgp-speaker-peer-add bgpspeaker bgppeer Added BGP peer bgppeer to BGP speaker bgpspeaker. #. Verify addition of the BGP peer to the BGP speaker. .. code-block:: console $ neutron bgp-speaker-show bgpspeaker +-----------------------------------+--------------------------------------+ | Field | Value | +-----------------------------------+--------------------------------------+ | advertise_floating_ip_host_routes | True | | advertise_tenant_networks | True | | id | 5f227f14-4f46-4eca-9524-fc5a1eabc358 | | ip_version | 4 | | local_as | 1234 | | name | bgpspeaker | | networks | 68ec148c-181f-4656-8334-8f4eb148689d | | peers | 35c89ca0-ac5a-4298-a815-0b073c2362e9 | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | +-----------------------------------+--------------------------------------+ .. note:: After creating a peering session, you cannot change the local or remote autonomous system numbers. Schedule the BGP speaker to an agent ------------------------------------ #. Unlike most agents, BGP speakers require manual scheduling to an agent. BGP speakers only form peering sessions and begin prefix advertisement after scheduling to an agent. Schedule the BGP speaker to agent ``37729181-2224-48d8-89ef-16eca8e2f77e``. .. code-block:: console $ neutron bgp-dragent-speaker-add 37729181-2224-48d8-89ef-16eca8e2f77e bgpspeaker Associated BGP speaker bgpspeaker to the Dynamic Routing agent. #. Verify scheduling of the BGP speaker to the agent. .. code-block:: console $ neutron bgp-dragent-list-hosting-speaker bgpspeaker +--------------------------------------+------------+----------------+-------+ | id | host | admin_state_up | alive | +--------------------------------------+------------+----------------+-------+ | 37729181-2224-48d8-89ef-16eca8e2f77e | controller | True | :-) | +--------------------------------------+------------+----------------+-------+ $ neutron bgp-speaker-list-on-dragent 37729181-2224-48d8-89ef-16eca8e2f77e +--------------------------------------+------------+----------+------------+ | id | name | local_as | ip_version | +--------------------------------------+------------+----------+------------+ | 5f227f14-4f46-4eca-9524-fc5a1eabc358 | bgpspeaker | 1234 | 4 | +--------------------------------------+------------+----------+------------+ Prefix advertisement ~~~~~~~~~~~~~~~~~~~~ BGP dynamic routing advertises prefixes for self-service networks and host routes for floating IP addresses. Advertisement of a self-service network requires satisfying the following conditions: * The external and self-service network reside in the same address scope. * The router contains an interface on the self-service subnet and a gateway on the external network. * The BGP speaker associates with the external network that provides a gateway on the router. * The BGP speaker has the ``advertise_tenant_networks`` attribute set to ``True``. .. image:: figures/bgp-dynamic-routing-example1.png :alt: Example of prefix advertisements with self-service networks Advertisement of a floating IP address requires satisfying the following conditions: * The router with the floating IP address binding contains a gateway on an external network with the BGP speaker association. * The BGP speaker has the ``advertise_floating_ip_host_routes`` attribute set to ``True``. .. image:: figures/bgp-dynamic-routing-example2.png :alt: Example of prefix advertisements with floating IP addresses Operation with Distributed Virtual Routers (DVR) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In deployments using DVR, the BGP speaker advertises floating IP addresses and self-service networks differently. For floating IP addresses, the BGP speaker advertises the floating IP agent gateway on the corresponding compute node as the next-hop IP address. For self-service networks using SNAT, the BGP speaker advertises the DVR SNAT node as the next-hop IP address. For example, consider the following components: #. A provider network using IP address range 203.0.113.0/24, and supporting floating IP addresses 203.0.113.101, 203.0.113.102, and 203.0.113.103. #. A self-service network using IP address range 198.51.100.0/24. #. The SNAT gateway resides on 203.0.113.11. #. The floating IP agent gateways (one per compute node) reside on 203.0.113.12, 203.0.113.13, and 203.0.113.14. #. Three instances, one per compute node, each with a floating IP address. .. code-block:: console $ neutron bgp-speaker-advertiseroute-list bgpspeaker +------------------+--------------+ | destination | next_hop | +------------------+--------------+ | 198.51.100.0/24 | 203.0.113.11 | | 203.0.113.101/32 | 203.0.113.12 | | 203.0.113.102/32 | 203.0.113.13 | | 203.0.113.103/32 | 203.0.113.14 | +------------------+--------------+ .. note:: DVR lacks support for routing directly to a fixed IP address via the floating IP agent gateway port and thus prevents the BGP speaker from advertising fixed IP addresses. You can also identify floating IP agent gateways in your environment to assist with verifying operation of the BGP speaker. .. code-block:: console $ neutron port-list --device_owner="network:floatingip_agent_gateway" +--------------------------------------+------+-------------------+--------------------------------------------------------------------------------------------------------+ | id | name | mac_address | fixed_ips | +--------------------------------------+------+-------------------+--------------------------------------------------------------------------------------------------------+ | 87cf2970-4970-462e-939e-00e808295dfa | | fa:16:3e:7c:68:e3 | {"subnet_id": "8ed65d41-2b2a-4f3a-9f92-45adb266e01a", "ip_address": "203.0.113.12"} | | 8d218440-0d2e-49d0-8a7b-3266a6146dc1 | | fa:16:3e:9d:78:cf | {"subnet_id": "8ed65d41-2b2a-4f3a-9f92-45adb266e01a", "ip_address": "203.0.113.13"} | | 87cf2970-4970-462e-939e-00e802281dfa | | fa:16:3e:6b:18:e0 | {"subnet_id": "8ed65d41-2b2a-4f3a-9f92-45adb266e01a", "ip_address": "203.0.113.14"} | +--------------------------------------+------+-------------------+--------------------------------------------------------------------------------------------------------+ IPv6 ~~~~ BGP dynamic routing supports peering via IPv6 and advertising IPv6 prefixes. * To enable peering via IPv6, create a BGP peer and use an IPv6 address for ``peer_ip``. * To enable advertising IPv6 prefixes, create an address scope with ``ip_version=6`` and a BGP speaker with ``ip_version=6``. .. note:: DVR with IPv6 functions similarly to DVR with IPv4. High availability ~~~~~~~~~~~~~~~~~ BGP dynamic routing supports scheduling a BGP speaker to multiple agents which effectively multiplies prefix advertisements to the same peer. If an agent fails, the peer continues to receive advertisements from one or more operational agents. #. Show available dynamic routing agents. .. code-block:: console $ neutron agent-list --agent-type="BGP dynamic routing agent" +--------------------------------------+---------------------------+----------+-------------------+-------+----------------+---------------------------+ | id | agent_type | host | availability_zone | alive | admin_state_up | binary | +--------------------------------------+---------------------------+----------+-------------------+-------+----------------+---------------------------+ | 37729181-2224-48d8-89ef-16eca8e2f77e | BGP dynamic routing agent | bgp-ha1 | | :-) | True | neutron-bgp-dragent | | 1a2d33bb-9321-30a2-76ab-22eff3d2f56a | BGP dynamic routing agent | bgp-ha2 | | :-) | True | neutron-bgp-dragent | +--------------------------------------+---------------------------+----------+-------------------+-------+----------------+---------------------------+ #. Schedule BGP speaker to multiple agents. .. code-block:: console $ neutron bgp-dragent-speaker-add 37729181-2224-48d8-89ef-16eca8e2f77e bgpspeaker Associated BGP speaker bgpspeaker to the Dynamic Routing agent. $ neutron bgp-dragent-speaker-add 1a2d33bb-9321-30a2-76ab-22eff3d2f56a bgpspeaker Associated BGP speaker bgpspeaker to the Dynamic Routing agent. $ neutron bgp-dragent-list-hosting-speaker bgpspeaker +--------------------------------------+---------+----------------+-------+ | id | host | admin_state_up | alive | +--------------------------------------+---------+----------------+-------+ | 37729181-2224-48d8-89ef-16eca8e2f77e | bgp-ha1 | True | :-) | | 1a2d33bb-9321-30a2-76ab-22eff3d2f56a | bgp-ha2 | True | :-) | +--------------------------------------+---------+----------------+-------+ $ neutron bgp-speaker-list-on-dragent 37729181-2224-48d8-89ef-16eca8e2f77e +--------------------------------------+------------+----------+------------+ | id | name | local_as | ip_version | +--------------------------------------+------------+----------+------------+ | 5f227f14-4f46-4eca-9524-fc5a1eabc358 | bgpspeaker | 1234 | 4 | +--------------------------------------+------------+----------+------------+ $ neutron bgp-speaker-list-on-dragent 1a2d33bb-9321-30a2-76ab-22eff3d2f56a +--------------------------------------+------------+----------+------------+ | id | name | local_as | ip_version | +--------------------------------------+------------+----------+------------+ | 5f227f14-4f46-4eca-9524-fc5a1eabc358 | bgpspeaker | 1234 | 4 | +--------------------------------------+------------+----------+------------+ neutron-12.1.1/doc/source/admin/deploy.rst0000664000175000017500000001137613553660047020514 0ustar zuulzuul00000000000000.. _deploy: =================== Deployment examples =================== The following deployment examples provide building blocks of increasing architectural complexity using the Networking service reference architecture which implements the Modular Layer 2 (ML2) plug-in and either the Open vSwitch (OVS) or Linux bridge mechanism drivers. Both mechanism drivers support the same basic features such as provider networks, self-service networks, and routers. However, more complex features often require a particular mechanism driver. Thus, you should consider the requirements (or goals) of your cloud before choosing a mechanism driver. After choosing a :ref:`mechanism driver `, the deployment examples generally include the following building blocks: #. Provider (public/external) networks using IPv4 and IPv6 #. Self-service (project/private/internal) networks including routers using IPv4 and IPv6 #. High-availability features #. Other features such as BGP dynamic routing Prerequisites ~~~~~~~~~~~~~ Prerequisites, typically hardware requirements, generally increase with each building block. Each building block depends on proper deployment and operation of prior building blocks. For example, the first building block (provider networks) only requires one controller and two compute nodes, the second building block (self-service networks) adds a network node, and the high-availability building blocks typically add a second network node for a total of five nodes. Each building block could also require additional infrastructure or changes to existing infrastructure such as networks. For basic configuration of prerequisites, see the `Queens Install Tutorials and Guides <../install/>`__. .. note:: Example commands using the ``openstack`` client assume version 3.2.0 or higher. Nodes ----- The deployment examples refer one or more of the following nodes: * Controller: Contains control plane components of OpenStack services and their dependencies. * Two network interfaces: management and provider. * Operational SQL server with databases necessary for each OpenStack service. * Operational message queue service. * Operational OpenStack Identity (keystone) service. * Operational OpenStack Image Service (glance). * Operational management components of the OpenStack Compute (nova) service with appropriate configuration to use the Networking service. * OpenStack Networking (neutron) server service and ML2 plug-in. * Network: Contains the OpenStack Networking service layer-3 (routing) component. High availability options may include additional components. * Three network interfaces: management, overlay, and provider. * OpenStack Networking layer-2 (switching) agent, layer-3 agent, and any dependencies. * Compute: Contains the hypervisor component of the OpenStack Compute service and the OpenStack Networking layer-2, DHCP, and metadata components. High-availability options may include additional components. * Two network interfaces: management and provider. * Operational hypervisor components of the OpenStack Compute (nova) service with appropriate configuration to use the Networking service. * OpenStack Networking layer-2 agent, DHCP agent, metadata agent, and any dependencies. Each building block defines the quantity and types of nodes including the components on each node. .. note:: You can virtualize these nodes for demonstration, training, or proof-of-concept purposes. However, you must use physical hosts for evaluation of performance or scaling. Networks and network interfaces ------------------------------- The deployment examples refer to one or more of the following networks and network interfaces: * Management: Handles API requests from clients and control plane traffic for OpenStack services including their dependencies. * Overlay: Handles self-service networks using an overlay protocol such as VXLAN or GRE. * Provider: Connects virtual and physical networks at layer-2. Typically uses physical network infrastructure for switching/routing traffic to external networks such as the Internet. .. note:: For best performance, 10+ Gbps physical network infrastructure should support jumbo frames. For illustration purposes, the configuration examples typically reference the following IP address ranges: * Provider network 1: * IPv4: 203.0.113.0/24 * IPv6: fd00:203:0:113::/64 * Provider network 2: * IPv4: 192.0.2.0/24 * IPv6: fd00:192:0:2::/64 * Self-service networks: * IPv4: 198.51.100.0/24 in /24 segments * IPv6: fd00:198:51::/48 in /64 segments You may change them to work with your particular network infrastructure. .. _deploy-mechanism-drivers: Mechanism drivers ~~~~~~~~~~~~~~~~~ .. toctree:: :maxdepth: 1 deploy-lb deploy-ovs neutron-12.1.1/doc/source/admin/config.rst0000664000175000017500000000133713553660047020461 0ustar zuulzuul00000000000000.. _config: ============= Configuration ============= .. toctree:: :maxdepth: 2 config-services-agent config-ml2 config-address-scopes config-auto-allocation config-az config-bgp-dynamic-routing config-dhcp-ha config-dns-int config-dns-int-ext-serv config-dns-res config-dvr-ha-snat config-ipam config-ipv6 config-lbaas config-logging config-macvtap config-mtu config-ovs-dpdk config-ovs-offload config-ovsfwdriver config-qos config-rbac config-routed-networks config-sfc config-sriov config-subnet-pools config-service-subnets config-trunking .. note:: For general configuration, see the `Configuration Reference <../configuration/>`_. neutron-12.1.1/doc/source/admin/config-dns-res.rst0000664000175000017500000001136313553660046022031 0ustar zuulzuul00000000000000.. _config-dns-res: ============================ DNS resolution for instances ============================ The Networking service offers several methods to configure name resolution (DNS) for instances. Most deployments should implement case 1 or 2a. Case 2b requires security considerations to prevent leaking internal DNS information to instances. .. note:: All of these setups require the configured DNS resolvers to be reachable from the virtual network in question. So unless the resolvers are located inside the virtual network itself, this implies the need for a router to be attached to that network having an external gateway configured. Case 1: Each virtual network uses unique DNS resolver(s) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In this case, the DHCP agent offers one or more unique DNS resolvers to instances via DHCP on each virtual network. You can configure a DNS resolver when creating or updating a subnet. To configure more than one DNS resolver, repeat the option multiple times. * Configure a DNS resolver when creating a subnet. .. code-block:: console $ openstack subnet create --dns-nameserver DNS_RESOLVER Replace ``DNS_RESOLVER`` with the IP address of a DNS resolver reachable from the virtual network. Repeat the option if you want to specify multiple IP addresses. For example: .. code-block:: console $ openstack subnet create --dns-nameserver 203.0.113.8 --dns-nameserver 198.51.100.53 .. note:: This command requires additional options outside the scope of this content. * Add a DNS resolver to an existing subnet. .. code-block:: console $ openstack subnet set --dns-nameserver DNS_RESOLVER SUBNET_ID_OR_NAME Replace ``DNS_RESOLVER`` with the IP address of a DNS resolver reachable from the virtual network and ``SUBNET_ID_OR_NAME`` with the UUID or name of the subnet. For example, using the ``selfservice`` subnet: .. code-block:: console $ openstack subnet set --dns-nameserver 203.0.113.9 selfservice * Remove all DNS resolvers from a subnet. .. code-block:: console $ openstack subnet set --no-dns-nameservers SUBNET_ID_OR_NAME Replace ``SUBNET_ID_OR_NAME`` with the UUID or name of the subnet. For example, using the ``selfservice`` subnet: .. code-block:: console $ openstack subnet set --no-dns-nameservers selfservice .. note:: You can use this option in combination with the previous one in order to replace all existing DNS resolver addresses with new ones. You can also set the DNS resolver address to ``0.0.0.0`` for IPv4 subnets, or ``::`` for IPv6 subnets, which are special values that indicate to the DHCP agent that it should not announce any DNS resolver at all on the subnet. .. note:: When DNS resolvers are explicitly specified for a subnet this way, that setting will take precedence over the options presented in case 2. Case 2: DHCP agents forward DNS queries from instances ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In this case, the DHCP agent offers the list of all DHCP agent's IP addresses on a subnet as DNS resolver(s) to instances via DHCP on that subnet. The DHCP agent then runs a masquerading forwarding DNS resolver with two possible options to determine where the DNS queries are sent to. .. note:: The DHCP agent will answer queries for names and addresses of instances running within the virtual network directly instead of forwarding them. Case 2a: Queries are forwarded to an explicitly configured set of DNS resolvers ------------------------------------------------------------------------------- In the ``dhcp_agent.ini`` file, configure one or more DNS resolvers. To configure more than one DNS resolver, use a comma between the values. .. code-block:: ini [DEFAULT] dnsmasq_dns_servers = DNS_RESOLVER Replace ``DNS_RESOLVER`` with a list of IP addresses of DNS resolvers reachable from all virtual networks. For example: .. code-block:: ini [DEFAULT] dnsmasq_dns_servers = 203.0.113.8, 198.51.100.53 .. note:: You must configure this option for all eligible DHCP agents and restart them to activate the values. Case 2b: Queries are forwarded to DNS resolver(s) configured on the host ------------------------------------------------------------------------ In this case, the DHCP agent forwards queries from the instances to the DNS resolver(s) configured in the ``resolv.conf`` file on the host running the DHCP agent. This requires these resolvers being reachable from all virtual networks. In the ``dhcp_agent.ini`` file, enable using the DNS resolver(s) configured on the host. .. code-block:: ini [DEFAULT] dnsmasq_local_resolv = True .. note:: You must configure this option for all eligible DHCP agents and restart them to activate this setting. neutron-12.1.1/doc/source/admin/config-ovs-offload.rst0000664000175000017500000003400013553660047022667 0ustar zuulzuul00000000000000.. _config-ovs-offload: ================================ Open vSwitch hardware offloading ================================ The purpose of this page is to describe how to enable Open vSwitch hardware offloading functionality available in OpenStack (using OpenStack Networking). This functionality was first introduced in the OpenStack Pike release. This page intends to serve as a guide for how to configure OpenStack Networking and OpenStack Compute to enable Open vSwitch hardware offloading. The basics ~~~~~~~~~~ Open vSwitch is a production quality, multilayer virtual switch licensed under the open source Apache 2.0 license. It is designed to enable massive network automation through programmatic extension, while still supporting standard management interfaces and protocols. Open vSwitch (OVS) allows Virtual Machines (VM) to communicate with each other and with the outside world. The OVS software based solution is CPU intensive, affecting system performance and preventing fully utilizing available bandwidth. .. list-table:: :header-rows: 1 :widths: 30 90 * - Term - Definition * - PF - Physical Function. The physical Ethernet controller that supports SR-IOV. * - VF - Virtual Function. The virtual PCIe device created from a physical Ethernet controller. * - Representor Port - Virtual network interface similar to SR-IOV port that represents Nova instance. * - First Compute Node - OpenStack Compute Node that can host Compute instances (Virtual Machines). * - Second Compute Node - OpenStack Compute Node that can host Compute instances (Virtual Machines). Supported Ethernet controllers ------------------------------ The following manufacturers are known to work: - Mellanox ConnectX-4 NIC (VLAN Offload) - Mellanox ConnectX-4 Lx/ConnectX-5 NICs (VLAN/VXLAN Offload) For information on **Mellanox Ethernet Cards**, see `Mellanox: Ethernet Cards - Overview `_. Prerequisites ------------- - Linux Kernel >= 4.13 - Open vSwitch >= 2.8 - iproute >= 4.12 - Mellanox NIC .. note:: Mellanox NIC FW that supports Open vSwitch hardware offloading: ConnectX-5 >= 16.21.0338 ConnectX-4 >= 12.18.2000 ConnectX-4 Lx >= 14.21.0338 Using Open vSwitch hardware offloading ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In order to enable Open vSwitch hardware offloading, the following steps are required: #. Enable SR-IOV #. Configure NIC to switchdev mode (relevant Nodes) #. Enable Open vSwitch hardware offloading .. note:: Throughout this guide, ``enp3s0f0`` is used as the PF and ``eth3`` is used as the representor port. These ports may vary in different environments. .. note:: Throughout this guide, we use ``systemctl`` to restart OpenStack services. This is correct for ``systemd`` OS. Other methods to restart services should be used in other environments. Create Compute virtual functions ---------------------------------- Create the VFs for the network interface that will be used for SR-IOV. We use ``enp3s0f0`` as PF, which is also used as the interface for the VLAN provider network and has access to the private networks of all nodes. .. note:: The following steps detail how to create VFs using Mellanox ConnectX-4 and SR-IOV Ethernet cards on an Intel system. Steps may be different for the hardware of your choice. #. Ensure SR-IOV and VT-d are enabled on the system. Enable IOMMU in Linux by adding ``intel_iommu=on`` to kernel parameters, for example, using GRUB. #. On each Compute node, create the VFs: .. code-block:: bash # echo '4' > /sys/class/net/enp3s0f0/device/sriov_numvfs .. note:: A network interface can be used both for PCI passthrough, using the PF, and SR-IOV, using the VFs. If the PF is used, the VF number stored in the ``sriov_numvfs`` file is lost. If the PF is attached again to the operating system, the number of VFs assigned to this interface will be zero. To keep the number of VFs always assigned to this interface, update a relevant file according to your OS. See some examples below: In Ubuntu, modifying the ``/etc/network/interfaces`` file: .. code-block:: ini auto enp3s0f0 iface enp3s0f0 inet dhcp pre-up echo '4' > /sys/class/net/enp3s0f0/device/sriov_numvfs In Red Hat, modifying the ``/sbin/ifup-local`` file: .. code-block:: bash #!/bin/sh if [[ "$1" == "enp3s0f0" ]] then echo '4' > /sys/class/net/enp3s0f0/device/sriov_numvfs fi .. warning:: Alternatively, you can create VFs by passing the ``max_vfs`` to the kernel module of your network interface. However, the ``max_vfs`` parameter has been deprecated, so the PCI /sys interface is the preferred method. You can determine the maximum number of VFs a PF can support: .. code-block:: bash # cat /sys/class/net/enp3s0f0/device/sriov_totalvfs 8 #. Verify that the VFs have been created and are in ``up`` state: .. note:: The PCI bus number of the PF (03:00.0) and VFs (03:00.2 .. 03:00.5) will be used later. .. code-block::bash # lspci | grep Ethernet 03:00.0 Ethernet controller: Mellanox Technologies MT27800 Family [ConnectX-5] 03:00.1 Ethernet controller: Mellanox Technologies MT27800 Family [ConnectX-5] 03:00.2 Ethernet controller: Mellanox Technologies MT27800 Family [ConnectX-5 Virtual Function] 03:00.3 Ethernet controller: Mellanox Technologies MT27800 Family [ConnectX-5 Virtual Function] 03:00.4 Ethernet controller: Mellanox Technologies MT27800 Family [ConnectX-5 Virtual Function] 03:00.5 Ethernet controller: Mellanox Technologies MT27800 Family [ConnectX-5 Virtual Function] .. code-block:: bash # ip link show enp3s0f0 8: enp3s0f0: mtu 1500 qdisc mq state UP mode DEFAULT qlen 1000 link/ether a0:36:9f:8f:3f:b8 brd ff:ff:ff:ff:ff:ff vf 0 MAC 00:00:00:00:00:00, spoof checking on, link-state auto vf 1 MAC 00:00:00:00:00:00, spoof checking on, link-state auto vf 2 MAC 00:00:00:00:00:00, spoof checking on, link-state auto vf 3 MAC 00:00:00:00:00:00, spoof checking on, link-state auto If the interfaces are down, set them to ``up`` before launching a guest, otherwise the instance will fail to spawn: .. code-block:: bash # ip link set enp3s0f0 up Configure Open vSwitch hardware offloading ------------------------------------------ #. Change the e-switch mode from legacy to switchdev on the PF device. This will also create the VF representor network devices in the host OS. .. code-block:: bash # echo 0000:03:00.2 > /sys/bus/pci/drivers/mlx5_core/unbind This tells the driver to unbind VF 03:00.2 .. note:: This should be done for all relevant VFs (in this example 0000:03:00.2 .. 0000:03:00.5) #. Enable Open vSwitch hardware offloading, set PF to switchdev mode and bind VFs back. .. code-block:: bash # sudo devlink dev eswitch set pci/0000:03:00.0 mode switchdev # sudo ethtool -K enp3s0f0 hw-tc-offload on # echo 0000:03:00.2 > /sys/bus/pci/drivers/mlx5_core/bind .. note:: This should be done for all relevant VFs (in this example 0000:03:00.2 .. 0000:03:00.5) #. Restart Open vSwitch .. code-block:: bash # sudo systemctl enable openvswitch.service # sudo ovs-vsctl set Open_vSwitch . other_config:hw-offload=true # sudo systemctl restart openvswitch.service .. note:: The given aging of OVS is given in milliseconds and can be controlled with: .. code-block:: bash # ovs-vsctl set Open_vSwitch . other_config:max-idle=30000 Configure Nodes (VLAN Configuration) ------------------------------------- #. Update ``/etc/neutron/plugins/ml2/ml2_conf.ini`` on Controller nodes .. code-block:: ini [ml2] tenant_network_types = vlan type_drivers = vlan mechanism_drivers = openvswitch .. end #. Update ``/etc/neutron/neutron.conf`` on Controller nodes .. code-block:: ini [DEFAULT] core_plugin = ml2 .. end #. Update ``/etc/nova/nova.conf`` on Controller nodes .. code-block:: ini [filter_scheduler] enabled_filters = PciPassthroughFilter .. end #. Update ``/etc/nova/nova.conf`` on Compute nodes .. code-block:: ini [pci] #VLAN Configuration passthrough_whitelist example passthrough_whitelist ={"'"address"'":"'"*:'"03:00"'.*"'","'"physical_network"'":"'"physnet2"'"} .. end Configure Nodes (VXLAN Configuration) ------------------------------------- #. Update ``/etc/neutron/plugins/ml2/ml2_conf.ini`` on Controller nodes .. code-block:: ini [ml2] tenant_network_types = vxlan type_drivers = vxlan mechanism_drivers = openvswitch .. end #. Update ``/etc/neutron/neutron.conf`` on Controller nodes .. code-block:: ini [DEFAULT] core_plugin = ml2 .. end #. Update ``/etc/nova/nova.conf`` on Controller nodes .. code-block:: ini [filter_scheduler] enabled_filters = PciPassthroughFilter .. end #. Update ``/etc/nova/nova.conf`` on Compute nodes .. note:: VXLAN configuration requires physical_network to be null. .. code-block:: ini [pci] #VLAN Configuration passthrough_whitelist example passthrough_whitelist ={"'"address"'":"'"*:'"03:00"'.*"'","'"physical_network"'":null} .. end #. Restart nova and neutron services .. code-block:: bash # sudo systemctl restart openstack-nova-compute.service # sudo systemctl restart openstack-nova-scheduler.service # sudo systemctl restart neutron-server.service Validate Open vSwitch hardware offloading ----------------------------------------- .. note:: In this example we will bring up two instances on different Compute nodes and send ICMP echo packets between them. Then we will check TCP packets on a representor port and we will see that only the first packet will be shown there. All the rest will be offloaded. #. Create a port ``direct`` on ``private`` network .. code-block:: bash # openstack port create --network private --vnic-type=direct --binding-profile '{"capabilities": ["switchdev"]}' direct_port1 #. Create an instance using the direct port on 'First Compute Node' .. code-block:: bash # openstack server create --flavor m1.small --image mellanox_fedora --nic port-id=direct_port1 vm1 .. note:: In this example, we used Mellanox Image with NIC Drivers that can be downloaded from http://www.mellanox.com/repository/solutions/openstack/images/mellanox_eth.img #. Repeat steps above and create a second instance on 'Second Compute Node' .. code-block:: bash # openstack port create --network private --vnic-type=direct --binding-profile '{"capabilities": ["switchdev"]}' direct_port2 # openstack server create --flavor m1.small --image mellanox_fedora --nic port-id=direct_port2 vm2 .. note:: You can use --availability-zone nova:compute_node_1 option to set the desired Compute Node #. Connect to instance1 and send ICMP Echo Request packets to instance2 .. code-block:: bash # vncviewer localhost:5900 vm_1# ping vm2 #. Connect to 'Second Compute Node' and find representor port of the instance .. note:: Find a representor port first, in our case it's eth3 .. code-block:: console compute_node2# ip link show enp3s0f0 6: enp3s0f0: mtu 1500 qdisc mq master ovs-system state UP mode DEFAULT group default qlen 1000 link/ether ec:0d:9a:46:9e:84 brd ff:ff:ff:ff:ff:ff vf 0 MAC 00:00:00:00:00:00, spoof checking off, link-state enable, trust off, query_rss off vf 1 MAC 00:00:00:00:00:00, spoof checking off, link-state enable, trust off, query_rss off vf 2 MAC 00:00:00:00:00:00, spoof checking off, link-state enable, trust off, query_rss off vf 3 MAC fa:16:3e:b9:b8:ce, vlan 57, spoof checking on, link-state enable, trust off, query_rss off compute_node2# ls -l /sys/class/net/ lrwxrwxrwx 1 root root 0 Sep 11 10:54 eth0 -> ../../devices/virtual/net/eth0 lrwxrwxrwx 1 root root 0 Sep 11 10:54 eth1 -> ../../devices/virtual/net/eth1 lrwxrwxrwx 1 root root 0 Sep 11 10:54 eth2 -> ../../devices/virtual/net/eth2 lrwxrwxrwx 1 root root 0 Sep 11 10:54 eth3 -> ../../devices/virtual/net/eth3 compute_node2# sudo ovs-dpctl show system@ovs-system: lookups: hit:1684 missed:1465 lost:0 flows: 0 masks: hit:8420 total:1 hit/pkt:2.67 port 0: ovs-system (internal) port 1: br-enp3s0f0 (internal) port 2: br-int (internal) port 3: br-ex (internal) port 4: enp3s0f0 port 5: tapfdc744bb-61 (internal) port 6: qr-a7b1e843-4f (internal) port 7: qg-79a77e6d-8f (internal) port 8: qr-f55e4c5f-f3 (internal) port 9: eth3 .. end #. Check traffic on the representor port. Verify that only the first ICMP packet appears. .. code-block:: console compute_node2# tcpdump -nnn -i eth3 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth3, link-type EN10MB (Ethernet), capture size 262144 bytes 17:12:41.220447 ARP, Request who-has 172.0.0.10 tell 172.0.0.13, length 46 17:12:41.220684 ARP, Reply 172.0.0.10 is-at fa:16:3e:f2:8b:23, length 42 17:12:41.260487 IP 172.0.0.13 > 172.0.0.10: ICMP echo request, id 1263, seq 1, length 64 17:12:41.260778 IP 172.0.0.10 > 172.0.0.13: ICMP echo reply, id 1263, seq 1, length 64 17:12:46.268951 ARP, Request who-has 172.0.0.13 tell 172.0.0.10, length 42 17:12:46.271771 ARP, Reply 172.0.0.13 is-at fa:16:3e:1a:10:05, length 46 17:12:55.354737 IP6 fe80::f816:3eff:fe29:8118 > ff02::1: ICMP6, router advertisement, length 64 17:12:56.106705 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 62:21:f0:89:40:73, length 300 .. end neutron-12.1.1/doc/source/admin/config-mtu.rst0000664000175000017500000000743213553660046021265 0ustar zuulzuul00000000000000.. _config-mtu: ================== MTU considerations ================== The Networking service uses the MTU of the underlying physical network to calculate the MTU for virtual network components including instance network interfaces. By default, it assumes a standard 1500-byte MTU for the underlying physical network. The Networking service only references the underlying physical network MTU. Changing the underlying physical network device MTU requires configuration of physical network devices such as switches and routers. Jumbo frames ~~~~~~~~~~~~ The Networking service supports underlying physical networks using jumbo frames and also enables instances to use jumbo frames minus any overlay protocol overhead. For example, an underlying physical network with a 9000-byte MTU yields a 8950-byte MTU for instances using a VXLAN network with IPv4 endpoints. Using IPv6 endpoints for overlay networks adds 20 bytes of overhead for any protocol. The Networking service supports the following underlying physical network architectures. Case 1 refers to the most common architecture. In general, architectures should avoid cases 2 and 3. .. note:: After you adjust MTU configuration options in ``neutron.conf`` and ``ml2_conf.ini``, you should update ``mtu`` attribute for all existing networks that need a new MTU. (Network MTU update is available for all core plugins that implement the ``net-mtu-writable`` API extension.) Case 1 ------ For typical underlying physical network architectures that implement a single MTU value, you can leverage jumbo frames using two options, one in the ``neutron.conf`` file and the other in the ``ml2_conf.ini`` file. Most environments should use this configuration. For example, referencing an underlying physical network with a 9000-byte MTU: #. In the ``neutron.conf`` file: .. code-block:: ini [DEFAULT] global_physnet_mtu = 9000 #. In the ``ml2_conf.ini`` file: .. code-block:: ini [ml2] path_mtu = 9000 Case 2 ------ Some underlying physical network architectures contain multiple layer-2 networks with different MTU values. You can configure each flat or VLAN provider network in the bridge or interface mapping options of the layer-2 agent to reference a unique MTU value. For example, referencing a 4000-byte MTU for ``provider2``, a 1500-byte MTU for ``provider3``, and a 9000-byte MTU for other networks using the Open vSwitch agent: #. In the ``neutron.conf`` file: .. code-block:: ini [DEFAULT] global_physnet_mtu = 9000 #. In the ``openvswitch_agent.ini`` file: .. code-block:: ini [ovs] bridge_mappings = provider1:eth1,provider2:eth2,provider3:eth3 #. In the ``ml2_conf.ini`` file: .. code-block:: ini [ml2] physical_network_mtus = provider2:4000,provider3:1500 path_mtu = 9000 Case 3 ------ Some underlying physical network architectures contain a unique layer-2 network for overlay networks using protocols such as VXLAN and GRE. For example, referencing a 4000-byte MTU for overlay networks and a 9000-byte MTU for other networks: #. In the ``neutron.conf`` file: .. code-block:: ini [DEFAULT] global_physnet_mtu = 9000 #. In the ``ml2_conf.ini`` file: .. code-block:: ini [ml2] path_mtu = 4000 .. note:: Other networks including provider networks and flat or VLAN self-service networks assume the value of the ``global_physnet_mtu`` option. Instance network interfaces (VIFs) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The DHCP agent provides an appropriate MTU value to instances using IPv4, while the L3 agent provides an appropriate MTU value to instances using IPv6. IPv6 uses RA via the L3 agent because the DHCP agent only supports IPv4. Instances using IPv4 and IPv6 should obtain the same MTU value regardless of method. neutron-12.1.1/doc/source/admin/deploy-lb-selfservice.rst0000664000175000017500000004073213553660047023415 0ustar zuulzuul00000000000000.. _deploy-lb-selfservice: =================================== Linux bridge: Self-service networks =================================== This architecture example augments :ref:`deploy-lb-provider` to support a nearly limitless quantity of entirely virtual networks. Although the Networking service supports VLAN self-service networks, this example focuses on VXLAN self-service networks. For more information on self-service networks, see :ref:`intro-os-networking-selfservice`. .. note:: The Linux bridge agent lacks support for other overlay protocols such as GRE and Geneve. Prerequisites ~~~~~~~~~~~~~ Add one network node with the following components: * Three network interfaces: management, provider, and overlay. * OpenStack Networking Linux bridge layer-2 agent, layer-3 agent, and any dependencies. Modify the compute nodes with the following components: * Add one network interface: overlay. .. note:: You can keep the DHCP and metadata agents on each compute node or move them to the network node. Architecture ~~~~~~~~~~~~ .. image:: figures/deploy-lb-selfservice-overview.png :alt: Self-service networks using Linux bridge - overview The following figure shows components and connectivity for one self-service network and one untagged (flat) provider network. In this particular case, the instance resides on the same compute node as the DHCP agent for the network. If the DHCP agent resides on another compute node, the latter only contains a DHCP namespace and Linux bridge with a port on the overlay physical network interface. .. image:: figures/deploy-lb-selfservice-compconn1.png :alt: Self-service networks using Linux bridge - components and connectivity - one network Example configuration ~~~~~~~~~~~~~~~~~~~~~ Use the following example configuration as a template to add support for self-service networks to an existing operational environment that supports provider networks. Controller node --------------- #. In the ``neutron.conf`` file: * Enable routing and allow overlapping IP address ranges. .. code-block:: ini [DEFAULT] service_plugins = router allow_overlapping_ips = True #. In the ``ml2_conf.ini`` file: * Add ``vxlan`` to type drivers and project network types. .. code-block:: ini [ml2] type_drivers = flat,vlan,vxlan tenant_network_types = vxlan * Enable the layer-2 population mechanism driver. .. code-block:: ini [ml2] mechanism_drivers = linuxbridge,l2population * Configure the VXLAN network ID (VNI) range. .. code-block:: ini [ml2_type_vxlan] vni_ranges = VNI_START:VNI_END Replace ``VNI_START`` and ``VNI_END`` with appropriate numerical values. #. Restart the following services: * Server Network node ------------ #. Install the Networking service layer-3 agent. #. In the ``neutron.conf`` file, configure common options: .. include:: shared/deploy-config-neutron-common.txt #. In the ``linuxbridge_agent.ini`` file, configure the layer-2 agent. .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE [vxlan] enable_vxlan = True l2_population = True local_ip = OVERLAY_INTERFACE_IP_ADDRESS [securitygroup] firewall_driver = iptables Replace ``PROVIDER_INTERFACE`` with the name of the underlying interface that handles provider networks. For example, ``eth1``. Replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the IP address of the interface that handles VXLAN overlays for self-service networks. #. In the ``l3_agent.ini`` file, configure the layer-3 agent. .. code-block:: ini [DEFAULT] interface_driver = linuxbridge external_network_bridge = .. note:: The ``external_network_bridge`` option intentionally contains no value. #. Start the following services: * Linux bridge agent * Layer-3 agent Compute nodes ------------- #. In the ``linuxbridge_agent.ini`` file, enable VXLAN support including layer-2 population. .. code-block:: ini [vxlan] enable_vxlan = True l2_population = True local_ip = OVERLAY_INTERFACE_IP_ADDRESS Replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the IP address of the interface that handles VXLAN overlays for self-service networks. #. Restart the following services: * Linux bridge agent Verify service operation ------------------------ #. Source the administrative project credentials. #. Verify presence and operation of the agents. .. code-block:: console $ openstack network agent list +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | 09de6af6-c5f1-4548-8b09-18801f068c57 | Linux bridge agent | compute2 | | True | UP | neutron-linuxbridge-agent | | 188945d1-9e70-4803-a276-df924e0788a4 | Linux bridge agent | compute1 | | True | UP | neutron-linuxbridge-agent | | e76c440d-d5f6-4316-a674-d689630b629e | DHCP agent | compute1 | nova | True | UP | neutron-dhcp-agent | | e67367de-6657-11e6-86a4-931cd04404bb | DHCP agent | compute2 | nova | True | UP | neutron-dhcp-agent | | e8174cae-6657-11e6-89f0-534ac6d0cb5c | Metadata agent | compute1 | | True | UP | neutron-metadata-agent | | ece49ec6-6657-11e6-bafb-c7560f19197d | Metadata agent | compute2 | | True | UP | neutron-metadata-agent | | 598f6357-4331-4da5-a420-0f5be000bec9 | L3 agent | network1 | nova | True | UP | neutron-l3-agent | | f4734e0f-bcd5-4922-a19d-e31d56b0a7ae | Linux bridge agent | network1 | | True | UP | neutron-linuxbridge-agent | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ Create initial networks ----------------------- .. include:: shared/deploy-selfservice-initialnetworks.txt Verify network operation ------------------------ .. include:: shared/deploy-selfservice-verifynetworkoperation.txt .. _deploy-lb-selfservice-networktrafficflow: Network traffic flow ~~~~~~~~~~~~~~~~~~~~ .. include:: shared/deploy-selfservice-networktrafficflow.txt North-south scenario 1: Instance with a fixed IP address -------------------------------------------------------- For instances with a fixed IPv4 address, the network node performs SNAT on north-south traffic passing from self-service to external networks such as the Internet. For instances with a fixed IPv6 address, the network node performs conventional routing of traffic between self-service and external networks. * The instance resides on compute node 1 and uses self-service network 1. * The instance sends a packet to a host on the Internet. The following steps involve compute node 1: #. The instance interface (1) forwards the packet to the self-service bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the self-service bridge handle firewalling and connection tracking for the packet. #. The self-service bridge forwards the packet to the VXLAN interface (4) which wraps the packet using VNI 101. #. The underlying physical interface (5) for the VXLAN interface forwards the packet to the network node via the overlay network (6). The following steps involve the network node: #. The underlying physical interface (7) for the VXLAN interface forwards the packet to the VXLAN interface (8) which unwraps the packet. #. The self-service bridge router port (9) forwards the packet to the self-service network interface (10) in the router namespace. * For IPv4, the router performs SNAT on the packet which changes the source IP address to the router IP address on the provider network and sends it to the gateway IP address on the provider network via the gateway interface on the provider network (11). * For IPv6, the router sends the packet to the next-hop IP address, typically the gateway IP address on the provider network, via the provider gateway interface (11). #. The router forwards the packet to the provider bridge router port (12). #. The VLAN sub-interface port (13) on the provider bridge forwards the packet to the provider physical network interface (14). #. The provider physical network interface (14) adds VLAN tag 101 to the packet and forwards it to the Internet via physical network infrastructure (15). .. note:: Return traffic follows similar steps in reverse. However, without a floating IPv4 address, hosts on the provider or external networks cannot originate connections to instances on the self-service network. .. image:: figures/deploy-lb-selfservice-flowns1.png :alt: Self-service networks using Linux bridge - network traffic flow - north/south scenario 1 North-south scenario 2: Instance with a floating IPv4 address ------------------------------------------------------------- For instances with a floating IPv4 address, the network node performs SNAT on north-south traffic passing from the instance to external networks such as the Internet and DNAT on north-south traffic passing from external networks to the instance. Floating IP addresses and NAT do not apply to IPv6. Thus, the network node routes IPv6 traffic in this scenario. * The instance resides on compute node 1 and uses self-service network 1. * A host on the Internet sends a packet to the instance. The following steps involve the network node: #. The physical network infrastructure (1) forwards the packet to the provider physical network interface (2). #. The provider physical network interface removes VLAN tag 101 and forwards the packet to the VLAN sub-interface on the provider bridge. #. The provider bridge forwards the packet to the self-service router gateway port on the provider network (5). * For IPv4, the router performs DNAT on the packet which changes the destination IP address to the instance IP address on the self-service network and sends it to the gateway IP address on the self-service network via the self-service interface (6). * For IPv6, the router sends the packet to the next-hop IP address, typically the gateway IP address on the self-service network, via the self-service interface (6). #. The router forwards the packet to the self-service bridge router port (7). #. The self-service bridge forwards the packet to the VXLAN interface (8) which wraps the packet using VNI 101. #. The underlying physical interface (9) for the VXLAN interface forwards the packet to the network node via the overlay network (10). The following steps involve the compute node: #. The underlying physical interface (11) for the VXLAN interface forwards the packet to the VXLAN interface (12) which unwraps the packet. #. Security group rules (13) on the self-service bridge handle firewalling and connection tracking for the packet. #. The self-service bridge instance port (14) forwards the packet to the instance interface (15) via ``veth`` pair. .. note:: Egress instance traffic flows similar to north-south scenario 1, except SNAT changes the source IP address of the packet to the floating IPv4 address rather than the router IP address on the provider network. .. image:: figures/deploy-lb-selfservice-flowns2.png :alt: Self-service networks using Linux bridge - network traffic flow - north/south scenario 2 East-west scenario 1: Instances on the same network --------------------------------------------------- Instances with a fixed IPv4/IPv6 or floating IPv4 address on the same network communicate directly between compute nodes containing those instances. By default, the VXLAN protocol lacks knowledge of target location and uses multicast to discover it. After discovery, it stores the location in the local forwarding database. In large deployments, the discovery process can generate a significant amount of network that all nodes must process. To eliminate the latter and generally increase efficiency, the Networking service includes the layer-2 population mechanism driver that automatically populates the forwarding database for VXLAN interfaces. The example configuration enables this driver. For more information, see :ref:`config-plugin-ml2`. * Instance 1 resides on compute node 1 and uses self-service network 1. * Instance 2 resides on compute node 2 and uses self-service network 1. * Instance 1 sends a packet to instance 2. The following steps involve compute node 1: #. The instance 1 interface (1) forwards the packet to the self-service bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the self-service bridge handle firewalling and connection tracking for the packet. #. The self-service bridge forwards the packet to the VXLAN interface (4) which wraps the packet using VNI 101. #. The underlying physical interface (5) for the VXLAN interface forwards the packet to compute node 2 via the overlay network (6). The following steps involve compute node 2: #. The underlying physical interface (7) for the VXLAN interface forwards the packet to the VXLAN interface (8) which unwraps the packet. #. Security group rules (9) on the self-service bridge handle firewalling and connection tracking for the packet. #. The self-service bridge instance port (10) forwards the packet to the instance 1 interface (11) via ``veth`` pair. .. note:: Return traffic follows similar steps in reverse. .. image:: figures/deploy-lb-selfservice-flowew1.png :alt: Self-service networks using Linux bridge - network traffic flow - east/west scenario 1 East-west scenario 2: Instances on different networks ----------------------------------------------------- Instances using a fixed IPv4/IPv6 address or floating IPv4 address communicate via router on the network node. The self-service networks must reside on the same router. * Instance 1 resides on compute node 1 and uses self-service network 1. * Instance 2 resides on compute node 1 and uses self-service network 2. * Instance 1 sends a packet to instance 2. .. note:: Both instances reside on the same compute node to illustrate how VXLAN enables multiple overlays to use the same layer-3 network. The following steps involve the compute node: #. The instance 1 interface (1) forwards the packet to the self-service bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the self-service bridge handle firewalling and connection tracking for the packet. #. The self-service bridge forwards the packet to the VXLAN interface (4) which wraps the packet using VNI 101. #. The underlying physical interface (5) for the VXLAN interface forwards the packet to the network node via the overlay network (6). The following steps involve the network node: #. The underlying physical interface (7) for the VXLAN interface forwards the packet to the VXLAN interface (8) which unwraps the packet. #. The self-service bridge router port (9) forwards the packet to the self-service network 1 interface (10) in the router namespace. #. The router sends the packet to the next-hop IP address, typically the gateway IP address on self-service network 2, via the self-service network 2 interface (11). #. The router forwards the packet to the self-service network 2 bridge router port (12). #. The self-service network 2 bridge forwards the packet to the VXLAN interface (13) which wraps the packet using VNI 102. #. The physical network interface (14) for the VXLAN interface sends the packet to the compute node via the overlay network (15). The following steps involve the compute node: #. The underlying physical interface (16) for the VXLAN interface sends the packet to the VXLAN interface (17) which unwraps the packet. #. Security group rules (18) on the self-service bridge handle firewalling and connection tracking for the packet. #. The self-service bridge instance port (19) forwards the packet to the instance 2 interface (20) via ``veth`` pair. .. note:: Return traffic follows similar steps in reverse. .. image:: figures/deploy-lb-selfservice-flowew2.png :alt: Self-service networks using Linux bridge - network traffic flow - east/west scenario 2 neutron-12.1.1/doc/source/admin/config-logging.rst0000664000175000017500000002452713553660047022113 0ustar zuulzuul00000000000000.. _config-logging: =========================== Logging for security groups =========================== Logging is designed as a service plug-in that captures events for relevant resources (for example, security groups or firewalls) when they occur. Supported logging resource types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As of the Queens release, the ``security_group`` resource type is supported. Configuration ~~~~~~~~~~~~~ To enable the service, follow the steps below. #. On Neutron server node: #. Add the Logging service to the ``service_plugins`` setting in ``/etc/neutron/neutron.conf``. For example: .. code-block:: none service_plugins = router,metering,log #. Add the Logging extension to the ``extensions`` setting in ``/etc/neutron/plugins/ml2/ml2_conf.ini``. For example: .. code-block:: ini [agent] extensions = log #. On compute/network nodes: #. In ``/etc/neutron/plugins/ml2/openvswitch_agent.ini``, add ``log`` to the ``extensions`` setting in the ``[agent]`` section. For example: .. code-block:: ini [agent] extensions = log #. In ``/etc/neutron/plugins/ml2/openvswitch_agent.ini``, add configuration options for logging feature in the ``[network_log]`` section. For example: .. code-block:: ini [network_log] rate_limit = 100 burst_limit = 25 #local_output_log_base = In which, ``rate_limit`` is used to configure the maximum number of packets to be logged per second (packets per second). When a high rate triggers ``rate_limit``, logging queues packets to be logged. ``burst_limit`` is used to configure the maximum of queued packets. And logged data can be stored anywhere by using ``local_output_log_base``. .. note:: - Logging currently works with ``openvswitch`` firewall driver only. - It requires at least 100 for ``rate_limit`` and at least 25 for ``burst_limit``. - If ``rate_limit`` is unset, logging will log unlimited. - If we don't specify ``local_output_log_base``, logged data will be stored in system journal like ``/var/log/syslog``. Trusted projects policy.json configuration ------------------------------------------ With the default ``/etc/neutron/policy.json``, administrators must set up resource logging on behalf of the cloud projects. If projects are trusted to administer their own resource logging in your cloud, neutron's file ``policy.json`` can be modified to allow this. Modify ``/etc/neutron/policy.json`` policy entries as follows: .. code-block:: none "get_loggable_resources": "rule:regular_user", "create_log": "rule:regular_user", "update_log": "rule:regular_user", "delete_log": "rule:regular_user", "get_logs": "rule:regular_user", "get_log": "rule:regular_user", Operator workflow ~~~~~~~~~~~~~~~~~ #. Confirm logging resources are supported: .. code-block:: console $ openstack network loggable resources list +-----------------+ | Supported types | +-----------------+ | security_group | +-----------------+ #. Create a logging resource with an appropriate resource type: .. code-block:: console $ openstack network log create --resource-type security_group \ --description "Collecting all security events in project demo" \ --enable --event ALL Log_Created +-----------------+------------------------------------------------+ | Field | Value | +-----------------+------------------------------------------------+ | Description | Collecting all security events in project demo | | Enabled | True | | Event | ALL | | ID | 8085c3e6-0fa2-4954-b5ce-ff6207931b6d | | Name | Log_Created | | Project | 02568bd62b414221956f15dbe9527d16 | | Resource | None | | Target | None | | Type | security_group | | created_at | 2017-07-05T02:56:43Z | | revision_number | 0 | | tenant_id | 02568bd62b414221956f15dbe9527d16 | | updated_at | 2017-07-05T02:56:43Z | +-----------------+------------------------------------------------+ .. note:: The ``Enabled`` field is set to ``True`` by default. If enabled, log information is written to the destination if configured in ``local_output_log_base`` or system journal like ``/var/log/syslog``. Enable/Disable log ------------------ We can enable or disable logging objects at runtime. It means that it will apply to all attached ports with the logging object immediately. For example: .. code-block:: console $ openstack network log set --disable Log_Created $ openstack network log show Log_Created +-----------------+------------------------------------------------+ | Field | Value | +-----------------+------------------------------------------------+ | Description | Collecting all security events in project demo | | Enabled | False | | Event | ALL | | ID | 8085c3e6-0fa2-4954-b5ce-ff6207931b6d | | Name | Log_Created | | Project | 02568bd62b414221956f15dbe9527d16 | | Resource | None | | Target | None | | Type | security_group | | created_at | 2017-07-05T02:56:43Z | | revision_number | 1 | | tenant_id | 02568bd62b414221956f15dbe9527d16 | | updated_at | 2017-07-05T03:12:01Z | +-----------------+------------------------------------------------+ Events collected description ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Logging will collect ``ACCEPT`` or ``DROP`` or both events related to security group, with the following general characteristics: * Log every ``DROP`` event: Every ``DROP`` security event will be generated when an incoming or outgoing session is dropped, that is the new session is not allowed for the security group and because of that blocked. * Log an ``ACCEPT`` event: An ``ACCEPT`` security event will be generated for each **NEW** incoming or outgoing session that is allowed by the ports security group. More details for the events follow below: * North/South ``ACCEPT``: For a North/South session there would be a single ``ACCEPT`` event irrespective of direction. * East/West ``ACCEPT``/``ACCEPT``: In an intra-project East/West session where the security group on the originating port allows the session and the security group on the destination port allows the session, i.e. the traffic is allowed, there would be two ``ACCEPT`` security events generated, one from the perspective of the originating port and one from the perspective of the destination port. * East/West ``ACCEPT``/``DROP``: In an intra-project East/West session initiation where the security group on the originating port allows the session and the security group on the destination port does not allow the session there would be ``ACCEPT`` security events generated from the perspective of the originating port and ``DROP`` security events generated from the perspective of the destination port. General data requirements: The security event should include: * A status of the flow ``ACCEPT``/``DROP``. * An indication of the originator of the flow, e.g which project or log resource generated the event. * A timestamp of the flow. * An identifier of the associated instance interface (neutron port id). * An identifier of the matching security group rule. * A layer 3 and 4 information (address, port, protocol, etc). .. note:: No other extraneous events are generated within the security event logs, e.g. no debugging data, etc. * Security event record format: * Logged data of an ``ACCEPT`` event would look like: .. code-block:: console May 5 09:05:07 action=ACCEPT project_id=736672c700cd43e1bd321aeaf940365c log_resource_ids=['4522efdf-8d44-4e19-b237-64cafc49469b', '42332d89-df42-4588-a2bb-3ce50829ac51'] vm_port=e0259ade-86de-482e-a717-f58258f7173f ethernet(dst='fa:16:3e:ec:36:32',ethertype=2048,src='fa:16:3e:50:aa:b5'), ipv4(csum=62071,dst='10.0.0.4',flags=2,header_length=5,identification=36638,offset=0, option=None,proto=6,src='172.24.4.10',tos=0,total_length=60,ttl=63,version=4), tcp(ack=0,bits=2,csum=15097,dst_port=80,offset=10,option=[TCPOptionMaximumSegmentSize(kind=2,length=4,max_seg_size=1460), TCPOptionSACKPermitted(kind=4,length=2), TCPOptionTimestamps(kind=8,length=10,ts_ecr=0,ts_val=196418896), TCPOptionNoOperation(kind=1,length=1), TCPOptionWindowScale(kind=3,length=3,shift_cnt=3)], seq=3284890090,src_port=47825,urgent=0,window_size=14600) * Logged data of a ``DROP`` event: .. code-block:: console May 5 09:05:07 action=DROP project_id=736672c700cd43e1bd321aeaf940365c log_resource_ids=['4522efdf-8d44-4e19-b237-64cafc49469b'] vm_port=e0259ade-86de-482e-a717-f58258f7173f ethernet(dst='fa:16:3e:ec:36:32',ethertype=2048,src='fa:16:3e:50:aa:b5'), ipv4(csum=62071,dst='10.0.0.4',flags=2,header_length=5,identification=36638,offset=0, option=None,proto=6,src='172.24.4.10',tos=0,total_length=60,ttl=63,version=4), tcp(ack=0,bits=2,csum=15097,dst_port=80,offset=10,option=[TCPOptionMaximumSegmentSize(kind=2,length=4,max_seg_size=1460), TCPOptionSACKPermitted(kind=4,length=2), TCPOptionTimestamps(kind=8,length=10,ts_ecr=0,ts_val=196418896), TCPOptionNoOperation(kind=1,length=1), TCPOptionWindowScale(kind=3,length=3,shift_cnt=3)], seq=3284890090,src_port=47825,urgent=0,window_size=14600) neutron-12.1.1/doc/source/admin/migration-nova-network-to-neutron.rst0000664000175000017500000001320713553660046025743 0ustar zuulzuul00000000000000.. _migration-nova-to-neutron: ===================================================== Legacy nova-network to OpenStack Networking (neutron) ===================================================== Two networking models exist in OpenStack. The first is called legacy networking (nova-network) and it is a sub-process embedded in the Compute project (nova). This model has some limitations, such as creating complex network topologies, extending its back-end implementation to vendor-specific technologies, and providing project-specific networking elements. These limitations are the main reasons the OpenStack Networking (neutron) model was created. This section describes the process of migrating clouds based on the legacy networking model to the OpenStack Networking model. This process requires additional changes to both compute and networking to support the migration. This document describes the overall process and the features required in both Networking and Compute. The current process as designed is a minimally viable migration with the goal of deprecating and then removing legacy networking. Both the Compute and Networking teams agree that a one-button migration process from legacy networking to OpenStack Networking (neutron) is not an essential requirement for the deprecation and removal of the legacy networking at a future date. This section includes a process and tools which are designed to solve a simple use case migration. Users are encouraged to take these tools, test them, provide feedback, and then expand on the feature set to suit their own deployments; deployers that refrain from participating in this process intending to wait for a path that better suits their use case are likely to be disappointed. Impact and limitations ~~~~~~~~~~~~~~~~~~~~~~ The migration process from the legacy nova-network networking service to OpenStack Networking (neutron) has some limitations and impacts on the operational state of the cloud. It is critical to understand them in order to decide whether or not this process is acceptable for your cloud and all users. Management impact ----------------- The Networking REST API is publicly read-only until after the migration is complete. During the migration, Networking REST API is read-write only to nova-api, and changes to Networking are only allowed via nova-api. The Compute REST API is available throughout the entire process, although there is a brief period where it is made read-only during a database migration. The Networking REST API will need to expose (to nova-api) all details necessary for reconstructing the information previously held in the legacy networking database. Compute needs a per-hypervisor "has_transitioned" boolean change in the data model to be used during the migration process. This flag is no longer required once the process is complete. Operations impact ----------------- In order to support a wide range of deployment options, the migration process described here requires a rolling restart of hypervisors. The rate and timing of specific hypervisor restarts is under the control of the operator. The migration may be paused, even for an extended period of time (for example, while testing or investigating issues) with some hypervisors on legacy networking and some on Networking, and Compute API remains fully functional. Individual hypervisors may be rolled back to legacy networking during this stage of the migration, although this requires an additional restart. In order to support the widest range of deployer needs, the process described here is easy to automate but is not already automated. Deployers should expect to perform multiple manual steps or write some simple scripts in order to perform this migration. Performance impact ------------------ During the migration, nova-network API calls will go through an additional internal conversion to Networking calls. This will have different and likely poorer performance characteristics compared with either the pre-migration or post-migration APIs. Migration process overview ~~~~~~~~~~~~~~~~~~~~~~~~~~ #. Start neutron-server in intended final config, except with REST API restricted to read-write only by nova-api. #. Make the Compute REST API read-only. #. Run a DB dump/restore tool that creates Networking data structures representing current legacy networking config. #. Enable a nova-api proxy that recreates internal Compute objects from Networking information (via the Networking REST API). #. Make Compute REST API read-write again. This means legacy networking DB is now unused, new changes are now stored in the Networking DB, and no rollback is possible from here without losing those new changes. .. note:: At this moment the Networking DB is the source of truth, but nova-api is the only public read-write API. Next, you'll need to migrate each hypervisor. To do that, follow these steps: #. Disable the hypervisor. This would be a good time to live migrate or evacuate the compute node, if supported. #. Disable nova-compute. #. Enable the Networking agent. #. Set the "has_transitioned" flag in the Compute hypervisor database/config. #. Reboot the hypervisor (or run "smart" live transition tool if available). #. Re-enable the hypervisor. At this point, all compute nodes have been migrated, but they are still using the nova-api API and Compute gateways. Finally, enable OpenStack Networking by following these steps: #. Bring up the Networking (l3) nodes. The new routers will have identical MAC+IPs as old Compute gateways so some sort of immediate cutover is possible, except for stateful connections issues such as NAT. #. Make the Networking API read-write and disable legacy networking. Migration Completed! neutron-12.1.1/doc/source/admin/config-services-agent.rst0000664000175000017500000000433313553660047023375 0ustar zuulzuul00000000000000.. _config-services-agent: =================== Services and agents =================== A usual neutron setup consists of multiple services and agents running on one or multiple nodes (though some setups may not need any agents). Each of these services provide some of the networking or API services. Among those of special interest are: #. The neutron-server that provides API endpoints and serves as a single point of access to the database. It usually runs on the controller nodes. #. Layer2 agent that can utilize Open vSwitch, Linux Bridge or other vendor-specific technology to provide network segmentation and isolation for project networks. The L2 agent should run on every node where it is deemed responsible for wiring and securing virtual interfaces (usually both compute and network nodes). #. Layer3 agent that runs on network node and provides east-west and north-south routing plus some advanced services such as FWaaS or VPNaaS. Configuration options ~~~~~~~~~~~~~~~~~~~~~ The neutron configuration options are segregated between neutron-server and agents. Both services and agents may load the main ``neutron.conf`` since this file should contain the oslo.messaging configuration for internal neutron RPCs and may contain host specific configuration, such as file paths. The ``neutron.conf`` contains the database, keystone, nova credentials, and endpoints strictly for neutron-server to use. In addition, neutron-server may load a plugin-specific configuration file, yet the agents should not. As the plugin configuration is primarily site wide options and the plugin provides the persistence layer for neutron, agents should be instructed to act upon these values through RPC. Each individual agent may have its own configuration file. This file should be loaded after the main ``neutron.conf`` file, so the agent configuration takes precedence. The agent-specific configuration may contain configurations which vary between hosts in a neutron deployment such as the ``external_network_bridge`` for an L3 agent. If any agent requires access to additional external services beyond the neutron RPC, those endpoints should be defined in the agent-specific configuration file (for example, nova metadata for metadata agent). neutron-12.1.1/doc/source/admin/migration-classic-to-l3ha.rst0000664000175000017500000001705113553660046024070 0ustar zuulzuul00000000000000.. _migration-to-vrrp: ============================== Add VRRP to an existing router ============================== This section describes the process of migrating from a classic router to an L3 HA router, which is available starting from the Mitaka release. Similar to the classic scenario, all network traffic on a project network that requires routing actively traverses only one network node regardless of the quantity of network nodes providing HA for the router. Therefore, this high-availability implementation primarily addresses failure situations instead of bandwidth constraints that limit performance. However, it supports random distribution of routers on different network nodes to reduce the chances of bandwidth constraints and to improve scaling. This section references parts of :ref:`deploy-lb-ha-vrrp` and :ref:`deploy-ovs-ha-vrrp`. For details regarding needed infrastructure and configuration to allow actual L3 HA deployment, read the relevant guide before continuing with the migration process. Migration ~~~~~~~~~ The migration process is quite simple, it involves turning down the router by setting the router's ``admin_state_up`` attribute to ``False``, upgrading the router to L3 HA and then setting the router's ``admin_state_up`` attribute back to ``True``. .. warning:: Once starting the migration, south-north connections (instances to internet) will be severed. New connections will be able to start only when the migration is complete. Here is the router we have used in our demonstration: .. code-block:: console $ openstack router show router1 +-------------------------+-------------------------------------------+ | Field | Value | +-------------------------+-------------------------------------------+ | admin_state_up | UP | | distributed | False | | external_gateway_info | | | ha | False | | id | 6b793b46-d082-4fd5-980f-a6f80cbb0f2a | | name | router1 | | project_id | bb8b84ab75be4e19bd0dfe02f6c3f5c1 | | routes | | | status | ACTIVE | | tags | [] | +-------------------------+-------------------------------------------+ #. Source the administrative project credentials. #. Set the admin_state_up to ``False``. This will severe south-north connections until admin_state_up is set to ``True`` again. .. code-block:: console $ openstack router set router1 --disable #. Set the ``ha`` attribute of the router to ``True``. .. code-block:: console $ openstack router set router1 --ha #. Set the admin_state_up to ``True``. After this, south-north connections can start. .. code-block:: console $ openstack router set router1 --enable #. Make sure that the router's ``ha`` attribute has changed to ``True``. .. code-block:: console $ openstack router show router1 +-------------------------+-------------------------------------------+ | Field | Value | +-------------------------+-------------------------------------------+ | admin_state_up | UP | | distributed | False | | external_gateway_info | | | ha | True | | id | 6b793b46-d082-4fd5-980f-a6f80cbb0f2a | | name | router1 | | project_id | bb8b84ab75be4e19bd0dfe02f6c3f5c1 | | routes | | | status | ACTIVE | | tags | [] | +-------------------------+-------------------------------------------+ L3 HA to Legacy ~~~~~~~~~~~~~~~ To return to classic mode, turn down the router again, turning off L3 HA and starting the router again. .. warning:: Once starting the migration, south-north connections (instances to internet) will be severed. New connections will be able to start only when the migration is complete. Here is the router we have used in our demonstration: .. code-block:: console $ openstack router show router1 +-------------------------+-------------------------------------------+ | Field | Value | +-------------------------+-------------------------------------------+ | admin_state_up | DOWN | | distributed | False | | external_gateway_info | | | ha | True | | id | 6b793b46-d082-4fd5-980f-a6f80cbb0f2a | | name | router1 | | project_id | bb8b84ab75be4e19bd0dfe02f6c3f5c1 | | routes | | | status | ACTIVE | | tags | [] | +-------------------------+-------------------------------------------+ #. Source the administrative project credentials. #. Set the admin_state_up to ``False``. This will severe south-north connections until admin_state_up is set to ``True`` again. .. code-block:: console $ openstack router set router1 --disable #. Set the ``ha`` attribute of the router to ``True``. .. code-block:: console $ openstack router set router1 --no-ha #. Set the admin_state_up to ``True``. After this, south-north connections can start. .. code-block:: console $ openstack router set router1 --enable #. Make sure that the router's ``ha`` attribute has changed to ``False``. .. code-block:: console $ openstack router show router1 +-------------------------+-------------------------------------------+ | Field | Value | +-------------------------+-------------------------------------------+ | admin_state_up | UP | | distributed | False | | external_gateway_info | | | ha | False | | id | 6b793b46-d082-4fd5-980f-a6f80cbb0f2a | | name | router1 | | project_id | bb8b84ab75be4e19bd0dfe02f6c3f5c1 | | routes | | | status | ACTIVE | | tags | [] | +-------------------------+-------------------------------------------+ neutron-12.1.1/doc/source/admin/intro.rst0000664000175000017500000000432713553660046020350 0ustar zuulzuul00000000000000.. _intro: ============ Introduction ============ The OpenStack Networking service (neutron) provides an API that allows users to set up and define network connectivity and addressing in the cloud. The project code-name for Networking services is neutron. OpenStack Networking handles the creation and management of a virtual networking infrastructure, including networks, switches, subnets, and routers for devices managed by the OpenStack Compute service (nova). Advanced services such as firewalls or virtual private network (VPN) can also be used. OpenStack Networking consists of the neutron-server, a database for persistent storage, and any number of plug-in agents, which provide other services such as interfacing with native Linux networking mechanisms, external devices, or SDN controllers. OpenStack Networking is entirely standalone and can be deployed to a dedicated host. If your deployment uses a controller host to run centralized Compute components, you can deploy the Networking server to that specific host instead. OpenStack Networking integrates with various OpenStack components: * OpenStack Identity service (keystone) is used for authentication and authorization of API requests. * OpenStack Compute service (nova) is used to plug each virtual NIC on the VM into a particular network. * OpenStack Dashboard (horizon) is used by administrators and project users to create and manage network services through a web-based graphical interface. .. note:: The network address ranges used in this guide are chosen in accordance with `RFC 5737 `_ and `RFC 3849 `_, and as such are restricted to the following: **IPv4:** * 192.0.2.0/24 * 198.51.100.0/24 * 203.0.113.0/24 **IPv6:** * 2001:DB8::/32 The network address ranges in the examples of this guide should not be used for any purpose other than documentation. .. note:: To reduce clutter, this guide removes command output without relevance to the particular action. .. toctree:: :maxdepth: 2 intro-basic-networking intro-network-components intro-overlay-protocols intro-network-namespaces intro-nat intro-os-networking fwaas neutron-12.1.1/doc/source/admin/deploy-lb.rst0000664000175000017500000000072113553660046021076 0ustar zuulzuul00000000000000.. _deploy-lb: ============================= Linux bridge mechanism driver ============================= The Linux bridge mechanism driver uses only Linux bridges and ``veth`` pairs as interconnection devices. A layer-2 agent manages Linux bridges on each compute node and any other node that provides layer-3 (routing), DHCP, metadata, or other network services. .. toctree:: :maxdepth: 2 deploy-lb-provider deploy-lb-selfservice deploy-lb-ha-vrrp neutron-12.1.1/doc/source/admin/fwaas-v1-scenario.rst0000664000175000017500000000721413553660047022442 0ustar zuulzuul00000000000000Firewall-as-a-Service (FWaaS) v1 scenario ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Enable FWaaS v1 --------------- FWaaS management options are also available in the Dashboard. #. Enable the FWaaS plug-in in the ``/etc/neutron/neutron.conf`` file: .. code-block:: ini service_plugins = firewall [service_providers] # ... service_provider = FIREWALL:Iptables:neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver:default [fwaas] driver = iptables enabled = True .. note:: On Ubuntu, modify the ``[fwaas]`` section in the ``/etc/neutron/fwaas_driver.ini`` file instead of ``/etc/neutron/neutron.conf``. #. Configure the FWaaS plugin for the L3 agent. In the ``AGENT`` section of ``l3_agent.ini``, make sure the FWaaS extension is loaded: .. code-block:: ini [AGENT] extensions = fwaas Edit the FWaaS section in the ``/etc/neutron/neutron.conf`` file to indicate the agent version and driver: .. code-block:: ini [fwaas] agent_version = v1 driver = iptables enabled = True conntrack_driver = conntrack #. Create the required tables in the database: .. code-block:: console # neutron-db-manage --subproject neutron-fwaas upgrade head #. Enable the option in the ``local_settings.py`` file, which is typically located on the controller node: .. code-block:: python OPENSTACK_NEUTRON_NETWORK = { # ... 'enable_firewall' = True, # ... } .. note:: By default, ``enable_firewall`` option value is ``True`` in ``local_settings.py`` file. Apply the settings by restarting the web server. #. Restart the ``neutron-l3-agent`` and ``neutron-server`` services to apply the settings. Configure Firewall-as-a-Service v1 ---------------------------------- Create the firewall rules and create a policy that contains them. Then, create a firewall that applies the policy. #. Create a firewall rule: .. code-block:: console $ neutron firewall-rule-create --protocol {tcp,udp,icmp,any} \ --source-ip-address SOURCE_IP_ADDRESS \ --destination-ip-address DESTINATION_IP_ADDRESS \ --source-port SOURCE_PORT_RANGE --destination-port DEST_PORT_RANGE \ --action {allow,deny,reject} The Networking client requires a protocol value. If the rule is protocol agnostic, you can use the ``any`` value. .. note:: When the source or destination IP address are not of the same IP version (for example, IPv6), the command returns an error. #. Create a firewall policy: .. code-block:: console $ neutron firewall-policy-create --firewall-rules \ "FIREWALL_RULE_IDS_OR_NAMES" myfirewallpolicy Separate firewall rule IDs or names with spaces. The order in which you specify the rules is important. You can create a firewall policy without any rules and add rules later, as follows: * To add multiple rules, use the update operation. * To add a single rule, use the insert-rule operation. For more details, see `Networking command-line client `_ in the OpenStack Command-Line Interface Reference. .. note:: FWaaS always adds a default ``deny all`` rule at the lowest precedence of each policy. Consequently, a firewall policy with no rules blocks all traffic by default. #. Create a firewall: .. code-block:: console $ neutron firewall-create FIREWALL_POLICY_UUID .. note:: The firewall remains in PENDING\_CREATE state until you create a Networking router and attach an interface to it. neutron-12.1.1/doc/source/admin/deploy-lb-provider.rst0000664000175000017500000003174413553660047022740 0ustar zuulzuul00000000000000.. _deploy-lb-provider: =============================== Linux bridge: Provider networks =============================== The provider networks architecture example provides layer-2 connectivity between instances and the physical network infrastructure using VLAN (802.1q) tagging. It supports one untagged (flat) network and and up to 4095 tagged (VLAN) networks. The actual quantity of VLAN networks depends on the physical network infrastructure. For more information on provider networks, see :ref:`intro-os-networking-provider`. Prerequisites ~~~~~~~~~~~~~ One controller node with the following components: * Two network interfaces: management and provider. * OpenStack Networking server service and ML2 plug-in. Two compute nodes with the following components: * Two network interfaces: management and provider. * OpenStack Networking Linux bridge layer-2 agent, DHCP agent, metadata agent, and any dependencies. .. note:: Larger deployments typically deploy the DHCP and metadata agents on a subset of compute nodes to increase performance and redundancy. However, too many agents can overwhelm the message bus. Also, to further simplify any deployment, you can omit the metadata agent and use a configuration drive to provide metadata to instances. Architecture ~~~~~~~~~~~~ .. image:: figures/deploy-lb-provider-overview.png :alt: Provider networks using Linux bridge - overview The following figure shows components and connectivity for one untagged (flat) network. In this particular case, the instance resides on the same compute node as the DHCP agent for the network. If the DHCP agent resides on another compute node, the latter only contains a DHCP namespace and Linux bridge with a port on the provider physical network interface. .. image:: figures/deploy-lb-provider-compconn1.png :alt: Provider networks using Linux bridge - components and connectivity - one network The following figure describes virtual connectivity among components for two tagged (VLAN) networks. Essentially, each network uses a separate bridge that contains a port on the VLAN sub-interface on the provider physical network interface. Similar to the single untagged network case, the DHCP agent may reside on a different compute node. .. image:: figures/deploy-lb-provider-compconn2.png :alt: Provider networks using Linux bridge - components and connectivity - multiple networks .. note:: These figures omit the controller node because it does not handle instance network traffic. Example configuration ~~~~~~~~~~~~~~~~~~~~~ Use the following example configuration as a template to deploy provider networks in your environment. Controller node --------------- #. Install the Networking service components that provides the ``neutron-server`` service and ML2 plug-in. #. In the ``neutron.conf`` file: * Configure common options: .. include:: shared/deploy-config-neutron-common.txt * Disable service plug-ins because provider networks do not require any. However, this breaks portions of the dashboard that manage the Networking service. See the `Queens Install Tutorials and Guides <../install/>`__ for more information. .. code-block:: ini [DEFAULT] service_plugins = * Enable two DHCP agents per network so both compute nodes can provide DHCP service provider networks. .. code-block:: ini [DEFAULT] dhcp_agents_per_network = 2 * If necessary, :ref:`configure MTU `. #. In the ``ml2_conf.ini`` file: * Configure drivers and network types: .. code-block:: ini [ml2] type_drivers = flat,vlan tenant_network_types = mechanism_drivers = linuxbridge extension_drivers = port_security * Configure network mappings: .. code-block:: ini [ml2_type_flat] flat_networks = provider [ml2_type_vlan] network_vlan_ranges = provider .. note:: The ``tenant_network_types`` option contains no value because the architecture does not support self-service networks. .. note:: The ``provider`` value in the ``network_vlan_ranges`` option lacks VLAN ID ranges to support use of arbitrary VLAN IDs. #. Populate the database. .. code-block:: console # su -s /bin/sh -c "neutron-db-manage --config-file /etc/neutron/neutron.conf \ --config-file /etc/neutron/plugins/ml2/ml2_conf.ini upgrade head" neutron #. Start the following services: * Server Compute nodes ------------- #. Install the Networking service Linux bridge layer-2 agent. #. In the ``neutron.conf`` file, configure common options: .. include:: shared/deploy-config-neutron-common.txt #. In the ``linuxbridge_agent.ini`` file, configure the Linux bridge agent: .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE [vxlan] enable_vxlan = False [securitygroup] firewall_driver = iptables Replace ``PROVIDER_INTERFACE`` with the name of the underlying interface that handles provider networks. For example, ``eth1``. #. In the ``dhcp_agent.ini`` file, configure the DHCP agent: .. code-block:: ini [DEFAULT] interface_driver = linuxbridge enable_isolated_metadata = True force_metadata = True .. note:: The ``force_metadata`` option forces the DHCP agent to provide a host route to the metadata service on ``169.254.169.254`` regardless of whether the subnet contains an interface on a router, thus maintaining similar and predictable metadata behavior among subnets. #. In the ``metadata_agent.ini`` file, configure the metadata agent: .. code-block:: ini [DEFAULT] nova_metadata_host = controller metadata_proxy_shared_secret = METADATA_SECRET The value of ``METADATA_SECRET`` must match the value of the same option in the ``[neutron]`` section of the ``nova.conf`` file. #. Start the following services: * Linux bridge agent * DHCP agent * Metadata agent Verify service operation ------------------------ #. Source the administrative project credentials. #. Verify presence and operation of the agents: .. code-block:: console $ openstack network agent list +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | 09de6af6-c5f1-4548-8b09-18801f068c57 | Linux bridge agent | compute2 | | True | UP | neutron-linuxbridge-agent | | 188945d1-9e70-4803-a276-df924e0788a4 | Linux bridge agent | compute1 | | True | UP | neutron-linuxbridge-agent | | e76c440d-d5f6-4316-a674-d689630b629e | DHCP agent | compute1 | nova | True | UP | neutron-dhcp-agent | | e67367de-6657-11e6-86a4-931cd04404bb | DHCP agent | compute2 | nova | True | UP | neutron-dhcp-agent | | e8174cae-6657-11e6-89f0-534ac6d0cb5c | Metadata agent | compute1 | | True | UP | neutron-metadata-agent | | ece49ec6-6657-11e6-bafb-c7560f19197d | Metadata agent | compute2 | | True | UP | neutron-metadata-agent | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ Create initial networks ----------------------- .. include:: shared/deploy-provider-initialnetworks.txt Verify network operation ------------------------ .. include:: shared/deploy-provider-verifynetworkoperation.txt Network traffic flow ~~~~~~~~~~~~~~~~~~~~ .. include:: shared/deploy-provider-networktrafficflow.txt North-south scenario: Instance with a fixed IP address ------------------------------------------------------ * The instance resides on compute node 1 and uses provider network 1. * The instance sends a packet to a host on the Internet. The following steps involve compute node 1. #. The instance interface (1) forwards the packet to the provider bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the provider bridge handle firewalling and connection tracking for the packet. #. The VLAN sub-interface port (4) on the provider bridge forwards the packet to the physical network interface (5). #. The physical network interface (5) adds VLAN tag 101 to the packet and forwards it to the physical network infrastructure switch (6). The following steps involve the physical network infrastructure: #. The switch removes VLAN tag 101 from the packet and forwards it to the router (7). #. The router routes the packet from the provider network (8) to the external network (9) and forwards the packet to the switch (10). #. The switch forwards the packet to the external network (11). #. The external network (12) receives the packet. .. image:: figures/deploy-lb-provider-flowns1.png :alt: Provider networks using Linux bridge - network traffic flow - north/south .. note:: Return traffic follows similar steps in reverse. East-west scenario 1: Instances on the same network --------------------------------------------------- Instances on the same network communicate directly between compute nodes containing those instances. * Instance 1 resides on compute node 1 and uses provider network 1. * Instance 2 resides on compute node 2 and uses provider network 1. * Instance 1 sends a packet to instance 2. The following steps involve compute node 1: #. The instance 1 interface (1) forwards the packet to the provider bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the provider bridge handle firewalling and connection tracking for the packet. #. The VLAN sub-interface port (4) on the provider bridge forwards the packet to the physical network interface (5). #. The physical network interface (5) adds VLAN tag 101 to the packet and forwards it to the physical network infrastructure switch (6). The following steps involve the physical network infrastructure: #. The switch forwards the packet from compute node 1 to compute node 2 (7). The following steps involve compute node 2: #. The physical network interface (8) removes VLAN tag 101 from the packet and forwards it to the VLAN sub-interface port (9) on the provider bridge. #. Security group rules (10) on the provider bridge handle firewalling and connection tracking for the packet. #. The provider bridge instance port (11) forwards the packet to the instance 2 interface (12) via ``veth`` pair. .. image:: figures/deploy-lb-provider-flowew1.png :alt: Provider networks using Linux bridge - network traffic flow - east/west scenario 1 .. note:: Return traffic follows similar steps in reverse. East-west scenario 2: Instances on different networks ----------------------------------------------------- Instances communicate via router on the physical network infrastructure. * Instance 1 resides on compute node 1 and uses provider network 1. * Instance 2 resides on compute node 1 and uses provider network 2. * Instance 1 sends a packet to instance 2. .. note:: Both instances reside on the same compute node to illustrate how VLAN tagging enables multiple logical layer-2 networks to use the same physical layer-2 network. The following steps involve the compute node: #. The instance 1 interface (1) forwards the packet to the provider bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the provider bridge handle firewalling and connection tracking for the packet. #. The VLAN sub-interface port (4) on the provider bridge forwards the packet to the physical network interface (5). #. The physical network interface (5) adds VLAN tag 101 to the packet and forwards it to the physical network infrastructure switch (6). The following steps involve the physical network infrastructure: #. The switch removes VLAN tag 101 from the packet and forwards it to the router (7). #. The router routes the packet from provider network 1 (8) to provider network 2 (9). #. The router forwards the packet to the switch (10). #. The switch adds VLAN tag 102 to the packet and forwards it to compute node 1 (11). The following steps involve the compute node: #. The physical network interface (12) removes VLAN tag 102 from the packet and forwards it to the VLAN sub-interface port (13) on the provider bridge. #. Security group rules (14) on the provider bridge handle firewalling and connection tracking for the packet. #. The provider bridge instance port (15) forwards the packet to the instance 2 interface (16) via ``veth`` pair. .. image:: figures/deploy-lb-provider-flowew2.png :alt: Provider networks using Linux bridge - network traffic flow - east/west scenario 2 .. note:: Return traffic follows similar steps in reverse. neutron-12.1.1/doc/source/admin/intro-network-components.rst0000664000175000017500000000441213553660046024215 0ustar zuulzuul00000000000000.. _intro-network-components: ================== Network components ================== Switches ~~~~~~~~ Switches are Multi-Input Multi-Output (MIMO) devices that enable packets to travel from one node to another. Switches connect hosts that belong to the same layer-2 network. Switches enable forwarding of the packet received on one port (input) to another port (output) so that they reach the desired destination node. Switches operate at layer-2 in the networking model. They forward the traffic based on the destination Ethernet address in the packet header. Routers ~~~~~~~ Routers are special devices that enable packets to travel from one layer-3 network to another. Routers enable communication between two nodes on different layer-3 networks that are not directly connected to each other. Routers operate at layer-3 in the networking model. They route the traffic based on the destination IP address in the packet header. Firewalls ~~~~~~~~~ Firewalls are used to regulate traffic to and from a host or a network. A firewall can be either a specialized device connecting two networks or a software-based filtering mechanism implemented on an operating system. Firewalls are used to restrict traffic to a host based on the rules defined on the host. They can filter packets based on several criteria such as source IP address, destination IP address, port numbers, connection state, and so on. It is primarily used to protect the hosts from unauthorized access and malicious attacks. Linux-based operating systems implement firewalls through ``iptables``. Load balancers ~~~~~~~~~~~~~~ Load balancers can be software-based or hardware-based devices that allow traffic to evenly be distributed across several servers. By distributing the traffic across multiple servers, it avoids overload of a single server thereby preventing a single point of failure in the product. This further improves the performance, network throughput, and response time of the servers. Load balancers are typically used in a 3-tier architecture. In this model, a load balancer receives a request from the front-end web server, which then forwards the request to one of the available back-end database servers for processing. The response from the database server is passed back to the web server for further processing. neutron-12.1.1/doc/source/admin/config-routed-networks.rst0000664000175000017500000005524513553660047023642 0ustar zuulzuul00000000000000.. _config-routed-provider-networks: ======================== Routed provider networks ======================== .. note:: Use of this feature requires the OpenStack client version 3.3 or newer. Before routed provider networks, the Networking service could not present a multi-segment layer-3 network as a single entity. Thus, each operator typically chose one of the following architectures: * Single large layer-2 network * Multiple smaller layer-2 networks Single large layer-2 networks become complex at scale and involve significant failure domains. Multiple smaller layer-2 networks scale better and shrink failure domains, but leave network selection to the user. Without additional information, users cannot easily differentiate these networks. A routed provider network enables a single provider network to represent multiple layer-2 networks (broadcast domains) or segments and enables the operator to present one network to users. However, the particular IP addresses available to an instance depend on the segment of the network available on the particular compute node. Similar to conventional networking, layer-2 (switching) handles transit of traffic between ports on the same segment and layer-3 (routing) handles transit of traffic between segments. Each segment requires at least one subnet that explicitly belongs to that segment. The association between a segment and a subnet distinguishes a routed provider network from other types of networks. The Networking service enforces that either zero or all subnets on a particular network associate with a segment. For example, attempting to create a subnet without a segment on a network containing subnets with segments generates an error. The Networking service does not provide layer-3 services between segments. Instead, it relies on physical network infrastructure to route subnets. Thus, both the Networking service and physical network infrastructure must contain configuration for routed provider networks, similar to conventional provider networks. In the future, implementation of dynamic routing protocols may ease configuration of routed networks. Prerequisites ~~~~~~~~~~~~~ Routed provider networks require additional prerequisites over conventional provider networks. We recommend using the following procedure: #. Begin with segments. The Networking service defines a segment using the following components: * Unique physical network name * Segmentation type * Segmentation ID For example, ``provider1``, ``VLAN``, and ``2016``. See the `API reference `__ for more information. Within a network, use a unique physical network name for each segment which enables reuse of the same segmentation details between subnets. For example, using the same VLAN ID across all segments of a particular provider network. Similar to conventional provider networks, the operator must provision the layer-2 physical network infrastructure accordingly. #. Implement routing between segments. The Networking service does not provision routing among segments. The operator must implement routing among segments of a provider network. Each subnet on a segment must contain the gateway address of the router interface on that particular subnet. For example: =========== ======= ======================= ===================== Segment Version Addresses Gateway =========== ======= ======================= ===================== segment1 4 203.0.113.0/24 203.0.113.1 segment1 6 fd00:203:0:113::/64 fd00:203:0:113::1 segment2 4 198.51.100.0/24 198.51.100.1 segment2 6 fd00:198:51:100::/64 fd00:198:51:100::1 =========== ======= ======================= ===================== #. Map segments to compute nodes. Routed provider networks imply that compute nodes reside on different segments. The operator must ensure that every compute host that is supposed to participate in a router provider network has direct connectivity to one of its segments. =========== ====== ================ Host Rack Physical Network =========== ====== ================ compute0001 rack 1 segment 1 compute0002 rack 1 segment 1 ... ... ... compute0101 rack 2 segment 2 compute0102 rack 2 segment 2 compute0102 rack 2 segment 2 ... ... ... =========== ====== ================ #. Deploy DHCP agents. Unlike conventional provider networks, a DHCP agent cannot support more than one segment within a network. The operator must deploy at least one DHCP agent per segment. Consider deploying DHCP agents on compute nodes containing the segments rather than one or more network nodes to reduce node count. =========== ====== ================ Host Rack Physical Network =========== ====== ================ network0001 rack 1 segment 1 network0002 rack 2 segment 2 ... ... ... =========== ====== ================ #. Configure communication of the Networking service with the Compute scheduler. An instance with an interface with an IPv4 address in a routed provider network must be placed by the Compute scheduler in a host that has access to a segment with available IPv4 addresses. To make this possible, the Networking service communicates to the Compute scheduler the inventory of IPv4 addresses associated with each segment of a routed provider network. The operator must configure the authentication credentials that the Networking service will use to communicate with the Compute scheduler's placement API. Please see below an example configuration. .. note:: Coordination between the Networking service and the Compute scheduler is not necessary for IPv6 subnets as a consequence of their large address spaces. .. note:: The coordination between the Networking service and the Compute scheduler requires the following minimum API micro-versions. * Compute service API: 2.41 * Placement API: 1.1 Example configuration ~~~~~~~~~~~~~~~~~~~~~ Controller node --------------- #. Enable the segments service plug-in by appending ``segments`` to the list of ``service_plugins`` in the ``neutron.conf`` file on all nodes running the ``neutron-server`` service: .. code-block:: ini [DEFAULT] # ... service_plugins = ..., segments #. Add a ``placement`` section to the ``neutron.conf`` file with authentication credentials for the Compute service placement API: .. code-block:: ini [placement] auth_uri = http://192.0.2.72/identity project_domain_name = Default project_name = service user_domain_name = Default password = apassword username = nova auth_url = http://192.0.2.72/identity_admin auth_type = password region_name = RegionOne #. Restart the ``neutron-server`` service. Network or compute nodes ------------------------ * Configure the layer-2 agent on each node to map one or more segments to the appropriate physical network bridge or interface and restart the agent. Create a routed provider network ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following steps create a routed provider network with two segments. Each segment contains one IPv4 subnet and one IPv6 subnet. #. Source the administrative project credentials. #. Create a VLAN provider network which includes a default segment. In this example, the network uses the ``provider1`` physical network with VLAN ID 2016. .. code-block:: console $ openstack network create --share --provider-physical-network provider1 \ --provider-network-type vlan --provider-segment 2016 multisegment1 +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | id | 6ab19caa-dda9-4b3d-abc4-5b8f435b98d9 | | ipv4_address_scope | None | | ipv6_address_scope | None | | l2_adjacency | True | | mtu | 1500 | | name | multisegment1 | | port_security_enabled | True | | provider:network_type | vlan | | provider:physical_network | provider1 | | provider:segmentation_id | 2016 | | revision_number | 1 | | router:external | Internal | | shared | True | | status | ACTIVE | | subnets | | | tags | [] | +---------------------------+--------------------------------------+ #. Rename the default segment to ``segment1``. .. code-block:: console $ openstack network segment list --network multisegment1 +--------------------------------------+----------+--------------------------------------+--------------+---------+ | ID | Name | Network | Network Type | Segment | +--------------------------------------+----------+--------------------------------------+--------------+---------+ | 43e16869-ad31-48e4-87ce-acf756709e18 | None | 6ab19caa-dda9-4b3d-abc4-5b8f435b98d9 | vlan | 2016 | +--------------------------------------+----------+--------------------------------------+--------------+---------+ .. code-block:: console $ openstack network segment set --name segment1 43e16869-ad31-48e4-87ce-acf756709e18 .. note:: This command provides no output. #. Create a second segment on the provider network. In this example, the segment uses the ``provider2`` physical network with VLAN ID 2016. .. code-block:: console $ openstack network segment create --physical-network provider2 \ --network-type vlan --segment 2016 --network multisegment1 segment2 +------------------+--------------------------------------+ | Field | Value | +------------------+--------------------------------------+ | description | None | | headers | | | id | 053b7925-9a89-4489-9992-e164c8cc8763 | | name | segment2 | | network_id | 6ab19caa-dda9-4b3d-abc4-5b8f435b98d9 | | network_type | vlan | | physical_network | provider2 | | revision_number | 1 | | segmentation_id | 2016 | | tags | [] | +------------------+--------------------------------------+ #. Verify that the network contains the ``segment1`` and ``segment2`` segments. .. code-block:: console $ openstack network segment list --network multisegment1 +--------------------------------------+----------+--------------------------------------+--------------+---------+ | ID | Name | Network | Network Type | Segment | +--------------------------------------+----------+--------------------------------------+--------------+---------+ | 053b7925-9a89-4489-9992-e164c8cc8763 | segment2 | 6ab19caa-dda9-4b3d-abc4-5b8f435b98d9 | vlan | 2016 | | 43e16869-ad31-48e4-87ce-acf756709e18 | segment1 | 6ab19caa-dda9-4b3d-abc4-5b8f435b98d9 | vlan | 2016 | +--------------------------------------+----------+--------------------------------------+--------------+---------+ #. Create subnets on the ``segment1`` segment. In this example, the IPv4 subnet uses 203.0.113.0/24 and the IPv6 subnet uses fd00:203:0:113::/64. .. code-block:: console $ openstack subnet create \ --network multisegment1 --network-segment segment1 \ --ip-version 4 --subnet-range 203.0.113.0/24 \ multisegment1-segment1-v4 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | allocation_pools | 203.0.113.2-203.0.113.254 | | cidr | 203.0.113.0/24 | | enable_dhcp | True | | gateway_ip | 203.0.113.1 | | id | c428797a-6f8e-4cb1-b394-c404318a2762 | | ip_version | 4 | | name | multisegment1-segment1-v4 | | network_id | 6ab19caa-dda9-4b3d-abc4-5b8f435b98d9 | | revision_number | 1 | | segment_id | 43e16869-ad31-48e4-87ce-acf756709e18 | | tags | [] | +-------------------+--------------------------------------+ $ openstack subnet create \ --network multisegment1 --network-segment segment1 \ --ip-version 6 --subnet-range fd00:203:0:113::/64 \ --ipv6-address-mode slaac multisegment1-segment1-v6 +-------------------+------------------------------------------------------+ | Field | Value | +-------------------+------------------------------------------------------+ | allocation_pools | fd00:203:0:113::2-fd00:203:0:113:ffff:ffff:ffff:ffff | | cidr | fd00:203:0:113::/64 | | enable_dhcp | True | | gateway_ip | fd00:203:0:113::1 | | id | e41cb069-9902-4c01-9e1c-268c8252256a | | ip_version | 6 | | ipv6_address_mode | slaac | | ipv6_ra_mode | None | | name | multisegment1-segment1-v6 | | network_id | 6ab19caa-dda9-4b3d-abc4-5b8f435b98d9 | | revision_number | 1 | | segment_id | 43e16869-ad31-48e4-87ce-acf756709e18 | | tags | [] | +-------------------+------------------------------------------------------+ .. note:: By default, IPv6 subnets on provider networks rely on physical network infrastructure for stateless address autoconfiguration (SLAAC) and router advertisement. #. Create subnets on the ``segment2`` segment. In this example, the IPv4 subnet uses 198.51.100.0/24 and the IPv6 subnet uses fd00:198:51:100::/64. .. code-block:: console $ openstack subnet create \ --network multisegment1 --network-segment segment2 \ --ip-version 4 --subnet-range 198.51.100.0/24 \ multisegment1-segment2-v4 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | allocation_pools | 198.51.100.2-198.51.100.254 | | cidr | 198.51.100.0/24 | | enable_dhcp | True | | gateway_ip | 198.51.100.1 | | id | 242755c2-f5fd-4e7d-bd7a-342ca95e50b2 | | ip_version | 4 | | name | multisegment1-segment2-v4 | | network_id | 6ab19caa-dda9-4b3d-abc4-5b8f435b98d9 | | revision_number | 1 | | segment_id | 053b7925-9a89-4489-9992-e164c8cc8763 | | tags | [] | +-------------------+--------------------------------------+ $ openstack subnet create \ --network multisegment1 --network-segment segment2 \ --ip-version 6 --subnet-range fd00:198:51:100::/64 \ --ipv6-address-mode slaac multisegment1-segment2-v6 +-------------------+--------------------------------------------------------+ | Field | Value | +-------------------+--------------------------------------------------------+ | allocation_pools | fd00:198:51:100::2-fd00:198:51:100:ffff:ffff:ffff:ffff | | cidr | fd00:198:51:100::/64 | | enable_dhcp | True | | gateway_ip | fd00:198:51:100::1 | | id | b884c40e-9cfe-4d1b-a085-0a15488e9441 | | ip_version | 6 | | ipv6_address_mode | slaac | | ipv6_ra_mode | None | | name | multisegment1-segment2-v6 | | network_id | 6ab19caa-dda9-4b3d-abc4-5b8f435b98d9 | | revision_number | 1 | | segment_id | 053b7925-9a89-4489-9992-e164c8cc8763 | | tags | [] | +-------------------+--------------------------------------------------------+ #. Verify that each IPv4 subnet associates with at least one DHCP agent. .. code-block:: console $ neutron dhcp-agent-list-hosting-net multisegment1 +--------------------------------------+-------------+----------------+-------+ | id | host | admin_state_up | alive | +--------------------------------------+-------------+----------------+-------+ | c904ed10-922c-4c1a-84fd-d928abaf8f55 | compute0001 | True | :-) | | e0b22cc0-d2a6-4f1c-b17c-27558e20b454 | compute0101 | True | :-) | +--------------------------------------+-------------+----------------+-------+ #. Verify that inventories were created for each segment IPv4 subnet in the Compute service placement API (for the sake of brevity, only one of the segments is shown in this example). .. code-block:: console $ SEGMENT_ID=053b7925-9a89-4489-9992-e164c8cc8763 $ curl -s -X GET \ http://localhost/placement/resource_providers/$SEGMENT_ID/inventories \ -H "Content-type: application/json" \ -H "X-Auth-Token: $TOKEN" \ -H "Openstack-Api-Version: placement 1.1" { "resource_provider_generation": 1, "inventories": { "allocation_ratio": 1, "total": 254, "reserved": 2, "step_size": 1, "min_unit": 1, "max_unit": 1 } } .. note:: As of the writing of this guide, there is not placement API CLI client, so the :command:`curl` command is used for this example. .. note:: Service points URLs differ depending on your OpenStack deployment. You can discover the Placement service URL by executing the :command:`openstack endpoint list | grep placement` command. This command has to be executed as admin. #. Verify that host aggregates were created for each segment in the Compute service (for the sake of brevity, only one of the segments is shown in this example). .. code-block:: console $ openstack aggregate list +----+---------------------------------------------------------+-------------------+ | Id | Name | Availability Zone | +----+---------------------------------------------------------+-------------------+ | 10 | Neutron segment id 053b7925-9a89-4489-9992-e164c8cc8763 | None | +----+---------------------------------------------------------+-------------------+ #. Launch one or more instances. Each instance obtains IP addresses according to the segment it uses on the particular compute node. .. note:: If a fixed IP is specified by the user in the port create request, that particular IP is allocated immediately to the port. However, creating a port and passing it to an instance yields a different behavior than conventional networks. If the fixed IP is not specified on the port create request, the Networking service defers assignment of IP addresses to the port until the particular compute node becomes apparent. For example: .. code-block:: console $ openstack port create --network multisegment1 port1 +-----------------------+--------------------------------------+ | Field | Value | +-----------------------+--------------------------------------+ | admin_state_up | UP | | binding_vnic_type | normal | | id | 6181fb47-7a74-4add-9b6b-f9837c1c90c4 | | ip_allocation | deferred | | mac_address | fa:16:3e:34:de:9b | | name | port1 | | network_id | 6ab19caa-dda9-4b3d-abc4-5b8f435b98d9 | | port_security_enabled | True | | revision_number | 1 | | security_groups | e4fcef0d-e2c5-40c3-a385-9c33ac9289c5 | | status | DOWN | | tags | [] | +-----------------------+--------------------------------------+ neutron-12.1.1/doc/source/admin/fwaas-v2-scenario.rst0000664000175000017500000000614213553660047022442 0ustar zuulzuul00000000000000Firewall-as-a-Service (FWaaS) v2 scenario ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Enable FWaaS v2 --------------- #. Enable the FWaaS plug-in in the ``/etc/neutron/neutron.conf`` file: .. code-block:: ini service_plugins = firewall_v2 [service_providers] # ... service_provider = FIREWALL:Iptables:neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver:default [fwaas] agent_version = v2 driver = neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas_v2.IptablesFwaasDriver enabled = True .. note:: On Ubuntu and Centos, modify the ``[fwaas]`` section in the ``/etc/neutron/fwaas_driver.ini`` file instead of ``/etc/neutron/neutron.conf``. #. Configure the FWaaS plugin for the L3 agent. In the ``AGENT`` section of ``l3_agent.ini``, make sure the FWaaS v2 extension is loaded: .. code-block:: ini [AGENT] extensions = fwaas_v2 #. Create the required tables in the database: .. code-block:: console # neutron-db-manage --subproject neutron-fwaas upgrade head #. Restart the ``neutron-l3-agent`` and ``neutron-server`` services to apply the settings. .. note:: Firewall v2 is not supported by horizon yet. Configure Firewall-as-a-Service v2 ---------------------------------- Create the firewall rules and create a policy that contains them. Then, create a firewall that applies the policy. #. Create a firewall rule: .. code-block:: console $ neutron firewall-rule-create --protocol {tcp,udp,icmp,any} \ --source-ip-address SOURCE_IP_ADDRESS \ --destination-ip-address DESTINATION_IP_ADDRESS \ --source-port SOURCE_PORT_RANGE --destination-port DEST_PORT_RANGE \ --action {allow,deny,reject} The Networking client requires a protocol value. If the rule is protocol agnostic, you can use the ``any`` value. .. note:: When the source or destination IP address are not of the same IP version (for example, IPv6), the command returns an error. #. Create a firewall policy: .. code-block:: console $ neutron firewall-policy-create --firewall-rules \ "FIREWALL_RULE_IDS_OR_NAMES" myfirewallpolicy Separate firewall rule IDs or names with spaces. The order in which you specify the rules is important. You can create a firewall policy without any rules and add rules later, as follows: * To add multiple rules, use the update operation. * To add a single rule, use the insert-rule operation. For more details, see `Networking command-line client `_ in the OpenStack Command-Line Interface Reference. .. note:: FWaaS always adds a default ``deny all`` rule at the lowest precedence of each policy. Consequently, a firewall policy with no rules blocks all traffic by default. #. Create a firewall: .. code-block:: console $ neutron firewall-create FIREWALL_POLICY_UUID .. note:: The firewall remains in PENDING\_CREATE state until you create a Networking router and attach an interface to it. neutron-12.1.1/doc/source/admin/config-ovs-dpdk.rst0000664000175000017500000001227513553660047022211 0ustar zuulzuul00000000000000.. _config-ovs-dpdk: =============================== Open vSwitch with DPDK datapath =============================== This page serves as a guide for how to use the OVS with DPDK datapath functionality available in the Networking service as of the Mitaka release. The basics ~~~~~~~~~~ Open vSwitch (OVS) provides support for a Data Plane Development Kit (DPDK) datapath since OVS 2.2, and a DPDK-backed ``vhost-user`` virtual interface since OVS 2.4. The DPDK datapath provides lower latency and higher performance than the standard kernel OVS datapath, while DPDK-backed ``vhost-user`` interfaces can connect guests to this datapath. For more information on DPDK, refer to the `DPDK `__ website. OVS with DPDK, or OVS-DPDK, can be used to provide high-performance networking between instances on OpenStack compute nodes. Prerequisites ------------- Using DPDK in OVS requires the following minimum software versions: * OVS 2.4 * DPDK 2.0 * QEMU 2.1.0 * libvirt 1.2.13 Support of ``vhost-user`` multiqueue that enables use of multiqueue with ``virtio-net`` and ``igb_uio`` is available if the following newer versions are used: * OVS 2.5 * DPDK 2.2 * QEMU 2.5 * libvirt 1.2.17 In both cases, install and configure Open vSwitch with DPDK support for each node. For more information, see the `OVS-DPDK `__ installation guide (select an appropriate OVS version in the :guilabel:`Branch` drop-down menu). :doc:`/contributor/internals/ovs_vhostuser` for configuration of neutron OVS agent. In case you wish to configure multiqueue, see the `OVS configuration chapter on vhost-user `__ in QEMU documentation. The technical background of multiqueue is explained in the corresponding `blueprint `__. Additionally, OpenStack supports ``vhost-user`` reconnect feature starting from the Ocata release, as implementation of fix for `bug 1604924 `__. Starting from OpenStack Ocata release this feature is used without any configuration necessary in case the following minimum software versions are used: * OVS 2.6 * DPDK 16.07 * QEMU 2.7 The support of this feature is not yet present in ML2 OVN and ODL mechanism drivers. Using vhost-user interfaces ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Once OVS and neutron are correctly configured with DPDK support, ``vhost-user`` interfaces are completely transparent to the guest (except in case of multiqueue configuration described below). However, guests must request huge pages. This can be done through flavors. For example: .. code-block:: console $ openstack flavor set m1.large --property hw:mem_page_size=large For more information about the syntax for ``hw:mem_page_size``, refer to the `Flavors `__ guide. .. note:: ``vhost-user`` requires file descriptor-backed shared memory. Currently, the only way to request this is by requesting large pages. This is why instances spawned on hosts with OVS-DPDK must request large pages. The aggregate flavor affinity filter can be used to associate flavors with large page support to hosts with OVS-DPDK support. Create and add ``vhost-user`` network interfaces to instances in the same fashion as conventional interfaces. These interfaces can use the kernel ``virtio-net`` driver or a DPDK-compatible driver in the guest .. code-block:: console $ openstack server create --nic net-id=$net_id ... testserver Using vhost-user multiqueue ~~~~~~~~~~~~~~~~~~~~~~~~~~~ To use this feature, the following should be set in the flavor extra specs (flavor keys): .. code-block:: console $ openstack flavor set $m1.large --property hw:vif_multiqueue_enabled=true This setting can be overridden by the image metadata property if the feature is enabled in the extra specs: .. code-block:: console $ openstack image set --property hw_vif_mutliqueue_enabled=true IMAGE_NAME Support of ``virtio-net`` multiqueue needs to be present in kernel of guest VM and is available starting from Linux kernel 3.8. Check pre-set maximum for number of combined channels in channel configuration. Configuration of OVS and flavor done successfully should result in maximum being more than '1'): .. code-block:: console $ ethtool -l INTERFACE_NAME To increase number of current combined channels run following command in guest VM: .. code-block:: console $ ethtool -L INTERFACE_NAME combined QUEUES_NR The number of queues should typically match the number of vCPUs defined for the instance. In newer kernel versions this is configured automatically. Known limitations ~~~~~~~~~~~~~~~~~ * This feature is only supported when using the libvirt compute driver, and the KVM/QEMU hypervisor. * Huge pages are required for each instance running on hosts with OVS-DPDK. If huge pages are not present in the guest, the interface will appear but will not function. * Expect performance degradation of services using tap devices: these devices do not support DPDK. Example services include DVR, FWaaS, or LBaaS. neutron-12.1.1/doc/source/admin/fwaas.rst0000664000175000017500000000540413553660047020314 0ustar zuulzuul00000000000000Firewall-as-a-Service (FWaaS) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Firewall-as-a-Service (FWaaS) plug-in applies firewalls to OpenStack objects such as projects, routers, and router ports. .. note:: We anticipate this to expand to VM ports in the Ocata cycle. The central concepts with OpenStack firewalls are the notions of a firewall policy and a firewall rule. A policy is an ordered collection of rules. A rule specifies a collection of attributes (such as port ranges, protocol, and IP addresses) that constitute match criteria and an action to take (allow or deny) on matched traffic. A policy can be made public, so it can be shared across projects. Firewalls are implemented in various ways, depending on the driver used. For example, an iptables driver implements firewalls using iptable rules. An OpenVSwitch driver implements firewall rules using flow entries in flow tables. A Cisco firewall driver manipulates NSX devices. FWaaS v1 -------- The original FWaaS implementation, v1, provides protection for routers. When a firewall is applied to a router, all internal ports are protected. The following diagram depicts FWaaS v1 protection. It illustrates the flow of ingress and egress traffic for the VM2 instance: .. figure:: figures/fwaas.png FWaaS v2 -------- The newer FWaaS implementation, v2, provides a much more granular service. The notion of a firewall has been replaced with firewall group to indicate that a firewall consists of two policies: an ingress policy and an egress policy. A firewall group is applied not at the router level (all ports on a router) but at the port level. Currently, router ports can be specified. For Ocata, VM ports can also be specified. FWaaS v1 versus v2 ------------------ The following table compares v1 and v2 features. +------------------------------------------+-----+------+ | Feature | v1 | v2 | +==========================================+=====+======+ | Supports L3 firewalling for routers | YES | NO* | +------------------------------------------+-----+------+ | Supports L3 firewalling for router ports | NO | YES | +------------------------------------------+-----+------+ | Supports L2 firewalling (VM ports) | NO | NO** | +------------------------------------------+-----+------+ | CLI support | YES | YES | +------------------------------------------+-----+------+ | Horizon support | YES | NO | +------------------------------------------+-----+------+ \* A firewall group can be applied to all ports on a given router in order to effect this. \*\* This feature is planned for Ocata. For further information, see `v1 configuration guide <./fwaas-v1-scenario.html>`_ or `v2 configuration guide <./fwaas-v2-scenario.html>`_. neutron-12.1.1/doc/source/admin/ops.rst0000664000175000017500000000022313553660047020006 0ustar zuulzuul00000000000000.. _operations: ========== Operations ========== .. toctree:: :maxdepth: 2 ops-ip-availability ops-resource-tags ops-resource-purge neutron-12.1.1/doc/source/admin/deploy-ovs-ha-vrrp.rst0000664000175000017500000001425413553660047022674 0ustar zuulzuul00000000000000.. _deploy-ovs-ha-vrrp: ========================================== Open vSwitch: High availability using VRRP ========================================== .. include:: shared/deploy-ha-vrrp.txt Prerequisites ~~~~~~~~~~~~~ Add one network node with the following components: * Three network interfaces: management, provider, and overlay. * OpenStack Networking layer-2 agent, layer-3 agent, and any dependencies. .. note:: You can keep the DHCP and metadata agents on each compute node or move them to the network nodes. Architecture ~~~~~~~~~~~~ .. image:: figures/deploy-ovs-ha-vrrp-overview.png :alt: High-availability using VRRP with Linux bridge - overview The following figure shows components and connectivity for one self-service network and one untagged (flat) network. The master router resides on network node 1. In this particular case, the instance resides on the same compute node as the DHCP agent for the network. If the DHCP agent resides on another compute node, the latter only contains a DHCP namespace and Linux bridge with a port on the overlay physical network interface. .. image:: figures/deploy-ovs-ha-vrrp-compconn1.png :alt: High-availability using VRRP with Linux bridge - components and connectivity - one network Example configuration ~~~~~~~~~~~~~~~~~~~~~ Use the following example configuration as a template to add support for high-availability using VRRP to an existing operational environment that supports self-service networks. Controller node --------------- #. In the ``neutron.conf`` file: * Enable VRRP. .. code-block:: ini [DEFAULT] l3_ha = True #. Restart the following services: * Server Network node 1 -------------- No changes. Network node 2 -------------- #. Install the Networking service OVS layer-2 agent and layer-3 agent. #. Install OVS. #. In the ``neutron.conf`` file, configure common options: .. include:: shared/deploy-config-neutron-common.txt #. Start the following services: * OVS #. Create the OVS provider bridge ``br-provider``: .. code-block:: console $ ovs-vsctl add-br br-provider #. Add the provider network interface as a port on the OVS provider bridge ``br-provider``: .. code-block:: console $ ovs-vsctl add-port br-provider PROVIDER_INTERFACE Replace ``PROVIDER_INTERFACE`` with the name of the underlying interface that handles provider networks. For example, ``eth1``. #. In the ``openvswitch_agent.ini`` file, configure the layer-2 agent. .. code-block:: ini [ovs] bridge_mappings = provider:br-provider local_ip = OVERLAY_INTERFACE_IP_ADDRESS [agent] tunnel_types = vxlan l2_population = true [securitygroup] firewall_driver = iptables_hybrid Replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the IP address of the interface that handles VXLAN overlays for self-service networks. #. In the ``l3_agent.ini`` file, configure the layer-3 agent. .. code-block:: ini [DEFAULT] interface_driver = openvswitch external_network_bridge = .. note:: The ``external_network_bridge`` option intentionally contains no value. #. Start the following services: * Open vSwitch agent * Layer-3 agent Compute nodes ------------- No changes. Verify service operation ------------------------ #. Source the administrative project credentials. #. Verify presence and operation of the agents. .. code-block:: console $ openstack network agent list +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | 1236bbcb-e0ba-48a9-80fc-81202ca4fa51 | Metadata agent | compute2 | | True | UP | neutron-metadata-agent | | 457d6898-b373-4bb3-b41f-59345dcfb5c5 | Open vSwitch agent | compute2 | | True | UP | neutron-openvswitch-agent | | 71f15e84-bc47-4c2a-b9fb-317840b2d753 | DHCP agent | compute2 | nova | True | UP | neutron-dhcp-agent | | 8805b962-de95-4e40-bdc2-7a0add7521e8 | L3 agent | network1 | nova | True | UP | neutron-l3-agent | | a33cac5a-0266-48f6-9cac-4cef4f8b0358 | Open vSwitch agent | network1 | | True | UP | neutron-openvswitch-agent | | a6c69690-e7f7-4e56-9831-1282753e5007 | Metadata agent | compute1 | | True | UP | neutron-metadata-agent | | af11f22f-a9f4-404f-9fd8-cd7ad55c0f68 | DHCP agent | compute1 | nova | True | UP | neutron-dhcp-agent | | bcfc977b-ec0e-4ba9-be62-9489b4b0e6f1 | Open vSwitch agent | compute1 | | True | UP | neutron-openvswitch-agent | | 7f00d759-f2c9-494a-9fbf-fd9118104d03 | Open vSwitch agent | network2 | | True | UP | neutron-openvswitch-agent | | b28d8818-9e32-4888-930b-29addbdd2ef9 | L3 agent | network2 | nova | True | UP | neutron-l3-agent | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ Create initial networks ----------------------- .. include:: shared/deploy-ha-vrrp-initialnetworks.txt Verify network operation ------------------------ .. include:: shared/deploy-ha-vrrp-verifynetworkoperation.txt Verify failover operation ------------------------- .. include:: shared/deploy-ha-vrrp-verifyfailoveroperation.txt Keepalived VRRP health check ---------------------------- .. include:: shared/keepalived-vrrp-healthcheck.txt Network traffic flow ~~~~~~~~~~~~~~~~~~~~ This high-availability mechanism simply augments :ref:`deploy-ovs-selfservice` with failover of layer-3 services to another router if the master router fails. Thus, you can reference :ref:`Self-service network traffic flow ` for normal operation. neutron-12.1.1/doc/source/admin/ops-resource-tags.rst0000664000175000017500000003022613553660047022575 0ustar zuulzuul00000000000000.. _ops-resource-tags: ============= Resource tags ============= Various virtual networking resources support tags for use by external systems or any other clients of the Networking service API. All resources that support standard attributes are applicable for tagging. This includes: * networks * subnets * subnetpools * ports * routers * floatingips * logs * security-groups * security-group-rules * segments * policies * trunks Use cases ~~~~~~~~~ The following use cases refer to adding tags to networks, but the same can be applicable to any other supported Networking service resource: #. Ability to map different networks in different OpenStack locations to one logically same network (for multi-site OpenStack). #. Ability to map IDs from different management/orchestration systems to OpenStack networks in mixed environments. For example, in the Kuryr project, the Docker network ID is mapped to the Neutron network ID. #. Ability to leverage tags by deployment tools. #. Ability to tag information about provider networks (for example, high-bandwidth, low-latency, and so on). Filtering with tags ~~~~~~~~~~~~~~~~~~~ The API allows searching/filtering of the ``GET /v2.0/networks`` API. The following query parameters are supported: * ``tags`` * ``tags-any`` * ``not-tags`` * ``not-tags-any`` To request the list of networks that have a single tag, ``tags`` argument should be set to the desired tag name. Example:: GET /v2.0/networks?tags=red To request the list of networks that have two or more tags, the ``tags`` argument should be set to the list of tags, separated by commas. In this case, the tags given must all be present for a network to be included in the query result. Example that returns networks that have the "red" and "blue" tags:: GET /v2.0/networks?tags=red,blue To request the list of networks that have one or more of a list of given tags, the ``tags-any`` argument should be set to the list of tags, separated by commas. In this case, as long as one of the given tags is present, the network will be included in the query result. Example that returns the networks that have the "red" or the "blue" tag:: GET /v2.0/networks?tags-any=red,blue To request the list of networks that do not have one or more tags, the ``not-tags`` argument should be set to the list of tags, separated by commas. In this case, only the networks that do not have any of the given tags will be included in the query results. Example that returns the networks that do not have either "red" or "blue" tag:: GET /v2.0/networks?not-tags=red,blue To request the list of networks that do not have at least one of a list of tags, the ``not-tags-any`` argument should be set to the list of tags, separated by commas. In this case, only the networks that do not have at least one of the given tags will be included in the query result. Example that returns the networks that do not have the "red" tag, or do not have the "blue" tag:: GET /v2.0/networks?not-tags-any=red,blue The ``tags``, ``tags-any``, ``not-tags``, and ``not-tags-any`` arguments can be combined to build more complex queries. Example:: GET /v2.0/networks?tags=red,blue&tags-any=green,orange The above example returns any networks that have the "red" and "blue" tags, plus at least one of "green" and "orange". Complex queries may have contradictory parameters. Example:: GET /v2.0/networks?tags=blue¬-tags=blue In this case, we should let the Networking service find these networks. Obviously, there are no such networks and the service will return an empty list. User workflow ~~~~~~~~~~~~~ Add a tag to a resource: .. code-block:: console $ neutron tag-add --resource-type network --resource ab442634-1cc9-49e5-bd49-0dac9c811f69 --tag red $ neutron net-show net +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | True | | availability_zone_hints | | | availability_zones | | | id | ab442634-1cc9-49e5-bd49-0dac9c811f69 | | ipv4_address_scope | | | ipv6_address_scope | | | mtu | 1450 | | name | net | | port_security_enabled | True | | router:external | False | | shared | False | | status | ACTIVE | | subnets | | | tags | red | | tenant_id | e6710680bfd14555891f265644e1dd5c | +-------------------------+--------------------------------------+ Remove a tag from a resource: .. code-block:: console $ neutron tag-remove --resource-type network --resource ab442634-1cc9-49e5-bd49-0dac9c811f69 --tag red $ neutron net-show net +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | True | | availability_zone_hints | | | availability_zones | | | id | ab442634-1cc9-49e5-bd49-0dac9c811f69 | | ipv4_address_scope | | | ipv6_address_scope | | | mtu | 1450 | | name | net | | port_security_enabled | True | | router:external | False | | shared | False | | status | ACTIVE | | subnets | | | tags | | | tenant_id | e6710680bfd14555891f265644e1dd5c | +-------------------------+--------------------------------------+ Replace all tags on the resource: .. code-block:: console $ neutron tag-replace --resource-type network --resource ab442634-1cc9-49e5-bd49-0dac9c811f69 --tag red --tag blue $ neutron net-show net +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | True | | availability_zone_hints | | | availability_zones | | | id | ab442634-1cc9-49e5-bd49-0dac9c811f69 | | ipv4_address_scope | | | ipv6_address_scope | | | mtu | 1450 | | name | net | | port_security_enabled | True | | router:external | False | | shared | False | | status | ACTIVE | | subnets | | | tags | red | | | blue | | tenant_id | e6710680bfd14555891f265644e1dd5c | +-------------------------+--------------------------------------+ Clear tags from a resource: .. code-block:: console $ neutron tag-remove --resource-type network --resource ab442634-1cc9-49e5-bd49-0dac9c811f69 --all $ neutron net-show net +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | True | | availability_zone_hints | | | availability_zones | | | id | ab442634-1cc9-49e5-bd49-0dac9c811f69 | | ipv4_address_scope | | | ipv6_address_scope | | | mtu | 1450 | | name | net | | port_security_enabled | True | | router:external | False | | shared | False | | status | ACTIVE | | subnets | | | tags | | | tenant_id | e6710680bfd14555891f265644e1dd5c | +-------------------------+--------------------------------------+ Get list of resources with tag filters from networks. The networks are: test-net1 with "red" tag, test-net2 with "red" and "blue" tags, test-net3 with "red", "blue", and "green" tags, and test-net4 with "green" tag. Get list of resources with ``tags`` filter: .. code-block:: console $ neutron net-list --tags red,blue +--------------------------------------+-----------+---------+ | id | name | subnets | +--------------------------------------+-----------+---------+ | 8ca3b9ed-f578-45fa-8c44-c53f13aec05a | test-net3 | | | e736e63d-42e4-4f4c-836c-6ad286ffd68a | test-net2 | | +--------------------------------------+-----------+---------+ Get list of resources with ``tags-any`` filter: .. code-block:: console $ neutron net-list --tags-any red,blue +--------------------------------------+-----------+---------+ | id | name | subnets | +--------------------------------------+-----------+---------+ | 30491224-3855-431f-a688-fb29df004d82 | test-net1 | | | 8ca3b9ed-f578-45fa-8c44-c53f13aec05a | test-net3 | | | e736e63d-42e4-4f4c-836c-6ad286ffd68a | test-net2 | | +--------------------------------------+-----------+---------+ Get list of resources with ``not-tags`` filter: .. code-block:: console $ neutron net-list --not-tags red,blue +--------------------------------------+-----------+---------+ | id | name | subnets | +--------------------------------------+-----------+---------+ | 30491224-3855-431f-a688-fb29df004d82 | test-net1 | | | cdb3ed08-ca63-4090-ba12-30b366372993 | test-net4 | | +--------------------------------------+-----------+---------+ Get list of resources with ``not-tags-any`` filter: .. code-block:: console $ neutron net-list --not-tags-any red,blue +--------------------------------------+-----------+---------+ | id | name | subnets | +--------------------------------------+-----------+---------+ | cdb3ed08-ca63-4090-ba12-30b366372993 | test-net4 | | +--------------------------------------+-----------+---------+ Limitations ~~~~~~~~~~~ Filtering resources with a tag whose name contains a comma is not supported. Thus, do not put such a tag name to resources. Future support ~~~~~~~~~~~~~~ In future releases, the Networking service may support setting tags for additional resources. neutron-12.1.1/doc/source/admin/config-service-subnets.rst0000664000175000017500000004626513553660047023611 0ustar zuulzuul00000000000000.. _config-service-subnets: =============== Service subnets =============== Service subnets enable operators to define valid port types for each subnet on a network without limiting networks to one subnet or manually creating ports with a specific subnet ID. Using this feature, operators can ensure that ports for instances and router interfaces, for example, always use different subnets. Operation ~~~~~~~~~ Define one or more service types for one or more subnets on a particular network. Each service type must correspond to a valid device owner within the port model in order for it to be used. During IP allocation, the :ref:`IPAM ` driver returns an address from a subnet with a service type matching the port device owner. If no subnets match, or all matching subnets lack available IP addresses, the IPAM driver attempts to use a subnet without any service types to preserve compatibility. If all subnets on a network have a service type, the IPAM driver cannot preserve compatibility. However, this feature enables strict IP allocation from subnets with a matching device owner. If multiple subnets contain the same service type, or a subnet without a service type exists, the IPAM driver selects the first subnet with a matching service type. For example, a floating IP agent gateway port uses the following selection process: * ``network:floatingip_agent_gateway`` * ``None`` .. note:: Ports with the device owner ``network:dhcp`` are exempt from the above IPAM logic for subnets with ``dhcp_enabled`` set to ``True``. This preserves the existing automatic DHCP port creation behaviour for DHCP-enabled subnets. Creating or updating a port with a specific subnet skips this selection process and explicitly uses the given subnet. Usage ~~~~~ .. note:: Creating a subnet with a service type requires administrative privileges. Example 1 - Proof-of-concept ---------------------------- This following example is not typical of an actual deployment. It is shown to allow users to experiment with configuring service subnets. #. Create a network. .. code-block:: console $ openstack network create demo-net1 +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | description | | | headers | | | id | b5b729d8-31cc-4d2c-8284-72b3291fec02 | | ipv4_address_scope | None | | ipv6_address_scope | None | | mtu | 1450 | | name | demo-net1 | | port_security_enabled | True | | project_id | a3db43cd0f224242a847ab84d091217d | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 110 | | revision_number | 1 | | router:external | Internal | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | +---------------------------+--------------------------------------+ #. Create a subnet on the network with one or more service types. For example, the ``compute:nova`` service type enables instances to use this subnet. .. code-block:: console $ openstack subnet create demo-subnet1 --subnet-range 192.0.2.0/24 \ --service-type 'compute:nova' --network demo-net1 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | id | 6e38b23f-0b27-4e3c-8e69-fd23a3df1935 | | ip_version | 4 | | cidr | 192.0.2.0/24 | | name | demo-subnet1 | | network_id | b5b729d8-31cc-4d2c-8284-72b3291fec02 | | revision_number | 1 | | service_types | ['compute:nova'] | | tags | [] | | tenant_id | a8b3054cc1214f18b1186b291525650f | +-------------------+--------------------------------------+ #. Optionally, create another subnet on the network with a different service type. For example, the ``compute:foo`` arbitrary service type. .. code-block:: console $ openstack subnet create demo-subnet2 --subnet-range 198.51.100.0/24 \ --service-type 'compute:foo' --network demo-net1 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | id | ea139dcd-17a3-4f0a-8cca-dff8b4e03f8a | | ip_version | 4 | | cidr | 198.51.100.0/24 | | name | demo-subnet2 | | network_id | b5b729d8-31cc-4d2c-8284-72b3291fec02 | | revision_number | 1 | | service_types | ['compute:foo'] | | tags | [] | | tenant_id | a8b3054cc1214f18b1186b291525650f | +-------------------+--------------------------------------+ #. Launch an instance using the network. For example, using the ``cirros`` image and ``m1.tiny`` flavor. .. code-block:: console $ openstack server create demo-instance1 --flavor m1.tiny \ --image cirros --nic net-id=b5b729d8-31cc-4d2c-8284-72b3291fec02 +--------------------------------------+-----------------------------------------------+ | Field | Value | +--------------------------------------+-----------------------------------------------+ | OS-DCF:diskConfig | MANUAL | | OS-EXT-AZ:availability_zone | | | OS-EXT-SRV-ATTR:host | None | | OS-EXT-SRV-ATTR:hypervisor_hostname | None | | OS-EXT-SRV-ATTR:instance_name | instance-00000009 | | OS-EXT-STS:power_state | 0 | | OS-EXT-STS:task_state | scheduling | | OS-EXT-STS:vm_state | building | | OS-SRV-USG:launched_at | None | | OS-SRV-USG:terminated_at | None | | accessIPv4 | | | accessIPv6 | | | addresses | | | adminPass | Fn85skabdxBL | | config_drive | | | created | 2016-09-19T15:07:42Z | | flavor | m1.tiny (1) | | hostId | | | id | 04222b73-1a6e-4c2a-9af4-ef3d17d521ff | | image | cirros (4aaec87d-c655-4856-8618-b2dada3a2b11) | | key_name | None | | name | demo-instance1 | | os-extended-volumes:volumes_attached | [] | | progress | 0 | | project_id | d44c19e056674381b86430575184b167 | | properties | | | security_groups | [{u'name': u'default'}] | | status | BUILD | | updated | 2016-09-19T15:07:42Z | | user_id | 331afbeb322d4c559a181e19051ae362 | +--------------------------------------+-----------------------------------------------+ #. Check the instance status. The ``Networks`` field contains an IP address from the subnet having the ``compute:nova`` service type. .. code-block:: console $ openstack server list +--------------------------------------+-----------------+---------+---------------------+ | ID | Name | Status | Networks | +--------------------------------------+-----------------+---------+---------------------+ | 20181f46-5cd2-4af8-9af0-f4cf5c983008 | demo-instance1 | ACTIVE | demo-net1=192.0.2.3 | +--------------------------------------+-----------------+---------+---------------------+ Example 2 - DVR configuration ----------------------------- The following example outlines how you can configure service subnets in a DVR-enabled deployment, with the goal of minimizing public IP address consumption. This example uses three subnets on the same external network: * 192.0.2.0/24 for instance floating IP addresses * 198.51.100.0/24 for floating IP agent gateway IPs configured on compute nodes * 203.0.113.0/25 for all other IP allocations on the external network This example uses again the private network, ``demo-net1`` (b5b729d8-31cc-4d2c-8284-72b3291fec02) which was created in `Example 1 - Proof-of-concept`_. .. note: The output of the commands is not always shown since it is very similar to the above. #. Create an external network: .. code-block:: console $ openstack network create --external demo-ext-net #. Create a subnet on the external network for the instance floating IP addresses. This uses the ``network:floatingip`` service type. .. code-block:: console $ openstack subnet create demo-floating-ip-subnet \ --subnet-range 192.0.2.0/24 --no-dhcp \ --service-type 'network:floatingip' --network demo-ext-net #. Create a subnet on the external network for the floating IP agent gateway IP addresses, which are configured by DVR on compute nodes. This will use the ``network:floatingip_agent_gateway`` service type. .. code-block:: console $ openstack subnet create demo-floating-ip-agent-gateway-subnet \ --subnet-range 198.51.100.0/24 --no-dhcp \ --service-type 'network:floatingip_agent_gateway' \ --network demo-ext-net #. Create a subnet on the external network for all other IP addresses allocated on the external network. This will not use any service type. It acts as a fall back for allocations that do not match either of the above two service subnets. .. code-block:: console $ openstack subnet create demo-other-subnet \ --subnet-range 203.0.113.0/25 --no-dhcp \ --network demo-ext-net #. Create a router: .. code-block:: console $ openstack router create demo-router #. Add an interface to the router on demo-subnet1: .. code-block:: console $ openstack router add subnet demo-router demo-subnet1 #. Set the external gateway for the router, which will create an interface and allocate an IP address on demo-ext-net: .. code-block:: console $ neutron router-gateway-set demo-router demo-ext-net #. Launch an instance on a private network and retrieve the neutron port ID that was allocated. As above, use the ``cirros`` image and ``m1.tiny`` flavor: .. code-block:: console $ openstack server create demo-instance1 --flavor m1.tiny \ --image cirros --nic net-id=b5b729d8-31cc-4d2c-8284-72b3291fec02 $ openstack port list --server demo-instance1 +--------------------------------------+------+-------------------+--------------------------------------------------+--------+ | ID | Name | MAC Address | Fixed IP Addresses | Status | +--------------------------------------+------+-------------------+--------------------------------------------------+--------+ | a752bb24-9bf2-4d37-b9d6-07da69c86f19 | | fa:16:3e:99:54:32 | ip_address='203.0.113.130', | ACTIVE | | | | | subnet_id='6e38b23f-0b27-4e3c-8e69-fd23a3df1935' | | +--------------------------------------+------+-------------------+--------------------------------------------------+--------+ #. Associate a floating IP with the instance port and verify it was allocated an IP address from the correct subnet: .. code-block:: console $ openstack floating ip create --port \ a752bb24-9bf2-4d37-b9d6-07da69c86f19 demo-ext-net +---------------------+--------------------------------------+ | Field | Value | +---------------------+--------------------------------------+ | fixed_ip_address | 203.0.113.130 | | floating_ip_address | 192.0.2.12 | | floating_network_id | 02d236d5-dad9-4082-bb6b-5245f9f84d13 | | id | f15cae7f-5e05-4b19-bd25-4bb71edcf3de | | port_id | a752bb24-9bf2-4d37-b9d6-07da69c86f19 | | project_id | d44c19e056674381b86430575184b167 | | revision_number | 1 | | router_id | 5a8ca19f-3703-4f81-bc29-db6bc2f528d6 | | status | ACTIVE | | tags | [] | +---------------------+--------------------------------------+ #. As the `admin` user, verify the neutron routers are allocated IP addresses from their correct subnets. Use ``openstack port list`` to find ports associated with the routers. First, the router gateway external port: .. code-block:: console $ neutron port-show f148ffeb-3c26-4067-bc5f-5c3dfddae2f5 +-----------------------+--------------------------------------------------------------------------+ | Field | Value | +-----------------------+--------------------------------------------------------------------------+ | admin_state_up | UP | | device_id | 5a8ca19f-3703-4f81-bc29-db6bc2f528d6 | | device_owner | network:router_gateway | | extra_dhcp_opts | | | fixed_ips | ip_address='203.0.113.11', | | | subnet_id='67c251d9-2b7a-4200-99f6-e13785b0334d' | | id | f148ffeb-3c26-4067-bc5f-5c3dfddae2f5 | | mac_address | fa:16:3e:2c:0f:69 | | network_id | 02d236d5-dad9-4082-bb6b-5245f9f84d13 | | revision_number | 1 | | project_id | | | status | ACTIVE | | tags | [] | +-----------------------+--------------------------------------------------------------------------+ Second, the router floating IP agent gateway external port: .. code-block:: console $ neutron port-show a2d1e756-8ae1-4f96-9aa1-e7ea16a6a68a +-----------------------+--------------------------------------------------------------------------+ | Field | Value | +-----------------------+--------------------------------------------------------------------------+ | admin_state_up | UP | | device_id | 3d0c98eb-bca3-45cc-8aa4-90ae3deb0844 | | device_owner | network:floatingip_agent_gateway | | extra_dhcp_opts | | | fixed_ips | ip_address='198.51.100.10', | | | subnet_id='67c251d9-2b7a-4200-99f6-e13785b0334d' | | id | a2d1e756-8ae1-4f96-9aa1-e7ea16a6a68a | | mac_address | fa:16:3e:f4:5d:fa | | network_id | 02d236d5-dad9-4082-bb6b-5245f9f84d13 | | project_id | | | revision_number | 1 | | status | ACTIVE | | tags | [] | +-----------------------+--------------------------------------------------------------------------+ neutron-12.1.1/doc/source/admin/intro-nat.rst0000664000175000017500000000716113553660047021130 0ustar zuulzuul00000000000000.. _intro-nat: =========================== Network address translation =========================== *Network Address Translation* (NAT) is a process for modifying the source or destination addresses in the headers of an IP packet while the packet is in transit. In general, the sender and receiver applications are not aware that the IP packets are being manipulated. NAT is often implemented by routers, and so we will refer to the host performing NAT as a *NAT router*. However, in OpenStack deployments it is typically Linux servers that implement the NAT functionality, not hardware routers. These servers use the `iptables `_ software package to implement the NAT functionality. There are multiple variations of NAT, and here we describe three kinds commonly found in OpenStack deployments. SNAT ~~~~ In *Source Network Address Translation* (SNAT), the NAT router modifies the IP address of the sender in IP packets. SNAT is commonly used to enable hosts with *private addresses* to communicate with servers on the public Internet. `RFC 5737 `_ reserves the following three subnets as private addresses: * ``192.0.2.0/24`` * ``198.51.100.0/24`` * ``203.0.113.0/24`` These IP addresses are not publicly routable, meaning that a host on the public Internet can not send an IP packet to any of these addresses. Private IP addresses are widely used in both residential and corporate environments. Often, an application running on a host with a private IP address will need to connect to a server on the public Internet. An example is a user who wants to access a public website such as www.openstack.org. If the IP packets reach the web server at www.openstack.org with a private IP address as the source, then the web server cannot send packets back to the sender. SNAT solves this problem by modifying the source IP address to an IP address that is routable on the public Internet. There are different variations of SNAT; in the form that OpenStack deployments use, a NAT router on the path between the sender and receiver replaces the packet's source IP address with the router's public IP address. The router also modifies the source TCP or UDP port to another value, and the router maintains a record of the sender's true IP address and port, as well as the modified IP address and port. When the router receives a packet with the matching IP address and port, it translates these back to the private IP address and port, and forwards the packet along. Because the NAT router modifies ports as well as IP addresses, this form of SNAT is sometimes referred to as *Port Address Translation* (PAT). It is also sometimes referred to as *NAT overload*. OpenStack uses SNAT to enable applications running inside of instances to connect out to the public Internet. DNAT ~~~~ In *Destination Network Address Translation* (DNAT), the NAT router modifies the IP address of the destination in IP packet headers. OpenStack uses DNAT to route packets from instances to the OpenStack metadata service. Applications running inside of instances access the OpenStack metadata service by making HTTP GET requests to a web server with IP address 169.254.169.254. In an OpenStack deployment, there is no host with this IP address. Instead, OpenStack uses DNAT to change the destination IP of these packets so they reach the network interface that a metadata service is listening on. One-to-one NAT ~~~~~~~~~~~~~~ In *one-to-one NAT*, the NAT router maintains a one-to-one mapping between private IP addresses and public IP addresses. OpenStack uses one-to-one NAT to implement floating IP addresses. neutron-12.1.1/doc/source/admin/config-dns-int.rst0000664000175000017500000003464413553660047022042 0ustar zuulzuul00000000000000.. _config-dns-int: =============== DNS integration =============== This page serves as a guide for how to use the DNS integration functionality of the Networking service. The functionality described covers DNS from two points of view: * The internal DNS functionality offered by the Networking service and its interaction with the Compute service. * Integration of the Compute service and the Networking service with an external DNSaaS (DNS-as-a-Service). Users can control the behavior of the Networking service in regards to DNS using two attributes associated with ports, networks, and floating IPs. The following table shows the attributes available for each one of these resources: .. list-table:: :header-rows: 1 :widths: 30 30 30 * - Resource - dns_name - dns_domain * - Ports - Yes - Yes * - Networks - No - Yes * - Floating IPs - Yes - Yes .. note:: The ``DNS Integration`` extension enables all the attribute and resource combinations shown in the previous table, except for ``dns_domain`` for ports, which requires the ``dns_domain for ports`` extension. .. note:: Since the ``DNS Integration`` extension is a subset of ``dns_domain for ports``, if ``dns_domain`` functionality for ports is required, only the latter extension has to be configured. .. note:: When the ``dns_domain for ports`` extension is configured, ``DNS Integration`` is also included when the Neutron server responds to a request to list the active API extensions. This preserves backwards API compatibility. .. _config-dns-int-dns-resolution: The Networking service internal DNS resolution ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Networking service enables users to control the name assigned to ports by the internal DNS. To enable this functionality, do the following: 1. Edit the ``/etc/neutron/neutron.conf`` file and assign a value different to ``openstacklocal`` (its default value) to the ``dns_domain`` parameter in the ``[default]`` section. As an example: .. code-block:: ini dns_domain = example.org. 2. Add ``dns`` (for the ``DNS Integration`` extension) or ``dns_domain_ports`` (for the ``dns_domain for ports`` extension) to ``extension_drivers`` in the ``[ml2]`` section of ``/etc/neutron/plugins/ml2/ml2_conf.ini``. The following is an example: .. code-block:: console [ml2] extension_drivers = port_security,dns_domain_ports After re-starting the ``neutron-server``, users will be able to assign a ``dns_name`` attribute to their ports. .. note:: The enablement of this functionality is prerequisite for the enablement of the Networking service integration with an external DNS service, which is described in detail in :ref:`config-dns-int-ext-serv`. The following illustrates the creation of a port with ``my-port`` in its ``dns_name`` attribute. .. note:: The name assigned to the port by the Networking service internal DNS is now visible in the response in the ``dns_assignment`` attribute. .. code-block:: console $ neutron port-create my-net --dns-name my-port Created a new port: +-----------------------+-------------------------------------------------------------------------------------+ | Field | Value | +-----------------------+-------------------------------------------------------------------------------------+ | admin_state_up | True | | allowed_address_pairs | | | binding:vnic_type | normal | | device_id | | | device_owner | | | dns_assignment | {"hostname": "my-port", "ip_address": "192.0.2.67", "fqdn": "my-port.example.org."} | | dns_name | my-port | | fixed_ips | {"subnet_id":"6141b474-56cd-430f-b731-71660bb79b79", "ip_address": "192.0.2.67"} | | id | fb3c10f4-017e-420c-9be1-8f8c557ae21f | | mac_address | fa:16:3e:aa:9b:e1 | | name | | | network_id | bf2802a0-99a0-4e8c-91e4-107d03f158ea | | port_security_enabled | True | | revision_number | 1 | | security_groups | 1f0ddd73-7e3c-48bd-a64c-7ded4fe0e635 | | status | DOWN | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | +-----------------------+-------------------------------------------------------------------------------------+ When this functionality is enabled, it is leveraged by the Compute service when creating instances. When allocating ports for an instance during boot, the Compute service populates the ``dns_name`` attributes of these ports with the ``hostname`` attribute of the instance, which is a DNS sanitized version of its display name. As a consequence, at the end of the boot process, the allocated ports will be known in the dnsmasq associated to their networks by their instance ``hostname``. The following is an example of an instance creation, showing how its ``hostname`` populates the ``dns_name`` attribute of the allocated port: .. code-block:: console $ openstack server create --image cirros --flavor 42 \ --nic net-id=37aaff3a-6047-45ac-bf4f-a825e56fd2b3 my_vm +--------------------------------------+----------------------------------------------------------------+ | Field | Value | +--------------------------------------+----------------------------------------------------------------+ | OS-DCF:diskConfig | MANUAL | | OS-EXT-AZ:availability_zone | | | OS-EXT-STS:power_state | 0 | | OS-EXT-STS:task_state | scheduling | | OS-EXT-STS:vm_state | building | | OS-SRV-USG:launched_at | - | | OS-SRV-USG:terminated_at | - | | accessIPv4 | | | accessIPv6 | | | adminPass | dB45Zvo8Jpfe | | config_drive | | | created | 2016-02-05T21:35:04Z | | flavor | m1.nano (42) | | hostId | | | id | 66c13cb4-3002-4ab3-8400-7efc2659c363 | | image | cirros-0.3.5-x86_64-uec(b9d981eb-d21c-4ce2-9dbc-dd38f3d9015f) | | key_name | - | | locked | False | | metadata | {} | | name | my_vm | | os-extended-volumes:volumes_attached | [] | | progress | 0 | | security_groups | default | | status | BUILD | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | | updated | 2016-02-05T21:35:04Z | | user_id | 8bb6e578cba24e7db9d3810633124525 | +--------------------------------------+----------------------------------------------------------------+ $ neutron port-list --device_id 66c13cb4-3002-4ab3-8400-7efc2659c363 +--------------------------------------+------+-------------------+---------------------------------------------------------------------------------------+ | id | name | mac_address | fixed_ips | +--------------------------------------+------+-------------------+---------------------------------------------------------------------------------------+ | b3ecc464-1263-44a7-8c38-2d8a52751773 | | fa:16:3e:a8:ce:b8 | {"subnet_id": "277eca5d-9869-474b-960e-6da5951d09f7", "ip_address": "203.0.113.8"} | | | | | {"subnet_id": "eab47748-3f0a-4775-a09f-b0c24bb64bc4", "ip_address":"2001:db8:10::8"} | +--------------------------------------+------+-------------------+---------------------------------------------------------------------------------------+ $ neutron port-show b3ecc464-1263-44a7-8c38-2d8a52751773 +-----------------------+---------------------------------------------------------------------------------------+ | Field | Value | +-----------------------+---------------------------------------------------------------------------------------+ | admin_state_up | True | | allowed_address_pairs | | | binding:vnic_type | normal | | device_id | 66c13cb4-3002-4ab3-8400-7efc2659c363 | | device_owner | compute:None | | dns_assignment | {"hostname": "my-vm", "ip_address": "203.0.113.8", "fqdn": "my-vm.example.org."} | | | {"hostname": "my-vm", "ip_address": "2001:db8:10::8", "fqdn": "my-vm.example.org."} | | dns_name | my-vm | | extra_dhcp_opts | | | fixed_ips | {"subnet_id": "277eca5d-9869-474b-960e-6da5951d09f7", "ip_address": "203.0.113.8"} | | | {"subnet_id": "eab47748-3f0a-4775-a09f-b0c24bb64bc4", "ip_address": "2001:db8:10::8"} | | id | b3ecc464-1263-44a7-8c38-2d8a52751773 | | mac_address | fa:16:3e:a8:ce:b8 | | name | | | network_id | 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 | | port_security_enabled | True | | revision_number | 1 | | security_groups | 1f0ddd73-7e3c-48bd-a64c-7ded4fe0e635 | | status | ACTIVE | | tags | [] | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | +-----------------------+---------------------------------------------------------------------------------------+ In the above example notice that: * The name given to the instance by the user, ``my_vm``, is sanitized by the Compute service and becomes ``my-vm`` as the port's ``dns_name``. * The port's ``dns_assignment`` attribute shows that its FQDN is ``my-vm.example.org.`` in the Networking service internal DNS, which is the result of concatenating the port's ``dns_name`` with the value configured in the ``dns_domain`` parameter in ``neutron.conf``, as explained previously. * The ``dns_assignment`` attribute also shows that the port's ``hostname`` in the Networking service internal DNS is ``my-vm``. * Instead of having the Compute service create the port for the instance, the user might have created it and assigned a value to its ``dns_name`` attribute. In this case, the value assigned to the ``dns_name`` attribute must be equal to the value that Compute service will assign to the instance's ``hostname``, in this example ``my-vm``. Otherwise, the instance boot will fail. neutron-12.1.1/doc/source/admin/config-dhcp-ha.rst0000664000175000017500000006235713553660047021774 0ustar zuulzuul00000000000000.. _config-dhcp-ha: ========================== High-availability for DHCP ========================== This section describes how to use the agent management (alias agent) and scheduler (alias agent_scheduler) extensions for DHCP agents scalability and HA. .. note:: Use the :command:`openstack extension list` command to check if these extensions are enabled. Check ``agent`` and ``agent_scheduler`` are included in the output. .. code-block:: console $ openstack extension list --network -c Name -c Alias +-------------------------------------------------------------+---------------------------+ | Name | Alias | +-------------------------------------------------------------+---------------------------+ | Default Subnetpools | default-subnetpools | | Network IP Availability | network-ip-availability | | Network Availability Zone | network_availability_zone | | Auto Allocated Topology Services | auto-allocated-topology | | Neutron L3 Configurable external gateway mode | ext-gw-mode | | Port Binding | binding | | Neutron Metering | metering | | agent | agent | | Subnet Allocation | subnet_allocation | | L3 Agent Scheduler | l3_agent_scheduler | | Tag support | tag | | Neutron external network | external-net | | Neutron Service Flavors | flavors | | Network MTU | net-mtu | | Availability Zone | availability_zone | | Quota management support | quotas | | HA Router extension | l3-ha | | Provider Network | provider | | Multi Provider Network | multi-provider | | Address scope | address-scope | | Neutron Extra Route | extraroute | | Subnet service types | subnet-service-types | | Resource timestamps | standard-attr-timestamp | | Neutron Service Type Management | service-type | | Router Flavor Extension | l3-flavors | | Tag support for resources: subnet, subnetpool, port, router | tag-ext | | Neutron Extra DHCP opts | extra_dhcp_opt | | Resource revision numbers | standard-attr-revisions | | Pagination support | pagination | | Sorting support | sorting | | security-group | security-group | | DHCP Agent Scheduler | dhcp_agent_scheduler | | Router Availability Zone | router_availability_zone | | RBAC Policies | rbac-policies | | standard-attr-description | standard-attr-description | | Neutron L3 Router | router | | Allowed Address Pairs | allowed-address-pairs | | project_id field enabled | project-id | | Distributed Virtual Router | dvr | +-------------------------------------------------------------+---------------------------+ Demo setup ~~~~~~~~~~ .. figure:: figures/demo_multiple_dhcp_agents.png There will be three hosts in the setup. .. list-table:: :widths: 25 50 :header-rows: 1 * - Host - Description * - OpenStack controller host - controlnode - Runs the Networking, Identity, and Compute services that are required to deploy VMs. The node must have at least one network interface that is connected to the Management Network. Note that ``nova-network`` should not be running because it is replaced by Neutron. * - HostA - Runs ``nova-compute``, the Neutron L2 agent and DHCP agent * - HostB - Same as HostA Configuration ~~~~~~~~~~~~~ **controlnode: neutron server** #. Neutron configuration file ``/etc/neutron/neutron.conf``: .. code-block:: ini [DEFAULT] core_plugin = linuxbridge rabbit_host = controlnode allow_overlapping_ips = True host = controlnode agent_down_time = 5 dhcp_agents_per_network = 1 .. note:: In the above configuration, we use ``dhcp_agents_per_network = 1`` for this demonstration. In usual deployments, we suggest setting ``dhcp_agents_per_network`` to more than one to match the number of DHCP agents in your deployment. See :ref:`conf-dhcp-agents-per-network`. #. Update the plug-in configuration file ``/etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini``: .. code-block:: ini [vlans] tenant_network_type = vlan network_vlan_ranges = physnet1:1000:2999 [database] connection = mysql://root:root@127.0.0.1:3306/neutron_linux_bridge retry_interval = 2 [linux_bridge] physical_interface_mappings = physnet1:eth0 **HostA and HostB: L2 agent** #. Neutron configuration file ``/etc/neutron/neutron.conf``: .. code-block:: ini [DEFAULT] rabbit_host = controlnode rabbit_password = openstack # host = HostB on hostb host = HostA #. Update the plug-in configuration file ``/etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini``: .. code-block:: ini [vlans] tenant_network_type = vlan network_vlan_ranges = physnet1:1000:2999 [database] connection = mysql://root:root@127.0.0.1:3306/neutron_linux_bridge retry_interval = 2 [linux_bridge] physical_interface_mappings = physnet1:eth0 #. Update the nova configuration file ``/etc/nova/nova.conf``: .. code-block:: ini [DEFAULT] use_neutron=True firewall_driver=nova.virt.firewall.NoopFirewallDriver [neutron] admin_username=neutron admin_password=servicepassword admin_auth_url=http://controlnode:35357/v2.0/ auth_strategy=keystone admin_tenant_name=servicetenant url=http://203.0.113.10:9696/ **HostA and HostB: DHCP agent** - Update the DHCP configuration file ``/etc/neutron/dhcp_agent.ini``: .. code-block:: ini [DEFAULT] interface_driver = neutron.agent.linux.interface.BridgeInterfaceDriver Prerequisites for demonstration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Admin role is required to use the agent management and scheduler extensions. Ensure you run the following commands under a project with an admin role. To experiment, you need VMs and a neutron network: .. code-block:: console $ openstack server list +--------------------------------------+-----------+--------+----------------+------------+ | ID | Name | Status | Networks | Image Name | +--------------------------------------+-----------+--------+----------------+------------+ | c394fcd0-0baa-43ae-a793-201815c3e8ce | myserver1 | ACTIVE | net1=192.0.2.3 | cirros | | 2d604e05-9a6c-4ddb-9082-8a1fbdcc797d | myserver2 | ACTIVE | net1=192.0.2.4 | ubuntu | | c7c0481c-3db8-4d7a-a948-60ce8211d585 | myserver3 | ACTIVE | net1=192.0.2.5 | centos | +--------------------------------------+-----------+--------+----------------+------------+ $ openstack network list +--------------------------------------+------+--------------------------------------+ | ID | Name | Subnets | +--------------------------------------+------+--------------------------------------+ | ad88e059-e7fa-4cf7-8857-6731a2a3a554 | net1 | 8086db87-3a7a-4cad-88c9-7bab9bc69258 | +--------------------------------------+------+--------------------------------------+ Managing agents in neutron deployment ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #. List all agents: .. code-block:: console $ openstack network agent list +--------------------------------------+--------------------+-------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+-------+-------------------+-------+-------+---------------------------+ | 22467163-01ea-4231-ba45-3bd316f425e6 | Linux bridge agent | HostA | None | True | UP | neutron-linuxbridge-agent | | 2444c54d-0d28-460c-ab0f-cd1e6b5d3c7b | DHCP agent | HostA | None | True | UP | neutron-dhcp-agent | | 3066d20c-9f8f-440c-ae7c-a40ffb4256b6 | Linux bridge agent | HostB | nova | True | UP | neutron-linuxbridge-agent | | 55569f4e-6f31-41a6-be9d-526efce1f7fe | DHCP agent | HostB | nova | True | UP | neutron-dhcp-agent | +--------------------------------------+--------------------+-------+-------------------+-------+-------+---------------------------+ Every agent that supports these extensions will register itself with the neutron server when it starts up. The output shows information for four agents. The ``alive`` field shows ``True`` if the agent reported its state within the period defined by the ``agent_down_time`` option in the ``neutron.conf`` file. Otherwise the ``alive`` is ``False``. #. List DHCP agents that host a specified network: .. code-block:: console $ openstack network agent list --network net1 +--------------------------------------+---------------+----------------+-------+ | ID | Host | Admin State Up | Alive | +--------------------------------------+---------------+----------------+-------+ | 22467163-01ea-4231-ba45-3bd316f425e6 | HostA | UP | True | +--------------------------------------+---------------+----------------+-------+ #. List the networks hosted by a given DHCP agent: This command is to show which networks a given dhcp agent is managing. .. code-block:: console $ openstack network list --agent 22467163-01ea-4231-ba45-3bd316f425e6 +--------------------------------+------------------------+---------------------------------+ | ID | Name | Subnets | +--------------------------------+------------------------+---------------------------------+ | ad88e059-e7fa- | net1 | 8086db87-3a7a-4cad- | | 4cf7-8857-6731a2a3a554 | | 88c9-7bab9bc69258 | +--------------------------------+------------------------+---------------------------------+ #. Show agent details. The :command:`openstack network agent show` command shows details for a specified agent: .. code-block:: console $ openstack network agent show 2444c54d-0d28-460c-ab0f-cd1e6b5d3c7b +---------------------+--------------------------------------------------+ | Field | Value | +---------------------+--------------------------------------------------+ | admin_state_up | UP | | agent_type | DHCP agent | | alive | True | | availability_zone | nova | | binary | neutron-dhcp-agent | | configurations | dhcp_driver='neutron.agent.linux.dhcp.Dnsmasq', | | | dhcp_lease_duration='86400', | | | log_agent_heartbeats='False', networks='1', | | | notifies_port_ready='True', ports='3', | | | subnets='1' | | created_at | 2016-12-14 00:25:54 | | description | None | | last_heartbeat_at | 2016-12-14 06:53:24 | | host | HostA | | id | 2444c54d-0d28-460c-ab0f-cd1e6b5d3c7b | | started_at | 2016-12-14 00:25:54 | | topic | dhcp_agent | +---------------------+--------------------------------------------------+ In this output, ``last_heartbeat_at`` is the time on the neutron server. You do not need to synchronize all agents to this time for this extension to run correctly. ``configurations`` describes the static configuration for the agent or run time data. This agent is a DHCP agent and it hosts one network, one subnet, and three ports. Different types of agents show different details. The following output shows information for a Linux bridge agent: .. code-block:: console $ openstack network agent show 22467163-01ea-4231-ba45-3bd316f425e6 +---------------------+--------------------------------------+ | Field | Value | +---------------------+--------------------------------------+ | admin_state_up | UP | | agent_type | Linux bridge agent | | alive | True | | availability_zone | nova | | binary | neutron-linuxbridge-agent | | configurations | { | | | "physnet1": "eth0", | | | "devices": "4" | | | } | | created_at | 2016-12-14 00:26:54 | | description | None | | last_heartbeat_at | 2016-12-14 06:53:24 | | host | HostA | | id | 22467163-01ea-4231-ba45-3bd316f425e6 | | started_at | 2016-12-14T06:48:39.000000 | | topic | N/A | +---------------------+--------------------------------------+ The output shows ``bridge-mapping`` and the number of virtual network devices on this L2 agent. Managing assignment of networks to DHCP agent ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A single network can be assigned to more than one DHCP agents and one DHCP agent can host more than one network. You can add a network to a DHCP agent and remove one from it. #. Default scheduling. When you create a network with one port, the network will be scheduled to an active DHCP agent. If many active DHCP agents are running, select one randomly. You can design more sophisticated scheduling algorithms in the same way as nova-schedule later on. .. code-block:: console $ openstack network create net2 $ openstack subnet create --network net2 --subnet-range 198.51.100.0/24 subnet2 $ openstack port create port2 --network net2 $ openstack network agent list --network net2 +--------------------------------------+---------------+----------------+-------+ | ID | Host | Admin State Up | Alive | +--------------------------------------+---------------+----------------+-------+ | 2444c54d-0d28-460c-ab0f-cd1e6b5d3c7b | HostA | UP | True | +--------------------------------------+---------------+----------------+-------+ It is allocated to DHCP agent on HostA. If you want to validate the behavior through the :command:`dnsmasq` command, you must create a subnet for the network because the DHCP agent starts the dnsmasq service only if there is a DHCP. #. Assign a network to a given DHCP agent. To add another DHCP agent to host the network, run this command: .. code-block:: console $ openstack network agent add network --dhcp \ 55569f4e-6f31-41a6-be9d-526efce1f7fe net2 $ openstack network agent list --network net2 +--------------------------------------+-------+----------------+--------+ | ID | Host | Admin State Up | Alive | +--------------------------------------+-------+----------------+--------+ | 2444c54d-0d28-460c-ab0f-cd1e6b5d3c7b | HostA | UP | True | | 55569f4e-6f31-41a6-be9d-526efce1f7fe | HostB | UP | True | +--------------------------------------+-------+----------------+--------+ Both DHCP agents host the ``net2`` network. #. Remove a network from a specified DHCP agent. This command is the sibling command for the previous one. Remove ``net2`` from the DHCP agent for HostA: .. code-block:: console $ openstack network agent remove network --dhcp \ 2444c54d-0d28-460c-ab0f-cd1e6b5d3c7b net2 $ openstack network agent list --network net2 +--------------------------------------+-------+----------------+-------+ | ID | Host | Admin State Up | Alive | +--------------------------------------+-------+----------------+-------+ | 55569f4e-6f31-41a6-be9d-526efce1f7fe | HostB | UP | True | +--------------------------------------+-------+----------------+-------+ You can see that only the DHCP agent for HostB is hosting the ``net2`` network. HA of DHCP agents ~~~~~~~~~~~~~~~~~ Boot a VM on ``net2``. Let both DHCP agents host ``net2``. Fail the agents in turn to see if the VM can still get the desired IP. #. Boot a VM on ``net2``: .. code-block:: console $ openstack network list +--------------------------------------+------+--------------------------------------+ | ID | Name | Subnets | +--------------------------------------+------+--------------------------------------+ | ad88e059-e7fa-4cf7-8857-6731a2a3a554 | net1 | 8086db87-3a7a-4cad-88c9-7bab9bc69258 | | 9b96b14f-71b8-4918-90aa-c5d705606b1a | net2 | 6979b71a-0ae8-448c-aa87-65f68eedcaaa | +--------------------------------------+------+--------------------------------------+ $ openstack server create --image tty --flavor 1 myserver4 \ --nic net-id=9b96b14f-71b8-4918-90aa-c5d705606b1a ... $ openstack server list +--------------------------------------+-----------+--------+-------------------+------------+ | ID | Name | Status | Networks | Image Name | +--------------------------------------+-----------+--------+-------------------+------------+ | c394fcd0-0baa-43ae-a793-201815c3e8ce | myserver1 | ACTIVE | net1=192.0.2.3 | cirros | | 2d604e05-9a6c-4ddb-9082-8a1fbdcc797d | myserver2 | ACTIVE | net1=192.0.2.4 | ubuntu | | c7c0481c-3db8-4d7a-a948-60ce8211d585 | myserver3 | ACTIVE | net1=192.0.2.5 | centos | | f62f4731-5591-46b1-9d74-f0c901de567f | myserver4 | ACTIVE | net2=198.51.100.2 | cirros1 | +--------------------------------------+-----------+--------+-------------------+------------+ #. Make sure both DHCP agents hosting ``net2``: Use the previous commands to assign the network to agents. .. code-block:: console $ openstack network agent list --network net2 +--------------------------------------+-------+----------------+-------+ | ID | Host | Admin State Up | Alive | +--------------------------------------+-------+----------------+-------+ | 2444c54d-0d28-460c-ab0f-cd1e6b5d3c7b | HostA | UP | True | | 55569f4e-6f31-41a6-be9d-526efce1f7fe | HostB | UP | True | +--------------------------------------+-------+----------------+-------+ To test the HA of DHCP agent: #. Log in to the ``myserver4`` VM, and run ``udhcpc``, ``dhclient`` or other DHCP client. #. Stop the DHCP agent on HostA. Besides stopping the ``neutron-dhcp-agent`` binary, you must stop the ``dnsmasq`` processes. #. Run a DHCP client in VM to see if it can get the wanted IP. #. Stop the DHCP agent on HostB too. #. Run ``udhcpc`` in the VM; it cannot get the wanted IP. #. Start DHCP agent on HostB. The VM gets the wanted IP again. Disabling and removing an agent ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ An administrator might want to disable an agent if a system hardware or software upgrade is planned. Some agents that support scheduling also support disabling and enabling agents, such as L3 and DHCP agents. After the agent is disabled, the scheduler does not schedule new resources to the agent. After the agent is disabled, you can safely remove the agent. Even after disabling the agent, resources on the agent are kept assigned. Ensure you remove the resources on the agent before you delete the agent. Disable the DHCP agent on HostA before you stop it: .. code-block:: console $ openstack network agent set 2444c54d-0d28-460c-ab0f-cd1e6b5d3c7b --disable $ openstack network agent list +--------------------------------------+--------------------+-------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+-------+-------------------+-------+-------+---------------------------+ | 22467163-01ea-4231-ba45-3bd316f425e6 | Linux bridge agent | HostA | None | True | UP | neutron-linuxbridge-agent | | 2444c54d-0d28-460c-ab0f-cd1e6b5d3c7b | DHCP agent | HostA | None | True | DOWN | neutron-dhcp-agent | | 3066d20c-9f8f-440c-ae7c-a40ffb4256b6 | Linux bridge agent | HostB | nova | True | UP | neutron-linuxbridge-agent | | 55569f4e-6f31-41a6-be9d-526efce1f7fe | DHCP agent | HostB | nova | True | UP | neutron-dhcp-agent | +--------------------------------------+--------------------+-------+-------------------+-------+-------+---------------------------+ After you stop the DHCP agent on HostA, you can delete it by the following command: .. code-block:: console $ openstack network agent delete 2444c54d-0d28-460c-ab0f-cd1e6b5d3c7b $ openstack network agent list +--------------------------------------+--------------------+-------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+-------+-------------------+-------+-------+---------------------------+ | 22467163-01ea-4231-ba45-3bd316f425e6 | Linux bridge agent | HostA | None | True | UP | neutron-linuxbridge-agent | | 3066d20c-9f8f-440c-ae7c-a40ffb4256b6 | Linux bridge agent | HostB | nova | True | UP | neutron-linuxbridge-agent | | 55569f4e-6f31-41a6-be9d-526efce1f7fe | DHCP agent | HostB | nova | True | UP | neutron-dhcp-agent | +--------------------------------------+--------------------+-------+-------------------+-------+-------+---------------------------+ After deletion, if you restart the DHCP agent, it appears on the agent list again. .. _conf-dhcp-agents-per-network: Enabling DHCP high availability by default ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can control the default number of DHCP agents assigned to a network by setting the following configuration option in the file ``/etc/neutron/neutron.conf``. .. code-block:: ini dhcp_agents_per_network = 3 neutron-12.1.1/doc/source/admin/intro-network-namespaces.rst0000664000175000017500000000532613553660046024154 0ustar zuulzuul00000000000000.. _intro-network-namespaces: ================== Network namespaces ================== A namespace is a way of scoping a particular set of identifiers. Using a namespace, you can use the same identifier multiple times in different namespaces. You can also restrict an identifier set visible to particular processes. For example, Linux provides namespaces for networking and processes, among other things. If a process is running within a process namespace, it can only see and communicate with other processes in the same namespace. So, if a shell in a particular process namespace ran :command:`ps waux`, it would only show the other processes in the same namespace. Linux network namespaces ~~~~~~~~~~~~~~~~~~~~~~~~ In a network namespace, the scoped 'identifiers' are network devices; so a given network device, such as ``eth0``, exists in a particular namespace. Linux starts up with a default network namespace, so if your operating system does not do anything special, that is where all the network devices will be located. But it is also possible to create further non-default namespaces, and create new devices in those namespaces, or to move an existing device from one namespace to another. Each network namespace also has its own routing table, and in fact this is the main reason for namespaces to exist. A routing table is keyed by destination IP address, so network namespaces are what you need if you want the same destination IP address to mean different things at different times - which is something that OpenStack Networking requires for its feature of providing overlapping IP addresses in different virtual networks. Each network namespace also has its own set of iptables (for both IPv4 and IPv6). So, you can apply different security to flows with the same IP addressing in different namespaces, as well as different routing. Any given Linux process runs in a particular network namespace. By default this is inherited from its parent process, but a process with the right capabilities can switch itself into a different namespace; in practice this is mostly done using the :command:`ip netns exec NETNS COMMAND...` invocation, which starts ``COMMAND`` running in the namespace named ``NETNS``. Suppose such a process sends out a message to IP address A.B.C.D, the effect of the namespace is that A.B.C.D will be looked up in that namespace's routing table, and that will determine the network device that the message is transmitted through. Virtual routing and forwarding (VRF) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Virtual routing and forwarding is an IP technology that allows multiple instances of a routing table to coexist on the same router at the same time. It is another name for the network namespace functionality described above. neutron-12.1.1/doc/source/admin/ops-resource-purge.rst0000664000175000017500000000263013553660046022756 0ustar zuulzuul00000000000000.. _ops-resource-purge: ============== Resource purge ============== The Networking service provides a purge mechanism to delete the following network resources for a project: * Networks * Subnets * Ports * Router interfaces * Routers * Floating IP addresses * Security groups Typically, one uses this mechanism to delete networking resources for a defunct project regardless of its existence in the Identity service. Usage ~~~~~ #. Source the necessary project credentials. The administrative project can delete resources for all other projects. A regular project can delete its own network resources and those belonging to other projects for which it has sufficient access. #. Delete the network resources for a particular project. .. code-block:: console $ neutron purge PROJECT_ID Replace ``PROJECT_ID`` with the project ID. The command provides output that includes a completion percentage and the quantity of successful or unsuccessful network resource deletions. An unsuccessful deletion usually indicates sharing of a resource with one or more additional projects. .. code-block:: console Purging resources: 100% complete. Deleted 1 security_group, 2 ports, 1 router, 1 floatingip, 2 networks. The following resources could not be deleted: 1 network. The command also indicates if a project lacks network resources. .. code-block:: console Tenant has no supported resources. neutron-12.1.1/doc/source/admin/config-dns-int-ext-serv.rst0000664000175000017500000017460513553660047023617 0ustar zuulzuul00000000000000.. _config-dns-int-ext-serv: ======================================== DNS integration with an external service ======================================== This page serves as a guide for how to use the DNS integration functionality of the Networking service with an external DNSaaS (DNS-as-a-Service). As a prerequisite this needs the internal DNS functionality offered by the Networking service to be enabled, see :ref:`config-dns-int`. Configuring OpenStack Networking for integration with an external DNS service ----------------------------------------------------------------------------- The first step to configure the integration with an external DNS service is to enable the functionality described in :ref:`config-dns-int-dns-resolution`. Once this is done, the user has to take the following steps and restart ``neutron-server``. #. Edit the ``[default]`` section of ``/etc/neutron/neutron.conf`` and specify the external DNS service driver to be used in parameter ``external_dns_driver``. The valid options are defined in namespace ``neutron.services.external_dns_drivers``. The following example shows how to set up the driver for the OpenStack DNS service: .. code-block:: console external_dns_driver = designate #. If the OpenStack DNS service is the target external DNS, the ``[designate]`` section of ``/etc/neutron/neutron.conf`` must define the following parameters: * ``url``: the OpenStack DNS service public endpoint URL. Note that this must always be the versioned endpoint currently. * ``auth_type``: the authorization plugin to use. Usually this should be ``password``, see https://docs.openstack.org/keystoneauth/latest/authentication-plugins.html for other options. * ``auth_url``: the Identity service authorization endpoint url. This endpoint will be used by the Networking service to authenticate as an user to create and update reverse lookup (PTR) zones. * ``username``: the username to be used by the Networking service to create and update reverse lookup (PTR) zones. * ``password``: the password of the user to be used by the Networking service to create and update reverse lookup (PTR) zones. * ``project_name``: the name of the project to be used by the Networking service to create and update reverse lookup (PTR) zones. * ``project_domain_name``: the name of the domain for the project to be used by the Networking service to create and update reverse lookup (PTR) zones. * ``user_domain_name``: the name of the domain for the user to be used by the Networking service to create and update reverse lookup (PTR) zones. * ``region_name``: the name of the region to be used by the Networking service to create and update reverse lookup (PTR) zones. * ``allow_reverse_dns_lookup``: a boolean value specifying whether to enable or not the creation of reverse lookup (PTR) records. * ``ipv4_ptr_zone_prefix_size``: the size in bits of the prefix for the IPv4 reverse lookup (PTR) zones. * ``ipv6_ptr_zone_prefix_size``: the size in bits of the prefix for the IPv6 reverse lookup (PTR) zones. * ``ptr_zone_email``: the email address to use when creating new reverse lookup (PTR) zones. The default is ``admin@`` where ```` is the domain for the first record being created in that zone. * ``insecure``: whether to disable SSL certificate validation. By default, certificates are validated. * ``cafile``: Path to a valid Certificate Authority (CA) certificate. Optional, the system CAs are used as default. * ``auth_uri``: the unversioned public endpoint of the Identity service. The following is an example: .. code-block:: console [designate] url = http://192.0.2.240:9001/v2 auth_type = password auth_url = http://192.0.2.240:35357 username = neutron password = PASSWORD project_name = service project_domain_name = Default user_domain_name = Default allow_reverse_dns_lookup = True ipv4_ptr_zone_prefix_size = 24 ipv6_ptr_zone_prefix_size = 116 ptr_zone_email = admin@example.org cafile = /etc/ssl/certs/my_ca_cert auth_uri = http://192.0.2.240:5000 Once the ``neutron-server`` has been configured and restarted, users will have functionality that covers three use cases, described in the following sections. In each of the use cases described below: * The examples assume the OpenStack DNS service as the external DNS. * A, AAAA and PTR records will be created in the DNS service. * Before executing any of the use cases, the user must create in the DNS service under his project a DNS zone where the A and AAAA records will be created. For the description of the use cases below, it is assumed the zone ``example.org.`` was created previously. * The PTR records will be created in zones owned by the project specified for ``project_name`` above. * Despite officially being deprecated, using the neutron CLI is still necessary for some of the tasks, as the corresponding features are not yet implemented for the openstack client. Use case 1: Floating IPs are published with associated port DNS attributes -------------------------------------------------------------------------- In this use case, the address of a floating IP is published in the external DNS service in conjunction with the ``dns_name`` of its associated port and the ``dns_domain`` of the port's network. The steps to execute in this use case are the following: #. Assign a valid domain name to the network's ``dns_domain`` attribute. This name must end with a period (``.``). #. Boot an instance or alternatively, create a port specifying a valid value to its ``dns_name`` attribute. If the port is going to be used for an instance boot, the value assigned to ``dns_name`` must be equal to the ``hostname`` that the Compute service will assign to the instance. Otherwise, the boot will fail. #. Create a floating IP and associate it to the port. Following is an example of these steps: .. code-block:: console $ neutron net-update 38c5e950-b450-4c30-83d4-ee181c28aad3 --dns_domain example.org. Updated network: 38c5e950-b450-4c30-83d4-ee181c28aad3 $ neutron net-show 38c5e950-b450-4c30-83d4-ee181c28aad3 +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | True | | availability_zone_hints | | | availability_zones | nova | | dns_domain | example.org. | | id | 38c5e950-b450-4c30-83d4-ee181c28aad3 | | mtu | 1450 | | name | private | | port_security_enabled | True | | revision_number | 1 | | router:external | False | | shared | False | | status | ACTIVE | | subnets | 43414c53-62ae-49bc-aa6c-c9dd7705818a | | | 5b9282a1-0be1-4ade-b478-7868ad2a16ff | | tags | [] | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | +-------------------------+--------------------------------------+ $ openstack server create --image cirros --flavor 42 \ --nic net-id=38c5e950-b450-4c30-83d4-ee181c28aad3 my_vm +--------------------------------------+----------------------------------------------------------------+ | Field | Value | +--------------------------------------+----------------------------------------------------------------+ | OS-DCF:diskConfig | MANUAL | | OS-EXT-AZ:availability_zone | | | OS-EXT-STS:power_state | 0 | | OS-EXT-STS:task_state | scheduling | | OS-EXT-STS:vm_state | building | | OS-SRV-USG:launched_at | - | | OS-SRV-USG:terminated_at | - | | accessIPv4 | | | accessIPv6 | | | adminPass | oTLQLR3Kezmt | | config_drive | | | created | 2016-02-15T19:27:34Z | | flavor | m1.nano (42) | | hostId | | | id | 43f328bb-b2d1-4cf1-a36f-3b2593397cb1 | | image | cirros-0.3.5-x86_64-uec (b9d981eb-d21c-4ce2-9dbc-dd38f3d9015f) | | key_name | - | | locked | False | | metadata | {} | | name | my_vm | | os-extended-volumes:volumes_attached | [] | | progress | 0 | | security_groups | default | | status | BUILD | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | | updated | 2016-02-15T19:27:34Z | | user_id | 8bb6e578cba24e7db9d3810633124525 | +--------------------------------------+----------------------------------------------------------------+ $ openstack server list +--------------------------------------+-------+--------+------------+-------------+----------------------------------------------------------+------------+ | ID | Name | Status | Task State | Power State | Networks | Image Name | +--------------------------------------+-------+--------+------------+-------------+----------------------------------------------------------+------------+ | 43f328bb-b2d1-4cf1-a36f-3b2593397cb1 | my_vm | ACTIVE | - | Running | private=fda4:653e:71b0:0:f816:3eff:fe16:b5f2, 192.0.2.15 | cirros | +--------------------------------------+-------+--------+------------+-------------+----------------------------------------------------------+------------+ $ neutron port-list --device_id 43f328bb-b2d1-4cf1-a36f-3b2593397cb1 +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ | id | name | mac_address | fixed_ips | +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ | da0b1f75-c895-460f-9fc1-4d6ec84cf85f | | fa:16:3e:16:b5:f2 | {"subnet_id": "5b9282a1-0be1-4ade-b478-7868ad2a16ff", "ip_address": "192.0.2.15"} | | | | | {"subnet_id": "43414c53-62ae-49bc-aa6c-c9dd7705818a", "ip_address": "fda4:653e:71b0:0:f816:3eff:fe16:b5f2"} | +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ $ neutron port-show da0b1f75-c895-460f-9fc1-4d6ec84cf85f +-----------------------+-------------------------------------------------------------------------------------------------------------+ | Field | Value | +-----------------------+-------------------------------------------------------------------------------------------------------------+ | admin_state_up | True | | allowed_address_pairs | | | binding:vnic_type | normal | | device_id | 43f328bb-b2d1-4cf1-a36f-3b2593397cb1 | | device_owner | compute:None | | dns_assignment | {"hostname": "my-vm", "ip_address": "192.0.2.15", "fqdn": "my-vm.example.org."} | | | {"hostname": "my-vm", "ip_address": "fda4:653e:71b0:0:f816:3eff:fe16:b5f2", "fqdn": "my-vm.example.org."} | | dns_name | my-vm | | extra_dhcp_opts | | | fixed_ips | {"subnet_id": "5b9282a1-0be1-4ade-b478-7868ad2a16ff", "ip_address": "192.0.2.15"} | | | {"subnet_id": "43414c53-62ae-49bc-aa6c-c9dd7705818a", "ip_address": "fda4:653e:71b0:0:f816:3eff:fe16:b5f2"} | | id | da0b1f75-c895-460f-9fc1-4d6ec84cf85f | | mac_address | fa:16:3e:16:b5:f2 | | name | | | network_id | 38c5e950-b450-4c30-83d4-ee181c28aad3 | | port_security_enabled | True | | revision_number | 1 | | security_groups | 1f0ddd73-7e3c-48bd-a64c-7ded4fe0e635 | | status | ACTIVE | | tags | [] | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | +-----------------------+-------------------------------------------------------------------------------------------------------------+ $ openstack recordset list example.org. +--------------------------------------+--------------------+------+-----------------------------------------------------------------------+--------+--------+ | id | name | type | records | status | action | +--------------------------------------+--------------------+------+-----------------------------------------------------------------------+--------+--------+ | a5fe696d-203f-4018-b0d8-590221adb513 | example.org. | NS | ns1.devstack.org. | ACTIVE | NONE | | e7c05a5d-83a0-4fe5-8bd5-ab058a3326aa | example.org. | SOA | ns1.devstack.org. malavall.us.ibm.com. 1513767794 3532 600 86400 3600 | ACTIVE | NONE | +--------------------------------------+--------------------+------+-----------------------------------------------------------------------+--------+--------+ $ neutron floatingip-create 41fa3995-9e4a-4cd9-bb51-3e5424f2ff2a \ --port_id da0b1f75-c895-460f-9fc1-4d6ec84cf85f Created a new floatingip: +---------------------+--------------------------------------+ | Field | Value | +---------------------+--------------------------------------+ | dns_domain | | | dns_name | | | fixed_ip_address | 192.0.2.15 | | floating_ip_address | 198.51.100.4 | | floating_network_id | 41fa3995-9e4a-4cd9-bb51-3e5424f2ff2a | | id | e78f6eb1-a35f-4a90-941d-87c888d5fcc7 | | port_id | da0b1f75-c895-460f-9fc1-4d6ec84cf85f | | revision_number | 1 | | router_id | 970ebe83-c4a3-4642-810e-43ab7b0c2b5f | | status | DOWN | | tags | [] | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | +---------------------+--------------------------------------+ $ openstack recordset list example.org. +--------------------------------------+--------------------+------+-----------------------------------------------------------------------+--------+--------+ | id | name | type | records | status | action | +--------------------------------------+--------------------+------+-----------------------------------------------------------------------+--------+--------+ | a5fe696d-203f-4018-b0d8-590221adb513 | example.org. | NS | ns1.devstack.org. | ACTIVE | NONE | | e7c05a5d-83a0-4fe5-8bd5-ab058a3326aa | example.org. | SOA | ns1.devstack.org. malavall.us.ibm.com. 1513768814 3532 600 86400 3600 | ACTIVE | NONE | | 5ff53fd0-3746-48da-b9c9-77ed3004ec67 | my-vm.example.org. | A | 198.51.100.4 | ACTIVE | NONE | +--------------------------------------+--------------------+------+-----------------------------------------------------------------------+--------+--------+ In this example, notice that the data is published in the DNS service when the floating IP is associated to the port. Following are the PTR records created for this example. Note that for IPv4, the value of ``ipv4_ptr_zone_prefix_size`` is 24. Also, since the zone for the PTR records is created in the ``service`` project, you need to use admin credentials in order to be able to view it. .. code-block:: console $ openstack recordset list --all-projects 100.51.198.in-addr.arpa. +--------------------------------------+----------------------------------+----------------------------+------+---------------------------------------------------------------------+--------+--------+ | id | project_id | name | type | data | status | action | +--------------------------------------+----------------------------------+-----------------------------------+---------------------------------------------------------------------+--------+--------+ | 2dd0b894-25fa-4563-9d32-9f13bd67f329 | 07224d17d76d42499a38f00ba4339710 | 100.51.198.in-addr.arpa. | NS | ns1.devstack.org. | ACTIVE | NONE | | 47b920f1-5eff-4dfa-9616-7cb5b7cb7ca6 | 07224d17d76d42499a38f00ba4339710 | 100.51.198.in-addr.arpa. | SOA | ns1.devstack.org. admin.example.org. 1455564862 3600 600 86400 3600 | ACTIVE | NONE | | fb1edf42-abba-410c-8397-831f45fd0cd7 | 07224d17d76d42499a38f00ba4339710 | 4.100.51.198.in-addr.arpa. | PTR | my-vm.example.org. | ACTIVE | NONE | +--------------------------------------+----------------------------------+----------------------------+------+---------------------------------------------------------------------+--------+--------+ Use case 2: Floating IPs are published in the external DNS service ------------------------------------------------------------------ In this use case, the user assigns ``dns_name`` and ``dns_domain`` attributes to a floating IP when it is created. The floating IP data becomes visible in the external DNS service as soon as it is created. The floating IP can be associated with a port on creation or later on. The following example shows a user booting an instance and then creating a floating IP associated to the port allocated for the instance: .. code-block:: console $ neutron net-show 38c5e950-b450-4c30-83d4-ee181c28aad3 +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | True | | availability_zone_hints | | | availability_zones | nova | | dns_domain | example.org. | | id | 38c5e950-b450-4c30-83d4-ee181c28aad3 | | mtu | 1450 | | name | private | | port_security_enabled | True | | revision_number | 1 | | router:external | False | | shared | False | | status | ACTIVE | | subnets | 43414c53-62ae-49bc-aa6c-c9dd7705818a | | | 5b9282a1-0be1-4ade-b478-7868ad2a16ff | | tags | [] | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | +-------------------------+--------------------------------------+ $ openstack server create --image cirros --flavor 42 \ --nic net-id=38c5e950-b450-4c30-83d4-ee181c28aad3 my_vm +--------------------------------------+----------------------------------------------------------------+ | Field | Value | +--------------------------------------+----------------------------------------------------------------+ | OS-DCF:diskConfig | MANUAL | | OS-EXT-AZ:availability_zone | | | OS-EXT-STS:power_state | 0 | | OS-EXT-STS:task_state | scheduling | | OS-EXT-STS:vm_state | building | | OS-SRV-USG:launched_at | - | | OS-SRV-USG:terminated_at | - | | accessIPv4 | | | accessIPv6 | | | adminPass | HLXGznYqXM4J | | config_drive | | | created | 2016-02-15T19:42:44Z | | flavor | m1.nano (42) | | hostId | | | id | 71fb4ac8-eed8-4644-8113-0641962bb125 | | image | cirros-0.3.5-x86_64-uec (b9d981eb-d21c-4ce2-9dbc-dd38f3d9015f) | | key_name | - | | locked | False | | metadata | {} | | name | my_vm | | os-extended-volumes:volumes_attached | [] | | progress | 0 | | security_groups | default | | status | BUILD | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | | updated | 2016-02-15T19:42:44Z | | user_id | 8bb6e578cba24e7db9d3810633124525 | +--------------------------------------+----------------------------------------------------------------+ $ openstack server list +--------------------------------------+-------+--------+------------+-------------+----------------------------------------------------------+------------+ | ID | Name | Status | Task State | Power State | Networks | Image Name | +--------------------------------------+-------+--------+------------+-------------+----------------------------------------------------------+------------+ | 71fb4ac8-eed8-4644-8113-0641962bb125 | my_vm | ACTIVE | - | Running | private=fda4:653e:71b0:0:f816:3eff:fe24:8614, 192.0.2.16 | cirros | +--------------------------------------+-------+--------+------------+-------------+----------------------------------------------------------+------------+ $ neutron port-list --device_id 71fb4ac8-eed8-4644-8113-0641962bb125 +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ | id | name | mac_address | fixed_ips | +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ | 1e7033fb-8e9d-458b-89ed-8312cafcfdcb | | fa:16:3e:24:86:14 | {"subnet_id": "5b9282a1-0be1-4ade-b478-7868ad2a16ff", "ip_address": "192.0.2.16"} | | | | | {"subnet_id": "43414c53-62ae-49bc-aa6c-c9dd7705818a", "ip_address": "fda4:653e:71b0:0:f816:3eff:fe24:8614"} | +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ $ neutron port-show 1e7033fb-8e9d-458b-89ed-8312cafcfdcb +-----------------------+-------------------------------------------------------------------------------------------------------------+ | Field | Value | +-----------------------+-------------------------------------------------------------------------------------------------------------+ | admin_state_up | True | | allowed_address_pairs | | | binding:vnic_type | normal | | device_id | 71fb4ac8-eed8-4644-8113-0641962bb125 | | device_owner | compute:None | | dns_assignment | {"hostname": "my-vm", "ip_address": "192.0.2.16", "fqdn": "my-vm.example.org."} | | | {"hostname": "my-vm", "ip_address": "fda4:653e:71b0:0:f816:3eff:fe24:8614", "fqdn": "my-vm.example.org."} | | dns_name | my-vm | | extra_dhcp_opts | | | fixed_ips | {"subnet_id": "5b9282a1-0be1-4ade-b478-7868ad2a16ff", "ip_address": "192.0.2.16"} | | | {"subnet_id": "43414c53-62ae-49bc-aa6c-c9dd7705818a", "ip_address": "fda4:653e:71b0:0:f816:3eff:fe24:8614"} | | id | 1e7033fb-8e9d-458b-89ed-8312cafcfdcb | | mac_address | fa:16:3e:24:86:14 | | name | | | network_id | 38c5e950-b450-4c30-83d4-ee181c28aad3 | | port_security_enabled | True | | revision_number | 1 | | security_groups | 1f0ddd73-7e3c-48bd-a64c-7ded4fe0e635 | | status | ACTIVE | | tags | [] | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | +-----------------------+-------------------------------------------------------------------------------------------------------------+ $ openstack recordset list example.org. +--------------------------------------+--------------------+------+-----------------------------------------------------------------------+--------+--------+ | id | name | type | records | status | action | +--------------------------------------+--------------------+------+-----------------------------------------------------------------------+--------+--------+ | 56ca0b88-e343-4c98-8faa-19746e169baf | example.org. | NS | ns1.devstack.org. | ACTIVE | NONE | | 10a36008-6ecf-47c3-b321-05652a929b04 | example.org. | SOA | ns1.devstack.org. malavall.us.ibm.com. 1455565110 3532 600 86400 3600 | ACTIVE | NONE | +--------------------------------------+--------------------+------+-----------------------------------------------------------------------+--------+--------+ $ neutron floatingip-create 41fa3995-9e4a-4cd9-bb51-3e5424f2ff2a \ --dns_domain example.org. --dns_name my-floatingip Created a new floatingip: +---------------------+--------------------------------------+ | Field | Value | +---------------------+--------------------------------------+ | dns_domain | example.org. | | dns_name | my-floatingip | | fixed_ip_address | | | floating_ip_address | 198.51.100.5 | | floating_network_id | 41fa3995-9e4a-4cd9-bb51-3e5424f2ff2a | | id | 9f23a9c6-eceb-42eb-9f45-beb58c473728 | | port_id | | | revision_number | 1 | | router_id | | | status | DOWN | | tags | [] | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | +---------------------+--------------------------------------+ $ openstack recordset list example.org. +--------------------------------------+----------------------------+------+-----------------------------------------------------------------------+--------+--------+ | id | name | type | records | status | action | +--------------------------------------+----------------------------+------+-----------------------------------------------------------------------+--------+--------+ | 56ca0b88-e343-4c98-8faa-19746e169baf | example.org. | NS | ns1.devstack.org. | ACTIVE | NONE | | 10a36008-6ecf-47c3-b321-05652a929b04 | example.org. | SOA | ns1.devstack.org. malavall.us.ibm.com. 1455565110 3532 600 86400 3600 | ACTIVE | NONE | | 8884c56f-3ef5-446e-ae4d-8053cc8bc2b4 | my-floatingip.example.org. | A | 198.51.100.53 | ACTIVE | NONE | +--------------------------------------+----------------------------+------+-----------------------------------------------------------------------+--------+--------+ Note that in this use case: * The ``dns_name`` and ``dns_domain`` attributes of a floating IP must be specified together on creation. They cannot be assigned to the floating IP separately and they cannot be changed after the floating IP has been created. * The ``dns_name`` and ``dns_domain`` of a floating IP have precedence, for purposes of being published in the external DNS service, over the ``dns_name`` of its associated port and the ``dns_domain`` of the port's network, whether they are specified or not. Only the ``dns_name`` and the ``dns_domain`` of the floating IP are published in the external DNS service. Following are the PTR records created for this example. Note that for IPv4, the value of ``ipv4_ptr_zone_prefix_size`` is 24. Also, since the zone for the PTR records is created in the ``service`` project, you need to use admin credentials in order to be able to view it. .. code-block:: console $ openstack recordset list --all-projects 100.51.198.in-addr.arpa. +--------------------------------------+----------------------------------+----------------------------+------+---------------------------------------------------------------------+--------+--------+ | id | project_id | name | type | data | status | action | +--------------------------------------+----------------------------------+-----------------------------------+---------------------------------------------------------------------+--------+--------+ | 2dd0b894-25fa-4563-9d32-9f13bd67f329 | 07224d17d76d42499a38f00ba4339710 | 100.51.198.in-addr.arpa. | NS | ns1.devstack.org. | ACTIVE | NONE | | 47b920f1-5eff-4dfa-9616-7cb5b7cb7ca6 | 07224d17d76d42499a38f00ba4339710 | 100.51.198.in-addr.arpa. | SOA | ns1.devstack.org. admin.example.org. 1455564862 3600 600 86400 3600 | ACTIVE | NONE | | 589a0171-e77a-4ab6-ba6e-23114f2b9366 | 07224d17d76d42499a38f00ba4339710 | 5.100.51.198.in-addr.arpa. | PTR | my-floatingip.example.org. | ACTIVE | NONE | +--------------------------------------+----------------------------------+----------------------------+------+---------------------------------------------------------------------+--------+--------+ .. _config-dns-use-case-3: Use case 3: Ports are published directly in the external DNS service -------------------------------------------------------------------- In this case, the user is creating ports or booting instances on a network that is accessible externally. If the user wants to publish a port in the external DNS service in a zone specified by the ``dns_domain`` attribute of the network, these are the steps to be taken: #. Assign a valid domain name to the network's ``dns_domain`` attribute. This name must end with a period (``.``). #. Boot an instance specifying the externally accessible network. Alternatively, create a port on the externally accessible network specifying a valid value to its ``dns_name`` attribute. If the port is going to be used for an instance boot, the value assigned to ``dns_name`` must be equal to the ``hostname`` that the Compute service will assign to the instance. Otherwise, the boot will fail. Once these steps are executed, the port's DNS data will be published in the external DNS service. This is an example: .. code-block:: console $ neutron net-list +--------------------------------------+----------+----------------------------------------------------------+ | id | name | subnets | +--------------------------------------+----------+----------------------------------------------------------+ | 41fa3995-9e4a-4cd9-bb51-3e5424f2ff2a | public | a67cfdf7-9d5d-406f-8a19-3f38e4fc3e74 | | | | cbd8c6dc-ca81-457e-9c5d-f8ece7ef67f8 | | 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 | external | 277eca5d-9869-474b-960e-6da5951d09f7 203.0.113.0/24 | | | | eab47748-3f0a-4775-a09f-b0c24bb64bc4 2001:db8:10::/64 | | bf2802a0-99a0-4e8c-91e4-107d03f158ea | my-net | 6141b474-56cd-430f-b731-71660bb79b79 192.0.2.64/26 | | 38c5e950-b450-4c30-83d4-ee181c28aad3 | private | 43414c53-62ae-49bc-aa6c-c9dd7705818a fda4:653e:71b0::/64 | | | | 5b9282a1-0be1-4ade-b478-7868ad2a16ff 192.0.2.0/26 | +--------------------------------------+----------+----------------------------------------------------------+ $ neutron net-update 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 --dns_domain example.org. Updated network: 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 $ neutron net-show 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | True | | availability_zone_hints | | | availability_zones | nova | | dns_domain | example.org. | | id | 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 | | mtu | 1450 | | name | external | | port_security_enabled | True | | provider:network_type | vlan | | provider:physical_network | | | provider:segmentation_id | 2016 | | revision_number | 4 | | router:external | False | | shared | True | | status | ACTIVE | | subnets | eab47748-3f0a-4775-a09f-b0c24bb64bc4 | | | 277eca5d-9869-474b-960e-6da5951d09f7 | | tags | [] | | tenant_id | 04fc2f83966245dba907efb783f8eab9 | +---------------------------+--------------------------------------+ $ openstack recordset list example.org. +--------------------------------------+--------------+------+-----------------------------------------------------------------------+--------+--------+ | id | name | type | records | status | action | +--------------------------------------+--------------+------+-----------------------------------------------------------------------+--------+--------+ | a5fe696d-203f-4018-b0d8-590221adb513 | example.org. | NS | ns1.devstack.org. | ACTIVE | NONE | | e7c05a5d-83a0-4fe5-8bd5-ab058a3326aa | example.org. | SOA | ns1.devstack.org. malavall.us.ibm.com. 1513767619 3532 600 86400 3600 | ACTIVE | NONE | +--------------------------------------+--------------+------+-----------------------------------------------------------------------+--------+--------+ $ neutron port-create 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 --dns_name my-vm Created a new port: +-----------------------+---------------------------------------------------------------------------------------+ | Field | Value | +-----------------------+---------------------------------------------------------------------------------------+ | admin_state_up | True | | allowed_address_pairs | | | binding:vnic_type | normal | | device_id | | | device_owner | | | dns_assignment | {"hostname": "my-vm", "ip_address": "203.0.113.9", "fqdn": "my-vm.example.org."} | | | {"hostname": "my-vm", "ip_address": "2001:db8:10::9", "fqdn": "my-vm.example.org."} | | dns_name | my-vm | | fixed_ips | {"subnet_id": "277eca5d-9869-474b-960e-6da5951d09f7", "ip_address": "203.0.113.9"} | | | {"subnet_id": "eab47748-3f0a-4775-a09f-b0c24bb64bc4", "ip_address": "2001:db8:10::9"} | | id | 04be331b-dc5e-410a-9103-9c8983aeb186 | | mac_address | fa:16:3e:0f:4b:e4 | | name | | | network_id | 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 | | port_security_enabled | True | | revision_number | 1 | | security_groups | 1f0ddd73-7e3c-48bd-a64c-7ded4fe0e635 | | status | DOWN | | tags | [] | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | +-----------------------+---------------------------------------------------------------------------------------+ $ openstack recordset list example.org. +--------------------------------------+--------------------+------+-----------------------------------------------------------------------+--------+--------+ | id | name | type | records | status | action | +--------------------------------------+--------------------+------+-----------------------------------------------------------------------+--------+--------+ | a5fe696d-203f-4018-b0d8-590221adb513 | example.org. | NS | ns1.devstack.org. | ACTIVE | NONE | | e7c05a5d-83a0-4fe5-8bd5-ab058a3326aa | example.org. | SOA | ns1.devstack.org. malavall.us.ibm.com. 1513767794 3532 600 86400 3600 | ACTIVE | NONE | | fa753ab8-bffa-400d-9ef8-d4a3b1a7ffbf | my-vm.example.org. | A | 203.0.113.9 | ACTIVE | NONE | | 04abf9f8-c7a3-43f6-9a55-95cee9b144a9 | my-vm.example.org. | AAAA | 2001:db8:10::9 | ACTIVE | NONE | +--------------------------------------+--------------------+------+-----------------------------------------------------------------------+--------+--------+ $ openstack server create --image cirros --flavor 42 \ --nic port-id=04be331b-dc5e-410a-9103-9c8983aeb186 my_vm +--------------------------------------+----------------------------------------------------------------+ | Field | Value | +--------------------------------------+----------------------------------------------------------------+ | OS-DCF:diskConfig | MANUAL | | OS-EXT-AZ:availability_zone | | | OS-EXT-STS:power_state | 0 | | OS-EXT-STS:task_state | scheduling | | OS-EXT-STS:vm_state | building | | OS-SRV-USG:launched_at | - | | OS-SRV-USG:terminated_at | - | | accessIPv4 | | | accessIPv6 | | | adminPass | TDc9EpBT3B9W | | config_drive | | | created | 2016-02-15T19:10:43Z | | flavor | m1.nano (42) | | hostId | | | id | 62c19691-d1c7-4d7b-a88e-9cc4d95d4f41 | | image | cirros-0.3.5-x86_64-uec (b9d981eb-d21c-4ce2-9dbc-dd38f3d9015f) | | key_name | - | | locked | False | | metadata | {} | | name | my_vm | | os-extended-volumes:volumes_attached | [] | | progress | 0 | | security_groups | default | | status | BUILD | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | | updated | 2016-02-15T19:10:43Z | | user_id | 8bb6e578cba24e7db9d3810633124525 | +--------------------------------------+----------------------------------------------------------------+ $ openstack server list +--------------------------------------+-------+--------+------------+-------------+--------------------------------------+------------+ | ID | Name | Status | Task State | Power State | Networks | Image Name | +--------------------------------------+-------+--------+------------+-------------+--------------------------------------+------------+ | 62c19691-d1c7-4d7b-a88e-9cc4d95d4f41 | my_vm | ACTIVE | - | Running | external=203.0.113.9, 2001:db8:10::9 | cirros | +--------------------------------------+-------+--------+------------+-------------+--------------------------------------+------------+ In this example the port is created manually by the user and then used to boot an instance. Notice that: * The port's data was visible in the DNS service as soon as it was created. * See :ref:`config-dns-performance-considerations` for an explanation of the potential performance impact associated with this use case. Following are the PTR records created for this example. Note that for IPv4, the value of ipv4_ptr_zone_prefix_size is 24. In the case of IPv6, the value of ipv6_ptr_zone_prefix_size is 116. .. code-block:: console $ openstack recordset list --all-projects 113.0.203.in-addr.arpa. +--------------------------------------+----------------------------------+---------------------------+------+---------------------------------------------------------------------+--------+--------+ | id | project_id | name | type | records | status | action | +--------------------------------------+----------------------------------+---------------------------+------+---------------------------------------------------------------------+--------+--------+ | 32f1c05b-7c5d-4230-9088-961a0a462d28 | 07224d17d76d42499a38f00ba4339710 | 113.0.203.in-addr.arpa. | SOA | ns1.devstack.org. admin.example.org. 1455563035 3600 600 86400 3600 | ACTIVE | NONE | | 3d402c43-b215-4a75-a730-51cbb8999cb8 | 07224d17d76d42499a38f00ba4339710 | 113.0.203.in-addr.arpa. | NS | ns1.devstack.org. | ACTIVE | NONE | | 8e4e618c-24b0-43db-ab06-91b741a91c10 | 07224d17d76d42499a38f00ba4339710 | 9.113.0.203.in-addr.arpa. | PTR | my-vm.example.org. | ACTIVE | NONE | +--------------------------------------+----------------------------------+---------------------------+------+---------------------------------------------------------------------+--------+--------+ $ openstack recordset list --all-projects 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.8.b.d.0.1.0.0.2.ip6.arpa. +--------------------------------------+----------------------------------+---------------------------------------------------------------------------+------+---------------------------------------------------------------------+--------+--------+ | id | project_id | name | type | records | status | action | +--------------------------------------+----------------------------------+---------------------------------------------------------------------------+------+---------------------------------------------------------------------+--------+--------+ | d8923354-13eb-4bd9-914a-0a2ae5f95989 | 07224d17d76d42499a38f00ba4339710 | 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.8.b.d.0.1.0.0.2.ip6.arpa. | SOA | ns1.devstack.org. admin.example.org. 1455563036 3600 600 86400 3600 | ACTIVE | NONE | | 72e60acd-098d-41ea-9771-5b6546c9c06f | 07224d17d76d42499a38f00ba4339710 | 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.8.b.d.0.1.0.0.2.ip6.arpa. | NS | ns1.devstack.org. | ACTIVE | NONE | | 877e0215-2ddf-4d01-a7da-47f1092dfd56 | 07224d17d76d42499a38f00ba4339710 | 9.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.8.b.d.0.1.0.0.2.ip6.arpa. | PTR | my-vm.example.org. | ACTIVE | NONE | +--------------------------------------+----------------------------------+---------------------------------------------------------------------------+------+---------------------------------------------------------------------+--------+--------+ See :ref:`config-dns-int-ext-serv-net` for detailed instructions on how to create the externally accessible network. Alternatively, if the ``dns_domain for ports`` extension has been configured, the user can create a port specifying a non-blank value in its ``dns_domain`` attribute, as shown here: .. code-block:: console $ neutron port-create 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 \ --dns-name my-vm --dns_domain port-domain.org. Created a new port: +-----------------------+---------------------------------------------------------------------------------------+ | Field | Value | +-----------------------+---------------------------------------------------------------------------------------+ | admin_state_up | True | | allowed_address_pairs | | | binding:vnic_type | normal | | created_at | 2017-08-16T22:05:57Z | | description | | | device_id | | | device_owner | | | dns_assignment | {"hostname": "my-vm", "ip_address": "203.0.113.9", "fqdn": "my-vm.example.org."} | | | {"hostname": "my-vm", "ip_address": "2001:db8:10::9", "fqdn": "my-vm.example.org."} | | dns_domain | port-domain.org. | | dns_name | my-vm | | extra_dhcp_opts | | | fixed_ips | {"subnet_id": "277eca5d-9869-474b-960e-6da5951d09f7", "ip_address": "203.0.113.9"} | | | {"subnet_id": "eab47748-3f0a-4775-a09f-b0c24bb64bc4", "ip_address": "2001:db8:10::9"} | | id | 422134a8-1088-458d-adbd-880863d8c07c | | ip_allocation | immediate | | mac_address | fa:16:3e:fb:d6:24 | | name | | | network_id | 37aaff3a-6047-45ac-bf4f-a825e56fd2b3 | | port_security_enabled | True | | project_id | d5660cb1e6934612a01b4fb2fb630725 | | revision_number | 5 | | security_groups | 07b21ad4-edb6-420b-bd76-9bb4aab0d135 | | status | DOWN | | tags | | | tenant_id | d5660cb1e6934612a01b4fb2fb630725 | | updated_at | 2017-08-16T22:05:58Z | +-----------------------+---------------------------------------------------------------------------------------+ In this case, the port's ``dns_name`` (``my-vm``) will be published in the ``port-domain.org.`` zone, as shown here: .. code-block:: console $ openstack recordset list port-domain.org. +--------------------------------------+-------------------------+------+-----------------------------------------------------------------------+--------+--------+ | id | name | type | records | status | action | +--------------------------------------+-------------------------+------+-----------------------------------------------------------------------+--------+--------+ | 03e5a35b-d984-4d10-942a-2de8ccb9b941 | port-domain.org. | SOA | ns1.devstack.org. malavall.us.ibm.com. 1503272259 3549 600 86400 3600 | ACTIVE | NONE | | d2dd1dfe-531d-4fea-8c0e-f5b559942ac5 | port-domain.org. | NS | ns1.devstack.org. | ACTIVE | NONE | | 67a8e83d-7e3c-4fb1-9261-0481318bb7b5 | my-vm.port-domain.org. | A | 203.0.113.9 | ACTIVE | NONE | | 5a4f671c-9969-47aa-82e1-e05754021852 | my-vm.port-domain.org. | AAAA | 2001:db8:10::9 | ACTIVE | NONE | +--------------------------------------+-------------------------+------+-----------------------------------------------------------------------+--------+--------+ .. note:: If both the port and its network have a valid non-blank string assigned to their ``dns_domain`` attributes, the port's ``dns_domain`` takes precedence over the network's. .. note:: The name assigned to the port's ``dns_domain`` attribute must end with a period (``.``). .. note:: In the above example, the ``port-domain.org.`` zone must be created before Neutron can publish any port data to it. .. _config-dns-performance-considerations: Performance considerations -------------------------- Only for :ref:`config-dns-use-case-3`, if the port binding extension is enabled in the Networking service, the Compute service will execute one additional port update operation when allocating the port for the instance during the boot process. This may have a noticeable adverse effect in the performance of the boot process that should be evaluated before adoption of this use case. .. _config-dns-int-ext-serv-net: Configuration of the externally accessible network for use case 3 ----------------------------------------------------------------- In :ref:`config-dns-use-case-3`, the externally accessible network must meet the following requirements: * The network may not have attribute ``router:external`` set to ``True``. * The network type can be FLAT, VLAN, GRE, VXLAN or GENEVE. * For network types VLAN, GRE, VXLAN or GENEVE, the segmentation ID must be outside the ranges assigned to project networks. This usually implies that this use case only works for networks specifically created for this purpose by an admin, it does not work for networks which tenants can create. neutron-12.1.1/doc/source/admin/config-ipam.rst0000664000175000017500000000345513553660047021410 0ustar zuulzuul00000000000000.. _config-ipam: ================== IPAM configuration ================== .. note:: Experimental feature or incomplete documentation. Starting with the Liberty release, OpenStack Networking includes a pluggable interface for the IP Address Management (IPAM) function. This interface creates a driver framework for the allocation and de-allocation of subnets and IP addresses, enabling the integration of alternate IPAM implementations or third-party IP Address Management systems. The basics ~~~~~~~~~~ In Liberty and Mitaka, the IPAM implementation within OpenStack Networking provided a pluggable and non-pluggable flavor. As of Newton, the non-pluggable flavor is no longer available. Instead, it is completely replaced with a reference driver implementation of the pluggable framework. All data will be automatically migrated during the upgrade process, unless you have previously configured a pluggable IPAM driver. In that case, no migration is necessary. To configure a driver other than the reference driver, specify it in the ``neutron.conf`` file. Do this after the migration is complete. There is no need to specify any value if you wish to use the reference driver. .. code-block:: ini ipam_driver = ipam-driver-name There is no need to specify any value if you wish to use the reference driver, though specifying ``internal`` will explicitly choose the reference driver. The documentation for any alternate drivers will include the value to use when specifying that driver. Known limitations ~~~~~~~~~~~~~~~~~ * The driver interface is designed to allow separate drivers for each subnet pool. However, the current implementation allows only a single IPAM driver system-wide. * Third-party drivers must provide their own migration mechanisms to convert existing OpenStack installations to their IPAM. neutron-12.1.1/doc/source/admin/deploy-ovs-selfservice.rst0000664000175000017500000005207313553660047023630 0ustar zuulzuul00000000000000.. _deploy-ovs-selfservice: =================================== Open vSwitch: Self-service networks =================================== This architecture example augments :ref:`deploy-ovs-provider` to support a nearly limitless quantity of entirely virtual networks. Although the Networking service supports VLAN self-service networks, this example focuses on VXLAN self-service networks. For more information on self-service networks, see :ref:`intro-os-networking-selfservice`. Prerequisites ~~~~~~~~~~~~~ Add one network node with the following components: * Three network interfaces: management, provider, and overlay. * OpenStack Networking Open vSwitch (OVS) layer-2 agent, layer-3 agent, and any including OVS. Modify the compute nodes with the following components: * Add one network interface: overlay. .. note:: You can keep the DHCP and metadata agents on each compute node or move them to the network node. Architecture ~~~~~~~~~~~~ .. image:: figures/deploy-ovs-selfservice-overview.png :alt: Self-service networks using OVS - overview The following figure shows components and connectivity for one self-service network and one untagged (flat) provider network. In this particular case, the instance resides on the same compute node as the DHCP agent for the network. If the DHCP agent resides on another compute node, the latter only contains a DHCP namespace and with a port on the OVS integration bridge. .. image:: figures/deploy-ovs-selfservice-compconn1.png :alt: Self-service networks using OVS - components and connectivity - one network Example configuration ~~~~~~~~~~~~~~~~~~~~~ Use the following example configuration as a template to add support for self-service networks to an existing operational environment that supports provider networks. Controller node --------------- #. In the ``neutron.conf`` file: * Enable routing and allow overlapping IP address ranges. .. code-block:: ini [DEFAULT] service_plugins = router allow_overlapping_ips = True #. In the ``ml2_conf.ini`` file: * Add ``vxlan`` to type drivers and project network types. .. code-block:: ini [ml2] type_drivers = flat,vlan,vxlan tenant_network_types = vxlan * Enable the layer-2 population mechanism driver. .. code-block:: ini [ml2] mechanism_drivers = openvswitch,l2population * Configure the VXLAN network ID (VNI) range. .. code-block:: ini [ml2_type_vxlan] vni_ranges = VNI_START:VNI_END Replace ``VNI_START`` and ``VNI_END`` with appropriate numerical values. #. Restart the following services: * Neutron Server * Open vSwitch agent Network node ------------ #. Install the Networking service OVS layer-2 agent and layer-3 agent. #. Install OVS. #. In the ``neutron.conf`` file, configure common options: .. include:: shared/deploy-config-neutron-common.txt #. Start the following services: * OVS #. Create the OVS provider bridge ``br-provider``: .. code-block:: console $ ovs-vsctl add-br br-provider #. Add the provider network interface as a port on the OVS provider bridge ``br-provider``: .. code-block:: console $ ovs-vsctl add-port br-provider PROVIDER_INTERFACE Replace ``PROVIDER_INTERFACE`` with the name of the underlying interface that handles provider networks. For example, ``eth1``. #. In the ``openvswitch_agent.ini`` file, configure the layer-2 agent. .. code-block:: ini [ovs] bridge_mappings = provider:br-provider local_ip = OVERLAY_INTERFACE_IP_ADDRESS [agent] tunnel_types = vxlan l2_population = True [securitygroup] firewall_driver = iptables_hybrid Replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the IP address of the interface that handles VXLAN overlays for self-service networks. #. In the ``l3_agent.ini`` file, configure the layer-3 agent. .. code-block:: ini [DEFAULT] interface_driver = openvswitch external_network_bridge = .. note:: The ``external_network_bridge`` option intentionally contains no value. #. Start the following services: * Open vSwitch agent * Layer-3 agent Compute nodes ------------- #. In the ``openvswitch_agent.ini`` file, enable VXLAN support including layer-2 population. .. code-block:: ini [ovs] local_ip = OVERLAY_INTERFACE_IP_ADDRESS [agent] tunnel_types = vxlan l2_population = True Replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the IP address of the interface that handles VXLAN overlays for self-service networks. #. Restart the following services: * Open vSwitch agent Verify service operation ------------------------ #. Source the administrative project credentials. #. Verify presence and operation of the agents. .. code-block:: console $ openstack network agent list +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | 1236bbcb-e0ba-48a9-80fc-81202ca4fa51 | Metadata agent | compute2 | | True | UP | neutron-metadata-agent | | 457d6898-b373-4bb3-b41f-59345dcfb5c5 | Open vSwitch agent | compute2 | | True | UP | neutron-openvswitch-agent | | 71f15e84-bc47-4c2a-b9fb-317840b2d753 | DHCP agent | compute2 | nova | True | UP | neutron-dhcp-agent | | 8805b962-de95-4e40-bdc2-7a0add7521e8 | L3 agent | network1 | nova | True | UP | neutron-l3-agent | | a33cac5a-0266-48f6-9cac-4cef4f8b0358 | Open vSwitch agent | network1 | | True | UP | neutron-openvswitch-agent | | a6c69690-e7f7-4e56-9831-1282753e5007 | Metadata agent | compute1 | | True | UP | neutron-metadata-agent | | af11f22f-a9f4-404f-9fd8-cd7ad55c0f68 | DHCP agent | compute1 | nova | True | UP | neutron-dhcp-agent | | bcfc977b-ec0e-4ba9-be62-9489b4b0e6f1 | Open vSwitch agent | compute1 | | True | UP | neutron-openvswitch-agent | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ Create initial networks ----------------------- .. include:: shared/deploy-selfservice-initialnetworks.txt Verify network operation ------------------------ .. include:: shared/deploy-selfservice-verifynetworkoperation.txt .. _deploy-ovs-selfservice-networktrafficflow: Network traffic flow ~~~~~~~~~~~~~~~~~~~~ .. include:: shared/deploy-selfservice-networktrafficflow.txt .. _deploy-ovs-selfservice-networktrafficflow-ns1: North-south scenario 1: Instance with a fixed IP address -------------------------------------------------------- For instances with a fixed IPv4 address, the network node performs SNAT on north-south traffic passing from self-service to external networks such as the Internet. For instances with a fixed IPv6 address, the network node performs conventional routing of traffic between self-service and external networks. * The instance resides on compute node 1 and uses self-service network 1. * The instance sends a packet to a host on the Internet. The following steps involve compute node 1: #. The instance interface (1) forwards the packet to the security group bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge OVS port (4) forwards the packet to the OVS integration bridge security group port (5) via ``veth`` pair. #. The OVS integration bridge adds an internal VLAN tag to the packet. #. The OVS integration bridge exchanges the internal VLAN tag for an internal tunnel ID. #. The OVS integration bridge patch port (6) forwards the packet to the OVS tunnel bridge patch port (7). #. The OVS tunnel bridge (8) wraps the packet using VNI 101. #. The underlying physical interface (9) for overlay networks forwards the packet to the network node via the overlay network (10). The following steps involve the network node: #. The underlying physical interface (11) for overlay networks forwards the packet to the OVS tunnel bridge (12). #. The OVS tunnel bridge unwraps the packet and adds an internal tunnel ID to it. #. The OVS tunnel bridge exchanges the internal tunnel ID for an internal VLAN tag. #. The OVS tunnel bridge patch port (13) forwards the packet to the OVS integration bridge patch port (14). #. The OVS integration bridge port for the self-service network (15) removes the internal VLAN tag and forwards the packet to the self-service network interface (16) in the router namespace. * For IPv4, the router performs SNAT on the packet which changes the source IP address to the router IP address on the provider network and sends it to the gateway IP address on the provider network via the gateway interface on the provider network (17). * For IPv6, the router sends the packet to the next-hop IP address, typically the gateway IP address on the provider network, via the provider gateway interface (17). #. The router forwards the packet to the OVS integration bridge port for the provider network (18). #. The OVS integration bridge adds the internal VLAN tag to the packet. #. The OVS integration bridge ``int-br-provider`` patch port (19) forwards the packet to the OVS provider bridge ``phy-br-provider`` patch port (20). #. The OVS provider bridge swaps the internal VLAN tag with actual VLAN tag 101. #. The OVS provider bridge provider network port (21) forwards the packet to the physical network interface (22). #. The physical network interface forwards the packet to the Internet via physical network infrastructure (23). .. note:: Return traffic follows similar steps in reverse. However, without a floating IPv4 address, hosts on the provider or external networks cannot originate connections to instances on the self-service network. .. image:: figures/deploy-ovs-selfservice-flowns1.png :alt: Self-service networks using Open vSwitch - network traffic flow - north/south scenario 1 North-south scenario 2: Instance with a floating IPv4 address ------------------------------------------------------------- For instances with a floating IPv4 address, the network node performs SNAT on north-south traffic passing from the instance to external networks such as the Internet and DNAT on north-south traffic passing from external networks to the instance. Floating IP addresses and NAT do not apply to IPv6. Thus, the network node routes IPv6 traffic in this scenario. * The instance resides on compute node 1 and uses self-service network 1. * A host on the Internet sends a packet to the instance. The following steps involve the network node: #. The physical network infrastructure (1) forwards the packet to the provider physical network interface (2). #. The provider physical network interface forwards the packet to the OVS provider bridge provider network port (3). #. The OVS provider bridge swaps actual VLAN tag 101 with the internal VLAN tag. #. The OVS provider bridge ``phy-br-provider`` port (4) forwards the packet to the OVS integration bridge ``int-br-provider`` port (5). #. The OVS integration bridge port for the provider network (6) removes the internal VLAN tag and forwards the packet to the provider network interface (6) in the router namespace. * For IPv4, the router performs DNAT on the packet which changes the destination IP address to the instance IP address on the self-service network and sends it to the gateway IP address on the self-service network via the self-service interface (7). * For IPv6, the router sends the packet to the next-hop IP address, typically the gateway IP address on the self-service network, via the self-service interface (8). #. The router forwards the packet to the OVS integration bridge port for the self-service network (9). #. The OVS integration bridge adds an internal VLAN tag to the packet. #. The OVS integration bridge exchanges the internal VLAN tag for an internal tunnel ID. #. The OVS integration bridge ``patch-tun`` patch port (10) forwards the packet to the OVS tunnel bridge ``patch-int`` patch port (11). #. The OVS tunnel bridge (12) wraps the packet using VNI 101. #. The underlying physical interface (13) for overlay networks forwards the packet to the network node via the overlay network (14). The following steps involve the compute node: #. The underlying physical interface (15) for overlay networks forwards the packet to the OVS tunnel bridge (16). #. The OVS tunnel bridge unwraps the packet and adds an internal tunnel ID to it. #. The OVS tunnel bridge exchanges the internal tunnel ID for an internal VLAN tag. #. The OVS tunnel bridge ``patch-int`` patch port (17) forwards the packet to the OVS integration bridge ``patch-tun`` patch port (18). #. The OVS integration bridge removes the internal VLAN tag from the packet. #. The OVS integration bridge security group port (19) forwards the packet to the security group bridge OVS port (20) via ``veth`` pair. #. Security group rules (21) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge instance port (22) forwards the packet to the instance interface (23) via ``veth`` pair. .. image:: figures/deploy-ovs-selfservice-flowns2.png :alt: Self-service networks using Open vSwitch - network traffic flow - north/south scenario 2 .. note:: Egress instance traffic flows similar to north-south scenario 1, except SNAT changes the source IP address of the packet to the floating IPv4 address rather than the router IP address on the provider network. East-west scenario 1: Instances on the same network --------------------------------------------------- Instances with a fixed IPv4/IPv6 address or floating IPv4 address on the same network communicate directly between compute nodes containing those instances. By default, the VXLAN protocol lacks knowledge of target location and uses multicast to discover it. After discovery, it stores the location in the local forwarding database. In large deployments, the discovery process can generate a significant amount of network that all nodes must process. To eliminate the latter and generally increase efficiency, the Networking service includes the layer-2 population mechanism driver that automatically populates the forwarding database for VXLAN interfaces. The example configuration enables this driver. For more information, see :ref:`config-plugin-ml2`. * Instance 1 resides on compute node 1 and uses self-service network 1. * Instance 2 resides on compute node 2 and uses self-service network 1. * Instance 1 sends a packet to instance 2. The following steps involve compute node 1: #. The instance 1 interface (1) forwards the packet to the security group bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge OVS port (4) forwards the packet to the OVS integration bridge security group port (5) via ``veth`` pair. #. The OVS integration bridge adds an internal VLAN tag to the packet. #. The OVS integration bridge exchanges the internal VLAN tag for an internal tunnel ID. #. The OVS integration bridge patch port (6) forwards the packet to the OVS tunnel bridge patch port (7). #. The OVS tunnel bridge (8) wraps the packet using VNI 101. #. The underlying physical interface (9) for overlay networks forwards the packet to compute node 2 via the overlay network (10). The following steps involve compute node 2: #. The underlying physical interface (11) for overlay networks forwards the packet to the OVS tunnel bridge (12). #. The OVS tunnel bridge unwraps the packet and adds an internal tunnel ID to it. #. The OVS tunnel bridge exchanges the internal tunnel ID for an internal VLAN tag. #. The OVS tunnel bridge ``patch-int`` patch port (13) forwards the packet to the OVS integration bridge ``patch-tun`` patch port (14). #. The OVS integration bridge removes the internal VLAN tag from the packet. #. The OVS integration bridge security group port (15) forwards the packet to the security group bridge OVS port (16) via ``veth`` pair. #. Security group rules (17) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge instance port (18) forwards the packet to the instance 2 interface (19) via ``veth`` pair. .. image:: figures/deploy-ovs-selfservice-flowew1.png :alt: Self-service networks using Open vSwitch - network traffic flow - east/west scenario 1 .. note:: Return traffic follows similar steps in reverse. East-west scenario 2: Instances on different networks ----------------------------------------------------- Instances using a fixed IPv4/IPv6 address or floating IPv4 address communicate via router on the network node. The self-service networks must reside on the same router. * Instance 1 resides on compute node 1 and uses self-service network 1. * Instance 2 resides on compute node 1 and uses self-service network 2. * Instance 1 sends a packet to instance 2. .. note:: Both instances reside on the same compute node to illustrate how VXLAN enables multiple overlays to use the same layer-3 network. The following steps involve the compute node: #. The instance interface (1) forwards the packet to the security group bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge OVS port (4) forwards the packet to the OVS integration bridge security group port (5) via ``veth`` pair. #. The OVS integration bridge adds an internal VLAN tag to the packet. #. The OVS integration bridge exchanges the internal VLAN tag for an internal tunnel ID. #. The OVS integration bridge ``patch-tun`` patch port (6) forwards the packet to the OVS tunnel bridge ``patch-int`` patch port (7). #. The OVS tunnel bridge (8) wraps the packet using VNI 101. #. The underlying physical interface (9) for overlay networks forwards the packet to the network node via the overlay network (10). The following steps involve the network node: #. The underlying physical interface (11) for overlay networks forwards the packet to the OVS tunnel bridge (12). #. The OVS tunnel bridge unwraps the packet and adds an internal tunnel ID to it. #. The OVS tunnel bridge exchanges the internal tunnel ID for an internal VLAN tag. #. The OVS tunnel bridge ``patch-int`` patch port (13) forwards the packet to the OVS integration bridge ``patch-tun`` patch port (14). #. The OVS integration bridge port for self-service network 1 (15) removes the internal VLAN tag and forwards the packet to the self-service network 1 interface (16) in the router namespace. #. The router sends the packet to the next-hop IP address, typically the gateway IP address on self-service network 2, via the self-service network 2 interface (17). #. The router forwards the packet to the OVS integration bridge port for self-service network 2 (18). #. The OVS integration bridge adds the internal VLAN tag to the packet. #. The OVS integration bridge exchanges the internal VLAN tag for an internal tunnel ID. #. The OVS integration bridge ``patch-tun`` patch port (19) forwards the packet to the OVS tunnel bridge ``patch-int`` patch port (20). #. The OVS tunnel bridge (21) wraps the packet using VNI 102. #. The underlying physical interface (22) for overlay networks forwards the packet to the compute node via the overlay network (23). The following steps involve the compute node: #. The underlying physical interface (24) for overlay networks forwards the packet to the OVS tunnel bridge (25). #. The OVS tunnel bridge unwraps the packet and adds an internal tunnel ID to it. #. The OVS tunnel bridge exchanges the internal tunnel ID for an internal VLAN tag. #. The OVS tunnel bridge ``patch-int`` patch port (26) forwards the packet to the OVS integration bridge ``patch-tun`` patch port (27). #. The OVS integration bridge removes the internal VLAN tag from the packet. #. The OVS integration bridge security group port (28) forwards the packet to the security group bridge OVS port (29) via ``veth`` pair. #. Security group rules (30) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge instance port (31) forwards the packet to the instance interface (32) via ``veth`` pair. .. note:: Return traffic follows similar steps in reverse. .. image:: figures/deploy-ovs-selfservice-flowew2.png :alt: Self-service networks using Open vSwitch - network traffic flow - east/west scenario 2 neutron-12.1.1/doc/source/admin/figures/0000775000175000017500000000000013553660156020123 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-vrrp-compconn1.png0000664000175000017500000105367013553660046026214 0ustar zuulzuul00000000000000‰PNG  IHDRKÌVV˜sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|EûÇgvïÒèJ öWQ_»Ø°w}í½BB³€H.x’»ÐTl” ¶÷¯è«¾ö®¯"6°`GTBi Inwçÿ{6¹°¹Ü%wÉ]rIžåvwê3ß™›™gÚ Á`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`õõº`L€ 06H`„×w¸®"¿gAL~½If>;Å“ÅhïÔÌŠ@`€p»þ,òŽÿsG, JD˜ —&ù|2ŸäË–ˆ $W2 ò0æ"àõ¾ïZ->ÙC¬]–ºLµd–ß³¢¹äi ñfy 34s㞦4h–¦„«¥îþuŽ÷¶u‰”?;Ï…RV‡"_ÞìDÆcêu„ßY)¥I)U"ãª/ì º²C»v®µwÝv[I]îƒn¥è¸n–wÔ¶ Û¼Sû”Ã¥H}¬Ðwû/Aó†Þ“‰OCÓ«¿He/ž,Ê ãB!Ô½šiL‚|wÄ"c$ù"…ï2‹¬‰p)ýõʼnO}þÂÙßìѹ\”íäJQ[ÈÉYÎÓìö©S;lÝnu3ÝÖö¢ÜÜÕÔ­ŸöuºÑ¦ÙÍÝ{­×{m™Ó<ô9Þ~u¡•³Ï®k/ºè"34.~gm‰+Km)·9­µŒðLÝÓ…ÅÆ‚#„)¶S‰†yò7I%Ÿl·sÆí3ÆŽÝ^Ës5¸áþûS+Ön¹MÖ5…êB,¶”Ô ËRYžüÏ5M»cΤÜ7ãˆâ._»åDåÎòû_¤ÎE¼ãHÆðÊJ­B©Ë7o±.…|O×%cy©5 ÞÕš¶éJ¸{2è¶Â SJä è ³áAs¾GG ÙË^CäkMe¢!é.çcsµÝ*=Ä2­·Ì€ü>ÿQŸïÍ[S,¡FJCÌ€Û±ëħý€Qk0£8°R nÝ ”\¬érzá¤ÜwCÃŽ·_¼ýÍRmáj)äûšKN›ãÍý.4^~g­€ÖÚÈéc‘d{òo0Uà+t …›_1{ð˜”Ú­ø›ˆY’ç (mWBÞ¶±dñH¯ÿ€Há´5ó²µ[æ qχÂÒÌJ)îRËÑ„œ… ˜ïÀó0(Må‰àòÀ7– ‰)>Î ä¬uÆA3*Ãr}ߌÊõõsšós)?Rþ¥«V'‹ÕO ®²W¿ïÄ»h|u”‰,?ä)‰—<>1Ô•þ¦¬NØw÷÷P'®AÛ±O}íFVa¡[Iu‘MÀ¥UlÐ;”“Íø½>L•m“|Ó÷˜UV§@{íWA$rñ‹ˆ7¡ œ]ù'ŸÀ^O`®4j ¶ìHñ²9h­xf©µæ,§«NÃ<g[ʼŽL4,¹™®Ü)^¯´œžnðÞß±<°å!4†õ&–Kì6mܸ­N7mí9;¯àË2¯Ag`æÒ›ã°<”ÁHïäݺ‹ñ¿ ‘j—w,¿;§2 šá——X§£#1H¸õÔ¸DÔÊ©‰îßʒդɉTöšTˆ:"‹U¾ºÊ:û×¢ËþiÑ%U¤ô7eÝ@KÖ PüàŒ1Mu9î_GU¼ád .uEôC‘7÷Ëî¤Z3×—7Ôi†¶H÷ø/ACõ„¥Äø¬\ßka÷T6Â/”£µE¾Ü‘ÎxÇÜsOzÉÆí·bùó$ ’ÝŸ•7eaQþøonø™ ´f<³Ôšs—Ó–­ë–œF–š&F£±)U”Èîï[`wƒçÑ uß´-0žÌë»æÏŸ¯Sõ¹ µ'? ñG (-A /ïJX‡Ùá*9?œ¢Dv³¼–…ã™yj„)ÕÙ5Þ£x!n±2§ü"èëÄë}4­1ilŒßHÐh>’]¼Ì›"*kô{FfË¢µ­;®DpÝzÍ't†÷à ä®5M[ð[êJm¬uC5¡ªY"e‰K½^±Ÿ¥,[™JʳJÕá„<`†Iú=ÿ‡ÙŸG00„ÉqRˆ“ˆ¯ñKËС@å£-|Ñ^®ngDŒˆ-˜@+$s‡®2à$µ1«Í…×AùÙË~W}º=\oò¥Û+”ñ/l̃}2:÷ÉÐþ´Y_A©ÊÆæâ«Ð†5ÞJ±Ô••ëûqÌ/ÌϹ‹ªpñPCº*à¿KËÎþ©ýÉ –¾|ÞÔ£%ê§*¾oß0„!Í¡Yy¾C!C'ø+ÆÒÁ×4wj^4‡,Àý«ˆg .]—ÏΟ°84.záõ2 k>F˜ÿ(òyNAÃÕþð{½c6ý&ŠÏïä¦a\‡xNGÙ þ\æùëG‹\Ò51X^‡MôŸ&-kòò“¹~ϵA¹BïHKÒrŒúís}9/‘}´q8à -{N»pÏö2«âõgK*;J*|¼K¶çcá,—B{´—+ó¾H›ö‘Oeôû *&ã™ÊÝHǨI¡¾˜€úá×Ð8c•/´LС-ÂX7IX†=£fg ÌŸ‚ñhšœ`šâJüìöóëÀ|aÐÎy§ÙeÃ0^A~­+Ê÷®þKƺÔQ;%˜A‘j0òk_Ô³[ðüFÖüs'åÒ¡/õ^4K„úõG´{¯² Ž…‡÷C=ôÎloÏA¥¤K*Ô¾®wäËb°f)µO]îÂÙ5ίxñž uP¸°ÙŒ ´VGGÚNƒeˆûI°åîB8Ó›»4h¼'ŠÖÝ*]V ÐëYŒ‹Šâ‡°<ìraX90¿ªÈ›]ŠÙ–ùãúÒíP€…¸/è>x¯Øn]ŒçΜw†O,¸:Eïð"-ë †A3¦øi¹ ƒ"“ÂÍá71îŸëÔ)íçqñp? âLeª"ü†ö ýÝãhÈý¾ÜܵðwQÖDÿÊ´^·Šüy—;Ãã½gÉ6£d ˜_€ßÁ N¦;ÜÉ+ìVsÍÛaVû)Yê†jÉ”uêœE{÷5EãÇošgåù‡*Ëš y§ùÑ0×\ÚS8¿'u>~K£‚õ{0LÔ»•\¥|"hÍþ$àPoÚׂhüÝ4Æoe–=ЈA³¯‚aò ´<³Ôr™ÓB@Ù &µ?B,"¾¢£UéVUú­åPªŠÙÏôæ,E7ѳ¥dŽsí:uÜÑðÂN*—K÷8RZæ„I¢±än\XØ[ñ*ÙUèØô_ð÷º1ÌÀñhx2`÷þlï„ß‚æÁ{o}übØUà} D4Þu¡×˜ š»tawꥻÍ꺣x”ìMaÖêhbéY¥™’¶›`8s<žâLמÇ`&m(düi;˜F]W+WÐqÃ4ctKw+ÃõXJÊÙ ÚYBCà {!ÞÃr’wN'{ÙRÐÁŸë£˜àT&ƒÖ±Þ÷IU~fÆâWºd¬!_T¬c‰3’[ÀëŒøzÔõ¥³Vy‰žØï†NÛö^®n+BÝ¡ŒV–M%»„Úß›’Oo-óýªx{ã¯Ü7#±ôR4Ò3µ†âA{gP†ÇÔäï³ó=ÿ ú©ë.ŽºÜ7ØNªª´˜Õiq†…ßÄûEÞ[ÃÖQØ;ô$¹5«–;ý5Å3 íx”u]h|¦A³J¸¤œgßëø/Yê†jÁ<܇dm…UI»üà·Ãï]Ùùd™¢Æìœ2Ê.ÆoË…x_qö=ý@öàH®oS…PËaq”°—\íõã#Íp5ʯ’;¡¿Ÿa¸ ÒdÜ}{ÉÏ˱œð>RîÐf\i`É)??3ÖD€~¬|16C€fÐð¬D‚û*CP'¥8šÄãÜ¡LÌ PàÏðîSÚÓÈ2»_á'SiZÜ+;¤J ´ÃÁH%ä¡cfk\« t ‰Ï㪔í[¶“Ûª=•ÎRÒ,JC­Ë2Rþ¢œZÚ”Z–a z¹{¿€}´îýì¬)S:GTo>½ÝæMåç¢QÞÖ±Sj­‘Ϫ½=£³ýȈ<ßÌœ G¤çÓѲØ}:Ž]?%8šNaâ@,G޼³`?ĵ„:éXbx.”­' Úã³w—,묔9½¿saÿ6‰ ÚÇÓ ߤ{c.{Iá’Ÿû# #Óåù=–cÍÓܲ2ÏB²F>Õ{!¥7ë¿­:ƒqaõßìüœœfu=+©]?7?·Æ P¨{°žxW‡š‡{§ò‰™ÁPÎÎ(6WûаÏ'èÓLHØ+>ÙÞ‚},˪¡ÐP )iÚ3srPvw\ômìÁì¦è‡BÐ2vÆáiäÌå—x‡Òw“ñiRði‰ž}ÑÞûA‰y”Ö*ãê[,qT{Šñò~äÄÉG™ÊÜ×N‡R™ ­D!Ok¤Å|ĺɪšéFŠú;Ü7Ù#-½E½F{¯Ž u–oܹ=3¼bÝÅ4ÐãN¯Ü³S—PÉP78åC~„ý­“Ÿ¿ì&@Óêý½Ãlçj÷,–,âq¦³~E z#üyô]¬é·|ÝæmøØíÙh¨GS¸èÛû•„®Áo㮾Yš‰~ŽŒ—Ť0ÇÚàÝwßp_,¸}öÙgµªþ5lMa½ ƒk6ñ‹¢Y K!¡èBqï’âJùoE 0pzKƒPê?íé:âHbÅÂÓ¼qyBà ”XGÃl™Ûß736Ï.+5©<¦ ÏþÆüãJ”ˆ è„—S‡•´%r¼RÜîÇË:Íïr”ŸÜŠ‘=Ë¡ðz,è–î ‰Ãé?Úça}çcßdȼ;Ò€è8ˆD¬B:¶"h!±Â¢Ù0–GÖPNR„¶¡óÁp“é4oÊg0.ëûL¸ñÞFqk+þ:#<]ñÛ}>Tù,[óÖ 5äÒDØ™¼nbx¡%‹¨_q(Œ:G”˜4ðð~cQCؘéÚ­zÏi˜`Å©§{Íiy£ Q{À¡%÷Ö÷á~¥X©‹ÊÁœÍóWºžúG¤Ù¯ l|g­+K­=‡9}áü Ã!8Ηf9þÎA³vì„U–V¹vƒõê0þÈSW{DPYë‚öèL¬·;}XˆÑÅ ÚEs¿ã¡¼Þh\ÖïFw‹G¡ä GGîJ¸~˜|à˜ìÊùªezõ‡"įçsœu NØ¢æ líP–Щ¢Ù"/Nâ¦Ù¢û0‹DöB—šÍŸ–Ùa¤ú pº`äÄ)ÿÄÒöm,¡åSys~"·¹ w·¿Dø¥êk8^xaLÊU¸ð)Œ·¿ñÛJ§=–S…‘tºIô3ÍTeçúÐSóÀVy b–' (:Äψt9Æ›[óð§L±ðA$o"ÔÍNÿô¬¥‹ßéNËdW~n"ŽAf½ 5×Î_VÎx–m#-ܯ½ãVa† 'ö‰Ó0Óy,Ì?¨ì\bïŒïÎô{þºmhAÿÑÞñ[8K—žÆÄÒvhC×w·gçâ@Š (QÓ!ï­8h¢FZ‚öÐ#–UKX;‘;„[]§ý5Õ]ÏП0·™S0èt%fnÇÓ NÞ¶gL°ëÑhåhîºÁ)§f¯ vš4þ³²Oâèç ½9¡=‚ò€ºYŽoÕ…9}3b„³½žOQG>2sUE©5³":±ˆÅ/ÊT ößÒ_L€ T`e‰‹BÛ# µW…2‡Òêt¾¦×5sBp†å+,›øåúWûaì%}á•%!vµý¸\˃~¥RKí’Õ#‡A»¦¾“’ƒŽæÔAíš©» ³´›¥øßF ŸÞB¦¸:-, ÐiÃ5G¼OÚÏp²ÞfŒ°…Î-Á; éßêîÞž”(ûÒ•|cåàЈ“ (uA"½ÁFÏ*QàtjÒ¸q÷¢ÍÕÑ|‡ªRªøü_5Ó³RŸØëŸÍ¤ãµ œÅ=es&öÖ‹4ùs¼—Ü``€ÊSÄ2µVì‹<:ZÚï½]ƒ/ Ý“±½$ÐÛNMÈÌRU ©ƒ~öÂ]‚û¨ê¸WîË«r#G0˜zïXšŠÎ¬raÙÔÍEù¹…zÀ ‚–H3Kp_=ê{ûVšÉå¡vMõ>{„¿Ñy¿Ó«ð ¨P—~¼esÙÙ¨7ÖÔ5*_s× ¡òÄû½—«×+«Å4@€=F¦áùtªûu-ò¼H2Ètm¼Øn‡ºz(fò¢º;’ÛPóÆø ‹ß™@[#€¾˜@Û"PäËyWtØT74îµFû4h >-O²Í4á îÃqº±Ÿ ëìZf0ÀSCÃÖˆu=Åá+«Ý¸ôÏ`† u>}¦Ú¼™Ðx££©d¹a\¸}»…åOJÇë¼XÅ©0KmÅ£“ß;ýÒ¨3âx«²î(8JÓ>è¿ê<¸!ÍÝÉ•(H'Àÿq¶MD¿_ §U‘M=m¿µÿ³—\ZFÅÕµ­Ú®Épïäþ˜yfâèð…~Ï34²oE)ÂV@Ù3¸('KC%òo&-× {¥öèøÊÎFLœïõÎOAy»åisúÎ5_iLa#Žlh§Å¥ãƒ¦!WÕ©‡‡×|•êX:¢¾¦aå›eˆf–Ôâpö5ÃïÏþ-a0Òo©2 % +ä%[7•Ÿ‰ºß ’O†Ë»H2%IÝI¼F›W}tø?¨óÒñI‡sàQ(—¿Íž4aa¬ÓÑñý&ÕÕ¦¡f9OX­/¬Æø­/l¶g­+K­=‡9}a àÓz· «Alq¾ÓòP¸çìNäŸëÐH7·z냋ÂCš¥ =‰‹” Œ.ß]éGÃ÷9v,e*ôæü€ŽÈÃèv®(5ç˜<¹Ö’êPÑ·š"ÅOóŒ ù”7Dè‹òç‘"‡¥Q…ÆažDÇž‡šÓ;}¤V©ÀlzÆ~—Wéî¼4­j–ÈTcÈ\ Ý^‚tc¯‹—âöì¾if;­Ý»Aû(î¿“l¤?+œ[MÓï Ë[˜ÑÛÕÜÂÎCR–C²=ÓvÇ…½ì£Î•ü7~Ó]W–ÞŽRH'W>3cìØíN‰ÃNÏ•åÑP´÷ªú¢®Z¹~ *]« Ã< 7m øB;Äö7pÌ> À(Ww{Ùlï2Ò4­2pD5}÷-R`t!êÆïÀú4(p´ÌŒ¦Lä>’ys× ‘䊛¹^yz!v§MFýæF5ôTÕlsÌQôrí>õØ2´›þá±Ð¿±ÄÃn™@k#àjm âô0hÐòÌúœhÒÆo¥®/ù«ô,)Y‚Q»%høÛão0÷Á=’—„+íz§²S#Œf£ ¾Ä‹pÙ3èäü€³ˆzaÓúyhûÃÿWÂÕÕV"œþR2´ RóP¸?ÓØfþ¥íŒ/CG‘އî'þ\?¤B*šY¹Øé/Ïô1Jl<~ aŸFê–A½nf3ž€Yq4–´}…¥v¿a´Å>É–7Pv îéè<½Ÿ©¾3TN(UKîÔIp³½C'÷ë¡nÀûEtN„9¾¯¤>‰ic±†ý5&Ž;Wòfäe?pĺ{­{ðC«sò'|óià=Î ˆ‘^Šÿ{(‡ýÁýÐ2cs7¼×RZaÖª¯úŸ¬R¿ü9U¨À’€…É\ek¡çúþFÇn Nrœ:wRn˜üŠ/šbð¢UrÁWåÀ@©õöõ¼€2UŒ9Øý”ª¸L|”Vœ†r’.f(Ę–1esÜ!ûµGBÝ56ŽÐð"¾ëøµ)è7qÊÝžæ°Äwz|gÀOÔ ô1` Ú„¿Ö'†aÅÿá8³²r꓃ð1Ú‹ðÛÀx„vK¡cTøPf:ëŽñ¿ãøèÏß¡ø@ö"”ƒGa‰.¬oC÷³@–",)¼•Æùxþ‚‚bµ¹ë†XåÕ}¦–ó¿bË·2¨ »]ú“±†tOûœ²'úÆâ8ò—ž':>î¸ó {ç½1~áð3hkxf©­å8§·š5úí\í÷Ó„œÃØ)Z£Ðp_ŽÉL}|¡K‘…@žé{':å®÷ßã<ÎF'³j:Nü‹ŽÑNèÐfºúI{fÈ­ó²O3Ú¥û!Øä? n±]‡Nòw ãqºBçÀì§àN‰{ÖEçÈMš¬ùm¥ê8¥x¢ïàæŸèÈ]@²VÊ+Î¥N-F¤³3Ý{œN±¤ÛBIúÍ J`¸½b©8‰­:.¡E¿žìÇJ-e°åÌ"ݺ#rœÎú8(¾£ñ»ø4’¤#ß@fú†ÙEù9Ÿ…ºml¡áEzŸ;Éó¬ {H!Îʺeív”µ­i®”äH­ZÂ.©R3´[„[bÉ–páçÛP7Ý…p.ÁûÏøhõ‰øØmƒ;ÜábtšÑ¬‡Kº¯‚¬ŸS>à÷î'ù±l§;zNwµ‚?P†pˆZÍýa¡n#½'CÝI¶x˜Ó œ`úo åó ú`ycÂ-œäy!½‰òй¬ÄšKXñK<ì– ´&¨wùbL€в7÷Ê¿÷èbk‘wüŸÑPÁˆñßèdwB'N§ù¡/¥+·îžyÇø_ƒfÑ„e/Å+,¥oéÔA­™6nÜÖhü5‡ÚKQºµ¢«)];á„ÀRÃÕ§ÖÕV›C6Z>f­/ÝÍÒM=­cú/¡Ë°‚2ÑÒ˲-ÛwOé+éˆß y[ºcv-ŠÅtv‹Ú¹3&„ã@¨´;êR¾7×ç9¡©øÐò¯2³lW¡»Šu GSÄAßøzÿ‡_ûi¦Lôé²´(;;+C*«¥WìÑ©½ü­©ëZv‹ïWõÁ7T×…«éÐ|«·IsµseÒÁ±¦¯©ÜG[74•<`-ƒ+K-#ŸXÊ$%P©,‰Î85“1•ÊR’ŠÊb1Z ,aæ@þ3Ííîÿ€wÜÊZ`@ EI`ÛßÇéˆyM²‡.œl–œ†çú.ÇTî“(O£|\šœR²TL€ 0†àex gÇ>™`-––WÑÎ:•ðïHŠ%®ÌØv@e"å—-6±,xBÐa ¦ù¸’®{ ʘhf¬,5spôL€ 0æ @3¡XZð!–áuÅ Ó¤ÐSéøílOÁ&>ž‰ez©É¹Í!'Ç™œPfŒcÖ®ØöÊÜü _$§¤,`L q\óξ™`L ÅÈ×ËíêP˜òòšÔ/Äɳ±,ï+R§¯ðí›B˜Ÿ/vé޷Зó\̳‡VJÀ>¥r ”èç¡Xß&ÝÝ´Oöl¥©åd1&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`IA@O )X&À˜@”ÆÜsOú CŽê7ä¬Ó+¾ýv Jo쬖ëûìc‡¨Å¾OߌŠêÂV妔”q|×/>zosTžZ™£›½3:pô±-úðݱ$­¡þb‰ƒÝ2&Àš“”¶9ésÜL€ DM {¢ïœ,OþÂm•”*%–mÞT¾ïK²r}öNÍŒ: v¯WµÌöAª”ÐzF“È1Þ{vÊòøÌÊóm,-µÖTµåi®7†åù¯‹&Œ–è&\Þn”Œ6-룬)S:Å’¦HþÂÅK¸ì– 0&,Zfc˜,ôX&Àš„@Vžÿ2e©ç•†&Åuº[¦Ií"%åÛ SWqûš&¤D’íÉ¿¾ØðµæäÞ>uj‡£ä ¡ÔuRˆ4©ŸárË#P¦ò…T[5L6µÖô¾?nðNíãLŸ&ÝÏ¢CpkÑøñ1ͬ…ó×Ê“?3&к ¸Zwò8uL€ ´t˜Q:KYÖcèÄÞÛ[ϽÍë•VUš>ÇýùÊgOKOfRÉ¥ôl´>©„Š£0^ï£i«¶¿‚ wqI÷A³|ã~tÿ)žïs¼·ªÇáy“4-£†¢D œí÷3nôÓÎ_k/?1bÇL€ ´x¬,µø,ä0ÖMÀ2…G ¹²·k E)b¢³¼…Ê\Ÿ•êdÌôJ.•RÎïåÊ™îô%|Ÿb$}¼’Z_ËR£0»°'Ü-5ywá$ÏË4›%”•…ˆÂßj!嬢|ϽΈ) ]ê·Y–ÕGIu¶¾ì#¥Z·óàö~§[z–Wp¬´¬›&LQ€ï|¸Åa!þFoÚjLwºÍÎõ] ùÞ€;é½nß‚ý°b£øU§»hÂtKY0sZÖø5–:úèñ~IaJ¬g”€b)åëP2¯‚ÑkøÛ[š:P†¿HAE$°„ãGxã ¯®LñŸ‘ž©{Wû’²+â=Sl üy€àn2üuE௎öäïtK‡ ¨íÖ"( ‡Céò ©FºWŠO °"˜z.S|û ûíþp=.ELe*†4 Q§7«°Ðrý1d½\ pãå÷ch2»Ôð_ùòšCQ²MÜ.9‹:”V üX2غµâH¼÷‚Ró`¥—Êÿ½Þ!‰Žð¡#«Ó©~Õ¡(ÙVÐ¥Þ„ÛtŠTlØz”¢]4³ZŽK¥k˜i‚Ki vã±þ0kºßñV”@úÄßõÙži»ï°‰ý©2äZ(-½C}+—þ¸Ólι‹‘ÿ0MÒ?hgÂìýB_µ¢d[¥èÚA7uÞ¥Ø yœ˜2…ˆ£ICP¾hÜB…¾y÷µCQ²½# ”Ï‘ÒLÊXg—34ì÷:%Ûm¬ÿùò~E P>/¯áWŠËÀþ³ª½J5¬ê|Ñ\¡üïTl-=+èn„×?fÿÐuQKñ ºá;`L ¹ ðÌRsçÇϘ@DRY«È3/™9,0R?¼Ð™Ÿ  Ì@¨Ý‚/U÷âwôÝÄ)”¯Óû-xïè4³Ÿ¥ü3Ô,£]Ê/›7•QXýl;¥Ò]w§. u+5m©ÂÆ,Ó0H¶ß‚öè®>Wߥö7öQ‰ra¤ö?  ;¼Oþ¥ÉôL–¨‘‚‰­EUñW“Y½aV9 {“.ýe˜XÎXþf¹^DzºûÑ©~+¬c‡aVÞ”ý¤0®‚è´/¬¬ÚCÊ!%úõ5¯ŽíÜ5d¤N–Ç·üí4W¹WYkKJÇ´•Ô 0Ì×U˜ULT™Q¦Á–,·w£täñÎä@Q&¥Y37–¿ Ëê`Y)ÊYÊR§»?K %Æ*îóõžãñßä÷÷(-U'`‰äèXÃ,Êÿ-f,?ÃÒÒká×Î;ð°OþÕSÛ#tÉh¬Á³{&À˜@ÂÔj¨Ì˜ˆ•€Ëe+؆ªäDI©@XK%h3»=´GG|{ð9än„¼G|E'¾V=¨¨€2€î|;©FmÙôJy±÷¦†lð^1Ò* ̶´£Gè÷c³ÿ4ç:Î×C™F½a†¸¯ñZäÍù WìYÏEâ:@{KÁÞ1yr—/Ø»u£²KЇßr¾åpœæ’8ìAnv8«~œ~ë­ÑȘ†5låÕžªî3¦ 4ê]Z‡úoPÚúÏŸ?_ #ì{ eŠüG™;ªhÜB±kÞ‹ùk?kr<6ª]k¤I{ ”ª 4E¸Á¡ñWªÞa>ò©Ü*“—PhÛËÔÅ(Ó†–¡?ÓÐáw.¸Ÿ2Ò;­'=¼"\ì t.ËkH¸ì‡ 0&H<³”Hº6`"pü>W¼³dé tŒ‡bÏÌ]´¬Î¥ZŒ>XålN¨C©ú¢ãŸwGØ8½ —ãÕ~Ü^ªŽ¡hQÐcý‹ÑÇV©ì‹×¿É¬ú2l38Ö~­6‹òASÚ2ÌI áÒ>™ãÍý2JorF3=àeúÃLÁÉP˜^³¶™×âýžÐ€ép„U¥Óàga‘ÏsAÐ>Ë{WWœ¯€Ÿ¢¿Ü°‹¸öõ:âλºaÆ PÝáât½M%®~ûÛ_®F„†Sã½ÊTøñ‚}=Ë 0mŸãËjç|ÇÌáï”9墔ÊYpÓILÏxoÜ‚<~S¤¤,Ý …÷R}iö„ 5Ëp”¡ê®ž þžaåŸ8y~×ý”¦?¥wvƘhµFD›E Ž” 0&†mÀפœHËÄÊõ“*G£Ã8 Ñ1Ö8ZÙîŒÍp§e`èÔïE_ï9Œãòˆ%{'‘Rà 2GËà4·þ™§uk·ýwŒöW8ÝÙÏʺÊÄŠ^b·ZKôj¹ 5p¹ñ­)Y¢ Äï«‘ZÚUçEKð ;}ŽÎü±á®,í…¼KEg›”«—Yv ò£áƒuÈgv2œ±#PZŽXq¢ó=Ò³µK÷yà¶ Kï¬wÿU3”©P¹‘tŠâI£r}ýBíœï2E,¬Y¹iXµËYµÃʽZ¦YoþVy¡ƒÆ©‹Ç@mÃ!$Ú¼ê ">öy§K×ÖVgYB E÷ß³¼lˆˆÀ)d×" C®õ)R=kŠ”K†â¨òó±dîʆ,E¢“ö²=þ|lNšŒp+4·x J@©2¬C”ÐRŸI{EbM3½ÿb:l N<Ój¯}Ô®,Eï$º”®2V݃%‘Ïd¤©Ÿ*Z;Ë´N„ütŠß¸pqtsç®XeøÖAYºò-Ð¥{½¡8ê\\…üø‹fAr¹ÜúT#`^½esùó#½þqšp­ æ‘JY“n¤¥•ÕQÑ eöDÿpÈþœå_ËõMºüÂ¥´u8ÞzL âýEú&P3•©jYí‡ön¿Ü¸æz'ŽÇT"Žõ.ßeÿ0¨ˆ:”V/¹£}E˜ z³«c¡Ül‘nù¢0e'”CQÆ> ôœ´ßn Þ^òóF|èù(_·hn·é<ÌÄŽÏñ_¦Ëó¾“U eíA”é5' ÚíÍB‡}¸Çpåg†wìFÛ­ÔçJe,D9írƒÃMøbL€ $7žYJîüaé˜@›'@ßéÁÑÓçb¯Ë0Œp÷B·HXÆçèì½nb20é®Ûn+IÏgâýwtîþ”¯6-ëEÌê|án§E§ÏÝÆë.…V€ï4-7-ó£ò@E1€{Ñ| …­1Û37?ßlR#°gã’r%5TÅOXÂw!:éæç>ÙPyHQDü×#½ç™õ™°¾E¸`‰ÓARi˜ÍŠýré)O?0_m l* ”¢‚µö…‚ú~i©µÆ¿B9¹iº+Ó•sw¸X(ï°|ìB0ùöïQšq¿GÁ_ˆàßåØý÷XM¯!0Š¿þ}Šo˜Ò·§8|e}JÙ¨‚‚q8ƒÕн‘d¥#ÅKŒ’.;¤¬›6nÉÖ¨‹>@Z²%°ÓL¿‡”û¢¥o%%F—Ð*ŽÞg×ÕѲ¥›–§–Éx§™–\¶šU=s4Æû ÝT…î~Ð;nU$¯MQ¦"Å4§C)ÞYòs‘®•ž¸ûîëêâ?Ú;5SŠôm´÷(èßyîÜ_78ÏwÚ7ö9\ù¡0ñ»¡™¥­ØÇvJcã`ÿL€ 0D`e)Ñ„9|&ÀZ-R–°”èžÂ|Ï­6‘œ0&Gý¾Ci”fñèçâ4ŘH^†—¬(`L€ 0&à$0Ò;y7+€(Kñ}/ׄçvü̘HV|ÀC²æ ËŘ`L Àa—bóÚƒ€±àø7¾ÜÞ Õ ÒÆI`L õ`e©õç1§ 0PB¿Ü¥ô_<ËZ­½þ†Øn]‚C9~žÙ*Ɖ`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L %M!ôˆ¼É™–y âꧤÊD¤š"^Žƒ 0&Àš—€b«Tr¤øC×ô7gçOXܼ5MìÜî5 gŽ… 0ä#ÐÚêý„)K^ï£iÅâÑBª›„}¤–Pr-”¥Uˆt[òe-KĘ`ñ&€F³=”¥L´=”šb%Ú‚ûz»{?èõ^[ïøš3Ç͘@²hmõ~B”¥,OþÉȰ¹hûJ)ßÀßSZ†öêì þN–Œd9˜`L éŒ˜<¹‹Uj¡”º§bíOÄ>¬È—÷VÓI‘¸˜¸ÝK[™ 0–I µÔûqW–²ò|7+KÜ%¥ú£‰7ú=¶Ì,f©™`L ²s}ÇXB<€°ÿ!5qkQ¾çÞDÄÓTar»×T¤9&ÀZ*–\ïëñ„^Ù`¨RÈÿw÷³ŠòÇ.‹gø`L€ ´|‹?zïƒO¸øqi•î‰Y¦±wÂæÅ¾÷iKL·{-1×Xf&Àšš@K®÷ã6³DK”’¯!Àç }¹cé–,òŘ`L <(J2ÛãÅyh2NoiKò¸Ý Ÿ¯lʘˆD %ÖûZ¤ÄÄbN›Zá~.-½în×°¢ =v˘h›ì¶mRÿ=þæVµ%-·{-"›XH&À’Œ@K¬÷ã¢,Ñ©wöaØ£TäÍ.M²|aq˜`L I P›†èjCìT“TÎP±¸Ý %ÂïL€ 0è´´z?.ÊMñ >Ì!ºB®˜`L`j;¨ ±?5±Ã8¹Ÿ¸ÝKîüaé˜Hj-©Þo´²DÞ«üŽ’|*©s……cL€ 0¤%eé)jKì6%i¥¬ŒÛ½$Ï  0A ¥ÔûV–LË<…>8KßQj9ÃB2&À˜@Ò 6„ÚjS’N¸¸Ý ¯L€ 0h)õ~£•%°é‡¯±¯åÎ6 ”°&À˜° ØmÚ¼ôkH¸Ýk™Ä"2&ÜZJ½ïj,F%U&ÂXÕØpØ?`L€ ´mhO¨-¡6%©/n÷’:{X8&ÀZ–Pï7ZYÂw•: O¶µ |aQ™`L  =¡¶„Ú”¤¾¸ÝKêìaá˜hAZB½ex-(KXT&À˜`L€ 0&À˜@tXYŠŽ»J1÷Ü“žåÉxëôéígÃr}Ÿe{ò¯s°`L€ $À0Oþ‚¬<ÿÐF÷ ¹½‰;R°½ ¯q²& ²'úÎQ–·í¯’#ÇæMå&”¦pXÈ)nwÞƒÞq­jœ×«4¯WZñÊúáyƒq"×!ÅÇEùž£qü¦ ÌlLoZä÷jרw©PBëÙ¨0Ø3`L 4EÝïú>^˜Ðì/-sÝåñ½­”:Q×µSçLÊ}3Tž›üþ¥¥ÖM—gNò¼jߨwûd,í@IDATno…=LJÏ,Ň#‡Œl]Eéyôî MŠët·•ç+ÉxZ (…g#Aë’()æ+%'Ìõ½4ËïY‘88P&À˜@k# º3¡õ}³æü/N-;mëæ2?ĸ¹YEáÈ™@`e©‰·õè,Sx¤+{»ö˜àP”"bÉòf(s}>°,¤êezK±äl~/WÎt§ÿ¬\ßÕ¨ÈO—®ôQÊ(»_ 5DY‚†ëéêv£[3T l:–!w¥’Ÿ¸Üúͳ¼–#Ç2ÀOu©ßfYV¸¹ JÈ>XݶLH9KÝîºéÙ>øûcLËú=ÏÍé><Ïi©1sýžý³üþ^¢TÍUJœŽx7!üCm·JæciܳôLéõS!çÉpÓËê¾Ñ¥æ™Ÿó‘í¶žÿÜ.Wn `1„˜ §gÖãܶ–Wp¬´¬<Ä9Lˆw‘K¤ŒŸå÷£Ó•ü÷Áì(üY`ò±Û•z“a”W8ÝŸ±þD0ÏÅ‚Àöz°{9ÓÕ'Çë½¶,è†ïL€ 0d Рº³Ž:®®úuá`Ô‰ýŠ|yÿr¦íÖ;¨óS ósq.¥F]ŠvE[;×—‹¶«²ˆ¶D~r¦Ëse±é¿uñ¥B¨n©Ý;uàÆËqŸ‡åùò…%NÔÝú¥s¼–ÍCïšTËÑøÑÜ‘•WðEù9Ÿ…º }¶ý&±´7m7Cåäw&P^†W!¶¯w~ :æû#À¹^ïEa;ÜÎÈȽ ¬ÿù•¤°hš¼Jj⬾£8àÎéö]ö™ÂØþ‡?¥Ðo€’ò6‹Ë”±î(Jï¢1*—R» ÷‡PáaÆs´¶ÜÎ?LeÞmI• ³G….®Eçÿk,¼/+ÏG£iö¥ ~Ô K;Í‚wK‰ndGïn)+ óã‹–~¥†>úCcö%Ùgº‘¾ñx¹@ºÐ Þ"”È0,ëý¬‰þ!䦾KZ©ÔäÍJ¨3hyc}î‡çú.—Ê|éÿ‰¸ ¤±³!*eçØûÇ(Œ›½3:«íÖ"4ä‡#|¯Úh¸]m*>‰$Õ¼²s}C‘}iבæ›áö-¸Vl¿ZÓ%¿1&ÀšŸ@¬ug}u\]õ=öxþŸ=Æ{ÏNÁ”ðúi°jÚ©ÁÙ'W¤ÁòïýQ—ž¯)µœÜÆÚÂï‘ÅFÁ½`<õ÷ƒâöHŠR¶Ç?N(u»®Ko]ŠÉa ™!ûv› ¥ïGaY‘\déŠEîXÚ›x´›‘dfs&‰Ï,E"Ãæq'°Z,„N}ª&õ¥Ñ¾Êøeˆ}1뱯cÖã…áž‚/ Ô¼œí)8½Ð—óZ0,4:˜µšY‚*³ç0B—ä*tÞgúrGÍÑðmµ„Ê_mL³_‚aà¾g;Wû~÷zÇlª2ûÏ0Ï@K1f¸wú}s¼·­s¸­óqfNÎ_p03J“”¿ÍÉÏïô V®åâ—[;p–7÷ë*»‡ßr4FSñ^Ý€:ý9Ÿ+ŒŠtŒVþé¼JݽYÞ»Þ*òÞºÁé&øLkÍ·l*ŸYž™ëóT+V^ïûO¬2,ÀŒÚ½PD§Î’@ F4e)Sö.Ì¿=ÈçEŒBnÅ)XÁ`Å ÷ߟZ¾nËtp~e®/ïÜ*‹yX»¿á=šOÕù 0&ÐLb©;£©ãPßS[¶¾ÏÊ›ò–Ÿk%FÙ1pó"%Ù(Ô•rªÛT¨"gÃÈž©)·¶MƒNJsÓ^s;(T¬¬”éÞsp]ƒ’t*že)?F·. whÅí¼fzQvv`„×7Ô4ÔÇÅ¥ãa?ÉéÆù‹Ü±´7ñh7rò3ˆ†ú|1&"`ʽ(&ÌK¬Ž.Fuf1Þu(J¶·9¾ÌÂÈ_•°. {{y[ÐËíÞ£g¥Ë3Q–¶¹å»ÝVÝ_s(J¶‘Û%gAÉK·ådž¸mÔkå ùµCQ²ÃƒÂ‡q5ÐõE€öoXºÜ#Ðè¦cfmF$?[·V‰tô‚RC£Õ—×;Äç™ tèÈ;§ìJëLäÓû…¾jEÉvŸ¢kOT{¬z¨Ø°õ((E»h:fã—J×(è´;Œù‘ 0&Ðìb©;[Çåÿõé)Ìã‚ G{.f–ÞGù>ãHYª¼,k¹%?•±·ƒÓIQÂÂUuí¬:ˆ#ÌÝnkf{=Ÿb¦l&dÏÍöìÆ]•QôrÇÒÞģ݌,3Û0ðxf)<6M©,û8pÌ`dF‚[Ìv¨ÝB픫S±Ó ø-¦)„n¹jEŽø¶˜¨BÔ\J åŸNÿôœÑ.å—Í›ÊÈm¿P»Æ¼#öPŒÒ1+ôƒ3(´TC37–"·Ìié¹È;þOÌää`àý¸?UèË{ƒfˆ°Äb‡¥hMèîÔZaJM[ª°¡Ì4 búþVùrï¼R:¦­¨ø«Äi$°ÿiÀû4¤erÐËøH1¶ B\¹Ãç;`L ±¢©;ãQÇaFÿ]´YÇ‘¼YÞ)}U p .Å­XöíByóhOþ®úò~‡¢3÷ö¬¹mH;èrw ®T  j^JõÇ*Ì ÉÅØ+UTÓ2º7·«sŽalÌtíùjÏ­RžE{w0cs·kŠP—c/l;8óŽ‘5G´aøìz¼¾„ÆêP,á°Šúq–wÔ6¥iÃ!ç« ß(lέë9z¹£noâÝn†•› ™@žY ¯‰#pü>W¼³dé t‡b“æ]´þ¹ÎؤZŒÆ%ü¬„T}¡,-­Ó,13Ó+ÔÛöRu ™A‹úƒî&æ©èŽJ;¨XÐkå%e&Ò|«óŽC–!Œís|¹óêt¥%8*oÊÐ ø2`l*€ò‚é0¨xU—%ôÅX)/¬RÙFÍí»a›!‘Ú¯Uæ”ÖÞ5ÜàeÄwuC#«#õÕVšÒ–aNJ—öÉoî—ÕüÀ˜hê«;ãQÇiéâs»’ÛŒ²£P/Ÿ…äZMx0#ÿº°äÉ%jûr(@šÛ²CYŠs;ˆvá^ìs›åñoÆRö{²ó¦,,ÌÿM¬Ù4wRîëP¶þÖ®À,W;ä ›ÜQ·7ñn7ƒâò ÔE Ö(z]ŽÙŽ 4†ÀE]djRN¤it±rý$,7ÛÑã°’ŸÁÁÉ8´ «Ó›e÷ƒ>²?NÆ{Ïiglb=)ô”Èp-'Óܺ½—FÕ°|n3ž猳òd=ëd§=cùÅj„±K¨9ÌèTº“FåúÂ+„¡¢xŸ™?þ{0žqGÑ(¦ÓKZ·vK ã`fȸÂin?+ë ,Û[ÑK춬ò]RZOý¡eVœX˯Ëodá˜ö€Â|1&ÀZºêNC©¾Ÿãñ£þýQæñP”ŽÆ~¡ê%溒/Aé8KÕi¿Ò·³¼Ž³Ç¹ÄÝFÊLWæXp´,c¾½¡Y–š¡ßŒ0Ê…aa¯,æÎœW,rÃ-¼FÕÞ$¢ÝtŠÍÏL žY G…ÍF §žóx±å?—ÖLgçùÆw‰ž2…†¾Õ'Àá-§ÖM%:uN½§·¥Œíoà{7»Ü)Ë,+°¿e³ ¬üÔKüPMÃ)?ó‡çM¾Ó¥kkqbÒY85o(*è;¿É„xßA#q:Ž¿YÓõ•);þqp×%T&4~ÿÅÞ)8ÅïL«½öQ»²}†wìFÑÞí—ÛWâCGïâÄ8¬!w}£Dù®`p1£Þа¢yï¥ïá/6~¾jÚ)hÀªgzèøØayþkÁþqð\Ÿ"Õ³¦HI±d€NE:Kæ® n Æ7¨¦óê-›ËŸéõÓ„k]À0TÊšö5–:Ò 8‚6›“&#Ü Í-žB¼¥Ê°Á éaBê3wlVŽ&ì† 0&Ðô"Õ±Ôqë{;9Ú;–°®D;aetÎx+˜Â4w»WKméÐ6®ÂzæyAsº'ª¤ïßey .VköÍFTW:ãæùœœõÃr}c°šâñJ÷HYՋܱ´7‰j7ƒró „#À3KᨰYÂÐrí}.†¦^è¤ ËøǪ¾½6·@1±! îºí¶’ô y&Ç ÌòÕ¦e½ˆÑ¹/Üí´£‚Kâ)¬Z}|Ï´ÌÊÅh¼îEÇÿ1|\µÆ¬ –TÜ»Pæ«<4E;ØPÙêò‹Ü±´7ÍÑnÖ•N¶k6QŸR"ƒ m&öÅ’ƒÊñÖì<ßÉ! KÁÚ–Òž´9Û^ â3&ÐÒ´„ú”—áµ´RÅò2&À˜`L€ 0&Ð$XYjÌ `L€ 0&À˜`-€«¥ Ìò2DPB¿Ü¥ô_>‡Ë˜`L€ 0&в°²Ô²ò‹¥M ¹¾|Õœ/&À˜`L€ 0&PI€—áqI`L€ 0&À˜`L€ „!ÀÊR(lĘ`L€ 0&À˜`e‰Ë`L€ 0&À˜`L V–Â@a#&À˜`L€ 0&À˜+K\˜`L€ 0&À˜`a°² 1&À˜`L€ 0&ÀXYâ2À˜`L€ 0&À˜C€¿³ 1&À˜`L $üÒ_ðr>ÍøÎš›€r@ÏÎw‡UÛ~l±ÊÒˆ¼É™–y ²¯Ÿ’*µP‡¶–•(Ñ[¥’«„RêºëÙù·vþ›Æ©Bʾm5ÿÛJ^s:›†@u}"ĺ¦¿Ù–ꓦ!ÜøX¸Ý«d˜¤e5¨ÑŠíª›'äNM= êR?¡É^ší_8&JŠ­ÂR«•¥þ´LóÍGïʧ¾¤…?Rœè¯Í_-JYòzM+Ö49Ö°Œ^RJÕ!#ÍêÜ1C¦¹ÝmnIaY `mÚRª¶––iàáËÎó¯6 óÞŽeí˜1clJwk*är̘{Ò¶¤m»ÁåÒo¶ó_kÛùßæk/W!õ‰ŸêÓ´fôIéó€×{-Õ'|5n÷jCO²²JJõ?ôãŽ;7£ßÁûŽJKOmYªg[ï£ÔÎ96IFÎß“æÒïÌÎó­ T÷møñÏ™/½ôH)d*NÉ(~“ÈÔb”¥,OþÉkÌU ÿß{Ÿ½Õ!ÿ öÛ­·ÌHKÕ›„TrFb+ˆ¥eåâÛeÅâ‹ïëùݲ•S··ß~Ó5·y†Í›î{b·ôBn7DHÏ)ÛÓ¶ÏÕ,™¹Ï€LÎÿä,,UË&®>™†z÷æëÇ{¯xŠ—ê“Ö4“ô¹Åí^Ä,J–²j+IÒ}éè[NîØ¹Ë,K©^ÜG‰˜ol‘œBO=ЗœÜkЀ¯8.û‰S߀Ø&þ¨?Ù&¯F+sü5Dnñ‡ïÏ£{".4c”’õîÞ¹ý°óŽÓNÙ¶½â²ƒŽ9nÛ—½ÿ"MX]ÒíI< 5…œÜîÕŸSÍXViúOnü¥^9vüÍí;ÎéÝ£ ·Qõg»HR¡¿§?Vÿ•±½ÂºtЃK¿þøÃ/%vSÔ§•ÝÖ&HýËÊCÜsðÞýµÛ¯>Sß½oÏF×òƒ&>ã®9S?xï]¥®»§]s»g,RE:UìTÁ·„«º!"ù)”Îÿ–u,ck"°£>é¶BÞ=4çÎ1H_KªKZbvp»×€\k²Jíõ¨]M»ü¦ÛƤ¥·óqÕ€Lc/IK`ÇïiWév§N¹æ¶\ªû©ÌSÙo)}ɸñMfeIbéÇ)˜QšŽJH =ïX™ânôDXÜÀ%s@ĉx7ò,8 ò¶”B^Ý‘Ü$?ç2—6–­µpÖ'RjÓ®¾5çT¤¹M6˜M×Üî5r•ÕjEéÜk³OÇŒÒÜF5"ÓØkÒpþžÜ)iþKFqö%“VîD–´ÊRV–7Ýív?ܧGgqÕYGµ9-6™MÜ2»u;wž½ï¾‡uB˜A…)Á'* »!"yIî>=º(ÎÿD¡æp™@ôèwØ»{•––1÷Ä/¤“½’¶ýˆ>UÉå’Û½øäGË*•yZÿŸÚ·ï;uÏì3ƒÛ¨øä‡’¼‚}ÉN]vš5hБ!)ýÚTýŸ¬‰ÕÌä ¦ee^|òa:i·|ÅN€¸]zêᚥDÏAÇ; !¤â/™ yuCDò’Üœÿ±ç;û`‰ @õÉ%§¦ÓöÞûïqâHæº$&·{q"œ ²Jƒ¶ÔIÁ_úá§ŸžÍ{=¸ŠS¦q0IKÀÙ—ÜoÈÑÔ—¤ß@›ZŽŒÊ’]!¹ÜúÍûîÖGѺI¾N€øÇv: G(éøKÖB^£!"y9ÿžïì“ $‚@°>IMOð“µ.IDÒ&·{q&œ€²Z=˜QÛuÚy§ë¸Šs¦qpIK ø{jס=õ%“}à=î“QYÒ®¸qÜ!ôN _$Pűۉÿºø0•¬…¼º!ª’³ç#3ž½3 ß¥RªÇ¹WgŠàyv)>Œ¹Ý‹Ç¡Ä±¬:óÒŽ>õìC°Ç½+·Q5póK+'Pù{ÝO»ø*”ÿ¶5X–lÊ’]!¹SSN¡¹á;J­¼è5Mòˆ#ñÜ©{cZò ÉÉùß4eƒca±Ö'í;w: ~yv)V€µÝs»W›I\LâXVƒmíûMïÖ»ÏqÜFÅ%‹8D ø{êܵ뉛~ mf)^R*KÈ€þ2Ò,|p¶£ä•8vHOUšËÕRÒR¼d+ä5"’“äåüOÞ2Å’µ]v}‚úYjz¿$¬KZbÆë?n÷âœ{q,«ÔW îWJs§¤öæ6*ΙÅÁ%=꾤n÷%ƒeɦG$„c²%²²BÒd¯Î3’M¶„d@SÚ¹S;©»]ÝŸsf)YWæ{å(uÉIò6›¶OyE@ü´|uÒ'ûç?Öˆíåaåüá·bñá—?‡µcÃÄ úYê²bjS f‚Èr»— °lœÊ*å-9¥òžš,mTÀ0Õç ½È?–Ô6Ô{Òú[û×±fÃæf—¯¾¶öÝÏËV¬mv9c€úfšKï?ô[h3˰)¡ÉtÙ’&d‡4·;.å?Wÿ% yYä\–èÛsç§uÝÆ­‚*Ûà0šÓ#ñÔ¥FÇýÒt]²òš äŒWþ7'óæŽû…÷‹Å?.¾Qç×åûßV‰‡_üŸ¸÷ÖË}±»)®Ü™ÿmÚjGE£S}ð;ºø”Ã"þžLÓ<ý¶¸ú¬£Ä!ûìZKDR–~ømµ8æŸ{Ö²cƒÄ ß§&ìú$Ùê’Ä'>þ1Ä½Ý '"Õo~ò­8êÀ=ħYÉ¿Ï ø}ÞpɉâûÔ°‹öÅ0Mñãï«Å~»5Ì´ñÄê.e•ú"ôG3K¶Â¤kzFs¶QTÿ=ûÎP6AÙ¢kçâ_Ç$Ú»¿X ³; _7]v²Ø{×Ìj\¯}¼D¼ýÉwâ®1—ˆµ7‹çÞY$–b@J×¥è׳«¸þÜcD§ÕîéêíCöé/ÎrP óp/Í•ÿáâ}ùï„i™"ûüãÃ‰Ú ³`_’2Å•g ¶ßÃZ_[ûög߉£ÜSì¶ é-ã²O•}Iú-—áÑï£õiÝŽ,išž’#Â:«+$%='ÕõêG_‰n;uŒØ¹K*a#ƒJ•¸;7ÉRÈ«ó²Ù Q•œRÁÆ%ðϽú‰}oi:E)(ïàýwCÙG¬Û¸E¼ðþbñÒÿ¾#.V›×òUDiYøÜX€~ÿë*ñâ‹“NY¢4Ä¡¬RY§rN¨”š¯²ríFqÿÿ½-°Ù^ =÷X xé˜eÿIÌ}þ‘V¥ìöêÚùüg eéëŸþûï± ”#M¼úÑ7¢"`ˆ ×)J¶—‹å«7ÔR”Θ®æÊÿpñ^{ÎÑ ëºý×±PNÛ‹_WnϾý™Ø éaû˪¹ÚÚ°ÂÄÕÐ.ÿÁßý6Zý•l=‘Ê ©²SŸøÿ~ý±S§öbÕº¿ÅwØ»›8ãèýEÿÌnv|ÿýàKñåOËEY¹!öéfÀ}°ø'ñÅ¿‹Ô·øä›eøÖÈábï½Äã/,~Är&eØ÷]ÄyÇ,ÒRÝ⛥+ÄÿàgÐî}Ä[ÉIKMǼWõ(8üüߟŠ?0ëµ3d9wÈ?áv{ÚøÉ׊kÿÝ¡˜]zêbä‹ßepÊód+äIÓÅuò†Dåsþ[Ÿ‰I#γË.*_pâ¡âõ…߈¿·”ˆƒöê/ÎA™LG¹ýü»_ÅË~#òGžg'¨´¬\øzY\qÆ‘¢[—⾿mÿ~ßo Xÿ÷4â=ÀnÈC th—&ðGûoùªõbá’e¶ZB2d_0D<ÿî"[™ò¾À½ð¤CÅ{öÛJËż—>ËV®ýzuÝ·óZøÍ/bÁWKÅÊuEûô4»#xúQƒÄàöŸ ž×,AeöH u3Ði䫱Ú^ƒÙXbü'¼Ý Æ»Û.Ý1›°VÐ×½úÓ*J!£m£ÛŸ}÷[Й ÑúHíÛ¦­%è$~0V‹Ží2Ä?÷îgwÊ©m¥ßXîƒÏavª·¸ ¿3Z ®M ÷›/¸áÂêøãÿШ²Thén·Í9 ÷ÆÂoii¡¸æì£„®UöS©þ^¶b½xýãoí™Ašaúõ!¾³h£ükó6ñçš¿ÄYÇh¿ÿùÈA»UþîѯþO´ÔÕ¯ùõr,ùOBDª³ÛcÆfá7K1ã²—xíõ£Ûw ˜ÿöçèm=Ð7:rÿÝЧÚ KøxŸyó3•ívŠâ¢²ýÚ‚¯Å¦­ÛÅ®ÿÏÞwÀÇuUéõÞ,ÛrïMîvÜb;±“8ôì.ÿ!vY`é»$„…IHïN³ã¸÷ØŽmY–{o²­ÞF£Íÿûî莞F#iš¤‘t~OïÍ+÷Ý{î}÷ž~ô–Ï,‹õ+]™ ¶¶þñYo mF+%ÒŒmË—c¨™%âÇ»Þc†ö÷¬µdRùÝ=ÿþ&µžQx-Ç ’Ì+ˆŸì?)Õ0?OÆú‹…LûìõX¯âQÎ6ù‚ŽX0Ç‹ t\2g¢wÕ:ì7¾_ý-X¿n­Yri†âV_äAzCG´zs°Ë«lò&¤Ú ñ±òY|Å ßݰ[GÛÑ6í‘;ÏVÑe$ªÈÈP"7mìPu~øÀÞ#CúgCÍ»@€©ÐÇùǓłìuu0:#[òŽ*"t(îãGo«­šýîÅ•êcùÌõs”IDÿÞêýϾ»Q’ã >¿VöÉCµY׿† ^3J|‡«ü ˱֣Ó¢ ÛÐ¥ãøäâ I¶ª7ÿùþFµ ÝrÕtµÈrâ'ØìÜë6ŸãoŒ!õ¬½Î©L?(9{uå6©†tò…¶ªïdÆø¼µœ‡9¥Ú4ÚšT ôM¬Ãï_úH- ·]=Cæ¹Z, .JGÎ\”O-˜.\Øùmià»ÿ BmÉ܉`ênW­"ƒEF©¬¢Zþ±l¾³Ñòà‹ä „$ë@D-,˜¡ܳJðÌí¹îi”"¡°Ò.ì„y®†í8ž:v˜þ©ö­­o^T&{‹åºy±Võ‚Ð/¦D}Aħ¨5ráÌ\UNkkš¯o¾I%Âø# cÕÓO¨V§ÒMœÃÆ׌’FSîðþJXÄßd–Ê*«å(æL p ¿h6F˜ ÓrôãŠ-{•{:ÙÆ¿Öèš@û¿µ9›ï)8vV1—§-=%I¦Aˆ÷M0S0·“Y§à®¥÷’Ϋ¨®Q-"þöÖ:¿s`27OiQ ŒÌ¡µõOÝàõ Ïv^óbƒ¿Ø, ¾êí½Ö>÷î&IƒDAÍ^Ï•yJg™; NÈc,•ÏÝ8_Õë ÐÑ4rÝÎC’wø hÍ+ÃK¡"…“ Öo¢“«Òþ¯ïÔÞGó4ò}\ ß)úL|6ÛSÁüÌ$Ÿ<©Ao,“ÔKåôRÌQ/,LÌ•šœ¨Î_3{>À¾J%K_¦ü#gšTò–Ó”Ô(™$~d”Œóã¼ãš2 Ò’EXT(á *œõåSÇÂ6Aæ@êsê|±ÇÏ£IÁ¡ý`Ÿk „k Ë¥)ø‘ÓYÿ–C þâò-²úã}2 kX_Ðo¼ë­ÏsO« 28’™¥u†è?F_^®a¹ÃÝk£Þ±÷ð)¥aËÉN“1øVÙ·;;Ó߃µÝö8ÒÌðˆè`'2¿;iH¿^ž{9éèˆ0œ¸ÉñÓŸbÝ΃àà§( ÏÍ–Jmž‹úà2Áù—”W+©ºå˜ e«Ÿ©)Ý‹¦DV8pœ ›KIÁõy2u§/”J6œ7Ãz€·;ž¬s¤Ö+ÀftÍÛ­ãaû=ïÖ)±D,ž3A™`ph- \ü¸P2ø‹g—˓߸G "XæÄòªQH“U 64Ð ‚Œa8Ì!(5}Ïl‚†ø+÷,V×(¡,«¬‘Ÿ=ý®ú­þEÚÈo¬YW<ÒßmW¬{$Õ¹CF%ט Ð.¨;x²PùµPÈ ­­oKçOÁ:æ?¿¾Z™ÔÞ·t¾"h­Ïó¸µ5¦ð„–¾yu1üÿÂ8V•RøkèG‰¤7Ê«j›ÝY MJRb¼'hÏŒÜáÊüŒó3ƒk纅¿|þj7^1U™\“ðg_~ÿ‹ŸRæÿÍ ö:á‹®ñºEýl­ÿé^ÐÒœ­Ë¢`KCÞ¡Ó01ÜÀ娣›r8ÝVúž–ödRöm¤ùç¸é²¢²*ÝÜOù»þñî¯Þ»D™áÑ*éϼ¯,€þí¶î‚ðßZoÏIh-߬QÒwVÚ.Â>šèÑ…ƒÚC2‚ ÐAÆð ø49ܾï¸z–ÿñÏ@Ça ™¥vo=MðZFךè/ôÓ š•ª[Jڽᣭû ù)’?t‡òSz7ørN§9½Õ)1Á]§¯!‚¯÷y—ÜïΛà«oW©g`­Šä»}UÖ—D ,¤Ï´•¦Ox~åÖ|Å$í:xRÎ],URMë=ÞÇôý#‘´a×Aµ(è…ª¥È|”dÓN¼¨´ÒS%Ûè uùÔÑŠøK@@.fÔøè/H‰·wD@ý¬Ù ô4 ð;ž B”¾J4·ý¢ yCkëMÓ©A¦ðƒfvyc­Ï﫵5s ¡¥oÞ»>ü»)'ÙÉ•¡ö¡šv ®è£a/,[4!Îsô%{gý.ß Éo4ÁÓ÷sO Ôn¹R¾õ›—UγmAKk…÷s­õ?ïmiÎÖå0pC…þJ43üæç–JLìûõKú¶6÷d®¶ï;ṑ éX74øÛ&}?÷´X G&Æ ºÞÖs<Öî—°Ž Á7Äþ+‡ OÃthh߇K­(Fês¼ÉŠ©cK³Ø—SZ1}„í#êiÜ4~iíQz`ev:²O–(S"~8³`'K aGè—!tJ§š›’qJ+Ò¡.†éÍ‹¬*]õ@ ÿøq‘Y£*—’J¾ÝfÄÀ¾Ê¼ï=HOb™’?N~÷Î~ ¸ó^áÀ„|ìÌ%ÏFçì@`ôþj±Ú€ ´±æÂk…eëv)mé7î»ãäïlTÌ•õ}L;rš–Ò~žNÊü¶rz¹ýõô=-íY6;•æ¬ ¬¢„×êíû¡y)3& —ìt·Ôš×izÂz3ŸE-ü¯¸8uµ¼º]`o¾åÀ;©Sp6 Zà»Á¿Ï³žæÎý­­o”òS(2„öXøÎh¢˜k$×.>K¼N\Óüé…@ñÎû­›?ïh—{®›7 Cü˜Tm8{±DiöIG,…‰²†ð{fŸ¬ÙQÐÄ4 ý´ù,çF‚ ðÞP þomÎö®5HåUÕд$ƒ.rÀÏ*¯É-Þïmr?hžÍuï£mÊ îÃ-ùÊ,ã3 ŸÑ D¤h÷¡Sn7µj©Lö{.R+µk­š4У ~XŒKS»hž4LƒŸÖÖü#Š>¥¦‰š§NõYra‹ˆïA㨽÷-«XÚû;Ëo@~çh8q¼•4'"ÆÐ¿ã9ÙnIËUàêÁîkOüShûz¢Ûýýí òðãÏ©è\×Ìš J*|·Êr–š%:é=ÿÁ&ùþS¯+'Í»í*e>Ä`Ϲ ”òLxð¶}@,Århà<ŽˆÄ:E^B®‰ÿýû;žrnºjZ³Hrž‹>(•£¶•¶í¯ÂtŽÒdš¸`ÐA˜aZ)mþ4–üàOoȇX |Eêaô#n\0&"—Ë×ÌTf#ZÊìãõžS”ªq1aÎ4Ö‰Ñ÷ö!_:D…æ%4Û£vö[Ü ‚³Ü‚ o¯ý¦Û”¤ŽuëJy- ?ÚaÊ¿O?KzQ˜ÿ_Ö‹‚xšNÞŒ5›k%µ¸¿z~…ºFsKö7ûÝ@Ç`€“tHðÅÿúÑðçw÷!u®IØÒ?ÿÍÿzkü¨¡3¹ÿºŠ þQúÑžÖ8)Հ󷆦”†áƒ†C¦£;Í"¬ ýš|ÕÁz_ ÇO>ûìÞw¨à¹_ÿüa:£¾5ÈÃ…Â O¿µ^Ùj0h µ¶z|ÿ©7ÔbbMIó F›¤IƒÐ0Àù$oÿÑ=Ïüòg÷¡¤°Í%a\OBk`O‡±žžù¯³×=_Mnm}ké#ñÕyœXn{¬i¾êëë\ƪ§P~D­Q4ƒvB8Ôšk/œðéjj‚y¶¥2yÞŸþ÷wζ¾‡må˜ji òõ^ëó<öµ~xßÓÞ¿)Ì#“jÅ;5Lýêe¹Ç™ˆ&Kfi-rgQHù«Gïó4Èdò[b0_BŽö®;ËÃ÷Ô¬šaœO›•®‘¦Y W»B*§%&…ƒÓÊ(ñ%Á2J|ÖWDžoéý¼fÀ` 30) †7£¤qÁà)¥N¦Ž‘¾¸¨ökÐ ë{¨Ñ2`0`0à?Z[ßZºæ½FòmfMóçÜIAk°Dé+ÁÈ{[»×ßþ÷gζ¾Ç[¨l½Æc_ïõ¾§¥õÃû¾öüM\o¼“ây ÐK` BßøB` +SDFQGcnÏ:š²›c Øï¬yIæŒÁ€Á€Á@'`€IWÂðiäÒ ³Ä°Åô¡2& Ðæ•m`ÀÌÙMDÿÝA •-{Ž ó~ƒ~KÌh 20`˜¥ÈèS ƒƒ 1À¨Dw-™äÓæ1ƒƒƒƒŽÄ€™³›c›¾UÜ D&")^dbÈÔÊ`À`À`À`À`À`À`À`À`À` GbÀ0K=²ÛM£          Ú€a–Ú¹n0`04è¨úåŸ=£¢]ˆyÐ`À`À`À` [b€©Rv#¡y À•ÿX¶!ÐÇÌýAaÀ0KA¡ÍÎ?&¹ÃûËü©c%>.Fe#/,.—8„¿åµ/ݹÇfJÒ83{ƒpc€Rü"h{Ž8=5Y%‡¾÷ÚÙÍrýóÞýÇÏɰþ½CJ§Ì{Í3]JÊåÿ^_£Æ2ÉkFZr›ëÃE¬GNÊÔ±CdõÇû$ ~ö?¯RHÌ?LfŒ.‡qýCDJMMŽÇµ³ò…›¯ ËXïú7-F³(ÆÌýÍ0ð>2«´5_æL%»öŸµ;ö#y^ç¾ÇeÎäQ²q÷aY¶v§:O½·ÖìD¸ÔÃ2=w¨a”<Ø2í‡ySFË7?wƒ,™3Q6í>(Ûñm† –<õê*E¨†Z–y¾ça€!³§ç“¢²Jùý+)´µ>ðz Âñ³—äoo¯—œ^i2¹¿¾¹VÊ+k$L‘Ò_ÍßbB[Uˆùg0Ð Œ·ä˜Kþa ÿèY¹jÆ8¹ Þ°½…Ì‘–^>E†ôË–Z{¬Su{ÃEf.讫‘¤/Îz»960h' ¤#9óˆ}ÔF_‘íûŽBÈ1R½mëÞ£òÞ†]`zjd8¾ãÏ,«ò•ÕØìò㿼­~ëðÆ}sd¤&ÊÍWM—ß¾¸RlŽüòùå-?ù÷;„ßöË+¶©$ÑL(Jb• áù÷7Ë œ^ÐrUÊúOÊ—ï¾ZFê«®™=ó¦Œò¬ßùÝ«*9+±`]ŠÁH?W¤ÃDÞ0rPŽ,Ä#¼¿q‚V‰ëQÖ–Þ™i2jpóg¼Ë0¿ Z€Ñ,µ„sÞ`À`Ào ”WÕHZr¢º?#%©IÖqžLŒW×’â< !Oôí•n%…óÏ` c1@âóÔùbا—zñÑ3åoHìL¢óþæI5$2Aõõ.ø”¸”Ôß^çôT²¼²Z*ªlæhæ„áêü­ /“n¾\¯ÛyHòŸ‘nºRnºrš¼þÑv¹s+B9ž[¶îÉ?zZn¸bŠôƒÿ‰ž‹D¬ ®„j[­Ú[ךlÓ [´FÞÐ/;Ýs*æÜõÆWÖƒs:Œf)tš z<r‡@D£S2mìPÙyàD3‡ÝÇdñì ²sÿIå·Ôãf`0ÐI &‡>‚4yš0rÇ·pkÞøv$Ègoœ¯j–’” ¿~~…9]†*«ÅÚÆÄDË`h‰ôYê×ÛÍøì=|J&$9Ùijãù0ѽvî$u¯­Ö!?~h©–(lôìy‡Nɬ #dÆ%Ö’I:tò|¤pá¦áøÙ‹úPíé‡ç R!Ä«¨ªöuÉœ3ð†YòUæFƒƒ–0°hf.Ln¶Ê#O¾§ñ¦YȹXÑüî?`^ÑÓ]‹g·TŒ9o0`0ÐÎ ††Nñ¯¯Ú.4©KSD()¯’}ÝLÎÉæLÎû`–œÐ8µŒ€yÄî‘Ó`æwÜs[a‘[³Ä£÷5Œ’3=û€ãëgO¿ƒÈ©N¹cñ¬°"ƒ>zϾ»Q6ì:(ßû·O!°IRXË7…õ f©gô³i¥Á@»b``ß,ùú}×)ó(ˆø´ÏRŸ¬tyâë÷¨wßtÕ4e²£+2êDÆ£š½Á€Á@``H¿^råô±bƒcÙºˆ6¾GÉæ§±9á©Aaq™:¦S4LíöºÆ -g.” zYŠ:ïý¦y±11²à²qrë¢Ë¼/«ßŒˆiÀ`à¾r—Bב( w[ëƒõúço¹² "ôÐmžß L‚†Óé¬GVCòzc€ñY ]æfƒƒÖ0ÐZèoP  "WÃ>5)Q^]¹]UˆZ¦ÒŠ*•—†¾EnÉWfy#öUáÀåd)!HYE5‚´ìC´K»§!4›¢„Ú¤êšZåã4 DêÖü#*45Md®´Ï’çAs`0ЀŽŸ–LéBE×Ã(…ŠÅžý¼¡^zvÿ›Ö „\˜®Ÿ?Ùð!ì/0  „ŒúÝ~õLø/•'Î+“¼;a"»|óùî^Gð‡"ù×[xò&];o²œ½X&ßúÍ˲ ~‰ô1Ñ@ÿ&j‘ýîÑ_½¨"ãÝqÍLŠ˜¿‚ßÓ—öŒ<ñìûR\n|G4ÎÌÞ`À` ë`Àè$»N_™š t PBxË‚é]¢®¦’= ?ù²Úïnõ´qCä©ï<àAÁÕ³r…C…'%º#Xê‹3‘ä“ý}…ú¿ÉmÝ&wQ*é4ŸûÒW+ó'j¡R ÅÒšƒïX¨‹5{ƒƒƒˆÇ€a–"¾‹L     :ÞŒ’õ;%}Ý—©5X òbÀ`À`À` «bÀ0K>zŽ9%¸i°ësì)i×à¶Ëmü­Ï›}çc ÜýΙ¾ïü~í̘1Õ™Ø7ï6èž÷¼ÒUÖ©p·»+Ž®ÒG] §ÔÕ0KÀ–þOœ½„$yg¤¡RK‘pÏsËä9½¸·/Jˆ•Œ”dé…èAt¤Ú?[E2ÌS€ø ãíºß™¼Ž‰O";xÁ±³°«¯’2äe¨µ;08Bxaè{â ÷-aKOôzßÒ}]ý¼S]½Mý "z^éi4Šnw»­Ñ‘×Õ¾kÔ@Kp'™ˆ^IZriɽ•P¶»¯­¾Óqg{4³Ä‘ Ѫ÷˪íH\†¬ÐލD±E'ŠSÂÚ4Fj$±¾Db]6ywÃIMI’E—…Sl®$$Äz§Žëúžû&ö;'_†­­­“5;÷ËÚOIe;õwé{ëÂåtºàì]"ç.•É„fÆu{›Cj±¯s83Ã߆Iˆ‹CÔ­ÉLK‘^™©295úõN—„î.Â3¦zî|bZn0Ð^è©4Нùt’)WTÙ”ß[LtŒDÇDI´–F·WD`¹õàÖcýeNªwÖï–tÐ’ gäÊ¢™ã#e˜¦öé´Ë,‘XÞ{ø´<÷þ)‡©$.G “ÇKöލ¸öÁ6JuÕIv]¡äØOËÛëvP?(÷.™)G ñíÉgÑnèák&©ùB¨E|yåÅ$wDÿwž·2H ¼cÿ ùdÿI9vî’Ô1ÒàŒŽ“ú¨Xq@À@!ƒ3Š‚8z»j$ÎeǸG˜a‹i+Çúˆ}dÂÈAˆª5ŒTr—eœÌ˜Ò£Àì  Â…žJ£xϧ¯ ´}9˜¤Ä„xɆ°þtÑ׆pừ•Sõ”XªjìòÖÚ²vG|úú¹ %:²:³Ç1KZb±r[¾¼¹ú©ŠI“ƒ©ó¥,¶w; ·y‘dÄ ã©-ÃqIÆÔäÉŸßX'7]1Qˆ&Hl,$&½l¤ÍqÊÝï´v»¬Þ±_ÞÛ”/Õªÿ§wHÿwµ¾ç¢ÅÍf«“•ç«ü+uÀ_mLŠ\Š"åÉYR›.5Ñ)RÌbb}µ¤8Ë%ÝQ"•0{=tr‡¼½ö™5~˜Ü¶h¦¤$Ç7ûšYcž–ŠªZ©¬¶)ž+66Z%¸%“EW­¡êÈïÆŒ©P¾Hó¬Á€Á€/ èy¥§Ñ(ºÝÖ5ú}¬Ññ ‰r²Ó}F`ô…¿žrŽ #Cösc„Ê„åÿã««åS §É5 % ޑУ˜%ý1®Üš/o¬Þ)—âJAò4HÄÃgnH÷AÛ‘z¥äV-ÓnåÃDqÈ2mz ˜lý^ÝïÔ„Ô‚QZõñ>Y¾¥ Sû?úžx!è½Æ¢_Pó;RQ]£˜ùSç‹å"¾—Si£¥"&CßОÌb%žåV?X=›ì¬”þöãRŸLö?/_¿÷je®w ŒÔáS…râ\±G¾—*0I-A "må )æ0øŽÚOFÎQÒGÍ@µô\(ç͘ {æÙP1ðß¿¦È~ÿ‹·Jß^iª8ŽÉ/ýôòð§—Hîð¡¾Â<ß ÐóJO£Qt»½×èäÄeºÝà³Ó ]Ò%^É•}³3¤¸´RÞXµSùZ_3Û0Láì¼Å,QJžw蔼¹æE(ç§Ì'.ƒ*‹Œë1¡JdÙú=ÒjæÉ£‡(†‰>BÇû“°­¶Vö<)Ë·îˆþïȾçb¤7³8_£³—JäRI…\ÄV üOØBǪP¿X%ÄÉQ0-' Kd_Êlà¬_èáUBuLªIšæiˆL¯Ú €d¬Æîtûá^®—Åô‘šÄT±G'H]T¼¸ Q‹F]£aðÀdG¥œ/¬’£g€.P‚†I#ÊÜ)£”l{˜·š1åÕ‘æg§`àå·È¿ß½¸SÞm^~ ôTÅ{>]5šŒRï¬Ôð#¹›–H†’øÂ²®h\ '¦{‡¡#ÃÑå=†YâÇHs¢ç—oS¦wÔ(E°>—UVÈ++wÊÈ}UJÆ©a2<ØïTëS£TV^)o­Ï“êè4¥Q ¾Ôð>Ùž}Ïö“Ib0‹ü#§e×ÁS²÷è9hj̤1 ÊO¯Þ­‘W½Ä×Û$fs„“‰cÚ…Qjx½ÚUƤËÙ8h›JIq üS&Hql|ŸüŸ¢¢Qï g‘ô‚O`ÝÑÓ²‚‘Œô¹åò)2kÒõ-…ã{êécÊÚoæ¸ó0pÕô±J8ßÛIðSðF÷|ùÃmrª°Xi^çAx°Žà„çßߌhZ©ròü%)À|Яw†üXœ8Gód™ž;Tn¼bª*Ÿ&²ç‹Ê•€’]>óU-ؘA¯Ú¶OÊ*kd,´Á×ÏŸ,ÃáÃH8w©T^ø` ´ÊE’:~ját+ÏsïmB{ŠPNºÜ{Ý\å÷¨êÿz*â=Ÿ¾ ¡q̬ È@à Þ.•É Ë·ÂÊ¢Ÿ$%:2p,6¢GPâšX\µ}Ÿ æp0qR§™Þ5ï÷j&MRfO\HèWC—u7t¿—556Ù€ˆwUR@(?üËÛò§××ʦ}§ä˜£—s¾=m¡¬Ï¸Q6¦]'[S¯–ñ{{êÙš¶ç—ʺ̛dKúµr<ÑM`ùª{8ÏÆw¹!ýFÙ›:[™üÂ(±õQÑRÛGiª6¦-Æv–œ­Ž•g@`ýäéw yº L Cù¦zò˜ g_›²BÇÀà~Ù2sÂ0+[ÕZá]"£dM7L¾ùÙëeÊØ!Š©aÔJæß\½C²ÀÐ|úú9R¦æ~C˜Tö¾ëçÉñsá0~@Ý[VQ-ÿX¶A.Ÿ6Z¼s‘œ¹P"ŒLFX±y¯\,­¯Ü³X®›7Qöé¥Î³|ú"f¦&É=×ÍSd“·ÖìP×ø¯„·.œ!߸ï:DÎt*&ç¹æýîÅ•`ÊjÁÍÁ;Ç€as›ý>ûîF˜×ÆÁÌðZ¼' Õf>Ò-@Ï+=FÑí¶®Ñ•X…ñaLï‚ÚÄ[Vz²Dp<:28YEÛúQ1 Ñ ÌŠË*¥¨´JfM ælˆ§|¦ ¸íê2{âY83Wžl|75E¼7Ìý ÷!¿ç®Ã§/Hʻ㚪¼Ex®OVºr=sQ.Ÿ:VÒ’dÎäQB?Ê"0jÝô¼ÒÓhÝn½Fo?xZE½£Žà1@ü1zà:м†Ž Ö'ý·q±>Յ޵äâ&Z:‰&OˆèÚÆ ’¬êB9ŽúŽ6@Ù›š`w™îw;B„Û MeÿÛjíèÿææ*—Þ>O„Ú÷šQ¢VÞáSòì{[¤¶>Z‚IbÆž LQÛWÆ"Êk«v(âîV™±˜ý1Ëë‰cª§Ž™®Òn2D×Í› ¹]2L‰ò–÷7î–Âârù›ÃÙîpŽ[ ÄgR’¡ÁÉRÌû7"la!0Q7Íå~öô»ê·úÁaéü)RSë?¿¾ZådÉ}KçË0$É$hõÍR9rÈÑ´ŽZ­'”Ђ>…ô£äV‚„à„¡ý›F§=€à/üþ¨áÒ@Ÿ–ÓJNÚàBŸïj{=¯ô4E·ÛºF× ï!Ã)Iñ&T*ÚgÌj N{³äT>ó—1R$ƒªê¹‹Ã0ØpÇ#¡'ÉwdHäHÆ¿usOÄHŽ f‰&xGÀ,Ezÿ‡Ò÷º½L²LFé`”*¢Ò$/uNÇj‡üí ¾‘øh–7¶aã¨p«KæN ˆaÒ8î)cªƒ»È¼.H ,™3Q6î:$¯¯Ú_÷’Nëmú+åï/ßüÜRŨ<öë—š¼!>®©ã7µD¾€jšEýøË·7»L¿¥ûo˜‡„˜¹B3¹¿¼±Ös_!ü&4aKB9ÔP@޶6åy¢ö­FtÒ—VlSó3à x?ó°iHDâvÂ×éo¢^v'Ðó ™ÒH_£ˆ÷PÖ)k¿év[çS¦Ob¥î—O¾ó¢TÞáM"‰OŽ+ú:2´.èöÌÕ¼ L‰–#*±]ΆÖî§IÔ±ž%pœ%ᛘà6 DŽztõ2t¿‡Œ‚GɨCÜ ZkÛ„¾±òênI¥‘áN•ÕËÿ턽ý$U 投=œReoÝÿ,ؾ׋ÛzæB14J[£´+e~Ä÷ QôcG'"x…9¶òd""æ ÌÉöähj«Ð`ÇÔ7æ%É¿Ns›3–׺$ÿ¢S~²¾ZŽ;Ûz¥ÏëqÈ\?op¬¬=îÀá󦆓ÁŽ©ÖÊ4×" $‚î¸f–üéµUŠ8bí¨A*‡é1ƒ,Ø¡!Z±%/èJO3ÌØAäYÛ3¸1*§Ë£¢ŸÚ«ÞÐ 蓉@ ý‘èû´ç=ÅÐñ¹X´i÷!˜Ûå¨o­¸¬ZÝCŒft›‰0Á‡“¦Ó;_Èm8Ç\McûË$½¥½|,˶ì¹CSux(Âx–‘%ß¾Lâ¾þ™k›Tî„Ì¿&z\7èd…ïXhý _£yM~ßtÕ4ᦡ½¹U Ê#ÒÅ6äna@n6øš¡±}&E$¾›½‰i5a ^-YÚ‘Ÿx dü¸1"^"´L¼Ÿ0~?üÒmð‘rÏ·4ëëê`W¹4¥3pêœbm·uî(­’ÆYÆæ90àÜFÉ#?X™­õCÈüÅHêÊ\EÔîAÊíÇà{×K®Ÿ7 9 âåËÐL9}B·=Ю¦A0Iú#1lYeµ0¢±ÖßÚÞß1ÕZæšÁ€¿ £ÓÆ'åoQÝú>ë¼ÒÕÊœbmw°kt8ÆèÁ}¥|î2Ú×­yGT±Ô ~ Ó<æ; j>‡ ÈFèýsŠQêSçDBç"uïP¡öèE$e.? š^;pòœlÛ{TžŒü#åÀdõŒ¾­ý÷Š~4td¨ˆîÖÌ‘ãù(Ĕå´3Ä µg*Ü O(œ}0•yÝx0N¿¹>E™Î¼^P«4 wOH’§wÙ”oÂÃs’„Û#BÇïj€âd¤ëï÷sæF…7âþhµåtül}ìm`„´ßÑþ¢Fæ·à’SÀEøšQÒçÞûÙ÷l—Û„¡N."zmÀ™;É@ëH¬¯–>u§eØ ^ Ì?Cܵ¡Œ)*ŠÞIþ޹a, T!ôƒ/Mó4h暥çÜg×ðŸ)Òå4Ûû9¦š=gN o³¾ Šè14Î+]¬É!Î)í¶®Ñ *õBEÂ2ò²õ»Uˆmúöèo7êhDoL•’2· *Á|Íëç.•øº,ç/5šÞWÃì/¾Á¬ÔçÍíp’+›Æu;ßcŠìöÌ{’%P &\¼µÛmSþƒÒc¡s9®É,H=sBvr´' –:À?3À@V ·ê "˜þÿõÖå³d-údƒ†p84»lnb—ÇkO6F%³…‡®õkr#O©}²ùŠP‡hŠZÆ@œË.ª¶Jl”K¦Èž›‚SÅÐýÇGÚ!]Cj—Žjì³aîùRuãœU -R¨`æ“P1hž7h? ³Fµ_mü+9sJ°ó©5lû®ƒÐ­AoXS~s]ŠŒ„Ã?5L[L­(1î•%”èú(@^„ Ô¿LOP¦T÷OI¾Ð*µÂùD)´XUfBC™‘Ȱ¨áÃ'@g=ƒX8=c0Áe ß ºYIŽK2­b¤×WÈÜ1}~8Q…Z¦¹„6™èè&o£Í1ôYŒ¥!`”¾0-A™åí‚é]Kp )¨aT½L˜Š0000¼$51FlÈ[#s'ŽjRp1¢32$:Ö$ø)¹P¿a§Ì›2Jz!áòð}$'ÛXx4AZ7øÑ­©øöà¦?¢f<œþ?W&Ÿz©\ŽX˜#އá`ž40z£]k0¹ÑçƒÙ·G[‚©GWx¦½pE ÿß©TDí÷eÈ#s’åÛkä£cš%oü”Â1ÿ…<›|çŠ$Ùø… åç}O[¿[jÏs£9ƒO0?Iv]a[Åù}ý×ץʔ~ãÙï}ÜHâ>ïKY’†ÞðÓ«Sä±Ëý…íý¼?¿GÕäÉÔʒꪖ)‘&+Uâ`ÎHxtî¿Ô*´Ô­>äÇŧ`깡Zþmz¢,¿/]rûÄÊ7VT¶š“küç9dõç2äç ´W{‚©‹yÆ` §b ˜ïb ¾œ¥¶ÝfÊ;÷¦Ë-cC‹ Ȩ¿ôÏiK ÷†R§`ž¥)Þæ¼Ã2gÒHa„I Ò-OÃÁ“…½Líõ¿WVnÓ‡º×¸îЗv³—u{f)”þ²j¾‹Ü&sÿZ*Ï#°Ãÿ\“";¾˜¥" Ña›æ3èàߤùÑÂde²õãuÁ™E@µ[^(—˜ýE'8$:.Q@Õé×±Ÿ-2hvî˜Çï[ÄÃú§“må"ÿ× ÓÒj…è°_g“úÚ*qV•‹³¢Bf§#ƒEj«Ot± ŒR PÓ²ò©ÅbhÞ÷ΉJô½C¢¼œK/+LtúÌD¢¿Ò•„„´U’[µ]v¥]²ÿÒ,HO!° ‰sFz$ótç„¥)ýÇ®Z¹„óß_¤LQum*"CÚåC,ØÎH’ }¢åL_ÝW+d05\9$Nn/›N;pÍ&ÞÌ#Y¦…Ããä¦1 òÉù:䶪“r0“bûŽÜé³Ö<0´Om·5aTuù-í$O—î)’é¼(ým'Äqì¼l?Q&Õ}¦ˆ¤åHLlÂ1£þtQ ò52oeðOû6¢Z–UÔJ]M•Ô"FMiôG$=Ág.°2Jþ”Éz0Jj¾çˆì9tRfM)‡!%¿vîDenôÞ†=2sâY±9ç&!úWoÙ{ø "y‘Áˆø5oòha”»¼Ã§åèé *Q-ßýѶ䉓Üa(v›\*«”?¿¾Z®›;Y÷ËÆ»N©2lîä¸7„å9)DPš91êØ—î\ˆcCŽÒ—-åLˆ’Çæ%Éää,öÞ!DìE*§Z„¹õvÌ£/å×*k‡õ'26;¦YÔß@êÑ]ïÕ OKí³5äû²^gN1Ýfvj£_½µd†?[¦&_s$=÷½^ÑLJÜÆkÌeƒ€1@ó2JÌEB“²øø9[+CR«à›³FòSgKUtð¶Ó †ÆË,¦y¨§ö‹•?Âìðú‘ òøíÝõ*ÌPߎEøÙ=6)¬rI5B¦Ÿ«pÊŠ#v¹ÉV)døÌkž¶]‡`o¨•/¡%?o†¢o Ø £°¸WËÝT`&iý„ëë€*Ê00VÕ]º3*FŠbûIQj?Iƒöm|ÕÇ’zñ©Ž›#®”ޥěL’®“Ù t ¼†hò±1áìNìy̰Ȍ¹nç„>¾&j’lBøä™ë\ó†+§ªÉ¿xö}ùþÿû”\“CfGÃñ³•ÿƔуÕsœ—ͯŽW}\€\4Gä¶E3dý΃RXTª’Ø^‚ßÇ[kv"Ég–bÐ £¤±øÞš Omv’š{ŸúØ-¨ýÒÌD±A^õ$æÔ$0Ró`“ W‚eíH‰Q'‡‹cÄ;êoàµ0O to fÉþõ¥ðÅ(Y‹ TJl}Ö W ƒ†eRª$%%*Í‘7V(ù¢3j\\LeþˆTÉͬ“Óp8-¸X#—•¯AÞ¥Ár&a¸T„Á,ï5D|\~¸Nåúðþ ˜EÉA½@@>¡A}¸¸ø’÷˜ËÿÌ–MEL‰‹’¦R-ÓÔ<íšqH¼êT~ňw3lñ¸¶ZŽCKu3˜¯ÓåõŠiÒï vO||’z¥Ì¬\#¹Žyä†Å`6ã|â™9~w}ªÔÔÄJy…KŠŠíRx>IVAh ƒ­³yÎ` T ä=+WÍ'—åSš#2KÈ0ݺè27¬¿:Efjî9~¸L3X¾öÄóM|7ôszŸœ”€PË)R[çP¡—y~÷’‘†œ4%¨’ [òŽÊ½×ÍQ0?ÜCw]­òé2ÌÞ øÊȧ?…ÜmL†Î¨½„á½bävœ#³DHÕG³`0½¾ÁÎ;꯺Ùü30P0ÌRB æ¦( iÌØÞ±òÝEé’Ý+CÒÓZ&˜ý­þ“Ï~ »÷*xî×?ÏœÇÆŒj0vSÔj£Nt0І‘ÞýTdcëwßÃýzÊøÑ¹Ü~d"j»¼"„m=ÛZ¶ÅÝŽ°¥—æw$òÉP¥"1ž¦}Á0ÅF—Ë©âZ‰¶Ÿ”lލx9ž8ŒÓˆÀÚð„­1¨h°4Èð¬áF˜Ø=6?IþKÕô€2°Œ,‘Üáí|¥›ñ: 5Y|ü+ïWÂ|$^¾Ž¼e@ût/´ULê Ø£ähâ8‰=¿Kèk1|`_åÌKmƒîˆòªIK¦B$ùa¬ß&Ï …Ùœ†‹Ðþ"PóÃ¥0A%X?½ ”ÉÈ`¾ ¤¢Z2Á@ze¤ËÒ˧*! ÷í•n%""Hð• þGŒI!–†‚‹¹s|¼ðš†µFÊ£Qaö~aÀ0K~¡É¿›¾·Ú·ƒ¿O›» Ù%šàÅC³¤˜%{ B˜:üAE %& †éb-|š$t[êÐt½s¨V™x0Â#µ=ÉÐÁ]Ác–z-Ìì–ÃﯟØä˳šP׊—¿ÁßiÉÈXå·äÝbÚ׆$ô•}ð sÆPýdŠF Êä+ûì²ù´SEKAP‚ +¡BY¬›8R1;|€ì>xJ¦*;¡õiP*ø¬î¨A}•©ÞÔ1Cd4RœoÆ í§‚»TŠ(`v©†³û1˜á „9!ŒXù‰óžòfŒ&g‘{æŠi£³Uf+:œùO!üWf'ÂWËÙÄ´&ÔrÍó=Ô}ý¾ë”`€Âí³Ô'+]žúÎM’ŠoâÑÏ^¯’^Sƒ­Úׇ?½D…HŽk<ÏëÙ™iò£‡nFce™,Fᣠ‚¦|„ùSǨMý0ÿН«–Ç‘íÏ7!—æÑ`žþwcsÁ”~©¯¨¿=Ùçšy÷†ì-‡O]Ð(2{ƒ1ÌR€ƒ`4$¼÷ |ò0!h„5§‰7Ïýñ†yq¯]þaÆ¿½²Z>>Û}=Á'?~¸¯ÝYý`a1qÒ]àŠ!±ÊG†!¼#´†‰þKtÖ}gë!)sÄÊž´¹RÆxé@BK9ô/ñDÁÓ[z`A •Q²œnrHF é*š@¸%>°öBÇKVª›YÒù(ˆÓH—òÜŠ¤™ºte Љ Œ?`e”¬÷{3JÖkšQÒçˆFÙëf4ÚÊÈè¤P©% [˜L]ƒ:èÀú÷ÞQ­×zÚñ˜Ÿ.„FÔ0K=­ç[o¯a–ZÁO/ätaÎq`ö:ä‰Í6ùr° H‹–I}cän$qã4ô“«Sd’Ón>]'ÿµªZ®E~†äüýÇÈmêòæ±q]†YûÄž•ˆ_öÕüG'ì#jÆþjÿØ(GÝSû´pÒ/÷Œ¨sD= C¥ñ &ó^àÄø¸êêÚšñ{ÄØÇóvGE¹^Ûÿèäbï÷ÄÞÏøüŠ©‹ŽŠú{Á£É2»0ŠÛ#ìuVR´¬ül†¬‡Cì•íÓÈ¡uÿäDøÒDÉ6ä úÓÎÙ}Þ)\”V BÜ㛪å‹Ó“dpz´|ß­uŽh3µ"+¶í—JDbÈKVFé9„?VêqÕm Ç;z9.H†£ùarP\#A޲}•A“˜ïcLõ˜¢ÿUNj”ìEΨ [ÿwsš|sErK¹µÐ_@¤Àýï®WƒEdHf”¼~Wº ê„qå«-æ\×Ç%è×ÏŸì øÐõ[Ôu[p²Ì‰À6u{šbõQò§8_Qýy®;Ý3¨o/¹~Þ$%@û2"59}!ô ä_?u¥¼¿q ­ÏöÞ²`ºTÁÜtå¶|=¤¢KEβ 2gòHa¢Zjlwî?ÑPÓãÛb|–Z·MPÎ’ÌlÝ?-F&çÄÀ'£VÖ€¸á9åÏ«‘ˆóÚçÊ„ 7©UÊNŽV!ŽyýTY½0ÂWWÔt³ÓU—®o´Ó~¹™¿íŽ¨ßº¢å™Ø¡1ƒ@[®p‰ã??ùê英±QÚvà›“¦Qâ½ =aDåº:69zQBBâ 8~˜(^ë p ×v„Ãæöí•Uò|i lðä–jùÜ›•l%ÍHö4g ˜èÿ„ß΋{mò ˜‹Ûüoáðö•GPB&Ép­²çÈ9å›T|~%Oƒ,AÀ†#Å݇YŠsÕʸꒂècf+ß/j“ôfizظ0E™þy£†eD«Pèéñîä½”þ^àþ—Éw5 Gmö‹H$ù ¢ Þ:®ýÇ•®‡Ùw_ p¬“èKOe0R23ï!Ñ÷—UÉýoTª$ß]ó¾¦(CdÇS…ÅR^m“eëv!GØ1µ&d Ò¬UÚŠè³:Rd†½>ÿö}Î4£°9„>òQkð»m6É/tÂÉ¡â‚ký[{œÌ’‰ŽìuurüÜÅ8Çôií‘-Æå”IUÛ$Þe—™#zI"Ìð$#¦IÄ{K`í“Vnóù8&CÁ =\'Ô8þ|¿¬° w—Œt3KÃ2£…æ¾<§áU/ƒYú´Õù‚WÖúërÍÞ`À` c1p´Ä)?ßP#Wü½\[Q-;´É[‹ð¼-”9Åúl óixjß¼†Á'£ä€ øÉóEÂäÉþÂÚTòæµ;÷+áÛÐþ½ý}´ÝïÓ¸n÷u㴯ػ“Ç¢!ÜŒha'ˆä$h~¹Å8“¥Ý m“ºYÁjÌó4¥ùÚ\ð`E§Áœë€%çõ¹–Žc]Ø7åc­mié¹pœÏ{tòþqï©É}|ßÄØ(‡Ã.QÎ}LÈwþbïBh˜Ú•ªìLêeQggüâ`vK¶è1ƒ£·7Ö) 6‹.wÂÆ“írä WL¼† _G¿^f…ʧ†©+bpìÓBÐL;”6’ÇþB }Of‰Z%;TÿQ uˆ`À78LªÚ*iÎb™4 MràØÎ°ëÌUE¿ Ìð5~| dLMÍqO—ܦ6åµõí3kú| F©)ýbdl&ße¢_͈FdC ¡á£–Û_tLù[®¹Ï`À`À? P‹´(/Á¹‰ÿr‰BZ! „… iº„:§´4ŸÖ)Xîhœù ®¨LU£ºÆ½þÆCøÖÙ@fj¾4øÂ»¾fö­c ó{³õú…íj ¤Ç‰õîÁìo¡¯C²û˜U½|GšÄƒ*þ.Ìg6ž²Ë—f$Ê+w¦Égßð-uØ‹$pµ˜óÖ=)ŒîõÄf7³åï{êm’ïNèï3a½/*êy—Ëy‡<@´+êy–ë¤ uU<:i>8m¤^;á—{Çúzþ]"†jOÄ„A¼†ŒpøÅé‰ò…·*•Ú}ð]úÎIP°5¢cxV#“K Á! ‘ëÏ»í{F˜¢f©š¥¤8„Á߯î´\ˆäÏëzÔ=IÎ*™X½E’•’›“,ÃûgI2­'%&JBB¼Ò,ùZ[BR cêHƒ¿}ŽÊ‚Lvo˜ëj sÄÈ™ ‡Å+fiÙÁ¦ŸÎPŒ% C ¡ d\:¦ô{ÌÞ`À` 4 P‹ôj¾]HKð»÷†˜n™$Wˆ–U›’d÷ÉÀhïò:êw{Ì)œOË#ÐqJGHµ2?}³Ò¤¬²)]ÇH´‘õ  ÀÈ@èè̹éôäü6l0™‹òÏ4ЦXŒ*“…ÈA JhâÅÏ–yÂçþ¾ÄÓ Ÿ~­‘yúïT¨ç¼µNž›[8`ýb¥VRR•„»3$®„¸¢jío×»¢œqQ®;XÕ>Ù)yçŠ*ëÇýbßíøù*Ï!\x/„ /ŽuÅ!¨típž‹$ îô–Ñ]œÔÔÿÇ Á¿wB¢ò9£9^ÿ4·° 0‰ÞÛàWBÀ[ðß=8 —0Åëdª“Á\½šßÔÜJ=п`û¾ÞU¯Â^3ôõ¤!™²óX¡ ª="§F¶ð¦žwº¿ý„ŒªÙ‹oË F)Q†öËTù¨RSS$99Iâ5³ÔÈ”øÂ’OÜ2¦A»\T]¯rT=¹¥^n„’·†šfw÷LLQHÊûŸ«é³z\e#èÌh©Þ(hzÝW]y.Ø1ÕRyæ¼Á€Á@ë i9´H/{k‘£i„"wOˆGz‘©©±ID‘ÁÐ(­×¤}®†sNñžOá/­"ºÒr£³ ¦w—*é)‰±ïDòå:¹P\.¹ÃûÃÔý’Œ1ë…4dgµï¥V‰øä¸"ž „†Ö)ƒÐÊŽˆ§õÇ8´_/EÏf×\/2<šQÒûÆ8PF‰e«úîÐ Ìÿ~ýÞŽØøÊ¸³¨B)>¯ê½ß˜tŠï\óùá6Tå(›~2CcßsÊYïü¯í~dì™(WÔú±Oämõÿä¹Î7â¦UÙ©ÐÔ¡Qôÿ*°€‡Š² W޼kñ{Û‡,»7šÅtyyŸ;胵­» øËÍ©Â$ª;%ï£ÂއÚ÷lóàìé“'#Áp‹²h½¬õì)ÇŒv7½bŒ©Þ%i±N™Ö?Q÷Í”Œô4IOOEße†GŸ%æ![‚PÇÔ¯¶Úä:FÊÄxš†Ç;úý’F"@L4Ó”H[ჵj\ýniªpŒ}ß9 Ô1åÏ;Ì=¢¾Yí‹ÄÈ–MÌí€ Ø¾S[®'\.G@(‚žWB¡QTAô/\sŠn·u¦%9stu&0t¤o\r¶[3KD%ï Lb9Žn³Fõ•uûÎJnõ'’Ÿ2#\µë½¹L ÉøøAiv'¨è\áŒ[6j×Juá­ýÎHgtà'^‡¦WË‘R8kßù)3ÃÖBNP‹þ\$£PúÞ-ƒf æwd OGïÑÌbB¼MúÚj¥´Æ!%v§ÄÛKl.x4ÕĤHeL†TG§‹-:Iꢤ.:×À@H=‘׫ ‰®ID4¹T¥½)ÂU0c–)Q˜¹`€oИêÝr(yž ¿+ÍY&w·$×WH¿„:D§Ä+‘â‹æ\x`¾‘-Q,ÌXãÔ÷žˆþ&N’“©U³‰LÏ‘‰òÇOÉ:@Â=¦tbkë;Âuʘ WL9Ý£~ºel´Ä>¦ù,ï6¢Eò~–¿­óJO¢Q¬í¶®ÑCÔíZ ŸÀ0P\Z‰À™;~ÖºµºÓb:20L6½;Ò˜%Zš¸Á£ÊV§lUBî]­êÕfJÃú÷’KZì;sF&  TAò´NÕ0QL¦wÝY–/{§+S*s¬3 Þp¨Omö:òñПHáØ²o:":çW“:9œŽšPûß»ßižFÍKßô$˜ÈÁÕªú,úÿcôÿôNëÿpõ=Çx%˜¢Õᓨ¤_ LO•Æ)NªªªUÄ¥¤D;\ $&Òr›SªÞæ(ƒéZ%Ø\»meDI\4.ǹ$±äS°gìúz$ñ X/G¡œJ…ÐåãÕJam^Jª¯‚IÝEh°NÁt²ŒX¤º :Ql£v¦)¢n-¥D˜Ø¹™%0LIdšÀ$á<K2UêùL{Ò˜j«s8Ÿ À CòY¿Ý¶3× º®ÿÍ¡„cûíä_„aõU¬´uz„ É54®4ê;× ñ[‹ÔRý畞B£x·Û{>_m—K%Ò+3Íh˜Z<–óJ£F©‚Ññ3„ã(Üt¤åu=î0’˜%Ï\T_ç¼PZVÅß!3K”:»‰¨8E,S²sÌÀ1— ==ƒ¯y¥'Ð(¾Úí½F"¢›£¨T²ÒSºD¸n¯ÑÒa?é£DÓE†:G]Žejf:²Ã/Š$fɃžºÚÚ³5µQä“Aè„îÒm¦D¢)5%³]& íâ/J ÎWÊÔʈ–#…qƒ¤{ó0S/æ(`èMF©b0‡XzÃÈt4Jii©’¢Ì…R<pvlC(@<Ÿ¶êª‹¡”ÓQϲž¡öKýnWš§ s_n“³5îþEÿ#Jb{ö{ö=(’,ÌÌÑ³í ´*5 Ìf8Ï’aªS‰l˜\©i¢éÜ’ùÑÚ)ö7ñȲ= ^¢qy÷2!.ËKL¨•Ìd;Æš]J$0ÑQ"iö²&Ù6dž:'¥¤$ÓC«Ì¤ä‰h0£ÄÄO!¥e±hë¡¥“üNh›­&2MÚžL’ºÖp?Ÿ zÚ˜j Gz>±×Tž‡¡¥BÍyƒNÂ@-’ËufÂ&j$Lsu®zç{ö³_:û‡û÷BØÔ›®ê¶4¯tw¥¥v{¯Ñçj\RXTŽy?QòhN¯„xáÂW-‡y”œQïÌ!f“¦ÊȽۅŽìªx W½#‘Yr?ubczvöÃy‡ÏÈì‰#Bn«›ŒU~+ä¶IÜÑ~8ÄÌÄ89z±\b*/(æ…º¬:IP‘¶œQáCOŒË!Ìz‡„³¤ékÑ'É%ƒ2ã•ÃyFó¾`#Ä:ú›$Óä j£NÜ¿÷Sú«7ï¨{t\¬çÀá#ïµÿ[êwíÓÃëiðç¹€áÑ`\³HûµCÿwTß³=\€<Œ ´-Ô¨PH@…šQâofw‚Q¢}3¿+ä;e²<…Khn”ùZÃÄs|†Œ’eW×Ô(í•bš ½##Ærk¡±ª…‹'wj¬TGã÷üb¡Áг†{·Ö"/ߣ$0}ŠYB{ÈQ›äÖ,ùfæh§ívnukÁXßPAáíç÷iKºë˜ò…/=Ÿœ:røc\×ß­¯[͹.‚H¶™䌒N—VV{B&‡R}|ÚJÓ›‰(”Ù)2aä Ò?;”"Ãúl›Z¤(9Šõ¯.gÙß~ërFzMÇÖ. hi^éî4JKín>Ÿ:áoë’"¬3Xâ°nÐìš>«¡ÏéaTP˜; =×RšÝcý ¥ÇȈ>é’¯ö¢#; iýŠðqái¦Zx×¾ûFþè)ÓŠ>Î?Ú ÌçÜÀ#Á£²ƒ€ã #ðc‡·øàª)©´I œàkáÚS‡vÐaš4ÅA“í’Œ„IƒÄ<fB))IJ£äÎÿ’¡ŽIjs!Ö=T!s–ïݾåÊb«4Ó€…PßòóÖúÔ³ž³¯¹® õN¥ÿÛêwå$ÈlÒŒC%(úʺ(±×Wǧ¦z¡ãž˜éȾw·ÙÝÚ–8'4,JÄÀ AÒ{j”ø¸×æx\¤|i–¬š73ã~‡f–lµ6IªN²ÑS‹¥™1·Ö {C¹ ¬®Ëg]Õ÷ØŸbVk4Ö-*!|ë®@ÓºšÚÆäã1,¦&Ã=Lb2Fdèɬ¤XÉJí:²»âÙßvE³d]tÏžyioTÔC‡Nž—ÑCúùÛžïsK0 iwÅ‹+…¯"ëv„'1™Óž¤$›dk‰;ü0HàÑ4‰  <õ#€œ Z:íŽØå~B=C‹DÓ;j””žG«žÀÄßÞç£N>ð!ªAJLoV|³ŠÖzèº9Y_$“½#Ôþ÷§ßk@à“¸O#‘¦~'2;³ïÙn÷†hvd Á49Á|PÓC‰{÷?%ë±Î=ãõ×›fdÜÚwÙ¼¦‰.â/%¹V1J6åf—:Lön¿(¾ËÍ„ñ~ý-é²¹·–fI§PÌ„ Ê %þVס5Òmd}Ýe¹…º\žohÏ1¥“³Þß½vˆ^À¶:{>9sü(³.êïÕú «ú™‘‰~{üÆ©Ezîý-R-’2AOÎíäÚÓòƪ²j[ÜÃ\¥mÒcº=1†¼H #ÿsù£±YýoÂ`ÍR,¾õ… Z$L=ÝûµIʼ4êQë íìϼÒiÚÝ^ktû÷j(oˆ‘ÓªÕ·J¡"¡ÑífO%©Ê Šnm:2”Vt—g#‰Y"NI©©x훯½rÇ—¾r÷ lÉúöçoŠŽGXàP%͈œ Ý¿É(A‚ BŒRq[³d•¶kâ.Ø÷ëwié<ë@¿ F¡)ý¨tÔ.š÷ð:ë*ØëœüÕ×;ê*6¾·l%Êc nšÈqSÆ¡¾(ôç=ýÞP?ë;däèŨZ¨ýßYýN´tVßë.Ñãˆ{2M$”¨Uå^3IŠ€j`bxl﬿jÌH§³½ñ¼—å™IJBæu;’ÅÂTBi¯hâ§L^)tpoÖò=e7”ÉïƒÌ˜Û÷ˆBwÔ:í{D&Éê{¤Ÿ×míÈ=qÐsI $…²2Õ¨þÀû;k>q9eë—½ùª‰s‰Œ™SÄ€û›¯—¶åC˳K^9˜:¿C‚ј>¡Ü܆¹ÿò*¹mÑerõ¬ñaYï¼{¹‘j–­“eÕ¹r¾OúXPcÉÕkÀ|†»Í´Húr§ìÛk^i«15§èzuV»õû#uŸ•™!ÜvœPU¬‡v)ïèyY03A/R;„ŽŒTÜtt½WæŽ~só÷ié$ yGié¥Ê};¶ý2jæœ>³lƒë_o»ÊÍZ7Îï3zB ‘£Ž1[ò˜ÌàÉ(éhaô·€éZqéžbId$. ú½Ð”(‚Õc^Ô  ¡I&‰çIÐrâà3¡ñvöb©ìX¿æŸÕÕ• Î4zÜ4‘£qê«By^×Aõ{Cýì¬/ë½pñƒ¡ö¿ÆGõ;‘¡»O¿»£ûÞ»CX]^s3-± {2HÚSÍ,ñ~‚ûY½w—£.àŸ.G›û9œR—Ô诤µWn&­)#ÖX¶[páÑ–à;ÑŒ6ècîùmpëlи ÷˜ŠA"] }z7Í1ÒÙcJÏ'»6¯ÿKe%B/EÞ\¢Qgö ðå÷Ç5ŒÒÛëvË¥¸–6£,¶·J Ï\‡¯}´]ù^\3kBØÖ½£%Nye_­¼±¿NÊl”ÃÑÕÈ ®c˜Ùþý—ÎÒ"Ykc=n¯yÅúëqgÏ)º.ÝnýÞ®°ïÛ'[úöé-wRëtÖ¿íÏÊWL“>HÜKÁ»öåm:²+à¨#êIÌÛË™„< úÚM+ÞÝ’Þ+ëYN^×goº<*T ?JJ¯õÇÉcJ°É0ÑŸƒÎên­RƒéPƒ…& Q—€€ïQÔ¨$ö´ä\…8£F‰sÖAƒ½ÀÇÍÔ(‘°Ù^p,êðÞÝËvmZ—‡ÛY³i†)’4Kž~×õd½³úô¥ûæPû¿£ûun`2ÐÿØ÷|¯? Ç¥?÷¶vË!áøCz·¹Ÿ[›¤QÝŒ’÷w¤ë¡÷ U®¢Æ½ûœ~WkõéÈk¬O¸ç’˜´ÆèŸ9H%à O9¦¬óɱý{_ݾö£¨—žO"m.ñFYþ­%®my‡Oɲõ{£ÔÙ Ùë£bTRxæ:|sõ'’“•.“FšaÒZ¤—öÖÊö³\Jšó"9j*>r–œùkÕs_yçâÅ‹R@qÐóJkìŒ9ÅW}:ºÝ¾ê©çrúõ•þýûÊŠ-ù| ¥ÖÛuyGåî%s%33½]èÈHÅEgÕ+™%N`\€mØj>xñ¹×—Üùi¤Ü”{Ε¸î¹vNt¸|˜øqRšMÓú-)&©Á<‰qJÛIð¼‰ÒÂvÝŸ‘TÿîP—Hc–HM‘hf.Æ4õH\ñÊóoN¿a}1ß@IDATbÁ…úyW~þÏ~1qÔ ×Ì #¢&R&*NÌü8•<4=äÚ¹À4zM <Ô' З{½ñ}ÚäHŸ ¨P¯›™÷„á|¥ŠÁœŽºÊ׬|5o립¸Uã±ÇÄ'¿Ä³»q8èdðÙï¨Åìq`øV?wqæ‚kî@ÿ§‡ÚÿÄy{÷;ñÉ÷è½îçp÷½zA„ýÓmõU-o¡ƒÆ‘¯{»Ò9¶#c*:¡Q*Π/Þ ñ¥qÌ}¸Ç”÷|BŸÇíëV¿°{óú¨çHžK¼QÖcs£éÓ¬Ù^ Õ5r0u:’AûÏŒ´7òX—ƒI“À0m”UÛ÷ɵs'©y“cº% éƒ#vy9ßîS‹ÄPÊ ‡ÇÉÝdþà8yòÙ³’çD˜Û.ášWZkzGÌ)­½ß×µŽh·¯÷Fê9ÝGH/sÖ…W>܆h€õb««—W?Ú‰`)ó%#µ]6F*j:¬^‘Ê,iÍeÌñ;ׯٶÇöÃs®]ºÄét,SŽäJKJpef¤D%ÆÅ…®–i@»7Açý;ÐÞÑ]?çý[Ÿto««s•–U¹˜ÀuTLÒÑ‚}¶¬xo£ÍVX°Â8¬hØóتYŠ4f©Y¿£¾£ÑdüíÞutÎ’¥óÁ ^ŽþO wÿ{÷³÷oÔ#`ðîkïßhèRðCÞ¿½7Ûsê©×ÖzŽ­ÞcÈû·õÞ@Ž}Í'Çö¬Ã|²¶¦¦ŠyfºÂ\H“»í½gŒ@‰ª*÷Ù¦½ÇUÔ;ú E°NŒÈ·ö“C²ð²±Í‚÷¸öG‹tçø¹=7^²“[f¶"­ýþÔ‡¸à¦™Hö¯u.±ûSž÷=V\ëwyßÓ¿u]Ú«ÝѦ`ß©ûh4°÷.//¯Ã ¨rhŸÿ`³Ü¿tž -lùæ¹Ö1iÌk«ý–hO)&Mð( ‹†Ó¿¬zãåwpüÑèIS‡=67)5-+!))=66®ÑØ7ôp8êjk«k*ª+ËKO©©¬(9qhÿC{wŸÄ{)ÀèjsIG +¢ßáÖ*!g ˜¥£§ …ÚÂÂäA[ç¸A*)ø±3eôÐþÊò‚šÚ@µHÛÀ0WL3a.6â‹ë©íöî˜aý{Ë×ÌT&'4L%åUòÏ÷Ý SrR#…½ÑÓ.¿#‘YÒ&Y\¤)"£¤µFš ®;”·ë¶£¸¦4 ÷êûð³ÇñE¼xpƒcjÈQ£Df©´á˜DñI&x¨Ž¿úw&b‹cx‰IŽ‘žØïh¶î†1?ð¿ºM/þþɧôqí»Ë\ÒAèŠÌ×x´JÛÏœgNœW3d´7‘ ªn˜Å Ž•¡zËI¬\o´É› íšÖœ¾HÝU‹Ô´¥æ—Á€o ŒØGn¿zÌðQ SQY¥üóƒ-r4LI Ô1'"‘Ybû4áO-ˆ&„­Ä4ÏÓ@Sù´`¯4OØkÝ»~§º-Í(i_/âFK‚©Y¢F‰LS$k•P=¦ß5&ÌÞ`ÀýÍvºã\Òx‹Èw¸™%„ð³Ädž%ÕâˆJĹëÆ:æ.—×Þ,‘=Åñ‹ú‰]}‘Áé®_$}Þì z*F Α[\&¯¯Þ®Ì2/–”Ë Ë¡aº~>"Óþö®>Žâz¿;õÞlIî½ã†Û¸Ñ B1%¡“˜’@- I0$ J衘Ž1Æ÷Þ«,÷ÞdK–euét÷ÿ¾9t’UNít'ÍÓoµmvæÍÛ½Ýùæ5‹ Õ—¼,±83IÏxrŸ`€š‚%¥eÀšýЀ©9%tW‘– Më(ʆ`‰àˆ`Iû.Qnﻦô.ñ€ø¼¯ Ft¥ã7Á“¬""^‘Åû#eÙ-þøˆ£¨3ŸEÐ"ùÉ-ýÃUD;áÈHÀ{$Чsõ;ÿ~ÙfÅÔÞ#'eÎò-2aDïaÒÇ9ñf°DÑòη뇜ƒhÚ¸%j–ô‚Í&M”‰^(#×AeD€Ä5žóvX,!sßKDa6š±àlâÒïý»kJïÐ[¡ ž6ÃcÞ@FÃ#xò °:䮾þrÓ 0i×2 É6½}ˆâ+’5|6u èÞ^2³reé¦]ª«›v’H„1 {SïºGúç+o"ôùÆçš­M"HÒ@I87e­º«Hµl4`Ò½¯Ïëë|i­y7÷Ý—îšáµ¾$ਗ਼Íá]R_÷ÄëëÑfx4Åcž% '¯çY3Ø%Ú*7ôò—È g¾C}ܬŒª—ÀÈó{HÓ–=‡Uá%v*ÀÔ¯k»ê/6%ª”€¯€%v‚o|=p¦Æ„àˆ‹Ö&i¤×8ÕdI¹Ö e£}Ì×À~˜ûîëwÑð_ 08Ë9ÔõñoϳF¶ BÉ{Îqâà‚=So=xNa÷ð7FÒï ®õ{„k}œe y±´f‰øˆy–ìL¼y¾BЋ)¾É¿î‹Î/ã+}0| 4¦&Œè'g‘€úÀ±TÅÆ÷˶HDh°tjݲ1Ùòù¶} ,iaë7?â$J å¶y¾)“ëwPË¥©öW÷ÏÜ÷¦z‡M¿ÊK€~‡e¨ç‹[?‹£5’ë­HÊ]r²mØ>§|Éùê7Jë2à¨ziyq 2<¨eâ¾ÏX%ߺ>÷aÔHÀK$À¾7Œ"ï}·\ï€/^üö+GH|l„—pé{lø"X*/e~ |èkPž}³_K ˜û^KÁ™Ë|FÔ¨–Pßi[b r¤íÎÇú -9h6Œ*€>”ÐõR¸ó5¾+¸æ‘@£I 0À_n¾t˜¼ûÝ2ÉÌΕ‚B›|:oµÜyÕ‰cl4C5•€öñ©éu¦¼‘€‘€‘€‘€%PhàûÚL yPæÍ©©>ñþ’ü`ŒZ6ß-ßÝ)×ô` ¥ÚQ€ŸE.îè½¹j×+s•‘€oH€ è&„'p"4}òãjÉG”LC5—€K5—™¹ÂHÀHÀHÀ£ÀL»eÇC½Ò`oœÔëåmö|%¹#éţ̘ƼVõ¡‘ytn¶Ü÷]¶ì<]$º8TÂ\­ÜÝïúÈöþòØ…u Y^ýqŸcSÒH iI >6R®;¸$ßÍò¾ZèÌÇÔ´zÚð½i fx /%Ó‚‘€‘€‘@#I Ç‹[–ô|iÛ/n%6„‚¾Eì¶—p¬„£ž/mýùŽÇú~YrÀl4[ `”.5ÃŒ"Ù~ª`É&ºFË%]äëÒ9ÆO§óâý$%Ë.S×äÉ÷{˜VäÙÑ¡’ŒkÚFYeRï yee®<0$XbB¬òÓíQ²ô`¡üe1Ó#VO¥¼%jyi:ž&IûŽJZF–œÉÌQ¹´X¦®’" èðP‰‹ “>]ÚJûVqå›oôýƒÇS%iïQ9‘-g²rš•–DOY¨ûÁû.̯TÝ}êܦ¥L¸¨ŸÌ.ÎÁ´ïèIa”¼QˆœgÈ} °ä¾¬LI###K`çïûòx£¦Áf/óZ2#‡Hv¡CüaƒòúUár6ß!Z#Akôòearð¬]’NÚ$.Ä"¿"§rì2mm®¬?a“uÇmÒ¦}OÎÏ–ô¼ºç›ïí`ø÷k“eþš­ -ïG®Xý3ÙŠ)'뇄9˜(ÌÆ„È7‹7"ätŒzžŒÜKŠM¹ê§%÷k!p.„¿ û¿`]²œEXl Pƒ¿ÕO,0ó´–‰íå~½¾\![¸‰§íEòÍ¢ *4øø¡}dì^âïïüÍ–ï_ä`¢ìt¦eÈÅÔ¦eŒti_¾¨Ù¯D,U"sØHÀHÀHÀ›$0ðß»[æ侸ã±~wºòÕëÅ­÷8¬Ö¢öy×õ¸Ù6¨è‰ ¶H?¤“E²è€Mµò—¶‘V¹óëLY}Ô¦4Jã;ÊÏz*°ÄvÂàÞt釙 \qÿD¶]ºÙ²îS#ª’ö‘f/ƒ)OBBöH˸ÍXï«5¯6Õ¹uÝ,¹¹=$+§¿|µ _®I’_\y‘Ò6y*Œ;A£¸Q‹ôÑœ•Ð"åJpP ÄE‡KHp  ¨[}iÊ…ìQn^dçÈ— ÖPn—Û®®îSEýf¦S™²cÿquú›%äîkF)mbEåͱ²0`©¬<Ìž‘€‘€‘€×I`ÐëÇB³rÏÆbˆÓóµäûKž#™i&Xò–×1mòI Xñ­:R(/,Í•mБœîÍ;Òtæ‘äÔ"i^êö¼š$j¡ Õ~Z„ÏkawRãgIpðÁú©¸šZÄÂÂ6«%/¯ƒ¤Ÿ™(Sgæ)ß—qC{¨•ÞójªªÕigRe;4iIÐpmB€?Iˆ‹” ˜ *•cXHZ´!ýl޼öé|¹~Ü`?¬OiA—­‰#Jjz–¤žÉ”¼üBRüΫ.?¿Š5R.—6û͆}꛽xŒŒŒê.¬Ìô [Ágˆép©änÑ €ÒO°K9?¨÷¼º·bj0ù×ê\ùÏê¼ D™Êp‚¤NðIÒÄíÔÜRð”gHZ4uZS«òÓêmòùOk%4$Iâ_÷P*Ï8ZBÂëŠæê™¾Š˜è<Ö7±NÖM³»y«¶Ê׋6J´I-c£ PªFØ’ñqQJ^|nøüTtûiêûæ]‡¤M|´ èÞ¡ÉÈ¢!:R:MÔµ›:ŒŒŒêEs~Ý-Çïû>ËÊúLM ýÎ~“]°^$k*©Nù;¿ú.KÚGùÉ¿ˆ’ß]*ÓÖåÊüý•ƒ¥ðuÚf“…wDÉÿ]V]æ<$Psªá£pRââ¼+¸%ù _Ï] Ÿ¦nêë¦i ”…\@ŸÍ['T ÿ$C5—åFù}8{EI´ÄòµtkŸ #t/9îD^Ö `ò;„ ÛÏj}>éw}öx%Ó†)Ÿ£ÚõšŠiéJh|”®ùä,9X$§¹\Ê=<'ÛeϹ™†Èx“>Ï”¨`«dæ—šëSÐP ``Ô·3ˆxG%jt¼‰ÈOlô,9qòD¦Û.—]ØWi-êêÃÄ~3â_~A,bÄ»œ<å£DM‰¡šK€r‹‰ •”´³êyºlxß ýÌðápÊi9€pìE¸_,X/÷\3RÒ¨y«Mÿ £Yjú÷ØôÐHÀH ©HÀ.ÏûùYÿnË?Jºä°¤ÛŠŠF–ì› #”@v9 T]Syv±»"«ê.h†çþ:Kë’TÔ;Os¨©¨É£ò-Ù¸Z‹ø!uü—t¿YWnnž¬Ü¶_ ÖM0‡šÞ™²å)?Fd¸õªîÑÕ„?Zº8y«f-Ý\¶"³W"–JDa6ŒŒŒ¼W=_Iîè°8r¶ÿ®ÏR$ÙÐdqä!0’ ¥åaÖF>&jW=%™ÙêÝVò—•S(ûÁ¯Íf«“9žS«dSZ¥}‡S$¡°ÃBƒÞP%@9ffç©çŠr®ˆÂCƒá¿4 äÔîC'd |Æ +–Ε‰9b$`$`$àuèä祽^Ú^â‰{Þ«[ÀèÏúÕé­ëu\†<-:t—.?‡÷‡ªó~H3ê¿§e×íiíJÒ¾#è»CåQj >Üm“yžÈgòþcRPXX¥æ¢ª:u¿YG^^¾ì*Ê“ÏUUÚ¥Îmâ傾]K*·*I¬’fCIÀ€%ó    ø€à ëåp}‡‘ÉX^Að¨ƒ`ý¤ßöÙä]0,6°ÊG¾bsA Aö†KbZ_]"Áeó½TÔŸújÏ[êq‚†"I;“%~Ö\€[ï¾WäÏÏš#iYˆ^G°T»Pâºß¬ƒ&xé™9âg±š„³õô`2“ŸÕO=WÕݣуzHkDÄ#1gÓìeޭݬ'Õ¨–j$.SØHÀHÀH ñ$°ãѾß!"Þ¿@¿ 1iø€ÕϯӎÇú>ßx™–½]!È­ ùâï𮀮r#oþà1,°ùÅœ¢‰³gà3bõËp‹×n“Ï D®cô>ò^™™WUÐýfyùù’‰ÀVû¦D#q®%B­7YíÏUu÷ˆA:®u¾ø#ŠißÑ“²qç¡ÆbÛ+Ûm~o&¯¼ †)###ª%ÐïÅ=ñù–ÜY(õN€=ô“MušWõæls•€«)[lX€ìKË•¸ÂI lë•"!o ¯—Ó!§¡W2ÚLÙýZ0£oƒV%¿Z¨ÿ*­–%›Kä}¨)é~,åçH!"âQâ)z`Ò8‰UÍeøí>|R~\µ­Zà‡È£Ò©M ÙƒòUŸãQˆ8w&3[N¥Ÿ­ªhƒ£<ù\¹sbiìà^Jdhþš$éܦ¥D…‡4¾TqÓ‚ñ¾$yë‘€‘€‘@ $°ù±.§¬â÷{\2(Ï–•Üó¥-õzië%Ï:æ=^96õ¢lr¨Á‘!0DZHBá¯í:ycn˜¸Èp]êsåµ ×c4EÓæh\û9òàA›xÕ„×~³Fë ફ¼˜õYKeMÒ~9¿géÑ!±Ú*;·¨è]m9o)àú|Uw÷î$[µP¬ÀÆ{öRcÝ­ï£Ñ,iI˜µ‘€‘€‘€Kƒ`ަ–paBÚãi9W‰Ã>ùã—¶ý«Ï?“îDž%äÁ‹ïŸ'X+J˜ Gˆy˜5ù©ub˜UŠ2S$Ê–*þÎÁ'øq§ òÍRÇø°~©`Ð}q§_.£ÍѪÈz[õ ¼:¯ÊøÖýæõ̳ÔXYÑ;Îè{ÇOQ µöíÚN®Hl-— ë# ±‘rúl¶lÙuXÖ%ï—¶ñ±rò…"xƒÐLí=rJ~X±EZDG󵊋†à^´~G‰æ) Ú«»®)‘a!’|àrI!ì:€ˆ§Hß'wM%¯Bt¼7¾\¤xd&ö{p¯Nžb×kÛ13’^{k cFFFK ?Ýßbu8ü1/í¥‰læ'Z Ó¼Àø2¥(ߣޭãdÇþã (µ,,’C'ÒTÙ­âà×*ŸÌ]­#h†øü’v:.k¶íSÛýºµ—ÎmdÛÞ£jßÿ(U-kwÛëß½=x<"§)_²ÙHVû‹ Ãݽ¼I–3`©IÞVÓ)##¦&þ/ím %ÒxA¿ô›Õ¿î¦¼†-ôb2d$P,FÀòØ H ”    ‰ Í•ìÈ?&}²×Jrèùb·” Õí)!R»E Ô¢ð˜´°JBtX1ŸNpGþÙæBN à[½­Í ¼|õ ¾±úOÿ¸³F³T’]h¸H]Û%ÈEºIld¸¤g8µ*þÃ]QC:žš^Ñi9‘Zá0fÅç*,Ü@k#ß+.ê/o¯@–ÀÚ±>Û4‡Þ_­KÞ ‡FFF²å÷]~©yOï™ç Z h3<‚¥`€¤à`$: F.›`‰-Ä,~@ÊùY™²;¤ŸÇ}˜è£DÓ;j”CÒ.&DñG>É/ù¦f©9˜áU{3+(×Wîœ0[)*Ê—ô¬C²jÛTÙ¶ïË J»w¨câEr,m3üTJÍÇܻҷKí‚ÖhѺäs:qÉ}žRåýï–KhH ÚöÆ:¼,qZÉujÉuÛeémçþ÷ÿž]‡~ù"x2ÏGSx Mª”Àb—³¿|ê¯ ]v|S¿+ðž8‡ë¹ï¼ô·õhÔŽÅß ./omÀ –¬ð·€f)8@$Lå±)(,TQÇœQ±r%$?WÒ 1à…†S“…$ùÖ`)²ÔÏÐÀÏa“ ;H8˧ˆ–uÑþÒ2Ä p¬x‹ˆ—°ð0µM~É7#ùiŸo•s}ñÅlM±lûtÖ~IIÛ&'Ó“¥×¥Gû+dëÞÏa‚,c=£öyS“Î’…ë§Àœª@ºµ»Dúu¹Y6íþPÆù‹ì;¶XâczBæárËø¥ vú—î+¯5ïµáßõZå®T_­c=ÌI´rë¹ oeŠ·!ù Š–§«Ýu(EèÓóÐM— ÞIùôÇÕòíÒ rÅ…ýä¾ëÇ(ÿ«¯nð*°¤e­ûàîš/®ÞOþ7g…ºdå–=ø 7ϸpõóFtWò—ã ˜Ò÷=úÚЃÏ{êø‡s?/KGDh°=:2ÔÐ<ïPÅ2sûh^a¡ýÌÙ²c#x–㯓ÿøÜ [aÑ«~§ÿyãgsQBÞLæùðæ»cx«W ,vÉ]ؽC«‘õZy5•¹¾+¬þ~Ợ°Àö¯ÔäCS¿ýöí\®S55™Ó-š°1Àý•¨±)Pr ñì4ÕË$-Ê‘\h™rí~bsˆÍž…Á;_»u#§»C8Èð·Ø%<À!!efG A\TT„DFb!`Ÿä—|<÷eO³<’6¡5àqéÝéY±åßêøEýARÒûÉ÷²;0€EÏN­Tp &ö³zû±TÞõ¹ÕØ`I$t(à–‡½42:f"à¶êݹcÂ-öíÚÆäIS”ú”­·Ô¥@fÌ0¶î9Š/û¶í9òwk‚õ×wÿáO÷¾ý¿Î£y#™çÃïŠá©Á$ðæÔR›÷ßÝvyƒµSIŽ+^hÕ¯ó¯oëò‡É¼òpGÞú¾¨¤[Íïp‰v àƒQå9xÓ³÷N 3=ãåçHÙ$!“#d´4*YýÈŒ|°=g›þ0±óÇ 3À(D¨Q"PŠŽŠRÛPŒ†×œ´Ju‘òÈ~B–QÒºÅ9‘¶UöY ªëÛåÙ~à[Y“ü¦Úê mÒ$–x€š§Eë_@™oÔyÜuµ>ž¶¥Nfxª’&ôOžÊº”Wì«äz>š"Ò[vLQÁð¿UgT‹¦ØÙJúÔX`Ik Ø~àm¿{â×Á!amí¸éÒaÒ­}bݧµ*éps= Ð)ÃÎëÌŲûÐ †¹L€­íw÷>õ—ÇßzþϯB.|czË”y>šëƒjúÝè¨à]ÔáøúÎÇŸyêÝÿ{Žï &#ñ¦÷E£ËÌР(È(Ž0ç«ÚúÑÔòr‘OFù20Ù‹X‚U‡êNmL«´´ #Ú‘?D#Pb›N­|”`zG’2Ã+Ñ*™ÀZ†Õ­-0‡:xb¹ü´öY€¥-ª8ýB‚¢åLó4Lß&à×Äsšös+½oÖFUI ¡Ñ‡ö餴m,ײETëªÊ7Ås–ô@˜ž¤A?ä÷(ý‚·Oa 0Ф†~ÐFåw^å÷þ¬edg~éž'Ÿµü÷…g_A»Þ02ÏGC?¦~#7%Pî]ñ÷;ÿ´åݧ0ÑþÄÞnö¤y#P!H!i-(¨â˜@ËXÊcà€%´K*AhIΛºÉL·GMMëœZ%gt>šÜÑ /44D-4¿ãyòkÈ= ,Ùü¢òYr-M¿¤ÌìˆÌÖ¥äpLDge¢Çsšl6ø2¨.Ð]6ï9"90CÄï5ìÞßþöò·^yå5¨Â§‹6XâÛ@)øÚ»&_ùg¥{¯»˜ƒdC’A©’ù—BÀô;{*ù½—ž×f6â¢ÂfÌóQ¡XÌA#Æ‘@¹wÅ”›üíÎO¦¾2Ü09ó‡5Ój5Ѐ…@DmCµÄm‚¥Ð”òaNTP_%ø# °GS–9ò¸Š §ázõUÔW ó|Ô—$M=F ¾+n¹ü+Þ‰}ÇŒ|`é%4£Mñ<ý¾h€6í* H”v ~D ¸ÀO$˜ÝÑ™]ÐÑ¿ê –(Mg›N?)&†"fÛj JóÔ´%_¿½c0‡¿оÒJ™¨ö«%“•ýš Oƒ:èÀú×XŒ†G l³•–w-ãmÛO¤Ivn©iayþÎï٠φVu-Íã¬Íž/;ÎF$ºTg•zê¶«’;UÚ|k¼Ë€U«·íSZŽ úv–˜u†‡É;t"UFì¡õí[µ€éØæ2"ÂÄÏõGÈôY¼q‡ û}aß®âã}:·A{eÃŽƒ%×Ü8~ˆOÍî+BRÚWmC~ªHé×­­D†# -¢ Ï[½fj=¥#Bmï?–*‹7ìëÇ–…HT{ôTº ìÑAâc#ålV®K+Îì‘A½: ÜPdßÑS˜ž.o9°»ti“ Áþ´fÖyr×Õ#åPÊiéÑ!Qª {^ÂtmpÂ#7¿ ï`üºã‡O¸ê.€%ú¼7iâÕäª5yùÕCðmðààkO°cÚ(/Þ |nüåÃCqŽ@OÞó|”¿!fßHÀK%à|WHü7ÝŽ÷¸²ðôûÂK%ã;l¬8“Ó4N| ?œÚŸˆëÚ.®õèºi‚G­’Ñ&9Ÿ“Sé;dþº¿ÈŒ¯F@³ó¬Ú˜ÊÌ»‚ 'î~Дvi¶uòÙ¼µrQÿn*!ì¡§eÙ†]ç%Ö†ðéÉòݲÍ2nHÕd€Rß®måÓ¹«Ë%žd[<ÿά¥ˆ6Д ÚerZ&j¥I_ÿ®íTDÉ_,TÜ߇¤µ}º´Qõ÷íÖy­RT=œ¤àdŘÁ½ä£¹«dÓ®Cð! RåZ·Œ‘v‰qòîwKåÈ©Ó*.O´jÈ•…2ãó…ª\cþ;}&Y¬ôÛgŸ}–qš4yJ³¤ÃhHË6mGãeí`¥&-]êîg¡—€mfNãŒqéTGÃöÅ< +_S»‘@½I@¿+¢[´J×b¡-Œ'ßõÖS‘SFÓã™'&nÉ¿“M»ÿmÃzhôðÃA˜*â’œXža¤Ž­8AHpë4ë¬í³£¯s®i² “[톧Ò3•¯Oò…t°NWê 2š¦´Œ,Yp“–DÌ(Ë%Ú( Ò.$—MÏÌ–¸èp¹ùÒ Ô±·¾^¬ÖHµ¢@Ðhw\u0%-C@‹DêØ¦%@Ö~UæàñTr¾G0„»&Ž5Iû% ¹Å¨A¢Ï)16 I€sPO®ZÈ;©€RLò~1a¸êGÊiº†:iMÒ>¥ÖûÕ­™c- L˺ºòîž?›™“¹ÛÏß¿®ií×"á.¬§»{½/–óX¢Kû+µ‰ r ŸGÙ'Ú%ØDxfn•ˆÐ`{~^^‡â{Å{FsëOÇ ú ÒÉÁ“ï‹zè…©ÂHÀs ióžeëÞÏ%¿0³¤a=¦Žh/~mäT†o ‰ŠŠ"%8CºR*PJÏœ»UQÙ €¯³Eœs©9"0Iy²Á¯†Ú ÒæÝ‡ÕÂíŽ0Ë‹ E€?eB—MÐ Iûâ0hÄÔ™?©cú_B\”ì:xB⢡1r*V\Û=“™£Lú¦®„RNŸ•Qˆ"·uï¤t}4ç‹€"Ô4…;ó`ëÖ!A>ü~….Z²¶UÐÏ’“lØQ^×ËÓɽ‚˪=ä€éîÑC‡Þiß¹óóÎzåñ… ¾9f̘ÚÝÀj[lüüÀy‚ØŸ† ¿ÿøè¨0ßx+xB:^ÒFtd¨Õbµ´;¼Wžz6Ø{ó|P †Œ|D|[ýýÀ.ßéž~_øˆ” ›ÍYÔ"}ðÃÏä¿ß]*ëv¼S(Y­Ò«ÃUrãØ÷ä—}){ÃŒ+šƒ`¯ù#Ÿaõ;×`S„æÔ†Ôí@´¹ à‡4qÔÀsª;y:S®¹ø|¹é’a²tã®sÎWt >:Rî½öbl7ÌéÊÓÒ;¥3´K÷ß0VZD…˖݇T‘­jçÁo Ö®”(”k·ïWuނ֋-Ò‘“§•Ô=׌‚Vj¤òkr½ÎÝmÊ‘òŒ„æ¬!è³·ßú®ÇX7ÖW$í¸®!Úñ–:ë÷i¯¸WE\ñÙ^ ²y‡4i°ÄBl´}‰xO%<ó^éûV?o®Ê¡ÛiÏ¿t¥©@m¨‰#ýͧ¾fˆjÃCC\“’ÆÜIlÕÕ»]'·tÎíÙ‘sçÒü5Û¥x»¶#Nh¾ä|WXù®à;Ý“ï‹æ+tÓsŸ@eZ$Í|ld'éßíé×ù&Œ¢$‰3³²a~#+·Â~ +@_ë kòçpX$1&¬Îæ]üŽé%6,@ö¥å*Ó8w¾Ì^Q"Žß-SÛ TòüÛ³ÔöîC)²÷'”£0‘£?‰mó»CZ²a§ZWöoÃŽÂkuynëvyMVNž¼ýÍå“§#JòøöýÇÔÂm’k;Ü&XÓu:K :!Hhß>F©$é~é2Õ­ibÈ®ñ¹jˆñ™3glÙo‡GG?C^„ͬŽ/_=ï °DÙPsÀjñ€¿4/¢§§~XýNÕ8´+Ò.!VÍ:´ÅštèxšzPŸºg¢´‡=©¦?MÿÑPÚË ˆ”Bâ@öÓ¹kd;BUž>›-±˜]`Ä–kFŸ/_!~þÜ•[U9š±pÀ5éÒ¡ÊöUô–Ž`ëiÍ’×>õqkΜ͑áȹ}ß1õœ„@å>¼_W¹vÌù*ßÈ_^ÿðxù9ì”5„úþOÓ¿”'‡ u˜|þÓ:eàçgÁ °…Üsí(‰ŠÕÅÕšÏòÞQï 2Ç+Ú¡™BòþãÊ©µ¢ó ylÇãÊ‚޳¤YK6*çØÉ×­·fõï–2 WDhˆôîÜZn»ò"µ_QCI¸?ÿ…ú«ÝªîKù2óVoSQ–š;XrÊE½ÇõïÖ“ï‹ò·Åì 4ªÊû"•g†Z¤í.—ÝŽwwé;žƒj âá'hÅ »¿Wƒ%òG¥R\d ¥`§|Ÿ«Û×ýf‘!¢€· û–ª«Ÿç]AKEå˃”ŠÊ¸s§|umºÖÇíÊê¬i=å륃q£ø\iY—/S×ýùsfxí­¿x}ˆ01eêž~ðW›êZ¯7^ï °¤5\«+„êU`‰7†TÆí-§3²%e‡Lùï,ùäcë–nÝ7>ðo}µÎ~iÌE‚h&±r"õL™k ľéFröÒMj€ø«ëÆ”)ÓØ;è‡ëýâ¶sÚ¥ásmÏkŸºtŸÏÆ¿>þù.ìòÀ¤±°yŽRQq>ž³Jò`+}’Ñ1„(CÞz:­1ti"Tq€Ï«$|òî«ð1ÉW3\åRMyLÚ{L¾^´Þã`‰ mÆç ä‰;¯’Ä §&é®kF6Ø“vïÏ.–ø`ì=’ŠhI«¥g§Öj£"y17ÇyV ”**ßœ¿+ø›uý 7ôû¢9‹ÜôÝË$P-RH°Ó±ßµ zËw>“÷ölÛB6î³ ]‡ƒƒºõŠmò•›ÛUºÅ‡ ’’*žÉ»î‡»Lêòºßº®øP?9šY€üHÞe™óíâ°q`p·YŽòËË/ãk}ÜáÝòå™WÝ0é]¿€j•0X,âúw®õµ2žK”‰ëÇÔ+g #‚U”F*AÎ!yî­oä‹ùëåÑÛ.wëžîEˆÈÍ»«&zÐï3/Ð,¹“y±~.9ð2Ì%âÕ«±k9/ØÖ÷ËS¬èö¸öÊç£.‚ظólÏÈc·_Qb¾5´Og¡C(5Ž´©&Xúz/GµÖbÓ΃ˆ¨ÓN½ìhFM”ޤÓ¹ª#>‹ü™ âÇ•Ûþ4PFMù¼~4g¥2xúµÏUxÓ[¯¸?C9–NIS9!n¹üBe‡Í¶VlÞ-Ë`2@›êp„æoˆ~Èm‚s» qé)Ÿþ¸Zµ÷³±ƒäýYË‘¥ü¸Òâ0ÑuÈ9A Ï>ùIÄW>š‹wV™òÐ *\+ó½üâJç¬+óg|¿ld”+Z·€ÆíBi‰¼žÏ@{vÃø¡2gÅfI‡wPÏŽr 4t–s[P#̉ùk’d?úN/åSžïîZÉLôá¯÷_§ä¾½ÍY9å« ˜Ÿ•̼~»xƒlÜqHrð;eûÉã¸Ï¡!¨glÄ=dˆØ±CzË¥œW{¾~Ìõ·ëë}1ü T+-ü‚bb6EÊ—ðEÚ{NùÊ´Hç,>@­Š3I¯ŸôîÐR’Ÿ’Óé%1ñu$)u¨ìZO·;_~é©Þkšoö¡¦äÚo¾#V¾uD€œÌ.R“Ö ’€Ï…WPr#ù á3 ùåH0Ôt|žêrÜésÊÑ”·Útj÷'ÏÐô­Ï¿÷ÞãOÝqGš;×úR™š?áµïÏ|T9Û13Ì—µwݶ÷(B@î+YòZcú“¨p‡´ƒÍ ¾¼ãå3ÏGMï×1$¤ãÇ@ƒ }}oh88ø?–š.­ZF+Ð&‘’tÖ‘4Íàa2<š{ºC……Ê$tÕÖ} \Ðô“`†Ú¬¸¨0eö|w^=BÆ é¥ªü`ör$÷ Gn½LÚ ßÃÇ?¬TÇ îÿpué…çÉ߸^©9)pÑ€îÐxœï˜ƒzuäáQ~UíÑÞäëG˺¡SióͼCú8ûô3˜ ²m“îeæ8?H ¯ú4i]Ú&Àdn¸šÑ#À"˜"1üëÿæ,àé¢ÌeióMàSð¬ƒ#í§/y˜qÚ·‹*Zß<Æú™Éžô!ìâù%L…©î „ŠÕÄ:×'”Ç¡¾ÚA^w74dÔø-Ù°[¶î9оR`øËùëäTúY}iSZëßmSê“é‹‘À9¸í>ÿžø»õ‰ßüÕþIb«y­R(ÑiÌ §äáë×É5£¦–1·;§²âZÃÂ-Á«ìØïÔxIKó.yòC¾ºµÖ+@Ïä]÷£²~–?®Ëë~³.æá „ÙX›0¾qLÅ®å¯5ûUKà4"ùqŒ0°c¬zžø\ÕæUÝJéÙé/=¿:¥EêˆÃìÈ̾»ôlÓÙò”f©œÄ”©W¹cÞµ‹0Úx1)Íælj°§÷sóóõ¦œÄ **ú±ŒÖm@2f³¤åÐx%ÃOð‚b¤ºü›»b‹Ú£vˆ%f^×TžoW@CÀÊýßüü2ð¡W§Ve’2Œl[ô‹¿û^Ú¨*iþ@Ú¶ç°Ò°%ÄEÀì2Bõ“f•—]ØW7ÿŒŒ¼_¯¿Þ:4Õž2Éa±O¶XŠ.(ÏqMµHå¯ç>µí ÚC°À¾íZFI*4»OöI½Iââ¾lT 5JJÙ9}¤m„Uà?pC^U¢aðÎ>Ô”Ê÷ÛYgÄ„æJvA¾œÂg1ù“b£#¼FÃTÓ>z²¼Ò(á[KsAšIò9ªë=ª”ÄÅÀ¹’®woaî ËÛ-rV/r»)‘'ÀRÍE^ á ˜HÑ ßÕ´‡&yåY4+ŒFÿÓàÂ]§]‚”6ñN@ÅúÚ%8ùOƒ?!’¢+ê ¥‰ù0‰«Œ~}Ë¥êwË +/¿?GiË~yÝè’â®|—Ä#©}1¸£,âðáÖÔ À‘&z4udN þž J‹ðåØuèú~­º8ž-Ùn¢Mþ·ÜDï›éV9 üýõÀ¾v‹í—iŽã·Ã0<ªüƒ]X‘à7.èáÿb©È©\uUîjs4à  ÆÄZ÷ÖÑRˆ÷ôô>Ð /PôwâÃD¥4˜¶”ÄP‡´‹ ‘ðGÉ+y¦Ö¢.fx®ýfݹ¹ÁZK‚IÃ'Ò–vFMìùZtá*oz=Ÿä$ýí©‘ë¨žŸú¸G± ë "ô帞³‡‡|韛ôÕç…éÓ>yÿý]Ëøú¶'ÀeÄwëâÕrã új˜ umï6ŸmZÆ*Õ'£|Uv˜Ñ¸î€I’w“Òêè{å)Vu{zí©v=ÒN[ üÔ ü³± Iê8»ÖZRk˜½%"øÃ†äý²÷ðI¹žòDs½»‘áÿž©‚<Ч:¢&È r–ûÍ­ÅËõºº¤À,Í_@º”û}0¹ž¦ù«·ô¥ÉsÜ€j€¼â.ÅÇFd,)®3˜GbBB“»}Ò幎…&Ú5‚WråÛõx«â0æ©0khíß Ôj:¿g{™³l3îë1È"æxý¨õ°åLñèA=…¾[M–œzôo–kC>*N’pÑ亭ÕtÍ MÚìJï{ãšZ¤4I¹sã“‹¤ðB5oîʨE r2eÁ–µ–ùá·]Þ±SÇ1J¥t-[ƒmʆ€#æ¾ aa¡’ô]#%ÐzFö§·”'ïÁäíLxmF™ønœ;ùVƒ&«,ÊÀoç7ç¥ÊW•­ÀòÞâÙþ_ÏŸS€ž9'Üš=837ßœyÔ8×…œ ÁŠw04K¨+<,Lò1ø¥ÿdkh Âü3åÈ™|9ß ¦i]!;ÀBŽøùg`]ÚìŽ@)²E!)l(ž 8€¤Xÿib8 V|ED„KXx˜Ú&¯ä™Qü؇šRUý¶¡ßÎÐÙ¹’Ÿ'é…VÉ,pH䦬Å@Èq¬›Ù‘h`÷ÉŽû„(ŠxZÙ¥mtDEFH}Þ£ò²åóÎç¾ 7÷Îñ Qò–ðós¼Û¦ÀŸ[gΜùø¤I“ŠÊ×á«ûîM9ûjïjÀ7#}­ÚºŽïð¥ˆS9lj2°áŸNñÿû~¥¼ñÅ"¥Ià@ëòáý XªÁ}hªE&æã¹x6¾\ |áèDÍÃÕð'r¥óo6´ô¿áGˆÄCÒž#BŸ9ªÙÑᆗ1 u­ÃÝm‚¸e›vË“ÿù ÑðÚ¬n¼ ÈÀdÌ×D"°ésßuJ«¢DðYç‹Ú§?Üye…Í]Œ¨{ï~»LyñC ZÈø¡}ä$ìÏI4Çcß-Ž&…/ÿö–2upPu#2«Ï]¹E…úfTÉ{6Zi¨8À«)ÑODÀ:,´¯“;õ0Gs§­Ý¾O· ¤öïâµ”M^¾Mùu‘/é“tõÅUî5Ëx¾VXD°´|Ôø5Ì=RÿãóŽwˆåȾ=kqHƒ%õÒìÓöàYìŠ3‰»ÓÒ¨m˜ëz½/o°„»7åA¼3«  g<}ç9%þzÙëhæÃ'?H î:ãDž‡ßJ@IDATsœ&m’sŽtÌW DÂ/I'\ed9‚¥ŠˆúòÏmµ(á‘áª9[[¹>ËŒJÇEMÃ\ëŽÀ䉻®Taìµv‡`€a³ùü’´ÏÞûP-4¿cû Úðç_©$»åÛáuÔ¦>‡ßµb:ñ,kºé²aÅ¿|¡q¹ï†1ú”ZJPÓK™hÞx‚>×>ðØ“ÆquUö»Õ+â»ü1jþ†÷ë¦>¼®r§†éÅ÷æ GÖp$î¬~ó̓ŠO Ýý7ŽS “² ÀCìtûfm$àI pàMÄx´½Ÿý´Q(‘º"d´ŒkXó®Ó.$4ývIrÊ%ËÍ—^?Ggj„º ¸k"CF´ëÚ×>)À?õf¼N+Ô"áóV€=ìÇ&g¦±zSõÀ#ð¢­kWî:µIû"–*±óJ7‰ï5"F…£yß÷$' €™ž?£ÅåI$¾6¼œZAjŠ :‹×ú?y`[Îöüabçw7ÍC”¶‚@):*Jm@‘×Új•4“îö;Ú¥Bô;ýŽÀDû^Q¿©mٲ눜ÎÌÖM(sñ¶!•3K úÀFé=‚©7€ª'î‘«Xð¼;vû™uKæ'ã8 úwá|-–™¸1OñÜŠ›±2`‰Â0T±ø@»¥ŠK™£ÍU•¥êäÁÁ¶ë€½ºòˆ ’\ë``憢ÿóñ9O„éiUTPÒåÝñ;rJú:O¯éWV^îÔ¬ñ8#ì¥ãÃLð¸áËéŸÆû¤‰ƒ 'CF%*9¸¤iSAA,D@’9+¶a’"Eã=8€>6að·á §ÏL”7¾Ê—‰#ûcR¤~Î |§Ô?HL̆˜gÿmù ¨¨?ë//à‹ä¢EZè<›Yž ¢&”°†~Cl‡öìž‹dª“vÜØUã\þBw÷5( ‚9œá³I}‚hî„€ ¹Ð.)Ÿ‡"»Só¤S*TÜi[Ëœï3òà‡|„³=§V >J0½£FI™x•h•jØ¡ú‹Ë›­Yµ}÷\;JùÑï+çîø(3•!#o‘€Jœ•ÏPZßǹ«’%,4©ÑBR”Ä„×U8êoa!KX0~ sଆu•á©ô²iÏGR(ŸJ|Bn;ÔÇ¥„Dv޼è{×E‹TrÞeƒ,ráàƒDªÝ –ÿíí»t½ôãVE>q×D+5äu%öŸr QÎ}gHñ øå,å1B±v‰¡2-KMxÑmqr§TcáŒÌG“;šx…"@šßé{U“6ª*[×~ó›´ )'rlv uúÓ2ÐV­ªjÖ§Î5æ=bŠ <çv„ÁËX:ëë¯!8þ4`Ò¿yâ¾û6?÷Ú´]8ÐKË)Ó§E¹U>%èJ˜5`©Á˜ÃFF¥ ‰é¤K‡•0[ÊaÝÝ`F\F!¤9°æ{Ë®Còãj'PjÙâÓÆ`§¤M«ÑÎÈò}·£ªèp†ƒpjRjK6[ž$œ%›výOަn8·h‘Šl2Ëa÷›þç‡ló+Ñ"{ÓO%:tåçädfmX¾è}ëÅãB`'ǽ×]\gµ˜Sj€‰ÛÌ— ¿ÞGüÀWAHpK5µÈ£æŠ¤Û…¦Liù¨™a› Níƒ9$ñxChuûµéw4¦ ×&K lXx¸êOØéÝѽ@>ê/þç ÷ˆÏ÷1äZܲzùôÌL„‡ÅóE¦²š#‹åkh•wŠÔ2k–œÂ0ÿŒŒŒŒŒ¼IÚôŽ¥Œ³Yò-r¸ÑôŽIN½…ÈKaJK•¿­ £qÂ?Fœk£Ö"mÛûr®cB'¶ÂÈüÓ©™¯mYéÿι1)^šÀ½†´ˆŒÛÍës7.[¼1¦eJôsY¶q·d!”}”¨Õñ"/±Ñ³T¡Eð¥ºô‚>%¦x VEÕi‘¬ÖéÙþ Ùu ›ìܰóýW^xã»ôªê¬âœ6ãð8Xd:#-øê³€Wˆ ®>ž–î¸ù² ¬õåÃDЗH&’T %g‚bR]À¯'¢È –Ô‚}¶ëÜw~¨îž°žº’“êûƒçz.ÒKäÚ%f‚¤‹eµ_·vueÁë®×r÷ä=¢Mï¨QÚ¿#iæO_|ÂIj•øìó7ÀßÁ%Õ¡ÃòŒýsð@†â5ÔïﯿÞþ‰É“•ðÑ –|ôƶKt,®©¹yM@^'wó+ø[eÔù=«‹¦]#Ÿ–€S«äôSÊFÞ¤u0ÁcÔ;ú y‘'ò¶b«¿\|~75@'@à ½"ªN‹ÙIúw»Uúuž„ý1òÏ? š}UU“c,iÍ iª¢m/øú³ÒRNœ 0ЯCûÖ­‘v"€ÊÇñã§N¦c¤ß”úé龨Šlyù¹¹gs³³ÒíÚ¹}ç– ûÀµG”+5JKMܦ žöW*£UÂqE0ÞœÏ:çŽÃ1¢ø°O¯ÊŽf|°+}:·Q3Ô_!äϽ' )ùþ¬å¤Wjåþ°g½nì`•(s3ò¢,†4#Zq6›/g†HfÒÐVlÅìv\ÕÐâÄžœŸùã•c†eÇ"Ü©ÖhmÝ}D~DGNž–v ±rÅEý„¡+ŸÇõㆠ¾-r ÄA=;Ê-—_ ‡Ž§ÉÌykäpÊiIˆ”áý»Êh It¬ûI7Á|‚3Czw’ÆQ3ñ•ñàƒ·Ï°ì£à³:¨WGéØº…<ýÚç*ß»Âß“Ä2´öéŒ,hŠÒTV»<LÝ0~¨ú]nB~ŒÝO¨:Cpm‹è•ضü5fßHÀH j ÐD‹`‰=-#+¹srð;ãØÇ;‰¼‘Gæ,ËÍÉ’½Ç¾GT»ÏäDúÆsÖ¾H ¬E:§Ýâ a›3ë(¹‚(ç¬mЮ­·c!B%°Ò³>ú2T têÖ#æ“'?B T|]ѲùóÞ™ûÕW›jR)[Fìð¹æB$ú"ñ9&(ÒfwÔ((U«UBùÿL~nêt^‰º¿8}züïï¿ÿ$Ïù*ùÀm,Úñg@J‹óé«å‰»©°,ÖG9AÒô§ L„’ö…§[I¡«/(Ýá3aƒã:#€P+D¿Šð° ùç?È‰Ô Il…ìŇi¥-Õ#ÔÂc0#~ é fÑYïøa½Õ>‰ jÄÀîj;ißDç9®ÀRtd(²—÷B²ÀBå˜8kñFÙ±ÿ„œó£ÕÛöb–½“L/®T®eͶ‘@CI`+À=5®k’ö s0$ñ·ãJ½:µ.š;¥7ÕºØgµÌ1K&&#   ÔLüæp¡f‰‘Ó ¯ѬZÍx7‘ǬðéÈ–ŒLøõ´X¤W‡+°¡A}‘j*=¸ä Ñu›I¾¸”J‚?`»L(ék±Y Yd5J)°ä°°T‰”úh•;×›2ÕJ€Ï¯^ø s! "0âó¬×Üæ¹*5J8_J î«Vþ€Ò¾¹åóš%-v’‹õ’¯á»ÔÉí4Í_½]ŸH“ç¸Aù)½ôþ}ªdÀwY)ùY´y±Ë1†î„IßhÄñÿÙØA¥'Š·è»ñÔÝWHí“¿_)1ËBáÿä,—Í”¦“F­Á+?ôWêÕ©•üþŽ ˜IÏ“Çÿõ©*F(&¶;…ˆc®äW ®eͶ‘@CIàïORUO„F–¯A­!ºh@wáR¹žwÝfùÛ®^rµº}¡ÁeD/=áPrÒl  T*­Yâ÷…¿;4LÎáJ¥—xÕ‰àÀH¹|ØËҷljÃ7»ìĦW1ëdFϲs­™Wi¤’T”Îäzag¼‰¥Ûg÷>}ÿ‹L\1_9‡î¿ý“7ß\éM|ú8/úõ Ÿc ˜4hÒûú¼ÛÝ ” µùÊ’ÉÑÇí ½´`“K”ïÄQý‘ôr¯l‰ÐˆN3»ô³Ù˜™…o„Uù'QÃÔ¯[ûZÝú'­NÚ‹€­¥+ÖižGP…—úæÝ‡¤g‡VªîØÈ-*Û¶n„çöê(g¡å¢Vé"˜äQƒt¦FÔvQsõ㪭º¸ZÓ|iCòAéÓ¥­òm¢ !sÔTƃñY*#>³ã 0ðHC'¸20¨™J†(:OÍjh¼Ò‘¡m¤U‹þÊ”P[47n·Ì§žu×>|yqÑÚ$ý²Ôk·+oŽ÷ì³ýb[ÄÏ„`P·&óÌéÔëÞ|ñE£QªßBƒ%®õB`¤}¬Æ­>þà]'þöÚ4FЃv@ÿùÖ[±¿»÷ÞÓ5®ÈK.hR`‰®}>ÌîJ'.ÜSÞýv™<òâ‡Ò¡U ?´œLϬ•øŽæA¯~ô£z™‡Ê}7ŒU>ß#|ù;ß,Q9f¨Ùí’P“ûÿþøGD&*T¾UŒÈÇ€ Þ0wå6ù~Ùhź«Àš±I0+üïW‹å_h‹tAß. ,UƃKZrfm$`$`$м%P˜¨bò!R#3ðìC@ÉUºz`É&I%çžs_o›u%øë«¯ ¶ø[gC˜Úጽ hÂKüãšJ.1‡ë&×—„~†ëVcñÕ¹;ð[¾€»yÔ.-->ås+ŸK™Ã1"M]Û%Ès^¯€ŠŽà¥Ï!öpÑDmÎŒ§ïÔ»0—‹)³OÇóûo§Ìra(,$¸ÄéÏ“¯Ua“­0áÓ¹aô Ÿš¨;&ŽT\y¸rD¹ ¡É9C¯¯ÑÇE…Ëãw^‰kljvÀŒTúZ³600000hT ÔëÀ³Q{â¡ÆŸ›úÆpº£¯DqøTËi«ãÒ'ûÐz±`š©_ ì@u ,!ÌŒKõ+Û†©Í¤Ôµ‚–ò¤Ã{—?î(ö먈ú'UEAãÙÊx¨ª.sÎHÀHÀHÀH éJ@OÐÕ¦‡‰q}åÎ ³Õ¥EEù’žuHVm›*Ûö}Y›ê`Fk‰Š´ V×ó¢ºô§Öš EϽ6c‚Þó 'P˲XýÇ?ùÀ/·4 C¦Ñ:K9ƒ¡Y*®ÆáèUç ±ãÐÀÂgXå r$¸IS½‘€‘€‘€‘@3•FéRs!|³äAùlá]H¢¾C.ö<ürÃj^ ®èÜæb3ð©_[Ê»eÕ¸ sI`Êk3Æ9,Ž9ðm)J–ñó´J>v'˲ ›©%GÒ®dÛ7*V[ø`G¼‘eš×=ÿðÞÈšáÉHÀHÀHÀHÀHà œÎÚ”ÛàÛ›,½nœ(=Ú_![÷~ŽDë]äR€§V±ý$3÷BûÿS’ÎR×_6ì”߆`Gíe@·[eñ†Ȉþ¿‘àX¹ÿº²ïè"™»ºæÀéæÌ&'¿Mq¹]_ákscVÇØ?<ð«Òv“ëuóèÅa9ª{ 0ÜZoûâÚ€%_¼k†ç‘ÀÁã©*añéŒl9ƒÈ…L\W¢‡o``€D#"c\T˜Šnؾ• ðSתݺ¾!úäVÃ^RÈ×ä? “Á´œôò¿ž»Oš?³nÚ Y© 0 Ñdý寱ïáxVæ¬|\:Aktͨ©’þý9‘¶U‚ãddÿÇ$+÷¤,ßòªN]+‡N®‘Öqýå»å¿“Ü|Ÿ ‚Õ´or#÷3&ŠÃþtˆ*F<æ—;üýÆüaòä½Ìši¾$P$þ'œõ•Y¥Kõ SS…‘€Ç%@sB„m_°6Yæ¯Ý\WH¾Ž‘¨Í,yÖ`Ä‚­ÚŸÌ]†ý§0Øž*þŽ<ùfñFä ETÆ^2vp/ (N\ìn]Õ•síÓ‚uÉWŸ«‚ø[ýÄâgA,[µ›aÖRE°†G]Ê?21tþ€¤à @‰‹‡)L XëX]xó–kí¸ï¹y’[ ä¿òÿEÈ¿©6áíƒ P*,#åA¹P>9ÿßòo ÏTmŸEsïK`Éæe馗K€{”žyPu,.¢4ÕF\D'ÉÎ;UÒa›MÏ —2MYaÁKí¹é¹©Ó¯I7Ÿ{múmAý×8’Xv]ü̃Í(M™:ýϽ6í/îÈòï3fôŸ9s¦3ŸŒ;ÔC™‚"éY$Ž›jZ¦aŠç¤‘x½íkk£Y*¾cI{ÈÒ»¥mBŒ„©ÁÔy]ÚÀ4+fZÛñá8#]Ú:g×ôMö÷ó“yèî:p\ºwl%cõ†'ڶ稬ںG‚á¯rň~²u÷a‰Š“]O`v;_Æ ë#í=绢yn®k n ”æ¯I’o—l–Ô€6’:Pì–ú1·«‰l3ü[ÈúðQÒ+g£|1Øv•0Ùj…¡œ›Z ŠúÄÄ̱Ð&Y릨ªIW|²l~—ñqQrúLV½Êßן)Ÿ¼™†é—µHÙðGÖç>Y¸áy| Ç }F¢ì92¿Ò¶Ogì‘ó»ß&¡­ÄV” ¿¥3•–5'|OOÝqGÚ‹Ó§Ï/’…ÏM›Vð̼Z]/¦¼6ýn˜Þ½ ËhçD½E’,þãþpÏ=)Õ]kΗJÀVdeOn.£‡¥•õÎ-ØÜehΰª·}mmÀîØN€7¿Z,×"pìÿlÞj™té0€¥×¿Xð ôë*sWn-sé<ß¹MK¹°7ùø‡UÂ0áƒzuT>0‹Öí«F @2Ü|,‚åÀ±TÙºg³\7nœÉÌ‘ÞþNþù»[”‰T™JÍN½K@ƒ ú'mÝsXf-Ý¢€RRØàzo«&¤‘‡>0ñÿzáFIˆ‰”¾ÝÚÁ™ºzÀTQŸ”ZĨȫ5a£Ù–% ¤¼RÓëGþJ¾üL5ÛÁt¼Z ØŠòäÓ·ËÄ‹^•É×.øÉ“å[ÿ%»ÿX鵻̓þ]o‘¯_-ûŽ-’™óo¯´¬9á›øýý÷Ÿü¿©ïŒËwä-‚i]Á3Þ7­²ž<7mÆ}ønMP*žÊ³lñ·Žì—¿L­ìšæz\…R·ÊhÌ¢&ÂãvæO·;¬–)¼ÿ~Èyú Ç‹í™9K`â–þLJîñ´imyÞ@½áý•7°;ŸyèÞƒ”ßsS§Íõ/XŠd*"Ò­³:¬ï:*©›å§Lq;ê²Ø, ²?úøƒw•h‡X¦&¥<èY~!5¹Ö›Ê3<ÜíûŽIïN­eäÀîrÕÈåpæå"äéI¹vÌ ‚FCsäJÐ"Ý0~¨œß³ƒô‚fi7´F¤M;Éh8ï;¯³\Œk8‹MêÞ!QF p¶*;ÔúùSõ™îI€fjJYÙ¹2sÞzÉö‹P%÷®nøRÔne[#ä£VK.üiÈouäÚ§Ïæ­>‹Ô(ª¹(7Êïã¹µ“Sy¦j.9sES’£Úýýƒö*lxEý:yz»üwÖ¥òÏOzËKõP¦zºÜ—‹%_,ºGïªuvnª¼7çjyuf?ùlÁeΙ¦#¤áæ6NÄþÛ)Ó¦Ý[QÏ`z÷k‡Ý>ƒp'P²Èú°à€±Mžl€Rƒ”B!¯§’–÷ˆoÑÉ*Ö×vþ̢ϭܶ_E½ÓÚËòå;{ ü=pÉÆ]5’Sx¦Ü“)e$`$`$@³®7†;ìŽ×`L÷g|¡6ÙmöÅÿøï[?1yò!?«ŒG¬Ù¿N™úÆÊ `ê åµÜ0fZt Ê–ø±èsf]Vøü¯×Gr0§WhÉÎ>gþXNÁ…˜»h“7ÏVhÿ©¨H>Ê9öòo”8Åw‹‹[¨ë⺪ºÿ6uúdá9§lEsvË#¸Çu l)KˆHë³`©TÕá*Éf¶Ý¯k[Ù°àÏLûà(¢ŒßÇõð1úaž¿[ºQ.8¯‹[’¼o­—?ü{&l»íòä]W©ë[DËÛß.•£'OË•0÷#3Ôppj`l­²ïpŠä °FJhÛ†k°Ž5§´•˜œÙô”tëÐJi-ýDÄ•Ê÷)¡°¼©ÍTwœõTúYw),$PÒð¡&òo Ï”ÇmlP ``¥‚Ä8× ÚTƒTîʃ4`*­›EÏú[åþ'x`="¸]ã›…¹ó§¼ùæè'ùË}/½þúàÇ&ÿ*•‘Ý0¹÷'ÝžÊEAaWýþöÛá¥k¨Z XÄ9Ë^MAx8g!{QæÓÜ7¿sgXq=µSeª©¤î¦¾ÕµÈQøûÐø}7iR.ÀîϬêh†'J›ålÞR>J,áÆÑ$#äÖ+.€ï‚¿üãÝÙ%·s@Ò¿{åGâ‡i“›.CàÐEÔqÑtÛ•Ãõ¦ MüËëF#ç„Müà¬ÏëH­ZDÊ— SZ*7Ôp(ÑÀJ@àt+e%o%Åxd>¯­[à¹ñ+ì¡¢>Q»Éç×ôÀ¤qHLé4EÍ„éêîÃ'åG„`¯Êd|ñYïÔ¦…ìAùªˆƒ¤Qç÷@”ìFK”#åYù»óL=:øÏŒØ]nREÄï<¯ì}”*ºÎ¥Ê$VÇÀ¢ • ^:Ìm–àÍ£TW‰<¦ed)¾‹ Og?4UÔ'?xÖz2áìæ]‡äYKašº_6é %ÕQç¶ñ2v0‚ôx9QŽ~V¿ÉßÝg*%Ë!×~rVþ¶8GíòðÚ[#Œlï/]èÞõÕ=S^~K {õ `/±Û¢ê¡¦†­¢¨(©6ÊjÒ¶ES»»xaêCà›t>Ës Í5ìÂþ ?¥G¹I£åOæ[­~Ð(½ì ”0õ}›°à«õuª¼ùW' àS5QÞƒÉÜì»îº >Aþ7#ýȘÏíF„¼ÃÙy…ïÔ¦îñ±sÄa Ø]yÒfO‚ÆjNmê)sÅQ–œÀ«oRÅèÀ7ûRo\_ˆ0ámbë­>VÔ¯{û2êµrSÙ9 ¶ƒ`£ ZóòóØ!Oò,ç˜ûžs]cȳI´6ä;8¨HimJœTQŸ¬åfbššý?uF-ÝÚ'Hß®í”&†&¦— wXBl¤0ÀÉ–]‡e]ò~i+W ï+¡ÐÚ<ÍÔÞ#§ä‡[¤LP/ÃñVÈuÄpý‹Öï(Ñ!ø»íês%£ï­‘UÐV¥ÃWHÓ¹ýºÊºíûäç¥?Ì`{”ÕQž•‘UÚT¿Vv¥1ÌsšrFk˜ý^)+8åï?åJl¨!S®‹XÚ)jm“Ôk£$3ß”æÉÞl,;à’ƒX?5+GÞ_SP®hÊjSåÞl]Ðà$ Áà’z×ZD…¢½а÷ غ7òØ®y¸.zËpalBò”pÊo‹ÓýbšÅtGФÞSRUpÃnû£ÇcŒõ¸Ì{ívÛmð_J…FÉ”€z?‰·Ûnµ€Rí4–aÆ9ºóΜ{áGvÔ(UõÉHœU”ªZ„ï>Çø4KÞ`ÁRà}"·6, TL4WÓ&k. æ]ÎtT¬”ú» ‘†>Ü¥4cº.ä&êÔ£C+‰G@‰мڡÕZ²f›TVN¾ü Ó<šžnA0 N8tn×S( ït#ʤ7Ùx§¶-„Ú£‘׈yÌ4qGÚ´û€,]»]mŸÖ££tMh-k·íSûuñ¯6änÈ WGÊ©Jbm2é×<Ìö‹ÜÐ;DŽæ™2@‰t >MoÝ%g¶µË €"ÒêC.y%Ms<Ò—í÷ž×ÇËZ—T'-ï²î³Î5, ø€R¡é¸ ~Í"ƒá d"Úëé¹: +DÞB1i¡L×ù)Ðu H†›S*7’¸ÁzáeüœµËDèêsg̘˜<ù?£G&=„c?¢_²A£ô.ø‹ ®ÿ¥»”ªçï¢ ´Ö Vh:‘Ú›Àn³Y`©Áþ’ãRÚd¦x.h•€4L=5("ïþÁ¡NœõÍ0úzþj5ŽÚ/R÷­ð¤‡4‰’£Ç½{;üJ"š×‘¤-é´L/Š*› ³¿ø\Ô%Õ†ü Ù ßY•/?!0Ãah†Hm"mÊ4Oíà߆t/¢FiÅïÑŸwUérНK«Sñë¬ý†/jn† |J°ØÑ΂eOfw»é$aa»ª’ä)/¯»ôj£øÕ¼kóã€b¶ 03ñí·ãݹ¯†a:̼0±»Õþ‘U§ßÑ ï¾{]AVö÷“SoéßüsäQz쟴hÐGü‘Ùî`ò‹‚¦ÏZë¦'¦!Þñ—ËÒ`skYš^Ëmô5æÀл Œ B·Ã®ºðUmU÷…y–¼Ú¤¢ú°^õ[§ÍÐý°xlAÎ0 ”(Õ«Îï'‡22å•é?--SÐ:ïUIÄ(’õIµ!ÿ#Ð==;G>^[àJ¬ã®ãé _$Mc½À0=·Ü@‹T]*©NÕ-Óº?ð$ µ1^ d—,Á’&Á6d½^yLbâ>{°írÓ#£&$§Þ­¹‰‹‹s-X¿a:ºÕ[õ1ÄeM†FiÅú˜µnÚx15µ%Ú•šD£H£ŸUC•ˆ–ê/gñ]®|à¢!%ÔŠ=æ½x%õq®…¨aZ¼f«œj7yôŽkÔŒ0#æiÚ¼ûF’Ù‘·]%7\Ö_ò œòÕüÒ>>N†ÿa  öÐ,*R_^ï뺔ÿÂ=NyvA®<Ð?L~¸3FúÄÛå¯3³%QòJ£ŸvºdK†Kæü9Vþ…ࡲêT‘û­kjUêçq›DüEÔj’6Ã#è •°pLR`i.m"LÉÉí'ii·Õ«†‰¥´ôÛ/ãB¤=r’OòK¾©Yª)3¼|‡Ót{Ü´ Vr.\WSÊïv/È1>ƒ¿Ø ˜6\¯køÔ°a»ážz%¦èþoBòëg¼:cF(4JŸAš7ékðÑÿ÷è‘ÃGZ@É'‘³1~RêSSk%·‡Ã´û"øbîo0B)Q+ÀC B±5] 0RÙ'·F+8à¿fRo¬È—/7˜»2º Á.¿v—9®LyõumÊ´Ù¥>zÁªÍLÛÈó÷³â 9yòÎ× Tt¼„D'mÚyP-L뀖‰þY¤‰oŸ-U¦Ï*Û¤OÝàÿ^Z”'\J£wW—hDÂËB€M „Ø'ùd¿®Œ\ ù$KbÃl¸Þr Ðòj kßîqº;žÃýj©T0`U`#88X¤ÈÈ)@bî¤PHp9ÅôäÉ¡¼¾â<ÔRš7û¦Î}˜è£Ds@j¹:@›ÚZ¥(ðH> êÈ·6ë‰ßôXfŽét8ŠûJøä^Ïh,e0<4|‘³G^øúësG º™uC”µíH8{¶-®YNæŽ_a®îj¿:?ðÔOùí ó2AÇSÇfDfëxQÿëûøXȱ-Þ×4lÁØÚØ 9^ZÇì _†ùÓ%ßà¹ëýŸý|JJ/§Çø!Ëûð8ò7ý¥a3€d¾P˜¯5ÖâMõÔÈ¡ëüï;aÛpµÕû¨Ÿ–´0¬µ%@@Mh]þŠècG®ù–~!òwäµ™µÝ)9U˜ô F è×E©Áív‡TVF5QŸÊ>³*×kÀSÚ½ùá^œòÀ!Щ®äï”*"“ãùUJuU§ŠÔúÆ+gAÁþ¬¼#À&fsU%/X²!2%4K('*2@ÉI § vÓfÉ!™ùr ¯¥<|Ìß¶âšÕ*› Í©¥¢š\ |7…×=1Ù¢Ú“ƒEK埳IA×§!‰è— «Ý‚å‹é¤ÀÓ)­‚ä"š%/Í—[½þgDÈÜ“€ÐÐCú†Ê#H&štn¸D"ùèԣĉ±í•ïEz+‹«"Þ‹ZŸÿ1TÏ¢Z”€¿¬õcü5Dùûó¯ëd­FæÁ=»Æ´hñðš­ûä¼SºV‹1š°1Pý•¨±q(ùG×äùè|9œë’£]$-¿;À‰‰Dȹd?ŽuÍLbxÌq»baòï1|© ·´7%¡Y˜D ÅÆFKL &ðI~ÉwM™àQ–h÷Æžm[…@ùÕ´¾œ…-‹ÞMié_Ú Û+†=h¬Ûé~ó°Ë3§oƒ†éâ6/!¿ÓÿõöÛ‘ˆ€7ß½‹ oG1Æ`à?Aïû¯Ç&&ÎE²Úû]n×·ÐP\ódRÒ&ÿóÖvÉxý¢ž3vd’OSG@5fd’J’6.9e.|ÃÞ†ö(4eÍ”©áþK—<ùe¼ÖK=ny» üùÑöä¨{ï…æ À,9õ6¤¶P‰—`Z¯ø­jh÷Âû>CIÀ—e"Š¿‘Ï(NH]Ü›>ÖŠ £A·&o†Ç°Ò?ýºA~\º^²²‘ëÈÆ…„Ãù6d—š Y$æI» “®y+ÑQDÈ•çö‘ËÏ&ÿ3Çfÿ”xo VÉ?& Í„©ˆ^ÔÑ./])»2=²î°KZ —ÎCGi0“JA-G=ò퇜ÝÎ.//Γ}YU׌@,F, X¨i p4aÎûöóu=N?3ã×uÛ›,qŽ­ÊäÓ.| ©¨Jà{T‚±0D¾´AôÉì,N$d†ÈåSÀ¦Ê÷»Ñíz4I±¡AsÜ0£p¥Q"Pj«¶i‚Çè}5¥U"¥iz<Ç–Ïÿ‰5%çÂ5O7iÚ‘v¼'Â|ç (Ãûã“_¿?† ¤éúXñõèáÜ8)5Éiz¾{6ùÍ«ŸqÿÖâ×XûÅ$`šQP¨¶,v´SѾqži˜»CͰfˆËît8LxãwG?ðÀ!Þ¥ñÿÙ ;Bã›­E>¬Éž<p¤<7eJ,~Ãç º>Ôf;˜ïrOÛ”ž>çf•]ù-[°m¦Ûéy¼´;Ákï"¬ä)Ý\¯´èx“¥¯Û¶WÞýv±Ï‚ÉUpk9ÑG2°vµZÕn:¥…ó´.Ø+Ÿÿ´\~ZºAîºö¥mª ³ƒj[ÅŠ(T”©zbiì4ø0­ƒ¿Ñ\8ÕŸ…D¡ 16ùËY²dŸKi”®ì"¿GbQ‚%RdˆÈÕïgùLötÞœ5(cûѪ™áU”gë:K–œüðî´ýûþ·Ö0’š¿GÇ6ÕªŒE¡Ðî -¤*‹–mô¢©[(‚)äåå+_¦¦˜’¦¿¡NàMMdUI÷eŒhG>‚s“‚|&A‘òQ‚f‰%e†çÓ*Õ\`ÊpíֽƾÛéÉ/yWµz ò>j’¨àÀ™p¥K®³‚2=îgï†ÑÆ7yò›i$D9¡Žt{ŒYМ­+ MÔ#þ½Ïõ„É“¯A»ùf›Ù ûâQ‰÷£žQ#‡Ï˜˜’òˆÛãüþÙÉ“¯|:1q§ÿ}Öv$`¥ª°î¸Ñ ç@m|6?dIÐä|ûôȡԢ ~×ÿa¢d(6S\Ï•Xd³íáHoå—˜¡6¨Z` v³iâÌU¡ÁQVIÔ[ÄwÇKZ i=kÉ:ùtö2É Š–MQÉq{q0_;µ!;’ –XWºôÌ[#ÉÓ~’›/?K®8·o™Ô÷_ªA¶eÀ„î S6ì- {reëžéó ’Óú”X)F­ýe/"•ÍÏ“µ…@¨5ˆ’6fiˆ6¤»¥B?kZ~ÀåJú˜µ¶$`IÀ’@àE æç}ñéô[¼í£ï‰{êžëm!ÁÕ³n PÑ– 0ÞýÂâðÊXÊGà'À’Ë傊`©fR-èçQSDÓ:ò¡£óÑäŽ>Jáj¡ùÏ“¿š  =¦Û}|þ×_ÐycD ˜Š>à5ñ°RÆæÃG^Dð†…N—Qëä)8'ý¯¯ÌašÝfžþ4€|”âÜns ‚=ß­RKh ~P:ƒUÄïIü<@irñ*›näÒ1Œ[ˆE=¦kØ„”É1_dº÷/†Fã”ûíøä·7fĽ{Šßoí—.F8AËÜ3¾Ù2}5~Ø«‘¾}›,÷m‹dãF5êÖð üƒ\NÏ…úžÙÒ K&-ó²~—ÆwΰMw›®…†Íö×1IÃaッFv¹ãÐeþ˜\¦q5´….rg¾Ë3¿ÿ)…‚¦2†Ž9ü­’C“½qÉ©ðΕûƒ ó {T”Ñ•w®˜î ìLçÁ‡afó_ÇýƒJ*Ã: ˜¢âe†ùbw—逽JA;ȶ\2yÌÏaÜ‹0›Æ—cG&>RòU; `|;L9•t÷QóH}u:!Òž>ÞÖM ,(ÑôîÓÙËPZéÓ,×ÛoF F>úásþÅœ•Ò:.FNíÑ¡F;Žz«\ <ØmƒFgëQ?PÄ.¯i4Vi9¹¯¨¼¸È#—vV Eçí,KÅÙÙ@ÇÐçÀo)#ÏT¡ž¹o‘%K– %ÀYä9Ð)X4óÛ_bšÇ½‡í? ²ÙÜ}ýÅFu4L :7 ·ž›À…%Âù{µJ á­ÿÐWr©*ñ™jÁ´35K|&µK tD°F€¬´MÞÈw5©Q"PZ¶a‡±cãÚO–ÍCg™.”/åÜ$5KÐùÄà7yN\£‘Wgñ¨áÃ#òµå÷ôY²®Îø­Fµ; œæ<´eB…{Üh÷ èÛ¤"´ cúôé6ÿcG  á¥CWz¶dg_1zDÒ\ÌECQ#(ÄɹôÖïˆm¾Óåùä Q0ŒuÖHi”ö-ôªÿ?éÍyÿ.–O_4h{*qªØoóò´iáyiÃQÎýHZìEþ—â9OA`¸Æÿ\CÜnR`‰Â{ðQ¢é5JDä'";K>ü~‰ü=¡5fÞèèZ?¯úK³Îg„„œvu Øøþ»c;µ<œ×Jf˜éÕé7®Œq¶üŠ:GKÍá³Q![vÉîjj} 0ç3ô›lyþÊHùþÎX¡)eYžÌÞQ:X:†ðã­É—Ñ—„ËX„ ¿à­c'äÒ©@•þØAK—ö-aîx8àyµ´$€ààš%æ»;ïûßÿìê[ÿDçÙÛd5o¿æ|[Mø0ÀÐH&’`vÇHy\ˆtž4 „ðøªŸCòj‹¼~RìÏø­à³Õ6@TMj“è£DÓ;j”¶­ÿíóŸ>Ÿ>,0ÁÊ•ò¥œ›XRáÁM³OlçŽs<øû‰)¯¯ñSäBºñéC}õý÷c‚އü6"iÈ¢çßx#Áãp̓ŠC‡|v6¹ á¢?†Ü|4>%u(4EOOIy%ÎlùæˆC²yš‹É ar™æðºÊ0nEÄó‰¯Ô šªON™=>eòZDšC|s64B{K½¡‚'Tt©"Ї@IDATÂI)“LùQó¶#×SGÓ°=‰ÛKÔ–V¬é1gÀ¼Ò“–„mò«=È8»4ó:€ …Ÿ| —VfC9ÞdÀ;F½;ŽˆwôQª/Ó»ÒùÙ~*ÓBùiÙz¹æ‚S½fè\ zuKè—«§áv÷CÕ_ƒ~ø ôCTÙîx±:<È ”]q{€¢î̓¤Gs›tknWy;¸o7<Êq93Ë”Œ#ùrè`lõöÝ,¹Lb †’ê›6ÂGéÆ3U8ð\æÞô ¬üŽÖ'Óxø=½ôKžˆ4F3½® ­dàY½-°tòOo±$P ð3ÂA<¿zØóC6sú‡_ô¿dÀaÏ…—ÞóÒ{ßÇžÒ=Á<§_WãÔîí«œ‡‰ †@… …¦oÔò0 ƒ6½#@Ò I¯ÁK¥Iƒ%®õÂgê€úX¥ .vó(1<8£Þ1˜ƒÇåÌZöóœV/ž¿—RŽÄsM¹R¾”³ÿg»—&¤L¹Ñ߀y×a$“5Ÿë­kŸ¼ï¾yãRRîv›î/zR˺8F#Ã.¨”~‚4ºP"øœb“?(}Z\B!†9 K‡â¢G%}í¸I)‡„‡¾Š²÷3øÃ„I)!N§ç;ø­\­Tñ2¬}Ü_cF$%"GÈÁ¨(à–ZQ†W„œVáz›k„ ÷“”x·ÿ¹QIIó±•>Æãx§_|155>ÆtZE5Ë[ü>}}!¸U iy @ø4}®"k¼ùÐ,y_5#$hAEî äkšXâGŸæ³—mTQïê*˜CexòŨ|óVnÁà³—oÖKC£Ò@Ñ62‚"U'¼Kêu*¥û* …ÚK– &Hkr”*Cyê³T™;Ƶ ­šË  òÃBdÄ+dÛÞ4™³lƒÜÓ¥òÝÂßdû>•âAnÐ_rr dü9C~VŸN²u÷a9ÿ´nRïò ;eÅÆ] £ÒUäòÜövù´‹ÍÃm2}½CZG*xÈ‚]Nyã†hy|fެ<èUÞ{F˜\Û3Dn™–)]ÊÈíUEV¬ÛKü˜ð«¥5Kà3+mÈŠùs—n\¾lëù× ¾Úív  )•j6‹4‚ƒKþâæÊPq`T|¿2eék‹÷[Å÷õuUYç;æ±ã9&΂WÃírfïØ¸áç_fΘ———“‰2)ìÂ5·ý5K•ûxãæ†H‹ ^±…‡Î\;F9ó³jzBMÏ‚æç|—Û|Àft™Y€’óPÏŽÞº›aÞ:*1é«’êÎ44a?ÄÄÆ¼•™™èÈ/Ø¿§ÏlFðK£“xÁ#BJÆó¸DIeXÇJ—@™~I¥ßVî~;pQ­›€(m¦Ûl¯2ŒÃ£†Ý\.s~A“Kœ5Û‰A[vNƒ÷ èŸäPp‚Ä!ÙøðÛ£S[5Èæx'€"ÃÓ*ã¾ÔUa ‘‹ ö{rïig;Ò£OK{˯?š#d¢Gž‹SÇñNí9tDÚÆ7“o~^%¹ù5“‹°tÂѯp‚¥R™¹½«¬š`½ hWþ4œI¦&„&x|l¹¹ÙòÓçÓ¾Áö짞ѥS^}£¢ãBÃÃcìö`‚ª&G.—³  7/+/;ëè®-7mY»š‘¶6©A"8¢/'Ê“råÇ…rntDÀ0t+»íj žO~£Ï„”Ô±èWç褤êð2̯ Óáš…HwÿæN6vâë¯÷4œú(éÁm>€ÒÍÐL|W¶ Œdè$~èÎ;o…éXK<ëgôÔ¿š×gØßd3†µ S†"e—cmtðÈ]'À³…z»!¯=Xâ µJë¶ïÅ»,*R ÿ`ÌóD>7ìØ/Úµô™LÔä¬\UêOP´Ý“ßËæ4úzŠL£l–ûžŠÔ|]¡âµÔ¹;ØÈæN¨h×qñxÜÜG÷mKÿì™ýÇ7-e˜Ë6<üÄz´íѲoaBتðKy-´ÇÈà ü¸<c„\Ä{Pô?Fe#ëäÁ »>) `)37_âªw÷Á ÅJ|Å*Bó–o’ûÓdßá£r‚štjÛRèwPŸÄž•‘EÛT„—ï„ȉ÷|™…ô.Y„åªn*݉ªî×›rû)a2qA®tnf“-‚äŸór+”Û«5|ØnôYü},jbÀ(ðj_•mÆßvÞh"`É-Dz‘lÙÉk1álM´òG>3Žg#R‘S%ä =ÊuA›ÝhßúDt:»ƒ-<ª»)¶¾þ ˆaq´ÝœêuJízÔGrØ^¯@BGBC¶®u³ðõsïéÂNMgOi‡ƒEåÐ'ªº.IV¡ ¡ÿÇVµôÚ½<†…œ8Y\R}Èë”é|H^é«Pœdp<#’›Çq lŽàø]ßİóa!E1òS–ü+Ú¦Îhí­Û†tïØ!³À#{3‹ÆßlqÈÃç‡Ëém‚äüöÁêsvÝS*H6û,Ô„”7NƒAQ<Ë@ëÉ2‚®•4tžÚÇ¿&OnåpË€Ñ#§éczÍgÂÙþPrc¼Ýv)ö•±9¾—ü]f9\_j­›ž|`É2æ6†ê×ÿ褖¥H“)ú<óÍHÃã|QïÂÇ$%ý·¸ìÂì¶7‘i$BÁ³êv[ü2k¿ Ià¹ÔÔÓþ¼ « à|þJ+GÖà%P±QA®&#ý¸– HNê ¶ðµq£t8 ¼ù/À;ëPUòE[‘'hëQ$—pvr± L–ÒéjM‘ ó9Ûz‚"ÄTZתyĆbš"Åj}"9Q# —æ‘Á²=#OZ8É¡ÿËf›¼qxÔ&.ÒÇwqæt}¸ÖuʃŸPdø‰Ú¨â÷Õæþ‘cYrV¯N&È/yN9|$Súti+;¤Kß®í|’c»À&Êx©Òò¯H›Ú‚w/#×#w*/ÿâ‘ëàŸVìëë5Å UGýÄ1Ìq«ÛË_ÊiSþ×[Ûõ.~ì5@òßæ Ÿƒ~¤ù²SåXX*åû«>Q$- –8³ Á$÷yœà€òÔ÷b³ñÍà¦Núû}¹ùßÀÜî h‘îŸ;wîÝ ×müxÁúGÍÿçûs=N÷ØnV(‰c˜ý¿fTbâR-äãé4>9õUD@»>MWâúŸ&¤¤8až÷¡¾†kø=¥#¨Ãì½yÄîTÿsÖvÓ”€Ûåñi• o0Viï^±îºqý¸ôWòú,1DÃú½ÏÞ—(P׃äÒH"!„Û.•VŠ:Î8²»Ð·¬ƒÃÔ<³LP´¾Ì¢êï$e¥L¸R+Žä‡0kíܰ`‰¼Ãä²EL8¸.zZŠ%Õ Ê%É[}‚¥Í»Éé=;ÊÈÛ®B4¼Ãò¿™KdÑê­rÙÙ½ä¡Û¯R ”Ï`€O~SŽ!v[¥ä_™6õÊ’|ƒ|[7ô ‘¶:å`ö‰“Ýßou W„lHsÉö°ùUÉí¥Û ×åµ)ÿk­í€‘;+®ÙH¸ÍY{Î:ø%Îè³cÐ 65Q&z¡\üeD€Ä5ž;ñ%ÃÆN÷ÜsO>Ìîn€6iÆÄ”ÔÉ£’†'nÞ¼ù‘‘áL>kº]ßA4{GlÁ¶«0ó¿Â»ïýQ¿nû¬ÛxÆøÔÔ³¡X†€W9<ÎY“S£F ÿD_ûlJJW©ÄÇécÖºiKÀ4Œët4'8Ò}ÓX¤Ñ¨Á$m†×àÀM‚ Uâ¢ÍšX ж`0µ­\Mï(™Ø³vˆ RÑæ˜›¨–%‹–È]›Wü÷ùgnÀiÚ|s–ŽŽ*("oÅÉ*PIf·1"Öm"mâÎ:$±®t ´ðñä)š¥Î­"}ü뺰ŽÞíëÔ*"Höe9TøíúÒÞääÈ;_/€Éhˆâƒ¼®ß±O-!ÈãR<Âݺíûpe/Sä†ÙÞÄ·¿Ö»õ²føòü‡ô¨¤ü+Ó¦>Y_ Ÿo(†¼Ïuš2÷Ïzb×[e&0>uòÉ îËÊíU–°*Ҧʺß:Wïà@_&jH´6‰ I%mÏÉÏzc'ʃ¤e£“Mz_Ÿ÷^ÝÄþÃ$/9Ž®C¤?ézD¶û Éh/Ä,¯Ê7%Ͱٯ=ìߊ‹‡æz0½û7ì’ÿ†s·?9â 5þ;XÌ?)Õ9fäð/ÿ•šÚÃáöÌ@?õ4X`5q L˜2¥-´+1F~‹ Û¬Æ"’F –´FF)•˜f‰þÛ$ùȇ(È•]9N˜Ïy”ùœî-*ÒÙ{úƒ"†âîg8:9$÷Ö%9r¨¾Ã«U¤R¸†Ú$o–x$©…¶†YãÛEË¡ôÈ]#+¢/ ˜ÄÄ6Ó­xb¶®­b¿šw_µÒêt8Ç-GŽçHë–Íà»TáÔÒ%Å©8P*~>öiŽJù1D}Uä_™6…YX”t½+óYªLn¯Š¶)͇µX ðsOÀÁ?5&G\ø¶ë›j›ëÆLºûãZ/”^ô±Æ,ƒ ÕÉ`_1ãr&60)õ $¡ý ‹P7rÐd»bÔðJ3[¾yÔL°ÕùéÄÄO ¶ÛƒÝnó;vèàp™v[âèáìCÖE^·y *©'o~"ho,•nÔ`‰?’05°_l3´F~—-6hïAlÁè ÝO® É 1AÒ ˆZ"‚¢ÍíÒ5îdPtòÝëˆÖÆp$ÁùK0»$Dæ‰+ þ4¹+d]ä9QqòéÉ”ž­Ã$,Œ|†(¾É¿® åvIujiÊÎ,&øµD ‹*'ʾVýª(ÿª¶)‚Ül'Çx5OmS5ÿd«ÄZ’€ºÁøƒ$>’ûM…( MZ.zßZûI€@i\rêï›üúõ.Óõ¢ÔÜ1føð¹×(@‘îðôš"¨1Tµ`ƒ )æa¡ ¡¡¡‘'9ž!û¥_ί²!¢½i˜8ûÏAmKç~Iˆ¶Iëf‘…|zÁù÷¹]^ÒàÖœ~4Kš7‹®W Sµ~¸:¼Yi””˜H·&ä_Ù65äF6®Yªl›ªÖÓ‘S÷û/Õ*κ¹RÐr¯ÔMÖÅMKÒp=Æ?´šôs»M»íò'‡ ÛæÝ÷þo:Az›¦ë«q“&¯4ìÆ8¡e¶ûk‡k-|“žy:)IÙ?=bè¯"ѾQþeXÛM[ÒúÝ‹Ø%`æ&/&&êÓÆ$‘&–ªóƒ]`—ß»1ÀfßTwÄiÂvQ†ôL–Þíäw›péÙ2¤1jŠN¬ËíÊËw©£³¥Úd`) ),Lô'¡9 5Jm"L鮸#Ÿä—|S‹T’^YuÊ@÷åÊ8&q1^®îÞ¨ŸD%šÞQ£T“òohmêD©T|/ßá4áOYÜÄâ¤w¹â%ZWZ°$P“@0†›¡QúƒWÕ`L±]ÊåO¶³øs Ïk=>eKzú_L—9 ¦vmóPe¾íR"ﳀ’–„µÖ@¼äÛá>âÕpòÕCwÞI¿÷FC,UW£ †×E g€·#3[]R˜Ò½2ZZ4”˜hšg…¨Ás]òP_Ïræ=v<§Z`I›¬+!ùRPP€²SâÝ9+•/<&gd/”£öÖ*J^FpëZËÃÄœ7 åÜÚ±Wâ\‡T®v.h”"Š$ <’O‚:ò­ÍðôïPÑ:¥»BåPF¦ ¸"áh;”‡I×§®×ôdxpF½£•M;D¸k\þ ©MUõ78–™c:Ž#U½ßº¯Úà Ä;0ñå¿]í­jLþÜö߯±‡/hÂäÉ·»Ýž÷p\ñ`Á½Qç/:ñÞ=ůõßgèq쿎œLo/Z¿ñN·éz÷î„ ø©ðz‰f}þ×—³mµÑr §ýÛd•Û(€ù}º>†Ķר¨Qƒ%þRÚg©²¿"MËë×E!A¥!SoŒº\ùîq!€šxE„\ÐÞŽ@1"sv:å…E4í2å™rျÀOˆÉ%·# ó²erU–ðòñ^]ÐWÙg×Óõú%5órsÒ³ò Œ\€›ˆ°ªåò økt„¡Œ¨ÈH%"³!)-´ Þƒy^'Gˆ˜ðBÃá‡iL ÀØmÔÌëdº$Ô“p†~µ¤)e3»CâÃmGaŠ·èh´µ¨HµM~É7#ø±š*^§|U§,´É Ô™EØàï„¶ËuS#ÍÂ)yÅð9‡üƒ’šÛ˜kMþ §MU¥-ð½äû‰Ä–q¿~o«R”uOÅ%ÀqßÏœmØŒkðê„—¹-š²å¤XqÖù•øâdãÃsoÉ.gAÁï¾òìr0Á—ZywÆOš|>uSQ~4c#1\>zؽ¸_*L^û4MïAÓtÊ»%wÏú<•–TÅ5ìdlw?òôYÁ¡¡Wé¶ŠÞLEá«Èó­kêVÐe±"eÍnÛýÃÔÇévZ¡6:>yÊŦé6ŒÃöéùCÝÖ öŸV3£ÁÚç³ÎŸàrò퇜ÝÎ.//ÎCxf/àqbеþ[>^S ¢g½:(R~Ãþg Ü"ÜÛú…ËÛ«òeܼ\yøüpµ<öƒ7ÁdW¢áînnÆðq!ØÖ‡ÅÚK¥üŒNxoHg¤V‘5ðYÒI"¹?uu¾Ò0úÜ&D­»¸£]%ž; ÍÒ ™žH¤E„Mî?S QûÖ¿%à?Øâ‡Ù½æ×Å[Ͻâšã¿®Û°TeÄâÓÄ|Ћ@IÇËð‚Ž $…1^A>€”KB\.‰ÆïN­SMjóÈŸ§N0r……†… 5JJÍbcÕ6£á×*iÉU¥N‘Ф­Þ²KÒeëb¤¢æuŽOÀ~xòldõ-ÿ@oS•ý¹ñ^b<æ9¶ìçÙp/ßYÿw¸²ÅY×—.Û½Oþý[Hð›w»¾]Û›çôë*§voo@ãÞ¸g9J—IC;£@ µ±œdÀ»ÓzíÖ½ã{]|öˆ¶§tKúhÒK3Q¡I ‹À ‰ˆ¹’Œ—Qõ™èvV…ÙmWýíÒkQh $¡üà?ŽüëÕ1ÍâRÐ×¶µÚj-J¼vŠ.©>Ûö´®ÝÕíÉaïýûùïñX ìOààßS§6ËÉÉ»U£}[1å„ ÉŽ–*ùCÆ#±é?.‹þmìr9{ZGÙ„É$5mE²XMRÍ# iHgZ3¥ÏYë$À»JX¡ºvoÝò’ÉÙ²û ôèØæ„‹+³£AQ(‚™±M¢¶þ@4uc„¼¼¼|¯/“Ûã5Ñ옺¶: ‰ƒt’ vwä#Èp ÄgzµJðQ‚é5JÊ Ï§U:1°ƒ*Äï_eêÄNzÉšm’SÀ¯IcBë8AgæWbãÜ ù„7„6UÑÀ÷ƒ=cÿ®_⾫ú½mü¨»¢BªþuüpØîõÏG Ãö¯¶ñ±æmWŸÇï ÷ƒRýò­êX4'§•'ÿø}üÃ’VŸÞõØSïåç^;ôâûTæ ~ilOHN}fƯèÉ@˜Þ-ƒ¹ñÕ6ìä,×¥R¹ãªâŽ!CP‡ÂÂ#ÿ¯]«fV[­œêê’Úé>Óüâ/OŒõοƿfOj£99ùwc´Ί Ï=jøðõU©bÆK•äŸO•¾ñArÕûÇ•ŸÒû7Ÿh2Þ%®h¯s3›dÃoä´M•*¢.tñEd§áX8ã«™»u¿ú£ï‰yêžë1¹Z$Wœ¯\¤´–THqtb0‹€v 0°uA»ÇØÓ.éçQSDÓ:¯VÉ&w4ˈW Íïxžü–G©ÓÑÌlùeýn)€dÃ""öÈÒ«SéݹmyÅ7šóõ)ÿ†Ö¦ÊûÑN·à}ô Þñù_ñ®×<¾»ú=.¯ë|ÙPƒÐûž~æQ¥ÎîÓżûú‹«õý+ûqÖÙº–'ÿžüËuAï~½À\¶aÇø;~<èƒÿ¼ððA j™*˜&LJy@é_ºøæýb·Û~‡$²Çõ±^k Ä({¡àÿa¥Zmµ†¥\ÏÅk§ÏýåñÑÆ;/L `òµQø´mNKX}ýɯi¼VÏl×Úã-°T†hwàC€sà·”‘gJVGÚB“”’'.ê,ýÛÚå§l;^âù[ú†Êªƒ.¹¹w¨,ÝçReèóÖºD °sÐ@‰FAnnVöŠ…sßµ]våHv*÷ß|Y•gUõ€™@DmCãÃm‚¥ˆðp”Tð}z Y‚¹ž¿)žž­+‘óR*•|à Z2€%æ}b¾§œÚ%s Hò&¢õj”ÈcyT^2ŽeÊÌ_7"HEDFG(yäÌÞ¤WÇÖªèªÔ©<žé¼¡–S]ËŸ@©¡µ©ò~?¾‡ûÓŽÉoKNÎÊR6|W5`²f„Ê`ÅÎÛþü·Q¿³Ù‚žçà³:ß½Š=κª>$ÀÉ?õÛ~&Lÿ¸éža[¾˜:å»B^8 àD¹4~RÊ(¼xô…øî- üä}÷Õ|7ý¯Røó«­ §1mk§nñ覓ÿÍvªÚèÖ´#¿ÇX¢+댾vçè¤a_9¼1‰ÀW ,ùDqòÍë>Z“/£/ —±0½»à­cò!;“Ú%æI"X#@ VÚ&o今h” •V'4csVlE²]»D(‘.íßCº%´®v|oJöõ ‚á†Ú¦JúY©Q*œ7vmÞðÑÒ9?.Áu|Gù®òå»k%¡šd4èŽÈ°°ˆ7hÎDR5˳np °OÛŸ~”¡„þݱcÏ%»woÎËZK[æ; ôO\øw]E|êæ„FG]ÿøÝw×fD)¶IŽCÁoóVíþݾuœÕVõÐH׺4$å´Ó.<ý·ßQkibZùoºÊ†i¦ Ï-³ÍêkâÚKåüjãççÉK¿äa‰‘tCW½w\…/)Qíq¬;?Ë’èPÄb,à7Ï¢ H€‚​³„ñcÏÈ¡?}>ý{tü@ßp ã¨yû5çÛªëÃÄA4ýˆp9žIм0„á -ޝA5T0|‰ ˆ›KjÁ>ŸíÝ÷~Ðת*ùÏ[¾·N9ùNùêçßÐV=ÊôŽ\{ÑiÒ©]ËÂpéÕ«S%Y«×˵LëRþ¥MéŽþ4½£FiÇÆuÓf}ú1'/)„ï(ßU= n}ì Œj_Õ VýºO/|¦ø •+ \À²²ó ä?.•œ§CkE„yÓÀ³¤ €RMÕ©¬Ì%” IË™ëÚ”ciS~‘»Lsð¸œY+þüΊs©Q¢yßO.þš¥u¸Æ¢ŠI@ÍØ‡†…=د[{Ó æP1¡5†«8ùÇ>m­˜÷¢>¯c¡I:&ö‹'иäÉ/ãƒþ¨ï äéÒéæJ&ŒõÝ^‰ ÕFq=ûæÈØÍï%ßV[­„𥺮۶—vv¯=!“»ÿþF¤ÜK¾_»úG1t0,k”ª ’;úApÖš6Ñ´oäZV/ž¿bóª[.¸fð@·Ûu)q1üšÑá¡f³ØHæñŽŒyq©80*¾_•bõ€]ß[|_¯îš¼:Ü.¥å3¸„~·pÍ E×DN(0ÀwŠË»ø~m³_\ÞÅ÷«òüâu(¾_•2yrm˜ÇŽç˜L8 > ¼gY»6oüiñ÷ßÎÌÍÍ>†KhzÁ% ßQ¾«zP‡M‹ª(5cëž ¹·fxp‹š–8ù‡>­å%¿»áœùßµµ×[­aâ„—11yÊkæ0BKïþ—=ã[ 2x0Íak“TÅ8F>1ÚiiµÕÚyà•]ØN[=ðø“DÛ»–¢aäÁ?âÅÀã¶f9jô`‰ .Á8"ÝhÍJ¯XiŸÃñCõÉnº$>.šw®›ùk—”tÅÊËËqBËô-ŽÏìyê™;õìÝ;<*ªyhxx¬=Èî‹ÝU¼ŠÐ"u騮½P à&Ïž}egåÔ¦IFq6¬ý.—Û•_——™—“±{ó¦õ›~[±U¢öˆ6j””š¸Í¶¥ý•,­„Q Rшèè«ð­7™G©eY·6@ ÐJ‚¿}|û„`9­µå 8É4&¤¤NíØW„aÁô‹úöþS%Umæ$f8ù´Úª÷whJÿ}í´mÛG|õ6Œ7ªšôØWFØhÔ`ÉdD†!™‡}}àS¨'_ sâhnýë¢5µE¨g¬õ€ÁDñ8ãù‡n^³r=–MØ&°RÚ'¬õ=ØlÔ¦]»ÈûyìÑ" d:g}óÕ”ŸøacÓ€UË€;|ÿ¸hÿA¾oEÚìŽ%%‚'ž³´JB wrÈfëæ±ÎÖ€DXÌoC+‰ã!¡íÁ:ýu©Á!`²( ò(½  ôgì’ñ!4Jw(d¦§¯¨áµžÀTš¥`ðI~Áw“ëskX® ª8¶ÓfÑáfXHHW2Ž¿ +ëøK ªUd¶Qƒ%™ÄD„ŠÝÌÇ‚¨g'G“ÈŸãÈÈÀd°ö¹â`M{‰q§÷98cçA°Ä΄?"Û¯6ÕkríæÍãCïyø‘G‘C© äÀéG×üx@i-÷-²$P èwƒ/‚ ¾ƒ|ß–Ž˜r ¾‹<ÏwÓ¢êK€Q»a3Ú6‹‰à¶EMP4'?pÈÞ U÷ùë0À>>9õ-|Ýÿ¤E‚‰ÓwF' ¿ëº|ÿTK¡AÁöVäWód­›Žb¢Âa¦í­¯Óéüàù§ž:Øjß$Àµ2ÚÄÉb$èlá<$‡Bö·%ŒÓ&.Òg†°ÌÖczÐÆÁ˜ÿ6h¬(ù:lKMâcöÄ߯GDFuƒ Hž5+–Müñ+eïî=bý·$P¾ »=ß»¦ÁMìô÷y\k”ô½8dQ%Àï— ›aDÕ„ïeùP·1¸fUšª”Ã0óõÅïPF&'ޤMËØ“ªp,3Wþ¶EœÕ["ÃËr›¿}-(LØ»tézùn%PºYWc™7”†a]—’/òG>«ÚVCÓ¿Gyëåvª¶xvß.å]Z¡ó›v”ŽmšK8ÒS§õÛ÷I:Òß]Ú¿WñS5¶¿û`êã³àÉ_¿zÝ+(\·ºl“5V§ŠÔèÁ—VÍ¢ü3XZ;÷4X"ô¯jŽèåü71âKÇAל=ã6ÁkÔ(©Îk%.úem‚ŠŒŒ4FŒóBDTTÔ]Ñþ=»ÆOŸ:õ½o­- TB|ÏôÂw ß;½æ6ÏÕåŒ6×艳öAøêåûÅH ß-úMæüºA²só)Ô&Ý;´–;] ­šÇÈ7?¯’¹Ë7È Ü®úRýkŒóKIhÕ\þ|ýÅòÁŒÅ²zËÜŸ'íâãäÚ‹Ïþ}:éKÕz÷ ™øö×j›QHã›EË5ž*矪ç{N¸¼Ê;_ÿ¼IãÝ2ì—ŸTÆ‘¬ùzÞJ9«Oç€K^f™rIì­[·ûóÃ¾Š¾®â¨&Jö Žñ]­kò¶Qo‹q£â³Â<ÔEûfþ*ÕV‹3,¯üíŽâ‡ëdÿ×u;.7߹ƸѨÁA†wÁŽ oBœl?$±®t9nop¿'ùŠƒf©s«H±©œ<Þü<ºÇpí3ÄA;®õàmVƒ$ ”ø!'ÕË`Ãûèºûÿè3ÿ÷,‚Z\­Ÿ˜™ùÜä矟¢÷êú©gŸùÜÓONj¨ü7`¾õàK¿o0iФ÷õù\Õ€cß,µÐ‰¿>¸ã ó‡Åkä–+ΖÓzv”ãÙy2mæRyñ½ïd|Ò°à5[v’ž”ů¤Í”½‡ŽÊ—%+7î–_Ön“¤[/—بÙ²çP©>Öïþß_¦€ÊÂU[Ãk±œÙ«£šÈ¬©ºßsã%Þ^£¦ ¬Ãr8kÛ¼yØý?‘d·_ê{´aü{ôˆá9Üw¨7|mÏT}¯Ÿv¡BlÔE»äŒžrJ7¯Õпþû­\yn?öÎUÖ’V¨bÕ¼è¢Ó{ÈÀsúÊá#™òùœåòÕ¼’xë%–Ê1ìËýšÛú¶ÿ¶uL÷úý{Üçôwß™ FýÛ†îGJä¿¡¬©×¡Ô˜WÅ›ü3HúvŠ—õ{ŽH¼5²"êRñ|ïƒlÈ…Ü#wЂ׵UŒÊÿ£ùfš0ñä`M&ÎnS \ü_Tì6~°4öåÿŒ ¾›•%!¡î¤çG=5Á»Wúÿ¼üÊ“ÝAÏ<úðÄÒ¯òžyì™gNY:kÖ¦ PÖuF‘11âaåòWg 5éNŽk½ð}Ó‹>Öt$R·5Õß±º}*žæt¹dÖ’u˜î­md ElÍ¥26åSY@sù9}”IÛÊM»}`‰Û4êÛµ|9w…ÒõíÚ^ñ_Ú̸:‰ÔVulÓBi¥–­ß!+6îRÚ¥1˜ivË@ùlö25xœøà­r0ã¸|ôÝbÙ ­T\t„\wéj–þ•~.íãåÆ^å:qœu¿÷¦KeÉšm*oÞ×^¨éÕŒmTü^Ò¿§fC­YÕ›v©>÷r \¯>ÿe:U?'Ü\ ;ÈÁg$>þÔݰð³üŠ~ìˆÄ§üöëcS·Q®+5 ©«6‹öÁ…D«œ¸ØH錃¤¥ó_ÿ¼ZÆ%y-i 8áͯ…m¤O—vò!ÚXs´ûý‡ÊZhhº¡m]{Éé¸?Þ×n¹ò\h`WËÑÌ9«wg¹q`Ÿ9Üâß¶ÊŒ¿)Í,ß je#*`æ& ­ãÔ²sš,B9¤Õ›÷È¢Õ›å’3{Ëÿf.‘Óz$È-Wž#ÿœò…ÜzÕ¹r&²s ä¯~–­{K§¶-¥U\´º—ÿN—^œÈÈ-pHÍö ¹+ª:}rWH¤'Sz¶DÔ¾°` Ý8y&ïºÅpÝ3ÛL4 ¢I¾°Ú§‚ëF»Œ{eÒ%?`d~°~á|†ð,·Î/îÄL%µså^Û<¾ÕÄK† áDJ¹×Öð5(®ÎŸY×u Äçù¿Cú½â;ÆöÂwŽïžEµ+:êœèãÀUŸ.mOxvK˜ÈÅcðÅÁ#éì>](vªmþ[ €szÏÊbã4¬¤“·>Ÿç3+ò]XÆÆ.ø?è#EÊ8ž-Éÿ›­s7CËåöxdÒdz$I¶ï ¶kB+yÏ 9_7lë%ï]¾q§8|º`p›™“/Y0$í=tD¨Õàà’@kÀŸ?ý¼b‹¬ÙºOþrý¥rý¥g* F­©8?þ÷ÕÖvBûÖ#££|@Éår>7vdÒ¨Úz^%ËÕãJµÕúlcº~ù~O¯ÉA3¦~_úØ‘Øf¾€f'4Ä.w£­ úvÁjuŽÿØ>øn¡œwJ7hSûËü•› á¹ãY¹ò߯ÈÅgöáЮîÃ;ó3ÎW„¿n€:5»KÖm—g÷V·9œNÙ°c?@Ü"hv;‰ÎgE> ð¾’fÃ$nÛ¾4¹ Ôøþ²f»:Μ„X¾a—<ñ—Áòçë.Rüß +ÁdYmÞW@± ‚Ašè’€çåxf6? º=¨ãý_Ð,bàÀ SB®;ÄÇJúqÄ 8¼Oú嘲!¢½j˜¨Q"PjéÜ/ Ñ6iÝ,/lˆâ•<“wÌ65övX•úq×dqSSûº]žPç ca¯V-ï;}º÷k_ޏµ¼Ü&¥^aÚd€xÌ6G;ß¾õ¦Í˜061qîøäɯÁè²H[ðÜq“&;2ñâiÓ¦…l9|äeÓ0o‚ÄáÔ ILüœŸœò#î}ÖpK2Î/;"é®qÉ)sa_ÿ¶GLtò¦ÇfÊÔÑ#“^àõ§Léív¹SQN|j1r š8vİ™P4‹íÓÅ[fÃ©Î•Öæ¯¹ d~ÉÛ/ÐÖj☴)RK^3<°ÐP Cг]3AÈC‘£û¥v–l ?­^|˜è£DÓ;j”ÚD˜Ò!.\ÂÁy$¯ä™š¥&n†×ßËê<ñ¿ÿmáÉÊý cxàfW¨Mn2dˆã„ +¸ƒn.ÂôxFa†è¾žñ-GlM;rƒÇmþ·Ï3"ñÁq“RnF¨£K»ÿþ#,rszúMÈ·#_&¦•:»=æ—P_áù6Æ9†Ç|Ì.ö[žLz`Àî0ÎpÚj† 0C\v§Ã¹`Âo¼ËÄuaAA‡óÝî±Ýã[.Ú”ž>Ð0ÝÿÄ þ`‰´È’@S‘@½Î„5ð!eaf½8Ñ̇.ƒ6´i+«Vb10Ô&xúš2ýßðßÃi5fÉ×JÌ‹4àÐ×è5}½;·•!WŸ«Ì‡x\OArVMÇ0cOêàçðÞ&KG0»NS>šX­Ü¸S¢#ÂÕlþ]×^¤oõ­9íк¹oŸ‘Ä4Qsµy÷A¸–­ß© £éiòçG«é5±ÃÔŠA4¥:ôâ«ãþùjá~½¶ðP­ç×GÓr,mÍ` ÅÉ¿mD!Õ ÍÙ.Ñß*¢©›&äCóix¨"Hyöíoõé KŒ ÿ¦g ÛÀô•—ÞûA^þëí¾r¼J"¶•]а^pZßi𶏑úÀ$–Z©yË7ªwƒ¿ßÑŠ´y_…Ôz¹ÜÞ9Ì~(wÛžÃÅ/á~µÚHIÚ±F–hÂFÀ¬€Hdd„À^µ{› ±“íG³åŒì…rÔÞZEÉËn]«y˜˜G‰áÁ[;öJœë¡‰µ‹pA£!‘‘áèd¸ŽP¼’gm†h Çâ§n$0eʔബÜO ꦞhH¶aßðxâ%~±*ÊÞ‹£G$¾ÃëÑ!|†\S^ž6-ü±!C¼SS~Áï|04E©pñc³_Üm›áÈägtÍ}í©Ä¡ëž9´è.3(õ‰÷äh«æ`v‚^«°ùx¦ñÜäÉËÝbô£æªªÀ¯èÖ–%+4°T|¶¾¦jÚ– Œ¶nÛ^eV§Ë¥Ã9ÍÑ®<Ï;“Î㌠·z ÒoÄFûLðôõ\3Úìï/?[¶b0¥TçûŸ-Ú¾fLØ•DÁ0=×D3@ÒAðÒ'Ò¡#Y¾Ù÷súuÃ`p=’dFA+äOê"¿m[6“Å~³âô7Ñ_`Î’3Œ85aþ¤Á›??þçkj;þ$þð‹ÏqžåîØ¼)åíWÿó.6 ÛEM=­Zåh^ôºÂ…ÕG+Îâò+àã‚ï #1î=¬æO¸Œ&xeQiÂ`±‡ „ñ#þPÖíežã»C`¾`ÕfnôÅŒŒ\QƒÊ ƒ ˜Ñjâ$‚¦þ½;Êw˜¸ ö©[B˜ãЇ±0–ÒÚ¼¾×½içAØ…Çxß@€;õn{#wê¶Àu£§²[G#¨¾,ÙAš%¨Y£"#¥(Ú„¶Ã‹iÏ’½Ç Äæ8$q¹ˆ‚ˆŸÝ‰4>¶0q5'ž Ó%¡ž|Ľ†KF˜´¬kfwH|¸ à(Lñ~dT¤Ú&¯ä™PX‹š¦»<0_3ÖÞp}Ǩ¤~«®40a¶\—öå?i²ÓÈÉaâ‘“ÀŽ]îOà|ïôAf;}-æèm½îßl™ÞF»,j {üäÉçÂüï±ñ)©íà„Ë+"''§Ð´Pßa­- X¨ ptLξ…‰]K€“3zv’cÙ9p_)ŒÖ¥‰‘Åf,\ ÍN®ÜsÃ%ú°ÌY¶AiZCó´?혈,4 ò]T… j‘hF4šª›ÂÖÐCèïSa6G:·_gùdÖRYºn›œ _’’¨gç6Êgi̦ztl- Šù’0ßÜO§üî8Ï@ÌEŸ­Ú¦¼|‡|€h€¦šÒ2ŽPúBï7†u ´±Û,‰,@; ©Û¼å›jL´ôÝ[3{éz¹ù¨%:Œ‰š§–Gô•ÚsðˆÊD ŽJoÓ<EþU¥•Ñ»s;e2ÊIŒ<ϳ@;ÌŒ…%¥f±±j›ŠÑð,­Ru¥ÞpïnÊ”ŽN§gªRC¢h?_ŽNJ|¾.j„¦º3×åêŠgÚ*ß"Çã0 \9lذ\‚œøxq” ˆJaòÕ3BoßÕ‰êU˜¡cFúŸ0#Q±ž¡”2­Ã–, TOì›Á µ4Ëã±’èŸÃo>éð0Àã’‹ ú2•F æ:ú/%žæóJ:—£±܈ì=`ªTœ-‚©â4áÇ5ÑÔŽ#  &M4_òwdg` 浡 ùgP ýŒ’øÑåVgͨb T5#›‘X÷ëâ~„­±R]µ1-¿ä§ïÖ›¾õŸΛ~r +α#.jòo3ß –‚lôŸ‚– Ïój•à£Ó;j””žO«dvP‚k‚ÿæÌ™c_¸~ÃG¨zsoõÝA†yO]‰M6Ùt™ÿ—Û˜1þüÔä650uMÅåÉ1+' å(ÌVQò7ämJhÕÜ7@KkóT&| ³\Mg@“K mS§&–øC0¨ø‘òî{CŠ‡Â¦3`)¿,Q»ÄÙ¦šÐ.ég±qҴΫUòFæ£ÉÍð"ч Íïxž¼YÔ4%°pýÆqÐ|zcÝ"×Mݸýéá‰ÌkPeB0‡qúæ1#†m.>32±Þ”ô!´HÓ ¨ö=”Ägß4uêÔ° ‡#ê¯C‡f M«€)¯‘´¾ëâÇ6ü!}zÌȤñÐ0ÁK„àIçºø}þç¬mK–, 4d ÐLñƒ‹áæòÇ>þ÷Î’^ð«²È’@MI€Øûœù—ß¶!§ÒF•Ǭ'Ìj¯¾À¿¼ç0ù¬A¥+ÏëWÞ-Mâ|“K´Œ¨m&n3ÿRDx¸J*øƒÃS|”è«DâàøËX¦Mûwohµs" î×?™ß1Ê^—vÞý ­.µÁo“KZˆJ1´íTà~K $!¨m6u1¯:`‰Ïô>Ïû¡äÇRåxHRÛ0ÏÓühþ¬uÓ“ÀQÉxÚËN¬9ÐRÏþmÔë`×ôÄaÕØ’€%K ZôaxðøhY2äÊsUBÝ]±FÀüèäOeÈUçø¢+Ÿ.QZ@IDAT6‚*U« ³–®Eh~¯‰h3„ì¿aÇ-*’@“KÁÁ ?bÔôÐ9Nû) i¤×E¢«ØŸAâZ/|AšHúšŠ•h]Õ%0qòä‹€ÓGèº6yèñÄÄj%žÕeYkK–, šedªþ•Q½ŠÓ±Ì\YøÛ•062¼(GLñëyÿ0è~øý"ÉE´2R0ò7ÝvÕy¥&ä äº4FÞÄ«²®”}Ù®›Q mÜq@Vn*Š×tÝ¥g4ùèwÅÛ& –´0üA ùƒ$½¯¯­ÊÚ égU¥ëžÆ)NØ››ÿ^¡£šñ „Ø8kkÕÊ’€%Æ&ÏZ.LªIbR׿1QHvÛO.B’ÎÒèëŸWª¨³ÃþpùI—Ï×È[CS †–fPú~Ð^ D¿Û¯9ÉâNª«u þ$Àä±o|6WZÄFÉçö•Øèل䮇(˜¡²>;ñÖÊU‚\rlø!L*™¼øõÏæÈi=:ªö¹}ïaÕ×Ì^ºA">œy‰æüº^âb"eã΃JksvßÎrvß.¼,`ˆG¾E²iMçôëbz- ¿µrÍOz“€Æ«ýñšÇyMå`:Gó¹*,þeù'ýV=, X(YÍb"Ôl{Ò+¥ò#ýoæRÉEòÕÒèž/‘ûoPÚé{œ9j˜GI%F'ûÓï, ˆ?(#Æõ gΫä鳋4ürî D“ÛªÓ§ÜŸÚÄ7ƒ%’M™©uïÐJX`iÚ¹?Möò¦(ܹ?]%Un¤±½»´•·¾˜‡¤ÎÞ úúú\ÓŠêó¹Ë‘‡ÌˉÀnàÙ}ê“¥€}ö‰­ `Ù´³$Ð8%ð|JJ/‡Çü›¯v†ñ·1‰‰E)æ}'*¶ñlJÊYnS>çÕ˜‹8‡Ï“™É}»iôÔÈ¡ë¸]WäpKo³Þ†ç}âÿLÖÛé1¾@ÈrõeFþ¦ßÀj3ÄSqÁa xÆÚ` U×üúóhm[°$Pq Ø‘C°}¡æ$,Ä.¿®ß!{zvl£fã™`–!‰ç¯Ü,#n»B–¬Ù¦ÌÞï¼Ö›%á8—Ï]¾Q%¶½¤ÿ‰©=È÷‹~“õÛ÷«è\vøùvhÓB%ýä,ÿû3 #yµj#üÝÒµ}ý8¦ï=|D>þa‰8,ŠŽüPä§M‹“M +.YëÊÚ’À…§wWÚËÎíZÊèIŸ¨ÜD|»2ñ,.tì<¡X`îöKœïÞ¡b‘ »%´–…þ?ß-üM¶ì9¤žY[uªL¹s—o’ý‡©[¨øýÀ³| u+SNS¸ÖÒ,5…_ÙªcÀJÀá1þ漓†Ì“4üÍê0‹œHËÇŒHêÈŰÙ^…]¢éy÷x6c0ò0uíß²gaü™Îf?7éu+ÉCu„u¯%:’@ªM»dáªÍòÙìåÒ©mKéÖޛ̒ÉXiv·nû^a8b‚ËÊõβs&þ$Â<£WG¡¿Ä¢U[NàúKÌ~Ó”íÙoEHävêÜ7_¦Öï}»PÂaöôðŸ®Á@6N>B@…ú Ý3ä£~ñ¥ˆ°¹cÐEPª£‚ÏÔÉŽ jI¹ù^M(A7‰&yÔ2q9^ŠVÈ? SVaxxu3þµi£7%Z*4Ìw°ž6¶í9,¿¬Ùê{ú5ÈÃdzŸ8NÚ°4K'‰Ä:`I n$0nòäâ6oàÓ¨Âdé£^MPí<üäÉ0Ýæ9cG&=¥Ÿ0~RÊ&e8.9e. Oßöˆ9 Ú(Í”©H(û¯—<ùeL¦-…O,Ìv€òçG[DØ“£î½7ç¡Ñêê2åmè±BÇ€ö*ø/cFÞ¿‹ç*KC† qãžÏÆOšœà÷Dl7OÚÊ ÃºÞ’@‘CcôýÉWy =r;LÏÈHS~KÆ' ö Bõq®7ÂŒ)îìÕ2Q3ó´Eš6í:$t>r«ÜD«àŒÎˆ²90óÛ¾/ ¦Ë%:"T·ŸÈ+ü V–´h­o¯õõ˜^MŸõ«ÊÏȇE‚—; QjY‡<Ôz%áÖlÙ#çöë*Ká›Ô&hI[v<¡¦}º´.šÜ0]cûÌ/pªöØÀÿ@ú1ɃÍNÙ´†•÷ÆøÒ·Äšy¿¾˜·ÂÇË=; ‹J—€–J—uÆ’@­I€vf’S_òÍH™òîÓ‰‰+kí,Ø4£ðánYìŠöóLÃÜj† 0C\v§Ã¹`Âo¼;úñ>ôÿg7‚îo¶6÷pÆdOžãVÜ›Âû]¦ñÀÕ»=ZµxKú‘¿š— 㹪’-Ø6Óíô<^Õû­û, X¨; ÐI~üˆ?ІVVoÞ#©Ÿü$‘H¸Þ¿÷Cÿ=[_œ«<ôsÒÔ±MÑ6õíÚVæ,Û "Êþ¼b“E<¾ Žó|Þ¿^À]Exæ^˜ÕXÚçþOf/SéFÈ@td84J¨Á·—#ë J€šÈgßþF¹åªs+ÄfG˜Òï©×¦ÉE0ã»ùòsT>¢§^À.ýª ¶B®¡‹œ.·|2k™ÏO‰Ú$j•,*[X*[>ÖYKµ"‰)Sî‚/OU¸aäŠMF×ʃ*[¨”úĈ{ÔÔÚøäÉs©ù ñ!‹1LãÛ§Gý•ÛRRþç1Í¡ØLñFóËëþÙö£GÃm†ù,ö*@˜2…—WÂÂÒÄ™ëµã©Z Ö]–, Ô±ÄèÔî **ÞŽ}‡}`‰QòJ£¶-›Ébø0i:š™£7Õúwž&“¾6ï:ˆ({§ üêxX¨wóÈŸ®–Žm[œpO]ìP ñ)"jÓª¥ÁH\td]<ÞzF5$ð܃CÔÝ×_v&ÖLíâ-ŒËŠâÈ«½ãŸf‰û£ý9Ôµx¿çÆKyÚGã’nöm×ׯ·óWK¢’¨©½ ’é¯dQÙ°|–Ê–uÖ’@KàÕ3B4&è‚ Ó|±:At9UY›¾.Â{wÏøfËt9à+Ëc¾^¹Ÿ–ûΉd£o‰âþþ\ÇÌÐ>''ÿG—Ó3 9§£ëÙÿÒë¯Wkôbft@V%S>ͧµ¶$`I n$à‚.1l€øßL9pKG8ÎW„zvn£Lç­Þ¢r Ÿ~úuƒtOh%—œÙKÚ"/Ã:“ºÂ'**"Lf ô1MïøÌµÛªÇÿ‘ånÓtÐ(1‘ç]×^d¥r%XÜk Tδ¿“¾§8PÒÇi½dí6Y¿£èý¸ñÒþÂvkQù°4KåËȺ’@JàøŽ]wÃ&.AjÈÁÐè(y¨}2 ¸™^¯U<íÙä7»»LGˆÿƒá/äðß?aÛc–xÎ&F¶iY£“†Ÿ[Üçj|rê ETfÇcºnG7Vä¸P™›­k- X¨S 0‘ì„·¾RÑ´zvjƒ(pçË9Ì)Ó @ˆæzï~³PÍÎ_{ÉQ4¨£Ès–+ó>úŒ ¿e 2‡ö‡òÞ·‹dtò§ª¾ô;é7üæ* €+*08¿œ»R™ò†\¾sÐ…0Á «hÖu–þŸ½ï€‹ëºÒ?ôÞ„I¨ Ô{µU,É’{Üä–²‰ãØIìØ»)›º©›lê7›Í¦8eĉ›Ü-Ù*Vï½#! „@ˆ^f`þßw‡; ˆ2À0ÌÀ=¿ß›÷æ•ûîûÞ}ïÝsÏ9ßñ*Ù°òrÐAËXÐÆÁ5Öˆ{eÉ=œÌ^ ×´Àÿö÷_Õ±JÿïkŸúTKŸœéÆBb§ÕÖø}ÖJ Æ­ñ r2¿ÆÖøÓßýá!œUQ„ÿòÏô•§ž*¹±¯ùåêյ׋ŸF=Ÿ  t¸*v~˜Ùà `è#¼uŽpjO¨Ø´×u$køÜªåˆ£°)«‰!î¸yš:äø¹\tòNËOž{X!.Š9aþðæ6°îWÊR:¨ÉøÌ*`Ï"ÂZŒÿ´>mÿŸÌÊSÌ}x?©²«Å%*pF ¾ˆsA½wQÝfi‘]2[ñ:ùbu}²NÆ Ï'o‹©TE ÅøÄŽoº¾²û ?zëZ¿ùùÏç‚Ånóüî÷§À4—7;X·òzzþ'žxÂöó6Úü¿ýÝyäLº\m±þ¥«åÚí N*§úzññF»Ì ˜Ë:wµ³¿AÀ àŸ„!?“+ƒž¾ Æ•VÖ(W»39W„É>™·ÉU¨$õ¶¢t Š)Ðu§3)!V> ?£(¹Þ ³ìK¢ÿUå‹HbH\t„ܿ̌AvõËRW3ûz€b•¾á<< à·Ï>ûh•󿇳黭‹DÎ¥gV¯^z-::à_î¾›I%H®ä»Ï>¡—9m¸sÛw¾ð \›åß¾ð…øw›^óg?Gw¹  G÷!Ѫ¯ë;Ï>½ë9µo|á g±Â™*ü»Ï=3½ÅæAÀ `3@i|­¸B˜^\^)ñ O kÉ,=æä˜Ÿ¹(öžtž,ùs>~ÇÍÈñÔ»–,ç Í‚A ‹Ð ËøAÒúS˜«ì‘•óU²ä.5àw7ÊÒ€oo!À¼JÈsäà' °„ʯ½un×ót—äºc7–‘·´‡™C ƒ@»Ú¸/é"χûO9ëGö¾Ý±lbFQr‚b| ZcßÜrÈÉ|G"‹U·ÎU¹¤|ª¢~R¿U–r¯«Ðâò*)ƒy¾4EÇ‚ø þ=ª&sVÉdP\”L—ª2¦÷¨@?:ø™ïþtNCƒíNDñŽDn a`fó^ÂnâTWkL7 ’ו\.«\ý¹ïü¨›¥™Ã žEïÎJÐÃ_A©—‚ƒ6üþGßr²zöL¦4ƒ€ÿ °ïdV‹Àx’M|ôöíæŒòŸ+35íϼ¿ë¸rWÕ×x÷â`LÒͼ‹ø•²dC2-²y0Ø“ éc"Â%&:\Â`^hRZQ'¹ðÝ®¬µÈ{ÛJ\L¤,Ÿ3Qn7IB€Gú‘|ùË¿ ¯¯úçàà /ÙmCÐ'&2¼1>62 <$Ä·ãï l§6œÝÕ-Iˆ‰6(&jX?º?æRü‹ÕÚF3{e%Ï×?ÿÝ_mhhüïá¡Ãÿ÷?`\šƒÀÀB`7¨Ì·Ît^ôˆ”DyìöùÊɹÒ,| &n&‰º¬ÎHwä&Óë̼kø†‘q!O^ú`¯” YÝÐ’Þyó™8:¥×:»gßì][W/™ äèÙËòζ#êåNÚVZ›Á2äçJ5¾ÀOí;wÔ†×þ)°1`Øä±Ãìó¦ŒEbÂÔdk÷‹ljû3²eóþ Õ@˜©žù8Œ| 5àPc©Ã‡6_fd9••÷‹k W¾ôä7ðäÿýìPßd¼÷±ÛãýêümÍ.‰Š“‡WÎóþÉÛ9# œÊ–Çï]Üb£¾ø‹—äßž¼Whý驰ùëØ9g1£ôöÑÛ8s<97˜ƒ€!@öH×vK%‰ùÉŒô ¿P–6¡“ùææC2•O¯ºEƤº—è®gÐøÏÑdš5a„šrò‹äíÇ‘í|«Ü¿t¦¬˜?Ù™•Òä?Wä¬);oAŸùæ÷¾ò³¡IqöÇàþªX¿3™ñ¦e:–| @È‚©c9œG‚ÑW7ìOÉ/,[ûÙoÿðëúñ÷~…z7újÝM½<‹ãìvߺÝ)‰qŠô¡­+e&OÈVx°ì…ûº/Qa ò‹±9]m3`dç_G‚æΫ&E8ÝïŒôŸv]"=ç&U¾±é Lƒ•ä /3ŠR'÷œŠä³,Sx½ƒ¤yî;)V«M0ê¦éN;)Â6SâW)äÓ_ÿÎW (ýbî¤1_ü#AÌ©áoÂE¥•ªÚ!!A2yŒñ¾ó·{8PëËçíŸþHÐÜI£ñ­ø¯§þíß¿ ,ølúÝ€Å@½‡=½îzkƒ¼º~ŸüåÝ’{­XWY]+¯mØ/ìœ=ÿÆÉÁÜU¸‰bOœ¿,¿[½Y¶ÂužåP¸|öÒ5$u="/¯Û«Ö1ùœãõH˜ï(<çÙ‹WÕ2¸ÿ‘3—ä:Þ§.¨õ´&ÑôÛÕ›°íb / æl¢ ÝŸÞÚ&d²kD> Ïyõz™Ðjæêb§6⇃³®ŠRw>rÛ|£(i€ÌÜ'päR:äìç‘­qX#ýܳÈg°öYe‰{ºÞ½¹ù°LOޤoô6£:î´âD¼ˆÛš'ðÁÊU “c”Ðç½h”Û®3äcÏýë]!!a?¢djÕÒ½ÿg/5ðÇjÜ8ÜiÄfŸA€ÏŸ?>‡H¢ü‹Ç¿úow¢rüv…ÉgîRïUäÐé‹2|È I†Eç§/¬•ZK½X@¨D%… êÁñÑ-*Àí»á¶÷Äy¹izš0ögÍö#jæH¢SQU+3'Œ”¼‚RùåKëeèà8wû£?¿«¶!‰¶l=䈪D®˜õ{NÊÁ±RTV)g.’‡Däƒ]'dËÓrÓ´49~î²³£ÈmyÊ”°¥s'Ê:Œ¶μÈÕr" Üë[q£2nxŠZ§H ~.ÓZÆÃåÿáó„Is|½²a$¹”b£"ä1¸Œ’*܈gðÙ7Ùí^Â]ïY9Ç3W;ÀJ!n)ƒbå (œU飅‰ “ ÛdÈÔ© âbãã?<%Áþ©{ûu§ì\™´0ÞΈAÀàsˆX{xxäŸV®|„½cŸý~ø#¾¾Zçô‘)²xæx¹î< ±Qˆu¼ÏèòÆ|G+æO3\¨ɼ¤¦«EeÎKùÈ’Y2{â(M–Óˆ5Ò2Š×'?²H&MÅ11&ËæN’Ûoš*£‡Vënžž.ˆ™Æä:“¶×D–Ô2étv¾,3AæL­FÑuù´be\¸¢p^¾VŠÜLrøtŽÞ,󦌑û˜ßç:*^´@i™4f¨<ªeÆý1ø*¥•Õòô•«k™6QKãGAko%{öŽùä[€z²Þ•VÔÈý·Ì0¥nÞsŽ?°t†”coÛ¡3RW_/dôa…‰í‘C!aÓo]ú,¼&† F)È_-J¼måU5N·’ ø»žÌÕF ~‡ŸC|„ƒXyhêŒñÿŒ à³ê“ß¿ׇ+ìšK(",DªÀ¾ªeÔDµÈo˾Yjº\P¢7;¿Ý‘HÜZUãèÌq㨡ƒœûÐE™qrZH(A«I8;›+‡çháŒt½‹sÎýb0ŠN‰nÎ©Ô bµÙœŠÎ¤1ÃeÎä1ÎãF6Õ[¯X»ã˜;wIÿ•©ã†ËËŒ “³à“°oGâ3t–nþT”Çû|&ŸÄ³£Jù܇Žîw «•mð5æ(¼!sèèöu¾øÇ}p¥¨ÅG®5âëƒñK´ÑÏ’Yþ"¢bbžžš6Üî1J®wåB^³/ÿ˜aƒAén\I]ñ1Ëþ…ŸG>—aÏ¡æ|VMü’ÝÂ.×6 –ñr¤êÈ/,•+ˆõ™ ‹Kk¡Âò…GW¨i>˜JµÐ…tŒ5¢¥¦-‹˜ 39W´õ*æyÄ#Í„5ŠróŒ4°2æH^a‰ÌsQvt9“`™¢2ÅØ¥Ã8‡¤“PS$7b* NGÝx~~ E×<-ÓÁvßÒY&ÖCbæ>‰ã_Z·G*0§ ¥Šr½Kme}õÉÊûa¥|NY¢Õã"‚E©1“áÍŸe×ñ,$sÄöåuGŽêeã#ä°.ù¤;Û¢²*­|ð±XN=8(¿–Ü«EÎúfÂ9Á0 ~‹ŸKt2Sxüóóqƺä·wÒ½ŠO=T^xo§ü7âŠîY2C»0jÍø¦oýæ ea¿ .vmÉÒÙ‘d¨üàoËÿ¼ü¡¬„[Ÿ¶ü̇»Ü9AÌH)°Nµ*Aì4~å¿^‘ ¸ìÅ!I»²×‘xâ¿~Mþõ¿_AjfeŠû°¯ñö¶Ã*¹½>fb¨>²d¦þkæŸD€.w/…ùF)ôZyí]?7>Yi?¯?t>#Úª”‘‡QQy”ºS¹u{NÁ2uNø¢}èÖY-Šøùß6¨\MŸ¹o¡²¸´ØØê ˜¬Ë…¨G÷âLv9úÝ12z˜ÃU¡Uñ-þfÁ1.ᡞ¿%ÌGE<É@4~ß!!!êáò¡LTŠ´U)|PrÊ 0¸Ø™G©H~øçRA³²LŸ{#GÏ%GÝíÑñq·áZa"̓!혌ô#ž¸ÿçÕ0FI$%ÄÊóßþ´s[{ w-š.÷/Ÿí<Žû¹–Éÿü6}òž…òqät|“¸Ö!tÏûÍ7?©ÿªù"ÄOq¢ÐMïËÿt§r¹ nùíºoZ»ÈŠ ­L”Ÿýó£JQzkëa¥ˆ©•ø™ Ëc¦Œ|@¼´9GAê@á3ó0bëÌ`lïÞ5Ÿ²,9”¥).«’˜ˆð%œ¥t‚?Y¦–Ë`Ý©­³ê¿ÎÏåÈ`àém¡Rö÷÷÷*?íÞ8ó0EÏRŒBÐÏjµú•¸V–BpýÁÁ#c"Âì®~ì½Ko—É—Yu“Ÿ>ýþ“A¶aÄ àï𹌉 o  ¢¯ŸYãŠçï7ÕúkEÉ][ìâîqTf¨8uGZ+J®e„aR+J\Ï4¯o>ØBQZ×A£(¹¢f–}­(éT$¤_µ|ŽŒƒ+«‘ÞE åPLïž«ÓÒi独 Añ1ÑáîßÑ£1ŠÏ­0¥ w¸?8Ÿ§¬UGÏ6û'³ ú:o>xVJæŸUËgJ‚NßÞrTª¡ÅÿìoëeÖ?¸|ü¶Ë7}·I—:oò(¹yú8UƧ¼ÜFÕµõj}hµ²F…èM‡åüåë •kç®ES%‡Öì«MþôÎNõRÿæãwvxžŽ®»½m±À³²Æ"–º:Ä-Y%<ÌAôÀQ VÂiY NŽ‹êægÓ®¦© ùh#ZF¤44ëu]™Sç¯±Ô ƒŸ»# vfBEɹðþ®ã2É{9rÜS©C{Ï]1]}Ú’Í ¦Õ/mDKá¶ö5ë &$ðzI ¥o#þù û<Õ&êhÄ DA¡¦U)q¾$ŒmzýÃx4Ç’.µ9™øŒ|’8¼ ×;RçSø-¿ŒŽ¤·7Òûø˜²dWš4›SÑ艀Â@&*'Ïç7+KYùrï’éð]nV–*¿a5”˜»N• }gÛ1Ù*GfM)£Så2”£ÇV̑ȇ¿ttdœSåþ¥ÓA…z ŠÓI䊡¬`oB¹¢Ýí˦ ·Qž6%"åuÐ 59ù ÆŠ/ì—>ØóÅ#.k$:…#”bÇ:0÷¥£ó¨ºøC<+jk¥4¬T–øÑÐIúºXToìÎŽo8A Œ ñ{e‰Iâ´¿; gæO8}E¹šÐoŸ÷àÚÂÑÔÿÃ;èä'Ë'î^è,žçýÞïß’g]‰ø‚((é‡Ô(jPP€Œ2Xž|à•ÏD°vç1!Tk óÕ¯¾ú‰Ö«{ý?Öl?ª}Z+KÌ›ò“Ö¨:b¤!&2BÑòžE„h»Éd»ÿ{g;®åã ³Öð!_/™5Á(K­éà?ŸÏ@ ŒÆ.|fùìúĨKU6›¼ˆ@$uØ‘ó%±‚hbõÆýr©)±.ë¶î|KfOð¥jšºn@€1üL¦Lšp %ÆÖ™÷7@Õk+z¦‘x°Zì q¢eI[czR|ƒ­Q¦MI•7·è ¼ KÐÙl€_çàÅf"C8”iiÃÔú ˆO:‰ü ËæNPÁ¢èxº2òÅžtñŒqR‡œE“0R½qßiÄ5]WŠV1Ü®>ûड़¥LB9ù-εdVºº¶’ŠjIAr½³pó›;y4rGÄ©ý³”<ÈAùØÞy/Ð]¡rD6<¬ gbÝÇÖöp9qR)L¬Rw¯Ñ—Ž+,)wV§;.x¼7ÿóÊF(¶ð½¿UR›$ $¯¬Û ¡Uå)a~‘íG2åãw-Ä}tœŽùNèzÉÜ%/¼»C%ªûÖg>¢ò0\áD\“B®+Ç©r)¿øÛû°ž"³Qn{ʇ>®/çO=¸TYv/äa¤x¿L3 ñcÛ¬ó¼Lý×¶¥60+ÝBíSrðÙÕÏq³ß³[¥˜ ½“u¾EÉ•Ö|霉²¨ :òÞ¯9ƒAÀ}JÑ_t°ÞÑ€ïˆQºl“ðÍ3â=|FYâ%k7b­ê•Bu¹L8ºK….~d‹< רOß{‹”W×Èß×îVn©î$Ô£ÂCwCÆ ZКóQxÎ39Wä2,Éóaiš9a”ÊÝÂz¢µ«}þñþ f„È}w͖çsäZq³ðþ3†ñ›OÜ£h‡Ÿc«|íñ»•2¹ùÀ™6ëKf°(xèg–s=9@ˆ€˜kö9,ðxnÌ×ðíÕrÛ‚©2L¹F ¾ŒJ_Ù°^$õªšŒÿ{ä¶ù2zhKï(_¾†þT7ŸQ–ªî¼{òkËø¢WÖD†ðP‹NG™\e å̸(̉4.qìä’#ëÉ Ñˆ_ªQ#ù¡ˆùace]äêa¡Ùzr-ŒIaœÑN(Ew.œ‚làyÊíOo§%):2„B–½Kˆ½ ep\´r…ËFÐi Êàèw{çÑåugN|iMb§QcÝrzáÝÑâ¼_ŒP—ƒ¤D‹kfy½Î9³Ó“۵èu´ˆRæN#{Nd©)­QÚÛøreüÀ; ÉÝzð ²{ߤ¬NÜæ)¡B²#µ”ÝÇÏË(HT–´Ü¿Ìá2Àço/êIE‡–¯SPžhùIIŒQ•>ººc]Ú°ç„*žùÃè~—좰ÔÚüà=sÐ!«öáèœ*“üÿ¥OÜ¡˜(“çÔœ.t‡e¾–Ic.¯úÑ^}ï¸yš>| Î]ŸÝxýýâš/Á=7nã!Á’nóþ|qüÖ–Tâ[ms^Æ Ø( ÒdªI¯d¦„††`P+Rñ=ž‚A(¦Ù0â[ôÇvÚÂ|þ®Ã3¥‘£Úöù{kÓAÓNÛÌ ë|JYâõ²ïI™€Œó lŸ†N™Žép-Ÿ¥;ož7¡Ó²fÇ ¥´,®”¥É ˆ8qI~úW°áq„¹™–ίr8m>˜ 3þ«¤Ë»å¼·ý¸;1P©-bnFçñµÉ÷þðžêŒ-™™VO>¹›AüÀó¿‹ã¿ÿÔG:<>_wæZIò4ÎÝ©K«cúU§«¢ªÙª·Ëî]ã¨4·²R¨´SfO׸cÊšâê‚Çm:”îq¯b„õom•ïînÓ˜·åº9Êe¤ ”Ò*žçÖ¢óKEG9˜üêH]ŽÌ¹Ük° Ê¡Óõ®RPܬØ8W¶±ð/»]¹á1þï¿^\‡‘½òÙUËœ{NMsÄ`9W4-d#I%ed;!i°]“lNB'‰.ztÌ/,UAú†÷´¾M§ï3ýÜöÇkë·×Ä÷¿ƒ[0€²åЕ¶‚ßÇà@0fâ{ ï~sít-ÖR ÏNm‰Fl€«zcƒ¼»íˆp‹ñ›L|K7a#ÞG` µÓ¶ÐÕm—ÝbÆÏSL;m ©Þ_çsÊ’'.™¬rZBC‚äGO߯ÿ*÷ŸŸÿó*ç.,™'ŽRÓ=ˆÌuŽà?÷è2!·=×SVÌ›(KgWŠ—kîn›ËÔ\““í®5›ÓÒÕám%ž½é 0òMA)Â:wtž«‹rïñëKtu_sÍ*ß•‹žØ®ÒEž#Å´XQér•ö(‹‡6År b$,nt—¥OËì‰#ehË3/^‘qÇÈ×?=])œAèI¶W_}¬™|>[ð ‰ìZehûŒ%LD 2mr{  GóùÍg ·¶‚ByZ%ÍÒD‚3ÐññÆõ›vÚ9ʦvŽ‘'÷ð—§®t< €kYt£ÓŠ’ëz~D\ãz ÕZQÒû“A¬µ¢¤·qÞ–¢¤·³SIEIKGçÑûôtî¸÷˯3s"i‰j¢×ÿÝÓUÄ /"–çb{˜_nnëvŸÅ`°sUÂÈŠ·óØ9´Ÿ`Å‚Çs1ùŠrA*Âcwu¸R±rWÒ_, ØU䌢û\Fvž»‡vºió÷g\³Pi¹¡%ÇÕe®£gD— Z€ŽÃ’;ÒźÕÑqT,c¢ÂeÓþ ¡Ujr,‘hB ;‘Õ¸wËA61×Î|1ZzR_]F?Ÿ÷Ëg¹¿Ü3*I$øùpß)ù=bñjáê“÷qÆÛ2›Q”wš8âB|sù›×6«wFi ¾|¦ºwwL;u'OíÕñ°§Îâf9ì¸û@çÝÍÚúçn4çjœ}kv²\'ÿ×¥ÖŒ›ÑBr…îÊánöʆ½pŸÛ‚xºDkÌ}ˆrR}S1b¬Vä9Hœ‘•'öœtÒæ3YäÜ•ù„ÎDž¢þéÄÜEÀ=t‚¬ß}ÒÝÃ;Üïá•óäïïï–_½¼QµIJ< «˜;dé£P™¤›ŒŽ™êð„M™ÿåµ äàél™ ÷Ät¸êj¡ÅÍRg“_½´AÕ‰I›“t¨ZÛ«ï@%x@è£ë3Ëe#>ˆßóºJæÈ÷vG o˜ ‚5©9¬Ö¾o«†8¦d .•ÀýâEðõÄ»fJ‹ÁÓ¾­aÿ9»i§Ý¿—¦v;wì~/ÎÝ3˜ý ²²i 5uw…~óŸÈáVGv=’5´%Tžÿö§[lâ ô럾~²Ä!ѳJÛo¿õ©ePqùÊ'ïDÀwóñL†G¡µµõ9¿ðè çñ$XÐ$ \I76×ýIxòÌ#+”+\-¢àöª½€\÷sˆ]··ûµ>g[ëh•[8=])®˜ÐÂôÿþ¶ù«‚ìe¬R–˜ÃŠä¤i慨<ƒ€¯! ; ŒO"å°¹RQ ò"#î!@…’x|TÞÜ|X¹<Ó%ÏÕÛĽ’Ì^í!`Úi{ȸ¿Þ´S÷±êΞ>ã†×Ê›c ¾Š@]½‹eÉŵ²'õmOQê¬L* ®JAgû·µ½§Ç·U¦^GK­(éõ½9Wî²­”G&mæzÆ.2SzrW@Þ&ÆŽ¹Ö­/êÛ›X˜²û/´(QQªB €×A0ÄX>Z”ŒtâFü˜z ¾«n×K3G¸"`Ú©+=[6í´gøµw´Q–ÚCƬ7ôŽ”iqYÔ«ÌÜ %îÉn‘‹`Ìûõ+å-Œ"kÞ38,{>XeS%ƒ@»h×»ºúzÙFÆ;°h&ÄF×»vëxGî‰I1È"H|ôÓN{Ž¡k ¦º¢á¹eã†ç9,MI'®.VUñ˜ä—“ƒ€?#ÀÁ2=ÖCQª­µÈÞS9ŠõŽFºñ#{ éÖW.pÄ.¹¾ë»_òÀ<Ò´ÓÞ¹ï¦zWcYò<¦¦DƒÜ¹šA •¼ƒ€AÀ à-£õ6ÄÖKöå0<2&0Ô[§ï×ç!Ž•HN ´±.õìV÷÷vÊøØ¾"2í´gm³õÑʲ´nÏ)•P– D`thxr<·§K °Œ<‰€ëh#cŒ o à­ÉŒ4ø¤ügÌÙ&ôG*0"¨aƒ10†¾®A=?Å€(¡¯Û)Ɉâ£#Ö•ˆé;A«ß™ÌcR«”Ý(¶‰[fO2Ä¿º›££òººÍ´Ó®"Öñþ.ãßïØ_¶Æ]ìË_!,›ßãùpÿ™þriæ:|WBæ6éò·5»šèsûÃÕ˜k0ôOÐ;X• ^ieš&LpK†vyhظ÷¤Jâ:&5Éyßv9+Ç1èP‹g„û߂Ķon>(O=°LQuóãt‰oW­˜+g/^•||¸˜ó(+·@Ò1ÒwëÜIxV‚œå™ƒ€A }HokR–êðœ’`†£ÌÞv2?ƒŽ!…Š­Z{Od!ÇS^§§OˆR¹Ø:sYJIˆUîM,·¡¬:Ä“¹çˆ3ñ6Òuú²¶®-Ñ[”såºÚT R”L(6÷”ôC@æ1YNeç«íË%jÐoíŽcRe‹Šòc·ÏW}ÈõH¾_¬ŽZæ#ßàîç…?œ_),•Sòõæ^›vê9x}JY¢''æ2¨ë¥8j$ö|uÃA¹\X‚]­,;Ô¾)ì>të™9~„úÿζc*(ö‘³ÕÿHvþù]réj±4â%]ZQ#OÞ¿HÆcDB *YŸºç&‰Æþs&’?½½SJ˫ի 7sÂH}H»ë;x`/ö \'¿™gÎô®9‘j $ô9tú¢<´r.FÏjå§/¬•_~åcÊ2K…(Ïó¡0 n•Çe$¬ª }7e ¬³‰Ê¢{äÌ%±ßÏNF€ú°œ†Â´ ÿŠÊ*‘f¿,‡‚tóŒtyŠ-¹s&î/šë0ô´´h÷&Ȇ×y&r®­³Ê |çî¼yºŠ›bbìŽdɬ ø6ºßÑ×*ŠÆYcn¾§ÝÝ–Û4fT¨û²¦c< ß+¦ˆ†õuÿÉ ª¢$ð8×¼P †ŸA cFK”Ìœ«Š‚? }ÅÜkÅjßQH”NëÑ«öãè°&qƒngs¯ÊSÙjßéé#eìð¯)K<©i§ úÿøŒ²¤¯( &ÎÜÂò_\{Ð%aɬñp™"q0ºÊD¸ßia>Ž”iÖ´ÌÑ2-Óô¢šÓ,ËÆ¹úÃÃÎõ$“¸ —½ó&ŠŒ¬Û/CÇA1›­\ûÚ[ï,À t#Œ¹Q9rÅݧ1E4!0(Æ8Ê¿´&ö¡«~(»ÇèÛ5ž¯”¡o#GUƒG2/©}ØÎ© QFŒ}$#sI,®¯œ¯¬NÇPÎy¨e©#ÄÌ6ƒ@3Ú½ÉÑE¼dX_èºt­¸\ J+dòdž)·¹Y—Õs<#íÑajÐqFÜó1ÒÎw ½9¨PMO.öž‚SA›îM¼R~c§Ãejñ¬t¸ÚäHf&Ç;§‰Þ]ÒPâm¤ëøB;Åð±T@1Z³ó¸ä`°–BJÚˆY43]ÅF«n® FœZ[B÷:ÊÕ¢Ò¶6Ë5x+iá`z¨—½$L;Õè÷lî3Ê’ëeÄF†IÌ tõ¡¢áI‰ÂKúÑ•sÚ-28¨ù%'Î7›K¯cÔ›úéѺë0·2I‹ìÿ,\ðRÁ¶×Z†‚´xÆ8ycóyyýùÆãw(–¢¶Ö·>¶»ÿ‰c•Å"I‘-Ãî–gŽëAMqmÜ“VÅþ"®±Xa!ê9Õ×6 .vÒ e.¬IÉpohK0®VÓÏU’“ ­½!Ù£UʈAÀ Ð9ì9&ºÁ5J#:}ia ¥Îæ°®WàYßzè4,Óe=¦’ôÚÆý½¿&sa=>Wø“ç/+ s{îM´ä ßÌñ#eóþÓ2ÖÛošªFë;³^uŽ`öPX;7bnÝÇÎWÚé9(äLØÜZn»i ”§"yqín¡GÑ?z{ë]œÿÙ¿¢P±Ê¿~£ÂäÕ6鬕˂i§.`tÑç¢ùÂ5$A †e^,èþ•yàÈñ£R”ê®ã¤¸¼Jv9¯ÜòwA×"Æ+m;t´UŠvòjQ™ŠÉ BFRv’éÚ—yÉq™9פ ¸R Žž$Z±jo½.AAñ¼È ¨ã‚ç)P;)‡æûhxPØÙgÐg¬ÜkRË*Gƒ¯ ¶h2MZK FÚHËʉþÚìôpУ¢ÚA"Ü‚`Ý͆–$+­ü·whkD̓€û8;¢P“8zÏÿÞº-}ê#‹ä‘•óÔÈzV®Ã=é<âÏÂ]҉ܫE2±üæWˆ ±?x·Ð½‰#ð$T¢{Óæg å)·(ºéiÙ~$SÌsÎw̨¡ƒõ&¯Ì‰ªÆÚ+'ìg'ÑØqЬ¯Úi[rÀ<:<\µQ†„Ü<5­Ån%4‡çHlT8¾k!ê[hE?oáŒ4á é˜aI’’Øöa‹‚¼ôÇ´SÏíS–%¾49%£3‰FH&ºY [è+a,Ò½·ÌP ÑšÇãÝÇï\ á¡Ø½m®¼ ò†ÿüÇFŲ÷‰»æÃ½.^>y×y$?{qƒª:c8&üÓíx±_Ã5åÂÔÛˆ`¹kÑTµ½½õžºnž348@b#BÔG…¦gµ§ÎaʹAXf§€R Å™~— p³{á½ø@”È=Kf€í1¦ÓT~–Ï(xs›z>¾ùÄ=ˆœ"¿{}³ è&e+㌠žAÀÙíE‰WÀoÌÅ+Eòá¾ ¸'•9/jbÉËÀó(Gú;Ô–B×™{ÓU¸ùQjj#û$jò¶´Uoo×ÁŸÏ××í´-ì芷÷d–Ü4mœrÅc|-Ùò´œƒÂ?VÍç» lx…Ê2úÞÎ#rbóž~h¹RüÞ2Y‘}EL;íùðþÛ¥:ë—&;Vužš G³¯)S(€yBîZ8U8µ%$uÐÄ®Ûé2ÇÉ‚`Õp(p®2uÜ0áT—5­LÁÁCëûµOޮ߹x3Úƒ`ÍãdÙ‚V¸¸½½õÜÖS¡)ù,,[©1A ×@Œš_â¬1ïé9Ìñm#7NJË$GQýYž¸ÿgõi-#ƒ…,BÏûÓj¹½Ÿû–Í–;ÍP$#܇ñM÷.©Ê`;Ô²n9œ´|òž…zÑÌ ?A€ÖÆ,¹ NOS¤-d›7yŒÜ÷¹ö¤3÷&“ì»=äÌzwøÝêÍíî¶ëØ9(L”å°uLIÂþŠœƒtI¯=?…ÖRNLKo"Æ R~òÂ5×?¯o: ÍÜÏð)7<šÒƒTg>H&J’HXpÞÙ~L5¾¾Æµµ¢äZŸ(ø´jEÉu=•$­(µ\ß¶Žêª@¹îßÝe>´oƒÑ/4(@†Å„(ªrR0kŒ‰·‘ÞC 1.ÆYø5—ÑUçJ?^ЊRW.!4Ä¡¨ëcèîàª(éõfn0t_AŽv¸%3n)Vöéé-½FŠË+eXR‚b £3]}}Ù½Éõù2î®õô•eÀ‹ OkEÉ? b•Z_Ýʵ¢äº¯¯,·®¯¯ÔËêá3½emå`Gžù‡"ÂÃdƨaâ××753Ëù¨¾RÇ×7Rø ‹l”P(ž¡PÞBC©Ø+…Icî+õíoõ ©maÒÿÍÜ `0ôì5O½q†î•™uù:ÒjËçV-Z©ž½Ô¢ Ãg.ªX¯~ò.™w_RÓ½) Ý›˜Ë&>:ªÅ1}ýGãÜ×õðÇókìs¼ÿ©³ÆÚjì{5mÛÄáùzª3«çmž Xmf§> &ÎIqRT^#'˜ÐnÁ¢s1êÔÌV×f!f¥²ÄQQ:‘•/Ia6I{_X&(JÄ•øgÍ8Ö²}87Ò†€6›#¤dÃa^"Nqц‘°šC F€ôÖ®Gººqéƒ=Îw"×»Ò}ŸCŠNtoâ¨=¥=÷¦ $ÕB—àöΩ÷1sƒ€AÀ мaYº¡Ómk°ÕZ¬ÖÖk7$$Du"¥ÎR'iCb%4°L.‚²ñù·vÈPzÏBfð‰£S<ž‡©EEýäaINÖ;’9„@b‘øˆ &#$XO*MÄ—8oW)«¨¶7Øl––¬µ©šÿ{*©HZ|tÛ”‹jn‹j»§çðÄñ®mË®ÿ=Q~o”Ñ÷«7êÙeºÞ¹_½ƒ)Ó `0 ½€7•%ö<ÕT[S]TY[PE(±IZøÑ'S[(:óáX%uPê­Vfk¨àJÉ+«“l$£RÀ¾~4ö‹…k“ÎY¤Ëó:°êUÀµ« 8²_O2¾øÀ:Ii„»]°D" -1Œ‰‰–(øzs™¸_#^³²Ä{Á{b­«»ìz®%øÁ Ðmq2ÉpîÕb9“sE˜}¾9ˆoO‘à(j!âî([œ–ÌlÂëcÒÔ 8£Õ‘Á׃Àä7elªŒ6X)N®ó¾ª½¾_—@IL7œб—!U=‰œ Ö¹ÐWµôÂyq£ø¾‹Ãàéé'&#‡&*·ZZ‹òä…{`Na0 o)KìÂè©ñòù³ÇRÇŒ{Œ®u ¦Žm6?ö$ `| -!V(J®¬$ìä'×Z¤ÒÒ U`m¬Gh‰¥ÚÙYò€1 E}|ùO‹‚qåq˜G5Hî&-F¡¡aÊ‚Då(Œl±±˜¨0OâJ|[»àñ^ 3P—»Ëå^ùòåw»nìtSAb†û:´Ÿm ¹ÝqôœTV[”E0¢ÛÙís¸¨±†>† ßÍù\÷ñ•e¤°”Æ;r 4ÈÚÇ‘x/9’&É­ó&*Eª/”&Þ/*D[f"çYÙ:Ü'»„ÔJX@9ž:_Ïkõ°I˜äØãÄb÷wŸÀ} •%³&ʲÙ1Pâ ÑŠ“×*eNd0ú L0ËùⲪ~rEæ2z‚€7”%­$qNòù†“÷fÍ_qGùÁŒìX(K-z¤Në:õè$QQÒ C‘‚›^0YÝ,‹Q{›Í¦:¾ìü²S5Єx—À@5x£…N.-JT”âãâÔ2]ðȆ×ÚªDÌp/Ÿ½dÛÚ·Nâ¯ë=ëWj%‰J8­$¨€’Ä âD$fž&LèÂgqÌ!öîö#²ýðùø]7ËÔ´á7(Ú½‰ï×)¼¼¼n7¬Hu’œ%iÇ1?+¡Ž„¿½y~_/»Þ.¶ ’W7CÞßU/»ŽfÊÃ+ç+« ÃÝ–ïo„¦ú:R¦~ÝA€¹8ft0#§K‡~ÕrùãÛÛúä›ÌNî”±Ãd'À\eå‚)*ïI$Œ:Cà–Ù$( PÞÜr°³]U¾@æ×̺\Øé¾fÿDÀÊ‘iĤ%ÌáÛ$¶Ü¬ó õQÆs¤‚UÍ¢•¢0{¨Ø£ û¯Êj22»ÕºTWWË“M‚³SEW*Ê@Pšô?툭!T”ˆ •"£×;Z””žÓªt#±ï:¤EW¯¼øô}â=ë7ÂpW’%@IDAT6Á6bƒ;g}}½l=œ)ëödH(òN¥$ÆÂâÖ2áp¿¹ðn^ƨˆ051ñ^iEüþ­òÀòY²rþÕætìæ):‡Ö@Z’Ï|oÞ¯†û@¹^u§Ýpà‰ÇõÅ·˜ICéRÝZbà–ÎäñF î ðÞö£îì¦ö;øÑ¸ÐRD×:‡UÉÁ"H—;ºá1n‰Ýï¸8º ×â4‚¯tÇ»oQYâ=â½Ò÷Íuw¿\Öo*ÖuP”¶<-öQñrƒðQ‡¾i¤¨H&'ÆI ÜÞÞrDµ ŽÒ²-u§#ÕÁ©Ô&}¿6íÏ·¡(¥gȬˆ·$8À‘-½³ãêv*’K‚þ GkWÉ»ñÃ*ë¼ÉÂA'ýì÷Æý¨x÷§ë‘’¨\â« hŒD~¸í°P’¶›2)˜‰Ï冽§Ð–‚dî¤1òÚÆýêùÿ<¶½öá ¨¸©Š,š™®ò%ɹ +ÏY¹yZšãXÆB8u¡mø#+çÉÕ¢rÓë rÚ¸ïÞÕVyâ¾%’[PR§!ò×5»d)ÜLG#>/q‹Ûá>½`ê8±À¾å%%ÄÊ}·Ìı²`Ú8•Óiö]ÁÖ-)4´,™5^Æ¥¦À« V68¥¼ \Ïõ»Õ›õ®f>@¸ý¦©øÆ‰|°ë„П3i”dåÊMÓÇ©¶É¼`¤¾ž<ƒSÓП•g]!ò®Ëú='(jý÷²½¡,=Z)´¢ÄƒºššÊª#»·½¸tås/®ÙejÕÒ]V­ð#¯–¡0q™y‚"#"”¢¤ÈÐù¥µ ±)Û²V–ú³G P¢1‚…Ns™ø\Q…C©¤’ÔÑè2±¿‚8š³Çÿ¢¤¤°S™å½ê7–%*ÑT”¨\Ÿ8—+÷g*EipB´ÂÑütŽJâUT*òζ£’2(V¦¥PÖÞÎîÚ¼_'Ï_ÆyŠÒ¼È׺VÀÞ› ¥Â«æ1´s‘D@L?R!Â÷'­óF ­Â>vx’üï«JÈ]X:Ç©,Eâ{ò»;e&hgO-q¿wñLE“€öÅ·ÖŠË¿ŠïÊSÙBeêø¹KJÉ¢ï+ëöIieKÅ*qvTÂþ²f'ò)΃Ҕ‚t!yPÔâÁ&Z Ïê½`ÊXåEòü›[…Ùi#ÓèÝ‹¦+ei2\ﲡDñZèVM¹ÊÕÞãYr!¿P>ûàRµn’ÜŽ’(]»Sæ#fzžºì¹žKíh~4QÍ.ùô@›š,á8¤·CÚðÕO]ÈG m­\†B?4)^Öî8&5PÞô?¼©,ÑZÁVÄ@ƒZNGwm?š”ü–WÉ[bÿÔ½‹\-LT4Å5G±¹L?|*ìü2èÛaUjP–%ä-W£_ZaB¹ýVˆšàPN˱¡u‰lfìQA QÖ¦æ`oW0ø£¢tèLN@~NÖÿí\÷Þ.lç½á=â½êÊ’v½£E©¼¢ áO—@¡EÉH× n…Ååòʆý’>b”r‡Õ·ë%µ}ï—Åb•W7ìU®w´(é:Ä­²&IÖì<&£† ’€¸¦÷Þ­­Ë]/ÝáË8¿ ê!jt¼ÑæpQï¨ÞÅeÕR ›ÚÂ2‰& %÷Z±Ø0É@w2/ò=‘uY¦ŒK•¸˜µ<'‹gMâò*<»ûÔqy…¥j¾°´B¨ PÎÁZ¥¥§X†Á¼`ç(|>öç·;O(IT–(2²Õ9G§&a$?Gíséj‘RrhQb{¦U‰ÊÒêP÷puRÑ}û’•ôÊõrµ~¥ÄØhù§»ªohA‰c=7ês©;øaL';Ðëv5›ÚA@c瘻ßNÛ)Î+«·> «æua[Ž6?jè`¥°WÔX$ }*>+¾$¦zînxKY⛚púÑPQâ°ßha[Þ~}=tú†Ýwµ¸ÔþÑ;n l+†‰ãs”B…I)IxiÓÕD“@ð%N(ʯÕá åˆç¢Â„1RËP¢Û[íÔÃM×;Z”._8÷·õ¯þîw¼'¼7¼G¼W]±“/ ÛÝ3£Ä·]GÏK £DK‰‘®#@Üb#¥ ¸B¶:-w.œî±ƒ¾_,—dŒQ2®w]¿G<‚¸M[#»jžTí~łɊ¸D¿ø>5Òÿh뾆á;PÑ@gŽ%¥(qHPÑq*N+¾±­årúÝwËl‰‰ “­Ϩ}Ãì*)ƒb0â^*ƒãcåÞɰF»–õçw¶9w_8# ïå8!ùBJb¼dç8·Ùð§”UÖ@‰JP¯!P¦Øa¥œ8wYnžž&5x·SYsU– ¶!%!F¹ä ŽBL‰(+@:r5þãƒ=êx×}.×um-7¢Nt»ÒÒîz›™ßˆ@[x¹ÛNo,Í{k®b Â¶F E_Ë—Å´SÏÝoÝi­,i˹é@Ì·Mð–w^__\píÚÜ[–ì¿þ¾>¦zû¼)c¦¥¥*w)^..*üØ3`™Ö:PAb'KOÜ—Ëý]ôˆs=Mø ×i˜G‰ôàd½#™C£ÍVqâÀžßÜúáìÃûÁÉÕ²ä÷ ²mP©¦U©y“ËSî†ÌA·Šî͉Ý\v9²t¡AñÙì©èûµ±d½3d=C”øÇÃç‚䦩£Õ{“)žº_=«9Ú[DÀ½­Á^¯Øe;bú¬ÄèøýKg+¥f瑳V¹è¬H/ÀTZ©r=€®HSá&w÷¢P€®BÑiév纯^N†RõÔKaµÈyäRl-Œ{zô¶ù*êz)ãšHÜ*ròBž<7g…Чj}ÌÞYr÷âPÖÊ1PæH1WX¢â£ž¼ÿÕ‡ •Œñ'î Gëìëåî±f¿öp·¶_Bïo¡G“¿ˆi§ž½SÞR–Xk±¥ñÅ7'müìa)Æã{w9wìÈù›ï¸{yCƒítècÑá·ÇD„Ùãã¢ÂCBÚ m­µþòû­P!r•Öÿ¹ÍbµÚËÊ«íL8 lÛU‘›uöÃ=ëÞ_S]]Q„]8T¬©¼'¼7ý"^‰í@Y•@n¢˜“]}ÔÉÒd¤çП›n9Ä5 AÙÚbÑÝ’õýby•5õ2ôàFzŽÀp0å®MS÷i"\¸aÇë9²¾]‚,ã|PTˆdתTd¸lOªñž|cóA5ø¦¿£;\”&ºqÒŒÁ¹ãˆmK~õòµšço«¬¶Ž9’yQ.ÂeNïÏ}~òÂç®UP¢^xw‡z×h/näúŸýõ}ç~,ãâÚ]êæÅ«H`­E™Üð!$ô;‹ž)×s©íü0¥ÇcG IPXµ³›YíÝi§nëõ]J@J2gÂ(ä» +bèÛ@ðv¥L;õ,âÞT–h©à[‰Ö%*HT”to_)RµµÕVX™øÖÛ8~ڬѣÆOœ=(,"".8(¸ý·<0ÒP6m6k]}­¥¼¶¦ª(÷üÙS™ÇŸÃžTŒHæ@E©¬i™.x¼7ýȯA%5¦ Þt©[’ ·¿Èâ™ãUGà:b¼-Ja‘39Wd b hí凯»âP–TyL8Ë‹F:@€˜Rˆ7'*@t@'¾TŠ´Û{ÙTšú•U ×£Ü39bH²à•WÕªs¹¡ð8OÉ@!ßJ%ØrÎ#Yép;û@3æÌävüÐ0iÝ[úBY"ŽÌíÅâÌëbǬ»ÂãYË ¨é³„³ã}KÆÎ~F]†­®\Ê Oʙߗª’óݺ´ÈøÑpQ —ÊâÌnßÓƒ˜‡)LjTûçsÀç!<¬ç÷«§õ2Ç÷Î(¾”$þ!S*çÉ‘A’_Y¯ÚrCλ^*ïn;ìV¥´zÓAÄmðsâ!Þ?°ˆ‘ÎÜRW/³Æ&+\9>¤1÷ £ñëJ;õ&:on>ä<ØsÒҀ%’¹½HmO÷t¶‘¾ÓN=¼­,ñ Øy×­‰zýŸo^ÆÌPY"ùÝôX?íªg%€ÑÑØRQ¢k1'¾š`ƒ SMÓDì¹÷¢_ãÙÈàÄÎ!)æ«1¢ÓàM¡‹Ê‘³—·„¹>& œh‰éHü)¹c_H›JœuB莮­£mú~±¼°ï[Ê\ëf©º&‡×|JbM”qó¿$i þUŽ­{Úu·—ÓæYjÊ.ö™²ÄІ–£ý'ªç@1ˆzà~¹ €ÙÑëpÐÂAðãH)Á8µa1!RXÝ %åÕ’r„Ö7L¿Á%w„ƒ5µü¬xNÜ=·çÎØõ’˜ó¾¤¼FÂa¦›<*ɉqO‰º^‹þsDwÚ©¯_=é¾ÓN{çô…²¤;ñ윻.³ÃÎλbÉÃ\‘?`ÞZY2J@é@ˆ)Ec«•%>ÅZ!åW‘Šÿs½ŽSÒÇb•ÿ Ý.8ñ£N‚²áY­°|pË‹BF)æᔎ¼!ÓðLe‰LNtca®"J“Í îí&·#w éÝP°E•WÕȶÙÎLád®b2ÅØ¨9sñŠl;”©hr½q™Ä³¾ž ¡ÞÄœ#†]×ûÅò‚U³ìj)žÛßÞX/•E™j “‰K¾/Á¡1b«¯”a”qó¾(áQ)RVpL2¶~KjÊ/bx9P–>¾KŽ~ð9™°ðÛ•0F.ŸzY†Ž¿Oê«%uò#rfûw¥0g³ç*êfIÁxÄÙþùèT óîÜ/7Okvëôˆ=•%Å«òì… •D°¤FÙåb%&ÄW Cœ‘®!À¤ÜV|Oæ¦'Á;LáKœ5æ]+m`ï­13íÔóíÀ´SÏcÊûBYâyÙ)g'ÞµCOå‰wZ”\%ÛÄ^X×{b8h qÕqvU˜ˆ3$Î9q[¿±(áZ”h·.‡Â„KdDn ]ë˜DGW#†*Š Ùœ˜«h%hOÁÄßVr;޾=vû|¥­ßsé)ÂG[Ë|$jÜ}â¼Ê;vÇÍÓä èt™(Ï[¢•ÒÎÜ ;«¾_,Ï—$6y:š\­5?d¶L¿ý$ïÌj)¾´]ÆÀ]oÎ}“]ÿ¸›]"b†ËœüUJ¯’³»~,UedÔô'¤ðÒfÉ?ýºT—žï»K®´ðyàÔÓûÕwbÎÜ´ø£Ïœ{ÌIÈÄäaH,›Y+ÕõurÃdE`“„A˜Ö¦ÎʈÛÕH=Lº ¦'GɈ¤8…+ñ%ÎÄÛH×0í´ë˜ut„i§¡Óóm}¥,éš³“ÎÞç´n°¯]ï¨$iEIûO™·@qCtSã«&­4éÿz»EúÏ.zÔœýn2âP52o_AúˆdIBðˆ”Aø¨Êþ“T*«- pÏA }°¢ÈeìÑèa‰’™sUZ'·cP>­G¯" l6²ÐkáÈålîU9p*[-OO)c‘YܛʕPâ¬1×õRróGËrTan×[»…†'Êô;~-ñÉ3…1GYþÕjak-”ÑM_U§¶ZÊdî/IüйPªue׎´pÙk„•ÊR‘'¥Wö÷VuÝ*—Ðò9àóÐÓûåÖ ÍN}†€vobg>JRxD8’G‡#×\8b—¬P”‘ “­¸ 9Ó¢ Lq|ÒH[0öƒ®‹d8*ã‡Å+<‰+ñ¥eĸᵅ\çëL;í#w÷0íÔ]¤º¿__+K¬9¿ãì¼k…‰Ö*GœØ#Ôe‰ ¸!Ä”¢ºŸMsâ«'½žûôKqvÀGÑùßÛ͇,?PŒÖì<®¨›ÛCI‘"‹f¦Ë d‘/Ň˜ ²„¶„îu”«EŽŒ­÷¹VDBC‡pä“Yå½)ª![¾Ý?sóýê~;Ѝ½Ñ&ýQ r6Ib˜(áÑC¥¢øŒó4åE§ÔrDlªSY*º´Õ¹Ý—ˆ¯zð1XF&ÇJ4°$žÄUÓðwg€Èsµõß’L;íÙ½3í´gøuõh_P–tužzŠ«’¤ÿ« æÇ-ˆ§­þ? æºSÈy_È9$VÜv¨¹s­ëpÛMS <É‹kwK$:)_üèízÓ óÚ¦€Q*Vù`¬j-õ>$ÏSøzªœÖuõ}m‘œüð+7F¢†¡éq®Š«–ëk˜®Ì! 6†ú¦ôõóà›¨ô¿Z9:¡Š"žTñÑQQr䜣+¦Ã³V"ê,Rj ”Êz»ã=Ccu bï‚‚@åŒù@“FºÛ6`Ps~2‚@¹ŸÖ(ÃãÃ$.6Fbb¢%*:Já©)øÉäf”¥îµÓN»‡›i§Ýí§Gù’²ÔúZd¿5æÿB€>îÑáá*‘b¬@7OMkq­“Ûå#Éjm…3ÒdóÁ3KS Fˆ KÈønÄ›ån“ѳž’Ñ3?¢†-ˆYú¼rË£ë]{RUš%q)³@M›Ž"{ho_³Þ à)èâD‚Æ+Ñb…¢ä«æp²H0ÜÌ5 a-œàkƒÄf€BÅD²«ÒäѬ|þC¡ …Â8 ÏĘð X"”r#±P˜b©0OâJ| ^ÏÚˆi§îãgÚ©ûXõÆž¾¬,õÆõš2¾b¡h jºâí=™%7M§\ñŽœ¹¤Øòô¾m%·{oç¹ëæéòôCËÕ¨ðÛ[ø¤²Ô]Ü»{œÆÌ[ó¢Ü’¹óÈÃô,ò~ µ•yrlÃÀ’ÂvFâ/Ÿ|Q&/ý±¬üüiëDr_ÁÝWðò§z8GíÑ©§»%m`wtPᦠg)T’ˆmSQ"6TŠTŒ\ïhQRnxN«’!vPÀyàÇ´ÓÎA4í´sŒz{£,õ6¦|ƒ@h…§MjU[ÉítlÄ7h¡(uZ%{Ÿ+JVÑìÐ/`G”Š•&1¢G<“Ê’…ÄP–9¸Àê$Äé—ÜpQZ8èZç°*9Xérǘ¯ÈÈ5ÑýŽÛ‰«Ï!`ÚiçXšvÚ9F½¹‡Q–z]S¶AÀ `0úÝÁb_-Ã’Âe*K‘ˆÇ¡¢TrGÂbX–`µvU–ú³²É¨äT"aq F)]ˆéÁi]"™•$®çvv쉥Ï!`ÚiûXꦦ12í´}¬zs‹Q–z]S¶AÀ `0úv²èz§;[šVœ -Jõ ypX•šòp zyb¢&ïÒ²Dlh]b%*•TB”µ‰9•Ód,J½Õ”M;mYÓNÛÇÆ[[Œ²ä-¤Íy ƒ€AÀ ÐGh‹ˆ&1`Ü’R’àvG·`M¡ã(VÌ­EŽx.*LAPЍ4©å&%‰V#½€i§7b¬ÛžÓNoD¨÷×e©÷16g0 ƒ@Ÿ# GïÙé¢K­'$tЮwš„HÊ’sä ±Ñ„z}Ÿß¸VÓN[ÞpâAÑí‘sÓN[bÔÛÿŒ²ÔÛ›ò m 0(.Zd—vÚˆß!•0N’G¯€ÃR£äe¼ê ïÅ« ‰”F[:°&Ä^<•)z  ;]ìpQ\•$ý_m?º#ÊKÕ¸ €Ëö‹KÔ÷ôSGÛÔ7Mã¢ÿ›yï#`”¥ÞÇØœÁ p·Ìž€ ñòæ–ƒ7lk½‚.!cRKÖåÂÖ›Ì 0~Ñ·dhÚ=²ýo‹ÝÂ#qÄ")/8Þm‡ f|£\¿´]åZ²YkÜ:owvš~Ÿ¤ßü5‰Œ…úVJñå=râÃ/JC/ž³;õ4Çø7¦óåß÷o ÔÞ´Ór§}ï:²ä{÷ÄÔh ðÞö£n_åØáɲ|ÎD£,¹Xû;…ʬ»ÿ${Wßå#«ý;Ø2xää  ‘ãžëu¥Åj)•œ#¿—‚ì;Bæ=𪌜ò É9ö§jh6 ƒ€AÀ à)Œ²ä)$M9. pûMSás,òÁ®’>rˆÌ™4J²r å¦éã¤ìT‡Ï\”#™—dxò ¹ká4‰DBÚg]!ò®Ëú='ºp¦¶+¬uKß%™;(ãæþ³„G“kYï˹½?‡«‘MæÞ÷¢Ð24ÿW1Þ*ÛÿºPƒÃeÒ’Èqw!ȽN. Êó ¸)Ë*×OIDÜH()—óûþSFÍ|Jm[ü‰M’úuÉ>ü[™ºò?eððÅbGbÏ‹›äìî;-W©–±sŸ“°¨dUÖÑ>§ò.qý¸ùÿ"¡‰Rti‡dlûÖ ù˜Š.ï¹ì¸‡õ5ERWS ÁHpkÄ `0 ï `”%ïàlÎbh@TD¨6m†"Ðzlj²„ƒ®vÓ Iž"T¦N]È—òêZ¹\P"C“âeíŽcRcÓ¶À±­?1ÃeêòŸË…C¿V–ŸÉË~,eGäê¹5˜Þ•„a äìÞŸJmE¾:|äÔ#þèV9±éK™,SWü?)¸°NjÊ/)E&}ÁW¡¤JÖ_Iñ•}“*cf^Nlü²ÔU_Cnšz©,8)—OþC–«?Hùµ£’wfµÄ™#Ónû¥:ïµ½káN7F)DaÑ)jý¹Ý?•²Âc2eÙOd”±ìÿ»á’"bGJ\ò<êV(c’æõö1+ ƒ€AÀ `茲Ô;¸šRûíÛì˜÷qeÜ<ýöÃg%çÊuÉ/,•éé#dÔÐÁr>÷šTÔX$ÉÚ ¹×ŠÝ,É»»i¬{rVO”ázþkÙëåâ±ÿS«R'T\*W2ß‚e'C­+/8!Õ¥7¼Á j(ÈÙ„ÿÙMS¬LwK6Üß(Á¡Q²ý﫤¡¾Zý¯IÉaD¼”^Ù¯þóG¹ÅÁª Ŧ²èŒ ½L)KÃ&®[]\öþ{Ùû'Cñ±T]‘k>Pë /m‘ž³ e)eŒ›‡ã¡\ŸÛý3(n×åteº¹Æ™s#ƒ€AÀ `0tŽ€Q–:ÇÈìág´Õ¤Ë[£­¹³ê‹—tµ¸\U«¦¶^ÍC‘ç×¥ ­bZÚÂ]oëhîz\Ê«–ÐŽvwk[Eáqç~õµÅéüïº,‰©7I¹2lü½ÎM‘ƒÆ9—K®t*JΕ. t¯£e(aè<±ÀÒ=ÔéNG7@‡‚Ö²í%ޏEY±>¶ÖYR{TÍ´h]8ô[‰Mš,sîý›DÆ‘Œ­ßt×ق͆¼17*G®¸wV†Ùn0 Š€o÷Æê]1×íqÂÑ ¯¨µz¼\OhµÚ&Rb£Â%",Ä'ªL‰gld˜ÂØ•â½byu)õöpOyCÕe9ð†k”A© $$<~i`šÛ«Òƒ’8|!þKLâDľáØöV„Ç CìR!{Ú ÈÜ‚˜¨yÎ]‹swJXô3ës*Ö‰ny$”(»c‘FÏ|R˜C)4*IY¦œ6-‚9‚CcTÙÉcVÂRuºõníþ'ŽÄ“í_? œ1 ƒ€AÀ=Œ²äNf/?CÀÙ1G¦ëÄè0†™H­$œË-ÂÒ yî±Ûä¾¥³}âNGâ9jÈ g'¼'Ó÷‹åÙíR`›Ð“âÚ=Öj)“K'_”I·ü»ÜúÔQÅŒ—¹ë‡RQx´Ü/ËÏf -Cá1CÛ-£õ†Ü/‚bÜþt¦¤ßôUÉ9ÚLë}åì;réÄßdüÂoȲO‹¿¼¥1Rç÷þBHqŽ»õ‰ƒ’4veë¢%Š×‚U¯ËÊÏgÈ”å?QÄ{rzÇwoد½Ä‘x²ý3±#†£p´[ùv6°îí‘´sH¯¬f(ÖeòÈÁNŒ‰wOÄõ~M™ ËRšÙFõ¤ÈeL“V”ôŽŒ ª¯mVfôzwç7*JÍGÚí P”ÈfèP”š·ˆZOúñޤ;Šñ#Ž)(`ûçs Ÿ‰žÞ¯Žêj¶ ƒ€A ?!гNBÂ\K¿A@[*Ø1d'144Sˆ O—´ø’òjÄ.õ›Ëõê…·’òX‚dúØ¡ _â¬1ïNeô±ú~±ÜÈÐ9a¹Wlv߈ÑêÎuõå1Äø…6ªvÏöÏç€ÏCOïW_^—9·AÀ `0¼€Q–¼¸9ŸWUr0:ñì$†Á}- ňð0(V[ƒ4ÁH×()«~6™—–"‘ c ¾Ä™x÷D\ïË›6T*“åhíªž;`%nÀoDl°j÷lÿ|‰e¯TŠ8•V·:™œ'£‡R¸_Z*zêÖÕú~±üI8O¾mЬyÌX˜Ü¼«´(/âÆvÎöÎvÏöÏçÀS÷ËRoµ7460K¯ó¹u³Šf7ƒ€AÀ `0ø†àÁ¯nW¿¬ì êJ½¥®¬¬¼šë»m® k;ñ!!!ª³)uèè×ÕÕÉp›ìѵR…ÉV\& ±Q°<w¯öZc”èºH‹Üø”(™6:Yˆ';áÄW»uµw¼;ëÛº_< V¬sS¤²f°L[+ƒƒ/¹S܀܇1Jt½«hL’”ðû!ѸWž¾_eÕvk}=¼\å†gÙu£Y6 ƒ€?"`”%¼ký¼Î–šê땵u´bDÂu®;âè|J(:óá(#:* ŠR½Ô[­è€7H*çB+,rµÖ.ÅŠi.*‚®z¡Hº°.Ì£Dzp²Þ‘Ì!L‚ÓS£e\ê`‰ŠŽRxWâë`Xëfíݯ)£’#(@Î^³Ë®š'%%8K†ƒ%/%ø¬„tœ,v ÜBæQ"=8YïHæÐ #"$)6\Ý£˜˜hß/>—|>ëkk Ææ ƒ€A``#`”¥}ÿ}éêî<¹ç2O¤Ž÷ØÉ¬|Y0ul·ëH×.´3^ƒ#ëV(J HÚ¨Êäö˜P‹Ö4Hi½]Š¡PO Dœ (œ4iõvC(µ1g%ä2•á±A26)Vb$6;àÀ“¸ßžºàiŒÛ»_cp¿âÃC$ûz…T•‚Ú4Ü'»„I„–K°tÌ$§ËïOs›„Š¥1N%œe¥`(IƒCm’ˆX2Z“¢$.®wîŸK°2\¾u˜êç¶?Á;à®åÒÕ"ɸ¯,ÈeU5~ËÚ§‡yè¢Êä߉qÑ2elªŒšØâÌ1½ˆ€i§¢\©M;íÅFÖNÑFYj³Ú«èç§í»°`åå3²c¡,éoY—+ä´V SOK %*GÇnzÁ$€°È«Mªê0Y¤¾±AlÐ§Ø hB¶ÀÈ ‘0Xs"‚%!:\¹rÑBAE)>.N¸L<²«yª¤1îì~…‚oDu­”VY¤´Ö&u¶±Ú#¤Î¡ûêb¼2¯©Èuž'2¶e^'ç†^\ ²8$lwÑ!v‰£Ëi0,¤½¿ð\Úíe‡wn9‹Kt}v{ñŠMÑžD€)¬xçm9xF¶Ìòê:ø<Û%²¡N"Ï+´*{OÖÅWʪ •œÐ© “w·‘¸¨0Y±`šÜ:o…ð°ñ:m¶S¼ûÂj%,€eu^¯S_ŸÐ†a ö8±àÛ§Ú)ޝ˜oÚioߣ,õ6¦üÎpíl5`g5åfýI4>Ÿ{MÒG鬌v·k¥(Ì*ö(‡¦DëãlèB† ÷ÚZ‹ŠeŠDç!Š-OM̾ר݋óÐ*)2Ò¯ @GÇ›ØP)R1/p½£EI¹u9­J='vh} îܯˆ‹$"öŒ= ôÉý:}ògÕÓ—þȹ셾¼_|Oeåä_ÌfÖdý¼º>ÃÞ€Àœ£›ð}Æ÷­H¯|°GJ«-2¢2Wf—œ•‘•9fxÏÖPÖ‡InÌÉ4AÞÚR'[œ’úÈb™2nxë]Íÿ^BÀµ¾¼n·”UÕ)ì´ã‚­!wº`[f8ÚéA´Ó{L;Õøxzn”%O#jÊë´ 莗 ˶ݬÙ4r\úm¯¬ßóÍ'î éþÈ;àu§°£éøßD)ޏ ”%KS盄 T–è†æP®ÔýüGãBK]ëV ‹ ]îèÖE÷.Nt¿ãvâØâ÷+:¢ùÊâãšÿxi©/îW½µAð<6ÚÊw®yg-.•Ϫ~nûÀ¾ç%°ûÉiøNã»mó ywûQd)–{s·ÉÐê+ýä =sTÓK3Õt5j˜ì¹\~óÚ&yhÅ­cÌîÄïr"¹ÏÉšûL;m…‘'ÿeÉ“hš²ºƒ€•f§‹/ Ô×ÔTÕÞ¹í¥Àå·=ýâš]ö§V-í¶OœîX²ƒ¯–¡0q™yg"#"”¢¤ÈêÁŽò‡Æ¦¸&­,õg©É¨äT"aÍS.'t±#>¤›VÔÓP*©$9›:,JIJ7ÄîW\Tó•' ö^lƒ†\cäÍûÅçðÊõ29¶w矫ª@èxVµÂ¤Ÿãf`Ì’O  Géùn£¢ôÞŽã2¶,K–ån’àF«OÔÑW+AEò³«eÛÈ•òÆ&ÖÒ—§)ê}é«uö×zµÕNSƒ3dVÄ[ˆÉ4í´£ûJ–Ø%AÏ«¼„¦v„T÷·e©ûØ™#=‡G¥µ¢D?5Û³ãdBR2G°ï“·Äþ©{t×ÂÄÎ¥¦¸¦åBÓŠS! ;W=è±V¥5újÇG‘/o­0yîR}¯$b£&D-вDlh]bÀ3•J*H!ÊÚä`¾ë-‹’+2¾~¿Àuá””!IÎeo,xû~Ñ¢DEéЙœ€œÌSoÚ¾ù0®S?§Üà³k,KÞ¸ù]<‡î€òw2벬ÙéP”V^\×Å’îîT(‰×¦ÑwÉ››E†$Æ)—<>‡F<ƒ@[픊ҼÈ×H^Z»[~ðÌ*eyïz)戶hÑN?< ±Ê¢ÔÖ¾f]ÇÐWU“$/½¿K~ðôC¦v —Û[²ä6TfÇ^D€.<ìpÑÖÎŽ]|Â11ÉRȶwßÜZ|íêõyËV>ü__;5m¸}Þ”±ÓÒR»•‡‰M*ìl’åˆÖ:ð…MåHO8÷€R–ˆ‹žˆ&|ÐëˆG_ˆ/Þ/ð^8…ÄÞâAÑ÷…sOÞ/æQ"=8YïHæÐh³VÚ±õ•ã{wÁiùlV5Íù¬ò™å³;püp±þ |ŸÑõ®îÅÛ‘räLcŒ’q½ëÞÝ#n‹.o•5á)Á;NSÏ]÷J3Ginh§5õˆQZk\ï4@]œÓÂ4-lìªzÒ´Ó.b×ÑîFYê³Í[heI[–Ø£¢Љm4ðäþ=§Î?–}Óíw/j°Y£ƒN¢=&"ÌÒc“Gk+Rëÿ¨G¿Ýרú¿^ïKóÖ÷§õÿÞ¬ëd—7çóonïÍSµYvëûÓú›u²ÒbµÚËÊ«íL8 ,ðœUådžÙ±oãÛkk«+p8ŸËʦ9—]-KFY ¾"|HæPE‰lŸûN^P¬w†Ì¡gwˆø‘=pëÁPEöÀçÎÏ^Ïjå¿GßÐNO]P¬wŒÁ1Ò}ˆ¸o=dÚi÷Qly¤Ë'¿åóÏ àeèÆÃØÆBpô:)ð4åZ£ÅRÓ°í½7¶l{O¶§M™1bÔø ã#£cãÃ"#b‚ƒC¨\1x©#eŒ>ÑÑSgsô²?Ïm6k]]MmemUeé¥ó™gÏŸ:ÎdRÀ ‰ÊQyÓDʼnÏ(ŸU¯|M£õÈEe)ûrÊ!7ôàFzŽ@pÜ3R.æ_—±#’•—BÏK˜%ÜÐN-6™zp#=G`8XòW§™vÚs(U FYò¦˜#àj]¢‚DEI[‹´"E—:@…de¿€‰Tî«÷âƒ@ï#ðÉŸü\ŸåÕßþòy½ìçs>ƒ|Ö\Ÿ7Z¨Ñ¢De©¬i™ ”ŽWâqF|çh½Õ*¸Tž½tM%œe%oKä¸I2ñgÿP§µ[뤮 _®½ý)ÙÁзîIÌ´ùR•!µl–ÞâȾÙy2jØ`åŠg¬K]¿m¶S$œM î¥~éã»%"v.Ä.uUR³IÎìø¾Ø»É™8b‘”[=Ç™¼/Ä‘ÉËM;õ öFYò ަÏ  ;i±Ö ;bŒ‰`ÇŒë™áFÅ2a®-OÚú¤Á&#¯!Ð7½6Ï]žVv´¢¤ãù¼i«¿ø´(Qi2V%€à«âè„6€ØÁª\ðJ+k$ÂféÓ„³9ÿý-±U•ËàÈÈÏþ›”Üe§¦Ë‡Ê¸¯ý§d~ëq±ä{_ùc…™‡)x—U)B ƽe©Ë·RÅ“Pɵ†I0P_IÞé×äò©È a dÂâoKIÞ¹–µ¶ËÕ •YwÿIö®¾ÊRV—÷ÄÄ1< Ö´SO€‰2Œ²ä! M1C€6T8ýŸ7¾E©,)ëæl¿Za2ŠÀ0Ò'”öÉY={Rý¬QQ¢kŸA>oT–¨QYbï–ŸEnç³iÄÇ k;¡L‡ÀdÛ•5‰´öÍ趆¦îZ®ÔdgJí¥ó’°ðvIXp«o[+¡á2üS_Rÿ¹oÉž%ÿïÿ#v[½ÄÍ[*ƒ—ß/E›Þ”áO|M*Žî•ˆÑi%éßû½Ø¬rê ÷êSxu<˪jþ?{çÅqýñ·WÕ…HÑ»ŒÁ€+6ŃKl§Ø‰óOâq·¸Ûà`WÜãì÷PL56½Ù¦7 B’PﺾûoOs: IœtEwÒ›Ïgµs»³³³ß™ÓÍoßÌ•3ñ&+ÚF ¹v¦!ãuÇ«©Tµ‘E(qÀ•2üWªXÒèÂ`Ø%³ ×ÀiäY Ž-‡Œ­Ï㚌V5]ïá·@î¡O`øeÏ@qöFˆI:CœÃblüðây(£TÉíÔGäY,ù$gã3¢ÓF1÷8uШ£&¼ä çMÅ‹&ŸUgä!bÓc2úŽQß5!–„%W&ÚÓwŽ‹yJâZ<Ä!XgO{½X¢Å¶mèÏ€º`†©ÅpÔ¡Kþí_ û„©pú«ÿ¨Ç{ýêNq¸^þ'o‚ÖÑ£.€ðþC¡lËj¨øyDž QÃσ¼ÿ¾ ÖâÓöHÄÓbµ«œ‰7‡¶h®êÔ/mÏË×We(ªûP(Éݬf=äÂG!yè/àøoªŸž?8кxtÛ<Ðê" !õRˆI ùK (ë;¨-qhÊØ>LUy¾.žÇùéð_6·SqµšÅR«xød _ê´¹wàH#«ž‹Oþ ¢Aµçvê›ê`±äŽœ‹ÿÐ/=uÖèIÌ¥ qD›°& ‘$öxŠr|ÊAô¦i/6ú¾‰M ågìeoìIÑ:K2¾¹§ÊëèPðù?Õ9Kîå yI¶Ò"0&õu6öê«z¹£s"Èh Ê Zñœoî‰;[–<¯¥`m§Eèïè¶=ÍK2ל†ˆn]Ç#º P½ÜÑ9d | ÂÀíÔ'•ÂbÉ'9“6êÀQBÉùÉùYÄyÏI€œ „zpïS‹ïZ¨?S—,¿«#Š2‰ÞÞÓç` •{¶B· 'CåϱˆŠ¯PãÍ—Ø\ƒ^†èç­² u蜱ƒ,;ê—Díˆ/ßæ)ÇÑPj§ÅÙëѹÃÕ8i­j©uÆ¿kdmE¦“¡{Ê`5•€Í‚ïÓê‡æµx‘ŸNp;õ XK¾áȹž€ú? ð·å;23å“®Žh %‚•ûþËÐÿ¾¹0è©7Õ¡wUûvÀ©E¯·ÈÑQ] Ek¾„Þ·? }î| öÝ~98jÉ›}Ç„`¢C¤mw •vzdãÓ ¿êMwý"uè]IÎ&Hßò\‹k3W@öEªw¼“æÂ÷ Ï; ¦ ÜN½ÏbÉ{†œ`L€ 0&Є9bØýë±MŽ6|¤…jO¼úˆsŽºßV¬ätÑÈ©ƒpì ŽÑþÔû/AþßÂZÎ, éÝÓpœ ´•ÀÆ.iñb·wÕLç%œGs™D §.Çâ îI`‘·<*îéÝ’p4„°X ¡Êâ¢2&À˜h‰@¨¾AvŸ£ÔÒ³¹—-Á5ò5T¹»3 dÔ”ƒÓ™+üþp:C4Ø­·–˜ß0DnÀ–¥©(.&`L€ 0@H{g$ßz¯G·’tˆ=o‚Gi[JÔ}ÂTôÄë ëÖtÑÝZJæ“ãñ“¯‡sþµF/Ú iï,…ž×Þê“|9“À˜øÇ­0äâÇ=º±Fk€ý¦x”¶¥DICc¯û á ¸Åµ”Ìëã$ʆO|®˜y·C0ñ[`àØ{¼Î—3h?¶,µŸ_ɘ`L Kˆs¤üö¨Ü½¥ÝbÇ^ UwAî¿_hwž^5l4|ýo¨Ú³ â'^«ŠÂÒ-«Á^Qêiœ. $ôC.z ŠOþÐîÒ÷èw9”žÚ‡7Îjwž\¨(°TÀîw@Miôq –ýQÈÇpMÕ§<ɂӸ˜‹%åì˜`L€ t±ã'B«~ •»6AÏ_üäº(^ó%¯ý ¢†ž ©w= º˜nöÏåP‰$wá<Ké©3Ÿ‚ˆþÃÀR9(‚jîÐh íí¥pâ•G å÷÷ƒ1)J¿_$–(P'^} c ôþÓÃÑw˜ñúÒõK¡xõj1 Rþð Ä?Y†²mk ï£×@2„AŸÛ‚nçOÅf…¢•‹¡pÙ"õ÷?Ùï>ëúXüÝWtËÝ98 *Úè:ΑÐ#8àJH=çPœµú½ì–jÈ9ðnŸ@\Ò89iZƒºÃÄ?mCÁ´ox"ãAÚ” ¦GÔVdá±§ âônI£Zsö¬œC/~ Óõ‡SCb¿+T0”ÇÞUV×3~Ùˆ‰¡^ŸwäsÈÞÿ‘šF£ ƒaž†^¦b:ò3—AÆæç€Ž¿tô8  dï{²vÿë à'v¿ë:V’³Qµ éŒQ<"ÏÅ%†HÚ|/&À˜`!D@k‡˜ÑA÷Ë®¼Eó¡öÄè}Ûà KIÔ¤ï[i!œ|ëïP¼êsõÉRïž UÇžû ˜rCê C¥ =’aàão€½¦ò>~*P„™Oe¡˜: æaÎÏZy*v¬ƒŒ§ï@³…ÓC ŠQóN¾åˆ¿tšz¯¼E¯C푽êñWÞ±c&@ö;OCþÿ‚”ÿC1Ö³w«¤£GŽÅnƒºã‡[MÇ'ƒŸ€V ©!yØ/!}ó³PY´†_ö è QP‡Ö˜ò‚Á\Sû×>9û?P(íò—À†¢ê§%¿S-8#&=ïzÐðèÞ0öÚñ|%dlyŠN®Sç)‘˜¢eèMXŽoáøÏo ÂãkÔã 8”¯­_$¶Êòvà>­LÓ]÷t5läd´ˆEtÇò}í~Ê£¸ŒÜN="uöD< ïìŒ8`L€ 0 '@ЦÁ¨Õ@¥‡ïxjO8­/öÊ 5'­1¢ÙcPDÑp»~÷Ìqw  ï;¬h‰¢@ó…Z 4,¯×M·CXR_0ŸÎU“J:#Hz£:äÏ„Ö-÷@b*zÄXuX`÷K®r2¦ôsÅÝ#dôÄP›±_îç<‰×!Ïža ûæ¸{’OWMÓ/j§å²Ó*ã — ´(Q°šÊÔ½N©î›þ‰ï}±:ÜnÔó]§ìhEŠN†ó‚òÔc%ÙN!îJÐ$’Øÿr0î^ˆì6ê*Nªg5Z#ЦGUE]Ab*>åBiã yÈu®sݺâî‘­Ÿ] $ê’_ã®_¤ZÀJs·¸'i5n–c ‚Ûi«Œ<=ÉbÉSRœŽ 0&À˜@ˆ×ká4Γ° Ø0Ú-í.½l6{t­£Þ7 Á«;ÞXÔˆ¢ [[) >æWµod̾ÅQŒZ¸V½N±YT+–¡WªúYüQvœ§dƒ¢µ_Bþ'oŠÃÍîIp‘ç={Ud½ñNRšM×ÒAâhBž1Æ–’ðñvPÛ)D€U ƒäY[kî62Z< vk­šì Šö±@IDATG‚×TÔМ% {kåpØÝ(ÉÙ ;¾º ­?ñ0åö]êu2ÎE’ñÚˆØþêgñG‘íxÜ ÙÁÑ­óÄáV÷6säü‡ëÍ‚ø>—‚§b‰8Z'·ÓVñz|’‡áyŒŠ2&À˜~îCœºGêÑ]‚9Ñ;n¾z KÞIuˆ k£!x5h­¡!u½nºC=NbÆà[|‰}l<Øqh]KN%ÜCõþêðº˜QBxê`è6~’zºç6ÅO˜†ÃÇYšèœ±WÃ*5®ç4à¡Á˜Ø —,‚ˆà rÈ(0&÷u¿E«qâH<ûöŠS‡áµš˜O¶JàŒvªHPhÚê5í=YW– 1©•¤Zk*NïR­OÇݧ½#Ç =úNò8{ ½|ˆè–ÚӠՇÀ1w7º–DT¯ÁÓqXÞ¥? 8-ž…8·)yÈ@–-²4ѹˆØ~®5DöÀùN7‚12ŒQ=ÑzuzêÏçÖGyr;m„¶ÝX,µ_Ș`L ¸¸: Ø¡× %'&\™ÝýÓ ­øy˜r2Õµ‹úß7z­EowBxŸ¸ŽÑ2ýÉVôR÷y:sˆ`sä« —~¤Š¤sßÿ/Ó@]æ!WÒÜ^Á9FE0hö;0üÕÏ ö‚)ê¹S͇:ž7xö?aÌg;aȳ Á€Î={$©ž÷ôñ‰êõCŸÿhK¾å/îÉZÇp½»E©b©¹!e­fÀ'U͵S½F†S¶sýB¨0ë;¨.M‡I·í„s¯zz­=+gBTüô|·®ºû( C‡ØP=º?Y޲v½ λ.¿sŸ:¤¯²÷õáȦ¿«sšÆÝð_¸äwk¡'z¿£¾åYÕ’5þ†Oaê='à‚_~aÑIõW9w´ÎRßQ·ÁÄ?lÉÚ©®utÛQÆ“Dou±èÏéטœ=ºïFÐF8çJ‘g<5à7÷kE&™Ï5^p³à«…pzé""'«ÞêD:Ú[‹ó!ã©Û@®ZµÎ{’5ëøKSOÐ}íÕ8·ªÉ;rÑÜýÝóo-Nür£SaLŸ•+é?Á¼µëø\c‚ñso§‰hÍ«%ö¾ Ën|Q3Ÿ6~ät¦@§È‰m"È+¬~«a¸¦µ®¶q½jU²YªÔdäìaóÇÑc^´úÙnu¶%j7î׊<^r«ˆªûÌŸÞ€èö›\ƒ+²­Ñ9ZiÇ—7 E(Bµ ÙëïIs©hý$r>¡7F£u«¯k<”Rlÿ¿ƒø¢@‹/‡­®QÞgû@ü íƒ`Ìn§gcåéy¶,yJŠÓ1&À˜rdMÒª"I :ôئÓé 9­K’ [úL;vÒü5•8In”5‰$—Pjtæìh~¹õn)Èf¡äž†®!áÔT(¹§iOœ¸mí3ÂuŒèÛÃŘxsh;Û)Z—ö›¯»âŸvJs€H„»I.¡ä~ƒ8ÍOj*”Ü/#¡#„’ûqºÆé„¢±PrOCål«P"nÄ/\Ïí´K/?ð·ÜK€|9`L€ 0` ÞØ“X"‘d0 ‹k % ¦( ÊÃâaCêÁPÔ+q+ë£û'@xzåC¾ÄY0¹êÀ fͶÓH ªä°ÇtS–0toMܪäDÝÛ©/k‘Å’/ir^L€ 0&ÀÚF€^-ã&µòŠÙó 5 t؉'‘d4Àˆ‚Éh4B\„zíp¢Û X×ošß,Lž—44R’E‰x·A‰‘ЧG¬Ê•øgâíãPßÔ±Y>i>(ŸÏËÔz;u@ž}$üTw³ß,L>`TYE‰x·Áþn§’«mŠvT,üQž³äªœ'`L€ 0³pu†Š£Îl³Ñg¯zßbxuæÃP$……‡A8n&S$FØp¤œUíøWàú-—änôhÓÙ£s¦ 9J4t‘,rýâ 0$¹›Ê“¸_²ŒøjžÙjSìöfý^»ÚHQ>ãþv\ÝÕÛ¶êI;ͳ€êºeüÖ£9LħÃoKs”hèY”ÑNGÓITg´‘‡âã°Xò1PÎŽ 0€ N¥{ÇÒ=î×B4™š¡õëÍšÏÜýljâ¿‚5Ùæ(ª¨¬¥zôªÓ'êÄëõzU$EFF€Ål‹Å6œÏÓÃQ z0C)ÄÁòÁ¿„>Õ90¨,R«³¼Z‡)¨á¶¡p´Ž¹'¯wäÌæz 鮃ÔĈB–Ä“Ä'ñÃðÚ}‹I+ªjqš–…fû}°™-åÞ¶UÏÚ©Jí °¥îè©Ë„Þú}¸Ïðj¦ ‡ëai%rN^ïÈ™ƒçz ‰P;µZË<,f§IÆb©ÓT%?èôTqtÇ£sÆIi*v)û‚FJÂÁKNWF|ü-uÙ®»Ýõdßõ®Š ï¥•”HÙØÁúnÑëóh5DšµL‹§ÕƒoCåüj“EªCasbÚœP °3†ùDEF¢P²‚mµÛhY¢&b‚pÊm(ˆNQE¬ ·áBšöj08Zv¬ÐÞrûuV­êtÑ`Ò‡! ʶè€ÞÝŒ ÑÑQ©ò$®Ä—<¹ooÕ9Õ½©®¶óß_ÚSp•‹Êém[õ¸bÛ¥vZj﯊ ÿá¡Â4•XGÖ`â²ØÁf9V]p–ÖQÒIo§V“é4>¬hy ‹¥Ž®¾?`g# ͘1'ÜÑ]º_˜? €¦vOƒT+‡kª$X:tî%º¸½ôlàëóv0Ê&9F±*‘.â™O=sÚb±½ýóÁw6lXBC$¸ áäëÛs~¾% v: Oål‰‰¿ÿ@f\6À«;Ð'r@@ó•ÈbC¡„Cgê…¨CÇ´Z3è¬6èŽÞ¼Lv”O²ì-ÔâБœ÷ ­@.ÁE0ôHQ¿ï…Ö¡'ÖbGL™·i±+·”ßßçŠ"B¼ˆ‹êtE^¯Ãù^a(ŒÂU‹ ¥n±±jœ†à‘—A_Y•èù°ÎÙá¨:ðÓöLüHßYÚ\mãÜË¡–ÊyþåS+±Ü1Þ´ÕPl§{ÞwÕEpÅ †vªÈ2®Cýý|Þ`j£~ÇÏbÉïˆùL€ ´ƒu5w>ùÌ’¤y)ZS¨ÔOòõªÙŽrõ%(¡ŽãÇM*Ñ«“|•ØÄ¯ÿïo?ýñüÞÄÂÓ8´¸ÞVõótÑ‘%Aí„–ä/>(I=–s§öò ‡EFÅJ¤SÑÛišgCCÈÈCžÉdvÎeBÁä–'E…@‹&Z¿H²Þ"P瓉$â¥ÕÐ\/´È!Eê%zG%užËªä;ÇT×3OI9™ÇÖ`Qè»J›ÚpOm#‚«baÔ2RyqÑãßxÛVC­†ª£+¶Óüì,Zù7ÛhCÅø!ÆbÉP9K&À¼"@=ÍOÌy…Ò˽õ‡”ÑaßhtR×›GÑŠ´âýe‘ïi÷šoRNÁȹ·Þÿˆö¿o¼üæaÁàQ‡ÇÙÆ‡  ÞÚSçØ¾ié7‹ošyÏo¯Þ÷øm×i zÔ¥Î?ñVš¨.ÅqÞÅ’™? XBOlh’;̺T‹hŠ×-VDý¾\ÈRDCëœV%§Aê Ó0¼ˆˆpu£áwtž¸ú"XmÀº–e»½jëÊed ¦—â‡û›{_Ü®½y4j£¢ŒTÞÔƒ®ÂòÇxÛVC©F…7`ìjíTq8*7/_²¤¾ Á$ÚG˜Nc±Ô +•‰ „8Í~òjFû" ¥qáŸ7ô¢BüÁü]|”N^7+§¢Fþý†Ûf[òÁ{«êï+†åù»œÛˆ¥Ž‡µ¬¬¨:cï®—¤óÆÏ[´|‹rçM½jûBP_£%…â$–"ÂÃU¡¤:°ZÎêç5 «R GäUH„ø¶lGêz£’KD¢•Å­OEëRT·ëd]"g$’è8§Ž=±ôE :Î/®€Ý[7,ª««®Á<éÅmB0“eIm£¢ŒT^*·fâõ¶­†R;Dõ¡«µÓý;·¾[]]!Úi°µQQ-~Ù³Xò VΔ 0vÐL›vk$ƒY­-T-Jí̧K_†–8©ÊÑC¤¤×RS‡ìÌÉ9ZŠ@ÄÀ`é€ué:r{xª²"PçüyÕ²-ÑqqïcüNø”?\7AòÆÂDQáâš:ú­8 ²(YÑɃӪäP-K 6KB0a9ÊŒ b©g¯¹'±Q7ô~G–%bCÖ%ZC‰D% $½jmrz¾ó¥E‰ÆÏG²¤ã‡÷ÿoÏ–{ðMõ›÷Ô¨MËwµQ­/§‰Ê×#‘, 7yÛVC¥Æ¹ù^íJí4ûè‘Å?®ÿng}ÝcÅ¢ù/°Xò[Ι 0¶ ×µÚÄ‘ýïEwI4G‰‡Þµ  HMÜF…-×àú$=/œ>}fÎ¿Ž¾†ç¨ÃC›M"9ï;–ÕuŒÉòGæÚ•Ÿ~´øê[þ~ŸÿPPZ®Ü2õB/æ0Q‡T81 yKªHBWâäB8pºüœ¥X}ƒXŠï‡îÿ@<(8­EÎù\$˜´(*I4©qQ¾´&ÑzG¥ã‡ö/ýaÉ—«±µ¸Ñ{ÚSV`jÁÎh£X¨0ÜŒ?üËÿ°1~½/Úª`¬í4Æm:]Wi§Y釾X÷õg$Šƒ¹bñüX,ù-ç̘@ÛЮßêÞ›ˆ^ïpŽoƺ´­ &5Ía"ŽJ·~·ãC-À†ÑÐ&Þc”CQÕ²„塉qõg>~ò•òùßýêÇ«»¥ ê­Œ9@:gPJ»×aoï©CJCÊÈzBH k’°(‰} øDº9x ‡ B,Ñ^lÄF8|Ǽ- ­£Dn¶Éë9sí¶êŸ7­_¼oûfrNõ]]¿§¸û[û`KÚ(–•ÜèHð•ž>=î²É¿Å¶ëm[%î$Tƒ±âÈLWè ítÏÖMîÞ²,JÁÞF]õâ‹%På<™h+Õªôë»î=$MOrÎÁ{ÈQ*ÒJ¸ôêëÇo^½l æ(ÞX “÷7á|A€êƒ„,ÍW!ë®ôÚŸÖ·åð;Ó/žvÍuèZúJìhÇbGR‰7*Ýb#¥0½›W¼ ½¡©0jú¹½ù¶åºIÚ†w#ÿúzc[.õ*-uÌÝCÓÏîçÚ7ÛlJEe­B ¸"SÉá°WŸL?¼iûš•ëM¦ZÑù¬Ä|i«ÂêžÚ€x©Ñ  ͶQ,jDá·ûèÞÝÇ.š:}2>çeØVc|ÝV›¶Ë¦ŸAj„[¯¹3·Óì£é?l_½bm]]Mr •6ê·&àVí~»g̘8U,EDG_‰¥§.£qælWóùf GœJ”Þ“0Á.ÜÄ[kêŒË[k,J—îÖ%ê|’<õ;P[[%÷Õâ/ðóŠa£ÇI<4-<"*Á«Óéhêß•) ­`ïÁŒ"Ê{»Ãn±˜L•¦šš2ì|¦=°ç$>½° ï! #LÔ¥)Åiž˜¯lßÏÛ(–YR(mheZŸ×9gL¿¾C† Šên ÕiuF<ò!-µS¶S3¶Ó*SmMiÎÑŒÃûwÓ÷/Û¨ßÚ‹%¿¡åŒ™hgQ£ékjå.¾àl°µž”ÖaBžŠÞ`LÁ”4€„†ÍÐ ñ¦‡à! v8±8dYȽƒjIß»knéx^µ<ážê‘6 âç§ûû·kßE^üÎüù"Â{!v¨^ióÒ¨~IÑ;ÚÈ¢DB)˜­JX<5œµb*r®mDax· Œwš6J~ÿ¢Ô™Úigl£TM>,–|Ž”3dL èU'i¤¤0©RtþÚ‘ _Ò”ò”´ú¨DI Ñž6:ì%,¢+pu¢õv*Ú§¨ÏÎÔF]ÕK¾ Èy0&à êä©yŒò&#¾¶y¸Æ(q¥ŽuÓÎ5ýHrNÔy¦ú¡=Y¨sí.xE]Š!•é»C‹(‡zß-QB0‰©ø,·âóвwÅ6Jõêí´+´QŸ|¯X,ù#g˜€—T±äe|yëÄÜfÝ:§`:KêT‹Î(Y!¨›«ËÎ$–ÈéA¨Ñ¥½Ø¨Å&Žu†çìŠm”ê-ÔÛiWi£^ÇX,y3`LÀG:SgÏGH|šIÌÙ§X’™èXSG›‚¨Kç§Îçàæót† :£ô,¢;Ãs5÷ âùºJ%¡v¥6Ú\»õè‹%0q"&À˜@ȸØïrìýÈpêÐg`·’S+ÿ­>d»—Ô¡¸í <¼±=Ô‚øÑ1 â"zU´ö6t¯nÊû”@go£‹Û©O›LðfÆb)xë†Kƺ ¯,C.y’]?šà¯ø>—@eá¾v ! &ün-goSõ)°ÛÈI™BÒàëaðE@Dl_,o5”ænƒýßÝßÜÓ+îþybΕ 0&À˜@pCƒ«T\&ÀºúaEþµ€h´3}!#{µ›oBê¥ iô°oÍ_áÈÆ§qpuÒî,[¼Ðf.‡¬ÝïÂÿ9~^ú{HH½ RGÞÚbú³œC·Äþ,Éù4`L€ 0&À–%nL€ t’&þq ¤o~Ž»¢’átæ 8ºýEÂf‡q×/² ÃblüðbÐèÂ`ø¥s ×ÀiàpX {ßû(Pþ¥29yT„ðØT)¿ƒc;^¾£ïTÏM¸uäþNìzÒ®xzO@Ý䀢“ë cëó.ËUʰ_Á€qE–¨æµgå °™+€Ž<ÿ>0„ÇCIö&8´á õ¸{e”änÈu±Ö•€¥®ta±îI8Θ`L€ ø‘‹%?Â嬙<ðèÞ6ùE8þó›êpµ“ž‡ŠÂÝPpt9nK!.ùÈØ>LUyjáRÓ~‡ó¦Àþu€1"Ò. ¯‚ºÊlUÈ ¾àa)EùãëPš¿¢S ÿy3aÿÚÁR{d‡ª @îOÀi¹z*OïSG¾€n½ÆÂ9WÎWï{zû·8œ®¿*ˆŒQ=ÕãG·ÎƒŠ¢½0rÒ? б»þy°ð˜TˆM }§ “!ïÈ—g¤áL€ 0&À˜€°XòWΕ 0$púÄj8¹÷?j RFÜ‚Ã×&B~ú7hÙ9¤«,ܵå™j<5f­ÃÏ'ê·L´2M‡8ü‚Î ?¾ ÖZõs]Ï,z§@yþNõ3ýÉÚ»ý‘i …MuÉHè7IKÉÃn»¥ ‡ì݇©œ%¢ð1×äÃéã+Õ<в€žtÏfÄR æ1p<^/Iptë (ÜByÑxõqù`L€ 0!Àb)dªŠ ʘ€§ªŠö¹’ZM¥ ÕE¸>»G$âS.„¸^ã yÈu®Sݺâe?¹„’ë [„†×‘e(.i<˜ÑÒ•äNGíA(Ñ¥ñ}.S­Xßü­+'\×wEëøÏï@L0öº ¢[8´þq÷$gL€ 0&ÀüD€Å’ŸÀr¶L€ t‡Í³å/ÙŽ.¹­}pZmæ5[`Ùfnö¸8Øç0Å$¦ÁÆE—¨ó”.øåWâØ,åÛc¤ë³ˆØmÕªeÉS~ŠlS=ø•älD+™g^ÿĽxϘ`L€ ´Ÿ{Ãk?;¾’ 0#P[ACèdèžrèú©Cç O¬A«ÒßûbI§ƒèøa8·¨ŸÇOŒs—Š@FáEÞêâ’Ç»®-Í٠ƨ^ÐÌ u® Ë#‡E'ÖÍEê7ú 5” ‘=TË”ëÂú9 5t†h5ïÄþW ¥êpÓdü™ 0&À˜ð¶,ù ,g˘@ð /tÙÁðËž“æÂ÷ Ï…ô-ÏBÚ”—aü Ÿªâ‰æí^y:x8éÑäì_£Ð‰ÃUN‡Ê¢ýµg!DÖ‹­üŒ%Ûë<rñc0tÂ,°šÊÐyÄj(Â9RǶ¿äÓÚHG·ÍC Ás±Û½âuí$½1M9¦wÎ"7ßMCî¡Å@›åù?ª â’Kr»µF®ß+¸ÓlHß4ôáÝÀZW†Çyÿùmœ‹ô6 ¡xtQ­zÕkr1úá18©–%Z”–`L€ 0&X,–Ë›ïÆ˜@hnNÍ "ËO{ÙB©!'Eq P*m8àké¸[a,”Üypœ 0&À˜@ ðœ¥@‘æû0&À˜`L€ 0&RX,…Tuqa™@§%@cÓp“œcÔ:ícvèƒÕ3VÇ2ç­ ¾9`L€ „ K¡RS\N&Ðy ¸:îÅQgS ®Ï÷‘÷d6ŨØíöæ|©3çÀU߉ 0&ÀB”‹¥­8.6èŒd›£È¬Är'Þ‡•kVb›|âÀ˜`L€ ´™‹¥6#ã ˜ðêÔç[•Hɪ„ùé]+[âHͼ«fF‰g½È@ÆMˆ%Ús`L€ 0&ÀÎB€ÅÒYñi&ÀüNÀÕßðí7d‡­4×~.wæ}€8:l–Ê?mÏÄìõ›‹·nÁY0&À˜èÔX,uêêå‡c!C€¬jg¾´°hq‘mTbï2…Æ‚?â˜{"k –Ï^¿ ÁD¼90&À˜`g!Àbé,€ø4`~' ,Ô‘·oZúÍbÙa-Ýo¾N¶+z¿ß¼3Þ€¸?´*Ul]¹l->£µ~#ÑDœsŒr`L€ 0&ÀZ"Àb©%2|œ 0@ Kuä­eeEÕG÷í{©JN„½æ›¨SÏ¡ˆñÛ³m󢺺ê¼ÜR¿‘h"ÎlYB˜`L€ œ€×b {2Õ¸EíF|ž 0&Ð 1 :óæÍ«–mÉËÊ|ÿ”m¤ô³éf…-L­s;Eœˆq;~xÿ’=[6îÁÓ´Æmf܈/Y–‚S,)R4(R–`L€ 0  àµX’)·ä x.`¡J€,HÔ‰·áFûÚ•Ÿ~´8÷øÑ±ã¯lª›!ó&¤ÒJ >›jgÊÄëø¡ýK~øß—«1y-ndY¢=q%¾bFƒ.$a‰ ‚®T\ &À˜è²¼KH.$¥çÝóæÅuYŠüàL€ xK@ˆ%Õ²„™Q¿fõg¾wÛ¦ËÍݪ¶ÔÝÛLPrmçâÚA¼'ă¸ŸrKLõ?¬]ðÃU(ÃjâX¿¹[–ˆwPõ7K°PÙAU0. `L€ ti:oŸ^«Ñ®±Ëöçå:ùÌëoóãë™è²ļ%š_C–òî ýiýw[ÿ¸3ýâi×\ç0ðÊ"ý X ¤Z%Lª”ô’?v­`S ŠY‰UhÁYT=’Ýj©Î>¶Óö5+×›LµB U"ÚhXñ$®A;_‰~C4:üMÁrr »gÏëpدIJU$%¿PѼ½z«åÕy®[Θ•²Áõ!@üUÓH¼]6õkÞ}î‰]º5߯CÁÐN·Ôžt•vƬ~\áv ÐMnãµX¢(wÍz(·bÞ,–šæL€ xL€¬4DŒ¬KdõÖ⦠¡ÚÚ*ù»¯ŸW =vHêà¡iáQ †ð°X.ŠPÁ©«€eQC­=ö„ˆjowØ-Si¥©&»,ûhzúÑ{Nâ½iˆYH‘`"¡TQ§!xb¾qº þ†HpŠ;©©éÁç‡U…ÕÜ«ÕÀøÂ3I’@ ³ÕÊ‘öZÉà°úbÔI»$©:ob»/nç…V­A®ÕE*f}„†^ÏxêÙÔîóSô)oÏ™s}¯8ž@³íÔ ÕÈáš*I–m§ñš“o§v0Ê&9†^”q; `{ôZ,©eU¤7P^žùÔÜËÞ{~Ö¦–ŸoŘ@ç"àn]È]DYÒ÷îÚ‡[:>¶jyÂ=ý`ŠMq_©Ü}ƒô¶¸Áâ%Ê|Ð^ˆbE›˜ëE–#EbØY”H4½U‰~;dE¹µñ#X^þ#@ßÍŸ™5µV_½P#i’ûTç(Ë2 µ:K2Ú-ô‚¢«õˆEg„œèþp¼ûÐ^9Ñ©/Ør˜1ë¹Û̽¶«‚é€çnh§Fl§Š&¹‡>Sé£Û=u’A2wùvJC° íCï…ëé½\`çvêÏvê±Do^òí§î•xkÆœ÷.Z0gf? Íy3&Щ + â3‰zÃŽMZ"±DÿÃ臓::Jxˆ¢4„ò†hÀb‚ %Z'¬J$–H‘`¢ÿô7:Oƒ.àoF„l+~ -9ɺ— º‚†~è;¢½ýñ§Ðjõ/Ä™K”Kr7BRm~À¾7¡€# .O§M*ˆL†-½'õ*OX‰‚éL¯…Â3„xµÓhM¡2Êø-$è²¹ºU, Fè£ßG›T¢ïKëêõª’{r;ucä˨ú&ÅÛ ëMÔwa>#ÁVü!§àFí-T¾ž t]B@RVŠ[1nE¸Öï)Þt£4~Ýb#Äæï{5ɿ鳊ς=7q"^ÄÄ¥JÂ"…‡‚#¨¿ø›¥‰Û]<ÜÉ/õB¿ÇôBAÿ§Ggý …ÒK+2¥Ž~¥E¡ä—v–L‰ÏǾÔ¬8¦ÁùtóQ0=ˆÏÆýÿTðí´·þtYÄ- %ÿܱ“äJ|.‹|OÛ[Û©ŸêÔ§_ú³ç> ÈÊkH_‚¾ÇŸØÂä§Zãl™@×! ¾eÄÇ%+m·½°*QGþ—‰ £þ ï<(íw¸ç5e”ˆhO¢GldYÖ%š“DÂHì)Nç‚Ö¢¤¾\å×’FzpÁs³^Dz†DÀNó*(Z&Ñ>ˆ}'è;¤ÿí_º:*¶Û×$”.?¹Ê§¿ýAüü>+Ú÷ý¦)Ç» –e‡ýÚÿ¼0‡œÝËŸ=là3:£’Pþ9·Ó6Ö…s½´j§¡ðÿÔ'–%Q—ôcG?zøä&°m£qèâï™`í @}fæ>‡æáãþFYRÊê÷÷û ¶@ܯ™{ˆç „#²&ÑP<â´žïè·A±o§ßŠPJÈ5”‚*”ÒÒ.ˆŽŽ~·;½›˜³Ž; í¨Aâg*­FúÏŒsÄmG~|I# í46úÝm¡2:ìn§yö¸Åh A«ãvê1ÏRùdÎ’û­H0¡J<ŒÇÊ lœ1kîjI’þ«‰Ð¬x÷‰'è‡`L -è ®°ˆù9ôãJý Š £jœö~ á—y"G â6íÅF¢RlâX ËtÖ{Ñ:JN÷àÊ­äÌæ(áEÓ<ÇçÏ ¯} èûA¿ñÆQS&Þ£h´½hŽ’N&ƒ#‡¶ nNmÐ.üËd¹»|/^ÿ2nôãàÆí´½hŽ’NâvÚ¬ÄmTØr-®»Çí´=[¸Æçb‰îC^cæÌù`hž-ï¯è%ï~š÷±R+Ë3žš[ˆk8ÐlRzûÉ 0&àüßâºÿ×ìM¤Q7Çuß;Ÿ|f…ëC€"8ÔÙõàh™ Ð]Ûw,h®_“쨵÷¤u”PΞBMû9sà9JícêÁUÔ(hx* [ ˆŒøs*z½cgk% Ía"Ž¹Ñ½Àdóq Ê­¡Á×Û§ôßõ!ÕN%I ÝS.€¸äó¡¦ìœÎ\á?2õ9ë Ñ`·V{{n§^d5ï%@¾œ 0&À˜@ðhûœ¥´w–Aò­÷zôH’αçMð(mK‰ºO˜ ƒžxtݺƒ.º[KÉ|r<~òõpοVÂèE›!í¥ÐóÚ[}’/gâ-¶·Ó‰Ü C.~Ü£k´èÑoŠGi[J”4ô0öºÁž€[\Kɼ>N¢løÄgኙ‡q;ÿ¸޽Çë|9ƒö`ËRûÙñ•L€ 0&À‚‰@Àß ÇŒ¹R~{TîÞÒn±c/…ªƒ» ÷ß/´;O/Œ6 ¾þ7TíÙñ¯UEaé–Õ`¯(õ4‹æÒœ{s…¡cç•Ðw" ¹è1(>ùC»1õèw9”žÚ‡7Îjwž\¨(°TÀîw@Miôq –ýQÈ?ºLÕ§<É¢¥4çÞRABí8‹¥P«1./`L€ 0– P‡È}k9¥gbÇO„Wý*wm‚ž¿ø#Èu5P¼æK(^ûD =Rïzt1Ý íŸË¡HîÂy–ÒRg>ý‡¥ rPÕÝ3S4ööR8ñÊ#òûûÁ˜” ¥ß/K(¯>†1zÿéaˆè;Ìx}éú¥P¼ú 5Æ)xâΟ Š,CÙ¶5÷Ñk  ÏmA·ó§€b³BÑÊÅP¸l‘zûŸìwŸu},þî+Hºånˆœ•?mt÷,ÒÈÁƒg—p*wîm”â^…ÄWBê9€â¬uÐìÝ`·TCÎpûâ’ÆÁÈIóÐÔ&þi ¦ pxÓ7Ò¦¼1=Ò ¶" =§wã·G£Zsö¬œC/~ Óõ‡SCb¿+Ô2R{WýÐy ¿lÄÄP¯Ï;ò9dïÿHM£Ñ…Á° OC¯S1 ù™Ë cós@LJ_:z œ‡²÷½Y»ÿuƳŸØý®ëXIÎFÕ‚¦3F´yD·SH/"< Ï x|)`L€ 0ÎL@k‡˜ÑA÷Ë®¼Eó¡öÄè}Ûà KIÔ¤ï[i!œ|ëïP¼êsEêݳÁ¢êØsSîqH½³a¨”¡G2 |ü °×TCÞÇo@Š0ó©,SÔ<ÌùÙ`C+OÅŽuñô(b6 pz´Q1jÞÉ·Üñ—NSï•·èu¨=²W=ÞãÊ!vÌÈ~çiÈÿâ_ò(Æzönµj¢GŽÅnƒºã‡[MÇ'ƒŸ€V ©!yØ/!}ó³PY´…Ì3 3DAZcÊ ~sMì_û äìÿ@} ´Ë_ŠªŸ–üNµàŒ˜ô¼ëAã{ÃØk?Äó•±åy(:¹N§DbŠò¨-?Öºb(ÄyK;¾ù%XÃ.ý;茱jdÅJzЧ!}ë\¨ÈÿQ=žšö;]S`ÿº sç«0ô’'!"¶¯ë¾î‘n½Æ@Ÿ´[U¡W€V¥ê’t÷Ó K„Í·bL€ 0&Š >{/|ùŸý$¢ÓÆ¡H*+ %Ùb†š#»Qôœmt,D >J¾ÿØ+Ë tÃrï? ‰)®Ç&+SÖüG¡|ÛZ0ee€½¶ ìUåj²©VÍ·hÅb°žRÅ”¤ÕAÌ9ãÕë»_6ʶßüʶ®†Š׫ÇcpÞ”*¼ò²¡æÐ.t‘q^îºgÓˆ>¾§*ú —} ¶²â¦§ùsˆ8¶ýet¼°Žíx DzèÞû"°Ôœ %‡Ýåù;UÑ£ëq½ÎƒS‡?«©òÒ¿D ÓHéãzrFdA*8¶ªŠÍZ Vs©š‡ÝZƒâë4œÜû>ÔUf«bJÒè ¡Ï%êõ)CoÂr| Ç~ Hè_£OÀ¡|…hý"±U–·÷™hešîº§{„¬a#'£E,¢;–ïk÷S0†`à|;&À˜`¡F ö„Óúb¯¬P‹®5F4û1(¢h¸]¿{æ¸Î;P …÷V´DQ ùB­–×ë¦Û!,©/˜OçªI%$½QògBë–{ 1=b¬:,°û%W¹NSú¹â zâ ¨ÍØ¯Š?÷smhQ¢`5•©{.RÝ7ýßûbu¸Ý¨+æ»NÙÑŠ0 çå©ÇJ²BÜ• I$±ÿå0`ܽÙmÔUœTÏj´F M ªŠ4º‚ÄT|Ê…(ÒÆAòë\ç"ºtÅÝ#[?»HÔ% ¾Æ]¿Hµ€•ænqOÂñ`± Ð|&À˜`¡J@6›=*º£Þ7 Á«;ÞXÔˆ¢ [--ç%IªÅ§jßȘ}Š£8µp­š^±YT+–¡Wj£ë‡ç)Ù hí—ÿÉ›Î5ý@‚‹<ïÙ«Ê ë§pz”Ò4 a2Z< vk­šìG‚×TÔМ% {km^Âaws $g3ìøê&´þÄÔÛw©×É8IÆk#bû«ŸÅE¶ãq+d\G·Î‡[ÝÛÌ{ðS®7 âû\ ,–ZÅå·“< Ïoh9c&À˜`›€%ï¤:ÄŽ†µÑ¼´Öк^7Ý¡'‡ 1cð-¾‡ÄŒ>6ì84Ž®%§î¡zÿNux]̨ ! wŸ:ôŽ/ôè;Éã'× ¥ÓÑ,µ§A«‡cînt-‰¨^ƒ§ã°¼K!:~$pZ< qnSò,[di¢s±ý]kˆìóncd"£z¢õêõÕ%<·®¨~`±@Ø|+&À˜`‰@ÅÏ›À”“©®]Ôÿ¾¹¸Ðk-z»{Âû ÄuŒ–ÁèO¶¢—ºGpÈ“gÏ« —~¤Š¤sßÿ/Ó@]æ!²Ü^Á9FE0hö;0üÕÏ ö‚)ê¹S͇:ž7xö?aÌg;aȳ Á€Î={$©ž÷ôñ‰êõCŸÿhK¾å/îÉ8Þ f}Õ¥é0é¶pîUoâB¯5°gåLˆŠ‚žï¶ÂUw…aèÂéHòìÈr”µë]pÞÝpùûÔ!}•…¸¯G6ý]Ó4î†ÿÂ%¿[ =Ñû…ô-Ϫ–¬ñ7| Sï9üò+‹Nª¿Ê¹£u–úŽº &þa LþÓNu}¨£Ûæ©sŸ%ä#àÙ¯€‡oĘ~+^”\cw®y¬í‹)ÿr [#0cÖsèü‚¹³'Ñ>ˆ‚ËŽ¹Ž‹Ç­×ïïôP5üÚÌoð£ÿ‚6*=à¡_ctç-‚6]c Ïxm daBßષºæ®Õ„…«$Gmc_Êä|‚îk¯Æ¹U~b÷í › Çðó/Ïý–­ 7ûåh®œ|¬YͶÓ>±¥Ã'D¾ßì¾:Hs€l¬2l["è ÑjÔnmÜ–ÄùÖö4?‰\ƒ+²­ÙdZ}„jA²Ó=Ý9ŸÐ£ÑºUŽG]?'n)0Š/ ´hñrØê÷ðÓ–ÚÛá´))èÛiÿ?u‘æ9K.aL€ 0&ÀÚCÀQSyÆeíI"šŸÔZÍÍÏM!Wà4 hŽÍjÚ#’D4?©µÐ’Ð!q%œP´x=а–®oñ>á< Ï/X9S&À˜`L€ 0&ÀB‹¥P¯A.?`L€ 04¦ÇµÙqq‹FßÂ8Ÿ†‹8æ9«Æ È²Bc gÏ/攂€`§î©Ú·SAÇ{›bävꎔÃóHΆ 0&À˜@8£³i5[*ê"bè8ÏQöQåÔê#‡Åî¾’íÜ}t«Îš‹×躘)ò…‘rA¢Ynì¾³>| žË¬Ä W}n§¾àÍbÉ9&À˜`AHÀ\W[lîÙK²«c{ëó+‚°øAW$âhÖGH¶ªÒü +\HQæh¾ýÇó£«LŽk¬eªÉ⸨¼ 4…J>˜a8X•00H­­oE$ŽV%R²Y¸ú¢:X,ù‚"çÁ˜`L xЛ{uË9š¾?¥ÿÀ›s¢ûÃàòôà)aˆ–„8"XétnöÖzÆ!ú$+öúW¢LŽº©ŠF™ºòåg®CÏØÝÂÈÙ!jwÕMÚ<õ¦LŒ_…ö¡ÐGßà‚;p¥ì\w"ŽÜN}W§,–|Ç’sbL€ 0&ÐÑ\B "üyÇñó§\Qy¼ûÐK<ÏËÚAŽŠì°•m\ñ?ZüI°ö2×Îuùúú…™ŠO] )òTtª}C£f¸ú„ ÞºÕe·hé-Üd› ²Š òéM—çêÏ@±ÄíÔË&‘k?—Û©— Ý/g±äNƒãL€ 0&ÀB—€è¼ÓžÖýQ·ÜÇ¿Óñ«‚ÈdHªåÑcí­^â—*•äú¼ž­;ïöfÛ)®[÷ZØ«Í:õãÕ¦¢ì«LÁi6HPª(Òºâ*ØòßUÚ?µÑÚ`‰WFgY5ØNKô}!A—Ýì¥|ðìJì}¡È6H*9Ííôì´ý\¹¥÷¤è}©Ñµ°€f(<\G•ÑŽ‹ˆ"?Y¶[Ë7.ùúK7Ænö’Ž*]àï»ùŸ±qÕµ5$EQ­G«%¥¥R ™ÈŒÂéGI’Ö€F·äš‡­GPT0=®Z,Çá^›³på~óuÑ—E¾§ÑIÍ/ôÚÒ}ø8BTô€üdÙÁíÔ—íÅ’/ir^L€ 0&À:Ž€°tX"¡d¥­®®Æ´{ëæÿŽŸ|åŸ7¦^¡\~rsjc·òðx8òÓŽ×**JÈm8ñ%΂ys ­ää˜aÕüçÏ•Ž© (S«ª«.Ã'дh=8AâHƒ›1¡ÏšÉ·D¯ ”šš¤èÙít‹³î5ߤŒ ÿœÛi=,OwÄ­JN„#»»f;õ”S[Ó±Xj+1NϘ`L x ¥C%r§n{·m:×#ñ[H;÷zè7M™˜³Nb ÓÙ+‘,J$”Žw$åÏX´míŠõL©×Oœ;­eiå›Q=$‹iªŒÖ#tÌp-jn-Š# *PÙlQ@Z-…—N¿ÏtÊ)ŽˆñIúÓ4œ½ÂÍÊè°o$¶05Ewæg²(‘P:eÙåÚé™4|„Å’ï™rŽL€ 0&Љ `‡±/)H‘:¡ôÆž:ó䃙Ž™¦$/’¾j´$—Àñs¦Håƒc•KNm’xÒi!Ð%zG¥ì£G¯ýòÓo0)ñ$®Ä—8w±DŽÌ%§.”Ñ1(Ê/SíðÅ=·ûU둬Y3õ‘Y%i² +Tw¸o54ÛNñŠðõK¿ú­W¤¾¦JNPF¿Õð¦–YÒ%zG¥l§ŠO—×òvüK_\&À˜!’"‘—„±AZdê­R'ž&|˜3:%\ºÏ&ÃÐüSwüg0W•@ùØéòòÁ¿Ô¦Vç(Ë2¤Ôê,^‡ Ñ:J䜼ޑ3‡Õ\½wÓ÷ìÞ¼až¦áwµÄ7âKœ[Ñx6ÈÃÊ—Œ%Å6U‘pÍ£â쩊èÔ»… A‰8´NÁ¡u†è哬¨p>>êžÇæ´pQ‹‡µSLE\Ãp£ûë7,ýz}éé‚âq—MþÕÇ1‰úL¥nŸÔS—Áë0! ZG‰Üƒ“×;ræà°a;ݲí”^<ýŒ[ÐKA[5\0&À˜RÙ )=ïž7/îÝ'ž(²2ªÐUWöœŠ<ÇìÇTZe¨²)àÀ—õ8nÌv*ûøŽM»ÞÞ4|ÊôóìC‡NÈé›C¨”0[a«‘Œ²­ËͱhôJ>J¡g d·Zª³ذcÍʵ8ç« ë˜¬‰$–hs·,…”X"Ç 5Õ5—ʸæ‘j=R¬)ê4óØÌh=Ú©à¼#IÒ-þ°õpƒ6Dä] ;º[@‰+ %rü@}SÍÛÛ·÷Ä…WM¿„Úi‘aÚN R­&UJzÉÚåÚ©M1(f%V¡g ³f†n;¥ÿ¡ŽZ{O4$fcm`±´UÃcL€ ´ŸÀÈ/ÂÊãt»f&×µ?¾²9Zv]¶?/×É×àùOšKÓQÇv]Ÿtµ,ËÏØeå|+vEÑv…’lÍ«SÖ|yÒ¼úD ®lƒÃ —}õÀƒFžÛ§ï¡C"¢bº#£uºˆ–­ õ`~¾¯Ýn³XLÅU¦šêòìcGŽØ›…·$ uâ+ë·*Ü“„æ…Ä|%å‹_kל\r®CrL%Ïuè˜áR,»¦¥Áƒ¸îÑqGYZ–Øgm3ŽðrŸŠG‰'q%¯xZÜ4¸QÍæ:džÛixm§%ØN³:E;¥ÿ¡hÍÔèðª³Êƒóo—SåÁY \*&ÀB‰ÀŠ饞3\ó¾®o& {eÿAüÙ¿+ýÁQÛÅésçg¤˜eËÁ†ð^'m¦GefBœaxÆÃè­µ†½r`-¾½}"ýáQ»†¿º¶¢hté§ý]œûio3ž´š_Ç‚\‘þð9ƒÅqÚ}uÿ K×K’R‡oìEƤݹk¦ÓïÈù‡Ùò—Ø)Z‰×=å~Ç='p׬çrQˆ\0wÖ4ϯò_JI Š$EQΧ»`ÛB7 XeÉ\TçXüa–å£U§jhžM,n4G 7öDTõM>î©“Úl{Æã9Ð÷™:î¢óNb’¬GÔ§ï&‰%2¥X–%JCéƒ.¬y9*Q‘Ñ1­Ã¹?×c†V YŽÿ È1ÚÇ ­¤öý)jsÔ©-¢+qˆÁÊËí!4 ªÒ³Í˜5w¶Ñ´…sg÷iò¬Aõ‘-KAU\&À: |Yö™Æ¡ÜŒÏãKVÅökì‰.]uß` !É%ÕIå¶ç0Ímyî Þ<“e5¡¨’–5½nø+‡/SÇ¥½Æ¥Ú0Y²}õÀ¿k«܆é ›¿¢Cv¼%i4qNÓKùs[(Òø#ÿò̧æ^öÞó³6µåR_¦"Iv8T‘$òF—Íf"ýû° æßµ¹”† ’8"‘$†>ÑÛ|Z ”Þλ¿Ñoõ»‚h_!”ˆ ‰ b#¬J$ŽH$‘h J«Ò¶ù½Ã+§/Žìríp,kÈ9õƒëŒ»OµIš53݆\@IDATW?£ Žˆ#¸¿ÂªÔý$)‰Îsh}ÊÛùöS÷¢ç­sÞ»hÁœ™îØ’HÂŽ‡{œ 5Zà ãWäàÓ‰7÷Ô©c*ÄYNH,¹[—Ä(ÑiÅÓ>.Ô9§ïŒ°*‘X"qDb‰ê–6êÐ…Eiõ|ã ‡Ý6UÂ5*ìyWµæ˜+³Ø)Žph.úÛFŽ™ƒÔáA´K*ˆ¨n§«Ep ©vÚø>áÿÌÙVüZ5s’u)o7œ ΋¥à¬.`!NàÈý#ŽáP¼¢‘¯ž€²yä‡Rí6yP1#Ö‰G“$Œ˜q>É¿æ(ÊEs$‰: …ô¿ ?öúžj÷Îí N“ Í·CÙïCŸ÷?2¨ˆö8Ô¯+u†é‘}æÌ¹Í¨‹¥ ®.`¡LíIŸË²ãf|†Í»ü| ü°øÐsaÇG—ñpÚ2Uw|þê¡?ã¡Òq=^z#®†ÐÖÖJŽÖæ,ˆ¤¼o#sg¯1{îʬ¼†?þ€oKÿä/ S;D’xêdQ˽³% ¿sJ$–H ‰ £:±#÷Ž(1"D{ÚèœÇ/30­W3¬ÍY2‡Í¢;oeªÃVM/]ÄÉ3òŽP ­OèóŸ3œqàvÚ2DÑF£ i§-¹ù3dQ"¡„‚ÿ×’FzpÁs³q8yðKÁ_G\B&ÀB”€¢Óvûv´Ý·øÕƒ8ôMóXs"î>*·mä;‡¾q˜d¯:dØY*Ã9K47E V­%FqH4g…ƒ,xnÖë(˜ÐK7¼"Ù‹†à8üû|9‡É ‘Ôôi…E„öd!!!@}Hb#‘$:äïì:Ÿ!˜DgT|ç©ýô—38ÎáP¦®Êúê,99h)c†Í8ïqAo\zùßLyNÝGÉO¶tM(¬¹6ÔVPµÓ†bµ-¦ÎQ¡wxÕH§PšõzÛrè¸Ô,–:Ž=ß™ 0NN ýÁá'Ñj”ýù+§¡U)å–‡Ò6Îyè̇>òðˆìá/ïÓarÌdz4ôÄ›CñŠ °W<{½¹â3ï}O@L³ž;Œ9/”AÙˆžVã¼ÿj"4+Ú»“E’û‹7Ó¢#JG´ k’Ibï~}g‹‹N(íÅFlÄ&Žùå¹É1C•íô…²F&ëÑ/Ð1ðo$+d‰#3L{dÖævÌÐbQ}p‚¸“Påvê„I<(ˆöH{ÑFi/ŽSš  ´Ž’Ó=¸r+9s 9JXÀé¡bQ0Y, ¼gL€ ø€èè”à›´¯Z›“sÎüÚêƒ{ñW^¤ß¾ö¤ù ‡þý×YzëÐoFZÜŽÖ¦%í˯ò” É›3烡y¶¼¿â“ûqhÞÇJ­,Ïxjn¡")ùØÑ%Gg ƒÊ3ãÆîì'›ÊÈ…²+È’V>ÑmpÁ®¤ rªŒ1£ðħ3诖ו–¿+%õyñ»ázpüŽºø+«) OÒ§Ç%h3»—Ùrã$[\óÈaÖR{¿òbûÀ²|ǨR³NâbÚ–Ì&·ËÏù«˜A›/·S|«€vêmÀ/U ûdZp–ÖQÂW1§°ä3‡P˜£ÔôùY,5%Ÿ™`>$`Ðè¿0ËÖW1Ë»[Ë–ÖAB·ßwãp¼ê;þÖ·r޼ꡇ¯ÙŽýhÕ²¢•épƒñ?8ÛƒŸ Ôw^ÁÛ¼r÷ìyci¾ Æûâ–Œ›kh$ÆÏB$uk]$ÑÐ9¿÷˜»€ðËͺP¦F©N×S›ÛSŸÙ=Aw<ÞuäP£Ù€‚ØQåèUUbë_ž/ŸSZnO"çÜp;uƒÜÑ,Þ.|ï’M ξûÜ»‚»¸­—Îÿ¯QZ¿?ŸeL€ „O¥íè‡ûž‚“÷ ô»f&s‡«£+£•û‹áv8 ëüÆÉ\€7ñn×8 *ä˜aeÖ’1’Æé˜Ä—`Å<°3ÊŠ°L²‰Cë¤5`ì¿vú}™ÞÃ=ã|€ 0ï°XòŽ_͘@$ b© VKH=2‹¤ª®V »îõÈžV«­ˆÊT\ìù¬ŽÐr¼ ­wkŒ:ã2§c†V³ç“L€ t0†×ÁÀ·gL€ 0®C@ˆ$Ùá`KRˆV;9f¨pœ¾] ªkY,u­:fÀÇÜKâH«Ñ¬¹ê¡Y[:±c†­Q.6h‹¥ÖùðY&À˜`^`‘ä5ÂÍ`Å«Æ!’æ®yTaÏ»'­·8÷ Z(†ÖEè¢WN~°W‚%?è¸ìá9ú|s&ÀÚN€ÅRÛ™ñL€ 0&À<"À"É#LA—hõü˜îŠ£v"º;žŠÞ)¯SìÖdá6…Rã ì ·ÞzƒnÙ•ZÑŸŠH„:‰`!M€ÅRHWž 0&À‚‘‹¤`¬•–Ë$3h$ÇÕèlcªÃVM.ºUÇ Bö4ºZ‚cÎ5¤5RXÿ(¥ß6º=`L pX,Ž5߉ 0&À:9I¡SÁë_ŠìeÖ˜§Ê²2uUÖWÓ±ä±nËM5z\L³ lRÐz¦7.w:f2*³QZþÀ˜@ç"Àb©sÕ'? `L€ tI½·$Ç •¶ÓËyª¤(××)uC–yÅ dóþ•Àî‚÷à:1kp¡ç5S™µ•3¸èp„ t),–ºTuóÃ2&À˜€/ °Hò%MßçµêEãPи3\Ž¢ÈH~ÎGÎ[¢×ºÕäœA³òÊ™å•.Ç Îñ}á8G&ÀB‚‹¥¨&.$`L€ IÁT eY÷vt¼Í\7QÁ¡u(t®•k2‰£f:f@ëÑv4#¡[oݲ©[ÓdTy³—ðA&ÀºK]¯Îù‰™`L X$µœŸ.#Ç «s–œ²Cuëm©­q9fhî–(ŽŽZŽ4¸ÉÆþëØ1Cs”ø`îX,¹Óà8`L€ 0f°HjJZýZD’b³¨â3LÃ!u±-¥Þ1ÃFZ§•Ö_õX]~ƒõˆ3´Ä3&Ð@€ÅR Ž1&À˜hD€ER#òáç÷’#JªŠ.–™†Ö]ç°š†¶X§c†Ý ­Óh4k®~hÖ¶Ç u-^Æ'˜`-`±Ô>Θ`]–‹¤Ž­ú5¯†Ùe;y­›ZTY0‚5¶X" NÓšGÍ:fxxN‹—ñ &À˜€'X,yB‰Ó0&À˜@— À"©cªÙå˜Á¡\MŽì[•DõZ×ÔuÓ1Ã6H­n9;fè˜:ã»2®B€ÅRW©i~N&À˜h‘€7"饌ƒÁ&G>:ö·Åð‰FÖ¯Ÿ¤³ìÙzžìpL´¡c†‹0¦Q"·8÷(ƒ„%Ç JXÿïÙ1ƒŽ2&àW,–üŠ—3gL€ 0`&àHÏe·Z?ÆELíøy‚8Æû3 ¬}1"Ù¦±¨CëL?mœ†CëbÎLåΘ€ °Xò/_Î 0&À‚€/D=Ö ´ÓdPö+ Œœ·@wÅ3ìë‚ðq;¤H䘡°ºèIV3\kSÐ1CK ÂÖ;fEZ (¦=2k»Ó1ƒËÎŽ:¤ù¦L€ ¨X,qC`L€ 0.C ½"é=ìøÏœ™F¯]– Y º×Ù1@–sd—Kß½fn³ÛU·Þè˜a2N:26r$®yT Ô¯y¤‹‰YuåÌòJç,%TTJL€ 0Ž'€ÿ«80&À˜@[¬xQrõÿ®yLáÿ£m×Ai…HRåüÆEÌX 5Zà ç-ËÅ5xÎ ó? O1[Ì?\4xâÈÉ“7Ðp»fÃ?H?J }²+Y—È1ƒ¥®ny­#Ç øÅP34 ˆLDl“Э·¤è—O{Ì’ÑB:>̘lY šªà‚0&À˜€¯ ‘„ŽÚ,’DYþöGSÞ?ÞÓìÙž¹ùÿðØâxÓ½FÑ<ÓÙ­Kä˜ÁúóֱšZÐ1Ã…ÈAãz{Њé$Ž@ƒ›¡ÿ Ž,MSòg&À˜@Pà7¢AY-\(&À‚™[–‚¹vœe"©­–¤WþÖ×&[ï×Hº…Ýe="žô…†‘2Ø– uixW³.}??<Åb·ªâH’”«[uÌPŠÖ£ 4ïH'W\õ9fàÀ˜]lY ݺã’3&À˜@ªHrÈsÐ’tAãSgn'Ò£Àú‹]±Ý2oæ3I‰}æqœKóø ë¡,Ðìî Ö%rÌPTY4ARä©è¸âZ³Í<¤ˆÕïÑ1ÎKÚ¥ZØ1C8ü‘ 0Î@€-K¡ù˜(¶,·G7" ;÷íIâFÏ¿§y­#qfÚÌï×’Fó÷'îœýÁ‹ ÿ1Ü¡Øþwñ‰#Z³.Í[¨ýíãwÎþÜéÍMäÜ{á˜d´LÆÒZ*1ºõÎG¯uk3èccV;3´”š3&ÀB›‹¥Ð®?.=`@€ÅR@oá–Þˆ¤>è–oϹ#:eÀ¿ï›žéšDCCñ,Ë÷Ɇ¾iEöüþ(ÞÀ¡gq’N{Ÿâÿ,IÒ†'ïr´8w©…¢Õáõ¯D'˜3È ¯»R«Ž°³°È1è¿eÇ AU•\&ÀüL€Å’ŸsöL€ t>,–:¾N½I¢ô//ŠJ´šk3Ђ´+*2î¦û~_V%Î ëÒS3ä·èؼÚE~-N9ÿÏÞu€GQuí33»éÐ!ôÞ{—" MÀ®ØÛ§±£ H‚®$AH‚íÓ_?ņ¢@¤wé%DzO!½íîÌÿž 6!”M²î}Xvvæ–sßÙÌÞsÏ9ïÛY£¾­nm}=ë’­gygb†ŒÝ›{Hj1äêðzk€ƒìZÇIaU&ksˆœe6B€@@ P~\ïAY~Rˆ‘€ ! ”¥Š»YŽP’lÒkšIžþο²,ÏUU5Àèæ5ú§ÓÎñu¶.e[³ÖÕskÜöé§Oeò9¶DÅ™ÏLEöãŠâvç”gœ›úzÙÇž ¤¬bÈ UáyT°Љ4’W(F÷¿FNL+¨žý¹N³ÕΖÍ~‡^o{Äþ¼8• ¡,U¦»)æ"” BY*˜ó âH%ɾcäF:ûæ8µÑ¬Ï Árd2ÝÆ¼ñtÖQ®“ߺdßÎ#f×ñV¥Ký¡ø@Þ#v­Ë%fÈ//~ü-Ȧ3(Š1üµ ŠcÕ=,Ö+-åâ?pÍ‹:4©ãÃùûŸ@eA@°áU–;)æ!*!6%©¤ìv³Â•Û‰”3ÌfW <]žó­o-ƒ{Í?ÌÆZÌÙ›ß 7Ž~s¼y‡»â6“­K°(-²Y— ì£O.›ãÖNR-:­·E½0¢èÄ P”®-Åä¸ÖIª{Õ£_f·C®©½nº¶>δŸUך¡Þ")Ê©ƒÛí¶UJK¹ô©¦IŸCézÓ¤i²I’Љ(€@ ò! ”¥ÊwOÅŒ€Ë#PZ% IdÈ ïW-jKM2 ÷ŠDç³3Óß‘´´‘`w[Œ×·šfY2+ÜðŸIÏfF¼»Èðtvv +Ñ©›—÷I&fÈTÓ«š6‚$X¬æº*F9‚¥ƒ¹n“Ö:Y5þuûì6h«pCÑÛΉfÉP¿G‹4«õõ6söGšÔi+G?ÌÚzxR‡EmfG>ôÓQÝÑÙŽv(*D@(K.xÓ„È€@ ²"PZ%É—¾hìêº5 3Ÿž2ë cç©Ï˜÷Ù]¿r(Áƒ'½{MûOj<ŸDÝãV‹å§÷¿rG SÖ:¢]Wê–ÿ3dîÜÔSw­CÜQ†šÚ;—˜Á¦÷؉Åæ_ð6D +Bsk².‡˜+æ’ýÙÕ¾zØ~^TؤŽE½Ð>Õv´éÁ’&ÿçФKoÿø¨ûɬŒ}ífGEÀŠ´ uéõdаh4ÇBY²'ÞJ…€P–*Õí“®‰@i”¤Å‹Û»•gæ†õ¥»ÏÁÊôžÕjyׯ±.Á씋}6E‰û`¥ tÚ­A^€:’g”2ùñgC‹5qGꈌë‡ÛˆtaòIù/AˆµXë®3p%öŠ;V$ùÚÌÛß×bU×Jôüjk„^ø¹»¯çÏË_n™ÕzNä‡*YŸÃGV–ô"‘¡‘u6>„\9%Þ@¥B@(K•êvŠÉ×B 4JÏ”-/[®ß2†ïÔy¹³—¤S 9ÀŸë]ߺt®i×ä*OEÉž˜²Œ±X2[²Ü…(G\Úi³œ´-‡˜ž‚”ÎÍŠUÚϪn±¨ a‘šÙB”«,Iš—¦f6Á¹ýÜ©—›Ç/Y3ùØV:4l»cÿéÈÖÝÃŽWÝÐ<Év^¼ Ê‚€P–*ËóJŒ€É¤ÉI4ÏO5f3ˆdw³AÉ"Mv³˜•lƒÑêMn©iõ¼SÂÌ%D4̃@i•$[gœëhî=ïÊÌÌ\5s‘ì9mœª/æAdpZ“ÔǸ^aÖ%Ä# ’H‹A•rÿ-\1×­=¬]ÌZ7Ä ƒ ƒNÌ€÷k ¬GѺr„Ø#Í£jÄ({b†É¦kêó‰¾sÏz&KÉ­£&¶ß ’kºúõ¡ÉïÏ_YU%_ƒ"OQ n²³36æ½®í±Z¥~8§+K{^nßfNä¥6ó694±í)®ûÓXÉÚzÎþuéiéCðñ7>'Š@@ ¨Là,Š@@ ¨¼ÀJz1(´‘E‘ZiªÚŠ4¼KZ3‰äZ²LµàªU4­ \ä¡kƒ;ðéžRrU)‘<¤$гvœ£jR´"kgdY‰œÿöÔãåi•¸‘ÌÎvݦ$!¦w^Ù¤Lü -’·YÝþ8›÷Ú?q‚YsFÚßš,ý8N z÷Kfš%+bÚxM·ÒääH:}\V £4U«¡iVzµz¸{<öÚ“¬0•iab†,-}3`î£`6ª{ÓÅF|"TÅø×è׳ŠÇ¨óÜÃõ³Ôìu õÖçßzvä1™”»Nnw °qAàp@3¸ÝwøÕ6‡¹NÛyû‡jV)} ¶µi={ÿÙ`xÕž¯íìÈg4™:z½ã+¶zâ] T„²TY‡@@  #ðÒÇ»[âS{Y5m ÜŠÂrÐ_ÕÈËѨXkVõÕªUñ6x{¸‘·—ùxº“§‡; 2ÉІð w‰XOYÙfÊ̶Pö•÷´ÌlJLI£„¤dJLÍTáÆ”«hɲ_(i©Út¶ÕÍSŠødÚ48À&ÃÍø^VJ’=–ï~Wµšš–¼wpS ¿¶o½üï¥iãÕ*6å54\~–œwH¢½’¤˜ÞgYgßÞ‘Ç옽{S/«Ueåh¾R½r‰ _5˜JüŠÆMZ¯kŸ?«€6l1bB(+!È™ä¯4’Ÿ‹Û>7v ÊO¤ÁMºûÀË#Þh>d8Å烺ÒOµ™}àÈzúðäŽó õîƒÈ}Ø`x V©u­?<ÔZ²˜×ÃÒÚaפÖmý´_åöÀí, ~ôá6PÄ»@@ PiÊR¥¹•b"›Å‹++.©Ú“@á.X“< °hõjVÑZ4¬#7®W“jW¯‚—/ùùz;¨´Œ,:”5Ñç(6>‘¢/$hgÎ%¨P®–C‘¥ÝX„þ¥hÆ„L9èpœ¸ÃòP’ì§ÿÞ5}­ÖKÁmm‹ž˜Uö8. qID9Ö¥èÞÓÆ[ÖÛ·qÔ13XÕì‘bô9ÌFÌP`ÿ]„R·–­G’Á}ÙȉéqÖ+ä$»Â‘Õ²’-Gm>ˆœ‰±Å ƒÏÝ{'6Mäfp™›ƒ1NœÔaAë¢îaÄ‹¨?¼.©ÍÜÈáš•^²4ÚV§íì;€ÀáW(˜›ð¾Û Ÿ¢¿«±a¶Šâ] TR„²TIo¬˜–@àf@àyÓ|«õòd,'XUµ–§‡›Ú£m¹S«FÔ¼A-ò‚µ¨¢Š sÖÉØx:t*ŽŒSC5\YYcÕè£ú†7ÿ4™*ïN|y+Iö÷9,ÌßëÅý‚„[ Šá¶)Ïš·Û_wÔ13hÚ¥*”#¸vކkîòVPÿø±µ@áØÁÊ‘&+·¿– b†¢±íµ™sàa7ÍðÏþÉmNÚún?ç@OXOÀâÓ³ÍÜý·‚ü.öУ`Á»O1*£¢^i†ó$AÁ‚Ôé®+qK±Õ¿:[_kˆÐ¼kË ¯Nzœ»˜ÝÔÝ£>³ßÙjt‹õÊLOèj1N~©M±]$mýˆw€@@ àŠeÉïšY p“#À„ q–§%Yšeµj5»´n¬õëÜRj×ÜŸ¸Ï9cIJI§-‘ÇióÞ#Ö‹—SX›ö©dx<µ(3cH@Øztûõˆ€ypÕ‰4Ù¸¬$Ä … b…å$É›OêÂuØÚw¿1‡&wz4çóþßMù(jrû5íæDÝ®’ú(KžBìÜ xŸ{èµNëÃ4߇:‡'wšXØ8l]Z÷tÓÌ®‹ó€@àfC@(K7Ûó¸0Ms«gªƒQ¬ÛÃ#ûJº¶rÙÙd0âÿ–m¡Q' ÜÈSÃBß³ŸÌxS˜—¤ÆÃb·7Èú‚¨¢§EUõ€+ƒAV}½=©ª·§ìëíAU¼<‰ß=ÜÝîˆd±Xs^V•ÌV+¥gfÑ…„5qUYfK®é F¸tM•Öc¿F2JküiÚÞâºV6%‰‰2vlêÍqGpa¨'ŒB+q!J·!)lQ‰ìïsQáFWך¡îVHº+jR‡m?8ð”ãÖ°,½Ì}´sày(HOê4…?C1ê¹—@òà ØqhR‡7Û}Õßï“ÔŽëˆ"7F Ð€75€c`÷ºë-ÖÇ…¬4Èò c‡ÈíšÕwìàлþëZÚwø¬ ó6Ô°öùì%,ÒÕG@ q7ba<á6¥6¬]Ö³RcXÐØŠV·†ŸÎØW‘SÒ2èÂå(OÉt&î<g=w1DÌ(%A×Z÷ÆŸýÿ_M¦§ µ0T&%iåGžÌÙÙ#r$ë‘o¡Ø‚˜ìpk8öÈ“<– ~#í\¡u|¡Ýܨ»TÕú¾·oÍ®é©_‡'ƒ®û¦ÃÇ‘ÍÍÙôËáI»Ø†ÕI ,æP˜·Ô‡­“P²f×íÞaêºÁ'·E n€€P–n¸,”ñ¡¡õ”L©……´–²¦¶ÀZ­ÜÆÚÂ¥©vìX”UÒè X¬ëïÌØ KK<´8ÕªEc‹ãs •óA~œ©cv¡1r׃%ÎIZ²ò2ùÃÅðŽÓ@g-7ƒ;]UP™«ÝÛ5•{´kBMë×"ƒ¢ë2e&qrj>G‡O£'c͉I©FÄT%CqúÆ`”¾X` Ük¼2(ILÌ`•.„ÕŽ­GœóèºÄ °¼mgåH‘”ˆá“²ù§*¦ÀbEä R”»¸á}j“ñFGOyèÄs¸Ö_òõ±T±î ð¥½(€@@ P\„²T\ÄD}€@ P^5ÍóK7§ ÃNöíPxF[Ukm[e,øÕ~>Ríj¾rA 14#&d€EC‡Eƒ²²Ì” " øÄ´ -)%CMÏÈÔµ…ê~>ÖwîVŒƒ­ÛJñn ûÍ’œ&umÕHéÙ¡9µkê¯cRQ“;|úˆ(ŽÒîC'õR¸?{š%žX3äIJþ`[sh2Ùò˜#[T–Ísï Y,ºk”#äà*œ˜×Îä¸ÖÖÛ£êÊ’3”ÅÜ:Í>ç%ÅïÄwœ¤Iá'wøÁ6'‡%7eÃÁWÚO€@@ pBYrŠ¢ÀMŒÀó¦÷ëš-æ§a‰¸.B½a%’½=ÜÕvÍüå–ëRä7ªUÍ—ªUñ)±ë{R(Ow#^…’¹ì]È6[ôd¸CÙZŠ P’ïnCLզ݇µè —¥jæDê}v5J>‰®¤Lü€,’·YÝþ8ëttÒLÌ ee܆ø6¸×˜¨Naó·'f0hÆ¿†¿‘åÔʆNNêf:ŒdB‡Âæ%Î @éÊRé1=nJÆ›f¶!³ú:¬HO#¿jZÔ—Û7k ÇÕ`*J%B`Ï¡3´dí$ßM¡ê–”X¯¬”ñoøÙ_Î2ÅaÝ’öõB¼×Ha€V›z@q/ü[(ÑZo9¢I£ÖëÛÊv–¹EŽŽ öW‹|¾Óå¢Ôu€@ äþCRò>EK€@ #0.0äxÎME<Ë7£¬ÝÒ¹•|[¯¶°U©Ä³Sc8ÑGèÏ û¬Éié f£o$ãËáS§&UB«çx4ÎVÍ#8),dv=büØq܇ExiËË“˜¡"°c €cÊ’cp½*=Ï›æûXÌ—?@@üx/Ëžm ƒº·!/O÷J?w1Á¼°Û`Ä–HZ¾e¿ ²ƒs*Ñcá3׿­åøOkç×òÉLKÈ®u°ËB‹ÂFÁ› Ô6(H²¢DŒ˜˜½£"‰ “SœçF@(KÎ}„t§@`Âô™T‰þOSÕ†Ãûtî#³Å×8P7™L=þåï¬ç.%ƒÄš&…}èH˜˜aÅ÷ޤ‚˜AsFÐÿõ‚ÖÎ@!Z¢Í­ê*g"fp$.¢/€@@ (?„²T~X‹‘.‡€É´ÖkÙ4 ‚O¬Y­ªöôý•f ²E `C€­Lß,ݬíÏ£ Y²áV§8Ÿ´{¨˜¡ L¯ØÑC_ÒjX«"<ÈcÅà)iH¸Ê5)EQ… !Î €@À!Ë’C`*¬(­Ž<ú=vúÇ>0¬7¥¶•grb&eŽÀÏ«vЪmQª¼Ç†}Ç21CVz­¶œG×%fÈ ]h+G$+#_ÏÞY·¾2Ÿ°@ *5BYªÔ·WLN P|ÆÏE ýD¡(;Ñ6hBó¾°?—1À{Á‡Õäs·—8o,‰NsÎ#&fðµV[ÙÊÅ”BëºÈ… ÓßínU­ìVØäþø±õuÑ…˜€@ Tàg EÒ$NV~ZÁ¦×gÁoî*U‡ÜX(K|*ëð/ÍœY+;MmK²Ô©YJšæߪX0Uáw|ñ Ø-VðYÖ1дt¼§cQ‘?°Dœ½€Ïçáig0JG½¼ÝŽÎ™<9­²âå,ó ˜>³¯¦Y7 êÞV~pDogKÈáb$$¥’)ì03ì¡îž¿^#=bøoy=[͸¼²3˜L_yĘc^$I{Ö±˜§JštϵX<óR¯Bœ•(K>XËaݧÕÑ×yEãYøQ}cýOM¦§3]mÊBYrµ;æ¤òÂÑA×£±4*ËrOì¨ÖµÕÝÍhõöpÓ<ÜÝ—ÇM1È2ÖIØKÆ‹‹ÙbÕÀª¥ffgkiéY”–™ EJËóýTdé¬91Ñd‹Q¥¥óCƒNÛ!ŽK‡ÀKìn¹”z ª·g³·î–ÝŒ"¤±tˆÞÜ­—¬ÝEoÝGC½?$o%¿ɬu²*G4jÚzce#fÀ3p8æ¸ ƒFx®­Àë;ÙKþë³7ß¼|sÄì› ï¾[MMWGc=÷(^#±~;,Æ…‡LÿÛ•0ɳu%Á…¬€É¤É±æÐû°(˜Æ´^,QÍj¾Ö j+þµ«QýZ~TÃÏ—ªùz“;¿‹S°à ÔôLº””Bñ‰©tþREŸO S±—,‰)ièLÒ om‡>5+³¿ý%ìœ^B˜Ñ%8Ñ=n4(¾½Ò»C³ ›ü®ƒ§èË%T«¦nU$ÅKÕ¬¡ð@8Å?Kõgªó‰—+‘P–r¡¨<Óguδ¦ïEÈÑ÷íEÁîQútä4HÂpàÈ»\ÅÇ“} M|l„äåîÞœ({×SHGŽQ}Á¥°e­j¾%¶4:ZƸøDúkÓ>B¨CºÎÊ6Ó¡Sq¥ê‹å‰a+‰ —«¸–Ï$Öï:Lf³EQŒÒ¢ò±üFòùa%[JÔ+m,îR–¯Û<þþç@ž+ ^Kß-Û’ç\aX‘ËÌ*{RB ›Ÿ¿˜˜BË7íSᦶt)pïõêºèµÆ ËøvâðjÒ=CzÐïëvÓîC§`©±Póµè±Q· næ-þ{͘p¯Þ†-Xÿ[ñŽ»D5ªúÐ݃»Q§– iÝ®C´ý'Aéiݸ.ÝÞ¯5-B|_ûfõ)®ƒ¿­ÝEOÞÑ_—)ÿ[÷c.›öSjz&µkæOÞÞ— ¯UºUiÞ÷d€xô€Î´r[½5în`@ôóª´çÐizã©QTÕÇ‹Öî<È4ù4å©Ñº¥ì—U;i÷áÓpY$êÙ®‰KÇd'ûŽœ¥-ûŽÐ€®mèG̽SË”ó„s !§½ðàmäîæ²:¾Ÿ-^­ÆÀ:ˆ¼÷. <›‹Êð·†M±.IèRðs*xžò³´|Ý[Š Ÿ°,0g­þ|`HCÜÌ'±¨”ó/ŠœUæÊ(W£z58“Wf‚¹‘«ÌÏbU]LJ³V¤¼¬¸°‚änÌY´³¥kóÞ#tßОԨn }ÁÏ®jœ×‹•Oælꟻ˜DGÏœ£ÖMêçÂZ±e?=0¬7=ugjT¯&yyºS6’CD iN"âO¡ ¤¦gAaéCý»¶¢zPr¹T¯âM÷ îA¯?6ŠˆÖ¢¢y±¬ íA¬Œ‰¿Æ$X8Xÿ®-鹆è±O ð±{,3Sr¹gp÷™ëÖ$Ž-beƒËn$]Æý¡ÃWb®¢ŽEŒpaÅp{ÔIÞ§èÛÊåqÝ]¯ñœ£ï—o¡îmcœf|:·,C Ö¶Ç I¥¦(1Kä{ÿ]f…rjç£a3Wæ(€@@ à¢eÉEo\~±ñ0ò'†õiŸÿ’ø\ŽÄœgÉêëK®ã¢£IÇ“S%V ʳ¤ÀÊòå’ ôÖg¿èŠÆÈ[:²þæŠpç­Ý°ÐoBwáÔ÷tøô9ª]½ ,Fµu%‚+²U…‰6Ú6õו¶°ƒòÄÜ_þr,ú‚®8Ýå¦W‡æ4¤gÛ\僭K]Û4‚Ç“ZÁ²Ä YQÜY™éÒº1!!²®Ôå3 +bìXßPøØZÄ–±†uªëÕ›@±k‰Ø·uª‘—9sž.$$S*,nLÎrèÔ9]–¿mÓzz›­ûëÊÖÐÞí‰_ŒÕ(l¶’m¶ÂÚÖ–³î¹V%¶V±å­\ÏÝ?Š¢Ÿ­z©Þ7ì>L3ýa=))E&m8(a,U‡¢±@@ àpÆo?=ôY‡w\†‚Õw[@Pð3e8„èZ pC„Þ !r ð÷l_ÕÛˊاa5s ä'婨xZ¿û ÉÂ÷§LIq\ÏeÛ“›Qþ ëw¶ì?ª[)Êv4ûÞ5]aÚ»ƒî&Æq_ö¥1¬t\|¼s’gÁZÂå–Î-èg$;} JÊ^(½`a«»Ì=÷6v‡Û°ûÝ1°3 îÙNocûïrrš~ØÊIþ²ív< Ë ªË ![ ó(pùÛðg›BõàðÞüùï´yßQ(nW•>¶ð°kß»_Ú§]½|M—¬t‡•Œ»`šÿ–PܾYÄÙó ºË^Û&þ°6Y) Š»1Ú +^ì¢È×l…3ûÂ8}û×ݵ±][û:Å9fkÞÏ+wZOÅÅ+è{µ›Áøô§¦)®³QPœÉŠº•ç¦Ïìæ­M‰Ü4ß[l±ä-X †?¿^á¡AÝó^)Ú'“I“M&É1 5E²Hµ0§Î’ªÕ-Re»JÈ…³Ï9åÊ#ÎŒ°»¤2©QzºzNV¤;Ãf-ͽTŸ%­ žÂÅ–¹TcŠÆ|ËR>@\õ#‚~^žn®*¾Ë˽#êÍýîo+5κ|§¹Ò„曦Áà†»—뻯—§ãñHù%ÆÏÍXð^'¶B!ØÌÏœ»e©y.ÜÜWè ÷QoXc~ü{;{Ÿ}ñ†[—ó°ÚØ—Óqá e«…¾x? îÑ&ç2þ°ŠZêÕòClU[b¥‹­M¶âáîFÕ`YšûúÃW_¯=l»|Í{XŽÎœK€{áyj‹Yˆutekïá3T§FâxDŽKâwXrrÛ³%ÊÃݨ_³4".ʾ°ò÷ä˜þäén€Ò´ÙþR±wþ{’æ|³œr%Ú£IJP”Š £hPQhÔ/`zÈóŽž­ 1–ÐpG÷ë ý©Vmá¤Ù³óîj9ƒ`B@# ”¥2¸»?Ÿ,9Šn¹åv顎Ã-jÁO«µ/àNfµX6j²[÷OL/ç]‰»Â 5š9EÞ°³&#èÖ¦ ý ò‡º5ª"6)Ç HŽía%«×•üÄl©á仈m:)Y뉆Å&!)‡Ž•šK ½Îug»fÏùúè°5‹’ýGÏæVìܪ¡N¥ÍäYÙ5-2\Ø­­=Gà:È$l¥j ËÇÿ¹@í ,±¼l5û.víšÖÏí·Cs¸óAb÷Ä#xñq§–7•ëÒºý箺Œa‰*iiÑ Žn¹ƒ… ¦,© ©–ío…ž?=äÝñ¦Y7¤¤‹vG ÑbM“Þåx_GtgëŒ;mÇ•ë]úËR’2C+×¼Äl7F à­Û·5œ I–#°›=yóÞ£:kXY‹Ç;ö3¿\JÓž¹CÀ¿ÑxRtö.{·¡µqÔuvK:x2N'pTŸ2ÿÇ•*ª2Öº˜¿ÞÒ±UÐØ±c¯ú@9j°règHÇ–¿¬Ž<º¹ú6©WC¶‘”ÃÐ%¢oç–ô îÔ-·=çöù_Ä?”’–AlµbÒ¶Äœ9w1·[–žºc }¿b ½½ðW=Îiܽ·êß ŽSz'l y}ï.ôûýò­¹íŠzàåá~M[Ž…âþþ@®¦ŸVnו#&eh‹Ë3L‹aû²0ñaªœWœ÷*3;;Wd×¼?A8ÁV'[yn_þ¾AÏ!Å–¶¶P¤˜h¢(¥‰-Õ¿3äÙFœ'Œq*naËÖƒÃ{q3%=3 ÊW4í9xºaäñ³ohËäñAÁ?ÂÖôiÈtç×À‹;yQßå@*@³Ù:ñ¾Ÿa2cŠ2!Äü ÅÆ@ ÷ºB)ЇßRCƒi&ÓÓ™ãCCëQº¶û£`NÄ÷_ÿãÀ¯C0êõCýÆá!Óï±g|`È*¸º‡´wÄ8ˆù“Ï/ |™ë7…yiÖø`8ö 'Ik‚> þâz†i³íÝýÐß“¸>ÜßôxŒ5ôÈ ¶V˽vÕÚŸ¼ür–ýضãqÓC‚I¥¡ŠQyx¡éÍS¶óùßÁšzJÕ¤P(LoŸ>óáÁӶ寓ÿsQåævWðû‡ýñÂPÚf£Áý‹å E*WºR¸_2Ç¿L‡ë:ÀpŸ"ÉAŸOÛh«#ÞŽD@(KŽD³ûóÔjìênú}ýî¾Wœm±ûׯ=T »è¡,E¥%ëv9TYÒ}VÅý6(² %1íÚÔ5އ Ƨ^Õ½pÚpÛW–•¼çBB4g©‘a¿¬«:õ©Ñ2S[—Uaâ~T˜¹Íž½]Î>•§*+\z_a“ãc¶˜ð‹é«=áúf+ùûc~1¡…¬6¶y¾<Îd³F±KŸ­äßv¾ 9p;û¶\—iÈù•’–‰x¡¼®rŽè} V±ÌCàJh_Æ ìBü²/,÷øûë1Jl‘²Ï#•ÎÜ.ÿ¹1Ð'^Ž(¬$2^RbJš´n'hØwz¬|÷ Ú©åLWÝHp>¢çC@&9]’ÕW5Uý¤„~=)C„Ñø;(#[dYF;­'ëãb,1Ñî6£$e[eú–eÞÁ9$KòBî¿»sƒàAõ¥‰¦¹Õç™^KàóL¡Œl0¬[RÀ[ï²b¥+/™>®’iN¾?*op=“i±[ŒùïÜÔÃNË{ ÑÅçŒÂ¥€ Ð)ª¦¾7ì;¯§(qß*I^R£Z“µ³ÆJªú9äên2ÍÉõ3zžÿ‹#÷«¦y~i©;¡Zñ2“ 2YYÌÙ[5Þ^²s‡fÔÎÄoƩƘßW’¦ý‹úÏY4uíø·B‡…Ï\›GñA à„²ä¥ £ª>‘™•ýÏœoWÔxù¡aJy*&¼ _9k8oË1ÔnCœ3†w°™¦˜Ý»xºuß1zhDꈼ/åžaªç‚rÄ´j\Ö#ç‹ù{ëÄf¸Ñ ĕإf‹Eßßv4^DA`?ïÞ³›ËÆùm?ý™Ú7¯¯“”öžñ\@1-3K[4hž!—ÏöÇ»ûËôËé_Ž .íåÞÞšŸ~²»¥}úã*wï ÝòQî‚\gÀ4œŠ½H¿­Ù¥»€±&±W”ò_³ÿìã•¿dΦ(ÙŸs䱯·GÝŸU`å|'Y™t¦ÂñgÌÀ7¸g[å×Õ;•mNÌXµÿÈ@,´-I^!Ë͉@¶%Û–žïaÅyŠÇ‡ãMsþ7Mºj†¶ƒå¥?vϺ<ŠÇŸ‹B¦ß}åÒרÛ‰Š/‚fŽš?mÚ2œ_ ‹Ò ,ðO, \lëbüôY« ”Éi–Ì8·„Ï[Ìú‘  hx©ìº§+KYjÊXƒM6êÔû±–£/ N¹uX2å ·Eùí¹ ™;¬šu)¢­_@Š„&pvèäolÝïzÊ ³âA± E ƒG "mÐ;³û}z†˜'˜BžµZ´ÍPà¦âò »*y‹#wš9 4©Ž$¹µ ~ã蕎–Àê•S®åö­EÇ>] F¹ëSàÞ+¾r >ÐïásŽE/·…8”³Tz ¦vwÑdypJzV\èçh¼ d÷˜ò(ÉØ1çÀvޝxbô-ċڿ $qa7$v7êÚº±žK¦)ˆ–{†ë”#†Ïý %ìŸÈtÿÐ^ÄLiœl“sïpÙ°û(E‹Ñ]¬îØ•°HÓã>jTõ†»SmÃ{ëcc§×wÔLÿÌÌj·õjGoþçz;ànÝŠû¿úxwÔhå×ÇÐÌüâ]q*¿Qo<ç1âüG]aAâÜD¢8/œD÷iÄF=uç¸;*ƒ3¬™kx×Üy%’ÝLÀûM_ÿHã(#ždɘWØü³/¦ô‡RÔy´¿²¯£yÊ+ø³&©ýìÏç?ž ëÈ9 ÖÛ5Œ‰ÌÓÚZ4^ ³ +K9EUs]n“sB»–“ÕvŠ’~zaÈ´?ar9η®´¼ú¦IA…)J0dio…Üq!R5ä¸J{í%½ŠDß‹î¬Øæo#> J‹€P–J‹ “µ3MûW‘«¶ÅÎÖ‚ˆ­´7>\¬~þÛzb&-Žå(Ë®8ŽºEÏ9Ó·SK:t’sÃrÇT׺ªCqá\2œG¦°Ü36ù ÊÃ×îÔ•º!ɦ}î>àØYÝêT§†/b0êP]$Ý ZiÞåæ }voⱕW†Ç,¨pÿ/=4TO¦ÊvW-LRºë÷¾ú‹"¶FêÄÎ0Î%4}Ü]tïm=r]ÖœA.!Cá°{ÞócƒZ\ë˜mIþ±ðšâŠ@ ü7M=ƒ­iø­z –¢‘,” ë€ÜjöPØžÁ/òÿ v<—Á pEd‚‹b^cŒ{+ô9²ªËâ,!/Èî(_Å›¯]nà{Ì܉3ó˜‡4IÒp/$ÚB‡ïòŒ¦/3$²xHñy΋ ”%€è¬],0½y »>SãÌ¡{ ¯|Œg}MNdÉ LYvÁ+j±åžÉÈnß>޾VPl|®uË+€ÜÛ÷YžÇávEÕ7–Þm…q•çØŽë“iÓøGçžçM¡]@ymZº~ï°Vªí@ Òìlœ•-x¢Š‚ÿ}"®Ïz:öâÛx.ý×7Š2OQÇõàïâ Óg=›­™w›-‰3¡eÂ=.w"²&³"ž“ òÖ…¦ÀݹŠq {Ò*Ä…J©–ÌþèûX”þ4™˜Œ,AÁËI•†§i§`:‘F·•¹]KÚ.(Os?ÛHZ#ôU,þÌíC(H¯A I‚ ß܀鳶„OÍñ™·ïûÇ‹f.‡²õ=V3­YÚUymíŠ'÷i4«okj{ŸðΜZPê`€»z/À’w SÆÂÀ¯mõÄ»@ ¬(Ð|ZÖƒŠþËç‚‚‡ÄYgFª¤}_»ZÕjÜÞ—f¿ò 2Öò.œçTl¼cÄŒc×Ë=SÙ8†e[Ôq=Ï[šb@4Á¹j¸ðØœ;ç2â¨XY,ï"Y yvÆÊ{üҎǾá¾[1J,Ù–°Ç¢Oþ±La¿Ñþh]·Ós\Ú‘Jßþ’´®ÜE«¶ý›ÏVú^EŽB`XUÕÇXB†8ªOÑ@ÀÌž…<³àU÷”“Nyú4·cÁž¦™5 \¿Àz‡¥ý5®` ƒ‚b°æ?–·!P” ^èW[OŠ&ý¥c ¤éñJ‘ Loœ³]ƒ2´ ý EÍÜs8iDGì{v–dZcþFÇP4¸Ž¿Áÿ5V´TÕ²Ö4Ÿµ+躻—ò*úÈ"‹Šx¯¼®‹Å’sDÿÃó'¼U­ÙCó ,Ö@aöB`HÁ dþâ³@ÀÝà€ÁD僀Éô•G¬%öC«¦€àÀzßçƒÝ™Š+·bWù+þ¡Wç|GOŒéG· ONa¹gJ"åýC{Ò·m¦¿ÿ[·œy"WÎs÷!¦PgÅlòO½ùÉO`Ãk Ç•dŒâ¶9B ü &/œ1ùW‹ÛÜéêcGõ„zX%ëåà;þ(ŸH„\v¢Îÿ…z¶k¢³«Ýh&û^ô;µ‡ËW 03ÇÒy½¾Ë"O—ýx‡H· HB<Üö§+åqûæþœÛ ÛÚ`LpU¥œ¤˜”Ë"POic9ü¬#°øÏµ -4M¾ší`'½‹\FÙ²â$¥kôáro’”ù6Bxqü´Y`«£úȽ3Ý”«,ò*P‰?Ž¿êåçõ· (£÷_p¿ó„Âô|ý¾¶ç÷ª~î$'fÝ¡Y2V`ìW F·cªjî¬Z- @jp¨žÒïsûúE=ÖsC™f>¨™ÕK»â=^Ô¶¶zì}0.0d"~t¿É9‡™])Å‘Û`TÞ³˜­O&'eý O†)ð¹`¶XoAŠŽ˜cÞ`kc¨”j~Ü,Ñj0N…¹oŸFYM‘—ª7`U`53ÙdïG! ”%G!é$ýðB6Ö³âôã¼.È£¢0c›£K#°ÑÙçžyî~^û\-Wr®äžàü7[!Øù˜œKa¹gòçƒáºùÏåϽ丘ʛóìx{zà7‡[ùz{ÒÔ§GS:¬J¬D•GaúmP&c“‘ä.ñËjŒqo…ܧZ//Àz­íš2á†Ó$°ý÷d¬~ÿŸ¹ûV‡)JŒcYäé²ÝVÄþ¼†¦>5†êºW~—Fv£Ý»5>!¹¥ ñ.p˜A‹ÿg!Ϧü2……¾‡x» Øša5ÓËP¨ô* ÔÞ)‘œi«oPÜ~0kY}‘¿h±–bõL#Ë\{Ÿ¯Ë²¶$/áwáWû\|š&&‚úz”Ž!Š,å*QÜfÎäÉi¯„†ŽIÏÐ>…Eåw³9«:Úgâwåw£—ü‚iZŽ+×-n 7M;„9½>Ÿ/ÇM]»(8ðËâö±(4è[`öhŽ‚yµuqäæ ÓgÞfQÕ/Ífu8qù÷û°&&ÍúñUŒ(|êÔ$äì¡fj  Lý¥œ…¤&¢'µE àp쿃ï\tXþÀ‡ø;Ëâ–+tñ>Ô·s jX§mÙw„tm£ÓÌsŽ.Žiûféf:k¬~Ô´ö÷ÂòÊV$S¥ŸVî Ã§ã¨Š·—κض©?…ÿ²NÏÓåçë¥çézxd ‚Å+LQ!!™f¾ô}¹d˜Ýiìplp¢ >‡þܸfL¸Wÿw1Q·®"—nùƒ ~>§Óÿ‡~¾”%ÄŠ46¾kô˶ÐÙó—¨6’??<²/5C®3g,ïý—ùDÌÅuXd wFùŠ"ž³ë¸v±ñ»(7LÒfI«æçëv¡°çûï½ç›–l®>?4è´£Ðaæ¹xshÃ[;·Šv¥$ÏÅ‘›Ý ½IV¯Zã GoñâÅʪý‡›§œ>´eË ®„Iᳺù®¸ÂóTX–*Ñ÷ò9Sh«Y}„“Á E©bn,Sžºx•{1ɪ|_a?¤#]ÉFåØ7ʤŊAñcÊv$ü…kÉú*M+[®­t(£œk+tñœk«WûfTî–·¶rÛ=Ÿ+6œ¼–YÏžK ^šé”öl‘d«èÀî­ÁÖhÕ¡&þ5u·PNvض—Fçûd4¡Ôääé:ç©;úé´÷<‡KI©4ÿÇÕÔ¼a-ÆœÏ%å£æÏ€[`BRq[s§^ßÙþ.:_qT•ΈÇ帲×c þʳÿêra‹qþ )ÊéùÅhã U‹#wa‰ šÇåè8_+zAŠsÊR ¸ê)ͪõgÙtaò5QÊ,h?ûi}ᲆ Ûû?›1mcyËàèñà#ÿâKÞ­…=\-åš~¾Ž¢Øýq®-¶ø°ådëþc°ÓãÒØ¢‚8=ŸwÊÊ’ž¯ktwÝÓ6ÐPк3#äE(FõkWƒ›]Œ®,±¥‰ïá¥Ä4(WÍmÕõ<]±° qž..66ɦü½ƒsë]ïàhäYÉyùáaÄ4Ú¶Â ŽY*³ ýØLZrÉz9FÏ–­>ZЇßEè¤&py³ å4ï—“Ò lkÑN#D ×A@(K×ÇÕ.i×a™ywœÝzD)?¢ŽGÓ¿o´ffe§k²ô`8hUËoô²)`zÈl¸ÝMê×¹=<¢7 Ê’;É‚rmå^,àÀ>_WRj:}¿üää:„Å^`HLG|[ŽËû¨~ïf¡ð_×"‘r5zlT?b«Sa¥C‹«JOAuTÐØ 31r)‰ë"[¥òû±ŸâäÏýwé¦Üjœ :úB"Çåžs†ƒèó—)Ól6ÀògGÈ ! ”¥!äB×e£ûrÕ’•¹|s¤ûóckIœH”²E )%~^½SÛuB‚eâ­w- ÒÝÊvä²í¥yP”^’;u+ÛÁŠÙ{A¹¶®×…}¾®Õ ?{î…<¿§4盫:-<ޏ !=ÛêÌŠŸÿ¶ž®Ÿ,ïã“ÿÞ²Ì9–"–‡•[±)dçßt£X"xëÊn‚¬ F_¸ÖÛÇh¸:¶‡{Îñ« ×] mc:ãûÞ#¾!YÁDzÖå2 €@ ?b5þÌô¦ y†ÝоX²^Ë6ëùî\xFÎ+zJZ&ý¾n7Mÿì7ëÎOfÉ$Míl7|»¾¢4=ôYЂ¿ &E§S”Jû` O/6dúÔîla²•ȣџ¨Çµn\ÊTŽRÔ<]-Õ¡cg/è}°k`Ô‰h[׺‚ÄTæ[ö#*Y¥bÆDv! ɳ5²…¨e£zx×¾£º«àú]‡sû)è YýÚz,Õ²Íût×;v=<€g€³žÛ¦=GÀøK+> ¼ ¼³ *ä€W·'íNŠC×E <8ðûñÓCjî>xfîɘßè‘‘}•Ž`Å1pÌ,bñR9ù-Ö´¿À‰kê§¡ÓO:f„Ší…IBT‹¶y¹hÌÀ.+LŒ~k6ôõ›è•Ùÿ§»Ä íÕž.\ΉÁfˆm`°³XT=~è°äqÉŸ§ëÅo+P²^ k8t"–f,ZåÅ“n‰ÄŠÍ‘z]¶,=uÇ@ú~Åz{ᯜkˆÆÝ{+ÜýªÓ ä [ü÷vú!âú`âÃP |i`·Ö´dí.úì|léòóÍ!(h`fò ¸oÎÈì\X kÿܽ¹ôùµ+ïsÿDã`ºQ–”å=¶O %E›|¢TFL!=$Uú/âÚ!x\½­w;¹[Û&ú"­2η¬æÄ®P'cãé ÁÛ¢NZ/%¦(°À@ÿE"½Ùœ¢¬Æ®ˆ~¦Ï\ëãå6 xÂ}Š£’ºVıëá`Íl±Ð[ŸýfÛêî° nuû .vì T·.©W ¸Ip…ç©°,UÒ/g˜)hçø°°.rôŧóðúK6´Z¼r»µkëÆ ï”·iâ_¬EXIaâàtÎic†k³Ð™ï>xš’ÓÒõÿªpÉb:i ä´"[`jz¦ni¸kдS1ñ®Á]U²"&i ,ßß_?1½\é¨Cªªuзv½îb¿¤ßgjW˜¢Ä2vÍ–LùF󸞢Äm9SþRP/‹[œMI²É¿tý^&ÓP f¾n;'Þ€@@ à eÉîR e àhópÄ ,šðöÌá)é™ÏnÚ{dԆ݇½Ü܌ֶMêÉýkJMÁøÅÔÅžŽÙŽ¿œLœ—æÀѳ´ÿÈY5#Û¬ÇÆ¿o0ukÓ¸„³qL³=‡Î€íl¼ç8ÕKáAö—‘Õü 6ö7)cy)›Â9¼ð®E•è w|/úvjQüUºëO_Ì Œ8Úô•ÿDi°È-  ÜTFÈn€@@ P&e©L`u®N±Hr@üzéãÝ3/¦ ¥ç.Äh Úwä,’2±â iU}=-µ«ù*È¥#×ðó!?X]x—Ý/¸õ±ÓÍq:9/Ø]‰©˜“S3(ïRÔ3q—´Ììl}± KL"Ž_£ð+Æxû‹ßÖ÷ˆí×IÖ§C¹[.xÁ†@ymûã`­SÖkÚ#R¦R[•¬þ¤JFEÖ²@Мmé²B¾'*£åèFßJXÍnkßÔ^_‚÷åFX‰ëEC ‰zÃ~Yk!Y:åçc˜R´V¢–@@ œ¡,9Ͻ(I>yùå, Ä|É:gòK¦«d«)=‘ж'”ž6P~šŽo¡ijWqÕ,Y ²” jÚqÄòìÕØ.™”]·ulvàJ†mo [§Yâü¹qï“o‹²öéÐLáü7­×…âdtøÜ™qìô¹‹t9h¢NÄXÀ‚fÚ%I’ß¿­c‹¹WäŠÃÀû>¸ vh2­5Ę77t†¤³.Ÿ¹8vkÁâ5jjZ–Ù()w¿?eJ“FuÅ)€@@ 8+BYrÖ;SNr]± ¬ÆpüÊ-l2''×”3U,’µ ŒSU@áN²f&U5K’?)fˆ¸íšž·)E¹à ÌîC¸) Ÿš` ›m6¿wÀ{áèÃV/°YÔ®n`ŠfN¦[ Ì_>HÄÂAðÿÁyr˜J™ Óg!þ Ö+¼ÌÄÞÉW¬[ñ‰©°n%[Ï%$Qv¶ÅfÝJ…· Üoõÿ%&ÓÓ™á9]‰ÿíh×.^‹ÙGrqÈìš‹C@˜ÐaÁâUÚ©¸‹š$ÓÃógLÊSA|€‹ ”%¹Qå-æ 'k¹a–O‹!Üg¦Àý¨þ$È'ž•c.ÔTuœ®ñ—S»! j=¨C%Š—A™þuçT«v>…‡I¾ÞuÿÔS¦D™L<ìD¹¬ìŽ ¹Œ{QýzõÄ5À`¿Ï~Z«9s^“%é‘…3¿Qq] P à¸XûØXûãJ8]1%EàÊV´.=ÛvÑ)9^l¡,9SÑc¸B>‘ǢŮ` èö—¬j}UÒü|áè‹d1FX”ô?`øªª$§iš”*ÉZФ) ’¢ÄÕ¥I VЦAQ%mÝÁ“qwa±«”„‰M (`ªý°_ÖiOÆà/VzjaHàbŠã˜0ýÝî`ík’æÕ·¯ãzwžðc€g¿ ‰O+²ñY𛻜@z›rÄîëò¯¾ÙÝèî> êRcÄìÕCâr'Qˆ ¸D­§ªÅiªvFµZ#¾šÌO¼É,”§+h e隯8QQ˜Lƒ-ûÌ•W1Ř\Ìú¢z~°øø9=3ëÞÿž¢>›ç¿,> ®‹@BR*-üy­õìùKH%, üöº ÄÅ"!`2}åcŽy„9¯YTK=v]öõòPýªxIFcãJ‹4”ËTÊ4›ÕÄät ¯20 ˜‡4ó¸5ø„]­Ëy"¬$ñ}P ºÛ«q/xxz¼O‰ºâ^•óÕû¿'Ù ¼0=äœ9ÛòÑŃgæÿñÇ—BaSœJÔeh$”¥ÊpÅ@à¶Î­¯Š°zõ™©¦g¾˜ebö×òp'Ò•$Œe|øÅׇWñ«¶1²õĽ*»/z.òÿ=ÕÁßÓ»õ:5{ùñæS¾÷Þ ŒjÅë¦ i¸Ù¸eò­ \Ÿ~úIë6pð!³Ùò8³ öh×TøØ»â,g™#¶ ÿ.ݤY/·ÙÁ‹‚ƒ¶—³å>\÷CžâAwmXû5¿—E¢4®Œÿ­_ÛÏgܽƒäÛûu’Ԯ±ÇiÛ±`Lzuh&µnRùý.z¥fd?Ò}à ÔÝ×nC½²R˜øÙÈë'¦ruüµ©¯zùTYX¿N5oq¯€ˆ(.‰@þ¿§Óq—¼2²Õ‡;õí—¾wó†e5©òxž–Vv¡,•AÑ^ P‰ؽaí <¸ q“nÍÈʦöÍëW¢Ù‰©83ç.1‘ƒõŸýÇŽ!ý@†šw„Ïxí‚#ÇpÖ¾ÊøÇ]ÊQ”hn¶M¥çÇ•kW¯â¬P8\5ªúPßN-åø„d)öbâˆn¥ìÞ¸®,w›Û+J¾2ùUOoß`Ü+÷Êi¾BR"põï)…Î'¤ ëÔç–´½[6²Âäð ˆ2~ž–‰œæºéÍ!=‰NJ@XpÐÛÈG¶zû¿ô˶ètí•bbbA€•è#¶Ñ»_þ©=w),”. zäJz‡Œqw"Ál,J³±øÖž½÷VÉÍ(ö4‹ú}`¬3ÆŽsê=9iÚH´åuŽ#­äÜŸ®(ÝýtÀ(X”Þ÷ª¨wHÔs%ìÿžŒn¡½0ñö+ßý›Nw¸é&ìJ_T!«@ "à ä°àid‰>Ú´ç}ðír5)…cÿm=âUJ×±híT@!¢ aæ8µ7>üAóÍrÚ¸çHò×,ºÿÈ;‡ŸXJuÓb V‹å¿;Çwç˜ Q‡€üØËSzrnЃ EÉA¸2–ÈŽ\çî'z¡ËÒX—te}ðŽºÇ€‘wöĦzMq¯t£D7.@ÎßÕ¾ýÁ'ðý¿¹¬K²ä_Q!¤@ b0™$ÞAšÿÆ{ï}“˜’ýú®ƒ§'íü÷¤wÓúµÔÝZË=Ú5q Jc¶˜D;KYÙfƒ¿bA-ƒÑ÷:C.'SM?Ÿ+/_òötÏ G2[¬úüÁxH1ðJ¤ØøËÚÙó ®ÉŠ,§âfÿ‰÷ߪø–¿?eJÊø»6ãÝ~´÷«“{v:}'·cqPô…¸ÑÝm»¿r¥Òt&Ú^EXâk+i>~U‡áìN¼l¹bŠk]²)K¼IàY«~ƒAâ^]ÅYÝØþžüjÖŠ33^6^la*îßš¸VÊ’kÝ/!­@ Bà…30Ÿ5kž”¦=úè‘c§õâ¿·©·tn!÷ëÒŠükU«P <-#‹6í=BvV/%¥êõš~¾e¯ ê.yŽçøÅ’uªÅªæñps3X%$œ—sÍBܠȉø¥Û§ªênE‘#êÊ-ךLcùG0·ôXröøÎ;꼉ŸDvg"U“¦î]çÛN?‘[I”ÛB¼‰¯—‡*ΖÆkÛKbL3ÒÒã*+:%]ÜñßÇhè–%£›{}_Ow ý_ó÷„:¢*%úß¾÷iɆF˜ ÿ-dâż™Z©‹P–*õí“” áS§&¡çOù5aúÌ™ÙæçÖì8tÿêíݪUñ¶vh^_i׬µiZ—<Ýù™Zþ…ÝÊ@NAÛ@L±=ê„ja«‰AÞ$‘ô©"KóY½³æ¤'n¯‹¶}¿|+ÁK•ŒrOüzekªÖºQ³l‹µû"ád"!R&~Õ2ñó–Nø#^žräGç‹rwºwŸð鮟=Š®àÒ¤yd“ô1Ú)J[Qçºä,Äe©ž_/>v‰²ëà)]Îîm›8µ¼Œi|BB=YšÅß^/qîŠÑPÛ¯ªw…?;˜¡óäîV2¯XnoP¶¾aZ•§pRu~äÕ­YµB'Å 'c/R›&üõ»¶pzŽÆõjP‹†u®½è¤gø{áR Ì ü7á2ϬÒ@*”¥Ò 'Ú ôYð´€aãxÓœWKÆ=‰Éé#@ 0ñ.>2\`š5¨­áÇBÅ.¬NÕ©V5_ü8;8^4œŠ»H‡NÅÑa¼ŽG_Ð$Y–Ò±ïõµlTæ/Dü\åØÙ󟳂ñðȾe"ãgXp™Yfú|ÉíÀ±hÂ\_7î¾Ró@Á-JvV2™ÔÝwÖ{AU­Û°‘5ÒFï]çÎnÿ£d=ŠVWÐâPd}=@3X¨ü¶fElÔ»ff«êU|hXŸöÄ–à’–Q'õ¦Î®,1¦2ÉL^ÒÅß~±eIW˜Yñ*«{…1nXþ=C?­ÚAç.&B) ¸ÜúÒ=Cºß‹8œ{'l ½òÈpjÛÔ?·¯e›÷ÓÊ­hÎćd4‰~^µ“Žœ>GŠ"Qãº5é™»RU_¯Üú|8ÿê ë»wÏs¾ ¼9uðdulÑ  Ëev® q—nØCVÕJ÷ qظgâ.ÑÌ/—êýá9 ‹¥'µkæOîÇÏÝlj: kÿzúpÒ#º©¯Üv€tmíRÊ’þ÷$éOü·`cÄcxC®Ò¡,UÚ[+&&(_ÂM“.bÄEü2™ÖbÕÍ}àô5âDô…Q`Vë l–ÈhPÔzÈÙP¿V5™wþªúxéq5>ˆ­ññr×ÙÜo+p# ÿ(ò;˜ùèbb Å_N¡K‰©t1 ï—Ó´Ø‹‰ˆ¹±@?#Õ`PZ­Ö²¤¬ñ­b\?gòä4[ü2ý‹ñA¡ 6ì>lJÍȤ'Çô/ñî¬}¿åyÌ;§[ö£¥ëwSrjºÔÕciLï¸oÃÊPˆnÄíÜ5ºÎ"(J<ŒU¢Ï>Ð`eߢ3ÊpØÊÜuîB˜¼ârÐìaa¡—F) ‚ߌüi?þ½º¶nL^v1mÊéºÁߊ®B°’.î¸=·½¢0•í½º€Ñçèãÿ­Ôc.Ÿ½ûV~žÒ†Ý‡hѯëÈã¡¡Ô¾yªWÓö>“GYÚ{è4unÕÊ‘LmÜGÙf ½ùŸ1Äɔ_Qºž ]‹:KKÖí*we© qŸ¾k@™-ÝŸ½çV=ôxôEúiå6xOøSïÍ ‚„ºµiL^/XQ*°ËœÔ¿ÿ¶¿ýwÝeD/¡ BY*!p¢™@@ P8&Ó`úÜtå5}âܹž™—³Úa·¯”žŽgâ:ÄœOìŒÏu êE’dM–Iƒrtݱ,ÉY²¢U‹vºÃ(G½ ëæ™^»!U_xHà;ãCÒv<3ëÈ™_èÎ]•][9½KJ:7Ûÿ=Ikw´ž¿˜¤Ôr‹¶ ò^b¨"_¨Ÿh–ÞžœW¦ÌЇ—qZF†ù>,@kb[»éùtËT öv™ Xù;ÎYˆ#®¬,§jB¢V}7íÀw(:þ2µÅ6ïš/^¹@òAuªW!ÄÒ mõºÙf+¬Ûiï‘3úßFÏvMéþ¡L†uµp.®O\…öú4¬w{ ùüztT_ý3×úbÉlŠx ]/Úwä,­Þ¥/öyÑΛòwêF}:6¿Ú¡ÃJµ¸³)´ü®/ñ¬)Ó{u½é¯ØI¬ø>ug ‹^•q=v6ž–oŽÔ•%¶0Á²Ï¹qôëˆÓ$Ä—Ò»êŸÙ5ì–N-r¿­øÎ#ß7NJÝ©eú*¸Wêц@ðÃV|Ý ˜¿Ÿþ êÓ#·÷…å+Ig×<{þÕÆ÷Š­øÍ®Ä‰nÙw”8éy4Hf|<=ôïÖ¨þ°aæ‰tG`qi…~›>^ïÍ ü~6îÛt÷ÄÇFߢÏaR ,Û´—S2¨©Mý»Y«ZÝU/Ö3Æoù–}t99º·iBw îV¨û8Ï£QÝÔÄ¿–þ=>‰¹³²Äøä—»Uãz„x^š1á^]I= KÞ÷Ë·PjzõëÜ’`¥Ñe`!Yyý_LÒ“ž•M^쾎¿7ž¸nèg;í9|š°HCz¶£á}:ä¹?åùÏ~Ûß‚ý߆°,•çMc •y¯½Æ–‡]W^¹»žoJ†Z m5dÍRCÒ¤PjH¤Õ@Ì¿Ù0Heቜ ƒŽ)[Ö¤Tø=œR=´ §M;ǬT¹ó`QhМñÓgEddš?ƒK^¿UÛ£¬øSøÇÏÏ×»˜½•]u^„„káĉì;zVe%î3»ðšÕßóójš¦~Á£Ã:ñâ²Ù†ßFM¶¬)+iÚÿp…ì­ˆøA×&íÓ ¼ËŸÑ1e5f%î7w±qeRfSÍÂbl/[lIØy±5©yýÚúxU¼=©+‰HäÊß/ì˜ï ^ø` ïï° æ†÷íHµÀ²h4äÝc帗ù‹W‘»Ñ@£ûwÖ™yqÎJ–­Àò©+Eü9ÛlÖÝ¿xâc‡÷Ò˜ß-ÛJ̨Éñ3eQ°¸Ë½Oïº8e!¿}Ÿ1.S,ÂmŠ’íZÛ¦õxEÿÈÊÒŸ÷Ò‰˜x]9á8ÇŽ²Û—.°0E@áù †Â“ÿžê•òýÇ÷ÝÿÒ3³uå‚YEY™éVÑU½áJVªKôÔý ðxè­¿ýk³¾Ðå‘ ×9„¤Ö[)ð™;‰7|¾ƒ ô8Xi˜dgÖWÁÊÙHw ÝïÚÁ“±Ä©)ø;ØÖϾŸ…›œ– %$‡s€1øê÷ Øh©ÏŸ½O~XE¦€{X!þ®~·|3ÝÞ¯³þæÄÛM SaÊ;+<’õxXÄêR¯+J>ã“_îx0“rÿx.ëxüß_[pŒtçíÝh6+Î]âÐßœvYË,M}z4„Ì ^K“Ÿ¥[ü ×`;’ÒÒéÛ?7ëx±ÂWÅþo¢Å(Ÿ¡ó>õÊgL1Š@@ Ð¸Â®Ç {'JIx``Išåi<•ƒ9ú yàÒåÔ—~[³»ÿ’µ»©5‚rAT!ñ" |ú óKÏÓ™ƒ>ðìÙó—é~4ÿ=«F_¸Œ&M‚%í~ß)FyÑBS “´c.{_ƒÝî{xxMµ.Úæß±G@lºƒÄ¹¦›îœûbטºÏaÐòx™%ó Tz暊âDQ°-:ŠR·ÄuXáþߊm”™M‹JÁê`ûN³¥â¶^muE§S‹†píÜC‡Nž£nmÓ¶Ç Ó”FõëtÍØ J¡¯ÿØD™Yzù¡áúîù5• 9ÁÖ¤æ jë;ýo~ò“¾°g+WÎü^’RÚö%óš6Éiº»rþ Þ°Îpü"+¯ìæÌ.ÎìzÇ–V’m.xÜîa½t÷Ë%kwÑÚ¡$÷ѯçï³ Ïw ꪻ÷q¿HRN¬ (æ„…”zÁùã·Aˆ­ýüÍż€šX˪%„š'Ö—^|üÕ ï辇…»Y[6â¡^öìŽí¶_Ä Žœ¢¿œ‹ æÐR+Žl¢Ç¦Ž¬=dËÐÁ>»GÎ$¨¦Ž±NiÙ‰#þ‰týÂÒõÃZÔ$Tb"§G3Ãycš][¶š{`&¾çÑ3‰·=›ÐQ÷8²–=a"ÙËÃCöö2²í½d4–(7Ôv`:•Ìœ¾>P‘«3[­W™ò`–ÿLÁBS³S§Óí$½²Ã£FÀÞ^|ÑÎE›"ìõ:ô{9#qålýx,wñ?õº¢¼´æãw&š·8æsåq貄õ±CBV@»4BšŽ, ûK re‚–ë`F™#–qT¼“qI0¿k¤úƒðL7›ý\ Ó¤÷¿S+fÿÎç8CîØ"¶ž„oÊ'K6P;Ì|³IÆ®š…Í’´ÄÌ›L9¦D˜ ±ßOà€v!Ÿ¡uÌWŽÝ.È:ÓGÖ><¯jf8Xƒ–ö³3â|…`6ÅûcûaÂ÷Én‚§åç=k ž»¿Mž¿X òÀþ8×J¬ *Mòò´å{Qùæ XŽåºwl! Q5}c„µŒŽ‰Wp·¥Øñ阿¸cþcœ¶ßæH€œ|m¦‚|\Ú>q^-Õ€ÏBûM9&­ÝŽ×ø¸N^ó‹JÔÚ-˜R#0O~\œN·6¤UïF4×x`Q›&=Óódé±±yj¯Î·ª Ó­ çêq&¥õî¨YÐL“ÐÄÅy›ÚÚQ¦w‚Éj†ÆIiŽò‹,Âä$0=;»ú…”ô@(]<àÿ­À“ŠÝ`Uy ¿$œ^ÆÕË+.“¤» ¬Kø)<‹`ħ :m +—ÙÜàIÖ/VÎ’ž@…ÐPU¶|ºq›Nm†ïç…8Ý’ :i²YQ¢—ÌÙ D(ñ~%¾Î-•UM¢×ɰ ”2ûdÁ /a¿Ùd¨!œÝYƒ”?ÖX°“ùÚÍ•ƒ<»½¾e}G8e‡ Õ§Á#Ø?ælB;ÕÁŸ^Ö`ñ2¼SKøÖlÃl8 C…ÓïðÃÚÓS5cÿ›æ C gqç¹³¸s~ÇÍm+‘öÀníU²EKþ ˆ€ÉÚ{ˆ8JÞx˜xi‰¯T6l?XÀMõ~Ûv@˜}  ܼ‡ç†HÍ«•-Ëžëâ¨|¬Éaá£)4ñì»´Ëaâïë£jH`Ú¬jœÖ£Íô U}–ªá^qéZã³p½lfç˜ØÔoÝ–Æp¦lõè×ÍûU³ÉÁ2æáä{lòÊÏû† KÜà›,ñGZ$€@@ ¨"¬|G÷" ~ÀÝb&±ãàWs¸«{±A}›} ?âêô0V}0tyÒÏ"Ð…_ÛnL6{ñ¾‰5rÞØž±´u³F¡žXre/ÊþIªÙƒi—#µ8àËðŸ(É7É1?³ƒº§‡±ÀeŽvơļö/øc@% ÁÉÑœ¬@fœÌùr5í=tbÏsg>rÉØØÙƒíŸòÃõᤄdFÈÃ/µŸziÒûZ7ou£ž•ÖV~fŒaYM²X“X–²ZýEíÙ‡Ç%-išEöSâôÙÒ¿Tûº'wSëgmå —¨@q‹Ú^k|U¯V¿¶ÏB$?gƯVΕ{ YHuÄ5L“æ-¦ÇñFSU úZ:À1ïÕ§ø;®&6Ýc,9˜‡vÍ•m+ -¼OWUãÂïéU´]u!ßàÕU€@@ ¸a zõõðCªj½ÀI6/pgcB:}Ö†ÚëPÈÁ)ŸS²ßöO*IhaæÎYF³° äØh#US¢’êtÌ/ޝF€Ÿ™#Ó}uŽâ¯0³]Ö²ÅSEà˜Á9 Jœ—…$MPÒÊr`ˆËW2Õ`›±ŽËÔ*ÞgêZ㳨zµº´½³ãW+çÊ=kU ãÎ_ç`*)W2èrìA–úLá(ñ»Â¦Ž×\Ù6A«x„°T<6âŽ@@ ¨tH’I†6i$x!Õ S¯U³õ»³#ŠdxG")ƒë@}vÜ_÷AwÖ'hW^<àÁÎñ/•·¢ååBà,úÊæpŸaý#ÞØç•§Þ´fe<©ðü=Ô¥"æ³–~úm;5@Ô¼ÑÃú” gQØu¸ÑHØu”€@ ô°ÙÝÊÙº÷!¹¼Â¥’ßý{VÍ_ºO¾x¥ôTJŸ3tyüEDÆcíÒd.¥XeÖ.-Ál±ð]*=Œ7ENö1áM¤›–ÙN¤|Ø·ª´Á"òK‰£ë…€Ð,]/¤E=€@à:" Ôxæñ\%´=uÓ¤Ko¸³z_h—`I¢ÖGJ»Ø¡uvg}‚¶@@ ®BXº(‹:ÀuF€µH’¤S5KjÕX{iõ6îjF«%qpž—>ȧ¯¼!|—òÑG€@@ P9ÂRå|n¢Õ€@àš šhý®!ë9#ìá ²Åœˆáš¥Ïàím|õÙLý¥íŽûj_ Øy²¢„@@ PEà°â»œqºwOÆÓÿ–ÿít9Q@ P„°TÔD€@@ PItƃ=¬œ­Ô]Moóý¹KðRŠÖèC³4I;{€@@ P+K†™°Ó)‹à^ï«("¼íŸ;uK\”à¡ÔP‰Œ€@ ò!PD°‡™+ç7ûyð‹ÇrÜуä1ÏBæ!(y€ º{ǽÁwvú%éwÔ%h:‡Ïâ'cM=àçCm±xèãn'Ë\ÞtèÔyj\§¦ºØgyi‰ò7I)i´è§ j„¼¾][S5:Œñ”x)Œ›ÛöŸDd¸Þ8ÎgY/àÞñ³‰Ô±eCZ¿í"hÄ¡S j8òÐÖ) .Ãý_7ïC¸mÜ‹§çîëá’±~s=Ñ[F@h–Ä8UcµjÓawQí¦BM(ûÄ8wu¹ã/çâ0Qü•F_&Ih—40*À¾[‡æ4ñßCèž;ÚÒ¦ÝG(öà©r·ÊbµÒÂ~WÕrn:6ï9NZ5¦äÔtZðýojÿy¡Ú¥vÐæ=Çp¯QA‰3ðýƒ€8Š¿HŸ/û‹BjøÓ­Mê`Ñã?(-=‹ªCèòö2Ró†u¨O—Ö%®#¦ÿÅ „¥b€—@UA XJ*Âx¿©õ ‚†¯žPC;wõ^gÔaÝ%v“BR”ûc‡Ö½ÕÕuzeC À׋šÖ«E<ƒß¼amŠ=pÂNh˾ôÆÂŸè¥w¾¢y_­¡ ˜ñ甕Káþ@NÄÙó~úóŸôú­”k¶Ðû_ÿJÙ9fšûõ5g2[,ôÕÊMôêÜoèµÓZÌðkéëUÿ¨¦QK~ßNÞû†ŽŸKÒn‰ýMˆ@·ͨ3„¥‡ú†Ò™óÉê⬠¡1ÃûR÷Ž-詇N«[zföU(ÝR?„zC êÕùVUkzZ¥šÕýÉkñ¾YƒuáÛ« Š R „¥R€$²ÊŽ€wh…`ލýP¨ºlIÝ]}ê¼äüAE’–3}HLÉÖWÝU— [6˜ù<›p‰êÕ²ÉÌ'â.ÐçX ”™Î§‡t£LH|»Žd¢5ì)yÖ?×lµW––žIW2²I¯ÓQ—6MÔëöîLÏÜ×]=þsÇQÚ{,ŽžÚƒ†ö¸ mÆÚ…¯4”[þçNÚâ ¹»Õ†ÿ‰H7/^žFµóÞyûÌl›…ppâ[9±Ik™xK…Ö¨pª`¿äs=Y–íçâ@ P^„°T^Ey€@@ P èÝ{ƒE§Ó9˜Ä)cVÏñl润ëô³h?µcXíZçâð!ð×Î#ªög*4EMëÓ€;Ûª-Ù²÷8|;<é_÷Þ¥ú{<ا3%±_ȹÄ[ª×ë¨AˆMàbŸ%ÖVqÚwì,µo^ŸB‚ü©Eê]³šªЈeçXhâ¿«._oOí²Øß„ì=zVÕPn…o/XËBRáÔªI]UËÄš¦zÁ…o«~xW]Ä?/õ™EÝ×¥F@K¥†Jd•­Kу?¹P­æÜYîêQ—eñaú·Y­‹Ok¶2Ò]u º¥G šÝÚ©Z7­G>y‚JJZ˜Ð|ËÌ!A*ÑäÔŒ"‰[¡q*.Y1«äLm†6ó³ê––žM‰É6³>.׬A°]kPqýæ@ ^­@Œ‘_è÷­ûé‘þ]]ÚiöÑã 6e?&‘eA ?´HYJ‹2€@@ PÉ0¼"I–­–`•G­yÏÐmÀ+–Mîè„"Ñ|ØáÝÁ´%E­˜zÍ’L,î¨KÐ, k× ZRv®¦p; EjDÕ/Îñ±NÛ‰$^JUÙÇ Iõ8×l¶ßKJAô2_û¹ã›æôzÕ„5TE%cù#ðEW\«\¼=n¸Úà¡=oÃ^²kˆî‚ŸoÅ%ÇûÏÞߣ@¶éc²Ÿs´¼vÐpZ­2yËkF8…€Ð,9—È,*7C&›cÁ”|­õÂjµN׎]½—êÔÿ4˜.„³z;¶Êçb\]™ ç}á ïçí… H¬eº|%ƒx]ìðëæýªY^ÓzÁªª~H mGä¼Ô+™´nËÊÊɵ×ÇfSÐ"ªÚ¤Ì¬ÕÇé60©[öWC@³¦‰…+-`„½ 8ä!Àã‡CÚ»#±ð.%w {óÐÂÒÍó¬EO€Š€‡Ñãuð%ª†BLŸ5ï Nͺ§Ð˜ífD 5rŠ"»-d¹V‡Ø—ö5z¸o¬as‚ŸNP…¥aýo§5ÿì¡iý„àÉôŸ{Ù×MЭ=Å_H¥ÉóӮç©k›¦öŠØçˆ£-^»•^÷­ï‘~]¨Qí š÷õZ;ó z÷ËUt)MøŽØA@¥A@è$+Í£ ®A ßøì+féþ}ÏóLÑ*[ßÄ®·k¨¤âåí•™#³î;¨×ñ¶ŸãvÌ%ήQc.PÍm·6¤…áÏØ¯õíÚ Z©¡Â½½<ì×ù  ùä-æ{Z„2Ç bq[›É„Y|›‰Ýèa}Uó'ÖBùB‹¥iF=â–¡æØq,\†€Ð,¹ JAH Tt‘`^U'1½VÏ2¸…ƒmóý©pÉßkÈX,¡]ÒÀ¨ û‚’c3‹”´ûlê¤ JÚ5Ö`qD2MPÒ®‹½@@ ¨,a©²<)ÑN€@@ àBÏ>ߥÏ5’VIÕ.i§.݃Qþ@#(=;¬©XXGDìN"Àë^ñ:BÚ¿ChðʾitxÏ´+jru¿ËƒÙõ.[YžQE;åm—0Ã+/‚¢¼@@ ¨¤=T9ØG£:AjD[à…¼L¶¬×í¿Öo›  Ð™óÉtðd<|ß2(k'åäÂí²¬ý¾n½pAEyðó΋ôVGôJ~Nmø9Õ­ ­-Ǹ1ÏȽ«$„°T)“h¤@@ p=ý_Ê:³r¶î30%£˜ºbÓ.¹\XbÚ’N·H‘å¶zäØ a‰ÁI P ,,°@ôû¶Cô{ìA,®Šu‚À[$/ÊÖy‘•\~]OYä%§Aɦï!?_oêÓ¹%w´"OOƒ]p*¦©.½Ìýf‰Ã}çä˜iÃŽCô'ÖJº’‘­šsêuzÒé%Ò©«¸´ê O º?’­ û™Ò/í¦<§Þ¡­¨O—[UAJMîy„BXr®‚ª@@ ¨èõž3,–ìçÐXð(w­œm¸gð$ËZ—7ÞËç+ÊÊxSäÞø½¿-vhÝN¡Ëãw¸¼AP P`aaß±sô«6c1ÕLJ1†P¢OkJÆÞ"ÝÖCƒb¦ s"…䞣eî‚ r„¿§ µmÖ€ØÿL[sË] Є$3Öôb-Ú÷mŸ!ÉËÓƒ‚ªûûÓé„…d|¬³²s)#+—–þ±ƒþØ~žt'žS}·?#w=ûŠLWø,Uä§#Ú&ÜŒÀ€W²Î‚ùXd¯FqïRè÷'R%RìHQµKöjÅ@@ Àë‘Akÿ£_·ì£XO ÙÚåwíñ½ƒ=ê»UPbüYãz¸>®7!Ë@1Kþĺ[{Él¶¨mã6º:iýæ:²³³iÝÖýôÙ²ª©]HP€ºh2‡¨‚’ yÆñàŤŸLhàx¼ð¸áñãŽgäêg^™è a©2=-ÑV€@@ à< ž3ñۛäÁݱz¶a ª ‘Á.”aÍ¥' ]òqK=Uœhø‚iìÛ_PÒ¥+öž2s4*꿪O‡ý¢8¨Tð3dÍʺ-ûiÉï;è‚¡.m÷íA©†š7¤\ïv¿j;–ý¹[`X˜á6º’×ú­ J99j=+7î#oh“jU+2Tý ¤‚VÊ*ƒãÅã†Ç«ŸQíúuk––®Ô¢"€@@ P1è;!+’LŒÖ:«»´KËãÿ†Mý¡¼z$’‡kuнó,þu³ó…D‰ ‹3¸{ž¥Ÿ7줋Æz´ß7”dÉu~Ieé8×Ïíàö,ÿkí9zFÕ0q[]•˜–&(íÆ‚Çk·"/OªèM’«j©Út'Æ‹qãñÃãȕϨj£wíÞ Ÿ¥kc$r*€Îè5SÎÍÍ’:ÛÕ]‘ñ0%ý è¿Ë€B»Ä¾Rÿåc‘œC g§–pü?H{á×Ò~ …G[üëV:›x‰BjP·ͨÁ9}½êDÓò£3 éà‰óT»f5zfèÝ´â¯]´~"-Ö¦û{ÝFujVWóÿ³ç­„Ózf6"¥Õ¥'áá ËW2èû_·ÑáÓçáhîCZ5¢{ïî¨Ò¯îïC Éi*ÓV»f={_OÕdˆ nØŽ€[PjzµlT›ÝÕžšÔ«¥Öuþâeúfõf:ö¡ôîDí›7 „‹©ô+7¡?É @¼“šæ•Q VòÌØfg›éë5[)CïO}n«P=âötN¿¢tK½`5(¯÷Ûb±ÂÜ.—RÓÒi2£AG5àŸ$’ó0nIÉ©ôÍš-Ô¼Amòö.ÿ3r¾U¯„Ð,U½g*z$œF`àøÌóÐ.± £&xN¼¢»roô¾=ÄüER¨ûÎj7VÅ?§hP;ˆº´i ae‹5¬paŽ’uÛ­iâ¿Q‡– U¡&3[µ´Tæ^¿!Ð<1¾0jÞŒY‚e ôÔ ntêü8ŒVI¦^ɤÿ-ÿ›ºßÖœF ëCqI)jd2¾¹öŸ}táò÷XØ­-Õ«UC-ÃùËþ؉ÇÞôØÀ; eÓÒ ÛÕ{ü¯F€/=Ø;”^yj ™Á(³Æ‰£Ÿ}øí:e9Èî@- °Ù–äúrÅF8÷é¥' ž@Tÿ¨eªÂ?6Cã¾ÿ{@ æpÄ«Ý ×(Æ•5LG¼ÛÑ•Ì,UØÍ…pÃm.9žÖo¦•••MïžtGûft6áêÍ÷Ù²W\ lÚ‹yŽ£ÞÝ(¥kAÇíâömÜ{BÕY,6ÿ¥k•+î¾ÖoÖ*e`ݤØ#çÔ¨wìƒ#RÙ`ü8zàŸˆdXÞgTöVT­’UÞ o÷ƒ!ÁV3µÃ"u­$cáE)Õ¨Ó·Ô®³/4f»¹¼sdxä¿IQíî%EÂ"„ÈRÊò…‘Ó~//ík•ù-˜ŽÑ‘᳊Ëk–tÐå[Ùäå«âòÜÌ×­V¥›¤(€Á7‡9ŸÔÈQ2Ûcž®>&†<Á7\AÖ“¾õíyqð1Ûtp982bú-`Lç3 ŒS+vçq°£®¡þÿL¦g³ËAúšEÇ™æäXRÈ£ÏG‘“W@Q¤³2žÎN,.ÏÍz=Ý7ÖÓz™b¤lù/`ê #§Eý‘çzÆDN{úZåOÊ9¾b–ô3ò=„1(‘ÕÂïÆ®UÎÙû°Þùô‡p9,·ÉÂR¤³4D~"ˆvk¹]t„Ç´÷è9Zµq7%^J³›¿Y¬6…çkbÓñ±¯·480©Â×G=÷¤8òsâE@Ù\næg+ÔsõŸ- ¾«eåX(æ§õT?$ž|5Æ"™œ4×T×ÈaAˆMëX«µýàiÕ§‚ÃQË26P(‹rjT§`@ƒÃ§TaŠ5\ZbߌsI—NÚ_»T)÷švå$„Á ˜9&ú´©ÐýH4Ö§ÀÌD:…ö6o\áıæ‘+á¥v&iýÎEˆðlh<¹ÿYˆèÆáÁE*?¾Þ˜LHWqm³Ú²<£ò·¢êP¨’ÂÒÎWǺ!#¦öÉÜ\¥½ú¸æ9)dÆŒœw.#öÞàUŠŽbº,KúÕv¯ ÿut«¤PIÒ½‹—¿.Ø‹¾²"1}Þ¢Èi¯•b©‹ Þ¥’N:Sê0c˜)2”,ÊCёӦVÀæ¹µIï|â››}á9pÿÊ–¯tV™SÔ¡FM2&ø¯œ;ž-ýŠñõÉ”–åøAÒnçå*ÝN'¬²e0>˜a’tàKºcâàÍxsÜ #MѷǘÂ2KGÉù\A4.=ž"Yü”xçKWœ8MèÖ.ŠŠØTqZU|KXoÙ4O‘•áàc´`ñòî ú÷àCðŸb¸=µnžox¿—3\*À{—%dæ¦¡Š ø–Ûî­ÝµË/ [óš vN pÏmi㮣ôÓï±ðõ°ý¤ã[¢ú+µjR‡&þ{°*¨Lzÿ»T=Œh‚RL8áj6‹Š{µžý–žÒ b¶"6“ûdÉö|‰ð›Ð l©‹5T§Ï_¤Õ›öœîjÕ¤.­ßv€¾[‹G^›C!sâüŽþH^X•ÓË(Ó°Nz\UþÙ„«-Š!0àu”*rRÛ‡v‚Û>cF#á›QFaÉŠÀfÕï8„%–·x¥ª’ºwl¾ºÂŸºë›GÆ“';ذ,Ïèú¶¸b×V¥Ìð“I{oÈKVsæ)̤ÎÂGÈ&(ñ 0›é‹”GÀB¬üwìý!íŠÈVÊKR\ôôð/b¢"ÞŽ‰ŒèFtÒØ3ÜúUŽŠøfáô©KÙÈŠ™ÍL!\VÌÆ¹¯U3côÏåf%aœ*óQK¨&(]U£¢:Û…#üÒ™1ºí3¢·_•lj z}õ¯£§G,\ñ”Q2öÆ{Ж,I×Ô88QÅUYM&I†Vãõ˜×^Ëçž®ÊU).<¤—~P)R¼yã·_­±Øýg<àË&üÆnæ2—ž¹9Yc)_š¼ ¾?—…ïäZ^)nƒZ=UqÏLÐ#ýºÒ–}'HÓñ> fMd!¢µX#§¬©C‹tæn¿ÁÔ/'t¡e:vÖ&;³öêü…ËT·Vuj¨Á*þõ´DwQïoÚ}æv!xäl¶g›—aŒÍè6!x„š à±€äéa 5›ö¨~TÌèC€Š¦Pfƒ+¡)ã2¹f+í;WÖ.U¨rlŠÆëâ°ÖÍ"y¹}¥òvž×aâv¦ ¸Gn®Ym;÷ÁÙ¤õ›id#\8k/õì)˜Ã¿iÌð¾4õ¹¡ê6îÑ~ª†¶4Á*ôТ5k|ÍòXï ,!XéF$ÆQ?+_eyF7¢ÝµÎü/[Ema)Û;¬iµíÛ>þÌ_ÿR±gÃ7ú.¸oÝ~oí1IøÜ~£Ìò×(ú–5W¾û䑟à ð#̽ZBY03û©`;±yR¶9mfÔA_P]‘h§Î@£M±cL3›™ÍÖU^FcïL“ÏiM=mÆÝ°=ÿˆŒµnW,æã}Ø3=‚nZ¼x±~ÝžÃo@P{¦ËKbByúW …M‹¥ØÚÁæ{%IÿJôô©ª·lXÄŒ‡` Ö /úW8ßž¡FòèTØ|JͧÈýu’î7¸¦NE@k>$Ÿ-|+\õGàJ'ÍšåŸrÅ<l× ´Ëˆ™ê5FýË ¦NM9-2 íx×rF„GöÃ>EÑI³$™^†Ðy7—ç_–¨†H k…Æ„…©¦“##¢&‚“»êóœ‡ý9¬ŠôoA¿OBcøñ¢éñ=NÅ=ÛÝüÿ£"#ëÉÙ ž•4ÂhÁ©Øüle>𳏾wÖå¸/eE¾zšöTñmð‹ÿ;*Z7)¿C3øQtäÔJzç9ïø9s¼Õ.ÉßÐUI·,À{3cñÜzŸïk ARXDÔBœúuh1røðáWiŽ F)2˜šH²P¬-Ý^¯{N.ßsf<ïÑ›æÔŸÙm&hÅÿO±*Ï©u=¦˜z—LòíÄ\XWU'Åþ?-סçΫ]e Ó=w¶£5ÀÀQìzvna7s ö/º¿W'5`Ã÷ˆ®ÇL k³š5A$¾³ÒŽÃ/BV#ê=*-ñýõ±¡qÚß$zlÀê­vÍêApªMoFÿŒ0Ç*mŽÎlj5KÏ íA_¯ÞDo,ü ÌžŽF<Ô&~5(ìá^*-^cŠGÄk3ê!´G=­´ÿØüÐf65#‹²u•c.&[çIi0dA‡ÛÎ}p6iýf99ðY¦ƒIæõL»œ¡UÞ0$þ}­U­' è%¥¦õƒ©wç[1aTR¶ qµÅ<®ÊúŒ*D'*H#ª„°ÄfwÖ¬ô x];”WÅKV”϶ ©ÖyE"3LåI÷pa™¼N؈(u Á2A(º ¦òyò’T†1Ç’¶Œ&Stc!L—ÌòhÙ¢ü… ˜¦#{%ÛbáW•cZVE~eö±ÙLýêÀgÉ>½¶nÏÑdù·¢Ó?bÔëÏZ,æ§a þ6®Ù§}TAI¡©`Þ& úMf«9 Lë¯ãL³ne¡ K„ºÁÐdtƒ £Ó럦®vaÛÀÉ–u‘Ü¿USu^Ò^k¶2‚ÜÏcL³›|dš”Àù (ÁÿAÉ– º¡l24î³Í™ò·¸Õß œiQ.5µ_£ï+éÁ-gVW¬æî£M3›~lšrÂdRtqæ¨çP‡Ew±Êå bòÃ`|Õ™i]/À²2šée0ü˜c5?A*•oLd83šHE?Û=Û.•%ÿ>ôkaî”RâÖ¢¶îŽõ:y •9Q‹tAá#d~ÞeN/˜fÕ…½x[Œ'UÈ)îÙ2Íll1[7ßÏ dg¥ÜF`¾_Ë´¤¯Ó~çÈi3þ²*4 QépƒâåMð̚ˆ ßõÖ4ü*Í |‘F™¢ÚZÍÊ ½D£e?ãb]ºÜ_‘­QÀANóSIcÇ–ëÚÏTËü¿hÿ‡ø1Œ¶cú–Þ·š­¼®ú®Ž l†o#“™’7=iÍ¥;ÀÈ/õúŒŒ…oM]4þÍy÷¢¿q 鯸¼ÿ””œŒKYc0þ õýü}ÏaøR¾Cÿ†°8ÉdêmaºfRzùYnKÉïüÔ#z«•±j¡¤›ñm¶;ÈO.ð«l”$¥]uE J\×G¦ð]¼Çdƒ#¬|éšiPÇ–¬<õÃIÈXM°ÕL1Çÿ…X8sYê¼ôü†Ø¡µÏâ]m€>Õܱýà@ÿÅeTaBQE˜Är@éÞ@¨aá†ý‚Ó¨Gz;žÂר[ó¡=o#Þ´Ä¡½y»‚(w‘Î?N‚·løšxyµìêž}&^E$¾¬ìܦU¬ ›€(x9`’5G~ž× ~¼qDe[Þw ãÃô”–š*æò,7_Ã=„Š0G¡ ×n‘ôº{£ß wðèÕ(8ì%Ũ÷5<ññ”))|훞ššý’,›ùY$`¾fð{aÿm?,<2Bépà#ÓØãÈÿ|9¬¡`Hi˜‰?¤X,}q|"Î:“5L©`è—ÊVb­Àš—Ms«g˜3BõFé?8‡ }ÒÑ¢EÓõÙ÷1ëî F6Ú¶9vf²Ð3à²Zb“ɬ y°ý>)oi×]¹gè•GPÊoެ„ό֟šfý$ÿâµàBÝv”)R†pÙ Çb~ãô¸§¡ÚWZÉ¢ž=žÇ+ÎBƒòB^¾ƒ/DL?œ£Ð‰QÓf‚pñ_%“¦ó;`ËV̵ô- õ¯z: Íè+Ú‰†¡Þ¢W œ¾çÖ‰üŒýfOž|EËãʽ4ü{ëªwtóð®ªïÞãñÑÀCÁ5‰iÁlú+T};ñ ”…°äxU*š á*’þ¾^E’*,(9f*ÎE”ó:û!ê]Q©*IŽýÒÌÑð~9^®ðÇÜ^vx+‹‰—Öo›Àdefä†õ™5˜uñdüµ c~‚ ›¯òZEýno­®?ÆZ^·¬ÌNùseBØbÓ½Gïéª @ì‹Ç“ XAK]Þ㞣ꢻ íGèýëiBª=§²<#­bO`1+yŠ]ø*CWu¯++Ò§{ï .µ—%f£;!üíOЪÌCá¾0‹uv3[Û¤5A‰Ï­Šµ-„þ¤Þ˜ZÀ˜d-!Õ×ÊÇàÇŒD£Ñ¦HÕ~! ‚î]Ó÷[áþŽ›1£„¶@E’6¼'Ùé3S‡<õÀ®…°²”7S?ƒgYå{9˜•×Õu[c?/þ ^”8Ë»'f@ÓuA‘%ÕHtÛË,Ùby_«O–”Éœ˜f¼/*¡Ü|:ûñ=I–Á€Jk€ËJ`q?kš2-Y½ð”Î/4…ïiz·&ÚŽg¥[çH š³µøö¬?p¼Qþõ‚Ï ÿ:ø÷LùgÐÌápÝe‡ì£„ú‡]F„ Οí™ÿÜJA“x_Ëfú üH »–ü w|`z1Í^´ègߌí¯ö<8ø0rÚI”?a¢}Lxøy  •U@ÂDÀÌ™ÈrŸÎHŸ:–±KJK,“·Á~Ž¿@Ï}Žç¥;Å>SGrBT>I'y|8dj‡:µqÊ{|WîÂýbñåñµóérfo—ø±‡O“~ Þ•xÁY°ÇCRúa\­âÃÒ¼óœ~¼¿¶üoUÌ/€æQ?ÌÝ>`^>AŸaò@ÄÀ»×bõlÃЂ­)ÿ™¤S¾Ñ¨àÛ4ôä3‹æÆµLb_)`AÇÏÇ»R´õF6’YÛ†VÜ@a¡, å”x%nº­¥¥£åç²LCfÍGi »0_sø ë×…^z¼?¡}ݲ÷¸J5¨ÛöŸDtÆL: ÿ<ˆ× ‚f5K5?„¥tNV}ù $k~ÛzBй¼r°TÏK‡Ïœ§­ð'Üyø ÇÂÊMë—šµÔH”o¯>çŸQù*­z¥ •¹KÜœ«¼îê— ¯}`¶Uz Ø„• ‰à£1È1ï· òÜ0µ›Ëq>0ÿXŠr ÏÔÂÌöŠú\xþ?ZÌôoÙ ‘æ_`Ä>+\†é)¹zØ@Xaó+ç¿¥¸&6[Ã'‡,ªÝêX‚wÿ—ËK}¼v‚}"›9œyºêº*“$ybF: f†j÷ÐNódçNÛa‘ÿWƒ ýB5Á³DÂ(]÷DŸv-ÿ^·ûˆ!Á:óNô¶ä»Õ\Ò‡ô– p£ÀÓf›‘GNÑ+9l|Ö>Æ ?­ftíÇ{ø¦ŒD˜å'b¦‡ɨjùÝsÔ;svÒÛΖ»V~àï †ÙÈWj!Ìhl^z ´¯zö¨Ç-L|Õòð5ÃsPñÅz…Ÿ"àd4ÌÏ&ŸÏÜø(î]hŠ€¦¨¨a’m^Þy·ï¬_?wÝ¥ÃøåÍ»PʱSÜ3-¢Öõ]u_"žÆÞ‹±j75ã± ?'¼¦%$ “²Ô7A™™€a(…è§l®êf¢ŸvéõvË’]EÓ‘bl…>£þHÓŒ[cLSi÷ÐDÓ#û9œ ?…ãû0{šKJƒBÏhy÷¼1é—)2á™Á£LÝF›LŸ{…M{»ž÷\Œo;ÇPš±S$ñ2^4’q.ÚГ ocb¢É¸ùó=át;ûÙIbL"ÏPÕä.Þ~nJ3œ>€¥5à˪VV¢_`r: §´EKûÎ3’ÒûáቊN÷8|dö•T¨Œ÷ÓÞoíÅuÖíÇ®:ÐÓb;)IzvX}a¿eD×^âçå…#ÙX·LOw¶åÏ{~º„Ðõ’?þ{Þj$D.¦nšQ ,¸Ü¤n- r´ðÎ/+Ž*/•VXÚ~_ýf`ú›¸zÅ)ËupÐOcÀ`´ÌŠlÙ˜cNK€ 3¾69š/©B¢AC3~Ÿ–Ô–êÆ©` vC§½–Î&]†Q$ÌpÇ;–A˜áw v|ˆ™ý¹fË¥dÅœu~A ­:} Ç|®8æYvò‘îCÛ¯@ðYg‰Ì’•ÜÃVE­Ñ×ûé—ar꥛πQµ ‚˜«!èµÐéó×b©m¨³Lv]0Õ›ý5àÃõ!ºXPÏ¥3RÑf¾V" 3«N%¥AðD<“T‹%¥$ÍH©iÎüªûï„–º@2bn¯øi¯2Ð+\$Oóyüñʰdœ…)üà”Úzƒa £0 ó‚ÏЖA(¿ŒCæ£#$;kZ˜é~;Þr.ã=žëŒÝýZžÒŒ-¯+öF]ÒKƒÐ†0Ir<'1í 4º¿a¬vÕèã>kcûÁ´.A/¢ù:vÀn;Þ§ê}Ú·üƒ¯Ù’î'Œë–è×*íJißy-IûEÓ§þ1ÿ&ü$¿ã(Ž%å-ï=˜ÎÓhÀ,qØ_©cZ»Tî}èÒD,_ ©Út`æ—”cå1$’@ Ê#P–ÿ6Á:86PÝvB Çèþ–åÂêÎúòõÀׯÉ™¾8“·M*SQ6Åûgï1º£Ý-Ä&ñmR£åiÄŽœI¤$,2û£ý龞ÑLËþÚAõjÒ¨‡{«Áªû¹ÅàIkB™÷÷2wê:,ßÛqYT5;†ÖyØ*[ՙܢî»äš$íòKbC—Ð*kCèü•j¢ç•ÿZ·x¶>•R|ò¹b³³)•âaÍ)‰±-¶°“7l}L©à«Oæ@…‹sPÎRÞè^Ì8š§^P™í•܀ó1†žŠbÝàæª3§Ž”ýò´‹n­Š#rØlUûWΚðá–Æ¾ùNÈ‚7&&–Ôökr6ãªâ¬åôÈ–|kRפ¢|÷ø½)îÞUÄŠ¹àêw¾˜j\vyåli+™.*AIzyÈ$Ù¥f¼±Cj¿‰É¢×óè‡ïîc.k¼A+ºÉ" L/Þ—#±#kÒž±´u³F¡žf·­Ê™NŸ¿H¿cñÚgïïQa:0çËÕ´÷Љ=_ÌùÅ“8iز°Y±•&ÙŸ2a«ýÔK“Þïкy«ò>+Ž—…ÈkiWÒéÓeéÀe#íòë^b›XXúa˜?½²&ƒRà‘üHêÕÈH=>O¥ ³]9_" Ç›F½Dÿ^SL¤ª‰@¥5ä½Ê­ “ ºØ‘Ù±Y$@Ypû8å†IVËu©§¬ ˆr•ßß‚óºÂ½€ðßfålÃ]®ìQÇå ûáߨzZÔÓ7!ÛìVÓRW¶½2ÑZè·-ûabÔŒv:Ml?¤šecØ?w¦ïÖlQ­YÝÖcñÌÛFí[6¤DÌP¿÷¥Í¢4 B ;Z:…µiÎaí£‹súQ-,ÐÙ§Kkõø÷m±hçnêz+F¾Ÿ×ïP‹]„ßÇÒ ;hóžcÔ©U#!(i`–aß¶–M›£i•^¾Ý›îmáAŸíÈ¡O±=p«ëÊÊK¨0!Huk`$S/Z}ÜL+Žæªß›êµuW}ð-‘7=•VXÂì£úÃíÎ'ÈÑÔBc¶›ÝY‡ ]Ÿã”Ì5ø”¼Šƒ,ºçn8Œ8Dò¯óë‘Gæ»æñÿ—Ø))t¿ýX¸ ý'â©'—έÓC} ºRÂÿìÓ™úvmMþ‡¼ãà)UÈéÒº =6 «ê§qþÂåbÛâãí‰P˾ªö‰C/óùîç)¨š?Ö¤IMOÚššÙ’‹¾Þ—ºwlQ,Mq£xXúêaZ0ØŸö'YiÃ)‹š™…#‚>ß­n,=ŒkZò‚ï½MY4ÂÑÎó:xÑVn/hÄÆÛŽµ¼b/ب¼Â’^:éö‡(‘ûëp{'D7Aw=ÆÐÅÉÏ_tûäÁÄQÔ}ãГ¦x¶íϰõs«W×Î]²W¤¥6ÅSL¦Jûû¤õ£¢íÓ°¨¦¿—Ú¬jˆà»^ 5‚Ùœ–.@ûÃ'6‘ó0èrz¦zí¢=ñBÅ¥”+™ê‚ž|¿Fµܽ#„%[îàäé! 7ŠÃîZ×y…ÍçÌôøWè‘ïÓT3;ö?ªî%Ñ‘ä|Ÿ™ƒ,TÃG¢«ùûÓbøZøŠûG*í‘^· ¯¾ã7Û±_®9–”-®!$¨Ü¬4ño±æK×ô+>`xŠYüµþ t¾…”MÖ¶.GàÏ3fêßÔH·×3PWl|üû©âœ¾Œ%µÁLu©k j^xÃ…,ëòg"V~*uø¤ûQñº¹å1HRbõ&~‚ u ¸7QN÷ frïsS¯ÓÉ×ÿ;"ÁÈMø ²ø+ߤQò{à­ü ×o»æ=C·¯X69d)ס,AXRòÒ¶ù-•*±\µÝ|…ëÒø§û A8U}ˆ…Z´0ü™€øAKôê¿‘Åb%çÔ’‹÷½ôÄ=jˆdêà˜‚Üaú˜‡ˆ£‚qeN,dq>9›òqº ~J¼‰äz"ÿ̤wúûRÌP?Õôn#„§Y‹7n¸ŒðãßìÍV£ãMëéCw~z™®ä¸×hÇõ½vEžhR¯&;›ä:¢‚R¥G Òj–y/ï ¯0 rÜ-OA¢YÍ?8–SZÚ&Óz¯SÚü"ß̓Àäç-¬¡´…rq·1?Ê“©).&{9“i±ó¹êޏPZ°íÔÒæ­¨ùºOfß8é­}V‹k=`‚ê7ðð&¡¦±CëÞªÕ%ö®CÀ™0ÝŽ‚’c JŽ÷4AI»fÀš5š ¤]û²!°fw­¤¨aË¢ S¼—V§Sè¢Tº-ú2XžNÉ™ˆ ŽÄ¸,›ë9¦È¿²èvI¡1)7µ Ä˜4…ùiŸÐÖŽðˆcUja©Í÷ûsIg ¥à›_þ»KªSÿCgÈÄ[þŽ {=j¬3enTÞñ¦95°Ø-7ªþ›­^6/2è £Ñï|ãr€€qÂè]kƵH|Ï{Ná|#"¦ÿözäÐѳZ‚‘WÃ6ÎÃça³›Ç™dòb¬Ž÷GFDMC¹s“fÍòw¼ŽúÖŽž6³³ã5íxÜüùž¸ÿ1¶£Úµ¢ö¸ÿ4h/*êž3צ½Ý¡ zê:BaÓŸvN}_œé³»ó*Ž$e¸+=ð>æk´>(ŠÛ4²Z7ížgÐÝÕÞðᦢt<.Í _#›@ãªæ°@”c)=k”…˜Ù.ý…rUO®úÁ5hP·vFâKc©q`·öj>nZ¯–½!÷÷êDýº¶QÏ›7¬h‘·Sh«&ô£ýhă=©Ó­ìyÅAÕ@ R KüB—ǯÅìú5ÆÒ?.é’AòîLÈpfÈH‘.Â|ä©Ò×sãrfX2ßÿÆµàæ«yòÌ[%4ÑU=Çì{† Ã&þ+ñš c$ãwøÉæ¨ùgšUmiUG×ÂΘ×6EÊ}÷ÖåšÍWoIÉL½’;½¸²Ž×Ç™æä$¥þ!ï¬ãõÂÇ&Óç®ëuÄÉŠ,|ÏÙsE¶Ì½àq¬€0ç, Wç¯cèþ?,ðÚo¬iF¥´C2Ù‹g¸SÅ…=XÒ®åM/éíQñH‘…ßR9°,©(›á1Óàg[ƒ§¤¼âžë0Ã,q͉\³"žø).äi\_“ XZRÙñ,Ö KË̦åî¢Ø'UsÕj~>LQý¼½ì‘"=`¢Ú´^0µmVÖmÝà'©tÏm…&µ´ W’|•ÚgIøóŠÄˆØ!Á`Çh×ʲ‡Ð•B|ÛÒs%Îz_E[1?«À/E’é?c§½ÝfÁô×ösžÅ‹ë×í9<í‚_•Tú/3” b"§Ý?jZÔpYQž‡R¬-OD™Š'ƒ44«ÒEÒ‘7˜[ôG?¢®¡ÙÊxËÑ9ÆàÈj˜œ¸ð­ˆ%\Gqt¢ß˜º3lZÔ§ÈÒõ^ÁÿmLdøtÖ$(VŠB¤LÌÞ6)ìcSÄæ°ðÈG1¯ &Èm\Ü·}ËW†.bæ0È.JSGÈó¢¢u5ð̧•‡$ J阆èd¥HGN> ÍRrXDÔ]Èþ7ɱX†a¼/5™†çB³T,“IÑÅ™£ñ””9Šô32Î.Y’æ+25mæÿNŸbc  dÈ?ùÀôbÚÈioÐô‰Vsγùw ·œ{ŒÜ? "#N³àoŽÛÑNËÅZ2ô]täÔ•|¬Hº­’"¿ˆ÷¬úô«§nòS§^€fê’”žæ ùOhÑRENëŽwâ_ЬLÁuÐûÛ`ð|å#Ó¤„’ÞÕ‘¦·Âéâ ¼§-ðþazQ¹Œ÷鳘éóF›f6µZ,ŸÁ·5îí÷Pè™Q§¹­#§E¾ˆwx"Ús^e.Þ¹\¾n2õ¶ Ý˜Í2 Ï#øZeKX<}úXm·¢ð³t™¦ÌÓK¿"3 N.Paa¬ß±ûÁàK“*F¢½¢8“j%6ŠùñP¥dÀ— >a•5±À­mÎöA+gÛ;[Ú=ù9 > JµÌV¬ –¬VâåY: ô?¶V#CÆ%¥Pûæ ¨QštôL‚{ê$U k'‹‰ìTzÍ’Ö—ÐIcÆe˜}(“Ë$iéôÝB—&nq¦ôÈèh£LÒ}’¾æ2ߘó¿µò¿í9ÌÚ›`ªîÖ<»¢mAC'Ù,ή3«û©kˆ`&ëN’Þ1…ƒùU™8f²°ºŸ±Y]Ôeñæ#+$×ÒûéÛé$ý0¬ø>3w%ÑÁËRúÊÇàÛÞè«ë™ì§Y›ýVÄrÜúÂX䢨ˆ,(|ûíj`4ßÖ¥ÉèÕŒ_û_÷î­õCì]‡@x˜ü:“@H&Û‚%Γ>nP =¦Œ´¬s¦¨BºïIž_F~cíÛüó¢ÎË3úB°Øõaä´“ø]L1mfÇœ72Úx«l]È‚•ã½¢Žc¦¿¶×ÓÈ‹Om1¹°›s¤¤à×XiX(w-˜gùÚ®I5!(½¥H†—üjø²¦F—%ã{°Æ¡} ¾FŸ»YPâk:=í2õCëºß‚7ä²Åšû_/î]å{d‘'):éÐë¬#e:hfAI½e±|€þQÏØ¢>Ú¼Ÿ©|]5Y”i’Á ƒðv'„‹Ûp™45á=Þ:ègåLÕ«}ƒ‡¨zCì´j–gKWõ¤Í÷ç.APÚÈô@[gµè¸Š¶ #¸°iõñ\znéðiôÉÎlJÉ ïKÈèü°Z¬^±Üˆæ9]§í4"Їcb†¼´©¨¼˜Æ 0©©¨ð÷ç“mA–2³Ô91òÀÚd7:1ž¬ùÒRQ¸k÷ľd Žö’óVø»]–'Î5èt·á´ª´'–¢#)¼zÿP˜ô*m9{¾³ÉC$Rþ‰1…eê| ?)΂LÞ})ºX\š&&é»H”á—`=ÞÇfOž|Åd‚.I¢]2)ÁMER<Á̽óÚk©|çƒ%n¡U’dÙW:ÏÉñßv¹ëZt¢ß ÿmžiüeo£‘™šm0£º[«Ãq¯dZúá|™ì¥?K^3˜ê¥`$…ù‹#H.<ž2Òú™ÎèÙÏýGü¾”ö×!cu†‡wp‡Òj”›l0输åhF™f6ƽ&c¿;æ)êX‘åg ÅQú!ÐÁäߎùðóhX9•' â¬Q£ï•㸹¤SŽ•º¼"­X4}ʶ¹&dA¨á~ò$E‘écSøžLSŽÅ×9‚uOi´bxGÕTä»Êwð{s·¤3lSsI˜ I껚g.ØÄWïóS¼× o½A÷ ÞÕ{ÙÜ1Çlî‹p/+y2‚µ\žãë<¡ÒÀ?O/åNškç•mß?,%ãw…ÖnE2?¡»b‰û7O…°ä PëŽk‘ÞÛ”E=ÿ›JãWgÐ?ç CuütêE?  Žu½ÉKº$O9›¼<46Ç5 öS/cý¯Š–dÌNsr~‚¯¶ì6c=²Š–OŸ¼¨”­m•­=7^ôu1b—'ìÉÁ»‡„´5Kô"6Cƒ Â~ZJ€³LÏ2DÔû"})Q»çÔ3ÜÏ`zè˜ù°d¨/K_÷½DVÁ4ïWÄK}ÄëQH&Ìz+'ÙäÚ¨d:›TgTÄô0³;—ñOUû“Wµ"ýŦ:ZCÀTõættHÌqÉ:©®T¿ÆÅÑcZàg1§Ì„PÖ.-5sXRK0 Eú§H²ÔmDW,Ý4ú`ëÔ™}>ÉõLy.û¨>2ûsÏææ\ó“™îÁƒ…Vò4&xÎ]ÀùV)¿x¾ÿìåËe§›¦œ€é×¹Kd™tÐé~tcEõPõ12§=­fWŒïІzB¨æ6ßs,cT¤sIÙ4Æ4û'³9·|¿x’R_§Î9Òw<ÆX.0‡‰q½]»oP¤t+I~Úyá=0²Й ÜÜOá<Ž«|µ­Q,æé#_ú¯U¶Œ‘$Ý—|=^Ž¿ïb½LsƯ¬ÏæNã›öæ{A¸Þ´›IÖ¤I ñiÇåýððD´¥ \&Ó³ª††iV¦„ œ¯«ü°ÚfEaaé Wµß éטÉ23^@‘ð9I P±`-Òºf˜Úå\%qËY‹Ô³±‘míIÝê);+›®à«àãI%F¾d¬°äö(‡|=ýÔ/qÁ¯±sÍæ²ÚæeБ³W¬ Áožs„\˜ûZîܲøz!ľ‹/›)éRµjR‡N¿H­›ÖUƒ>¸°J·bOWåyFni\%$Zå„%ítX‘¸ÇSyãÚÃbÜë?¤Aƒä`Uv›Òq3fÔÊδvÆk}¸.•Ê•¤¡<zWÕÕMý#NŽJ†îhˆ¢SÖ×ÕÕSµ51aafø3¼‡oê#$É«< ƾL|elIÂ×È!9ÌÐKºq #§nt¸¬G‚Ò°ʢȈ^œþI?«R–Zªà?dË»¸4:rÚËïˆ3w#0éÙœ£¨Ã”·Ñ̯ªês-ž¾¾>éc‡_€ωyDÈIåNºïðS4X!¥‹N¹6ck¶¤>НìbORÞT«¯ ¿¥ãä#Cqþ£csØO>QóÍ–Ü9OåšEo㬊Òô·[ |ó%}fY¡6¸·T«ï_ÁM»^x?Æ4³ÖŒ™è[çk¡F…G>i•”Ç8_qï*ßó ù½\EZ‰6œÑ¤wšÂ·òu,Z-Ö•èÈð®ø1*ÀÈÃ_*ù›r>Nñ–È&ØÙ5ù*k[̹ÐWNA‰û¤x4Y!eOEÇÙŒ·Ùê9Æ®'˜Ulø~yR‡¥q»¶­ ƒyÛ~_½Û@Ï.|–‡¶(+p|‘ØÄ®Pb-Òí=hX+O Æ1'ÖZhÂB£Ú5èŸg(ÈœH‰õ •®8§Ü>þIª[Âÿå <ζP+Ç4°!ùyÒ‰ä,u._oOgɹ,ÿ‘3‰Ô¡ECD¶ëOÇÏ%Ñwk·Ð¦ÝǨghKzñ±þjˆý'x.¿<8ñZg—ˆÇ•†unn…oZ•–‘Ç@á/×%Çk®8ÎÉ’Ÿ„e û2×è½úÎ;Ÿ¦¥fO{Ù4·z"Íh§ÁøZ¯Öµ² Làà ýT'èø‡ž´òEíÁØ®“;b¤)z'›û±Ö(˜j媳ûÅÑ‘”˜Q5I#£¢ê(YlÊ"ýÄô¡Y8‰v1ó¦&0| Oÿ9—™^6ÕŠ÷šåÏf€Z±¿>䯙ùÕÅ -†¶•Ç„WcøßD%Ö€Ÿñg EyõÃéSOjÃ"f|"ËÖgp^@XâûJƒšs¤3váËìÏçeMx§Ž@#ªš¨}ðâ‹9ÐÀì {s†Ê,ÛK{[(yüŸÊ¤LZ.Á¯ª ~ä± ¤úž¹ð$®«ÚZ¼«m‹{W!‰=‰‡/ê雿‡ ‚ÐøÚp«§¯½7^‰“Ü‚µ+?pƒ8,ÿ\Ó„K’ž6ÈVÉÄß6……œô@Þ| g#«9Á"”#êI%ý7øÅc9+gë~Ä/òsÜ8ª?K„%þfo²ôžbÚ¤XñýÂ’Š…øWapF‹ÔÚ¤Â_-‰eEJ Ucb>W¡…%nŸ&sAÕ|ˆ(æ¶ký(íƒÑòsY¦¡Ã:\Õ}ê"ºðù¹‘ÂRFVýwùßÄrrm¬Ù“qį–[ÈÜnÿ‰8âMKVÀ3>[®Þ°=ãèíaPÇUYžÑ kx­Ø>ÓYAÛW±›¥(Ïètú/ùîĉ˜ ÿ%Ëšñ˜dVØÄ§¹Å’²ë×=GŽc¶yÌ™úÙ°•‘YþÎ^882"òXØ´È7é8ëüô˜!¨Næ gÀ8î1[.ާµK¢ò`6õ ho¤,å0 1MÉ _§?Ú³[÷…‘Ó6€™ûŸ…Ý ¿#Þ•$¥›Ñò‹}Õ@ࣨˆ³èÉ%L ôŽIRšáٟ׶S£>‡Z+zúÔ¶®.™@IDAT³yû-Çï[×—¢¢B¯ó1kLqo44¬*OBð9T#€€s‹üÚ¶Õl¶² ÜbíÞ5÷’´zÚÿá½[0é« cyCËúÌW÷ƒ·ûÅ÷®2}Ôÿ"yj¼åð¾xKÔ)¼3+9( k…ð^>Ÿš(Ð<Š:Î",?p#Zøfø2pFÛ2,éÇqµÜM àá{œ |uÆwb·í¬òþ—Ý×Z롱|TYì§šÝáÛXdb­ŠÂomQ 47Õ,po®€‰ÛÅíkZËW ¥­µ›ûàlrì7/|l@@„Ú¾0KÌɵ )ÎÒte~n¾;H” ܬ@',äqû[7¬i[eyF¨K7¼)Ž¿7¼a•½xÉ$@‡až7€#‰q¦͸>NoƒUÚ†©˜sZD-5lñî#GÈGê~¾¸¾«a”)Ù/úW’yæÌã»×¢Ã~'²¹(šcgÌZ0uj²vÛ<úÍwkÉuR˜ñÕ®‹½@àz"ÀZøí1u÷/0Mµk_xñ[Šâl[˜žçùoí=`:³&MJçwˆi•ø®JºIüÎz d-çÇ{7»ãX`Ÿs=sf ”`þÈ46ÏtÒv}üœ9ÞÕÒGs;^˜7;1mŸÞ¨ï¿Ð4å”-gåü¯(&ݪÙožuÔèõ÷ yÕò«+zÃ!Ã͹JhóĨÙOtë²ÃN?W´¥0 Œ | c ïË‘X¸ôÆðìĈ¥­›5 ð´ ËçUEç|¹šö:±ç‹¹3YKÉ¿wü{˜…MÕ*c­¤>#¯&­‚j xõÏÚ·<©ó êˆGþ‰}‘zA{4¾HEi‘Š«$ŒmfV]¾œF‰IÉô͆½PƒûÐv¿$CE]Q’.ÓÒÿ¤@)´¯G!!µ¨fP ª^=ky“‡‡Ñ©¦:öûbò%ºpá"%]¸D±gñ EØÒšÕUÿ.§ˆŠÌðù‚g3Öz2À$é±^í)$8¨ÌϨ(8]ð>]EÖ…ßÓ«h»êÂMa†ç*°œ¡óæ›ô˜>6ËFL!ñL4eXÂ8>‹o*OÃÀULŒaº{‚ s­ÍÞò®ìÓ«ZS®IGcµŽ{GA‰¯ç1IŽyı@àz#À‚úˆˆ“á\; uOÐê/‹ Äeó»ðÏt°id©¤w/~rHºä¯2F0Umd&¥‚Jüa'€ƒ§LQE8^ãc6ù+|-÷•aÓ¾ªì‚÷K’LòŠÙºoÁ8ŽWû)ËlŠça‰×Vн7x'L';á¯(—û€öRµñO ph9k[3øÉ‡‘Þã_ðAjµ‡¢|‘œi‡¡6€M`aÃÎø]›ÓŸâ©UæNÚïê )·æm•¹ƒ|­iÔº¾?y{y’'ÌÔ¸ÍÜö¢Bi_«1ŽýfZž*݆™tü²•8ÐBÍ"¢Î]‹îÍ~ÿÒåt¦°Ð­ëªã©<ÏèfÇÒ±ÿBXrDÃ…Çilô´£àPþ:ÌvêRºå"æ¡6úߺœ¥÷$KÖÔ‘Qkà·`Æx‡N/õ^$§š`ðžá:NU*2 ܇#G-¼¹=•ô®²`… *Kâ-羃I«^Ö£pG~+ú­©efÚ£§‡ÿŸÛ;u=+Pô0ų¨Âü¾ZÿyãѽŸ=å¢ìw©tâî€6û-•÷ë ‰¨«r#Ðfñ~Ëi‹À+#`©¦LÔÉ{¯0éS¤)8¥E²—w8ÐÌј©õòô¤ÆujÐÅÔ :GmàºzÐ綪ab J5ÍñÔ8ЃêÕ /o/µ­Üf6Å+‹‰Wá~3MolÁÞ”‘›N ™¹t1å Õ¨î/4L㥸CÖ(± ”™C­ëUSǧò<£â꺯 aÉOýãéSÿyÞ '6%±Ï˜¾YÚsh˜Ø°¹ÜtJ[ŸÈ'¨ª”ð®Ìe?D¿y©†L6Ç®˜-…¨9&Ü2/œ‚l?‘ÕéK¤‘§¨eºÇi¢€@À Z½ ¹l¶üÇ|Úú,ŠÕ˜FRÿÙ¨X-ç³ã­½Ýx¬[×uš÷lÜÔv½ÿ9Ø F£Q •Ú5& ´”GÓÓèˆw{J5Ô,G-e+Ê>JÍ3÷’¯œF ªé©YíòCû¸,Øp›¹íÜgSQýΣŸ““Cõ-ÐßËY”ˆˆn–äËà[)Âu;‹«ò³Ò%Øê¼Eˆ¯:~\ñŒ\Õ¾ª@GKUá)Š>‰€"A»¤¼ÁM@0Œ'±s‰°$ש·IŠ;—à¾`YoÙ{Ýí–ÆŸ½‘]uW-µHV³µd£‚œ¿ß&«uµùüÑïâ=»;7##¤ÇK“ºº ›Ð C¤5h–`Þæçë !—Ú4ªE^‰&¤SÇô”b ¡Dc}JÆÞë0ñ:Jœ£Þq0Ìê¨>4Jþþ~äëç«¶‘ÛÊm¶EÄ+Yi°)®ß¹f¬3¦¿¢Êy¤eÓy„aOLNS£Óùz³©žÇ ]‡©4}»yx%ÎQï8˜ƒ‡ 5Öó£[êÕtÙ3ºý¨,ua©²<)ÑN€@@ PQ0¿&K®*,¡‰ƒ¬Ö?,%µ¼Í Ùn޲ tú3-„jî…Ý—|,’ë8Å6÷Sg§/§gº$³ÏlTÝÏa¦}©Í-õ©a›ËëZ^vJ×Ò"aÙ3X¸úSììÏ÷EteŸÄln雤q48öÛa€ƒÕj¥&ª{éÄ…4Ò§'©Â ‹rfò¤Y%×±pzÅBžr6±à,+Ó8`E-o…êW÷ HÕªùS@6˜ÐFn+·¹,&xÚS+®ß¼þ'¾ïï‘MI™VJÉU(B+±¡B ¢±¿Ù¢¯bl ^,öì:‡µ|©~€ (>^®~F7¾Åõ×uoZq5ˆë€@@ P¥òJΑ•³¥Xüx‡bó4§¦=ŒæŠNƒ7Úž@–°"}/ÐÂR9Ep!0äú}ÛAúmÛ!º’wYm‘¼(›™pÒ—³[q=o/ù"”lZúÇN0Ý>Ô¯k+êÚ &\ןý@D;cýgbÔûV{Z¤ÞEi‘à ¸(Å<újÛU&I]bÁàˆ…nKv- Ö° „šX`ððÐSƒŒ,JIϦ”, åX²È¬daQ[×5‰ƒƒ¡Éò@H›jžzò÷ÖC›ãÁÈ[Õ(1^½Z5õ˜Mð<ÐÖÿgï;࣪²ÿÏ›š^!¡÷^EDA,`Ûuu×òswiºêZ…à\‚ˆ]wAŠeW]]°‚‚ô.H„Ð{ é™L}ÿïy“7™„I2™’zo>“×î=÷Üsï›9ß{Î=×_«’ÊueíæâzÁ$Š(ã5ß‚6¨°:dGÛË$TÉÖ‹#»Ö™-î¸D¤zŠ€ë#@bF¤}®£ø¨ÐöQ½fQýßVA`Z¨]ÀLüp—SÂw!Üú=à.(`I£Ó®uòú $l }£r"þù%Ijßµ–NgÒ>lx‹ñ©°WF×öhM{y½XõõQí“Mõs$ÀRõË\Ô($ $ $Pÿ$ 7|GV ïAÅ?íÖ½‘yë„ Äó ,9’šn“ÒÏBŒ€:Ø~×=Í[ôþöì™À¨6¼Ò ’'­ÚºVžß¨@M‡¢VKà^c“ah¡|8h@'sÍ\°šî¹éjÖ¿[@®\åõ$\6iå1-.ìJ鉱u²Ü¥T^¬EÂ8-kE*•¥º/8¨71®kJ­5BE EÅ`‰@pr¿ú ”Ôö©u±¥ˆ]ë˜ޤÆêØåŽ×QED„+v¿ãçÌ[°RMµ;Xü‡ŠN|\,\ëbiÇþ“JŒ‰´cçiÞ™¸èÈjí£Pµ±®Ð`©®ô”àSH@H@H K`Ä3E'ÏvÅÞ5avùï®p ^·´Íµni87ßn· Áás>©r ¨Ö$^4Ï@iÑú]tIß¼ÆBRsT7Þp•÷úfÕveíÅðþÝå[µrTÞªòsœÌqÒW{-ôí \ÖØÚÂKJ…þ>ë$£qÞÁ'»¤3¥ê¶"qÞ’ ZŒ(çPŽùœÁoüÊ@‰ƒ?X­ˆ‡þtÂ]Ï,UոĖ+Nj½X±Øõ]ì¸NLìrÇÁ$ñ}~Îà&}媽¤þêj·Zo]86IL &ÓÏ»+kÉ |Ú~(îÔ‡⪭ꂬBÉ£K¡”® -$ $ $Ѐ$j!”óÞJ“%ùnK - ­%')`IvHCpO€%E0ÿS»m¥9M?lØ­¥šÞìÔ)i• Wy¡ï×줤øêÙ±¥ßJ¸jEZôË—Ëf)É`_$gaîZGöÙó¿¼ðÒÁƒ¼§¡£TžZrÁ D ÇÍ D +Îà…ûÑŠµ,.«éƒU‰ÿ€’øãOâú”ìÁlYâúغÄÁ9¼0@Ò+Ö&Wä»`Z”<ù­îv{Ö]ÛÏ“’›Prr"­Ü²OYÓÆ}°~÷qºÿÖë(..¦Úú¨¶Ë)”ü °Jé ÚBBB IZí÷Ðä^æ&c}ÑíkÖ Ñ ºÖ‹öZE¡HÚµ°)¹ IbÝ’¯Òc«+Øù°`ůŠëorZ[ó‘ŸG_,ÝB/·H‚ƒƒp<ßÒ•V¤ÒåšFi¨™í9OïØ?ÿ”§ñ4ÿPEiÒ!¿R­7¼–H/X·¤€$Å’’+÷1§@À—wÕçZ#Å} …BÎõ*çQ*?œ7”I­'Ôíe‚M›dB£xb+ÓÂõ;Ñ÷X{ˆH€K6í¥‡nˆpêa ÐUeìú=DB†F>gÛ W¼S Õ Ý,[7Âùš@iG㷙͗\ë–dêðÛ-š÷ùñŒ²?PÚõµ<+ÑìªeËÖÚíû)¯ÐŒ5J}¡dùFB-æåPxO¦Ÿiõö}tëõ=Ý.aåÕÍV¤X‹Ä®vÞ¬HòzH=ÝßÝHƒZëéÏÎRš½ pÀ^C!¼¯Z[X V"ÃÁÒÃT×;O‹R `I±.[™¸>5àƒz?„ͼ‚tu´ûŠJkñ –§XD$ŒŠŠ¢¯WmC€'™múS9b¢B°±K§zX`©zä,jh€SÏ"8ý딜ìŠ0XêþÕ^ë¶;’6ƒÖ0¦k“lƒqø’ÏEºR¬8³‹¯máHb›öœP¢Þñz¡Ú–˜'ŽÈ·nçazug·CUU~}±"ÝÛÝ@÷v5R“ÈàPë¯É£ XÄpòIêµòÀÏž²Vëò“TP‹©¼„ªÝAe6ÄÄÔ>êÐ*™¼m-X ÀëbN¾–ÙÍ `ŠŒ0†˜‹†K¾Þ‚¥½ ºNœ8p \•‡ã‡»py3ØÞ è꜅“È/²V¿ˆ÷©«Ý?6eêmIwñƒ©/ýZWÛj¾M¦5ºóö9£ô æ¾øbÀ›d›ßûÈÚ=§öÜ„ÉÂ[0Nûà ±~ Xœ‡ãq(ž[H«ûñÀ³]Ó‚]wUé•7ÞÆNy­7æ°JZé—¹¦É;ªJWä¯ pJ’.cöÔ—v“Äðúªº– Õ1Xú{0èkHÞ‡#,a+˜ )ÀR9‚uY•°ÀÒ±ÓTXd¡Œ|µÔÒ”¡oAñ…tüìEêØº©bÕ`÷¯ªZ‘\óïµ´‘AdKA$Y'H5Ôv—휶ÍéÞaýè+&~×/çИ9€"ÂXÍ)بw`iûœfsÎ?{òø¾§ ¬DÕ9Y=²qÞÿÿ€çßÀFŠë$Ik1Á¾–ŸU%My½#ÉÖ¿#ÎNЋ•IÚ×Þofÿ#廪Ðñ7/‡'±Fu;Ê °TŽó#·Ù4W*rn@–Z–º/Øk°’ŸØ}2íyŒÇf*û%®ʈíu79l¯vz3m«V¦ìŸÐs±š·ªÇq¦ÔþØ¢ânlü=eÏá‡çW’´ßÏú¢O@ÌÛx39uì´÷­u²ƒŠ@·V€¥1)©ÿÐÜ9'uòŒªÊ©¡äçþ”dç/hoPÁRXÿÖn[—/Ú8Œâ6KßÒ÷¾í9GÉ (É’n3É.o*L.\±z\ØmU²Ù¨ éàÉó˜ÄbXojkRxû§SëfélDß´àc-ŽhWšs^‹T_­H¥[*®„¼K }‹&tÏЫé›ÕÛKã¥l^ûÀ4âzDȀɻÔü¿[¯lÕ?½©»!#çÜ>ì€=?Ò‰¾ˆÞ 7:Ž5Kfhþ½ffb”/e8Ïè—Sÿ “å7Y’[@)øXÒHS`Å:âÏ¡@Îñ•NCÈÅuECh§¯mìþÎÞ>öSÎ]ˆÏô¶'Pª°¼,÷ÇrÞš¾éþöÞFæõòpÌËÓnwÚåÍØñv›ÌÊ}_Xó @iY¤.òùwMÏd3±)¯Þƒ'†Àšð_hgoBQë§'Ëè?Wf]¥±)ÓfãZ7¼w§1žíPó„êØõÝÃaÝù² ÈF޶u$ɺ²Ç»iöü½g…À^m‹$K—ÑfõÚÛ±²qèYãú§ì¥8±¸o/Í›–r½gõœó¢î;1¾{á”]ZŽñë~wÊ?\Þ[¿âÝ{ ÝØ õÝÃyÔ4fJêtÉI1JOð;ƒq·{îÔ”÷ù¹bËwÌ€¿Â­?ɸuaqïúÀôÒ1~^üÜ3)<ÉΛa¡[…µ9“ ¸·Æó•µû±ç‰2fíÎwñì*ð{csÞ‹wq”Áë4¼ÏáÔù Ç1KÖH3Àÿß禦 RëcË“S–n—Z%ö›;v¬ïI™6íè‚wù/|=.eêh‡,ý §í!›ã²†>˜75e?ãäë÷·K–/aS•?Ì5½Xê½qQòý´¿4WÊ´à{Èù\µâ½ðV7?mˆ&×—ç_ðþtãs¸òx`‰…á‘8Zš½,ñ¦<Ëìü››ñ òS§¤ÃL§•d‡ }ë ›ZkéÞQJD;Œk‘„„Ê‘@öÍÉŠ-–nÚ­ä8rú-ÛœF· àŸ\‘‚!ÀÀE08ÆO3ŒÎƒLPÚ‚öž¶ìì+b ŠOäÛÊJ¼·|¹¹n@=ù™,I È<î™/ݹéV|ùwtêVó}¥ï‘³©¤ÓÜ©×K·€~’­Ðù¿’2rSrÈ&( #¼þ¢ “ x^™t:#ƒˆmX´}§!]…Ê %§c:ç4hõ?‚çÛx1¾Z’AÎÇCySøPïóQëp@žr'ÙîøÇï)Bê.“æ €WV¦~LÍ«(™2M’$Íÿ3è Akf6Wòþ4Ó Ø€öå8ìt«Jù€<Âñ#¿ûžCÌa3Mo ´ Ó$t’~  7œ…öü¥ ~¸ òÆàÞHYvÌŸiuº®Mtý«ôøèJ’Ü@÷ïÕ ”z¿}°¹Ó.ý@'  änLílvù “ ÕØ‡$i4_@œú,[F½©|–”ÒéãgÀ30?Ëý¦q÷gI.E‘¿ @i ÆöظhC YÒNÄs·‚[Ñøa:ÞúûˆG¿SÖ r&¤'ß߈‰„±㺃÷‰(ÁuŽ–çÛ~@ÀÝ~$/éuƶ6Ï%Ñ‹'øye<¨4Ô£‹'zôþ‚:&iÃ$þE:ŽP®ß?nz=>9µ¥Ýîü u%Þ×n¤¥1þ&Ž{ùÕÑü\§Ÿð´ð–ŸQ;\Òè¶!Ï ãMÓÛq“IÖ(ý™Û¡9{é&¾çJN¸+ “ºþ†ï—©ÔÇaz}| ÎÁ@Ÿ¬*5;¾[*ûþ`7M¥¹XYÿ·@W{ÃÄKyø¾r¯Ø,–»Ýìp¼Y-°~z.Ž. `üð÷œbYâ=y8ƒ§º’ôøá|¸§–ŽŠ¤wn¤A­`]ª+Ì >…jP}»´¦½;º9Øqà$ý¼«” ä~&Nª.V–ët’%ëü6xUþüm~Zî]2Cû»‘1W&(Im¡œœºò‰ëÔ÷ÓøÉJxÒô~ŒEŸ÷o¹¦Ž7Mëõi² ö;äG¡$ýo®il!f•@É¢ÑéÚÏ6½t‚)@¡Mź«ul) üQ…ª$w26‰MþçSOY”k/ÿf™^ÀJ^zK}¸·í²mïøéÓãÿùÒÄ3 ÷À+k< Mçí›oC[ ͵͈¼'hâoÌIòIñÓE ± òf%E”˜O,Þ™“:ék΃jµ±EvûÃóžzj:f« à"« Îåt)~À‡át+„éöÔ{ äNÁLñô Œu³íÒOœßis<>OÏK¬ð[ûÿ–2õ E¦cã¦Lke}ên/i5wÀ:µ˜Ëq00òQk×b6ÿÕ÷ðÃÛ—¢ôÃ˺œ7Éì´ÂÂ!»•÷`Ô~¸é‹·öþ´*÷\×ì©“~ƸzÈ)Ñôœœ¢)èÇt²ôÞ¬i)§ù¹Ïã3#Í2=‘?fÊ´Bœ:=ûVyèñX¶@A¾¢Î¼>ñ)ž\XçñFÀòÇòMç¼Þúü¯wJÖGðx ç±däÞ…zlrË„øÚ3¹,—ŽAZ®¯Gä7¼+“•l¾ðàIO9—d½6R÷Ç^z)‹¯1ΦB®O;6žŒ80Ï¢í{`ýIUò}ƒ÷b(É8\ÏeùAæVœ[<ä— +ÜÙnçwãØYÇt¶0å ] ˜ãœ/û»é¸[A?­^ú+®]òÓÐ<¬{¯‘þËi$¾£Rð.½íž¨àûctÊÔáxEÿƒð¼wcœlq‘ ü?Öq.„ ¿ŸHŠ+Þ?]çþÿ×;é'¼Ë^-™®§ ÷¿ê†Ç®x¼Ï^ :#Œöñz «žâÃ0‚áN(’€€ï¸ñê.J(ñ=GÏ(…Öýz€b£"ˆ-O"&: ––¼®ƒÆqc`"(¯´3J/÷òK#AA’cË+ ðÀdÔ-¹`Ô¨§XZbW=ÇÀÅ‘ï¸K£§Á\ä{B©2;íö÷ *$áN¦Ã¬,ï7Ñ7\` 3Ð%.8fνt:ónìmr=~ [`‰oøÔÊb^Á3}€Ÿ ùp±›ðγϚ13û,B_šLqyoIÒIp½+•ŽC"­øÎ3¦·åÛ š£½w‚w¥=BhÔ;¸•*åqßÀePt&°5 ãâôþ°<ÉpíûdåîƒÓVï>8ÈdZ°)Ý~ô´ÿ(.Ö-V$è_©SŽCÙ<å“gõ]‹É%º ËÕ2Ï|ê¹C¶1ЯÑk;Ì®æâ]ßÙ××a·³²‚ä4]=Gþϯc%[eÄáž6 ô׫öºJì3PèÓà*6„Ý!}‡å×Âng6›|—šÃ¨×褨¹6[Ö`‹-÷\Ê>%2Μ“úÂaÎãóøñÒ¯xg>¸ãwôåâwôQ?S쪦òÀG'91~èÒÿxñ7€%ÏG¾óPª”r‘®%¾zs„2¹(;¥è⬈(Çã½XX|Í ¯øh§^{;¢Ì2¨·ÃñlžätÞ Ëô2„Ì^‚{ÿÃÄÂøtûô!x»ÎÍ6¥ìcz³±l3'á=YéI ïôr(š¯­Ùw´5îs=óþýïƒxÿ&à¥}…µ'@Ï%]Ø"Éfþº:^w¼qFãhÅâá^׌߷}Û¬\ˆÁwLÓíw6kÕï‡ôS¬WE1Æxœ)øˆ÷YÂ:^þ)©3 œ+ûC1¾SÛ‚÷ºÎð/¨i Ü1¨·²õÉs™ +?nø¢°ÿR›¦µoµš–UUê×T%síËëx"T<ỺûÒ7õ7z£%íŒ.Þžñ=¸[w†ûÍ1uV›”íOìöæ,t°«Ùa(;[•ò’d„‚d†›Ô¿Ô@Ò»­æ§Æø«’ÿ è¬zîí€a Ó×â§æY¬ùY+é^Óêèÿyæ35epL&Ö"ÝË3Ôxv—VÒ|ä™§ì¹Ö¡ƒk_I‚òæþå²]q)ƒ{Þw*ïÅLJ ’î’R¥ÏnîÝi ð‘År)o\—îÁïXVЩÂíè÷gG®‡ü‹†õì ÈçÐëê„r 1Ð@YOÀŸa2 E^/I¢.PÙ68lŽ·½< é-D[,å†ÔÊdjžŸ·ïn_i²œçü#e!ÆÂP(Ýkà©Ã–Fí>ÊêPŠÀó¦êG¶ÙôlAÁڢߑFw#ú1A–­{1yð"Ó©Âø¹¢_£"°öKŠ?%uèÓÓ¦%¡-·ÊZïccŠ­‹…Å Š«v§*ðà.Ã'hK©÷¢ÔC¾ˆëLó|/°&ï|áþþм¥o,E»†²Åï,®Ò77õê¼ÇùyÇtXR`q•¥¥\$öRTÄ~˜¥Þ ezϲÜïF¹ß²Ä.ÁŸàýza\jjP§o{¦ðØØÉ¼BùÕçQÖP>$I&Œ£Ø{¬8am d"’§TÁÀƒ­3|]gXe¾Õ6Ô¾£BµD¼ï½Ã®¡Æñ®y;þøfÕvº˜ÅsL"ù+÷©¿jªÜ¦·[„gÙÎÞÊú1ÈX±Y[¶æÛ%ëd`—¯UžÏÙº#Ÿ¾åXš£Þoª¸8ݹQ>ïÜ4 îuÁåìCõ~öá·¡±NCÕ€곪ÏÚõÃÏ̃^Ûy¦iÒ!.;zÊ´ø§GM¬,Â|É›íù€“ƒlhËnpáÉÙ&PÀU7 µ:åð†0Â¥ ±Â…yV ww¡ücJfô~Ýß”ƒû+Ý`S’·B,þfšÑÌb³vwj]k7JUêíB§y2B/g™ å°H=þMoÙ‚}ý+uysÏï‚MדdÌV+Å Òó~Eç®±0y ç Æ8„L7®HÅ{9= ëÒ^¾cšóþ,ÓØJÇÏ„Šo°ecù ,ÇøS¡™vCÞ:×4é€×ü:„ ŒVO˜^í¤¾j>_ưš·*GŒßý÷CÔ€%^ËJƒé=ŸE4Š\—¹ úœ=õQŒymsÝKF’œ°Z-ró6˜l‡ÉZ¸."±û,WGð=u3.7«tDäfÈÃbkšp\½WÞã`&¾Oý$G‘<ß_CÕ@å•©Ò}¶Ër_.˜ù\T¥òÞ2k¤_È©Ðb¢×"Ë|oÙò=lð±.%æVå½.ñ-x¨M0ôôÀ-×Ò¿ØHùˆ†iAdÌù)þè7PtdxmbµÎðRgÁRžóü5²bÙ•´¡Äßàö¬Ô‰û¡¤¼%eÖm<©ÐüähÔ(_særÇé ïaæ7Ϙí¶`°µ â?N‡ü4fœ¯6D”(þÍu)«ÓmÓ¶c±þ[ˆj55Y—²þœýö’d»jöÔÉ ¼Õïí^”>â@¾½ :áù¡ÑS¦_#Éö§ÊþTê#¤<âe¥HI‹W€ mŸ·§¡Ñê}e's˜ì´\cpÈûfNK9‰õA[(Ï޶ ADÀµîê4¯[šGðÌùl:eÚKQºð9TÔÄj·™`ã:LÍ}V¾Þ›<9cô”W„{Ó2¬cÙòÁÔIÜu„è¤ç»{º@I êZ¥²¬¢ÀSö¾çõØ)Ó:ܹ¹™î†“éa["9ì7Ü0Ç "ç`CÏ:ùœ÷2þü?MϰµëÓZ+yš’•Ž%³÷lµ“}9,½ÝeI3×{.WV¤;~>`³9ßcšö25mœ¦=s¹—Qv”G <”W§žôï`½àÿá½ &¦9R“ØtËÅü>Z|g©ãÀg¾¦p¤¾¹Å®¡ æ78‰^íïL&ÄÇCÒæ[Xaß‚ÀÚIú•îz%é}Ðyk÷(ëµÎ^ºãA÷ýª€ž¸hýè¬\Ûv:}ï3=ë¦ð‰f96Ξ¨‘ŠN€41ɲMý^ƒÝäêɉâÅèÞDG_ßçš¶ÂÎ:ÇIóvÑBl ëOÒk%ÐRGëN(¯º?$D!!?%PtÿÍýéÓ%?cÏ5åÑü[è‘Ûo ƒ¾Îªþ~J#ðbuÖ Ï6É ððZ/¼%ëY¬°69 ì— d:eûRøÎ¤‘>ñÚ²ë‹ :íÇPšG€Ø"5$8V”¡é.ÌæaÆ}ùY{*ÖY:dy¼×ŠË¹ùŽéÙËi^FØ9Pв°Óý÷àïEŠSžEŠëæ=PúéÃ5ÿõ|æÏ9Âo¿Uî_0 ¼c³_ÎÄŠ‹#³m#¦ÇJ ü„f mßšt†£nñ}S^…>Ô(.x|ÏdeÅZ¯%˜ íì #'Ûü ¹›™¿×ËÞ o(a-éwxÐèKÄ,K –¤ç–и èàe½|cEêý/G²{þúð€:&í ˆQXH K )!–îÚÞÜ®wøÂå<ún­{uGnYõ³îß·`õóyE?ÍÐLÀ¬¢¢è]ñ0ˆ7"úߨçé+"É{Ù‹Š¢þ9iÒÅŠòUöŒ]øè\VÓ˜Hm&/¯,¿·ç¬8<5}zãŠxûÒlx³ÇÏK tлÆÉ‡Å ªÄyMUºþpÂÜÉ“yƒß‰×ZI1²…gàý&R;¿±û1Ì‚ê*±/WÓ½Ot?_Q=PÖ›ÛØiOÌâHŒåå Æ8,K›­'‘ƒµ¢~«hü”¥çï5Üÿ""© Œ'¼ÑÜvC‘Ù˜ú_𶦎ƒ¾Dk4ö@#42ø·Mº¨Z£¼µ¯&î-™!-Å;p+× ú¿Ž˜àØ¢½ýޤ |×5Qhj ¯^tæHu¶ žk¹>Lš ác ÆFeƒê˜Ç&¤,ìÖ¡u¿g¾ÍorÏl.¢Ü¼|ʼœEç/ÒÊ]'é¤-–~‹òê(á®Kµ,ýaA.í»è FýüX½´ª€¾?`¥vñZ<õh¢¥Œ|'ÍÜZDKޏ¬N¦!´eZÄjhT7#½³ÙL_FñáÊ@<Ô 'môʺr¿rúäo¤…4ê¦Þ”Ð(žb¢£(<<Œ´Z‘ÿéíÏ–RÚc»?}gúC ’‰O.>üûáÀÇ—äî#df/䇞~á½ÞÝ:v ¤¯|©¸lžSX´¿÷ØYÊÌɧì¼BeãaΣZZËæ÷õšA\·â9-!6’º·oA­š†Ô!ÂWÖJå;yîí=z–.çPv~¡â^V*C=¾P•u¥Ÿ¢¹Ÿ¢¨{»æ>õÓ΃§è§Ÿw¹¥3èªN4èªÎî몜á}º¢º ~Ÿ^A;X7ê¬-ΩÁâ ,Qx§3dÈÔ²ÖûÃâ»Å `ÀÊ{ñl{)KP…{yÈkQp»\ÐÆ{!÷Ølö&/źõ/ÓÄôò°ÕÏJLÛ#ÌryUÕªûP”*ôÕà0TúÃ_lí<[™€‚1ËÖái )ûL½®hü¨y=ƒÄrµ¶PðPÜöœòx÷ŒªW^_î³›©Ý—üÕ•{[-Ç#,à𺥀ÁÔC¡Ð„½/Ϋ,U—ìj²ž‰.R`“ kjiÎQ”‹ý^^]Ha5z û ÌuÒÞ vJ—è©þát±ÐI³¶™éWÏß~ÎN=áÚÇ`+«(Pu¾&%Q³usø÷ÕÛöÓª­i m•€ûÃŒ˜9ØUar‚•°49í±0¾‡ÓÂu;)&ÊHÃú÷ ›úu%} ¹m±eцÍV¹ý«·ï§Ü|3ÚÏ–7¸‡s„ !‚%…ÚOFÃÆÓØObáÚè§pÞ¿;ÝtMWÒé¼O,\Õ¹dWèÞwiÃÎCÔ,1žÚ·Pæ›j£k‡u,á%¹Àƒ&¤ á†%ÉzM7¤p‡ûÛP€Ëˆ÷6¶ý÷SЧ+K5pÐ0«Àw=ØÐ&ü\Øc{vÈ m-‚º€ŸÐêV`³4¥0~懱õ»xRÇO‚\LÂ÷—€]…>¯ï  ÒQôI€ž˜0‰zèì½à µ'ìtuSµˆÑÐÿ}ŸG[ÎÚ‹Òðvú}ƒ–X0‘¢[>Ï#WœÎâÔÑ.Óöt»r-þU]{±_Îg‹7ŠT+ÛJLØ…ãAXh‹ªNÌÇNøÀ›Í 7}·ÚBk¶î¥‡n¨X›Tw.IùAGqc+Ò?m†ÉLaF%ÄÁÚf@tà†’Ê ÁÂÈ\d¥³•¾]½€r=|û¥ŸÊæåkÞƒéRv><éš³f@ü—»c¦À\e½ÕUïa¾¨n&Y£ÙjÎñ:†¼ŽP·A¥Ž6 q#¥§›ênx^½/Ž¡•€Á` ùŠƒk‡JB# mW ê~Jàög­i˜v¹ˆ"òçò· l ,É’’ÜEÄm/0‚¢´*„™§_ÎØèÁoòèÞ¯rɆYì¤H—ªp ³dîpÿ%5‹*Q!~…%IJ*-qô_+·ì¥Í_Ifë)Jnò%%~J‘‘»B ”˜[b\×Çõrý3¬"æ‡L¨×ÁÖ´¿ì¡¾^ƒúm””CMESd¸Q¥â`ÀÈò`¹°| œþ5ßÕOåõуû(î{ü¼Èb¥oVo£êð|)Ÿºt¿ÎZ–F<;y×Oo¼ràZ $ ¡ceMàÞÀ¡à¬ê4gÿc2LpM¨:Q ì~ªã™No¤Å8jïOyŸÊHŒS‘„j±\!Äf±ï»â¶ÊX ¶b$YbË’HA’À{[ÌÊš%Or§S[¬Iú­¨ä|Ý©’HwEbºÆSd~Ÿ³Ue¬9_¯ÜN‘{)!á[„9ûMØ‚aa'))iefÞƒ½zðîÊNÅå‹÷ò ¶•Iµ&1Pâö/Z¿‹"ÂŒÔÖ$ð"•/ÞÁ.ÃrôõÊmȈ=&àšW¶8 ޽ï¡Om +ÜÏ_Ê¡å›ÓhÄÀÞåO ”L Õ1°{Â_J¶5’n~(é Ú Cøžÿ_H[*Kbœ†TÀ‚x Ð:±ßRq‚ûô-깿ǫ¿;u  ®}‰»îiÞÂ_Z¢\åØŸé ‹p«ûK_#µ`z¸7f´aUª(,øqDÓk£¥d䋃[ŸH•K€»Þñ&¢ ”ϯ1 ¤rË@ù`~¾_³ƒÒŸV,LÌk°’ ”x}ÒîÃ§è‡ » Ô8^%_eÌ€’åÅó›U¿*ãÈ[q`ˆ»—Ì/qð‡]èS‘*–@KÜ,„º}‡ÐØ…%ZzëóÖ‹O<ðAaúYØ$Ô¿ÍJ*!ºÛ=ß}c%ÙÄc!š•€Á¸Re¿é¶Ïi¡^ûsÄŒ)¯|úM-ë°(AÔKq ²,X{4æÇ|j«¥¥ÅÒ³×EЬífZu¼|‹Çj¬u:œi§5ÆÒë7G™£úIÎ —³Ï±FÉ ¿ X”jS+ÙÂ¥__.ÛŒ5MÖ ºä±ë¥ü3}µb;騀-J"U],7–ßç‹7¹£%–¥Ò©u2 ìÝÑ}{é¦4ÊÈTæžÜ÷ÄIi Ôi°4òyë>øm~\ºIA¸’È¡‘ôƒ@I ƒOvI‡r÷v(D!ie±þ,‚4ƒ*Ûž)<‡uK{˜(&¤—r. ´Lî 5NÜ'Pz ½^³…‰åÇA1x<•×Gð¡u²+<áãÞ›çß]xãšFL°îö¯´«Ô“¦÷cL&lÇZIò5_%dÜ«B÷‡Z°`÷X“Åy_7qqâ·’"_( 1U¶r°÷<Ûk]Ùûµñš÷Ǫ| žªOذ{¹Z›,;yÝR`Iãñ>9¥¥}•@޾fF¾¬q‚ϼHH€Ý¥°Ç@È^%ꯪ‰ùâ¨|ëw€ÕªðìÍÕËWÞÕv3-ÞlóžãJÔ;^ƒ#’ÿ`ùqô@·Î㪼>º{H_%º ×ÄûVý¸q—ÿ•Öó’u6ÀƒÚ/7ÍÊYò¦á„¦Ý€ï›€ƒ=@ýâ¶ ŽiôàyéqÓô6›ã+ICKæNM™¬f}òÕW-…ޝ,öœ°tJ;eZꜩ“?WŸ«G_óqþòêRiñÑz£S¦¾†ö×’vƒCv¼ŸYhX‘y(lìäÔMs¦¥<áIÏÇM™öÛ…OL¦5zo›i–Í/®Ë—ÀÚÇÚu}sß]NÙ¾ zC³òsúöp~jzuçú}ôäԗᵇ 4ŸõÌŽñ°Q£‘fhœúCvÙz@«ÕÜVDÉ6.eêh§$uÀøž8>eFg‡lû~î´”®ž4ÔsŒ•Q§ü¡^/ žešìvûò´a²ÓùA¡½à"ê3â­º| àÆ,6É-ÊÈ}§ÃçMK)ñ P‰Šc½’€$iV`ƒe :¥À×-a&j¯‡v÷z%,ј%žý?qö"åX•ð൹ñQ»èbf:~;¶nŠèy’ß»¬Jvl.k¥c§3¨Ý8<¸HK 2Ü@™øÀãª]Ë&^û(*"ŒîtÍ_±E©ðÐÉó”vä õì –€–íJ-e ÔÆkvÇÓJú¡ˆ „?¸ô}8¢Í ¨plô”Wo´Ûíߢ¾+ÖŠX œ/#øæb(„×Q”þZ zÓ8ÓWìüåk¾ŠêòlkeôÀØ•Fé´†ÿI²¦PÒiÆCA½~nêä¾N‰ºA1âIïIÓŒP~@8•“ž÷ŹÿØÿ|·“ Ð9ä?ô¢D_·3„ÿÞ×pá:I?ú>×pÕÌý‹³®M5”‰°zÃ×?M¦OªÊÛ˜)©Ó%éAð•æY–­–N§ü‘DÆÛ禦 Ôj4˜Tfs¶€Z.ä¬Ã +K=…VÏÇ6Y1â a'S•ïF&Ò\Mœ¼ã§ø»Zî¸}ÌÕb:Åï—LJCAie{ ¢®–²:ð¨×º¢¬©m¨–Šk¸žõçÙÿ½ÇΠßde¥f©ÂêyŸ'æsÿñtDT³Uh¹¨ˆÚn¦QTdÁþ?çA—Ü–ŽŠÊŠg•K€÷£byò¸ªÈºÔ@êÚ%Áz—#d{>\!E*-z–¸I·N°î1DFõÁ—l•#äa@]Öh5ÿ7âçhiÔWŽÒ"ºòJ«Õï×è†cûè+&ö±—ÑïÂÃéS.5÷ÅsðSþ“ì°þ®,_óUT—'ÍÊèK™6¼™ezáü¬Ô‰ûç˜&íãò Ñþ­˜¡-53k±[gj4º‰ÉFÏzÄy`H{¦×1Š7ôƒÜ?V~qª@*PýSŸïußOOut)>”ÿ uâA|gfŽM™6PÍn±Ûï½…&Ó(ÕIcY³ò¬ã¬ÛJªæ­ì¨'Ýs¦¦Üƒ|¥4²UiGúC—=5'õ…ÃLC±ZÉÔýÓÛþiz*—$ýhÞøaeôÅóú!~cÓ ¡º¦0Ñ$‹µè†@ZÖû³Œ|ƒd@Lzݹ³¡WÊ2È(›Œ FgíW~˜Ç0Ci¯poí)Û¾º~í Å  Õ˜C¾R òâ}˜´šBÊÌÉGPKŽrݼ*ªKm7Ó`¼¬¼BÒÂU¿qÏ|”ËQ«Ñ*㪲>Ú¯ 5MtyÊ[dd±pÇ»BÊõ,qˆÿ-/sä ÎQ­vÖ1-ÄX±]ÑbÏÅí+]tÇÏ;þãù¨¢óÙ¦ øÃñ˜<óáåç×¼Ñ{“'g¸ïKÒ)xŒ¶t_ãÄ×|\¦¼ºªJÊÄŸÀÛ=Ëñ9ó‚·êȰZ}6zÊ´?£%»çL}q—þõR3‰£ß8ø—.yŸïù­VË i>Æ`ÅšŒD0°^×ë©ãç{þÓŸŠeÒÌGŸŽ*)ë¼½_Ò\’#"t“0aøèÓ«]JòU~6sê‹{ÑèÁ¥ß§ä„»¡tªI>SDæ|oîÔÓŒzk©w¨T^qQï$€qâ¶ÆcÄ–áàsÌr•šð©wÂó³Aáz-éaÐÓÉÿúI>(Ř7xŒ„齡%vEce–׌h´u#"ó™ƒÈu½yç6T5©ífE åÁšI몒©ÕùoèÓ‰ãcjŒGv‘äqUYñ¾Yw#œ¸NëR÷Žž¹@¿*ýÓ]c¨%×Ëo¦ÏÛùyã†Y±ñùƒVzaê‘׉pÌ…Eè¨ìÔþ2r‚õWVò·‚‘žxeV$ôÅR3þ°øHNjåIß×|že*:¯ŒžÉ´Àn;t»Q3¡,X& ûF¶6ñ³Ç'§¶´9åÑaI1CÊæ×Á•Àþgºq4¯:t ZsÙ6P£7G }#>ù«ÇínkÏæ=·~5JªÔâYw:f¾ÃæØˆ #?O¯µrÚím›énpd€í»¦g²±Æn’lsέ›*¢çË3SŽØÆìIÂÞhäbK% À’£ØsN X¶4ß,Cì—)ÀRñ`Âï{ (ŸF‘z:–i¦[e”yŠâ\µçÀ¼±y09>ÒÍwíá.´œÀU™ì«V•RêCh+€º;aXÁ/æÛPÕ¤¶›iX…͆Íh«Óªôø¨aåŠ_•àwøôb´Ê€ÜÉ©móÆtù+Jüþ îÛ™²ó èbVptÌŠêóöŒåÉãÊ—>j„ý—†öëJ+¶(AKiå–½Ô®y"ÅD†{#ÝàîÕK°¤öâ Çs²p¾°ø£Þ.>b&NfÁL³LOäc1½Á“&"@EË’“ùp'_ó¹ TrR½³ö#·¡¥›×'Zã^Ný=Ö%Ý%µLªÞ¶ÍÁùsï?ù¤iLÊ4Joz(¸‚R+GEliÂÉââÏR9pÅªßøÀôÒ±1)SÏœ·§bò@s \î¾ñ´ƒƒ‘Œ™œúgŒã‡a)ªút¡'ké2(D{Þ‚F-kåRïCéçâª>K@³ÉšÍãJå¸Ï²7’"oÀîtþ%̈îu8]óøž` bTA \º4®‡;ŽDI¶3µ,1o¼7LBL8¸.zþŠºSŠ]ÑøS™›Tml‘Ì ÏnwóÎíà±çKòl7Óàhxþ._êª(Ï.XOv´Ô‘øy ¨¼²lë5c¼ ·½È²ªt-ëNÀdzZ†Céã§´µdÔÀýU¤†(Ž^ }ʵßv~p:3¯ DN’ز¤$¨Ø ,¹^4D·„[bŸâ˜©¡xXobí—TqÕš#óļµiææ—unµ-µ†Ñ1¢º£±"[—’ª„WæâU^›Ôvsy;¬J@åe Ù}޾wîb6mÙs”N ôìàRÝ’ÇÑ÷¤çA¾{0õëÚVá¡E“F4b@OjIOÀ2uÛ€^ÊýÆqÑô§‘”ü£#ulU»&Ö«ÇîDO?x Ýr}l8\½6 µŸ*³˜©B¾cp7ÇÓ/ÑŽ'ÕG ú(ÀR»ßñ_Û%éÏLvŒéÍÆpò©ÑP®'§ÞÇ{ñyEùÆ›R¯ãPÍœ¯¢ä™¯Ê°>f/{g@ë–’Â4û¡X+PLt<üd‡†­IX©|t°Öèt:j0,˜ªèX˜FÙe‰« c‹yaž8 ^»&1àÕÅ7óÏí¨ï‰Y×-­°ˆ|Á9¬J¼‡³îj‡¯ôÔü\–i8˜ª*•pË®u¼¶È +§œ8p">[ò3,-çiøµÝ”}‹xÖéŒËÊÆ¹?¬ÿ¶ï;®ŒÓûoéOF=-Ý”¦¬õ¹„Ýjêß½"Ò¥õ;Pßέ©“Ró„ô¨ôï}ÄànXÿ’y§UÛö*ëžBÊc ^ÿ¿ª¹ ºØ×0,¯Ãž2ÛÉV´N¸&Ž@Ç __d)Sååcvvù%YGWƺg¾òèeçÚ8JÙb¨g0w;ÞÀ½¶d·mÅè~+«P<¯Ó˜5-…-œ—à+é*H‘ñ©¯®_Ž òUúHÒ>JNûŒ±­p³x…CÖWZFd¨×€k²,9 òÐò«3fhYÇɤÍ9•× #â©Ö ’ >z|tÔ"ÒI‘Î\êZÈK%kGb^˜§N Æ|¾™µ-µƒÓÐqá¡«"4”ñÁ€I忪•¨å˜[=øººSG„;oø5°úܬö-iGò ŠhÛÞ㔕[ˆµL jÓ,û`™ d·9KÔ¥ì×=Ï\ÞϽåóFÏ{iqWH ú$à (U_í¢¦Ú$[Ÿ3Ÿ^ù?S¯ü_(Éz&¤û0ñ>J\×ÅuÆÈ¹Ô2ÂN-⌠_.þ"þ˜oÕ ¯*ã .æõgÆ_mçÂõOÐWk£‹ÙèÖk_Å¢|ÿâñh5ºgÈ<ŠŽHVIûu¬J[ª’×/f(ÄVͨ°0ÊǾO¡ñúJQ» ×»¸è„ÔÆ$Ö)½…Íy4 w~ˆ¢¶Í))!¦T™ÚrQU¹³u->Æ5®ròÍå_µµ¥QÕć°,U“ E5BBBBn 0Xú#_oNûžûIO<ÁBÕ·­bñz—Ý–4PžaY 3(E*ûØXmØQÇ\Q±Ìn1S– óìG²a‹7‹& [¡G5ÐÊv2:¡tbÃY,MÁ^¸™ë¬l€¤0…·èè(ŠŒŠTΙ_æ›#øq;Bb¶ªJ,ËårþqÊÈÜC²öS×ûî¤Î­FPÚѯá‚F7]¢\s§î?ù­ùu9`QìØòfêÕþúíðç4üšWèXú:jß2¢‡‰-ÐlôÁ·|»Ê»?ü{–­åJå¶‘]ñ6§¡ëz¶W\ñvì?©DÌS :•A½;µ¢¿Ý3ñæ­ó—o¡EvЈë{Ѹ? UÞ¯ïÖì  —á\TK’*몲Ã/F âßK7+E7í>Œ=Æ{YVVÁùF,KU\     ”#Y§ÛˆéXå)6cȲ„Ç<â»5xË •]ñ8À¯WbË’ ²ö ñìrÕ+"6mä($3<…ÌN-Ùe+ÙØhra™XÉÐ!žL”^¦p½¤¸Ù±…‹A\ll4ÅÄàÀ |2¿ÌwCˆ„¨|Õòì–ÇIu¡ÜçêÖönÚ´û}åþÀ^OcSÒ"Z»c:‚DÀò1–î´÷ø÷Ø+h]ÊîJ-›\KkwN§œü†³«Ã¬«ùxûÇ®g›ìAFÙÛf ýû‡Jt< ÞNOœW>áaôƒCy×øþ«ÿÀwújåV÷y]9iÓ¬1un“¬´-h  b`©!öºh³€€@ J`ä3–=KÞÐdÃÚ‡Oò’×íG¾`9êK ﲫ$¬md“ 1HMN¿hÕ“BnëÀ"*Ê›:{ïRpÓÓé©ÈR e'B&Gc“Ñ`G%c>¸>W:¸ê d²k`8±E‰R\l¬rΊ£á5$«R ÃmP¯ç ËXjÖ¸ÏL£#gV+äz¶¿—öXD[÷ÏS®Åv€5i”–ø[žÖþ:y*ÏÑëÊñ\ænÊÌ9¢œ‹ÒÜc Æ‹@ŠŠ×*y>2#€C}L7]ÓŸº |?8¹0<Ü^ÛYQ›XªH:♀€€@Ð%%Z^òº´ žY#™¸¤±³uÉ/°Ôû³Œ‚mw$e`8 3Á†=;ç6­ÓL·!'eÉ‘» r†Áˆ×±«GÈ3c?‹Å¢&‡Ó墇ÿJ^žU÷71HâÄí˜-"‰1Pâ:]V%¬¡‚ë[”7<·U©aì¯ä¯\=ËI’†Nžÿ™Vn3,íVñú£pc]„kžš.dí¡>DÔ[t4ݬÜ7ĉ@ˆŽ¤þÝÚbÍ’ë+:1!®YÙëå#–êe·ŠF   Ôn (›Óʲ–`þ`°ô9†j~ª}—·9•  ,±,¨0Há¤Zy8€‚R냊–ŠŠÁ’Ö%eƒÐ íy£ÖÇ–"v­sY•\Ñù\A'")""\ù°û?g~EòMëw½¡¬YòÌÍë’ò Î#2[{÷íøèvŠ‹?S“Ý^jgõ¶8 ”+ö°ûÈiâðçøþˆüëÓÏÜòá{ï|Ynzö@€¥zÖ¡¢9BBBuAZY³Þý «p ÏÇPþ:¦ƒ¯[ZÇç =©€…ˆr‹Ÿ3XŠW€’îDV+Ö*qð¸yºâùc\*6*¹Á™–,¢Š±‹×ËáÁÙºÄÁ$¹6¢uY”˜G‘“ÀÑô5îpÜò–+knøüð™åÍÊ=Ž|Nj•t-]"‹5W¹.·€xÐ %`DHÿAWu¦e›]ÛØµlßîÙÎ;{ð oÓXÿ“Kõ¿E …„„jbtÉÛ³mgí°ñïPç53£†>qÑß^,) ±ÅDU82QCq+.qÅaŸðz%+©»¬J¼0݉%d®èlºáq½üÇ–%®Ÿ­K¼‡ƒ5HzÅÚäŠ|',JàéŠm/Ó]ß§Q7} ¹èxúzZýëÔr©š-Ù´ãà§Jt¼[ú§Ò» z+€©ÜµäÁ >°î*2³«ö•ѼI<õëÖ†®ÝY#-á` XÅGûÁ»gzäŽôíê_•åž÷kÓyß.­iù/iBWwmCýº¶¥cg/*þÃj…ƒ®êDí›'Qn™Vn݃c=v× :•q™:·N¦ŠÂž«4BuÔ€Ú,|wѤ#ïx`Éï}òBÅg°éñÕ 6k‚ž€€€@}–t†íjû ó\­žWõ¨×hÜnx(+,K•ÁŠ 0¹\ãØ%OYO…S]OÄG?¼I¥£Òf<¶* k’«s8L÷š©4ûûÁ´|ëÌÖTÒkâ±§ÂŒpá×ÂRÚ¾E¸°m§¯Vl£½;*ž:™6î8tPbb">}?ý¸q »¦»BV Ô³C š¿lK) Ä¹.~þÉmPД¤ÔË›ÓnÞ}DqéëÝ¡%6þuÐìoÖ(|} ›ÖvoÏÁ9‰zvl‰}­2:Z…âu2«pë“Ý&˜¨û04n¾Ëqá¤+ȃLü ò aÚm·79‰tR¤r™Úâ¸G9ÿ„jXlEÚuä Ú}ôkD Ìvs£êô1‘ÍI¯iF—rëÆüµÃCa­»|R ”zXæÂ[^#ÀW®Ã^&§o—6&)›ìpÁck§]‡O+>o·¼F10¢U\è šÌ°Øpb·=N4b悕ʹú/)!–CÝx3k^N‚!!!! K`èc'Š0—¼·˜¬ÆjÎêëoP<«e¡ò´SÏÅQH &$p¥éÃR@I#é`9¸îú=2b!µmÚn\°„Õ»>×Éü1Ÿ‘†àε‡Ø8ÂÜ_ëRÙ@´¹ë°éÎÁ7¦tºp9î¾±/Ýóµ´aç¡Ò˹jCýÝØÐYK‡áNW6mØyÚÁº4þÞ›¨qlí>|JÉ’ Ö®x»qôLD¡Ü¶ï¸Bs0Br³E‹Ó™ —•õQ¹{0=vç e]“g9_ÏYŽ,ÏXÎB‘|øá¬k<Ï´ñÞv󾃿 E=µ…fpG{mi•àCH@H@H@H NH³¿âG·73‹@슷Þ/ÆKy`É/!ŠBJ <+’J7.ªõîø õj?Ü¿‘çåÀý*ž6ï;…ëÎY: Z¶6™?Y–(9>2`÷.¶t¨ŸF‘z:–iVÜãØ•®²ôÙâËˉ7*Ù9PÉ«ÿ œ>•AGÏ pB™t.r¼ˆ×­2Y¿£âý‚v8A\VÍÏçj½L+¿°ˆ>^¸^Y“§F”äûûާ+>çäYŸ3XSiºr :!H¨î«¥’“Ú.5OeGv3d¯FWÜÎ`§ììl{a~îG‘ѱ“™66x‡¯ƒ]Om¡§©-Œ>„„„„ž$™<Ö-ùä«ÜA #°Ôð†RµØ7+ÒºØç4î÷éúO`mMc…_,4Á:™pƒJ·2oPcm©¬bæJ 1áÊ>Z*ÿ••+û\-Ç{q10ˆ GxÍU¼,Ê®´x—²ùË‚”²ÏË^û’¿¢úÊÒãëòh2(y+WÙ=–c:ŠÇ•*ëÊÊTõùºeË>G÷)]¡ƒ§Íš§D6­*º_X–êB/ …„„ê©$½v;B·Næðá~%(ØkÉXr‹Bœ„JU±"©àÈ“U‰å‰Þ¼·K‹Æ´ó˜“ŠŠZSXØIϬµâœù2›;PÇ&‘¤Q‚~p‡’ñ¾2Y¶Ý*­&Z:›gEÐ ›;°‚¯4C™oÑºî ¡¬'X´Y~E+]Õ®‰2®üé#_xÙ¼vmî­wßó­^÷ç—v¶.ö¥l]Ë#,Ku­Ç¿BBBõHÆøæ»¡o©h©ÓÆ£ýif§Ý–%”oë QFH 2 ÄÇkõqñ»clôFqD;ok‘¼[‘¼Ñf«Šm°[ëD ÓKt9ëN¬Ý©]ј™æË •©]“%ôµÊ7·¡ªÉ³ÝF›ÃÊ7‹†u ¤.ç ýU¥ºü9ØI݈5tµ‡2Ëí2¢ý…ÁLÇã)>ò…£Ìógç•ä“ÿôö‡6*¹®?gUáõ§í¢%BBBB5,Ï ð±— ´ÙW®ÈöǨÈp7XÂ’ –|™Èâ»þ8V×aâkšçŸ}ÕùeӦ˚Ët¸Ta^‹tãUé‰?l¥ßžCm› .õÜÛ…jaa…–Áo¬zU›Ædµ5¡ÌÌ{¼©±{ÌóÕ±q8¬^z2 ÏÌ»Ú_™Só«ífZ¼—ncÍ#e˜\F´8‘ª.ˈäg³Û1Ž)ã‰Ç•?}äkÍÿœ1ã(\)×r~à´pD|Ìײu)ŸpëK½%x¨‡€òäò`—슷¾ªÍìøß#¹Ûoo’ƒìX,ß~g³Æý~(Þ¥ªÄD~!Hà“OÚ„¥[Nß+“s¬$9n(+Žh×±ÕÍԧß|GeË󵛪ê8,ð¾-cé,‡/`³ÔK÷SB·¤‘\¡­½•õ=¶(1P*(ìN-¢5” 9×&ÆÊFÃàÛPÕT¶Ý.šFŠ0SÕB‹Ð|ìŸÔ(.í¯*õ†—_±(`"°»Iò8âñH•+EÄÅÀ³’äüdiçâðŸ×§$ÀR}êMÑ!!!º)í`ûÏÌ:>ø½9-JŸÅox¬BG²7Ãѵc$ßIHÀG ̘gèê”í£Óm'ÅöFeuu»-Ê¢ÓÞdxb”Iò¶ÉÇj”lª;+µaF#……‡Q§fqd³ÙèDVwX °/PÜ5²†‰×(eÂõÎfK¤ä™ZƇS8øc™Wæ™­¸áy¶›i›ÍaÔ$†À VÊ`²gfS| ´Úå–X•>u^^£Ä®‹ì*Ø&Þ ŒŸ`ô‘7¾‹¬6' =ŸÙ#"¾Ñ½ï^¸àÉ=_›=»÷‹ãÆÕÞŽžÌûx.À’‚Ù„„„„B#Nû«£$ÈC`‰–¨s)Éšæ8ìæs‘*–GäòŒÊåy^qÉòŸ²«•šT·+õº6ÙŠtÞvúlE²;mƒÊòˆÖØ iížmÒ*]Ä#7·iÛf(€RI#ËðñšeÀC¯×+@$22‚,EêCM6ÏJ¤óþ—ª#…âááN€"B”x%ž_Ða¼;NrPË,JØ[ ›¨F?æ‘ ó¬ºxU•òÚm±X ôÛ(ÑQ@z*¢Kv#edæœAÐ9`i XìUe·Öåç}”8<8G½ã`¼Ö«S‚ŽZa=Y°úÈ[£³s d›ÕzÙó™é±Ç¬Sg~ðlMOó}›C~–<…$Î…„„„„‘y0_8i‡_Oà)An˜xÉE 2ƒ%%aßK"y‘€ Ž\¡•e:u.“öc?˜Ë¹”D`±"Þ;Ùø›ŠuY#Ö ÄB±nIÝÚ5§ÖM+DmO¥¬H2af¼tBSŽÙmÒ6®Ò}»ü;+¤"'ücÁUyf‹TPuF$hÐ`³SX–@+*2’,P~­°,5ƒ¥ R—Gg²-tÙÒ®i°>HX($­.Gk U—*ë” ä°ÇÂ*À ©‘ÎJ‰á€£0…¯èè(ŠŒŠTΙW晣øqªš*j·ív…à6S¸¥ˆ²lʃè3!®J#qP „DZ¡%'6šu8éq^B?É”htR‹8#ÅÆDS0û¨¬ly¼ó¸·šÍ¼-C¸¿%t’ü¾À°„.úã‚ &Ž5ʵITYBuðZX–ê`§ –…„„ê“8ÈÃâÒ^´©7)O›Í›Íl¬je”^FKf7<‘<$À ɵ‹ ¹Öî8@ëw ¼VBeÒi̤.‘Å£”ÿ§2É Üî §ÅwSt¤†^݆\Ýë)tnàä þ•4™šÍ2~_‘ xaIÚ9Ö³)«L&S‘•£4&àã<}øàoÍÛ¶¿?íÈYº¶G;ÿ˜ð(Ånl¼Ÿ×í°Õ†]ðx`p…o›×Ù(Ë—¬N[P5UVšI 5ˆèÀ¢ô2…#*»È±‰\ll4Å@aÀ™WæÙ<•×ÊÚÍϵÚ"ÒÁͬ‘ÖÛZ²£sìvÞPÖ;Hc¥^^Ç ,±ºžT<Š­¨È€wÕ€“(x&F‡iÑGlñ MyÊÇ;¾C¤3ÇŽlÃ},)£ï¥Çÿ5õ_…º=ò4=x1kò,÷,_—ÏXªË½'x¨/¤4ü3HâUÂìJWe°„¨Lgñc­’°,)’pýSA+â{¥¯Vn¥\€¤ðð#”˜Z÷.·k64]´ÞJëvì§n¹Žzth©X&Q¸=šXé)G´ëÜÓ9Jg8÷ ¢{µ"A¹þÐ)…b¼Ë²Ó™½}ýªý¸äwA}/\ƒP¢¯pçEÎ+ÉŽp`‰…!’€€€€@P$ Ѿbœƒƒ–ªLåÜnxÐ`X‚Uk»6Y­VZóëúiÓ¸PePr“ê Àkl"±Þ†?4àrö4÷; Ý9¨7 ëߊŸ+H+ƒÁOvŠ‹û-Îô¾ô‰Þèp­‡óÀ¨Ñ¦Z‘^ú«m%xÀÓe»˜'Ž P‘÷³Ÿ:rx6SuøÔyDÄK.ÎæÿAEF¸Ãɟ͉ÅÁk‚ØÝ͈€ fX—”5=§Ëòää0hHþ‚&UæŽyÐjxý¬\¨ÏeUÂ%¸Þ±EIqñr[•ü ì 0ëñ/XífµãÀI¸‰Ù ÇU0P Fßx°[#§5ÝGÜhç{Žœ‘ÒO_ˆK~Ô÷ß %Á#s¾f°$KÒí“`ß§ºŸêÜ®ûý Z $ $ $Р%€ÝZöa>Ý%Ù?°¤#)Ý^LC–„ž ”xVÞ ´zÛ>ZöË~ŠŒØ[c!©ÃÂNRrÒ%õ¢õ®©éa×tStVœUÅ0З!3çývø¿d¥/))¹°èñÇ€CŽŽÌ“5៨V¤É£½‚5”üaå•D^(dýyÉ¢å­Úw¸åË¥¿Ä¼øØ6 4qû¨pb9¸®]!ÅX#T°TÄŠ­KGp¬Kj]l)*±X¸"ó±Ë»xED„+v¿c™·`¥@ÛmPÚ²ûÚœ¡°Õ¹uSêÒ¦i°X¬q:5ÙGV›ƒ0΃—³á‡ï¿‡0øP“ú~ÐKãÇïL9대ޛLŸ9·?òm©qá–‚ DABH@H@H@H 0 84ºý„pÁœ0Ó¯D´«*E^{Ž,®‰NÌg>Ý_UjY~žmgÅšì݇NÑò-. ”Øx~rÊû)<`¡7%ÆEQ¯Ž­%œ-)þ&‡ÃBûO-¡]‡þK§/l½‚ [‘°6~±SÖÎ~yœm9P(zWX‘®(‡<¨T Ä º,……yù;~^û©æÆáûô‡ò_ï¹Ñ+ÒòF¬¼{ªBÌ`D9`rYy u)ÜJð_%Bñº&ÕªTU<Õ§Ö K™båc;Þ£‡Ãƒ³u‰ƒ90HrmD|+ Z¿?í¶0®Ù¶ŸÌ€²‘QQŠhy,uG@‘újCñøN¿˜M»·üüA^v½ÅøÇGL®/ÜbaË’æ;’øÒ©‘ïÀA€¥bÙˆƒ€€€€@@Ùò®£?øÚ…ψyüæKÞo3ò©Ë¹U!jkœœAgO+E 'Ö'7ªÈó2Pb…š-J9¹ù´hÃoŠëorZ[óbËH¤¯Wn£öÍ›À‚T…GÕŠ”vìŠì+Š:ìÑ–Ëó?HÛ¡ûdÕ"+,Jvóÿ_%÷ VÙ²Ä b>fþìܸng|bži¿‡¾%ù‘;oµ01pPÃq³Å…Ï9D7ƒ¾V;à5K¼®‰-K¼FÁ’ ˜ÀK•×§|Öƒ-K\[—8¸ƒHze}Œ+ò]0-JžŒúÓn–ÅâŸáÚé”(*&V!×7zuléIºÎŸ×T±E‰ÒöýÇ¥“‡ö¹uÍ ><öùàw߉R`Iã¤e¸¡€%Ù)߉çSð©óIX–ê|Š   Ô} H£¾r,™!BKzrk´–¼®8TiV²ßÜ_mÛnO ¼9"évŽjÚÇ‹ø4¨ÄŠ3+Ò¼F‰×¹lÜy˜òJŒ×(±U§¶$æ¥QÜÊ>Bk±–ê–ëº+î]ªrXŸ•Y‘4’Ž:µº…ŽœìL~úÎô¡ßeVD³‚gª •E6G!BW÷ÕRàöI»ë\f–üÀ­×i‚±N†A Ë×)à€IIÁ. ä Á ˜S `‰Ë»ês­‘bÀ¤Eý\¯r¥òÃyC™Ôz*k·¹¨ˆ–oÛA°$GÀMÓྩw§V¡d¯Fhó8àT}Äk”ØõŽ-JÇì]°ò›ÿñ¤[•xìó;Àïƒ%~7Ü)º]«9ÇOb@ÂRî:óã–)OüÙ5ƒåÎU÷NXª{}&8¨Ÿ$y°„x_ìŠW%°ÄBZ‘_o%Ò™¶PN­–\V%×:¥ì›´.xõŽ× Õ¶Ä<1o›ÒttcßŽŠ‚ÎŠ2+éÞReV¤¸¨VÔ»ã©WûQØÄ´1½ýÙR9æTUî©`Iµ,±ÒÈ,)ѶWÿÕÒÌŒóçû úà[Ÿ-íÑ¡…|M÷vRÏÍÚ‡‰•d–+Ƀ-=ÐûWµ&© I=V¥QœWUÄù¨~¸>5àƒz¯ªtÉÏuVÔn ¾xÍÊ)´‘1Œ1+Ñðk{PŸNõË¢¤ÊåÁIí >†¢X®œ£Þq0§Ý–·óçõÿÞ±q-ó¾w<îùãiY*–ž9Ò’:óƒu¸9ùàRmŽÃ'|^—“Ku¹÷ïBBBõKûÔæH~yZÊÀ\gW¦ãˆÁÒ•fC8²Ò¬X•"¼ÊÏñ³Élq(áÁkkû£"vÑÅÌt¼vlÓÌ­(«J¢¯V¤ÞÿDm› U3Ù„Ãë–x½Ï®c—bD§D:صyÃŽC¿í8|ý­#‡:öÁP6cÀ¿n”ãb#¥0=6. R* ŒÊ^WµUÎj¹²×êýš>r;Y3·Â ‘ÏÙòÅI@ùKÚ1å£Ü¨‡ÿÊöIÙë@š\d³ÉÙ92o8 ¹J¿y'X½yéâå……ùìךSüa·hûüð»à2kâÄ3v/CO)` oÀ3–<$Î…„„„„ü–€–%¬Àw%ÿ"â¡t†Z?~°“Õó†rd%’Ý´x?%vÁ; Âq ÂÃÖZ0oÌãç© Â=s¨l¶¤\Î=ªD´+o-R\TkX‘t[‘BÜ@˜ìvÄÖ%H ”T¤)³¹À+ÓbÜ_Þ©çUmZwêÒ%<*ª‘1<wîÂŬ¬¶vˆä§ì{‘ÅlÎ5ägž:tpßÁÝ;ØËÖ#FlQb°Ä ‰ÏÙO]¯¤~Yã–GÒÈ«”7·a Ç“:{*,Ku¶ëãBBBõKZY{îwJ£ð#ÛÑŸÖA{=¯þ‚ãÈ–¥•ØE‹Á/~çèi9ùf¬?)„Ûë>µ31oÌcV^ÂcбóËéÀɯ(=sÛ «k‘BlEº¢Þâ (Â9Ϭ«@‰‡› ¢ø~8>ÆCi;÷áÕ•b}ÂQ-ƒS‘ª"Öí;Æ=ôÿÙ»8)Šìýºg6'`É’sP0%)Šz&Ĭ‡"° §wž Ð;ïHæó²Ë‚¨wf1'A” €JŽ’sZÒ¦ Ýÿïõlõëî²³;ËÎ̾ú1tuwUõ«¯kfë«jĈû™(Öó.üvÎÿ¾üàýŸiGÊž„€ú©äqÍÇì‹Äã˜I‘2»c¥Sj•P†ÆŒ±fBfÖq,Ü$AØ~â+¯Ôs÷Ýam-d‰ß¬$A@@ ö¢ý–˜U6c“$õ½¼òÙš%´QÉ’IžB²Äa¦sðѼ0Ú‰eܱoMûôaÈ{Œ¢0%vøÍPN³©,°xRÉJN<6Õ9O0™‘2YbG6Óã(S=!J£"©ý™gÖ|÷Ðû™¯^a}Ïâùó^QZZ‘ö¤ÎI¨1ÌD‰Mëxló8f²Ääˆ Sná‡Ç8ßç1_jÂo¶1~ò^éèkÊw÷Ä‘D„mòû) Û>ˆà‚€ €À€‡öå|ñ´vSк°&‹™õBBCtkO ]ÃŒô bWiu©îeÙO™áqä4ކçÆFºÆV3¡XÆã¹.ÈŒâ|,qà¨}óË`jW¥¾H£&—¢d’ë—ŸûbÆŒËÛ†”+õ3©Æ°"Küc¡ˆ?&&N|ÎוŸ’ª‹K%'üñû±Èv³²T2LrUA@¨ÛPË"9¦ÇÕù€Èæ¯ÕS¡“JUùšrTfxlŠÇû,}„M×YTþ$Å5¡îgÞLÝ:ÞB‰ñJ™RÝ`PyrÉÇâæK¬Qò'JÊ·‰I’%€PÞtÙUWµèyÙåO#ò[ýÂ:ëW¯¸ÿÃ×__XÞ6¤\™ðøUÏþ„‰˜ ñ‘?|¯LîÛ Ñæc÷‚¤£rázÍR¸¾9‘[D³É­øë}ž¯kfsÒMMw‚ÓNa³Fj–˜ppD<8ðL(\RLT 8ÿiêÒ¾?%%& ÔyÈÇDàé CÌG^uçÉ¥2½c’¤ˆ’ø,Œ@ÒíCG´kßõ¬—ùC±å¼][·ý鬬iGÊ–‰€úyPãX&EšÔ¹º_fcþ7M‡c ~€¬KÐ2uö¿Žy!KáøÖDfA@"ÍԶ᫯w¦Ù"ànjEš%L´jœfI™â1†j?ž€1¬¦ ) M¨qÝs,SBîG˜$”g…Š0ñ*<“#þ(m’Ò(©#nI* žxâÌÚõ¼‡à[ìÐèÄ‘ƒÙƒ²ž}jAiuäz…P_2>ªcõQ×n|ìðá{ÆMÎäzµÐrÃç§O¯óÀСØ0<<“¥ð|o"µ ‰€©ÑV›+5´“Ý<è[Ï´jÖ(Í’ÂÊ&LáC8,Ñ­™d#¢¤ 磚XòD““"J¾31ÁS8”yü× /œ«9c¾À °6–Fác¦áøÜ?Æ..³¢Ü¬(±¯Ê U›? D»¬C¦ÿ@¿íüÖ¾h¦2ý ôYR¾z˜8eJO˜~‰ïlKµÜ!‡®]::mØ/Õ+™<½Âh´ÎÒ·r¦Ù±Âí„@Eåt¢ˆ‚€ 5grÂ6? šûåËŸ5é*ìÊ«Yñ˜`} å?~2$½7w8²Á&RtTBù+û•luÆ%Ôçœ1~WÊ—-’3’jã¦LémxÍYx÷Q‚Ji¿3ÊÑgtZš¥pº¥Y²z€_¥&áÝ•p–^dA@ˆ(.ž}Ëʼ[<§øÙSk§ø²åÿß$-[•ö‡5K’ʉÀá[hëžhöO§(g<µov…U35¥5ÝrÙ»ôÀÍkéÞkæRÇæWÛ-8Óî6ê}îhúËM+霶·Óå¸V'¥¥]¿Ð"]vaÉ~LÈʺTóš3A}¬\£½Ñäìýèðá+ýŠI6 pŽJl´Æ*ŽGñY Ç·&2 ‚€ ɘtݳV™Í£¹¼ÇÊÑ»ËQ˜¬¤iºåÿ ÎåX>Ø,“Ë}‚tÝI7öý¸ŽÑ—‹¦–Ð]sqeÏÜJ{­¤„ØTêÕõA:‘·Ÿ¬xv\BÛ÷ÿDS»Òç  ¼‚aY>`¥”ÀÄÉY½^ó\à|¡PÒvéQ޾ »wƒ]H2a‹€—œ{}õÙ „,…í›ÁA@B dɤV,˜éðò>+RÓÌ#á“F†%åú¿W—¿QlL BxŸm¡Mð9jZ¿;ÕJlFoϾ‰¶í]Dk·}Fíš ³Z¶ÊpÃl®÷ÆÇ׃\åXÏ9ž»‡Ü)íhÇþËõ\)T³?9ëì6³hÞÀ—ƒ9lshfßÑÆm®YHDnoÛÕKÜ·~¾ÕA¼ßF0³ Û`;â³¹ãTz&‚@X"çnÖ,YÉ4Lµ)¥ºTŽc‘¾ÈZå¨$E€4q D èµ/¯¥ÿμ’¼†‹’âZØìË^kc´/{ ¥$œaŸïØ¿Ä&JöEÉD,3§^;!cÊØŠtpbFÖ Äª|K!Š(mFö’ÑééB”*èi¨ótVV[ë½ð¬Áƒ»ð‹b©•A”¢&½újØnå d)€/EA@NÚ~õ¯I“%8Ûfx†¦‰fIYŽãüåÏÐ÷¿>Ñ »töq_ÌÔ$KÙg]OMjI9ù6§%Ç·‚lW’Ld#û½aÒMã3¦<HGÇgdÝbùOž­zš¶1*:ê’ÇF õìH“QÄóññ“3ÿYžÎ<™•ÕuÆŒÑ,Où`”qy©47Ú¾`ŠçKfAA•·£¥p{c"¯ ‘Ž›á©¤®YÒMÍ&KZáž-ª99Žk‘ràt~çT;©uë8„ãÒ¦sJmìðÑMT+©%Å7¢¸á«¥¦7ÆÜuסXõ‡¹ì]ã33ÿRžn Ü]¦i¼¢ä°Êk´Vwê—¡Ž¬¡mSäãC]ÞÒä“¥!#×A@ªÃ,UD³¤ƒ,aÙÛ—4‰†WÉ·èñæÓ»ßÞIW÷|†_;Ÿø|ÁÊÿ£;¾.µå;gS×6·ÐÈ~¤Í»çÑŒ9w–ZVn„'¥¥í:ãÕ~fþ*í“23[yLz»pw‚ÙÛj¢¨?)MÝøŒÌÙ¨;IóR†©™KuSÿoim3Î2²îDû£ ‰Í1Zìß9ÄÖú.à?š¯üGÉ«Y<m#Ê‹f)Þ‚È ‚€ P„€¦Ùš%˜qp4¼S‘&¢Ö(zÕîÉכѾC«J,½ÿðzù³Ëèùw:ѳoµ·LõTÁ¿FÌ»GZÇœ¼ƒô¿/ÿH/ÌèBï}û§“îÉIä Ài¸¹õ#2þÊ„¨¤žH¥Ã·îù朚ö‹#)¾“­’Ê×ôk¦Fñ¦aŒIZо~Ý–:éSAvþÁ¸<62íÏ8ìêÅD‰¯yLíEhÓ_kW¿nM×°‚áÃ×}Ië®æNr z,=í®²Ú¶ÊkŽ_ZÔÕèÜ¡5žÄ¥åݬZªØQ³ís5‡W±6ª¿–¥ê" ‚€?ºŸf‰7ÃC”ÛôNäïÓä/ŠäOF€C‰Ó“/–q–_pÏŒ2JÈ­pC`ü”)= ±˜-ÇǦNmôXZÚ.§Óѯy k&üûÃ&zxÿÐV`ÝI£%NÍìÇf|þå$2·gìÈ´ÿr„ÑéÃ?Ä7®Ëó3füŽl¼úê«ÐÖ˜-ãb?Üœ§kæÛ({•Âù¨5l5Ú´¾ˆeµ=6ýÞ£GÝ´aÃldÎ#C»ødÉ<3©ˆ,™úïä°µj+.d©Ú — ‚€ ”„€éÕmÍæåxмÚ1Õ.fhaaۯ䕣 ʌϘv"TNéù&â¿ã»§^~¹16‘Ýî€YIÆ¿&dL;›ûÀÁ@ þ]ÔmQ”Óq)¢ÞÙ›FÝ“œ?0][¦Î™äàwÌ­åäĨkê¸;×u!~#ÏÈÉÉŸíqßx½ôXÎîç¦MKUeÚ¦¦ÎUy>–Õö¸Œ)ÃA„¿<àñÎ2 í~¼c_ ÿÉkEdÉ0ÃW³$>K¼t)+‚€ På |u¶²sÇë€5C¨ŒLo¡œZr• BÀÄŠ7÷,ü„`å¥HvŸ"¢œÕ¤ØéBÀô>áÔ) „g"¸ €kàzwžk΄—^ê=úÞ{7?;uj·‡;ˆÈnc Ý˜`‹¥ißסÔ#‡fÕ¤¤S! ‘ëTEø¾NÚ ì^t|lúˆøîàçò÷ÉÂÛïz)mOÊ˜ÞÆkºН_÷¬ÎÙ½ ¿Ã•4Ã#?µ2¼¥Â4…­àaŠ·ˆ-‚€ p âÈ™ãW$Á/_®¬CóØš%¬ºÖ²ÄD£xÒñWÞðmgSüVH›f EA5áŸJêÿ}ÉŸ~âë§^c¥ÉSð)üf®†'L—祺~p|fÖ8ÌÚm¢êþm}§~ùÈ‘B”‚ñÆð5ßšëñ´â¶ÆG­„®È˜”9õÕöóÓ§×Qù@Ž^ÓÓ¦wë˜(M:•5J·R¿Ä²&ÙA@ìòJ,Oþe EDA@ÈF ÊŒ³É¦ÿ“¥h§Ó&K˜´Õ²TÒ¨ˆu:Èð¬œ+©©*½æõ&Sl´/¢t•>HIÓºÃ7é\®Èi>Â.ì_ðSúça’·¤|t®; x Á ³®[·´Y XW >·òvää»_­ÈÚÕ¯ó%™ZÞá¢ýc54V_V¤“êh¦M–°peû/T& NÄ / ^’ˆ(‚@MB çãrf>ýO«Ë ;ïÍa8k'—Ïí û,Õ8Ÿ%S¶XØLyÍx2ŒXÒõМ«°l^#žb¢lš4ÞC¹¯2§dzLOoÍMu'LÎ|gÌÈ´ûÙÜK3ͺ†×{É„¬¬<Ók¶“–ö9&ï/À!æþ¢þhŸ'·j>è¾ Š®I®$ÌaœºŽìŸ!Ï;=6*­¡:›žþ4?ï$$X9l!îµGñÚÉ”êö×à=>2í¤  §jíôƒZÒÃwß}‚ß3Î_äç–T¯Ÿ:q¸pnÿk¢Y:5^RBA@(šö;4«™½¾ðù&'ýÁ?Uß[íB}7—ßé¨Õ7v>­»ÝŸJ¾ª¼o%,÷ëXîOMŒÁüU£¼¼öUùØJµÍ²±Œë$Bjåo…7(©Z˜1µœò!ätçäZÉmLÒ:LÌ̚‘Ö4§~‹ahón§S¿‰I•?QÂ[ü¨~”~½¥ªy…ÐÔ¹ï»ýv[ƒÎOáÀþD©¢O~äž{Ž¥Š6a׃ϓ­Y25oØšá‰fÉ~¥’A@!ØÏúC[`eS¼€þÐbbwTɲÝ×=GX»t(„úV%¢ØD <ÃÿÝá Z Qð2éDnWJHX^%Ï­l£,[ f#©)ñ–Üìz¥úRÙ¶¥~Å?yÊ}&y¯Ðúó…çc]}MîþC3'fLù¿±£ÒïC˳Aœt„ Ÿ ÿ¥¡ö“4mFÏNnëӧǾ&™‰”S *Xf:ÕXØa!>Ka÷ÊD`A@"h„l¿%ƒÜû-a¾ ²äKù¦·Æ˜â±6É’Äø”à㤆 Lyym(?¿¹‚$dŽ,ËÖ¢^²%¯’û!éô#0ñ•WêÁïåíh-vÔ²­`bw•’‚ý•b’®‚ðœ‰Yƒf̘áŸ9åU¢bõfûz©· QR¨Õô£Ö@!àpš‡U>ÜŽòknoLäA  í‚b¸€Œ€É&tv}Ým&ÖÈlmŒ(9)::Ÿ(jR;–¢tƒe_Èx•Û6%˜8²,‡!SŒÓ¤vR,y™Ü±ü¢Y &ÒåoË››ÿW,4,zxä½äÐú‚0]9!3ÓÖ=tç9õœŽvjÿñúý‡ÞÀ}{ZÔ{!¬ïD¨j·¿ü–’‡kñ©Çcídm¢=áÚI!KáúæDnA@"[³¤ë“¸&ÛdÉí <¢^¸B«ë9A6˜$ÅÄDS S\l 5Kv’Û]Ÿº>dºÆ²¸ SdžɖŒ,/ËÍòs?$>Ø QÐ^Å´¶³¥¿ÇO~,-m—3Jï‹eÇÀÔÎ&E¸å^°zÝ;0s½YI¨kÚT)¸“b¿}uÔ]9ÖDžÍʪ‹~ûÜ}Ló ûY…+B–Âõ͉܂€ D66Y‚)PÀñ'À®¯{)”5K°8ĺ¦ˆ¿nÎW*)3<&±11KqøÔOŽÃž,&åäv¦nªV k”¼É’¥Eíh:£.†CF–—åfÍR°Ìðò]nÓkxy,X8+…q$Væ•¥¡Ñ½‚¼Þ«U>|;Üßú›dükBÆ´³az½ßm¼Ó»TL$_“>"Í׆º*Çp@`üä¬k&feuª Y]¦ÓŽàÒÝUñŒÓÕ¦ñ®§ÉsA@ò `ú¢ÙqQèþ[…™Ÿ­YÂ49`3¾òˆ„2692ÜÞýGŽæðy¥T*˜°Zd#**Ê"I ñT_@ÔÄD¿l@IDATãÆö•y´/¯¹÷Õ¥:µ>§ØØmAèFù›`%6d-WÓµV)2²œLêXne†WþVK/yäXŽév¹ŠûJظ—^³æÝáðÐðEž=ôŸ‰Ó¦Í3lØFQÖ6cÃÙn©ÑQ'68ô1xç6:šö4J"àƒ}‰÷eÒ†ëÑ#B3¢ˆ-ééÏLȘòÍ{øÉ¦Ið}=€œdb+Â|_|Ú%ÒÍL}Žç®ñöS™™í݆ö1B–wäëðc[>] ¿¥üB±"r¾©c5lµ½“òš§‘:ÇÏ’%†A@  €?È–.aò°ÖÉ-æÁ²zHk–,¸Ü»çh¹ 6ñ0›«hò‘%¢A:bÑNbBˆ’ ænnòx¼t†aPô±|Ú“W—öî¿æo›Pf9ˆÊú*Û‡‰÷QâðàõŽƒ9°ÿT›Ú:5F)))‘,9Y^–›#ùq?*›KÆÔ•—»¯²mEbýñS¦œ»ç1Yï Žþu}§þL¥>Ÿ‘åuygcßž>L”¸ïˆ’»+7ï3Lðûûa1 ü1~çVÖ£{0¥~ÿÉÉÓ–9™.^±œƒXþÝ䟜¹>Ú¡]õðˆ´áÐuM׎MK[Å=68|ÇôÎÁ;îWÚ;ÆØjk÷ˤ]v> 3¯Ö…aEdA@0CKÿ¶ïƒ†¹sÀâ³^¡þÄ)T5Kª[æÞÛ$§¦Þ¿rÓ.:ÿÌVêz…ŽlÂÆØ_‰56n%¯×‹i}òý¤è|ÚŸë¡ì‚–t ¿ È ©–KçQ]znñJ†M^O Èn¼µ’SóRƒ8“šÔŠ¥$¤””$JNƇ ädyYî`™à1–¼¾ã·MK Ñ(¾$žð®?pð]Ó_МŽÇ½nïôýãUܾ ¦w:<ÚkjŸ¿®Ï¾þzÜþã'¾z—V·ÒÆ(ýÝ>÷Ë<ž–6›Õõx=_@C1à‘ôôõ~·%[  ¯7À·ûã£ÒUE˜P=6*ÝÚ$m\Fæp(kþøã]‘®…mÖ$±FÀ7qQŽƒ”ëàW˜†÷ DÀûãØáÃ÷Œ›2¥·njM˜D9¢£=Gã ñùªãˆz÷ î=§Îù8aÊ”7·b‚¾Pל‹Æ¤ Å0ÖŒ1£FÌœ˜™ù¯áþjÒ”)ýG§¥mõ¯'ù `:²¬°î¨Šè…s¡6î‡ì[Ü’fj_Œ5Œµ¨„÷ú®ašÃÍôF?uèúÞ%ßÊOðgmÐ7øT<ÅÆ wnýÒ€RdÉ÷;bSÈRi@ÉuA@A "`®nÏÖ1ɘ,ᙹê¹ðY eÍÛÆY“ùï>þà½Ai¾éí¯×~tÈÕzt”Cu¡BG&*LR81ñ†‡P>ÈR>?¸A–<Lõ˜,ÁȧDI ªžÇš"6­c9Tt>6¹c_ªøø8ëÃæw|Ÿå Fr¹½ Óë=úýg³ó:Ïa²Í;ƒñ¬picÃþÃÏ"x÷Ç; CáQ8'-†‰×§àÆ3œºÙu4ˆ|”j{½æTÓAw˜U¥ƒo˜' šâ—y›Ý¦_&#cFâaó`ëö¼›~ÏöÇF õ+^YÑ,…×ûiA@¨`Š€…Q_W1» x9jVŒÉ‚=¡1ð¸‡üቼçÈ‘ƒ'Ö,ûéßZ÷ þõÚg?˜C¯¿ä¤‰R ²+ÂÂDÄÊTÎó~FñqqQ²‚?¸\Vð£Ð¯I‘%… ÏUS;õlš,§“÷}žOx.&Ö.q0&I|ï3Qâ:ÁHŒÝîGè×EßO?qâ(›cºðQ„IaŒG…Ošþž×ô,Ðtýo¥X̂׋rŒÚïñÖG^ ˜YS» ÚÀ¿i1Ñ[róòç¨.\ï…‡BÚãé#¦òyñÄ&{ã2²¢ñö†:4³Ÿ31Ñå:ž×ñ/D½‘ LçCQŒùÓP·(’^ñ†jú¹©!*^Ñ®Ñl¾è1]Ñþ°@;Èc¹äd˜%ÞÃ÷Án´O•ö—’+Vì*ˆñÍXÇZXRí£ÚÁsÔr4^kx •T.\® Y —7%r ‚€ P³°ÿ¸VÄ PÙš%äCÕ ß(s:žÈóD§`á×_,N®Sûuäï¢ɼóê?h•Ñ01Q¡¸™pžÃs3qa’ËÅQòX«äµ4K¬Ðc²¤ä8ñ3­–Y³ÄÏdíï¡Äd R”¥mòE¾ ¦F‰‰ÒÒµ[´-ëV½¿ô»9¼Ò^Pøa|gÅ¡‘­9 :Ÿd¼“?Ék,öÕY4fĈEˆ|Çß‘ëÆgL»H×<-ð®Æêžh—+7¾|j率 J¯*´06´÷Þ{O÷ðøÈ/#¼t4âJÏ¡'ú™>åùc%E ¾lu.Çß#àtêß»=Æ?€/ Ò`ëÊÒhí ,Çüoüäéÿ†vg·ÿÔ´iIØCëèï¥8õ•çg̈Ë;phÚŠM‹KôY2L¦›…_5Í\yêVC»„¥Ð~?" 5LÖðÏ—* ÏaæZú_¡N–X³Ä“ù||ò¾zç/»ñÖ(äoÞs(Û¼yÀz0|˜˜À°‘E\@˜,’³;Qò‚ azƒsN˜YÇŠüÇÏáäÓùü¤˜0ÁgÂz¶•‰ ¦6‰}”ØôŽ5J¿­YñÑ·½7"ä~WÆ—q®QdÉ nšSZ4›{ßÀ_MÌœ¶Ä°Ò5£G[òŸ7ÞHv^12}ð§^~¹q'޾Šï‘ñº š¨7q´ÓøÌ¬aП™ùBm³îô‘#Ÿà›Ð\L™ “Ç4gÁ7êRÿɸDijá+5ÃÏÈœ3>sÊ*DšC,s4B;K­PÎVtÂÉ™“MͽQó6c¯§f¦¦?‚ê/—³ «˜i˜3a^éÍ9p¶Ñ§CëÆ2—؆i^ ®C³T¢öI݇cptÞáÐS‘QA l˜ù´öæëײÀ˜â_7ð/6Ã,ZöÇF×ó¤k`Ò÷Q·Ï÷e•–Ûöظy|œ6þñÞ|¬dâ¿ÃLŒâ𩥪Ϲ½z_tÎEÑQ)g¶ibvïÜJ;«Í•Ú‡ mÛš#ÐAù)1AR$I¹| I‘%>ª#ðA] ´Ýâåy%ÎQï8˜ƒáq]:îÛË}ÿ3ÊÁçßç8òLžÜøTœ ¢r¸¤ ™S¯„&é%¼ÖýlIså#÷ܳ{\ffL`_ÃËÉÂø2àk˜ÎÍdÌÅh]Ø? #o;*mFñþnXúbÌÏDë;Ðþ;Ñq1ÿá¶¹ì„É™Á—æº:ZêeŠHoCÎKGÁ8¢÷&&j ·¬ ZÂwZ{6+«^²®gC«È߃*KÐ2Â7Í´|–¢uêPYòïi•ôI4KU«4*‚€ P0ca%̹Y+h²Íð0I`"ª‰'îþš%^¥ç]i£þ~ÞOë–-ÝtÁ€—y½žÞ lJe&ÅصR´Ø¨¨ ,x'FÅÏ+"ÿTüÜÿ^ ù|·ÛœB–Âùí‰ì‚€ D.±ªkpo œ,™&Oœ “êd‰i¡Ò.qä?&J˜{ZI)÷Æ•¿®Çg3®òßn.ÇUÙ“/ÆÅÆy#LŒX£ÄD‰Íð”éòW*¤ß¸A‰ƒ-@g0à€Û¸bîܹƒ{÷î½¾C9Ýç™×^KxèÎ;;2í9¨è<‘Ó›&NÚÁãvÍA¾1ßÇ@ÊÓÚu J³ø¼¬Ô®~Ï78ø<ük^ƒQcrûz©×±FÄ-sbÆÔ>޾š ~?’j¦9@u¦œ?¨|8ù‡V’ ‚€ R`Vk“%Ó[9Í&¡N–{5ñWÚ%ÖŠpô°Ãø°ï ù\iL˜ø›šñ„8Ò?¬5âê;cÄx”„SÐ*¡ïÄQéêEé·€9¬Yû“§±#G Çwhaþ±œw¦Nj›´bŸ¤I±I w?9yZgÃmÌCu‹(Áö.×thWÁôÎ&JÏL™RÂä)ƒùÅ“/žöˆRÛzNýFe:Æiì¨s„(G¬kt™ê-ÆÔw*ÎG!KáüöDvA@"ÛÏÈÐ'KºQ¤Y‚)ˆÝVˆÃÅ„‰¯YK¤€‰€ Rpyþ¨sEž˜(°…‘þñï§"‘ŒGql7Æqd<#^ÃÁæwíêÕ„¾¦¬?pð¿8Rûzuï†9ëqì¥ô4N¶%‘77·[ó΃R.•Ò ìt9¢¦}kã?öyÊw:’íYD½»K]÷?Æ:õé8oŠP𬠕$|áÎÂØidA¡ÑޱÇ/‹X„,EÂ[”>‚€ aÀ‡ÂÖ,E;tžô”\1±Efx¡àÁ¿_l&Va:€{윽¯ðÈùâ.©Ÿâ}Uç î7§’ˆãñ‰µ;gÄÇ]‡„`n÷Ò7ÞhôìÔáND¾3X³îï À¤ÌÌóÜó[°¡º| ÐQì¯tÙ˜ôôïùœöãi>>#ë£Çï½w‹åì@&dfÞê»[ô?üž‚pÍÙ™WpKÑUÉÕd<ž"<Œ»Ï1¾"â»g¯4Ôä—+}A@-ü<®ÀÉvBµ<øk©B«—%JÓ ö_Rĉó¬aó<6§ŠÆ‡ÿvój¾òmBk„ïc¢>Œ 8pû$1FêÈy¾ñ%ôñ¤4dÈ|˜ÝýÚ¤™3³¦ŒI‘¶aÆ[ â !ºÀkj_®„ïX6v¿0zİ%þôìÜz׫×=>+«Û˜aÖ"`Ä¥.ÃýÍÄŒ,ט‘#ÞWeA¼ZyM+JmuMŽ5ý¸ªíó¢|xçD³ÞïO¤A R°5KF”3`ÍRÅi–4-\Ìðüß%OôU„<î ›•±¯Ž¿)kRüÍÑø<Ò?ª¿ÊäPr`|'&•ù};e‚I^nª{´>gMš2åj6Ñ;v4§ 4D_ÃDÊG”0N±®oL[¼Á>}úx4Ýü7yùÞ##ï]å¸ÜKÆ‹ã'g]ÃמÎÊj ¢4 „ëSŽ´Ç×$ÕlžÎxµ!–2zY( ‚iLrü7‘‚ˆ¥Hy“ÒA@"3¼º ‰“¥Îï­va"gi0AŒ6o¼1ý*”–Ii–˜ °ÆŒ9ø9à`5é£úÎ80Š$)ãV£o›Ü²E_˜×}:nÊ”Þdz¿Â÷ ÉEÓö;£}F§¥ýRHµÍºÓ1ñí ²Õ‚Ë<:|øJ§® „VjêøŒ)£\óKÍ¡§MOÿ²´6äzÍBÀEùƒÐc‹W@Õ=WEaŒ„,EÂ[”>‚€ yØš¥ó† ˜,Yp˜O¢­´"v¾ÝžºFGžü+M›š±ö„?Œ ÷Q9ÉÕO>* ¥Iªñ$ XØé¾ &de]ªyÍ™&oÀ‡dO49{3ùQ¹Ì¸Œ)ïMȘv¶ºÆd û›MóôWuÉ•Cs\ Ò5ÚtÐб#FÌV÷ä(`Œ V(˜¤} ò‘p² oQú ‚@!ðÃSuyÜ·B©Qަ=Q1ßÍ"2î#Žp4Å+í­*òĸðGùïÔ„£ê3…•6Bp}âä¬¦Çø Ž}mg´C»„Íêü«5މù,j¾IžOÇMžò û*ñ}=Úù"êÞ ß$Û'‰Íöš$ĵFä¼yþmH¾f#ðÔË/7FО 0ÁKII|/’²IoSú"‚@ ›cOÎ0ήh—°Î+Åèj¨®ÈQˆ\Ø·ÈKæG ;1ÜK˜¤nsêæ%±±x¯9(Äãé#^Dèñ6ºn~AcLífê.³ʾíRš.ï.yAÀ“ïº ((Nñé}·ßÎfÁ“$^ļJéˆ ‘€×ã)"K¾½ƒ*Ô1Lóàûd%Wg3¼âý/ýŽÏ%……£ÏŒóþçU&-¢Ö 2È| +ýÖf´øl&3ºïè‘C·•õÐÂe§aO¦W®Yw»×ô¼º[Á´ÎúÏ̙ϱY_Yõ‹Ý“1Z =õ“£øªúçÐéu•”£¥Hy“ÒA@"Ó0m²„¿ÞÖ,a=þ;¾¹€¡»ÂÙ ÏšxÞóðÝ4]€ áÍI×aŸÃ~„¼÷HëË 2Ì=‚Ûܳ_{aÒ2ôQ™VxbZN¼’aš¯A«j4ÁÀÙ€±Ò÷±´¡»Êªç£ááü¿3fÌx}ãÁƒ7™ ÊݱƒÊ"KбvA¿ó/£Ï‹Š‰¹TU´DÿgH>tÀ¦ÝÇyœâww»áõÎzõÙqjœ–kŒŽÏ˜v‘iz:q0\Ø¡ÃW¡Ó»àH"d)88J+‚€ AC ˆ,á¯o%È’øÀ’ ÎêáH–´aÞˆóÖÑþŒèe1 ³!oò˜kÔJŽ×b£¢”ÙKЗ†‚‡@¾Ûm9–kÏÍ×u§ãŸÃ¿7?/ò¶¥«2æÍû˜£ø©½ |Œ¾’Æ>Jw½‚߸Ðhm4Åõ}8mÈÞŠ4 MË÷Vá§´&˜$ñó½{_ß¼Û™#cãbGÉX- ®Ð»^Ò8u»<ÿwpíöŒO?}…Çé)ü½÷ª^a0¼SH¶Õ¥ˆ8 YŠˆ×(A r€I‡­YÂZ%ÈRÅæ’¨Å‘ᬤZ¸™áéw?ò÷ztÔtÓ0wju†Ù½s+:«ÍZ|lL8†AW¯¢&-Ò’›_@+7í¢%«77Xµiçøöè6²Ñ™­ÓßžüÜ×C…;ç i…¢ÙÝ‹¯ÉT|W˜¼ i+cuêÿPÚý¾ó*ùß"Ih9ê–Q»,¹VíLhµÉX­¬«²Ñ’Æé¤F]ZÝwGëG†¿þï§XS¤ˆýIr<9ujŠÛcØQðäÄŒ¼$d)òÞ©ôHðFÀôÓ,U O#3_Ѭ0Ò,Y+õCÇüó/š¦?ݨ^ŠyÓeçSÛf 'Ááýjk¢ô ·tþ™­ø£mܾ—Þ™õc}ŠîxàÑ¿¿þü“ÿ&• >.3s$L2_„éoŒhÚ/ŽÄøKºë®CU„·5FÑ6Ï!£ÑûbãþÕ¸~-«Uøéh¶¤qºË4?þÓÃùïÓã_€ ¿ ÑïñwÀ7.žåÓHûöÑQÃVŸYO÷3,6yº*ÏA@Ê@ÀÖ,ÁÁ¼fxEû,™¦74KÖ$ôžÑOüDéÙn[jßu•D© ¨äV8!Àïò‘?]åàw ‚1þ¶ûú+äç±ÉÁxN)ž1å¯0’š\D”h‰S3û©z¢ÄòƲüÜ«@#‚’ÿ8ŠŠyòOåqzÒŘӡÍü‹ê¶®ë/ª|¤…,EÚ•þ‚€ æàO°M–`’w¸¢ÝVÉq&fxú]޹\×Oaòi½þ-:J,î*úþCµ¿S~·üŽã“ÿqíá!«?a*—èæð4TÏÖ¥¤$÷ž^‰†¢ÖJÉñ¼Ñ"J,7Ë/cµ¤Âü²ÿ8ŠŽpóÈ¿^Qøî-î0)sêµ&™­¹›XÔÚÖ¦níϼ˥Š/d©Thä† ‚@u  TÇ~®Æ>KKšß>Ka`†§_qÅm ±±ñ/5iPÛ¼óê?¤a¨BR«:àwܸ^-ªß¸É¿›5kÇcž÷DbÓ¶SÎÍÆOÎ| ÁžTò#ðÇü:ZêeU¼¿ ËÅòŰ¼,·ŒUõ"÷¨ÆiJí:™]º\”\8t¯i>XÔk-³0 HÑ¥Êò A}•®‚€ „¦f6°ÅÔ´v>ÐŒ^àªP6Ãcbä¨ß¹åŸÙA>JÑ(ú²Ã¯<¿ã[.¿@‡´ÁG8bc4>¬N,•,#˜ÃxÔ‡2VQšSÏ©_1räàêZ­1ŠvY¾8–—å–±ZH‡X“jœ&5<«O¯‘<þ?=LäBKTMˉšbbU!KA…SA@¨,˜•5Vm8t}·ÊzÔ Í6Ã3u3”É’µbûç3Û41ÅG)Ð7¾åù]ó;OI­s7z‘€O™Ú¥q™YOá~¬ê1œêg{ÕðáÃ9ÄsU&kŒâ,_Ë+cµ*á­¶Õ8MHJÁc *Úñ’ü¬†­°¹´j'”B–Bùíˆl‚€ ÔL©n'ÄÆU˜,!6˜M–tÃZµW͆ÒÑZ±¿ñÞ?÷€Ãt„/U£JB‹,ÁCÀ÷.ÿcw´Ê¤¾Dí4J/aØ“T(Ÿ>OnÕüš!C†ØãK¤™ö$ÒÔCvŸ%k"Ÿ”t)o8‹}”Âèm‰¨Á@€ß9¿ûzg4éöØ(ئxénBæ”Lh”îÇu+A£ôQý(ýúû,Pתð¨ÈËÇrÊX­B´C´i5NÔoðW}cúÃ#+¶éqˆv³D±dŸ¥a‘‹‚€ Õ€f±Mð ÚSÙdÉ·×§i¡j†Ç‹–ÒõæIñ±†l8[™7žuy›¤¸óht 3e¥Yb¢¯cRJ JÓp¸Gõ‘ÇÞíÙ©Ãí}úôá}oNGòQŸÆ+6 r²¼›I”¤‚ÓZIqf\lL+_—5W^^γ5¡û¢Yª oYú(‚@˜ `ê^Û3± ›àqw±XŸ§º­™!«Yâ¿ÃNM×ÕJŽ—¿Éê…Õ°c­”Íå¬n³O›á9{÷îíœ0%ë¿þD êÖ7ÚÕ«{Ûi$Jü&¬1Z(W ËÉòò I5 äÄ8û½{ÜoMxðÁJ-h… zòÃ.oJäA  €hp¶f Vê±fù,a’Šš%žxðÇ¡kZblT”=©ŽW›_—Ä8«`r¹½¬¼jû£½–Øà‘c¹ôÅË)'ïtX®•(B©ùÝ;tG< XD©uëÖÑ}¯¿ñ¦aÞ¡*Áôí•ÇÒÓî:Í!ší1 9Ø)šå¬èX„1¦ÞÇ©ŽËÖn¥¥k¶œªX¹ï¯ß¶—ò \%–_³yÍÿy}‰÷‚uqǾC°µ~¯¸É‚ «WýG5>‚õ˜lGÌðBòµˆP‚€ ÔL *"Kø» ÍRÅ'ïš3<Ó7Çyö ÅÄ‹–t³Zˆ“£/® ¹KÖÒ‰Ü|r8tjÓ´ÝvÅ…T¿N2}>ÿWš·l-=ó—›Ù¯ÆÆoüôO¨Iý:t×Õ 7g.¢åw ~5®W›®üÃÙtnÇævYÎlßsˆ&¾âÛ³ÒétP½ZI4ࢳ肳ZŸT®²'ŸÍÿ…¼†—†ßÐ÷wM>žCŸ}÷ ×±%ı'Ô OAHêÔ©sÛ¨û^¶ñJ%!ÈôÔ1é#ÒØWH];GßõùQaÞhÉYîÇŸŽ1öù÷¿Zcµ¸P±1Qôƒ·¿|ZΗ¬ÞraP·N-÷¼±С#Ç­ëlÞÖ¤~mºiÀùtŽ%%¯× ß™m}ߺ—Г¥5›÷ÐÅç¶/©zP®}óÓ»£G²_~{úô½¸P#”.B–ìW/A@êFSÁFj6ˆ™c¥Ìð0I³<`ŽÊš%D™ l¬÷Ä“ÌY‹VÒ ~ݨK»ftôDÍøú'zöõ/i|ú ±à2·ï£vÍZ=}Œvî˦k.9~Y·¯úÒoìK)‰ñ´qǾR'|\yèu—XDeÁ¯éí¯Ñ9í›QL4Ç NrM¯ÊðëàQÁVxÕ>%%%&mÌØ£¢¢ú¨f0}qìÈ´ûðQ—Nç‘ œúpÐ ‡Ÿv¡\rœŽ1Öëìvtfë&–ªŸíå÷‹Õƒz¸£&ò ‚€ TEfxšV93<ÝÌÃÂna YŸ%5áP‚ž¶£Ûã¡o~\ÕèÖ¤œš’Bs1=žùýBÓ·{GjX7…~Y¿Ý&Kœ‹‰¦N­Ó'ó~¶´DZù¢ø•¶2®:ÅÚªf S-­›(ý¼n›¥]z +íÃõ¡ç,µ&ÿ|#í=t”Þþrm…VªvR<]uñÙÖ*ý o΢–gÔ£kzŸk5Ëfw¼ê~÷µÓ+³L o¿ò"ëžO3¶Î’·×¹í”Ö‘ûÏÄð—õÛˆµ]}1q½ì‚3¡ 0©$yNª\'ºNZúè1ãccãz¨æ¡Ì{vìÈt¿páêÎi=ª1ÊÇ€4 §kŒ¥`|ð‡"Rí”jѸ®uþÈügó—Ó¸ôë­s6œ0ý3â1Ò±ecz c¬ÆýîýÙ´ šÖ[WöêŠúõì±0¨h`—Sö±:¯C º¦Ï¹Ö˜â­ØD3Xaifù;ÁZÙørh.“b©IƒÚÖgëî´ípZ¾a-\¾zÓÞýúGêÒ¶ êßþ9õcºñÒt6NäÐ?O›vî§æêRýÚIV]þÏåöXÄ‹2ra¶ï*ÔÂôðW@®èǼ]¹„ŒÇã…vy}çPö±=Ûû¢Ô¸°ïEj& A© H¿A@BLT[(ItSÛ¡ò9jF‘Ï–=CÕ »Æ“ŽÓž9aM¬:¶´cjX2Ô…‰\=L¾xòÈ©[Ç– [­<ÿ÷ N×vM-“½.8î9x„^þè;Û¬È.XFfÛÞCÖÝØhLä=Aïα&s×CËå5 šüÎ7ðÑpÓ˜Ô¶jRŸ¦ãlÎ×y5±äºËÖm%ˆOKLnåäÓq˜rÚ¹ï0±Vƒ'—L´‚üù§ù?o¤•›vÑŸ®¾˜®¾ø‹¨±ÖŒSqyüëUMÞ¤¦g4lŸ˜d%¯ÇóÌc#Ó©šçܪš4V«sŒ©滓7¾.l½_åcÇcæchvb¢t'ÆÚa"ömS‰Ç›_. óÏl mê¹ôý/,BÃ÷Ï¥ÿ}öý᜶4ÚÕ]øÎÌÇýò¤½ð¯ûD5»?®ÞL½»u°ª¹ÜnZ»e7HÜBhv›ö7²®³ Bœæü´š~Ûu€®Å‚k|¯Ül]çÿxbÙÚmôðŸÒ]Wõ´úz74®L&ËóvÅ2‹°q<Ç÷‚9(9zœÔx(V:2O…,Eæ{•^ ‚€ ®ØN,ŽØ¨ß*Ó ¯Ãá /$Íð*Ó½J×=“;N‰ñ¿·PLˆ‹¥£9¹Ö}ö?âà¼úÍ“Ã-˜¤ʼný›î¿õ2Úó»¿g}dÖÌ”–اè™ÿͤ)3æP³F©tV[ŸFŠË³¶hØõ},íѦûé |:nÀŠ:û|܉Iûv0I:~N,ÇoXUçô3éÙ£¸ÉÕº­{¬þÛ^„‰n+êw~g«¼úoÕ¦Öª}ƒÔ$j׬¥AcM—Jþò¨kUqt#0† «÷±±1‰ªýœœÏ=ñ—ûþ©ÎCëX~“ÑêcÁŠÇÖm'g·oNviKë¶ìµH•j‹¯÷ëÑ ¤¨5n×BʼnÍÓê$'йšãOg¶ib-&¨zeÙÏèí¯~Qû™bAÔÎl]ô]`"w]ßóèÚ>çÙ2ÿ¶ÖmÝmùÞÝ:ÂG°+¾‡õíÛ‚} Ø,¶cK_›.·uÿTcÞn¤0ÃDrÑJŸÆ‹/9l‰Yó’˜áÕ¼w.=A $˜›Q/1÷ÄÁ…¹úß÷ÈNºÿ‰ ˪{Íü"+¼Ô,ñêlµ¥ZI Ö³cBT<±™O¸8qІ†©)´æw)˜*ÎZÍ¿ãÊžVyÿÿ˜Ô5mPǾԬaQž5W¶ïµ×Ò5[í2MO%yÔµ`ÙdêY‹ Q í¦îÛ÷Âÿûçs…ªuŒ@†J=¿:Ƙ d)–P<ùÄøbóA—*¦ ›º©„ýÐl k€ØÏoÒ+_¨ÛåFŒM\™ 1‘yoözîõYôüßn¶ÛaâURâ€Û aeR§›¶2qãÔ&±¬•ú¦s¬éâwÀßÑòŒyÕž:~½x%±'ncËÎê–ÿ±RcÄ¿¡PÍ Y Õ7#r ‚€ PÃ(È?bk•0EÛ¬iOü~V&šû,ù¬V0#uŸ¥ò¯ÖA™ESk%G [ýÛNˬNf‡s6Gë~'uÉ ô°|ãvø4%Ù&xöMd8HÃu}»k„~Ç.ð¿[”ÿ#̘xbWRŠrMIØ Ó^ÈÒ 'Nû·Wß»wnÉàL©Aªà3wäv·nÚ4õåžYž€†Ê$TÉ¢Ž,j¹RuŒ±â‚!.¿õNyÒϾi;÷.^Ä2ÁûÝE¿ ¥Vˆ…?Pm, Œyƒ_éÀ²üÝabþï,r£jGAÖ’kPyÁàÌhUâE•ÎíÐŒ¾ÄÂkŸZ7is¼.V$K˜_ic^Õõ?²†Š?œø®°OßË;¿ÃÄ›ÿöÃ)/fxáô¶DVA@"„Ù-"K&Ír+Øg‡Q´)-¨H(û,U°‡•«Æ§K¡âIÚ׋W!°ÂqKÛ2íùÖj4GëR‰#‹ñ*5GÅS&x|oîÒµ–ûy¬úm—5m„€•M¬Eb3¢¯¡©b¹¾Ehó#ý}œÝ9õèÜ„îý´ú7ê_’’R» -?ª…Ë7Zäï‡b¾$‰ïGÔ_s=^uçþ)Ÿ¥’Ú æµ|8Þ¿‰ÀþDéÀÁ#{A”>æsª»­Pcm›5Y"úã€M;¿[¼ýˆØwÛœƒ°Úðb³ÃM0I-Ob·{[%¿Z¸ÒZphP§|ß-[ÁQ؇÷_Z‹1¬Òj|sÄ‚#í±yiL U*ï˜gÓPÖ*©tÌXSkîOhÑ2ŽBDŽ‚€ ‚@5 € 8Û=VY*2M*º^þœ36.Ï•ë3çÂl©æþ¥/²=»À×G§Ù‹W[þF<¹m‹ ÖˆA}­UxU•£Üñjö±ùV_à«ßëlZ]èkÂe9ÂØë_, ÞúÚÒ<ÄÅF[ýæU™ró]ôÖW -¨žÜg)R1©p¬î1V¿N’µÿqxænlþ¦Ì+ o—¶M­¨ŒŸbÿ®÷fÿdipØ•}ùN•˜Äó‡£âqØs<Ë“X#ʤˆ÷.ãþñ~ek6ﶪò÷4¿ÀC5’µ¤üýpáYôÇKÎ)uÌ׫|Òc¿ûy,…/²OãE~&'¬!'¬B“$‚€ T;3ŸÖ§âû0›oÞ7ðaãÅʵôÆV)fî ßæ DǺ±¿|˶§xè°ÇÆÍã"ÓÆ?Þ›•HlgÃ$.yÈC}Ò©MónÜqy%š«\ÕƒWüÙ¤©xÕû°°üÔ¢¢ÏPmê˜_°7±¿k#Tº $îÕåk6®}ãÿž¾×Ùöé>ì@ÅQ8|N#ÈTC²Ç(žŠOÃÛïøÿºvjÛ±"cµ:ǘÇë…ý˜fEq¬ Ù÷/.6ê´A`<ý ûZ=ü ºåŠ ¢•E–˜ø0Q|áÁÛíñ]֘ߋ薯"Ÿ2G|éùv‰ç_ÿŠV®Û¼âµOºeŒñ÷´*^«Õ¦˜áU´Ò° ‚@`i–0©©´Õ®]¨V²œ?D³tŠ—Á“®Š%n¶ªˆ·í#pœ«XâÐ¥%n‘7üäÕóª&J~ùõ™ l¢ÄXój?k'jJªÎ1ÆZS~×U•XCÄÏ8ÉŸ(ñs9øuÐ’ ³ÕM;÷Ñ ìÛÔ¤~“ÆwicžÍQ?E¸}E”ºblúGÚ;} ¥gUݨ ¥^Š,‚€ !ŒîlçÃáÜTY»M[æÆB²µ*¶£Ì'z‹éyeA•úBàˆÒ3Y{øpL”®ÅƦl~%I¬½›3oE$Èÿ¼ý5Lk—Y!Ó°TyûsÌöi=™üõ/n¿s!©MÙŸåÆK{ˆHU_ŽöÇf|@Ë7l/GÉšQäÛ%kHíCÆ!ûû÷(ySéšÆï{)dé÷˜ÈA@ÓŒ€I¦M–àÑQùàEòÛš%DÑÍR.ÕšÛwèí=x´DŽË¥/°©fN^A‰÷Ãá"ï×ôˆÒ‰Ü|K\ŽÆwÓeçËj}ˆ¼<¯áEƒÀ…á(rã^ú$ðŠ!\cý¶=´líV[BŽÎ…Pý’ŠŸ¥",$'‚€ PM`ââ·ÇRå7¤õ놭Y2=šh–ü€ Vö£o—ѬE+­æx?¤:ɉØì¶3õ<»]©ølþ/ØÖKÃoèû»2‡Åë3ì[s6ÂMˆ+ÚPówCôÁ·f-"ëÌ):Ê ¢ÔŽöy[R¨ °„ö¥çQjJ"õëщR’â­ Š÷>f…š_²z ¥ÝØyßT™#Ľ…„™äóÆÍ]Ú6³Æçæû­½–¸_s~ZKñ~a—64ÚšÚÉ ´në^l¨œKÝ:µÀ‡­C'Å&º¼0¡Ë×¼‘ŒS…‡:ŠfI!!GA@jD H³¤éZðÌð4ÓÖ,¡s¢Yª¢7\+9ž¿÷JÜߊ¾õî×?Qnš¡!×ô¢¡×ö®"iª¯Ù=Ø£æMl8«ˆG'»eÀB”ªï•”úäÅ+~æÅ-ˆÍ%3Þ›c•ãý¯>™÷3-^±ÉÚÐX%ÕHÃzµ°Y³Žm;Yæ”ûA¬ÖoÝ£nÓÖÝhç¾ÃÖùÖÝ­M•`ÓØ-ÑˇMýŽìjÕ’aòÇû/ñ>fœj'%Pßn«E–P¨h–Bý ‰|‚€ ÔI¹2‹Ñ4fxÞ`õÚÖ,¡A!KÁBµX;NÝAgÔ¯m]žFKÖl¡²©]³†Öj|“uèБôý/häMýèÇ•¿Y{ÂÜ~åEVÏá\>oÙ:k¿¦^瞬‘Ú±÷0}µp­Ù¼ÛÒÒ8±WNÓ†©4bPk•ŸÍÝvì;Dõë$Ó-—_Xm¦n»ögÓ;_ÿH.ßä“÷‡b¢Ô¨n­bhÉi( pQ×6–ö²Eãº4vòûÖÞD,oìš>¸Ÿ î0ˆÔÖ=¼÷*a,7 3@–8ša›¦ ¬k§ú¯u“ÔÄŠÓ— VÐÆû¬gžªÞé¸ÿ¾o»ðåÄG8”½梒~€h–~‰\A@8pØp2í=–Œº‰õ6ïñZŽÝ–æ²dƒÜLÛC¿®ßF ~Ý`ííÒ¼Q]j}F}ë!ÇròiëZ½y']Ù«+5LM!¾v<×·ÊÎ+ñŸc#̳몋Ϧ…¿žüú?™·Ì"I“þ|#B"7¶Ú¼÷úK¬#Gš‹ƒÙÓý·ÀD¶6½ýÕ¢àv¬œ­í@ÞžµØ&J¼Yèm—_$D©œøUG1&³œâ ¹ù>9&ݬäÄ&y¬e⛬•”ü]ŸxãaÿÔ05Ù>†9ŸM_C!mÞµŸ¢O*]vá™2V%E³T(rIAàô!°}ÛÆ˜pX aÚÒmønmP¥¶Ê&KšæH¬TcR¹T8ÁÛ_ýHù.y<Ý|ùXG\Ô_à¡ñéíI¨ºÎÇu0cЇoÒí}Z&ˆ×›Ð©´~Û>ºõŠ ˆ'·m±ºÿëúíÖJ8€ØŒÍ7G êKIñ1tüD^xs4XÇ)µ–µµ–j¢JÛ y˜1ûGh$|ÚPîËmè½ÚEå*@¯+7î [ÑOðMªß"&I·ï=©­Ž-Tò‚ìðødÓ5 @üÙô2¯Àe™nžÒ°rüž…\â}¿>ž÷‹-W—6MéœöÍísÉü!K¿ÇD®‚€ œF òÚqjAšVóÑ0ŸÈQk¹†éMfÛÒVì$?~ä ˆ0f"$óÊzÿ[Jˆ±ü>¸T›¦õK$J|o OS˜é©Ô¬aQž¯ujÕˆæ.]‹‰§Fó^o‘"¾¾Žóü¼ÿ}öŸZ)ÏܹÿÈi#KLÖÞÿf y¼>¢”K·]q¡4@É$ÇÐD€5‘“^ùÜ 42!ÝË“šÁü“Mð}qõ„ßõ}»SËÆõèÑÿÌa£ÎØ ¶<Ï V§<^óAî85€æëòžg«ùˆmGÈRľZé˜ á€n˜g*B£™È’¿aKåú`j0ÃSÎP& Yªœ§¬Í„æ¬6M`6ç Úo“%>/-±OÏ"ø0©¤ö{Qç—_Ô…¦€|mضQöÎDûM­[±1¾)Ì_n½ŒšUC¯M;öÓs–`²í½I ±tûYДìr Mžüó`K°«/9Gl]]¨âŽeEqäJ½m€­Yâóû1þ\n¯5æù\¥!×\¬²Öq\úõ'WÇÉÌV¥àĦ†7ôíF¼ÿ—¤²Ÿ¥²ñ‘»‚€ UŒ€©™¶f ‘ð‚ªYï:a‹¯ Y²±rƃ0àˆa̘ÞEž<6ƒã|yR» -Ó¹…Ë7ïOô‚@ø§o—¬¥6MêS¯sÚï"Å ëÌ÷[Á'Š593,·êó3Wý¶Ë¿j•噸½ïG”RãèŽ+{ Qª2Ä«¦a&÷Š(òåï¤ê”µ ÊT÷‘C¡¯úm§-Æ5 еOÒ©ÍÒ©1’‚€ UˆôH6YÒɲä3i Æ#M >K…Š*(˜Äg) –Ðo$;áåO­UêvÍ"*ÝÔ½œ{Ê´:·cszíóÖêü•Øsõæ"ÒÃ> â˜ÍûX‹Ã‰#á±9ÔðzÓë_,¤±XR±ßIç×Wh\B·J¼´vënÈó³eÈ8äòm/¤äÙÆ«DÀäbµ#À¾Tßü´Ú–£g×¶åŽègWªÁ!K5øåK×A@¨nf=Ó ÁkîoÁ|+¼îÔä³Ö- šXþ>Kš˜á Wÿ†®ë{ñ§´ÄĦxò¿Æa‹‡]ß‘ä<–ÖˆC ¸ÐçG±|Ãvú›{N5ˆêÀ/ŠÃrOý`¢îm´&{mšü_i×[öüŒ¸˜èâ ê9Gôûô»_m¢ÄØG)IˆRPq–Æ‚‡kk?˜³Ô³-¡ñ½ä¼Á{@ hIÈR xÉÒEA@BÓq¸³é†Ó'“6t¾Ì·IM°öÓ,‘˜á Õ*i'û3•”8Üröñ\k²Ç!ºy³OAš$ñ³V z‡8W©."î݆}‰O’ Šp(sÞû‹#øqJ†_ݵ½K_ØÅ>„‚L%ÿ2…‚d"ƒ ‚@Ä#`z½ìam%cXÌàܨáï³$š%Ðaô×vÍhï¡côÅ÷ðK:zÜò±`-V¯sNÞ¸¶ª»ô öâMEUª_'‰nÅ>JñØOI’ Š0AzwöOt{šqŠB•Aý{`_2³¾/!K"&åA@‚†ÈŒM–t“Š6ÿÒÐ,yÅg)HhVO3°a&ª+-]»…¾^Tw„÷Ö¹%µ™iuÉ%ÏJC€CêøíR;ò²¸¾O7kCèÒêÈõÒhx¥c#wA@ªÓ´É’¦ë•"Kÿ~µV­ISCýE6H?îw.»„ú!ÙS#ðãêÍ'¥ÆØ›çvø( Q:5vR¢ú˜ùÃrkÃf%ÁØK‰©HªB–*†›ÔA@¨$挡ø¡.ªg\\…ÈÒÓScZOœ¦ÿ'Ïut“AfwÕ5ÍãÛT„ó¤%ùß“¼ P Wl¢9?Ek‚¨|·"ÊïO#IU¾ÿu-‡JÀÞQg·k®NåX„,U4©"‚€ Py¾ÞöIGXÈŶ´½ÿ¨ã‡iõÉé΋'NÕ>òëÔË6õøÎc‡ÃýÛpšš¿fIÈ’?8§Èÿï³èýo–œ¢Ôé½½vËnb¹Š'×K#'½F»ög¿U¡ó0᜷t­]·YÃTºeÀ…m.Þ 6(’ 9VlÚAßÿŒ€¢…‰7ˆ¾øÜöêTŽD@¾õNª ‚€ TÓ( îµO¹´JS§žuXûu°IÆ_ ¯×©iúF5½eÈ­ðbÎù@ÑNç1ÂMs13¼ß!TúŽBgšFéªáû qЇ’ïÁŒôݲu´äªÔ¡–Ã1Þét¨KrBŽ9ó‡¢ $"üÊ?t 99ÃQ !KáøÖDfA@"LÅÏUÝÐ5 d©0ƒºXÂñýr7œ—'£|†IάÑÃ\눶–PÒwÉpÖ:N®Ö‰F¦h–JEªä.·—Þùj±µQ¿ó;kX81OʺŸÙ þ<+­=‘ZžQÏn€ï<ôÃ/¨cËFÔóìöÖ†³s±gRãúuh4D9yt+ü¶ï9d‘§CÇ*xjšl=óœÍ©}‹FV»ŸÌû™š6H¥„¸húmÇ>:»}3bmÒÂå›h%VÓy\vbW‰÷lZ¼rmضÞ¶ sÚ7'Þ¿é­/QŸnéëÅ«ˆIPñýf¾ýi -^õ›jÆòóÔ¯9B”lP$rð^JïÏYB¼ÀÁ‰£5ÞЯ;Ƽãe ŠÁ@QÚA@­(¸ƒIÚÏåi ¶vãךþG{³î'LÕvN˜¦¿:ašãæg§&Õ-ÞFç÷V»à«TÀ×AÅ¢¶ü©…2û+^TÎK@`éš­Ô¤aªΤW>§¼|åCS7¦>ïÎú‘xSغµOªÉ÷ÀŒmÑŠtA—6úì;ßëåÕï—>œGÇNäY„gç¾lzþͯ¨QÝJIЧqÓ?±î9:Í] ŒÄ¡¿Z¸’ÖM¦ƒGŽÓÚ­»­ëLؘÜ\pV™7@G@IDATZ¾a‡½é&ß|õÓù »¤[+ä÷²u[­:l¦”ùÞ·TÏjݤuMý7ûÇU'¥¶Í Ôrw!J 9†$ÙÇrèm|Õ^JIØKé¦KÏ“Ñ ¾-Ñ,LiJA |˜æúÌgþik–bјMç²òƒwr¡/ ?4éÕØäv]s±›\t" äi£®9ÓGs9ÛhtLÉÚ94;'ŸµK¾GNù4)À„Ä9-øu#­Ûº—š4¨ElòÆûu€æ‡µO?¯Ûf•aÒÃ"NWõ:‡Î@P„üÍ©¹ÁºJVøâ;®êi}úÝ/Ô©Ucê m§ =?ƒØ\Ø¥­œ¡.‡înÞ(•8Ý–]>-!—]³y4Cíéñýî[Ò/±ƒ0ò%±vø¼}‹†t6yFCR¨"päD.½ñåB:‘ëûIãà#7_v>%%Ä…ªÈa)—¥°|m"´ áÀ¬Oìc™ÅÁzjw¿òvU¤G£‡äoE½,þ°?Ó!mùE¦S;Éy Z(Žˆgi wû-͸q"©tbcŠ6°äpÙ'òŠxfs˜äq*p¹h1"Çqê"¾œ¢± &'Þ¸õD®¥Ü³Î›7ªcù¿ƒÙÇqßâ±Öµ„¸K³Ä$‹?¿®ßNKWo¡‹º¶µë¨ k§Ô¤09±hr˜ ó>·ÇcŽ-›Prb‘B‘M ýÓ³¼|ÃvûR§–éšÞçždÖgß”Œ "ƒÆõÍ™‹Š6…OÝàK{P½Úü')˜Y &šÒ– ‚@¹ðz½=ü þä—/w–µS“^{®¶ÓpÕ1=Fj¶ñ+Ï‚ëhnƒgßkTCšfa²’ÓðÊLBSŽã¦í{éèñ\¤Ú}àu‚ÿ‘¡À,¬Ï„%}p?»5öŸàÄ&|—ÂÏéçµÛ,¿%»€_¦UÓú4{ñj:r,—Ü^m„?Ò•½|NévmCK@”vî?L£:õ÷«åËvlu†E¦Øiž¡R­d6±«ó9úv÷i¬ÔÊ»*ÃGÞ¸óóï…ÏÓNû2G»ª×ÙB”lD$Šðx~¥£Ð,qr§n0Lïš6(ZˆE¹ÃU&!KáúæDnA@ÂÍ4{ò25 dIºS§ióQºóÄiÿL†KÿQF‡p~5a7¥C¤Ó|ÿVàÞtDµï!£–ÿ=É—XxåÓï’û°EbRk%‘"CeׄQ%ü›FO~ŸÃ4o0LƒJJ—  Ã޽‡é‰iQb\,õïÑÉ "Áe{@Kõþì%t.=ÄA;U<1šñõôÀsoS×¶M)%1Þ.¾F_.XAüç]‚0èZhŠüC(3Qâ khB%޲'ÑÃr UXsúæW‹ˆ}•89ÄaPÿn–©j¨Êîr Y ÷7(ò ‚€ –˜¶fI7t¥€Â>§h}áèaù­Ë*v lP∺ªB–Ê;\†\s±]”}”xRƉÍ|²ÆþÉÊ—õß=»Ð5}εëqYÿ6ùœØÝqåEÖf¯ìäÐÎ2Ï›üè\ÌN=á?ÅNl¦÷×Û/·L'Og8:k»8*ûP±–‰Ó“lE ûpîRZÿ+•˜]~Qu*GA $È+pƒ(-¦CGNXòñwæú¾Ý¨ÕõCRÞHÊ÷ë)½‘~‚€ „<s_µ"ÒÅ‚brl:k%- Hh“94Óñ{¢TJ+º Í’Jºh–Q  ŠU´¼õ˜Ìø¥@žSœ(ù׉vÚD‰¯3éûàÛ“‰|¢äšäC”ò64JJ³Ë¡òYcÊAX$U-'/ÅTí³¤uA@A€òî<VPQ 4>ë/ž}4 X`vçÕŒòç›Efx†f Y ìÀ ' `k•¢BlWøÉa/š¥ôÛÎýv§.8³5õ…éŸ$A ”à=ÉÞ‚FI%–õ—œmE£ e¹#E6!K‘ò&¥‚€ „ †iØ&xØ &xÊH®|ÐLm¬öŠÂŸ¢š¿Ï’fhB–NWeoÇ#¢G“ ¥äñxiÆ7?ïó¤RODØ+¾)­º'GA T°¢Þ!˜ƒòQb¹8IçVMBEĈ—CÈRÄ¿bé  !‡€ííš²XBĤ6îœ4͑⌭ûÕCwî;)TxñÖ`é—mÓ1Ñ,‡'âÏÝØêÝÙ?Òö½ˆÿQ˜zÓŽzÓ^ÊQI²çÐ[_.BÔ»6½ã=Â:#¤¤Ó‡€¥Ó‡µ– 'ù\ûÜ?c Ȉé5Écx­Íj“ã°AngâoyU_ÒéG &Ó’ÐUc_W‹øs§%!Uõׄ,U=ÆòA@?²¤1Yªxš0ÝÙOóz/05ê‰)þ¤ Û^|·OSµé¤F£LãP¡2‹¹Y%K<ù4°Ïk‘ØiüHRlL4¥ÖJ¤¸Øhâìšžx5?/ßE9y.úû1}»dµinçÖyìt §§FZÆé©1 f !KÁDSÚA@(ÞŒ6ïÀ¶s92æåfbb"‚;¶Å7þÌk‰õáŸô)^'éú?Æ õ|äÛ vßþ]*hØô0íÚa]75³&c(®±5&1Iòz šóÓjúä»_):ÊA R“)æg’Š`˜€ÐçüasDÖDM~wÝЯõ?¿sQAÉU 2NË«ŒÓòá¬Rz°’vA@²(8°³;î}›Ñš´®WúÑ"Ïû²*»çpé]sücÌ0³Û˜{½žŠøt›¶Œp8(k–+¯j^c‚<ð*=oÆÊfw³¯¤çýBqÐ&Õ«“"DÉ¥ÿÇD²~jŠ…×ûß,¡o~\…8"5Šc—NïÈ8­8 2N+Ž]ykŠf©¼HI9A@J!€ÍhíáÐ-UØ_é¡ÇCY,Œi>¡Ozí¹ÚNÃUÇô©¦a¤Õ?Ô¾ÑÃ=_ ¬ÁoÉLæs·Ã`S¼ µ¢öB?ÇP^©g¢´rÓúìûCuk'†¾ð!"¡ËDÆë FËs–QC'6ÉA Ã_ §•‡2N+aY-ˆf©,täž ‚@ðЂ範áóñÙ?iÚ?÷˜ù'x\®ÿyLÏÆf^cšF¦¡·œ$¸Föޤ¦æ®~KŠ(ÈÉ£÷f/¥(*¨ÿ$I#À¸1~o|±\-0pª®†ŒÓàa+ã4xXú·$dÉ É ‚€ T X=†—õTk¦sÊWäèpü?{çGq=þ·{M½Û’å&W¹:.ÔÐ ~É?@ „ò ÒHâ$¿„ÐL:IH ”„bªm ¦¹Û€ånÙ–-7Ù–,«Xýúîÿ½9Íé$Kò®èîôæó9íìîììÌwxÞ¾7o²¯ùñ7µüŸÜ­çÿôn}/PÀü#´‹_ýÿ“Z8ú[]êÕõNeÉÿñhêr¹qÝVSÄ»vd¦%a‡.TøÄOÄøQP Š"H|9O€å4x†¾5°œúÒ]ž•¥Ð±äš˜`L +Ÿ´LÂÕ¢ƒL§®ú¡½¼—¢~]þá×ëZ|×*ýæ†[tݱ£â=ú“»´®¡Ã©FjeÅ8¡È“ùx«lÀñUX¾¯D21wæxhliƒÚÓž@œ}•õ=–ÓÐee)´<¹6&À˜耢ëKeÅT–‚[óñÛ§;Üö7q¦ú’¢ªï8­u_D S6®ŒÊB=,•£lƒ1ëëä®GÍÁ·Õt6Kse‰B…;… Þé–v0(*o8Û9øAåhƒj€úÆV’݈AXY ©GYX9ݱ¿¶–…¢ül¸ôœIp´ºö®ê³3£ ó`Á¬ gU–ú¬$7YNC ™•¥ÐòäÚ˜`L wŸL©9ì…tçšÍW޼q„‚Jz1*F0jÄh þ?Xp¨Wu¨Â—ìÂ7ÕJÍ·`ÄpL*†—ê.5‰[e‰\›h_%ŠØf³Û¡;¨†øñ¸ÏÉL… #†Â†lÍŠ‹Þ[ÛgâMÖ%Nˆ9%÷ÔêÚFñ[”SЇ eiHN\Žçg¥ACsìÜ J÷†Â¼,¸ê‚)~ß ÷¢eêàñZxoãNÈÉH…+ñúP +ß„r±zË>¯2•ŽÖ«;¯¿Ò’aï‘* ¶²Ê?‰å4t”YY K®‰ 0&Àz  ;såe´.mTnYâ–çý=âf´Ïâ³ôÃ$mVž³Î¿eÞ¬jRk4§çµñ솧á‚,W‡²d·;À‰ñè+s$M2¿†CJ¤°‘UkÓÎ ÜãéøY_Ÿ™– Fƒá¬.Kù™i½‰êuP€âiw¸gâÍ)p)§Ý[K®u¹(W‡«<1`Ú0(Ê>TlÞÇMœÇ—; v:Mè®w¬¦†æfÀòµÛ¡•-R”¿tÅ9Bzoã.HL0AZe:§d4lØy@ìuvåùS êÔiØ}ð„¼Ö#Ëièð²²:–\`L€ ô@­?^<%Dë•zxMŸ—t­3ÀZ¤â2À¹6I÷&—Ë%¢á Ädþ­U[Àjw´ñEð¹ó§ŠuSgûš~ñŒñp¿âû»¾c UÉY2gW¼>ÿóërS2#…z åtìð<ÈÅý³†çg¡¢®Â§»ŠvRÍe‡Ál2âZ¦¡œ– ûW‹ü¹øÑ¥ò¤g'‚C³¬G/¯üè\Ç$塼²>Û}HÔ;ulŒ.̘²D/e9èƒþÃÊRй&À˜è‹Ná=æ,„ë‹Ö»^©¯wõvoÆÌ1§¶lÞC¶´ è¹ú¢Eª²h‘ôÌëí±˜».Ý›<Q´¤ €õ…\—NÖ7A .lŸtÛ0á6·³âÌš8æà—ö”D ®©‡øÅý~i§¨a%£‡‰¯óSÇÂÊM»Ñ©¦G÷&šNE—©‹fŒEWClÝwG#:VrJ¼9N ä?Ü@3*FËÖí€ÃÝ,²”ЇçÃ…ÓÇBVZ œnòÄŒ1â:µž¹×Qª®;ÝÓm8Y×ä½N–(3®q‹db9 mv´ G®… 0&Àz °ò±”<œÛާ[佤›Gnî¡XØ/)‹V»°ÂÏÛ£î(ýçа¿4Â/ ‰‘çGnph8ùH ­á dw9űݘV•îßÝ”$JŽ+N$÷aä¾eèÞTU{ÚëÞ”d1áº]°ãK÷&új?}\|ôéTÊáŠó& +€¨,RkâFÌ9ùO Zät?*ä+7í‚•'½ŠõâòóJ ¦¾~ÿß•°äãÏúì˜]])‘bÕS:›Eµ§gBzå4$8YY F®„ 0&Àz" éÖyò:þ»ýéÕß«À  áMúl/º-nQä¦'Óš­ûÄ‚y:Òº‘Csä­ˆI=’¬#òÂ8{‰d‡êý€ÉiOHiÝ\JB‚Q*ôçO.îR¬¡±2R“0`C$¢"O–Q'ºå]0­²ÒS`Ô°\ÈÏNëòÌ@ž°œ††>+K¡áȵ0&À˜@t¯²„n/kz,‹8 Sܺó¿ù»"äuOÞHº[Þýf¼œ{'¢dñ ÷¦#UuðÜò ðÌÒµàîpU›?{"Üsó%p;*RSÆŠ’\ÛÑýÙÜ›ªÑÍR»ÕóeßlŒüªâÌ©ÿZN{j9¹âmÚUçM?øò•BF)bžLûQá?…î¥ßùÒåpý¼™bmÞÒu[¡ 7¾õ…"ØCFJ²,G–Óà‡!òÿw ¾Í\`L€ Ä´mø(K**KáYãñì³#Ni§²ù‡%[U O¸Áý4îÅtì'ßtážNž„FŒcr~«êz\Z–d_òHÖZ³ä›†b¤¼ ¦ÃKï}"¢ŽÍ™4 Ý&ûé’÷uo:nyÝ“3Bá—»¿—ÏãƒÀ__ý¨×ެ߾¦ƒÂrØ}MZ›Õÿ^¶,fŒˆèq/%k)ýh#XZ™h½ ¥ß>³¬Ë;–|Ø·K_—Â|UXYŠªáàÆ0&Àâ‡ÀªÇSs¬Zk }GEÅ™›ž· p¤P¤Gþ•“êrׯE/žl¬/½Êq”L õ$»ÁsT>ÆwvùÄ‹F'´,y¬º–¥hþ‚œ–’ †Ö-e`ô°©hYòMõM-0 ¿ÎS2J¾îMmÞ é¸¾÷ŠÆÍÜ™WÿH…§·§mk•|ïÓº»hN,§ýV–úÏŽŸdL€ 0>Ø }.ZrPé³ÙwWµ÷Q< [~­¶õ±$|ÑdN¬ÿß;N7¡;W7Ÿ(:ífÅRõcÞKqjY¢ Qç/ ¤a-\q¬Žâz¤»nš6Ü£hÍ–½ð¹ ¦zß¹eï 3>þ¿«àuÛaÇc@îMWaèqro¢/üo®Úê- É9Úkmì<ÇXk}lµW²Ž­VGWkYYŠ®ñàÖ0&Àↀ®éó;;C땺é37Îu(G1ÖüàN©õ]ª¨ÇÑIF½–ºš6ú~”ïúAàd]ã®Gò1úRÿŸ…åHFó ÷½#áÑÜ›äWûÞÜ›ÊpƒP™h=Tww'yL€ 0P`e)¹&À˜8“€Þ©,骺ú KÏ™O„õŠA׎yT%|®³²VÚ=W.¥žï‚WQò½íîM¾må<`ñG€£áÅߘr˜`N€Ö+¡žXÁOë•òSó6 t£Œ 9'Ðå1o)0T¿ùæÈî9ÐøýL€ 0&0V–FÆ0&À˜ÀÙ´kíóµ^élïîí~É’2vðlú£ƒa§{cÜmLÛ[ßù:`L€ ô+KýãÆO1&À˜@<ЕÕ}ì-ßi.Yúü6&hƒÙ쌔˜h+72üXY ?c~`L`ÐЕÎõJªX¯Ð%ð¸l‰®Å_øpÙ·Pi/¤9%£®êÍ<~0Ð$÷âãΨé²sK`܈!g\ç L 'sgއù3'ötëŒkU…âáyg\ç ñC€<ÄÏXrO˜`QA`ÅSrÁÖ&öWÂ9rÒò6†j¥ ;ècYÒUÞ˜¶/ž´É&Fì«H÷è‹<=G!‹#hÓÐÊêú3^›š”–Ž=œÎ¸É˜@7K×lëv¥÷ÓÑ…y°`Ö¨8æñðí½$߉U¬,ÅêÈq»™`QJ@±Yçkr%> åþJÁv×,ÇY¼¨FÕ8|8žŸ çN ­¨hågÁšmû€ÂvSš“.öF"Ågå¦Ý`2`öÄQðÊûŸ …ènÜ7é•>ƒÓÍm¢¼üsáô±0qäPØ{¸Öm+‡ó§ƒŸ-]Ÿí>¾aÃo¾lT×5Á¸¢!PÛØï²ì'ÜyýÅPYÓãÑ"ôïeëaÞÌ 0rh6®ªƒ5[÷a›Ç€ 7ýëËÍLƒëçNÇgËàÜ)cÄžN#°ì¥ç”ˆ¶¥$YdÓ„åiLA>й~¶¶.ïúë«yËrfp¸â¼É€#X±~'ŒE¹œ5qTTž‚󦎲Iû‚‘ æeÁUL$ü°pï-—ÂÁãµðÞÆƒZ÷šÝðâxp¹kL€ 0 €Û¢.ïU@Y%óQqTpcÚŽ„®‚>YTFæŠb—oØ—Î)‘ˆ ÉbgÞ^ÛÊ+aæÄ‘pøD ÍN‡Œ”$T\rÀátŸ¡(ÑÃÕµðÜò 0c|¤%'%kJq!¼²òÓ.Š•M°˜Äýg—­ƒDÌ+ʧ˨¨e€Ýî„¿½¶ ¦·æ†¿½¾JX¬èü@åI˜2Ö³ìlÒèap•(ê íÕDi.*W›vTÀ;ëwˆ6еa¹™0|H6ü{ù:8^ÛÓÆÑå.ïøÏ &œh†ä‚m6`tAL..@åºL(ö¤L™ÑRÙ„ ÷1Tè›Ûm°lív(ÝsxPs‹×γ²¯#ËýbL€ .Ê’¢F•²dT G}°ŒöÉÇE–Üß: ¾Žk~¸ÃÕ7¶“ªSŠVRZ(Už¬n([ߨŠ×@ÃM`wVƒ’10qôP‘Ÿ6v8~U¿ n½ò+;$Þ9² ŽV׉2t1,G<ƒëá„U‰”¥û+åcâX›‡±,ííTUÛ$®@E);-¾rõh± äj(“|—<ïíHžI '^þÊ陵EîJu½Gá&Y£d6F÷šå4t²Á–¥Ð±äš˜`ƒž€î´Ï—t6-¸óˆMžGÃqö²ª:´½ˆ™6RJoBóB§D\'äÖ58›u©¿Žß0o&Ürù9°nkyŸDp}’]âN «Tª|hGek2ºÉÑz¦ýG«QÑ麞ɷ¬Ìç¡Rõç¡k“ëT–ä}Z÷4­K÷|ñÈÁˆw;x¬H»‡I£ÐÂÕMY£ç6í¬€«/šŸ_0K¬Ç¢kÇO57ª¯ß0î¼îb\‹2’.ûˆ#ñLóYå÷Ã\°WþÊi¯Dà†-”±’XNC;RÑ­‡¶¯\`L€ „™ºRu®WRh½’æ7ö«z².KOêûx^’ǵÉ㎗•l‚CõV°¢“œèYƒÑS?Ûlvxí£Í]¢Ø­õQšWÕb`…Zï£FtCÚÑÍíMÞüýWŠ,µCFÄó­K–ó=nÝwŽt¸ÙÉë¿}f™ÌB+*sϼ½V¸?‘+ Ltýwÿ~GžŠ:Ž,_/Î÷©†r´VÉ6ÈB` r£¢ö¹;,g¾ï’åz:GÔ—`ÄLñ|Oeøšú#§þÕÙR èz7kü±.Îér÷ø!²-ñß;Ëi訳e)t,¹&&À˜ð îeë•äà``¯+žªëqçŠ'&¡èI AZ"O0´u¸Ig=.|Ý‹îå ò2áŽë.‚úæVŒüÕw¨ä³ÕÕ½nÊû*JÝŸïé¼·:©©(õô\o׈c‚Ùyáщ5§À x¥€å4ðw…û‰ý•5@kó¾ó¥Ëáz´ÎFCb9 í(°e)´<¹6&À˜À %°ò‰Äá.—­¸@{QÑøÏÊ¢‡¢” ó¶LW•¸Q–¼Pœ¿p­’j0ˆc^’N´8DÀ‹Ù¸ÁwPŽ×ž†·Woñ½ÔkžÖ½úáf\#dïµL 7hOrÝ‹öDáÌmvÌ'¸’ž$™G{Û£©}’ñ DN#Ù‡×?*õ¾®ìÐ  ŸLnT´}-‘´·…¶§(Œ$#XNC?lY =S®‘ 0&0( ¸Ý޲ã8ÚPrKYT΀MñZ–Ðò7ʱ'k’A(I0bÀ#.B–ŠÖ%ü×¾¡© ×.Éê<ºÐu¨©ÕÚy¡YcB©(Ñ«èÝä¾͉¸54µCšé&Èõ2&Þœ'Ð9 ü-‘}‚éÞ,™‘j ËixHóåááʵ2&Àßáh±Y­TÅàU–ж7Ê’übOÊ)If³&ü¡ Y ­¯à8 ît¹`ÆÈ, 7n|‰³dxƒ÷ ÉŒå4ô2Àrz¦T#+Kááʵ2&À!Ÿàª!j•%C¦«-_ÂÆ¢è0Z¿ùf\ÕIÅMY8‰'%É‚nAT˜,¸±lf’r-.áîVwº¥G S|m/èK=ñjÇcó’axnºàJ|‰3ñæ8–ÓÀ™õõËi_t‚¿ÇÊRð ¹&À˜À '°ò±„Q¨~Œ hIœya§Ó”Ñ™öBMÎÅ"<š¶Ú7в&ö»9Ò½‰&ó ¨$%$&@"þ, —¤B¶É!¦SõQ±¾¢ßÀƒ´ö£¦®Qð™i†qÃ2OâJ|É2Ânxý–Óþqëé)–Óž¨„öx-O® 0&0( ¸uçÙq´Ö¬_°`uToJ‚®@ûq}A!µYÓ\äŠW!ÛËGrq¢I¼ÉdJRrrØÑ*b·ÛÑÌ ¹î60 ê\¨Á` ´(=9ÑŒ®efP9²›ØŠÂƒS41ZƒBk½Æe¡(/ R%ñ$哸J7¼X–—j;Ëipäi%–Óàò4+KÐâ²L€ 0&Ð# Ç=_n©„;ü¬êðrë±lt\¤ ú%Ô–Žðá+¢£]ÁµÂ3 UqsW´,áÚš”ädT”àp:9xBo[!ÑnƒÓNZ:Ôã}Ò“T…‚C`Èq<¶¤áF³n·ŽÊ’&%PãϵhP˜aô´THMMä”dÁ“¸_ŠäF¼9N€å4pfôËiÿ¸û+KÁäç™`LM_€.m"i`@e©sãÐhÄ#öZêh°®ÆObM.NàÖ+‘%ĉŠE±“{y\ l`D7³,7nX‹6@«f—® BEÉ@ê:´hÍŒ ’3)]=5Á€¤D¡¥§§B*Li¤0!OâJ|Ù/¸ÿ²YNýçÇrê?«p”de)T¹N&À˜À "°âQË]w—6œf7]5êÆmK¢š~ÙÞÑûDQ9ˆ›ˆxÔ!ïW{œÔ“»)JxÉ3AE7=#:ã¡uÉét£¼¥â qR¦:ô±§•‘ýK¼ˆ‹:€JÉdk¼’“…E‰¥Œôt‘'<Š2ÈV¥àLjå40†,§ñ eiV–BI“ëbL€ B:¸x»­À:å–%ѽi6Ö ÷»À» T\)K4R)²èfÐ1l8%ú:MëlÈ…Œ"äY­6ÏZ&T˜ÜšÇò¤QX-LƒAi¢É'%R’ˆ—A¥µ^h‘C6¤‰5JèzG%á†çµ*q`.XNÏ‘åôìŒÂ]‚•¥pæú™`qN·æw.p–­¬ŽþõJÓgÞ²yÛmR0ÐCéuÃ’f/«j§¡¢‰(Mþ)ɯÒ˜@„Çu76T–lø•%Z—ÜnmÐY—$²‘kǪä‰"H.w´æ+))QüÈýŽîWN¡#Àrzv–,§ggά,…“.×͘Ðm¾ì&Z.VGûz%j«²hµkó5y‡1;&EU\ŘßI÷â%É MðE-)”'e) ×ã¢$‚?8žà뚤UIºîÅ ß~t•¼J¤Š7£‘ö§¢}©Ì"캹ŽJ%)ItîÓÄžXr –ÓÞYJQ“ŒXN{gÎ;¬,…“.×͘ˆsï-¶»Žê&N!›®¸ÿ§Û`ᢘè5N@(|¸pÁÓ4•Žq¥,Ñ Ð$K†¸¦‰>å¥u‰,J òà±*¹…e‰Öq‘²$¦˜È~6’؈J.Y–ˆ Y—h%R*IA2 k“'ò[”ú ÚÇXN{‡ÄrÚ;›HÝae)R¤ù=L€ 08$àv¹æ{»Eë•”EÑÏÛXʈðá⊢k»Üгi‘A hÝ’P’0¨€A dļÁ¢,Ñ0{ØxÖs‘Âd@¥’”&‘G%J²‹3‘ˆÊîHÖ,§ÃCÊ%–ÓN&‘α²iâü>&À˜@\Ðçy»#ë•:ÛëcIR”Þëqš‘_ïiÒE.ed=¡€2 ž¯Ei0)KÞ/÷8)%62àƒ¼§âµÝb9í:4ă’”G:²œveî3V–ÂM˜ëgL€ Ä1 î0×üˆ¤ƒaM,¬Wò‡Qß ¸ÇPGãgz¯ÇyFNºhÂEÉWI’çâÆ ø#'¢ÔUÉet;&º(ǃåÔ#›rÐ$yÎÇð`e)üŒù L€ 0¸$ðî“ #5‡}¸èœ-WŒþý•|bÖŒ‰{K7ﵡº€«tFìý|AöÄ7OÔû– yž| †QŽý>²œÆþÆj8þe¬Ž·› 0&0Àt§s®O6ÄÂþJ>íñУÅÔ¡Õí4Ö%_œgL€ 0Þ °²Ô;¾Ã˜`}ðY¯¤(kû,­7}‹lšâVYY’0øÈ˜`‚+K,L€ 0&Ð?ºîµ,)º›Ê([;;¯³²Ô ƒsL€ 0&€XYb1`L€ 0€ ¼÷dÒP ìP,TÀ:bäøÍW|•¥YQÑ$n`L€ D V–¢f(¸!L€ 0Ø! 9í^«¶ú“’[ʱÓúΖ&&dîÆ…ã²í£KoÞy—sL€ 0&0Ø p4¼Á.Ü&À˜@èúEò1”uQNœNX¼sž®)÷`à„RР4Ý’°õÓïm–e£íX²¤ÌQzmÞnl×LìÚÈÚi¿¥ÕÑÖNn`L€ V–†;¿• 0&Ót•¥Ž –tU]/÷W2iPãýÔ;öb8î6:¬3'<¾³Ë–*ŠZª¨Jibò¤O¶Ü­8£­[ò¬WR@£uK«£§mÜ&À˜Hì†7ôùÝL€ 0$°âYi¨üLMWÀ”˜¹IvcשûÐÒô© êúò¦^U¾pj¾Ád¸wü|¬\Í­=dsí±ÈòQqÔ}ƒ<(¼n)*…Á˜ˆlYŠŽqàV0&Àb†€âh>Jâc›°cÁ½µ­¾WL†Ÿ+n=S^+û~I%æé÷†¼UG£¾\ÞqDüsj¶½­u 5'™¶œä©ÛÐóîŒ~”ÝWRƨ¿ ¬FgBR–ðàžãRYÒÑ|ætºàãÍ{aÕæ2hj³ãxêä¶C¢£ÌnTÉa0Ãas*´,ðöê­žlKÏ—Ì™Fã^¥ƒŠÍ@u¶G9E3u‚b‹ÒF°TÓì½.°ÀA=lz¢GNSPNÏa9 ÷€°²nÂ\?`L N8líÞ(xØ¥í³ïÞW&TÖ Õìû4\8Q›'ÃæíM>q,aEziÅF8Ýfƒá-•0³¡ŠZƒÅ5ø&Ÿ^8»Ñ•©£ "k<¼ñ±V}¶¾ríEP2¦°{Q>_9ýï»ÐÚi‡|cãvhùÆr0+¶0½9vªuè PãÇmÓh¯‘Â8öô>ñ«NŠÀŸ_ù¾pé¸ìÜ’AË%Rï.§i†SpQÒ2È1Tbâ=¤07í¿:רÕ~=ËiGŽ•¥0Â媙`ñD¿øz-KŠP–<½[¤(Ú„Çwâ§wešd¾àvºžðø®t ”°tµÔ`P¶hfu}Çš¦¨E2ñÍõ¸9ínT§ ÂdÅEë–>ˆÚûÑ0ù•Þår EiéÚ0º±æW~F-® ƒ~Ь)’7–¿ «‹.ƒ×>¤guty*AMZ®t ¬>.Ý;žä´ÀX3ߣÂrÚ;9Šäņ¿Á6ëM,§} â‡?ʘTt˜#ûkTŒhYòMÊÿá\27¤ýò¾…SÇ'§&E} '˜-nM»M±9/ö-ÅùÕ²mª[‰iW<9¥õI;T²uEé²#ï²¢$ù,GR(‰)˜¯TŠ.ŒÇÉEó,Oñí@ô$§¤(ÍIz…%?A’BI¼ˆË©ŸÐ(ÆÊR°¸(`L`°øèñ„8EÌíèÿé˰QoÚ{ÿä7Á ®•¶Ü=¦©üþɫʘòXùÂ)·î]8•Üô¢>)uµl$ZÆæË|,É¥‰¥Ö6+¼öþgÂõŽ,Jœ'@Ü2­ ðŸåÀ1ˆ¢N*ð'ºÈéŸAšZ#,J×ÄO%.M=ÿyg=ËiÅ•¥Â䪘`ñJÀ¡;½µ¢Ò6tEêòyÎ÷ÝWòßXï¿®+kÑÉÊÓ7E™sìæÂÄXìM@ÉõÎîpÀêÒ½ÐduÀ¸F‰]ïú7šÄíÂc«DP Š"H|9Oà 9mwÀËr¶(õ-Y˜¦X–Ái ŠÁrÚOˆ=<ÆÊRPø`L€ t%€û&y•%\›´µëÝø9Àuº¢”QÐ=È\ku^k½#·& æà@EÉjµÁ'»ЍwÌ!¸‘$~=­_vÇ Žçrºû ˆzÇÁ‚ãJü(zàªR–ÓàHv>ÍÊR' Î1&À˜@/0¬¶WYÂàq«,Q÷ñÆÕÚæË|¬=_ë]ªtèX ´Ú]PŒáÁ9O€86£õãȉZ¶.‰ó 9µ¹ #¼q žqlnc9 ž¤§ކ*’\`L Ž  _šWY݈ʒ;n{«²íJßñt0¶ö[ò~­w:Áf³CùÑ“bÃYÚG)Ò)iÌD˜ð»Åku§ì5'àä›ÏBÃÚýnJê”s ­¢ 4k[¿ëæAâHø–:#†å€ªª¯@{”Sôì¥}”"Í»}$¦ ÇWë`o­šÃÂÞµ¿½Ÿ#³‡_M5;ÀåhˆîŽä)ÍrülY G®… 0&·Þ{2i(Î!†Pq=OëçüñÙÙ í~ÆÿwM|rÏÌY×Mò^¬ ‰€Ê’Lʹ±´nÉ3 uc`§pÁ;ÝÒ‰.Û€n8{øÉCÅï~ÖÊ (úæO@ML’p:*F3Œyàq0gåô\( Ó>LIȳ¾±]ñÜìŠ×O¸=É©ÚtÃÙã{^M¯^G¶ÿЦ|òG_Ù¯Þ©3̸úi°$‹ÿeö«Ž`¢}˜+Ëi° ;žgËRˆ@r5L€ 0x% ¹íÓ}ú¶]QyW·ŒÊ š[½#ǧ¹Ü÷¶µì3þñ]{P¹*ůq[ŒŠ¾aשû|žúìÌ%'kq¿¥=اI8©3Ÿ²9ÏÇFõ Ç’kMâ)b›Ín‡–v$9æë¶äe?Y í‡öõèȼà È<÷¨_½s~õÅ9•mØøœxá »>gä,¸ê>| ï|š·m‚đŨh%ÃØŸ?ºÛ »¿}|ED‰È³±µ]p&Þd]âžä4Am ¬’—vXë…5ˆ,By£/‡‚‰_„“ËA5&À„ ‚!c®ÛUWXå~šÛ!ÊN¼Ž•½çþj®´œ `4§À97¾šî„5ÿ˜e¥‰å4D2ÂÊRˆ@r5L€ 0¸% éSdßtUÙ)ƒÅѵ²”lÇýD:ñ±ÄÓÊééàVfcœ¹.]…7ê¸C}56v5X×Ä~K1¢,éàêP–ìv81"ž'uÑ’FOÍpw¸Ð »íÛuÑ•pòµ‰ëC¾ø ÐÐ]¯êÅ?‚Áœ©SÏ…ÄQã¡aý{ÐXº’…”‰3áÄþŽÚ“Ö%âiw¸gMërÀÚk/&nÝåÔÑ!§dJÉuÇÖ ¬ãÎ{†¿~öGq>æœïƒ­‹û7> cä] iy%PUþœ:ü´5L„ÌaçBù¦‡ÁÚ|bÀ†ÆèúÊrþ¬,…#W˜ˆ_8ô*K Á.ÙÓÔ,Qœ[ FBÎü넲DTKT=õ'hØðž¸¯èƒfûÁ½`;ù5X¢$gÉCçûÞæ|$3²€úÊé@ÛçòF^ )™cQÑ™ƒJPÙöwÑ ²0Uí_ ‡·?-Γ²Š¡pâ-BY¢ dy*ßð0Tï[Üǯ+âØT³ÚNwÙŽÎs?‚YNC›•¥ÐpäZ˜`ñK@ï´,)ŠŠÊ’g2€¡©sQ“¸ lÎYãß©ãtq ®).UTC©!ѽu×·§žŽU(&³¾#o‹„ÑÿÎ=|ÇÈ„Qÿ>b‹öþH÷&ÏDƒp 5àIQ¡e×gpüÙÇ¡ýàS\dHM[¥wùX샜Ë>´6I¦¦mëe6ªŽrJ¼9N :åT[k5ìúè~¨«\ZÈÊdJÈ€Öú½ÞN¶Ôî†á%·‰{òbí‘e6ªŽ,§¡V–BÑkaL€ Ä%U«æ­›×LsnSzênT¶°„f¾_¤ŽÏz¼<§ œ³tEŸ¥éÚ7ÝípÎøÇvi&ì¾ʱXƒ3íÍšS¸ni/Új&âÏRWg;û°:šû!¿ØÓXÑ>@~¹U ª_ù«X³äËŽÖ%9ëOeèïeË"ÊÝ“I“«¼-G„Lœ%s¶,ù?0’Y´Éé)Œ€·ãïºt„Ö%ÙZOBRÆïõ¤ŒÑ"ÊÝ“ ×uÊltYNC2mõ I'¸&À˜ë–ãðß[KGí•—ß}ºÇUØ[Ž¯Û·pòJœoTtMJ*†®ý(gæäêð´,µªbÝ’x†‹¾"o úÞ‰(9¢ÕƒÎ£55mÛç-€Ô’Ùø›%ò¥>»5ÜV]I, uÒLtëKGÿ§›ÂUɺ[3ùÔ’ªœQ/§µGWap‡ÏAVáùUpžÈÓÚ¤ÞR[ãa Ëž+¬R€–ÕJ,§¡!?p#šös-L€ 0&Fºæž(«ÇehU:3ÑÚ¥ OìZˆáÃËÑïQtÕ[eLTGí[8õîÕ ×™OÄÆUS<‹eDsõ«c£Õ“øhV”ˆå±gƒöнPüÓ?ÂØ_ülÇÀñçß+fwKœZ¹ ¿¶¦ýë0`d¼LÑÎw Ùøón¯ÂÅ =õcC#®?š}ýópÎM¯@+®CÚ·þ×½vÑik„£»žÑñ.ùÆ6¯×¸ÁrE8CزCƒÅMeL€ DšZ“Šå;Ñjä íT²x÷—ÝšûCœ X ªáÊ¡³&?ž%êó´jÚð°Vö·zjëR¬~A¦5J¾Š’äÝÛQ³[£JQŠUî½ñ ÷õXåEk”Q|ÜN”SÅ*Ü\ÏV¬r?[¿"qŸ-K‘ Ìï`L€ Ä(´ªŒ•MÇÍ7QYòDÿRÔ«þ?ø…®y²zË®‰è†wËnÅPªhJéž…Ëpá»×0%뉥#Fø[¸œÚŒGR–<ñƒ£´4!êüEi#ã¤Y’sœt'¢Ýì<Lj¾zнL²ta‡Ù²B˜\`L Þ ‚àµ,™ Fç™6žuYµo½¿ä•} §,Ü÷À”ù·Ý?e¨ àNUQ>ÁuNÓ4Åõ‡[–@Ìÿ£*¦w:ÇT¿¬ìæ’θÖ78ǘ`qJ€-Kq:°Ü-&À˜@°Jÿ>,éTSõ02 ¡;žóÒ‚ëŽ,«â¢èx_~é‰Ý_¿x×·Ëï›RºHQÈä´¯ã÷"¡œþÄxš±üøÒkó*Ð`SŒ¿{{ÝÜï7Ÿ 0&À ó_ýè+eL€ 0Ô´ÕA=Ñ„ Úå–%`ûF5–/œr1Z‘þŒ*ÒRÜOé¯Sþº3“îÅgRVÈ~u¸âÉS>2&À˜@œ`e)Θ»Ç˜è/Õíöºà¡Æ$\ð|ëÚ{ÿäç“S“&b€(§³])›ðøîÛÑ?^(W¾åb=¯èªWY‹®‰õþpû™`LÀ¬,ùÏŠK2&ÀT|¼ÁPòFÂó…°åî1MûîŸò}£>‡ö§Û&<¾kÍ„Å{§ø–‰õ|ÚèäÕ (Ò¸km’'ËCB@5@êä90ô‹wAæù—…¤Ê³U¢&òžßïJ@Q ]xŸó¿0¤82ßLŒæÔ®à³!ÀÊÒ€`ç—2&Àb€®Ó‘­DëQËY&?±kxÉce—`¼»Ü.ø º¨B¥b,ÆË}X>DZª°ƒ¢¿+û2¶¾¾k6Ó| @IDAT†}¨pïG½é†ÇŸ¾wý¿ä” ‡c°»}6YW”70&÷©Ððæ#9›à¢Ê>$7Ófœ·Ý M[×÷»Çé³.†æÝ[àØ?×ï:ü}0eÂt¨~ýŸÐ¼m#dÏ»V(…õëßWc½¿Up¹$3bŒ;ÿ‡P{äã~·>wä¥P|ìYóP¿ëðçA]wƒ½¥¶¾óuh­/‡ÂI·bÛ„*Ü×ÚrÜŸ*¸Lˆ °²b \`L ŽŒ”}q ¸‡’ tU9©ê†/åe&\}ç¨Î­ìeÁ8=f¤¥¾ÓÔÔâ@‹š9Ý~:5ÝÞHã>¥Ï™¹WÜ M[ÖBþ ·ƒÖÞ µ+—@íû¯AÊøiPôÍŸ€1-&ÿu4¡rìé‡!¡`ÝýSH5ìÕ•P‰JPÛþª “ÿü6zü(øÊ÷Á2´ê?z HY¢Duz⇘ӡðŽ…4bØðùúUoCí{¯Š2ª% ¾úÈ/žñýsô©_yOk?x †Þz$ M›×x¯s&öä¾Ц|j£fÝ.{ Tîz/BæÐÙP2ÿa´eÁ¼;6¢Â´ö¬þ $gÃäK~i¹“¡­ñ0^û)4žÜŠß€TaÍÙ¶â.ÁO±Ü(8¾û%Èy™Cul÷[b?³‰sAZö$ñü‰½¯ÀÑω2ª1&\ôs2úJ,§AUÅR(_÷k ë/^CÆ\n·ŽîxoýÛÀm}Ê{­®r° -)-ÞËœ‰ V–"›_Řˆ):äËö¦»R«ì°÷“ðÕ{äArûŸŠæÍ׿}„Ý½Šº<¶!¾]ñä°̉6ý|0$§Â‰çCÚÌ‹¡ðÎ…P¿î]°×UCë¾mµdè.'´lÒ,ÅÕƒ1 rпɒûÖý ]î.…‰s UåoA;ZcNWéyÓ`ç÷ÓÖ ú>ùÒG1ß›ßú¡hMšÿØø²øÏS aÖµÿÆçJ¡|ýoP:¹£.—£*>û=´>Fs ÔT¼ƒ–¦ŸAþÈËaÂÅ¿€ø>—½IX±†¿oy Ú›+As{¾+MþTº.þ/X’ò`ò¥AÍÁw¡½éèã‘1d¤æL‚‰_‚j´*µÔí;£ _ˆ 52¯á·0&À˜@,Xõ—\üŒ žUð X/úaÝ ÿ¦‰®†oÈ1,j:•ë–°¸'–ü c8p½F°©úå§àô¦ êå¿¢§¥ 2Ìgý)pÔ×€f·AëÞ­`;~ ©é2v Ô}ô&¸š ~õ2H5ÌyÞ&•éðâáôÆ÷Áz¸\mÍàj>-êЬm¢ÞSï¼$F´h)#¤M™#žÏš{54lúªßø4lx?[%®§áº)*k;qZ˶àñ0džw©÷Ý3¦ì|¡ôÕ,}œ µÝo÷yN<ÍFƒ—sŸ…ùf:e”dÕ#§.^Nlz NV¬€Ÿ<†"džöÖ“`m­·Ë §«>…Ö†`JÈ€Ì!3áøžÿ‚ÃZ'ö-A S $¦ ÷¶—¬LdAª>° škËÀéh‡­^Ôár´‚ ë=²ý¡èœ:ò!¾Ï9Ã/ÏŒ¿ Û±–þI(:5WŠë9¨ÄÕ õ‹”­†Ÿà±­LW{ßé›!kXÉ´ˆ%eaû^÷½åWÞ–S¿H½[–ÎΈK0&Àk[³×ª¤èP3èôÐa“–:ðwÜ{JͶžJßzóÜ™KN6Óî¡ÞP]¢ h÷d1¨Ðd"½7¸ÔvÈc}q5y¬GKÏÑäÒP‰"w»‘÷.ò¾ÐÊPâˆbp %Š­ê+‘[Þ›¾ CG€íä1QT1Z@1Y„ËŸõÐÞ.“2•:i–p ̺ð ï=KÁHoÞ7£&&Cñÿmå;…òç{ÏŸ|;òÌOèœØ÷ÄÝŸzk™žx‘œžÖÒƒFÒx Ý=19¬ë‘јÜcÕŽÜí¦^¶Ø{Ÿ,B©9p]Ð q­î¨G÷è–Éu)Œžý]HÎ íGÄ]Õ`ú™0Dó©]]ž e*»àù{“•%Iq9&À˜À "`0¸ó].O‡qç$V–Å´7kNm¾&f+sñ§h6ý<þQ›M8‰ë$ì¨lX\—µþ4V³ù·<ÍmmÕ“ ^ûÁ®J )Q”4Gí@…Üüšw|å?û:*G™0õé÷Ås:ºÜ‘Ë<¤HœË?:†bÔQ‹=õþ¨zñòrGR¸(òž«¹ÿ᧸< c8ˆ£y¦% Š%k ®¨S4d;ô0+þÉZOoÔÐzäOr9<;|†.xÝ•R¢(¹]}µCA·»EPW¹>yí&´þdÃ%_Û"žÓp-’†Ï&¥çò®¹ðºŽî~öoxX^îóè´5±Ýÿ…ñ>ÙÃ/•%âhGž,§}âõû&»áùŠ 2&ÀMÓóeoÑ^ÁÊR ü*îuÅÓ5ý ’Q4é˽üe%›0\‚•©]'n¡j¯ýÄábGnmä‚׊Ör©rÓ×Åu È6¿âû™H™1¥gƒ ]ãèY *á›Zv~*ÜëÒ¦ž‰Ec!cÎ|q»qójȾè*±_YšèžeH§K•(„û9¾ÿ°äâš§ç!iôH7,ÃFø¾¢Ï²Ë³æä\6þó`IÎKJ>Z¯îïh©ómq¤ýÁYN» í÷ +KýFÇ2&Àâ—€¯²ºÂÊRÇP[ †7Py”æˆËJ¯Uk—¼PœÐ«hÉIK4YÕ¡"+<“ÐÆÒµ`­¬{úÞÿ­9:ôøƒ8| îc´¦¿¸£Ô=€v8¤æGÒ6¨yû9¡$M{æ#|L…öŠ2ï“Çž}ׂâŸý&>ñ2¤Ÿ{‰¸wü¹ÅÐŽîycöW˜ñò§0îWOƒ8ßdÉ*"óÄóãó,ÐoØ­ßö-Ögž8&šTÈËHñ*¤}>À7{$ГœšT Ž;§õX>Ø‹5‡?€–ú}0ÿÎOaÚÄ@ ­°mÅÝ’=#ßm€+îÙ0  ª_¯"Ëo=ó¸ô;„K_S ;ÒÞµ¿kšfßø¸ðÞ‡|Œ~Gißú_ KÖœÿ WÞ{ÎýÂk:´ã)ÏöY1õN˜÷Õõ°àŽOÅþPû7>,Ö>u)ØÇ qL4³œö( [ì†..̘pn›'=”Ø ¯ş¼]ulåK𳬵´ÀˆáÔh勵u–¸œwŠó=®Q qÌM6Â1­ª“‡Áжª³6p÷½×{ËPúɤ£ÑÖ›gÉS±?QùOnCJ:F´óÄ¡`eßû++©øu× 7«_{N¾ý<ª¤šˆV'ËÑÑQ[å?½Ô„DøÁÝæy'Y³>zŸ>Aï‘ù¤wT@‘ñzz¿oý}å‰ß±Ô"˜1ÔÃÙšç<ÁèE‹£ŸLšÛïý©Ó]ÓÑ^›^½^X•œöfQŒ‚=¬{aFµKçéN$”ßg=Jßú²ÌŠcÅæ?À! ûM¡ÁuÍÙåí‡ôÉ’Ñ"”$?¸:ÞIk©hÿ$ >a²¤¢uë4>'¿½xª €›–àƒø¡À€/·ÓãÚÚå}œ¿W1ÌÍrÚ¦€n±e) \\˜ 0&08 ‚äý,“¶,ù ûáô1§ä)ºdÝ*óÑp$k’A(I0bÄ6£ÑÃRѺ¤h°~ø|pá$-ÉÝÚ„‹‘´.U“’äU”ºÜ9û ­O¢°Þ½%Íf©(ù–¡gHq t-’o=å‰Û†á ѨÀ¤¹^ÆÄ›Sàz•S´.í´].=yUØ,Lg¶(¶¯E‰x·â¼dž›.¸_âL¼Cœ:äAøfQ>RÈÛÔ·œºá„«6·)l¦h€Ê6E‰x·±á–SüB„m÷ý…²+QY¯YŠÊaáF1&Àœ€w#ƒªt,:ð6EMgŒ©Ím;™%¤Ãmx|²óN†Ýº»ÝætÒyP³oéÞD“ùT’ Vkä%9ÑSÎ!&þ¸)ç…ÇÖøµ†©ýŠ‹Gh¹.’End¦Æ Ë<‰+ñ%ËH¨Üðl§îrõ÷Ú+#õŒ÷»pw×`eÕ9=áœ-í90ղܯ5LÄgÀ_Kk”ÈõŽ,J‘S·ÛÝ}Õ22àPBÜV–B ”«cL€ Ä Îdt¥q⤷vcÖ¤Ús«6ŽÒuÝŒæ¥9Û?_0nú›'öX·¸ætŸjlj£IGPʹ8Ñ$Þd2 %)99 ì6;Øívpâzž\w˜Àõ ËÆ~†·TBqC9µj&oGbip™ ÐjÎDCYð ÀYÙ­…—Éœ;LfÃ~”ºõØ€C—©º ±¥&„‰”ÍôôTHC…)&äI\‰o¨\ðhÌQv”cÊ·c3H›•Ñ@e)\¼|Û£Q; FùR°²Úo9Åõ96 1rJ!ÁeJL-”Ù°}åÔˆrš4€rzüPÅfì°¯L„½ÿýV–zøýL€ 0è$ÐiÞÐ ¬,õ0FZAá»Ê‰ãµ:è¹8Ù-ÚzýXìãŠövÉ;áX½ü]ÅS§×o.;”…ÊRPÚŠ÷«=Nê5Œ@AŠ’ÜrÈ3AE7=#N8í6T¤ð[µË©š.”)ìGom ëõk_òÖ_ð•ïyó‘È/â"‚ d2q½W*F‰Â¢DŠRFzºÈ“ E •U‰ú‡c®knwó®Í›*ðÔÝñóÊ•ÀäÛÑ6jç9—^Ù„íN FVcQNË·=ãŠÂ ~ìÍG" rªkîCýÑ^ì/É‚¯lDÁ€½ƒ•¥CÏ/fL€ D5Ù:·Ål“y>v˜ý-ÎÒkó_Â)ƒ˜Ý£¢q;Þ DY¢ÊÈ’ &¡uÕU/íV”ï¨< c‹†Ð½~'©YhIU2ÍiÐBˆ*­³!2ŠgµÚµ·hLñØþ´`e5–ä4%±ã`“SÝínZ·ì­·:d šd´sP”ce)L`¹Z&À˜@Œð~C½ê»ð½ÎÉlŒ÷+¤ÍŸ½¬jëæków£)f2ºã%Û¬õ7ã ž à%ôÕž&ކ†S-åÛ·<ªÌœóðóËÖë߸i^PÐ¥"@|‘GK åIYJJLŠ’þàpx‚?t¬k’V¥Hzä5š;Üœìðõ¯h‡QÉ«D¢••%ÚŸŠö¥2‹°ëd]¢`¤$ÑuºO{bŠDc\UÛ[7¬~¾½½…lrw¥ŸT˜¢É²$dT¶‘ÚKíVç]ö`e5–ä4= t¤Á&§;?ÝðTKK£”Óh“Q9,a9²²¬\)`L v ¬øc±E·3BœÚq2C_—9õB#É=‹€ž Û¨0Ý…‡@•%újO“Ûºw—®OÍÌ|ó߀7@ÿêu)ÁX˜h"*C\ÓD_†'…€,J òà±*¹…e Û/\ð¤Â„íˆHj°t*KùCr#òNb#~ýŽ,KƬK´‡)•¤ ™„µÉù.”%R0J÷VîÙùæ¶õk¶a‡­?ry%Y ™ˆ&eÉ+£í´R»3sóÈÒpS°²+rš™Š½íHƒINîßûÒg«>ø»Nr2*‡%,GV–‚•+eL€ Ä.4k£¹I6_‡ÁGZöÝߣb|^÷è`˜ñw^é ùSf¿]³ËÏÇI¥‰(q¦‰HÛŠÿ>÷ÒçnýôïóW«ëOë·^yžŠ5L4!•A hÝ’P’0”8€A <¡Å#¿f)ÝÔ©,egeb×߈%µÈ³ž‹&*•¤4‰<*Q¡´&Ñr½#‹ÒÁ²oüÖ’÷° mø£/öt$ Y ™ˆ–gÈ(¶Ö4Z>~Û¯ Þõ¡UÉ:Zå4Íg9Ý`‘ÓÃûÊ^ýðõ—I)ŽfÅæ…/±²>¶\3`L & 8óRœpª^´£HûxéÇdwÂÞhtÅ«CW¼7ðE·ÒËpë².}—ò~$9–%,OË{/¿ðÊœ—Wkç\pÏ/¼—1¹¸PŸS2Z™R\Ðï}˜ä×{š’KYO( )HdI’?j3å#™’}8|‰ßÌäº%Z¯BÖÜé ›W}°~ÏgŸî»àªk®ÃÐÒ—ãD;'’zj¢EÏHOVL>Qzi—?—»+FÝÏý©#Ø2ó ë€þöúš`«óûyš˜û¦îç¾÷ÍÛœN½±©M§ \‘©âv»ZŽìÛ³vÓÊ«¬Ö69ù$#.ýšñGcO2@냢Å›"R2Šw„I¿­û·o=pþ•W/À~ÎEYM µ¬v—Ëîçí ëa’Ϭ9žåôèþ}ozï÷ÛÛ[h¬ÈhØÆÞgØÃö®˜ 0&Àbˆ€¢,ÒV<¢¸ðs²ø7®5ѲfV–úÃÙKO®*½6ïdÆâ/£¦Ýu ®G|où~¹§É'…Á³ø¶¶fíƒ×^zÏß™0}Ö¸¢±ã''&¥ä˜ÒFn<;Èú¶.DW<³]‡»ñ‘_属nÓä™&1t$«ƒ\CF ’ü‘‚k–°¾É·Þ÷Jìåiì(Éq” “œÊsyßS:¶þʶF¥‘Šu9 2’ÿ¢XY F®„ 0&gôÇïø§TQ•¥†C&ÿ ¥ Å#|«ô®YÏþDz šhhR-'£T‡\0/­IÒ¢$¾#ËSЃXOr"JGù£q”?y-ú9e”Æ-Öåt°ÈhÐÿ±²4B®€ 0&]iÀ@¢clY l|gΞøú–Ò='0„x U'nÞ¬–.¥åÄš&Ú”¤¢ä9‹/<ê­ç‰‡$'£Ô9†ñЯžú û7Xd”ăœ&íInýºÆÊ’_˜¸`L`PÐ ¯ãŸQÜ·3mõ>¨î*‹V»J¯Íÿ Îë©H-M/Ð&AUÜù°œ˜v^‰¯Y*8Å6x—Q–ÓØ–Q¿[/}ý~€ 2&À˜À  Cµì%YHdžþ0ŸBûퟃ:“>yË C¯öïI.Ř`ÑD€•¥h n `L J ¤R6 "E2ÏGÿÌxëH£B›Ôv$Ý­=(ó|dL€ 0Ø!ÀÊR쌷” 0&1ºA=&_†þ4Ãežþ0êÆ'qq‘ ì0·ô†üsýšK2&À˜@4`e)FÛÀ˜ˆ2F—©Ó²¤[–ú1>Ó—?ýÇû¨~äÍs† 0&Àb‚+K11LÜH&À˜@d \þàƒ'1°ƒ°Š e)wÕ³#"Û‚øx›jTCë-v§µK7l»±`z|ôŒ{Á˜XYã̽dL€ D@QQ`¯u©ýtÕØ€*à‚ÀÌ·OîÁ@¯Ó jLŠÛåú9£aL€ 0Ø!ÀÊR쌷” 0&i{ä —{’Ìó10&]ù¥u鯭×L ¬.͘`E€•¥"ÏïeL€ D;Eñ*Khae©Ÿã5íšÝ¾Ö% \¿ègUü`L€ D˜+KίcL€ Ä ÝGYÒYY fܺ[—¶_?lF0õñ³L€ 0&¬,E†3¿… 0&st]õ±,ÁĘë@5XX—@yšDk—\šëwQÔ•ÍÐÝÚ¹2ÏÇ~0'üBQ”vzZ}Ö¶k‡|©Ÿ5ñcL€ 0&¬,E2¿‚ 0&«pbÿ‰·í °²ä…Ñ¿Ìì7ŽV+:<)Ÿv+ðÛÃwð†¿’™`ÑF€•¥hn`L Š(µÓ²¤ë¬,…`lÒ2RE%´NT¥ë£êë¬ CP-WÁ˜`a ÀÊR r•L€ 0x!påðw㺥¶Žþ­|"qx¼ôm ú1ö?ͺ¢?äóþïºasõÂY&À˜@´`e)ZF‚ÛÁ˜ˆBÊ-Kܸ¸f½lšÛí¸\æùسg}ûiŒŒ·jÀ I6·û±þׯO2&À˜@¸°².²\/`L n(x»¢ë¬,yaô?£,Z¤jü®·]ÿÒæk‡Ì÷žs† 0&À¢‚+KQ1 Ü&À˜@ôPT£WYB÷±Ë8ÜuhÆjÎÒª ¸?틵i;ðÝbKç9ç˜`L`  °²4Ð#ÀïgL€ D9«pìÄ&Öˆfê³ò1óŒ(orÌ4O±$<ˆ-5u°ßt¸Åw-SÌôƒÊ˜ˆW¬,ÅëÈr¿˜`¡$ tºâéà¾:”Uæº(”¸®Â½ tý‡;®ÉŸì=ç `L€ (V–?¿œ 0&P–Ë–j Aæù<ÙKkþ×QM:€É©ÀÓú¢Eüïsðh¹&À˜@ÐøÆA#ä ˜`ñO 19ët³‰žê0ý½Å–âøïudzˆ{.é&ÕøMTHíôF\vÞ–-O=™·ó[˜`L /¬,õE‡ï1&À˜€ °àÞÚV]•‡æt²uIÂÁqÚÒªrTFå­J‡_n»±`º÷œ3L€ 0&0 ŒòV~)`L€ ÄUQ_ÓtíÑpEÿ"‰¹NDqƒg&Í}¤ÔºöZ4-Ö%³Ëåzñð#gú÷E/Lm¿çgÏr»]ŸE)Âh‡ÃPiK Ó«z­vYË ï½»*Xí=‰PÝ[]©Â×5¨†•OýúÇ["ôj~Ÿ¢AN×·ñ¶ö®‡F®öžD(Ãr!ÐÝ^ÃÊR7 |ʘ`=0¦¥-s46:ð®Y×aöûZÆ^ñ ý@Ï¥ùj ”%KÜ¥7ÿà²oG¾)¨4•Ô×Y‡õüo u¥¼òƒ,NhNhý®A…ÿui®¡¸fJOp¶iÉ®6Åìv ¨×ÉЖóÎÒþßvÌZ›1Y·™’Täñ›»~ú«jÔŦ‚?/ZtgX•Õw&~*ìQNÍJ«–¨6+F°¨œf«G".§.°hV-MwèÉ,§”sü€Ä‰ 0&À˜€V<ª¼ùÏSiEU¹úíGþ=?¥îzè׫©7ÿø¿ŸÍ§c¨SéuùßÐ5ýiY¯ªÀM³–ŸzSžq¤óÕ;xèJ³Éô´®¨ÃŠZ*õ1 åJQËa°¸Ä’© ªýGíF T¦Ž‚ƒYãõÊÔ"Eý„®(_ñ~?ö{3=è”S Ê©®Ë3UèÃ;”|c9˜Ö]zÔ¸ÆÃ1×4ý”³ÿWŒr ±)§áþÿi(¤~@µòPt€ë`L€ 0È@W¼É·¡«Øí«VÍg $DÇÙËjþ‰AÞ’Õ¡rú,ZœÆÈó~éß{ã×~ôóûLf˲L{Cþu^‡Ï|[{z+JPIa$Ä…ø¤[†èº²'t?è'w~,0]ä4U­Í¿(é_pAâóÊpÓV”:X’ÂH<ˆ ñIQjYN“³€J³²..̘ܮqÓ{ €g‰Cì¥ë®ÜDÂÓ{ƒ1ñN´Ý¢ÚqBºî´¿†ë—úñ6úJoÀŸéŽºÏ`0=:¦±B¹qÿk†¡m´D‡SoˆÏç,1Œi< ¢Âº¸Ca"žœBOà 9-4•)s“þaÈ1 ýÛâ¨Fâ37ùï†BÓn–Ó0++KaËÕ2&Àâ‘€rË7†¸~Nö >|]æù:3Þ:Òh20ˆ†×çhz}}ûß|M@éßyÓmß¹ÿ*“Éò[T”ôK¼«5g€U Îâĉx7´0=öõ-ºIWN¡#p†œ¢¢¤ÏN|E1*,§þ`&NÄ‹¸±œúC,°2¬,Æ‹K3&À˜˜ž¡€ÿ\½ò±„Q %ô¦/­Ú†ë•¾'kÖ5¸×3²FL(J“'Ÿ›žššúT–­NŸWù!Oô%ÐŽÄ-ÓZUù×]w-J àQ.zvršžúTš¡FŸžðËéÙ¹Q‚¸¥©5`0²œž'ˆ ¬,eL€ FW?h?ˆZ’gÁ»—æu´¶ÁˆµÇ>ÏZ^ó4®_ú§÷¦¦ÿvËõCoòž÷ž¡ßi=™eê%óîÕUà ­1°E©w`}Ý!n_m  î,å»X–çO}óÿ^W9é–å8×g‹’ÿ;K·© Ë ƒå´“K°9þ=X‚ü<`L`PUÃã²ÛŠ¢}Ý_Ó3å9CL`Xá·ÑõqÕŠ–Šóè}Àá[L€ 0&Ðõ yWý»e¯–%ƒSL}§ò´j0_ƒ.yu¢z]ÏÖÜÚ;®ËÕíu¾_ë²òò/Å :í£Ä)xÄ‘xš,f ôÀÖ¥þ#íQNi%NÁ Ž,§Ás”5°²$Ið‘ 0&À"—>ý%´.‰øÓh]vôèÞ»ª€ D`ÖÒ㊮~j¦‰¹SÓ?ØqSA¡OErjÂk‰ªÑX”àl×yÃYBAd‰#òÔ°Š‘øce©ÿ,ÏS³Ò¦ó†³ýêû$qDž,§¾P‚ȳ²<~” 0&0˜ ̾{‹wÔË@Ó²qq!G ó }fÖ;Õ[ Ãu (Vª×0q8\k},Lôïº\¯”`0󒜭41å"ÈSUŠÕgžGõërš 4±œöeO!O–ÓÉ~‘ÿ#œ?Á˜`”„ÑÃÎq:ʼnûÐFWÕ½ '¼f.­^‹±¾ˆ.yñ&]åÔõu;®6ÏéßuŠ‚G.‘ƒ¢¦X4'OBÆÿoïLà¤(®?þºgvfï]öà>åTP@@%€"§‰&111F‰ñ£áÒ†FaDh45Þ‘åryp¹–{X`Ù{çîþ¿×ÐÃìÉÞ;;ó«Ï§·{ª«ª«¾]»[¿yU¯ê+O•”8.O8cU;°åúi„âF?­Ë s OôÓ ÑÔ8¿ä5F†  & ÷g¸U™g~æµK36ÌO‘$BôÉÉtEUoô[˜tjçÒ|ë–K½Š+C0ñT= @à=p?®æ4<0®cáeòC?­»¥F?­®JC,UŠ7@@ªC 5¾ÿ«œî ‘V§”|:;¥:ù¦n||b…EQÆóº±¢sìõ–qвêÕ!I7ñçó‚ÉÔ×íAÈ]žÀ9ŠixåÉT7FÆŸÂý´ºÄj“ý´6ÔÊåX*‡  5! k—Eý³?¢OÿôÙ¨Žþϸh0W.=ù9Y”Ñâ%­HâV<ª}”ºèÙ«ï³r€e©aÐ3gÓ:bžæA¡YªÉLΆ`B?m˜~Z?\!–ê‡#J°&0~Úì7xbÍV‚NQn§+-¬4bã.ÉþʦÓÕª¢ìa½DìKi©þn^ߘ¿(šO¦9!4 sÐß0¥‡v©&;9c,Ú°ïÚdݰO áÒÑACøå¢i  ÐXeŽfµXî¿ð<ýÖôÖá>ãª! \±,û`Ž7b ¥µ"˜äˆ³*W'x‹º4äsë¥lÕBq}Q›[G×K‘+DоX’êÜ—A(Bí ˜ƒøfÁQQ,”Üþ{Ôíª¨u·‰µou rZmõ²ü³Yð­–FO ±ÔèÈñ@Mc§x7ñt°7/´Î÷œþîdš B#·23oÞ>ï <ú <:½ÄÆ>Mö[ªQèóüÇÔöößU+bµQ•C«•¶²DICÇR·‡Ÿ!kbYã+KV/ñÉ#@—ÿ=ú-^O}ž_B­n¸½^ÊE!u%Póµuñ‘z|ï¡j=˜7t¦ÔΣª•¶²DmzÞH¾ÿO²E¥ðÑ¢²duŽQvéðGiôäïøØEñº€“Ñ:ƒ­CKu€‡¬  ¥ XõÈéìp XbyÂË}ð@éøÔ€”Õs½·}ž3oûYßo5òðYFÑñýS»ŸkñÀ„è`çV:öÊtzÕûµ(¡úYb{õ£¼B;ïH9ÿ]jˆBkbrõ ¨8%¾¹¯˜Ke±Î+¥Ópê9ä•Õ§Zñ©¯£œÌ/è»ÏgÑÑoT+Oméº\…'hÛ²»è¿¯ô£c;ߢQT\àÞÓµ)ž1kE3a.smÉ!€€@9cf”OŸ¯Îe©ô„ÜÔ}ö%ãþàÊ(— A@¢Ê;ó7önaûÍ÷®²?GäI­íƒ §Ô1?¢ü­ë¨Õ¿ ­¤ˆN¯|Ï5±=ûRÇ»ÿHÖøDêóÂRÊß¾‰Ž½ü8E¶ëB'Ϥè.½Èuâ(eT¼ï[^™¢RŸ¿-¡ƒONcõ{²·éH9Ÿý‡D,I2>5ƒ¯tjÿ˩ݩ99Κ%tzÅ»FÕIíîxZ\5’tM£³›VRÖëO“b‹¤wN¡Ä«F‘îqÓ©ôSöÇ‹B­/Ëé4:žñ1í]?—$þÒas¨u×ñäó¹èÈÿ^¥CÛþ^®½·½è;sôsÂfµÇú£«y~ZMPU&ƒe©J<¸   PSQW]û€¶ùØÙƒÏãþÇy¯L5- éë@`W®;Ça‰:S‡"Èb‹¢ø~ƒ)éÚ‰”µx!ÜMíïœJjT ¹Îœ ¢=ÛÉ““M‡ÿú':½üãQïM>Uûçþ–ÇPÇ__˜*eKmK]z–¼E…”õ¯g)E˜3ó‹©FÎãGÈ“—Cy_®¦½ÜÅ"f- §)d‰7Ênûãû(yØxãYY‹Ÿ¡âÝßñ©×ßD ý‡Ò‘ç¡ãïþÚýœÅX«ª¿‰ë=ˆt¯‡J|WDÈ,ÖhJé8œÚöº™ö¬”òO}ËBæÏdµÅRIa&åžøšœE'èÛUÒÑo_3jÜçºäaQµù??¥¢œ½tÙˆÇü-+΀þÉ÷óiï†ÇèÔáÕTtv¿!¦¤Œâ܃ä.9MÙËèËo¦SWR¯a"«=Á(£ÇàÔ¶çY<ý“ölœGyÇ¿6â;öù)‹®Qôíê(ã«§ kWtB'ÿs/[÷§}n7„Þ‰}K¨ðÌžÀÛ¸nDKp 0räZ¯U±þŠ“÷|{¯]±Àro8´=TÛxâí)÷‹ÏèøÛ/b`‡ Y$"7 %Í夢ÝÛXô$K\Åv¿œÎ|öyóÏRÎÚ¥Õ¥'ÙZ¶ó£+Ó¡…Ó)wÓ*rÚKÞâòäehŽb£ÜSËþM®ìLCL)+Å_>ÈÈŸtí:ûŧtâÃÐÙ+(ïë5F|<¯›2„WÖ*Úµ•œY‡¨Å5×ùŸYö""¹•!ú²?þyΞ.{Ÿ›)ý_¤ÑÉŒtÚÿeˆ"(©ý`r$ %Ÿ×A¹Ç¿2DODd"µh}%e~÷¹9”µç=¶0õ¦¨øþ–‹•I,H'ö/¥‚Ó»ÈãÎ'·3Ç(Ãë.bñu’ó*•ä1Ä”¢Z)¥Ã#»ž“¸ŸÐ-%:ÙVñ)<•/›­_"¶Îf}Éç ¶2Mð?3ðB¬a½G²E,:‰ë÷Aà-\72LÃkdàx€„1Ó=ß,[ >ÁsPfI{y'ùùËŸŽLÿ óp8´?ÔÚX|ðœõÅ›Ÿg4Íb¯Ø›\<‹(™n×ù¾9~>CQº‘›-Q xº^UA¦åµžô+ŠlÓ‰œ'I«”»1åÏÁÖ­À b*î²$Ó“†Œñß²·ë쿼«X·‡Ÿ¥â½ßâ/ð®›7<¶(Ip;Îg«5Æ8—ý!^ídºÝ£úoyÙŠ—Ò‹…YFÜ™#焸?A™‹–]®£KþŽb/¡’¼ÃÆ]Õb'9"ØDÁ©¥rˆ˜Jnw ‹´Ô¶Ç÷ý÷¢“ºú¯/6¾=ŽDÔµéþ}øƒÅ†,çØ†À$¸n$Kp#йó¥sþnOí¿L'Šå9-o­Y3âZ±<…‹æÞ^Íé¬V|Ž#LÁ+9PZÔˆˆ’ ¹]ƹÂìD¦ùüïKÚ;û.G-芗WIu˰bÙZ—ÞïX÷yy’‡N­zŽ¿ÁK´ª"¸Äóž·à,zv&«xî™!C@cëQu‚×mø ¡¯y ^YQ#"J‚Ï[UŸWxÚÝ:st=}ùþ$¶þ$Ó¨_m5òi¼Iã¼Ñ ]ŒÏæ]ór¼›Žì\Lû6>nFWyö8ó =‡Ì¢äÃb©J\ vÓð - ð&ÐûÖ]n]·þ‚Ç¿!ÁãÒÁŽÍëþÞTB«õ®¬ÃÆ;™Ö&SðŠØZ#SêZOºËˆ‡ ñýù[üj3 Éäå©q’WœJ†Âo¿2¦×Å_q EuìN‰ƒF·óxmSòÐñÆ~Mbi’{öÖ¦T‰x?§K¦Ì'{Ëö”ýŸÅ}I/ŠéqÙÛv |®C@ÉÙ ŠŽïH‘±m kMÞÉ­†õ©ëÀû©wâx!µÓˆj·\eK§=:•\Å'ÉE—ô/=ËXDTëîxZÞ0ŠKîE-/9gñÌæµMm{Üdì×$–&¹йÔsm1©¼Þé&²Ç´${l+¶^Ýg<£ð ÖÖ•Õˆ –6 áF`â ÏEWø+üs=<”žfe~ƹyÈÛ²ŽG3Œ½‹ºÜ?dÍÑÁ'§ST‡®¼ÑÇÔïì¥nOyâlÕºÛIÙK^7DRßW?ãl*•dìòç<öÚ“¼Æèu›ý<]úÔÛ”põ¹®”ùúB*áéyÝg¿@ýßþŠz<ú2ÙXÀ{jÃó^DrK#ÏÇ^#9Úþø·Ép‚²}J…9{hÄ_Qß1Ï‘¬9Úž>™b“{°ç»4æÞ}Ô‹BœsÎwqb9:´õEºäÊ{éº_ÿϘҗŸÍçóa÷º?kšþðMòÓUÔŠ½ßIسáQÃ’5è‡oÑØûÒÕ7¿O‘qmÎç:w’}–:]q' ¿cüåWÆþPû6=n¬}*•@õþz5Zuð P# žð–§©+زd|½ÊãæãQjlß‘S ëä©­©8Ý3kîZyö¢y³GÈ9ˆ‚…ëŇ¸Ž“̓Zÿì÷ÓŸíJ—Þñ!l¸`‰M`xìטÝy›ÁÍ®Ž9ˆg¼š±0±opÃ[]EyÕÈ( ’¯¸´/eq>!ÏõòÚªšb÷I·ItÔ–²åµ´y7rÝ ø¹_¾Šê‰¸ TØO;$ä\:4æÕ 3ÔW¤¬ò¸ø•qß2ƒÕg\zÝ¥û’y¿ª³¬O×àºfÏË%µDDó¬>+»2—nr!ˆó‰{[·r9²’© üE…-^>Ϲ©­rWïjCñ¯è¤£MÐ÷Ó þ{ê Ë’.@@‚€¢(ºÍ}‹¤SR>aÛ:|EoéïþHM!@ÀWÄûß%i’ˆ¤Ú%É+ë“Ä­weAs:Ê %#ç‘i€ %”*«â›Y(”¤Ö"’j#”$¯¬OªL(É}:e…’ÄKžsN(*JçÕZ(Iv„ú#±T,Q€€@%F?PœÍß“Êú%ctÀ?®O?òAõV9WR&¢A@@ ¡ @,54a”  `7Ý»‚­L£G·£Ÿ–ÇR«ˆ¥ZaC&Ú7Õ÷()´ÄÌ«éÚ+«Dô3?ã\'å›n§+¯$"¶\|ž晋#bØ[¹7p'[ð­YŸ(ÇKú©SO(_³b‘:€SG? R‡kˆ¥:ÀCVšõK zòÏY0í9Ÿ3Ú«{?Z™Û²f%!uu8KŠO;#¢»:F¨;á(<=.×ñº—†LÒOÝzŒâÖ#Í(œë@@8 OôÓ:@ È ±—   O`èŒ3…d±ÝÈîXÙ+€1—©³O/þdËKm£þéañù†Þ8ŽîÛó-_(G㺄Eú‘ÂQxž_ö‘lþd²®c©a™Ýd'g?=æí‹~ZÝA8¢ŸÖÈóE@,ÕK”  Pã§ûÒURý;‚²`º!ýðÏ× $-M p*"Ô8Ž<ðéѸŽÊ‰˜¶¥SãS?áxædö;çÙò®QYaž8[©~zÊÓM9ãíæxêÖ|ágpD?­È€ÜK0p   иX0-âuLñ?U×'/KSöÆEM Èn›æÔË×ÞéKWûÜÎü íGh^Þ ¡æ„›ðÓ¼îœÏÿóÁ{\‚ÉøÂî¦5/6œsTÜO=Îüoß×¼:úim:‡p~šý´6ü*˱Tă€4 Óµ™¤(oø¦éY6_ýÿ3.ªKÀüÆ^ò"”Ür””9¶m\ÿfnT2}Þq´¤A¨!á&üvoßöt^Þq.|…³É¼†%†ur“Y©~Ú9µ$¹hÿ’ÃZKe»cRXªmã¿qNÒ™íÞ†~Z[†å³V‰8hL­úÿêTþ¶6<ï:y.;Í{!}¥dÂtßâÆ¬Gç¨5KÞÿL']¡>ý&æF&êCŽ}®¶)†ìÊÞ„¬Q’©wbQ:²o÷¿W½÷Ö‡çY Wá+–ˆ%†PÓ°ô ë5n·v·Ûç¾¥¨„¢ó‹‰ øp³.òæ|ANŸ¥(«ý˜˜B=E¿Âþ‰šb=RÓG„MzY£$SïÄ¢Ô,û©®ÄñËÊ æ±Ìou0" .Å×<8ÎáÎÿ/ ’þ,š,io¤§Y¦ù– EWDa –ú”©‡Lq’A¼|/B‰‡ $›×ÈFKk—|°&çä‰Ó‡¼ei÷›ãMȉó°IDAT;Õ»žÝ«t,Jâ\¼Þ‰3^ëUøÍºÏ^Û¶~í&&#Óï„§p¾æ4<¾D¸ôçbSÉQr ö»tÝw©•G >î­6^ži3„’çTmÞ¾Oùj˾™½ùz¼vä-|wÅ·ŒÈÐ;Xÿ§´²î%›"Z5¼ƒì£$îÁÅë8sðy¸Ÿ~Ñlû©|ñ´%˜ß(os  ÁC`Í“q)­ès¶.]&µRr)ºzëø¾ƒ¡–÷Ìœ÷GRô¹–kÊ‹?œ u*S æá'Åð‘ÀG2I|´à#žèÈÈèèkÆLÒ¥gÏ¡V[dôHO‰í)Rìš'ìÆ.5B/‰ˆÕeÃY¿+^·«àHƾµ_®L_Åk¾Î23yÏrÎáCöÑdZ—ø¡"º>G]þÔܱ¤iwñýðï´ßsƒÆ69/ËÍb'í>œ­|ðæjuÝîC^é»UöS›R¬G*ùJ„â»~êÑmº“·õ– gC¡ŸÞûøã-|ÅÞ3lðž½è±Y©¨C\Øu´`€Ž:€€TM`ÅÓÑm4·cº)y?&UUï?Õ÷zÕ9þî½³àÕ¼[¸>?iîÌ7þ‰5~‚üo—™#bQÁ”ȇ @åñËG"¨,Ýz÷íЩGÏѱñ‰öè¨8«5B¬Pa¼^Ëåp8Š sìß»{ÿŽo1± ‰5IÄ‘ˆ%9òø¡$æ Y³Ä]¡,ô'm—‘4ývÔ¾ì}žd[Èólÿ_¢¾þ“9Þ|ý´¤ò¡ÖO'Ï~ìgš¦ý˪Z¾8÷á­å[1KÁñP 2V>ÕÁës~ÆÃÑîr‹-L¼àF™2~ºöt™¤þñîYsq]v.š7‹W¢eo·ò-¾&GbQÑ$bIÖÈàTîIV’^Žpˆà‘µGrˆ’)v"†Dò!bIDR"žäž¤Áz%†`†•i±-½zÉOX?þœg+œ¦Ê¿ÃR_Q•”÷ÆN˾è§&À‹ŸC®ŸÞ3kÞrž–Ùçåy³;\¼ùM—Bþ@"€€€@Ð;ÅqŒ`C}zñ %ô§üuaú|5y mV“VXWžåº¤Mž9ïÚ—›µ®IëRñÃÍ¿,D2 ¶dL“x±.k™ø,ÓŸLÁÄ—þö}´;|Zñ8Ž*7®dtŠ»Ób‹jýÇØ©î=çº`6'5ú©I¢òsHöSùÛ©é:÷eZåMŽ;æÐਠj  e¤?—¯;r—rôµæ-ÞÈöïã§=rŸ¢Ìi’Aëœ9¯E÷fîÕu%O‰H¼hÎä³nAt–ÿñæ7÷"Š¢ÏbiË’ˆ¥@ë’)˜Âil QéC"”­J¦s Kònå¡dZ”Ì,G…Wйӯx*bˆîÓî`+Ò­ B¬•¥w '·ñ±EUÛ[9räZa[Y@?­ŒÌ…øê§÷Ìy)Z÷œþ‚·ˆHlkmßsΜ;ÅZ´!œþ íK@Å@@ª&°iaû¨Lò¿ßŠ8’ÃŒ3Çæ™o…l0¡r6FæaÆ…,³aƆ±Î’[øKˆó—ÃÄ™ŠyÏVÈÁ‘銪¾Õ±c¯OzߺK,o ¤r˜}ý´™õSÙGI+Ñ&r¿ºqÜ·Žòû¼»9L½ ìØåïâ@@‚”/2ïëóy?âanZa±ÆÝ>î²h£qúåÉú?öÙ÷{®S{ð—óJ¶®èÇùŸm“¬«ª.žJèOÊSdÂflÀÓ'ý çiA~ápaWJ¬"¶§´³îJM°fµPØj[¶Ý©Z®¯CîqOŸS™ÞþgܺM„d“ôSVÍ Ÿò/U,[ÙÛò¯W+Þ*KÞLþ³òl»ˆv vguîr¿%B€€# ã“4Oá[üÏy¬Y?),NÔ»ÇO÷¥›q}–k}šOêÔ‰ÅR[þg+{!€@“ˆQó#ÚYw¤´¶ìNI´f&²O±Ö” :Ï'Í÷¶ËÍòö>}ÌÓÿŒK®Ê“]©¼øB€ÿ²X:ΗG,ªee0o8[7±TJH  ´t}Žº<íѹü/úáÀéCl5x-"1áÁzå Úv¢b Pâ¤ÁKî›Ør4‰?såÁÀ³¤h=›ÞQÕè÷ÇN+⽑@„Äú€€@HX¶ÀÂnŵ—ùkÍÖfƒxxŒÀýzÂtï*3guËÚ.W¼Þ›XMâ¶VìÅï¯E Ù¬öw¯ûƒ#+Ô¹ } PKµ¡†<  AI`õßâ’ÝÅÅãu7âêØØá¢x=iêÐg ý‘¸!À¢HY¾Ðv5ù|7±hOƒêVaÓÃõô:¶º~D‘ö&ÜïȬ0"Aü –ü(p  *ÒŸ´ÜB>í4¦úÛ¤P»hÜTï›,žø4_éÏ%ÅëÎüëyzÝD^7>ТØ*è9yÑj]Q?´GE<úÿ sïã@ jKUóÁ]fJ@Ü!ëŽâ¿sõe*R`øÚjµ<8vŠwS`$®A Ø ,Ÿoï©©ž‰"¸®ââ[6.xúi{KWUå#{tRúÈûNµ7Ær @ˆ¥ z¨ €€@ýX¾Àò]מbSR›ÀÒy*ÒÛ‘ö×ÿÞ!{ €@ÐH®›œ‡‡sÿÈÆPÞ¯†ºVVIH§¸OÌËÑ?êØ¹×êFØ©²ª BŠÄRH½N4@@ "kžOuç<Ìë9þÀ¢)ÒLcLQ"å)ŠJ\0áþ³f<Î ÐT–?ÙY÷z®7ÑhH1ÕÅð`G´]4§³{æec¦ÎÜÜ2WT7Ä@(€X ¥·‰¶€€TIÀˆzÜ xAüJ%T(ÏGYbž9µðL©{ø H`ý - ŠŠF±sºÑ,æG³˜¯Ø9ƒÔAáýkˆV+ŠºL±ÚÓÇ=Xr¢«†¢A˜Äº€€@ØX±À:ÌG¾§yQü€2/á©L‹ìö'áJ¹ |¬ÆÔ:×á!l9º^[Ž®ä‚Ëí}ä˜BûYÈ/SUuY‡N=×azŸ .@ Q@,5 f<@@ ØÈf¶+̽]S´™<`íY¦~nM¯“1Ât×2÷ðªMÀègiõÓÈwÎr¤ÐPéQ•ÀÓëŠùþ:]QVZ¬ËÆýÁ•QYZă4<ˆ¥†gŒ'€€1Ì®H›{³FÚyÚ¯TUy_þG¹œuÑøN“Ò•[ßó•º P†À®w{Û2íèójÃHчò´:G‰e’]ø(}L§Í,ŽV+¤®îÔ¹ç°]Àƒ+hjKMýð| !°"Í2QÓ KÓàr•â}šx:Ô«v‹í×Mu)waI`Ãü”¸%o°NÚ0žV7ŒÅÑUUYŽ H íá¾´š]{¯¶Äǯ½~rn~XÂC£A €Xj/ Uh\+æ[GjŠï!~*{%+·¾Wã˜U¼†äåÔ¸~KNÞêiÜÚáiMI`õ31­\nçÐsÂHÊuéÇâÈRUx°u‚å¿<µsµi[=á~GfUéq@ x@,Ï»@M@@‚Œ@ú{WR¥sEÕà®NVO«Ùº°Ê¢X׌æÞYQ:Ä5 5O'&:}E}tM»œßa_žR7ˆkr9 £ˆ‹ÖH!§ÙÂÅõ*©ëµÈøMØÐø¢Ôš-ˆ¥fûêPq¦&°òɈšÏw3[!ø •Õ‡ÝAŸbá´†Øÿµu͘é®ý•¥E|ýÏtGìï¥k¾ËIÕ/g{9—.¢¨CužÂƒ$/§ÛÁë6³åh³Å¢nÛá‡;á±:ôBƒÄRh¼G´@@ ‰ ¬L³õÑÈ{3¯oá$ƒòJÿóÍdWÑk9Áv±52ªÅ7˜¶W)®‹ÞXó|j¬Ë™×•=v'MïÁÓè˜? #…zTËZÄO`A˯ö‰(RÚÌûÄnŽLi¿ëÐ.Š @ ¤ @,…ôëEã@@š‚ÀŠ…önš×s=[2FñóGò(<ù"õÐx°ÎÖ&e+Ù·), ¬ñqÛáRú5ÃE·5¿›âÓºiŠÞMѨ».gº1ß6R^üŠY»8ÏnVH;T-G¤n³&ÆmC 7KáöÆÑ^F% ëºòiš­¯Gñb«Ç(¶v\Ë‚(®Z•Pè$‹ñ¬¶Ÿóe° MU÷Ç{2†Î8SX­2šA¢5kFXÝÛ¾j£ižö,0Û³jÇŒÚsÕÛ3?™2וòÞ/Ò6±±eé³cA¤ì°°8²X-;F·»q¦Ò]nƒ –Ð@@@  Â`ËÆ>Ý'›—àébW²KêËøºÊ½zÊV‘…À)¶ŽâäÙ,Nrþl¾>É&*>+'U«%Ûn?ÙÓûεqK)î$Ÿ¦%+ºž¬©dœÅÊÆõLâv·ä6±8¢v'BH-ÛÆ|v³ :Èåfð4:±Ð}gUÔ¶èÄ]MÑþÔIA‚œÿ]AhJ›¶ÊÓ³ûê>ßþÇ|%›CXDÑ¥|ØêZ/.ÏËb¬ˆ-TE,(ŠY˜±@)f±UÄF¯"¾_ÌñNN£«ì©BžÇÖÖ®Ï[hØ+:Erº(?Ñœ,ŠGñ9šËˆâÔÑçîQ,Ç'H9õøÙ.®×A>g(º²ŸŸ•¡«j†j±î÷ÀCGeŽVŸÏCY  øï€€€@°Ðõ9êŠgžè¨ù¼ÝM“u9ÝY¿tçs7&—p}ë,¤‚¨Í²WQ6J2¹N™¤+YrVT9+™6ÅzhÔ”‡ŽAÑCU@ L@,…É‹F3A@B‡€)¤TÍÛVÓõÖäÓ[³¥E¦²µf»PkTÆ5[“Zñud£·\!?3—9|>ËG  ù¬«tV×ø³¢œæ5XÇU5"ÓvåÕ'FŽ\+nº@@ ¨@,Õë@e@@@ ~ lyi@„×[âpÇ’î‹ñúôXUÕcYdŲ‹ìX~Z Oû³±°2Ælâ‘év2 Ï8kçã¥V*)NçÐ,ä`ot%<ΡY9NSJ,|íTQ>{шró93ë4sÿ$¥Q°Ñ0LÎIEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-dvr-overview.graffle0000664000175000017500000001522413553660046026601 0ustar zuulzuul00000000000000‹í]mSÛH¶þ<ùºùº úýe6›-™IvIÂ&¹;EÕ-a £°YÀd§æ¿ßÓ’±eI6–±Á˜Cªbº%¹Õý<çœ>/¯þþûEì]‡é Jú{I}òÒ û¤õ{{ùËñOÛæåß_¿xõ?ûŸöŽÿ}øÖ»Œ£Aæþòæàýž÷r{gg÷ò2wvö÷½Ãƒ÷GÇ\cgçíÇ—ÞËó,»üqgçææÆ\+¿“\¸†ƒÃ4¹ Óìû\l:øÝ¬ûnS\}âqàh7êd¯_üðê[øýõn'‹®Ãƒà{˜¾ïwÃß_í¸£p2êga/L_“W;·¿Þv{G ƒK~)®<ê¤ià~ùáÕ Ká+¿†'ô“‹~ÔK“«KÿüösœÅ¡zµ3lRjM•õÇgJSfJM^íÜ^ºx„«,Ùíþçjn¥WáÎíù7Aç›»e¿ ·»<:£fÃï>læZ nÏã?È–GþÜòþHK©4‘B #Å–§ ó±m(“þ9ñ]ÜÅöâ`P¿îQGãªty¿?n;à¬<öE³£ì{Ž[Þ~â$\2ù6>[:]œßOƒ›ñcìã§òãÀ`Féö·ñ/ÅðÂ_“äbŽÉ²ô¯ƒÁ§4êEãir;Îù0—ßrÞ#‰¯.ú»qÔë×®Oë×Ï[]¸Ä¨}ñks,ÿå¶)üá&í~…ÕGa„ŠmJ·)ñ¨þ‘³…ôþBà§ötî"IZíÿ!È2ïŸðÒƒ› ªö£Áe|?êqíΰ&áÇë\xóF¿W/ðS‡Çß/kXÕ¶ùÛO:Wa?«.ÐÑ@²ÚH'æÀaHžœi••SZ:œIßÁ$!Ê(%¤Üò¨„cDJJ,a‚móÅ¥|e™¥Îj-c[3>ÐII ÌZE'WWÓò¯¯óà2lZ`ÃALúÙûþY2}}Àd¿ÚÉóEƒ›ÒÉñœ¸Uiõ”îÛ°è>]†ý£ ?Ø> /¢Ó$îV.“¯öè¿a¹k>õhZOܬŠ"ã—-©˜’¢é‡ ×³ò0Ž^öÓ—èÄ9ŸÉ§·Ï‘¼Ñjïä,ŠãY¯¤úÎ&ZMN˧‡ƒH|m½)ýÈÒ°Þöì5ö4–ÊÆ=yCÏ´±§¥Š–ïI'zNÌœê,œÝäfÖH¼ û€MçMSôßoUêXâ@ n$QÓ¾ô¬DS€™=›š3Á•´ãŸ©}ûåÒ~˜~ºÑÕ`â;æ½L¹wyÉUVßqø{6}&Ož¯““4;£'€Qþ_ç²B;é$$p§0‹?W§ð·ÐäÅ'g*Ùi|rFNÎú|v΃tfÄ«áÊ_ÿ„ö÷ö Ã_OҰˤ<é¥aØw¿œÆW!|æ' /ŽS¸¥;N)ƒÞ'—AÚ=É~—ŠÀÿp,ÿPÆ}0Às÷aˆûàÍ›( MÜu£´dWiŸüÖq²¨wž…ý³ 4K^¼pßðôäl@•wÒ9cÞOq,ÞïyïO^| .Âpøç]L{fçÒr 0Ê}˜Å†Áò%†Ãi$°%0ކ††öô lß™d>'WY˜®ŠÀ`úPaáy¥Õ–½åén¹²ÒpʬQ¹¦ …–NΔ¢@uDøÆPc,•ðPØý Œ-‘ÀÖ†Œe\lÁ@Vj+ĸ'›ŸŒ¼ÔS !!µa ÜڿͽÝ^ØÏ¦ÏxÀcà'gLl62i¡nOVÌpR_r:ñ³–Zs¡QÛZœ¬T[²ÅwÉ Õ%$+$«'GV‡iru®ô&º½öŠÒô?ˆúá=° J¿ ãë0‹:Á¼èLÙ x~ÝéPïYªÆ| ŠiÀüÃÝùœRß+£†ÀÉxÙ‚éTKmœ.É…œfcãVùxÝ€®j…„ÞRú”JŒ ¾Z5½·>çDÉ„Ðð  ¿rí‹üÞTÂõ8³÷㣻q¸º«>m[ýAA˜h.9\JÅ(çÚØ¹QÚ3èKç.CÍÜ(¬ÕŠIm„ÑÜ ow °›ß»iš4ÊIdrðŠa/è|Ÿh~ăÜócÜ–vy·|b}ÐÉy~‹ÕA·{’¯Q·Â½ùWeóRܰíÒ® fÃÆ@5õ›"T®jUŽ¡¢õp~KÜš ö[â” *a¾ð–š70Õ`Ž)ÚiJDq·‘®5µÉ É Éª Y½w@”æ v]šN?ô"¸¢VIêDR‹AŠ­+IŽôƒôƒôƒô³Q†ïö½Å­t»qœÜ§a¼Ÿ&—¥—Ur)yŽ.c5cù.c0qu|hBU À|rδ”i_JK¤àZ(+ ›ê2¦µï̯š hÍ„s£Ì—.·Ræ2÷LéÍ\î.ëü©XŠ×y5–g0pÎÙÀ¸—ZÃæGvÅžô2Z*Jæ§PxíRð΃ þfËuÅ•èŠûÄpURyoWÜùà JÀØá à ç‹!tV5`DXEX}ް:#xiF·v„òÌИ,…k$i$Ñ[ŒÞLh$A#ÉI>„YÐ ²àäÅaštÂÁƒ‹Fí™ÉfYænÅ•µs¼‘ ,ïO“´…nŸâ¤<ÅU9}Ó Êé(§?Uó‡^­ùc†PO¤ú8gR fo6RNq½=&•R.éA™~ 2=!›ê$ ³ŠH°èÓÒI”ƒ|Î1΢ÞÒI”¦@е„”éQ¦G™~™~-¼DGdåœ à¥97zpùT¶b¾“_@UÄ*j¬F¶Z‚ S±bH²²ÕÚ¸é¬Eµ gÛ“”øŒ+Fµ°F»ì÷È=Ê=teåYHùǽA¬Ö‚L†L†L¶9ˆ«ŠšŸyp#C~‹ÊÀ|šÕîîÊÀ-Ëo$‘ÜÜÐ<¸ y­XESŒûå@\†Iã”m´ÝD¶¡‚æyTæÄ@J ÀàÜÀg¨ezj\2 2Ì#3 ÃL'__†¡Ët%çÂN?Ò„)«µt £´¯$HŸ°he̪<Ô–äUÅ–H!QV™8SVmÑ:.£ÀmÓÏá Gõ¥Çé®,»qÔë7¥#M9êîoàé†gYp 2Ä*÷ßí¡i r6`dXú=ŒÏ¶azªô‹XÌÙ¡ÿ_a1×WáäÛZ…è09wÕ}$•PHÕv©Õ@S‘\Υò.£•b2¯)_=&Åü/7?æ(Q-"ý£´‡ëêŸ'bäÓŒw&b4‹øAå‰í2eÏÕFëÏ-›-M1ºZŸ8Ã圻H%,[KËÂ'ù¢Í%Ê÷•$Ê(;¬…ìðé:Lãàû­ØpâÎ;]…€Ö'; *<Š(!|RÎîšt‘ðAž'8¨Ç(ITE<]IbCü¨Ÿƒ(ñÜ2Ô²òBR„/š¡^»dW~†;묰®’”0 t«±še¸žæ$ÔÔÓÛ%)6Ôç=ösÌ;·@4R9÷mmlË[Bç.CMËM\© ¾Ã$óËM2_õ Ç{÷„FÚf<ùBÐÈ­…u Ò#¬Eea=º2y’ _+„„Uªœhú<¡‘Á?!‰!J2&æ†F)(•Â2J$ˆ{„Ì/¶ aµ’sIÝÝ)GhDh|æÐH…ÆQÆ]í’ÈÁÄ–Ç•ð®­7Ä2µþh“›JµZ ¬”ßíUK%“L—X+ßyÃ(¸»”LsްŒ°Œ° 0Âi¢™àIÏj q7•{%PN”C@ç 8?üQí4Ó±mSëVлìcI9,)÷T±³•Æhf`2ˆ! è "´m£í/‚鈌ˆŒO5*ûKEF½ò"ÆÖ,ïÚä¹®>…;ß³¦”¥-ˆïªÝö5ÈßJF­`´åöµ–ÐM¼*†Ð¸\hTK„Æz!«ùÐN/s×§ÚYæk' âÓÚõÚ'îšÄa½ši½9-¼¡ –Öy@ É}*9þ— òãv:n§oVÄÊ5ÀJuïm RáEŵ¯­d”6.4gtÚ1¦|ír‚ŽFA25 µ• GñRWÿEÁ@ Ö. W)ŬÉܶŠìUVÁM¹$ÐS#N.'«Ñ戓‚“òq4è&œTʯT·EœDõ¡Õïõ€J±(TV7£¹u1ƒÂ´ÒDI âåŒ jD;D»g…vÑn©û0rõû0 ‡êTaPÒ-d +7 W6VOõüYXŸ*4ZŠÐ¸Lh\Òx΄ƅCu˜²¾S‡à(gB8¯îù¡q`EçtÞyšÈÈ«ž ËFÆi”o¤Ëe j:a ÖUÍû˜‹"Ã(m° ’LX·qçò2T?ôSÈÏ]$€l‘ »ÈûØ"AwžîQ=pÊTÓ>eªÙÄ”©yúu²HŠ"{©Çüu(òŒìãž©b"U¬CÑ&ªûzÞõÑM”uνÝ^ØÏfç)r£~ð",ãè”dP P# €p`´«·Äˆ4ŽðjuÖËI¾¥– ›M)&ùž‡±t[ÆâZn*cIkˆÛy$ ™·d,9Æ1.EVKÆ¢†)€qK¸AÆBÆBÆjÃX?EixÄñ2Ó~3 +S¸ªpÊ( ¹å\é—3çQ,—´‰å’@ùÞÜrIkë%Œä†ä†•“®rÒ­RåœArwX½åÕ«)5W¨T­D©bZ£R…Jò*UkA;ƒ,èw–Yƒ¶)P)î»ýe—8ǸÈBê8ÇR,‡°Œ}%…å°ÂZ”C8L“먦µz»=¸Y/ÈÂG©†@µ%ÕÐŒ55„j…•æù„Ë!lFÝh,‡°y%zõ>Ö‚<Ž#aµ¥’ŸÖø"¯A£,·jVïÛ{K ¤K½õ5g ”5X$Ë„ÅùKÕJNHUUÕPV¶¬fúÝòê&â"8«ê. V¸‹85ÊÕ#¦Tó5ÎZyا`+ûÜÐ*«imcV]cPG+ì Û}lãëÙ€‰ÂôêíÁ×M“8SïcÒ Ùéd_;˜Úô £²ÚwÒP‹¡Î5§Kl‡ÙîGKÜ*ÌF€ÙÖ"Áaš\G]À¡a.‚GÉ;@µôµ Uq©á ¡THàÉ¢"‘b\䇉ª0"ŸnâÂàŸÑÓDšYb¨: RÏ–£›Å(!@ã[ÌG°¬| 9ú©Ì*¦,ÌQ Eµù‰æq›šûîh¢PÂIè×ôÄãZÈjýš¦ Zœ\u×Ð6Î •oT¾7Ü*Ï …ÞOwxMµê½/nì¹¥HZZªSŤ_uÈÆ”§c°uc2 A¶íÀViAª¬ÒZ·[”FŒ1’ZŽXËÜÀš‘ y¡ßZßmƒæm-aœk kÇÏ÷‚µ N °Œ'ôsêNY´ÞSçgM‡h:Ü,cଭ Î*Æo{ï ñvp HœFq”}÷à̹·ÿåóI›;Œv>>]‡éuÞ<Š™±!5zó: ²O©¡ÄhaA2‘¼^ Xêç°ƒÎ(ÅtÜAG%wÐ×mýâò* óíóÁƒìŸ?7WmV­óKb–YaEqík+™¦Fs¡9£óº¬Z¡h)µRÆ¢Ë*º¬n€ËªÆbRKÅEÃ:J×ê\oA‡Z³•U „:„:„ºe{ç3ÃýŠSÃï|®…O]° ÅRk*ijðÎ×m h[@ÛÚÖ˶0ÌŒ¹BÛÂXÂʯòsuË=z™£³G—Ažat‡!™úf’uÆSgè‚õŸ$mì>âÀÊþVÉÍÀõ7o‚¢u?¸'s´ûü[]E0ºIç[8êØ ¥Ö_¢At:f‡)·É%§Ã ŽW3åÌt ÷€×³0£ù?Ãðò89ꥧ X…p|ÓñG’tE”p_ü®-Ž¢åÇà¢Áû-¿GëÍáÏy·Eâ¨æ{Œ/Qxsçe§MpxÚä*›>ÅwûÑE…µ TÙòqg;QÚI>DýýhU–C¹‚õ¸íQ+>È¢¤_irX¥}œ?æÛ~äÚ³t“¬ö0Ž2'N„ý^‰¥úÄ ÷Äj¨­aÊÌ}q‡Moà=}ÌÑÖ-çé çãÑ›$Ë’‹A O0ž%~x½³8 ²Ée=ü]ÐÉ%<†¬âï’4úoÒbxî¨_™xõu€5»7z³›ý·ûîñ¿¿Rþï›ø×÷ï’]ñ¯7ÿzôö|÷üÓ/DÒÓ¯ÿˆ;½=ø»ûéúëùé»/ñ.œß{+Ù?~ûõëG²ûþëÛݣߠÍO¿Ç7{?Ï~üƒð,»Ïð8T™uýCP¿Ò n¸|ñÑxýhlbŽ{Lh]M÷¨9³Ü }ÒJŸi)•&Rha¤ØòŒ( QÀ!PGAüóŽGpñsx ä6¬Ž§0>;±o•Sá8¹\Åå'±<Ÿú¡C‘)2Èç0è~êÇßÏÉÍDxÜ ÑšVÄÈ‚uøXêŠÀÇQVG½  &„–¢Çˆäùc8)ºõv;Yt=¤—wvÂ@Ðï„-ûþÒ~» ÇÆ‰ßþ—Aø¶ŸEé¬aÿ2¯Ðù5êƒ6>Õ÷®ÒF"ɺÌÞ ²¿ýý2èÃbû¿bˆK2ݘ‡‡Æžt©Æ!xÜð 5 2 1 °ã2Ý@IDATxì|EûÇgvï’Д&EŠ;Øõµ‚"ö®Ø»$EDH.z’»Ð|-(%á}mï«E}íbÁ‚ŠŠ•¦¡ƒ´ÔÛÛùÿž½ì±¹\È%w <óù$»;;m¿3³÷<3ÏÌ ÁŽ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜h0²ÁRæ„™`L Là.ïøÀÂíú«Ð;ê¯ð >Ùåîõ>²oq äp·[[7Ù›½h—€3l0v?s ±|Šß³¢Á2â„™¨7ôOvL€ 4Lïí5£ôH¡dGå¿§7OÿåÑaÃJ"¯=%ÍÁ9¾.A©TJ¶ºÜ 5±ú¬Ãþµ_¿~Á†zÆ»½ã;•¤H}¶Àwÿâ†Ê§Ü0®B=¦1ÈãÁ†Ê'–t½Þ9®µâ«ÎΰºÐÊÎ8¼ÛÚ†díÌ/çY¹þ•2›úr§Ö%½R£ì%Ô[†ù<âÝX—¸ ÖYÁöû®(ÌÊ ì,Ÿ»óóÛ²Y;±ß*¯÷¶²…ÝÛîÙýÌšÏîßÛžŸŸ— 4¬|4†Zâ26^¯ÒVüÕÃT ´]HbÆD‰âMÅfÿßÏ.M»kj^ögæ¡vAA³¼¾ãMCŒ¯Pê,\Ò…ÒŸ3,Ú˜éñ½Ù§G÷þ !Wý•9BZ#ã»àqw{ëÄW]€QEÑ2Pª, ö÷ä­–BÎÑ\rÂ4oÎÏ»½°5àîI“RË×n} ­Äé÷¿^˜“³º† Iïí¬¹b Ì$8×èÊŠÍ è#·®E§#ÐÜò &À˜@Ð’°L\$&Ð( ðŽíºÊð‚ÕñRªfRÊ÷¥&'hR ‘R›,„üvTP?ÎÌõùi´³Q>h‚ =8wÜÊPs)Åpûx »q¸þŸ*‚Ö ¡xX"å§BÊåÈVä£ezü¹ÆÇEúï1×Rlë©¡?pâc<[ºRê¦`@ý˜éÉËJÖg}bÈr´÷„Ÿwd¯MÖrÖ£\#yýG×#^½£ÐL FÐÌc½áˆL€ 0 °ð#(ÆvFšÌòø_…âq,”Œuáî7Õ7ò÷È8™ø/Ähþ³Ÿ½Êü| îcsïve<‚™‡¦¤¨ŒÉåƒÙ;œ×ûtÚ*±¶íŸÄž!OR:ºFKõyêó«h÷ö?(k }9ƒœÏrï#4)ÞT:æLcP“2sÇ}Q˜7ê'g˜d9‡¹Õ¥¡²ä&K‘â*”í è ­ Ã| ƒ'z½½iBªÁ]y±yü­CÑgǘhX<óѰ|9õ½„FÈo$Åæ*vtw<)šâA( Çä¼#5×ÙhIÀΦu!± "“XÂ9ÃÐÌJ}gWHàw¦ÕP礴ۉVúzÚÄHŃüɦ}w,ІÐ}fCºÕõÙ‡Oœ˜Q—8õ­£ºäQ—°´. Iá×aý–"LãÂÚâϘ1C¯­îª6­¬µ•-ZÛ/ž~”é-H·Ó‰é¨äÁý{( Ç_Œˆ)N-bâ.Õ%µ$Ó ·c*c %¨3ãÒ±½½Þ)ñ”ÇN‡L€ ÔL_예‡ ^3\ôéý¥¦õ/ÌËùWméeæø^Ð}%L²/ðåµÃcíÍqÎÒ}µ¦ëkM£ìŸX°N‹c€bSå[—t¬I¹±×œ@·¹a{V¦û :úÓ%†éWUgç'¥ûšt=ev‹§!þ¡Ð JïWMùÓÆx^«³úUÿüçKÓ|ñ¾œî÷ÜV=Dȧ¿ÇW(…:C ýþé¾ì7aڳ͚ùH×:Äb·ð>¤t•Ú°~!2°}åïnOú=Õf–ÂùK=kz^ö'aBLF]Àó¿€ç¿Òrºúö#Í¥]E$`)W¢‡çýüp¦ynׇ&ä©éÿ2•ñ5Ú_P¸µ£ ½Ù¿E†‡‰ÔÓ´æ¼Oïjk>2½ù‡ #ˆµ#ò뽄wÒøJsëçyGÿi§7À“wVP´uq*ù!½e`„jHqÜ®” £ücÜÙæVºg;ðï‹óITÖ–ÍÝ'O9r›}ŽvÃOÓ}¹WÛ÷êÒ6ì8v?­±ÝÏ`JèA¥õ3a×?wì BO£¼ÅÒíºÚÔÈòL8Ø#ñ^ºm´ž(Ä&üûg¯uÔs¦y½Ò 'Ä'L€ ÄE€g>âÂÇ‘™€s.íRù¿¥ƒ~Ê311Ñ¥¥˜J’`vööPtˆÆUf üKüb–Dü!é¿ø/Æâ¥AQñõ€ü–ðŽˆ“ûÇoVð¿ !p<‡¶øá|BÃkRÉHóI ¯Ñ¾3ŽŸR«‹â/ x\ûËÿiä9i ªÿá‡?Ó/Ú¹ÔZ‚ä;ÀjêÆ{ü~ü€Wwƒóó[AP½wöß§™>§2DȤ§DÝT=Fuäñ;1‚ qä]²YG™/¦ûæo¼OŠ"Œkà`šÞô{ºo3€mÍB5qohî¿r±µÜ‚ëæúÛNw¨÷Ñ–0·ûÈÒM%Ûë(ãÔÕÉ€]ý‹|*ŠÍ/~?ÔÏj9Ïàï'K šïBè¿ÍNw•Ð,³\´#R¾Âíð`hÇ òŒ?,h?B¹/ ±‚yš¦Y‚ræ¸q-0ø2Õàp(îŽg}m£qo€-Ñ˱2U(i_Qʼ%œQĉT·„êU@ù 9* ùÙ×ö‘Ö_•¶~E> lS‘/å=õqDëO®6òO?2MÑ‹öŸD‹|÷A[˜>ŸDæQÓ5¤Ûôiy£¿G[œ€4R…aþ›¡šÂGóÏòä_ÅÏ./Åý_!„ ×/FzWšàP8zÙñL)Çûaê8Ô¦¥Xd·sM‰ß¦xï_ƒk7Þ§ ôŒ¯Æy\gÕ‰P‡oÝnœo§k€êK÷¡.³ýêÚ6ìxv?­/ã^?Œ÷ѺaÓá¶â1Ø›ß]© ¼kÅíhOÓ»mõ áRE¨¶³x<'‡/÷#áXÍ·QÞÏ ýÕGÇ)|‰±ÝålšŸ‰&òšéŽ—x'¶ÝO¿ÉN7|TæÝ¤ ˆ¦î[£±FzÀú?;cN«AOÌÓBÉ›–’æÌ Êƨx ?lÑ"õއGŒ€b¼ÃÉb\Ô9àò.žóúéŽ6Õ?×-4¾§°›Ùð½ƒõÛ$\ú³Ò0½h£×âÙ‡E>»5»¸`ÑuPŠ‘µzfGNÑÏLË;Ý‘ÿ+è 7Ú}ùÕ†ÏA–úJTO?¦™‡¶ÝYÓµó¦Éù j;ó”*n§¶i>;y]§¬ úï‚פE³ï‘§2JŸÆµær‹Ó¦z=áõJPJ®2Uðå ’xwIïŽÂ<Ï“û$f ¿Áñx)R†ø«n7öø!î 0e 7Žáuld޶*ð9äϨë#ñŽ e3S;ÚùÙèoh&’Ò°\Û†Ñ>Öƒ1Í,†ñ’hŠ¿‹» V |U+”òAÌÎŒ±³¡#͈è.Úv`‰$P§•DfÌi1=…„óƒ*Ÿey¬ÏÔJÜ¿ ?ÌVØ&´}õxr›+Cl+tŸÌJ¦çyr!Ü|‹û®Ø’óF;Þ ï„ýð yB)—K÷ØÂÝÌ{ïf%õatŽ0:Vwd†–é†)Lóæ)ÿ® ÷¥í@«Ç‹ðQâ©}Ö¿& |Vºi¾ì·Ábž+@Báß[E† ¬5v ÇÑ…Iž¦]–7ÿpÇ-RЮ@:¥í@þC‘@v¸]`4³î‚a8•ÐI¦w\gŒŽÞE|Ò›hWGšŒMóŽX)D[yKQ’š®×ÈÂZëUÆÜ.Ùí²Kô±Û”ã Zër¥¾Q-s´ø­-È˹.Rñ Ù'S)ìð&¶»2\7F¶©éy9/¢¦  ï3¿\J›F¡¡LÎB¶Yc|y^d~³^ÜáÛ£ | 3©¥‘÷ו#ôý(ÿ4W‹;œ}F°Û»<è43UÝÅÛÀìMj·ÖKñ@q0*o­µ¢2£¯Þg€L/òi&§zi£ø¥Ù(Cs¤ô²Sñ 0ÿ{ÀPu×þZY·Øœ®[Ê&…ÕwìXkŒ¹g NZ¡Ì/!Ý_Ñ.„Rã¶ïÓ9Êr:ž¡$¥u³¹ä_Ÿ¶a§gëʘ¾ã0‚3¿µú5h?aEÈJS©ÎtÔÜĦª£ïþLñŽŽÚVª†ä+&ÀêB€•ºÐâ°L *­­å­ä†¨·£xVNã[£â®€¨f¢„ô§Žýw”¨Xê §“?„µÓìûF0p~”Ó!Ì™ê½Ìö·õQóq¯×ÒnF¶ø“”Bïðjå·K%?¡pAÓì_à Öz|Á¦Â2Ëq£ÑG”údhQLÍóXiÚ÷!LÅZ–žP°ž†`š ¡&S™¡„ÌÌôúµÃÙGM«ˆ‚fX "S/p;BÛÜ”&ÊT ¡.³ãУædÆ&LM[ùÐÆé(g*ò|¥r¶„’®Ý)9ç‰ììõ‘¬QðZYGƯÏ5Úо0ºk†CáKk >úñ÷?1»ö8”¸rü@ô ){QR×´‡H!޼chòtÔŠ%×Іu—k ÅCä ìøÈëi:Ç,˜¥ Úþ–ŸYi’§Ð>jq°Ý? }ÉÉW£ÍnQßC±_Š–L¼ýˆH™Ž–v]ý¬¾¤$f&T†i­>CQ]jÿŽæmèŸ`,ÕQÑîGó3Ó]³Á2ˆÎÓÊ;š{È™Bƒrо$ÄlÌGÍB_h!‹6ô ÝÅÿ¿ÖŸDeGl+€õmá4q+cäkÒ—ìñÑ™0µÜ_SXäË~Ý™Vå¹¥Áoê@ïØ¢Üg/&ÀLÀ•àô89&°×Ààä_ø¡#e#t#ò¿5B¸b]kòOMþм½º_e ©Lk$b@×p½TÃ+Gc!  QV&.%¡_E—„{pøÞ'“RBÊíxT¶n˩ఱ)c5[Þ„³ÙÙI0l2§-ÚY¸È{`³"ÒϾ¶Ycã‚TÛ¯¦#ÍúÀäï°Èû)iÚÇ“³³7FúG¿VmÀøaº–¶ÛL!È41qšÇSd{FS4×÷‘~tu…üåÒh÷ɯõç*áà´Í´Ð7\Ú»;¾Vd¬Ü ¿Kh]€=cB»‡mÙ\~Fз7o‘ú Åß™ƒfÑ…@¹¸¦ph+ñÜÕoÇÙÐ'£2©žQl>Í[¦foÝ\~ ÊÚ&‰w`C„¨J¥VišÖmV™ÁAhÿƒªç2E¨¬£ê!"}¨ð‘ÏyÀuÊ ‡òIiùå¡­Å/ƒ&°éœÝçÍúqIK¥‚wÁ”Ž”ý( Ð=‹Ž`VôëÛ6(ÛÅÊíFÉË(È!x+ø¦ù<ÏÛi8²s›ÇÕ_ëOûñÒ aünojšV8õ¡ì£½§œqùœ 0úÐêc1&& ÂBV—°_-')+Öu²e,ô´lÞ«…7I‹î\ںРVvðÃiç} ÌÎúù£†CƬ¦D@`©6=óÚ}SÜîçh¤ÂÇ $¤ìˆ!¡|H…ÌŸÝáWýŒÌ•0êû°Ø¿íA(o!ž-BËÔ»¼ã;Ø¡Ûk>ƒàS†äδä"¯Ëé¾Û¯…ÂioãÒ2q1]—oØv ‘4Ä›Y9ó VÏÿx°s¹c'¬˜’ÒĆ˜ÂÕÈ š×AØ{%ò/P\]!©1))VbþÙô§iî£3ÜM÷Ýû>r‡îLñ ôZ ,FŽâ°.£“å-ÅÚ(·-/âÅëQ·îÏþaµcÚRíöEª#¹=p•wÛÖ Ô«ÊÀ½‘&^vçs1­ëäE&jÙâîGBEeâ,_]Îéy¥.3)úíÃv@ùѽ0í×èHŽ8O뢦w”z€=t纸К Ã½)VÖcOD^Q'o’â˜Ò¶é,RMlÙk÷y\[³Œ($ B®¾mÃŽOG2&8®Ä%Å)1½“€l"–ŠR<;áÒ(m,’!ô#ÈÀq¬YŒjä”lSÍÏö0LkÆRG¥‚d¤XMr^l½idÛÁc9&r„ïIïÈU0—úå9`®ïLäÿñ@¯ïdì|s ŸY“ýžå±”©r1}Lz@ 9¹"8ñž¥¸$¨Âÿ3¥ç¬ Žë‰‘òeb›A ¿çOö…ÒwKýõ eú ¸\Š(ÿB¡‘X¡Â#±”V½)4[`*Zÿ³ÃˆÏŽ9†˜cUˆ??€0·%òŽÖDüéWÓ5ÚPñ4_îìšîïÌÿÁ…òz«‡@ÿÁ”¸Xí´zÔZhä¼Ú¿ÙJt ·cÝ-žÄľ ñ¬Q~l¾š1«4ËŠ–^U?e)w0‹*lRXh>MhlüÕ—LýÈ.>‚ùúÓS`v{y @æj—¡o•Û÷í£«¹{£ØÀó¨¼êÔ&í4j:"ošÍðb–—úÐã˜å°}˜w½JqȬ Û%¿ W z`ܱ0í\¸}S1™X.wnOÛ |ÈÅú®ÂìN+¥‹«acvÜ­Åâ§Ý2³rü«Œx^/„¶ã ÞÆØÁ|ìOÏ{Áù” 08 àw`ñ˜š“³ ?‰?àÇ*}ë–Š(¦ÕS ó>òÅÀ|å(}µ0áYÈ;.º…üäŸö=¬e™þHq¨í·›OSþ° §]¯Ê舎åOç±:ÌÊ|Ia•”á™Ê¸ÖˆjP™g@ñè Q£¾!aˆîOÎõ êe ì^–™[è{B¦íØy§2z H-µ"JÙµ^ ĉ¾ï@3D‘µÍXÄ™míÑ5ñ­h'\²òó±A¶’Uò/¯·_…è4¯çkp]Ýä å¯Ü²¹”‚Åô¼v¸ÑNþ¤ûšT¡˜h+gG0ŠŽæ±Ã%a?² —îθ\V£\J»…¡OTÙ]ŒÑÇ!á¿´ÈzÇSÅvNÏîóÿ¼{°ž„•Äl¢ÜænÛÔ2±¢t%_§#¶_>ŠÇ©x¹¥àÒê£äo¹8Ú†D¬G˜_Nœ>Æóªt·Lm ì.Ǻ¦¡µÅŸž7ú|—åFÌ„œŠvg ‰¶Ípméð}&Àj&ÀÊGÍløˆ‰ÄiºNñÃ<‚¶XÝYDëJõÂÛ_]æDô½h÷ ÜBþœæ‡ï»ôyH#êêJú–FØ7¤¶kŽý ,¨7¯¤/£ÌW“ðÒ¤UúÿêQ¤n¡8ÊÄçr‡”Œ§Ã÷<ºã®*éca:¾¹!šÒBXL'¢ ?Ç*œC*-£41:¾#&´JþæÕ‰þÊrd^é:Uk.˜QP꪿ö^¢¬6Œ`_D>ÚÊÓ .Ë ãêÒRæW¿Vò™Èp5^›âº‡t.§uQÃU*¢Õî%Y?²ËG;Öa§‰ÐÀ†©&AµJ³ïU=JKQÇ"ë̪þµ\‘ #œ&¨mL«Às6 î›ù`þy¨ÜÃñÚ{Ç^HNqÓÜøP$VhÁÜ }§ùa´¡Ê,c¼mÃJ3ÆБ,¥–v tk.Úý¬oÈ ™¹ùX_»+ÌËž‡¾ÿ2…T"xpí18`±`å#VRŽ ì„L#fáG÷]üLµ eß;?äåŒF_ßÅ®QoZ~RmÙ¹;TžC`n¾ykÀg¯g°ƒÐèpïLü®S®¶–Y Ý+ðf/Äå¿ñ+Ù²¢$øÌÀ±c÷±ãØGý§ï^Ø× y$¡Âþ (këÕE÷ƒK;ä÷Î:ó¥Ú¹ù§×$$b¡ù".…Pó·t¥TT§zs~‡5»>‚ÑàÿKä—ß±»5 œá˜Üø\[Õ‘Xga"αèôò‚}z4{qú7ò'åßVX?Éš]q¤Au7È;¹©Ãk¯8µv˜’’>´× ‹¥¡ïA8œ¾Š¨÷¡¾H8|ÀyÎÓÓåÀÕ€Ry)Úô87aJöld¸š®­]¢ðÝÔKçY Ž 7À“Êv~¤?]'[?r–ÑÚ©IÒw4T(vw:ïÙçºKÏWZ;3¼¿'ÿÛßy¤ín£´K«­c¡úÅΰÎs{‡9¬1º—ü±mí«Îû!I|¶X¼þˆu_ZÆ,g˜xÛ†3­ºœÓ,¨’ÚÝô*8ƒvÁ²ãÓ–Þ‘m”îQÆÏAt.]4sÂŽ 0D¨ò£¨D9&°7hÑ"­ß–-åB0è})gcMÂbŒ.‹"üRоØR¨Î7c¡èMÓòr HÔà¤xŠD|±üd˜ ÌÆNDò|?árK0“Ú}•´SHI×FJ‚'BèºÈØüü£-&Éì¨%„.Øú²w…´Ö;\cÇiÈ£¦éOMã.,ngÁ(¨F6×U\¹iŒï¤ÌžùãïÀk>ÊZ„ÑÒ 5/êxìæt 1V¯¶0îá Û¡mX7¯Jâ¸ØOýe‘é_‹©¾tm•‘ØÈðÎë)ŽúÛÐ~z;iüÖ²»—²XæOö: —Ë5"`Ç£ w¨ëNAø1"\Œï•º;³b"Íè³[ÎÌö°óŽ®ì¡Øa­#¸gâ›%'  „6¼m·‡¡Œ«!»ÁfD¡/'dºæx~Ú™ ;A‘ç[+2°³Y¬³Uv2.—¼?h¨Ð_òЮÎ@^Ÿ î±LFkŠà¥h ÓоØáÇdëGβ鮴»Í@ùÙh“Qg7i›m°††> Bö8ÿ¬¿‡Ò½ßi]!Ô±ncûá8ÿ5œ¶&_‚RqÂÅÚ.èK³ÑaÛ¢~Â} uYib¥ÎÁ`@i³î÷ÂñÃ'êuôÝ>¸Ä÷=Ô—–B¾:‰§mD$U§K|_æ)ìÚÕ ÏqÓv£øY/¡Yke‹ÌÏÆ½wQö%ðÚ¥÷±bLÌÄÁh³/FÛ¾¼N™s`&Àªà™*8ø‚ ÔŸ-4‡­p&L°.‚1?búÕÕøƒ±ê‹ôb¬–|Þ­d*(Fìg ·< q\ÚF þDx_†ý…ø``Ÿ*ÛÎVÙÚ^uÿ¶'`œ1>‚ˆ±Û”x‚0OÊKá÷›½@´þO{Ìiy£¿Ç7íCßù•Ì"cÓìžéU”[˪sÁêvÌRÜÁà6è+ôÃÿ‚æÖzTû0XeBH;ls.¥»ÊH,¡…¤`ø“ÀÔÁÕéS:Å‘`‚oÜŒ˜_£<¢l~(aKO;>}€,ÃÕô($þfz:@’ˆÒÃ!ÄœÞ?Àîüg;ìÞt$î`}L…ž@ÝDujµaZÄ+Ä/P¾Ï„`»¥L¼Ýô§Ñ·_êèè{R¤:œ‹jìzñAñƒó#éCsÝÝïA½£%›lýÈYFÚ ßùÙéºúfŽ&]'Ò³£†C±Âž¾8;ºÂÿi|9d3ÝÂ19ï ®²­]°”è‡~åÌ´LIípHw)úÐ2ëƒÑ6ÖHu¥¼a‡‡òUÑ¿mìÈ¡®gتy žá7ôÏ‹°›Þ«4&"ÉTs[è£üx_N¢søµ@yí˜ÎÙ1&@ø]fǘ@C¸;?¿ME©ÖAéû,v~­¼¦üÈ,‹\|¡Û áì! G,ù»¢{‹¦rÙ„‘#·Õ7Òß2½*˜JßÚ¢™ZS—¸‘i5ô5™]}¾xq벀Ĉ®™®Ü²¨ ;{ ) w,éÓ—¯ñÝ’NJ¸ÖÑ׸kŠ3(Ç·Э5k/FÿV¹ƒNMA÷Zó³Úø³‹tR÷/r.0ßèû.Ʀm‡¤Šô¿¢Ä×V†ÆÔ"Ÿ…úÕ' —✮¡ížqx·µ´~#2œ}M¬Ìõ%™zPOkÞdq¤™¤.QÇÝÝ6œÏAkåŒ ÑN Ê&R׋&?8bm²¼œåäs&°'`åcO¨E~†=†€­|ÐìEAžçÁ=æÁøA˜`L€ 0&lvÅÍ€ 0&À˜`L€ 0]B€•]‚™3aL€ 0&À˜`L€•nL€ 0&À˜`L€ ì¼Õî.ÁÌ™0Ø`ãÇUØk.vªªqQsl)q(&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L Ád‚Óãä˜`Uô÷äÍ•R{¦0/ç_Un$ÑEÿܱ'H3øâ`¼WÍuOaÞ¨Ÿzý= Ãô %•R)é‘¢«-JHsŠwô’†~„þ9¾›ï½yžã$N:?N¿qðzŸN[e¬ìØÁÕ©Èë½­¬q”:¶RfææŸ$ô”¥…Þáb‹{¨»¼ã;˜Vß½Mì±8$`‰&ÀL4QN D!0 7ÿÔ œ+¤ø¼0Ïsz4A‚æT¾'ú=ÇEI¢V/¯Wi^¯4k ¸‹à™zJSík¶Y9¾kL¡^¬-¼&õ |ÙïÖ®¶û™Þ‡[«@Ù,!ÕBMjyJ‰v.½Ùw{'5/l-…\®iÒo*Ùª©žºt{ ä)…tO¯-íÝÏôø>RJõ‰F“bH/÷ ´“¶sL´0ÉènçÑ 'Ŧé¾ÜVÑnÅëW[ÛèõlÔ—º¦® šò%MÈû üžGœùfæŽ;J™Qßï ^ä¼G癞¼¯q0 }¹'GÞ‹÷º¶òÛégy&lŠòGŠEÂOB1ðÞXˆö8¿e3÷=FŽÜf‡mŒG¯wŽkUàóO„*‚òKÄ3 õ>Ú²Ø(#…y]y ¢5¥‰º\ƒ¾>zZ^Î3‰ÈƒÓ`L nXù¨/Íâ# Ä©Y¹¾AHdr| U]dø–ßíÿ'¼#WV¿Ûx|RÜîÏËÌÀUv‰¡¸ô„B+¤–-4µÈö×5×wöyåÉ_—ˆŒD”“Ó`{V>ö¦ÚægÝý¤˜¡”;(Ç÷æ¿gE¢ 4 wì1AÓ蔨ôvg:•ÊSXÊzÀ¿UMár©9S½ž¯]6%´ÎC­ô‘¿1jGòR’¿™Ò¦i%gº/ûÍâ>Û0}ŒçÕ¸SI² à}:mŒgö®(ÇKÏúšòòzg¤¿_”f’To¦T²ßŒ3ô~ýú…•Hñ )gÀÄîZalì‹ô^·Ó,7·a–KéP€gÚ~‰:ÖV~;Ÿ-Û‚§ ¥ºišvKA^Îs•þÔfØaö„ãô<Íz$Ìe¸3n/iŸ^\•¨Lt)¹oþÞ8u~#üâž=MXa9!&°—`åc/©h~Ìä àv¹r`oØìLE‰ª™vD+%ÖLô…E,þ ²òÓ[°õζm½xòú›ÊCqË`R€µá~Dø.0¹Ü™.Fôgb8ÌNó/äóƧ×N÷å ¡ð4ú«‚ëó`ÈÕ#†]‘æ"„ŸÑÞ•=ÑiÞ…ônÁý¾\ž›Š‚þ‡PÎë ¨µImÛ¢íC†”;ó¶Ï1º™'LÑGwë×MóŽþÓö¯ëÏúÊ5\s‹ 3 Àóö‚à;&5¾‰Jàn©0Ê-Ug„Ûáò=C=uôè¿ïñûÛ•–¨lXÒô#Eÿ÷0a|)ŸÀŒÇaJÁšIÙÚ­_“¿.Ä„i~Ïó^ÿ¥)QŽ<;Ë‹zè{ò<{ø§áïw)õ{ ³&Îðñœ÷ÏÍ?Sšf.ž·ž-Áú[—H5Å7òWJ£éÅS÷ñ”-–¸df´*à¿ ÜoãÃ0Ûgøìýc æ>„üYêrµ&QÇ9¾`"4ûßh÷àσ¼Z¢âÛ¬Ü|2?"©ÞuŸ¨rî,R^ÁpÚa1€úÑfÑ& VÅ«ƒ<ã³ãŠÍ—ÐWƒé{hÓ7ßF©“A=Û£ Ôßs¨§5ðûÚ=ý!½*3UJ¡c­•/w)¥›’¡Í¦ZT†ìM×äŒó\4¡§|Œ[ïáï"Rèžå”ìòεÛtfAýƒÚÎ ˆõ6ÚÞ}€žn˜æœÌüátQþʈö(÷Z´uÏÝ“&¥ÚþÑŽµµÇXË_™öhÛDPä£eÍ”þàóþîڼ͘X%ÿZúœ#lÍýVˆ#G;;lÞEv¤ZŽÖó£!XÂg²jÉšo3&<óÁÍ€ ìBFEA/`–áØõ?†ÅÎÖ´« åë¶N„Pó6ê^VYÌg0ªý-”€§`¯|Ù+OÏËþ¤®¿½0ÕTWÊ[öšÐZS+6ÊÎ@\k‹n‘Žü„“ÌUæQº¶Y‰ÒÜÑõ*c1>u$FÓ´GÓáýÚOþ7A|ËΛ’ƒ€Ø&J=:¸9ÕëíW£m?®;MSAÈ×®Ÿ6&‡ì×ãv`s Èœ3Ÿ*éafâ%$N¶{ÜÛÂäÆšqz";›Lufdæú°¦DõÂâS§ùÊwý=þ¡{\áo§>Z£²õ`wÿF¡/çJÊS-÷¯p  Ÿ81cëæò‰Èë¥é>OXyÅBÝÿ¬2æÎ5Mó1<ËÉYŒŸ©ÌúÕ}"ŠòvŒ,ŸåL J÷<ç¬Æt¿ç$ç}(ŸTÅBCHø•D±bfÄПÇRáiò6ùŒeÑê‰6²óu©Â³TTïÖ¨·¤$XÂ3T‘ p>šwĺ^ßÛÁ€Ê]Kåû’,—ÛF»kË©•ëû£ÕírkÇLñæüPéýoÌŠý‰Y’ñ¸>1å¯L×:†Ã1P ŸÀ{a1fã&¥¹›>á²Õ.–öKùiâü0(m]'ggÓŒ¹—0ãÙýžÚàËÿjësv8:ÖÔoaè¼®ï¢ÈøÑ®åŠu`þÚ,E*6¹Šˆý˜@Ø1²ÓÀqòL€ @ì©VŸ“.÷@ȨM„QúhM\*6l; ‚äþš.žv†QM4Ì„àÇ[š§:ý#Ïi«XŒÜ¯‘"ØË¾‡BŽÌJ–â„<ÔUøqžåP<,ïi¾lŒòÊ¥J˜×VÆÜqPÒS“â3 •õ€ïR(\Ó0Z=(š ¸#¡ºÁfV¬ŠŒ&åL-Hˆ¯[.5‡VƺSf{TìcÅ£æÎ;˜1‚ ¼¥Ú ÍQܶmV^Ðöhv)ìh¡.ê ›¨=4®[|uN¶Þ'P¿N…v™ó#ç=w–`¨­ÉµP6:R¸BØè#þ¯ø»ƒÌyv·¦{ØLà:Ìͦn§b‰æ/g¡œN»+…f8Ôù˜Ù³Ñ©æ|ƒ¾°mÜêÅFé”5]ÓifËrx¾ëЛp(!A³”â88HDùC¹íøo™")y&ʳu=‹àW¢íL¸÷‘GšØ¡bi±”ßN/t”ï8Ë ]ʾjæœY­'tUSŸ‹½ßÖã]­ •~¼þ#Ѧò5MøŸôåþ±“ |‹ 0"À3 –“e;#Pèõf0²a{2 Çç±­êû–à ©Âv°é?€ÎÍ ˜€û±¶¿*ÅÊ É#hÛ¯¦#F„!0©^t?Ó;®³ ŽÑ¥nJåBü¡wyòºY?À0+fdÍzPXÜ;åùGq‹â H—{{8ò%ØŠÇ(ló±Ö¤°z€8|dô¡H +ÙT|-ô†rÐ#×­ ÌYB½†Y8r¬¦>˜Êº;eQ•1\ ÿ„ üHdЦ͵âH?ëZ©騻S—DÞ—š¶H¡± ƒêfY}ë>2]˜þ ƒŸ•¯uOj³0ãVSÛ°‚hRÝ1Í— ó¦šÍÌaÇâ›î(²)J+´îpÝH—~•2‚0ó+ÿ #íd5 3‡ÖœjÕ;0º>o޵ÅyGSÚ,S‡­Ÿ/ÄçÔÞ[c÷(Ë\ú!f ˆ(Z÷1Z*³·Â6ÁíôQßãÒJíéÏMÐ/¡ìph_dâ¨7ïã’xË¿#ågXÏ2Wç‘mÔ½P:‡o*¡Yš3)T,í1ÖòSzäÐNW„Îÿ¥ö7Ö¡ˆra¤Ø¾uês5ô[;-ûXŸw‘7ò8À;¶«¾wÝ̳ê>¦ 2_3&°K°ò±K0s&L :ö.Ïd¬Ÿ¸Ò´AÞÉG¿ÎPXveèWЦá€QN²¼ù‡CÐ((nä°Ÿa”Ÿ ŸÙdöd§E6åü ÌŸ·ý¬£Uv¨Tir=]ÇSþ*éF¹˜æÍùÞwÀk>ûÉ™ÞüC­übh±–ß‘m‰ã<êi]û\´~5aò¬ã»(Z:ôÁ #0ïÀßÓÚ¶¸Ö¹ÓY´ðìǘ@Ã`å£áØrÊL`§H˜œ;îÎ ø.`l·T{pŒÎ.Á86Vfi_BÐø.|£'Z13Xªäv£ì4¤}1FŽß&J£¶ïaytßbUú'´ÍíNù(œ´Tó¡Œt _;OB;GÕi”ÏöŽa™?¾n>’•;î |Sc3ÙDžã£ƒ ±tǶ¤} Æä„Ÿ £çWÂ?‘YѨð”b¹(éŒÃæ„&‘æWæcy´0K$åUUø6,? }kK)Z½ë>"O¬c ÛôGܪ×%- ^X4Âç…>ÏUv"Öú YÚ ‰íei&'oÑ×¾P@Þ5·oÃuµ#+Bå?ÓÞŒ´ÖvÐNyPU%y;>ò8ÊßqhéPÐ?p*ûd¸?ܼûÒ*ÎD=`¢ó”3˜r-_:Í—óŒÓ?Úy}Ë-­h~Mõô·›Å“eÀ¼÷‹¥=Ö¥üÑòŒæ×`}.ï"kg»ÒŠÙ¨çÒÕæâ'†d•G{öcL`×Ooïšì8&Àœ&çú6Ñã`E5Â~ç=árc$V«€ÂâÖÚ\hW¥Š`Ì=ÂnšÇSYîWMÏ‚âq:Ö[„Íet%ß„Tw™•@`ùiŠ÷~Ú5(äh;Sl!j „¶ŽÖ"v…/–kb¶Ã»ÖSj›(PW‡aP‚™¦1³=0³i0× Ï´½½vÊ;ú®ž÷:û:QG™"¾ÅìJyа¾¨d£¦“Ö&ãGÔ'fXŒ«PætW´Y&Yõ®ûj 'Öc}`Q{ý©ÐI¡Øá‚e×B±Øé€™\AùÂ3”‚ƒ@¹mµJ»§uhsà¡^°•m;üŽ£š…s(âdpƒ™ÕW¹}짦™TV]×gî¸k©G´ÛØ9ƒs|]œþµÇZþÚÒqÞ/ –^Bצ.–Ó1–ö^õ*?¥¿×0}.Îw½ÃJJÍYV;qµ¹ÐùñÈ< ßbL  ìôE߀ùrÒL€ Th¯w÷ã#hWcö\ˆßÙ`hçlK›‡Åc1b_oYÀÄC–(Ã<'aÃÿÉöñsŽ:hîG?þ¾ ;Ýè>Lcí]¯0>Óº ‡™Þ2=l/ŸæÎxæVM ߌáûgì|騢eê?±«ÒÅÊ(}yu¹S–˜f §4¦@Ðþ­½~꿜ác=§o“À<ä0¿5ŒMdzuS¬qëB'v·®2¾ÈÁZ—§5Ó8næ‚E·Y‚;8‘Ž„|ŒÈ? åqÖGl…Ïë"([ ÞN„¹ÜÇÓòFc­@bmõŠÍnƒ™Ís¨—õØ­çå HI1e€v»ùÝTuÑÝë>%5…è5 ×o­¯±Óƒ°^ŽÝ®ÞhãÎY±Êð­ƒòq ža®.Ýë À–Òâf´­võx½O§­2V=³¯—ÒÓÔo-à š}ðœ¤,Œ´Ó…’ùÌÍÆa'¶‹Ì¦Úge)új3ÿ8( Ý.í9;\äQ—ú,à >ˆø¦»‰¬6;B[èbeÕ£è˧xG[ ]8¦n¿Ü¸ X™…ß°–ɵŸ~醾†µ¦%ÃOùõ³”õp~8©ÚPð=9W*}%>ïÒ*(eï 2o·Ÿ›7K}ŸÂÇÔc(?¥U×P}.žw­A)ÞTL•ìmôq3¸á2ì¸Wå±\¦ú,‘|­’8_0&•Ï|DÅžL`×°„E%ï„baË]áÌ |9ãiñ.·+°ýç¼`Àü ‹ÔŸ‚ ôqRiá5–ý²ÔF ÜáBý‰ †Í¢4M}Á޾ÿè°a¥vâôñ-Œ~J÷t)ÃJ ÝxĈâ&éÖ–´ Í7òÕAÓ|Âû7î í´šG“íÔk>’]:žén(7B¾½æõ¿ÓÁuZfy† x;Ù/Ç:ìç!-ÓDÊ9õOµæ˜øÀâý é£èŽÀê3äešÁ„íªeç>=/çE<Ë@4—kË•\j¨ŠßÐ&®º¼ ‹ùÿk‡£c}êÞ¿¾ç¨Û\0xÉù¥Á2]"ó&ƒWƒÔϦòãxƒ&õ«Ñ¾3x,óHl];§¤Ä\cŒ¥øðã?ñìwpeÿÓ.›KOy‘Ú(ýÏPÛ›‹%wB ¿ýéÇÈݨì8t vjõÂCiü:r''ºïÒõ·éˆ^I3$U-`×Òäñ0øeú¯©Êá™?@yiq~¥«ùíœGl¼ë_nÆÞ]oc à‡ •K©á(á{0±;ú¬¾¶ö[ùíÔb;6TŸ‹ç]T¼µ¢ ê¥'ÑàUˆvñ|ä_@ÇÇ+Ù1&°K `0”`-/6Š÷iÙ,eó«Â‘e§]4Ñ|ÙµGÞ«Ï5™°¬ø÷?³g÷•q‘&íòÕçðŠvUÙia«M¶G~¡>ìk‹C_Ц%Ö¶øº¶tv×}ú†Fyj™ÜYùi[äâb£Kh§ÞmuMõxÿøñÍŠ·öì÷R³Ë™óÍüñ÷®¢‰VÒçàƒ×E–/Ñ姯±ËrÑÊtK§æä¬Â,PµA çÃ×Ök+¿3­XϪÏ5öwQ¬ü8`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜@ídíAâ10wìqA3x.Rꢤê€L›ÅŸ*§À˜`L€ 0&À˜@} (!¶I%W!þr]Ó?˜š7z~}ÓŠ5^ƒ)^ïÓiE¢»„T÷%:I)L¡äZ(«éöX Èá˜`L€ 0&À˜H<(M¡|t€¼ÞN)¡ )VB^¼£»ã“^ïme‰ÏQ ‹p™ž¼¾Hv:¢³”ò}ü=¯¥kïL=úïÈŽ“dL€ 0&À˜`L žŽ»Yb^¨”ºçaÒà/$տЗûa=“¬1Z•Ì\ßPeЇ¥T?C“Rà÷|Zcî|ƒ 0&À˜`L€ 0¤!•ã;Ãâ 詉á…yžÇY8=‘‰…õ¨òUán{qaÞ°%‰LŸÓbL€ 0&À˜`L áÌÿlöòãϾæ9i–‚YaÇ÷:{ËüOg•¨6óA¦VJÉw‘àÿ |9×ÀÔ fdì˜`L€ 0&À˜hl xÈ,ÿ%ôW@¬¿ Q&XZ"@Ðâr¤3L­„»Í­¬x$‚*§Á˜`L€ 0&ÀvKž‡\ÜÁßôJy?îÂ$Dù ]­¬ÅåXãQèÍ*‰»Tœ`L€ 0&À˜`»•ÉõPî&9ßÚÅ6¥IˆòAÛéB;zŸ—' F8 &À˜`L€ 0&$H¾'9ßú|FÊ·òA }ÇC>Ÿ€òpL€ 0&À˜`L€ $¦rï:@IDAT(Ï“¼oÉýq–+n僾\N¤ïxÄYŽÎ˜`L€ 0&À˜@’ 9Ÿä}’ûã-ZÜÊ Ð…¾\ÎŒ·*8>`L€ 0&À˜H>–œy%ëoé\ñ& ¤ê€4VÅ›ÇgL€ 0&À˜`L 9 @æ'yŸäþ¸\ÜʾëÑ %ØW)82`L€ 0&À˜`IK2?Éû$÷ÇåâV>âÊ#3&Àö0÷>òH“âMÅš·H[óðˆÅ{Øãí‘Ó?Ç7O“ª°À—ûï=òù¡˜`ID€•$ª . `—@Ö¾K•©FnßXüzŠ-›Ëƒ™ž¼…X÷MŠÛû¤wäežêõ*Íë•f¢jl@nþ©XÈ8WHñyažçtëãV‰CI˜Š‘· ýžã"nÅw)ÕÑJhûÅ—ÇfL€ 0X$bÁy,ùp&À˜ÀK 3×=ÿ)! MŠÛu·Ú¡xÔø$™Þ‚t\Ÿƒ¥¾øZlW˜e-‚‰ÑŒö®ì‰Îø™9¾[°³ÈÒÕd°2Ê&I¡z !‹!ø,]m†±-]Ê& ©Â5—J~érëC§xG/±3‡Ù×WºÔG˜¦Ù aîP¸”j‰ò˜6M²Ã òNnüý9¦Âóñ%Û—l:Èõß4Õ½Óýžž™~{Q¢¦+%.@¾›‘þ‰VX%ó` õ2Óó‰Àúñ(g_„i3ªºÔË¡xXѦù²1K —*a^™„eËœÉö‡yÕl:Wº¬2S‚m§,)Ê÷·ÃVßu(–—Û%§@ijbÊÏŒ×%fe®Ї•¨÷qrÜÝ“&¥Ö–©Öï’t¹Bgh‚™ŸGkгm[Å)xŽöPÆžt†ñz{à<”Nôиnti]„zšSà +V”]û3®6Ï™&_3&À˜@ˆÏ|pK`L€ Ô“€T¦µ}.FØ;Ä’f2„’ñ¿Âb4^yO¹Z9ý4]l …ÐMW•­{±ÃÖÖ`¢¸)Îðºÿªr‹ôŒ”Å[6—QØ.‘÷â¹Fî@Ñh‚Y‹…Ît0û°/®µà¦bRŒ–8ïÕt^èõvÕʆÙ×$ŸÇ78Þ§ ˆa§ÍòÝZ-M©i‹ä ƒ˜.ÞU¾Já.¥yÚŠŠU?Ç’ÈçpæÅçL€ 0& +Ü ˜`õ%àr-kÆe5¥¡Æ$• D½§üeµ™‚3K ½YÕ¢E…QÍ3ŠŒ‰ªÍp**šRPÜ«ª¨D‰aßÍ;šfe2è7X`þ|•û–E“Fš¬ÓYí]žÉX s=fu¦aQü‘Fàï¨ìdªQÝ_gDÄú›iž¥¼J¹pñȽ÷–ezüPçv¸D?ÇŽ”ùŒ 0&ÀXùà6À˜¨'³?pÅÌ­€P{')?\˜•U]v¦-Õ|˜EŸmª3í|å žˆsÌ´L§´DA~ÐJ–Ó1ˆy:Bè¶uº 9);T™m°ý£±è{ Ò(æËy&Êí:{Ñî_ƒsÇÝY¡ßŒÍù˜øÀt¥ÉXi™BŸO«³Dv†ÇßU20,?<¤¶´ÒŸžµc•0¸øÐô ^·T±Ê›‰~ŽÈ<ùš 0&°7¨6"¶7ÃàggL€ Ô…@¿~ý‚š”™ŽX¹~ Ì‹vHÆÑRrôÅ"êÖÎÛ™¹ãŽ‚5QOì|5Û韈slÑ{í²åL eè…õ¦æÖç‘ÿïàí0—Ú‚Ýg{9Ã…vÎ2û:ýè ¼W# 2¡ªâàG»N38Ç]Áª:¶‹Éy£~ãq(î`àµvݲc¦µÉø3-eP@n´ýÂGeÞ3­íÅA!“,°Ç½¾´H='f°¢óšÎâ9"óàk&À˜ÀÞJ€g>öÖšççfL !öÓ³Ÿ+2ý—ag¦QY¹þãñ]ŒçƒBƒÀl¶ÄOX®¥cW*ÚéI´h™úOìÎt±2JßÇw<†ºÜ)KL3ÐÓ S üÿÖ^?õ_ )TÕDÒŠ‹f ÈûK×Öb7©‹±+Ö°_p~QfbVálÁ;TÓõ×UP¶ÀWÌG"Ü>U“ƒp.Å0i‡]º.2›jŸe”¥èz‡mMÝ~¹=p>´1 ;w‚eï%Ê»ÁIóuìbåL+–ëözw‘ñûÕP{ÎEîÖ¶¾ï‰!CÊûçúoûçÀs}ŠT/EJŠ)wš¦ºÛßdoŒo Œ7Á[¶n)ÿß ¯¤&\ëFð¥Ì1`_Z¥ ôUòà &À˜À^J€g>öÒŠçÇfL 1È4[á^†Îý1ÒBo¡0¯ñÕó÷M!o-ЦÜ1¢¸Iº¼§`†à@ |uÐ4_§uî í4Ú¥)1¥Ú‘ŠZ¾&ÕŸA3øYy ¢˵ƒÿlW'l‹»ÃiMÄ=¸7‚ü£Á€ñ‡iŸay÷zMè·í:sé)/RùMeÎPÛ›‹%wÒÂQ£¶hiòx( ¿@¨ÿ¯©ÊaFçìâ5,2º\[ „’È%ŠpÓór^„9Ö@ܺ¶\É¥†ªø ¦fW ]^U—ó_;8)Zº¦ò˜ß‡X}Jê×!Ñv8:6Ôs8óàs&À˜ÀÞJƒZñ9ì÷þ1¥€­^tdǘ؛ ÐêôÕ›öo'ö[åõÞ“ èŽLšÖüûŸÙ³ûJ2ߊ*>_¼Ÿ·!…ÒŒå·FŒë¼Ÿ8yåΔúÚ¹fljÓÚåYîüâz´’Ü?~|³â­}'û=Ë#ïϘ1CŸùãï]E­¤ÏÁ¯k¨gtæ;8?¿•)¥9uôèªë?œpNfoB3­Ùšˆ{‘—»ã9"ËÀ×L€ 0d 42?Ä.L2€á20&À˜@ˆ)Y¹¾‡˜`L€ 0x $Jæg³«xk‚ã3&À˜`L€ 0&V>bÂĘ`L€ 0&À˜ˆ—ïv/AŽÏ˜HRJè7¸”¾8I‹ÇÅbL€ 0½+{a¥ó#3&°w˜îË~sïxR~J&À˜h,Øìª±Ô—“ 0&À˜`L€ 4r¬|4ò äâ3&À˜`L€ 0ÆB€•ÆRS\N&À˜`L€ 0&ÐÈ °òÑÈ+‹Ï˜`L€ 0&À V>KMq9™`L€ 0&À˜@#'ÀÊG#¯@.>`L€ 0&À˜h,Xùh,5ÅådL€ 0&À˜`œ+¼¹øL€ 0&À˜`L ±h´˜;ö¸ < »(©:H!š5è‰*§b›Tr•Pê/]w½?5oôüD¥ìéXõ4ÎRvÞ[ë?ÙëˆË×pÂ}_ˆåº¦À}¿áXsÊL Ùì­ýŸå>!ÂußÈå>Èìñ¹LOÞÇ”B¡/·Òy½O§ŠîÒ49Ì4ÍöRJÕ,=ÍlÙ<]¦¹Ý{Ý,NY `nÞZ¢¶•”iJ)©iÚjÃ>Ö¼¬é>:¬ uvºÇ8y、mMÛ~·Ë¥åúßcꕤ¢õý`Ð|´SJ§'¼ÞÛ¨ïïIŽûþžT›ü,qˆÖÿMS=ÒÑÝñÉ=­ÿ³ÜWµ¹D«û])÷%Jæo4ʸ¯&µ§‚¦Ùñȃ:©Ž8@uPG‘ž–Zµfö«’²rñÓ’"ñÍ/ËÔÏKVJ]ÓV•••öf¢ïà0ñט•j£Ú­#<禥5™ŽúïÀõ¿6r~䨢õý@ pÇ¿Çy©ï7æ~OÏË}?j­³'ˆÒÿ‹LeÞŽÁà÷F,÷Õ\‹Qê~—È}{•ò‡½û;µk)®é{’~pçýj®‘½üÎâ¿Öˆ?˜,Z÷· £ž7æ1 â”Ææh6K¿}ÔCuÝ=®S»}×c«B.ï®"°£ïoF–æýÓýPßoŒýžqß' ì˜@ŒœýF!# €<cÔ¤ Ær_ìÕ²£î^îK”ò¡ÇþxÑCwÆY·ÒùŸÎy†Ž v2ÔÅ#ÇÖMê×Gk»oóg±g%תESñkë7mk6níÛã§ÿðù§ß4²§¤Oj›î[ï÷Üër¥ŒGý ®ÿFV‹\Ü]J`Gßß*WmØ|î±§÷ÚöÝg½K fÜ÷ãgÈ)ì…ªôÿõ›Ï;þ̳¶B.›×Q°ÜWÇJÛQ÷ /÷%JæOæuòŽQÞsiÆ‚§ºóŠ3eŠ;n]©ŽUÚ8ƒ'âEÜÜîÔüëîºï|<‰TßôãžÌŽÊGåtS¹©ü\ÿÉ\]\¶d"àìûRjnž}Ê×ú=a侟L‰ËÒè8û?ÉN$CUö«Æò,,÷Õ³¦œuß侤U>23½MÜn÷¿ÉÔêæ‹OKv¹žÍ¥a£·mZŠæ-[N=òÈ“Z 7[iØŒãKÝR<¨¼Tn2µâú(ÇÞûPŸéØv•––>½OŸ«›‚@Ò¾ëµÃ}߃O™@} „úKA2ÉRõMgWÇc¹/~âEîKÖ$-¸¯¼›“?itìêN€¸]wÞÉš©Ä~=Î:s0R Õù´½rÒÖ{eùR©¼Tn®aÇêH€úþµçž¤›Jµïسû݈žÌýžžŽÞITFîûDƒˆƒ€ÝÿI†"Y I%ëo¾ó)YîsÒ¨çyc‘û’±AÒ,‡îrëCiW#^\^ÏXøÇŒfÍÀ‹F@RðGÚ\²Í&Yõ^Y¾&T^®Ð`ÇêIÀîû©MšÜ…$’µßÓÓq߯gs4&P»ÿ“,…0Éø›ï,:Ë}NqžÛuŸÌr_2*ÚCFž€=«÷£ít㬎•Ûô¹üš“p™¬³á‘ÏÊr¶áúçæËâ#@}ßjwÙ-Y'"¥dýà¾_5sl&•õ’¥H¦B€d”÷ìr³Üg“HÐ1Ùå¾dkŒ–öëNM9—> Hßñ`?âH<÷mÛîl¤–†¿duŽ|¦Q9¹þã¯wN Ø}¿iËç$a¿§ â¾ÏÍ” 4»ÿ“L…,’uöƒå¾¨»î“UîKJåõЕ¾\ÎLL‹$ŽÍš¤*ÍåêŒÉôŠž'Ó‹È@¨\M¨œT^®Ð`Çâ `õ}¼K¥¦wA2ÉÖïéɸïÇQ¿• 쌀Ýÿ¦+þ’é7ßYlûÀrŸ“JœçÉ.÷%›òAåÑ…&Û·lžžle‹³)ìÞè-[dHÝíj‹R8g>’…q¨ÞC32iTN*ïî%ƹ3=ƒ½K¥.ÛãiìÏdé÷˜ûþžÑÌø)’”€%KA¦BñHùH¦¾o ½Xî³y$ì˜Ìr_²5D*K²YšÛÍÂgš 4ðÔ¥F[nÒšB’ÉþÛª÷Êr¥R9¹þA£ŽníÆ­b͆-uŒµkƒ—WÄo®®1ÓY_/KV¬­ñ>ߨ;êKš°ú~²õ{zîûu¯Òj1Cß§Bÿ¾|(-¯¨V~òX¸¬H|úÝïQï±gý „ú¿l†’é7ßù@,÷9i8Îé÷2—Ìr5Ædq¤lXÓoJ¨z+9“_'ÞU\Öû¸z?×:|<`ûä×;DFܲ½D¾ú±X¿y›˜pÏ5õNZ)‹¯-€ØS°ÄZÕ;Ñø#†ëIQ{L©,gü)ï)üµz£Èê-‘}ÇÅ¢ó~­ª<á[Ÿ~/‚fPd]yVÿ†º°ËBékšÍÒ›ˆÃè nºðTë:Z¾¿,[%þýú'â±á× ·«úëç£y?‹Ó9D´»hÑÙ¯ž°èÜò“¥ßÓ“p߯c}ÖôûÖú~0hŠ'^üHÜrñiøîVíÉIùX¸lµ8ãØCªÝcøTÊTÉÔ÷í ¿ê+÷Ù¿CÑ~íLb9&“Ü÷Å‚Åâ­Oo-­[6½Ž?Lô9éðX£Z˜$•û,a¯Zaw£ý@ê…©Aî6÷Îgß‹6û6O åcYÑzQðÊ‘â®.¨Õ¥ÔQBô¢?â .Tï¡2¡|õW>“áavWn»ôôÝ¢FÞyù™xA6KWn/4OÚ­ƒ8éÈ¢b8öÐ.âÈû¢+Q#°g‚X}*Ùú==÷ýÔpcèûº®‰G†]— ß²@Û›’ÉTÉô›ï¤z°Üf²tÅ:qÁi=Åv_ý¸D¼6ç[ü¦vÍ2êó½Èä”û!цÅybkÀ´5dB”‹VˆOæÿ&zÜI|øåÏ"-5ä¡á‘•Ÿ¯~õ³X¹n“ؿݾâüS{ˆ? ì³ð‘šâ_.X‚u,ŽBü‘Îl˜„lÙ^*鲟¶[Ç6¢¶<*AñÊ̯Å‹þ¢§¬Ÿ«úœ`Í¬ÌøðkñýïË…Ë¥‹³N8\ô=ùÈj‹Öý-úœ|„1sÞ/Õî×Å£R¶0o¤‘ 3T«l<óQ—ZÝö¥æ l«(n¼ð”ZÛåS¯*š¦§Š~}i÷e!¾þy©xû³bÌÀ+,Ójk÷ÝxžhÑ,]|üí¯Vû§ëh/¿¶PÔi¦k‡6bÖ׿ˆ?V®³”ê_,X„™ŒCÅKγúa÷.íÅ œS>$ŒÆ ï}!¶—”‹S{LæV[§2U ñæ'߉ïûK”ÀT#ýHÜóù"½I Ò©½ÿP:ìÐÁCïTê_ÉÒï©Zœeá¾GCm }Ÿ~÷*x]\}ΉâèC:[}þ™7?Kð¾èÒ¾µh»Y…÷}›DbŽ•ýßÙßvço¾ó¡ÂeJ”Ü÷Â{_Š}[4« ;ýŒÙ´!§]xzOë÷‰2~ããïÄw¿ý)ÊÊ q`§6âÆ N±~ß"å¾Ãh/ž{ësñ+Ì„if¿çÁû‹+Î:r¤»Öß×xå¾›.:5ÌèŒcÅïà÷bYÑѳûþaÿXO’TîKš‘o›£Ýí븎€eGúÕOËÄU}NÄ ®•%••,!í…÷¿]áwÏu}!u­Z4Ãq‘Ñ$UsHqë%§‰n[[eØ·y†¸¼÷ñ–@0‚âÏ~°üw–xããùâë_–YÓfW}|ØœäÓ–‰[/>C\|Æ1â³¾ëÿÞj¥éüwú1ÝÅ9'sš„½+h”ÁæLÇdpÎòPùØÕƒÀÖâ2±­¤ÔŠY[»ÜR\jýøÛÙ”¢OlÚRl]žÒó ìù ÅË3¿›·cÔå;ñEU<()ßBa Ê@lTO<ê@+*גּ”‹ãëBß›B·l‡¶jÏÿ}ç ‘ …ãúóÿ!6À¬pÍÆkV(Íù¿.÷ßz¸/cŠw;fwH!еÿXá6g?³ýv÷ÑY&îûõ¬ÆÐ÷éѨ—cP T,Å`ße½ŽÝ1 G¿Ó¶ã¾o“HèÑîk M4‰%´\Ô^Ÿ3È.q3â6Áté¹ ¬bÒzÂ÷¿ø ðI–|×Jo:ä½hrŸK×EgȇYWö‚œvšøæ—?,¥…ªí÷5^¹ÏÉô÷å«­:’UãpI'÷%Ó͕̇bBÝ¥½Ž‡ÁähÀ_b ‹„%[ƒ\úS0âê´§£F·o‹ A_‰´5Nr[¶•X/J²±¥ÙÛÕ”Ç<Œ(wX7qfUœîç%+¬‘àv­š úÛ¯u 4ìåâÜå ÖçvGO8ç8 ›¬åŠó±voôšÚåÎJEíŸfO~î=(›EÛ}›A®>+g§ñ^æähö‚Ì­ÚîÓܾ…—tP\~áq–âAžN›ìYézè çŠC»¶Gmoõ;ò"ôÓNXwÕ¢i:î…¾ùc/ÀÛýÇ.^c<Ú},ÙÊn—+ÙÞIÉÆ©NåI¶¾ïü½¤ùíÏU–%Ù³“[ò×Z˲€Î¹ï…„»dî_ -m3{f4ÈmØ\lÍv¸¦kä6jkkŧeÉ…¦,_¢É}}0ðKí–Æh ð/K‹,y‘â«©%Jî£ßHšá'«t‹Ã%Ý;6•8øFJ3äšfÐFO#/¡²®ì-ž‡Éǘé¯ã%xë?,s++PÄ?Ò¤iÖ4MK %óú³]´< Ì@Öy¿}í`Ö1ˆ4¡ñ/Åtó· ÿ ߣKÖ%Æœ­aËH©7–r6<‰xsˆÖ.£¥IíÚéhÁ7)´3Uæ½k\@Nq†`æÌ®h„éŸPXþ3Šý¯èNîȃ:…Ï'´ž‰\g¼øÉÑF+,®³Ýat´fMÈt’Ì[6˰~,v_ÿ±KÆÇ†#À}?Ql“¹ïÓo' üý£ÇÁáÇ¥wÈOKVZ×Ü÷ÃXø¤œ2™ÓB¤D©<Ͳ¿yîÓïÁê¤§è “÷hŽ6úyὯ §­ÅoO:—XV1ΰÑúX¢ä>²Ðyò¥™–iØ%gë̶ŽçÉùNM&å#¡š¯³vjZ¬ÝµCk‘}û%P–‰ÿ¾û¥Øgnº¸6}‘nùê ˜ªûIÜs}_Kø™óÍBËÄĹ,>Z´–#Å­Wí¥´Iû&-»×q‡"¿úïÊYÎz^7÷˳»ó±˜3X´vIOBmÐVÂézåºÍt;R hº·C›}Ä_þ$Ž9´³µf) Ê ™&Òì")ÕNçF?ˆæÚc¶Ü†ÍÛEgL}Ón8[±¦ÊvÇ"Ï÷0]N#¤vÚæW=¬2è0 K¢þc·±“¡ß%C[½Å\ÞdîûdCOkÅ6¢ïÛnL²lÇ}ß&Ñ Çdêw R2¹ªÉÑnjÇcGÔ·>]`Éqvjg™WE†Ÿ5o¡X±f£ð ºÊZçA–‘.ZK„ÜG Ìä³Ds,0¿ý²3h©c¢]âS¬c “ÍÆ–€à¯á552¡FÛKʬõ$8¥Ò‚V¸ýZµ®ZIAf›¶”Xþû ÌFL¿}Ó-Ëí˜ø]GùOæ^ßaÆ„lßiôvž“; îæý²TüŽ‘eÉ¥{N“”(I%Ê«’qxÝG¢Ò'd,S<ÏÓàq©½ühôGçuuun‡ïi¬«×o¶ÌYq¤tŒ` íæâ}f5úˆµ›¶`c†š7; u¤ Ó Å‚Å+ð"­“ª­L¤Ø4Ëøö¾À­êX{TW+mïîmÝ×0¦Ø¦CBMB B5 äAz#ïOþB 6!%€é˜fc0¶±ÁuÝ{[ï®·7­ê?ßÕ­V«-’®¤+éûî•®n™óÍ™sgÎÌ™cQ)Àkò'tÀ•(pq·Ù;•‘© L¯=#¢$P~ ɵ÷e’ ”3­ÐH>Ë2ú+d{(*á-È>è4šß;R¥"{E‘²/Pk¯èTBÖÔº©Z÷é¢+özßÑêEç‚Ñp*ÏADAr!”`½!O9ò«ç:¤†d°%½^Á%/­`}°….8½Bñb@/ÊÈÁ{±lUbv^ßæaÌ©c=„79»Ï¯¬âõ ‡ŠâåõyRZ"ð$¢@‰ÿï;._µ?uêÚÉkn ä0‹×çX0{"½½z«ríœ_“ö®æ¬400!ôßïmPæI¡ƒ.Èœ…‚˜ØsæN¦sO íÆ¾ß!KÿZþ)ì§Y“F÷˜g…‘Qd$yè™åŠŒà%€9Q—-8EÊO(0å±´@`íÖ}<9{Ÿ¿®?¿óJÿçÁ|ЊìÃó#ëa^ÞÛ¹OB‘²?NÊsÂEs7ž[¾–Z8á Ö¥º•ûÒBßÅ`½ogFýÇ«Ó=¿û§’í¼S§R ƒ)Ñè}uM-„L¬(BoÄçÙ“G÷gƱd.°‚¢*wÜwÿJÜ`é?[ˆ}±Hbœsó÷ï{eJù¨9ß¹á¢(n7¸K1Òªçõ‘ö3°`RRº"‚(ð‚+Yì¾ÓáRæ‹ÀÈ,5ÁН¶LK,\kþGýáé·ióö=;þù§ßÞÃOðVÇ&™ Î%¼¡3¾@Åâç;ß“sʾvÏþ4cÊøÉñ࿊õHÊ[¡]ö碎G¥0Òƒ rt@.ðÐ tÝÅó”ùX0Ð?ü|§’Åä¡ï}Í/+ñ’Ÿxà«g@ö·îÜ¿å©?þêkü ­È=ª+e?VLÄ}µ û ³ÃîPßÉRöêì!ÿÛ÷ÚðÄïÀ•Þùó÷ñÔû sevEºJïC ÒëFRRQïSKçO[χhH¡VYÆoˆ± 4â´|ŒD •ødó%KM*ÕIÖE" )ûc$ÏHúG@ýã#•HB €ìSÁ«¢‡8M’HR )û)ÆPY‰@s>º|¤D YÀ"œðâUutÖ)i}åºéÒ3ò±îV?ß´–æÏ(WÖ¯ÁXgќɼ0á6=´ˆ×™”,Õ•tJ$]D"û+Öo§¡œ‘n'/ªÛÖÑI_½øt‰§D@" è…€ô|ô‚DHþ{©©WV;_¶òsÚÌénEÁÊÄÈÑ?sâHzêõÕþ…‘¶ì=ByñÊ϶Ҹá¥ât¹—H’Hdÿàñ“ôØK+©¹µCé’¨º’T‰€D ŽHÏGÁ–’$X\iÿ±ºï¶Ë«¨;œ.zñÝõþ* Wúå g+¹Ñw:AÛy´sÚøáÊïsyu¬D.‹D@"|D#ûe…¹tÃÏH¾JKŠ%¸! =qƒZ>H"\´Ù;y^‡—²»ÁÌÍÊìQäãÇ¢L(™¼,Â4DY†Åêe‘H’hdÔ‚d¬²¤Y" ˆ#Òøˆ#ØòQdB 07‹ŠóshÃŽƒŠr…z4òÊã{T+«ï8PE“Ç I¦êIZ%>²ß0ò°D@"  2ìJåM$©‰À—ÎM/}°^ûpMÝsþÆø‘e¼úønÚÊsUm¾ãù’÷ár!±ç«Ý$ÿûçgÚ¢Áy8](Vs>\UG;§úæ6jjk§N‡‹¨ÛéÍP¿v­a6R®ÍJ¹6š2vRHzNOŠØHCÝB‹-¢ :~’*÷£ú¦6jlm'‡ÃÙÍúHÛ@Šð¿W;o·;¨ÃNY2Œ”Ÿm£¬®ÕÏ“± ¾KÙ­|iùî¢ HÙï›Kv–wȾ"ÿüÙh4PË~NV¦r‘”ýØ¥H¿¢f)sHÊ~bY™¶Æ”·ÛCNZùùNZµq'µ´9Øð’QßAzcé¨Sy)ƒ<®\ry2é·P¶ÍL‹fO¡…³'S+rÂQåaò&ƒBmÆëwÒŠ •ÔÜÖÞƒ˜dã{G'mÛw”*÷£íûRK»#$¦&£Žòs¬4nøšV>œ¦Îiy š6¨¥ì‡deZ ”ý>ÛA-­$£Sì ”}m04-at8Ne¤ûÅ÷>eÅÓA™™{©¸p3ïw±1ÀQ ŠÇc¡ŽŽ‰ÔÚ>ƒ^]å ?ßA×^0*ÊG(k)À‘%ö  lÛ{”ž}k5{9:ãÂ{ÔJëüw³‚ôÖê-ôÎÚmäpºÉhhgãxäe™`ƒœåB¯ë$7ƒÜ®6¦s¹-—Ѻ­-´fË^ÊͲÐ gÓÜ©cƒZkíYÊ~ìeKëO²ÿÏ·ÖP3<TS–C Y&rŰÿ5rŸ“ßꤒ;÷ý›xÀk]wÁ©šéûˇŸí¤×Vmf/˸AG™&j*²‘˨'·^§à…ÚÌ}ƒ™î¬ie|ˆO—ËÍ#ÞZÁí[ŸlãÑšj*+y,–CÑ#:À ¼Ùl›•ÍnEõ—ÒÒ—;éÒ³fй§NU\ÚÒ 2ˆQü,ÚÀ{ŸVÒ²Ÿ3ïk˜÷¯Å…÷ [«üWFƒØÿë‹+hçÁ*²en§¢ÂUd6Ú^¯‰–ñÔÜ´€ž|ÝN‡OÔ±2KQBÚØ‹R¹ÿ8UÕ6ÒɦVr¹<|_*™4²´ÊG”Pa^¶òœX…p¾KÙ;Sò$Ñ|²¿‘Ú3 ´wx5[Mq©/ ›Úœ eËiwRym+÷ý«¸ïŸžÐ¾_àòÔëŸÐºmû¨Éj¦Ã# ãÒ>Ô@:6Fr¹>#:èßïo ýGkèÆ/ž©È~'÷);TÑÑšªkl!{§‹eßHÙ®9²´€Æ/¡ÒÂÅ[OÙó“Jêˆ#ÿµÊûþøšJ¿‰6.e_;\MãC4>§ÓÅó8j³–¯ÝA6k%¾Ä£¹Î¸sÆNY骫»ŠGÂ|SKÎ;…L&† fˆ6ðÞºJz™ DòõKÿFüZ;ìäâÑK=hdÙèíO6+†GaÁ«”µ!,èX~`°X-;¨¾áRcãúq;Þ}øíç¹4üHÒéÝdB(£ŽC¸Øsâre“Ûãë‚Æ±rÖÌ ~‰šŠˆà»”ý°XšR'‹6 d¿.+ƒv³·Ã“ G3 žM#òi‰fîû7+sËâÕ÷·±Ü·pˆ©ÓåR”þ<ø´r¿bx-°Ò!öt„S¼¢ï¶u¢µë0,l u-üñ„2—ÒË8;FöœèÈȉ tÜ÷ Œ`#䌙ãiþôrÿà[8Ïïï\Á÷`ÙO$ÿÉûþ°JÕßD²¯-§ñw: {g'mÙ}˜ÞYç3<Š‹þ•PŽÀèQh8y ½þQq^ÇÍT ÄÎË¢h[÷¡e+}†G¢yšÅ‹ÿH¦°mïZ»eí=Z¥„#‹0wdá÷Á|©‚ü7¨­ízkÍ666Z(/çS%”Ñl>x*ÖqhW u´O¤#Õ³è¯ÕÐ{ë¶ÑWÎ?çóÈ©:sG¤ìÁž†_»e#AñÜ94'á(Àð“Ž7Ókm‰Yßåk7{3WsXäîÃÕÔÈÉT‚  Ú8Ä*\Ã#ø>G ­TÚÒÉQ»ÉáZÇó2©>ËL-½½(™º•×ÊI,ššéÈòuüN®¤«ÏÃahÃU Ù –ýåëvj‚ÿñâ}0Òñ{:˾–ùÆÂ-àñhjn¥W?Ú¤„ZÁã¡•ZœÕÅôï÷ÖÓ¸a%”eËTF¥´7«¼Â¥mÀΙšž[¾F µÒïQ—XñõÆäÑ疯將62a‡e7U¬Ü7ss³WÂÄ£ œ|!‡r³? Ú^çãžyyïð½ÜŽ7*Ïèu’r€cÇ9äÑœ[M¹¹«¨­}:U×_H?×D—ž=“Îa/ ˆÔ"e?4òétTÈþ³¬à"Ô -Ð3óH½øþgªöý0:¯¥§ß\CÇ9ìÉÃó0êÙãÒZœElxx´AÏçXœÊà÷cuŽEXdò|/ȳô;q¿Ãd ŽüLªâ-Ÿç\Ž;ÙFþg%;w]¶`–ß ¢–ì¿Â^‡{j‰ÿ±â½*ŒL›¤«ì'ëRÞø@ŒŒV˜ãÑÁîæ7îáp—2Ç#¡V}5 ÐR÷¨¹•Vò\” æMUFÐñFÚùöõ¬t;.ÚÀ¶SO.Ç-ñüP›ÿ¨3:Þß]¯´'³©žJŠ—S¦e·'_¸C,ÛANöš°oo³nQè;ÉaˆËVz©Gh¯\4;â0DÁw)ûa³"e.m²Éå˜ã‘¨P«¾@={Ù È<Ú¨Jß/d9Ïg|Úlh,˦“œ "u¯Àˆià­ÏØ0[ÓJïsöÁ:Nw~Ï1±‰Tð=PöÛ:šã¿Ú¼ï«¥ãqÑÒIö“‰Ï ŠxD¾‘Oß<6^·c‡\!«U<&—‡[KÐÚ>ÙºGñÒ¸8&ôË¢ ¬âtÊZå=j¨ÿ}®›ž~}µ¢ÌÀRög²fá ·ôúN6’žãžk9Ün_Ö-Ný‰,\¨W8EðO)ûá —:çŠ6€tª˜“¯Éåá"º@ßê­û¢êû!#¨ó«nä¹$©†Cž>•O5lÄÃð·ÞçcîȾÒl:ÀsN6í>B/¼»N •VCö×ï>ªYþ«Åû@,ågd–DÄ‹‹ÒEö“ç)m|Ë×Á7ìœ:ðÀ±Z^ ÍÍ £mÖ,Ÿ@[{f2­ ^›p•.ÍV.„‰6Þc­ -óðDË_}Ý%€)ƒxdAþ›œn+½½f+Ëm¢„àe2XY|—²?°SôÑ ûXÇ¡&[°¢XÁúÚyt>Ò¾õ…Œl¨<@Ëy¾Õ‰\ í’£¤Èͱ¸ïqžô~Œ·5[÷³r(lDð=PöíœXËü–÷±àC2ßS´t‘ýdäUnî¼xUf¹ÚÇ/!LˆÅ:Z-  4îYù+ºÓMöc…c,ï›ÒÆ:+(ïXÅY®š8æ× oWÖZˆ%¨ÑÜë@€Æ†–6…nÐ?ØN7šç¦êµ¢ ÔñúZç=x ÿQW¼¤ßY[É©;<‰ý•¤f+3ÌÎYEGjé0§ë…!1XO à»”ý¤nQ/ÚdßÅ™Ób¹€`T„v] ú\zCD}¿¯®%…ê9P|”}¶ÚUw¢áý@x¤ãï¢ ¤ƒì'+µ+* Šô¢®.㣳“ã¾yÓšT¸slo›ÛíŠñúQY"C@´¦6^›³;%C‰„ÿ¾‘llVB²²>UŒ­d¨o4fÙ6)?WrÆ. v”à;)ûý!œº¿‰6Ùï4²6›¥Ó¤‹¨ï‡²ÕÖÞA«7ïå‰å„LRÉ^x¾ VYß¹¿Še˜×%áÁ‡Á½ßû&í«:‘ò>Ùù úÓIöc_<î©}‰Œ(cÂõ…Y/œœRPÎ4^@£ÓáV-v…ºÈmÀáp1ï;ûA‚ÎŽ„ÿ¨+ÚùÖ=GùM”æ" ªê€Õë;ÈÈékê›#Â~Ñ¿,ò]Êþ€§ä m²ïÆ"6IP@g¸}?rÈVwph"æz¤B”·³u’CÉ0g‹"B©oû*|”ýdà$¼ï ‡t>ØR]ö“™Ï)k|€)Âõ3÷ZIÃ+è–aWѱL´þ^XÑ=!6W‡ËŸâRV7›9G~]lKÀ]½ƒ9€Ãé\æ+Áw)û `˜F)Ú@ºÈþ.^HÐËž‚æL³F8 Ûg|(a—g½|—²¯þÉxÑR]ö“‘7‚æ”5>Ðè|)qâV^’Çô`;‰ÿùâÛa3õ?Ú#˜)÷=¸)6§ò§çïZþÿQO´Œ "ÆÕhªÒrÕ¢ÍåÎ&·'“¬¼8B¨|žþ³^ò]‰—²æ©pr`H¦A'`®ì‹““M-ne$Ì—H…‚­ÉÞd®‚ìàoû*|—²ßJ©}<° ¤²ì';SÖøcü»sXÂýuZšc$÷¯x ‰:h޾$!Hà×÷ëJ£ ƒÿ¨£o”ÏÅiš;yQ®6V*|²Z[ç* ÉËäù[>ÃÊÇ`d¹›ïRöÃG=5®ènIVŸdó!šÛ;ÉΞT)Åͤç0«¡¼:B¨|²ï{'öWÇn¾KÙï§Tþ­» $Y-Ãý$«Y/rSÚø@mý°ŸÑ’^¨hà”eA»ÈIj€c²•pøúA!ÇÈ ‘•¯GÛ)EË‹NÇPjj>‹J8EªÕÌ#º\Ïpx)ÎçšÁÒËóÂá},éH…{'ïy8üGýºe_O†$ìëBµ³ §›ÆÔ¶R–Å@<^Èr¨sCç'ÿÃá}¨zËcÝ$ïAy:ñ?åî¦(?IÔEÀ 7sjÜÄ+úèdz‘A«%ƒ÷…êV´»™ŒV^“&6YuìöQT]s39ýè„Rã¬ãgù¶>ÈIÐaÐ T!ùØ>°}ü’ØÃ0>Ùg=Ó5p*ÚH©í¯þV£z2`åÓ4’‰U±)%V–}½²ùä?Rꣿ®¯:¸_ÊT±þÑS*ï 6&nƒØú*ð7¢È2xúFsð÷Ðä™±°z¿´ðqZ8ëÇþúÚ¬%tÃE/Ó·¾ü™ÿØÌñ×Ñ-_|‡¿«ÛcQ?Ñ)úAMÌ2-ùôk·SYá4*)˜B×_øoúÞWwÓ½Wo¦ËÎú3™Œ™ Š×œ÷ 9ý^Õí¯.ìDˆ”!ùÙœ¶ªJÃÔ1WÒâ+V±¢­§)£/£;¯üˆ¾{ÝNº÷š-tÕÂǸþV²eó±]T”7!êgŸ¬»šëA9<×ÃÈ/v#+~Fƒ‘CÊ ~E¤¯‡ô‡U_× t<°þâ\Ÿ·_ö>Ýü…·”CjÖ_<ûXÔ'ðþ©ø9Z̾8r½qñ"Χ£+G w¿x.­»òBzë’Etã„1= +Ê´ÐÓç̧—žç?þå±#è?œ­Ê[ ¿ºà7EþùÏÂ,2cŽ{ ¢-Ÿ1‡î>I¹M_õÏ2™è§§TЧW^Dky{rÑé4£0_¹&šú—Ÿh!3QYœÙIJobÙ‡Üch¢?¬ÂÁDÔ¿¿:ŽÎ¶ÑßœFŸ}ébZ{Å…ôÌ9gМbßÀÏÒ³O£oLÎ#Cž«V}BÞ}²õa–'ut6ÐÚm¥?ÿ{ýëý¯Ñ˜!gÓ)ã¯çù&µ´yïó´ð” xÏN()~†¬ÖmTowѺýõÔÔábå# ƒó~b|!É ®¿8éÌß&›¥[þÕ¬ ýâyrÃð®"e„ó[Ó&ÒÒ{ÈÃ產|Zº}]ðÆ´ìÀQºwÚ$*dÊö‹çžÁß{f˜Zvàeóäï/Žîã•óíØ¶Œ*ÍUŒ¥¡ ƒ½$äy§pæ•Ñ“»ö+¿÷Ux†Ù¬t×ÇŸÒßZANö¾Þ=Õ7ðMý÷ É¡š 59‰Öj¢šf;<øàÒ_ Ä. øü· ¬u¬³;h[C]±üCºøÍÔÎóR¾ÍmåQn/_Ÿ8–ò3z¶ ÿCúùH?§ÉŸúA@`ØÏ)!‚!(ûc³³h/'r¸bù*úæÇëiÑR‚qX¾Ám>PþOrZè—æ~bbàiƒþ,hÇ>•‹6ýÈDüôiwÓV6,Úíõ uÅù“ØYÊŸu¬lÞá§ØãqÑg;þN§OûmÛÿ’ÿx¸jvÐß_û>eÚˆ,<¨näèž~¼~ƒºýä)4ùÁ%:¸e4^~ÇësýúéèÈdGqfV²ó»¸©å2…ÿPÈ0çÐì‰7Ðóï]¯|ß~ðÂæ+ûèxÝ&^zmÚó,eådÓš5ñëôñæ?tÙ®®±•–­Úʼ7“‰Gñâ%7ÛJ·\~V×H Z¡Žê›ÚiùêÍäá°…¦–ù”iÙGÞ¢-SÇ\ÎáOÜž_Vnu ê#ÿ-Û:NRkG5edä*ÇÖU>Jw}içM¤ÚÆ]þóÂý`6§’¢Ø‹3Œjk¯£·6ì¡O¶í§,›Ìæ š¹œ^ëºøm ýÑÖ6úÙú-”‘Mfk&XÕ1=ãr²è¾Y=.ßZ×DmÜJv¾ÆÞÜBV½‹Êš:¨Áj¦z^¤/’rÓüÊÁ£ÔÀ©­Qú«ÿ7ÙðåÝ£Uôƒ™“•¯ÑÔ $î)˦£VšTÕDïo;B/Õ5Ò9ãèÆ Ÿr/ž :_9x„ý¾>ƒÄ‹4¦ÛÁ .βfÐlsøa±Áõ龜-ÜÇ<´e§ E1DÎV¦|ÿüd=íã̃ו¦¿TîöŸ3ЇݼHì—ÞYE†¬,Ò[,¤ýýþ@ׇüý^Åð9Sž|Ô× Cž”øƒë˜„uä¦'^|#:b¸ x83¢Çn§õvÒgø sÓ`ÙóÈq†r …hkC#Í*. ÷VŽMÎÏ¥+x€a·ÃÓK‹•cøó6Üá--çþÆ‹,½èÖfzÿ& P’7…ª*ýG h®cãC¯ïm¿¨ßJ…9cy¤vðÞcùAsæŽc>›zð_i1çRYþT:^»A"ð¿”Å«`$Å3ÀÞ³ƒ¼übg4œšõê͇®°/ ‰ežT=Ë~£…=“¼.”öaõí„tµá–‰¹9´“•àP%¸þçœÊÞ’Ml$ˆmý;8ä²rX¹[[}‡V*F…Å3b±ï¯þÁu„g䌲bZÔÒÖëe,ƒ?š9E16j¹} T±× &Ɉ,+íc¯Ä`Ëð,}gît*,ÈPö§åÑ_Ï>•ZùY lì(²_}’>?PKº“m4²¾eÑqNU=˜’Á}L©ÕB‡Ø“\ú«ÿ8eÑкiÅÿe‘Ößþà0ê™v+ªk£³ró”Œ~&“—[|²ùèá„M”P²/~̾¿ú‡ªã0öÄþiþlÂuï=A[껯ÌáÜ®9 ƒy¶<'ñô%û ì³§S ËýKŽ(„ÞÁÂìaƒgä¿òzzäpdhG(Èi|„Æ¥ÇÑÛå{s›ÏýÖãÇ_ZÚªù¨—r¬C#6>Jx´ùêsÞÔ ( ½ýáé·ióö=;þù§ßÞÃ?žà K`cx ÂÝCÚü%Î5ÞŒ"(ä­ìk÷üàO3¦ŒŸü.⯑—ÀÑ㯮¦º–îð‰pîšcF¡x¿höOhXÑ)ôÔÛ—ó¨Ëwœ›c*¾Æ|† B³ ”[9T#—žŠ¡ôÙá&êp™9v=² º­é¯À±ynË'Û¡Rž|ÿ•EO²±1†Þ^ë›ëÑÌŠ90S³dfÖ9TÅa&9<ÑÓÄ<=ƇšÏ ¼W`ýn9¯âôŸ·Ê~‡ga!™CshÏ—èp±¢ÎÞÂÁ–26žwÿö½‡6<ñ».ç¯ZxçBâÿßüýû^™R>jŽÚïým=ÛràÃ?‡’}œóN¾0£0®ÿàÅó9ž=7OKÿµzáÁÁ<öŠ TIv \€ÿ}YB# îBãÒãhK;Œ ¢l«/ž³Ç!¾dq,x>Z; ãË’ìÀ³ÌûÓ¦ÜIÓÇ]«L¸6Lp.®‰Wñ) œ ³Â@±ff²b¥ÊšV>Ø¢+xrs>Š˜Ô%+DÛ‡ÁUur3í¯úPIÆ ‹ú ­Ê훚ڨ“cº±è²{Å£ÖtÙllXYÍn¿lÍÿUÅð¸õÒwü¤Ä¢þþ›Ë1G£›Å™Ý!³›zäÌ9œü “~¸n£bP †dÁ‚磆çŪ@ö¡ˆcBvǶCö1±e¿ ý%YÊ܉Á>_xtJ˜vQú«ÿļlú?Ææ<÷áÃ=§¨U2 4óHr·ìÞ õÌ>Týû«£¸çѶvzåÐQe‚ùÄ®P+`ˆ¶$Kò ,û üæ‰ãèªÑ#éŽ>¥ª.£|^i!YXæÕìÕ‹ÐWÆŽT —8Ã(%lÈKþ 4zï¥ç£7&½Ž8]íÔÊF7÷:™ L¥¾ù ²—’Œh#ªÙ”Eg+Í(¿–Íþ1½·þçʱaų• «õ¥\ÎW”|£àñª9&£#­…³ðÀó±õp5´:©¸èßd³nŠ Ô¿(·Üiã¾LÇOnd㺆†²çgüðóèPÕjÿïùÜþ7î~Úÿ]nw–r½ÎKn ¾lv±ö~Öÿó]Osâ‰û«4Ú=4vèÙôôò«üÇbQÿÍ凘#pÃõÆòänäîÿÃé³h‡×Ü·~3Må?”Fnƒb„S9âÏ(ŸB9ÔÚâWõAöáõTXᩨ¾jÌú6{=~³i;§~6úÛøó{)IDÍ0ç猲"ºáƒî°C´ÿö§È}Òø¤¯¯Õ Û9Ĥ¢¯Ÿ{/+˜¦„`¸ÜrÔ£0Iú¥®i¯VUÆü?\½–3YÝÄ5ÑÑysÑ£F¿~z¤ò½¬p:`o@< p¬‡ïGuC+m;XCy9«¢6ëïö8ÈÍŠ§(nO'‡´yx>¢àMýÅóä>ö@™¼xÄEñjµÒžË€‚QNQÞ9REß]û¹ørCJ'‡ƲÀø@"d¿ŽÓÒnÞW­Ìñ×ð4îllት0²ŽPõßÁª‘rø¼áeÊ&®ÿÞšÏi9g¾R«þYœnÅbÒ+§…×3ÆžXÿ ¸n}Õ†Ò¬ŽbC­||ZSG÷|²Zù3 꿺ºVù,ÿ$²„×­x/1×+°LãŒ\N‡Ï(ÆñNn0D‘ ó£fmI–ÐHã#4.½Ž®ÞúGºî¼±’õ§“k×pÜ;6Q°ðØÜɷӊχä>ɰ;šhýNNŸ\q—b|<ñÆÅ}Öhdé<%ÓÙ‹+` Ä¯Àøð…`èy­‘}†ÑF¹¹+U! òÀ2š?í¿8Ìì+Ê:o­ù!ß÷‡”aÆ‚†=;W`´ûðÛTÛ°S•g‹›´·O¥üLN9ÊA/ʇø=–ûàú>ëÿ!l¢Äªþâþr{Þ8tœ0™™1¹ŠÆ@å±û›(X/à†ñc8i‡8Ó½}¤£v³!r¸Èçu‰ä¡ݱ›?{-áuNŽðDöþêÿ·€:>KÍú´v’…3_eñæf/ˆðx>OÍÏõGýúª#ž‰ÉÆXÝÜÁtfG›ËéXGçØè›«×«Iš¼WŒ–ý¯¼7¸p凷î$l¢Ü:©œÞ?v‚0'H–ÐÈ9¡qéuôxí&În³L ¹éõcÀÉ£¾@'›w¬ð£ü˜´`½Ü¬œBvr¿u8eâôÑ–ÿ¥{C¿çÅâGŒÚ9½ßî#µ”•õ)#¾ÃhŸ…µk>øì~š^~ ߊcºJ°áaµÒØa Øðþ¥8E•½Ý>šÃÝJ¨,7CíD=ãYúª0 ±ªðsä÷Ø"€5*>óJ¹èníá=óÂáCh_K‹€ð®ÿlȼÛTщœ %=møwñ]±•'Õ¿yä˜ré=Ôª¿…WlÏosPY–ÉOJ¬å?Üú#Ô*Ðð¡×”¦Gx}Æ®µRüÄËšF@ Ù/àGÏR¤ÌƒÒteLœô|„Á߈oÿl;ð2a“%µ€¢½ô•…Vê•UßðœXœàS>¼<×êÑœqLÕÇì9ò.aë¯ ñ£/ŸÕß)ýÖØt™ ^šoU²z!ÌDxz"ºa%²þ+/‰, ˆ-Òò:O¾Æ"d¿º¾Yñ ´gDÿZÿù†è扩Ud¹Â*#Ù“ƒµL|žHMÂÁs#Úú#ôL–äD ZÙ¯ç•ï/yserV>ŽT§´çC((¾½o¡ÐhRŽÆ‹/^o+[Ý è—%2D0±k<xZFÂŒz" “±QtÔªHÒ?MM Ø›3’Fòhn§3ļL¬GjáþäBðÝ·—²Ÿ¤ìŠlÑ û†8{Ë"%ÜÀ³¸Ãíûa|(²ßo_¿`¤5øºž¿RÄç‡fÉš!dßÀÙ½xAÅ.C$Ô]ße?ø ïCÕ_ã÷_WrªË~2ó:e4¾à’Á ½ÇåËVü›–¾»Ý9JŒk M¡êø»üÜ@Ìlì õ¸µÏ{Ô"þCAúÙ쮡Ngio@’ìˆÝ>Ž›Ï!D\ +ÈT²ù £òüÃøèK 仨r²È¾Ë•Mæ ëPõõ’ûÐbÙÏp&‡Jžáò„Ý÷‹‡,– ÖºÈÊ¡JÉ^l.Wݪ$˜í—}”ã¬^|€ì‡*|¿Cö3œÚŒ‰„÷¢Žrß@`HuÙï®uò} -ÁÉWAQœiâ k^+»¦ÃK?8¨›«ths{¬d Ö@TººÞ&ÇÊqÐŒ«–yÞDÊ ìºÙûa6ꨔÓZZNeJoÐA‰­ÀfR²ŽŽ‰}á‘ðã  JcY>ǸvÑžp¢’œà8ª,_ó¼Ì‘òžû]1ª”sáçSSó¤䜓'—Ÿ8q;ÇŸYhl®‘WKÏ$¯ Œ ˆY ¿ê{ôSTZÈöÉ"ûIJïtl£<ÁÙ‡‹ iže‰!ûX0Ÿ×½ÑrQèc:ÃíûeÚ˜R2ræ¥1µmZ®jŸ´aôú‘²ð$îÑÙ̳,‹²nQ&¯]$RÙ×:ÿ#å}Ÿ@Êý ï}­ó¬JGþ§´ñáW>8nÚœLÇÓzxÁÀšMЧGaN&SÝm8i–`&Ú@I^ešõšæ= Tƒÿ£JsiOÐllZD­­³5Ρžäµ´Î¡ã'³ (‹•/°ò£ÃÊë-ØxËäüéXÏ뀷}ÁwÈPÒÈ~÷K^N]ì=®$;vò3zêíËéÃÏÍÙ¾’S™ì‹?ñ8.Úd?ƒ;Õ’m¯»úLF}T}Iž& aïGc¨‹íâ†jó°°ÕA35Íé¦Q67ó;Ð7è`ãì}6EöÍv5èù^²ÏÓ~4Í5x¯6?’ù~é(ûÉÆ¯”5>Dãƒ~EEÏ®ZìK¬&].'»}”æxš@Û¨›Ÿ^Ð/ê¢9‚5NÀM´IË4Ë{@ ÿQW(Ùhãx9ÏUDùV¬¿œê.a¯HÐD ñÎë5Öñ¨:q'ÕÕ_FžÆäx©0ÛJÙ¼€WNvåðŠÓ6ö€Àó¸ï¾b¾Q­`¾'ìÛËi\I1Ms ” µ´sÓÚíÒ’WÐÎC¯7¡!¶&””à60exåqÊÖœvmz?@èÃüÑ^Û÷+²Ï2ÙGB†Š‘…Tšm¢‘œ)jBU3a"³V GU*©t+Ž6r¨UÙ¼.ÓexdeYÙG€!û¨'êªó]`YÊ}¡Vù ïCaîÇ‚Û@*Ë~2óZ»‰ ¨ú”1ÈãË’3”;åš6'Õ5\JCÊ–p?m¼Œ<^+‰—’™SŠŽå¸]Ð º±õ§h©SJß"° LUL;x à\¦!ÞƒÑòïb=ÿÁdLÄE[9NzÖÈ|Úv¤žª[æ±r?™ ò—³¡’Ÿ6xe×ë.fa=¾u[inGMMg±QÃôòjá<Çá¦G&½›JÌN*¶eGnNåäæPvV–~‘OÈÆ@2È÷d’ýòÒ|*-½L–kiÓ¾?Su³o‘²¶ŽZ¶ê„E,/8õ*Ê›5/Rým²¿ýH«i¥Í, »éy* è2tõý˜ÿ€ Fy†ó"2h×q;Ë>q¶¨üv,Ê¢Ú yBëì!›‚…=£k[yþÑöaÑ'ë@檱\O¤v1þF6в:\J&2÷Iùfê9ÏÉÎæERYîs²ÙÉæc®Ï÷ˆBö«ÛÜšã´¼É8yPy?ý)•e?™Y­¡.X]…õë3<ŒÊ$UÄ‹šÙý>ÌÆqÕÎbª;y•ºânuuW) ©/Âè.èDGÛR´¯‘ž(™ò—·L5?et‘‚3ðÖR‰–ÿP´Ð^ÐÆiŒN,˦ò<¥,ªá ÜGñÎá•ɇ³ÂZüá%i·OPÎ?VõMöÈŒåp°Ù<¤(*È<k\H:©òtŽ"ô“sec ËòÐ^HЧxdS^n.åæ±ñÁže¾+Uy=@`0ß!KÉ&ûÅùåô•óž /Ìÿ_ʶug.;\½–ýBZùù¯d(V?­1¸ @ög.àLP.šp¢¹Ÿ+ãÿèQè*â„ ôý¨+dYàà°²ìC^Æ•dѤ|H¾‡Ê«[hî¾:s²rÙÒªÀßÇŸh¡Sò€‡Ba‘?‹¦ Õ-<1<ñ¿¸£ƒJÛìTbgÃHï¤!,ûå9.šcVòØèÈc¹‡ñ‘ËÆ‡rÅ}Z´²?ÜæÕÿ£å}4ÙàÄœ¨,›eØé—}«¹ê؈¨ëp“©ÞCCÙ!‚DxìEèDH–Ax7ÌœŽÖÊ}†òf.@îs ªfÙŸTÕBÛ†çR[ fqÚÜr®g&§.eÙ·²Öáóèûè6}ï: ¿›a4)2ÏF Ÿìg*²?¯O0߃eŸZ)áüä}|ÞûŠN¥ºŒ©$¬]t©£÷…jZ–ýøð_{ý¿–Œ4@¥¸½îv»SĬ“X\ݽnwèØ,Ü1£sîè°°{×ÉiMÑ!O!guä½ÎŠÏ¡î‹ãð 1þÿ‚¦Ìê¥ùœÑƒé tû:éÐ#Ôáhw8½¼DGˆkü¸‡ø-‡z=ßåvu¨Áª Lš§¼œ6LUFóÁ{Ц&ÿ¡€à¥‹6“‰tͼࠛGax LÆ*èì$n ÔêðR›[G.w>¯R ôLÐOM,qYFYøíhS PÊí³Ï·çÓñª»Y!XË£«ÛYQâ‰ÑÊ•8§wAÆ­–¯¶¶)ìiAF¾ç0 ¯E‚t™@‰¨s*Ⱦ^o¥sçüœfŒ¿žÞ]wª^£€=ØP,È>·‡àÙǽä®7c~¤ ñ’}j褖´¯8›šy`¼ âü‚GeCt´Ñá}º§¨F„Ù÷ûŒ½¢ grª^ȾÇëQd_ J˜Œí”—ɲß餖}¶CÈÁ繸ð @ÿÁ’ öBZر=ʼ#Ⱦ‰Ï;b×ьà T›IµYÔÊÉ[ú á2sÖª‚¶N^$Ðç5ѳ^Y–á¤\öìÀƒ!d{!ûx÷Ê>!¬™¾õ}Ô–} ‰â?x0;ð>Öï}´kÈ?¯˜±¢—ÜÅ»ý<ÏO‹Zzîªÿï}-È~¼ø¯a½Sik°xœîšÆ¦64ʨŒ1"„ΙNvõv²ætql¹»»ÖN:é,¢5·r¸Ê^5ÚÌçîâÆ›Ì(XÇéT[9³M‡yty„•G„òJ”Ce˜FÐ zA7ŒÔ#ÚÒØÜæuvv†?díƒ#¸ÞiïlPƒÿxt_m œG=ÍúF:ÐP7ÞƒžXò.^Òéórˆ0ºXWA»Çkf¥,—G™¬¬úBZ Œ*ÎÔsgës•ÃÅlãÜæ ×—V°ïÌâÞí#ðìho;Éçâ­ ¶.çï‚&/èTƒÿ ¾¿60”Û€ÍØBG;9)v¼ñà¿èpYWŠøŽÔ´JØ!·)»ÝÎ[—Î!XÝÃJ”0JØ_ãóºù _È‹ÕáàÔÔÁ£§M¬˜ž:r‚êy@ÇíÿÉýGèŸú«ù³È¬œWí2C^Åð€aϲdic¯BfxaðߺB2ݼ&ˆ}!3¸žIô(8ÇÁ²ŸeeÙ·;¨Ñîâ°e"‹½,ü;äž;å½ÉþÐÃb§ì=1ñ€c‰%ò€ëùÇO¦}1ø€cÿ@ÙÇãZQD=~ïÛ•÷¾¾ù Å€ÿýó>¶ï}`!ä:õuòøA{E5½Uë¯ ¾÷+û±å¿à½Võ¾îžJQy U=üqNaá=[÷£Ó*ÆFE™o4Ø7² ‚ˆ…õÂ=g'#‡¢¸;‰u*êðÈÅs1\¥Æ*ÑxA)ƒNhdc&‹;åLŽoAç E ÆFn.gõP2{øBM ¨ù^á)]¡ÀŽü‚ÑÙ³kÿ‹Ëÿ²u~ŽÒãÃÆŒ»F þ£.µŒð—°G ÅÎáHœüÌáaC‘º=u—^1,ñ俯Ӆ²ï«·¿î¬€d²×¡ÃÞ¡NnïÞà„,ÀÁµ U ‰âp(¼øu]J¿‹ v;+-ðœXx³ZTÈ×û<'°Å aÃõ¾ûˆxnßÈ+” (ÊZŠ‚ÐBߢaP>D(†â QB²|Ïý¸_$Å_w#Õdßd\(Ö¾£¼N4ËþÑý{×3†r ¤±¸&¦ʾ«KöYÙîôMAeÕ}ÀŽ­Qçé³ïpì(Zrà(Uò@˜ŽåaUÍIúêŸÐÃgÎ¥qü^L²s•ÏìÁT¼!Šì³Ç= xp`Že—CÜùu„˜) {`eŸGÍ{È~¦Åg¼ø.|Æ îÑ-û¾tßÂøá!Œ„Uá³X0²þFô(¸>>²ÏF™xï³ Çþ+Õ`Pïû_·50ïcõÞeâÝŠ¿ Yë›èÄü¢Ð¥¦Þ‡j Ôÿw¿÷+û±â¿à½Võ>-B0¼+_ikùô™uë+÷°ñ™æÑ%D¾Ù7ú‚ØV(K¢³õ5NŸrdï´+£Afîh³YƒBeJ­:ðTf°ýª¼÷ƒ¨q½Oss>Ð * èɪãÏmÓéîÞsøYŒkXß…"”Á¡Oˆ…GÁ¨:>¸fÑ1û”©.w4OÔE‡ŒÑ`tÀ‘ŠP¨ <… Ïôy=¸CFf~ )aWìñy=^Ë`04¿m{êïݳœÏçñeS0æÏ¾·Î`nÛsü|ï¢Ïzy¨«Õà?HOTHÿ{*!¾Tœ" JŒâ>ÏEwØ®C[U”–|GûÇ5JØ{=ؼ`J Œa¨ù®ïVb0’‰ð*´ux^¯_„Uá7(:à ÄÄ·÷/j5·Dñô ”xÈ~_¡X×[ºaCß˸á.ýÙO?âyžÉìkEî”ý€¾ÿ»³§Ñ,64~þùVjgíàí{k>§mÆÒ·§OR&†´þ Úb<°\ Y†ì;]ŠÁÙ÷xÏ ,û8·/Ùƒè#ð\ß³»Ò~ó3Mløødß—ìÅ7èà ©ìîc¤ìÃ3«æ{íC¼û¡KñW­½ó›°¿PSïÃÕÿdzïR|¼×²Þ§EÏ„ĵꕗž»jñ]×=÷öÚüÝ|©ÞÌ)£)h„PúQÐ0|ß}#3<2ƒ]„•`T(pD(ÃCÐ+ž'F•|^_ö-t:»nit@øôE[<ññóx\®æÕo¾úßX°ÁÎbÔ?&¬¾3  wä¸ò ˜þ5øÚ%ª $Šÿ¢Î¨·ž3M!”†Ú˜Ïè`£•´qÑÎA+6ß Û¾ÏøM‰û扪™ì=a#Ä7wÊ7Š*î…ç¡àyP,ð<Œ~ú |Ìïðe´ÂwZsñ¼X–DñuŠ'ïC±–¯½ŽÔø²bNë¤étÿ/Ñ}áÄ!ã·ÿú[ççLZä#*ê1KÊ~ˆ¾ÿ¢‘CiB~.ÝóÉ:ÔâKRôÝûiK}#ýú´™4„ÖSÐö|^ xò}²(dUÙ÷!ûJæ«.¹²ïÌtÈ>‡nñ`„xWö²g ÙÇ`›˜×ÙGß e?6ï}´ ñîç4W Ð¥ø–ÞùÍ·G ¶Þ‡%ªÿgß¨à½Æõ>Mz> $Žúúš–]›>û­nÖÜ_=õÚÇÞÛ®Z•†"{å3wÌø ÷/Â@`x( »¦•Ñ£¹`,÷Óa¡S‰góH¾Òé % ±° R&Ù±b†ãjvÊÀíxm#}¾zåSíí-œ\S{ù6a€het(|4‚^Э_pÞÝjðŸïëWãÕÍÔYtÀ(0  ,ˆ¶v­¢`Äß}çsfÈ ·Qg&‡m tƒŸ2㻟rA×50´ÅÈ&®ƒ""<j…Vˆç ´ò/¾ƒžDò¡XmßôÖÖ óæç¾Üf4º•IÌ‚yÃǹ>zp‰þFcÁý?¼õdË@ØÅáw)û!úþq9Yô¯sϤ¯ßD+Žù²š}~²ž®zç#úÙì ºdÄÐA³íß'‹˜Ï(ÿ=e7Ĺh»¾}·ì›Ý¾A¼«œÎÌ.Š/YúQ„¬‰61õªÐˆ÷›èÄu±Ú zÒEöŽâÝ º×Ú;_н¿P[ïÃÍãÝÙ÷£¾‚÷Z×û´äùnh„‡ Ø?zëÕ³óóÿΟo£—È{ã¥gê¢ñ€ ¢ŸE\*”)ežÃŽpŽeÅ?VÌ•3¦%¬‚g)Ç …LâxWtˆ08'Ù©Ñ)ÃòEܰã€nßö-/oüøÃL4ÖøÀ†ÂÀ8w¿5øKK¾3 ­ ;¿¸d¿J þ£~ñn‰à?êÙ_ôwNðozÎýƒD1"`˜gøÖp³â!d{}¹Ýë9f\´y´k~¤F¦o ï¢ÎØãùé û‡v›žÛø‘÷•îÒ]UT⽊ML¤Nâ>ßû}—«îk.5ÜûÓ;Ü/ „]Œ—²Ïžabmã÷ÃÃóçÐc;÷Ñ#Ûv‘›å«• þ®ÝH¯¡ûfUpF©ð^áB>ÑAȾc3Ë;„ç¤{£·ì‹/Èš}ñüÀzÆúsºÈ>p |÷;°÷qèP|X‹ïü@¶÷èÔÖûð x·ÑÎYûóËŽÔûY®¼ˆzHð7 ÁB)æœCŠÚöæ³O>wѵ7 ‡½±ª®Á{í…óôjÌAãÊ\ÃJ¨U@ÇŠÁ`1¢­ñÁ´+ ?RQzÐ#¬QùÜ5É4E[ë‡P+x<öUnyåƒe/¾Í÷„ïžì¡Ø_àÜ=äÍ_Xzñi±ð–ñÁËL?ë½üù2µøzâå6 xê{^ìùºÅ¢ ¾¶ê=íV:|£§2‚s}[÷ª8 Ú¹g¼øšPg”xñ>Pöì¬|á½ÿ<ýî?£_]}‡qÉôSÜ?g{ðl…(¢!äõüë—Kußä€Ô»~t‡£²ëx¼wRö»ú¢¾€¿}Ò8šWRH?Z·‰·ú°^?|Œ6Ö5(aX3 óûºT•ãBvÑŽ!çáÂïHß¾û5"ÎEÓïþìëT!&Š›¤²ì–@ù?²o÷“o?ÿ4­´úÎäd¯> z¯6Ⱦ?Yô¾¨µÝ;î»%˜ºôŸ-Ä^…‚ÉÈJ‹¥ry+ä­`î¢óÏž~êüoèÆ¼ŠòáÞ¹SÇꦕ‹j¾¯¿ó„2…ÉwÝJU·Ç#P±Â5áÑ;b€š#AÈ猴jÈn€Éå—³iêÏm^óRë6ó†Å±ÈXoM¼¡CÞþ¨‰’ïLÞ¬93N?k朳]§7šrÕä?j.^ ±hñà?ê KøÄ’ï &¼%ûW¯úÇç¯\Ç$ô’û–¾Ì+YÿšÇ¡ˆ¹˜Ö? $(KÊ~3úúˆ è¿ÞXI/<â?ó3îœ<žO)Ôdtÿ…iþ!d,ì-ÿ®Æ-Ÿ~ò×õ+Þ]Å?kýØ Cö±ÒûðàX¶DõýñÐûÔÒùµh|€&x:0ò $7( ù6[NÑü‹¿péÈò‰çóü‰\f°7;3Û—kÓYL¼p†  2°üm°ŸECçÇÃÝÛN/VÇ‚|L§Îív5ܹ}Õšåo®èèhCœ'¼06 „`kä †ܰ˜cѳ²| ¥O¾3M¹¼eefÚ²O¿ð’E£'M9Û`0æÄ‚ÿ¨0σ¿ãœpJ0¿ƒ¿‡s/ynìæsð÷HžÌëàï‘Üׄ’ýC»w~°æí7Þioo…œ÷)÷¼Plkj¨»Ÿ[ú]Ü®bWÅ9^Š%e?Œ†pÀà¥5&2O\VÌ+g: ”§¥]—û`YþI‚e=ø{$÷×Ë?§Òo:¼w×»Ÿ¼õÆkmmÍXL8Þù¢:Ø÷ÙÄCïÁ<þŽsÂ)ÁüþνÏ æ}¼õ¾T6>€3ÂlðR„’Å[o0B „bò¤mÒÌÙFŽŸX‘iÍ*2gZrFS·U ¾RοÞÙÙÑÑÔÑÚZÏŠÇÎÝ[7dÂRãF (!PFà1‚ßpâ,µVä;Œ6aš0í”Ñ£&Lš”™•U‘™™k43´VID V°ìÛYö›;ÚZëïÞµ}×–Ï÷ó³Â’û_þÝ<…gÿë«‹éäÁôŠ%e? |vd˜ ‡Ç•–´ñŒ8•N÷Ÿ¨o,;Z×À£Qâ°Ü§<èêr9;ö¦ŽöÖ“‡÷ìÚ¶sÓg»¹šÉøÎä΀}€ÔûxÔ8Áz_ªhpà 0B`Œ`C§ …çà\4\l(é`„ˆ· Œlb® 2Za^ l0:`„‡˜ïÁ‡4Y$ß5ÉI”FP]î|Ìð5×ó „bIÙ£‘éÙÛ?ì·œ—=kæ%ãç÷`yÚÚNÔüçµgêß_y ŒÛÉSµ€ê²¯Á*Ë> ¦$¼ ¨e|„—*£PÔþ 5e.¾C¹ÆŒeœ÷¨‡0@ÒÁðàê*EàÃaTÂëãÆŒö® ¸iÕãÁ¤ù‹à3ˆúnÉw?DòCš# äB¹ÿéíîr(Ö² P,#‡ü€³bÝǬXRöÃhØÎ|uäá%/äÍŸûQéµ_¹Ý›3—ëm¶²²›®ûNÞ‚3Þ=þÈÒÙ«kñ¾”%5PUö5‰ìfJJ´-`(žŸ¡„B¡†áÏ&§‡2>RÙ(¡„`¹PÒa€à¥ƒï8ã‚-®åš,¢N’ïšd$*Ù2¢šÜßuu-*¾Í¡Xéz†bÅ3+–¨—”ý0Yã'ëk[?Ýø­aß¾ûRÛ”‰·òÌs+B-£F\0ö—ÿofÓº ÿwlé?Ö†qKyªöˆ™ìk¬ª²è›!)Õ´l|€/XÑ ñYŒ‚ÃÍhxÀóƒClü1¥ 0p TD€ ì±á7ÉR$ß“…S’Îx# d^Ȉªrÿ“[Û¹Bç‡bqHÖ797ýr©>ÖY±D½°GŸ…úÉ>ŸAè¯ Uü¡ß=ôXáy _-þÒe÷é­ÖÊùFcIîóþ'{ÖŒÕ ïôÛê_:Ðß}äošF2!6È…ª²¯¡š£Ž¨ö²èÉÁQÒ¶¨½jÅõÄ7ä7Њ902° o‡ø.~çŸÒnÎG`C{lÉàíÏú*‚¯‚ÏéÎ÷¾p’ÇÓÈ:Šk!çªË=‡be…bùžL¯¬XRöâaîÇÿêç—™†–ÞϯÂ"q)'p¹ZÛþqrÙkÿ[÷ÎJÌ”%¹ˆ›ìkÙôdFÂÛ€Z:?UQ‹0ˆÍ¢AŠIæâ˜¨Ø‡qÛ¤;U4BìÅ…DlâXÒU¬‚ϱǿž[ÈäG îrß_V,¯Þt÷Oosl‹1ªBÎ¥ì‡ôÐoÞœŸ7gö}:£éVž¿ƒÁ_ÑéNzÛÛ¨þý_ž¨ß»ï Y’¸Ë¾†`‘}€ ojéüQ+kjE#RÜ"ê:‰%Á^4DŠÏß“€ü¨HLg¾Gœ¼8é”ó¸É}p(VŠ<—L÷G£±àþ8.P(e?Œ&<õñ‡+ȘñGÎÄ»(ð2ö„löz\ßÝþõ»W—Ÿ5@Bd_ƒˆ¤sÐ6 –ίõ9ƒióq{ù†yNÜ|ÔòA¢¾²bñ˜Ç÷9+Ö×â˜KÊ~ ²òÖÿÚ̧ŸSñÔ£WyI÷{ö‚ŒÁå ã¶òT‰@"}@"ÑWáÙpcË"H$@V¬Ÿ,ö|Û 7Íà¡Çˆ¬X+ü›¹"à¸ü¨¶ÝxçKÅ#ÌSôzÝOyµe¤bW {D.àUëÖMyêÑeÏ>6]—{‰€D@"+¤ñ+då}%‰@Š"ðÃÛ;~²Ø{Ž^§ÿWñ˜¨&²b‘Û¹ñÁ%úßþæñ", +‹†X¹èfû¶ÿÒl2Nà… ÿÎFøŠ—.÷¸<›¦>µäùéO?®¬"~’{‰€D@" &ÒøPMy/‰€D@"Füø÷3yùE“t¤{ˆ=!H‰‹Âá¼J(Ö®_=f¸ÆwHþÕ¯»õøößjôê¦0ßžcÚ|Ïy}˺ÆíuUN}êÑ'¦>ý·r-Ñ-i‘HRi|¤e-$‰@Bè/Ëãñ<ÿË¥:Š•Î üÐÍ7ݱ»ò¦;¿j0èf²'d™¸ٱ؋õuž¾‹=!ÿžþÔ’ÓÄor/H¢E@Ñ"(¯—H$’¡XÉÛ¶~mñVö„\©×Ñ\΂µ< &z6D¾äòz×NyrɇO.ý"gg‰,‰€D r¤ñ9vòJ‰€D@" B@„béuº?ÊP¬ p4þ•'¥o¨¼ñ΋x.ÈYœBù-ö† «PWñží!ÏkO/­d#ä–©/¼`¿È½D@" i|„ƒ–¸`ˆ Å @C£+o\üñö›_b$Ãt^÷$Ïéóy8E¯w2!{í §>µôþiÏü}¬F«!É’H4Š€4>4ÊI–D@" Hv”P¬;¼çʬXÉÉÉ-7Þ¶­ò†Å_7fZưò;ÒQ³¿&^ï¯×sŸÛíÜË“Óßç¹!_]¸â ‹ÿwùA" ô€4>úF–H$u¡Xêà˜¨»l¾ú¦cl„ü [Ÿ5B¯×ŸÃéüé•Ù²èöˆöšNO¯zÍù++¯¾Ú|Žü.$jéüœ]‰€D@" Ä„bñ“ÎýÕRÃõ¯ç7üyž°@áÆ‚ûxëÉ—E{¬\´ÈÅT!5ï²Sž{|h§Ó}“޼·°â_„?äßïòºé.êhh™úä£Ëu:ý«&“á}¬3¢½ZIŠ$x! =ñBZ>G" Hz ðÈ ÅYÍuÿÊêÝœVÉðc‡÷|ûÇ·»ÿpL~Ô0ÌCÝô§CF¬[˜ÌKÙ˜Ìï‹\V<Žxuºµ¯µ–c¿×ŽÌ؈Õ×û:_—H´€ô|hƒ’ ‰€D@" ˆdÅâK¿Ã¡X…b‰¬Xß¡X‚çË8Ì iy?Ħ„eÙs†GG—y=žKùØø@røÄìêÁû¯¸ù‡Ú#Ç”§–lc£d-ûô¤ÛÇaxûx’û¾Í×ßv¬ëÞ·ˆø3Œ¤yÏ<“íÐÙóäaÉ›¯óêò¼ÊeïMOªÏb¬üÃH'ìåc.¦·ƒé«òêôÇu^Ïq“Ét¼Ü]ýâÕW£ ²H$a À²]QË ŠŽ yµD@" $;Á¡X]õq±òùÁPð?2+99<íÙ%“<.ºŒÕø‹X‘?• [5édE¥†¯«çkêø¼×5òw'/Nn.ò“Ø‚àßMü»‰?Yù <'ÇËFÏ%62øº\~~¯9*|^$ÅÃk¡T³Ñ²…Hÿ Ó²¦h¸ñ#éʼnJyM2  –Îaª¨EHTDÈ‹%‰€D %¡X)ÁÆ>+ñ•^0lïl©ð’k‡]Ícƒ` ‘5«Ï‹’èöØ´²«ä-^þ?#ŠG.{ë’K:“ˆ|IªD _ÔÒù£vµé·¶òG‰€D@" H+dV¬ôa÷Ì—ŸÈs¶¹§²2޼îq¬˜Œóx‰÷ºqìµ(V ì!i`J{BøüÌ&雈Iª"ÉLq*žzl²—Ü_âÐ«ÛØ32*°º<'¤‰ýeªîÏŸÍYì üM~–$jy>út&’F‰€D@" H}ëÇwx¾cЛfp(Ë5B^Ïs¿\ª[ùàßÌÇåG‰@BØvãí;*o¼ó©–üq:½þË< ŽRx¢{.‡tý¯½’¶ÈUà*rŸŽHã#¹.ë,H’„býäï¹f=“LTÃ[Û¹‘C±~÷›Ç‹²Åq¹—$ ¤à­¼áŽÿTÞt發¸âEìõÀâšJAh–ÇåYSñôÒï!õ¯8.÷tA@éÂiYO‰€D@""üt±ûÙ¼ü¢I¬Ðý57¾bdEî{.WÝ®—®M‘ªÊj¤•7Þ±¼xÄ„ézîžàÞŒ*±bòx<¿«xzÉòÙÏ.)JjÊ*H€4> •ý·rqLî%©Ž€4>Rò~‰€D …L(ÖÃÿ,ÈIadÕ’-7Þ~ däijØk÷ “ÍÉºØ âõ–{½î5Óž|³d‘¤<ÒøHyË ¦(ˆ†üŠ +öÊ-=0<Ç^Æ‹3(ý…bµ´5ìd(–”«ô¡„ö•ìñVÞ¸øÿ‘Ûu§æ”À\¼Þ"7é>˜üÄ_®àoñ¦Oö) äŸx! ×ùˆÒò9è€RäWŒn¼÷dzMçó‘Qœ×~ˆžtYÑÝ^^LðÕòx«¼ïaÛ½ü‰ßßÿÓQTN¦£lÉTÕhE(ß컼@áßÜ^çÿqHË9]7W²b=¸D÷ 2˜îúémŽm]ǹºõ?Ÿ£Óë.òÄ‹ÊÉIëªqEÞ¨OöÖS£Õ¼k×°Ü©.½ÎÌ¢kÑëõÿ¹ò׿­,j¶c퓸Ù§Ä jù FoTE­œ¿Q!/–¤.Âà0,\x…uÔœŠ»,™–»=o»í½ÙV‹'/Ǫ³˜L¹’%M°;žÆævoK»]l9z½î„ÓáúÓɇyõÕ¿c$U"i‚Hèj/PØu–‹eç¡ÖÚ¹¿¯n¹äëF“á^)O¡ñ“Gãƒ@+[»ËÍ^jæ= Ü:ôTê‰ZESî7˜?²O Jòµtþ¨[¶Z„H–J$½€A÷麻¿{AN^þ_<^ïŠòáÞ¹SÇꦕ#«%£×Eò@ú Ðn來{ÑúÊýÞm{ò཮ª½­eñÓüÍÛŒ‚›7%¦<}é]S,PØÔX÷ kù«v&q†Çms×5\b3ôb)O¹OÕívºiå:Öæ‹Â²™Œôü¹gÒèl[\i’}J\áNº‡©¥óC±‰ªÌ>ûœ¯ãŸ­Zñìe‘H¢FƒŠÑÁûŒ¾ó£{­Y9+Í·Ý~ÕBýÅgL× /É'“QFMFt’ßmmáÔŠ±º‰£ËèPUµÃá¹núég´oZ½j}’WOòß|±ÝñþkÞw.¸Üüo/y&óMÇàÆ:½So³n§Òº9SR^V©*Ï“7‘D‚@‹†–Ò;GOP›ËEN‡>­©£ËG'“>~ŽmÙ§D½ô¹F-_éÓfdM“xÓ`„Örý=ß¿7Ó–}ÿœÉcè›WŸ§/‘I{’ƒ‹  ²07‹NŸ>^_[ßBÕõ-çOŸ7¿mÓ'ÁñÅr$€& =R÷ÞkîúÑ?/r¹Š/Èɪâh5;dšÛŽÑ¦=Ï’ÓÙFÊg‘ÁÀ¡÷²H€@ŽÙD§—Ó«’‹',5t:è({BÎ>$ÔÉ>%!°kú¡jñ3§5 §$N" ü†Ç7/¾„=ÿ ïmW-ЙMQh¦’’Ø €6‚¶‚6c2[¼ö®o_ÌO‚!›î}½þ¦ïýä"½Þð›É£.÷Þ{ÍÝ©“oç\ >ï!§9¥uÛ—Ð’WÒöƒ¯Æ†9ò®A 0!7›þ{ö4ÿ™o9NØUdŸ’(äSû¹éþBJmîÊÚ%GhC#GN((:üÃKó½7^z¦2B›l•‘ô&´™¡Åy”›_ð—éÓçc ´«tíïõ_|½Íb±>&äÉd´Ò9s~F·^ú*›ïgT[G ½úÑÝôÌò¯Pmã.ÿqùA"O.5Œ®;ÒÿÈßlÚN-N§ÿ{">È>%¨§î3Óõe”º•5KV``Àµ˜Ìy—\²˜ceJ¯¹à4ƒôx$+KG7ÚÌuÍÓ{¼T6mÑYwuµ+´¯t3d¹*™:æ[HÖ,O…¹åtÝùÏÓ¥g>LYÖ2?ÃŽÔ¬£¿¿~­Øð9œÈÞ+‹D ¾|wÆ*µZ”‡Öqb‰‡¶ìŒ/AO“}J òkTHã#*øäÅÕð{=øŽ¶Ü‚[ÕjüÈn…Hµ'É¥h;hC¶ì¬;¹ÂH‹–ŽÞE®2,–oõ'OSÇ\A¯t²@IDAT‹/_I½B±v,í Åz%-ÚŒ¬¤v° ô“™Sý½¸ÿ0jióOÄÙ§$õÔ|¦4>R“¯²VÉ…@ ×ÃrÖE—Íåê"¤ÓM®jHjµ†ÚÏ[-¹øš¹M)^µtò~(rõ•Û¿u*Ï./HžúÅú– ÅÒZãNzÎVFg–•(5EÖˆ¥;ö$¼ÖiÞ§$ÿT!@©ÂIYdF@˜œY¬ÙÙç‡#O"ë²³þ,C±¢i|òZUøÆ”ñþû¼~ø8iK¬÷#Íû?/ä‡èÆGtøÉ«%j 9ó=,&sưìÌ ¯\@P hÓûhChKzƒ³W1Ÿí,]ú}Ÿ\éõ£²­O¸ò4eôåJ(Öi“ïè™K†b¥·PŹöÓ ó8ýn‘òTž·DÏï=g z>.Íû”ž`Èo#./¡ˆ’Jâ€äñøP3 &cI^®Mõ+'/\ÕéˆÅÏù!:ð"’E" H02DØ•b€ô«ÅdRÍø€Òûâ{ëù¥ÝÈÆQQ^6]yÎlš=y4Uñ±_,YF÷|õš>üô‘ÿP]c ‡ùåfÙhúøáÄÙ…øšˆô¾÷_*÷§Ç—}H}ï«!WxG]·ï¯¢³gM—D¼õÁ 0Ú‡•į¹ð4ÆûPÅíöПŸ—nºôLš;eL¯SÔ¤­×Íû9€¶¤×é³ø´/v…ö•XK²šUø õÃfÐëtYÑÊ“ÅÚ~ðú೩µý„B¢ÈŠuê¤[éŒ÷’Ù˜#/ÍíôÜòµÜ† ˆL‹™æO/§+ÍRÚ;dµ|D ]Iwzàšúfú￾Dw]}æÙ-«s§ŒæûÎX—ÛM;TÑ´òáž«ö h=¤ˆ,ˆ$zmÕFr{Ü´øKç¨ö¨ÃUuôË¿¿¦ÜO¯×Q¶5“¦ŒJ7|á Â÷Pe ~èÝuÛè¬S&2¯`óG_æ±ç™¯ªÛíTowÐGU5´WCOTIÓ>%Qp§äs¥ñ‘’l••J2 C)ÄÆ2é ýÆ‹ RG«ëéáçÞ%ž$H·]±€{vÒc/­$˵çÑÔqÃiHQmÚu¸‡ñ±iç!š1a„b8¼ñÑfN7ê¢ßòEe4ö`ÕÉ^†‡ íŒãé,Vü÷ð¨ßKﯧ‰£‡(FŽø=Úý¬I£¨â»¡ hïêzÔgÑÜ)ÿŸ½ë€³8öswê½Z’{“;î6Ø4Ó‹1-8@è!@HƒBB{!¡¥¼ äÑKè`Šml°q/Ø–»…ånI¶¬bõ®koþßiϧS;]‘ît³ú}úÚ~»3ÿ-·³3;Kà}¹r+}µzÝ;÷‚¶¢jX=÷À<õÇnU«SªŽyOl ¿yhkWV﹆)Vfÿ‹hÝŽçhKÎ[d±BȲ)Öž£_ÒSÿ‡Ç­â¿>ú–L,ÄþâÇçSZr¼6{þÑ’ï©¡ÑH·\q¦Ö–VsûýÉe35!ùl㶦 ˜ßZ°Æå¶ê*Ù‡ hþª­Ý.|@èyå³ôÇÛ¯ ôðxÜ;®:Ûg"óÏ®9—'fbèб“4oÙ&Å“1§Ú&LÝÝááÊýèõœC=KóOô¨ða%(û”6ëƒ<ì:þø+Ùu.ä A pÀïŠ:´Á!A¼&|,ݰ›Øä„n¿ò,ž¶9¯»p:jJhÉúÝšð ÈÚíû±/„†bie å–Òœs&i÷G Nj³¯jÆÄ öÝÿÆFGÐà¾)Ú±k>}¿ë 6`úpÉFꟖĚ‘-¯_^ LO¢ÏY£²m_®¦‘ÁLìµL¥UY9´yÏavæØXÏ¿¿”†öïÃé¦Ò§ßn¢¿Ü{­6د©k¤w¾ZCÓ ž!í“Û¢&läü¿^·‹gª´ÁÙM—Í ¨ÈpÚÉ´mعŸg'GÑ'œ´4s/šÞâ[Ü€Þ˜N;Ž”ÐN¡­ï¯»pš¦EB:Gä<Û§ ™í9yTÇfZQF³Ïž@é<«»`Õ6Úœ}DX_2cmÜ}ˆ®Þ® ~0ÀB(,­$ü¸b-Jïò áÈÚšééÃÑÚÆyÓFkù¼·x=›„…²¹æ%Ô/5‘>ZºQ{^ÇëT>`aåbnÏOþâGZ+… ~æÄm¶¹ƒr~÷üh×÷³h ÷ÛrŽjýÕ´±6ž®aÓ0Lž  ©®«×®Ñ_½ÍšžaýÓØDj&Õ±9'hS,YÏÄ0ºêÜÉÚ¤‰öú´ËOXðoàµeÓO¦Em«¯pî‡Þ_¼ÂYà€`v’MOÑg©à­¶>’Íe“Ø ¦Wû8?ªá,ApÑ|¸ •D|Š€O:ñªÚzžq oEx4ÏHbp»r5 ©f/gRñ!fò¡-˜ÏfG+·ì¥.9C›im•(?Øuà˜6{{ ¿˜LœöEgŒµGkh4ÑS¿¸Ü.lÜuˆýCèÂÓmqðƒ ÍÂ5<ó - XˆmÛ{”b£Y¸ÈàÁÁ)á ç-ÐÖWÌšjÌ+bAÊ68Ù}ð%ñ  &¼Éœ–æ¥3OÓfºfö”vD ìr€cŸ¤XÇ3¿*8¢cèˆ6¬ŸÁ’ø˜(6w³¥©œì9˜¯ÍB§%DzéM¬6Ø„iÍ%3lt;æÑÅkUǺøY@G÷ù È[¦XU\o¡)pêÞSYÐ@;A»„¶Äq¢ +mÕ9Ÿ«fMÒL/Ñ@cˆÁ84*‰Ü† X#£6<ÅBx ü~Ýù¼>"œÎà5)/|ð¶ÞëxIaíÓ˜¡ý4óÃ~,,54Ù„qäçÜfð ímƒv´ûìCÇi&k°¦k> p9‡MF{¿ž52{¹=ŸÑ,Thþ}³a—v%4}Xƒ¢‚3ÝŽ“qÿM—h$˜ØA{UÁ›mýŒÔú:¿@KÚ$=‚±OéaÈ{Gö"|ôŽr.zV¯ ˜x&½ª¶µ×œž=ÄbÖÐ[€ L ,@ãà8Ð…ÙÄgOÔÌ>æÅ°¯}±’þt÷Õš¹…3ìÐH$² pÛgñ$Þ>âaUiB Á F™rá=0×À»Óy€Aƒ)üOg! Ùf8sy±èŒñ§üàÃlB4Dž}kñ©ÏœÒ€@ÒQ8Ÿgz±(‚Á¼e[èŸï}CÏýîû'í}ßm£yp3 ˜»@#á þÍ íÏ+ä2(fAë¨=xù‘à¿xà Z/˜79‡êægñ1‘ګɬ©ƒéU~aY«‰‚®´Uç| ñ@ˆ‰¶MV4²F¤­°ïh¡&,@«¨2+®Ð&/ }Æ.`f‰µ_¿¾á"M;;¶h\?ä5-èsÐW•ó‚{® úûõ± (ˆ? ÍFie-÷5¶`Š©»[¶k%Õ3Çóon¼X3»*ãtÿùîM›s×µ³ìQé¶?ä ¥1Ø,,‹d¡À›m}jŸd»ð‘ãm¨#+r-t"|tÔ’‘ Ð&NÃá6ã¸ý³ë{Y;€ÙHG¯S{xvQýX"q5 Y½uE„Ù¯:g ÉO¯:‡þðïO ‹Îa#í`³Ýž°P,9°˜[`-J‘ƒyuë ÞM3”íܳh7kR°hþŽ+y¡©S€8hÀ:ÊØÔBØ®cÖö©_þH=juÆ|W„¦q¼aߺû5aA}ÓÞ÷Ñ6yÔ@Z²n§¦¹Ö?ͯÆk¶ñ–°Àÿ¬)£4d*œ}Zï|@oW“ìþ”)–;^±úó@‚7<<ÁÄP…=‡Žik úrûCèËfN0[ܶ÷b ãE¬p®´Uço\u”n6ÜÏò`2åΚ˜Ét›!…ôÃx­–cpl3ßmú…¨RÖˆ^§µýÿåA¿«šÈ¬ríÑá•!Ž×i©à*O*>ÎЖBûƒ ÇàH·ãóŒf­ÌIî‡²à„¾Z,¼ÙÖGÆŸêsTúÝ„D´9…³œ ¯Ú—ëB­ à7 Óv<¼FØ¥3ÇS5› ½þåjm9ìʱ€úËÏš`ÏG hVmÝk_¼Š—0ÍZ̃d¬Á€Y¦#`ðãiÇž¶`6ó˜&àz|&öÂ#M010®Ý®iG5$ŽùŽÜWÓŒÀ]&ÒÁÂU`2šás¿‘M?0 èªï}˜º`†ë`°xBHZ’k¼wDLKj5OZ#¦Q4Ï–ª0‰mä7e¢}Ì 4!ÐŒ8šy¨x]:Ûœø¤Žu‰ŽîÜ̯÷4‰®’ßñ…ç„ç€úŠzþî¢õ´‡×ü îÂTh »½>‹×LÀDOÍI Â\Ã-,‚¯Ú*Ú:\hCÓmåÐ~}ØÜ)‚¾^¿S{“$Lf À|j%;Œ˜1~Memer\Ç®‡‘fó¥çå0qTë¼& É&kIœÍaÚUQ]Ëí{¯Ö>–}Ÿ­™a>wÌ>syRÚÈòY°:¥5é(=ôpL±|S6Ak²œû˜³ªàͶ><þžG˜÷Ýï#8ûU¬röÑ|x |.ø3˜½ÿ¦K5Ï,ϲ/{,K€7Økà ®cÀ€‚†Z¼Šw0uÊf3¦oxàm4™›=ÊÌÔÌ¿uçúÞ®A±7†™g|±öaî…SíIaÁç{<ƒGœö4øq‡Ÿ~̄ž{$ Œg-ÌU¼ý+^Ä>oÙfm ¯QÎ zÛKÏá¹–àÑ æj®„ŽhÃÀ k``+A_0u»òÜIZXÐûÛ¯ãòCy¡Ü$î˜bÝÇš‚¾ÙȦ+´õXk…zt%¯Çp “›Û*ê»Òhúª­B(Z·ã=üâ<Í;Þ¯ÙE7ˆ¿Ç‹¬±êóØŸ_«Íú«ë`~íÈnŸíH¾ýú\öªõÎWëè¾¼¯y«»púX*.¯ÖÞÃü ¼ÃóÌ=ÿùÛíßáÂÇÜ‹Ngg»4׸É<0ÿÙ5³4 г ÒâÃvnÞš¿F{æ–j­H;Ñ[¥/;ƒËÕxÇÞ XÐÿƒ7kh„ùvK‡s ,Ý Ï½·”vçÞõîóÏÞÌi”ò{ Ø…˜ùè­ÁÞ®îøýc Æ 4ÕÛí©«À9›bá{Î@ÓFý”ΚðÛVb¶ÂGWƒ¯Ú*èAƒP¡ê'‚r_ûÖ‚µPh› ¢¯€öæO¯|© ÏmR­ÚHP¥­Îèw v4ÛTïÔÙ¹©çÝy† … Ç>Òmýæhgi¹ÆÚ»çϤIɉÝɦ–Wö)ÝŽ³?fè­1¿˜]ùcé M‚€€OÇÆ®dñ†»ßv–O¤ß‘à÷Ð\ô„àÑm¤`M¼ê”³éÄA^l‹=Q`óï0®Óf´aÞâø éILÚ3ÅÚ¼÷uzuAkS,w ã«¶ zä¡C ¸GÀBuìMsÁïÙ#¾IOîXkמàô`ZÖ‘à8µÄñu@›vî#}ÑÖ•»]ðSƦ›@D ëS È¥Ð,‚€Ÿ €u#w^}oÀxHÛm ü±î{#HèݸcŠHˆ\Í›ø-çý€`N fFJýîæKƒÖdÐm=%ü”6¬T„@jB«"|8€!—‚€ t°WÇ!!88åë+Z±õ)Ö‚j@äo¢·]Ú®)–¿£OQ?æµ\N!àí¶áà¡ÏÈZT ‚@ " fWXjB³ ‚@À#0fð•tÏU«èôÑw“^g› ´ZͤL±²Ìx…ï"`r8àš[‚ ˆˆðˆ¥&4 ‚€ ô ”)Ös¾¥Aé3í<ÕÖ—ÐÂu¿¡¾™K%ûìÏå"¸0±—+BØKžA šˆ¥&4 ~Š\oîÜŸçWÔ-a7Á«²ö¶¢ ; ÿþ…OZ=—‚@O  L±®<û%Þ¯"ÝN‚2ÅZ‘õ${œ;µ‰¦=‚Ÿ^¸Û ]:îžî§ìõYŽš^ä.ADDøÄRš?EÀlÛ\ÿ"‹¹ÛÚx î}áÎV‚ àOØM±ÆÜЦXîöi¼©á„¶ÍF;*l.¸f[ði„LÖS}–ÕyçÏÈ‚s.¡M@ŠË«èõ/V6ýÂF]ñ±QÚnÝEeUìR×@[²Ð½sÏká^»yçó®ëåÓÌ ÃíìÈ~ÞÔÑô-{ÑÜ7…Ι“Q²Ò‡K7Ò—œÁnu#5O;ؼ슳'RïZ ¯3Øì û}¬Øò–Vаþ§f—sEU­Ù¾OsŠü±!v‡ÿzÝ.š6n(}»q·¶à~©X*Br ! ™bMy”Æ¿ž¾ÝüånÐÈW¦X;ö@Ÿþ¥&Œôk¶Üé J¸Ÿ8”_DGÔúã%ÚŽáóŠ(sP:Ï}ÁQnÓ˸?ˆ‰ £œ£ôÓ+Ï!¸†ÐBó!k>‚¡È{%¢ùè•Å*L =‡\ÈbæÒÊúϼï4B°ÙØ‚UÛØ½ìA~7¨…àxÿùw[›…aGåÿòÎæ»Ó¾Ýu0ŸþoÞ Jd!fXÿ4Z™•£Åσ“¢Ò*úç{KxA%¼3ò–Žjßì>pœöçj» -8É{”iÏ_ý|%c!g*oî·9ûö ÿ yòÚ~ “F¢—>YN•¼WAC“Q›]ýä›MÚ®Å) 1öoäBð5nŠån_°— ô ó–mÒÚ⌠™,pdÓÎù”À}6 ͘Á»‘±ïòîëòð‡ô›×|èdçe"4t©¹]ÇL¾€Ö` ×^0•µ¥ÚfzˆŽÝ~ñã 謉#ØvÝLÛrrµãÄÉ -µÈˆpºjÖdÖzdòŒoúm¿U˜6v]Éïú§%Ò¶½GiÖÔQ¼;øºá’é„ÙÕ<;:süpÖªÖ>Ù‚Åã†i›ù©4°‹òáãÅtõyS4úfM¥^ÑžUņ`ø]/f!&޵'Y{sµ÷f~xÍùS4-´*îF PM±Üí ñŤ×]8&D£gÐÜBJIˆ¥HÖfâ ¥óæ‡Žß÷¶ëÂú;K)¼ñ£A á#KMhüµ[qdx¨Fe]óFX}’â4ó'¤‚€ ½ ˜bÝpÑGtÕ9ÿáM8ÓíÜåo¢·_F+zÐËNŒ\¸Œ@>¯÷(jv?J£Eøp<‰èwˆðáwE" ‚€ ÞA`ô 9tÏU«èô1÷^g[˜mµši³ÝëKïd$©ø¬âSZÉ)‰ìÎC‚ ¸‡‹ˆÀ-Ÿn¥üÞÇŸb¶˜/áLYu־ܹÅz‹+QµÎªÃ–Õ¹½á›—Ÿ|x«·Ò–t¼ƒ@”}è8•UÖj›ö56ÙüÉ{’:~ Ãx'â¶ONަ±ÃúL¬ü1;ÿþX&LSÞ‰RÊ>|œJ+k¨‚7±ljnOÜzÜiSÊ ^±–m~œŽ®×h°™bÝG;öHO’÷ÁåmŽûÿŽtu×µ·ù?ZUC§56iäÇV›èýÊ ~ß§vÖ’Oà! ÂGà•™W)~≷#ŽÿJG–LSÿ°Y#Œu–hS­.ÌÜä5ÍX“!ÌRmmÒs>Oßýè_NX¬ºçû‡õñ‰'î8µ•µW¹“Ä:BÀjµ’‘7ê[±e/­Ü’M•µ<›f¥(s#E6U—GŸ»üŽËžŽ„ÅR!œ¬ÞNñÑátþéãèü©£ ;÷TpäEÖ^m¯x ÑHgбM*†yž cj5[Éd1küÇÅDòF†czœÏ9“œ€ûg´§å¼Ç öžðE}BžîÖ)eе7w!}—õ$ï—S¨± L±¦¾ƒÎÿ{ŠqfÍ¥{Åÿw›wSe ÷':+·§zÒ‡Trkjt) W"Y)œ,¦xnS‘Ím*œÛTÏ÷)Šÿe\þÕØ{„»³Á@ !:2»ëþ¯ @Ž—WSiI̶>%–û” ¥Oi)yä¯ôÜ/¿¿"DtÝýØ“Ÿ0æ¿Å=d¿ÕùÖaeûh`õ]¸©Ñà4A¦1$œòb‡Ð¡¤‘éy±ÿ^Дwÿ|âÎ7ÿúÄ7œ§§“‚> »÷%‰A7ö¹€–㣯7Pym ¨Î£É¶ò'.Ÿ0­Êþ`ÒHúrE#­Ú”M7]q¦¦ éN×¶Žü¸d#kyêy_0JNˆ!¸ÙÕ{qअqÇ"µõMÌÿVZ¹y/ol8£Ûùw¤I®½‡@ö¡côÞâ š–£;ê(w·NÁkx¿ hÝÎhËÞ78)S¬ì# èü)ÑØ!×t ÿë˜ÿŠŒ@ÿw¹ü+YËUFÅéqTJ¦æ P»¦‹‘C¸O¬1RÆ} ß[.—>ÅEø$Z"àñ sÊ9çßú·®YùÎ<~kµêþ›Øp2æÂ#Kô“вtÉ ')„gg}>ò^¾O×·úG¥G5†ÅüdÊ9³j¶­]¹‰ó6BY(á|`ËÚ˜ñgœyYzjrêŒ ÃùÖ»B‡Ùl¡å<ðŸÞQÕEÄåO\þZ¹ø²üUÙg–ï#”}~h ­Ù_Hál–5¤oJ·ì­Ñ’ÿï¡“ :âyæûøRBÚÈ#:2\Ûİš~¿û0óâSþ7î:HÅ'Ë‹v~¿î ®MØ Ò¥‰ÞÜÖìíjÒ™çܘš”Ð×í‰1ÔÚÓ®ç|÷Õ'dìI2èÑîΦQƒf³©åAÂó5^Œ¦:ÚŸ·”r 7PFòx®¯)ÚóŽþÙø_Ǫ€…Ž)!~ ›[1}¨f¾ HùÄDïä6u˜ªkÓ¹MwkŸîTù—³¦'‡…ŽcÉQTBMd(D‘>ò)‰‹ ÊÈ0 «®§»¤OQøÈÙûxkÌï5³ï³()úMð ç†UÐ_½ÿ3CF-–bt@¾×˜gü3úÏŸ=òçß2ˆ½cïÒý,ùmŽ™YE3«eßï¦ù«¶ÓòƒtÕ¾yÔå<¯Þÿ©FÃçßeѲM{4ú@§/B[üG²¶#5)^¨ø"ÏŽÒ„ÀÕ'9ž@Cwðß-òÎ}P¯–sÝýlù­,{ª>wë”2ÅrÇ+–#ÿQ‘Ù”ÖçUŠˆÈuP¾D¾ii¯èè®6åÈÿIÖvìèŸHUQ˜Kêþ€|w H$ÐÑ]üw?—’coA@„ÞR’®ñ¡c§KXãña­]¢ ±x¾¨Øµ¬ÛŽ…üAèÑéô¿íÁG.嘨—"€´ Y—Ÿâ3þ-‰õeô1›ÕózÐëÍàÈÿ¼eYb $6µò—Z@ÓGßlò ÿþÂgo£Þ«Þgÿ°PÿªOÀÙÝ:eóŠõµ½Aá}ä¸A¡ÿuÌ1%'âÏè eº>úÆ7} 8ÿXãSn ýljåOôÔ…èCéSü©X„d€çF/¿Ô›“t¿¶êô}ÏÌ_mèÉï¶p=g[e`ú2úMñkŽgR?Û« Ï0ð†–Ʀ&ZÅ*yp?3oUj<œÉGÙŸ™¿’*êiEÖ½Þ@œù¯âu‰qQ=ªñpæ³Õ ©’¾{›ç¼äÞ; ^aq/œ$ÄúW}‡žÖ©ÎL±¾ÛògúöûÌ#%&,ìq‡s©B“ÄtÁã–/Ú”*´Ùƒ)1=®ñp惩1šÇ5_ðïœŸÜ ]E@w]E,0ãCËa`÷¬Î³ö„¿+°.Ðö+Žšùð+ +|øK˜`qy õõXÜ|Hójååšàqkí¶}½ ô{œùß¸çˆæÕ ¶ñþ@<$­Ù¾ßküû½…U¯àžeæõ X{£NµeŠea§!ßg¿Ißlܬyµê©5Õ'Я[k¶çxµM©òÿŽË^­zjGgüƒ.зZú”Π’÷=€€=zd©¿ù7˜f%}:»ÓõëÁ¼FŸNŸvõm÷LgœDûáAe±Íú›4­Çáü"ªi4Ñpv§ë¯´U³fæÈñÖ~˜<6¿ræ¿–ÝÜF³G  ­†µ?Þâß_ù tºP¯Žr­fÕþ\Ÿ€³7ꔳ)æû’ÉC1Q;ýº8A_MÑ«mJ• —ql„_óúj¥Oñë2 VâDøèý%¯i=BÃÃ.á +ïãáUŽu!ÞÌ>Г*Ú7KKÍÎ5ÔÐÐHûr W+öqq3Å6>c7ºïileo¥½G t{¢ýh“®XØÇÃ_hƒgNoðï¯<:]ª^e>¦••/êÜ1¼´7„7ëL±®¿ðCºâÌ‘É8‘ù·jûxøs™bŸÐé­6åXþø¡Â>þ4ú¤Oñç" ZÚ0³,¡w#À]f¾4;—{²à°‡ž£ú‚£Tðþ¿)éìË©Ïå7PÔÐÑT{pyþaj:YH)^C©—^O{#ÜtYlp:ëõ†Aü1zvl³ÝÛ÷$è2N}`û‘„k]£frUÎ_EšÜÞ@å1÷nʾÿZ­\Ýû??åÒ‡†Quvå¾üg647и—PÎ÷QCþ¡ÎHlõeK+k4ºÃBC)„b»³÷F[ütz¶MGsèz7¬?=i{ ÛA·ò&fÎa_î Zºq7ý⺠èí…ké$ï@ÜÕÚ Œ¡7øïjÞß5T½*­¨ÑÊÊÝú„Üæ^8NVÖÒÊ-?h™Ÿ5qM9ˆb£#èhÁIúpéF¾HSF¥7æ¯ÒâtõŸ·ëøÏìw Ɉåõ9>Ý@°«¼¶ôu^kSŽåob;f_n Ø?]}ú@§ô)]ENâûÑ|øážOelàQWF”±ÆíòŽ9bÇO§â¯Þ#}d º÷1ªØ´‚öüúj2UUÐÐÿ¡qZºr!¢cY8¹ÌmÎA§Î Ëà”æÃmºÝ&"À?„iöõ€G–†ÆF†vŒ«[\A»Ñ÷Æ{©ð‹7xKeÞQ÷Œó)qæE´ï±;¸ü¯¢ˆ~ƒ)õ²ëÉTQJ'W, ~?Á’÷h¬d¯\ ôƒwB[üë îU#=ÿ€Ÿ;e$­ßq€ŠËªèÝEëíÇû¼C|»0>QR¡™7ìØŸGçMíÉÚ7z^)ì þÝ&@>ìU¯*jêxàíž ‹ ú÷I¢Á}SiÓîƒZ~çOMF ¤ÍÙ‡éÅO–Ó+·jÏw8ÆëJBè´áýµ{wþy³N)þ«jy®¡ë¶;ô{úÞPéµ6¥øGù7†¸_þžòÔ•ïA§ô)]ALâvîýwe’‡·@‡èIn1ºÝ[¦]ûS*]µˆrJ˜r.YyApÑÂ÷©©ø8|ü5l …&¦ðs•|ý1¥_{§ÛôƒNž£†/T²îà $-+ÛdÛ„ÆÆ&2²Ç«03”H]Ig]ÂŽp*]³Dû8$6‘Ì5Õ\ödª,#cÅI2DØ\×-x—â§žC‡w=#þ465™4áôƒwB[ü»;K=nh?n@ÚÍ{:@Ð8V\f?¢y 5ÛYè@À`2s`¥&ºçz4zƒw0“o:G@Õ«F®£îÖ'ä2sB&﹓Ou¼ ‹Ö§ŒB‹×í M{ñZ’zž0°µU v·dÑâwN]Û1¼Y§Zòߨv†~öT¯óNŸ¶ù7»©Eínx@§ô)ݺä×"|t†P`¿‡°Ã`%«Û‚ ˆ4‚êØ+[ëIÇ&1º0›ý|HL<¢PDÿ¡Ú¹î0{é;ˆã„k÷îücõ¶&4ñ·Êã•Gô»CC ~Óe€…Ûðv…MwCÄ LªË=À¿¼f-‰²ËÈT[I™z™†<ðW:S©xé'Ú;ci™«+µúân~VNL74Š—®¤¥¾Á÷Þà?59ŽŠÊ«Úô¾5mÜúápÕó ¡Š¡Ö7)-É=áixÊ?Òà}ë®= ¨E¥UZé\¿BX€…píySYË6J3½Ré–VPr|Œæ®P=ëêÙuÊ›üw•~Oã ÿžõ©žâ/ß ÎˆðáŒHï»Gx­±Ûƒw]X…&÷¡†y:U»¾'b ÇðG^¤¾7ÿ†ÜùöÜʃ=„†BŽÇ?¤áéî› ðLš89ROl‚2° À¹\<,Ed ¢Ææ² .ÊVM1#'² Ö…T±e5³S‡zžá~Ùcºq€w‚7ùOŽ‹¦²ªÖ&k) šùLÖÞ#-H,ã5+Ø·ÃÝà þÝÍ[¾ëU¯<>BØë:TŠ‹ŽÔÛ³'$Ãû÷i±®¨¬ªV#Êê”7øïaß¼õV› vþ}S:’j0" ƒºÞ]êÀkw¾n a,x 4ñ¬6‚¥±~xðª;œC¡ )tü×´ç˜õF0–•hƒÝ°ä4íÞÍô:j=ܦßüùüÐÚØ<΃xOæiÃ’ÓÉXZh‡NÒ¯¾ö=~'¯ûø)%ͼ˜úßö;û{Ô“°,Ùq/0õÝ—/]II}ƒï½Á?‡Õ5 ­H˜6vˆ¶Öë=´q1òïH‹\{Çzå‰0Ç‚ê 4XW4oÙfÚ°ë }¹jÅsýIc\ú"Äs=t7xÚ¦¯·øw—O¾þ=ëS=Á^¾ÚB@„¶Pé]Ï”â6WÆr&8„%¥ÚÓh<‘OÇÞþå¾ô?,€$kGcÉ í=Ö~@ó¡¾³Ôõ E»]ÄÎ>Pà,fëpïnh*+¦$›Š4XÛQÎÎj÷ïÒŽ¢…ïÖ…¨a߸ tà¯Yˆr'õ-Òñ” þbšŒŠ–˜¨3¤/9k=ð³Ú°Ûw;x·ó–;DàT½ê0Z‡/«yÃO„X®C%ÍžÑÂBlÎ'±®ëŒPתëz¾Nyƒ™îþç¥6ìüww±I~½>zoÙ:ræÑàÝÒP¯ 0¿Q­ôá‘?í\ö„ôK*^ô}M@xú@-Z (%|x˜Lp~nÿ¡ô@ðr¹äXöµ9;(vÌdM»—À^ÐfPÕžÍvÃÓhߨtñ‚Ç  *;oñ_Ê&WÉñÑ*Yí\v=·•Ýt÷U«))nˆ½ì#_ÒÂu÷ñ¦†Ðñâ-´n× öwzñï3§ÒáêzaW]3xýbÜJŒ cµuôÑÁ£ôîþ#k³ú¦Ñ“SÇS{AË­®¥û6dÑ!ž¹nèºqøºîÛ5¬Ï• 6"|vùuF½GÇÄ ?{½½B'>{]ÛÏ!û·×‘ŽÍ̵-}½cÇó>WÜDÇßû—ãçÞ¸ö/Þ ÆŸÓðöÌdÙÚ¥”þ£ŸQʬ9¼Ç|ÍÝò‘çØÉÛ©Cø°O¹ÜL¿æmÿ—ú<ï ]å§«ñ;+Ë=‡Ó™3iBæÂ>Xÿ‡ߴùÙŒñ™¼›ü 6¥±y2j3Rz›Ÿ.f/Ñ›ðf9¬Ý¹nºt&­Û¹_ÛæÄÉ zýËÕÚ¦šFTÀNçÓÇ £ÍªçžžÝáÅoÚ£sæ¸û(Ÿ… ‘ቴ~÷¿éÈñUÚ}]c©v^ÏBÇÏ—¶í—Ý—iϼñÏ^ÜùFÑ:)9‘Îè“Bʲ •“RéµкºrPºÿ´Q´8¯€jØäîÙéh  oä¤G'¥§¦M ¿[Oó§»GgÒƒúÑÂÜã*i·ÎžðâV†ò‘ à„€N€ôÂÛfÓ%÷œ“Ú{¨lÝRJ=ÿj:ñÅ›d©oÛ qÆ…Tì0•­_ê”É ¥>:'iw|¨ž¢y“¤/Ê ñãà;D‰¶}õ¨[«nƒÙJµ&+U-TÚh¡†É?¦¨0÷¬,±w˱wŸ×™Ÿ\¹6Q6º°–ÄrJð‰O¢¸‰3èàSîo2¨Æ£:Ô³®œÕ·¶sW¾lkF–oþfŒ® ­cØžDE†ÓÐþ©ôñ7›Ú‹âÒóâ²Jzý‹UMáÂæXìIÈ6`ü“/ýß©étO ÀoŒFzæ­…QŽz`ât¨²®‰ÂÂÜw%^P\AÙ‡ÑÄÌ´~ç)AÝQ𱣇dPIE5Çõl°‰´ÛîÝ * w¾Uß„‡ÅÑ”‘·ÐÇËoj~¤cá#ž J¶Ò±’,M;cådåš<òvÔžkñ®«7ŠvœÝ *®~ÿ³ÑÃiÁÑcTÞ¼Ëÿ4 !Hç“ùô+Ö‚ŒOJ 3ÓÃíýÙí{èdC#½°{½=ë êI¼æçÃGé.NËáCÑî ÿ]å[â í! ÂG{ÈÈóVä½òT«gÎÊÖ~M8$ô.*·¬&l8˜ý««:аï䎎B]}#½<¯Ù.±£ˆòN`¾^×¹,¸çÐqÂÑ›Brü0¦Cyïœl­<à”qÂð)sÀ%´/o =±–…%››í²ݔ–86 !G+ŽÛ¼A:3rz*;laÁvOy%ÍÊ`glÚ Á!¿Öæf9#*J>~à8ÄŽ¦0¶:@< ‚@ "àÙtZ r-t ‚€ ‚@·#;„M¨JÙaƒÍd×b1RNîbªo*ç÷ChîùoÓì3Ÿ·ÓUQ}¤Åzû‹¹ç5biì±,·Æ&H8’5œ8†ÞÙ˜JØ ™×³¶Z…úf¼¸pÛ=ùà÷ôIŠg;÷ ”œ”Hq±1É,rw7<÷ÞRÚsx×»Ï? íIÁdoWwüþ±c†šên{R :¶«7¿ZO¥U¸¾U‰á9.º{÷:å%®ÉXKó×ÜkGâô1?§³'>@ËÂÓÄ‹îkhéÍ®’ \%G‡†ÐΞNÛycÉÙ¼ ¡²ÉH‘lj¬BTs»¯fÏzÅì% Fcl†u¨Ê&¼i/äŸ `œªåF¸+‚€ …@u]!ïq’Þ.Ñ¥U)ÄÁq2´8ˆ‹o5@£Ð‡'T€ÙÔΚJel^õ‡MÛíÞ«Ž×Õi&U©ÍqÆØÜ,+Á%…ŸCóQ\/‚¯ÂRΉ€Yn~K5¼]é#[î‰à·Ä a‚€ðtQ¸I‘¤zp팣·…²ªÃ™Ja¡1k}GñZ‹ùY õMH§½‡÷9)$ÄCHŒj¿ÖØ¿:6‚28Öö»hà2}nÆdÀ%ÞÚwˆÆ&ÆÓDö†…÷[ŠKY i¢;GÓLµn1”r*ª4w¼`{P³0’[Ӷ׃FÈ bÄì*ˆ ¿3֓ξœ2æÞMÙ÷_˲*wš}ò+ÍëÑþÇï¤ÞlN…رS©ÿR䀡Ú~;ï8t!¡4óðmÔHE•s€ 0ì¡ç¨¾à(¼ÿo⸠gÐûŸ¡ÒµKèØ[ÿОõ¿íwÔgö­8:úïÇ)é¼+¨vïNvÏüZ«÷þþ ’÷êøåÜ èý%©Ý " í—JWÏšL»yð²ï÷ØY˜}öD1 M£ô”{¢”­ÛAõ<€¸ñÒ3èXa­Ý±ßW.‚¶êÔ¬©£i&{Q{oñzÞßÃæJ6$Ä@gOA§ ëO¡|ÍY.å}?¢£Âé×]@o/\K'›wED4K+òω‘Ò“ÆQ^Ñ÷ì^8‚Ιø ¥Ä †¦*:xl-ßòDZ™¥'§#'V"«všsØcÙè,¬ÏgÏUQtnß>Ú»×Î9ÝçÛüô»ï·ÑïùøÛéé¦ÌÁÚ‚ó6l³Ç r”÷þhìE{¾Ø™“‹ B@„ *n×™…àÐ÷Æ{éħ¯j‚‡!&ކÜ÷ …g h•HôˆÓhø£ÿ¦’åó)÷å¿PcÑ1Ûþì6ðäŠÔ–Cûm«ïäÿ"3rï\>r_yR#2ýêÛ(ýÚ;ÉÒØrãÈâ%SÅ÷ËíŒDgžFýnû-ÕÜCM'OÐðG^äM çi{ƒØ#ÀÅYFP> JðÀ®ÓØëÃè°ØÅ®P±á›óWSûè¿uö™4uÌZ»m­Û¾Ÿ®¿øtÚšs”Ø6×B¢/p¬S¼ÜÕ³&i;š;ç9r`†æ‚÷‹YÔăÌë/œN•Õu´q÷AÍÕóySFÓ¼å›? ˜û†¦JÚ’óÍ÷KMø((ÙAo.¼XBÌæ&»—+040í JŽJóVÞ0üµEèË{÷Ó›çœA¯î=  §Í[ÜV4íÙæ’R:oÑwšËÝví¬B(»Û¾%s=·k¯z$gA `³«€-:ßžtÖ%¤ §Ò5K´Œ Q±d®¯¥Cÿ]«ŒÓ®¾N®Zij᧺C?¹†×‡7ûR/Zð.ÅO=‡wÄÞê;y࿤]ûS*å25U•kD¢üöÿùçÔXr¢ÑMÅÇ©fßNûÅ‚hÕŽ ÔX˜¯iưçKêe?nñ¿ß„‡…Ҕуè{쩚G°¤ÒÉÜ!Š‘ Fª¨©'¸Ú­á¡á¼3Ù'Ëkh #‚ç:Áu¤±ÉLŸ-o¹¯PÂÞSna)(© mûriøÀ4 ÀM\'3ù:5þ)7là ãcPŸÄÑv&L&^LÝì^W=œ4òVZ»ëŸ¬I´õCêy wó¢ò¯óÓµCZO޵Nj£à8—ôÏ CÕ՜ΩÅúí}+ÏG@4þ^B=D_Ä LªËåͯš}‰cyä¹?PxZÿVÅŽ™L'>y…Üõ0"£èä·ŸÛM²Œ¥Ed®®¤¨A¬RÏ;5˜k•ˆ<ð+P^•›WÙi‚‚ž5bí…°”tJ<ýüZ®ºÃ9\ö#ÛûÄ/Ÿ§$Äh›ú–ÁÉš-|µÚfú€§ÃÞ#,¨ ¦Ÿ°‰U- ±ìÙfÑÚö('xБ΂‹„àFÀ¹NU°ûåÊ,^ÏÐz}œ‘7õŒ`X…HpSbµÛªÚªga7ëTIù©ú©âÊnv_[0«Sr¬ùE§q%ÂY»="uQÞqÂ!Aè ´ü%í  ÎÀ3:œ]ƒ¨ñD^§ñõá‘dˆŽ£´«n'SE™Ä“oRÔð±öo8ðŒÖB‹=B[:ÍH3ívg mÅ”gm  «ÚÎmDèà‘.,‚B“ûÊ­ÍÐNMJ¹ôÇš©UåöõöÏš ¹ìû´ß»záH¿«ß8Æsü¾«kv“â¢5-F#»½l38ðàîƒÇi@ZÏX÷¥ý¼aÛd«PÎ×Iq¶…µê™+gGú]‰/qºU.]Í­Ó:åàþ£E4¬*]~ֺ✉4zpYvå.«¬as­(‡/\»T´ãìnPi¸û}O~§hþÝ/ÿž,?É»w! šÞUžÎÜØ‡If«¹®QŠ{—zž°ätªÍÙîœ^ëûæ™àÂÏß ’eŸkï#Yk’0mÕÌÖî›Xû–bs›Ø:ÖOšôaVö©ïìÎÃÎKë/ä‰#mý¸¢˜š aŽÑÚ½cÁåæjЇGPê…×PáoÛMîliê’«4†ñÂmÇÐ?Žï¯ÛŠþ-&תP\t$ïCÐrm‹sê~ÒÈ4£yÁ0ž]}ÞTÍCÑòM¶º_U[Ï{`×™¡òïZN««8Ö+,w:K«+ujïÑjZn¢Ó2üŸÈ@IDAThf~[sriD³ÙòAÝŒëÂ&sÞªS­ùw­?é _¿·ZýҦœù78„¾æÁ“ô éS<ÁO¾õ -å}“‡¤êXŒæâºÐ×F_LoSY1…$Ù¡‘oáu ÆÒb2DšÝ5×בžgÏUÀ`é¹jC£­Æ¦&›ÅÕ$^‡Dð`‰Ë¿Ã8ꥱ¼D» KJU:='Ÿ{¯ Ó 8Få:dìBكƈ0÷7ÔsÌÛñü[̧vv|ç|]Íë6`>åJ9¸/å=ANJ˵cÓžƒ4vH?û§H§Úis1ûË6.@£/øo#+yäÑì ÍÕú„lºR§ÿ¯ù˜¿r+­Ü²—ú³V-¿øTwÍuŠ…ZWƒ/ê”xoòÿ`6Çy½Mÿp£Ë?§= R¸Iú”-ɼMDøh–^÷Ðjll,hÒ5†„»Ä\cA.ÁôÊ•P‘µ†’yÖ;zÄxŠŸv.ÅdŽ£òË쟆§ ¤çJ} ³©¾¾ã£wŒÞæº9fêÔ¢§zviéJù[êY`(q¹üÁVêå7RÙúoÙÙ@e .ù5t¡ìActx¨î‰uñFñŽ3ø7óbVWf«aÖ·¦aÍ Ç;Êö/*”žÄ‹g# ®T‡² Í£¼£¼ 0¹*­3ÓéÍ6¥øáõ!pAïÇôáð&ÿ~Ì®@ˆð@…å&©Ú¾èXÞ:¾ÐåÅq)™úÜ}ó)âX; ÇþËûAð>#þü* øéCt‚M°j÷ïÒ> MN£¸Dª?Ê‹×] t;|p G×ho>»ðµDQàR;ØÊ›ß%Ç„3˜:rµüëŽî§È¡£Tržã&Τˆ~ƒ©dé'­âE MuG÷µzÞÖ[Ùó^2I1Lé)Á©­¸=k“®MØ£³p’… ÿ`g$w>³»-ç(—UÓWK¿š{¡&ܬÛqª®g$'PQYK¬½üA,9¼Á{yÈsÏPõ 'ÊÊ•ú„»R§ÒSâé¡[/§ß\o¾—Nï.Zo6âXëÅBna©k‹Í½]§Zò¯£úzÿv&ú¬Vïô)(GGþñë”XcÄc¿ }Ò§ømù3a²æ£w—>w;¶üªE_ì6n\顤‘I™å9®û([»”Òô3J™5‡MiæÛQÂÛæN±ßã‚Õ*t˜]ðêØ5¯Õă;[Øôkî ŠM+¨>ïÔ€¬ÅÇN7LŸÕbjbeÊw{ù•É;N±å¶-Ô$ËÚÆwzƒ¢C)Lo¥ƒI#‰Ë¿­ÏZ<+ä3ÿô oø:;(µ¿ÛûÐOì×ê®uëÞÅŽB}Ñ¡¿Þ§¢vxm,ë&ÇGitƒ~ÅK‡:½Tß8ójбGª&ÞI¹cí_Ccmþá͘0\swê˜ü› Ö8Þjûw|Á^‹'„<^«d?(=™’¢é“åGìÏ:ºmᬡñ”ÿŽòwî#àX¯2’c)2,Ä¥ú„Û«SpHðÌ[ [uœMø^øðvÅkâYû–3ë3ÆgÒ¾Ü.{ºòfjÍ¿ž]KO èèSÞÝZ0â7 /œG9ÞhSÎü‡sÂ}ª¨$®ãþ¤'a}a¡Ò§ôdHÞm# š¶qéMOñë…‘¹´¨ø£¼ØºÑ};åÏÊî½û<%_pF€ÆG!Ž‚GH|ÅMœAÇ›wÈî,ХїŸ¿€ãšøÐèæsË_àÎ’÷Ú@ØÀB˜p„„„PZL(åÇ$WÊ¿öÀ*[·”RÏ¿Úm4S.™Ë›T¾Â{…Øvï(!ÐÚõ‰ÓèU´c@ïNÀw* Åz´A¶ëÅÊ!£õ¬½HàE½}\t“k…É”ƒà¤&Lk¶ïsiv4a€:85Æ+ü;°"—^DÀ±^˜âr} ]©SØ”ÒYðˆb¡y({ÁZÁë@\ ¾¨S-ùïÚáÔÐ0Èrº=è}ƒS½Ó§€GþÇ H¡„Ú&Š«óOíè}CR¤OéöÊ'vŠ€{¿ì&+ü¥5À Þ´fÁ±V¡t]ÿY“þ”ùöh­Ü²šö=zG ¢½¸m=7U–Qö¯®Ò6œkë½ã3к,ÆÆòµ çCÕû%€(>?‘ëvP3t6Á#„ÂÂØƒïÐ?1‚ÂtZ7`¹Rþy¯p© ÏFX`lnj¨Þ±ú»w¶­[la@G͇} ÁÏ{}h¨«-©®oÔa65ŠÍÜ ¶I={:aͧ͂G›"y d¦~ìE'¬ª )‘¸üi@u /ÛG¾,ç²Çú“á zêÏØØŠŽ‰Öè½ ÛÀ;}ƒw‚«üŸ¨·R»-`S¯èH˜¦…‘ÞÍ<;£ûxÀõ)<aq9ÖŸ Oô ÿŠÔ!Ô¥¦úº"õ,ØÎØãÈÓö¤0k¯^”Ê{Éè(§°¶Ûêhêî:Õ9ÿV*,¾“'RLÔN>ïcí%ºrßìãwºðj…Åå¡<õïË6Õÿ{¹üO;VAÑaTAåìäÃä¦öÖ݇Üé«—³³<ÖxHŸâ v§gá£gñï®Üa¶„u* UÀ´ŒaËÊeë~ؼ)gæe³ç˜‡»(oÐÀxêY#ŒuÖ(c.Übtoälj;‡F}¨;¬cA–$tú†j ß³Šjömݱ-§‚6CÀãYÐZAso7¹bµK;òöçìê7dØõ»§ÓÇ m~ÝõL°pë= Q0²àLØÃïcè¸ÎD'bûi ŽIˆ46P”©šÂÌÞñâÒd¥ºXªàÔu\ù,”i¥þ ËG||,ÅÅñ„é½ ôy\çßLåMV*er‡^OYì:—ÏÞÞ8Ðl¶ò@Ñ¢9ƒ áI̸Oùí¨Cì‰K—¨Å¾9Þ`+PÒ°æç®KN¾ÏÓö¤n¯^ áv•J‡Kª¨°¦Égõ tôdrÿ¡T€Žëº^WG†J>ÊÖ;Áb #³)žqˆâúÍëÛtænéS@½+ü¸ü! @w ᣑgÌ^œÔ0ðDF¸Ñ¶ ~5ôìF=UúïT.I¥[á£[`îñL0¨UÚŒ¨°s &XÔÖVY–}öѧ|¿xÔÄ)#fŽ“ÍwpÏî…ÀëËNVÕ×Ö”¦ÛÞ·_ÍñIá<Àë¥;÷Á±qÇÿ7»ê%Î>Y¡ý€É•ZïÚ{{ЄfgËž¬ï~ᥕ[²DZðá6þöY:Ìk»g³àÁ¿YZ°ý€²YV¤7PºÑD5|°¼ÙFuÖdªõNÑkP^8ͳ’ñáŠÄ:ÞÉ<:RÓx@ðHˆ×®arï\žh=l*Ó3Öþt•Þ¹ØÄò™—ª>ŒX ãºŽ}Vlü‡t ÿÀëÕj±Tl]»bß:Ö3So>kü®^üevæ„I¥ŒE’'íIÕY» cHj멼¦ÊëMÔÈ&YXÁÅÕÊk¡'ëT×ù$£%’'=¼Æ¾C›R}JX·µ©®óÏåÏÓhÞäBúÔ0öæ%}Š÷ꕤÔ}ˆðÑ}X÷tNèú•öC hñ㬄’Æœ[wòè4ÍŸ!¨¨é_õ ?êR@ȇ™mùM÷Œº+5Bwf$›*dDêò¯iñ¹÷m©ü¿6­‡6@b¾UY <Ìy÷-ãͯ;WH™Óù‘{A áE59{>¼b%Á¿²¥Á²º‡‰ sYqK|@ø@Ý€†‡»‚ª•@&6ý1¾º¿þɧ'Gÿ)!T?Hz¤á_žŸœÍŠÒ·9hmÁ4¡Œ™Õðá³iý× —–yÑGK¿ýãsôa¡( ÷ ô#`ðb»ovÁËë+Xøhh$aá¬Ù á&B(6ςʚ ˜Rå} &VX‹©0·Â{ÐçÍŒü7ÍÄuÇb5›+yßœEŒ'&”,mËÞ®VÏÿ|Þu÷þúzÆ$ÑÓö¤êfOÕ+hSÀ@øï™~µ§Ê_úÕòåì DøðŠ“†0¸w¼†ðQǬpã£-ᣫBˆ½ª¼ÔຩÖdj|å@ý=ÏNŠ}#Æ ›Æ«|ãÂt/m˜Ö8sqÑÛœ¿ú–/{uŸ8ìØðuS]]MýÖµ«>ПwÑÏß]¸Îú³kÏí*övÐÔöÚ5 ¸Æ~Q‘‘šà¡-FobïO¼ÝÒ¼.D îÈ ÍJ»°ÃZm¦&PÈ´X\¡Ï1“ˆ hôfFþQg J*hÇÆµoÔÔ°[-›£@Tó&Ìþ––âQkW'k~غùyÝ´3þâi{RŒvw½RÍBåÛ“m (:º«_þm5OáÞÝå/}Šjùrö"|xÅÀJC tqV3ƒF €@ãá(x¨µ! º;"Tƒ•¯}SÞdüªØzÝýõ Ù~uÏw³aŽõ-sú„N[Xü*ç,å€!Ö¹ ´cdž5»Sû`ÖúJú‚¬·Î9Kç®?XÊu-ø¸†;[0 ibׯ6­‡YÓ|XY‚𡦡ËyjWh>'´ØÃ¡š6ÄæÙ tù* ÿ˜Ä !kïÝ‘œ=Ÿe­þn+cªêêêY0i>ìíj÷‹¿KJ|ù¿ÍÓöÄih¡»ë•?µ) üwo¿Úå/}Š­­Ëï"à±ðÁ#J,Îð.Y’Z7 €ˆ@Ô´Òv@èP‚‡z"|€•„u˜ÞÞSV9w@úE©zËR¦f2¤ã˜¯dÍI Ÿº°èßø0°&Âü îqD®\ðÙw,ûÙ'JË­7\r†ÞÓ5 øÓvê… Àˆ&t°™Ÿ:§Z€®žuwÙö&þ±\È«—[LÆê¬5+?Ú¹qí¶æz„=sPŸP¯P¿ÐÞU›äË^ÚlWßÎûpþä³g[fžs·§xOÛ“B°;êò@PmghSŠ&_÷+¿ïË_ú­‰É¿ö€²!«ý×®½±Õd×â¶ëîGŸz„tÖ' Ñ!)/?üpy›‘äa !à(t€vOë‰ã`׎÷vlr®[c)ÿšgÞÏRuzý“SþºïÅghš`òÍG<É|$ñ‘ÈGQQQg\|ù™CG9ËË?ÄÖØÈpkB|´."4ÔÓ2jebå‰àÁôjA Ú»WÏýáì̯ó½;4úŠÿ£ÑŠ]»±yÓ©3³Ðq$gïšï¿ýzu}}-öÊÁþ¸ŒR>°‡ŽZû$XB»í***&éŒK.¿xðÈѳ †8o·'°s=r¾Wñ\=ûªN¹šWã9óë|ßÕô„ÿ–]½3]ÅSÅ—>E!!çö¸÷Ùg͵¦“lŒñøkO?öL{ñ\yî±æƒÝg~c²˜ž¶ÔYfs†ï»’©Äñ{Ú|Iù¨¯öUï¼%íRc-d ÈyÈ‹÷(xOëCxÜïv"Ͷ¡ÝLí®ÇžÌgÊ=¯=õØen&!Ÿ vòçö,ª7~Æ3d—«‡l]ðmŒ>ñ:(êY/ ñHàšØYB+âè ñqx¥s:L(ÓEe2‰u\ÑF |`ÓNh@ ŒàÌ®ðM°iWÁVâ¯;HŸâjAôÍÝ=µ„'†Ç½þÔãýØO‰’ïƒóŽÕ[Ÿ˜uÕÖ¬½/³ò3 ÁÒöÅÕæò5Y׺|ê¹'z)B޳´J ÀÌd0« ­öb–ÃQ3‚Á‚úÆv'ÿ{#¨Jð@Ý€Pº¡´6 t@0‚wP‚Qð`¶5¾Á?pPmDÚ‘ œ2ƒ–>EjC»`|o±Z/å.ô÷íFê Õwá“ÖQŸxâíˆÓ±}lz\¡ MñÚ÷`Ï ‚€Ç°ÉÕã  æÃWArÒ œ1P€àá¨õPÎ | Æ·Òx(Á…]vtE. wéSºV°E½û‰W£¬Æ’¼Ì4¡oHÿ‘O%H ¾#¶µ5D/rœ±:==èMÁùúd°,Nûu^$J¿Xöäþì³Å| 0ˆ…¾œ©f·ßAòLp±%»SÏ(X;Ro1ÃüH ‡2 Ö¼àP“!3ÁA@A@ÚA€…j> øu.6ùɇ·¶Õk»Eøðµ’ à„À¶+úM°XŸsãq\’ªÓ]ß‹Ýñ:¡ ·‚€ ‚€ °o• ,“ߟ7™ÕÆÙ™°ZÇ­Ö-[g§Ým&‚€ ‚€ =Ž€h>z¼„o!°mvÚO-:z‘Jac>-Àís…ß3~q^¹z&gA@A@žA@„žÁ]rõ[¯Ém5ZØ–u¼Ê‚+ù ^˜{÷”…E‹Ô39 ‚€ ‚€ t?"|t?æ’£8rûàˆÒҺ笺×1+öUý_ƒ!êþIóV8>—kA@A@îA@„îÁYré¶ÎI»‚Ýf¾Æ‹Ñ3Töìòù¸^§¿kòÂÂ%ꙜA@A@º>ºgÉ¥‡°mJ؈u 79’ÀkAÞŒOˆ} ïŒîÈ®\ ‚€ ‚€ àˆðáÅ Dø¬9}®¶Zu¯Õš¦òb-HYõ÷O]\8O=“³ ‚€ ‚€ïáÃwØJÊ~†ÀÞkú%×Mÿaäz'Ò–†“î—ãvz.·‚€ ‚€ ^D@„/‚)IÛædüÈb1¿è¸„H×Àš§##Sþ>v^vS`p"T ‚€ ‚€ XˆðXå%Ôz 7 «ª¬~ÚJÖ_X­tj³MíÓéõ÷Nýªp¥—²’dA@A@šáCªBP#°uvÆ Y^!²NuBÛ1]oøãÔ…yŽÏåZA@AÀ}Døp;ù²— `}â ý¶¬WîµZ-¬ ¡x;[:]½ÞJÏED¥ü•M±jìÏåBA@AÀ-Døp 6ù¨7"=wpz}]ýslŠu£…¤×=6uʽoëžxÂâôNnA@A@\D@„’hÁƒ@Öœ¾gY­æçØ+Ö4G®yAúNN÷À”…E+ŸËµ ‚€ ‚€kˆðáN+ÈàM uÛ®J»Éb¦g™õþ-Ø×é†Yé‘ ‹‹ö´x.7‚€ ‚€ " ÂG‡ðÈË`G nÿÈ¢†¦ß“E÷›cE+zQa +þÀÎkûõ76™~ÉZ»yH’#ÅØ5ÝJºE¼.ä_¼.d…ã;¹A@A 7 ÂGo(Eá!àÈŸÛ?²¸Îx kAîãu!cœ`Y?Xõô†Ž ïM]XpÒù½Ü ‚€ ‚€ ˆˆðˆ¥&4÷*¶\Ùç"Ew?ûúŒ…‘m;§“•ÞðÆ”)w/—꽪è…A@A èh1Ð :î…aAÀØqM¿F£ñ7LÒ­,pÄ:“ÆÚ\M>2„„|2iþñÎïƒåþé×ôÿyä®ÿùµN÷{5nžyMÿZˆ5ôoÝÓh_?ó×7C'XÌæŸE$ô}è«W_=óºáN6uË{ô.Ó2õÌñüì[C­Æ¦Ç’iÒ=÷ܳըÞ=ûjÈÅVåN¾7t†—þp—±…ûäg_3üˆ5Zƒ¹ÛòœúF΂€ ‚€ @¤AÀ?˜øåñýÓÿ*,Q—a Ýì k£#eÌZ­Ö?šŒÆí[®è““5'ý/;æ¤uŒè×{=ô Î|üõuíϼf¸ÏÙ…ñï¼óN˜sÜ?ûZè4^K3ÛLÆ{ß[Ì–ŒßÝ åÏ8>gmÓNop‹gÍ7O¿r‘ÕØø§w¥)1/\ÅùÛ›¡Sكٿ Ã?H¯Ùd1½û÷WÇá½Õú„ž…£gx×ûGøæ"õœA@AÀ†€R?C`Â{Eµ“½5mQÑLCˆ~¬žtϳùUËuViµX7Z,{²f§e³ ò¿ìÖ÷¬%ñ3vºFŽ>4,–¿½ýöàÇ-˃z«îž±iš±ØRãì1L‹ÎBÁí:Ò?Ê‚ÆÓ` :«î#ϼr®JÛª#£Ž¬m¦¥·X. ‹ºœãTU›ìqLfó¬…zþw³ù™i=‹C¯uÆ»æ?ßüëV'›C !÷²–*Då#gA@A@°! ‡ÔAÀ˜¼ ð‡)‹‹ˆŒLé§×é®d!äÔ¶Ø„ÚcXù¼—Õ˳®Hû.kNÚ±ÇkJÊ´òwÖ0Å› ŒùW«bùÛ›á#YàHúÃÝFèsБÉal5°ÿôÓ±a¬m˜‘2w“_ZhÌ¿A¥!ƒ¿Óëô¬U±šßúϧ©1xÇ ‘=µJ ï¾ÇòàCwÔ²´c 1›í +#(D¿ q ýÖžLÂõƒ?kÈeS«Çuf2ñ­ý¼“ ‚€ bv%u@ÆÎËnš²¨háÔEE7§¤DõÑ ?âÙ÷OY©sd€‘p8ηZ¬Ï²0’Å‘’-W¤}¼eNÚ¼×È@Ǹþz­Óéß"²Ü®è³˜Œ?f^ç1¯,ƒh+òŽÂ€Šw¨"gÇYóû[‹jõzÃk,3üB½ƒ¶„“è‡ï2­âU”—þïðœÎ„“Åh± (,¬$X-úZ•6Yô¥|a¿ç kZ\H×ñ¹A@‚Ñ|E1 “½ !ïm˜òÕ‰/¦..ºžt†ThDجèU^#’ߊO«5™Ý×ó"oX-¦Ü-³ûìËšÓçÿ¶ÎI¿=몴ӬsçZ}ÓÆÇúŠÕ5“Ÿûod?ÂÕ\ƒÞð‰YFƒõ”&B=çu·1¯cž~M7ßb6=ÉBÂ$¬ÏÀ{^Cc"ûþ‘å˜ÿgï<࣪²?~Þ{3“N ”„.H‘"u­ØØAý+Š ìmuUÊš°¯ºVŠ+Še] E,ˆº ¤÷B ÔiïýÏy“7LÂ$¤L’ÉÌïò^»åÜï½3¹ç{îðÜ$ÛEœ¹‹ËªPùàç\žîÃ×9¼%K²U®¡âRJ dOxžÎUq¾VzA@@ šøßæES¥QWˆ¼ˆ zg”|hÉå-zñL«Ky~¬O òö½‹¡S¶ Üͯç)Ó» ˆ§i-çþž“”Éé%鎳V+Ÿ}æ­/FC†¬rñŠUŸ8ΛŸ™èøÒPÜþv‡káãwòpžƒX+œzi?§&¶pœ¤*Úõ¼z¯@Enyy=^±~ÜÆó«<ŠîSdµ«gþm¦{¼Ÿ°äíJ( n6›ø•N³‰§Yuæ|yCoÇ2í’s+ØX^+ õGˆnP>¢»ýQû#>}¯ø"Èç¹åCS\‡ŒÓT¯r6+Ø2ÒŸ§d•^%Ê0âxà|*Ç?•-2_ÈTHd5-V¯çiLüÑ×뺺Þf‹]ßç«­¹uŒ}3¦^σ<¼Ï‰9½ŒÅôVÜZ€2 ~-¼Ölý™öÄpÏÿ¬x¯}˜²9¿ðàÆg?JnbÊ2¹~e`äížßxUªÿ°ñ0O‘šh¥ vÍ8¢|°’ö¡—¼ÿd“^éfEéN¾ÇÎìGô5US<ì÷ß×`@q@@ ª àcT7?*ÉdÕ,®ß%bŸx¼ñ*t*{O¤ó}qHowVHø;P}ø¹X8èäq¯¬µï¯g‡îõ¬œlW #‹­ YiY1ŠgWϾ÷ì ÅFˆOÞá^ÆÓ§xÜoܪi¶a¦VdÊÁÿTàq¹~?‘‡÷xð˜ºj¿•È%·ÌðÀM9‡ÇOTßS ò/Um);œ³ju$$µéøTþÎM§r~‡ŽÜ=úŒYåxÙ”a=‡óý|ãÁÕ˹l…ýQf?q§çã'‡û£®iNÖåJMŲÒã  ÑLàÈ_Ëh¦€ºƒ@”`…¤™¢¬ˆèé<Òÿˆà IåùÈ*OÙl1Éâ£LEÚÏêK./“›Ë–ä빆ªæ¤æÚ =— [®=.ùPwê^Tvª×³“Ô{Xù>r„qRå‹MÌÌáévÚ½ÛN±±öÓ¦Ûìv×mÓu/l6«†¡ó}ݦvÅëåZ)†ß‡Îð’¡i ¯Ì“½4/¯¨esq|W\¬Ãå-ÔZ|R,"i‘ €€4P>N[AR¨«·Iq¹ôxäÜ…ÿì"ñ)]X9éľ$µ¶ÿ‰Sx+.Ål(2Ã<²Ò"S¦<\6;q+^–Cççl|‘yb|Æ&¹8üŒuRù±Æwø¨ˆC=[x §åÙZdãglÇP윟LÃ:rxO2«åÀõt±ðy,o>ËÇÈgùðù>ßÃÅïá ìñ*J¶æP¶ôé5bG(¬Jµ\-d  0ÿ`WA@€ °"¢,¹¼u[U÷vñªÆñ<¶oÍ÷ZóƒVjÎ9€”Øp§˜üù-ÉP[y¼z+ö IÑ¥1›&ó²PIW³E£1+ó¹—ÌæŠd¾ËoýÃfÙ_þaäM ÙâÂÖ6ªÈT2Ëòâa™=¬°¿ ;•ó‘0®"/flð«’À÷ÙXd¨|T¹~ìäÏŽþlYá¸V"bø:‘óð;À[éªsdůóýƒ‹Ùj2/>¶Ù<¥+¿:y! €€Ô6þÛˆ õO@ü,’ ÅçŽsÛ´XݭıßD,ñîââ_AªfçùVšª³oϧÒUMeâó¸_ôÞσ},t¾Ë+_yù‘®žµ¤z¼Äk~)œó„>u“ÝÆÅØ=ªÇí¶Åǹc]Åî6=:¹•Œù¢lÔzEͳ³(±Øð&©n#Q·iXÜf¬@¤reRùÇ9•ýýSYQiÅ Lyãóc‡’é\?³Â÷ )¶ox9f^¹ @@ƒ”ðhH X5¸G¢ËÛÉãñvb‹RVÄÒYÑèÇV£W, ÌIQV±ef’¦ÅM­«¥’‹Ç9€€€òHç  ÐÀ,¿*µ…Ë­ôã‰^§³uäžÎv[NŽþmWvæ7¦±¿Èľ3÷þÖÀª qA@"„ÀÑ "¤b¨€Ô”@iÓTœ;†§>}·êæ¿Xùõžö~k—³h$;v´åiR³× ñ¶õ¬çû“²CþµrÍÓ¿f ¾éÎ/3x§Fëym3¯nßRq9/æ]ì/å².æ©[‰G•©(?±~ò·þ3³õ 7@@@  ø×¥¯Å25€TŠÀ€ySb»Møò€yóJm€Úãƒw:uŸ:ÉàwÒm=>zçäò2ìþÁ„[»8ᦲϻ}0áͲéz}ðï®Ý§NøGÙ¸rÝcÚ”4£èà<½é ö,élÅÉ0 Õ]\ô=¿¹É´Ê£ìr~÷©0Ó|ðïÞºbÜÌ[¾É¼ÁS¢ú색—YiëâØï‹m»ÓgfOé7sïุæ-Y–á¬hd–*Û0Î&C_È›F~ºäò6J=À€Ô"(µYƒTÀüsn-æiCéûv¬»$0¥nx®çkÙïƒÒÏÖ½z×ÀçÖ9öÙÏšä@%Áºo åFÝëý }ÆÄxë¾n¸“xŠÒ_¬ëRG§çBU£ñœÉl^¡Ê¿2Õ?˜|& ±{õл¦,¿eøz~þ0Kõ ¤]5ôöå«o1dÕMï¼ù®L–ç öÙë%ȪWé3÷Lî?kO»fëË>"“Yi29Š@¼çÉÃëZÍJÈsæÆŠõ"% ˆ&¥ÿ8GSÍQW°$Àä):)ÃJ § QTõS¹Ç?ZnVPüÊ@`¼^L>‹ßòËË?ÿpR)†×ÄÚËJÄÜ¢ƒÊsVC±Ë@À'/7«u'*2lÆm:éïœþõ¿“xw@ÙL°”‰/ö‘ÿUÙD$ ï˜Ãy—±r(ݦø’ÓÞIñ÷¿6E¹ë³÷Hnás–>k÷¶„œÃ\2wOgÑLçtC?s`ê«æ¶ð’€€D(Ò¨DVÞå¡ð0©“®°òaÓ¬ú™–ƒŽRÜT|5§³ïÙîü¸û§³‚ï%ý.+ÝûšØ×Üx×>ÿ*÷ûÕër²Fs”"Fæ&ñNçqTÚIJu¶â¤gN´óófIZâ~¹'Ó¯ôbï žr•±b舥V¼p=²ò/U±Â ÈKFV<\2(m’‘‘¿A@BBXB‚™€„’€¹¬-/Ë«VÇŠG;q/´òç-7O}:Ja`ŸŽ[ ÒFò›üçíŠò+7ð úæK¾ù&FÒŠ±o_‚©´¤¶uŒdßôÓÙ·äl~tT^VYæ‘§]ZZºÛ›,`™Žïñá»lHaSÊã&Þcã§ß† )göBAy€„ž€ª¼G^…ßÊ—âVÔÒ CÏÿ¼+ûmt\=ôŽwEQ‹ƒ©À(ʯ[÷ïb¥SÝnSÑ0WÕ²i·°jðü±|>xIÚ|ÓúQ’ÉgC†xyêÕÿ±ÄǼLï*Vbn‰S”»åqQ. åÃ)¼×[Ýߟ°ÛüL´³/Á[’L×õ’=@”bMsßwúެˆ¯8*  R°|„'2È$Ðwúî÷y•E¾Ú±†×uOdÖµ¨MP>j“.ò!`nà¨ÏZÕá͇o¸¿SŒu#€€T†”ÊPBJO¿g:û~l²ôîá-ùÿ,   PP>ªB qA@ Š (ºboZtCÀ:Ç@@*C NÎïólºW÷^ĵ7£šTá@:žž”§Š8foÓTí»·Ç>¹¤!×iÙ•Ç5öº‹v²å#Aê¡iZöÉlÈu‚ì  uG Ö”ŒŒ)±»Ü»î#Åxíóm…t2”=¬|dq¡ùuWE” õG€•DV>ZñoaªaJ íäßµ¶·~##ãÖâú“¬ú%g^Öb ×k˜äÀŸ>kÏèê熔  ÑD V”á£Ç^È'óÚvì¤ø->RãÕYo?ùäÁh‚‹º‚€€EàîgŸm¢ê—ñ>7òçb~!³ŸÝ9iܘï­8 å¸xPê¤_™ò*ʪþ3÷ôl(²CNú%råcø˜q:½¤(ÆJ~Û÷ÀÄñ£ÔoQ:€„£Æ¥½ÎRõPTztÒØÑ¯†—„K³cp›¸ì"÷Þq0Nb:Jjï/÷ì­8ž‚€€€XÌC|Їñ k4_­ÅiPÆ#(‡€é¿¡æÒäåÄ ·ÛºªåøeR”¶þsœ€€€@j¬|È‚¾}<”*(@@* À/p>’ßRó7µ‚xáòHUu¿ò¡cÃ¥a €„9+²s¹l (ûx„y]!€„-ù •ßRùM [!Óµÿ¾Mì7/~   pL5V>¸„ö²s96<&kDr ˜¿¡ü[ÊÚ—)Œ4m¤ø•^lÄF¢A0&`«©l†b´â<²jšÒƒ€@´àßSù-•ßÔ°¸ýBJ‘ÿ'   P[>x_$þäWP€T‚€ü–Êoj%¢Ö{»îM³„0ü °Xà  P1[>*ξa=½sÔ¸¡<}àá‰cG§×Õ^%ÃÇdA™ P.ÍhI^_Jv8?\<@@ úD¼òqטgNgÎ_Ž4­RÀ ÆNvüÍ¡Ú^zsì²¹—XáhÁ›eõ±®kû˜‘1Ï–åþõ'2ŠÞâ² uy<¨íÍyþXQ¾~> ý:iìè3ƒ)]^ÝS¯Š‡Tý¢N›½V ãÎÛp;š-£[j¥d   q¢Fù(Ûroeü-{ø¨qÿãwÏÀg²Îþ=Ï=טŸ½ÀÓbÎægM RVÚõïoù³ÄågŸ³ƒeÎäq£‡¦5Ÿ÷<¬úN7ú‚{Ÿy¦©§Ð¸C7ŒódÚç·ž—%^¨Øéý‰£3Íø£Çþn(ÊÔ@%dxÆÄxûo,ÿq¿€ãwâp(¤Îà7ä¦ebÄè:ë侟ŸŸÃy¶ã89¤(ßi ê“Õ]òØn³r»½çxˆÞf¹–­W°kö)9ŸH¥Ô‡!ûx:ÛŒV¶6#32n->~|K*4&ó”¢K¹î¹ÃG=ÙÌÃPÆr¼Ó9~ûIãÆ”Rú˜ëR(†­gNÿâr>%R÷L7êÉÃÏG§ ¹þÇqý×süi-m#_ œÞÅeþÎÏUíäÒÝôw.s[¾žãݤÇ«Ç?^¬bülÖäñ£Ÿ'Ø=–ûn£K•DûpÊó¼Ìq°\͘ÍJ•Œ1–Òûàøñ©E…ÆH"}ˆÔs—{ü2žÒFšFžýeEòV¶Í9 ™ÕÕ<¼?ß #ŽÙ/b¿MdëÊÝcÆq_ô˜õrºÝ?p\7?ÛÏÓéΕz•W¾foòªÛ}ðW•èfÇmq$Ü5fü0¯n<̼dŠŸ?Ü5zìûËÑ‹oÊëE{Ј5¶–×/lö&³«RNyòZí[Qÿô Š“êPèÑ"%¨­ðᨘ'¢7ð€±1e]}à¿¥O¾w>æ½<4ÎþÉ`[yuý»{Gk_q¿¹¾•­'þÕ^ä~FÆ”X2Œ»8þJ¹vèŸòð^Ï6õf¾õ º)^­ÄMS'rt`@IDATbQn„TóŒÿ»ÿµ×bȽo!+wðåWŠª gÅcËuÄa¸ú–(ߪrÇ›ÉåÞì)ð¾nåSÕ£Jj!—õË{Ùð1ãÿïXéGŒw³ù–—šÊé¸ßsš;wyv™›MÚÅÅ÷§òà7›ïÿÁ,Çɇy/å7ÿÛøÞåg¼œb•swÆø^üìVVNñ÷g}Š ?¼?ãµF\Î5ªÁƒVÓ†{ßo¤ÓPn£÷¸Œ›•f²¯ÎS<˜ÿ\ℜçŬx|Å ÏFŽûˆªj³žûOEYb%ó¾q¨µ½Ësþ•9Q”f\Ï”ïþ‰ers9O²ÌÏr]šqCϺoôØ’®(:?û•OçO±¦*ãåcÓ?ä9‡òå­D›s›œÅyÌ6Hã…F’¢Žaè Ëtœ(s^EåwšY’b¼&íÁýòUóÚ÷_Ðò5òró½t•šÄ5Ouƒš—(þG#ÆŒ¿I÷ùq••§XŽ¿Š"¦hÊîŠúEUËáƒÊ+‚«ú…ÅIµ,ššÀß¹¾Vb-ƶÔ:Ç@@*"µ–»Ç¾þ/¿åm¡J¥,Áòty\ql‰ø˜ó¹ÑÐWÙ)úûòVàɹ÷ð‹¬dÍœàö¥iÊÅlYøÎ¼ä¿»Ÿ}¶‰·ÀóÌ÷$7޹*ã±!® Ñ*¼ÅåÄ« }ölø‚¯ßÐb7)báyùõ‘#eúÙ´ácÆõfN™”$a2¸¼•isV¦ÙE9­m£o °½kå=yìÈŸîÃ)Ýx Ææ˜ñzÆã;­gÖ1Xù¦hE8ÆÑ´J¹÷½Àmþ5[ꮥ§$É;Iƒö‹ª”cåTÞJôÏÀ>bå…cå xsè/ÛüûÁm¼·÷»ŽêK•Ï 1A@¢‰¿ÑŒŽà5”Á<ÅåQ\?ÇÓdfò@u>O}ùÚ¶_–€Í°O ¼7á©QKx:Ö~~{œÜÿרQ{Xi™Á#ÅarmÝÐÙb ¬x+cÔ“FŒpó¨k n—)3VœcyPz-+,óËS<ÊKÏoºç±Õ"ùÑ_L(/NE÷yÙ›ýn/Æ‘§è•òâ»öçÁr¶U5šLjSÙª‚¢Ÿx¿ì¹,)Ë<³ò°žq™W2Óyœx[5Dùð]?GâY†Ö¸–§Fý x˜ñ&Œ9“A›ø­ÿõVR9*¤ü8áéQA.ÓEÊ[àeˑ³¦â/{é±Ç ÓWåܰiö›ÊäU‘¼eÓ—ms…´<o¶Û=þ!YI­lüÊ\W¥ü`ùž½§q?lÉéÕÅ#XÔÜ &oMûgH‹ðLxJ¿ðnos:ªu#€€TD Z”Š2 ×g< ¿‚G¢…,ß^änãÚu<Àÿo°R|²º=°gøèq‡8‡uŸÏ'ëdÌæ·ÛýÄcøsÏ%ó|ÿKyŠË(›v­áñòT çZVxfs¹¯±uA¦'•ØzÑ‘ó¨ÐBòðË/Çæ\Ïo—Ïá?üÇq𦬠°Ͻf‹B¹™WâÁ¤Œ'¶³c$O§yM7æ[“j­ èzG9×½ô+sÏZ÷"’°Ï GmoÝ+ïȲÿÈjÊy><ã¹v†Û݇—¯}TW §H¦*½1nÌV Îáv3­—ŸÏò|!çAÂŽÑ©Ô}Å?¥©ÔmóÂP Ø‚ó6³;MVB{%㑜£#UþN£û‘©qœ,X¿9fnåÈ[™6ç·ùŸ³¥i<3z†—p~„•íI± êÛ%—cmF(§üÊ%æþ§¨Y 'ÍîX_Ù45ŠDÞPôÏɉ ¿òÁ¿…s¢¢Ê¨$€€@HÔh  ê(U1nb'ðN<ø??7È@ÍT)ÿÅG%¥ÂÐÒ6ú{ho7Üt‹DTòÝW³r¡Ùlö¬„“2F®å½3Nd_ ~«OI<(üîÎÑãæÉ4+Nc<ß‹ƒÜ7oÉù ÿà9õOs©TÒdÎþü»¼4U½Ïu{“óþU‰ ÷d¼™È#~w`l2­+¬“½¦)ê |ÞÎsü'ÆvΓq~` F/ñûPœkݯ̱*åT”_(úgEùGû³Ü-ùƒ¸ÍãL ­KŸ¾sc´3AýA@*O j,•GRù˜üÊw Ì›þ¸bã<ð?—-ïW”Z¦\qüEüVúìòâñÿŽs‘¼í.'Nàå·TO›g=gåGã_HͲó;û<Çãó{ÅBa•emöE<ê,0ÜÆ¥î¹àéU»¹NmË>š0zô.¶v¬QÉ{.jÏäU ¾°âh†2ës–b˜þ–Rèd©b¢ Ëòa'öy Ü› ˜kås¬#×Ëx5ãá\n—Mlº‹Ó_ÏKijNÇÊ·žW«Í_Ïxà0³}•â¦ÙÞM=|r)¢˜Ëë=ªMÊ“[6@dߊC<Ñn@`Ù¡œïñÒ¾G‚â LŽëôzô›ŽÜ=ú,X¿¨J9Gçp§ ý3 N+MÀ¸ÙŠÊmý¡uŽ#€€T†,•¡TNÙŒßÒK†þ"òíÍ“¾¶¢Ê²»Yž¬—ÙùÓøXc­Ë­&è^ý|]7NekÈãV¼²G›Ý1Þãqc‡ïïGü}üãªnÛn(žÞìƒÑšWz…^QІeyþ7Š}%¦¨º'}Îòõ·ò@ž­es«ÙuK­Ëø]žuƒY±ºˆ• ¿…cBÆc{GŒ?–;že‹‹‹÷ÑøˆŸ½?*N!E{ÓrgEékv ŽW£¨'ª?';´#¾êô¡¬Lèñã¿·¤µ'ÌbkDWçf~³þžu_޼Õ?ç:ž¢o¹ì‡˜×F]w÷Ö½ž·x ´¶¥vú;ñ+{Ϋ›}ÌùÇœÿÅŠÌï–ü•M_›ñ*Ûæ,ÿ-¬DÅš² b÷{‹{x ýIN¿?®IÌZ‘ñ‚;ýòÊu9¼ÚØ_yé迲YÉlÕ« õ™ÃªKy¥®‡TMûŠ×íMæëç¶+5…P”J¶ð½ÁŠÝ#¼ÜíaÅ®|E—ûÊÉ<%Ï¿Bý¢Rå‘Ï«*ýÓŸ'•"°tpZs½P¿X~j¸í »"ß}¨<X>*Ï*hLv\ŸÌo–;ò^Ÿ¾þÀÎ#‘x«÷$]ŸWX¨g{ÜžM†¡ÿ“‚/µ²ô/Ï{$¾ïLÞòó2²¼Ï…R¤{Ùõ–ý¸ ý$‰ÑÊvÆD¶¼ÄãõÛØI{;}ÄynVÉqAÙ¼jzmNÓ1”;X–£Ô^Röyö£¹W{ÝÆB¯[ÿ“+ü.OÇHW Õ?ßߦ9>á8_s¦yîÜw!çç ªjüÀ “ìõñí+Ž  ‘Ü:¯´ç³ xøYà@@   |T–•—Ø]žåÙÙ‰ý <¬xŒŸ8nô§Ö3A@ Òüyy‹Ôbn÷×KU^÷Ÿã@@ª@ÊG`YQ5Uf(zŠ£YÒ/¥Ì­8‚€@äpéôˆµ·¯*·°ßôìy‘S;Ô@@ . @ù¨í cŸ\VdH  ŽÀŠËÚ5qRñÝ–à¼Dâ3Ö9Ž   PUXj·ªÄ@¢ˆ€[)~€—½N2«¬(+ûÌÌžEÕGUA@BLÊGˆ";ˆ«÷HÔ å«>¼9ä³Á–ý¶žã  Ç"åãX„ð@¢”@qÑžneÈF ”Íé±gaq  ü  PMP>ª É@@ ’ ¬¹ªuSÃ0ž´êÈ,žW>ûÌk]ã  Õ!å£:Ô@"œ@Ç=Ö £II5×­ÛL‰ð*£z  u@ÊG@F  Ð,¹¼E/àá~™õá~“–¸ý×8j€òQMpH ‘J@ו×x…+ͬŸB³ûÏÌþ&RëŠz€Ô-(uË¥€@Xȼ,m0o(x¶©¹ªíá°Â€4( v“Á»Ç<›îÕ½1íö†b´â?’¾uèþš kå)†’Å…íšfûöí±O.©YŽÑ}*ºÛ_jïÿNmÓTí»hûNíÜ&nO‘û%aE}½÷ô¬u%W8€€€@ 4(å##cJì.÷®ûTUyÄ£{ZÊzóIñ±zãFñJ¬ÝuVœb·[Ï=\hä«Ìc܈1ãw{<ÞW'¾þÊ+sï°†5î(‘šúT¤¶lõêUæ;5^¾S^¯þJG›×32n•ïTD‡ì"Ïãü2£T’_÷R\üÓ]aT@@ Î 4åcøè±f{³Þåñtëî[ý{t¤;µVâcc|ó’ë]Xh*\…ÅNúsã.Z¼jsÚÊ;Ÿ/J,zpØc£ï|ïÅqß±”: „i.ô© Pp+Øwêþíyèö'2nÿ÷sòŠÈïÓƒÒzxtã åÕï³Í‡Ð%@@@ ”„µ€‰†òMËæÉiz1ÝwÝùÊ)=;+¡dÑ`óÂC¸Ÿ´fÉ©vǦ۞øû#\)Q0D;×e OÕ%í†WV°ï”ªÙfÞ9êiñˆ¸ï“‘1ÀÆŠÇû¼´®ù£ª²0=ýn~Ùƒ  ¡%îDß ‘^î×í8õo· Ô:·K -ËMø<>l Ö¯[EÓì/ ûÛhQ@ìü »ÆD}@ŸŠú.P5G¾SÇñï¥òÏ;FþCˆú>-Y¼úIV<Ò}d”b»¦Ý¢ddˆÕ@@BJ œ•…§9\Äy mÜqõÙŠÃÍ3¬*ßîÂIx 7»=æ™îûë%œZiïhV@Ч*ß3€@àwJQÔnytäÅü8"¾OK¶îÍucU—]éFÁÉÜ¢#€€@¨ „­ò1|xFœÝnÿw›ÔÆtó 3¢yÀ\í6n­š7¦F¿Ý³ç)Éœ‘¥€T;φœ}ª!·^xÈ.ß©Ö-š±±ñ“Ï?p"K¶¿¡•!–9<Ý®“›§[™¿ ìdN¿¤÷»çÕʤE¨pýézS”û½ºÞêº OÑ`ñ¨NÓ ·.>UÕ JëuîÙ÷r.2Ÿ;Z}@Чª×*€€|§®¿èM7Œ–­{w¹¿ÁŸ²vþw2ˇ¬nU¨¨Ž[1Ý* Áq   rá¨|ˆ•C³Ùµ‡zvjcÀÇ£fm.ü„cBRÒ]œS—?ÑdMBŸâG ë;wçØ`¿OK/oÙÏ0Œ'üTz"}úÎþkœ€€€@-GåC½éÇû뺑ÆËéFÓ¹š×—e Çæç_uÝ)|'­èSµÖ»¢3cùNñÀ=õÊ[FœÌœ5qùÐÔ]×?(‘]¬óÓ§g¿­‰Zƒ€Ô%pS>Ì7ÔöÇE² ïãQ—,"¶,á(¢1úT­÷®è,@~£Miɵ·,aÿ}ZrUËnlñxÓßbŠ2%}úÞý×8Z&n,E›JJR¬Ý6¾ðÔU–Ÿ,5¸yêÕÄu}Êíñ0«ŒËérÓÚ­»«œn÷¾\šõËòj•YåÂÂ(|§T2¿S âû´cp›8Ý­Oã¾/yóÕŠ¢‰Ó<€€Ô€†KeÜ"Ã;íÖºâ1êÍÿÒÜ<™ïLã©çñm膋N!›­æÊÍ(6Ffc„Oàñ¨pµJÖŠWr¯ê#Õð©VE’HÝä£ÕEŸªHºx–µï }>'“ÖoË&MS¨}Z3ºýʳ(9ÉkS„U›³èß_ýD¯>úd·ÙhoN‰"ÃûZT˜vçÞšñÓ2ºè/=ɦÕüûSaaaöò¦rËb…ý÷)»È5‘¿é=!ûwÚeÈI3² à )Ĉpá¤|jùC®ñH0Öz8­wg:£OÚ²kÚQçv©tê‰Çר\×K>ŸKO Hi1²¯_8S©“6—’|„w¤‡:íSõ sÖÏËÉåöГ· ¤‚"'mݽ¿ÒЇÈÝ÷„öÔó¯>ÅC®gý¼Œš§4:¦ò!q%ð›tßITýo~§Âþû”9(õC7†ZM£tÿI3³WY×8‚€€@]'åÃzK-KXÖÉ(¦QB,ulÝÜü,_¿2Woö+ Wn¦o~ùƒróЍC«ft㥡æMQQ±‹Æ½3ݼîÞÑ·׿¿Z@ɉ±tùÙ}éõOæP±ÓM¯|üÏSiü}ךo§}¿ˆ–­ÛfZVÎíß.<Õ|IÏþÚ¤¦°&Ÿ~^¶žî½î<:¾¸f„>”X>¬’Ÿ7—é–:ëS¡oµÊç¸%k?Ö«“_YèÒ>ÍL<é‹yÔ´Q"]s~sjÔÓ“¿¦Ô”dºëÚsÌço|òuä>׺E Mû~!=}÷ÕôÝoÒâÕ[(Æa§ß–oäõN¥;·¡ßVl¤oÿ·‚åQ»Ô¦4¢$~“N¿ý¹‘¾a(.ÖAg§Ÿ@gõíZyáhÌ’ß*ùN…í÷‰÷ó8ËëõþÓBÌmõNß™{Þµ®qº$no¾­?àuÉ€råÓŽìjÝ<Å,w3[B¦|½€•€TzÙiTÈ ‡(¼÷ˆ9x;Àñ]n¯_ÆÃù…”WPL+ý{t0ï_uN: »ü ó|ÁÒ ôçÆ]4lÐY4è¬>ôÅ™´ïàaóÙaN7cÁ2Zµy']vfoJkZëÖis‹³#=XuôzÒI]Ú²Ò°’¾ÿ}¥©ðZEcù†íæåÖ¬¦UäÏ;L+‰(Ê2ÝJ—ÛMÒ·¹—S¯Îm)!.†útmoö㭛Ѧ{éý¿P;žÎ5lЙԓ•‰#áôëëM§]ZSú”•É;J‚ÕÇÂîû´üêÖmx?ϸÌMlZ˜Ü! ~QÒ1QMGádù°øÔÙp±4,^µÅpõ`Ÿ™³.a៛(1>†nxºy-¬}ü=¾ö°‚RþüwMS©-[1$ˆÏGZ3Ÿ"±’z½x –Ú4ÉüÈý¥k·qy'šq‹wÏ¥æ[fóFíþ¶¥Z¬võ©Z¬Ã1³|ÁÉÏ}õ«yKhÞâ5¦µ¢7+$'thE³kE!­Ù²‹ºóõ¦ûhýö=~›WW¶’ˆBb±Æ‰ÿFJrOGôYPf,øÃ´jÜvÅY¦¯”×:Šå¯ç}|릅dûžHùQ¬ïTXUuÃýbr·ä}Áš¡Ï”ª({ld¿¦óëa%(„ˆ*á¨|ÔY´g᤮í苹™$S¨dà&áàáóM°%H[ž^"áÀ!¾Dùð²E¤¼àÕuäe›o3WoõGÛsÀgùÚ¶¨#Å£n¦³ù+‰“:% SŸžy¥Ÿp}òÝï$Ó­ž~%[ðš›ä›v죵[vSÿžÅá˜ÖmÉbÇtÕœVX™ÅrçóT«” Š‡T´}Kß÷$1Á÷=r²%¡þänÎ{‡µËþ"kGnÅPŸ4kç®ú“%ƒ€€@‰)>L@ÔùÛévi)æ¼ôb^btÆ‚¥Ô¯{{ö݈§)Iìÿ±ÍeOÎ!ó\|DTžZ%A¦¨Xa×ÞƒÔ”ß 2KÞ à9ðW›, 9ì2e¼ÞBs¯ÃšFrÝÊÅØ²ycëÄã¯M3Î[°Ó¸(¸[ví%™R8tàiÜçl¦u$ŽWdkEeBb\,­ÉÞ]nTÉÁœÒXï2¦Žæ©p7Y‚ŠúH¿™»¶®qú"¦>uû†þ_p ;ï® 1o}ÁÎè莫ø÷í¸è´^RBˆŽ®ÑÎ  ±dI]»ðk6{õJ²ÅÃ¥êUý¾Ú±)b+Š€4Xðùh°MÁA¢€‘‘¡Úœ÷s8KX°¡Êॆ¥ÏÂÊVÑÞ7PWP>µe €ƒÀ’Ì·_áéD{¬ÿŒ=ÿ ¸Æ)€€„(aÕ@*G`ÉÀ´Çx)ïü±UåÕ~³öþÓCP>°Q €TD€÷òfúóV…”iý¦g?b]ã  áJÊG¸¶ ä –\ÞòjÝÐßñïå¡(?%wLº™Íù€€„7(áÝ>@ü_ÞâC×ÿCiæM…–)q Wt~}£Ó '   ưÏG7D‹ÀÒ-þ¢{•/ÙÁÜ·ë©Bë´8õ¢¾Ÿm>dÅÁ@@”po!È õ–\Þ¢+³XñH<Åj{Œ¦]pâgYû¢€€4(˜vÕ š ‚DÌA­NÐuå{V<šHÝYñØ«‘ýü¿ÎÚm,P_†OÊGÃoCÔ@ B ,ئ3éž¹¼u`ªTQQ(W%Û…}fîÜ¡UFµ@@"œ”o`T@ aXqYjGášËKXµ4k Pÿ`_Úwæ®å ³F@@ˆ | €€@˜XzuËö.¢¹,V÷ñ(P í²¾3÷þf¢B¨(UÂ…È  P»’‡^§Î£½Y’¢‘¦ JŸµûçÚ-¹ƒ€€@íÀjWµÏ%€€@¥ˆâqÙÆÏO"2â| ”bR+úMß3¯R €€„9(aÞ@@ :ðªVí 6ü÷¤xw©xðªV.UQ®î;}ÏÑAµˆ˜v ­Œ:‚„5åƒR;îý)ÞïW<¢kûÎÈžÖ‚C8¨"X>Ê3 žiÍ+ž[÷ªzä7˜þ$rx퀓ˆ&ê~؇Чv×ɼ²íñ.‹§Um¥&†¢ê|¸ªßÌìovÍ =€€M ê•kP¨ë:éºAÛw 5[²(çp*($§Ëã£ÁUúN‰Þã°QrB<¥$'P÷Ž­©}˦¤ªª©ˆ$+/"†5«_mËÚO«6C”›_H.—ûHwªn¿BŸ 붯Šp\Õº‹Çíä}<¨µ¤óªšw^» W=ùæûP<ªqA@ ¨U>dp( ‡×«“Óé¦ùK×Ò‚ek)¯ÀÅ A6µˆTÛ!^âÒ’Æ4(†÷ K&G³~YAI :'½; HïF116¿"’ÂI½~% ÆÜÅki^æ*:\କþ$DŸª·fIÁK¯Hëîq¹ç°jîãÁ/! 絿hÖÆÇ熤d  aH *•Kép»Ýæ[éÏæ,âA¢‹ââ6Ró¦Ëù¸Ž•âZi.]¥¢¢®”_Ø›¦/pÑOK×ÐõžJ=;µ%MSM%¤V F¦µN@úÕÊ;éãÙ¿²•ÃY'ýI*…>UëMòع¼¯îõ~ÇŠG3ÉÜÜÇCQ.cÅã!/ ‚€€@ˆ*åòvx<^~;í¢yKÖÒìÿ­$‡}¥µ˜I±±Ûj½iD©IHXn~Š‹ÛSNî šô¥“Ù›Î;¹Ùl¬ µÞ ¡-ÀêWs­¢¯æ-åþ´—ûÓŒ:éORô©Ð¶gmçÆŠÇlÉŠG²Yï\NŠvißY¿ ï]Û¥#¨_Q£|XD·ÛÃ~.ž³š¾û} %ᢦM¿ Uq×yKˆ²“–:‘¸š­ >×’óúw'»Ó°ê¼1ªY Õ¯æ,\E_²âQŸýIª€>U͆¬£d¬x\H†÷KV<â}E*9šª^ÔwzVf‰€b@@@ ^ DÍR»2%Fb§“–¯ÛFß/ô)Í›}Z/ЇÕê¢ôˆ 2hùórZ±a»)§È‹þ¤þܰƒ¾šïS<ê»? 1ô©ðì7K¶¸Š¬°–(”í :»ïôÝP<Â³É €€@-ˆ åCˆ2ÕJ,‡çÓôŸÿ0§Z‰Å#\‚Èbçé:ŸÏYL…Ŧ¼P@Â¥u‚Ë!íS\ì¦ÿ|÷›9Õ*œú“HŒ>¼ÝêãîÒA-‡òë„ÏXñ`}ÃôñئjŽ3{ÏÚ³²>äA™   P_"^ùi1²¢•øxÓ/Ë6P~‘‡š4žY¯² .o«SÏ Ã….šÏ¾("¯È-ò#„«_ÍÍ\M‡Ø¹¼ ·]}LÝ«ˆ úTEtêîYæÀèº÷}^žL+)u}ŒM;3}úÎu'Jð ñʇÏêáóó(à};2×o7W!ª çòª6±È$+nýïÏ ¦•Æãñ˜ËW5į}V¿ZÀK4K›…c èSµßÊ+TeñÀ/ðûƒò+swÞct¹Ã¡œyâ×Y;ÊK‡û   É"Zù°ÞN»xIÝâb'mÙµŠœ^JŒ_¶m*²°ef+Ë*rÃú~Meõ+éOyl© çþ$ôЧê¾lK¥¾ÇÖŽÇJ_@q‰g÷þrÏÞ€{8¨"ʇ—¸Ý攫MœNðGÕÂÿÅ£Èx˜W¼¥Iä—z „«_*(âþt8|«@ô© à„èÑÄ¿4m¯_²eì+K…Ô§ûÍÊ~Šº²ná  QM b•™cMÇmY=ÊíÖyE"WØ7¸Èèvyy¹]i¹±êÂS7Â^öHÐj ŸEÍÃýÉÙ ªŒ>U»ÍôÏôÆ]û6Õ>äþÑJJâ¯*/U§ÞÇŠÇÛµ[2r†E *¦]É@Qöù`m¤Á´Ž¥8aÚUø5™5íJÚ¨!ô©Úi­q'5î{VšãKžjåSd|(+F鬀4¤¡"ÛmJVºÉgÅå£þ¿lV[˜zGCS>ЧBÞz^Ÿ|në˜Iü-57äŠrE߻燼0d  @ b•iÿ@‘]ò¶Z®L`QE±êÐ`äŽA­6i@½É×*èS!íí7üD³nÇXÍù|ÜãÑm—œ6;kYH Bf   A"zÚ•´“ Ø‘;@v©BxhPŠl :ô©õ!ÝKÛßKÖ|/ßSSñð´~åaϹ¬x¬Q)È@@"’@D[>êºÅ4ÕaZ+tÝ]×E£<: à-̧Í/ýòþ\è/­ÐC _[W4ì¿[óvúoâ@@@ (ˆµ|„âÍt\lzäúÕ”ÖôÄx Ý÷ zbèvjÓ¢¿h‹”îtãEŸÓ£ÿ·ž²œ.?óu²Ûâ(!®9ýõ†uÔ¬qÜêž„¢>Õ-鎨i;ôèp¸r¯†d}õ‚÷))1!¾ ½øKºÿÚ%~Чü(êüĵ7­}[)ÅccR‡œk>x7+ cÍå:§†A@@ 4kTún„\É@ñȧê•:­çƒ´cïbÊ>ð'Å:’éºó¦R·ö—•Q³FiîzzgÆy4mî-Թͅ$ƒÌ‚¢}´|ã'4 ÏG¥©Ì#²Ë„„p!`µKUåQU;Õç1úߟ¯q¿Ô+ìS­›õ¥Û.ý†Øf¥ŠAŸ*…£Î. 7­¦µOÞBÅ;6ùËÜrÂù4»å¹;÷º=þ›8¨¦]•ƒ'ÆÑˆÒ»¥OæÜhÆk§;Ÿþ;ïº}Ð¥R­Þú5ÉÇ6QÖ?¨Mê)ôdži᪠tï5 ©yã®´/w]©t]ì=¸†ÞñÅ%ÅÆÙì²SuE)Žý¬[wê6~¢òý±cFbŒñäâj=÷AÍê&›Í{xV]qÑ¡¼ÛÈn­qT‰l{t¸‚lj ­Üü¥»¢>Õ¼É ´h5/¢Ä.'w^*÷êö©R™à¢Òrϧ­ÿEºÓ·A¹bsPûûž¢ù›9‹µò€€€@e Ôp8[Ùb^¼¦ÉÇó`ßN{®2…?”¿ƒ¾Zp7¹½¾Hy5 IZ“”µ/ÓŒ’W¸›Šœ¹Ô¢I÷ò’à~”hÞ¤íÍ]ÃVÞs†CE}ÊT\YùPգߠOÕ]‡Ù;ócÚüâ£~ÅCKJ¦ÎO½M)§_\wB $ˆ Gl"¨r5©JJR*,>@NW^•²¹ä//R^a6[=>ñ§;xx 5itœÿ'ÑI %©#åp_E@Ÿ Åòó0¼ÚñÎs´ŽÏJ%1cÒÚR§‘¯SL˶å'Ä @ù(O£„Öt¸ «œ§ÁoŸ“>’Z7ëCS¿½‚÷9²âÕáÂ,’üªZð[ò!ç~OMSšP£¤DŠ‹‹%MÓª’E©¸/ð--_½a͇ÿzáA~ÍŸü'Yž@D¾Wñ|aA€ñ¤5jtëc£¿îÞ©}¿G†Öìµì8_TTL‡óòéßÓ¥y•_YµQB+Ú¹wQHW§O…¤à(ÈÄsø ¯hõå¯Yæ¯mb×Þtüã¯X>@@@ªOÓ®Êa'Ö‹¤ø´rž}û”îwQ¯ã¯§O¼é(¥Eò‘ü¢›€ôÄ*ô©Šh¡OUD§úÏŠ¶m µOÜ\JñhzöeÔ9cêcEJð€òáGQú$çðfs©\‡=±ôƒ W½;]Oç¤?I¿®x…$~ëæéæÇŠÚ„§päü¢›€ô©¦:†úTH0–Ê$wÑ<^J÷Vrí+±x* µú ;—?MâdŽ   Ps˜vUÇ6šS§ÒRzÒö=¿—Ëw»o×[øD¡óûÿ£T¼ç>hÇÖ“–›ÂŽë«K=ÃEôØË‹t?nïñ¡ùΫC}ª:Ô*N³û‹Óîÿ¼å¤ò2sz–’ûžî¿‡š€òQÃb×!Z¼ö]úKÏ{K)¹yÛx¹Öv¥RM™uI©ëÀ I¿~û·´ïàÚÀÛ8B«¶|E§øOÏlîÿb!Ö§¬g¿­|“äЧiÔìÜpÓÖ7ÿAÿwdê˜Ô6tü“¯Rlë5Ë©A@@Ž"åã($GnÈfp·\2ƒ—Éå%Ryߪ†øØ¦Ô±õÙô霡UMŠøH@×=4wÉX:µÇ=¬||Ê5¬úæ‘ ­Omß}€VmÞE9‡ò)7¿œÎÐíÇc£Æ‰ñ”’œH=:¶¦v-›V©×¸ì¥M/Õ ¡Oy<^𛹆æþ¾’r ŠyB¢Añ^'Åñ÷Éá•­Cr4mv$Q¡C_Ï_JbéÜS{Ò¹ýº‘ÍVñÊpëWÐ&޿Ó+‹¾ùBó‹‡PÛ[åÝ<+NkÅÇ@@@ ê"ZùPØaôÈÇ·C¸î ÇQÈ!»¦È®T½e‘¢ÖX}ÊÎ\Ýÿþ$ t½núÔj¶r|8ãgÊÉ/¦¶yÛ©OÎ:j—·…b<ÎZk§-†¶ó¢SºÒ?Ó¼…+iè 3©;[C‚…}ßN;§¼D†lUÏAÑlÔæö¿Qó ® ÷@@@BH b• – 1< ?\þÓ)¼ÞFë(ýö5X}ÊÖ×µO °b¤{ÿ? ]¯ý>õã¢ÕôùœE”¿AÛçSË*î“SÝÖŦ3ûTÉg7ï¥òk»sèõO~ kÏïOçÜß­ávÒöÉÏÑyÓý÷ló4«(±{ºÿN@@@j@Ä*ÁÅÙ5òñ<‹%U-¥Þï‰l^=žQÕ4õν:4Š1Û*œû“Ô+XŸ:px-Í|›zuÂ~IªSýRiDñøì‡EÔ1w# Ø>‡l›l–ŠXË¢ð\¹nÍow>ËÃ^5ìVsþ)=ȵ?ÛÜ8°pÓ‘Uçâ;ž@{‰ÍZÖ²TÈ@@@À"ñû|XSd䘒`çÁˆÂ;TwµêvG‘MdLk’àŸvvBB ³mÚ§5 ûþ$MU^ŸÚµo)M›{3½;óB’•¸ C¯VˮڴӴxˆâqþÖÙõ¦xX‹â#rˆ<ÿýq1-úñGZóø¨x40ºŽ{Ї G¨#­|øvyUU•ÅÙÙ—B§üÂÞu„·êňlbôhÚ(Ž¥>â³Rõœ¢¶XýªEãDŠs¨aÝŸ„A°>Õ,¹¸r <ýö[ÑiÆ/ðâgÐ’uï³¥Äç Q~n·‡>œù‹9ÕJ,áDž&EhÚ‚ÕT|8ß\[Lü;ÚÞñµ¿÷¤ØcÂI\È  QA b•k€(®ûz¨šf[ÄÛùMp'*.nv ,2‰líSüòŠüV]ÂNà(Èj «_ЦYØö'ižŠú”¦Ù©gÇkèŽAsèÚsÞå 1Óè‡EchÙ†+ݲs¯¡ƒì\~ûxÔ×T«ò„yNß1ŸòyE¬?›H¶ÆÍ¨ËÓ“©ùEƒËK‚û   µL b•á&ÖÍT:4séM›ÍF­’Øú¡êtàà ~ãk¯e¼•Ï^dÉa™šA[42åµd—z „À~Õ½}sе+fÛ…SZUéSÚœO7]üÝ>èê}üu•‚­ë:Í]´Ò\Õª®œË+%X@$‘KVÝZÓªuÿÅuêð§   uM bGµÖjÀ‹Òáp8øcçZ'(äv7§û¯®kÞå–wàÀÕär· ÎÍâ(6Vät˜r‹üV]ÊMŒuFÀj «_ÅÅÆPŸãš™m'mN¡:}ªyã®ÜïbKUc÷þ?èÇÌÐCý÷ öäÞºk?*pR'^N7œƒÈW ÆÒŽSÆSi’7ì˜vUíþ]+ ­iWýªK«Æt\‡©€ÔW’ÊÖFŸ:¾õ9tï5‹èŒ^ÓŽ½ éýÙWÐGß]O[²6P<ï±Q• Sμ”z¼ö•oÇO–×Þ¤u?…zMþ¾T[Ù›¶ ®Ï¼O'M]@'½¿À¿`³ó¯¢n/}R%…Dä‹óÓÜ<¶P¹aý(E   P·"z3 ™"#ƒw»ÝnêâÉYì$§ÓInÞݸ¹·€ìä¤ýîf”½÷vŠ‹ÝH‰ Ë9î:ð×Î> ²ç‚,}š_Ð›ŠŠ;‘MñRÛx/[ù\ÊúèuÊ_û‡ùizö@jò—óiÏô©´ï›O(íêÛ«¤|8¼.rº<är¹ÙòéûîâX 9.@@@ ND´ò!e€!çâï!yóéõZЇµ"V1ÙxP’âuR‘‡7eÓ5ò°/†‡ÒeÿšyÁªòœsgdce&ÑnP¯$Óvd˜(EÉÉIÔ¨Da9E^‘¤šÒ¯ôÇêW¢4¶(*¦¼b7åó¶.•O^PÀru®©Ïs}ö)]7ÄpÁÊqñ×¥Ò!¶}g*ܶµ&¯™fÿ_˜Ç´«n-•‡–؈TžéÜ»Ëß¹g9šûv"/ܼ†Z}ÐܧÃp;ýqŽu"~*¢|xÌï¿ÕÇJ…ç   ¡$ñʇÿ-5æuñ‰âa ü|Hž–ÅÓAŠÅ¬˜xÈÁ©Iæà*´«âˆRž5hµÛm¦ï‰Lµ‹‡(““ÍsQHdµ+X=BÙÕC›Weû•ÃQÌS“<~Gg‡jµ¥úèS"»||ߣª àc[¶'çîíÇl-.ÑŒ£ùãêÎ"Ò“Ìëâl΃¿O1im¨xÇ&œcü½‡sKv©‡0D¨;¯|JKɈá©OF‚oÀ$cñ§©M²V¿¥6}Ax èå7³2H”7¼*,nܱ׌өm óXö?kp#+$‰š*>(l‰á2}Vöñà©Vbñ0§]ù­p4/Ë2Ü®k³_UT×úîSòݰðÉYö™£i¬]VööQ×Þ‚Ãæ=56ÎÿL‰#o~žyíÎÙ'_Jr4M­šòQ¢4‰ìRº'ʇ`•¢ ú%XoŒÅ¡Û\‚—ý+ŠYù(Gô’·Ô²@eÞRï[¹Õ̳ÿ‰Éæ1ØVybÉ©T>«‡oõ-™b%Ó®âããÌL·’ç"/Bø¨­~u¬š×WŸ²,<Ž7€cÉøÜ•³—l)Á•ôÀxÞÂ|òäQL˶þÛbå8´xžy-+d‰åÃ}•*¶Ù°Ò$–'ß X>ªQA@@ D¢Fù°k2°7Ïyð"ç¢|ÄÇřЇéŒîrñÔ ~3ZòvÔ²z˜ƒ­ Ð’|SAš7kzÔSkF‡U¶Ê–YeG¦TI¹²ü¯¹ô/+?¢tø6ôY<$ Bø°Ú6Ôýª¼š[ÝÂ*·>ú”_)OÈrî;³¶Ql›å<-}ûào?P³s¯¤ÜE?QrßÓyå«&tpá\3RLZ;óX¼{GéDǺ¥Cþñ—Ùú^+ žƒ€€„–@Ô(‚MlÖÒµòÆÚZ†W±x˜Î¨%sÂÍ7¤•¨$5òYUÁ{ѶuÔäô Ù ©ùÎË«ç®ß ãyÞžoN'ñýØùÞKì/âS6:v#QdŒ Kô–—ŸÜ÷k xTÄÏ@@@ ¶ D•òaÁ”¾ à̪yà/~¦#jÉ\vß|vÙŠÀ7/¼¢VO™’Ð4¥‰•}©£”#ÁW¦ÏÏDe%ÄÊâ³eï—w}ê‰ÌGò¶Ê#PÕ~U^>¸    ™”¶Þ}ºÔ0$è €Í$¢£QK¨wP>ê½ €€€€D(ÑÑΨ%€€€€Ô;(5l‚Ÿ—­'ù €›@Ê™—R×x’ÕåìMšQ×ñS¨×äïK%nyýÝÔ÷³%þOŸÿün>ovþUÔí¥OdÙ¸Rñq   Ð0Àἆí´xåf3‡3áx^C’Hé›ZÝp7íž6Qvð¤„.½¨#ïb®Ù©Ü–Ô˜ Ö¯ ]S_5±XKã˜7Ãܨ0åÌK(gAõ—ÜtÖ¨€€„+(áÚ2 "Œ@Ê‘j¡ f›5‹k߉öÎøÐJ‘Á€€4 ˜vÕ0Ú R‚@ƒ'Û¾3nÛÀV¯Y—ý?|A{f|@Šíèw ¶¤dŠ?® µ¿ïijvÁÕ¤%6ò׿póŠmÕžVd@@@( «½ -„ ›M%—樴<±-Û“s÷öJÅÏ_™IE;6’êpPëoL'Åk¦-Îæ<Øç#&­M¥ò’H"§MÃÏ]¥!"€€Ô£_9ÖRAÈ@ 2Èní:”ËS \öÄJWÊÑ4 Ö.«TüÝÿ}Çϑچ•¯©É)çRÎÏß;g‘a£i*ïØäWÑI!Ë™æÐXgQüŸŠâ €@í€òQ†«8¶Zέò(ð¼LTóR7tóèõú¦’‹#+XƒëÇè PÕ~u,*õÕ§¤Üïwí¦73ÿ$5÷o‹%§-†b<Îc‰L®œ½dKiqÌxe#¸ì!½¨€bZú,²B–X>ÜY ©DùŠXÎG韻@†•ÈQ@@@B@ ô_ãdØÐ²°…:¯¾£ëmß}€ÖlÉ¢œÃt¨ œ.k å×êpA‘ùðå¾ ©DïˆáOrB<¥$'P÷Ž­©}˦¼Ú¨Š·°Á©5ø»V¿Ú–µŸVmÞE9‡ (7¿\.÷‘îTA¿ª@=õ©_³÷Ñ+®¡5{öSîÏ—õ8ž–ÿ±•¶'u Î×V(²+§ÕéO•¨i]ö©g–­¢ÿlÜJ­ããèý{Ñ“é@ÎAZ¿j;mLéZ)å£hÛ:jrú…¼Ç‡æw:/¯šÉ'àeyïe?å­Ê¤moüò×,5£'tìf*2F%zƒå'òÅp‘)‰±PöƒÂ=¨CQ©|XJ‡Ûí6ßJO›“Iy¬tä&8hoZ#:˜h'OÉ&h¡n +‘®lߊ<¬¬çý{çE™þñwfvSIHhÒm€ Šž¨g9 g/¨çx÷÷l§$ ¢gá(It5›€ä<½SO΂(ØPQÁŠ&HÐ{úNùÿžÙ¼›MØÝl’Ýd³yÞ|&3;ó–çýÎóμÏÛ¦´ 6•"ŽêÕYüà1Å–Ôž¢GYIÈ·û³wíott9g˜Øù>4Xã¶¾ñœ Íß­~`¤=´JÅ$s³ÊÛ»H×G‚èvÙµbó¿Ÿô÷ô˜äÚ˜ÖWôë”â5ðIj4(ð« Èø`L€ 0¨hW5]j•¦¹.*++Åü¯—‹™s‰í˜8»´w†XÞ«£Ø‘ž5Ãî$5”¥EinƒTüƧâƒ/—Úr‘|$'»¶C@êÕ_-Ó^ýXlÓ=-¦OD©%têÒ¾½Äð#ú 'z-¼•waËG÷é,’ÑC¸¨Ï9Ãò¦Ñ7:6½0Yt>÷ Û°é™.¢øt*ó´óDŦub÷¢ ÃÉS#yH®DÕ‡vNE‡‹fËL™l€HJ¼gL€ 0&вÚñADj™¶  ‘"ÃcÞ¢ebz;~è)ö§„®8Eã¶Pš?ôÉ;!ÜO´e"ùHN6@¢A<òqJ½šÿÕrñÆGßÙ÷²µô‰r×ÚW]-ûñ'qÊïŠowî ‹zç4»"¯‰Ìý8:¼'©³XÐ÷¼ aä…}ß|"VåÜdò\cö´ÚÕ÷ma!yvC®Ã2l92>¼÷0†…=1&À˜ˆ8vc|ø?®Ú Þûj¥ØÕ!Q¬ì™.ÌV¤@i“ $Ë[Ÿ-KVÿjH$/»Ø'@÷iéêbö‚ïcBŸˆXcuª½m3V®Í[ žÿy¸°wqLFF@ø²Ç€*ñ|0s2zvî ë”(Öeôó»¸Á€Gð$õx$OTKtIK„œN[V’™d—ùˆ`²`L€ 0&v1çƒ*ˆºn`åªj±o©x•ü ¬ùÿ3æwÄŠ#YNظGüïÃoÅ‘½ºaBz²]AâÚX¹CËAzUYé/½÷•(ÇŒæXÒ'’¶!¢Á}³ÖnÓW¬;+«ÄÐ^‡ˆ;=Z™úÛªªàƒ}ÔëáÄb "Èá];Àhöˆu¢ŸØ›”!ÎØøIƒs@&Úü34Çc!†ZQOL×D]t‡QŸ˜ˆ 2’¬$3ÉNy`ǘ`L€ ´<¸7>¼ãñM¬@T-***ÅÂïW‹²Šj±ó-Z³Ç£þ­&YÖ —¼i¯XðíJqÁ©ƒ|+`Q+-»Ø" õê£Å+ìÉ屦OD«!Z°y›p·L éÒY)9I$cë“™,4ë€(™â­þ}ü*úí^%úXÖw@ÂJ<€'úŽ-§K«ZÑär§0ðAÁJÑ9%òye#IV’™z>ب’O1&À˜hqo|x{=t»×£ ßíøæçMö*D­1Ç£¡ûI2Ñ I‹–®gŸØßÖB-´TYb[¤^ÑòÌtÏbQŸˆX(:»g7ñúgŠþ×HÆ0é¤Óé´ŽT|¿¦ ='U˜KÕ“í“”r±£¼ZlIëe ¾²‘ì©)ú‘`x"v#«5§(w¤‰ g’ýŒŽ µJd8 ‘”$eKF/bŠ É@"™å°«ˆ Â1&À˜`aˆkãC¶NWc8H%*Gë7ï•X&t{fã*[aÓŒ€ÇíiI"cë~ñ díXO_+-÷~Dn„¢zEúDß…¡å™cÙIZôó¯â|P¶ü«0"kxP>½Æ‡*P‘OJJDå>†Gµ rFÃÉ0S” ‘Q]%*ðÎrC:†9–:3m#!R¬È¨Ñ°¥+U"Y1ð-ÓÎõn¤à{$$WZZ‘Ú!Õ>&YIfZÒšËS¤îÇØ`L qÚñAKëzì!WkQY¤¾Ñw¨"D•úât%†„ìÇßè›Ñú€`$î‰W>Mì9PfË”è­Ðq…)t#‡Ô«]ûJ…ŽJo,ëå˜äó`;.%5b:E•~êA Õ®LÒ¸ ô¼† †e9œ(w•öêmºNKH{—»–ùmŽ#(-{<Œ §ÓaÏñHÅb ÔãA†GFÇŽö1$$+÷z4‡8‡eL€ 0&Ð|qn|XB¯1>hXH¶*g+®«æýªr*by¥]Q$ù©ÒÆ.vÐý û²¯¬BTá{mÁUCïË0ô ñHé”42­aaI[r²‚†7Ñ*SÔûAsAèû5†é5¤¥>7Õ‘½dt >|H†¥çíõÀ µ¢{Ø•¯×ƒ'š·]e™`L ¾ Ä­ñAÚ¨E–Z]iµ+=!T;ŠqG2zª½r{[”½y‘•®?®Åó׫êjT¨Û€>Ñ ‰–NQåŸ*þädOMê¶—àÅ‹JÔëHÆ•CÃ0í¡YM5<ì„üÒ¢ž Jåíõð®¼EC¬hØÍû †[Ñu’•`L€ 0&кâÖø ¬rxŒ×1šüUåÖ¸EÒp"Ùå8úÖƒÓ<˜€Ô«æV Ž9ºg¢¡Sµ‡Ãk|ÀóöB$ˆ”ädÛð°'£Ãø·'£×è³dW3$ìŒK[O¦«bØ͉¢!UdðÐrºÔûA“ËÉè ót 6ÞÃÆÌ™`L€ D@ÜT¹ñnhõ¥ÖVTzÚÒà%HoËM•3™®†¯¢ˆÊµVÓo{y¢¦jé0dt4³’ÖÒ"·—ôjõªå8Ê:%{äpš÷aµªYøÁ;„ÐÛ#Iä¼å±ñ ¥îMO®t…ծлAFˆwå+šˆÎ½§Ë!˜`L€ D—@\„ÎWQls­Ôµ²GW8ö¦hjŹ)iE* ™Þ²ÿ—Ž€øô›Æä¾9°ß¡'Ý}ÝEÍÊ(}«¢½Yû”Š™s‰ŸÊÂבîÐïwîn0ýþè%»éè#0Äo±8÷>Cîh¸ÖaЩ_jŒ—¦èTƒ ³&À˜`L€ D™AoG³[2M”ßy0hWH§º&7tì|µ-ø¸d› ­©Žuª©ä8`L€ 0& âºçCÁØúÚÍûpÍÂàùw¾RèÔT?ÙþÎCŒg!®Å“:åth¢-èÝ Ö©¸VIÎ`L€ 06C nª Öw‰¨Ð'zj'òÖ¿+¿ñõ¤„ºßô”ŸX‘·=ÉáR1¬.ÑûÆ,ÝÖ©ö¤¥œW&À˜`±K n@È“šp`å ÚbÕIùRxD\¬Þ#)WzJ"tɈi}"YY§äã=`L€ 0&ÐÚâÞøCdhß)Õ)ª3K=­Í=hú¶l±{fªoØUPÏ|¡Õ>Ú=3æõ‰±NµššpÂL€ 0&À˜@=qm|ø ¬-¤ªªHOv ÓÝTÖÃ;?I6§CÓ“!u휕ؑ%‘zÕ-£ƒHDU,ëÝ-Ö)ÖY&À˜`L VÄ­ñ!+ˆ4õCÃ\UÃä`ìIÑDFYµH/½Þ’‰d;¬S²O^’_æ%V”¦=Ë!ï…Ô«½;Ŭ>Ñ}bjÏÚÊygL€ 0&{âÖø ÔÔÛ¡ÙFæz ËÃápˆžièý@®Ü^*ÔšúA²LNMGtK·å•²S>ØÅ½xhW‘ˆ¹D±¦OD‹u*vt†%aL€ 0&À¼â¶V+[¨©OFGBB6'6‡èj‰”j] Ø;_'Yl™º$Ф$’3Á–›ä—ya¥m}ò^H½JNJ'Ö)æô‰H±Nµ¾¾°L€ 0&À˜@]±f|к¥ØÚ7Û©ª‚9šmt$&bYTTèEfŠCtMÔEçÒ*qtÉþVí¡Öi’dé†aa©5rz%’ŸòAWØ8Û[£ŽÉ¨jò¢Ö׫>];ŠþÝRcBŸlùZ^§bòÆ·ˆPНµ—òÔ"X9&À˜ˆ_±´ž+½¼mgXFy¥Çþ€B³jÝrx õx$ÁèHJNÉØ**’D·0Íj!J…8~ãn±¶kšØŸ‚Õ°ZÐÑx|®C=ÝS,Ñ'3Ù–ä$yInjaİ«Êj¥ëzE€ìù¸¸ÖÖOùò)" ôj@Ï áñ`ÑžªVÓ'’­%uŠÒkÏŽÊ”aåõøt®Þyþɘ`L€ €@,¾bzŒí{÷•ÑK¼YÆ ‘¡Ê»Óé´+õ©©)¢ª²JTUU î]2á•bGu¢8nÓ^±75AlOK{:8…¥yôÍZú”V ¢Éåä{¦èñH©©É¢d$9ÉH"¹å°+œ&ìÝ_fyªªö41x›)"Áôª_÷t‘ îëöè-¦O$Oké¥Ýž]¦ª«w·gœw&À˜`%‹ÆU’KTT)å0R0¦¾©Î[ITE*ñIˆ§Cj* jQj]7ÐóA3Î+DrU¥ØãÁŠXå–mÉCÆG•S-k§Y–ýuuª(Ò@ FG†P² c#É–--­ƒHíj“¼$7­ÐEùhŽ#ŽÄ³¢¼l'â!£Nn͉¶-…˜NQ¦CéUOèUªã€Ø´· úT5}"9ZS§(ýöìd™ª®¨Ø í­<µç[ÏygL€ 0fˆ5ãÃ~‰oÛôëÂôÎï\ºf³øí±G4+‹4D†&œÓ|êQ ¡1*QcxÈ!4•ÂQíŒ Q¡Ã15¡›ŠÐ«0 Ûj^ÅŸ„÷Ú–ÝÍäPLÑÁi‰d§b«¢2Š:vLééØÈœ$/ɉ!WÄÑBF6®^õÄ!‹KV–hï.â:EÀÒ+2»UTŠ•º oZVƒºAúTC¶h³\këT³„ƒÀ²LmZ·ædÇÖ±š}䎳À˜`L zbÉøð½À¼ýúÒ~ƒOØõÍòu`|4«öïk¥FeÞD YñóV 1,ËÁWèýðxt‘ ë"Í´lãöˆ‘'9(=Yiu:˜×‘C#YP;ÚÇdÐjW‘èõ  €£eÆþ¥ß|±?šÍÇ›üÄ©óå1’:E¬ÂÕ«„„J‘½Â|[§¨·-RzÕš:§úv¶¨LY¦¹wñ§þ„@T¦|ºv$ì‘ 0&À˜@;$KÆá§Vy»r¼sKÉËËåöÕ¿nýûvoÖ­‘FF¢• ,,³KŽZŽi> m¢°*ÐJmÏAEÑ0½=#&ŒrÍ©,R‘­Drh*ÍAAO Òôöz`ކZQ‡=ìÊ×뙉æÄoÙšMʯkV¿1ЯcoÒ!ÞñS­µôªµu*Þ¦¡üÉ2U²aý›ðKeª=•§†ððu&À˜`! Ä’ñ![éE®úæë/_•}Û_^~÷ËÌq7 Sð!·æ8ª(R¥Ÿœl1¦ Ýö¼˜_Q 㣒&¢×´R†±Þ™õdÐP*o¯‡wõ-bEîRR’í†[Ñu’·¹®Úcð3M]ß¿hÞœ÷–÷²7Ya’Ì››T¬†—ù‹ŠNQ¦[K¯ZK§bõF·”\²La™«}Ÿ½5{6Ò¥2Õ^ÊSKaæt˜`L Ž Ä’ñA˜©•š^äÕ»wo?°ê‡oVNcGEO }i†T‘ÑCËéRïM.'£ÃûaAoÉØ\GÜJvìß-ZðByù,*,ªj6Yaj/=QÑ)º?Ò ]j ½’j!Ómi¢<·g'ËÔ’¯M;p`¯,Sí©<µçÛÏygL€ 0h¶ñ¦å£Gd¡(äz™W~öΜ…i™™ÿÄñ-âua]?ìwJsz@¨Â&—®%CD.ÃK†õxTcÒ9ͧy!ÔóaѬi€€u”¦½a -êù 4½_\wÚÆN»7Ä»²U¤z<¨’´ø§õÊÚKÞø~á'ßCnúÆm•؈/õ´ãƒòB¼öýmI½j ¢|¶gG=²Lmøù§—¿þøƒ¯À#òåÉRÒïæöÌšóΘ`ñM ÙƇb)%@4$B˜h˜ U±>ýb/›÷Òó/_ôçëHÎë·ìÚcýùÂSÕHÌ¡ œý¥jTüiÞ‡mt`2°×ððNJ÷.Å©9´d®wž UT5?d„ØÇ0JÈè ™šëh<: µ¢µË—¼ùÑìÿ½‹8˰Q+-í©ÂD|‰3ñŽw×":Eå=Œ¶^I=ñ¦}Šwi(þejýÊ峿¿ö_n­òD 9‹’‰¯3&À˜h«š]ÛÍÊqOŠ•¯¥:ºL?>±£É ØR±uÄÖ[§“‡žÖàSN¥:Çöëm<èå¸~½šõÄëëÙ Cƒ&˜ËÕˆü{<šÛóAéøZ«m£‡Œ ïtyžü4ÕÑ7héOZ‡&—›ºgßâO?~ùÇ/>£¥u÷c£ûBCÛ…m62BdïãÞµ¨NM©?ÑÐ+i|HÝ¡="‘Ô©¸×ˆ2¨L}¿èÓ}·põxD¥<š81Ó(Ów KÉ+.È-l@D¾Ì˜`L MhvÏVozO7õ³Ü¼þ 4ˆÆèÓüª$;±iß|üÁÂ_µòô‹/†ecÏG%»#*]VZr¢•Ñ1UIrâÃpõ ú¿›’„¬,ʰõËóÝWz<} ž> 9ÃÐü²rŧ_¼7ï㊊2G­³dlÐFFñ$®Ä·= ¹B6mת:EÔ×£ú¿½b†ÿ¿¾Õÿ~LìÓŸ@ 2µáç•}ñîÜ÷ËËK÷ÂoÔÊ=C1ÊSuà™ê/3&À˜ˆ'©°ÈÍ߈™ ˊݹG-õDFG¶ØÒ±e`£žzô CôíÔ±É)º$$'ut8œ‰¨áE$?ˆ¿M8ÝЫª**öU”–îFiåÏK¿ÿ‚Ó*š×A† TY¢JdŒÐ5òÓžŒdW°Nv!  LU¢Lí¯(+ÝõëÏ«V¬ZòÝ:h‘ò”•ë~3ÌŽáÎëRH¾È˜`L  ˆHe}DŽû^´í>‚Y gä~!4TF 4‹Œ2BÈ¡-["6»g{ª\ÒF."ùòF³ÿå| 2"h“se¨gƒæu¡Ad„1" òÛëT{¼ëáå¹UËSvŽû,,qñ ]cfä>žÈì‹ 0&À˜@Û#ÐìaW”å^Î^O—è›FcÊÄ”,WÑiÅ®ìò   5µÐ“£ŠüM•kªD“ñA=#d|P>¨bIÆG{0áo¥;p·î¶÷¡Èôï¦ä¾·™lFÆns¨6Íãf<˜ûZ3¢‰û C9«~X¦¸ï¥!²ú¢%ã¸Ç`±PÔw&eÌÿßðáTÎâÖñ;*ô­Ÿ‹űmzþøïCûä«L ýp´fÖ¿¿ò° ÃSžSjî ƒ£ƒ-‹%%ÂeW˜Çѹºeå~si·4¡ÜâÜms¤¯Æì³róD\OQÅRè±ßõtô~Þ庩²1q5ÖoVŽûj¤}Ü߆ kYÊ´Šrkü°ñQTiêâDc¯(V*ÍÏp‰z|ègáLÇiÂ0ܺ^ý{ÿËÐ=麡`‹×(ž²)Åê‹IŽÄûîù[ÅFé¡1{èõ –b §0(SØmTë­éî¼OSüfåºÿ«åû"wÎCÁÂ{õ7°¹Åu6>@2 ëtÅ²îÆ¥F#]ÇË­:Õ E® +D§Èè¨\!n¯X!ÆÀÈèagÊW˜pà}GõÆù3„eÞ¹¢b÷ÆAÏO˜œQÔT#dD®ûYDl§…FƒÝˆ{­p8ÿYì÷k4¡Žv=•^¥ï[å ¿ŸêûS°´øŒŒ÷¼)ÄhÅ2¿Ä¯°Œ‘.÷)†nÝÿƒð<["T嵞êéï¹\CõÐ)ñU&Ðv ¨­%ú·—÷¸ÈÐ+VQ…Ügx4,Ì †°Þ\|Ù!o,¿º7µä6Ê©Š#/‹KTE}SQÅPâÙüM–«(¥Q‘5Ò³¦8— Í72XÌy‡ç‘ã>=ækç=5¯_ba±ú E(Ou `hPöœ¨PÝXé©\YX¬æ/äyU R”©Wà­¿âXÓRæÈÍŸ2\.*–xSQ”…ˆªÕ¢Èv¹OBKia« Є„³r ¯4uóÜ÷ ,Ùèçp’l• Ç¿T<¨b…õeYû $A™êc ó™å•{¾>áÅçú7à=àep=¦üó³àaÞQ°tÏêh?w;‹Ñ¥Ho†ÞÁ* (X9Ù–ÞQdx˜ºõîõr‡¢Ý…çç˰f–è ]m7‹ÉšD Uz>¾½¬ûm–i<…JR“Œ¼ ®,/¯¸xXÏóOz«äׯæ\Ó2^šêº´ÓoÍ}èU½BÑ·_‡ßE+\ÿÓÜc©7ƒ¶¶î®ÒëÓ¶ž‰x’ÿ©ÿtJ?°qí[ÈÓYMÌWŠe™Å‹ÕAã³Ì¿7>esQ~Î 5á&eåäÁÈà¶ÂÂGž™0aWãã /DQAîËáùŒa_q‘)”Ì–°Žh#ò þ,,ãI¡h—£Gémœxtƒÿ3ý\ÇzyKkRþ,ëDQõÕ ÿ<;lù_oYÔè8åóbwÎó2*ÔKpü ¶óä¹Hï].öâ¾HÇÛ ñµ™wÔtWî×#]ôŸîƒá°¶û,;Ïi™Êõø•[sŽwL î´¸ññí°î7š¦ùtH–>Èé0@v65>ê^ƃºGíVªìÜ«LaƒÕÑþ¨%¬“œ"áDòw««àÝcÑŽ1Ô¤ ­Sï¥:Rï}Âu×^ ?™‡á&o`¸É ) ÍQج/üRš -b hQìνX^y_Á…`1‘ÒÆõUªªŽ·L³Êÿ…n§©›OÀÏoжrM-ÊŸðö"„ÝEnZ 8J˜b4ƾì+vçH×üןxG1­3,¡\…k¦"¬ÙªÃñÔt×ø_¤_<øFb\3ÅÓç–*ŠvÒû‚ºä+õ}ó`0‡ÊÒ h…«PUñ’i‰^à°v†;‡ò!fÍš¥Íÿñç¯þÈq·Œ|>Ruj‘{«v\žý¡•çbä"õ—ïU‡SäÊ]LþïzüñäÒ]å?(iŽS¬Rý A¸-CàÒ“2>ÚÃU²s ¦ãÐqÞñ²†ÇùXkÿ¼ËãY³%¬Ù»âMü>KžkêóCî,,R+&d›ã›‡7œùöÕæ‘Øï ¦£¡ôàV×Ä~ñN’Ó9tŠkì&)Ϩ¼Â3 Óœ*œ]ké;žRÐRXœŸû]·uoɪûQ~®CùLGµøSEsì‚"#À>˜Ž“—Påß/ŠZuªWÝœ cë·ÐgÌ¥QfŸ7¸ÿRÿñÐCi{x&C[/†\NÛ÷œ)ÚßÉ(ËÊs ¬ÝƒsU(Oça¿ÇR•‡Pü;žgÊô¨g½I—*}»žTœí¡ó0ðhÐÑŹ7Óù# K¹‡G"ßë-UL›‘Ÿ;•®‘ v¼Wkÿt»{™•ž'ÊdwÔ›UÇ%$+xÊÅéÅùÖâ¹™PçbœüüBÑo CÌÁ=kVo8ž•™ÂÔç ú÷̳–_wóÍÂc)/á™:Šâö|„.†zGAFC†bè/=ÿ}z8:—žÒ9å^ÃËÿVJÌÏ/Ĥ¿é茊·•CæPyÝ3mXsÏ4ÑÁÙ[Ú$ˆ*Yú¹5ÇÝG×ÍEˆ|¾š¤ š‡´ÆŽ¼¯Ðoˆ Æ– •Ÿ‹aüÜ Ãdøº{« Aþ*Õ¡^¢8”?!·'£Xú³+e–˜#áþGÂQÈÿ0Ž>íz¨÷S÷>êèp*{å–¢ÞšêL=9%3ež‘lþQÆñÑ’UTi:¾Èø¢ó”ä}¨Ã2íªôýoáEzœ¢*· §z* %èzþŒ&¿’Í0€Å`•zàOlÃäSµÚ4º&Ïð@<éοËÊž¼Þ^ök÷þ4 åàœHå÷i\a‘ve3㻀›"i7žÀ:J¦ºÆ¯A™9P©ëÔ#és†eÞ ÝYVìÊ.‡Žô@ùê,/Î_²šZ o°Tm¸Ó‘8Hõsèaá_¡tœâ VþerïÓQÝxrÌ)Ê tä>½|`þÒÕ7I06ôŽÂ»ì$}evîþá`£Ÿz*5“å?¬‰·ŸC1m£2œwe%”Ž{³Ú°îI ½£B½cíEyʺ¯àRêUªz{ ÃÛïºÿS9ïsºWì˜@Üh±žª,b¸Uꉑ¤‰x/ùfØ!9ù­maÁ°Äžc1ÖÒ4LѧJ÷ÜŽûZ¼ø_”r!Î#M½¬èÁœ¹òZîïÁË~#Zø©u‘ÜO·ç毪²Äº‘y/†Éð/«\är žæÊYbû0¬P±ø/U”Fæاä?ÓcQïÂJ¿ž’WGäMÜ€Þœ¯Æö†ÙfwÛ2´¦¸k½† ÅP<ÌFâ·Ïh@<»uì>åŽ;ªdü÷ÊB´ûZµ1„âE˜EÒ/ZasQ™L½t\H/›*€0D¨gc/Zh-M1J©·‡üàõž©‹‡ïr=Þi²ëîݦPÿ$ó_ÈÃ_·˜ŸŸ /zTå|´¬­|¦ wµ\ë¦q–éïã$Ę#pFµÇnq÷UÜv=ZoïÁÞv÷>òH*hºfeå> J'Âp;ïá±cx}´¯ÿg8‡˜¦~gÄs­˜Ï<3«ëüۆ‰ :”é4Ò{a(ua‡ÊL.îÍÅ®{wú×ÓÑ0õ`&ôs4â°{ÕèþïÛ[ý#ôÜ õäŒüñ¶‘‹pÀØè=þ«”#”ŽËt•¾þµïGŠÜyÏÕœŸƒÞ€÷` žŠßÏÒ}4PœƒÞÅ#eïbvŽÛ¦OàïH ý\ ?Õð[%ËŽ÷ãY³ÒÒõsq¼n³1ñLì÷¡Œ¿i‚Z¬ßû»krF™§ì$ͩ܂ßè E¹UÅŒù9²gðiô¦¢§5½Aû óz÷€ÂJ‡!r+ÊÌù`û*ZÆ”çÛÛÞ0Ê ñàëÉ|£|)*vß8Ç„¯iõ¦~¼£º¢Q †‡8ÃRÕóë…¯ó|„Þz÷©©T_xò(®ªmû/‡ny¬>ßû÷ÛF‡L#œwTC:î3TCèžLÏ»ýŽ õNÄ;ŠÞ‰­úŽòÏ ÞiçÁà#®˜ž?á+ÿkÁމ'šmþ¢:µÓ‚ùáóL ´XËÕwú_†‡ðo£ Cް\®°ó¢ë.äÏ¡Bœ…Jòû"Íqê×û}²)b7­6áûí=D=þçžvç­Gø50çälA…kžnBŒš81~/Wb¦ß±b¥uï7:d&.óÿããЛ‰Æ›rCÅè œ§á,~Ny­aÃ’*¢NüèµXŠR:EDÆîO/<ø†É´Pq›ô‘-¼<ƒ¸é®œeh‘ÛRj–¥VZ¼¸1ÞV{qÏÃ×Û½oYXBÙ²[r Ë8rlŸzÿ„¥þQâæ½„ûŸÓTõ%ÿßòذ<·#[ñ€¾º=/¹ Ãã0 [ï%¿†öЉžûöìªÓ‹* TäD¬Öò:†C=²x®ª‰Û°ÕÝuÃÔÕÑpô ÅÑáET¦årSe^ì‡áÙúúqcy™Â®ÐßLô@.¨{MñéYØ:¸ü×¶æz1ÔªŽ[ž<»L¡ì‡Æƒ Sן”eÊT¬±ä’~uBùý@¸÷Œš±ýŠiÂØRÞÃMžWP+×+ÎAyÚBe/Ëõh”ÏCã|¿(D}ŸÊöÇ+ÖZ{¾î=¨=/LôÆÌFœe¨,æúoW‡¿yñÙCaìÚ]ä3®Ü~ìK3pŸÂv·£×{F ܇{ÛAqª'ÌÈŸð‰èÏÇï( }œ‰2z=Þ#(J¶»ïµäp¾šsÞ]ï¨ðu<¨îÕM2Ä;ªÆc˜ïÄÚh[ãÿÞ‹¯£ N„ᱨVšàGÔ€žo(B»9žW N€¯´'Ž–Ê,æQØãU£‘^²ý7•Z ëÁÒr:2×L8æe[ýeîðâ×ñb¢VʺãµQù²9*š˜i¢ÃÆn)_t <®¦ euÈ_Š} ©u§õî]=÷* ª9§ˆD-UTÕ7TÞèšFÝ¤Ö©ŠØ\û+äQôü}V ÝnõÂБ70—c…ßµ§–Vâ÷ûàC†ƒ©œ»Õš¸åmü¢-fa’‰a^¨,.ÑÝto®£€¨Dé¾/<™K;>¤‹N$«Ž>j–¶É¾XÿŸ"ŽÆ©Ï0dìqì½N}?qþ{RQr_S©ÄðÀheÔÊḞ»"0¿§v.…™þ`Ý:l} G¨7ãÀ_Ó=âDù%ªK×èýg}Ý¡ô¬j åɪf–ÑoéP^*%¢FèøAå_ÆW¯Ž:éÁø©ÕlEIDÏGÊïÓ2\Mï SM¤yfÁÜ»¨¸¼@†ÆfÝ}zsþï÷ǵs©[‰hµÎC1{—§ ° …<ë<›,ͪBÊ[m™ªd⨇F™ º”••WðÅù9~é?^÷Õ†y ÔyE.¯V’e7"¾‡ÂŠSUÆÁÈþg(¿õŸÐóßQ:§¼Z¶«|ʨ<÷P ³[^Qa^hiÚ½Ó çžŽÓ½é}GÙ~Ã|'oK¿£,…†\?‡wü?0ê¿ÓssC¾£³ó&oèžwQÖïÂK{äÁAyàL ŽDéA[—Ð×’Z½Û:¿îÙÈþR {(FXÆGSRFâkT®ðKóEª<ÕƒLÍ;öº‡zÆÜs¡µÕüü\ ­ø+$Ïúû¯w¼/ý³ýÏÍ_²ê0¼üðœö:¼L~Âñ9þµ|Éô§ºþ±ÝÄ{‘ÏnEæÊáò²oë P÷­*Ê»040…YŠêδj °KŒEÉ[=¹”ÖÓÙëSŠsn¿F;kï,WáÑÅ® +eÄh):C>|¿åù€{‡::ÅiíÁ÷P¾G75};åÑ€þâø¤¡T]#UªJÄs Ý;bÒLçñãnö4o¢lÉÂÕÌ š‰a'¯bˆãÃÕÂ:Ëa‰E9åþlÁP£ K·ËÔbéÇ°ì ØöÏpu\†mî­Ë+pº8T±cª+燀ñaF<ÎÃpªu)R?)Ý]–¶Ew߀£õrŒÿløpÅÄwMæ¼fû¹–æ] Ç^ì"7 æfÑóõ  æPC­ÊÓ£ózy.ØezYQ~Þ˜÷¹¿¤a"Ù®ÂÚcË+îׂ1ŠÄy<ï)þðŒ&$Î;jòÝwW`(--rmy…XùÚÿ9\/Ù†ßQáèx½H›ó3œwb,¼£Ðñ V+ËG™=Ĩ´^É**°w 00lû7¦å™‡Ñlx4G;8l["Ð"ÆGõådT;£šJ+EçÐþ#<æ­#r Æwp$•‰ÊnպDž!D«E¯®s(eê-AþyÓ ±çbHBJ]cÅ_:T žEƒå‡Ž1 9|n‹±í(ËÔóáYñ:§pNÖ-ψsº@Š”nKªv”ž aå¬iù>“þ"µGk*^ŒÖXeæèô?]T$YfÕÉ †µ‚ækØéX ¥; ­²ók–fÉZòü2«ìEœ¿Rö|›¼o£Ë®* äGÏ=îˆehÁý,ÎÊ›”“¨¥l¨ÒÜŒñ§`ˆÚïÂÍ }~D^á_0,å=Ìø*<•¥5üÁê8ݧ(QÀÔ *SQ1>ÂÕƒ¢&,€Q±Ý‹O£¢4jAîÆ@Ù¥þŒyŸ #öfLòüNôê²PÙ¼ãÅÔƒãsaé¸Ïwóz9r?*ñ,öèÖcX*¿»#÷Ó-ú#G*Šç7ÓósfQìx|†gE^Ö¤IåBª ¨þ Fþ8x±yò« •†¶=†@G()ÎùtÎvŠò⹫ê,³ÇîoÞy0ÍQˆ÷©`ÔO_9GÙ?ßîxíV×3'7Ð;ì´Í™UÔ±¢R ¬}úF>Kè‚‰ç ˇ¯ÓK±”ÂxGQZš¢ÎÔ…Ža®Ö ,R,ýpÞQáèx°ø›r>¬wbŒ¼£(iÎ{ö{‹ÛÉè¬7«ëá;?†®c˜²¸C8Ó¢ŽœS¤î®3Ü{šÿ3¸ €Þúè;tý÷v*¨˜D5šÖ¡Ë1^öª2½l#a,¹Õ+ˆ\äÿ‚OphÿÄð¡‹‘ß9¡¾q€Õd¨'àl“JôM»`x|ŒúÆ{Òr^GËû*šr1Î]Y…•N09ðZ?4PQ—~"¹Çrµ Šó4Zè&{ôÝ»,OÅTÖI¦yžÄñyJµ •½":Oͱûá2~?ø¨Oèœ×©¯£'«fyç{Ð9šüšèL¿(ò¼¨Ê³+*ŒwcŒúŸƒQ«‰®ÞŽÆ@c˜Ë¨½B+õÔ»ç?£_¦Pyí-ˆáê=ˆ «8¡Rx æHÍ %V=›€²ò#*Þïãe¿ƒÝš*îòŽŽûûoαmœ§(—CöX±ê} ¡ª0­êU(¿£d¼XÅmÊÈ:Qêù >à f «²¨šâ‚ÑÝÑc>ÊRO˜,_JC…âÁðœ§QŠ-aL¿î؇ü“a3‹PäÉtÂÝ[}ºAÙܧë{BõÚ†]›ñWíÁ<À¢)0 éÒª­4Œ2AIDATÂ}GMËÿ-2ºÙØ1=á•`ò„óŽ GǃÅß”óá¼céE‹¡`»?!¯Ùh }gyÄ<:A7ž6<úzÿ­ÊØÿ·:žùˆ#Q}ØJNXåjŒi™á—š°?éäNŵ@oBÐF¡Õf”t«ŠZ(0€gšøwÛòÌýc¶ù?È'µŒ&T*©]Ä)ÛëÏG9ÈsNÐ2+Á¨ f@ÑõæÊ‚®h§Ør cU‘" {{ˆ¢ HYƒ|Õ¼¢ÒŸ“…¥m£ì"­.×sIûÄž”£8¨ô éxЀM¸àÍãžé©Ú®GÇŒ©3O„¢£*ÒTUoîÊmd„gz&ì½’Mµ]¡ b2ZmoR”(8åÔ%×g¥è}ѶÊ;Ê~ž×qŸp:hèÈï¨æh˜@´ˆñ¥pï¦E“ƒ£ækÈÛÛ´Pø¨%Î3&ãƒæÇÝd•âœl³Î°¥è¦Ç±3Ö!pÜ Ågã2 ¢ºæÐNZzíˆo£ÇϘˆe-2ì ¶F†Ð׿£= >ÚÙàø™@X0×(êe ‚l KöÄÚ8EÑZ¢< Õ™Ð"é´ñÛÁâ3&çZÄøPԺߗˆÓeQŠ—£e±GŸŒºPJ ¤õLpL aÎcôu0è›=Œ6TJˆÏW_zÙòPð5&À˜@œhãcÈì­Ë0!|GT™)âã¨ÆÏ‘3X" *EY3!)yA”Óàè™@Løö¤lVåû,ºÂ(XT„{ç£Ë˜cgL -hã£æûßh¡ùŠ–µø£%7ÇËšJ ©cw ÷ûš¾¡ph¥ýhÌõ¥ÛòÇ×™@ÜP”—£šUnüQž#gL€ DŽ@‹$®¢9ŸÂÒôA­È;Eyó¤Ù×F>bŽ‘ Ä&»‡oÂeZ´¤STí±hÅÍñ2X$зKï—…¢l‰†lh€[?0¡ãш›ãdL€ ´5-f| ™³i >ð55Ò€ðP¯vª}Œ‹hWTÑqz?"Þ;^Æß¢ã;ì˜@û!ðÎ%—à[–b|Tr¬(cþ‡oE%nŽ” 0&ÐÆ´˜ñA\œÄx ´DhÄ> 5îø9%«!¾Î­º\Gõ‹ë‘‡ý2¦—½gŸPÕ›`,Dn•7EìNt$Eý۔ߖ,‡ô݃¦0æ®ÇOž5k–èšË5+!ËU”è}— ÐùúçBÅQßoS~‡+Çh×SétÂI# O¼F?õTb8qµ¦Ÿå7d?ÞÙ‘•AùÏòë²^‹lœc£÷};*ðÕÈ ¥£ÿxè¡´È¥$éV øH7ƒ•µúþÃ-ÿáÊÞ˜²ÑPœm¥lÔgÊ¿™@sDý!U_¸%—rD•°á|÷ú×û[JѹÛF66¾Ì=CÀ.,vçžÝذ­á¤Ë}Jc¿þÝrrš­C  HÅW¼›ÿ< *T¡]0.[_NNðEîeª¢(ÊŸð…ô?ÒíîeTZË’ºuì^µ}ß7ªCý¿é®œ€+s+‡Y¹ùK,¡|=Ã{‹Œ—*ÌV©þ=Î!ÏùïGæ 7LëY§S9kª+çy-û¾‚s-ÓÄð4e‡%¬Däñšbwž=D“*ÕÛö?¿çÍ(Èí/ÃÛÊ›8İŒÇñ5âøIB­üs|ú6é<&áørl娖+}ºÝR”•¥C†BaŠ+-Å¢E7R à ÈP„ãƒ\ 8г1ºž %û­®‰ý<ãXep¾~žCA©B®£ »V•ÿC°$lŠ¢º‹òsþCñHGr¢!i«&´ÏB1ñó?3óªÀù*y.V÷ƒfÍê *÷| f'5[FE|Ö­Oâ †ÞTÙP\·æ>tŒ.<ïõ¡`ëkPÈÊsßx²&܆å™]\{L°¸Pv~€ñô,}õ^ú•ûÐQºU½RÓÔ‹¦?˜óž¼ZÇ…*ÿþÕ=¬²‘ã^ät­Ò«/ ‡x¶™²áÏŒ™@s„ÕªÕœê‡|xiR’8yü°9²‚ù\‡H¹8ÃҙêûÌĽY¹§ÕI×× ‡³ÁJ|vÞ¤ã¡[;a¨üµNxü€n®3MsŠËõ”!](ýj@G×;œ™§Aϧ%‹«Ë|(‹¾zÂÓW ¢ë潪%ÞByûmgʹ–e>pkŽ»ÈÌ,ׄ:ç§²foÃïÝõ“ UþëûÅï°do¨lŒr¹OE‘ß<Å5vS8q¶µ²€ŸbM"àhR¨f¢ù+/?jH©¹÷q<(oF%/³ð*›áûö“ÞÚ:-<áòó5"Ç}:XÓ]}ÏÐõëqÉ%/Së© yð0;ñD¤S"Êèóöÿqþ’U!çéµ7Z\<ˆc^ÎWdåº?P…5Ñ´”g„b-ƹëð€¾Æ éRáoÖ¹ƒºg8ÆûÒÃ0X<Ùy×[¦5qÐ…Gâ=Z‚nT–U}€8Ž€Ü?âükxØ>HFdÿ'ê!ãòKÜøLAî™Þ·?²ÍiÍp~oXú³h)Ô(FokZröØ›Ë÷ %á˪¦⻨rKiZ¦¸FU¬J?T9¤°ª&îµ 1 •—µüûÇžÎ»ehÿù—®9ýZäþ‡ÝÐA­¾0hž»Ëõx§É®;vgåM¡:´m†§ê&ÿpÁ޽•CïUÊ/Œž¯M/ëhí½Ï£—]®¡¶ˆJÓ hàȃïâi®Ü/eœJUºÇRv'*•Žƒ*e¡âái?ÅuÇþ`²kšXçy¦Q5^{ø‡kŒ)ÉŠÝê_0{Ç2ª¯D\ÅßÈÜ‚3±[3Õõ­ØÓf»úLèäh×C½Ñ|‹° k´×gìÿÿvxö¾«gͺð§ª=c¡ß¹(S‡þÊ ÊS)Þ÷/».k²,ü<ƒï 2è?§ëTÙÖ-q”èÕå}±qoèÆ2Ës“¥ªÏ*¦¸å¶¼IƒžÉ·Ü/U(U¿l66SOéePJ¿Bé(Þ}ïËH§çæn‘ë6´-û3q.è¼´`úJnvnÁåÉSЫ+Äd×Ý(¯îw SœŽçÁ+8õ6'§8”¯,ݼÚû«öèò÷îZŸPì0e§ü‡*†G\«*âEŠ»¡8ÛjÙðçÆÇL ©Z¼çC zôœUNz{ÛE'àaýZfÊäµ@{\ÿ ݸw;3•£Nzk{ÓÇä*âFEh/;’•×ð|û =ä(=ÊVÒ‰iÎ?õtäR¥~¥ª(O»r¾ûpɪó᥺§ÏÔ‰§ @g‡£Óµ¶œèú…áq·SuP¸ì.a!&iNå™4Ñþ`ɪ¡ä7T<¨lýàpjÃz:~w$&æïÕê?O™0aZkoµ‡ x[y¤xв9ÅRÔz9ôÆ£÷}è<»öM`ìÏ—§õ?ûèÌ ÐѯBÑÀuÐbŽªjC'dYÃmx r»R¯XÛo½o¢ÝC‘åšÔuò~=¿›*mûZr(Ã%$©_¢dþ(~ÝaW>äù@{ªdQ%z3ý¯›ŠÙåãWÿs0î7UŠ ”TòÇ-MtV× SÇoˆösò.tˆ„šï­(=-Kõ¥…V 0|úÈ(¨Á û>÷0ݳç_Hpf±ëÞòZí>tµþ‚Ë>Ý5f;m 0_ ÉAùBÀNOæäló¥§(¿š~y•y-üØ,ŸÌDˆJ½zšp¨w#Z´Ç´-GÄ—]—]¨©Ú¼ ˜ •h;gX) z8I$%ö_~ÃÈÇmx ¼f¡‚û'9×FW2"ßhȧS(—+Z—9( /{,Ï uh£Q+Å‘2M7d¹ ®s-Ààe#<µÓ°ÄÒÅÑûNÓW›ž-û«ñy¶”4hçÁ²{ÄEЯšrèó-*ÿµ>듽¡²aÏ·q¢¤:^­£âlËe£~þø7h,V3>¤ Cæl_rÒÛÛoèÜ5¹‹PÔ¡ŠªÜ Cã!Ì瘌ßàÁ½CIpÒÜmO~kÛäãÿ½-¤‘"ã ´÷N\³.îáèñö3&ìBEdMÖ}Ï"¿[µƒð{ÇÃcÇp¹ð¢TÄxáÊ¡ð£.¦¢ý0U,TÊ:P8¼(UM™B g•ëçáô3IÛ(’t´r*o˜¸‚üÂgš+g Y-éñ35!/@ ²-—7XíÿšnóÃSµ”×K’Ö%kõeÈp™ýâ¯õÆGí”ÀС ôq#Œ&d[§&'%õFºz›‡íqTÄÁ~Œ¦¨—tHÍì£ãŠñ#ôÍA¥õò¨¥VX}8 Äë²õ?X¼¡Ê¡ cT;4´0Þݾó¶÷¡ò|cöªi¥£0Õy^À/†ÖÄïPi¡Uv ž }½!–HWUË—–Ç™T† Q†ŒÃðcMC<Šü^€äù:ûâ¨ã·‰?’㶦¦âVùG™ËðD´óB4¸vi¢3ý ecë3Á’›¡o˨Ç?¾¶v¼ä¯#6-»>ûÞIz¢Uûd4‚DY*DÞžÀV€^ô,ô¾8üº¬Þ˯Ï¿|øM¾Þ ÆæµfÞÂ–ÍÆDê]‚3¯Q4 olÈmÜu)Öø¢Ø•]®¦:^‡ž§žöÚ`Šö„뮽w‚ð˜SkÏ7ò( µÐÍ"M±l8•:ž-/£‡ÿ…¬¼‚ÿ£¡•°n‡âÝXíf”«`0ôó*%Íñ¤ÿy:nJù%{Cec³ùùù0ÊO?~¿,⌗²áŸO>f!Ð*î xø¿~¡ y j¶@^š}®lOÅ`tÞ¬oú“¼`_ˆ®˜Œz#"þÄìÝi™Ø¸½MÂBÛ‚ñ±W&¦ª¶€®ì„i>9⾂˜8:Õ¬õ5Cl™º«ý?–Â)¦r.®_,è§Ó9¤Aí±?Òq¨xì oB¹-½‰x˜Ò ¾ÎŒÂ“+1KNCœ½Ê=ety˜8c­$ûÇ:ã0@k*…b× Ü}CÅfä{Và¼×uØKg1âK׿@Kí%º{8Ʀm(X¨rè–ZßG Òá±ÄÌz¾6`ðPÿXUvȯ·òŽ•fiV££òzä}î?`rûåJŸ®Cåy”ñÝhjõ¥¥ OºáW†1I8›üÞêz¸»î©^6ÒUpNý‰ø Å!Ójξ!9¦ºn+Å8zþøæö ÅÙ´™mÖ×\„çÏç44Ççõ™ØÃ…„•-´®çPÃHöý…hO“ Çp¨C¶=W³TîbHNÛAîÇn9è\SNA¯˜æð‘®‰`,ö=ïØþ Šˆó!nÄ›àT¼×Vèe:ùîñÁ²Õ`ÿýŽÀ0º¿á_‡žBz}4Ê…££˜Kõ,^zs§ä}Ô¨Èëy.rç<4"oâGÂÒ‡a´Â"hOGôL³•%^õ_4À ŸŽáò¼oß„òJö†Ê†0-ôjõÖ3ˆ900 ´zÏG˜rFÄæTܨªÊXïñRÚœŠó¼/»÷‘GRínmt­£ËãOè®í—àtžKÞ(ážê„OÐ’´ ­—b–í¦žŽ^Wø är ÷µÆ ¾2loâ%²Üäª=Áâ¡U=ðâ“Ú)å*„ù½fÿx†v`<±ræSdüö>à0)ù˜ DžÀt×ø_놳ðbÔ({‘Ž7”J¨rX?l/-g:*­Ý÷”ê—Ö¿ÖàoËñ Zƒþh( *·*‰ª¯#¯…»§ ·†!\)Éêý‡Â`HÎ4/)ãÁÐÈ#Àc£ü-÷v£æØ`ŒúPyNîÃCúoÎ>´V‰=±·&ô‰EM^Œk1T¶N+]U.Dðc„¾}[vž{¿0¬¹°;†a2õ–æÈÝ.Â:´W°˜Áņa\CíUš/*ß´ÊȼwÎï6„ƒ³†sª·Â!ÌÀÔ€×CœlHGq¯Áû©龜CDö¥ùã¿Á܉ûŠÜæ¡©ít¬Øõ9¦¥saÄ¿…ÕÖþQ߈÷EÞÈòŽì`°lx—üµ†Ò¨ _ú8'— B|Ü^ ´ãÃ~™*b@wm«O»óÖÓFC¥PAøèÀþê?Ú `Y·b|ò¢w×gžv-‘J±M‹JL¢Ã‘1îücÌv¹‚/›ˆ¦½¹ˆór¨ˆ½yÍúþÁâ1Lc&(®œ|÷ÝÞ±»Â;Ÿ„H4ÁÿÃé\/­ûRtu˜#ó&zeÆ9š@k_äL U ÷ð Ѱýª=d1„ a•C¿ðŸ¦8FaÈÅ$´—£h…ïŠòÇQc&gG¡¶šW aYýaõcÌvqŸÛW¾äu”¹ß õõiÅéVgN<`‰âW!ãÿÕ KÂåo8‰gÅsIvå°&*«˜œr Œ åtŠVÇ¡¥Pé8XõýÑïÆº†ä@køÕò{ íUÌ5ø¥‘åz´ ¸_¢:ߢïàŸÑCàkMƤ8?çYTÓä†^ß‹Ý,˜Ñì%Ö›÷¶æ¿Ø5îW¼o¶¢[K×Z ¹ªª0¯¥²Ökå»-==«¼‰ßú¾E±kÂJ'/ 4ÝÛX6¡t:4=[½{:s²ëÇ;"·ðr"Uÿ|¸¿1ìêv¯¿Ð°4ÒÓr½ìMäïÑéî u*ûŸÔå†Ê¿ôW&¨ìþþ‚• ųã ]ïû× ‚ñà²î]gñL f†]E2:¢¯ÃªT/Õ¯áaú<–鼓Å^ÚìY´Rñ˜‰;ʱН/åçÞ¯x0LCýu}Ï,ÙãÀCe*!㱤àAk§»ó uïi°~ÄPªu%ž‚¾ŠnÑP”™Áâ¡—ùfóç;Ðþ†~uFºSPË¢.sá]5$ÿ3Äõ5ndøÂߟ-a>‡ýD´`%•ée4¦ºNo …eÇZ‚€šdÍ2*ÄcÐÙQõÓ3uócè®=ò2¬ÜóÏPåá_¨Ç´üñߢ̽…!ïýP´„©Ï‚ ° D’âÐhoHgéÆÿ¡’‘O¯ù{4Mã<ºç+Äg_Bž7ÓÒºÓóÇ_+ÑW-Á5ê ]‘Ø5}æŽÒ-žöCÇ w¢z5üß,JÈßýn¬Û‘°%5˜T™+ÝUþrÙžÊ3ïW ŽŽ“ªôý¯`Ïbá©ÌÄõ” 7àú\ÿžÞPL+#û¯%`aè•PÌ{ñþY„^„Ú XÐúUÛ{d/wm UUÇMµžP+3¦ +Œ²?«Âù±ÿ5:NÍLq—î.ÿ ô¼Q.˜Ž’Q]ê)›ˆwÔ ¿Ü,ËLêéX–úŒðzÄ4Ô“XÍ·{nAoC ¥гë_)|ùžŠëÐËx:žÎ¾8ꥩéÉ?ùë²Rþýu²¯&{j§ä‡ýã Q6®Å»Ø'Kƒ<|Bó`íšb¢uåïMÖÃËvmVAA\ûùöÜüÃåµì¼ÂÓ`œ|"ÚÓØfZ_®sN¿ЇZÉ_ øhíu—¼>jâÄÌ[]ÏØßå9Þ3&p0@ßÕ8Ø—÷ *mïÒƒ]užÊh ¯.Sù TV©Ì×ö–xcG ¡äv-˜·v®Æ›fx_8¯–Ç7@:,ÇÔûBï¾`ï¶`áhÔõ66\ ]Tþù $K \6‘âsL |+ºáŸx8>Ž®Ž_ŠÌ™’]\ì°6n?p^Ö«{ápnùK ^A]¾ôCûm.Ö÷ï¹vKL8h¾Ï¢›O8i±&ÀšN+ÑŒ.rçMiz ’ 0Içºul !RÒï™h_Øø¨¹ß4¶YèÐñp,ºO=0D¾Ó4åšÌ6*¯ðLÃ2G ë·'†NìÄöeFçLZ–·1ê©x“&ûeL€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&Ðæ ü?¹OÕÁÙ˜¾jIEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-dvr-compconn1.svg0000664000175000017500000023507413553660046026030 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-04 23:06:33 +0000Canvas 1Layer 1Network NodeCompute NodeOpen vSwitch - High-availability with DVRComponents and ConnectivityProvider network 1VLAN 1 (untagged)InstanceLinux BridgeqbrDHCP NamespaceqdhcpMetadataProcessvethtapeth0iptablesPorttapVNI 101Provider networkAggregateInternet OVS Tunnel Bridgebr-tunOVS Integration Bridgebr-inttapInterface 3PortqvoPortPatchtunPatchintPortInterface 3PortqvbvethRouter NamespaceqrouterVLAN 1 OVS Provider Bridgebr-providerOVS Integration Bridgebr-intInterface 2Patchint-br-providerPatchphy-br-providerPortInterface 2OVS Tunnel Bridgebr-tunPatchintPatchtunInterface 3PortInterface 3Self-service networkVNI 101Overlay network10.0.1.0/24VNI 101PortPortqrInternalTunnel IDInternalTunnel IDInternalVLAN OVS Provider Bridgebr-providerDist Router NamespaceqrouterSNAT NamespacesnatFIP NamespacefipPortInterface 2Patchphy-br-providerPortrfpPortqrPortfprPortfgPortfgPortqrPatchint-br-providerPhysical Network InfrastructureInterface 2VLAN 1InternalVLANPortPortPortsgPortqgDVR internal network neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-dvr-compconn1.png0000664000175000017500000077617713553660046026033 0ustar zuulzuul00000000000000‰PNG  IHDRN9ЉåsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì`ÅþÇgvï’ºR¤–§ÏÞ{ïýoA}Ø•„ª‚JI.x’Kh*6J‚Ë{O±÷Ž>ÅŽìˆEZê–ù{Ù°¹\Â]’K.Éoá²»³S~ó™Ù™ùM[!ø`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ ´ Ù‚asÐL€ 0VG`d0t°e_a^`I«¾f>[ 7%‹1Á}+ c°ðû~- NúuK( ¿J„Ÿ —&ù\2ŸäK–ˆ ´4_K Àá3d& ¾é[-ÞßYö?….K}–Z>7/ð[2ËÜÒ²e Ò5kÝ.–´k¶¦„«¥îÿa~ðÆ5‰”-3'ïb¥ìÎ…¡œy‰ Ç4ÕKð¿›RJ“RªD†µ5¿Çæç÷4+eçŽ}Ürã%õÙwíJÑeÍÜàèͮݱÁÛW˜•#¤H}  4á{×¼¡çdâÓÐ8Äë®®¼×”,*Ló|!ÔíšeN…|7Å#c]i\—ŸuÙ'Ìd²[WúlMƺølÍ]}Ï©NùC|0 ÒNšH]?;8~]¤¹÷þºàìn¢|_ŠÚtWVÖZï³h×fÌ輩Ìîiùí²ÂììÕdÇ-¼öÉ¿;'Oþ³¥Ë3¯L|Í’•+NÉš2,W‹˜±‹%* ŠÌ%‡%Ra,% \ ä®—J>ÜqÛô ³Ç/kQA“(ð±wÞ™ZùÇÆ•±v¼%TwÍÚzta«Œ@îGš¦Ý4jö+M-6…]ñÇÆû”?#/ïi·‘ÐÔá$›å¥öl¡Ô° í‹ Û#õÉWQjÏ‚²w™¦­¿övíVZÆp¥D¶F˜pÍùÖ÷âMãxíÇFªel%[ú¬ 2 ³VÅfaŠáÙ¡µBªoQ¿,Ó:ˆéó"/µ2»ôÛ²_µ ù5Ìÿé}ízÃFcº-Ô(iŠÙx>žì¸e†×¾‰Š-3WðƒòôpG_‡»¶¦ÄyÝó5hO´öYŽ+ˆ…@f w¬¥ŒÏИ< ö@Eò€”Ú øMÁèÉã¨ÔÊ”Pc6¯+ùdT0oïXülvÊÿØ8•t.”—.`öž”âf!µ,Mȹ˜ù<‚U‘w]sM/!}Þíkdýá ƒzXÑ X6:;4ÐkÎ×U¤|[Hù °7˜IüêË{ñû– ñ¦q=ö3y9è<šž I›ÜÛúÒ§EË)Ö£N™Wõ+@Ùõ<ÊÉu¨_¦úÅ*ßP] ªêvÚq»ï´åêïx¾ÛÖꞌ‚¿’j¨Ô§Uw”T–ò-¼÷÷†âq„ÿ Ön Ĩ}p³Qú-:VÛå &Àª ðˆS5 ¾`4𔦭¬;ÁÂ’Bf÷õeO¥íe36xg— cã=è½?ß4íW0bÇ™'nòÚio×™9ù‡Ø¶u9*õ5šO?h~pòÏ‘ F§íØKLúQˆœÈGMr)zg…=ªéE‰}*†½ö~=µIjcžLÍ&…iP‹V³F§®¼×¬BÔX¼i\Ÿ}4Ú¯B~POpI÷¨®ôiɲJÊ…¡ìQ‘°n˜5«ã† å9˜<ÞVâÎb#ogØKö†jaäþ¿¸gYjΟ“yÔ£èÏÑYÕõØ×…ÁìOkÙ‘²`AnvQêª5]#)7ŒûàæÄZîØ€ ´sÕ=íœGŸ š{.…5“PhšƒÅÿù‘J=»+xÍF< %áITL½Öo6&‘ùÖŽE‹éÆÖìE>'7 qÅNÒ4•Hÿq¯„}㯒‹¢)MôlnpòÊh<!O ?¥:³Æ} 7Ä-^放1xÝj­ƒ÷§5&Žq[4êU¯ëYS™7G”×è}EfÛ™ÿºÅf"¸nñ½æUFÎô=02¹CMÓV|×€²boÙ!Z«¸ ”3I×ôC ôI5":°ÚªÑ#e‹‹¼£QÕÏ«.”í(VBaê]䳺îïN,ÆÁKè92ãÑÁà¢ð4õº°9h‡ânĵCFåvB`µõÞ•P„vÁÔ…ŸÔö=ïÝj´¥?(”ùXÈ3ëjîö®«¡õ<¨±>ƒ‚•‰…É—¢Q4üµe+ö‘b…/#;ôÂXT›u ¦´af[íƒ*Dô4Þ€Jó¬³Ú‹l`zÌW¨ÌîGïéœHUá-CxÃáÿù¨õ®ÎÈAe«dW¸+Âô5jN,4Àþ gˆ.}ÃæåNþ$2,ºÌÛÓ2íE¨^) NB8Pœ¨ÄTÑŽ ÎÜÎ4+Þ‚û¿àþ°È0êé¼þZݺøŽÑsÃGˆ_ ‘EóB8 0M° 7°+qÈ=Öb*ÒÔñß0ì—`³ùIRY´ 8Ž®Ýφà:„ö뾘v8¤X.‘PÂxµŸoçQÁàÐJ×.”fÛ #'o(–o] óƒ__þ]WØÇ”7ñBGúÍ­e@8ÚYHó9¡œ»Ü8Ò9#8}€0Íz +2Šú/æð@h•ªšÙK÷u:øöà¸õtMG¢øŒ Nl™æ•âTäÁê×5`ž»i´Ô'}SÜü:|JÞ)Ò¶g#-ß_¸Â*ÊÄ¥q9R }‚PÖ³d%Ö0¼ÞEæ=ï³h×Î4ª¢µgJÊ;JîYd¼Û?3ð® ~–B»¿¯ïÁàåÑÜ"Êéý1ŒÊi¸¦|·3âñ J’¥(/&£|ø!Ò]}ii—î#íÓ†/Â\3UØæÕôÌNƒ¬ßÒ5š&'[–¸¯Ýn`~%˜¿~Ró/:›¦ù<ÒkManà¨hå_2– ¤¬ŽÎÏßÖ,±§búÛaH¯ÝQÎnÄõ׈|Þ‚©Ù´aL“”—Q†Ï†‚4»xÀó}(=Bý ê”]‹íü£`ô&™{QÁ9LsÝY(ç”ôéÿö>ÛÚ5­«BÚþÿ·[ký¸ìµ57üœ ´'<âÔžR›ãZ/T‡“ôòÍ+ÌÌtØõ9(Ìôˆï QÙA«P{í¢a°~ÇÏ åbÎøýhªë°KóÈa_ì ³™¨ŸÖ›M;!y/ÂߨøzAžÇ ÕS¨ûB ¸•ÚSm¦-‡žT'£2ÏÂõ£p»-,–i=„°³‘6Àý¼SÿAÄÓ Ó©¦2ßËæÿƒ@H­Çÿ W_4ä/¾6/¯w48Ô†²q9žõïÞYGZ‡XÃpíÓ92ïyŸE»–E-,õ8žßÈs4-ê+âj+{z‘¹ŠÊu;™fåGˆÛÉ`°a? 6!ß륙Sòjt'u¥q]DÚïàÿ³3˜@x_†ÝÈ ôUÿlõ7ò˜Ó;)2êòJv¾„%?‰¦4‘»d+œ¸àݨ,±ßCü±fH®Fz-Äï p?LXö‹X‹tE]qnŒ¹Oïž‹pÐI¡ö&…Òõ e´£ ¹£J®¹{¶¬õgƒs:ÕOñn]Ou þw&¿¬T³º3Äõ›ÏL ½à§öž8þ^4›P~ç5¬ï •ïñüHÛ–;Ö¶§v”¶«IqÜüPÎ[îóÑÁüiôÔéÚok.‡ù÷7lªœŒÓIhˆü7­W—+œÅÍ0 ­hKÍ’çàîlì€D£à·åPb{4à'kB?½ ”õ¢û€±¿7U~w»®6ŠhNýmgŸþ€4í ì_ˆ))ãƒÁcL¯=šôú²9½™J-¤gåy-3'ô1*ë¤QþΈœü«ççf½ëuy v¯Âl„-cp®fNÓ`ŠwÏ@åK4wGC’Ö.atkËÁq ˆ$ùõ@öÝxp7”ÉqÞ_Š”k òjo·]Õ{|óiR]] T6ÒÈß1«Wd» rÊd©Ë3 §^pŸoa-v­X»q$ÌowŸµ¶3zn¥x‚ñ¬ÅÈuå‡Bxmùš è‘ÝR|òî9Áìî3÷œ(>Î&->Û(–ºaQg@‘Yt¦ ¦óK ƒ™¥…Y9®*-ƒ2,Ä®}÷\Yf_€4ôC)|Ä;¢k®? 9Ï¿9ëõSò/KÑ;?MS]?h$Ÿ#.—¢ƒdj´Ñ#¼4êóx×®i—{· ‡ý‘hðÎQ–*Ä;´Kä{ë†ÑóÙÙÀÝÐŒ)y§)Ë~ÜÞ)ÌËæõk\ð¶å›Í’é`~Þƒ±^¦[ìÉ‹Vó-ÜbVû*YʆjÉ”=eÎÓ¢“ÿòÂI“6¸æ9yW+Û^yg‚ùCMɜ Ï üúiþÓ6v…Ñïd®ù´[†Â;©ÎÅû8Ú­#è(»Ãi#åCa“Øÿ¢Cï8ØîˆÂõ§yÙÙÅŠÝ1Ûdí€8µƒDæ(ÆJ@9Ê&µ_buFWØ® »­åNª»½J=ŸÌZ0®¥k[É,ï=à­i*œ-ea'zm&å\¯ÒDa8 )Ÿp“b_:×wP%`o úíù»ùþÉ‘vùýñ¨Èû Çûm·aG½ÇZšü?4.>¤JÞ²­%¤°Pâ®Q.]w'©(ÇzÃøÝ\r$ü ³GÑÛú zµOóŽÌ9Óœ”8a•¦ôè¼Äë¶!צiLA\ÑPP`—S­4‘_´&Ëéâ1¢<·pjvµÒDV¼¬ÑzÙ/гÄ)ûV4œ¿­ï‡8þ_<£ßù²/…ÿ¯;Ê“È÷ÏÀ?Ý0-jdE=ÁÓ[ß÷*M°3¥M“áxŒPV £œQÜÚ—T›y/ìðZM(í©>â £ÚU|ô¾äf?èUšÈ‡yÁìå8‘¢/ÐB åÚ‡åÒ×a¤Wi"Kxçáô:ÞÁ¿[ïAAiÞƒ¦¦"_`t\u\¿Ù„RZó £A?ïíg¹“–Õ|Zó.YÊW**kRÓõ ¯ÒDϺtIù/^r(‹=ŠÍ%]ûM|þÞñ϶ÑTº¦u¤é]ôòu3þÜxª7¼AêèQÇ#-*ÒõtՌ頱ÌìÐ |êŒÆN¤|“c¶ÄÚVœÚQbsTë&P¥¼8S•dªùgÝ6#ŸØŽ]Ô.Q§ Ÿöd¤ ºÇüõPᣑ3h‘×ϵcZƱ¨„ÓñìÍyÁÉ?ºæî¹Ÿ>é<«Äýq·ÝÖÁ5wϺÐkŒÌ¸æ>]8 |(#;¸fõQ0ÜOÏ-aÕjtbzZØLIÇŽëÍïëÛåHŒ°] ¿AÜö§ÞØbsÕo´…1$¹vél§û£‚¶PGƒD ¶ÐΡ+[ˆÅ¨¾ß€?]©MUÏůk¢Æ˜¿åU,ÝÇñžö UnæÄãVúd½¬!_L¬ã ³.»€× áõ®ï´V~©Ë?'=°>¯²>¾ž¿EÚC çM%»G>sO?­ï›Uáöqﳑ˜ž)ö˜QC ¡µ6ÈC;½ê¹ÿ¹nê;G £>û ~&UU\¬ê¸xýÂ;ñfað†¨eÖ=Lv­ªiÇ^wÍq¡'e_žeÒh):çzþ$KÙP-"˜Gûଣ¼*éä¼{‰yߥ¨Jkm›jyœ 夵m‰#Ê,¿ï§Vž÷®;¬éÉ`«[°Vêkúamæ¯Xƒ[ÏI<‚x`o$1 ëe¡óÁ˜@$z¹ø`íž, ·~@ P¦ KQ,P°cQ_Œ,Pcà×èöS£šSO*¬যҴA8‡§J qüA&ä¡mgkÅfˆúñ)^•R¶±ŒìV­9[KI³)µÛLùKˆ ´«>æ[ËFMƒ>þ~OaÍo?3cúô®nO«³UîúгÑÛ¹¹K×ÔÇkº¢Þg…{Ñð¾odN茨@ çbM×T,¨>[¹Ÿäö²“Ÿ¨°1…P:êæü=à×rj°câÙP¼Ö°çξ±|e7¥¬1h žç¯Qx },Áð:7æp¦.ÿnü0ûú?ųUzš_†Ó,B—5Òi«;R|3nÊsE¯7>Ìœ—›õŽ×¬¾k%µ«"·Ž´Ö ï²Hóh÷”?1bøòÙiEV1±¯1ê„ûsÉÊ4Bõˆ‡Of07Û¶k(7äiJšöÖœ¬,äÝ-}{ëM0ê)" €ŒÝ°qEÙs¹Å&îÑ{Óð1kRöiŸsÐZçB‰…ÑzÕã £Ê˸O”ö£¦L;ÜRÖîN<”ê ù¡Üª=!3ô\tAD?ê,›ìªpŒ Šî4±¦4=å­Õ:„”Õ¹¡‰ßPˆÎˆñok. N‡ðúœú$I†²Á+Ò#ê»NvðªüåTš¶Õ÷Ýëgì×j°ŽR+½n:ú:>†©‘Ø@Gœî-£!kX‘’Q¾Ýäõ@ªÔp~ƒ!V5á/­ÃݨwÔw7yrx]¨×>_3&à`ʼn3p ( EF ÀJa´Ô5®÷¬ÔzŽÊ³F¥æºé+ö#å#êJ »9ÿHQsÜQØt AË©ø²ÖßrŒ”cD&%òIw#k­ÀÚùÆ4ý EcY›óàß½ä禕˜î…imJÞ9UÈfUct1ÌÓVºØêY¸;¸ÂÜ„Ùø-vi’:Ô4Å10[ž9eÚ`Ðþ/¤o–`þþk6lFOè™hhŽ!Q¹;뛄®Ám㎷–­è‹¶¡—E¤<ÇãÛa;íôçñ8ˆb÷±ÇÓª6 ¨ñÔö08¾†a3ß(½°ÕiJ/”øî)¾”g* c0`(æWî‰\ÿÁv¾C^®K¬xøØ–} Dúe”ØGÀl ™;ßO37Ì+/µ(?¦ Íи³W!Gü‰y…Óôà Ùu¿ÿÁ à]‡!ÿdoQ’œÑ…è×.†×}¬×ç„ÎÅšÀiy'ÄÃ@Ä&&¢ñØ®¨—!±²kÄÅõs¨j(’®9S„ög%Ɖa§¯×¼9¯Á¸¬ï°¤q%½‘ÂÖ~ûë$ôöôÀ»ûd¤"\·l-[6ÔKsG}j˜6Ï’;S~Ðý5×ÞÒÔH”ÑØ”F%J,êȸïé䩃ÖõõíX½Î5š PÌÇ.mùŽ:J^Dºb•Ø×ÀþÍÑܰ`h00&Àª ü€«c0…F?ž¨6­ÿâŸô+|¢*NÅþ÷zâñê¨^HÕõ!êD{û ‹µNƒ7˜*q‹këù¦›„ cµ]¿=Ý/î‡Â3"^›÷’ml½}q•«û«Î[=Í>®S—c§.Ú©Š×Å 5"±»7"ÝÑ%zŽ®OÍáOSñЃý287jÊô}1=ñëÍëJhŠÕ/…Á¬oÉncÓßó/a¿TÝãõçüóÏKÑŠæ?ùñÚ²<§qé}Ž)W¿zï[âšF°°æÚ¤Zþs+ŒÊ¹ŽÈ h?*:ÈqÁ욇x匇y¾nðº§k|Sæ':ÓTÚb3£ˆ#‘XOIÍwíjIÏè„–o&!lþKߥAƒ;ÿ‰S0zLߢµ6Ø€a–7æä~qí74 ×}¬g¼ 'c7´G0àTÍèráïùØlfẇB5 òÞ€M*jÄÅ}ݰμj {²«Ë×]sõtý!k³5P—`Dwu€`7ogë¶îUŽ–.¼rjÎÌa¯Ió\ÓFB•MÙ”˜ÑcEd¨ñ}l? uÖ9xvòÊOd|O/ÊN ‘ÎkÜëÒ?ÎRFríIØ’ÿ¡hSÅk8à&ÐN °âÔNž£…€Ô^ʺšfР!6«¾r=<'ÿ(a[û¡R[›âë´$ŠBδ¿èŠ“;8n|¾Ÿ]·˜1±Âi-Iñ׬¥Î¤ð Ñù55Vé‹òºß´JK±èXŠïñí¥èñ­CØ_×÷Ê ÚÁ¸fOø {íü!vèÛ€^ÓÃÑP¢izg þ›ü½:‘B庒O£ý´¨:–ï\…¥jš¿U# q+ÈMúÖ}Qš -»Mìï= y³~kEšüŽÖ³mÝuì6ÐI@ù©Î<õ‡Èßit$4¶Ÿúù¹{YY‰ÑÏ -bÄ©Jj¬Ÿ‚µsâü:ªñáu|UvD#Ãp½ÙêÓW3вõažãu…¹ÙD:@ƒ—ºFœ`¿z„:Ò-֛ɟ#Ÿ5×=MóBgÇcxO/Å7¦ŽCYúîÆ åg¢Üø½¾ÊHùZºlˆ”§%î±ûªÓY÷î¿TVEÊÐÇ×çùb£ˆ:Ž¥Mxp}*Õº¶•iz‘á~^hâw±¢©×[†u'ŒNb˜@»'€Ž>˜ …¡¬§ÑEãMõDE_kÀK‰æìÓ&ÇLAwݎ׎smÚgÖ2ƒ>Îz4’hL¬ÙN¼ªÚŽOÿfÉPçÒwfªÍ[è•0JV˜æùee6¦H)· ã§Ò*u”Dô„åuK½Ñc1*ëm2nÊ? Ônh$¼àÝô!Íßä•”¥ãàþhǽ&b_ß„]¯È&Íí·µÿ8Ó2m³ò²ÚگɈà´A¹ æ`;òÙyG±Cäâ¦Všb!lÊÙE>Y©4‘{Ó´hJ_Ô#µw—g‘wÖaÐàÜ`pQ òÛùÈO:l›þ¤×AcÂðúõŸŽ§FU»Ga\óVª£hÛûš†á;ÛùÎ8©O¢=o¬Þ?ç]Âè`]ïR8% ÂòÂMë+NGY‡o ɇ£¥]]2%IÙP—x 5§ÑOt"MDyˆ2Oþ­ùÒÆE °ê#ÉOÀ^|jâ,Ø9öœ7uò{ÑìoÍ,Õ×u*ÕIðï´Ì)!ò&À"°â„oÛ7Í'®§i¨èoÂw`sÓ üuíêG%õu?ý°Âº¨ÑèUäŽ^¤¡×ùÖ° ßÿØ2Ý© ˜õ5%÷¢Ø­²ÔZ8rÚ´ZÓr¨qES8ê ³)ÍÓÓåC¨HM¡¯ÐŸCJ¦O=>ºy‚÷ÞçôA\¥Œyd†õ1/xŸÑ5¶p YÊiH¡;Óô\{ÎÎPRü•ùáhôÒT-«£Öñ ÷y çŸÈáŸÍ®¦é7Qš£a=ÖcEÚ¡u/‘fíá[ôÇSî“‘3}QÁ92‚é-ña_Í/œ4$Å9²C!30s'¼OÓêJgût%ÿƒwºÇjcÅäBÚóÑÙãÇ—yÝ4& ¯?1\‡ó£©h­VõAeµjít,nÚ¡Ú0ÊâÑeýF#Dö½o,aë~§áëëåL­õ>oŠkMÓÂé€ÏÐwåêò“v4DÙø%XŸeަ‘Ñ0Èýuٯ˼¥Ë†ºäJ”9½_èT;¶ØÈû˜>„Lå-ö¹®Þ‘pkq`EÜ4”‘~eÿ®ÉŽ[LêÄÚ§ÉŽ¶¸ƒä‰ÛvÀÚ8_G ÄE€¦§¡â:Þ¢EãJ]UòWéy˜v²_j_ŽF@'üö´ k7œSQ©=+|iWyŸ¡—Íñ妨\ŠÍEƒçk|œ¥¼Ÿƒ nÜ&|=…Âë.%]›l”ZÂþéæfë(p¯£y%´åô@lÇ}L¥T4âr×]"®éט¾ñ"ü>…T L•z5ÚˆF&†Uy¦½}†éx?¢EçLårä5ÊO¹RoöÕ»9RN( UÓòÔ °SÖ¹«ÿ¥H;àý4”ÉãaŽï7©÷ëÛf·–[ ëq,l¡®äuHËà¸êZ/÷£®ós'ó™à=Ñ6ÄÛˆ/…ÿÅAà~`¹¹¡'îk)°µÂic½õCÞ/Vフô9Y(c¹acÐGùØ%-;ô7h˱#äŒS³£¤WÓÂè-[Z,—|Yö1Jí±è)ä©"ŒÍî¡Tå04ñ\q òIÔ†”ãû,Ûƒ¼9öH[¿/RÂÆ†é_÷:>xm z'Æ"ßía^K|(tÜôF¹@FNôq}q^d䌕b´Vþ‰òd?|øv(Þ ôMh×D™ÖÝ·øLçÞ4é'|Zà#„w >ƽùà>X¢ û ôúY 1íS¾ä¹¸þ˜:…¼Ïc¹né²!a§?êgä<±›¢¢Œµ}P÷ ˆBéƒoâéÒ7Ÿ®¨wô°¯–õ¿";´ÊU¸ý>ÝQ¤*WáÔ¬û1Ú…)«ý…¹& þÔÚ´¥¡~³;&ÐÔè±j â80Æ @G_§=4!ç¯߰ln´G£"¹ ”Á¨Ö>Ö¥ÈÀ'Ϫë{*$j?ßñ{í|.ç¡"Äh‡š…ÃÆ£‘´ 7}}ÛmÞº³ëTÿ^`ƒ€©°‹å=´#˜¸ kÑ,: fߺ›'P8‰?ôûQ±ûé§Éšßnª[ŠÇÐ8úvöEÉ–WœM \ôTgöõï|r4%“>¢ …éGÇ/(„ÑÖ–¥bG·ê°„æ46¶Ü×å|¤VjYh4ƒ¥ –óÑ?{ƒ×Õ‚PÎ$M—gÂì'ü°ƒ¶&w¤"¾*ŸöÚm/׫Í%PTU?4à°Æ Û”âEú‘Œ4¥MLŽ‚BúÂð@>qKèAùFóù0â)I‘‚MJn€²>rœ††ûD(Ácð^|P—¤#Ë 3}#í›Âܬ#í66ŒHÿêº_05ðæŽÆslfu$ʾy #arSš/å )R êr ;*5]»^ø%¦d >}#ʦ[àÏ…¸ÿÈ>ÖmTùî°:^rŸô_Š«(ð¾ç‘üX?¶W¤»¾NQGòŽg÷G>å>ʆXälˆÊ‹`x"ýPÆ ¦ðçU0»[jÚ°‚ÜÀ![Sš(\Ú éòºFÿ˜>°N× =(þ5ð "Š›kvCCåewL ¹  ¬åƒ 0úÐÔ8ÿª¿w6t±©08é×úìºÏГü7*îhÐáÛÎX¨üºÎM“~pÍ\ûõéz¥b°­ô];«ßgNœ¸©>û-ùŒÖ^”nªìaIß6Øi°ÔôuÃîwµ5·„Œ4ÅÌ^[º£­[zZ—ßGNÕre¢é™åËvJVÑ–¿®y{:cÔ-­¦ùhÄvô§OŽÆ#×;v)/Žk.>4E¬Ü*ßAè¾¢z§05B æƒ¾!öæ×? Ô,ÙÁؾûŠÂÌL#^‘)¯–þ]¹s×NòÇæ.hj.¾µ=¾µº&Z¹H›à[p´1N𝣝o2(Ö²!ÞôaûL€ ´=¬8µ½4å%°â$ºa÷9 Ò„§$‹E`1€â„¹ošß?è®àÄUÑ‘rQblþ½ÜØe1§YÖÜE“ƒÍ’“ÀˆìÐ0 ñ>ŒüñòÇEÉ)%KŘˆOÕ‹ÛfL€ ´i˜:D+ehwÿëRš@¹¹yï0ùi›‘‹›m$bI‘K•ôÝ·ì€ 0&¤XqJÒ„a±˜`-AÀYã ÄÛ˜ª×#OS#wK¤-½3ùçYøÈ&¦òØõkAKÈÉa&'ä™!†¹î^¬ÛÚëÉž_;ùãä””¥bL€ ÄOÀ¿vÁ˜`mš@º¼J–©ÿAyÊ1ŒŠ4†±¶OþŒÍ¶-2V À8BgLÁú[Çÿ«`jÖmšG.&È#ïaxiF,û…ÈØ!ôò˜³%&À˜@+!À#N­$¡XÌÖE=ñ ç~Ië’š¥ea…ÙÙ«ûúßM“úùØr¦î}†'•Xû¾­SósEÿ^ BY33&&àìv¹ õ“P²o”þžû8;„2&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ $?=ùEd ™`[Œ»í¶{pøÀcÎ8µò½×^3¶<á+& ÄðìЇuŒúäí7éÛS1øh«\Ÿ’Ò{ÿCŽíññ;‹7Ää¨Yº.8»ÛÞGµßÒ·ßø-ž¨5Ô]°û¼xº[ÿ”™'nòú=<{¼2[Âü^ žëëÛ>+¼¢Üµ‹Ÿyy}D©Z€ðNExëáæ@ǽ’¹…yÇFççok–ª«m¥ŽÃó‡àû¡ô‹ ‚¥nXÞ3MÙ*6ò.R\,”Øœ:ÂíwàœGœ]»˜"wž*;ù3Ä&ó6˜ ÿ{ ^_jBåÌå,víÒ¹JVRpÇφÛwý¾ÔkM³¢’ž×wÙÅÇ`¤éHMÓ2çæÖPš¢:‹9OҸã‹|-Œµ3¾'‚_oð\¦K-0/7ëH¡Gr¶‘„P{âY~ßI©_+•µ»­Ì©d¿Â0^CúH¿?‘¶Ç:ò1¾0”½7Þ…gÏH%WäF“}ï»w€÷Ž B9§yÝeæço-ÿÀ¿§l[œ­kúåós'×X{6"8meXÏ]L]05ð„7¾fL€ $´ø`L€ $'`pQ ‰{AºÁàЭ6ˆÉ¾2Ö¾/l†º\¨iòR©‰ç±øÿ¦"#ïñˆXþÓâ[Ù×hR͇½IxÞJÔ"L5ºSoFƒï)µÑh\®ÂýìÌ@þ9µüPÖ­¶Th¤ŠûÑø» ýÏa÷¬›ÉóÚ‘††ëb48ÿFÁ{-~&“ÝLQ¹43'ÿ¯]\ÿÃj3…%òÑø]»Ã¡D¼‡ß˜õ›ÌY^»™Ù¡ ß˰§#¾×Áî«x>¼È,zÁk/?ýRVÂÁœ¦>~Žé!ú!ÜOÉ/£Ä~T (™R¾…óR½ˆß®ÒÒ2úAÊ*û£ Œ•ÀŸ<ø7òêÊOŒ ÌØµÚ•”=îéb³ñ?¤9&ÃÞ4¸ëÏ_ÈÝÁµK¨2{)”‡ƒ¡€…ÔÆ Þ«M£ò}(¯ðf+‡¥€2^vÜ;Ý»›"®<Gǘã›QPàG¾~²ÈÏÐk®‡šnÚö›SòŽñÆ!3'ïbä7`f#n›ëIù”º\mIÝEŽ}©î¤´E:†; O•¢åz· év±v èLSqÞËs»ºò®ü QúÛ¶u¥ãÆóÇ6¬áÙ®iiÚÛc¾dL€ $%qJÊda¡˜ «ÅŠ=ÑPOÅú“±)6¿G£^íŽQœÝ=£8Oäl)ë9(>§„²¨±ïèQìów078z3 qþJ…?ÚRŒLóû‡Üœ¸ŠÌÇÞyç£k6¬SÂ:·O’™çØ¥£¯ÓÀÛƒãÜ51O „L(oãFgÝ1?xãšfÍê¸q}Å,%ä£ B¹nƒÁ7*6—,ÁˆÕíPîFCmÕêc×”ŽÚ 9YY4ÒDÇ£èÝï ä~@®ÔŠ5g¡ýsƒÙŸWÉv/òÙ϶gàÞtFÆŒµ3‘vÏ`äè\O~º§ÊÍÏØô¢]“êKyÎÍßUÏ"O ¯å”™›)ÏW+˜ë7çÀ¼ƒ.RˆtPoþÉ=æC½ÞÛ 3ŒìÊïÊÊj³SC#9ñ=`­—:ø`L€ $)Kþƒ$ÃÂêØ$TçaÄá Òä8›ÊB/½üA ûÂþHõ’«4‘ùÝ¡œŸ0ñ3~x•w]sMü}ZMÿîÃ7/z”&ÇÄï“s©qiG‘Á¦M•‡â¾œ»ÃNƒÁcLø;âGÝ<}ï3ÄúÒä<‚^õ ìv¦ÝàÈ òÏM‡CAê¯éíòªƒ†(Ø”öac\nÝÏšö·Üffˆÿ7ø]•˜¹Ó–'ñ_…ÓGþ¦_¤kåÓôšÍ¿)û¤ÿŸ>䚃Ãé0{³ T­49Rtí!×N½g)þ4NLžBÀ±ÄÁ•/»P§/BÚ}îQšçˆ¥ó~¤@“2×8ù ûí¥É±ïŸÂPÎðc òç°n¥øØXµ¶©Æ£zo4ß=ÈÿÛÙ+Îpí æí ³꺨¥„¹vøÌ˜H&<â”L©Á²0&Pƒ€Tv1`D¦ouÜ {‘#B®mŒL¨Ý›ªsQÄ=Úqb£Ê ×û ü¸ïâ5s®¥ü5Ò,½cÊ÷Ö—“_gJ ¡³îO]iWjÚ ……\–i’l?ºÏÑ8ýͽ®>Kío¬»ÂL!3¬—Lg8Ÿ‰uNÓèšLc#eK‘ªÂ“ÙVý¬²õ$}úyÊ´0å±â[Œ~½„©ww¢ýjTËÃŒœé{Ha^ ÑiÙöxÔ Rn )ÑÆ¯ytéè¯!#)Ððwâ\e\e­õ0)]Ò~«ü«¤¦‡Qî Äc´1QyJÄG²XìBÞÁP’: ¿öFJ3)Кµ®„ú•˜z7SO‘ÏRVxí5øZJ(4vÁˆP¨ßü@ èÚ¼¼Þ¥¥ê8L£¯Ÿ…¹“¾ÀH懘~zÜ:igš6¦éÉ¿¶ÓvŽœV¯÷lŸ 0&Ð,jUZÍ*˜ˆ…€Ïç(X¶©ðÔíZ)#êC%h!¼Ó3ï>G£¼Ì½Ž8›÷uÞ¢A_«5*+¡ i_³±/dªY[6=,/ÖêÔ ÎKë ´êFa:Ò%t‹;±QÀLïè« —,ˆðc«~Fدq[Ìú›^ìYÏFä:C1{ÓÅÞ9mZ÷=7Xëu²åhÏï9Ÿ‚¢8QóIl!7x¬U_κá†XdLÃ<·ŠjGU·W[~‡ÆúPà-Z´Hô#ê}yŠÜÇ'¨XìBÉëÞŸxÓ×¹Öä$,l»ÂL“Î47(XéäiŠðƒCãT½ó"¤S…]./$ßÊÊÕÈÓ¦–®?Úßáv¸Ÿ4*8s;(}¸…¿XGèº×Ù `L ¹ðˆSs‘æp˜ˆ›À±» ùíõå+~C#ùj¬±¹…¦‹Õë‰TŸ =剴(Õ4›¦'Þã7v>èã¹u.ËJÕ‘tê:c à´7…]*àöo2«>LÇ –µªÍb¼Ð”¶cUBø´÷ç³?ÑY£¬ÑbÇZÓøjÙ4ðùñ-+Y¢ …ÍšîÀ櫚þUïAÓô G}„†ýQÑ,®5VôAÚ¥¢áMŠÖ–Ã*¿éÑðŽ;¤3<;‘6ÝØâ)MY¬<Þ{_׵ݿ×Bp[‰i7ou½V ä©H¹‘´ã £³C#ŸyïeŠXŠÑ´ Ë´kç³j‹áµ]•–µÕô­rB›DìÝ„ ‡ L´…Õ^ÕqQWþqÖJñˆ-ÄùJYçBÖ¯äNþ¸oؘ 0&t^q%]TX &ÀÚ"íô¬‹ì¼³±Å÷$lµ¼ÿˆœ¼[BûJ“v7,~ÂæZ:va£ÅD×n©·b÷º3”Yö2Öà\ç󧬴mc/Û2碑ömý°{À(­ÈX±½Ù§kTš•gØB]Æîæ';Êm.ÝÌ®@„\kS¤zÌ))¶4®ÆöççbZÝ% ™®D;öeòr±˜iü­ÔüâßPJ•i „vúZ[oœ¡ô=ƒa²éØðt»“öNÇò½«è^Zl߆i“¦§©o+ ­£mÙÇC~Ú pb´0zú³+6Ck 8]ù–èÒ¿ÖT¶O—"=þ¢Ñ‘†>¿>Ã4¬Ë6n¨xrT0o¢&|k Ó:T){*ü­kúeuP4r™9%od\‰ŠO‡g‡f ]~ìSÚl™½3† ±y„þ4}s¨…òTµ¬ÎE'žÜl\‚½'aˆ[…W쀼ÔE lìÑ:$ŒÝQ×ñPt6J¿|ZX²+òÇÈcoQ|NØcÇ%¯-ÿn>*}=±ë5¿ßòn„â„çùÓ×xßá*‚âv7òôïÇí¹ã+žçÑ.£åŸÙÁñë»R_ •ùòé älŒÂ`L õà§Ö“V,)h—è;@ØÎúl¬Žžï>hì Ûü ¿—±¹ÀõPR†¸`n¹ñÆ’éòtÜÿ„†Þ3†Q±Ú²í§1Ú󱿣v8íbçÚmª³Z>¾õ³e[ïT•EPnG£ð|€¶Æ(ЂÜl|JÄ +”üÁT•ßbšßùh°ŸW›ýpCå!¥á_…øžcêC˰¿€¿÷aÔ~RiåŠÿðé)?l4°Hm2Ö—¥W ÑÞÚ»CY}³´ÔþÝ4Ì ¨ÜŠ8ÝÒ×—uk´P(í0Åì|0ùÏSœq†íåÏGš¼ÍM,f¤bmÏqµlögaî¾I¤_„°ÂßÚŠGS³ßHó¥ìk´¹ÅaÙ/š¶¹£!P4NC^K#/Z"OQ¸Þ£pÒ¤ ZšÜ#>_ùöªX¸¿‚ôïµG×Pt&`Ý}dy<åäÿw \eB!tFçœ-Ø¥v#Òw·J¡~Ƈpë¾JiEò!„G¬þËîÑóOXÒª¦¯—~2M58߇}ã¿L€ 0æ%€$>˜`­‡}OH_½®o±]q0xEŠA0¨´µF^ÿ£öÚyU,½†@ïþ&¸»•zü)¼ßÅôÛ‰ƒWmMAŸ¿-6v°ºV¤.Yi›ò³¤{·Î)kfNœH²5ê –l4¶™“ ÅÇ9hz\I‰ÙÛ'´Ê#vÛau¬léCª©å²©ãLÓ2; Í®Ñpó<6?¿§ªÔýw'×å´9òT]a»æ´¡ÅëË¿$:h¥Çï´Óšúø Îè+E‡Í´VÉuï=N¤‰.z·ä÷>oìu´üC~⽡§MX÷vRcÃ`÷L€ 0æ$ÀŠSsÒæ°˜hSHqÂt£Û r7µ©ˆqd˜@‚Œ†¤‘QuÄǨOP0ì-`L !xª^B°²§L€ 0&À˜€—À¨à´mk–â«>¾ÉOzŸñ5`L 5àÍ!ZC*±ŒL€ 0&ÀZ)lTq»Ýmæ6ؼãS|Hy˜³vª•ƇÅfL ý`Å©ý¦=Çœ 0FPBæSú÷ô†36M@뤿,Êì ±¡ÇOîN“m:Â9&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜@ìdìVnsdδý,Û: > TRõE î»dL€ 0öL@ ±I*Y ¿èšþʼÜÉŸ´\—¶‡Tæ82&Pd(û¦8ƒ÷§Ec„T× %¶—RØBÉ? 8#ÐÍuAas&À˜`õ@åÙ ŠS_Ô/½•šbê—;úùûÝ ^Q^ŸÛÖöŒëÒÖ–b,/`‰" eB§Œ@¶Ú)åËøý[K×^˜7yò߉‚Éþ2&À˜@û"0rÚ´îv©}šRj~'£ƒîW^Êyµ-ິ-¤"Ç 0¦&Ð’e“+N9¡ë”-n‘R}‰Ák òo750ö 0&À˜€—@fvèH[ˆ»`öO©‰ s·{Ÿ·¶k®K[[б¼L€ ´æ.ûõ¦Œd¸ W³¥O¯3 sǯlJÿÙ/&À˜`Ñ|òÎâ_ö?î‚¥]º FŸÆïôq>y{ñÑì&»ץɞB,`ÉB ¹Ëþ&q¢)JÉáá“¡ì 0=Sù`L€ 0&Ð| 4ÉÌ@Þ£¨€ÎA5tjk›¶Çuióå‰ 0¶C ¹Ê~­)ÑâUø³€¦ç ÏËYij ªì`L€ ÄKÀ©PÁÝWø-¨ªŸâõ¦Eìs]Ú"Ø9P&ÀÚæ*û›Dq¢Ýóœ °¦©0˜YÚøs˜`L • z•ÛXª—œÝ][I<¸.m% Åb2&”š£ìoʼn¶‡¦÷2o‘”ùˆ…bL€ ´;TQ½ä|£µÄžëÒÖ’R,'`IJ Ñe£'ú _ø;MòßIÊÅbL€ 0vHŠÓ¿©~rê©$?×¥Iž@,`­†@"ËþF+N–mD·¥ï4µ¢,(`L€ ´yT/QýDõT²G–ëÒdO!– 0ÖB ‘e£'@ˆ/¶ÿÁ·m-Ù‰ådL€ ´N½„ú ±Ø bÌui+H$‘ 0ä'Ȳßרè+©úÂâÆúÃî™`L€ 45ÔQT?Q=•Ô×¥I<,`­Œ@¢ÊþF+NønSg°ÜÜÊx²¸L€ 0&Ð Ž¢ú‰ê©¤>¸.Mêäaá˜heUö7ÅT½V†’ÅeL€ 0&À˜`L€ ÄG€§øx±í"@_„¾6/¯÷èìPkX«ÐB”’/ØáÙ¡3¹W%Ÿd,`L€ 4–Àð@î’Œœ¼«ëOsºçz©9i·½°=U¯í!á%qÁÛ¶)1˦f䄆a[án$[Fvè/%ÄR¡ÉE r³ïK&y›B–`PiÁ ´›Â/ò##z Šçñº®<jö+‘þ’BZZjÿ®éòÌ‚©ç"Ÿ7ê^ª½•жk”ì˜ 0&ä]Î6u½ÐT7xE@IDAT81j/i«¸ËøDóª7~\/Õ‹‡ÖO€GœêçÃO[À„3:—˜%/ ¥®Dá|—&õÓ|~yˆ’2µÜ¤)¹¾ÅKHÐ4:Sdæ&ÂsÛRóo˜5«c"üf?™`L@`ïûÄ”³Efè—±ÁÛ·5ƉâÕÖ8q|’‡8%OZ°$ÁàýiÅ›Šž‡QŸôï774ñÏãp}‡ç¾Í\b$íLDfmÓGH>ƒfNÙ´¡<~_×ôþ³L€ 0öN 1å순iûX¶Ùæ”&!ë½çBŽb °â”X¾ì{ ÙÅÇ`¤éHMÓ2çæÖPšêôqxNþQÒ¶s |ì‰)£TK}"eR„Ò…©k¹`¨u’’ÚÛV£aoØÿ\jòVšª†ùÚÿÊÎ@@ûá·ZH9·07p»0¦ ^¦¤8©“/} FÄîBxGâ[f~<ÿ@÷ë×ÍNþ¹Ún ïFôAžVÊ9Ú5sÏ4ÏZJµÏæeäåõ¥jRâTȳ2èØS2·0/ð]c.ùñ¨h²¥û ̵pû\_ßöYÁà実u5©~¶•̃òtSFNþ s³>¬Ë®kž,HWÖÚ\L<|!Ž+ð5îE}|Y³"§VÉOÊìáø!(õ®ß—z­iVTºþ¹gòWkg '"®½…Ët©æåf½ãÚá3`L µhh9[_y8";ÜVæTbQa¯¡n0Pÿ©KÿHS‹P܈:äU—Õð)¡s…%¦hšœU›ý°k޶”¼Ã/ýºub,u&Õw(ÿOìë \RdåÝŒúç"!TÏÔ^]{ÝuÍ5®ÿÞóðœP®°Åñ¨/òÖ‡^;tÝ^ä.¹É×KD¦&ÀSõšš(û×4,u€²ì¸=vº7Gd‡†Ie-Fƒýodêkñ @ÉèfŠÊ¥™9ù‡DøñOKˆ[le_ƒ‚{¾ÔÄ$<ï%j¦ÊÝ®lu3„G¤ÔF£‚Z…ûÙ™üsªý²‡ê´ÍfÉÛT¡ô¿Nhbª”b_Û°–Ž ÎôÌ÷¶û`_‹ÝªÝz.PáíŽÛÞdä—²݃P$~ÇíçšÔBôC…õ)=ÏÌ]€°^F…¥ÃÞuˆU”Ë̢èùÖ[Èt9 ç4(+ßÛ¾'\”RŸz®Œµï£ò»ŠãB„y)8=µR7y{Ý^œÝM•ÙK¡ å3(¤6ò­6Ê÷wDiË‘QPà‡¿ïÂd˜=×cíZºiÛofLÉ;f‹M¾bL€ ´.ñ–³±”‡–ÔW ì_äêNªÐéw{oß„ïQ¸öÇïÔ”,1 åên(«‡yͱhöTÔ'ŽÞs‡ds‰úáZdæßŽN´“QÎß çêRš2yÑé9A×e°>¥‰dˆ—¹‰Un®—ˆ‰ À#N‰ Ê~6š*ƒ¡ý8tèPè8õ´ngãúŠYJÈG„ÿrmƒo>Tl.YbÛöí¨D†2½!|H%ûüÝÌ Žv¾A6&ûJ…?ÚRŒLóû‡Üœ¸Šl޽óÎG+ÖlX§„u.nŸ »†ú¢D(O„7¹f#‚Ó^„â´£,7ÀŒ~1s²²þ‚åEèMœŠxü8?7;\QÂ2¤V¬Ù8 Æó B9gWyºJÞRÄë>(u§„²^¬/0(z 33‘ÁÐÕ–©Þ-2V²8µ.7Åæ÷£I±ÃˆÝînï$ì>5"ÿ±¥¬ç¼a–%×@Ií-eÊ®¹¾¯òóiô:nÂØ)°ÕÁ¨Uk‡ƒÞÞ>¿¶ÏÜ`öçUîý …nîÃ#mÕ.ø‚ 0&Ð:Ä[ÎÆR.ÈÍúßðÌH°Õ5©¾”çܺIˆ,š=ñ&*µ£]:ãn»­Cɺ’“ èܳËFçtrë8˜ƒ2}1Õ©ñÖ™J¨AˆÛž}ý» ­tË<Óîzè€ÌCïÞ¿¢mDi?^^ñÈÍõR$m¾o*èæƒ $!)þ§Õ±H¶iSå¡(Øû@± ž°ê#<ÆÄˆÇ4ÔuóôªÐ…T/UW(¸½;”óÂû¿¶TLØ‘SàÇ»¨œú×pMùÿã5£Þ5Ø{Ô)^óÆ^Wþ¹ép(Hý5]ÜïõKuÐ0…ØIû0¯y×λ>/ø»ÜÍAü³3ƒùQGÂÂîÕyˆ÷¥É1žÊÂ(‘üA ûB7TƧc\éÍ‚PµÒäýU‡[êLÌÐpgÔF¯Ô (M˜Å ?ÁºªÂÚ¶n /Lõˆ§®çziëØÙF8Ú~ܱ&Ph\ÿˆ²A‹-Òc H¦šF-»ºr̰ö¦ÆH”‚²ZvÃf浌u˪õþ ÷¯#--óÓj¹¬2o(Q×Ójs[Jg qô Ý‰Mfz˜ï~Ö-¨¶Ã´)MåèÕfh4WEŸz¡Âìjy©-NöòLC…Vk‘ðmãÆ•£"­1ÕÊpGÌÜûÄçZ““0½ã 3M&`GÁZ1`&À˜@B ÄRÎ6¶<œœ¼u%fJXGÓwžÐ¡u>Óñ´1%ŸE%t*™C©9†êÔyÁÉÞÎCO9ç¦Q5{Á<ô ¬]…Ûgiv¶‘žG1_ÆÂËõ,F¹¹^rñ¹I ðˆS“âdÏšŠ€&Ô»–—½öÅ÷—ÁOš¯]ça ý!La—ʰôw ‹¦c†± í‡æMpSZbö75GG”: •ÈoPÌpr,›ÂÔ‰ˆ##xKe”ù·®]Ñ”@m%ÆÌ ŽiïÏfáUƒnLÍ~ Ü d¾U¡^«å‰TŸ@÷XËœ °ÀŠÓ ϳ_pÝÏsï\޼ù–ž¨H¡øn‰%Ö­­Dc¡l~({a¤}¾gL€ ´%[+g›¨<|åø‘Ø´ç`°ë‘"ÕSÄŸñxÆP•ÓV‹¼}q{4¦'T—óM]g¢º»;ûÏämÀ4îÛ2s¦¿W;iÉϱ5^qÊÍõR<ðÙnÌjõ˜Çì’-2°û÷Zˆ÷Jl ~sf`æNõ•Ö³ãr´Í1ºa^\Ëž²/†ó[±cM§–Åø LË:ÝëjDpV/LEÛ ŠÅ{Õæ»ò Õ•¾ÃQm† e”G]…!VCÍ éj[Ÿÿ#°(Q†ÂÆ Mw¤¦ë×Aª¦=þ»Š^8%?„'’‚ç 1#gú©Ú ;ì-®6‡]\Ÿùq]Ûª¬5e~Ò·'ŒÎEWʪ=å &À˜@ë'P_9{y^ï[iY5ëàÁš"g”l$?§õºD-¼>U®T&™£Ì2ÓÖRŸS˜;鋚>mý¬µà:²<¶j¼êèÚ-õVìTx†2Ë^F˜×ùü)+mÛØË¶Ì¹ˆû·}ôÃîqíúüú Ó°.Û¸¡âÉQÁ¼‰šð­1LëP¥ì©°[sJd'žÜl\‚l½ù&¡_tÒwTÿa—Ž^Ë ë/Ÿ™`­@}嬈±< eꘄ2£Òýª;»UgÒ73‚ù(Ã^Šu´ó Ï%ñ¦_}¼â‘›ë¥xɳýX ðˆS¬¤Ø^³(˜šýFš/eL=ec„e¿hÚæR[ˆ(§a$'ÍjAn6¾»¤Fbàä %0Uå·X {>”­ó¼tí7öŒÊ¨\×ô3ð-¨IåFåoøª;M¡ë‡ž·ã ƒYߺþÓt|j$d=жì×­2Lã³UŽôÉó ¦ÔšÊàÓSA÷ ü]¤6ëKŒÒ«É¯‚Pö |sê*<;Ç2Ô‡–aøÝ¥g?©´Z ˜þÖÎ òA {%ÒÞ-7ÞXÒ!]ÒˆÚO$aT¬¶lûiŒì}ìï¨N;ºnhž=Ö'‡‘¨Á†aVaTaûv|kD¿œjıpÒ¤ ZšÜcn_A±zØV+àîLïïúÇg&À˜@["PW9kyè|–Cj7¢,Þ­R¨Ÿñ!Üê©Ò…ÁþD]òv•ÝFj>gšžËN—ú3dŽûoŽ[ïšÓ9Qu&Õ¨«Æ¢\¿šWzÃŒõº.^ñÈÍõR¬´Ù^¼¶t1Çë²Ê>ÖI¼E—è)>šÎ|0D›ŸßSUêþ»ƒkí|ç st~þ¶ØPÁž7yrÍõN^K¸Æ<îëÑè¿¥0Ðh-mk¦”ëÔSV—·´@w‘×/ÅßqSdÍÍ„3:—l4¶™“ø%ò9…Wb–tïÖ9eÍ̉7E>oê{’}­‘×ÿ¨½v^µµïjÑÔ¾ŽB³1JæLݨOÚøãõåß ´ÒãwÚiÍÖü®Ï/~Æê"ÐZê¨Ö"g]œÙ¼qb-ñ½ÀAšèò§÷s YˆD×™•¯.÷±ÊÍõR]Û¶yÒ–©$˜+\ÛNŽ Åixv.6΋aç<†Æ˜@‹h-uTk‘³E“gL€ ÄH Qe*OÕ‹1Ø`L€ 0&À˜`í—+Ní7í9æL€ 0&À˜`L€ ÄH€wÕ‹[c.)üÏJiþäùV“ûˆÏL€ 0&À˜`m”+Nm4a9Z‰#Pšð=|§L€ 0&À˜`í„OÕk' ÍÑdL€ 0&À˜`L áXqj8;vɘ`L€ 0&À˜@;!ÀŠS;IhŽ&`L€ 0&À˜` 'ÀŠSÃÙ±K&À˜`L€ 0&ÀÚ VœÚIBs4™`L€ 0&À˜h8VœÎŽ]2&À˜`L€ 0&ÐN°âÔNš£É˜`L€ 0&À˜@à ðwœÎŽ]2&À˜`L ¹ HH?÷ð^»f|f-I@y§kï½çQë»lµŠÓÈœiûY¶uTRõE©Ñ¹õáoœÄÈ…›¤’ÅB©_uÝ÷ò¼ÜÉŸ4ÎÇÖãÚI˵«~­Z‘jUŠS0xZ‘Q4FÓäxÓ6ûH)Uçô4»[—t™æ÷·»i‡å†a¯ßXª6•–kàÊÌÉ[mšÖí]Ê;Ý5{öørdÐ6£á#.rܸÛÒ6¦mëóé×9鯵ïô>˜@“ˆ(Oò¨<±,{öö)Ûß ^Aå m‡—§i-ÿÛ¶º­Ÿ¿ßÝ-˜ÿeFF°ƒµëóë×AžíÚ{»'"Ùø6I xß'ͧߜ™ú½¼¬üî_–~9ç­·ž.…Ø~¤Hµºvj«Qœ2¹'þn߯ývÜOðÏÁbûÉô´T=IóMsˆå(‹¥åâ‹•Eâã¯~ÜîË•«f”u*»öòÃÎ ½ÒZ3¦õ´iˆÏIeie 4[öÝmp_N ¾dMD Zy2åîuWM ^uïô •'­®’k"6mÅ.OëNÉhùå´?®, å¼Z·Ó„<Ñ®œ8å$-ŲmÔ{ÜîIeö4Q"ß§ÞhŸ†v9|ÿÑ}v2ê¿wßJ«@%JŽ&÷·ÑJÇ~G{9IõÉÛo.¤s"Zã”’ôëÕ­ÓðsŽÖN9lO¹}¯îÂïk5z_"°TûIˆÇ»–» ÚNü¼úÏô’rã_{vDÉgKþ÷Q•ÅÖØà¡Ïwå¤)ã|þ”ûþ9ý«“/˜@BD+O6—Uþk¿#Þüé;o~ˆ@[UYÒuTS$D3ÈÉåi %ÿwÜXR1lÿ£ŽÙ„vÎ1xÑX+¤ÜêWgÝœþ­!éXƶD`Ky2u…¼•sˆ_k*KÚRr44.\ž6œ7ÿ+%n£ö¼Jd=J~kWMŽ“R»…ë½&;KJ[Þ§dZ‡Ž¡a×ÞHïS~ÔF%}$‘ï¼oš#™'‰é!'a¤i uõ9GɣȚ†Z’ûBœˆqóûSó/sý)¹µdL§â yIn’ŸÓ?É3‹×¦ xË4æf^vCÖɈp«©äÚtâl=r\žnQ½6¼ùŸÚ#Ô.ƒD5ð4z¿4MŸÁõ^½ÉÂ[)ïû”Þ©ËMg_‘y*¢âUž’>fI«8Ñ‚H¿ßïö½»‰KÏ8i"o½Þh§œ2¬#½_\ïy±ðu[$à¶O{õÝ~ö€;oƒ8¦âGëo’¾^IV5ÚEÆÂ‚È NX³•ØnÏc ’=cR~¤'•ä%¹9ýãOwvÁA€Ê“ O:H·•êÓo¯Ç"ŒVQÉ%‚E+ñ“ËÓ&L(7ÿS»„Ú'ðº)ÛOÔ9¬÷úçcéýâz¯ ޽JJnû f{|ê©™’:#RðKú©àMùâ7Uâ8m½¹ûŽÛ+šÉGà ?âØ±sçð%™3¦“î‘^œ$/§ÃÓ]2DpË“ÔÆÀÿVQÉ%‚C+ð“ËÓ$’›ÿ©}ç(¹©iic¹ÞK@±—IIÀ}Ÿºn»Í•°#~ÉÞ¹ïpLFÅI»øš‰Ð÷ °å8þ|4’@ÇžÇÿßÁ«d͘NÅAòUÉÙ“Ó¿‘ ÏΙ@Ð{©”ê}öe™Â{uJã&ð’ËÓ&€Í ÊÿÔ>¡v ž7EÊQrÏ>ö@z¯¸Þ‹FÍÚ*p~—=Ž8ùLzŸh­SÒwÈ5ÅKß”éé þÔ”“è#oøNSSúÝný"ŽÄs›^½„d̘ÞÞÑ4’“Ó¿ÝfWŽx’pË“NݺžQ“¾’Krœ‰ËÓDP­òÓÍÿÔNQSŒ:9é•Þ¹ó \ï%0áØë¤$à¾O=ûm4¤YQ´¿)Þ+x“˜#)'DuPçô4·ML¬Û™¯Ä±s‡T¥ù|õd̘nEO/L’“äåôog•£Û*8å Êg©é!pÒWr­jÓ ÉåiÓò¬á››ÿa8¿¦hàQ;Lš6Û= ÁG»"à¶Oý)©4RâíØO6ý¤:]’M°ªDöéÖ%=Ùd«†Ö/ºuí(u¿¯W’fÌpº‡{¯ÓHN’·5rn 2WTâÛŸW'½¨ßýò»(«¨Œ*ç×?‰·?ý.ê36L<*Ÿ¥.û $wĉËëÄc5.Oc%Õ@{NûDsò?)NÍûäÞ'á_2´{ð½*QRVÑ@2ag•†Õ(÷Éèø¿6ŠßÿÜ¢µµºÑÓ>¥ÑªS’z 8 —L‡S€hBvNóû›¤áüëê¿Dþ}ω¬«Î¶Û¶Áq]³n“0LS`;ÞûÑ’‰§.5ÚB83¦“î^˜T’³©Ò¿%™·tØO-þD|òÍÏ"4úÜ¢|õc±¸÷éÿ‰Ûoø— ¯z7Ç‘=ç ñ×úMNPÔô=Þ£ N:¨Î÷ɲlq×#¯‰ËÎ8\°ÛµD$ÅéëW‹#÷Ý¥Ö36H<z?5á”'I_É%žF҅Ьå)•3¯¼ÿ…Þíþ½»‹óO8H`KíFƒIÖz7œÿegD°± @¹Zì´u:¤¥ˆC÷ÜQœ}̾N=qsÁÓbÇþ½Ä°S­N×5ë6Š)󞣇M¨ú‰¿ø¾Xöýobsi™èÛ³»8íð½Å¾»ÒÀô–£®zi‹šWÔÉ7¨O‘–JƒÛÍwD†ûÜÛŸ ˶Dæ¹Ç6©\7b˜‰Ú§šž°n}âŽäÒ»U>¹*`“å¨.@”PtTÇ ï|&>ûî—¤’)^aГD\£eÌx½jJûÕéO©J©’³)Ã`¿<öýÇ@1ûúæSšÜ Ûk'¸ú,q1*Þ¿7•Šgÿ÷©û¨ÖY×5qÛø‹¢*Mµ,³A‹ÀBv·î­äZD´š@‹”§)ÙW).<ù ±¡¤¬Þw»ZÒ.’¹Þ­j§4EÞ§÷HGóÒ®E¼ËâŽÿ¾*Š×®£†+òÇžOŸ2ï-[)yùCG¦ýv$>_ñ« )÷øôÛ_D‡Ô±Ûà¾â³o|ùƒ¸âÌÃrþtj5¶£Ù´,1ÿñÅb=ê‹æ<¢…{ÅYGˆ«Ï>:!bpÝHXv?µé¢_2é'gËÑ<ÝÍ[ÂÛÚU ÿ¼ô¾Ø¦k'Q¼æoñ%z­‡ôë)N;b/1¨oOG¶gÞúT|úíÏ¢¼ÂC¶ïé4òÞúä[ññ×?‰Ô¿xÉ…',vÜG<øÜ»âô†hš{íÔ_œsìþN¯È²¿‰ÿÁÍž;m/^}ÿK˜¥ˆ£÷ÿGuïøê?׋ÿ¾üø£aÛBêÑÙîiøáß¿ýñ—èµM\‡ˆÁ¯éŽ¤Í˜át¿,ȓɧ87]´¼O”?½ú¡˜:ò'ï0tÞñŠ—Þ[&þÞX"öûÇ qò$Uˆ¡"|îíe"wÔ9Žà¥å"ïžçÄŧ*zvï,îøÏkÎûsðCÄÚ¿7Š;ÿûº8ó¨½vÊ©ÑÎÓœ^hê‰þ¹x­xoùJÇUÚ$CæyLj'ßX*¨3oÌy‚z8Ï?á@±÷.ЃY!>û¶X¹jˆÞÇ^Û{¼·ì{±ä³bÕšu¢S‡4Ú Eœzøžâ°½wï#œ—,‡åN?ì”CDzxå£qœ÷4é+¹ÆÅ±UºnöòÔ§é¢ÿvÛ8¿ÒòJñ(F.Ê+ §>üý¯ ⿨wF}×½sº8ýȽÅþU£È÷=ý¶è”ž*†žH¾ §¼yþeNÙôâ’eµêÝ=P§Öõ>S¹öÞ²âˆ}þ!EùFõ/• ;ŠNcx®¢K;U¶˜âôÙw¿ j—Üpé)Uêí ;e8),4BtÆ‘ûRœžçsñê?ªí|ŽÎä½vî/¨£‹ÊôžÝ:£Œ oê‹Òä–ýÑê]Ó0ëàu'ÍþÏ+‡{ªhæÏ¢W?r:²}>]{Ànâăwwd®«è”Þ¡VÞø¿c÷‹Ú†£ö\´p}åCݺûðËQ¯|Feb‡¾=0wêÄ.P,ÃõY´8Qí¨«nŒ–§Ï;þ€˜ëÆJÃt:1H©-Å´÷t uã„ÿgï;à㪮ô4êÝUî½ËwcŠ ØÔÐK€@Bv“lB–Bø§l:›ì.%aÓØH€@è½÷ÞmY–{‘«l«K£©šÿ÷ÝÑ•žÆ3ÒTi¤yG¿§÷æ•ûî=÷¾{O?èç¬Ì´€xôWÇXŸÚ8þõzÒò]àœUu-‚+Ÿl¼@ ¢b9Ô6ØäÍe[À¥ÈçAüU‚P|4áÀ±rùpíNefp¤&Ã@ ‘À"S“ýôñÃ…çGî+) ®÷Úv¡Üs¢M%‡ÃÅrN'ÔÝ'd}ñ!EÇ}œÈ¹Ðéw˜ H~îšyrñôq2°o>“çÞ[#™©òàÝWÉ`¨¹_üp:­í Ìh½"œrZú«¦¹žá”e>8>+jê1yç#ÿãƒ52wòh¹qÁ Y„6Áæpá^¯‰£oÔ³´aï‹E’Ú«W—l+lâ_„d’ßɬIç3M|ö4lĹÐÒ¬gCÉ!%Làyëðû—>U“ù-WÌj9gÇÄOøtc‰"Ç@ó<ÍoÖø G^°YB$0öE§Ï§$Ôö>ád®bš,V­wXûîÅz;jHùË+„&ôj§¸jà}•5 ê§¿u·½ï™óZéá“òÂkAä÷+¼Ñï‰Æ¾yüñn±ºŒpŸø¹“g«„Lˆfšt“FRŒÀÉsU2°_ ­²mÏQu™B¶#'Ï×#Õï©` È|ýý«Í²u9íí­?dœfy˾ù²™Šöb9+·î—â'@w]ª: Û(´koð76ÑpÞKÚ±&ˆ„CX‹žyk%„ë…j\SX@f‹Œ!P›ÔE?ÿ­þê­ËfmÜ ÿ–Ò£òð}×Ê>s‘ª×¿@s–F <ú©^gž"O¢¿îãâMã¤SdÑ[Ûéž«nj™Hòƒáþ@Ùi¹jþ™ˆIƒ0¤°·b”zçg viX4·HM*çà»AéJÉÁ2æHn\8]•A­¥dtè£.?ªoܵ¸E2ÃûéŒÉñ«·].¹À̓mñoþñ‘š€ú€@"ÄãÀÔýÎ}<1óQD{|u„WÌ™¤*¹Ú›R0þÔ"uŸ¹tGå ŒÕS0ó /!Z~„Ããµ –ú÷Εɣۦ‰ïä+·\¦ž¥ÔÎ{ŽœTf{ gMT§”•KM½w£Š J:ÓRS t(P o,>p\zçe+¿‘\R-üWãû6!*0~»Q)Ð,$b û¤SæSÑ?}ê 9‹µ0'+Eïw|àØáúø­{®–ñxPÓ´Ú j›)xlü­»Ô*·÷=S sóu3cÎ4ê­qm8Ö!Ëé2¨Å\JM½/èsd³À$±ï¨É£–J›éñ™®ï¾RþAÕ1®€&ˆZjÿ;‚@ëÏPÐ^ú8‘i#ì:pLi ûä 7žç4°oAÀu€Ïùh8ïe6TšÒσ!P`øä ·ÑÆj“.øoomô­w(kã>Мô'ÎÏÉ-ê]oŠW]Ø¥k£þž:4Fvòq¼1Nl~Ì6 &h&@Õ/# ·»aÆó4R+·îƒ4cš\†ßÔÔ[!ÙZ¯>”pïUp¬äÇcjš9ÙÞóvHÄt”0šaï‘Óª”Žk ƒwüLµD™qŠ×¯õÒÝÑ£÷Æñˆ¸¢¥Y¾&ƒb—X<¯ßÂ:eÊÑžyÆå³'Â,u¦pâ~å“MòøsÉß¹S %X&‹ý¥x4k½pj«P‚^ÈFÂÌ– ø«K6)!Ç~hޏs±ºF 4¬_=ýžú­þÅ|†i}Uéï6šÚ­š¨û¥SF;-%.‡àe3,/š°–jsí›2ÔT‰Á#*!<ôMÐPµÁ|ÏŽt"t ~cÝj hÊì uÍçòs˜ÅDT šë;]©¬´™ž~ŽÂæŸõf˜°í€eÁ.Ð>-ftúû ×Œ}l„°lóî#-E1âÝ|¬Ö}£qlCÃéç|÷ îßJG-ôÒzЖŽê½;Ø6ñî@k£~¯±Þú÷­!L¤–®#´¶(ÈÍVÂ|j‚áÑX~çw©j¨íŒGÆ)Ô6„|?Íô£tÍš4Bùu¼;ZªbýIÆ>ݰ“G…üâ Æb!(—t|s߸ëJ|·Œ¼Ïï*MzAãäY°½ú™×L ô, Ä~>ÍHKS>¼F Tþ´°¸Vôƒ$œ6¬wåˆP«µÍ´ò @Q…íA0ßs*LκÄã5L"0’ûPîƒÇ•ì æ9‘òô†§ô°„6q1,d|óûÍð÷¦¶‘÷È<ß;Îÿhýñ½“ã…&v gNPÚ,ßëÖ}ŸqlCÃéç|÷´˜Ø¼ûhËéòÊuœ‡µHC°mÒ÷sï»6êkÆzësÜw´6Θ0L>K‹ÑCÀdoªÒZ lÆwtñq\|¾8è5¾ïKüîr/¯’½˜88àéIà@&p² ó#ý”(1§Ä!êÏd|ÈT³Òa2 ³DÆí#øRQJB ÚñòJ0Qý•‰Ãûkv(ó<ªgwÁô¯ «ñÞÕïïwÝ+\Ð>q®e«®óú[£±Ã‚qY ó=šÜ¬Ø²·Í£ï¬Ü®´¨ß)΄ƒäïï®QŒV››šÐ܃’J:ª~¸¶X}[…½½æþî7žcÙ4Ç oM^”EµÅË6ï¡6ZfÁ&¾O£î{QÖûÓ»ñݺ„&)ôe4!&0¿å˜ 5¤B»´(à¸löy ~' $CíÍ„$‰¡Å—n*…3}ƒ0Èa̰BE`SàÂïºäÐñ6õ]wãø{Žï|[ìÜ6È5ü ^i-ð,æï]ðâœI“íÖì„PjœêC};ƒD¬‚Ћt£éiX¶¹TÑ3ô'íB!›ößÖ÷„ºçx¢©µ#ôaâ\?þ«J*ZšjRèãÔÞ:àï½íÑpþÞk,ƒfáÇŸn,Uïþd}‰2Ý#Äjm¤ûH¾CZN÷– +& ð¨¯wѾù[hñsê¢j´ÿÚóU"íßë«]:p²`ƒ:ø`ä" ËUp6/ì“§Ú¼FÂûæcÿÚµ.@”¼¿½½Z|ôyåkÑœ"9SÕêDQÔ8Ñ©ñ…×ÊOžz]ùU}ù–ÊĈ&ž{o­0®?oÑWoÁĨ´°ÎÇãÀŒÇ:……Üx{ˆ“ûÿíÝ–j]¿`úyéZ.ú9 dZXTyæu4) ºŸpôÔ9ùxý.a˜VQ¹ÁN~öoÈ'Ð@é(GÆ"ñˆ5D“GAà”Ù†’Ñ2>K #ædcèÅ|#šê,\di¾@­íÿ»ï:ØåF”x{Å6˜nT‹0ëæëm|—y$¼QŒßnš·ÅÆ>áq§s÷¬Ûy¾ÃÛTHëî\$ϼ½ ù~^±mAnŸiˆÄ6LÕi{ðÿüÏo‚ðÌ”3ǡkŠ[êë»îÒ‡8~¾gÅèh\·Ô¹»<Mý‹­“?½¾TùqNç¼{|µ0Œjqf4= •°`ßÑŽî4­¾æ¢iúrX{ÒK¬£èý´ÙãߺK­ ¤õø‘YâBßð^X—­þ^Þ çï½Æ2È81WÙGëvb]Ù ¢#éæ…Êúˆu ­Á”ÓÞÚH’¢é/ÏzQÐO¦@pýõ‡GF4¡} D<©~å?~±œ¯øÓ#?ZÈ}@ý: ió¾øÝÿxkҘ᳾}ïÕþ£ôCò6’ßC#¸wc(cr5œ·1šP´OŒfcí坯ûB9~â¹eÇîý¥Ï?ù?â¹ÓØ*°Õb£‡}W¦ùnéwÔƒ†Âîyðá'§M;±«úu0Á4ãHŸq¡ô¹¥S~6"zJ#<ýÖ*î_¨Í¥ä'pNæb:ÅàïP}1h¢`Bdà|R¼çÐÎgý«{PR¼Ì%~Å5ÊoùÑ:¥zÆí|ʵ’þÔ2¡÷·îç]ù=süï>ptó3>r#ªÉ:ÚÒ_]M÷Ç1µ;F:Çx­£ã@ôSGϵwÑ©”#ã­3¾+[¥Ÿ vÐÏë}{4œ¿÷êçôÞßš¤¯uæÞ·ôÝø7/Ë]hÎF¤[2N+¶îQBÐßPÐWÁò„ÅH3B6£ðÂc]×î^¾É8u÷4ëob Á1ÀÒK`2ø4òjqbXZú\™& >0Ìæ›0100×¶]Míî¿Þt©¬‡ÙìJhšÀ…~NÌwhBd0§Èðg>mbÀÄ@c€y]î¸rn×Â|½‰&L tÌuà|ÌÓ‹› ÑÅ@«g_tË5K31`bÀÄ€‰&L ˜01`bÀÄ@Á€É8õ˜®4bbÀÄ€‰&L ˜01`bÀÄ@¬0`2N±Â¬Y®‰Ê!õë¿zÑ(&6L ˜01`bÀÄ@Ì1ð÷wVË«K6…ü¦¢yåÑ0`2N0cž71`b b 0*“š`bÀÄ€‰&:MXs<žÐ×F¦›3y”JîÛ^=™Ãð#2!11`‡HÌ~7[mb ª`4;&½Ü¹¿ Ïh9PV®Hë—ì:x\6•–‰#ÊEŒWù8ö9%啵’Š^W¬’ÁŽܯå=´Œ(=|BæâúágåÄÙja>$®gc‡ËgMTá½™÷\M=’/“«/œª®·bôx ˜§ßÅfM Ä`1útC ²É‘í{ŽÊŠ-{°`9[^¼¥ôˆÌ›:FÖì8 ï¬ØªÎ3¯Ä[Ë·"\ê™1q¸É4µ`Ë<01Ðuxù“õ]÷róÍ&¢„ͻȽ¥Ÿ|ùÕÓï*sq›Ã‰ÐÜ{å¥6ÈØa¤oAN›·ñúÖң¤°\Ÿ^ùdƒÔ ?à…ÓÆÊ'ëKdÇþcJèÇçúäÊå³'WF›Í=¦x·Gv«Ù(‹’C'eÁ¬ 2sâ1¨¯Q2µOS?;¦%`°nm¾HiÞýw\!Ì9a‚‰]‹3ÆËÒM¥R|à¸L3ä¼ÊP³üì;k¤Úb&ל6v¨Ürù,•Dt’kR`ÂðÇŸ ¯Z 4ÉÌ­cm´Ë‡k‹‘¬4]Ï›,sŠF©rOŸ«‘çß_+ÇÊ+ áÊ“»®¾PF5Kÿ)PÙºçˆØì.=¤ŸÜsí|ÙìŒ|º±Dfãù÷Ví¼^nX8šѪ¼²Sòò'Q^¥¢¼ùÓÆÈBhÔ>¼ºd£lßW¦’cÏž4Rn[4[Ú«ƒzÐü×m109‹.¾`œªÿšíûeÏ‘Ó2¤°@™Žß|ùL™0b [!è# ì›/)HªkZ@ܶhŽëî?zZ­q }nwºdÌÐBãíæq‚` í(IF›Í41`b º¨… Dns÷üìÌ6™Éù&&ß#d¦§*SõÿH0™L“Ɔ¹71е s&2&”´Sêî )‹2Kú·[Ê}×_¬LlÉàN§ì>tB¶ï=*w^5OH\>ýæJY -óí >{åf£ÜV‡ýçÞ[#™©0¼J÷ë%/~¸N•sàX9­rûâ¹rß ã}}%+3]•¿„ëæ’CÈÛ6Gõ/¼¿N%½æƒy˜w¦O!ßýü52mü0õ.«Í®Ê|kùÙˆçÈHÝvŬ‚7PÔCæ¿nŒtïšÃF¨u§ÑÖÒžáç»Ã¡,hõ@†ÛúõÊULϧ¦¦˜þº¾JÐß&ã” o6ÛÄ@41@ÿJœIlmá׈6°¥ô°¸\nH‘Ë”ŸS›‹æ&â.|¿·BƒT ó¤%wû­Ó"ø‹ŒÒ_™( îßKJžhsß ¦ƒ&—ί|¤®¹hª2Å]4¯Hù˜PËÓ-Ô!ø\ Ç\h¢hÆ{ìt¥TÀ<Ê’œ¬æe§•öúêùSÚ–-ÓŒ Ãås×ÌWþ',‡P—%WÌ™($v§ŽªˆÜ=‡O«kv„¦`¤\‹º1¼ŒU{uP™ÿº58~hfwâL•œ„¯Ò$ø×úB.˜mZX³Cœéß;WÝãr»”À¦Þj‡©°·ºœöêÐþ+&to Œ‡)ÞÓo¯ãT)×]2Mاg«j£Ò¨é`¼WlÙ+ß~üEeFJ]&ã”8}m¶ÔÄ@Ì0@Éó·î¹ZI€“ ¦Ó>NýzåÉcߺS½÷zH¢)MÖpìϹ™`bÀÄ@|aàJø"Ñ/äõ¥›ÛmùtÃnh†*ä‘ûoS~M=ûÁyOMµ´9gIjýæõ…Œt/éñMDë6Ðk6¥¯q)|­fM!ï¬Ü!/!‚Ùè!­¾$å5ÐDõƒÉ¯MELܯ·ÒPÑ¿‰Q;¿û…k¡Ù²ÉÃO¾¤Š¤¯UêäK4wTc}Ìãî…/ÞxiK…™C¯;\žúá}-×|Œ×}×§{¯›ßr;™°_Ü‹ 8Áæ&$ΟÑ«ýfkM ˜ˆ"Ú '®¯(¾Î,ÊÄ€‰`€Ìâ7ì:¢Öd Uµ ’—“¿dåÏDÍS80jp‹È÷¡!¢yƒ7ìj6ù;^^Ô)•KG›Oý —n,ULÐû«w(‚˜AXÇÚ«Ò‚Q£õñúâ6Õ¢)1£¥•>©L·v HD{uhó°ù£[c –ëŽÉ4uë¡våMSب341`bÀ¸PѯA‹ðwyÎÄ€‰øÆý”hîD&F#gþííÕòà£ÏËpmX4§HÎTÕéËAï3$†&ž{o­0bоz‹ ýâGë•?TnV¦ÊWØ'OÊNŸSŽúipÒÿñßPÇ×]z‚Ëxɘ+/œ"­Û¥òô,˜9NùGé Ýqå\ùë+äÉ>V§‰oÚ¸aëÈ·E—gîM ˜H\ ˜ŒSâö½Ùr1ÁMõn„· &L t üç×o=¯²ßúÜUmÎ1üò#¸Ï†md~ŒÀ  Ü4PËc4‹„ÈyÆßÌ£óó¯ÁÜÉîPÐ_‰ÀÀ Üx^ŸSð¯©Éƒ°åóUo.´MIb1„¾)®‚‰!çãy>Û'?G¾ï:øZºPF¢ùyߨú}æÞÄ€‰¾0'_Œ˜¿M ˜01`bÀÄ€‰€ðešÞÄ_æH?è¼G<ÊgIßgÜÓİ=ÐÚ)ß{½Ë÷>ó·‰&LÆÉÏ`´nŒÇú\({JÀ4ðØø[Ÿ7÷]h÷;[dìk³ï»¾;»æ˜êlŒ›ïë©HKMU¹¡°‚öÔ&vi»¢=Wu·µ/ÚíïÒÎ ñåÝ­¯Bl^Ôo7' T0GOž“$ð«¬ij„]u „r ûÔrb4Ïñ”tåggIïül•Y}8" ÑÁÖ$¦CÄgo×ýNÓ š€0ó<‡+á]Gcšu´€0^œ}Oô>–ôĬ÷îëîçÍ1ÕÝ{Ь¼b€aʹ™ è¹*Q鞘¶?:]Ô9¥€Ni¡OâŸ&¶Œt™ SX“Fõß Í8ñÃ!s´tÓYº¹T9£R˜åJÊ[r†¸¥}µ¿”ú?k‘FÉhª’MÞ[½Srxír$\8s¢¤#4« þŸ6ÏFìw2KLÖj‡­þò­{dŶýR߀„v1èÿžÒ÷z¡ñâ΃¤‚Ur É,ÏTÖˆÕfW‘±ÑÊŽYN— (=˜-’I1s¸äfKï‚_‡}óà‡`é1‚sLEó 5Ë21`b VHtºÇØþe ûj±îÓ(È’l‘d ˜…Ôh6ABÜäö`ÍvË{kvJnv†\:}œ,œ1ôiªò4iÔÖ/2a'»—ç‘̯Ú¥ªÔB)Ïš$Ø»’Ú:½¶¢+ò£Sú8Ë¥Ðq\Þ^¹Dû>¹ëÊÙ2™ÎéÐÊÁiBì0 &§Ó©´‹//Ù¢æÎèÿîØ÷FfÉÚh—-{ŽÊ¶=erøÔ9q‚IÒàNN•¦¤qAØ@ƒ;‰B‡$Ió4JªÇÀ æ¯ë£÷“¢ÑC±}¤ #Ü]µ¯æ˜Ò£ÀÜ›01ÏHtºG·ÿ…ÖÁªÂl$éaƒ…$\*â¹cY·&¬Ñ6‡44:”€Õ¶}rû¢YR4j°¤BjÒ¨^ì'ã¤%ÃK6–ț˶Iƒ%Wöå\$5)}c9[Ê&SVž6Dmù®s2®±XþôÆJ¹þ’©rB»Ò¹Õäì[еÝï.—ZF‡,Û²GÞ_["VÕÿ3:¥ÿ»[ßs‘áf³9eɦùùSœÀŸÝ’-çR†ImV/iHÉ“ÆälAŠÁûŠŒcF“U²Ýµ’窒z˜Æî/Û"o¯Ø&sìò–ËgKvVZ¿0͸1‰a]ƒ]%½$ÿ•’’¬r¸äf)3Ítu¦) 9¦:ìróc€–¿|úùÑ—oŒ¸¬D-@ÏU‰J÷ø¶Ÿ ‘âÞ˜,Qdž±Ýd³aÂÍk¬ªZ«<ýö¹f~‘\6s‚¤¥¥™4*–PŒSËdz¡DÞX¶UÎ¥–Ò¬é”wLôW´ŽÉ¬mɹT&Z·Aû´C¹Ó\1{8{Ót/Z8f9ºß©!±ƒiZºi·|´¾´Kû?úžxÑøQÍÿ¼ø‚ÚÞí–:k£bì®”³ø^ŽåŽ•:K¾ñö É8ÖãYnåi^_…,w½ t‘¦’òûÈiùÖ]W(“¾Ã`ª+—£§*åÈé i°Ú¾'‰4 ‘fì²Ç  c2™DÍL|0‚ 昊yæ£=%Ë*˜;)ì…âéŠ(^WŒüOSd$´Ú‰z®Z’ toû³2Ò•É8†™ í`€Leÿ>ùRY]DÕ»=p9èÓtOZÂÓ¨ Å8Qz^¼ÿ˜¼¹|›"šK²gµ3l:ç™6Ö£¨AäU;¥ÔÆ\È<ÑÄ„È1À~'Ód³Ûe'2Æ´aO\ôgö=½1 ‘“çªä’WžÅf~ˆ'ìœIÐädAêÛæC``ÊÊ«dwö\àl@äáS‚Õ’#3'ƒ‘&3VË^]&·×ß ÷Úp½ÆÒO3rÄ‘œ.Τ4ñ@*–ìiÂæ†) M²\õrº¼AØ ¦¸Til§€h»M ba^`Ž)ŸŽ4&,˜ ÷ÏH.{ë³¥fï¯|²AåY|ƒ;tJfC£”›çÍíõ/þò–üòë·)ó ­¥GÅs#…9IH‚[/»¨çf™)ú( 3‰6j˜9ñ¹Ï ùíÈAýäw/-‘ŸýÛÍ(Ï%+·îÅÝÔÁ#¦"q4!‘iÔ„aœøñÐäè…6*óO‘õûæyÔ4ÕÔÖË[«ŠÅšœ«4‘•½§cÙ÷l? š»P*¼}ß1Ùuè48„Ñ .Kºòëkò$+¦Ä¦$­ÉÆÄ©î(˦I¿ŸûzKžœL…ªú°TZào˜]$•)…ð• ~Š"3•ï®Þð!t:.; $ÉÇD#cΙ2J}KÑøž}LûÍ<61°š¡IˆÄu œÉ kwhƒ”Ùða¼aw½¥Yî¤Qƒd!´P„˜¶î9¢4FmjþÁ5p0'~·Ô*ö= f€~DÎ@蓇@K›ÁtM;ÎíMJK5aÄÀæ{—ètnÿ‹mP¹¿œÈ„Ð1@¼©¨‘·!Ü> ·$å{Óê$*yæç®™§$Ô‘ø&ò˜êŒ1`¾£ûa€QÉr`.«¡ 'SîZžc@IDATªý°}Z~SÃM©¿úRÔÂI_“Ûhvª`žGs> ™vøÀÞú'»Ö÷µœLÀ=W%*Ýcl ÆMB Ã&GDøM&ÞzåeIyE­¢£®˜;IÓÐk)éÔD‚ð(”n†!/éRì=­³A„Š&Ö‹õ[S|HiH\0=`ÝMºß©mj@^¦MûN(üÆcÿG«ï½‹…[iÙNžYÌ  ¥mGk“¡e›)kr¯Á~–œ†i\´<í <ÌÚYL“êa̽á2M¾#„u?—:P6ç,„™á,)«´ËÏ,KÁ@Rîw•ˆcÊ·æoF L€¶iïÑSÜb‡‰]”Á2ŒÚ_™îÑGélU­ì‡ã†«Èf4©=tü¬2Û+9x¢¥2eLs`Cê´±CUê2KWÌ™M×XÒ¿•qjy0Áô\ÅȽ‰H÷èö¯Dû=Ï ÙAü›÷WôéªD¥Q{¼ÆIKÃî™æåYE‘ž?]ž:DzYËåê;vÄ å礹ú¿ºG¯ûݰã6äbÿÛìôÿ¸mg¤}¯™&ús÷à¹÷׋½)Yöab$ÇDµ¨‚–m<‚°¼¶t \×Ügü&DiuƒÅK"Ž©`qcÞ—¸˜:fˆì?ÒüáUé×+·]’Á@3?ýÓÈí–!‹æL­‘Z„¨²xåSøXZd˜)ú*xfzßûíËrüo_¦bÔf±= Œ`ù…ë/–»a ›Šê¿ÿö^K ÿë;ZŽy@‹ž{ÁôÜ}õ¼fSôÖËŒ wý‚ Tšßõî[Ÿ»Jiœ2𙣹þÀ9gxNßÿÔïk-0ô\U †6Þ×=vS$kŸ¿n6¶ŸcŽc´§ÀÅŒƒ†÷´ÒØvv›¼ÑjE­}ÃÅ6f·‰H£öxƉêZ†Uf¸SWRFL“ÛFc“Àc=«êoyoÒ½fEÔ:™<t¿‡Œ¦Gg—xƒ ´WJQÿyõö\u‹æŽÕ4ÉŸ·Úä­½Hà&\8$EvžqKƒÃk¿¨˜pû^/lë‰3•Ð4mPLÓöì‹â~¼ÂE¬Îʘ¬ü¶Þ_[, —<¸°OÐaËÃSß™Ÿ)_šîõ©µ{¤ä¬[þs•UVºÃjf*²ÛÏš"+ŽxM—Ú+$Ü1Õ^™æ5þ0@¦)X Yž? ¶)h¦Éx½'ÄÆvEz¬çªPèžOîÍ—!y„—Ÿ­o’¥˜_~¹ªQœÄ ÏO–ô”$ÙWÑñ<íyÊØ~K24"äƒsÿH§‘“¥ÐE?½ýÇΨPü¬O{`}7rp_¤à8ÓÞmj­ºtÆx„áoèƉx´À—˜ôéªD¥QýÏ^ív]÷ºÈè;.0N5ĶNrrC6„]®…z™ƒ’ugL ºß‰C;Lô°…Òÿßù¨A¾únƒìqûãY’ ±U@"÷·×äHavpŸZ8}ÏIY‡[eÉqÀ<¯8kžÉ4è/†?w"…݇ëvoΠý"Såõ¹éŸµò‹V“,Ìnë@ ª~O_2,Eº0øçÃS~_lž41.œ:Fål âVó–a@ÏU¡Ò=¯•Ú峯ÖÉßv`?9] ßüñþ9™rå¨à5=Ñœ§Œí7‰ºÛ»)Ožyg•lDnBšœŽGnÁŽ`Ôþrù¬IÝד!ô =Eº*QiÔàEDqÑe¡U‚’øVi¼KÜXš¨ä`µžç¼DÓaSŽwÔ–évhs„pÊL¤g4¾ˆ;:/2ªžÓ Íc¡­Ô¸e74{+]rí˜Y<:UÞÜãQ½,Š‘šÜß"åÊý~£MÞ?àÕFýta–”â™!´Ý1)]¾ùA½pñÈNK’gnÌäNdѳ5ívE¨}O¦I‡[߉ðÛeå•r sšiž×–™ êlÊ@Ù[V's°P”wäG阢Ôv/$¯Üò3’ä{gIÆE=´7ŒK“¯‚‘êŸ$;ËÝòÓåV)Ãøc$£!þÆÑCó³dDA²<†þ§`ä{e&Ë’Ïç˪£Nù˜±ö Ô1Õ^Yæ5a€qºƹÊáî©hôHq9|d±]>2UnŸ&`}£æèahίÄ:Hâûûòè:h£`•Áûn˜./•Øå‡—dʲ#.©±5ÉucÓÄŠùí¦‰iòÈJ«,ï@C­yÊ·ý¥mÒ=ÞóÑSg«Õ6vX¡L3T¹ŠP«ô™K§!Q{?EÓíÃú³Ñ^û÷Ê“kæ#¢2Ì ¿ÕAHùpíNå¢A¿¿ñÃ@ K˜]2¸!Z­/Þp‰ Å_zä¤,ß¼GPÑuˆåžø$=EºŠôU"Ò¨=šqâàÑ*[~L¡‚ÖÜ Ì!$åìL`}9 ¹u¤æíÌzu—wé~÷2Oè»0úŸmÜÏËl78‘J£ÿûLŽÐÜêÇK­r$ÿ_•ˆuÈ‘tÆ%}2“ä`”ÎZ›ä› ¦¯j’÷ö;dÖ y‹Ì‰ºöÕõ·¡ô=ÛI­ `l.="Nh+O§ ×E™ûhLΆ„]¬ÐìÒF›¦C%œŽÖ˜¢9(û¸cê‚Bø„,Ζ×!é]Sæ’‘¡ÆØg^¨Q&3ƒs“å©ërdë)·<¶¦QŽƒYß|Ê%SPÆ÷?m*[póZ(c*ÊÌÓ&L t 蹊ß}8@ÚgLo‹¬=î5þæÜLù '¤>dœ¬”3©#U[õRó_@ d55¨ð´$ÍåäO\¶‘Ž©^`ª½2[1<´ýÿkÎ7`ìTAÊûC0â„j0å½!G¦c¬mƒDØéï7¡iÒpº¡Iƺ<²ù¤÷º>ßÞÞœOÚÃŽyÍÄ@ÏÁ€q® •î¹lxªŒUÅÌ¥ezf›]!æ&ÌS>³ÃË8Su+Αq"d€’|tïÑà€–ýd]çÏS‘´_×=’ýX„Üï‡(~C {+iCñÁ–â6”R~J¹YHÞ\+4Ñc¢vºfôsºÊ¿¢åÞ)£‡Ènh™ÖìØßrNÓ€{ËNÉÆ]‡Ôù©c‡¡œÂNeœ¸Z’®JTµG3NU-‘bÁÿs¹“üj ¨šÞ sšÛ% LÔÿ^“­Ìk(1¦Æá³E™òôv›òexp^¦pû¶è º™$vtýƒ~μQa@ã8¤ô‰¿CšI­‡´íWpŽÝmAû)í©he„KϹe|V4l±«™&}.ä}}ÏvyMr¶²íô¨ÜL!¿/ÁÈh²b‘:.#†ôöšÄ]Gɘ¢y tò7Ì tº> a|ßh¾§A3ÚÔ4m=å=»òhð ’.ç¼}cê¼çÌ&L t; ´ÎU¡U1NcnúÁRhÀ¹Ä …Å"ƒ<”žuÉ퓎×4¬€ÙpÄ¥y*ÜöG\„¿Ú›¼³j‡ ÛM?uó‘] í*†HÏÍ΄ÕC+£¤njþGë>)¯ðoÚú\ëyj¨Òy¹3øUtU‚Ò¨=žqâ`b'‡ ôIÐDŒ¯¶€RÖCò,Šè¹&[dœ$ˆ…I ¡OVrK4-u"„¬±þøCx̼Հ¿púÿÉ ÊÇÉPüN¼ÄîHh ¶Ã†›Àãe­ †-:4nP}ObŸÚ×"Á/Á‰¨Œ&Æ@ªÇ!E $%É#ÓGÏ¡ÍᎩJh•hZç ÔF_;¦µÏFä{ÀsÖÖ9‹ÄK¤`Î'‘bÐ|ÞÄ@÷Â@8ë…:¿†Y¹(,fp›­Äùp3J,¯i°·Êô©÷Ñœ§ÂiÈöóÀ>$u^¾¹ô¼+sŠFÊ€¾ùò{ä*cBg†åÚÅ Wn¶ß[ø|WC¸kaW×;ïo•G£´(£$Ä¿»6GV±@þ÷êl µ65O̱(Iî•$”›Ðý1PŠþ< 3©‘.4·ºwZºô‡¶©½°ÐG«¡ÍÀj0~NùÉŠáŽ&8q¹›ÃÝ2Ó=^SŠh½£'•“ï:'ÓëVJ^S\8®?$~0›`nÈyаL7ÇÐç1–†iú—ééÊto;ÌóÁaDz¤Ð†Ñù( 6ÁÄ€‰±ÄÀJÌS‹ao.\æ`ãñÒ#­fyþÞMÓô©…åînTZåv×sy9™RMSÖíQÈÑ9©04TV× Í÷ò°&éDÎôš0r ŒÔOùJ ":Ÿ.ÏÜÇ=š¢…Äá p&Á rñó5rÓKµç9=Ž#¥Q°5ëT³YŽ>Î>m §Ýá™Xኒÿ¯¼[¯ÜïÉ—oÏË’?ln”O·jœ|ñS çý‹m*ÚКÉW‹ˆï=ýÔžçF•9W¤§¥HgyGÅ}ýÉ«sdÚ€Öñôƒ~n$¡_üµ^’›~>¡ÿË+²åዃ¯í§øOi,– ê×HŽÇ*ÓçÈ ^9Èzž¢"êщ·#¾)PtøânXs æ «­òe…øèž<8X§Èw>®o7ç×RøÛí¯pɲ/äËÿ °D8«ö„Só&¢‡X|ÛŒŠ· ~ºº>GþvS®ò×ýï5í é^Üå‰}-²ùËrÕ˜àÃ’1N[ÂyÆøÎXoÙ}‘óÒå»÷^#Œ<¹a×Á–W1Â}žþý³‹å†3Ôù×—(“¾»,úË7/@Xó-÷ÇãA¼â=¸êñ¦zìÌp;Ô¨-`˜Î:{“ „”÷´n¨.B$™pâ^j œyý6„¢Þ~Ú%·LH—"@C8 ënýÃygOyƈ;  Ññ&þ¾*àý{àÓt#òñ0hC­‹~àƒóM±XÐ#ð“z|}£2 Ö”ÏXÿ€•ÁãØÖ;Sìåg$Ç]-õ–‚ö êÚGC{¼ÖØÂóK…Íê+wäªEç_m=Cv‰Ì?`.L‹ rëïjtÎY eLÑiš[ x9S¸‘©¬Cp œ7üÅ Dmd¤Oj09' ÆúûŒyŸ‰p1 Ç›~Þ8Wés¡îšá®Ô‡Zïξß÷Á¼ñs­~3¾÷3üƒÖ+Ÿ&º)͇ÂB?»äC>Å–‡yŠáɃ]÷HÆ‹.#ØwFë¾?¼üiÀ¢Ž•WÈ`¦—¯©]C£]þöÎjÉHO;rOjê­òwœKMeßä–ó¿|ú6ïxeÉÆ6¿;ë×@çHúª³êÍ÷ôxÆ)dµ?Bî” ÿZ-/ (Ä-Ê–-_é¥"©ÑÙ›&6àž)éò‹Ë²”Y×#+Û—Êèç|÷{íÆkÅÓÀät—$§f’ÍøÞÂï¹pÊš;qÜ£÷|ÂC]r+§µ"n‡i·btöwB%nowC­¸ëêdn²xå´ûTHiß 4VJ,†&€ï­Gß»$ ÙÏpñð¹o2&TŒ nt&­÷d"ùmƒLlØ,ÛsDìï4gHŠCuêŒIFêö¢t¥Aýûv»œÃùŸ,ÌT檿¹*&ò BÑ~uV¦õK–Ã0S|u·]Èlj¸tXªÊï±ö¸ ×lâËH’Áº yA®—.ÛN;‘;ˉðïM2a»oC®~0}-sûÔf[¦U—h¿7k†ðL“÷Yh;*®Ã§eóѱö›&’[(–§Â1£"þòòläIjÕŒqÑÿ¢cÖÔÙÅÙØ v,hÕ2ùŸe´ÀÈ4Sf(ÄH0å™÷˜ˆšˆ¢œþ—e§*TþšÊÚ$ ·‚ðƒùihÓfÛê4’Ü¥¥¥JrÙôÎÏ–¢Qƒeø ¾]jrÛ¶¢=÷—ѧ)˜V²»Íyª-¦|™&ãU’Êú‚ÑöØ÷´ù» 1`2N ßW[@ƈ’jüÏ50ͺçõºó¤Ç¼Æ¼lb d PâJ¦)uhv–––.'RdXN|y–KIÎ\iHÎ ¹\ýÀÂái²¹…Šq‚Ç H‘?Â4ñšÑéòøùÝñ*LUrŸÒÈçvÚ¤¼Á#V„a?Uç–:TÞ >÷Z.R®F „7÷Úåk`®å&Éÿ4RÑ7Ðoìº1éH®hU™ë™p˜ aÿ )ÞÚKɦKù †C¹“,R‘2@*rH.´r“6IÎÙmbM'žl^JÔÊ0é:™{&c€ ™%ª±Û²|ëY±m¿Ô7@ËŠÏÉ•„œ?Éa% ôV‹X%£©RRàÓù.¢—1BÙå³&Êå³'(¦Ê¨™ T†yÞÄ€‰á`Àdœ‚Àš?m?¦ÉXT¨Òcã³æ±‰b`Áh^¦äHff†ß䬌¼“MS*¸f¤gÈE£rdbSŽ#^éÙF™Y»\Τ •é#¥. ¦{¯!räGœ*¯Õ'÷æÃTÌ¢ô’P  >_LúK>d˜-Cì0^6­ÎÂ÷]@uM[¶Ep:>^ïV~„•ˆ,w2×ÿ|…UŽ@{u0G²a2P‘ñ±-çR™]¿\&ºJåÛ×-V—¿$¸4‰ûÝ59ÒØ˜"µÈKRQéòÓ™²dG²D#o¤m1Ÿ71ÐUÐ “p—:!//Ù"u`˜ªR ¥;:W‘1ð‚8°Œ^~"Â1RÜéz/vš+j¸¨zàƒz¹ubš| yÑîƒVê.h± ‘pÃj“#9]eL”ÓÛåè©s2rpeîC-ž &L Æ€Ö21¢'Ó!aym‰X-¹²/g†Ô¤ô üp¯)+O¢6F͇ 0|u™ÜtÙtY4§H1O¦ö)Š7‹21`b@LÆ)ŠƒàÇËüˆâ+Ì¢L ´`€ÍôÒ qRŒ“#[œÈïàr# LgÈ‘&ƒy:k‡”D®¥Y Ø»ûíÊ‘"©Ê‚æ(iˆ´éêU0ÅûfzÝf“¯Ï9ßèjDWzþQWŽNú9ùÂûû2Yé_Ù ?"0j ÿO‰&{¯ìvȺãn}.LW¶D 5)äÕENÀq—‰ -ˆTa2N‘bÕ|¾'c@3Mœkì`š–nÚ-­/•s©ƒ¥4kº4Á$¶+€ÌÚh‘'Z·ÉK·*ªEs‹yb@&ªÝŽäíÛËlr© à$ÛÝa¾ÓÄ@Å€É8õØ®íÚ†Mx´øû{¾;åW][‹žýv2N$ò•©^Fò9e©œN$h¬6»ì-«•z0M•)…r0k²X“#‹ŽQ ¯Ü–óÀ$B›ØÝ}Ö-[NºeBc¿Vê×÷8ä‘…Ùrå¨4yÁ$ŒÁÈèdÁ7péçóÆ¿I~‰0ܾÀà+ÿ>'S>ü\¾0bßêcyø«üöšlɃ†‹åýi«-*Lßíñx}šœ.—p#.IÆ“”ú~øƒ-9ìP‘/¾Ìß&:ÊpޱÙí²s_™|´abšJ²guvUÎ{™6Ö£2Ì7—o“ÂÞy2eìP¿¦Îç=ÜÍNI:ƨs°Úû¹íwsp$»Ì@ äð"w3l˜Õ51Ðy0§pM’ëÝ»óäD¿ó5šÜ?Eæ#*ÙV„%ß|ò|Éz¯ËG‹ž(™Sòí¢–¸˜˜ÓŒŠvkÆ) Âü¡)í&¸íêÎÐÁ!Òš¼Z'4å•5²r_%Â¥'Iiö5áåXø÷ê6Í[†üA?ZfU¾KF]ÏߪkÑ8ñKTƒ¡KRYåuð‡cµn™ÿ´·¼ßnh—Á¢õÿ}Òª©=ƒPÿÔÜ÷)É¥ub™×½P+˜­ŒŒÏG 9MÞ:ågÁy&ž$ ã ú÷¹ðír›ŒS†ù‹çþx]¶ü àþ¡Ë¿·Ä*›N†ºí«ãö×ÔGôw¸­ÿ‰ .ŽÛJ†Q±K†¥(Ÿ†gК'ú;1+ù»öK+Evæ^±–É·Ý\Àýö]â5 ܾ’ÃCF¦ÉpºÍ!ÃÞª(¬†³ÑfšXô`ûa$%L“^9^ƉڦxÓ8±žÐqg‚‰.ÿ FΣO}HW#róÏìË™ÕeæyAÍÓ¾Ì)`žÖÈÒÍ»åêùS»M¸òH™¤Bø‰N(H“‘Yn©Ük‘²¶ñw¡Ì<C ŒÒ_.›9ÁdœbˆãÎ,ÚdœÚÁvoäŒùæÜL™fi'Ô୳ɯãePn²Léo‘Ï" %éÈÿ¼"[f!î:ØÿÇR«\5:U–ƒØþý¦Fqb¼a|j·aœÆ?¶s â }£ä¡¢ÝDÍøßìŸär>µç¡)—MùõÎQNWÒÓ -'X/Ah‚û2ÒR­V{ã'ø=jü£Å;’’<¯íyhêϱwOxl×°Øþ?ãLNJú[éC“Í2»0ÜOJ»Wf²,ù|¾¬Bø´Ÿ!ºÛÝÈÑuïÔ øÞ$ÉFøèüßÖFÙqÚu"#ÒÜ£k­ò•™24/Y>„¯Ï/VxÇ@g´™Òà7î‘zDq(ΙU¦éy„?\Ý•1D¢ÁÞ®3’憎# QxŽ0Н38E~‚1ÕcŠþZ…9I² 9©Vclýù†\ùîÇ È]åÕNÿ "^‡(‚w¼Âø-"à ’äõ;òdHŒ«(¢À,ªcÀ«mòú55 /Ó¦}'Tô¼Î *êX/F÷[¾uELRZpQ3C-7š÷ÏüÊÿ%W š”d±Ìß—íz ¡<û§ê6 fÛ{ƒéк¥ôHQ?‹Lî—"yiMÍÁ‘šä¯G°0EÈ8š·žC Ë‘sïVä×{©Ä.?¼$¹ü\ò_~L°ÛkCO¿6¤o¹f>˜xè¾~ÇrðøYùpíN¹qát±Â‘÷“ » &"O'O½ºTÆ 3'—egdÞÔÑÐð:eKéÙºçhOGW·hŸÉ8µÓM·ŒO—òzL‡êä÷È[3µÐ»š¨~ðiƒ’S¼ I?ˆßKA`SÛÔ'+Y…MfÑÇjšd6B3w@<±unOÓ¨ïOYçd·ãORÒ:;\I¿•dy6e¨åy÷Ѧïx’\?ØöIÿVôë]÷»ÜM?ßûÝ©Wð>ÈPå¹"%+ùr‹;-Åá°­†fê;¿;¦[èª#øÁf„Øž‚EéûèÛ*äç"0(Âë­ra©¿sa¦Ü?+Kþí]ož¢Á`¨pq–ünc#ˆâd•vu™9b§±¢X4à`eçÁS*r^ƒ%üüMª¡>ÿþ‚`= R=v™`Ý*Ùˆl1~på+FÍÞbÑÖŸ_– Ÿ3ülƒU®EôÁŰ”!ÀÛars ghÆé¦ iÊÌW³sÔrÿv£Múcnù·YȯÛq‹ö›ev_ ´h›vÜÿÉÃ'ÎÂÇ aù³†Äu£ÊS‡H/k¹ªï£4mæ7ÞpûËKññ=“ÄÓ4³I<3“š<3ë’<¤zTTY9ËJ)Ù”þêÛÖÒ“dLÔ­ï†ÅqT!мŗd¦Ðœ=U&‚i{gŸC–€2¡-jžÿXy¥ ìW ï®Üÿco ¦lDÂ5BzZŠäç0À0<#èÓ(Dy…`Z–l,‘1C åÊy“e×ÁÒ^]cyæqì0Ð}(ú0q !4>JAÏÃÁ¶ Eð-hÕ1§òÙ8^×$…ÐDœ³Ô{B®rh÷?vÔ$]÷Μè1 þÏúuÔí§¬èò;°ØÜ¹ð™Ã§ÎÕÌLÎyÝRÕ”ÙâxQ\Î5XP“&ÿ¦„·¶p[IÉòTÉ׋ú\&<¶s™Ãb_„ÃÚÞ›_FÜ…³N2”öiøÛŒAkôQ[†E„þˆö¶Æ ‘¤¶IÃÈsDmá’¡i2wpjXŒ“±þºl{#AsäÔÅDUZúù»Õ<׌‹Ç-S6JšÇ!³Gaq‚$A!RRRÚ%ªŒ}ê˜"Ñ3&ýTR~±_& ÌÒ¬eÅÓ QôRƨ€¢ ¤;Š|4Iþ˜¤@íÐßz ëÁžïhÞb9ô=}|m#’“G¹•åéºGÒOº –×ÕÀ¼fµV›ôƒíyÙif VlÙ+‡Ož•ÈÍ8AN†ì+ûËI|!1º“Ÿ‘Æs$}£êÅ´X ùž ÆÎLo‘Ђ4lEP‡L„[þõz¯®›ÓíÍPKs¢0¥ÅF Ôø›ÐF¤€Ù𓝽ˆt ¤xÝ‹œšŒm1œŽúañCS÷LxtgãÄGwONIr¹’äÞýí¢÷ã».ÃB9ØæªÿDeRoN:9ëñ}}TÜk?5± MÞÜz: vžìÖß±;ò‡+¦æ±¯‘Â·Ð¯× ä6L•“»Zpì‡I8Èð¯Í°·Ò¥´”úw0ûPûž ÑàPå'5×! þ1À9`JÃÉuWÊ”A¹RØ+O…rg.,&$‘ŽtL]Pè4JÏyƒOÔÚ›Z´Ò¬é» :DŽªi,2Ì6ý2©°fÊ ™†}cÔ~ ¡Ž©`Ë5ïK P«Íà)œgM¯¦^$=¦Ém£]æyr%eHEM}Kð~ãÑ„Þcæ&÷ýÒ''§¦O§&É“ÔÁûŽ59l»†ºNÒGÿøÎ¹0Ÿ¹3ÎUáÐ=ƺv4oé{WD!x´æ)ßöÓï·;i _8UQ£NY½kz„{] ħE/L†Êñn8Ý#»¾: ­Ù*g4ya°¯|RàÂôêåÛr% ò–[e Â# ¦2¯Üž+ŸÃk¢å[Þ®³°¾ò¾a”°ÇÖ…fdœÞdƒŠ6Ý·ØÎû”ô‚Çã¾Í~ Ù“¤4D)AZRO]éCSæài3ÁTo¼¿Ê•ÜQ7|gâ5 -ùWfdÈ¿¼U¯üÙî¯íºáóÕRìÈ^­‹25û oËMí„Ú÷Mð¡£Ã6%Á™©Mâ€ó¸œARHÚb ÓÝ “­ë%Ë]/ ³däÀ^’•%™’žž¦4NŒ,„2¦6û‡ÑG©aÝÉp÷…Ù2JŒÀyÙˆ4Å8ÑìÅÃ1–4 ƒæ*”qê˜Òï1÷&48ϸš';Lô°Ù’Ûšé{ãmÏzÒ\Šõg;"Ƽ¡plŸ³ŸìË0tÜmy[RRa~'™MM­B³@ï€1pŠØkŒ-’”¼ÅU[½õàO/¢j9ï’ïþÇ[C ‡Ž„iò}o8t±ŒŽæ-}¯½ã¦ë[îc1O±ýMXã8ŒP?„Î÷FÉ7 >ÓS;7XB0Nä„ó²Ò%Åcó:H¡‚šk=øa=I&]¨ÍsîâçjZB#Oü}UKQw¿ÖÊHÑï…Ïùj£ZnpÀú¥ˆ]²ÓsZÔ nÙiOzê‹IvÇÛMž$wj’ç6¾¨_ŸìâSõMß}+~¾ÊsAÞ!È+S<©p µä¹xö»Þ2 þK…—l(ý ä]EBs´Šs½Du9˜aÀ·À…€·à¿wp\>2M>†¹^_˜òM£õjI«¹•º¹áö}“Ç+ ¦DxʰÙz¸\†ØÊñôÑí¼-±. t•1»ðm¹Á4eÈð’““ƒ-[²²2%=M3N­ Š? éñÄ}(cj?´ÎÖ&I‡\“|þL¾šk¯¹^ºŒAà,m ÑÎzèqÕk¦A{õFiÛëþêÊsᎩ@å™çÔjkS=¯vÛA 朤îA>¸™þÛáR'Ý~¿f’TŽ$q¡@t4ÆÞ5}”@áF;ÅBƒÜ¬M‘¾L’§)eóއƟks ð»Å|ÏçZD?ÙÆpèãKƒ™·Œ÷‡{‹yJ·>Û*Ú¬?N¸õ õ¹Êê:™9~¸äeg _ [íN9³½9“GKß^¹ðµM“ÑðiŠw ¶‰øäÚGüê-Þëíúu™/‚VëŽ> ·¬Û]&}œåR¢4ÞóLhdÏuÔÖñ Þ`œø×<8;z.š×÷>0á$¢ëUci±ìúΔc,{ùGÚ&<^|§xÜÏLx¬øWX€2ÜMn¤g—w|{ü $¼]5þ±âxæMDàûe4ëNYoÄ!M3úä¤Ë¡ŠÆú)ØÁÛ'¹d’»®‚3þ¿¿ß O¸ä»ò¤NþO"£¤a;´¹!Gå5Z?–OCeiß³ÍCûd˱³ˆÄV¿ ¶F9”Y„áÔ1‘`lCO:fÔ¼Ñ%0Í«’l˜ÝN臈ˆ}ò%?/Wòòr$ŒS&œtéãdy,q"S¿Ù`“ÿ€†ò†ñiò˜ëÓÌ~L?¼$KJA ªj+Â}wŸ½e\ÑGêÓf_;ãóþŽ#SþÊ4Ï%´©…3ÌãDç×îšñc;üAûL’¿'ÚžÃ:ÓF“€IjûP é¹*ºGW¯£yKßÉ>Úó”oû!;³ë¬xö••Ë´qÃäß?»QõÎÈKo’C^¿»¯Ü¼P…õgļùÓÆD‚Ƙ?K<òÓ'=¥ÌÚ»ˆFyC;xAfœôÇCZh`Ÿ\H•S¥fL¡2Nà0ª—Y?Ú%÷ÉÏj&äZð¢ú¢ ÛûÐÔE¾·ìùΔµ87~ÊvöjòXœüP¯ïÙóÝ)_˜û¿ûó\éc”]âÞïNi#E3õ }o¬÷Æ~'1œ óµ‚lú°$…ÔÿÔÜóùÉRŸZy|áM${ÞàôÿÜåuØ×^kÊ\òý% ’•†{¡áö}2ì>f7ã& Àdøîì>Ä® …!·dLFXÞø—fEk\X µí Ä`hÝrÝÕÊ,ŽÂ!½³À,åJA~žääI^n®AÛÄ|lA0M¸ÅwLõw7§¼ºÛM‘]i¢­?Ë¿PЦÉÕˆÜ8å­l^ä˜ÓZíß#ª^¨ã*Ü1Õ¦bæ„Æ€ÖÒ`¢Yp˜§Ðf¶8@*Ïú³-.|TŒdé_“Ôq]¸!ÝV!κ3'Uyv”—­9õÂCÇñd[iGÇEÅäãú º§½y‹!¢"šó”oû3­®>B]É81ßÙßÞY-0 ghqBu]ƒ<ÿþZí,Ðàz‡Îªm{Õµ’C'ÀXPÇüçÃÿ˧ßiùÝUÄ#“Ó“ž"]å6v ÚU8à{{4ãÄ’+&QÉmò°>b?p¹[ÎI<æž`½zà50OÁºÞÑvf%^"âû§¶¥îš Ûð±Þ¤3‘¥gýN†‚Óf#4@myÈýOŸ#h¦ÉxN'¤ Q*ÓnßÓ'‡“ƒd¤#p ´'4=Ý×.™–9Q_'SÖa¼÷‘“i#å\ê $« Ì èv»g°…>ŽÓRÚGImxå`‹é>ú'¸Ï‰-)KìÍ>¬Cüײܵ’窔^øŽ¨¶ep–B„#˜Ÿ.9ðeÊÉÉ’\0KÍLSn.´Mðoòj›ÚgœXIß1•lI‘Fg’ôN~LQ²M¦I‰¹`!Ôqî˜ ¶>æ}‰ƒæ ßµ6üÝ€é$~½Î*Ür 6UìîàæÀ@Ñížxnì>yôÔ²_?²xˆ›uO÷‰q®ŠÝɼ¥ëh‹yÊØþIÃúÊÐ}dX(<ïJ`_ÐL“ïùxûMü±þCóR=Eº*^iÔXã®G3NZòÀÎ%á{†Õnj‚p‚ÑRÀŒõv•˱ô1‡>X1aí×ÁÅ̦˜Ý…fëÌ++Á$!øEŽRà %auðš+zñàÕ¾‘iÊ€ž—qó”I ΓÉ$ƒ¥ž÷#yT­ý•ùÞÇ RzÒ-nKš$¥‚ùJÏ™…ˆ¸˜i—¤Æž3¦á@Ÿç|‚à0 ígüvões߃0‰Y“¸¾ŠÔ콎!9üŸ·Úä­½ç›,‹² ‘~çÌ_!øæ¥'Ë—.È”™Ã³å‚ÁÙÒ7'ÞHŸ`[Ü}¾ë_¢Ñ=Û_#çàhл ÇÔ<1””¦ L“A!dyýD:ŠôT´iÔ ª7·ÄÓìÑB¶59Ýgªkø;bƉÒh/A•ªgJœ§Œè¯ˆG)?!3ëke_æÔ.ñy¢m/ÍÆ²›jeh¾EÆ Èk‘Š·FúêØŒ(˜ÑT]ÛàqØíˆp´àý¼+sâ¼÷;löêHûß_¿Û¡u±#yã—9 ÁCÚNÊ øíï‚þfß{ÛJ?'óïÜt Í@Øl6Å<1‹Ãá#áBˆ}šôÁÙ — ›Ã…ÃPÑž¡èQtGü~°Ñº…y‹,ðëá÷Ä€\œHT€Câ’¼F§œ²Zö»XÆØŠ¥>9_j,}¤Á’+É0!lÖd¹¡‘òàù&µhrIšÇîÝà§”Ž”Ô¼2¨CZsÞ­4È ,PäâN×&Fûjï7ž‚ºQØÚ¬mÊS£&y0KJ:†‰8RÁPwâ)Zäÿ6Ûä©ÍVHÝ0)á=Hv)cåȽcS$ÝQ#Õ5µÀEÏSá…ó‰Óá¨ô¹ï¼oÙçºù³›bÀh¦Ž¥Þw>júÝV”&?F$Ò%‡œ-vBA Ó{MŽ ÚsÚó`¹Ïî’†€0)˜‚ûîƒ-;ïó® ¨%&ÝÓ^û÷•7À@Â%½ò²»Üç)ÇŽ®}šhžGMSa†[†dÂbfïÍ–ѦQõ{»Ã>ž§|9íö“uö$+Ý,=‘€÷òš2‘ˆÌÉÎñì¢áý@&Iééz¹ ~ ¢ŽJyê©À>Ø•êá™s„ Ï<ºãwï;­‰B÷tÔþ=§¤¼¢kV¢íÑŒ?M »w¯G^{æibÈqFÏc ˆO‡f¹¥_ü§A;ÇŠF¼æ[B<2NžÓÇŽ®ÉëÓçÁâ'dîäQcÄKTB2 BŠ'ztž b± #U!ï¥þŒbd8{#öŠŠØÍ yulH Š”$}3úezÀŧ)gõü|æ•ÁFæ u 6!g0È!Aà&•íÛ³÷“ŽÕ[0wÖ=ºNÖsðÈÑŸ´ÿõ»ÎåÁë¹ðÿ9ƒ°ãÉ`b{‘ŒAÿwVß³=^FGk`,JÓB5mh¦‰¿™•Ü ¦É‰ßƒ‘yÒÎ2É4)\‚)Q&nÍÌÏñ2M”mmlTZ-Å@A«Gf‚åÚ¡²afyNÊÔd©ŽÆ?îùprNãCÇSî½Ñz 9òñUʨ5H4·##§´_ÍuôeìèHfJk˜Xß`f@ÿ»±QžßiW!Áõs3¥ÊÏäJÿt,.ÖFâðÎ%=uLév÷z>9vðÀ&œ×ß­ñóØÄ€_ 0èAG&ýæÜLù̸4yj“MÿÚì ±<x™lΚ*ñÌ;û²9ñTZdÖ \o”uàœLˆ5gcþKTº'˜öŸ®wHÖ.xÓzÐo6øu" ò»Ù…&ø2»!¬àže Öä¾in韕ìõÓKµ›¡+î‘«ExÅ{o”Œ6½bSÉ¡Þ`œ0¤#ƒé'•ùÄW<͇64JU½Mªà@o‡+f7  £4{JÅB–ì‘üt‹äB’NS"ª>ÉÅ{óËä«c‡Ú¤ˆu€GroÔîÚ¼þ Êb«4±ÓŒ…HßñóÆú4±žs]]ƒzçEÒÿõ; tçéé6&¢Ô}=ÂK;š¬ðù±Âœ/rÜ3Ù÷Þ6{Í;É0¤º¡yQš'}‘OfIï©irá[à^›ì‘ IËã7bÔèxï;4ãd³Û$Óš)¶F˜B»¥3¯6«5„1ƒµëòY¶úã^½§­V‹ßMì¨5ã7A%†ç9_†H=24#åÝërC[ßA²ÙÿYc“s`¨5 —|{^–Ü3-㢠8ô2‚d 5º¼ uÏSzÏù8¨Þ²j)“Ž¿]}‹¹71ÐÌÉ”¼Œ$™ Ÿ§ø'-GrqÂMÒT gvx§‘½-r+Αq"ð»{|mcK® ®ß„b”á›$Z]0ÿµ`€s"çmΣ‰H÷„Þ~¬^°Z¯‡D!Í—𨠵"üx ZjoAT.Jí½ï£ú‘¾&Ì“Rƒ¶h›¢‚øÛuàxRÙ½Ÿ üJôfÄ7«ØU`¬‡®››õE‚µÛ"íÿ`ú½Ä> ý,0}¢ÔïDfWö=ÛíÝ "(7Púd–¸÷n”4µŽu­9áxgýõ¦MõÈh ¯ñ>2cÄ_v–]1M6åKæ'4Q^?*¾ËËñ~ý-é²¹7–ŸJ-6šÙ)Æ ¯¯–×WI]G=t5®ù©ñœ.7Ô½æx¿XÙ(›Nzêç)õþÙÂL’ç•–'áÄi{sIOSćžON9ÄlŒú{5~ÃmæÞÄ@ ¨Y^Ü)¿ZÕ(»Îx™&•HÌÔ>eë½µô¬KnŸ”¦’lê‡Wmû=êóæ¾c p>ìh®ê‰tÆLWµ_¿?ž÷0(Šx…—ø@'Ïš¨L;›Fgª[<1N¬#E¼j1^ñæk¯Üöµ>ûâ‡ë{}ï‹×'§!Ôp¤ÀˆL ÁK¬ñ·—8K‡9¥å¶fÆÉ(…ׄ^¸ï×ïÒR{ÖI™„fy´ÕÑ¿¨VçuÖ5Rp 5ð×ÔärÖ­yÿ%(+7Mð´ŠÔ#}YdÏ·ô{sý\¬ï°Ñc£þ¹‘öWõ;QÒU}¯»C#î¹€z5JøÐ Ð Ç7%’š©1ŽwÍ„Ð|A3&,K—Ë{Y›ÌLdgw 1-L”V‹f€Ê”Í€Âûîó']¿s;0Fü6ù-h_%2LÚÇŠÏèºé¶†»¯jôÈoa–G_Šf9‰*ª0'I¾wq–\=ÎV>À÷wÅ\ÂjtÕ˜Òó‰Çí®YõΛï¢*ñ8—øô”ù30ðä†Fåãd¬ ý˜Êë=2¢ umŽcšÉòššùš>ºj®êªyÊS]Õ~ßzÄÛï^ùrn*•µ Žš$•õv™2°¿Zc;‹F7œ[Ÿxbœ´Ô’Ó¤«ºú\ýî-4{ÞÏŸ}gµçK·,ðªn‚m™Ÿûô‡ÌA¡ŽA ò˜Rm†+&Ó¤£ŽÑ?æmÍ„¦wÐ$è÷Bƒ¢ˆ×$¥åòJÓÉ0ñ<‰[MzŸÿ?ñvòlµlYµüVk=C‘3,7Mðhœ‡ÿ’ÈŸÔuPýÞ\?ëËz'_¶ø«‘ö¿Ægõ;QÒÕ}ïÛ-ÄÆ¯y™¤³ÔªUÕŒï'xŸÕ{o9êþér´I +Ý-ÎÌVÿ&­ÕòešôóÆzµhe›µZ:؃8hM¿h€\}˜þÿŠzChcä –/LËûáo‘•êÚѸL¤1¥ç“íëVý¥¾á–âo.‰Æ°0ËèD ¬,sÊâQ©²ì0æ"¼—ÇKU~´BÜ8~NxÔÙ½¿;±ÊÝîU=WÅãÚ§™'… T0–t_w s/˜(+·íUZ§³u°¸êÛ[™öwÚpe¬k<1N¬5$ê9sÚ×~üÞú¼Þ½žÃñäuñ|þú‹“"Õ<ñáT[O&<ÖZ'šÑÑÝ«mj6/jöÉÐÄ$êð=jÃÐ4JÔUØäfi:%鬃& CzŸ›)&‘³¹ôpÒ];ÞÙ¾ve1nCTŠ–M3Oñ¤qjéw]OÖ»W¿þ”lßiÿwv¿£ÎÍ ú¿ûžï ô¸ æÞöîa9.L¿©°Noòš’Y¢©«×á´U£e,O×CïþœeµÝ¼çô»ŒÏ‡{üÉADï‚ïıڶâì‹`–÷ý‹3e4|-:Ö§3çÖGã©3Ç”q>9¼g׫›W|ºUÑóI¼Í%u›y=Ž0ðÈJ«<º8[þt}Ž2Ï[Fê¿á_ªÒüÅb›Š²÷#„5¿ð¯Õ`žÈr™Ð:{®êŠy*žÚß^]âéZá€~ràd£.cqõ-MÔWibI£Æ©K<2N¤d¸sölüðŸÏ¿~åíw#½§Üyª¢ÊsçUó’£åóÄ›RnJ¶é礦f&JÊ©a"ñGˆ„qâó$Ic’ÐR~{ÜÛúŒðÞH€>4Ï£¦iÿ®íï.ëµOQžÂ%öÔ:ñ˜ø%žã‰qjÓï¨ëš¹ì­W?ÉMêüºhô¿·bß隷"r¹ï¬¾ç»º ôÂŒáÝ¢…òj£È0és^GK|† Yïqëoãyucþm<á’Ç‘«¸œ|z+Œêe†E^0‚SMhГǔq>9¸{çKßxå}`‡ßf¼Î%¡užywL1P¦‰¿GÆÑÀ¤¸~X¯˜&úAÙ]­LÐ{û-A!Œ??©Ç×7ªy…y·M5Wé¹=ÞÖ¾Îòšÿ”@IDATjð=ÒõwÎ>AÖlß/Iøœ®–ù3&Æ”FíúG^ƒxcœ8k’€¦7(fšƒd|üÊ oθdᙦù—~ññç>ÌŸR¡>73¯ C3ÚA¸]ÎúMË—¼Z¼aí.ܪñXcâ“¿‰_â¹u•Â.¿ýŽú0‰W*˜¿e§O½pÑmèÿ¼HûŸ8u¿—|Þë~Žvß«ÄÙ?ÝVÕÒL“¾¦q¤Çj¿çœ[ôm#äg$Ë×geÈÝSÓ‚Õx%´c¶£§Œ)ßù„>’›W.{qǺU[Î!ñ<—„ÖqæÝq£OS0B®mÂÄ@gÌUz^ç^oñ²öuFûÃìš.ylÞ´ñ²u?RÖÀ:¤²Á&V»K ûäD•Fí’†Åð¥ñÊ8ihÏi[W-߸gËæó®ºöJ·Ûµ B>Onfº§ ?;)#5€3BÈó%î|‡Z$?T#øþ6^ 娿tzªkD9û´ya2¤(­\.H=!gwºåIf8‹øŽ!ßßá¼Òw ùþ§L>ão>9¼§t%æ“ µ¸¥;Ì%á6ß|ÎÄ@Ba€ó724ÎMÆùÉxbŒó’~W8åÄê]§Xµ?VõŽv¹Lõ1uì)9t\]zä´ Ø/Ú¯éQåÅãDäj?'ÚÐSºIÛ:$#`€,}ãåwqüéØ)Œ>vüÄÌœÜ^é™™y)):Ÿ`àr9ívkcµ¾¶úè¾½û”ì8h“7j–Hè0µ:7>Ä'ñJ‡x1ÓCUìw}Ì {ùÛ¯.]þ¶¬S4mèðqãÇeåä¤geæ&bÿ7ãÅÜùÁ€-µ õTÁUÙ#²q¹Urê½Õ®ÜV•âªsïòól¢žÒóIc}]ÕÑý{öîßµ£ ¸ 0£»Í%‰Ú…1k·&2½û˜½&¦ë6Äô%ݸðDÇO"·ú„á²ûð 5zKEs‹Zên<¤cVõxdœ´ÙlŠBÈ4iÂG×ÎýÅÛ÷b;„klïã¦ïÃaÂñE¼´àÇÔ*‘I¢¦‰LSuó1 â5žÌôPAõ;îÌÀ– &ñ ¶Ã8NÔ~WH3ÿµÅ@öø‹úö½úE郋¦A†Ê±ÑöÓvU~ð›öí^y¦å¤y`Ä@O™KŒm2#Ä J_ ’ÂâéF)¨gLñ5øk¾fîM $"†ì#ù9YRSo•F»Sö••Ë„Aµ9'V\3ÔŽèYÛHXó|&6僽ÒHa¯ %ý NõX >šiÒ¾aÄ–SãDM¨xÖ6¡z Ì~ט0÷!a Î-Ãz]zÏ¢´Â1àAÌúó€C_Eٮʥy¿fã›e!š87kdõ¤¹$qz¯ ZšF$½‰ò¹ø‡t’›gäÇEÍšèB L;Tþ?{ßGq¾ý^ÓÝ©ËV±,Ùr·e¹wŒ1îônz‰IÈ jIH!!tò‘! ºé.¸÷^dY¶ån¹È¶¬.®~ï3§‘N²Êét:ÝæÕoµ{[gžÝgÞ¶ŠC“CvæSÄ©™{¬Ä EÆG\º€âÃ.ƒ ÅqÚž£’I£Ø÷IÉù3qBiñ1LJÜó£.µæ–¤IúB0…3iâê &rFž`²„9&l vM±VÔ}¯…B-HbMŒîzÕ7F$fÜNZ]²\9‚Ûåç–nÿŸÓ_ÿ9Ïs›ZnÙŽÈ÷.œÚ’+¯vhZÒÄ_U‘¤5½ñQÒsüþ[~P'”Ïa Ô®1h#yjþV«­Ì>Ýq »9H„"N?ÁNœd©ÑéÇGshN@ < “$MÕÇɳÃ#;=˜K„í¡(꾇â]ós™S¯ýEjä¨+ïÑ›cïà9ºÁéËU¥ÿ«ÜúíÛ'¿üÓÉÛÔOïíƒ|ßdÛNm‰wH¨½EZ&˜ÆbBÎC$oOÒ‘½´€âìg©DŸØèq¹åJ`Ó~ÝjË.COwd¹ÔµÁŠÀà^Ýiñú¡q:zªÊ*ª(& ^1J<â„2Kr É4) J˜¤–Ijšäœ7…­ÈÎær6r’ëBÔC@éÇÕ™ï{¨ßO¯ÊŸ””Dñ|8Ekê2‡{iWsŠ)ü‚ŸtÚ,µìœÿα~‹(J|G@B+Û Ìe;‚¹\ïûÔ‘!‹€Ô8¹I“ž"""x2Pz‚‰N–WPÿÊlÚ3™œŒ_‡h]P•M¦= ë“*ˆÊ/ë¥T¥P‘f#õJM¤C'Έ‚å:Iã†ô ®BAiB‰8I¸äGtˆ$Mî_îßr9ÜçÀBŠÄEþ·¹¬Ÿºïávg=ê“ùÜšt‡1zwpæ0Yê%pϧ\C9½\]±åÃÿ½ƒ'Jüƒ€'Êò]óÏ™ÕYB­Væy:A˜ŒÆ6‹`Çq#eÄVÒâRʬÜJ9Qcƒ¦ž™•Û(ÒQJãv§ÈH£(7Êz(Q(šF`0›ëIâ´÷ˆ"N!ŠÄ©a=ÔG¾!"ã·ºïarŸ/xå˜ùœ³ô r9~ä Í,Vph™4Õ&R+´׋»º€—l­·«ú¡èìȶQÌí{•ÅfòϬAšêAÓd2Éd6‘™§äX3UX9xkå ʪØD¹‘£:TóMHS¢í8 N‹ãÑó.¢¼(74Nþ2Õ³Xm.§Ó…¨µëÎþÌ©ú‡ zv£ùšÂ\"*ªª)Š5QJêâTWµ¤P„^Ú›¨Õدt¹œ×œsÍâÞG¤»à臸… ÒYn½ß×’þ_¹ Þ…µšGk6ª™B@!Ðu/PÍV«¥º¸¸¤ë}&Nü. âa0aŠŠŠ¤jK5UWWSºÝF.'qµœ Qåe”gÖ!>Oði‚Ù`”³”¤DÑÐ^É„r‚à¡ÜÒT¯1ÐZ»®¸´Âå°ÛÝöLîƒÏýµçTû+‚„ ïÉÑõްdßÑS4r`F0-hÊ ˆSÐÜ U…@x#õJN?›Óu»Ï\ËQò/t:EÖ8¹w·„´®·õ=´_åÜ”…@0J °TVœ)«ªÖT2щdó:_ÄMœ´ÁÄÄ爎ŠbÒd%«ó8Ù”Æ/sD©…NT•шò5T¤OÑö )íšç yšrÑóÂÀ‘þ2Ó¢©oZ"EEG‰r¢¼(7"¢mà×-æȧwç­£’² *bSÊ‚ÈL ˜Ieu>}±l -Û˜Kw^qÐBùÃ|2øu–kõï‘"ˆê{àX"N7^'0Ô¢B 3#õIN„îTDrµÝÒÛuçNNºFãìÉÝ‚><õui¨›ÛÅ×b$Ã×®h° ÑhȵŠ\šUz½öû¼ó·|Â{©Ÿ …€À;&IrßÙ×ÌÿfIϾýg~¸p}Ì/î¾Jað=çH‹Ô Óëþ ÒÄaÊÙŸÈÂÄÉÂA#lLœìv;k¦œbÔº-¤IÂ#¯ ÌïPåD ¾W‘‘f14a;Êç±ÚÄø99œ^ÑÊ/ç~Ê甇L›¶dCÍ]º™*u1´7ú€ñ°k Âç­ "]$LFŽ­7?YF×OMÓÇ öÛ=òÇ}Vçp#зG2­Ú¾OüØŸZÁâ€"N`¨E…@(#pã'.Ýž³Ù±¶jC—ÆërhbIãŒåøÞql–Çõ‹c¿ƒ¶±‰Çœ‡J»0±IäAÓDÛGœ8:–ô x­ç±ç²ØêñO£)å áÙ¼f§K£ÙªÑëWí}xÐ^=(ç瞿ԲB@!ÐHí^`&W±VV–WmYµâÚ©3ï{÷›Õ®{®¿Øg»9I^@JÄ2“',ƒ8EšÍ‚4‰ÀV«á¬ñƒ’Ä©^›â%ÐjAäµY{ÆÄ y¥˜¬ñuAž uB &¬Çv&ãn'ÎÓî-_-.>‹PäÀ8KÌýq™v9°_º1‡>[²™ÎÒ8lüÈ _¢O¤-Ñ“EØx8f¢l¾—å×{Õ. v²“vOJà÷ÙH•Ž!ÉOž-¦ÔĺqÓNG½ê*âTõC!ð‡N3ôõ]É6'õ`·ë4Ë•Ì>CÉ\ºdöêÂ_ê.ÜH$dˆ\±;ŽdG¹Kß /,ïìÅ÷¥fV;¯[pooæ?÷A*˜hàëæòõ9Ùƒ&[£ÑîäpáGš9¬vÓ°7òÒ.«V€¨…D-(ü^wIš8z"(Põöµßg'$%ËËWÓçäºëªI_5O #2¼7È –ò$š&pk›BãÄm*ˆ‰¯ï“àšbâÆ'\Z' qY2-”;‚ž?5M M›siŽØûîÚEóÖs€)H)pjp‡yÞÜ¥[iʉÃEîXqjtœ0y çþb'×åÛ(%!–†öï¡ÈSÇÞ–ó®Þ/-™vî?&ÖC뤈“"EœÎ{TÔ …@`øâžÞ¤µŽq‘f¤ÆéÌó_ÞÅæq®šØÁì„ÝH‘ĺÆ64²¯W«4øŠ4§ù;Ëá°ˆCíºŽk´Ä­¦æ“¤ƒ<€{`×ÃC±Íg©®¶ÜJk¸èIŸO¢T(šCyhBб·ð52&óò¯>[Êô…_gºâda‘ë–K&hÛêó2¿#Ab˜< ÂÄ‘öç"Kà |sP'Ø÷ا æyÐ4“ <‘œû냅è7é)¬5D ßÍHƒ©náP–>l®'‰ÓaqшáP­6×A§6C¨N h£ÿq ®²¢r÷-8ñ«k–Ëeíãþôò/q*þïKÿ‚‰^¦qiJ™œ”2á)eòÅËšRî°”0)*áåËYÌ]§"îë¹´TDNC¡Nã<»ë烋¸sâË•›`Ä«‡â·ÿ¼w1vâ‘âõN§ã9,|#/Éæ´¤Úõúã{~šéζ‡ J ¶ €wmTÏèäó¸>™xÂ@ŒaÅWs—ž:yf씳_~oaì~é®±Y}4Cû¥ù”ç „]˜ÇAûƒ` KRË$ “œs9Z-’8a.'\S‹ëZ}â OBŽ#zA8í¶²mk¿ÿ÷ÖU+Öò®0ÑžÀøg¿·™|N¿î¢ç•pä<ø4AÓL‚òì3eò´†–mÞM—\0TÜ[ÜW%@ïTŒqº%ÿt‘ K^ƒ…"Nò©Ps…@;#0øÅœñvã¡ò²ŠÙ<ðjhérÜ8ÇŸä|&9Çx¬•-hNóÐæiÎñXÈëœK«)Ò;4Ev£®X¯u”æ<…ºÏ¢yÄçC›=°ÊQöï0ã²7òŒ§Q[ÊËÎË|y×]•Õ–G¹”í²Û¦d½ž3Q™ï5 £Ú¨ðIœ¤Æ íHSOøæk³7¬Ý•·cûÁ ³.¿Ða·Mb‚Ãí+ÆltÅÇEiLƒ[½Ã;û* IRÃß¾œäÈSþöÜÖÚe‹Íæ*.©p!¹-—UãpØËïÍ]±þ»ù‹ØG쟯Œ'`‰ÉSã”Ä x#0ÇÒÍ{Dô<ø£ \ˆî·r[M=°VcèÏ{Œõ…2™MÔ-‘Ã’Ÿ-ƒ G8§S[4Ô¡Pgoʨˆ“7(©}>"0e¹K_°9g¶Cã|ÈîrLÀØdï, ÊyåZÒº¶i\Úv/‡sÈÛû£AøP‡´ z1{ ×í¢/f¯>h­:±÷Áþ7 ziç!ŽLugjbÌøw÷¶d¾´ë~‡Õù{®èBº²ªð àAæcð¿/4$¨ºAå;-–JÇŠ¯?[¶âkZÙ/kxŒDFÇÆ#Í1z½¡ÆL˜è$b·Ûª««ªJ«ÊËŠŽäíÍÍËÞ~ˆ«ÍˆRIÍÄš|'p jÿ&h›`^U^QÅ!ÇsqƒW é"aò!.oN¾ -¢2Ù ŽûÕ§{’ N(Í¡ãŠ8Eœ€‚…@; 0蕟ܲ뿬ÊhÈ–˜,mâu‹´ݢȘÌu[îÕ4ˆèÐ À)þkOŒ¶ÜÙm÷ƒ™û¹Ž.ð84Ž­:î™Ýf-Ex0w=ûw@šð[§wÍå¨Å¿Â²…€BÀ/`|fdÐ:,4Iu$Uhs`ÂgØŸ³ãO ØWîÇ‹F€pñÄíH'°@œ`nŒe)éß„ã‚N¤¶)ç`¾¸›ÈÓÌ"ÊÇO]î¡”Ñ=±ÖôSi:þ®õækwî9x\…%Š8uüs©Jf@ËtrKö3N‡æ)¦ r„°*&Kïs,¨78 Ý®p¨öà—³ïà¨zósÉ:—ùêήs¶gATChª­v¶×Ä-õN «8‹y]LÓzèš‚8AsôÄD3ÍêkªËùÜÒ¿¸ŽµQlåìï°ÿŸÊÝÍó†HúsöýC‹°mÛƒýÏpˆU;îzƒƒDìóò¾\×[6We"oaÊ÷<6ä¿ØW‰B@!àwÐþ€`ŽN?–Ñ‹€ —Œ¶‡6&%9ñbX 0‘pñ$OÀd sLØÔš&._mxw-T­õbàЭýºèhm>ªMôðx3]9 ‚þ¾ ’è'cMda4^YWIf&U{(3IGßì³Ò’ƒ6A¨îj¢G¬ôÅ+(t-‹ „^eÙar¨ÌõZÆ­½÷ÈaÉ÷ŠËfâÔÙEiœ:û êßfFÿÃe(/Û5—ù8GÌ­àæ~êÞdžlÒ<ÖæÓÕ âµñÏ9ж ³=—«7iœ¢55º"Wk*c´ZÝÜÝdå ÐðYŠÖDC;¥7Ь¯º Ÿõ˜®å[îø‹ ª˜*ŒB üšÌ¡9)æyž¤©³ú8e ‡/'`$—%v¼*øEšêx´F¦f¨_‚ŽF§ê„Ò¿·[];(‚æåYéß;ÜÄ©7“ªxˆÄÄOÑËk«Ä>bÿ³2 :Qæ¢Í'£w"}³@œP%Á@ZR|­ŸSû9•±IeL¬|;§(âÔ9ﻪµ`ÒôWnð§ºO©q’F{Û¾G³6ùñAsªuô¨âh÷ºœö¿29œ«%·ÆiçãýNzyç¯8¹í·èa%o3;lÎñåZë .|i©âÛ¨HnÐÔFD!Ðéä@’'¨@”0I-æ9wÿ Ïÿ’Y`.'`#'¹.djòážP#Y=ïŠ4Y§80ÄS˪iÍ1;UÛ9á kŸâMõƒ<äž±Óƒ#Ä6yæ•GÜÚ)ùÛ—9—\äžB±e=”ÆÉ$ý{ |Í$âð ·ŸÓÑ‚sl®—æß‹„ÐÙq ¡›¥Š| x)ç2—Ëq,§ŸxbÏ£Y_Ëßá8ßóȰ•l²·íóîç®ÕYÇ={eèK;çó0öhV»ˆŠÎº#\¬Ë:ª¹B L„@ëKÒ$«×H“¬«'»¸Èm!9—¤Ã³bÞT‘ô^eß%OSA¹‹zÅC)é– ^®`ÿ%l“Rí5žÜ½ñ9ŸäI–¿ñÔÚŽ@ GJ—Zât¼ H§Ž¸ êš PG@˜è•f¿*?ààý= }9ÔëåMù †'èœuc„+i­çþÙ Ûÿ1)Q(B4c²) R«’6‰È‡¿äû£6šÙÇ@ËéÅC‚åe‡aéÙ´d¿¦a):ŠŠ`ÎE©°µ\ì¡HSÓ˜vä–t&NRŽtn?'¥q’O‚š+Z‰@yù®;¹¡ˆÃ˜4Y Zzª•§ÙÝ÷þhršü;d+  ®P(^!€¨x/ÎŒ¢^-ÌóÖ0‘zaÛß©©|¸ËJ¿™l¦Í?ާ§—UÒçê\Iè"žÔEê±=}®”¬6;çÛêœ"˜kÝ™Müñvy4ˆÎÑñχËõpÎíztø þ]gÏP·Q-)‚po/‚U>…€_ðUÓ4ó½’&¯oe“¼‡– ÒÄæèÂ÷I˜Ê’ƒVZÊS¬I+“7ÜîÍo_ëâ͹Õ>­CÀ`ÐQJ—X:Uè~NòOŸ£>iÉ­;I˜ìLÄIv„…£ê]ÿr´ÁhœÉCù¤Õ¤²zt˜`Ðj¸4œíÜé:ɪò#êó»ýù·›¹¡H¤‚êù(4¤ÆírÑPÜL ¹œãJ—ŽšüÔ³ËzsÕÅ~D@¶ÜNu:ßýû¥ßoáÓÃ&Û ?"£N¥-ÚËÜÍÓ§ÉDÐp §SkD–]‘¦Ö ˜}{tëRGœØÏI§ÀàÞØUÐ!YÒM™rmdƘ!˜Ì¦Ÿrò³nÈirÆÇFjL6„RÒj,6›³¸´ÒUViÑrCô콿~î”ÝæxMwÎõ—þó·ðEÛÌ”ÏÇZK/w0_F.3â´vXz '} fUÙÍ"àÙVhõºß¡­°Yí¯ŸÍ=úæ×_¿ƒ¸Ã’D5{µQ! P(Âôä.´)ç¨4NU:Zã$ƒo¸õ§ÎŠOø«ÓåJÜ'Í56« í—¦‰4•éSÛžNA8+-Õ”½ÿ8?ôSvíÏÿ“6EûàŸüÍ=ï¼ðìw|úÖ µ­<­9:hŸ…ï•2qr‡zâò~4>mPkê¥öU#µLÖçÁ;û>yï{¯¾° ‡>XÛ‹`ÄT•I! P„ NRŽŸ)<:c¸øŽ"NR‹€ëGÜùÈ/4™£žížïºyÖxêß³¶+ñ#L@iü>˜4yGOÑGßmH9~ºèÛ{žúÝo?ÿÌk|©`Mêçã\¥‹Ž•ºIS¤AÃÉ;ê5òã¢N¥¨A ‘¶"ù¸Ëõåœ'~õÔþüÚ d´ ¦öBÝ;…€B@! hgb¢LËSi……l6‰(£”®±í|Õà;½a p±d§ØÀ×5ÝþÐã?gÒôܘÌÞš'~p¥ŽIS€‹Óù.ŒŸœs¥˜k4Ú—~ôËßþœQÀ³ „5èŸãeu +2ⵤ¨ó=¶ªÆ€€g[a0ÿ4çñ§ÑV í–ö¢PQ—T(4­Sg5×ëˆ.®)HÓµwß{ydtì3ÜwÝsýÅšŽÚ¡$0k`ìµZÝ ?xì©KùÊñ<4¬pÐ?Ç9³º”îÑÁ™,š+ü€g[aˆ0ýá–~~_E’'ÿ_PQ! P(‚zæz§‹‚²Œí]¨@÷úp=Ø5{öÐ%¹{ú«é) ®»®š šŽöÆ:(ÏìÓ’\&Sä[—]v{2ÐÏ„'.!ñ|8=ÂiõêÑõ¼j9|@[Ñ=)žâºüuذ‰°Ï@[Þ‘íEø‚­j¦P(‚ôä„ÚRåtÎü衇 •ROæ —_~/÷?SاI§4MµÏaÀ€ý-—Œ×!(GrVïŸqp:‚ „ÌóáñÖXì,*àwO]P!8ÐVÜzé-t:õ¢øÊhË;ª½\ÅÕ• …€B@ Ÿ&½ÎmV\^I•/±±G°ÝŸ \Kh›x׵ˇôKw)Ÿ¦vǽŠàà^M&§ŽE™ç#ʃ9•XqjñS;„ ²­ˆŠ‰¾+eä©£Ú‹°ÁTUD!Ì<<ÁLóo£ïçÄÑë—FS'´…\Ÿi¤gÇж{hÙâhÉ]q4{0š%ጀV«%XHÉï„æz"NžÚÓE—^=–•‰r¼#4ò~«¹¸œç)åÆÿl¯ô(rH=}9 „”¼su"ä:5W„3î¶‚’/»ù.nÇ•Ö)œïµª[çFcÿo´‰þ´º‚ž\RI9§í"¡-ÈÓo§˜é_[«iÖû%ÂDeÉA}¶»ói:ãRÏ\¯æsªë¶ïÝ—c8›“ÒÒ§ ¹-çijß«ª³{îîIdLÌL>¨£ˆSH<É":ÂÍùK«]tÒ#X„×€«!Š€l+âgpðκ½QäT±A€¿åøž“A¯ã—Ù|»Ë%HÑèîÊfÒôÏ­q¢Q©:2h5´æ˜•ÎT8i_¡“¢Ð´Aô.;tZQnYþ6œNÚŽ¤§ÔåsꌑõEœp|\ao2DÓbÌFò…( p/b"MNÒj3¸D¸Wz6@È=C“‘[ÖµÉE5W„=¢­àö[«Ó÷äÊJ?§@¶a±ª BÀW@:¤D™"Èäô] ´ã”ƒ~·²’nÍ2ÒR6Å»c˜IœzGÊ­.zòÂHºeˆ‘Æt×Ñ—{¬ò²>ÍN ™"꾫8‰g]|:©:¨]ð I~òl 9œu‘†Ûå‚AvÒ@}ìpØÂã#kÔôÉñqQuowÒY‹©Õh5©\ÿ@û-„Üóqq/<ÊnY~X'‰…šwÐ~kõº®-^„@·dUK…@ˆ4’ÞeáÉ÷oÔG»ªiÆ{¥ôõÞjzú"3e%ë Ià?e³¼¬$õŒÓÒ=_—Ó–“¾k¶P>=USTš%ÁŽ€Ùh .qÑ¢˜N&M'Ï{‘ýZ¾@'$LJäI§ÕEš †°&NÕVß*¿ÞáVœ ÷D«ÑàmÀ½’÷­gðiWyz>¦öªkà׳Siµw#.lù@U¾Úìvb_4Ÿ€æƒ Ké\u´à½Ýsød“ÅXºq7í?VÐäöβÁÝVhÑVàEd{ÑY VõT´ hk2ºqèhþ\tµùÖf LÔÑ„t=UqôØoòÜ¥J+¦ðWûŽ¡F¡ešŸg£åmóõåãrvKˆª5ÕkSåÕÁíŽ@Os½N–¼®÷×¾0ƒ áãZÓ9vizú͹TX\&`­ á¸æâQ$í8ž,¤çßù†žúÑUÔ³[×Z¤~ó·¹4¬Oš=>ÒDèÔ~üÝFÚ}ð8+­Œ|ü>tÍ”QôŲ-ôݺl±L]2R»ÒM³ÆQjb]t±±£ÿ¹MšqŸAªemƒúù…ôœ÷ŒÓÑÐ=e³ÉB5TææZéîn3Ïýärqi%}øÝz~6NˆçÄÌ&‡õ£k§Žb;t=ýî_R¿Étûåå!tú\)ýæoŸÓ7Í ®ñQôÙ’Í´ïÈ)ÒéðAL¤];™âb"k÷Ǟ屃{ñyG×[ߨ»ÃA¹‡NÒÐ~émn×u '½RÉÄ#Wo¾ßÆê~Ý{Ã4¿]W¾·8¡–¿ô1‘fܧ;ÝyÅ…âwcÊáûó¯/WÒkÝ&îKÃ}oØEÈ÷ Ê–Î.¢—íz ۋμª¿B Y¤Pr|4# ”b˧‚ˆÖ·ó=buôëÉfê©e-““Þâ`‡ŠÁß ]gôôäHB^CþÉ#tËÜR²{7†X¯ü(ü±ºÆšyÄÖퟥÌôêAt?8ÿ'íØwT”«³EÖ q’ÌÅG¶Æß0¨„ ‡÷§iãÓ¹’ Z±eýá_ßГs.§^Ý“¼*'´o±’Ÿ(D®êÑ­  ×W_‚”ýìæ™tüLÍ[µ]tÿïú©^?P;q=<ï–Û[½áy½ }>ÃÿV¶íq‚|]Ms˜8¡2 ÏÆë."»ÃI÷ß4RºÆ ­Å‡ Ö“¥ÚFw^y!ÎìE+·î¡Û.›È#nî3lÝs„ÌÆÑÙç«ïÉj³Ó/x¥ÐX>yö<ÒÔðº-ýÎ9p‚¾\±%àÄ „íïŸ-£_̹’ºãD1ï¾æ¢v{Òî¹îbJäÄü³ôéâ 4¨ww€Fc2jP y´qÒÔØþy]M[wÖónïö¢3C®ê®hIšðÑq°…Áé T}°€âìg©DŸØâñž;,9h%L†Ÿ&)ÏM3Óñ2'›èS¥ÍEéL°æßCö4ÐÊVš®£\ ¬ë•EZ΄2£ì²òšj\ôHö Q\…kçÒ‚8¡ žÖ ™Œ‰24&ÎiDϽýÍ]º…½óR¯nÁüÓ̾Ѭ11>Ó¬¯±•'Ðkuµ×¨ds­Oo¢ªj«èË}‚d.ïW Š#¯‡yP>qy?½°F+³æ—:é›}Vºz@ï“Û÷¡ázˆÉ¡g…† Ï'd@F7yú&çxWò À°þé´hÝ.ÖîDД1ƒhò¨|ÓôÁ‚uT^i¡§ÿßg”Õ7IÛÂ\îýùkéXA!%w‰åA€ ¨Oš{ð`íŽÖÄ ¢m×»nÚhz÷›5”Ëš%h{†÷ïA×O#–ÿòÑA_ýà;ÒsNˆ?üt6kj7“‡.ï¸Â­qÛ°ë Í_½1ª¢ÞÝYw%%Ä 3Å_±VmöŒq´`í*bÍîèA½èÖÜd6&¨4ÅYº1‡qÝAœ€OÃrÈH¥O¸Ïþäzû^Öð}°`-ãTM\aµZSIÙ¯Wn¥m{ŽR%¿Ç‘¸>còßçHsŸg#mã{¨ç‘ÔicÓ¬ C+^¨¯ó|wC½.ªü °@¹vt‚„èhpFí>vŽúWeÓÖèÉäÔ`¬£uâIšä‘‘z ¥°«z™}"¸]$:x®uê&­ËAý+³E4½>ÉœXQkÊ:( ^ºò`$úþö¡?‹oqBlTðØ% ä“2XŒvŒä‘ç#'ÏÔƒz×ã´1ç`íd±Ö9C=uNì S oä›ÿ¡ãiäŽX îU %dž Œ‘?w ¯ë¬ÿeCU£f 'XÈBCó®Á¬ù€6êÄÙ"Jå„rÝã¸~DœÐa&K£3{‹ß#˜@}ÇägÑú]ÂÔO–¡¹¹Õff£ë³ ¢óPh¹ºÆEqy’)žº9WO¢©c3ũޛ·†Ì&=tÛ%”–”@.\'Ö£aü­Y ¡ß߃PCb€àÂXfc“¿‚`ŒÎÌ Îó#2‹÷äëÝ{ÚsÕ$Ú”sˆ¶î9L:þŽÍr×é:6'ĵ!¥*«¬ËŸ¡³†­oz ›ÕM¤J‹•@¶@¬ …%å\–5L~ú “ÚULæ@‚šŸÍ»qÝ7’…}˜Æ í+vm¬ÜX‡ó»j­ïÏ[+ÞQʳlÎ{ª°Î çÜ’{„ž`ÍôXkˆã~Èš3˜O~¿5²÷çºO&ãÏ—n¦3E¥M1”×Ë÷6”ë Ê®¤¦DÏfàfv Õ+¢¥4¨r«_êùê: çÁÂ?ÏŒ¢xêÝEKw}YFÇJ[çë”Éå‰r–Ò€DŽþÇ߈ˆQf”]ÖÃ/V'iÒ’ë\M:“¹^ 4N nš0k°.¸~rhnî:„FH–컵٢ã'WU×9ùŸæU\tdm¤¹ç¼ŒG÷ßùò{:Ì„ìô¹2ºò¢MúZx×ù–ƒÿù÷dÎp½¿“G[ª\­Ó‡l²wçðúaöKË«„†F#çÐÚ@@ c˜$­Ý¹Ÿnœ9Ž5GkÍô° ë"Í쌻| -ß”K·\2Ah£°­%¹fÊHÊd’ÍÑ:>?ˆ4Y:Áæ¤ý{ºµWZÒrßìiìd¤ ìƒõÚÿ¾þÇÏ“ƒM ÷I£ƒžIU<“º¼+×]1Z&Yžã³1ဦ,‡&²ÖF:•ÂÇ d±¡lÈ>ÀZ,#ÝÅdÅõ~ýƒEõ´q#fÐt6­…¬aMX.ûN¨!Db¥Ç¿ïÖî¿ 5‚¦)™5WR–Û“Ü€¼â÷÷_Bƒz¥2†©LþÜÄÇÃß ‰ñÞgöN§”Aaví?&4o)]cØ43FÔÇ^rÁPyi5W(í‚´üz&ìßdd­@tL,)/¦Œè4¸bí‰哿I¹ Ÿ_])¶zMHS¢í¥Çh)…}xLšPV”eG”7él®wàØiQÈãl‰ÒþÒP ˆSH>ý%lF~Oó˜í5 !oZ4›úad¼yq óŸ㇈NU|LЫ5qïqæoK¶FrpÈÿef“=÷GäUt)›ð%EÕ)s¡€I\C‘†ÄE›Å|kk`®wŒµ—žfz؈Ñ7m˜¥}ÄA&þùùrzæÿ®ætâàfþAÓ‰ŽrºjÖª4&{ŸDç¿ß¬®ÝŒ@&ù§‹é»ðÙ’M"hJG–ûÙ-3k÷ÃL\¥””W²j½ ;ñ\ÿ"Ž䀰xæ‰è‘â.!ûöëá>C“.)ä¨f³¹¦äÁ[g‰÷[^~wТýøú)µ»{–»v%/€DBzÖD]ãcÄ:üËd 3>˜C?]DxŸAP‘ÓbßÑSÂróîõû#r`˜KH¿Ëa~oTõ:ÒT$¤Âi 7:éx ‡,%48ñ*/£<ó°Vû<ùBø4Á<š¦n‘.ê‘`&3"š0‚8Aã¤LõüvûžCPÃUŽt?§@'`Šªç„uA+YßÀ¦EýÒ“½.cZRaB…hanLÕël¶Ü"´=ò^ª¨òzr¨ëúå:·‹9-9„íx$îµKÜ9pŒÊ BÃgc×|1ªÖµ7îl×GlÍ=$Fqf²æ§¡À¤ï‡×L¦'ßø„5—g½"NÐy#&£{¿‡oc¢QC¶<›4¢?‚6]ÓÈHßï¢"IYºa7ÀBzîþÙ"rÞKLX¼•ä.1lZw¤v÷‚snó¸Xœâmäþ˜wa ´nðïòÏr{®O­Ñ†-.§žLúÐ.@{(eÔ ž´`õ¾¯'‹nl²7L\“\Œ˜N=ˆàë¶âŽÀ)ßYÌ•„.ò>Êt¦ûYõÀ¢Æó·Ä#¤æhù(¶êè+tÚj$­1’ŽVYi´¾š ÎrQ¾†Šô)"Ú^¡!…ìšös@ž&„O±æS‚½@DáëigMS$EE™yPóHA  ì¾ MõB ôNXØ4Ž g .°Î€Å¢8†»x×£ w¸~0—‚OÉ6+Zº!‡ qúÙÍ3¼®ùˆ=Ø$'–âWÓ-U&<ÅÜÉBr°†¾-^ŸTíؤàwS"éÎ/ÊDy¿Ûo£%ý­4ƒf!0‹ƒ©Ú»ß®¡Û8ØB·ÄXa.·`ÍNšÄ>B0õ’"£ëH9ø$-Ý´[¨Á#Y º~ç±;HV[ÇÏ‘ü áÁõú¤%³‰œ‰æ¯ÙA7rˆýþAs2„G a\¾yûJ¡ÞL>âx[s‚sÆr½0jˆðüzÐýk@c‹sƒ A³ë)0\ÂÄkéÆ\>&¯Ï¦{(Ÿ/¿$”ï÷޼cÂÜЛó€È"hÌn®e?-¤Óà K5û‡ A#¢X#%e$ûmÈ9 îa¿ž)"èF„A'\È}Ô\!ÐH¢Õ¸ö®‡9Ú`4ÎäáÍ NÒ“ÊqÍêF~:°¼4–qlí“ìÞxÄåt}÷¯?ÿv3_$*äˆÚ×B6h¸a5¨2Ò¤uÚèÇ£t”a¬ ’Ò2Ê/®&­µ€* İ6wy©Zk"‡ÆÝBËNF§… œÜ(Âú.^o¥$³–‰’‰ SÅÄDSTt”X6q!ˆ"ëu&îÈ'Ýׂï6ú½2#"Fûúö_©ÚÿLþ{CÚ¿¬ízD [Ÿ½Ÿæc8¿LW‘#Q¼¼¼äp¨ÿßüuôϹ+„†®K'WÄÉ[Cx¿1ÝõtC¦‘ó9¹ýÞ~½¼’†qž§ä“½‡XCóáwül|¾LøÎÁ_ ‰«ÙÿÈSFqXòy¬Å€¿²BðýÈÙŸOð±³Ù5Qæ&Ö3õ<‡·Ë t«·çÑ/ÿò)GÕKgÓ»"˜Ã{ù  9Y÷]/´-2¸žuj€VêÉ9W4z¹‹9zß¾^M½ø>ç,K¤ã²èt‘›XÂduGÔ9˜¾üó[ëÄ鯙ã9ÆN>Ñ)ï¹nŠÐ\µVàWyÆA0¤o”7çA6äfÛ´û b3Ié†c¥Ú.üÀP.Dø0]}ñH‘Û 6^cß,l9„ïXkÚoʧöQ´ôFѰè¦L¹62cÌØDê§ü>wã÷ÚÅf¯NN›¡áè‘îÆ§•'åÝ-6›“óí¹Ø„ZËïì³÷þú¹Sv›ã5Ý9×_þùÏßBÕÜúƧƒ9]᤻¿ª £eœIo ƒÉL¿˜A¢*¨¸Ø}kñ}I®â <¶”`ëm++‡³¢¶’>4µõjëæ=.‘![¯qR´ÛA6m‡ù Ló@šâ¸¿ËÈÆÁÏ -”™^=(ƒú|–k‰›ë)âÔ·Ë…ûÃ74{2tÿþôœóöyö'õƒ):Ÿè(!Ì8|"¤Àd'¬ÍvdE;ñüÉ 9,÷1Áq¶Øâ¢_,© w®qûÄIJ“LîŠu N :÷ Ÿ5¨¾Ÿ`‚‚B`#šïð|–Ý“˜yž;†M$~q÷"”¨Ôú€ 7ž_ˆôñ{—É Lôp}|xæï_ˆ„¾ ¯ƒã e}Žß+hËd’[¬—ró%ãkÞþ²&æ¾ÙSå&1Ÿ>'“ªØoP– @Ú<ë€u÷ß4³ó¤©÷VîØX¹®ƒFpâ°þ‚°zâÍÓ‹ÿ]À9¸&rÂá>âG.ï@=hî~rãtA8ewÜ yu5WA˜øª†[úè¬Øø„¿:]®T¨pá¹Ú/MÃ߬:{Û€¯Ã/(E%k‘sSÎÁ”]ûóÿ¤MÑ>øÃ'sÏ;/<û—°uñ¶; J Mw±õÑR¶¥åÁ=kpþ8#’F%Z©´Ôýí1¶ÀÀ¤*"ÂB±ü]±s› LèÃøCÐ^ãZîëéÉÀ– &£I˜çAÓÒ'´N SˆªçÎ㤴MþÀ?ç€?2¬1 HUÒ¤éXg¨};Õ…'ij§Ë¨Ó1FÏŠ>;ëòíôæ¦*z`¬;øƒ,nS¤InojŽŽ·g罩ýZ»¾±òHÂäy.•@î)øk!?žón¬¦oN#Mroü”½1–zŇîGf¨7ñè´’:à‹…I‰B ˆ@£Òdºöî{/‹ŒŽ}¤éžë/VZ&/oÈ¥Àësyzá=•ûß—ž_ȇ·.Û«—×óe·¦HÓ´Þî;7YÒ»‰“'·ö'‚MÆÍ‚4U³9±Õjes=Ö8ñÀ˜'qj­Õ^²©–¤iY»³@˜á¬!ä¸?Τ „ÉôÖ­i wM…/÷6ŽÖ)÷Ð QT˜ë)â wM•Q!D°u½ÂáÈgRJÇËœ"Dù½ß–ÓG7ÄP‚YõW‚èV©¢(Â& Ž{öÐ%¹{ú«i) BÓΕn¯º±†Ns²°ØyòŒæ­Ë.»}Ђÿ+çku¸ÏSK¤ x€ÈßÂtŽÉŒÔ:ÁDÏÊa¤ÝÚ&‡Ð8¹Ø&Wm1ÕÃ5ñ® ­C€´,„ ¡ÇÝ>PíußÔyÛOℜ†ˆ΢4Ná|wUÝ: x“†þrY4Ýöy)Y8‚õÑÝ?¯œþsm4õ¡Mžq蓮c9ÌzÏVá‹©»†@.³VUKí¬FÐÈÀ±þÍ.¿ü^öZIO“2Ïóív·[.¯{ù½…©ÉY½F èÏ|&8u˜¿“7¤É³¶ Mn:Î5"Ã&{‚0q@kš0Aä-Ä Ç»¯çö©yÒñõq]±\C˜”–dý2@IDAT H…¶¤§$ÔV ¿ üD(âT{»Õ‚BÀ¿d&éèåYÑôÓùåâ˺½ÀNs¤=$Ç…V*TÄ­5ß@]S8o”7d ù› z-M50T!RåVt4µÚ&.HT\×.?Ò/Ý¥A´í¶Àç 8r·Ÿñ™^á £CLöZKšdÍ¥ö ¤FDÖc ‚ÝHóßN:ÃÅуzÑ­—N£' é“ÅéÛ„¦t‰¥‰ÃûÑ”1™â|V›ƒ>[²‘¶ï;*ÔÜc÷¦Ù3ÆŠQü¦Ê Tÿ‚Ç99n™ÕEŸî®å{oG5™ø“ûÈ‘AYÞ– …g ½º'ÒÓÿï3‘ÏÇàABZ„ë>Ǥê0?ã„ên(pž=cœx/·ï9ByGN‰sšùØÄø‘D·á1ê·B@!Ð"•þM&C„1-Æltq~¢€kFZ,iî€`Z‰®Yߣ[ýßè ¢ñÞwôŸç4mÞ}Xì…¥bùÞ¦Òÿ¬¥gßú’ÆîC·_~‡útÛšTç4—–Ô…8+Žyß‚5;Äȼ$Zv‡]8Ä—sT’ž5å;ó¿–Ê ÷SóàE€ƒÅÑë¦ü NŠ;?Ï* ó½*›‹þĤJW÷èo%¸diI ôÇw¾ågÒA³gÂGÚ2‘}žÞ›·†VoßG¿ùñµmößÉÕ™áDÑ¡¯!O®€·*kù;¼tãnBN—x¶ÜÀÀ‡ò&˜§?ÿÎ7Ô;-‘žœseíØÄÖ,\O¯>z[í:˜ý>öêǬÁÖÓ Þ$LÖ¿]µ¾ý~{í>rð×»]þ ÌÜUKPÛuZ‘¦ÀÜNu•¶#ЃHa¢AXräw G yâ$o ÈÉÅ£3éKöuê“–,WÓÒ »éØ©BzîþÙ¯é¥wÔn“ ÎÏà):Íùí È?ÀfSF¢ë¦öÜ],Ã×ã©^Í¤ê ½?%¬Žd)÷~5+ìxšÍ—ºsYÎþM™½Séñ\Î#ìzâõŹà?…œ0ô”–Ê๯Z^@Ž^šE&Îç„@o™DY8¨í+—D’Áƒøc-þô³›D±®bM-·¥æèÂSSâ¹ÝsûßyÅÄÚÃÐÉÊš]Œ6ËÁ‡ÚjA! h$9 òÄßš€§jN¦ŠoÛ4ö×½÷†iìÃ[Xo0í¬3}ó‚aýš¬ËNXÄ gUµ•ö°•4Øqû2¤oº8æÏÿG3Æe‰ 5sháA[‰³œûÝp 1Òôڥфh­JÁ†'Ø®uE çÈzç3„`»­(ÏU“‡ £lF$¥¨´‚G¬#ÙœN+ü™ yòUFr‡nCÎJÚŒ¦Ü æ݇ØAÞ"ü›`º„œRv䥃ì_*h›ñš¥R6GB1h´­Ï–»‹9>[s’†â:;8H¤©2ˆê_È €/í¦EÒíCë´ŸKZéyTm÷û÷·]pA&Išü} (ÒäoTÕù: ²#yÀ¿ñ0ѱTÛ„‰9,)DæôR0hˆANtÂ~MÉF6WÒ7M%$ΆÄñ÷ƒ”˜¸õ¡„¸(±Ü³[צNÓÞë%Ö~¿NS¤ÉÓÔÛïU'T´O ú­á*oTÛHD»¹fʨzÙ®/3Hš‡^|Ÿ¾^¹]ŒPùZDµËàúµÑ|—^zocÓ?˜+ÍçèO¾ñ1k>dhŠGòNhÀÞøp!¿Tßô$Bd?D›uÁPÎc³‹}å#Qf| ¤ÜĦ‡ U¯óµ~ÿÖW´}© M•A§æ¡…À¯&GÒFšj ½ê¨~øU9U…yª-¸ZP(‚ Ù¡Ç< ’‘š$ÌóþÁyØrä7zíK/"®ÎcӻƑmwí?Î ³{3?øÃt/¥]ðU¤)ï´*R‹ ® UÁïpQYE‹Ç„â!mª×˜É¢×a’Ò¯G =÷À bdKÞP¹ A0I–çïOÏ‘?Ù¤.¡Þo8­ÿäÆé„æQœNޏ?sïµ¢açP;µ¹g¤ÿÕà>Ý9TúEÏ2\1i8]ÂáÌ1r|TžÒ•잘sc³É­Áj® žÇ«åÐA౉fâÔEôæF‹(ôÖSvºù³2úÇUÑÔ;¾þs:µR%U(‚Àú8á›øË»¯ ÿ|³šþòÑabw›ââ›&æñ×LM±_Ó¤‘ü½–Òš6ç¦ömÖvw€˜îA{î¢HS¸ßáð®üŸ ’_P$¢S‡[;M¯Ì“°´õ&‚ä€À4hë…©!µ`bŽ}+ü™;F–ޱ’4Éu˜7UÏ}Ôrè ðÓ±fzjRd­3±RÝòY)m<”£¬¡¬*©B s!Ð.ÖB“º‡n›E÷\w1à<‹o}¾â¼SLb%ø&#nC¿b˜æ!ÍÇ+ï-Öpa’æzç(8Vø÷ WÝõE)q§…B0!ø4)ó¼à¸Éª-#àA÷D˜æsê4Ä©åÛÝ>{§Á'ÚçJꬡŽÀÃôÿ.&sßoiµ‹~ôu}¹Ç}/Ôë§Ê¯Ptâ=§€\´±‹ŒáÄí0I?̾½öâ)LD¤=äRÜ}¨.EÈ9öKÞ¬@øóÈèF˜²Ø×i›ýÁ„/x¤^pˆ6Ë¢‹2Ì«ÈT¤©ÍHªt$ž‰pqd½p”6Õ ö¼çvc°S•/ˆ@´¤÷¯‹¥ŸÌ+'˜l°• ýri)vÐCTXî ºUª( …@#ìåxEe•Ý6I˜šïÜw„sÆ6jYSz«5Ûój-2¤I´ï¸|"Á"‚¨³Û÷~L[Ø×é"Ήn’tõ#)»â§¬vºý]•¦)Üîpç©OZb‚p?« ¢H#øY¸zRÄ©ó<Ϫ¦- pääY‘ù\Isþ/ø¤µU0ìÁ€xŽìØ•#@eq(Ýžœ¹9œ¤£OfÇÒ½óÊhïYŽQÎò÷-:ÊæœI>${Bþ”Ù-ä ÅÜ©±ÖÔ­­!(|©_su÷e[{ÕÍ—²tÔ1èhâë"ž±4N´]h¦£Ê¤®Û9°ñhϵ;9Ça‰ @H¹qýô1M‚qoÛÉ‘g¥ š(IÒ„õ1Q&‘äæzáFœÒï»[dƘϪµZÁšMšÚ«ý †oƒ|¦š›·Wý›»f°móç÷þ‹É]bÅûzgs½ÞÝ“‚­Êm*"Nm‚OÊ`DÄÆ£!Ë6åÒÒM{xT³R¸Ø5&²ð7Ì!òG¶½†:ª&“ó,é]újå6ŠáîŒq™"ω¡&Irë¤Dkèƒëcè‘E´ò°›ÀÍßo¥#ìûô:Û¼§Å´lek·;DÝs"Ê2&‚0ÞñwÝPn_ê×°¾­ý-ë¶dc•–W B½VG&•ÚZO±Öž5t÷w²'¥Ëáb ¥ƒ£‡nIƒ‘GgÚØLÂ3í·…@ ÒCˆ÷»N¤éÐñûˆÔR0hä€ ë8}ÇOÜ)w¡_ÿøšÚeÏøLyÊ›¿¼ËógH.÷{a}ªV‰ä’}P­ÆÅ>M1íîÓ$ÛÏ¥³©¤¼šÛéµU¤Õ—pëé?sHÉiãvÉ,¾}±ÑFš>nH³ß¾@ÜHYõýp£íïïG:'ÂÅÀ aÉqrãÜ)ÿ#§Ò;_­¢¾™}Å–=œôöªN‰E(W„ÉÉy¸r§÷¬çŽw%R¨ 2“ yn×´OrA½ËF]m”RO_,ÛBË6ærâ× „ª±Žm¤ACeŸ§çWWÑÿvº#îåœvÐõ—ÒŸgDÑŽš.'¿;o•p8Ð@Ô ÏCkëçë3„º½7o­Ðœ™8_Z×xö 㨓ZEÈÉÏv•ÅJUVúrÅVZ±9—n» ÏXšÈeרsæë}PÇ)šCIÜ•4ÀÀ¿ìéNÛ2ò臽4ärN7çi§÷ßôA~Øân?Wsûi!³y?%uÝÁó½Ü>¸¿1~¸Äy§p² bUÕ@N 3œ¿}Õ´œ¼î¸âÂ&¿}çÀ+Ô÷£i0ýõý@€ˆ-Žùœ”ÆIÜZïþ¡Ã-\eçÛ»#Õ^Á‚îáRn¸¿â¼^ºÚ}!•èÛß´ „¬ "]Lqö³4 *›Þüd]?m4 Û†8!šÔ¯.2S¿-=¿ªŠli A#îc¨ûF›èAö{j¨KX²!‡æ.ÝL•\·½ªÊíKýÖ·¥ß²n蔥t¦@-Ó™¶ƒ{?™Ø÷à²IÃ(;ïg=¢}GNñˆp5MŸE˜í¼^=:ÃIt¡¢iúúûtÖF¹‘#É© üÈ(ˆÚ–èÉ”Y¹M§Ë)’3£SÛ˜Và–!FÊbß§‡VÐÉrwt*ø=í(°Ó˳¢)ÁÌã•üÜ¡nŸ-ÙÜ¡uÃóÔÚúµô zÖ ‰®»°– ¤RIÓÀn=¹k+.gíÓ6¡ey‚ïHSÏYÓgS[  Ðiª.8ðItø¢ \â¼sx¶ŸQ‘9ÔµëçÜ~¶Ý‡÷¼ y±d-%åTXx=ûˆµäÍû¼8e‹»xÖ_}?Z„KìЖïG<÷u1€‡$¸À;[\FHŽ.¢†ùN" Ð[_¬æ,¸±Ÿ.Þ ¢`ùs—s¯sù§mÌ9€Uµ‚`EìlÁðþ´hÝ.ÚÁ䟙Oo¤azŠ@1œó áXßgó"³$vœûã;ß “šÚ“©…vC&4Mðg‚ò7«v b‘5¦CH“¬(Ê÷åòm‚\£œ(oc24EOŸßK“zÖ™è­Ë·ÓuŸ”жS66=ÌçÑ– ¨ÊßÚú5Vg¬²nøè%&(ÒÔV ׃\/à†çÏ?ރ枳†çP¿ ÿ Ði²WÝyøÅëùç 知®ýÜL MI‰wi’¥iC9Pž/—omñÛ'óe^Wÿ-¢TßïQlË÷Ã3ŸS¸iqâgh÷Á4¸ww­çÊ‹Fpd*w†sKµ?M×N-2–Oa’§xävöŒq4jPe²Æ)µIí{ÒvÌ?¤]ÌÇ€¹CIÿpN¸ç°{±Qýk7$i*¯¨bB»E˜çAÓ,‚²Thc胅ØÜ*:µM•-Þ¤¡^MŒãÌ5;”»hÎgÅô¯Ö ó¼`ªŠØšú5VoDÄ Ìó iRÒz€Ú«Oo&¼’<µþLê…€BÀiª¶.÷4Ïs¹œ7xæây¾œÏÛcÜíçjn?O M“·Çb?h¾ \®¿[×â·Ï×ò¨ï‡¯ÈÕçË÷Ó83s=EœøÙ(åŽD4k…¤ÄG»óåTTY¸ë¢X… ‰g²ã)ÈM¡­±Bä*k HàeôÜU,{Ʋ‡3{9Ÿ_Iû"Òó¼j«U8Ê—UVÑ>ÓÐÕ45¬143ûÌCÅs¸lónQ^”»)aúéX³ P RìSLÉUÈf©¢½AV7Ô¡µõó¬7p€·˜#çáýSæyžèx¿ Üb#©´Ò"Þ¼x/š{μ?»ÚS! hZÒä"‘„ >M¤Õݸïñ_7w\[·ÕµŸÕ”ÿM‡kšÖš§.\.DöóæÛ×ðø–~×Õ_}?Zª¹í¾|?êù9qHòpEœønbmÓÞ#'9âKµ°ÅšVnÉ¥Ç^ý¨^> ¹½±ùT6ÓCàˆ'ßø„~õ·¹„„ªnü ½óõ*z탅t›ë˜)i?0Úd·Û…¶é౪䠆ôö»`ÏŒ²U°FàÐñ3¢ÜÞhRX úûqUdrUÓiŽØÌÒÚú¡þ‡ h‰2GsÕZU¶Ì^Ý…ip«òÓÎÀ±‚Õã}pkÜþN~:½:M ðÉâõaP‹à¨BG‘&Ô¾®ý´²E͎उR |å•¶V}ûš8Uíêºú‡×÷£¶‚°ÐšïúÀ)ìÏ/‰pÃETT=¾“0›ûÁU“8ßÉöÐÓ ÿ©3910ƒ†ÈN§¥›/qçX¸}•0I¹óЉr‘³&ÇЯŸ"6’ÿá8Hjb,Ý8s¼Ð^y&¬=P-ø 9ÚdµÙÈ„i/üÏØ\ yš‚UDÙ¸ŒÝÍèžÈÏMóÑÏd÷:îuÝßGé±ZN—Jt†£ó-ãäº2̹/¸\®§œ[ªÂÚ¼v çnMýdÝræ#T¯xG})_k™Ê c/*Òª|Or¿Åëw MtKçêÅ÷ìÄ™âGÖ÷íÎ&3ÚÝ£øhë€'Þ$#5 ->g-Õ[m.5P˜Æfïϧ¡< ØPì½ûÍÊå€J0SÎÖ)m2hǾc<ȸ‡÷Iï ¢7^;uUrd­…k³¹ón¤™†Ð¸¬>ⴧΖÐûó×Ò±‚BþfÆÒ­—^À˜IbÛWœƒlëžÃüÚ9šmÝqùDÊ;vZD ËÇÏ[µC˜í^=eMÚWsôd¡ÊtŒƒ9¡Ã6qx?ákŒV›ƒ£n¤íûŽŠQð±ƒ{³òXj® â¤mø×‘¤©~ûéyšZªÊO®[CqÑ=x7“˜ÊË_B‹7=Ã}ߢï%Äôâž&:S´§¥K‹ò! ¯·ß¾–NX¿þû~´T®PßÞÚïüœN…a"\¥qòx’Ašt4$ùil{SëàÓÔØqŠ45…˜ÿÖ»N;ÁÛ„YR›HÚ5¦vKnë’#ÊXÈ‘QnwRP¦DÖ±ÃM·¦nss«éæÏÊè?;xÎaÎgô©‹Ò×Ôµ[oÐiè/—ESJ”wÍHkêçY7– $^ 4\o¹‚¾ãH™qìß8yäÀ¯Œwzöô±ìÛXç+ÙâA°pÔi´„÷æ«ÞÒ7Ï/ÞïÛyÔÇ¿ùÉR‘/içMسárHuÖ0-å¼odh¿jòHêß#™GÜ‹hõŽ}t’µU-kÀÅûÀï…7ÏY ʤ®ØÙ·òÖ ýæïsiÉÆÝ4kBÖy›Á9ñÌ#7 F”sgíNÿÚý®¾x¤ˆ kw:(ýz/»p`"Í“^yo¡Ðò`€á ›áÞ7{Ű&js_ûßwTÈçÄ Æ‹à|ÉÄ¡”É>Èž-SßôdêÍÞ_þåSqžüŽÅsð“éã2…oñ°~=蛕ÛhÏ¡S4*3ƒ6ì:À‘q{Óå\)È1ÓTÚbFßѤ õ«×~êZn—k1©.¤“gwˆ©_™4¤ïlÊ=ò-»˜hÚè_q["¤áußÐò-à¶ÎÊmØLÖ÷Úž÷>Íû;ÚŸ¿”¿¹E”Õëj&»¬¹¼‘oüµX/¯ÓØ\ËåôöÛרñžëêÕ?Ì" !23,ΕzV9`Ë­ù~xˆ8 ï[ €PÄ©”'³¹‚¿etf/ŸR¯ #‘èVspwzw´ÄfëðMÒs™ÝZ”õhJd«­vrPë“øBcÔ¯‹ŽÖæ»M1o¦+DÐß7¹G'2ÖDÌ}e]%™™TMìa LNÂûÍ>ŽPÈ&~ûÏéhLw=o¯¢ãeMGô,¿·õó¬[ µMžeÅr·Ä8aÖhåÜGè,ž:SB[srGBË&J£™ìQ6w1ÒŽ„ÖË9E G„ }’h¯Û¹ŸŠYÃc«Ƕ©´ƒÍÌ[½“&@“† OÙ”(<ñ>à½ÀûÑÒsˆ2©k —NFóWo§ œRÃSJxæƒëé@~ˆrYÄÁèÒSzÔ :F׬ïÑÍ=)ã]ÚË©8ð>ý÷›Õµ‡"×Xþéb6ïÁfóÐË·Ð÷[÷ñÃpš:vpí~Ý“âÅrk´Ìì>¡ NÙyù´`Í*8W*Ì屓ÝaÑ#ø©gM9䉚+ƒ¯Ä)HêW¿ýtkÝd½½™ë´”7ŸZ%vŸ<â ö˼†Öî|Cü¾pØCŒ­…Vlý#t‘Lb/¢”®Y<8ô%í;º€ •Fº›ö_JÙû?¥ÂÒ¼/«Õ =²û¥Mª_ÿÀY+ÜÓtŠvG_.ãP˜—.bSo¹æƒ½Ói?ïßœh¸íFÿ´¸¬¢ãˆS+¾xGÈÂßš*ö­-ârCóꢈS¨ßAUþóÀ4NèahHCE\ }\niª‡º ÁôÏ:b¹525Ã@ýt4:U'´LÿÞæþ°^;(‚æåYéß;ÜÄ©7“ªxˆÄÄ­ÅËk«Ä>ø ­ $›}œ¹ÍeÄŠþµT¿¶Ô­…K{µ9’ý€®™2’º'&pï(Zµm¯xžpð6÷Á½@hôÓ<âׇG¾‘ø5ȉ³Åü5mÌ}Küî×µL7 â„ÐH­ØòGÞç+±ÿ *-ϧc§7Ô®ki¡¥oCKÇc{[êïÍù[ÚgûÑmesÕž)]Y:˜Žœ<ÛbTJ|G¦ò@[KÄ©¥kj{k¾i<Ðq ßMa®§ˆS î’ºŽB •HU½›U–†‡‹ßìGZtò““ì/#qð!kí½”çhõœŸ˜zóœµúÜê€GdÉÝÿ1w?—uÕ)â…±<¢óahY¡yÖ¿gÝ^.!Š-Þ§ùß8/VtßÖN:Âó•»1•þzsš˜·ôëáÃØ6;ýôìa§ˆ²·ãK"Â^$¼D*_,e‹„¯Î).=Åîúî׋ ºpå“äàŽdqY•ÈSó/p¼ùe}šÅ)–ÚÔÊJz ÌÍ7Ò ·¥ 9ºØEíL‹ßï¤WÊÃ[^:š$±•5ÔãkŸîùà†–y¿ýv»˜Ó„®˜û$ A#ð ¥û¶9èÝzDÎÄzŸ…²6¨mÌ›À¯/B¢Î‡ØM ¨/rDOBT=h ó‡QÃ0Y^Ïä± úç†÷ÅRþC²Qü4Ò4 i ¼Ìœ¬¹¡Ð1M4uøÍt$såLÒqT&µXš†¢—‡_º2ìå^¯‹^Úz·˜Ó¤ão"êIB@ˆà rÅéõ2-“ÖrRØ—vÑã 5/ßÀÁ8Ñé^'½ÉÉcA?[i£2¾¦(CO·Ï±ÐwÞj§¯-µF$eõðþé“çàüd#É;–}‘<Þ×1m_l4ÕK=‡»±©I+EÓÀÈÐ@Ó^n£Ÿ04 K“§£éŽ?]ñªš$”ﵚxŠ”ÉûP¾I²ŒHëŒÇypõ¾‘ƒ™ÀÕî{põ–.lH¦Ž`%ßøäZ1'ö¹wöWï›W-súÀ¼Žñyj¡hÚ¾„”ÉN£8atx#»æ¼ËaÛ8¹¼<:Nü©OTRÑp„\”â«'£ŸÝ¾8£y‡“\ÃYÎW_6›]yFým§¶„ïŸÇ _An?Ù>ûè ©ÔêòÓO6vÒ•ŒôÀ‡ítªÕG%µʱêè[ ”ê:}ôÐ>N~×Ä#“ìÞm’Ø„ ­1 i@Ó€¦M½4 ASÅqòŸ«¦ò®ªßÜòF¯µQ«‡Öm+{®Þ<è¯ìÖd±Ò;ɦûrõþå_^ëUG¼§÷ª4ÊBŽŠ)Óm ¼0Bó½EYdBOÕ=ö’ÕôÔ»¨…}/›LTc›M ¼ôèzG Šå2úݔ㮡W5½´±˜“âŽ.V(1%–õie)cØ«žo2Jç¼FóyÎS LØ\åᄱF*J×ÓyÄqÏY°4­™b¦[f™pBavÖtíßÚÐÂvY½2ñ'Ú$±¸V#Mš4 hH¼BA“^ç§i­û*_ÿÍWÞI]üC9BZ€µâÔ«ivçâ|~ŸH(ð¤YŸúà2q¬ÉK•¼ä¹HÇyYÕìåÄžœOÈí$ŸËIÞyÛ|4!½ÿòBþ~CÌq Þš#ì&ó¦œ=ë[8ÿ‘¤¡IehKMš4 Œ$ —UÑ~þé9’Â|ò€|Oϼµ‹ÏžDÛTˆ0âW/šI“9?QÉ0ß8Ål¦ËæLæùãiëþr:X~†N·èt݃ÃM}¦ß]—J·7QépW®•¯i`i`L^}P¡t¾.¹ÇF•Å   îyÿÜP,@S‰ý²„?–màc.Gj|yÓ*àè)—L/„ià‰( `KÏñ¦ €Ôw\a»§elɪãÉÁ_^d¡ÿÙ飫'™(Ÿ­M[ªz€ShåƒNZ¶­i@Ó€¦$Ô@3»Âÿå•­<ñ}ãoêÎ9sèø*?užn¸z!•W§'_ßN?»û!!rÞ|xÙ%ÔØÚAzi ýæ;Ÿd°”Mȳdîd‘|z¸U4]3ÙÄÀi¸k×Ê×40²4‚\æ¶JV GpB”¼§yNÜó`iR[{=óöú g¶ZÍv51–°Dè%sÒ¥fóük§2òΘ˜[ëȧ Ï´*¤("ŽåyH“RMTÄ-³ÉJ6—ŽUèôó !D÷]¯·Ó¯×ØéíÏqn .ï¡÷´ádxàœ$öÇoÙŸ›©ƒKh”|˜46—ÎñHY>êÉ'•Ʊ¦áÕ€Ýj!ü6°{üõZ@S‹ò^1:-a‹ÒÄÂ\ayjïtŠD¡ó¦ÑñêZjmçqüWu¾ž¦/×Màs‹ ²e ÇJ i8êÓÊT‡fN*$=ÿ•U‹š¡»o]E½´YD*úâ!^‘Js§Œ¥ml½ ¦¹SÆQ.Ï-ÚR|4xwBÖó™xSùxðQ«1Ÿ Á0’‘F pÂÍBôDÖdÔ4 ˜ŒFú¯{n¡wv¦?þã]úÐ¥3èk–ˆ²Çp´-¹Û ‹Òüô‘—híÒ94~L®Hx;”ÐÔ¢‚(þi ) e°SëšÚb>,!ŸÒ¢Yiý®Ã““™ˆwÑÁaÞ¨|H§J)œ Ö[5ú²Ùé";ø¹ÐÐJŒLFÀ ]/Ï{ÙðþQ=/^ ¢} À¢ûm9pŒV-žp×KF—½péD—#„~yðŒ@‚×@²ûþÂyÃã­ ’‡¶ÚÁ )B’X5Ñ×—¦Ðgç¥P[—~»ÛAß]n£;_n§ÿZe£C5úï!oÕc7¦Ò÷Þé ß[n¥k9§4ÿæ17ýf—C¸SŠ“Gè?¸}òÚËÉÌi>{Ý2á.ô ‡½iåBêäûîž#BrŒ’há zä…¢ýÚm«é=žOyåü"±a»#mæ‘?nv„ªKKÓ@/ 8xdVÚ›V-¦\î8mÚ<;èâoøÁŠ3¢ƒ…ó+N] ·Gq@ÇËÊ#Ô­œ6„†Ç⤦^·Ž¬)YtÃòßÑøü%t®þ:~ö]š:v5=·á2­tÕ‚ïÒ̉×QŠ)œîV$†£§×ßJíêÉ7Ô[¢þ·fN(Àigóqúê-+¨ŽÉŠò³8m=­gàãU—RFª•ÆæfŠA´ÐÒ®¼t:Íž4†ÊNžgëO¹p)?½ˆü¶Óé¢W6\²ì’i”Ê o³Óì"4÷;»sð„6ºócWÑéšFÖk!!¼ùU gŠ2›ØJ³õÀQš=y9]´›ó@È}ŒsC½³»„.¿d*ºÐÀüÙèÚ+æñ½3³e§ƒÛ+¥¯2gòXZÈs ý¼½'æsoãŒ35Mtż)ôô[;‡}PpLnF8!²žœƒúV`mª:[Gí9>G} qTc*¢,þèœd~§OÃàI§j—½^Iç›ë'ÝX¢HœÃç÷ºÎù:[ÎŒÕ7NŸkÌýæ Ù¢¤'³áâÆ5HeÚj”˜™k o,±Òº’.*>ç¹§Æ¥éÉÄö¾ð³-àtÝT3YYÿLý;ƒ«f˜é‘}NQã½K8߃ÔßîBGfäžRž¨Ž(@›xÒz »ìÖÞ#y³Q4ªRhÀ>²|í8xLtþ0gã\]“Hd(ÏÑ–šFºš;èϽKÊÇàÃM+ûêp)‚@pˆïÿ~w<³#` XuÙ,zôŸ› ó%~pçGcª: 4]¬Î%³¾BãòÑæ÷“Í’M+þ5µ'Ξx#wè?J}ëF—»ˆn]ñ=ýö-I š r Éj€’m 0^弛߸ýñm?xì´xö^ßöÁÅÊâ=z°÷H%Ý}ëJ:XqŠ ÜCbÛgÞÞEU ¾‚ uædÐß9HÊå,eÉœ)ôÆöƒ¢|$º}ä…M46/‹¦±k+<઺bÑlnO*è#WÎÀi»çUr¹¨Gº½!J .¼°aݼr'¯„_ÍÜÇ^ÜLYévZ{ù\úÛ›;ùžÒÀÀðÞßW(ô`~c±^“ÉÅ(ÏÏ…ú–X™2F¼ÅIZ›J*«Er[äiR3 þ¸(;yŽ&²óœ0Ò&_æDñ€TésÎÔ»us|H~Ý\N8§@ê6u/°Ê~t:•Žt%øù|Þ2oÓÙõ/þì\Kù^ /\öíïý~ú˜é¹sº“φ)¨ßÝÐWÏX‡Dÿ'9õ[cl™G“A¹ß’ÿp%Ëã°ˆ(rÙ–3æ^ýt³âZhá¯À¯²‹j^+]f¥……FQºvšI$ñå †t3ç«BBß'*Ài2»I~œ÷Eœ"•/X6b&’0ðRÓaT£qÕ;Ê#æ{K*ûó§OàN`~Âô‰†Q©g,5Ò40À|¤_ëv¶´zƒ ’îÿæír•2ÓmôȾØþï{oV*­`úw¯»rx~ƒ÷u}¤&ù^£mðù?dRá‡x¾Ïk´¿ü)¡âqlyJµæ‹õ …Ë©¶©Œ::kÙ"³Uì3›”vd0÷Ãï·DÜö T~oùß~TV×)srx°,­CÁ×¼O]{…Øõ§—·ˆeum“ð(¨mj ÇérSMCK4}åæ•âx{ç!q>‚$ }9]Ó@ó¦ 6û's°šÛÜ/븎n`‹×ºw›DŸÀÀiÝ;{)-Èo\~6í?zR̵‚U© sÓÉÊ*o_»TÔaà>†¤ƒÇÎDš†Ò~Àâ$INR*\*ÀÉ+:;]ʰ&·…øÈó>áçív»y„Î$BµÆ«cãÓuã¾üûÙ¶‰—×[S§ùI?' yáX'¾CÝó{Â~“ äþX©Hä/eXRi-Ý|çd¥ç­( óš¬üƒ³{™–”‹ûúß—®,ü¡°ø‚«íëÊÄï)fK/Fú’'xŸMò)¾ÈGoæ3(Â\-I¥õJhul#Äûa¶.]Ë–¦F‡fæèÇ›:¹AÓQ&'®àÈ‚’Êê v/Oziæ N}ïƒa®zÐÅ;õ6µ:QS,œ—ˆeˆuˆ.îèžäNòñF/àß1δ¯}µä]¾ÈžKâk t…£Hì¡D¯7”ö„>Ê­•¡]håÆÖD2úݪÏàÍH.<ݨ]ºÍÂr9#–í‡t¿if8+´÷îð¿ÎV¥Ï\’B5<ÊÕ ¤ ýšv?MÊìiØ&ò:æzE š#dór~3ŒrõL†öT íj»ð™y⻤<ž¿Jr~FèþDmCÐ'Þ4 Œv ôšþ÷ÃvBÈñ‘@âûé³q"…ûÑ úý^’]ÔÆT‘f+ ¬=ýw޿ů¢³u{é©·of+Çà@ øó2Ÿö ‹d ¢!¬Äºý¨d«Ï•ÜóžfW·`бyÓÆ³Ýž›w^Ì/pêÐò~ö#Ë…5èÍ»ÿ•²çÑ´ ô5vlïtÑvaæ´:ßX| ©Pæ×ŠÝÿ± s±Æäð|@D¥$òpxëþ£bŽ®‡û“­íôúÖ‹ë .§¯õ¡¶èÇæe¥²N‰yà3Ù¨§ÕO6Î#ä<4pð*AÛ"¼2q§y¹ûÜÕåÀ ¼C†ÁR0@:ÎÖ‚ãMKB2׋‹À²w/:P±´ ùÙÅN_ €ÄN„%ùÙ¶² ’¸"Q)À.¯`äIþ²í&ªlpPŽ»†jÌEÁ§©f¼á+W˜eð=sobaí*=±l{ª½ôõ%zOf*>«QˆB}opЇï_Éó™¦[èuvÝ“´•ý®b¢M'âcŒõU=Çåyá–ÑÊ,›ƒ%„4N5²Öë‰<Ï©ƒGùàá »A,7U„{ÅÈæTvÃS;A,á}ï–imšÖL¼k›štØëûé˜IvûÁ¨Ù«º°ƒfŒ¿ŽJ«^ &1Hêt)ÑÛ¦Œ[ÉþL*>ú¶ftÃÕc]¶"óÇîÿQµ}ÕÑKþÛ$Y–ôË¿¼&Wé)Î1&é¡ç7/ `Єc¿{f½8õ¢­!Œ~Uеbgп3"4Txp½(ç•ÍûÅÀ9Ð%!lÿý}Cnöª§¾©žxu›ø¾K>pâòÓâ L©ñô;eD²‹ö£ ;#œ`eÓ€S$šã9xhðƒÅ)øŠ#ðCH@IDAT ƒ® ‘O"¢êAj4œ°:!2¢#¹ÝŠŸ8BЂðþbßóïíÅ"a~9i\šò>È÷¢¿ç,aÌjk& ŒÐ$Û†YE¹üýôq뉔’r**­6´§G_¾šæÒù{×J«ÿD'£!…>wÝ‹ôÖ®ïQÉÉWpÂ\§• ÀÀé®#ò¶|98KÉôÛ¾PkûÊ÷û¥'Cw©z;VíGAwî6‹ÀÉH#8¡ó¢üøÖðz2|IO4¸©±±‹Î»:éT‡›]ì8ú »ØE# ÷o{$„÷ž–ed tq˜ïã{:¨ÆEoXÅ Åh:"âgd+Ž‘ç¢ŒM3ñ¼Mï཈ä9S“/š†¢‘š¤n.þ~ÖQcÓTXø(?£O*ÐL<™t½"ÏSº} e¦Nà°ÙkE”½h@“Ïo|™ þ¨Ú¾`^úZ¿X~u´Á¼bNT²P,Û$Á•TÇn…Á†¹_íË œ üxRûáÑËžy«ô6žIÄÆ½‰{>lGèL¥h:"X¦g9éÅ)\#e¿qR@“‘̆ÓÌ– 3ûÙäik¥Ùû©Ä®d¯O´ÜàÅîk¥)<">Í¢c þ¥,¡<ÊýRFkŠ…Mâ¹NÇêX¶,Ûe¡—„Ýîâ¯âùvy{ÊD+__²-œ”-dkäHF¹<¹T£è4ÐØÜ&BÚNJã€ü(ïÃÀÏYtµhgkP¯F hêûû™ËßO5ÔßJyyÏ ú&¹Ü-Ôî¨å92ôêöoÒ¼©§…Óïàív:]»‡ö°«^4ÔЀ°óù47жo òû–_k?Ò[ÇcÙ~Àk9[8`›—çmÕsž·d£œpCž’À±à;„÷`€¤¸Ø^€Ôß=Eâ`#t-œmÞÂ`Äb±P–ÍÁ‘àxNëÍíØGe¶E ³<ÁP‘ë>GEœ€¶ ÓÞͧôÀ?äG¡2ŽÏË z¶ÊPíY–ͱl¥u^ZýdìMæC‘/œlÇj;¨¾ÉOÙfµÕ„SÙ¨Û/F 4at3Ïâáçß"Þå}ˆì9uJÓqP9hÍ=ŒÆ¬£úýL1×»suÐZ¢ºORN²<í8ôûÀ³qòÜÎ-´%°Í ,MMsÕö TW¨ü²mÔÚ4×ûøpµp×p!²^u¿ ±*fïCPù1YÀi(šZVd¤Cµ^vy(åD{-ºËcSu4£ÈD³Æ¥Ð¬B+ÍÈ5J R´ºÃùÒTà”€)ÅšBVþ9)”os³y˜Íä Xµ·Ñ1ëü¸Ïyœ¸çÁÒThóÓø,«à|‚_ð käG}É8cl¦ÈÿEM‰“ üU¾þd«jê"OC³È€žÈ9Oáî‹ZöÃ'îyp/Ì1uñsoägKy¢yÎÔ"Ƈ¦Áh @“Ëë÷v:ÝÂ:ühY„ þ~6w£Gî'ËÉmG¢ëÈ]“˃O¯G=ç)Âêû= sšØmÐíÎtÛ×o|0X~Ùþ˶Qk?Òžr|8Û¸ëUprx€“³Ëíç`h˜SJªO#8 ÕÒ„Èa¼>•n¡*»†É)ô1éÞžÎîv?^“F9ÙvJOƒ —Yt¤Ãœ®íÑÌõ&N Àd·ÛÈåt‘Ëå⎤›ò¼<߃œÄ9\éÒöÔd,ÑöLÖç yŒ’» «š²<5ĵyØÒÄù+ìVJeÁ'øßÒU/D´Àf8§¦“YßL•Míq“ LÅR¾d;ɹ¨ "…­‰v«™}íÍ<‚:äþF@·Éº‚¹‘‹èGaäAìB“‹2­†î÷ úç,Yu¡ñX H7yÉÅPÛc”ƒï‚$¬oËýÁË:žÓú…—ÛDboì7ò8ò4©1ä¸Ïí­mnaW3X†Á¬Ëïg}K ½Wü]ÒœliæHOîÏs`/tæÑ…Ú/ówó8·;ùÛPÎ`#ò©Ñò„ß@eœÀ³±µƒºwrT6¥,,ñ;ÂùLž'‘a§¹SÆÑı¹â[ü½hú|’€& Çsm—®“ølÜ … ‡ Ð+Ûïb•w Ðd6dÒÒÿAÞ.å¶*mC£k2Õ1Ññ‹^×Ic /c´Àç7“דÁ9#mÜŸáÁLLÙ1hûÒÖ~ ¤¡ÞÇeû%f‡øyÈÔw [ûQÐ 8µž{GgG=s…/„üõfRE[±éªH X±âñê8h]6ÖH¿Ýå ³m øAD±Ò/ýã°‹ÌÜIûÃõv:ÄÛÈ…“cÕÑ'çZé/8éç[:éÛWXÅï_ƒráÄŠ?­œ5s=¢‡a>,9nMÁáÝs¾“ŒìÒ”íí$·ëŸ<~vó!áiwK=pUaÏ@‡ß¼hFÎ!œÊA>¬&høaYBg6##ÒÓùðÄ|‚_ð þ¢døÊw8©Íé¥v¦Ôåcø:Ä— e‡L¡¨º‹Ž§|‘Ëæé–GT=œ|°›Ë¡Êv‘°*Þ¡GLJSÔ×Ö“ƒƒŸ¸y@À6i,ÙÆKÓPŸ3‹®±–` 0,y½>œrÓæýGiËcl]`Ïþþyt)äÄ@T Ð ¤øÙÊí¤×·¤4¶Ø¯¾l6­^2K|[ë;ýIšøúkªOoOÏÉùöáãgéòyS†tWÏÖÓó›>ÏíXƒ!vÎÈ¡k.ý-ù=éÔܬÌ)éiÜÝßO9¨!VßO1˜Å¥!°‘ÁX¬Û¾¤ÆöÃÅ}'¿# $¢µ²Ç„퇙ï<¬Ž–ÈÏ <^Ìf˰´éü¾ÊD¼ì¦Ç`]§;s¬üÖ:Ú8ÉGQ *êŃœz©£gÃÍ#feõð&:Ìsœ*9o’¤':ùF+QìÊ9ú݇&IDkÙâô›Š«fŽMO_Y˜"/Ó–qÖ@`Ô‰LÐJ2a… å£Ê®|FvØëöý6sÂá4¾ïèÄÒÊ>PŸü›øƒ‰¹&pσ )3#C¬L!ªÞ@Ö&©ÊHe4›”ÎþíHª¬ŒÇNÆá’O ²I='Ãúr5ûè »¢šø:]ÛLS&Žåg+}ÈÏY2ȯñ HÀ„A©’ʳ´î½bjcÀÔÄ.Ï5¶94œ®Ï6Ø=ø•-ûiSñQºqÕåô‹Céìé¡èCÍîyÌa “¸ùõO›iþ’ÊlNƒµhznÃçØ%½ƒÛNv–K7_ùWèͧÖV%?_pû—Œmƒrgûÿ¯Æö£¸ªŠ­¯JÿpÎä±ThíqYë_šá?ÚÓŽ+é*bÙOé‹ûü¬4ª®U¼óŒF]Çá}»Žóyèhãx/úº6Ñû4àåÈã$ª?]a£E…F¤ӂT=5;{€ññ €P•mÓqÂI}ÀbeuÚéCÔ€l ,ì2à·+÷ #ab4…ÝáiÏÁ1÷‰…—sS(ÀB9w( ""ü€ç¾ÀÇu*Ö&žÓÄîy°4 ª€µ©ÿ ¡*I”Œñ/Q²…êXÍÛÁ÷aáì)äò¤öNøY;zºžfMŸ,ÀÓPŸ35ë@ã-¾V&¸=wuu ÀòæÎê4¤QEꢸÛñè87Ÿ¹Hüfç±ûÛk›Éc™Edá¹4ìò¿NU圦 ;†QvÑa¬?îÙ#:Ý7޾@Ó'ÙªMJµåÑ'WÿƒÒmx€]Å™âÑþ“†³íõóOMís88çFyvM›Ï·†~¸Ï¡DÝ«¼ àävtT°´°Tà'Á“âæ5DU‹œ¢RÑXhNžÖþ­EÌkúÛ­i½J˜œÕó"LÊÔS;Ï}Bާ|<X@øH(ÛÝaÊÙ—ÜÉÀ >l°ÈÀå$VV'Y,Hp¿SFq”(p˃ æŸà=ÑR¢dŒ‡|‰’-Ú{ÈóƒïÚei´ñýr~ØõäÓ©ìT=ݰrbLž³Dʨխ HЄ屢AÓÆ}¥´~wÕ›Æq „… KïÐbÌ¥b|0‹óØMs•±WˆŸþý¦tÍdž°¬^’#ëè,z¶¾òâ³·ÞýõO?ûöî¬Üy£ÞlêéO $B(h²[óè3k×QvúÑžázù@Б¦cÛ?Y×p·}éE-íÇ™šF²Úl‚ÝœŒTÊÏ͈õ¸OĽÊJïÉÇhÒùa Åä:ü$x’ïFÜôiEpêGS§Øíqì—ð<§‡ŸÚ\>æz¶4yùÀ•ãM´hŒ‘6žìÉÄ㟘c¡.xèÖYÚ{–]£ãE?5i‡†Sò£P"Ö÷m¾ŸáøÖö_¬|²b15¶ï¤fžŒìa³÷¦â2ºeõeñbK«g„ißB¸1cNæ…nçÈyÏiº,aîyáT ËS…õ²q޾ï—ÒuËç‹ï¾|WÂ]— ýè( £×t;Þ|æÉg¯ûÔØþüù†&ÿ§>|…¾¯9O‘€&.#@ñú~J=dzmÙÏJ¼äeáô…2q@(G™Ïà0úS&'¨ƒây¯0wÝP}çêšéDÉ¡WÆä\µÆ`4¦2¶e«V¥íÚ´é4kïÞÕv 5‹SÏ®·1„úMÁ§h )XêZLJ Q¸ÏÁúƒ¼#Ò=/ØÒ à„úäuÊ`rßph'2¢”ËxÈن㞠W™ý݇V.¦gÞÚ%îQYÕyš_]KS‹ò‡‹­Ü¬ÅÚ¤Ìkêà¼Lû*Ίèy˜[¤F_ˆî·y­Y:G|wñÝW!Ià$,NÌæ{XÞþÇÓÏ-Yµö¼oéò{xúíÌyÓŠüKæNÑ]2mœÈó-h’rÇãûÙß7I‚yŽä+^ËxÈ*KMÙi²¤(–çMŸ B…ž“¨my°”¿X¶ãÈK†û-ÒäxµÎçq·½¿uÓ³wmû`ÉÒE3Ø 7²Ï[°p§b^•' 8%ꡈg½Mldԩ׺OU$E]Á 0 ˜äöP‘%”#ëj™Ñ\/ëćk)ßpËž}n¸û0yl»èM CÇÎ×ëºå¹Ç¿/=ød Ÿ+¯Q¡h£—¥ÏÝ}ï‡‰Š„i¶î¹ÏüßÚnvx¦ñ“sø`QÂ<>¸£¶w::óRÁ`œÌ+ª·6Ù œÄÝèþ—n³p–q§È4®æ=2¡ÙZi7ÛƒÙ×Ö5 hÐ40h ÌŸ6ž³»Þ©ó Â]ó݇ésY>èò´ G—0ÔÓ œº¡ƒN½2gC횟-ÂüC•:™•Y¹íúا Ó–®ôý€ Vôz¨éŧt?/=è»À›h‚RTHã'OsxÀZõ©ª]¼-fAùü4Ik“€ÂÛíMuuGx©^‹žwÕÓ¨N°ÖL,Ì¢]¥§)Ç]#²Œ«õ΀?|. ³ìW=µòªñ¥i@Ó@òhàzŽ,öØK[DDy:¡ŠHž›—@NáV$]õQõÜn¶@é’£ûàeãLW›{s/5¼ý¢þ;÷zÏò&\ö$p’Kœ¦Q‚50sμ,{ZÚ”n×SUuBMü #/xvåÏ2~°:rvº§óbÖÕL#8IÐĸ‰ÃOëiNQ¹*k(ÃSOj ¡ ¾²Øâ4)ßNzäû"økØ þÔü i¼iÐ4 ~ \¹`:¬8MnîüÖ4´RIåYš;eœú×8L˜:”÷~8Gw=¢„14˜Š… À{Š,hS“€|÷?f¼‚m{¯0ÛrBbÍ©ãÆ_{Î]ÉüKФ„LÕ€“êniÁ˜1«%S.‡ó^oÛ#| ÐÂHÖ%x’JnÃyö8ÀɧóhÀ‰•‘pÂDP%ѨæLÌ£Ò34Ýq˜ö§^­ª¤}z¿—¦wÑô¦ä§‹è?’oOfMøýÕÐ4 i r Ø­º|ÞTÚþA…¸hkq9Íž4FÍæ#N;sØ4 ÃŽ@¾»a«l FÏMÊ0 ÅK‘ š–3hz[‚&†z¦5?ÐuŒ+”îy@€Êå°ð£¾xÏ=¹‚œ£ á«­9ÿ—Ð])I{¶š¹¯^÷ Jþä~œçY!¿ÀIõ4*,N F£‘¬)Z4‰ç:«£Ù¨Ä~™jnÐìÎýd÷µÒŒ‚JI1‘Ùl<ƒwÍ⤚ۤ1¢i é5pÅ%S©¸ì$9\njjë å§iñìII/—&Àðj@îŽàðV6 ¥'ß}&Ní´êçÿÚu´[5Á€ »4Ð4 ÏÌPŠœ:oÁõü̉è‡Ü‡ÛþäÿX5”ò’ðZ žÀzP D§?FìÚ}’œBõ“ˆm½^GFfžßd±˜i|^Õ·tÕ²‹J‡ŸÊl‹jy‚¥  )×}ŽŠÒôTiç¹XfÁ+xïA#Mš4 ÄBf“‘–³ËÞ†½¥¢¸í+hþôñbne,Ê×ÊÐ4 i`ðš~xw4¡ððÑÁW­]C pžâÛcß,y¸àž¦Qˆtlq ,?%p’¾±!¢ŒœM骒b±p’­š16“&e™XYÔ¾EÌyJ„ĘӴ¨m«à£Ðæ§ñYVâdi‚Gð žaqÒ\õqw´:5 Œ\ \Ʀt»Nº£ÓEûV\a5Ɇ¤¡Xjææ©ìëYâwðžLzýÓétÓLóøYVdätƒLŠž×G{ð]ùÐÂWoI¥*žÏCÚŠª4€w«ç=kÿ¶¾ƒîy½ƒÊ½ô“6²›|LýñúT*°G?æ+ùžûø\¡¦øè9µø»¼çzÄCʱ‘·~ïëw"×–F}hàGwß}žM‘—¿1”õË'ŸÌéã4UíÑsœ i8éÉÌ $…ç8¥Úí„~]n7åÈRvcU7»HßUCYœC‰¿çnΈîâ¤y±ÌSaðsžuŸ“LcÏvIxße»(ϪgÀ”"øJKK%{ª]¬ƒWð¬DÖ\#£ª'M…ÌȆTétúé4'-;yŽ[;8ab'¹8÷‡pˆ,ïÝ·Íbf€Î`8;ÃNs8‚ÙÄ19йkRÆSçêEµÆ–jnïä¼&îÑvò(M°|Ã*[”ªHèé|ÏXºfOKÄ3¦ë÷9CRÜ-ûË §¶§x>æM-J¨(Zå#SU-^*­ó2pòÐG¦eÒÚ©&zùhYŒ:úÞr+]ËÛøœ¼yÌM¿Ùå ·×O«'›èã³-ô\‰‹~t••¶öÐ̃°6=qS*q*)ZóTˈPX_ É 7­þþW{¹çYGƒ<{»”“›W¸éiÔøÝGHòKqН£V'UGñÀ 7®n¹C°æ¸4a4fÀI¾ÃImN/µ»9À¼û:KFÁC"%ò©Ÿ l£ÎG©&?YyÄ ®x°,Ìed¤Qz:ÿž˜Gð ž57½!©¾Ï‹ÑáÆ½Gh]Oß¼ÿ(m9pŒÚ9»çå)ßÔ·Ò¨~çr+Ý0ÃLìCNL¢{—ð·—g‚üvW'YT-o¢Ù|Ík]´¹ÊMÇ tÙX#wÐÙ6¥ýNNMôpÝh*ë9K[K ü¿Ÿ(tùW_þÞzufã‹ÉÂ{ù„»žNtÀiwy°êQœÐY8B¤:w(š$R@»òÉÎIén'øóܵÐI‹Ô¥Ôgd7<#ϹJîy°44efdÖ¦À«fmŠ…æ{—!À3rج{¯˜GÛÔd* Ûjà¥Ggê}QŒ¶à¦™ÃyºàªùêÖ°UЧ¯]Bóxä÷ÏF,29^Mϼµƒ­K.~žŽS^ÎA^–sJ%õ„–áó¥Ã1“Ú;°|]lÍ(£O]{ELåë‘mËÆ€”¾äprkkŠ™­¸±Ÿ¡ò%Ã6¾ogu8ºˆÝv ‚n[s™ÈÕ¤¸þö~ÎsBÜò:U±erÒØÜdUã1‰4ðÍ¥VJOÑÑ|žóTRëe„\˜D7Ï2ÓǺ艃Êwir¶>Îûœ@)ÜCy`§Cœƒm<ß Ã\FeSòϵ×@“¸#ê_9?Á)¹Ÿ¶üè«_eW&úÕ€Žç9u¿Ûœ.XõóœFp “Éâ7“ß®||Å(5ûúÃ%ÎÂÁlur¹\l‘òˆ‘[tÒ|HÏÅ4X%Gx<`T  õ)Ö&ö÷f÷]³t.[•{.Ÿ™hE—2¾··„^Þ´ŸŸëZ*Ì]TOE[Ô Î(³ÛŠŸÓ9‘›odù\,ß‚!Ë×[¶,› rÒÙ2;< wP PÁEÈÕ„Ÿ‹-ŽM­ô—WwÐõËç²p– ~αW·n¡×,™¸÷Ñ‚')ã{{Jè%Mv[ åä¼ÈV˜Ät:Ö ¥††[Y>eªØ`å •ÍÆïj6[™´(ýý¿Õ•ù9 `ÛéÍG„¥}5?c<’ß·÷C8ÈßV¯>ŬEÓëSS½wêôœW¾çIYoT§°dT€wÐp²Y­4‰Àܹ†eÂ×=J§nKbï;ÞÏ,Z Y¯ž­[í…êDÈqX€IIz;4«ƒR£ö?X°44ª8Më÷ )Ñ }:ƒHÂ<·ƒèµm‡(ÁÀüéÄ3‰¨gÑd<|ì ½¼YMy¹HRžXh|Ô’^ßFƒ–¯G¶Д›•šXÁ’¨v€K諾‰è~îs8xÄ|¶0ðíÃs–™f§™“ éèÉóbÿÞ#•ôÑ«ws±Cû§i`˜4°õ´›ÖN1Ѧ“F1x„õU]ak;ÕÌ^ 첄ç958üÔæR¶Ã^ ÂhRáM‰K<Ï´ÇMhó¿ô¥º=¢‹ñÇÉ« òr“5EíÂŽ*à„›#C| ×9î8H«:טP¯X›ŠS®{°æf¢>ñã ë°8¡nX€%“°BõLàL=Ú5}k@ºçÁÒÔÒÚN¯l;Lú4aiêûŠøï…Õkq{=ÿÞ~š:._`;n £Óé¦g×ïîy°4©‰À»&^xo_ÔòõȶG¸çÁÒ¤Qô€ÞjZèUè ³I—Ñý]âテ—ÏNG*ÏÑšËçjnÑ«Y»"J Ü·µ“~³ÖNݘ*Üóv0úõŽðó0›~zö°SDÙû1‡5_öçfOŠ;}”U'ät 4%Díq«Tç÷Ý.ŸFƤEÓ‹Pó?üÊWjñàÃx“-l ÈzôÑGmwß}·2Ñ1Â2âyÚ¨NR¹è,(SdØeO&î„Êü+°0¡ã’V'y}¤KÔRêC?ƒòãú ”me¶<7Ò²µóú×îÀ/æ4aþÚvŽœ×ápñœ¦Ë➎[Xž*¬—ózmæ¹W×^1·ûyQ:·á®Ã~)ãÆ÷K©…A`NS¢ÜóÂñ ~²3_£ µ_ŽJ¾Þ²9Äœ&Í=/œ–ûß½e¥Û¨¦¡U¼×\>GÓßÁqùY4&/“Î×5‹ïß‘gi1'ÉÕHÓÀP4PÂó™f?ÈæÎ0ÔÅîzß~»]€&<£.ìv’À¡tÏ“z`·ƒ¿}DNeºTè)ªÜ¾4鍯 !ǵèyª¼cÑ1ußÿýi"'³¹RqL'Å`P×ftâÄõlîûúï{ð¡³l§Ö¦®ˆ¨ˆ+QT6jt à‚Î\è`B0€%tÚäçb}0$Á–ò‡úd°¹o0ek×ô¯ÜG€aX›:8/Ӿг"zæ©À"ûí8\I+M ÏžÏþHʸ•Cª#z^¼AôÇS_ÇÀøÛyر|=²UˆèyZ ˆ¾4ù>èÁ Þ¯¨¦+æMß<%åòœ-`7Q'ÐÁc§5à¹jµ3‡¨ÌwІ‰™º ‹½Î šVqž& 4õÒT2otÝÁÝD1RÎýºwÿýî»ë“Yšøó®«æž¶Nl¼Ð€Süo@t5Jð@ Lr[ä?”/IÖ%·µåðh÷PX›8ì¸Ó颓gëxŽS‡Çû¨Nª1‰$ÌUÌëôIc >øù æ\ÊÙÚ:9™2‡W3¥ÚR]Ã4ŠD¾Þ²9EÈq5Ë–,¼Ù­fjà`xff1ˆRB”+ƒGs§Œ¥w9¸,îê[¨¶±ò³Ó’E4OMªÔ€šTy[bΔ_§ûM¯…“¥zÈ2`rÛ¹œ ñ…Û”QvøßŸiñÑãûôJùžö‘ðs–é'lD˜Ýþyž<ºjjë|§X8²#Ë!-¡¡×JZÚ9?X'Ÿ~Rµ¼öÞ[vPFêxÞôs¢Ú:Vý½»ï§\Ïà|_&^IçR—»]Vv þÀg$òõ–-"AVÛ°hÔôhàØ¯xð^àý~ÎÌω¡•ö I”B>Üó7`eÚ #B¡ç ƒßº[•ÛFæÓÌ®ø’ú’G‹åRM±Ô¦ºËò{ý_âpÝwÞ9pct¶ÊðÓY©^³8I]hKMqÓæªyºBÌwðÏ©O‰¸þ[ßA÷¼ÞAå^ú Go²›.îFûÊþD g|–fŽÿpŸç ´Ó 7Ó­+§4[á@§Žë -É×K¶b6»aΙ<6 “x¯è9º'Þ%õG zÎü6#Õ&Xr¸ÜTqJKzïû“ õ¥0±ø’£ohñ»89®jÕ@S\ÕÐÊþðæ›ŸßÿIÉ*<)×µeäàA½ ‹“_³8E®:íLMC×ÜŽ¤«‚C ªžÛÍ(]äÖª¯HØXÞè¡LˤµSMôòÑ.š’e@j^¾“7úèÁ½Nzó¸búÙJ•Õy©(CO·Ï±ÐwÞj§¯-µ’ݬ£'nJ%fÖ<ÕÒ¯€^6»»œøR–ÐQR¹_±ªyØ"ãê·Üàƒ®:_Pü¦_Kó¦~‚ÊN½NFC ­^üŸ4sÂõ|ºŽ÷½F›ŠA^_MçóæOý}pìo´fÉQå¹-”Ÿ5‹C„§Ò§×kò‰º8öÛ]úÁ{œé6B'|ã9‘”ç†`1Ê͘I]…¯«/ý[Bn¢=%ðïaºdÊÇéªKÿMm2ØhòØ«èÃWüŠŽž~“JO¾Ì?Å{ó_ÑëÛÿ%b"‘o¨²EÌL'¶u8éO/o¦õ»ŽP†=…®^8³³zï2ð0ã'®YBi|~R?3x/úz΄YÒ‰êZÂ_nkËÑ© ˜¸c(\7sR-p­¡·º-’‚?æSä.ë~Ãy5Ð4œÚUgÙ~ÿó’3~Ä4k“TF”K“Ù°8ñ¥ã¢¼<®§G>W¶´Ê4 ^rôx‘õ| @¢…ßd”žÂ£ï ŠJx~Òæ*-c¤¢t=}ñå6ÚsÖ#,Mk¦˜é–Yf>G±2ØÙ#äÚ¿µ€¨¬^٘˨lb!é²H¾ÌEHYÐq &¹_`¦(Ó´q×PnútŸ¿„CR§0HzT} [žJ«^¥½e‹íìŒileº6ïÿ•؆Ejsñ¯ø0qà~±ÿ|Ã!jh9.Ö#ù7|C‘-’ú:ÇË`åð³ZLœ vŽœ qÃÕ hÒ˜˜Ìf3ÿLT”•BçÛ;hzçaÚŸv5ùt=‘ë­%½ßK3‡)Ål¤ùSƾÁ¿”%–ü}ïÆËõ&ïKhŠ¥V“¤,¿ï Nuú'ëÚÊ 4Àx‰ÝõüûK·¦ì“ƒ*h˜/Ò€Ó0+X+~äh ¬ÁKu>úò" ýÏN]=ÉDùlmÚRÕœB¥=ÕÌ®‚Œ–Œ5RƒÃOm.e;ô<5lŸ8·‰ÝÊ®£ãÕï €õcÕï†e­©õ$Ÿç£ —S‡³ž\]­b;ìIró8`EÚsä;SË€Ò!8Oµ¥ÐuË.¡ñÙl±sò|&+9œ=V¤`ñÐICΤ.Ñç³’Î.v{R:u}y^<—u\^­kÏBÅ™š?M´gñdC«K%г Ï(“…´…Á“5ÅBÓ;éDs+ÍîÜO%ö%*á–˜Ÿdó¶ÒÒ™cÉf³¾Á?äˆ%}õ_ —eæyÿ¦¦Xj59ʺÿ‘G¸=¾ù‚[®Ól1½œ«—Köv¬–cÁ>¿^µ>À=>Fêեƙ¦¨40\O—ÇOw½ÞN2 ôöç2è_¯°ÑCï;hÃÉðÀ©Ùé§g;éGWYiÇ—2D„½¨„á“û’§¯}Ñ–zþ»û~‘öÑí«Ÿ¢Ï\ûœ˜·´±øç¡§¶®fÚ_þ”ˆ²÷íÛˆ{ƒQ¬„ʺEQ19µÃé¢×ÙoÿÑSЄ‚—ÎLpÝ{ðù Gs;ö,=‰$Ô?·ã}æç,Í—Áó³¿àƒ±tÕ³Y«ì“fúþ®¦DÞñÄÕÍîûkÃñ—¾ÿå/z5qì&GÍ~}O.''ÁÕ,NÉñ8i\F©t{~‘_Œ ³l {ÁQžÓtÓ?Zêä„¶lL Ð7ßê;jÞ}ÛôÀnóC©+_ïÁ5ª+òœÞ{ûßzø¥+Þàõºè¥­w‹9M:N’êñöäiA@"¸€-DÕƒ&<žžóƒÏék]òŽe_$÷u,QûÒ9hB;[šØaʸ<¶<åpž£ ‚FNB ž'æp:ΑĮx'ÏÕ‰PÞˆL×îp±ÛŸ-p~¢d®ª—zîë>Ìäù[˜›ª¬®ó¼C£Ñ§¸¸x˜L&˜ìv¹x€ÁÅI”‹µsã®$Ìi‚Û Ý×J3 ìtɤ|Ÿxà[ºêÅ‚1? 1c×ab¢ðOÔæ4ÅB«ÉSƦM›Œ;JŽ~F¶^:ƒNsÓ‹ÁíëeqÒéÆÅ Èa)B³8 ‹ZµBGº:B@Ó@ò:Ø()h¨¬á>Ž9MÁ i úÜGT i òÔz¼¸ô˰Ðw︞®^4S¸òI^‘,¶¸¬ŠÖ^1þå3׊{ïì..}Ÿ¾î úê-+D@y~2,39nAN†`1Ž3Ôhtj@Nz~®ÙâÄï@ªÝNöT;¥¥¥Šõq™oóPº¯.mßAóÛwSAWµÈó4œCž&Ô3¿c·¨7S×FóÇ¥Ò܉y‚?ð ~Á7"BŽ¡ÒÙºbòÐï¸,·šˆj=]¦k¾ÿÕ®žÈ:C­D»^ÕØUVö,,“üLýáÝwoP5ÃÉœNדßÀOyje;i‡Os¨ÜŽdÕÀ#½ÍmœÌQq—’#jUx,ùB˜Utn²38—Gïš8&þ£|±”G+KÓ@¢5°i_áשi ‡ØMV—¾’ܾÉrUß „.‡ÕéÉ×¶óh7»±Û nx_,å¿ÃÇ« ?5‚DÈyZåUç8ÏWO´=5ò«ñ4|€›‚C`~,9n·[äCtFާ™TÛé#=G³Ëêä>ã7YÈ¥O‰*ù@Rü²øœœ„—ƒÕð gà¹KcÒ 4%/²²Ò(= êÀ'øß±pÓhznÃç¸Ê@œº ÕÆüáç]GâY;>r4àóëîDâÜM ž”—`䈘IX‹²b¶>eËuµ-“ 8¡3²‘;5Ȱ„Á£46祦eº4µºèô¹zjs8éÕ-(ƒ#}­âè\«—ÌæÎš1&£kj{`5~4 $Z}&É“›¿Q¡ävcßÅûCÏSãölK¾u¹` 't’cÑU£¬OýkÖXmUÇwOªØ•ψàN*t{¨Ýå¥v·Žº|äñw²;ßЭ=Jm¨¸." g|ɲ)‹ûvÖ @SfF†X‡›ø…µI‚¦.·â’íó¥¸Ë>pâ™GÝÊ "™Ó–#Z¿zøáI¯_änbA}œÎìñ-p…óé}Ýé!—ê4à4TÝ—œ¨¦¿¿¹‹[;Øå¥£[Í¥Y“ 8÷Œ"×áꢣU5t ü ½¼y?m)>Jp‚ <4 hÐ4­r2SEHö&þîž©mó¸¢-G;dh@$‹ßL~»‚šÐ¼`þÜá,8ÂÁy˜ûdcð”ãó °íChQ¦¾æÒEªÙŽ!2ø0è1çŠ-`\'R*[—à>KTµièA!BA“ŽÓ5=û©òg}ìx¤ükç 0hº‹%Ó\ø™\¯ånŠÝ}ÕÞ®îÀIšÅihŠ}oO‰po)ÌI§{n½š&Ó\Ò‚5 ð¸pæxñ;y¶ž^Þr~aÝ´âRºfé\1Ú††F#Mš4 D«Â(fàª:[§§h8ÂÎG[À˜Q¶»Ã”ó|"''''7'Gôr¢çÁ$!ïKm²>Xà~>d”?%Ìi²Ù¬â=jÛ šìÖ\êr| à0òh8} £íK: <ú裦:·ïK2,”^ç$é„P3ÃKu'Îé¤Yœs¯0:µao ½ðÞûœC¤ˆn[³˜GµÔ“do02 ÷5•_¿m%=ÿ^1[Ÿˆ à ÙÞÑ€ÈQ»áæC+_Ó€¦‘¡Éc8qà P%G \‘€d½¢ríŸ*4 Á @‰Xgð„uäw²Y­4¹Ø ¢«‹ƒÌ°ëª]ú‚“tï‹Fé4!ëÖ³… mÜðP/À¬NÀ„ý±hóÎÖïsš¤{@ÓgÖ®£'_…¡éT4"hçŽ Ô»}·È Íjm®hï䮿ŸíØþL^êùWÝü1ÕÎqh‚{Þ?7 ÐôÙë—&ôæ&Så—B_o½¶íåñdÙK8y¥}C㣑¦P ¤˜3hÚ¸Õ±€VqTÊ:p@xq„¿ócWÑéšF”æ¡uºAÜËçMÉm'dÓ–G©¼ê‚¨÷LM]ÁÇž~k'0OKx´çð Αå£3Æ‹y›A¿ëÖ•ôÜú=¢¬=G*…ÅkÍåsÄ\¢JŽš)©ÁËU—Ngw( >v†ö=EË.™FF~ÏçNG{œûäù±\bžS8±»žFš¤ð ´ M{ç Bºç[šbœPŸü¡N,Bî“| v©¦Ájnd_×å×Ý-CóøÚ÷¿üås#[âØH×ÞÚºÀGºßsTM/[ì8§Îçã%ÖÙ :gHjóë(ùEƒéÕ»U9Ï)që-õ^Á‡“I7sd8DÏÓAôÖO´[Ðô¸»´Š>té4¶$( )VL´ü$óù__šBŸ—Bm<ìôÛÝúîrÝùr;móÒ;wdзÞj§ç}“2õ´úIX›“ƒ,æ4ºùª‡¸óc¡U‹~D‹f|žž|ëFš?õvîà†;þs¨ÓÕÈE>Úyètðø?T+ò¹¤°»(Åb¢L2عuÕeâ=ØXsù\ž¸G  æ‰W·Ñ'®YB3&ˆüJc°;]CpÀ•`B]SŠòèÿx—²8—ÚÍ+ à„z‹ò³xÀb³È÷tÇõËéϯnEJúÒÇ®¦¹œ¯^Hv@z.—‡]§à£ç ³©(/›ùØÎ_&ª\ÁIww:.€Õ½ŸX-xCލy<ÿóÙ·vSS›âR¸ †+“ø;²íÅý±’Dh¤i T²M‘ƒrÁ€ ç4ɺP‡$YŸÜŽÅRM±ÐâÈ+ã‰'žH9Ûéø¢t4Õu<)‡G¢~íkÛ¸äEáJ¿ïÁ‡ƒ*èxÀe„Á–œ‚v£TˆÜ„ÏõËç…=/l?xœ;@Y4ilNBÙEĽòS¨òL ÍàÑv¸5`”n$»ìÉFUY]ý3s ô%VZWÒEÅçÜô­¥V—ÆÑ¥ºÇI°þÈGSiÿy/ýÏÇ+ æ?\aòœpÇ#Ýïñ¸èý£akÌ *æåÉ Û¸ŸI×]ñ+zyÛ× ‰/^ÿ•Ÿ^3Ð$yDz/’Çû:;“Ý›†ÖvJµ¥\téWn^É“ÉôöÎCâØY¶¡ƒw¶¶‘˜dbÚ½%•býú+¬/‡ŽŸ¡3ì²ÖÐÜA—›µÍ”fã ê š@Ù"„¹@EùÙÔátQ[·[ Öó²ìTvòÍæw± ;Ë:-®‘ÿÆñ7ã ×ïaW'ð/­U s¯ÍoïtŠÄ׸¦‚­\ƒMP½Ôs¸ûÌ@B’#‡ž%.’'m©n ÈgKÝ\öp§¦]hk½5p®Óu;7 YØËÖ¦?¼ûî÷~tÏ=½OÒ¶©¶ ‘[^eì{éCàZN^U*;CInkK1SIåYvѹTŒª‚-Ì;Àèp¤TÁî9oï:2hài=è”=ýÆ.#¾ŠR²{:J‘^?ÐyÐ#:lMþ[º8mçHW/žIW±Ëž™ŸçÇ_Ú|YÄë>þÞXŒ ‹ø">óÃ$p‚Û¦FšF‚4Ð4îâðÉpÿ#,p{|W nrYL†'†¯¶‘Y2[Œ~_Q×pã!£‡±Ž{,l)òýòOZòã{ïÝ,5Ïê±8ùýÂâôŸ÷Þ{–ÏÁO¤*à7=DáÁ(mG¸ MbË ’Ážàß4ž‡:t¬ZX±”ŸéUtqÙ)Ú°¯œ:9"×tž×°UÓÃ_Úx@Œ"ßÿäÛ4“÷ß²j!»ð4‹ßç8,onf*-™3‘–ÍŸ*Ê;ÁV£—8wR‡£Kì·ð$YÙé8zs+;SÇ`™Äõ믜ÇsŽˆþòÚNrrÒ¯Ç_ÞÆ– =ýà ×õ[O/æ#ÜHg}¢“‡Ä„ˆX˜bQrkHô‹IªÓ å/ÅÈnuä £ŸÃäê¢ï4žhòÐM3{M¡ýâ{—G%¬’À›‘\d·¤ø¨,È—Î}¯ÏÆ8)ül ×Õél Ž?Kó¯ .oýƒ#L!äPüO;»³ÉûÒ_yÙ¸“+ æç DUçë©êõíâ´§ßØ8} Ï™”´•ÎNž+$­Br¿<õÊ÷Ç~ù—×ä)-áz÷†}By,Gž¼‡ƒ6ÀÕO—û{q³\Ëàëþ¹áý@™»-Rgjèïoîd‹±â2‡‹ K4=zYŸxä=À2šÀ®‚’Ø[P#MI¯ 4%ý-v¸ïðÉó“îŸÿ~÷Ý={†½öä¯àþÿ{l.ƒ¥ùú4Û%?üÂó—ÂIÆÆ¼ÿÏÞuÀWQäÿÙ}ý½T’Ð{ MÀ†ÊaCQ”¿ Šå,‡! z'ªg¡‰xzçIB(¢è©‡bCEiRT”ÞªtRHÏ«»ûÿþæe_^B)/ÉKØÉg³³ûfg~óÛÝÙùί‘W=ž°ãÀ©lY’^egg‹‘‘‘\˜×y|ŒÒ:Ae©«ãcÒÝ'5'@Žš$ж{Çð>URw<É.ëÔªTµä’x)Í5=Û³Gþï:v&+—ý²ûÔk`äß*Š……ZØý¯†…`’zYçV,þÞYÏN-¢vAŠå½oË´ŒÐýÿóÍW°s¹Eܸژ€¨UÓöÈàkÙˆÛ®f;ÒŽ³ÝOpWª—ÃÃ%²é¢¶(]¨^ ŠÿˆŸn¨-y<€ÏÄïÆš|“B¬8Œ èAw£Ü^U£ªöû—k&<ùþ¾% ªªuUTžÓ[6p¢?Lh/4©Uog/ÎnïZQÕU>O‘®îúWîaoß‘¯X^QÉ{TåÊŠ/ úˆÎÊô¯tß—ÖT·Ýò®+ šüË”8þ¿Ÿ÷‚Ž‹]s±ßϯ·bcz²3ªn"©}”è} ÷¢2Ï™ÚVSHÃI-‘õÇ@ƒ–44Ph ©Þ¸:${ö¢EPþ¢6©•yj^ÛWŽAîUÏ5•MT£ ˆ>÷ãPÙãÀ‰‚äN;ÉÁ·¹É®ÔôLOº[r¤fd.ª-4À‰>Ä´yã>Ô|BåG}mÅvC]ê¥Ø#:´Œ.ÅÁýGO³ÈP+ʶdI]aÏ´ëÐ)Ž˜Gd·`ÄÊ,y¦##nJá!v=@ÚìÞ¾VÐÉ*ƒLgå°Á×÷b½»´bÃo-m[E×ÞpEÖ®EØ5XY³hØR@$L-c¼jVä‰Kõ"XQ;TOu%o4wÏěƖÔI7-¤)Ê|„ ª‰0+læ>Q­în;ãfßt³WÚØòÃáI/ðªJDML£Â­œn¢_íKY¢ÕóT¦ET(³E8 èS¶XµÉ¾éLÖ.vÛ5¯°Çîü’%û•ýuð L¶«?W.(ìƒ÷I¾hÿÎï›þ¼øFÕîX/<¸F_®­™.€äTª*’†ðÐû@ï½zÎü+¥ûBñœÔ•_«š×ö4ÐÔîVýÑêv:ŸÀ à‰Ôô„½Åâê Ù²q´òAJT)ClLIs}ÝT>ñ†ã‡Ç¦ŽK0ó-1Þh =ð9š{¨ï}eë0SýYP-©ªêb2OuĶiƽ@†ºÞîçY/H›ÊE8–Ác½üçkKzä]D.9öËí?r†­þm?ËÈ)€ªž×XžÀÞ±3Ù¼T«h/"»"rQ¬¦<¨É‘êÅ— (#¯Öb·Éjÿ}Eíø—©r<¡•v¢—6âwcM´šN0h#[.ò$ØÂ¦cž¼³,Ü“Érõ¥ôÅøÇ̺Z`¨äõnªgõòJHp×}®÷þ_¬žŠ~'š"! ëØ"ŒÓ«Ò~!UJÿ>öhÛ”ý~PfG;ذýQQ3žwºòÙ«ï·õý>¸ßk,§ð8WÑs{ŠXDH[6æî5Xx¸‘B€Üª&¢ËîèÌdÇZ¨âZXËÍø½©¨¥û¾á¶@ª“…ª¶_åé]¢÷¸¡$ŠIEžþÚ„éùû@ïEež3ÿþµÂâa”0~• pþ…´¼Æ 怚‚øæi¤»œ¿qUÐ%ˆÊ¿ƒˆ¼CŠNp$Y0¦¥gæLŸ›ô3¦KÙ¡ðˆÇtF‹1®l<,Eì¤D j{åÚìLH½oæ;ïüC²;Hþ}^¸ÿ p" ãÝÐ{ä‘ôuïжM'ÙÞçØƒúžW­ îÑHº3á±AçýVö‘õÕO;Y—61PÕ»‰¾œ¾è^ŒÔX(CÐÞV0Ö§XTÅ.ˆéü†mØéÌ\öü£wÀ8^Ï’—­£Óå¦ µSî•ö†Zê^H0ÏeaÍ›§@2^y'(eëV:\l7‡¤ÎˆEˆ;ñ¼È,+÷ús¥÷²b`YÙwaÑ)—y\ëÙê­XHøË,:zˆ¯þ••ß· ®þÚ å8„ó¿\Ë—Ã÷çr ™:ôÐû@ïÅÅž³²Uµ‚{u5MFMâ¤2CÛ7h ©Aܦ  2-ãܽ˜#µ'b0CÊhi±, ÂzA(tìUQÑI°±•`« å'ìõLŠðxrÊvGPø/V“P.p¢_E·Û‚ÀºNµd]îƒ8Q§Õ‰| ÓjÎ ²GúhåF^ö(þ ­ºú§žZ°-{Ž2Š¹Ô·G{°2 Ò¤öPék{§"®†g„©Q‘—,Z.¬6¯ƒËs55‡c²Kú éŽ?õdÛÓNpÕ@õwZ™¦82dàNÞúÈs©úQІÚMɃyõ"õ™ŠÚQë«ÎžøKR&rõ¨òº:õÔÂ5tËý·7Aqªô4A4A²gÂ$Ño‚íŠءœ<Ö½h+Ûc;HW¶af¢§ d¸‚®ì—#ZlRëÑ*w ‘è%º‰~êGEÉ¿V<7};·`ë÷*,+k‹‰þ_E—Uêüºm³Ù5=âØÐëß F–•w€ýwÕ¼ TêzÿBDÛÝ”y —2Þƒ•;Æ~øm2–›rXÿ>qþE}ùóûÖ };ÅΑ¤稜ªÆs9ù|<ê„àÌüÃûPÙçÌ¿%’8© ï”%""B—““£¾»êOÚ^ã@Ðq@MAwK‚š Ì–žQ ÄülîÈ‘#ãyI­ôÙ?ŸŽ®.S»‹¹§°víZ]FF™ ?ƒÒ)ßY…qà4}î¼!}º³DÓX¬(3›Û-߀‰óKj½u¹*àD§ } S×¶ÍDÖ«Kkðøüš»8Ýѯ'û~ó^¶|ýN`nº² N=°Šÿëž?جwáU¯]sF±Ÿnº*–LjúqË~vb4µiV2‘„z¾Z·ƒm‡»c²¯R함Õ~½:±ÿ­úý#å+¯é†Ë»°LL)¨ë׫#oÿK\ÿ⨻.Ø¿¨šÿTÀh>W“ºì¼î‘𪇠/ž ¢ùBíø·Ùón‡3;'·&aÕN$Á#àAÁ~i¢h³Y™®£pÇÞÚã†ÚlS§Ø•ù쀥w•mžjÊw²i"•A›œÇÚ„ëXçæX‚F¢“èUƒS?*Jåõ±W{Hv<–v¶'$ Ñ_WËæ©¢6+{žlš² 6H’&µQM"Ùðo±ŸSg°¬‚íølM{ŸåÛO±{nL†„­D*á¾2O–§T`ÙÊÒu)•#é:©ç‘Sœffx…Œ°àù²Tù9+Ë3òš_ÈO·jݶ2ëÊ–ÑŽ5 4Ð,w¢áП´ ³¼÷&?þxFá>ø(…SŸšÈ[ß|cÊ;|ìZŽL9ÍÆ²Ô zHœTãŧ©‰qP޶R ñ¡æNMŒO,u²‚8ƒ„TíÊK¬²ú'²ª(‘Š‘éžÇÊMj]¤èŸ.ÔŽ¹šäƒïÅôP⛽¨03ßîŠt¬P]«NòN¼Eè—þ†Øl^Wìn¸bÇD²TÓ0£Ya—KÙ9}3–nlͲ ͪç©24Rœ&r9NÞóÈ„ÏJûH‘µ†¤)44„ÙBlœN5@±×ãÙÅ€Óù}ìÙ.qz¶ÿŒÂΤ? vj¢;°O…”ªö´ (N¹'ï~v{gØÓȬs™þ5‰Œ„;þP}…¥ž\Î žø‘}¼ê/ì¾[Þ…J¥×V°¢ûWÒ·Bv6+ Ff³¦@¬b^Uæþ4†2§‰\Ž“÷5x0Ù%­Ÿ67¹+œ”=ˆül’¡‹çPõ÷7‹¯¦Îœ7¯‡äQf!Âb?,§CØÄÕõh"n›þvòÕ¢A|lr\\I°Fÿ k!_z¦^ T¥JšÄÑD¾*¤7˜²4¨|"^sÀ&Ò^>~ u{«îß…¸[×^Ö±Ú¼%u/2~'û&’业To‚ßt±u§L„Ç8/±Íΰ&haîíf&æ¬UÌë¡S—Lv /Æ ôTÌb, $Fz¶±ððP†Àè$z‰î ©é©L©¨ #Ìv8#)èÈ2dð @ÛE°3ÊÅ^]ÒQkªþ^Và ÅIºÏF5Þ?s¹ý³š-lèMÿf›vµc›÷¼Í>‘±6T÷±ûoù@(šŸ«\ß\, 0“u?RË¥ý¥–d8î$håcOï¸÷:Ú(±¦V‘Y­Všjòœùó“@™š òw9òêû«žÖö`Ç›Éö`“Ô“‚Ú—µ/®‹Ô¥ReŽ@‘(Ø÷ìØŠµEÀû²IMe9¢W†Š[¹Ô0–.Ÿ˜Z™ë´2Uã¾Òa2‹Ê^¥“e‡ÏàµqR$å)X7}»ïGaãv¶i#õ²X¤{÷ÿ8u\üMeë¨íãÀÌ k›J­þÆÌuÒE{ZhvmÙ|ðš[ånÙs8 ÀIýVV™>©€­Äh¢IåG»œlí á“EqÁ{ô‰ÆJ½Kbn¹ä"æQŠ ÎWí¦KÑJ€Å QTX¸IÇB-:HJH}ÐÂ%Mš"ÂÃyžÔôÈÛÙŤMjõ‘~'àa4êX›B;Ë.p°l»‡9=æ–-p¢ÖPó=I5,,FH˜¼ý3Vª7]ù<³$ýøÛKœˆôs{Ùß cÞö\ö·¢Õ>Îâ‡ÿý£Âå÷ÍÃÈ2NV—«x­ÁóÏ Jtd:ÌF‹iyÆè˜ñ%B8/bPXž3ƒ¶}zÎü¹¸ÿÈ)ò/C¤ ¢¨kyû!‘ß/_ž‡3tVK—(h!Îíö°Õ[ö!dÇ–Wàä‹5zÑÎD,Ö´p ¤`qKÆbcÙ—k·Âa“‰ÝzM/vKßîXtÒ1 4ˆÑ—X5pq#9U»€·¯«ym_}À ù|ÏmЬcŠ¢Þ ö·—a^Ô¿l­¦ÐP‡¼xR¼îÈñÁibR,ËžYʰõž.{}]kÀ©.¸¬µq1x þ¼ÀɃžc|‡#;úÀÁGu“Wj¡c&HEd›ÂÞØlg+Ž›™ÎŽHvYK ûK4ìrñ~fÝäy‚Sª‰TŽ&þ”h’LtèDšÌBç¸M$N$iâ*T>iÓ…BðJýþù÷QA)QÓdãe„—·=‹‚}Ml¤õ1ýëÛýqf1…³›ž¯%ØÎeKVþ™=0ð¿ð6Ù…ó&Btÿê²o~ì XVQD¶nëßÊ{ëØv¬¼qÖ4â>T–zwmôh"= ׯo¾ú¾m§Î·´òç°‰#‡ˆä0£º‰€hya½›­: 83Ü¿ëôìºvV–p9VI؃xA9V \Üm;‰§j˜hRËA‰ú'ÕË©å‘í•Õjá©èÑïœÞ*¶K×е”Ô6ÉÁwÅ/‡½vú¨¶E²êöﲎÙÉƾø)¼wÂFê ûïwÃÙˆ[Þg-¢ûp~ÔGߪx *U¼{§¶,õï"Ù¹B'ëÖ)¼R×]¬P îÃÅÚpÁ÷>ÞGY‘¤Üœ¼ß,ÍÌwÒ5ý½°[Ž­æ/ U¨¥Ã'i¼üñ×=ìËuÛ°P“Κ7]^giÈfÓfÛÁ7rHs.gûð;kÙ R›¹Ú¯šÌãT¯„.^¼Ø|²Ðîs4€/÷?ë• FÔ8Ôé|AjI r­×¹A†YYð„cœ> ©K*FìÅùóç›3Üò Å#õÁéB© 0Ò¦,(º<6)!áp]³JNuÍq­½ò8@'4‘>‡³¨(¿`ëÆµKÄ›Ž[²|ƒ2jØMÕÖi*‚ÂlâŠ"öË € ½×‰À]­ì™ktð´çNØÉ¸ u"Ç$Ø8U? ›| Ò3®BBjgf<‘Ô‰A`¢ó$Y!D“àª&uâLƒçQå©-«ÅÂa û¨’¨¶[Óþuis»ÿÖ÷Ù§kFò•b»3‡}¸ê~î0¢m³ë|à©.úVUÞW¥¼5$”8W@¢LVä–™…‚˜K¼ V¥.*èûp¡öé=<•‘Ãvþ²1¹[÷®ÆèfM9p2ä ‚Þa-]"Àd†‘4^húj=À‹uOB Ô”ufó¬y³Çî\öP¶±QC&i’¦š2ö¹þ”Ýþ(V~¸ãê±1M>½Dº^ëݬŠ;rN D`Ï%š2=<¦îIô ´Œ¬·áùz®G–caÖû/”Zë(Ó€œÊ0D;¬¨ªz$m¢¾Â™}Û†uÛ"cš~ü0öSr½PUÉSF¡ÌâV°}ˆ»„Ù7&šzöØåVö·¾z¸iö0¼±‘úš ®›½Ò&‰¯ ’Mh«n¢I>ßH©R#© 6¨!°dàR(ŠÙ$rpPݶè:j‹Úàà}Uݱxª>ºhuø?>ÌìÎlÜŸ"¶ôÇGÙˆ[—0ú­.ûV“ûp±k{œ>ÇŽ9Ç‹eyX§öü[}±Ë*ü=Ð÷¡lC$i"ÐôÛ¾#Âiû>úuͪ_"B-‘]z@ÇIÔ‰ä BNœÿŸ šhLÙuð8[þ“4Õ4èv 8' noðoıKϼ‘É48Á7—ºÂˆF´:ðL Ó“æ÷ Íö¯²’F×é:ìPUÜ‘s²Ìa:MùBÎ IS_ÄnšIÒ¨s“eIt­,–å' ë°¾¦.)àôí¦Ý9@è„TõÈ™ &RN§exÓêÏ?Y‰ºvlèé¬låA׉•µyÚŸ)±±+òÙÙ‚ðóL{ârCÅ„ƒ¨³qÀÄUN4yHÐï”j œ¨¯ÉksD“@ µÍóÅ€)vUjE¶UµÙG•æ@ö¯yT/öð ÏØG?<ÈUö<’£xª«¾Ñ}«­tmŸn,3¯þDV»=2¢F»Ú¸jßɦ‰ÔóHÒtdÿž¥?,û˜2 ~Y·îä ·ß‰…Ÿ1¡Ã›‹‡==rdŽz¶o¼ ±‘@SœÎ|²êW¨çå’¦`ê1ÿöœa¬ØÀ^ U`|ﵤq "ÌHJ¹ û®ô;ÆÓÜH%jaEeµó5ãÀÅÜ‘{k°€î·¹nç„ ¸ŒÇ‚Â^‚SÓÇPÌqêE*xI'º!á!öøÐ?±3ˆóïûت_ö±‡ïD,.-Õ'è !à¤Jœ Ë·Ip†M¿ú‹OVf=sæê<øÏ÷W†_Ö¹µÒ·gG¡WçVÆyZ}ÄÍž]UÀìÅ~-¡ ˦Ýbcîæý€h¡I8©Ç‘ô‡œA¨êy–TÀ¤îAG•“:¡¥½ºQ›ª³õ\•+®ÄTwm÷‘Ú ¤öƒöè_Tx'ö—Û—²ÿ~?¢\ðT}«‹«]¤O7Û´÷(+BÀm¨°Ì<;ëÔºiµë ô} øi ^-r!{ÜùÛ6®w놵¿€HD{g¹¹¹9²ìÙ¯z}q]ÝÊk©ñr€ÆHRÏsB­yíoûX^‘ 6M_#@ñ@$]'z"#–ó8väåoПzñ±)HÈÓÈ2àëï x ÒRGÐDKµÈ¬†—뎜7éUÕãYÙé4cfñµ +mé„/R˜¡-¹¥“~/TÇÿ.9àD«ýÍ£ÂùF"—ÿ´‹9 ¦uød&& GyÜ Ò×îÞ¾9—FmK=Î~ÄÀ›wÎmšE²a·\ÁcGÐ}:{.}±n;y6›E†YÙýz²îZ°ôsùlÙš­ìtF.‹Š°±{n¾‚µkÞ„åb…îëõ»;2…ZÍŒ&þ¯é^áù:~ê»9ñÙ8‘ÄÉ€ÆúóåËö¥ì±¯`dH ©‡´“ÒŠ»™¾&©,ßËW¦n‡Û­ää*|¼ðžåÿ‘¶õæ•+¾/** i9;¢|ŵì6¼8áCvÎiÀ Lh¬‰Þ'rA¶ v8›Ù¼û÷žG¶EÁ˜ˆ. þ½ú7#xmOßO0ÒªÑT˜>oÞÕŠG¾‰(À¸éƘöVýQÓ8[®Š;râ€" Ðd±™„¢Ó§ÄÅ-Q9mÐM3fÌßq¯¼jAêu¸¿ä€“?o§çàEÁÄjS.¨<‘ÎNgæ²Ë»¶AP½–ìØ!|üývuö,¶MS¸Ncï|µ‰=ó—Û¸DbñòMð¦gÃ\Î ²«i“P^ý²Õ[aôo`OÜs=ûy×aöÅÚmìoÜÊÖýžÆ²ò ñêÏ2³ó¹]PÑyZ/<ÖÞ}R'K4“TÁUv{¡Ò§8ÿ}l¯+Ú·‹íÖÍÒÄd±„ëuz“û¥£Ñ×GŸ³µõé^=…îNé?œ9ëÊvŸÅ…ZjX0™îÑw踴¥ÞP„± <ä7å?þ¸çLA~[²…k°Éd4é;wnÛ† :¡ìN=tÌãÆR~=&äq8íö<{aAÖ±´Ô½©;·9ÄgZÈ I&P”'•Úß±ý‡$qÒR#æ€WÚ„XpN‡ŸÅ7ÏÃÈåx0§ë–‘Õ™=™Á:âNRx-iðçÔ¾J¤Mûxj|üIÿßµ|8PwäÔVî™°8•HqqqE‘‘‘º””öÙ=©EëjTÀ‰VIi3`õÕ S©Ðîd·…O?Ç2s ¹Ä‡T§(‘ôð[z²ËcÛðã/Ön‡;S#»ïÖ+ù±ù…_l@Dô,3;¯ˆ=qwÛ¶ÿþAŠE€ëÑÿ»Ž… üUÝÛ±ŸÿIJ=]Dßrò‹]wy×¶¾k*:ï+€ŒÓåm ñ—¯ªp>¯ƒ,q€šHê¤ç¨è< šÒvmÛ‹-yY¢µÃ•-y}ŒÎíMžœ3.J\°ûÌšøi©r ö21úÁ1ìïF‹DIlÛnYôÊeBÒÆäƒ ´Kœìg^™þTD“&$>䢼ÍŸ÷}=õGýFÑûG›joHï$R[¡$Mšè}rŠŠHÀ‰'¼¬—«ymßø8à“6¹ÝÌUÎÔ?Îàëm ÁÁ›ˆ>¢sÏá¬]Ëh®®„ß½àe`#§lúÛ Û)Ìu¯ÚM½(üSÍkûÀqÀßy¥j…žª2$¸EyÚܤ÷ðßO×’«—ÔôL#ÞãüŒôL²qz¼Ru°PÐ'ÿÁÌOgÇÒiq³v¼@±®ˆ…Z]snãŽ;| uƒŠžšr ì¬yt„zÈZç³~ÔÔºi¤šåûC'2¸4jé*ßœ‚;¢8 µ¾[ûvc¾ýñiÂÒ®äê/Uq HÕ0¤D›ÍW›?ß}'ë7C7Uaž&tê1Mâhõ›n9Ž ='z~uQƒÆõŠð×§˜ÎŠcž\g®;žü×ERa^½®â«ôhûês m·œýé»â+÷T¦Œ¬ ´mw WÆ*²8cÓjùpõk®ß+§î[ue¿þ\ï°c×®×b5ü䜤ž’ú®´jEï ½oœ(p¢6zÝͬ†énènÑâ…Àº¾±t©eüˆT^KŒ^à$Á)„›«éÑ7P'ˆÐ#¼‰èÓ‰v–•SÀÿMk~ó‚—2Á=3q>Æ:òÇŽ nj¾sç. 9'f]M]h*Š!9RçyåõŠĽIpÉ/$$<†Úx"õJæQ&_ýW=W—û Nþ³šXt¨íˆ­CÞï™l1𪠫Ôû‰ò£Ãmlç©mF-¶Âµè#g”20 “ý’šLF/KGCM¯¼ö•M÷,]ß§ûôÇ­ìÕ¿² bÅòΗ½¶ºÇÄÇÄ+ŠA ÕÔ ½Tþyš¬Ñ¤{ÛÞ }Û'?xÈÜ®÷(”ä:S<…~™wbþ˜¯ð»– öl•3ÌÝø¡ɯC³6àÉ|ç½ÊsŠ¢{zóéxCìæÚo¾ùæŠkû=*ˆb¨Ñdj6hذVß|òÉö:î‹w +y×TàDêêb!š!Ó1'`%“ÊVSñîuÇÄZW”•Õ çŦ¥FÆRÓ#POa(Xx~‘nèkoq3ì#:s Š8ýÔr`£%³-jér¸Æ¨œDáu5¯íËéII×e+YŸAU+ß‹é²t±~š’… Z|+“¼¤ÅŸ¦r~Ÿ:vìo¯Í›7É)ÉïãÂË\\ë‡Aœh5¨]óH¶yï1¶ÿèYvìê+ŶkÆ~Ú~mØqÎ"š±õ[pÕ½¶Í£¸6²oZû[*Ü¿˜I5~#pF%†\ßAÍìð©LÖ uí?rN$lÜýy§Ö1Pw8Ë»VÑù@õ›øHvvMl¾ÚF<òU7zchG{z©(O@Š&n$i2†_5¨IÓ{^œ-˜Cªï"צçmÿæ™ÓMÑVÀ¤Æ–~ß(‰ŠÖ=~ãò{V#E D´÷¾êš›œVÖC?è=S7z×h#pD ‰Þ;uOyúÍ÷¡ÃH²Ž!ºãSdvvp"f4²D …žbàD´ÉO„ývCH¢à„]–‡Ó¯.x6º5k—»{2F-Zˆ%À_¦ÄÇW»-^µ+Âè%$¾Ÿð9Û~íÝ/5=냙É)+”å æ«¾Õ ,,ú¾7þåÚ7irÁp×øŸ««|P'uBß4"„YMFíê8ÁviÈ}88ZO{ä9ï¡;®eæb©ÒˆÛ®fŸÃñÃë|u‘ýåÎk ‚Á{óOá âÕ%Þ÷0ýéúðílßÑ3èÓ1 à2‹‰ ewö¿ŒßçŠÎê! 6z…Y ^oŒ*¯ÕF-ÕC/Œ žÔ‰œ¾ý„/þdŒê°h­¶«HîŸs7¼;6ýëÿd¨ç´}ããÀ÷_JYm:êêØMú½ Ák×üÎ{¥$ƒÙøçµ+\Þ¨² ¨Û§NœzGN!!¶[®¹þzç¯6Z\]&zÇ(©ï› žT¥«¿{Kã?Œx·áâ餽š“3'Cjzªªż#¯z €¨ô«} ò…ÃÆóðiOf/XÐÚít*!O÷’¼– $/^l>Yd26þ 8ÞàUSpáÉÉ_BÕ¥‚¶.œð‹óçÏÃ:´{©‚ëkõtÐ'u"Oþ t°AêÚ*’m;|†›ð­¢„;ÿt£­¼D!T§þ¿“Zm§›™æüÓeZ2Ú í.î]O¯÷Þk¢÷¹GnçnΩ¼Ùè½îÏð¾G›«_*ø¢ß+:O¿Õ4ÿH²Õ*W)Ø*`Åžø¬ò¼¦mÔòõ4©ã«Ü÷-]*íü#öYE_œ*qäq¿õÅÔ)ç~ù–&vZjäXô¦gä×t÷‡†ËŸáá0AúÔyà÷«Õ6ø›O ëtÔˆÛÿM™ûË+ÿIÚ÷±úb¹ýϾ Àé½UZõ‹é£D{u£wIÝÔsT¦tÒ‹Û™ÇûÚ¡Iœ´Ô9 ªê‘ºÅqâ* ¨Ÿ*p¢~hIã€Ûåž‚ñŠbEÒ\hÓÔĸï5®Ôl6›“d«í3çÍë!{”ÑXÐë¾$¯ø€Vûå™IICñê.UŸ>7Ù{ïŽ=þzJÊ•ÏÆÅeª¿ÕÅ>h€u–tÉ](m=ÚA•íÄ9ÄIÚÎï€èä%óäº`LÙ6Ê‚&ÿßÉó^yILeóMþ¿UtÞ¿LUòä%ðsx4"úkËPwNƱ*’®w÷7÷6ßyÌó»[øÔŒ€Ü î åÇS']õ5?¬ s´² š³ž—¾›ž¬û‹¨“ÿ‡q>~W\[ÑG]ûµüæø C‡¨øÀÙå»x®ß¤C“ÉJÒ›yÅ?Õå$øåý}?”͘a[‰7¡­bAF›–eT>V¥4¤îMqœd€§J=ÁÔg.5#¼ç•ži§`º9uK ÷¤§¸W‡8AÿQ·\Z­‘t @çû×.l…žŸÀÖ é=èú.z~ìØårÃß«žC”''&ͺW­™™ï¼#Û]¸xD]ƒ&¢·ÕÑQ=&UúA“z=¬¿-fëÓ.’™ýä‡uõHbƒkú“~ãüki•™ê…FH¾ŒF#ç/ñYåy°w¬Û뻆ác½ß¼[|´ l½Éd¾nޱü“£õ<ˆVóñ.’ý’ÒÖÜ…©ëç˜x¦n•ž´N嘕Zg-XЙ絊*àìàö½tÜQ«ö¡!Ñ­ÑZpOÅÓÀW»12nìµÑŠVg ¦&Æ?âj¹->n¡ u_nsO‰¼ÚÑßozmbÂYçx¶;x‚ý.¼I‚¢¥‹s€øDüÚyð$‹1yX¤U~š˜  ‰øJü%>«ñ«J×où¹O¥Ë×ÞÑU)‡Â»¾¾k‰¬(ËðÑ+Ö×dQ_éÓ¶×-;ŸêÂ_ÄÚ£@«9Ø90iŒôžãçU:ñð›µ@÷ªzÜöôߨ´J‚ç15ßöüîRéÄyÖÓR#ä€ <hRoEC¥[¥_ÛלÓ,èUßø …°k^«VCU8p_áš5kôK—.5R rSþÖ„áýò%6q[­Nüq‰MÖéJ”|—×nF_»ÕóÚ1—)¡Þîp»é¼?£|ªz4±7c¢o¶˜YlË;b@@zv>»ç¦ËfóTšªÆqD6M¤žwq£¢ .Ö Él2#P¡™ó“øJü%‰SYU=‡Ë­@Ü?Xìy÷®®¸ûÏ=·æç-Æ2a_›‚p\•Göïµn¿ï¤–¹Ô90i´„%¡¡Ö§~ëÔ0‚‡ø3VË×:&þÇs³¼ÜƒéPj ÏϼY ô‡'ö¬­õÆÐ@”Ž­HW„L>´C€¿¨võ…ª~ë›oLyGŽÝ®ä=“_¨l KII1d3fÇßE|þv©9 éšÄ)Ì‚º0¹©1_®OdvW6ëÓåA6èÚ™,íøw>þkt•kB'Ù°›°÷¾ʲrVî¢2¥ÑŸ2Uj‡õÄ9ÉÉMe£Ñ>á‰'¼A6/@T ;{×#%E4Oz%¼¨ÝÜÔq !Uk¡$Ž“ ÓÉ’,ç+LØJ`KT˜•aIgçLŽ‹«—5ôºN4úòÍ^T˜™ow EEVØ2©É œD8‚€Ä çCl6F1#\ˆVÞž|lú|v"ÇÉ?Ëp A¹° ÔÐê²+*Åõ»§Øyv·ub¾8À!:Y¤A†JžžYð–xÂl!6ž'¾½žõJp Ý º'n§“ìjþ¥¬kº½¾{P–”3Íwò»ýøþtÖÎj«êñ ´†ÇŽÝÎÜ8}î¼ôìi¾5tSü•$—ô"F0ÓS¦•“ÇŽÝÜhÐ8;âN:³Y¶ Äbâ¿’fâ>?{03óÚÉ ?M››<`¹E]Û9Õp¢ÛJ£MØÓʤçØÁß!ëˆÇΰ.m›ãTIR’ #Íá PKS fFâì:9NH¤<ë{%Rj4s|¼K*k¤9L”È3ñK'’m˜×{$nÓõ<’4qU=Ÿ´é|§tvõªTï“÷ Å[ ü¿®söÜòÑë»gc0»ºtí«Ñüì¶§ºÔ«­Ui𴣆ÄI£Ý¿Ã9ÄØdÁ#”ÇvS–°íŸèƒOJ¬ýÁ{½ã×NŸÂÂþ=¨ \‡óu:¨½6o^—,CJˆÿba¬;cnr'°3#OÈ %HãGŒðz4RȳžB€‰Þ;œ¨Œ=3·‹ H±(Çòú˜·¾ÜÃlD¹»é-5n`!Œýqf#ûaËKN;ygÉ^ÉbŠ`PßSSzönv9ì è75:åYê±¶×8PÈ=5¤Ý_A*?Õî‘vAõyFlLTò‘ŒÜX—â~Àw¨I›|¼Ò >v>ž§ï»Ï9kÞ¼?`ñ>˜È…ƒ°ÍXs¿ÙŸð]:¨÷ˆÈÓÂb¥ºNôÁ§&äš\´müæ«ïÛvê|ûG+›8rˆX6À-”$x½nÊM°Óq89ŠIx`¾KLê¤ò…$H¤~ç•6y½’Z©ê‘m¤¢G¿ý¹.Ç=ÀœFÎ^ÿågœèѽRï›ñ绾±Ì]Zý@IDATëjDÙ™ û“Ûü+C_öcj¿ÿ¹^kýÏkyÕá<ê}>k¾8 ªÜ59ÀÈ“pS¾qÒhéÕ©¯®®1u»]Êk$¹ÁÈwëŒ šáÝ8ëß>y‚ªÜpša‚ {bJâ˜íþ¿"a¡Þ[!öO™ð ôiÆ0óU“’o¡-> ÆÎN,\ñÉ HçúX(óEaFÖmèÈaèl iŠ ¬Ó Êf2§»a¹ZAÎõ;æp'2ÉŽ)¿ð kVbÒÚ‘«ñÑojòx4¡¤Ê m_š4NÎLJy³•“-m¦#½*“àb‘Ä\ÿJËÈ‚Ör%®âL%WÀ“ÞE¥á¥[ÑŽjÊhRlÇŠšs<ŽÀ‹p$DÀtœÃ!b=M¢vHÕÛ‘WਠB¾2Tó~NËÈü¾)seYØõ‡¾$ybŠÜçùÄQû'$ަKë,Õp¢ÎôBM4 :‹Šò ¶n\»D¼ià¸%Ë7(£†Ýä¡Pi$0«,é½y{%*FfµX8hâŽ#\.¨ëAâTl…‰__¼ãùÆö¬àIå$wNdÛäÓD.ǹûqLLÞ ·^I]㟈÷§2rXêöß_;w.=¿Ñ‹îU@%N±¯ïì&(ÂtERHýÈ/ ATfêÛˆ³÷ŒèYòµô+¡e5T‡“ÆÈ³gÌúbæÏÔyÎ^`Ü9a´«d‰»:×â5$†ÎëÐÄÍËt‚K"0òoèñ÷ÿp|x†Kn)ƒâYH}ìŠN®•1R®%h—¶rFÙ4R$üÈhÄ©8)Œ$N iäÄøø\úð©?i{þ8tj CÜÕ½ïI”çœXå_¤T>;ïÊɬm³kY¡#“9]yü¸T!íà’áÀŒ¤y ñؘI~²Ð¾`&&Ó“Ç~6)qÔA0á®II½­°xæê-nS}<:½ðDYsxÈ“—à)Ï$ÌŠ$8DY1—„Áq9þøxÌCYYtM˜½¶€/ó¦_P»W"tÝÝø¾xonv¬V>ºåÐON’bÐÄØÔ;ìÛ6¬ÛÓô 䇱ϘòèëÉMòU·Ù\­ØU9q%=—ËÍmœÈyIœÈð_<¡ÞF›ˆ7|ÃD'âI(FLK.…òzÐ+OÒD é·}G„“G.úéÛ¯6€Ytoèѽ Èd§Çœ=×J‚ü4îʽظŽ1êÕŒüp-¶èŒ/ïßõ$Ó’Æ@s ÔùxAQv/ 4Éñ(îeð¾×—Iº­@Õ‡…„ÿbnx3Õ‡±ì!ìþí‘äixk¶ÃƒÝCÇÆ“N?£ˆé’¤| u¸k|*sôC]$½ø½Çèã¡,œÔu¯g=MÓ.l7#9jzB,Tøp”X¼÷¦n® 2µ6‚›«¶üƒ íÿqË|ÃŒìÈ©õlõïxÔ+HvgÛšº„{Ù»ýšéì_KûpðTAqít#æB6\…Øæ/Œ‹ÿ¿Ysç÷õžÉ°vŸ3}nRàU<œ&ÅÇ“cˆ Që®+àDˆ&ãdœM ‰>¸fl¦ÕŸ²x‡ÄrCOge+ ºN,Ïæ‰@Ùópp;'®žÕõã¹8=i^Kä>F =Å151á5ØÕÀ£WÚ„ÙáçSkÁôRæUúþ·uÂ4˜•lE B¨ê‰²*{l‚¨“†c£Z¼iû$N8W@çg&% …\d©ZŽŠ LÄÿá£2æ•l~¥l%‹æ•·«ejs_WÀ‰ú@â ²¡˜1_ìc,U‹üÙ‹ ?OxÂý[› XUxµ?áÀ ïíC¨øez¡’r$ÂÖIV†á¸–µ “?°†ý*’%åv&NMHøÐït…Y¬ÛÀA„8)2wñ]y…!µÚÄD©~Û^ÞïÚ94\ˆ4¼ˆÊ¼ûî»FH1ºMKJ˜á–®ÃríO3æ¦ ‰Kéi©×‹/]¨>í·Úå–÷]˜T¦¿0f 4ÎOp!ï;‰ûwp‚ëñ¯P€´Ô‚"Õ%p"ñƒjçÄÅt8VgþTÙí…nHŸVàü÷±½®hß.¶[7KHH“Å®×éIB¥¥ªp†ÇãvºìŽ\{QA汩»÷oÿ\IùØ4åçi Ï…í¢jzw¾uÀtÔm¿U’…ûB&pô —–.¡ž Q“õz%i÷ß{•ò †ß´¤q Î9Ð%¢ûßæì½ Ïk_<­¨û~ðÆÒÖWŒq¢Þ—¯¡VÒ[ÏÜÎ ©Ä˜VËò…vZЭ±¤ÏUÕ»0b¶L0ˆwMŽ‹ÛOei5Ž$Aê1 H#°•JM% #wIî»^‘t‡23Ûa™ðF( <u .ýóÐ2AÍZ”+ÙHE«k’1¨iV‰S3BKE¥_=¯í& m˜’î–^¢Øq\‹9r¤ï‰XLš‚ññ@ט¨qðÀFªÙÞ¤°%F•ŒMêym_wP”CønuFƒ«Ë6 µîP·ÃEsRžð½ó'ä¹ÄIý-Xöu œ¨Ï aO’ 47>@Eç-ØLi»¶íÅF“Y´QR¯ñiÿ+âñ”ñ›6šüÚ(ñ—&‰ô0ÒF3P•6žž-.R ºû°Ëq‡ –°÷KĦÊZX£-ieûßÚ‘4t?îhÙúåÀˆ{\s˜ïw3ç<·¡ ¦«#çÔkØ?YŸ”MOJzLV<¯¹™n8è 1a2à€^øg*ý•Žaý&Ï!ØìKtL‰Èæ9v;Tâ˜[o|La¶¡ªËpo©êÿ‡Bô"s¿ƒII®(èNaU7¶<¦Ô¥ÿCª1þµcj½’<ï)™u§ úðœ»ÞÛâð$<"ý [„»&ÄÅñ~z kÿƒ™8Ê&åÙÃËžÊcI ƒÊ È+¯?AI¸FÔyÀXyÆÄ>M ºðtY¾3›ÙcâÆò©ÉcãÐð¤7 ãûå”Ç“[h°'S^KõÇø‡Ý„{5÷n!Þ?š“ú¼è= ¿x[|'ÊQÕóý$™ºNÔmbMâ)ÑWV=¦I=M´ 8‘HŽTùˆ>UïüÑ?j©B¨¼%ÐDêwÄsâ/'JœHÔMñž~÷=Ð=çî ‘ìJ<èQÑ@£ú(¤UÊSñäÈ{@£Ó<øï›Læÿî|ªË :Í—ÂÕßµ½Æ áÀs£Gf.Ðý ñÞñ’¤$ÎJÑ/Ÿçù¾®IäÀçðÑ·ð‘ï¯Ó‹7•• :áCÅãNX°¸ÿ¾ûî{†h$gs¿‚²ƒðBnÁû· 12¢¦$ÄßVö£T“>)1£T¦•oÅ}o}*ÀQFÒt²|Þ¢ˆh5ŠO ¬+µ —5Kt:ù­¾]»0`CZj°t0,°ÂlÐ éÓyBÐôè“d+ ³j +AsSj@$àý1ÖÍ/VÓ[ùɵ°Å|\ò(«¦''3?¸í®éª ÆÃÙžxâT šÔ. ^ˆ_ á±IÉßA¥r¶^â›åQ„!€Qc &ýÕj3øÎø$NøÞÐ<5蓺N4ëVÁ’ž&ï4‰çÞö°§%"¢¯,pÒ˜rD<¥¤òVN¤†§‚SOôµ£c:ïéþúÞÖ˜Lö“E^ð”þn»t9j Þ{“Zkñ!!Ó€˜¾„±ßÇûžî¡yÉRù¤íƒž“GK‹g¦CñHßÐÌ/½3ë¿á½&ý%7»®ˆŸþöÂvy‡ÿøúC©æPÛµåI‰º4i²:5#+ïcSÐ/uóûOŸûÎs®íÓBlæ„§GŽ$U[’ìtœ=wIyöÐq R”Û“)êµD‡¨Œ»s h ƒìszš`B:{Á‚ÖˆKU²’èG‚&Xcš,®L½~—iÙzäIjÔ­‰ÍÀg ÌnïÊl6î¿)«¸i¢“mÖ®y$§½â’Ú/ÁÎ.I‚ %\`mPi-^(ZøæâÅŸê 9/72$ÜÍèw<®Ç-1Q¯«e1&‘æbp×}Ü•†Ky}î÷oÜ—úæ™Ó‹¦îÂ9|}f‚¹ï„ÑŸPyƒ›â[Á×TõT¶ð=MÃiBO{â‘¿*IšüAMÞ ,©²Zºˆ¯êF¼õOîæXm}në'Z"®€G“«Qð¸%áƒM‰Ì©t „ ‘~Á ´Â¨¾Üýt¯}¥KhG ¶1‚»ðOxö ”´R ó“°°.z€ Àà,h›öBb|rEmB-O‚ºÏ@c±C ù^QÞ-Ñ:Ä.Iñ¿Nºî®¦y¬êº#ê€=3· K"“‹1€â0‘ºÞ×SÇ%Ì,§ úÓy$ãD¬·å*YQÜš+Ê ¢ôÌg§ÍM^¼Dåµ¼P $L" ³ RŽNfE}‚8}£ÈšF„ø@_ðrY£¬"¼6wqsMü\.(ê&)ò"H-šMO.NŒf%'·J~º¤q‚ÿ ~¿KRØÔ™o§<9y\Ü/%å´\]p Xãà ´E[… щ qòB'Lú5‰S9œ"îà;̹Dj$ýP¥L˜TФÙ8UHÄSJrdϦðÛÇôÐGµ¾\0˜¯Bü¤+ñíëèýÌÇì­üªÀvÁß8ÙÿA´ˆëö$ö ʇ¸|úµ³*æÀäÇ 2^]¨…ÀÙä­‡Þƒf-Ð}5i4w¿]ñ…5øm0l~ çîÝàƒrQé ˆŸ"èN)‡GëåçÒ™pR%ÁeÁþéO8vL†%ðøVîÔ25Ùed~5Úh Ôi0Ä> +Æ+l„=XW^½XÌÝOª‡ô^7ìVM{;i&Ð>‚ /r<nÂØ’AÀ‰âç–Wv.x8àM˜ÅP°uhð}S«ÌïÌŽvÌlþ#x.¦„è²Û;³+:Fsz±ø§§ »K•#ÈÉÿfÂb,²<;3%e¡ì‘þ g9O@MïÉÉññ©IV^Ãl†ëdâ™ýyJBÜÇS½C'ýNžÙ`¯ ïnò»ðâ¶Y0&M=ú,ý¦¥ â€\¢ªª‚rÎI ¥¾ÍÜI"B JµÅ! D†:߆,ÏÓ^Kp Cü¢V†æ]¯SÌÖë Mº+„½ÀàÊÜç\|U~HÚ¬ˆÂf¨mþ=®yÝÓ’ÆFɉ£¤å3RÄ…€M£¨ƒ¨¼ýzJèªgãò3k£Ãø˜+Pe»»¸Êý1ÝÃÌP±3áýtc sb×ýŠSÇŒ· àíA¢¡KÓ&k¡®— d :[g)ÊÕ/$Œ}þÕ””pG^‰XvíQïQ\¿Òír/ycáÂ{Çu¹À$ÄCT¶&¨ ¶‰Ó×8ß§ìooöA*ÍO‹L!‰Ú÷´¶š»Ã ~Œ# &“ƒÕcmÜ )“dâ(Rõ–¡–^èfYÙCX‹æ)LÈL68’¬Ø9ÐeF“íb|´S?´Ôð8@îÆ­1Qëˆòb[ÐA3“Rî‘%ù½i))ƒkð@÷Ñï4ÎŠŠøwÚÓ±z!qìJ¸2ÿ1Ó-ý]vz~ÃBÖêÑí?šý¥?—ê;_Ç  @N¹ôÓFŠ’ š¼GhRùPj;}Ssf´ÝНÖ0ìf¬ö¶§Ä]ªŒ$¢¾ˆ¦¯¯÷ \_i»ÝÞØ×K‘Ý#ÀÁ°t¼(,ÈTÀ,ZuQàän€ñÑø³˜-úÖâÎ=#z’Z¤/ Ïú²ZFãÀ%ÃįÎ×%JLþ†:é±™ ôïLíù©®˜pDï"my¤Ëï,( pôžÚ¾"ˆŸÀ9N \–?ƒ×ý X›L%Ý~ØA™9o^¸%'‰UŒz] ÷t2[Qº¸=òýhß]4Q{‚(ïW—¾P¦;ƒÔj(í§%'ߌ¨P1Z5‘4o‡bÔ¿‚ÓGè7-”`=ÿvªûWÕL¦ð Àd2™ àÉd2±H«ºœ,ÃÞ‘ûîgQÑŸÕ«ä‰$Mš ‹zBZkcmbÂ9½D7ÑOý\â€ñ8pt5®šôŠîßÁóh| û¤»ýA_XB8<íWQ¯qwíL0MT9ÀÕ£““ïÅùpܨŒMdìfÃï!ŠGo Ã!™Ì¾…¿SÙ j=Ú¾î8€)k(À/O¸ošÄ©îXßøZêýÖÖN—ó!ä–%w/_Õ'¬øÀQ‹Ÿ¡r·b›ÂõæŸyª ÅkÒ’Æåp`âéÛ™ó…Oñ*Ý‹ këRrJÊUWÄÅý^'ºGø`·…#…aøøw*Žx‡WÀ)ÃRÕ°ùú±«±* õ;òVÇÚÍš;¿¯Ä¤˜6ñvG\&KÊë°Gì&èŸrºXíSX•m Æûh;6Ã##¯rú:Äj¾¥¼J'ÆÇ…í‹9fô§9©b2’ p·º-píN\·Ç4ˆ~€KöO Lp-†ÅSŽ’Ê<’Çîp»é|µQƒªªGÄ Àd¶˜™›ÝnfM­nس¹X–½sŸfM"¾®›'²i"µA7$Mí#,¶e§“è%ºIâ(U=‡Ë­È²\XÂeTýµl 80qܘ=¨ëŠáçÿÁ>iµjŸ4gÉ›#¯ÀßIÍ’ˆ«m#°x?<öÿøµcçO°¯Ä=ÎÀN³cQ7 Y,d:9CM*×êw*Z¥@–uS;Wë Ä>˜%Nè_ƒ®ƒVKº½±wÜ7þÝárÜÌ%Kez„Y&wÂ|`:eMóË/Û²v4~µ¤q@ã@¥9`6™ÿîp:áãJ«]=³Ø6òÎDÁqk%Q,&Yp —–q䙋U Ð'$¥VR«ážõ Ë­WAòÔiúÜùÏSæ£O×å=zŠi*2cl ~w;œÙ9¹…5N¸¯x ˜l6+s:œÌét2·ÇÍb¤B^t²Lw4;“þÔ㲸)·XRV|¦m5èEù—Rœ&r9^P؇Ù™A”Yl”žµm†ö­ðøgåôݪª^ù5UílN^¡"y<U»J+]À‘Ãé ‚¶Ì!½^|ÀæH½W¤{ä‰XtùuÝà,("éRËâzO™ÂBJÙSÇlÂBÓµEú7ÊtúÉ/Œ}¤¸¼¶ B`.Cƒ%,u¤{sÁõ_NÁu?857¯Qô§·î~ ë?wMÄ:t J'ÕI^à$2#ˆu„ØlM.Ì_ÝÌãÁ“ Ф%eÁ¹l7 œX€ iŠ‹BÓés±'mÔÀ$Y12É!¦•¯'ê c’Xë  e¡¡!Ìbãt½D7y¤~Ô4‰Ÿn§óê"k)À@Ȇ‡E~ãÆbŒÅÅ3Fó øâÀ·ÿ€@^GNj˜ìxFmZÄIåÅÛ”8f Æ›þ3“æ=¦¸Ük ?F'Ì*®K½\Û ðBQˆž½”‹Tj)X8@“‰Ø×w:ýû®Ь¼_ 4AƒþwùÕ[uÍÒžíõà¾ñ½>÷MÁÒ ‘]"‡¿…iŸàƒ~«$8Þª~LHHH…ÒÔffLIŒ[s1ÐD4tnÚ„¤2<Ø-$b¡Bweט&«]ö›!¹ygãž}§0GMƘZa=ÀŽ%P´“KrEö,† ÕpÌ~á@(À„Xuå»$‡¶£oýÁî!¶}èïuX-6L~ì±,¬ whÚ­ʉIññE§V0áˆöòñ©ÛéÛ²ë Ï‹½¯`U2¤æFÎ!Ⱦ‰$9a'a)áá¡È‡²«ÕÄbðäµ39Y } X²ÀîHtÇ"#Æz 7æj kÀÖLu< Â=Fê<¬­ÅͺE Èm”…ƒ&Nè"úˆN¢—蔚ñ‘øyöÄ1zvU^W…•ZÙ‹pcÜs£aHŒALÆâ‹"èu¯Ð%¨hï3³Á|>ÎaLú Î" R\~ÂÜI™’ÿnXxXo |k†GÚEªå—ÖÎÖ/^L*ÞÅÎ!©y×-jW“8]ˆ;uø9|èöúîŒÃýh$V„|L:æ1“á_©Ov£.-iÐ8P 1âiöÃXƒÞ„79\¹{æ|ýM“ÇxÖº9´ÏògÔKw/šHò3mnÒW éQ*,»¥{gg“¡Åß }^fÙÄçâã¹Z‚ÊN·gf‘w½„‹V\‰5qI>v{êá‡óàöwn®¢´Gsê?‚Ž­žŸ°û…Ä€X‰^\2EÔ‰<íIŠ)íÚ²ùà5·Êݲçpص—u¬¶ÈÅ'u‘qc%I€ðòÕ ªtÌ 70¨¼ÂÎÈÃŒ ….*I£4vˆj;¬ 2ô°»2$Y¸¤‰À\Dx8Ï“y ”´‰:>¢;ʹµ_¶ ‡þüX/õŠÀTƒÛ)è2™ôw<°K§ÄÅ.¶·œÞ|Ðs—ôñIŸ&pä;® Cc~zföÜ ÝŠû_°ÁÜÉ9ÝG-κ\%Ò&¥´‰Ø¤§z~X®J9e-ÈÏzI‘ÜOã­÷¿8ùo‹2wûÓøJs=“ª5¯q Ñs`Âh÷Ï3æ‹ïa>ôWê,¦‡¯bã'° _C­•NÔ:¦‹ —'H•îÅ2NßN¿ù§ë{t{iã¾ÔýÏ:A—ä‚nÔlx“@ŸÜ%9V{iÂÃÓÔqñµ"ÉSë×öܯ!Mà…‡¶c|‡Àµ#;ú´m^m©ÉU9Åæ§’öÙ‘:yÚ³Û^Û'€'Iöªñ!Þo³&Š%LD‡NPh¢6 q›&H½HÒÄUõ|Ò¦À9… þí>xBÈ<}ê#¢òØû°sê´á€é9s}Áž'¶iT<7"Tí^_„¼÷ãÿòµ#K§&Æ‘ô'8„è-+ž1ö¤aÔ„ô[؇ggŸÈ¤}ä¼yyBâh¨C:¯¥ â€GQ|À #_ *‹‰ñŸ¨#}š¦nÿÜu@ÓÛÚù:J1–6+Jˆxmóø6vßy-£q@ã@pÀ¬7ýÃév<ˆÛätÝÌÝ=“ã¤/ÙøÔ±c)øâšªÔÖ¡í÷¹‡ÒŠi&]^7¯ÏıcUÕB_UÅnÂ_ð`¦2.É-ÑáŠ2ª¼dp‚1#ÙI Šëo'…Iðò) cgÄK½*<¶\ B“zMdXäÚøÍWß·íÔùöVþ6qäÑhÐátõ,”Ìx‹Ý”ÞÈàä §NH$I˜ÔIm$H¤~ç•6y½ü‘ZÙ^Y­¾‘ŠýNô"¹ÜÿðøÊÙë¿üŒ€ñ—ø¬ò<Í\ÒuL{Þ¢AHGÛ¹3ÞN²BV955#s€’ …½¼ƒîI„2x¿–3Jz‘MðgZ—èð=i™[±É¡ÎaQ‘{ÉLH@Àíˆ å@ò#PÞÕÚj=Ò/(·Z¾~8 ȲÏ1ä‡pªŸÛœ­ö{ã¸%KÎNÆ*Ücþâ´ZôñûŸéŽ•-iÐ8Pxæqûñ™óŹ'ã©}Eg.]zßrRå $=ì“2±P’ ÍÀ\¬æ ±:¦=4OrðÁÿ…¢ÝS»ä¶þå¸î/tì‘Å{*œÞX¸°‰ÝéJÞú°0ÿ+$=Ó¨lMRU]’“õio'F›-A«!57·­àrÏCŸ¢±4ŸIúa’‰@~àÔ3ù³šÐ¦][.H¢‚&r¾á,*Ê/غqíñ¦ã–,ß Œv“W|Sîå>©‚%<å)¾“Õbá ‰;Žp¹¼Ž# Ò篮W­½ba“¨AzàDq¥(ž”‘»H'©9‚ ÀDçéwMDc ñíTFKÝþûkçÎ¥“C&¥ÄgMâTCC½8@çXù²Y§ÿ?ªnʸ„9Ï9œž›1>fON³vöüùaøiޝ9EysR|ÂQß12dÿ4ów–ìÎzAypRb¨9_&ˆâßúwï¶psZZgÙ#4Ñ@“?ׂ# ["qMU/8nKýSqÙ[»:e¹r–aŤ5ÝŸI}¶W…Æ~eµ¬ÆµÍ“m&sŒB3î°îs?{ùÅlv긄™¯b2Š8N÷ ‡D½0G’„P€¦(ÌÆ^l E›8QÛðõ©¤H8ÁЄ€Ó s’“›:dög¬Ð /t¸¯Â<‘l!%|„¾£kjšªê’œ·'‡<[R^tJ1 T–rpáöHƒWbr rµ ÐDž@>MìØHsÁ¾múm‘1MIr:Œ}Æ”G‡\/TWòD`DuïMà„òäò›@ Iš\.ò¶GÒ&DƒÄ žÑð8x7´_­Dmò ›$NÔ&I(F7K.…òzÐ ¤¤‰@ÓoûŽ'\ôÓ·_m@ˆ¯Ä_ⳜÀ„š$È ¿t1ÇVH+øKÅò\ x ïuÓ¢á·sL×Σ¸_ƒÄ(£7ýka1®u\'Ëž¡(·îBeµßꟂ¬4U_&MâTÿ÷£^)ˆýçÎñøˆ`rã}é1êÇêØ=ûžî±µ^ Ó×8 q B<÷èÙÂYóÅW Ù™K…àþvüš57¿=`ÀZ²kh„’¡•¡¢A7hò˜1i7 ~ uœêîB¹RR)ºŽ‡Tœ£A$Õ¬'¸ím ƒkxª~IJ­wâs˜,ܵiO*ÉqeDv¸Ø±ºÄ8p¢xPþvN‚,0|)1ñÕ&4eXócZ]§ç”$¤$!1`ÓaãžvlþikÚö­ú <@’<7 „t(¡“nÌCHYTö´T9•Fe«\¡ß·[ÉÉ-T(¸-½²$å;˜ºjÓ·+–æe¢h.6rÎBü$¾jöM`BM¹OÍÈúã‹ÁiçŠëSŸxâa’¦ÃG?½Åð@Ó€¦®Ôîy®Álô9t¯’’>±%&Ž @{^je5ÇŸ(rlš‘œü£>>¯€v"h8€2¾yÉA˜ Mâ4w¦Ž é:g×s˜pÁí°÷iÀ‚Øn³h¼cÇÓ]k°Žû¡5§qàR䀭uÇE'ýãÿÙ»)Ьýzf6'–ea—œ“ä(9A1áTÄDRÏpDï<½SOý=I¢˜õÄœ@EÉAâ’ã—6§™éÿ{5[=³Ãæ™Ù­½]Ý]]õêuMw½,µNköþ~#èð‘'i1kÖÂHDŠúf(!Asß'î½—h^_´($ýà‘KÍš}Ç´)SðIøA-Ò­¼¶ÃgF£A¯øa4ç'ù÷üù ò¬×áÜõ¤çvÀ ãí6ñuç8ZªÚ_›Õ>4ø¡ÄŸ€ÏÀ«ˆTu&²Eó)ém/®eÍn†ÆÉ渄„·\¨h>¨âÚUçÊMþàð`­3KÌ4IfH0U99YÐ>ý€ó?·íܽy³¶íÛ‡EFÖ ‹±˜-¬¡ª=¦Ñj-ÈËÏÉMËÉÎd€¼Æ{¡¥Ÿ;w–nÓàP1N®Äñ³2žœ¡q2隊ªçgÏÇ'è°¦ÉÁ49ºÃ*gydTø˜[ #Ÿ ¢:QP¨ا ¾Nÿ…ÖÉáë¤ÛACeœÎRêchsßSS&?†¾ý¥÷ß(ÈȾ¦ë×§<Ü‹†–XéNBÁ8q2HD‰bmuO0OA™i™—b¡±« ßú3˜¯oÀN=7°c‡ßŠ5¡ÃM•ô€¦|/h±Ç¦›Úf;¶ÌTxqíÁ_{€P§¥£táßÒòA]X[© ƒ„{X;"™&^TJ†ŠÏ‡a Ù³móNl»Qf&Kh¥°—÷ XcA.²™V¼1mxæ2m˜Ab-o¬ibJi›@OÁ9:ß뢕OMœ¸¿°Í{^˜=»§Í®½äWNŸ<éìçã¥ÃSÌHí÷§¦Lz&x…ÕY sïªf½y ‚ßÿwƬ¹“tæ>öŸ”„@)ßúTDxhwyNíý”šnhœl!&Å8ùécòZí^Þ6 LÓdÐ4ýZ×Tgôê‰*7“¤‰Ú+ BÌ‘srlO×p¬²º¿0ß2|Ú=Ö_=…{D|Ü –üü hn†Ôôú¼Œ¬‹±d]jÒô÷aÚ¶ ŒÇàþ;¼[´?í{°$=ùœ]‡ŸÓäÉŸƒ‰jŒCýµwß‘L'ƒDÉPv”.zÅŽ Ä݆ÅË0Üõ <ïóôšž_ð4|K/®¥¿Mœx!É9§™ðåyóêñ9Y·<ù d]µ¯˜(da…vD3cÀ~:Ì8…bã…©›4ç« L†+€™'É4±ùÓ‹iÃŒ3JÌ8ÁŒVlL7¾ÎõT`Z"Aòß!è)"H†f}#ÞeýgÎ~§ñó³çÝYÛ—»Á„Ì BpW›$G„kM˜°÷ôC’ÜñºÕþ=‚K|džÎïœü<ëK&“>uXc¨ÀŸ)“D¯n~ŒßšêIÉ’ÄUí=@/m……Ų)‡¦©ÞU*¡­¤ˆÚ+ ¾;ý,~ÃïHŒ‘~‰µN ‘~>ã+ð<Ã4“>¿m|\shŸnÇÁŒ÷Èí¦¨ðq’’jffœ$èWbÑ`â°æð“Zœ™•“Š@æÛdѱȳé€QqHlå-܇DF~Ž[Ä\ Þ CÁ÷ +’×~ÈMqþ(öU€YMŒÑ4‡$/D§nÅù à¨½¸I±ÚØìoC‚œ:\ÖS{S@2’`&€ª©ØxaÂRÝS…{.»o\§¦nîc•Ç’ygþNO ¾C#\Ûz~Þ¼öv«ýM‹ÙtÅãÇóâ­Àe#LXNâ=“€Mwñuĉm×5ø8Ù­œw ‹‘gò22¿†$ö=D¬ŠGz!É+®­òž+h–ÖK=g–fÑ÷<1q"Kå‹‚FH‚[x !É+“ªhƒê¨ŠàÅ#?ÞKæ€×’a’L“´Daª¦ƒœ¡’6’y’ ”<–×k:=¼:>ø'ý ‘óî‚¶ùG0I_†‡=ýðí·ŸE§Ó^˜õÖÛ6Ê LPøÌÙ³“ÒþŠBdì&Ýt÷ĉ™y-Ì`á}y#i¯†véø>Í—ŸzOÊ—Écµ÷o €Sn*1Äú°,ûã^1Nz*C—é–·/ÄOà&±°9«i–+6?ІÕý œ&2¿j#Ûubº~Ó;ï4p„Cl¾ã1`ó:,,>ÆŠõ+h‹òñ sæÒíÖëí¤1™.æàÜ!’:~oÓm‚qÂrxôS÷Mz ‹‡ûá+5-/+ ¾QZ˜¦hkCŒ=î·ª"Y\T¼ÒÚo·L¦ì[qMu u~85×-¦< §p6ñ³Ø-OÜwïGmõ׋à'"5'ÒŸ‡%ÞðXŒ EQæ}MÉ8ñ^nÌ$ÉMž«É4ðÙØ 5?óáßô9£BS½ï¬€¡škû€Èèç,ˆ·gçïœãɩז„$ mž›7ï*ÍjÿýùÙoîyrʽËKª«Îû/Lv­)¬µ‚xæGüSgôÆ1 pÓô29OyNtº~×#ØtE¢€¢@  À­+1Œ½<¼Þc޽ÚÓÂv†F:âBhKáàÌÉÚuëSvMÛa ±ô‘L÷£Ç.ÅJ—Í­zWŽ…| ÿÔíö‡1âEœ[ÐmH{+Û‹3öüìÙƒ°Py A(nsÜ罿Xy0Zר—Á4ýãcáÒvÍj{—Ïá#S YŸå²ŸQ€W(ÌHÍÏ#ÞXÀAäžË5y“ã佤›ç1]˜>Ž• @( CãT(“ÔL&¿Ö8)Æ© sªý+;ø‡=ÒÑ„f×ÉtûÚÚp(LŠŠ5”x©¿Æ„'xA³0N•!×Ó÷ÜsLÈNhl:‚9Csnøô)@[Â|¯2m–vÌë¾ÎJI½ ›0YÜfo´åfMŸO!¡{ž¼óÎ"ÁpÀ &Ãú¢.·™o25N¶ZÏ‘fÚ`Òì“Ý´'X )šœóAeçç·µ˜ÍIìô].ꚢ€¢@Í Ì”{ÚE”%GLôˆû»D^*mÏ9ñp½Ä@¥Ý«®UX 1cÖÜ&ƒ8R'I î'?ýBOdæP“Í ®mˆ¹‘XÍ£•ÐX®)"d½ ·L×Ô0ÿÐѶ9ÎúÔs%Ü¡N{šxÉfÀ é8Ú=l6™šóÜ´žî£ªíU÷<­*þêþ )pôh®¹q£#2™ j©ûýÿœº6¯ ?ØÅ‚·ç)BIü®#w Ð%Ø1ãäƒ4&…i`˜ìåé‘Ûa®§wẚÍÖNÜ› þk¬Lš­m¾ž3ŒŸÐXAeVl>¨òô£ê( ( &XK”•ræø·˜y°ÜYúÔ”)ïæhÖ•¡Àóo¾™€ïD°¸WÓΰµBeÚñÕ=¥qzæ™wB»f[íÖD6m‰ µ×‰×Bƒ‚L¾"÷óKNk²ˆß9Å™²éŠz§š™(¡™/q¨í}åØÏ§gëÙ¹&̇™ŸžyÂf³¿Ú8¸ñŸyfGIªð§yZ-¨Z© BnÁ'Ð(!¥™º•8j¯ÏSDàCç2 (\àhXØJl‰}œ¸òJÃ_IƒŸGà4…D±Âù JìM]PPt à}ð ›ìŠqh”iÖhb Iá_1 h3=À`ݯý›Ë€aœî}ê¹KOÚŽ/WÚ¨cËFzï‹ZRçÖ´ðÐ÷RHt_ìÖ$[iþ7FWÿ½¶uOhd«‚Ï( ˜åìÜ<Ú¶ï­ßq aû¾ä1O¼ë‰gîzû_ÏüLäºÍ'HùÓ<õÉ€ki'[÷§Ó¢UÆ©qƒdºùÒËK£„Wçi} ­<] !„²Ø]ôÒœ9õ=í×ä:8˜U˜f¼õV3“Õ '­-˜#ø9ñ¦ÁÇéPS˜ÝÔc«ðž£ò^h‡s¹¢ù äýj¯( (P³(€ôWÛì.‰Ä5ÓÓ¦L:P³F©FSt²»*—U¿º¯ûTKSÙÁb1úL %ÆÇ$ãH¢òXíkð 14NˆŒxÄßGî³Ee% ¡9£ôJ¯ÍMmnÓ¦ÕlÉ£ýgatˆÖèoýêÕµ;x~<~Çh3ÏHÀÿs÷“ÿd扵’°bòøÝ<õÚHUÂañ”'\wàWd¥ÇWTˆ2žž§šfúU" Û¼h®§™RÀ½ ­QsÖ!¼ùýXìüwúäÉ?q  pnûu³½…ÄÅŽà²,5NòXíj'XÀ£ØßÃû¢SLÓQe¢W;ç‚cÔÎPä¶)Æ© SAƒ¹Õeø}½Ô«C ýîë†hÁA>·Ê+‚þù\ÞXïôŸÚ'”ê…û;ïYdµâ€ç Ïž7XP¾8þoO²?(o0O~7OkÅCöƒA¶n4ÂÀâàñåF¹¼OÎS“¦ÿâìWw"æ<é‘¢áí@”¼î%6¦iVÝ.¢ŠÙl6'œ>N%Þ«.( ( Ô ßýqžúŽÂµ»§æ‰ $Ÿ^g”+ZðÄ<3™ÖA, ìñ†jñœ9Í+ŠGyêO›<ù^Ʊ3çÏoP\}\‚d»Gåµ °0£  å* £öе”3g½Ù ¹[ž—Ǚ￞œrïry¬öµ’Íä¨ÍvóYö׽ǒ¨ÉVW»ßf·7¼ñÒ¾æêÖM`ˆ;@IDAT4ñ˜ò¬:}´ÍESz‡TË ü˜fØÁ%Ö‰ö8ß'2í´ñ„Ã×ìþÒ9¤ÂOòTj:<“VáûÔ ž£Ï'ͬ%¢EéçT•¹ïwóÔs”R-U„Q %nÉEpˆ¬ìÓ¹ý‚ºUž§®ùœÈ‹ùœ.À¼”š ãdÒãT ©Ô%EšF™³ç=–‘…ã²# ÄmM˜P¹H:58j<ˆ>Nã‰t@hœüMw Rð'ZThPKõ«ïË7pèß$ˆê†EëȉTz~Áw¢ŽÙl¢øØ(º¨eC=¨;……òè»›Éf·ÑÄ뇋ã’þì:t‚š'Ö£ÐG¤.×z_ýº‘~Z½MœbÉt“±4ö’¾ÔûªÂé³T`µBwWµ)¿½Ÿç“‰L‘@ Ïûª2NÕ>O§Ïú‚RÏg4²Xè¿ßJrN>y×UÔ4!ŽÜçN³Ä8wiJ¬gEmÈûZ4ªÇI„v×ï8@ÿ¸†^}äãœ*8)P¯N[:–²QœHIÛCá•wã©ê< 6Ñ/yl„ @°†! .47Îagì8íû¿:‘ÂÙP1N’j¯(PÃ)€´Ý­6}¦1LM{ᩩΨ›ÆyU¨Íè %´Ò8Ib”sÏ ofØçåNÊÙ€7ª-Þ‹(¿…pe#$]÷w_;„%]zq'Ú´ëýû½Èfs¬`&ŒDw_3Ôµúe«ÍFs?ÿ•Îgd_pMž€4š¦ßu5Ýty_JËÊ¡o—WͧB¶ûÃï›ióîÃò°Æîa2 s PšêUfžñ=¼ùÅ<Э-=u÷Õb{b¨Ÿϧï#æÎÙôLÁÌW‹m: aÀê­Êg·8úw.Œ“„”óU3×ãvª2O›4i/žáÑÂvbö§œï!q«¶½f7Lõ줧j{ªcER`Ö¬…‘V;}"C㫹v`ÇöÏøÕ•ŸS‚½`¬÷[¢i‰‹Ûåç( ôüQãd†£Xe´§wrº¶Ÿv˜é!iÓˆj‚d§õëF é~3hŒš7¬GÏÍÿ†VlÞMÃzu OZKˆH·ŽêÆ(‹>[²žv>AÑáÔ£C3Álý÷K)7¯€^ýø'¨ÜL4ó¾dÓÆÞb2S“„ºbËÎÍG»kÄ=¬¡:™šFŸ,^M‡°è §Ñƒ»Q¯Ž-Ľ ¾^A‘á!Ð2ôÇë¶ï§ïßBÏN¾Ž­ÜBëw¤à Z½eò]LÛ4 çE+·Rfv.u„í/Wô£ð°Ú²ç(­Ú²‡uoOŸþ¼–º îØKú8úwA0äÌ4ñVU“_ÌÓ˜ˆPhë–Ivž;¬Qä-;'OÌÁœ¼| )* €I ÅœýjÙFêÞ®Y±ÚÏß6î¢_×í¤´Ìj×,®Ð…Z4Šç?=-Øu#z‰yuB€ží›ÑžèýVRZFædsqÝŒ9ÎÀ ZqólÛÞdúyÍv„ø> íj]ÑG‡ écÌñ:˜ß'aþºmïQJ¨M®BõëF ÞÿîJ‚æÖ„°—]Û4¡ë†÷2ÆÀ}ý¸j«À»iƒ8šxÃ0ŠÀœ. ‡2‰ZX!.¦Q55m¯Q®|¡jóp9ú¾•û·é¶~Ø­çru.Lõ€•eªW]ÏAõ«(àK œ¥3ïa-'^Žð¿ÌÐ-&zÜ— ú:pú| )øü=´ÆÇbAW‡wX¨¤gåRF¶c>ü¼z;¥À¼êþ›.¡Ëûw¢Fñu‰‘½/j!P¼vXOºãꥢ›_`¥]OPݘ(±(D¢`zŒW¯ÛÀœµl\ŸÞúj¹0Ùâ†X;•™g´ÉõΦe‰ã.X\òâ‘ÉÜ/›i¥aÁûÞw+i`÷64iìp:vú˜À=¢~~A%<Žì*ê ¦!”vý½P8¯\µMå{ EÆ÷ˆÍ_æiQôÊ>b>dðÍ)^py¿N`ôíôÃïÛXÝèºvX/zäÖËaâi+R/5-“þA3_ƒ»·¥ß1o^ûøGº¸skÑ·#ý†ë@²Ã¨¤yÆB†\ ³Õ8úëÍ—‚9oJq˜ë ü[úvùfªíÙÅ`‚ré›ßfr³™šâž‰×¥;®Hëw„ö÷¸o?úä9Ý4¡® ¢N`øyÞ—„ƒ¸©œb£š5Ó³åʪ:O52­–}ƒ]aÆ©Z!Èf64N°gW¦zÕú4TçŠÞ§ÀÌÙs§iºNö„¸5“žž8q¿B« aç…diÀÚ ^pÖ‰ ™lx%»¼8îmÈÒæ]‡ÁèuÇù6ºvTÏ€bšâŽ?rŽñ¾² Û¨ìý»oñªmÐÌìíµiÚ€þz˥ŶÍ!kH!öi=¨›ÐÊW9(ÈLc†ö¤ÿÁ¯i`w Ù~ϘÙf`¦£-æûða±oÔ`-Ó 0MV0UK€[Çhxo‡óh‘v>)æÚ¶}É¥Î3fðúwmC#Áp¹B¨¡µâsé ü\èûÇÇ#û^$páßk×vì?&ÚX·ý€ð9¼sÌà"Ã) n³,ˆ O4ªdd4ÊU,È9V”øåhT³hkôB c,Xªq2›mÈ+W¦z’j¯(P)ðܬy—Úí¶rlä¼6}ʤå±Ú+ 8)`ÂÂÀáÒ¢k¦€ðobÜýqbœ*¼Pà›< y:íJq|ía¥GÚ//©ßs0ÉkÛ̹ ’õ®Ðš!+½ùå2ØáÖ+Ó>y½´}Xh ïÓ‘6@’ŽDrÔæQ Ò/ª‰KpÁ ZqÀÚ„Ò€5Jl†õ‚œÕÜž 3S <·ÑTj$žh£R»ÞÄÌmßΜ™é’A'^2²o'a^ÉÌGi0¾SËa’Çæ˜\v…¯aÆ·1é°ÐJq›¬!â… …‹™‚ƒ-8v‰@`“hLJ›g,øZÍgçM½;¶¤¿\ÙO˜“ò}‰.‚…ƘóÐB±¦Ë\>^¼†ö'Ÿæ|çÒ³…V‰ïa¿®¦ÀË,¾¯<ž`TËÈ>a”«X¨ô<íß¶íÖ•;weƒƒ Çk¢Ù‹³ÞIxlêqtW³¸¸Ô=)©à­u(œ(Ö/VTtª¾¢€¢@™xnþü”gý¶Ø-бݣeÞ¨*ÔN èvC*jÒuÅ8ú,Ø|Ò M³:Ö·Px‚ü±YKõ¯“äì'Äæt,ÿà‡?„IÝŒ©×»W+ö848˜÷hGí›'Ò?ç}-|3úui-"ùñ 'ϦÌÔ)ôß©U#Ñ›æÁÄNBòéÒ#†Âï%§Òð ¢XAõS |뤨4l¢ÂÃh<Ì×Ê Ì`ÜtY_zùýÅB)ï;|â ü„¶ Íû-[¿ÌÕº"¬hpPQyŒþUÅAióŒýŸ¼ójÚ°ó}¸h5Å® §ká¯Äp þ|NaγŸkH9zàÑ“©4cÊ Â„•q—JI'/djJÃAÞ[Ö><4æ¶Áˆœ™O¹ùiдå’ÅR[V‹U»>lØ0ëŒYs6àý5˜[Ê7å²Ö髪µZù»9ªßŒ7fŸE ¬þ6íËÌdõºa¾Wù–ÕŠŠþBW. ËN9ó%Þ;…N·Zr¨‰ÆñûÈ_pTxø KãäXi#tWÀ0N©€ÐÒ/$ø’2w÷J(º”u\÷œaßÑS´tíšõé/Ð"Å ¿×:\f§÷)ç©a|˜Õ%bçh›ƒK°ßÉž#'…ó¾«é“{\wXïöôÅ/(;7O„&‰ ‡ÉÒvÁ°ýº>I¡à  ­aÂÅæ|Ü/›Ií8\¤É„¸:t6Zh“.Càw³ ®¿mßQZ‹¨vVÄéd_&)EgGõ¡=ÛÓBHïÿ‡hyÿyèf#”k?²ÑésâZ19x,…ØÌð‹¥ëe2TD) ‡ŠÎõÐàh’z°¼|GŸ¢ãÊüñÀ<å0 tô®ëUOÝ£ég ¾®;m7=Õ¾j§J˜üô =m6ëåxÙ7…AeC¼àÑXªÔªºÙ€Œ*CÓµãhû0´ù?Íynš#:Ž7:+g›3gÍý«]·ÿEVGÒÄû¦Ož SÏ‚š§ž¥§7[+kžLIk‹:!´äÇﺫŠNoަhÛe«RŠÖ¯G6¶ž6¼™©gbÉþM|aîô;J¥Ë$„=–p ÂzóÆ¡ÇÝÝÞÓ¨k‡÷DUM0?òÞóyÇ5ÇYN®ûŸ‡o6ªpÎÓÃírhqW¦­^(zø¶Ë…öˆ™; ¡[»¦‚ÉÊöŠM 8Ä4oì?¾U@‚£èR$=t ý3³O÷9é>wJ"‡û}\5K¯?v›q û-=Œ $¬™äyÆÀæ£Ü îIsŸ™t­¬*ö%ͳL¼Fh„Lø³•+D„Óßn¿‚r’_&™æëÌì°yiq¿-iz8¡ó³Ž'ù) ×>Ë*["*ù¶,£\]-ļF/Ì„‹¤³½–-[f©V“]ÃGÑ¡–´Ù5µ(¯®‰áìW{è¡WBÓC3ïÇoúA«Ýš¨™4=*<ÔŽœo1ýÑ9ïS¥j¦@nAý|z¶Ž€?&<·™Ÿžy9#_mÜø¿Ï<3!××èÍxcî`0M/Ë~Át¿9}Ê”·äq÷jžV‘€Õu{Yó4_³]lÓˆ<“”ÔGƒUŒS1„Þ† Ì<1´¨c¢Ø0V0xÜ™&Ùº»ˆ<_Þ}Iíòý’i*®-^DJ¦Éõzò)Pp§€dšÜÏWö¸¸yd)ýõäÊ4¹ö[Úo€X×ß_Òy×¶K*…—òó«Ÿqš~Ï=§àWto±ØÂÖ$%±ÃeµI¦aC!?’šfWŒ“1[|^à™éŽGŸº,'4g¾É®5Dž>…aÐ&ká¡!e›WøeÕ¡SËfúÛöCÚ… Û÷%¿xÒvüÁ»žxæ®·ÿõÌO¨ïXÀ¸ÝèéÃÏŸß8?ßúz/k¸¬iw¿úQóÔD¬æ&J§èõ““ÔdÒWU3®ê¾ô•I…šª9•“ £éñˆº•ÿ©æŒ\DQÀ¿)À‰œaGïwàªqʳÍÒ×HÃâo5ÖO-¸_›ÃÏ©Ú'|$3äcÓ”ÆÉ×SAöÇ óOüýA³9è_‰ñ1úHŠŽ4¼HU`“K};µäMÛ ßhäÏkpìôùïï™þìcógþý5 §ôð¹Uïë‹…¤8üÞ1õ¹)´Sˆ¥w‚Á8ó¸T®5O+G7¿¼«¤yjµÛR̰(aÐíÎ܃~97¤X»¬í‡‡Óœfz-ë*\mŸjüþC[¯ì/¢RúFL‚\4NVkŽ gÒÖHDÀlVo€Mõ AS¦z’>Ú3cIJ ;{êa0M/öêÐB{lüh³ÌÝç#5yò±* QÍÓ*/n•ó¹M`šäÜ ¯_w} à/qTŒ“¤„Ëþðy'ãÔìÐRçü}`Š+(æ <'Jˆš×Q—bH¤N) ( ¸P@·;…-ˆhçr¥úŠñš¶_"¡þ‚Æ©åKs據êÀq Æ Q9”“ï‚`š:uê]§ÎœÆ bõÛ¯0 ß‘©æôÄÏ‘IõÐÐðù#GŽå¨5}!=ÿƼ¾v»ö_I1ùàé)“Œcy¾‚{5O+H°@®“Rýœüü†Þ˜§F^(xôåü|Þ$¢„Ó± Ç"ˆ¿.M•ÆÉçÏ@uØX´r =úÚÿhÆ[ߊ¹œ0·¦ƒú…´×/†;qâÄÈš ¿¦‚j K®ë&ƒq‚BN1N¾™!<Ù9¤Ëð!Sí:%À§É¬4M¾!~uõÂÏÉËÍv]OlÔµíý…sÀ#/¥¿ývC»fÿ^)Áb|š¶9¼^ÜÄ*ŽUÍÓ*0Ðn?všó¡; 7/?ÂÓóT¶í­½G~LÞB®:Ú=šn'|`$F™()º( ( ”V+ý¸j+Mºa8M›0šZ6®çóÏÿUŽ¡»&¬æDÖþ±Vâb·SOYöùÁ!dŸXÐ)ÆIÃ{{ž„l.Á ܰˆ¨¨IZ7Ö•O“÷îO-ósæçv_áà¹P¥ÓKï¿‘Ÿ›ÿ=Þux¬h,Õb¢ë`fU§N5O™˜µ ’OŸ3Fܼa¼Gç©Ñ° Šqr#îé,g šFQ¥›é}õëFš4ó]úyÍö"­Ìý|}´( ¢+Á_( T†È%BŸ/Ý@ù6úyõ6Ú¾ÿ%Ÿ:KGO¦Šæ–­ßI›væèO"¹í‘Âó•éËßî±Ù¤L¦’ó¾ùoM§-²O„ï"˾ޛL.'R¦z> ¿!Åyí}Ñ_´Ù‡b£"è³%Îðöü𘡠úë-—Q£øXúäGG´á}GO Æhì%}鎫¢¿zÆ¢55-“>]²–÷hG#Àä¬Úº—¶ì=*ðŒŽâµ9=zûÔµ]SÑg#gøæ·´nÇÁHÝ0¢µnâðSY±i¯ÈV~ÇUƒéªÁÝéË_6«^( ”‡ÁANY 3ö™9¹å¹Íïëäæ¹0N!þÃ8q2J¼¢v1ñ®ÒÒÌg;W1-6s–K¿Šqr!†Šü]gíkBÍA–úub"ªiba[^~A¥‡™—ï0ë©t¸‘ñßágIð˺Äßaƒ:Ñá&ͬ%/©qªÐšïù9sFÃŽ Ú¦Bд…OM<]Vqï±yZæXyiy‚ú€‰×žž€S©éB(_\[çÓ³E_Yæ{ w®“ׯ+š­ê<õnåmǹŠ)ï5¼^º‹©^t94NVøutk׌Ú7O¤O^KOÀ!ÞFö½HLò3Ðø L(í€ïGÿ®mŒjWéN,‰G&eâ àW è‚6›RdD½òÁbRGE„Ò0fìtMÔÅ]ZÓkýD©hÓl2¡}¢}¸÷²þ©C‹†FÛ\@R<ØÝ!åßq ™öàУ}3Âd¥}:ˆZ—ÖMè»å›i×Á“Ô£C3Z»}?õìЂ®.®°}ßQêÒ¦15ˆ‹[B½á·rY¿jY¹¢¦Ê@ž£iÙ”‘GÇSÎQÇüM|È-paœ‚ëøÕ€ 0ÞŠ÷ƒøÚl:ÿ üN¾BÔdXáú¾V,ýVà= ð‚”¿í¼XA¢ÉÈР Ÿ3N,üléz|¿Î‹ïS½:Qtíðžø®4§8÷Ïy_Cxi‘ïÕ¢?¶Ò’ÕÛéå‡nß fHŽÁ‘¼„ˆüMD´¸"Tc«‰ç|'αÅF<úàoàÅ[©WÕƒŽÓÛ_/§×þv Y.\6-Y»uog«ÚŸ§îççn" *x.0âåfœ^˜3§»Õ®ý3ámuãðÐñlþç8®òß*ÏSoÏ1¶òyfÞWÅô›/ÖBÅ^ôâÉdD¤ãµÚeý: Á¼kWÓaaÄkBÖä4ÆzóFüfxÝY|·b3Ù°öœxý𠪜ÍÈ}ño6",ä‚ë=q*52³ÂÒ0X]5¬ïøVVežVOÔ¿ð à‰V¸ WS½:¡e¿c$×0¯ôÜ[ßÐ[ö‚‘q~£Ò2³éãÅkhò)¼üÃé8x÷ ؤƒëŽ,œ˜Mг#Ýn˜4q_ï}·Ò .ÿ0’¿kÛ&tËý„¹àŠM{  êJà]’ï\È5‚jô |M¶íM¦Ål¡SgÓaúçp;°Ú¬dµÚ0¹ó¨i!²ÂríÁÂwòiÚ°ó$Øê»ã=ºóLJ7^ì2ƒñÙp~pÂÀAa^ÿd !@Ý}Í0fZ±iÍÿò7 ½i$]Ôª1%â]ðçî#E§?<†¿glâÎfäÃa:Î :6Owý®ºáîk‡ˆïê0ÿeKŒî`²B‚=ÇŸ³ ±Ó#Å3Mî¸øÛ1Ö ’Aá9!çG©ÌÏŒ9sA&ü˜&¡†ðå€),dÌ„ 6šæN¿C^ !N¬MlRÊL8ϧšéYÇaD…%øM'‰ÖÍ`œï5üî»Êó¾ÜCÜ] ãÏ­h}9ˆÀé‹X¼ -\”BÇçc`]¶h`Óqù;¿æçûަ@X·M0N,Åþ}óásËè±Y9GÚdpÖH忯‹…zR°WÒ0Øç·)‚Ѱéφl˜úÁ*ããÅ«Œ¦.¤ð™¢¯©7Ž Ö'8ÚªX3Gî†og Zºv­Û~€¦Ýy~ÃŽž^ýðG‘R!“áÛ»–ž|ðÞ}ø$Ú^%Œ¼P…Ä\6ù®Òü€‹ÃÇ}YÒ+^<9Ê|é¾þá‡Ñiç3~À;CF9o²˜G=yçN«Ê#ãzg•æ©/毅š7¬'pf7 ±åqNn>MãsúË•ý ÍÓÛpµˆ‰ …?xÇ-t~†54$®í…Û ÁÙÿœë-^µõ,á>>îa¨ÇÀî‹VnšŽ-‚aëg¸`ˆ %üaë$ºÀÛ!˜Å­B; ²Ï‰7 .¼|þþ±ô)¢ÜÚ‘ƒçÖQ†ð{0ˆ¿Á÷žñÔ£m‘^VA1°¿YÖzE†… ÿ÷+v¡ð[.¾,t— IÉc`È¿ºrÏSç}¾/•ù#ò=JÕÛ#3•ÖôðÜZèCÄíð":2!LŸ‰5O•fÆ8tã"hˆXËÁ8Ü3C2œívÃüŽýGd WiÛ–½G„™ûR±¶©=LùX³ÄÒÖ‚ñËàç5ÛŠ Åæ~›’SÒÁãÂTb ‚D0°$oíŽý¢?^ü²…;ƒU¤!u (P äbª˜Kwê|V²sLd£ì/…àÐ Ö8‚^-6µZ¾Å騢)“|^ØKI¾±Á7­ ðBo%4Éß…öÍ ¦IVcA/º˜qb‹ 6AgؼëˆX¬ñ"±Yb¼0Ï›÷å2˜¶;_¢b)¦8 v,>Ó!dS$6Q5¨+ÅÇFÑÿ[J9`ÊnÃB‘ó̽õÕr(‰ûeÆM~£O¬ˆ$6ÃgdfìôBG ³Ä–l‚Ïu%”æìŽ/´X0˹à:7$ºEö¯/Z’ž–þ5±BÀ³¼8I]÷äĉÂO²H媸â"˧Õ5Çä°™á9Ák1 é˜Ï,„f>ìk¶ R3¼Ùƒ |ïG‹ÿ ¾Zј!=cÏÃØœ­‹voC“Æë¬`XÊ'aÄk½Ÿd­ðSooÜÆ}Îúô0`Át|×xNfd;¢B³ïûßÿf±,PàN8hÙG8\ Áç¦\/~ l>ËLSyðÍ£ÉÏŒ޵„0Â*2O]龜²bœðâBÆŒ¿¸Ùüé?¤¹Ÿÿ*l_Ä9Í…˜ézý“Ÿqm™ˆ¸Ç‘ýØNûRø%ýIÈ#¯pzݤ0‚ã`zÈLÕÿ}ü3=7ÿ|Ô ¼9²‡™~ ç§¾ð>½üÁb: óCŠeQ KÛ¦`Ú¥³¬Ús=-Óñ±cŒc"ýo|ßu×q|©ö¹DÑð]hîkêZ#MŽÕ:†‡„Ò8y÷¸.J«åŸž•#¢Âº3Rj^8²V†­4ØG–Íóx±Çfzñ•5>Ó&Œ’þ F‡¿C¼è+ ˜9zé½E4gá/Â"£sçï#Í>zû•ðçíMVšø^^ïk™n=@ø‚°T¾!.±/ˆüÖmJ:$˜·¶`]…¡,,ä…'3RR/ë¸ú·Ezé,¯»âãn¶/ëxaï:'ŠmkSúÃa)0̨ é÷LŸ:q™qìÙ‚+Nž§¾žc•ú˜¡Ý…ß83Gœë™q ìÏó’}ÐÙ’( ~ Ûö%‹`al"Zš[$36楼·¤=û}}òãZX?m‚Ù¸…:µrþøžˆÄ|ïuÃÄüwoƒ p4\ŽÊÜ·SKÿ| û -bü9 t ”ðûÍ- ÜR|]µMìžÂ¸bÀuNsÙ?NUÀÍ?ö6®Â9­PºTRŸìèêl2Ç›ŽF7cêõâcÁÌ+°ý7oXËãjæÄ/r×cÎΦ9y£©Òe®Ÿ7>/ÏÉ6yÏ’´ñW A \q5°+]†PâÌýóÇÊâb"é±;Fá+´hvÚœ¯³Ö‹íeùÄýñ‡Pš5¸Þ¯ÊŠîàEFM„ôL§D<&Âÿ4NLs0+[!cÎe,ØÏé—}¦Ü\xN‚¦©ïޤ…÷öÕºa¡[zÖ…‘¸2!Ùæhš2ÀB/ Z ó¸K.¾Hhz\ƒ Å  Á¦w]–ý£Š ¾Ä$dÁk†Æ]ÚÚ*‡i•$më&õ §óä34qq–o“¦³…LY_•X¶> æÄ}„¹_Ÿ‹Z\ð}“²¦…¾É,0•¾šåñvÅGâ能œ%v5söœYxG\/+`]ðøSS&¿'½´—xñ¾Bàë9Väl°8rÖ41p°/†*U¥xuB¥šª7…¹ÈB³‚„€›àqÔŠŠÞ¡@§g[Ì¡ø ´ñNGUlÕ®iû`ÐähE×ZU±¹ ß>vìØ‚³æˆûðZryËV¸)uCÙà‡ëVö®ÁþKþårá‡ûò,±<’ÎCØ&u®À&¼œÐ“óJ(@èúWmEª‹4±ðã Ò™]Ö©Ìž¥í÷#ú;ð þûœ/DôX6Yï ÿK }`®÷Á÷”9–}™?ýi­ßy9››ÑK`?`NVÏ~À¼ØdÓD7¿ZÀáPì|˜1{öXÝ®¿!ñšañ€Ží'`ï ²+N\®øÃ» ‚oÛL¿¾Päð’Á¹*4b*w:ϱo‘¯é3D?æµÜ¥p«`ײ€™Þ˜¹ï„°ÿ<y–G)À$Ö¿ùÏšQN²£ÐïŠ5«2ÀãÃÐØ/ëq¸t”…ï¾£ÒÞÚ&øý ¥ÌÓbëWãIÅ8¹?:Ä©„KÏóŻà u¨( (08yÖ©mªÛ8§Y? B³í×Éá‰Jk_ãÆ ±çޘ͉œLø€#•“nÂ9Gb'_#£úó Ú!©ûÓ÷ŒyÙ÷£$³ï«ž7W`£N­¯ËÌføàšE¹Öá2/Ü\ý€Ý¯OBèew`ÿ Æ‹ƒT°4kÀ"ÀáÅyswäˆ$Ö¿KhšlŒ«4?àâðqíÇ—åçæÌ®ÛèCTÄ¢¿Ç5ñÓ Ã† sªÏ|‰P%úòÅ“hÝÉùŽÜ 7ÌHycmŠk$c®æ>g8•‹ë\u-sý)ãFð΀+t!Þ8J_Xhá£äÞ®q 3K1ícfǽO¾×uNòïŒG°–‹s¯™“Tú.øæw˜!F =þ-³Fìs¿B´èãÔÁ+J—ûØ}øïHó>yˆ{'—ˆØ{ç¨ç[4S1N^ °jRQ æPÀÕL/!Žc.ø'LŸz×Q(!ç„Ê ^zÿý¢†ø>@oVC‡ÿßÅ‹•ÖÉ4÷‡.ض$¦©,üXê]ÓTÖ½e]gS+w¦©¬{\¯ó¢²´qI?àªôáÚŸ'ËHpÛb ;îm¦Q’)2|ôĉÑ3<Ù™Úªî9æÎ4yrȬ9b¦Ë—Àóšç·;p` °Â¾\lJËÌX‚Kg®ïŽï1˜éJ_)öé’y°ÜÛ¤cÅ8¹=­¨`çdIÏ\Ó›Èq¡Ë( ( x§\4N êvò^GUlYhw4:(›ÉÏÌk%˾ÚëšfÖ 9zÔ·+_ Rõ£(àçxæÕ7pRÿZßBG-Y£ÐËž?>ÕÏQWèU#®Ar^Ö8-øf…ØN"´ÿ#·^^¦ êÖ ŒúEˆðì-Aˆ/I£LõܨêÂ8yQãÄLÍ8—Â^EÄÅ?“–!lX—­ßI ‘ý|ÂQf!á'Ø;‚ çÀfÕ‚Hvƒ{´'™£é]ØiìÛI8(r"AN®vÇUƒ! ØG[v¥œÜiˆÃ¦ŸG®¥›w iAÿ®­…­,;ïrfêÞˆ×ÿ3’¥±J–cü+PP(\5N‰uýWãÄ£Az…}‰\ šncs=—ĸåoUjÁ>Ï,EQyMšLTUÚT÷* ( ”Ÿ÷MŸÞÞl^ ¦ .…#\Jf¹ôñ©wB#­@Q d p 1ÎïY°Ùl´ó ÓL¯s§/aEÚñ·ºJãäöD¢]4Ni¹ò3ïV©Š‡œizæÛßQTx•zôÉO«é@rŠhõÐñ3"_GQáÜLɧÎÑ+ýH•óZ<÷Ö7FèSÎ2žåЬ³MꦤÃd³ÛDì}Î'Ñ‘O8”+^’â>V¡vGµ7>]*¢ÀäÂ.wŦÝpr]+œ[ëÕqFI©âÕíŠ5žâ|æ1N³)!|¬®‚4ÖŒ8³ºn÷½ÆÉ% yBffÀøQTȪº¢€_R`Üøñ-$6ü ‘è 2ÛsH©¦éž$¿DX!ðØ}ä¤MësùšŠqr{Šõ#œ$9™åßå=˜L¡!DíéEàdÊ‘O\óކŒæœ¡yÓ®C"·ÅÐ^„Fª9’ûñ¹Ò IG>‹¦ õ¨I•!î>G@/E§ÏePtDm“ÅÀ k¤8{uÎ+PP(žXnTL„“Éäç | Æ Æé>eœ ¼aU¾x¹ÂlP7nœÒ8³G¼KA#G6ìܻϧÐ1Éféf²\6}ê½z·gÕzm¦À¶½ÉÆð9ò^M'—PSFTÅq4Šr’äx†w¾íl"æL['²(ÃÒ,±®1Š3`t83¹„$Ü“Éöä9Þ§£Í’àœù\ý†ô쀰’Î>š•8°¤öÔyEE‚–øWƒ - 3ÊþZ0‡$wtélªç3xóÍ7]¹J¥mòåUGµ–`‹eäUcÞÓÔˆi F¢Žš6õÞõµ6žÿ{ß­¤Ï—*²2]3³s‹$½íÜZ1Nžžo~ÓGÕ‹,4×ËA ¨s9ž7×ëм!D~ ŽL Ó\m@Ý Ñ²I}J‚(û(qB²½GOQ·öÍDµÐL±‰k“6»h¡8£u2OK3¾®àôÙ<™¥}:Рîm 2u2Nî}ªcEE²(€Lè.§V†—uCµ_7›ìû]ð©Æ)+6ÖÉ8éºbœ\„*zŽz’ç–œP´¼‰DKj#Îóê¥qB|+“ÙÜXà­i¹V»õš§¦N\Hã\í°àt¥Ð­i½¶íO6rGµhX–NNeA¥äG79?f~„Tu£ÂZ§Ý©mÓ1hbÃ'7Ÿò­ÈÇd)4YÑò³ÒÏßò¯iÓ–ê˜oRÿïÇ5B(>¢ïEb½VV .ö5w è5¸g;úâ—õt÷5CÅ\OB°°$ÌmN½‚c.V€g»nm/m8\98$»?ÁÖ½G t:× 3=Ô…oc¨µ·Ð(Ú…qJשS}ÏÓ‚³•_Ö¯“ˆÏÿõ²Ð@¥‹N&Œ\¤3f‚nÓsËåãcKàÀ¼Y‘„4OÂÕÈ:}ù€®ý踡/"çñÆ/SGþ Çùâ¢É6Ô^Q@Q x ì?æ\{´j8´øJ~v–™¤³æœ€¿Q#læ=§Óâa_ iÏËsùÖ(“/h^Sú¸ûÚ!¼Iû“ÏÐgKÖRû Å·¬¸ñõ€5F§GŠgšŠ«_SÏe#"ïGX¼s`¨B°¦œ<1éõÏ.‘'ÔÞ;ذó]?²¥!À× ¾§W¾™d ®ÇR¨ÖaÂ]ôbu^§q°/} ?? Éf3a™Æ Gœx–ÃÀ,õC¢æOðœ9LxÏͽ3 J´z¹›R3+Ú7çOMÍ—YÍTUGÒ0Š9wG®ÆdáçäÍLE’šImRqc+IÒÀÉÝ!,Ô‘ïÎý¼:VP(?öw2N-ÀLÏ™N§P¾d.`GqŸ0Nzh¨*::)“ñ@T¡, Ô¯-¤öÍÆÃºbL>-'Ž*»jËhÚ‹T]Ú4¦¶Íi!Òr<;ù:a±ûðIúxñ*ø[äÑ,2Cƒ‚ f"¿ÀJß.ß3÷#” “ùp˜·³dò±Û¯ ð°`´³Ž6ï>,„’Ã{wÁ™ÊÂÕ®³oÉG?®6®“~xßž'ßzõÕŸü¿šŽC›¦ h ‚~1üñç^˜ŽžDʘ:F .f"X+µi—ãÕË“]z5n‹hÊk†’hÅÚÔFöÚ¨?ÑÎ^Ìsbœ6ìøþ¤òhZf²OÙvuíÙ$ì£Å«élz–Âé”Ô#`š~4N¨‚W)Ê x!°;DfN¡Àçd ®¼ü|Z³uŸ¨Õ nõc£å-Eörósu…øØ(Ã\¦˜‚)s½^eTÆ&³º·o*‹5fïü˜Õ˜!U} íãšš]>`œ8ì¸EEÀ @Ò¡o D›ÔïM!ÁQƱßtý¤ÄåYööoÔ §Ë´2Õó6½kRû?­Ú*†ÃZ#67w]d²äþÚQ=‘³°¥¨ÃŒ“„s`øøÁ¿\&L…:´H4¤ü\g2Î+N gàà Û÷%Ö`5ˆ‹[´¬!à$ñþ ¼`ýhÑj:—á`šØ´ß ZZZ–“[ôWäk^ûn& ¾ä™0—<_¤Ž˜wv§É¤)§~™2n„1j¾ì èKq0Me+#þÕ¤A]Ú±ÿ˜Q— ®.E.øÁÁÚíû ­.§Ïa?ûšŠq*扶«ëdœœµ“_|‹S UÌꔢ€¢@m¡ÀÖý ¡vh>Æ(DAÓ qrHã5]—9]¼Žºn³¹~k«S¯÷ª:¨ xàæK…©kQþóþbøt¬¦{®j ­SkGÀ8ãDaýIšbáÉÀi=âê8… °d3¾åw‰·u¢"À@5Ò{ε¸&ì¯"áT¡²<ö§}Zf6}¦‰÷ Ì4]7¬'}¿B¥iòõsjS¼ßþŽ9u–F ê*æœ+C_>̹ôÙç"šýÙ/ÂÌ}÷ä|.î~9—“WóÖ#:ýº¶6Ê5©àú1«IãªÒX"Žœ#ë˰SB}8g£¶qNfªJ«›–)çvÑ©³Ûþs(ul~u@E#í¤4ÿ€ØÒgŒäNNGQåãPsÆ_­A-Å †Æ§ ¢‰±ï;Ö7EþCŽ&ëš±Lˆ¯Ü”ãÔªq=vGü$42ccÿã¡=Û‹äð®}ùc™5L-Z…ô#“03˜¦ëGôFä@/DµòGøN®Á½8Ø? †x˜â•ˆË= ×µÃ{"úr7ÑÏK à?Å›æ/°a笌ñI@ºœðK¬‰ ô(%<ÕvõœŒÒîTåË\™ÔiEZEmœÚ¦vM.,3=~R&ƒC‚ï4NPÚGÊ^ahâ·ä<¡JŠ%R€ý’Ÿ8#4C[â¸)ÌÊ ãc) ¹c–®Ý!|~–"‡‡p–ÀæOY¹y4 Ú¡?Â%Ñ<û3­Ý±_„~æ0ç\,Kk Ûõåžµp¬i2˜&0|c/QL“/ŸAI}I¦©¤ëÅç€^®À˜wešŠ»Ç_Îqtg× ýºÔLmÓ[iœJ˜uí aúõ Ã¢dWŠ®r2ø%Ü¡N+ ( Ôd ØíVÚ~àKcˆ[3ÊS°›À89rÔñ™ÆÉªi°‘r˜êšž0ôRˆV;|½BàÐþHÃ{w@÷ŽåÆi Òr|úÓ:ZIxöÍ©MS§[GëËͳÒký$|2ؼ}˜®FŽÅFö¦~øƒ^ûøgq£ÑNºa¸_‡8†òDäÐã ]w"­5G²Qо¦›è±©C,Ì^kZrWz*ÆÉ•.åNõ¤ÙtÂ)¥r©¢ŠŠŠµˆû’1’ÞF…'RóÄ7z ™Næ2N`c|Æ8Ai°kDJãp3ÇrëòÓ@IDAT÷7MŒ+Õ¼‰BÈ ;÷sº—6Ð4Ù v~ÓYóôÒ{‹é¶Ñý©wÇ–‚9Z¾iqNÅ«w§ÈðPšŠÛ‚ýnø,:ï­î‘#»=”rrÚÁ'¢+-[M›“>§[G Èy S¹l™È>XÓÂ=B 2A²Ý(ùVÞ^iœÊ WM¾¼jË^ú ¦=OÞy•ß “€o~¹œî»qäþP/#én¯ŽÍi(„œÕ?­ÞF“]7ABßq—ôžÞ[§©‚¢@)X¶!I^á* â¢‰Ãø×¨µŒçvøâ— ”mŽ¢Ý‘(Í⛞̔ n,¶ëj›³f-ü•®C²3³Ê!Q}rüõ2uº~§NÎû¢WSFÔþ„Ž Z<ȼ—wÌ­–ñûf´UïE=§¢4tÐc=LæNSBýï(4ôpÑ ^:b¦,"b‹Ørs›Ñ¹óWaÞæâwÛ˧¿[O O×5ØU8ؘ»Ù''àmäq‚öIEÕóă Ð68—¹‘ü 8ÜxŸN-‹•–s'¸9ûX«þÕo›hï‘“FߜԖß9œßG¢@uRàøéóÄA!$\Ò·“,Öø}­cœø¥ý˺ôùÒ t&¨%…w'»V=/!fÖ6F¦Ù›g×í0ß»H0OÞÒ¾¸Ž¿Í)¢‘Ûu ª¦o3kcרii'Ígã”_´zNEŸ”+="ÂwP\Ü—drÄ8(ZÑG̬5h0RS¯Ã¼E„.ün=:,MÏ.ä›H³Û}Ã8q§B5—Iåqòèã ÄÆX@Èš”É)Ô¿kkêÚÖ!©þxñjÍÎÏk¶‹|DCz¶/2<¾Þ>¿Ãt-.&R-b¢ÂE¢ÚSgÓ‰Ãr³/Ðä±ÃDļU0#:|Gý–¼]³¡éÒÁqÒ[†u;ÐiÜ7¤G;J:xŒú2O¼ \¹y¢ìÅaRx oEÞuÛ÷SËÆ ¨_—–ŽÄ¹ËÖ句ˆÈ·ëàqÊB^¥[®èWïŠpn¦O—®£)N‰f'Xe\5¸›²Ì¨(1U}¯P`É:§¿]û‰µ*¾oÔ^ylo”_læó"è0Ó´#¢Wµ1M{fÚÆçëe›i^Ê,c\= ®ãg¦éŠ­ÕÇ4ɱ1ÓÆx0>Þ¿ìÓß÷ê9}BNzl fšâë}ZmL“ÄŒ™6ƃñùzÙ&¯þneŸžÚÑ}œèùÄT¯3§“nR¦zòÔÒ=tÉÉͧn0íyÿû?ðûI”غï(ÍþìW$Ð §V`Lܯ†üE=;´ 3i4ë3G¤K'þ ´3kÀ(õèÐL0;3ßþ–rÑGG0ï}û»0wãÄœ?¬Ü*‘sÛ?­Ú&ü„riSÒa‘·iס4ÿËß`†ÛHø<»j|ØúÇU[`¶×žO¿}nçÐñ3âžt„dæ1UΦeһ߭,Â4õC^NÌë-jUðU÷Ö> lÇ:úØésbàAoD¡ ¢¶P¢V1N%îø4±ykšü Ÿ,S}üãZøRä{Å”Çÿ|šØ<5MþŒO xþ·xׯïOã- õœŠR‡éñ!|š‚ƒN MSÑ«Õ{Äš¯ àõÉO«fÞêĦz€ï‘O4N𫊖}ê&UOÒ¢¶îÃB‚iÌÐžÐØ´¦.mšÐNhj$ô¾¨]=´5n+L6í:L{\ÌÕ@{ÄÌÑõ0YcŒs11pRÛ)ãF'½Ý}˜ÍÛ4º~do¡EºýlL:H[;|iw:NÇSÎÑ©Ôtê‹ðÉ®°Ú¦Ž-iP÷¶4zP7ª‹èu¶ì> MW9yŽ¢"B3&…œ q1H¨; J‘úŽžJ¥wÁHžç@Uf”.ïßÅÐI<Ô^Q º(ÀI——mp†¿ZÚ;«MPk'Öâ°´( /¤Ý¡«]Óä>ÉXó´'¬³ëøë†ÄöÍž´—ã?'ØÁðiª.ó<÷qËcÆgÈN;ÏÎ%oŒ_öãï{õœŠ>!Ióˆœ[ç»j×4ÅŽ>uGö  ykhœ°¶ôÍO'É3îtTǵ‹Ø<€0$pÍÄ{_BÓgªŽÍI‡„)ÉÅ—"8ÈaZÏÌCv®ƒqâHs2`™s0¡sOजiˆº‰¤·¬½as¾ ØX;Ä!”]!¸D…;å l (áÒ”H?äº1ÑtåÀnÒ•š%Ö•Õ*µgæñcsóòÅýlv8vdêѾY¥ÚS7) xƒlFËÁT8Xÿ®m¼Ñ_·Y+'–Ùlvú\2GÏóU ˆŠ>yÆ‹ñ[¾y/åçç œ¥4«¢m¹Ö—ã_Æ‘£çù*„+å)3^Œßï›öxtüåéÛê¨çTô)Hzüºa‡ˆžç«@E±(ûˆñâè~+6ï ˆy‹«¡q²ûHã„`ñ%-–£¬ µ’çÉvßÑSB[”tðu€DqÀÖ"ÖÓ¸Ìæì,õ^ ÿ$Ö1Ãä­×§0Oû“O “À-»P÷B¤|ª8_#û1±•;´oÑØ\™¹”séÐ.á£T]/ßæAÝÛÀ¿ª¢í5̘¼^Ùýêmû`ö·‘8CDx¢í ¡@QÀ_(À¿ öñ“pÉÅ ¿By\[öµ‚qb©5gÏ—|*¨±_?[Æ/ /ìƒÀ× Óƽª ÇŸŽvÛ!š?ã—ÇXOŽßŸÇ늛zN®Ô@Ð…ÂßmFV>$[[Š^ô³#Æ/3» 0æ­]7'Íä}S=0À& 5Lôþ­[;W¢~ö:¾¡@›¦ ´²§ç|MM\…ü‚"ÂBé…ß‹€ c/é[,‰ñuèFä9š¨¿Ïý’øøâÎ&)±^0\‘Äš¥-àÇä]`ÎÇŒ×ôY_Àoi9µiÒÀ¨1Á*¢ƒðé9_Òß^ýŸð—6.V¢Àï¸E+·`,IÆÝõêDÑ£›þ)Pð ° aÑ[ tØìµCó†Æqm*Ôø¨zRj½ãœOaÀyšü~À3 jûf ë!ᦹJQö\ÇÏ–œ§ÉŸñÓðÏSã÷籺⦞“+5 Ÿ(Ôóï–“Ûrž&Æñ ˆy«!³·ó5PuÉLæ…wÞa¦IéðXÏ 6̃©µËè\]ö; °o ¬a1»¤àø×ýãÊÄ—}¢nÕõð¥(4÷smS6ÀLoV›­Hd<¾þÄ„Q²šØÇÇFÓÜéwçÆ_5‘ñ.†4½è‰Íön½²¿hÓŽå2Ùç„1ƒ{Ë[ÈFàŠ/~YOGO5ni3Åà—%M ª (PÍønÅŸ†)û4]Ö¯s5cT}Ý}+T^ëÙ±³QêùL$¶ -WrÛ%·ÅPãh“X[¤dÂ7êP=ÿ{T2™C³…X4Ú“j+sœœç‰ñLEd‚‚¼˜ƒÈ[çÊFÓqx¾V®ä¶m>¹‚£Dªë™,ÊXL'__Cºµrk¬ˆ‰”³û ٳĩ à6ª9~{¡0×q:–tãg6eƼմ`Ã1C79*J˜'ÎççfzXçžöD“ªšAW¦©"#ªÈ÷°²9Ü™&WüD›UÈbrúl}¶t-|#sŒf»´nŸ©.†•qAª™ìxQ$%\=¤›!4çjӾƛê±*܉G©É5…–ûÙ~‘”G7~žAïnÁ¾Slét4-w#…§ô £K[:YËso® ެ0+ähbŒ;¡²à:þˆ<§˜¹¬öÎ/ÚC'G©Ÿí¤Ø«ÚQÔÀfeÝRìu ޼MžAAõ"н^ÜIÆÓSã/®}Úï¢ß¯em”G'ä»m¤•^ÏwÐã—Øhé7•Û½t]ž…jœ:Ý<ÒBO¬¨¡e¬Áj-±a M!8v´!ÔØ~K2î2Õî)[ÊÅý(ýª¡T±ì0iVõzp¢`£ýåKÐég7’îòPÊEý)ýÚ<*]¸—r¾=™*7œ Ût2$™iÀ¯«ýw½ÑfS@§“û ‚cGÛßfe1’!Üý„f¥ÍBÙ_G¦¬DÑ'ÿ¸†œ…GyÿýÿÙê5{"AÓTÆ}–6m0yk]”~õP:õ×u¬i¬wm¦HöS ­ýc{î¤"ÖaK°¤ñú?g³—~ÿ+iÌ [è±ߡd[/:Q²•>]÷•Vam®fß²ŠÞ^þMš:þqöDÛöý—F¼‘Ы9„ñíB;Pà[3¦9,pÍ 9»ßêõ³8,ÒI÷ú'Vc+Á)Òxwãò»ºyЪ­ûØ·«Þì&y7sèuøT©¤ˆ50qÿîòÍb´åôHaúcÎhÓÓ­5N0÷‘&?؇’ÌF†f©Úí{þ»“mtý0 ½¸ÙA/ðvó }›5JH66Ç»°Ÿ™~qy"}rÐEîw²ðä¢rÂ>?ä¤GUÓ6ÆÚ“t|Œ+ÛÑžçóÈçd÷Ú{ ‘u`yì>a¯çýã)ýŠATòÚ*æ-ý*œßç[Ë`¢ä ¹Ôûá ©bùQªXtÊU=·‰ ³²½ÕR8ÚßîÊ:9c$úÉ6*›r¿”jvÑÉ߯&c²…úÿvó3’9'™ú?5<*š·QI8®XyTôSÍîöo#ÑOáÀmÌÉË…ŸÊí1ž®¿ø¯Tpf}¼ö²±Puû´³Ðä›LIKêKwLýÛp—óOÒþÂETë(£½Ç?¡V}NoA‘m¦HàÑf¥Ag`S½º¤EÁTõåþQ¡´ö1–$PíÝL¾ÃQó…¦ Žøuö¥RBS7èànÚ„÷Vl¡¢³<ÙÊ .#7_6^iE…@¤ÿ“&?Œ“¦0ÓÐ #ß›ˆ^Úâ›õ† è¥m>Ÿ‹A,TÝÆ×þ´¶FϲýqM­È#ës²t¢R§M'Ú'4á9Ð Új µý)ô%ë€4J<§XËTò¿‚h#Ê–f¾|qnNWçQÑüMâ\³ÅqùbŸÀ$¥øNíÞråxãíLáj;«ëôláî§4ÖzÊtâ·«DÛÜ•ð‡«(qt/¦N‹kµ»ÎPÁ/–úÛîåµÃ\§ª¨f;l_ŠT?…ŠG¢5‹n¸äoÔ'ë\‚ÒªíæwÉC£‡ÜÆ‚P)}´æ¢avgÝ9ý?Ô7{ SŵÂ3›éݳý ÷xTQU@Ç‹Öû¯µu)<Úª7˜ûéù5dCæö«ó‚©$ /×—-ëÓ5%8@£ã¸ ¼µx/¶[or<ˆ?ÝÂ!ÖêÖ¢ŠT»ˆô¸‡— iÆä1”™–,Oãzß­' b|÷q‚¢õœâÀ?^â ÕÇÝä`´Oé ƒ<ì>ã¦ÛGYÄ=ÉI˶nŠ'óµ¶gÊYhò ²e;‚qˆEÙò9Ñô Û;D×™1ð®Út‚t‡› }2¦ZÉq°>x€c ¯&îÉöT®+‡!ïÃÑþ+òƒáî'oÎN"{@?Ù¹Ÿ,9I,8‰CªZWè;èÀÿHôSGðÀ;âõºiîçhÁ"îS{³ù^½ö©Riɹ~Áéà‰z!2TH"G¨´´ü4N>QFóF!8„®±©^èäU¦z-÷‹ºÓÝ8x¼ˆÞ[±™5Ùõc‚ £ьɣCøÔÝ0Rí‰=¶ðºgX[L¦I£Óy¼X´J>ºµà„&úaAö8"éý™}—ü˜NWé40½ÞWjW³ÿîÉäh;xžÌÚòž‹Ã LÒßrÆÖïÈçAÏH«õüònåš‚y]`‚“»¨†,ýÒý—Í}ÓD´<Ü“IwÖËkAïÃÔþ ëí¤ÂÙOh‚³ ‚ҦġHVî'$wi}t:/û‘u8E¨Ÿñ†Æj{1}¸úá&œ­8B£^￞™2X#¿Lw=6òZÐûá4­?à7Õ#«Õ"œ¼/~‹'¶ŒTQõ|P¨ÿÝ•ìË´’}šdBÁ+9„³€JDÔ>8Äkˆ~²¦~½¦arh: ú*Õ#Ðí'4ƒ°p¥Ç\4ƒ#ì-=lc/9ÒúØãP©‡Æö2R’…E&¥ÚÕ6=È!¥=œí¯ÜPÀ!úSÕš£¢-8®\{¬EÇÙ>–MÇõâA{-yª«v»gû[$.Æn„³Ÿª7RÖ£)ëK£D8ù¬»ÆÓ½š]-[GËÉ62[óŸzyA×¶R$û)œx>¹Œ&z€&޼,¡É£f Ó=˜çµ”ŠËPïçqØU˜'è"PDKyåõHâ!ëèðž—Ÿ‘eµ "oȇ‚ÜóòV=‹Heª'¡èîû£'‹)ÿ`!-¯Qmì'Û`ù»“ëXnb)Éf¥›8„šbŽ,¡üC…b)‰²Ê” Tú¿Y±GrØ)B’t^›ÉÊË‘ì=zŠý˜|ÓìX,~M*5D .§†MîØ¢âý~F=wC²0Ï[Í‚ÔoW·>SýÚN'ýìRmúF:=¾¤†ÞæPç]5a=§ÜÇ/ã@W ó¼ªM…têŸHsmBÀ³ïîQözçÚsÓÉ áI¥ˆ"PÅ‚Ó鬧¬/¥^ßš,|— ~¹Ô·–VÝG±1gßÛC½¿;…F|øa¢YöñþÆYºìùá+hñ¦_ð:OsèŠ ¿à°álBó CU"ª^s Û²ïeºjÒ“ôð]»„oÔöƒ šËÖ¯ùÔL¹A×}Ιl‡ Ï•£ÍDõFó¬S}0Ñár¹i ûF |q¯Q“w“×!d3w }¢:µF˜ ëdçõ«ízîíe”šl£+8ôø4«n6«áWgu›ýyÁ§‹6ä+>åN`§ò²Õ”Ç‹`dCŠ…ý¬ôdºùÒñd4vër!±`·~sC±žñJ½gcTÌ\ßù¤JMÂ÷I&ÀÖ8-âˆz‹yKM0ˆðäï·ç<”¶„ò hÙ÷›-’3¼‚Ÿ/ñù41ð}’ !êƒBÈ«ì+ÆÂÖi˜ýñ©‡hÇj[ꩈݣPÛÖZ?¡µ%oî›#ê5VùÇ|×å/5¤’#êU®:JÆ«ˆ¶×$C;.„Ú–À¢C-cÙæ§[Kiãî ›Õ’ÊáÉYZ—yïéWšÚoï;ö)í;ö;p§±Bû›Èr±µ-e„ÿXÏ‘eêF£Ï L^ˆÀ^Óõ¾ò+i4›[qúU‘‘E<Ž`.Ð.ý÷㵬]ªA0ð²%X8L¼ LMñö2Fµv'U×:é%_ÐÒ »é+×]ÀAkú*_§¦pEôJþÁzåÃ5í‚s(>­‡;OMï·ÿþ€¾| ø4WDÓ ÖϾ¾äîuÔíEI|Ô#1xOS ÐÔ[`à€5‚I’öŽÐ/˦Þöä…OS ÐÔÖ3ºÝ´Ð$iïHûÛ¢+Vî˶F‚žBS[0£BKL’´‡³Ÿd™ÁÐÑÞ¼BSÛÏðŒqB“¤=œx´Mgûrüö…R¸‹“›‡³µÎšÕò,QûŠl5׿ÏïÁõùÖk *ûÑý÷W¶ú€ºÙ¥€À„ÙûÏ×í¤yo.¥Z6Çõ=3S„yšš|Ý `®\€OÃIs,¡Eëó;5·K1K  ¼ÿñúb6Óv*>m¦?šò©K¼×x¿ñžã}W)‘«Nîš rß¡WÞ¥S"%­”`0ñÌeèrþÕ ÃÕdûÃI¿ª÷zì(4˹€å'i× /u¨$/k<ì ®Š#C×NN D£ß¥C媇Ð\.¿¶‰ïE\ÛÄ"x€*OSÚ¦ÆÒEÏ1) …¦ÅlîôþŠm”˜`¥LÖ2µ` ÜE[~²áOÒ3+β”ãû?}Òh5£~¨ý%‚_Á§o.Ú¤øÔJë|úî²-â}¿‚ùë9Ø”/žµO¡D[Ç\ÝU( CÀíõÖ NZäýt—ÞÏFJpòƒÑu¤Ð¦íûÑ•ÛÅ`´G†šÚÛ«.„Íw—n¡û‹i,j©ÛÛ¦XÍLaž÷Öâ/ŸÙI|Š÷ï;Þ{LšÄ3¯*Á)HFRÙ …@WE@÷j½ý´ëZä5hõ‚ÿØ(ÁÉ~×=À  ƒ§ªêZzãóMdæhhšT àü^ût=Õ²ÿ“2… öžp²ùè«ìÓdáu(Ÿ¶…Vó÷%Ÿâ}Ç{/…§æswÿ«Ý:8D÷ï¾îÛBëLü©”•™A©)ìdlKàè.Æü§W>¡{mùÏO}… Áj°ˆ€…º‚]t DÀg#uæòÞ¨¡&<üU†ž<ÿhÚ©¢²ŠV¾¿š£sÔ.½Tõ¤B )ìoUSP¯qR‚SÓébW¤yžƒCm/Cä¼»ðQæy¡u$pËHM¤Ó%‰p]Åë<Á ¦P*uð+¢ç!` |ËŸ††i Ÿâ½ŸqÁÁ§ñÊ«êí ÔS …€B ë! ‘_ãÄc¶ˆû81@‚“òqêz SO1Ls</¯sã=kwQÉà ¡Rè?Dw[±eŸÀdzTèH6|Rò+Bã_ŧ ñ öLò)Þ{Lôâ;¯¼ª§`¹Gå8duÚŒ!”ýµs)ah¦B%àµR’Æ÷¦ì{Ï¥ÔËÆE1Cƒ¦i@Î…tñØïÒˆ×Å ]Q'D¯œx鎈›êñXÛÂkÐE½½ªÂ°!àÓ6¹9”¿“?Ík99Rœ d€cUƒžáèe>’p”Ïe€_0ž•ÕönŧŸ;Œ²3R;¥kÁ§xïñþã;¯¼ªLõ:…ýº~¥¹]BÖþiT¹®«¥uýuƒ¤_1˜zÿB*ýøKa…¨’D`ô ›èê)OÓö Èf/–—ãnϦzÃü6EET¯q2”“ü®u g8«sÝ{ô›ê°Í2¯Ó¤RÇŽÀs÷á4 Oa–ï‘Ë:‚ªä×üCQåӇғé•ì ´ÿx}Æ¡¼!ĵ–°àì Üt€ó·–`wéøá¼U5)¾I¿äS¼ÿý{gñ"Îæ¸äU5âmKÕ½fÐLJžÐ‡Š_ÝN§ÿ±žì{ãw Ú,@t1é‚~T½åúËZ*}o'Q›Õé{=µ–>ÛðÚ²ïÕØ$2ÂTñ`B#M?GVcöXwÈãHì¹>þ}Ñû lþÁ×ÓúõãY•º"¾¨‡Â]ÂL§”5òòj¦ðô&p4ŒTR^%0†ß+0W)4$¿–pÈwàM>ݶï½´p%mÈ?LãG  áêÝJ[jÍà¾=iÚ„Q-ÝŽ™ë‚Où½Çûs=|â‘W•Æ)–œ”k¢Ÿ_–H™6½±‹PKÖhg‘›^Ùæ iƒÌtÛH+½žï Ç/±ÑÒ#nzzUM¥ÇfÖ”‹úSúµyTºp/å|{2UmNO>û,ü©Äo Ïüßµ×:"ßJUC$ÀŒ9HˆPfw8¨’ƒBº™¥L 0“Þ3ùè3{à—³–'X}‹ª ¡q³äײªkh¥„öLÙNž)[^ÿ^tÎÐ~B“­Òõ—Ž£½³…P¼ïØi\±‹z²ÙÝ5žÃ¡Ò-4‡5V ÎÐ'k¶‹õ’°¾×ðþ9üÙæê¬\²a— *µZ3o¼„R“l´ûÈ Ô²‡œé2 ï=Þ|â•W•à§ýjjU9túåúº6ÏB3†˜éX™Ok3ita?3Ì6ÒÂ}NZtÈDɱ›Õ`bíR.%äeQù¢CT¹æ“-”<1—ª6ŸÄ›2(ãÆáTüúN:ù—uÔóñb+øÅ²Øm\7¢¬ríqJ¹¨yª]tæ_[Y`-§Öú¤I¿®8ÒÐhØ”‹(¯ï ²»*iõ¶¿ÐÙŠCtó%ϰ­öšûÖdš1éWÔ¿×zwåC ìfg:ÇÊ ’éÛ#ÝqF\öðäé3å´y÷12±ðqë´óY¸*¥C'Šéøé³Ô;;>X±•jXðBºüü4fH.­Ý~€MójÈÅ ¸SѼ")ÃF=îö[‰kíýçä€ßáj{ëìì|í'ûþòT²³f…j¶×+Zë“Æý,‘ì§@<¼ºß‚¥Oæ?}6ŸjåTë(¥ãE>­Zÿœ)ôéúó,]?µŽ†õ»Š*¡P—®[cšo9V NuI§ˆ N^]Z_Çjßµ€Ù“4}‚38¢iuÆ &P›÷åIŽ,ºbÒ(:z²XÌä·†&L ¦ò´-ß‘Öʈæ=*c,ÍŸ€{G~ÿ£I{¬Ôȯ8ŽvÊëד²y}®~½2…€´~ÇA? ëó‰þLOI¤"öQnãÅÖ?Ûå¡c§°RŠ/3¤/íb-Óêmûå%?/ì=v’6ìôÇÆæõçrzEMp1xÿñˆW^ q(íïǘ>üà$±4Á[2½çöòAµ»Ø§aªpx© ÂwXèò£×4Y½vJ°X‹õ¿0 .¶qÒ¸ýg¬ÀBûˆ mÔç8Z¯ýÐY2²Êœ“L®SUm<ÙðvuQ/6 Lm ¼ÞŽÛ>íH?5Æ£¥>‘ùÚÓ¯2oã}¤ú©1^OÃ ŠÆt„r~øÄJ:øLzé¼a_¥ß ¥˜Ïx<©üÞÆ0ßzë'M3lk@|NØ¥*O~o4ƒ¦§`­"¥éônžmæ'ZUûëiÉ*§G:͘<šze¦²6¹š¶ï;N›v¦¾=3›5ꑞBW±iTï¬4*gS®e_ìñ Vi…†J8·PÀŒ:h‚€ä×Μxªž*8’ß•Û|Që4Eɉ t5k‡ PÁÔ-…Íìjíõ‚R`#°f%1œ.©SÞ?U\* OŒG5ñûïø4y5n‚C¤&ZɤÛy M°9XæS“öMõAÆ‘Ÿ©GbSøõÚÔøô™ÈAI–ðÊ´hE'{ˆKnèζfá({2Yû¦’·šM:Šªå¥víA_ +ÂÝþvU™:ÚO›ÐVŸ´§_—‰óhõððxyÁÒtÓÚüPJbõçåëw=K˶<Õ¡ÒAèŒi¾Õôqõ4D\ãÄæ€~›êÕO›Ö¡Žºr²Ömñò`)úbS=PÒÊÉš¤jvRßÃ~¯|´šö±Òôɣĺ=ð‚ fó² Ô¦]‡Å²w^9‰­fö#ÙA[Y‹UÌd‚ Tþ¡BZ±y>€†±J4|Y|kãø„¨ÎøG³½‘¨+_;CÀßÇ¡º?]»ƒö;%ÌØd'D9=Òhî‹éŸï.g¾kÙÿ[NPd¤$ÉÇì£åÏÔ Ò€¼ÿøÄ+¯6ù€Ó]1ƒ= 'CL~f¹N‡Ô¬ý%*©ñÒWÇZ©'›íÝ;.`šî$èc®ÌÉHZ¦ÀÙ÷Pë’í³éºÙ_[õ›{&‰€Ö”~Í0ªÞÊ&cAÚiƒ>ÐÎö·Ew¬ÜD?…£OšÃ'ýäǃƒÁÕÖoŽŒ¯Mqm¢­û_åHVxö¬cq @‚ÖÅ*ßþþå—“xà;¸0w^vºÏË8dÛñ Ð8ùòédP§v@«YüƒQþ:c6¿3ô0º}úDúÎÝ3„I¬4Â:=9‚YiE ‡>-„£}²xýžZ!4¹ëL ŠË*y¶?ƒ UZ¼a7›6Ô=W?¹'M ¶ì=F'yV&PQMüãáIâÕº»Qe?Œ%b%¥&Ûx­.;‰øCœ›Í¼˜å'í,ó&Ì÷R“ÈÆB=|£F êÍŒ²…¯Ô°vDçóáà+¾qÊ«úG¸Ç‚,ƒ/l=Ùæ+÷rqô0Kß KñeÿËz;ý„#æÝ8ÜBŸpÑ©ª¦¦z!ðèƒ?RVª¾>ÚA¨)°ý‰fííí¦Zö²fÇiʸe$õþáEä.©¡“]4Ù{9—•ý»ÂÕþ  è¤"ÕOáè“æ ‰t?âa³øg%%…ϺìLénºì¼G)¯ÿ v6±CîYލ÷ ¦|>z͵¹µk JâXå[{UÕLÝD™¶ûŽ;îè˜#fk`ÔÝãáŸ_ãd°™”Æ©˜Årÿ`ª§NH-™@ í׋.:72S“©´Ü'™8us QÈN—6w›:Û ÈJœ›%P]l7À1–Ò»ŽÒ —žK?üê5Ìe´~çA’%DØ7¬?}ëÎU¯ˆ^ÿl=¯ÿ”O7OOw_=E4cÇÖb…6ñ $ŸÆΑhkã2»µà$_;Œ<Õ7ƒ‡NSš»˜ÊM=cÑæù›»ôÎnY9‚^K§e÷¦ûŸA@ˆŽ…]¬ÈšÛ¸‚fÐ.Ûᯬò9Ùþáý2i‹«ˆ ™ìܲö‚€2(„|&x».Ižú÷ž ùöGdà¨{ÞªàÇe ë(OÂäeÙÂÒ~?a1~Ž~BþàÓ&-m©Ošë×&·p!ÒýÔ}{ЖC^^xs%$mªÖ/¿¾è†¼~WÒ„÷Ó3o_À¶è…d1'Ñ-—ͧqyw‡$8®ÚÚ¡”¦÷ÖOh8tíj¡rç2ù{°4œE7WÖïçÍëi÷èryûŠßw_}™æP×m ¨e›v7É5cÊhö%)¦—?XM‰6 }ç®+›ä‘j9„¬BŽhÖ8u¶ TczÔy×Bà™‹[$øøéz†Íô,<ݘϪkô¯…«„‰©ƒ#Õ!Áÿîß|ÍlÆ:T’×óâÂu¼±hCƒsuyº½©ÖA€£¶Q²ÉÊL›W»ƒ ¼ŽK(ÉÓšd פèÉ«Ù!L÷L1ü%ÝYË¡qûm¬ÍZ6’ÃIF°çCš@è²òZál¿ì§XßGºŸB铿0‹V?5Æ#Á¬ÑÙÒØÌ!D'½F1LÂÇ)-¹¯«q ;Š£"޾l= Ëbdó‹0¾·ÁÒÑv~'_âµh?‘Ç‘Ú;¼F¿¶‰çJÛ) £Pn,Ï(›øw=9!A˜@ÁRã‚1õlh›@rØf›î]8n(e¦% 3¨^YR¾˜AVËØÙ”¨euÌ MÀØY°oL?øU Mycí¸1ݱF_8é‰àð9œd†V–œ¹†b2™8J‰•ƳÿM’§‚FÖl ­Ð€§Šª=Tå ¹ÞȚ͔䭠a=8ú_‚™,‹ ´ËvTÝ®Cù\`ûÏ”Igy ÌEcB7ÿk®rw¹ƒÜeöænµëè)aº†f[ÃÖþvU™"ÕOí“æ ‰F?5‡Çy{ð,]O*)¹µ9²‚º¶ÿøg´>ÿYºxÜ÷èÎ+^áèz_§e›Ÿ¢»_ªd= +¯‡-fùö·Ï?ß—MÑG‹ÆišÝÖ£Ç2qÉš;OÏ_åß$Áè¢{ Šê·ØiÖY»ãM9g}ïž«Äo%• &Pû ¨/ÏK¸èý•›)7;ƒfß6•("=¹y|YF´÷õ8×OÐF›†®^ŸÄ°«·#–é‡Ò@âŒ}<¥nmª‡ŽÄjܘ•²°“Õj¡~ÙiT\^CTTH£«uÚ8ž¼ZóöÐm1ÂoV¶•¥ÍûÐ4Ahêá:A}S Ô+=‰}±,‚VÐ ÚцPSKíßOl >V£é;u2‡Aö;ù‡Õ!‘ ãûÙ7퉨Nx(ýjŸ4×üh÷S‹xñø¿øNÊÊz›Í|& ÍÑÛÖµuùó[¨ š&MÕ5£cžoÝ.W½¶‰ôßqGm¨ínÿsžú¯û°h¥qj?p*g3´fµjë>žŠA\ãÞÍ™@í=rа!ܳ“góÁ I™@5¼º¤P4A Ö'ˆ­ºG÷VÛ]Â.t‰¡®©ÒäBH‚•µ¶Ö'Õõ<è*=Aã«*i¿mlH>OMÐ ò|š`žMSN¢NýxÑXÓA+h†¶m5µÖþýä¤Ržl»ŒÍÆÛëó*Í=_˜çAÓ©öËzíN—îñzà9,x¬n/o»*ŸÆS?ÉŽho”Žæ•Ë{Pfú!û<ÉzBÙç©„Íó\®ì°óm˜ùT4O÷ø7‘q3½ºJý' ”Æ)FSÏ´)ü´ôL §Z^ÿF%…€B@!,±$8ùu}^—§¨¬œÕAìÇlƒç‡Ù„³Ù,„’¤¤DrØ44'•,†2:TZEçV­¦RS/m¯ÄÜ‹ÜZxü(Ó‚s¬Ó„㽜”á>MXªO¢›5M¼ GüIfú@#(Ð,Mõš+«=×ÚjÿrÑ[9\;¯Ã6œ£í ,Ö)!ô‰ü6IÂú?e¨laf9¦_¢'bí—•UTë.§ó¬<¯Ûûy®ÑõÖNýÏD“O㥟$ðmñíáÒl:Ut?Ïàwf¿/{y‚!tSQYoK{¬Ó„ãUÕãxÑ¡dÒ<áÛ0ò©hÊ‚ Œ{‹Š¯í2‘)*‚¿$¬qªKºŠ¨'¡P{…€B@! èÚÄ’àäGÒåpœ¨¬uh5,à$²_RG’ofàH&¬qâ²’“’ÈÁ³ONÖ8õá•“L•TPæ ƒ“Ci×p¨Gd\d%‡´ä0ÅáJFÝMV¯‡ÐyÀú.Ýä¤l›¥AWJJ2%±½5h­ ÙY/tù±½í/Ì2° ƒ€¯D6§FÉ,îÐëmŒ›Ó¤S••×ãáEx!y˜ø¦ÉñöƒðøÉY‹_ê >íÎýØ3íåÛ³ŽAt†xÀ¦{5d4•ó>|3É^ÝBw¥HdS ž„a)3Bïm$øt_ÑÙ‹WÖíŠhzG4ç¬_ŽBÒɯq²Õ¼_óBÚ‡1ŽÄùïæ¾”ãÐkE¨2€ËøàƒE‘¨G•©P(ºEÞ';Žœ(îJd+Z!k‚“Ô.ÿðü¼qç•lÌ?”É‚S‡ÕþÙkF°j3„&9¨ò ÎØ”Ï„HvvJu¹Éív ¡*œ+¤ƒÔ%ƒf~¬ ,$Ùš&Méiiâ¢êuTÛ$û:ÞÛÏ|¤ë^oÙ+—ìeL¤à$Çצ`öŠO™gÃͧ; Þø6Ü|:þüÄ3nïržÀ ^nŒq$Î]ºƒ#xøgwÉcµWÜž4aÔ@zoYp‘moóîüÖZÓõ—",¾E×]<Ž>Z½Ý?áŠ:GÎ¥)´ü‹=‘&A•ß œ›M7_>žþðÊ'„ˆm¥}zÐ ŽÙZó¶ÊP÷Ã@, NZÏ™…¯ïÔ´‡ö;Eyýs:Ür) YÙôFOò™¡ ‚L⬌¡–µN‡ƒ5RnÖ8ù4R^¯/o¨áñÁEB”0Ð`äÍ!4¡>Ÿ¶‰}šØ<š&aªç×6u,(DcÀâµýàŸ ´Â#‡°j¾TØy­1Tm>«ø”×NoE*Å ßF€OéŒË{Ʀˆ¾Ñh÷çÌZ©~ ,—ëô Nüõ ~q¬ÀÂÔq·CÀÌ¿¹i!„OKf¿_¶Â茄ßÿ}ü["­T$ V3%'&ÈSµW´ŠÀž#'鯶Oh2òïê—®˜H/-\I%eU­–«nFÎù µÜFįƒÛåï¾õÆ—üö¯}².ãÑ™7,¼zrGaZ ÐøÎ}aÊ­ìSdgÁÉ^'8AëäñxŇ2T¡IÒ+ë‚ æw>m“/ÂÌò`ª—˜hLôp?ƒÑxk?BÍ2ÿxu§|åÂw?àþpó&…§ŽaW|A>•ïÜww¾ŸjúLi¦gÐé%‰gä÷õ‚¯4®§È³5Ìâµ’v,¤‘ƒûÐîÃ'iå(ý‰¢7ÒW¯»ˆ¡N  ;QþáBºïÆKèùw–‹Ùõ.=Í™ÎÐŽ Ú–7 M8’ΔVÒgëvRÏŒT›×—R“m—5P‹uÁ9Cù·52S’ø7ÕÈùwð"וtûô‰tüt)Ma7€W>^C£åÒȽ©´²†VlÙÃ4Ùhܰ~ôÖâMbœðÍ[/§×?]O×^8–öóºP©l%rå”1"œyYe5O²ú&WG êCçH:Ÿ#Dú±S%MêRàÝw'P\9e4Í{s)Üè¡Û§Ñ¢ ùtÑØaÌW ´›«e¬½„€~ç•“yRßD÷\}ó˜—æ.XwxÅjƒcIp’3ùغËÊŠ«v}±áÏÚÄ)¿zyá*ý[/ ‹Éž„ a†…'ŸöÇB‰6›šDà§“ÍõXãTç%'iÞ×ÞάS6ù…4Ï´™x†fxXS !Ç¡uB L¾Eo}3øRSÕÞºÚ“O phs<´|5÷Öµ+ÿYUUŽPäˆ …'Éoí.0|Nñ)ODD‚OÁÆqwçÛHðéSóæ äyŸËëÌôÜfÍöJc\#u®“Æ'ß`R3”à) »@¹YiÉt¢¸Œ6ì:L³oJÛöTãúÜ7–Pûù~ùª)´‰MðN—TÐ`0+?¬/ú|}SÖq3S¿ôþJ1<•ÕvrúÓ?YÛÄoÂRNVýçãµ4yô`š8j0}¸jÿîš©/› >ûö2ÊLM¢¡}{ŠYý¼~½è²ñ#é­%›‚[’Í*3‡ÃMeU5v2T9˜è9.áã?(hœ‚“ž˜– ê¿*Ð8¡>h „%˜øBû| ‚ª ÈÌñÐ~Ìàc0Ê?ÆÚá=;ßÜ´|ñ ‡3›ž:ªqR|$ïu${wäÛHò©ÛKðGKL6ñ'èãGæÌ<Õüƒ{¶^ãd2››Ž~ƒ+LåŽü¿câ÷ ¦çücíö Çm‘Ÿ$ÿ®ž.-gÇø ž t Qeu-aÚ¤DŸ@²……ªI,àÀ<þ0;Ï'Ù,tÿM—Šâÿùîr±/dM~‹ ‹ÎRïé,8b«Ü/4]sÑ8‚ÉöÇEþ“,´aöþØé34×Oê¶ýÇ™' ê3€ x êâ‰Òìáºþ’s…ôîÃ'h$kze¦rYÇüÏá ·g&mÞsXЭRÓ‘Ó#•lü[~ÇŒI"/~ëe’uÉó–öð¿6ósX·”W]o‰¡™àËiÏÑS´!ÿ ql^”ÛShZOŸ­×0ñKšJàid×þÄ-¯Æ¢à„™| n±(Kí'ÿ{õí+oÿ2VºëdI©~×US áòyÂËß#!ȰŸ“0Ïã¬Ohò6Íœ˜v1#Fƒà$6þåA½¾sŸÀz¢• 4vÇöÃWæyÐ4ܵý%ï¼ñcŠélà)ðx¬£‚“âS1Ú©»ðm$ùtîÜÉ¥Tü.4ã‹Ñê§ß¾ðBg­C„?ç:Ë~tÿý'¢U·ª'24÷»då~…sGm§œ¬T*`';=•N—Sz G¶åIÉÆéàñ"ºjÊ9,àô¥M¬¡Â`qî‚E ²õä²ö=I=Y“t¸ðŒ¸XÖÇ«·ùó_:~8õâ|HÐ :)> ²ð=ÐUù6Z|ZJggó¤`†@\Óö?öà7ßÿñC³Â×­”ä´»ÇÔßÖ”¶©Œnudc38î³ù†6&üÎÚ®eM˜¼Apj)Á§iüˆt° ùö)¬zàæË¨²ÆÎþF§„ÓRY¸Ž©È{®½Phƒ>Z³µIÖ]¬YÊ&ÝqUÕ8h9›E!(*þW¬Ù‚¶,0mgmÕ ¬™ê×+‹*Xk†DH[±yðGqóøálE}°¢i}åcߣ{)ÉkC²@*»Þq*ó p¾mñigµ&z]%I>ÅûÏ)ÖÞR9 •' x±®eóÊeö|±éÀ”«®½Òãq_ÎäT@é)6«žž–¤%˜ya¤0¥ÆBRãó`«Á@/05>¼ ÇÛÛø–ÎÆ|Úø¼½åE“O,X`Ù[Tü=I…xšén:½/3„yo ïhY¯§§0ãÛ™ÅÿQZ^@IDATå–™d¦C%µTË3äðj-Á¯I¾GNÓ‘V‰k˜¤üÍ‹xêK˜ÀÜΓÌ+¯cÿʇkÄ)h÷Ë Ì+Ÿ>Û$T¸,yPÎ{Ë6‹ÉSi]"Ÿ…¿R`’ts` øYÒ|[öúeøèÀºËj| ™ÊÉHòcÜ8:ôÏ€œ Z»ëX»ø4¸Ò#Ÿûly•àÏìçT]ë¼äûÈ×Þ| ’OñþËoöñ–bMpþøÝ…Ÿh `¦ñÖPSSEKÞYð/Î;çÜAò†´%§dXm¶T“ÉÜú×›R©{!àv»ŽšÚÊÚªÊÒ£û÷ìÝ¿sŒÑ!Ì@³A©¼nƒ^O·äØŽCNŠOC†.¾Œ&Ÿî=Sr/£ÛLJ°VÐÃlx%šh³7¨?9Ï÷+Á)šàG°.ÿ ‰u8°˜Hµ±.ÿ*c@×–à„g %I…/ÊíÓ'‘7¼ÂAZK‘<¶E_sô´D‡šš{¦¥kÀÑb2PVª®P[ʯ®·Ž€äמéÉlþhjŸ¶^bôïÖ²/à»Ð Žàˆ úϧM´ŸÑ¦ |Š÷ï¿0›S^EÁ)PëKMR¤•ƒU×þ[÷òvˆï¡ ȇMæãC•âð xÂÏ| ­„$hš 8Á€Ç¦ TuÔL‹Iñ©DBíÛB *|Ê ÞšÏ¸= 2$6MùìY³ÚùnEð@D=%8Eçè-¡˜XFÐD‡Å¾g¢‘ +}3áVvÔ\šÿöÒ6…&<‡à ¬Ü¦oø|‡'ÁOª+$˜"PE^Ï$?¶ÀZâÞÚK4JÜ$¿Žì›I[ ¡£%>ýù)›LR‹)ÏßX´AŠ=Bî#ª¾éíõ•kP@O$Ÿæ¦øÞùˆG^EÁ ]-ÂÐHa¼ƒA/¿¸nã Z&¿FŠ!Ö–€~£Fƒ{¦ _k‰30W)4ùuÔ€lÚ}¼„q®a>Mk–OC«%:OaR¡³“äS3³$Þ{¼ÿ0«W^UÁ |‚A©œ-ÅDžcpŒi)N ¼ApB; ™RZ'!N’ä |U`~^_@p‚ Á©¦nÏà>x(ÜIò%Ê•4¡.ŧáFºk–'y"¢|úäüù½u·÷g"vÿ|’µMàÿ¨¥'æ¾ØO×í©¾ µ³Ñ µfÆ]Erƒ$V¬9hæ¥4L”›¤Ó‘JžÊ*©G†/Â\ÜÔ7 ŒG÷ÂzŽÀÔ"0Ö÷—JÜ$¿ÚxÌófÒÚýg˜O«˜O“ã—Ž4ZòéÀ]¼÷XN'žy5–'9àÀ 4ðR  4‰À¼oNpjfþ‹sªÔÕ/ IžR©‰”Âöà\—~MòY¾¶$éP|6H»EA’×$D”OYhú=k{êF®ÚŽ‹FŽøG´QÔ4û8¸‹¤‘Ò6E»"XŸX¶ƒó0aÍC,ØnåÜ3y-&§ƒÎð×A29mŒ|ševò{nbþô½÷ñΫ±.8ÉîÅÀƒì1 ÀÀ8P`’B“4 V‚ÔxIò…žäÀTžËû¾Ü‘ÿ/ëS|y¬»B çS^p6Åiw¾àû<2$½ú“9³Wt8ºî=OÖ«é´E«}×GæOÌ›y¡xLII‰äàõó .ÊöT³Í¼ŠÝV:Í Í&°V*Éf!/@«ëçD³W°þ B9#*A˜øé—èaMS"ci£dƘ[`,Mõ¢Icwª«%~š“JC.u+>m¦Ã›ãÓ³ƒÒmƺ÷^ñ*`ë*‚h•³¶rP m%éרiR‚ë¾IH±—øBnòZg €º!¸)>í ôc«Nð’äGì%b/¯#OHÉew¼À¥äÕ=\a%ÛC*( 餱Ʃ®ÉfÃÖ0©Šˆ|QYxPŸÀ>#ÉII,49y½"—ˆöå ç]K6‡J]ªtêTÂ÷qË !°‡1ç}¼%//¾êñ輫W¬Ódd)ÓÄ‚¦ÍÀ‚R‚À1%%™’’“Ä1°ƈZÌU Öøµkú’L•TPæ ³lfªø””šáÓtƒ“2Ì^6Í3Qb"¦$R¼êãÇ®$8É7H60ð@ ˜ä¹¸¡þukêFh¢’'b©Á’&ŧ±Ô+ѧ%b|úÄÜyßb¿¦Ûe“x¨õÎ ÈÍ—ËîŠÆò Å•Ò¯Ÿòq’ÓMö0Bpø7@;³'Çã7î3²“‰MÑ2=¼8.Û†ÔzäÖ5®°hm| Bhä‰áOÀkP'›u²™5aŠÍ¢ii)”šÊ„'ÆØce¦×ñ—¦-~!õkíTiwSOÃ;ù—Ú>­«Úï¯ÙqRb¾ ôn94¡6£‡l,@ëi±ðTœâÕ&ý×§ÆÔÆ×Õ¹B –P|K½ÑÅiybÞ¼Iº‡þ(›ÁÂÊߟóàyí½ËîÇBœó€c×ÿ]{-³¨ÔðÏâóà&=šäàÒ7HeS>ì±ÖÉår“Åí¦ö.‡6Šy£!Ñþ¦3`#ñf³ÏOæy˜½‡Ð”ž–&Ž1@E¤2¥mj?¾­ål/¿Z,vJe~u3¿‚Wã•_ëyÕn\ñjËÜÕ§–[§î( n†ÀÓóçŸãr{?d³8LfCó.ÛdøAg6S×ôóäT-KOÊL¯3;#‚uKÉʬ§s(r$hVÄì4›˜!Ò^-Ïâ ß'Œz¼>”á¹8Å‹…A(¢»3£þa¬­c||3øìÓÄæyÐ4 ó'¿¶I…À…éŸâ×¶T¼Ú6Fs(Á©1"ê\! PÄ(Oÿã¹Ñn—{1“×CÈ Ýòª wÌšu|>;-iº~®Ô)ð± Ñi=ùŠ1…€„A—ï¼.L9ûèØYp²#hDÝ,¾ÇŸ³øh`~ç›Á÷E$„YLõà;‚ &z¸,U /Š_ÛÆSñjÛæP‚S êX! PÄ(¿ûüH¤»„5;Ùu$–iF횟̾ïxg“ÌB“?¢[e)Á©³;$‚õËAú★'c}§D›MM"p„“¹•ñ½u~PRÛÔÝ­öê”M~¡ÒÀ¾"&üE°ö•E„‡Ö 0ùõiš€§JáE@ñkËxJv“)^m«À;Jp DC+ŠÀ‚ Œ¥¥¥ÖŒŒ ÷wÜeTbx­¦^׿ù0½2“fºò±ÙßÜÔÙÍŸ?ß\äòŽü¬›LšZé³;%Âõc %CfcFÇ£ Áš&'ˆ€Ïü  qÒñÇ“ž"L^§|ÄÆ±« q>Ð:a&™–ÌB å‹ §4M‘í2ů-ã«xµelZº£§–Q× °!°téRÓúüýÃ=÷8Òµ<ÖPärá}Ùë÷4(‰<®²í-*~;EEÅÄQã0ÒBLc;çw°9X‡8És²§8ôõ)ìŦÍÚÁGx ¤ÅXØhïÌ‚€Ûª]{žæn¿/éà6–Éxåcs¾¹Q^ëÌ}±[c¡ÉçoÅûÃΚUÞ™ô¨º£‡üxÁ—G,< ‰ì}B“/€„/\y<ú8Ÿ„'#ãœÄ1 T¿èõX|×$ñVüZÏx‘|Ø(^­G¦å#%8µŒº£P„ˆ0Hc¿ð©ºWŸÎÒÏ«ówf‰Æ*V1’Qüe7/ëðì4¾èVž¬Èʹzò¿a¾Ü\ªïßà™mŽáöÄ3ÏÚýyûYvÚÂÕ£ìâ,;{ {gÍšÕ©>@ ?Øôä3Ï]•¿çYnàòYþ;ª™ ·>6ë››åµÎß{xý¦º¤+ÿ& E¼ì1ð‚ €LÒ QA0,PÓO'ô?°‘ð‘Á"äµxá‘Xj§â׆½<$Ob¯xµ!FÏ”àÔu®P„„‡ÈÎÕ<ú—uM»ÕíöNäA’1Ø‚ø£ÍËE²v‰ÈÌÏ›ƒz^×8ÿ9¬¹:b n"ñz]¿žûÌ>Âlg¡j›W7l3°9Ùã³f ªü(ef¡³¿Ëíù5“ý®Òï-ÎØ¼Ç hÎ|lÖ¬Ò(‘Ò®j¸¿üþMŒ»Š¨×.Ôº_&9ð  )P`’çâFœü2Ilä¹Úw>²O¿ú„&Ù#y®öMP‚SSLÔ…€B ¼ôÒK 'jìw±°òUÝ£_΃h˜š}šÇGùÆv¾½S3ޱlS¨™´B6]9¥»ÝÕƒ32jýšxà¥ýë_ÿ²žv:­F¯×êÒ´]Ï1ð¦“7ÊáerIÓ‡ð§×ë‹4רö:Œ5^úh®ûn^'¼¬zâÏœáç¶òóì“£mÕ ´­3µSOÍ›wž×£Ït»<ßä&-šÂ?d.‚ýˆ×iús£¦ÅÄi à¤Œ*0DLôJç¡`ߊ‚ö# øµýXÅ{N%8Å;¨ö+B@àOÿügfÝùPAýÛ,°ôl¦/ûEÁÂÑ"2j‹ÌÃÁú¾ð$0{݆*Šxƒù]³ æn¯w8Ó3’ý¨Fðœ7ûÞh¬}¢Â?Ä…³o•>ƒiœÁ{âe©Èãu²¹ßn¢¶ó¥]Û©õ]Ξ}´ŽžÆÅtèüwÏ>›çôè·q]_u{ôQ cas¡I7>öè·¾™ßø^,œWöE'i1YMJp’`¨½B@! Pt;”àÔíºT5H!9žzæ™ ·®=ÎBÓl<’µK,X°'8-áý«Í»ð±‡*%MK®Ì6ðlþ4wî‚är*­<ãxœÏcd,úSü™ü"ÈÁ8ÝKB@ƒˆ*á?5÷™ƒœm? cÙåû8Kb…d¤BþˆsàŠò 3g΄ × ‰Àûöõq»µ¾šîéËe»€Láý‡ÛÛ¬–Œé[Ãnº?úÉœY«c'O=÷Üp&)U¥Ñ©Ýÿ‰#Q‘£P( °! §°A© Rt_0ø_½kÏlŽîö ÖÎdA$“ÏÏð6»ûO,ú Í™sGÓº¾ndCSòô¼yƒtÝp®W÷Žã`çÖ SE†Æÿ|þS¬Á"Dcc¿:ÜB®¹ ªk ÐÅeW³ðhäf®Ì«òwû}½ÄS<¹—uiT¥éÚ[ì‹õÊãsf/–—cy¯»õI~út­Àê¿® …€B@!ÐMP‚S7éHÕ …@¤xâÏ^ºj×îgYVX [yûý…#G.˜:u*‹]'Õ™Ýbб½-)æ~n.¡écX›v·yùjÁQ>æß³Ð„€é¼×ËFþŒu,X•°k•´×³MÚ{ý¯¦qžX>glj’>ôòXí …€B@!ÐP‚SwìUÕ&…@@à‡ÂêÚ'Y¿ò] ꣻÁTMÓùñœÙ~# ÕÅDuæ~,È6‚@¥{óÌx¹¡§Xhby‰½TH;MFºññTfW-`¦.ûp:çóQ]ˆz-ÿá;î¨UØ( …€B »# §îÞê} fxbî¼ßz½ú#lžç»«Ñn£®}ìÁ4“]]R4@ÀàÑ'zë®pD=%h7@G( …@wE@ NݵgU»- ðë¹óþ¢ëúwämø.MJ´Ýú½™3Ëä5µW´†€WÓ&±¦RdáÿÊ¿©5°Ô=…€B@! è6(Á©Ût¥jˆB uXXÒ~3ïÙ¹¬izPædó¼w†egÝo ÚÊö«}ˆ†05¥q Fõ˜B@! Pt-”àÔµúËOíƒ?}ê|Ç}5iZ]Óû°“JŠÿ¦:ˆ)xF¾RÓµLÔQ£Áøé¼_?öEgÈ ÛþžµõB“F¯_4jÄW¦NêŽ=ŠO#…løËm/Ÿ>9~o¯ËÓ_P i5“‡Ï?5ªÄ®ŽÀѓŔ°Î–WSYU 9œ®®Þ¤ è‡ã¨Åb¦ôäDÊJK¢ÑCúRÿÞYA•¡2Gc'K(ÿP!•”WQYe 9ëøÔ§O|ý±PƒµŽO3ŸæÒ€Þþ»±@^ÌÑ §˜ë’ Ò¾÷½?%T$T}Ûd2~×íu÷Ö šž’˜àMOMÔÌæ:GíŸW7: »Ëå-«¨Ñ+kìî·'gýôÉ“÷Ï}-}ÿþ‹_Ì´Gƒ,šæ…m¿ï¯K£—‡e÷¸…&ÿZxŸ†Ǩ—Ò^>ÕÝú¥’8ôõ‘¼e=jû°F›\.7-Ù¸›–ņòjœÑ)Ñã ›³’,gì7"Ì::lI¡£•Þ[¾…Ò’¬4mòš6a$™Íjøf¸Û]œÛí|ºxÃ*¯b>ÕtJÐjɪ•“‰í.§»dt“•êid×mô~ŸN8š¦Môñ)/OÒ]š–v¨77,0F´p¬áë?üÉUµ µÏ¼ZŸQƒûèG¦s†æj‰ VcDkW…‡!ÔÖØ´ã@!mÌ?”³ó@ÁïNyN|÷þGqÿ OÿâS®$b\âÖ4ýÁßÞþÉCÎä¡ôï÷ßêÀâÓ€#¶‹OyŒq‰ŸYuZ#´+2: L^¯Wh—^ûh •VÛ©_å1v/õ¯Í%ƒÁÀ¦ ±œ:Èh~ã}þì»F£ùéÞÙiúWN¦¼þ9Š{# |$Šg!—&ŒMÛìýïÓõ½ ‹Ê>øÆã¿zäù'ö®3œ‚ŒhÂSsŸ›èÑÝ/ð@×Ç3-O4ðËašŸF‚a:©Ì¶øÔK'ÊN"SU@`bí9-Þ/4*™öºáØ2ê] Ëd•$óJ÷ˆídRZÝo*Í]°˜n6®˜4J Je^µ‹X`}kñFJ5Ñʼn ©‡éhä*ë‚%CxìgÞ&¶b÷ÚQ{Í{s ÝtÙy̧£ÉÈ¡w!@Å{RÄ&` M’ùëüäaš~7aä í‘{¯7²Ð›+ª‚Býø£¯_oœ0r ¿ƒÚøñ/¿Ç ÏÃ&ÿæßÿÎrëž7Yh²‚8ž,Ún6oú¿k¯ ×°âSÛSc>}ð§Oü˜ùh šÌ·«‡Y[Û›¯šÖÐ2y<aš÷ùº<;½…• ›ö¾¡„¦0“—!TÞ¼oÀë­Å›èóõ;–ÀT¥È l1Îo.ÚH}Œùt‰m¾šÚ€Bå%‰ó^x¿ñžÃï}¼óªœÚ`žN¸Á(úÅ|÷·¾Ùlý Mú·^¦YÌÊ*¯ú#bU¢?ѯè_M3üîÞüøêº¾ï°ðÄ6ƒ·²æ5¶Nü<Ø-Õ-æ›5«eg÷_2oIþübÖ¬Y5±N¿¢/¼`ÐMQÛ÷£…+·Ñà²4ýÈÇdòÆWˆP‘NÀ ¸½»t3íØ\`ïÒPñlí9` ó<©¹¦|š˜ø:™4ŧ­a&ï'àÜðžã}Ç{÷?žyU N’Cbg/„¦1c&§¥¦§ÏëÛ+CÿÚ ËJìP©( èßÜžzBBâóÓ§ßžÌwø½|rÞüïê¤Ï‘¬`ÁŒîùé7¾q8lD× ÷ŠOÈhŒ>ÍJ{úÿp*ÿ& Fí¥ÐTU]Ko~¶`žwù±Eq„@øš Ü2jÏÒÿ>^KµµN1 _éª$ €(yÿaŸ&˜çg{[À-ÅPDo~¾ðÞKá)„¢ºÅ# u b§èøYÇN»lŽW§öi2*MSìtP$(AÿÞuÕd£W×{çŽöí:ùÝ|jî?‡ò/ð’V6yâñ‡úXž‡a¯ø4 vµ"À§YiÉ~¾ô¸k¸ þó®ÖEoð@hBD2‡ÓɎ㻩œû²O“Ò4%žn_Je5Z²i—À«€%¢<–rä¼s, •¦)DX¡yk]Hå5¾÷ï?¾ñÊ«êG/DFŠÀcÐ*ÁÏ›-)%eö˜¡}uåÓ¤c°Hô3úÛj³}«ŽBòwb-€ÆžaÍÔ´-ñ«06YñiÁìJE¹ø‡²”×9©KúöM›6ñqH|* Qû®ƒLsÂɃ¦ÚZ;­ÛqPDÏS :Ö‡ÀQWnÞ+°ÆñlÕ14럖üº”#Ã!zž QM(GÀ8®ÛyP¼ÿøÄ+¯*Á)ŠÌ3è ¡mš~Ë“ù8›CŽ+½È`“¥¢¿ùcßëæ{gMªã… ßÏßÌ›—q)ç}³Q›æuvŸÆ$÷Dž¨‚¢³þÓåѼ¦äá\+¾YAóiä©U5„Ÿ¶É-´M‡ŽŸ¦*‡›†rÈq•:Žp¬díÝáÂ3<“ïó!éx©ñ]øõãYQí¤¾)N¥Ž#«ìnÂûïÓ:Å'¯âGO¥ÎG p?!³g¯+à—‚uš:Ÿ4EA´àþþHÉéiðMÂl>§…ÝF»Â-ýí£¬‡<Áù}I£ß?:{v81ŸJlãpìd‰¿ÕN·K•Oý…¨ƒ.ƒ€œ½wº\dçõèö=%·Å:M‘ÆÌ}Ÿ,=sùËÈ‹îž=Ce_¬ ‚—þ@º;4§kï~d0'Pí±ýÑ±Þ Þ}ø èÓƒÃ>Õº9è ɯù‡ ø7Uë4u ¸½ìÞÕdKíÇÏëä¨:M§/¢Ý+~NzˆATÓ2o$PeÉžiêȃXï xâýïß;‹q6Ç%¯ª™ÂŽpQøž•R3i3˜LýSlV멨? ¿S¼šÁ8€[ ^Ê ªòðчxgÓüŒÙjyÇaLŠOÃfW+ê@Ái?ɼšGÈ|ê/Dt|Q„w 3˜lÚÜöN]ܶdÉ{´çÇ÷Rчÿ¥ì·QúÄËCƳ÷íߤôÉÓB~¾£b'àYR^%0V!Ÿ;†¨ä×’²*JÐj;uqÛ‚]¯ÓÚ7Ò‘­ÿ¤þçÜC½_rã†NúõrMÈÏwôA¬ód¥a² s]|â‘W•àÔQN Ïóèéß”`4›z¦§%aÚ)ÉŦŽDjr8Ý¡>¶ç@ÿž#'[,oñ†]t€Õͱ–ÒS šQëÍtÁ× <Ñ®wtÁ‚š~äo¦ýêG÷ß_é?ÏAXù”é¥êÚŽ-)Å&cáiYJùb÷Ú´+|3ô˜õ«u8›¥|סBZÁ>áJ•IétI…( &Ú,!ñi¸èQåD˜=a€„ev‡ƒ*kì”誊.jsU”RÍ|:½ðªÚ³…2/¿^äÐ, ÔïGiì󟉭ïÌ’fÂç”(mâe4ä‘?QÚø‹hôßߥ¾÷~Ÿzßöe^tõ¼înóÌBJ;ÿ‘7Úÿ€g9¿gÀXÇ«ã}8p—üZVUCV-\+p„F™³¶„ÊOo£Ã[ž£Ò(wä—üõ~ ]ò•e4cÖnšxók”˜6ÐwO3Ðe__C©=Ljë—Ï\GC&þõv# <÷~q¯ç +üåDó ÁP.Þ|â•W•©^49®åº0 E_àën5j†ä³9ê‚[oðq§ŠË`A=ÒSè–içÓù#ÒI¾öËùïÒw¾|%ÔÇß’Vo§Ï×î¤?|ï.ZÏNƒH ‹J)=%‰ÎÞÑâüyåÁãsߢ’²JVùòYrÍëK=P¬J-óttŸè½ðîrú˾LfSS6Ç¢ƒ—œ7œ†öëÕѪÂú<úgó“¹Pðo—à´¯¨äN?.øÏ6懕0_aaáÓ²ŠzíÓu´‹ûBº-ÁBŽJ7O/ú |6´_OºçÚ ýM(:[A?›÷6͹c:ÑÈ¥ÿ|´–¶ñÚ'U5µÔ';ƒ®»ø\?R(ÛÄ3¬ÜJ¬Øê^$XÍÌ÷ÈÓ¨î7ææ÷ÊKF jP/Là~óâBqÍ`ÐXëh£QƒûÐW¯»ˆÍušÿ À)÷ïÿûœî½ábšØ¨<„wyס“téx¸!u<((ò2 '‹Nž) ‰Oý…¨ƒ.…€—C¼ºë' ëbñ4/´G»aŠl}‡PÅŽõ¢ê>w?D™_E§Þ|Aœç|éòºtâÕ¿‘Ñb£”±“É6h8]õ •­_"Ìû²¯¹“Ê7¯¢’¥ É^¾É`°žNžtÄ`Xs•BC@ò+&qMԱɹÐ(hú”Áh¡äÌáT||¥¸™ž3žÆ^ùW*ؽ€JŽ.§Aã¤óoü7­zuöédKéKç_ÿ/*=¹‰ö®z’ªÊÒ€±3©èèb*ÜõU—vŽY©‰|ï?¾ñÊ«MG”Mû[]‰,aƒvAO,´4?ZŠ §ÏÒß^ûœ8@=póe<€5òŒõzþíe”p×t=¤/õî‘N[÷k 8mÝs”Æ ë'>ô 8Æÿ´ #iÖmÓèøé2¶0èC3.—G—ð n?k}Þ^¼‘†ì-´p5qüˆ4æûÍ Máª#Rå°™PÀ’?Úúý?I NsyaÒÐU†² †{IG‡ø&}í3æ/=tÇ4ê••&4¯}¼Žg²]ôÕë/|°œyïË×\(„k±™ùÌfµbËžc"²ÏC·OcÁ;Qð¯ƒÕ€ÚKÎFc˜g‘~÷ïiú¤Ñ,X lQiðp'}üÅdËDÉ#Ï%Íl¥Óï¿"@ìqÙõ,}F§?xUœ[sRËo‚.¬ tbÞßéìêOÄ}üƒo”³è$UíÞì¿Ö:TÆXš?wöwî RºlüŠãÎN=^AÉy”Ñg"M td‹oN³ÏˆÛÈU[J;ý@è²—Ñ„›ÿCé½'°°´Q\+;µ™¶~<Û߯×IöŠÖ\ù& ü7¢|€÷ßxåÕèüêF¹S»`u€`@Z7(Õ£þ¥üdÍb31úú³Àrˆ¾4}jÏÐÇ«wÁ š§•[öÑÝWO÷a}ìT ÝpéyB#…/¯ž™)b™Zø—’”@Ù Ûö}ÇiÝöbÀü_^°o¯LÖHU‰ºæÜyõÏɤ·m¢Í{ MØÄQéÖ+&ð:"{hÃÎCôØ}7øØ~õÜ·'—›M >[O¿zðV¡É‚YÓ?^à ‡ÚX³#’¼µ\ÿG«¶ó}»œßsÍlŽdågšÒ3„Ëlý/ùÁ×­Tøô³ÏŽs¹½…§Öf±<ßJöŽÜê0ŸnaÁÚË|í¿¶oóLûL¼³ä ÁKà3hŒ²OÔn徇€áȉ3”ÍÚÐQƒsE[ M¸˜–’(6kü—‘–$x çX3ºpÅ6úõC·â”jØÙýÉ.¤¯\w!`þ'¬ï¼f•VTÓù#ÒM¬ ƒà¶yuù{„–ô3Ö´&ðµË'Œðkv A[À‹‚nazM<ù0mâ(ºrÊQO[ÿzf¦2¯g Þ]¼!Ÿ³–‚ê\³mkHGÐëÌÓÐÐ~iúD¡¾}Æ$¡Ù_ÿëýÍЀÞ=¨gFŠ¿:'¯ôþþòͳ†g ™f¼0p°É]›ôzŠ1—ihÿž"t27ñj7ŸÊçÕ¾ë! MŸ|Â›ÇÆÀ€<ì<[DGžù%Ul[OºÓ.LòŒ)idòP{dõ˜~‹ß\è—oY“ Teª×±î‘ü ‚øÔ^u’v,þ>[I^öeCJHîM%»ý -/Þ)Žm©¹~Á©øèRÿý˜:à÷ë8Oã‘WÛ”ÅTguOb0ø›„`’)ÚM…y݈½ýB“¬ä Þ„0ÄHЖ³Íð!ñ‰„˜Ô è-Ìóæ¿½”òˆûíù“„gJÉjA,âСvÔn!Dùî’q”É÷–m¦ læ„èUŒ¡µ¼~ÈÂå[„€Á l¤S%å"Ú ¿ˆþÁ*o¤W?\CV–¾ÌQ1› "¯Låqƒÿ¡Ì/v¥G¾~-ÝË=YSìåovçÏãGÒ[IÇÿù4•o\.„&«»ÙŒ¨¤ˆ¬½øq¶æ omµ¸'/zy¶<~¯|kãˆ÷Kà‹tÆ2MüÊv:©EIo×òŸPÑ¡ÏýBˆª);BÉé¾o9ΓÒ|ÇΚbœŠä©²äy¬ì*¾ñÊ«JpŠ N ”vJŸT°SjrbÓ(~I¶1(ÄLºÐÂ<)P k‚Çf^'Uÿß"úë?´–àݾ¿€æ½±˜ùëáç2cÊhV;¯òï]KWLEI¬õY»ý ÑôÉ£Ån kˆàÛmƒ`ofç{øV c00aàÁ+´Txš…À´ã@e¦&Ìû2YëÆ ÑúËD¾Æô>Áã@žhµþ±õ NÒÞm5sÇnÒŸVT1Ÿ1O5NòU¤ ,$A;…„½Ðq¡ ¾vûØÌógϾæž›Â>À8wøÁŸ7L„]ÝÍÂR`ºéòó„O+üx@èAÚyà¸ÐõÊJ¡aý{ f†íIŸ®ÙNÿc߯¥wÑ9CrYk”ê Bü ožz¾_sæ¿É{Žœ¾N—³©ìucŒêµ¢û˜¶¾üžÀ¬qä Ÿ–Ni½Íôëåã@žhtKvüƒQØc6籚ʷ¬¦ô)S)eôÞÎÇe›–·Jn-û5%æ!ƒ-‰·ÄVóFô&à áIâѺºqá¿ØåRbíÓ2²&çp°‡ûDPˆAãg Ó=˜çµ”ªJPZ¯óÈdIf=ßVKy#yøŠï@œòjHƒŸHvH—Ý©t6mª¨nêD ç{8ïË r@‹([ÐôH-ú æQÐÂWãû@À?ª¥„Ì\ß{ýÅôGöÕ€y‘LôI 4Rˆ¼hŽÕMù*y€{“yV^L±ŸÄ„¸À$5dýù9$„ýÎbS/™0›_Îú§^üPlk¶ 3¬•’)y- {É­VõÔ¼yùv.2ñŽ4ʬ7ÜoõÉoJº¡Ü¾òÀ#0‡lœ¥ )-Ù&öôp–5#ÇOm  ‹›üJ~5ûö]EŸ­ÛIŸ¯Ï—·‚ÞCðiœù‘CÄû5;24MHÉI¾É²ò€rß±S¬í9è祊*»?|¶¥ýÿÝ}%Y¹›~óíÛÙD¶ˆ^ûdmƒ¬è›K°7?Ê&`â*LþdÉ&  &†o±?!&€_{é=p̧ÑEy5…|Iòƒ¬Ní»)þÁh M€þø‹¿çh{»iè㣼ŸÏ'{Á*xù/­öJñ§oR"Œ8÷å”1ez«y#y}‰s$뉇²c,§âc+hÏÊ_ÐàñsèÒ¯­ô¶~ú¹U-’}|ÇË”šÍȳvQï!×µ˜/7$ŸÆ:ΑÀ‰BU™E@iPÚ‘Z1#½›g­1ˆ„‰L;’8p ZøŸ,ÿb/%X|Îú2¯Ü#jX9áàÞ¸<™g\^?1{.Ï÷3,Á|ÉÄ Â÷êt€i"¬!:îM5˜}c6ÑÖ`!ÀÅÌ›†’íÍf_HÅì7ÕŸ5X  š™à§’Á§'æÜ&/5ØÒÓàF œ¸uíÌQŠ¤Ñ¢9sîhù«vzƒçÓ¾=3…À‹Pñ0©”i'›w"‚\6ÓC‚6fš›w¦ƒ,DÌøÒ4™Õ¿‡p}Ë´ BÈ@šâ¿Õê‚–àcm˜ JSÔÀ‡Ú ºÐÜ}ø‚'/?„ЖÌ1´Ÿƒs³yb¢> žGÀ–æpƒüe‚Ð)ÓøýéãUÛ„VjHß6ÙË“ yk‹^”‰¾HÀ»_¯†A8dj¯ˆ&;çÜØbu:GÐ;ôǺäüNÂ÷I&„ !¯—mXJe—‘1)•R8–yZÛçõïÍ‚Ñ*®ýŽI€p¥ó8ÿúüƒâ}€Fþríõq‚ßü‰ B¨õþZضè1°Ð¼"´9Þ“Ýk˜å3FÕc*ª€ù`RÀÂÚmÑ»ëp½‰âàÜ,ܶú“i«­ê~tèª3Êðw šÚD? ±&4uUìÛÄ:‚º"f „¦6±ÑcRhꊸ· u ”Æ©`¢|ŽÀ-ÊÕ“ð_úî=W‹HtOñš2XBð™Í3ý˜0 ýg°Íô8ª}Â>Ð afA%àSŽt¯ñôâ{+ĺ5ˆà?Û§×— 'úWx –ÒMì4ÿú§hã®CìË4òXØ’i,k¿pÿ}8ñ‡TÇl<Q4c–$‰ìÞ¤]ü éÚ˜="±>b{d kÀ£!’¿Ã&i¯}º–ž{{‰È€è…ÐÒÜÈ~C áÃÁgS¸ð³¬ù„#="P L./›G×\T/Ü–ÑÜ1¢>b}£w—~Ao~¾‘#ßækÍå ö¢Ý½òájú ûøá‡f®x‡ð.µ•^|w…ÈÁ4Áǯ½ øA@ÂzPh0Ã:YHÐFÁOï/ÿùTÐáçª Î¡/;ODçk^”)ÓÈ>ÿ(yÎlÈ£!󃿤LÅpä´ô'Ñ„Ã2l¼çwKïG Šþ܇ó2Œ#À0„§@j-®+#Ð%NÖÓÆœ@QÕáï0@ ÃÃË”ÁXȧŠ#hèSÓ!jNQá/nþì-NŸ½¡z…šü qËjª[p¹Aˆ€1f$¦¯Bɱù¬÷zËi0_¥ »µ}^mƒQ\È•ÑklÙCƒ˜,ÂeuO>"Áàõ€ È•9Ò\&6Ó“Hðz ¤|÷A˜öü§1}( FówÎH×Nyö=Ÿ:4‰©`omŸ»¯sÞîÒ'ÂÄ'ÿf¿½f¾ºÒú{Pê„ûé@‹æë}ŒÀÄ¥ÀŠïïòø®q£–‚Zëú)öø:™‘®]vÛFˆI]†È4°ZœóæÉóƒ¹1ázœlw'\~O¬ºû8̾æe Å©0qê^œ›ð#îáèZý¨bS•××í‚6 ˜úú¢¢§KÊq>3çˆ9MÆ;"Þ9)±/îÍ÷`ÒÜŒûå³ õ>’cäÌ…XŽ ÿòk8÷ê)RÊkH -%ùpê¿n¼?<ÑóV@ìò«‡ì~\°  TiùÐÛ§7éoÍâÓ.…R™€ìí íhËð4¿ÅT…G^€-¯ÌCŸ~âÓ–CZÆw<½œóµ!À¦z, ÊuÐ ÑŸ‚¢vu—hRÚÑ®yØæTê®R~r #³»ˆšR1qò¢]ìØ™r 7éŸm…5—Ìs ys½¿^s$û¬«jãGº¶yƒ4pºŠiÿνþ$ß|hã’ fßf(}çïà@çøGÿ*´M÷-pò¾5 ÐêaÔ?‡è—Ãb†Š/Þ…òÏÞ )Ê*xö—úÝŸ€nDýýq±MõúÜûPµu=”}üŒ¾ÿqˆœ¶-÷ìP{xœó/.󽸕×AòMw‚&&š s ˳5Ô8þ‚:2ê3÷Añ?ÿÐe¢ÜêÝ_-25åeAø”ÙPùõGò¯…RhŸrv> ãæýôá)P–÷9œÞû4þnXaÞõomÓ‚ß»ÃÛÿµ”j=L¹ä Hw5NKÑ E™¯"YyQ<}Æ¥OAýÅ“`ˆJCÂrœÙ÷,ŒžõCqnÙw6ÁùSk¡àðó0mõ³?rÊ© *În‚ÜÝ¿w™ï¥Nþ&Œ÷茉¢¬£_Ü-&Ä¥ãã<ZCT퀬mt™(·²d'@‰³!ÌÍ•ÐÚ\j}T ¶Ì°Ö™‰Ó°Â\7/¾P%&ध¢ÈatJOÛ¯]*ö»{Ú,œ¤ó•O¶ÃŸq[—ù™(ÿ×ûOÂ%³'1qê<4ÏSÈiç ×f79ùPo˜-6xï«}b^¨U 3 -9çój/v‡ùÓÆÂÆ½'Ä„±cR\Åì8’ ™¹%8o’Eä§É—ÿõÙNX½pŒLŠ“óþ{ã~øÁšåPx¾Î_¬Å9ĬW\p¾´ËæMaÿ]úÉFeMá{L‰üšfáäМ¡@@›iw?†„æ°›Z`Ô†¦ÓÇ¡fÏ×P³k9pþí¿‚ù¢Ól4áò› jö2(zþqPGÇÃè{‡Úý[ õb)PYãþ 4ædB%’!SY‰8—´æv8û·ÿKuú™¡%?*7~ ÆþüÐ|æ$’ªÏÀ8q’ªÿä§öíM K%H“&6A/}ë¯@dhÔ„Õ7AÙ'ÿêUx$„¥O‚Ò½_÷˜‡O†ˆ‘0íÒ§!ÿÐ_Á†ftSWþjËÀ…ÓëpùbRBîÞ§ ¥Þ9YxÚ´ÛÐ_é28¾é§  K„i«žòü/¡¹®Hš „¥òüªJ÷>"ÆÌùO8¾ñgÐÚTv›ÊO@ɉ·À©Ñz êʎ¹ì÷!:y.L¿ü9qß²½ë!,jŒ Gºð$qüôî§ ¶âd¬üŒBbVpø]€&sÀ¨Ä ˆ8€p>{m—<| wØT¯w|ø¬üð¦ðÐ÷¯ÁÇÓaïñ<8xª°ÇRæL úy÷¤©Ç‹ø„@À,‰bX¼CàЩ³029ã¢à©W׋IdM¨ %rôï ûqâ×dˆîhÞ†ùmø£3?c Ì›:FÜ8çGªorÚ§·š­p$»óØÄÜIk¿Þu Ͱ'jþz_džiöó®ÊCvÕᜳ®²'§'C„‘ý4\€„ð‘èöE(yÀŒfJMµ¶BÅúw rÓÇМŸ ‘³‹À ÍgsEÑt¬1ûˆØŽœ³Lh‰Lç‹ 1ë0˜ÎBÌ¢U®*é*|î!$^ÁRU­ŠÑìÉ!®o-?'ò•¯ s3Á\Q -Eg röq]㪿Zk„}Þ,Êk¬Ì…æZ졜ÒõD®({j.B2v*³!>}¥8ž2ùf°¶Ö£Y߃HÆ6¸4Y‰H‚L¥P–ÿ˜J¡¢h $¹ÝS\Üö/˘yåó:囈â]$qÝO÷¹mEŒS¼ ‰3­C)±Æ)”ZÛGÏš)FáÓS`ó,(n?yïÍbÎ\tJçË=bÂÎ¥ØÉÔk4øMqêUÌ+|¶ýÍ)†æV3„éðƒˆ/ìCß» Z,çÍ-#ù—ÍŸ W,ê{®A24·Q(ÈÎÌY¶ÃÁƒ ^¢<ç5Z6k¢¸z÷±3s¶ µFÑ‚‘&Iš‘žÌ?$ƒáž{\ªÓ÷"-9^h˜úºµF­†o®FgwüÁ9–SgPÎçNIïë2Ÿž§g;ž×NèæNãÓûóÍüî:E:ìà×i:&xSûæ‚S®Ë¬õ5=TP¨Ô1u.„Oš ±K¯p]£Ã¨y2ÕÝ#7»]“ iŒÂ'ÍBbsÍÁÚ茩£OAÖä÷´­„ˆ‹„éÞä§ßl/³Sžö©·ÿŒ¦Aî£?èwd½fÄ3R«r/NtL;à>p—W£^ eö›¢ÕWdºîkn©•ºû€ ¥â0ÈCLò˜P¥á2¹#SA'YkûÝo+5nÔr¡ÝZrëz×}d¿Éu mƒ4]ù‡ž‡È„©0wÍë=²¶>Ü9[û&G$Ĩº%wÜ{¼8HN0q ’†ô§Ç òSQ]ùç.Ü/˜îüh˜-È.,…’²jX€DjÖ¤Ñp±¦ªê)€«x„·>ß®¿zFMUY•ó‡NÂýÃ8Šÿð×¢ùÓExñƒ­ðKÔlEE„!Aˆyç…iTŽú¿¹~7Ìž” 1‘þÍ ÖE–þòó‰k›Zï@IDATÍ B…鉀·%’½Æ“Ü…Ñh¶'ÓQôýih6AjB´‹8És×õhêçžb"\æªZ2÷óþ°M4[¤Dï ™,rbºCÀ QAúr´ªu Ã°ÆÞ&»©ý]ë­ ‡ÍŠ_; Tl\ d6×!¡%»¹÷z$^÷3N>p½ðk¢(x2YÑ—)Íë:'[K#j¯Êàäý7t>Õe?éúïAü¥7@îw -U— ½ [O£–»d½ÀÔïS‘8ÈÕ a`vèA«ðLÖº»‰ÍÒñ{Þ]:æ°[1Ì·ŠN¾d6×]²[z¯G:úáHxÒû/¿´µÄáÖâ>xÞ¿}…­®F"ôÿÞøÞýj/üèæ•®;Oë!RWj‘(¥%ÅŠu˜^qÑb›þM›*Ìø¶ÎÜ蜧cLн?ß|4 $™Ê«êåfP®83‰›Æ‰‰“—­< #:¾ŠÎWTõ—Ì2GšÐÞýV\:o2¼ôá6®›´ «NÅÐæ‡PûyJø>©Ðœ)P½{ô¾RÒi58àÁÑô¥í|QOW' ;÷4hiЀV逼ØI0¡&gЫ@Ñì*6¬…‘wþƒFü 2ï\%"ð¾ç70á7ÿDŠ|’ žAGû §S~_•¸ˆå¥?ðÌzc'4åŸòw¢ ”ªw}‰dg:¤Üv?¤~ï§@fƒäëTwh”¾û<¤|ë^ùýŸ 2W¶îM Bq¬Ãí®ú–0yç/;?rËÜû=íŽ:Ä‹4 Âíµ§ü|¼w¤¼&¢oªA«„s–™0JÓnn×ûÕžŸµ˜j¡èÄ0eùÿ`ЈßÁæ—gBή'aÚeÏÀüßDŠ|’Ž|ñ#ô_:ëQÁÅÇ߀â œk©®ýö޾ Æ6âUšû D%ωK~“–=æ–jôuú *ЧêÌÞ?ž˜|É“ê Ž¾„~R;Ü3Iؤ¥¿mX<úOc‰Ï ïàŸ;äém‡pÔ(íâý§ï@¨Ê*§Þ¤„Ï šf,F"#BãžÈa³»4µD”*k!Í ÃPžv¨olW‘Ï™œ_îÊD”R72úÁ 1ê¡Â^¬Z¥‚•s' mVweã1$Mí¶) …>Ÿq¨ŸéŽ–»nAÁTøc@‰LÕ^|ôb»§ׯœW-)e)‰ Åjµ ?»Û®^,.]ŠþS´Èt{'Mª<>œëC§ \·§HzêÞQW&Þd'” h0@‰ßZZ'ÕPbOƒ ÆÑTÚ'dbç2³Ãw­3©ÈúgÊ ¹—Jßþsà‘Ó¼)ÿÿ%æfR……ƒµ¡O9‡Ž:—EQÀ ZdjÌ>*Lîhb\{K',§ä•§¡äµgA%ˆ“,»ì£Wu„iB³r4ÅꜲº­ó!÷ ¿’ˆ4˜gtaKXKÜ=.ˆ3 $nR^'Œ‡£v¨´Ž†xuQŸ(‘‰ËÌ ÈWKëpÍ‘õwvا¹—Nïy ÅåÔê”Ó#Ÿß%æfÒè"ÜÔ`~§œRèðΉNÐ"SMé “;š×jn”‡ÛÖœëé7³ã ТÁÜ\Çeçú;ú.ýIQhÑù:] Y[~Yð+¡q²š:ŸîuŸð+·Ž‡Ôçû/¿¡(«3$Úk“òIB€ü’Š.Ti†(zXÚˆŽ&y=Õ5%!£xéaÓþ,1ú½ Gî)„³LYèœßdj…K1ð™ãQ#%ù3íÏʇܳ„ŠÌ®úÒÈku*s«Ä™‰ÓR’¦þ£E[ïΚ¥@#ô¾æ•´nÌa3½þˆ@Hä¥Ñe• L*AªÕì$%µNŸfר•`Å <‡"Ù[[\¤I–ï°Z:yÜÓuÒä~!šñYë°3ÚM:Þir¿¼¿Û„ÛnÄO‡Ú»±‰‘[‰3aÎÉ;ÜåuêèôÉQÀqÓ°:†FNÉJ’&Ycò#"$6ò¸§ë®¤©ýJ‡Ã†¤‰¦p’¦ö3臄Ç)¤yo©¿¤‰p#ü´¨m¢÷žÞú UYeSoÒÅç¼BàÕOvˆëRÑé²ùS`Õ‚©—sŽâÿ{à a^s&§‹PÐòbŠÖgjµÂŸßÞ Fwèãx%†<¿~ÅlŒX6Þü|7üù✣éÜóÍË‚:8þ¬¶Ê©[ñwž‰“^÷ GO»òOEÓ×4åÄHä>u’¨Ã¤ÕjqAÒ„ RÂpÖÛÒVÃê³_ÊKxí!„[µ>2ô ×¦Z1a-q÷°(ÎÖ†€ÄMÊ«Xg¥ÇÃÞ3è{Úr3Ìû7cÕO·z{¤£ç½÷Î÷?te•‰S?ˆ³÷Œ@Úˆ¸^Í›ægŒEß± è|ŒÂB/™15M6ôµhOÒ<=óú—pûuK`þÔ±‚m?’Ÿl= k–ÏÆÑz¸÷–U¼¯C• züáép« ÛAgb4ÕkqR ÂdA‡?P_¶é úÊ´lv»I¡<ÆkF€Âè“94u˜tE‡|N1a-ЄÑì ¢Çæô«aeñ&PãH;§Þ M‘&Âmd„’¢m˜:I)aM˜sòÎò:*! *ëšáLE@ó­0Ûð¨,§}¡Kš&"Mç­ ³âûŽQ4ñ½w¾ÿ¡+«í=Ó¾ä󌀠ž;i¢ÛR¤2:ÞØÜ 5 MbRÑã>ydbl‚DfSD¢B")0ìŽKSïàw9$}p²³¶)Þ-ËàÞ‰Kó!ôUp-V›µÅd3xÝ—¦ODœôØqÒãÀ”—–=$†Yðûl$ #‹--Ùî‘Ï“ñð«[‘O™7Ö ¦)9Ì£b K”°%ŒI[2¦z8ˆ'á&‡.—<øîur †œÊgëN^'¦Dãt 8[“ÓJÄà Ýz|žd™¡¶&Ÿ&2Ï#MSœÆŒï¹åÓùÞ¶¬ú¹œvizîlu„ø#éë®—þãù°5Mzñ$?§+ù$·½4†Ò¡´ÙÀi¬‡¿@ÝGÜèåz>Ú°¶)(ÛŸ:£’ÙÔZ[[×Dǽ&NdþDy NHN„Éh ƒVô7mmmEë $`ß\&¨‚X7á0ª¡ÆWçBZCá€æyêð ¼Có4QÈqŠžG 4`ƒQaVÔ4aèr£ÂO”°%Œ¥©Þ@¹¶¾Éa1›ÉÑÆ=u‘÷“>ÚîR‡ÁSY÷žäu|r$úéÔ"yJ€]ÍwA’:Fb´½$uî€æy’÷ ô5ÍÓD!Ç)z‚Ð(l¬i…hƒªí½Yõc9í¶I™8u ôG¦b8rZ8¹py+Š.?D®\¼ÁtƒÀΣ¹®£Sq^4Ö6¹àª SsÓņ–VE3šâÁ›äìˆ*A‹z=–n4"i2MjNQ$í%  J3ÔX”p!"Uœ¤ 8ág˜µ´¶Ð32«4Ð¬Ž€‘@sG$L±j3$”H”ôLjˆp0†Å6aK“åa>DíMínni.H9¾ºv0äTÖµ7yMAy5ªà\m+T™Ç‚ ÀŸO4ƒ^Y‡mÔ{`y`Z[ÑÒßd“ÛÒs‘ýä¡}ù W_Uw0« ‰“×j ×(>vîíØ«$Ò$;—ÎN*šò©Ñ`ç\²Xp úD o*+šãf8Òùíóã¤~÷AŸW0#ld'^£qú‰yÞiŠŽŠÛD¦(ªÞ`h›èA±½»½öðÎ-¤Zv— ŸãÐà Ýë4hr*ï婼jµ&ˆDyµ¢¼’¬‡¼æ}UVF.yĵíËvYu†÷•¬€œvi&N] ỵ*v>7\ûn|Žžjñ‡¨s?êøƒìI ŽhW8r€º`^~¾ÁA «à<—Ñ N-k›W?)Ž3Júh±çå~V~“"(NHKöºª’ éZpßXÒ¬ˆÑi41£ˆ[-8Š/|Ÿ°3Š Ú:£Î¼¾&Pu;O$ޝuB)Q@#ÂL¥$ÿ0ÔÖ!>Nmú4¡yiš„ù“KÛ48A!¨OæSœ?[°«!åÀ]6Dý†ñŸ{]dýlƒ%§ò¹E^±;ãJ¾”SºépÊjÈ©«]Ü7ºö:ÝÏò6#І}€6ì=ç.Â’™ãaæÄ4qfëÁS‚‘ír K¡©¥n»zqÌÞùr/Ì™’;äB\T¸˜Ó)*"LLTKZ! N¢v0«C‰_Š?°œ¿©NáD·³pB[º®àüE8„çe¹çÊ«aãÞ“ðUó »ÐizBä©ôb |½/  ”âJV‚:ã$¼NæÃØ‘I°xÆXaçßW½åõþ¼FÒärøR:ü¹®\7ÿ@Àb±Á–ƒÙ®Ê,˜:bñ½äTÐxŠìŒ’ ¯u÷ë6¥›pù»_í‹xøŽ5Jš¸ÙÛD¿D(ÉQj h ”£Ž ‰“‰‚F´âÛlÃ3ŠOõ«EÓ8™b¢£ä¦OÖÒ ‘ùsß‘:ÇdªG¾#´‰'lšÌøŽc;Û6[ÝÎuŸ¬ÇòH¤<ȱ¶Þf0®R9• y 7ÈÚøZNéÎÃ!«$§íÓ¶ÅÄ© $| ;Š/TMŽI„æõ»ák”0}ÂH8[Z Ÿï:3'Œ‚¹SÓ»\z<¯òÏUÀµËfÁ¡ìx~ífaòWYÛŸ¢IÝHœ$w>jŽ4øÃòâ[C’"1›[ДˆHÚu8GÓḭ̂bîd  uwgæaœ5ÕêG²‹àök–¢+§ þðÊzAÊã"áã-‡aü¨$Qê$:•7_6ÉÛi(¯ª$¬¯zwy?<€Ž×)Îq\¬œÎûa¹J~†À®c¹@f®”(l?ÏÛäg 4ðêÈ‘|ê(S‡™<ÝÍÍÍ-‡wn{[yéå÷¼±n—ã‡7¯hgý¼§ìdQG_l£v…¶‰8… ‚4‰Àf³3pD›”Ô6ùÒj¯VÛNDâãúù¤ÞeoS6¹:£¨éCâDþ"4÷•V„'­‚ ÂäœôÖ©i"<š¨}K/Ö±½;ÿ¯±±ŽL¸I$y’ò1ÐÛ ôzY!“SYÁ@×(4º—ÉWrJ÷“â&1ò¥¬ˆœÊfé°fâÔÞé ~ðoX9W¼h§‹Êàj˜ˆ8QJŽ‹Â‰i—Ší¢ •P%¾Õs&ǖ⤶s¦Œ†Ñ#báÑç?s1Ñ šÔö¾o­Â ´š­p2ÿ<Š~T#¢Q+¤…W>Ù·^¹fà}ÐRæ *„ï_·L”+ÿQ}hÞ§›.›+Á}–G)3·5]hšTFì;Qß¾j‘8ç^oq Àþá/Oº¬2ÆÁaâ$Áàu·T×7Á~ÔÞÊtÙü)ÝšºÊó¼Xh$Ÿ>€Ôan•˱=;NÄ$$’âzøß[³Lá­æ‰:Z2d6èÓ¶Ô:‘¦ÉŒ"Èg„ü Hãä ?dL’bÁHz¤q"|HëD!ˆdY¢ÁBgèq§Ô@+G#øÔ=”]¨(Ì9ùÁ¡í›c™²ý%yò7ÓÊ©ÄÔßå5&BÖÀ—rJwõµ¬ œ¶7NÛ§.ðî r#G' : N 'cÏ€ Dòš¼’ a†Gû’8Ég"_”(ü$¥ÄØHAšh»®±EŒNa¢n@[y…»µ Ô{_íƒIé#„ÍxƸ1®Èˆÿš[ ÂmÒÛh4,«ª§kš!:Ò9œ × æKŽx‘ Ôôþûï«r/VMw‡Ð«Û½ýõ¡¸ÞCŠ™¸Êh£’â`Ú¸‘Cz?.|Ø&PÔY¦5©i1lýôƒÍH_H­qí…ªÇ\¹H9PŸ'êx‘/ h²'úÄ:I“3€„”;_§(M;qŠ‹ÁÇöM"L(©¤M"NbÁ}Âɹï$L2ï@jF¾"džGš¦üSÇ?ÞòñÚ/°<Ùî$$ ¤Ýñ7âDur9Å{ˆälÿ“×H7÷;_Ê)"åϲ rê·ÿLœÜÀàÍž¨mh‚¼’rÔ.E£oѸ|QF·™W-˜*LæÜOž@£cq´»b‘Äa¢È=%ÆF FÈ{OäÃå 3Ð'©çlJš¤iãRÑ<Ï›dÁâéã\/º¼~2ª××í‚’²jœ‹&N”ºü6æ¡ù ù?]2{‚]¯kl?òòÚ@]çUWOB(>·øÃ\òë;HÏÂõzrÎ^@Á q#ú¡¼r1rnNÁŠ™AQ‡”&N¢Î3™kéq¡Iœ4Û>ýpkUÙ…‹óW®þæÿ{ó«ÈiãG:ægŒULŸêÕýæ+OìßsòL求EW\³Ôfµ,ÃŽw~g#:ʨÐkÜ¢*àÞ¤Î$©ó¾7eö÷š•*g§®{ñÃíý½|@ùe‡TÒy_ïïÚd±8jëš4¹-bªÀök,ÌÉÞ±oãÛ[ZšÈÔ‚Ú»¡mMÛî'$NÃ*§ˆHå³ó¾Ì7ë©n=q_Ë)=OgÙì¼ïÍ3‘œvy|·æêrŽ0òQ¢…’ M0(rLwܰ\nö¸žDëök—ày}sfs/S^865~{ßÍÂdF.Ýù/I&:ž />úW–k—ÍÄQôi Æ‘O÷Dü¾{ÍP‚FAeørOêí^Ž¿mÛŽ5®±C…b‡¿Õëã?¬ßuL˜½R"zX1g’ÿTŽk2TIù]4i4¸ÐÇQ~¼í&S³mÛglÙölŸ1sÔ艓&†…GFë jµ†ˆVÀ§Ë0F>ÄÑ“¹…r;×V«¥µµ¹¥¡¥±¡¦èLN™ÅøH³DD‰¦¦ …Hµ=ÉÉ‚?™éauDb9E¦¥±œ" þ,§NimûÏÄ©¼Ó¯¼îçû3‚Ñ™4¹—ÓÛvgÒäžWœëÈ©ÜOÔök¯½¦?ßÔr¹:ÄÀQ4W'F  è(.¢ZÊkVÌqùÊc¼JܵND–èë'Õ/²³Jjy2ÒäeeæãBÄ‚òÊ|¸Øéç×&>-Ÿà½çŸ{QnøšÚ–ÚнI«D$‰4MDšjÛ¶‰L©ò73=¬’H,§ÃíO+XNý[NÛÄÕ¹bâÔÞl.7’0D8§ÁCàBsëuøkãŒx¡€ì‡î¹çÌà•Î% UµÖ?Ûõ8‹¦‡ÑÉq®}ÞzdÇšFr%rï¨ÒqšAFø>áZj¤¤VJ^ƒ§‚"±äDmGI’&éÇFí(µM¤q"M(×6aEb9•H8×,§ñð»=&N~×$ÁU!vBüöÄà¾?–¥b̦Ëm^3rÌÿtûa¢JÇhðbå\6Ñ“ø„К:¥N‡Og`¹OmÒRqZ'\S@’§`#MøhPCÿ<y¢6$ÒDæwÔ¶ÔŽDœ¨ÃMÄ©¹m¡6¦ó”ßß“”Kª§|Fª;Ë©¿·\÷õ“mlr*ž–‰S÷ÎG¿Dà_|q¦Åj_N•CóG‹ô/ùeE¹RÊÀÖC9’Ÿ,v°'Œ~7 ‰EJârÈ uBÝ·©CJl"M¤q’#:§`"Py”ÚŽ’lCÙ!%3š£è¹ ½ ê”RG[vNi[ŽæSÐwÒ$}¡ˆ0iÂÇ@ÿVRûÉ…Ú"GD’¨=åš¶é\ hš°š®Ärê„‚åÔ%þ¹ÁÄÉ?ÛeXkEa8ÝCqºokÅ|ps÷ ´í¾ïƒÛ÷z‹ß¾ðÂJ°9nlËdW*TÏôzAŸd9u6°»œž«¨†ÏweºZž¦˜Ÿ1ƵÏ!u¤%y’nižG„I’&©š 6âTà­OmGI¶£$O’@É}yÞ™;ðþËúÓšå4ðÚ/è唉Sà å ×Xv@‹J+!«à+Ô`†ƒ4œ–ÓÀn¿ ¨½´eЇá‡è½'Sž¯÷Ÿ„>Ø -¨q¢PÅdÖc4èpÄšI¡H8„ ác2[áÕÏvæY`2™Àb±  Ï¡L·?ð@‚B©y ïCþ8,¬Øòëûîyn(ïée³œzÖ å”HRM£ N¡É­/åÔ³Úr.F€`F °`S`·_¿jOQÞ´? >ÞzM{t‹Z&2ïáÔ3:­㢠'ýb÷IA˜.›?t-jŸÔCfº7}útý„ÉSÞG“à4ªr¹4Ñûš -[ë Ÿœa9õf’Ó¤øhŸË©wµå«F€`ÀC€5N×f^טHÓ‰3%ðɶ£‚4ÅÇ0iòL"—„‘Íûs 3·L­­BóD¸v2Šï¸ëyÔ0Ím+ÛêÕ<&z,§ÞK“¯åÔûšò•Œ#À0Œ@à!ÀÄ)ðÚÌ«SgÔd²À»öƒV£š&¯ ñ‹HC§Q«à³Ç¡®¾ZÍf°ZmB“7XÐðÇ¿yüZ­ö*Y&Z`Ýÿ›ûÿs£ÜÖ5Ëéà´¬/ätpjÊ¥0Œ#À0ƒ§Ài+¯kêô±Ã–C§ ADG„±yž—hÒˆ~Ld4bЈ]GÏ@K‹ #šÑ|㜠’¿Sʈø4cxĵ²Š<ûè}÷ýSîëšåtðZÖr:xµå’F€`À@€‰S`´Ó€jI£øV«v9-¢ç‘/'ï ü( á¡Óç ©©¹Mëd°ÖÉŠA;,¨½ 3DËÚÙ¬Ö—¿ÿ¾‡å~0¯YN·u‡JN·–\#À0Œ#80q œ¶òª¦r¿ðüE  2­WåðE [Z-@¸šL­`¶X¤ujĶyó‹=`s´ûK™šZ^~â§þ¢ãƒsåthÚu°åthjÉ¥2Œ#À0§Àh'¯kéìÚ »°TLnKó4q8„#EnÏGâDæzAœl^™ë•WÕÃkëvÂ…‹µ®ŠÕVW¿úû_ýü × ßf9’žSǤ K ¦œËðMF€`?B€Ã‘ûQc EUÈü‰æmªªkÄÉ1U{RŸÐª0Ìá7W͇Ìïïi rêïÏÆõcF€`|‰kœ|‰¶ïEæOÒÊl¶úTÛtß·VAtx˜xâ$mgJ*`#Žä“f¡·DÒ1©ñ‡ù{K8 ,,Ÿ3 jšàbM}oY‡ìæ[,vU‚ofObNõë)]¨¬Å¹´Ž@M}“+KL¤n½|üë³]®c¡²!1#ü|-§„± e²¢ºA,V/ÄÉ‘ÄR˜ùë–Ï„ô ¢]O—Öƒ§Äö­W,İþjøÎU‹ÅõÏ¿¿ÔHzW/È€IiÉ€od¡†i3N6MÉŽïâšå³a¨D()¯]™§;˜fŠLCôÏ[9¢êp±Œ#À0Œ@À"ÀÄ)`›Î³ŠK(êœú:ež.†#8QlZR¬Z0Š.T _«Þê1vd"\:wrŸÄ©·2|zq¥6uúié’Fjofì>ž×!_ÆØ‘pÕ’i¢³îÓºûÑ͆SNÝaHŽBÊÂüŽÞ™²‹up$»Ô¨]¼ù²¹HvjàDþy8UpÒ’ã`ë¡la®Ie¬D¹6.öbûÖ64ƒÛ[¦I£GÀÑÜbøµŒËçL„e3'ÂÚMäé¡_÷CN‡¾2|F€`F 0`â˜íæQ­åH¾àLâŸG— Z¦&“YŒªSЃ iI0}ü(Aœ’ã£áò… Õ¨u9~ºeÂÈÄX¸zÉtÃÀ ÷£Æ*ÿÜEøjÏqˆŽ€+ñøˆ¸(ì¤6öÃ9.b…Z­;®¿"È>[ Ûåôés2hˆQ'Û.HÍã„ûø–ΧÓEeð5j¨þ2‘Æâª%3Dg[ ŵÄLˆ¨øç[HÞnX9RâcpŽ.#ì<š+Új±?«@´%Í}VšM"ö™gJ ¼Ú©å,EíaUm£¨ðôq#ü˜vgžéò­hÊùåîLq<* HV—LCxÀS9Â*pÑŒ#À0Œ@À#ÀÄ)à›°÷puJ{Ï6¤gÉüŽ|‘ K/Šû4aº$9÷@Ó¥dauGðÉ«¤¼F$DÃúÇ ‰Z¸õŠ‚ }µçôtÜwvT©°cQƒsMæ¬påâéPZQ'Q#à«Dø’¶„L³$Öî÷¦ ›öŸ‚‚óM‰$Rgˆ§vÂI|_'(ÐÜÎûOæ “R2-¥¦‡«P¦F%ÅŠPþHÎ[LUÝVO¥R¡lj¡ƒKt—.T¶oA¹Vc~"×$3¾H}É©/êÀ÷`F€`&NÞ‚ÔßW³ÎU!Ž„èpÑñ$S§ý'òErÆ?˜U(|DΔ” _¥ô”8 sõ8ŸQ‚ÅÅeÎêèq‚\¼·aò!5:¹ÅàÀÉQîŒ i¨Hò)q¢KÂäŽ3ùÌìF? ïžH»±rÞ˜51Íý0o#îøù&œ‡‹ˆzç´ c éÞók7 â~ûµK:gqí“™&™lÆD]ÇÜ7,è7Ü©;9î:ñýF€`@B€‰S µV€Õ•Fòë‘$­Û™)&Š%JãG%ÁÒY 62jêœÔ*½»D&x”.TÖtwÊÜFòIC¥EýáLøáè™sp ¸'"zs§¤Ã hA8ù?‘á ‰‰)°ÃØÔˆ2¹¤Táý‰ˆŒF?§¦Œ¬ˆ¦x¤QÎ> —/š&LDŸ{{šžfÁ—Îo_µH”s"ïœ_'ŽC#ð#À0Œ#àG0qò£ÆŠªPçÈŸ:Häۮ׋‘|š³hñ´ñ»}‚æNÁôb~£óè³dAÓ½%3ÇÃæƒÙ8ÏŽšqÂY2…ó‡Df‡m9 af4‚ŸM¦Éé#`)FOKо¹¦d]ü}=\rºeŠ–îRIyüÍô(ˆGwÂëST=òR"3=Ò:½¾nh44Ñ´RìS¹n>Ô¡x"T´ø:‘;•ÄÙŸ¾ ¾ÆïÇ0Œ#Àx‹'o‘ãë¼B@„ä>‘‹¦æzG²‹:ÌgCsåÌDÿŸn½\˜<ý{ã~ølç¸zñ ¸ç—Š@ o=â7ÄÉl± )WûÜTDgL‹gNѽ‰/ò+º#M²‚4‘qçDDg‡ê|˜÷F€`F À`âà è¯ÕÿNÚSÚuì4ìÅ@4êÝyÞ£&ô ùŽÚëuZ׈}îÙ2 …¢–™±SJŽø”þð꺷ðé¼8î Œ¡ÒçÏœs§ŽƒH @QÖ81Œ#À0Œ#ÀLœ‚§-êI$ùé©Ò¦6ß&÷óÆÙß‘¹KçφñcGA\l Ɖ#À0Œ#À0Œ@ð!  ¾Gâ'b|‡@˜^8ß'F€`F€`àF€‰Sp·/?#À0Œ#À0Œ#À lª7 rŒ#œPD½”„h8[ZœÈOÅ ÷þ橹6›õ*P(Ò GŠÀçó1¬k8ïzº»KÝæÚñѲlP84 z‘J©ÚðÂo9ì£[óm|HMŒySÓáÓmGû…Çõ+æÀ‘ìB8‡!Å}hÂÙk—Í„/vï€"cl*ÄÇDÀöÃ9¾®ß/ ‰oo\9ž}ó+8÷öé)ñPz±¶Ûðå½]Çç @@ñ³Ÿ=§¯×7þX¥„ŸZíÖøvè-Mv£µI¡µ™‡ÕÂeDÃù¾ÆØ¬ÒÚ›ÔF‡I¦D<~÷£O^°;©ù·'ž¸Ãäëúðýœ<ñÄkúó–ó(Àþ_BNq¦­¢ÉnPÖ+ÔÐ:¬r§<ës9µ‚ÎÞbt˜ÁHrú;’S›þi ÿÛŸþô_$§È­8I˜8I$xÝ#Œnìñ|O'¢Âõ,axDŒ"ö..ë@š¨žzÂÙ'©§&ããÈ9{þß[ž‘&•R ß\5^[·ªj;•Ä»Œ@Ð"€Ê$Pþà—]Ù¤ixç0KÕPìW i … µ5”#æˆNx«ZÅc ?vRrqDÚKÍÅ?½ëá'îzåŸØ€Øq§Ô‡¯ÆÝýöŠ Ö’W© š|Ç(u&$©sZ…)äåÔìÐC¹u”Xg&WXÆ?Ýdlø ¾×?ú×3¿#9¥yWXV„áéÕúð%á[õ[Vχ •u01-.âd´4É'%NêyûµKE „' «ð<Üyý%ðòÇÛÅèúšå³Ñœéb—‰='ŒN¨sSàb³¬Ä˜Hœçh$D†ãD¶¦ÖZ¬ÅÓÇC8N|‹á¼iÑûNˆùš¨N%å5°hÚXxóË=„eÌÇmJû1¬¹‡FfN%&%MÓÝ7¯„oØ×,™gp^¨Hœ4÷ŠEÓD8óÚ†&°Ùï~r|4\2k„tpâL É)‚Î÷â°€9dÿMÀ÷àŠEðÂ[Eøüûn¹ 6È‚¥3&ŠIš³‘XmCí%…Õ¿õŠ…b²Üï\µeÌÏ÷’?då6ˆ¨î|øñŸªTšÿ1U:––l‡M¥D¦8µ!€ä&ÔäТ¸`L]#W&Õâ×ÿèÑ'zù÷ÿ³µOȨ Hš~æp(ž‰P•à ÝzˆW±œº¡äFi2iQTjFÃqÓš¤zmâ:|¿~õŸ$9¥¹`B^V‡U%éÖ^¼é'F†H šp{bZ’¨Y\T8|²í¬Ýt–â䮼¼ª&§EÊw¦¤¢ËS©yí³ ×:ËR)b‚Û½Çó:&ºîKó!­Ý|ò±¬ùSäˆê4Í_ühÔÔ7 ôÑ–Cð1.W-žEeÕ–F$@é#â¡µÕ µÍ‚‘"RU×Ô o~¾H+ ÓŠ9“`?’À7?ß‹gL¡ÄÝïŤI"ºk-†— ùëJÛ×,™ 'óÏÁö#90{RšxGˆŒŸ*púSl=” ëv ]ÐøÉCz%h„^óƒ‡û/$MW›§¸ñô*$M¡ðü^?#ásÓ™µªqµgðÇHñÿ~øëÿùY–ò3ãuÙ|a(œ¤ ž©9©\öO’¦3ó R Ë/©Fj²ô~Ó{ޏhp¡÷>¤eµ½É’”q ¥?é<ú$‘©Û¹òjZJ¤1jhjªºF0†éD™GOAƸT¡*Dçy£A ÷kµXtH”(GM•u¾¢½¬òª:—³ýÕKgŠü— ‰¡t¡²VŒÞ—W §|qÿe¢FˆævJÁú4¡¦ª¡Éõ¸ÐvBŒ² Kaʘ˜:6ŽçËËÄ:51Š.TŠz—U¹ÎNŽƒ•ó&ÃmW-‚ÆfÄF9Íå½\ûØ x%Îýź¢Cæ´Ä/8§¨ dÀÑÜb¡“š(d¶¼º^T¿eØ]Îüå™XNý¥%¾ôƒB}Í·øùÕîHš«Î~©PÛ-ÿp¾x‰ð"Ü å¿ÿ‹__Õ†iÿ~¬}QÙÀ¿‡Í"¯$M’Ç<ÿjË©'ÍJ8^„½çô¾ãuÔ¹£÷?de•‰“'Ò yÜ;ñ41« Œ') µ>”’â¢Q«T'¶-¨9êœH+ƒfuÓÆ„ãHlHCóüû›ÄÒjv~˜ã"Åe‰X¦ìXº—õåîL‘ç‘\‘OÞ›4Oe¨Ñ’Éj% 1MRL¤ˆ˜Ñn@›ÜªA–& Ö+=%² :Žx–W×™RŠnìT‚q×ÑÓðÖ{àõõ»„òÈ{Ñvo‰ð$ ZçäŽ{çs¼ßw¼ú#§]KòÍ‘2$F25£æ•´RþœXNý¹u²n‚4M›¶0*""â…X4Ï[Q¼©ë‡0 Í·•&ÜbZª:­îåÕ«o Ç»sŸl›à0¨ÔŠW"Ue0KÿË©øn‘Ê ˆˆŠxÞ{,B’'/J üKø% ü6ôè Œz-Ø»!?Ý]œ ?¼qšà©Ðü®¼»,®c'òÎARl$äŸëj¦G™"P;Eeé0¬ó ÖÐW¢¯Úw®Y‚&Pép8§°KvÒ m?œ ?ºi%|÷ê%°ãh.Øl6(­¨þWçP³%I›¼˜HÝøQI¨YZ,Ìñäq"M‹Ñìðûk–ay+äaׄ§Nͯǀy±?rêAqC’ÅbµI¹CU(ËéP!’åÒ|£u3.[q¿C©JFŸ&kš¼“ÂmÙ¹m*‡B9"uæÄ·aË?*ÞÁÙÝUJ[¬âLJ2}š?±¦©;ú:F¸ÍЯSâPq2½÷˜_‡ }BRV98D_çiD? ŒÍaZ'e¦{GrÎÂÙ6Ó6z|Ú>‹Jäÿ‡W׉mú§ÆÑöãHžÈ¯s"ß!JtyÞ½¬Îùi¿Í;‡ —åÈüûOæ S)Y¦<þOôrO²ž•hfH~Vîõ |%hø6j›È·Êbqj´:ß˽<÷mÅ'<õHœ¨\¹¸çáíþ!@öGNûWúÐç®F3V’I2mj1 ßYF‡¾ïÀrÚÞ4®EêU-.†0cØ=i=A S ¤Ñ‹Åá#À’þ‚‹Œ^ÖõGu`· µ«…¼bPàŸ&hòb`ÍO>O‰ˆc¹qÌ=XÒ?p!9 IY I¶80ñ ¬«e‡~tr,vê@uðä úêð‘IipÆâ¼5»îµÈ¾ÊêõâNzSfO×HÒÔíº=LÁ1ϸp(1àR'&OÝ"åÙAoåÔ³Ò}“«¥Õ‡³ÏÂåÁñg·]!‚¦øæÎ=ß…å´gløL¿pi›VßtëB…R•€!ÇÙô©ß0v½@à¨P&Ýøýÿ\€gCv$¿+2:¢üšïe2†g9”΋ GzïéýÇ#!«ubÓ “¿!;£¤`­ZŒ„Sô¹žÒgÛb˜psO§]Ç-ès´~çQ Áup€‡N°ß]NJÑF (qHK…3>ÎsßÕ$ðï$1ëœúꩳ0R-2I-¦Ü_»é€Ük ßOQõh¨ØS_¹ òËé ºÅQÇSj›ô±‰I«ð€ƒæi ]HïÉGò´w„GG]Ž¥ÂE†}¦O §þ# äU£Ó^I¸Ò@î\רDŠE$°3uè5èèa0==}0¬pº¼ ¬UVÊö“]íÉW„ÌóÈw%Ioƒ‘Ñ0 ŽXžD _iª7t5 Þ’YNÞ¶,§ÇKèêÐS©#Æ_ Û__ÖwfÌ7j)Ô•gzMvÔÚpXvÛF¸X´ZÎÕÒìÑ}½É4bÂõ0añ/!,j4Ö·ªJöÀñ¯¶~ß³ÿï¿7õõ×k˜8ùkË A½¨SJD€u¾œûDš0¤6úé˜ZLèÏã$N<Âf#âD&kÁOš$ÜÒ ‘ùáEQó(z™å‘XX˜A,d¢Gç GNƒ‡ËißX²œöç4|>¢Lš›q¿|rù>˜Îzõ ‘3‚B­Â¿ü즡#MT9CêXh)ɇâ—~êˆhÿØß¡qùa¨üú#¯êîv‘ϱw»w nú 3¥J ³¯yö¾=‘<¯ðŠO»J dnxÀ Ó¿[ZL5Pxä(/Øa‘£`þïAZÆw ðØËý+¨ûÜ>ýûÛûî('ßa=ìw’-êì‹m$O´MÄ)Ì`¤‰G˜ÍfŒº‡'4és'NÁÌŸÚ”M.B©DMœZMó_9çi"ò$·ÍÝäœôÖ©i",9 ,§=c)EMbÄrÚ3V|fP œû2°Âq°iÚß?…s¯?É7ßÚ¸$¨Ù·Jßù;8ðwgü£ÒMxüÜ·ÀÉûÖ€B«‡Qwü¢\‹*¾xÊ?{ƒLDYÏþR¿ûÐHƒ¢¿?.¶©’SŸ{ª¶®‡²_ƒÑ÷?‘Ó åžjï€óoþťъ[y$ßt'hb⡹0ò±<[Cˆãßø!¨#c >sÿó`k¬ëðüÕ»¿Zd"MYø”Ùý'NÎ(pƒ‡³¬Pè­Û0 fý¼W|äì|ÆÍû1èÃS ,ïs8½÷iP¶Â¼ëßÒ-¸ñ]tƒ°Àö-¥ZS.y’Ç]ƒÏ­P”ù*’•E d\úÔ_< †¨4$,·Á™}ÏÂèY?ç–}gœ?µ ?ÓV? ñ#—¡œÚ âì&ÈÝý{—F+uò7aì¼@gLeýân°˜jŽ[ð h qPY´²¶="Ž»7}eÉN€çss%´6—ƒZ垥?ÛîrJÛ!“˜8…LS;”:\2l6ìËPåDžÈDÏŒ¡·Ú&›Ð8¡µ¶Ð8…‚Ö‰° öHãDØÖ‰æh"‚IdI#´PÎz¬iº—‡å´glYN{ƆÏÚ„H»û1$4¯Ш> M§CÍž¯¡f×$sàüÛóÅ2ñP —ßQ³—AÑó_Ösi@IDATƒ::Fßû8Ôîß‚fx¥@e{ø/И“ •H†Le%â\ÒšÛáìßþ,Õèwd†–ü¨Üø!(4Zûó?Bó™“Hª>ãÄHªþGŸÚ·7.y” MšØq¼ô­¿ ³ÁQ?zVßeŸü«G Uá‘–> J÷~Ýc>8"F´KŸ†üC¡©+µåGàÂéu¸| 1) !wïSÐR^"ÆÌùO8¾ñgÐÚT†Öfh(?%'Þ§Fë%¨+; ç²ß‡èä¹0ýòçÄ}Ëö®G“»1‚é“ÄñÓ»Ÿ‚ÚŠc±ò0 ‰YÁátÚ™Q‰? pá|öÚ.yø@ï0q꟠=K~ꀑ? èç$¾H›+Ž5çg»Lõ"ç,Z"Óù"\È„/fÑ*([÷¦ÈK¤«ð¹‡\@´^(¦.hÌ>â:V¾þ-¡¡Ò%¦BKшœ½D§Øåׂ­©Îþå1q¼ ïi®,Ú0:VwtDÓ={!N£ïy\µÊÍŸÊbxà”|g½"ž"uê@|Ú (Íù5>YâX]ùqhªqšêÅc‡òÂM¸_жä¡öé(@9Jj­¶¿y3ØÌÎ@%ÍIhŠŠrZSº_œ§Âtµ]aHr*³!>}¥ N)“okk=šõ=ˆ¹Ú](‘™K¡,ÿ QFEÑH¢{vCœR±Œqóñzü9½û‘Ä]×ð?Ï`âä9VA—SŽêSŒÌÒH«BÁ ¤y‘%I˜ä:è@p{ Ù!¥µ\,Bs»„7}€áN¤•åÔ 6áAIÊ#­YNØðÿÀB ¹à”«ÂÖúPê ®}÷ …J SçŠà ±K¯pÒaÔ<™êݮÉ4Fá“f!±¹ˆæ8|›É6>ÉIÖ:Ù£GÌX$L÷&?í$g¢àNyÜo–zûOÀ8aä>úƒFÖs/–·‡úŠLW Ì-U R‡¹öÝ7J5Ä¥.‚˜äy2qëTXì8×võ…ƒ.Òä:è¶A&x¤1Š1L¨Ò‡p™Ü‘© “¬µ“&º4nÔr¡ÝZrëzWI=õÙHÓ•èyˆL˜ s×¼aÑc këîëx£o˜8õQÐç0ê|Qr'Lr_œ²SJ*q LjG”íÁrê”MÙh¹ÏkF P°›LUÕa³¢_“*6®2›ëÚ~·ìæÖ‡;ï$^÷3N>p½ðkšøäÿ¹²XÑ—)Íë:'[K#XªÊ<Š˜tý÷ µf¹OÜ-´TËâýÀEÀfñ,¸ˆÃnÅ0ßf(:ùjsžêöí–Þe>}ž"§Áö7– ¿¦…ßøÀUŽ¥µ¢2\ûrÃji'O#:ì °²x;jÏ–Ébxí!Ξ²‡™9[h @1ç¶Ó,Íi¢†&kd¶ä‹ûsœü–S6õ_éäš ™Ù¡™xÄÔ9  Gçu$Gµ·Aܲ«!bÚ| ”!m‚ðEòô>€ÂRS)&(Œœ¹Â'Ïr]Úp|?hPE>QÚø»üŒ¢îàvÐ&Ž„Äën¥Þ€¾Uq®“Ä_v#£x.¬ý'¨ô8múLÑÂ)¸hª%3;;Ħ.>G´”±nj›n‚¸‘Kp5¥q“Ñ)Ýc ô)èëTâoER³}¨æ»®­*Þ ºðd3ûnáE¦{Œ¢£ä‘ïRú¬»€æhÒ„ÆÊuaÛ 9£ÔÚQvâ˜Õ¨Áj×úvÎÏûÝ#À§îqᣌ#À0Œ#0 P4»Š kaä¿€Q?üdÞ¹JDà}Ïo`Âoþ!ˆù$<ƒŽöN§ü¾ªyËKà ˜õÆNhÊ?äïDA (Uïú‰ÎtH¹í~HýÞOÌÉÿªîÐ(}÷yHùÖ½0òû?dŽ|ªsŽu¸]ÂUß"50Ö÷—޹en‡}Þ .(š]щ7`Êòÿ©+›_ž 9»ž„i—=ƒ¡¾Ñw‰ù$ùâGâ¬G_|ü ˜ ®¸'ê*ÐoïèË`l#^¥¹Ÿ@Tò˜¸äW0iÙc`n©ÆÀ_AúTÙûG À“/ùoAæ Ž¾5h螢‘„MZúkІÅc0‹b 2ñäü³{Þö&N€ÄYF€`FÀ{ÈÄÎef‡Ú¤Î¤"ÿéŸu(üÜ«„Ò·ÿ†ÇÐï¶ÕiÞ”ÿÇÿs3©ÂÂÁÚPKvåâšÎeÑÁÊM‹EdÀÙG…É…9··8óå9*§ä•§¡äµgq¦(AœdÙe½ ´¨£b1€D£ˆÎ纮m#û¡Û:âýE€Lì\fv¨MúêoižäÈú;;ìgoNïyJ¸8Ø­N9=òù]bn&.ÉM æwÊ)…ïœJ²ÞZdª)= &ߥ0çVs£<ܶv@ööß@ÎŽ'@cˆss5w–èïè»ôw$EqHÖDt¾NCÖ–_AüJhœh\NÞ!ÀÄÉ;Üø*F€`F€Bì­]}KVœ25BÞ¦.¤É½ œ7ÇZGÑ®©§ã]sò‘PC ;(ò#"·©+ij/Éá°!iªj?à¶ÕÓq·,HȘ4¹ãÑßmöqê/bœŸ`F€`F€`B&N!×äüÀŒ#À0Œ@¿ { ×bµY[Z•§P¿ŠáÌ=!`Vj6»l]8÷”—÷‰€ÀÐæ°7YZ–Ó>áò<ƒÅ¡sàœŸ¤ v—Ó˜‰“çòÂ9F€`PC K§Èlj­mÖ„w9jÀ æó6iŒ‹ÙÜÙ¶‹1î?È.Ìì[…ÉåÚïQ|EgLŽH‡¹µ »¤Á™‰S—¶çŒ#À0Œ#Ц榋&M˜¢U­ë) ï„#áini.ïÇeœµ,­­¥f‡QavèûÈɧ=A€p$<éý÷$°æaâ¬-ËÏÅ0Œ#À 4¢,–âÓ9ÇqCQ1fðJá’G³$?âGKœC‘AytGYIÑnµÜÚurãA¹CˆB8Š÷ß|t)§´©ÄÄ)¤š›–`F€è7î$ûÉCûòm–ÖºüØI!×iê7r\@8Ú­æšÃ;·äbvw¬=¸š³tƒ€ÀpûçgÙm–ªëL–Ón@êï!‘Þ{zÿñZ;.!)«Lœú+9œŸ`F€Ü;G6|l±”ä]‘¦¸`L $†àI ?±´¸xÄ×î˜Á]ƒºHwìl•eåÿ®°ŒWTZGõCõÃ~„#½÷x/×w·ÝñêjøEùLœü¢¸Œ#À0Œ€ß"@£Ë²³dÅmëî/Öm²™Mu»F®´[•¿­¸?WŒp#üì–Öšë>YO¸â"q&Ì9y‡€K^·òáZ»Í\uÜ´Ænu°œz'áFøÙ,¦:zï± ñ ÀuHÊ*O€ëé5Ū «àáVcˆƒÌ]Ûþ¯±±ŽB‘¶’Û~¢Œ:¸t~\6 h4jP(ø›;á’Ó(§(§ è- SÔZ‡£JÃzO+è ß&‡åtX["dnN#ø’4Ñ '–c{vœˆIH\Óf^éW;VoR¨íηAÆ‹%M‘¦üèñŠÂœ“Ú¾ù°Ä×’<±ÆÉ lÛ.é ¯{6~¾/26æM—ñ}€[³ô)Ô –Ó¾à%M‘¦s– EÞÉÌuô¾ã5®÷·CRV™8õ%9A|>+ÿ¼µ~Ô4š`TC1̩΅´†BÐYC¯#Ú¹™)<,E:Ê‹Ÿlk…íOÁ·¯Y"´PJ¥’ TgÀ†p_Èéç(§ &HRçÁxC&®sA«0 á]£h K‘ŽÎµÎrºíPÜvõR–ÓÀh¾µÄaò<0¢ÃAÿÙ‘¦OÔQ¢Fi1lýôƒÍp(`Ú¬kkôÑŽ¥%Û•#šJñ§î Ÿ&2Ï#MSþ©ãoùxí˜OâIØƤ-aâ„ x™ºÈëWï½õѷܦ‰ÿQowÌЭWÆ«‹¼,>ø/#Ÿ&2Ï#MÓ™“ÇÖoûôÃÍøÔîï¾ÿ˪Cu>?Ø­ÅÄi° ò6íÏ‚7„XS¬)ÞüC×±áˆd•¢y'B=_ÅÄ©gl‚òŒÃá€Í²àƒM‡`lm¬,ÞlZÑ{S©¼ñôû°-m5Žêã0 Ý.È“Z­ä‰Í÷zÇÏ›³îršªÎ‚Ù†€M+zG’Hå%ª—àhËÍ,§½Cå¯g‹@áHº÷©§b^xä‘?«¤ìˆJužˆ4iq¡~„’:Wg2,ºâš¥ÖI“–N‹@Ãf‡ÞÒì³4*tvKÈÙ9·*5ŽfM¸ƒ&·EEGôìÉã;ömüb{KKS=âF8’¦‘Ö´¸â3qB@¼L=Êë‘Ûä>”·èÊk®°ŽŸ¸²B;>’äT«hrèu Ârrjqh&G”ƒ&·•rZ˜›¹ åt·ÉÔÜŒm@K@É*}GmMÖ$T†y)C=^ÆÄ©Gh‚ïuFÉìéÃÍNÒ´úì—Á÷CôDD. ¯MéWú 10}ü(á÷Ħ{ƒ º»œišöïÁ½A—FäRàÕ|+Ëi€µ³J©Ú`µ[oo¶_‹UË«/ýFÈ–›´$¢L…‹T½Û±“eÛöÙ[¶lŸ1sÔ艓&†…GFë ju-Ÿ¦HKÃyÃzMD¡ÜöÕÚjµ´¶6_lhil¨):““{ædf1Þ›È'ÞSG´®m!E˜¶äKÆfzÂSòŠ`ËÇï¯Çò7O˜>kÌè “¦„E¢Ž‰µ2¼ý¾ ›Én¨´94D‚69å´²¡¹± ¶ètî鼬Ì|XÒz¬¬Òw»¼J5~W»á˜8 6¢~\EÉ{{ýnažGš&NýG€p«ÕÇÂÀŒ¸n4ò¤RQÿÓ` ä}šÈ<4MœúáÖМ|ÍrÚô†çŠ~ûÈá=öÛs8pð¬?'÷Q|"KôÑ“£ó²“Jffd§ÁÎW>.DV(¯Ì‡›¾K?žþ´¼Û{9/Êm® 3ÂÆÒ*I"M§Ú¶m"SÒ¿‰®ã40<’×3'ŽåªŽ;NqÜøáØÙ6’Í-P´å˜â‹5vjŸPHA%«â;ª€sô]ìÆcâ4؈úiyd^FÑójšL§‰Íó¼k(ÂmiÉVX§ÿl;” —/ž&E¹û²Xp/ø“|Æ ‘Uú~ÚŽ«ðsóË¡h>&NCªŸ•I¦O6ü*l=˜%¢çq ˆ5áGQ÷PòYãAÙFšˆ<±¿“÷غä#ÃQô¼ÿÏÞuÀGQmý3[³é’Ð!Ы€EQDì½ÏŽ‚J5ïQT@T,Oð©O°}ê³" ¢): „ÞÒI/›dËÌwÎ$³,i¦ìnv“sø]fvfî½çþïÍîýÏ9÷\Ñt,)'áG8þžÄã´yHº.w”6êÍtKêã8]{#!qå%«§º£‹M°È²DB“.å3‘²¦q’­Nx¤9†Bž.b1xÝéâ§½¨JW¯S°!ÒDîw„áCĉˆ'eýaG÷ KÇ" ŒO*UésŸÎ¯›ÇYgâRå‘fìÜ™ðX²+Exû—=â>|ô¢ÁãX•Ü®4«ø½é-šÏ¿Ó±³‘š¨74'g êfeÒ[üÓiç¡Èh‚Arœ¥ùtC7ùÅÀÉsYУs$h4´8 m]öšŠ®mœ–š ;†gi>0âÞî²nO 1PÈ“u&ÆBIyk¯&刷œ/>‡ @ ®e2JG%>JR®¹JŸ6]ÏK5—ˆ–âÿCÂk_ð妜vZLú;¦.-OCpÜb¼¶P'yäX¥}š*CŽKwQ ZÓ„øMt¦¥Ié&N ­ô(â*c § täÕYée`ˉ¹(ŒÇ“å0d4$'Açî} ‚†]&+—·ãgH[ó:Z£L0d „Ž»r6þ:<0 wýÖ’B9¬eF7 ν· wÿæò†ž&´õWTà‰áLx³4ûqª‘½}_†£rH¢ ŠsRä¤õ €ØK_Î,¦bˆìytò$xù´ƒ‚¬}¼i O㯰 ÆÜ· ö®K€ž#æ€OPg8—ô Dô˜ VS)Dõ¾o™Ù§~q”š .GƒF3ÓãåN’Û^bâ=ÓÌi¡ ʓ辷F*Å„9 ³$AJÇù_˾ û°P_Û¨¿KIUìµï-[ýÏþç¶.8ÁPA¶Æã[pÔÈUÔ†@/¯_£DqKW¼'wN°¥sæþi{Ëo0‰’ðaÂÜ ¹Zz¼^Ðĵgž4VñÊW„HÚ܉° {5»v&‚pÖš¦ê½ÁÄ©:"­è3¹?).Ptt4:0tè EÿUмã52¿|OþÜþæ‡A4W@úG+@­3€_ßa`èÜò¶ýü*»÷…]}îÙ¹›¾‡ò´S-Ö4šð›L&°à*S²ì)˜³»^ûDÁLÁ¯á9ÿ¤x_ìS´ÚšØ~ ô½òuH=ü9äžÙÑ¥oÐäaÛG—á+e ~`ФÿB~Æ.8²m”œ€Ž}€ì3¿@Ú¡/ 4¿iî~Žh%SG Ø2eTM–aíËþ1ïÅAVÑ:Ï;bŠÄä‡ÉmÅž0ØOÎ\¥°=_AO÷ùtm¸½`Ô 1|¢>¢À€¯ÍÊ&§œ3÷ÍS®Ù[z¼ÚëÂçu"wvã{˜3´¹­3öiª³æªLœþ!¿¯¸@¹q 8 QÁ·W´zÈúnŒnè˜IHŠ6@ÖÚäÏú¨N:öZ™8Ñ•Þ ÒÿýämÿI¾OÿÑÚ(Sv”Þc»Ö"'HH-ø6Ÿ&ý”o–Æ#àNãTç}'¬€Àðþ@k”Žÿù*’'+DÆÞæ²|HÚ8Cn ¹¼_ÿ1F F²ô—|­ sÏEn}"Z¯Ê‹R!?½ò%Aã‘qP§²e‹©š$8|CÇ–m•ójŸrM¸,¡õn¬ójâ’Ý rÍÃïlrͳ‘&´4íôR«n›÷Âç¾p'uYC€‰“‡uXcÔUÞäËÆ&ù¿Æäv³øÍeʈÓoÿŠöÿ’©Èú¤ö €r» e§S tü ò=E‹Â½Û”S·:Ò¯²(“&\…€ÌÙâÔðnR0s§q*‰8½odÚ¸æ‰ÄË7ŠrÛV˜“$Ÿü£lÄ)çÌ&Û}w:áqêN½Áº0Œ€3ÀßáÇejtiµ.Åï/ص¥^•Ëp]“w÷xP|0y×û¬³oÚ&ýn޳³qhnùî>NsÎn½o{èÔÿAðè„kœ¦Ê®{äžW—”䇀v0¸„/®×ó©ë1—\çq꘹F€p1UQóöU#M;q+ˆþ×\s[§®£…£ë¸Î—]„¹æý´Tý”Õb]‚K‰åù*EÍã m]Ô\M­0qª¾ØRxEu‚€—b´Îÿò¼®3u4:,¿‡-fgVÃe·2|‚ºBx§Ëq䈚üY½!ÉÑtµÖDK!±:¢8.ƒhÓ¨ÕÁK,–Ü«$Aûäœ)¦m 7m<»æ¹iǰZÀÁ!xÔ‰@äÝO@ü[ßÖy¿ú ¿>Cå i«_oègÚ̶ײÏÀ·÷@ІGXQÖЬ~ÎЩôø×`ÀÇÛ¡ßû¿@§§Joht9œ¡åè1rŒ¹o[ƒ ‰)oHÛà Õ¤ÍlGݹ‚¢†ƒÁ?,fcµ'÷1¢ûdÜ3ê7¸bZ \žpL|ˆD±0Œ@ÓxbA«½O,·4½Îé,È5O´“«¤}¨qyCÛ«fÒz&F å`‹SËaߪj&ËM×™Ë eÖ}PžvªImóï7 ÷rÒ©×gƒXî<ÒDÊ¢º@Ù¹pvåBÐøB·¹oBÉèÝóóWMÒ3y*µN&;?ŸŒV¢ãMR:4æRTZØ¿þ1°:‘4‘ræò|8µçßurxûGÃë?ƒ˜¸»àÔ¾w›¤;gbJf?h:„gó÷A .×¥üö/è:øqðò„Ìã?ÀÑ/£››O^-[›†^ÿ)ˆ’¶üw¨4^ÐëÒDhßõj°Z+àÌþ÷‘¬¼#ƒ7îE(:Ÿ†€$,w±ߗAÇþË÷FݵÒ}'w¿ñã—Ah‡Q8N­}z#Ù¾Èæ¾{3tüè}Âå²ö®K÷u¢ë]‡>:CäœÙ É›gÕØï)çÜoç*ûÃdÌ chp3]F€`ZõºæÉV¦Í­©¹ÜF€‰“wž«U×…EBLÂ\$4ïɡ臟‡Ò£ ÇÏ¿m=öñ 0Ï”U »â0 μ54¡Ðñó¡à_¡"+´Hj‚n›æü\Èøò](:¼´aÐîÚ{àô/€9/×™ ìD äløZtyf %!©ú|zôERõO TðñFз–I“68L¾žþÑ 2=e–LÌ2¿ùop©}ýÁ»SOHßùsÏð ÏAÀà×âǽ 'v­-B½Ç.‚‚¬=qô{LßBPä08²óE(+J“'®Wº l| ôÞáùRÈ:ñ# ÏȤ¦û°HX²áøŸ¯AnúïàåN…¦CEi&ˆVg„s?‚J‹ÖJ(ÌÜ ©‡?‡Àöƒ ÏËåz3w®ï€Î29Òû¶“¯Ýþ"d±‹!‰ÙÉÝoךÜÂã ´#¾€Ài‡¿¨ñ _`FÀS¨-j¶enh{;oh멽ÚzõfâÔzûVn™€!h(i5j(A7¥æJÁŸ› {í'r1!ã®ÿþ—@ÞÖu`<}D¾fÉòµÂ¬Pš_骊A²NmÄÏ'«Òq´>M„“è"G¢ÑùÀ–57‚ÕT*6¶CWT§ùéÈŸé?Ùu­]ÞHrŠsCh§±2qŠŒ½,EèÖ÷>…q¡ª$IPyI:džX'_É>ó+´£:k!NQXF×!˜ÇÖÑí/!‰;¯Ó £EÒó8mRü#À¸ú\󼇰kž+û‚ëj8LœŽ•Ç=i?‰÷ñÒA¶Ö·Ùm0ž$·ðJ±å×PAPkÀ¯÷ ðíÙ‚G^©d=FÍS¤8eŸ4)×ìä‚G#ßžý‘ØœG÷Àp°”ÊèBÛU’5œÀÚ‹_ßá²ë^ìËk.\®öÌ…Q÷< >ÝãáÈœûYψx¨k%{ÜíëâóÚ°Ç‹Æi¦Ø|W´¢ìý¶ÊLe¹ ÖÔPAPi ƒ<µ ‘=®µåñîj;ÏËøËFšlíNÈ,FAC -P^¾6—;r¬$kÓèѲukÄmkm%á$ÂvnB–®»Þÿ°Þ0èÚÁ;°3$ozÞþ‘zÏË%âqZ/F|“`\‹ÀÆ7ýBÖ-QÑ[Tûyò†¶ìšçÚÎàÚ…§FÁå¹û{ë¡ ×rThô Ç°ÆM±¼¼AY%«×5™!{Ã@nsµ‰TQ¿á¸æÉ»s,$=6Y^×DQð±àZ&t¯«.Ö²0çfBÒ£×U¿Uãs»É÷B(ZÍŽ$&ÈVªÔsp$<ÛkÕõ<Å·‹Ó ð“ä:¡ac­¶:¬æ†‘D †ù6Á™¤ÕhÍy±¶¢@4ׯG'\óä[V”×5 »éK[9æŠ|‹³}VN,æbÙâ´åÃQÊ¥z’h†Â¬ýsv ZÏ–‡ $ OÓzñ囌#à:È5Ïd,¹hC[¬]ó\×\S3àpäÍÏS²ÒýŽíƒÐQH€³~¢v9¹Ùáú ? %®öE‹º×üµBF] ~ñC€,P†˜îòZ¤†*@(Ìù9X®üû ߨþ¶¬Åþ-Z hM”.4‚GO”ƒQþµtá |Ò ò2àÚª‹ò)„^v=Da¸õŒ/VÚË[^3Eë¦*„#áì£e7½†‚ö7ÏÙÆ©$@–¥&)þ›ì º]Zp =æDŽZ¯@tSaĺõhmºB:ŒÀh) ‰ÅµHT=äå‰k²qø[ÔŒÆ5TClysÏþzßöÐy@‚¼6Š\÷(E6FÉ£µKú?$‡×ù„É+[ƪ A{Fit~rÙáÇ£ë‚Õ·úóÕ?ޏ‡$ÓêÀðç6À‹+Uó­NÖ–¯NÐõ6 “O®y?.QMÇ m· =šªÁé þ” ¯z3†×39 x.Ö¡°ÅÉ¡pº_a4¥è‹oUp<¸'tÏOq¸¢Í.{ýÐáÁýðs°ÿÁËå|§ÍƒîóÞ–‰­I:¹ÚgV… û-ÎcyK„þ«ƒÒ‡€Ö;Q’¼m?"Ùé‘w> Q÷>ä6Hë¯ wm…ôOß‚È[ÿî{F&s™ß¯t ´—°«n•¿±;<8Óþ2ì¹eÐEŸëú@8êTø´ÈUH¡xS]X5äúEãT§‚Ts?ˆÖ^p·kH yÆ\^g®†^£ÿ ½Ç.„_Þí)Ûþñ—-ÅP߸v‰­IÚ³n ‡8Ý"áìÕÐ@\‰{-f€S{ߟ*â•~äh?zŒxzŽš ¦²< <ñd㚪c;—žˆ½ô™ÌÜ»òÑ-Ð^‘„õ9tÞ¡Ìâ,™øŽÿõšý#õžŽZ•Èã´^”øf[C@¹Ì”_z‘+„· K /AÄëÂzû5‰m g´·V×<€\tÍ»¯ro¦ÍΨ–ËdŽ@ͬ"aî‚Í”eÕÂycéÈÒhÈ׋v^õÇ‚©ýÝO>ûz¿ÞÝ{=}ÏUø±éBk&ÊË+ ¸¤òó aóî#°÷d6\{ìQšÞô‚ëÉY¹‰¬„›×^po¢½™ÔÞ¾`).ÀߢÚ×qÔS¤æ\,«\˜_ã9•÷a ‰Sõ²5Á@¢DŽÎW#_3.døDÂ÷Ýo‚(?5ôކ°°Ph AAàçë ^^z™¬6¥Šåk~‚ƒ)'¬~õÅ»1.¦"LäwfÅÔ’âòq:Êû=ÕœqJ›ÕZ§»qŠ{3iõ~Hnò±ÎÆSÚ×b*©U_APƒÖ&c^²uÞ!HÖŠåè|µfÆ‹dq²˜Šëº]ëõKGØf|¨­ÓXðoT HÚÌ…¿® ·ý!ù!»ÎùÎK+ƒ¬P¸ ÿîÇàßê”Ù –-m$4TŽšgµ~¦X™ªªÜ¡ÑpÔ<ÀÏU8vÕs0 îVYCÔjµœzw ƒF€íÑãÀ‚“DgˆXQvi¢:$‹¹VbÓÐúë$MTºñY q2Z !£ëÒÜ‘B¸m‹‹ëoDˆôÓ‚F£ÁT‰/áLx³4ãT+ÀòkÁ"9gœÒ({ÒDÓ:"²áYã€9ê"MT˜$Y‘4®Y6]§æõIcIáFøéÐÚÄã´>dù^[Gà¥ÿ¨¯Å] cF!(ª“&ÇrÍûa©êivÍs¦\RË#À®z-ßNÓ@q¢ =Mð h éß9~?&Áæ˜ñ0þôN«»µL¸å{…@'_ t: &-&Œ/á¬`ÞZÛïŒv)˜]4N;…ÂN§{Ën„!ÞÿçŒj[u™„[‘ü€Çi«îin\SX¶Ò/Ô$”®­bAÐÜ<+ÁŒ~²©M-ŽóUCÀÞ5Oy]„&?vÍ«†ô<˜8y^Ÿ5Jcî3¤Á =MðõzD‡@N¡ŽA7ØØéj{v#hðM;Ký¥‰HÓÉÀn¦·@FÓë1!i"\ _™ðfi<uŽÓlŒHg¼ ¾Àãôï%K‘¦4KÓ¿‹ï·Y^\©¹Ò$”|€Q ÖhuÞO¯®ÔFØ2}ª1Ãþ3Ÿ7:£æ±k^ÃAä'Ý&NnÛ5ŽQLq¢‰½Nô½ ^Ð#2Ì*ü$’§Œ,6òܧ­yrL+Z¶ZÓDîydi Ñš Ü[ƒXz±$< W—]õšÞOõÓÓùqPl …¾úµN[óÔtÍÝ''­i"÷<²4ñ8uŸ~aMÜA€^³¹Ðm¢ÅdœHZjªÙ·æ%¾Räš·n™š¢æ½ŒV&y~IQóIxÕkèèçÆÛ\ Ìõ•È÷÷C€‰“ûõ‰C5"7(šÐkµZy¢ïãã 0¢[{\ÿP§ó‚å@ÑÅg¡[Þˆ)>Õ¬}žª| Fû4QÈqŠžwÎ/´›¡½¶ ê* à‹XžD _ÅU¯Õöتÿvœæ‡ÉÚiŽCŒ¶×Ns¤YûW9!U'öíÍ×Ç**L`B‹S¤Å >šbH-¨€ ˆ’ m©`À ?½-Å ³¶=×(“Z F”i½ tsD¨ª€ ­ˆ.yðö&Âä~~¾àãë#Ÿ®„¯ZaÉ‘¨²4†ŽÓ\Sg™ ‚z0‚—ªû¨þÀ ׯýsX@åb€¼¹-íÓÄãÔýûŒ5to¿§¹D²Š[­¹×b8òŸPÛwÝ[c÷ÑŽ]óܧ/Xç#ÀÄÉù·x äEÁ!h=YHÈMÏjµâ†¢¬MøÃËÊ¡¸Ü %È•L¸ h©.”Àʵ¬sZ›Lç/„I×…E:­žº ¦ ¹)S€`ƒÊŠQA¶$étzÙ²D¤) Àüý1yB< W—pfi:§¢*ÄpPŠ»rœ–§Újðë`;wÕ‰2N½hœªyœº w®§u!°øC¿0•Þ¢ôþ(vÆÝøÞRIÝŸŸŠûw°ü-ìš÷·ñ­&NîÑ©tÆ–,VKY¹ÙLŸb¾°½ÍÇ ¾ˆ³K"MÊ$³r²Š®|¸×’NWþf X,™T±¢½n\)i[?µUu÷¶sWž^„‹ B!Òj+×4ùødK‘¦Àܳ ‰¹éQT=GY›ÊMf|éi¥M«lãÁ•mÿ›ºìu’Úò8=²÷}TF̲»ò„Ç©+ÑæºZ/¿§Œ¿ƒÏ@EÉ@\ó¥F§»Ñb2ÏÂoþ_˜45¬§Ù5¯a8ñS­&N-ß§5˜‰©¼¢  °”®;„8Q‚¤—t ùTVI^e´.‡ÜÌ(B\Z***Ð"eœ¼W‘§Êg]E hŸ)EÈšãJ¡‰( Ex#¼Ô¸¹.‘&†’¼¦ ÝóÈÒ$»êÙ¬MŽÛ¿© ¨T2›L´™½Ô#ö7]t^C‡¶šù@ù©Å+Ùc ¡]Ä®y EŠŸk0qrÃ^-7–ž/.«ŒÄÁ×Ï8Jˆ  QÞVSP §­ÇzÊ‘8•W'²:Y­¢Lž\EšH¯ÜøT‘ ÀåÔeG² ]°6UF#¤ 2¹êÑ:'Jä¢Gx®ŽêoêwS™1Ëå9»Œ¶c<ÙSÄ×ÐMo„nzf±â»Å«„t¯Àwgÿç ú·”ŽuÍëµìPÇvƒz¥m'p4½–ê4®×á0qr8¤M.ÞÖÊéìÑ”Q»Þvðx ‹ïÒä«gTHMöåsü•¨´¨èÀÛ`I“8ÂdBw=´8U­ƒRˆ“+¼ö tHHXhHõ&8ís•±ÉF(Uh‰Óhhÿ«Ê}š(ä¸~ &¦ÊMo+-M„¥#„ú›~˜Î8Ž1Ú\õQ´#ËàqŠhø\€”Ç©ËÄšAHŸ²É½—%o=4#îG§*É…3.B€‰“‹€®§ûɨŸ“ÓÙãG~F«ÇÍÇÎfB÷˜öõdoÚ-šôI õ<29@—=Ù=BT’¦ÊJä=W§íâÔ´Æ51áAR‰Måú/"OjÄŠ0’Ï‘P)Ø5±šZ³Q?'OÒNŸüPÆýب5Ÿ /Úë¢ègm«ãÔßnùSq¢1ÂÂx,OÜW„Ê¿³h¥0Ãcá@ÅÉâÇ%ê§qCÛ—ð[ž/âÏdƒ6´50V¤õjzz°|ßoù‘áûŸî™æ@õ¸(F E`âÔ"°×¨”â‚+“Qò¶l_÷ýƘ®Ý¯øô§ßýžàZ•N«®‘©¹ˆ( "@nidUEɶ®ÉÞÒä*âäc‚‚0¸RâDG%6J°嚣u2™­€ý,JVkáoß³˧1 Œ‡Ê˜ñŽ®´iåñ8­Â c…؄ǩö•¤'%«DáÞG)ù“§ÇíÃó«”Ï|d<&NîÑkÊ„T±8ÑšH“õ‘êà;’Žíßwrø•GZ-æQ8ñö£7<~½à#xiívŽÅ ž*cí_¾¦u¼”*7›¥‚ÂR‰6·%Wì¿’S)‡·þ¾aÝ–²²RŠâDý]\u¤s{‹“;§6?N{c')ÂãÔí¾Ò5|djE`źnú'&'·è6-MqÍ«mo&Œ<;-I_ Þ6tÃË#P?÷U¯W.‹í`»¤§údµi ¹ñ­ &NîÓäEþÃôeNÖò±&¸Ÿ[,/7Z7÷寛¿ƒ-ÝâúEwìѳ‡·¯ ÞÛà§Ñh‰hy¼Œè¬4b÷ç”sO>Z,æŠ cYqYIqþ™c)GŽ%í?‹í!òA–%"J…U‰Hõ= î䦇êÈÂãaèÃãapçqZ9ZùF JRO.^¼J‚?¯k Z¿/¦?PPPËc­úR]®y¸IɽW?kE×ñÍ5Ú_×ÞLH–þìõÊÿX¬ÖoÆ~pêÊÍt.§»=—,Óª¼Üñw¬FÛø#ÐP˜85)ç?gou"²D¤I • LVÉ}‹\ø´Ç“÷ŸÀDÄ‚žUžÃSÏ–g® ·¹O|öÖòw<»56í©o©íû‘¬JD’ÈÒDĉ~¸éœÈ”²¾‰ò¹›ð8ŹçeÇ©{Swû»a}Ün½f/HA—1é.£¹pÉ╯‚ ú¨k`ì·ÞšLß¿­ZãšgD}{3~¦ï‚Ø¥ý3rJvö\z`¨?ÛÉmϾ >g<&NîÕƒÊÄšÞä*dÈ~¢J× ˜äµOxT,RŠUJɃ·Z…±ðdQˆBš”ulÔŠµ‰,Ndi"ÒäîÖ&TQ§ •G§ãÁŸ·F ŠÑzœï^~/ÔÏjÉ¿w0üÇñ‚C+¯R}¥’T=?ղͭÑåšâšg_ÍßíÍ”2³ÏÌ^¯ŽWH Z{ÇÓq_'ΰ/ÏÏG€‰“ûõ!MJɲDBoå3M¶ÉJAÄI¶:á‘úO!O­4aÓ ŸþópQúH¹ßQßR?q¢ 7'cU¢>¦ûÔçî.ʸ$=•6’î4Ö5/îód]ò­q5¬o·7ÓágzÿŽHQ‚Äg<3Öž¨ &Nµ¡Ò²×” MBíÏiBJl"Mö#ª§ÖD <ÙÄO}G¢ô¡2!¥"…\y"EŸéº²®IÉ‹—ÜV”vñ8àqê¶Ã”cêG`Éúî°çNQÄð䢵׿|¤Óèo¨|·U^O¹+»æ•–ü~iw°Ó¹Ö¨ytƒ<\e=+>{ËçÒ_Ü*ÐoWíÂ{3ÕŽ _mÕ0qrÏî¥I)}Y)“S:WÞæSÐ%Ú‘&JD–”„§­F2=¼%ÔJ¢>¤DäˆHõ§r¤sºç –&TÓ&<£ï7·Lï½á³W’ž>p&ùDa~]HðÞLu!Ã×[3LœÜ»wi"M“S:*nê3…0)¤Iq)hMÖ&l&äÒ,Ôw$J?*äI!PÊgå~åÓž÷¿¢?SÏë;Ò¸­ŒSÏìÖÚá˜Êú*oý@IDATŒáe• žõüÃs7 B"~wÑ×Wë›kžtñ†¶uEÍËÖû¨%SÙ‚ØWZamߥÇï®ãî^¯Øzxzßu!Ã{3Õ… _o­0qrÿž¥Ib òDÖ "J”+“B˜”#ÞjBA¸ýŒ#À0Œ€!ðù矫¢m9JU%>9j•(ª¼ËÂ͉‰Іâ­^j‹š‡Ö£íàåu{]QóP‚ÿŸóÄüÕ}Þ>tð‘¾ùêhÕLëq{ÏeÉ7™÷%=—òL¯£x˜W•è #ÀT!Àĉ‡#À0Œ#À´8O%¾X¥=$«ÐC’Ä,¶Z=BÑ*ªR ax-XÁïçýG(ªl¥Tò!¦ÌYhU `ÄçKð’oJ2ÀÇ\¾¦bH˜;ðA’R%•ê´Füvâ£%JžrlLÔ2Œ@M˜8ÕÄ„¯0Œ#À0Œ€HL”Té°¸?X¥Ñ’£q]ΨRsIXe•¸ÂF¤_ƒÅßÇ öõöRùôàS•D R®ÉÁçpSCüÏ*JPa2«+L&¿r“Õ¯¼ÂÆòö_PéÅF,â2 ñ ¢,Rž8mþ¢Ó¢UÚWö©UªßÚ©/Ù™˜8ή»R÷ù¿±QóHóSVKâìØ¥žÍÇgô>C×µo‹Éxú1}¦õNIoº¹Bú?ަk,Œ#P;LœjÇ…¯2Œ#À0Œ€ ²”iY8÷™¸/òø´ ùQñA¾ÖîÑíÔÑíƒ!<ÈÚûCh¯ Q«µŽª¾ ‰T~Q)dæ"‘ÊÎW¥fçu9—™“[Xz³E´ ™Òö’„9 ×#_ûÑ ñýßk‰Ó«lYŽÒ éå4Õ5/ez¯Ó‰’4ò³åŸ%ËŸhiz!噸•½ÞJY+”™_ûÝ o÷*Æé\©AZ£ îÝYN˜A}2íª< h¦µ\ ŠšWŸ~´n ï¿Á ÖšÀü’Ùª5eõåâ{Œ#`'{4øœ`F€`š…À#‰KÚ‹FË/XHìý“/…á}ºº=aªÞ`½N“Ç„ý»«V~¹)8-;ÿ{ (1éÍYoÿìã+VèM9Å£$«4 ƒV Crr‰¥Ìj r刾>ÞB€—àçc/Ðk5`±Š`¶b  &:ÇcIY¹”[4&ߥÚe¹ÅV—º¬À$y' ňÇ ›Þ˜=û¼ífN(ZŸ.ŸøÆqÝ‘&äç,Œ@[F€‰S[î}n;#À0Œ#à@7i2¬Û×étÚžÝz¹ª[t;–îú¢Býà©»&¨–®ùQ:Ÿ[¸&1ñƒ˜Þ½}Ì?82­FwšÏÝ"Š’/®;’‚ü¤N‘¡ªŽ‘¡Ð)" :´ ­F£j„ÖD0… “ÎçAv^1dãñTZvà‘3Ya¤Àk© M›¿ð°$J?K‚úÓU fÿшòmVYŸ*lø„`„§ÁÄ1Œ#À0­K—ú”–šºZD©»Jº¡5¢º†õÀÉu˜ ôî[‡×4˜Ô¯@#„—„"ôüʰZÅT ž.i™È24Å"‘pÓeƒ1ðƒg“&¥·) ú†«_ýè§°t1í«ÌƒÂP Ñâ¥×b‹ŽªA½:B—á pˆe¬]ÚËIѰ?“‘ GÎd GNgÄ;—ÙÓl¶>9uþ¢tñ[ýšU‰3ÈÕ…`œˆ''‚ËE3Œ#À0î†@BâJosÆW£{Ù5…åEdèN&†¨p%À=Ž€öM²?ÒJeå…%eј†•Å’2Å{Ã$ALûë¨þ=Ü#úƒÒ°f;G†N«‘ÀLèÛ=Z5$®3Äwà j×4‰*®CK¦ —Ä«È*µëÐIؾÿX“©ç— –ò—1œ:íÏôÚ»‹æîhfs9;#ÀÔ§:€áËŒ#À0Œ@kA`ZâÒpÑZq'Ò k$Ëù1È´´þ&¶S„ª+Z†Âƒü ÷PBw3"q/#ˆTVQ„¢’2@"Ee©qžßªD§UCâ´T>^:Ðë¶½T“1"«ÔÈþ=(©2r ( vçã7”–UÜ2uÞÂo%µêùU‰³Sš\gdZ`âT+,|‘`F€ð|¦Î]Ò]”LO‹ÖòÑ¥KÑÎß×¥ƒºwåJ*²$9B¨œ 99¢/˸!±ª±ƒb6zeiP0‹[® — í­ú߯¿ô{RÎ,ɰ.¾nZâ‹w¿“8ëtÛ@[É8ÇØè£—Ê0Œ#À0 D aþ¢q&±$C4<0qd_xù‰[4“.íϤ©øµ¶ÇB|!á¦qƒ×]ŠëÖÔÃ%‹¸£ð]ÞÚÚÉía\‰'W¢Íu1Œ#À0F€6aňj¯ã&¬¿„úD>wÿ5mÞªA=F`h|W˜÷ðduDh€¿h•ÖO»à!F…`š†§¦áƹF€`G€H“)»ˆÂP?~ŰÞ¼‡¯SQÈjFÀÜÈ÷¹&©ûtDIxwʼ…ØßçsF€hLœ†?Å0Œ#À¸2i:_ô(Á„{&nÂÈjler«.r+e(¤ú´›/S êÝ p¯7§Í[t«[)ÈÊ0€'è¤P‘và ±¡$ò÷pI (ÉUuV«Gi3[ÙN$Ø¢Ö%-7NE§U !uÉßFµzxœ¶®±ÜèÖ$&~®CKÓW8eÒ4²_÷F—ÁÚ6×< =;µq/¯§Í{q@ÛC[Ì4Žª×tìZSNš€ =›8XP .t•!HàçêF~_’n«rÊìÈM¶.:A*Aæ–œ1WTü¼úµwcÕ8E–Ò:¼ÃÒR¸Í8ÝfN- ËDÉ$ùð8µë‹¶r:mîâIVÉzϤÑýIS[éuǶÓृû®¥}ý“ ñ¹œ'°ôW[CÛ+¬ÀÙ–S]E•%]b¬’~;8[ÀÑþ HZDE…“ù7K„2|Qm”$(TP’­’¤lQ-¤ëUšc+^x6M0'‹[!`7Mmš^ sl¦œ«ÎKGA@õàsó'hÔêÿH‚*2¦ø¬Ô5ïˆS| ô– i„³­Ðèá¬_g8ÜS:ëƒ_]bFqQÑ#Ÿ¾ùʬ“ÞÌ)ÊY*p¹•TŽS-ŽSI®=.Ekö í4G@'”·yŒL’dYzÂ9K?)ÛÜMÇi!SûÑÚ~£hŸ&³X|´}H`Øì‡&©ÈzÀÂ47ÿo£õÐÉ´ÜuTÇÄÄøKµ@þãŃÄëUÈlƃ ÆïßÞ¸Ôà"ƒ„V«±tÉ ÓV«F$zöȵ˜L©Âl&c¹Y-IâEsr• ”aÙ‡‘xíÀ;¼µÞë_M|:¯*ò£N@à¢vBù\¤û!@˜ª‡gÿó)AP- *Ï‘FžÛ¥éýÁºŸÚ®ÕˆÈc÷üJB†O$lë06\ ùß=O??Íò—V 6&LLl}rN×\4NýTYR_ýZ@ëS;¼‰ï¢qÚA›,õ÷úJ¥xœÖ%’Jí³R½¯üF)âxœÖ–‡Þ›:ï¥~¢hI˜pI¼â¡­`µÝ 1íÀÇKgA‹Ç$Ô‹‰S=CÖ^“¥h•(Zoóóõ¶Žì×MÝ¿gGˆn‚¦¤z26ð–A”Ú‡`Ž(%— I’útz.$ŸHÕìØlB^Qé5Sç.ü8B3òþÄÄqr̲†î»}Ñý¯7ºÿõF}bÑú”fNÕÓ,…Þô’ÐÛ^úlÄ+ s• *)M´Â¼zB-H{Djw$tOJL¼UÉB9Xì`âdF8UÝ7cöU*•úe"M—ŸþÑêm5l"‘K¯NWyzáú¦û惕?VµžfôäºÇâlã”HÓ`Ãÿñ8m ®D.+ñºMJõãqÚ@Ü<å1I²LWã솈“§èÌzº7ä:ª:r:#Þ½5uœvŸþ¹zÓSÝÐI.]£Ñ‚ÓKo’¼IƒÄC.rtª+ÁuI¹‚¹è^—g‹P©„Q7Œ —é_×üRuŽ •Ó5—ö×ü¶÷|¶þ÷;ÒÍÛƒ‘@ Ö£c1D¢!¤÷ÒZ£BUíB?o=x¼À׳yé4`E&eÅ0œV«å&3}ò‹K{»¦ç^V^aRƒY„tá¨ËÝ.ðÚµ+>{Ìqè{~ILœ<¿ÚÕÕWßå£×éß%÷<²454#?wÄMÈÓIÐ>âÕ˜˜œ={4ïâw¬œÈòÄÒ<*Ç©^ÿ®Ÿ:K¶45¯¸¶™-tB‘5L‚§­eÐÛî KÑý{Ĩ¼ñ­4 #à(| *Œ¤é¨òÜ­œÄÄMšt˶ËP¯Ñ‚ ùËÁ£C1»7éYù£-I>­U£FG6\XDkTȘ*Ìf\{dRYATÉû¤àó÷N¾†÷éJY[DȲ5z`O8~6KõgòɫÃýŸ.QªØÎ‘Ð)"ü} v ´k€œ'· ÎdäaÊÑ&8wiZvÁX *–O·è®¿z;ÐO·šÝûQÛǧ­z-¢ëü8‚ˆ 5Mìž×´Î&ÜF¥nV}ßý¦vÃ'Nœzö£¯bIôÝ«¸ë‰biÆ)¨"hM»ç5 H­¯×÷ªmƇxœ6 B·ËUn-žHoÄ/áÐãn×7ž®'ÀØmežÞŽêúOM\8X´÷fZw܉‘ëBp½‘Ý.-7aªN‘¡•k‘ü¼!ÀÏ›Ö Ö9®@ëL ®?ÑZä_½šù|螺pËCÁÏÇËa/ÁCý€ÒÀ^á†Ë©Ñ%;¿ï?{:#çÍÂó’„9 ß7x _Ÿ3'«Eî•Ö9PÜ@7VÁqЖF§Ó>NÑó8Dó€¥5O„ã™Àȱ¤U˜ÈmX Á.{M‡×6N)z‚h:”“Ö<ŽR`'§ÍƒÒ-rãB‡+½p-J\—HþÝv‹i=JœËηZ%±Õ¸cÑZ@,/‰fé*Œdgíß=Z=8® ÄuTa$áFwœ^§Jî$ü09S‚1ÀÅØA±”Tg3raóîï?’NÖ`”˜os ÷wã°«÷iÄSPk5áø…¸"W=zµÅ#MÛ8õ Ã&XWÄ“Çi]àxÈõôˆ£•–¦pÓ ô÷†yS®ƒÛ¯yE%ðýÖ½¡†!›/úÜÔc;«ÏÜ hùhjõæÃÀðÃoû¡¨¤ñ1,V+¼óå¯PPl¬·OºYb,‡w¿ÙlÅøq9ÞZŸç=zËåjC%ãK®¨ÓêpÎ7”;´Œu è’Mñ¾zÑì˜_8*™OµÊ‡Bš*¤IqÕ#œã¿Ò6p¾hœjSö;ቛ×ó8u ¦®.jÕÔ©´¯J† &·`—ŠF¥†¨ð 91²Ø?ÿe•{cÒz«Ã:$$¸¦ÆàBr •üËŸ‡`ÛÞ£0÷áÉ@„…ä•5?AÜ‹fÜ^rGÎd€?~}R¯I—ö‡ýGÏÁçþ€ýãF9Y¶>ýéw œ è¢×½»DÂêï·Ãa´þÐ:ýºG× /ýß/ÜÇ͆î…_oÚ ÷];ªV wÀ¶l;D*¨®»®¾hþŸm”­M¯~²4*\si?øùd˜?åz .ûåÆ¿`oÊxöþ‰àë ›v†?“NÂs÷_™¹…ðé;á4¶##¸MÝ÷î,×ÿ ^ï€QÞr Jà7ÄëÑÛ.¯¡×gëÿ€ôì|ùž£”b?®øôg17¿Ô" Âä×§WškÔîþ,æü‚ zM»i<î]äïÜ5ÇiÝ>ówQãßΉ‰<‘˜ø@¹ãJwÏ’øÍ®{ö‹#µ¢>Vã4ž'£ŽDÕV–D¸Ò "M”øo Ah‚ð8mh ÏÂã´áX¹ç“ø&æx^‘±E_ȉ!²¤×Vr'Û¾ï(Ü4~Ä´ÿCâCîl}ºu]úªôþÊÌ)„cg3¡g§ذ3 Îã~1ß~àZˆ «ôð1áž9¹…%øS…›tâo"Y)1V y£ô€ˆÐyq?-RŸzÓX¸ÉÏ_ɧ`OÊéuézËøÁ@äèTÚùyp3PøðûmXWw˜vËe†de+’ S Câ*‰Î èvÿäQØÖP µHiè®H²çði° Î¸‰¬ü9ùxª¶š6¥v”!&÷\3ºt‡ÿ|½ÈEޤ¨´\¶à%ŸL•ÉXûùºòß:\C†QÌàÄÀQ¤‰ˆÜ‹¬µ¦få™%Ü´jÁì?”ú<í8-qi8ŽÇŒè×]‹c‹¥eèN.»¾ç­Y][F×ÖÊ“<×âíêÚhR/'üÑ #‹ƒÀßbÂU!M6¼\Mk/ΆSçt5SçàêÒR%ø ׿X‰T¸RŠÑúòþ7[aþ¿ÿ'“Ž«Fô‘­=Š“Ç „A½:Áux$ÝŽœÉÜ”º"I BA²çÈÚ˜zá&d)ÊC‚”[P Cã»Â€ØùûÿާfË$êf$:ôÌeh¥RöÏ?,N.›Ö{‘%,ùDš}Ö:ωØôïÙh‚M¯ºD²Cá—Ée0Ýã‘ü‘‰,f´÷ m0Ú=¦=Z‰‚À×Û ŽžÍ‚ì¼"yÚ5åt¦,ƒôïÕ9ŽŸË†$‰D,ÉÊDQɼ½ô°É›"å˜yïD¸|hoð©Úؘ¬XDÉú5íæË4*7ëH–°Eï}oÍ/,)À=ǬúלšU` g¶ZLw`t7ý„Kâ[X“¶]}’q³Z“Õ`W½ÖßËʤ´õ·´åZH/ œéÈÒxüŸ“s4§ EÊ ŸS ÒÇeå¦iB=÷¤qH2y?,úvï~>UÝ-@$¾>´Ô -G$#úuƒ/Ùw#aÙ‡d(ZmÈZ5qd?´ÀX`ÕW›dr÷Ä‘@›‘ÚK>n¼IÒ‰Š½–ᓇ©Y¨‡7ämdÃþ¹ÚÎÉâDrÛ•Ã`Á¾…íû‘»íÑÃh+ÄõO/¾oÇ%.ܶ=§œ;q. #Òº †A÷Žíá 4q.+O¶ºõê GÐÊFO‘h$]Dé†oë«[“§5?ìÝ;ØåUò4öxõüòç]ÖÓçÉm½—Axðõ9³=~’‹{›Å{{{Y‘TÓËK–@€,˜èš+ *Õg«gä´€ .¯’-N.‡¼E*¬çë¿Eôim•¾Jjmmse{xœ:meŒ2ÎÎÅÙ)¥¿³pÞ¯hý8±uÏ‘Jà”Zjêçm×Ñڥ꤉ž®+Ü3YXÐ<&Ÿ„³™¹Hœ*½x¼ÑªBnkp‚öÕ!×µê¢X^²Ðšc/¿üqÎaY ¹×O]×$KLDX ®ÅêßàZ'²B)â¥×AZœ–?sÇ…ôôÊíÇX´(ÍÌCÄ,ˆEKZ÷èv2ñÚwä,ÐZ ªä'çË´kGV^1®í2ØÊÓikÎùEQ‚û&Œ‡j»íÙ¦œÐz³e«Ä5Vçi°ŸÔÝC¯Ï™ãñ¤IÆBBýª¬tMÁ†ó4zÙ°lõOd/P©uÓ›Wšçäfâä9}Õ&4õŠêí®½ÚMº T†‹ßl:A£Aó÷ ‹ë®›] ëÆ¦õÞñ ê $@§ƒFçüÑj­7¾­¯9Áj8‰q£ßĉº@w²¢ Œík1p­Ý¡µI$¥Êëƒ"‘Àôìj:¾G¯ÓÀú +·h‚†krÐÂTŠ.Þèî§‚C'ÓdËSSp¸vt? rràØ9[v ¯LnuD4*L9úž‚3¹’è(Z(@Y¯z¡Å‰6=‘– ½‘8‘¾QïßÑ ¯wçÊÈnd]¢`´®+ Ó¯ÆÈ|¥Ð-w'ý{ÆÀƒ×–u¤ÀMÂ’ÊBA ê}•ÅR‘†F6M›»` mÛÔrÝ$ß1\/'P_²¸rO]³v;‘0PÌ!A£ôNâÌl×iв51qjYü]{³Þ,GÞýÄ¿õmƒuôë3´Yd‡ˆR¯eŸoï  ±¢ñacª¬¡Sèñ¯ÿÀ€·C¿÷NO-•Þa¿!ͽ¡mhEÏ5 ¯#gÁ˜û¶5Žè‘Í";D”Fݹ‚¢†ƒÁ?,fç…(Žè>Fßû\1-.O8&¾ D¢$ÍÂÝA:p1@Àß_ÿ®J­:úŸ¯¶ˆÅXÀÝå’~Ýq-S1 Åõ?Š<~^úïZxì¥5t"£å VnÙŽdqºÿÚÑpƒ8¼ðÎWr€"5cÇÊïž\ú|·eŒgËÓ˜ZgtÝØmÞÛ#ôѵïpo**ÿ¹ŸË$Ê%}ÆbÄÀÏ7ü 3^ûLvÅ ôƒPL‹ÕF É}/ÉY£H(´:î+$,Z#öͦ]pͨ~ЯGÍu]r†jÿuŠ ƒ‰øü?ÿ!Èj·ô‘È­“BKš†¬|×\Ú£Ï\j•`UY¾1-aî¢f,]êü·” Ò¶ÑmDk‡jçcÎØ” Ôƒö5# jC„È2i ¡ú´nÏ‘Bkq½£ôÂ;_KÛ÷Ç·qÒ C ×°wgvd=î^VÍW=î®1ë×Xhr„ɹoœÉrÓuæ2H™u”§j¬Žòóþý†ÉÖŸS¯Ï±Üy¤‰*3Du²s'àìÊ… ñ „nsß„’Ñ»!ç篚¢{Æì®×ðªò¸dœ¢;L>v~>,¦ ´£whÌ¥ ¨´°ýc`u"i"ÌåùpjÏ¿!ëäðö†!×1qwÁ©}ï6FeåY§ z\6sfé£ó^º±ØX¶ûýo·êž¸ã 4„P·:Gn¸lPªMh•ýZ+r»{gÎý=J®z$ê¢ÒÑùâ›Eß³#^½< A‰"ëy¡5‡Â‚“,|ô¦yézõütM‘ÚÚ@®‡”ìåê‘}‘Rƒ—Vv%Tîß6aXº'^ÐE¹OG 5NÉ^(Ü8j/Yáì»kÚÍãì•Ï«·ƒBµSr„(aå±<õ `ñ럇w>óBq‘ijÂÜŬZ8ûGÔãª2V-œ·aÚüE¿½yÏ <¢ưõî$?ü¶ÂÐZI¸»Z’O¤Ã7›wË.U7‘±Ôì|4\bøÇ úìÞ ik^±¬rápÈØIÐþ†A ÆS)pbÙL°‚|ý&t¡ò‚¢ý¿ÃÙU‹ÁZR(—­ü—·ý' ¤Héñdðí5 ©ÄI)†-nèAÖ§”ßþ]?^¾‘yü8ºóe|»lÁ“WËÖ¦¡× ¢d†-ÿ*ôº4Úw½×dTÀ™ýï#YyGnIܸ¡è|b°Ü Ç~_û?,ßu×FH;ôœÜýÄ_¡Fá8µBöépdû"$f• À£bo†.ƒ½O¸\ÖÞu Hˆ €®wúè !sf+$ož%_·‡0çÜoç*¯˜Œ9PaÌ×Å!‹íŸ¯ëÜOÛkáÃÂZrK/pRˆ»6ã·?þY·™/·ñº—…VâÏ3 ΚûÁžS7ÉëƒhO¢ºÖµTç’Õåtz|ýënÜ·©7u¦ºØ“¦ê÷ì?ûzW°¿Öмöysîçƒ µHspv¶Îµ¨[ï%ŠzˆI¸"ý<헡濞2gA½yÜñ&YAŒe&y×w^)»…ºJOÚ‡‹Èí³•„®£]ѺG{|‘¥BÉÿuè”L–wî?·O.»hÖ¶W­û£}Ìvì? —ˆ•£>R –›1ãÜ·þ‡Ç¡ðãŽý²«ê t½÷43àš<³Å"[A÷bÔJz±pþ­]9<]X³1ˆÊNÙ:;çÍ/!®k”ü²¢¹¸ñŸŸpŠÜUOá¸9‘z^ûgò©ñ9ùE6<–>uîâ)+Î^×ÜzÜ=›øÙt÷Nðýta‘“0ò~[韽 ¡HxF¢dümëåf¤}¼N¿ñ‚|vÞ0 μ5Ò?¢îyôí*}»µHj"n›ýF@Æ—ïBÑáÝPðǯø C’óçmý$‹ ÊN¤ÀÉåÏÁé·^€à‘ høårÙ>=ú"©ú'”ž: §ß| wo“I“68L¾ž»ñk8¹ä0Ät“‰Y}«}ýÁ»SO(=²¿¾Çøž‡ `ðëñã^†ô#_!ÑYÑñwBx—+@Äqšq´ÒõôÈÎáÀ†ér‹bè~§ËàÀƧàø¯@Ï‘³Á; £|HM÷a3 ,f,ÿó5ÈMÿ²Nüˆ÷p}æ§:D« гÂÞ§aÓ!¢Çd$aåüíAŸ+–#a:ñÞùÓ¿ÊäHïÛN¾žšô)ìùáað …h$fµ ¹¶ïv5Ä_¾T~vø‹Úãkmí~èëõ†Ë>/}°V^?ãNͧ}’~øm Àµ5´÷‹û"@“üçîŸæÄÞ"æ¾Z_¬™H/V1ââ‚w¿‘]-Éºç ¡}¸(È­o»žÐ ƒ0‘ë'¹xÀø´÷WgÜü¹®½ÂèyÚÇŒÖò}òã ïßÑfÍ¥½Í>þq; ðüòŸÖ»É"Ùºçºã¥Én­×Ž_aKZwà­1ƒPÝ´é´#…ˆ^\×0yÌ Ôr£zƽW“»j{ ¿vê¼…KY—;–Å'wì7Ö©àÏM½öYÃq×ÿK oë:0ž>"_3ž8lsÕó8J¶•§ÀD.|D|2¿ýP~Vmð†¤Ç¯C R告Œ³2q*9¼Ç†@ÖÚä×âz\óTvæøÖ¨ï xô5`--†Ó¯Ï•ó(°NSN¦l £k…{·A ÕùÍ•Gj;N›æ<Üoã—ÊIuø‚Ç!yò'8½ï=Yï¨Þ·ChÌHOù L²|­0ë”æWºê…vº²NmÄÏ'«Òq™øœD9ζ¬¹¬¦JK§±º¢"ÁÏOÿC¾OÿÉ®shíòF’SœsB;…ÔßCdì`©(B·¾'ð© ðŽ—AyI:dž¨|9—}æWh‡dëäî·me*'QXF×!˜Ÿ§o ­Nç•[|lÃtÑýþê,Ø•w¼øÞ·øzŒêßÃæÒÖ’ÐоN”X<Ú[‹¬~íïë_wy†ÒÕ´”D ×*Yz(‘AÙ‘‚cО[ÎZ/w×Ärñ9¸7Ù·›÷Ðφږ]=ƒ‘ÄÐÞ_$Ûô({…Ñge¯0ÚÄd¶Â × ²‘& BBBûÑ>_$Fÿ0Z·hß°$\/H–©v!~rj›DïI9.é#GˆLÏ)°Õ-gvÒÝ0¢äÌû®Q}¿u/á?cÚ¼EÉï,˜ó_'U×âÅ2qjñ.ð,Œ'Ù¶å×PAPkÀ¯÷ ðíÙ-EWÚòè1jž"Å)ûl¤I¹f$¼è)³°ŒþHlΣ{`8Xª\ît¡í*ÉZÕ‹’ϯïpÙu/öå5Ê¥‹ˆÕ…‹•gdóéGÐ7_²¸æ-Uuø³ã(Ê®|ëG%›ÊrA­©= ‚ Ò@yj?"{\kSÄ;¸«íGÿå%ézã+Œˆ©¢ÁÓŸY9N—0Àe#+$–·zËî‚"d…òTâT NºÕ¦^t8Ô[‡4OE_ª»M&œªa?«ßÌÎöjD$¾§Àz>´t¡H|çå˨œœã­ÈÐy 9­§ØôÀh·^YÁ³‚ ºL !^W4YéÂy4Ylºÿê¢IÅî[޾C4×l»cœ›KU,GÔY §wÆE”é&DB÷lz Üœé%Ž×t–=ç;Aç©a‹¾bù[ç¸õšÎ¼þ£‚zœ9ÇD£æy"0‘¸p9Óܧåÿ;×3ºÔke:EêÁõâÈÞåàžyW¦ÔÊ8Á¯˜Er;FDO;+++÷?×¹»¾FïÎmô0÷ÝŽXɯFÀÆ\|†X¿c¯JÏÊyùOî&yS¸ä™;q+—ÇÇGÅý.…œ£„jJÆ>JÛ˜ªj—ILÏÛ`ù #'¥{¾Ïô’tët(¨’A"K2~âY¾&¯OïušÜð×¾F‰bzw´»­&Û®©¶XÇ©¦öóv²IÌ; `J\ƒò:^êã«–‰¦ç^&ô*ˆÜ¡›lÕÞk$È…óØÔkˆ††‰°ž'- ¥mø]ØÁ"ŸOÍZCtïr·1ŠÔUËE@‹v¢Å•·-(XØÂ›*g5Þì‚kD[˜[OZ˜ ô A:Stpðo2ŽïÒMÚž)ìAØŒ„xÝÁ߀Ût­hÚîlÜ‚SÚ´'tœ:y DPƒ6"'㦿 DÍpѸÍЂ²G÷þ,ÃZ¹}>5hëÝ#c‡`%t—: ¼Çm^< ´¹›cUP0ÿ‚ HÏ([L£î§ë$×·h~¾¯;œùòކŽ|ëT¾èu|Ì”_çNr±M³ ÉÎu½‰ÅË>(ý»Íy?õ҇ƲÕ[}ÑL•ë ‘ªí»“àßéO±fëî*×Çø20bH/ ñ^1>6¶­ok¯þÚ&DO;›šB}Ьqƒð›/&ž{äF}äßêùx3ò_¶FHç*'× 3ô¥û 󦾢yH‡ð÷Íÿ¸ß'â@‘ésÒq¢@m“+ò}–c5ì æÜx–~¼'•ý–o kv‡¾Y(ÚÝý„hïD±þî Ýø:ŽÝ¢¡·BŠt’vÎzBä$Óiùá0êëô€C |ëg‘ñÏAúNá•òËW tú‰6·Ü/ÚÞñˆ ±AÒ¿J]ý“H|Žh3f‚h÷ïÇÝÄ\òço‹tˆz†æ—Žq댴»ûIÏh±öVd.ˆŸÝ5»=ß½†ÿGô+¾Ÿ?@lûeªè{Á,˜ú†î)ÒIZ»d¬ÈLÝíÕè÷nxKô‡ˆ‹ák)õбkÝ|šOx%n_$µ,ºŸ=Qô87 bƒ)0.ñµ8ª¿>ë6<Ñó™ÊÿÿücóNñî’_Å9ºŠŒPß-èÈTòÖ]I>5¿ì Ø6{Z7+dBÝ3ÝŸ®É Á?ý)UŽ6ã*˜›µyŒÇ‚€Dç_³\¦ñôˆ\×^8D éuš~*]œnm^<òÜ»âŽ+Ïgï™å+lá·¸ÅùÈ éU&^ÚÛ_®/¾·ÔÍQ  pûìjÞ¸¡›HûåÏbò+ ݆ÈŸXM„¿ö$ ¨Ìe_د󖄚hð´©Äªÿ–¯6µ 4‘þÁ8ÞõdÔgr ¹òïOªµ[yNdœ×žo’6»ÐC„+íx™úF¥uŒãZ&È‹åÑtøajä&œðÏQ(ÙÖ¨ ˆµt·u¾B >ºù¢ëuâÑpë;/=[/uG|#UShž¶ Nrnè›>¬¾xUº=Øý¡1]ódöÀ nŽ¡@á¹T¼†â1ä×2A^4UJ]؃ÃEnfJ±ºBš‚XKs[ç+Zκ'Ž“+7ͺ­Ðù—Œ»Å¾Ô¦õvž–ö"¢+—ñÛï·Ò´çsW€Œš¶õÉ~_ºÓaµ]ú=›Ÿèó åñ–pêù†ó…)^!¾í„'áÔóùM¯(Síq6Ë]߉Ì5"øì Ov=TV9«ÞœÇ>{½®Ä\K͇ôî$¯1HÐbªºBE§×?].²ssÅ7^äÓî¥1ò[ó̸k|Z/UFDÙ³?“`}ŽðëC¸Æÿˆ98#!&ª€è¯­ã~ô…‚3S2¿@G^tfoIæºIŒ­6Z–deç²DçJòæ™^‘k2Éž•“ +~AdC¨P ÓáDP•¤sU(£nˆÛõŸøE&ºŸyŸª¬•UØje¯¸Su3§¸n ] ŽPeC©DU1>W*-F‹‡Òâ‹çä˜ú†@I:P¤GD¡Ê†Òˆ&ªO)DÙd(J‹÷ÌYY¢É³¾.Š€ü17âpN}ŸßØÞeŠ! û.-È©´' i¼vz¼¼&Bzm=&À!¹¸Q¹*s V,­ êÃ|Ò]c·Ë!·&¢K*©¯Vû e•£üå…3Û8 ç\ÓPW·kÝÔ„£Yé©X^^y_¤³|Í6·µ/²nFz#†ôt;›%SÍväI™q×+°+NÈŽOw›Z¾ÿÆ ‰û,ÀŽü¾ƒ)¢%ˆž îÄÇÜÝÛ¸c¿XúÛ&RBíQŽÖ’HQ¿5$¢E>p"àd–Ì4J9ávDûúbXA;Kôîœ'…öÆ¢ŸD£° ·¯j€üì|½rƒHMÏZ6w]}žxó³ŸÝâV³ßûFØ =÷À¿Ä›(GÆÆ\|¦»_lúâ‡ëÅÔ ×¹7eж=íÁDò‘TñÎ’•×QÑãºùÒ³ÜVßÜÔ²Ÿ €:sᵬ[%v'#%sFÞsÍyÒÓ)s‰™k8’™¢Du¡4_a•éž®k˜‹A%-©í3V1Òå2ÄÿB–á3„-ðé*VW«‹³ŽS­~<Ü9F€`ê@ÊÕ àù_Ž|=#—c”ŸzHÁ¿A~s}fÚæG+ÒâÑQt›bs´Žj²é‘~­úˆû„8·ògi嬼e#žŽ»Ði¸6Âæ•7\t¦˜t×UZMMÔ?òG³æ’Û¸ÓMŒtlÝÔíГô<úwm/Z7 w ä_¦e“F‚|áãÍ;÷»‡’nFC˜ÅÔ³“xþa@cá·«D&ví‰òÞ׿Bd®©xøæ‹Aœu€Ïš¥ú­!8s>ü ×qD·È@Å‘Ùg+œ€ÿÚý§@Êþ÷ù/¢C«fðsžè SÏÔ—¡}Ns§“O*ê7…ÔŒ,8=)j˜…ñ¥¤æ¹, ô¢mS‰UÙÅ÷\"Ú6o 1®_)ºV†\—¡)©N¨VöRˆ Q3{à¹Þ“ÞµŽhª¥ù¼[¤_•ðÉ2µót}…¼-Áñ”Óý70áä¿ÏÖÉá8)Ço%ðÙgäcì–ï¢kG€çiÅ1«h ž§ˆm~¬ÏßBФfo<¢κԈ Urõ\Ýf+e>Üë¹- *yÑwî–P=¹ÚÆ&e†’Z•vöÇFÅM'kiÛæááO½Z¿ðŒ^ÅDv*ÙåJ#ñÀÁð¥CN;I”hûždѦ›C 6D&•É·MP`žþxvŽ DÒån_5ä44¼a®{A¼°›Ø¢…Ù¶]ÉP•‰‰°'騗iÔ™½Á¹i „ºýÖØuw½DœYá4øñwÝH1>}Ê lÚékºûêáðÓAþ íæg‹é8Y>yÊ«‹Ò=Û&ýp |îÀ Ö¿«Ø—œâVà÷¦®šÌC}u:]öÔÔd»•iK‰ÜáTî¼AdÄCM#@såÕ¾3ÁI¦M¡{çÇNY\Ó}¨éöXT¯¦¯Ùöh¡ä†22s4;Ýç}yòãùTyrµåÊu—Mt¯Á*_o=,Yhž:UÏSN§ T.ÏÓ’ èCl‘Žé9{ëa¸:ôiß{YQ›…Äêõܦ8%ŒWÒmÎ[KªÇÛ¸Í÷÷I‡îT@¡üÊl¦W¥e™ÇEÆÎa÷È9X8ÞtÉ™µF·ƒ8MÂBót;rÀ‰*-tmß~§Ná"q¼¯V¬!^G„\\Pˆ¸~¤x÷«•bêüE0HÑÙ-vçYÖÉã§o×<‘<¨B—¸XVH9‘ñ¼&•":IF©hðl{ûîd7Ç‹8ZV ç©ûMÃóÆhÅŸêóßûò¢6›úýT÷¥¼öM%sé“{¢•M…•—Ó}ˆÀž¤#bÞÇËŒãi醦icãc"ßòaõµ¶*&œjí£ñmÇL§q(30Œ¤>„5êœiUPÞòa_ü¥*š§ÙAxžúðf«†Ê™“Vé…¹»R몲hºœÆ2i›áï壅cäI.ÞÞøxŸyï?¿éN-Åu…YÕ­'%IŸjÓãýÜÊ>˜ì]`̱°9E¶Ëº«yæýWž7P\9|`YYk<­$ß3¥u‚8EV ezÒoêuZkñä¿/w‹ÑÁœº•,:µi&¦Ü=N?wB_èWÑø—ø±q«Œäñ¼°ƒeh,îÜ—$Nh2áÜ´1AäA¹~kr’•Tæ™üèxƒD žmæõã‘[.ò‰Ê¢ùkËý®à„ž0Z7' µ:‡È¯³²Dî×+6Ø'Üpl Tõ­Õí#±ÛÅË׊WmSx ’…®ŽwD®­«N°¨^ €\ šÀÂ)'1Û"sl…-»Ô‚¾ÕÉ.Ž„gVfÉòÒBß:êäxjI§Ýó4W…Ê\U²¢k-égéáHxò<-ù‘m~¸Ï^©d²©T¬çÓó,áô’šˆ§w½òAwr7UpúsÛ›A¯êr=H~^Ñ a-ð~"šF\ëˆ¦ŠŽÅ3?q–NddBü.ºH.‚ØXLºI«¶ì‚nQ¶[¿ f§E ŒOPðÆo ‰¶kÙØí?*5-S|÷û·52«ž§µÇÓ2Ä·¿o†¾R:ô´þN—ËmÈäíMd¥Œt¥ºvh)þÞwH$>îC$­²Bç¶-Ü üKÀI#ÿ:4–MÿÔ>I¸œ\—Xµy§¡„|?!"â$…YÖàNaÚK‘‘¡98DÅþ»øg·ÄSØ¿nšÞÇïÿØ*¢_ûØõêm´É4Ç µõI¨GD=`&œüzš»ç^ÐÜ¿÷\Ƚ Nóÿ×À GÂsߎí¢9’Ñp㜮ø]…æéAW¿à©áÈó´läA¸€¡šlz¼÷вrn{¢ÿÈpS¥-íFö 01 "{«ÓEÎïšÐáK.«í¢iùÔ¿”@IDAT¢§ŸŽn¼tFŸÎârX•ó§@\š‹Ïê'¾E¾Ç_øÀM¤—‰‚+ªK`¹nâËЧ^úÀM\œ÷_AÎEÛ4oâö[CVðJ —œÝ_$NE ÄŸÛ÷ÂÐ tMÖûý¸FLÕ¿¾[E~Ýaæ9PŒ€OžKÿO¼øÛÂÞ0AHdL¬Ÿ;°l"Ú"`E0„™oèÙ·QßïŸÕzí8ÿGÉX “nß¼ÚÑ£ò{1?&òM˜þŸòû¦]*&á3“\¾C€}øõ“^^h,üöw‘ž•»BÚå qQ&Lš”ê»–êFMUúÐKó‘Q7†ï÷½¤ç„ƒ¨ß3éé2“š\úÏgU~î~\9üºËÕjWPËo>{/²ÂABá$•†ƒL4Õ²Ï!zT{C±yÚ*hO“³ƒßâyZÅg¶2ë•”Ù¾^ÏÓÚúêñƶ7ßÝ#ƒ¸YyÌd™oÂ3Ó~krúÆ_£{ŠƒU¤žÚž—Ì—‡Œ3 ÄÒ ßXRš·~krr…ôª<Û ë}¤ôN&ž=%¿hÇèfOÑBâМô¬§´kò¹C!8Ÿ[VZ¾šŽ'ì¦ÌùÈcŸÏ‹º®¦Û¯j{c£§¯Kñ_Ã0:Áܼº`hoé©gVÕúëSy2¿eW"‰Ä;÷†,-xóR}&¤ß^Sj½î[u>«Š½íÕÙ®»º 2±T£½¯µéð@RhÑ:#±ºÚóûz ¿½ :È}Û·}ƒÁÒ—”7Æ8Wh„üò(2O»>pÄÞQ4³ía|*‰ÀWGqÈÙUîÛÉó´’Vk±í÷ôLsÜSñ&"ž™v¹2Ì3®…“O%š˜U/œ²Æí­ßš² Jõ¾’ü씤·UQ¢‰UÛ& è…ß­R •]Ú#­¸ºtž3eù8G|i¹ûî¤Ga¿u³Æ Œ¡½:éý»w„•Ã<ÎeMŒi/,?f8ïѱUM4WndÕ2 &ôI¶QX0|¥ÙÜÀâ¥)[>–&Žà8Žè޽É6Ü/ ”ÚfïØ”ú`nlô¾rª˜pòï‡ìÂiQïúé³OÞ¿vlÄÍ¿´ÑøÚ 5~r¨.Í.€ŸiäæœX±d19ʤ­C:,âɼb×ïÜf'ç鸈›7d_Õxxh¼fóÞïhýFÑcôXúàgNž§°øÇ¥¡þÝ 4Ø€£ÏÒ) ÿ)¢ ÇÅpþ &Ÿ77v"9p®“!Á‘‰ŽÏr8~œd®¸¾µÆBìóü¯Vn´Á'—ѧK[D?OkÓ\´…¾ùðEÈÊÎ{’ŠMïk·í5¡#‡Š¥zà¦Q²o—¶¾h¢ÒuèâKï}êÌÁ‚»êÂØñÑŸ€ËO6e.56rW¥öÓ‚L8ùéƒõíäÓ¢>7%åPÚ_Ö?ÛsðÐéË;ŒRîþŠE¡<€òæ’p;ÜT¬[þÝ[™™ié(C>Yè°ˆ'æ8ydñ<…çéú¼yúgöujHð‡¨Äi"¢i¿³ügˆOyž3%Idzö™/+ÿqË1Îñ\3Ó™ÕˆÃr`|é5ÍYðƒ‘›kœ€8ÖuÇú–ùUxÑñ(ÙŠ/ÿã3:À\â™°¦yÆ#Ç{&Mí"•: ´é„{@$9Á¥Ù¯L±DçS˜kl¶Æ¿ÍuÜO›ªÛgYFÆ[0rÕW+6çì¦÷íÚÎÍí²œ9{ݘéY×ë/ˆåmÞyÀ€¸ Ž~eA-oNHã §g?öXI~'½¨™³x"Pe êb¤, ?j)$E z’Ë£—&cÉ{ÿ{ÿÒ›n·‰.Ýï8®ÎÙ·\c' SJ ¦_Ú/Ž5•¿w­Õ¯_ƒ2Ì?Ø—û•Óé$\ _™ðæPqʘ§}î8a6Sý¿ÐXç©t`I§‰ÄóˆÓôÏæ Ÿý°há×ȃ>ât®¯ó”6öVã¨ó!Øž£g⟦,Ÿ:?H@!@F*^zÿ[3ùè1ChÚõs¦FÕ ¥ÒǤ½™Ž…ž`ßçx¶•ΆS5ºl$ ÕPj2P˜Ê k$NHÞáí“™àú&"01þ™Ç‚x*õ›ŸO°&£éÙO~³rã%  lvŽ–eÛµ–M¹õŽC÷(,$P§‹ôïìù:~du‘Ìß“Ï02DB>”Ò`Ž?úJ©iYnÓöä(:éÈq×±ùkzihº\U½BlÁïÎv<–â9N¾®U×ò)˜L1z¨­Ùk“'³“Ū=ê*Mb8ÈË_#Mq4:ò¢áý†œ9A ï¶WuIÙ.;¤í.’<«ßü4‘ÉñšôPdBËNw¬ùÒvtµIÑЮá"ÌNòÀ2á¯lýÈß‘O'"ž8TÒçéPÌS{`x ûߪ½m½liÛ.$1úêw ?Mdr|Ÿk€"C†3;uÍÏ?½¿þןÉLþ ôŸLÍ£8Èl,Pw—þ&LŸÞØÈp×)¦s§ÕõÑ>øòË9‡R3G é­Ýxñu}8ÜÿSˆ-Â_z©Úuà°KiòêùS#¿:…Ý©M›1£‘–a\]£óÀŒAQ”ç}¹’€p#®¿plǶíMªß6Xõìĉ$ Æ¡¨2áDþ$\¦k5ä#o‰|§úÈUVzδAlhzIÃq4¦#4´a³³/»âªö»\„…i#dTAÎLâL—¦³ÊómÔ©£ÙU¦=L‘s[lôHŒ@ìÙ±ý§_¿YòcÿPW«sZŒì¢Ѥ5ñÔÄS0ìŸØd:è©ÿh¶W,NÜ^§]{:[¡y 3TL…; )¯_Á©T¶j¤È¹mÑyš••ALâ2¡D„$ªBDQ›$û_ê.)Òü&DDÇA¦ß|Û¦Ù†¼3y? ,"*ö÷æMÂùµ¬çäôŒÌª¿òÁwæßûà;V×ÇOúìtƒ›$~«ÎöJíà¦,ÞUh$^¬ w@6ÀñgM¢ì™ ¶ÒðÒ§Ã÷ÜQpÀ’BDP2s“¥š >YpŒŠÙE¸M ±Q—Õl÷¹µ @ÖTèE$â) GCD@ŠæC{<½{‡n=ú‡„5 jd³ÙñòúdŽ þ:àµ>''++5+==eÏ_Û¶ýµqÝntœÄðhÁI Ï´á­BnìrE§0Ûõ!6Ùˆ4,mt[ÿkXJ±b¯œþYÒ’²Øø¨‹CqxžǤXŒ7ó…ˆp"‚‰8ODHѦ¹\oôðÆEÅ~¥„ê;?6º=Æí!"*n¢©ÌÝ~©èÞ¡•_Œ‰Qs>vBÌ]øƒ‘'Àø^Ý Nl!qµšë ·ÄÔM|²(ûÖ‹³4!Ï‹ú©nBQ/zM;”ñDœ'"˜ˆ€"BŠŽ`8(å¥E,|2Wòªªµ¿Ö.<-,-c´Ð$ÙEÒ¡Å'´M»¤cˆùd÷Ðë´ ºP}W(€–ú°½*‚Cÿ;dáNZÄrðž§eãT¡yŠª,NÍåz#J;Üj9ÞÁ'áÌó¹²!­;©OÍœÙàxšsô#FÞs•ŽÍ™ºÓyîé)E`ãßûÅŸýd8s]'„2ÿ5/6ú‡SÚ!nœ¨ƒøä×áø¿ D×þí`N—öægåÛѯƒpø}—éy[;úD …äD4!E„q¤ˆp"Ñ>‹xòÉ0I-ÞûêWcwÒâNþ_[Ûûüm#6#P“øt!<.:ö8Óš }§…ÂÞüNæ<Õ䣬p[Äy²D¢ˆÃDÄ’u¶ˆ&J§9b¸ôë@‹Që ‘&:ˆ8""‰8KÖÙy"¢©Ä°úª6!Ò4nƒ¨ÐdX,“”YR¨…J³% ]œ¸¢X:GXð<µ8y¶æ(«4OOVéWÄiÎÃÿŦŠ0#ühBLÔ‹þ1²â£ I$#ñ Xèºà_£†ÊQgö.ž‰cê-YÙ¹â³ekÅòµÛa_@‚DÄÃP¥ø°ÞÂg|„€O 'êSñ$žÃNß&ìº?Ä:O>zRÕS =kaJD’'ÁdM”NÁçs%¯ÚZõK Q ÉZ”Z”uo¥çå.çwÝU­†JMvD&H,²PÀFà›l»ÏÂýìo¡:îž§…1©–yZ¸‰ºw—§Ó$^AÏûÀRØþL4YOçÑ^Î8šù1ÅËÎêßUÜrÙ0öñdSOÏ.Ã+×ÿ-/_g¤gæÐ‡û•ð†ö§Ù~¾ªæµ8%B‰+Κ#ÖÙWmÖÆz¬)­ƒˆ$ë°â*Õ÷m£{4HSÇnýèqx7ȇC‘ ³!Æ÷±&åüA‹“~Â{cõ§H¾z{kÍIkŽò<­†yZ—fùi23Í+ ²v+ŽKñþìEÿÇú«x^IÏÆáPZ’+6ÖTbrËfŒ;®8GïÒ®EIY9ÎÈÌÊËÖl?¬ÞjÀÙ*T™´š.î{͹Á‡ÍCcjj[ “Áçà÷a,AÛჩ% §*’‚=‡ZŒD. z‡ÝÌj›'Ô’ p QŸjëU«´Ä°¾G6´nbW ›é"N_¡e ÍÞÓ¨óÁ­Íú<Òœt«8”€ÏSìpTã<-òZ…—4  mðº¶Ä& ºŠýø›z©­½í«õUcüÓq—‹ùÝkwî nrôðA¢a©®rðgH‡é× ‹ëÿ2s¦Ô5ù5,æ= ‹yËüyÜ<6FàT!P}+C‘“\Ã4.ATGNmÐ(™ÁæÀÔ{ §ÖóèÆÝS¶¶ÏJ!ÓðÅÂñÀ&i;w;¸½iïCéa¤_Ũ׀pJá”öèšþ¿8·­êC}b֬ЩÙ1 §°Ùtí¡½ô Ïè-„2UUlkKypVÅ_{ŠõÛ÷ˆ?·ï5SNd@HAæâxÛ.ìÏϸµ¶ô•ûÁø#5B8ù#p<&FÀ׬»¦í@Ãé MÞáx¸i õ»ð\ þß;­‚ì‹Ú/ÜÏœ¨@â(F ¾#ð@TÌi°d38Ü¢Aák`ÚðÓ{ŠëŽÃÜ­»EP€]œÖ¶¹ß=Î?AôdçºD³ð0÷Þ€¼.—!+8v'1²rr!ŠÑn!—‚ÃøI¨=äóÙŽÇX?¶8„Ãø&œ|)WÈT ÕãN·«¤ý—ƒxº»êWBT’LÄ’vÜÝúPï 2áGépF€` ¸Ï1½«ašRã]0LÓ²iC5|pOyV¿."$¸øßJAÁStA–àVÀ°Áò5[ÍÃÇÒ´€»wßõzƒPò˜á!ùhªpÌû´Ð`lºn6 &yér¹¤Óe 'ˆ&z =Nw B ¬¥m.S­’B[Ú$è›Ù=Æ›g@|fj&œjhn†¨ ë®énº²n0•º /ëyôa-Zô þ=©Ë·‡|vpcÑt¾gú‡À®;;=œ}#ôÀîs íŒáÝÄÖýErpKÿ ªWç¶²Oç¶¢cëfî¸S‰ž‹Kp;Äï›þžŽ¡éºþ«¡Ìw4%^>¸‡í¦K‡Š®ù¼MÃ4Åœ¿7·îJÊ’6§é²1tÀO3•Ù¤-\‘û p’à/PÃabƒLjÛ`ðvckÛ“ÿ8’7È|þT¸BF b[„U¬8çfšBÀí\×iÞNÓmXlô,©],ˆÖÃZÀ;š´¿7xñ>ÒáÀ0õ W´ìœ#Õxì±Ü]’Èïá–[V·¶#©Q‡v†ƒÈ¸Fhp Ñ»s[DùÚ¶h,Ú4“«®@¢gÛv'‰í8vìI6³r0'³ÁryÛf“¯Z–àÆEŽ Ñ÷ÝvùYòœÝ««;5R/Y½‹ÿd™ú ãÅø­ìS©F`çFŸ#À„“Ï!å êG`Í­OWÒ¼-Ý "ª˜ía²b‰ÝË ç±0@WŸõ[|è`õ÷Š[`Sq—RŽfçâN|Ôɺ^ˆW^ÈÕZ€è9C¿HþÃêãýÓ¦5ÍÍRÉš½ÿ`Š‘‘•C~…Ý®ír¹Ä×ÐÕùA…iß&Lš”jõÎÇ‚€d㯠S]yõˆÁâ²sú{&׉k⨑ջÏàWéDz–•³1ñS£>«çN2Œ@1˜p* G0uåa[·fÛEX¨Ü!¾«ñ‘)Ú{"¢·ǧv)?ðùÁ]Eóð=#ÀÔ-”á­]óÚÐ(·³t=z_‚UN¹z1óBìú›½>=p´¬â¿CNøÏŒŽÂ¥úBõÃßF_)õJ™Ý‘VŒJ‚Ñ ` ~1¥a¸èå˜XWua!•M—G”C\m ÄÓ–ëÁâ‡yQQÊꥋ·‹}‡ÞýqS‡ÖMͪuïPû\dç8Åš­»Ä·¿o1’×aýq™ÐÕƒó‘›Ê3§3Œ@íE€ §Úûl¸gŒ@… »éFêu0ù»jdñ]ç‚êþÄ‹ÿ‰]€ˆúò Ä `á F ö#°ft‹þP•!Ç[ðŽ·-Úc÷F‰’_Á:çÜ!CÆ]UÃ1ǶCâ·NJÍ@5†j†¶›* ‡)šàš¸[¹¸Ï…Šè[æ‚nÊ…nUªú.e;ÛˆÖ»«ê_kìÓ±×Û„ö2 &´éÓ¥:g@7Ù¿{;à E!8e÷9¹Nˆ &‹5[v‰uÛ÷˜0ð `Z'¤3ojTa‹§¬—Ü0#ÀTüçq`CàÏ+Ûµ…m¦ë°³{¬ò‡s‰« ,²vàOàS¥ËON_”üÌcÝÃ`jë¯kÛ.7׸œœÛÁ]ê[bߤ؎·÷]Ý®½=hQòîóÔñÈqŽøá:ò(|>Oçªm8^CzvÒ{un#º¶o!…c¸WëˆÓ2²Å¾ƒGÅÎý‡A0%©û¹e¤ÁKƒ¬ô» šæÅL^W­àÊF F`©FáæÆšG`õUmši¦14ׂ€º¨Dóæèˆ(²Î·H'jÐ^Ë¥cYxâÀ0§Õ7tn¤ef\9ÛÛ@0À»Yì{ŽCèÚº”ï úz9vC¯Ãb ¾¢Dƒ’‹Ê¨0|®™ò“ÆÍƒ—žößÝÙ%çãXF€ð–7¾„ÒUxG‹90±”‰öa¡þΠ ó–Ê… _µ_ëY°`þý–¿)—<ܸó±tþßZzŽ…üA…ÚI'K„†鸖P:¼y¹°™„¢RÀç•ÈÌÉU0^adeåŠÌ\§t:]…¸õÀ?ÇN´±DÛ:*® ÑBÖ½èxô¸g›|Í0þ‰Nþù\yTŒ@¹ìx°k`êžôQÐY¸™Gcéм¤BXd`¯û[è-, ê›þ_ÜYR>ŽcŠ#°þö–¡Îãê",ÜGcý~5VóMŠÕ‚5=>ÖßÃ"ÛÛAÍõY¸9½XŽ(@à‰Y³BÓN]•éê]¯®0tÿ6l#ÙXj2¢°W‘G>‹…; -•‚ã8œÞÇ¡ÇuØïçoO Ý¾û•gžJá”Or4ÉŒ#PO`©žOÜvªûÍí3Œ#ÀœD Ä?î“É|Å0õ?¯mÛÝé4.Æbï쌀@KXi˜`Ñw fˆ¿'"JhK’´§´¼Ïø3Ð' ÈÊ>†÷ÅB œ¥2¸¸H߈ñbSÊÅC>OZÅ¢`þ<3xlŒ#P—`©.?=î;#PÃòº–tàlC¨‹áV÷bˆú.Ã_õî/(±/ÅyiPp³Y7£†7W£l½¶mÓ,— ã+tÅ%О)Ñø >¼Npž–C‡f±®ËÏýÕ|x‚Ï1Œ#P0áT sŒ€¿"@ ÅL§k§/†Yª‹1Îv¥5o±(~ÅnúO ¦VJ=è×A‹v³%ªÒãøZÀ®;;;š}¦)ÌóA$±t6Î…¬°YƒÈãÆŠ%˜ÿ‹6 ûšu-døÌ0Œ@ÝA€ §ºó¬¸§Œ@­G`íÕ­z+—ºØ” ¢}ò|ˆ'•ê‘>0$&·à¼Ö¬VèÒ¶rÐûwÔúArë-D(MÉ> –ÖÎ'±U胶ÀÒi쿅Ѓ ú|Èž¿°o´ÒâxF€`êL8ÕçĽdêdîüÄî´s°È¼Ž!/Æ"s eþç€Ø:Œ®AµRhÚŠfMÖ°©:÷èý¦Ãûnh|(ÛyÄêFeâg”C(™à8ý bés»^|ú§I[ý #À0Œ@Ù‹Ƈ`_!°þÚ–-œ¹àBiŠÄ™Î‘J!e/«~ˆ5å"}­r®W®d³Ìe!ÆiUA`õUmB4Ó8órˆýóA矮i@Yub^næÀrì,!òûÁ “‰øçÀ0Œ#à‡”¹ûë‡ãå!1Œ@-A€vóg»Î€ï¨³Ñ%Z¬žὦåwO’ޕГú € *$xÃ…;SË/Ç9€P‚¿2×`7'I©óñAZ.1/äV¡©eRhˤ¹¼ßâC ×ÊwŒ#À0þŠNþúdy\Œ@C;ûrÍè¶=hÇßàH¹íU,dËýŸ‚ˆß%Åd\OÄ”]×6ô=x¨¾ãÉãgF ¾"P¾ÃãfS€ÛjŸËuLžŸ Âêˆì ÅR6Ø«žI™…|› Fµz×kBn°™ú¹÷˜Wå9SDÀm2?1¹·Lå«A zƒHP–ÿ1k D(Nß„ûeš¦-7a2NhXé|fF€¨ß0áT¿Ÿ?ž¨SТXO:0Ðp‹÷aA,Ä,t{•§‡RdûÁ¡ZÅô D• ÐÖ«æmþ’°ÆY$ßÖrÖßÞ2Ôu\ôS&qˆ“.’Ä}9zIÖ°0ˆˆþShr­T抻ý§^Ÿ8j¥ó™`F€ðD€ 'O4øš`êÊ1¶aÕÖžNMô‡Á‰¦ÂYŠþà0´ñz0R@‚Kîâ›ô§v‚Kµ Ž; )wêÁb+ü{¤Ï3n¸¢CcCËíªLÕÅ”¢+8Ž]𜺀Hê öPë 4˜ C Ä:©iëta®ðùÁ](ÏYF€`ê9L8Õó ÀÃgü|1¿Âý±àQbJˆ>Æ ªè˜Aˆ¥ƒ³µ‹u7Ae‚ B;5»ÜÕ¤Qà.6™^QDOæwH®»ºCke¸@)7a„çå&ŒðêóßOæöòJÊ] °Ö' úLëB‚ƒÖ²5F/±ãlŒ#À0¥"À„S©Ðp#Àøê†ôµ®_º §™GL™õƒhÙíI¹¥2ãE!¸ùIDTÁ@¸U2qÉt€c.‡–¬ëɃí>^™úër"Š`ð£©ÐŒÖÂÔq˜Ä!j©5ð꼺§ÎÈWª£ä²Æ²¯”;€ÿ:<†µðýµNׂÖÕG¬Ë‰ÓF€`|ƒ¾;F€¨ßiôƒ¹Î®ÒP]M¡u“Bu!•w®ˆÈ_™0Êlüá’éêd,ôáëG¥`Ñ D„û,¥:fšZ DÈŽ™Òv,8(„Vhzï>7džjë€ùP°d†Ê,[¸¡;›h¦l¢”Ùx5–ÒlÎNp‡š@o¨1°k b´5ÆÛ×eúê*²üDˆØÁ’Ú¼¶Ã@Èvˆdn×í¶ítÛ)Ë\ÞÔÁyF€`ª"À„SUäòŒ#à×㘠ú4ÎÎ`-u€È_GSª :B„¯ˆŠÕ€ÌF™ ¸2@@dâgÜãŒ>åâÞ@¼ ×°ä.@H(×.w¼T.ˆ+‚¹#u16¤á,mHÓQÞbG1Bñ p$q~B0ÆP:ãE›¡ ‚‚QEª+`|RíA»Á±Û­4±[3ÅnÓ&vé2x;sª w®—`F "Tㇰ"Ý༌#ÀÔMvÝÙ)(5ÕÙÁi8;ê†loh¢5¬õµAÒ #*8¼1‡]7(»×ù–ëˆË–„œiTItø8ï ÞÝ{Á®ƒà*6ãÀ0Œ#ÀÔ^˜pª½Ï†{Æ0~„q®œi­”i´Ò £©Ò$ÄÜTc·hDÞÜgºÏyƒø› ÈÍÑ¥J(ð¡ G°$j˜«T\§@œ1|,ˆ*ˆÊ°´R¤)S4÷½:,mZRxxP2Î8•OŽÛfF€ð%L8ùM®‹`j@€¸ZÙ©ÎP—©BrÌ<1:iªQ(¼ðzÃ&Óæÿg |WݪÊh¯P!¾aF€`ü&œüäAò0FÀhÞä#O#ˆp±Fé2²o€YðϺüïÄ0'¶Åˆ²&~»Ô´Ç@ìŒ/’ƒ,í=o #aæÍXi(aè°ÎgÝ[ç•q‘&´x˜ÿN—Š¥[ùøÌ0Œ#Àø#L8ùãSå11Œ€_!pÿ˜Ãé ’>ÏRé7[·èFMiïÓ½¡ƒ£DBfú|ûp9ÿLº7úCPCg½Ö¢ ”C½ëa¢|‘a¤<_BÌ©¯oòXãÃÉãŒOóÛ,±=«>3Œ#À0þ†NþöDy<Œ#à—hRûDèî Á=÷zPG!Eç.¯ýžîAÊ,NèPšPæ]BhoIé0áÓé]gVæÝîxJB9|BÃÛDâîüéñ¶‹ÝE_ÇÉ*î—6N |fF€¨0áT/3’`ê:“îuþˆ1´œÐÓ©rÇ€úx̘…µË#€J"dæ,h×·ÿ¼iZ‚\(¥¹ãÈÀƒUbyúccögÁt—)„—ßiÒu9]¹ÅEõ¨ 7¡V ‡+/ÿ2Œ#À0þ‡Nþ÷LyDŒ#à‡HP=RÈ·¥pݽ¥«ô|1=ª›T!sâxÊ¡ä)õçAÍÊF&’f¼ëæ,yrŽ&s­Dý§g”Q†VB}¬RÇ©4—•‰ÏŒ#À0Œ€Ÿ!P¢2±Ÿ‘‡Ã0Œ€_ `³Ûßråæ.a4qœsŤð†ˆ"¨Øÿ¹ 1=]ê'u­´€ òWLÓ #_S9ažä,…·‰Ê:v`Ò ]«ÏªCATeYTτό#À0õæ8Õ‹Ç̃d@à©»rv`aÐáCâ@Yc‚aˆ\X,|Z¼L²Ž™oè·"½9q’¬|tîÞó”ì6íͰæTGõ…4Ù“6ý.ÄižR)²ññp‹ –’…£F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€¨-ÜÛq|dì­µ¥?µ­÷;¦uûtìõµ­_ÜŸÚ‰À¸§ãFFDO;«vöîÔ÷*"*æÒñÑÓúžpF€`Ú€­6vqß í‚f›rÈ牙¾îß¸ÈØ+©ÆP½RÉ,œöiR}>/6ú_·U´¾qQ±H!×ÅÇFÎ,šfÝ;¥†EŒñîßµâø|ÃPgK¥CÌÇ'cùª,”rhsÎ ±ë±ÆYVÞŠ¦Š}]ÕšÊI!RpúGØìo&8&í­h]ÍïÍû$Ló^¥äÔýkEë¯ùM!”Êü c]W‘ñދ޻W(óü„ØèÛ+RÎ_òî¸ìåÀÜ,ÝÞûÇû2¤”Ê_ÆÅã`F€(ZA8­½²ÅYXÜÜ€¯Ïp¬¾z%gæ†P·W]Ù„Úbãg©´Nÿ2é粇ãEª&zâ3×^Jí9¥T´w¡©ä„±Q1/ΞäE •΂v?“š¬öe¥;èEÁGìáR×ÅÇFOñ";g9ÌYÐ<,-5å:S©+„RgLKøOtC∘– â=ûïÔR]ú`â=™‰Ué"ˆ¥JÈe˜Û?c'bÞãk…Ë9yldìÈùqQ+«Rwyeù}*!ß§;?Ú]+_T¦#¥0|ßBí¬qÛ91= §¸ ›nb¾÷Í>z¼1õtÓ™±ÎgÄìñô+Þ…E½4_""|º9Q;á^1Œ#P?8¥„ÓêÑ­F*C=k(5RÞðÜ»S*‘ƒ”P8Œ‡V]ÑâOMè“@@}SµÇ%ÄÇD¾•_ÇŒqQqÑhþ?÷O›6kΔ)G«Vwé¥ãã¢Þ/=µŽ¤8Å¥¦îECéq½éæÿý_§ $çÞ'Ry ¯Q‰ÏH)Ñ€\‚wêÕ5#.A{ÛnŽ|ꮌäJ%åÊ„ØÈÿYåÇEÅlÀõT£¬¸ê8óûT¨–]g¢sÅÈÑDÓÅXeŠyeç®û©‡Åô’¦šérŠ+ñÎȼÏT¡”£ìM¸ˆ½sóŸ‡öm6ujŸK£ß” †F€`Bà”N;옺+ýEe˜ã+æ@S_¯¾¢Å[†Ü×~á~µóA0ßC%S\³ ÎGÝâGšü"a=„)ÄNz*DRSCã£bÆJ>€Ë.ØaÜ¥4ñÚü˜¨¹÷9¦wu:¯‚ìö‘¯8&î§¼&DO;Ï0͹ÂÞüLå:ü2vi7$ÄD½Li ,п۰ý|oÇNfCÔ÷6q{~–)_Dtìx,R¨Ä9Ø(¥þx|Ì·èÑ£/¼œ~4óOÙÀv†JwÍzDg%ÄEM£²V(Èg×®.s6g‚Û¶V‹Fõïöâ˜1cÜ»ÇOÍœÙàXšs6ê¹ ý²C å{ˆþ”ã¢cãÐÇ—ŽÂ(œ)MÎÄòà‘„Ø¨ó¬¶ 71 \¼+d‡æC"òv_Aœ> îGOôëÊWŽ'ë˜v)Ôìä¾ ‘ ç°hbƒMá²²¸ÏãccÛšÙ ÏJÎÆBúÃB‰õèfF|@¿ÄÜ= 1äÞóÌŽgr·Ó™yÝ´ùú=SÆŸx[¶Ì|J¾‡¹:òÌ»æçƒŽ—f;OÌÄ<¼ Ü£p,K×i6ñd¼#j5æÐƒ˜CĵºÎ³-ÌÁé˜o ñ¬ï§÷Ôó}zÀ1³MŽÓ9ïÑp”9&5ñÅB»ÿeÍoj'"ªäy77vâVÏ~¸ó)ó"MjßcSg °ìˆôïtM{sÞÔÈ‚ûq].óE¤ 7îæó\¼»/’xWM¾Oeý§—Ræda·]_’¸¥Í.c[ˆn[’Í#ñNzõ»ëÍÃb3M5Ãýžx9V`Òߌù›–Ä޾휙7÷\1±J]¿•Ä0Œ@Gâ;56ßÐ',uWÚ×ø8W”h*è(>dw&g9¿_}CçF‘U»¸˜Š›"hg^5ÐÙ0”ßÊË ÿt$¯¢x,tÑ#¤|RO?ÅcáIDÁ\Ç俱JËv¹ Éüʼ‹ÈM Žèk©ÖXÔ5Ík«¬ ;¢pýo¥écì¶À>¦Z‰±Í°Òéì&š”˜ÑÂgl=°àúÖ4ÍotÌlGéºaàªî*Ýù9 m"`˜¦¿Fiž¡ Ÿ‹Çj‘‘}”Ðfa§ô?ßmÜq—•DÓ"w?mÚUv»¼‹½–ÎL“v™…Mo<cn‘ü8Ô:4ÐÖh”Ôl«PǹÓ;S‡CiXðÞMãÔ¹€âò‚y=Ææ^|–…£•X‚T—+eÌÁ˜ßÖm¶^-lgì°Òéüp\\KMߣKê3Ñ4}¾m„)+‰×D“'Ž˜çá N?š6_{¤P|%o@]¢û*^0ïJ˜Ÿ9®˜³ªÄWïvmˆ§ ¦KýL@ð¼¿Bñk"¢žífuãÁ—_ÄK!@¬äÅ|Ÿ0e®+÷kÔç´Ùz ;Dˆ%n²ÊÓ¹¬ùMéÞÌ»“ùðî óÌõ)zìø]Ø$YtŸãÙV”ç¾ÈØö šV€¨ÿÿ!½….&¡ŸÇ?=m,¥×äûDïtIÿiÔ+ÀÆø_Nšö@IDå›ëˆüÓá“«LÃöÏ@siã™1óð?û:Î}†XeÓ¬ ó;`û¸SÝ‘õðçÙû@§éúC«Êð1ðèÕìé úñÉãŒÿV¨.Sµ#ΊaŠæXPƒhç(M»¨H…æ'qb]¦1Üf×¼æˆÜŸ÷aèž“ë4'ã™Þ…9÷“)sï@Z4¥ç<1š#Õ¾)·PÿÌ´ 0†~vpã\ÇSÉHLÆü¹ F †ƒ s‡òæ7¸Ênb¯¤yW¨1ëF*»j»åµÉ“QÞ÷˜ÔÔì‡MÓ9·Éà>†5ø&pYcó‹|Œwwdþ¦QÞ§ôš|Ÿ€]±ÿ´ü~ à> „Õÿ4M»z^̔߭øúxÞ2,v&Þ…ˆªŽ¸O†ÓXºyijÃú,sÏɪVÉåF€`N1XG×\X›µ< ‹7wÇG­—‰û¬E‰×Ubx°áRŸ`wøEˆã]yýûaéî±ÂÈ=‰¦qŽçšaÉß\¡ï<ó´cjøã–:†ØÂÞÅ­ãG,"âˆ&d‡!š—¿C~²äƒÓ¦5LJµ±’rÙÉXº’­ûG/4Až¶à…]…Eägt€°Z„]qT«H¬° @D轂›2.¤MB<¯PØ……eCŠA½ý°ÐË2]®—¬öL©&RT]é\R@¹oŒ|}iš å7˜XK€ÅÕÄÊteÀ¢-iž#r“78´!EJíì±§‚x.Ø"Ô™Ån”G|½ºœõVËP—p‘x^•ˆ&OÐLa¾6#!ÜÏ …ÀYYƒOc¦„I»6p~Ì”åž5Ÿ†2úBÌîÐÜg¦ÌwÊy³“›¸7X»Ê7ð~ÞBQîðopß²Ä?óãÜ'÷àÆ¢>‘:‡ÃÜÞbåóz~—>שׁ¬s¢E4QÄsO>™£‡•)ägè‡6[ï1–sVèÝÍÏ[pª¶÷ œbÏÿ4«Alüt‡ð¼·ÓA4­°âëãyÓ°Ø«0—žôÕØñ¼;šY9oûª>®‡`FàÔ"`«©æ×Œn×U™NXbËßþõQ毿¢åÛ¾<¸Éë*¥XœË<óÏ›Zxý­IqÀ3=Dè® ¬Ô±°#ŽKAPºÊÇ >e#. t >†"ñ¿‘á7,÷îÀBêMZÀÈ¿P¹ºfÇ…¦›žiX!f[™s„+€Ò R÷©¦\"êU»Ò=ËéJßïy_ÚµnØ µÂídï¤ Û!KjÚ«Vùü닦¸ÎŠ+áü5^o‘tÀ{¸h·\ЯÇ/ß­ÿË–lL? #…%ì×TÎ=ê?èpŒÄÆ}á€utàñ<Öqà(Ü’éÑX¸–ºçÌ:Lº5¥´•¡A†pÎEÙó½.¯ÉIØx³¬üEç'Þ#ˆí\Ùç.޹žƒÍ÷ÿRXÓ2Žf¾2!:v$Ä27ge™—(]¢Äv ‡â°…(§,÷>E{?¿Kœw…kvÿ“z—Цƒ‡ˆ¸xŸæYiô>Á؆^f¨–÷©èZA”¼ýú?ìG<ÁæEEúï+ÈççûÎz!8ÕȘãóa*5jóYSoéóëÓõòÊçxzY!Y€Lvýr— µ}ä¹Ááeñ:“|±Ii;8/frYßè:3î(#PÛ¨1 â+S°…ì&| Šz®„è—(¬ËàÓ6PEØ1þrïáöW«~èd\â(ÇÙºé.Šƒ¾ÆWú記gs…nSâN+¯çù•gžJ‚˜_–rIZ ®¶Ò å6:á¾%‘#ˆÐeÓ"~j”%heõù»£[°ølfÓÄaÒe(±<Ä úN†&¡ËÓS2$¹bÿ Jok›üó˜1Ò„ŸÅ†0/Ùx¡ÒÝÏÈkOÖ^ü xlЉ~Žp#ñ¢Ç´?ãS 8 ÅKø_Ìôw56ÓO{Ûãcø·nk²Ð%²‚”™34ÀP[æÄEí)©îÊÆµµEýèŒ[ít©çaõ.¦•-ê§$׬.R:Í‹‰\@õbüð×#¢Ç͘Ñ(aÒ¤TŠ£.t5~Æø$Ü~êÈ7ë Ä!ŸG¡Î2Äþåu/p´²–už?5êcàržršßç˜3”tFÊÊïWii7c<>Ñ+ŠÝ‹¸j#œ.ì×y8’? §ùì¸è‘zÈžWÚ=Ø\9C³‹s­þèR{âˆßS}”Ô¬ø¢çÖZ÷oÍí{¡k7 ÄÓ”ÀHeÀ0‹P¤oô3å÷f~­·*÷vaŸíRÎ;±ù1¬§xÙ¢QbÎáôº¯ÅLq÷©6½Oá ìcp®ûá?G]® u£,Ä’ÇúX ¢`à Dºo:+ö|D,+ˆ,áB“¶††éº¢ÑôȆøkôk¾3H3¼ÙÖvÎ}n.¼iàÒÆI1 ÿÃO"ÞmТ=¥);Àúél|rBÁôîUÈs;ŽbA—ö †t¾],¡œsßB’ƒ6ëU`߆õêqó`R)¨þa…Qpuµ„’=Óé¼´ºê·ê…8Ò«mK€ùáÙbïáT|̈˜X#ÑV÷Ž"¬îá|¹ÚV|Içð†ö)Ø‘^z–b±r»’±º&õÌ;j@÷YØY|;ˆ³®”£ òYÐ#šghzÏ|¾¸v[#Pø¦WX}EKøÂÈ32P]­»óÊ/º?*ÕÕ†g½d »±sÊa‹»â™VÑk‡ãÿ‚RűَÇRÊ*KI`äT§“^«}ˆëØEÒ±Ö Cõ£¤ônÅ[ç Ó§7n i.ZlYq•9ûÇÊ´_WËÄ%ÈcX …Wkÿu{¿È{s7Uk¨éƒeÁŽ'Óà/Íÿ§êUM×.Ç&hü¿Së Èsq@³¿€›ú(t¨âÉ¿• Ó=ü˜M—ÁâV#W 5~Ìh#ŽÊ"1á@Ž“ÏD$wXñeµIyhÓã=€õÅ£p °ÊH7Fa™ñ,Ö.~eÊ”Ãyõ”þKã‡úß ;ay |­m„Žßƒù¾ØN#ãòÆ@¾Ø\*¥þ-s0öÇ ¡›9"3\N·oÃדwº&9ãÈ·¡+ß·a¾¥Xòm˜'¡áömHn:`Ù2Èfû8Çp^(ùˆ A© ¥a•—J¿n_l†ÛÛžºž'sð#ÀTš!œDž¹ëªv¶ìòßæÀÔL‘Û &† «üNÕÐÜÆ)G¢¯Õ>×±¨¯Rmlçî9à\7`nW›t›ö®ËiN%‡ÑsSþw¸Z-í¡š[µT%:ŒŽ½ú²¡XŒŸGz¸_ç³ L± ù Ä÷&[Ñc£ãæÀga<Ý÷ \Ž –z<üû èð–éÇ̪ „O)þÅJoÓ*[D¤p!8nÑ9Yêj¤{'ºÍ¾Ø,(ùÌ0^"P#„vUrðgíe—*—-¯Ê•åRŒ@]B@7´Üšè¯f ¼·z€€„›‰êýD)AßÁʇd±z­0°¯trð,ˆ›BáW§Ë ®“ú··€ë´¤Žk#pV W^‘T—MÞB†u*ÒøH+Äy¶ ܯòÁ?fV[è_ÔU„óV²1oÚœ=ýtC¹®Æ£ëb°1†Øm5¶Úóâ\ÌŒ\”è‹Íª«ä1X©ygp³,߆óÉ·!ž |ª%Œv€5!Ñ5}Fø6Œrû6„î2|Dê' (¡ò +¾3ÈG$nÝ"ˆ-Ëô}åê»/¶ÂO‚ïß# ù¾Êâ5âOö@ñX߯@<¡ÚÛðm¹6F r ì~Æaˆ}8+WÚûRJ—‰ÞçæœŒ@ÝEïSµ?@¼Té}2Í\·œÕ¶YHcAþ¨¡,X ƒ›ô/MjïXig)6B_éN: Þ÷‚0UŒÃ±   Ý»‹b>ÒÊ-vÒÙ«äz ‹Žk=Ë–ê_L÷ËæYÖk'º”‹ˆ$`o·kO€É'0œþyò°Š@°Fô'6_/bÎÔ—]óY]:^h’áÊh ¯ïÿø¬R®¨Î#0#> Ÿ)œk°ój÷å`4¡a”iÎ÷¾¨™½\ÂùM|LdGÏwþHÂ;>sõv¼c4›vËÒm¶•.gîmìm•'®åEµõ6˦a1/ÀÜ·O ̹#RëÑçײœ×[ÐyàŒ#ÀÔ!jDTÏÂCÐþ ÉäëÞçº°ßæ¹pó¦NZøÀâÍ,ðnó&ÿ©Î¢éFŒ±:8 §zhÜ~˜‘»fø'W¡ŠbE±È[TÑD…H„YŽÃAãY…*QâF¡iŠ+᦬wb‚;a†÷‡ãÿ‚J(Z, ÄZœ©Ì›±«¿±Xb~D~]OƒÂŒ--·ñÊtÍ>ðwøÒò¶Oä¬Sjâ‹$WâÃÞ–á|Å j7j>ûÆûdjB¿“‰¦âXs #À0uŸ‹$”ÂàO’ö¬»ºÍU†ËX F¡eå-?Mfkš¸vÐçûw”Ÿ·Hå¼KiÚëðL~ïýÑ3ú̉™´™rI×ï6lŸŽü³AൃȆþ¶`÷üjÚцî=X,vÑÔDa“¢ŽÆð†>‹–`È¢ß_ cÛØº.Itíx„Ù5ÈGý|rÞÔ(·|{iõÄ?3e]DtÜèÆ(´›†÷ðñtìUÊqÉÊÄŽù›]D¼æˆú-"2öFø¹Å‡9}\paÿW·¨Bù¶ a>— uÆ\ÃÜ«bâ7{P l&ô®"%?Àü»™WRû"cÛ»”è!Ú6[Zn¥¼ƒùå¶ÃïîÆHÜG—W—]ØÞ{5fâ¼?ËJË›äÚ=ùëœØ¨=”zo ïÿ‹‹ú‰î¡`? º"×âþþˆ§ã.Ä»>ïv+˜¾„ܘŽ#¥w”{zç;3ÌŸPæØüØèsÇ9¦õ.cþ7º¡Nú?š†ÿ 7¥½ï£zw[_ÚÍÇôΆËõ¦R²7þ?6(q眸¼~ž>ÈT.úŸh ¢ù{èxØ,ex©Î29Ûð?ôlE7“Bœ¶ì®ìgμ"Gæþ;V)´‡{ÿùeUëáòŒ#À0µå8Ñ}–¸R×äHìWÚ§‡¤¦_túç¨(Œãâãí¦£¥Þl1Ž÷Ê .X^ø~Ãvâê´‡8Ðyº-ð ,‚šÚlMn%¹mÓTÓa±æ_mlQ´˜Ù¦IùR‚#r-©”yÙPfïÚÆ6y1L½Þ¬„ÙJ£ý4©ß`šâ%"Êʪ‡:R“ï†ØBûÃóûplTÞþ cf»ø©QŸ#é-f±óã¢ÑD²ÖX¤ÍÐíòZaêVÿo7l™? >Õ3þ¿½3¯ª8ÿþÌ9÷&!l²*àò¢""¢ë¾`A©nµŠ¶¶µ¶MÂ"T‘5¹¡Gs“ˆ ($¡VkÝ@pEqEë^qQ«¬²È–åžsæýÍMÎår—@¶ß| g™gž™ó½3Çyæ™™““éAý ¢M¡Zìvx!%­c¿ú¬kòùÍY0,®Õß*ѹÚRÀá™’¬¬P¼RÄkƒátR¥§ûÒ³aÜ6Jâ)CœøÐíƒ1h²u´‘1òYTì¡Ò”Ív\›ØÁLt¨¾VéЖmòÝ.¾î]Ñù-v„ü»ŽÃ4¿¡[‹ÝÍzk£Ißk.ÒÖ™ÒÌýÕIÝD½ÒoÈsÁï÷Ý h‹zÀìæžl†Ý+ S‘ 4$&7œtáOyaíGBúNÆÿø«ïÃ`ô÷©fÍš|Ú «ß©oÚ°üŠýÑ¿|¯ÄÊÚn4÷=QÛáV+ƒÁb|¬;aáNˆTð}¼ko{b”yýÝ£Go³,ŒCK±Èª£—?:©}çÑ; éøð(µaaç ×m.¿CçæÛ×>[vn"=Åwæ¼vŸuÛæf~tT õöòˆ>ªív?\?蓮+DšÂ6´ÏÁìº*Z†çìL7W ó"<µöx$¤Ø, y Ò_U£Ig0Í»‡5«œÂšzê^c$á4=¿ B¥4u;@;Ì!wšÎ«B7i(]Þä‚’kŠòrÖë˜:ùÆ>’“ô·têJ¬w:›ž—½ðºë®s}Í}ŸÀ|í©¦í½ÎwM͔®ÍÍô§W§}×ÌôOà}r9fä`kj X†ÝðžÀ4Æ›+JòrGýƒè2Avy¥kw‹¾Çóúèþ^`UZÛÖçÀø)óªzjx»^žÙóýq×3ÅI€H€öqM:U/š %â÷ÿ½¼ËL=Šók0bÜ&ZÆ;Çÿ¸¶àüt 8uîšO¼û»sTÊý:fgašÍ—v™­UtzuÉ7ºÃù¦ï½*\÷þŒqù_ W‡ÎÒ÷zíFÈ7Šë:éꘊ·ë/®N {…tr%ZVß°2}‰ò^=ÝÄ6L²«¾ÆDwÙYÞvN,=ºSd‡6Â@ëµuKe9 ºîð@EÇk=^®¼™^Šïfœ£ï!?=Ö=šî‰òx›i/(.>µçF¹èx,ÿŒ6hêAù†É#iþVŷݼysN†®EõðÆÌÄðÀ€VáNÈ9²ß‰ÝÞ,N &^ŒNŠ'ÅÔ¸?ëMðnp£ãê}.Õá†ò­Œ•Î0\YÝZk$¤Š¼gô`ÊR[1EWˆòu ´‚g¸¶޵VÑIx¡Ë ÷ð¶Kbµ÷˜ïšqù}‘¾ËöPÙ«®&ýÐ,ZuÇ=í”]þ¸Z¢v¼2€ËêÚ¿œ\‰©Ã‡{ñ<î>n/ «DêLÝ›^©ªnÁùïÀûˆº4â7‚¬z3ŠNø w^]2¼G$@$°ÿØk†“‡î/®Òþ¿*ËÊüô㽄²{ÀSÓ}Ý;Øà—æ×'§ž»X>õTØñÒíÎqhAA‡ŠíΩèhœ›RÝ'UR^¡\÷OÐ÷Rg#û­UnþFø”ú+C½ÑÙèöâè©GèÝã*q­îK)>ÿ…S²G¯”AâkQFO™)¡EÁìw£n‡OcéÑ4ž#5#è£ÑQ{6l…SÕþbØ&Z=W̽µv ¯vYYŸèº¨·æÿ÷]ÅmZ+cÛ©èìu…Ç¢T…!äÊ”´”EÃo*_…:±Í{†ÌgÎtmûMá8zºÜìDëìµÁ] ã7cýЫhv‰«Ç <é*G…;½aƒ×6Œ’ÈûFSÏhuˆOÚËà†ÄT¼Sn/ÉÏ}[€„ìŸ7j]ñÞ±Þ5J¥˜&¸­8˜sF´¤õeå”á]u´>÷ò=Æ;×G\aH…ß–¡¡è©{Ð5Fÿé-Ëá¡=Ó¯;ÁÛçC=Á ž±¼yZ§Oôú¨†Ê“zH€H€öM‘ŽÃÞ.ž´,=¸ª¨:<'O5Hñ*ËÝaÍÆ:…o=…#&Lxpë–ŠÜ[­I‡ü$ ŽÄ4¾TŸ¯Í˜>'t(¯Õ Ô‹ï}æÕ¢S»oH°†ÃPb®+ÜŒL«øS=%Pw¦:ŠUáí‹cé‘êdŒV†=L™ùùT¹{1ºA˜"¤-<ã{”««Wft¬ _ükHNp’^4®×˜¬NßROôdx$1Y›t}x}W»8MvIòN‰5æGl+¾V¹2 †ØDɵÁJ»¶½¢¿O„Á†G`ã€UðD"ý±âÑî—Á›ÛÍ‹‡Q²jœ‡ë¹h§éÊ^w dÖzññŽð.üo»Ø® ˜Ÿ‡NžœZ±nËÑXß~o9Îæß¡¬—© 1Ú;Þ5'Öõ®éböùjµÊÅ&¿†ÙZ‹þžhš­?·ÝM­3r OŸ‘7ö£[y]+±¾øH€—±›ßŸ²,rƒ' JÞ¤o Pÿ1 „êœÆsÀrPêO†a>ý|GŽ,Ãò˲dHmB\7ÛÞ´èÕÅ˾Å4¡ôn[úCùë-^+Ö•.ÏÊ Þ­'úÜhaÐ9D„ÖëNåbŒ@/]-¾;,žl"1 PÜÝïŠr5¸O§ô™Ï “÷+”gþÎÓ»{a”ó¸>ƒþÿ®寓¥¡k=yI ) (L׃QÒ^’]<¬®í¾:º¦ú/¸õ8n¬«ÜÍÛ¤µ‡¨®¸zÜ[„ [N‹Èû̧`àœƒöý±²×/ÁFoEâH9>æ!íÜ)ÆUb7¾I¢4´^âOᕆAT½©@¼öû]ssÞ7`{õ|èûy¬ÀçÒEšf )U¦È®3_·{˜˜OÀ˜œåWo&o´9ùï£èqò ðH$@$@$ÐðôâktF–éQ\O;¦Æœ Cæ-ÜŸˆQïÈ´8½™ ˜oµgÈ“­ë¨z믚kÝ:>=C­É­êÒ¥ï )(h§õ´&tÔ»”Eßç9 @mº Í~5Ä*ÀÚÅA{ƒ½ö¹ãnâ3­/º­jÏ“þ‹N¯½Çz×D§TXØF—/úž>¿«°³æÎ÷ñ‰‚©xO Ûù>¯I€H€H€†À>3U¯ag÷µÜqÇ›&v‡\¿ij-zËoQfcà ±VöÌáÓ.Jf•”ø°õ÷/1=¦•uˆ;5Nï|…t%Ö­R‡„z¦XöV‹îúïÔììðÚ /¦f „Þ‚˜H ½Þ(#P0:d;!6ÜÕ^üy—Ik¶\‡Ó·:hÏ“wu¬³½Û[:•úükê|×D¥ÓÇŽÕð]BM»¯õîÑ(Ž]ÑKÞ12À³KBÞ   Ø#aOÈi8€Ê-èí(7Sx:c Ìü½H ÿƒ›ËÍTa—gÃn:S’Búû¦)gY9KêóøÚûÔzê“'eI€öxí=Ö»Fò`ï”–¹’ ìr/äYg–ó&·m%«¶ž­”ÛC¸¢½B¢p”!¿nf¶|¯ïm›7×™p?º9Ä*8.rO.É<µ»I‹jYoøÖÚïÜì¶ðÏ*3fK“f~€eÖóÞ/Žu„8mª«T²…+U…tÅJ)Õ¢^GöZôÔ‰è½bµ‰¬Ü»Nv…}®4åû%VÎ÷n)™{²ôïY庽fܘ“lÊ‘ ÀþBÀ·· :ïnßEJ9ÃDŦ‹\%ü‘ò(!ðŸŽÛÝ-μñraS.éÌÈÔó$+pw7¡ªnURœí­•KLCÌ)º3ðL=Uí–¸ã¨s”T£˜†S ‚¥Í?Nu6‹Yá.„ §œbÝ>sò7­6WVd¡~ÿ%ä8Ý=9¥[SM›R8~öÃçë»OXü˜!}÷}5â„<¹úo±Æw®rB×(W\Ž‘ŽfÈä)ÌwŠƒÙO'£«®6‘™œ¥\»—”ò-åˆ èÙ' '”ë: 1#?pf2Ïv0ÊèßS*5ÏNÃé`¬|f 8À {ëùæOHë:ïn¹FÓ|”¡?:s;Œ¦ ¥„‰ËÅŽë¾8w¼|kî=©Çí,’è:c\ð7JT.‚ár8:ÿ”†Ì•B}çºêQtˆŠ¥?˜â3ÁW¦ç=PžµûÄÅ7nª*ÿFÒÝh/£)Æóu€Ì­Žp–"]Þ©Å*vû‹¡`Paa›J»êxˆ¯1¤šiHùP²¥’îé1’$¼y×]­…T¿6ü©¿, æ œ‘—óÏ„‰šHÀ”þÅÒÿn¢ìv+›,+xZV ¯`·3 @\{ÅãôÒDßÅŽS9·ÖqKWwäùÒ®úä¥ñæ—Žvž¯[¤öÝ&4ß²¹rª²Óän‹Š}aPnásްß8.ÿé¢;s´wP‡AñÝmQõ‹ƒÂ~öð}ÞP¾5Ÿ,™®”úk½‹®T*Úa të’ ›¸ôÊe#ºoHV‡[¦úB¶#ŒœS‹¬‘ëjÒ=œlúºäŒR·7残ÒW—Ø^¹7=8z)2Öûn‰K\!Ûì»dÉH€H€H`ÿ%Ðä†ÓËÌþ®ë<ƒÎZ½G¸=ÌHÛ£ÚOϽۼ¾ÿ('á”m[+ÿ€õR)‡´òótxÇéyc?ÉÈ >ê¸j îÍÇhíPWɾ˜Žs'£™¹ÁB¬iUœ2jüø–›¶…&aZÒ¥a÷cÍÈ|ºyëÔììZ6#ü‡0äLYéŽÑø¡AßRÌÝÅÉ äc.IÎÇ-z^UÒ7yFÞØ²rƒõô'z—k^@ S Ÿ Ôê$ßvï½ÍJ7n_$ýÆUÂv'Á«v&Ö‰}ýÏö;©Û} ˆ¬g©Ñ­Ëu$ô~.¥y{q^ö{Yãò/tœÐ¿0åê0ùFç‰5&™Âu§û|Æ Ó¬œEúޠ܂޶ëþÓ0åàâ;sÂÞ©Vþ‰NHÍiÓÊÚÝ£GolåÿŸR!þ xùÊ i~s_ó÷Y·…תe ®q…êƒéX åN„—ä4¿HÑŒ~ÔyxÆ€Ì äáÚ×ïäã2£ŸÃ“9˜–RÆ÷|þ8êÏu{ÂüÏÆ4Ö·à«Þ_ßÒ#\é“Âùíªâ‚ŽÚ¨ X!V}ÛYžßQ®rayc‡šú·íðìåôµ®«®£† ÍôFùÇE3cDINή¶{NOAü:Ô³i¨ã÷á¨"m¥¥ï Ujc@Eõ7…ñ £ÔUi~ß)Öè•Z‡º®ÃÓ=Mø;œ)í—(áf”—VÇ 10—á(y 8tC=ß~ 8˜ûŽW/½wŒ”)Aû÷îÁ;*ïŒÛñˆ•à×ÇMX#:ï­[QæÞ^^Ú#…w\yd‡ÓJ²²Bú~f ¤Pêx¼oþ¢¯½çÂé1x¶ï•!¦ÏÈ LÓq:$ûŽ vq+Þ…rÞ3«Só_  Ø? 4éT½ySN@'dVÜiyÉrÄô=)ÜÇæÝã?%QtœN„±õ¡îÐ×%kò ©D/gú|/ápux=TðÐÉ“Sae i¼¦oÁhz­NÒg\á÷K¬Ñ’‡†¶»OÖˆã :am–ãåRLaú‹‘&¯Ø·ãÌçKÕÅGRú®éòaÛ¥ëj‰Óÿ"Ê|‰^$ï¥Ð'à|:¯{÷¼£é8ø-ÕqÊv°¶D=+ÒeO%Œ 0<îXðù77{ráN¬ÙROñ¥t‡®W]×}u¨5þð”ö-ßÜm(Ç–æþæ§ë¿ÎÆ9 ñ|[[\ìé€ü èL5Ã4Çë#÷ŽÚÍx Uøÿ`4ýÛ{|í“þsQ° L‹[6ó–@^׺$ãÕ·å}þ6Ó0à V麗æo©sѲ–õPêàèHOø|m»š~ù”ã•âììµZnpNðÛvßEg}ÚÞ ÂcP—FW¡ã#m¥4ô~ò‰”³ds#ur[…mÿAËxÁQî_0À°¤ÄÊÚ®ë, Ì£¼¸šA–BÔßYê~ñ¦ŽOTO‡wŒ”)Aû÷îñ™m QÖ9h]s4¿T_ë~Òð}„g?oUx´Î˲”£éÏ`×ÎXµá/!\Le_ék`·ÀÌC#ý' Éã…!ŠaTå„«H‚Ä︿åç £é54ùy4š"àxB$@$°Ðñ& JYÆKîÐëÒ*CtS¥c?úÆ}NîÛ÷M;–^ŒwE'àǘñR¬@‡¨ÝPkr«)Ö°åðê¼íʪ?B>W§©üië•è„Ôí^ÀHî9èð÷1|¾cЬ±ÿÓñY9Á «Ô[ÚƒÏÒ·úäKíØú°)ÆU†¯ëøgš5Jwôîñ¢0Mî^[…¾ÐkG¦Œ½ú^€a¡;{·h™µö{ñ)]ÌÎ1Þ£Ç;ÁñF’ç¡c>8…óh‘€Å$,ÞŸ¾ÆƒQ–î0Î6¬ž 2VŸçÒ2èü¾ #éBœŽ×¯Õvð‘¹R¹…³fÍʪ6hF¸¥6:…rnG9WÌæ„Ë[_¡c½´R‰ïæêÑúyZºc¤i\¯Õ\}­¦U¦ê£i›*3·à~tð~!ZøûÅ2zµìÁzÜóåYŽr´§´Á~“>OÞ³DOgÔËXÊ-KºØEí‚íN­TòÔµ¹Jš÷ÎÈË~ËK¯¾A&Æk¿‰Þ=`ñ-dª ¨2ŠßVxò¾V¶­Ûïw«œÂÞ8nÁûé9×Ú«>ÿVkÒ!e¡²Ó`€þ×Õï<֗ݯ¯€×·9Þ£´÷{#qÞqC Ú•—¹ Àn6¼†wV«á¿$@$@$°h2Ãéå{òþ€ü.ÓÕötžPþÑÛÚ¸˜[—Ü„î@ÌõT0„ZÁRÕû„Ãʦ@ :]è@)Œä«q0Ð?7áøH1¦µ sÕ ñå®mßóp–˜ræƒwI`׿cq£Úp¨o<£I'Ì,.ö˯r¥‹iRêpG„A ¯ýB™ÅttVfbÏÈIÇ—cÓá)z²n®Ðéë Ò'¿Øéþ÷x=%OÜfÝÛ¶4TÖÏvÊ~¾¾£vºQ˜’£¯ë ®+æÃ€©=o?­/<V…é-X¼4ÿõÅK{[Ö¬ÿ¬¶—BŸéuz‚Wx Ÿ§ï`î÷èÔ-Gçõ$Ü NèTý Ö|O&úè¨6º~óØ"nK&rŽ)¯÷¢¾à§iØà 1®×´Åÿü|ðIºÆ S­ìeø•žªéÚr°„÷ìÜâ`ÎøÝ­oÑ¢n¥¬ -ÛaJõL¥[Cèvå:¯eòÞ‚±1µ(/0¿¦­ê䨑OµAÎÓ…¶|$êùÑÞµ>š†ñxôuº¯ÅcevéÄAVð¬éVàý­›+¯ÜõÅy×fÃc(ñšg;̪V=$Y†(ùði¼ö‹gJòÝS[+ÒÍw„臻3ðû\«¤œo5÷žÄ@È Õva¼ÖY%™ÖÄö*T~(è,ˆÖ‚÷Î+ðòÝõÆ—ß…ûßUÇÅ|ǹðÀ? eHª¶f£•ñœH€H€öSès4MPŽÙmð«·¿©†Ârô/%€-»cZÏwÞHj‹vé³!ßzPn°¯žn‚ÀÅÊ4 §—2‘rix0˜î3Lã"×HýÄËÃb•w^×QwÅŠõo*á 7”ñ®)}w™>ñ÷hYtØ^…M³k—®Õ£Âˆ»Ò”5刌:7¦ÿíè$E:Ö•ÂNÑ1˜Â÷ŒWöšãïS¤oÒŽTµÏ~uòqÀVª¬Ü°íLµÔÉg4+¬µzÓ~~½ÊY~6 ¯Š {ûaµ~=àîê‘ïÚ:6ÚXÿɲúB¶Ž Åñ0¬:!m0ìLàøI‹Áë’#(Õªª¬Úû¬ú"+g vÁŒÝ*ÿ c¸žVº»õ-:Ïm×êúÛÉû3„ÙJÇ#¯I¾æQRï£îý;+7ÿp›Ò‘Rh¯åçÑuëñFáe÷kíS™+½s}¬öÒÈ9vHܤ¯a’þQOW‹2Èôíp(ó—‡=£Â'µÇk×dvN¯ý¢,I½{vÖ‰ë—ñ0}µ·íÞb9ç‚“º/Ômq­Sˆ:o±’/ëtéðõjw9XÕj¿ááÄÃàŠ´ßXï8 ,„·Îûïâï´^  8Dþ'ؘ3Bʉ¶êÕXy ãtìË÷úϸdx(Üqß9¬a˜i˪½ ÓÂ^‹Ž×^µbý`tнûÚ»ƒíÇñ]©·—‹Å0^>,±²¿Öñ¹þ–G{Ÿ!Ö{›%xéêsÄ(úiÐvNŠßì^3j/2róoÀ½ˆÝaC9P.õ‡r»4†ÊR½™ED ž'zj Fè7CmÇâ;Þ4œZZ`ÈÙ;7BI˜îó6»èƒÒ]‰ô7‡òl1I*g î/ð OĈŽÚUÑŠõ÷~*CU=]³zEt\ç>chº_m*ß®>…§J;gbréM×5Ð!…o¨‘~OÝáP_õ®0–b®Wǵ¾w- V%ªo‰ô×xmÕ%7}ìXíËdž “7m«úz½üJ\ÏFÙ¿Bîãm\RWÚX÷`ø=¨\w6¦–Þ]%Ôù>%þT—¬Þ„­RxÙ~…ø]Þ;{R†ºòÓ÷’z÷`Ljú£u¤·mþVéÏe-רÁ›Ð.Í.¾±  .>;ð¼#ÜKàn¾P™˜Â‹ G<×rx—ôs½çéqmõ+¼+CÚ}ïÝ‹uDû_Rœ—;Ÿ€x³~ÿ•e,*¶²¿Œ%Ïû$@$@$°¿hÃÉö ›"è/­Mo4€é†w ƒ73¼è_ÖíSÛÂè<óuZ»³3ÌÛ÷=#Kß/É <€_¿ÞçIâÇõ[ð»ê¡yØì&WÇ×'¨#:Ž„a¶Å¶7¡n0 ÀþM}‹Æóî–ØN\Þ˜9Áàxý²Qáßf£¿™bWT´˜’½>¡p=ÍO¬ÙÔ©UssãÄ‘#k­-Š“¬V¦ßÈa……íã•Óõаn¤ÍŒ`îõµïá…ž>§RœJïûSÑêôz‘ÕþoÚyßÅ‰Ž«Ï¹^›%[©J=ê]Ÿt”M û”¯;«Šª°G'¶TÄò†e·÷Š Ô¥»¬™ —/oªòËD­WßêÒèž^Gµ>å½ÖuÕ_/­ö¼¤TÈæíÅëb®§ó„wóÞiÒ_p¨Þ½®õPQ†Dï½3gKð÷t7J=XÑ&”½ÞóRí&"&#  ‚@ÓNãåË•½¸Q‰a#þ£ÔùšÇ^P®¿‰„í½ ¿¼ ÄÊùï^(³ÜÇô˜øåQ޲ÿרÅÂ`Äï—Ž8i½œ]Nê'   ¦ Ð$kœ°‹Óf¬lnÔ縹Q3hbå˜"×kÿÏÁ®rÄGt.¶4ššø7ØW³Ko™ºyÛÖðržF-"¼'[5*'   ýˆ@S­qZÞØL°yB£çÑØÏ­?L ¶n £éo|爎ãùÁMà“¬c¶À¨ÙÐØ”éÿ¦±ó ~   Ø_4‰ÇÉ0ä{öönÌ`Ù:·1ói*ÝEwæèÍ".4U¾Ìg¿! ëûUZì(·ñë¿u_&om¬¨—H€H€H€ö/MâqJmÄkØÊv[c¡Á4½ •ÖšFFc¦Þ}€Ï6j¡¤x¾®5O*'   }˜@“N}oþ_¶ÚþW£qòñˆý¼µÑôS1 ìc|iÆ, èÀ6Jð†þT @ &1œÂy5K]ºvkËîx¿tV¦øSòãÉ0Ž4_ éYŠgÂÇŒ!HùâÃ{Öù1éFÈ*I€H€H€H`¿ Ðd†ÓeÃÊWbêOvCSÁZ «ßmßí©Þ¡ÖäVú{,‰ô$+—H¯¿—”i§{×ñŽúûSú»9ñd’ÕOãö¾£ÌIøxé¢-­”[¥òkP¤¬¡Ûb#“jI€H€H€˜íÓ´aÞxã %Ô ‘+¼MÏ^:òï¿‘Òrél 9OICÌ+É äxòC :TnwžÂuþ‘Òçå<êÅ{Çdå´|¬¼<]Þ1#wίÄßvü}!èø×’¬¬Pt<ŒÍµ¦0:ʹ;º§ . ÖÝŠóC<9ï807ÿOŽë>ÔÅž¿±>öéåÅã¾A ç½_rœÿ 4ö¸DR8Rš×,½½çó‰t Œïa‹Ð|´•£¢×Beæ‡áÓ§—sÿÈ[ŒÝ.?œ üÕÓ§?«JíOqïh}OË>ãwEVÎOÆ;ÆjG‰Ú"ÚÁlFó¿_ž?ÍÊiXÃÒ+$@$@$@„–†&rT×7Áà™Ózç6ëpÔo“1š2r ~iÛöÓ°ßÙ9ßÊ2wœÆ\tôÎ-üg*¥¬Ö„Ž»+/¯hY9Áóá-ëÝÅwÞI3‚¹§Ás’+7ÜìÉ (®à3Sž”ÊØ.}Æ ù³K‚9¿p¥8a` ¯'«C­ñ‡£³x¾™õCô}žØ0¥n¹ßg^„ßý§=yR´É*ÜMÉM:ŸiÁÑ_ ¡6gòÏ®•¯× Ãx2rO‰Þ™9Áó"×IžÄkGñÚ, ·|W¹x/ˆÏ“ÌŠb$@$@$@$&7œzø¢ ^¢Ø¢ÜÂN{NR¥¬-䢓xWú¿¼Zo:Q;ªî+ÓôeøÒú C,ÝYßJººY3ñˆ¾_2fÌ¡ÄKÊ©ºzwåâå­Sç‹çÂó J<cé:Of` ¿7ʲ|š5j­î¤[Ù_ê8=ºNá‡p±õôdõ±Ò®šj¾ÑR¨Ôèû¿9 F̵ÞÚ@[ʈ|&zºiJšñ>|§Ÿ‰×ßI˜ÄI¬v”¨-NÍó…\Àt`4   h8è¯ï½Ðx•žNsÅ‚IiGW…ª®E§è|\÷@ǯ= ÝñÙ •øZr¡_ùg_4ªò!"K€ö¸àCî˜Öý«ÊhEè–IW}/Y¹è4 Ï•he˜*²Ë`ÈŸV¦B‡ètzÈաeýSý­Fî¬'+?ãwª§Ja=FùC®ÊH;´UŸey}p°$j®ÿ¸n–zhÉK.À”ÎËѦÎ@Sꊱ‰¨ë0'VÂ@_dæ+)N»ç8,RëKkš5v9Öé­YåöFÚ·„p¯—¦16ZSå3Ó›…nß^îþwHNp&Zïæèøúž7J[¬o!(O$@$@$pPØ«†“G¼fW¼»q­ÿêÚ!U˾©C¦þ·¦YCJ3r‚zÃ…H0”l©¤[ëû8ÉÊE”$q‚ìÏ•-=QS„ZaÞb8ßUöòK`5þgŠ5¬Ö·©Ž þ똮”Gtè륃ۭç·O:´ kNÄêNË´ÑÉpxj€ÔS`_­ùkT R3¥ëhþà„œ#ûØíM]!£ƒöèf`íQH‰)Ø}åÆZ+Z0‰óÆh‹IdK   ÀªŸƒ<`VÏêðÚŒ˜¦w  ¬ØK²r;§‹u ¯Úù?Æ‹ÇæGÃ<¬É×¹îjMÓÓ›I8Ž°Ò›¿ñ¦B )(h'¤ê²ÍÏÊ nÕÐ×^¬X÷3v+»ÈÓÍ# 4Ÿ9ž­Kǹ Säf0@m»„.fNêêa›Jíþ»DÖóFC·ÅzfOq   ƒ” '!fcmÆŸõïŸiMl©—¾Ôôõ +xÖ Àøîú.œ¤ä´l¬­Ïæl,žúž–W-2âÙQãÇ·Äöçv2Ž{ÉÓ30·ðL!|@ú}WD¯Çšš½»¶ŒþCš }çµÁ½È}O$ÐÐJ¬1?bàZáŠ,Ôç»é픑eIëAæ.´1íBÞí¯-î¶R&$   zÃ)Å×ßRRga­ÆÇ"TñÀnz';ÍͱÕXGTeèódå´l¬­¯(oì§›³Ú^ºXËA#­C«7o éÁæZÖ€*Oë:pÞUØ¡ »Fÿ…Ëë ðH{‘€Ât=x“ÚsÞWŒéyc?Ñô¦©¢ºï~ˆ×w_+S’ $E@{z¼ÝÁ¼Õ÷ 5 2 1 °ã2Ý@IDATxì|EûÇgvïRé½ET¬ˆbWPìõµ`ï’€("Š@r“\ŠQZxíåÿŠ¾ï«¯]Š,X(" ºÔ$$¹ÝÿïÙdÃæ¸KrI.á™Ï'Ù½™Ù™g¾Óž¶B°aL€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L æ Ș—d1BàÖÑFP5éäí¼Èï¿¥0FÄjbÜé°Sq0x€ðzÖäøÇ® 'ôHÿc- ‚ù‡K]´Ö,ï²Gö\9xð`3œ_¶c±NÀ)ó!VÏÈòýëò²|5#€|fSŸîÊÎnk˦ÉÉž_™,wùìRd•"þ¹Ùû«ÌCrwX¸eÖ…VXܱſœÔÔ Û¾º÷5á4¬"Þ~›Ä†ƒp]Y]bñ¹ÿì$ÍÜz°)Í4KSBŠõR÷þ>Ë?zS4ä-2Œ+…PS5Ó˜ˆð&„†™’‘u{~0*ü$+¨O–(ó–,Ï„¿ñ¡~ËïÔŒ¬ë•²šæ2fÆJšüþ=ÅWÝH³c«¿*«wNm/:¬ãŒò¹è”yCj>¸d•wå_+Sõœ£…ÖcB©ëvì´®(ÿªLœb38D)‘.d° ü­ÌCrwX¸e6èÇšM"%=° wß M>-º´yµ²FÞ†û¾1ós§3Òû»¦M‹/Þ¸s´ ne Õ’ž³´E7–Jñe~£iÚ„YÓß4̪ú£¿`Påà¹!µ'4M}¤,y¸.<¯U5¬†âŸ¸mÜù40{S²²^ÏIO_ ²o_õ0‚†ý’&ÿÚD )šÂ|ë!–›×‰ÜSáiA…Ù 4r¬L5´ –ò3Œ¬†öQC=Ry¥”ó„vƒ®„ŠCÇÞFHÕJä™ÂRg¢‘¿H)u-üQ—_5³ð« Â;gQgÞ&x~‰û•Њ4¥º(©Nóã¡TU%̪ú êNÄ+¥¦ÌÉLÿgéóoT5œ†äÿ‰#Š ¨¾ ™[w ¦mx?ŠA3Šîk3üé?Ô•l4Òm^œOÏò­®«x9&PS¬LÕ”`??{b:)Q=ê8Ú:ÝêSs2Ó÷¥:>ë˲þƒþêa²æB¨ÿVU°ý_¤LR3²O´,óf)Å&Í£?Ë?îÏÐgïðOêÕNŒ]%DF¨SÔ~cáæA¶V¬ËýjdÓ{—”@¬=¶ÕÍ$”‰-¨gm ÃzSýýþFuêÊsEùÖùP¬^=¾*ϱ_&Pß´ú€ã¯]~ÿ3 µCÝ…^2Õ¤=C1ZJô$fZ ‰¿P?ù’~ÿ\¼ WÍÐsÕ‰“ÖªÐ_Õb«™o%¬ã픜N‘"·þq+ý~ií+¦šË®:RøšðnÝW<‘¸ÝÿàƒM)"ñK~j"{uò9R¹BýÝ7eJr¨]E¿£Rç•| Õ÷P¨úæ_Ž®(®ªØG$—TW%ÌhûHÆ "vy ö§&òT &[×:m¼k) ûU°X´z#­¦¡·˜>;ñ„“ø2{é½*Iû+?X0 n§¬3Ö2$=°þ—jºÈž5Ñ·×hNjzÀ‡)ë¥Ð}³iaש`Jâÿ^_Çs!u°Ã&MjiæïbD¡™îÑÏò§ÿìÈâ\S}™Ùp¿ 38Ïædù&;ö5º*eïS–háÇI¿æÑ®8£ÏAKçý¸‚:€Ë±¨ß_àÏ9ßäßñÊÏ +u|à"Ë£R}Y}a—„µZ?)M¼ÓY?ùuÆ‚b¤G˜†^®³¶(_Öõp•šèˆžûS“[œpé*ÑAÍÎò;¿_ië‚Y÷Áá²\cÁQd?Ä—ù £ÓµvUØ7g/ŽUVk*û=>š˜¿5ÿfP¹¼7J˜Á¢/PÎL;½švoÎÄô·+K;É‘kfß,”u¸ºmgqRJF e~ ~¿'4ONNæØŸÜáTUv§ÌTT¶ ûRÄu .=×ÍÌ·Ø—s?ÌŸu¤iXM•«s¾sÈžÖ£áÒlv¦ïÐÐ)k*SC3wb'À`Lu¾c{a øÏÃóK¤Ð²POßq¶Ãòg" kœäq¹Áµ]Qþraÿ•æÕï«HQv?zôÄIé¹ÍÆ7XR0!ÅŸýßÚ²P•ýÆsÉ5Ô—y2~"ÊÂÉf0h½‹ôé r½ž¸ë £è¤ïoð³ý›cà÷lÜOC[c¶hæ=á¡1cv9ntuø£þþ4'q¥ãFœSÇg G$Wáï´—MQvVb)Å·^%Æ…Û…WYypÚ'Žp×!“ŽÊxòæK¯çJg‡kªï¡ƒ°ùbŒê‚ÜàŠöH½D›°U ù^þÛYOŸUÙËM¸øØ®ö ðÈTí3Žj ‘i‰ç`!%-@/3޽RÁ+óü/©ƒ†ãj4DÏà s1äcMSý AJÙC¥7h`ÚÛa ³y¨›ëwwòƒEÊvÇ;sܸmX,ŒÆKjÖó)³g{]~5Ž–’ca§y¼-Ÿt»Õä ÊÑô¼&Õ"w8Nú¡uÿðÇO"½“Ð ¶DÚ?‚ŒŸ:~¡üÈ=Å—•n™â ¬Ä:ñ|«gpÝŽq™{sƒ_”뼜ðèŠNï14zÏ£ê É^Є¼q/E;MŠÕÁÔbêòU,ä.“ƒFPrƒYï@ΑGíÐX¾ ÿ…ÂÕ Ï<‰Îá¿Ôлã‰þ½U¢dHunŠÿáråi_qECöÝ­ÐO DÞP§g3%p¿Ân‰ýgªû’Áqƒ2ú8Öt=…ß‚ʽ| Æïž`{'–‚•KWudwÊLEe ÊÍW”φ²nrä ½¦º‰ü GI² êÝA%åñ)¹ÒPæ?D¼T¿úÇ ”ã×QV¢œ€BQ®ÝNõe_Eê[¸_‚–bñÙlÄóÒ¹4@]P>†Êa(2iVæ¸ïÁó!„/ ë)RB+rªÈeIÙù¶õ`›‚+œ²€½¥Ëføï߀ß^Ô‰“†ùD*oökl¾BõÙ™gœWÞUir6¹CiÅ”u‰I™<¹9êüûÊRO€ë!x¡üeè%”ÃÝ´)óš?¦ŒÏºÀñï\++Ž¿Š®CýY‡C‘zùØÓ£i÷9ŠÔp6Ú‹â…Èó[Q6¶!Ï_„9L1¥ø+!Óx[Žÿ¾r£yû’dxvvëâ|óNä÷ßÉž¤ÞùG•MRyÙµ½¨ÖÈ}æ£F²WP¶Rü“»IÃòSÙDÝZ·æÎ«c¤ô(Íè¯Õ³nyÂÝy>”›3¡.åUs\;ý†ú§´ë Ž-K')ÁÊØý ÂÁK‹8e¦ß÷•&”™+,e¾ŠJ8/;‡Wi¬Töôx|Ûf±ëð2¤í¤ufÖ{šþ¾®U•+'Ó÷$Â{uæ[\•"nÄì¬òG¿€ÉpjÉà@\—ãÏ64U».øÅE¨»?Cñ;m)•Üc ˜œ‰2†ê&) ÛÈ|œÕYàüŽhâ½vŽ«½’‘u5Ö<-LõÂ=þG{¹Ë–ó<”ø*·5´Ñ0ŒFü]433ís'¼â’­!匞¡-ÚchÄJ÷˜H›X% Åª`,Wu аqBŠ[‘¢š5‹û?4…xëiEÛŸ«zèsñžæÃÑ™ý‰7ñÃüȽ0¸óq4\ÝðF5aNæ8j«f”ÊÆÐö"úC㺘¦évn/ú úͺÔ.Æ0ÿéu ˆ·¯&µ›«¢H‘pP¤Òñ¬pª[‘"·é™c· tj”AëŽ0î$Õ›.EÊö'F›lCSeæÿCÐ*ÞFSy<ºÏQ¤ÈÃTÿ=Û1û6ŠîáÇVNé¾6 ­%È £ùÊ!¦e. ÎŒïŠÖjÄŠìÄC+ŒÚæ«ÐÎŽÎlÔj9Eª¦²WT¶hdåÿ#HÔvƒ±ðÜR™Ê.óþmøvîÏ0}û{™C˜RÌPœ0¢& “µ+CL s¿Ê)kÆî4ÈÕ eéU·"EAÛSö¤(Õ[[³ÙV\ÃDÖ £&öZK*›(‡·¡Œ@GÙCý“z„} Ô²äÒu[™ÂŒ®8ÃÝcÁiàÛ2¿nKÁï÷H9݃ѩHCA\›¦ èÙáéî–R#`—çIö\Ú^Ú`¤šp[bÉD¹!'îŠÊƒãz¥39öm°¤â*”…2ÅÎö«T7ºj^ʳò†Î¤ååmùW,`e*–r#²Hõq¸·{û@P%?¥(LËê¨(Œ'ü#v¢±½ŽîMC=ŽøZ4Þ7ÓèQ'oz5×I©æëJþ0¼ÝZ<~w´„u…=LŽá »E³io…sÛ—Òp¹kÏÓáü%´o6—:”P7Le¶,±ÓV„ºÉvÍiÚS²¹{ŠÄ0ƒg !NBÃÿñLÿ¸U¡ÏuÖÇ.†[1줵E¡îÑüM#D<Ÿ¦Iq;uDà|,`bíÈ_˜É ·~w|±${;qÒÏà»:é9X'5‚F€Ü²†Þ×Tö}•-4¤ÏP|¦0o×´T‰’¶ŸPw÷o-hœJeŠÁk§§ot»Up!Ùã%ã©pü’ìM©Žç‰Ýœ,ß—(Ã9RÉ–aΉäø‰º\V’g>”Fù=/V@Tb,¡Ñ’ªœó1ö÷ø5—¹[”¸âÿšÍô“Œ:þ‰óâbhòT¼­ÄÙJ(-YctgYã¹óÃ8£ºDÖÖày £[­pØò<¬…êª)ysN íõ0aÚŠž Š™Ãü“ãÎV1L \CÃr²h‘PrmE^ñfŒÑ»q@#=C-¦û{Ö¼ˆVf»ðxn¨öü¾Ô†‡`¯é1 1ô.VP]µ —‡[ Œ&öûª¦Ì~‹ýkS'4¬Á™ƮƂݽ‚ F S7ƒ)yeFzÅ|4~0ö¡«”9à&¸eçùhØÁcq94¥E[ÏÁíÿì{׿uF€&„0¦¯âvïÜM~v9—»¥N%eB–Ý™¸™ž–ö7Ð`=Ð¥èD߯º—Ç1•vFÖæ$&ɧÂ*"5”}_e«£·ósµÛ!ëÅ´Çé éÆÛ‹.¥fÍã_ MKèoÓ^ûE¶{+ç¡~K§{ ^[¦2ï@Yº#ÔÊ¥]ÖPžzíí¹M³ñiÆf5KnÃæ—°Ê…X[rSL væ¤;È&åp •L‡_Š:¶õ¬#{ýÑ’•-”2ïÄôÜ¥p§é4R†ì‘,¼à½O¿É l—ò¿—Øìý¿½ûç:‘Eµ»'¥)ôÓFû*îРõç¯Bƒ1ö˜ð½ävwîe·¶«5›OA½¿Ä4ŒåÈÏÿaeÎÌÒ> QdÇ_c“LÅf¾T[*Ô9tøÑ7h³ÊÞÆÞÄû4,$†GìZsU†õ[ Km£uØv›xL:̤†44(¼ùmµ«ô÷úmÑ@S]ØTI£µ×HAG‘þÞPÅó£#ŸM‹}q¢uG\‡`D"“âÖ4YNÉ‚üÝKe:¸Î û‡ÙR„» ŠÂ>ßW_}UCÇñZèF'”Æñ…ÒŽ)ÎùØy9XóÈ¡\"½ê¬ó;DSv'ÌŠ®– 5LÚ‚ù{,{JÖãÅn8m&4Ð6/«`·µî³”î°k*û¾Ê–ý)%ÿ…3Aæ¯pâݵ³ø`˜Œ<žÉç¢P KÊ>çã„QÑõó_ÿèˆ4ym÷ŠÊ”(*GX¬=­ú†d—ºL¡P&¦ïÎÑ=Z»“×QÈé7™Ú•«dÍ“aˆWêøIýÁ 3øþ”¸vM>"ÅÕºG§}Àï3ɯеì+þaýZû^нê´ã‡^€Àm31¦49öÎu_åÁñCWA2ÿlyq½ŽÖEºÝ{ZºÐÙ›~¯½PD›r™iZïAYüµdÄßñÉ×X$À#S±˜+5©e Ú!£¥lA­¦ÇÜÓhºSyÆ#h(Ðp¡JÁÉÉOÕÆÉɵÞïæZË‹ÑPvöÀdêxþtËQô7KÖÿÞ±=ñË·à—î¡k$¢î… Ôà☈SpLí¾KÁpRŠ(°èƒ,x@¬Ã›å Xý&ýt ÷ÍŒŠËÃŽ}u®W^y¥õáY£CŸEß¶&Ô®*¿gù}ß`âfì\za¯ãЦì•Ƀ]”ï£Ó+ÛDáø×ÅÎ=]K÷;°SoÌö¼àMÈȑȋ›ðqšó±Cê”éþ´䯦²WV¶t¯xÆ Š¡ÈÚÝS'¾Ès=]až)¹Tò_[ìÉdKµªÄ§ð4óþ-þ":U€rT©ÿÊ«ÌåøC¼0< ¶·ƒ4v)ÊqQèsµ)â¦Ñ&?fÜi´ k3M*›4ÍùoºÚ#ȾÌ÷P®¸cüäc0MþkÞÖüù«ÝG; Z% ¤ÚÐsá â¢Q¯ÖPÒ­Ö¢ç¦P?••Ç?^.[+]\‰¹Ö 1$v3¾Aù4Ü.sÜÝWjOðûeú+9>Á¼Œ1"o½öÇ#Ÿ±Ö’M,`e*s¥Že‚’€­·h’¥L®0j©ºPÛÎ ¸#´Hõk%µ1n>> ü2²~µ;Ü3Õµ[×/Ùk 1„‘†Fë¨jlè­»t ¯-½5Ò"ð íLö¡çLéEZ"”§fPž>×5ùî5¯&×[5]ê¬Ñp‡‡]+l”R⶯Î}iƒ^#…¬¢xã<Í¿, î@¹Pö(ù‹¦ìÅëØ£ãX€{ú‹È”ž-ô$;ÌÉ5–¿„ŽëŠbì²ÃÃ×Rµ-;) èðh”ò4¹Ñ½†YP€-í8¦›&"J:ùßíN^Ê$ó¾ Õ-Lý…øºÑâæ'ücÖîË4Ü’¼É÷óσŒ—ÐŽ7èùÔv¸MmÊuÖQ½¿ÆtîÄJ©²s¢ßåm×ÄžÒ#9t%_Ç.W`CÅYP¤Z¢©ˆCÝüÀ-#Æ¡Ùo<ûàœš"X¿¦äŸ¥Sá傈ô‡ÔÄ”œ‰¾ãPÏwEp3¤ýküðé$ßÔ}…Qºyçú”Œì'„2 ½#p,ÄŒ™1Ë÷õ»Õžæ«î±+Ô[ K '}v f“pnÔˆcIètUùRÄß0'3íS¼>„ìм¿  ÷Lìr·ÐšLmÈüöžò[§k®?Ðó…F^Ù~îð†úýÁ ÉmçÜA:œô°dO“‹éPÔ9h8iT.œ"e?ãÑ¿F‹7Pu9mïw‰µk±YГd¨ä/e²5Ù©ã‹óèé%2«Þu);ÊÁ3ôZRdWîÞmaºc8´¶L†Jn4¡-.ñb]Ù‰Úr!ùÇâf{ ®’àkìl¿hH­dm–¥¦á½!!| Õ”Ë^*€P¥Ñ!\¸4•ÆóѾ´J™}.Ú‚>xµzÛ]×¼ÉoC) ¢àž‰²;ÀGï»Ã‹×š‚3½“©+*<]¾£›d¤úÒýlUï1ÀULÏÐn_¯æŒº_€Úÿ”$,Œ¯Üäd¦a·-C‡Ò$̃*‚}ÔV¦êƒzŒÅiéri©HƒèŒ(·x4RƒÅš¹íœ{Ú¡Vh_@‹„Ã1Õ½´}—Ü:é½' ÁøUè_öÅŽÿš^íÃ-ëY #2Ï–‰×4Xûy]ªÉtƒ­Bãìíé®Pé G+(lw—uÙ-÷®è0½ùfÁ¹´ýž:Aúsïà+óŒ›Ùþ´_Ñ8>…–±Eqù,&ïv§{ZO‡ø…ÚGó7N~>‹ä ¦}N Î$7ŒX¾íø‰ÙIRDCóÊ‘Ó(]¸ŽNõWÇ®.dOJ’/ ì»y|)͘–|Α¡²+ Žg^‡âŽó£6Osoñ§g©LÝáŸ^öb£{ô4( 8òDÞWQ]£cÜÏT&CeîöN4Iç8©¶PFnç¿rýAáaAýEáÂ%;M+e2Õ=ôÇ ØS|tO¦D២]‹íÒ•f²–üQ‰kÉ{C…”t¸ok,¬4t×ê°ŒIý DÑỤw?[“{:f£÷w!^/F›æÒ.?'¼”ŒÉG„ÊAnT°>«ÝKÏžòL¿ÙÄO숲K‚¥Ž“0d8 x³Ù‚sg‡s‹†]ÎiïaüW¨à'wþ9^ÂzìÍèzåy—âíŸF_ ó?ÙNŸ†¼j:¬·11Ûq£‘_p=vÝ-B³øz^Žã7Ü,F㌩ë7„O 6»c.²+ÙA‘ú,¾]Ó{÷h\iá5✋Îa0ŽKþ<^Ecº zlß¼qäÁç`pjh|hèèóã±®áÿ‚-!Ùd{¡@8#k-V™ýÛ“¨M¦hγqIÚ¸`I£]yæRø£mÓ+ÁšÖ¦uÇvîÅRÑÛôUÎ3Ѿbœ/hŸŠ©©ï1•· oV¹‡°ð\ÁþãNø”Ž;îX䱊­ƒpò÷ÿSL¥,ÆP߰þã°Vét(%Â#ËM¥Ô¶ì´‹õçÈqÆ=¨°~Pz@)~DfðɦÑAÃ8eñ6õצ“P?ÁËJ>Ö¯‹Sóû¡üO@HQht´â…²ŠÓÒÍ7pÿ)«ïqVyVßt‚—£‹…:ÆkäõÁ½óÒDÖÈèž„»¬`Ñ™¤Œ„ ¨ÚriòhR7 #Q»£>̇úÔ.'noæ ¸J×MÑÝY(Ÿ»›6÷¾»· êuäA°ÇùRja¸©ûΞ´‘Ø9Ûá¥àSQÇaêíCœaµåæHCWB/XFÜ¿ï~õm°Sùil€xoÈ3òŸÃõbăwVcJ®õÅÑp{²¯„Õ.´+}Å_›0¥)‚\ÿ w”Jõ%á'£I€G¦¢I³a¡Qê Ó7Ü*Õ5ºÒG©"Ç{½—£á r7ÈŽ“ñ¦¢ƒ½Õ-šÆŠFíw@©þÀ±ø=½ÅflE¿ÍíF÷ô­>ô%>„ÕF(j0Dd,ŽAc‚ÅšeÇ!|_P¾«Iýr:´Ó=¬Y¨•û†«ñ‰œ4ò‰–m8”ǰçVpX$šzNŸOCC±ß,•<ܼP¶ÖáïúÓ÷`· R[tì÷ç[o¹GlŪk»ã°P|"üa‰‡ºiœ€ NñY©–9‹jCãŒÚo)^EÖÓ©Ñħe‹»KâÇÂb(VX<ŸÚÉÛûÜr‡D"ò˜rèÂK‡e¾Û“ˆ1OÇŸ¹G;$?Õ=òÌúwn^u#»þ ˜âÓn Kç*?[Ê-ÝÓጘ2>åèy¬×Áç…¬avúpR=ÊÅPv?ƒÃgjÒÓenòíxÄ;’8àJe·ìŸÁ QÜÏÔôž•š¹¯pª#—}Ü ê äFƒQ'fAm¾ÏÂýmÁ*Û/ávIÆ{â¨\”­ÜŸcK#Û<]®Cå¨ê;½NA¼WÃÏ/Ø¡x:)[iuž‰ÖÇd C–Q‡Eîö´ÿAø»ÐVÞ‚¼ËB]œF÷°kŽvvd¥{61J â.Fåg±¢L€¾T¾Ñø£§†O<9aÜo¤hE9ŠÜÐ@ ³ehM;‰q+*šN¤³g>\²üOT¢8Jà̙ҿ åUzÎÞlU{¦ŸVÑÙOöT_8ÀRúÎæMՆеÖ&4šÆ,ØUÜÆ”žV8½Àð´ÀΧ²ÏâTu}ÊNÂÝ5mZ¼µqgÃ+Úz„¶3(Z¯Tþú–½R¸ðpGz «éÕšvã–UTp¨L~úëÊÑ'i³é´>=7†žäø­ËkUå²ótsA/K7õ„f‰¿E{#KhÚ©Ý[oüÙ]z‚ñÄA¨óöÙk¡Þêä7M_Å¢½fÊD©ë¹Ó'ŒÞÚ®Ô‰ I•°2U%\ì™ ì!€oŸoŸÑb×ìÕ!8=WIÏè8]íPBZ3üãVÖvz†¤n@¼÷ÌÎôõãÓÙk›và ÿöVÐã}Â?fmÃ’|ßÒŽô?Ö¢ÐÜ}ج̴/öí“]™@ä<‘{eŸ±N ÅøP)5H×µsgML?T^j ¬ š./ž=Ñ÷f¨{e¿ý~¥UöÕúʨ w('GIKuˆ4ìÔôÀU–PÿªÌ¿&õ fÒÞ©Ì_eî)þ‡Û¨`áG8 ýWMj™8-½½GoúÇ]þiÍŠ‚;çã¸ôÕš&³,%[7ÑãÏ ¼3Ô „{jeaïË}hFöɦe.ëGŠ­s­¡@µC™éÖOŒZ:å<œxš#f2žçV»ÊÊþ0à#¨z4ý4C™·#®‹;{|­CëKŠ/óßÈÿË4¯3+3}®ÛoC¼ßÌ¿ÓbbÊäÉ-sÆŽÝ­4øýsãÖW<‚šßYy-99œÚ!ÀÊTíp­×P-SͺoÊ”Ã=:?š‚äÕwù<±¡¿©Æy½_ZÁ+6PÄŽB—!¤–&4µÂ±×5OT>L,ÝaDª©Þ!³1å„OÑ\‰†·µ¦éá-y¡c?Ä—= r˜Îïš^Ñ> 4Y/…‡/œ×4Üz}^Š¥B“¡2(Kýjß••}Óøþ¢øcÆÄq †ŽÏîiYÖÍɤ¤.vâO™=Û«Öl> ¿Me¨‹q-§La0r 슛6OøÜy&Z×Êäwâ²7 G™ù—ú«¸n´”êÓýû[RFµ=qâ¬ë«&½¯ ,˜EEjxz {npÅ\ŒòvÀ·8Ùì‡X™jt™.ßPR·kGa’62ZÉš1©¯i]¢^}†Sª ®udHŸµS™–ðxÔÇ3ý¾¯ûh]ñÙ¾nR¨Ýô1¿1vO°R’½×¶I9¥mN í{<ÕüŠÓg³&úæ×<¤˜ aËœ‰¾×…D••}‘È5–_…Ò'hªth ð‘Ø-„a™¤•)SÚ_›N…†Ü Ê˰'ej¼[~Œ Àó_Eû%¨2ùÝ2àþ&|Orå #{_?xð`G¡oTÓa3c–#ôCŠT±Tßa„y‘ôè·¨ ‰zÎf#ÀÊT#ËqMª?1]”…jBJFöÿåd¦}]YSü³“Dpóƒx¡:B{¼aÿ¨KÍ733Í~CêËb)c"…S ~ˆ·× ÖølÑ¥w˜¡‚ô66:'ñÏñË…)ÆcêjÊìÌô{„3ÀTòq¯ô^=#0f)ÙÉÈ>]ZVâ>Ó`AÄ¿È#âÆ:îä#87ÁíìNß ¹fÖR‰k„PmãÛ5o÷ĈEä'Ô Éd K Ò½ú5³üãþ uô7ÒúÒzŸæÅVPŒ‡œ œLžå ¤ú:ÈÁ»¤R!_7øÛ*¤|_OÖÆÍ7nM«î.Pix« ¦ñ¹Á¬ï1e‚0ùF¤U öд 7îü†ìu!š•å{iˆ/ðOM‰"Ä1Ü-'ñ³„LGÚ„}þ–K©ß=Û5ªåö_“ûÊò%Õ—9,º#ßÿáŽy5ÒŠ|? Ç ëÚ^ÁxØÆ9ôŽ]m_+Ë'~äñÙí2p„|IDüy¹pv }ʰŒÀíáÊ~N–ï çùõÆÊ ‘­îÈn–Ï— Ëp;ãÏ6¦” Œ¨i/Cy+Å?¹[ŽìrÄ”Ss‘<É~ Ä7ì*©—äoxvvk£@AFu&äîr¸²-½â9šF¬¨îºåw⳯Jæ!œ-.Eªœ³ûG$åù>ÊY:JB_Lª7;yº¤ùý·:aQCkj²µ´ÄŒ÷… Ãýõͼã3f—ã· yZa½¥ö²ŒÊ ¤]®ŒFÐ9r¸¯Ó³|«S2²îtÄA¯|¶dy·° ’û¾o”P†Ù4&èl“d·¶“ÐX,–õOzkÞWú쩇àfzó¼s?o¡±»C’aY§ŒÏHÏšR§ºd­„TÓ°î' ¤6µ½çþßÐèuÅßùåâ0Åu§Öâ\ç¶Ç:…óIépdO{*mhzà:©ÌùPü¶¡ Þ?Þ†(^”š‘}bÙ³R¶Aü'åÙSÑØž‹NëI¸Ý_‘"•êË#”º_×¥¿&ŠTiü‡Aæs¡H½Ž4­„‚8 ÓrïÚnªø˜REê}L£Ý »·ïF¾i¯ÙÁ´ˆÿÄ–F» u¬‹¢?hMßéžX8/,Ç^ɸo(\t< ÿzÒ½cR3²®¿ðÁÊ ÐÈî…ÏŸ¥.×;~¢u$_ ®F|ßã´•/-¦‡ðÑžœ:~¦†J ­ Cþ]®)õ§cW'×Jò‡dÀú¹ÓpyW +k‚ÒÀS‡r”âÔÑVTöÝò+aBÙ_@±ü½Ì^JZ#wêܹs¡#—”• 4%?Z›á¿PÁ‹ÊÜ ŒÓÀÚãydI½$Á|ë(€Ã!뻨“7ÂŠÖø*MÝUŠD~ Ç1J¯¡>„¢t†cîIy¤µ‰È÷÷ @ê¨#‘8zá’k便a(Ûá,~çÂß°ûwnßeL)ç7‚<-õ_q½¥ö¤ä…¤,èHÊ|™ç07Øüò2) Åö zlÕè ðÈT#ËbL%椦± övÓP_`,’8±¢dªµ›‡ a9ÚãÕúÎð§;ëMžÂèÈŸPÆÄsýçd¦}:$#«£°ÔˆxOÜ›{ÖL¥ ¼U~ŒŽ€þ=>š˜¿5ÿ4¢OÃî¦;üÓ›Ìðϳݕˆþi>5:XÓ•¼s{Ñ%ä+s¾kçýþ_Xg,X€5'S¡Œ@¹¡Ãè´ÙÉ{ðÉ~ÿ`´Yá Þo·,¥E»6Ü"üðOíÛ\:3,ê÷½ïö‰‘#Œ¶úsÌ¿ñ&ÞN*y!Y<‘–¶—¹XЋ5Yj@ÈâÝï†ø²„.uEˆ½VÙ•F(TpóC`õÞ¦/w˜ÀÃ?Ë eG4¶4ÊTfüþÂC£úßñÀäžet£¤¯"EJ¡O¸Ò,¼ßQ™‚R.ÜJ~€ÅG‘*fÝ€r©šS]I°;+c“Í ;µ: ::Æ7Ñ1îpÿaô‘pDš/t¬FÕ6HapÂA\Š<ÿùô1¦XIQ(1–5üÒ3ŽUͯª5´ìKCÿÌ|}Ÿå54°Èz :Ý6ëƒY#¡ÈWùå› ®BfZM¼sÝiJò$~‚4c†PBI‚jiØ£Q4*ƒ ·PNÜÿàƒMé7̰û”Ê?ýˆ¤^ÒKÒ¿·Ñô=WSCÊŒ×Óê¼L@=$%ø LA/,Á³ƒ¤<oÙu ”¦®š.žqˤ5jWPD¬“ÝöHñÛ.EÊvByÂË‹jê´-åýïùš§ŽK¤õ6Ò2ï„ËW&Ž@•p°]lðzZ¤ÆÖK”abºO²FÑC ôÐè$bDåW·Bš¾ÑÌ­ù]q]évsßëº>ϲ éŒP ÿ ná¬Açý5ºêTÿ-1aA»Ó=úöóJHWÝ¿WØRÓV(˦aô‚—U¶üóx[:#gŽÕž«R= HaN.Æzœ=Q¸“áwˆÑ(\ÁÖü«1b4{ j n˜B8ŒÚD!f;L«`au²î[Q­0•ü':ïåîg5Küìþ]v_…|Aº?Bg7€ž¥õ?*ì«KqŸ%• ÊÈ;}™=Ÿ düžs ‰ G¥†ù<ØRÁe2ণ·ó½îu5n7ºG[ŒÓBíÝ¿#ÉŒŽ¼†ršy³×¿Å!'!Y›Y:ªè.ì=Õ›àð¿Ð-öSý÷lG¸‹±–‰”©±Žñ\?¥Q) (ÎocTjÇ.óœa“&}dæG¡Ô«È?ÙG’/=)•”F𔣭<§|ÖÉsð'aW±”щèàF¬P™²<Á¦Ýéçþ['ŠkÔ6U% |øD’ì‰ø_Dþ,*0ÿ¤54ûâCnöˆ¯ý ü?Ö/ÖMatåÄÒ¯S¤f¯g²½Ò¢eÔ‡Ÿ¡$Ÿiå«ÁG‹“ú<'œHëeŽ?mYN¦ïÔŒ Цç}LÑ ­¥Vu¯¤Ì⼫§©¾ %ùqlÞxµ¢ieÔ2ÌÑeoì¤Tn-“tæµUc× /E™HöèZ¾Ç`ªÓ¡çC¡Z<Þ0=sì/ŽcUê%â¡âñ&ý¡®Ÿ …ê+ϼ¿u«ɕv¦b}ßPº‰Â‰¤„÷ êôé{\ö–[åwT®i'"êÕòQyôx¿Ï|TØà5S­<­,öª”ùÊÂb÷ý—LíyOë?°älÛ/Ž@Sí˜&Þ,™¼<}„6cñ*ù#Ž8ê‰a¡ãñî­£qö“׳ŽèµàÃ%Ë·b×½P¼îÅPƒéìê³ôøùÂ*ÄÑHöTÛãNÐtEÃþ쇡!VIÞÄùŽk€‚·`úïyŒælÆhÖ«¦ˆ‹³dvã]Ž©€ö5*à„z¥©‰öU*h-Âz±™p¿!ÔO4~#]Ø•$n^g|™ŽõBÏh–ÑoÞ+n±D$8šÆ>·È—ù$ÂBàÒ‹iTS6Ç“þàôɬÌqßG+¾ªç‹6Ê (QVR‹¤=ʲ7ùí#/(nD!x6Zò9á@‘i;4#k°óÛ¹jºö=õaþ ìÝDÓÉJ—Ÿ%‹„M…fáa†²Æ!·$¶Œ_Fá†+ûë­ìC¡puöz*˜âÃsT1åö%êÝP¦ÊÕ wÐQ½¢NmÇÈÝéÐÌn%»2A½Äš¦„uƺG±ù╤µ¬8¨%[¦5õ‡vÁŽq '¿Sw?tµ?odìxyù>¥´ÂÚvýÖÞ’å"À(ìMä/’ò8Ë?zŽ(ÉDÆÅ8§í%´ʰŽÃ@åñBêÓ«º!¡¶ê\ÕËÝÏDrOëHÐ9Ý…t\…­|'Iøéä9e6Fbp £u+^¯ÆBÛ—–Ušˆ£ÝŒQ78°ô~(%t¸é(3hýdZÖçˆ;Õ²L{mJ4#¬J¾h¦ŽKò÷½ÇFÚíÈAyÑ–ÏÈM—{F$÷š^¡È¯ìõgçQØ‘æ)å> §V/Í æÿSË?ÃÉšG^à¤'\ÙÇKMñ- Ý»wº¤½ ëªÞ u³ÃÒ¶ÇHžíÏñY½LFu±Ç&Ó77qôÂïHË#(‡wò¤•íÖ '¿ûZ$â ¤©ÊØ,â ¬àDðOü>XòZÔÉÒ—1â[yyÄ¡§¢Þ†6ã23¨¾¦r‹ðž†rÙO* £ÒU3‘æiÕB-ñ]•2_ðù™ÆO} &PB€†óñÚC$jƒ:hSIc¿7¡þI=4ÑlKÙùQ{{©² äL‡\ÒúŒ*?\ÏÐN¶A}È­ˆW´Å»Óÿ`')óì)©h^CÎ')•åMÙmóf·5‚²¹&šlØ×Ú(ûŽœ]+«—t G~¾ÑÞ#´âSûô\¿¯r‰ütJpçÎ6º¡%'&{ss»*’/’òHÇäù-[4Ûä>Ѽ¢0+³¯,O+{~_î¡Ìï+}ìÆ˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜Øß Ⱥ0,cR?Ó2ÏA\Ý•TiÓºˆ—ã`L€ 0& %Ä.©ä:ø]­kúû33Ç-Žä9öÈ@­)S~ÿ3 ¹ÁÜ;…Tw %ºH),¡äF(SëiãgL€ 0&+ L52Õ }V{¥„†Þq-ú¬Ç;{;?é÷ßR+r²±I V”©_æÙHîÈnRÊ÷ð÷’–¤½=sܸm±‰¥bL€ 0& İI“ZZÖJ©ëðw.ր˜@Æ̇ TD êÊTJF`¤²ÄÃRªŸ¡å˜åû¬¢ÈÙž 0&À˜@¬HMœf ñä;Lj⾜LßÔX••åª_z4£/Q¤ÔcRÈ o»‹r2G­Œfø`L€ 0º"°øóù«=óªç¥Up0F©F;àÌ‹?›ÿU]ÅÏñ4Q™¢©=¥ä;ð?³éWajSÐl˜`L€ 4lP¤dª/ëtj—¡k;Ÿ§üv~Ö†ôZ4¥ÅægMí oÛ›Y‘ŠUƒ 0&Àb€Ý§¡oƒ,¿àoNiŸ ¢± 1B *ÊíÚ³›cTŽ?µ FÒÆb0&À˜ˆ êÛÐaÞE}½S=*¡r …@T”):þšû{¼Ø¼± N`L€ „ >Žú:ûÈŸPGþ½_¨±2Er–œ#%_Ú¯Irâ™`L Ñ€2õõyvß×èSË Œ”@•):Ùœä¤s¤"”ý1&À˜hˆ¨¯£>¯ô« 1 ,s-¨±2™ºÓÉæ| g-äɘ`1EÀîëÐçA¨î1% S¯<5¾µ‡0è{Fl˜`L€ 4zôY4$’ú>6LÀ&Pce çJÑG‹ù[{\ ˜`L`¿ €~ú<êûØ0›@•)æÈêƒÀ=>š˜¿5¿S³æ =:¿>dà8«F`HFÖ­RYCqàaÿª=ɾ™`±M€•©ØÎ–.„@êøÀ%ÊRcòþÎ?‘œvl/2qúþ¯X·÷mœ×›ñ¤L£šröû•æ÷K|,:fhFöÉX8»@Hñ¾3vª}aHÐCÒ3ñæÝ?'Ë×/Ä©f?•húÖ,òOûýsãÖW<¢„ºS“â¼ÙŒ÷Êûà_L€ 0Ú'èµ/%ÇÀ@ %#ëZ(RÿÁ' tœ·ê^y¼&µÁJÊáܼ¸Cc•ê˼-×ÈÊ©•4)qrjFàŽZ »Žžèž\ñ9Îü¹¸Ž¢äh˜`a ðÈTX,lk0"u‘²¬ç 8Mí¬§vÖ|YÿS"¯/ÖÄ®‘Üü ä<~ÚcÚîG]j¾™™iŸ“{eÆëñ¤ƒæ@Cˆ™ð{aeþÉ}HFöéÒ²2ç‘`D¼‹<"nìŒÀ˜¥¡Ïcñv…õQàÞ,–Áïl[²¼[QEÙž 0&Pxš¯ s5#@ëbÐ…PðÑÁa;dw ä_7/–¸M“7JM¼…/¿OÈ f½æö ÷6ûBaìž'„Z#…NßÞúûµÊØô © „I©ÆõŸP–N4 ã5ZËä ç0S™XR¥Ãî¡‹[ ü€)ÉÇS2YŽ?]˜xFii¢´å…ê@IDATµcç\-%Ú’ýöJY ™Ÿ‡\4mù¦2ôæ;rO™=Û‹ô}ÛëÒåã^œÈœdXÖÇ)ã³’ŸÊŒ&´©É‘XktMŸVæhzà:©ÌùHÿ6$ânüaP¶0Dñ¢ÔŒl{ýšÔû1Š8‡hº¸~ç`ÑdT·:~œkjzà*(\Xç¤t¤y$ü~·!¹Fn¥‡çd¦¿Ü±Gç¿Ë*s½1å@Øýæ„ëÁÉž&ݧúïÙ^j÷ï!¾€…îž¡þ)ÏòÞäò»ÏÛéiiÃÃ\ŒHMTB®š•™>×ý€Z»y”£=^­ï ú¥nO!¾?…e=ˆß•î–+6б«îe¤ó:(}SSüã¿o‹;çþ¾)S’wn/šY^™ð•)^~ÿÇ/¬3,ÀˆÜT(ª'Ðbö”É“›«¼à8<û<üÞî„1ÒÿØ«ùÁ|(«{Ì]Ó¦ÅmÚ9œßšȸ´ÔåY¬[„ðžͧ=Oò`L ¶ð]låKŽ€)!k|Âa}8ç½íÔ˜jûÈ¥HÙ^fÒ0Š#Wº:ô)ãìé3ÇÓyóé^é²ÜHFXl{)Šº:~K¯ï¸)ÛÊë‘3 &ZÁ¢ÓCüÖè'FÍ®¡Q—"e‡…v²õ#%¥²¤ˆ·ë¾ôx‡AJÄÈÜc=³kWñIHGG(=Oºýøý pžJýïx`rOr“yÖÉð×Óž9n¿6)ÞrÛoÙu ”¦®½zÆm¯5J¬&À˜@ À#S “öwq6‘}ÜF@:EÂ#MBiúO~1š¤z…º)Oó\·:ø¦)„nyʵ€„;Í T !âÜþ¡é­)÷?’’ã~Û±½üvu«ÉoÄ~§DŒ*ýêŠI+üÖÌ­ù¤è•­érû ½Ïñ]ƒ‘ 4L3NÃõ%:Z€F˜0%·Ç«R4 'toü^aJM[¡° Í4 bºJ ³$­º×=jg‡eø/‚ᬿ:€îñøCHË$Ç^í¶Hq¶ BT¹9áó• 0&m¬LE›(‡}Ï*40R!÷R‚*ŒL©`X7%`/÷¹™=!ewŽ?u¯GLQlìeÆÓW{ò‹‹›W¸•W¼Â<åÅÎ:œFÍ’è·XpþR9w{M #AVi`Go:Ö’]‹Q·YX$¸Ü–Œ7ö¶×ÁbýU)S-ê‘°̽ք+KäCI*3–D: 1Aw›†5aùe¥7¦„Úño&À˜@,`e*s…e*GàŒ>þ5oÉ Œj¨Û±øúáœÔÔ½;u÷R-ÆôSøQ ©ºA+‹hí•;ÈÊî1²Ó1ÔÏîuÙAËZMWã\t…2´—2¢S¹Ñ òXÁ"ð•c÷¬@ú³x©’5ínž1ùöbü.hlφrƒá´=Z%ôÅ8ÚKX²ÞV.pöC"µßÉ#f«iðIZqÙAvŽÁˆWŒ :?…¦´•Ó£-œåO·×—9ò `L ØëmºÉ΢î'hÇ–&åxšÞk7OÄtÖžž>%¿†‡³±¨ºÛ9%cò9 ;ûæ»í£q#΢]„î° Ãš®Ò¼ú×d?Ã?<Ê k€Û_ÉÎ@ël·ÝcÌf= )»rv´«î,:k©œC ~LÏû O†¸Ã×ÞUè—Ð6y ´¤B(T×;veWe]%鯎¢—=èQ/iŠ uY™Ü”¦ñ ·ðxqFŽ¡*,¨gØh¸xdªáæÝ~%y=íù\+ëRì<›š‘u,ÎezÉ«v°aº–„]w´“M4oÿvŸ]¤ŒÝïa›þH7n¥e²Lc”™eõ“ÿY ðp÷Ü¡“ðèÚFì–»»þn‡âó²ûL*Ä;£>çãÈ„‘š®¿®LÙ§œ¿–¡2A{3f“± ñB«‰öyraœþ˜ÔVÑÄ›%ó‚7à §°ãm,†v~T¢¨'EMÇ.=hX‘üî¨÷ÎÊ5–_ 5î(9e#EOŒQ„ïêÝöσçæ8©^5E\œ%ƒ·[–ºSt78GVÐùOøÍ PöÆá™uñºç½bSµ^gdÝ Ê A;S}Y™X5 ák^iKY  ë8Ei‘úôœÌ±?E";ûaL€ Ô'™ªOúwÄh* G\ŠÏC0BÕxްŒopžÑ{X®|/{‘4H>NL’âötêoƒEëMËzÖy“µShZÄGèQ -[“êO|÷îó¢`q.&³¦B1x‡O–uÑÅÝp[Åä13hüaYÆçXî½Yú-¡Qyô¸‘ü–²æª]ÁíùÁ‚ÛÉOÎØ±;´y,”ž_p0æ‹–*Z·÷±KqThUùm+DJ"Hbæd¦ÿ ÓÃàtu‘’¿ªx¦6¯º¼bvfú‹ngA±{A(kºÍÂ2"ÌcŽ¡‘PB¿Î£ôßy29yL€ 0&PV¦*ÃÖL Rsiÿ‹Ô/ûcL€ 0ÆG€§ù_žrŠ˜`L€ 0:$ÀÊTÂæ¨˜`L€ 0ÆG€•©Æ—§œ"&À˜`L  °2U‡°9*&À˜`L ñ`eªñå)§ˆ 0&À˜¨C¬LÕ!lŽŠ 0&À˜h|X™j|yÊ)bL€ 0&Àê+Su›£bL€ 0&À{hç°ŒIýLË<YÒ]IÕI Ñ´ñeϾS¤„Ø%•\'”Z£ëž÷ffŽ[¼ï'«ÿ¦q®²Ûþšÿ'79%U%PV÷…X­kúûûSݯ*+öÏê‚@ƒR¦üþgrƒ¹wjšeXFG)¥jš”`µh–$¼Þýn”­0´¶ï,P» 5ð¤fd­7 sj³Â&O<öبB ´¹ÆÈ{îy4agBÞ]>ÒÎmÿÎÿF“³œ*©ûYT÷MÓz¬K\—'üþ[¨î³aL  4e*Å—yösÝÓÐ:÷9 ³:î°Ä½:ˤ„x½yÅZT¶YPX$~Z™+¾ýeU‡ŸW®}pw“Ýwß<Ú7äÙ)÷!°…¿†¬TaÐQhHÏ9»vÏÑ,Ù©Ï8ÿc­$²Rvi×Rx= F¬ ,eaâÑÿðäÁ=:ˆ?×oIÊ/ ^{ôɧæ¿àÓoJ=6Ć•: Ï­cÇßãñÆ=üOæü/Ëv¾avZ÷óv_Ûï´yß}þñ×@Ôë}Ìçl]ô{1,G Ö§Æd‰"%=öÐÚý7]¨Ô­C¹ðòˆÏ˜›/Ô=´§ÔuïC7ßï^ü‘âL£< Áœ$¯—ä§tPz8ÿBÖ±ŒõE`OÝïv]>r{Ú÷@–†Tïë ÇËjL –•)‰áês0"5©ºý²Óeœ·Æi5Ö NÄ‹¸y½ñÙ×Üyïy›*ÊïXW¨H>’ÓKr“üœÿ Á† D@À]÷¥Ôºé¾´sñXC¨÷¤Ž½0Ø%³ÊTJŠ?Ñëõ>Õ¥} qãE§Äº“9LÜ:µm!šµh1óðÃo!…*&å-ÊV¤H^’»Kû–Šó?–³‹e‹ETg:·k©’æ teȳm},òc™˜@U ÄjÓÌVò.Ó²:]uöñ:HU5[Kü·kÎ=A³”èp䧇m<þh¡YÌæ{©|ñ$/ÉÍù"l˜@ PÝ¿úœãuK©Žê}åz_ÅÔ±w&{b±Sµ×Ëx¼úÈÃ{uQ¼Fªf…†øÇä¦M‡"¤DüÅá/×QØù^*_"ÉËùl˜@5 8u?>1ñN«õ¾š©ãǘ@lˆEeJ»~Ęã,KuÀñ<½…òRʱí \u<‚‹ÕÑ)*‹ôö_*g[Îÿ(d>±_ :¤”jéM©ý‚G§öëÒÀ‰¯M±¦LÙ£Þø¸sè@Nœ#U›ißoÂ&ŽÄ³U»ög"Ñ ø‹µ·T÷¨TÉÉù¿ßONh-pê~“ÍÏŠÁz_‹)ç ™@݈Ie zÐÉæ8³ni4ÒØˆcÓÄx¥y<ÝDšê‹µ£eŠäJ$9I^ÎÿFZ 9YuFÀ®ûhK¥¦wG¤±VïëŒGÄj›@¬)S$.4ÙŸˆ‰5Ùj;/j5üÍ“¥îõ´C$î‘©Xa\’ï%#f $'É[«@8p&°Ÿ ¶Tê²#’ëŒHÇJ½ßOr€“¹?ˆµJEòx4!›â[{Ü™F±O]j´Eš†û¨Q¥õv¾—ÊOrrþƒFÍÆ¿wŠ [vTñ©ºõ^TËþ\_a¤}ó«Xùׯ ÝÙ¡ê¨.i®û±V﫞~‚ Ä(XR¦Hy²§{”PQU¤Ò§ÿ[¼þñâeÁ¦­»Dî¦m5 £¾VÊæë4¨Î޾¨²®FËòÏ’‚W*g5‚jü¬Yÿ·šõ¬X³áï½ûægß‹7>­Y9ß+Ð}X8²EôìÜÆ–«U³dñÇÚNÐ0ÅÛŸÿ`Ûï+òðÆ'‹Å7¿¬‚w¨¸âÌcE¯®ííç>ûî7ñÓÊ\qóE§‰‹Në+þóÑ"±yÛNÛ-Šÿ(ÏÎtã–'ÖÊd,ð‰H†ù…bWÁnÛoeepGþn[qÞò¿uG¾ýó¤£zaˆ¯ÎûVlß•/þûñwâÄ#{…U¤èš–[„—W ÜbMTÿ#´Ã!–þ±N²ÔïÐîg ²û{G´÷’~üÅ·¿ñP ®=ïD±eû.±áï=k¾(ÌÅKW‹ûo>_ÜtáÉös·bô¼:ª+v:Ñ?w=kDÉâ¤0ú'K#S:éà/ÐWŠéˆ0’´pÉJ»C8ªwW[†ÕX—rÞ’ßÇ‘Ixt]´jž,Ü'²“’EfÇ®Ñ#S´fÅy«&ûŠâø£ýí)ÎǨ—Ûü¼ò/ûí½}릂þ:´i.¾[¶Zœsâno5½Õ5Våª)ïz}¾¢2¸/¡¨¬ÓèÖÃÏ¿ åf»hת©8ëøŠGHßÿr‰.Ñô^»–ÍÊ‚§QØ\ÐÏV¤ÈÒýr°mg¾ý{äuçˆCztD}ìh—wçáPÒº`Z½y“$¸•œ9G ØÉÔQ]qDi,W§Ž5–ôp:˜@̈EeªNàЈ™&É%gYá™LêåÅKx“ž8çuL¿ ®;ÿD{zÏv ùG‹ÚéÍÙ²,{ʂ޺߆‹ÃÀu:Ý:´*¾C(V¬Ù€u'›ð–ÿg™íЊž©½éÓèÉH!59£›êÚ-\ •a·¡ÑRRphç]Êe…¦UüŽ3£¸4Í·ÊÑ#PÀþ£»C.P> Tvï¾Y…és2Ý0N†ÎDjÝ¢©}Oÿ= ³=ªEÓò´ù£EÓdû¨nêJ™|ب”@,)S·Ö•&£êâ¼á“Þ£S‘vëÅPhV‰ßY(Z.Hÿ8ãØ½"X½~‹xïËŸÄÝמm7ðû«=Íá^:.Z E!u¿¡S຦٣_ú‚øúí_-ZÔ)÷0é¨ïøÃˆÔx¬Â•AJ•7ç‚~¯Ý´.e†å«×‹Nm[Š÷þ$úÒÍ^ßWæ!Ì M{ÓH/½¸e>œéˆ‘W2[¶ç‰n˜F7MKìÄúCǃ8ß]ð#ºuâÀ.0Ýw¤-ƒŽ)H=«‡ºâˆÖ®\ïC.rb†@¬­O¡ Ž¿ú™ ) ZhžWPh¯—¢Î!ž½ÂthÝ\ü¹n³ µU4Õ°uGmß~þÆZ/1Uh›–tÒôâwÑ¢õ$ôÆý#¢“é{p7ñõ/¿‹å  ·or Uºl5ûWʸlÝTÍB‹ÎÓ±(StRVK¡PÙø#w‹ýG÷U5½ºµÇyN›ÄúÍÛí)î_V­- Â0Mñ<Ž88#³#®$6nÝM¿”¹‡ÞÐ:'z¹ ¤ûKtëX²¦0Ô_èoRÔš&'Ø‹ÝiTk6s¸Løå÷\‘_X$b#FoÈ›ìú"BÕ•P‘îï’ÒîzÖpÓÂ’3$~x&­ ‘LËï`GÓ3o|†­Û:Þ²Û‰¥»üNLjÑÿ½÷•ùðKâF,†íÅ´´NêÙ¯cz"룎Á”ĈĤTOý÷SñøËØþOÀ‚Ý£zw³wô½ðöb*ìiíU"ÂzÅ¢­k JD°§FO€Îsr )%ãS.q~FtíXO± g>Ñtv“¤Dqz¿ƒÅ{_üd?ûög?ZÌ>ø¬þ¶²s)Êökó¡Œvµ_*B# dèØ3Ž;TœÙÏZÃP¿¡¿©Þ¼òþ7x‰Y%Ž9¤G¹5‰´K°°ÈS_zß®FÓhýàŧ÷åº ’3&P¯èM¥F&Å—ù È@ך  }×Íní{£O¯îÇŽºáÜWýGéíXÙÁ´uÛmhÇön¼)'aJÂ14JEG&TÇöz+RšÜ†¦;vc+xrbíŠyô…÷Ä¿þ¶ôÅǺҩˆtê#-È¢yõeÊòÐB¶×ß}ÿãGõ9èÐúÊÿúQñRŒ«ßw*ZgH»aÝrP¼ê\qÍy'ØkéåâÓï–Ù‡ïN½ïú²zQu¥>ò¡6㤺ÿÓ²UKžlÒõˆ'Vê}m&¹ÖÃŽb¿Wë²ruC ~[ѺIc•cñzÂc!ÅÆ­HQÀÕU¤Jž )qM’èzl˜@íp+0µSÅ¡ÓâöP9HÁ"{Ú¬± Ç3б KpnU—v­Ê) ‘ëJÅ\Ù… 0º#¾7¯»ø9&&À˜À^è%å¶KO_-ùgJ- qqöº©³O¬øˆ†½a &À˜@`eªŽ@s4L€ T@@l˜`±N ü¢ X—–åcL€ 0&À˜@Œ`e*Æ2„Åa±D }ú¿ËŽîˆ%¹X&À˜@,`e*–rƒea1F€Ž )ýîpŒIÆâ0&Àb‡¯™Š¼`I˜@LØ„mÏùÏ'øèwû )úÐ0,»qëNû<¶oùC »r ³5Ä|¼¹ßó;µïÁ‚ìoºè”˜L Řˆ&™Š&M‹ 4B´£î˜C{ˆ¿wä‰é¯~d§Ž*xã“ï°Ûn%ܺC©òˆY¯},þÚ´Õ>9ýu¸ýˆ£ Ø0&Àö¬Lí¹Ìid5 pÒQ½D?(S—y¬X³þoûì' ŽÖ¼cð™â”£{ÛŸYZ•»Iüc`?ÛïÀc­AŒü(`L a`eªaåKËêœ@B|É ÿ‰¥×|€ }îÅ9´–¾¡Gm6-=l¶yú˜&À˜ÀþA€•©ý#Ÿ9•L Ú~ÂÇ‹‹ƒ†øk èãߤD…ZOEß\´ôO[©â)¾PBü› 0ÆL€ 7æÜå´1(èŒ)Ozú-A;û®ÀÇ+2—ŸÙOügþ"ñæ§?ˆƒ{´¯ÈÛ3&ÀV¦]–r‚˜@ôL¾k°ØE§÷ÅU–}ïd¬“¢?·9úàîâ¨ÞÝíw/_½;þ6ºùž 0&Ðh °2Õh³–Æ¢G@ÒW¾#0ä>>̆ 0&°?àVoÊmN+¨#mZ4ƒŽïSG±q4L€ 0ú%ÀÊTýòçØ™@£$ЮUSqþ)G5Ê´q¢˜`¡X™ %¿™`L€ 0&P¬LU{eL€ 0&À˜@(^€BDá«®ôç÷½cWÕ«{ñ.Ý»W5,ö_û¢]ÜùÍù_ûùWÝ¢ï$ç=¶€Ò6Ð=Æ}¿Ç¶jw{h!èÞý»j!±o&%û½2å4 –eÙ‡ Òç2–þ±NlÝ™/väØo­QU-m:âã<¢yr’hÕýþ7‘—¿Ûn ™ µa =*Øu±[$XÛ„GŠ·,M’Åýú*âÑ19ŠUT"ã@""@e€¦ùß.ó-»j)ïI˜†–ÿ»‹ÄÏ¿¯Å_®øuÕz|õ"Œñxtѧ¢Ô¥8¢WqäA]p4‚^nD&ÌcõjÅu¿Vð; ”>`À¥IÝ=|x|¼÷N%´pPq2ßJÔvJ(ŠÊÒCÄ[»e3U¬%kšG %ý †¡ß²tÍôÿýïé¤ÐQ¬j%±(%°_*SŽ 푈¹óÛé6o{±1©øWC–|,XM{TP´ní‹×Šÿ}ö”¸âš³‡÷êjŸÏCJ›Ú'@eàç•kÅ‹ï~%vbª.òžRëùoš¦x÷‹%âý¯~T? -^lÓÛˆ‰­ì:a¢;¤ºAé@w&âðrÐdçN±ù§Ubá’•¢Y“$ñ}Åq‡õ´_b­ØqXÛ®êsÏèWg<<QCؘT‰ H:­ŠL™_¤ª‚F"@_/YCŸü¸’Ê•tZ—6’Ší’8wD»]]-SÝKý+òhÖ{ßÒ' §3ŽBü¥/´TÉ«ó˜»p%½ÿõÏTfÔý MR÷\*»Ö¿¡¥yzî­y´æ×´×Ù™¶¦÷§%3¦ÊPH¥ì`õô­§—?ú¶îÚG:Òø@(ƒ–k妴sO!í-*%UÕÈåRˆCîÞ¡-õíÖžÚf¥÷IÖp¡Yï¢íÇT±F2µQܸ®3þ67õþte·>ÜýµsláëI iÔ͹Œ7i¯³-÷]Ð^Oiÿþ5wO¼ï¥G¦<Ž›«Ø„–*©µ 2gZ ™2_¦A,Øê‘újÑ*|}¯F§Ñ…V§Nšdõ‡SÓ·%i'Ó òŸ¡¥Zf öŸqÔ`r:Ű_ÃèÅÃ|æ.XIï}½ôÖ=K¨êŸq()óQi…T s˲DmÒRiÎË "µÎ;‚vº{Æp¯&`{©¿o}½x yð¯Ýº›6ïØƒIë‘dò+)èÙÄ‘ š-Âzútë@'Ö¯J£e%©2ë]´ý¸ª´¡È&‘b¾ûÏ·»Dê]+õÃ<ïÊ)ØPz˯ƒ¼ÑÉ©3•_|ëÛiÈC׌› ½4m**F*ËF"ÐjÈ«÷ùeêóûiùº­ôÙ‚5Fgº2õÈH<šü˜IË0¤ŒèÃï–SNVìNº„ŠmOD°~òÖo£÷çýl‹ºç’5UýkšŽaÍmôÓò´nûزYIÍÀf>.â%R5rA»Á3œ:¶Ñ§óWPPñR¾{ ðî-`F­¨:¥„J0콋ü;·ÑÆmßÓ VÑ¥¿9ŠútͱÌöJ´ý°[õƒ‡Ù˜Hy.ºvô¹)iÿÇDêHï›I×FÕW&qa.cB5õò[î\ûÆ33>E&Taæ^_âš@ ‘´ 2Å/SVï³Fª¨¸”>ø.ÊåtC#ÕHÜ,OÆÚ±#JKè­¹K©O—ö†:wnv³9±¼àM”!?>ÌDûïg ¡=ÆÛN!YõÏå^…Šÿ³³K`å¡}Ž*ñö¡&YèìªíÞP¹aÿ”ïî0,òÜèlÅÝÎîØGï_uÜ™ o[©?µ‡apß*zâõ/è“GÐéÐÒò:‰ {‹¶ŸpuFË€‰÷îîÝû·ÉéÔiF†öX#-ò¡8Y¤âPŽ.eµ}vøðãG,_þcäà™~†êPTH+¸§mþdaÍ*~ž±Ç6RÖø3ö؆cwØ!Ú««œ¬¡`™J0kjl¹X^–›å!1Ìgà«Å« cóu{Õ=—Îêú—9D³?_HO¿ù%í(Õ¡=š~L?‹Ö¤Œ4HÓ†å 1bmÔVOZïŽ Kf®ùïtõ¨“HE«ÑØ.H; ”†öð/Úd&Dif½‹¶ íFŸcfÌ*s6ï±ç7š$¥l¤”C1´WW)X–áže”ŽÃN;é–JyYîè̾®ŒÄy@Œ´x2þ2 ÛI•ahcѺ|cæVS›ÇXUÑX&žUöfF±MUUÃ}CUqÐ(Ìg€gN2¾v¬{.˜Uõo©WaÎÄ|»»-L?Í N¬‰²sàÙ‚Lúò]½éÔ×ç?­0ÜWð,Ãx •YïÜ–DÛ·¬Ö«´RÈ15#+óºö˜µÇöJv ,Ë–’â½ ²¹±±6­Å÷yv«‡Ö"O‹~°ª¾L1Kɧƒ›ó÷Àf*@»]m[¿,ûøù² ·ÐN%VUæ3Àu_Vî³uÝsI­“H±‘ýO+6ѯž´Ñ;ãÍ«©oHF¬©b»«Íùqk¨Ìzç6$Ú~bm("u¤VÊsÒ9%)Žvp`[†nÈ&+íϽì/G¡¬MÚ©ˆ ‡Ö!мްq–ÛìXØ_ñmD‡ÊJ^ö#e×`È×üºËðóÓ˜/r»–íPÈe>ìÕÞîuÏø$Zÿ¬Ù¾{?}ˆÙ¡<„·Å3àPÀnÉ=×aØ1Hz.,X»ÄH¸|±³ÞEÛ­˜ã˜dŠ Ï½9]ºžŠ:üHÅœASGdÙXƬvíÎĽYnA¦šºZÉýZ4™â/“örͳøŠJ+àtÄ4‡œV<3쇈½¯()«Þˆµ±âþ--óØÇþ€+ãkçHýsYy¢Åç?­¤ .Óº”Ãì\Ôe ¡®~u÷¥íh+–úab«¦Ö¬wÑö„9žÜ_˜öR§ËÝžÍáÝÜOM—eceÅÑ765S-ºßkR€ÅͪhÑOW+É”Ã{eØx‰»¼NcHŠ;–ŸË!Bã0Ÿ",ÓêžKÙ˜ú7‡µöÓÏpý±ÆßA‰ûŽæx¨¯™ÈD±ÚšõÎmH´}Ëžî/Øîˆ,·ât´÷HE¬­²u`±ä G°ÜÂnÊÖµÕ|…k±dŠ;SÕÏ/`žÑ BSW»v€ „ŒŽÃæã²ˆ‘Ï@  Z¶Îb|RÄ»1õÏeåçþ¹ÔO'¾XD7|XB'tsÑeƒÝ´·\£wVûẖ‰“ÉiÇR³3íói4SU¨ Rr•†ª>yùZ$vqÀ5ÛÚå7#Ýz”‡ÚFÔ³•å”ß¼Ÿ öüPGnI’Ôï¸q´qÑ“¨,œž6T¸k -xûâðöÎ¥FÞ²=´}ÕÔÿøñ¾—‘0l,o‰ì‰ "R·TìïtÉ&È:ÂCï­ñÓŠpÇÖÚ‰ñoî<͠‹Á«ËüÄñ?X0Oǽ_»W¥ß¡³V ý’Ý*ÉN8•á¾Çu=fPÿiW~·0Mœ`!î·°˜è…g$vghj´ 4ÄL7ÙÕ8ïçé ÇW sÑ ÿ+5äùx}€x3Ãr¬#:;höJ¿A¸6îÓèÏÃÜôôÂÄO§^»€ÒÚ Ò}ö]ºæ ÁʼnV…@"½s«j`;­Ù _U•;N~qcâ °jJ½ ½pµ^cŠƒ0iÎ'zgˆíΪ=Õõo–'Ã-Ó`ë_ µ2ÃJáAx^š*°ÛC`Û§g‘?$ÓвùÔÍ·‘d|ù'øÃa-4q¡Y× …Îêã¤)§§à¼Dï­“Ë]¥š¡¹ØÖšò—(Ù”-•,i”VºÇ±ÚÉÖŒV¦0¥‹v¥ñçj—ß·æg¤Ò#?T|ÁÖV—¿Fæ-èGZÎ *Þ·UnSNo6e´LÃ~3ƒº ýsµé„¯t'}…”Ñnp B@¥¥!`ͯ¥¡R«À ^3$£üfÞ-eï+ÝEîÔŽQ‹ÃÃzj ´†&Ê“Þöoü (‚@Àž2C½”uƒ±T,¡†E8üZÔ4/ûXdqqñ´÷T‚s¸3×î‚VêÌÜKƒK€Ã1Ó“73pýÿz i•}2&'°ûþbÍTÞÖ}t Ì#î#h‡»—)Z£ö¬qé Í“Øðšµ2iÀã„nNÌftž°Úxˆ‡E·Z[~§F•%ÝX´\5<¡‡gëšr%kYþÿ®Ð1ÿ,ªÚ^Â$Ê?3ÍŒò›y·”}Yá& ëU;÷q-¥dö ozWÃM‚ÕFû¶ýXUÜ”Ì^ÜTõ[ì†@u`7Él&Ÿ‚!n,a(†>øìW¶±‰%½ˆc/6ÀºwРA½.ÌWéOC݆æqÂI5íeUÎ>šã ï·WÛP5EiX;Åþ˜X;µû@)­øµ€¶¹ûÑgâC#L”Îëë5œS²úÈN{œ—Ú‚`nÇŒCžÙÈ.!8ðð&»…X1YÊò§‡Š û,žU ªMª™Š,ðf†Žùg‰?|.Yå7ï×Rö%{VR§~ Ný}ﳩÿ±w“ ›©}ùó)ï‹;éÀŽFq=iÈåmC%{Vµ”â‹r´@™Š±RŸ—.Ì çû :f2¶áÍ ìÈïêÃ<4íGÌ¡E Pì×èÕå~º¶2 áàâÙlj= Ãó^˜ýwÓ'Ö:ÍŒ~·ê³L¦xc Õ÷Ë7‘*{èWÏÀê }´.@7ÃØü÷Ö|{•Ÿþþu9ýù±fª¶Q>c4wS€ÖaöŸ•!'˜OY^HlØÓ;ñÅã+Yj—?2¯?UofHVùÍü[Ê~Ǻ÷©ÏÑ·Q—A4üH-zïr ¶p VãÉåí}Ä-´{ã*Ù·¦¥_”£" †ùb¬Ôå»BôÑz?]2¨~gœçbºø†j ?D1ÞBD³1ÏAóÒî4 ¼Z«§`œmú#kÊ"1¹ðùƒ´~[å»z’ÏÒVöö0Ü\Œg?Òrª6‘j¶'õpàC¢úãŠûgai™”P u‚;tÓ™¥ùÆšG]å¯>Yå¯}Ÿ–ð[×TZóýdê:ä2Ç|ª@”k)WJ[j×ãZûÃ-¡Ø¢ -¡™Š£rù‹¼¡ð?|Åó&BËB€‰Ãy¯UÛÅÔUº1Ÿ5ͺxµïÏ$CÃP䎽á)‹ÅެÚQúýÕæ ñV_`÷g½Z·Ö®¾´õ]ëå[ nuÎN ûÒ‚öÍÔÄÕ—ÎÊk‡²üV–ÃNylú‚x«/Ê÷Ñ·¯œT_qM ` ¬ùtµEQÂ|á†÷aâ¼Þ—݃2:+§}›e°»Ìv•ÏÄÏ {"…ì_÷ŒccꟇ½ØD `yókß®5›\=|k)CÝO=2à…ìÍuúØ×m]Á¬÷ð^´ýºpŠó<†UmjH­ê®°±Xœ5eô îÖCZˆ¿rªdoÊû‹{µZ,™Šö¢uƒ ¸5k‡ ’ñ˜°ŒWMc÷håIƽ[Rž‘˜¥z\äÑšÖŽ©±X6¦þY3ÅîÒ½á¥xX¬¹‡luáp”gLviã5Ü>°ë§³þ#ëÝÄ@´}‰Fï"MŸË4ft¾ÑwHRBŸž¡ýµ²·½Üµä?mŽ@‹%SÑp÷:1Û‰üøò¯¸"ZÚ¦:Dz9 cªKŒÀZ‰yFŠõŽål\÷\ÞÆÖ?¸… rÁ[ûì4êâßþ·Ä&ÌË­UÐ`¬ÉÇ+2õmËÎHÝ Snò¸=ära}>h¦x Xƒhû±"{<_yÙž€ž*tO쉚8&ËfÈXQ¾»‰o-n×ÊhñdŠ¿RÍ­M*$E§Ó6hßveÈ;f§VÉÝÊžIË‹Ëõߣc¶íëž ÞØú7 ³ÙnjxÏháʨ–‘iŽ!Zµ‘¥ß’ ÃÝ}°$SFª—RSSŒI•Ëîs3ë+ŸÙîy/Ú~}HÅu o§ðpÙÖuk–ㇴ[WM™ec»K_õéÝÍɾLX~–#ТÉTÕ˳ExÊx†×‰•ʱÎ^p»å@Z•!ËÆö=m3¼ºšZ•kËÇ|Úg¥67v®{®+ê¿{ûLêÑ.•zÂÞˆ—`iN¡S` ^ò y¡Eì•¢6iƒD¥¤€Laã5 Ù—VCKɘõÎmH´}Ëž€*"…µ‹Ú ú‹¶©#lKP¶G*¦NΧŒùƒúþ7('BvÛÊkYM‰ŒšK¦ª^¦ ௌaÞwÄRÙÐLebºµÝËIJõlã©’—mlͲØM^»ËcâÆrÝîšmÛºg,©.+“.'gèÑŽ²SÔ¿bõ-Ï#Y‡› ëæwÐ%ó¨ù/”&©7ˆTÛôJÇ:|¼6eFFH•×îã5¹¬u…Úõ.Ú~]HÅu>’H±1cÛ¶iãÁ¾Ò^µG\™5Ed–©@íKžâùx^$œ:÷ì¤Íž}¿<デñµ!‚@ÀBê~#Yx“C•U¸sa¥TÍ꜎AÐNõ3:k &RNY2ñ,¾Þí3 yYnÞêë8¹gkHù î‘Cn¼QûU0¹°OÝs=$ZÿLeüã%eÂëó¹id÷,âu»6ÑÑ%s©= ;½Œ'¤á«~xé4$ÇŠÀ²W<ÇÈsHÙB:¬ô;:±è#Ã>*K+¤ö.©L¢ì4(¨ÌŒ ÊÈÌ ô´4c™ˆÏèk¨MDÖ»9P´ý„k¹I¤xj¬úÃ'Î }EË}hª3 ›–…eÒTÿ­+—<‰æQÄ„ ›äRôQŠ´/ïÓ‡gØD\!F @ Å’)óë4L¤†Ñ*ÛZ¸`ØÝ5U£T­˜•/µM²,,Sÿv˜uæa9adk g„§€syDˆÚÏ€ö6#{fS* òŸãË,ɱ­&ü¼ð3îõxÈ‹õùÒÒRh@Çtê‹…xÓ©œÁ ûØâÏ1ü·†2BûIb«õ(AFÙFÝmÄ mϪëÜB)Zi”رŸré~êQ±–R0„×ùwTwPûÐÊRÔÙ ¾é*sº¡‰J§ÌÌtÊÊ̤Ì,)h¦ØfÊô†´R,Mízç¶$Ú~ìõTGLS3ÅdЉûà”——V,ýþ»×еöô‹ïâèT&ó4ËÂ2-ûiþ?ßû¦â³×æ*—ƒ4‡ç,ð«K<öÐ¥ÐO“ŸùþávéÉ”EäÝ:°Û”±Ê‹GÝ‚À³}xÖ¿HùkÝ—*ÏÊN© ²¦ÉûwÐt0«SFÂ[tMWÜ>¦,X#Ái;h º¦ËÔ!+µRÎpﬥnj¾Í} I’Å”ÅÜ[vÃÚÏ@·œLÚ[ç«ù¨{ýÖ=Òªúga)§Ë°+bM¯aǾ§Ø8=Ý]F{J‚´ß_A.ÿZbÿM:<¤WÈ)ä“R ¯ÂP8H”G/'O¨ÜÐ`qÇ“&(Ãn»C©4´t-O;Ž|HoÈT÷á9_B.ôÁpýY‡Lú Ëžµ—­T:k¦ ‘b"•Ž-¶RLŠx3–‹ÚõÞêÛ~X%i¶/Þ7&°fÊ$RìgÄØ~ùñÛ¼ìœöÑÐ]¦æyWrH‡fÆ4k¤˜Hm‘6¯Yñöâo¾\Âr.Û üu]ÿâ}Ê9·öεE³*ú_‹¤}çCKuý¹÷¨_â¼F!`'2UÕÀCz¨Ü òï„Ô1¦ªŸÉÕzðBfÖŠ µO Âc4>®@bF––Ðzïp*r´kˆMÄ62<ÜÈ©Ž):uËöò±œ,/ËmÕ0Ÿ/Ôᇨæ¢WaÁ«pol9LwÐýÙ õÏrE{úwÎÑÀËþÀ¡«{–ÍÊúg‚Á‚Ÿ¯†õÍ@¢à¨Ð R&NGµñû Ï‚±¦^YH¢´P€T<áJÐÉï·C' í­AxXR<‡jò}etTÉ—´ÝÕ‡öº:Q©’…tu7Qü$¶SwÁ*Ž7n:{ va­«1ü¦8 ¿QL”ŒáIhÔØØœm¥˜²FЉÅ2¼ÇÒr0Ë,Ú>q}ëxj/ßpP» #Wïs˜µRì°ß'¼y¿þàí/uÐszØùÅZ;}¸û#¹c .5]`)ÚcÔÆUËßûê½·>ÁÝMYÞÀµ„^Ÿ›úIª·üü¾Ø®RKõÉ#òóz›{N¼goówÒfLükJìD¦ªÊ­C…EP$H¦¸ƒa2ÂCL¢øÅì÷ùÉ%ˆÎ!/{'Þ {ð­uXétÀÑv»ºÒ>gR¥äŒÿ³!žþÞ!°Ã'»ÃLŠ OýÆÐLåpcÖäfùcù¯¯ŽƒÂâ2=è÷¨ã²­N}þVÔ?ª®g oÇ rÉ…´é@i“Õ=Ë“ÌúgÁ„ƒµ0z*º6|z›çœŽ°v¶¢Â‡ç?@ixþU„ ¤‹½§›!LÊÂ6I‘Z ¸\Hñ¨´ÍR¬§îþõ‚6·Xiƒ¶â24»!è¶zÃÐnÁ5ƒéÓË ‚–åR©­GÆ6œoV'&:L¢Ø 'Ÿs0íÔ TLª`pn|T0ùjØNÊ,ïëª÷VÛövZ W¬ÇüNæa>V;1Ia¯âìdŠ=Å:ç}ðÎ×ûvíÜsäɧ]ò}èúŒöÎ z7Ç2©ƒc-¹¤ä8Kf?Rìþ€g²!<ì·J–|÷ùëËæÇ6,Móžåe¹C—æ–±oœ?|œOØEçÁZ1þ8`"eøŠ24O<”ÍvPL˜x¨/~¿× Q&‘2µ4\VNgbe*þÍí€I)¶òŠ ã˜‡ Y³ …q‚~ нpUm $‰µLüLóýy†!k¥ø7ÛAq;à{)ÆGé ¤º­$òÜ™eiím_×´ÂÅß~¹º²}E¶·ÆÀk¶S&.L ˜H™ïh“hñpÿ97¬\¶Ûfs\3,ß/òž¬}bâÄ)&R…•ÇL°X^ó½‡ÃšAh©jâ!~Å€ÈKÏ ÃèP÷îÜñú IºuýÖ]Ô¯{ÇøK‘Â|±»õ°- _â]Æà¡~©‡mI`KÅ3 `¸k~­sÜDH¿Ð9ðÐ Ë¡ÈlÃÅÃ.aûÃFŠg.DÃ|О…µRñÙˆ7‰òñ[±a»´uÃúÏp™¿Ìx30Æžñ¶C¨ªwcÈÈòÂÙâ¥VÔ?ðP=‡ªþÃDÂ$TæL¿°†ˆµ³L¢øÃ"¬Yªæãtü¬rÛ0íõ 2…4þ ña˜0„ŠÛKdŠI“)“x†Ó³V*쪇y8ÉkÆRRÂ¤Š _3µ¯ÜL¸Ž8=oV…CUï,¿YŽCÝöwlÙüı²Ý›†µ=feE’,>ÏK·¶TØ›š+S{e¦Á¥¸߇Ë`¾7˜¼ñ=M­k¦X#ŤªN­®ÕBKUñ#NìD¦¸‘˜ Rýöƒw_¿xô-W¼>ç§ìñ×^ »°Hq"_ªLb8˜/|Z¨²%á¯n6L¯ÔLDv‰Ü7ò~UŒ¡• Ï.äáæã!Þ˜H±œ,o¢! ðƒã:µø‡Oþ÷9òã¯3ÞÌ«‰y¢·J$½)<–Ë‘åíÞ§ïY?ÊúgÕ3`>oM]ÿf™¹Ü2fçñ°?×üŒ…IH´NL–Ì–•7Ö™Ç|Ó‚ØB»RÅZ)þmº_`2e¾1cˆm,üÁÂvQá{µm¡ø>É ‡ªÞ¹L\¶0•䲉Û>¦ñ}÷áûïC«Û=W8“܆ÍßLjXCÄdÊÐNaÏ/^“PYQÙæýÌwËÁ÷d2Åä‰ÉÏ^äåáëÕ(~ÔbÑR½"æûÕa«¼f'2ÅÀ¼Ñ¡îß_P²ö—%H#zð•¿×o¸ø”„¡ùRc¢bã%ÖÁ(ÃL¤ŒF¶ÆÐGå,'³“Aw0ûóÞ¼¬w0f§Â3•ÂÃaã\>Ÿ¨H¤ŒÛŽ=…´ô‡y¯”——ð †_,¼™/Ö˜_0‘ù&ḪÞ+åó³¼,·|Ê™·ZQÿ,³YMõ êú¬'îÐ90ÉágÚÔ$ñsm>ã|1âÀÚü;åh;LÄ‚^Õ QÕö‡áüŒ•i˜8šÚ-NÇ(SÅ×XS&3]²öM]ï\ŽC]÷fÛ_¾à‡çJJ Ͷoe»7 •Èc~¿0‰1gù™ÆéµÉTcÞçæ[ؼŸI¦¸\|_“Pñ¾ö{ÎL‹K ‡†´T« w¨ƒyQ€@Âd O(sôNáÉ*7n¾ï>ýß÷éÙÙÿÆñ ô.é¹àD) ¿TÍ¡‹ð×bØmk§ø ›íHÂ6 <ü;þCoÙÙ@–¸ßÓØ  7;îXLÃ[&PÜÉ4vÖR4aX#Å/ÓÅ«7Kìoåçï¿awßüÕÆ¿h_ÆÙNdªªÞ!—!+Ë g€üU}±õ|ŒºhÊgàPÔ?—³¾`¶ƒúâÔ¾&Ëìf!<\èä 7æ<ñ0a¥]™ÙNªÊ‹ç^†SPó™“§°–¦©HTdÌ2ó¾µ´ý-ëV¿¾ðë/‡dµ{&(Ün#É “+&2l˜I¤˜L1276*ð½ÌïÍ›©Ñæ{ó»÷¼ñµ„Þqui©Žò¾Ñc§:ø×¿À{ºðK”E0Ô¯ Á éÒdpDB™T'6'7~”}òß—_?çò«˜ôýeç¾úåg+[aC~©VÛ’$ ƒiGÂ_ìæÐE¢dŠ‹Ç/p¼Ç«¾ÖyÅ0¼5lSÂ_é,S¢mŒxh5RW.ÿà«÷ßšƒ,t4YIu´{îÞ{­p É ¿hxUïLlüà¶9ê´ßœ<üèão–ެ¡}»êG é- ëÛ%!?TÈ·JóÄĉ§‡WTk¤"; NO0_¨‘ ¿\M£Tó|êZø¥Š¤'òPpÝ<éÁ#TM] ‚pÕÌÉþc†,Õ°f‚ U6îP³SS3Úîùtï;à7°?ÊĬ§{ÝzVfªäqÂq“¡6qªý»1·0_ªfÚÚ¿ÍóñîyÉöÎ.!§ ©Å¿®YõíüÏ>ùŽëxø•¿J™<ñK•·BlL¤x¨¿ Íy¨³Þ!Y&¶4¯75ý¸³Ï;­çÀÁ'+Š##õÏ(Ô®óÚ¿9N<¡v}×þO^"nò¨]ϵ7æÎµëºöïÆäÉi¢µý-ëÖ|5ÎÇŸcñançvi÷Ü®#ßÍ‘Ç\”Æ„È÷VRÉS4áFOšzegeÙ«#½ïʤqÿmQ$å†sÆ©sÍSbßz°âÁ¦'NÞÅþŠYS&žktüEÃcîL¨Ò°e`㇖;U^á;uàaGôïÞoÀPoJZ;—דép8Ýè-)òokØùýE¥¥ûñ"]³.ïç_!8á1YbÒÄ„Š_ªüre “+¾ÆqøKÎn¡Áz‡ÀüL8û;¼gþzÓÒÚ¸½ÞL8™l¼«|»¡ ä4€Ú¾m¿¸¢¬tßÖukW­]¾t’4×vß@iíuyÔÄ)ŸbÜbè%™C‘á=½RLf¡¥²W•5‰4–'L‹ïùi° :eæÔ‰ßZ$9û˜„Š5TL¢˜T1¹â§Þ&à ²mÁü:3ÕÛl+À$‰?Ù.Љo‘þVL"ÅqíD½Ûµf„\v@ ¥¶{;`Û  £'L9S“¾²mÜ S'NçlK#‘'¡çoW•ÐRUAÑZ,!S¹¹/zv¨Û×B1T(9sŽ›•;š§Æ&X6SSÁ¤)¥rc"ÅäŠÉ”¡¥ÀÞ4väø–” ù4‡À/V&S¦ñ¥©•2Œ÷qžÉT4+æ —mD½Û®J„@6C %¶{›A|°8£rg¦èÁ=óa]’ÕÙÑu@nîµüqj¶¥Z*Ö¹·ŒxŒš8ù,©Oá»3§L¸ ªN+:l³ce²ÄFéLª˜@™DŠGN¿$S–• ÷°[0±­ýRe£rÖLq#gBÅ{þÍçÙFЉ—™‡¶ ¢Þm[5B°Cˆ€Ùv[j»?„ÐÖkØÏI£'N}À_Œ®í¼YS&}-…ÐREC¥uœ³”pŒš4å]ÓgÀ~ê-ræ\c‘†Šk‚I’9ücú/1÷¦VНsyÌ ‡-:ð ÕÜÌéÀæ´dÖP1â=o|‰Ts ¢Þ›[ y“€ÙæyßRÛ}²1Œ+ÖHQpÏK°“ú£$KwΚ<ññú2¨GKõ|†Þæná—ª>ôšï5KÉÃ&T4ì}|PÝf¡ Ëjv®Lœ"I”I¤ø:ËËÎÖVÿùeÊÁÔ6™/VÞ3©2›×9ns ¢Þ›c­ ™“…@ki÷ÉÂ/®|Ã6Rô Ú± ©ÈÌ…–*–œÒÁC~€î8¾ìŽá¾9Ø^“Sä-òCÅ2›,“'ÞÌsfyÌ=.µØ`¾TyonLœÌÍ<×R0ëØ¬óÖZï-¥>E9‡@kk÷C)T7?ø`¶V®¡½?c;¾W·"»ëÚ«ïVBKU:-ëZÒH¥çóo…CÏÛÑÕwʼnIÒn]Òwà¦lmYÀÐbU^PÅ&­LU7±É†S« õ³M¤j:1Zk½7ÂâNvD µ·ûdÕ ^¦iMéŒ×j(09¶£;y¢‹³ËÓ‘Ææ¹¿ÐR5µæ•¦Iz`vìÒBgš SqSvs ‚@@ lÈTIåòh[Yùì¹É÷.±R0¡¥²MûåÕ$dÊ~Å €@ éZª¦Ç¼)îÈv'"€@@ þ8[ç RUáÜ»C¯;¤Ô!0}y§ê¤N=0zóÅ'ÈÏ}ÿp;1rSLó9š©æSWBR€@@ hf ˜ž÷9œã×Þ1pmmÑ…–ª6"Í÷·ÐL5ߺ’ €2{%;•>Ýá åGSh©¢¡Ò<Ï 2Õ<ëMH-vG`+-‘¾zå-CêœÁ~ö¸Ò‚óîÖ/‘eù Ì Ük nn*’öåÍ™æ8Ó<'ööE@)ûÖL š1ª¤Wˆ_Gaø“ë»bèï™ÃŸ\ŸyþÜq¡7„-U$"ÍëX©æU_BZ€@@ h&Hš~*DgŠ;hZÞù€o1´UÛJ bÐŒU#Ík¼ZªH4š×± SÍ«¾„´€@@ Ð 8b¦î„画\z»ïOýZw œ–7 .¥ŸÁPÞÅkÆH­ÐÖCêOÑŠ"´TÑP±÷9A¦ì]?B:€@@ h†øÊWÂ^JúE“÷´Ùµ$ï;©¾Îýð5w ûqÀ£+Nƒvj¨®K¼†jÔ ´TQa±íIA¦l[5B0€@@ h®„Bú)ÐBíTuúËÇþwíØa¿ÏûëðCžY™†á¿™’î¸K×@yUŽ˜¹1³r`ÕI¡¥ª‚ÂÖ‚LÙºz„p€@@ Ð<ЇBî#%EÁ°Þ°§Ì2ððHÖ[«Ç ^µÿjøz,-)ŸÚ¦_mÆ5÷BKe"aß½ Sö­!™@@ 4S²åìëõlçÈÕw^j¡rxï$¥»ü\]ÇòôºVumúÊKð»ûåc†ü‹Ï |lõ°ÃflÎ2¯ó¾>-ÕÇÈÏ ïé‘h5í± SM‹·¸›@@ ´æéV±öú%fQÍá=P¨ëV^:$0o¡ÿ ÛL1}m;I×u]›+IÇÕµà>½ôh3½¹¯KKEº>Zø¥2Qjú½ SM¹¸£@@ ´2B>í&Þ[;fØb.º7oƒ¢Kºa€^Jg`<õtÞØákøZx(Púj͘aŸóïhAh©¢¡rèÎ 2uè°wV‚Àåc†>–š>4×,n;UÁl?mÀ£+/äá½á=†>Æ×Œ¡@~›––r—·®}½Z*Ú·BxO¯ 9ëÏ×0~³>{‘£@@ µð¯5ét ¸çw¸Hÿ=k¥xxO­-wÈÊ_WŽ2§všú~6-­}H/{óÿP#ž$ÍÌÔÛŒ;ñž½UCŽ5®‹– È”%0ŠL€@@ ;çíJõK{0£ïÿÖŽ6Sò23 t¯½kØ Ñr>mC{ÍQÑVÕ([é&/fÛ«Úñ>¦\®éÚS¤S»ˆk[Y¹þœqê܈sâÐBÄ0Ÿ…`Ь€@@  ËÇu,së®!ñ ï¤òÔ>‰4ý ¸P¸ Ú}¢ÚRui¡/ÄŒ¿hˆYsNh¦¬ÁQä"F#0púòŸ =Êmhxoàô¼¿á&gÁwUT2)€ÐRE¢‘Üc¡™J.¾"w€@@ 4ˆ€KoFCDjЫúé¤ß£»£Ì„–*”¬‰#4SÖà(r@Ò`'Ÿ¯?ºâY’^X}×ÐWÌ }4¯›ªë“áZ¡«Dò«kÆ}Ù¼¹ÿäå2´§…-U$*Ö Í”uXŠœ€@@ Þ|låmÐ~Ö R3ò©šþnø£‹èV"ýÂ.ªµ:ïîЛ)u°y'B@aKF"‡‚L%‚žH+$#ÀÃ{˜¡76rx]+Àý}’¤¿­;|–áðSrÜ ×¬KÓ/•$É—cÖàÞªxì=]ø¥ª‚£1‚L55‘F M„€ =#Kò}kÿ6p‡yKé@p<Ü*| CôwÍsðÞÚ«‚êßÑ„–*:.‰œu$’8Ö´7OzðLË<ñ{À}~gTvz¬iE<€@@ $Ø•HºÄde fÕ}öÜä{—$ûž±æïry®[~[?vðivî*×®K•\ÃÌsÆ^§»d]zŽYså, ¶×R‡ìܱlÌ€üȸ¬¥ÂïK²¥ k©Îýxºã†óǪ_D¦±êX×ué¶<•î§ò, Q¦C )*¹·ÏÊ[­-³êfM”xMrBnü`þ­$é·Ãà­+Æi5°èÝ S;pÓÒäÜUä*ø™J™êŒ>«†Êd ƒmGŸõDg—§ss¯õÅŸcòRð’3¼°ñÚ±Ã.7ï2hzÞU )w¬;ìHI’ôÓòÆ¡ß=×¢ly‘qÍ4æ¾)¼§Ê:’‚ú9XÇù<¤c€ñAÊp?¨B¾&ÉK1ù¥*oÍ?¾È”ÓÎû¤©Q'Ÿ…B¿°º£Rç`{MN‘?~îÞ{Ø !›@@ ´nn~ðÁl­\;ÄäÏØÐùÓV rã¬)“ê\t¸©2}ÅQ!Ò&ÂVêw|ïA3VÔC¡’Ï\9vÈ*Sž!­ì«jÚWäv9Dh^¯½?HKް•¥ÑZªQ“8†´ÐÓ tG";=[ɧvÊfÉ+MùÉ)ûù4UèäÓ2¨LkCjßPP÷(à„>’ä'IÉùǬÜÑåµåµÓoËÉÔ¨ISîÐ5šV€åß6sêÄoíT`!‹@@ bA`ô„)'kDO!îI¦±³&O|<–tÉŽÃnÞx4ïMŒ–lè𔮀|7¬3¬ŠðÕåJ¡!ÙêÒRA)2+Co36ž5þnœ4õrIÓ^qË%ò ×WJgçJrBä†4W´OíN[ƒGb¡ƒO,Ou¤>#wÌþÜ\]Þzš-ýxhçz"jòóq<]r.œ5y|^C÷°úº¥d*L¤ôX û-ræ\cw&i5˜"?€@@ hYŒÊ™BÁ=/ÁYæ%YºÓV„jÆŠ3õÞEv8款sðÎHä=ºâŽ3bñ”™Î<ŽGK5{ölå«uë:JªâÖTEÑ™ù’ZÔE×ýym[Çxÿ#ÇB¢Ì{Gîw«ýhAù•†þÞ„–pÈÙõIm9Ž“ü!\¦«äСɒUÝ¥ðyªõ:)/zéÏ<•{[1ŸKv°ŒLñÐXò'ÈðÝ™S&\Æc¶É^ä/d#R"ž8õMtj£k;ÏNC~ÑÊn¸Rª_“Ûut,Ã{Ñòàsõi©–ø¯yl›¿×éçžÃ€;2prØ?yÎNŸ&»¥²ÈKq/­ø=4T‡s:½‹s…ÔÙ±ŠÚ;6BËUQ#¯2-›ö†zÑ6õðÐÞ`OY&}?ÉÒÄ™“'>_#b~XB¦ØØ|‡º}-ä;@ŽöÇ TjJd)‡ ÖPéÁ=óA¦²:;º°›Qº Lc‡÷ÌôÑö‘Zª€î¡þsikàp De e+Û¥tyxK£n:•k™†íS¶#Ÿz:GË2®sûCÝh{p8õvýDiò¾˜Ò…:Qžÿ\m¯Ú. ¤÷<ÎŒk’©¥²„LÝ8aÊXÆi˜þpа‘Š©žE$€@@ hf„m¨ôoÐ9{aêÄévÐô7`HòwÞ««L¬¥Êvyãçò‹N è)ÔÇõ#õr- ’Û9l K+}çj`~+Sé'?ž{ga2䵯i'Üð¬=A¤’QE"O€@@ °ÜÇq_g¸ü±ƒ@QdÈ’3_#ãš(—:õ^ñ}ýæ—]y´"B§¦=GC<ŸÛžHqû@›ulÊË2†‡”«%sþöä“5†#%"qÂdŠrB«?RÒkùŠC€@@ ´8Œ¾}žÑ÷Ù°tóÇt«Xsë ØÆÂb”ÿ¦Üiíu]}?MÙç>)õŸJ†¼;Æ”öˆÖÞ±‰Žò¾)öëÿžâ¿'Cª„É{6g‡œìG*Š<€@@ `¸¯ã>¯rU»ˆ•T94Õ÷Üdã}Íᑚ§ÏíNŽ5†ý–®é÷`Â\«K˜LA ìÙ\8ä´ºjD~€@@ `7Œ¾}äêa7Ù’!Ï͹S‡Ã÷ÅÝ_)ér³]íÅ€f ç+¸¶Ç:,pÂj5V “)^k—ˆ±Z0‘Ÿ@@ ìˆ÷yÜ÷ÙQ6«e ©ú CllÞÜkÕÚ;×cB¦fxŽ·²< “)LLÇÖ<õ~V")òV÷yÜ÷µô²-ñ€o'Å)ñ²/Í?ÀÞ U'÷fOêV–æ …­Ì\ä%Hw>ö˜·lYçŒLÏ®éãÆ%æ.YBŠ|k €¥%®ÃáMpxxt þ¸5÷áÎðˆ¬=›{÷®³Énýûñ´K›p·”€!K#äæJX)Ⱥ È”uXŠœšÑŸò;6 ,ÝWvß®¨Ð‚1á* ƒ/r9“žÎ½§E 9óד•þ¦IœÃÙïáYï,‹qf&U¾Zª+~ãžÃ§Ûѳ¦N<¢ú¬G:u@.†ãDs»#wFV™Z~?쀯ðí8?<»dI¾÷ùÉ^J4‘^  RuͰ Kkæ¶R‘uY¬uÀÚÉÚöÈsV[ªæ²B ‘‡@ .FMšú'©wÑû«prôÎÇ ó¼«`~4™í¨ei&FOœ|}¾:uV]x$t^§FOšòׄò8„‰Ë‚eÿ–tý7¼˜:fVõU$:âüÒ´GO|à¼C(š¸µ@ Å »°,.Œ«Ö²-͵€åZíVûç©ü¦Õeš)«ù%h¤.Ð5íe§Ç»(ÆEhkâ†ï†o:1)÷>T™‚4^ˆ{ïIÊý%š sˆÿ:aÊÿž:q[Rî‘ÄLS©×•wJ)›9zt°ò6ï~øáEŠƒgk¤]‰sŸ$ñö"k@«@+…¬ºè^Jmæ%Öa"µ¤âlV®èòsVG)«ù%-D%’¶wqô¿7‚HÕy/c­ÐžÉ? ÞŠ{bp†´fwrÜ7-2ý¨ S®Æ××y’Ã{‹®úžDC; KE”á2OräÜFT‚õ¸|Ó0ìu*âeàå2ßáTîx6÷Þ æÍ1¼ô“")ã4MëŠ8·ƒ¤ ÆèÙ,]þ†Òž4ãý5÷™´`ðÀP?OÊ5¾Œnš4õš¦ß‰%*FŒš:µ•ë/`lÿ<Ü·ù‡mŒti2†ÞÞâü*W²rž…8Ð[Ó2E’'>7ù¾ïÌûÕ·w:‚ÁÐi*¿T~[_\óÚ“8+¶OÂ=‡Ó î»ØA®ñÏN¹gµÇÜC‹xƒû(àÞX¬AÜ™H0¯Gîoœ8ùL`>އ#ÎÄÿkŸÝWßÚgÑ–„xäž{J€ÕräíÌ_ C@wÐ&ÂçJI¨=ÖßËo\&q¦r¸ÒàÄ+`lq&­3ºª»h‘ïR}_¨‡¤4:b˜¯NøÅ» ›;Û…Îxäy!7÷Ò¨r¤¬Ÿ$%®bB#ËÒ_$™>ÂÌ”ÿËN};2.®·CÞ¿%µb.Ö—Ü*‘ò7˜/бÿIW Þ‘ú$Ì/Iò8ìÿ ²tœªªoך 2$¤‡Õ$}ò~‘ºäà I>1jÒ”©æý ¡½éÃ5™Úšç̽¦S_ãßNI @æW Tÿ‚¡Ì)¼À,åë£fÎt¢|?àðÏpšòÈÇ]X… EÕ´¯Gý}êi§¡ “\.ÉÒÐwŸÏç Å¿i”?Kzè+”ÿ q;¶‰(c–JÅ£'=`د™y€ Þ -â Œ¬ÐUˆû,=!£~ÇÜc­³Ë@¸æ ì Ê|â~Žk7æ«ùq;6p!„ô¨KD˜•{ßøeÚ¶#4ß9Ö§7›Î½Š2Ú«ÌcõGÝN§^»Ð8Ä/ç¼è5ê{ô ßx}à$ÚìWª´A—+Î0JB3qÊ^ì¤uÃÑé»eIY‹d;Ôõ·€` …Ödh„Öä½›&>°¤çC¶©™9循a §h½fC+ô@eþoC[’‚òtîÏΜ2áó<:ÿôÉ;ÕiìAw}„<Ri="4&ïÜ8qŠ Bw'–bxâùÜqqë=|æ¾ûx)ˆÙвܙ4›`P=;2¾}Îé‡9œòáÏæNø¥òÚ¿p¿_IÓÆïgËÔ€³êþ‹rþ¤ïñQ¹Ó?Ÿ•;6ªG¾±Ó¦¥ú§A–7_˜2±Šxåæ~ýêõûï¡‘{DõX6fõÐC™zið^Èð âÞ`Ê ƒñ·`ç²Zx,Añ4àüÑ S&]Tyå%ØŠ-F~ÿ®]OÕ)£IÛ n‚V0Ý%éUu=¦8+ÄŠ€.)ÿÚ쟻ßÕÚ(ÖXô9òv:°cäb 8á^êÔïÚòË?iÇúÿQ(Xaœß¸àq:òÂWhkÞ+¨Ø«ÈÅë‹E™CP³m ÑKÕ<‹ð޹kæ”IO1è+DØ4%„¡ñÎØ$Õ/A§úe‘2’=?å>hq¤:i—×ÎG’\Æð™yÃy_ñ±®H54Yаç%òw3ãVî?‰ RÆ)§Cz$Ыý§ÔŠ›ÐOhÍ®`­O‘2ò!„†‡Žˆe!O‰ÜFÛ—ΛÁ¼ÐÌͨK¨’’Àñ(G'ž§#ãäæž¦çg€ÒÑýÇC½øšTª€xYöœ×ÀF¢"Ïö–œÒÔ Ú«#Ïë^™Ë…òŠ1Ü”;u(>ešúô”I›cL&¢  !àh7 ïˆ +.•k™ ÅnðºÃA=†_E›—†Í–îLê>ìjÊûr,mþyùKw‘ê/2ò9°s•î_O݇_Ó`¾õEp€~¨ç3:+m†ÒѹƑ€'YÛ^_šx¯ 2/b"~“#ßD†»h@:Çrshšú€4m¨#.´IzßÚ×tGf ƒtðÅGÑ5\-`¡q·«F’´µÆoüHIuš+ÄíQûZ"¿AlzCÃÓZ¥U‘ˆÉE(»Ú_V›èÕy»Y¹ãY[tÒ]‰¯µs8"k˜j$ÐuÖ‘ât„©$ˆ¶0¤ª¦:…ÂeUœFÙ#ó®ñY û«Þ|öpÔ(G…†¡UÒ SL¸Ý”û`O-¨3›{ưþ÷GÞS ‰!0+wt9>6Ï hie_—þ5´-8 ÷ÆS‡´ì>pMà¤â=+ Á2Û%ÙáÂ{G¡ÃÎ}žú7Ž-¾7´)pL£2LÉêEò}¤JŒôž4|#ãåÐ÷˜1ö[N9=N§c.y·*ïò¢Í”šÝ«êw¢f% õ|Ï*RZI‘ïÒDó3Ó;̱ØÓ÷Ù6wùºmèäo€‘ñôYÕÓᣋ,éKÐP¢k5$½;È”¡M‰ž¸qg¡6îT;eE¹~2ŸËÚÂûô\¼:ˆÌQtFùørƒFàGÅóS&¼Ô`ä"ðìÆ[&=tC@. ª…€Üø€QUJ”%píEZ¹Ô'T]àÕ8‡BÊù'†·p)$ŸÖ»°®ž/ @ã…ÏÍê2ʺ¼!TÈ!Ï>w‚a\ŽÛÿÛ§NíPQø ¶\Û0óò‚§n}‹-'K h™¹÷­ºùÁÏ •†ÉÝ;û÷_¬½i]ÈWZ­ðg”$;héG×vQ»7Í¡“®œGé9ƒ©dÏ*ò•쀦 „ËÂÍ9Ée=‡Y•íA_ÓVe,òX…kdIú;oÑö=÷c8«º§v]Z€gÁ¨ÚðŒmF5é¡aà+#0³ï+óœU{¸Tø Ï"ŒÌ2œÊÃU²SYÀçŸÍ½ëyI Ú©‘ñÂ3µ³"Ïñ1HÂNäqÐÎñ¬ºßÜ2aJtÂX;£~?3yüJ`üĽðLâÉI]–äsº2ò¼q¬kW‚$mëD}!@‡®xI! jGÆ­,ãé‘çÈá„0¸¡ê0¨/pÝ–c8ÐÀÈ‘s>EÄ—ƒˆ-ăÀßrŸÌ•¿“(Ôs¤ç]êçþ>žäUq}e»È“Z=ŒWºŸ¿m1ÚqS¥F\E1”ÜÆŸvTV·\e¼±*ƒ%Y Í”%0ŠL’@Gå¾Wòµ©aæÙøÑ“¦ ¿L¯…H€K[MÕH fÝñL6ÊÌr?ŠÙgèjÅLÓ¿Ãátmдà-¤> 2³¦“rÂ?“ ¯'?¸nöM“ü‡C‘wc¶Ü˜õwˆÏ#}Rá¾s¡õ9.îå}=$eÂËù=ˆ—][&±`œðf!þVK“¿Kõ¹”¹cöSšsªT¼ Žž¾ÄŒ·ñPí,ÓÉß @ï®)˜¥—[;¯X~wRúOÍW×þ4îl¼Üª4EOÝv›ëê] ì_ž{0[î­¹\š¼AÓô?`Hî*ÓeÅ3S'nÁr4¯‚ìÝ‹4;ÜŠcN ¤·Ý¡N½ 2´”ƒg8Žž8u2Œ£D¾˜Q¼†û–ëªvL¿Ž!IyfÖäñy‘iø¸r]ƹÐÎu•%ý -´÷"¸w¨ ¦ß%×L›ˆV‚ íT'¿ ­õ ã½/Ëí›]ò²›È•šCìOЉSñ^hŸ ©êü=´þ§é0N¿¿wQá®ð+(%³7•njôýêJÐSñ lSd¡™ª iqÞVðP\\ƒçyf:ñY¤© áÏh>/îq1Œ¤Yh^øØ›"ý‡›Ñ© úwb™‘÷ñ²È™*ŸÈ³Ð¬.œDòèØźwßa­¸| f=bð2œOÖкÈ^º×¾1™ ª›5MýæÞ{dR®­-“Cq½Áòkº6[/ –Ëoà8³Æ/’=Ò‘ =+áó?šî_Ûg˜¥8¦vñü6‘.áµ Ð‘É “'¼é͸t¹_—6ªz` †6ÿHŠtÉÌÉþyŸ.Î.7ƒØ½ ;ˆg ,4u>ò,ÅPà5‘ñø˜ 0p»å¼8Ô„‚Zòý7†•÷"švШìÜß­uÜQŸ§lþ¢ÚœÚí”ý·'Ë8m€-)eßo=Ô½#»½>ÒÆ†Þ²º?§câ–HìѰÃ2)éeÅÁ6¬õ©}}öìÙÊÜåk{’W.?³_¿‚d•1ò¾·<ð@[kÏÝ{oMû©ÈH8†*Ç.ú©+Û¼Å"×¹µ)S˲³Ò]ìͼVvâ§@À6´¦~W[‚»·æ87´9Îûjœ+qÀ‰(½í@ZüÁU5êTvxð\ý զ˱4òüÒ7¯žDÁŠz_75òièÏ/÷KGŸçrïµDí•00­é¡j¨‚ÄõÖ‰“)Ø =6sòÄÿkˆR Z­©ßƒ)Á%šz뤔¨­£†w“FWºÃ•NÇ]ú!ý2çf*Ù»ºÎ|;÷Yùç–e/Ö'Þ lrûeÙm¡r½Í/3§ü~k‚°™²G‘‹@@ Z…ŽT$Uk£ä[fÄn¾ûÏ© bõ˧m0N¼¶«#¨Tk«ÈŠ49Þ´õÅ· œún"® €@@ Ðü€=h†ÓsÙ?SseZ6†÷~‚­Ô¢çÿ1áV–Gh¦¬DSäÕ*ÐIù³CWÖ·Ê‹B -Ý«„à™IÏãæBä Wè*¹T¬ÛzõA+=$X0A¦P$¼0å>K¿p¢€@À.HýSL¾»[ð0êé\d±â–c™ïB*u‡¢Ûk¯ÛwfQˆa¾( ˆS€@@ À}IîÄÅ[°Ú†êÓ²š,º.ÓϾ‹hkà0xZ—¬ë™É(€ SÉ@Uä)‚€Bî«1ÔXPþg- ‡=“7‡¢ÁМ¾+¿>´%0nî¤ûgMžXíÀÊâ2e1 ";€@@ ´$ž›rÏÚÉE&û¦l4•h9¶.^™ÖÆð#õUÙ­ÚP7v|E²]×›)[?B8€@@ :ÏìœRP´ë¢·oëéøIú©üÏôué-ÔÇ=Ÿú¹¾!—Tíd³!)5ÝAë'Ñnu€v\Ê+²KJlIMödþCÙµ!·T&§Ê…’ #ùBµ|He+p¢ kyéU¬†qß&ìnH¶D¯ 2•(‚"½@@ Z ŸMwœ\P´óßX¾ÉX®+[ÙN§§=]8¿ü/Ë6N8i£ÿ8½«s™ÒŹ=·`¾\ * åXBu[pý<*T¡e(ˆ$o K݉­9¿Î•éí¤ ½ÝüZ·®Xk¯\—ä50Žšçp8ß|6÷nëWHŽZB¬ZÇyqZ VˆÀgÓ:¤†ô=S±Öèm RU+¥`&܇).mô´ñÏî¼)wêP=(ݼM=ì/[ƒ‡§aM-UÚò*…•Ã+•jåÕ‹¡)‚¸–&—é[ ¹ýQo^8åòn¹³3þu÷Ønk¥ÿlÚ8‚ʧ??yâ=‡ºš™:Ô5 î/› ðÉ#ŽST­àß§w„HdE¾óܱ¡—‰ÂCsÏçNXë·äæ¾x×mÇqXœüÔR­ÝR½-4Dz;\óÁA1–oYI˜P')ŽfåŽßÊyÞ‘;cY¹ZÒsaÅeÇtRW齋¤le+9¤ºýX19+PûÐNu0円TrC¿EμÂßaXÎÙÒ ÈÔ!…_Ü\ ‡SEº ÒÔÐFÉNïèsî,ßMÊÊ…æ¿Æ5Þb çÞYvj~0ÿî]ê q;ƒƒ¡ÙÒ´,y›Ä¶O2¬ŸxÈP[*Ãa¹ÖV+Ó³IÓnŠÀ¢ÞRœÒLè^°‘âB7[2uó¤€ òl”¡‡.éQóé1Õb Ф•@qº_[Å1ç¹É÷.iAÅ«·(Fý‡ÔsH’º·Öú¯ q±E#PÕö‰¶`‘±ÏZSÛoÑ{ˆ «6ÊJñ*IØý£fÎ|PÙVpBˆäs µ§†ºå€Ê¥atÑ6… mUY‹÷ü&Räo¨K»¹³F®[…e¥qäլȘ¬LöVø‹£jj'vŸžâѲ2R$ÓÙêÜ<ø‚A­°¸\/)÷ÉÀcÊèISwªjèñ _ÚS3fŒá)Æ£Çó`ç¨Òw>æ)ö”þÍáPî0êƒô­¹þí\YB¶ä"P«íOå¶ i3ººº>UÙI%W‘{‹@ ±Ú(+ _IŒæ!OÞšmh6djÔÄÉgí íÀ8®Þepï.úQCzÓ°¾]¤›g´Ö`ÈrŸŸò6äÓ¢•›:®Ø°ýኴŠÛ¯7ñÆ—¦Mù Àðê”Í™T±ºYFyήðT¼ kRçÁ½;‹úo­O¼(·‰@´¶ÿÞ‘w\?>÷ú=”Ëm¿9·{³œbŸ$…6*IE±E¶Í‚LHÝ #¶iÚgÒegCýºw¬ϵЇXJ:fhoÞ¤õ[wÑŸ-è_ }xÝø¿ÿ÷C÷?ñBؘT5·À†‚rÜ¡(·:ådê¢þ›[ y“‰@ô¶_øÑî¿û…©ç¶ßÛ}2!kõyÛAÕ+ÁîCcR˜HÑcGê)ß}õo©–X–•‰ñ¹çšß*Gê%€FÑt½S—ýÿVÙ®ìÚî[we%©ôB•$`ëÈ֎˰—q8•;†öíª ©:j.ÆÓŒ㘚ž~’x±¹°Ùю¨÷Jù¼,¯¨ !‚@ ‘˜mßíõÞŠ,ìÚîY:‘¬>„6ª>t’sÍŽdJ¾ò¶{ŽÒ4½#܈á= ê½Çœ3Ù1ÈήÚ)~yv©»RÎQÿT¾È¢U#ÀmÆÆ.ºzôтۗßù­ºŽ¬,¼ÐFY‰f|yÙÍ5‚¡pº]g³CNö#_qDìhG8 —ô6í;œëì%½Çšîìà‹&R+åa9EýG«IqN fÛOËÊü R.Æf§v_aDìzˆô…™zf8hM=óÂÙ+]Z¾4tõƒ—šçľñØí+ÅìT{²gkö¡"Bâ0Žé^·.;Ý‘õÙÍU‚Yï,——ådyEý'^÷"‡Ö€Ñöñ.•d¥°[»oÝ•cQé« m ݪ‡B_ xjMg‹DiÕÙØM3ÅäN!Yê„%bìFôšõƒ’•™*íÚãhBx°±ý/7ÃÛÁ©_¸ÞÃry§£=Ë ÙDD€ß¥{öïï„lìÖî,™H¯6ªbºô]¢Ç$p&Î_Pãšø7v#,,C&)kí‰Î4îê¬;ã©H2O‘fu¿Tíd?aÔ{¥\n–SÔ?Ј3ìÞWL»öbAu Hk~ÝY§„_.\E¶í®óº¸?Ü–d2Ú¾ÝÚ}ü…) âÑFÁfNôXÞïNÏ›:hÆò39ƒÃflÎÂbC»¯3ôAŒf ztÅ_´‰!`'2ÅäÉîÑ |ÙÂ0á™wèý¯ÙT¨ñ¡` åh|6HYéñÖ|¡š3ú,źŬªw¤e‚çŠðÌÛˆìZv’­;÷ÑMS_¢­»öTпý™>ø&±çü Lë9aÊÂòüõÁ—éž'fÓË~O˜ÿim/ØOÝ:´¡sON›ó÷ТU›ÉírÒüeà ïX†ôóÏW–(*­ =:q{uÉ¡†î†èí¹ é—u[y–5¸]ræQÆ×úìÏÒÏk·Ã¡ÐéG ¦³Ž¥;U©ñ1_¨Ux#·ºU »U<©"å0dš©x૎ûæg ­Ð•çßà3øï÷¿¥´7]Š…Ã9,\±‘>únÝóÅôíÒµ4wÁJºëÊs(3=…æ-^m<ëü;=•ç/Ô íñ‘ѽc[êÙ9‡¾\¸’6o/0È·ƒ—­£“Ho~¾Àhsý{t¢Ù8æû(Šlh¡þûéTZî§Fô# IÚ¿qƒ@P¥ÿ}³”~^³•ÊýJA[Eƒ¡»ÿr.¥x]È'ym¥f ›ÿ¯Êw*·¯Èöv(Û}óµ KP—m´Qwœwwè¢ò¨Ò¤©¡2Úç¹ô-ãÃ9ôémýüƒf¬º\©¿ zbÕû«o¼ž^6fÈ“¯?ºâ<Ü·ú®¡ÈO„x°ÛŠÙÐã-GÌñÁ ­Ú”O?åm9šztjk¼è}þ ÑýwÎ|ê‰s·_q^þÝ©mf:öÝ(Õë¦Ãô k.<‘zuigܯMF*ýþ´#N'¨†èãï~1Î×wŽðÁ¼%´på&¸AtÉGRßnŒtß.]Oyòéš N¦ N>œÞýr1í9Pl\³ð×¹‰3ïí"å±Û3i|b’¡¸ÌG%åF܆žÁ¢² ƒÀ˜Wàùß_Tfü<~D_̑譹‹¨°¤ŒÞûz)7¼oT"Å xXn1>6Þ¹ñÁ&êèa}Œ|X†Õ›w“¥#õ ø<">·¯¨ì=ÜÿçãÉ õ§s£½…%´k_µÍç¹dõºûšóèêßž`¤»Ú7&xMÔVŒr´ ‘í¬«å¥!Û¨0‘ª.ÿ é«z yfeGóÌ’ÑËñu²rå–0ÏÁÂN(ÿ§êÁÐýæ9î“Êu Ý“Øe‚y^ìcGÀŽW“tð¿;õp‰üïNI¡ft²¾õØ¥¸:ó˜ÁÔ¾M:u…†Ê¡(Ô&3•Ø«pZ Oˆ#ƒdþÿì] |TÕÕ?ïÍ–™l„}G‘}s÷ ÜÅj[«X[Û¯¶Ü­ .$ØX‚¨à^°ZÛÚª¸o(n  ¢JÂö5ì„ì™™7ï}ÿó&w˜ ™d&3“L’{¿7ïÍ{w9÷·sÏ9÷ÞA½)3ÍI ™ZƒC̪ù{]iðûï 9øX ©0' ä ˆVmÜaÎÞ»tH§½»P׎™´bÝ6ó[’µCMVºâ}ÓG®ÖG ×u–n13ó÷×>3ÛÀ…§†—.ø&Ÿ^]°”(b±@IDATþ°††׃:ge¢g)ì/ÏI¿3’+ÿ$D|,.­0' WbB1rð1fšâß׃Ië µzfš‹ÛÃüÄì욨­˜iµ¢ÑÆZQ–ZoV¢µb$ Ò^õUéÏ£b±X²}Šñ«üÄ{«K}Ïcrý¶tæk–RÙN'¬7Ô#üÉ{ä$“š/rªãà“%RìÒRý{Y¹1cf7ñWc还Iÿíùw ~ëK¿{º©Þ3?†ü°Q;Ïœu]7Ulxl|[W$X¬Òèݵ}­Øp!­ß¾v'û0ˆm |ãZñs‰SŸÆFŽ©¥Ðß\'"¶ºê`]épv,-tL7såÝ„+ǘhûÏ·AŠËj¾C`Žý÷Gô ¤»ã¯->óqFçà‡ÍPŸ³ëÉ ;Þ©C»@Oƒûö0¥Z¬–çÅíÒSÁTu§¦i+&IòG"Ðä4Æ6Љd©”A¾H}]Áê:ØD}?h檷a;õ&û×°íÔª›†T z´@{®i«hüù“»úÅÓMžë–Ÿ`21Sþ¦€nÿÍEfϳrVs1 IJ´:Ò`[(>„4T}gQUSú5zä s&T‚‰{Ѥ¸×‘æN¿’ZÏ«ºê çŽë›˜@ðÿûó-à˜Ñ)ܶ›ºwÊ¢ßK`Ù¾¯>Çjï¾°ä A°³¡Î×åºAòÊîÀárê 5:KˆKa(ÜIHó£%+ÁÐÑq=»BÝ7¤Á:XzÖ mEÖîõfkÈa ÌCcm£Ì¬*¾Ó0}ÛªX>×tß"¬Îû¬ðÖAEümxŸ¡wl[õ¶Gx{ð£ùs±ÂoÄX¯/Ÿ¨ø¥-«d#9ÙÔ|ÜÀq5d‚Ulh^^YmÚKñàà`£W¸®2ikÑ~bÛ*V5*ñüeÁÏAØz|“¿ÑôWc âó˳ëh±= ϸWÂ݉P÷}·zbž}ó·P¦+L”Ѽ®Á8`7MØDùMFš•׸ÄËucË®æÅÏѺ~P#oܱvï?lª¸WoÞˆBóùèߨâ`$³·]{í=T‚E«ßCØÎ‰',AZ¹aõîV[ê_ügF-=5Å4vg©ÖgX̼eÂêM»¨¢ÚMc°ƒÕÞ©A'"4Q[¤¶ü»…tp;kùyjE9`iÔüGÔ§°Ro!²ÕWd ó†÷-vçÐPÛ(ñ=ø¾ö®á¯­»kÄ^§*꬚Í8M/¯S|Ø á*¬¿†õcIµ|‚~w‡—ϱ!P·x&¶8[lhŸî£ùXÑôÏw¿ÂÒm fÙitÍ*¿s!1zåã¥ô—Yÿ¥ßÃöØ:±ÔsÞzÂû¨“ ’ø6¢¼ó ªÞþ’žüß'¦ÿÓ`°{ü€Þ抾ÿ|ø5=÷l{åD¼7\uu ²A‰(é©Õ#Àû9 ÇLÉý~.þFt?eè±´{>±:;Íå¤sG¤¿.0Ã~øÕOÄÆìã.<Ådv~ºýÆgËPG{™“ŠÐxe ;Þ6伓Óù§ õö?·›×|IÌf:iÐ1¦M¢ðÌ««Ý=ñßf{P!M»øôátŹ'ʶ"@’÷@°4*(3ÅáVêñ†›‹¦w[ÿ°6 áVç­›4ì¤Á)’• ‚;öGž©Ää&äL[ÄÌÍ›:šï18ÖðºëŒë'ç¼;¤_ŸQwþî’¢k|Pžƒ³7í ‚cCU˜)» ’Ž¥T¼eBcœÛ£™öVÌ4;VwTa)xª3…W‚ÇÅ=öŸiåš k_~ò‘Û!ïŠx7FÖ«ðá§Íå娭ëu·ßýäñCún®òo. š#]®ƒ{óΩØÎWÃÓÁmðî'æÑµ—žfÚ.òäâËëÌÍwŸ˜t] ]$¢­4G9$2Mnûë6çÿûñ×!di÷‰ÌrÂãŽÇ¸ÇÒ(Ÿ±41· zzz–F…Û7Š36ðÑü‡À u,¼køŸëË(¶>è¯{µ…hX§u_}þå·ØH65_l¹‰ShÞP÷À uÌØ3Rü½±Œ”?¬Õ”>…¦ÃióŠÁx1R¡ñËÿ@0#Þ5õÛCé`‹ßóbblϰqç^ÊǾU=;·¯Õ.d[iêÒ’éűR“„[ƒ)–FÕ½‹9ŒË‡=U`î9bM±äAT{ÞÐÇV×+màÕy¬î£joN¯pÒÐ7ðnKºX݇]?(õTÿ ï¥kF¤ÍT3‚/“–´öá°íçßZ„C¿ÓÌ=¤ø aÞXvï¡Rs?¶Vo¡¯ƒÍl5úoî>x˜Î>q ñûÿ»ü¬–EI£D n„³²zû%wûþCäßð9L‚Êžå§áì¼{|•¾eªj¹VéE;ôqš¡?4hV¾K1q˜ê¾÷ &XÇê>»ÏÎÛ I׌HÉT3‚/“–´–æo¡àÇÐÁ’rzæõÏM’`×ÿw­ ¥ØùŸ çíDf¿±vì;dîœþ¾­ÄVÒIÚ  I£.¹‡©ú¤L‹H¥syÅž¡ª7ê†ï_bÍ]Ã_.œ4üE±þ~Ž'Åèæ5h*c¿?ÞÖßÐ%¿®YÌT³À.•´Î8¾3uåù£hûîƒæÞOL=o¬yÓ¸ó鬘Ç,mÞµ~9f¤éw̨Á-'ƒ’R‰@Œ„³‚4ê÷cï6ê´8kUÎÐY«NNZ1Ô…8’ì\~gªðHYìÛ®ÿUøY{×¥ë&øb±öɲd…]Å'üË{Ó! ™©¦ÃZ¦$h‘¤8ü;ü;kî•8€÷"6­å3ôx£Ítl6Ë.3MjL äO«F ±Ò(nXT¥Àgïa‹>•Ât§ò5˜©QCç­6Åð¯Ø£«>V0JøáûÚ;†ìþöÎ^r˃`PšùY2SÍ\2y‰@²#P€Ã‹=^¾‡ þÍLT¨c{*>CrÙÚ­&S%U|¡Éÿ­ nÖÂLMß—º‹y8iThþ×Ü9ô]‹]=6P×€¡z{ø³ùY憜D«h;Âþýê>e"éô^é‡üŸ<Hf*yÊBR"HJzà å/~ãòÕt?ç~uþHZøÃšôø«8(¹^#ÛpQÈ÷¤G€¥Q'¥¼ÙïT×±}Á8þËÜ7*Û(fõíC·w5ìØ@z*i9Žˆáö‰FÆhá‡Õ}ÃöK–f‰wòž|ÈÕ|ÉW&’"‰@Ò ðЭãLZ.?÷DÜ•À¹xgÂNН`wÂÀ>tü€>æáÝ…ÛvcÅßÞàÏòY"Ðâøh¦u4¤Q/ô²íë´eHq„+õêÌÿ¢1І÷‚‘Z¤éú`Êæ)†ÎŒZž?yÐñ,ïɉ€”L%g¹Hª$I…fÅFª>Âøpîº ¯/Œü&HvLÛ¨‡Õ§ Ã÷hI.¯«ïú19l§áûHCQNj½pH%ç{ÉL%g¹Hª$-Ží2è‚S‡´è¸ûŽ&ÁVRþŒ‘Çë£Jw5UU{)Ån£v8ðXÔ]qšæö+Ê]¶ý¸–×x¾Ø„Dýý_îis8.Ä›>¤*ÝÐã¥Å#5l_YFº±ÛÐíºÏ·àŸ³¦-G¼:®¨™«ú¤QNrMswÅžxÐ,ãhÝ´YfŠ;RîD}>Ün/-Z±Ž¾üq•W`‡~tš’BÕj ùÈ—`¡*JÑ‹ÉjTÓ‡Kò)-ÕIçH£G&‡Ã`¬â’˜Œ$"L Ó?¬£/–­¥²•=ÓÒÊ_Ó|´~ûZæ2c(.5ÛK0°ŠªPšËI1Aè׫3 ?®'õëÝ%é'²í—bÜže=ú®>£†ÝìpØn1H튆]©Ðj©b%w\ìt5rèUJ†áQSUÕjy`Bö{4ÍxòÀÚíϼ÷Þ‹¼c¬`¬êÍ`ÃÒ¨ŠzÃË@›d¦åõzMIļϖ›i±­ íu ¡ƒ¸kŠÿ<2T¼îVÃK¼{©‹g'½÷ÕO`âÖÓµLÃúõ2÷çai•t‰G€ëÀª;éå–R)¤PMQöœ«d/Æå«…ôþW+©¢ªš ÅB%ÖTb4ÕJ>´ ê±C¯&»§Šöì-¡-»VÓ§KWS×íèWçŸdJ_…ä5ñ¥y ²íGŽU>¹ÓâY§íÚ[îº(=3ãY0QÝ:Ù6½¬+©‹µP±+Õñ™•!Êì(=F íÕÒíø.û¨ßŒ®ÃûÜö»ãî™øŸÇþ^yU3UG9): ù"FÚ3%f¤<ëöx<´pù:šÿÍjª´¤Óú´“0htŒΆƒ3“¶×ÞÓ¼2µ4 ª€æ¾ý]~ö:ÿ”¡dµb½Hú¯áؤhuà3òΩÂ,û3›¤ì™Öd-Æ…¥´/¾·˜Và|½R0PÛÒN¤b´ Œ Âl‡Äµƒg¹o¤gæ}A?;k]xÚ0s‚ !ÞõÛöÐî%tðpyñßnƒÚ‡!³ºû˜îɕ⨠L¬D¹Ë¶ßðÂÒ(Gì¿»óÞÛRœ©K·ì5F8> ŽÖmü=¡Lõ²­äK9`ëCùÕ—w6\ßùÃÝ9S^z$ï $λ‹×’RÕ%‚Yë!EQo¿t²ïe")Jh¡µÒÈÛ 3%:S/luƒ‘úgˆ-Xº–ØzÐZ׉¤cÞÔŽ™·åiçÐàÊ!¥Zi*ûÏ?yÙ0ÐH†*þ¥!êÀgß­¦·®hÖ²çÜ5gù3SQõ¶æóÁ”E!§ÃFŸ|»Êd¤¶¤ ¦í)µŠi¨4±–¿?¿>ZŒí .[gJ{Ö¹N «jkŽ2i(Mhc¨4ãûö—?Ñν UCaÅwQî܆dÛ¨Ä|–J¥œ}É'+kGlЬvRõåª'Vúy(ƒÖ”ž8ëW9ÆoÇM¯ÚÿIKo}y‘ß’6ÀLùÐ{Mß& ¨ÜŒx©du&m qÝÖ=&ݼ J2S/-Á\ð®öÉ^öœËXËŸ¥1û–Л —Óa['ÚÕqKtÌP­ƒ]›fXé-²³t‰í9‘8Qî²íG‚VÄ~˜ až;;õè9/ ì#qMí‘ic7zGóXÇt3ýœé$qE U3SÜñ23ÂÇ‚ð*¾’ò*l:âH؆œñ(Þ‡ˆw_/.«0éfú#@â‘~k‹CÔƒ%å&®‰ÚŒ5^¸ÅRþœWf8>Æ| Ò:çIñ"«YâñBådžì›a@¿Æólû©¤V”»lûq-:/„½TŠÍîèͱ»yu\‰gdLÓ¨Z¬½/Ûy1ý­zÜ‹'~2®ÈhÕ•JÇÒoÞG‡;T7Ô{¸øˆ˜dwÕªƒJ¡’bº™~·tC@ÔÓÊžsÙ˜òj­b0Ë×n£"{Ü‘üu½¡RÝcëezYµiWTv„¢ÜeÛoᨾóxÁ‹–˜)qXlÖÎ)JIÒKy˜F9ÃꦛéoÕãò']3 Ðj+.BÔφÜ<«õz!© ³L»°›¤íÝëñAÊ ™’5‘—°ä‡:¸ù¥“ÀÒœ”Öé5©^6¦üE]Ï߸ƒ|Pí¶“Tyj,1L,x£Ï}‡JMÛ'Þ+®¡ÉEp¹Ë¶ßXä ÇL_,Ù1*‹jqÙOÒ3SL£ª¨|À2Ó-Ô|IO7h•®!Ðj™).!êçÁ”Õà®ZLшÁQªùb+2QÏ–ä¢-Î'Û­ß¾—¼–s/­–”ßúhÕ ÅÌK—½/ÊhØŽP”»lûõ!õ7/˜©a¨P0-Æ™´ Ú[õ¸×bФ•Új+•˜òÊv:wÀ-¨ð W«±aÐ/ekAä'©7“jiÌTåïg¼`/…:¾¿¸œÊ”̤À?DØ 7¬†Ý”b³øÕÞ5Fèœçp.¸ÜeÛ‡RÔï™q—É” Z 3…:Á´2Ý"-†ö¨KJhZ-3Åh:U L2*ͽ<.íÇ›èÖvᾨÔé͵Õtçiþζv¨†ÿ¡½å©©Î]ÓøF›š Ôí§¥ÐìeUÄÛ|ÐÅJo^N]G÷Á“NO¡v`¼¯{«Œ^]å6™‹é*½½Y€¸b oo½´ó=R×·k˜7ƒzº7F¤A¡X\p¬~5ÈA“>­ ?¾WNâÿŸOL¡Xë{0!ëRO¢Ã8«ò@•Nßo+¥ ^sà±_:ì·®ç`좀¯®¨(4ÿ·œì$¯Ï 3^,¡ì/*éÆQ)Ô¿ƒ%®ù¦¿N¢šç%Wþà«ÑT(ªúŸ>™6ýð•\Cß½ñ«Àõý[׿© ’½?‘§b?í\ó* 8ãÞF§eôËÇ…öØ‘¡[+µÅ+­5—ÈW:Ák‡cÀ`Á.†rÑÍó+è=H‚]}ß^øÑmÎ^¹³ÝpÛ3Dè hôóWJÉiˆêÐHµaF5Þ÷T¢ž§0óºO"$¡Ù¼}”¿/%zþ™âØh€¤F÷V“î® “°g—j?š—ÀelØ•èýB:Ü€ŽúçOns$¹þÄÚÞöuУßV™Œ73ß¿è KúÙé…«é?+Ý4ad ½[O¸ôêz¿«T§Ë*°Õ B»F $3ÂuNUè‘ ¡þÂðíèRhgÑ3?¹ŒêåÞ@ÅÖN&C"Â4öŠÅ%ýíôÕv/}¿ ۬ý¶ÆM£µÑ?߯Ö÷PÚ˜öâ´NÔ^ÛGƒ*–Ó[ßo¥âõédM·“ðŸÞÇ [j$B"좭^”Ê«w}7y«*È]^IU‡«¨¯ i#{»Ðü³ÄzãA žAùW §Ùxœ¦¸ä?!m¿û©#̼6_`õ=¨-ù*“¯ìUYù˜»ƒQGźø9$**|^ï^ˆ§k¿ËHµØiÇšWÌw[V̦Ñ×GiRùÁä=º&ùÐ&ˆetnÕ€õÍRÍéšý~h'¶;”SuÚ“ú¾í)×ép5ì>:4²'oÕ('oæ˜y*ó „éæ­v›5x;–J²ÚkgéFyêJ7H¦Ø­Ù¯Ñ±YJ¤}ߎöK0ÞPƒгÈíShxù·ÔýlVlé¡XTie€Á®]ŠBýkTÙñ®ï‡¬© í4¬i×(­r'oÇ"gÿ%º¡*èeŒ¡ùŸ³¬š~9ØA9ç¸è_P÷Íß࡟öøËxç?FÒ“6xZ§ÁTzp­ÉH…Ùç„?Ñî ïƒöO¦ªËw“·ú0etêUþ—$ r„SÇdZèP¥iTä*–0QѶÃ:õn—ˆn>\Šò}¬‹òߊrkÈAPbº*ï‘zR f# »­%>SšÕ;S¥bDãzd¨4ë¢TÊHO%§3Å´ª+<3SlKÔ·ƒ¦_˜IûÀÀíõÒ[Q¿ªê‹q›œCpÄ̱uoð](ŸoòÒLÐ5mŒ‹¬ÓÛ†]×Dïú^fÉ¢½8V¦§¶‹¦o£öØ&!%Ånªð9ïÂ>ÆF|ñ6UUV*-3èà!íÝã¤ÏV*TZÇDH„­ïšÿ÷xi50fÛ8VÇå.róî ïü"nE©™}©âð–£r”ÙeeuEë?Pëûuµ;¦Ö;ùG"LHf*LitƒQñnH•âá8žîˆ/7°£•¦ž—AÚgb0M«w0$ÞÇþó1­\³aíËO>r;üïÁÅòy(Ó¨ Wt£<Äѱ¬‡ÊXwÚW×ën¿ûÉã‡ô|çï.Á߯;ÿ ZAÕJ/¼g¥5‡#‹ËTv ¹·uÖ¨yؿ˪ÐfH#Ùí+gÓp"Ž/ZfÊŒ ‚ÁLÙlPM:f]ilxw-ÛŽcX â´B•ÒXŠÅÇ›pÎå‡ýb°ŠÊtzvbçAÍ'\cê»î~ÐÖ•ºz¶ÓîýÅ”–ê4 ÒU¨½ƒ™©pac}šÿgƦÁŽN¡sÿu˜®uîétý»e´¬È__bÍ"Ú~ÁºÍùÿ~|ÆuÀ¢±íþ¨v:íñ n ¾Îôîtx7+ôk»>Çÿ ¶R+Í+øKuY¥¤÷~%Ÿ%I…@t#|R‘žXböVèÔ%íȬ7–Ô8ž½þÁ5–xdئC€ËŸí’r,¹,CÕ’'áX µ·†ç€Ë>Ä—(çg¦ –ÆŠ·”¥º\`¨\X8á¦* ö=Îá´ÝѯÑÉ×…ÅbØLݵ ‚ý¦ŠNên¥5j.N$õ]lDZ\RQ³:ŸÑ—8LƒÁ Î?ÛRžÑËF¬êcÉõK°£ËÇ•‹úYdˆüÓÓž«Ë÷#µk­¬8R;S·þ—Óöü—j½ç?)é]aÿÆs@é$ɉÀ‘ 9ék6ªXÅÃK½yï X« ·n¼d ÖôeøèØ‚ò?¦]ˆT˜h>Ú襫†8¨'Ôr¿î 6PþÒv}Pöì¶–$vàg) oàà}¦ \½ã—»©Ðuírô5ihìO(6ì¯ÄvbÜ>n=5…Néa¥ÃÐ^¸DÔwkÍVlÿåv»Í£søÀãhV: ú¢½çŸçõ°¥» ¯pÊ/ ÀÂ’°û¾F*Åq'"ÿÑÒœìþ+o¦Ô¬Úõ²÷ðß“æ.3í¥Béwe µàæÐ×ò¿D iÌT˜¢ØTŒƒcÑYíÙ€&ê õ®kHf*FÉø~VSÄ@ɾŒ/­¤âj>ý]&݉}Èf,©¤m5ÌÓ0Ԍݰ£J¤ª>;T}ûK*)Ë^ÚiÔ^{Ϙ“ ÅbH'•~œ˜E‹þIçc§ëÞ,£í° c—¨úžê+5W-b#t›säX™˜3A¡ùŸ iÜ@,PXú§LzþgiæJÍÏ7û™çDå?2[”—²ý«)½Ã`”©¿U-ê5ì:¬à{«1ýXŠ ¥¤u#»³=•í_#^É»D é6SaŠ¤Ô­ÓòÝ4þ¤,÷oÀ^w`ÕÖà0KöëúÆá?CG˳YéZ¬÷˜ûñª­7°ô_¸¹Ë«‰¯`Ç«5ÿv9¥BRQ CtÁ6±çÿNH¡™ßT{OÈ33S¼K8K¨–äo!/–ouŠKZ¡X¬ÜãÃK‡Í­Bx¿¥`—¨úÞų‹2œv•×ûL_SH¥8o¡ù_'_N˜‰U…µ'*ÿÁ·†ç¢õïÐq§ÜF=_mî#¥ûÜôÅ?N¨3k}GÞL{7}Œý¨ÖÕù]¾”$R2UO)<÷C5õÈ€³ÐƸöØàñì>V ¦µ߯Ä%Ã4-´r]EWÂÈ:REoE#ÅÔ^zœ6kô!–Î7…cæÂíñÒúí{°r¯b«·¡´Ö…Eq•an\ì7Qõ=ÝWL©8^¦k†ÍTëq>#%îÁtÄû¹®üs¡ŒT¢òïü$C|†®Ñº%Ó¨çÐk@Nøfwu Ž}Υ¯L²% °HÉTXhx“NƒÆþ·¤õbÕ‹þà æ¤k‰|±ÅK|5Ö½é_Mᘩ`¢ÝüõµÌš×d#Á"QõýØêuØàQ¡ž8*‡%obKãøj לùoŠü5Gû6J|Õç<•髟]ŸùM"´jÉ”èlýwÿâ£áåîÍ]2VÐhÃŒÁô77M-5}¡ »mòÆ-Á5¦ü™™2·‚¨öKAã%•jn¼º{¶P–wŒû±íƒÝJ6ÎèÞZ|qÙ†s¢ÜýwÙöÃáå{Öé.ͧa{5{m=o”6…w¯á0|ºG ¡½)Ò•i´-Z-3UWGëƒâГ_åÆ4¦Øk«hêÊOÛªªÑç6³Tlò˜¢±}Š>¶¦ јòçm4ì'•îòuãòál™î2|‡¨eöí"ê••b®T´ãH ÁP Uh6ƒË]|“m_ ÑèûQL“§Ú sÁÌ£Þ7:…¬62 ¯Çs($ú¤§;„^ù7ÉhµÌT]¸;qú¼ÜØÀ°ñª›ºâç;¦Í S1 —.~d€É°ÕI]öœÛÆ–?SØ…\'‡U¥Ž™©Ôó5~à5CL6ÃMC+~0Ï<®ƒ Û=8°‡V ¥8RÈnÇù|L±Á}¤N¶ýH‘ŠÜ_ueÅ~‘ªxpd²:¦Í¤±ªro²Ò(éj´zfŠg©âjŸŠå7t:x“·]™´Æ®°t·ŽªÖ|¹`ût… Q’—=#ÔØòg5Ÿ°›Þ§#9!™êY½±ù@!å½’N,_lJ‘ûfµÃŽç©©.r¹œæZ,jHÍÇÉ‹öÃwÙöc(ÚAÑŠüê²íë×åã²WXÛGýcژƛ6þ èN"ò$)­VÍL:S¬a•@Ö2³!kïΤ-B¦í{:d8AõF0i NrÂDèÜ. 67¶¤.{†2ål·,êÑÞ…3ùÖP'OQ’—Pmò:zwÓȲEäÒ+¨O6ÎMŒ”ËÜÙwxç½´¬h#ܦÃ9Qî܆dÛ‡RÔïŒBê«–-ÝäóºKvhÇóû¤tL›®yŠ—/þ¢ÓŸ”ôJ¢Z.á{£–›'“ò@g M€¶Rªi´ªRWï‘ÉT¦v érÈ41mÇ´O ÐË6¶"/IGp’$pc ¹ 陕´eÏPÆRþ"¯¬úb©Í‰ÇvÄaËRùS]–"iÇ;³µCÝQþ-T{ß“‹n£{:fµpذUáð×xW¦…iâU|};g˜3oA{}G¼éhmñ1vÇ!}:‘«ÁúW%WÙ›u5Æò7FäÕرRÁtŒê“I*õÁÖ£ÊR–¶?êâe•ÛàÊeÔ·*>»OóaÅ#K"Îå4 j¥ÉÞÓ³«!cÛÉë­^K™:6à„:»‡{uÑŠ¨RE]í^êŸî¥îØ!-5LT:eµË¤Ìvæ3ÛLq¾˜Yäò¬Ï…–»lûõ¡ñ7!Ña†„Þ@ÍSYY^µbÉâÿ–êé§ê+ÙOR8¦…iZ¹ôÛ”—ãtl?½‚¡yI Z%­d[2VSÉ•¸4JVyðªf¢øXžÕ:Ð!g¹ª¨Âƒeòî"sÅÐZ«×œÕÔÅÊ)H;z‹¨gºJ]Ú¥ÖÐégþ¢]µý¢#÷‚$Ü‹ EÜã–`hèÕ)“àì:Ú· eoPs–=g2^åÏ 3v›Ý”à¸=ò`7tMóQ§§;*i©‡yJÉÉQiŠƒ*,iT­:¡û€*{¦;À0¥ƒÙT±×K»\ª—Ò/íÓ]¦© ítò \´ÎÏÀ.3Ê»Ø«É ~È/5ôKƒAfœ2ÒÓL‰3Uéxv9fÛe©çµ!Zîm¾íûõ¼¢}ñ½1Ž%:‚‘â}FÌë§o¾*ÈêÔùvüD×'¤¼¥XQgšÃ±DŠ©Þ¡Ê–u«ÞXöåç˸3ÈôKÉ@.¾$3hà>ÃWYíÅÙ0Š%»BÅÃÌÏjSœ)æj ªªêìòbÇh´-01'•—Ñç*±vŒ%¹¨Ã² «öX"ÕÕe`/ÿj%¦“é«–8±ºj×À>DUuÄÀ½ŽoMñê¨ôy3Àx”?_WнËEg_Ü|eϴųüýÌ”jÖ§žbî9Å›xò ?SFËa«¢ŽÕnªtkT†Ýý«ð=M¯Â±0¢~dÅ<Æ¡ú(EfÓ±57A{kh+žHª¾ mŽþtÀÞL˜‹³ÖÙ ‰jƒòNžÝPáí!ž"uOñP†ƒ È­¸üRcž…z’Wì¹\~©«ùÒÒRk©ÈÔ{‚˜ºÊ݉vÕVÛ>êB¨Xñ¨v'°«ç.Ô|Ì”ð†}ÜŸðå\øîŸc=©BÃN¸¬TïhŒp| v´nç¦sl#Ū=–HmZ“ÿöo¿>© ™^¦›%k’™ÒÅdb¦9Ó½¾}‡K 6ˆ‘™³u#寃‰Ûí&¯æ¥NPuØÐ'ìÇ\åØl[»Ð^{O:hë‚Y{bôÿ¬êàåï]<;aò—p.uwiHaµ lÓ@#ÓÉô •F$3ñxa—V^·»8Ìç¤zí­vÇ£ü9Sáê@¿®dWÓæâò&+{¦'‘åÏ 3(,…ÑQ‡˜‘ïXÕå(·ƒ™€TÈí¡t0“Ìlù.-pÖ㥪þ•r~3S Žªq¹}TTá¦ãªW›—­k³­€í"¤»6ÝkªX—‚‹"\Ç3­ur*˜$`óMSBl7ë·_õµ6$jvHMf R(n üìŸT°Êۿ빟š†Õ{›mûGoZÙ0ˆGûà>™™;1“Âê3ÞdŠE•¶Eï¾¹ðàžÝûG3æª%¾?et¶m4zYW*]¬…dW³Y2ï#ÅÛðª=66‡ýVÙòÅŸ¼²òÛÅlÃÁôñ)õ|gz™n¦¿1Œ$‚I'@22S<è•U¹•J0>.ع4Öù;TÌÖÁLñ* ¶Åpc ñ` aõïÍmÌ鮢b/Ô`n˜Áay˜ýƒ[MÁˆøÞÙš7 å¦ $µ³z0À¨8RLÚX‘Š™8Ó)–€GªÖ¨#Æ‘ñ¬ª¬à%ŒÜ‘ˆ«¾`MýMÐd0ñ(Î@}u ;ê@ªµŒvv“êAÙó¾~ ({¦£)ÊÿÁ)мCúcªºý**ª6'S:çóéf;`ÆëH˜#ª7Ž“3].¨ 3]n*¯öRI•ʵ2ry+1͇% ˆžÜ­ŠŽÁÓ‡Å:¤[6 e‰§o''6ÞûE™6P¦#3VÖ€žë¾¨ÿ<¡`FŠ™Âh\}åÞÛ¾§ªjðm,(ƒýrxfF„dŠî í¸¸£T ¾ûfÕ†•?m>í¢±gjžµÏÞ/k‡]©0R”Ŧxü žcq|Œ ï¾Îr‚(EóT—o]ŸÿÕÒOæYUUÁ¢2m| ßù –Lù+;^J'ˆñáâEMMcß»sû’Œn/ظ‹NÖ7¦Øý3sGÍVïðÀàg¤„;cc hIx¤TU:Nq3<¤é嘱ÇÞöýãT(È 4i`œ6ů’Ê4¸ÍÄŠ%sÕ*ÐɳwV…D;ˆÔãˆÁRÙ±¡ð'|gRtªÉÒ©Ó£3=Ž=îšx”?ãÑP`†µ3$6eÕ`0wõ@ºâƒTE€SÃgpTrMYþÌD0óÁia®ØfFéøTáì>–NáxšI…†ö $SŒãaªáÀ„‰úçõjä†!‡MÁ•–é.Þ‰¶ŒÑ‘´ýêNÓ0å®Äœ«|Kñ¶ ë 7¬Z¹Ñ2“Ç(fžøÄo¾˜±b:™^i/¤K ÉÄLÔE¼UÐoÄ X½¹=˜©Æõ¤5xf¨`Ntôø~µ†ÿ£¿³…j eŸ ;Ÿo•3[b¶è™NOtðþ†UNÓÀ–©v™™æ3:<“‡TŠiކîó•üðíFü噥u ÌÙ[s9AßMÚ˜ÎSο¸tgÄZþœ©H뀆Ѩ|Æ—<ë@S–¿?¿þU &ÀzÇõ뫹„t–'BJ#êºË ™ÉÌ+0K7ts­ªÊ*ª„t‹™*aàn¶)°ž"aÌtÁ”1£dJ¤¶ÓTßù7ßdZø½`¢,&çWåq½m%–Ji¹·ö¶oèúáe_}¾¶¦}··ÆÀ+Ú)3.Ì@1#%úhÁh±:Õ¶«WnµÏìWøÃcÌŽéàô‚Ódé3N,‘bFêpÍ33XÂ^ŠÃI'ˆ;ÉÄLqæ¸a˜êÝE¯¬R”[6lßCý{w)ã~¶$†ŒT[âÙ³õ“Ö΃ƒiOÁ³mÝ/¹ÒÁT±ƒDcˆà¯.òl`ÞX¥Q3S7m¤ Ú3W/±š/ •:"hLº" ã·jãNeûÆ ðŽgf| †ŠñN(wcÒÈôb£Õqñ(Î`sÕæ*Á †Â”ú€±aõ™`¢˜i Uóù™ÿÊ@!âúÏa˜ «ÄJXÚJ5·H·ü’^?ã)*’YÏk¤[¦ä fžX2Æ÷#U#i2­ü,0ñÅrçø˜1lËm¿hÛ–wa<Û½``XÚ#$î,¹_a¦…ß;q™¶T¸ É•^‰0øµÌÓ ú fÞ8M!•bÉK¤˜©’R)€ ]âH&fЉhÚWï¾õÊ•o¾ö•—fÝ{ý媇Çâ¸Se&†è´Å¬Ù†‡j6L¯‘L„2±¤-Òꦃռj™'V󙆷0¸eõgzcu¯€6®ÓJ¿žÿÞ'ˆ;:¾DÇ*05©X ¸#fºL™ÞÞÇõ»ôgÄ£ü™ÀæªÍUþ"Ïœo¾˜©à ‚¥´¼™uR'~Žiå‹%Eâ™™)>DÙæ‰!fª¸­ð–l IžˆƒÓbFŒÓã6Æ OÌíI ª=þÎ~9D:N£­¶}Ãç+Á¦•ïßx·{®4Ìİã6,þ3SÃ"f¦LéîÜñ †*…-Ò}ÓÁi23ÅÌ3S¼z‘/¦‡¿©äø#D Þ$3Åyã o¨‡í++üiù#ÊI'Ïø÷ûKŒ?_ynL;lÑ©šÏøÏ,wðwß}ñØ’ÏhRqeâꀫýÉc.ÏAùg$¢ü‘žÉ\ñ]¸Xë@hy‡þéÈ{ó"ZΡÿC]hY‡þoLœ¦®¶¿mýº/¾ýøÃOpøðaxi®v/˜nÏì#åÿçÿ/ž{ç4„é‰ÿ ¿óXͶjÅØ—ðÄd-¸0 ãs¦í€0ÕܼœKã”s@™‰bUO® \íp±¤ŠuÕ©ƒN9 wÿÜ®´ŽvgJ¦Õjs`ŒK~‹p8ÃÎí®ª*©*/?„ŽtÝú‚·‚pVá±]3¢ÌPq§Ê+3SÌ\ñ7ö#:;<&k°ÜA)× Û€á'ÓgÀ Aδ´ö§3Ój±Æe3À¤AB"¨´ýj´ýÒªŠòƒÛ×®)Ì_±Þ[j»¯'§É÷iBNÞG°ö|ÞÔ^ÉG¤¨¹ˆ ó1>;oæó3avzîœé9_Å)3¬ö «ü˜‰b¦Š™+¾xém"ö1A´-‰Ù3E|±­3I,Ñc»(fœø ÞoE0Rì7Y,÷d-IW2 ÐZÛ}2`Û ³óÎ!È—°M~~zάHm˜Õ|ŒT[¿i;o…ÉÑÓrçœ>7w"/Õ1ƒÀÌ;î@Äf˜)`fÊ”Rà.ŒY²ñ´'paæˆU£B*eïã?3S-m¿QÎ ]–;ƒ D ÖØîC²˜|1¶¹tïþ§±Žb{wk¿'…’¢æD .ÌTnîõÕr¦GFæ“wÿK°5¸¶bÕØü‰ƒ„àgf¦µIcimêp[‹`¦ZÃ~+"O²Ü›ºVÉô’ÖÜî“{ŒiÊÄœé/È¡¸Æò˜‡»tq•âL˜š÷C7‡ýÔëdëô‡8I¨˜X–8 õ«þxµŸ¸ ©çüˆ ­ÚqÇ*.f¦‚*fDšf¿•ÄB,Ë=±øÊØ[¢Íó½µ¶û¤*–H™B2®VT厹ÓržH*%1I@\™)ΑŸ¡¢YL­ÂT·ÅцŠiƒ+3NÁL”`¤ø;»¸çËmRýrgÊŽÕbÁ«`ªDG+¾›ž[à,÷Xh’ä„!ÐVÚ}ÂŒ&b¿=0Cq~÷$ÉHEƒ^Ûò›¦*¿‹ãóX>Úê¾qýWu©Æi*¦Y °Ì<ñ%Þ‰üˆ;>µZ':U¾‹‹'q‰w­QÆ¢ÌÛj¹·–ò”ùhm­Ý7¥Bñ>RþíŒßB½w ÛH!ºñsó¦~C´2\’7ø$ñIDATh+G aLGnî?SvywÝ‚ =oÇPß‹ ”½†b!Q6ŒŽ›ƒj1–¬&,OD’äêÔ@Æ!~NªšŽŒ¶ZîM‡°L)hëí>Qe‚Î4 Ú”îèV»ð>R˜¢ïÄpò$/°’6R‰B½õÄÛ$#ðSgŒÄ^¶>`¦º#QÞæ@:‰€D@" $`¦ÊjŽGÛÆ›Q?7í¾åIA˜$B" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@"-J´åÁLÛ©>ò]`è4B!£»¡M1¨”e“¢*K »sþØÛÊ÷'*ýDÇ;1gÚ%ªbÝÿÜ´û–':­–nîBëmÉõzšmÞÜ{ï-i©ùHº‡>³:M«ö]J†r&‘1À ÊR Ňû>E1VŠeÑñ½†,z}œâkNzE™+©Ö7ž»ï¾bAË„ÜYU­ê—¤*ž{ ûD‹oòž¼ˆò”m8yËHR–¬‰‰6²XyW[>ÞöæŸ Ý˜¤éZ?Êì5ñã¿>㥪B›ÿ°òŽ¢Ú¦]:Ù“/üFzŸ˜óH2<“6 1g¤¬²¨ôæì¿å¼i±øÓ‰n%ò-C’™ dyê2‡ï0ÍUªõÅð"™©08Õ÷zØ£½¼:e{«|¿GrÖ´"3ˆáoQdôK2|9+·¯Ú;ðÑ‚§z§'ò'w­¨/Þpßn}ê)‡g_ùeº¡_NŠ1Ì[¢¢Ž§Zÿ C,ÊÜ^©|ƒ4LfjâÔé×éÞêÇ|ŠòX=Ü”&i£áò(ÞßšûT†[+)´’ý¼góîY+ÞËûDyFÛ†'äÎq‘v`‘¾rnÞÔ9Gb”O–€Ú\d.˜iöÑÖ7Vèº1LS€‘ GüXq]¥ÞÎTY6g¤-œßÐ÷ãïÏû•AîŸ Åè‰ñäEHº¦Búµi¿¶ Î,¸Í«ëÀ4Mô3R o]0‘É«¦}ë<ºúü|×ù¹zoél|OB‚¼^%˃`¨öBº|E÷ê.î:DðŒÙµD=ð|^ΟçNÏ™A&ñÒn-GBÏkiFQ“$ØÈDZZ¾9;¯áÝÿµaWÒÈlË`fE Y$SÏ´\¦éÞyȹ+êÜdÁÔzò¾’§|:'ëçN,®W‚1iæÌÔ’Ãîgˆ”¹è˜ïJïý§Îx×GÚÒîŸþÖì¿e/úÖ&oÌyx Fž“Údæ[p¦GÎ1l奫^u]#³Ñ“tý“A3óo_7yÄß#ƒU:EÞ¯¯ÅŒìÖÙyÙÏׄ{+ÒðuùËÍ5Ô"-o´ª€1K2—›«@ÀL÷'YµÈiimøöéÓ»TUéßCý8òÞ,6•N"ÐhrfêÃYÖ uÝÇbûˆ%Ku‹6w®çðáùß<Öó‚3îÜYU—~WVêþ Ãìí2lGu‚l¿4>;ïeŸnÜ ¯ ¥š¯*ÆÛ³ó¦ŠxÀØ¥-Yj!Kîì¼)ÜýðÃéÅeÞÇ¡>a{fÐ l.Ë_ž™2å §7>'ï°óxS1Œ¤Cµ§%[Å LÈ™v¼éròbG<ŸŠõ©ç§Ý÷ÃÄ©y7`vÿ30?ã8…C˜\¨'{òŒ]¼ãû=æ,?Xù“bSNšþ8¤§’B«ÿ;Œèÿĸqãv15q3]½´@Q,wÍ™6åÛ‰÷O?ßçóþKQ¨0ÙÀñ*eÙç¬Võ×ÏæfÿÄïnœúàÙš®¿¨Z”›æü-Û”bÝ;}˜Ïk¼™•aõÈ=÷”Ý”;ýÍk@dO'Aõ‘² Õš:é‰Ü;ss¼R'c4Ô7ÿ%CŸ&`”ìŒÑvþ.fªÊÄœé³ñßzÁñ&çCøiëwÆhЬ‚—0ý&6, œÂÓgh…“†3æ¸Ñ:)KŠ}ªº³>Ïf}Ðô'àçD ›-åYÔ¹'p¯5rNÌÎ;g—–—‡úëÒIõЭÚèÚ9¹9¬¯ånÊ}¤«¦ys‘ÿ1ø…z»íîïsÿ–ý!{l(ͺÚ)ÚóžúÚ¿³}Êçhgùªb;'ïn³Ü0uƉèÏf"I®¿*êû‚çó¦^ ìG¸¶Ú†ŸÌÎÞ;aêƒWÌÍ›ò— új¯ÀDÞ%- &Uó}ú¤³7ù|¯yˆ‰‘ øŒboÑÓAÿzDç= =ö÷<Èõ/TUYˆ.}83e±ÏPn öW¤s1xÝÚá ~Fêøì¦XÕËm6å"ÄßÅ[©¿z$ŒÑ|F.„KÑ1ÿIMQ.?òíÈ“Õê`&ãE±^®¸”‘x¥¢ûf°»Åöh¾dâÔ‡Ž!˜©Ãó€L:Ä{¾[|>”#ŒŒ5¤Æ;äR†¤ÎÄ@óÀg®~MFÊ )Š¢þÕnµD\Ÿêºþé­¹÷´wL_w€Ž’T[êÉ|uWÏXŒü•ø4ºXÄÿ¿N¨H̓ßëøŽq±„1¾!wÆ1`¤¾Á,sU± ÂÆC¥Ú¯R+ÿ˜~ö¿x7Ö0|Ï€†ÿX¬ÖÁ­§˜ƒgÇ~MF v8`„ÿ")?.¡¿ƒ[uWìŒTP¬†ñô G Îzö‘%5(Ð×`Öþ,3ãuy¼);¯—¦é_CòðÚÂÈ•ïEÙÞsÃýŽõoï’ñbu^Éï Eý×Áó‡ ø1Ôÿ÷z=E%±ØlösÑ~ŽWH}Þªª…ü-²4n§ µÿšvÖϪèvN‡ëºÏÐP×i'˜¾KìŠÁíøÉÈi`Ÿ~×–ÛðÜiS¾cЛýC $ò&hQðÝdÎS]ý,ËŠo‚ÆŸ>ži}õ’ÉÚguÅk~,FïZ`Þw@2ÒÁ4.µ•½dTÒ´s§x.7;ßôç3þÍüÕ¹¹+1S>LÀhÕj=nvî}[ù;fÓyºa|É’&H 6™ac€£sf×§o»-¬Ýȳ¹wïßGMÿøxþ1Íð®¾qÆŒ¬§ï»g'â{Ì8·°Ÿ=Ú·—`д÷°t«Fã2sNÞÔ²¸÷Ç0®§áùüÒ®`ñøœ¼)o˜ÿ #wBÎô‰Õšö»ço»m$F°1„‰ý€ûŒ”ר`®$EªúŒyóæMô39Æ`Š>bÿº×ǃûŽçó²Mºñjí-9Ó ÝmÆ,þRüŸÏþ0 §XÔŸAºeJøT²¾[4‹Ùê“èYO¢4Ûáaöۖݰ§ Žóx(/ž ì¬˜¼Õáˆå•¥ݬ=ï-ÒvÂÌÉx’Ý­0>º›eÀ‹¹¹ãše=K÷ù.K.ºaJt>«;n¥CwfÝßÀ&‘‘†Êsö®ãÆÝVʪ>Íd è.fl|å¾+0ë<‡ÃC¢2C•®iO‚Q1£„ºŠH¸Ô~xág¦Hy³>FŠN˜3Ǧì8øs]ÑOgÑÓGÞv Ób¸‰iÍôÁ× þŸüøwVa´ú=$J¯äæ^_ÍáërŠUYò~ f{½ùݹµ/÷Vô@~/íf~À(!OËßjbʯàØþž7u Ñ@Gà½ÉLøC|Õi§æ3¼ÌˆÝ¨Ú,ýfË-‚¡¬õìuS.êŒÉ€Öúã0ºƒ*ÊWÿÑ<ßPT5õñN´•i¾ ý¨§Ù»¼ë¯ã}zÑp´›,Ô¹wE\ˆ¿7ê]_ñ¿¾;ê—ñÝð£ß@ö™UµÜâÓ}óÐ^/ò\»4Û¿‚˜îÓ¬ÝNçfgﮯýh<C1Y©Uןˆ"¤!(e®‡ü'h)`"Ô4N÷é·&*%th£æ?jc ÌQÌÃF0ƒŽúPóÂðÑ@¨ 6 5’b¡`ëô[V«é•>Vem˜›ó½é]QªUý»¸ÀD=¡ZÔ‹tÕ±\¤¡*´K<×uç‚vì_dïNÕP¿¶(Ö‡,Vúk°ß9Ór>Ÿs6Wý%÷q0Zt…EQ_öúlñY¡:<â ¶ˆÍݤ™ª ¨ÿÞ´×ܯ³+ÖÇ„ªýtáñ¾ÿäv(;çëW!·+HäßÕ å—»|O3V}þð~&FxƤ\7¥µbB̼ïÍÍ¿u8…ÙZìóú«ã«|Ì*ìFê׉’&!YŒ( Þ#jn^ö£kÊH”óÀÝúƒcÌ€ 1³‡í‚ÚŒE¹Ï/#‰ø`û=ˆŽº‰ +38Û;v·e÷7,t?˜³?.ól?uÆÉü u'¢4ëj§õ¶3òàƒûƒZm.ð5BþkdEDþ—´ ‚·„Q<ÿ©~£jÓÏ–Gìó]…ߥ¡i`O˜×4Å“Í6P)}ü¥CÆŽý°‘Ræˆ÷ÝÔ3?,Ò—{ôo·úî:¨´þ!¾¡Ó^)TG«Jû…A¶øÍ3íQϰÛ,ŸÉ²žÃŽŸ:#X‘>È€:t¿«ÒÊí`^ cÙð“Õ!ãs¦F´çü-Ç´ëi‰;˜;-„á!fœ0[ÿõ£Aݽé_ÅÞ?ºþ¸bøJðþ³3ª߃yý¹ˆ“ï·ä>ÜÝíõ Õ-´6ø}Øg«z«ËfWU?B¢5éùé9³Âúm£Ãósàž°ö Õ÷ˆÁO®é¿öö!¢ø¹¿NÚ?aêôC¶G¸ÏAãZÔ©Ñb±B4q±ß ïu…«‘|½‰oo¢Žb¢¡MÂó5±¤Y_û¥“´]ã‚Ð÷ü?êŠßÉ6ù^"Ðü$¬3ΚŻu$D®àwñ~£sv]qòæzP1<Ãéyèäoµ¸Ô|íÛ—«; óíØ÷$¤ReŽ.é KKÀtüK÷·c†;Òî:Âô°æ|Qä¾Ì«Þ3mZWkÎW»µ™Ç)Š÷ÄÙÓ²çÕ•~]ïÒl®uåZE5 »àûzžQ+†v:àZÎæR^„qûý`¤Ra‹R¯TªVÀ0ׇÑÍßŽÝØ7Z¬í_ר*ÅÐÝ'Û}Æšg¦çlƒ}ÒwT¦9‘·ÑXѸ(ª°ÝÔd0R®óF \<z¨ý?/ò­ï %á8HÌ øµZ^&¯~Óøœé÷¥Ys*¨º³«® #Û@=:½ð×À¯ò?õÁk]_€„ß=7mÊâ‚´©ÏžÄ{'$ïºæã6–™â… ÍwŠaµ57wÒØÆ Õñÿ¡žuÇO˜(Ù‡-àЦ‚¸hŽÒ9³È½¿ü ìÿb)Slgr±³ó+V›ÛŸ”TwÓVkšõµŽ;ØYË3šîû“ž©—2ÛâqBóZ1è¹Üœ¥‰Ê·lÃÁ% Ÿ%Ƀ@“¨ù4Û$Ø¡# › ÃïF't',°s}ÚÚ±¯R7´±Ž¹€lN µo²[-¼gK¿'¶<`òÍ™°K¹ƒXT\Ÿ`7l™<…>èsæ.ËçÞy«îÇæÎs0È“¡½úîñ=8LMÚQ6§úßàoyÆöX­þv_{µC oÕ~0+³}ª¥=ÇÇ;V*=Œ¼½…™þÎ ÷O7mܬºÛ&ŒŸjªøØ/ÃÖl>T¬õÂw¿››;ež®€$ëÊ ­bi¾„íŠ{—Ì8Ñ+üEr~Ú”/¡ª|êÅ×x?šH´!?aU×qÀ·÷¨Çi¥b ƒY\P_` Ï’a¥/V±Ž“;e åÉ ¶Ø¸íónCÙäÞ[Zfèúçh3§ÔuƒŸP'~Sq¨b3ÚÏ®ÒÃî U`µàÝñH3\û%Š%Ū¢þR¸‰î }W…V¾WÓŒìxК–ø/Û°@BÞ%É…ú¸Ä»ù¨“ 9Â@žX7öØ«¬Ê¸×}õ¥Âû¹hÕÕiOO™²¿> }c!í.î–‘j98kòäºí&ˆ˜(·Í˜Ñ±>Z0ëÅñzŒnÙ~+nŽUo†ÝçfEälÓUdÛÐ rÅ»ÆÜÙÖKÉ0Ü,=hLx&<flÀ Þ/¼Ø¿à¤€ ïþç†bâ•°>oiæ3yÙ;Y=Îÿ„‡Ê´W+©é”}aíåÂóþ†Ü™Yµ.ÝD¤Y)·>ø`'JK+ ˜±ßDÑ Ûp]%!ßIš¦a¦V±\ÞÜÄ1¡¹{÷_-Š’«'4‘&Œœ÷±ÁV?áÈçóææf¯h¤eRIŽŽŽY6Õ?‘d‚Aù6ðäí9¤“H$zh›)¬¿ßYt=dÄþ *²C­…‘‚zm VžÕl¿‡Êä%ì-©Ø«HëŠA!´)J,3EÊÖšÌD@" H Mb3…eÑ‘­âŠ-M‘FlF…b‡q{0R·w³žÅ+”¤“ÔF@‰pedíPQýÃj¦ÝST¤g‰€D@"ÐhÉTÊÈ3¬üáË2̤Ó‡±ò¥¹ 9q 4YÌ5‡.Œº›,a™P‹A‚^ÔwúS" Æy”_%2~·D@" h-4‰dj̘EØ¿û%ÐYÉòz£—QK’ ôt¶™PªF”¢|¿vÒm ‹_F,HZMÂL1^ع¸Î"ã„åWÝíý)NqÉh$IÀò‰Ç•`¯'ŠPììŸÈöš(²e¼‰€D Yh2fê’»½+ z5Þ¹„ṡX­÷Ä;^ŸD ÙPö°â®<Þtb‰ïŠ_ß1$îm5ÞtÊø$‰@² ÐdÌgØ¢ºxWñ=ñͼòØØ»¼Ø,°ñŽ÷ÉÁÁ¬ b©¿H)‰&>ÞkÞ¼yØ8:¼›;'¡»Ì‡OY~i oT„ÝñïˆoÚJµb±ý[Ö'ý#Ñ´ŸX1âöWWÜoÄ«Ýñaäæ¹A q›çCƃ^ÅüXsÖgƒñD’7¦­¡~©Á„¤‰@+@“Цu fÚNõÚç0 M5eÿ‘ó”s¯`›¬úâº)wF?¯×÷:Žd™?wZ޹C1ûçöÜ•>¶µJÁÕNQÔ¼9Ó²_æoÁ.R&\ZÑÆÇÇo@ê°FÀ‹}†ï1àŧ€ãûfÎôœ›ƒããç¦Nÿv…þgÛY¶xmˆš†üŸœ`ϧ°õÈ­±S§èhS×NÑÑH¨£«TÅ2~δ)ߊ´oÈËëá«6V¥tÎìêÞWò˜½ïŸÏË lüÉXåÚx×—Ãਧ|Õªþfvnö*‡¸ó@]½·ô ü¿ç3¶h¨=FÒEâ.Ì ÷çýÒ§w@þC©Újµ_ÃgäÕäãQ,ªáÕ+ñí°Õj¹õÙÜû6Š8ƒïhŸãÏ?l6åœàs=™aÙí›þWC§Ÿaó˜ýÀÿ«¹Ósä°Ø°÷YlØ;}Ò)±Ú²~ólîÍGI"Ãáœ>?óù¤Ø}þ9ØÚíÇe¤u N‡Ø$üÝ8uÆHô5OQšu,ʨÁ¼‰²ÆI7¡OzEÄ#ﶈ@ƒÒ˜xƒrñdïwQ]ÌûBÅ7¿ãìÜçʆ)œïv®¦io¡ãXšŽ€¸Ö\¢C9 çÒŠÉsyGåÆú«/­à8J—wFG‡7Îj±¿ªj%Î9»ƒÉésó²OÂh7„ÏÎ ŽÏHCGýkìçµ-ø½|nÞ5ìv•ÔÀù’ʵ¢¸aØø›H)Nƒè«dø® NOwÓÕüß ìnÐÙ8jæ¬`?‘<³Ô ÌØ—h;Bý××~"mƒÁq† ÜIùpŠËò+´¿SUUyÐëõüÛ ›•Ui!õ¼?žÛ&V iúÁñŠç Só¦ë†~-ú,­TÛiN#Õµ»íÌSÏ%‚‘2Û¸aƒ“Κ›—s.¶JY£ùŠL©ˆ¥>œ„¾×äå…—!¾3-ªŠI¥ÂÇl&¹¿EÏó_Š0o¾*zå³(|´ašœ™b¬ÇÞ­}ms¤œ•ߨ±W¨ ›É—NÖ¯sýÖW3Y,¶µª5å …¡i¡ƒú…ÓIfçÈçÒaö÷‘áóü¢±þêK+8Άҽ!g:b»‘gÀ|¶™8ç ùÆQuô=ô/CƒãskžgTÕzÌÇâªNC>'/\/ÖMvw ØÑo´‰:µÊª¨§¯¿køkQåR±¿¾ÿ*?óï ÆàÔÀ½•j!Þ'í9óø¥(":÷¶RRlãU›ã¡Áêk?‘¶Áà8Ã…ù|Õ†ÑðW Ž{â-Kõ1¬Šãs&gçMù@Ä£XLõ!âðÝFÖÿÍ™–s%ÞÕ’ s<]ï²¥Þ*MFgItÀ¡£Æ¶_(©WNÁ^?/؈³ísòîÞÀïÍíW ʪEþ_cæð+Å’2/’¼Ÿ:ý¨k?‚¦í^:‰@[G Y˜)ýÂÛ«¶_v·qV q'óMƒ¡P©Ê³vGÊ ±wë³xi0 <Ìμ/¼[«#â/Ú?™½7¢lGÕ+ð‘úã0áÒŠ6>dì· í¿ÁáøÙ´ ãb+Ù¿߸SCNòçL»w%²T¯M•#ï­µ“‡½jwpˆö4LTö5˜KEYƒö7¡ÛÈá'®¹kè úñ`ÌŠ±ï¦ûg˜’§ ¹õF³ì‡f?^í)êR´¼•´}Ô¶]s§Ý[à°y¢j·‘´AA›¸‡ cU–Šú–î eì¦ýDØÀÝK—à[ ]Þãá™i÷®æþ ŒS­¼ìÖ¶öA/v°B¯<}BNÞ‹PyNœ4s¦ßü¡Wç…X­éŸ“÷?¨ûþЏO 4Ë‹ÁñŠçºpßÄ]WôîDÕf|cg5Uõd?»õÇ ßY57wÒѻއä%áÈÍõԫテË>G€,ïmksçþÒÉ>Þêí3SŽÕ ïù˜ÕŽÛ€†Ï33¥÷M`¢–:;ôZè—DUÅ…ä›x6}›;82Ìx+`vÛ;ø]¤þ‚ÃÔ÷ÜP|æ!ÃÞõ—9l“C㙘3}2)ê–Vñ·›²ózyuc|J—ŒÑ¡~åÿ¶‰@ÁM#Š‘óû¯žg<°jûÚ3 ò… ,Ê€ìÃ}f«Iµ.Zwç`Sí´.¨À¸½æ3 Võ-6¼Ú8¨øÞ –²øÏ4æL¯'UùI"ÐvhvfJ@}ñäê-xþ‡øï¿ Ÿ¶Öþã?6äŸW[”n(醢ó`p‘ú hà¡¡øvi/Á€÷)¾Š«Æö ¥W§@‡­Í—»žºõV.îô©¨Ûz—®­#ðú8-عæJ°ç{Íдo¡&º­HËGªzOhb,ý»!¯AOc•Çok5°PÏ üo¨ý4<êÏÝmÎÜåÛp%˜ÄA²ýÍGž RÛ§‰ˆnÊ}¤«Ws¿j±ªãf³©@d…ôi;ì¢î­ ö¤SëM»MÍ{ŠNú-.kꟽÊ;±оS`7Ù8îEUAr…‘nXŒâÜܦìÒv^œ™á¨µˆ¡®¼M˜:ýϰ­›û·{—áR`’³N»A>¡¶ ˆ¤W‰@‹A ÙÔ|É€Ú~Kw-˜Á@Ž2xÔŸˆ§¡{ýñù~ Áy­âÄì¼s|>Êu9Õ_ñŒ•ã¿ùÁ; #>q-˜85¯”/¼îH;öB‡|QC4Èïx 0;÷¾­ˆg[‘þक़úôè®Nù²®x{X²g£¾v-.×.«ë{4ïêo?ÑÄÔ°ßÜÜqžç§e¿ &fûFw<~ç¦xœ·мž÷±âöîºV#6;Ôõ;L+È ˆ¿T%-ŒÔUP>ûDî‡ÙfKµ)SÀ°\ä5ºGúRÿ¾"ÛHaÖÕGq¨»vk»-ÀÂY“'$Waó¦W‚sš úÐ;Þ0LFïY·¼KÚ"m›™"zCS”?rÁOÈÕ×XÕêxßüŸwµØ[NX7æævcÎê<ÁþÂÅw÷ç£s=³›:à#ß Sgœõãß›õò`û®g¦L9ˆUˆéÁ×ÿ·s7!Q„qÇwv'1ŠJ^ª‹·n‚ôr*¨S]"¬ÀPºu‰%‹…r:¥`g‹¤KD—Ž ¡:Q÷ C&¤kÖÌÎôûïöȨϸ½ }dvŸçÿ<Ì~Øyf}ÞTæSWx¤CiO]yÎl¾€z§êiUß݇•Š*K/á †ËFuŸéüçGÞýÓªÆþ¡êI¶jçË·¹Lõ4º­aöqË·¶a)®=ÒµŒg'¢»²¢[i?\ÚÚóä­«ï,Í­v¼\©ÚplØÜ^!}¡û¾×•Iã¤WyÏì½m Ñcø˜ËË;gãlN¥âö\º>zÀâ?&ç4Ì÷f²\ž×PðY­½çêÙè³iµáñUmNLiUçE­Dtå9#ð? ü3Ã|[ßîýy ýr^¢åÍ껯 :kLç–î׿—ëºfóâìšëqZ.¾Û [µ”{dãòêS£Ú§ žØî¢$©i2ñþBͪǩ‘¬'Ñ ô¸Îl¥@±=Ñ2yÍí)lø@¼Y~¥ïøcMÃþ«Þ©¼û§¥AšŒ%õâù–q™ ­Mè`·æ2ujsÂz©,{iþë9õSÒgéÖ}i{a5°xbÇ®ío³í‡ËòKÛ‚ õ(–Ër% A©Ø˜+†Óqüù®ê~.×XsmAzÅêˆjÉQõÎUõr¯¯N—¶..(õ’xFu.hˇö ,±¨Z¼ØÓ<íÊmôÙ¦*×^»8Î €À*ëú¹4x%½1Œ¶ò®ùÂ×L›i[ºî­/ÎWߺ‚$ €€WàwîûÑ ï+b½µùmCÌÁ‘‘n~ª¯ýȶÍ~ïìôå[»dsš²y–ö+×”gŽfëã5 € ÐR` 2²o`¨ª¹A € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € °y?|³Ç-r‘ìxIEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-compconn1.svg0000664000175000017500000010041013553660046026462 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-04 15:54:45 +0000Canvas 1Layer 1Compute NodeOpen vSwitch - Provider NetworksComponents and ConnectivityProvider network 1VLAN 1 (untagged)InstanceLinux BridgeqbrDHCP NamespaceqdhcpMetadataProcessvethtapeth0iptablesPorttapVLAN 1Provider networkAggregatePhysical Network InfrastructureInternet OVS Provider Bridgebr-providerOVS Integration Bridgebr-inttapInterface 2PortqvoPortPatchint-br-providerPatchphy-br-providerPortInterface 2PortqvbvethInternal VLANVLAN 1 neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-flowew2.png0000664000175000017500000044536013553660046026637 0ustar zuulzuul00000000000000‰PNG  IHDR>¬Zò@sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì`UþÇß›ÝM ¡IoRPQ± X°€b¯gï„bA,@²Ñ•lBó %ÁvÞéÿDïôìÄ‚A‘^SwfÞÿû›d–Éf“l²H²¿›™yóêçµß«#+&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&PC²†æÙ8¨÷†ûü'áÎÍòί÷QïöMê\"<îµ¹¾±kÃ9«”’#2&÷1µÀášÒ¶‹$ùËŒqãv„3ËzL ¾°ó¼[ˆ5Ó³¼ëê{x9|õ‡òLÃV>ß<÷FñM0.Yà6Ô.Õ§éÈtwCªCÑv·­RÏêÛ{Ù5×\cTo»v&R3²nRÊlžëϘQ;"³¥ëê˜l…†^“RªÈlÕ­©_N’fl?ÌÆ!š©)!ÅFéò¬šé{hs,|.Öõ«…PS5C÷ u“‰ToÖ¿”PƒRؤ?ù¢èÁ)SÚ>þÐCù¡æÃó=¾I]‹õ’aR$þ#Çÿðïõ!NT_mßv£°Z¯ËMM T®{²³Ûé%²yÑqƒÏw{QUfãíçu©y÷¬x‹?Ç·ö¬à3Ü;é0C”ääéóOJ$X %¨êÍÜ)•üWr›¤‡Ÿ=º°öxŸÍTŸÿS“J”: Ü ðܨœ³xŶ¯ÿí!ýú µtÏÓO'oÚý|ò¤de½•›ž¾±ñÑ­#ŠwɦÝ©À–цP‘ Kè ö¦©R¼™ßišöèÌñéU´;½d¼ k!ÊÆ3 ]Hû&Uè!r%F`¨R"]È@[<‹ÍÚ»´Y|ÛCè–&×m¦ÆšíJUQ¾9étÛ‘w: ÅÍf¥@øˆ)ø¤z3ï1T`*ò¦¨È—¡WÿògðHRÂ쇊} z¶wïÝžö_Ö Ó}é?Å€UƒwbdÆÄ#z`dfRŠ¿Àìü~RµJõA{."yH¬…÷̽÷£‘§‘˜6i›Ú#[Q¯üH¯—NËò®±õõhÓî™Ôp³|ú5î?QB+֔ꪤ: óI€Šë2®#Òý”ºi¾vH¿ÃNª‹ô­Ëð×Úm)¿@ý°y{n­Ý¨[‹cP?½±?ë§Æ\Öê6©ØõÆD Á >C½Ù—šÊx‰`H!Ó;»Ó'ú|Òt&Ê=¾§[v?‡©Ž«uÝüèáI“zM3fÓL<Þ”þÚfR““sƧ òù^l²Alj_Wl0ÅuY©Ûå¼(Î7/„@ÐOx\‰å^4ð‡ÔŒìSLÓ¸ ÇfÍí:i¦oÜŸ¡Qá›Ð«½û‡噄š‹æYwÉ^Ö¨žßÄÐ`Èã$ðôˆ†]]ÙEžØŠ²ØõÓ ˜þàó ÖëÊ/§»µ¬9ãÈ÷L :ZuêÓ{š—ÂÀЯš&îÆâÕìP¡‡Þ=ã»w7Þ]ƒÊ忨\ÚïÜKúÕ©Ù³g»ÈêÌ…¾';µ±Gî°ê^]<ÓÂVŒ‚ °Üv5™*ô>­!¨lal]„)è¦T—ï#¼¡)¤š2§ôÐù˜ÃèãI–CJÎ'ôлé¾q+Ãåag¢É_¥þ‹N¥îÉíNwksNDsÊK‘Ú&ì´.*R¢5W“üMœ‚áÄT<ê§Q?—§ýPP?Š›ˆê’Z”µ(‚TÁjDa¬`«T#ÖùÁç›Mx* &k7W`õ!.X›bšfŽrµ<¸ÝaÕ- Lɘx´2õňdv°ê\WBk+„’?B@J…»· 2§õÇÁ, >?ÃÙ9™i‡ˆ…ϧ´ ¬1Mt¦ÜŽ)ã³ö_ÄÈÆ´²çà¥Ô3MJϵI®„uù‚ xyìŽv¤ö–i.‘=s¼÷Í ¥Jn†>’u4Í'aï›YYÞÛ+1†µNþ\)ÔJ¸žåO{qÞƒ86“IZg'‹pöGø&wÔõâÏ0E³-×ïjnÑ´ØÓ`h´já99tDm¸/«Ÿ¡›³ÁæçYþ ,¼Âb.D‹œLïÄu˜7ó,Cˆñ˜z³ÜÇÞà ³Xý›7Ëï=›îmû‡âÞxë¡pØdüø¸‹»ÏŸïš2‹5^;ð¢ÒÁ5,#ë,¥¹Ú'Ãí–0©ñ^²'é±'}££È¿ÊTŠ7ë,æ~ñÊ¥|V™¹pú5Í_C½Y÷ eNÕ49|%7i=—ÐÕMàó7¤{7LûìL3’’«‘®”ÞWþ—‘gdß·GÀîp' î” Ý×âùC¡¹ss3ÇÒ4sPÕ4ìvÙÐÜÚU´ÀkÍH¸áî¿þšY›ün»‹pNËñg< `ÙM¤ù‰Œ×4N¡~ÙÏ4ÂÀMÈéRs=g*ê!Cx´cs}iËmsöuhºÿE”†Û‡NGšoëÛ×_öáB7°VHžˆ:ì`˜Ëûo5ëA§°]]Yó¸nŒuy§0’œúHÖH”¹kñ;e‚³\‰týޣĸpQìt«,? ^8„ܶó¼Äâæ\:­— ª¡NJló¥Ç}µÝ¡KõNîmŠ’1¨/B8: „@&¶ãÏ"ܽÙÅ•>³ºŽHоi°Ôˆ ÑiD™tFuB™£ ½ª/1ÒÑT+V'“ž­P©öÆï¬¡þL4NT¹¸`ö 8óTÁ›“S2üo§ääxl;ö•z½y¬÷á.ÖÉöÏëÕ›h‡;£axê›Tàmót5•8ïS*pu¾žÿ5*€+ ½ö_„¿ áÖ †¡þ‹BŸâ´î^jm?‡óÑðßt_V oE52;» †xnÛƒjîšWf¢´*P7W´Q^gºïá¿P'xSi!yù·T¡Éë­øÕw÷^½BªÔ¹ô¦qJ¸ö&=ûÙ”²/˜m¿–ž+àçúaßS¹F`hFÖu0óì_‡4J¿`ñRMñ;Özl·qÝ Á#Ý0Õ¿1ÖÕ‘ÌÃÿÿ¢òÅ:0u/Òà[êå;Ì×Á­YÊ[ªóS|ÓÛˆTmòW8‡•!N£|‚ÖsYåf›ÍV„³ªáþ)¬Azú:±FIÊç‘f¿à¹'8Þ¥KåâU›°ÛeÃ4E÷O¯xîN@„r1áü¼¶ùÝva.FŠcMòSmâD~T¥L¬Gœ™9ëëädÄ7Qèæó$\Ue'ô]ª7û =ß#}/Ã;¬uÔrPëü÷®4ÆOvÙvª+kuQÞS&Nl‰2ø‘2„Ntð¤ú yèÔ[…¨@nÄÜÞâ”G².²Ãh_ít«,?Øæ*»óe¡çCTÀ=Ýšö -ôŒôe÷Qªää©;Pì@þúÂó?” õÜԗײÐSÕÆ¥_ãi}êñ#ßÊß" 2øï0{†iÊ^í¨^Ò”÷hRœ=ÓŸñ™ýž H `~€Âp±¶nómПe¿£ë®=%ãp9•Öÿ5ißâvZ¸Kú£|O¶*Ðó߽˱u˜FþAúN…†;…íMÙÜ}{îØ±»ìw)Yw)Óœe*s†Ò1ï_ùœ®/µ£9³áÖ…„xÊvǾ–š×‚”BÅ¿íÑM¸² a¼ƒŠqˆ·jÙ˜åg9õÿÒçŸ ­â5„yzL9GÆèýéð« ¡móùN»µ¹×õÀ# š äÓ1ezÈ-ê¡Y½Õ0£g9=w|zPè!#– 'å,ãJõc-fZ4§5‘‡àu¸aóIÈ#!·²µÑ篘_¨up™k߆6L´ >'Ë[Nè‰6ìÈ3Ç¡ÜÝ*ôXa¨E~¯ŒDMòS´qª, (;ÖÚ>ª?PW܉<‚þƒÈæ›Ð£2;åôõÂ4ð‚p/_w =d&ÇŸF#×ï£ÌôÑÖn¹¦œ½*bYÞéœ0S©{¯½îd÷M¡õݬÌôSyF¹8Óþá‚Ue~cÎm èÆ¼j+…ëZLu—ït)Õ¬ibS^Ñ9O´Þ®¼.?5V Fð)>¬!k™¨oÇ™zöX;³aì¦tk—J^$áURvÆ|2ÊŸ\Þ|bX}ê­c‘ì*Øé¬4­®ë,ûŠÖ:@a+*ÂóÖ½ãÏÝvsÆB%î.$³¿8^Þ¤8„UðrÿQaT¯ ‰¼³pãÍ0fÛÀPïMÖ½/Q.nß áÍEÜQaÍ Ïì3]ºFG×qê/Tê#À\Äûmª¨Ú7›KÃÙ&¶ÉÚaÀóÙ–}—V~¨yŸ£ß}¶x-wÿM$üFlöî]ad­&öÉì믿®A¨{#ô‡Q¹GkêU®˜Vý”ŽZÐÜò¤„ur±¾Ëg»mþ²Ý‰äIܬi`·»ð´ÈRm¾¬‚Bs-â—pw°\ц½ukvea6¿“»5ÍOÑÆ©²¸„êÓˆ/Ö£¥>2ùãvDYBñÆÈ¶ëѕԗ¿®î„py¬‡Êê £tbGÕ¡¨ôæFz rÙ¹Tlíáåd|ì’¦–,¡ Ï4ŠãÃÅy £;Õ†Ø-þ4•…‘±ÁéªL<Ó{¿âÄlšf[n{.Ù­‰Ò=í¶‰ðËÒÏ=ÔÄîÕW_]#A)œÛäÆ'‹³ }‡vhm¨^Mžgú¼ß¡gÖZÑî4b:šì£¡‹*Õ$ ‘Æ­lWÌ, ƒ3±nEZB¾¸Uˆ ±à4Z‹°WW6b‘ßkšŸögz`¡ø'(Ï/€íÅM]޲]š¦îžmb[µ*@Ô:ô}4ϱ*ï¨~ƒ@)uh% ~Ñèq”m³è¬cmãÕ几9%Û(—¸ózctÿ¶ü@þ xWa”–Ì—už^Åí«¥[ÞûÀø:,¤~ìOOkm'™eÕx 4¬©•®ÍÀG¦iWIuÉ24#ûL”=,`•[ÜÍæ‡5°w û¶§¥ëvÿi¿•JY•<¦z·õðõEòënhw—ÀWÉo¤+ÖÒ§ûH$¾!³Öô`™¥sŽé³•Ø.TaØ7J^ž{<í›YÓZdÌ¥¤5ŸŽÅ»ç@èæ4Òõh¹M;ØàÿFTNI˜škOzûSQåK£b¡¿™™é³£ G‚»å×äz½m·ögþªiÜha8íêìîÓùÿ „¼]‰núösØ£Êï5ÍOû3=ˆc’'ùÊï`{í|BöË·ùÚWúþ ôסL4£½¶~,®1+ïšøÁ ”=* Wjv66Š`+?΄ sEeÖ*èãËSf÷þGzÚ»_ÁîoX“6ª‚ÁY™ã¾Ç™@7ah \a¹7ÜÑ!Öø±hP‚-XCe FµÛ½«¨B/Ü™´³‰ñ–ž&|Á5NCt¯›—†jÑ3Š¥ ÄæŽâäõA3n×èa$A]IÛ}ƒúè&±C‹·i½ DŸ+é$R(W“ Ò´MRØEÛÕ³TÐr©mަ³àæ§fZ§<š}>*‡¾¨tßs.ZnâI~~޳¥ƒ,»šøÈv£Ú+ífƒÒ¤Þ±³V%jê%·Vò¾Aj—o0 òõ,…KT‚Û•^ú޾ñV¦öCØc”ß#ÏOû!N6>ºÒÎP!µ–ž©žF©°v9Í”ÞK«“‚½)ßU¡SMY‹UyOÔš/¤aZŒÀ\Ui'µ£†¤¤²:U„ºÊWè•j=š›Nì/@ sز±É¢z•›™†]—tjuaô®Þ›hè”àC°5·x€J „’Gq¶ÇsávNÑ–P,b›CgP ‹k`ne …B3"tW 4˜|þ{©m²óL_Ú¯($Ï£„´*)0^>aÂA¡n“ÐEg…ê×Å3 …y<Ún ¬xE—v¯½úUzêÏÈ>† õ—×éN(Wáó [v u?]±UÔšæ²Ý)­¬Åç¨å°pPœF²–<×~Áu5™ÁBMŒ&UTšæz”ÒÂÝÃÃ|þ¡&èÛl¡zõá'ÖžCÛ¡Ã…Å:‡EfÐ;Œ£½g›©Où‹ÊAŠob7;lΫnšÖNB*_¶þþ{¤ùÝS¸kMòÓþˆSh­I’ÎÅRí 8Üúžž]nW "Ž¿Ò÷ Ù¡-å#|Óš…¼«²¬‘ÙX”w«£)%FÙ‹¶Ÿݽ8“,Ð ·ÐŸ ÞcÌ€ø»1Èÿ¥@{€û€"}â * Ð;  ;¡¼£ät¬øiózy +ŠÎõ)xÎëìø˜3 õ%™%foœ üÂÓ“ÅBSˆÕg'ô¦OĈgZ=k·œº¿ÃI~w†)ô¾¦ùé@¤‡ËÝä3P|6 ¡á§g:Jå`4Ê+>cü÷Ÿ£üüˆ³Ÿ÷b @g9ß9Þ£ïE]ˆ³¶lUMY#c±*ï]Üi£°Ëµ ÜKÙ Ï?ÓOŸà¬¢-È7ýt¥_ðz0‚ÿÊ9­ÝŒ™ÂA/ >oÞ«çÿ×KiZŸ/š’g~u,Þ½B¸Z{P‡'ÖmÆ4¾èpý;Ü%1 ;To4¸"G;c’ÝÍŽ¦ïÝàqF2(µ4`)®Aç‡ æõ%¹„g-ò ¥ë©tDÙ˜†Ÿ—ò˜îr˳s}鋜¬÷GØ#ÉïÎ0…»¯I~Úq #í¸”š¬r É¥I÷šþG~: ùl¥U¶%uÜä‹8¨ÜN¹HÊZ¬Ê;-$Fz#Šè3ÈG½¨¾±êXZLŒŽ vª ¡çÉиÇâ¹EËÄᨳ–£Ü]ŒÔ‘›¨¯ÿ‹ËžÒ:Oe¡,>M÷Ðk‰zäQ„•îYÅ´ý _ÑP¥gýŽ>—Øc—¥ºXaa*ï–(xøFÆ} hhXy\žiŽ]eëUç½·¦» Ä!¦rínÙ\ýUÕg "q¯.ÍÐT×W¿ÿÞ¶( Ñ“4“ðQ‹¼œ´´¿jߺ };ËÜRÐËt®&-šþ:]gûKÓ›E» {7Mׇž(l›©OWúLFÁž’¶†t·ÆîÂÝÝ ;Þ* Ô•…ù@ç/+]6íî¨{D;·ÐvD›õ‘†ÿ@‡½2¦Nýšæ§ú'*ןÿºòP=Išpo>£oÏM¡çâ8ãiYsÚ‰æžÖnÔÿì.ÝÄŽ¢÷Šh3G²KS¸z‰è ²©t¹ò¦=úЦúPÿE/¶Ïª%@‚ÏÐôLüªQÕF˜ 0&À˜`XåÁŠ 0&À˜`qB€Ÿ8IhŽ&`L€ 0&€õ^ 0&À˜`ñB ÁmgUÂ`Åÿ·ØzÆE¬œgw˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L >ðwªâ##Š%¾_6_¥)73ý¹ˆ,CC3&œ(Mã1|U¶72ï:¡¹ïËÍûóp_V?]7³ðáÙÃñÍÙ<%Ý%¸Ô.%¤9Ý7ne]uhºÿfø{N¦·?ð°®i7÷}¾›lÐ×wÁ—¿ó|¾Û‹NÈ«iJFöI•°*×÷àÖêMGn‚> Z¼yWׯÈ,r l². Ä톕A–‘=Ð0ù8Üð«ÜLïéá14r3ÐèÈÍòö¯Ìªô}>¥ù|Ò¬ÊÌx‡8#MÕ1R¿SÓýךBý»:óšt]”ãO{¿:sÕ½Oñ=ÞVŠæ ©~Õ¤†/ÌŠnWóÕ÷øžnQØý)¥\£i2ËT²M3W⪽‚÷¤:Ü=½:·«zŸâõ‚Ù gF“âÞÆ3È'íaæ¸pfê£^0Ÿ‡ œÛgù3Ú„{­^uy¸Ï²P߸4u­aÊ×4!ÈÉò>áô7%câÑÊ ,Az¿‡2x±óݧx3¿ÃÅÌõgœú.ÚçêÂo»ŸêÜÛÅOäò.‚ž„ð££Þøùqa«æžû&³Ç6Û¯>ß<÷†ÀWŸ U8á‹8 ÝÎE‡&BÏ1(Ûn°S`ö–«©¸g¦×› ?Ø &@Xð©,(105Ã?¯§Uf¤¶úyºÍ=¾I§<ã³¾¶nÔ{ ÏWEfà*;,š¨ÂÊRKšZaë»4÷"û>š«Ô /AÅØ\HÏÐŒòØn¥¤û¯VBµÑ4×%33Ó¾±õ‡z³' †ýÕUŠeB“¡n(Sýª×žÑ?¦4dIaÇwlJê"©ÞÌ;óô¬Sàö]•¹oèòt:VÏï}=%#k"ëÁ0[Nð‘ʸ#y†’êì_NR®/µÀv„à¢Àîã¥&&Ùz±ºF~òëáI“šïØSòn=ˆÇ¸ÜâKeÈn¦0 ܺÐCqôùëC3ü£5áùŠž£U#|YÇBà}WH9Ìîq¹å&Ã0.Je…ò t*N × Ö_¶ŸXð©,Ý¥˜­”œ0"Ýÿöô,ïºÊŒÕTXÆ„ã SïZS{õÑ|™àÞRÉÚ­ S¸ÝjÞ Ÿ÷ÛX‡Y ­›D³³kÌR!Æîs^JÒ7Ú5+'`Íò§½½ÏPÔw[g÷þ'jWê™r¾˜9Þûéþ„ÖKáÏ–Êüòùf'äé¿]‹‘GASC½þ9RÉkfÏžíºæšk‚,ˆ‹Ð@ÎÆ´æuBßv.Ü{Ëv³ØÜƒÑ=å‚ð=ÇÖ‹ÕµºðÛþìÚcœŽ»§¦i·æd¦¿\¦Oåa¶m¦1\gezi´'&jº/ý§a¾)]gúÚìpðiŒµEzfÜýØ„ÞÐv¦fø– Ô˜ >• ó¸Ýé€1ó$3`¤Âpz8kX#3£ÚéR‰ãPInAÝýæ©Óì¹ýaÞÌ¡¦ÒÇ“Ýâ@àê*ï­0·æ»chþoNw1’1½ßDTžg8{;ðç5ôË7Íò§ßKæ©×«Œ-™˜<;Ó@=àæ ˜ŸÝÉ6Å9¥÷nÅûs;»½7çY!œ×£Ri—ؾeûgî½·Øé·}^]¦0Å—ÇuýL߸?mýš^×o®5(1âÄw݉˜Æð—N î‘ ½{©ºÁÜv4l¹’µq3ÆÛq_VV‡Â•†Ù‹kˆG^ ëG £m“Ï`¤ç¥ ©¨hÓîïHß%Ää™YÞWÐp>§)Q ?F:Ëtd"÷~Ðo‚ßoRºîËqŒ9ÍGs?4#ûLišˆo?Ä-€Fý·H;Ý?f¹‹Q„©Ñ¤}4a‹Ä.MíldÝ î7ñeIF~û¬œñÞwl7Ffg·Ñ Ô]¦RgãýÄuÒqôˆ(ÌjF/Ä»È ,{JfbªêuÛúJôðEkøõOÒCÚÍňÏй¿® )ÄHoø„ é{S]š¼~õ†¼{ ´ƒ‚0ÍÁ˜+LlÛük2OŠÊ‡l™ÎçÂÿp±KjÞ™i_–š"á·ÝB˜öÒ½t©åA½*nªË‘„ŸÊ6ÒæBÙÌ“"öèOÀ»AH ò ‚ÃLFP¸4M««/¨L+)_v @‘ÖEáp„=¶‘ùt£ZS[ƒ¯L Zh{X…#  ­•û(4¬aÈý†pfœz´Þý‡hL]Xg2 `?Æû¡yzÞ{¶9Cº¨1(íõIõ4Ö©ø•ÔÐðik`æÒû}O´¶ÍÒb]T\ƒÑX L}dBiC—4”7®Ô”ú“ÌR/Y¶|á“| ~ß‚aþw14ü(„7ÈLPIÙvOÍÓ³§BH: À³x÷peBOª7k z®»\ÒÐSæÿ‘ˆÏùzÞ‚ ·á©©¬wªäø2¡ç#L»Ü½wáï-z¾ñ ½7¥4až†Ô©×\„F/‹~p¡k¿e ÓÖW2á;²Áî4v=éÞV©Y7™BÌÅ3œ•Bzz€é’m3±ºK÷߈i™OÑ í@~¸?/üj¥‹’R3²iʇäµZ§}¬ÂY•;$8ƒáäÃ|ÍBžEž@“nˆÿŒðN:¶È7_CY ¦ Oßý÷ñ;B.ó=%H¿—‘NAï'Ê÷ôƒ{‹lûtU€`޵uþŒUôœ¬¡±FÎÐå`z&¥˜çá¢aQígxõ~SCNï,¥ä`„w¾§Srr<(”wn„­w‘÷€GIºiÎKy$+èn,Â_ÑɓޯZnB^÷ÒB][?ܵºüiøQöÛ‚ïÅboàsÜÀ{ÒiÒ­-†ÊÞ»Û›ÙÓö?Ò4%7«©/ŽøûÜ­A]d[ªæŠQ÷~(Û›g޳¤£üš DL€G|*AU¢—4Eü*FWnÄ:Ž©XXûqe»Jw!ìž‚ õ], ½¼ÌɗЛÿÈ ©Þì iqï¬Ì´Ï‡fdu¦º7ÑðŽ½Æ§t±¦©åëEgÀ®Õ{Å|7Ü‘? ¢FÅi^ ýä®=”¯4Ï'ô¼Aÿ: £GÙ£Ð~s˜7û{CïØ~“YRhœz`Z¨_gÏa}¾kJJu+þ…°w—i*Ú 3ǧÓz…¨*átQçcj¥œ{‘Á– Ÿ­þîí1Ía´=“–FÓ#³S2üXC¤ÍÌLwN,êÍ:rÏU!ú¶[Á«Õ l™ŒuÿËõ§_‰Fm¤¥ž ŠÑ̓S¦$ïÞY<~½6Ëï ÎXúÏ úüù¦iNE\NN}dÒeÖ.ícTCÈ;Ðs?ËéþÎÑœYYÞ“œï!ð|P%¿ê2@BÈ2jœÅºÍi£rü^KX…~è4ãlø3<þ—N´p]Š.pI£t‡%È´$ L¡0@ º—…4:0ÌçרŒÆ ß7£|O¶*Ð÷‹|3ŽÌ’Rë· ÅßcÝí8šN)ÕÏc4ðOŒMÂó€X„¿Ì]ëB‚FJÇCx¦xóîß1 ùtO‹Üg|÷îvš‹$?F~ÛM”¯$,¶?ÇŸžfë¥úüs €}’F“i$ÈRÕ¥©m.Òú‚Ì×´.²ý¨ìzÿO4ÍßV0 ©ŽM Á²Z™qÖgØ×SŠØJ|ÄŒŠÅFº=ÃQæš ½ðÉÊb^²uÏihÄÖ\âE§ÕTê]itê‡ÞÓvlôjþ’Âd¿ƒŸ—£–ŸËó0šC‚O©¢¡|˜%;¥ê*ôŠæ:„K{¦? ½[¹J óº2›û.Jz+z0t­Rñ_ao&z#Â5RûªÙ¦ æF*DiRÎC¥Û’ˆšùR¹i¥o>nvBÂN­qEŠ‘24»*ü¨Á£öì)±üB£M£jAE‹B‘^X0¯ŒxlbÏèÒ>èl­oМ „ôw¹ó' qLU–æ5¹ ‚N2—›š€ýeøÝIÓ–UÙ­ì®_‘1Ãlæq µÈþr.ÊÁé´‹¨tdG]€MM3MÿžFÇ­ò‘¯žÆ_si.ѳâw=ΟBO©¾ ÑYÑŸ:-±©oûþZÓ?Jž‰ðüŠ´ž‚×ë‘w&Scn›Š$?F~Û=º*·ëeçóÌGÓ‚ÑV oöpê‡Þ‡¦i¹÷UÔå̉ZÔEå(÷´w[Át¤½§iÓ}‚l9üÀjI€G|ª—ë»#7iï×W°uùC«ÑDf+¬á8„îMCLÆHÅ[_b% †"`´»­WÙ=aTÖj½OñMì¦ã\R6¨%Íß‚÷µ¼Áôâ½83ç)„ö?Èßo M[/5µ Ó'ï#Í‚*×—¶ÂÄÑÃͺÒƒ(a:é3w²ë Z 4æ&Õ—Ý× ý¥K{4ôµËÝò …òÿNEÃr!µµƒHÿÎZ.E†]XfˆÛFø&wÔõâÐù”¦šlw D'#œßc1ó+¶žuµÂq®‰ÜBÏÑ„¿œ»afúÒö˜þZˆNÅ´_öá–äÇHÃo{;åÁ è!û1ì5Ò4µ-WU_Øf‚×ÖEA{ŽÎ£"z‡©^Á„GVL ¦Xð‰'U¤#3&ÞU¢‹úÎlT¾Eè‰m¢WºýwœŠ¤}ƒJnQðE n°gaŽQ¨ä^½è4¸} zÌïÒ´9ÞêXŠ{n¾*ü‚æñ$|tZª…„ºŸ7¥;¤j4º¸M…°3:Å›…SÍ'R3&~3s;å=$!«¶þž›3>=/T~WZbX =èÍjS¢XtÃeg ®àÆ•b)®0 $ùU¾á×-=tùµUd±Öiâ+Ö- ÑŠê‘Îo¬˜ õë\¿÷*Û1k=Ž,l aÈÖ²®4º€›wè¡ù\?ï›{Ûñ\a¤Ì²PöÇÔ[àÖ¦ÎÚ©9õéú^ŒX,€àÙù! ƒœ‚ÍAÉžwîÆþË@É™p£Ÿ¦‰œn`úl%„‡Â™þô—œúáîkþpn…ÓkæJú÷^3š ˜âýòHòcMÂÎÏP½š¦i¨ý*ŸcPA(…N½‚kbqèi•áå—qK 8T·"Œø´Ì±K±îd"f®FBÐèWΚۃ¨ÌW……”Õ©ÒÝC%†ACìAeLŠCò4aœ¡çt¬¯ NQ¸”|-Ê4”Êòç龇iwL©¢-ÃØ¦k5F¶®Ö‚i…“˜5ñ©C»Ú[4ÛÉPgwçÑÀV˜¦>£\˜Ú¨3ÕqÚÛI;užíÛ‚øbmFl•L?`T©ØÐÍ›bërEך´K^¹#KzE¿”yÙuD¯•d³Öi_ÑÛ˜êl ¬è#Â6 3û”Qt„š*;M4Í9è;4ÜHJ„–È«åò=­ÛAžõª-èÛæ÷]Õ\ÜCð'ƒ¦¶ö©²Ã¿0¥H¡°º\®9ûÞZ¢íª;gdº¿»S¿ºûHÃ_;Î÷…Fá¥ôlºÄºF’Á«Vá'÷éhÒ4œ{åô¢¬‹h„£†“HèÉõ§½UÎm~`1$PeåC…S\}²pÀÚÕèyž‡jk‘)Úa‚­ß™XÌ3#%8«Ãê²@鿉 9 zL³#Ÿst¯ùŸ,ùm;vò<€Êø ßöî.ôýçàp¶›QÙ™I­’‚ë#šx’ßÃWS·`¸ÿ%Û_º¶l•øwìºDé…ÂïQnOÂJÓ cút4òË;¹>ç4é==„!ùkUÀüA×·Ót×͑ڭ‰94xØù&nÛ ޵M/j¦ÞÎâ·[B"KE*×g!¸ŽÆz˜Ý˜8yK²%Òm¦(?›™9kCb£h;5vðÝŽ©—‘.[¤zÝ ¦ Ðn¹+áßÍå˜×<ícRç½ ËȲÖSÙîAP(Æ®®ÿµó¤¯Û û7Cð¹q˜ï’ž-º `Ý…¸yk›<¥ß£Úð¦Ú^Kj¢–—´dÓ0‡ ž$¨Œ±Ý…€û?ôæ'bÇáÅf3íËä¢×F3»?–.·ö²m.ôê’®¹ºi< û¦§©¬0*DÛÔ±’îI”5¾ËÖÌ“%÷nÆJs±Ãk×Ü‹q´SO”5¬µ1]p|Ñ„ÿIßh«£à ³5ja$çKåZã›ÚR6”y¸ýÒ¢yâ‡d>¢üAø~WwišVçN¸÷ÑÔEtôƒ¡Ä4¤ñltü’Bp¼“G€ÂQg½ÚàŸP³*%ï‚Pc×ùAÛØB:‰Š¢Ñ¸[lóg,ˆ~kúK¥×”X'ÐJí!˜ë["ÔŸ8È08¥iê4*´èòÃ'G.´Ÿê»'zË_Ð;—”AˆÞc>?¿i’µí{5Üü_ P¼Ñ0Í·h]ƒ'Y;­ò^´ízåWZ‡€8ÝÁä&4âwTn²öo:»OËÁèÖãh„îÀ‚î5Xóû „¡?4‘pNí]­Ü&o| $(8šÒ¬¾„ß©¦iÄl÷˜íû¬Ìô#.Ñ]®+Vr•®J–#O\-\ò*,ÿ—mŽ®µI{§ýÚÞ#m3Àà5ç‹5]DSJhpð9k„âS ?ü¹ß^»ùË1ò’Œh™Ga{ø¼‚ó/= ¯Â¡’GÜïìNû»6·+áß”G±À|¶ÚØ™(¸ €[Pž–„íÐÕèÚç7aDUÉ殮¥ms¾£{·Ëõ.]Q*id¨œ¢ÅÒZy:+K¦™ªxâüÂK ÁËTíÃo»à¼bËý.Œ\Ü‚=jï¢òô¹¨D?À´æùTfmóÕåÇÈÂo»Vý5ò4­Þ­PÑÔE±;itêZä‰W*ü”uô@¨—üÌjEƒ ¬bM€(çëùµjž°¹ªïò óM衉[iC,Â@Ó[YŸyLŸõÎ#þcáöþpƒv³ é{HÞþ ûݾI¥hº7ô|•ºˆ+ L1V·Ð·.üŽ…›tFNqb‘¬*ütô@~¾ÞÁ-´’ÓûöÜXY:Ò·¬òwZOËò®‰EØ"uƒ¦Pç,ù­‡hª éÝ{shøb~:eZ‹6¦Û(œ‘ž¾£_:LΰW—« ¿Ó­Hî#IÓHÜ g¦¡×EáâÄzL€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜ˆ†€ŒÆr¤v‡gLèo˜Æy0ß]IÕž6Ô.›cL€ 0&ÐP (!öH%7 ük\šë£™ã6Ô¸4–p×™àãó½Ø$/w·ê>¡DW)…)”ÜÁg<ÝÛXr<˜`L€ TF‚O3>ÑvPJhBŠõh Ÿêâéò¬Ïw{QeöX¿îÔ‰à“âÍ<Až…Dî&¥ü¿W´$í½ãÆí¨»¨°ËL€ 0&Àê'á&d˜)¥nÄï| ¬EH‡æú3>®Ÿ!n¼¡Š¹à“’á¥Lñ¸”êH¹÷ædy¿h¼ø8fL€ 0&ÀjF 5݆)Ä3°u¤Ôă¹™Þ©5sMGCÀåP»¥BzR ùáiInæè•¡fø™ 0&À˜@<Xøå§kN8ûÚ—¥YpFFŸ0èì] ¿øôÛxf²?㳚ÞRJ¾ÿ›ãO¿Ó[˜ÚdŘ`L€ „#¡G¦z³^CcyšÌ yÚ+¥Øëi±p’2ÃY4½%<ínc¡'TÙ &À˜h̬¶m&⸿YemicŽr½ˆ[LÚ½e-dÆšž\_jA½ˆ‚ 0&À˜@='@m&â{¨ µvB×óð6†àÅDð¡-ë\?ä…Ì!Kp˜`L` ¶“ÚPëø—ýéqœúµàC‡–žÓ#_‰S†m&À˜`Q€àó µ¥V›•Kl¹:Q >t"3NHçôTç¿gL€ 0&À* 6”ÚÒ²¯T4À:1#µàƒt§™ùp˜¥ ;Ę`qFÀjCÑ–"ÚÝã,êû=ºîh}¤ooÁ ú +&À˜`L –è“N°Jm*«:$µàƒs{胣üí­:L$vš 0&À?´§Ô–òG¼ë8©c1ÕUÇAdç™@Ý ÄîËÊê02ÝÏÃËu‡9¦.ÍȺ¦~SGÙ1&Àâ†@Ô#>qCŠ#Ú¨Üï{¢u¾^8ŸY¹;)ZQäRÒýÛp‚êB“³ge¦¿Ð˜"ìó)Íç“øÓ=~cm¨¥…gÝûùê ø|³T`Ë7Â7“ð¡iò©‰w± úѼ@ÖåìKÙòÅB/œ#„Z+…‹¾½ó ᔾù5=s!0K©=„ëslNÑuý Z{ãpçHC7¥J‡Þ‹Â%nGCþ“2ÕSX‹”å0‡[³ FåõJŸÐà…;Z# éZ¡a¶õFª§5©ù•Ô¦ÒsJNŽñû ·7 Ä öÀz§$Ý4ç¥<’5زSÍMhR“£”PAH¹¡ãbXºÿF©ŒOÿˆø}øyÇVº(ù!5#û§}“+ÓœE 4—¸fga‘¨îpš£ûÔtÿµˆç‡`ïB:‚Ù¡=4OÏãƒUCañ3ˆs<âç Þ¢ádÝ?®¹æ£º¸oÐIBF#ŽrŒF¼9Ì›ý=”wR½ÙæøÓÞ·Ý “¡jö¬,ov™Þ…H‚0q âé9ÜlÞ@IDATþô‘¶>ê=¦P™õ)‡BïwÛ \Kv7ë>Õw¿½Îè?C½~Â×ýÃ|Sžšé{h³Ãlµ·³2Ó>Ç.¨NÂT÷&ºÞyÆ7f½mI­ß2‚±nvÜt_úOeúÏÿ?…iNÂóÛle×½¤)F–^Eß¼nÐçÏÇH×T•'ÓB锉[ª½qpçe˜½Ëvo”ïÉ×óù,÷©{ž~:±xóî)àüî,Æåeo^Jõfþ÷^M§}6ùŽ 0x$€ú˜ˆ#Rádcd1VWaTe®C豬Íô§atD®R¼.Ô)^wêaJëSzV.Yn„#–¾Å;Íãþ}‡Ðc½ò¸åtŒ¨45Åg†˜ê£Q×ÓhŠCè±ÜŒœˆþ$PTç‰V"ÝžáWšbÄëÉÊììÙSr*âÑ ʳN3>ß`œ§Ò€MìIïä^s ̵ÂÔ_®Ó¬ÅF Z£T%[÷œç`Œ ½ÔÄjªQ<0·X1&ÀÊðˆg…¸"€†w„‘ΑD#8‡BÀùo%f1J£z…¾Sî–yN=4Æ» Œ-¹Lw¹ÓÍ]¹Û 4B$8ÍcJmm¹g<$%'ü¾kg™íú.šgø~„œ¦­ùÕ鄈ÖxÖŒíù$”× 9Í„ÞçúÆ®ÅKš©ÄÓ¸¾’ãÏøFn0-µÏ¨R4º%\žÄ nJM[¡LCºNLÿPÂ(«Ëã ³ÜÂ÷ŒÖ [a½Ð!të“— ¶¾*4c]*¶Ül÷ùʘ@Ã$À‚OÃL7u-  ¡ÿ jûÏž=ÛÉtî@X¯”€¾¬0"’óhJa®/µ‚C”è4Ãh` §Â(l ¤¤Å»òBRû––’ı²·A}€Épô{,f~%¨I7Ö<’z¹¥œ~5ÜÞiXûtF³fŽðM;JìËN&êõ]à ‹X/TÆTkQF˜MŒâPo•)ò!Е)H7³h S~ðEÙ!äêP=~fL ~ °à¿i—1ׄúÊPâÖO~þýVx¡JR-ÄL÷°f¤ê†9”aßE¡‰“N¡Ö Ô¤‰hãN›¶*PèqÈ+åo±Öi%„ŸÂ™þô—Ê¿©Ýír™1ñ®XÐwfCÁ0Õ¾˜ÂµP,W*ÝàÃŽr¾è–"©­"}¨kHt“Ef\v‘ž­0’Ô£mö£Ð”¶cEB¸µofúÒ_ð `L ½Ë0fX‹ 4æÁí_BsºR(ó±TïäÞUFLÉh¶Ï%aÂi.%câÑ`8;¼>uêÇâÛàÏ¡ÝdN·†A4e£y\ ‚úR®ÇVÁâg×[33ÇýزUâß± é¥~ˆ­Õ£Üž„•¦8Æ4ôé‘XÞÉ5ð¹:ÀÓ$/°b6šÇÜ.mvM]‚Ý_wAHyÕyæîyB c"â3Y&¨åF±<S>!L.ö©sŽî5ÿ“%¿mÇÖð ä< y<†µ»«™'Kî ÜŒƒtæbçÓX ™,“žv9 ÓL.ìÖòís%ò»N®>YyúoWC ;Ipæ™{ï-Ƴ۱ûëeðÜ’ Õë†HH0eà.DäJLSÝìó]SB>MËò®AÚü‚Ù8ØÙèrXb¨6ô¬ðº34´Ó-Õ›•‰Å<àn‰æ˜º“J7OTéš–›9ög§¾gL ~ ðˆOü¦}ÜÆV/ÇbL Î p{Zçˆ-xªkÿpf_˜`L€ 0z@€Ÿz&À˜`L`ÿà]]û‡3ûª% „ëF·rý^­A6À˜`µ&À‚O­Ñ±E&[³üioÇÖEv 0&ÀB ðTW(~fL€ 0&À-|mÒrĘ`L€ 0P,ø„ág&À˜`L Ñ`Á§Ñ&-GŒ 0&À˜%À‚O(~fL€ 0&À-|mÒrĘ`L€ 0P,ø„ág&À˜`L Ñ`Á§Ñ&-GŒ 0&À˜%Ð`0ž1¡¿aç!BÝ•Tñ™ùæ¡‘kìÏJˆ=RÉ B©µ.—ûÙã6ö8×eü8OÕ%]v»!Ö)B¬qi®¸Ni©Æa¬)%øø|/6É äÝ­ir´nꤔªyR³U‹$ÙÄ㉻ѫ¢@Àܹ»@í)(ÒÀßš‘µQש-Šš=óä“£‹P±ªŠç©ªèð»x#R§dQbæ“]º>ãóÝNu +&Ðà 4Á'Å›yî_ƆЖwé{Hu⑇ˆ£{u‘IM] >jKØ+(*?¯Ìß/ý£ã/+×O*lVxßmy‡¾4Åÿœ6ñc( cÎSa °V¼W§LFÝ;êα¾;ŸŸè£:…ë“xÏ% <þQ ýÏ8ë6b°ð‹y/ѵ.¨û•’ÿèÒ¾U³¡W Ò.ØOvmð¸ŒÜVX‚nâ1à¨Cäa=:Š?7nMÊ/ ÜpìÀÓóœÿùwe¹² ‚ó”ß2áꔽ…%7ô?cÐÞE_Î[ã\Ÿ„0‹ÅãþhOcΆîF}Ÿ’¥ ”xâ„#zhßz±«w·Ž y†ŸøŒ¹íb× Gô”.—gòm{GÃC~$äb)TÜ+ÎSqŸ@Mì«Sz ½¿+í±ûaŸë“š@d³õŠ@}|$†VÏÃHÏ4âê®+Δ ž¨¨êüº q"^ÄÍãI̾þî.€_$üPzdzðÃyª®2»Û¨ 8ë)µÉ·>˜v>"ïõI£Nóƹz+ø¤¤øšz<žç»vh%n¹ä´xn¬kÿˆ[çv­D‹V­fuÔI-á-üÔÚ͆l‘óTCN={} @uJ—ö©&M’f ru3„©Þ¶!õ‡¡~¨¯™V3ZË{ Óì|í¹'¹x¤§v™‡¸]þÉš©DÇ~g9®$âG £êkº×.¢‘Ùâ<'6Å*%@uÊuçä2•êÔå˜>÷À`¼Ö'•2âõŸ@}litÇåö¸FÕ««â5=Ñe"âG“›7—šâ—@|ñ‹§Q4ÎSHpVL ì:%±iÓ»á^<Ö'±ÀÈn@õQðÑnºw̉¦©:bËz<5Îu– Ê8¶ò·kO‚'ñ8êÃyªÎr;¨NQJu¸üÖÔˆ?úÄc&hÀq®o‚Õ3÷$&œG‡✞Œ¶þ8ÏÖí;œP5Á/žziœ§êOVä4vÒ¬UËsâ¬>i$)ßѨ—‚’¤Ȍà ã;ub{âØ¼i¢ÒÜînp’¦»âi{»%ø Μ§b”ŸØ&`Õ)¨£¥æêñTŸpâ7õMð¡ð¸„&;á3õ-l :¹[µL–.»="áñ‰Æœ§tÎåÀ×WTGK—ì„ðÙ#ÈñPŸÔ×äàpÕ€@}˨·&ds|{‹×÷Ô !«3J<]R£í§4ŒFU¼ÌËsžª.sTñ¾¸$ –ÿ¹± õãÕokþ…Å%aóëyâ‹E¿…}Çšµ'@uŠ&¬:%žê“Úc›õ†@}|Hб¦%”PµzÒ§ýG¼5/º”oÞ¾GämÞQoi×Þ1åŸz-ª0)eñµ+){gW­YG˜ýc9&yjÿõÀúRY¹YúÇñÌ¿?]ßo¤° ËzÉúþûÿ‰'þùa•åÑ´ÂøËª¼°a$Áç³–‡}ÇšÑÀg«cWâ¡>‰Û®7¨×_Ÿ"¾s@ã÷¾üQ´kÝBà ®Îæ¼-"çy"Á‹¤²Jrˆ*)úÕ'Á·®X׋±¯Ø¼}·x™·?_$†_}vØèº\šxbôõ1*#a½`ÍJ XuJ<Õ'•’à ‡@,ZÓXÅÖîÓ6ɘ>‹W¬Ÿ/\.úõî*>þæÑ$1A :ápqÆñ‡Yaþù÷õâãoë7owh-ðñS±‚Æ÷¿®‰ ñÍâ•8¬ëdq4ìw>ýîW±ko¡8¬{GËlÏ.íDu~” ñÆœïÄO+ÖÒÎ*qbßžâª!'Z=èÙ'~ümp»]â,Tòçž|T–4ò4ää#˜ˆ9 –Vx_²»’ ò†õƒƒÁ8Æ*OÕ„wc0Kù{öÇ ÄøáWM“‹ј«† |½XìØ/úÞC\6øxÑeë»_V‰w¾X,2G\aE½ ¨Xd=÷ޏé¢SÅ–{¬üûÀMç‹–Í“0³Ì*SôÜ<™ÖÛ—WÍ“›ˆ®²~nØ"¾^²Ò2@áùzñ qúq‡‹×.*ÛTžËyK\}ÎqìaÝÄÞ‚bñÒÛ_ˆ•ë7‹îÚŠö5:^Ð-!êÇåkE¦Æ’nLñð-ˆ¤¦ ˆkõe2èßP½DeŒê”`YÃ}c­O8Å úÖã· OL𖂆¹¿ýù«²îÞ©UYÎ ¯~øè½û®?h7Ѧes\ÉMÅq‡u·]zšèÙ¥­–Ö-’Åߟ ¨¢è†xïËŸ,ýªü ÿûl¡øn鸎W}‚èupËÞ‹~?¯Ì·]r†¸äŒãÄçþ€Æa·õÎùçôãúˆsN:RoŒ¥¹Í™®]Ùqmìñ¬“øQþÞ¶k/Z²ÒüG÷¯|ð•8é¨CÅeg/¾üq…%ü“çE%:Ìî †‚¶e—„ÿSé…= R¼>ç{±sO>Fq‰Súõ +ômÛ-~B§à£o~ ¬òs¸å.…gÙê âÕ¾ýè.pžŒ¥Oá*†PCjîwKÅ*t`.t¼èƒN •[ý€NÍÂekÄ÷](n½x ¾;.;ÝÆ"-“¶[| °ËX<Ô'ÁHóMÃ%PŸF|lŠ1/<— :Nѳ³8#4ß çH !és°åßšÛP)÷CNêkû/Ü.—hÝ2Y8O&ˆÔ®=VeúÎ?Z£0¶¥ÊüX€^pÿ#zŠ 1šäT¿¬\gõV;´i.è×±mK±hùqÞ)G;ÕÅ}üúgqß çZ•ä¼ïÅ”Ùw¥Fe¦ÃùAkwè£~¡SX.M³F•õ?þõ¯àß~Ö¨3îû9á¼kÌq ßý¢.¯“Ç.¬¢Ñø)ïÓÚ9§¢…ú¿­Ù(:·;ÈšÂ:îðnÖº7§™Ð{ZkwNŸÿÓŠr;»BÌÇô×V”“Ïî;;G7 ñò;óÅ Y½÷ú!bÓö]ØXPùbýÝùEbÝ_Û-B¦N @Z·Œ(8‡÷@™Ã´ñZŒüдö2Ç9DK±å=‹®iÇXŸnD²ãtøX&#ŠW½4Tº7žê“z™ ¨š¨O#>5 y”¦ Óï¹X¼ø¿/°U×…õ?íÅ ²Ý^gb$æÿ>üVŒzüq @ÀJZ$I;G’š$ˆË°hòÕ¾‰(ל{’xþÍÏÅS¯~l™§µÇôéfíDùç{_‰©Ð§^rS¸;쪳D»ƒZDä.b±&ðíÏ«°xUÐYß°¿ï#¹¡u;´c’¦…ßÀôMWÑT©÷¾ø ëuŠÄ5ØyE;¶háñs~°ÖÚQG#T} á‰~dö¨CKwnQ‰DѨ 8Ù/¼c­¢2÷+Î#"E£AEź˜úÊGV¹Ó0úJëê.=ó8.“‘Àe3L  I=*•âÍüŒÈõg ¢kŠÆ®i_k‹Ûòþ¯o¯î'Œ¾ùü(œ‹Ì*̦áüQZ·ãTÔs-DÏ0 ;¼lE£?Ôó¬*ÆŽZZyÓ::q6¹iÚU[gŠ[üëïËþõÔäûàÉ_ømÃâW³®=,4u@òTaSgÁ¤Ñ‰¡eªÎ<¬ÄᢒråÊúÃSg‹ë/8ÙZÓGŽÏ-·µ©Þ,û«LVì£MuÊÏËÿXòò“nB ã¡>©ó´‰a{ZçamÈÄíˆhwx$„8…2_[¡§Ônx¨qh†¢¬˜@c!@keêƒ ídÐÂgZDv`K=MÇ-Á¹@]Û· =n.“õ!õ8 L î„oëÎ?v™ 0&p@PÇåÎËÏß.Y…ow-M¬u>çžRù¶úPö” 0:%À‚OâeÇ™¨Oúb;;ýX1&¿Ê/l‰_s&À˜`L °à‰ÌQd ­¹9áel*(iÁå02&Ð@ °àÓ@ŽƒÍÚeEgZ±bL€ Ô%^ãS—tÙm&À* íîß`ñ’ß׊øØ)}Kë<Çã_V­ß/]SÒ;‰Çf~N§¥wÆî«å84¿°XÜpÁ)Üe &À˜@$xÄ'Jl† 0˜ø`þ1wÁR|Ø´—ø ',Ów³ qJº­.ûSœŒ/·µx¥xçóE–öŸ¶ŠYÿýLìÞ[(ŽÅ©ç¬˜`µ%À#>µ%Çö˜¨¥8EùÌýè!è{y$è8Õ…§ƒû¶±>3Ò•e/é„ç›q’:+&À˜@4xÄ'zl— 0Ø_(è«î¤Z&7-wx éÑù:¤š&z¬Ã­üéÞ©µ}ËW&À˜@­ °àSktl‘ 0Ú ÷.ƉÉôiˆE¿­±>lêtgá²ÕÖÞᥴ·`L –Xð‰%Mv‹ 0j ÐÇKwï-£Ÿø?±ø·µåÌÓç[è›xãž}CìÄg%†œÄ§*—ÄL€ DM€×øD`L &º´?HÜÓù‚>*ñQ<{O»ƒZˆÇï¿Îrê|-Ý…/§ÛêöËΰoùʘˆŠÀ¾š%*gØ2`L f*û@0¹âzjæ*›fL€ TM€Ÿªùð[&Àê 8 ì\ì\‡^±ÓL€ 0‹OuqF`Là€ ©®ËÀüg™ˆ?<âiÎ1fL€ 0&·Xð‰Û¤çˆ3&À˜ˆ?<Õ’æô¡DúÙÊyoëÕôJÃù¶¢{ç³­ÏׯM ÖùÊ™‡8OÕß¼ët§˜rÚ×ßôæ5 q/øØ“‰¯B›¦k7nËð!Äí»óÅ®üœ)¢ ±Oªyª–É<‰ nœR›$Z·L}é‚ShÛ ;¹Ñª9Ò†`ÃÎWkð©¥ä‰í»òÅNœ]S‚3j‚Ù)xSÃ5‚TAQåD§Ç㉟-Z.>ÿñw±Çé Ô¹ºl"Š´&®*FúÒ% Es‡p«"ñ>ÒØ GõŸÕÿ01¨ÿ"1Ñ‚"uÍÕOVƒáæÓï—‹OX&öÔQ~¢Ø7¤¶À¬Þøì9 ­j‡§ƒØ”ÔWlÃU—žêéÕ„[D›À&Ñ¡d½xû‹Ÿ p­ן{¢8ª×ÁÂåÒ,¨β•z@€òÕ/+׋}ð­u2ñþÈOíúœ§JG>LñNh~ãÓEbçî½Bi.±Ck#v5é"JйХ[(oR™"ƒD³H4-Þ+v¬Üd}Úâõ¹?ˆ!úbÛûÑ‚ÎþqNõÔƒdv ¸>©©Áa`Õˆ+ÁÇî•éº)‡1oárñþ×KE«¹XÑìx±ËݶzbQš jSBWë×Rß*úþ,rßüB\rz?qö€#…ÛíâÑŸ(ïoëv¾šóÝRñÖ¼E¾•Ÿî—üDq­yÊfB#ª|µD|ðõÏàÒR¬N>Iìpw¦cÝ[eé%1)ØBß.:—¬¶Üø £E#¯>[4Áieø·?ÿk6n[wîÅ”Y‰ðx\"¹I‚8¸Ckѳk{ëZ—B’G®O*KAÖgõ“@Ü>v%èX·S‚©ˆ_ÅGß.[=]IJ¤ãPÇfJ«&ÉL‚ÖÂfgˆ# ~ÄèÏbkíÇÙ'öEÎS_5áx ÍÚùj΂¥âÍy‹h~"*OZGß×*P¢°^®yr¢ø3o‹%ô°ÿÊ™‘o$U˜sÞåÆÈ~ÛÜ…X¿PÌþxÐMCü´b=¦©áÌè®&¢D& ·Ð…Ç,šù»•%è§s¨|–J¿ø«¼b§;×'±"Êî0ýG nr§Jª¨¸X,Y±V|´`¹ÕH-M>aÿÑã \†#ó…xçË%¢]«f¢_ïn–ðãría,LY« ”¯~þ}xë³ëE~¢ îÓÓ¿>}J0‚ÏÌ JYò—]6°3;»³9ÇÙÙÉ»ïÿ|u§zzf'tšžÛÝUó»soßP÷ÔW§Î=uΩ* [vÂìÎÚê^,””A ôÒQVœjƒ{iUÃNÒÙ5vÈ>‘޹ÇP›µúë‘+ê¥r¶¦¶wî¥&®——V7ÒçœL§Ÿ4#c®d%O­.uQ!`j Bñ‚9–ž6Ž1xòõzòZJ„¥Ç,µ«Ó’ÎzôÅu4m\~†™#¿T2'à+¿?D{nµpo¡Í”†ƒ§¤¥ãHsý˽uïa"ŽÙ9fEαä³³"bð¬;âå˜/»¶jNPNRÁiŸs:µÛ*é€s*…´-8~‹‡;&Š­$ÒB3ü鯤FVÎ>~áélýáx¢\mѨäÉ@Ȩó Ü@ ïjÄ ¦Ççó‹Þi—/À1=KEÏØ,Õ„^úV÷òt¾I+8öèüÓæÅb}ÒÒf)_¾Ñ!ùêå5›D óÖâ÷˜ŠŸ€w¦y |´¥ÕwÐC¬ôu ídž=ì˜ ‚“O¨ã h±×¶dR‡µ‚ÖEý[‰6m¦¦–Nºùš×W*íJÖ»’'ÉÔ‚ºW!`.òÞœ`ôÎŒ¸ž.ž—ç­XxŽÎZài2Õø ÐöfýNa óð_Я’ù|…Qyfå' – ž2>öá*~§}xðÙ•Ô¬UЪ’s…õ#²ÌžöºfRƒçÚÍó*ýö±\ŽâNQ²IÖ;¬ÇJž$‹žº_!`òZñ‰õÎxغŸ'JÛÅ–~ž(íˆ}¼9Ðï‡ Ðæe‹‚BƒL7zØ©è~²V§2„€ä+ðS—×oj~B‘Óå)ãc¡íìÖzìåµbTV]ÑéPìÊ¢ÙÉæ˜},[U²Ëë­XÓ ÜßÉ´-Yïh—Jžd§ÎÔ[Ã@(>詆„›k¨09!æé1k´1ð°”b©$Œ^Ë—dé®;LjŠr&bõ‘õ®äI¾p*G¡"·Šì¡A¾ ?>,'9TËl¯tƒ~Y–"?oI•u!¾›ÝÏ\)l2<%Ýy°”ŠX&v¿bYˆ|IÅÑ6*ó8Å(4¨×ÁR|½+y2RêšBÀüä­âècŠ•ˆD„›©ª þX %”MEw#ëdðO¥ H§P>êaX|:¼^E=”ž²p3¹#4¥¶Ì°¨Š66t}õÔ»’'C£¥îP˜¼V|{LX Ñ£3[á£*i7mŠ£nr ‡Dy |‡ŽFtÁij+EO|Ø·™q±ê!šã[ON‡¦©à6–\]Ê6‰}.¥Dë>—ʤhU¤Š@Þ+>©£žSŒ;T‘KRñVŸOaÕÈ °4ü{”_7,ÉõÓ¢Î7Èí¢S§V’Ýfåea´´ÖíB9S›’ÌíÊW!@yÛ¼†£GvïÅÅtëéžÛŒ*²Ðß>\B¯__;÷ѹzâêRÑKŽÌÀÁp”'d\™®ÉSžs¶‡Ö}¾‚Ö|¾œ¼²„NmÌ—3<…rbƒå§²ÄÉŸ0•„[3ZßnÖ|®œæÕØèâzþÚRZËeû³åô«‹ŠH*€¼´˜n:Åö»§ø7QQ¤JVr;dŠ…°‚üPk–fºÞQ˜Kf:iù'Ëøý½‹SÿÅ š\n¡jEðÄôJkï›Òü5åI“$õ¸B kä­â¥ð6öéaº¸ÖF§·ÓŸßõ‰ŒðQúÇGKX0õ–Z7©Ô©Ñ¥³^=:QJâéOôußð# ë%Ý7Å󔇟ñ¥úÂ3tуmÄKIÑWO3>ö#ÅS('RM™[XFÆv¤[ä^Ïa‰›Ö SÃÑ0µúuúúùç6úìÓôž úØ\#®è¾wütý"'AQJ'ítÍ£ýÎiÔÔèÅhÛãdµÂêcIÈê#ëÝØ§C ‘µ›OsÑÿ®ñQü4]óY üðìÙqÌ¥löӭݼê[ÛQZµ/DM!Æ:± S}—zN!`vr{ Ö,¢ûù%.z¼1@->ãc0³ÚÊJÌÿ$„²$%Ì2å ÂýOn ÊÓIï· Óeµ“•­Jg˜,vžx‘têiÑøesfÞùÉçSÏ#wŸ|“I³è÷÷¶¤W¶€DC~Šºh1ƒ¶p¬Hª©7Oétã¿:cY=¿#Hß8ÓP|2ÅSÈœ'*¦ï¯ðRs®ÕÁñ;–K‚“¥ÁwÎvÓ¸b^Ù…M P¢ºFÞ&½ËB£Ü¨&XKGé¯uWƒk8è³Oe~‹?Ê25{‰š¼*qŠ”£ÍQúÄ'ýzµ_Þ–ôÚ;ÜóiŸkÍîZK/®ÛNï."½|‡;£Â"6»Ê–²âÓ—2_HyŠTÒ‰û €M«´Ðl¦<:Gì-lÉN§ãÕ—õ[!0rdF’ŒýYy³Ó¦Ñèbv·&f"ÞÍ }Õ‰eÚ~¼G°g…Xõ’œ@`0žúÇ»œ;ÕFŸøg(Sjöõ´·½Ü¦–Ëœ¸ÂLÖ{œ³i®÷úÚ‚6:yÖd²Ûm'¸¼à~üÝ%ÅbØ»Ïg£öšéÈa7½¸ÁB{zë •7MáòÇË“Y¬\||¾‹.¸*ûqéíaÙ3±Ç[D<ÖÒ±6:-ƒuÑ/ê¤B`Èœ$F"G:ë1¬ô âžW"éh'fà!‚ HUñ™Å=­ïœ[JU•eü‘*ð#•=¸çž–Ó†MÛ6?ø‹ŸÞÌ?1o3oìü!˜±òU;ƒI>ƒÒëïøö“s§OZzëµòÏÔF:ù|~þÚèOÙhSŠñ¾ñÔ’16úï÷{è›/z©þH8Fh&x*–YpsÁÚãt:ïyÜn*.òÓä0ÏäÜÌëw1Ö4†¸£mômO÷¾ã£ß­õÓìQVúí‹iR¹•þkE— ÷Öò3™LÇ»g¢Þä8ÍŸÆî.Ö ¡ðe#¡üëØ…‡„àæ[Dÿ`W:‚ÜaùBšÀû£]:yÙ:ƒòM£üÀî«<_UY"äIPsÐa¯…°+og„¶ë´¿="äÜ{ý%Ðp¨3H/î4”!§¶„\U'M¹eñÕ¡–ý/ì¿ÿ–5ý=§Î)̆€R|¨¤ÑE ‡¡Àh/¨JG»ú Cç îÈwúã)ôúÃü»Vúè_ÛzzÚÀ"›»¨ÇuKÓMÿw…VoïﱎÉ|RÝÃTYd¼“[½²éb+\#»áÙeßȱU›Ù͸÷}ÝöÝFç˜2ûØ1ß´õÍ™wnhbÁ÷²¦[þé¤QÏÔÝQkh­½rW?#@dyZLKz\Pb¦pÐ-‡J“ØŒ´»M)>CaU¨×ûòÔDæ™?ò‡îíýaÚx$B‹x!Ò®ÖˆˆïÈ6Oõµø¹iݶCÔæ‹ÐVÏÉtÈ1)­ªƒ›ü"¶pàC{9dªãr£Ä#&Ïlg,züH(ÿî–Ì&…Å*!+x!^ŒÕÉCÜáæƒâ7Üi—AÄH(ó²?´Å^9³ÊB\QBW=ÖA›Yù@‚kðáF\aìÆa:@ÀòˆM&Ä"íbüa¥Zu L«yȳÞIŦûc~í¨oæõËyª€Gõ Û¿¶Ü0»£÷½ê—B`äèáì‘£!'Þ Ÿ7‚üIó¹çÁg®‡–È{Õ=¹…@Þ€N›yø÷!Çä´•ÔÒŽ–(Áº3Û >¢‹ù#{;dpµ<Ã/ c—iþ(½±¿·L^Ku_1ü”%‡Xse+5²åâén1Y#”Švn.S'·GêàsÑ7†òof4R “Jb.!lWÍ3†²}÷ÿ^¥·Fì±On°ºË–²ÊX£O‡‹Y¿"ªëWÐñÖuϱOï±r»ë©U_‘€Ý<–“:Pd¥ø$é½<߯ý—–ÒoÖø{õt“€M&;KˆOì¢;ßâ1¹*)A ž§~·Žùˆ·þÒHñ”ÙhÃŽƒÆ ½{n$&}úêô¹Å.V|:黯x黜 b\âž‘ñ©H;…G}áß™µxT‡‰!ûcªŒàål®ç÷¯­Aú"r_1ÇImê].¡ÇM»ŒW³5Å¡ìIWN‚”[|4®sgóK?»îvÇÓ<úËwŒštO†ð¶ú,ìÉFw±nwÏpYkÀçyW݃VÝö«ÍwÌÝØs:RdÌF f©Ž‡ÿk[€>2§gr±þˆ¸ˆ{ëÛ[¢ÇÚßuuN! Èž‚2€•Ú÷££öqÒz¬Q²©îÃÌ!ƒØ&™ú*=8õ|'ýŠŸåZòÞtöX³kLp/«ô»l'Xr~ò¦®dy2˜c­’'H=k’;Rý+ÅÙ¦{ ÷y½^ÚõÓK6m¹cá¶Ü±à$«Ã6“Ý\ßâ­† »Ã>¡pý¬»ê^b%èò>¢÷0@üÍêX!0L(‹OÀ¢W:TzŠ{rØTR$‚€Yy ±.Px"<ãaG——‚ÓééYš%‘² u”œ‹ÿÚÛ2Ðý·>—ùÙ í<*-DsÇVX·ä6™> elƒ% ù?ÿÜó m¾yî6.×°Íÿeý´P@g+v5»¿N–åe;—Ï­Û[¿{Öõ÷9Šô?Ôiaš3ŒÊÜÕ^!00Ùïê LKƯHAf쉭¼æÙ“iD/4ž~³Ó\HôÉzÁ"—V2??¡n’å)(>º ‡(èþ8çIÈZYøÏC´Æ”9©ª¬ˆ¬ÝñLˆk,ÉzWòd0”N¼¶ñ« v°%è'[ïX°H³YÎdüf+W¬á0«MfרOƒ]´æu¿›}Ïæ'æ¢Î(2‡Àà-=sïÉzNN}““• ¬Ðlö]¼b|ê¯<ñ×Õqvˆ¯‡"—ƒg1*’K²<S|Bü}âùzä\Ú:“ÅJÐÚø'™ÿ.†Â ¬ý|Þ= =£ÅâoRÇ È{Å=5¹Uq`&w+ªBGR„kø´1µE1º‡ÿ­ê É"žšT[az~B¹Rá)v?pSAœOTÄùLUD¼d)ÍêzW”9Y¼Fú~Äó,ì\Ieáf’í¤qÕ¥bRFÌHÙ©±\…S(‘¤,Á^É“!ÀJàò³_Ø|Ûü¿°´Ôb£3×GXð1÷1›é¬£êúÍáHdûÌ»6Þ²ä·zæ"ë MÝ’¿äµâR<´fìR·ýù¼î÷\ÍšD¯šcGªJÝLuÒfVz ‘.ÉW5åÅ<éÝÔü„úI‡§PV¤b—®ä˜˜ðašá¯cÎß&qÍìÿ ì,í\Aå‘c<,ž‡Æ.¥’â"*æ ë•ÁÕ…¥*ŽñQòdXª¼ñ–…+Ù ö1 é§°ä{U¾„9­‚ôè=7;«þJy^í©"·Šü8Anc 6ìkyêû ¶ø ÀÑl 4¶É•®½ _–Ålô"=².$_Í_aZ~Bý¤ÊS(GÊ+…+h\…›Fóðê±]´°k%!^ÆÌ©4ÜBs»Þ¡“;ß bÝK3y¦äÉ£Xéáµïб®.;wˆº Tž¾õ®äÉ@H¥¾ñö…kyHü9šÕz9³àÖXŽº>'Dü@¿6ëžú¥±óê@!$y«øôàГ“+Mc-ž±%ìÏg«Ï o=YÒXd1Iœ‡¼´€&ŒæšZS*VÆ–´Õ2suCFˆç«¹“F‘“Ý$3|æâ'8žÂ‡å´³R€¥œ ƒx˜INëŽPy¸‰Nm&ø·%=²Í®i¦÷]ZÒñ*»ÏÒŸ¸„g`>½í9ZÔñÍcEgaç[tfÛ¿iQçklí:DÕ®(-¨±ÓتR*+ãE:KK„ÅÇÃë¹x-2¹F×PL_ïBTòd(ÈÒº¾åÖyO—,˜ÏÖŸ›yâÊ2ÅârÕì»6þøœWxÁ0•I"·Šì¡JM˜³Äèà áñEQ*жÓïº$á¾ÛA hšYÍ#…Ø­ Gš€~Y–á{»Ê9Qd]H¾r³2°xrEÀO½æjK4Ëa»/žB9¡ Í¸x‘Ò"†…ûZ>µ(D%– Mõo¢Ó[—ÓLߪdk%†Í÷—àƒ‚2Äeí/òä{x”X+Õ÷õw{âçØ2Å·‰d>*zŒjÃhtôUð¬Âµ.^f¦R§©˜]QVJååP|x+5¬>psA±ÊÚbúÖ;Ú§’'‰WSªw®½Q ±õç—n[ñt®„»Yêž$M·Dõè7®Ýøæ‚ŸÕMM5õ\a"`6m¼ÁméV ‚m¬8@@!€Qô\¹‡Wáññˆl¦=ÄÍžÅÅJÍ#Ð+Ǫ:tÆ—XhtyQ7†`ý‰]&Az7Æ"HÇùž2ÊS«/_MUFÇÚxrË£˜ŸxM«ä'A_xʰnkuAA(f…£»"¼¨'F|ApÛ¼hiZC²wÓ˜ÀnÒÙEÐ<ä·x(¢Ù¸%GY) Å£ª8<†Š(D¥Öºhšw£¸÷¸­&i>tG»ØJZGl}ª±©Ì¡ %ÆPJ€eXt ¬•SIi1+=lía7¬=°Ü ¬‰¤¾õ®ä £f{ÅË”D Lúžwo™‚…Õng%ç¾`D»y—Æà¤ë§"ÚzžúK ýWqNýS €™ŸØG8¢G¼þ/‰.Ää%ä²4M‹^+ @ pqŸÏE5žXáÎ+‹;;h›{!µÙªÉ-ó—÷,=µ&p èØ BTŽ[Û»ôP P°ÓÁgЧ€ý@|5½¶”–VÚÙÒ™5~=ÃÅSøÐÃ"âÄ·¥Èøžq“Š)NVν^ù¹Ma†g7Ovh¬íåo[wº“¡0óäHë%¨p8ÂñQ!:æcFèÕðuØ*Øjäb‹‘UXb-œ—+ê%7+SpiÁV‰ÕÂ˪)â‰Ýž˜•mF¸ ìl1)>n*båÇãA‡Â%¬¿ÒÒƒºL4 Tï…(O€™)ÁàñDñËÔ}\`Æ{æülÓŠh$ü³ÒL#oýº/z+E×l¹uÁšL½O哘QñÁú`‡/ yY z8†"Õd* 9D¯Õ)b XwBBšÈGî/€Èæz +"PF`Ž‘“ßÌ@„¥20{.&P„àÆ”!å¶ r[Xá+¡H˜à‹0Ì6‰Ñ&‰`§ÏÛ…¡lrKäñ|¸'c<0㫱ÌWE¶Úß KùÉ;<ü:²ÁSRùauA”ÛømÄþ àóàøü~£3ÁŠ[ż?Pl€•p±XŠ`= ²¥(À›ËÁí€÷X»«‹çuEšØf„Yƒx`3ÿA5±kì2ãç ¥^'¸Ôec†-¤ NÌŠ#âøºão¸ÝC9CP¶tu në¬Ô¶dÒ`õ^Hò˜I™ôùóÏ‘'›o™»nᇴ¦_1¿]/ê’G~1ã¼ÅÃÞÿ³ñöw‹sêŸB ™ùª÷É4Ÿ¢Ù¿÷Òªª›ë· eóÓ‹[“‚þxXRDœ¯Ad(=Ò|ͳïÙ„ÎV[[|Q^‰ã`®×´f2ä«. ø6ÞÅvã#4Ñ3…eŠŽmRÊñ Óˆ?0zÉi¾ž€# mß¶-<û-b‚Êø:¥ûs?ŸqžBq‡â+|àk|~êðG¨“?æÁ(âcºðx¾[/ÀaJ)[èP„¸ …#†òƒòAùÒ…Ãá…•…‘¢Üýlyõ1FØpìâç+ø<ÖÓ¹£€<¤n¼-ÎG¸±Xéáö,­¸peAñÁotr ÜÀ%ï•®/iåA^©¦¡ê×­Öü–'ÀNÊ”ý;·¿Ã?EëÞãrÖRݵXÁö33ﮎ¢úoù¸Œù†%¬~Ïú<µñ¶ù_f}*)b˜Iñ‰5žÿúgýô…'7¿Ó°³’Ÿ´4X/%ÏÑ-TòB 1ìð øY) “ƒ{¬%Ü…b${­1´Ò8xŸœÌ0»Ã½…`K ±-/+ÇB€3½‰Œ6I„$ÆQçM{ý;+·óý?Œ-†w"yäè=±2f’§€E¢|åpø©´ÛÒãÊŒÎOe5”ãÃŽPé(@RТç+„7÷Z!œñNkŒ–{ –(@=ÖžÌ5¿Û÷k{·o{ŽADãÇ&•Ÿž@Œ´6õÃÃÂS(ñHñÕHñ”T¤ë –”0ÇÙ„ÜPx"ÝJÚz`®áZ¥ÕÏcÃ5Xu è@q’VŸ [}‚¬øH·‘ÌÏ@yåHXtXé@Û»ºbJÓÅÈx¡äàXâ%ÊÀ?ä)¬^ýÄ>å»<|R¦ܳëIþiyÒx˜ݬäœ}híÆû™w>Z±çߥçüy×U+®ŸbþªA´JÃŽ€™Ù;ÇG9üÚ“ÿ|èÊoºæ¡åoW|ãúK,8÷ÓHVP8 aÜ=9÷«àï6·Cñ‘½ÖLY}äû ¼¥ù£¶0z Š\]ø`ƒ t‚ÞtS0!Æ/ ‡Ûßü÷SÏs~˜›Vót_eÖçeù†…§Pè‘â«‘â)¼ Ê Fúí @IDAT~¶³SÁuEí%Òm)•J î´ ÈPBp ÷†ÜaޝsQÀñ>¬ô ÞGtÝq;Ü~¥{K*Gxßp§‘ªw”KàÙ6å Þ-e çj{ýé'žÀ)ÞL#O`Ùaþº–-=m¼ÿhæý%‡š;ŸõÇÆK·Ü0»çT*l̤ø &Ð;G# ?~´cË»kª->åÇyú ý³W¾7-i&…” )ˆ Ë‹«€Ãdϱ=|î#|»;° 1á$å¯|7¦¹‡ð–n(?²· …çežI7·ƒM­´îÍñz;0Ä“#«Å&…U¡X|†…§P?²n³ÅW’-ä{³ÍS(3Þo+‹ \løÀk®ãX&Ü º=†²÷Ù¹Ai‹*r‰vßéˆ^*Zx'°ün A‡¡Ég†{/ñÏV½£þ,ý“ôë.9SKÇòa!)…–!DÉ ÑÓ "8SŒJA€%Çøà…t¼O–?ð.±ñØÙ#…ÐÆ[M(;2ø×!àÓMè•A@­Ù¼KÛ±©îñõo¼Š)…1‡6˜{/p.ÅgØxŠ1õ›M¾ žB9J’\çæ•P²ðœ>àwaʼnØ)Âíî/ÄáIklwÈíˆõémqÂùø÷'ôò Ý„÷f³ÞA¶,+K•¬É¼7^¦ìÙºù¡Õ¯¼°ŠO›ZžðŒÏßž}w}+»^ïD˜ŸNÑýá×fýªñ¼-_™}çT*L̦ø@SÇGŠÇÁˆFÕõï¿ýßC^}-è¼îPs‹~õ§Y2ócÓîiù¹×)˜êYø£JX+àßHR‹IþÃ{ mÌa‰àK(@â¸[á‘÷&ùŠ^·Ãÿ÷,=;êž|ù‰G—ó ù€ÞöVÀ8÷tËùGž¦¬ð°“akÆpð•ä“lñÔpðÊ€ e°Zѹ°u+•¼å3˜SÃK1ÒÌÑp¨cÍk¯<´aåë¾ áׯ[¼Å§Ÿaç)ÆV|˜†›¯²ÁS(K¶’lh¹šP†á®w`“­ºïO¦¬óµû×½±–žœ’'[oŸÿ‡Yw×µñl˜²Ší`£â”/òøE¿Üvγ_÷¿J†€ÙÀ3 b2À°PØy³¾óÊ olZ½ªñŒ‹>x ÇßœÇø2z‰Û©——i.;OŒ“ÔWÉéû;•WHa%Ÿíû[žOve=0Ã5&'d:µH$ܱ»qÓk+Ÿû÷+>_—Nmœ/¶vÞ€'p¾…àæâbŠ4¢< úòQßß™‰ÿïËC}'ž“ºs8è[Ï}§òî¾uÝ÷w*yÊgú“){¶6¾¼rù3Ï{½­|_NÊ“-·-|tÎÝu]lÄ’{{F§íøÇåù”,»ÚfT|¤k=tt5 ”š®®öè =ôÿ~föÉKfNœ1k¾ÛS\íp»Êl6»“¿.Q~8ÿœH<J àóµù:;³pjÜZ¿~77¬:Pr ü@PA`án.àZ(n..ªHЧ$j¯–)~–)í¾®Îæ½[·lÚR·n'ßžòdóm ÿÍ3:ßÊÊç/ ôë8¨î°A Q—ò3*>€9¾‡.•™øW ñݵxkä{…Eˆ÷P’¤­\>çò6$`…MÆFÁ¢Gº¶`éÒS¨Ö.ºHЧ$j¯8‚'¼ŒÅ¯fÞY·€‹ÿ9@À ŸÌ¼«¡aëíóž=u&_0«â¼ñ¡‚õ RþƇ=7o.Þ ø ° Añ)¥‡‹)’ÄJÜWÒÚÅŠ”¬žŒ ¸á:p,Ô$yå—ØÅS…ʪÜñÈ6‘×ò¤¤tÁMíõ³¹àgÏ‘‡fÞUwÚÖÛ¢#­R `fÅG6B|˜âñ‘‡JV0å% ûU|òYH)¨àÆ’q(?Pñçe\|–O\’x)ž*¸ªW)dûÈky²öF-´è—Û>ì úßa·×$Æ¥LÓµ§ÜW·¬þK [ÁI]Ê̬øb4D4Âø){è°ôÄ+=2 O>+=\<‘€‰Ü€Q¼°FPv°Ç†k…léáâÇ’â©ê@!C@ÊÙ>òZž¬ÿꌦY?¯¿”Wðz‹Gyñh¯¯öðGÑ/zô* eW)0»â#¡ÇG {X.ðQ—î-(o+®”¸È ØÈMžÃ=*õ€â©þqQg ´$)7°—²{y÷äMâ`çμ»þû\ºï‰Béúwfß³ùÉÆ[çÔçM!UAN@ —I¼l€hŒHRé1~†›K–XÈ$q‘¿Õ>q$vЧÇLÝ™¤<9iÂünسñrÖíNƇ ÝÏ«¼/§ùWŪD@ Ÿ¾5'?Z}Ï«ß TP<•*rê9…@Ž!€˜žy÷4ÜŠDW²òëÈÑâCëêáòúAŽE‘› 2&&ÁÛÕm …€B@! È/n·Ú¢é¿‰•*ª}{Î/6͈ýVy…€R|òª:Ua …€B ŠJоÅ˳pyEB‘{RÉG=c~”âcþ:R* …À0#°öÆim¬ñ|-ö]ÿÐì{êÏýVyƒ€R|ò¦*UA …€B ¶Ü±à-³\æÐÏ9Ð9bae‘ÔžPŠb…€B@! Pt#à°ÑMwPŠOîÖ¢\! P(†¢’ªß°¦Ó‚Wð¤†3æÜÝ æõF¼³•µR|²…´zB@! PäkoëÕ4º_­SôKòXís¥øänÝ)Ê …€B`˜°Z¬÷q¬X¹ž=]šsצIÃüJ•ý0# ŸaXe¯P(¹‹/`ºÝ]Ï%ÀêíÑ/äniå@@)>Š …€B@!0šå>y9JÑk¿§ëêÛ)ÉÁ½ª¼¬4E²B@! Pd…æ>ÃËXoä ÿþ³ú³²÷võ¦L# ŸL#ªòS( ¼BàÑ«´,•…Ò#Ú5òXís¥øä^)Š …€B ËèVËC±Wjôaµj{ œ;PŠOÎU™"X! P(²@ã׿½¥‘¶ïå‰ «®Ýt^¶iPïË JñÉ Ž*…€B@! ÈcxÑRã|–EÔôèeòXís ¥øäV})j …€B`„ÐÉòTìÕ];V9…€R|rªº± …€B@!0RŒ]2÷m~wÞÏî®I3瀞=R´¨÷¦Ž€R|RÇN=©P(„ÀŠ÷iažÅù%YdM³(«#‡ö¶¢u R±ZnüйñÇ=“/çÅ4êÝ…Áqüï|)ãH”£yj$ðVï4ñòCÉ“êÄ¢iË£º~¥¸¬ëP|~>À­ê´IÈEÅG|”nøú÷–jíVy&‘EÃag%&ÅxØÈŠ’ÞIQý«;{BÀ ùù×òˢݛ\‰#¯x*q¬ÔyŒ€®QdŠÕ÷F#‘çþ|פLQò¤»Þm¤/vóŠígcX»°å1_ä[ÑrIñÑ>ÿùï¹#•ÚWlvë×¢Q½Qö%W´¼Ô£¹ìö‚sÛùC¡hk»Wïðú-›õû7~燇ý>ÿ¯÷¬ÙxïŠOx™Y#¼AŠïÉå§SÅSé §žÍ;ú“)¡`øÇ6ï½÷©§þ™Rðòdãm öͺ«n·®Ód–¬îC6Îe\êòŽò¸@¹¢øX>óß½Àâ°ÿAFÇÎ:N?eÞTZ0}œæq9­y\?CM({^€ê· wvŽÞ¸}ÿg¹ô¦1ó§}é¡_ß…õB¼Ih¨ü éºâ©BªmUÖDèO¦üxÌ©_½vÚÜøÀÏ~²œ3RòDר¦O †ô%¼WŠ#7þ™]ñ ÂòÙo~ÿkDöÓ1£Êô¿ŒfL¬Åy•º`å–ÍŸŠMÛ¶÷0ýý¹U5ìƒþǵ·~ã»Üó?¿äÛ`™ óVð½5Æ@ñƒ ’B`0ú“)tý‰OýÛß¼ÿ§?DLKAË"kÙŒþa¡¦-åýŸÃS]3fv‰Ô ÿù½[Xé¹kéœ)Ú×?õ!++=æBÐdÔŸÿøô‡¬ÀËå.úá'n¾ã&ÑÅ›7Ôw!+Ч˜TR$ƒ@¼L±Ûÿóé;¾™RØòÄJˆ}‰ã|`ñQ)‡0³âcùÔíß¼Ðb±þ„?âúg¯|¯æ°²W+q®NÀ ¸yŠKÿëòëo¼˜ŸŽW~Ï,¿îT<•_õ©J“%âeŠÝáúï«oºå"~µT~²D…‰^ã°ÅÒµ“Ôº]&ª›H1«âc¹è¢O¹\žß]¡_wÉ™…l¥H û¿¸UN5cÇÿlâÄ™•|—“7¸7ÍZïý$3gOeG•K# eJYEå} žQÊP¤ Éæt¹¾2úx]Åô¤ÇÀ8–UU~†s*â­­>ЧÒc#õ´B †€”)E%Å_à“…(OÜ›Šsw‘Šó‰qˆù̦øˆžùG?÷•Sy”Ñ…ÔKS<5lœ¥2.T Sx›š‹>vdJ!É“ž*WÎ=X䨑)OIÉy˜œçéÉ18ÍI.pž£Æ?‡)„» A‰…âîŠâ)®q•B@Ê”òêêp–…$ObZ–ØÜ=iÓbÔé0›âz¬d±LŒ̘KB¥ôŽ%n§nw8¡IÆ[|ÌVÿéöÄOˆ‰:£H )S,VÛDÎHZ| AžÄpÛø¥¹GXá«W°‡¢úô{ö¡S©R `6F=6^ƒk /Ca6ÚF´:ALÀœz*/+Ò¬v[ çm‚ªPFc(žJm|׸ûP9dçÑ-{“/ WPêýÎM;Ðkë¶ô>©~¥d /•3š3*$yà VtÒôòD«µc¼° KÃ$—€§ÕbõðSRHIWpÎ×µ¼2ÂSÉ!›wÔnv¤?>ñ*ýüö“Ý–qZš[;°,Œçvø± – Ø#‘(ýêï/Ч.9“Ûþ”*ŠÏ¦‡èìųN¸¦N¤ŽdŠE³s`ŒB''€ÅqNûù¤`ºhPŸÀÇÛN¸I0Ù‘d‰½s+†ñÁ±ôÌëëiTe适6›„íØw”.>ó$š7m½]·e /M1…JŠR±ªòÚË=B ‚ª¬j¦à©lòL&ßµxö$š[ö”Iû{NšAï;e.=ÞÎ<¿–žzu}ñ£ï——{í­V Ýsë5ä°›Mœõ"3O™YR(ò¤W=²@Ý'{‹®,>½Ð1ï3I |”ÅÆþRìÓN¶î£W×6ÒÂãéù•Éåt°Ådv¬çW¿m?=ÿöFÚô8M]I½g!í:ÐDïlÚEN‡VnØNW_p-àçWp>/¯ÞDm>š5©VÜ;eÜ(êÁP„{q5½»u/ŒEô#8EX”y~5­ß²‡l6+ËBþüÓæŸPæk?ôžØ¹³ÏæÀzÚyà4‹ä÷N€«R1¼ùœl»Éehþ»ceÌO™¿È™¥üýÈó«èÿ}ñJ½ÓèÛlùÈN¥gßÚ ¬KfO¦ËÞ·˜ÜܶVoÜÁÖÉ ôƒ/])ˆÀâ¹ÿý‡§é“<ƒšZ:èÅU tÛ'/¤²­X³Y´)üîO‰/)rO^*¶Ý›è-Vú‘@Ï[¶ÒY‹fÓÃLÚ6ÚÓ÷û}ô¼SéäY©Ó ûŸz¶ï?J“ÆTSME‰xÿ‚¡°P¢Ö7î%/»ÆÝ¥’°øœx:c:ÌÖã—'#@C!6q ·ëw a=iL•–þ@ˆ¢<×ß–¯¤É|îækÎg:‘ªÊJx?ŠÜNZ4k}úÒ3iʸjAKei]ñ¾¥Bp‡ÂzæõwÅùÁÞž\±–V7ì.ª¼)MŸ—8qÌÁ6±¢ú§/9›.9{ýó¥5üqh×ú·eÏ!Bï4§‘Pçgìó=ɲæ{9‡¥|àïæ¶N–í†tÇñ_Ÿ}“­ŽÓè²÷.¦××oÊ^î†ù^ÃE…߬h‹g¡üŸqÒtb6=úâ;ÔÚÑÅVœutúÂéý*=xöps;½Ë‚çVÖÓ*Ñ~fã4+.!Ú¼ë ýíÙ·hÉœIÄêÅyÐ`¥é¥Õ ´ƒ;0—Ÿ³˜fr'í_¦5Ü©Y»y}ýÓÓ§¸Sç>sÙYBK¥MÊ| |/ÛX!È“^UÍQ>ûb'¢”|o4ö°:È&f²øÈrg¼ñ\vÎ"š3e,Me ÍJî9"RZLöjf¡<£WÜŒÍj¥Ê²"¬/i ~´ux…0}úµõèíÄ®ôŽUÜ ^2g ]ÌÖ¤ø´qû>Ñ[]UBØj«Ëh]ãºàôñ·ÅŽãƒÞ(¬Bè1§‘ QHeœ§ÒÀ?ç=™;ï?Õˆ3{“cÐ6sçâ´Ó-Ú,?wýåYVjZ©¦²„Î[v¢…Sf‚ ¬¯í]>qï|võÊEêŠ.‰)=ñí÷4î>(,«ˆ‡CÚ¾÷ˆ°Ôâx+·}Ä •{X&yÊɶIä§’@@Ê”‚ƒ#gña?…ruå˜QñÉ8t°ô ÃãÜkDºñÃïãÞë[ôÿ~ÿ Ê©ô‰‹O..q±Ï¿'8Î=Åh4*¬.°a“©¿w„Ù2³ûÄZ,“Õ“"œÇÖ½‡i›â×lÚ»p„{¹ý%X¨~ýð‹4mü(º”{Ù©§Ì¸S¿z2€ûH&žv"fi‘çäAÇñ ÖÎٓLjQbŸ¿ò}Âu=þøÜSæˆ PJ}áºûçèžÛ®ŽÝ³‘ÇŽãÐ&Ñ™9}áŒØé‰µUl]Ý/~Ï™:NX‹à?p´…ÊKŠD§(Ù6Ë\4l>ß'¹œ­?Êâ“#Ü`&ÅgØzå=N[MßüÌ¥¬|ì¤ÿ½’*ÞðÐç.=¡êö:FËߪ§›?~¾’¯¼³‰]f« ‡Q÷Ýý½±;XÛ¦¯ Ëj±zÀç,™Íï[rÂûâO@yº÷‘—¨”ƒ™?sùÙGÈtÊ|Ž™¦0õüò¹l©£’æ“ýñ:²´r ¬/àYð>bçâÓN¶àÀ];vT…pa-š=QĽÅßÓ÷±vóyÎ7ÞÝ*yÝÎù÷—‡TÚ[;c—³;K¦ÅüÎgߨ ¬BӯײËk¡ ÁÊ +Ñ6)óRû~(¨6g³iûƒ!ÙV1>ýr„ Oš4Ægø-0—#ˆ¹Óëñ=ˆáq"БSmU! –ô8·yÅù ¾Ãle ew؃¸6Ð?¸ØÖ±¥q èanà g¤E„¹ªamá9RÐÛĵ¾ z¯¿ýç+"0ôüÓ狞,¬DY†¢¡Ïy¦ø­Ïå¼ûÙ]Öáç©\G®•ݸ»8p^nP`’I3&Žq=o°ûë·“W×öÌŽDè/O¿AKÙ²úÕk>@GŽ·ñÀ‚†³oïòÓ¾ÃÇ AÈèt@]Y6àýñfOæ6׸‡ö²åníÍqó5ì8@]tc3'ަ¢¸IRi“ñïQÇŒ€1`¢äI¯j¯¿yþQyB×´ry¬öæFÀLŸ¬"‰Fè߯o ??ùÏObåøŸ:§{ž÷²%æ¡åoÓ×îú+]ǧr%‚$1rÄãrÐe4ù·gW&DïUç/£?>þ*ýâoÏ‹û qÒ̉b$ÊϼI?çóè%»9ß/|ä\UQË¢y†$ŸÇñ’9“ésWžƒC•Càíú¼#–ß÷¾pEì8‘Äí`®¸…c÷ÜUp%!=ÃsQA™¹ŠG^aÄ{qˆµCG£oÂüUØpïüiÆÈ-´‘D,©Pp~ô§§E|ÚÜ&ž Ö  L?ÿës¢ÝYØúЏºKß»(¡6™ÈûÕ=…ƒ&1œyg½Ÿ5@3”vΟw¹V\?…«df ©§•>ÿí¬@¿ûáwÎÁ>Û5&§)½þŽo?9wú¤¥·^{aÙ%ö(&*äI¸DÜNüˆ[öqÏÐÃ#¼d‚õ=ÏTR€G¼ >¨¯ðFfœ-r»†Ã#õž–Ó†MÛ6?ø‹ŸÞÌ'óÖÌ‚Š|¼%×µçr$Oå6ÃF&¬;<•ÿ mjØ^8@Æ>°W{C[ÿúÏ¡k.:MÄô¡ÃñêºF¡¨ýüöOÆÚ_¶ÚädçÌiÈ”úÆuùÙ?ÉD‚<é·nfÝYwœ?b¶[gUÖiaK¿7&p2ƒßÓÞV¸·¬ÅGVù@³Ñ"–&^éÁý©*=ƳýCáéÅ ª’B _@¬ŒRßN\Lj€ƒRw\Ï 4¾¦2¦ô€nÕ&ÍP{9E:Bñ Fè¼§¬øäT©s˜Øþ¿Æ9\ EºB@! èt\nào×íày´Éåpˆ8ÄÏ©¤HsbÅÇp¶ë!(>*™¥ø˜¼‚y …@æ˜ËÃÙ±©¤ÈæãChR8lKk’5#õ¸0Û¨®á.¯Ê_! P(D#Y)ª,> ï•âcâÊQ¤) …€¹àå\bŠO„"Jñ1wu ê”â“•¤HT6¾éÇáÑ”ÁB(®*cž À#cŠNV¥øä@½*Å'*I‘¨(0¼“yª¤È1bŠE¹ºr¢êTpsNT“"R!?`žŸ•<²ªnÛ^:•WyÇ"¢Ĭڸc?½Ó°‹—‡Cï9y–XöËÄŒåaç<z—/@¿èôüD•$§à¸æØ„…zTSs“ä@m*‹OT’"Q!O<ûF½´ªWtŸNïòÒX0ÔÇËÃÈ´vón:mátzsÃvzúÕuâôîƒÇè÷ÿ\Aí>:™—{QI!`x…º˜‡¶÷0²YTtœ€@¬ÂN¸¢N( a@ —xïÒÙbé, E'>]|æI„Õ1Sú‹¬ }¸û"–¶¸–—QI!`&XÙa+1ž]Ó¢1ë™hT´ôF@Y|zã¡~)ÃŒ@{—Jºg+/+r÷š5¯ÆÄ‚Hn§]̲,~ð¿Ic*å¡Ú+̃€Îëtu'Í¢+ÅG‚aâ½R|L\9Š4…@>"0gÊXÚÀKE`M¬u[öˆÝã˹vó.ž.Â+¬ïq>ñ×Ô±BÀlðB¥1Å'±*ÅÇlÔ=JñéuJ! >°j{{§—n½ç!Ú°eo¯aÝ:¸¸þó×Q+¯§õej9‰^©æC@§˜â£Y”«Ë|t"E*ÆçDLÔ…€B`WSA·|òBÂjéÜ[ŽÅøŒª(¥»n¹Z¼ù’÷."«¥§_výeg#E*k…@hìêê^²Â¢,>i™½G{$KöÞ©Þ¤P(Èn¸ß¯ô(¨fFÀn6(´XUŒ™ëJÒ¦‰„Ú+YG ÎEïY vÎ:ê… tˆnÖ£t³SÏ?w¹†ÿÝê …@#W×eç,.pTñs7cÖq$»Ý¥‚›s 2•Å'*I‘¨P(fE@­Ïå-Š(ÅǬÕG—R|âÀP‡ …€B@! H¶õÄFu‡g)Å'ðFè^åêêüžCǨaÇ:ÞÖE­<äCkÓMgàpØ©¼ØCUeE4oÚxš8¦*ÝlÕó9€ÀÞCÍÔ°ó5·uòðl/»ù©{HÊ%P<•2tY}PÉ“¬Âõ—-ûå¶Ò–€ÏŠ³Ë«síjÉŠ¬WB /,xžÙP(L/¿³™·FjïòŠ™d5Ýɳó ²QÁÓ)@Ûç‹—[FéZ€ž|u=•yèýËæÐ¹Kç°_¸à«¡X¹ý“ïŸ^\½IÌWÃa,”q~D9ÈS¢½ñ0öm¼0é.^Ë’×"Ÿ?HšE£ÊRU”ñ¾ˆ;Õb8 \IJžäJMe†Îv=4ZæÄŸ’#òXíÍ@Á~q! ¢Ñ¨°îüõÙ·©­;–hÙÓI‹Tò‡Ê>,5§s‡@·§ÎèzüåµôÊêÍôÉž.¬@¹$à‡œ<È´Wà_+ÙZØ•~dfç)ÙÖÖnÚÍ«²o§íûŽRˆWh7Bˆ lV‚zÕü<“s„ŒTSYJï9i½x&¹]Ü1i’e„µXÉ“VÒ0 é1ŇùY)>ÀñpdYŠL—ÿÒê¶¾¼Ë¨"²ñ¾|80î•'*-<š,¼E-­Ô®o£{y™®ú2ÝrÍùTZì¦w·î¥ºíûiïÁf ²ë,>ª(¡©ãkhéœIÂ’:\ %OâQ/ÌcVz”Å'«¾`)¤ÏS¿}=ýzøHÙóF´Ú p ØŠÿÄ+ëi4OÛ¿`Æ¥üŒh­$þrðÜ[ÿxi­)ø ”g“§P~¯?@»Øš³›¹[Û»ØÖ˜²"5oç­ƒ-`'±"“ÚêêZ¤ŒlþÅr¯¢ß?ñªX±=ÂV[XÖ´p-;Ê\lõŠîvû©¹©“š[öÒªú"–î’³O¢3Ø]fµf®s£äIâm$ŸïÔtª•øX¹ºr¤² FñAJOg—ya iìÞ‚¥Ç, ´èlÞhù*š>~4¹ÝÔý¹ÌB±¢`”ÖϬ亹ø ´ OÉþþ#Çé%â^·y…9fG¼“œ¼l‘…•/ÕLb«“•cçRUzD¦ø§ÛXÉ©âÁ‡É®2Gh+;îØåø´=¨³¥¨™:õ½s³’^]³…n¸â,ª­f++‚‹ÒLJž¤ `ž<®kÑÑr®¨¦Í“bå}1 Bñ‚{+ ÒŠ5›©Ãë1=#áÞˆ£@‹50ƒÚ´õôòšMtÁé „€.3ý@t¨ó‰#¾Âè-Æ#FÌLü„Rdš§¤Âƒ)žy£Ž^á¶„-XË1;5†k*.fÇŠ/+kkLúÉœIÖд„pR¨&‹¯šc…šè`ÓúŸûÿM7\vÍŸ>>­X:%OÒ¯Ë|É­<£¥Å‡%*‹OŽTlæl¿&-0„5™ƒ¬ôø|~Z¹q hô@‡?9YH@h{mÝ6A/èý*™ÉW˜Á¬üÔ2ÅSFy#Ôå Ð}ŒÿÕ§à²yO'[h&Y"ܞ┼[(°Êô9k©$(r©Œ¶´DF‘Í·”B'ýöŸ+hó΃,zFŽ%C‹¬w%O’A-ïÕµžŸ¨®)Å'Gª:ï£wÖžûŽðè XÆâÑLWM ­“4ÄL„9hô«d>P/»¹Ž:¼>SóK—§ð±SžsçÿñŠe Î%{p+"¹a4ÖtÇ LzØ#”Ÿc{”Šò£ä‰ùÚâHRÄNÓØÇÄjWŠÏHÖE2ïÎkÅ'Ö; …ÈϘ[öf÷÷Dyž³&1‡Ó¸y×A 2ÝÊêc¾š’|Õ°s¿éù è¥ÃSRé 1/.«Žv²²gõctVLÞ›¯‚ Ö"›Çúýcéж’éXÈzG»Tòd ítܨ®RÍ®,>9Rÿ øDXÐñ̰lEiáùD´¨#%sy¶êSÌÉuŠ%@7z¥¸*™ã¡æÖNæ%§©ù ¨¥ÊS²œàÃGȼ…G®ÕŠÍ<µ‘%°üXCShËÞÃ<‘⑤:ñx(y’îùx7–«`ÉìeÓÈ·ê«3Úó±œùX¦¼V|Лƒâ€‘7þ@@5ëXŠÂôÉAm<ú tƒþdz¥¦/Z(ù k¹‰eMr¢LÉó>ôrPÀ+k9TYã¹x¦åDi#Òƒ’ÐÛ<Ü=w²¬w%OC·p®u„z5†…SòÜ/iž+>,¸»ŸÇ'„xd—–¡@Ëa­z^, ÅôGåÔ ÃúR•y¢ >P/®£Œ­å–èËS½/Iž2¬˜"D¼~݆mxym)z¥ayŒH)í;|\Lq‘¨;YÖ;%OÆ·P®ð\R3dYyt×vy¬öæG onišF¯£0rIÐñqeº¥« eQiäˆç«\«“dx e톟­»‘˜00iñ#"<þ;šóõ$Â~QûnYš1®R”‹'’zê]É“DðÊû{ôW—UW®®\ªï¼V|P1a•!¥Çá`“ÿ¨Ù¬6É[&>ª’öLä§òÈ,¨›L¥Áx*Sï@>Éð”áÖáàfvwÙÀïZïÐ3I—Ë5@ƒâ—¸œ<[sfš” ËW„í{ibu1[|Š’.†l“™®ÿLÉDJ¦î3ñ¾|ÏcÞ½ ÅÜ-åhn¼hËÇn›³/ßËœOåËŸnÜ0ÔJQ‘…¾{Û8úõШ*;}ð¼2ª®´óäeQÚºÓO9Š7…¹œmô¡óÊiÙâb²Û5Ú¹×OO=ÛJ»÷aaW"àôÞ3ŠiÂ8§8÷o¢–¶HZåš:uGÂ{¬=¬ùìüž¦%f6LøêÆáD {-~8KÑOÞ™è‘]pN9íÜ }‚¼FQ„^z­¾ýãýô›ÿ;B³§»é=§”ˆ7׎²Óá#!úñ/Òoÿï(-˜íaÁUDz{m]rAE?&w*åIîêîþH§` üàùeôü+­â£=OMæþ7¡’âÞM4[<%•ì«K¹¯¢––þ Iø\|ù9äŽÚøCþ‡›è[?ÚG+Ww°XA•åF_ìÒ Ê_üî0½Á×®º¬J\{{m¹ÝѾ~q÷:Î`„6h#•«ÕÊ\î³aXI§Þ%S':iæ47½òF›èU±BûÛ¿J0ð¸˜!$t˜®¹²’ê¼ôƒ»°ì‰Ò Ÿ0FÔ¥S~Iö™(O|~…vÌce¦Ç•YÅ÷Ä‘ ‡ymñ‰ÞÉöÝl~?sY1Ý÷gcò-Ûý±úìèŒP{GX`œ\[×%6yÞýš:ÙEo½ÓI/½ÞNßÿúx3ÚA‡Žå-CîÒÿp¯Øåê$‡³ˆl6ž!8­žé*·Ì™óå[.}~È—çã ì©ÙÓHtó·ö¤U:ãAÂá]¤•¸·Î‹p&˜–,ô•Ÿ5ºÄƒñÔØZ; ëǹg–özCª<…L¢´üðãÇ(íbÚ]½V)·Ù4ú([.+Êá^êÖت²a]çiÔ(±1^ñ<ÕÔ·ü/¾ÞËj3·¯]À®-—ñÞ“æÑSϵÒÎ=±-[T,,d/ñ3¯½ÕNçSFï¼kàËdˆ[x"Ï:]ÃåØKÛ ]OÕS­k>·cŸh_¥%6ºîªê^îjX¢}ê8[0¼Ÿ­½]äíj£öö ‡t{÷GʘæUë:©³ Æ(+=Gc·mØè¥+.6:Hóg»9öˆè¥7:x¯Ó3/´Ð|e¬°¶sg*ÕòÇËÃØ‹ÕAJhzt†48j¤«¡ì)¡8rõîNަ{ó蛀ûõ¸¨ÐC;i¾‡{cUÄòˆV¯;Qð¢7:~¬ƒvï5LÖ­Ü«õrm\­ÃteTe±ì=ÀÊ/>j2 ÄSPš_fŇ '¤l𔾳ˆØ(Ò®QÔ {ÚwŸ@O¢'ú+ÿä‰zÏ©ÅtÕ¥U¢ópðpHt(àþinéi{ÇŽGX!3ÀØw0H5Õ¼î+jÉ&±Xip&Ï@=Âä£ÖðÞd³Hù~È€qò$>£S]´«[fÀ}nã.)0@*òå®­±‹ßé”_d þ¥¯Ê³øDÉ¢Ÿ´ÍnymñIÊQËÓÉ–ÄóÈt ÷:/xzß=ý\ µwžðyÍÕÔÖ¡•k:åctôX˜ª«ûù‚ÅîP…€@ lj51/ħDx*þ~yœ*OiìÖ¹êòJ¶³‚ÁëVõ£Y!¶±/6›•;]rá(jjÒhíÎctÌ»‹t-ÄŠÃ&%9ñÑ_ù¡ð F–•g_6:nvõ A)Š’‡;HMÍ!ᚪ®´¥;g ×ni%¯v>þáS9v¯L”7féê~1Ó/|Š­Dì‹Âú\íÔ|ÜFGGéÅ í,ºobg·[¨¼Ì*hï{ëv‹/˜ã¦_üÞ°.7n÷ñûtñ^X¼`B’ s&Êß—õ;ifÊ',eñ‘XäÊ>9É•+¥Êk€`Âø´üåV”A}ãu54Š{?Ñ»åÒ ËiÊ;Ý󿇅à’ZÛÂTÉÁšÉ¤±Ü;¼êãc©ª²bÐT¢yÞóÀrÚ°iÛæñÓ›ù™Ã¼pøR|¼õ.(ŸÈ“m_Òëïøö“s§OZz뵦U´øàŸÚÃîËij+gع§ÇeŠ'‡â©rO…§Ê«ïyií±±ÙÁáp°ûÉI›æÔÓ–ÃítÔ»ŸG·$+[N¬á1}ðwåÿé¯qÞVZ¼À#>ô÷ýéíá˜:$Œv“ÉÁŠƒ×gX€Ð±€ëm4AÖð8 ÚPýŽýtvY1+q.>ùâ íËË ¥­¥µ·â;u’“>Îä¿þ£™öt6‡B:ýäW‡D7ÜÑÉúܵ5KR¦ÊŸ¡¢d6ì6\ î$GƒPì¬ø8¶´¸Ù:äæ`c7M,w‘››ÏdÆÏWÔ`´Êk•ßËÞâ]°ðÌäA°²úxUÕÓY¨æc({H%‹ƒ$ä—NÒ¢XX[£c-ÝöXwÓÉ·¿g!OÊK{Ê4nŒ>Ϩ§X±YÛ÷%ŸmjÓ?Ÿ9N}ì˜ÛÃe=Þjà©òËw©}r̺³æÎ2<Åm娆[gH.u÷H# Ÿjàè± X«j‹[N]TD£yôæÁˆ®l~>À±H§/-æ‘[ålªoãû5šÂ£7°É„¡ðMœŸJ…ÜSˆM‘i0ž’÷ ´nž2\]ÝŠ[{ŠŠ<äbרÖã!òG°Jûl²†&D^¿çã˶…!Û°h”²2pÞ{Ë„…çà!£M­¯÷r»*!¸šÎ:­„Šy„×å„TÓ­55§Û™àÑ\ü‡ª1_‘1Sz{­ßB¤xJ];+/5£ ŧšeÂ?=š¶îpG*“!’1¸¯ùsãc· «Ü]÷µR»Ÿ'4 ÎgרÁHì÷Z|ù1„üìÓKèê+*8¶ÆB —FH"=ý| ÝÀs}÷vvG±Òë¬ Hxð:&!ŽûI'é–€°\yxž"¬K†Ø&Œ|BÙ‡#a ÜäH‹æ¹…,9‰÷Ødºÿ¡&ZÏ#¼¾ñÕ±Bv´±•ë¥×ÚD »¼'Så—ù©}rèºv²1(ì…ônrO«»Í€€R|¨Ÿ/J¯®ì=Ñí»Žò„„ÇùÎãÂâðŒÇï¼÷й}àì21Ç!ž®Ra#°fƒ—ƒãËĤu~Œ§$R˜ÐONê'Ïeƒ§ðñ‡rËÜ^ÇÚ½´ýP+[y&§¤ô€ö¾å¿ç‰F/Å2ã^Œ„üÕŽˆ9m‚<)¨œŽ3ŸóžRzŠ'4L7éÝóâœÅú[Ò¤’nÆ<¿œçoúògjé¹môÏ †m ô#žŒá.¿ø{2Yþø|ÕqhúÉbt~„ç‚RŠOЙåVåê¤&žc‹N%›Úá‹—©¯Ò#Ï÷·/fëМ™.z’ç#QI!€9YçÙy—-)|‰H.ðÔêMûx‰RBž$ÉNzß_ù¡ÐôUzâ3ƬÍRéÁùE}¸)³ Åß›ìqÄz˜¬ +Å'G«5¹–žƒ…DOMn.6«k¼àb’¯e³Ø -Ê4qÐ¥¤;›ïWïJ ÔM©ÇÉ!/ææ'”&žBŒ¯p qŒO˜—¢ž2ªHÄÄ 6&WSÔÒFaçfró(²“&”‹I±lf§¶ÅViÜ(Û$öJžä*'¤N7·‰˜âÃãÕ>©C9¢OæµâR<ÛzsUÅÆ¤‚ºCÓÍ™$mc+yäþº7sR[˜TÉ:™T[)FÉ:3+’¾dy V$ì'Uó‡ÞÊŠC=+R9b劫ˆí…\ëÙ…¬ÓIcKxŽœb± f¥ÆìÔ‰X|d½£]*ynžóç]˜ª¶Q\-jVÖHÑó®˜y«øÄ„wà0‰…{tåEv²óä‚fM ÍÁ˜ª2O÷ÚAì¥PÊiªKÖW ©*a—¤ÝÔüàRå)‹Æí†; h?Nžäo^­›ç-á‰]ëXù1&4MÅ @ˆ®ù)äÜHaÇ&âæO'uSey1ÏD]Ä‹‰…Z1I#dÌÕ_ï9%O¬G(Ò¨®ž'PrDÉ“XeÈÓ‡[;±TÄ 0”[ëî¨ÍF0dÉ ï†¼U|P•†Ð¶²à¶ Á†^]-Ï­µ4óÖjºÚM  nbÐm0lºBAñ|5ob•iù U‘*OA±Ã†r¡R'»ƒÊÙb:³Šy’g<¹×ˆX8ÄRIÂåÌ®§L$¸ä»7(eQÛaVv(è~›tkÕò²\óG»©¢¬„×K+áe2Š©¸˜—áà©í¬ø$ÒÆâëí3'䉵™:iÔ°%H<ÒBzBeÍD½ä]í Y&æ¹·å±Úçy«øÈš¡ô«L#ˆq|…KX}ÂŽ­—¦©1КœÜ»ž9ëæwÐ/Ëbb ˜Y’¯NÃ1#6m.~B¥ÃSò#OPz°NVh¯.qÓL^™ÅmåØû6V,V±E‰×hLp´$$§°m/]oóÆ–#KêsI6ŒÚ°U§-Qk™ž•¼­¦c3é¶£TéˆÐìJ¦TQy9\\¥TÊÊOI±áêBÙ†²öà=}ëí3ä‰ÕÈßâà”'~ŒÐ½:ÆëƒùEy$~jŸ<­Õ{zî´¼Ùs¬Žr ¼U|P¢ÇÊŠüørnîáM,ÕXàvQ„Í’@ hš3†ÍïL#è£M„Ågð€K³”¡Pèˆç+8Ÿ2£†­ æâ'ÔE:<…=”;‡Û +=ÅÅìbe¡¤¤˜ÊØR2­\£q®0w"º„Âp½ÁÊG+A{ÙúÒ̼ÜiX_X±Õ)ÂÊ ÜM!Ϭ$î [ÀÕZžNÒ-üÎ]ädÅk´=@£x«u„hJq˜æVê4¥ÊIÕPxÊK©¢¼ŒÊx_VZjX{xDW"±=’¾øzÏyrÒÄb^⣊ì,û¬;ùýþÁc½Öþ’åSû!Ðô˜âã°êJñ.3_6ÛÌͰ›ë=Úå…pœÖ_öZ¡@¸XÈ¡× !^Sꦮ`'ö%âÉÖà„+ŽH=¡WŽT„Mõ“+4®ºTÐ zA7>>‰˜á‡"Þ é<4Ù|ûlC=šë×3ÊS£/_MSIÇÚºhÓæ'ŽŸ·FŽŸ@_&xÊP|x‘ ¶ø Í`öf1´½{!Oà 棲@:xk ò4¶‘cb¥g „àˆk˜Š­!VT¢ÔÊ£ÅZˆ•$V˜lÁ™¤éˆM,Á¥±íî6›¦Ó8O”\L«a‰3ÜPˆÝíXY1=eÝ–ž’t.ŒÑ\ˆ×AYI}ë=WäIÕ¬ :iA)=óR¼>È9ž|¶…Ž6…éªË*D1‘ò÷½2% Î%^¦ô½-/~Ï»§az(©Aa˜_Ž×m~£vK^­ a&ÅG¤h(r´µ­ ¿“HÝÏõÝÉ^+|øRü 4>Œ‰Ù|tÄ”cºøc5“,Ñò¾Y ëoô„…Ë{Íx÷éµÜBÚ#èÝä‰ æÁˆmmïÒƒ@M1Ü{>G¯ÅÊ–)žýñÕ‚É5B1Øz„ùÉ=2üÚ2ÉSøÐCñ‰F¬8¸ÅzV¢ü|î!¸{¼^ïƒT ñLÏa †u ²O Ì.–¨èNp4(Ç ¹XÒ8xQC‰çix¤˜ƒgƒ¶úƒ¬ú4‹XK„?ÂØtn‡üNV!9+U#ÂzD¬ìDÙ’µ%‡2G”Æ[ÈíôÝÆƒ˜^´iå…‚ãá6…@f”JÎmËÞF™IýÕ{®È“‰•%tËt=üDíÞo¬r¿rM5Ñg>>ŠåMò?È”P0Øwˆl¬Í%‚i®ÜÑéLI+ð-æ…¼,§,c¾ïͤøÄ°;|ÍËJЇÝ>©&CPYØ\ÏÎÂ/À½Ó iÌM2Ž{¯Žv?òu‰¡®–HY#£9\¡’{Ÿ<dæTÁðbŒ´A 3O)B“-J{@IDATÊ-4ž-=p#Á¥Àt‚^ÐLt r#ðô{»šº'ßÏgЧ€Ó@|5oÒ(þÀkÔx¨‹ãWÖ³"Íü^~=ÃÅS('(8rh{o«‡Wð©¿»3âɱÌÖøÂýØŒ<Œ i9J Üâ·ƒÝRe÷ujÍl1êûE {'—%Jev'!´R[t`EÇÆp³‹˜Û­»Í£ å‡ã“p ww*J(¨ÞsEž±[ö¦ŠèѧZiõzc@ÒÖ~ºûÑ®«¡êªÄež”)AŸ×¼Cd{³MZ¿t=ssq‹Sn®´Ðù‡Í¨øè‡÷íy³´ªêæúíhÙü©i¡A ?>z{!Ñ3…p6›q½Äá§£Þ(µ„ x S½¦s“7MÏ Dbº1ybP”‡;¿4Ú¥Óør'ZføRŒ6òÃtA—ÉÏ&ÛXÀ‘?BÚÞ­u|=¹õw{>žË(O øj óU¹ËN;›ÚépÇq ±r‹”i~BžÙà)” 3~c„$øS(^Áêág+*–·€;,–Ù¾¤²€6hÄÓsSáX^¡49yïqEÛ„ÅÈË«€D <±½‡wü_ck‘ÎÿØkÜ60XÁp_#àZXqx>¡Øplœ°ø°âoù2„ßr´$èJ% Tï²¼f—'(ÿ'>RM5£ô¯ç[MǬü¡>^MÓ§$æn”2eߎíïp&y/OX‡)>6‹U)>©4=“™¯zæ $ЫÏ<Þ0ã¤EÍï4ì¬dÅ'5 ÕM“¼èµF™{Ñ#åH†³ ¹Óé§ZŽcè D¨“ç½²,¬ûøÞ´^C–QÌ!ä°èTæ´R‰ÛÊ=Uĸ…¥JOyY™8FïôfÂÚG=‰´o\óöþ O ªn$pWÞ¦ŒóН¼äÈ„.µtú©Å¦@˜ç“ÑÙU“Á„Ùâ©åÇ!>ô Ń-(E‚AÃ’Ê{Ä1¯õQ|x¼P|Œé€¤ [_½>Ÿp—V#~ž­@%¬íHEBb Œàân¥‡ß-”~?\Y8–qqñ fd–J—lO©*=’ä“ëòä¼÷–RM•xŒã²8œÒËÚæ½:JW_^IË–£¨ƒ&È^íuíë/oáóZžp|Oe(R²¬©ð¼³iPtÔE³#`&Å'¾ñDšxx£¦ýöÞ>®âÚ?Û´ZõjIîÝ–+®cšM% %´ž¡%<’÷ÞïO^’Ç{)@ ý’@ÀtclÀcÜ î]¶å.ÉVïÒ–ÿùÎjv¯V+i»¶ÌÑçêÞ»÷Þ™s¾wæÜ3gÎÌüàðÉs4jpaP8JÇÌG*²Á‡ bÔ F|™©¹¹E´@SXqçÚŠîxtó‹?ÿI% ¥ >°´"Üáž1=ìñ§Gtu¹¼=¡ j~{KËt'K~ʬcEM¹iñöSª˜¹]+cHËð¥\Y,-”Ë *>èÁ–«¾(SùbC½1Á‹*Ê°ÓØ‘ÞáñaëNvuá9tqÁXpz|xÙnyà>LŽJIiáú×,¼?ðÈ¢ÚÓðAžHF º´Ð}…àbx{0û²³±À^ŽóÁ}¸_äÝQï$ï¡(¹¾¼÷XÐ'“'¤PNvýõÕóTWÌôú{•"èù¦k»w”:åôñ£Kϸ×'<Øf¶l;tŽí«Æ³Šbh2|€#Úâ"­~ÿÝ·¿òý‡ïX¸lSöSܤO2·h'” ” ó¼c˜;·[Øð»^Ž^‘îú`Œ‘™&?©üÁ‡eŘ´X±9cBÓÅÕÖn#ÆÏn·¶×¯ÿhÉ fQؤ² ¡ÿSN [™‚¸}U®dŽd™’Fd–ù¢á ìa°à* -Ÿxç x^a,Áhinẇî2Þ[Ù@t¦ƒ®.gÃCN˜}]\ð¶ÀÛ¤í‘]ÃNƒGQ;ëºÈ0Äÿúê½C -¦0&ƒÑ'ƒ˜é‰ï±ñSA§Ï:»áW¬­¥ŠÊvºïö#y,ï1'YÞöÜ´q½szl.Ϙ¬{xNx|Øp’zÔa±á%»¶Po°E‚¤Lñ O²xDé£ß)¤WÞº@{8'”ܽ¿‰žÿë9úÎ=ù< €û3!uÊÎkÿÖÐÀs8$€>±k›Ù™¨ ŸHT°0çá.ÑaÎÈÇäÑ:ÇGçÖ Ÿ|¸)#'ûU>¾ŸÞ#Ç}7]ª Æó#¨TZP¦ÒuOt×£5 hs²ò– ˜ùð›—Ø¸wXºà¡¼¡´¡4¡ÀÑm>p=Š­2(¨­ŽéJ÷îZ²sÃ,¦‡•%å&ŸDñø„­L¡@Dº\õE™‚œž$ùeõDÖ¹Ç= í^Þç4bØëÊÕ–ìôuFKãI>ë4j: 4 D#Â9oìF–yˆ #ðùA‡`þbYŸ$%éè›wçÓ˪]“–i£ßpÐówïÍç`hƒK§+ÙûÎÖÕŸmcˆã^ŸŒk_Rû û éñ1Ù,Êð‰@Ý wÑhø uŽ3úQ›—½ñ¯÷|õk&>¾óleµãÎkféCóãTVkiÜõN£Ç-•¯Tẫß$•1#¾¢åÊÊÒÀçPüÎs§Á#ïõ;ÍèG÷<=‡÷î\ºjñ»Ÿñe%ïáõÁ1ðΉbø„½L1–.Osr?6jÃT®d9‰T™‚l¾ø’¼õt¿¼{g—¯Ÿ×á1’^#i!y¿S^il¸ñö%Ïžø öšä+Üï]b}8Þ=ôÓÍ×e³‘c¤·W‹wRWg¥çþr–RrŽÚÏ7œ #ûw/Z¹èí˜è’¸×'ÖÓ=Ρn:]éîäYJÅ:Ñfø k©vÞP©àJMþäí×ߟ:÷ò ûìËøí«Ë2'Œè˜1~¸nâÈÏóãTºÎn%oîz­â …á#•7öPZÚVj°Šsj`x)FZ ÙfmoزjÅ;{6oØ«Á±¡Oà |3ðŽwŠX™‘(W²¼`/·P—©H -ïÈOÖ9oõÍSæHðçO༇X×'—LÇZfúç›Uì×±'ލ®b˜î\ÙÑÏÙèYƸ@/'„>á©©¯’å€_ñjy¬ö±@´>ÒãƒÊ… “¶¯]õEɶ­¥³®¹~Ï{9à3XÙ8Ò-fGVfª.Ù3 /À÷â©t=ÏIV*mù¬ç¹üÝß=–õÀ טœùÏÑû×múä£õ--Mè¬Ç ë;ö8Öz|Éðé³2Ř»º€p ¶\y–!Ïsg.ê_#àùž=ÏáÏó]{ž’¦|F«SHoÑ™t“ì:¦uÖé† œÅM·¤žYòþ_ãû¡KAŸ¸ ½]·Bâ¤ö±@´>@SÆù ÿ- tsaH—žti墷–òñg£&^4lȨ1Å–´ôl³Å’a4šŸâ™ŒE²Zy>â¦æú¦†ºš‡*Ý·ëË!»uàÙbªíØêx<+b^¡›‹Å¤Ê”DBí= uJsC}õ‰Ã%«+«n¾õùoZR²²¹®2ì’{î¹ÿuú×?¿öKN&®õɤ_ŸKmuœŸ%Fr#ÛäH^ÙtêR !†ìš@ C4`ô8#$ÝFQûá=;òv”¯A܇MÞLJ CÀ vùqG7¼:PJðôÀðÁ]8†1$ã{ÁÛÃâ ReJ"¡ö žè¢OÞy뻿üòí|8%%wÍÊ|ÏwZnZöÁ¾ò䞸Õ'­†Ê¹¼b6ã™×iÏî«ø`†p•qx'¤1£ýxáw oðò¸Tð^€d+ç¨dðhÀðA´= È!ºÃxŸF‹)Hâ£ÝWÒÛÆ ëÜp¸&*©2•¨o^Éí Ýꓺº³o½þÀwîº÷õÿÏ”d¹‰±1pcfþˆž~zß O?=º&ÞÈmøèíŸÅ›p‰,O4>²âc­=Æs="ð™÷Þ Ÿx6€€Hâ" ¸¥aã"Îñ»Œë‘ÏòO G/U¦îÕ+{@@êY?¼ê“ººó-ùÃüïÿàÑU§õ:ã“"=‡ã²Êºšå?½éºŸžïr\Дç7¶µL‚0<š«]ŸlX‚)!Ñlø€ATDTBm…ÄG sxzÐÿ* ƒ'žO0‘0Ò*+`c{l¸–ÈžßEªL¹ P R—ÈúÑ­>ùãó—ÿ䇯/çEŸ‹§y¤Úl]íÓGŸÞqÍóOOA€[~Ü¥ñO lÒË#==rÏ—â–¤¢Â^nÀFnò·¸ ‚#iÊ©D.S!€T%£ .€¤ÞÀ^êìåï¸GÐ ÏÎþߨÐÎK´ý™/³ÎuLµê+ŸxbëÕ¿ýíô ò¾XÜóÜH äœK:½ lŽÅwØϱdøH9dDeI£Çy–Ý\RV`!Iâ"ÏÕÞw$vªLùŽ™º3þð[Ÿ¼ðÛÙýácëÛ:ÝߨPÐórm“[-«ù馫~÷ß³Êc¢ñ¿Ù7®ÝaÒÁ{mÑEã7ŒEAÏÝ"‹†§0ò£åù»:WŠ€*S"§žK8^xnÎßz|=<ðÿà aãí-Vx~æÅ¢çÇê°ßÀ2âÀæOV]¡ƒ7XQ! cbâH$%ŠB@! PDß?;ç_ì{¿› a$°çg\›£eöʈ$!ÉKG×Ëtt:ÃGòXíãeøÄÏ»T’( >Cà÷Ï^ú¦No`ãG'ºŒÙø™Fµ¶¥O?½5¥Ï˜ò3ã‹_8̆šcŽxŒ—©0ìû™„º=P†O ¼$Å¢B@! ˆ^øÍ%oq˜å·yñB¼ÎÕÜ u­ïñ$‡bé‡h—¡¦­ƒšÅT)`ÛÞG'ÆdœR´ãÜ×ü)ç¯ß€Ê_! PÄ/>;ûe½^÷˜K$‡ãšÊÚê…o½å@üO”“]ÓÍ¥SÝ\Qþ¶eO>"§žS( ¯ðh¯ß‘NÿÿäEvÿܶfãú—xäWÔN5"xsÐu’g½C>‹xÛ+Ã'ÞÞ¨’G! PD¿vöÏ9àù7Vîø‰õ¿ÔœGÕaño÷L容¦Îßþ£q[¢ŠAÅLÈP†OÈ T ) …€xþ1ÇûüÕõ›ƒ~òðã¾á:¢öEi»¹–=ݤE,*VB„€2|B¤JF! P(º"pÙ%³À+}.•W¸KéÏýhý•òúäÆâ¨`™˜üìÁ¼›~°»Ù˜º ÇŠâeøÄç{UR) ¨Aàé§Ç7XŒŽu¤+Sìœe³Ú>üÑÖ÷‹&›í·ÈÕØ¤[µó±aq±Ê|4`<(Ã'ߊâI! PÄ¿úÕ¥gHgDwRDãùr†ñòî‹£bŽ»ý 7{|ÉcµO”áŸïUI¥P(¢Ÿ½x—^Gw0c60ÇÆÏ¬ u5Ú‘_çyâwgóûËEÆ<[3%™Gœ •aDP†ODáV™) ÄFà…g/ýH§ÓÿÈ…‚ÃñðCoøªë<­Mº¹ë­cÁnÇ{&Â,¨ì"Œ€2|" ¸ÊN! P$:<»óóïóŽÄAGŽ—~øãM£äyD÷ºUæÇþÕÍ%Áˆã½2|âøå*Ñ …@Ô"iø&?‡ÁqOwX­ï<öØK$ù½äÙS6º®‘yêô&eøH0âx¯ Ÿ8~¹J4…€B@!­¼øô¬:vþ*Ïîܹ»i’Uçø}$ù­¢ºœ¯sõxnÉŇ"™¿Ê«oP†Oßà®rU( ‚É¡HÁžŸoüð±õwÊópïv›«›‹óz?Üù©ô£eøDÇ{P\( „Dà÷ÏÍ~‰ÿ§ž×Ëzá‰'¶æÉópí§ý…'RtЗdú:½Šï‘XÄû^>ñþ†•| …€B ÊÈËÌ~ˆ‡”Ÿ›Üõ”ßæh}>Ü,75ì™Ïye#žUúÄÁÇ'n wž*ýè@@>Ññ …€B aÀÌÎ:ÒWÀ]^w?ôÄÆëäy8ö6»ó uãMy¤öñ€2|âÿ+  …@Ô#ðÂs³—ñ(¯W%£:»íÏ<«sš<åþº›Ù·t³L“½MoÉcµ”áÿïXI¨P(b´dãcÜïTf¹jpe]õ3á`üh{†°g:ÓÖ)ùѤmáÈG¥(Ã':ß‹âJ! P$ÏÑ€g‡1eø<ýôß“O·Ÿ~H¯×=nµ[‹8ß‘ž’lÏÊHÑ%›LÊ{g…³7qZÚÛí5uMŽú¦=—‡_|÷?yÖjµ=ŸÑ’öâsÏ=ŽIÑX—)êÝc=›\—Üð°ÑhxTÔ)½ªS=à¥.E¶– tö¸³-«·æô:hH?“¹5$¹³æ½ÉiôdZhZQÒt¢BòÐ'¿„>±ÙìÏ LøâÓO? &Y *‘>G f ¶øœ³y™¿eÆ à˜1~8M9@—’lv–à>‡R1Ðc·©¥•ö”ž¦-ûŽî--ûßæ´æG¾þãÿøö?~ý‹åÌ“7eu~9ì %=ctMsróÿéíºþã†÷Wuª3Fê¬xéµó´{“à"/e}ókù!áè±å D¥í"­{¦fÓƒ3¯•ézÓ'¿âïΣß|êéo¾ô?OCŸ(]"ÑŠá}LxIØèyÌáÐ}T”ŸYøÄ½×ÒCw\­»xÂpb£'†¡W¬‡ ””” ”¼ÌS’yÉ7žús0îc¢œ‡ ^ÒF`Œ¸N¨:Õ bêrŸ pýU±Çœûî}MTv¦-h>šÙÞYuÜiô ±kGšº¤éMŸè Æ¥ßþ÷ÿzŒoVº¤ b±÷C´¿DÓè¡g§Õ?yÿ†Qƒ ceÅqÄ@ùø·¯ßh˜^%¤Õ':þW÷ÿè§èKT]âfÑ~SÔ>ßùÎÓ“ÉôÒÀ‚,ºï¦Kñcíe'êùC¹éŸŸEYYš0ábhPiüD=ï!fõÜ €ÅÀ‚l‡ªS!FX% û™hÐgHƒÕê {Χ¼ÁA›Êœî|PnñÑð‘¢Î è—íHNNù¿«¯þ*&VŒÚï§äYí½#­/NoËÑ=l³Ûûß±àbƒòôxyêמ@¹¹ëÚYz»ƒ ']9ïA¾4Ñb~PÇ!³ U§ E1ƒÀŒ‹ÜÝ][v8ƒaþýƒ­®ÈäÙƒLÔ/Í¿ÏôÉ×\l°;E&~˜yH4]ìQùŒo>2"À7M†G'ŒèP1=‘=^sAùA9JMOÿËhá þíDé£u©Cf 0PuŠÑPSL›œJúŽ/Õ‘ã-TUӤ㧋KÜÃáo)ö­›Ë3 ©OÌËC|-‘t‰'1}†þžþÛ »ÝQÈCÖUWL¯è`¾£å_}ë3G‰äõAýÞžÙóUŠŽ2©¸ð´T=F›ÅI;÷úïõÙuÎFÇjœñA©I:š?¬ëh.™~o{Ô!^Dµà–û¿;“ïU^ŸÞ‹ÂëÑføˆªÉœt &'äyz¢2ÅR¬!€r„ò”Ó¯à*æ=™·Dh©i½=É]Õ©X+¹Š_‰À¤q)òJú?—à"·çú‘Id6Þ¦–ú$-+s>3•ºÄ…}¼D¥áÃàÅŒÌjžžx)f}+ÊQºÅìЃ™4ÑÜ‹÷î.iø@V dªN1ŠbQÃÑ^qÒ‘«ãÇ4‚m6}TêÍh7—Ì_èþ>éô†!ü["è)zÜì£Íð?ÒëŠxŠhã-n^z" ’•™ª3˜ŒýXv­Ç'žË˜³.9[¤É$â»W2Ç>¹ÙFÊÊtNg‚!ígι ™Þ¤[q¬ê[–Ò`Ncj!z§‚#|Ÿt]§"=>ñ¬K‚+ ŸŽ¶—~ŒzÒ¥óÚ[SÒXò€cŠ~=mí¶€ŸôƒÛ§­û…,Ûƒ'ÎQs«w%´ÿèiZ³ý`Èò &!”'ƒN!¨ˆñ²Š÷¾yQ—:d5CöHÖ)Î7hj·Z©µÍ=Ë®¿ ¶¶ëo>‘¾¿§:I^Ê+ëèÜï“ òzôáº]ÔØì(–·¡ƒœÃÚ‘Îés¾—‹NAÍcQõƒ'Ô%= }’º$xÀ¢,…àMßÐ CG¸çä»ÑCçã »éó-¨¡©… =T@w_w õËÉ ¥kvÒªmè×Þ‰ø—”¿øÛbØ/‡î¿éRzí£´ëð)~¾™úçgÓ —^DS‹áýtÒÒµ;E:ò\î“Í&zþGwËÓˆî·ì;Ænb;M7¬S¾'ÏVÒ¿¼DüÆ‹ÀRzŠ…xý&º÷†9<¢Â-¿ö!^À^|ãSÅ ôp ŸýGÏÒeSÇhë³cvC©¨dW~ Üêí3izÌ2aƒŒ¨ãI²óaôÊÍÛ+¶ðGµFtiäe¥Ó­WN£iÅCé,ÿö³¿¼O|mëïæ£õ»éÓ{é7ÝI›÷¡Ï¾ØO§+ª)+=•.3Ã]÷â@–÷Ÿ~ó&\˜Ûéš·“Šªz‚!Æó¸x»¶ß¬68v–×%(òè­ÎÊÈ¢•ÛhùÆ=âq ÛÎÉH£ù³ÆÓœ‹Fw›ä’5;Èf·Ñw¿|e—{ªêiÉêâ¥ZÜK—ýø39ËÀæså¾>çí´î¤Û¾yLh °ÍβqϺÄ7;·F“áÔP ü‚Ò+Á(AEÿÊUÓiÒèÁTÛÐLo}òýæÕé?ø²¨°¸çðÉr=ĹLÆùê:*+¯¦›çM£%'i+Ø|õJÊLK¡Ã§Ê»(ʬ4&Œp*¬_ýóCºzæx6Œ†vkH„U`ÿÖ­ó(/+Ž”] ·?ÝLcùã‚u°¼ŒÅg¿‹’LÑVŒ¼q‹ß„A f¡¨°¡¼Å+9ë’Ëø c"@–•WÑ ?%9Cߺe™Œö–Ðÿ½·Š’:Æs}*ÊË¢Ov2|v–œ É£ …·>ý‚®œ^,>ȧÊ+ÉÐáî¿®ÝAùÜ Š´á³ïÈzÕ6—áÎ:ÇÝ7ôðó©ž‚ëw¢7YN3„Rº1\¸ynD› ýy2CIçÎ{÷2ËërÿÁ¡6’Îü‹©z(«¼¨S‰ K$œq³¦/–l¥b¨`X ´ÜVlÞÇžˆ±tÅŒqâeæf¦Ñ·n½ŒþóïÒº‡éÊÅXì’v°‚•†Ž-æ$á Y¼j;åsK”WŠÏ{Sˆ™é)„ ¤ã¿ìÌTÚ?OœÁFÓ’5»èç?¸Mœ£»í—[B÷Ü0[(ô×?ÞH9ÌÓnµîåðˆùtÃÜÉü|>í:tŠVo+¡I£Ò'ÜÊMfž.Ÿ>ÖåY|0âvŸ}±Ž•UÃynØuˆæNË q³Èû+WÏ­ï¯ÎŸ)ZÕ M­ôÖP)?3¤(úe§»²kk·Ò«· ƒ±‰»ÆR˜gv¥Ñ“÷]ÇŠ5)`~]øpÐáõŠÊUÞøÑxõø@F!o¬x|–mØCøýK—²ÁâüH}åê™Tzê<}¼~0|àùYËfžœR¼õÊÚ:y®’nºlŠðµ´¶ é_N:{oÝe°§"ÒS}ûˆ»l¶p÷°9ÉDw•²÷hM亷qw)}´n·ðÃ; o1Œ„žÊ:ºäPeMƒáÁ;®"“Á ŒµSlôpý›=y$×çbn|Tø‚GúßÿË>@È —¬s¨ëï®ØJÛ¹®ó;¦ã†Òmܘ3rš½é O<ŒzƒË°KN2 ™ËÎWÓhž |xò½yÏ"rzÉK„Žœ;µ³§èÔ¹*ZÆöýGψ†’‘MƒXÏ|ï+Wˆî²}´`¤BÿÜuí%4œõ'Éü^WïÛÒï—¸ ¤[‹Cãy’|u|§P¿âY—HqãjJó7ÀÈŠ´ºMã+(§âaˆMs\êùü±†±âõŒØx8.ŽñoGG«­®Iܺ„Ûý¥E«Y‰Õ»îñõ …ã*kÝÏAiAËx¡ºÆzÿóm¬lt+–ªºFÑgŽôÛÚÛE7Ò¦=G …!E¹Â­Ù~˜ö”ž¦¯ßt™ø¼÷ÙV‚·ÊBüb€ÐÚkáøŠ™GˆÇçcgXn`oØñaÁðÜÊX‚`(9}žn¹|ª0ÁŸ$¤¹íÀ zòë×Óý7ÎÏ}ƒ[Œ0 ƒáW¦ïÇe^–3ìã•´2F[=ïstORä2z䨫eUâ†OmC岂÷U6H†å‹î­¿¼÷9í;R&®ûò¯§ú6iÔ Bw ¼0Ȇ È£Úú&úç’uté”Qô=öú‚ï5lŒz*ëÈ]DûŽ–‰†Lan&e¤ZhÊØ¡ôcnLæn¹·?ÝBhårCiä ~l¦Š|¯àÆH[çÐû‚»¯Ñ°¹æ’ ´‘t1zÓâ&Í?Ôãl@­ßyˆÞûl›h¼Œ€ñlhxá¿Õs7?ž:xÈÑ­xãeÑnEÄÝÖèæBƒáx?yàÊæØžßXA¿{ýa(øòöº«oðtÀƒ’Æfî…ÞØSZÆq0©4uìÞ§`vpÑ0B>=•u\oiµ²‘s=]5sœ0¨àáºjf±htM9ˆãf좮#>)›ó@Ì òEŸ'mÜ}„!Ãèê‹Ç‹ FáöDi©;=¡½Çð,-\¶YxŸàmºtÊèNÝòž|kŸ/9~Vx»îa}…®ñ«˜-<á @|ã(ÖIM-m¸Eð3 ØK/Ãq…fš5i$Á;ä­1iÖŒwimïÝãóþAgC|\3ÂDwO™–µ`e 6õ|p›ÐÌ´¯³‚BÕs‹Å“Ð]ƒ4Ëh‘íâ.®LV@²U)ŸApå}ïVvwïâx¡½”–šìs—’LCîa¸xÒàÂ×Oø@ék+xz@i©ÎE+{e 4<'Üä[÷×ñ#0|¡Þµ@tuÁÃôÛW>­¯oßv¹ëQ(xo„`ñ MºÌðs— ¼E袓A§À/X~e^¾íÃÛ…ê}uWlÈžÅÀºÆ®#0€À’œÄ1?N•oìæ½GErövLt /"%ð¼À{‚ø §¸Ñu½»ƒîê›vpƒ|ÞOÄ>óò‡ò'g۟Ϻ+ëòFxqÐm&iÏá2îÆÛEåUu®®9«ÍéE•÷xÛ#ž †ƒ¶›}iëØó„k’¼é yM»Gwÿ/ü²Ð1è&ûó;+)•_rÀ†'ßÚgÑ(DÞ’´Xâ·qËèó­Ä@Œô„:x9‡ðž‰ø вŠÊe¼–´#o<ö·'jg}ôá!w9ºelWcº§çÕµøF š XΡܬTŽ‹1 W8ZŠ’*Xñ Kèê‹q?ø-¨]‡O²Û9ÝÕª”÷cvë•Ó9¡‚ŽðFΰí-^p‰ oµÚDŽtãkoF7WOä-¨qh^>m¬ðÎôô|O×КE?;Z~ZB°©7ÂÈ/ôÏ#vARwƒIš:v0}Ì"¼B#r—×$¡ ÜD¿2Ÿö+wðè#1+¸Œ !ޤ½GNÓ`͇ct­¬Þv’“œqwò^¹ÇÈÅÚ†¤ï™ž¼G»ï­¾iïE\¼10<©»².ïƒGœÄÆFWÞï¿^4ÆžüÝ›òr{Ôx‹Ê+ÝCʡàÛpM’7=!¯yÛÃÐÃ(2ðyìt…ËðÑòíù¼Qèf“TÍ '-];{ý‰ )xÃæs·ÜDöl’ÍN÷(ªƒ;rÚç´ÇÍ-nº©—™—W³RMÇý8 y&6G€b¶ÞE›¨Ê­Y¢ƒ-ÞÂÛ:…R@å[Ç}ÙŸlÚKª /É_9.Þ 9“Ý^ ŒÂ‚‡£»d7 Bë­>xF ”a¸±ûÝW5¸ˆ ¢u»ÓŽ‚Má~öÍûŽpkê¬ð¨€_c|α=qö‚ðÌ`¨þ`Rö•ÆíOèRÃPat»àü%ícŒ9nÁäè~CKRR0üÊ4üØw”1WœÆÔ­1)'>õÜÝõ;‡€å3\‹ÀZÄ\Ïݧ’¤7SNÈn.\C™ßÄ`è2Ù}èw!et2¢dþìáùE—7ºÛÐE‹Ÿc}ÏßûÊ•Â#y†žŒ:n9b䆤*öl,ãQ&²„K]<×Íq+fy_w{Œ6Áü6`~‡ÝñE&»àº{Æ×ß1Ú ƒÏs|¼Jè"€\øôF/¿¿FܹÁâ|%x™ ô1ä›ÅÑņˆxþµå‚'`î‰/Í›ÂÚóë+oê¾Ø@ñ;Þ}-òÛÌÝHKDãååwÌТNBÀ‹QRÚI»Õ.FÁ o,<)å,Íã²½pÙ&zô7¯Ñ}œ?›G7sÿHü6{là%A€1bÿz*ëž| ën×t•c„ؼi£;ÅÏÁÀÂ(ÓŸ¼ø¶ÑöÓÒ .¦—¯óiÙ¸{ ñs_½:0yÏøË—>Þ"ŒdŨ9oósió—Ç#ب녧膹q·Û€>®Cº·aì`Dðúî—/g}µþýïŠä€ßøïÝ&âµdúØŸ8å6f Ü]…Ú{p\Õä 5ÇÝFÒÍc»¿×óYužÀZ оó?_…þú‹ÿ¼û &¼…·Œ~ü‹Ç2ýñ{¯ "9ßmæ@;¸‡½õå÷– f-FìO „¾x u׺õMËó9¸÷ÁCô)ž0¶$!6éÉçߢ»®ƒ". ŸÕ<7 áó?ºÇÅ[8ù}öÕe´kÿáÿúݯa¾ÎñVÉŸéîˆà“8 W]bYVxÏ#Oþnò¸QÅ‘ªS¡ÀÝÀ6ŽÕð§ Jæ ÏÁÀõªcH¼ü=˜=<´Íìµôœ×±‚•„Æ“¯eÝ“È ýÓhâXÔ©îtô92Ê3ýHÃûoŽvÒÓ]‡NÒkìµ{êë7ˆ):à1ûË»«x ƒEL~*y“3Àw§OŸùÝ:Wá4h¾ñµ~4y<>]é;[é×;¨L+2Ò¿në+Ôõ ÿ>ÙSrt÷+Ï=s?2]Âï©ÿB%Ð ëñѾcíGZû»/ÇÝUR_žÅ=Ú¾x_Ÿñõ>(Qo#×|}>Ðû<ñDP"!Ç«yFWtìæ–fÀÖd}Åo rªçÂ‹æ  TAõ(×(«žFÒJçA ’|-ëò~¹‡¬=‘·|µ÷‡Shóéí¸;#ÕÎ^žjžÆæ+:~æ‚ίM¯']ºy[ƒËèÁjFãF»»ÊµiàøíýnÏЭÅî˜ç}ê_#†Û–òdŒ£xžŽ+9ÝÞF“ȼÔ^!  ½¼Xê^Hs Íá5©0â káõçQ%< *&&ü¯ì®¥“<‰(¥1›1tæ,¤€¨¯˜ÿkϪî­þkÓˆ…c;‡Ž´ÐîýM´s_3ÇòtžY"%EOwÝ’K“Ƨô(ΧGÛè<Aƒú¥êéªájîžKð‹ª«+J ÖÕÈksõã™ZŸyy©Ë=¾›ýãÛ+yÑÅ^ê¡ ·X-J,RˆéùA˜ 3Á‚0§Æv^ÝÆ3²Âíþö§›Åb«—ðäk˜¸33+R(‹À¶ÇÅÚTëw•òÊéÛEfÎ5Ä0ã3V4÷$\?ݱ=tÀë¼bù$¾S>üsé:q»·úï™N4žŸ-o£5ë误VÐS¿à5ÁþYA¶4t2zŒ¼$Åe—¤Ó>6 W£2.ÜãîN¼}|ñNŠÝ" <>ÝBÙ ˜5úÒ‹œÃ;×óL­%¼xß^ß Ã3ƒ0;¦¶Ç¢¥#xºwÐàÂ<áá'=üÃäf_¹z¦˜S+[æ)îÑ’T¤P„,µ.&î[±yÉÕ½°Ž< 4ËÄTÖ:׷Šïž4‹g†Ç,ÊC¸®Ãóƒ•Ô8½Â¾ÖÏ4#qŽIËδґãmtô/ëq¼•0<½;ÊÎ2Ò,®>‡·î‚˜=Ÿ-­²Ñ–3Πf<·SAÍž©óÎ(ç3}v†E%Yxé†f÷HÙí…ë;¸õXÏJoOí/ ùœç¾Ž×<ÒR~vºkFU“Épü6Mu¬PôŒæ­‰zÍ“xJRä^Í‹c17ç(×¹ÜLRÇD‡Æɼõ‡¡sâT>ÖB¥G[èèÉjkëˆ8î&Ó¼\Mk¡ÉRhØ`ÿ–…{ݘÎa¢|îêR¤è eøô„N¯•ò€µ<³i÷÷ŸáXœqàd × d0Û©³k+[Ü^À­È£eç †î(9.~“ÿ´³$ËßÔ^! /Û£ù··½ä¤ˆóñ–ÖÄëy]¼®}7Xïγþ{K;Ü¿ÕÕYiïÁ*9ÜÄñ:­ÜMß½G¼¤¥hä03–LÅ£-”›øg¨©ÝA‹KÜÝ\_›è¿án|Túч@à%.úd‰iްã˘Œ~ýæN Ÿö& ™+¦ëÞåeÑSÜÀÍãxhüV±j4ºÈº[û§·´Õu…€B x°d º¸~òûwĪä·Ï¿8øD;RðVÿC–x/ U×ÚhÛî1 žž(+Ó@#†&óÆÝó¼/ìºÀãÅÛ¨‘ЈÍ躴{’I]‹m”áïï›/sq¶vaÅÿyøv×5o_⢯åUá ¼AÎØ°è!F€ÈÑ"s8~›¤{o˜-Õ^! XUþ7Ý)R¾‰cô´õZ[ç½e­½®ÕY)ôçÿºëÏúﺆ«•‡›óè«M¼nFbÁãìàÁ9<™½:Nc'Ž·ôµ¿i»¹îš ¼=ZlÔq÷(ç{lúäŠV9úÊ€·Õ0u_ÑS÷)Â@ õÚW®¼Õ_Ÿõå>Ì ¼fc=­ÞTçZ:BûF`™LãÇ¦ÐØQÉ”ÃÊ‘ ­g¬t¸Ò9ü=….½e¬;N2ù«>€¤nQ(‰€@)CƒWC?_ií$îÀþItÕe4eB*EÃèзy].,SšVd¤Ñ¹}g„9¹Pÿc eøÄÒÛR¼* 0 `·;èÃOkè³µu‚– òM¼NV6c C®%ɬқûÜóœ©!ìá˜ÈO)ÃÇãícaAl’´Çò7÷:M ÇÚsÓŠÄý¡Æ@+o,È Œ-D-S¡–å&ÔõéBe;ýý <ò»ëÈbÑÓuWfÑÜY˜ô4ðÒùWk§òzgPsnŠŒPAÍ¿¡Ä|2á Y1±B:Z='ÏVÒ^8°ª®‘jyÍ+¬wEn;ÈÿRÒogN2Rfj åd¦Ò¸áh/>ˆ È¢Áœàõö=MUmoÅd6]FP0yøû,0€q³rK }¾õËÞ̲Ï7b =/zÃf™¿Iv{¿M(;MbÁTÈŸžšLWðJó})·Ìª #¨eÊ›>Y³ý0//ã¬S:Ï1ã`Ï„=D±(zîêÑU‘C×ê¬O)žÌtŒÏõ‰U-Y^M+×Õ¹Þ5†¥éšlš7;Ýõ›¯Þä_½ã05`ÙV#V6RZôÉd£ÐÈŸDí”n«§ 'Nѯ_ÙOi©ºršïòû*—º/>HHÃG<íííÂÃñΊ/Ø»ÓFÆReÙÅûƒ”¤s÷!‡òÕ·9’©Ü:†ÊZ'Ókx•âí莳hÂÈAb–ex"AÀ`oi½þñFöî4Ö ËÍJ#Kré5]s¡æÅÎÆVs ϶ÚÜÆòï¤ÕÛÒ fF\þPË¥Òãoz‚–)O}òöŠm¢¡·ç’É:Œt¶Ò9Â3£°CÇ^YC5ÚËõi×§kz®OuÜMô…èÈ ·ŽÃh­o|-ŸÄì/yÊÿË_ÏOµ©€ÊSÆQ%ï­ºðÈot´Sn{9´• ùWm?Dw-˜¡ô‰¿/1ÁîO(ÃG¶J0«q[[}¾­„>Þ°—2ôåtiÊRÊ3žûë‡A5È´Kl¬ChOóMô×E­t/SqÕÌñb¶eÙf$+¾ØGテ0ùY/€hN bò”FUªÅ,6Lå_]×Äò¯aù'ED~O~Ôyð$j™’rwÖ'ûHoO%SëÞgn/)À ÒY HÏ›]_CõŽÃ=Ö'ŒÚúÇóÔó=’&ð¡÷|5=þ5º¼Éÿц}ÔdH§CiS©Ö˜'³ÛUyÒ@±eZ/Ðèæ==Ê6FTÂ1…@Â>²’¶·[9n§»wöÓòMh€qM±¼GFn9Eš`hÍ5ü…v4߯­g(ÑU3ÆVN‡ñ#1X±y-ú|;¥$›y–Õ4öðDZrg~0¶úñ¢ªU5 ,ÿ.ONùûFÊøÎ5QË””ÛSŸlýÈÐZ̽;þ¡(%0´tÍÓÈf>àµ>­X[O~RíµÇî ²i>S÷—º“ÿ‚iH™Bv]hº´üá †Ö¶´Ë¨¸i‡WùµAáþ¤«î?Æð;Jª¥µ•v:IŸlv=3RÞìÓ· ƒKðÐt-]K”φȤQƒ…ñc0„Vyƒ=‡OÑû«v£'/;­OeGæ0ºÀÇ…jbùw‡Uþ>6HÔ2ÕUŸ”Œcëø>}Ë0¸R$ëÓˆéÍŵt°Ô¯˜Î+¤ßGâ5µ!Où—o.!=ûR§’\ÈžÁÆ7r “Ò'!Ã5ÞŠ|³¤D%…;žžÚº®;E÷<=ÑBà%]_Aï®ØBM-‚_ð*BZ--í´pùfѽOO4ø1ñ¢ªïp|@8ä&Yã…—D-Sžúäƒ5{HgOžžhy·ÂëÄ]no,ßF¿úý Ú°==N}2|ˆ™ž|°0(£G«O¯ÝCMútáé‰ùáuOo¯Ø®ôI´¼”(â#î ¸d1r 1=ÍÍ-´ŽGÔ7[i¢yiŸtou÷îáù™d^BµMm´ŠcÀ/øÿÁ’Ä`åÖýTËÌYé)}Ö½Õ,ðüdóÊÓulô…ZþîòT¿Ž@¢–))·VŸ4´´²Ñ3ºOº·º{ƒðüZGQSk U7Ÿd]ÂAÐlø\=7þV!edæì÷&cs+²Lì“î­îä‡çÏ-  äQU¡"½;ÓCHà­=RÇ™×6æ;X¯Ä²×³7%ÕâÿpÕŠ×kRàCÝC%¯ªüF e p¨(TeJÊz)õI3ÏfàUÑJ[é VºåF# b JŸx“¿¥µÊM£U|Á[{¤”>‰ÚWÔ'ŒæïìVýÏÔYQmÔÜ.º¹ŽðÇ“bžž@hê /QCu)Úð ¥åŒ¢qó~AYES©­¹ŠNx›oú  žô­_x gá7x%ÇÏÑÐùcCÜ‘ 1ÀlÔÅyz¥¯^=ƒ.ð¬Îî—š9~x—dsÐt’IOÓŠ‡ÓßÞ_Õåº/?€?ð*ù}ÉSÝã¡*Sx×~õ*úÏ%uîB ¥ð4×ÏžDCûçq92Ò² »i{É ºëÚYTv®ŠÖî<ä£w‡ªLI¹µú“óažžh%1‡óx²¢‚Æ*JŸt'?æé‰V¼)}­¯§ÏøŠkÃnY› söp Gs!¾ÅLMMN˜]4r]J{WþX¼¬‘3çn(ÿ·)”U0…¦ßò=¼„ÊØ1óê?öV:Sâð4æùÕõ‚ïd³Mtw:±¡Ä ²¶Á9#3¬Šh`¿þ åó,±»(Éh¤’cg]© ÈÏ¢«.Ogxh0žsÑhš8r íá ý%ÌócÐéC&¿¿ù«û{G TeêÒÉ£é40z`ØßÃQ~–®ÛÉÞÙ ®nÞu;ñ$ŸÓ¶’ãÔÄ“_úK¡*SRn­>ÑÛÙPÓä„þÊéí~1Ïó }âM~ÌH®É ½Éãïoà ³F‡B~óV÷G/qÝÕ…µ·¬†O+»dyKÖ×ô6§÷æë@IDAT†OHxuàÝ%Yr¨±æYÛ¨¾ $Miä°[éÄΗiÄô‡Å}üò…‚ÿ#P’ÔòLªú &ì™=yíæ¡ðøð`¯²Š*×ÖŸ¢#e<a£øXmÙwŒp ¤7èC& <¨çºG e ñ\ÓŠ‡Ð¦=¥"£ñ¼~Fõ½ù鍯ä–í<tª¼Š§;h iㆉó@þ…¢LI¹Q/¥>q`)Ѝ'sHê“7ù± E´S‹>4òG»œŠ?߈[ÃnYéšE0Fa´·Ûyí-ÿ[Œ€3=oÕ_ØçBöÈÖi`ñíT<ïç4óÖ·è졨æÜvq½öüJÍÎë]¦Ác{›‡´[…ÇJÊâÊÜÇùœÓëe j)Š‚œ *¯t¯ë#YÈà5rÆ-¤mŽÉŸè\e åf¦ñÍC„z(äw1¤B†@¨ÊT&ÎäåYÎU9ËÔ Â\6¤«izñPB—ê¤QXÂÅ]~Îr™*ä2([¦´rkõ‰Îáæ1PÞÂþ¯l}êN~›.ú; l¬õƒ•?ìïHeQâÖðŠÒ5‹?æ`K( põÆdJN+™@õÙ­T[±›O¸›,éÔÞ×ø’3ý&öae¾”Ì!òv¿÷P4àä”$H/P2ò‹ŠVñHž4mÜP^笙JOU¸.ae{†§J¡’?ÐüÕsÝ#Š2•“‘J:…g”‘’LÃ8¶§€gò®¨®Ë—ÌãE'%¡«"'#¸¹§‚-SRî`õ‰”)’û`e¯‰.$ß—Ê+¼ĭ჊î܈;¯ n ÔØ ¾%­H4ןq½©7¾$ ©Ïÿ>ƒmüšqËë”Ýÿbq½¥¡œ÷¼Â{z×ýþ0÷‚oØ+R¿ÓÐ`¨Ñ‡<3ØèÕ5º5Ĺ‘»&¦ŒBÛœÀ©‹ê;îËdoP  ùÍ[=×=², ;ZüëþÞž®ÀS¨-OÖEg+kéƒÕÛi5ÏcµeßQ3¸Ð•D×x& ¦LiåVŸ#C Ï#;òLtùÅ]=Ä­á¸]••´Vpµ4Â!áõÁÞhΠ¼As Ý]mM•t|Çߨ¦|'޼—ÉœÖÿ먵ñœ8è üu/¥ÁÉç“Ü™k=OüJçV¹–&q³Ñ¨§<7’–Ò:îÃäaSˆä8õ`·„¤Lq ›¶‘2Et¤ì¾]½Œ>¨Ü2³È/“RûØG ® ¼—² ÐèA¶ö&je/NjÖpœ’µµŽê+K¨`øµd0¥PzîX±UÞ ®§f:ƒ0kŽ‹ó@þq=uñÈóÚg€A0ÔÞn£þPåf¦vJfißwô ˜ë7…n PUmS§ûý9 ¥üþä«îõ `ËT2LM1‹aëȱ´¬œ ó2iʘ!”ŸÁ£Ñãn+º¹*y*…`(e*ú$}6²#ïD—?PüÕsÑ…@ôG¦E ^uöSFþ7×ý’‡­ÿ®úönjç‘^º^~äq=3"5V%»µs×ëá<@`3â/$ØO0/^å è–¿c_„×ùÆiŠÞÀ|P𠬜8Ç^Ó3è³ÍûhÞÔ1Â:pü,wws=Z”›EGϸãÈ\âà )Iǃ/`št%£QÇñ‰Þ¯u½[ý¢Pø‚@Ü>Á¶H=Á+ÝòͼåM*Ýú;jk]]K7ÎÏ¢‹§òâ¼&=ÙB|\CÇO9WP¿xZ]57ƒòsMtúl+-ý´–J7Óìi4wV/H‘>ØL£GXèóuµd6ë)7ÛHy¥œ~ùÜiÈ@¼ŽV–`79YO·]ŸMë67ГÿuŠŽl£[ù´i[#Y,zš>¹s·³¸ØË?-ÿ½Üêõ²öù@ä×&jâ9Å™•LÞÚL˜¦,;YG;ÎYéîwëÅvÿ"çÒ Mvz÷@ =>+¸Àvä­å_Ë‹:N<âÖãŽW¹oå¿õšì™ƒ‹[<ÒGëv÷*ÖÞ#§ [°TQUK[´šRRSÉœœÌ“CšÄ¼/Á¤;fÔÐâŸÿþÎþÈ`ŠÑg[8`Ø›—ÎqÐ=eåtZ[Z¨¶©’’›« yb(ûŸßý¼×ì}¾­×{bᆫ/ϤÍÛ¨¡ÓSØÙèqwÝíÚÛä2nx°$Áø9}Ö9÷ØùÊvš<ÎùáÇ$‚k6ÔÑ|NkËÎàbžú³F›ÈÌr.9èŒÌbyOÔØi;?žôÒŽVúüþL•k Ã•¡óúxæ£Î¸öø$ÎkT’*ÑŽÀ€Â$6fœóyò:jx2{vœÝ\0ŒVo¨§îÌ£›¯Ë¦ë¯Î¢ÅËk\œ:ÓFýò°†_`ËϸêÃÑy:ÈFŒ­Ã™›ÅŸ±üÛÿ\J·7S{Ä$k°SM‹ƒÆæªvºÄDíƒCÀ]º‚KG=­P(Ý `âÅ{³2 ï'Mãn«‰ÅZò‰Û¸Ù¸µAt‡]>;*«¬t°ÔGˆ4°ä^^NìËãìá‘ôÅév:Ìr&±è1îÖZqo™5†¼Aƒ³b×Гrª}t »5':ðS\„ ~9ÐxådÊÍɦŒô4ŽkHî´„¿Ù>ûê2ÚµÿðýîWð³˜`©’7¬—€/J¼ùÏ1úF°ÆC.o…÷<òäï&Uüø½×òià„Y‹›y^§ºúzéƒõTYçþ žjü?™•élcV×tîÊ>ÄL_»-—^{·’Nt6Ã@zì»"z@wÝšKOü ˆ~þÛ3<ÎAµu<+âb²Œt®¢«! h¥éiûY7Úê;0Ã@Ÿ²á³`x-9äô‚e¯O~F‘B ¨’ U …€B `¬€²2ÜmÍE&úÎ}ýèƒåÕ´m—;^§xå> OVÕRCƒÞ\\E9lä ”$ÒHO7 OmÛpbè_9wçõKõîÁ)g#§‘‡÷k=<i:*oìè‹!9«Ñ‰€2|ü|/®®Ó»g˜õóñ˜¿ÃÕ±¸dw„ëI&·rïî>õ»B@"åóôÔ±¡Ò/ßY7òx˜ú÷¿^@‡Ž´Òɲ6Æ#¾°¥ððí£'Zyo qc,<´_GS&XxD›Ê8ØÔ¯#Öå| ú㮫¡YpL:éžIÉ4ˆ==ÒõôðÅJå¹6—¹ »¡ì;QoŽY)½ÚGõ…êq“%›æÝ·ž¾Xt»–m4î²ÿ¢ì¢é<ª¥‰ÎÿŒö®|RÌᓞ7–æÜÕy°Ðö¿ÅËXì y÷®£oÝ$†¸÷UT^š0b Í2ZŒ¼é—“N fM ýr¨|=|ªœ>Z·Kƒù!E¹4ÿâ‰<ën·Fuôìk˹kJG?øÊUô÷%ké/<©H! -SXòdîÔ±b-¸šú&Úzà}±÷(—±lºïÆK»€uðÄY:ZVAÓŠ‡ÓßÞ_Õåz´ÿPÆÍŠœ^›)ã-”žf É¼Ç&é ÏÓáõñʺí†1䣻Þ]RåšäpPÿ$ª¸ÐNííîù|¬ìK.Xéú‘b!œ¯f¢Gyx»™@¼ÏS+ië§áSÈ]\Ù¸  ŸXy¿ÑΧ2|zxC#¦?BÕg¶P]Å*}3/Sqˆ£¤äš~ó¿¨ÿ˜¼*ûëdJvα±eѼª³UÖP]Jí-5T¶ÿ =û)Ú¾ô=ä}—àÕÁêØk·ó_äe¦Óy^O CÚS’“èÎkfÑÉ•´ãà Àª;\ÌÇ'éÃu;©š?br ¬ãuÅ´bz{ÅÑ'¤â(¢x–©9´~ç!1æ¤QƒèÊéÅ´§BÀêì¯,]ïâMÏs¾Ü>&=_C»—Ñœ‹Fó’iOi™ëžX8Xöy =ôBZÎ]XŸ®©[w|/û¬–°yÎê Ðås2èƒeÕÝ=¿/=Ô&&,¼µØLïìo¥×óê†<ŒŸéÍaKßžšL+Ž¶Ñ¡öpÅÄKI &»ï³H ¼‰Š…H‡Lº—Žmÿ“¸Œ ÷¯ú)/Eq„ªÏn¡Z^”T®ÆžÄ†–§¨,ÛÀ×¶Š FèØö?S¿aWSZîq+ÿ& @F½öq~\0!á² »y½¤:UÎKtðGhϨ ºdâHñAúdÓ^:{¡ÆeôàÚæ=¥4jpX çŠÏ2õ!{ a8cåõm%ÇEjÿüljk·RYE•kKM6³÷PO;؈ÆC%oy©ëþ#ê2•‘šÌñaãiãž#ÔÐÔ¹e_”—%âÊÿ#©ªÎéíˆt™’r;÷N}âÐÅ€áÅ<†¢>y“ßàp Ë÷m{#ó ù£M.ÅOàĭǕԓÌlLTÛÝ+Œ{^מ[ÒPKÃíOâxÌœŸRVÁÚøöÍä°;[+Õg¾àX w Ë%·/¥ÅwPÅÑOÅ3-õg(9}@—´ºû¡Å‘A9˜ÉKCÞäÑ\öz¨}&•ãrì6߃!3R-tê\×Öé•3Š9¦'‹þ±dh¡ Î"´ž/Ý^rBðQÀУŠ˜ ü¯O¯Éä+ÏäÈïk~ê>ßu™Âˆ®ÛçÏân­jZ½­¤ #3Æåñ=’`tƒ2¹Œú4l™ÒÊ-ù€>!ÓØ—¿EåžyLNJîÄš7y:Ýàqâí~Èo¶w6T=‹ŠSð˜ì±´Š7y¢‚YÅDDˆk'‚“x (µ9:+ÏûpNrja§Kæ~Ž»“¶|pW£HÞŒn.xŒ$%§RkÃ9yÚã¼ÇÔ¤ÐÚ¤)f=a÷ÙëƒK·Äµ4‹cy&B —oq¸†xŒzŽÑ0óZZ’Zù7x‚$¥s:¸Ç‚·|†Z~_òV÷ø‡@°eÊÀ«°#h¹±¥•¯ÚÞ%ó´”d7¬¿í¥½ˆßAõM}[¦ Oº6Þzî®ÑòécðfgÃQŸ ¿‰µ•ѽòƒ7#óù#ý.U~¡C î XörËI5qÌ O„eí=и±ú(%¥æ“1ɈƒgÌœŸPéÏñ‚™i”U8Mlx…#¯£Ì‚É„XŸ‚×RÁðk8Ðy½ë-¥d£Æš£®óžÀx,ÌNuñÝÓý¾\ƒüC ³9]ôq…ëJîæÊÍt¯}ÑèÁoÏ:Žå1³ÂÃcl C'Ëé¢1ƒÅù(öô àÕ’cnoYw›UwtOôÆ/øŸ¡”¿·<Õuÿ¶Láù/_9²ÓShÓîR*ÊËå''Óø;uì^ÈÔ*â{´¢<ªj›´?w{Ê2%u öÐ' ‡¡«g´[f"|AòªúÔE~®«¹íå–Ê÷ìoJŸøX‚ÜZ·B”檤ø<Ùõ•4$'x}Òü…,¿­¾œ2­¨Ö˜×RvŸ%xÊfoÔÐéÓîsRWb ¸îêB« qØŒlõFêŸn¢$öúìn¹‰¬Žž?þG¾x,éƒ(=¯Ø§÷êiô$¥äRÞytpý÷ú0Ÿ?RzMüTÕ6òê̺ÊßkÆê¿ˆd™òd®xX¯©'Ì+Õ…ºLiåÖê7N¬I<Ñ'uöLõÆ_8¯ƒð”ĽPé“îä‡×kTÓÒóìöÑBàýÖÄÝ^z{¥Oñ=Uü1ÃR£òÌ!“ß'&ÕM~!Ð×ej/ÏìüÆòM>ñÊ2å)·VŸ HsCßH¶¤Àô„OÂøy“Í|@ð4*/)$õ©'ù¦ò`{7u P÷“íÝ^ÀÓèÉ2ÆTBQ@´>ðCð¦ëÅáv˜êÞȆ 3OºgfcÂl6SvŠ‘òÍ6:mO[šîèÕóã[nÝOx/y¾‚¬Ô>†ø‡’'ƒò3EWR¤ÁPàÞõüOyÙ !¯O½ÉŸ×~†Æ7néSÏ<=༄]Ÿpøc‡‘ûÕ1•Pxˆ¦A6‡­©¥½çñùaéš…á“ÌO²%™,¼57'S¿æio£ÓíãxXlM2/í1æÇÉYhÿ#¦Ý[ð>¦8hP¶Eð>Á/ø†·rJÞ0Ý?‹8l§ãÕmdåYp³y”Lo1?æßÓsˆ¿@÷<=á’_æßÒÖî°Z­Íò\³w•;Ío±~ØE&«ÍÚŠ:`µLy“ÛSŸTRòj$CëèˆÇü ¦Gt¹±÷)õÉù‰ Ž© õtØ2)â1?ˆéA÷<=á_« O¸k¿Iûw©w×Õi” M† {»­¢¦¶1hÃîY&žc *55…ZÙÓÑÚÚÊÛvÊ·5Šy(*­y´®é›T`,¥<Ú«Àx’tᙘ óô`È:Fo!ÙijªJ±±§‡çîá ÙÒ˜Gð ~Á·ìêrãçAwŒ,ÌàX§:Vm¥rq…e(R-IdáXzDT‡‰0O†c¤ ™ìÜ §üRŒšºFG{kkl¯ì(… `ßÞÒZŠ:…¬µLu'wg}ÒB¼p:µ'ï ½-— ¶ÒÙr؇ÝsÃ,?øG9Byjnj¼À§0¨å†ËñLRNdEX‰Z¦z’»“>Ñ7 }RO¨™#‰K/ÈéÊuèØº“':õO¥öúä³ü­NùõlˆÀߞǑR«>™lºÐÈ¥20#3&PDmF4@Vô)Þ¥Ô'mÍÍçøTÖ1\R#„¦†NXQˆÊËN®ËÈÍ}dOéiºxÂð R‡{AÂè‡']<}ä4z¤Û¾…ŒÜí’ck¥fÖ'Ívƒˆûiá.()=Qs}™ë²%Ýû\N»ÅÁFQ2+«4“ƒ,<]YðìÀ ËÌL§Œ Þ`ü0Ÿà|ƒÿ`©7 Ä0ÿæªo±R·VÛØ´YØÈ†J0$Œ)Ö¬úÙà³G\~”#ž‹HwêðÁ̬]©¬‚”,TÂþ¬VF;d0lÄ¡¨SàñOþ¦ÎòÛÂ#„õ©Ô'eGK·ðûÒÖ3_^Ÿº' ˆ&ÃÇU€V-}oÏÈIUnÙw4‡ Ÿž-^@tµRØË!°Ñ#?äÎJÌ]aFw»´°QÄ~ñ”η0Œ0q_otpÇË®[Îþ‰ëØó| ?©8L¼FQ²Ýo‚§FOVf¦8†1„Q#Áz{$¾b”Äkj1 #ä÷™OOû¾”ŸË‘Ãn³ÕíÙ²±”yĘ[l®òÖß1zM+›²Ï¼êšZÆ"#Ø:LµLù*w úÄ×òÖWõ)ÑåÇû>qØí5[×|†a|ñ®K|-’1u_4>­q¡¨/œ=³p¯N÷†Tc„`H8fv5;RÆ ZMˆŸAwFz5³ÇCôÕó‡ßfwz„ìCžz2€8$ÆEðÔx#"À‡A˜#ö@qžNoÇôp÷<=¢«Ëåí .¨Ù“pbà™—ö¼¯åGùÙ[Z¦;Yz³á±?Ol¢Œñ1Ê[¼’«.±€Bn`ÀïÝŠ:еLõ•ÜÀ¼¯ëS_¾÷h_ê“3'Ž-f~P¯A—ú¸¢h2|d+ɺfñ{ oûîƒw-\¶)û©nÒ'ñÄ{Á” ”‡ó¼c˜;ÇÓ´°áÓ‚ ç‡W÷Åã‘fqs•å}åw™<8è¾rz{œ£Ì`,¡«+%Å"6tqá:ø 5… ƒÞøì+ùÛÚmÄåÇn·ZëÖôÁ'Ì'"°I…%Ë\o"ÄÚu)—¨K2·ƒÁ#F.`L2BQ§J¢•)YúJnäßWõIÊŽ}"Ê/õ çª]»äý÷†DÐ%Ú×7ÇÑdøT´RñQj«ªª¨?¸sÛ¯tSg<óÊ’uŽoÝ6/è./YY…â`ãÇéyI¢‹E="ð¹ûâøÜ$½=ÝõziÖñ¤ü¼\Èà¢gKQaš{ÌøŠn,Ì+„!ëðú sÒB§§<†š¤Â„ܡĠ;>¥2ßHËrsæ| m_¿ê•¦¦zÌØÈ‘b“ +Þ=>¢.I¹°ÐÏ»ú¡PÔ)¼wùn¥LAfP¤åvæ)²våéúä̽3‘zïÑ ¿Ô'»7¯ÿS}½˜ú$t‰öÕÇÅq4>h¥¢0µ¬ýøƒuéÙÙ/óñ·è=rÜwÓ¥º`ï;[Yí¸óšYúPÄü òˆ™Ha„pœ0x8 Ùiô8 åȯž Ÿ MXOnN6³Ù™ ¡(ÄÆç0€œçΠgyoçÂsæä'4ôÄ¡”)Rò£Ý[ðôÙ·{ñÊ÷ß^Æü5òöPX(_(g(oñJ]ê šÌ›yå"ÆÄA°°¿ª:ãµLA¶ž(Rrƒ‡H×§žä–×âY~­>9V²ï­ï¾FC¢éùªãf­†ðøt0ó²7^}sÆóÏÚgÎþþo_]–5aä@ÇŒñÃuGxž(¨´è~‚÷ÁÌ2®ÆŽ4xäÞÛ[çž*!@Ù“¤¢Â^nÈS;Ëß<Ÿ‹Ä9ò=ñŠ<@RNìC-?æÕÀSŒ¶@ ³ÝÚ^¿uÍç wm\‹áëPRõ{k=>‰`øtªK,¿˜Uaeù¹sÓ/»â.®S™¡¨Sœ¶xÏñP¦ ‹?‰º~"QŸü‘[ÞOò{Ó';Ö¯ùÇöu«àéID]"_sÜì£Íð°2Îý§h¡cÊOÖÏ?]·ÿ‹Í%³¯»á&Ž¿™Ï¸L®lŽt‹Ù‘•™ªK6aäàÉÓÈñ<÷ÌaœÁ?¿»Úó²8—ÊJ^ô<—¿GËÞSfÏsùô”×óÜßôäýX‚³cb>æQg³Yë—ì_³qùGŸ777JUË÷c«ã å å ±/ñÜÍÅâ òZ—øŠèOeÃpû¡Û_rÍõW0v—qÊàwò:N<Ëç¹àÖžeÈóܤÂz«§œžçdî)«çy i†ëOy=ÏÉ×S^Ïó@ÒÄ3ÞôɉC%+7.û𓦦†¾%‘uI °FåsšÏvÔð']ôh©BAc8—0jë쟾³ð->ÿpìEÓF5f‚%%-/É’œi4šÌ¬]Cbüpú>Ó„Á4\Þ¼sïÁ£òXíï?ÕÚÚÜ\ÛÜÐPÅ ªäОÇ9WtcÁ«#Æ””ŽÑÍ%ã{âÙÛÃb ê¶.ñUa±ØÎÞŸùü“ѧ 2zìXKZZŽÙbÉ4ŒO›îÌ_ýWÄ ¬OZXŸÔ576Tžž!(ìˆ=œ—'%ì"˜ž@Dè\– =è¾’Þ>0t`ø`el(7¸Žr”h$ëä–˜‹h®K‰öŽ”¼}‹€¬J—ôí{ˆHîÑløÈ‚­=†²Æ‡LŒPá½Öä½§áÓÐyæCQø@yÉr!•º±äÇÆ dœãw×#ŸåŸ†$N±T—æå(Aû©dQº¤O_Gd2fà0¢ j ¥l©ÂÓ£5zd, ž¾0z8[:‡Š"‚Ê„ÜPF´ eÆöØp-==,¶‹b­.¹W 0# õˆ¬#J—„ð¾N>Ú ‰>Z(”ØËÙhe÷ iôD<ƇóÖR¥öD‡”,Òø‘JKžËëλÕ‰G´×%õ¦‘B@é’H!%ùĊḤ5.6Zò2Szy¤§Gî# 3iE©¬°—ʆÜäo‘á&¶r60 £¹.Å¢ŠÛXFõ$uöR`/Ç=ŠâX2|$ܲ¢@‚¤Ñã<ë»n.ä¸E‘CeA’,ò\í{G@bu©wîÕ Ð! tIè°Œú”bÑðñU*oÏßûâ­hE XE šêR¬b¨øV(¢ål*ö …€B@! P€2|‚ÇP¥ P( …@Œ   ŸyQŠM…€B@! P(‚G@>Ác¨RP( …€B FP†OŒ¼(ŦB@! P(Á#  Ÿà1T)( …€B@!#(Ã'F^”bS! P( àP†O𪠅€B@! ˆ”á#/J±©P( …@ð(Ã'x U …€B@! PÄÊð‰‘¥ØT( …€B x”á<†*…€B@! P(beøÄÈ‹Rl* …€B@!<Êð C•‚B@! P(1‚€2|bäE)6 …€B@! eø¡JA! P( A@>1ò¢› …€B@! P€2|‚ÇP¥ P( …@Œ   ŸyQŠM…€B@! P(‚G@>Ác¨RP( …€B FP†OŒ¼(ŦB@! P(Á#  Ÿà1T)( …€B@!#(Ã'F^”bS! P( à0ŸDÜ¥ c‰°IÒËß\{»ÝuˆC§3÷‰Ã}H8Öžk.©C…€B@! P(‰€2|œFŒx¿ô÷=ú“i&³y>›>CH¯+Ò“.­§°®éŒ<ïøöO‡|ÞݽÕ“ÝqÖawœ´ÛlËÿþ›Ÿoã{a6)C¨;ÐÔï …€B@! 1‰løHcÇpùå·¤ ™>áA³Ùôƒô…|Á‘¤k´[ôu:#µúÚ¨Ë3ž˜ÛÝû±’ÙÞ¬Ëp´éSõz£ágßù÷Ÿ³Z¿»pàä>øàå&~NAÝ%¡~W( …€B HÕð1ƒn)Ó]=± =3ãlðå›JƒŒ»¨ÀxP—¤ké®Û*PÈ…ÕæH¦rë:e\PA#Ÿ)œ8ä‡÷Žø·ï¾úÜÿ.ã„m¼uî< 47õœB@! P(]ðÕ›ÑåÁý^aðð>ùÞÇŸz,-3ëÝtÃù~—¦¼D³-¯è™v=aïÿgï:ࣨ¶þ¹3»É¦½Jï¡ H±R,¨OÔçÓ§>)Š tà[%€tgyvñ{‚°€…¦€”Ð ½÷žÝ¹ß9“̲ IHÙÝäÜü&3së¹ÿ¹;÷?çž{/åMePYTf˜r¡²-8tùSc¢G`¡ÖLùHNvŒ#À0Œ#àeÊñ1‡¶ˆ\Øqôp[PHLMë.Ñ-x±ŠÃT^†öúÙQ™ÝB©$ƒÕøúS£' ÇT$=&?ׇc0Œ#À0B ,ª«Azú==¨Ophø¿‘pÈöAŸ ‹p4oF¦²I’Å`›üèá½3å,KÏÆ›r^Œ#À0Œ@®”•ΕêIöLµk7ªP©Zµ9áêYÙÚö¥ÏhUH–på„G”{+*ªsx¦¼eåùäÚ@9€`F€ð&e¡c%rCv=xuêÓgµJTà·jIjz²?D’%Êö"A­Ú²ç­C2å%¹}†œe—™ïF€`C ,·¶NjTž©Œ³·J¦çzƒd"Ù‚ƒƒcÜ@T?Ó¾Çf ¬+2ãªÌ_§§|µöY«+ÙºÈ@½AÇ‘`±†ÂºOî„#ÛÞ–wΆò àÄž/Àj ‡êM(ÔÓ!ÙHFEµÔÆ LOiN…Š1Œ#À0E ´w¨T?²“!¨Z-•mâJ¡T1õÚ¿'‘Ô8R/•nê Çb?€äøÃptû{šx *Ö¾¤î‚£ÛÞ…úí‡ôY¸ã“Œ¸­E•L¹ÙÎÇ _0Œ#À0EC 4"8tÆÇ ?ª¢[…£PÄ'¬b3H¼°Ë@[jNƒàXiÖ9¢XÁb †Ð Œû+çcQûS•ì“ îHFE(´9*ÉmuJî‚—Î)F€`Ò‹u¬¥Ù±#âI~pôB8Åb[hUC»CÉuÍç¬Æ·¼ !B…êQ  :j{È¥ ˆ8Wp¹:ti¿áWð†¬¦ì¥™ NÁ0Œ#ÀÒÜ¡É1ƒ@Hiܪ ÐjFÎ2]ìêQphË[QŽÇ~É—ƒ#錜–tÏlaÕÍè>Ké&>f EÚ \0'`F€`J1¥™øÐcó$ …®kZ2Üà µ>¦ÓÉptÇû°õûpáÄZ«Ø.ŸÙb†VƳ€ôä "d¦)ÄÙSþB$ç$Œ#À0Œ#à‰@¡É€g&>~]dò 9S µ8!õÜUµ„¶=¡‘¡õÝo¢ýÏ^¸tr£R®®qNŽ?âŽ_È SöB&çdŒ#À0Œ#à‰@i·ññ¬+^ÎÆ‡2I¸°Â+µpçW¿ãp¨Ûzæçü±5°kÍxwX¹J-qèë讂O›wgÂŒ#À0Œ#àuJ3ñ!m‰×\ܦ9Сßç·y8’ÏÃþu¯Ãa´ñq¤\ÌR­ä\§ÍØ·nr/Üxµ>^‡³`F€`üÒ>Ôe™çB? +g¶Á©ýË¡VÓG<¤î¼†ôP@Õ}™\§÷Uè²2 ÈbŸÄ¤§hhrjF€`FÀ@ 4k|¼þˆw­{ÝÙthhœ"£ý1rsLŠrC†ýF€`Ê$L|òxìõÛ¿—Om‚„s±P­Ñýxq?’±`«íïÿª7ÞÇw~âΡ\•(¨Ù´¿ûž:<±û3hÔùeøëÛgÜþ½øÏ×ÉÉÿù¶bºÍÙÓ®y³Rhzr|#ô 2Ĥ(;D|Ï0Œ#À¸`âã†"ë…%0êD=›–?nЂ„梄Ép®œÝå«wt¡X¡åí³àȶÅHtƹ3;ü×ÛÐãé?€öôJº¸Ïíï­‹žCÎ'a^eY²eR”¾aF€`x¨+·6ZG•Ì$œßuMK`9Ü·«9œ‰ûÖVÿæ¡Îý% ñIK: δx¯Ø¬Xˆ[€.òCŠ@è t ½9|†¢ÄáîôhöZçð`Ø‹`F Ä(íšÿä>\š+Õ)èþº[@GÔ5¶¤p9¯y8-oŸiÉgàø®ÏŒ°°ŠM¡vË'aÝgwC@Pä5ñ‰GÜtnN(5]KÆp·ì¹Å-¬q“"=§Ùg(/“¢Â>4NÇ0Œ#PdJ3ñ!ÒÅ9ÒÒãÓd¹|Ÿ Ð–t*KzºiÜe(¸\]8û¡;N^$ ,ŽŒÛ„ñLÙóJâ3a…!E¤1 TÉ£î)ùp\Ÿ¨–¸ÛF¾|œW´ÎUbF Œ"PÚ‰Iè¬ïܼñ`‡Ûî¸rÜÚ*‰Ožv>I—㌡¬ðJ-àÒÉP;Ꟙ…€¦Ý^ÍÒTVί ®ô+n?—3Ã&È•ž`,bh ­†v? ñünwœ¼.Ž»ZIÝåˆßòûjšæ)^É|>ìF’¢4íêŠÖLM“"Ÿo*, #À0ÅŠ@i&>ž¤ACãø¡ƒ?ª›ý킵T´Í\"3G¶¿ õÚ1ˆÏúÏzç×3€‘!ÓÕk7Î\‰kí5½r=_pÕsÎâÔ±¸o0’)³g=rMëÏLŠüùé±ìŒ#Àø¥™øÐ“ Í;MáÂk׺ï¿ù©V½zwîH»7¬[È"Å"rßzâàŸoÀ-ý¿šµ•xaåW  ët‡Í_=qÝt.i”IבqýþÍrš'Oòš²—ÙMH¯GŠRÓâ*ºÞ¦ä›†Ö…>35EßM©ø8%ß´)bMÑu›0G`FÀ/(ÍÄÇÔ”y á #%%)õ¯µ¿|sÏ;oK{P¶ú<×!/šÊþûG=0Yáœ#å"üöÁ­ùJL²$è•aûÆ_þ“”t…¦²“¼&ù1ë’¯¼ÊJ¤LRD+ZÓ‘ÅÑâ…%E˜QS$D hþ:í(JGК¯þïg\bO@‹Ö}ôŠ2*ð[%/›LãuG6=4¼Ešžƒ»w,[½ì‹ï±SF’—ä&ù™ø ÞrÅMа]¢Ò”üìš"&EÞzŠœ#P " ku²Ö̧ªTdâƒ/rZå/cn·OU͆z "dÈC„‚†lxâaýå«ÿ­¹xæôùöÝzþm­ö¯ðÊÖ8Y˲]T±ì»î:?˜¾PŽÖé¡)ë4{‹ ™5gZâ–ßøtû†ßi¿-’öÞ¢3ÉKr“ü=)^°+^ LŠ25F(U•<$ À0CS”R¤*JœUXð”ü<å F t"@J„k6£.U-¹Zåjß’_‘NˆBNRC,Žw9¿én`<Ë¢Ž'rxDâQòx„ãl³wº«O—ºwµØÂ ’¥M\Vá(2FXÐV´j4-Nˆ,F¸i‰Göïÿmãßÿšššœ€Qè ühÅæ‹xÐyÓÖ‡È;F ‹M‘9|–?R”W­HãgZ›š"&EyÁÅaŒ€ÿ"ðÜÔ©åµd׈˜¸xrôÿ­‰ïK^dª¨«\ºk²ž¢÷Åê~äƒU¦a"²ó!»"V<ˆ )xÓÓÒR´_¾þ¿Õ¿üÚ y«Zu5nf±‘v¨ÈÎår¦§§\HLM:|ùè½ûìÜ~ 3¥Ž4;¤å!¢C ’“ä%¹y˜ Aðuw£4E¸‡ª3Ö)2gŸ1)òõÖÁò1×G€úP´3T,ا^?6Ç( ^Ñf ˆžt\€Ø¹8&:‹ÝEâÂ¥%’C„‡†¹Bñ MO¤¢1UÒQÅ!2Hñéð >˜9®"c1Æ";"9d'E¤'">D„(Œâ0ñAJ«cMQi}²\/F ` ŒŽYCá-–ÄL¬U°”» x¥c0!föë3ÝMŽþ­ BÜ ø¤å1É"…ÁÊĀΤñ1‰ÙöÁ¡Nä‡ÎtOþ¦]™½Ø1ùG€IQþ±â˜Œ@I" ±Ó=ùs|Ù?ˆ]fŸÅ1(IyÊJÙ…éÌsÅfàĘ—¤.ç ½Ï`­ô”j~H~Òü˜C_¤"²cžMÒCá„yàe¡ó âãI~H³Cd‡ÎtPÛô ìŠì¤[flž†ÆK¬RÈR© Óì3CS„«cÅ)B9ÀSò ‰&'+¦œçßGíÃBÃOŠž[&*î•ô*ñ¡úd˜‰ìu'ªË‡ù°ÍÕÝ$@Dr< Iz¼mãCÈ$?&2ï‰ðP8;F D`RT"°s¡e ›˜Uo.ŤçÆ6¯Ÿ†½ð´§æÕÆ!¯•x|¬+ßùè:?¦F‡HŽy˜~&>晪WPg’SëCg"9æaú4_ŽÏÜ0r%E5FªR5EÒb‰ëõÒËÇ„°³ö³Àr2ßC€Öéɘ².Ç!®^¨!=†Ràá­ÿ¬ŠÒ¡ç)-<Ÿtž|7|_Œ5ñ!£Ñº8+…<……’1¯Ï:®sˆjÈ"c„Cî Q¥éΛ/Ò€€J9Ëéà0q!(T¹".+ñA6<D* #Øé èizxjD¤&kR“õ ©I²b*nï‚×Ó=~¢Λ07|ù‡âèGuìªÐ:=h§°à£´àïÂIã¶Üp!¸À,Üâ“¥D¾aF á3!/5Ô@GãjÙ5ÆÖE>p_lhJ¾ihm ŸÕ¸÷¸èÿÙÔ±c2ŒŸ2üð¹êŒ€¯"À¤ÈWŸ ËÅø?L|üÿr 2…€')Ò…l thhLÉ÷’¦H‚ˆ#MÉgMQ™jZ\Ù2‚Ÿ2ò ¹šŒ@Y@€IQYxÊ\GF h0ñ)~œš`üœHN¶@»"ÔeJ~¦M‘©)’Ч¨–½Ø¦ÈOZ‹YÖ`âSÖž8×—`®A€IÑ5°#Pj`âSj-WŒ`¼“"o Èy0¾ƒßy, #ÀøLŠü챸Œ"Àć›#À0Å€“¢b•³d¼€/€ÈY0Œ#PLRäRô¸oSCš’ïECë84´>€/÷eÝкÙì]÷K!þÜ3¼Ùé‚<Ž[º`âSºŸ/׎`ü &EÞ{`gľ%@îÞ;:j™«]JåÓÙ±SBeàÌt‹CEÿ*;_j¹Ã çséG€‰OéÆ\CF€(%\CŠp›c»ïLÉ74EŠ„8]U”†)ùgìx7Èî¾wTÔSfh:k瓺”öjyk“Y;I]¼eQ•Æ»F43ã4›¹«7jÍvïÕìh‹¹±Uœ`Ø÷R“}fxY9ÛíR9çœ\C·ŠrºSF€*ÊI]O·@ÀqÍq|±}PŠ?bañG¡YfF€`Ê"w>›ŒõÞ–yd ¤(×1j‚›i6Á°A§\5ÜA ïÿŸã»é‚ö>óKR$TëfÐ#L Ú-’Ö¤ÄX»Ô' ?)pÕoØ iú(<6ãéR¡ e<Þuª1x@IDATi²ŸŽ¶x=È /Íç!S¦D:“õ{„½O¹&Ý0œØ:Èig8œç`Є×.ëB¬Ãöñ=„Z?YüòËW2"úöÖøøöóaéF€(2¹‘"ahŒ Z! pàô˜kH‘EZâîªsï1_Øöá¥R=ºórHxdÕ-ƒª§4¹óyÔõÙ;ªå=Tç&3w|%…2¤þ™Õ"¢pÈël¦ÿak0´}>êr“™±3¥„³ûF·œ‘§¨q•"eÒ~ûFEUñ ï±FZÎïÞ]±R³f~é)\ža¾xMÚ“®)c‹ (_H°’ WT*‘êQPRÀ"ÒÁ éHŒ-ª‡Cª, ze8ël¬§Ë`5k¨ýQ¦@­ŠÓäôÅ:š21ñ1‘à3#À0eÒNŠÐÎg­Å"ÆÚ‚+lMN¸pÀb½M›$5»,¸Íé’#„ÚÞ‘-Ç7_º+ÀuT;±otTejDް£|oϨ¨åžÍƒ†À\.ù“e!êÉžÃá´–fx“Ù{ZJÍõ9jMÎ#iª§Xàé=ã~2Ã}íŒöbPôä/>TͺG6 \-ƒæKÔKZ-8èè"O:›!Tò»;[5¹¿ÿþÚà˜˜2]ôE Z#FuP¡ޓɓ‘$CÒü—uÝ’Iã6å«/FbâãE09+F€`J¥5;;vª BCçÖhèü8=#ÃÈyÖγhëS©éœÝÕt͵-" ¨aŠ#µ:ª+ÞG"Ó‰â9R}dÏèf;éÞÓÑÐYJâÎ8<ø<æó€†di1‰Ô·XþH(ïÙ²’+c¬¨Éœ·€sˆ­{Fµ|ÎLw#Î/DOªëje¡ëBQ4‹ªvhοI]Îk¸ ®+´‡aGZ_ä4bz¢ ef…4-X½¤XEºÐd€ŽÚ"=M5Lm’ƒ •Å!‘Áó猑Zè ‰OÀ⨌#À0)’Ê¥ºÔSò3 ­‹:|†Ú€ÃXš’oZeø¬ñÌ )nÅÞ¸—U•·Å:D5h>oWm—Cÿ ‡°ÚÐ}ã™±KP#±_ØDæQô"ƒÅ&UPÊGnQ+ÇN™†ÏPËq˜1F>s÷6§s¦oF÷ä0ï?¬*<µsxË=d\xý‰ÑV:뵌XÅóéÒ¥ê;âú ÐþRvG\ËåTREËQ½kð;Xý¢¹“^Ò’õ j’ Õ³nT«Z÷B˜rášLÓe“5„cζÚí& Æ%ÄðE“£?¿&²—=ظÙË€rvŒ#À”2 ­·c]éÈâŠ@ŠpX¤1f†ÇUCkªGÈÐúûé¢à¤È°YºK°S}Ï$=$¬táæ´B0Â2CJçÏ ”Ù¨…0fx}ñÆîš˜îBn¤‡ÒñC²¶ÇÌ4g$YYÔ&¨apéºb£8Á¡Í?Ý2H|ÐdÆŽÅX–»|wz/^ |erÏŸ¶ïýÖ¡^ HÔ„¨å-§ XÄ£ˆ"¬B’^íu" ¦5¶È¤‡Doa[¥:ô ¨° É RÈ\\ H†ÚÛèP/ºjÁÎô>•/k5>=¹{uK×ìöþhA]<މOñàʹ2Œ#PfÈ/)ºÄéø¸x#à"Ž×7´Î)Âá•8$hJ¾¡)ª>1®éñW¦©6e‘çÑAk€COÁ!ÿ½#›îÇa­?‘n ¡N ?"G˜—;ùew˜.>)¾6ý1~-Ckæ=‘ÌUQUí ]#éqÒÉH#\DhIƵ÷ÿŠž4E×äË!Êe½…m%TµìSq¨ïš‚*]ãS4j–«0¿9EZŽC7ËbuOú°?½ÛàSÎý5íö5Úí=]ùÍ£ ñ˜ø-ŽË0Œ#P$n)2ºwœŽojŠf‹ÿ; ÉЦ)nRô«˜·u­þÌJÏʨŠ2 U3±c4ˆ ‘# Ï[+#eƒÀ›;c Eœ3óm=çpDš+)bÇK-‹á¦/’!!XÕPwº«!E¿8!f®U4ì&ëŸe[¡*¾?© 5Pšþ!â’Øšvÿ½']kßA$þYt4®ÍÉ);F€`FÀ·ð>skŠt`'†£ÂOÉ7mŠLMQ¢Rù¨Uê{ûÝÔýH³“¯VFMMðÎa-æ„M—ßq4ö"N/o.Ó¢‰7 g…4ǸЧ»÷7óh·èTpRâÅ£dXmúyë»G4ßeHÓÝqaÅÅhØ|‹éç3M×Rå¾J–#AƒÞõu®ÚÊO9mû¾I—ã`ÿú©FôÀÊЦÏ"« «ßmç΢rÝ; åí³ÀT’ãÃÖïž…¤K VóÇ vÔÓ°îÓ»1nÁe@lø-y ¯Õ8WÝÚµ¶·‡¼x¨Ëýù‚`FÀ(ððYþ4EYlŠŒî[âðnCkY gf¡MÑ4åy7)ªüʱ§í]5‡þ öùí@µÜ³{DS7é1ðÕ]8îª}‘·0×ÓäHEÈ 6¶ÿšô”¯Ö"ku…«GbETm‹¤g1h®¬“Ú‹ ¢îšŽý‡¶,€fÝc å³aÃÒ{áÄž/ ~‡¡z“àÔÞ/ \=öj¸FÝòj§õu}1ƒ¯ œI ˜øä1Œ#Àø7%AŠfMÙéHŠ4¸UN·TóÔ© Žë ?ó&²c¦M ‹OLTÓº] V ;êBe_¯ý p‰‹#õ’‘>¬b8²u1^ ¨Ûf ;ÏÈšÁ»›Žäó¨z:<¸‚ÂjBjâ 8ºí]¨ß~h¡ˆRY=@+Ek.=ˆÔFL|ÜÈó#À0Œ#PHn4)Zqäÿœ8%ÿè-MMQ·¦¨ˆÛ|Ä'j·£)¸Žes!ÑÈHV±œ;tÕ6èøÎOŒ€úí†dÉ×Z÷sK7H¤\9b„ÛÂjÄçÊùXh\~(j Ï,À?¦ SÏ᜴ڴŸšWk|¼ 'gÆ0Œ#P’Øí‡mN=:v—÷‹"G~HnnÚ@Á©ø8 F‹8^×Чµ[Q&\£(c"Ïá3“aœy& î‘"©ßn­‚zgÉÎÑð²×¹ž³”Ã%‹®¹2¯­¶ŒõSŒ<—«ƒv?û¯—]ŽáºDŠ"qƒ0/;&>^”³cF€(9ìöºiCG¬ñâø+æMéäÞpjøØ?›»Î^óçt™5lÔºJ@àŹSÚŸ.Œ¤ÅMŠp]#üC—iS”ORT?T\À¡s_4ðzu ­fDIM.‰z%‚!ÇuTVaŸÂ"ÇéF€`|Í8³Š¦}o èt:_WñÝ#Ÿ˜çJMÇ 3áf3œÎÃF®üƬ®o×HŽ‚-a'_=ê²gœë]„é¸h#JC‹$æ9%ßSS”)ú)ñBU›’XhmÕ+-9ƒ'’Ö'5áXžUMI8n c†Vt$9Á7ñMÒZï¤'ŸÉ3ŸÜ9ÛàÚÒWm¡¨<&>¹¡ÎþŒ#À0~‰‰MHÚ£ðñ6b}gŠjQ!¼ÜC™ªçdô¿ýÙ&¿—ì[#\ )/ã¥A|ˆ%CòT¼7ÈÅ)ª+NR¨$á‚EQs¦$&$¢\>õgžy]:±)¡^Ûçáð_ á¦Ö!ñün7a )W×HŸ$Ï|r LÔ+®´»uEÀ/‹&ßSœ¢ø1ñ) zœ–`FÀç@â³ÇHž5C-É܉Ün·7w Ð…„S¨¯ë Å8ñ)Éh£ã9Å\6zŽ++¿4jÝ-.MÎP¶.˜Óù9³:Û훃“’ÒCgÎìâ^½Ù3<·ëB‘" àU§ßþ vCx¥×/‘n[õ<´ºk>Ôiõ47…­+¹Ó•«Ô’/£·+Íí—Ÿ ܯ 6¤<©é ^°<‘Ÿ4Ãħ ˆq|F€`ŸF ЛS\b! 9tôº»¤KVŒ ïô!Ý_L<[O§*†ßò¿ ë§¼4zCÛ¹3nùË¥)h œ±=E¦t¥°°ÎÇ)§:bý“.]þW|ÞD»«g t1!ÈTÂÐë’CÂîñ*{iüæjj:8gÍjívåže»Î)ÒÁò|Š^n`jKBsØ =[–¹ÞÆmšú}q›ç¹glQäƒ[Þ4Ï„¤õYón{cZ»Ë‘èRÔ¨Ófì[7Ùí—ߋش>Ò!ƒpÆ¿Øáp9®å7ƒ|ÄóÊn¬ù(‡£0Œ#À07éÓ»Ö¹hà\ EbUL°Û3¬~…ÐpWv8H÷¨á™‰3³ÆBézC\üÏÐð9BtÄLã)t‹ÆŸ.˜Ý¥jŒA¹º»:¬ú¸éË!áí"û´EÛ?“ “(í˜1kÃÐàz³––þ¡C¦}ƒ×ÿ5dó̸×DŠztn?mmnG2–~Àѵ9e$»rfœÚ¿j5}4ßùx’JTµA_àùôþ‚/¿ssÐç¢~àz°Èôîh½}ð+1ä[|Fdâ“O 8#À0Œ€ÿ €ËÀlÒS]8ö"µ7fv¹Ú뢡"…Ap"ëý‡Än5H‹€ú¸b°±9)‘#ó:{ jïÌôk„SÙøÆ½ E|@"LŠÕ6ÉS C"–á–ÛpŠýógw½Ë<§§jÿ0Òyéßâ NcV Ž:ÚÉSÎfEÊu×ê±ppóüBçqjß2ØüU᪤$@ËÀ•p{èjyõd®ËÏÙcÈ^ËkމנäŒF€`|Í8\ò²ª¨ã…Ž“1Œ#À0ždÚíd±Ý±*!kt-¹(:.(]ïâJ— âµõƒ1í6ÏôE¾ÖcDšÖÔ¶Z-l^嫵‡ÈZ]açêÑFÕo†ƒ›æÁù#¿@¦ƒÆÇÁ©}Ë!=å\9·vÿ:¹–ZÜ>aØÆ/î‡{¾€ú^„êM€S{¿,¬(Fº“ZKgÀôé¡L™øé‘pbF€`F w2‡Ðh㫼7¿Ê=‹|…Øík,§œkï¯𗪂3_irŠT¯ý p‰‹#õ’¼óç D7Çv~ ;‚ˆªmàÜ¡ ™‘ *õ{·RwÁÑmïBýöC‹D|ôÊpÎYmÏ…WgÀ±q³ùÔøÌ0Œ#Àø)çaC=T²”SŒE« ]‹°ŠÍ ñ®ÓGÖè‚3ñ†¦‡"¨Ö`¨X»;Ô¿yToüüs®;Ý•ó±Æp˜¢ºý z±'ý\I&[‚ ¿šb…²Æ'PØ‹`F€ðO oº«XlhËS’ã_Su²ñiÚ͇þZdìàN‚ÃkCÛ¾KиÙg®„ø3Wí¶SŒ<—«cl_qM†×ñ8äì§M„"”É4Eÿ:Ñ ÌŸÁÅ‘F€`ßCÀY-ò0O»®hU -\’r©‰YµF–€Phwïáòé-p`à wþ‰÷ Á¯ÿíjøuzx9®™a^”–tý$ØÂŒÍãÝiòsqI« ±©}PÛ›oj83?i ‡‰OAÐ⸌#À0Œ€"°xÐ 'H±ìˆ³½¦AᶪHK&²†ÖǬ" Uµ»÷=´ù¹;~ŠÞ×ÎÒJM8†vAK! ¨„UÊØ',0´2ÆÅ]S“ 6 =]†ÀŸ©iÈ .còGû÷ﯙ²xëÌÄÇ[Hr>Œ#À0Œ@ "€û’MsMlO»÷Zv’¹4gŠ1ŒQψMÚ›Ö½ßÆ!­:pxËB¯Ô ›Û¶;jÞå«wK@„UlŠÓן2ÖòI‰?b¤ )W×8'gÞ7×ù—¤GÂo)ƒ]æaé³8fâÁë$)T0Ûø 6NÄ0Œ#Àø 'Û2(zòøcŽÖ¯Š$høCL¸° N #]PxM¨\÷vãº}¿Ýy9ðœ=´tŽ$¨.¸IpñøzØúݳxhÄ+‡$)ùò!œêžæN—×ÅQg[ÞÒtaÑw‘}|±Í‚+¼T^5à0F€`F€(DO~§_ ­jÝ+ÛÚ– sÛ‡üS®jkèÐïsøõîàH>Ý$ª5ÈXÇGÊ«#RŠÝž\ ûÖM†Óû¿Ê5ÜK θÃGWíŠVME¥_‹å©·íãŽäšÈ L|¼"gÁ0Œ#Àøƒ&Æ –ºœ¯‚C4±­Qo²n‹päKÄæ·Mƒ+W.Ãg¿ŸÓu©Ên!K ´ "Mm¯Þä!ØüÕ?Üås¶‚íi÷ë¡ÊE°ŠÅ%ƒô½ àÎë .Px@ê”…¯ÿ/®ÙS¨a:wAù¸`â“8 #À0Œ#ào ±Oiäpj´C}U85Ü»K­jÙÕ£H>R¯©ŽSÚà‚VN¸ZÉÓΦț„ #t þ(]aå»&ùy-U8Sµv!ñGv“€ç­¸…ÇÏ‹^ÿË <¦üL|L$øÌ0Œ#À”BÛc:h.(@ ·Í¦­äE²$®Uu(ºn•i2LOÕà »_ä9Ü'â=¤ÌƒTíG‹LoÒ)äc5 SAÝWØšú€–"+hªUt_hÞXÐ<¼Ÿ‰·åüF€`DÀn_pF‹»w‘¿5/8uKÖRI!S‘ àÊÜy~ÓmQ7§‘?gŸZOw9@íO½Z±PÛºERàªMOöª&ê•pñÁ¦pÊÕ\ת)H¤Ž ÅòØÛ“ƯË·$î™ø”ê\&#À0Œ#à' µ¿žæº2^‘0¸KÈ;å25?rSê£B— à08e  VGOÑÊ#%RÑnhsÑ ¨aú¸º¥æ{vûÓù›Þu0¹!Ä繉SÛiºFûÓ×AfY »uã"F€`EmYQ«BK!ÅuvVÑ”ó¨…/]ºT :òØEê”ÍO)#6¦ºÂ‚¥A¸xb2h«sH‚8¨ZV.°ÍºtÊöfÒb#>vû{¶“Γ/àP⋸ÐcMbÌY$>§°Ð$oV‚óbF€`|$>¡H|ªc_XEJ!pûÂy5¬5ø’$?Ø­yï&[ʹ£†U4öéÎÞ£ÿm®ç'­/Å)â30zÒ]XÉ%øk#û[‰ÇÇJ°òÝÂqã.ûRåYF€`FàF ðÜÔ©åõ½¯”òqceÝ¢ù¼—׉ÏÀ‰1/IfâØÞNd¹ÃMŽþÍçQ`F€`„À  1ÝPM2‹k.µxRôÜTt‘ŠY9ÍÒSm5e‚äa-Ÿ[‹”a %V¼Yné‘s/ÁR¹3“o¢Ëy1Œ#À”¨oÖJ·P_‰‹åÌ¡¾Ó?ê%kšr¢]ÏqóÚßÎ^Û«‹†· Mˆ/ÅLxäF.Fäo ³¼Œ#À0eÅöA)8äõî­…»KÀLìCwûú°—.d-÷æìd«ä§Î+2dÆú/¡á-°VzŠIŸ¶›`Fà†!`ô•Øgb»ðX’Ù—Þ°ò Q[ãƒKø­ÆÇ+ćfo†ÌhÓC,¶`rF€`F Ì!@}&vÄC©5fBû4¨ñÉtHÚʶƇ¦¬#+Ù¦Çl|fF€`ò‡€aóƒ}¨±üKþ’”T¬ÚfÁº®”]-N˜±NøØ„ÏŒ#À0Œ#Pyð1õ¥FŸšÿd7,¦”v\¸š†‡4¯ýí\ä¡.Z‘™'¤uzü­ò,/#À0Œ#à PJ}iæ.¾ R~˜ùz$fA™žgo}þŠß®ËWd⃠â,/N˜¥ð #À0Œ#oŒ>ûRLP'߉n`D]⮣¦°Û¼ôÇs‘§³ÓÞ[XqŸÜÃËÌ0Œ#P6 -°æÔ§úœÃ©÷ÄGìÁÝ}NÆü Td⃠0ц£¼÷V~çxŒ#À0Œ@`J}©onâ- ™ÉuPN$>þë¼1Ô念gÉF€`2Ž@“Y;j2+¶³]Ê<8ÁUÐ&>e¼Ípõ¿B`À„˜'NˆÙäOB˜8ù\ÙöO’™eeü)Ÿ)ßþtVì±&3bßn>{W¯æKwxÊ[î¡.üšøy¨Ë¾fü Ñ1?â¸õªªôzûµ «²ËþâäÉURRô3Š*î[ôZô7ÙÃóso·KÅn¸¡ï8œ6[ëݦ  ž8¥ Î8Y‹»®ÃMoÅ|®äGRµÕàOŽnWÐüóŒ/¡ †Xæ<óä@F€È@@Q>‘šlbµÊ7]š¸Ï¥kÃà(|ÐdfìÏ8‡}ÙSc¶@êÊŠŒ¿ï+wMñk»Þ<ÔZÜ"²€®É·G͘âíÚŠžô¯“®É‹½o‰ç'¡Ë ‰1Ï—¸,#ÀxòËÑX¹îΗZžÝ;²Å’}£¢ú@…€ú8½~¹.ài ‡xä×Úªk|<ž&_–EÄW8“¢wâ•´ÉX{¯îŒêû0Ïó¥UK¥SŸŸóõ[“£ývõÖR÷\¸BŒ@!Ø2¨:m5õ„gò}ÿj’ˆ÷ŸÓ1{†2·Ô0jz·dŸÑ5zôŸUÓuG?\Ú&X†«ÿ™oï”Û7ÿ3ññÍçÂRÝ !èRLFòóï§|ºxÒø?®Wô@û¢`pžŸ†ï»Pí[u¿ÛU¡D/œ4þwJ;pòäj"—à‹¢†Ç£mJò—ºò.Iÿ ŽöÜ…yÀ+1¯(Š˜±hÒ„(.¹ÁÑ“zhR̳ ë£oÅŒ5¾²LœÒ]èúD,; órbþ›-𲞑eˆž´_B£+8t'¼‚ñ{ z÷u\?ÆŒãyÆøõ‘Ì|‚~ß-™ýšgXök«Å2ÁéÔzºbØ=ÙÃsºÏ¯Ü”vàÄÉÏJ©Fù!V{±Ž‹P~GŽùFOº•ïpЭ Æ9ñ¿©n©9Þn:-§øìÇ0WˆšWÙ)R†âo§gñ´1"VnQ+õj,zwÉŽæ½"Ë;’†ó/&¬ÿ„ò€n…+ÚŸ/ŽY{ÿ¼é]÷QVÃ_ÝVn®½M¼™GIŸy¨«¤Ÿ—_¢è ‚EíJSñG¿tý?vûÒ,}Ù…¸h‘U:ϯCÿÇAo±£‰S<ƒ]º¾fà+“{R|«||€ö¼Ý¦%†ÜÛæ;ô«…GŠçv<Žù4ÃÄãn?¼@à>Hnj÷ˆª»ŸüOˆy\Hm5’´ËøÃ}hìô#\àØ£k_q¬ññ•'Ár”dÐâAƒœÏÙcžÕ\rÝIçþ—Q×rFž8?;ÚÖ«Òæ-û„m™ñÞs‰Ó4¼ïðæøññ¼µ(¯!¡8ôö¤ KÍüÐo ¾$z˜÷ÃgÏJ¾”|7vàï¢ß?Ÿ·¿ú–}HƺXRôDÒ²ºÿþÙ %ħÏÀü>_í&vûšO¹Ö®Õu}.¾h:y£Æi‚ª 4ÜŽ¾ÆpÛ,ÿ¹©SËkÉ®UXþÙrØG÷w˜a¹.Gj¬>=éqüœ;Ð>ó‡ÅöQrŠ_¹¾þz9™ä‡ù|€u|ÖÌï%ûœ/’ÉÇŒ·h¦çÐ7ÞL?—0Iç·Kb&öËô~íª6#ÒgQÌøïÍ<øÌ0Y8s!ùNüMý´wt‹‘!³›ÎÙ]Mj®)ɉ;iBG¯ŸæØê¥;Ò+eƹÜgDÚyõ×(¤¬ïf fvY=läº-/ŽZÿôûPjþ>Å×f¸/œñ#‰#P¦0~ íÑ%(o¢–d û”f¹!‚C*‘6ƒôQæÚQ‡œ[ZòÇÎ$QÃí³+Ð}Ò¥´;ñVËd,ÛêÔ/ßMþDð¾-ŧûÄDGgÔ°TÃŽ~Ý›ÎnïéÂüÞD2ÖáùW_¯kúÓeú9§ÙjF¾­HV-YûIµ÷9zt²gúÜ®˜ ‹õ9$fAàJ“[Ü‚È-’ô.X¿U¨‹=ó›k•ùÖÓÏq!±+œZ¨zÏÓ_)ô°J˜;F€ÈüèÃQ~ž=žáÍNïõ4ú6Ÿµ³•Óé¼:Ìð§çÇ¥Å÷Ì.Ì©²g>2 —’#qÌ‚eô³‚øÊ3¼¤¯™ø”ôàò}«%b<~Çœ‘. ‡¼r^È ÉG=ŒSµ»=ü‘÷C ‹¢]J®•W…TUý‰ºådWZ7Ї{öC ІÅö—áí K2ˆ‘âêFù)ªJÃ7˜DÖ§“j Œ£³§вŸî5—«§?'S#•ÅÛ¸APc²åî,„2vŽ}Ä¥k#åíC2#ãQΠ¦¥ÅÎþR,ˆÜ´:F‰ªõ@ö’Q ŸÅˆíœêQTÍOÏòRõŸi£G”)#¯ìñ=#À<2ªÅ üýVÁ)ë5·»¡',=Ö a‘`Õ@MÅwž›øà<Ë0¥ycvçŸçÏéã™~þÔ¶ç1ï¯/&®§¡ó„9s:Ÿô /ékê*é'Àåû 4Ä4à•ɃAÓ¿?튢Z>2,m<$Dþd+›Ð˜ùcob0è¸l"ÏY\oÙÇÅ¡ËZ$W_ŸrÅÜ«à )#/)¾F }%)èß¿¤-´;äYŽt9=ïkU:É ín²h›„äªÁÁ—Y9LÛIÂV]êóÆL›öûô±ciG\5Kô›'“ÿŽÅ¿Ãt-\ÎË×ʇ9æOnÅFxë6-=»R‡d”ÕítÏß̈×h?uM=5‡Ý‘ù‚`®AÀ.„ŽçÎ3[bGI‡öS“™;Náj þ¬ŽÙû¾?.íÙt?Lnâƒï¤,ÄgÔ¨u•Ó% EO-Eà«RlTV"ÑI•á–™ FÏŒk /a&>%ü¸xßB`ÉkV Î'¨…™¢¥Ë³K‡†ÅqH~Rߎ™ð~ö°Üÿ„ùwCÂÐ ÓT r¥µëWNé˜z&·ÅÛør—¯ƒŠSH] §ˆÚv™â»ËðÔtû]ç‚´"Á–Ю)®¤HŒº5>ÑAÃK8ŒW0G‹3™øú³éüË銟‚D$ _~îL "7¾T". Òôxºâ΄ü„¨Šj/·—"•8 §ÂEÙð¶}Î&aÇ0Eà—žÂ…i^x©œ{|gG|·É©†ï¹÷£j·øìK›=²»ùÓ³#ñÉø>²ÛÛ.&œÜˆ¿ÎeBUÿ‡6Ž u!Ÿ¥ ¡#×-nQÍòÚždíö¡Ÿ+¨\ÅŸ‡ºŠaÎßï V_ÂÎ;\:Ú®d]»tšUuç 1u®W1ÔGœÆø× }á $ÃÎIým[3ñ0å•1%]ÄIùC+$Ã\xc«²JiH~È`0«“ú?¯ ®ËñêÚI²A#僨)Œ_yâôùWcäÿêÍI/ﯽבK !û%Ï”‘Û"u$wB§þ gÃŽúmž~`±þ‰q“¥SJæ,yñ #PÆø¢¿ÐöŽl¹~ϨsöŽjño\Àðcò;~x_|7šd|—í¿ã…Dš¸a¸‹IgîÄßàOógw9æ-ß¼1»Ëì³»ôµØ[#Qª»/ý›¹3ºl›>½«Ï­òÌó)ò™ÈD`þøñçqë…á8”òA†×U „Z'‹$縀ÎÏ8sèeT9l—^Õ-ø¥¤«H$ì&¨Uù Gn^=å=Tù=$-@%[] \ z®ÿ¸óÌøtF2ôú?‡ÄK[ƒV›aó‡ KÇýªžF•ò8TvµD_h  糺.Âáž'ìöëÏÈ2óó7(zò$4晊²;p½"z)ҥߌ¦QA¨o¦îø‚(¥ vG=‹,ßìÕìh^ÕÔýnF'‡W¨Æ·“Ûéî¾Í¼˜;¥ýi¼|ú…ák× ñG«ù³;nϧ¤ïYãSÒO€Ë÷Ip¿ñ§¾*»p‹_~ùŠbíñ° ÙûH—éûñ‹h•}Dö¸5à3ìÄ¿Bš¥2ÑŸìLy–âÐÔo¤R[ñRA(c˜ËL‹3š¾"¼ßdÌf2ð¼dÒ„Ï=‡J¨GÓ¥8è’Ž½HVUüÍsáC$ù¿´Vв‘Òùya¶ï0H—X¿¬2  r×°ÖxIä‡8áäÍt§ã¤Ô]0Ï$Æ{*{eÅL˜† Pþ å~PsÊ?4§‹x¼‹„µ jÇØ1Œ@v]>Šô%FJ×*´ëù WšÌÞÓ2{<ã^—w¹ýåU 4ùE†v^“#ª ±î£a£76tÇà ÔÒZðgµ¤zúûÊ5ÊV4‡ö¿Pø¥ÛƒÎ첂ÀÒ¥KÕŸvì» ‚””;6MfÅâV3JŽ#›¿oV-jÆÞºÅñ’–~65ð±©Îf=P“ý…#ñùµÏÙÃŒ›ý¼‡ÏÖnÜÐQzGJû￵c—Ïú£Pö¸¾pÏ6>¾ðXF€`Fà!€†ÌY¥bwŒnrO³33·}Rœhúœ\\&ÁYÁtn¾yáƒg&>>øPX$F€`F ¤@Ûñý åN³|UQÑÞ1ÃÊùÅQ£t©5Š ‹Xi·7O2ãøÓ™‰?=-–•`F€(fV͈BOU£îéø FeXÆ )jƒÈ~ñJü‚F¬Û„3P— ›íkZ­¹˜ÅòZöL|¼%gÄ0Œ#Àø?º¦ÝµâG´nvsÉ0± àE¸©råÈÙÛq½Ò -eÒÐáë⤂‹± ëò3;Ò°™Ï:žÎcF€`¼ƒ@ã™;Ç}¹Œšq&äz9¢VçQ3Ž‚+3›×tžoö>¿*'¯Tycv×ïqÑåºÔÄq¯CQtgŒg|_¼f/>–‰`F€ð"¸ºº §¦L‡sïâÚ=¿âõòPøÍ–Q/x³bF@”®;›’ÎæJ®T®ÊwY_ž?»ëpÏ4´u Þ¯Í<<ƒ|òš‰O>Š`F€ð{F¶üsû¼Ý¢ƒå’Sû€ÐH‚ôÙMfÄnÇáªeV_îÙò8è.·¶—ðùºý S)Þ“Â7rbâãÏ¥`F€`Š»”Š<¥Ì· ªOŸÒÑûGÒÓî@ž~¸àŽýê Á³á)>3¯KÓ™‰Oizš\F€`F ŸÎܹí{ZáêÊËT¡,ß5¢¹±¡ñŠa Ó1*e¬˜i½Y×\õŒäâkßÔd%À®l¹ùÿ-ÿ†\F€`F WpÇõ!-fíj¯IÙO“úWhãƒûû*Ë‹²|Ïðf¹jš{˜KH±¬yÿ]¤2ÜБkPSEðª™3[%›þþxfâãOefF€`ò‰@ætôMŽ Mfíi$Àu¯t¹¦! j€$'f÷¨ﮘ®ôwg)æÊX´üÐÚ…÷ÓdÒ»¸~ϯ G[•ÀofÍjŸÅ8ÚÞ‡/Jñ¡U•<÷ó¼öaèY4/ à^[ó¢kÏ{/dïÓYp»÷éÇS¬Ây¶ó²Öî‹زy“{"!Mk‡oË(œ¶ÕÐ9PáøÊYÖ»ðº&a€/—óA7w] ð‹’³»ÆÑcÇn.—ârôA[ zÚlÜ};«eiùröìÇÝ |øÂ‰ñÂÿ×{{¡ˆ»ñ ÕETÃå•Â|g­À^èò4þ€:ÓÓü`îÔ-X }¢ÐQÚ:“èÐÚ[Ê“/kg ¼Ólÿ¸ÖF(ú³+#à,œDjûR—Çp±¹UïÍœd¶ýÒÖîËÈ-¾j>ŒˆÆßÙQê¢y·LuÖE¢ò3(Ê*ìC_Þ;¼Ùi*]N_›RH!>êÙóÔðd8Ü…]Éœ²Ó¦µwGEãh8vîl‡ýœà4Œ£Í4¾|ö'â#´iÄP‹U}I×eURß…Ûôˆð`a³Zy1F_niÅ [šÓ©Ç'¤ÈÄ”4E±¨¯šs&-5mÁÑÍ;ßüå—å4“v6IP1HpC²$ÂCm[íÑ£_pö-†Ø‚l/pû¿!Øûl!9µ}§Ã5ïžco~ýõ»Ôöý½Ýû,öþ$Xó™;oÞq,7ûqR×w¸çÖЇ‡7ÛlÎð2ë²bŽí&ݑޗîqíiëB²{ÎpÖ/@ÍN+\ p™ŠC\sæt6Œ£çg3Ž6ãûúÙ_ˆòÌØWîV¬ÿ‘º^½Y½òææõ eƒ"بú:È,_±!`Ý”´tˆ; ›vª²3îDLã®í‡TkQÿùOÌÂ<8ñ0 P± RL„ó¶>öÂÈ»Â#Ê¿…‰Õ¸ýÚþ•mNmjµ¨zÞ¨?vЇs¦ál¿m÷þõ$|XZiáŠS|‰jÀn¸§VkMjg¿xk÷1ùŒ§ØÒéxï6…ºòî›~À3üY‡¼4j}{üàêçò+$A:ò£eŠª.Ÿ;ã–«ÆÑž‰|øš¾&‹äFOú…2X3±½ìŒ¯ÝgÇ¿ú’ÊôšUÊËGîê¨6¬]ÕËÅpv¥ÇÎÀg«þÐNž»,ÒR“_ùpöëo`ÝHKj[ù 6µ<ôaðĈ—‡Ù‚B^ãöh°Ë϶ït¦zÌ\ŒìOí>׺••o÷§¸8á~Äî½–uZLßylO]ºÀáñûИù,Ž˜,«²ì­ð>ÇSÏ=ä(ÒÀY(÷ö£}›æ#FüÑÈ)]÷âY/Ô5À…cÌéüN^i|),ƒáù’DWe1^þÿgޤgfû¦uŘÞäç*>|•DŠÇ>uJíÉBÌã/ަ¥ÕmxXñ ö^d²y§3IÉk#ù©Üþ‹òÒ‘·gÛ·Z_jôjûþÒîKÇCð±Zª=‘Ðô=¶ó+E‘;÷޾oTT}Õ¢<ûqK‡Ëžvñø#WIî3zâ÷yUcܸ?"£…ŒÂxÍñ|EX¤_5›õòå¡.埣Æ÷ÂϦáK_>û`w_ï°LLù\ÂXU0ÚË— 7ï9üï~O:°ü½E+2Å2‡¾JXÊ\‹'rf”»wphø¿¹ýçŠdC [ÛŸüèáû>{sµ}_o÷Ùj·Þ@`ûˆÆ'{¬‘=No‰¬éÚ–æ³w=‚‹þ¹kxóm˜?dÔLSÜ ‡+5/ÂNšq·[ŠÆÑk7nèˆ ó^¨Ù¹;)ÍYW*âg´÷Y¥¾yoW_ÖLùÁc.›"b»§.\Æ 00§víF;¶ÿ""7Æ‘åÇí#Q;§ßd Ê[¡rõšsjàð.·y:~$†ÙöÏx+*ªs«;ÖÓLjû¾ØîýYÿõ—ž‚†;Ç6û›¦Ñâ…±SöŽj9Ÿj²j†µ£Kwµ§k|O¦ k(Wáæë™îÅ‘ënþ}úÐây?¶\áY ,×y³9ÃËŒçog_$¤ÙQ+7¯;” 9ɦ‡¾bØ1E€ÚÍc½:á:[P¥SŸ>ƒ0}xPƒò5 ¢Ñî3å "yInnÿˆ»#`¶}]BÕ–=o‚øj»/pÝ8AáØ3ºåw¶›‘ÀÜÓx接ßÙ†¤gâÕÜħ½F$\2ïíöÃ6œ.kCÒCÆÑð ²5¾5Û$'oªlÆñ׳/j|Œ¯Þ@›mhóú5$Ž[ûZå¯ÏºLÊMv-Ô”;A>ƒ,ƃ¾~LƒOšíå+έíABÊEVx†äæöï+Çÿä0Ûþ®ƒ'£ôô…ï‹íÞÿ€õ‰‘Ø<Žk{Ý&AáðTv¢AdÓãp¤à=NÌ=:]þâV؎gÝ`™‘1$£‚WRN¶p¹Ä'hnòX—Ž®ûc=G뤺c^¾î,ZL.ǰeoÌè”eXFjßþïkã«÷áC;àCª‚SÖ™ôøvûñ é2Ú‘¨xk¯ûnFÉÐÙ×¾~vŸ)—-CNQ‘Û¿_4/Ÿ’Ún5P¹÷#ORÛ÷µvïÓØù³pŠ"R¤P~G›å¸.Ï;ªT¦£öfŒÅ"ZTå)i³Ö}ØeoeÖgx}vçpÇóžÎs¦wÝ,e &]_àp׈y³ºü6v×á æt­o±Àó¨JÆ MížiüåÚ×4>Fv'-NHëôø ,§ï"€íµµBVªQ³JI+ܦáaNq'»‡’v&ñ!£æ ’“ÛI?’ÒQ¾Ùö#*V¼kDF¬¾ÔîKÈ>X‹=#Z.ËK¬3,·èB¿Ëˆ#@“ªõUÏ Í´ fݲbè˜7ƒKû÷çêj þç\{›ø¹3º¸£Í¸þtö5É£âRÚuhEf\œÐŸ°dY}jGaAÒXEôÔøøJûÏh÷_ä6’“äåöï£ ÊÄ2Û¾¢Zj£Ø¦ÆÇWÚ½!YºDÕ¥öšY#œõqß‘é´ÞOŽnþôN'"Ã;wDZ}®+É[†ÜØ.Lj~äék?’Ç‚û‡TÃm(|M6?z¬,jv"Ê…Õj!£LYâû꽸ßeª3þ¾Â0)Ãäñ* £FmIד÷¡"EHHÑ„’¢‚ž¬¨2Å¥YRÔ‰~Éó¦´=5•]ùñ¡×¬qà :ó•ë]0-Š µªV0Ž”4|Ž_¾ô·ZáÌÅ+ðéŠ p¿Ëã×ë=ÝZCûfu Þ]þ„Bÿ»pñJtî<ßþ¾^{îAƒ@mÚ}¬°a{ÿáˆÂ¸ßÙÁÈ×ó_÷¶aõ¦=¸ùæ ÜŒµ¦gqM_ªKüŽŸ½UtunÕz´oj„}‚õ¨P.޹{†ªËÁS÷Þ ßý¾ v: pŠ÷ý=Ú@µŠFüÜd%­Ó?n‚}GOCxH0´mZî¹µ5PþˆÑ™‹ {à8æOß×Ý­úeË^Xêþ+I©Ð¸NUèÝ% êÖ¨d”uúB<|ºr#Eù#QFÒ4D5¬•+^F"/ýÃ~ŸÚ•ùò7ÚÞÓÙ4>nÙ2åôR­¯f³uß1 üG=Ùۭ耛ýÆ'¦iDïíÖÆ >ßb;9xâ¬;ζ}G¡U£Z¨áQàð© ††ÈÔn6Âç›ÝU¯”¡ù¼’”bÕª a™¤'¯ß³hÔvþ펰býv¸œ íšÜ÷c!âE¿_±mÑoæ‡ ;ñw€m¾ tÃß 9Òº.ýáOØŠòZ,*Üvs3¸«S #ìzÿ*ão¨vÕH¸©z%øùÏ]pøÄ9ƒøP[¯Y¥\ŒO‚ß·î‡!ÜŽ¿ét,çã÷O˜ìCí×'+Öþ]Z5Ô°¸‡Ÿ¹+×ï04lV àŒ|ÿDÂà¿õ„3®ÀG߯ÇßðEüí„ãšS·@½ÌßÉõä-hxæ;•Ú¾¯´û‚Vãõ³kÅ;OÒÞ…™Nü§÷ð´#æyž9³U2^/2ïKãÙט¿ù£,V¬é‹uïáÓH ңé:,øì'H¥¯^TÅ׫Yþ³ìW rAîJrªñR3…¢x—®PÛ£ÓABÓ¦qxê¾®ØÁW„+Ø‘ü÷›µÐµMCüðm†mÁoøÒ$çp:aÏáSÆ‹² œnjøgÿG/G £¯O\m3{0‘ hƒÂhìÄZ5®mÚ¥œ\Br,_³Å pïÝ Î"Ayuñ2 ï?ðKþÈéóØì3âæ%+u.çãaè£wB¯Î- F¥ îü¿Æ¡ƒˆÐ x´W'$8iðÕ/4Y*ÃUz¶‡‘ÿè…‘f. ¡zÎÔq<ŽrumÓÉW¹<ñÊÌÒ›'jóf;£³/8OyŠí7yêüeƒd‚i†Ú "¸Ê5T«aå­¨å!Gäã’vMë÷­‘­ÂvñÃÆÑ0< ð/¯ßesñJ|¼b’ŽÐn/Å@IDATúp÷¶Ù ÂCŽ~;»‘¸oŒ=d£:Õ"úx!÷Û_ðCá$’ün‰#mÓùËWW¡5"åòÈËfü€ù‰SÚðthY߈I¿¥o~Û »€¾·¶‚ª¨%#9HN\#ňóÑwë!ÉÎßñãæþ^è#Êtô» ßÝÔ¡@ä?àÁîFð‡ß­ƒ ›‡¯ïÆßVyü Ø`&+®³g;+®28_E Þu*[ìM†x.(–ÐhµØÅ*¶—l$§g±8z‰Ûß^Ãg}‚_®ç Û*(îø9ã…õÐ7Zž'QåOªùõ¨µ¹ž£¯A‹ª"‰ Z0,4Øfhi¨ó'•x^Ã…èÀìH(?xà¶v¨íh‡_˜s,‚†#º­½ñ5þjO²;¶»½CS¨T> ¢Ô"o{ŸqG#9Ȇ‰ÈS㛪_šôb&­M놵  ŠL¥Üd%‡Kø‚¿Ÿ °#jÓ„&†d¸ˆ°xðööÆWqÏ››Ãfip(n9$F¤ØD:Ö8üЦãow´7ò» ÓU*ž§ fž^<ûêË¿ØåJ@ \hеÃM¦uòäpo0Ôš3®éls‘i'ïFLÄú•…Ë -ŒÑ‹ÿZãGÄíšĸ6’›=Hv<i+©1"2M¤…Üθã†6¨Jdj5«ކéòãV¡Væ3Ô¯Ù´ZÖ¯•±]š.-Ý…} ™è#ÇÓÑ;…Èýh˜0» Ó¾£gßi•¢L¤iFQc˜ðÐÉóеuc´+ „NhgEÚ¡‹HœŠÑ™m¬‹à¬}Óã;x”[6¡Œñ\¥Ùí_F.|i¨«Ø!§¯«Ûð…ºy×aÀí0ÜjeRõ“«åaœLÆÏÔéçät$y9ÒèÐ0ÏÔw¿»^9ŽÈÐõ‘›^£pÈltÊfo{à¬X·΢á©iíÒ\î,k!!3]vvÕ*–wkÒË;µ^äò’µO—V¨sÁâ/×FàÿèÓÅMÔHScºšˆ["všDjhøŠ:Å-{ŽáDC:®›OuäêTËJöò’Á,Ã;çâBõŽlž¹œd€LC¯Ù]b¦UrD*h¸‹:bÏa. Ã)­Æp' AQ ¶ñïýŒ¡ /¨Ëé·äÙ>pY w[5ó&M¹Ð ’Ž"þdStÉõæÝGŒpúGÚÎü¸aÝe u]Â6:냆öeÀƒ=Œ¤dìMCÙ99"/äjgþÞèƒ)¨M׬^5X³yÛoí3…í;rÆø Ͱé(í‰sñYÒ›a|fŠ‚€Ž7QAI;ȶg]ïQ®÷at¶N©(øYZ_">Åþl†=@“›ª³WȶåüÒ"­ ¹3H"Ì1ö³8S«~ù‘£/4z¹šŽ^Ny9²=(Ÿ˜!åÍŠ6ùqd£°nÛørõf÷ äl†}OÓºÕ`ô?û¤c̼ϳd—}³Üf¨ä%+Ù$ÑÐifH-OÃfÎz¨ó‰|‘V€4GGÑØuåúX÷ì3ú‚¦á\úö®<ŽêZŸmÚ]õb[’-[¶q¯¸aÓlƒ©! \ž{g±¢a½˜ïåñ¬YÌÞ6ÞË«4¡ÕJÅmÜ% m²JŸ=n¸‰Wc]„SÜéNÑt¤ª’ôºC­Muáfä-6_¼¢waHxÁ´Qô.ÛÀ.Ú¬ôTÅ`KÓaT Ã^)ƒ†±ŠÓaX¢ µ:æûÕ„—Ä!ž:ƒ­||Àsýðçaoq*ÂpýzÀHÆžkw”òKË«¥Á¾®±I10†½ÒÂ5ÛÕì„tܯÐ*¡Íx!Ž,.ô=¬Q¾ŒWnÙ«\_µu«óó•¯ÚÓµ^í?¨í}Ó…|›á…bN1¦ð%MÏ16Î\âÖ1†q¦lÁåŠm*Á‹ØÇ¤6à £äW>YÉÓBÇ•1º’ûÛxÊe„rˆŠ1m³œûB–wƒ0¾áçHy•’ƾ Œÿ`©»{)Ør¥›Ä¶nkw`mJ™¢‚ßž`m|`—¡ÆÓ[Ù`ŸV2P}8!†Û_¬Ý©Ü˜–†‘µ è^Ùqõìɼ\~3¯ShnüJ»âO} ö2#ù tü­\röxÅÈ+ÇfMᛂRç æö8ñºm&Öò 6§Ó­ØK|mŽ¡-Pá¿Ê†±š ù°ë?˜Töƒ²±2 O_û|ýòoï)š4z¦£3”Œò_Ô¸‡§t^_°š§¨+/[hõ %¹ŠífÔ4™93ØÈÓ• ,oßÉ6a X££õ!lŸ?X ,uw/[N t×±4“zm¡2E?`mü÷tGXm‚` 'lŒ‚%ŒÝ7¬£õ»JÙ®o°bë'ò¢ÈÆþàZg‘_î%!Ð\ó,szÕ»::nIËû%QÒ®B uæˆ%ÿÛöèRøÂo~>ûs?02ȼõÁŸ}8fXñÔû¾uiÅ…—_µ˜ÏÇCÞŸ ½Æ"A›ncí^$j‚í l‹ †69ùå› ñbŠ´üÎx&0Îô·w–ð‹ÅMw\·¹…Û§L‹ËÊš¯Îì"+»,Œ%4ZjêŒušpŽÿøêç´u×¾Ýÿ~úñ{89oU¼Áó‰ŒÒî÷Ì$ˆ‚›ïyèé‰c†ŽõøobÏÍþã•ëï–0Ö¡iìì^è¶NÐÕ½Lþ®Ò`ºÉfoaAÛð>î*o¸×`ÃaPÉÖ½Gx…Újúñ·/WÜKàžxþÝ¥ŠÑÿ-Wžç« ¼‚0] ÂØß¾§tÛ+Oýöf._+ã>MMú2£ø>¥Ïoø–‹Ü¯ø@Ñ鯻ü!—WÊöìßâ½ ¥Õê¼? ꇚÿ5J^"‰úhüö"-³3^»Â$Ð>ºzp~‘Qg<J+ÏE†@ ñL‰ë]Ý Á”iþ®êÀ‡4+ñ$ØÐjŒ·«yñìŒàs ®°,^M±xÔuÈãÞ…ÀÂÇÍÞ–¿ŠVó×Ëó¤Ð#àP¼Øú~ȉ@0@h Á^4˜"e‰@CSåpòù);;­ª­g›<öoÅn,ÎgÿU’$±B`ç[cSÚõ+f¦–Œ{ º¾wóÇXU™tåJOÒuYâ¾YÇ)ñÜH$ÚE`îÙã›$‰@¼8|p÷ïY虌úX;k׌7ν¿ÂëK$^Lh¼­­êÒ8\’=‰€D@" hÏŸ0\ÎÅïõq§Ó=péýŽ­¾ßò@A@ >r H$‰€D ɘÿŒµÈåqÿÓ× }8ïA÷Ÿ}¿å)øø ‰@¼€¿«þöeu`¼ë–õIz ð×CÍÍŸ²wfÅ->Oq53¾ÓSÚívHÁ'Úˆj¤¼/×íæ Þ@¤aI²!è€V;Á¿M8‡ƒ/øU8Ye‰@A`ÃóSLd«~‡o¥ h =ÞðÍÞ‹«»Î•ÆÍÝ!£ëˆ™´˜½¼–UÕ(^]×sü0øö€“B„t€‡Wœ»ãú Ÿ7ð€|’½ËžÃvG²ƒ@ЂÕÛ•ÐgµFxÿ÷üUŠ^„%XÄѳÓSS8<Á úÎU3ùåâá¨Öû×ùpN‡hòX‚ ç‚L­DÛFÀT8c”$ˆ7›ñŽ0,çràN8E¸“þfc{øndŸC²+¨¦®‰ÞâP(•ñÂ.={íæ±áþ÷Æ5Æ9¼Q¯`oÓp1sòHžÄ%I’ŠÚÍ/p.íÐ{ôß½ôç ñ[î;" 5>1‰Ë8¨½ÐŽQ¾üÑWt¤¬ŠfM¥„(ظÇ[Æ6öÎü×·S°<£(_%÷¸"€1å+·î§—mRê†Ï›ß[ª„|9“ÃQ¨ ‚QŸìtêË^™1ÆqÜÙý€|ç¯}¾š&p9ðóó¯Oä{A§c³+Ú.î3Z½Ëf¥#RG!fð|\Y]Ï1¯ÚÂE ÞUm}[ôgö× ì$šX+ˆšÂ”ž#XƒF)¢Ìt‹/i(±–|™äD ŠXR¼á¬áLæŠC%P°ˆWÁ'uu?æe*YRZä„k[¨^yN"O>{Ò8×ãr=/êdgêŸYÏšy'ÑRqJî»@@ >]€«KyY¬¢Ïɤ ¬ÞŸ5y”oš+P}gpdçS,ü8v’ú÷ɦ­%GhRëCß¾R¢¶ðyuTh|ÉÖswPv&¦±ú)*~`ákW’D@+@‹yñô±´iÏÅÎ'_ªšÆc¼îp¹/iW÷ƒ7¿$I’=n·ë}n†òþfcæMÖ´>ß¸à‚¥Î$oZÜØ—6>qƒº}E_¿hŠb¼ùÀSoP]«€Ò>…÷Waßlº#Ÿ?÷ö—ô ŽhŽß3ÆS."rv= 0ˆ ¿lãžvQ²!~9r>ðÔëŠ"W<~Š~æMºŸÏm.9¨:yN"w ¤cŠë'~‡jêiÎôà<Ob{zÖÝ÷‡×iÓîÃJ$ùÎ7JV(ˆì ðj·Ëõ/[÷Nèè°•R/¿à‡§bP]-Rj|Ôµgòªª‰#ŠY[㦒Ãe¼šËGå\^µ…MM³y¥6¬ÖRGy‡ôóÛ®Vlx`ì©&ƒŽ^DˆTb2òÔ–U±BTl#wŠˆî¿»ûêlòX"W ù|òG7*u^Évf†ÖéXœ¸õê™]ò’Ç6lÞy­âHËíì~Psh@ÿöÓowY¶¼(П=a¸‘ß0\öjzˆtæ /x°±Mí©5¦5Ê|Ø1¬¢ô Á°¡zÔéý…q /õ‹çEéå^"HüÇj°¼¡G¤ïì~×å^"l|ú„áVzþÎ|+³4üêØKVËEsÿÇv,ÙÚ¢~åT—z¡Ov&«÷Çh€É‚D@" h Ï~¯¿Sçq¿Ät ÝùÏgk”àžzw™œƒIçÊeÁ§-LD4Yõèä1œ¦w½¿x#-Y·›n¾ülE ¤Ã['Ž´“#b¿òéjÑHÕ¦|ªHMU¼wêbÓv4ÍèqPž£‚òíÇ”ö/æö+Aí#ÔšªJ=öµ¼¦ÁFs åq O, ×Çhºùž³qX•F[KÂǾºCÚã±’ñ°S¾q? ³nå} ¥èbóÎkñX¨Â9’Ž5Od<ì´tÝNú¯ËÏMȳ@‡†Žñ@ÔûÁŸÍm4׿ÈQÇû÷5í÷ 4*ý¢ã~‰Þ—™·ÑŠÀ!úå¨sbÁIǰÇO8ÜûÝ?òÝ—~÷ÈN©¼¼°ç©užx†ïïùÞ:*g÷%_Ÿ{¿sU‡ òDÄôJÁNáØïK~Ð|¸l éÝid²Oâ}vÄ€vW*3Ÿô¼¹õ5TçÙGyk1]{áå+XÄÓꮜH¯#ä»_n &C•¤ŸKµÆ>‘T~U)Eʖ嬤¶í iPÌöÀDþc>oò9†•ˆ Ë&C B¬9l˜B®®kJx߷ǃ5±†“t^êÇÔÇx8–P(eC hÚªl•ÎbÚn»’ñhægÁÔ¸> bÞÐð*€bøÎq¯Á`ú]†¾Â3Áü úÅ'„Wl÷¹Tý¢«4Ó¶æ+óë ùŸÜöÓ_?ôâc¿ø—àî¾”àR,xÂ4½Æyü_üM0Rähp÷­ëc®ŸzÑ}¶ãâœÜG¡R‹n©- _v.ö~Œ©­Ek¶ÓK7³Ò— ¶)qzüa eDÝÌ„Ekw(üÏXÊþ‚ëyç‹õtÊØŸ6¤ÍŒ›Ðãß&[Óg*|Ä«ýþ<ô–߯¾•µ<}s³â"ôøã A«G^‰èû@xô7ì¤ó­ÏÇEèñÇ‚Öù©ÏxHþü$ð7hrLß~èg÷±Ðóx‘i§nfê †x£þíF3Óž7™vð»R÷‡ïýï¯~ÔÊ_D؆秘>}\ÿ¨Óã\©zÊ£*–ÙîØ"…ÿžˆîï^#øàA‡¯;=Ûö¡—o#ƒ«ícÙ’!q0 nð^>X²™¶ï;ªð ~£M(Ó[ï~¹‘*MhgÚTr뢭-kÔ>ÀO¬Ûg='u ±Ÿj1SŸœtžÖJ\;Q7x/ñìûŽxl¥Æ4-õM2òTt¢uƒðòÁ’M1}$ªÝԋш‡±é¦»î¿Ìd2ÿ =ž©Ö7u‰îð^t:ýã·<ð¿—¶òÖÝ³à‰”qµ›Ö±½ÅÏxâLy³2´Žôúוּ}sÓmŒþÿà{ÛåĽñ㌴zmôÖ¢ ¤ãé-ƒÝ©<ά¬¼€§×?_K6¶¿Ñ&¬Òz•mz0½µ;Õ·R2ÚÕ„UøiÔgÐk1lXŒõ€Lê±ÿ6}“Ñ@¹lÏ£/àéõ±û궪ñxgÑ:ÊÔWÐ$ë{ê$ =/™ú“ôÆç«cö,Hh;¯\zÆ›ž•‘•ñ\¦¡Âs¦å½°„‹Î«ÿ xá~ñ˜Íæç̹7PHïOç=0?äò86°Às¦-1™-ã/Ðõ²ïœ<ˆ)!u\L9‰aáxÐaõ’½¥…–òêDp6؇'TÓãß\h~ÀS- f‹7ìRø¦ðƒ²°r­–Wn•XÆ'\Óãß~h~öZÇóªºØ´ß¿¾ÞòÛì×ñØÏá ‰Ôôøc^ÀS-YÇbì«ëóÇ£¶©…ƳíH"5 jþp ^Æ›?¦^Uk<üëNào¼‹`sjžpᬲ"¤€mz Zë— – Ò˜8âîV~ƒz‡~ö¤iÚgÿЧµ<¿gÅ;/fÒ‘M¯×Ý3ïA÷Eßc;¢œ“ÿâ‚@PNbT ÔÚ0dna¡Çfk¦Õ;²=O^Blzºk"l~ÀÛW›ö)ü‚oð) ¾Ü°GY½/CæPù_X]¶lstÛ*=%½èwõØÇê­x2‡Š!xo_mÞÕ±¯æÃ5;(«·a;¢æ+Ð1xÂʲå›÷Ä @õ&è´:˜òïkjZêúñê-­ö xK1§ÜÕÊ/øîT+õùS©…ó¯ÿ§Çí\ËOòœVÐ:½'eÒeºŸáÕ¼‘?äE©r=^ðñ~á9mOéÑ jäå´^Q¥Uo , <~е>ΨLyƒC\¦ù*LEZmºÂøkd­D4Û¯éǹ@c?ÍÚæ-†U‡U4xkh²Ç¬ïýñhhvR¯ªÒ*·ú&GÌðÐP»ñR´=s¾vÃtÞЗ—¬w*L$šo…7>ÿš[¾V+ßÞ£óŸfþô ýOÜ-¶½ìÅêþ~UÚöPmÃoV35ÛíŠQ³¡(4O)Š‘3øÿhG¸$0@Ü1„¡HjÖ›£Öþdho,xý®ûzö÷,õË¥ÁýûÒÚíû•,g¦8Ös¹Û¿°mv*«ª¥çß[J}{1µð"‚ 8 hÛëL/W^ËÄÆž1éû@xXôµ ŸÁüË)œJyÏ£C›ŸW’ryÙ߈m7Úe7¤¤‘5s müøúêÕY|ï:iøŒ”4Çv¿M&K&õõµvyºúaáPXðgAWõ$ð¥°ï±LÆ~Üæ …ÿ~É.˜LçÜ0ŸR¬²vÖgaö‹žÃºZ­Vó?~l¸ê£ÿÓ­å‡õB–gÛ„Uòë— ¹n’WèI ʲêvÿ$l—-9~¸ù!íl|ìörðCYç‰L—–s|°–§ò°ÿhG¸$0°syÑŒ½.?Áäsñt´ÚL}=1èw¼0ÅØ%Å9‡³¿«£ÔÄ6q ¾¹™څÛ ž›Šà¡Ó¬¡Ãµ²Ê%ì’­8M•Õ 4eÌUŽ®Ác,ú><ʺfFuuèÔ»è8 .-¶ÓÊÙô¼Q´þƒÉVÏSÈ*²7”+BÏéãk¨©æUìÿ”rúOSR@H:¼åtÆT, ŽÀc,ð®ö¸¤Â;3ŽÍ¬•L7éZ‚|üû%£Ï(N_ £ÛÿÝùÎú,œ~]ó §ïn^œnq¿ÁOè©¢BÂxžN5e Ÿ÷û99­%ÑξÇNuAµ-ÔÛ0ÆÊ–Hˆxw™Bó hK¨Ó]"Ÿ(#Þmˆ¤¾h´?’ú“9¯ºßÃûù,èìe›8A-Û¤ô¿•Löb`¿<êß7›ÆQÄ.#¼ScÈPVUC\V(í¾ï Ž-éœËŒ>cèd)B5yiÛÂ{”3SnѹTSæÅ©jOm§‘9?å™3ÏŠØ;Ϩºm€W¨·ñâ‡etj÷n¹ÀÃ|cC;Â%ÊK&ŠVû“©ÍÑäUô{8cŸƒ#RFš…N×5fÉo(e§§Ñus¦ò•*§ãlç#¨šà.ì8í ®ÚÇ¢ï#ÁCo´%½€kb·ÓsýG\CùC/¦5ï|Ý—¦I)CG©YÅÔp:8Û§Xàác(ñ?!ø´ ?ž µ=áöK &‡×/xǹ<Ôàtè^0™¬OÌ{¨‘¿lªç4„@(=b»{Vð°ðn<0ÙŽš“î³i&s¯ð­|I´¶%TæÔ$“ЇvF£ý¡âÕSÒ«û=œ±ŸÉB¨®18CÌSÕuôø¿æ+6>ÈwËçùŒñá23ÍŠÓAS´û>R<¬é… ï¶úA·!§p›óm_t?ÕVlñåkn¨àcY2úûÎuwm<º«/Ž×…ÆGh} ü¨ Zð §_:k[8ýÂŽ›'ktygqÊ”ëqÿüò‡OvV¾<¯-z¬à˜}<~Ðà‹¿“†˜U<ðDÂå[äO¢–{›¥ö‡‹[²çkë÷ÐÇ~=û‘e¤z `±¨aíζýGcæ|@*ʨo ñ 8} ÍVHÑú(ÝüËè3š¦\ù²²šëÄÞÚ¥6§÷ãßìÌŠ`)x[uÒ©…ŸÞG¡öKWm §_jy»þw÷sﯰ×qÙA l]ñ!¯ÅZ|XŠn-¾^2 = Á{¤ˆ œx‘É £´”ÈŸÑl¼Ú®µzÄø µÿ;l¦¼¬´n›„eìƒ òoÐýØ–gÊè!Ê"‚êºF%onf:/Çöw[Xk‚Xõ}¸x¸MdgMMZöÐn›š5˜¦]ýª:¶Š5=Û(»`в™,ÙJÞ´¬!ʾ‘ Ÿƒ¥XálýqH§~‚®.”~é®ÐðúEÁ{wUÈëB GÛøDŠsZšž~qÿúóKtôx‹â\íòKrèâ™™ôô åTzØkœø«‡Š(+SÔíé‘'ÓOïéOx®ŒÊO&.ês{®‚ÿuå3ýð, ÍûO­bÀ‡;üÞ³­tûd Ýü^=m,k[Ê;}€‘~r~* Ë5Ë>4ýïµ”Â,¼9‹¾ñNí?-ýu|âSVTÕ‘ÐÚtÅMºÕBçOAðçƒ^‡Ëªè/Ö+ÇÈW˜—M¥'’ ®reö×ʵ‚aó(%µåŸq©²‰ [>»ƒÊy…WVßñÔX]JngpÓˆ"ïÙoãL‚í—îð“ýÒB=ëz|BýÊ Ô­sgóCû]z¬V=}û†>Ô'¯#d/½vŠŒ*ÝÙœÙY”•a š'­ÙXOWÎÍ¡_ìáN{ÂÉ#p0±?•{fXèÏëlŠÐ“iÖÓ.I¥âlUC[O,0Ð W¦ÓÛ;íô³ÅMt¤ÖMõvžZäëïîn¦ûfXéÎùÊŠ »ÙGÒ–nŠîq—£Õò­%ô_—žC+¶îå"m«^úð«vxí,=NØàµÙé[®˜5AyÙiôæÛå õG¤í‰4?øÝ¿þ):ëš7iÿ†§©¥ñ”¯ «Þ¸ÌwŒƒÒU¶v'[è )T<é6ž{,Ðå ÏE£=AWÛ„ø–Šˆ:ë—ÿBØ‘ŸE«_¸®ˆÛˆ_y.út|‹G¿Ž„•ˆDÛV‹žÎ›žN}Ù;¿ŸÊ¿››=ô÷WÒÿÇkì(J²üiòèÁt´„l­¾€ü¯wõ[ÍW邽¦./¼4™nŠBÇêÜô£ 40À”Þ´þFzf]3ýrV*¥²}Ï[;ì¾i°ò7Õ°À8Š5e¡ >‘¶Aæ ù+"{O¾¿dcd h,÷ÎÅGÄB[`“]d¿DÏÞPZÇy‹ÞÐê ÚØ—œ†ky‚÷¡Ã;iæÙ´fS#Ç™jËw²ÒI}XH&’e C5mmèŒw+Ë™fÝ6‰ãk5y¥¤_›AãóÛdêÃ\Πl©î Cy^" Hâ‡@ÛÛ)~u&EM9ÙFªfmM(4vT*åå˜hÅšöö<5µN6þL.¨ Óõ´Ie¼Ü" Âslô&Ûø€Fñ´ÖECL´½Âkü\ÆZŸþ\ž$‰€D t°<þÜ R^nef¤“•ç» ì,2Xú㫟Óö=¥Û^yê·7sÌ%aù5æ¥C{Àq†(aå sçy¼Ü|OÞÓÙT5š%Ib†€|umm‹²Lët’\9=ûœ Úµ×F˜SSkOj¸¼d¢ŠF7õKë^KÓØâ¡“,Ød¨–°78ØA›±-o~ºŽ*¥N2õ¿äU" ôT¤àÓIÏž¬tP¯Ì‚±r0Ô¿ÀDÇZhù¶Ðó£¾y&:Åå%äé©ÁÙÁ}U~yÈA×M¡IFº5=yškÁþ6CîÁ,ø®I.Á/™ú*ž¼".WgdNéÚþ­³|É|Þ`b‹cIšCÀÒ¹*Ùgšë®¸3”\ó/q„§ü”C1R.êo¢ýÛVmuÆÂ¬s2©²ÊA»Y㣦l~é§³? cåÉ%øìa»¤yìŠOaà¬n—úøw+lôǹiô¯kÒ©Òæ!L{m.÷j½ xŠ+Ǫ£Ý•RðQc– Ç×Ï™F•ì|p¯êš=eM>PY¶~´¢Š¯ÛÍ1¹¼+‡ʧ+Î;S‰Ê~šÂOeM=Ýté ÷§iù–½ÉÐÜnyœ|ùKÔP½ŸJ7ü™Fœý0 }=Lª>±^ñÒ\SÞŒ´Ï ™4qîŸcæÝ_ýR){àØ›hЄ[iåësù·Ô€v x D¿ì]õ[*ý 6ã~ö²]H¶º#txÛ?9Zûßɘ’ÑiŸÉ~ è”,8uFjp°M±ÙÜ´lu=]<Ëëz_ä«<í {~zØç¼Pœý½*zôcùÌ™™EÛv6QYy›DäÑòþ“½-dszèk£y»ŠŽò”Ýè¿TûVmáR KFw±Ÿž©/ÖÒEÿª¥¿ð /A·±³Ã/J[hoˆKÙE~¹O Eýripÿ¾´vû~J1);=•ÞZ¸–ž{g1¹8õL„@LzÕÌIŠã°›½…®<ÿLåÚŠÍ{iúø3”ʉ$þ—S8•òžÇ/Ñç Úkæ@Úøñ-ôÕ«³8Ž“†ÏxÀ׺¡Sî 3/ûyø¼šŽí~›L–Lê?êkêÓò8Ôý‚b²ûO£럦¥/³Ð½ë-yÎO‡’]õ™ì—: I³JÁ§‹Ž[°¤–rsŒ4 0<~zºF°Ð‡ jº¨E›—œ¼ ë÷+mtíè” ½rARÇæ¦êèüb#=±ªMÒfk%Wþœ3q8mÛw”šØO=}sÑZ:\^EEQr¨Œåç(Y÷÷†«X°v;•WÕÒ’õ»©°o6e± t´â4UV7Д1Cü‹OºßC§ÞEÇYpi±æðåŠÐsúøjª9Dì‘9‡_¸‚ÒóFÑún$[ý1qJÙC:¼åtÆÔ»Û—?ÂG@Ý/(eÇ—ÒѯQsà :²ãUÒéM2dR—}&û%|ü“5§|ºè9,eì©t¼Õ—OI^ÂrøGÿpB™ ˜@ã'tÐ7ß­o'Ì„Âòi^Þ~É«uìÉYNs…‚›ÒæsÜ-„­DÅ}è;8e¤ZyJØåóî\ÍJAYéX¬CTVUC\V²SFŸ1T_¹3`3r‹Î¥š²¶i®m ïQâtá¥ëOµ§¶SZÎPÒÚkRýÓÉßÁ!ÐU¿ä 8—µnª=ÙÑ•ŸÉ~ ïž’ªG >:v¬Ó¶%_—µñÞ¶B*ÔVˆ2Bͧ…ô‚wì%…†€»PáÃôUFš…N×u 32vh(Χ%v+ YØ ¹êÁVr´[ÌÞ—>!* •Ôü‡š7Pzuy¡â¡7Z”Èì5;ÝÄ5”?ôb*aû’`¨I)CG©YÅÁ$÷¥Qóï;Ù3 $VoA·ª«~Ïè™Pé¦ç•³êBõYXý¢kÇ7Ú )I豂þ7ï]h˜G“¡}×jûü«ó –GSòO¢ÉßF3*í×dãâÀ”ºßEuûî c5d²Ðªkl?E90?—®8}üÕV:ÑjØl³;(E¨N7óyo6ÊLój”ÝüÑûêj:ÃÃI)êd[ù% ²Õ··áË)œFãæ £_<Ž––Óq€DVz¼àƒ¯=±YøËÔ£káM»KËÁ››yLã©Áw¤ýŽr2SÍdô4ó¦Ý¶£àÏHö¨¶?Rü’5¿?Øcì»<î ´>‡‹šš)/Ë+Ä@˜¹iî :t¢ŠÊNÕPQ¿e³ò=TVIM6;=~˜2=6}ÜŠmPM«­¦¹ªxI|0mxŒæØW×ë‡R©ÅÓ&ܩӪ]Ž&eº$-{¨r:5k0M»ú?Tul•bË“]0… h§ðŠ­lu¶€ÇiY^CïF6ŠîŽÀxŒÝÕÇëN”ÍÖÔXÙâIÓ…Ó/:AYM—šYL7>G™}Ç+ý›ªîú,Ô~Qx´Ù ½ Þã—¬*Rz´ßƒŽ¿°ô¬ëÏK7Si•<†Ó¤sæGŠ]Lòƒ7PÿÜtæºMh ·2AqA.­Þu„òT‘Rnq1Ïþð(‰VûcΰF+ýŽ1¤ûˆ”žfíÞ°†Íùy^ÈèÁ…Jž‘ƒ › ÷o¤Ý‡NÐûK7ÒÕ³&Ó´±C8Âv#½·xƒHB…yÙTz¢}ßE¿ð†Ù¸Xô}gxT8GÒ@ÓV?N:þ¬«ÜÅ/ÒqÊ…‚aó”%Òùg\JØmùì*ç^]Q¿Œ«KÉíl?(xóxt1Á#P} :'ìÝG÷•l0äŒÂékfõr‘ÒŒ©×üÇלò}ŸR•§¤öQú+PŸ…Ü/¬¶;Vº=W¢æßW§<Ð6=Vðñ=èØÔÇÀö2z6ØÌN3‘I¯#—±‚ô|À[ «áó²R¾aª$ÚêPùPFa^Á³n¾ã˜¦ð{¤h´?T¼zJzu¿·û5Ú‚|–o-¡ÿºôZ±u/­Ú¶_Ù:ÃçpY=óÆ"e|Ù[Ú4ŠÅy”—Fo~q°³¬í΃73k¦¢Ý÷á‘bðÐ1ÇÄ Ÿý럢³®y“öoxšJ7þUÙÚ1àǪ7.kwVoH¡âI·ñôØcíÎwö¼™ù m<:«/çÕBŒ/]ÛׯÞ?í‚‹jš&f²@ÊO®®IÝ/Mµ‡éógušýˆB헣Ή¼¾¥fÃW_ÂÂ|«Û¨ yNcôè©.|é"˜6#¿LF#°o·¾Š7ïr\-õxoCú¦)ü ÞÑŽpIÁ¸Ay”Õ,ge¸ÅÅ4øC£Øþ˜2¬áÂÕýîûil×ÂÕÂIgM8ÁËÕaËsæðÎ_$þyýË^ÏÓZðIÇlNaU| Ç2Ó LvÀ o$—šJmxO£ Ù>ó~Á7øG;Â%5©là|Ö°~”ꪣÑM›Ã-2&ùF7m¢4ækLAtÛf“ Pu¿·ûzr8]tšcii…À x]Õ±¯n_gx Ì4R»m¶]«NžÐcðžFdÆ „6°}åøúB–Ú›šê6¯Zþ 0ØÒ|- MxOÛ×­}®¾¾N®À¯~´ó­ ´´Í„ÖEjæ•ÍGÄ^¨·!@XÌf²X-då­_¦• Ry‰á$kYv&Tó£hzÌ;^ç¤Ð€>¼|•y¿àÚ´#\òÇ`pa.E}Çilㆄk~ éÛ¸žù9A±h¿À­¹Åáq:6þ-¾ÌÄ^$Iä^ð¢ìáË$Òñïßïþc¡(*«ëƒÒüÄ hzÀx‰e߃ÿîð8îKë›nJó+< Ýà%šx`ì»Ü.,­S³X5#Ôr!0@ã߸Gm›W,Û|`×¶Ž9Æê6Ønð£ùá|1!Ô ÀËá½»ßX·„ã·xù¿àüKÁ‡AHÒ’q3nJ…Ü×ÉšÚFü_ÕÌlÕ ÁÁd2)OZZ*Ù›íd·Û©Èé`wæ6ªh>É«¼É`Azw÷ËQ½Fç?lz”)7Öô ä(îÃø /yŸÐÀ·˜ê ·Æ@ŒÜX ª8NSêh¯uÕû„[EØù`Ó3¼‰]ø»ëbÖ~Á\M]£§ÅndØåw"mœ÷êoi¶×D:þõ{DZßBN)åê0|'Á¦Ó[ÐôÄjì«Û Ç›9,ESš`þ„ú«³Çüö,˜Þ‚F!Úx`ìð9ÓaÜż‘+`¡| ˜¿ÿöç,ªéiì„«êÜ}<Ü/úõ‹ýrpÏη¾x÷ LqAÛ>Á/øÿZÁ“Y‘ÔZ||¼:ìöõ6»®‰…”Tžö —¼;=G—f—“ž–ÆBO µ8üâwÑ·›Rêš©ÌÆ`ËfÒ»òÈàÊ'+—tAÌù‡Ãüô`É:VoÁÙÄ¢]q¶žŠXÓ“‘‘Niéi Ÿà|cUÚ.u†ÁØâ¾lC £Ýå tfÃJª6åS…©ˆªxïÔÅî%?=X²ŽÕ[0d6ò4ÞàœØµ¸aa<575ž Çx柑ŽÿÎú½ãØ÷(~w,<µšfÅTp é#o]á?=0tÆê-2›X‘9,Æ}/ø r[ZÑô]Ê7î§"^æžo,¡]÷KÏE=¡ìá«˶±z †Ì°7Š6bì·Øš¼^Ca0>i…à#4>*ðЇ[mãâÞþ¼ª¢¼|Êù3oZáúnV?Ó~Ï@ãV]<ú«·`Èìr4×oYõå?7­X MæˆÁ#6µÆG > H²OùÑÃ+3óòîÙ¾ÿ8M74",¡â†‘0ì{ Iq°Ðƒ Šnz@¸ž‘ÒL'›ÜTí¨"‡¡J9¯óð}ǛΈ<:ï4¶Gû›—س,“oñPQ¶™2XØÉÊÊ ÌLÞ ü0Ÿà|ƒ¿H©3 †0Ù•žª#CÃIEŽÍÁÏ»ÞB.]tÚ®´—ÃPÀ#³ Óâüˆ€ÙR_+ÚŸóöcyØ!Ê‘½{­(±E m4ó ž<à¾L"ÿõ{DZï¢êU±0™G¯ÃjHöÿÃûh›º\<©Ë{øé1ê<”o¥¸Œ}5ÿÁãá¤*ÇEÑ1¯fj"‹¾–kzï]u™á#T¼FÃ9!üô9DM¾r/X¢~/ˆ±ô@;Ÿ3á°Ë_˜.‚d‰ „ñàÅýê´Ù¬ýù”Ž?ipñˆQ£¬éé¹f«5ËhÂ#'gìŽØ¶®Ùn«ª³5®:²·dWɶM¥œZ9Ðô@ð„cLs_9ÍÅ $iIðQß”®S'Ž¿¹C§»sß‘r>¨ "\…€cf Ž' ÕÀþ‡ï.¬øâé$3Û8>lRYøÉs{5BîÖ5¿¬-»~Ðô(S]>mOdFÍþ ƒÕÊa cÀÆŠVlIi ­<â½·§õ7ÑyC‹!&ÚU5àÁ ‡ÍkàsxŠMS Ûñ³cÿ1Ý‘ý%‹4DlêñæW<«ù¼¹À/;ݼ.ÒñL¿Gcì»\:úb?Þ­^ÛºÉ3Æù¦hÕ÷:1^xª;Þ÷‚ûÇ•~Ìüˆ±¥oØLÔ9EÀáÊ!= ¡¼‚o8ÏzB2ïݾyol’Ä—˜ÈçB&ÔØP'lwP'1µM„©ía’™´$øG1è\Ë>x÷íëî¸û†×?_“óã[¯Ô§˜B{áúw xxá‚ðòþn]æÎö4Í,ø4·¾ôaøëâ`‰˜ˆDè< ¾¼\=:jæeÄl­Ç/ÿÃÇShø°eZ ¶G©©VeÃøѦp0¨àÅ +žD£‹Ì”“úxCƒ…é;´O¬²Ã´^¬ÚßÂ1§xü°ß GýÊùÁM€Z›x `¼i|ãž™Qx¿ƒÎ~1óŸéø§ßÃû9ÙYÄ+ˆ<Í‹bŸ—¨¾ïªSã…G â…‡ûB¼–}Î|Ò:®´6îý!Â}à@^í”ø š>0|ÆTDb:,¡‡‹Q¸q‚Ô) ®!ü ;6ðƒëH/) ý-»FŠ/ܜΚšÊ†]×=¥›6ãׯ|¼Âó½kgE4¸Å/]嘅ÃÇIªÕª=Šás ¯taÃgži'ø„£ôiUöø­‹feÑ' ÙžÆÀv5GÌ\o6 D<åÅ‚Loaúfðm 'cpÂÅSnéÞû{ʰTʵ/‰&ˆz6íC;;„h}bÕ~Œ›Tsãò¥ÿá9z¡šÆ×£~Ę‹6Ô¡”'xPÆ=g-à|ë/¸ø‘ެÇ~A~_:Ubþ,OM£~ëKÔï¾W˜èäŸà)Öx¨«÷½ Æþ–ÕËÿÞРD‰ÕÚ¸WÃ#ŽHÜS@IDAT…ð¡B} A‡²Ú‹÷Šá3ïýŸpš¨$êS߇Bà³B~ ñPyùPR2! %Á¸a0᥄Áe_µðÓ5™¹9¯òñ-ôyþûÊót‘h~ðÀËÃ!\ˆ¥îx cz§…¿V½Ú—¢ññð¸†Æ'­êT6ÖÞêi÷>+•Ÿrñ”‰¶îÖÑ5óÒÙÎZï .ðK ƒýUŒG* >ŒAkœ2kêöCãÌ¡õ"¼x E»ýøÚŃÃý;¶~¼eÕWÛ™q<´Ä¦~x…ܦdh7îŸà;§o?|­_鸥ߡí gìP@6žò¥XS)¿ ŸoìÇ«ï•ʃø<üوǽ û÷ìxgò/72Z÷þá7„ jA‚ÚMZèàƒ'6> ‹P—ØP·ZøAÝx^` ×¼_‚| )9Ð¢àƒ…¦¨?ãßï]rý71ào,«ªöÜ8w†>6?x)Þ\ñf¡Cxxj FšØ á«_"|04¼Z¢+.M¥Wßâèðlë³y‡‡®¸ÄÄS=¦˜iyPw Z¥®08Rf'½Õ+ˆMlbíTj ¢º<œA¢ý|”<@ÞßD6JZ.Ø5`z šž};¶|²ôÃw¿äê…ÊZ|¹a|ié†i»qÏ¿Á«uɇï|ÉB@¼<ã_àÜU¿‡;ö°às´ 3L,Øçåæx•¾õÚÔŲï•ÊBüK<üY‰õ½ ûìüï}öƒ3ŸyÀ8Òê¸÷‡Hý÷„~Äǰ˜Þ‚À#„ñ¥è}ШKþõ€D¸Å&´Ãây!Ò*ä¿äD@k‚$kܬ0"³,|ûµ&Ÿ?û¤ûœ™·þáÕÏ³Æ +òL;T7~Ø€°ýüà!„‡0|ÊêÖ>À˜YØ6¨5=Ñ|P¶)t´b­“Žpp]:Z¶º‘®½<›êƒCM,Z¼Ï“ÉC¬l€­Ðu€Dû±æÂàUœ­Ôö©á«Ëv±‚†Ì.§£aýÒ/ÞÙ¾vÕN)ÆÞÈOø-¾Ü´ò 8î™Oø31±ð¶¤ª¼ìÔ´Ùs®ãñŸéøæ±ûE}Ùòô³Ì_.Ü"ìƒDÇ¢ï• "ü+<üÙB= hâá?öa϶á«%¯c 8W…ñ®åq8º"q_@ ‚û‚6€)6>Tޱ‡Äs{±¡N±‰sá”-óh­ >BホVqfµiùÒu{6nØ?cî¼K\.çl~ÁeòÄ“a5{²³Òt\FNþBŽÿïpj<‘×iÊàÿ@åçWkêéÀÉÍd0â¾Ö‰6¯jËîiÊ£`ïÎô·¯ýF¨\ú·ßÿw¨å‰ôëÇpöÇ<+Oéî]+Ö,œ¿²¹¹I"b a%öØ ýñÀãS %ð¿Ã¸çs¸GõâömÝR:ã’yç²`wÿŒhÑï\ŸBþ¿ÅùÎö˜ 3²öt¸¼šž{g©ò’W§V߫ˌձûý‡S¯ûý[f ±pÏî¯xì/ã¥ß¸Q“aÜÛ\!x@©ñ[¹Á?õ³@ÔAq2«–К଄t9]|±àI µ¦ž >iñûo}ÂÇ_æâá#G[Ó3rØ—C¦Ñh ßÅ3oÊ6fõ7ê3ÍÖúìßk¬mpn?oºªÏ£3PUñôÁÞÏS¢c{·.sÛѧ«¬q»æt²ï&[}SC]Íá½%{÷ïÜz”+4;xø'‹Ó±NÇ}+ næ\K?zgñÒhÙ°±9"5=3ÛœjÍÐÂøg-’nÔˆ!ƒÁ/ îÍ;JãXRôcßÖP_}xßž’};¶áZ 8'Û¸)˜„ƒšÌãC@‹‚µxyA¥)ærÁ´x98ömßRÂ[)ŸS¾ˆy/ÔŸH§yš:½rÔŒsn¿ŒšuÅ©½ýëgNžÜ‡µ&(sÂ%…ßúÖ½`ÆÓ\úígûWM0Ö‘ ŒŒ ߨàchu 4CÓ“,NÇ‚÷Üe9/ yx;È¿55îùÔÓ?g­O:k2ô+ç¿ÿúу¿¤è#ÐSÆ}ô‘‘%JºA@‹‚X/1h}Ä–úÅ€óŠ3+Þû4B|Œ—HäñþÒàÿ k_Ù8aâõ¦¦åó;ÂtÞì{Î}ï­»ÞÓ «©Ãgø u§Ž0_$´D !ô@X† Ɔøê… ™LNÇ’~ÜÛššNfde)Æ=‡McÁç÷¤è!ÐÇ}ôБ%I‚@@«‚XÇK/2øº/6|ÑÇÒ™•Ri¬ÿ•”|þÖ¤É7=ˆzú˜xÞ€Þ9~|›&¾ÍFõíwÕŸÚÇÇÕâ·†öb\@èÆb5>ÂÖÆÆ•–)©Ç}s³í8 >CpQq1–jqÜh¹ÿƒá­'Žû`Ú-ÓH¢‚€–qsãe¥>Æ /³X8³Š ¨Á²rÙ_{åÕfsúVR¥Ìºè9¯½òß 6,ÓóŠú‹ò§Ë¶ñ±–"›c<€Ä¸‚l0>„ðƒ=~ã<#"/j’D›’rÜ·Ø[JÕólŸ~ù™¼ÓÒ¸[ÉLbìŠ1Ò“Æ}2÷‹ä=Éвà(qƒãæVßèx!àe†)®X8³âbãGåå;ž..žñÔ˜Ûgè¼qã®xvÇŽO¼!âãÇF‡š ©Þ¯v\h9ºk5ïNvH”ØbÃQ¿0F ì` ×´®éa}”´ãÞåtî­ÈÊUù”‹ßrĘc¤'û¨$ ‘t‡€ÖÁ¿øRÇ_îx©w>‹ v=Icãü*ôñ{|pÇÿ,½Ý`0Nä׸åì™w~“ŸGZ/'d×÷ªûòYåuÂBžšªÕoø^f a(p¥xðƒÄØÂxˆßâº7urý¼'͸gç‡{ijvb â?=d߯}é*Ù ­",‚ð_8â%€/y:Ø ôˆµoÜ &Ù[êMµæ¾ƒßKÖÍ_ÿúS¿{÷Ý%LÃ’9aÞÁþÃsàÓVH,ŽÄ {±alˆMœ‹EÝñ,퀗ã^—bØ/ÀÑt,@krì“qñã{1æ±ç‘F’D@"d|ûâÆÆMR <â·r!YþýãoW}p×VnàgÖTöb-4õ˜÷ûÅ¿)»ß vb­ûY.á[¢xé¦ÞV.•Tb\t“%i/‹öizܧçç—¶!¬+xä‘GšyS÷SÛey.j<Ÿ·,™O"ÐëSCÉÜpÜøxˆMLq$Õžý>":—·ßþàƒë°ª*!m`'ÈÃ/¬OÛ›(>‚¨Wô9öê—ý| ÉqÿÔ}÷ÙØéee+îÆÔ¾Å}ø8!ã¸×Û›Ç}¾¥eÓâ…@O|â…ULëyöÉs?eåk}ø ÎZŸfWËÃ1­°ËÂ=>ÁÇC:>’$Á#à¡ã"q‹Á^$Žå^" h)øh¡ZyPk}Xú>k} Á ;¼¼ÞKFÒKÁG€!÷A!À!ôŽù:i€ïXH$  t‚`A ZŸG<=M!xÒYÆÍ’$Á# Òø°ž|‚GN¦”H |âr(U$Zëóú“%ÅòÀ?â“–íüáXÍÄ G™6èõ¾©.Òy¤à“À®UK$‚OGLz&ÑZƒÁÕf“á¡C CVž”°/Ÿ6ÁÇ-5>IÙ‰’i‰@F@ >ìÜDj}\.w¡€Ä££2q,÷h|¤Æ'ØdR‰€D HÁ'(‡XGBµ>:ò ><×u"DÖer‰™Œº6ÁÇ£kÓ Jl$‰€‚:! Óúè<>ÁGïñHO Î‘çºDÀãrµ­ê’ÆÍ]b%/J$ñG@ >ñÇ<¨¥õñ¸u>Á‡ý IÁ'¨Þ’‰ÔüäÎ;«ye çx eüñ­·¬êëòX" $)ø$ýnêNŒÖÇÓßÇ–Á ò Ø>¬Z¤oªªÊÇr/H€|Ý]ÔŸ­ÊÆÇ(5>]ô޼Ô:î´¸ÎÚŸq,÷‰€D ÑHÁ'Ñ=ÐMýñÖúèH‰¨­pep[¤Æ§›þ‘—#ÀÄ|Ç#ŸÀ0ɳ‰@‚O@¥Êxk}ØKºà/·¡N˽D D|‚Û­—S]!‚'“K$±C@ >±Ã6j%ÇKëÆ¨<+á±(ŒsÀ¥¥·iŽZ#dA½ §mªK'5>½«óek%ÚF@ >Úî…»xi}¦¾Pæ[}ÃÓ¶$€F²¨Yt>³(§º4ÛO’1‰@ïC@ >IÒçñÐúÔ××§úàÐéš|Çò@""çͧñqKÁ'Dôdr‰€D –HÁ'–èF±ìxh}L&g›àã!)øD±ÿz[Qjãf½G'm|zÛí•h)øh¸süY‹µÖÇé1ø¦ºØÖGNuùw€ü4ü`ñMu¹IÚø œL(Ä)øÄâèUk­Áåòi|ØÌYj|¢×u½®$›|S]:´ñéu@6X" a¤à£áÎ ÄZ,µ>.½Ç'ø°:)øêy.8ŒmyäTWp¨ÉT‰@‚O@Žf±ÔúèÜìŧäØhÃB…Š€N½ªK—jv™^" Ä ùr‹²1,7VZƒ^ïÓòðT—OûæȢ{(:·ÛçŠC™{h3e³$$D@ >IØi±Òú¸Üm‚Ž<*Cç$I²œPô))-‚vŒ™"Žå^" $c¢õ‡‡€ÛXùo½3o*r³ ÅÝ?øé“g¹u6Gx¥ys5×.HY“q¡òÃè¶ßþ³G—FRžÌÛ{8t¸ÂØ¿Ÿw†‹Ÿ¾±K°`VDÊ̽‹Ù^9\¯Ó™b1–šwM]“§¾©Yït;ûþÏ+s¹ÜO¥=ûÈ#·ú¦Úzm'ȆK$‚O@X´w’¿˜/)wøëwŒ:À3mìP2QÝ¿^÷ºK±Õ÷¡‡nŸ@†°™oqyè•¿ÕxóLtß·. »,™±w#àr»é÷ÿüTA¯×Çj,)SõMÍvÚ¾ÿ8­ßYZ°cÿ±Çù>¹÷»?~ä»/ýî‘Ì€Ú`¿wwŠl½D@"  m|’` °Ðó#Ž:¿°oVÁý,ŒÜuÃÝôqCiò¸L8Àk7Ê¿ôÅòÚˆZ“b`3ÔV²;åûB`!÷¡#À†ò¾L‚bI©3á~À}û£ OV¾Þ`üä¶ŸþúG\o#±dB–-$ ò¡ í®Òy…úãÔуõÝr…aø ‚v_vQ–ï÷ÊuT_ïòýçÀjjËÕèÂOò(T8^—/ ÛùøŽcy€ûãáo_aÀýB¤ûÃ÷þ÷W~ mc&– Ȳ%Í# ív‘ŽÕõsYÓóÄÔÑC<ß»v–.ÅÔqkìHkTµ>ýRۆĉºØ~©kzÉY40ÚÆÛÞD£È ÊÀ}‚û÷N§ü–þs¶`F ?A!(Iz6mO¦žÝΤkÝí·?b5™L/ågÓ_y^—ìhj}fµ WǤà“tãFK U‚Ó™&2œvá¾Ð/Çc±¤¾8gÎõé\†|Þ…¤Ì#èaÈ6;TïÊÕÝͶýo¸dº!¦GÍv4µ>3Û†ÄQ)ø¨a–Ç!"ÐÞÎ'>S]jqßÜ8wº’˜8ân¾†Åm\XK$½ùÐ^WC»c0š ÷ŽVäñ·ééŒÝhi}Š²Ú†Äñí…:ãUžïômŠJ—;þ‚PÆýƒûÈlµÞÅ?áHQÚûI^Œ@Û[®ƒ ±¦ëoþŸ‡§¹Ýž^²Þöæè†Éhi}ª–ÃKO7 ËË]"€eì‚ÜîøOu‰ºq±quþ5·|ÿ,>'µ>¹—ôRÚžL½5[Ñö˜Ì)syEŒgü°!± ­O‘zª«6~©!5T&N Xx÷ñÉ1}Çñ>À}„û)=;ëb®[j}âݲ>‰€Æ‚¶:D|˜¥Á©7ü“„BÑÐú TMuAãÃ> %IÂB@í¿G=íVadÂ}„ûI§7s1pØ §»"ÀSf•$;RðÑV¢? ¤×fgªÖ•‡Àc¤ZŸô ­œî¬HÜEÍ–I5ˆ€V@ƒûIgÐò¡ÐøÈgŸÇŒdI"äÍ”ƒ¯ýaÔ“.Ãb2mߣ.>ZŸiýÛ"™¬=\ÜS‡ÓIö–àÒªùÇÈ/'w¢ÎxìK—“Íî T*Ö¾Ùs¨,à5œürÝ.Ú4²Xoþ…;U¾{Œ†ÄMu/ÜOzÒcI;içP$Iz)mo¸^ €†š AG™êâhëa =¢-Ðú¼ðÊIå'¼9Ï9?+¤^g 0Ñû{¼/ëõÇtÛdQrÇý®Òãôö멼²†¢>Ùôµ §Ð”у©ŒÏýêùèžo^B£‡ô÷ež¿r-Z½ƒžüÑTqº–ÞùbíeÁÀ!3Š úÐw¯™IY©¾ô8øé_Þ¥icÓ5Liw>ÐøŒÙ}°ŒÆ+ t9fçüë…Ó¾gßXD·\yó>$jõ¾¿x#-X½])K¶s3ÓéâcéÜ3GtZÇÎÒôÒËèO|“LÆŽ·ý¢µ;èüI#iØÀüNËõ‚Úi¡Ú™a¨åD+= Öʇ—'¦ºpŸÉÉÜh,Ë‘$ Ÿ€IÂxef?Š#|„Öçèq;‰^_›—4dg h›ÊœŠ*Œ—¯œc§é™×¦~ïšYüB5ÐW›öЋï-%ËshìETØ'›¶”i'ølÙs˜&ŽÈ‚Žž>]¾•ZNúÉw® F›•Uvz|y°óÀ ú`鯏 >þõ¢}¼ï&J1µádºMÆS7t÷ G&§•›÷Ò› ×Ѥ‘Å”j l6yT1»?°ÐÓmea&PkðÔá+Â,. Ù” =ؤ¶; ˆÊ"$Ɉ@ôŸÈɈ‚6x†°£lüˆHðAs"ÑúôÏÐÓގ׻ ñºvtÒøüŽCåóUÛa;Aß¾ê<Îê®›sO™œ¢ÏVnWh~–ó‹ù¦Kg((WÕ6Бò*ºræ$å÷Á•t΄aÄv•ß#ŠÛÇ"SNúýÛº÷(-Û¸‡& /¢…¬9²˜ShöÔQ4sòH:pì$½öÙjj`à§~‡y@ß¼ìlÖHÕҿ篢£UÔ/7“ù9›†è«]-ÛD›÷¡&ž’Jå²x =ôߗѧ+¶RQ~.UÕ4(møá ‘‰§lÞZ´ŽË9Mù\Î9‡qÝ£Ö‹6CãuýÅgÑ™#¦óÞeíÖ¦’Êv ¬k/šJ˜êªM~ÍW~y•”ÀÌ’b¤õ»Ò±SÕ4‚ýÖ ýþ|74Ùé­…ké×w\«œ˜‚{í³UŒ“Î8SA¾©Æ£å§éóUÛhk‰ ´ÁóÀ‚<úÁutŠ£?.•§fC‚§¹o­÷•ZÛƒûLj|@r/è%ȯmu´~"æJh}PÐú„R¨Z볆§»Ññ“Õ4ª¸Ð'ôˆ4£‡Ò±“§•Ÿ|jš¨ôø)å7 + c†z§¾ÎdÍÏ^®Ù¡¢Œ®ö-¿”Óší¥A«¸05k©Ùî ¼¬4ž®éÇYš"]0m´RÔ«Ÿ®$«ÅÄÓnsi@ßzýóÕÊù ,0lÜ}˜úö<ºåŠs ‚Ùw®>_Ñ:Õ56ÓÇ_m¦¥Çèòó'RA^e¦YiÒ¨Áô FY˜y{Ñzjj¶wZ/ʳ³F ôáÒM´nçAºdÆ8š{ö8Z½ý}¼l³r­«6) üþ¡Ì-,@­Ü²—Þûr#cЇÎÐOIˆo”^xUIóïOW‘™……•5õT^Uë«áCÖ–AàùíÝ×ûúé¶kg)×;ÃÑ—¹õ@í°PºÂ?]~‹{,â‹ð.«”H¢€€|¢b”‹ˆÚ9’^Ó‹Ú´/* l´\×h£ôÔŽS+iV‹"„@ÃQØ7› úd¦·@xY‹i.ü†6dî9ãèƒ%éϽ¯h>p>ºzö$š<º˜®ž5™`O-FvFå°ÐÛxíÅT¦Ð xwæH^Öl¦¬a‚V£Š_ø°-*bmSVz*OÇyý&©´›íNræÑEg¡4žF‚†ë¢³FSßœ š0l aåÒžƒëõoÃêmxZpÍ™>VÙ ®Ú¶¿]²@mj— õ4Z¯¾VÑ>AËuÞ¤¤WyJöç[]Fu]#ª®S´MàáæËÏQ_f+š7‹ÙÄæ³`×¢·]áØ®þ¡6lNäRv¾ø·|\’§$Þ€@Çù‹ÞÐê^ÒF¡õ ÇÖçÂÁ&2ñ‹ÔÁNè¶W8éX‹—¹c– ²Ù¹®ÑÞv¢õ¨¡ÉÆÚ•Ÿ-Gɦµ;JÜÇ*XÛ1Þ—¶Wœ&Ma-Ê ÖÐ ï-¡_Þ~2åKÔÉ4= ô4¯ðeg­F *9T®Lãüëã¾Ëðírìd :@™òÁÔ4XœÔ†ØÐ™SÚ„ÀíûŽñ4ÞV6Ê®c3”òœ®À1_e|Ãgbz ×ò4Ú žTð ¶MyYéô›~]i¦ÉþöÎbJã6Aùó­œlý'´oƒ¸~°Èc£tAc†Ò’ »áôm¶JA׺ÂQiÛOuÉï+`"I" ÐRðÑF?€‹¨izÔM ×Ö'ì£óŠ´ä W˜˜¿ÏA·Oi/ø@S²ûÐ EÛ¢^µ³ãÀq/Uð‚—ñ'Ë·°]N YRÚ¦¹Ô|B3ô«gÒÃϼ¥8ç; ÖhØböó{yuÙ VaI” ­Ôgl˳‡ÛqFQOyMP^øâº:@,V­Á¾SyÞ2êy*ì¡§ßI»ÜÃŽÚ¢ Õ”ÒIž UQ/õ¶M¢2'X½>?é|Ô|‹´b_È8P%Û. b-´eu 6q™.=g=Ç‚´aó´ÜxÖlºÂÑ—¹õ ½Í >1¹çü±¿%m! Ù'’¶`Š7­jøÈ›ÇBëƒß¡Úú\>.O¼ôînïòvñ{¼ ëyºëÅ÷—)Ë'ظ†µXí5)¾¤ýÙ¦ö1K7în7Í›ÃØv&0‚!m$„ü˜ÆÂ”´,CÙö%=ÕBóYSƒó-A8íä}#Ûè\0m ç+Z“Îê†f§®±‰µB©ŠQôÂ5Þ%å"½½â¼ØãUn›y…¦ä TàxÂðAârH{'ǾÂtÝÞ#åŠ}Ú4¨Ÿ Ê@d¤Y苵;é4côûð(hñúÝ4¬¨Ÿ²¼BVëºÂQä{ð#(Ðòyq-®{ïjI1Õ%…ž¸‚/+“h©ñÑN_ÄŒ“pµ>s†˜šŸz»‡ŽÔºh9«ž¡¥¹÷¿.UV ýö++•úædòêŸ iä`8Ém#Ø’@È™ÂÓ^‚XYA;÷£¼:ÌátÑ~qÿ×¼sh¯ Š„`C´bË>úɳo++Ëîæ¥õßÿúlz• záÒØ\«ìaó§ÿ,P¦XSqWÍò®:Sóø%| ÆØóWl£YSFÐ`•°á_ï]¼ LM7^2þñáWŠoLÁ¦èú9SÕI‚>®©k¢Ç^úHÑÁ+ÈBñtõìÉôæ‚u¼¬”&ó4#ì¡a:6W˜BƒæB#VtÁÇOg8¢/Õ» A©lT.I" h¿ÇUèlÝþ³G—"× ¿ùùlì%…>«­¼eÞúàÏ>3¬xê}ߺ4ìÂü3>ù×r‚­hö¹ìd0H¿>-·Ñ¿·5+ù.f¢§æÂùmGr²à‚•=¬C»kN¸¶>Ÿ »k/ÍßßBËyê«§V|Á¸7TúŒW¥-e‡’Ú°ÙÕ6>RðiCFI$‰F@ >‰î8×®ÖAJ¯Ñžâ—Kš¨ÎîŽ3÷±­Îžq°Â,T‚]|ÜtG_®Û­xBî.]O¸Þ¨²ñQ¿ö„FÉ6H$=)øôˆn ­áj}þ÷|+å¦z×x—5¸éÁE=.´õIŽaõâ{K9ðçªå•M ’CeŠÀ²šãj=óú¢vÎþpýHY¥/(ë’õ»”xdo,XËa$–‚ ‚ö­ EˆFÔË%ñ=™ÚÙøpH I‰€D@+HÁG+=G>ÂÕúd[tôèì4§_v4?=‰°Âk2;[D$ó¿¼ý¥Ò4x•Fdõ5,ø ü†ÿRt,÷>ÞÇ/´œò9Ž×(ö_óÒË”pðöŒèðÃÒ…ì%º«åð=Oø2$ $ä^" ÐRðÑB/$€‡pµ>²7ç;§¶Ùû¼½ËN³æ§ÅÆQÚÝ]•çLÆÞ¥+‘Ë”U)>~ÎïüÆEá}f¡hG›Ç†(éþtFQ¾cö”Q”ÉQß÷±¶§µòòyìáÙß¿Éü˜ˆX]ö™¤ŽE–Ìí’¼K$=)øôŒ~ ¹áj}PÑÝÓ-tíè¶é‹ö¶Ð7Þ©§-Å=Ù ACAÖÖ½pć"J;"³Cûƒ­VÜS´½ ¯-Àj ‡¹€CÀÞD5 mZÀÌ*I" h )øh©7âÌK¸Z°ù› SéÆqmÂOI¥‹nbáçþ…´–cz%«þgû¾£JøŒu;RnfšËË¿[Fé¯h B\+ê,Ôb^ÕsÓžNÕGLPk¼$I$-! j©7âÌ‹Ðú„êÍlÂÄù—³RiTýnE5·*{æïk!l}Rõtv‘‰F÷ÓÓÐ,õÏÐS¿4eY´-kàÈå¿ýÇ'+ª=rÎÄá(u%PÝK¿¸ížk›2ŒjE .¬¦Õ(lä°ð(I" h )øh©7À ´>/¼rR©yåºFšs~ed ^jptÃX3Í*6Ñã+môû÷TÙä¦÷Úyg¼{KLÙV=ÁP:›#¿çðq.¿ÿû¦é©€·âl”² ”Ç‚S¼éwwC©òJ%2»Î¯ë\¶ëÁÖÝzõLß%õ1N>zçµ¾kgŽD㇑Ëå¦SϽõªë|mÎb£nI‰€D@Kôܧ¯–PÖ0/‘h}D³ ÒõôǹiôIfú°Ä¡@U,ø"Ø@ãZUÛlH d¬ÒÓö4}b…¥Ð`ˆâE±4<6èõ„­'“ÔøôäÞ•m“$?RðIþ>Œ¸‘j}ãú ÛOγҎ m;é Õn:\㢊Fonjl Îúç$§ý¢´…7”nãØ`zºbx ]ÃFÕÅY=[px&ë‘ÝåH€Bî% tD"Ùˆ†ÖGÍ¿ž§³&”M}LjõUcã­ÙCµvUóñIÖU² s¢ÞMkÝtà´‹ší¤cunúÛÆfeCܰïN²Ð¼´^’¶p°cÆÆV>zÖle¦õL;&m¡.¹‘HBA@ >¡ ÕƒÓFKëÓD&–Šú²‘sß.l^!ò”²ð³¡ÌÉQá´ú˜£¦h#ŸßXÖ@#ó tûš;L @Ýá¯ë5õ¾ª²{¨ñ¶¯ò@" HJäœARv[ô™Z”Jäöèsâ]1vF®`8ýìei´æ»Ùô—yé4g(;ÃSØ’*Ý» þûýÚËÇ’ÀéZÕ4—\Ñ•ø‘H$ŸôÞmZ­X[Ož›¡¬ðò„¹S£ÚXÇêߪdBØÇhl•MVúç;½¾Ãî›[ÂA_{ÓAßš`¦{g¤’%ÂQöªÛ¬>î”É..¨ÛNû»(:ª—¢Ñî²ÊjŸÃÆ<|ÔεÜö¨) “H4@„¯M·M2$â…—šVO©9G88g#Ùõvög³…òr[‡H{“› KædÞ˜¦ìõØHYi©”›•Fc† âÂ<‚ H¨/Cøzà+Ý6ÙL/l´Ó+ÛšÉÉ ÈØtˆþµÕN_t°sÅ4^ üÐí?Ìq¶v–çïÃ--Ž6GŒiðÀwŸR´‰›DˆŽÝOÐéºFªeG‹övÎb»×L(]¹um.9µ¾ï¾E2…D@" èàßÝ—%S$xñáå¾xýZ²a7Õ5Ú¼¾kLfÒyR¨®ÞÀ›#:­ÒsL+ÝiòèìôéŠm”‘j¥ ¦Ž¤ÙSF“Ùlô AÁVGˆžk¥¯I¡Ç–ÛhÕQ/Ÿ0‚þöõŠWiHi¦VÉ+@Áêö/æö×sû!¨9ujÖ[ÈEÁû3 P|»S^™fqW“ÑÓ¬´?~/œ~ûÛâ´Âü ÙíZÊã¿Ú¼—Û߬ô¿Ao =;\b±4Ä’½É!ЂàÁ››¥'7û1€SH¥ïÓØ0㘅Û÷ÞZ䉀D@"R𠷤υߎýÇè?Ÿ­áxSM¤wç‘É9„t®\zbg,ìѱÅpšÝôÑW[hÙÆºqîY4nØ@%b¹xi ðнtU:ûj¡ÿ[ÞDu¼R ôO…-;ä __Ê~€:¶G´ÿßÜþ:nµ)Ÿ*RÿŸ½ï¬ªÈþž¹÷¾tB¯v° رì}]Wq]ÝÏuWPAA¤%Á§y¡‚HÐ-îßÞ+ŠPQ¥(Å.„&-ý½wïïwnr/!å%y)$gàå–™9sÎoî9÷Ì™™£ÄM¹oúHù©+¡‚¢sp«èØèÈÿáWëÅõžÔ`ùë*¯j¼«ðƒAǺõ¢åPxËm&Ú¹cvZM{nT%VÏk WiY³¾åuÿêþ†×}=‹çäŒ#À8„¹Š2"íúÚ·,K¼»lµ˜û⢨Pž²ãð$4»†7¡ÒCø}*‡Ê£r ‹t‘÷ÊÇâÝÏVÁ©Útx#ëþpxŒxë/ÉŽ´›ws‘-†½Q$2Þ/EëU•K™!¾N:]¬L_#þùúRg8«;6UíÖ©ƒHŒm2¥‡ø"…ŠÊ ²¨L‹FÝ×$3ßgF :Xñ©•6z:?úâ_´lxåý¯ €tzé °ötl‰©\ƒÊ¯üÓSÇL<6Dù!ÿšöV‘Nß;Lóòw~qù3{ÄÒ_•äßnô_&ž%ö]ZD~*÷ˤ³ñ ùk­wGéñûœ,]ècD×Î)¡]çkÊß÷i§ûn(›xhJÙ›‚w¦É0û7¬øìßõW/îI¡ ÝÇ_ýp…ЭnÂð€GË>T>ñAü¼¹x¥X¹áWÇòC¼64\z¬?×§ˆK±Ò³¶)qËk…"ã•ïù÷ôkO¶Œž[V}ŽT>ñAü¼%ù«–OXºJÏ7p6~gÙZ‘+º¤&Á S5uó]SÙÄñ­ºo>î¹$F€Ø_hÙ^oEm?ä›:¿²² xæíe°ð$ Ýd«’‚ø‘àë…E_ŠâÌÔ ÀQ~ÈâóÀ…‰âá‹“Ê­?ʆÿR@l]¿BiÄw ǵ*ù‰Ÿðõ¢¯¢"¿+aHXú±›‡¾åʃõ:ÁŸ§µâÅcèâÅ(Õ}k‘‹ù`Ö‰+>­³^¢ÊU¹‡-Þ_þ-¦)— Í߯Å-=U$˾ ¡ô|øåZÌ6 8³Ž2äNû‚C=âÍ?'‹s2D/ÿ"³ª6ÄlqKO8tN–Ÿõà«°¤4jò»õNX–––‰%+6ˆ"8§b}–´ôT•xIMNQ®ûªåð5#À0„+>íà9(ÿê7ÅG˜AD³·Zʧ§.¨‰/âï“•?: Ó,÷÷©+_]ñ)±JÌÉç‡f—-]ùÝz'kO1ÖåY¾~£3{‹ükZ[ žhfÙ'«~ˆjÝ·69™F€hyXñiù:hRܯþŸ6mEø¢Ö1£ª5⯸Ì/~¿L¹¦µfcõ —?è/Û0£ª5‡­ž>¢›|6V~Wn° xRýÓ,ªÄø½~OMCø*Õõ¥O¼ÑT÷ÆÊ^ßr9=#À´/Xñiãõ]ÞZΊ¼´< ­ÓÓšƒ³Žø\ûó8ä’âcEAñ)—ŸÖã£uzZspø‹‚ün½†4ÌõªZ§'Òpíù'‰!'J~Hï®bÌ ‰ N9:t/ü¤W×T1áo—‰3ŽíïܾþâSÄ™çáéj:'ÞˆÇhÕ}Måð}F€hß°â٠;Hyر§kèЊ̭o˜#¼ ˆ?iLj]Ø6ƒV•&ÞI†††pùiEæ¦\œ°¡<†ç#þˆÏÆÊïÊM–a ûž¢R¡K-âuzútë$êÕU,[õ½ÃÞ©WŸ{"V_®~%Zxò²3ÁJ‡m.*¬}òÀC1k+2e‹Öù!+»[>F€¨V|ªC¥ Ý£=˜L(äÔ,° ÅþbGWê´‰w’¡¡!\~Ú†beZãåwå& ý~øøà§é‘¿î§ÓK ü&J°Ò2…®’±Ê÷§Î*ßÕaxƱýÄօζ'nüo[wŠßw‰Ž:ؽUç‘x$'çhÔ}…qF€h—DÞ¶Kxöo¡i¸ÃòІ“vË®Y1šà3°0 {ïJÎ$G}CUù£¹÷V}y©OzK’?\nÂfuƒvÄÖâµ;­; Bl¿þÑWbóï»…^±W(‚Òb!Âã?ÐYÑÐ+?c›wì=@+Ò@VŸhÔ}¤åq:F€h°âÓÆëÜòhˆâВп4Ì­¡®ö&¿[ï„­ã 8âê$å¥6݉uª a¤È™ùò3Ž|ñã”Méñ¦a«NÉõ[3(Zu_-ï|“`Ú=¬ø´áG€:ò„¬GÇ× çPzhFWyGÞ™FÊ_{•?\nÂІò¦«Ô _2” ´yi]át ‰‘Cò¦í»Dç”$g£YZ‰9)Á¥Q*’±}}B4ê¾>åqZF€h_ðîìm¼¾Càþ&')<ô¯Byk(ûnþútü -+ªù)ÿ^¹Ë÷g£kÌ鋈ÅBÌ£ÐÊËnXlj ‡ôé†MG“EúŸ†„’4à`gö  Z”±^¡‘²×«,NÌ0íV|ÚA•—wzû— ÔM»wc9o¯ò»øÕWþ`ÐrÖ|꜒(~Ûº£VøŸ]ø™ ]nøû•gŠ5?nK¿ÙàÜ¢a®{jWžÜ¼î1šuïÒä##À0.¬ø¸Hð±Ñ†„? u[öwȱ™œ–ë áÓ×)-)YäSäøáºgçŽâÇümu‘áxF€`š ½ŸjÍV$Ô\Ô÷K¿*_'“$2Çôv|8(Ž|9.¿(U<”s 8äÀØPòÝ<âötÓ³úŠ{Çõ—žßщëÐA3î9@P|cCCdiHžp>¯è+Þ¾1%´¯U×DM<ý§bñÍ•‚Q'Ç‹ïnK ýVŽ(—ÿÚ£bÄ«Ø',²¦ð’÷=¯,õI»oIåw³N ê×W$&ì­gŠyâµÅ»ËV×”MÌ}ñýµçÀE玉â‹oª1}$Ñ'’r8 #À´XñiãõLFC:]—â² SÄ;ìF~¬ø¯‰á7uǽ¯£ê%çv„#²S6Ч_þ]\4$Eôì# -ñÙ—…â (K .ï áß-Ï¥á^Gzô`çÌ;N‰ó–— ZFèØî†xéÚ¢K¾jLjœ+¶˜â†— ßM¯”φzem@$ÇJqåá [?Éå½!ò‡çuÜ{"¼"]þ¶Ý²Ú(Žíw@=sîM~ü‘‰W¬¥kí©û,œÿºSs F€`"G€ŸÈ±jW)O” …÷ˆ£ˆw¡}2¶â—õ÷ˆX,IóƺòüúwÑÅ¿¾ö‹gW•_‡³ÞØü²Û_Aù¡)AL,8ýßoü"í„ýcáÄp™è|Á’•!ëMÕ¸H®_ùàKñÅšÆY{")‡Ó0Œ#PØÇ§>hµ£´½zƈM[X5¹\è»Lñ¯g·‹.ö¶z÷Ã=â–»9Ca‡'¾ZU,~þÍïdܽÇ%%¶èÝ#JѾJCk…”u;°Ž¬=ž_S.OuJLGX|Hù™v~¢øj³)Þþ>(  Røv»)N1P".1'†ÿ0Œ#À´lñi Ô÷ƒ2»uöˆí°âD~üÕ/~Ýä§NR ñÉç…ŽÕÇÍKÖ .P$ö§ppŠ.~†'’ðù¦ Ø°Ó„r#ÄèSâÅ¢¿&‹X8zSøŠ¯šÿaF …àÖ¸…+ µß1Å»÷D¦ø ƒµ‡üH²¦oo.Ü-nû{wqèA{‡wˆN'ÐÛŸBÏ$Ml-ŠLñ™»¼LŒ{·DŒYX,®}¡PtÀ0ß…‡”ûõl+¢Õˆ0» ô80Œ#À´<Ü·|´Jö˜"%¹n+M<†xhx‹†»ŠŠ,ñÁÒñóÆ€8f@BH®XOv`Û„ý(l-¶E·Ä}™ë”¥â€t,ÏK3Áèlèq`F€hyXñiù:h•ÐðT·.ûúóTe¶´ Í[bàQ "&FŠžðåéÕÝ#¾ÿiïv]a³`Õ¬­úú' sÔ±nÅ„¸qPœè %±wMŒÄÔöDà°lc¹µì@(}~ÞÓöŸê60u„ç?Œ#À´"ö¯ñ‡V\[geÓf¿83»hQ^×Á¹&™_}{·3…}jf_Q «Ï+ŠÅªïJœäÑñ'Áê±qËþ¥ø¬…âwéañ>É!çšä?ï`¸Sßc‘˜ü}&,*ËóËŸ£»ê‚”(ÿ~¸°ãµçŸ$~ǪË|ñ­#ú!½»Š«Î9^¬úa“x÷³½kù}hqÒQ‹]R°g×nñ*fs—Šë/>õ¾S,þz}MÐñ}F€`šV|šòý£Àåß”8ÊÌÉÇ'‰O—ïÝ¥û÷AqGÆ/•„X»¡TÐ,> ó„‡óÏJ+×”8V¡ðû­ýüÍõ1âÄ8ñÇ#cŋߖÏè"žó¾,s~áüßüZ¡3œYi˜~GËÜtlœ˜ñI¹ž§µŸ÷éÖIÔ««xkÉ7«§aÅð)#À0QC€‡º¢eÛ"dcÕ¾Wþ·[œ|BRhåæº$¬ªô$%éâÈþqâ58<ïoÖà™¾´T\}dLD+/“º®ô¼—Š!?,ðÖ†ýg¿[O§a×õ•~ ),]±éSÿûTì)ª¬ÄõëÛñ~¶úG±q}õèÙ¥£³;ûo[wŠßw‰` âÀ0Œ@kA€->­¥&Z!«1\E¿†rvÎ~ ¿¡Ù[<ßû?ý^‡Õˆ~ûcèEgý/[B¬ÓNëªúñ-SÐ*߆®‰ÆDãcËg³ué˜ältºyÇnÑ´80Œ#ÐZ`ŧµÔDñ!±Áý<œlµÈ¦§7+‘“•VÆl¨ ÞéØÐàÒ ùu±Èo(³Ñò»r—Qõä«¡Ÿ‘¡ë¢CbœØY°wˆ³þa£™?nÚŽ-=”3¤µqÛ.ѯO7')Y )ì*,õìâœGò‡hE«î#)Ó0Œ@ûC€‡ºÚp‡+ ‰qô%¾×W¥U‹-ý"ŽV ᲄݮõ4<ÉgïòÇÚe’?\n X(’¶Ù̲d(= Š÷ÎÌséT=Ò.ìó_ùHlù}HŠ|ù“¤°"/99''[U:î5ñºwéñ‘`ª°Å§*"mô:»l+- ” ©êž¦ÞR0¶ `JxRTY ù U†_P˜²õÊOüPPc£*¼GÇì4lA‹ŠV‡­°´\áé'vÃbSWØUPÚ±ý¸Ãp†¸È߇Ñ(,)­‹„O¼YʆìÜ,E'b!ÀŸÁ¶e" À=RÕ••¾³U3ïò×#514ÔÕX†]ùi åÎÁ­%פùþÀg4ä'¹Ý_§DSÿ‘ì” ZŽòÒ9%2å+6Æã ¥ö; ‡8û„#Åçptvw”$v`J|$xƒîÙ#)Ó0Œ@ûD€?­Úx½»_78›ÆáK:`o…«O÷V+µel1ðÇéœÙT{;î†2.?uÐ݃ÅÖ˜> %×äùˆ?òGj¬ü®Ü„¡Ÿäx('=,.…5 CRu…­; D÷Î)u%sâ‡ýñ gÁ²ƒa­OW~/–­þ!”¯gçŽâÇüm¡ëÚNˆ·Ck´ìµ•ÁqŒ#À°âÓ†ŸPçß`>Göé$Vü¸MØÚn¡Ù[äÄ—­ívJl‰g•qå¨/Ãn>¢A´Žê“*ü?n)æïb¹Ãm}Ëmhzâ+©ƒº5Nþªr»XvKÐŦ€ð‚‚”ÀÚÂâoÖ‰.>M,ùf½(.ÙëõÄkï“-ïåœ:"ºááÀE玉â¹E?…ß®öœò–ù¢_#e¯–8ßdF ê £-žÒ×¾î(º8êÀ®"ŸýfÌzŒúDæèÚ\˜?ÄW ”Cº% ƒfaUðM244T•? îõ+]%4Uy!¾†ÒV>â§_É*gFS4ä—›°4 Côꫠ܉¡§ŠIW5²Ÿ¿m·XóãFql¿jLãF‚¦£L¹×îñø#¯X'ê^#^ˆ§L‹†ìnù|dF :Þ£TGïµ*Ü/R ¨ã‹‹ÇÔ NÎÅŠ-Ÿ}ÓZ&~ˆ¯~]0û K ÇÄÄ8<ï®õåÕÍ.ÿñ¥ŠD«@Y²¢¾äš4ý‘%_‰D»@ô‚üUå&,c`á‰ÁPgïD嬾¼swaò,X²R,ýfCéjJ@+9±¦nkñB+B÷뵺¯‰'¾Ï0Œ+>­ëÀ·/¹àJ:F%h-.§;_,—ëÛ5ÅN°ômÂŒ]Óâ–ÇÒ>ˆŸ>Øä³;†FbÑQ¯ÔYï$CCCMòw nŠ¿hqËYzˆ.Áü¨Ê_UîrLcEj‚!ºÆšÎŠÌ¿ï*¬ÓòÓPÜ#ÉG–â¶³hŠºß‡IïV¥ß>Iø#À´}ØÇ§õÔqHÙ±”UR ÒuÃ{ü ¹Ü!R"âbñE'ú÷ê(‚Á øy×6¡â‹…îïß">?äÓã »ÁÒÓ#A‰¾©ñ"üÄ+ñLÖšh uU'¿Ø•/Ž/*ⵈÏùôÐðYz¢-uõNØ––Ɖn X2Àˆ˜µnbeåÔdR6k÷ù‰ökB>=4¼E–žhË^¯e ²,«ê2ä¡w®º<|`Ú&¬ø´ÂzµƒÖ¶Ý{Š£¢øÐ°)ÇQ*„¿Ì/ë‘,b xü´OÜ ¡Y…nuÒêÔ¤ëüÐ:=4eÝÒ· [ß! ¨v=ãMXzD"ºKÄ#uÒij;ÔÕÐjªKþ±—Ô±EKÅ.£»3Ûk‡§{“®óCëôДõî"ÕÜêìþÞ+!úò×$·ß]­báeâw3VÐ ®8XØãc0Sç:? ­ Z§‡ü}hö920löM°š¬îÃùÜ]P¬‚@ë^Ë!œa>g&C€Ÿ&ƒ¶Á„UÐïÏ/,õË(( ðËiL(ï5%"´’…NŸ^øÚN4 ÅÆÝ~±3°C¡ˆP «<ã'Uô%±]&TøQÐ1âÐÉ@¯AщsøêÐ!I$&%:çÄ+ñ\>³«á†¯Hå×[Ej ÖøAQ˜ó$üZœ°dôä×± ­Èì¡Õ³¡ÒÒè]G#Ðdò×&7­¶lc_-l«*âýebWP…%v๠pš$‡xLƒÇ1ÁÆ¢„–…AMi OsÔ½Ë;½Gô>JKió1ú  F€h§D¯eo§FYl§QÞºñ×%É;ß±êûMâä£it4ìAÎÍäçAÖæ‚Ù¿¢ó*­®È@IDAT+ŸêÝ «õ–aá:ÌH؈W%¡Þ:«ÆêL©¯¡‡Í¶H2”ˆ÷Hg(‹,;¤Œ¥¤tÉÉø‘ò‰Wâ¹1Ã\.Ï ’ß.n:ù=Í#]rS¼®c5k ;u²JE)tÓR³þ”¦ ‡E HÒ†)NÝ7“ìá|Ó{„åÆ¿ÿ÷]ŧ‘Oux |Î0û¬ø´žÚ 5ȾùòªÃ»ã‹5?v‚âÓèÞ'ôõeÂÙJ«Ì”wŽ 3hÖO™HÆÔdÓ4¥ˆ¬î ¼…‰x ²ÜÎØƒiåq±qPrâYzHé阒✓2D3‘kíqyn¯òG*w¬>AÔ{ ê½<Ž£YïT-Y÷T>Þ#¥l{÷òߣ©Œ´ŽAè]£xŒ#о`ŧuÕ7?PÃlý¾9ÿ™ÕRÞ¾á×-‚¶hlpœX a)Li¦@_ãäCCCJ±p&.…ÕÇñA'hÙå!w—í†*@ÔéQ YF㮑¿¬O(¯ÜÚŸ o‘¥Çê Y{çÔìö§½ÊßRrô­¡îéýYýýF™ÿËO¯%Ø´Êß/[×BV`ˆ#À4¬ø4Α”â~…’âc~üÚËÏ\~ÛõϼýYê„›¯Ðbh¿FêIé à~…“1M…OMŸ2r~­°úXØ);_ÿnYdÁ¡á«rkOù 3Ö¢¡®„„xçGC\O¼F;´Wù[Jîðç¬%ê>€=Çðþؘεgñ¯¾ ~ÈÁÌU~Ü÷-ÚÓcVŽ+>­«‚è+”æÀÎÛ ×}ýåýòø“¦>ùÆuËÕgGeÈËíeÖ˜rëKŒHˆw”Çñ9Àp,>~@®µÇ‹² cOHÉ¢­haÆ"e‹¦¬“Õ‡™Iá)_´°ÜÒãZ "-+’t®F2·'ù›[nª‹ÖP÷ôÞäoß-V.[:·°pwØ¢½7\å‡->TQvˆ@£|6Ѱ=Û!vM!²;ÔEsÙâÿ½¾¤Cjê?q~‹xY¨ÿwŲ±–êÝ)⤹SÝI!KOŽ®äãCÎÏdñQô«üÔWh*Ïùaº}õSydõ¡uuH!eÇãXÊgpOMÚ«üÍ-7ÕaKÕ=YzHéYþÝOò—õß=óùï.;¥øaå"Gñ!«*+>C+C@ÉàhS+ãªÍ±ÓhÅG*™TNhsÈ´Œ@d~§F™v{¤†ºxÁÓÿyæâ?ÿ•êéÿmÞ±Kýù¢S´hùüPÇä¬ðKІ¼…ÍåJO¹tù´gxƒÖ×܆)PH¡¡SR|œ®I*¿.wzvÓ:šøO9?íOþæ’›ªÏ­Ïæ¬{òé¡á-²ôü´vÍó‹^z–†¸ÈÚSŒ½SônÑ;FïF µ!@F„å­©¶ÆO£ò |é>bêÔÔ¹'îjk5³<®âãX|P65رo?ûßçNrÁf{ði#øïÛ>¬:iÀ!ràa½µÎuL¤xPÇDCPd!gfׯ'ÜÒÓXŇÊrTžëììÞkfœâÚ«üÍ!7Lå¸G·ž›¢îiš²N³·È‘Ù6ƒ…+–~üﯖ|H–²HÓ{D¿p‹+>„CëA€úP«ØìŽÅÎ~i=\µMN­ø`–ÎBÓ6sìû2@ôm¦f•ŠLðäçCþô•J{ è_|ðî’o?_¶ö´K.»¾7 OAg¢:ÄǪŽ)‰2΃…q¢ª*9U¯ë[ŒÛºùª^»÷[˱ª¼U¯ëËgUy«^×—^S¥¯*gÕ놔[UÖª× ¡éæ¡m]h…sZœ¼JË2 Y¿öýOß~ë’’¢ÝH·§âW€#½KôNÑ»ÅÃ\CëB€úPÖ5}jëâ¬íq•ÎrXföoøž_ç˼¤íAÔ"‘£ )!©KL|\Šaxb1•ú}Œ@«GÀ´Ì2iiAiqÑŽ_ׯûvÝʯ~ÓdÕ!%‡,=¤øDŠkñ¡¡.V|‡Ö…@Z¦ïð¦¤ÀÒB?”>*u :ÖŒ)?®ÒCÃWdÉ!k3)GR~¨¢)D®¥ÇUšp‹#Ðò¤ysTpû§ð\èØËès¸×{3=Çš¨X|¨¢Ò2³‡Ï"¸ýßo¿cùÜÀ4®â܆ìðsjÄ©1'¥‡"g$«*>¬m·]qß ²ö8k_áHüБ®Ã×íqóâ6F å ß´ôÌœƒ“ø]ÊJPh†ÕÎ1-Ëw§²Õløû¼ <]ÿÆ–Ÿ¨Õ YrH±!ëýHÙq®µ‡â©>ÝN90mR`Ü)>áÊ}(²CGúQY†80­ ²ô8†¡®•š—ù`«b° 3UŇp*W~ÄL|VcŸQì󵧇êÊU€HÉ Wx\¥‡â)D½^ËÉò_F U àZnH¡¡sWùq ÷ÚoL3Œ€‹€ãÓ#Ä#¸ 51–•™æ96I‰a¯ Áþ|LÍ;C^oã÷”– ½ÅëüD¥R©Î\%ˆú¹÷ÜútQ)‰0­ Wñq­>t$%Çý¹÷[ÛÌN{F€Öé)Ÿ²®nÀ×ÅXæêWà1,Ï—õN{Æ¥%do²’ž77ÝŽÅ ïÀ7YT2ö?[•Tù(”9D  /†(av]“Õi¨>aZ §‡z ´07\<#P3xP“0úÑ‹ú¥uzð™ºÍôC½=½eŸžšqkʘfi1FdM=Á²­‹ ÈP|z¡PZ‹†#À0Œ#Ц€âSX±µÓ/´àïÜì‰_¶iY8F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F ¹Í]`uå)¥äž3laŸ/„¤”èÆo„=ÍQf[)ãØÙ?u,µ /Jž.¤ê/”HÅ{d)!¶I¡Ö(¡Ø¡ÃQ‹¾L—Á–”Ù­c™h¼8wâÄ]./iÞ™]4³ôB“¿Ï½7ãU)%XçÐÚpë“ßÙÖ^Sí›?£%Å_ž{‚gÛžÃÌÐîBÃ|`8/N+Wþçl¡Ôß­@©½à~ù¦&Œ{/ü*y,K†²Ç!+>5W”¸<ÖÚ-òd™½IXñ©§ðÛ‡ÏX{°Á¬«è/x§bñ!áüwÒà‹‚þ^&„5®¨`õŽ#f®~L¥3×ýãˆB'²žF>üpl`[Ñe¶²¯€‚5ŠÖ*©‰/E¢ñßH”U·ŽcJä'(ÚQ|Ò³rn´ƒe³,)_—–ÀA¾RO¶š$ùHïÃÉ~sÏ:CÄœ;Ç7þ»&)d?'êÖg}ßÙ4on‚0Ÿ)„ýMž/+w?‡Ùoåh-Åß;÷{ŽÝºû«o`íy¸ªÒSOÚí+-e~ñÖtí¡†F½aa¤wzŸMæÆ¯ÐÜŒFõ=¥Ë±ø \Eä/P†¾îóõnå¶—+-3Û;,ÃwZÛ“¬mIDVÓ#X5V‰à·8¿ u¾J¨ÎPX&‹]Áu‡ÏZsqC)ÛZ0ÏÖC°&­×„>ÊÏVe‹+{•u÷7„åO×ã}¼w¾/óXDÓJ'Úù:‹‘E 9ßLRùѦMzi™¾w£I¯©iÝ–á;P·/Ås{ 磚º<¦Ï´ˆÅgÁtý*S™Ïþ¸T”55J•þpââ9)—Ÿyëžy°Kå} õHéµñtWëR}ÜÆeܯÅðüš˜#Xý$:Žë"òõ¶½ ÿ+Ç®¿kЬHiаF~péõx!GÎóe̯È÷r¤ù«Kçõ*-ßô£I(Q­,x½ÒK“[[•Ø‘9ýpSޝt³_Ü‘“Ó½´Ôþíñl(Ï@aoÅÜ2kmfW|ÌÔ/–ýïÆ–}ZAaÁÛËs{ 91=¿$Ò îÑÍ úÿŸ®‹kÕÊÿøñ…é™S&Ã×èõ[½÷gX¦ÿZ[É!ós2¯§Ÿ–囊&097'ó6ºŸžåޝܑøÚ=—«¤ÔïÊÍžô©—9åj[¨s`YzJ({&¾²Oôˆ˜ãmaRt(8 ¾•“#𵌛dqZޝÞgr}YOÐQ@ˆE±R]ø¨/ë'7SZÖ”“•²þg¤œòˆwü¡ö†a™¾Ç…&þ'muº’ø·áÛñªfÏóNüÙMy«7çXÓ´Äõq0lŸsÀûƒ£î}¤C™¹gÚ¡¶OÂêSªiâi[oò‡ù¾Œ©DãùçŸ×}³þsÈþLÔc\ºi¾÷¥Ôæäú&½HCeÁ‚éðÙºž•+4CÜëÍ\NéGÏš_´£äkÙÁ¬ŠÌÇ Ø^††p¢réÑ´LÏÌ™‡Sãücú§ :Ô oç„ ”žÿâ8´Qòƒ,¯1sUpíØDFë<KvYš¶±¶ô5=cxÖ*õré¾³6™>øHÀ;ø ž9¿æ×»ÏIxx?{˜fÐ ¹‡à~ª”^Óä£y÷e¼Eéê*³üý/I¥Ç;GïîX¶hR½2Ï—å*q‚”»Mæ’Ït¡{ã;އçt¥&c.ÍõÛ@å Ïšzœm[ô¬Ÿ«ñÂù¾,G­‹Êï†Ð;àÑþ L{608ï˼k¯ž?¨ßƒáÏzMíMúäœó,+ø`ÑØ9üI]¦A©kÚŸçx3¾¦òFdM9Ó´íjº¼5÷¾ŒwéÞpoÎÑVP½”šì9‘ÚA‡÷ ÂГ82ƒ…‰FâØ½£wSz´“Õ¶kˆú•âÝP×;ûPFÆV´cWæù&-£:ogÐÍËGF ©ÀÇZó……3â†ÒóL”—éÁÛvo™ã^DrDÃp$ºP=9ѳ¨ºô â> xÛô¥ÆÿæªôÌûû¹iɧ eºÚ{tÏi„”˜„Nþž#æp4æïÚ¶ý. §Q<Úödô(—BAy qÿÍ#»ƒF‰âÝPñ5¹ ðhX¢Æý\à4oä”)]ËÉüÙ–€”·¸é£3¤!×WUzÊÓ¨žPzþ üš¡]* y ¸9É Zy.[3|}¡ô,…’±H‹ƒ‰YÐP>yʰ‡ïY˜h$]ŽrK”ÔnMô$ž”š0M ßûO.÷W®;çÝÁëMÔIÐ}¢ oˆ¡ì/èÚo¼¹JMÞ&<Ú)èîVÚ¦ZLÞ¯[žCÕ_‘Nl…Å)À`.Ź!Ô€‚NÇdÏᛦ=`Õx`Ó8¥' 8(鳚µ†ê´Î@Ï,:ªçà2=‡:Ýê2ÔöŒUMÓ=y™4âII‡¢ý•ž¹óŽê¿¢j:º÷@Ö=ž˜³e‚S¼ºô³ÇŒ)…µbè<Ç;ñ}ø¸|lËÀÿCÚ,JïßZp%:ñ êÛù º†¥'­Àl²l8×JyÓ2sÒËLó¯¸v¬"h•ºv9¾®œ¯QJ7<+‡•B^væ£a7^@ÙYþRõÜ{eÌUÊžåâ¯wˆI XÙ¶=×á«x«!È% 9Ñ–•ó˜v®{ ›Ó´c«áG᫸÷x‚r†CI#i7¾áe¹_zø2\ˆFòþÑÞYf{Çì´…vö¿ÑÞ¸Ùþäläy/¨É `[K [ù×¥u–áÑŽ™ëÍXYQÎÃ2³Omâ-Ô¨ãü§ùÙ™wU¤cgÌH¤sÝÔ¾ B«x¼HòœO³›¦=|èÛ~vÀºÏjô`Àt/˲ŸÀðÙÑk†€¡±öÐÓè3!ßÜ·õ:Þ›ŸáØüHO½ÿ?½Þ¡NÞÚž1P)áTÊ#£Fùñ|[›Ä!uUHÏ™"kÇÂñÑï¸-ñ¡!¶ˆËÄl·Øn)=¨\¢‘–“óoU"²Gxs…žUKÝ„gûÙÜŸŒ†í¸ Ç'sÓÓƒÔù‹{#þ 4PgQùhd £@5âÐ?RìÄ—WüÈšz¾ ÿ€¼ “Šþ¬h¤^z¿7×?¸ÉüôR\¾N Ê)ë©î4V”¦j@üêð{`ë'4¢Éa÷B¤Tðþš{r€|‡¸×Uó¼«ÑÁm.²K† }%?˜sµ®é×YJuB{K_ëï¡E<8|YÊ:ô¶Í¹gÒ*t&!rhàßœ•ž]Óž%;±TàšG?lOk!cÍ{qáXB7£pEªŸù«ýw¢aÅZCÅ È1#¦NͶŠí¿CÊÀsz3žSÉ"„Ìõ~ÆÂ ôzŸ½ ¡{R}‚¡¤E†¦ßnÙÖóx¯€¶0·c’ç?a q„eÊ—\¥‡èçedl½¦£ìˆ» SªUd] kŽón‡x¨xŸÞ ]V>‰‡Ê™`™ÅðV¥ð“èNÄíM¥ìPdl± wÓÇÒÖíSÃ"¤0|ö¯E+×åÅ’o®ƒŒú}Y «$ ±ã#è{| BGñ}¦ÆvßÙ*•À—­ ô?Í”å~=D½@t Ç-|À8-Âð-ÁË+Œ¾ý~¯b–qd–ï 4ü±0µo ÛI^DC‘2"Ë7„ñС_¤tí Šó Óét`’~EjÚ£a¿c¤1›ÒT„­d¥q/ª;Âwe<¾!n3ÊËõx´±(ëG7­ó-Õ?ñ•vKŽ¿BîÿÔA·ÄÍ_íQÒtgø$…ñ Ò8<¬6½{SŠw0ÈqÞkê©àQv×'.Å—þ+¶TWÑ%Aúº|›’ƒ– >1Jç(.èt> +ûyéJßJ~"Åhdc˜nVøíö|>hÆ÷Ý€èµM‡º­>´É_.Ï—ñ€nÄ€NöðÍö”!Nþ†>c…ïè´äDO÷‡™cŽâ>7{â—½<ý”.&CYÿÛîÂÀò¦žTŸ2ñ nª(&tºxCÙ7а­]b]‡ˆ 󼙟‡T:Q±x¬‹+Ýr/(·n•èaøäwB=Ú7‹s¼à˜þË ëøý¿žfÙö5 ø }ôadé5ø0þq“õý©xËÎx˜#'ÎÑVÙŽÅ®!Ð@ÞðåšÛ5~g+AÇ­ ð‡¸É8£©çªì,¦ÖdEÛtüN>©«„žbâÚ|é[4Õ¤^5}Pˆ;ÐÉ~™{ßøÕyÙ„3ô•™CN½7””Š•°š|žç´–ò‘™C6»Ñøu˽/ÓÛ¯J/Òk8sŽ"‡^ ;Í¥<˜aÖaw¡5§/¸4 ¥äú¥ZK37ðEu1|aîvãrDu|‡†ì×Á±:Ͳ+7vð!•d:¿³R|;¼heÀ°G“½¿ø88úˆ¾ë¿ö®#××Þ¹÷ŒÝž–•³V¿È÷^$ÏXmô+,2#ªKSaQz q/ÁRƒs,ίkL™° ¾•o/Q[ìOγ•ºCBÎSuå£-6°nVÃC5äœ[‘´7JXfåD’|Þƒ#÷9à –b,w@AÃúH¶=¯øÜ_äZÀÿ9^\b…Û½Ó{ùƒ¶.¾ ݬí„ßÙÚÐá¸F ÉÎJr•ý|"”žøJ÷¢|/š3#!I%QJÚïâXŠá“‡æÞ3á—ô{§÷E›1fá[@çÂp ….µ'La¾KÆX‚òÂ˲2ßé™ÙßëF§LQ§lÿI1–ú–|\ÂÓÖv:+à6x}iþ.>ﶫМ€{´zu(¹™0Kó ïçs}ã×…"pâžÙ¦ þ ÊÛ4Rªd·”|ÿö¢cu ŸÌÍž´Ø!©$¯€%gQEG#âõøEŪø)Ü¿J¶£<’å 뇼 çØ,4šº‹@ž7ðÕ˜õõÚ÷§eMˈÕ~ñ›…ÿ@Ç8CgDÊ6Íþ–5åziÛ á7´,Ä_¤ÚX:X9"zÞ#¶²M*£FŇø¦5X1çyÇþ?ƒ1Ô{žÛ^ÒÐÞ¡²#zÆÀäðÉ9ÅwŒÿ˜>LÈ÷fÏž²nxU [&=Çxþc[Š>€NˆI¨¬„³ªKý1Ó¶–Á/.+6AÎÓñA¿(>b®7󳦒ØÖÞÞ$y–‰B3~xfö9˜öaˆ_ÍùX¹JM¹ƒ_<½µ~ïå[ë{Àj;mÜ´PZCÿ?¼¯·ËÌ™˜dÄ狲nÌ ƒíiƒèÝõõPº:Nø­ Žn1ši¨ SF›>D\¬*KtC; /üqpäûu@͵0qŸì‘ž“`Q¨d9"³:ÕßàïsTJrÌsá¢`Zõ |:=ЏÙAsç,ÝŽÎyž¥éÂÓÕuŽ/®)º8&ß\²#`6À y'ø{¸j>˜âáä¬ÎBÅá ·qVŸÅt×K ÛU~%€ßP¡²í÷à¯3Ø¥Œx²dµU¶BéÊ¥ûäԌ×¹#Ñè^yÐ^†¥3ÛÊý{è}EÆz’¯‚,At¤KýÁ‚-JÀ)Tj®y¡‚\•ÃüìIÁô/ÌõÏ9ÃŽUâÛÓ%ž—ˆŸ÷ã"k/Ã4E"¬~3é™Ç³ñ¬}‡`–Ô¥¹ÞIßR¹‘¿³Õ¡Â÷ZôyMܯEg ¡iÃ¥_cÈ¡/Xõ)…|R~÷÷è"Æmq-õÉž–ÌÁ*Æò×ÖX†§¯îœö(:ÿ¨v¹SÕ4ø’;CO¯%§Äõ˜y÷Ý•üª¦­ÏuÚ´i)1e2±‹¼­:¿!’­¦¸HËIËÍõˆÍ…)dˆ4§«þ3Wm@‡{Xõ±Ñ¹‹åžXw×@²€Öh&+Xò˜/cc¸¥´j¦ºž±ªé#¹¦u¹hx­¦r›¢Ìêø¢e'DRRA¸³´›®©x¨­½!§ð|φÎä°íòÑãÞÙe²ò“e­!ù9#ÐhÅgº†é‘ÎbXMŠÁ¥ãîÑ¥ôB/h›Ê£„ÿÄ" ¹}ŠÅ3Û¦”,U$>så:|©ã ¾é”‰Ç×8¬éJ`ÊŒ#À4?Íãã#åv|6©tÆ4õ¶©ôЗ]0ü3f~ 9ìeôqÌ×M (oÝ(¼SÎÐEÓ±‰a”Á`¶…@³øø(¥9ãýM M_F“ P3qÓ§QõÁ,ŒW„Á®ãpÍ98¦Í# #œ]Ó `n³ïT#`ᬌ#°Ÿ#Ð,Ÿ„Á§]úùG4e2¥©ð‚Óë‡øn*ò-Jw^f&­72¦E™àÂ[êC0T§ÿMc˜öæ¸ÞJœ—`Öƒ@³X|† ù bÉ—›TlÃx¾Ié3qF !Ð1&žö5k2‡SÌ’útõ]kE"3+Œ#ÀDfQ|ˆSCêûLÍŽŠDDŠ.À”KŒ@û@`Ù¨~ÿüwSI‹)ÑZ³©øbºŒ#À4fS|.ü³DhÑ»¨8û*M3ÆG•(cöŒí>Xf¢¾a+ÞÓ/Ö9š-¨ûÁ3À,2Œ@ýh6ŇX‹×ï„u†üU¢°ßÌôKÆ¿ˆA¢50Ò¼¹ uÑŒ4]]tÜøúУõ‡êâ‘6%¤ÍX]ú|l[¬¹mÀ()#£*†Ï ¡ý t[½Ã­DïAT坨èY³â«‹Šä=¬._u÷hRjÂãèý¥÷8ü^cÏimžHhD"·1‘ ÉiZͲŽO¸Ð gzN°,óC´ªIá÷rkÏë—tÍÕõ]´^Ö²­¢Ìó±Js¿ð²iû\_‰_ ~kdßn·T·Zi¤éj+«>å–ïÜn=,’ŒKU‘ùü¸O"ÁnÃÐGÎñNü>œÞpŸ¯·U¦VkJÞš›“ùLxŸ·-Ž˜±ú¬ Üxçw),]“×~7fà+‘ „w`µ&õa¹Ù“>uÓ»Ï]\·”þm{¾À‡Éçó}™!'lZÌÏï Ü;„ò¤ef¯Ô í/ó¼«]w‡ ô—X/ ]~± ¸/7;ãÿÜ|·z§ Z/`ãÜyÙ™ÎJÊn\MÇšò Ÿìû£e«Ñ؆’Š2ȹŽö̪£Î÷Ð-oxVÎPÐyÜã‘gÍñf|íÞ'åb³•sö]»mâv4Èceù)­0æ`•óA°êYxß÷žÔ¿ÌñÞ†ýñ*‡špªœJˆôÉ9çaeö¹ð ØN1£¬ë°Øn:nc\$øØÖh–¯¥p/üRóçá¥kä!òywèÐz+=øJDƒüÊßÇq3=Ãw™3{g šïË: DPnüýæpþé<ÒtôEZSYá4#¡g)ó¬«ò”HM-Ñ…ö*¶cð;3ÙÞ4Í};=«T<? /‡ÏÛ&kï>ú.l°w¯¥‰)Ë4!‡FªôPPªŸÊÙÁ£A+BtÇeãíë.õ½¿>y)í#ÞQBz†ižØÇ«æEã‚=¥Ä3î– šO¢Q½¶¡éj++œf]åV˜óÿ$õ¸çÉú4Ï7éM7¿4ä2!ì£Ük:ËÊù;†*Và+õ×ðû|ÞvX;ö艚”Rn­¿”òkM7¯;°~3/eÌsXLüZQÜ-øup»{Ö½Öt1çsíJÜ›k{wè}‰ŽB{SÁò?e®"²ºîùN3âΚXA1N’šò¼·zÃ9H°ê‘I“œ´y÷e,ÄàA4É{è–ïÆÓ¹Ù™Wã³[÷¢ËËÍ žÄñn›ãÆbùùJC^h¨±ÿ/ZÌ*¡6œÂ“¾·êûÁøHú5×7nÝ'Y€Û^£kncíQ|ÜóÆ”nºtœº_ª—áuþ¿º| váKòACÄ~ÉÝv£fˆåeOXë ìÓˆà ¨[ ) †¿ ±è»ïÃi:!j.+œjíô6ÛS†œÕÕîq£ßyߥF;fcsÁ›Eß.SÐ ² L;8’â’×C?Y‘ùÒAá‘Úßþ2öèÖŽ9rkD]9¨TÛn<Õ±è¤y§ÅఞƋܼ1qÚgP‹¾¿níÞ‹ôXÝ»CJ^ÜN´ówˆŽ”¿B+pÞÓyÞ»·Ñï@5ïw(G¥“šòš$«pèý'« È&oßV‰]TyÃãËž°†|¦ äTâi³ùóð¤ÚQl—œš–éû'†ýÒi§y'oßnÐÆ¾Ã2}OcÈë”{¬HÒÿN×=¯'7Î=ÚÒî…ö-Ô¶9÷¥ÚX&JûÐ9·1.R|l ooÙpÉ8k8X°àáø>²,p.Aèä{AŠA;Q€/·¤­}vÀÁ‡/0t 6€&×›& J$kº müôÄ«`Ù¾Ž€‘¦‹”Í:è)[Ý€ÎlŸq#¼9ƒ,Ó¾Z&y†¸E•™\!1óaBƒéÞæc;A€¦¹CTŸW©)ÏÍún°Ö®2$RÑñYx·¶a÷ô5JùpÝG8V/Ùd¤Ðž³”ºÙ« 9'/‡[/¬€¡'Äï*)µ¿º-Ã÷¶öÞÝÀ¢œl·Ý;'íB%K/,@ÅÒ4†nuyóNZa:‰÷èQ ;}´h庫€£®L«RùÕ½‡ÕÑ«zO o[Ⱦ¶ÏVÒ˜+•ù‡‚=eo!Ý9dQBÙÿ‡ò€ŸÏ%äÇäX·ª‰ðZ³U2”§PÛFÙàU,,é,*ËmL„@r²6@‹+>.Š—Ž*݈ó'Ýëò£k‚áW¬©ÕWøÛ)lÙÁ%­‹`²%Ä.÷Ú=FšÎM_×±6z^ï¿â6™/JIŽ­4{çVïý=‚¦ÿYÝІÎ#s?60½_•kóî›°?9<3G*Œ8_É8ÔÅÇ·¼ª€ŸUüšL0ihÏ)ÓüC%£òMßP¡iû,-AÖ™aðs *ñ<‘oØç…ªwäÜ;,ÃWyHÉJÚ![#½<ýOßdm¸ ݺðÜg‰Àù‰óÝ Õ½‡n\]G´-¬:¿ÂgBEÚ/`õYïø+™ÁÁpX¿=ÁHdÅ”zà×ô°¸<_Fþf4¹á¶­¼<ÕAéj·1uÕÇ·5´¶&PcäÁ×Õ/ø:Ô¥'ÉC -¹»Rˆ4]¥Lµ\ÔFo³¹ùr|©0óî»C_k4Õ ÞÐ…>®Òl[] -'-=ËW@?ð>L)§ÁœSKñÅ4yÞ‰?#ó/ùö”Kð¼õî¥Mú¨:b½õŒyèä{ì*2/«.¾>÷ Ãçßšá Aa˜ëP4dû¼§õ¡YSZ¯wh`~vƳP8²) µ³ÇŒ)¥óßCŠŒ "æ7,BYiø ô 4a&C鹟*sôŽÞM>FšGNÂÌ5­>‰2~†ý7’|zPð2VÛÄmŒ‹ Û ¬ø„Õ´&ôaÒþËÞµ4äß1Ôö*%áõ2"súáti:J[S )¹Ã&ç\BñµÑÃPÅ šÐžvéÐz"%fñkh´f†;9SEÐ}]#ð~MC‡Žþ½áÏþ‹¦Ä»‰æÙô.ÕŒØ7ê¢8,sÊ•4,UWºêâÉ÷ÆRÁÙBj3)¾¶÷â1LumMkÿP<…¹¾ñÎP£;ëí6ï”þÍ(_šB}Š!¼“ËSBá2í“÷1]‡·n|uÇðt¹Ù¾AšŽiYÓRÚ-vÎ0Ôµzîĉ»¸©=¾×–h5C]­äyÙW z)ß\·&gø‰oc»&?A¼Y¦š(D€fDŒ4å«)‹í ñå:ñÖD¾(‹Í¢{§^ëÒ)ÙUúW4ƒ§ax¬?x|н/ í²aš -N=åà‹"jU°çfOüï×xveõ‰1R¦ù͂簎Ðr,KšôÒÚ:uŠ«ì¶¥ý­Îta 0¼4ÊZøÞtÂ0Þ²þPtmïabrüwE;Jž)ÞUv:’. #·Ï©î‘7[Aõp) íB©kwS"Ãèô”iîœwüàjâ·#Fª1ÞvÐuMaŸtR¿IØæó Y¿¯8ièC¹© =¾Ï´3hêmÕU‘ÇMŸÞa¯%¨HÓU™šGL ‡Ó½¡:z{cùŒ`Â('#[¹™:xtøëÉß-œF]ç´8`Õ÷´®<Û”)#I禹ÕûXµ ºR;A>8n::V×v„Ç»ç5¥£ÅÝ4|dF€`6ˆÀpïÔƒ†gN¿F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`rêµoMSƒ–›»Ü³z½ÙOHÕK)áÁ¶åñBÿaÆŒÁuo@ØÄÌ¥gf_¬Ic;m´èåõ~`䛟^.”Ÿ ß~(#c«ÇÇÖ‹ÕÛsÉÍv’çù¼ ö´^N™3F€`h#Ð⻳“²³jÿ:vãêuþ³°‰`|HH[ˆRa‰ÛÇ,ý÷^÷(9öìÓV…âëq2Üël[âÊç#Ûf)å—Bê¯æeOˆˆX‰=Ú—#¯£øÜ꽿Ç&s %¤Ücå§8oqÅg¤÷ád¿¹g!bÎãÿxâP¢Äå±Ön‘'ËìňŠHñ¡çÇ2ÕmH?@ ¹;‚¿ÔK;m¡×;ĬBž/F€`Z1-ªøŒûéÐUëÊf %úFª& ”:Q#ƒB9zé‹é3kÖàßjJ^õ~ÚäœËlÓ~] í1MÊé¶²Â>]“âs¤Hñ©JÓ¶‚Ã\¶!Ï—uEÕ¸–¼î,Få ß|3Iå·$u•²½Ø(ûù9™ŸÔ•¶¥ã¥ÙTïJ%stM{Üö ÊROäÛKo™-Í—Ï0Œ#9-¢øŒ|xC¬üyÛã¶eÝ9«å)•P×D༑c—þõ‘™§¿Q~Û¾ì{ós2F…¥Ÿv^ïS[©s¡ø|]ïŒMœÁë•0N‰ÉM\L4È_­Kõq455yÞÌχ{gô›ç½{[EY‹Ó³|©Ê–ÿ׬ø4u0}F€`¢ˆ@³+>£GüiëXwÎi°J¤â‹ûµ‘w-¹é‘Îxª.:øRß)$FÍj ·zsŽ5MûA$9JÒ6 …ÍÉÍžô Ž• QiÓ¦¥ˆ"3>H— !Ã2|—!Í“y¾ŒìêȧeøÆ¡lX…Ô ÄÓÐÓ;°9ŠÉ¸éÓ;ì* ÎFÜ%BIŠZèIÐï|lÒ¤DkX¦ïqR‘J.l µIµÊÖ¿l%‡ÀRr5¥qCZ–o*Tžä„Î c‹v”¬ÔdÌ¥¹¾q(¾œgkºPê"%UÜúÁ0Œ+çz'þX.}÷XΓøŸ´ÕéJHâÁ–B½ªÆÃó¼vÓÕ„ç¨{éPfîYüÚB> üJ5Mœ/ó0Q¹ôèˆáU™ž™3§ÆùÇôO:t¨OçaJ¡W¢\TF€`ý ­¹™ Jõ_”yNÊÕñÅý¯;Ç~Z'-©iO£{ûkz†oÌØ3«–}k†¯/”ž¥èiqò(¡‹ è ÇŸyñ½Õë/W¶Îy†cΙãÍ ?fJ…BñšK–€`•8Ľ®ë˜–•óÐ=ÌI‡Y^²yH÷¤æƒ»Îò ~Ætø'…ˆ}Ì~By¡•Ú¦ùÊu²ÙBäá­iÑ)W|„|)\é¡„È÷”&xœ\¡ˆÝDÃm¹ééAtê•[Ø Çïsï›ð5§÷Oä<¸9Ê ·:ü¬5?aè*9ì^½ñœçÍXa¬ÍEvɯW½’̹Z×ôë,¥:ÁêECjïAðó¡à9VKYGƒmsî™´ ŠO¨h˜»Þ„…n஧ïÝsKoÇù(0‡Í‹pZûoÎ Ë´_‘BÿG®wÒ·.->2Œ#Àì4›â¥g8:êNM‹RÃ'LX™3mÚ ]µÑ¯Æx |¼çÔ—MS’Eg8†Abq\…!1òóp¬08(«â2‚ƒ#[OJ뇣|ÌñÞV„˫Ҳ¦AÉ Ž*°ûdX–¦ÁZ å¥e>ê§2´µX×2±©üöÞ¿I^,ÞQòȈ,ß;rrÖ”–Ú)]¯Vñƒ?•SR¡ í%BgòP9“sURͽ½·Чï`é¼-jê(8²»>qéf{Jœ¡5(C#óMßy(ä¯T•¨*fîù1Vé¹Ö•¾q/sagR«Å¦›…#)Wµ†ô¬iÇXfðm )ކÿ׋µ&æHF€`V‰@¥¢)9„Eদ¢Ú‰ÅÂk@~$ePg +ËØNN§ô°|¥ãœÜû2Þ$ui0ôR`ªÆW¬ôWX}ž‚"òbš7÷aenûw14±½ÂêT5[׳nj)ÅP9ßPR*VÂâòyžwÒÚj3hb=Ô·Èïå1ï¤õái 6˜‡p:UÏ#ÁU`Ùp&Ï‹¥Þ†’s/¬TEPm^¡jðïù ßZ¿%˜s=4µ½<½?¦<ðÿ\ØÁ>iÞ)G„Ë_¦  yVExatnh#éY9˜ÎnÚË8ã—ü¸e‰Z‘}&ÖòICúi”×#<³MüüC¦Áô“+»¥äû·« 37{ÒbJÓîõ#<[ñŽß‹…‹Åž"Ø{sæûÎY4Õp‚Îîad~¼Ùœq¨”Áãæeg<_WyºÔž0…ù¬9à$\É)Aû~XÔ2bõ„_üfá?06Xóˆ3(M$VÛ–5åziÛ á7´¬ºú¦z´Ls!”ãÉpæ^>Ü;õ —¶G$î|Ä;ªÀ½æ##À0Œ@ëF#MÒ<¡éK‘Ç×T”œë·>ß\â…æfXîÀXÉä^žŒû)­p,uy :¶«üJþgáBeÛïÁ¿dpM4#¹¯‚b@Y0ø¬ Û älCÇ}ʹ.¾8NgŸ ¯„Õ§V w6™¾R[Ö¡Ì‘Ц­3Àïoð÷9*%9湚òâ`xô+`é Lû õÛ¶"˶^ ˆ².塦2#Á8Дòó¡ nÅð_.Ñ"§f¾„LÏtøGt¯1º±¼ÉÅ’¢éËhr!¸F€`F€hJšEñ™1cð¬Z”¯)‚ š‚.ÓdF€`¶ƒ@³(>\Jþ³©`ÃPÚîN:¾ÒTô™.#À0Œ#À´ šMñéœóOl‘ß°a –½ÞXí—#À0Œ#À05#ÐlŠ×{b 6ß]3+ Œ‘ò{’ÎB„ ¤Ð¤Ù¼Þçchýœê =kV<6NÅÑÑ D¯&Jã¦OïPS\}îÓîé$SÕ<5ÉžŽV¯Ž$å¹Ó;»cxÞÚÎë’0¦uœj£ÁqŒ#À0ífYÇ'ÊÛG/Å*½Š¶‹ˆB%†¡ùàŒS¿ª‰VMÆæªcž/kLxlO±DÓäteËc±ÍAzÇdÏ‘÷_è¦A¾w MŸH+$c_¬,Ð0r³3ïqãÃór†Z¶zÜã‘g…ï»E[` Ý•øÑ‚‰kdßn·ÐªÍ#²¦ž€ÝÅga%`R â }~‚ÝãoÃy­áVïÔÂA멉Ø,ÃML÷MÓÂníâ`Ü‹ÁêÅ÷æù&½JñØ“,+Fß®¤ÚC\a×ÓÙó³3žuó†k¢O Ëf+çe‹Ë±×v<4c£Õ)”7=Ãw-ÕHì£åG½z ÅMžçËú0œ.-b¨ŠÌ°úñI¸ SÅnÃÐGÎñNü><§OÎ9«fϽíØl¬ŠëPw;Õ—§FÝlˆóx†øÍÀe‘Ȇzx éüØ‹ëêªåñ5#À0Œ@ûB Ù,>.¬gzÚ­ØTóE÷º¡G8K—jBþ©6¥‡hÒó:ëk±!gHÉéÞQGöÔú/tÊ—ªdOa Û9¯çŸ´,_¶Ä¸ùªð¬P΂œgö6Î4ß—u":ò ÜøûÍ”F*­DÚtħæù2Ž·¥8 ûuž¿ê9ö“:Û4Í—!Ä’ªq¦iÕ”xôNNò$œ‡}Éî½5Ã×·"ÝO†'õTðp†/®–Êžî¬t\…HmôóÍ)YPzzôòœ~2ʸØUzˆPÍ‘FüUó}™çA¦;±ñ¨£U"ŸšZ¢ íUä=†d†‚ô&x®¤ˆRz²Ì`…ï'°¹ûey¾ÌÓuMƒr'ç…Óáõ%tí†ûuʆa s ‘ÏF€hÇ4»â3t¨´:§œvöŽšI*@ƒ°—â7!ôsž}ÚÛuåŸë¿ÊÂŽôÌœÓÝ´~Ó¼JÉk^ïЀsOʇ¡]A;p»i"=z„ñ4,AdI0ÃóÀ‚}¤Ä3î¶PLž„òu-¥¡½¬r½“¾¥sÚ)JÓ犥=§HIDATPÐuMA×=ßiFÜùØcl]xG¡SêÊøÎ /Ó}ÚŽ¡·-!N£kXKÞ™ã½ÍéøçefnRBZÚæ‚TŠ 5ѧa-X^nNð$ŽweqóõÚœ†bTb¢ÐÀ>öS· e5¬\ó|“Þt£°Ñç2!ì£Ük÷øÞªïƒÞ¯¹¾qèÞ¼û2ÂJ4€†×Ü4VPÜ€‡–62­S6RpÁÐMRÓ*)O.->2Œ#À´?š]ñ!ˆisÌGgq7”!èú¿Žv( ( ɉƒ™uÊg‘æSB{ÚÅнéiÓRîAçˆ}BµÑؼs ëìMW÷ÙcÙÖòåúUx½”Ò~uï`±_P¦k…qoc” ù”ºÈ1ï‡nVs2Ï{÷6ú¡¤JåPÙз•ø÷n®©dÈ{DU2iÞ)GЦ¨D§j\Mô7›?ˆvÛ%§b—ôÒÐÙØ3)¿³É©Ô²ŠÌâé¾m[yº!÷±äT-KÅÅcymi÷ÂóÂÌÉ'ÕÆ2QJ:”÷A ¥L4ö±V'[™˜+ m Êr³}øàŒ#À0ízuòÑFç‘Ù§}ôèìÓ“º~hÿŠ _T PtÈ’² VŒŒxCüȬ3F>è=nw¥Du\Àè9( ×R3Ü;õ $?¸—qF¨ã…&aÌ÷Mz:Ä–MVÎð:ÈE­D²¦©ÐVAO\1:à}va‰º¶’%dŠŒð¾©¤Ðž±LóÉ´¬œ¿ÐЩ!PæÊ­YÉç^ÓÎÕ¥ª—ƒ¹þÞ ÕWØöÙJêð½} ö”½år¡éÚ"ÇwHˆÑ°pí´âŒZåáÍååjÙÁxÈ¥á5[%CÉ aF÷¡Õ K¦Ðù&û“ PWËçNœ¸‹®ÝPlâú”ÂÕyÞŒý¿Üü|dF€h?ÀÑòá‘™§.ô&¬Lõ«²^fÐŒº*8ú°Ø_»×\ïÄa‘ظÅôe í$(/U¶¡²á°2* Ô'·zï9 4ÊJ»ÌNaËÐL*]“1.T©Ã>Ù÷G8E_)ûvBå74äú2¦Ëšú¾PæRèK¡X¤hRl §çß¶çq((oÍËÉ )|áñ5ƒg¥_á#4¡"ÍÀrýpïŒn¶H.²ƒÛ?Ð<âÚž"ó«M攑¢Ðü鎩Žpí4ýÏê†6tÞ„ {öI£Éð% aV¯:(]•ãf« Ÿ3Ìž·ªläßd •.ô®çE-ýž)p*8w,dá™ùœ`F ]!Ð*ŸpħMD\%!<¾áçÚs¶°/ÅÔIðD¹§::ådþ ÌÃA30 ÖÌRjx€¥êX^u)À1ù ãÀ7©<ó³e oB‚váCðqï7ô8?{âÈK?šÉåÓuãA—Vz–oŠ28%×{Ú?†à~³T°Òð,@š0“•¹ãDœ™ëÍÄv!™TÜC˜A•žžy?×OÇ妧—‹ßÐ¥>nžwÒj÷~¥£2~–"œÊYèòMß2VÛDÓà•¹mHO£ç-n<«“ÍÔäEÀþHanÛŠx˜¤ËQ fçmF–áùùœ`F }!ТC]Í µ¡Ôóè¤/G™öôd,©©lÕ·Ë, ·Àb!«)M$÷5¡¿ˆÎ÷/{×¼‘‡cò«”—œ¨axTzŒ+ÊÈØNoXæ”+i8(ü^}Î1Ôu; ?»SÅÓ2|`éèÓË“‘^•â®­míJOÎátDÚ3èx›wJ$úqñêK`:|o(nä”)]iØë¼AýH×.}*£Ä,~ ùf†;9S nºÜì ßà²cZÖ´t‹ó(,«ihK·ÿ´ßñzo.£8 ÈW­lyÙ黃ûSšv ’¿Že¬ô8ÈñF€`Ú/N‡ÕÄŸ““ù¬;¡Ô|á8åÖ 4Í@‚5f„­ÔGðYip˜—=q:æ—òÍu+a!›oc»&?Aá<´fpâœ2 @lr¦½+{†misnFø§b¡[`¥ê§˜o¥§ë”•fC‹§BaØËÉ&·, õÍKìÑŽ’gŠw•Ñl7̲ª9èy³TOAž²`Ð.”º¿$˜w ´ýÿvî5 ð.¦0DÒx‡Â+Øo“"­Ä ¤Haš½Gðbã  ÿÞ+*™ÂF¾­æÍìÌ×ì0?/VQ&ßëù,•ýýnb¾W~TUµI“¦ý8½É]/¶ÿ^¢Ç•¨8xÜz~\6qÑä¢([oÅv=¸Ÿ¸ÚÞ.Z‡CéáóÛUÇº×Æ)¾.F  p3”ë&'[qüðW‡›^=J[Biµ%·Þ°®»Ÿ)ÞGãÎ¥øæ¦×iùÛ?—’ž¶å¸3tãlðY§í8 @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€Ù{oœ `@`IIEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-compconn1.svg0000664000175000017500000015616313553660046027162 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-09-27 23:26:12 +0000Canvas 1Layer 1Network NodeCompute NodeOpen vSwitch - Self-service NetworksComponents and ConnectivityProvider network 1VLAN 1 (untagged)InstanceLinux BridgeqbrDHCP NamespaceqdhcpMetadataProcessvethtapeth0iptablesPorttapVNI 101Provider networkAggregateInternet OVS Tunnel Bridgebr-tunOVS Integration Bridgebr-inttapInterface 3PortqvoPortPatchtunPatchintPortInterface 3PortqvbvethRouter NamespaceqrouterVLAN 1 OVS Provider Bridgebr-providerOVS Integration Bridgebr-intInterface 2Patchint-br-providerPatchphy-br-providerPortInterface 2OVS Tunnel Bridgebr-tunPatchintPatchtunPhysical Network InfrastructureInterface 3PortInterface 3Self-service networkVNI 101Overlay network10.0.1.0/24VNI 101PortPortPortPortInternalTunnel IDInternalTunnel IDInternalVLAN neutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-compconn1.graffle0000664000175000017500000001141213553660046027274 0ustar zuulzuul00000000000000‹í]kSÛȶýœü Ý|º·ˆ~?ædrŠ@f’8$w¦¨º%lat"$G–!ÌTþûÝ-ÙÖÓ` iRÁFê—Z½×Ú½w÷îçÿúz:~2 âèçgØEÏ?êÅý üüìÃÑ/›êÙ¿^<}þ_»û;G¼r†a0Jƒ/÷Þì8Ï6·¶¶‡ÃÐßÚÚ=ÚuöÞ9PÆÖÖ«wÏœggi:üikëòòÒõL*·Ÿ›„£­ƒ$úIzµ…mB·ŸöŸA5y长ÀÕ~ÐK_<}òü³õb»—þžwå'o¢¾ÿõù–¹ 7ƒ(õ~ò=ßš~fºƒž—B‘ó’g™¼$ñÌ—'ÏGiüZèÆçQ0HâñÐ݇o¿&Þéiè‹ç[“$¥ÔXh— h>UJò|kZtÞ„qo÷ÿ3¥³ªÓdìoMï¿ôzŸM•Qªž½Y²É³O’™£é½Y3þþm8èÛ†ó7çРɹˆ3ÉgŽTÄUJK¸„¤Â„ûVySØNèšåÆaP4¨–åÍn‘~Úá¤Ü÷y²Ãô*ô‹”ÓÇÉoB‘ñçânév~7ñ.‹f {·_ntö¬—¦ßŠ/y÷Žü?ãø|Á²ãEÞh? A1L¦ýœusù-g9âp|m‡Á j”›åg©‡^Š˜¥O|/|AaŒe_¦Iá3hw½Ô¯7… Ì61ÞÄÈÁò'J~bÜù‚ŸFëL!qRÏÿÖKSçwxéÞ¥—xõL»ÁhzW‡=/lÔ 2 ?NïÜùÙ™}¯ðKúGWÃFfä ž6`»qo|îGi]@gI=9˜#ƒ!-]i5É)‰A D NÁBj¡7Š¥+•’BkЍ‚á¢rÃ!¤*=mâSÈÏ™7ôÛhÚIé‘ÿ5mæûãÕaKê0¾l&}ï‚¿ü–Ôq”¾‰Nãù²±Õûy‚“ÒÍY}vþ)U©r’cОƒ3Œ9oË´g` Ъ–¡ñÒ¶@ÃþнhTËAQÖSEŽLÖÔLæ*uÔ!®‰œV„z’t/ˆ®Á±rAEIÌôb¹¤<ñA< Ò’ ÌšjzR3¡f:”)AKÏ3¿Ó 3ÊÂW®ÕÚ^{â*Z7žã4Ãë†L¯çvómŽÎ¼~y|/µìË,§ìz×ÕD¹Ö†êÝ~Ž“ôÃx ²_½áš÷â^ì™[ ±üÑøþf=ýûø†~zŸ¢ãÓ(€ÏÞ™—Œü9ÓÑÿÏo¬g$Òýó8ñû„óãAâû‘ùrŽ}øÌn€læ70UÙ ™‡^Ò?N¿rà7Æ$ûÊ|²…Ì¥Yª³$ŒeI8Ê’@nó!p–DHHbÊ ’ÈKljé™ i08KýèÔëQ¡§O³çaá÷N‰óqoûƒ¿Í!ü¢wßû¡gTÃmˆ6œé™(IxÊkAñOÔÍœÕ!PR5%x¡.×D &2 Cý„‚NÆ@gÔð[%¤i˜ –`à5Á8ãøiAt'~Aõ Öˆ^Ð’è…Ù‘^ˆâBCïSs˜DK/–^îL/dJ/(»)Y/‚y2„Ó<`ž!D²R‰`ÞÏ)° Ôϔ˩æÐ‰Xa€IaFÕDæÞÚpÎ’ºëD@ª)%TbΨ¢HwFjr=RO³—%«€HTî' êvΰ†s† ?=[’Ž»„ß ” WÖlRS—‚bLÆHQ˜8dP®\@zÆ!©”Hka&kK„ú•©ýJ0$:hýšKÍX­_+Í(»›Ú¿yèŸ'qØ_”UdWVaŠ´°Ê[oùiùuÍ Oæ›§Ÿ”ÜËëúË­¤hy½3ÂÊ­†’sŽ2ãš•«íM—Œ^7LŒ8S U¹®í•ÏrfvDÊǦv\1—Uß}wnyéG¾—žµµºÚsz¹uOƒ>À‘@³¼pG3$1Ƶ9Û;šF×ÅÏÜŽž>\ùÉ{¯Œ«ÚÐÄ {ΡšzóŒå7Z"=)Qé<ÇñÓ/'ßnr¹u°ˆiå2±æBS 7I iQ·FZÆŸ)\¡‰†¹#К&½!féí¾èMt¥7ŠØéíaPŸ¤›r.¦T*½0"Czy‘ TC1X-ŒÈ’a)—Š) "V£[K}–ú,õ-F}™yÒøY&ér®A¥¥’ Zÿ](ÐÎðrÂã /sÛI­à+B%]|‚—ùï(ÒÁT+¡:LðWˆ0¥(‘ˆËr–å,Ëud9/íßw›F˜äÍž]mž$›Ã$¾ú~²LÊÄú„Á€(£”µá4g¢–r%å2K¹–r-åZʵ”Û \ÆUQnË,·r­ó‘ù19¡Ø9–oôcò‚ xG?fQ¥²œk9×rngcî™­u…Že¶ƒÙˆe6Ël–Ù,³­Õ xÅ+t(C.æ’IEivKÛ:ÐVÊ”´+tì K}–úÖžúV³6‡èM(&5æ‚iffl.jÅØW„Iaçvq÷…”v÷…Ý}a ÎÜZ\ê ;Ûj1“ï@,s ùµ^øiÐó…dÈk.&¿ö½~ç(J²5ÌEõiæDd[xJÔæË:ˆáÒM¤€v¥YkD ÓªE•Àó¦L­sëšáY²»qÎýn¢~¬ŒV çí$©to¡µ)O{þÀë]U’Ÿzá( ù¤b ¶­îøžÂ±„ÝZò)è×è5ßÿ¿(;@…Ýà ХH²EÆ9–œU £º-2¶yЈ–® RjÊÒœq=ÛrC&—R$4”Â8þ1¡q½­Yom“ïdÕÐØêÓru-’ÓâZc[îæB<«5Z­ñ‘hÄB㡱±=qÐ(o=ŸÖt<Åí1à.Ј9è8Ô" ¢Dšé8ç.Æ .0M•|~nî„‚k Ëäë¬ë½Åâê­p•u†Êk”Ô¢—ÙÁÒLVÌ| ˜;Kf¡ÖjR¦@ÂqAjAÞ©€á‚•–kåSk}xý×1¢ÇèëÊ<™èúňíP”û4‹œda(Ê›EN»–úºÖÉÕ¥è$\'Ê=]“uótí<Ì–s ’,î¬ó2 ú 6ɱ¼}'É&`åÍAEB¯ç›“lZ·Š·K`Q¥[õ×5 8ë,ЉeÑ»³(Ö–E-‹Zµ,º,šÑèÁd#÷:rèM›Ì¿‘RDŠ$R0½ 8ˆ” ÒʲOC¤’º cR ̸Fì¡.ñÌ‘¦ÃÏ`:¬ñÌpE|gÆî~n^mü5ó~›¯}'ŒÇýæë¶ü¾$~_K³¨%wKîKÞí µÌ}~u,ÄØE„ àBެgÛØSO¹á4×'Ú åé‰޶ôÄ× qÈîHOf8S‚”QÜ:Ò†°­U–ž,=YzZ|úÙô‘:ïüô2N>_&ôå¸ÅøvÃõþÖî¯`ݾõ’´Ææ ŠÕ.5“cjŒ%ºøjÝrÿ +'2 6æ.„àˆên,$¥ -¤”Ý0XÓ!¥¼L»"m¹+'0_튴ùúªƒÅoëhËw™¨ž¶<€bO[¶§„ßÍ”¹2¥ˆ©eŒÎˆìx0ºF(Óš+…ª›m:]?±¶º=í|ÍN;ÿ¸·ýÎÁK8ïÜåÆðý;o=&ÀwþCÜâÂܲ·¬ËÜZ£`Ê· »Õáà9ÞBo²-˜r¶Ã´Fo”Zz{@ôÆìbK†– ->$2œ¬×†©wú£ÇV®s¡¶3½Çx®À£>6Ç’›%7Kn,´òúÁê§gh‰,«¸+áåkm‡á aXVT¬*£ÙGå±»?_–Ç‹«Ž/ÞÙÃE¬‡Ëz¸”‡ëðq þ-Ð"]ÂWæà²1T—^š-3P`ÃÏÈl UCuµ+Ö¼ÔìÚ\Ñ÷%¯\o˜«m,ÕußÓÃnKõ61ôížÿ‡ÜÔÓ<õNKjË–lŒn D,q‹KX þPíàRK…qC¸ÒÐ1¤‹! Œ×>bSø1Âó׋(¦ s˜‹‡€Îß3U0¦a0+Îß²o^8`%Ì8)È‘–Öníàv×~3ø[?õú^ê?=Hâž?Z¦³—WTΪ#Í ß0‹–Êä`ZQÿ‰™[X»&‚)ÍFP…·Ñe–pÀ-³áJm¸RK>–|Ö€|v_ï8ï¼s4ôzk¨ôKÿ¬7ü.!J4ŸÁ’ Œ×1œÀK<¬÷&£ÿÝÖe`]Þe ¬Ë`©¹z—¿u°¶É §æBdw|µiò¡z V}0e·ÓÖwÕTh®(&ÆÌºàikšXÃË2v‚Qkx±†kx±†—50¼çŽ¿®Ýù0_Nîû\˜ºgÉqj¡Ú¶jSÑ<`mÍüò”8‰²Äi‰Ó§%ε8ƒe”zQÏ¿ïƒ@ƒá 0 •ÈF©0qž'þrN±"Zòlý˜Ö6ÎóeaØÆy¶qž×2γƒg‘žóH€Î£Ô üþÿÜKÄgÌ$€æ’=Ec©lÈçE =àϹýzžÆs£ýš_¯_g¿.ªT6äó²B>s]9¤´W™óI(•ƒÍr&‚tk«`ÜUü©°†Ý‹};áº=ÔtbÂ4Ź8¼ ÒÞ™³Yh“ÓºFÇ] ‘‰Én'>ÆÌrFŽõáÏ(ò{ip¤W÷¤{¸¸ìb6«pZŽ©Ì€K»æ"•Ã|e»ß@ù0{ 05;8¨0ö`çeì¼ÊÃ2Øò¡ó‘ö6/X#àF{÷ƒ¤ÌtãÔwÞÅýÕXÿ ×DVʯIÐ/óÅìUÎî½´aVÃdI‹«ª›C‹3ñœü'NZ³ÏV¢Ô˜ÏCãË‘ÉU$oWËóÔ‘7Å ¤/ž>{ª1¨$£½¸÷ÙŸel…RêÁ(8)~N5Ùú¥oàÏD«}áMÎ2çptŒÔOHþ»ïâÞWjÅl™S¾ÄÉ»ò“¢æ™ ª¦Ö˜¿I'ÊSš5ó-k"M5n&?€?Q†AÏ-ÖŒyc±ó8´6§ó‡øvœ{©ß@5ÑÜíI/~D»Á(­‰Cy}‘öÐy÷*µf¦ŒZú0kæ«hPŽæ5mK?N1n°Ø,jÛó£AÉ!6Ûâ?¥w­¡•¥~Eþ õ},”¾*lÐÄÏó¤§)l‡k‡ô#žÃ3¿ä¥ÍþL*ÞÆýà4èe­Þ-½éi¡a±‰Ñ&bæ?qöãÎ?LÏ5êÊ *Éêäö[/MÏüKçwÐ×½KèŸzÆwqê/þHûI€^éäö˜jÓÔ0d.Ü`ÓKxOŸG ¤5â<_pÞ¾ŒÓ4>ë%Ђb<”<ß“òNÃØK«b=ùÎpU„ ÈÊkx'Á_qä…Ðî ª ¼fE= Í~kE/·Ó¿ú¯ ÿø„‘ÿ¿/Ã?ß¼Ž·Ù¿_þûÍ᫳í³ýˆã“O¿…½ÁüÝßÿ€ÿ<;yý1܆û;¯øÑùíËŸŸÞ¡í7Ÿ^m~4¿| /w~½¾ù{0ýK÷`u}ù0L* ÝR|þÑZ~0Š7=¶@•)T[ ;Øl)·æ.‘œ ‰8“ &ËŽbØUJƒÞÀ‘T ~»¡ f ¾÷/€Üüéx#á½QúV9Žâá*Нbyö"ö#ß Èä½ïõ÷£ðêfxy_Vpר>´¦Fæ¬C ­s¢>i w¼èÂU”–<Ç9(äY3Œ«zÛÆ²´ˆ¦—e6Ê€qÕwÌû! ¾ŒýÂÐpÍÓù¯¢4H®ëö‹*Ÿ‚fÖóQ}gœ$ÐYO6uö•ýÕס°ý_ÞÅ%®àá‰å&© UaL£H~6‰ñÒl8’ÖLý³ÙAŶõ’z,ÇÆ4Tac—äÓß×}NwŸ§ÿ|{ŸÂt%ƒ¿=¡>yâ…ƒ^Òç{òûñÏÛæÉߟýøô¿^¼Ý;þ×ÁKï*ކ™wðûó7¯÷¼'Û;;»WWq¸³óâø…wðæõѱmìì¼Üâ=¹È²«ŸvvnnnüÀ•ò{É¥+8Ü9H“«0;¼Æ¶¡‚ßÏúO ›¢õÊpàÓ~ÔËžýøÃÓá—g»½,ú¾ ¾„éëA?üütÇ} £Až‡é3òtgüv\úŽzAM¾+ZžT Ò4po~x:ÌR¸åg0B?¹Dçir}å¿…w¿¤ÁÙYª§;£"¥ÒTYŸ>Sš2S*òtgÜt1„ë,Ùíÿûz˜MºÎÒëpg|ýyÐûàºô¡»«‹¨7)6º÷Q1Wb8¾6ÆŸ’-|Ýòþ”¤¥TšH¡…‘bËÓ†ùÆX m(“_¿VîÅ5¶Ãf»GIMT«òúÅ´üø³ò³/Še_âpZr|;ÅEh2ù0½Zº\\‘7ÓaM¶ÿ¶<xØ“§4~7}S<ÞaøG’\Î1Yö‚Á§`ø6Σé4?çü1—¿å¼F__vãè|ÐhŸ6ÛÏK]=hbR> ƒø‡9–¿…ܤ}da}(ŒP±Mé6%Õ?qö“Þ_ü4FçIÒzýß‚,óþ_zp¤A½Ò‹hx_ŽzAÜèd~¼Þ¥÷7oò¾ÞÀÏQ¹jT@êeó ö"é]_†ƒ¬. “ÉOr41‡CZº:Ój’SK|n¤aŠRb¸5DF1å"@Џ‚?† '\BmyŒU¥§M|¦òs\…m4~HÙqø9kÖû×Ë£–ÒqrÓ,z£ÿ„-¥“Aözp–Ì–-˜ˆÓiQ½^8-]œô`ÇàטJ—£çí5¤ Tʶ i{!­j¦2^ºÃhx{Ž‚Á°V;‡¢üIMkä²f&2Wé£qÓ™hX4FEßDƒ[p¬ÜP©%ª«-…’a”•d`2Tx’„P%µ47F0#Kƒ/*&YŽeá+÷Ékc¯Ýq­÷qÅñmS¦Ž×³»ùm/‚~y~/µíË,§íú£«‰rm Õ«Sø9I³3zó5Êÿô®Î ÙI/é%»$ˆ(þ^ŸÂÿB“ÿ<9ƒ©ŸÆ'gäälÁkï"H‡aF¼ñìÿëW(Ösåþz’†}&åÉy†÷æ4¾á5¿²Y\ Üä$T>¹ ÒþIöY*)eù‹2î…1‘¿â^8Ï‹p›"/"I^j»Eó"JC×n”‚ì: â“=÷A_dáà,èQ‘ÌïkH•wÒ;cÞ»7»û%ôë ÊŸ>ßÃ0œr¸ 2цô ?#5‰ny´ ÇßT7kV'AIÙ˜—bŒô‰’„Í·škG$ù‡ ª `r­DA1Ü"Å4VNE†@ØÒv` J²°ëÊ0Ô.‹aˆéÈ0LhI%jâ–(aYg†a·3̸zéVò¨ö9‘\*£¥¥š°ÊcCnz”ÜÄùˆšhAMÔÚ5#§ý×Kâ&`&LóïONTK_ÓáDhjÕzËÓ Rjà%Æ…àÒÚ|`Ì=m¨RLZe`¤µÏ¤X"yÝI03úU ³¨Ì Ñ”uÅhai Fç7ԼϽ(íÅ- úˆV§×J‰‚€H¥Ûãš3K‹Etß}Ä |wV´k·Üîs°D¥’ •ó=¥vÑ…p£QÖÁ­ÔVˆëàÖXÁÅ·­ƒo…—Ñi÷W¦åJ·@ÏoÁ9°Lùëšlþÿ0Ûe㇒kÎ'×ý5Z´Ñ`×>k-å6 c”3Éç.-(à,°\k [ðÙ‚¹"‰"“:?=Mµ¾½fûSæLp%gKùî;ôCÙ ˜ GÕÕõ[\…îÊY‡=ÓÒ®ÀÓÿùz—ƒZÞ³ À hjà3k„ÖiÑÆLR+•åVY•/ôUHŽBÐñÁgD ñÝñ©îÄÇ–H|ÝHñžhN‚Зià…‚Ò®ùÜP,‰ÖâÄ sk”™Š… Ò&Œó0%–IxÏÃAdmÓ°¾4…œ‡œ÷p9O/“ôê§e“ô4Ÿ—ôEÒ»/ÒÓ]IO2±Rk‰ ‰ ‰ ‰mnb3݉mµ8Îu_üý®~¦ ;eT¸r73¢¢ævŠm[£³l%Jy5@ˆ7k+öm¤ó@—ÍÖ›Ò¦Óy7M+Owªµ)OoÂó ÷¥Rü,ˆ‡y°æeoìšôÌù=Æã Š»ä}Ô¯ñk~«l^z€»JrCùZP’Pù9Q>Ts"¬ej´mÃý²"Ï nðlà!7xÐ+m´ ˆM V¹Ã£@Å”V晌 ?·Ä Š‹]¸ØeˆÞÄÅ® qVÀ%.¤³Ç@gl™l&•¯,³”)SŽmyBŽœ÷„€¿rdÇ!›m›)ܺÁ­ä5äµµà5¹T—#|ç} ´à„Raš´Æºám ž!ÝðòòòÖe’,•óõ5gÌ0óTXƒ¦Ü£1å6Ò ¯H-G Š1ÔÖ-Ô ¨e´T”ˆn Ææ2@ ¼†¼†¼Ö•×ì 7Ü,wÉHÔ(€0Š0f…E^ÛH^ã¹á–gåÖ·{ƒÌ0ØòôÜS¼• 6«uÉÄ ±!±!±u#6½R{M!±=–°)I7Ö`{ð™{ÌÌ™™e’p•¯ˆb`¦i!`ž™Idì¦…Û 0Hf›j¥±Í´Ò6ÂÅÙ ÙìQ쥱Uú¸”‹Ó:<(ú(HÌQEG’Ž^#ÓŽL‡L‡LבéèÊ 7Œg{†Aà 7¤3¤³û¤3¾êðlô‚|t¦­Ý`o)%1&&ݼE„e”H`DBlGo ÒB]ï 5d6d¶ŽÌ¦Vžw7رÌ;‚yGÍÍî‘ÍøªóŽÔÙ ã³7ò˜«1>㳑òòÖžòĪ)ž†O©¡Äha9PžDn# ¸MÞh£0ƒÜ¢PªÛž›`ðBµ‚ÑŽ{nZB5Í”T ‰ ‰ ‰­ãž›ÄcRî<&Ŭþ˜½è1)ƒNÜùÚÄ‚Úo •\H­·w“"µf, µUq¢3‚ ‹ÇyJJ±&)¸b¡œkc;®I¢8·Ð 5—'¥.+O1<&e©Ç¤4Òv/û˜”G†Œ ·±U £ZA}wÚ“°š ú)`³ÚW\kË¥ VHÎBÆzIi[wÙfÕ܇ºP™°óÔ¨üX€u½mÖ…€Õ"°.XõêU.Så”’ùZJëRU.„ÐsŸÌ‡Ðˆ:çæBcÃe¡qýuN±ð¡¥-zßüЈZ#jJkdK…Fºzhä kÖøÂEÒKeAϰÚe¥VÚ×TIç™ ™Ò™Z£– ©ꑦp£Z7Íq¦fÖ®÷Íï†e< Oƒ~¨¸*W—¹d¿ƒ5ÎÅUíœ+”SË´›GrZ(щÆå¶ŸUyO+Ü·Â}««¯’Õâêìбš]i©³ 0©ÔB+ ðÊv¡cmYBóÕ©)Òf–­UèXë—N×þ®61¤+Ø#·Ÿ/ÖŽ[EèÞ´&›·Š¾YÙ‡1¢ #ºî7¢Ëðâs°qòÏ)eëÐu˜\gaêí—áð*è…'÷=¼!ÅÈ>¦ùÐfE›M'ÁA 㾠˰>Û*ñ.@ ‚ù²ºV°åIJ|ÄHAk6nL´¨°Sé!V±ŒºŒ˜h$P$P$P$Ðu Pïí»#ï M>E}àÑçiÔ?_#=M·¯Fc»g"¥ÚÅ+¸ª”£IÝ RKÍ]–è˜H­F"]‘J$R$R$R$Ò5 RÇ£Ç׃A¯#‹f׃u³DmëN6h}ÏÛp´DWD     tMôµ£4È¢d°Ž, XyÏ,ZÏ¥ÉÕ–WöRsin`.MÉ$æÒÄ\šHטKsMri.õ˜¾o]æ"Œ”@SºÉlš#³m$³±Md¶õv1GbCbCbk'6»rbkD[â‘?xä'â±ùƒ¤‡¤‡¤÷I<á®@c&W'˜¾Ÿ€¸zm Z £Ì—.Ð?&”Ę6ŒiÛ̘6D»&Ú±•£Y8ü·y€XULû.—„ÇÞvØõ­ý ÞP€Euþ3+·ô\GJM1©&UØ ä«Nªp‹Ëu=÷‰nSŠ.×ßu¥ž ÇzŒá¢zŒ¡ÇØÃðk‹®³¨aü=Y”ZdQdQdQdÑu‰ÆÈ¥[|®I5Uq‡[â+e™Ñ”X®ÁäÌÔ¥§’KdÈ(«IÀ ø)JÇe8= ‡9i}¿Íÿž:ìþ¨Ôa÷?#õm4?/»›ÎQU­I¸¿‘”¿a¹ï^—i» ìÆÑù m)ª5iÝw¦ŒogŠ~x–§#LvÆÍB÷³!#~N;€fy“¤N tUÞî9tvdáíØú© )ÕïnDåÂ'y ?Ãy¾Â'¶<ÐJFiý‚ƒª‘Ç‚jŸ”-•/ÕW?“ßÕ%ê»ì×Ì–¶Å·ü†š÷¹¥½¸öï-Ìc½Ïú\3Øì =/gaB}n˜Ó¸$bÎÛ€+›{:râ«ÝÞšan·<Æ»RTluQŠò±JQ¾f×T)¦mÉÃmšuÎJM[¥9H†‘[l •8ÿY&$1ŒJI@3­n—¹â‡I–¯ Ö6ð&}²Û÷¶ÇÕK·RìËiák¦•.¿2¦Œ?¨®R+\_=õ¿¾åà›ÄÍ<~xE„D„|è©!—Šjå)—v=mCH6ó$úðòa¦“KJ*³ÈÞ© Õ8»åÕΗ7…SP½¤ÛeÅt+qÖá‹°¸š‹°k°˜ œ{ýyíB`?ž®ÛòL7ˆÓæ»­mû²uÞ\³ÝÖÊ›Ì o"o"o"o®o¾ ³`РÚ|}êŒÄòDGͬÈ[ãÌ0€êŒQe× ¨¸kX^÷˜bÅ1YâéÅÓ‡£gC6Ró÷’Ë«ë,ôö“~xÿùn*Ásô}M­ŸÏ2i7œÙH©K…ƒ oy›<9=r&¼YÄ7¤¼y÷fwß6ƒ®G¸O|ç1Nv˜XŸ 8 Áºˆ-δbTH+1Niè΀Ãà×èYÛmZ"Ø..wÅ­ #íZ¢Vk1íÒ`œ¥eÀ©GX‚š$ü<E ”0–Ñ"´$aQKÚ$-©;x1®PM 0‹)>·™œÒ¡ÃP¼OG7Qֻ𶽣0>ۆ駨fe¡ Oº4>1NGµ½ã48;‹zžƒèanöbç(¹Î.¼£^8Ò(ñؽèSm‹œRßPJuG¯Мr4nqÙç PA N¡ á’ŠG±ö¤…€„k¸öwßkcx]ÙÚßt‡7oå—4ê—1iòUN®]=䇑c oª ;8£ è'ikõ‰?_m€…ù–Ü ]­iñvu¶(=®†ÇÉå§wŸßÕuÔ‡o’Þ‡pR±>J¥ßEÃèt †3ºÉ½@‚óp"Zíî‹_BÁ= ±,Lç(þ0¼:NŽzAigÑÂQ4ø¦Óž';ù5æt7~í%÷ƒËc:ïÆ£Íâðï¼–ÈQ ªÈp¾a¼‹Â›;›5Áa´ ”̞⻃è2ÈÂÆª©!îj/J{ÉoÑàE4ÌjâPND;-{‚¼WÓANVjåã|˜/çå4˜ã±ô“¬1çM8×à7áà¼äW0É”;v°†0ÎÊùn§õ; `é*š«|UØ`ˆfIOSØF~+ÊAzŽ.Â’³K1ñ'Rñ[Ò@QÍGý¢ôMe„ªmJ¶‰ò¨ù‰˜Ÿóþâž\£¯¼¡’¬Ž.ÿdÙExãýtÂàžO½â~’…óßÒÛ4@¯<äöä£ãÒ0eænÜaÓsøž> ç(ëÄy¶àì=O²,¹ü-HaÓùPr µw'AVëÑ{A«"<…¬¢‡WIý'1Œ;Ô&^³£f¿µ£ç»Ùú¯~ÿõž’ðŸÇ¼~•ìŠ>ÿç룗»o'’ž¾ÿ5îïÁÿý·¿Ó?.N_½‹wáúÞKy|Ì~ýøÇû}²ûúýËÝ£PæçÏñÍÞ/·ÿ ˜UßòxªÜÖþ˜i¤[š/^ZÛ†Év æè£be´õÑX?«|ÒJŸi)•&Rha¤ØòŒcÌXÐ$ÑTÀ¯w ÁMÄÃð[Ø"a&:¥o•Sá8¹ZEóU,Ï¿ˆ·ƒÐ¡È ä0 úoñ—»áå0¹©l_Ý¢ú@ÑšY°Ÿj#õxà8ÊšH¸ >ÊÒRÔ¸…<†Ó¢ Uo·ç2ÙΡéå•2à<ž:Öý}}¼§¶ø-wÿû0|9È¢ô¶Çþn^¥ó}4Ëz6ªï]§)<‰üI6uö•ýåç«`ÂöÅ#.étS-n¤¤šã°ü¬š/ÉPêbq43Õe—‰uPY?yš¥×áNåúáu±Ä2fuÅ—`lcµœ*Í¥f-Oì$¹¬kª¾äù:¢FrÂ4¯ÒÕy€m 5 2 1 °ã2Ý@IDATxì|EûÇgöî’ªHo¢(ööZ_;6Þ×òÚ±wI(À$=É]ñU,”„×úªÅòÚÅÞ@°`¯ˆ ½Cê–ùÿž½lØ\.É%¹K.É3ŸÏÝîÎNy滳3óL[!Ø0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L ­(-œ&À˜@Ü Œ ÜÓµH/ÞÇçÓÖÎd-‰{­<À‘à‘¦.¼!ÿühIUJÉQ9w ¶4}/MiEºü~Öĉ›¢¹e;&ÐPžW¦æ™›µ %ÈË22Ö@ÀÛÁi`-@ ð¾wX4À+´òãWÄ"fNè2¥¬ŽÁœY±¸o)nny•VšHÛ<=0v³Û¾1ç åWb”¥„z¥Ü°žDü—5F†dô;:;¸‹)Õ è=…G®—šXuâ>{ü4lØ03ò†zátâ¢I)ñhw˜ëSûdúCO€ïØ,ú+¥·L›Öíî[o-Úá²õœÝ˜Ú¯Ì(!EêcùÁÛ~M–”Ý—×Ý(—•ðnŸ¸ummr9ï¬é³J ²³WÕæ¶-Þ£`ððx)6Ñä,7Êg Æ!BŠÅRÉ òx„Pi­U™!å¦>\)‘-¤Þ —#¢qi»Òbë^¡Ô¥Bo …ö¹/;{MMr¬Óô5„úMêò]¸9¹&wlϘh*¬Ð4iއ 4‚À7ÞX–áÏ¥žîûèYhhd7"´$õ*Åf)´ÿ#éh¹qÚWGY–¸ÅúH4ÂOÈø¿¨o Ú¿z@3e_ÝÐ߇"ÓAJ±JãÛø}%¤êøƒÁüT·["•™QÙÁþºRg#þe'°ç‰Œ«hïTÊ·ÿgR’Ï(ѵ¸Äz‚]ДÂeøC9JXíçs&4e¼`­ƒ+4­ã9r*ÚL5;+œÌœV™Z,è[SÌ™¸Œ)S:«mÆ#hdŸcéò!Ü?0ÒM,×­_, 7º2îÁ(A©É»ò'eOˆœ <’¶R¬éá¸OÄÑðÈÝí!!¶e Á›™‰`0¡`®GÞ8?ÓŸwn~0ë…x„K˜vx5”êE±¸e7L€ 0HZ¤_3&Ðò ÐM³jù)¢`„-Ò—–Q‘–}3ùéu¥kîܹž†¦¿¡þH  xë’-ò>ù‹´Kä5å 4·ãð¤M‹TfÈ>¸º´ 0aY]r4Jv%z‡Ã—늧®ûÀÜ”úÊR_÷Ž É[Nõ9b=QûXÜÓºúÅâ¶67Rj7Ó}%Ì#'OÞ©6·±Ü‹E®Œœ)ûcÄj×XÂK„zçíQឈüpÛÔ©*O’À^˜@« €NQ6L€ 45QÉ»ëXCƒÞÐ_0r°W,ñcÊÙgp×)?׿·»jÛ+ù v‘ž‘¼ÓY®³¬JvÆ¢B¬Mx]ó¥æD.ô¸·K±±zDWC†¢É0"'ïhK™¡ ˜—ÌCn2ý¹7`ÝÃh!´÷¢¨Ð"s]ªW„’z»tyZmsñ)¼XY Eè9NOO×z¹Ã sѾ„,#FæL>ÄPæX´ØOƒ0oí*pyŒâ©‰Ý»!p§2cÛ]hD Áåø­»Ï5¯¼Sª+â}Ïê)pº”Ü;�e€0Œ â: éí N&Ë­BaíÏ“îíp¤{ƒƒŒ@Þ^Â0Cè‘> qö‡œ…p½Hóyn™˜øçŸ‰9‹möMºÖ§¾ë±ê+;ÖAmBë¸KAÐoo @kw@é2ä¡s ÃäWL5ë5äp÷ÏXRé¿kK”ëÓí ðÖFü}‰³ÿõõdϤ嫾²×š·¤ø7Â>q/œò_íŽÇ}>Ü,€ŒÇ)á¹mN0ëelNq6÷ÈBšgàzÀí–Ω!;"'x½ròÕ~Äyo;òÊ·˜’¨É뎟@@i+õÐ-ȫ炧3rùÂ~yu†ã.–#ä|‘_šÖÞÓ£¬Èš…¸ÏC9óžÇU‘þé/êO°~ïWµ54±ÊewNk'áÑQ™ÕéÜŠ¸*7Ð49ѲÔ!°;_HmRAnöS‘² Ì{éŒ0†,wEÞ¯ä/=™sr³>tî LÞÍ4 ¼ƒ‚ÂÿÛ ê«Ií‰üÜìÇûXk~p•5”çñ :àøÜþ+¸äCÞcQ.?‘òé>ÙšyW e‚¿½©œC^b¹ ×ó„æ-(Èð;,>gL *¡©Êƒ¯˜@Ò@ƒeTt{F hÛKõTìY8 ƒQ ¾ŠJóYTš\gXzÙÂÈi=¥ÄCáÁÏ È0k(3ím7JöqìRzt¦ÊkÉ­‘˜–r¾cOGjŒ•Kñ(üìéLùnÅÃí®¾ç£yƒ&Uò+"äô#âCFù§îmZæÛhž†É÷öôMûÙ‰Ëæ…߈Àä¥úÖÏ‘žL„ŸŠ¸žÄï}0ÜSéâ#ø¯Ö`£0©Q' ý;Èuv¢0EÃR>Œø;34Fº)¡½$ÿ1»zJ9h*”Ä'i áOèÏGüWj@Zºùõî ŽÛãbuy}∇ìÊÇ í}ð[MqƒÝ°ú6üKb‘'œÊ‚õ5x®›Ð~!½„0LäÁ“¡Ð^©Ì4DöÚò–T¾—)È+—a=ÖzU7£óòv†,WáNÿ:zÞ'–;ÙùCÊntí6ÔÉ€]ßÞ†›û‘¶Cž%HÛ‹ˆg)Þ®#‘'+ëlêÅ/ÔC¯#ßL…òÓŸƒÿá[ñ ßÿ£÷Ñ~,çV‘•ŽŽˆÑoü_9âöÐÐXü9nê#W;ßúŽHã€ðûJ!È-á|P‘,ä/á÷É:Ó‰Ã9Ò;ˆôŸI÷ÆU޽s¤Ñ(“âzPš§ÃWŽýˆœÐ0(3ß ì ß6ŠƒzMWÁ —£Å© ño4¤:¡ûvŒå:n0}å¦Òµ[Vã^—¯|pF »²‘ž¸»›2J[ÍëÇÌ øi„Ì6¤B|ÖT2?#?¿‚ÌLD‘ƒ|’g ój cÄ­KçŽ)“ï?~[m±ÅKö‚\ÿtÄ3¶ÑFïû‘W愲¯¯-îÈ{åyPÜá÷,"Ÿä¾O#7/m×¶Ã4FöÚòzÿçâ9_[\".Bl÷íˆ1|V^b¡1­|Pž®‹/ùÀˆ©‚Ÿ„¼ø±h'/œãÚyD`Z^âÐÊéy[¶•S¾Š<ÿÈ·W;ù6<òZô Þù³¡]7Qر%}é÷eÿ $cáçQ4ð PŽì‹üG}äªèœ–q{èteZ¯‚ÕÇ¡ª£ (_zézŠ9$2 º&ÎÁ^ß@ ¨caRtÝߊz绥‡V¼£óÜH£?¾«—a”=„ð`´s ‚Y/:á’ªYÔ)t–0ÖÞû€sÏ9Ö–7ÑŽ™9ÁiÈï˜:+ßìë|Q 0Ä wgy‘y=)öí½éƒï Œ«|Æ4ÝpÛæ²C°îŠ:VØ0&P ÊÞžZÜð-&ÀZ)gº•Ùn„Hù<[RüŽñ0³Ùߣð¸ ÿΆ^þ “¦ó 5ó*¬y¹®Þñ(9S5¾pý¾G£j½nXŸ"¬ùš7mP‹”±3ܬuq}”™‘þ©4â5Œ”¥4oçkF!ÉN=ü½½~? Ké:Ò aów²“Â÷´û…/¡qæ"v’ûž0J²¨_Ϻ•rƒ´=‡À0H Ö–­VÅ_œ/f³^Eš1½EꤰmÚª‚u¾½–¡¦¸’Dv[<¥ÐQ󯪆¾í230±ê3kŒìµå-%ÇnE鲄m¯ U᮪¬î+{ú¢’è¥éí´ "§Ò”Q§Lr(>Pî¥òz=~w¾¥©˜Gaà òoýŒ%ñ (˜ðTMù&òë€-[õ)±„’¹Â%±ªgf o·(oÎE.bbO5Ó (".ƒû'Ò%FÉÞt¬ C MØCÜçVfèþŒ¬¬ ©íµ‹)L\ÞJßIrüUkË•ŽªžÐnPf0‚+>–¾îçÃʪ\õ¯8_äVfÈŽÊ2LKceÆÅG&P Vhj÷˜@K"àž¹ÑäõzÄkd¹'»F»ßP;šÿŠÿ (5§czËH¡[ÿÅôTé‘WnY_ïp¥òÀMÝ ÿ”ìYWÈM#ÉGšFùŵ.º×´;Ñå /±¬ÁÀô'šÆ#ŸwzpݾI©Aϸíè %Ìd‘©áÓÛÛ}yõûâwÛNÉÈEÕg½GjEú¡k¬‘ø„ŽøÐåþtL¤ÁüþY^é;ÌA‹/Ï1CYú·PlÞÉ„¢)¿I#;¸Ì'6˜8‹ÖBÄÀ©q²×·°vÏK.¡éa4åÑ-­ ƒ„GbøàY¹þÝ÷¢kºq,žA*òûs‘Ó*#ݦ~"ò-MÁ|V`b8¯¹õõLXŒ{Ôh4öž{Ú¹nÕëTú¼¤ì[BŒ™“wl]ž%—¦‰·(nŒâœèÈ@ÓüÀêh¼ðóSÚI(,ôût” kÛ`,ÍS©ÐÀÍÉd'<Þ|ûñ÷@VÖ:°{Žøêº~|Äíðe ùÁ훽vo4F²'ãçè49£ Yìv×Cõ=Ê‘-vhFNðÆZË8·G>gL  j(°aL HI³VDK†e¤l¢ õ¸H‰v¿1v˜kuI‰úí{,@F#_“÷`z¾iÒ ó¦íåöþ"ù‚Cñåíl,˜þö·KÎF…r´m~S4oåywµC[íNä¯5¹Cƒ˜*·IqBÃÿRæ ͕Ԉª2JƒëóÈ£g9l¨¡‚V„WËTæ((40ÂXƒÈóçÑ®ÝPo5¦UiD“”4íêi®ÝwøîÌàøŸpv ¦3M0õ²+ Û HëIRŸ`JØuñz‚\Æ[öpìÑÿIY̸#tnä]¯ÒÖÎÊÍú˜ìå€î÷©eëŽÿ³°â°|YÓ´‚Ywf½©ÔÆCöÚòÕ wL6E9Æd9r†q™}®Ä£‘29nÜG³r-›¶Ämõ\U¬{S¢Òþ‘nVAÊC˜w§RJ¶–Pž¢õõ6´ÓÂÇwaԃȳA9:ðÞqãhô"ºI”\˜J nQ– …æAм´Tœ…ׯ˜x”Èù¬$E‡BÚ¹ S?‡ }HÁÃÌQ*OVów!e/ÿŽñË‘> *Š¡ò_Å oRí~mùÁå¸SpOÁË~òÈ_ºöϲÂÓÞ\n ÓëæÎF\¯)K݇2b,Ê–9([ªK±u‡ÃçL ­@Ï$&ÀZtô,ÆÙ ‘ˆº¸fC.`´&¤ #=MÚS?jöQ¿;TÙÓÔ¬¾ÞcΡN('¼óݯWG ¥›¯ã‰v¯&;ôéöµïIg—­ê.ïšê¶H±&íÆ>ô“IhL¤é)hˆ3ܺw@³{Q/ïßç9~?þñÞh`†w=Rr(EûíŽÞÚM˜Î†ömíÆ2­‹ñ—ç"XPMÉ©=$!h:Fîý{PüÛØZ³œ)7ñ–½6yž}öY-2MtmZÖŽ?Z_Ô×—}®Ô´K¡L`q·8×4­yX3òcFNèÇã!{my+Åç{Ïší\jÜUF.¡ÐH…^„Ç*­j9Á{´‹}[îØå«&çx>a·¤EÏG´¿”òÊêÈÀ®Š3¡| Æ{m,º“dÒ}^$—ÞzÜq™DÉÕ[ëó1b*Îãim›·RçÐÑg‰ÿ…EÐ^"§¤TœI×eë·$ þÞq6ˆØ >ë7^(9ëjS25a­¶Ã”Ø…/Š©-?8Î&E™öè®.õëRç^äqv0çáõa÷JmÒˆÍDT7…2|4#ê鞯™¨NÀ[ÝŠm˜h‰î¸C¨@ ¾’£BîB!¢]¥áâÄBkPÐ3}Ú5ehÌ¥¢Fc^\èÜב( ´^â0(5ÇàøŸÈ°–~µžÂAϨÎÈ0éÚRSvÐÄ@KÃ}NnöÓ™ÙAì ¬ÅÍ™ezùLû>ZLh,=ƒEÝcÙá…¿dïíäÛ 6èCc}BWwX 9GÄo"¦-‘~µvâH»X¯+6"ÈÄ.OàyY®ë§Àïcñ–½6y.¸àëíoB·Fºn½ÌmWÑH} vO Ï™Œ|aÞôa·¨'¡XÆXcîµå­ãW">ìf'þ92'x<¢ü`d x¤¡«Ax/ÞòÿErÔi4±Þž¤d©:ó”ìuˆL鼻ΰá€þXl]¹aÒ®`ã°íöÜ4Ÿ¹¼X¯h¢ä¢ï"!ObtN²Úœr >¶û»ØfÐæ ‹gÃ|}Òób¹²‚PÄiÍO$ 17´rºÙÎâ¶•+ep;Þfl(Q³1…†]Å,òVl"œÖ–§x>é8_€N¡;”2ß<ÓOÎÉø¹ãÆ}¬øîÓ(lÀ0~óvýJ”)càçJQ,NÿcÜ›¸ýñ9`avOÃ`L íð•·sF¨âj0îÒ/ê XÒÇ M¥?E·’ÖËy¨„‡aKÔ«jòÓ8{ttÚF¥6.œ¾‘¾?éJ“ªÆt"Mö(-šÙá3|†¾bÚ"Û@Ïê8MÈ‹°GìIžv²¾ÝqQä¢nšªƒ¶árðêpC`jÍñEFRÃ5²óiT%ò7Ûï/¬ÁKÌÖs!9Ÿ>tŒ·ìfM†Бi¢ëÙ¹ÙskòCÄ9Aÿe±9j Ö+6|h*Ù!Ù,¡ÑngÂÔ…Ý Ö¶'»º ”Ÿßl7R¬Ó­RKÂnE•)šuùkè}jLkB Gxð]¦‡5½]u†Æ£*×[$¿©¬ã ÌœŠgŒÑNíy'M3r'ü –â}<»úÐñ1„îÉ4š®6a%X~‰w:60pì#HÇ@ÛNÕ<5ÒOõkiöõ}Z~nÖBÌ‹8S¤eέëc¥´‘ v|°wð>Шžóî冨>Û0&à&À ›Ÿ36D ü¡G¹ m׃†:µ&$+_胒ÃpÄú``Š×w-*à–²/ˆ®ÉgCí­pEh_74„jþ,a‡MéZkQí>YT4Œ"ïÑ÷k0²óìg`Ûæ{±Ñ3˜:ò^í …´…2£<#2¼$»Þ5,D#Ñ1É/{AnÖ§µÂ÷XðØ„¹‡#9šµ åžÚ³>î(°Ý®u)úÈO@AÙÒnçôvÈPû¬ó c]í(á{ÿ^ϧPÜ0›LGÛþV¹— ‹“Øãn¤q1òý‹Œ¢I}©Uå‚RJaáÙÕ¨dx}aÅÊÆ±púrïÞ*|±y¾ÙƒÌ ן€AåÃñ ¾¯þ>*{„Ä40 ÅÐVÉ‘>ßæëÓ>â$&+èå1ÐBìGy9Ð,2%Ú -ÅëɻŇCÙ0&P+VhjÅÃ7™@«'@‹Â©ÛÓ^ÄîNíðÛƒçaÊCT…‹]OÂô“[ШYŠï¾Œ%4õ»w¤…®›OÒ\wx =§M|+e:Â=Œ­šW³¬ Ïí=ÿøˆ½>`À»ß,™è¾Gç#üyg òÏH{ºÆª‰þa{y0mw<*0£5Di1r4÷dçñz²_)†>nîÏûW4wô±@ +Ú½xØÑèíXU“G߇Á³= ÁMÒ›ò‰g2ÈîÈB¼£å/ê™ÇHÚîäNzåŽûDËnoÕ­$Vv[¥/¹ Móžˆû™ZÐ;ÂUgåN\ŒüM r|«hÝýö(ƒË ­qòE~ ëG4þ‚Ð¥¼Ø|4Z¯?ù§ï²¸‚hÔ)mÄ¡IßµÈ:òoV´À*¦eýAáAE:–¾£-ìYìoÁg5ÆIÿMê8¼G?Ì ŽÿÅí»’¼H×èh¸…Fp0‚Z9:ã¸Ã·só‘† XòKdGå©­›Ë¦S'ø>RÈúÙñרcçÎi£ óOx¾ÿÂ6Î7;á‘BjoÙíX¸ŽFŦ·2/»nó)`.qip¸ÂãS&ÀêC@ÉXòeM^°ð<4g’¿rZEMîlï‘(3¯c÷ ‰X€: ŠÏ0ͨ#*ûC¥¥NC¥>̓îðíÁ›£ùaáã—»¿ûBÓ‚ì>¤.Yi.¸þ*zÝ!D?G#º¾AóŠs•¸•s»¹0íp]‚ËÕѶ©uü4äèõÊÛLC½i)•‹yúÇaÊÓ‡è\Åñ7K˜˜_ÅÓÓó÷…+Õ‚yHë?°𷺵ю¾t ¶jÍnBëì[¥iSçLÊ~Ñ‹dŸq¸‡/Á›/áüC4¿BÃk;"ìw•cÔËglßçaeÓñ§c™eì¡,ë½w¾ýe=Ò»½ã…ظz=qÝñ¬ÅÎiû#Í&d¼È½ýv2Èî P–1­ÐZpòìë{)zÃñµwy°X¾–¶áÞ ß§Ýù¤)d×4Ïæe\Þý‰ ³¡´‡yc=z½Þ[uÃ8£‚תåkBÃ÷¤­ß :´P‚¼yº—ÂKI×&êÅæáHïÆvó'|äó(s4åª ¢ßE,[7¤\ÚëG.Œ5þºÜåçNøfxNp*–ùÃn[„iˆ\3ï˜ðÒú”Ë¢/ðþ<Œr¨È#¬ïhÔsGòm°¹œ®sîûðY/ÏÄ……VhÓƒ÷¸rýŒã–¾S”™“w&Þw•a-@z ¡,©…ú‚SðD „×›R‘FÇgãŽTNâƒÄÃ,]}†Q¸)ᚎf•[{Ã\€4ˆÛ£Üù1õÆøÛaøv uâcƒé‹}3ÖO€GhZÿ3æ&1TàØMJ\ÓJ§&ÎPc›vŠB¥I[_ˆîÿFe{;b„EØç÷õõ‹{¨cw4¢þƒ[4ò"?I®Ò½F£µ± ÃsòŽßá³®3ÕážáüHQ@½H1@cºH—5X³® k¹Oi@¯í¡h,ÎG|C°¨8ˆã»j?)<â«ÞX\Ž~„YeÌ?Ÿ¾àc ½ ð{~õ}4(iÑöñP_‹‰¡ï¿hÒ{8ÅwG Œ¡"Ž× 1<ö¨ö»,EÄKê ‡ŒÏCgÃH’jÇk©ÛÀûjÈCÊÀSšO;rVëÝnnÙô#oÒT£ma™U þûévñ¬îèãíGçUL¢eŸ;ñ+°£×èû0?Ñô·*ÄpAmïí°?ü?Ž)S}GÚdTØ Cª¯ÑàýÞ ÆÞš»ÃÀbî™xß°Ëš¸,°9‚< v?cÄ4î!}=ƒs©ÁïÈylˆ\Èó ßDºÏî3<ÇAÈ“! ³LÇš¤Ýá#}o;×Rúª¥ÖÈ@ѱ;E c òÁGŽ{÷‘ |/ëL¸ýñAARÓ6mDÑþ¦÷ñõ=$üAO·¯ÆŸÓ‰‘Tl.ÐYc=CCá[†çõB?Ê~Ö–šVí‚ø¡Ç+O*dרéÕx©8&Ð:à}fØ↼¼îz‰ìשƒ¶”¦¶E&4]ÌØ¸mÏT‘¾,¼Æ(:Œ¬d¢Q2Ÿ‚ö¾ô‰‘_ø&_4­¬;ïaÁúIÑB¢i_þ¸”péšð®=nŸ]×DûÆN4¿µ£¸üúk·R]î %6]ùda~VÖjj\ÆvsÊîÈG B£\ôÔLÙNz<…3î¸uM,ò'ƒìNj;ŽÊö7}ZÇÞbâÏÎÖÃ5¹·§‹Ý,åÙÚ¹£Z,ïp}å¢ÅúØ9±Úük+vþª)Éq±§r¯¬\õoç…ÍùÝ*{¬5[{>ÑÝ+´­ºØyEäG8ã’`„ ´R¬Ð´ÒËÉbL q Ð 'Yþ-Íçø@`üŠh1ÑZ€"}û&ôÚÿŠÑ¸­eˆÛ1&À˜hËxÊY[~úœv&ÀêMSTh‰Í>ÈØT“2C–Û ŽmbÙ0&À˜` #À MÂÐrÀL€ ´F4¥ CÛaÊY7ŒÔLŠüžmÛ›éÏ;ßTr.¦¤éR“sZ#N`L€ 0d!ÀSÎ’åI°L€ ´¡PoQb}¥ÆþÖ ¦•-Âæ?±fg,ä€EÆa·› ÜÌÂÇñØ0&À˜`‰"À M¢Èr¸L€ ´jôÍŠUÆÂ³¡ÄœˆihØõM´‡"C‹êW ¥-Týw~¥ 3SoÕ8qL€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜@²ð$‹ ,`L 6øÖ‹Üœ’ÒóпŸØíóßÛR›[¾×6 Ï~zØñCÔâÞÿ*Vc﹧݇³Ë3O+ÿäí·ÛäwƒÆîírбÇòÅGï.•¹ËÈÉ;âÿY¶øƒ·ŠëãÝ2&ÀâM@‹w€`L žÆîéšá=˜‘ÜX\l­.êÏŒìàz4^ç Ï ]ϸ8,!Õrë©RBëËs̼=xV†?÷“튊•K·l.Û‚ëo‘·º>0µO,a´D7Ñžo‰^t½iYgL™Ò9Ö4чe…e}(Œ’,·Ÿhá»ïó9`L ZnÅ•&`IEà¶©S;Eó„R×H!Фçt¯Oþ]I™+¤Ú¦aÐ&©nÂÁ¿nLí× ’Rc2rB—(K½ „04)®ñøäšÔ†!_½ O»‰ÛV×è¹ßÈôç^[h„ "“ Iß³h ÜR0aBÌ#ŸÀªï8)}¹Ãk ùÇ^>gL 9x“C –‚ 0&P•@ ðHÚÊm…¯Â¶¿Wú™ÿ“ËÅ"œßçºæÓ8‘3ù`Ó2Zµ2ƒ‘™3•e=åez_Oö­€´*Ð}†ã ásh&_Pàþ©ÖEJ6+8þØÑ¯^fN®¦ÛC[È?îôò9`ÉC€šäy, `.…ÖÊ!™9NÓ´Ì™¹U”—«ª§ÃsòŽ—–•ƒ†ÛÁÑ1ªó…W¤LˆP†¦-Bô%µ–¥FÃÝžpÿµÔä¿ó'ù_¡|¡¬ „~~«„”3 rýÓØ0-éJ%ÅÐÞôë1‚ôâ;N(éÃýEŸgÌìÀÄ?·tÌä§+s].šÎ§"žp»DJ9··7kš«A-ÂáªÓd_†Øfܯ'À}7!ä÷šP9³ƒ9ïE†+ôuSÿ©HCO!Å7©ùgåf}츋5ÌþÜá–2&‘¿2]Œtȹ¾ ä?‘ìp}ªÚ¹B¨“•Píßg¸¿0?˜= iÕ M?Z©‡.‡\— %öVRµ‡¿_À9Dœ±ÊXé>ê-Š)´Çàg!Ü>oêM†QVéh™Â/…\Ñ×;x¢›}Mîñìê^䟺ž3É_)«R§àÙì.¤Ð^íD.xÍÁÔºÓÀ3žåávz•ÌÅó}Ö–GˆqÁìƒFä‡èTŒZ]:;ý½›KF o/¥[Ï eÏf½JïÃÇI±©)ÿH Y¦ºÇ£y®š;±ÊÚ¦ÉMÝ|IxĤ9“üÏ»ãâs&À˜@}hõqÌn™`MFÀT‡¡áYrÒþ{<Kœ#²ƒ—Je¾‡FÜ&l7á‡nvÙÅå_dæäý="Œ}M!î¶”u£&Õl©‰ ¸ß ÊÍ\LË™ŽéHw¢‘þ´”Úh4 WàúÞLóFÊnR¨Ó·EQ£_hrŒÐÄ$)Åß,ÝübTà®ÊuÀÜ¥¯[(,†½|TÓäˆïUlrpG¡zÎ Ò>ÚáŠ3ÄvýC¸Õáv"ŸŒ†h7ÈûÚõþÜ]÷ùù>„»×—"îW¡SÜ ¥!ݰ¬÷3n qÜ!ÈZw˜¦ô,A\sÃr¨û1+…ÏVâ2³ƒÇÁþ %¬vš”YBj9`» a¬I™¡pHa€¦3 òA1 !ÌñˆÃ£Lñü(ÿÔ½í¸è/FÉ)-`W%ÖP’Ž„b€,×ãY¯2ôò…Ø7I­Ùг€ƒábN 0¬Nå'QÏ®>éõ9ßpÿý©Pz>•J]‡ô½6PffƒÿrŸ”åÈK#í4•îkz¶ôÃ3ùÒ¦þPT óÎS /ínÖeö=÷Ÿa^?ƒ­š£0ï ö=ÉIMù§·ÖgâíoYæ5î èïÊ%¸·wZšöQä=¾fL€ Ô‡ÐÔ‡»eL É ¡s8”“߇ †¶|íæ–iÓÚoÝ\6M ùÌœ ÿÇ5.ÿw¥1¾eYÓ¡@én|K%wóúv030z;¹‡²ðf™¿[RŒLóù=¿‚ìÑP|¦lí–J˜çáò²#ƒFz'4ŸÏúïÛç×ÑH[‚‘‚[`G?±Òøu4ˆûa¤h?×HÑÿFøó>7•ù ¥ÓòƒY¯;a Üt¬ëx#YŽ]f øŽ¥‹Ïu!ÏܵbÝpüäõiÏ d]áö¡áþàŸX¬=×á^xrC˜sr³>Ä& ½1Þqcª7å'ý.žÃÉ8lìëõ_íÕx˜îÕeæ„üG¸Ý@‘ùXWå?R ûÊi„±ÈHáéE7Bê)eÊÞù¹·ýZö‹Ãs‚Û ;)±VÕ«Ä’0º”еXKªß­n“¨gG1ÅšÞXŸsÙº­#¡¼ìë‘ÞC"GB*R6#*“ðŽü>;7;¬¸VO²¸küømþàóPØ/Æ;3ÑýÎÀ/õb´µ6µåŒýyè2(ˆ7»IØa$T¾þ@VVµipQDc+&À˜@й† 0&„¤Ø #4«b‘lÛ¶ò£ÐPíŠÝîí…ËJÎ@óñðQwNÙÕ}=Ío8Ê Ù?Ìùñý‰ß"wcþo,CÏÿ4ûWñ MùžrÛÑT3¸{´î°WçÃÿ».eƾESvÐXü £íp>S^Ïãn»Ùwd/ÆØÃzKÊŽ=&y]ŒÆû×.eƾùçáä»ÇÞqŒc,aºœW9•Âó-àÝVé¡1öîVUîÖï"ÌA®ÚÑ7Òg,2B9<,ÞÏV*3v0)í¿‘áU»6å^dÿ1å+PKȳsäŠ)½1>g(çƒé5(3N”11ºóž÷€þбŽ{”S©]ÇcŽ]ÌGÍû¼›] ­%g:~FBÀn_§á9ð‘ 0&PA€Gh8+0&”  ¬Ä(JŸ˜„Sj¹óøR—Fº—š¶Daá„i»ãÞï®û…®sûÊÈVô@¯Œ´G#n+ì:EÚ§uõ-‹´Ó4ñ«e Ѱ †ƒ ¸TŽì8öGŒ0(’«ŠéÔÞ·ÜmA½äè5ß9R{ðÙ ÊK»áþÜ;:¢aÛÍÜXD X%XÂ$ÿÑ FžCï~iÉ[©/‡-³ ÒÚk³béYÏÈ™²¿Æʲ×)õCø åÎæŠEÆ£Œ»@%©¶Þ"¥SÚòò E‘AV¹–ʲŸ-FìbÊW‰zvŽP±¤7ÖçL#ŽÐÔ^vÂṉ·–õþJ+´œFdÎGòÕ¥üZyÒ{¾_ÏÀ r'|‡üó©0ÅÕðj?;ð0Ú#7ôÒ¿VÏàØ9`L jJ5lÁ˜hhìüŽQ”sçÎõĽL5ôjn=ʶC¯sªû”„÷µëÜp×zê1Íje(Fchá» @*Œ Ëà\V• …÷Uä¢{Ón¹¥¸ÒM 'hÀ¶Gs1hßUå§É M»ÚH“U¦ñÄf QÙÖÁ¿H—»bšÐHؘ²bóOZ·T›|;èFeéßB™Ù:Ùÿ \Ž×¼ȨÛÇ(cZ×e‘ñÞ3vl)Ú§'z½¶B‹¥6Õ”ÈÈð*¯ð윰cIo=žsº4Þ„§ªÇ1zx­#¢Q9äçaxgžˆe h4 gæà}JëËÂ ØÓî)hÑü±`L ÞX±&À˜@SÀ®^ L%®|û»_¯Dܵ®×°„g1>)"¬b9n7U‘Õ°í0f¡ýVÅ>ÅEFoS9 b©ÔñéYŽÆ0R-†nƒQ…(FªhÜÇ´ž#Ò7‹¥hì–Ìf?y/Q×ÙÙ4Uk ûóÊô­OYØ: Þg£5J©!¼R_r8|RôŸïÈ”¸»özÀwè{νÁ]ßH·#ï¼»;Æ üÖî‰û ZþηK–C!º‹íï.È̬®»NгsGQ×y¬Ï™íä3Êÿq1>L{,×Íì•Ö¯§`ÐJ¾ê.=žÇ¸ÇÛõ]ßt¯i”]0âöÉ_à좴†‡×P9Ø`­“@µÞÅÖ™LN`-€Õ¿Ç£hœ.ÅöÉwfúïÚ£6ùÓº·ÿíXôN—Us§¬ËШ^Þ[ì^Uñ¨æ°þ†ižáö5"0­¦í%æ“J{%?EûT»!_i‰-v1 SšÄÄ«÷\Ö1Ÿ"LÚÑí”ÑÙÁèÊRÌ!¹†×,•›&MW«Ñ<¸q+Òx?Òºójó·}£9\§/éû©X'Q¹=³íÎ,½énxgx"œSi#w¼–Y~²û:Ú9.`—¶Ûi—X±nR•Q´hôì¢EU“]¬ÏîæC‡™ÏÜáb Ù*¸«õÙ:îg²°µ¸X¨Lu>¶]ÆèŒøcL€ 4œ@Ã+•†ÇÉ>™`u ÞóÌÛC#°•òsJ”}‰uÓ„G~îUÚZl;[cÓÏ‹´šîc‡®«±½òãØQi]ŠTÏš"%Å’úu𶨽<Ú(BBÔâ 2[1ß–™Ú˜âñ¾§›VwË(›Ž†Ÿ!¼žÉŽ×Î]RÿØÎTFÉ<È6ÆëKYjYú–iÌD?÷öýÇm½Ž|!¹]¿Ûy;¥MÀ,·oÀiW EaW1˃)bz…ǧì¿ûü·¿ýe#>ÏTC7¯Üº¥ì…QÐxMx×ê†y”RÖ$„[Ó4ÂÊ@{y²/´Bg#¯LÀ³;tDNèISh?hÒê‚Ýá°i€–Žæh—8‘°gW)M '1>gä«v×»J%oὯYÞeJbßì¾ø¾Ì½òæKȳS°ÃÞ´õrûÒϽqk‘â1<ãI÷cÎkÊ?•¤gŽT”}9 جƒ `L >x„&>9&À@ Rö»iÞ”ýô[ø]/LëuÃ2¾@/o¾¥ÔéèiOs¢“›ïƨ‘9¸¨LÉß Uþ3s@ :??7û Ç]¼Žh—âcgâ[6Jõòå¦e|‰°ûbØI¬ÊFþÝ·ÞZÔ.]ÒHÎQyI×ËV™–õ"F”>÷µ×Ž¡Ø"m«¥ÉC1è4柰TÙŒ|¼‰uãù±×GHíVȹO¹Pâ›öt8(3&â)Ýüi»^´Á°Ì°yB{¬‡9ýÞqã¢*´,.§¿ô{ô2&Ðö`ô™ `L e¸!/¯»*÷ø Œ·w«ªIêÑyy;c‹ckÖĉU×ÓÔ䡞öþÐÍhàßµ!˜Á$ÕØÀ=]”RO]»~J[§‡úàà ]\MTÚ8áo(ÚiÅ'ï±ÇÚx„M_q×D§õÎÖÖ$û&_^wC—5Ñaµ=í,š0Qìèc˜e©¥2Þσ¦WµšUÇ(C‰vXч+=«6öï)z­ ®.Ýq§êY¢ž]ÕXj¿Šõ9¤­Ó {>Ì^Fù32ÔÛ¦NíX´Uï:#ä'e3!&2ÿ8‘`·3¡Ù†wg¨cÇG&À˜@c °BÓX‚ìŸ 06G R¡is8ÁL F‚‡›ºú”Féh+ðÁ^˜`Q 𔳨XØ’ 0&À˜ˆQÉ»[ºxÓ èíXÓw™â‡Ã˜@#À›´±ÎÉeL€ 0&ÐT2³ƒc1ÕƒºntŦ _J¯çÒðwnšJއ 0¶@€š¶ð”9L€ Ä•€¾—¥4þˆ¶>!®q`L …Ð:xæ‰ë"läðmèГÃâ3&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&ÀêM@ÖÛG<ŒÌ™|ˆi™Cáu%UDÚ±Á°&À˜%Ä6©äJœþåщ7Q 0&8ÍQG%D¡Éðçž LsPÑRÎÃïI-]{mÖĉ›‡CfL€ ´-#'OÞÉ*¶NWJ]Šß?Ðq´ †sÞj-$¸>i-O’ÓÁ˜@[#ДuTÜšŒœàe‰»¥TߣñÆüÿ£¶ö9½L€ 0¦&™<ÎâÄ»¯ÔÄ-¹þéM-C¼ããú$ÞD9<&À˜@óHtå‰g²Â•ºW ù¼ðõ8³ wÜÒx†Ïa1&À˜@t‹?~ï¯COºðqiï‰Ñšq‡žpҖŽ·(ºëä·åú$ùŸKȘˆ•@¢ë¨¸Ðд¥äëð…ü`ö…˜f†)tl˜`L ) @™‘™þÐ3(€ÏE1|ZKœ~ÆõIS掋 0&ÐtUGiñH-ØD8shš™ðu¿Š•™xPå0˜`õ'`—¿(‡áóüæT”Ïõ¨™|p}ÒLà9Z&À˜@HT…†v3³7Àš™‚@fqðà(˜`L T£p¿Êe{·ÉÜ%£5×'ÉøTX&&À˜@ü$¢ŽŠ‹BC[3CãšÇÄïasHL€ 0Æ ò˜Êe{ëüÆÔÔ~¹>ijâ`L É Ä»Žj´BC9 gF>Ùä48B&À˜¨‘š'©|¶Ëé]%Ï ®O’çY°$L€ 0DˆgÕh…Æ´Ì¡ôÑLúÎL¢Îá3&À˜@ì¨\¦ò™ÊéØ}5ŸK®Oš=Ç̘hjñ¬£­Ð ñ»à Õkø£™M 8>&À˜@íìrå3\íR»Ë¤¹ËõIÒ< „ 0&Xñ¬£¼UIÕa¬ll8ìŸ 0&ÀâOe4•ÏTN'½áú$é Șˆ+xÕQVhðÝ™ŽHÙö¸¦ŽcL€ 0¸@Må3•ÓIo¸>IúGÄ2&ÀâJ ^uT<¦œÅ5aXò{Ï=íðÁ»A·L›Ö>y¥dÉ܆g?Íôç^ë¶ãs&À˜H^(·/ÏÈ~ž¼V—lxNè´>«~‡m˜@ÓhôMӈɱ4'ÌÛƒg)Kß¾¡èï$Ç–Íe& ®±vêóŸ/çÁÀøV5å0PZ ­x1Ïð߯—qOöx´Ìž”ýfd¸7…B=‹‹­ÕšGþ+’ÿ•Èûº–ê %´^ ƒ=3&ÀšÀˆœ¼£±QÄ|!Å‚‚\ÿ±ØIEŠÆÿ,ôð^òy/–ëx—÷±ÄY—¤³ꌃëry¿)xEÆYy­DOœ×[æJÿ|ÂI€Gh °µ{ÏÈ ]eæÔ"†&Å5ŸÞ~YÀÕ¥¶ÛZþ4©þ´” AÖ;2ròþ¯ 7ëÓZœÛ·2ùéÊ\—‹Éo§‚Ï@Lñ[‚©s{{³¦E> ùïƒÇcðCTjÏ›z“a”•GÆCá }ÝT¤áT¤µ'¦s|㑚VnÖÇ‘nùš 0&ÐÜ|^o¶®›C !fA–3b‘§¶òº¶òåúÑ(w)æœãŽkZÞAY™šŸ›}œ{êâA½¢­™̾‘ÜÇZnS=ˆ2ýÍ'Ê-]ÜŽ8O@=5õTЯs÷ƒPÏ=…ë×PgMrì£Â+V¹)>ÔÑ×)e€üƒÁëgÔ#ù¿Z]Cnk{tŸ ˆ¼?l˜@uÀÜRâΜ@`XÔ‚ÊíkDvðR©Ì÷ÐÞ„Lu~º‘] QþEfNž½öÆå~_Sˆ»-e݈†þl©‰ ¸× ÊÍ\L˜Ž)nw¢ÂxZJm4 ̸¾7ÓŸw®Ë?îk*óß–TÙ8DxÄÕˆïk¸½/#'H£ ¶ñ¢¨,MììØ9GK‰ît®}R–kš|i¦)t_cZ]~P$¾¤û™ÙÁ !Ë<¸÷ÀÝú¬‡…1}PÖ2]è>…þO²þC|)Üš ÝWúº…—“’†8¯§W1¯úŽB=ôœÛߘÀ½]T‰õ*Û#¡„Ô®‡|« ½|!*@$i‡ÉÈÏ÷!ܰ¹T rC±» 37æP}«yÄå¨kæ`Á)êuM¤u=‡H÷|ÍC€GhC¯û]%–€Ê#U“ž%u%“Ö…lÝ\6 î3s‚þK÷Àûÿ]iÌŸQ”éhˆéîÕ’Jîæõí4`f`´½å÷õþÜ7Ë”øÝ’bdšÏ7èÀøÎ ÷ßÿLÙÚ-•0ÏÃR×N@IDATå NØÇ=Û{;ì2=0vsÅõóÃýAJÀØi÷Íܺ6Â}—3²²6àæ\ô‚MB:~Ÿ›=×q RËÖn††ÿ«s‚9gWØ? åë ¤ëa([§å³^wÜG;J¡Údfê#ÁëLC-(Ô—7)š[²[iü:•Ü~áÚÏ5Âõ¿þ¼Ï¡È½â޳H/BÏ ì)eÊÞù¹·ýZæ‹Ãs‚Û0VCŠee4jźáPÊòú´ƒg²¿®¸ñ¸ý Ek*®++ëJO|˜hFåFy;Œ˜<…ÞþKÑi5=#p÷[[ÖG)–òå=•×QËûŒœ)ï Á®¥ÇÁÍ‹‡¡+”ûò ”É©ÿ¬ìö2kÛ±ÔÉ¥4­)­W¹Mî¡ e{<›Åø«mC÷ÉŒœwX|Îb!À#4±Pjƒn¤²ì­˜1ºÒ§Îä+5ˆÜx|©K#ÝJM[¢°Ç4ŒÝqïw×ýB×¹} ¥e+F2ìxÝ÷Ðë³×Üvö¹”Ë"íÒÛ§üºes)Ê\±K但^c]Ðnäɸ =„“p0Í‹> ½lõŠËçí’eÏR†‰©gê˜M"Ï ²òˆ0Aqy¡Ò¢ê Fañt â—Ï;Î1¥SÚòò Uë?({»¡m‡tü踣#6š^¡™‹Hq¬öÉ &À˜@s(LX†‘ñ,L¾Ç'óƒ9óì‘˜Ž‰GyQúwQ*ž@af¦ Pº~°GŠ[0ÅÙ‹¨Æ`FÁ®Ô Ec´{t†ÜÖ³ÜF±­œQrò^Õ(Y„™³P6…™§ß·±ªƒº¯báE¡Ä*7fJ„ë:uPUƒºp9U¼Ž‰ÇspÂâ#ˆ…+4±Pj‹n¼Þß…nPÇ»á\+ ™jèÕx”Ž-,ëUzþQ •Ts¶0j°¯f EÇîµsßÐËË;Ð5îÕºFÅö£”Ïí·¦sKÊöT⣗î~¬«©ª!À“)ä5ùfO#SÃo¦õú*#8ZKõ>u©ºSvÑŒ°¯Â3 ò•E:½gìØÒ ÈtÛcª_{Àù›<é¶'`DÍH“ Øá­JL|Á˜h0Þ^ÿ ¬#¼%ællú²Ÿ¡oªRNÆ£¼ÆHöÛ(ñ/¡u4ÅFÑYJн¼Ç|„½`4LÞV.Å™˜fõx±±ý Œ¤O¯’˜ØËmÔ)²Z}℅ΧÎ8?ŠÂWXozßmS§~|×øñÛœû±ëâUNLrkiTWYifµúFY¢²Všx<‡ÊÀø„ Ä@€š µE''î3hù;ß.A‹º Éï¦õ5q°„g1f «X€›MUܶTí·*öq¸@/]ïÈ`JŠÕqdMç/:šÂck hÈWQ¨èj“>¤¨Ôe4¥-Å“^máì@ö—u¹åþœIÙo`½ÎSˆ=Ï,SoWó#ÕbT”»T³' ©@Ót¯m¢´öt;ò뻣ïÍCŠŠc°iÃR°(™Ì~Ô±ã#`L ¥ GçL¹®\é_êÆæÓ´.eëç0MÃðÿÜ9“ïôz´5ØÙåLK¨ëÐhjf`¢[zöiØÎyŒæñ¼¨LٹЇ»"ãDõFÔ§`7±3¬ÚÇíKS<˜»¼6ÓÊÅb™ÉH_9¾€éZ²XÖaè ;BHÏŒ‹B#C¬ùú¬¬uóƒc1JôxØ$ª0ØÍæßØ9îLe”ÌCœc¼¾”¥–¥h™ÆL(h?÷öýÇ­×ç™jèæ•[·”½0*¯ ïZÝ0Âw&ÁmÕ©}|!¹]¿ z;¥MÀ°Ó7J”íŠgy<*Ï€.™`ÉJ ·gp¨Ðøå´Æ‡¢<®9§Ý-c-¯k(ï7†Ó¬½ƒÉU—£T¶Ò»¤¿åpHóµ £&í Ô\QþG{:Ö§Üvû«ë¼ 7û)Ô'aV>K0eQCꛚxÕGî!ÿ_¨³þ ]k"êü•©ï¼rSí¼ÒÝŒph$©ÒÔç9Tzâ&Ð ÷É£ëe«LËz½†ŸûÚkÇ8ÓÈ)oø8ÎIèÁÛM×­¯Â,Ì ’ž‹‘îoÜaL˜°EK“‡¢ðž',U¶þÞÄ®iãÜîøœ 0&ÌìN2%Q>GŒnCèXËëšÊ{J·¦a*°´Yʼ{Ç«ì¢QŒì|D÷å6¹¯—ñu¿uÁŸXêòLäh|,áÔÆ«>r÷õõ :ÿÅì‹v]c ñ ¶ØU‘rÄú"ýñ5ht>4Î`ÀzvO #›ÖK€>ÊèYµ±OÑke puøÑyy;cA 5kâĪëiâˆùŽGþ›Fh¸{µ˜2 —8r…»¡vЌݻyý‘²yß}˜‹¶ê]©GÊmOç´P´È(Ú©KÇ”µ Y¤^]×”¾uz¨ÿñ^ASksOSÔÚ ÍŠeGœ¹sçzÞùö—¢V|ò{¬­+ìÚâå{ÉK %•Ñ-IÖä}â,™›@,åumå½;¬úœ×§Ü®O¸‰v«ÜøÎœwµXÔÖÛÆRwÄò6?9 $M¹O‚8Â$'*–ª5@žÛ–™¼³5¦ÓÄâI %•Ñ-IÖx>#‹ 0&ÐV Ä«Üç)gm5qº™`L€ 0&À˜@+ À M+xˆœ&À˜`L€ 0&ÐV xÛjÂ9Ý-›€žK½ÊókËNKϘ`L€ 0&ÐX¬Ð4– ûos‚Y/7KÄ)`L€ 0&À˜@Rà)gIõ8X&À˜`L€ 0&ÀêC€šúÐb·L€ 0&À˜`L€ $Vh’êq°0L€ 0&À˜`L€ Ô‡+4õ¡Ån™`L€ 0&À˜H*¬Ð$Õã`a˜`L€ 0&À˜¨VhêC‹Ý2&À˜`L€ 0&TX¡IªÇÁÂ0&À˜`L€ 0&P¬ÐÔ‡»eL€ 0&À˜`L ©´ØkŽÌ™|ˆi™CAs%U)DǤ"ÛÂ(!¶I%W ¥–y<Þy³r'.n‚h9 &À˜@‹!ÀuE|Uk®oì_›m*ÕukóÖbµ­¸T`fNh•a˜Ó;•vxàÞ{Ç•â1#ï³aL€ ´-\WÄÿy·²úFŽ{OÚÖ´í7x½ž1v{BkÛí‰øç1"Þõû,KÝÓ××÷Á@àjj÷µYÓbš î©«Í•£ÞwŸÝúªÃöÝMì¿{_™ž–êi³OO[‰+.-ß--Ÿÿð{¯ï—®˜Zҡ䦫nõtZðM°±ðcŦ gN:hK¸®HØÓn õ &s õãÐ’´’9š%ûì³[nO$,ËpÀ í=œ†öñ”}×sÞJ@œ-"È¡Ðà!UJNëÝ£³¸ðÔ#ÄzQ¡Ä¦‚”:qÄ~»ÑOþºlµxúÍO{®µ^¹fÂíž2i:œ™ø‘bÆ 0&Ðj p]‘øGÛ‚ëjzP/Žñx|Szwטּ=‘øüÂ1$†@”÷°WáÚͯ£ ¼Jͽ‰‰5¹Cµ5½$Q†+(qÏ¡{Ôn»ò ”™$·ùE#>ã¯:ÃsèÞ»JÚw]u›¤òáG#Y¬6ÿ#b ˜ˆ?®+âÏ´Î[H}CõÕ>ª©^¤ú‘Ûu>^vÐBìxjJ‰{¨Ý ÑÛ\{/™yí„ÀP™Aᣮ;÷x™âk˳Ëb³ˆñ"n>_jÞÅ×ßüOø&¥†žw›Ë䱓c—L€ ´@\W4ãCKòú†ê;ª÷|TR}Èí‰fÌ,u¸ßCj7Sû‘µ©ö^Ò*4v>Ÿï¡~=»ˆ+Î<¦M=”xåxâÖ§{Ñ©K—YûíwDg„ë(5ñŠ‚ÃaL€ 4+®+šeäIZßØÊ ÕTö빓âöDå#ã“VH€òwß]µŸ©ll…I¬1IɪÐhfWyƒiY}0ÇÕÃ#35>¿Zo·‹ÿq¤f)Ñë€ Ç©øÑº©d}‡o2&À"p]¤¹.“°¾¡zŽê»Tªÿ¨äöDs厷©Ð{xÑÐ#<Ô~¦v4âm3í½dL(Æx¼>Ϙývï§xÍLã^âGÛwì8!‘¶žB|ñãQ/@`Ø@‹%ÀuE’=º$ªoì¼N£•ëŠæ{QcN‚úÆÉTϵ£zê?nOD}\lÙJ Øï!ÚÑHÞ@üX¡i†çL –Gh²w—Néɦl5ŽøEÙ¥s{éñy{ D÷ 3Žb‰ 0¦#ÀuEÓ±®wLÍ\ß„óFx&BÕ{$O½Á˜@ '`·£ÑžF2H¡iõí½dK ÉãÕ„ì˜æóqÇ—‰xz¤ÖAÒ°M9ãÝÎâÈ—ƒbL I p]Ѥ¸ëY3×7vÞ€ÄTÏ¥R½Çí‰ðó[³a«X½~Kýf».+×ÅÏ®ª1Öw?ûQ,]¾¦Æû|cÊ÷Ôž†M›hïQ"“ÅC?*nÊ̲UDÞkÏzíÜമݸMè†!úöØ©Áa4·G|A–¸:ÊŒ3Ivª¹eãø™`1 2‹~q­+bŒ»š3§Ž¡^¯GtëÜAœrä¾âèƒWs[_ Ã4ÅO¬ûïÞ¯¾^›Ý}3Õ7•y¨}“R!G³óhJ²gÿñ‘šâ ¿YŠ)öÞ­·xü•â' ROÃ{ôçžx¨HKõ‰o–,ÂÏ{ôo-üv)â„C÷ÇýmO;ŽUë7‹ÿ›·Hü…Ñ£!ËÙCþ·ýí¡à'^ÿD,_³Aô€òtñ?þ.vƒ|ñ2=UŽ2SÉáóM¼ s8L€ $š@eٕȺ¢¾‰è¹sg{Íøì‡ßízàïìnì?ÿÎâË_þ(ƒížósO:Tx=ñÙ÷¿‰W>úFäŽ:׎®¸´L„þóЏìô£DŠÏ+¨¾Ú^\*²|Nì;¨¯¸äŸ¯±ž áGÏ|æùCÄ ï~!ÖnÜ*òn¸ ¾Éˆ›ûfªo*ób×urÄ-]-9 gÞüT`+_;ÕÕNyøÅD‡ôT1ìTúÚƒ°óê«#&ùf F\öϼõ©Ýf¼Ko1çÇ£‰_þZ÷à¼eâè÷˜F…÷(Üd)× ñò‡_Н~^&ŠËÊE:ÚZØÒUÜvÅ?Ez»„ó™ø ïœžxØ>âÔ#÷kɯA²W”‘î÷¢Õ¶÷¨'#™Œ=a2m-*/¾¿ʉW\JcãÖ"ñÚüoìøh^æ¼O¾œr„¸ê_Lj½»á¥Hµ• êy;xÏ]lû]ûv³+£½wÆÐí âª3Ÿÿð‡­Q@åº.~„²´è»ßÅù'.v;zYKËtaš–xðéwì—óÒ)Ž9x°èÝ­³ÿ_[ Ú¥ùÄM— }»ï¥g¡mç?zæg:²aL€ ´4N–TrS;ë›_—Û2õéÞÅ>R'Ùg¨¨15ôïû‰…ßA‰ùð+û^i¹!6lÙV™ò¿aËvÔ!&:»ÚcZMÑ¥S{»ÞrØÞ¶»Úê ò;ã™wíÆ)MI`š£¾qò“­Ó¬„Ú?ÛŠKljk§ƒ-E%v;Ÿí—[ŠìË£Ü{7Iñì;Ÿ‹ÍÛŠÄÿÞÿRòM™!¤”|Nág `”bÌáû²Ã!~úc¥­°²÷.ßN±ÛO”1UÊvóÄkŸˆT(1¤Ì¯ß¼M¬Þ°c …¹ø§¿ÄmW&®<ãhûݹ£P¤d}ôå¯â»¥…hŸ'Î<î`[É_·i«füsÞ‰Vôd¡q@ø„šp)F^Ȭß\$¨Â¡ŠÄ£iöqé²ÕbèQû‹½1,J¦_Ï®¶Ó }Õ1'±¯ÝS@/­­ùá·BqzsÖ ÛaÐ(ËÂo—Ú/5àÐËzãŧ`.éŽïì•”‰ß ׉矈y¦©âHÓŸ|Sl@Ø;w¡5]q3NÆN8ç¸IÌ1&ÀªHª2,ÿ¹÷E k%¥åâŒÖï‚12 1í°}wT_¡Ù'¨ÈMm¦KÇöb'(3+1¢ïÔ;µÕ4ó€Ì®¨o2ÎR[ÐMy¯¹ê›æŠ·)ÙÆ%®hí”÷¯5l]¤QÄ»ùy3f”t§QóèÇ›è(&C£,4Õ¬ÇN*Ã'åýœÓ±•²t+›ÐáL×c.*öØí©Þè8þ«Òï(JýÐöêÜ!÷Âí)ÚT€Ì÷K—Û#>=wî(è× Çäwèß÷¯ô߆Nè}hõ&š„CЫke4¤J‹ýi“*êøFp>úr ´úÅ SF3[¶£Waæ„®]аik±=Úí–FfÈthO‹ Q†Þˆ ‹’q*;û¿ü¹Ú–á±Wæ;Vöüëk7ÇQ¡IÜT¾J¡ù„ 0&Ð ކšn˜öÔ›¿íEŸü‚õ“âÞL¦?:ÈæµÄ¾‰‰Fðk3µÕŽB³_Ò|:Yê›d‘£¶'Û|÷¢µS¢IcYUó&-Ê'%ƒv$#º¦EþÖŸjO9£1ÿ†D³O†Ÿ{Be4ûÕ°éuò’€w† uF»;x÷F§0MG£)þ…XF@ÔmBÖ%è˜þ SÛ¾øñOÛ/ýÑ.olZ/dRhšLƒ¤éf5Zçr(v¡yÍ4<:¨_OL=«¾;Ú»Ÿþ(–¯Þ ‚£Î·×ÍPOE¤¡9Б†¦®‘YƒùÍîõ1i©a·c.Á‹%¾ÈpâxÝdÜã(3Ř@Û%”em@ë–._kO1¾þÂSì‘}|  ©Ódhm ÔS/·ë/©3Í€"DóüW¬ÝXëS­­žpÖDÛªÖ@›öf¢Ÿ]¢ÃoZZM[´v EK3V¨Ö1Ô¹ê6¤lüò×*ÑÓãß\ø6Á€å+µãïŠGj÷¢á6>äýhÆ™Ž¿~óv1m'Rø·nO™#÷Ôqð– üüçJ´ÕzaêÙ¶ ÈAï× ‡ì…‘Ъ;½E‹§ ÙÕþ€Z8ˆd›_J°ñkž•k6a¤d•½ópÌå$Cé…Ÿ®\g¯ƒ¡!M í„aN /=­—¡‘šX ½Ì¤PÑ,õÐüÑk6â%ïxiâõߨÓÌhö{LaK€©`\¹Ž&QpL€ 0„hÖº¢¶”]pÊa˜îR(¾þe™íl¿Aý°0y™=嘦ÈÐù{„GpöÐ ó¿ùÕ^ðáâ_ªMõM;¦ú†Fzš°ž¨"G#.š£¾iŽ8(1^7o+®¯ü‘Ò\³û€ž¶r¾jÝf;ïþðûŠJï4òø8f“ºÏn}9´[Ä[‹~¨¼yBÓ,ÿZµÞI¡5f´>9CÊRÇöiö4ºó¾AC3jCÓü‹°‘ͤ yÛcÇ1ï9@|úÃov›ŽFlhÇ=Íq×úv{Úy'Zur«!´êäÖž8Z óo.Û°ŽöK§œ=wÏõ<š>íL6æî'ÅX|v|_±vÓŽÅ5ÅD#4´Hí©yŸˆ;f¿`÷‚ ?÷x{m0ð_,€£=äÉÐÎ ûŽ8—6í`Ø`-€À ~=°“Ù®öNM´Nò"ìõðK‰ž~=̦=×ÿ‚“ÃëghíÍ  jž{ûsìÄ´·=mÆI&­e˜ÿõ¯bâÏb—³~↋N¶7¢‰VO8~øÈˆÀ"l>A?ÇFœãœÆt<ë¾~Æ7a&Íy­íÄñ‡ì)æ-øÎöûÚG_ Ú``Ø)‡Û ÇÙ'üM<‡ü(¿’ihÇ424õ’òøI‡GŸÊé®ÏBØÏ`ôÏü#2+דÑ=j#ÑŽ´´Þ˜F(©ƒ™ÖÈüëøƒ±!ÓahO-ÓŸz˾×.-Å^£Üݵ~‡Â`Óz4º©œñÿì}`ÅÕÿ»ÞT-[’mYî¶l¹àŠM±1½wH @ࣆ„’þ䃄|1&@ !¤HB3˜Ž îàÞ­î"[®’mY]×ïþ¿7§•NòIºÓ¤ÓiÆ^ím›}ó›Ý÷æµ_=·šáxóù_Ïáu…uŽï/áî'~õÙØƒ§þìŽKè®ã—²Ÿ‹‰Ãÿµ(<“fÅlG>S G.cóŽv3B[æþEñ³ Dƒÿy¡ü~éßKhWþÞ‚ÿ¼ò‡Gq])–r,lPÊúÛЦnp,‰@Ï@ ‚ßèNop´FÍX <«ÍLW “0>¦Â?S¨ÔC;à ™¿IOgŒîÝ‘}Ý8Þ4> ›mÅÓoôÉW&Ž9¦»ø‰ŽàMר‰¯-3ý® •CM³š?¬©yòåé6D‹M¿[k¶ŠÉ—¿½q"˜ÍÔø]±˜Œûº‚æh¸¿‡ùûJ¶¾5ÿùk@OÔò{A~÷Û…Tjh@Ôš Áša†/í¨0Ã×r@‚@¥µû:Wî“H$èG€mú[+mãkZŽ;¼OŽŒ‚,€¿ÑÙ÷j­~8Ð’rx?O W t4[ØìF^›ŒÔ>Íž$`s~Yb)ÐÄ~ËJ$‰€D@" ˆØ¿ùžkgÑF„E_ ÍŒQ¯~4ÃU@–Þ‰€hzg¿ËVK$‰€D@" è±°š>¿ÛIxDl°‘ªe%‰€D@" H$‰€D sMçâ+k—H$‰€D@"“pTÖ]{ÅdÛd£zR éYý%©•H$‰€D@"¸=½/*H‘Dôr¤@ÓËÙ|‰€D@"  údå6zà·ÿB‚ÀÜfp¼ñÑ*úï×ë›í“‰@'*ªéo‹VÓ¢[© =¹p>¿–n¢â£'éV"ÁçID$³Ñç«·Ó_?^)’¿äœ²H"…€h"…¤¬G" Hz4âõ«owQu-§ç’E" Ž46yÌ*¯ª¥×®—ØND+BÍÈÌtê›Áf>qš¦"o̧lv!̲,H! £œE IYD@" ôh²5éèÉJúdÕ6ºóªóÎh 'À|ç‹uTpð¸È1qä º~îT‘Œ™³5Û EÔ¥o åá„É×^0™8)æ’õ9"ïØE3ÆÑôìa¢^N øá²Í´£¨Dœ;wÚXºǹ|fo{áA‘}xF?ºýòsDbÍ_Á_ᆠ§¡¾Ýty7¦ súm—Î 9/§¿ÙL‡ËNS2¨Ÿ3qÍ™:FÔçpº‘É}3턯'çœ6v¨È¤Þ âBùG"ü¬M@3d@_zúµDn¾ÌíñÐus§PÖþx–ÐÖœ _Ý{5 è—L§‹~³%ˆÚå)àMp8ɳ$‰€D Æàlã7Í›*f’gMMCökÖbN€™Ù?…fM-2—¿ùñjÁÄ3q$4§0£aFíÖKfÐÊ-ùôÏO×Rfz êœNsö NhÖnßK9ûŽÒ]WÍ¢ªºzú÷—ëhÒèLª‚vˆ–‡o¹ˆ4HXA…k2m<þÁ7›èªóÏ¢J˜ö|½n>&g ¦‹‰&AÀ¹õ’³i×Þþ{Ff£Ò6ÚœWLÏOý0S®Óú†þÖh藜ЬÝrC"ÐJ‚q“A'N«·Ù“[ÆóÏ¥û8f|C’ËÄ8“Ø/ÿH"…€49‹’²‰€D@" èѸÜ:kô`1£üÁ²MÛ2ïìlž‘*Lh¦&SÞþ£Íλzö$™Bèa!ä²s'Àg0Í›‘-ü JOU‰ós÷¦ #3(-%^$Lï›­L „µp²Þw¨TK—ž3¾YýS1~Þ¤Qt嬳h0„«=ÐqIJ0Ó…ÓÇP¿äxš0b˜/²ÞÞƒ€hzO_Ë–J$‰@+pxÙ–…M¿xQʈAiôüÃ7aB‰ì¤cg{^”ÂZ“U뿇hOÞt!¹ˆÀjwÀÇØ8ÃÍNû¼ÏdÐ+Õ5®Ç€Òç“y>üi¸â¼‰t Â>óLyËpvÈ~ò®+pK˜û°ðÄ¥-o(H:öóºϬ,H! šH!)ë‘H$^€¿ nƒYø`Á¢e $Ìð9Þ?…@4°¿L[Å <ä·FC[uÉc‰€D šüu‹& %-À°µ®¿Å®ÿïÀWȽ®EÀ2ËwOþí¿Ýµ”È»Å×Ð"è@L4L6B" „‰€hÈ¡;•ð|Øÿw€ÓÛÝÅ&Jñ9Î5m+ûåº]4^Ø›PY”}  ÊºÝÊä NF@^A†×¿EÙßÉdÈêc;~÷ÈM±Òœ˜oG¤ù¬'ñÑþžòÐô¤~ê)˜C§h€’òâ• bL^ñQ:]UG•È7à€²Â•4ýV¿sXlVõ'ZÌÔ'ÑBc‡  Ñ8RŽpü° ü“d†m)xágVsÝÝLŽOJ¾P­Ñ RiÔéj•:ûe‘Dxx½òÖz=Þã^ç­®î›÷ÿòÇ­ Òݰ°Óø™‰*â%1‰@Ð(|‡$æèx‡Ž—‹¨r§¯ èØ)¬7=Êy‰NoÐ=ÑÍ'6ô¯ôˆN˜„¨ˆÌ÷e3ß7 ¯äù:±{zµ@Ã/ -+·ÒÊ­"ª ë\*#ÙÔFpmÛ#‡Ò/²’ÑSAZ¯¾Bš8‹‰æN<cÈ`Њ0 þR}(uÇ𹊠Ã1Ku#FLˆŸ~ù¥÷Å%Äß®K^Þx³Ñƒ Ùj£N×ð‰a4dÓz$6§ÓSY]ï­©·©uïïûÕóev«õµý›v¾þÝw_Õ¢Q,ܰ`#‹D@"ÐÃ`>‚…î`·;iõöBZ³c/ÕÖY…MA¤ù‰hã%ººý=éñÐP=ø¾Ó‚ïûòÛ]Ï|ßÔ14wZ–v$ÏÙÞìµ €r÷¡ÿ ës5´1º4*3¥r¬]ª¦˜ÿ‘…ꯓRœe”æ8BŸ¯Ý‰ßºíâi4nÄ ™†µ6²4š–ñóÉáx ×ÜußÅi½„©ì´ìá^Ž&4~Ä@B«FNê”ÀK:ñR×Û씳ï(mÉ+NÅ·ç¹ì93Êœ<æÁw_}q n‹é[!ÔHmMçô¬U"qAÆét 뎗o£]ÁOD/Ñíxgvb…þ}õÙší´j[!Ý~Ù ð}b2»oÝ«ªîu2›°|s²@ï :M<í‰;—ª´}»¤ãYX*Ógˆ%ÑuŠFYsèÍOÖÒUçO  ‘Ó€£Ô(¦h]BPôÝ„5-̲TiÀb¾õáŸ>ŸÔç©iÉÞ[OadfºÔÆYzœ×äìqÃxQí=TJï/Ý”êñzÝõÄÓOÿkþo_FkœX¤ ZÏêÖ.¡öÍE«0FL¢þý’ºä~ò&m# ð.—VÁ ~½>ê?1¹Kø‰îä%¢¡ým÷Pt ÔWùh]{Á$‘˪—ó|ë¬^¥P^Âå›òè“•Ûé¤vm³Ìê’O c!j[Ü,AÇçkw YNdyæY¦µ—E˜á8¦q7?øãG Ìüj꘡ª'ï¼Ra¦—Â"›Kðsüó»®Ôðs­Ó_€Pó´…ø^õMŽ•>=~²’Þùrýç«õ´½ „–mÌM[µ%ŸŠJJé³ÕÛtVÀ§â«ïvÑ[Ÿ­Ç þõù·t¤¬BlVÁj€…‡ÓMk·Ñ®¢Ãôá7›1nmÇaÂHŸ¯Ù!î¹ïp™R…¸ÓòößÑÌËy>‚Çj›Ý.Æí¯×åÒ©nä'º’—ˆÆöG¾—;¯Fÿ¾b>”ùÑ^ÎóE ì^5xòC“³÷0}ºzÒ ¤<ËTò¨º×b‰ïÏt0=_|»›vï=Ô(ÔD¬—{NEü<²Ö53–Ko½ãÒÄ>ý~¦Ï{ïõ³Uz®´çô¤¤´]øyæçšŸo5¿½íG]Š‹øùïUßåvŠòXèøí?¾ x³†ìKï-Ý@ÅGN ª"ÐÌß­†Y³•8Y&3ƒ¿ýÇçd³9hìð zB̶‚ƒâÜ]{S5œÇ¹°9 Fn›2Óû`íAÒΡ4uìPQÇsÿ öøFš4z0½öÁrªªñ]·{ßaúó•”o¦ái¢.ù'²0¡3»ŠJhé¦Â¨à'ºŠ—ˆÖöG¶—;·6ÿ¾b~”ùRÆU–ðè5'?,6›“Þ]ºY˜™˜'…‡\„¯fzêÕñ´pùvª«·«²{ÙÎfd,]²ÏŒ)55#eа‘/ LM¢\už41‹ðó&«‹øùS¢„¤¤¿L™2'”ñ{ Ÿùèé¢6)ÙóA#»\7w*{Ö(AÅ¿¤§$ÒWž+¢[²¶†»ö†yÓ„ùጠ# Ðð?ýŒßƒÒRľÌô¾4ÂÍÞCeØèDE %ÀÑx+„¥°àsõœÉ”‘–¬ì’ë!Àc2Ív˜™UU×Ògßæˆq;šø‰Îä%zBû#ÔÕ]R ÷Uø>æK™?íe<_Ä1î ÏŠq’•[óE€=ÆñÝ®™iÙ“,±ï1§šz+­†©Ûå2ÍL{/)Í´3ç^yõ=ˆoØïÖKf¨¥f¦—<½´™ü|ßvé 5xÔô±³f>¤–¦= 5ˆfgb Y_IŠ3)?Åzpÿ>Û§ €˜MAg,&´+ˆ†Õ¢Ts„¬VJ´1ju“¼;‘2ýï‘™î€Z¹\îî  Ác³Õj£ïɬÎjã6ßÑR:‹—è)í–~†¥¯80󧽌ç ¢ÎéoVÁ%"Šqô‘® ROàd¦‹é[—S,f€\.Ÿ?M¨õôÀóytV´3ÌÄ¥¤§@¼Òg¦ö¦$9dø9ççÝd1?Œ‹YK)µ4!£Ø=Œ2€JË«èè‰ ²Ú”àx«„ ÏH¥“jö9AV˜í*:D“²‹óÓ ÉaS5Ö¾ì(<ØX; › úFs´‰# ßb.œ>†ÎŸ4’2R›„¦Æ åˆ" ð¬©ƒià–=GÅxüDgð=©ýíøN®Lé+ŽxÛ‹x¾NA5æeVáÀѓ”«L×Ü SP £R¦¯³>A¯¡ {‰Ä®4‚Çd¯3?aƒðÍüD$y‰žØþˆt|UÂ}Å®ü\õž¯SeÓ†˜.¾Ñ-2ö²U:癉æ"è…KiÈÀ~¤×éD(çOÀÄ‚u£†¦æs9i&癉澒´I"‰žwÎ"íOJš‡z7aá÷BzŠFäNªëŠó&Ò%3Ç‘V£A:€mÐØøŒ»¯™uÆçLÉBBå,r¹Ýâ|åvøç…}48|ÿ÷.›©>1—ž;‘4 ¦f á¿…–‡e|øý#77^#D…à|3ln¶Œg0üDvª–>º‰Ýâˆn/®òÐß¶Ûè³"G‡ˆÓiTtÎ -­9ÈÞÛ.‘ä%:Úþ¶)ìØÑîÄ´c·•ÒWŽ!°H¿ÞÀóµJΈy «IÝ8Ê«j‘0ÓØ©I3;€ÿ—p¼r¦³¢¦~4¬¡éÁø9dáZ$ÑÔâM/çíE"Ð[àç=Þl„Yµ&mæ÷!æ¿Ï±Ô·,Ì„RZ;Ÿ…™@…}­4šæ„ Ú E˜ tÜ>‚ÇdÕ\…¨u.ãäñ:˜òØÒ:zàË:*:í¦gf›É¢ëØ\Ýù™Zz|fs­ÖîI^"Üö·Fc8û»ÓpèmëZ¥¯˜Oí%<_[ptøXÌkhØ™gªàdiS79nv±.¸Ð¦6P5ÔüñdÚ¹ 1\øËÎ âB¨Ñéu}“-ûâÇPe˜éåÙ±ô¾‰QÛ2;žÓO›5¤@ó‹Ò©ÊZš5ytÀãrg0_R—WTr²%~”w#¦?M­_£§Ó€Ta,6zÓ× …à1ÙS³:,¡ð«Ü”Ò ÆE—H¢‹†ëèÓB KÖg\ª†Êj=ôúf}½Ï§½yvŽ™ pMF¢šnk ?n°ÒCÓŒ”lRÓò$Ò·%NúÍ_¸îÖ°Ž/nû[£/œýÝ…i84·u-?O̧öž¯-Â:Ó 3‚ŠªÔ¸þnÁ#„†×Ì -í>á¦:G×ñ08 §ƒC7ƒf4J;bx&ާ™BF­A  Na…Ö½QsöÓ¯LÓÆAæà)Íhúbí‘‹âþæ6ÛßY‡Ž—Óïþù…¨ž#)Å›M7;€î¸âÜf‘•üïŸW|Œþñézùñï‘N{æ'…šüâãR ñ­•ßüÜ«Uê8æw¡ùt|+×ÈÝÑ…ÀØa£‹ IMX(ã/Å<&s”3§–ª3¿uíÝh\?~­‰êœ^Òâíþë•qTm÷Ò3+ëé\h_\b¡’jåpQŠIE?žn¢“õúó+m+uÑÖã.3¶_®¨£ [ûx=­Ý^DË7åÑc·_J‰È"¾zkÈwÄÛñHÌײ¤öI@†ò2 ­ØœGf–‘™†õ»ö Tl}°l“ eÔàþô!~ó}ØÆ¿¶ÞNÿú|-íÃ5ƒû÷¥ÔdŸS,ßÃátÑçk¶#,í!ª‡Ù†mà0NOþà22›ô¨g3í@ömö#˜;m,][Ö—`»ú‹åu¢'¤˜ü27=º¤ûê銑zºd¸/L§PŸÍ*b/=ÛÖ¾pytÆ™ÌbP¤° Ãÿ„™ ®éÙ') ¯ei@ ºÎ&’­ò&‡ eó­ÈStã¼éR„Pa³û"Þ°ý- J±bÿé*ßs{ÎĤ†à°¡^+pâ“UÛi&²”føzÎh¾5ÿêGcØO?\TË4p$–w¯§)cB ºØ™‘ŸW.,q k‘­œý ˜^¥pÛÕüÉ».§;‘=¯û!´P,d­Ý¾—rö¥»®šEWÍšD‹VlEÎŽ^ŽV¾ÊCÓÃ×ü çWeQ4ï]+õðšë–¥óPÆaþÆu÷W6YéO›l S|¨a¢s(|d”¿OY›[ðŠ¥Š3×à%Âmÿ™D…¿§[1 Ÿü€5ðÛ¬`ð¹³MbZC£´¼#}'ñ Nù¾&9PW4™œ½µËæ .ÖPQ¹›Îƒíë¢#yšùë|YžSÌjºwRÇôî[˜Ü(Ý'×-¸fÎ$°aï¸a÷>!|L5¨ÅYÍ79¢ky^|g1BÊVRjŸxºèìÖµK×ﰀĚ™Ô䄯 N7]wÅ!ÌðΖBGáÁcð íÑq;Ce"*oì ”‡éÄ83Úàó5à \r÷Ÿ´”xâ…!l/,A8Üñâ¸ü#è ( I eäa‚'*‘ œÊÅÆ!¶¨AÌ5èµ”h1SŸD |ÜŠÉ NÀÉZüÖä‡VdOWúµ#üD J À;œ„ Ù=“ ôâzÍ¢£ThgÚ É|QÒnË6R:γÁô¬2?šHñ‘n LÂÝ×U˜†Kg[×Gêùjë±z¬W4‘ì¼~Pÿ?„]œœ®¥ø¥áÃâÿQÙç'ø°°ÓǬ¢ñj:ZÓ4ëIzb .9b'²f†KœÅÖÚ­I Â3‰þeÄ 4‰¬ðàqºïú Zuòçk~|ÛÅÂäìtu-€ôÞ’ ô?×Ïi¬Yíûÿàh8%,0sÂÈÆÝlº–³ïˆØ拵;l:ǙՓâ-B8sƒÖ=‡JEõ­ù¯å(o½¼È÷£‡<̈°Ð²rK!­„Ig 4¦ì Èaø9‚QG‚Ò´Öt YÉè© ­×F_}·ßÍ2ùmÆÁ Å»ínZ»^îï~ìHîû²–þož…–Üž…èÏ[­´â@àï9S¼¾77uѪ;åì“îû¢¶ûEHL£¨3º”˜h:CÒ½s¢Æ"RÉEÿ©~3ÿ¹¾É?€ûo(Â0*eHü í8´6á–ÎhK¸4EðzfÚü—V{Uéu_[ ˜áæÈ‰Êf/†XQÉqÐ/™–nÈ¡IY™íÎæöI°MÐ~øÃø]+¹282ûß”#L³RNìL)“qÏÅßíBâØc4<#¦g Ì,³‰^7·y¤7åÚÞ³n ÷4»·”'r!´ÿgñFª†6¦ œËÌcE"ç`s•t¤ùZ¯“Rœe”æ8BŸ¯ÝI«·ï¡Û.žFãF þl,ØÈጽ­lÌë­PŸ™kÞ¯¾ºõàüw,ö™ û_\Žˆg¤(Ѩ¦{ÇøŠPÛêùþôvÆïhÄ´3Úm¸wF#]gÌíø¡èèƒQR »g|a¦ €z63ëÌ)hfÜ8pî MF¤ÿÂÇoDÌø}4t}–6#z ×Ñ‘¢ÐÞQú;rOyM÷!PYSOŽžj\8cx(eDfí;|‚ŽŸ¬fhyÅ>­×Á±íßùâ;d"íË<*;]EË6æµZ}iy4-§„&e×ÞÔ çþ`KÖÂTŒC@³/N4BJÉÛ”êlvºÿ£@¯Å/yê¤Ñ™´)o?á|ÖØ°§¥9›R\K¢þ6³/Ì7›ré/­¢R›–vÆK»-3¨LŸtâÅŽ¶……%¾ßï[jÕÒ›Ÿ¬¥o6æ ´pSØÿŽÖ/¯ó!à?w†Û§……M¨²ùx“`ûÇŸþ`¯ñ?ÏÿúÎh¿ÿ½"õ»³1-ëQ°n¹_n·@sn¼ýó{ÕlJö^Žž>ßD¿»c/'@IDAT†™ÙÌTÒ»9vú=TÄÛîK¡ÿµÓF™‰MZŽJrûx=wYØÇ>¿ÖÖ«0“í8söÃ~cÏ>p]ãï`~LÏJ…È ó¿ûÑÎL4&(KÖåˆK¿Âì-¸ù¢é`û-ßJì{“žrfÒNŽ˜Æe ü]æNCN+¶ƒùÃZ\8Ÿ ûêÌ@@|ÐÅ…µ76»‹^þïR1ÑÀ3Èì#sõìIt0þýÕ:zùÝeâgAàÆ¹ÔÏÏ'˜ûËs$]3¬™áèdã”n ˜'‘GÕ4tÊ=ª´}i[Ü,S¿Úš]‚A¾:ht¥ š‚’\K$±Š€hÚéÙçfqÁF+,6®,°\ôï*¡"”l³ BÐí‹j(Þ ‚J8”9—v‘‡cß>|CÀö=pãû9¢/Ja­7ž¾KÙDÞ˜xúÙ—’IdÙi˜Ë•çŸ%Ö× a'/Ja‡}Åi_ÙÇëLøçø×錷¤!Ð>væî¡ëÉjs %Jq")Ýü·ÓWžƒ Äвf{!}ºj›ˆjg6Òƒ7](B£ZÒÙb2ÂM¹Z®%Ñ… 39Ð^~ºz‡fò,S»@¦˜ŽlX+}ñínê‡\RFf ¡Fƒï…,‰€D VM=k à£H˜ñ¯J 3þhÈß]‰€"Ìtå=ÝË_˜áã0€}l8rZBGŸª¬¡ÝÈk“‘Ú§™à¹lX¸‘E"­°0c³9éÝ¥›©N/43ÑD+kЦÔÖ Lûv>0U àègÒ§&´^â\sGªÝ´ÿ¤‹ösоc6*>b%|Y¨ƒB£@ž-‹€h‚E*ˆó*`]¦¢Ž9êQ½½’&‰@èH&tÌ䉀D@"Ð P´3½®Þ†ÐÌÙQB™.ƒ’ëËè è‰èƒ,ÌÄb€€ñÞ쬧as„×éåõ¸G¡cF¨µúÁ[µúô-µ*zÿ³¦ðñ¡tZ?D.€Ü>ýt:J€`d´©¼TMeÒE6;õ\uŽçê‹^E-ClwjãdåA# š ¡:óDöWþò{ tõ{ÕͶþe\ª–ÎÉÐÒöRm=†ŒY!–J„e¬´z(%Äëä鉀D@"Ð9ø7@Ô> åЀ´W¾¹#‘2Ô"êØIä$[yÐI¿C°gãùNT“A«¢=HÜÜ^ô΃¥4d`?Òƒ)×"‡TO3;›³Ê«-ß™“‰‘t˜×EEˆ*"–áüÛ^GI „±VÁ´Õ?–H‹¡ù ÈL:Bÿhhúˆî«A¼ œù·fäV«ªkÐß§íTVª£å§p‡¾µgTî·C§QÑ9ƒ´´ýß›Êp¤°øò¶ú!JÖ’)å)Fºg²‘ÞÞi§»'è²w«éDCξ§Î3Ó¼á:ºâÝ*–¬¥nЧ>¬¦ü“g>óü.¬ûa"Y^šý¯ªfi2øÝK‹SÑU¨»Ú8.œ~#ï¡ä3èQèâgç'3MthãOŠK*Û×.µÐø4-Õá^ó×Yéƒ<»8Æ8éú+8ÎÏÐùoU5î—?º)ЄˆõÈ Ýš°ÌkëÅ•ÃHSDbòûjò¾¿\a¡÷s" ð/–×Ó–cM/r0·,Ë}×§µ4e8^ôl=]>FOiñ½ËT œä9¡#ð6òÑXL&9ô«åÞ‹››qÞ™r$‹u©ŒAç™ù¸ÀæÇAS‘·ì‰sM"?Ùâ}ŽùÐtFŽ´=åÖv¯ç<5L'áp8œd4¸E¨éh  hY„°âñ -`.‡£‘ÃŽmËÉ„Ó4ú·BRl •ÊkVÙUcÒÌBÊ ¤ZÈ„À2(^M)æ¶Óñ¡»#RÎÏÔÒOg˜z@³ÿ4‚*`¹Š¿@sÑp=­8िn³Ñå£ôôÄ9&zbY1õý úñâZ)íC?wˆŽ8ºl<4'32t´þð™=u¾™îÿ²} ]‚AM .6Ó`$EoYŸi¤$“J9 ÕѳsÌôÝ!'…ŸÕYr^½ÌB¶–3Û-+‘ÛŠ€hÚ€·YE?9ÛDYxÁv—¹èÅ 6za®…à#8>UC· c/W{¡E Tü²þje=]‚w5fa^ßâ›…»z´.d†Éâºw”ºiWE=Íßä¡iƒôté=]4LG}Ûùóõ²H!ÀL™×Ûºù@7—û$=ŽÔÇIj«ê¬dS‰¯Üꥌ!¼Ì3tÍh=±@óËO‚‘»cÏ ½×Ió7`ÜcÄçÝ0Æ f9Úªƒ.0nºb¤^˜¼\‹I®ç1±ÆcM[Ŧ6P5ÌãX aÚ¹ ÝQ’§ÌÓ ¸ñ‡zŒqCü´,Ã1Ð K£–E!kåg{ôbR‘X`)ö:ì‡Ýõ•Ç¥E§kòÖÚ/Ÿ1ü‰IYò~vý¥íUÓiÇ'§kéYä²K6©iùéÛ'ýfM=}9ëî˜`Ä쾊6qÑ_·[iÆ|Ö",ƒvaþúzh LbÖÉ~=·¦ãš½Nk\/í· áøÿ®ñ<<ÔØ~úÓf_{~³ºŽþum<½<?ÏÅÏ4 ;Á”+! ­žœ*ã*ün)мŸã ;&h6Ÿö´c ¢ZÌ3<üu}~kB³Û_4Ì@ ðn²Æ†—ëF?ö6ÕWCoAÓÄï0k›dé¤@Óî×ã-«õÒ3«jèõËÏ?MC‹ íÄædO­¨/?À«Šô4¶WâCÅÚžñ9‡C.-eÚ€Ða6ëbÓçäûæ£.±<‡Â´ZNz1JáF@-ÿ„€€Ãé¦÷—l$Î÷ráÙÙ”™žB5`Ò¾þn7M7Œ–mÈ /û%ÇÓÊÍùt¼¼’Ο4š¶ä ;¯:/„;ÉS%±››)&gäzrû) ‚m!›€ ÎúÓž0c†ì-¾ÌN3b–—è¥ õd‚°sÎ ã÷Å-Ç8ÂÎíã´ºÄAŸ:hEûê7iÉé°‰@¬]RÚÑfgJİbW:š0!eÈ“_üD—зZoàUkÂRGCж¿´âÿ;˜Ð²@¼9 §o1N)ÆØ»ÁÜŠµ*UqîOÆ—5\ÆZ¨ÌÜ([l§«§?Ù^í8­sËX\l<¼Ã/Á+T@£Àå8ö¿´±žŽÕxé1˜9=4•5 °mCa¦ŸM¯^ÓŸs§¦š„F`Ùþà}QI”üYºÏI?šfšŒêY[SëðÒºC¾‡ax›ÏŠôø,Þ65 ¦$Õ4k°Žúª–,à™ž¿ÐLÏ®Q‘ÝÕÔåù§\ôÕ^ýX®?ÄîMÇZÞƒù¶Ÿ.­&‡þÇXscÇð”ÂA%ú£¸|Ø`zvÌèdé>Bç´»ÖÝ™?Ú¼è`7Üô% ®ªéðéÉ ¬‘㌇\©å[¨8ÙíTi˜i9 !¦Ö\xÖÀæ÷r)×´µÖz]4¬ŽþçìÚUm¢u't´ó$òx4\į£"Ü<¿V 7ma)F`kþAºaÞTªªµÒ ÿü’^úÙmdà îÚíET âéjú")ß­BrLÍ? ‘ ½¢}µ}à;ʽž€brÆBA(å0]#0Ù5¥¿FheÞÚá³½¿6KfËAoíò 4C!ìÜ€},Ðpa&jÁz«8G¹Ÿf€CñÍT1h¸ -lQs¼aÁÌ‰ÐÆëó6¯±¯)bØH)R‘V÷r¾—@«Èüµ,,¸EMŸgñp½ñÀâlrXèh:xÂGøÖðÃ? Á8laßÒ:/àßo«´©ð¿Ø!÷¸¬QÊb ó}¦‰çÃBãì: E a^BæÅŸ~¥îPÖþ×£ŸBnÿ>˜œCø¾hx4ó†ùÌÍüýȾ‡5—¬aaA/˜r)#öaë=!ú…MÐüÍ9y?kº–|?‰î:Ë@¬Q µÄ7䇿{)¥}™hôë,å@×ÜWúÿ6Æ[–ЈiÆÿ¡° c¹ÑœÔ¯À¸Îü&HýÜè³UæGì:˜ð ã_ügxÿ`§2-^®IP5á¼é_ŸÁcCŽ%Â^óººszY½ZvÀEKñÒóQ±à×K 7þèÉßÁ 023Î;‹­Û¹W8 g¤%‘ÌÎus§PÖþd³;!Üœ _Ý{5 è—L§‹~³%˜êå9˜C@ÑlY&D†ù“R0iO­´ÓºÃ.1ƒÌÚš$0DþÎýHàxÓX=˜Ð&†f ÌiÂ-Šó©0ÙJ;üÇGÿú- çeá°ÆGªÜÂ‖cاŒ=þ×õ»…–˜ì‚Ë™Z–3ªÛsÆž®Û'„§Ã-?Ÿp˜|5À€Òœ¸Û5ø­hØ÷D)E§]ÂJDÙf­ðþçjÿñ–¿ß‘ö³q5–B˜Ÿ _²¿oošc^‰ý‹6À쎵’çeê„6ª%--·¯B},\¾s]¼8äÖÐxú 4|€-mÞÜf…–Ë(4A-ëio»Êîd˜TŠÔâM›²/œµÁk'³1±±Š@¸7”?"Ђ5xNLìL0Hëµa6Ž’Á”EP> 5å‡7Æ éÿ׫ë19èA¼ uãŸøTÃ-ëÊÅ dÇäÍÚ»’èfe^„Ýe°…éÓ’,zK³KØŸ‡ímy9]ï%¶§ E¸iV™Üèõ SN@Âd€úQ|”2æg\êlvaooö©Ñãdjl#¹î(Â@Óð/úÞ¢júÅy&zf6L1ßu‘Ãï=hI/[ÏL`Ħjk-'¡ýÏ ô›ÍãXûÈ•ÂA%V¾?”ë­E_?M0§:,÷‰@¯hXÒœžLòQгŒÊôAÁÃjâG—Ô ^,åݺèßUBKcƒÐ2æõŠÆº¾÷q“€Ãv°ìðê‹Ãôñ÷,=ÙÒ¨&n¼AÃŽ7éÚ1¤‰×%Ç=7¹6]iË:åvïB`ß¡Rªª©‡ c§c'+iìÐþ˜ymH”Ä8ê—œ@[ "1_íÚs¸w$[+€ ‘*k%‰ƒ¼¬: l,ÿ^y°íèglºÃþœœƒ/â²ífø~½¢–ÊIM'\ÐøhḌ„“)¾–£ƒ1C—Á‘Â8Ì1¢†ùG {éßëi×Á½'¾}óŸã<Þ”ci»aAsªª6]ð)Nä%9ùeécÑQ1¢Ì…ÂO€¶å¶l#¥?6Cïï›í/ä'‡‡¾¦†\püõõéÜ¡zZ³³¾0I›è£<Ÿ)¢8±?ÁðíTÑì°Òv^w¤ý{a©ÂÏî÷Æ…ÿbnÆa•‚ óëꉵƒ/Að_r{‚Ц¼º©i2x‚3Á"KáÙ0ãägÿ™Uõ‚óFßK™¿¥,ñ=^½4®]åHpk6ÿ»+à{'Âf&–#Q”¾b>•ñ•¥cļ@£¼„©ð0 >}šóHÐi ¡„…™öJ ëÚ»†écŸ”“ø°)ô·v]°ÂÍq|$'§¸û÷¼ŽÚmîºêO\û?8ô—‡JZ«[î]Fä쟟KGOœ¦+ΟH)Iñt²¢úŒßpáZ´r+}±f'’vÆq¹C" è8¥lþEzóª8af¶Îÿ­kÒ–ªù=¤xf–‰¶þO=¨š‹º½R‰Ùåõc`ögkK”Q´,™BPÐá…_~u3s¸öî©ãæ—Zv?‘Α̈d½˜“tÙ«{ ]åW£ŒÃ,lpÈ뀤³K(üÄJD©»i¬‹V݉(gèç!’›‹-ÕèŸWÀ¼ÿ?DBó/;a¾þ÷«ã„ðÊæX+Ú‰hçm¨¼„ÿµ-G¢ý\'[–<„àKüB–?už…VºëOÕ{DŒÏ0Ò—†¡”MV),±È· xøkqØze#üi8ÚYK†¯ýZ¢À‘ý¢C-DðÍ̹mê!H½ð]}cn›Pëjy>÷ó§Ì§*X·þ>7˜ùQ«ô–iZ^’FKQ#0ÿÈú lj–š ²9+tw$  7·3ˆ…g-ž|'"¦ÅÕÕêûñ&64Ü«†ê~„œ9ÎzÕŸ¤p-OC×Ñ¡3mÝQÈÛ:G“ôX¨é¨`Ó>þ>3m§cfŠsÒ[8úæ3g!§Ú 8= ,&1.{}gž—õZAÊøÔ¬Ê…7«úÖd-ȽA›ÏVf à/.zbüC á›=¿S{µ·<>6Çžr×{Ãþ_t­þ|„Z­ùètÄæb´L˜Žé”éÿnh÷èÿÛ~Ýøwg)·Æø‚žÎ½Ù7ù&¼³^Üý „“<¬@ûr7Ÿ3¯ÂþÚ¨ý~ƒ0ÃÑÚŠq(§à‰±¹|ŽÞ«{«køw¤‹2þ*|Íz˜éõZʰxĸÍãw¤ ‡.®´½å}#ÍKtWû[¶+·™e~”ùRæO™OÏ‹XÓ¦hhx2 'ÑŠLQãã­…PÀ "; ê—H#aÒÕ×y”²ë¶KÈÝYøþLG_ç1a«œ–d-¥^ÐÊ43í܆H•Ú}*JþxË{~1åFGúa|°VKá&RÇn=©}âéòó&ÆneË$ÎE@¥NpxU ”›¸lÞi˜±Ûü¬JåsöO±Ä›Ïј43¡~fÒ«{û>>n)Ø‚:›³ŸrÆ,hhÔh@”уGÂX6`γÏò`ÙÀG4®Îêøª%ᣠ”lÖR?ƒKŒß‘ä'ØmîÛUH¦z¦Éak­èL^¢«ÛßZceS_ü(ó¥ÌŸvÏ×ÀO+ïD¬@°Ñ$Ð4 1n¯»Þæ "tKÀ&5ß©¨JùA1CÆd¤Q’hH²^|„&×®>5ͯêš-¶œ\³VБnöÒ d™@ÓÈ´2Í,­wÄä¬e 4Ñër¹šB†à„ƒÿzðäžÇ'¾Qôøø Œ^ó•ZõnZ"'·%‰€D ô¯ÿô¤£œýÇÜs¹>•Ç;þ/ßñoh]®C>–…ÖZÛ0·Í‹ãªcV»íá†cÅ. ãß\0åYìõxù¶ˆØ\ ®6¥¥†kÎaêr»¬áòø1VŒ”jVSŠÎÑ­üDgóÑÞ~å9è kÿ¾b>”ùÑÎàù æûHWÓhÀ¦‘Ïî X…JcTúÐxœî•U(ŽïV¨ jy>«KY(ÐétBX°XÌdG~é ¤WW"Œ`-U»Ž*´i"úY¹.-è<5-ïÌ6Çç}iŽ#”ì*‰´˜]”–d& ìšã@ÓÈK¦YQ?Sw[çTV×yv{SŒé'ï~bÄ ìú /æïKµ«­7`~ë&XðÎÂ:°ÏÍüœo½*Õ‡2 @ 0å¦D@" œÀêgŸõf-¸án—÷·88’É ''"Ñ;BÇÿŒ¼î­ù‹Õ^Õ+^½fÃ0^ e¸8´*/âU5 8â¢ý^­š…žfœ6{E¸üDk|„Ýn''Â`÷s×!/‚®Ëø‰®ä%¢±ýʳÐÖ-ûJ «›a)0…¿tgñ|Œ ó}n—«ÙûÐðê(Ñ(Ð0ã}¬ÆjWÕCð0î0œâ{Õ¤‡p`D]q Ùíd=wÒ—›,Ú:Ri'µ£Œ’ëÁ "”“ dGlK·*rðh¼.‘YX‡¤™¬g+²$­ƒú!™”ÅbtÅÇ#6þKøÇ<™=?o®›<- )›¹jR¿ï%Ϲ…OL`Ó³fE­Ò|åöz~ÿ›oœ-%Éx°ôTíiÿ“¼:ÝM^«†Qã¼<î…ËO´ÅG¸ÀG`¤XÉd·R…-Ád%OXv?ѼD4µß¿Ï£ýw ¾J1¸)#É@‰ ñÔY<ã¢ð}ÌOc“߇˜/‘ãØ#•ø•9ô]BJÊ£9ûŽÒÙãµÌ¾«KÙÙŠí^Yûá„0ãv+!_HçTdJ¯±¹©<¬ÃƒãÐÒ)OTáaŽÅσ­Uy(Nç%“N%]Öİ•˜O xÀX¨L+Ó s3Æ‘C_Þ[´$ð—WàܰƪõË G+r:]´rK­ØRH5uHZ†¾r©ŒdcVQLµOPG4bžSÈÜl£ÏÖì x7󦡹SÇ@ m¯`PM ú$ÆØ† „#eTZ^EåUµØFî0`Ï §¾JÄá7t”g¢> ðsCnœAé)g6ˆÀAßTž(D5˜+ü9Æà?ÈÒm÷Ù"ÍñÙo×Uç>™5?çYH7[qì¯Çk*zbÂOóË^1æÅÜä}NåvþwõÝc8±æ,¥è'YEØPcA¦Æ1ÎÃãÞÀ¡Ão —Ÿhð™eÙH‹‰°>îz²B[cõhÈåuËS‹18¼‰InhwòÑÐ~Æ š‹.ÙåV_Hn¦³ÎPäKMC.§8Q¼Që¶Äé\žï­ð}ÌOcSáùøPÌ–hâ¦À½«¿\”3bÂYå[òŠû@  û+Ð8»!³¡³0£)¾—&iZŽZb£0Yð53.<ë Y$ ÓÀ÷R> ÌÈaËff,¥³0“”˜(~³Ã‘T"¡aÚ£×ãvWçlÙ°›—F¼ñ;¨+ ÷)÷-kcþ³x#UCSSÃ2óêL“ÃFµ³ý}²r­Ü\@w\1Shmøùˆ…ÂØòRáeSÎ~Ê+>FGNTø½G*ò¨uBXt!l: >é1ðk=X¡ðãÙTã¡ÙÚŸ&ŒDFŠˆßT»ü%è:f"¡ßîîÂðvu]w§ÂŸNص ç µ‡ ”»n»_åœ0¿ð»Úq?æý®V©Ôz2ýW9^ðø¸?(¿ÛXûkb¬ãqoú…—TaL‡Ÿ–°Ùmb¢L>" m$ùnwwñÑÒþ6ú>*õÑyè“fe×UjšÐß@׎…;x»®àùæûPN3?Mÿ÷‚Çd‰&†fíø:~ì½\•êG{•ÒÈÌð#+‚‹Á«'¯Å'¤ð³Æ>*lÚe€¾Za ¡Æíñip<n…™´Ž…Qå(!LƒFÍþ<Ðá~>í r˜™±fF¨µ3‘ Àøåî;¢:´o/"ÅæŒÄ"0ÆoÆ»C¥§ 7<¸¸ÝZ±9Ú’T§‰§=qçvI¢U— y ôbaÇÀQˆ?ÿú‡+éú¹SèÂéc{4£Îïc{²¼š>^µ ³CGð!'ªÖ¤P…aÕh’©^G6•™àwÕæ3‡tu¤÷ØÉäF3„´L´¦ŠÜZ¿{%A¸¹zÖDš1aÄx)兀DM *wÎa>kgñÎ%@ó£¼m J:Nžù§ËâDrÅbžÊíå¦g¿i Áî'²`_¸¹dùÔ%Æ<ÿóæpù‰îâ#'åÛÕ]¼ÓÐíçû÷„§¢ÿA¨‹ œt |#ø¾¼jxXí7ÐC3)%ÙÒ©<c¤ð}ÌGc3l~¯'àÎ4F“@Ã/ ¾kíg‹Þ»þþ‡o{oÉÆä_Ü}•Z¯ ? &¿ŒŠ‰|Û¾pÎø¬Ø ÐØØÉ¯AKÃŒo$fW”{±Æ…ÍÈ|Ú_Ä56/cõ£Ùl ›šñq¦-ܦ<ÀÏãq¹ª×}ýù2ÔÇ1 yá<ã¬`N7}èÕ´–Ø çµY:,ܨU U®ÊOŠ~~^u›7ˆÀA…áf{gf>_»‹NéRyR·$X­Òö¥mHîÊqè?^±šCÌвE¿+WšÝéU0®¼ð»²§ä8½±h 9Ü*:dÌ¢c†Áä€ù^¨Å}MmK….• A6òPç lÛGï|µžö=I·^|6r]Tpà88v’Š–ÓQhƒìŽÀ¡N9z ÌØú÷M Q™i4zðšÔÈ(„J§<_",&˜›¼y%̉‘ñý­kâÈ –{Þ;U”ª¥_ž‡„—}5TRé¡Evúoü,Qžc¦2ä"𬦠†è©ø´‡ž\^K%Už‡ –Üž|ž2¦ > c™ÃG\Œñ0!\~¢»øî”îâ%üˆîl¿?Ñü›ûé‘Yjú¸ÐI!ŽŸFK%v½¼ÛHÏ\”D™}â:…çcL¾áÍ*˜Æ®3ø½hÆ.Ú¢I ávð—šÁwœ>}¢¦hç¶?¨&O{á/¾óÞ{ýì¶§uùêvŠò1`AüÆCÇ¿9þ·v,̈€`ˆ„£_ƒŸ2óª’F™ˆVî‹"4œÇ—g†C3³–†° ãK¦éÓÌð5áÆíØÉJÚ¾nõ;õõ5µ¨GJ^¡¦qdÌ=”ûL¾b‡Ípî’pãñÎöª_õ‡]ëœUeK*Vüy}åF6ŽlQ„Tsö¦/¾Ý-„™<ËÔÈÞ(ÄÚØÔŠiÈFPÅOWí ´äß`R‰þ‘œ3NgÜø=8\VN' y9QQCÕuä‚ð‚©äÁš}^âM¼?zZ²!Ÿª=fÚ7A5LgÔÎ/LãËuébbƒ•ÊÎ= ©Š?-LDY­_£I¤õ@²á]v©ôäÄ¢j|V4ˆ.hòÖSiu-‚€”Ó΢C‚œh|Î?ŒÎ™8‚úÿhÀ=œäµÑ‰€ BþW{4É-_Ú`¥£5¾Oï©z7-Ûï ç׺h.„–_œg¦/ö8©Úî¡“ŠnÉ6Ñ?wÚè¹5õôè “X~¶Ô? kt¶·›©jä#@‡óxüãqP={ÞÂå'”ñ¼«øÆRa”{w5/áߟ ]Ù~ÿûGóï–ýôH :€‰å|<’p¦9‰€S¿Ú £?^n¦ÉýLçù…ïcþ™ùhì:ƒß‹f á-žYá°}»øóïâ““ÿ‰ß÷Ò"òþàªóTájjøeTB!óLƒÒ™…fxpècö³áYgÌ?‹hE¨ l¾ŸX0Ç̾ki8òXˆa“_ˆfŸM¨÷hy>KèüPo-8 ÚŸ¿û“ßÁC°øÖŒ/ãìUñÓZNØ-§á§(Ù/åõ¿Ñ•ù?Ëþ÷ÄËÌÕ‰,o²~OÑ=Yü‚U‚nÐùº¤ôóSoxΓréÏr¹$ Uzlƒ"Ö²qßÖÖYéÃo¶ 33ÖÌDKaZ̵5ôî’MôLF„\öŸ _#Ù‘ö1Vì´û[áTX“EÆŽ æ™È­å` „ƒ+4Žˆ ¨ƒyAÃĦdy0ß‹´0Ó²‡ £i £„ VR™n0¤jm2è ^«iðX…è$õ³¥êõ¹´x}M„Î5³'Sÿ~RkÓs¹íC@ùžë01wï aqÂt¹à”ïüøÐWðç— ñÒ;»ìdA˜U%zäl#ͤ¡¥û|ŸæÐÐÌ_ÇŸn¢ä;¹wRèO-"lê0ö(´ó:Æ ƒ×ÈGà·ûxLî—Ê3fׇËO0†]ÉGp5ö_ò|ß@¥;ÚˆŽhÜײŸî†P“=ÔK/lp’Ý«%ðýè ½|¹†f ‰Œ5ãàÏ÷=°ïÌ?cw@~/q‹MÑ&Ð(&g/—?Bu_¿ûö{—ÞzÓùƒãåÞ[/™¡Ž”O ?xl* øÑAFøX°0ã À —p¾ž…'Gø#(ló}}Û>A†é ·°í$›™±ffÞîÏV~ºp êä)=ÖÐðšqe|ùƒÏx7U©á`„ò¬×«~Aî»8ºqìK9—w}Zò²ß¸—sîÍýÉøÝÊU-×c_ÊËÎûéØ|´Å¯n¢`…%išDïBlÚóI-]2BG— ×Q_ æ)ܬe`3¤Õ[ ¨¦Þ Ÿ™ÉÝbfÖý¬©Ùc¡f­ÜšO—Ì//~fº¢ð³ÍKU­•VmΧ5;ö ¼ì •kÑiK*Õ«ãɦ1µ*4è¼<ÊRqxaÖƒi¯Ú˜ ó TiÚõÇi­>ºJõ™bÑ{­Ôß^BÞ}ûi÷¾Ï鼉£„_kŸ"ñN¶FƒÜß³ð,F="V…Ý€9CttÿT# IÔÐ!˜œqÑû½÷ûÞ_Tî¦>f DÄ$EÃÃûÛ+Œúæï¥[Ú»¾?ƒ@X4¬üã¡oæãêHð¾q½óùî¥ÏºŠ—à{¶Wº²ýíÑ-ÇõÓå0)1ÐK~c£ › ‚†Yb¥ß_¤¡+F ¶+,òýù¾Ãû÷¼½äý³©Yü^X·‹Ú‹£U  ÆL¸óÁ´ .:î™~΃ þ½$i܈ ï´ìaªñ#†•§†<(ø¥dS0Ö˜pf‚&Od”u¨=©<ܼV¾ŸâØ§ì µ^ÿó9Þ8‡èã¨ÀãrÖl]»ê½]¾å0ÍŒ!kUxÍ‹¿ÄÞ(t¨T„ÎT‹QÂÌsøè×µq/[ݵ{1ñ}YÑÏÆoðêÞ ‡Ã¶uÊ_½ƒ8"͘WòGz®'”¼vÒœN÷ª VÓܧÕéKá&ûÙÍév­ëVµF#éô3p_¡š`¶ƒ¦Ëo×L5 Ø„(ÜpŸ±–Ía†>¬Ï=(¢™±ÿJ´¦‰#­±0qÁ”Ñ °ï™éLZùYgŒv•Ð{K7 Gúºþt$~$L¸’‚¾uW2þĸ¡ÂDeDŠCe¢áó3Œ[‹èÛET û¯ŸMû%‹¾ˆÈd%1ƒ@B‰svŽ\ÈÁ>:Røñ}ê|­;ä¢ï\C}“ìÛ»›U54¹IS;$IMµ/‡Ö&ØÂôiaueÑ[‚½$ÎSšf|Æœ£–'ùÊËJK§Îºà6ð‰áò<†w6ÁÂ÷QÖ ßi^BÜ Ä?]ÕþÉê¶Ó[ë§ÉƒTôîMúŸÏké(4³.<¥/CÎ;„yþþ„Ð5¯gò}®êÝ›×ÿe˪oÀ1µÍïu8|ãhh¸¹üµff˜§|YÂäÑBƒNú.ó¦Âs.»â*„ ¾Œ{"/ì÷½I‰•Q}„JKá¥åv¨·Qp庖ÛÊþP×6§ÓË9ihT¹Ý®šƒ…ùk7,ýz•ÕZ§1<È ;Þ3žŒ+ãÛlTÄ»U‡˜ÿº1/î¾2Ý•Z³ú\›­æû`©ë0þæè»—9Ö…0;Jª¯/蛵 ï¯Óý{Ôóùø?ïNÎyhB”Z×#¡Èç«/Pµ*Ìàüf%ïÙéœÅöïX>LštÕȸiW^Ÿ:xä=uúÄ¢óuiG…ŸvÆ%´ ŇËD¢©2sF³ûGÓF™.C$w=‡÷‘ƒû ¡—ÊÎ(üL+Ú«–o¿—j!À$œ+´1qÏh¯“}nö™ÇÓ)WÊ®ÜNx{ =zÛ…4l`ªj¢½óº>þ~NO¦ ù‡(ÅY&¢s{vúç ™ÓàGSnõ"x†~2j:Qç!“VE÷0'맦Çhg©‹®Ï2Ðæ£H)ÀÄ Ó‡I"JGd%¦;RcO·ïÎÓò H¨½1Ù·}ÏÎí{g^rù7gŸH6å'Zò -·;NËþk¹Ý‘:;ëš–ím¹ÝY÷†z[ö‹ÿölŒ3‹5£©Âm¤>ÿ­•¾Þ¼¦ŽEzK¾SrÚWôÍúÅ_}QWWÍÉÓÛå÷‚ºQ<)þ\+ö¯üñaŽNp¶è,Ï7½÷!¶¿Ê:kʨ̑£Ç™Ìq}õ&c¢V«3@­1¡÷ˆúâr»ìv«µÊZ[{ºdOaážœA4›“±†…jøá®løÍæfŠÿL³aÐqr³ 0nÞj³æçŽÂ¾5ƒÕ¿tö\Œ À‡ ~:öø„ù¥ ª“ tä:ëéí9«¼×—n˽Ö@/°ÉÚ³PùàÚJåŽ/NcùrΣO^4:kì˜aSΣ%ûÐÒ4 âLt0šþx í 7Ú Á**)O癉Ö"hÀŽÑà}gýü?†‘ Ý‡[øÆ°0³n÷~:bNÅÆì›pE‚®h©£Ú²-q³iRÝw"¬öã·_*üjx6T–Þ¿‹¼¤&ʼnD°iÎ#A 4•6/½—c£§¡•ùõl3ÍüG%ýc»î™l¤avö^®ƒrÊšÏm;î¢ÛÇè¹ Ìt‚ÏókùÓ|aúØß'%!Ëù_ïjZå#€žv0éç„¶æ+l/5~ÒÁ£²²Lqq} &S¢V£mn£<äòL‰@»dj ÕŽ´‹Ó1q+T3Ûí©¤ÜZ=¤|Ý)ð±­_¡ÛårÚV[•µ¾öÔ¡½E¹…;·íÁ!ñ{­ß g‰F†õŸ]Q„ÿ”¸ K!ά™ÛP8åìŠÙ¢<õŒ/,²0Ã\3Ö̰`ÓªvÇÀì«ÁåûE¯J}ÕîÇE.òª½yx¹nì×o¼gõͪ/qÖ—`†UªÇYÅsò °¶è‰ñ¯bÿ«Z˜›áÜóàþó÷ä¼”õbN±Þkº·Á¼ŒïR1ªtë8ƒXN×{iY±#$á¦>ìå„@Ãæf5õ0 1¶kÂaT?º)^ÐÊ3¨‡"õoÛmôYË+Á&ÓcÓ¦±¼ªVÐÍù‘Ø2’"Ìpð‹Õx}X˜9l aflÇ£W9`}¹#îš^³Á6BSsú)ÔÄh·ß,E€L€É5ÍH&{qqN©`ÍXy6vÁF«àYl]þ¼ÕJßaÛ8 e©‚tû¢Š7¨¨Æ~æñ–çûo3]ÉÐÐ IµGÅò%jö™.Eò›âÏ(ûÝ.z9£“ùXŠð»·ñQÖe½‡]Âßtü×½ú¾™ÙÜêò¸ ¥µ´·äµï¿í±Ã­yQ^þÈð{Í뎙­hh`î8fйpg*Ṵ̂ó4ˆXºe†ÛÁšþõaÍEÁ……žÚS´3,аÃB k^xaÜøxË»àpúXöçsÞ:ÐwõÝC§ûO÷´.וnËY‘5÷B ‚EX¾áóQ ´1ê…ü› ››AÔÙ?îRö¯Éz1÷N;Yß¡+Ä aüaGØP…›)ijš“顳ûÚÉ‹pÜ5õÈ1‚ÃúcZfâÆl==ƒÙÔåÅNªs*ß”àj2=é*DccÃhð%w­3µìDy%}½>OøíHa&p²oÍ>zš#;(ÿÊ1¨7Íp¥—ïåwÑÈECc÷£üçi$änGN)îL±*£ZÃÉGD7–š &Fº#õ=iæ(9®ð LŽ2^rH>"J:¨·’á„uØáWnù}æ#ï> Kz.ã K1eðOº½|ËknT²Í‹òü†Íï5¯6v¶¢Y Q:?>þ¿ùCÄ : 3¬Ž~X·hbY°a<¸(¸(8«”5 5,œð6ïg‡_åZül^ü…>Òà sUÖüœ9Ðt^ˆXXBMó2méÖœ¹ÞDÍÝJ Ð’Þ¬Vy_`a†÷±vFgó–U* °^¸±Óæƒ6òØjh”©††ºŠ j°å`•›òOº©è´‹.‘D!ÒÚ§…']pÆ¥jDÒ»×7Ûèk˜ÅqáDx¸&#QM7Ãîý'‹ké¡é¦3’éµE;»;v_øpH‰¤"R…%âÛÒyä#µ'~B¤ªÉzÊô4Úº‹ £ÑC:ׯ)&Œ¡F) 8B¥ ùÃ&/ÍÞ“"An¤óZUàëÍ e;RÆÔo'‹§šF¥qž3]C¾ _TM¥©·^£Œ‘’èë$Ch¡Ã¼á7?ýèA}ê°ë¸½º”̳‡<þ‰­dþu/¸ìB¨Q˜åYŽ¿‹ØF³@Ãxsrçùw$˜˜IgfÙ_˜Q|mX‰eaÍ…1QÆÈÿ!gŒ˜Ëæ5/|¬c##.,|büj¬xåxqR¾©ì…?]«º‡ÈÝÌ›>eü²‚†s¥`ÌØŠ6;eÕªpsÔ)?z1Ké… “Wj%‹ÉMˆBr×Ï7ëÊÚ-ôE¶ïj˜~<³²žÎÍÔÒ‚K,TRí¡¼.‘ïÇ`NÖ{èÏ[¬´·Â0™^{D 8Cc.$Bxa&$œâÓÎp>$í«©£ÜâRäpAJµÏ11œºcùZŽðÊ!ž9©(k¶˜‘U«ÃïXÆ,–Û&ÂüC á¢e–ŒHTÓu¨ñÜè‘„3ΘÌœ¤wë|zSÓýlã“3ŒMF5 ILPÅ1šŒ¹{M‚ö© ö¾ÁœÇ>3lfÆš™t³—%›¤^@+ÓÌÚ¥^fræä#üÑ¿£ ý¿»ä—#~ó­]mNü&LcN¾lÈSK¬G^ºâÇÖòr…Pža…¿ã5óÄʶrœ«èu¥§4Ü1 ÓÎÆÈšŹ eÁÏ^£¡á¶2.ÊÂØ(‹²Ï‰hyÖÁ쑬? IO2–ž(¯?HjÏå&Ù¯äeºîÌ[óݳ ÐÕã8Ý8FO— ²PñQ;-Þ]M%%>)8XZxsã'½'Þ\h_¸¤Y|òraã·ÉN¹iB«*…£uÄ×F¹ž×ÐɈm,ƒEJCæk¬aàˆo§*8N"Gh|ÁĆüöbR!ÒÏij¹^¤Ì€7“;£Ö”²P CÀ,3ÙñNHO@BÌJ*®¨¥³ ·B›&¢ŸqäÂŽæ©  Î3á™ÓG(ÙUF|·˜]Ð̘A›‰â@ÓÈ´2ÍL{¸ÚÞ`èŠâsx\dæOòQÜI½•´}ÿïüŸ|a«[¥Õ?À¨ôæë=ù½â;î;™—§ðtÊZáõx­ìë­Ð‰v÷$Fé(¥ã¸¹ø 2ʶ8Ð þ0JQpQ¶;}]øÓ1 }wYì3LdOÇö‡ ‚ÿ¡.û­ ð´š“©¡eåj*a8ÈòÊ&«ð¡ñ?ý"žq ™¶¦ßk5UÌ‘‹Â.èIj”6„[×#440¿³ÛTke‹MŒêªžøú‡‹Fð×';OÐP{¾0ÛIïcQóGÆS–Þ‰€O QG 4‡&Îbœ ðZ´5t¤ÒNjG™È)Å£“®žvµ1¢ï›Æë"ƒÇ»k¼Ëxy&Ië ~Èkc±]ññqd‰³ˆßL+Óì‹tÆCf¯/Êx)ùˆ^ÿ(D{9õG£çï¤ê!A™Z{[ò]ï:ç .úá›oVåù.â»™šXàhdÇvóCÔòöEg„}¼tkQ‚H1 ån‘ âžÉzq½‡f ÑQ*´3k6 4-Ü2™^Ý—\¯åyþÛâÓ)º¹_k—01 ‰ƒ¡¬5 þ·–¿ü¿(𙡶|Š3¨éœQibV»—ÏlËg£6Ùb_*ƒ^/´ì—Æï—¢½c¡!¡âklnªÅ§ÁáÁqO˜Bå*•‡}îtðéC]ZD^‰ÓyÉ„¼ÒlRÆš²ã)! 5ÐÐ0­>ÿ¯&mrCsäʇ€ä#ä“5>>áGY rañ욌J?Ø}hŒ Û÷bâgU–Ä‚@ Yr—D òØ]^ºïËZú¿yZr{")y$Vh]  ”L/Ԭᶄ^|Z7%ár™¦¯ó8ÕE@ 9gŽ®¥§§V„îĨm¿»ÐB•üþðêk* Züý:矕!çähª¥í_z¯&Ô®'‹»šâõ*š243ñzÒ¨}¾ÌÌJÁ¦m cýh£–B‚ï 3Šâv`’¦åÈb6J€™';ýò»IíÓÀ÷R„+N ?63kfX˜IJL¿YÈуV©‰õ'S¶/V`¡å¡Ñ rÔ˜ ¹Û…íBÈÁ'Ç{•¶F²R ‰$𲮍@/|‡éàhec^¯hõúBøÌ\ó~µÅ\ï`ð¦òÈâÀ }ËdzMW÷+œö(wà:¦Êæ'Ù„$žÖtÌ0”œ*Øñr ÂM+Ûꔚ0¦70Éû Ïgò¦ìo¹Æ7¢|œiÃ{8Ò\gµÇ…{ûìkп;×’Þ’@úû2)‹D@\ ^=y-¾¯kNØG…M» pÂ焾väÀbß57“øÞ?ß¹}§Yá‘ʘ´Y˜áûù´3ð™™kf„ÉY£v¦W¬D Ç!Ð Ô<Р©¹—€ïÆ½Øæ X÷¸uÁR éå-ºfš–Èß¿Ìn(¥e2½ö®eÚWpЗ%5¤Ö9Û>Z¹î¼ÁZºo2§bj*Ëö;é¿9œu¼¹0à35=ÝM7 C°m0D¼ ïk¤Ó‡j(«n;åYÎFRÀæ×4ÕÚþ¯ÁIjš †y1ÑŒ -IáÐÆD“î“;±‹#ÄÝs–Q$+™¡£'—×Ñ€xýp’‰b½³ÌÚítºÞ‡«fq¿že†É—ŠÞÞi;߉©J‹SÑ-ÙFkPÑ¢q@nŽÀe2îwatÚhW)ûWlˆí½)þ"2¹ë(ÅUJ¶bZ¶í*·Òí—ÎhÄ/¸ÚäY±Œ ,LpáwÊ·í çl€ÏŠ ­A a-Û-r/Ö¸°™O;ã‹¸Ææelrf6›Ä¦f|œi“E" èYà]+à½BŒë»™z¬BnÀ#H§ñBÏjMçS+šÎÇXÞA"н41<{¬Õø $ œ„ÕµÔ‘‘&Ö~Kù–i"ßJGeS°s ¤ nÂZkè‰óLôæ6›ÐÚüv®™Ü+êißiUCÜrÌIKö9É—G›KEÛ°½aö›i‚c³Š^Ùè33»|¤Žþ°ÎJýÌjZxSÍøGe3ÒØ úÓ[éµ-6Ú aˆó]þnµœn@RÓ'—×"xƒ¦iÍ. zà Áæˆf8…küi¨$Þúj=ýè¦ … ÌL¥,½E°`AüÆ3Á¿9ç‹Ùd áp8¾y ~6`Hp « AT9å¾j~ŸµœG/îÉ¡™YKÃXáý|œ…ù¼ ³ ÍÌ{ rõøv|Ÿ‰ó¨è·£_Ü}¨èñ ÿ*b»™)ÐtsÈÛKZC`6‚\5>N0),Œ´W.®#^Z6uq8œTW_/.=.fz¦ 2ÓÑÓµTRSIgW/§ãúÁtÜ0˜j5‰-«i»à^nݸmM@IDATL¦ÔÒ¹ƒ´Èãc…ƒ´—# )kR¸”Öz(÷¤›&§k‰Í÷¦ húmAžŸ…ù>i䆱z¡ù)<©$"šŠsÙüLSâLD; ÍÎ¥#tBÓÃᵯe î°ÑÑš¦kBjDÃÉœXóáÿ³w€UUçÿ{{eVØ{…=ĉ€(¢¢VUëÔ]GkÕÚZ[µ¶jýÛÚW•:Š¢"C@EöBØ›°!ûååÍÿï;7çå%$á½——ä%9nî}÷žñïÜñýÎ7N/r³YÞÁ ´"sÞWij¢af3,Ã@A†BfàÀÇ™A ›šñs§igx])DÈãåÚãhØÁí‰ ºHÖЈI hi8 ƒ)1&¡µÑ"š)ÍL4\Veâ‹Ï`©ŒÓ·e¶ÇëƒgÕ0£¦{§ïË›otà’ø¢¶ñ¨© Õ²â€â@=r€ lØ<… ›¥°9 Ï·…)ŒEç¤c0ÏÒyöR÷^øÔXh¿µ´=¢¢Šýdd*t¨´,Õ¥gÇ9¨_šžfÃ\ì$@PŠ­´{*€û²¤Z¹ŽŠsíZ¼?é£Í.±ê&˜—±Ó£­"€À£ Kh!LñꚎš»P‡²}´tÝ:kPO!T†2ëÚ®*ß48 µ ìÛÂÏûÑ ƒÉ ðä'©¥‰´wü,sÒÚÓ|vø>4”?ßâ@GÒiý*¿â€â@|r`ÝTgÄ´ÝSŠ‹J–Ô ÄÔøŸx%ëÜ-ddÅ'Õ K•4 ËoÕšâ@ƒs€… žÍå™[hìX 0Ñ•ï9“欬ו’®ÄCù^ÂÁFi§UÞ3eÍ>3{ò}tN#½½A«/Z”´psQ#=½Ô _!/4KŽJ|Ù!±ZR¬:êÝÊ@ËÂ($PÀ·•ýì8 †5>ˆq LÖ¸í\§Ÿ~½ „|ˆF72Ý@ÃÄå™ZÓ‰‚ݘuGà‘Jh¬4d-þ‡öœi÷›z±Æ„´e#µ2ÈÈ}¤L“€†÷rãûP ç"­WåWPˆ¬›Ú³`ðk;'¹Ü¥+¡ìíˆ-šß¯ûÿ}Ë™[p$þ{P¿*@S¿üUµ+ÄXèá™c¶­g‡a^;Cn%.7í-ñ!™ŸN˜ÚÓ^[9õ QÓ½ ^7ÐLc»šE@€E{4 ɧÛÜô§qvº¦¿….‡å}ø3´4%çhÞ.7 ƒé™L+³½"uUq ÅqÀdÐÑÙðÿ¨má̦Â|„–¦0¬¥ñxlÌÝtäD­;TJ$Œ¦“ƶwi|]x“É_™ß,tÂÇ w†˜…-Úã†/›€ˆ£¾ýsµ‹Þ\_Fn¬ï3îq˜‰ÉÄ~2²Žƒ…¾J!µ¿Øá&Þ &ÃE44ö½aM¤‘åd›5í}y”l· ¨™…R]S)u¾¥s@Ž–~¨þ+Ä–YÜØÿ¥Ì)ø¼~ó3> Ã¼Nß̱K“—ŽÓ…}ǶÝx¯M›BŠw*}ŠÀó`.õfû›KâcÖ(Ë—]n?-É:LXU‡Ö$Œ‹ ÌÔÆ DBóð¹P Á‹•Ö ª«#´>>æ…JÌ„¦Ð6BÏG{lõ;)Õsœº·OúF°9‘JŠŠŠŠŠ Í­ Z¨ÓÄ¢›Ü6¾FY¿ù? MG<µ§M<†¢%n8À‘·ž9ßNÝR°¾ÊÍÉôsºaÎæÉ´öîú×% 4¤½fÅa„9ßDDÚš}m­¾3>va 7!šù Üëa×?oÍrù ´)ą́C6WíÛV,<: NþÍ-usm…Q¿N­„_D]͆šT–ÛÍx¦gd«àŽ~/e>)·´½4-mÄUÃâ@6B ¯…³9‡þ-dNGðû••NºeN1YM:ºw¤tøZÇD==y®>FÄ­é›\t|EÆu«NÂÈx¼:”“OGs i·5!‰c§…ÊÊñÒûè{sJm<‡¨;›ú¦'’ áx• Qs]ÕÅÅŦËú#¾Iÿ•=€áÀŸú¼”5IþnI{hZÒh· ¾J¡SÛGÞñ—£%~,þ µ‡½´û¤¶Úü¬zÏa€ãÚªlˆâÅ3÷2Íâ‘3·¸…HÖ1îxêº02omûPúkËî53¼àpÍ»²‰¢¹ RÍHò¤~Î ”h5RÿNib¡BöEÒëÔb…5sM]QPPPh(¤x7d†%Z{½.àû`Ðß7õh¨öã¥hâe$1リ‰u÷•{>5 nJ¢§$Ò•ý,XØ‘w¬hOnûI/ nW™«*MÕý6‚FæÐT]B¯‡{ÌŽìªÙå*£€OãE,xnûM-_¢/Ÿ†¯ «!@û$‹` ³E,œ(@M(’mjSô*(((4 p »ÙzÌr‡àO“êöê>1íp… I³èií¨,9ÕžW]Uh²°b]‹¿n¦PÛéîáVúÃ’Rš0½€>(7C ukïžZñHuKÑÓΓ!a¾ÂàÓh5G‚¨VøÎs3±z9"ƒAãÀ©çp8Å[\61VôõѰJIrÍf †Weçà ±š-޹ªÃŠŠŠŠ1åÀ†{'ÒMÁ‡IØÇC.\Ttâ­˜6ç•UH_qN¨"Oq R°À)7+âÿš°`¤1þªñ{afÖ kŸ´Ç:'¼Àcz¢¦‰9s³NIzºªŸYºàãøîfÚÎHz˜i0ÐJ,nbÚxQK‡Å¤;ܲ§Ë§Ð|h¼ÐÔ$4µK4S÷Ò­”à+8]ñsÝ„1èãü ffë(Áè§!lÔ*%°94Pà ”*@Óbn ÕQÅÅÅ&Á \±çÞ ±º¾ÏK›+~/4Ïhšç¸¶ø^I Ã`ƒÃ§%X„6Í£ùŽ„Ã Åû¼´3×KKnI¦¿^èÀz4^Z}ÈKs¯O¢™×$ÑŒµWd}?õÒ[—'Ð?&9hýQñŠöá&AtÅZ%€ê 0nùHóõlc'‹ì‹ Høi;M!¿Žü”^¶F.¤tÏ~jc ÐÀö3É ”œ”H‰‰r8lBCcÂZ>lr¦44MadŠŠŠ-‡Ûô¤‡iÁ/÷}5spðw3>ˆŸL͘ɪk Ë ˜aÿvà†yPŠÃ„õWtÔΓMÇÌÂ"(×é§kgQ²UµNüb­“[æ‘ÑÍJ°x$§7kÑϤ;żô¢¢ÙÍÚba5Tž‰i3Á4.-Ù^.0C\®eФžêòÊzÄZ4z¬r-ƒÃj¢­´5ÇCKVRø²Ç: f!œ«£#ÞÎñú2íÜ©cÙ2Üdƒ¿L'hâÒ`’’(%9‰’’))1€Ðl˜‰·‘Tô((((0zX¬ív»Î„­ùÌäZÉ«ûþ4#ÖMíàlÎR€¦9n î›\DR,$ `„Ÿî0·ð%{OP±uØÜ)pUöƒ‘`¦º tpÇc¨Ã+×G’˜¦Thz¤'•GÒèã~Ä*q]Fö%Bèa«Õ"|Bí6ê“æ£ÃÐ$¹Q÷a:jéB‡ÍݨØ«¦E=6‰0o;nêÓzkª¬•÷ýrìä! éɇÀ>²ûJ}…b¡L‡¿P·üÔZ™´3Ùm6hd4ÍLrJ44I09³“Õ"(ÿ™šx®Î+(((4.æ=Ø»¬ï«Û®#¯{œ:`rÞ¯¸(÷ êŽÆ¥¬~[W€¦~ù«joHm„fŒdƬ:;rwJµb™êí̤õ‰cȯ‹­ó=¯QãŒ"šBvïÒL2ÃϧOz² —˜ôÓˆ…6€ëàúLF“3¬màðÍ^¯áœá+„qJ)-£œR˜è¹÷ ó«bC @X瓦vT¤Oº(òÁä(j©ÞãÔÞ½ŸÒ0ím³‡–¬sä•EP"Ñ—Gƒ u貆rV½¾DJ5"k«°Øð¼°ÏLr¹f†3ÈáûH™šÕÀLuú8’KY{QnA1å9ÉíÖÌO#›ê8¥Zñòû,÷eZ²ƒ2zv¢.éi§fTgZ,¶ÿªßö~/m¾ž³ï2jnïÿ·Í ·þzàÇÍ•) Ð4בmáýÒÃÌž?ü¬‘`S!´]“œ´;¿ú;×S–cT̸„Ŭhü{Ñ9×3-¬1Ð 7‚F¦—éfú¹±H h¸.“É( ;¸{Â9€…69iàÏ€ÅBËÈåqS>,éÌÞ“”P–OÝ\Ûȧ3R 46EØœ†,Æi%ÎD^=#ØãÇ/6³¿ŒXæ\I\XkÅÝH@ÀƒR„fèíÜH”;il‹®UªƒÛj 2ˆ WÛ™œÐËèqýg€"g3é5€³©BcÅ|q@Pd MbûÍhš3¬ÝŠ¥Æ¬áêG³àO,^³•¾]IÅe0 Q_Jz#?šyj,:€ÆÑïM&¯ßFŸ·’à#xÁiüÈþâEªÅŦÍm |¯ïß2'ÀnäÜÎÿ«ï?¶}¿ý~Í2¼©4Mû~UÔ×ÀirÆÀ€gÞ­¹ËawÛ&Ù`VLä»uTê÷ÂTË s½ÜÚ”Á>2Ä1ëaÞeôÁ/Å/|S0E¬Œ¡¾AÅ+)רž²-=(ߨ&*ílŒA §4ÏQh‚ ÈÚMÿûj´1.Ü;»¨MÚFì·ã¾©[¸øÚ8ä÷[©´´/;‡Ðg‹ËhÉê,úÅ¥ç­ ßó*)(´p´2Ý«ËsŸƒÏowÌ»¦bÆòMpäÒæÈhšã¨ª> V˜XažSab…%Ë ¬wòz ™(%r¦áÅE´Ó68"ŸšX°—}fØô}8:'¨W{øi@ˆ–‚´I+¶¾R¨gmƒLlBÅfhø•8\ÂMd<à•£Ü,Íï÷ yÉu”ׯ#Þ>LB‹ÄÚ^Ô‚(3Ê™In›×Áaó¶f/ûu¾£”VrÚåÁ§©&n¥úáóâƒ9 ?ÍŠN_8¡] ”‘ ëô˜¬Ì¬×-P4[I0/Óó¢©hß®‡Œ=@­F€4‹.ÜG ”ˆ>³f†µwà…165³Y5Ы«¦%CG¸¬å£öÕq`Ѫ,šýíÜ39Ô¾í\hA÷W—-æç,9Åæru¥¼üÉôú ]5~$469ÇU…ŠM‹ÛïèWÔï•M·üº%˜TÄW2pIß—3oçhhM«'§§6ž Ï"J0…Oªƒ9jªÍÇ¡¿k,Ô\.°Ê‚«‚+;À³)ûŒ¸=á7Òµ¹ÐE‡K‹hhñ¨Û‰èg¹ðñBÀ®ÄëÌpxdŽfÆŒü»a!ÎNÐ̰‰“&NL'ÓËt3ý±¤¥–AÆ#-`›¹•°Ëi# ÀùÅ›§œg F|ÔøYÙÂ6¹b“¼âú¤Ä€Hjl¢Å`€7N hØÌÇÀbvcQO¸Tìó¼í4UGdUáíñF0C‰Î¾0v¼É’±V­Ü¼P>`Ð"ü‘@Ó&€ÔHÒQì¡á=ó^óaÒ´2±ƒð:§r5ð3ð-´"³­…f/‹ÒÒ>…Y£æ+ÓÐô3ˆj×nåæ^p…ç-à§ gd'šž&Öž’7𨀵@rCe¸ˆdºm þ®ïK™¯¡ÐCÌ7¼¶þžñY‹²Ê8МøO€F¾PØ®Có¯ža²X.¤éŠ/D:D»„æÄø†ê ðx¾lGgöüùoÿõ™µh;¢‡¡¡hu;´Îõ< ÏšÌY(gÁš_O„‰UÂ3ë0RpZÇ]ȱÊôVá7 šØ1ÞÍ/ìÉœgMF§˜…Ÿ†p>ÇZ'I j@'Ó˵± !´®—…tümH_#RlnÅ`Æm–Ëå"´4 @$¨a@ÂüóCK#ù(M×4€¤¦ŸÍØ40¡µçóùQ—»¼nÔvØ´B>˜¶ùP¯ÛþCûÃ>I|“: ð$ÀŽ7Alfôµ@܃43ô}` ‹Ð²@ãÂEÒÁyõÐø0Pdºp±&ŒAï™ò¼äQ(ßÔ±â@(ø¾g3³Ùßj`¦MëOB/7Ê1ƒ)AljëhÎø¥&Ñ ÞÅs¢€y¥!QòF%v¨M…ÑÊt­ô)OœôçMÂk«>ªI^ÿ ôùâ¦ÒïpèŒ@Ã/1†±c¯´w9ð>زß©=^ÀD»ÕŸ’d×YMðâU)b¸<~¡3Pätéñ~vêÓ>êõø^5œ üã7žá#®·)6k#üxŠÌ`' ¬B …¹•Åâ¢ö/—ù¨Ø£#7L™¼'ÌÒøÖ¬{bÇ`“‘}J”l1P¢ Î÷6ƒÓÂ'̤$' - âLo}hgB{¢ñFóÑa­ òÒ9žMÍÜnh³BÀŒüa0ÃNÏþrPÈ€Fò“ÁQ°NQŸ¤–†Ûfþ³Æ‡5@¥KNg©7¢nhi4°©%y“‰ë–›Ô‰1,3Â| a6ó1» Æj€FÒ ƒ JmüIN"©IRÉoµ?=øùx>3fSŽÐÌœ¾DÃå`M‘çXúhþ êÕ©>¿S4MiÃQ—-)y#.‡E.¢•éV<Ò¹´ßß7Ýðê–Aìcyp"\»ý׃f„Ûv¼çkl@#€ ˜dºþþG/JJIýÏô=:Feô A½:êìVH€*Õ…:1ãž¹ë­ÉÚÓnó®ì¿èÛé¼ýñßßù΋ÏÎGå•Z©KkqVVKÀL‡& ³ðÍw6-²@Ö|FÊÈ¡=­Ü¤Š5œB…êH»Æ8')@³k ¸M.Âgff¬™&gAíLlƒÔD·¼—Z ³ýJ\|X‹«À„æ,¡ CÖÅî¯Ôt/€N\–…@ÖÌ0˜±Ûá«Ã~Mî2h€4Àt´˜5h±&L(ïƒõ @¢iY4 Æ‘ì431‹ÔÎ@3Úæ3AúÈ÷BÕzxˆBÏ×Ä+u^q ” æ9šY>"™±ÏLc™™…ÒzÌô´J™KGsî Åk·Ðij‰{Ÿïõœ”¼Ñ‚¿u=j™nÛÃWôyyó¿0Ã{¿à‡Ž^ýÚÎoV=Ø[[­‰3©±œ%áöÍ7=òÛ­6dzÚ¦®»h4õîÒ^“›8sã‰|C=°oºŽÒÇóWµ;”“÷åOþñ7o=ÿ‡WA+ƒšŠ)ñx"¾Ž´ðGœ'hµß fàC¾HŸ)T³i”¦}¨;;d{002ê›—±Ï ;¤óƦf|½¡…ŽP!_£ùå&e B?`40£™›I0#A×Á‰i8€F pø×ÉAÔpT1ÐÀà†K“¶ww‹PÒ¿ÅaBë—u1@Ñ¡i2ÆXÏä6Hõš”× M‘/ö¼¶ÜøÐ¯˜ùãÈþÝ7O>Wo6)…LSõtÀHßz™aúÜeµ[÷¾tÇÏèÞ~ᙿ£¹f jø#.A8Æo‚YøeÁš…j0fV¡&UR˜±| {DÊåû €ÒCgœmn—AæçMÎñy¾Ît2•4@¢Íâj Î(@ƒæ‡z¼©}RX’üÖöš©÷‡ë Ö H S$}dL°¢?µoo ‚É ÉéÃÀFji„L¹OŒ2 ÎdY‡Ú+Ô•ü ì;tœŠJØ¡™ã9%Ø7ÒñÜ^´ôöçA ŒÏ4ǘ6%oĘ¡ªºøã@$2kc`jö+ôBsú Ð}ý^Úô޶ǯ‹¿žEFQc––ÌX¯¼mê${BÒÌÜyÕù'ÅEƳf‘›£àù§Ä æÅ[{rë{/=ÿ :·ìæ—X¸3ûسËÇbVà‚j®Ù´Jj$ ¾ Z îÑp$(à#Ò ×šð _á„Î{M‹ÀtHá;švê« ó)4I^TÝË<Ü_NU÷òº>DRd¦mf 9ršÔþ˜ðJ IkeÅd[A^–ŸÔ‰±„)Ÿä/óX^“tÈöÕ^q ®àû‘µ3Y{²qŸ0)±½®UÖky¦éܺ÷0uíÐZ¼ƒZÈWòF½Þaªòxá@$2ûÍ êÙíxŸMÄ_fÝkèÇ9ñÒ—héhh@Ã/nÓÒ¥KŸVm;tú{Çv©B3mT¹ºqZ1Ý‘Ü|ÿ‘ãº7'Mº±ß¼y`ÕÉæíSÂ. ½š#FÍZ[ÆÂ/ƒ ivƦ_lzÆ!‹à _î¨ÏÎú±Ø¸>®—ëçv¸=6;ãö™¦§¥%9TBc%Ìð`†f@èeÞ¤_Qè^šè±¹o¬éâM‚CfZÚ]ÔðýeÐÍùÅNÒ Â&`Ó®Oè½y—Óš­oѰ>7RßΘ23äa”Ÿvi¦³ ¤Th¡¥&4ìÂM7£’7šîØ)ÊëÀpeº¬G2va*ðŸ²)Ì#þ%cFVŪÛòBÚ7¤†&8[þ8’ÓZÝ>°W§€ Ðøw Û_òXdí>Ä€æl¬žh–¦g¡Ü®:«Ï³¯¡™ÐãÐr‘‡‚Ù^$å[J^É¢¤ñZ@ ôTRˆ7°6ÑËáÇÝ^¼ÀºRa¦’²\:rb£Øzu¾ö¼š¶îÿkYiüˆßQß. Pt87—–¬{c7õF¾Á=N?í|Ÿ&Œú#íÊþþ~y”Ñír¬çT‚( ×ÐÂÕO‹óµ‘¡×qör³ZÐÎ}hIÉ-`U«ç@¸2Éáÿ³Û©»²N+({øøY|¹úZãÿlCihBgK¬ç]|ù(¼¼[#4sË›šŽÓ{‚Ç7u»kîzà ÈÒd‹ªy–_nšI˜\£$º½¬‹÷¡à&NoƒF'«CGm±©¤8oà ÞXËÁûh’Ao¦ÖÉ}Éí-ÅÇ ý è~­Êú¶Ó S輡Šk&ƒºw8&žùm;ð5mÙ7‡vZD¥eù´ýà7ô岇éð‰ a‘E•ƒ>‚²alš™”¼Ñ4ÇMQC„#ÓeÞ;8@æ²Y,Æð»AÿÚ”*7µ}Cihä †ƒØÚtì4Â]€×™ij k®ôb,XàØ/DWcób‹î«Ý\™¤úUïøÛ…ŽzoC5 8-¤ÉY¤€¦WÇ ¨uRoêÜv”ðY•5M0šš-û¾ Õ[ß¿[%÷‚VæZZºþñ›58K×½€<ŸIfíMaq6ÌYa8‹Äá ÎíZÑç§NØs:p$—žg.=yÇdêÒ>Mœã?¿ÿ÷lÜ» ]=.AH„ÿýdþjÚ²ç,,¡VÉ 4z`ºbìpúlñ:š¿"SäcäÜ5=®½è Jo"ÎÅýN—Z¤ Q³ÿúÅï ŠŠMƒ±»/A@€‹áØ¿@ >Þ™½°VFœ(Ø…ïÆ02›øUj- rµÍ ³!îåÓU,®³L3j@7ºr܈JÕÍý~À²¦N_é|}ý²×Ïáõí6УÝté9âwuífí9LoÏùŽ^}ìâE’«¦…«6ÓyÃúR¯Îíª^R¿«p \™îÎßï¥ÍB´Û¹Šr-Íß…M•:ãùgCÍÂs;,$ó†;47`FÎ9CzÓÓw]A7^|6¢À=÷ö\Úwø¸¼|Ú=äÞúì;ÊÜ•  2šž»ÿjºqÒ™xèÚË2Xâ6~~ñhžbâ—KÜ%¬°šxœêÞˆ;(‚{óýÄ›JŠ- ×ü‘Ï6ѵã§Ó }‚õbvÑâuªµëvL§ö­2葟o¡~].­5o ½÷òFcËmWœGw^9¶Á›¿ógçÓon¹„.:k­Ø´‹ÖlÙ[# Ãûu¥¿?Z=˜©±ºP3”éªjiÊœÄÏšT:þÆž|9c"edÿ¸4‰+ul›*6„0¦?¿õ9Íþv=zÓÅaqdwvmÜq~qÉÙ4´oQ& šÐdÄŠæ² giÍ\¸kܰ£ŽŸÐßf¡ãÅÇJC:ˆê¸^9ðÜ÷NQÿ[—W~vêµQU¹â@ˆV3óïÏΩ±vŸ¯Œ>û~ªð™áåÒ p €Ð`²’æÓŽ ÈjIFç|y:¢}´}‰¨‘ÆÉúý“¨ñ(o4k$«DØî_\z¶W¾[· V&hÁŠÍ¸ŸÌ4vd?3¼¯ ï9ßS‚Ý"&hùÄêÍ»éË6Ò³÷\E߯ßN‹VeÑ£¿¸˜’±òÒµ[¼b›øè°Ò½¶­’„uK·mèÛÕY´ò[¯°Ì´|ãh\úÑ' V ZútM§8æv Xxzûþ£ôá¼åX”¶ŒxâZ‡ Ù¤Ûã¥/¾[O¶ 'd);ËRˆVú››'aÍ73êYM¶ïG 5€.:sà)´5÷áÊt¬¥éûÒ¦ç𼼯ñDwï¤×v¾8ïÁÞ1°ym.7Ô,|èK¦¡ÚŒšƒ¼Îá0Ìì?RYC³y÷!Zµ'¸¹Û_¦GOŠCV§†“öÃŒÃøZðpÆa’㇤)’š3ÜXýˆ7•â‘ ê °?M(˜9}߃I{}Ðzz4‡ü~ñ>îå†äLa‰ Á)JE“nG˜Ç¯Ìܳù3„<ƒ W™æÿË‹±2ˆ©çOháÆÏÒ Á[t4sÑÊ/*¡Ï–¬§³÷¢êÀ —gP²Z™O0\ð‘9cPOQ-Ó°uïaXFôïJ5 J4•Åìñ%ò¼ÿÕr!'Ý0é,:×€£¹>@\纭ûé7·^B·\vŽ(w;´P ²¾_¿3‡èÖÉchò˜aôé·k€£PÔÙÿÈg¢Ö®§_Ê™°ŒÇ^ûúZ ÄÙņÐÐÈ.K†ò>î"~áÁò Š$vþòL̤U¼KË*öH¬™i›š¬Ÿå¬Ÿ]:B€> :òà‹Ì¿uãDê×-t¦Óúmûƒew€ÖN°¬aÙ«÷Žâ¼ È´y×A¡ñi—–H¼µo,ÊN„Ù[ LaÉtKÇé¼}ÿ¶éŸ€’aùþ_a÷.7…Ô€&„ñçCBœ8,(r’ÍÊa5+ÌÁØü¬jPY.&kN—[þ¬a*Ô xðYÍ›’è¨!Ÿ:­8 8 8 8 8 8PwÄ¿¼Q÷>F_(â”àЖÑ(ƒ†¤ºÄk0…&vÊgÁÉî¾j\­“³^‘8XÒËA}³‚îºjl°:6ó¯.í9¤YÉt)ÐÄ•ÒRƒYû÷è(´;l:w('OÈT Î| uǣĮk·ì æç(o*ÕγƒÞp—ÐïjìPGéûòæqÛ¸¤öRñqµ!MXÈ0>Ø¡Qáóù±jó^êթ¡ÿtôulÓJD9㇛òêGø¸eò¹Õ]Š×sMnìâ•‘Š.ÅÅÅÅzç€úfEÈb³©z1­QBÁMvNe-Û÷¡mRE×aýº3úÚšo•äš ¡É—êR:´*œNäS›E¬¥TX¬™ÌñùáhsÞ²U‡©g§ö0=,h0À޵HcGô£Ÿ¯é˵àtÚçƒ#›õ})ó]€™{Ÿ_b¯MÈMÃL ÝB.ÅÇ!Û–î;|‚ŽÃFó[8»±†æë&„MÜо¡ÖL¢és—!ŠÙ™B šg5šVxÁJAÂîsÈ(mÌe_bakÎ~R2ñqèoy>^öõÑÿHû@HQN¼¢yC¦Ðq‰÷qjH¾¨¶š BeŠpìH]H· ³÷Љ`Û¥Ã9èÕ¥…ŽϧB˜ËgíÉóâý̲ÎÈ=hÊ#è™ishÁÊ,šxVõfgì÷Âß–±6î<ˆÐÍšyX°Â,qÀ&@p%€ÉL4óò2eÁ¯¹ÄUFãàðÏÁ!‹µCp¦UY»E˜hî ¯¿c6¨Mˆ¹›¬§yï#—éôÿ}䀚š+2^Éj•õH†æ(Ç̪šÇ1ÁõEÚò;ieæ.JKN¤nPÁÞq嘈n|‚ºa"}ðõ zcöR8xúăxñÙCš ©/Ç_½R€gÐé÷ÄzCì Èjñ‚'•qÐ<ÍQ§òO©Ål¤dûW9Ä‹œUüzÌ~5¶à\ïý‚q‰ùyåÕÏØEQåé‹”ïÌf¥_8eà£ÛµCëF§Ów@åPPP¨Ì•™»!Óìž|æ—? ‡spFFwÚ†5až}s¢Ùèü}雵µô¾úþ'âIàk/A±OÝi5P)Yýyd ¸è«e›`«l£ñø8ŒÑŸ,cÜÔ½¥ðjhèþ‡G•Ì¥¡‹¼c•íµåÕúÞȉ±:)ÆŠC”rÔžñ#ûã#ÜO€PMN}Ó¢êWPPˆ†5É4¿¼z\°:Ž(Æ›Ll¢*ç´†¯Ê#ðæ‰=ž”ãÄŒ8];y“i,Þ‘¼UM5ÉN2_Uø|ÕsçíCgî ÍŒ/HçcMÍßÞ›G7]v6í!ä®ïð-Ÿ³dˆj–€ÀN÷\søÎ³<æ°Y19Å%U ‹:ú/&tŪñ˜×½ e  ‹qÍ, =¡`¦™u¯IwG³ö¢‹ÖQ€Lž©³ \콺ú ¥m x(ÍsŒÚ¹³é Ìp-]¿ƒ®¿h ìÕY‹àÙ¥úNÙÿúî[,ë«Ï±ÎÁ8þ å²ójCŒS,û¢êŠtkÎ݈šÅñC”¢Dq H0SK–z¿¤×c‰‹rP%c« >Ï‘Óò:šC:oº6Ú¶ª\x-7*EÆ›>á#§¿ø˜©Xh†÷}5sðö_ ÒÂÕEVUƒå® ªÁº¢R¨™¬•`¿ âr¹\´‹{½ýÅt¬ÌD?%œC›gÒ1s§z3L%nƒÛâ6–éϾ§…+3]LÓY)ú_ýª¯:««ÏZB Wm÷Q}S}õGÕÛø0èÍtÕØ7)Ñ^a2ÓPTåìăӱfÈþ†jRµ£8P¯°À<˜]ö!8Ák-À:3ë¨3¢¡ÝsÍøzm·¥TþÓÃÝóIø,Ø_oàšàqœ(“³8EVì8ÀÂ'k&Ì”¹Ý´xÍš¿r+0u¤­öaä×5 ¿Fy· Œ­i]ÂêïÜmÍFáªsMˆ8#ýkbÅxì¬úÖõ„ŽÕg‹× ¿ª £3b>N ÑÕFãpÀd´ÑµL‡Sr]?á#òA[ûïOϦöiƒh¨g¨mÊÌ2ï¥M»>¡uÛßDNýfŸÀ÷®'"nNÀ¢»io_Äðù½´aÇû´íÐûÔ½ÓPÞÿjÐýr,Kq]ª€â@¼p€ƒ „` ^hnJtôº™>_@,® ÝU ýéx¦_ihâytm1á€3.,„ºqû~š¿j›3YŽ‘fd§H1 ¬æþ°‰6í< @Ó˯ýe뻮бš³te"RO¬Ç©¾û ê¯;¢ äÁ€bËÞÏK7¼@_.{X—¸8ôíWôþ‚)´ãà|º`ÔÈjÖœªÖ4:oècT\šÍàï(ÁÖ†Æ ûMT®øÃ>GrÂ;ðwô™Ãé³ï§Ò΃ p/W¿öHT©BŠŠÍ‚vGÚ|¼2œ¢3À€~/oíÏS€&žGGÑVg°Ðé…3!kf ‹éó2É©Oš™:W£ XKÄ4Í\´žJ“é•°Üú#66H5]Å Žåe‰lGr7ÑÁœU⸨ä(­ÙúŽ0Û•½ˆô:#¢lž¬®Øyþ[ÏÑæ½ŸÑºmÿ¥®íÎ ^ ÷AŠ(5ûÚŸ ­¢f”ÁÀÆðLÍ£ÙKï¤×f …k~OGNl ·Z•Oq@q ™s`ÝÔNhfæËnúÖÒÄmR€&n‡FVWh~#~rÌ”–ºh"™•”–ÑÛ FÕÌTíÏþ3MEÎRZ çs¦—#°ÕÕO£©ô¿*?âù·«Âb'-^»%&ãÏýU´UÏBÀú}§†§­>wÍg{uº€nš4‡îùÙ b3N}EtÌãð}‘)'o+‚ͤ!|õ«ªË|U÷>_MLÛþHw\¶Qûž¦ôÖZ´*™×U–Àô.½7o2M›3†Vl~¡ë³åeµW¨‘O½>›6î8Pãuu¡is@§§OCzpeÈqÜ*@wC¢Š4í„æ7S‚ueÖì8$¢™±OD¼%¦‰#­ý˜¹Gh“¼IYW-MSê¼Gmôȱâ(u±§ÚÚR×⬩I²cÕr¿Ïh]¢'é„ÿLÎÉ-ôÚ̡ДÜqJgÓà?#SjRw¬Ü^„EËS§Ý3}L§Ãbë)Y-É4¬÷tóÅshê•ßÓÙï§ä„Εêaï6¼(||>Zpeîž©¢²UâúÊC®§X6¡Í¨ãFâ€Ùø ƪÒ~äˆi»ë>“SO}iÒ€æ³Åëè—Ͻ‹j7WbÏ胯—W:§~´,µÏìÂJÂ{ Å…XôÇL‘Ín6$ט6'4HµÅ ºë¢¥iŠýoH^×µ-+6äûª.ãTW:Tù†å€ô¡éÚ¾„84¿}Ã& ¯p/Êø©K»Ñd…3> pXÛÀñÿ¨8=àžSêJ²w !½~N­SúÐàž×Ñ£+E§d¬áÓÇtvh•€%¶ð`LšÎ¥&v>9÷üìGºá¢¨ÿZ²˜+Õ´ÿØ újù£\Ãà!Ú{ø»ˆÚ¯T™úÑl9“WHo~º‘ÆÖRA‘ærÁË!|2íÁ;ò?³‹we1Þ™_,]OÓf/¦-X6ό˚-OšKÇ2ïœ UDÃA ¡¸Øu~¼ö­If*Çÿ àcQD•Ê9  ô¦Ù#ÌÍvã¥Ê‹fò:3ñšm qÛ¾£‚qnŠý×q©Ž.9V[÷ ©¿pÛÕµ­Î5$`ÿ“ô´D²™õ#CÂ&¦f]ë:™M¿ºfƒðgY™õoq=tíF ý)>,ÙÇ×Ј~·Ñ“!Üs;Z S°HÓ‡µ{)-Ù.¾•L»ìGh=]ÚI—œý=xÍzš|îkÔ£ÃX䫈þèõ¹( ~<Ÿ|{ýsöHá×s^{Ñä54<}Í„‘´bÓ.¡Ò¬Êh#><]ÒÓhê”±t+Ïš¬½´~Û>‘ÍzXíùÊÏ'žI­’ôΜïiùÆ]¨ó 8R:*=p߯ßI™»¡ž14yÌ0¡^=U+¯Xûá7+&ºþ"Ü» fÄ4¡˜žO®¢1Ãû"$çZ¾i'mDÈWNI ÀùõÍ“hHß.¢-'Ì£8}¾t­ÎÚ#ÎÕŒ¤^5ÍBM4ˆBêO<& f”ÁÔ¬›K¾½û£óKè—_–Ðö“>úýù°A7aj3Šd2è蓨#¼G͇àB¨å™n¦ŸûMªkÿ£ióte‹§§£+Úë|?À¬¢.ãmÛª\Ãs@j6FÜ#ZÌ›Œˆa¦° òxqÏÌÈäó•á9¯=lr¤`†éaº,ÆõIO&³Ù,hfÚe?dû5í¶Ö4zÀÝt; Ü1y!Ñÿ.LòUÖpÏ߆à¢~zÍX|3mÙ÷E¥¾ÕT·:ß¼8ÀÄœlå{)Çð¹®íÓxG%mø»”h×¾ÃÉ 6q^ý‰˜­—a^]¼¤`Á:hп6¥Æ#ÕáIYñHy9M^DƒÚ·«˜ødÁªj)åEðzvj+TžÛ¦RÖîC•ò]~þ0‹.4fÛ„vexÿ®4áÌ b;У' DþÍ»¬t¢v07èÓ¥µo p´_h~8Ã~h\,XqÂèÔ¶U…-òHÌ\œ;¬]6f(uèÙm§”$;]pFj“šHƒ{u³Ûö×VmÞît 4=£ ! Àé&ÄEõGp€ÇPš\±Ó6G óx ±AHÔpÓ¾­ÈöÐs?8É0saOí…Ý#Õ@ï^™HkïN¡¯nH¢KzU„n}f¬®Ë°Ð£gÛhÕ)tVG#½sy9 ôü÷ŠZtóé}é|d$Û'œÍ¥)÷%’‹þGÒ^¸y‹§áÒi>poyƒ&g‘ŽS¤í©üÏÖòó$™«”Û¡ÁÕ+3Ím)77öÑLeyä,˪ÓLÓÕ¿}Ù¬²XÌ‚f¦ûij“ÒWDG»Êj±@è€nW€Dì´çÐRúâ‡ûz´áÓÉž°×ÎÀO¥æÏ^—‹5.«1aÌÃmaqR5¥%'@ÖI¢µ[÷ `£Ìͪr(~oúuûP·IP§§7îDØËŠv«~8o%íÎ>F)‰vÊ+t’ÃV“…MÄ8%”Ÿï ‡ÏÐßü ²êtÇ£¨'‡ÖnÙ'®óŸc¹…âxê”qôÁ¼åôì›s`Öƒn¼ä,¬ ­±7½MÅjÌÛ´‚cå Q†ÍÔæý¸‘Ž,  ¯Ï aÖ'T¶]Êé™ñçt4È|j5ÊMÎ0?p¢bËÀ6šy‰'@FÀÿi—%PaY€~¿ØIçt1ÒË´¿ÐOY9^J³éèÁ3ltÜé§­)¥y~új§›Fv0Ò++JéP‘ R;)|O3ÝÒ”©öÜÕ_Uÿ«¯½ngƒ§u£¸æÒr¬˜ß*5H“34V‹ZùVt¢ „¶Ê :q¥¥}еdj×¶„Ë¥oV>nÖ`>ÖÌ0˜)qfP·T3ulDV›UÐÊ4³†¦6“³`E5è„ ûØx¼N˜rÏ¥Í{fÓc<™¨½cÝ–}–Q×.ÔõéWéý¢ýgÙ{õ|ü–wCÕª MšaBöÂ;_BFñÑÕžQc_¦\0‚>]¼–æ~÷õíVYÛWc!u!.8±„Œ`btzÿpì*üjød¤&h$4œ?¢?Í/M޶¼ß®ÚBæÒŸï½ZøÍ¼4}ž,Ü›LšÐ*OàœY5`;ͳ[cGô6¡U¯³íè“·_°³‡Þÿz¥.³ÃGŒ=+×ðp™€— •åköŸéß=~}Ë%йè7ÿ÷‰¨–}yÌ ‰ÍÙBÓéhÍÛ’YÈÔ6€@hðü‘™L’¬: Ÿš,ø¿,Ýç¥éFꔤ§[çѪC^úz—›&ô0ÓÏú™ až; °¹èý"bÄië ¯Øg¢Ž=yVa$¶¯×"g1ÓúÂf"á&Y†ï±hûn[‘äkLžFBgDyÅøD7Nµ£2Çø9dP`2™È àpØiP·¶B£ºãX„üÖÔ*å˰}jbÙ)ö™É…™™š™ÎÉêíLèc™V¦YšœÅ¢]“ÑË‚ëÄVä<‚ðγDà€Ü‚]Áê&j›Ú:05µµoêÀ‘ºÕàßt 9>ylj‘6«Ì©š*þòÀµ‚ôɰtáÈ;òSÅÚ˜ÿ]Å„ãöýGhû¾c•®«ñË<»ëY¶àäÐÄ]:Ur;Ã'hò˜!B•¹©ÜG…Kæ–PR‚]ÌJ±¿ kj¢MÃ`öµ*k7Â#B[r('O€và_³e/´*.á?Ã*WVñË´qçº}uX;Ó¯{ØÝ{©k£°Öˆ5@ VfÊìbßyÖoÝOE‰Û‘ WÕDC¥ÂêG08àÙsù †ËVò­„ÉÙõ³‹èê™…äA€é³-·b6~ë uH¨xŒÖñÁL¸m’ï ¦[“S®‡qB–¶ÿa4q–FåiÄÔ†W€_ï’×á•P¹š24@£Ç„440åJp8È‘à€S|Ü13méhÎtìøÍTRÂߣ ³¬úè7×Ïíp{Ü.ùZS¯T=õl›D‰‰0wmL#ÓÊ4sTPîC¬S¢=ÎôÝuùb¬qóŒ1Ça"É¢Íæ”7†f1uø§›Š?÷†îÓç§®œ1#£âCk¢T} ʾ¯Â¹µ8߇*5-õz-t3ÈÆH+“³ú>;^ÚWŒ³Á¦ÎÙÞýb=ô·÷á¿Òš&œ‘ˆ-EÁë‘p”±ÿ}õ#½úá!ÄØ°Zô/¯GI+}ÐÑÿýü{¬'`¢± kŒ^ûh¢my„¯GZ3Á¶ø¢³Ñü›éëe› ]ê#"„È2ׄîíϾ£ÿC[œÎÔ3]D¤³êhàÙ•*s@ š¼4ýߪRÚr¼²Få"žqꞬ§Ÿ\Çߨ01qUú„GÚª–?VBr]úåµ—jLžÖNYÝ®FsÕ­EUº19À&[ìb£=k?84<›‡vÇÄIŠÕD{ŽÒÑÍdóû@IDATâtÆtºLМd0`}¤ÄªýõÌäó&c¦ÔŽoüzt>jg P§+%Ä$#(MR65 ‘iešëfnV•Šêwh3€æzÊÚ1bgiþËŸô8˜ê\¼×„­7ö&Ì×ü SM?Û•·%ï¹7ôé†éONõ°ÝšJ-„­S’„¿q én“ïf›Tû¦Ã'Š!øô©údÌÈ2g]›»—Z 8Ô¤MÎ8`ÕÄÑÄx“‰£ƒýù¾)LÈHò;Ûó&kEBU¤Z0ô7—{®¹@˜ñ”"j–j|9#ñ‡©WŠÊz˜«ÉÙ)è èÑ!¥ÏCØMÅ[ çÒs‡ÐD„}–³~’Þ³Ýon½e´ã7M·ú£÷@ìOÜæÚ'ó¨}óäNº2JMƒKoëîêûRæA¼ëº2¨ñÑaO;ã‰ú& h"ad(ˆ¤\uyùÃÀÀ¢jb­Ku)PnWZ ì/S[²˜«¯³&j««¥\“@2Öý-óèî/‹éÅ úæÉÄÚ˜­-¥o÷Ö hò]ú(ÓEOg£§þù¬·ójð EÚŸHóG@J̳6OcÞTØ”ø^ýoÀ:ƒ/àsº<Âa-övTµtHÓÒÈMIÀ¡‘£™Ò°ÿ£ &Çø­¸( k`yDPm­"†=Ú{Eš‹q¤2¦Á gh‹D{VÍgÖÌ“³ v¦®ÁjaF5—°¨b&Kå¥ Ü+ЛøýÚ oYzüž[`{#ÀMw™ûÞ0þ³ÎSö§çßÐ}‡þOHšýÛ©y!yÔ¡â€â@ãq`/šîÊÍ<~Ö(@ÃÌh)IøÓT :ÐRúÞ˜ýd¡b ŸŽVÖÿõ¼ lƒÏÌŠPÌN,””¬PâyÝçÔôçJéå•¥ ‡:5Ç©g*hmáÔ|5 -Ïí6fŠžÖ$¯ë«~UoÍð{|9ù%|‡7( aŠP0˜àÄ@CûÍ`ÆL˜?»J]bQ_4>^èˆÂŸO4òG¶Å“ZlFÆ4pÄ5ŽfÆæeì3c·ÛÄÆ¦f|ikÈ”_Xð”!ît5é‰;Ë8jÀÓxn~ÿ›¦1p1¾ /Ç«1ˆÉœï+˱0IKºüןCÿ™N¯Ÿ~fÏs·4F½ÜŠJŠŠQp`ÊŒår] Â¼‰OÄAª~ú?k$ð‡çù®i]Q}¨Â€™HRiÍJœHªiÖyO›õðÖGçXh>\TZ¦ã…ü؇²!“ Ä1Þ÷|Ì€Æn³ 0Ëúò:X:ž£-†(‰t¢Õ‹$ÛÕ#Úšˆˆ ÐÂm2¨aó2À@F[LSÓÌp™†J<<&¥ÎŽdÆ/J¹U"4ñùïxûï»ÝØsðJ€››‘ûBœÓd“Y1mt}Àç»~ÅÎïŽÜ|€~Oÿíž•*‹ÃûœkÞDXï|,Á&çuM<Š~;ŽÒ’Œ¢g'±px]ë­òõÑÿú ³>ê”O›+~b‚Œãv¬ÂæŽöˆ§™ TÖ®†]E}fT€¦>¹«êVPPP¨!ùXöeIiieî:D£6ü„! Y˜€•‡tf€Áš7„XM;ÃëJÁ ŠÿÉðMâöÄ%kh¸mÖÒ°àÄ`ŠA ›>k!šõ ®™á>ñX ºƒ;·ÿ„Ÿì9$M¾í¶}.äû˜·¿MOhë.uÞ÷(7CqN$° —ñy½òâ fh°o‹ð£@&fr‘\Æ0rÖº¦NkÃß´ ís»Úo È0=‘0h£ 3׬`Ó2É[pÌNGÓ¯o.ÎAž¿óö—7Ì~òÞ ~]ßeYT6U¾7¿"ôBø(OoMíæLz86¨A6ƞǒÇ5k÷!DXýÚ˜2hÊvQ›´ØoÇX1V‹}âÝ¥¥}±\Äúlq-YE¿¸ô¡µiȱo¬þÇž£õWcèX}бZ¼f3Ýté¹b¬ê¯ÕØ×ðSÐŒ1?*VŒ}SQÕ¨Mlã—gÙ8ɇ8‚â*«â€â€â€â@l8À/b!,Ÿ8rø£Í:Ýý;¥Þ]ÚǦö(ja!’ƒ 6c ffüÍà“ÜGÚŒTy/7nO ç"­7Vùy 6ïÊÖصs>êdÞ$¨Ñ>ž4öÛ»ÝYÈþë@à™Ç_|óÏ"FäMàÞ•`££¼= ÒD@ʼn'èHñsÿÑÏ**ÔòÚ3æMN§3‚–¢ËÊcË2Á·¬-1›r¨}Û¹ ²°*%‡c£ØxQÕ¼üÉôú ]5~$46Ä}]¯Â/Õ˜ýŸÊÆÏyêX]NÿüdM¹`BWg4>aR 7èòýü4#aºDñ÷7kw6ý°a'uj—J6Ø"·KK¢=;RQ‰ hz Íͧž*,øx-\•E;ö¡>ÝÒi܈~âCÆ«ÍP»¯ÌÜEV˜L:w0eb±ÏäDíØ”Jac|nâ.íÓâŠ"ÅÅÅøæ€œíçO«÷ûÏ?ý誩÷]ÿÑ7+S{Ûd½¹‘ƒ°HPÁ@ƒS(‘¿Å…(ÿpý2ɶäïÆÜóÓ¿ßë-üñë/x5^£‚7 jä¸EL¦N÷ ƒ!Ió_ŸÑ&¡0ÿäÕ€ÿfTx>ÎiŒÆª0S»51Ùw듯”Ê?¡ÿú‡º•7FSæÙ7ŠÁÌßXس(-íS¬7Tw_™0H¨”ÅjÝOíÚM£ÜÜ«hö·€w˜Jç5÷4M^ÅýR©P~Ä[ÿëЕ/ªÕÄXÍZÄÍ@3ÄEƒaƒ~Òçkó‚î¸4òEa·šWöí$obË N3®¢œ“…âxÚì%”}ì$ЃVgíçävzË+(¦³†ô¦X s#@ '¶¡±p5 ÆB˜]ÒÓ(!ž÷>Aïµ\¦6­’è…w¾°‰«5‰d·Ô^q Þ9Ë2c†H3f³¢!ÕFCr€\”Ý'OæmÿiÝ_ϧés—±ÐW‰A‡¦EÑLÁ‚fbÒ\,Â}h]¡à¦±;ͼç1XÿãÒéNgQ1èaó/Þ$¨š—ß±uFX毢¥÷¾k?q·ïÝ'§ÆõÖ®zîI<à[CëË;¦¶ Üuùþ·»÷x·›–à{´– Íñ±æÙ_fÓÎ4‹j3˜iÓú“F3² ¤˜¦eÎ’õbUježXìãµÿ±è[CÕ:V³¿] sÅ쨵¶ E3·c xh´L¯dF<í·ì9L°¨æyÃúÐeç ¥VˆHÁÉUæ¡=‡rèÊq#hDÿn4˜Ðd‚YÁÕΠáýºRhhvBûÂé§íh,ôØIõ|”±@KéO×ötîP­TD¾Ø¶OË/.ª?õÆ–px½“H“ ÷ùX<³¥¥ž­ ´õ¾T:«Så¾ÿr„•ÖÜ•Bý[ió½©tÍ€Š¨R#ÒåeŒ”ÑV;Цæ5–þz¡ƒ6þ2…ÎèXÙêuáMÉ´éžêš\1×Âà§:zä¸0Xyø,›ÈÃtÈÄ‹žÎ¸:‘Ö‚æ5w§Ðuôrž6=}8%‘~¸MD‹•ÅÔ¾ip€…cÖа°ìúaÞËíÝõÎÚ­{uo}ú]€µ*5 ˜×Ìsæýî-›ælXÛ+"^ƒ†7v á1â€ÆÐù õÆï`zþ KD:³O„yðø]¥ÙOÜíá©»ô:ã0„y`æxhq«õ¸ º#úǬ4{é´ýÀ7äóG?™È ÁL1O‰‰K³é˜ÐÌ„¶Ù˜Ç¬%2Áôí£ù+àc㚤XÒïýe_ë».+6S|ÿ«e"€H}·W×ú&kpM(LÆÝ‡³Bj¨kO›pùB¼˜BÊLI°‰Þ”`¶NrhïÙ€ÐÔ&5IØ.ó9¶—öáEÇéda %:* O|ÞŒ<2Ù¬f*Fý*Õ¾l:楷ֻèÑ%t¨H›HZ;¯‹‘ƒ ÜÒÒî“>â힕ͅ=ÍbÑ­'¼ôߟ\ôð™X•Ü‚Ùg Š§ÇØiîŽ2Z‘Í“æµ'»IGt7¯ésYsµ™Ÿ<¯ò³Vm&œL²èéÉ 4©WeZ9ÿcgY)Ŧ£_|ZDo.£gÆÚ©c¢öÊÚÎH³¯I¤Öv¥»©‰·q~žq’Ù¾‡ç’¯?|»w¼ Á:ðâ»sýìÏ¡Rýr€yü—ÿÎõ3Ïwgmš³ø³™ßðX`c ïylxŒx¬Ä¬’N¯ótg˜^xË4,p/=ì;Ü yèÅ·['.Y2¶âcÉ'ÃHOÜíYóÔTÿC³§¥ô>š­¿ .4 Q,ˆ\ü/í<¸€>ûînúÇÌ´`ÕStø8c¯ð“43+C(î¥k·R¡ÓM©)_6ªf¦*õ<ûß*e. 8Áâµ[´áå²IÕ¼‘þn ý´O™ŸÇ*cÅ$ز‡ùÏ©ØàŽk ÁÝÓÚ™íû ZHÈ/¢GOŠ{*-%‘´¬ÎÚ'À k^B^ÊÕ¦Á½;Óº-{EÜùüB'•b-N»ðò/(r„-/ÒÐBpýÅÃè\o)wÿ÷ÚRšT•GgºiLWSXÚ±$̃¤û¾fÙ©rº°‡…¦ot‹{â¥å¥tÒ ‹{iªOk@Yq[*5IH@ÃÈ7 ÐÅß|ü¿O~Zþý‹Ÿ(|ùß°ãm`Õæ=Äk£¨0/™§Ì[æqö‘ãE«/xcñfxа‰ñÀžÇ†Ç(hÊ54Ðü}š=N7³€úÖÄ]vb™é^_îF¬9³õ…7 7¢\0½ð†þ Žz&Oümz;Ç´i#*^RåÖ­ËóýãO¾Ï=xô?/è¯ËÉ{DO=d1±w¹ hýŽÿÑôo® isÆÐ›þ@3¯”1ä›Zq^W¨“‘+6ïÑÌØ'"ÞÓđ־߰MÐ+B†ƒþº¤¦Ôÿºô³¡ËʱâPß±§ú¤ßj´Í.â$rÁª>‰CÝÏ‚Ô3=Rýà^hB2ÿî_³`0k›ª™œ11S.AŸ.^G_þ°ÎØ3,úÆÁÜìó¥ëèñ×f/À'n»L”kß:…Þùâ:”s’.…i&•êÆ~E¯ˆ™ü›]:^‚g ³~Oåg-R\‘]ì§µ¨w̧žø¶„ò\ÚÇàο²ÒI‡‹ô(´7÷Ž´ÓÔ/ùû˜¢êŸ<×Nÿ„°ßæN¿i£e<´`7OP6­4¼¼`…5?AÓÅÚšb,&úãMãÂîÏ?8é“8yyE)å:+ó¼¦_Ö×L‹÷¹éûýáGÃf}‹öT‹-Ð}µSGO€—ËÛ§Ô”² ýôðübêœ|ÏŠ¬¬¹±âí–]Èr”–"o:ƈӌ,MÀ½ft*5Yð Ç7$&#Zn k–,\¶eõªmgOºt2Â_ˆ¨[Éð5 $Ú,”d‡Îj‚ŠP¥ˆ9àòxù%^4­ÎçóíÛ¶åûó¿^RZZ"A ›¤ðÆN¨<&<6¯Rt¨ÄT­ ê³T˜ê`¡~69|’°fÊp9 pÔZˆµ‡+Ê-Ù«“¶ð¿X•í¡FÛ„É•¬v€ÀÌ-šp~^g3îhŠÐÀéŽL¸_ØÙWn²îHö²¬¶çu+ é¡îpÓ.˜œíɃÙ4N h&ôÐÌÍ<0Á”iÅA9rìpºg@Nj¯³;› Q)&ŽeÜtYos%@ÃÁþ¶ÜIßܘB·µÐÛÂÓü„¶ŸXnÉÆš ™œËdkìdY+3üè$¯e;jß`àÁå*ß|üBå­à’’BÿÂYÍÀï¯ú ѧKï¾mö„Öf›5Ùh4Yà»´„äõyËÊJK J‹‹Oîß±mÛŽÌ ûÐo~!òÊà…A ƒvæc67ã± jgpLnŸÁ‡Q*+Ë;ü†ká“wûÞfMK®ný/aˆ¶ç/o<ûW¿Þ8oÀ,X©~ø¶ü/Ÿ{S¿€Æèñ8ß}nšaúSS}âÚMƒñÚ¬ÄÕטR»Ò¹CÛÁœÕ´y÷lÚvà+-<˜³Jl V?M}:]ps5õè0WôšvÆã!4TÛá/Ë‹fò:3ñš˜6¦‘×ÏëÚ¡u0¤8¿¯"MAíLê¤}lÌür¬²öd‹±ª¯ètuí£Ùh6x|üXãé  ©+Cë³<¯°\]âç_‚’ê®×t.Ôg&43¡Üï8,ƒªX`¾°»…ÆHôN0R^žŸŽÕÓÉcz²ø#Œ«RÇÎçì¯È:Ä~Ǩˀc©E`ß™¶ŸôÒàvÁ yºÖ=Óh5Wö¿ŠôT]~ @R¤ýŸó²Ë¡MygC ‡³ý[ëÙŠ¤"±ª€&·4@ÀŸæé%§_÷aR/‹àÙý£¬4Ú‘öÐd¥xr$²Ô%ӱ⽱®Z.+}³ÁHSA™V—-d2ÞnÔÑžrM[¤õU—ß(#»µÂ/²:¾WWN‹)Bµ4RR :eÛ~Z·Û6´*48Ø3ø‘3J²LL‰j†•ɇ“ùÍ¿ḛ̀†%ibÆè€ÁLµÚœ',ŒàÉ¥üž€”[Ÿ¸Ë÷»'§êè„!³/jõÝçü‘«vü0Qð>“–‹ô:ý½œt07»Ã7éÉ»ÝG½ø¦éLŸß[úÛ;ïð¿ÑÓΓüí/1mFȳ\3.¼rá䊦ÿóa*wÌήh`Cš›q{ÝÑ·Û‡YDÀ…2hZ¦]–@s¶iþ*µÑ3Ú˜Lh|–—àÉ‚»ÀS»O·²lT‘Þš‚Hjžm‹8J›ÇÔ„FKë‚Èißî <*(9õHŒUÀEIö¶§^Tgš<¨šêTs<—¿ù†b‰“#{°m!?ø,8˜e‘¥ϧó‡> áš™Æ=r»=dµø„¥ ÏþGšØÜŒm®ÇUVFENWDý´½pò7Oá-š<Â8©h%²#ÖÄŒê‡ØNäïÐümö~†`AG…û3¯žsr7í;°fs·,™ÃN'‹÷Ò±ÜÍ”“·•ú_3™úv™D™Ð ¥%÷¤‹pÒ[ ¦¢Ò£´ì§W„Vˆ+ž8úäßLÉ ]hhïè»õ/Â\îW0‘oE÷\µKJ,¥ù«ž¬•½Î â@Ä @ÂQ[£I\ŽË3 )C#åº35Oë«Ïz]̽‚ÏÑŽS}Ñ&ëõy m´G›ŸF]¥ðè2Ocî iLî«¶Z÷í«³Mxr”)µÃµÆ„V·Liâ³XVyr¯6S••Aa`ƒg:’l&¨ßuÔΓ6 Y¼Ï‹µV¼´ä–dúÎý÷#’ÖêC^š{}böÿÿV•£ŸÉö:ŠPÑ—'*aüí>–±ÂKL¯m”–dÕ`,¼Ò•sÅ¢ÿ\ã7÷´LF7ãs— Rk®ø¸¼¼°Ì ¾$ÓmC­AíË T|ñqІø´0ø M_ïtӎׅ©š0­Ïìù«êïÞðÚ$ñÚ6N´Ë4î/¨|OUm/Üß–UîÎßv)´2Ìà:=u§{óóÓtÇu~e[]¾ejÒ‡ÆÛoaŠömÜ>“6ïšO~Ÿ‹|è53"šÄæRœÜžb|—ŒtÍø÷„ϼ¿¡îо\1æuÊûzÍÍ$ÖyCÃò9ˆÂö*<±†À÷§CÚúòÇG=U‹Æz:: F¼^1óϦc¼ñ;+Ü$˰††ëá(oñ$p7OÃå]¤ù¤iŸäy$ãi[Ñäøý4Á¤Mê Esà™@@ÿáË[Φ€ï:¼Z¯Æ¢}u ‰ÄÈòRÀäw6û>éÅJÜzjÙWtŒ’½'¨ÀØZf¯qÏQ»®UGr=Ì—9 DÞ2§ˆðË`Áœ¯oÂIjˆØ,ë‰E%ÂQ>Ô'DdªåÓ” M·¶Ž ½L¿ìK-EO¹$ËÔµÿ\ñ?V¹ÄÚÈ×ð­á-4íË÷Ó cÒµ<õ½âXžãý¿ª¹jš¹¥ 4>^ø¿SÍ8nû<Äv°jáòßͬj›ùð—¹ù³b1^ hªBÞX炯NívðU›”c5 G£áÑSÕzÕï˜p€ijÞK[jeÈH0#Ñ3FO¥08 É_ j$°‘¿åõ0ª<5ËãwyVâ,oH‡Å_LI} aú妟zbªwA à¿Y§×¿­á&¢×g´I(È;Ñ+`LÌÑð«(£?:êž~¥§žA£û=Në·Î¥µ›æÒ¡àzéá5sÞàGaò•LZ`e|`:·E)о|´ð:Út…ÐÌôé<‘õ¼VäášÍ&½?窠yZ‘óy’ûˆÀᵌ‡†ÁolÊM’&g¨ÁP×y2šú£)Ó˜<†ÞpÊȱŠvœÂi£.yðm[Q> M3ÔQKã@ÄÿÚ^Þ|5^ŠéÌù¥”ü0ʼ½-ÆßLê)4,ÈëáìY+Ã!*ycGH#>tH4›‹õvfÒúÄ1ä×±lsúTu fª+ÉëÅq"3z8¢2MݬGÛ$A¯¤=Z;ÚXö¿º~6¥sµW¤ýcUšI,’; k›à=í8EÚ¾Êød[‚Ö0€áŒÜp(Žy¯Rí¯iÞËù+7y®öZ¢¸úÄݾ^xǺ  žÏ–F®âó,Éé7 (»¨­ ïä ãâÇï8Á3á½Ô# CΖ Vê‘> ChÞÚTØv-M Ðò#-Zó ÀÊ&Q.ѦÍლLÇò¶Ðÿ³wðqWÿíõ¢.Û’l¹wËr§˜fL1¦†’@h)$´$Ô8Áàä II€PB1`º1Æ7lƒq•e[–Üd«Y]×o¿÷æ4§“,Éw§“t'½Ñoµ{»3³oþ³»3ÿyoÞ$Z‡ÈŸH\¶øÉŒÿdˆÔ"ùÖ7ñ‘* zdù‰ÃP>è ]<Á¦ï®x½‰iw•‰ˆ"á,1¥žºM¦€ŒUE(+Ÿ @ˆ®ºGtÉÏÒD9‹¤&æ$$ßà OCÕêúŸ{ÁïMS'™xzfxs™¤†ÂGft`0pÓ㦃L« Üõu0±iäZO‰(z´FM ‡H«·Æ¥™Àd"9 ‚€‘ü²,¡d)ÓôtùC‘1VãNlÚVOÌ5mÙ]ª§Ž1¥»­ƒØñ}û܉¡–$2TØà{u}š° DxÊ ±•¿»uÿMöƒtƒÅÿVFº@yøîkŠT½^t ùKw ;˜Dhô\¡Á°@4Nróu;‹94Ѫ눟©ñ£ Ø±­ùx$(ùÒÍíM{ìOx€r’ܲ —‚=–ieùéwo‡^Å´› O¨J¬»é]Êû²îñ˜54]B“ÇA“E)ÃÅ{®šŠJŸ»q§­ª( ˜¿üu—Ë©AµŽ £Ñ€óp3!ÙbCí š79J «q ìµÌZSÓ™PdŽvîË'šKu–†Fû‰Ì p•@&.ø˜–dm–ÓGÀH~*G8¡§ËŽŒ±”&°®Æ¢YàЉ⹢ç«+õ½ßsBȉ"ú 1"+‹Ù÷Ýb?„§Ÿ“—¿7ÈikœtÆØ³W¬‘§»e/;š²C×Õ›6¦çÇœ–u;|¹í0:sÄYÒ¡ è‹³>^[3ÆÝñ– œÕ7¹UMàn6Z•‡×›Ç„ÒäŠ:œ&$2&³ A3¡;g ²¸p” ç€ ‘˜ÑPùæ)AÍ©‘òEbOó0ÈÌŒ43é†&›…|$'ÉKr“†%\S¦h/$0ì©<ëjD²Æ NÏS$ê©mìNλô†îϵmFü›èÜwcCùŸ_Jš4oÞš0tã½  ¼µúF¸ôÌ¿Àm—¯C‚b‡¯rþò®ìP°|\3gê˜ïÁÏ®ü¬¥_ÜØaÜþx1íæZW`¼ÔLâ|ûºùn!gÏ„&dÈ8D -‰Á‘…vçÄ´Cb„ È¢ûDNrr®HÂ3ªË©mì¡!³+"z\LŒˆŒÕj®ðì@ú.· zÑõ‘*°œÖðTëÒ„÷³*}ZØëÔH\:ÚÓÚ%äî7ÍYÉî2±Àä`‹53”Ï q(#ÉIò’ÜÒ䬣ü:;åïLÞh»Ö¶®t¨)•ªƒa8Ç)’õÔ¶Ü5uªÇí&U~׆SÛfÌ¿>й|î΢ueÄœ¼•=ùê°Å+?¾^øèœü×/"60;lPíBOhH0ˆdNÈ…KV84&ð(‘áûZÕ F¯ šºa)ÉŠ,Iç„f “->]=ÇYÅ1ÉK«:ûÿfÔã*­Ô¡u+&°S‡6‚S´Ø™5y+q¥z;|°v;Ä#¹™êD8wÖDîjè2‰¹§Óª—7Ú“g"OaÁþíƒG޾.§ N›<ªÓÄ]$³+ònFógHóáB2èºÒg–e×OØP[cójqj'¸½ Ø€…G&eòYÔ©b5?rº8½ ftûL—41D´ã!;Ë DjPN’—ä×ÜLÞ?Ê/e v_\/x¯ˆ>çõTðÕ“ÏÆ½)o‡«—Ä›´XO¤=ë¾z’e¤ç3¥¼¸h=ž |/dÞ3Œ#À0=€ &È›b×(OGÓ>º{ÒÝŒTî"xå“MP[ßÕhjTf™ÝirDÅñ›²8Šà½Õ[aõ7{á†Kf ­ ªGK Å.ÜüÆY^þJšƒ­ÎÍÉÚjbHÌÌçK,[Uüî_e£ ¤Ý@½XAfpïÎùfãþSÏ» fKîÁD$4aƒâ×R Aðâ°‘9ºäëì£Iš Ïv$;n0à¢añ8»Ÿ´74J©@rÐý$Á k2’œYŒø™IJLÇDrÈËYW´3Rîh)¿”'˜ý›;ZœÝ;›¦Võ\h©'t(„²§êI–Ÿw|èÔª/?|‡üºÒûÐÂîd$Þ3Œ#À0=€³Ä0ÞW_ÛÝÓ·æ~ý–Ьú:Þýâ[hÒÆC^Ü™=6)Ü­àZ(†L±Ñdãq¸¦Åß—®†…ç΄óNÔåQù`*½£8Ô…¯1dÄyô«Ç´æø‹±7•N à„®½œØß ‰ ¼GSCÕ1¯™xî'ç›){Ú¨G;Ié*œ!/¨Š²IGÓ¾ßê ~ñM.¼³ê[¨ÔA·½Ó#â¶7œJ¥Õê·Æƒ®{· råU½h†–%:¿²“N¾¡¤¡Ó¶R7¬(pÁ²†Д¤Ÿ€Ö6|]ÿ€œB$1)‘<À2¯KýýÜ/U]eÎîán\£hTªÑ¿ºï®‰‡1.DK2ƒMÀñÕ'~2ôçw]ôÆŠÍ ¿ýñ¥ƒ>üõÒ¨S/Íúä(“3oÒ3{ÏÆt4 š:päVŽ&QÐ:¦­ëV“f‘ÖÌÊÝå¼°ÇÚø¯viéþ¥‰‚MV¼Þª'ÒÌ™ùvï!åØÑÃÏ­ùhÙ—Xzþé= ÷!ü‡s`èA@‹ö´#ŸŠVGP=$Izj6üèâOà¥åCYÕɧ_$ÇÀ¶ÒÕá/u’5ò»pÑéO®‚¥`¶WvkI§Œ¾ΞvX𴦡¶åý¾ÙóŸn½gOfîVÔ3ü­‘ {òÞ¡Ü«ßq'ofµèÉŒæÌf$šɳߜ¤æ+Xýí¸pv¶t?\9ƒ%1© \0Ju‡v@ÍáÜ/=õèxϺav¨H;ÓPW5 Ž»¾v‚²µ¢©é>ìË£Ù™ò7UçÇN¤:á™]¯ãü«ñ^ëp£œÔÐKêÙV½ûæ󯼎TW•VU«×]xº¦«sj¨KóZ¹@Ó3Adðù §Òq€Ï­3ÐEBƒr7kÛ|óvˆÔh‘PѽÅ1"2$SOy¯ž(8åÑZ[ÌíRSÃÉ"ä4{6=SO4g†ÌÌH3Sräà¿>yí¥—QpzîIC)54LhB®MNÀD'£†Ì9Ó~Ó/ M¨5ræÔ» ¦îp—ÍèÌóàȱM°ò›‡B½}Èñ3_íú?±Øiö¨«`îôû!÷Ðû@ëõ…€–5ódc¤Ñ¨Lhz³R©SJó"¾øvŸðfFsW¢1\ämmíö|˜7s¼¿£+;\ÁÈ*‰¹pŒN‚¦H˜ù’Â:¨Áy<á4)[ˆ®ü’tŠæ}{Sõ0Ìo%þ“woö 2?Œ³N«zÉõßa:§Õißñ8=›©ê/ùÌÎHDshHCCöoÄ:5HjÞŸqöÜïs~ôÌ«+’&ÉTOÉ¥dÖ:5„'‘ êÀ’iKÈ €43 ÔÌD‚ÐÐýäF÷”Nä9,c†ž*8…Òéðr–Ðð  ëƒöÝQO´Î ¹f&ofä@õzjölݲdãÊOhÈ–LÍè¹§çŸ@ïEÄ„F ºÈ8 .:í 0›RàŽ…á`ñøìë`ÆøaÖ„›!Î< Ë6ÁÆÝÏBIÅvüiàö+6À—[ÿ³'ÿ×°û ?Á4a;å[/ºJš4"uGa@ò1Š+·Á†]ÒÊpfö5â2aº•=æj\Rãa((ú²G_gà5‹)•¬ƒ_ßæâ50vèù0eôu°#ÿ5˜ÊïE\»³Æ ™/„"¼ß[{;«x} JšÕõ‡PsólÍ£q$4UBmÐy3qC/ÄAL/ì9ò!,?†ëŽZžô”Ép¼î¬üúA!«Hðoù¦ûü¿¶å¿ŠÚš{að€éôsÿùX= 5K+ëÏ“òë5µ…êê¨.®€†Fºfž$ë%*÷eúLHn*ƒC(ïØá¢óKï΂$1ŸâÄþ•­æÄ´N%51$¦uŒÐÑDÿ²­»ŸGã, ë ê¹ÅŸ}Ž«®lÎÎ)D³³ø¼›'ÔSÎØO\êV½Wáá úûˬ Oï*|뙽gáÏ@- ÓS¡}=MìÜm[¿fã¾­[sÏXð‹pŽË|ì&bÇS7Õ¤D«bÒãâ.] mÉKÛßád/;Ë2mÛßò|4ìÛ–·íïžQ£?Í›¾»ÖÜÝmë¥íï®Üßîr©5µ*-š‰˜*^¯§¶øÐÁ¾ú䣷ëë«iV:§w„ÌÍX;ƒ p`ú5EPXþ N u7ØÇEñêKpó ¨m,£úgeßKWß(®%Z3aþ¬ßcG ÄY2°£ÿ ¡È;òiÌCcER2}Ü÷‘„¼Ë7ÿΞz7–ï—ðΗ?†üâU0s¡ ø zªêò±üipÉK`Ͷ' ´j\pÚaúØëaÓî€^k‘ƒÏ†´Ô,¡Ù_ø)’>7ŒÍ<ì®zøjç_ŒĹªq‡óhV"A¢kçò;ßî¬𳬑—ÃæÜç ¦¾ס#E9À‚ÙO!iª…7?¿¦#ù¼à´Çá¥OtŠÿˆ4´BÒYZEÞ÷c?”V7ÍÅ>¦p9Ší⾜»²Fk©ú<¡¡Nigr‰î1­3ÍAȇ]ó½‡J`øà~MBÛVo’‰ȶ}÷N™Q¶5÷èž©2ïÞ)¿¡óãïz¿±a÷ï÷Ý—ýÀ„Å9_*µžKñôëtÍgvæóv¶fž"†âÑíš3&–î¥-ÞÎh”Z*ª0Gkjªw¬z÷ šSóáÄé§Œ6v|¶Ù—j0›’tZbáû ›=Vbçžü|yÃ{ÕãqÛvGµ½©±¢è@þÖœ-›ö`yȼŒ6ÒÌ™aí ‚ÀˆF¨ýmÙB—°¡éÔ7•‚+q-ÿÚŸAAÑ*qLöÂcÅ< ÒÎȰ÷ÈÇØéCü5x. ÇÎr8„¦EvÙ¬Ê;·L߬Ð.a'±ˆH¬Øü[#).ΙökÁ u E~¬¦ýÔ5• –j¹ˆ x5Œv± 4tB§5Áš­OÀžÃˆëôφùÛÕþ<œ®ز÷E\®ÁŠZœUH¢îg¢³€å0yÔB ¬7æ<ëOo6&Á3`ÙÚ[ ÑQ»¼ ×ÿ$Æ Å© Gýñâ‘x’hóžç¡¦Ã ëpÒvGÅëùuÂDPácyû~Bh€Þ¹öûó¬¨Ù‹fLÓü¿ƒ9PU#j0Z¥i¯<åÕ^üH”ÿØñÝþÛ6Ù RBdN–ÝFdœ#Ìò~tq@ºÍüÖ%¢Û˜¬ÕñœS3µ\)ñ£Ðäì°¸¦Õq ×…3&CÙñœVñ‡§ŸXi„fH^ 6ÍäÚ#4}\}îËPT¾Öm_,“½§º2`_O†öp—×zrØ%ò~ø&|"£qßç ™›ÑDït`×ÄÆ ¾_²Z4#Ï[F£rJДì·ÇÍÉü¬Ò]õ)ÍŸ½ëαE«KH^¨FŒcþǯn×Ó<’{M™Ìˆ5|ò»»ŽäüIÌ'ø"XÓgMžš™“½é²H»ÀcêðÑ=©…ÃÜÓóۖМØú`$1‰@ELJí{nItùüJBCï†$æDjˆÜÐo:O„‡È ¥áÀ0QŒ€ ;žu¶®9-IO³³~ o®º—nÀ¹4?s@?)‰£ý($Ç„Šš<ÿï`<ž0Z:ÉÁ¤ &N$Êïr‰.D0·šŽ‘yÞ?ß;«Ãøn\à®ã ÍÉ¡’õðÚŠ…bÎ/®Ú*¢{<p{ì8Oid«ä9¨ŸcU­ÉN«ˆøƒHÑÕç¾MèMíà ¿À3¡Æ©®,¦®“ä¶²uå÷ø¿äLQ]êˆæ£ùGWÂÔ1×Á)o&xd6F®‹Ê·´—ü„s:$VÓ@4;†K%˜á´Iw´ŠCDgˆ‹ÑÙÀ—Ð`¯NŽ  iŽÈÁß> x~XÚé¡C`bÒ¨]qÎ?!9n8|²ñ ÕÈÜæîd]%XŒ!kЂÉ?ì8nõûþ´ ,¡çOÖ“}šÐÐüߨ÷$°]½×ájl­n½qáº%XÒCO‘˜¶òï»gò¿qÞÌÔCÛ³¸åC8_ ¯ì¸kd Ε¹éÄыٿêkÑì5ȄՔG³C€æ)2ms=éoêäIRC#ØÔ $2’ÌHzKKpÒ¬9B P2v$"=·ä3,I$6ò·¼î‹ÍÿF êðw污#ˆ©qF8Xe›mwuƒê\yj›ß¾È“™À[È u)àk/6ñ£Í±<ÇûØF€æ™Är gVùüÊß¼gA€´2¾…µÂI-ŒœŽ¤æhݰۇƒÉt$è’ÐÚ)A’™Àsò˜9!]¡’Éfã3„¼Rv*G8!’åþþªŸÌ¦i²‡?fJóe: .wûózÚ#2åÊ5YW3Ç ò?cáÖS(÷=i\ÕsƒŒƒú½7¢ÝÜŒd ïé–¥Œ½ŸÔÄ€¬"ŽNÆ…µf™`Z:ZUEÛzïøÊ80œ13û²@y{á˜:ˆr´›öÒ„‡÷±Eà£ëuø|’›À2ò1#ÀD1R«A¤€ˆŒÁ`ÀM™É&Ðk¼PU})xUšÚÙ@nŠ8×&Ô@²G™Œ:Æe$ yIn’_–%”v_•ÇѼïó„†À9s³æ'F’±h{€ˆÔÄ[6ÜXF€`ØC@£QЭ°V£Ñ€&Ha51,A‡Ë+ ‚ªª…-™£ýãÝÓÑìjEÈù’,N”ibz‚‘ä%FòS9 =]þpdŒÅ4²®N“tÐÕzŠ •7`ÿ“<2ýÛ\spk¤òîÎ|ú¡é €³Ñ?—Ù3F€`F€ˆz¤y§Ø»=n›Ýåê’†Tš\Q‡Ód4‚Él3nƒÌnÁ ¥MYPQqm·hj‚E›Fû+*¯²ŒH6à¢è²e$yInù×”)Ê,NÑ/°®& IÄuvR"ROmËfwºT¯W%{EùN´Òîo¯ ¿p‰Žçåq´ïûüš®T€Ý:<» ®y§:Éú¥÷B¬j™z1¾3#À0Œ@?CàââÄI+5µt>ì‘I2»"B Ç…®‰ÈX­pØàp8 ÓíÕkƒ2Û$p• €”¤CšS‰ú¡ydúFÚ¢¡‰ZƒÚ™8”‘ä$yIniÊÎý¢½üᔩ·ÒP]‘I iÑÆ¥Y!{Ä ˆÕSÛ2ÕÔ5ª·;p=·Þ¶i&þy×|²è<šÕCŠþ¥¶q¢õwŸ&4z¸f[f4‰ý×wâ„væ¥ïÆ ­áç¿R‹žMøãy˜=D‡~è¾<ì‚ÅmÐèTaÑ\ ”5xa$Ι7Â{á׫àH-™Ò‡¤ìLfBÇŽS0Œ#À0ö¦ÆŠz›CiBbAç:á_‡^ƒÞÆPCƒyÄY­HfœØ)uÛí!èÔPg‡RÛ8V~3šz`œH&òP+Ü:5¡ÊEk—»_òEh>Ïì{d¢f&>û.qV!'ÉKr“‡2*G8!ËN9z+MÛº2 ‡¼)Câ`ô­§ÀòÑóNϽËá( <²c¯G¹Ó§ÐÁ˜x©yé“%‹Šë}žÐ„‹²Û£À'ùN˜5XK6Ù ¸ÞGJ\Èbö”yàÍÜüuváïe{Šî•¯Í2Ë;ìðØÚ&øåéf±Ýý™ßÃq¸âp:F€`F€‰[áþ}»†Œ}mNA1œ6yTp©Û‰EfW4¹žæÏæÃ…dƃ‹vÓÒèz¼ÁåMn¨vŒ„ û$*h”&ÐêjqO˨u=xUx܉hÞfÁ[œÛ£x ͬBf’ â‘Ä$&ÆCBnDjPN’—ä×ÜLJ-å—òÄÂþ„ºBÒ™™ …Q 9¹{êIâBÏ;†+ÇŽ¡Etè]8i˜üלÑN\""âëS4Ïž4QE`BÓAe¸Ðˆpo%-{SХ /í´ãÇ Äƒ™W峆é¡¡ë娡Yü•Ï3 -‚ù“é&™Œ÷Œ#À0Œ#нøÉ ÞÆ»ûÛÍN›Qí–܃ HhÂSQ`F~-/z%2#ú:ûh’¦ÓƒÑh‡t—¸¹p¢&Åí1 ò‰bë$™q0Õ€ãD£âÍœAfpf¡™!2“”˜(ŽÉÜŒ¼²uE;#eîJù.-4تpí—ÌÁj€¸¢'×>ô8K×Fë%›µŒ„³»ëIB‰Ï;òõøÚOÞËÅsò—ÛÝ;ê¯ñ‚œ[¿<÷v#FéI&4!VÌ@«~7Ç3ÒuPÞè…´8 ÔØ[ÈoAñ!²“‚Îï†ÄküžoÇÑF€`F 8dÇö~·ï…yŸã‚˜W僱Ã҃˩X’¸QK¢Z}í¾oÁIô€†f]Fœ€o³ÙÅÜ ’šT¯Oƒƒ³En]1—æbäqŒäÐjhNjŒðžD\Äœì0“fF˜œùµ3á;h A8å/?¾v|Mól~5ÁÐA³`ÂÐïôI2 õDÏùî‚"¥¢¤ø-¬Czß‹¶Õ*~“vÆåPoò=©‚À?ÓnÄ(>É„&ÄÊùáT#L¨…ó_«óf^[ß*‡‘É-£ #’4ЀskJQkÃ`F€`ºjp%™!3 ÷WË?Z5lôØóßX±9þ·?¾Tcз´Ó¡JCz"¨óêûMdÝ9ã|;;: p!¡q»Ý¨Éñ ³´®)£¼i\ÈŒŒä^×ȼŒæöX,f±‘©]'ù"B)ÿÞÃ+`ÏÑe 7ª 3àп¢ƒ)c¯aƒN¤HQ—WoÖ“Óå|νèÞ¬zíûï¾àÈw¡Óލˋ̈ëÕy÷Lþ2ê€=‰@Lh:èH ~ˆ°†OÁy4U6ê^È@L%jfÚ þdáœ.™žÉN½è¸bïϧ)1€ÅldF8 p:…Ãoó<Ih¤™Êt -ÙQFmZI)"5¤¥!Ddè<]'9)M$ƒ”ÊÜQù›šêaÍÖ%p¸â+°â˜/•ÙlL†³¦Ü )ñáÏcŠd9º#/ µÄ¨7ꉞسõ›?×ÔT’Ëfzµ4'}Ò’Ü,·Ç{½¼€ÄóAyK{&4Ô™’½‘c‡Ï6ÃÃhf6û…x<9ß [oM†Ür7ü C7‰2l-uòðØ< T ñùúîñp"ïÇ{F€`F€ð#@#Ñ’Ì8ðXl;6®ËI8ècü},õÆKÏRÂÕÔP‡UºA&Ò ]:¹ ÍŒÓIÞÏH;ã9Ijü’†p@÷:Ó% Ý“´4´Æ ‘ "1z¡µñy4#¹º+tVþªš#°î›_ãÀïˆKÀILXéÉÙ0oæÃH¼’ºK¤¨É··ê‰43Df¾Ý{H9z ï•+?ÙŒ Ð³O¤žÞ‡54HfCÚ)”ÿ£½÷L¢´1˜Ðœ¤Êþ°ÞÏlFÛOa°ã#A„åüWk…;grÕÜ6Ô" úÁ²zˆ7*¨Ñ9ñzÛøü›`F€`"†uÜhDš:r4¢H^zh3ùÁ;_ ­ •Å%¥UÕêuž®éêœêÀÒ¼A.p 2Ø‹÷‘Ÿãé ­«„ånÖº€ 4Dj´H\èÞâ‰Nwheè¾íy/Yþ’ʯáÃwÍY f+v‘±&&º 53÷¢\º.ºöîçèy àægê‰æÌ™ifŽìßûÆÊ·__†"Ð3OÏ?½ô>´KhÆ/É™¥zÔ+ð:©Ñ³™ò°8ŽÁLh‚¨4[‹E™?v{dƘ̢ÁÇŒ#À0Œ@ @#‰Ô£–›:u´n¹¥Ehôk>x÷˪c¥§ÌÕ3¯®H˜<&S=%k”’=fHXëÔP–Èu`ÉÌ‹´%ä€HŒÔÊH"#÷(GÈAv”i/7º§t Ï…œqÐ}©ü[ö½k¶ýMì½ óe 0ÿÔßCÖˆ…~"Ó•òwQÌKNxPõAûî¨'Zg†\3“73ràu»ê·o\÷Ò¶õk6âíÉÔŒž{zþé= ÷¡ýv<Ž×D@É—æÞ3y§ük{&4¬±jäÂJû$8‚wá¬F€`F $¡‘êÜ™Án¶˜ô¬Éùzãîü;ž~ÁÅgzÜ®³°CO5ÞlT“­ŠI¯÷õJ;¸A0§ÛvÞÛþ&¶qdgYžoû[žïÙ=΂ÿbÏç[q[²tS4É Uï€uߦà¶FœDù{¶\áß­m½´ý~Ψrq¹ÔšÚF•ÍDLÇ]8oïšÍŸ-_‰sÅŽcÞõ¸Ñ3O[ †æB3ñé=çxT÷:¯EÑ="Žcô_Ÿ'4ô Eòa꬞ù2² hJÙ{JþÎÊÆ×F€`A€ÌkhÞÍ! †YMv•K¼v{“g͇ï¬^ó!¬“5uèðqãÇYâ’Œs¼N§'Äá$ÕºáÃ?H3šª‰,Š`³¥Ûþn™ËeÃ:Ø'Oó>B¸Ý.‡Ãf«³5ÔWÉÏÛ›Ÿ³ãfMš"0µÍ[îé¹§ç¿Ãù3h Q^ÞwÏÄý?fCŸ&4D@ª`­¨×è¯+ê=Ú†À²žçcF€`F€h…@ –†S"3Rë"É™â)š¾ wçܨcHqe<<äÐgŸ¯;ÿrõzòK ã.€Í/ÿ_ÙNç? c‘G€žkÂ6ð&- ‘ÒÌ¡©i>&’#çÏPºVaÂ’ÜëÑßYt‡ýèfâÑVbðGŸ&4õa5¡kC/Õuô£×&EM±„Œ#À0QŠ€ìôÑ(µ$)D‡Î›qskp/58r4Q¦ÁK¸îVåÜì™êåx·• ®-”·Þ͋ޱNè;&åãð J2#çˆÑ3,µ3¤¡!Í ›Nµ3YKrS\^ÏŸ1žHhþ¶÷ÞIGäïXÝ÷ BCÚ‹tª7t§¨ö9:ɧC-¡ÕçŸT’²TŒ#À0Œ@T#@@éÖ‡:…ò7uid›ÐÒàžúC’Ô0™A0Ú†¤$­áÇw{o³å5· ª>[¦üeãjïAyŽ÷Ý‚€|~‰Ì=×ô ¡!C„¦©y£ç›®Óó~Bp{½‹‘wå¨Ö¬üî„H1x¢Ï"3´ OOM{ !ÕUe†Ì¨­*’8§ ¡¡¿œµ °`Œ#À0Œ@èÈN uî©ÃG?éýL: hKh˜Ø4c>ý múe×z6˜`Tó)°5BΛÿш‹‹>ÄòU•ÊýÏ<è}«ƒ~3EãYè¹¥ ŸaIj$±‘¿åu_ì6ÿ‘Ì<„õ8®ùt­F«ûe›(1ý³O©Ù b@+éN•¹…•0Ζ[ãί"ÞϨ¨@ê±(—A§q‰`0ü«ÿÊrD… ,#À0Œ#›PÇPŽ`©¡m"0´I­Œ$2r—ú_X° Î<ûÒÆç´ZõJìËP\U¡½þ™‡ÜÛä Þ÷²h/7"/r“ç:&ëéÜInÕûk_rì‡ßì½kRi‡ bðBŸ&4T´Š® ÍÍ,èàÔ1ƒ`Ýž˜Ø´r­³¢¦Ê&6m«§&eƃÙd£Ñ d&Ù© F€`F "È u)H2ãûåû-ûÝ~Ñß# úÆw‘ÈL‘…Gó¤õ ækŸy¨©\žã}"@Ϭ òù•¿;ÝÓœžÞý/TábÉ7ì½{ò¿”{:Msû¡ñ™œ¡10"#*kaOq1d¡_ˆ½–齪©!Í ‘™®‘l€!Àd6 YIfÒ.±ÉY̽W,0#À0Œ@ì R1vŠº¤O<¯»À«8ß@2“âO­(ÿLU§ßùÀ][I£Å!Æ¿$÷§¨™9“ÄFWSNhnCRHb¬Dí‹Ûç ™k)Ðëõ`F¢`µZ {Ä p»QÛ\V 3ê`¿yJ¯Ì©¡93c›rÀê­ƒ¡‰Z“žq(ÉH²’Ì$;•#À0Œ#À0Ý…ÀãÏkîQϟШÉ?_GTïxàÏK[»ë¶œo7"0aÉÞl¯Çý´¼ö'ŸÌ½7küÝ—öý„ÐhÀ€äÀ„¦\qV+8NÈ>L:ök€i _Aµ> Êô™P…ûî\§†Ö™!×ÌäÍŒèМlD²2Q3Ö8«‘d%™}žÎ˜Ðô¥—ŽËÂ0Œ#ÀD ‹_I³ºl/ IÒµr¾ Ž£¡ÁþÂûoqm‰9YŽÐ˜²ø˜Õá­XŠÚrOŽêeçHƒñûBË&fb÷yBC5A&[äÀˆíIûár¹ÀãñÀH¯’Lz8XQÚ†rA0È’Ö… ;4&ð(‘ƒG«ºÁ赃žÜ„£¢¦Å 4«™d€x$1‰‰ñ€‘”‘d%™ÙÜ,fÞ%”`F€ˆ)žxÑ4Êe/»%­æË¨ŠåêniÄ…ñ8Ä*v¨ø¨ê’53  Ó_ûéci­š>"×cbx|fg¨¥A’€ 2#G!ˆ0 ZÚhƒê;TÛÜàpÛÀ¥ÚùNÄ™+êQ#dШhÔB¼Y‹ódÈÎ,43Df’Å1™›‘¬¬‰üœ#À0Œ#À æË¸oà «¾ αxçËüò¶[y¾LT1wˆN~èU½7úWàŽ¼_MÈóÿîƒý‚ÐP½ù´4Z0¢“Õê› ESShŽ ™vÑa€Ùl‡T‡58nðx=Hh¼¸ù⢗ˆ°ª_Î!Oe$ƒVCóyP[$îgòÍ™A if„É™_;ÃΜ1Œ#À0Œ@§üñ_š{qx÷Iž/Ó)L1yqÜÓ»&¨àý»û¡/åÝ“ýšüÝW÷ý†ÐP¡ 2Aˆ†ï7‘qΊÝf{3¡!§/jìÂ#3âF÷" ™‘‘ äq¼™‘yÍë±XÌb#S3ºN²q`F€`F Rˆù2öб_sÌ“çËH$b?{ÉQóqoõRì¶ZEieO\|êÏc¿d'/A¿"4-$Fç#4øû´%°˜Í‚ÌçӉ^ÐPCƒól M¨¼†4@ä}5¨ ÒéhM$PH¢ˆÔy9 "ã[LÓ§™¡4F€`F€ˆþù2mÖ—áù2‘@7:ò¨òVÿµnÙBlZU{íÖÛ7E‡tÝ+E¿"4%é Y˜€5»t&‚A¦fN§K¸t&§¤¡A¯BC®–†î'6ô6@º7iih"SDbôBkãóhÆš™î}à9wF€`F ¿!Àóeú~O\¼û:ê½µ¥¤š;÷Þ7iwËï¾}Ôï¬N"D4hn‹ 8F˜™¡‰™ÌxÈ€ÐÐPš®JﻟoΑ-ÞŸî+Ž‘èHy(.F€`F€`"Ï—‰ŠÑGÖŸs§¹=Þã¼/(ðÆþ{'ÿ'º¥Ž¬tý–ÐŒR[Cd‚LÁHcBN¤™‘Idä>Tøéò^BS#Hßy.Ô|9>#À0Œ#À0!Àóe:B¦oÿì¾Á.‡óc$3qT2´ ʇdým}«”'/M¿&4I*ˆØP$2ò·¸æ?Ij(¹¼W˜Yq2F€`F€`:E€çËt OŸ¹H‹gÚåDf†ˆB)PƒCñ—í¿yB}Ÿ)daWZíE¤ƒÈÜ|fah&F¦bal2Ú’›vnͧF€`F€æù2[ÐȤe±L±¾ÌŒóäÅ2ÃÆ5Ú.RUC)ÉÌt’ »®.EÕ^¹ÿÞ)û¢MÖž‡54=2߃`F€`nF€çËt3ÀQ”ý›Ïä<¤õ2¿HªrGÞ}Y«ý¿ûÙš~Vá\\F€`F€è[ð|™¾UŸ'+Íø§sn÷ªê]2ž¢?áâ™/ÈßýqÏ&gý±Ö¹ÌŒ#À0Œ#Ð'hž/³çÿ.–¹×—™uÿ­®-}¢\?–ä\€“½Ÿ•'Ð À»ûîξ_þî¯{&4ýµæ¹ÜŒ#À0Œ#Óð|™˜®¾…Ÿ´$7 Wy½3 +œ—½%E›tî¥Ãæóì+ Øä¬¯Ô$—ƒ`F€`ú <_¦ßTµ(èägr†º<Þåè‹7N f¦PgÖ\¶égCmý ‰öKË„¦}\ø,#À0Œ#À0Q‡Ï—‰º*év&ÿ%'Íå‚/Ð%ó0º’™zE«ûNîÏ&ëö›ÇÈ ˜ÐÄHE±˜Œ#À0Œ#пèh}½ÉzÕ}76”÷otúfé³ÿ±+ÙÙ¤~Ž6ecE šѪ ÷Þ51§o–8¼RñšðpãTŒ#À0Œ#ÀôíÍ—ÁÅGþ‘ªÎ8ÉLUCÞhü ûâMÊ tÏœM7VÜŠFsíÞ»¦¬êQAbàf¬¡‰JbF€`F ÿ"ðÄ¿5÷©ªç \DQK(`ÇÖ«ßñÀ-ž—¶ö_`úpÉg/9j>^]ó!z4;ÕWLÅ«h5?ÜwwÖ}¸Øa MØÐqBF€`F€`º9_Æëmå’¹HÝÂûoa—Ì݇|ïæœµ4×Pu¤æt0WJ¢QÔ;̼.ó¾5lrÖþÅ0Œ#À0Œ@¯#ÐÑú28_f&¯/ÓëÕÓm,øk¾ÑUè]†dæby¢Ü»ïÞ)ÿ’¿y"1«¡),­‚܃ÅPUÛ5õMàtºDéú“#n£AIqHI´BÖè!0Ȥ‹VS¿ÁŸ×܇[ãÿÑCøý¨WT¥owD«Ñ~öÜc÷³-GaÏ·aN†€øFy=b¼á½õ:™Œ‘º®Õ4%—UNðêež^µÄáP¯Íc·> jµºýé m”¬‹îÚc¤i¨ü4kèRä= ªýˆEm¼ôœ‡»”Û(‰Ê‰û˜"4n·VoÙ‹ÛAbêq™Mg£>¦ŠrbM„q¦ºÎ…%•Po³Ã‡k·Cb¼æÍœçž2ôˆ.´F®œ¤! Üu×S©áZ üÊíugà#£”¯YS§èÀÑãÜT8Ü¿æ´üè™#7½6o‚êT­Äãñ[|´TÍ’!ú![´èÇöž‘‚ïÂ0E‹^2»Š®€÷nñÂak“«Éku7*³Ç¿QR®žÙÓt1e¦ùvÎÁNmez£ÎªÚõúFý¾Q/ü%Á÷ìŸÿ|7}£°ÏÛgBÔµQ=ì CÓ ÇISÃéÞÜFu\1ÃrÁÿ–o‚ãu0~x:\4; &ŒH³ÑÐqéúɛà û—Áö¼£ðþšm°vë>øÞE§ ­F£abÓOžƒŠILWó£ûº°ÑXÿoª@2Óý7;„áBø$Ù«Óè›NßvŸ«c¢×ÔÜFµJÛSÜFµE$Êxo¯úz7¼³j d㑟^5F9Awb©úñÂçgWÏx½¿f;’šp¹ÜàñxÐó_,ØôãJLÑiÄ‹F5õ?úõCwkµú§2õ¹Ê9–ièŽDæ}4ÂçëóÚLýn ¾BKšI áÉ`"€â#3°dtM¾æòýïh3iZ‡Ž |®È[;º¦@¡o;}ã1.ͽ¡o~¬|«¸ê¨‚OržÛ(@QËà©óMffï~±¦ŒÉ„ï/8 úþ¬e>Ép™p"¼·Öï‚]ù…‚Ô¦†IMPýç z×õßûù= ôzã‘̨³Ìo):ÅçL£ÿ@^I '‹pÃQãÅ7ÿvMLŽ•ŽBx…æTŒ@Ï# Ð»EïvÎÕóªè¼ü ¦'‹p£o<}ë1‘úöGû·ŠÛ¨`*¹“8ÜFE±††¼–ýïÓMžšWÏŸÙI5ò¥Ž ÜÒRà$… 6?©é(>Ÿï³23yòi‰ñ‰ñÏ%hËÔi¦eÑÞÀEeen š2Ðê”n½u‘9*…d¡E€Þ)­Fy!Å^s Wñ7*Œz$Ü’mUÿ}ó1 IjÂÈ­Ç’p!¨ûsEQÔÒ$7³êº&øî9SY3f ‘¦æò9S¡¶Ák¾Ý §“çÔ„‰e '£wœì©SÎó3\d:}Šñcìó¨g8uJ¸M1}¤UÑ‘‚'Eùæ•ßÐpÊÆi^F@ChŸyt­–53áÕávVÑôõ‚ßzüæc.FÜ¢yN ·QáUu»©úsu1™DyÐáôÔEÞÌxÎL»ÏlÐ' ?ÂqóžÃ`C÷ÎN$5„/›ž a,G¤N²Ó$W€f‹Õrû ôfÆsfºV¥„á¨Õ¯šñå‘ä®AÊ©ñ­¢wjX}¡Êsfºö@~„#}ó1'Ò$Sói¸êZU·›º¿¶QQGhH;s¸¸BhÈs‡®#@8649ààѲf-›=ŸuÖXÈÁ?ò5ÿŠkOS4Úèš™;ߨ9Â×§IÿÁ¿9³‹ºïhŠÈY0=‰€†Þ%z§Ð53£"€<áHß|úöcvѪ¥á6*uÝ^ý±Šª†XjgráÚ) Ö™i¯¢bå܆p¸¤ª×Å¥õzϼ#ÇÀnw€Óåb-M¯×J· @©1¥ J;O¨¸ÎL·ß¸?Ü€p$<õF9ˆÆ‘ÏþP \ƾ€øVÑ»Dï®3Ó7JÕË¥  Oúö£(&Ü¢MK#ê½Y.n£"ü¼ôÇ6* ªj Þl {™O7î†ß<» Þ]½ý„GäO/&®QçþdÁ¦YûŸ<^Gù¬Û–PÛL((ª»ÓLÔãÐ:5qˆgu}“0;s BÃnœC2¶ÈÆ‚&„š5:Ý0ƒÒ¨ò:3‘©DÂñ¤žFàÆ„&2°r.ýù­ar5yy™È<„#â©Ò·s$³³hsã,ëÛ¨ÈTy«\úcU„†ÌÍh½”š†&ˆ£…ðƒÅd€ÜƒÅ­æŠ-«›#øÉÐû Ë`ùÆœð…2%§W?Ùu8y¿»BâYßd»54èAŽp&¼9ôYèÝökh´zÝ “RK ‡!€xj@£d`v„sT}K#TDΆè |ß*|—,®~"ˆ8â©Ð·³ ÔÐD ƾz÷iŽLÜFE°â›³êomy¾ˆšàõªàÆŽ¶5F}×D‘‘ ‡Š+QCR c2Š2îÊ/flÛó޶*óÖ½Gà‹-yÐdÕw‡¥ÁÂyÓ ¬ºÞC O£Ý O¾¼Æãù+æM‡âò±¶KIE HŠƒS& ‡ÙSF‹ü –å=\Ì²Ñæç© rò=‘–wVm…ü£ Á×xÒÈÁ°àÌÉØxñ£B;óï÷׃V«ßþð¢NïÓJø ,u68NAhg›CŸE€ z‰ÈÌÀ¨U4qzÅÉ„&‚ÕMxj@‰oÆ9Z: ,!gÅôâ[Eï’ÑëâoT!'<µkfIsh¨-ˆ&ogÜFE°®Û˪¿µQQÓSÇß7‡&2¦PèÇ &ŽÊ€œüb=ï*(†É£‡øÓA]£–"Ñ85kÜpÉép¬ª¾Þ}’,0bH*$Ä›áÚù³àŒ©>Òg1Âä1CàŽ«Î¬Ñƒ‘Üä ÖÇ)ò$7ƒNWÌÇk› I‘ :$*C%Á Ÿל? vî? » Š@‹ìfÚ8ŸóƒgL÷¢4ÝGæêž y9s»ÝBC#15ŽõP§€6ÒRƒ¯wº¡Úð«%q–˜wÃ]8KF Ï" ßmó»Ôg Ú[kþöK2Cm‚ļ·D¢ûJ¸êæZèOmTÔªSir&µ]©gÛ ÙHrBñ“}×éBÎ_!¶#Ǫ É-viÁ8…ŽîÓQü`Ϊ^Afh‚Ù§Sȇ¾„U¨ÜD#†Uꥆ 5ŒïÄYî»þñê99KF JïÒü.E©˜±+Vó·_’?ÞX¢ÞüVÊÁmT7>^ý©ŠBCõ);Ø‘zËt¨]™82v¡ÙÙžƒ%pÝ…´dDë`4ê„Öå7?$ï«"®ßc‡D“³9ЀsnþðÂr‘hP ™Ò¯k‚!耯l@s66lχÒÊZøõÉ ƒçÞ]+/°ïì>'DááKZ0¤1~¬CHÎQc À#ZFäb ÁॕXŸ‚c2Œ€D€ß‰D÷í© 8Ó>B <ÜFuoH¬»÷.½œ{Ô=DÔéŽd ù.[öC£†øœæŸ52IH#К1—[x;\R)¢ JŽƒj$(dNÖ„Îh"}jZÈ̉¦[k·î÷g•žš 潬GâBñ×ï(&n2idâ,&t yO;‚&p2 @ó5Ò’,©ÞÇ…s\:ºLîÞO#Œs¸òpºnE@~Ä¢¥ë´°Š¢…ÔÌ3`Ì©¿‚ô1—t7Ruß@Dó‹ |»XFNÎt'1óiã åì‹!ãª[À E˜=½Ÿ9à‘ç?„•›÷ÂÙÓÆúÓ‘‹éÙÙ£à£u»àÑ>ÞÇ:»?!0A#úš9?ü ÆñÛ î`Iñ©‚ŠÛQ¤Œñß…™—þ 渵¼WÅ÷<5Jç< óoÛƒ[.Ìùá=ógáfÇéF —üƒ;aòß?úîñÙ§‚ÆÜb t€ˆ#~ö{ȸúVÐÆ%‚¢í>#­%†þä·0íµ 0í•õ0îÑÿ€uÜ”I‚= ýÛlΑºœ¡´Q­Ž8·K"sÕ%øº=q÷½!ŠIÍ y “Á ×Âc·Wþ“QúÅBÿo:˜7k¼Øšp!*¼NÉ(Ðb”?¿f®ÐšÐy ç2æL'ÈMè §N³& jrÚº18ȬÏ$“³¶á»s¦Â‚3²ð´$sg÷i›¶+¿#‰{Wäà´E ÇG;Çœz4Õ†úª}adàˆó ªhìYûPØy“PUÑ5|})lûäfh¨ÊƒÌI×Á¸Ù¿†’ý€­¾(˜,:ŠÓã¸w$Ÿgb{gFß÷4ì»ÿ‡`/><ŠN SOƒÂ=Uk> +`iÌè”hà`8ðÄ/Áy¼†Ýú ¾îvÈô§ÁfÑY¼ý!züþ†ÏÁïüo âðêD:ùin£NŽQoÆ8±gÝ‹ÒHs¨Þ\%·̨A 47§£ A§FMǰ¶Gfd^†6kïtv™&Ô=YšIœ™Ì„Š^Lŧ#pë’ðƒFòo„ŠC«`äÌ;Àí¨‡Âœ—q{ FŸr'dŒ» <ÎF2éjØ»öa(?ôX“ÇÀäsŸ„„“¡±æìYó ÔÛ”WæÄëàhîk0ñœß‹¸n{ 1_È8çGaǧ·‹çtâ9‹ !u’H_¼÷-8²ëeG£3Á„³ôQbzÎcsÀ‘/¡mÿ<¡ì·=ç?WY¸Vh¢tF\®¡ÅÓºÿzç­œt•¯2Œ@{4§B¡o•.0ùo@ÑËK }áÍ`HMƒêÍ_@ÉëÇ<øW¡ûÈsøÛ»z)( ýñ=tê¹ ºœP¾ü (ûð‘íÐ[û¡<Ч ó¯€C¾}ç è0ä†_ÂÀ ¯Frt£ØºäzÐ' €úÜoáØ{/Bã~߂ܦÌQBËb5œ%PüÆß¡öÛu`2†Ýö XFNGi!þçIL³«Uq\UåHfîôŸ«Ù¼ 2x·ÿwˆmGCˆ¨LµQɳ kî¨ùOj_*¯ÁöèÛ(ÀÆHs¿}ù­0þŒ1ÞH(Úý·QÑðÔt"CÇ=óNñ%F€è_hu0l žp%ì[ÿ(Ô–ïdDgˆƒòëÀe«cWÀ®•w!iÙ.À™|ÞSàBâ³åýë…&dÒÜÇÅy_^gCÖ¼'àXÁr(Ýÿ¾È£á8Î?CÂCy4VgS”|›—] å?ƒ gÿtFŸAi<þr$8ÿ…}_ýjJ¾y›|=6:ç®U¿‚‚¯Ÿñg>–ÄáíVVRút:ùû¢¡+EíL}eøÚ¥voÀ'F G ư[‚ãë—CÉ›ÿ€HDgƒÆ Õ>²ÿï¯pøÙ߉ãçãõégÁ‘¿?%Kÿ)ˆŠ1-S\Ó'$CƵ·CâÔ3 ôCSaT|ú†¸V±üM8úÒbqì¬*ÊsÿïoшsknçI›3æÿ}|"þû ¨üâ=°ù4CÃîxf~ç¿Ø†ÕBÞ†Ç¹Š‡Š M T‹ÈD ù› ’¿y1bé!%s6ÔWìÁy_N°×a£Qò58mÇAoJ‚äôP´çuü]ÅûÞFMM˜|‹È’&%ï«'`ÿÆ'¡¶l'ÔUä‚ËY N{•ÈÃílÀÆçÞñ"4Õ‰‚šÏCÏP ¿åø|û,¢ ì€¯³2ÍÖÊP‹D„èxñfÜ ¶æâvá£;"UK Ê÷n»qø$#ÀÄ5ß| å¿•«Þƒ¦{!aÚlPÝ.h:œ' BçönÇ 3΂š­ëÐí4än¦hɧŸç/°;ò¹Iäç®®„†};Å5[Ñ¿†4.5_ .¼^¿û[a’¨-Š?U˜Œ½úA°Ê‘9Ž-œ¸±Ù‚à¸k Ó5óÈñ`ÔzÑo¿xrÖH:å(ùß³§ù¸Úk£Ø–ØÌxÜ6ѾÐàÙÉÚ(ÊšØÈZ 4ÿ#n£ÚÁ:ÚNulm’²<Œ#ÐëÔ f†‘ :]ûlÉ[©í§Ì_"âÑ?7ŽtÅhqp2[æA#σQ³~Ö¤Qb~å¡ÑŦG§uå>³:OOêÓ‘HÍ‚Áã.õÄÿ–”ÑþãÀƒ¯Þ¼H4jc/…Y—½"4IUG7FácF€ˆ1šîñK쮫F­IûÂhBü¤™‚x¤œy?qÈÿqý¾àµ5ù·w0øû¿€äÙóñû£Aâä¦kÁ0 MDo<Ð"H˜< ãh`ÄωëôÏÓXæácÀY^ì?'â&Lƒá?}µH¿‡ÆüÝò4ï;@ m”­ÞW•G¾ìà.¾ÓÜFu O_dBÓãó ØEÀ‹#\Á7Χ¡ð š›µ%ã|N:¼8ǥ㠠‰Ù"¨,\›ßYˆZ”T8÷¦­":¥óºíhJ6²UrÕëÆóN8²ûØÚŸ`‚ çîÝý:š¦=©CÏ&4Á ÆqèEÀk·%™¡©.”¯|J^ûk»iTGgß(,= Ò/ÿ‘0«ßõ5 ZpdÞtMTw}­ÈÓ”1ë[æÇxš ™›‘¶¨³`1Fÿö/PŒòßðigQùZ3‘h£h0Ž‚Û™Ž·QcÓ;WØä¬wpç»2} 4íJL›4§Fk°¢ª~«Ð⌞u§03#³Ãç]fÎFË@p4­Þ £¦ßÑ*-ô±£ ÚÙÂ]ô Q¾Ö2œk3xÜb=ÒØ+iKâˆVi Ö8ÿæ 0Z1. µ@?÷¨¯l=’Ú*ÿ`˜FÀŽ“ïÑ6µ23„ËeÒ’ÔlY©hÎ?ùá‚Ù£fÜ!¼¨‘75šk#ÃÞu¿ƒ©ý f]þ?qŠæÀ”\ û6<ŠžÕÃ)—¿.LÞÜŽ:ضüœ‡sX&E—ëZ>åÇïOhb€š²8—ç 1ljF O!àAIùgo£å^ô<öØyÓyÂ#ÚðÛ†±ÿC˜yëáàâ{Å\—` _»m#z6Û “–¼fcõÂÁÐ[îIé~‡ÿö0 »åÈú¿eÂ1yJ³ÞŸþ5 GÇ“ÿþ¡ˆKžÎrw,šy_šË£KJ…¤Óæ‰Mž?´ä7P½i•üÉû (;ô9df}æþøk¨<²¾ýðFn£‚Ä.V¢õ+BC•’g†›.;ŽUÕÁªoöÂç_ï…,8-Vê‹ådz µ/û&àÓ iâ=m2x=NXñì0ùSLÊ/;°G¾̸(ƒ€õ¯ÎA­M¼øívÖ‹}Û¼ÄIü÷íûß—‡b_°åÿà º\&·Ìª×Õê­³ùíËQ³bsgˆ¸P ¹=´¾ 9,Ðãñw5žUÅ5ù&ˆnzGRѬ@‹š#«I^â=#ÀÄd*æ7Cí˶«g¶’þÀŸîjõ»èŧš'Ö«àuøÌ‰ÍÃÒ!ÑÚÚGý45“¡¦=” H’?apóq5’2%ËC±?PTæ—*,ýÜçb–N’‚R4O;ï” `Çr½öé×1 Qh}ÈŒ­£ó­2îâ‡Ó Úfâ˜U îçù8¦8¡ÁrãŠb.Õ@ç»wÄ ¦a Mx—jT½^oc@ªp¸Æ‡Œ#p"þwÆ£zš=£NÄ(ì3NAu;ÛõµïÇ=ìÌ»–ð„ûsÕ5@ÛKÝßÚ¨¨!4•a5 °¼6ðTÄŽ­f#\3¿õÄÁÀÌuZ­ÿç€D+ìÊoY誢Æ7©9Îbfg±¢¦AÌ‘‰Œ¤· ¹Ùô¢Ö6\…¦kgM ï|± ^_ñ ü懂ËÛÞù¶i»ò›æÅúµ…aWà‹ù´.»£Ú®&rg!‚5iWp) wE³ä¬~‹€×å)o2Æñ7*‚O@£Þªºê»09(‚²œ,+n£N†Pè×û[u=\Ò$ ah°ÙqΊ3ôŒ`ŠqÃÓÄ„ý ;@Um¬Û–/LІ¥§Â0œäoÔë`Í·yPQÝùGË¡´²ϧb£p$@ffdƶïH™jß¡cPVU/ÜDÎ’ütt>RE!p±1“NÃæf‘56ò¡ÎØlM•NÕª8USlHåRŽ„§Ëá(iÆ8Ê%fñ¨F€9Jìz‹âÀ5¨8t‘ð¤o?ææo ºžsDsðËÅmTDqÅö©ÿµQQ¥¡!2CyÛ´§ö.ƒé89¿·͹ôœ©‚´|„žÏÈ“Ùõ¦f-Ì5çÏ‚÷pÂÿÓ¯­Ds. |Á©hJ–7 èwÐ1À“¯|&Dãpì=| ¶ç‚ LއgN×;:©rŽä‰25Έk‰!©¡¿f¬#uÎ'êð7(™÷h~ÞŽ!#G_[æCõ;£NØXˆpD€•²¢Â (»Ä:ÖŠÁò2Ñ€€xè]JHMýeaüH[½/äŠiGúFÑ· âÅM~§h !Pn£"\#ý±ŠB#;ØØÏ†ŒÔx°˜ôØù?QB³àŒÉ@[{Hg×É<Œ6;z8#ÏeaòèÁ@[£Í)´5:Ô€P9dÜwÃÂ4ý6|é®@oh´Ùq>‹$Et½£ót-H”A§@’U jA‹sˆg‰y$îÁyD …%óälÙTpʼójê§& ¡áy4]¬®£î©ª×ã:¾æãe9˜U Þ]Ì™“3ý ÿ»CïÒèÉ“«¤ŒOABÃߨ.>ˆ£êÁWèÛY‰v÷~¼»˜}W“ÊÁmTWÑl'}l£¢Ê䌴ZÑáÖÂÄÌTÈ;r áú0ÑÚ’™@™Èš$3ç‰ÈH2Óú|û<2äÆïÊ1á—‡&oi-ʨÃo#¼9ôYhDN6bäaÃ}ôà¡ÏÊ]c”J÷ð>[èž(áG8V••¿€1áÍ`BGÀÿ­¢wª0~˜Rjz.œÂáG8Ò7OŠï?îe{-ß*½“Œ©ZSSéöÜFù«1ìƒþÚFEMVj ¨³Mï)£2ÀbÔá•;Ä<”°k¶'¤ù;ï¡ëi£VÌd` D¾t‚ÔHÌû1D}µèrô‹0jÌh2šó«å®Ä»š]öK½nµµ¶±¯érn„Ÿ×ã¬Z÷Á2"4„/á,1ô-9?F /# ßñ­¢wÊëvVmÈœëukøNÅn„ŸÇ騡o>æ!¾ÿ¸¦o•¿Þu¥vòï¬üöÖK½/ v®œ¡q×ÂNûw€Û¨pj+¹·QQCh¨êh=ò2Fo :˜5z”¯‡·Wµ¸@¯Šûgª·W}‹øÕAf¼‚žÔŒè„ÀFt!MøÎrýþ‰NŸ/5~I2ãÀcGSS}Ãöë_©ó‚ö…Ô pÂðÛ¿sçSÇ—“ÛCê,ÎÑ2êb‰8:#Ðëø¿UôNíßµó©js*¬6Ÿ¿QaT áFømß´þúæcâûû¨ùV­ÿGbò{kn{çQeãÌ Þ÷pêñ¥4ު׺ASü>Ô{ÓTn£Â¨|LÒŸÛ¨ömŸÂñ³T’‹=ù·»\tÜÊNVšœQ‡Ûd4ˆŒ¨DOa» Š>¸Ý-ãßÙ}ø"@š"3» Š!ݢ 3˜Í&0ц¸¾¤ kkrfwºp] •>€²¾ÏØE@ªó©³ãf£mû†µÛ“z&MYp­:Í´LÑ)®Ø-eIN£^ÔP¹²”âC/¬ÿôà xk•ð¥Ñe&4F Z}«èÝŠON~FŽù ŒX Î)\¥è¼ü:®¤™!2s iŒr`Ï®÷è[iÄw÷Qñ­úl±þ4×s[]CÝuØ•3SO­àqŽ/àgš˜tß*CþáG€æÌ™ifÒLÈL2ƒC, O"6„/áLx†šºÆ¶ëjœPwñ$932@IDATù8ª º£Ž6õ¨Q£ Ég³qõ{o¯À†DYS.«óP§?Ö ÐÁKÚC€ì‘ÉÌŒ43GìyÅ›¯’©áI¸¾Òä 90Œ@ˆœð­ZþúËo\tÝ :=îÆjS’zæÑµšŒFòΡ=hÎ ™™‘fæ@î®V¿ßxß7Š({õ[EÚ˜º†úëU½Ãíug ù›{4»"¯òñ7û”–®öàj*À;Iç^®x¹j¯¶O<Çm”“ž 4' ooj¬¨·9”&»½™µøœ÷ jaPCƒçã¬Vpà*YÃâ* ä•5À?—­ƒñ¸>ÌôñÃ`ˆ40£ U´Î ¹f&ofä€ÖÏjqÃÀ“À0>>¬qVqL¸¾>Og-„†ê‚ê¤y]þi_(¿ì$H 5lô²Ñ ££¯ªìر™gŸó½ ž›é Ô¡ºJš. æõï@>üÉí%yŠùöÎ>ŠjkàçÎì¦WB(¡wH•®"‚ "Ø öÆ“ìP h>i* ÏòÔÄú,(¨Ï‚€ ÒT:ˆ„@BïÒ“Ý™ùΙ0aYÒ³»Ùr.¿ef§ÜòŸìÌ=s9©–F ZùÞúe¿®D2Ä’>¶o=Ï>¢}›ž Ô‚@¹÷*|q0·÷ «vëÕwìü·F´Ì= µ;•!Zæî+YQùv¡<3𙢙Q¥¤(wãÒ%_ý³ú ÓL÷'2‰­·{ÕÂÙæ~˜£"ž´1ø-Ðþ‰ïSÿÑ4ñÁ[ƒæ½ýMíÇ 3üŒBU~F]ˆÈ• ýÁêŸ;Ò·PNŒT4‰êÛµíy½"3(rZ'Ò(X,PÚ¨*D`(ç=Y9°÷Ðq}âN †œ ‡…–%©<¯2/ÿRŒáŸsò 1if±žgÆŒ‚LC?+4 ’Ð)H`ÂÃC!, ?$Ô OâJ|íÍÍèZhxwÉ<¸ÿ¯³×ÉËéùÄð ÛtzúÓ[:ò²%›MüKÀߦ›7í¼ôšáƒ¬: 8î×> RšŸÈ×Äa%ç$^:ÁŠEóÓŠ´p’fâÍJdîÙöëªE æçççPÈÅ3øÉÁñ$®ì?ƒ¸0:(÷^…/þܶnmúe׎¸ÞÚ¦ÝЭZê÷¨K¶äŸš¢Kf­À¢QÒLºGYKŠs÷§mY¹zñÂe……ù†C÷)—ß«lµ1šõ|m ý}à%1}xß»fœ£5ÚDóP²àgAª¢ð3ªb@®hÊ„슺uÚÝ}‡ ;³>mO 4çMšÊ´48ñV1$ 3”’ MÂýüdh‘_ÙyE]h…bk!œÎ.”w|®¼¡â ¢pšbÖ ÄŸÌõüÑO&@73#Í 3áá@ëdnFQÎìµ3¯Ê3Ú© ¾OïÆõò9¦^6`ºŽdEZbH˜1~oú€ÔÖ,ÀíK:v»¤u«Ž;†„4ð 7ÉçÔ§x€§ô ò— šýµ¨§JTÿÓÆ÷ —øƒ²Z-Å%…§Î8q`gÆÖôÍwàñô ¥‰M¨Z's3Ãæì ·paL 6*¼Wá‹5åwë¦ô…ˆÆaEÑ­…50 N†ª%~¡§Š$3½`ð™‚>ÈÅŧNœ)ÌË;µGzúŽÔ¿÷áàÉô•4Æõr¯2´1¹¹9waʹ!þƒs»äÀà¨Ï=š…Z£² […×ëñÚgŽ­v…ŸQ•rs…@C°Æ‡&WúçÀ®Œ_1Éãm;dB‡–es½³¥Zü5?ЂK¤!¿2—òGÇöÀÀ"ˆ*.F ŽÐÉ ?¥ÇâļÒA{ÃNÃÿ…"•/Y"a5[:25CŸ43#ÍŒnrV¦¹0]ƒ­»‰¬#‡çž½>Æõò~ÞðÇPùô‡BÚC˜¡ëj:´=?þø`܆Ÿ \'áG×âàÒ8W=§Ür¥¸³q$ô§£¼™±S|·&Mý§‚çÄŠ>Ć&ĆÃlƒ&N$ÌФö±v!pa"pÁ½*2ÄO~¶càC üĨB«&烒Üc]¢f,9TôÙ®\ë)lÛ£ïU5dç6÷ªrµ16ƒÁ‡®‘…üî° –M¥ÓÀ,›#ÊV/¸î¸ÇëŸQe£¯ÞŠÛ\÷êu·~Žr…@C#3& 4Q I€õ¯…ókÙ®ÃÕ_ý²&ôù‡®—죗Ñ$&èThò^ú„ =ŒffE…EPtV ¡ ŠBJfSú9¾ðŸÁ…4.dFF¼(ŠE3#ó2òA Ô?djFû‰£m¡ˆhx T o–½â‡ï¾Á}ºÀ‰Kºf\¼ƒ]Kš S¡ˆñ&åôVRù“Iýè ³4f°ÿ0•”òðp52$È UôïªÝ_T,ýßæ]ê~Ú_N1¸÷(ã§P„š‚³âFûù7‚¸00îMðÉåÑÝ¢´d‹ªuAaü% Š(J˶|ž¼»pZoÐ}ÊãïUµ`W¯÷ª*µ16 )åhc*jÙuǃŒñ/•¼òUŒ ö\øU W4tèc\2Ó()(È+ÜøÇò/¥AWùlþŸÚÃøJÕ¶Æd&áú: 5´NMP` .ÌPÀ€’’Œ‚†4M³h¼Y®!mƒjºP ¡ü=¥yfH¨ÑÃ4£àG‚Li2ÍRÍ c[ˆý‘¬Ó°mãº7NŸ>A“68éZ×Íöp^÷Lƶë41§Iºý —zÀ\ÚOÎÿ£ÁܽXPPŸ¿Z~ñ_ÃÕ·ñ¶ƒýõ¿é íé¼Bù±]‡•l›þ*Ûû”!ð‘PCUúN÷/C3cœ‹›¸0&àÚ¼Ûãä–YÑââ‹*L%8ÕõCK°hëæÈKúáP1½æ÷š{U ˜÷—ß«¨©h¸Æ˜|æUˆr¶×Ûu/§/n½É  é›&4 IþÙ¼je*æÄø¿ßÿíþëû [M M¾ðºYNÜ)ä0 5djVRbÑC:“Ÿ ih4ú‡’Œ/hiˆþA‹ ÒÐ'ÒÒPŽüHˆ1ëZ›ÒˆfåifH˜Ù°}¯8¸;ã³UK¬9{]l'l¸‰‹— ›¢!¨Òï‘Ö7`¤™± @CBŒñÁUÏ+‡Ž+™Óå1Ãú©_âÏ%GýèMjâksÍÿ:zÂB÷ £ãC\l…bD¿ ZÒ‡ö?.L€ 8˜Àæb.±žøŒÝõçÞ‰$!òKejü¦âóòŠiÎâu÷ª`4îS´tú½ÊIÚ˜Š†kŒ‰–>ñŒªD9Û]zÝËiß#6¹R ¡M èM'½ñ¤OಿýEš88z2[»óš~Ry>5ts#}ÒŽBnf†&f¥ÂLiàÒÐPñ†ÆJ‚ ¢ÑjdüNŒôutJ÷Þs…|fÈÌŒ43ûwlÿjÉ7sþ‡{·ÐtxÒv—·­ÑÄx`/è>@BŒñ¡?Ã6ñü?ÜáIåç5ÊÉÞ]ä„¶1êçØo‚ ûË£¬G¿ÛŒƒxP1ØZKã7a«ŸÀÿ1&Pwâ{šÅ‘ƒ/Xë$¬MŸ—RŸû+J,êèKOîÇítòÚ{U5)÷§Ý«\ ©j¨ÆØhéõϨª`œÝïôë^Í~¸õa®hèbЄ€ÞpÒÄ™œj 3óò¿[v2óhVïCn›ýù/a]Û7×zǵÝÚ7+ËSC MÒi‚NæU¤…  †™™­fÆ—ý-²)øH€)õ72¶#g½Pž ÍLÑÌ(€jµäþ½jå'›þX¾  S3º&tmŒ·ÐÆ7qñ2ÆïÑx`Ð5'†>$À\Õ×ié±å¥O”ùÿM”&h³ièbvËœ—¤­wÿŸ:óì Œ¿uZbc|Œmgç`Ž" keŽúçÝ:ñ‰–7¡I=æg¾‰Ï2úýÑ=‰&·^}¯2Æ_É’XP1îI´4îS´4¶Ó15*.ÖÆTÕ7‡ñ‰¯{éu%fÆõuØu¯êBxÒ~W 4†††&ÐeIþp]J]»jëÎ6ïé7tøåŠÕÒ'Ý¡t# ô×"ƒE€ÙL7´ нðbÿý‚¼h -¶Åþ;í+²X´Ógò5Jš‰l„¢Xs÷el_¾fñÂ%èÇDbŒ¸õtMH{fhhèÃÅ» 7GzR¡?(Û?*ÛuýOýïiê›?¿"uÄß@!À¬Mþ~ª¼åæIÊgÇdû÷npñÔár¿™€Û ­Œväp"je±³çæ"¨•ñÓ`T÷Çöèæ¥#1~“^¯ªÆ…sØ½Ê ´1U —¯û9B»îçªô®µs7ç‹nD$i“í:iÈnŸTȆi‹ZTT ,ÿéۥ˂íã.jѪc§ŽA!aþA¡&&XáRc”W£¸°0§0/7{ÿÎŒí;S7ïÅJHCL½$ߪñ øW0®jÏ¥í÷ðXáúÐÍî*lXøû©ÿ]òš¹ÿÐ ʬͅ 00´2h^™V¦²Þxõ½ª²;bic4EIÀ¼1wb}æ©]¤2Gt­ª:øºWEȇ÷×Y Á¿.zÃß´ é‘Tˆ¤ !†„ã °!ì:Y»+íŸÝøÙ{öXã8üÊ¥ˆ9±µåKZ(éºq¢@„ÀÅ7 ´ÜúËëa·«–ܵøÃh‚M°UXúíßÁ½‡<… .¼µh‚"ö„ááu©îóĆÃ}´#PC­ŒÝÙüµ.ÊÓÆÐÁ(8ɪnÞã^2ÇpÐ3ªÎÐÄQÏjŽÊ˜X“–ÆRè7e:´]Oò‡K[ Ž¡Å1ÎÁÝ\*!`ܧ aÆð_"¾†v†44œ(°ˆ¼Ëû {&çÔ¢Wý¯ÓDÉ´FŽ@¡¦EIqÁ ßj?pø»è÷á¥)jƒ' ¬†ÏO÷ñ,he˜e-x°6¦£åS<”€CžQuhÞ~Zã±3fD¾7q¢m~‡Š¸Ò$›41Thâm|§Éi¼.ÉŽ©>ŠÁ–„Ã©ŽøAH áDõqe¸Íz%písÅ _3Ýe ñ$㥟(ÚóvêÞz혧û²’omŒ$÷;¡zgTYÓç‰3úÀu:ke³šU‘6&7?÷4é«Y­±tšñ–S¯B &Tód!¿;l‚eSé^JïÃ… ¸–€#ŸQuhdI^lU­ÓÔubø¢(Œ‰6 5¶ë$Ðxe’¿j0qä!Æ}Ë`k4dêg$ÔpCßi; <$Xçâ*&ཆO°.Yðšô$þÉ¿C£Ä`÷,œ)m>^æM£¦û2j¡$Þ§=a\µxžx°|¶¬•qí¥·ÕÆàÜÌ÷Ï/胂LrPpԃ͗šôØçÂê€#ŸQ1á=iÊA ³¸5eê¤kkˆÅð¥!ó2úPÂ,cIÂùÙ¾6ÔW‡ôëñöB‚‰ñ!ÆV¨!A’„ZÒ‡öñ] !pñ= _“ÞCaf ƒjBH·^;^ùÞ[HÄOšºó|uý`êäž2¦:F€"Ÿ ð»õ3§iè6‘Ï<2÷•~ÿÅû0%?£=I˜Áþ‚ƒž'T ­ 9´MÌ^Â&uË4CÏÇÉõÓ½ægÒ“dº¨/ÞØ icÐçïÔÆÑTím¼GoZF¾1 Æ7Œ¹v‚:š…oü+ðž19ëUÁ£Bªñ“§>…?¶7ðFö ˜£¬¥¦¦Âúy`LÀY0òÙP J#ŸQBˆ/‡OP=*ò½õÒ_*v»ÄÓ)S&ýÛY¼œ]/?OœM¸îõ³V¦î +ªµ1‘áížLÀ™Ï(‡ 4¹ô!³ÐÔa+æx¢†>5ž|¸ïL€ x8| ú(ªhôÈg4 &yJä3Ýàmìvœ`œ' 3ÆŸ?O î·Ô}eT…,2º½#­ j:{ÎÏ| _Õ—0|c„¦•냓¶¿ÑÝ/%(ĈTVÃøp&POœýŒr¸@Cœâ'MŠ‹P-Úoj¿Ð›N)HZPÍ<5õ„š›eL€ xRä3Šá_öR»'–ÃÐÿç^ÃÑ)S'/ñ–kÉÏ÷º’¬•qüõ`mŒã™rîAÀ•Ï(§4„‘;[?†I7ŸÄ˜[ÍñA«bPÔcšÐŽ`£”Ô‘ `LÀíHøbùŠ ”îÒáHêœ*LÊêü‡þ>¡´Èw—Îâ«ïÔ€ÇPRc|q„® pï¯o637{ÇÓ|fªÃ”Ÿ'Õ¡äücbò„\±ÿ÷NaŧCŒÖɤliÔc}ÛxY=L»ÂZû­oÚXÚÑHÊ>ÍùjÃÜ–‹î³ô;V¢ùQP.LÀ#ÔÇ3Êi-ñ±“gôTTåÜÖ šl4Ôv?¯3&À܉€¿(0]ün@‘Hý*Ö‚‹þ(xdS¾J¹›ê½àÃ"š#Ø‘ý”Œò½)7Ö{§\Ô~ž¸´M3’¦Š~‡ÿj›õO+J×dì:ØèôòVC2NFyL=£ïõµ¤{K¿õZ˜7ʼnì`û~(`¶·vÈÚ]|é‘“j+~ùkˆ¿{úxF•ݘ<‚w’ 0&à"‹^õ金’5¨aŽ &ñf¹Û þÄ.ŠÈ… øö•qÌefßÇpäZ˜@EX ©ˆ ogLÀç xCä3Ÿ¿ˆ  VÈWž¤úVp. ·+ü5Å¡˜«ÆÊ¾1U3â#˜€£°@ã(’\`^I`áLé1Êý` Γ"Ÿ}æ%¨ ÖÊÔ„Ö…Ç’6FSÕÌjušÞØ/ŽTf…¿3:`¦Žùt&À¼Ÿ€'E>óþ«Á#tÖÊÔž,kcjÏŽÏdŽ À#(rL€ x5eËš ׯXŒÅ®¢bÔÆ|˜ú`Á Ý\˜€ç`­Lí®!kcjÇÏbŽ&À£‰r}L€ x%_^k Zr×¢ I{ 5ýü‚zy*ÿ˜W˜åÒnó+,8™È¾2տܺ6&/÷^4)ƒ/9b/8S@.ÆRŸ+@~oØ˦ öó&ÀN€‡#å ™ðV‹gùu¶ª–ÕùÌ[¯°o‹µ25»ÞÕÑÆ ]r@PÔ—ƒÍâË5ÃËG3:`¦Nøød&À|G>óµ+î}ãe­Lõ¯)kcªÏŠdõI€šú¤Ïm3&àöâf¥Å¦‹ÛfÛQŽ|fKƒ×=‰À†ëcz€ª|ªÖÍè·‘‰–{ÎÏ|KV•\Ï6]¦XÕøÊ"•±6†ÿN˜€û`Æ}®÷„ 07#Ðiö–Û…&Ʀ린íނפ÷AÓ04«îO£ !Ýzíxå{Ûcx ¸ ÖÊT}%ª£ÁûÁ×’ßgߘªyòLÀ•X q%mn‹ 0!Ð3ùHP~îÉm²,îI{ºë_öçÈgöDø»»`­LåW†µ1•óá½LÀ°@ã W‰ûȘ€Ë tš¹õeZëŒqÝqŽ|VÞîX+SñU`mLÅlxðD,ÐxâUã>3&àTÝg¦·)%kLòEiÆeVÖE>SËt<§ãð¦ºÛ þÄ®âÊÎã}LÀ™*ÔÊHðBÏŸ2ßöU_™*µ16!›ŽTæÌ¿N®› 8ž 4ŽgÊ52&àá:ÍJýoŽ¢ïÌlc(±3Óú* > lÜ>¾ÛGÆvZrä3[¼^ŸX+s!ýêjcÀ$¿7üYËßÖÀ[˜pw,иûâþ1&àR_O ª6+8´[Ï¢¢mM­Võ>¡i÷cîM“ÄçŠYúÞ¯Zm·È¶cùÌ–¯×šjef}ÐJ•åâ åWª…¬±8¢MÖÆ8‚"×Á<ƒ 4žq¸—L€ ¸€@ÏdÍœŸ›ú·0WÓÄ(Ã\„!mçÊ >O×u=™¢•ˆ’ŸÐ¼¬yú³ÝØ›ípä3\$nâµÕÊLK«qpâ…xíú *õÐ ¬ñÐ ÇÝfu$ÀMòéL€ x.3·uÕ„²™EB“>‹ûec‚°Ðc_O»BUÕqujkš§ïv‹ýÈ9ò™=þîl5ÕÊý!íL±ZœŒ“³$¤ÿ>?ZùÌØç‰KÖÆxâUã>3Ç`Æq,¹&&À¼€@ܼ4¿´‘q%¶Cé4;u¨ð‚M7kªupWÓ3ÖÆØ_5þΘ€-Œ@Ê… 0&Àì 4èÕ¸Xð5†n¾—„ÚÂÌ(¼üHë àLIá_Bhkí…Ú?|‚u û­SÑ4팄–Xúÿg5#@Z™Â‚ÐÄl2ž©›˜¡éc¾Ä“½ægªÈÄLÓÔ±»Ï|÷Le­ ‘¤šýüF)ª:ýÕ‚b*;ÖÕûH³ð5鿹¹9Gð7ô–¦A¬mH#IbLPpØáÏ©ñ…]î2k[+ ½n{.¯3&à=XCã=×’G˜€“ tšµe·d6  4Õ4åsMHÏfŒ‹û–šíôQz¨œ­ô·ç̑Ϝ|Q¼¼úÚhel‘ÌJmXyÑ÷ÿšJÒm÷Ù¯ë¦g*t|!A}Ä~Ÿ+¿“6&/?÷>`ì½r…&¾®nÞ2 EÓÑ?𵂔>®û¥® ·Å˜€k°†Æ5œ¹&À<œE@M«k¬Ÿ‚Ðn1„ÒÖˆì’ÕŠ¦ µfPïá[䥴'g˜ÒFý|Ékæ‹íãïLÀž@Mµ2o|1=Åt¥m=ãrO€$=« Ë'óæÝ.Ûî³_Ÿøð‹oDÆ‹‹(|¿M>OH8R@íLO‘èotÃÄÑêÌŠÚ­íôÛД¼{¡¸L3Û¸õ.„šƒdú.ýÙ.;hL]fm}Ø_þ¶XÉHµji«±r›L€ 8 4ÎcË53&àE:ÏÚÂÌ H)bÔêgZÒÐ(œ³Ð`:æñ¸gÛ³q¿W6\Ž|VÞg¨K³ÿÌ‹9“}bµ$I3QÃq ꇀŸ™…ß»B5狼uB3¯ÊôÌè‹3—4ãr5°Š#•”Ü`ΠG³òªê D¢ó,2+ƒ½’DÚxµ2£1_Ôrãܸ×Ó ŸÐ¶àШ¶btAÏØÇK&À<› 4ž}ý¸÷L€ Ô$#œ³¦]'üL×o2vguºÁ‘ϪCÉ7©©Væõy͋νvâÃÖï1øF/.-Ó’Mƒ(³ÛGÆöÛ—¿¯ÕRˆZEía<`= äéhó8 }ämWTǫ̃ËÑKCƒÂÖ¬»Ëõ“o ˆ¯@–߯ȹŸÎ¡\QêeøöqÝ0ê@§ÿšf}£I¯n}—ÖØÙi—(ªòSHXÃN¶ÂKçY©_€~K6îSã\^2&àù؇Æó¯!€ 0 pÎè`ü6Ù=ÀÚ¯ºÂ u‘#Ÿ¹ðByPS5õ•¡¡•œ>Þ#ž}2ãiñ«67†›˜`]¦ ±zWöö—&<”ŸùB¼úRûˆØ68‰ÿV€v 6—îÊþßõÆñ®X’6†"•©–ÜÃäƒmž'Ì )Ü&Ôr&˜D£¦Ã'¨ • 3Ô_u¿ò´ª‰¯(r™ÑMR0(&a†¶¡Æôoü¾sÖôìM4=ë5rdZIEu8z{U¾1 `£RŠ$~yÍøcù5m?î´‹UE–ÌR†Õ¢þÙ´g×¶Æ‹TÖJ²–¸ýéî¿Q½zâÛ’ÂS&Indûûë2+õ>µ£Æq5íϘ€û`Æý® ÷ˆ 0/ Ðù-—‚s¤¤òorä3/¸ÈµB]´2öÍMO–¦á¶æÀè/Jв^Cÿ˜aø]Ò@E¦gõì?ø%sb‚:Åþ\G~w´6¦ÔG-õ-¹¥üLÚȸ …±N3SàË‚”mÏÄéÉn»ÌN½CSaÀ€¥E×ÆÈ¦kÓŸî²ÏØÆK&À¼ûÐxß5å1&PϺÌÞz¿¦ˆ¹8Ùº·ÖÐxþ5ä0&à&èÍòW¯§NU\+ ù†íãb÷WÕ5Ž|V!Ïß_­Ì´±º¡Ötð 8ºó=ÑÌ~±'1íƒàÆ ì1 ÓåâÏ™+¾úQÃPaÍm4!¡x·ý9uýîhmŒÑŸ¸ÿ¤…(…êŽp¿€ÎkŸèÓiVÚm(˜Í×}°qŒýòöyš¼åÀÖ½(Ä]aüÖJÍ=áS”ãp‚s\“ቌgºçGd_gLÀó °@ãù×GÀ˜€  ™µ@ùýa„(ß“öh\YîŒ.3SGàûö±¤… æ¥?Óuº­Ä™Òcšª½m ÷%b´§éÆw^z.ÚúÊ Ì!ÉÏ«*¹X)ŸJÑ|cìI à³Gø‡ô~áÜ“öûùÝ‘¾1g§=(4õ S6.õ‘îÙF?;ÏL}'›3Æu{¿g²fÎÏÙz@øÉ*‹$ØiæÖ—%¡ #8€Q/™ð-lræ[×›G˘€“(…Ê'hb“qç³Ýn6„™K_?ˆ 9?ÅgÓeIzǯF?‡ö˜ðqÛn ¯¾ƒÓÖäsÛ´©‹fÊ•šÛœ;–×Ü‘ie6\×xЦZ×b¤­ne}b…Šè5ÿØÛ¶BmÙþ³+zy9(¾òlæŸUqf¶ý1ô]hbTTЯ¼}uÝFÚ˜E3¥'¼*¶išò'šÁÝ&]ùl¡¼1B¤“©Çˆ Z¯k'()•9úÇÎLÙùíQh`iƺ†aàŒ¿ÑÔ¬Lƒ A“1´t<Õ¾1AX4Iû/X”ÑFså-M~âÃò¶ó6&À|‹ 4¾u½y´L€ 8‰jeBó˜çtûlƒ„™l%CË CÉöÁ(K¿lßþ¸Ÿæ÷2¾r¿É¾A½<†Û—ÒvÔä Ü£~¾ä5óÅöÇñw÷'Pæ+£i“°·&ê1þäcïr}eÐßeÒŒsoÛ‘É -C­Ý•´M÷…Á23>4‘Ó¿}Y†•£Ìì¸BÚ˜3¥Ï*ÊSßÛ^©B›¢YEC9“g Q kÒ(EU“;ÏÜ:ûÚ·vúë¹c4¡ÄÍÚª³0›Å*hP"MÛzl×ÓžŒ;ÀÚ["¼Î|“ 4¾yÝyÔL€ 8˜€¡•1ªÍVO¿Šo¯wm×í_F^ ÚgËU(±ì2Ž3–ƒ-·J¦ÐÛÑX߇BM°¬?ýöoô“àâj£•!-$D*f¹ÿ £’=i 44"ò/zÍ›ç÷èȬ<ÐäiŠ’üJrd8óú‡a f$KŸƒ$¬ÏÇ+‹Œój»´×Æ€ªÝW[mÌÀOöèùvA¿–“¦–þV´¥VPZAó% Ô¨½%…ë;¿¾½j³ÞµžÕÒl}¢Ûn¡Á–ª‚Ø5Ã_™ðA,ÐøàEç!3&à\äOƒ³ëZû³¥åYë5ÑÊØìùÑÊôEµÜè;óýŒ/Ã#K…غ';£okz6=E¾¡HÉ]j™Mžü‰}}5ùî mÌñì‚vªªþŽBͶ}A dJ m²)µ4c3þÕ9_>ˆ¡¦_Õº¼¦¨£Üé£ôP:Nr²ª•š¡Ñw.L€ 0ò°@SÞÆ˜¨€BS¾YζÕÌÿ(â{!LwQNŒžÉ»Ã»ÌÚú0½ÉNÂèhFs׌+IÇÙÞH|;¯Ð6|KÞŠö|dìç¥{¨V¦¼<ŸPxàÒŽW@ß™ 5?g㫚û åáU¨ã Ó3ô­™`òó–˜ ¾!DRÍÍH³à5éIÃ7¦.Ú£o¶KÌ “†ã¸ `Îë6kKç²}šV [e] Ùþtì&ô—‘(\3íÏ÷­¿ä×µ—ƒPŠiÙ–{h{Hhìø#ø‚Ö¹0&À*"ÀQÎ*"ÃÛ™`u € ý>£°±˜Øp"à.œ¼µ“…t_Ú³]ÿ¡jKÃËŠQ8YŠ_»cN8Ûlæù¬ð]tjm#˜UÕ=ò•Q%E1óÌ\ôB¼vµqå¥y.þ¹CµdH£ 5A¨Úí癓• Ø(„”,CÃ9•9÷‡WµÄàwi N3Ê—¡™Y&Èøƒ¤lßm‹ßãÑ·è4ËkÔEùwbg¥ÝaÓÚ-ã;ï5¶ó’ 0&Ph*£Ãû˜`µ$p6GÆc(ÈÄ‚$¯hcòûÎVcCÕ’ êaæ`r÷¤òpâ[ô÷ñµž8Íuð¥·tëµã•ïkÙ%>ÍAô¼2…'&ãäûy¬Rwú׫ÆfþŒ²MYÛ&_ý((FQ ¿D_ª®/Ä«Èצ6u‘6F±æÝwöï¨ËuP¤2@'}Y~ø³–¿/Ø_Ç f¦ŽÇ?Ý;å ùJŒ˜Bú.ýÙ®ßQµzî™%ÃuÜ2¾I~›âÓ™ða,ÐøðÅç¡3&PºÌÞz?úL5Éâ®´§»þU^O–-h*X¿b jqÑ~jòM`ê?t¹$ŠåÇÛœGÀYZ™òz]Y;޲ϕZ™Ú0s'mLEý§PÎGOæýWù?(į¬è8ÞΘ¨ hjCÏaL€ Ô‚:AƒBÉž»ží61IˆE§B‡î¡( ñ-7 (wˆ/‡OPï­¬ñÓ¦5•е~Š*:£EG¤èÓYUµ•WÝ}’$ ð!r…¢}•mÖ½Sû ðYÂüô¨“AÁU3c WE¢ —XqIŸÈ+,†ìœ|8“›ËZWžÈÑpŸ.l¡¸¥a=û°˰ý_‚ÌÁ¿zš6§"­ Ffx¾×ü£ÿ©­“¾q=j»¬Jƒ—l:Þ§8*RYmûÉç1&À\I€WÒæ¶˜`u$`ù̢߱¨ðy+FÃzµ7#0Ѐ …¥}‹Fr»¡]óFТqð3Ÿ ÆUÇ.Txú©3ypèx¶þÙ{ø8dì?¦””XPÈ N´W£0ðI39fNRÒCEVRÏ;ÜQ+S]mŒä÷9`D=ÿqóL€ Ô hê;7ʘ¨#ò™¦ŠA,=`{ÉÕZ‘$ÂBƒ”>±mäÞqm UÓ†µ«ÜÁg¡iì=’Û÷µi»•¬S¹²$I§1úV²0ÎJIwÂÁMÖ©:wÓʰ6¦N—“OfLÀ‡°@ãC›‡Ê˜€w÷âã#@˜ÿwÆéG˜ë\ [Çè¦bî<Â2á¯Í;aíÖ=h’Ùš*NžúÂÿjÛç ¯¾z:·s´È@¨TMÊB+Æz‹AÒŠ1‰c‘&«Åf«vr@÷NFŽ©”×–;ieXSÞâmL€ 0Ê °@S9Þ˘p+ñ“§Ý-4í¿Q‘¡âö!½åîZ¸UÿªÓ™Ãh–6wÉZmÇþL|‰÷S¦&>R™O 7ÅZ4«ëƒ>}ñØÎ¨å‰F¿ê´GÇHBXѯ砢ié¸`'æ©Ü…‚ЦûRSòý-…Ÿi u3êÂ6ò]í+ÃÚƒ>/™`5'ÀMÍ™ñL€ 0z!0yêU…w»´i*âoþÕžÏ×K«jô›_×Áïë¶¡fIz…š)Æñcg̈T Ô[Q`Ž6j—)ªÒ˜öáºÖ¬q¤Ö,:R „à naú2üýL`±*úÇŠÁ ¬~ÍjU!'¿²²sáø©8‘«efç¨ÅÅäÛ`Um’wXŠÉ9ÍòATቚ6ªû‚c{h¿3KUÚô;ÊÑ@|ž1μ \7`Þ@€o¸Š<&À<‚@RÒ<¿#°«­P çÑfÔ˜0¬˜YÏÿ¢ ³&©²¬jÙøÕ‚ÕÌ÷&NÌ666iZwÕªmìÚ¡¹<æÖ«E#ó†òá÷+`ãö}%þ&s»"ÅÚß$à^EÕ†jšfÆhlJ»fÑr똆Ð:&Z4i&ùl µ:>7¿v¢À™¾÷`”6½F îvZ¨bŽf¥$%nªc3åž®kcT55m·aïûƒ8R™=þΘ¨œ€w<+#ïeL€ ¸”@RÒ'G¬‡ GW4]j/ËRZGÔ4A³)©ºA3)‹$KYšJ¹^ÔH“I´}yÌ-RƒðêVáöÇí=œ¯~ºû‰bhrdX°Ú;¶Ô;®­.À¸j¹ù…ŽÂÍÖaÓöýªEQ0~œªi f! ù²®a§Yãª+Éí0&à‹X ñÅ«ÎcfLÀáIšÑ^±Z¯Eó©(´ DjM¢ÔèÈPÊÿ”ÿ¥~ÂB‚ô|/hB¥ç{‘QÛ‚³gü•À™¼B8[9yùh.U¤G³)åò‹:ȃzÇ:¼ïõY!™†½÷Í2-*m/ü¹y‡vàèItÙ`> /¿™˜x¬&ýcmLMhñ±L€ 0Ú`¦vÜø,&À˜è¾yÊX4S…!ŠÛ’& ÃÕ®íšI±m›é9`üýÌLʃ :– +7¥St64­XñªÉ1ûݤGó*icT%ï~ôŠÇcºØǾ1öDø;`L nX ©?>› 0$ŸôJKô<+ 5+0¶mŒzIçÖR\Ûð&s0¼´9+;~X¶ ý}öcÈiq¥›—®îÖ1Ù6tu´1hq˜l ¿ºfü±ü ãL€ 0&P#,ÐÔ̘€/x$qj ‹€xã¼'µ¢O\[éê~qéËX|jìûŽœ€ï—nT3ö¥? MM{íüÉe¬ñ©?,`nF€7» Ü&ÀÜ“@Âäi÷ƒïÈ&)hpï.èËÒÂц‹oÀÈlðÕ¢UjaQ‘èîÿ“håw~@4ŠTÆÚßüÛàQ3&àz,иž9·È˜€x|úôèâ-EÓÔ›:¶j¢=pÝå"*"ÔƒFÀ]uŠŒöéO@Úž#ÐÆo\ä¿0GHÚÈÉC'X6;«]®— 0&ÀÎ'ÀÍù<ø`L ŒÀØÉÓ¯PAýŸ,¤7]ÕSÜÇ»"Œ• ”WjM#ÚÁOË7¢U©±Nû¹©©ó­II#Kj]!ŸÈ˜`5&ÀM‘ñ L€ øøÄ©ý1\ï’è!æGnbjæ Ãæ1Ö’Àï€9‹Vkh–¸0Æ|ùMIIƒ¬µ¬ŠOcL€ 0pLÊå6ʇ3&ÀÜ™ÀèÄ©—É~nê?î¾keŽ\æÎWË=úÖªiD…‡ˆÍ;tÌÓEo\¹”²…raL€ 0`ƹ &À<‡Àؤ©ý4MüÚ(*,…)48Ðs:Ï=­W-7@»‡ûöî9`ðžM,ÝR¯âÆ™`>B€MÎ|äBó0™¨š@ü+¯„‹|kFȨq÷ 7……°0S55>žÀ_ü¢ì8x,70@t~31ñ˜ý~þî¾4|›ñÄÿ½ ~yþ–3æÌÕ$«Ù"dI*j\ܤ0)é¡"÷í=÷Œ ø.h|÷ÚóÈ™°#0ijŠâባFˆ–M¢ìöòW&P='ÏäÁ‹ï~§ª*¼“2mÒ“Õ;‹r¤¤e¦LXÕY±h— ´Ò4­%ªÕZb’ÜVß!¯Y0€VÉÜHh˜(„vBÕ´#B™ Án¡i;„Ó#6½ñÌ3…® ·Á˜À9•ühÏÄkL€ 0o'0fÒ”Š&–^siœ¸ùª^Þ>\Ÿ“ `€À@Er°óÞĉÙNnŽ«¯„@©æU¹C¯ ®FADW½¢ÖE V£ÂƒåðÐ`ä~~àïo?“ ¤ìc±*Pb±‚?ùE%@!»ó ‹µÙ¹JvN¾Œuêó)v(Äf4=\(kæ¯ßúÜöJºÆ»˜ph’«aLÀs $%}©IÉMëGß$™p2Ã… Ô…À£'aúÇóA’¤û’§$~Q—ºøÜÚx<é­°bKÎÿ¡&&^Uµ ò¡ti#whÙš5Š„ÆQá€BMí*·9 놓grá`f6~NÀŽÇÔ=‡³Pi£ I’—£XôÌûS&þmsJÙêãI¯6/²–tBjš‰ŸHÔø„£¶k…PÁ‚µ"\?*dqH¨æCMMÑÙô­ !¯0 4ü‡À˜€ÏH˜…âb6™àæA=¥Å«SÍ­c¢E,j€[Ç4Ä?Œó…–Êú™eˆŽ Ó?6ÇQ¦3y°}ïQHÝy°éÖ݇..±$,MÝy¿+™ý?|?iüq›sx• x-h¼öÒòÀ˜¨+(7áäBë×­mõgÕ©˜ñy¹…äqÚçATàñéÓ£K Õ ¿§îx5 ÌJ÷ö-ä¶ÍB›fÑмQ œ*j¹`·ÛüžÛ·h øqJÂQ³Ó¯[;ý£¨ªœºó¬Ü”³mÏ‘©ªµø¥øÉSÿí/‡M{;鉜 ñ&àEX ñ¢‹ÉCaL æ„ —7o©F„s$€šãã3* @fBGŽ£,#`S‡øüæ ¯¾šg™€ÂÌ3# W—6¢W\ˆkÛL®…ãó<)ÀÁÅZÒG?•‹W¥ú­Ú²k¼EÉýWüäiO¤LIœãó€×¨ñ+¯%ÁcLÀ7 HpItD¨Û3G³NÂ?ÿÁôønßåØÉÈN­G„arp—ˆ° ˜<úF¸sX_8•“$xØtw¶ýZëõ‡p²øðMk}~uNœ÷ëšêvÁ1i»Àÿ–n¸`»§o 4ëTŽŒWð)O‹£û?zÒ”!˜ å‹­›'?|úÉD;º ®ÏŽ ¸ep/¸iPOÔüÂÝèWó„Ý!ü• x<ö¡ñøKÈ`L ¶ e¹ó×AAQqm«¨õy&IÖ“ûQ‚?ŒÎßüº ‹Kôúh²:u,üãÄP®Wöì ”Gã÷uÛàÏ¿wÀ¤‡o(3Ë™ýù/@ÎÓƒzwÑëÈØ‚ƒ G—VpÝÃÜÅkÿÝ;â2½îÕ[v¡ö` †-„–£ á¶Aàïg‚ÏæÿÛ÷ÕCÆ^„f?·\Õ 0czUåJì×ÒõÛuMK·öÍË=œÚ\øçÈ+(‚ض1pϵ—ÂÑ“g`΢Õú¶Äw¾…¸vÍ(úüº6 0¹)fhøö·õðwú~˜ðàp ççe¶Ãº­{à¹G@&žÿž¿XFbÔ«ë\ ½bÛèíS½ôfúäé<øy=zÇà úõ5r9r<[ßçïWõ8/¨ ‚ 4ÖÅ«·¢ïŒøàý—Wp˜OnŽŸ4¥âû¦"Ä#·–èë »¬ì9|\KÛu8)>9ù½”„‹ëZç–˜€s °†Æ¹|¹v&Àܘ@òsÏåàÄÙzêL~½ör?NÊIˆñ7—N¬É,ê¯Í;àÖ!½¡e“(˜»d-[€2MÛvÖœ‹|cvÈ„N­›ÂœDgÎ…Ç&]ÖšE—&Ä<€Ñ¶ôñí>tþ;ÿO¬³!ÔÚ6o~¿ G*4fÒx¥í9#®¸š`FxÛ²}”ÖnÝ w]Û…9Ç 3$<}¾à/U–䥢Eô£¶mò:ÅGSd³)ð1¬¿3@IDAT±‘W³0SO{vøû‰‡O ¬§.p³LÀ)X q V®” 0O @ášqšµ2y»º¿¹¨­øø‡•ðâ{ß½Õ§·§¶ õn¸²ôìÒnÄ% û3}Ûáä&úT6e쇰@肹-èÜSgòP+‘}º¶ƒK:·Ô±ý´¤ñuã=ÙÕeHß8½î†!ºæ(m÷acW¥K8nEmÎi\~C ’}!™hÒ×£s+\AWÊHë‚Qå ·SŽ-›@SL0H9CB‚`Çcè—“y¨¹¢´éû2õ »P ëÒ¦)ì:xN ðFieîGçgJHˆÊš/*¶Âøû‡Ãà>±ºÐF;HëC‚i‹ÆÜv•ÞfÙ uX±Z:×Á— W¡Ÿ,Rå¨ëùí÷ù@IšÑ}8îÒ»‹Læ–\ê‡@™¯h¥o<ê§Ü*p8Ö÷:)WȘ€' eÈÞÃ'b‹K¬²kM`4ÝllHß®Ð5%4Á·-­PcB%$Ø__[J­C.»¨=|ûû¸‰Í(ôÁ0·¤Ý~ùE¨±°BÊÿ–é‚Á½Ã/×M¸ô“ÏþG¾:-Ñ‹&ö¶…’óÍY´v:¦'-¤Œä¤¹©n¡ ê°Ëº£ °úum{Þi€ÌÛf|¼àÜv»öÏíèŒÚ¦ÝéÉÉ¿¢C«&0ÌŒ®k©º´Ž ÔJQiæzFi tF!íOÙäíìFâôù‚UºP×Üæ\ãœÚ,wa_±N£ÉQžÇׇtëøgi¿¤EQ ´']Š¿\êÀ–è÷¯Ê&¹ö‘Bê¯ûÜ2¨kh*DÃ;˜ð’>¬â2™ ¹²„ÂhÞE¾1ö õÃ3¡—WH#¡`­ui{à@æIhÚ釡BæWh€LÈÈ˾„À±S¹ö›á÷µÛà Ö5õ‘ÛÐ?çÆZi.HÛCõ““?eG7J€¿Ÿ®‰yýÙ» ìóÌ]Æî –Qs óšÒƒÎ¨yê€I I ÚœqG…¡‰Zš•F¥ËD-ŽQh\aÁÆW]óSöåì ù=p]ô7‘i˜ýî}'á)å»eڬϑ©ßQYhCR¦NÇÂLù…¦ÆšL’J&ƒ\ê‡iK—¬Ù¦ Þ{7iâ9ufýt‡[e%ÀCqreL€ xä¤I0‹ß?+7¥_èↃ!­Cέág @¾!äûB…2„“ÿILttjÕúÏ Æ0H@8›¯;ÞÓ„|Mên°X­“¦kAh¶&Á¶=‡uMqNu—&“ · 郾){ÐïÅZvÚE[èæaе`ƒÂ i5¨PÿO¢éµŸæeTº †&+;v>±Ø_ÒšQ¶ø5hNÛ¦™~ ic(Hù GA†‚иº¡¦«ªB‰ÉänË΃zÀ€ªŽ¯h?ñß”¾_×5©ŠvHé’ø¤W.´ó«¨_Û®‰SV«&èo€‹k ÉêË7Áæþ®áO|­Ÿ)ô×ö€[cÎ'páÏùmr L€ 0·" ©ÊÛ‡Že¸aÛÞ²HYnÕA»Î\zQ]¹a`²=©»êŽîV« M†ÃÍè×b_ú óþÞÃYðòðF#•K:µ‚+{u†Oúžœù´jÚ†ô‰ƒãÙjrìë³ÿN~; Àpâ§ý”(ñFìçOÊø›_×éæq¤Íišvþܼ&¾ý F9kŽ †@梼@E%%e™¡ýüÇfÔØ4Õ›$ ûÉOè>Hä‡3¢ÿEX_õä‰Ö1Ñ0§@ч4?5-w^ÓOïÿ.Ô$¡SoÔ–õ‹63aò´Uèš5»‰”øcR’ð!¹¦c¯Íñ’¶(š"Ò÷©öuªM;|Î9Å%X±)~[»MÉÉ+…Þo"ux*)idi8Ås‡òðx•X2{üØxL€ 0j ìÙ˶îX.˦Ë0³¶DÎ÷î\H‹òÖW¿Â´GoÕÛ¾R4´ªÂ-“éiDH A{ú²RsË®ÅJ.F 0ë&q¶§SØj V@>.5-ÔgÒZÕâÔš6UéñX6b°†•›2”Ù¹¨ô»0iëãÉS'ÿRé‰>²“~cKSwnnÖ6ñ_×a°3ç¿O¥Ä¯½c[ëùWªÂLÉP·ï=ªG¬êXgìOÇH~­ñeBU¿Ýš´ýRò÷Ú±gôF˜ûN6‹—ßKJÜR“:øX&à)dOé(÷“ 0&à,ß|óÖcèà_4‹6 µ þèx/d´Íp·r M³hÒE9kúbô/ —l_Èô«ªB‚™rÙ Õ9·ªº+ÛOm’Y›}1£¿Pm„ª‡úl?ûú]ñ=2Š@7¨W¬Ô²IÀPÜyÅ÷õºrp—¾WÝúËúå }ú­8ýÆ.0h; ¯÷ì:”%ÐlR˜ä ÿy­È±š`’©eU%u×aø5—Ö¸ªC¾Ÿ„©éÏ×µÃô’ÁQ%<8H˜ñ÷aÁP„y…%±ø"clŸAW_×sÀUÅ=ï¹cÛÆŸf ¢£`s=õN æ¯Ãê½ËÜ&À˜€s$¼8íjMU!“¤1·’Â1a£;ÒP•¸¶Í0‘ä%å:¾»S}¹/¤£$¡?.ßH¡œµ5¾Œã¼±“Š0´Û†ôšÜYÅVCóÏŽƒ°bcºQ|¯(XÅ@4µ¤ ”Ÿ)å»åz’×üÍS’×»1ù+åyúB°wR™~×°K1‰m´B|j()íÿ0â …ŸöÔÀmäG¶hÕ?º_XOôu»qP Bá§±¼³$¿9•(ŸÝoL(ðS]Ž.¤݈QÑMÅñJ8d³ÄíÀѤ¹¾ú"Põ«¼úê·Ë˜p1+–îé5àê ¹y7¯MÝmj×¢± \)îRÈáÿJ|ƒÜ¥mŒòÙ]úÅý¸iœ(t4ùQÞ …šó •X¬bã¶}°:u·’—_ˆ‰6ýÐÒ©µÕÐP?dPS×ãË ƒ¾dÍVÜ;V1@ÂK‘Å ”ä•®Y(jJÞûf)úÁ½#.‡lL¾»jËN]¢|ƒ>h[0ù_]…¹Žè·IÛ2öAA©‹þÒa¾|hŒ/šc¨tJ0;ëóEp &½‚>d«þÙ ÆŒ'_2JKÈîÖ(,{TxÈy¬ñ…´  þò‹;ˆvøw™¾?3º °äá~WýqýÊß²Ñ×Áê“€sõ½õ92n› 0&P ÉS_Xš©'ú˜ìŸýù"õG4C1"pÕ¢:>ÅÇ Ð„õÉ»®A¿!©ÖfuÞˆçðYè¤> #Ü-\¼z«õÕO†'^ûR›üÞÿ”å¨IqV¹qà%УK«óÖ–—ä•~ó{0€Fÿ‹;¡pãýº·ÇÐæ§ô¨|Fß(ú^ü-ƒÎ $r1Ù d®ý/騵؎þnT*J0+ãßE x¨ %™uv¡D¼‰£®—ÃC¡|Ÿœlvv›\?p6ç{å9{\?`LÀÁÞ›ú\ÆSIoôÈ·ä¿ñËê­÷/ݰmë»HCúÆAh°ãlÜÜm®ÎM ´iÖnÒ¾þe  Xsgò´IsÝ´«õÒ­1“¦_Bù¯¦i N£&—NëGE kíÌØ—©÷ã¿óÿ,Ûà‡ŽŸ†g5(]Û—†/;W(J QHËSŒZ*5M0kÔá¬e i#‡ô‘1oWqè$…K\묶¸^&à ,и‚2·Á˜€ÇøwÒÓ§±Ó=’4cšÅª¾´dMêÝ¿¯OÃ/1à ʑÒÌAÙæ=w¸ÆÈÙ|Ù†íÊñ“9ðdhåíÑ,Ö·0œóø¢Àz5šbõ¿¸ƒL‚ƒ³JE kíÛ3ò8=u÷вðáÆ1†ÀU^¤¶Šê7ÌNÅÈ„îRHЦ"iJw\°@£Óàÿ<•›œyê•ã~3&àä4›<%ñ>“ÉÔ ½_ÏØw$r¸LùàGÿï¹ÊrÔÞ¸C!'tÊÿBùZ(x÷#0°G''Ã=Æ&Íhë~½smÆ$N½GX­é~~òu7_Õ¦?v›‰ò9S˜©l„öI^Û6k¤‡5_ø×?º™Y‰E­»KÍÇ*«§¢}•%˜¥0ñäsµó¾!0UT—£¶S¨q½HÒGÕÉõ0ú"ÀM}‘çv™ð(¥‚ͤñþRÐeâSꄳ|½= c¦} ˜°‘šª²¬KÛïÌýò0׋#}~ŽŸÊ…Ãdz«l¿¶Pþ Ê%ã Ålµ(—ûÂx+cü¤i“оèЪ‰ÿËcn–¯¹´”§í¨è|gl'#&ºžäõãÿÐóÁ$Ü:ŽdŠ–öÄkŸÃ¼%kQب]ë¶ f)íso̓T *@%8Ð_=oÉ:÷ï¯]ö{   (HYD ´®v£â³˜€ûà°Íîs-¸'L€ ¸91“§=ˆš×5Ð"úĵ7\yɉ-5h(7Å ÿºZ6‰ª²Ú¿_E%%ðØWWylMøäÇ•€Éáº+.®ÉiÕ:–òoŒ{ãkxþÁë IÃðjãÉÑx{åsÀ$‡S¦MzÅ“ÇRÛ¾Nœz±þ¬ÿÅážá—¹E!Û±†Ä>Ékaqi !#ü²íñµY¯(Á,F~Ãê„K±:v ¦}4Å3mVÊÔÉdÉ… x4ö¡ñèËÇgLÀ&½Ö$Ë‡Š¢èØ²‰zç°¾"&:ÒM—µ1gÑjÝùjK¶bä¤vaiÄåÌ¡ð°[vÔM|ç[ˆÇ7ËmÞ©‡‹=y:þø{aÁpIçÖ0þþká¢N-á›_×CAQ1ÿÍœ_VcØØ( 1<“¶Ä\¡¸l¡›Ã\‚¡h©^ɘlïÿƒæm”£ã–Á½tAƒ¶‘ŸQrò €ÞDS¡Ä…-ªe“†ðàõW@×Íõ¾ôŽk£ï¿yPO½~úr&¿ëÛé¿BxNa£Ø·MÛ?_ð¾U7Ów_ƒ™á#á+‹;–³þpÇþ9»OGÔ—ãßZƒk.ëî–ÂŒ³ÇïõŸÁßås–(M®@¦¡o'=‘ãýâ>0º` M] òùL€ x%yóæÉ¿mÙù¦¦ªvj£Žºñ )‚ú,ä0Mf:TNœÎÇ,ô›pn ТIt¦ÆÄ„øvÙ6EQ±¦>2\×°è'a÷÷éÅ%]š¿âoHß›©çå ýûÑÌí²‹:À¾±úáôi„—Õk8,98蚨WVÖmÝ£›ñŒºqÀy&F¶ù7jbrfÛ¶‘/dÌmW•å ù÷—‹uGÐʺåò}GNPà<½ì4V|i©©B¿ þœö¤>®;½XHþv¹’WˆÎ ]›<åùÔúè·ÉœA€gPå:™ðhdóû–_áäýæë\ #ú_ìo”[¢àb2˲X­ºv„"$•W(S=™‹…ÌÊaÔ¦c§r QƒÒɾU)Í“‘pë ørÑ*xùƒ wl[Ý|Ìö\£cY^c-›h §ròÐ̬ÁyÂŒí±•­«˜Ñ¾Ø¶]Y¾wh2ögU 1»§ É´ƒ¿˜äMEUAWn×¼‘ƒkçê*" àoè×5i€/0TtÑÙ²týûI/l«èxÞÎ<‘ 4žxÕ¸ÏL€ 8if–¦îüÿ‡=pýåâRÌî.…ÌÍjRüÌrÙá¤É!ÿ™.mšÂø†ëæ`Þœ[¶¿uLCxaÔ °aÛøbájˆü3n¾ªWÙ~ûÛ¨T’Tj½\b9­Œ"£E¡f‡JH`lÏÚ;é¹Càƒ3Ú6o$úvm[Ýð©6IˆFÿ;åØÉ32£øBM£}õïϧ.¼–}h|ð¢ó™¨˜Àï[2f£vaب›¸•0Sq«·‡419ù„¾.V pÎÚ„|_Èù?¯ H÷Ÿi þþ~zÅä{³ïH–î¼O¦jå•4!jÞ8RÏs3½ÿ¶vú¾”F†¢ã)\19"ÿº6 ýaò`Mên]»T^þö-îƒÇá(†Ë¥ÉùUV/¤²¶ê²Ær,;Ç„rå'u©ÇÓÏ-£Ÿ$±ôÓŸþÐ(gi¸8žÀ6 òú‹•7¾ø²²s Vf8FÖÉÂŒãYsîA€÷¸Ü &ÀÜ€@‹SoDK©'¯»â"Ñ;¶ÔaÝ ºå.Vc(æûX¼z+<ûú׺©ie¨(ª 1’ØsoÍ… o~­ =˜Rßw%f¸?˜™ OÍú²Ò„èè9;Îèù56gì‡>qçÞ¾Ó:ES£ /`¶o11)ÍcËË¿á°uÇ~2}£PÔÞ·²B”#ó…TÖV]ö¡¹‚š¬ÃÍÌÏ©ÅêR¡‡ž›’`‘创P°ûøç•›1:Þ|eë®Ú'¬ôP Név6 ¡H…ÿ—üƒõ­¯~ÅÇÐÆQzª©ûÁˉ‹œÒ(WÊÜ„@ù†×nÒ9î`LÀUž~ýõÀÂÓ…;1¤pÓçáµ/{¬V…Bƒ,_8DòÉ‘„tÁ>2W+Ähhå…´µ¿>¤Å©È÷†¢©‘HPÀyþ4ååß(.±êÝìë¯ì»¡rt˜ÝÊڬξMéû!å»e€6?ñïOüAuÎñ…cF¿8íZü+|_UTLµ®î'õêÒZ á ã¯ëé7²ûÐ1Øuà¤ï?ªì9tqjþÖàoö}h=‡Ⱥ¶Ãç3O À'\%î#`N'0yZ<: 'Oxp„þØé r>A€Âb¿ôþ÷Öü‚’-ƒ»wè3räÈs±­}‚@åƒ$ŸµßÿÙqÎÂÇ£ùYOô¡RÑDQ\Ô±…èÖ¾9D¢ù£3ËÞÃ'àïô}pôÄiíÞ—‰ð g6W­ºIsµã@¦†aÖEDh øù%e¥Oa‘NœÉÓÐ'FÁœN2.!ƪ؈ï~ôõëw¦NÞ[­†ø &àEjæaêEç¡0&Àl  Òâ¡æ"Õ¶Í¢/T]ØÈëL ¾X°JËǼ:’I<ÄÂÌ…àÎ2!3¼¹£'Ïè­‚rKú¾#·¥í>¤Gã ´bD4SkL$‹jˆŽ òñª(²ß…-œÛB¾b„™øÁåÛöQÐçKƺH‹¡~øÃJ¿Gn¿JÔ—†B¢/Áhdä[„}Ú*K"DQÃÁ©ÌŠNýüä¡à² Ý%4Ø%diuh¨ßšYãÇŸKÖtnȼÆ|†kh|æRó@™¨ˆ@|r²/vywùÆ=*:Œ·3 †ÿý¾Èჩ‰oÖèd?ø‘I¯vQ dÆî‹ûËEmm !-NdXÚ O`s1gÐæ:½• 4Þzey\L€ T›À#I3Ú[,Öw ëäÏ… Ô•ÀZL&J‘¼°žy)S'ÝY×ú|ýüǧO.*ÌøªµøÁ¨mq⃚ÕHÔZDàöp”apRƒ&}ŠÛ1Á’(Ö@=ª©@¡òŽâ¾ƒ Ã?fɼñ¤çŽØ3OšÖCRámž.ó÷7+±mšÊ]Ú4ƒ61ÑÐ4:\O2kNM¿çæêy džB-Ñ È8©dŸÉGÉIhým p3>˜òŠšÖËÇ3_'À¯ÿðø™€G§¶*mßÝ×^ ÎF÷b,L ¶6n߇ÚVjèÛ°¬‰ÜtDRÒCEµ­‹Ïs=1“§_®©êÝhÎu“¢(1Ô4ÓF+QarDH ÅÀ(ôè0üL¥š’^QÐÒ5C%è°_TR‚¡Ð‹}]HS¤dɇ’K™Ú5OÇѤì/¬z±Éäÿã»I2© .L€ Ôœ 45gÆg0&àeâ“’ƒÀzüÌ ^]L#‡öõ²Ññp\IàŸ0¢Ùr|Ñ.V«RÔД¤„W¶Ïm9–io1 [MS/ÆHãí$-ÑŸ¥J.‘hÙPQk¨!ÂÃ¥BÔºœAÓ6ŒK­š8¤I°S"CHrZJÒó*:Ÿ·3&P3,ÐԌ͘€—=iÊÊÈÐàK§?v» '#\˜@ fæãWªø’~=æZònÒ£y5®„OðIIšt$àÕP©Hö§N›ý,Zq‰F'Ó gŽW@Z w” x8~l{øäî3&àñ“¦$Pî†Ñ· „ž˜ ƒ ¨.ŠNõýÒzRCLžù—,àŒìÕ¥ÇÇ1&ÀêN€Ã“Ö!×À˜€7hÑèc|£ºÞ¯ë¬EÅœ‹Î.©+Æ@>o~õ«FÍÐ9ý­yÃA,̸‚<·Á˜8G€54çXð`>N`̤)W¡áû¯qíšÃ£#‡ O·ááWJ“Â'?¬´žÉ+RPš‰Ož’øY¥'ðN&À˜p ²hN©+eL€ x +—íí9`ˆrüÔ™Á§Îäf+¯U?2wµ(ôîœE«áÛßÖSD«ýBHC“§¼°¸Uñ)L€ 0&à,Ð8"WÁ˜€÷ظré½8˜yªÿ‘ãÙZ·ö-„Ifë\ï¹Âµ ºÊÀŠÛá½o—)˜mÞŠ¬¦5•›Ý5ëåÇ)Ï &À˜¨'lPQOà¹Y&ÀÜ›@|âÔ 8Ý ÆÜ:HnÖ(Ò½;̽sL´ëÒöÀâÕ©ÖÌg0 žXb2ɾ›4q—Ó劙`L ÚX ©6*> 0_#€¡œ‡H }-$-òšK»KÃ/ï8‘õ5 >;ÞÂâX¹iü¾~›’“[ £F&Uð¼<é;Ÿ…ÂgL€ ¸!hÜð¢p—˜pNŸe)PßÀм÷5ˆQn¼²‡Ü'®-úÖ¸O¹'Ž#`±ZaÛž£°9c?lL߯PfwY’Ó„öZòˉ¿:®%®‰ 0&ÀE€ÉŽ"Éõ0&àÕ(føž…Y¿/i¡ é« 6~f“WÛ—•»eÁ?;öCÚî#j‰Å*É’ÈÁLðßÊfñæ{I‰[|‘ 0&à©X ñÔ+ÇýfLÀåPK#â_šv‹¤ÁK(Øt ð3©ýºµ—®èÑ ÜÝdžÚ·î>þ¡µh%®p±Ëù9³ABàí¯UÑ¿ED…‡ˆ¨ˆh ‘¡A âàK, X,Vò ‹áhÖi8t<Ždeë õM–åLUQ¿²ø¡©tÙŠ¤¤AVgö™ëfL€ 0Ç`Æ1¹&À|ŒÀؤ©ýTEŒ îP5Õ¿m³hõÒ‹:HqíšAƒ°`·¡QPT nÞ +7f¨'NçbnQ„óŸôð ÂÝ…°š@\¾a;|½x-àסXK^¡Sax:4#;‰fƒ[UÙŒÂTÙ$ýýnRâæš´ÉÇ2&À˜€{`Æ=®÷‚ 0%0vÆŒHk¾z¿$´±¨µéDÃh®¢`#Ŷޭš€ÙäZ³´ìœ|Hßw¶ï= §ïSQ+!™MÒ*Eo˜‚`™µPÛÝ¡U“°§îºšJþ\·3ögÂæý¦X­êRLn9”ö$%Íó;»[*%$É"@IšT$AI‘Ù|æ¤gN«×˜`LÀ“ xþ“Ì“ésß™ð*ñIÓ;kVíIÀµ¨¹HšÌa£¶Ž‰ÖP"ÇDG}šEGBP ¿CÆN!…ObÐý™'a 1éû3µ¬S9ú½]’¤(d‘ÈÞOJÜj4˜0éÿÛ;ø(ŠôïWuÏ$á‘uUDW/t½PQ¼o]ÙõZEMÂ!*È™L°%“‘# «ïßUÝý¸Î2ñ¦ s…ËëG}ì|ùƒ˜õÞ]!ÅW²‘qÙŒQ£¶Õ‡\Ê  h8¨Ð4œïŠ=%h@,ëÙ„un^O店 !ÿ%§»ãªæÞ%4oÜÈîЦ…Ñ´q‚þˆ&Pp"?ºœƒ˜ Kɧäx÷žB±eûn±³»;vHÛ£ËCÙ%…Zà ñ¾_øæOKù½N¯lÃ"‰(—}tÇ6âΫÏ3:@ÑjHÛ’ŸÖŠ×?\ä®Ù° ^fÆ›Í[ÄÝ6aذü†t ì+ @ý BS?)…H€j$Ð/ì$ŠåÉX=ëd(!'ÃÛ« FãíPñp®7GZ/¹AÁëáBµ®dËá2¶ÒPj¥ë—ÿë(R¾±, =%º /½ïV™á*qØù§v‘×^pšhÞ´Qt•‡R»ò ÄçKV‰ÿ,þÑY»q«é3å*GÈA¹cRßþºÃ&I€H€Th/‚Ý 8´ h+Kò¸qÍÍBÕÚÎaš†!d±°}E¦(.¶d±k'uGî´¬>ÅõEë!kRË=¡üG±Ú@X‘Œ“ºt’X¹MœrÜÄKD×lØ*–®ÊCø'ôûolÇ…!‘%¤õ~OQ±Ø ÷±­ˆÒ lÙ¾ËÙ´#v¨PXyÑnuP¬~r”ø¯òƒxÓ÷îTkD^Ý®ŒµI€H€VThÖo–×E$@õ@ Éšp¸áö„«×iXÜ +þÓèŠÅŽÃyÓºŠ‡’´ºÒÏJ¸+õ>v+ a|Ý´¹ÿø×•.ë“ À¡C€ Í¡ó]óJI€H Þ ÊÌlãì1©P+iÊV†£ZºRµ„EÅÔË®))µF›h”F‘Tr;bƒ¶>¹Ýæö8‘°y²5x{½uˆ‚H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€Hà÷#005xT¿Ôàí¿_ì–Z™]Go>°{ÉÞ‘ TEÀWUÓ£#”¼KIÕG—–J`·Úê­ì`ÚÑIØÿRIàKRȯs‚©ãª’’ÆiB8ÿBUeåtÇQçJ¥†€ÁœC™¯H€H€H€*£¡vü€é·!NBaHãeôi‘âdWÉwéccÝG©ÄRÊcÝN,å'[ÁÉôÌX¶AÙ$@$@$@$@/Zhê廕ksÒSŸ/56)‘&”xt`fæøi))[ꥉJ„äd^¬$¹a%…Ä®­V§Ù[    …ÀA­Ð,ºáˆce¨øWªîpÍê%#àwŠò“0ägíâ}ñÊí&VÏ›ûã»Çb¿%1| íÍkÓñÂƒà›¶#7˜vú kJóÂÐÎqèÏ•°¶´TR|møÄ°+°h€•Õ%rþ•à÷÷zÒ±Æë`ÿ´ÌžŽëNþ6Rö¦)RŠÅ¹é):Ö¬Yæ¼ÅËJÞ 7¸æ°ý[ çMåU.Ý'§û©’~‰¤ï¤4ÎIOùÎNdÞä u,?/åNPBõð‹¸Ó§Gü¯´zx.§ÜKa™š¯„“‚6ŽBÆ<Ó0žÉ“:×+;|ܸfÛv…& ¡®D¿üRª¹þÆæCZÑKJ f #­(15ØûmÊã¤+Ê zz2´V¯«å‘mzä&'‡t:”ÆaB©r3÷êó~ôDGÉûqx,®{•2ÄŒ™éé:OoU}%¹{ÿö ;¹… ß•œ…Q[ݸ•øðË|…‹>>÷Åù®]q?·Â÷î€ÕFܻߛ¦ùч¾=€YÖ‡¾õöÇ}eßì£Fm;®)×€gó )}²ÓG} ù”I$@$@Ñ8(š/¯ëp“rÝa*Ttvx0?˜ïå¡%6ï^tMûÿ3âäØÓ_]÷ËÞu>ºLKpEÂÊIªƒp„Åe;âkî r…N/²w¾寔†1 ÏO2äöwmµê§L³F-O ¤ï*´í;Q4«DŽî¶{/ê,ɵ’÷À­­µµ^Þ¼Å+8¾KæŸý¦¹Ú¶Cw*áj×7×+Vf”H‘Òâ÷™Ÿ†œP²ëºï²Æ '(Í1P½J)÷\(+¹†iÞÙVœU¦PyrJÊ¡-áþ DŠ‘ ¿s Õ ([¯°;zº5|½. eæuÐ/”>ãZÜln($ íq_BÖ¥>³U–­¶þÑGEMüM†8Ât‹Äž–Ê ßßÊ:f†5j¥e)cm(ã´ak7_Œz¥Ê’{³r6ΣûW„”Y >ßœ"'t”T(=Mrƒ©ãuô¡Òï $¯äïƒí ÜùJÉ@Q¢2S ç݉ÍsC»‡üwA6áäð3UR üláܱmñöcòCÊÉ =.xºW¯ìRûµë7:x#¾×›ñèžÅ}7”þÅBÆMÎ ??û%4ÊJ»›,Šw¶‹Ü¸=òST¡BS7ü° ’Êý ÙThª`Äd  Ø8¨š¯nêp¬"ϹŽsa4è”M1ÆîçË»¾¸¦½Õã­uãa™ÀЬvtÍú[Ý1WÝÂn7¨ À'çZC7—I’ªk|ÛíŸ|à"¦--¶ë\àó§Ì°R—–{JÊyÅ!wÎûâó´î ìà ÍÐñã›ìØ^ôg(F7”–/¿SêA!'f¦ú¢4c<˜¶J©;¼‚°ˆèÿ¤œ`JX@ž…r¤â„´c¥i\“3&õm¯^¥{©üfßmÞ 6ú—¾cGხ:å×Ãêr®„µÇðùŽÍ¶Fý¬e$§ƒ®R  ¬;ÝøÊ#¹h²5x»ÎǶ -ü lû¯\ëdiKÍ(qo¸Ž¸ Çs²&µÌå÷0ýò>œÃ`€k2ÄÌ™é©OèslS“¡Ì@Ù Àj5±OŸ>N8µÂwN+ý÷ÀÖùî<°=3#0&2ïP>~{¼y›Ú5Eë¨8(q¾ßì‚/<0w‚ÿo— }U½ …p<ïûv|ï“aAœÇ²©+Ük…*þ:)yGn0Š27ƒ&luSN0-…4H€H€HàP%pÐ, °èÚŽç»Eîט™J™)÷…+Õ£âq_^ÓþµÕ·tnT./Š¸ÜœîØêUX'&úq‰aŠXyL¯œ±É9ž2£圌YçÓIù.¢Æåâ=(CÝuZc_Ó0<ª¿<[Ÿï„2ƒ¼Mp1›¯Ï#·A™™m XµRR~™ëG™üÁÖÄÃP¦”¸k¡P¼¡?Px^Ç`b•v+Ù¤ØÚÑ8·ÌmÌK®dŸç)3:o°aù°mR®l¦Ï!·¬®m?áµ÷¿:Ï0Œ.z_Ù†zs¡ôÖyÒu¡ÀɹàòX\¯-6{ì‚‹0Ð]—m¥.I²&޾·ƒÄy‘²`z kó—þtÔÞôòßÁÞtXŽö¸¯Cf>\µ•ëß ÔÊwÆ“„ëê{0:e&‚ØŸh;ö§ÿz̼5"9ªÃÄÑW⻸î‹—Î ¦ÔÊ bÔþÇÁ½1OÏŒ$+§qTÂöB:M1í`ÿšy}$@$@Õ8(,4_^Ûîl¥œ¹¬×iƒúׯßSüꢤ3®ë‘ûe8V£z|¥¹R,@ÌÇ•‘e³Ç”R”¹†ér Û´W´)a¡áïE[-û1lj»På3¨ƒÂðLÅ:Zž*6ýè Ãtóõ¹·AS)Ä€4¼ [ÇÁƒÇxÍ0ÄÒ’Ôðß©~eæEœo°¬^vÄy¥‡[®­} I M\ê¦zyèÞ&»F|u.*ï"Þæù°»™¼ V§Û.îvüÇó¾]î[ïdƒ+è ì]-¨±0í|Œ~ÁS[zÊ6eª"íhg”²Ô¿¯0ÆîÝÁãq “’Ò2nËMOý‡—w¨îßo>‰ûp`¯?V²ÞyÌt¯îDíÂ'5 –²Y3ÒSîÓ¾¯m¦ mJ’öÆ»gö~MqfÚ5³º˜±Á'6Ú½eÏ7²™ï,µÛž†{ëjÜ[ÚMѳö…» ïÅ<'#÷ÔuHÀ¤€XåêEXFžÖ´ÂŽÉ„¹‘q[¥î’ŸSL˜9&0G—ó¶ƒ5M_ÜUG ¿ïæ\kä¯ÞõrO$@$@±$€q`ÃÞ¾½±][×U¯aF¹NÊL…+DÞZwÓ ññÿ…fÑ9ÉÊýŸÃ+Õ‹Ì B.évÌX>!÷±¤´±©ñfã_Šì]÷b q–áç{ÅsMùñ Û‹¤šŠú¼éÕ^^ä^[mtÜ a`ñ•ètøÇrí¦« ì%—+§ä8 NÄêD?š¾Ã^±EA‚r‹ÎŒsÔÒi_"ËÖõ¸“/ðA^(cQÈVc²ôö¾À¿×Ùã•2tZvzê,-K 1LK;¶EîÈ‘;tÚ¤!C [³#È‘8}­t0‰q–¡ÝúG¥cdcÿÞA¢”S çaÌ/QG´~K¬Ý|\¥úCîoU4-·¦MÏ¢ƒKOrç °¦‰ŸÝ5Õ9ØòçOlÔ©È.œTŸ×…sÜŸE¸V)-­T¹•ĉ¶Á ¸ÊÙ})ŒÕ¸f»ßÄ,þJ±;ô+EeÊnLX`TWÃ,YÉL—oïë0z[G|=ŸEõ@î>¹X>z’øuÓ\¿V–ÞÁ" i^;ÑîÕmáî¤vØö¶§¢­s0•+ =‚ïcŸÁ|]¯ÏÔiÿz,½ÆxšñC‡Â# …+[Tݦ‹<µUçWguÌä ^§F7ÃþiYg@‰ƒûô9L¼ ë¡^ ¾•aŒzQ*ç>}Vˆ”¼÷quÏìAƒ†ß­.`ñ*,Ñ×ÏH€H€~K ÚB£¤ó1Ù0¨ëùõ N=íõµßT×Ó£ªË×yf¾º²2OZìDúÍI99~±nW‹™‘«¢ETÀÒÃ8ÕŸr[E¹± nµ¬gûîÛO²†„}H{Í«XºâWXÞýÖ¸Ž*Î)Š|ù','GYý©v«ªb‰ŽŒ¬˜›šºç7èk”ë¶uhÞÄÜ¢ðÊ”.*pYÿ¬¬VÍ ÃöÒñ˜‰8ÖŸ²Í²úâ$¼à@YbéÁÌ`j³ôÒË­B©›<«ŽW®"+/½âu”ZtÎöò¥ý‡“Z¶,íø[¬ž)L<ž/TÇ´ÔÒ¸ör®˜åê(ÙJç¿tZuqfX><Ú˜1a*sM¹6*œÀJ:ÂVöHL¤`ðþ®ßolÙÎÿA¹.+铿 ”ù«ð Ú±=ÿZäl˜‘X¬\¯ÆOWÙsP&$ò Ä Çå¸Ögñ Ç{œ^ÊÖF^I€H€H Ö¬B£¬‹|‹-½1–€ð^ mý©V¡©öKÑ{—x®£ÐÒ¿üW»MµFD.PmÙºf–^c•nD‘«¥Õ¥­'RS7‘Z‡l݂Юë ÈÇÇ dŸ9BÂQ— -ü¥†6^Àjx¡˜O(½—ËŠ÷KË<ÏUΩ ¹ÓKÔqfxïÔlÄ™=VŽ3wë<3kJiÌXà ¯üþì±dôz±¼Ÿ(_£_»}—sd½âÉ›‘>êKXp¾Ûãìù3Òôê|•.àᕯiX˜¥P.÷!mº•ZùïŽAûÆ íÞš¿7ͧcФ›†cÐàž©cк}­¢Ï^ Ú¼>Õ.MNäK:ä·Ã»¨^ÆäE¯Ú¸zzmrO$@$@ûK Á*4‹ýp:þ³¯t¶~aT¬‡Å…ÓxN+ÜïÄúÚŠÜ~¦ž¯®ŽþNóBkï̳×|”˜–5¨“yì·y +ÉÝ¡šæ±\+åOFuqfPBà:Z÷˜1Èù« cYú6‹ÿ¶Ý¶ Ö¬ìa  w–ŠŒOD¼Ûéñ>ß}^÷gßÐbÐZ6ó'nÛZwWí®[aÙúý!À:$@$@$¸x7Ì ³ŒŒuÏ1(‰y±¾Ê'è ¨˜ßïˆy«± m•ñù[‰8¯¯¥²ÿ¹6´|Úeoq•›arÜ<˙ઋ3«¯˜1¸‡eâ7ç”<ûã-Åvñ ¼ðv+ÜÞ¦Td‹ú^‚ –v—ûàIkÄšŠùµ9oh1hÚ嫬iëT2èÐ{n$@$@$ð›À„bÃÜ]Ýöa 2&ĺ÷=_蓯¼¢Ý:¸‘ÀAMàíÇärX=‹åER>uåp7±6m”¼<µÙžð’͵©X¡le1cŠÔxªûÒûÄ#·•Æ¢íS^[pÖ†>YcHãþœ`Êì} ìgBIœÝ¾1hž8/­4ŽÎK®õ¾$-eŸ´Z b  ø 4\…æÚöCá7?>Ö¬¨ÐÄš0å(Þ'W`’ Klû#Ÿ¾z„['W¬Øö¯nÒÒ Ê#ÚžÆ8’º±dm  ˆ–€/Ú‚Z9¸lÁÀ!¦´½´ÎÄ1…@#²š˜*4x/Ë–è’ë­+ÉiIxè‰øMº±5WR™©7´D$@$@5h¸14Â- ®ñ*÷»€ü ÚØïα" Ô+ Äc~¿ã}A1o£^¡D+ ï£QÂØ&}柰ÚÇÑVc9   ºh°šøFm¾,(ؼ+35®;†ª$¨…UåTL×¾ó{‹ûÞ–>ß=õ¥Ìèû>Éʉ*vî!kRËú¼–ÚÈ>n\³êÚ4eJ¼~Ž«+Ã<   š 4ØE¼KëöFÞ꯮n×Ç•òm(q^úþî1øú¤õáE]õ–«¥PÿÑ/ýÃËí^µó¯1HUjÝ€"a,*](`#âV6 ‘ßt½³³Þ2¾©ä%x#DRZðW¨¶^›JªøN¾óGZ#{ÙBŒ‰ô«¤adcÅW4+ÕnñÓüo—Ÿ' –—ªåäŒI [˜†Žß$$œ/ŠC¡žhãE¯o¯öؽqÝoº æjŒ=…²åXûzäÏóÊpè¸j¨3éñæiõåB)¥1ðʇCŸÕ† ¶<:®zÊï—L·R¿ñêâY»Ö^vÎ÷àø{¼ïå¾Ê–HF<Û%xOÕŒ=vþ&”‹ÇýýXFòäDî«j+²L4òSƒ+üþ^EvñÕ°ºÞ¿mWñ¤ù…!'ÍLO-çÊ×/ìT¸qÇ’ùv »ï3Ù6I€H€H€ª'Ðà-4úòN{Ã<%äPöT¹Õç¢þG-Z6¿êè¿ÿ\X}ɽ¹J¹wâq MKí|ûXX:¼¿dÅeº„2Œ÷a¹+qtÆ•‰Œáv¶jº5|½Ûù°%ÈîÐ/žØ/ypÅ ñŒ—ˤ*¹Ð²´2S²a0v±pÝÇÅ.{žþ@Ùhê²curXÓš&¥Ÿ„õÑÎE³Áç ¬üžÌȽtå%8¿n¯|uÞIÒ(² Zo|æ}±¾ŽWíB¼r¸“[9Pð3\åÞŠ ¾‹¬·È `í e¿ûÌ`Z×[bZÆ=øøôkI ÿ’ Ô…ÀA£Ðh§¾µþû3]xâQ’pº< 06f|gÅ)yêÿÜ8¢Gní–‹ ÜÛ©2[»²L ¦­ÒŸæÍã13,.Ö¾öDæÉpG‹÷ùZ޼ô䮯[Vß½–ðï3ÛL›jÈ«®¯¸½ëD/n@[_tA¸NUr¤:JÚ"]&)#C»¸]îµá cbfŽöÎ¥RoãüÆ©Á£tše)CÇÕxùܺ®æN '`>j´€âÞ_K`J»–þxÅûÃý!§Õ@{X—vÂr›ì¨”Q¦À_ö(.G”+‚Lt„E¦¬\8_ª5…¢ sŲU·µ·dMòô;¨´²%›øfï­Ur”dež åȶ†mÔ):Ž WÖWqx&ê`üLE`<'  ý Ðàch*^³|åi3õçëkÛŸé(Õƒõîˆèˆ„ ÆN 0~2 ó3XHþÕã­<ĵìç¦Ô݆áëY{°aùð›ÿg“ÿWÓocFù8ÛÞöÍû‹·ùà¶ ŸQp™ùhmè“dÈý@¬Þ´')D×Ä ˜•~$R–wl45önçYÚô+\ÈòBöÖÖy¾]ç` •W•Xt¦;®;²“E戕ÌE_ÂÞfþš°C¯£?g ÁÊ>J dL õ-ä¯Ì e)m5yO—ÔàßC™ÀUh7ªsÇ·f«ÍW@‰8ÏSW¤µÂ-…ÇHl€ñ½¡Œ.?úÆË>ú¬VGß?œJ47LUf- ùòU¨pŸ ÃUÍáZVN7¦­œÂ‘û¥¤×$o­ûé¥à°hƨQÛ"/LýmÜ™cJ5ØK/´‹s„ô ™™œÂ3ç%sO$@$@$PBÉâ´·Ösý‰ÉËÌ©• Æêa}á #“Ó‚Ë`F9kj0°J—KNËÂêg7ètí£?ïÛåËaIÉÎIMÕ/ ÕŸ²­t tƒe=›'¶4Í}äá-zÊÇ„ªäd§¦|§ ²¦4Òz œ]®5RÏ^Ÿ>03³õ´””-º!,é<}žÐÿÑ mÜÍ·å`À¥Ó¹‘€Gàòa´’0§ôã%—î1mÝFˆW*¤×ß)ì5[a~)[9Ì¡æÐœÊ)áÖ ¹U¹¢¬\IT3eª}ËFÓ½šä¹êv)̰»Y¤¸¢;ž‚‘éí쌴tzRZÆ}°:ý;fä"|d¿@†T0'ëß ýR/ Ò2)ml7}¾Þ͸.gK´¥3²œÎ«j‹,W<¸^‰‹÷"ããð¼ÄdEçŽþÔäHùPn®‚U·™÷ÁsŸk(y,ºTf"Añ˜H€H€jI€šZ«Mñé) Q^*n»0¤bbmÏa©Ññ?u–SÛvYž~Ù飾†²0'Ï^¶î–ÅèÃÒø6ÍÃq^¡|÷2X?2‘vT¸oÒ¼K¸ö,”Û…%œ7ÖG§ïS.\xß?û”«B&)nG€ÚdO‚^Imw(? JÎ&X…Ö¢ýÒ,#nzå¸'    8D èwºx+þy´[^l[¶4²—^qµ¾ªÊyå½}Uå*ÊóÊsO$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$S2¦ÒácsZµF~Ge¨8Ó5wöèröê^½>²î±$РXJ³'.é2E+Ó5œx_ÜÆ¯8nSƒºv–H€H€Hà !pÐ*4Y3}»Ê½]*Õ[ qdä7†‹á|‘òÍF"á¹ÁÉ{ÖEæLÇSƒG9BœŸxá`º®úº–Vf×b×í6sL`N}É<åüiÊŠæÛŠ‹îʽÏϹx¦W¸ÎMRФ/u;òä7^é#qÛý>›e}è[oÜW6ñÍž1jÔ6¯IÖ„Ã »àFaÈÍ3M}]J‰Ëàv ð¾O·©VîÈ‘;ôþ²$@$@¿=ßoßdl[Ìœéë©\g²ë:§ë–*± ͬs{N(°2sŒœFqÍÜwûöÚö.)5x—’ª®'•,Ànµ!Õ[ÙÁ´j+«¶å“Á—0€ü:'˜:®ªº!iœ&„3ùTh*ä8ê\(½CE…¦>'Íú>ÎùÕº­¨`8²[è"•=SHn£”¸E uËâ_¿[uüã‹G,{¸û+º|m·AS¦ÄoÜ}5&$®ÅCÕM(ù4Ä—¢‰ïÿE3 ÝÝdQ¼³]äÆí‘Ÿ¢í°B“œ–q‡*œèHù&T-ìäkµíW,ʲ¦4/²w,󉸋§Gü/m4t™Þ÷) Ý…¸*4 ý eÿI€H ŒÈü]D*e™¹Æ8¡œè@X™‰¦#œÅcö@AñŽo3süЦN¹2†83ÖGÒxé‹„'»J¾“H[®\ N0¿üfÇ@ôo&2Ù öH¤gþf ²¡¨ t›´ø˜Ð¯Îg®R¨Vf¢© ÅæhåŠY]'|÷â9y-95Š(ܰ3ÛÎBÊå†03¡Ôl€¼ë:¶+ª±r Ý ƒÌ£3ƒûr3ÉUûÍ“[‹A»ÑèL»©ÊûͯEƒ˜`=ÖÞ¶CXó@õÂi+<[_⹚;&õí’üêÛ¬ìÃ=¿.¨¯ÁÕS΄VÚÖÚf ÓjtXÂüÝ[ö,6dÜU9Áá+t;ýÒ²NƒËìxj%ÂÀ³}ú”Å9Uõ›<:ãÇ =-Á.Ü?iÊ$áº3|>ã¯Ó­Ôot[úwÉvÝg SÈ“ú¾Nëgeœì„ÔœVÍý=1bW¸ï!¥Ý_OÇ5å£s›øš l »úVõÛƒò¿jyÞ†ïG&2²qîë}JפÈëðÊpO$@$phhð.gY9æ-Øö«¯¯ î2M\Ç~yâ¬Îê ó2]× +Kd¨ÂQWbps¯‘ ¯ÕéEöη Tt“†(üÆÙŠ-vmµPªO·Fýˆ±Ù®BÛ¾³DFÉ_G¹÷¢Î’\+y”„¼·öòç-^Àñ]Ê0ûø}ñ'a,ô)Fwå\ßÂ%R¤4‰óÅàû®ë¾?È×YËA›P„ÔUJ9Ó÷ÿLŸïm}g…2^;{Ë¡-áÞ‹>¤˜ ²;ÒWAÙz] ½²Pf^÷Óg\ë÷ËË0iÚ㾤ó}f«,\ bWäœ&þ&gÆûZô–†ï XÎïoe£ËèÙu(3÷èë4Ön¾X§•lî͸¶ÿéc(F÷C™IGçŸx‚0Dî‰Ô°ÒSZZ÷¡²ï ,fd´ƒ23}|çPUf4ǶŸÇ½Ú)’M]ŽñL]²xõw)ÑÈÐ |×/KGM׃éÊê H eæ(óð,(L1÷̈~£3+–k×üsékt“NWÒ¸Sßg—œØµÒÉŠP¨“Êôûã.”å)R3}†±L×®Í}ï1%åBÜ›´ oËs?½×xœëkýé8ø V]|ÒÓùý¬¬?8ÊþŠÇ(^WÄIu:þ„΋®ºdÉV*»«²X¸Ô뢱ìÐìô§£qîB<#;[\îÉÀoÈ_qM\W…/î"¿';´2£¯Ë)\—üÁ'ýçá¢ñ;Óe½û]­ èòÑüöè²ae¿›-›û¢2£Éq# hКœœŽ·Èu“1øªïí¸Âíyà tL4‚aÅhÖßÊèŽPã¶p»A“ àèɹÖÐÍeõ¥êß¶Eû'x P2£é\àó§Ì°R—–{±7ç‡ÜQ8×ާ¥paŸ¥ó‡ŽßdÇö¢?c°sƒ>ßgSêA!'f¦ú¢4o<+m1¸Ã+ ‹H#‡I9Á”Ù: yþÉ¥ŠS¸¤+MãÌ´†g¦½ºûì¥ò›M|·y+I¡é;v>躡SPv=ftÏÅ å"Ãç;6Ûõ³®ó b2$Òn ü eŠ‘\äÍÒâx'ZøAÙ¶Ì®\ëdiKÍ(>o¸ŽÐÓ¹Y“Zæ‡ò{˜~yÎaHÂ5bæÌôÔðÀIS1èie+«ÕIJAO…ï@×õ6¸¶.Èwçí왨¾w¯îÁ´?~üw}p?÷®ïkR®Lùã„¥Ïÿo艿Ô$»ƒ¯óÈ<{ Â^Ô›¸~Æ‚Ov0»>cY}ô½"°ÞúŒk—À:,•5÷p/¥\=±‘[šÞéç g­øXHSíÒ÷ÙäÈåO…å`ÁtkøúÒä2W·¨Û¬p%edü]íéú÷¡ì9wÔ]¾¿¤'%ô3ÙÇv‡`¡µ°PÞ‘¾JG݇ˆŠúÊÃøœ`Ú³¥ÉoâÙ› %ólœ?¥ÓªûM˜ùÀY°œä#¦IE<£¿+ïByÑÏè¸R—¾›ðÛ“&•›…g.¹ä™S°ºÊé6Üó°Â‚)3ƒ©÷ëslÿ»?¾¬H‰•°H]‰ówtbe¿=`¯óLÛTIi™Oà·õtÑÔß[+J: 4h…f³±¾/”‡Ž1ù•zhüóíö· p¨~ÃLñ鎭^ÅÿÆ;0Ëø3Ü.êea1àŠ¨(çxÊŒNt”s2Ü86N$å; tÊÊaðñþãÏ|6ö5}!ßÞ=¡¿<{†øl'”üg¾)'=0.(euôÁ ÌÌ6…ùN+ *>*—!äw&„“[ÛÊï%ãZ j.Љ˜@¿ºW­²zRlÅ,l™ÛXYú¾yž2£³& –àáM¼6Óç`Ñ Ï×¶Ÿ@{: _—òéî†Ñ§?…+üA½¹Ž½‘Ĺ”m,«—]vVÅúY®­}ŠI íL…"3Ù0Ë\#þË}ÊïM@ü†ì¥g~1NÂ̯œsq÷ãâZ|ë¬s@¢7”ÈwuñƘ·…ꄞ½÷D(S…-aP„Â,uzÅï ¬¬’Ý1þ çLJ˸ÍK?ÔöÇOü®ÓÝbuÝ®·ëe £•¯•åÜ`êã¦/á |÷ǯs3{…ëJ¡gì±”óÞ{“ÃqDõ¼n9l=ĉÞ+©5×rg¤ú²£?õ8eŠÑPîÞ¾«xebZÖ™:í¨Ú¬ì“¦xšüí:vÆÝãh—¬PÆÿ–»Ï¥Ÿ™ÊŸ«(ûPQ¤éøÊÉþþðV‹ß¯Jxé)]?dzYT´y×¹p3Õ-¯i«Œò ¸ˆÞ¸Öùñü>^Ò­Kø:qŒçÔ-÷Œ†Aê–=£H«ú·GŠð=,tBÎÄrá À!O ò?’#cf“vÂÝ£ƒfc¶!ˆÿ*ÿ¿X4€øøÿ£s’•yB®•òƒ×ܤ.ÅÚe爯yZ¹îl¸g%îöÊFîŸ|dø:¸Y([^ˆôE^F:H>¼iW¸´mÇ€©mΘ€çžå­÷=…K1۸ϛ¼àá}ÁªH +c^^ãÚ,ؽ5¿Ù:;x³“oÔÂ>}¤ ëÏ›Žp¯€ªx aKŠVú0Sÿ#¬1—¢þ<ˆEº†§¢P‡Ö«¼´ªöà±$'=mXâèàg0=—le~“c¥,­ªüÁš/}¿ÇnÃÂÎ ©XP›Ff<2tÍ­°€je ‹Pˆÿáž¾È >¯,]¶ÔRÚ¿²z¥ 9È›ËÊÓ‡Åñ_êÒfã¼·óÜÕz÷ÓKàny¬¨OUÖ¶NÃ=»\+ì•å×¥•ÉÓiÑü&`Ñ»‚Ò!´ò>óñyúuž¡¾á6 ¼ßÇu'Iåì@ú<ÏÝùÿų|}d?î·Æu, Ÿäš%±p‘y•ûŒAýj[Áõ5,;Cá:¡ÒrL$ 8ä4X…FªÂ3ñfŒ7U2;ƒV.évÌ’yß.ÿP„ÜÇ’ÒÆ¦Æ›)²wÝ‹AÛYpG9ßk2çÑ” ¨l/’j*;ó¦gV{y‘{mµÑ0¤Ÿ4:ã+ÑéðåÚMWÁÏ=¹\9%ÇaÀô VûÑôöŠ- ”[tfœ£–NËüY¶®Ç|òB‹B¶z«¥·÷þ½Î¬”¡Ó²ÓSgiùPNb—–4vl 雷“† )À€e!fôG¢Èk¥ƒLx¯b6øqT:F6öÏ+럔S çaøú/QG´~K¬Ý|Uý!wŠ·*ZYÙjfŽ Ì—ž*äÎ`M;1>ú!‡Ì†û«âgb{½JêgjAUèÅ)Šmç,å‹û·ŽAƒ"4Ü"ï‚E®£ôïéz~áŸd«ÐÝú]O0›äȶ-òŠ6í>Õ"nFzʪd×”ÞotÆåZ6ú·¾ÿÂñj; Ûb ¿³®mjk'úúœëè7qF\ãòûÈ~™Òœf»Îçˆ J‹o,³ÍâF¡"‘‚v9Õuƒmõ¿ MýŸ‹]v#<Ãaµ¶ÊúkHG3 ?=µ5Œãæç9ËÛãícJclYYŸùø­ÈÕÔ×('_¶-ÆŠr°­Ú¼YV®†ƒ'RS7$¦eÞ WÔ¹ˆCü¼.ßw M1›H€H h°.gpw8*æœUx)â˜4£g.ãýÍoÀü!åÚŸ…v®‡22±tG »—ÁÍ ãÌ«°êfŒ«Þ°êO `ßb0ÿžX½q»rDÐ4°BQĆeN±¬1±-“BöÖ-ˆ?Ù„ÁA¶c˜‡E«—ð"ÒX^‡¾ï‚Ê{X>·ÀUÅË¥ÊfÇͦæ›psZ)v‡~Å ¯LYÃù.¯®p%šíu¦½¯Ã< ž:B úÌS~t–­ž 7š\Ì$O¿nÚë×ÊÒ;X„!Í«í^Ñv¾“¶½­ÊYôhe5¼rê±î3Öd®ö¹µm¬2(°\8îK(èÅðQ‚+ <«^åYÍôâX:øJÜë7)ùSц»`ÅœûꬺôÊòmù[óWâ>\‹xµPðò±ŠÚp-³®mÆùÌg ,^ QoV÷n*íö†¥ÐÿЉä¢|w-bè6ض ÙÕµU±©é7A?kðé‡gøU|'k0arµ–åsÕ\ýŒâ÷$ìn¦Óô p}Ïôñn‚@~ÉVj…¾›òíüÕÂvtl_{¬¢xEm&´´™é) ð¼?Šïëe½2aiÜ‘ Â0&h˜[fŽ1Ì,Æv;\œ—œüe(–­$åäøÅº]-Ê­ŠV‡-ëÙ„b[ãIÖ­Õ‰Ñ.*Î)ªn€U]ýÚä•\ã¶Í›˜[ôâëê`éf†aGaW,չഠ¥lò¬:QUb¡0®ÿåó˜˜ââïˇvï[SúMNhg‹iÁÔ5•ÅŒyõµe/®P69\œµ1š¸/¯^uû~Öø¶ÚÍ­ªvcÑfeýÑ }ˆ¦MwF.&â•‹UªûMЋ)äùW´Ö xýØŸ½^¥P6WEÚ¶?õY‡H€H€*h° MF®1³˜S*^P}žÃÂIIÚT^Ÿ²)‹4Ph–@¡9)–ý‚ÕcƲaÝIJ Ê&  8´4X—3ø}—¹'Åì+S"ömĬóLµ#€8¤˜ßïpýµv½bi   ¨ž@ƒUh ÷Uõ—V/¹¿EõÒQ !º@\BÌïwÈ}uåÀú$@$@$@ ‹@ƒUhF&üŠƒþSÜÒ@`:784øŒØÞïpáÌoÒ¤õLJM^% ÀoE Á*4!äs1µG6iZ¶ÂV Û¡h8 |?øD(ò§Xu BÏþ2¹ãžXɧ\   C“@ƒVhüÍs°ªÁ¶X|uXá({Ôí;b";ý¥L¨+½ª\Âb³r Øð ó±ºö‘õI€H€H€H "­Ð î»};f”GU¼¨z8_×Èß<½äP 4(rÒÓX‰lQ}w2§~?ô¤¥õ-—òH€H€H€H A+4úëKIvµ•æÕzü*mÃ0ï(Q–êQj%¢,K–õ¡¯’,&EI ßèàIVæ Qg±àÕí®gÞ†ø4LÔÓ&Å×Z7YOÒb*F¿ÿF?—1m„ÂI€H€H€ê•@ƒ}M$…‰³:7*ܾö_x;õ…‘éµ=ÖïQÒ¸;5Ñù¿hêââK i&椧üÇ+ß/ìäª% m[´/Ú¸ã ÃgÜ–m¥.ñò#÷xëv Ž—çåúH_¬„üïÌ`à>¯¼~‘žÚm´c¼´È}¿´Œ>Ž«žòûåÓ­Ôo"ó¼cý¢¾Â|÷㦭ŸZ——Ú ¶&†·}·Ê ¦Å,ÞÂësMûÄ@ð†0fããŸÚ¡â;ú;fY} kªÇüê œ8ñûžx;Þø®šV_²ú\ïÛ)ÊwG£õ·¡¤Ÿ-šúÿEÎêgo«ë'¦e^hÛö«¨÷q¤<“ |F7êÞ‹¾}Ö³ò·gäšoIᎇµ¦s´R1PyÕP ƒGÞ‡¥ k³É¸—•*þƒŸÁ: ZWU®ø‹!Õ£5‰IL ž‹*+1<×±í¿¡¼YÇ0ÅPåˆI99§ç&'‡"ó*û…ïSÓG,MNËø¨bžw^jUéߦE˜KâèàÍÂUgÎ ¦•¹¡OË0k}¼®ƒYîзg”rSpa®Ƴ¹ÁÔñÉ£ƒ×¢_à»åûûü"yú#©Ÿ£í§Q­7´´]BÊ—P6ƒv³G ·u…5ª mGþ3½»u}rÞâeY}.â :£^<–b@y½e͊˳WLJÝ€:…†!†e ¼¦ûÕ/-ë4WÙº­N˜©Ÿê>” oÒŒ~À÷ñ˜÷}”äðïþøah÷/O´êÔ{w&¾·D|WÑý^„ÝÕŒ1ÝôäIµVf 8šgª6ÏFžæ¦ü.Þ_ŒîîÝô³‡„ÞHMÝP–*寮PGès}_éž°rõÊÊò€H€H€Hàw%ÝŒëïÚÅÚ7Þ§Ï÷ŨõœþÌšuRÜŠ+ºŽÛ³°q ï0¸ŸFÜ»'/ìô%ÜÚ7QŠÓËŽRÚíl¡ Ù}0 {Õ²zÙEö9:½ æŠ"ûŽû(×wddI€H€H€L¥B‰ºT¹ÑAùùÕê‘Õk<–>ãeeÛÿÁÊHäÙÁ>Â0FÔT)[Á°i´^k¯ù ÊŒö˜j£\÷nÔ+Sh´ =cœˆø˜O"Jùö:½GÊÎ0¤¬Õr«Ú0ˆCWönü]•I¹ËPn“²óˆ(Míж,X«ºíÜQTÅéxXlæê"P<æ*;”ž4:ãïŽk€ÛÿÓéxáÉûÂuŸH±:eW¤¬ò”'tâbä'vÁ¹MËÀÇ5dG×Þ‡³%¥ÊŒÎ‚§šÊ+×i!×(GugòOL,z<”O¡?1Ù¢y¦êóÙ˜n Ü …÷ÖÞ ±<Í”tëôÈí•Æ#   X8è\Îb «2ÙÙÖ¨Ÿ‘þKž›y%ÜŸ:u4RTV.2M¹ên¼Àðîx!®Ö¿ô_eãm݈,§;™©ÙPÚoÛm_]1¯6ç†k“R6ÐÇ­Nù=z'håu–ÕG[ºjÜ Ì €ÓŽBüËEX±íJÈYìUŠîãГ΂[ÏŦOŽÏMO}JçiNP|¶`üjD‚¯éèët½Wéùp57µ3½ÏÌôÔ—–˜™c¼rzeæØ çG˜5(n‘åy|àˆö™ª¯gCÀí›7 5x„GîfÇâYísO$@$@$pà BS/ß ¬4ŽÊÄÀ}¶eaˆ^Í4Iѵ½™2{j0m•þè(CìÚY|sŪZž)}ý¡ŒŒÅ`ÅöoÃ’ÎËÃù¥Õ]¿«X‰s¼wnØ!§,¥¦\a¬BGŽ.+'Õ)P˜ÂÖœ¤ŒŒPÀ.÷ò Ý eçùNf×ôö"µÌâ³AdžŒU´â}¾–#/=¹ëë‘K-J¼UÛ“¬œÆZ޶é…L³…^Å­EbZÖ™:ýþ@úÑk讽 .nÇù}q˽sî*šŸ©úz64!< ³m)ïÑÇIÖ„Ãñ¬]eøâßÒçÜH€H€H€lThêáû1Ô,ˆé†AþËʶû!âCÖ•|‚?Â,r'‚ãÿQQñ1„ùœ«Ô]ëëó飾Äë-8„aܵ›¯©‰•šÄÑž(×ù+”ùkíà¬èô=”ŽÎU܉ô™¯AÁºõæé¥¥a9™îºêaij|" Ôl(v¹^/ iüˆUçRòìeKò쌟QþýNRÚç8ÛÞöÍû‹—ÿ„ô/à~×[×3šš(&-EhÓ¯à¶8do]–'V¶×®AʉÒuæ"ý+(K/¢Ïš}xÓ `¦ÝœòÈðj]ë¼òܸª{¦"{]φ–çk1˳q.¡Âϱ‚å¹@F¶Çc    ß‘@rZp<‡²ešuW´åC¿lpº503³ud=ýžÈó¤´Ì?áÅ—Fʇ"2Ÿø,ׯ|rZæ9PˆxçzoYÏ&èÙòŠ+Àés­E–ÕÇÉ©Áix'ÏÓyNÑ>n\3Ïjm–#  ø} ô‹ü¾x¬ÖU_Pì¶ß¢0ÙsñŠ6N¦²+™–’²%2]¿ç#òæ$x ·õÖfaËVk;*$Ôņá[è:NÈõ›¦.VNòí›P¶\ÌBi s­¡‘bï{•;"õK»°›ìÜö¡Èt“@m<6bÄ.!j\×£6"Y–H€H€H ÆÂÍ·Añ‡0XLî×îiˆR@<Œ\¯¹y9Á”Wû§eöt'ƒ¨ ŽpÛŒÏg-›úŸ.PÂÀxé$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$Pÿä>ôÜž(šIEND®B`‚neutron-12.1.1/doc/source/admin/figures/bgp-dynamic-routing-example1.svg0000664000175000017500000006407413553660046026246 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6 2016-08-10 22:15:39 +0000Canvas 1Layer 1BGP Dynamic RoutingExample with self-service networksSelf-service network 110.0.1.0/24External networksL3 AgentPeering network192.0.2.0/30BGP AgentExternalNetworksSelf-service network 210.0.2.0/24Provider RouterProviderNetworkSelf-serviceNetwork 1Self-serviceNetwork 2Self-serviceNetwork 3Router 1203.0.113.110.0.1.1Router 2203.0.113.210.0.2.1Router 3203.0.113.310.0.3.1Provider network203.0.113.0/24Self-service network 310.0.3.0/24Prefix Advertisements10.0.1.0/24 next-hop 203.0.113.110.0.2.0/24 next-hop 203.0.113.2Address scopePeering SessionAS 1234Peering SessionAS 4321 neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-flowns1.png0000664000175000017500000031554013553660046026425 0ustar zuulzuul00000000000000‰PNG  IHDR)šûÍÉÅsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì]|TÅÖŸ¹»›„„&½ƒ¨XÅ® Øë³÷ò”„¢ˆ(-ÙÈJ6AЧXûûžè³<6ì"ŠDz‡Ô½÷Î÷?7¹áfÙ$»É&l’3¿ßî;õÌÚ™3gæ Á†`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`zŽ€¬çô3ù1„Àp_æ±z@5íäé¼Ð绽0†H‹yRîòMéTô÷šßø5¡VJÉáéSû˜Zà0MiÛE¢ümÆ„ ;B…e7F Ö°Û¼[ˆÕOgzׯ:½LßAí#¶Ï÷™{“ø®›[hÅOúÆ­ ‡Ú”ôÌ›”2›åøÓg„¾¾„¹;+«­^,›9éu ­°¸cË-9))§{uíwû¦t)Ò‹‡JÿB¶쟑¤ÐÍg¾ÿf±±7ž+"‰ëa“}Ù‰š±ýPC=5SSBŠ Òåùk¦oÌæhÐ^¤ëW ¡¦i†> éM N“ôoæËJ¨A¤¿©4éS“ÐPÂ2Û¢=}rífšXi‚­Ðæ™SQO·­¹§#Ð×dF€‰@Ì3)!©®ÄñÉ‘#‹0q|€ ­;R7 Ì· ÍH)?ÁiI9°²ŽJ´Rõsq¶0ÕÙ;+#í?Á™ } ó<Ó4ßÀÄyݰ‰™sàÿfp˜ªÞ³'¥sÒ£ªpÁ?%=ëdÓ4ns°Ys»Nœé›ðwp¹‡û&÷j'Ư¢öÚšî’½„Aü¦œßh”6–Û"ÚÄVô³6ºnÎÆПo%Ô nÑ~/Ê3/£|´ð¸â£6§ÇÄ"Z,U—4‘2"‰ôë2ÏÚÊ«dËA{ŽÒ7•N>¤kN¸à0„›Ï7 ºÈÏ÷\œ9s°]™¡x‘ŨYh%Ì­”œŠA!¿§}Vø|Ò¬,'Òc _ea*õS¢c‰¿Ü^i¸0<ÇN™ÒŒê.Œ VšÐ^ݶ.mÎpԇß&e*ËSÉ—Á¨ü F¥_®þí˜2÷XÂjßR]Zƒ,j•pޤý83Œv{ ñ',ÌœD°½Þ!ö`u JF+ÕtR0 ,ƒ„¤J¢“ôðhžá=ܹåa¹+¹('Ó;:Wc[ãNl— J¶ÀvI.öúß×<ñéÁÊ)i~/Dû7IáòfûS_…Òþ?¸÷s»ÝÓÄEaR¼wcý;Bm^Ž?mxp¼iþî©ÞAþ&‰òÂÇÓÒ°=U±âõ¿lé¤hÚõ¡$)å‹2MÁª{æ¬Lï0;5(ßeâTÍ­]uVßÞ|²h9 ¬W‚¾þÀáïY~oO k‡CØžíOÒŽo?Sð_bšb46’úÁœ_•&Þïì:õÁõú×_bÐ>Y“ñ}‚•n“}uºîmg£¼‘¾Ž©rªØN›ž‰î¦'MóÝ»ÓvKöe&t{ÿò Ž]¥¹ðûNó¸î¯ˆq°ãÖô™ìͼ˜=‚ÿñ@é&Äÿpí†zÚ‰ø¥mD®Êñ{/N'øèÈ5²nCÚÃ÷p¤“ˆtŠQkð>WhŒñ¿:ãEJ»Ýf*j[Г!Mó1ðFóÑ&owæå´£}ç =œ¡„kì,êÿìtAgȶˆ>wâû‘îqx‚ÝM•¹Àë£Îî>Ã}¾kÊÚV¤erÒå´Ûã‘&äÓRs=c*ý{`ivlŽ/u©3,Ù‡¤ùŸ£íB´¡ÓцöÓI ·}õfœ…=¾IØÚ=•ÒEz+Ѷd‡w®Çw£®}ŸmhVò³ °:ö'ˆÖ–Í='M7níGÏa¾Ì£ Ýœì~åO¿º¼ß䞆®“þMüA{0.Ö¤örvFZŽ3,Ùíz«¨=ØcÝæ%g1>RúefHúä„ÒŸC^yÒã¾Ú>ñ–âÚÛÅãÐN.B½·Gé…ØŽ¿Ÿ`{³³+mfU‹†²LØR/h’t´ÞŒ ®Ë]ªó1‘§Âþ*&ÌÖÕÞEg ã› ïÉf h~0wްíKÒ3Z§éxïNa \Y&]ˆkׂ:p1&ºa)Þ¬«aÁk(Y,Åóˆs”ÔdvU Š3nevtÔcÉ_“j¡3$+}`0º¼hùS(ëdtîƒÀü}ŠQî ;¬NHIгå &í4ÓoƒA9ùü ÏçðÜ 9Â}¹oÞ/ØñB̘п"0zòo4:LÞPðU¢)Ñ„q¦ ô<>$Ï­\vT`v”à~ÜþÀ`–üþíWšã Üí°µó4K&oj3¾Göã¢üpA<Õ¿c JuÊDùWf :Kœ™1ágÐ68Æ Ý|–¡ÊâûEÒ¾M)û¢ÞÖ£ ì°Ò‘¨ÿÒ¶€³fKŸö݈wÚÕ)üSöû”¼žúhí»{¯¾s‹«Î%,ê°u¹Ï Mϼ Ê"ÔßµHÆBœ0ß–#±µœ &ì­` ‰]oµ‡}©‡¶ õe e.:ÈÁnM»ßfPFø²ú(U<ãÆ?AÃŒ]/£M¿ º ”{0Úϵ̠„Æ´>»V_]_K­D h4áºR‘²‰•²{Š ±¾!KRGkZDRâE‡»+L´æÌᾩ_[ƒ Ny€VÎj u²P«‘HóÆQÔ¤Ý»Š“Aÿuh¾T]Û¾2 ÓÌÀ@×MsiçGr"%Å—ÕWéÆ$л Ð¥3ýéŸÛéHèˆb¥¿ƒAî`ÛÍùÄrï_ðkºÀŠ)ÃöƒèøžÂÍ»6¯eœ[>5Ý—V6ñS ô‚çVs{Äi3|ÞïìxÄð™ÊxÍP2;9;ûÈêžd²Ó«è ‰ÆÇ)é~Ô8A ¿šžuçÌŒÔo* o»ïÚS<öó0ý_B»æ·S; ¿Q¾ÇZæëyÀI]ŽãÄ·Àér¯Èädx§ÁoV¦7á ÑKÀ~nNfÚ]…v§‰¿8ϸ‹˜›$wbŸÇ|£Ë¶‹¨½ìÙYÔz_:ãÕˆöJÚ¤$s€ãù`*„xÜ™'Ù‹ ÌkÁ\x@ë‚WùÁaÑ:ÅÏ7ÚâÙ~/1a–!Fa³x¸ýNϕəÓ.IE|Ûæ“pšð Ôé)ëLª›'œÁ*²GھўBZOaõžÇK72;³ü˜¸?‚ßPSá¹ ?ËÐöÖúÀ7—¯ßÀ¤i*“˜þ9¥ÞÖþÙ¨4YIiX†p†tæY0(0Ú9þÔ·l?j[<“z— }óX¸ûl¿²g%í¡,L…$Uº® ç¦ø]2##õ+;H±¦ KP9ÒŒEû IX\nKqkŸ#ÛZƒ(E¤…òi'ƒBÑ­QÊÿ’Ý”‚ÄÇQ13}i¿dÚ‚i­[+¶d°…Am2ä Ò“pgÄ)•5Ä›±~´~Äð÷îEÛæm.©] qï™M܈úAL{[$ Ñ% q5¬†§9ò›ž1~ V7ÉÊ@4|2¹KáùÓŸ&o`ð6Rwtãl§ŸÐ R‘_sÄzÍÉ PkËM 0˜ª¶fË5åâEñ#³Òä?0€/@ýf˜Æ×4I$§gÞ,m³³¥cý= [¹Ý.¯Í ?mcA`7šìã¥gmU¬º–¦ÿ“A!7:ºœé-Ç Ô”öJÛ–³Kh1o.¥©üÖ»&Ti¸òÞÎ7]<“1úqzƒBah%m/车e¢4B¬òÈêuyÚæ~‘5Ô7¹G¨ðû¹ÕBûv¹,&;{â,g~õ¯Ï ñ4¿ŠNø$¹÷ØaÈŽº;eÈkÓìkÛ8ûàÞTjâq'ƒBþÓSS·Å'i×£ÿ’äs ÝãcdzŸ•¶;ãI÷â`øNm°µ~-¶EË&+˜RÝè©y¨ï—7´½lo³—÷á·úŽ@£dR\ÂUnaW¢Û%Þ#;:óÁ¶[4ž¤ÛÎüŠ‹ ê&æK(â¥KÞšã»käy¨ˆCbsë1g;¤÷ަ0¯²Ä¥$ :Îô§¾[w…ÎˆÏ OÍí9‰$´o>‡êàÀ8aûX¶ Á¬£»íÚýýÅJËMIë‡ÿÅdÓUn²ý¥Ð¾%»!ÕQ¶[mú(¶&jÏ´§üÜwa2?/9Ý?²*eåšÒ^YÛ‚. êKb‹Bôîr¸³Ô´zÓq–ñ«fdx¿pú…²£MS꾟nOpøš–)8½PïVÙ”„¤C%™º1+T˜nQoßf¢{06P߃¬~Wš©)´+ÈŠÎ9Û4Ÿ¢ ·¹[–z㞥-'íèãŸ;™j¸ ¶Â¸ÜÙea–'SS· -¿Ní=œé𲬕µgXäkÞë{´.‘üƒEWMÉÛ‚™¢ÒðMœÃ|“{:Ó`{ÃEÀÝp‹VqÉâÌu¡|M=n›Ì+Ê¿&nPŒ½½ @-Æœ·&…¨Äš‘©Vœµt&ŒÂë0ùÍ€º.ùÌ+s&¥YL—3äü³ó=»µêZ»+%˜1qüj(¬î7ìOoAÙˆq*3$€ôásbÐrõ—ãœ4ïWR`HwÓ“ M¦Pìí¨¦¡Œá`ìhû-Ș‡Ý^Aå^i°Nž˜i ÒN7vœ¢d§_°½T¿bo5{Xº©äP”óJláM‚.Î…wûž8ïIßÈÝV_t+#PKÜÆ¾ÁÈ™iÜcy&§" ñ˜(0A‹ka¢aï¨õù ×\VŒ<º{ð¡îHóogº• 8ÎpN{ó$×¶];4Ú†<찘Û`àÁ l$õÁ1nRÏ#‘§‹ÅO[qô¼*šÈ{}iû.Ãr7÷lÛHIåƒ!he§_çÕW_m~¼(sLp\Ìk‚Ý"yŸéó~óm8É@§Jþ¸£)>ü-„Œ´?bÙjé/ܲ•žŽåðq;÷nEŒBû¸Uä‹ qbâ´é¾ÔåDbMi¯ªm=å·ÌéGÀçH¤ÎD–ŸóùOÂÉ’CÐ/> çUÝÓv›l&ž&x{0$Ê5-SÈD+p$É(Ê7Øþ³( 6~9ÊZ<ší;8mäMÒv^ÏÂóqHM¨mÒ¶©¥ogI<½sÑ®þÀCÇaËñ÷½Ûóh«mµóuk1výzéß ŠªChmˆ- ÅTÕìTÁµV.q5ön/Æa‚Ûòy³áw…íï|–2¦ÿ†Û¿KŽ)÷cHÍW€ý‰èsÐcÓ Õ1›*Àäk­Ö!Iª0¨T]*ò£K‡ ø7:S¢’Ú5è¼4H\ƒã}·U§ºîë;’pC3ŒÔ5ÒS©±)ù>Œ¤­œ¤ŠV9¥™t¦§+Rƒ3ÅáLRÜÓ±¢;&®Cˆ³]Md(ù^ü}¥ÇF.ækWSR¦ N+’w(¡3ðHðofFZH½¤HÒŽs·ø–ÂðNv<©”5ácÔ>Ìv«­g¤e#åp’¶ur÷é ú^åm‹uÓgÓWG´?GùAOâ:zq#=1Yîd¯ÌäøRòÁÐl ¾4ÔWþO¨xuT¦²¬=I÷}Àö2:&æ—WæYj‰fûNûœcú,@þ»À'ãø:%/ÁH°ÇÓ®)1/–q)ùY ~”S1Åáõ#˳ô¯„?Á/”~N;ðía½«’Ït8ýµC1÷áY“¼ÿ•ž¶#@ûïÀîСUUüY~À+7A²r*F< ÕÈPǯ«J‡ýcfR©\|e3ÅàPÁéútšPCù‘Ûz}]bvV+³è’ª8·çLÛqàÉ¥ÁŠbVÃ=w+íÙb«Dæµw—?¢XÔœQ~¡—B}ïÕNGÛ>Ôç@‡ýî|Ò‰èD< ·é8‚üN•¼JÛ&¤”ê WÞ.çÓ;”é’Ë»ÇÎ[±‘0QækIUn× ˜ø«+é˜f™{ YHÇ&ÎíJ+!Iõ)#­hoßüÔöÁ¦\IÌ;&Ó«iRmÒ:ñ2:ª¶,¤ ¦^|k•Aë LN¬‹¥6Ür3¤¥Z‚ÓŸ½ší[ŠBJC“zHƶt€é<0­’'f± /8•÷œ ± ž¤÷€yõp6Úî@‹&M”é£XïÖŸú†é[CÇØÁ _eµw¶ D°œÀKS@b@=š‹8ÈøL159= ½U›œŒTœ¾£»ˆÐë„Ñ»ê¢>!ÀLJµeºä¥ÁCI²¹3 I @9Õéæ´ã>Ü? ïÇT¶¢E‹ø{ÉÄÞ¿#Æ&0^ >!⌉ݺÜÌ4Ÿ§8Xe?Ξ}¸é»¤zˆÂâèÀëöXGDºcÆ Ëßá\f…B×’Ù/9ý¡£†û¦7¥  *ûËíJÅ Z1ÅýC¼Y—–%æ°Ð%q”–Ã)ªVÔÝ9­"­{.T`eˆõê{vÆÙ¾Ôß1`>‹Ñ²eq¾ñü°É“÷Û– Edº˜ÊŽS[Ob’‚ëÊÎK7Më„MÉʵĵ.h§É«{’*¶ÙX>èµGtÁ¦­ª§¦¹&¢…c.6ÇsÞÙGë¢LÁù['S$ÝC¢ÚbûâÎ`z¯Aû^Eñ¡P Ih£i¥RCYã ŽóZ[=vèFJ| :µ'$-éSÛß~⎼l´mй?Ù—yœíNO³ (<˜!´÷çœ[EÎp‘Úé:H›qS7ZʘC§~ì4hì5VRÂvS/ 'Ý$‰aÓp×›Â(ÙJ—?UD/”33IdX‘MÜsL‹ã¦ß¡ãœTؽt¼=Ò-uöÊÓ÷^ŽÕÈ6L¨ß ÃžêÌǺL+ßx]ÇÄ…d7—l›”„ í¤sD²7¬7¾y®¥+[g ¡íÒTcpGʶ/ÒéŽk-f Ê—ñíšÝgûGãI’ä9ƒî5¸=öàñæ‰$îݹ;pòØ ¾§ç×Þuòüõ꛹(ëùBÌíVB¨byÒY¬4mʬIiØqéø.ð ?\åm¼ ûà~ƶÑ^(·tB¸cq·ûq}o_Øÿ°ãEó é7`ŸÆŠq%8zKòƒ½»ž‡g`ÿY'|À™o\¢6!odéb}¯ñ£•+ÐNHo©;Ž|*–ŠV®×:ãEÛn›½qcï7 ÿ ˆÔ„xgòèˆUê ¸îLkÅê–ÓœùÖí`2f¦~0€ö±€6ÛICUvºémq*ðæøK´ j7K0YöÎ õ]mñ^ÆÖE™‚iv¹îÆíÕgcÌ)M«vûÖ Ãe¨›ÑFƒîhSó`;ç%‰¥z) IƒöYЬ…§¬_í£S½ìI2ŒûQÔ|ç§(ì0t÷¤Ä—àÈý§J7¿²õkÀ|1·é¶sþ±Hÿw·;ÎkljÆ:l³‘×@”ãæ½zÞ x^J[›ÊÔÎ5¿9~ïƒöp ¸²ŸX»[[¢7ÚóBûMœÆC ÞHRÐÙña+ѯ¢&n(pÕŽ¡ïñ\ ñÄ+˜4»†4ï¬`¥kÞ¿iÙ,ît Ÿ玉êt&º<+øB2 ‹ïÔŒÀľÿø!éYgǯèX‡t¡dVöÃwmDhù@“®+é27§x·¢t"u‡>® ×R)FŒ˜ÄÃq܇…¢™û4àóE¨47è_c0T1ˆè3G±áz?ÃEЦgbà}/Xb‚ËœfhÒ=øp'¢Ü£pjÉ‹'åÙîÏáfŠ¡òŒŠ›¯¡îè–N·܊{èz/=¹8þ˜ÒÉÓçüà/àÒEW¢k»è;©À¸ƒ+Õ¹°#×E™ì¼ì'}ÿ Ÿ»¨T¯¢:íÛºV}ííJ\ƒrÏ„D ’Ú}éþ…¾µÒr3ì\Ù¡âÝqÔ.Jb«§Ä+;#u>N-]‚ñ Ÿµs$ÔÃÈs |;#i<û;/ϳS¬é³y‹øaH)q8½Cù—•oà±tÜŽ>™‰þôÙáÖõ>±“» ÙÙ40ÐöØD‚í£oÒW¬á æ§&NÀ‡%æ«Æg†úýM]kÖILX^Ù¶V¹)4bÀÉIò$N¾ù”ƒTæ>0=€É™E¸’ãAÒÝ_ü¾â N‰špo>£ïÁ›hÿ=(X­¼ÒvVþžâ6†t·Â´ùº»%NB¤ä‡›™µå“/zšÊµ»E3µ±ªkßÃM7Üp´­fnÚÝA÷ˆ¶n¡íˆÖëÂ¥ÿ@ÓNéR¼ÂݽD“u¡ÚWp±X¦HÛ·U§[ò{™.Õмɟ‘l—ãîûÝYYm‹p‹qÈ yÏN¸ Õ0I¨õbÑ^3déråNŸ8fSc‡ke½ˆÎLJ½¨¦úK$˜”ﱚ?.Áãéñ¤oܺP%!½ž¼À^ú`ØŸXÖº®F(Ø`F ö¨7Û=±STÅb#_o•jGE ¥CÇ–¤…#lF€`R˜Iá¦Pk¢º/±ÝCWÄO >)C[gôEc|Íx¶ƒØÃŸUkÄpÂŒ#À0õÞî©wUV¿¶®^/0¿£bÝ_€-5Prûº%­¡àØ ŠxÍà¶Ç$ï¶¾n\¿ŠÇÔ2Œ#ÀÔ"̤Ô"¸œt t·Á}>NÄ(|!Xá´“Àͽj#$-…Òæ«®­ß¡ïr0^Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ@ð=)ëÎøLû×RjÏçd¤=«´Ò²ëõeô÷‹ñÆ8Ü‹ò¾¤<Áº7%ðÍxSªËq'm\Lû±êÚnŒÈÝzZ'픯‚¿*íò Ióߌ<ïÍÎðöç“Õ ]j‡š”/K|·ææžÌÌöfÀí©ì3 5Ï%ü¢U¾X+WøT’¾»Uh13#õ›ÊC²/#P=ÜÕ‹;±’½þqAØ`—K;椴ý>9NƒC~¾¹‘>ëž=ÉûN¤”û|J«ì+¿‘¦­ðà.‘¦êIzÉiþ?p¡Úa•ÅÁEk?à#* ®ß}™·ÊŽšx\˜âw©‰w}à›±øªO:¾zü˜„Ÿq¡[¾–»õ,Ã0çnßFOÃÍ#8ÜÐô¬S Óø:ØÝz—bû,zkäÙm¦_È01êXÛí¼ªbWÔ¨"nÄõFq®Âcõ:9Ý?R™"}µ½Åþ[ÐôÜÌŒ´9UÑWSÿh—Ϧç@—˦£¶ž¼»L!&%?ôÐA9ãÇïŠV>Ö'°ü_¯îÒ¤¸ ÛŸ>7Zis:õ zϤØp›†šyÿÃùȘ1y¶[4ž¹ºõݾ)'ÇÊÊ®Fe’ê¡Éfv`r2ñÀá~ÛM3ÕÛ^Ó'¤'7 ©>ÈÉHŸ”ÖøVÏçªÜg»ÓŠ,ßÌ÷ÆiÍ~°ÝjòÄ÷ Òä¯Î4ð¡ªbç{}´×V;¯ ‹höúT‚ÊWƒ¥wP¾ø®ÓeªÇQgÿÁç^Ãs“©es€)eTûsEåŒfù칟aê]¢•ÞN’œ4`u>ï{fMòþ×é ;­L×ëþ.¤ÌØ/=©ºAŠò–Ó}šï^’²PýEÅ€!ùræ$H$3‰ÔN;¯ªxÑî`PnDž…nÏAv»»ßsZ1øè>7]sÍ5F)=u¶}íò9ð< årÐQkÖþqË8ý¢bˆA)–ê',bJ·ëv0–D%aN¤Þ"Ð ˜Mª¿M%3Á¨LLNÏú¿œŒÔUÕH²/;Q¶L£~.DÖí!z^ä’šwFFêWw¨7cˆ©ôId/ >ƪ(€ w«Kz†é*@\þç¤?äÿ•Âhš|8;#íeŠGé ÄW~÷HÏuOûÇýAnCҳΔ¦™Ž¼†¤!€üºEÜxÛŸÂAžß!Ïû5(6℈É÷¡ìLo™ä£$dÉÿt¶U»<®ëgú&üíô‹ÄŽm¡[A×¹ÜÞ›s̱â½ßÚiß®E»ÖÛï¬dÞ ¼nÂ*çp`žú—á Æ™övâ_È‚{°sÚ°ôÉýuÓ˜íòÈgúÒ~³é%üL!ÓÿÑpKÀo™”®{²3RçÛa¢õ¤6¡Œ-Òç‚®À~9¶†ætt§>L[~üS­iÝׄÖê¶óÊÊdÓSQÃ<úÁÎÙr2½gÙq!ÓékÖ¦0‡¡­ôv[Á„¾Ù¼yü¤P’Mô[À”¼ñ´oÄ^+ %÷¢lu0(vÒû=«ª#;B²7s $1¡´Ýì'ÚÝÐð<üfTÔÏkR>;´ŸÈË¥Ô9ˆ× ýj›Ú;9þ´ávzÐ ÜÒcŒØSïtrwIõùn/´ÃИ1b¼¡ÉÖhÇ#Ñ×úÛmð«esÏSÇÛc‡MñNímŠÀÝR©A¨³nÈw;êíCW’6aÆ„ eÕÊÆ!j7 e4è<}Öîg‡·ŸÓ3½«“Ó3ï|TïW¿\¼¬[‘íÁÏF‹Úsý7˜Äe·¶“ÑCþ¦ù ígVVªäìl l¡•ÚЗx}ë>Lš‰ºi~–ü@æ ŠkH×rtÚ’½p©žÐ¤æWR›ÖÞ=öOtú®ø]X.C܈túB×V‰eƤp}ðrršæ¿Q*c¡ÿü X*[ê¢xaJzÖÉeK,G îù`PÞBÚ+ÀÖ4×Aa¬×oæ8¡ÔX—Kúj X‰IÙe?%WÏš†î|0$OÁ}ì“#GÑd œ‡cðËm™ÀùJ—2ć{§Nñ1™nÓò<Ù÷#0(O"Ì60‚/@œ¯ã÷9¹ã÷/ fš}tŒˆJ¤w2)é™7?Òw€Ô_NRÃöüMºä+@ÿ¨Í MÌ“L>¬oÍ»¨Ï‰`¸^§¬jZ÷5%7ÒvN™Êhª ÎQµ¿‡êeñÈ¢>idI¡Þ@¿—à6zÏ΢©åÂá…¤h;GA×àEÛOiâuô‡A`βÝB=#*0;‚yê*ô¥#áÝ—Šûy¹x”Ï/ÜrÝýÄñX0-³p'â¿…¾“ e&úÙZ;½”4ÿµ¨èe(Úæ(Œ$’«ç¾g‡)}>3 ¦,„ƒpCÐO¿Åï®{ô‡Ë…UÅÇ•2(bkôŸð{õv‹žgÿ†®¾Á~æx”kREeSë¶`•¯Žu{´~OûÒ~) ÷ì¯ÿoÌ–Sð>`VFêCÒ3; SŒwǽ³O'%•$Ÿah§ï£6ÉÛžw‘Ùp»u¸ozSÇ*qÅyÔé 3“´{gÑÃJÈW¡0xƒ§]^Z¯ýµišÓ0)žä\‘`@Oã¥`ï‡vøà'VwBB _»!”òppøpÞ1‰ô®GwòzªÏw Æ‹}fV¦÷Ä}oàX¼S¾ ¨âßu8îÌô§NŒ X³ù”õç`ÅG¬_‚8÷×`wgšÖŠ9°e*â¿UÚ•Ljå4ÓzýÏ4yA¢u¤C¢õæPoÖ†2Þ¤àB0hïW·îe«®=ÒvN™²ý©ïÛôTTç¡û O©úx]=¦§¦ÒŠÌ«Xa·Àdy3ì#,—Ò?0£·€q]×ÙM[qé–k\íÕâ|c8“Äò-0ÿBûÖì‘–'8~¨÷Šû¹#tåsÄá–«hËîaOŽpIwÿ™~v¦AvbbŠ6ï~ ©w!u¼¼ÔÿùoÆBŒ³©m:ëþ‡Ç%iûÕê—Æœ¡¥ñ$²¯ÂN?Ûü}³NÜ]l;ØÏpÆ! [1Î΃ŸŒ@( §j•c†ÏûÚt ši)¾¬«(BIl?È_ Š… &NÒ ïo­l,—И0q¢H}¯ïÑVbïöÂsðˆw&÷Ì4Y h¼·ÀûqžÞ÷ì)>EG 6$™(3täéMã4`øƒ\æ hú´"ÆCaVMyÀ”gbu5¼²Iß™fØv%½Á J¨¸%“ºÜ„UtçPþÕqSúf +Tì4ƒvR˜ðÞÁ„·ËùÃv˜%µ ˆº uð©ƒA±‚Íô§BÒ&ÿR¼Žª[÷¡óŒØ5¢vŽöV™ÊQf;ã`‚}ßÁ X^hš`ªU3»#=G¸¾L9; Šëq·: “ãDÔÁéˆ÷ ¶dæCzp†¦äYò”O Zoá–/8ñpËFã*ôÏC1(”fñÖ=§!LWH#Ÿs桚h4^amdžêtG+}/œú(§ä §©°S-ˆÙpúW69ÃUgŒsÆg;#Œ@ƒ¤8 åq·LÕõí—)ÝÀ¶:mI=ƒ :aOtº&X5üîôÂ@@L‡flÏëŠç §ŸÓîr¹>1M]æé…4ˆ¾%… q·˜?Ë7~ &ž\ ÷ÿÊ|ý ìh.·ë#+¾R‡ÐÓå‰ß/m©iË•iC×{!ÈJ+<ýIeKzÊœÊ,Jõƒ©‘üz09eîQ²@±1dÞÉé…›[pdôPÐ×Ù5ÅPÙÛQcz±ÔÓD.OÜòjGÉg n_挫™â7ç»ÓŽ•â!˜<ßpº9ì¢|T/¢ÚuïHŒ¬¤ßbªÀH§sGOçûœúN¿`{Xí<Ì29Ó®¨Îa‚í˜ØÖ»¡)쨿"¡—m½nÔ矜Ûy„çÅàð¥’G¿Ï÷Ü#ôu7¢ßŒ3}ô|H>¡ðáÖQpÚ5}·|¡ò §\`Îz‚ûý_¨øäýµžôÄð0xL&;U€‡Á¸t/q)ù‡[Ù6Q™{ˆú pþö¼ë ­„1±èh1Û7M³|_®l*Ë–êŒqÎølg‚(߃<ëã+ JÓ†¢ãž¼A÷(Ö´âàr 3&ADñ#ô#¦–ûir<¶Ln×ä–à8Î÷§}V€Éù[ c bAϼDS¥§U”üVEØPˆA·r†oÂ>¦ Éx=àLϲ»”å†ýèx§&Î<ç»ÓÆè¼cpSÀ‘.@TÍô‰Ã÷Ë»äÞ‡ÀbÌ?cµü&˜«qš[B‰VFíŽ*MK7â©Âê z1oÏÊð>íüA¼ýe¥i©’:Ø/Œ¤4mÕKMëÞNÛtšA£¿ó·^‡ÝÃiçV^a”ɦ‰ž¡êÜé_=¿÷rÎ`:oAX,­r"& wb<ënê€öEýìN§?&ÁýûpÔQ¹ð¡^”ô„r®Ä-¬òUR¤JË•^£Âv…,Kª<ž(7^aü3ÂYAyWIo²ï‘6{·åÿFpF¤?5ᚆ4R0v=”–õZÙ82|c\¨øìÆØ48I lÖ¤´0iÿ“x–Q¤>¶ k?11¬£R0ÓŸö¼íVç'Hÿ (Už„¸mâ0aSnéyú“7ˆÌãð:K²ü!øQ]˜ù²üÊ´ç)TIÉ Kí/ëÆ­i8¡0'va;âÑ”ô‡¾Íο(Œ¨Õ BŠ‹ë˧bÀú6Çï½ÊN„׊msæÌqáäÉE(Ú²P‘”GÊuh«-è4QY|X09_à|/±×¼|û§Ú%T¹Ðîð‰ u^pÛ+KÁíùcLž ((üGÍ@*övÔNùÌN‘hÃ@ }½ê›hqÕ§„c6¢:±Ä(O¦¦nòݽ¿XB—cíÔÔ“)÷nÆ%ŸB3~<–(‹”(:c(N­˜.H'|眣z}ýñâeÛ•iÞ†æ>Íã1ìS>¦+~$´:5m¹ö“DÉɾ¬kUÀ\}œp¿Ùö‹æ³­'m-.hÛ &åVÐÿ5îŒÙ‚{Cp4RÊ53ÓëÍ…4ì)¬0GCr·ô`;Í-è&RàT¡’au hÑ2þ_8yu‰Ò æ¢l£Üž¸¦8Æ4ô§Q¶¥]§>c§]º·ãFûYY;¤L•ÑUY?¨,^y?y ÚÇû³|÷ouºßí{¢y‘¾ëôÎw5û7„¶W ødñ2úÞSO¬ôoµÃGR)ÜŸ @·2Jy sªŒSK"(å*õ ÒÃBaŸ‰Nùö¥G¶HÊ…¶–©ëE· ½à#Ð:N3ÝkpõÌ1ÐË錓mÍôÙŒ+2Ðö'£mãÞ¤WÀ´ä+Ý<*o' éšž“1þ×òTþ†1 'ƒÄmëõoÓ’}=§™zÿO-¿ÝZDÕ #×ÖWyiØ·!#Ð`%)Ti8Æø:3N”7ô -Auե̗MU´öCl™Œv†´.—’Úð}‹…ú—Y•‰üs0Øb`ÅwgD+©¹­­;®KºÞ&w¼ÿPz“ªí%fe¤ýÄ0w®+Rò/]/Å`tµpÉ«œ—À•EÓ’ãK] =Œ»QŽ›Àý3Ìh£Þ\1l5"Î#Úñ¼“ËÕÜÞ(±0ã2¹±Ø.£KïFóWÃ4¿™Rr§J D„.k’h½\…ú~;(Ú€üÞB¹~ð$i§9?zXݺ€œˆ‚VÔÎ#)SeVÖ*‹gû ÷Mî…ÌS\.ñ‚íf?‹D¼Ž>Ú u<—ù}©ÌÀbô‡gð~¨&ä 襋 !")m{âœ8Ú«˜†ù‰Q Ö↟té–t’¦Ü–hMËg—ÅùŒ¤\OûÆnÄÑ÷À¡Ÿ=ø dL0ga<:ÖN3ÛŸ6ýû´Í+Œ€Z@ý8ÍÓÕ_*­ÓeÇ©ìÙÉ}Z6$‚ ?ýS«¡”û Æ¥•šˆ£“Š52µ5ÆÕˆ(Ž\o@m܆DœXµõM´üÁ½{o¶¬ õMæ[KµõC„ˆÜiDVVk(Å™ÎÛ#OåÀÄ ƒ¢øBYW´ßå›ÒIŠ&{ŸôÜ]Û%&¥ç-Ì®gÓg]Eí¡¶iˆvúÑ*Smô*+ûìÞÝÆ¥kIM’<¹¡nªubny(Üæ@fç8OÒžàƒ3=ÛíòE^®ç¶rÛ?åO[ƒí_ðjû:Ö§çÔ²YÜfç ²û‡ Ï’”nƒûöÌ­¶^ŸÇ¸ðÐãPŒ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ@l# 낼aé“û¦qòꮤê„L›ÕE¾œ#À0Œ#Jˆ=RÉõ»Ú¥¹>œ‘1áÇpâq˜ÚE Ö˜Ÿï¹„Ü@î]Bª{„]¤¦Pr˜”õÈtoí‹SgF€`ÂGLJS0)0gµWJhBŠu˜³ïìéü”Ïw{aø)qÈh"P+LJ²7ã\9 ÝMJ9¿W´Dí½&ìˆ&ñœ#À0Œ#M†Mž|™o^¤”º¿ó±À^ƒô‡äøÓ?Šf>œVxDIIN÷R¦xDJõ¸Ò‘Ù™Þ/Ã#…C1Œ#À0±ƒ@Jšÿ Sˆ'AÑR÷çdx§ÅuƒW4‹Y ¨Ç¤ÿžv—ädŒ^Íô9-F€`F ®øñ«y«?ûÚ¥™(¤*£xö®¿œ÷]]åÏùìºEÉÐRò}$øF¶?íZlñ`‹ #À0Œ#P¿ƒ"S¼™¯bR»SÛ…¼õSwõ©E#+R’E:³h‹GxÚÞÆ J4På4F€`bkNÃÜZ–à7«t΋Ò< QaRè¥$ ”_J~ƒG È0Œ#Ш ¹ æÝ4×Y'WUé\a£Â¤Ð1cpšsYIöÀU$çÌ0Œ#P»ÐGsuµFífÅ©—"Pc&….j+¹E¾Â¨2Œ#À0 0)¯ÐœgÍ} ¹ 1R¶3)t“,]ÔF÷ ÄH™˜ F€`F V ¹Žæ¼Ò[Ôk%Nt5fRTwºI–/jÛ*ÛF€`&Ö\‡9¥ëÞ0K[¥r×”úÒ ï°aF€`<ôy’æ>6µŒ@™Ü‹B äoñÔrEqòŒ#À0±æ=šóøC¹uPÑØî©29 F |èâ¥{23ÛHó³86|ØhÈ!é™ÿÄ…ßP"8sF€ˆ9j,I‰¹1A{}¶ÊÓ &áó 7Bû¾%‘œæß†[" MΙ•‘6»!ãó)Íç“ø´HôL²×ÿ1˜¼Á.—vþÌIi§LÌ_~¾¹QsÉK³'yß ö¯ö»í·_µãsDF€h°$¥AVkã+ÔØ)Sšåéys…Rÿ„(öIMº.r{äÉJÊ Üi°GSrgCC%W÷¯¾Û7¥Km”Ë4ÔÌû~8©6Òæ4F€–¤„‹‡‹YèŠêõ{rß]ÝÒÓÿiÿ¸?ÄÒÇÀw¼7ëÐôÉý S¯ŸôzŠìÙU˜ °F5À¸Œ#P/`&¥^VíD ×\?”34MKy:£ƒâ VÎ>$=ëLišéØ :’–¤/ Ý"n|ƒ# 'ñÄã•Ôº™¦p‡"ü/R“ÿ¢íŽäôÌ„2“‘xü6)Ÿv~ÎÛM·*)ÎkêN¼ ’ž'‘ß8²ïAØï\ר™¾ Ãn™doæ!Ì‹ðñ²¥Ne!iþø6Öóð›1Ô›1ÄTú$ò, >¤¹5'Ó{–aˆ7c0˜4|æ³òÜ‚¸ïtrwIõùn/´ÃTôÔ¤úÛT2ŒÊÄäô¬ÿËÉH]PQXÛ=Ù—¨Œ-Ø|:øô=ËqéÕœŽîÔ‡ƒ·¤€ÙJ™C¦èZ L³Ac±–óIéŠÀ–)ð?áÚ㓨‹\RóÎÈHýÊŽíŒ#Ð0à힆Y¯«T†:A YpöQ½Ÿ §àCÓü7JeÌÃ$¼àü¼˜Ð[ê¢xaJzÖÉAiañˆ©Ì‘˜¼gJMŒ‡s0,sR¼Ó”©Ädü)µ˜t×áý±oÖeiHÙF uÑ^=ïKb$ 3Jhb.ƒ:Î  ‡û¦v( +ÌŽÐùí»ï}Ÿ “ù‘x#½ aH×r¤5Çò•ê Mj~0QÓ¬wü¥¤ù¯…ÿ\!”KC~(ßGp’«ç†uá¢)d¢ìÖv2ƒ?„i>ãó͉³Óõ$Ø2_˜âf0iÏ#Ï[€Ó»Ðm™˜È|ÝLÛXeš³€÷/šKÜ ÚfA©æ>Ú¦s†#{rv¶é~ëÀì]`€p"Q7ÍÏ’ÈžßF á!À’”†W§®DXañòšk®?Q¹!=‹Ý;‹VB¾:Ëï½Áíó}öÒzýë¯MÓœ†Éõ$0˜£KŒT²§ÛsP·§}#¬£öwy3>,Rb¥)Űç'}ãÖQÈ»Ÿxâբͻ¶+a\‰×7JbƒUP¢9&îÿfû½m·¡¾ÉïƒIY®ëE÷Ã~a›Y©_à4LGaª‘ñî¸wìü)Ð_´y÷ØÌßåO¿¼4ÑçÁP-D¹fƒº0ÛŸú~e™©j’“’æóßièê›ÜÀrbÌ&Ug½þçb¢ ‰:Ò!‰zs¨7ëCïØy&?ôP µ70é¼ìï´Óå{ìµ¼@ÞÔc9£ÖmôŽu{´~OûÒ~)õ|vˆ×ÿ7˜§)xP.¿0Œ@ƒC 6Œ@=G@ŠÃ IÙN)öì)>E Õ“øSÎð>ß RŒé˜ ð¡ƒ~ؾøÀfPÈý)ú*ä÷7~ß9„'GŽ,B߀»éZ.>^4åù·Ó¶yî}H<.pº×Ô^¼uÏi`FºBJñœ3-ÕDƒd¥“æ©N÷ ìÖ¸0ÃçýN m:ÊŸ–âË )áOŠž@IDAT)‰¯®B¹?u0(–óL*¤ò/%ÌëÈAîEÞ8uå’®œ’x%ÿÓ|÷îÄ6λN7²ƒM¼ž$.Å Ü©,ý‰!³ø`,,Ii°UÛx ¦c=¤Â*±R‡P8—'~Epx©iË•iC×{Áo¥Ã?×a·¬`0vCâ@·N–3ì†CórŽxIhåYì¦iâOÓƒƒÝkò=›žŘ ½”ÉvZª›8øÞ¤:Ým·pžwËT]ß~™Ò lû¨Óvˆ¬ý¢!ÍCÀŒ”IŽ‚ü Öˆðb”äíòÀ­¼mk œA½öCÒåøÝé&¬Þ5c{1ƒûÕ£3,ÛF ~#ÀLJý®?¦`"[‰ewÿ9sæ¸ÂÙò!Ðd¼Ø<— @¯B@)¶Ü pÁ~aKô Ü÷svÆ~RKH’Àè¸1é"‹}ÛKûE&R¶­"3¥L¢ý%$Gº*yäæ4†«œïUÙI‚4äÌ¡Â0ßß ûGhñî—‘ËþѰ e”€»§–@qÍ£(8¨2E•rŒg*÷(ʾRÞƒÞ¤Ðä–rîüÂ0 fR\•6¾iB}c(qëÇ¿þy+J?»2LáúQ]˜ù²Âí(V·Ü°F×þ*ç…—ü<½#’)¿êWêLÖ:¨¹ˆý”T“}´QOÐ’*Mi+ ­͟éKû)d gMJû'ˆþ Z³Œ"õñ~Ñ¥ú|V÷ýÜÉAªn`°–[V!W# ! ÍÎxì"7Ûƒ$kqè­£R0ÓŸö¼ÓíŒ#ÐxØou×xŠÎ%m(˜]Û=©oŽ?˜âÚ»²r%´MZŒEx!•›ö §Ì›0Y®í(z•g&ö ¹ƒn;c õ=ÜÛ}1‰[æ.q:H¨tJ™,*PBo¥D§Ø0Ê뿸=¸Z^æ©€‚ÒiôL|¢k˜"¡›çQ.e%€:—˜)§{rúCGA¨s NúÌ#w·2Á JCÌ}§ŸàN7çBÂr–3.Ù‘&À:‡?oŒ ¿3–¤4žºn°%¥“()Ø’À±àו(ú wŠ<,\ò·Ò6›¦ÑÇW¡XëzkfÆ„ŸI¹'cnÇQáqvKœT¯".Δ;ÿJl‘Üìó]SM°°EÛHcSÒ3·Ç¹Üó†ÙÖÔ‹¦a{CnW™ÞˆîÏd¡Íã1H‰w¦oÌæof”O&£|ÅšG`«Dæ+Ý<J°' éšž“1þ×})…g{25u p½´¼XcŸ\§EËøáÄÔ%J/˜‹Å ¡ñ`s¡~†¨ëD05.Üãs„f+#À4@X’Ò+µ1){RÚ§ Pöð»‹t(tS_hâ¢0LØAB}ˆƒoøà^5 는üKWÅKM%®csUvFt.¢k v(ti®Kp×ÊøÂ@ñZÜKÛ0±•qvŽ/u©[vÆøE¸oeh`æ'F¶‚L•.Ýò*°‹ìpô´to¤6~ßb¡þÆ¥nÖ– ùeûÓ¦àN—;àw…P Œ€ù+Ê7 F©´rÌ…×ÌÊô¾†çÃàðŒ“×$Q’¤hò|;(Ú`˜æ[¤OâIÒN£“SvœÎžÎÃÀd¼©×ô¢@q®2õùHs/¶Øn³ÃØÏœñãwi òxÈ’–àò·—MU´’™qZh´†ŸŒ#аØ·ªf9±Wý9E uKf5“ähŒ@¸;+«­*vyžòÛïŽ3ñYY­¡ljΘ0¡¼~Š3P ì¸Eö>L°äø½éžÐGõ¸BI&*J–¶?62;Çy’öXÇs+ XêŽ;Wzh¢ùVç1i;JÉGójÙ,nóÔqãöØîµõ$Ú·2»žyLŸu•)1ã^÷Fñ]—³ú²¶²p6¤ýÉâe=D-pïޛÉcÇå'#mxÞ‹6¢µ˜U–]aµ˜ 'ÍÔKˆI’–<¸Ÿ #À4xÞ«»jäힺÚsbF€``&%°8(#À0Œ#ÀÔ|º§î°æœ!Rxþ'¥¾ÊqJ#D‹Ì0Œ@õ`&¥z¸q,F ,²ýcÿD@ú±aF€ˆÞî‰0Î0Œ#À0uƒ3)uƒ3çÂ0Œ#À0"ÀLJ„€qpF€`F€¨˜I©œ9F€`F€ˆfR"Œƒ3Œ#À0Œ@Ý ÀLJÝà̹0Œ#À0Œ@„0“!`œ`F€`êfRêgÎ…`F€`"D Þ^æ6,}rÃ4ÎCy»ãóððõ¶f–½ÞWBì‘J®J­q¹ÜsgdLø±Þ*ÌXõoèç )»5Öú*Ö(ëûB¬vi®Sßo€ÕÉEªzŤø|Ï%ärïÒ49Z7õŽtÕx³Ä³eóD™àñ4:©Pa `îܯöäjÀß’ž¹A×iÍ ›>ùØc£ QïËŒ‘÷ÞûhÂw»Ý®QVýk»þLÍrA"F ¨ïgRß7 ó±.q]žôùn§¾Ï†hÔ&ŸÆ>w£±~6æÝÎ}{vV'ÑSÕ«³LLˆw5ˆš¨^!,Æ,¿°Hüº"Wü°de‡ßV¬›RдàžÛÆx‡<ÿ°ÿC$kâWŸ™É„†òœWP0K3e§¾=;qýW¯½p¬†ƒ@¨¾?cä¨;Æûîxö!õýúÜïNMqIj„@'øþgœuQðã—Ÿ=OÏÚ0`PîUJ¾Ð¹]˦C®¨]pêѲK»ƒ„Ç]ox¬Ú€¥,MÂðpdOyhâï [ó 7{êéy?ýÅ÷¥ëã€E±ûŸã¸×퉛úOâú/«v¶0ÖÜ÷÷ßÐÿŒ{úê³€¨>öû˜¯Ùº˜÷b„:"0Ö·Hd ƒ"=þðÚØ[/võîÖ¡Ž ©ŸÙ>ãn»ØuüáK—Ë3õ¶±ÞÑ(‰?bHI*Q ÑIôzˆ~*•‡ë¿>TÓx Ø×÷{`\—ÿº3õÁ{AK}ê÷ :Î7†ˆe&EBly$(c‚Rw^q¦ŒóÔXðÃU=Ò'‹póxâ³®¿ë¾ :1*T߱Ψ}D§‡è&ú¹þF œ}_Jmê­÷§žhõ¡ß‡Q:ÒˆY&%9Ù×Äãñ<Û¥}KqË%§ÅúÄ“m‡pëÔ¶¥hÞ²åŒ#<±ˆ´•˜¤·”(‹A!z‰î.íR\ÿ±\]L[,"@}¦s»ƒTBBâ¬Áƒ¯n cv¬Eü˜¦ØA V®f´’w¦ÙéÚsOt±¥z †p»þü“4S‰GŸuæ¤)òÄl½—ÒOôÝ\ÿ@„ #!Ô÷¯;ïD—©TÇÎÇô¹Ñc¹ßGX:Þ˜ˆÅÉÊÒGp{\£ŽìÕE±JÍš#áG8&5k6)5Á/¿Xܧ¶ê½”¾&D/×?Ð`ÃT»ïÇ7ir’ˆÕ~_ÍÒq´Æ‚@,2)ÚM#Ç`šªŽó6OZb)ŽmÿãÚ‘\¬JS¨-Òj/¾”ζ\ÿQ¨|N¢Q#@}H)Õþò[S–¦4êÖP? kLеšöÄÇGµá”ú‰jŒQM8ž­Úµ?¤%àk«*§%èäú±FÄäÔKì¾ß´e‹sb°ß×KL™èºE &™@Ѓn’ÅEmu‹FÍplÖ$^inw7‘¶|bíH²Í¤]MˆN¢—ë¿6H.V!`õ}Œ¥RsuG¦±Öïë Ψþ"kL Ñãšìˆ«îc¶ú[Ë ¼e‹$éò¸ÛÁê”¤Ä Æ%õ^"áI :‰Þz 8ÏÄ4–J—ìrl j¬ôûAˆÉˆeb­±=nMÈføORQl9„§Kjt‘ÄS4XÅÒþ´Uï¥tÅ\ÿ@#B³iÛn±që®cÕmð¢â€Xú÷† 3ýôûßÅŠµ›*ôgÈ ¾¤ «ïÇZ¿¼0£Ñ!KL 1%–Ø_ U%múÅ[ŸÕìÁ›·ï¹›wÔ뢔…¯=PÙ'|¢Šu5*«wÄ%Æ)®”Îj$Õð£¬Ù°M Í|^¬Ù¸m¿Â¾óåÏâí/jÖÎ÷K´›¢gøäĸÇçˆÞùZ@é½ÂXKV®OþçcÐõa>^ð›X¶zcH?v¬>Pžµ±Òï«_ŽÙ¨ I!– u$¾6q 'Îý0y﫟EÛVÍ.HÚϯþ8XÌÕ9 Tô‹&µ¤ÞKh}ÑeRëOýÔŒÒÛ/;ý€|©åΜ)Ú´l*þZ·U¼öñqØÁĉGö Y˜ãë.޼ïþîVHtjÓÑêS±Öïk³ÀœvA –˜{EMGæjIY´|­øâÇ¥âèÞ]ÄGó ñqbàñ‡‰3Ž;ÔªÒ_ÿ\'>úî7±nóvѵ}+ŠU¹[Ä¿¯ñq1Ñ \’t’8 ñ?G:ó žÞµ·@Ú½ƒöàÎmEUy ñú'ß‹_–¯¡S7ℾ‹«Ÿ`­.ç|ô½øyÙjáv»ÄY'ôçžtdÔšZ©„¨ÊðF/}£–{… 9é°hcIJ…XUêñê‡ ,)ÆMReœýÖ—¢ib¼¸æ\:•.Ä÷¿ý%Þýj‘˜4ì ñåOËÄ' –ˆûn:_´h–(>_ø‡ÕÖé½Yé]—7íÀ¼wëÐZôèÔV|úý±jÝf‹I¡~ðí¢åâô~‡‰W?Z`õ¹>Ý;Š9°S>.—fIMþýÁ·bo~‘8õ˜Þ[ýßÊ 8 ‹ÿ}ñ“øyé‘_T,ÑWÑaÄØ[.‰MâNíõ•ò%¬ÿo¥c*õ/g;ý¾þƒÊ%¨be%mÖî@ö{ÔŸÅ€ø}e®øîו` ˆî[[haQÀàÿ=w¾è·{®?ƒj7ѺE3<»Š¤&ñ¢ß¡ÝÅm—ž&îÜÆ¢«Uó$ñAÇ[ƒy@7Ä{_ýb¹W–xûóÅ÷KV‚9:\\uöñ¢W×öV¼/úSüº"WÜvÉâ’3ú‰7>](¶ìØmùEñêÜÆ™ž±`œôÄZ›Œ|¢aw^¡Ø“_`…­ª îÊ+°;á´ÿí»ò¬×SŽéÝu)^ûä±sOžxó³ŸÄÉG÷ É PÚžY&þU0 …Ð9pÔ!V:DÃ«Ö bBúÞ]àÎAnÛvíW\2?¾üÞ·"ŒÉ œ,¶îÜ#6nÛ§SCiþøÇj1ö¶ Å­ŸjÅû'¤EÄ8ÕQ_±ÊÑ€þœý¬‹‹Òˆ%IŠsLœ— ì'‡Xº'$ó¯°ÚcútµhX}ÿS°ª|b_›&áv¹D«IÂy.1/dvíÉ} I!{Hî屫Öþ‡,.„”Æi~[±ÖZm¶oÝLЯC›â§¥«Åy'å VS{¬T±JWMñ> ñ+jƒ•Em¤1¼ø˜†¢]«fâœ+–è}øíb+9’†Ð6O»ƒš—%ORÃ\ÔßbPÈÑÉtïØg½ºñÆûvß^Ý}óù5H®úQi5§áŽF:"é4t2²+»Dˆ¦mCR:š\ST¬[ú,ÄŒ8 ‰½ pä2©I¸ŒŠyô¥¹bÑïþñòãSïA‚t[ÝF /$_7ð;P¦¬ÞA) u¸éž±Ó·÷áªþ"_jƒñqv­Bz\t:ÎIõÁ±Óæˆë/8ÉÒ #¦ý‹Ÿ–Z—2N»ÿ¦²~Q}å@ÔCmæI}ÿ×¥+¿øØä›O¬ôûÚ,r­§Åy¯Öi­ïØÑ)FÑó¸CÃB ƒ“A!ò«Ë ”Ä 1GMé;lÚEÀÉÔnN§NJ¹ÁtãBî¤d¾Ç éxòbܻҥ]«2…Rä¾R1®ìÃ4BÏ’ ¡d\F€¨·óÇågˆïÿ…;Q–Š„¸8K/åÜ“+> ]o Ë„3Œ@…0“R!4ìÁ0¾8‚L?6Œ#Ðx(¯tÑxqà’3Œ#À0Œ@Œ!ÀLJŒU“ÃÄôqûˆ|,ÑÅ´0Œ@ã@€™”ÆQÏ\JF ZбüÒïýU+>GbF &°NJMÐ㸌@#@`3>r9ëÏñ±Í¦Ö(ô?ºppÓöÝÖ}B?,Y%†]=—êÖWÁ7à{?§÷;Tû­—œÖâ"2Œ@m!À’”ÚB–ÓetÂæ¸Ã{X_!žþÚ§V©èHðÛŸÿ„Ó7+à×ÌŠ[Ì|ý3±vóvë¦Ú·à·G†Ù0Œ#P˜I© z—hœrL/|µ»‡¸âìãÅ|!œî.!C® ¿ælqÚ±}¬ÏE¬ÌÝ,þ1¨¿vÐñ‡7d¸ˆŒ#PÛ0“RÛsúŒ@=G !¾äFå&¥Ï|ܺL†®­·/3¤oìÐlÍJ/!lÑ”.fÃ0Œ@Í`&¥føqlF Á#ð+>XÐÅ÷Ð1¡nslH_…¾1µð¿-f…·z‚âwF€¨¬8[Ô8#ЈèŒNžý.¾Ìmˆ«ðÑÁŠÌ•g÷oÌ[(ÞùâqhöcwF€`ÂF€™”°¡â€Œ@ãCà¡»¯± } ¾<,„,ûnΩÐC¡ŸÓ{hw|)¹»õÑÌe«7àÐ&§7ÛF€ˆfR"†Œ#0æç¸)Xð×ÃZ\bF€ˆ¬“-$9F€(C MËæbð‰}ËÞÙÂ0Œ@u`&¥:¨qF€¨v­š‰ O;¦Ò0ìÉ0Œ@U0“RBìÏ0Œ#À0fRìœ)#À0Œ#ÀT…+Î!¤ð55úÙÆi·Ý"}:•Éî|4-_ûD» 8ë›ë¿ö믺9D»Þ‰®{ £caûŒÓ¾Ï52Û¾Z²;ß#K‰CÇ<žI±&Ó4­K¨èÚï?V­Ûwç‰]yùÖGÓjÔJ»d|œ[´HJ­Z$‰¾=;‹î[ MÓ¬AÌ9Å|‹i€Úm`õú­bÉÊ\±}WžØ¹7_öU}u‡ÁRÿtËl~a±(( ˆ‚¢b‘ï5KMKo˜­mØ®wîûQíÔÔâéGRzí–Qú{âãÏKw¡ÉŽñšF#7%Åaª ÊTkLÃøð¹G2~Dº&~Ì´DàJ£Ñ2)4@Ñàd¦(* ˆÏZ*¾øùO±7¯ÀêbºL…Z‚0„+*Õå"ÁÜ!ܪP¼÷õbÑ4©‰8«ÿ¡b`ÿÃE<|›a‰JfœHXP FdÞKż…ˆ=µT÷DL}«ÿü‚"ñÛ_ëðË¿¯Ü€ïõ _„0n·K´Ä-´½»´Gõê"ŽîÝG]å$!¢P'îûµ¿Í˜¸¼<±ûñGŽˆ÷Ü¥„Ö*Næ™M´ÝÒ-Š¢¢b ‹x³@6WÅZ’¦¹]&§=¸Q×Õã[ÿX3ýÿ›Ú K­–­;%“b3'@ÀZ9ÏùäGk‚Úái/6%öÛðÔeÉ÷J¢]n­›Dûâuâ_þæh¹¸þÜÄ‘½ºZ÷K³Â¦ö 6ðÛŠuâå¾»!5©‹º§RÅzý†!>øf±øð»ßõ]‹;\mÄ®&­¬>a`š¡¾AåÀ4!âÀt7ݽ[lùu¥˜/"7oš(þ1°Ÿ8ሃ-Æ;ÖÚ3÷ýZé[4hÑjÎsý]÷Û¬Eó§ÁœtlëY¡ºº‰öîe2NFgµ·|k ,V b“~¨X«Ó~³è5¹ÃQÝGÞ|ȸ”—›2A üˆYaShTLн‚Òu+èbñÙKÅûß.ù®fbyÓãÄ.w›Z¯Jà7Åu±~-ô­¢OÁ¯"çÍ/Å%§-Îp„ •)KUj¯ì6ðÉ÷KÄ[Ÿý,ò¬º?µNêžJ«õoI•À”Ìxís±ôïõb«§“XÓ¬ØãjVe¸„. l= ÿ/¼ûX³q›¸|àqã©Ì’•ëņ-;ÅÖ]{…®›".Î%è#„ÝÚ·½º¶­[6³ò©­m#»Þ¹ï‡Uá²¥'4ÄÝ[¸T$ /[³I¬Z¿ÊèùKM¹1c¸ÚqÄ|ˆÌ!]Û‹Óí]&‰&³b×;÷ýˆª´ªÀ6ƒB"çøïs”»x–¨cÞÐÜ2PUü¨ûƒ)g$e»~)¼B­GlXdŒsŸ¹›Ê€4´Æ»ÿqKÁ”‰q¡ë0t‚4ÔÓråï„×?]7^Ö.G©"Îò'oûa§¿ˆA¡±ÃÔºu§F©ÍšÍIÄ%’ øó\ß¡HJU„äÆïôê5´÷¶m¿"´ó‡ë¨ÔG„‘fÔTª0òPkPõÒÒA±¡x{5îà¡5òÝ–žõ²ÄS±4£&šŠqÅJÔ•!z‰n¢Ÿ»ð`uà§M;U%ÙÝæè*{ÊÖåïɳ –n€·>ûŽ•((±¿&\ 9Ö~*3rÚØ\•pôä°¹ì±ôBæ]“¢jüÇx"r Š¿=…ºZëã/‡Sº¦ª´ëó7ªÒOb4Bi¬ÜyÛ÷‡vÈ߈ã$¯/ËàÑ£'€ kŠ:(ºúXâ©)DK/󷢺ôž#/XN/ÑíŸc®)"þ½ÞhðLŠg&åÑC)E÷ÆÝyêNŽH(É[ºDí2Yƒ;%Hêãv»ÕmÒÁÆÃýWF€ÕÚIEøFcÙÅZ•?cP>FVbxš.‚ #U†„$'Ñìh÷1SyÆöð3–×Òu;Ômâ´ë(XF…•;µ%Þö5+u¯cŒKLNúKîâ!}hsDÑfµZ@ÚLx‘ô§ÁyÑVáÒÓ  Ì;“Â] v4Fu ï4ê¤8ᤡe¸¸ÕYx¢lTDZH7—¦„5«Tö¥eö¨.{Êi¸åÏR^·c?4w}–(玭¦¾×ÚH²Bz-òN-QaåNmˆ·ýðÚOh_)ŠùÒkn èôMp›qÔr¾*m¢.íÚÛî€ù é—¦øh,<ÆVÏ$¢¬Ã&{´Ô³*ö‘”hu*mHcÎÁªŠPfÑš·ú ‹Õ²"íeOø„[þ$=8zò,|‹»ÅHYõ¹s}À®Iš»qùÉfø·Š“4„ß)8Vî¼í‚VÀ~“B ³–Ô-Gàí A¤=mDcr“&WbÚD7gR"]a¦× ™êÐh'«¢´«§°Ä†›æMuf¨-̲Pƒ“ ²v{®¸Ô+æ´cÖ"ý†«ùdŸq%|£Ù…Sþ”WR_º.\Š»­}¢9«ç¥M²:hêGOƒÃxd1JY¹ó¶^˜ƒñ@ãÓG1Œ¦hI­ÉÚƒ‰#¢~‰6¢QÔé[cÂL’ҠǽˆÄtaѶKw9“âÀežR¼ÈÔ}´;;Zù,Â¥ ê`‰~Êw¡!Àê@!š¼…²§\†RþlyãLAü†[ì¡ÒªK >9¶-ù£3•h¢¨ž+wjC¼íkVh¼ ½ªX&AŸf IºÕŽhDÓù$>'º¹^JT—Vuâ,“B6ùRÇFþ.JVP1/ÚÆr9%µCfË=”î‚CÀ·8nÍÎa ŽŠà}‡Rþ”WªçÛ÷¬ÜyÛ×´i¼ A¾œQÁ³ˆcÆ©´2Úô¸3E ¡ ¶°ØlŠÆ&ZÇÆã¼UK>âRïÞP¤ÒMô³¼Ô;Q1FÃMåObI ¢ü= ZTF&åLa)¡¡Â†âŒŠ 3t‚ÏúQ—?UIJívS|Ë·}Íj1$ìR{lR1ä` Z‰n–‡˜¡]³ŒÑˆ,“Båáí¬°Ã§™½ÇŒ#æ„þ昢;ÊføÅPÉ{ ¢ü)‰Ïãq©–d£¬B&§¹ã ¶Y²õ³pÉ®\²x¾6QQî¼í‡ ~õ€¾|,޾ôWÏÿ•ÄbE HogK æUF{Pæž«!p¾­Z€(øLùSþ<Ë=ð°@½B¿Æ¾K  µc¤ÆP’¢º=°ökåLÙ×C)Çò@Ïh¯Øx’¡"Ðà™”Páá8çCÀ T¦à|þêúw“⑦ÄYp@Wð´Ê8Ôš„ N\’û ô.Y£âÛ¹iœzR3e¥åéÈZNÙ¯+ ´ ¯nãˆ%”ºE‚Ç^w4X&¥.fOoއ'†X½¥‘'Â'·&À/÷&y¿ý±›¾º=QóÅںȗèú %f)6K†îizèÒDÿ“¿OH†µ÷%Á+WÇòª{ÿÆx˜8À¢9¢µå…„„´4HÒ”f)ñ‡ƒâД†:™`ñI€‚ÝÑKïJ„Íã“aÝýÉðæµq@ K«[Ƨ@‡F´ôžëVºt(JÂM£z½/=èuzõdäó8XV¡Rå›1©ßý_"|~›çtg-óÏÒ {]äÇ7þŸ5gG›w¾.»kj‰ˆÐqðd¸æ‘ÃÞkÔÄ}*YƸT¸ê\ˆoÔ)@2ö¦y~N™{ ˤ Ô¸+® p©æ¹_º†´4À¿ÛÔßú4ÕÃçLÀ޹r]ÿ2Ç ‰&nìL[òÃs´“˜»P`†Ö7Ìý-°ù¸²O¹á¢ìÉ—à†O‹`Âw%py[#Ð Fîv¸·¯ ˆ© ×1Úé¨kß,Ee[9<| ájóGKH 6ÃÜM6 “=vþ±ÅÃ>(„û¿-†KZá¶n&8S&Ãç»ìðÄàð™´ñƒá”¡9äÛeX»¿ mîò‘“¦øb|~a¨šæéáfhìSÎZæß—~–^Ü©Rû^!“$ˆè8d ìÛøvÔ2Ì àÄfX¿pŒçú|¬·³ô4Ýù_è4ôéÓRz”|5¡=ÞzlnCZËððà ©&E¿ò  Û$–7îæ“œpÙJÁQR¶´×a» ±·«šJ«[ªnîb„/“¡8yaN‹ü×IÛo>¨W§Ywlct}÷iKRiHÅgÀ¦ÇcF ?è¨(@óN7VËýR o4§@iÁ8w|Sµøl™ #î]ñ;CI~ôšà¯F8ÿ  áŒzš+‘ti¢‡œ3hk¥ÜÑ€D"J¢«¹§ÝÐgÛÆ w±º&±ü¡}ЍêFì<]Qþ,?‰&º!Ãú;JY˜ËF³+Ö—H9ÒÓ‘[Ñét ╯$ƒC¡GéZheß"ÎTÃqÄç¢äHòè´LÔÁÕ ër+~àË]¦íD‰¬JZº4Ö&ÿźØŽRQ!¾ä?"©º¼å/OVÉß/¡«š=öšÏ_/¯±Ú¬ä´Î¥ÈÐK|jW(Êß…EéiSK $6î=¯š­zü2¢Kèö’ãà²@b“n ž•@Ц' 4µõgÒ Ð4^€ƒuö %UÂÒ:I„½g«l1 ÃKvÛ$œ-S Äé3J—£‘5Ò 'KeX¸³B²rËP‹ð›V‹DQÕwIL@‹YeBü‚‡I¡]#̼* N!“|ü”¿8 ¢}´uäÀ~sWÈ3µ÷ü¼ßÚaþ«Öý›p9ó‹ÍªçÕ_mo«h‡°´NÖŽ](ᤱ5´p†¿ÔAã3˜Í&u)×W‘vD[ÐEÛ”m6=+O=?aåxPoQ…è¼yöõP5ÿ´ôw¢XV¥i“W_ÚÒ:ÿ¾´4”縤öªä„åçìÑ_!.å"С˜¬Ó§¡óÐgá§^ ²Û#$)‹5¹-óÎïá÷¤XÍA!wg‰¸S%4Óh:“ÒgâÓ/O„Æ’ 1!¾ÖA*š^ýx1lݹg׿_ù1ô/’Ó⢠’M}rR$‹¢žž´ã•~çcS_ïÝ­c×'îº_CwžÁÊŽƒ•ÞÿF; B‹‹ÊÑ_ÙOjÞé:¸}a1¸|ÎW"¿é&RŽj’¢ ¼Í`µZ )Þ =š;`Óá"°ãR—«»þh¦üoñ‘‘Ÿ·7Ú`þf;tÁey×ÅC›düm¥gW忹ÆùÏ×7…fŽƒp<¿ÛCÑéĈìöñÍ'\ƺ¯Ÿ&.*ö(-M6‹ªÄ´]²Ê'1áæ¿.ÚþöœýÛ>šóÂXt¡¶ûjít¶SŒ¯+Õ‡`%¡9§…]SuSÊŸ-‰­aøŸWCúE×z—ƒìÅÇÀœÐ‚yç÷ Èõ¤1 ê©RÏ º)êâh×ù<…3lîb’”$Í×Ý×× ·v5¡âhI5¦)–ÿÉò:㦮ž=L .Iá“ÉV‹™Z+dŸt€Í-ÀK/8Œ§ ‡ê(ÿ¤SÕc¶ý¤~A•¡­*æ;„•Öùw‰EôB´¨KæñéÚÍ ç›ÿ!-õ`ƬÒ.®ïqgÏØîF åkÜÑÇ\]äŸÅÝPîö’`ŠK÷›ZÞq;K*INÌ é¨_Ds+î.48“@‰—¹•á =“@\“;X™N4š¸ŸÐ ¥Ú^‡ ¬äþ€;YžD)ÊÛ¸“'·ÞöÅ_t1Gåð\d…S´-—¶éÒ2IR¶·s¥T>íÇLíi!ÝIBÐ%%Ì‘Â(IâKZpw“õµ*”3hyìP¶ù7Èžå4QPÔÃBÙéàÁì|bô{÷Íÿ';œ0è…ÞëCTž§%ÝKp§su‘wC¹—ìÇåŠåÇ6½ïkR°$´T·#ëñäWov­Iípyh¿÷?\8Tô¬NžCÊ))ÍvEÂ@\SÇæpW×a$<÷]ì;‡çâ Ô ;–ë†<7ü_“*)›vie}„®å»‘z¤êaõÑ •H䆤)dO„¤)'Ï•ÀŽƒ§àˆ©#œ6„/"'dt‹j´Œ”gû5ÓÃä!hŒŒÛQÜD;hë59Zæ¢í×»|”̵È‚T¨ê¿Ð.#—ËQIŠoþ]]Ì9ñ™^‹žou•–^C¹ŸÎ†foÀ2թʳiíGA§ÁSAD”ü¼µ°}Ù$8wl½š]s|30ZAñé %û<A À™”ÁzmD|xc"¼»É®Úƒ`Áh]ž.æÈÀÓŸû˜aÖ¯¸7“»@‘C†·9`ê"lÀ}¾c*7fÛán ikLÍj_‰I¡‹$*«·í·h†ƒæ.Âxún·D%Ù[pykáNüuEüã#IJUebÂhù~'ìÆÝ@ZºTW$[ŒÈz,ëÒRO$¤(”‡ªù÷Íלu6 ‹¹ºÊ?‹¿¡Üíþ .ø(´èúGÕÊÆ/oǬ¡nšZ\xR~Û÷Ÿ'÷-†âüœ†’}ž àË=‚µí„ßíqÀºÖn¤íZÜ–¹÷œ»’“àÞ¢wQRзÝv>4ív”²¼‰J¥ÌžN$³Dƒ¶Ýá‚=GNAž±-ÈhÉS G¶^Âí¶c°îûj¦TeP¡aÃKÛè‘A¯`ÚµH?Mä[¥bh†æg™‘3-â 4Žšò_5|]å¿j: á]‘ݳz´ì~f‡Õ*d@«0(FkchÒf8ä®y¾!d›ç!¸$%Ðhy>÷ Î:éâ®a!@òèÿTèÔ”»'–DæÜœªéÓà-ã’Ô±3ž-LEúäª^Âzÿé€ èªÍÑ6í«?®YÊT[ØÚ~kgß…öˆhžbõØ‚Ai“ÕNËßê3ÿZæ#šâ:µÐU›s–åê.­Í ÿ­# ÍT+JAb™çî1ØJçD»£Sl åÛ+Y¢æh¥ág@} DÙŽ¡”?-Жk§“1Èlv­%]mì¹è> mÑê-Z fçøÐ–k*Ûš+wÏ·ýšp ò;)Þx/·ä¶¹£G'Ȉ"éÝ¥˜I–höà¥=’éó´ÂC Á2)þ:0ü&Y[QtxðûM4š••týåÇhþ•!à‹YœÙf9²z"ŒŽ`ï¡”?IRh[n‚ÅsD-ĺKqŸV ÑѪ,êöjÚbm0Ô~Рo¹3 xÛgH„|¯ÆŒ8í<Æ)©Ú÷S¨£€v%Qq9g«DõtW¡÷‚}m°LŠ¿µp÷8p¦Z»ØÚ_ØH}#ÚôHcœ‘¯Äi‰y¢Õ„åŽÇDqÙS~C-äQ@BiŠ­#§áIÈ-<óF-AŒ`\&ÙÝðÌ:Y¢Cc2RgB&Åf“ŒF<¿‡Ž ƒ‹t¼íTÞìe¥§JœàTÌA„ЬW¢M¥ÑVv2²)óÔ´B Á3)4«bW£8< ;óƮ譯*mHczJœ—n­ ûB‡Ê¿MzJÔ—=•O¨åÏJI/¥WÛT”•B4‡‹.¥@ýJV—=/£%ã,gU/bVŒê²^ÝÉT[þX»§;oûµ!ÔoØ;y–MïÎÙ†/ÂIwç "ˆ¤g¢h<²oïFFw$Óçi…@ƒfR¼jÓÖÌD‹OÞÄsx\GÃG®Žb ÚH¢q¢©®`°ê(¹-«iÉñªNC4—=†åß:- Ú4‰ƒ¶¨ÏA¦äcÉ5s‚¾Å?ƒ¥^í%hoV™«™¼è #²s>“ø¬Ü© ñ¶¯Y ð2(£¼cÓº}’ËQxÄÝ›¾G¥#Úd·óÜæ_~¢ã“}éJz9QÕh°LŠ·“B‰0uht:,ÝÓÑdy JR’p[c´9¢‰hkÛÈ쥗tY^¢Þh§‡áFRÙwk™µeOX†Sþ”WŒ)Ÿ¤TÚ»MH±ê¡“m+t(ÛöIÈuYÖž+”ê:ý‹WB§²ß!^tA{dP'X!Ï顳«ã‘Y±¨Ë>tFåµ&WµÜyÛ¯ © ¾ûðdG½Žì߷씫ƒpÆÝ&¨È"á™h"ÚŽ>ü-£ï¾ùˆ<0¨¹¥‡q4÷tÚĜ輻š'à”¦tT;nm N…“g,'šhWOû´D•^¢›®Ú:äpÒ¼ÂúÖnmRÁ„J—m4hGOÙS9„[þĈ‰øLã{Îï1A¿ÖÉ@çµpî‡ÅË! 2†Œ‹—Š WɯЙ-Ñ2¤h±g÷Ò Ð§äVøª’,@š”$€”xdN1IJL„ĤDHˆWÍý‘A¡>çk¾åÎvñ¶v ¢Ås‚wÚ*ç^³èÛå’Ë^¸Í~ƒìVp9=JÑB4Énǹ_¾ýê;¢/Æ\Q>¸‹,“ÂfSE¯*ÛÑZ¶R[ÆÉ'Aײ-QSLD ÑÔ© îB1¨¨Šµ=[-)?܇@Õ:`A}†~mS Þ®e¿Yû·üiP¦úBuÜ‚'![ðüžx< ¹sztÀð  º¢"êࢥ¸ ”‰ÒYHÛÖ±/oä>©úï‡Ò Úe“î:V¹ÄïÀ?´±å‚—rÒ0þt÷1H“NC²Î ÍMNèàFƒm&”œ$@RR$'%AR22)(I!3*ΞOŠBÔT-wjK¼í^N5ødèiÀ§½îβ²Û–Õ¿ü§HNƒßícüW¨"¬ËÏD Ñ´uÝÚ””à©”z£ÂòR—$ð¸5B Ú¶”WŒBÝÑ$;(´Í˜vñ’¬à£+ŽüKH-ÜG3N¢Ö(éNôÝD( ã®>ˆF&…ÓcÅ6‡P† …õBužŽJR¶#û ñqqjíÄY4uОÎÙŸs¡è™bhrèÂvçÀ“d%AˆÈ?Y%crÔDH”ž¬wBªEDæÄ¬ÒFbí8sª=•I L¼]F„#ái++¥-MÔ@ÙU[°HÿÆhRˆN-ÊŸ2P[hŽu N_ G 8ðbÙ“½§:({¢#åÏfïUÇÞiË®ºÜ‰mÀn·ãUΨãrY©%Û*ÄÐ#¦†-Áx¤x:j/V4¹Ÿ`q‚ 1,´¹¡ØéF†ä¸qMŒÙ ‚„ƒÞQ"“HR´žl4¢16$ŒbD¬ 2%LW¤ü^ÞNͨSCôR»¥ 1KDW0®¶r¿Û¾Óf;ø±6 ”¾~)< òL’B uÐT㨣·¯ÿuÇž­¿ï|õèKÜ;;eì€5H1 ¥ŠY( ‚ÓS¡Ðs8ŽÌñ“µ[2Ô†D n§½¤ð𯇗-ïa2¸!% †Ý=JÜþÑùCL‡èô•¤x*{8ð°C@›X;rÕFtòèáÕ‰?¶}o êÑ>¬Ø=3IÏL$)$æ÷=Òï:Z"Ex#”ˆbß 6OyÁ¡ì€ÃuW¥cDzA†xƒ4õ­Šæ±Ã&¦„ñ*W¤ÖéƒíœýÑJ8â $Ù“K[4hÚ¬â\~÷$Òß|鑉Îí.ºM‹ò§Œœ¯Ð ˜††b»%8×rÊXGPÀz²ôKÆ(’åÏbT(ßÞ¼ã oA)†ÍnS™Öw'^$Q$f K´R½Óã2 I/„ræÀíÆŽH’Ęñ²â²Mc ïiKže%‰Â{âñl…¦8HŠCL71(ª­URBKš¤gBŒ-ùxví¨’•r&=¡øBqÞ¼#zÛ?º¿’13VµC•úÒë }’PÈ™ú)ÛíeÒÊoþ´àçÝ{·jÓ©s'k|b²ÉjIÐëËÏmÀá8·§²e8—)9pîОœÜ=;¶Æøœ·_)Ü—[Q¾Ó«ƒ2î>£nõûßJôÑËõQ½žÂF“â¨V~÷Åö½úäoÌÞß™”Ðz¨r@=6JS°£’±¥N• :žNÌÓ‰ÚvUÑÐHçŸ`‡M7›]jQ6D‡ï A3uÒ´ÄCbPh7=ÓL“èõ·‡Bnjº@IDATCâ¨È’T´}ãڽ̈́˜È“aNôá†e4Ð]¥èxŨB¤;1Üò'â­F£½Ò¸µ«‘,O~=Ë„j/W' …ÃaQ™Æ 0©å•Â1¬£BïÔˆ™!ÅŽW™Í¦>Ó²Ib$ɃIRÈx…ñ¶-d>H*BušÒ§G$E¡wÒ3¡v@iéÔÉÛr_ÑV©\,/zÛWd¹`Óªw•·/ßö ¼¬’4…bPXÍZV¡e ÃÞì­ûð:€Ïä—ùÃǰÑAéù¦iÿvøæc·Ê,ÐÔúK{ʯ56›.}ùêR7ë÷ÂNœG9¢‰I¡\S…Sª3Ç}ºCÞsøtl"¬Ã4)žµzŠ ûQU߃DÊÔYzÖêQW…vD Â!›]’ßp˜ê(É‘èЉ¤#CâwÏú»ªƒB;9Q—{PÚã‘¢·¯&âçá·cïQáðÞ=KðgšIÐ¥bŒwÂ;œ·Ü‘•F¢pÕ¢ü)ƒõUê«ü=4cTØÎDƒ¤‰ÄœÃî‘„T,÷P8ª«´ÔÃô¡T&Ã8¬¸ÔƒËEVdT¨½8‘I!æ…˜ÆÐy“ų%š–šhY‡˜’äXqŠË–sè7&-¤fBeDáéÒÊÕW¹ý,õÝö:ð5’£e»gŒI'Xaù2/ôÝ‚—ª«‚w&i!F… ãy î?¥CŽh`ý1E”¦­Ô&•üô›nò=£äð¸®Fx®hêà>Î_mÙrøsŽ’îb hbR¨ò±Šî^õõŸŽ™0ñŽO¯KyúÞD#Ž£ÎŠ˜r¬#%³w­žf‰¤P[>“öíxÃI×7=oÇ­JQ<»H MË=$ §‹¢“è ×9] ~hÐÈ]´fÑ7K1>šýÐÅ:,†y¸I…žÑ@ŒÑ¥ÒHô¶¾¨ÃÕH¢åOÖW`õ-ÒåÏòLùq·-ŸP½¦:æaN¹@) 1!Œ'Zé"É{¦ß(œÓåDý-”Æ ³BRzgÛœ‰IaŽÒ#G]êÁ6æ™Þ‰gOU]J§.]}•;å‰òæÁ£œi‹pÛÇm=…hÌì+$EëvONÌ9jÃì˜Òÿ &E•¦à:^ƨhQØ,=Ög”¦ªÌ».[:tõ Ýøžmäÿª|°º6;öþ>/îb hbR:ªèê@uöì©âÜß7¿,ôðÂGß®Vî3<¬ÊÍ: bÔgì<< TæCñ31(jNjʪ¼|×ë¼±ŸÚ±¾—¥M湩ãf5í\ðˆÁ=J…ô=Üux_" ·c§ `Ëš•••“òu t±«bdñ ùgo¹—Óç z‰nqø•kQþ”%V‘ªõ]þ¾ÅH%9b¨N3ÉÕkVÇéwˆÍþÉÑ»Ç?*ÂbÛ!Çeq«ÌI…~—'>5@ybȘ4†Â‘Ä„INè7¢‡ÑÄÂÕÕ=ÒåNù¨ï²gmÛú5ï°¶¯e»gŒ1¾ÏÔ¿ÐvgbP|•j«2)¡ôç¬fé1&…òŘ#bTìÏýSZºàïâd½¨¼FeÌøý‹f‰[GO‘ßÂß¹‹!ÂfR°¶c~›i”g¬¨âQ¥³ÿòÃ7«RRþ‰Ï÷àÜ}Ã0!‰ uVL„í™Ýx¶'“4…f„´NïYc'18®³Óöâ¾8Ò”£4Õ %œ¬ã¦›) cBw¨»üCê¤6í: ìÛ¹íËßVÿLæU©ñª ï„/áMLŠ·ÜËé´Ýh$Šfc´(ŒG-‹HÖú(Êgm޵ƒÚüTýMi;³gÙÈ@ ¼IV¥1R¹Þk'Þüb½ÑX«ó¦Ä#Uˆsâ›–gº_(mÿÐî]ŸnX±l=âPW힘j·¾L[z!…Z¶ë‡ºˆ1a>†ä(-vQÚt1 ,¥M}Ý]cÿ&¿ùý,±?r(wá;qã¯.zYÿÛè©î5ê{8ÿ!ƒç…a3)‚"äú–Üy}±JO•ŒVé¢Oþõé5·ßEtÞ}<ÿœrû¨Á¢:*žÎªb­^eN°Ãeëô4Ãd"ìp™Ê5uŒØ?zg—$NWÕµϬ’h ב-ñe_ö¶¯úê‹1Îh·P­Ü‘fu&öÓ—H?íl¸Q«ò'Œ=åAVÝÖV¦‘*Ê[]8ʇG2B»tÕ$1¾m„üz.SXñ~ý7o‘*w¢“òM.ReïÛöäd/Xþù‰Á')J]Û ¡Éµaº3†úlÆœ0…­a‡S(r,Mƨ0f…½«¿[›´_vúP7¤®?ö餅K_²ö¿ú©2·Âq41ßNÚ=)tCÂ.ö7ìŽ^‚vŒI¡;»ˆ!aûæxÙë–Ö.»}þª~`}Û¶Ý.ë>6›úÀ Ýƒ/¼"•¢¹pE˜>fÆóAGÀ…@Ø’Ü­²Ä-»gÊeòu˜ò¿ƒJÝ¿gªlÄÓ£wþÆËVïܰ>gèµ×Ý€Ûi¯ÂÁ8 ;%ÁbR’“â³ hàª2$UßCI‚uV,lÕwö=Ø;™Ž'ˬdø é$É]|0g窵K­°ÙJiŽfQÄ”ÐEÌ áI¸¾„s49¿åŽª³/d¸¶ìþ}Ëž!£FÄ|^†åŸXåO€T-óªïÁ‚Vµ¼«¾÷_7T-çªï¡¤Zµ¬«¾‡'…ñ×öíÎùiíâï—â¡襾Ú=cXÿ–U-úiJƒ9–{¯v¿ê1ÛáféÇ‚,-CÏzdm>¸ëmô’"-u(•õ8öUKŒÐ-* ŒË˜q¼;æge\«…40Ñš&‰üãñJÄ+/’¬ÐZ`\—>ý;µîع‡ÅßÄh1'éõŽ.šäã ‡gÜ86[¡­¤ä,vP9»·ÿv §¥2Òt'†„ꬨÓ"&…˜úü°N£Æ·Ü‘Rª†N=û¶mÓ©KK||#“Å’„ÆÇB7M5Ùç„pCÛ¾Û~‘­´$ÿðîܹ۶ìDZÚîËt˜¾¾YœˆcD…â,š¸¸nªLÌJPn|FÖ(gïñ^ÖôVAäžCB@“A}Ü´¬É8ÿœ…ZÃçÍÌX%Õ‘øŸ1*´ôCÌ 1+Ä´ÐE[Üêb>FŽÍ&ˆÙ ‹Öb‰ù I éóCB1'ĬÓÂò­Ž—{´– §+h¨í>"Ø¢"íGL‘U†°¿Ô F‘v´¬ËpKÅϸ¢5å½™¯D„è <M˜”ÌÌÌÇÜGsQQ R‡ÌÏœPõÄÍP`&ÚØÌš˜:ž.bPˆi!&EUã)i‘Mò„ñÄ‚£‹1(´„ä(ªÒ1¾“BeA1/L‚Â::üuŽ—{Ô '(Êhˆí>"¯ø ­iW“"­š ' Š% EÚñ™ó¬ŠëôZÔ2Hn®oÙ93ó^šôqWÇh6 Ï˜q52)‹0Â/æeM» ×^µÙ€EL)Ó³BŒ cPè¾ûcR4ËÆmŽa[µ³"E0bF¨ñ°m‡ôNß™ ‹Ÿ¢ÖñrÚ¢á„Õ#¬í6ÔvhCQ¤Eý$aBÆÌÏø18´žŸ5}iDˆå‰h+u?=ëqEVæ ~ÊÿÀzF*&’°eZ"Ƅ݃B¿ÓàÆ.|lÐŽ:*v±mwĈCBv§gú$.±æx¹ÇZ‰qzëÖæéÞPÛ}]c¨H;4ó(Òbj¢ üãÚ©ò8 “\§?D=”? ¢0iþŒŒ×üùãßêÍ¥ F^AnsÚPyTC6»&f„._æ„1(4¨‘Ó<_žh£ê?uRäˆùðí°¨ã"f…u`ìwü“Ž—{L'ºŽ¸PÚ}ÁWm Š´xCuGÛ„“9ƒR_¤žêd0§¥ÌÀ{¸M«5.û,Æë?¢Uü^#;*D3¸ˆ)¡‹}cùawü©Á:ÖYÑ]İ‹}k(°2fe~¡–{C)OžÐ¸ÐÚ}h(ÊŸ"í·ÒŒžmÆÊŸp™çT°=ŒÑãK<‚ª±·:ÌI™6Ï•÷0z{ ‡Ð–XШ-œTå&J š9\bòÆ…"¹:Ë“7‘(yÀe5oÆQ %TEŽŒ µÜ#‡0O)¸ÐÛ½–e¢ÝÂpË;}ãÅ3´{ú⤕% 697«ÂQN^oahñW’ÕõàâŠÈÈöàôúK²4 IkƒLJsLT­Á‘Ê}s8ŽG@[âugMÃ-sûé;é:‚MN:¼ÎõÈØWþ:c½¶)ñØBA "LJ(„ñ0ŽG€#ÀˆÁ(ÒF‚žF´®ÏG€#Ààp.X®â^‰[|&1dE¹_U¬eø½Þà’”zƒž'Ìàp8Ñ„€?EÚ`,ÒFS^ -\’ÒPJ’çƒ#ÀàpÂBÀÚ¤õxT˜ÝL‘àîTƒÒÂ¥/Y›‡)œI >˜#Ààp #ï=h7šÌcp‰á´š'ÒÝ`û<{AwU©¶¡ä3–òÁ™”X*-N+G€#ÀàÔ)W=f;,ˆº±È¨QL2B5øðÁ]AŸ–\§D^@‘s&å*lžUŽG€#À8?\‘öüEÊWœÒ<ŽG€#Àˆ)¸"mý—¤Ôp 8ŽG è5koš/\‘ÖúyæLJýàÎSåp8(B ûì½BÙr_’¸"­/õóÌ™”úÁ§Êàp8Q‚$(HŠ<Ý«UI⊴U‰ì;gR"‹7O#Ààp¢ ®³·C’œ»žèö/¤ùS¤ýá%ñ!~ù7màLжxòØ8ŽG †èûÆžTá9½Nx@*N–¯š…ÑSä·ÐtþÇì;–ûÚ¢—õ—°w~¯8“R7¸òX9ŽG °9ì¯ kò“zîbäö™s ¹ë¬í÷u%{ûFw®Hë‹FdžùäÈàÌSáp8(C Ë¬í#@P楈)}J%ƒèÎÜ ߎ&ñ‡ ,Ax/gr%¾d/{ÝÒÚe·oB#o(A Àº6íº ï>6Ûéë?kƒgR´Á‘ÇÂàp81„@÷ÙFé°´Iñ”þxXÏhdJVãógbü&÷¾.ŤPÛãÕì^ÙOöØê›µféG(²´ =}á×N•I¯…;àLŠÆ€òè8ŽG úè2gÛàg”…ü„ÌÈg}—¿OjWÀ(ïü~Nœs}„†ñG5kœÐhå½íìì7º/š%>¬ÈÊ›ì›ÂÄkŸ’ßaïü® œIÑG G€#ÀàÄĈĤ*Ù]fïêŠk!~ß‚WçœÉ½†TõCïÜ"­?T´ýÆgµÅ“ÇÆàp81‚€eûõ »—áÎdÔKAÉŠ@ÌŠ_Çiý¢éGΤh 'Œ#ÀàpbÒ?é:{ÛtÔMyA0ê®Ü5©Ç2`Œº™n‘¶îKš3)u1O#Ààp¢^³NÄu™½ã Eú*)Æ¡»ë¶§ëkÛ#É'vMîvˆHÏT·%g¡¿[}³Â-Òú¢¡ý3gR´Ç”ÇÈàp81„€Ô&ß%*ðÝ®É=nõ.¹Å? 2¬*Eé?o_Ò§¯ìøwótÑ™…J[’)›ªEZAx‚eYV”û¹EZ†Fxw®8~<4G€#Àà4@ІÊ!ƒ^)K:³¤H_àöäv=Ùã²JÛÿ•Ü&.£hÞöhÇ£¾Y犴¾hhóÌ%)ÚàÈcáp8‚@÷W³¢‘·|—,tsƒ¼Xt“r&÷|ž”n³³û–‚c£Óe»®jv¹"mUDˆ$åÁé/ô—di’ÛÏ;hމ&„O:#Ààp´D—3ŠE8†qÒ‰º%ïÎxf³–ñÇJ\(E™¥p=(àÔëÄ[³Ÿè¾—hï6{û’¢ÌÆ]?"Óò…¿üp‹´þP ý[1)™™˜ó\y#7útKAá$2)Ç0Ñ’ÐIæ!9ŽG .@&%™”æØo7EÓð¨’G±ß~½…¡Å[™™÷V2fVéGKœgm›ƒŒH+U¼'{b÷’+ýñMÛgá·ët‚xsöäî;k£•[¤­ à~«&e|ÆŒ«‘Œ÷°’·FñØb¼þ#ZÅïß}æ™sÁ‘Ç}s8Ž@¤xð…Rä2ù:Ü–û'¼®ÁIæa¤aÜü¬éK#MK}¤GLÉŠ 1ý“Rp.ÀñÌfÑÇÿÉ×*mm´-zY|±{ƒùáiÁÝ5gRÆOÏz\‘á\ºÛù£óff¬ Ž$î›#Ààp¢ Ó².C{!dþ½» Âäù32^‹ÚꚎîs²ûH’ô%Žeÿ¹mr¿f B¸ãŠ´cU“OMg= Š‚b2øôiC9ƒRìü;G€#Àˆ ¨ ©C¨_dzjæP?”‡O¥¤ˆZœ}r×”ž51(=çlkßuÎÎ~\ èª¦Èi«"ü»f’ZâAƒ}‹¨"ÏËšv‰É‚'‡‡àp8hD€,²NȘùvìc°{}¡,ýœ¯,ºÌÞ>õ.Âå ~xá’Ü)½&ù†áŠ´¾hÿ¬‰$…”d1é÷h‰ ©÷p%ø‚à!8Ž@4# öëØ¿#Ùx½WÞïG3É¡-çÉž¿  ,D½+ˆºUåi«"Ü»&L íâQ•dQe~愲àHà¾9ŽG  þ T…ÖêîÍX ºid¦òeEx^ÑFå>ÙýÉù³H‹ŠµúóË¿UF@&…¶#—½˜ë T—¿q8††€ª£‚ý½j^¢¡e.ˆüô™s Y5•/(=“MæÁ¹wÉ­-øè©ò›x²òÇ~”×½¬¿¤â?ùC l&… µyì ÿñ—ÿÆàp8 œ”þ‡ú}µÿoXY (7Ý_Éîf—Š×ãn§ »žìyóúG;±€]^Ù>¦Ë+Û>éüÊöÈûNw®Hë‹F`Ïa3)dI– µ‘”À’ä¾8ŽG – þžúýrKⱜ• iÇ<ÍÜŠ´DtSPå类ÆÒûÕÜh©TEÝg¸mùnE^ØãµíMY"#ï=h7šÌcpƒÉiõ›én°}ž½ »‘ùá÷Ê„ͤ`tmÈ’,7ÔVXþÆàp*jý>æ¯MCÍcMùÚ5©ÛqÁbèã«BÛí²ã´Ñ» %+#v>Ñýëœ)=¾EIË?ÝnëW¤õEãüÏa3)t™º?RÜG€#Àà4¨ß§þ¿¡ä'˜|ä<Ü5Ÿùï?O18ÝÂw¢ <ƒ’4dêc~CQz ÄéóËî\‘–!qþ{ØL Š­ðâgñœkîƒ#Àà4¨ß§þ¿Ád(ÄŒØJv ]0å” |æE×Ù;îÆ÷‹›öëñ¹ïwöÌiµßÃfRjžÿʸp—1cõøé3拏–µ~BÆŒûb‰fN+G š@w© Þi[rçÙÛ§ÉŠü7Ю_9Rp“Á·³··ªJ7W¤­ŠHõw}õOü G t˜þü%¨L·OO]ƒg|\ZIôY-Œïâ làü™ýCI)3S33ƒ;C#”t‚ ƒyê-ÈJz°áÆgd-CkžWêtâ5sŸ›†Šw•Ýc3g6-+“Oˆ:áÆyÏe|[ù×0ߥv4Ía¦Êƒs:ñ¿‚$-ï<;û€dúïìíO`æÎ‰:ý°]“º§ŒâéÉ}]Šœ;~Ú‰ LF3ûêFR¤E‹´c\vû&´ä›Š;¦˜"íðîc³ ¤ÐsÂ%)¡cÇCÖ†€—L˜žõPm^Bý-Ïuè‘Ì—Z†>ZÃÉ’2wò¬YqÑJ§‹#À¨Ž)Òê@ü£ H·à&’p×Ï ¹“{¦ïÌ·5¾û|<y7j«”èDØÆ¾Ó+Òú¢Qý™3)Õ1á_´@@<Ú\xá¡iYÕDœáDÿÀôú’}†pâˆÎ°Âפ„X\hŸôqª8šÈžÜ}gÎä^÷æNîy·ï®òO[KŠwü„VzSÒ›Ä_ºãÉžGªÆCŠ´‚(<ɾˊr?·HëAƒ/÷°ZÁïš"`Ðë§¹\ÒH7À»ñõDŽ:W¢`tÎ6ú¢èó4*£}Û\ßòÙÌÌ{íþŒãdÅý=;\®ex¨¥ g.gÐß6ôß<»…~cnü´¬å(g5Í›1í2ße'LÜÄ“ïeM{”üŽÏœgU¤Ó3péj´¢ÙãÜþ4Ó?;ËwY ãû3þ~us}Æ]yÒÌ¿#w M„TSZRÚ›>ê`éúÞÇMÏš2\©3èùÌAßß|ŸEA9ˆ¦µg"£ò·ñÓŸÿtþŒg×ûþîï9Pº)ìø™3›A™ò:>à “RÖô¦ÇÜnG5‘2Å ®Ó/!¦WãVSÄp«N3Þñì/wŽ@`t5{ Û-}†}Êœœ)=ßÈ©%صSä7¾Ÿ%^Œ­ó.7åõ%³õ[G=éþµ–` þ'.IiðE\?A,ÙÁã (ס2éÿŠ Ó²nƼ}ˆá°b.Å0ãòÜy^#’ Û~¨q Ê¢ f)‚øêTÂo7NÊ|µKçÁÌ™½¡‰³—K&üõ…ìû#™o$b·ŠŠr¾ef.0*®Ók‘‘¸ áCLûn´mðêˆü-Ï5s! §Þ¡ †šç~þ5dh®ÁÁû-ü>µ&OŒ} eªN'dÖÆ PÜ2V¡uê ÈìYþÑ¥¦Yÿ`è~¤ÂŸ7Ï€x¬Á/B›ß!ø$J®¬nY^1þ¯3GVøäOŽ@m òì_°Ý,ÄvtO.2(µùe¿UU¤uKÒçK_²6g¿_ˆwΤ\ˆ¥<;ÝNËüÓ>Áq‘"+¯Ï|¥IMÉ>òÆ&E€YøûwïeM¿lîŒiâù qô|ùË'd7ëY”"ûo÷ý®>+BFfæØjË$ô &” ͺ ³¹(•yˆ©jáýPñz73cò[o#íÓ&d>ßÍ¿Wú8ÝHÒõ˜çó²¦îñϨ};óüâYÂ~'sÚï¾~qw•Eb(}¿ógŽG :qñ) Ò' ó§RÝwå/\‘¶2\'¥2üMcæg>}íp<++ðÞÿƒ3óÅ8ø£@6Ê ËíéQ–àeÔy}Ç% \ÁZåü¦·ÑNÁ8p °ã3_l­¸\}uL–Eá8cF»·²¦@.b$JT) ùÅß.Bz¾ g?u¥CÕïzCJ¥¼ÒïŠÒ”‚›IjQé·_ úägÝî³7)n —}”açàùj!ƒ¤» ÒSÍ ”1Ñ|Ä™_Z)n,˜öÈX°¼vÈ2‘´”&JgK[á}/}ãŽ#ÀðÀæ ÍËüÿØWR¤ýa–ø$ö%¤GF )ÒnAp¤ãWÉ=9+Mr9ð =uÈ”‹Ðwœx%b¿FRÒBl½‡qâsŸ`¯»Òœ–ôKMKÔ•"Ž’.I‰’‚hÈd4Óg¼ƒä:d9æ>”ùv<Žx.ßüʞƄ ‰ò.¼ì{á²Ë}¨Cñž¯ÏÈö ­¡é¥n×MȈœM×[Õ\ßy%¦]ì„H7%}p€÷2)j\UèñƯ)æV“¼ý·‡*ìÞØ܇¯ßàÓ@Tì}Ðç§€ßÉœX¢ˆâHçãNQô+µ©Š£7êt›Ño5ÅÞW'M²#6’7>à¡hqˆÝfß2PŸEáiTº×mNûúçÏŽ@Ý @Š´ >ÒNU‘v(KmÂ_g^…¿µÈ œÀþæ#”ÝÑD·oH3ý®Ž­¿5moXßRÿ{‹&ºƒCâ„üÛy;¨eŽ“…Çqò¢ªŸÇ"‹â;—¤Dqá4Òh‡ÌÄé/ÞïT\[\î‚瑱ãàïÍž¨ˆ{%À±R/®›9m‹÷‡ D ,—lŠPâ¶øoÀÑö»ÌLÚ\„Ú·3~Y¸ºT±ÄF* Æ &EP6#sƒ’?NPZc\»ýüRã'Ìé©<1>cf!.½:aú‹¿Î›ñôÖÔðÃ{ÏMû™œOéy^r(ô2ÿÁÑ}ƒµ`AÙýÁ¿¿’Š›Yö ³«ìEFÅ67kÚ‡Þü#À¨H‘¶ìô¡nØoõÇI‹¡\‘¶ÿÂâg&ãÔ“,B¡t‘y¦Û ‰º“Ø–kt:§b†SîNçêžrÜÝuªÓ]ðgÜ`0êÝÌi¨·çqg¾ÔÜ)»;£Á¹6Ø#“£ í»²¦˜ŸóÄ6æ/’w.I‰$ÚpZoÏx:àzWo&’Ä£zÃ(K—‚J›çsÂqòá”$Zvðº¹y8ÖîAº”KQÄ»„£S„op°¿LPT}”íïdNÅ™G¹S„õ8D_]U±wüô{b§ÐwúüļrÇþ,ùk®oþ18²ì^ J \Åɪ{ãp€[F}”ùº`èF¿ôꪆâdÉy¥o”ôŒXü„ŒÊU§eùgܪàïŽ@!àO‘v›ã’_±ošÔÞ¸®ŠM×Á¸†”óÒ`ìÐÒ° Y?…qsQD\œ*»¥%d¾Ð'D™ã3žÛáp9óIþI’åPï}¼>•eé×’ü²b”Ú¬&3S_z)¢ç5qIÊy‹–{Ð fºN3óܹÄÙû(½“¹™SNávݸîúÚ"qŠøþ^¦¸å(ø„»wÞfŠ®Wõì°zٶܳŠ,?‰é“(‘ÞÌ|ꨇFq¹ ò]8ÐÊÖdëRF·Ù÷}™»Ä‚£üݴ͘}§{R²ivQãÅm[Œi?®7÷ʲ«·,¹ßAÝŒœfºKþáë?Ðg²í2>óùÛ—¼ õKhù®@Ã2o>ûìiäÄ“uÇ ¿åºÕe÷¥ƒKÚOᲤr!Y<V±Å(2¸Á%rc8'µÐ¡fp‰œzIa±ãÅñÓ³þÖ\wÉ\&­™rIJ q/Ú  îˆQp‹mU©F?/kÚKhÐì>œÅ‘\ÊzÉ%oGeÛâÝ_PDÔð¸±cÇJ¨£2ýus‚rºy—cD—E Ïž¢É×2' bí*úM'^æ…b|eÊ”R‹U cs0ί].ÇqœE|…"…†8qX8p~æ³9˜§G°³¹sÜô™¡ô‚uïÍÌÀ5iaIÕpÁÐýNæ3{QŸä ¤£½Ë%ÿ¦Î–@B3º;q«´5ÿé§ E³p12’ÙÈÄü[V»1Ü\ºz¢* ü#ÀвÎ=.#ëßà²åɲò® JîNÓïí…ÒÖ-õÛ%êNv9Œ¢ º˜ƒðú%²±þ0$ЧäV†ß„+ã_†Å} t0þ MôU&Å,A¼xÒõ¹ÐÕô\ÿ¦nxÜ<ú=zß<æþeõYYÕ–‘ý&ÆÇŠ©Yˆ‘ ˜h%ÅÖºsÇR~-u—¦$'O½üÔSÅ5ÅGbJÏ¢iM~‚ùNžvÍl5¼w§£*3Làzô Ý4K‹Qž“ù„º,UÙ ,Ð-ß–Û,bÙ•;žŠ%LjËÿMx߯ Ž‹:‰‘eÜ8à2·3lÔµ1l†]ôꨣT~³ß"ÉŠE.´¿¼Þ] èq&%P¤¸?ŽG€#Àð"À™/a=ejY‘_LÕï—ûš¿­"îŽWŠË@«mqÛ¥øA0­j‡I«,ðå­äñp8ŽG è<0bPÚ·*C­ÿŠ…²'æÃ0ËûzTÈMVÀþyfæ }YØ+gR†Š{äp8Ž€6I4žø6êxÈ}L_ ÝJÔ‹1‡Š¼Ð×ò9Ìzwÿê=ÅYËlp&EK4y\ŽG€#À'Às:p™/6/Dë ±Ç °,¦ëw£rí@zœôØØw­îœIÑ IG€#Ààp@@=ºC;Ú›6èhM¬»¶Æ ¢¬éËwì¹Lë¼p&EkDy|ŽG€#À¨ ·„ö¢@‡¦ëkó3¿5ÒñЪ(ݵ&š3)Z#Êããp8Ž@-àW¡ÉÒ¤(”M=ÆV ‰žíþs&E;,yLŽG€#À8/x˜_9“ ZÜ/G€#ÀàpÂF@9i““ÂŽ%ÐDôÆø@½ì¯LN†µewIÈp™ŒÆ„GΤ÷Êàp8pPáçSîŽn<ø4ܨ*…oÞù¸ì®Ux&©ghoÔr\rÇR¸êÁ\¸rÂNЛ’À— W= ñ:U ì üsé©Tn¤CC/SßÊ|Js}¢‰3)Á– ÷Ïàp80DqS1ë;û†Kå ‚h€ŽC¦À¾oà!æ2$§÷…‹oüÎ[ëþw,ŸßÜŽ"p–ž†£;ÿ †>]9‚ ßôà„V†­:à–%E˜7.cÆ‹ua¿Nlí™W¿ÞœþBIr_ƒÚ8­AiŽüf‚_üc­ ÃbAQ5®éDÝ’wg<³¹Ö1ò#¯1RPuHfC­Ûu:J˜ÿÜ´ïÇg<·v‡ýÚôGt bø'7ïtèDËýRÍeûþ!o×BØõó_«åúÀ–¹0âÞõ߸3”äçVû=Ð=Ì‹¡³i…¸Ó1 8/~*Ͻº†ýs áñMLŠ0iÒ«æ"sÉ#z½îq·ìn&ˆ‚’`5ËɉVÁl0p©O %ZÅÝå’ ŠÊ”â2»ˆ˜Îœ0}æqYV^mahñVfæ½ö*Þ£ù•×h.z ­Õíz@'YßAø“ŒkW•ŒkÒÓò½®•~;ÎÉѺ|ˆ.>µ+åïE‘Ôµ{Ö͆n#žG}”88²ýßpîøFõ7{ÉqpÙ ±I·°˜ŠÌ€6Rz›¿“P9ŽwO˜–µhÞÌŒÏÔ„4ø L -ʉ÷LÉe3ÛÞe¡y·öÍ•ÝÛCÏ-«ÙÔ0T 5(¬£P™»2»¶ï̓ÙûÓwì=:ë„tìq »¢ò­g²ŸóËs¨»Žù º Ÿ¡nM.8áÙÜiŽoFK#(>½3œ$«…-–šSa{óÙgÃß®T{¤™JX-S¯Ë‡O”H¿íêA:.A©VÖû@Øß>jN’åæR#áL8ÒuÂ7¯¼~ø¢ÁŸÃB ÊêvXyá"ÈÏš…R©›éGM2wl÷W ¹Ë E×?ªñÉ’¶|?,Ÿ×V~0ö®ŸãM‡¶'ŸÜ·XÕSñ~Ôàá„Ô Ôiz~O$$’–‹G 5–¸„„zth©p jF˜QPPYè ºÇ1**£úlñúf9òàÕˆ’º]0þå‚Fà‘çŸOÅ]=×¶7®Ek­nM°Pd7䬞-»ß†ñUtáĬ žˆ7 £µ14i3r×<ïý¦Åé-”JIz< ù -âcqD’IñÎ’¯¼å¶AH@*n3®@’QÄïõ‚•®¦ßùèS€HÖ –_^?ü®)QP·5Í,öpØ•”‹FúÚfæÔþeh]öŒ³‚)©š€³,V}t)”¬úSX﹎Ëe”†Ô„OʨJàH F¾³ds£´¦W¸_ í T!‡¿ÖTT&“‘h#-Máõ£¾ þH·žëö€0Ïb°( 4¢0F°4*ýrö‡Óî¶¢,Š3Þ|ôQÝhæ"ͤr‹¨×·N°˜nE³r ;"* ²î‹µÅ«¾˜^?Â.IAUê¹nW%‡¿_ÀÐVÊųôW³|ø(ÁPª¤Ä<RsØæ¸AÆ9îúºisµÎP¤,Î3ÄôQÌ:ƒ>-9)Ž/õh]šaƇLjg ‹èì*+*³Ðm4G ¯ÁáÅ}‰@=Öí )åÞ"+ÞN·•æßµh–ø(®Äti$B3òeh¡µ74ÓçÄl–ñDØ`»C–A(À|Wffvýk@!’L ¥EJ³& ÆãY<œI©¡Pêë3•‰äHe))e—ÒŠùúq2¿ÏÍÀ-kM’(OQéN8vº´%^´ºÛ¹?ΔÀeý:Wÿ1†¿ÔcÝŽaÔ8éá"°øUSÉízÈVzæ>ì¼–ÕHY¶­q£’ëg­…FºÃá&ñðÅ ëJï’lr’,èÄ›ç=7uO] &…˜ºhv®DXXõΠL{ûsÈ/(F= €Äx+ô¸¨%Ü1jàá†aãœsð8´mÖÌ&Z½ˆ-‡k¥¬¬èNWÍXÚd¥Uõ£¶¬QÝЭ-Ü<²%oß®ú $Y‚ ·^^é{]½>žÏÿU¢Cz\®³ž{w]w‰úî/ÝìýÇàý¯~†×&ÿôÕ›?1);÷opL aQuÛ_ðo ZÒY4Ûp¥ KÈnçõXñÐ`|…ï?ý«±ñè?‡¼p}Ù ›§³Šž¢üéœÔ6–•lâAwǼçžÕfµŸ|Wï¥üxÒàÍ”i*ˆÔPƒhËbhïŽ0¬o'8w.ß[7…Á=/ +R·$ÁÜ…?ÁÓ÷\é¦èQטIIåDe)•õ#ØÌß{Ó¥uÏÒù!êþ[†C“äxØwô üoÙzèÒ®¹zN“¯Ð¯Kèñ¤ÅŸÿõ­~êvƒ‚g¦fª.éø2&j(rpsÂÛkãGN<Ysà¡ óÍ.E\³²ôÄÁÖOtÑ.Q±É‰°Çy)ìw"Öë$Ž3Ƴ¡ˆX3”•~‰“ÂfÊtW•h¤ ‰qfhß"U½¶î> ›vî÷2)ëwì‡E«‡‚b´kÞþ4z¤¦$‚Í|£¾wkïÙôþW« )Þ 7ïoþw9Ø.˜óÉÀ ã0óá?€Ëí†K7Ào¹‡TIÍåºÁÕƒÕhðÉk¡eÓF(Õ)_~Û o».j™V©"ùB³LÏ÷ªÖÖ4¦Ç7­¨ªÁæó³%ë·qÃ× …­»ÀÏ›s WÇ–°t픪aÄÅ]¼Šb‰·š`ìÕ´`ÃŽ}ðÝ/[á¹ÇÀª-¹°|}6ÇhŒËFMôÂÎå1W78#µƒDÝ) d°Ëñ/·—ϸۂ¬Š ˆóMú„§ßÌ|´(ìÄÎA$˜"Áw Šä ý<Ù÷ü|¶°Žœ8ë@ö£d僯WIZH|Nƒ 1™nQuòÑ¿Ó%yã.*)”¶ƒ™’ÝÛÁžÃ'à\ hœ§úYµelß›÷Üp––ÁÇß­¾[«LOQ©h™ )Þ×]ÚÒG…ô…•—7uüÀÒ£{ÔÕ@óNe©(]c§Ë…Ë&yP†Lí®ˆåDevo¯.–VÞzhCÆöla©šÔÐÞà§ ;áË7bØ‹áË[`ô°^~ {èœ:[„’”Ó`G“åÒ@¢a×cj݈'`÷éÜNŸ+ª¿¸ô¡¦ELÍ>¬ï7è¥Hëâ5Û±^z•M;Àæ]‡àé{¯S¥s®€)­2N?nØUcV#ŽÞ¬®E/…œ²¨G %dè?Ä`Þ=Õ± ¶M¦ªWÝÍÏš¾oü‹/„iæ>çйúAÃ]ãfHOUàóÅ¥Xਫ'ä:FJv%N¯;ë¾RÿZXã:Äü»ýFÉ©Ðø¥ÐÄ2& ìÄî;ƒ35íˆç{Ýÿ+3A’Ç:ÿ%ddZx'ÉÅÆìj§ÝuRF ñH7Öoß§Îrï¾þ5™8‹ ^ÿd)'¡EjÍ[ÆhfÚ ¥"äH'…)PîÀŠfÔM'¨}ß’sÓë©úµ;ÜõÐh0£F‡¥>Ê(êê‡Z8aþ»iD_èŠË/$±[»m¯ÊPôîÔªÖXõ:*yå£àD~¤5J€«yꦿ€K~ݦ~&©È dFÒPâÇ1Ó·\×™çöê'bR|]ÎÁc¨_Ó¥<]ÕÏ{Ÿ„µ™Ÿ–i)È@[1©!)Þ’;_V=Eç¿ú¨Ûщ§*hؒγÄÇÏï\MÌ\mI'0‰Èü§Ÿ.Dbž0ýÅ÷$Å4u¿sÈö9‡ÍB™»±~¿>N< f±gp HŠÊä(”Ò•³rkEVDRIÛˆÒÅ%R£ÌŽ+¡›)4ËôdL6Ùn“¨+ˆâ%Fîˆ[ïâM¦‚W¦LñÌ¢‚F-¼‘dR|(o÷A‰Æ?mB‰I a›TÏ•B‹4³AZ5m¬~ÏÇ™®?&EBMÏÄÝ(YÙ‡âôM;z½ÑNæ:´J‹&…‘U÷è¨ZЦ™§îÄÇyê–¥þœŒõÄ×uhÕTÝCJØãÇŒ¬Q–Âó•ÇD·í&;Ä<æê> %-p”ñŽ×¸j„[~…møíA4|Œá¶S:ã2²Zív\v_±œ¦t5þ($êN”|Ü òœ=à€k$)F7v8÷Ôµ®I@„¡'o¦ ‚¿¨¹´No¤.ñˆüÛU[àânmÔY#Í\7í<äÍêɳÄäztXgUŸI”Îuàli‡}cwZ¢™ñˆþ]à–Ëû³Ï•îQ|Àb$Ê.iTÂ;’/Fƒÿ&Fõ—a9zª²f?-9æ:ÍQr·dívèÛ¥5Y®•ôF‰qªÄ†b_g¨aÇM¿HŸ…ô¡˜£¥Oæúaš?¬Þ $m¹¨e:L½§—Jƒé8_fqDñ½v0£˜pNZdðYÒywé\‡BŒj»t]Ò –âù™“Ï`˜÷Ë/R3üû+©&ƒS°étöyO=UäÑo©óüÓÆM˜þ|î wçÌã®.V‹X$§ê÷‰)âQÐ NT u·?;ä8Õ\©Œúî¶’MIÔaƒÀAMøJŒÓæÏ¨›íÄ•© ìÍXØ`|Q‡à{¶Îý^Ь«6çâŸMpßÍ—©R•åëw¢â.\¦iËÖe«Ë?í[¤©ú-›¦àZýAèŒJˆQ:bC¥Bæ¨Ó§Á„¤'¤˜k1Uý“õÙûTý–¸ƒèÄ™B Æ„q£Ï©R VV‘"¥Çî‘J7ät ŠËPOƒúk…u"GõàGTŽ=~ºŠÊì½ÿ¨78íûèÛÕÈ4·‡[¯è™ó¾‚¥XÙr¤×cùÉüBUWê Ú?ÙºçˆZ«ú©é½KÛæêÒcÿ®mÁ†Ìú.”Ü$–+çfïËC=ŒÄövF ­3G:U±S§ÕõR·Yâü#Ð’Ž½$ÿn\Ò!Ãkš-é„“ýr†Ä;ûÀå!¿Ñ•û›…’˜t’ãz$^{ÄÙgÔaè[MÙ§Û¨(#•A·•D•–¯Ë—üÆ]_#ŤÔWþJ—tIn½bÌûü'uK2-ýüñªA8ƒÝ¦nélœ÷ß2Âk÷dÔÐ^ðÍÊßà©7‰åI’9Ò_!© íæùï’u0{Ò¨ü8>þ~ ¼†z-È«ŒË¸µ’"@ åt÷ãH;˜öM’Ø€èÓï|lêë½»uìúÄ]×àkt;ÚrLÒŽ”kRv¥í›$<ò]Ê‘$Y•ºÄYÌ(mñcý~{õãŰsï¡MÌʺ )!¥Ò ¬ëJÓõ#Üs8ÝX‡ê®PµžÓ¶ù©¯-€;®ŒŠµíU&åç-9ðÕŠÍhîNoýö:Íʧžê6K¾AÞèû£>ß¾K:HìuUÍc`7]XyI'ê³Ôà¬ÿÞ1Ê!­‰A!²kbPè7º$±‰·šégî8*ÑÀ !Uë9)Õ’Î í:W\Цò‹aÚ]i‰ å¾ 6¯Ó¼"Ç",éà$ó-K\ãy ¯¶K'±ˆvš9“í%ÄéãÔÄ€“~Öºmûи\˜Fè„z4W—oÓ¯’x’°8ß’&ðƒ¨Ó½éÏðZ؉óBB€3)!ÁÆq>¤›Å¬*7üÜò6T|—t"½K§¡bÉ|q&%’hó´8ŽG "œwI`®]¾Í—t"R!'™”¡ã9uÀ¿p+2í£bÜq8çG€–td—k¢­ôÌ_pƒNµ]:ƒº¤síd÷ω géœ?vî#Òp&%Òˆóô8A @–hÙy@Aã^9U—t9Á£f* ¨¾K‡v_úwÝ_ͨ“t§·MérÀ¿þ5’Ä4“rMz“}‡v-šÀS÷\ïÅmcö~ødñ:˜ƒGÒsLjuèüÿb}&£W ꮚÀ/Æ ­Þ𬞥h‘–ì—ÐÁ€t0áñÿgïJ࣪®þy³Ïd'! K ì¨à.ˆHAQܗںת­VëZ«~-m­m]û¹´j¿ÚV­û *àÈ"Ê!ì„} $dÏìó¾ó¿“›LV²Ì$“ɽüoÞvï¹ÿ÷òîÿsî9œïgâ‰Çqþ‘@IDAT¹"/Õ 39¢¶* E µ&£ÖûßÓ(äÜ3-ÏÒa‚Ò‹Ó>Ìökþ÷²;c¶nÕ­nMR€4¦C"ÿ’·>6§[¯„U´ä|ºœ³!#ñߟ^ý”ž¹÷‘íxñš­„ðùÈpœ–OÈRl·™9 [}¼h •Ô…¸oM;ê…@wA 5&M3>7ãAßA“Ný6cþ¶!Å[­]«éºÃb´¾±þÞÜè»?à”£¶þŽ£ÐÿÏø—w>²ú¶!eÝ“X•³Û“¾#2¸~ÄA¦NÌÍ® zíÞ"¾0ñ’ÏÍΤóÏËÚ—Þ"@Õ£/~@—;¿J×s<ˆj?<›£Ž ×>[JeNM>PG®!|ÁVr(ó‘ƒûÒÏ?]$&ÌÛ¾ŸC—o¤ýGމLÈhÙoßœ»œ³98›m9åqÈòÌ´Dºé¢³EtÏ`øóe"9bRŒÚŸ.›2¡¶hkg¸…Ü8ÉámWœ#üš“!´Ïêwì 0”§þžuÂ0Ñ¡eë¶s.ÎNœ‘Ì/Ô€È5|`Ž ëeÂr„½å"‘ïÁßûre쀠zÒã€Igî“æ0•øfé4iÒ!í_¤™_˜ñ ›ÃA7mÒþÂæTO•óÑ«œê×íx~6ï¢÷ŒÙÌ ¹úÞÑ/¿óôƱՕΛxû¯=ø. 8òv±mþ<ŽÝÛýgKÖ5Y¯]zκïÚóÈëó×;¯˜ª½=ÿ{At&8Œ–¬ÝÆáë牯ÑsOÉù{6QAM¶2&1pd<ëÄ¡tû•S‰óù|õ&gžÈoïæŒ´c‡à„ƒ B„7ŸýÍZJŽ·ÓÏ; ‡‹>Y´ZC’¶|Ím—O¦Y-¿2çQÙ-Ž!IÚ™ÆÇ&Òè¡Y‚ 4'ƒ¸Hý“جuíV3U:]µýÌæ¬Å(ȱƒç0¡&X`?oª(b˜tæþÅðsÎ¥³Y×ýóùÃô‘a1KçN£!½ßŒ_î ”æ{®¹üW³¶ä“-÷yjó}£AB~åó믃m¹ìŸgiZ@3^àí;fézLŒ‘Í£ýGbâ˜9YßÅ“ÇÓ‚•›©5‚ýXÖP ‹,^ÜÃX“²i×A¡E‘§@{2‘ Ê…O hLFê'r L;m´È²uÏaq*R؃ðœÄç÷JtÐèœ,Z»eˆÌ‰`v²rÖÛ©LnE¤¸‡¶æTV˟ùU|P–©ìc0$+]¨ë‘{yTPVl,Q@o¾xÀÉÜ Js2ˆƒê¿˜D`?/ § Å9!áÈA}õù¥°r'¾YYÏÑaUQtg`Òùü/†gy–Îv‘‰InHüû36éL¿àWúÈ  ¼ô9 9ƒžøÜöÞ£žÌŸº—ë2³™§6ÝÇ–ûÇ|ÈuW&ï yÞ¦{Gåë¤ïyç©çË}jÝ5t{s„ êðoجóÎßתÆå1äY½yж Œ7^âX`bAé—ÑK¬MœÒÞÂyTú…d´EæW¯ÈÃÓê™Üý>ç‹ÿ‚U°6äúïÜoEÂ8ä:ùñŒÓkÃæ÷I«K@™ÅD¤‚µ+Ðà˜X–7ç~G;÷ “PIyµÐ– Þcå•lâ©‚û[’ÇU‰=rÙœóêì%LRŽÑÇQjr-Aj¥ú“?\°Šæ|³ŽrfÔ?¨¶ÝiÒaq›tf´×¤#»Zíq¾ÈZ“SÆ¿¬]}›æÅ~þ]Ú÷?ËsÄÚ ý[Óé:þý^í~öiá™uwñvÈ ¿ö¨úÑIÄ Ií§ŸJO½6—z%ÅÕ·çPûuäÑÝ?š&|D®ÜÄDf&¨Õ–†yvŒä¼k\ vOaMÊcw\ÞèàÀ¾iôðÍѪMôÆçË)e©ƒý&ˆó ‹ë|¯ •“ëF棫ißábzìçW?È.K<'!Ü|øÜ¬]·$CíIêGÌ pkÒdŠô‚Öä¥Gn”‡ÄúöÉ7,[ñ­{ÑÖÝõë¬6Q„@è,©1áuh×Z;K:ï ž†lcÈŠêò|¼´ßÆþ«î¹ìí§òzó°-÷؆}ƒ¾Èã×þ’ÄE´|ͽ#?}ëé¼gÇ<µaxÞýc·àc­Ëwy;ÙŸÆÇƒPÓ2ÈëÔ:v¥¥‚¬C[¨ŠB ; “N³ý h©d0=bÒ/éšþ y|N˜Ši4õ”Qt¤¤B;eÔ`ÚÅÓKaªúà«•"{2f/5'ÚPE! HKN¾Qr[­Ñ‚@¸M:ßÚð»Åþ÷µw =ÚÇ-Œž#·ùœçxÏŽ±bÖ‚i€áï¾=‡?½ñ§[îýöG™F}÷êÛú¿jk.Ü|ߘwdjÝ5tk’‚™1 UÞИ<÷ L‹Á?“{yVÛã­õ™B ^ß0ܬÛ/•U‰5¦c_ bR`†Êoo»Dh: <¯­á—lœÝB÷_>9]á +.àÿ@,`:ÂôQÏÚ-Ò|uÕN¡*§[ìCiN†àQõ¿B€„ãöŒ³Æ)(QƒL:ÕUÅ7ð,_4gÒ1ö‚ÁÐû?Ç ¼–ûԆϭºãÆ äáö«ö¸oãõcÍvV£ç) Á¿äœ“Õ(Ïø§¶þ *àyeøSyR€¶hdjlÇo¶Bu ³èÖ$¥- !õ|8KBœ­QufSËpÚY“ÓTiHPBÏsoSmáœæö‡^¯~+ ®D 4ðûˆ$6°´øãës6R>ß\൦dgsù:¯V bòM7=¯k¾y“ê^tŽÖdp”¸ø´×«Ê‹g}rGz ±¡Õ÷çñõ—5U¿Ú=(ãu„ïE¼ÃÊZZ3g„[SÕ+ ®G&ÏŸ0Mûì/Ú§…ö5šûAüqóë€ú¿ÛÛlè‰ß¡Ûí­3,׵ݤS˜0T€QOå ˜Þ×ð9í Lå©Àgç>•7/Ùb»ïû»†–o¸kè~6ù|/gíp6Až.¬=Àu4ïCb2½¨ù}3CÛQ¿£ER¢ÿ) #Œ€œ÷,¢|žeu¬¬ŠJ+«ÉÃ~LµoÿÚm¦fx²rü¤8‡˜?rp?v”æ‰<««+nÙoDkFÜ $ìDÌF+«ª3Øêhc¿qz÷½½ií%’”@Km¸þ—¿o¶ZÀXd“AëÃw<¾µµtž®Q;zÒúÞ€ß?ÿ_Oý¡pÖ%¤%Ü&€8›5%ãØwäcÖ¢¼Ì ‚ͤ–y\xû¶-÷žo"ãs~ <Áûß¾ú¾1³ßæéÂ#žÝtÒæ{F®á}Ê–{FìæÏ7: vD5ФDõíQÂE Ò " Vrn'ž}…ÌÂX}š\ù©éx9í‘ÉHN²JȤ»è3Îýg§)ãsi2牲ZMµ„¥=u·õôÄÄï›¶­ÙB߬ÝN•ê4õ½­Xµá|ãäÉ—8²'Œ¾Ãj5ßÉ!#3ù€nѪvC¹f"wXLì>²œZ¢î1Ä &ãïn}äw‡}>ý‹6ï}qöìW1CE–6t¡í§ö5å§Ô˜tš ¼f±XžŸz« ¹\:£ŸÎëo}X[ƒ¬)%MQ05£á¥€ß·,>aLnUåÆùá}•µ*Kâ-–ûJ=.Sî3yfiÚªÜ'óþF~œcoDªÄФį}T½h#¤7ršƒ78âo9kMJÌTèIżöiáu²ͤ{)Õ[Hžý4{ñ:&Ûèši'sŠ…þbV´+‘,’œx½^¡5z÷«Õ‚œuFÿ»ºïÄU®ß|Í÷MKHJü““>½Í;ôþ¦õ”aÚªY4Wøo°#âAñè6*ôåÒ>߸Œ#”ó§Ì1Ùw]7äW·½þì_æñiý²Ö"M:ÎÀ3'ÛµRW>¶EtsýY:M›t¤P^]ÚW‘çã<9×2Ùòòõ¥¬%$…5#‡†?¹ayUÕ¦+9ßΛ§?³ïÄÙcЪ°™g>ù5Ä@¹Á§ÿŸ·Šv"~ÃéȲ-µî~(’¡{öõŠÍ3Å@¡Ó#Ô”ª¶ H-ÂW+ò9ÍZª2&жø3©Ì”Ö†ZÚ*P¡%K,I¾"æÌ£W>ZL3'Ž¥s9N¦ÌK3Pû[i|¥ì·lz<ZÈ)$>ÿ6ŸªEÿOê”þwUߣ¶=R{‚÷¨åº{ºËfû}‚±Pký”ÒL{p<¢…Éõ7¯Ç¢™³iƒkfºîHÿøÆ}øßO<†äy˜í­Š0éø¼wr.›Ø¨”È%´ob–Žn0>wÁý¾/ƒV§ÂÐãÍþl±_Wàq~Ææšçø¤;q"[+Ñt­6Ÿˆfà<úóæò{û³Ê“îõìÆý>ú›¾pfã{ó~>¢xø“oð™œMÎðA½ªt?"ûÙÅxTVsvâEkèåˆh¯È8Œ•ÿ;œ¹€©½ôþP 1V–rÄØW>Xȉÿv’ÏÌM…dŸp²€˜,父ˆBûåwi1«Ñ_ùp!a`À—’¾ýãÃEœYy3UsÖZ„éGCÔóæÜå²*µŽr þŠ£ø~´` 5õ¥Õq“:e€nª; F«ã' 9f/^O NÈmd W‘ýFÝ.·[´óù²TÔ…ýמּ‡ Ã&ê‘j7Ûï~à&(e™óµIŽWŒLPš¸$²»Ð椸—Ál¶þùƹ‡[„|xÏ·‹0ñ³£Í}Ê4½v–Ž®ßÍf¢ì‰_7ûرêY9K'HPäÑÖ­çÞ5Ôm²/ÑI;™ýM~‡«LºPÐ܃íÍ÷üŽF=µñdl£äß3zYŠ1yœQ3œ¾åÎÅØ‡ny?[‚ߪÄ=–¤¼ôþBÚÇ Û&p2À™ Ȭ±.&$‹×lDeè€L‘ø_vÝÖ=4£À.fõüû_®wÿçáÙºûPí“°ûàQÚ_xL$ D ·¡úp6å‘B Íó¾]O“9Êì>ïã…Ar³›5A\bÙŽU‰üó¶ïãû½–ŠÌý(?nÇw ·¾mò£}Èyæ,Ù ¢%K¢Ò¶šš?ý–e=?Çó¿ßý7J‡àÝ)Ê%7Ý6ßø[&úû;š)˜Ç®Ã ´§´  ‹Ùbûãï¸çü9Ûô®‡Iç³' wpàµÍì˜ ÓÑLNB‰ÎæÍîs·Ï«üÕrÎ@|oÐç¤=¯É¿cT¥É``Y—³oÉ]šî8ÆíÕjRp–Q£ç|ºŸ“ڭʦûF­­Ý¡~Ämzpc¥÷ˆðZpà]zÎx?b 3aD½®!‘Û¥SƳú}$YÌfÚ¸s?]>åd:ix6]8éZ½eO‹_ºiœ¥ÖÎÁã°FTYÌàÀàš”@{—p6+geÞ][Gfj]wᙄYªD Ô.——Þœ¿B˜x6;NŒLCí¬òT软ÖPkù }ƒÌ-¨u¹ÙÄSÆ™µ?Y’'Ú‰¦þGªïÅ®…ëñÞ„‰Ç:`À°^½ûôy6‘M<'Ø> Ä[¸<ò‡ K¢á%&'ýmìØ3 ù€¼Ç}ß‹\:OþÊ&ü‚z‰Bnˆ´0é|Ê_]Ó.ø•>r›çìƒ>²tü!­i ÿÞQÇt«e;ÌÞí5:Ïäg½ˆ™Zû¬Lù$Dõ³ p܇61¨bS ¦\&8‚IñöFÝÌÎLûÊX×<òþ ÄÛ­"¼=’ ¢„*åÅì±·ñ%ÕÂ×Gz%%ÒŒ³Nàw@ð¼ì>½_ ö„ ˜;0“eÁªMÂIv›mL—kPvZ…mö1TQí¤Eì/¿Èܳì7êr:]´”gð ÅÚéj Rhÿ#Ñ÷ÐúÃüDê7¼ì§Í˜qiÆ öA1v¥¥a!ËXÛƒNÆÌ1çL¼£F^È݈HϤÔuÔ¤ÓP¾æ¶·þbøA³E›¦è Nøg¥ëµcBÙo½ÔûÍ]«öÇ&µ@lv¯é^¥&ÅRݯbmÈŠ4õ4uvz¯Ö|Øi9g!Æ ±bck<úr e°äPQ)9Ý‘ay›qd‰gTÁ±&d™0r øšxâPÖÐŒ`3‡Mà÷ªD 6Á'fÒ`Kg9ɶµg ò-Ë+ÏŠ³^wD›"û -J?‹+·õGcÿÃÝ÷¶b߆óñÎZ^DZ¦âætžÅÓ>(Ç“2A6‡Ã~;Ÿk­‘»ö¿ô/i Ç3éð,›;Œ†ô~á0éO^y|ã]cvr†‘ËY£òÁœWÂ@6 ÖÝ ü±õÈrù¹ãéëhÎ7ë(w`Ë„våÎp¼„æ.[OÃö¥™“‚¦‚ûР¾½é¡çÞe­ŒF…˜kÎ7”³/·¿ùé%c8û¬£ÿùû‡‚ìÀ´sóÅ{$öÙi©M@Fi˜Q £:³ù6·Uh΢”êBÚÍòågÍÈ ,Û3ÛGöÛÃS]¬9Dÿ]L¦ Ym–©³.Wß#(o¨Å6ñ¼‹NÖŒ¦4L3ŽÖ²iG¼9éç_}ýÉsßyí[–Ó?ç ë`#yï(§b1K'TÌÇÛ=K'œlüå˜ \ßMá¬SÕÕ=è±$å„Ül7,[|©nÝsˆ` Å„†¥afäÁýzÓ~~™0û`Ѝ,Èv|÷¦L?sÝ~‡옡Y‚XÌA˜"3ƒ ½‘ûnºx’¬N­#€@p°ö‹HªPt#J4!˹e÷aÈÏ|¢ðÌ…†—oü²ßˆ‡SÏN&)ÑÞÿpõ½5ø´óIRà0kïÝ/k2ïÐ¥õEü2– ·]ONK›úôÆøìÌÀO ºg“z2óFOû}Õj¶¼ÐRൈ ¬P4@ Ç’àÀN`bæMLšÝ %(¡'5$(ò˜‘sa -&þ2V ÌPD"û&?Ãâ²JI6’ÚÂÑȇˆ·%U"®Ít mk7ÙoDÔÅ´cá[Åÿhî¸úŽûÐLøcÆ×üQlf‹µG’åh²®z~3×vÉnÄQ±²ŒIq{~šnÜ ':¶Zó{©VœÍL€Ÿ7j½_›þ@aU0ú|í1õC!Ðåôh’"ÑOKN¤©§Ž”›jC@kíUÇ¿A¨ûîP\+•³i CjÞÚ*·ì7êp³™§Š—îÐÿpô½­Xµá| íxg‚¤XfSºMãd¾Q^l†2ƒO³'Àן9/éÆè0éD9lJ¼(A@‘¾pŽqÖ¸(¹%JŒp!“‡4{x<¾°æâ —ŒMÕãçqÐëÁ4d–™ –ìGkM>ò|\‹:0»ÇëegމöÒѾG° #X IDÅh0:Ìš'êIЉ<üÏÊÏUTºµ7J*LÏÝö´g+£màŽAôTÕ v"ýo­vvL]¦Òì»;I®@4Ї¶Ùï QÁçs÷éGûÞV¬Úp¾4÷ÔÈÞMŠËŸ°ï‰·Lm?à=Hä©è&b+1Çî£0RtW¤FAŒÏÝhÞ¬N×[ö£µ÷Ažk1mž#†Â¡Û”Žô=‚”š©M12¾Ý†¤8öcLP<ŒOh?"—ªZ!”&¥Žò/wc»£%TMߡۭ[]ß2ò~vü.¶ÜNزÀ¬¥üm­_^‡: U ÇsÜVÚ}~ûÞîvaè_çzzüë¢åŒPù£E&%‡B EzÊ?⣠‡ZèòáVúåüJr²5ð¥ñ´¿,@ÿ·ÖE[颗gÆÓ›]Tâ옆AÊÞùC¯©èH1³¶äîÓlô 'Ai2=ÇLççXé²w˨ܣÓ/M¤±òq}°ÙE÷žf§Ÿ^Ù‘&kM]íé{‡nùb ì¡KËg·pT3˜ièéÐŽïža»^€Ö~þSÖ¦"m° žp'Yéä®k^ÜUä¯*%E9MH(ZÜ®êú»˜£¾ZéàÖÄõe…uùL–xêÕ÷TÚ¼d–8¶kÍK4ù¦ï)>5—*‹9¼‰* „@wôPo×í‘!Â×oÝCó¿ß"J~Ü„.!(² GdiÎ’ ´aû^A¤ ¯*ÑÀàA›°éhpÐur ¬DK¶<Ù¦ÑÐ^,ù<ÈŽH‹þ?,ÍH[™pHß›y;ñ:ÄYæíä\8ŸétÉ “¡½µÑMSØÜ# ÎÍäkb¥ ÿkØŒ%Ë•#Ùôu’®ý0¸Úê?/ :£ÿ};Ðÿ\ÖBýÏ”DJí•D‰ ñd·ÛØ©¼NS%åhËú™×çѦ{VýëÉÇ.æëÊygs³:~eÀÎK"/©¼dN¾ûÁÿåõ^Ú\ì }©ôЊF×¥žJޤ´wÃëõ޹*’-¡_½}jC!Јy’Ô¢ýPª8îÉÊmÄ,øƒD[L%æ Z–W@gŸ4”hᛢuømýl<~öOÜÃΨ’xÀ1u_“±/@UÞ V£5uuôœÂªeÄ×× ,aŸ,(¯²ÙcÍáºA<ƒ}) «:O¾Žöïx×£ÿ0éÈ2=ÇB_ximMŸÿÅš”Ÿ2iùóÒàÀê»ý±ÓÙïp®]•‡É—Ù¨Êãn¡£»’³|o½c¶„L*Þ·¬Þ>µ¡è Ä4I©Õ¢ðTc—ËM»å´õ*tdEí½-4gQJu!ífY‡ì+J¬:Ñ{jkšÑ_™ãw9sÙ?œçá Ó4ãàuv[ÎÊr;½úFY»î†Sh2²4Ê´Y)Õè§£[ t N{Þ¦zaêHs(ŽýPªØäc62qÔtá, ‡ÚSØ´#§ £âl˜GJÚò‘Þ&q:ýä]Üÿ!)ušŒ5ýta®…ú&xv“Ngö7Ów5NÄæ±wX»¤JóT•P|¯œz'$¤ §^Y§ÓªÙ××Û GÒ Ú›W_»Òè$µC!ƒô’âgGT¯0õìä‹Y[­EÈÆ2nÙ}˜öëM³YLKîŽ&ŸQïæ[‡´lݬkú` ¯i0Ûbs‡èº;Ái—s½$RÁ9$-ß¡8ŽO’•d þ‰F^˜ðÒ¿f»_¢&]‘\&¾rÖÀüswûIÊΞÆ~.£zÅŒž‘½ ôöå‰Â‘3X®ý ‚à‡"ËèÞ&Zº¿5½WD÷z ;Ïȱ31 ®{+ßEÃxöÎGW'²OÑ÷'åï<õä1e6Õáݽëé*ŽæSŸ¡3Ù§ÇXë<›=î'T]¶›Šö,ª'”-¾Y콨âè¦zûÕ†B ' Ó$¦ TkRxVOY¥“|œ²9sècß>¥YY¬ŠÎîˆ6„mkþØÞÏ&¶þ{*ÀZÓµ£AÛé ˜ ¶ÞŸ[ÄûÍš°ßýàuFêU‚˜~ŒðÇ+÷ÎÍzÁ./ai©`šö´×1qF•Ö p¤àKÂÒRñTÓâ×&¶tŠ:¦ˆi¢ÄîüÒ܃¡ðy´ï@m{©$XýèH‘qCBgÊìÅ”]&&˜5S7S&‡ýC(‹‰Å5hiÞq›•Ú>U&&lßÙ©™ †>úžü«Fu©sˆ³ÉÈl¨nÎq;Ö…'˜t;ç„ÜRþ¶Š#¯ ®ƒ[\o´—pô=‚}ÄDíâóûœ^Ý‚íγ=¶£s^ݪû~°çZÙÛQºD!Ð%Ä,I‘ÚðÄI @#Ñ%·¯QÈ ùe_0à4W¤6DÆ SuG„I¢€¶¿ïµ!†€¶Ó`Ô B´!͉եûCñгY8 õñ5!]*pMãÖ€‹lk=QBûRï@MkeÒƒz£½t´ïì_£?!ËÍqí’°¿ù?Ì ÔÚª]z¢ÎšÙc ÎoÔŸÇÕ¦B *ˆY’tåàŽÚlw›Â¢BnÙhCsà0© ’¦´!mï!¦ÖbvŒ»”ó‘T”>à¶ý¯ÁhÞlŒKØbäØÕÕÚ¶÷¨ñ‰+™t/Þ¨vœ†|&rótç¸ÆèÀ»ÙÈñvQÝÿHõ½°µx©«ºê¨GÏÔ<º,Zt@ÈæÑã4óXa‹QQŠ@L“`.ùnEPXî"g€žûŽ0Ù‡¦ÐÃq>KøÌÃÔP9S&¸Æô]LÝ5ÖΔyæõoiÓÁ=û—<ùØk|I{¢r¶U´N9š…ìÌZ¾i/¥z EêNi¸@>¨½2SâjÍ=í¨F\‚~Ë¥Wœ™ ŠQÝÿpö½½˜µâ:|éˆeï¶-ú ru¡/—ú›×·âÒÎ?²±°Ú¾;VJ¹;_ Õ¢B ýÄû|ãx"‘§tõ’[YæPùÛÐmuªB ëˆY’Í bq|ymÝðI%åñ Ãó/9©Ú­5yKV×ä3ÁÛéá‰vºš§ŸyP™Ï¹]úªš­}Á×_õ~9í8&çé¶ïfót_Êd²-È&"x˜5І´¯öؼªv æddŸŒ‘Y)ä.(¤$_EcZȕš”éœ ‡sÕ@f~œÚLTö[Ö•ÉáúýÑÙÿpõ=‚Orè?f±ì+Øù¥1wäEælJ3í‰`óm¯ºÈ—MG¼9ÚÁ½;æHyyÚ¶Wª®Pt21KR€c¨©§=î(··ˆJ¢Õ@OOsPvrc•’Ë]3ÆFZR-Ñ=>ÅA׎±Ò«œÓäƒÍ.º÷4;ýüóÊ6ÝÚ^6nn¥aYÊí›@ƒ{;ÈÊ~ª´ hœËÈìÞ´iß1êÌ£5ñ“I£¥t? ­Î³z§s$WÌFª‘»=üBûºªo‚™s D;k¢§ÿáî{ï)¦ÙI‚}©oÙçs¾ê?xð6¸f&LŠ{Ù`ÒZž¦AÙêUíÓÍÄ2>wÙ’9 Yy‘²wlº`½–Ô†B ²4q#Û^·©=µ ÈËòϵA‡¸Džl£w|Þ8ÆÔÁZÇÉÖþ›ç±$>Ýî¥ór‚ö™®uÓ9Lb†rñ¶”ÞüÕ{þPä…1 mIO5×´³†çJ{ ÒvÎãsÒÀŠó—Óˆêµ OïÒíÕk(.PNÃÒx’ÍL‹EÈ Ùe?Z+ <_öuYØÔe±˜(+. ÚA{ÑRÂÙ÷öIj 0ÐcÀÇÔzOuu¥sÍÒ%ÿ-¤Ó:×e8'* dLë¿[þ••exiA^ITd_¢BV%„B %IiÁÚ>&›ŽâÄÉ8®È= ÜÕćR;¥â¸,ŒÖ÷£`ú/ÏS¤á©1­´’]oïZ¾4庽õ4ºY¤M<Ðc¶Z-Ô¿w esJš÷ªZIøŠïÊ‚ö!Gš÷ 'D4PFrûÎX„¬²£m- û¬ÓJ)õ¶úD{]ÝÿHõ½y¬`4­5w4ZóGðG. òˆeÝ·‹óvl\ÿé~ï(m•ójZŒ®*h2@–][6~°ê›¯WK9y-‰JÝ˪«Uí*Z‰€9› ™\A‘FW,D{JëΫf"“ÈæYöpÙÉuÛr¿Z ꀫA²\^/öw4iöÀ€o³ZÉf·Ñ°¾É"é$•¤“*+h»}l—ø¨À&hP2:õO±“僌2CÒsOh¿Q·Ói£t‡—§äóxÅĨ«úɾ7õwåòxuCªmôÜ5u]ƒ}ÜÁj1ØCÅê¬Yì ?yÿk Ñè.(¤éc­Ÿ:ÛG>(0ñ@ƒ²sÓ†|ôÞç!2B^È ùIaTé(’ÒÌ}êÃÚ‘C¬iM)sëd笼²Ä™4*gí‰,¨§/×§JëðºÜ%¥eUa!)0}` 7s6i Òqqr»Ü”“™HC)”TÒ •˨Ĕ!fý u$P"¦ÚfxöSНPdîË3¸2’,›âY>ÈY!³4÷´¹º³šë·›mz}^êÍHÍ<Îe½@gõ¿³ú^‡BݯÒò*Nåã4è*x&1ÈCŸ ‚Òƒd`ˆ¼g^ôÉ ‹::aÒ9W,õÿ$1ݼCïoZ¯e˜¶F,Ž â `š1fñÀIÖïuU¬^òÅ[ë—/=òÁkÈ ¹!Ýˉ7TQD3Ф4swà`˜_G<š9Mì>X¡SN¯:ÒŸkC êùn¿z/´„!@bqVWU8ÝZ5“ û‘t¤kYxÀ·q]ñqq„d“¯—úrª„8Sí/u“ÁSH)Õ£„o¹—Ç7'¢ gž'„¤G4U3,ÜKXp’Mêm70)± ¹â).>Nü†¬98çuÏa(N-õ)"‚©œdws,¯ L˜@š"ÑÿÎî{(øçÏ“×í>È›ùCĵ¡š< ¬Kå||[ó¾ÿvãöõë N›6ãL_nîYG,9 |÷t‹V¥Û´2ͬyÚ~3¹â†áøíÚX(ÍçqUîÞ¶añw_|þÓY…G­¢fß¡š”Ž`ÀU©¢è<IiëÝl¢IsðÂÑX«Žcò™»ÝMÿ¸(&egOœ7ÄBÿYt¸Eõ0½³ækUšA/M¹ömߺA²òv SGnæ’Ö޳ðË€–ÂË%4'ˆ@ºÓE.?Uò·¦'ÀÇÙ2 ßäí™*¦£{øc3iŠ75o0Å@câ”””@‰‰¼€¨°Œ2·ÇÔ#Û>^¿qÜhä(¼/õòW““µ*Îg8Ò=ä Tò츎§]Õw‰ÖxŽx¦ŸV¸ïRÞ”ÏYè)mù õ*üRð Pà€&¿R.WµÑì÷,"ú&gÔ¸þÙÃr‡9â“­{‚Édïëæ†P|>¦\ÕÌå+w•ìÙ¾eëöë÷òn˜s 1)A, ,òBîÖ©‡ùDUÑ€€")ÍÜ…%B(úQ½´âþ¶›/Ë÷ûèÿÖ¸èùóã8hšÆ3|<ôÚ† IA¤WÄ[Ù\„0Uš@@X$ÞÊå;N9wzÙÊü‚D&))kµ <ð˜q3K% äl2aö‹‹½>‘}š†p¦R€ hK³ÙÄ~'0?Ù ”ä¤$ñÄ3rÚ«E‘·¶ß.·‹‰›,œ-<Ÿùpö²tEß%XósÄE?¶èÓóx3ôy =­µ¿ås Bb‚"ŸQI``Vȼ#ýN^vñoœ+ÏãŸ.í…¶‰— 4( (¥5¿A\¤? ®SE!ÐmP$¥™[UîÐëÜôSܶâ>L‚™ƒG¼X"7k×åö/¬àÜ(ü* 8‹ë¿âȱۊI©«ñ¼hAá5¡oïŽíó9ÙUÛ÷¦¡2_ÑÆ=’ŒXu éqÁ÷4¾òá󳊕U¬M><`sÖX1XxÐFAÌö Ð(˜mŒøÇ°V‡Û jQØ…M<РsO­¥}³ eìª~CŽ®î;dÀó³qÇ~­èÐÁ·xS>cxÞ:R$1€vB< ¨dûí¼@kªiQA‘×·Úö¿|!ƒü»)B›R‹‚4( +J‹Â ¨Ò}P$¥…{÷÷•.z÷ÊDÊM3ÒÖVhB|üÊÀ"K/‡F³MtËl¼'Ti¼tå /yϲÏg1`Hδ·æ}—øÐM3 –0²Ã€ ‚€"¿îᜊ©ÉVöq1IqÁ±´F›"²g‡!1¥l š˜p‚Z”àL#˜v`îq8ìb™Ç!k¸JWõ;ç®è»Çë'~~<­§dñ'‚¤àù‘ÏDloÁ_:È ê“Û Ðh€¤m ¯ñÐI“PG W#Šl}AŸ ÚIÁË$¥ºf<8òfâ-UÝERZ¸Q˜~<ã¿Ðš¶¯` ó´×ñA£ÊqÀ T¼TÝÕÕ•k–-zÍpöÔ;_›³T¿å²³;ür—d$@üf-~ƒ¤8ìvAP„S­‡}2à\êjS¤¥­Ê”%J-!BxzD…)mbš1´)p’9 p jP _¸Jg÷rKñeÛ]Ñw<7–ÒÖu«Ÿ8vì´ ÀxÎÂ1`K¢úÏ/‚œõ#j’”öÜ`´ƒ"Û“$Ejo$QÁr„öW^‹ëUQtbš¤àY·@åΟ3<Ë"Ú‹‰e4‹¼-uòG»Ì”ƒ†T•Ë­síÒoÖ¦ôNÿ˜]F’~ý̳´ŽjTð<Èi½Ð0ÈéÉ Ð x؉ÔǾð[&…c_Ô¦WhOkŸ?ÖðC›€ö MÓ,ˆ‰YhW‚3y©A •·³û¶»ªïР€ ¬Ú¼K;°kÇ?—̽”ÅÁs…AÏY8H W#Èê % - 0ó„é»rÒ‚—‰‚¶ä‚¶±H‚¶ÑG¬±„³¯\* ÎG fI ^ ‹• Löm–ú“šêO´÷£ ò᥋*^¬Re-¾D9 Õ<~%ÃöqÑ¡âý‡ÓO3„ËG˜ŠÈ¬ löä„Í;A‚âgr£o£HmŠØhÃò¾|à‘I oƒ¬·ƒµòÜ6TßæSƒrD¾ßLö§3û˜x AÙ·sÛæ½ý:Ì<0๒7ž·p<$¨kIðn1‘ ^HÒ†×øåÄ[Y¤ì²MIT$Y‘Ûòx+«U§)¢˜%)MÁmg¿3¿«T*’»šj»µû ›‰?Äâ,q­½$ÎË/W|‚E¦–&¾D|üÞ¼âÂÇ9HÖ5O¿>/itN–~ò¨ÁÚ˜œ~Š£‚A$ƒ(Ì0ÐlÀQVÎn1‘äD®Y¦69Pc-´'iå¾6UÚÁ“Ñf¤û ÑŽ\Ë~F¢ïˆƒ‚iƘÅ'Ù€ÏW¾aÅ·_¹ðËÅÜ>ž%,¡š9ÐC¼pù K¢BR‚@È…vX“‚:О\Ц\ä>œ£ŠB Û#ó$E¾±îg¦‚b§ˆøYhÉŠÊ›‡h¤xõd¦ÄÕjQ)hø…’/w¨Êñå:+‚AsÛº5ÛOŸ>ã¿ß7‰£D¾§z‚ݪ''Åi6sHÈßÊÖ4ÜnkõxöBKÃíÐc]ù»a?n·G¶†}m¸Ýž:å5H€ÈÄÔÆ²jìCT¾wÇÖ/¿ûÙœªªò">¯+â„H’€ç%”œÈmq ÿ¡ Yd{r[­1…@L“¼ÅÂï |Á%ÚÙîÏSA3¼ûEôh¼“ÍÌ_õ©‰v–ºîë;e ³LxÙJm ¾>¥ ÍÑ4½¬UùŒ·¿6æÄÙÆ·ÇÇ÷²ÚíI&£©¾} W©»0AE@3ÓU欮,Ú»}ëÆ-ëVoãƒàFSœE"b÷)T=ëb–¤ÔþŽÃ"f`g E!!ÁY™)­ n})…5)9K¯”á²/­¯©ÛžªM‘ê‡Pò-‹ˆ?±-oí&^¶ò¶T©£ÓòüV%6Àó€‚g ˆ-L+x6TœA…@,!³$7 Ú“ cbpê'û&C×#ûìš„IÐðÁÞõië!fõ NO>Rvô£ <tP0 Ém Bð+ˆdü ®^•n€€|.@Pਊçφtº†ŠŠ ¨¢èîÄ,I‘Ú ôÁ)ŸˆCÐç&ÊŠs’¯¢œFT¯¡ü¸“£âB–¸@9 Ë@Ü ÈÌÝùe_¢BÐÈ ! <¡¿AR""ò½R-„<(ò¹$×’Ä‚¨€°`ûAb@tåµüS…€B » ³$7@L/åAäDDA´¬”âprÒ@~‡qRÔQU+i³ã¤.Ó¨@ƒ‚’æ=HY ÊH޽ /ä6±üèG+P0…FR¥©ø= ânÛ]<rÁ3JTðŒ€˜`Ç@PTQ(º)1NR‚æ öˆîiã螈ðétÚ(Ýá婦ü>crpRem·ítø ÀÄ J¦C§þ)ö`RD!ey!74)=ÌÜú§$¿€±Žtü‰ÐvÕïèEE>’¨H²"·åñàÙê…€B ["Ó$f òÈÏr‚<)nŽ© ’Èù¼ÔÛ_Åó\]t”‡¿*—Q‰)CÌú)6gD,Ž â `šq†g?¥ø ÉÈJ’¾kP8ÑgÄg!'ä…ÜÒÜÓ-Ÿ®ðAI~Kÿé, “\ÐZS9¡Ó=¬H’"µ)XƒÈEîïa°¨î*b@R "š(ò£ ‘›ÈÍâEèó`^øÚÙÝN*ñò & ê¼KÌm°‘_ DÇH²f˜Êù5 N²ÉC½í&%6!›È‚Ëq!'äETÌHÙR¥VÅÁ „ú[œ þ‹Y@DdQ¤D"¡Ö D <#pS g‘À  /„=—á΃3€\dâœ-½üÕääougÀH>“Ì*9âhÇǾ ÇÐE*T“ x³NvŽ=s4& %II ”˜ÈKB¼òBîlê9ÞS¥§ã!¤Ž+ nŽ@Ì“” É‡µ)<è8Ì9ŠÌf$0l2±ÑÇí æ,œ\.!$4z¸î/ä@{Á6M"»Í ” AAINJ¿A\ ¯Ò¢„ }UB@! PtGbž¤à¦H2bÕ-¤Ç5ÅÐnÀß&+;©:® ¯ gÂõ‚šäqAéHxpiª‘¹ZŒ†`Ž´Ô¢° ›x AæÖöµ(=ÚaVà®þS( ž@ )¸Å *H ‡"µpLS“ÙÿÃÅ$Ååv mŠÈ†ëÔ&šuà?Ù4#0á@9Û&(˜{»X@Ppòª¢P( žŒ@!)’(€ˆß¬JÁo‡Ý.Špªõ°/ œjküV¤EšˆÚò°}QêHBÝ#Û.L9hDÚ8É‚œ¸5(Q…€B@! Pôdz IÁMÆÀ/§ôBS!§'ƒ0xÙÌãaçÙ ÅϾ+žNÂÿ˜H¢ÒžmŠ…'¢@“‚6ƒpÍ‚$˜˜…v%8“GiPÚƒ²ºF! P(bERä qi™ À/E“@@8ÖJçZ9¨£$íÛ úÁ€¨Yñ›É‹”I欅 …€B@! èéôH’‚›.µ* 0ÁÀôGY©=‘äD®Ûó° ٞШrRœí#÷µ§nuB@! P(bKRäM•$dEy¼#EÖ!‰ ¶e{ò˜Z+ …€B@!Ð4=ž¤4„E‘ˆ†ˆ¨m…€B@! Pt jžk×à®ZU( …€Bà8(’r€Ôa…€B@! P(ºERºwÕªB@! P(ÇA@‘”ã¤+ …€B@!Ð5(’Ò5¸«V …€B@! 8Ф uX! P( ®A@‘”®Á]µªP( …ÀqP$å8©Ã …€B@! Pt Фt îªU…€B@! P(Žƒ€")ÇHV( …€B kPañà¾çPåï<@Çʪ¨´²šÜoƒ3Ú¾‰ƒ‹™’㔚G£†dÑ€>©m¯H]¡P( „@')H èõúhÁÊÍ´`U>•Wº9  N&ƒ“ ¦2ÒÈ–ÇA'+|Iä Øé“oÖRb¼•Î=e4M™0Bd`K#ª…€B@! PÄ=–¤€œ¡5ysî2Öš¸ÉnßA½S×óz+ ®ˆÜæ@ÀFNg.UV£¸iáŠ|ºö‚3…v%4[rDW•* …€B !Ð#I ȉß ¯™ @«a1¡Ìô9d³í‰ø­ù‰‹[/—+›JJgÒ‹ïºè²)X³2’É‘rŠøMP ( …@·@ G‘©=ñùü‚ Ì^ÌdÁ‘O©©’Aë¸ïI[ï8HQFÆËT\|}ð5Q@ÐÔSF ¢¢´*ÇE®>Xd ý-÷©ul" ‡t ¿C·C©Ÿ …@wG ÇIPà’·cÍY$(½ÓÞéÒ{r$d(ºš>^H”‘’Hc†öWD¥ñ]‘¤ª&Ãõ¿üõx³Õú¦)ÙÌ0ûH‹o|‰ÚËèU0³?ÄeÐçÿó‰Y«¸¿Š´ÄòMW}ëqô’Je•“Þûr›x …%Zî8´9ÞÂÞôÖü唓•Á~12Ñ"^WÊrbbœ<ùGö„ÑwØì¶;=“µMz‚ÃHNth6³YÙɺò.uAÛ.¯7PZ^­WT» üòûÛþç±Ã>¯ÿ¯Æcúó¯¼2ËÉ") KÜÕ¤B œô’‚Ûã¡E«6Syµ‡}P>íOs7•^ÉNËûÍ@IDATsèð‘Ÿð,£M4ýô1<ËHëé>*‚œ0fækî¼oZbrÊߺÞgäà~úÉ£Ó˜œ~šÃfUL®¹‡*ö÷ bZír³vô­Ì/ÈØ¸cÿŸ †»nþÕonyõ/¿ŸÏbÕC…@ì"ó_Ÿ0óÀIÖÃÅétÑò;Å,žÎp’mëc™0ÃhñÚ-B^È ù{`öäÃÌ‹íº{º'>)ùƒ¾éÉé÷]wÝyõTíÔу‰ J„Fu¹!xð<à¹Àó‘™–”a4™?½åáßÝÃçâ9RþJ ASÛ n‚@Ì“” Å'´(û ©Êå£xÇú¨½=­²ÚK»eíOL“ŽZa##˜4ï‚ò㻸Çf{lˆAÚƒ7\h: 32­ªZc<¿ºñB#žM3<õ“_ÏQÁ{N•˜¸Ãª= ˜&)µZ¯—\¬Þºç°Ô†8(ÑZ ‚ÉmÞu<,wÔ¦à™å’›n›áˆOü-8ú-—­YÌʲ­Ïm4É…çÏ žƒÁø—îø<–/¦ßuÑ„¿’E!Nbú7HRüì0릞’Šj2ªÙÏ#2ÚÂqc d,.«rûýþždòÁó?)ë€Ãz¥÷Íz6+#E¿~æYê+8W«ÏM¿ôÝfsüãüóÇÝé÷]»½ª»=˜þ£ mó³kRÜnâYd0–Eý­…Œe< rƒ¤ = €ˆ@UbáÅ~ÚŒ·±7NÆÕÓN5* J¸ûè"ž›N?ÕgëôQƒ~ÁM(ÿ”ପTD'):ùxÇ`ïv{ÈË3| š'’x†¥nÈèñø„ÜŸ§Û†¥Þ(¯¤V‹ÂrÆ%¥öºytN–®|P¢ü®E¹xx~ðYm6hébúå·C‰§h31û S4÷À³{ºÓ`ÏÁ©„ã¬4÷ /1\Bµ(¶‰ç]t2û9¦ñ4ceæ‰á›ÞY]ÃsÄ?Wþô§p›J›ÒYÀ«va@ fI °‘æ ôˆ“¬% uN’`õs$)p˜µ÷î—5Ú8J瀭Z‰iðáyr$$ü€;ªHJLßmÕ¹XC fI ùàBb†L€‰J÷¡(•Œï^%ûk_HðJ›Ùbí—`·ê*JBêg»Às„ÈÄì1Ÿ]óœÅì{¯Ý © QŠ@Lÿ±ÊÁ>´*Øî6äÿjÈV·‘»}‚â9„¿œf­F³)=9)®Û™z ‹ËépQt;f»Ù?kËîCÍÞ¥Mhñšè¢ß¬àÇ9À© çxâÓ”_Êq°R‡Ñ„þ`cºÈA¾[¾# SRö˜¾AÁ [ÒÜ#ˆŠÑ`tp.ž¨%)¼øñ!záÍ®kËŽpô§-íuâ¹ $rD…-\Ý’¤¼3ÿ{1ƒìÚ Î õÛöÑ7«·ÐØ¡YôÅòd³Zhò„ᵊW?^Ìé¬tÕ´SÔ+8§Ô§KÖÓïv™0·|õ}>Ýwíy””àI1q]ØNˆ³7º5é½i@f* ìÛ›¾^‘O»ö¹l ÷ë·ÑćÓ;_|/d–݇ÞåßhÇh4p 7ý{öbÚÁ×d÷I£ô”„Úú=œ5|ö7khí–½TÍSøÜö@¥¯?Ÿœ¥ûÝ/VÐÚ­{Èd2Ò”“GÒ´ÓF×^?øoH>gr­4)Ñx£”L bú‹BšK‚ë^·â§Õ’Hãs¯£ïóÿ.ÎÆ¶Û[I,¼¥ÑÕ-û>ÿ%ÊÉšJ½“Û®>•¿Q£±µCXwÛg²¼ÊÅâÎ ¥L'ßåÐSOa*ˆ‚ËíǬA'ï?VV%6Ï—Ãñ|4zï«•TZQE-\C§Íi’ à¤{XÅÚ“w˜4¸Øçä”1CD=éÞœû-‘MÈ}ˆf 'šœ'ê’É'ѰìL!¯8Àÿ¡ÎÕ›÷Ѓ7Π.§ƒÃñeãŽ}B3“‘š@X8ó0­Ù²‡¦³É)ÊKL=_Q޵O!ÐaIiÂ^ ƒ¨ÚUÌÙ“+š9£õ»KÊw5i&j} =íÌîé“ÒÔ]‚%>Î*ÖnÖd4U¦>ÈéŸAÃö3qn½ìœfaQ×]×LæžcåUô4›·æ-§Ÿ^6¹¶ޏZû;ô‚îaçÛÓÇ­Ý ³QÞŽýb{Äà~B ³Õ#%LÐãáòóL¹m{ÓN6­Ú´»öZÌnRE! P„ERšA31®•WlæhÛv—W$Ô§J“Äô—­ÅÜôŸ˜Ñ``óaaÙ¤´8l‚ÙºçõíBó—çщà Y½snôJŒ‡ÐbfŸ‘¦ fÁŸ¥¸´²öð16Ér·9wéz&JiHV&›}Æ Œ,´=“ǧK§ÔŸá$¯íë–ÁìP"*zÝÖþé›SQ}˜§tf†¥ÔƒúTi ¡K³'FËRΨ½ë@Qí""·A¸œ´cß:t´Tø”䵨ùš^›³”&ŒÌZ’©Tx¬Œ¾ø.hvlª‰ÃÅe¬)Žºë·ï£ìÛÚ2|`_a¦Átf˜¢6‡ÄPÉßy€ª\n:‡b‡±¼qM–s°¿ÖNÚÊçC³MKCS’<7zÖõg£G,%‰B@!Ð,Mæ5{zÏ9p¬¼€0…ØbŽggú¯Ëö ¦£µÛ^oÏ¥êš(E໼ìdº³VºY·_Zû»5?N5ˆ¶pÌ’ßÿãcžåc§³ÇçÒ¼eyâÒϯc-ž‹®úÁ)ì,kN­ïµJø²d¦&5ª3…Pú±ÿÈ”“Gй§ŒltNs;  A¼ø¾œÆN·›X.hY\nýõ¿óEÌkàsrÑÙ'²3ðÉôúgËè¯o~!ŽÙmºýŠ)Ô;Ħ¹6Õ~…€B@!ÐZIi©â²<ÔK™½FÓÞÂïš9ëø»}ÈaëŸ›Ž²:£[ ðÇ;.oRÎÛ¯8§v?fÒ`‘æ‘—¹Qnr윺÷ºóØçÉGVKðÏð‰'ˆãs8,²À©U:¶Ê}X`—Ð:CáwCšÚ‡×?üü2rº<¢!‹—“r>ùŸ¹tÝ…g°cí`AD¾Y³…>^¸ZÌæ‰wØègWž+RN8yzrœÝ†Ùɪ( °" HJ3pº©™£ÝOyïÓŽÂ÷)+s(4òj=èbþjÍhæ µ[!^Fòd,ª( ®B ¦5)˜²Y·tÄíkW˜÷ù?Øù‹Ê¶Ó‚UÑ œBï.¸ž6íþ„|>Wû*VW) …€B › ³š”¦bJðä øëœ£õ麕z%¦S¿Ô)t¤ê[ÓUã SÁEb1›8 èÀ™4zðå4 ãT>Gy-FëýTr) …@ûˆY’Ò6jUîl<…³©s»rŸßŸHÉ))<-õ×””h§ƒ¥œîÀlNvø=‹t¨õúªiÃŽwÄ‚Dc†\A£]J©I9])ºj;Ìü‡ã¥ÄÙ­bÊo˜«VÕ) ¨G æIJ¹G#›É@~ÝÁ3l‚¬õNI +6Ñ!NJ8ñÄ\ž¶‹n˜yV½ºÔ†B@! èNÄ,I©%(ìªX¦•g&³QçØ㢖¤@6+ß•Ô$‡޳²/¡VÖ˜`™zò,Ú¾ï+ÊßõíØ¿€µ0qZ@÷Ѷ}óÅb³$ÑHž4zМ &0,´.õ;º@¿Ë§NÙ‰ÿôê§ô̽׋³/^³•ý“ŽÒ)LTÒ’ãé¥÷²öÌÌQcsèãEk¨¨¤c‘’£%B@!ЈY’‚›í‰‘É û£˜L&Êd¢²¯<‡\®l²ÙöDÕ=†LNgåöIòJÙÑæŠ¦9¯Êt±¸=åLV>¡»Þ§ƒG×Ö^‚Àtk¶¾&–”„4šýWF³ÿ bÀ¨ý å¼9g0LºlÝvNøw˜²2’EÎ$øC¶d—ÛË„å=zËE")¡Çë£÷¾\ýS* h~ôká¢îpHj‚ÅD‹…3e¥ØÈlPqÉL èæ¨é d9Æ2YM: ë“$ä©‚ü²/ÇÖjI¤“r¯£ëÏû„n¿d 9ænJޝ·¤b7-Y÷ýý£3éÍ/®Ž·ˆ£Jô"`³Ö™êìì«Té¬ó§ÊÎL‚# ¢Ä&ÔDMŠ·Go‡”d …€B •Ä,IAÿÖ9S@N¬ü¢·2Q±s&׉&òzÓ©¸ø²VÂùÓ ‹‡e‘™(d„¼ò£m-É Ù4ñ„ûèöK—Ò§¿Gãr~ÈýO¬W r}¾üzîý“hö’;yÞÐF&D*=¢`cÇÞÃTÆY—‘iø gM9¨O#©R“âEr¿U›w ²²žCØ«¢P(º;=Â܃ÁÞfµ’“ ÙyIçi½UžJ:\=ŠèèÕ”šö!4o—ÜKhP@PªX–)ê—–(䄼š”–Ì=­ºú©„eÚ©ÑVÎ#”Ï·³ƒ®_\î÷»9@Ülñ{Ð Û¸GŸ5ü¦ºÊøïgõ*{AkŽð9¹lÎyuö&)Ç肉ã(•“-)oÔêå玧¬¢9߬£Ü*2q#€Ô…€B Û!Ó$f òf³Y“¸8¹Y-îv»)Ëç%=à¤BçHò¦Q¯äO;ÝG>(0;A«Ó?ÉH9¬E‰g!'Èä–æžpg=„­¬½¨³û‡óÆ" ¦cœdá““b ,Ö $$ÄS\|œòBnÌLÙ wqØzÑ)#o ÂîçíxVlz‡M>%µMé:±ªIÒérýå¯h_’nxÍbOûäë «jOR?: IPZj žU …@, Ó$7¦8 Â /¿ß/¾6åñ‹‹ŽTû¨Ä=ˆŽºr„_†A«&£©ŒÍ@Á)½½ÙÝB~_;ë:D“æ§ »NYÉ6J`b’””@‰‰¼€¨°œrwÔÔÓ¹Ó’†Ò9㦵›N¤=‡¾Ü–ÚëýVMg’Ws½ÙËtN*0ÝãŸ5.aêA…´ÍL8CÎ6‚i¾2‡],0óà8äëìRZ^¥û}¾F£ÆiöO,ÏŸþôŠùdÖG]Çää‡LRzKù7ï×O&oÕ³ì¿ò™¦^‹ï7è³»fìpËsÔZ! P(mA 3I H‰XœÕUEN·VÍ3mL:£ÔS¤0Q j4,ä°ÛANµpª Ôø­H’Òe ´5(²m„æGä[˜rDÚ8É‚œ`?Žƒ H L°†Èÿ{{âu»rk¸OM–_ß*¦%¯\¸pò½Ë·/™¡Sàz¶d]ÈÈiâ«/æ¤xWìßYÊþ+okFö_ù‰oy“FÑν‡Šyzö*.«¤RŽKâáÐó(Í‚ÑJÙñ`:yr¼ƒÓÄѨ!Y4 O0[+«è”Óö*¢üèXY•VV“»¦ÿi¼»ô½#}léÚŸýÏŸÆûý¾óøz€®é}„–Îoí1~&+Xs‰¿Õ=¬ÿ÷?üzuk¯Uç)ºERðž—K`ßö­ëú ruÞŽt*ç鬂_NéÓ“A AÁ Ô¢ø…&…5ÂÔ#‰J{äD›baË4)hÚ Z I &f¡] Îäé ú…{ÁýÔ ÷ï]Ê›ò^5ÛåsÎYäヮ2ûÙ%';=åWóKøz¾òŒÚ‹tJæªn×}þÛÿø²¶“©×Èlùï¯ovÔžÓÅ?0ËkÁÊÍôõŠ<Îãæ{¥“Éà$;Mk>%Î.ÀŽÓ¾€>ùf-%r²ËsOMS&ŒÏAWÀ€çÏ=ú¿`U>•G¨ÿÑØ÷â­ÝsÏ3¶r[å/øÏý—¾€¯¿t›·*ç«Ò,~OXT¤£%PeŠÓ]f‡Ûøã­üþP@מͲd=?kÖM‘™šAÐTÕCàç³^Œ÷JÎá?éA¤kI<É!I'-‘Ÿ½~±ïÕɸµÛ»ú•?k–Ö­&;tI‘ÖˆæÏ[¹|Ç)çN/[™_È$¥Fßб›Ô–«¥¦BD¤a`¿ANxJ2k¥s-âM t”¤ Ž`›˜Ì%„é¤ +ÁmN€ØÚÈ% ß î¦~lѧæñ¾Ð{&Oiv}ÏM¥¥|ðe,zÕ6X÷ynàº~ÌÛCB. ý÷äuÿžýW–h<;ÈfIxŸ¯í²˜üù;÷ÓëŸ-e­‰‹5Z;¨wjd§Ÿ‹Ð)è-pÓÂùtíg íJgiÏøÞS&´&oÎ]ÆZw§ô?úò<†û'Þc†xtz•¹âÍзÅ^}ȱ­4 b—fõ¹anP·ÉJ{ÑÎ^¹™{òØGdÖ^|eÖ#kº4AR€F{APx/pßÞÛç³ùãªíò{è€LÞÕ¹EjU@`bV޲ &x‰ËRáw{‹x„6¥F«‚6¥#­ÜßÞúÃqîÁÆûµ¢Cßâúä}jۮђü–ëùíŸ_6Ð×3šWòë’µ*ÁÂpNd¤'V{ËžÿãK†Ù••Ú;ÿyƲòàA§<%â믾ϧ¾^Éš¬#”™>§Óù!þNÇâÁ‚`~%¥3éÅw]tÙ” ¬Y)Èj$;çþV_39‚FÇbî¼þwuß#ˆ+ƒñæ‡~óK£ÑüçW‘~æ¾o¨OÕÁˆ€1ù¡¡%[°h‡âúÒÒ¬É%ö´OúÈïüÇóW–«]ÇÄJU~ö?O¬.ªz‡Õ½}R«‹Y{ ý*öSZõ²øšG=@SO­ú ‚óú?{1¥.ìgö½E{O‘éÆeHéýì½_øK¶½u¶û:&Etéö÷Œß ˜ªïLÎyú–‡§ýßã¿•D¥ý_Zí–H] ~úÈcgøþ žR휽_QFÕ¡ààÕLcV¿‹¬<1¤—³ˆ†ç“Ç`1¬Íœ@ÒÇßtÈ»‚ n›õøHݸ„_ûhº>"à=’‚'ø=F·>ú{ö}ÒVðàõ5mo¿2ëþ¢fšŠÈîÎ )l^úÝÕÕ•k–-zÍpöÔ;_›³T¿å²³#þÕAŽW¢DOÞŽöHZ·uÝê'Ž;Ó H$îUؾÀjfø¼Ëu¾ûø«ñ½u_õ˜ ‚°œÄ‹(¬dJKê¥_÷ƒ‹é:·çîËQyO xGø´l¨aâùàë AéöŽl¾ËÖÈ%ä(ºš>^H”ÁáîÇ íV¢" üOòvì£9K‚¥«ûß}à•Å|Í÷g6[A9w÷Ü.} ž¢òÄ ÷?¼å?O=>qÀß³"*| ÂYõ?žêwéø»åöõKä÷¥•ë¯d¿’=à4Þ]®]ºý]#4im-¬i¡S~KqÞ*íÛ~“fÜúð¾ xý§2Ñ3ª õ^ÕG ž ŒÅ>kš‰ÊmI}Äõ¹°Ì’t‰p>{ë£½Åæ‡G_™õÐÞ¶¶Ýžó;“¤àë ‹èõk—~³6¥wúÇüû2úôëgž¥u•F…eèQ”U›wivíøç’¹³¡õÀ½Á=½ IáºjKM •ÿåÿûø«fï¾ùÝù#Þî'O²ZJ­>ú€^øàCÔç,ö׸‚rûŸÇÆvyJ»ÖpŒ~ƒ}P`â€%š äñö¦·æ/§œ¬ öA0¿ðh¡AA©¬rÒ{_®àþFUÿ#Ù÷Þc|½šG>5)!!áï0ñ@ƒÁöÚT5Ë¢³¦Jm)ÿ˜:õÊ_}õ^%W€¿kU¢[óÇ Ø{‡·Úˆßüzo6üq_t¨¸DÿáôÓ ]á£Âí÷˜˜x AÙ·sÛæ½ý:Ì<¸'¸7¸G¸Wÿêzøf2þ:77íç^Z2#9-ðcGMÕ TÃFtÚuh‰Xæ3A>àÍ„%;ót¾¬¹¿%>ÔDÁ@Y,p…JWe½nB4± òôJž#rH-Xµ‰¦Ÿ>F˜¥²¹ëŽ·_šxÜ<µ~ѪÍT^íáþUýT߇MŽã}…w§u씳ïÐ ÆLø t…‰§¹>@–³ö/2ÎzyŸ~ã†ý‚¾¢'ù\üMGäã£99ÔþÖ!ð‹YÉr{ݯèþÀùŸÓ7¢x£¡où>JsÕÌ~Î%ÀÃd%û–ðŒ.J¯:ܺŠ[8‹ë¥Ë·¾¥%¸ËæV˜'S\Åtú¥4öÈ:“KArΓ0=ŧ:~üÿì]|ÅþŸÙ»KO ôÒD±ay*ŠbÅŠŠú×÷¬$ ¢O)Iô4—ÐH°ûl ŠúD@ŧ‚ ½÷„PCz¹½Ýÿ÷·—9.ýîr—\`&ŸÍîÎÎüæ7ßÛùîo~;3mÔ(ê;ꛤK ±z2_…`3ÿ4îÂc‡²².0ð¾W?ZؤO÷zÿÞ]yßîíëm•€ DBiú̘¾â!'YMUs×ý±ræŸKX5é÷ ÍÝ’p’"àÙºõ¨¾u"[óM-Z›fß7âšYíÚîo‡µ|åTî±õ»¾0¶èÈ6¬O—!°°Ü‰1¢BLµ{î gQúÌ–¾â!ˆ` ¤é·|k)éòq÷iòFoQïRšQù· ;ƒ¶þþ®»78y™–: 2qQÛ1¼¾â©'Y/õ$Ç]|Y´OßÕáIä%ë¥ò©·gÛ[OÇôqIû–ÚK~‚Å$öâƒ+ÙÙG×c•Ê?‘‚Ÿ/¦4רü…S³¢c^‹Š´ç³AÖÜв[ÙnÀí…Ç ¿ŠKK»%=>ž^pýꋤâô€¯ ¢Ñ[»=ìôVÂþþmÅ_ÛÖþµýÒëoˆÉ #Å7Þ\Õ›6‰äa‹w¯Î$TFËÐì¾4Q:-ŽIêr÷íØúÃÊï¿û¶  — r°åb£ß„~¿ú£@žWáè!Gqvö¹9ÚÞÝ.îÎsÙ†Ý_± »æ±£'¶¹ääá-â·3Œ­MósXŸ®w²³»ÜBÛÌ•Æý€¬ {2ް¼‚Rã3c÷kÁvñ7;r¬;Û }{tn ¢âœÛÇ=V“²•²]û±‚b5¨ëïϺû‚—‡y\V”AwÜs1WL-é3ã` ÐãÓäÖ·?ÑüÒÈ)Úa9ì$?ØÖÔžöRûÏᎢ蛷Ï35)9$šÕ®FŸ#³G _Öéºëø¾#o"G|í¹¼OQŸ$…¨!=dM¡Š )**°Ãªòâ÷ì{^—Î={õ ŠjÞÄl„2x‡HžªÚKJ‹ŠsŠ óîÛ¾uÖµ«©·'BBN²DPè© c2×ÑoS/C=(§Ö@N³—ôalYÇÖƒ¬|Á6íù†Âì(BÖ±uŒ¶W½ÌºµÂrëÑqÖH¢]Øaʬ(w0&j Þ…ô%ýhB¹Í»3Yçv- ¿òñÖš"ê]ŠU¿‹aEÛº7+èëﯺŽ îV”°f­Z_ƒæA Pyu ݨ‘Õ£š6¹ÂVa£ç[XTê,_ ð«u©ù }Å'¡Ziô­Û暣a%il¡çñ-,'4–¯i}aܰÄä¹³mIKü]‡ú$)¤»»5E<Øîä…ÞäÉ!tÛú5›°QB„ư¶`/òàP†¶B›6j˜ÈGøÃ;ôTA + ô¨6´iÞ—ÑvÍ…/°Ë ²ýÀ ãP•ˆŒ8ØœÓËÊ-¬7,,횟4vìD>ˆ f’Å<%ÁH?“RhLÍoÁ ‰ißHгvc¨'Óü“Ü`®¿¿êÀß—Úz¹"+p¸b6w ³êp` Úv‰œ+¡£V¤˜:—éM/"d-mehÚ}Jcü‚û~d‘ Ì.ÈúƒíŒí©æ‡Ä$#®Ñ“ªušbìŠqN½ õ DRÈ©–"QÔ(I Ú†ºcØA¡F‰0'|‰¤)!¢RX¶öt~‹ œ›ðõË5ÆV‚Ïä6ïý–mØù;p„^¡o$k¶}ll1á`Y¹•ÍéÆŽ‚?ž9ø‡¾F u®Èí­- õ9#´pf^aq£¨¿?êÀ_XX€ÉLf²˜[EØóƒ¾]‚ŽÊ St[èLzS@õúg:ž²>¶çÐè…´.9X1¤/ˆ}¯1ÿÚáÊKOœtÖ Û˜Íþ¬N}[RHwÑyR§è~L%ušÆW?ØÓUI úFz7d L)lI¡7(A‰¨PcEçâÍŠ-‘‡ÁBC¢Ù¹=þÏØNäï3|W6îúŠeçíq)Ÿ·ýºöMv4ûX$\ÑA} ðR‹²µ¤@4h&doåQËH -œiÇ$n$7؃?êÀ:RgBwµM¡&®D…jÎ//XfE“ŽŠI‰*Ó›ôoÔb zÆ‘[u…·9çЩ±.dǼ½ªªn¿ž¤Pe¨Õ¥ÎÓ½#%ÒB&YPÜ YRˆ˜ˆ ‡2Ô‚á*6ÂÙ¨ÎÔ[Ñž6ºÖèߪšFub—ŸóŒ±‘U…¬+›÷|Ç ÔÌäŠÒÖˆ¦Ep-vIþ%´y:ä#Ò“%…Ö¤¢¯{|!: W]ê@EûCí‘ATè' `y~{B,÷ö´‘=~…¤a…iìzÌOâÀTöô{4ú«6<ý]™†~·oï´§! ꦤñ}ÍSTX´Ð¸„¥s×T—Æ×ø` )BwÑqRGJA’AV:o4¡Žu`=E›DûÆhp×?€0Iѵ!€Á·È=ø}Äj«]Çtýlm« 1_¿¾;-yüožäñ&M0‘”ŠzŸ¶kE äyݨ¯:ÄÉJíôáT݃qó—YP|•æ"*õ@Pè«+³)ºä“]·àº×Mƒs×G¯€`c¥9ìV£jµ^º×šP&,˜Y3C$±-%ðÒu¬ »¢ã@v"¬œ³ùˆ@”Øßƒ”)¨„@ï3î`ñ·/‡Ãªó1‰ŒhÅþyÃWlä]å=òÏé6”=qçïlÔ½›Ùð;~aý˜!ëÜ÷±Gn^Œcê_¸óªwØUçs)~F»ìßC×¾>"²YLWvﵟ²çïßaÔÿ_7|Í:µ¾Ä¸|Ï áÈüo‘ôÜ{ï“Òíù©¬ÝO¹°ˆéw ë÷ÞO¬Ã#£]qî‘=ú°ó?ýk;äQ#ºÅ ;ØY¯|†[ªqÞSîu;1ú¶ãXxK1jP'(<¹§Â;÷`çÏ]]nkÚÿ*£ÜîIo±¶wÅy­”ºÜ¤okÖ‹ÌŠ/¤½œðƒ×B<È IŠ É$ºXPÅœ7š­\?/°kßâ|öÈM Xdx‹J`thÕŸýºîu6ãËK°>ÒvÕyãŒtëvÎÅ<'1˜÷ŽJy¼ð¦.Þ¤­Nr<ÿØ”f$¡™ï0‹94òo? Š3þÎþæ6ë«ËY©Z`ÔŸRüºî5¶øj—+8)¥æ#Ô§æ<ºZgVuf?}ÎEìð7¶¹ýAÖõÙÉ ÓBW©7[XçáIÌQtÒ:wlé·ÌÍš]qc•y¼ˆ¬s}¼(K&­€€nâ‹c ²",X—àé=eŽjb³ý¥ál[â#Æ–·Ùé>’5w6k}ë?™9Æ;à ó oa-xÐÝ?Üj£Ïý‚y¸Çï••O?¨ƒó¥“ë}Æm̬„b ÐZÆöB‡ŽcŽŽ·ü[Ç‚ßN¾ÿµý#vŹϱv-ÎcÛ÷ÿÀVo~—]Úw$ä|é5øBw_ôwÏëËhÏ¥}ŸdëA² ‹Îú-PÿO~¸—]‘­\=hâ¼eMpÅ<¾ŽÙÑÙ8ü'&ÑÛÎÎ?ó!öËßS]ij;8œ½™½óÍh‰I“0µ#újÌ”[[®Ú¯wïÎ.LIãµ§¬œ‚>°RáÒ_ŒÑ,â yðyä,ÏPJ3&xZy„[ö_¦æfÂ:ugÛÐit|tL•ÚÜõ+Ü»¹wcºCeG|ÆÚÀ²r|ù‚*óUÙ¼øX¿O4[ßÄg3ÇLtóæT•Ò‹¸5o‰‡ nE#t2x@îÆØœ³`kZ]Èþ‘±Ü‹œå“zzO™cš0­¤˜åmø³¼œåoYËŠìb-oÊ~î|)©”¨ŠZi{èæÿ˜v7íÎþh{iBýk|‚í_i)‰ŸV‘Üç(?<ú>—-3J‚–±g±Ã'6ƒàЇgŒ­Ýþ û$EQjæõ]Z_GU;;ˆõ„(d_ÏšcHÄdj\KOµjz6;”½Ñ¨ýûï/O²0U s;£Ý•ì²¾O±>"[ ŠTÿÖ±½Åéi½èÜ“íÞêÂ`Ïô$V¸sSˆ…UágœÉZ^s;ðÁ4¡ò÷Ná®Í,¬]çJñDÈÓ F ¦$‡õ:º‰ÑJ»›tóYSOï)s4¬$x[é<âÖö¾ÇYXû3Ê•Y¸k ‹è|f¹8ONLh»goewmýÄÔ*?Ó„—£÷žHšè×^’O~ ™æ´C YtWv7eJ$Ë-Y®èZŸ«öýÀúþËãúzsO5½äjѽ·ñOÓ‹2úü8wýŸ®²Bq_gîuûz`‚?žì¦ÊŽV> •Ã=>'³Ú†ÓèÙ]nÁ «Éå<[U›Du`Ý;\c\¢yADز÷;6ùÖ¦Y_ø¶ìŠÄÅâR£ØÊÞÄZ7ëS«®‘a­Øåýž1üNJìùloÖJöå²ÇXIÙ‚cmšŸÃvtš”kvŠ'(ܳ…wíÅØO5WT+*,—€\d%%Æ×t!²ëY¬Š^¼÷Tjºò¬MÇÅi£Ü+“šÎQe’îÐÏÅ,3æñqÚ‹î×Åñä÷B{ì8±ió¤w":y´Ð5î8!MIÂüÉñfsó³Æ4Úé…/yÿËšÿ¾«*¸§rÿþÍuîëA^H2òiX‹ä ¯2*æ“$¥""ò\"6îžÏþ¯UÎév7û{&Ï* ¿mx‹Ñ& ,Q>ñ£“fSO{“ÂúŸ5Œ-ýËùì~-Ø]?Ý7èsöëú×YL¸"¼÷ÝâÐØoÚó5£Íb+-GèhR7ú²iîÒËå9]O²¾Hg=^œÅ~1›©'È5Ä6?ÿâ°ÊýÆ‘·»â¹9„µºù~–ñÜa‚8˜L–ÏŽÒ%舟¾ “f‡wPõ⳺ÆôZ´3{ó¹5©¯–Ú‰YâP‹Éfr…´…šãx2âþ]!¾ÒijºùJÝQút¨Ì”ÕÂÿˆÔ”‰i±MÚ‰ÕS>ŒZ<ú_ù‡SÓ”Ìò{6tX_I¨Ÿ"â^H½åŽË3–2_ ©áé=•5ÿ’˜‚ïú5·ywHFtï Œ¯ÅvNt'Rœ¯!3ºƒ†ÙtÿN‹/3©ø*éd¾1q,^I‚ “–ý´:™Óíˆñy…÷zžÕy0;š»ÍèĽÏݰ92¬…ÞóY¿î÷z¤M‡/>×Î;ó_lźWYQq¶ˆ:­÷ä{ü—…¬åÕ'I‡·€Ä^:ȘÓâø¯ ½ÍZ¯éÇ_B/‚3|ÿÛ̸뜰˗|½üq¶jó{¾d=eóì›e3üK|­àñ Øø¬4†€NÿskC…®X€îL!wÒ,).Tا¾mÃø)_;zXñn¨3hºÊ,¦ò¦{]j+ ª´4­|c¨]ë^6u¸N|¹ :Ô¢ÅR)¾\¢ 8)UBt‡ÃQÞ×Wó õ÷Hñ.äß¶ `jéÝ ó\Vãð }iÕîS™’’Î7aݬ$Ø<î«êKŸ%ô),z‘0ù½È6ø4/TcO =’IÅ!¨^Æ&ñêdx=k´Âu#¸¿Øg AQr""¸ÕßÕ)ßú[º”'"ÃB˜æh$ÚÔ¬†ÃÃÂBüÛx…ISƒ¿þ¨{Íhû~Õ^\’]h‰ z’R`‰Ôí¥¥˜º¶þFU>×4í&XTnRLµõäæ3é ½>)ƒ-Jè hýçÎ[n©¨ýsïEg?]µM­d©˜¸Šs’ÌWß 7†wè2~Èn ø~µTQ¬eÖ´mtpÈ˹ˆª“×ñ?wÄrBš(ܤ =!¡öÙ ½TV’/“ÉdYˆ‰Å ¾XW',¨+@ú‘ž‘¡c˜ª*«ˆ§ ¼b 3+Ì¡wýýYwO1ò!‘c+*,8Zl‰à%æPÄÔOÒt,-2fº×Oá(%Ì:ί7£àÎcMü¥¶‚u†¡³’Fþ(bÃÏÛF|™›éçM…Çv?ŽZh+eçü‹RGÉ#ÿJZt "RfKØ·•Ò âM[ÒnE×W¬oy®CÃ45¬n{±±öšš)³’梒¤U)3htç6ÍЖqVTtfÐèV•"¤éÙ®Y¬Ñ'IFUikŠõ&˜—‚5 úúû«î5áRÇk¢“§½¶ûÖµ8àûÊ&ì«£ì€d'ÝHÇ»vÐÌ]îú¤¼ŠBŸ}¤h?¬Çé«έå†d@ F¥¦ñƒ®ímÓ?¡aËñª¿¹Ë‰jÆ·ˆ¿(evdk÷x:Ž_mWL¦8tYD*¦©î<:¢éDÌAs æXYUÊòW˜b}þႬêÒû;^7›^,æË;ô·è€Ë#bµz¯n}Êâï§%'Ž T¡rž”@!+å68®Ž­dÛæÑ,GZòøT¦pÛæ}Ù·ÝïÔè+_CNhSVlöÏp¶sΓ¶Œ† i†{’½¥yoöm»´¯{ÜÅrCc³0[åMX\21…°–_ï™/èêì©“>§k[¶iß!v<ûÖ¦MÚ†êß¼ê»ršn1ô 5ë¬gÛ&,$$ÄЙtõðT'‘^Ô›d…„XX‡Ø0–•ŸËŽ¡þmƒ¨þþ¬»§ùNX ¨£'‚B’•þºà›ÅÏ8ãº_:\sÇö¹ŠY¬]âCþÌ‚ÅêtÒ4{IΊoçÏ/Ó—ô&ýE]üY¤”åe|R\‚mÝ‘ˆ6i_ôü¿¦½Žoä}°r³¢£I,4G°Um/%¡wÊÛ­ß°ó[ïßj*”´¸ËÍŽ#­*9ËàKöý˜£fZtÓ·_=Ú0mUÈê÷SIRü©L( AGOtœgûwoË–oÂT˜Ç†°–->UIŸR{+vn K……²ÐP'± Ý©Þ÷z“¬P’Û)ÆÌvžhTõ÷wݽÅÊ‹ôd…† J óò×ü¶âà ¯ôäÏé×ìùÞûË  ö1£$)>'³ùñfgìiþ‰b»ŽëÜà‹a")a¡¡¬KÛfìhNÛ”¹”ŽÞÚ7ÿ²A-*dE Nº °7ëÂÚ·ÀçÇáa†®¤3YCê2Ü#êM2ñµŠ g¥ù, å±#¨‹†« ê^ÕY\j×5Mw¶ò$ªÊPuœî¡Îžæ¹(¢mÍ/?¯‰mÙj>;ûœ!¬Ëú•û–ð†²¨…ÊΦÝùÞm›?ýcé¿—éIú’ÞdI‘$ [˜9nÍÑò4ÈŠU+P8Ñâîãá-.þ›_q>—ê 0µ~`2›ßŒ4ÊÍ.Z»°ë-g Ø¿ÔÔ=Û;>q4¼%û«ÍEúž&]ÁIØœYxƒ›­@IDATp†-³ô¸ÊkˆIRuY¦;DFÊ…Òâ’'r üBRhèƒ:z‹ÅbtÒ‘‘¬o—VLUU¶íPofW[°fMÿËÂÂö–Ó¡>Nȃ†^ì° tlbbÝÛİ(èG:¡ Åp·úTUï’â,¦[Â:¨v¬VÄÍ쇦þ¬{UXÈ-ЪzÄíZ¥ûÎíZu‡”‡:y'$‚Bænrý髹 ñ’«°ÞçÜšÖT¿lÿÏJ[¼×g â! Êî-ç,™÷ ó1#=I_Ò[ ÷àP†`D Œ¬£ïVë{aÏÃ0KG‡ÎÚ–ÍÔ_ˆ¯¥21AåÚ7¬c¸×áIë¤+KKùüŸ:_×ÿ¯Öýsw›ÚauåV…‡™E+u9æ™Í ay!1, –—ÝMºi¹!1à&:ˆë¬äñSƒ PÝ$Iqÿ…åqP P\Xp$¯¨„¢SÀE]‚³³VX:ü0ÈŠŠŒDG]ÊzwnÉÂÌœmÉÂâ ‡)ØÁ¢"þÆ~+,Ædu)¶Ú¼4}jK_²«EÑX÷X…u€%::ŠEFE:’®¤³ó ï JÕÕ»ÔnAs°öšÆBr‹ÙÁ¢Îú‡¡þøê)õ¯¯ºWŸî#ºŸì%%ue ‚¤K ºAéM×üÓü¹ Êʺàò÷}ÛãÎ&òöéÝŽoåð`¡*¶ø?c#}fL_ñ“¬£´8oíÏ?¾ÿ×/ËÈ‚’‡t¤ÍÝ’â Aƒê«õaúÝè“ìrŸeW§Ç›Ö1™V«~ÉAGê9a±÷¯½òïVç»QX^t“®j ).8°¨˜¹w ,¶Ÿ†˜¢ç¿a}*7Ý–P]õ/IJ½C. ¬j8mß¶-ëÚŸÑížõ;2ØÅ}ºV“Üóh.!ÇYòË +…5¦ gg £nfa»Žä¡´+;Ò@_Ò)¼™Ì9ØS_äŸ@SÒ;0㫆 Õh3w°Öá:ëÐ4ŒEƒ˜4iÍbb°Qޤ+éìËPиºzcP# ])f‡ U–]r;R˜ú7DÝ´§ûm3ÏÚ¿÷WœÖµƒ&ðȯƒXY(,بÁ7œÿþmÅ_ÛÖþµýÒëo¨ö8sÀ¾Îb@1õ0{¡aÏ硚Ý{Æ áMÇO³ÝÒDm¨WKKòömÞðÓo ¿[\X˜OËÙæ”mô™-éIúJ€pª«•Ó=ú!md‰ÉT Àq¬‡ÉM<¯'Hq“]ºYß… ¶Í°>AD6(ƒ$)Aù³œvJ¹ j®mXõ¿º!çÏ»b@Rêܨ»¬ èø5¸˜AÁÎÔQ‡` úŽE,;¿˜e©¬D gv-³Óúïw0£+ 7a±XNš„šXtxüNÂ@H ”¦MšÇ4ÔC_äøjEZ×Voú4Ùb¶ÀI·˜µ±«,¿úÐRX{TG˜A¦„¬ºì¢îîúâ>GÑÿüÝW/î5÷$ÞS~2!KÄ„ЏG STT`‡Uå;Ä/îÙ÷¼.{öêÕ,4<¼‰Ùïm?øm—?š[Tlß¶­›¶®ûkÄÒ[7² I!²BÇ4Ô#üQÊî|ÄÈpÊ#Pf‰YŒŠÒÖ(ƒ$)òg;¥”†hü©pìÛ±õLBv×ö}Y¬G§6u®°Óª`b¡°hè‘ÎvÚ9Ù¾üÁ°J(œjÃËYsølØÑac•R ›3-:9Ÿt ¢@¾¶!LXPÌbUÇ(/ÌéƒK YPŒá—Å7‡ÙŠJzRbÃW%õnî§z“ ]wÒîŸ ;ð#™ô)—ðÇ Ó·”„:O…5E’'È Y-±…n[¿f¶­8&BcX[°ypèuz!¢Ê$_*“ȈÞ! iE24^$Ii¼¿Ý©¤¹hl©Á¥Æ_ýuÁ·K:uëqí§ ÿ=öá[”“C¨>×›:l"¨užAÁ'ºð)Fg]\FRȱÖá ’¢ÑPÏeº—E–Â!èK#úↆvÈO&""ÜØh˜‡®“nþ U{©ÝÁpÿhø¬'ûçùóæBº¿h£û­®d9 @7ˆ8'²@ ")äTKÃAtÓ‰!¡ºˆ1‚(OtÝu†$õ‹€$)õ‹·,­2ÔàÒ&\2K—b\½hõŠe+¯þá·¿è ¹²ÎûIbbv’§U#„E„‡…œjKKK çR ÃBî$Å[®RfDq"šžž†Xh(‡ˆÚ!'Y"'Î Üœa…¨ —÷1õ]oÒ0êN÷Mæ‘lÓê?¦8q”:n"ÀtŸ‰{‡>Aˆ¸) r`|õƒ½áT‹}E’âËýLåPå¹?3‚Q!ÂBçô, ?‘Q2H’¤4žßêTÖ”Þò¨1¥F•Wc[»rùzÌ?ñ_œßʾdú¿n¹œ×Õ¢B¶ø¬—, âód" 4ÌSZJ_¿ÅaXRàõnXR|µ¦PyÆ ?YR¨<²¦Ðü%Dˆ˜X ëŠóKZP€›+Ôw½©à†ª;YPˆ ¬Ú¼›ïß¹õÕ‹¿ûÔ©Øi»°©Ãuü‚ôÐ=LÇDZ¨,² ¸á»BäÄ‚‚lF 2ÅFå¹*›ž!ÚÓFפ ÈÐx¨3IÁÓBãžm/Ró @@4ðÔÀ ³5½†/ýú‹A¨Q|ðX¶~ïõ—(þòQ¡NÔ˜™•HüR r‚á'Aq:׊/aêBR_"d] ’bl8'²â<Ç"€Æõºô]TJíA”èz“&„/…ú¬;ù ÐYPh2³Ås?ù*ëÝ_è¸éþ%â@{A¶Å‘APÄ^]~h*‡‚(SAVŸîLˆÿ:†ØŒ@ˆ–2%:“šTÂ.å^"àÔðRãJob\]˜Ë-˾ž·ôXÖÁ#ý¯t׫-ŒéÓ½ƒÞ¿wWÞ·{û:Í£"¬ Ô‰_ºÀ²A޲bˆ‡ˆ‰ 'bïmÝDGM{±Qy‘VÄy+·.é©L"G¬7éG刽¨g êNó ÐgÆô9Éjª=oÍÊåïýµbÙJ”OÃ<ä©\„ùëR´Bm0ŠýçEË”rˆ7Pϧ7ßr“dá\+..t,û拟–}Ã~îÞ»_ÇÎ=ÏìÓ44"<Úl¶øeþ‰rÉ“ F@U1‡lQQnQ~^öÞí[7o_¿v7&‹ ‘’`™Ì¬AHD 8jïahTÌhÿ]–”z#à—·Ïa‰ÉûaÌÞnK¼ñô†SÖ¾ŽyœÈ õDa‹ÁÖ[l4þ‰®Q"ØîætœÊp!@¿â ?^!kIÅÉÌh¾aI¡4ÂÂC|E .Ñö=Cû̶%uôU†Ì'ð:[RŒBtþ:nØ)ñ ¶i)‰Ë=)X¦‘T€»5EhêŒÈ_…Lõde1&ÉÂÞÝÒBd…‚Èã<“ÿOEè~  Šðe¢{CXQˆ”ÈÉÌ¥jç1só xÜF@¼)(‡€_õ²õ¶bXø·´¼4ÝOóÈ ðº'…E…†p"Ê6²¬%%“dA¼ aE!‚ânEN×DRªšÌLœFPÅàT1Κ¡Ûüw°¦íÌÎ,›v=8••ZøÅ’B7j\bò0 ²€Ù¼'¶{à '„Sâ©÷JˆˆLóîÇô¦¨I²ê½’²@ŸmЏ/IV6ñù:íé~¡x"1duyq(ƒ/sr|bÊûÈÛÛM’ Ž€_,)B˸$Û¿uMŸÿ”¹ÌÒò!iQÈȽE…映¡Úèk±'‚í>ÝË~½Ÿ!O†àC€È†Øˆ¤¸"¶DLhO]“>(¡®,(Æ (Óïæ &=9ñµºÊ”ù%ž à÷FÝITØ+0¤lÀ*OIO~™¦èd…H‰;9ñç$Y5¨ /Â""¬#‚¨²"ÎÅõ R½qªbø 0ö´ïÍöœ$(ówl¬Zû¤ú¹»ÙøD­†}bûX‰P¾“ó¨4ÖÛ$(ô¦{U"-´‰8q‹}P(,•‚¤k 퉈MĤðÓE(̓âüÌX¿Ã<7`^¾}¨û°t[ÒâÓYÏà@ `:9ÓfØ3žÄDoOÃ8Û79–“å‡t®g¢Prl“A"P'0´èÊ_6u¾ë\œº`8ÙõÃcèáÔ­hÔ ÀFÁÞŽ&è¤yPðpíöëí-íß”>( ðƒÈ"ëg DÒ„ šãzàÝ$¥ššóB‰€D@" "@RòÊ–:ÙKuÎL·:ˆÔ“ªH$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$S ¸RÆ'¥^z*ÕÉŸu‰OL¾axÒ„óü)SÊ’H$àFÀÜêùO»a‰¶·ÓÛ’DÎØqìv2³åÝtëØ}þ+¥²¤‘Öé1%jÎV3 ¹z†mÌæÊ)Êb4í1]çGqö[µiNã c#¹®ý¬9aU—H$§AGRV éÜ–—”¶ãÜ¢p%WeÊî ¿Í,¬ë¯br•Îø2®³Œëý@î`ª}ܰÛÀÙ)‰+ë*¿ºüÍÙÈüLf›­Fé™Õ¥i ñq‰ÉV`¶8X5›ŽçôÙ·{ǺY‹Å½ïa¦ÃW?;v?çVð>$‰@p#Ðà$EGÏ÷×­mk:{D⽤¨…N˜áŸC×çš¶êæVëW¾1™Ø»çÍÏÚã3¤œ¯L·%| ò£ã]‡ã—± qþÞ[­œ:ƒü-·ä 1q}y”+‹ô¥¯D·(Ò ÂC4dïžMâQ²0Õx¤X1Ly)ï»I|¹¢(ŸuêÜkNï¡K½,"¨’[­KÍYê/óHó3ÇË*å‚H2åÜ|hVò8i ¢ßEªR3 JRVÝÒîºÕ7·yMgúY¤¦AN*è«ëLAT?¦kýT[usë÷7ƒu…†Fêtþ ¬*#HÈ3S§†ç+\Ë£Íéùê[Ðf0×ù”ô”ÄÔÇ­)çªvý$;_çzŒEš#Ÿ{ÍúÌ <ø#5“5fˆ»2qI¶  '1Í#žƒÜu ¹)ÍöüvJó¤uR»»}HÙœfs…ýõ´»ç~Ò¤èì<û4èq#Ó¹…s}‘%Âôï·Æ?FéŒá+…Ïãº~&ÓØHÔ#'Ý–t¾» :ŽOL¢éÚµ°JýˆéñÀ¸3¢—˜åÝY/',é:ªÚk8?¼ñ0ç|FZòø×žzéèb5gôë Êø!,OEŠÂ>©lvζ%L sæÌ1-ù{ÛÐãgè1JÈK°ýĹ2#Í6þ ú*¶çNÂxÛx«oªs¶F1³ÑiÖÄU”¾¦ß@È{ª—®kã˜Å|g ‡ìD™Á¾_9µCø {fB¡#ÿèQ­¾:‹ÆµÁ𦠉™ôý+¦ñ7>çp‘÷jóÕpaø ¶;»qoÎY>~ßuŒ‡¼&îù²ÖùR~äªPÇ –RÈÉ"*IJ5ˆâmI™VƒŒ^ˆÔ{ЭW™WßÜú ]S ‚â¡fX^C¾õÝÚ–:ø:t’׃ì$!&‡Xè=õ|û·èDÁ‡äS”iæpë„. (+aÒÙbæ–Ëhgz÷B5!YLfó÷È~{|âäB™‘Ó§‡‚8ÄÃúóc™Üîf®…ÐuÊSª–.Dgn7[BÎf~7@g4î÷Šü´A™Ôm¹Y¹Åbá×![k{¡öÙÉ4ð¯qèVä»Qáú£J¿å䵓G 71 CêL{ûñ¦0~®îvhÚüÇ­“ÛPÊÇlUUû¤l äœÍLl,ô3ü…ÔaÓ_™iŽºO¡Î•Ç#-‘ý#b#&rÆá_£Ý)JúiÝÖ+pÜ$èAz³rñû4ëÚŸt^¢æ[½/WøÌ¢\B™¦ê+ž°¦ö¤ëÕýtÍ=ÀÉx0J:S”'%Aq"³è•^'ÔŒUÀ;1Õw qŒ{¯æÐÞ_0™½`z³˜ —=:}Gs°O!íéK ¸i×[hz隸ÄÔÛ=r$Š·Ú.ÄKMêiPUYE‰€ß¨wKʪ¸ ,«Vmž‡Ö±ÊNÕÚµAúø¯Á­‡žÿÝ¡o<̃>Uï@¼ñµÔ5 …]¦+ʵòïžœø¬ˆCü2:Þý° ñY Ç%¥ @§mX+þ"t•b6w›e·‡"ãl6M×FYÝ`©0ˆÊèÚªI›7žzªÄÈXÝ?®[L‘æÿ¦ðç¦LIÎÉ)~ZÓìý% #£@‚6Àjd+1/.1e ˆÀpt8éˆ;twä“õˆÒ ·¦,ÒT6ùëÔfÓ¬£ŽkL¹‹qí}XW8¨­¼I~´+üZX“¶¼•’¸wDRêªæ`¶(ýfZÖ‘ „§‡%&_Vj‡E„±‡ç¿r¿[<,HɃ@Î>ÀPÅm³’Çÿî~ít=^8Ùr¾Ãa_ŒÛ§¹¯àž¼•e/[1£É5W<žã±5bØ )72M{ÈÄMWÍL¿Â­üÿà^Åýä˜gM[œn¯³_™›ìÆyhg7ÀÛ8•—ZKz'),c?uzu!(Rxc u0þùªÛZ_uáׇ<í¬ž„Åàt¤‡S6båÜtëøMîÐcä÷sÃ|Íp{Ó–´ötâd•ÀP¤Â²ñÒÕxûÓâãí åH ˆÆ™°JvÿÒ‡ò€ ¹ôÀi_È,ÒTõu4ôFÑÓÍ U0(Ýá$)ŒÏ«• ¹Y¦ (túÊèÑq‰¶#ºÆÉìO¡/ÊŒEY_;Oé [ï=»ŠóŠûYÖ„ Ðù`¾V8ÐjÕ¿Ê´§ 1)¦{ºÞ $bÒÿ!ƒ@¤ÈÒ„(G£Þ/Ž_’âËu®×»"pPÅo`\†5¨;,_£aÕz åW÷<§ëñ¢WÃ;:Åß×… ì ã¼¼¼Üo–.½jàÀËÀ]kÜ¡ã÷`s*gFs«TÝ~$Ž«‡|Ü:á»Ýñ}˜Å2ð ë˜B2‘WXõf0KË‹‰ÈÄ'نÆ/;!ÍzÎMÏbØÑøâ­ºá@¤{]È£=îG%Ó‘’kæ­8mmîïOÓlIïÐuºÏuÎáedS <ö”_aE|eöˉóœ±Îÿž™Ö4D‹áßÔëYèQ‚¶cöÙºÂ'Äÿ;Ý–x…(,->Ì;µ¼0íÅã…a4ž¥^x‰x”·'&sèœ^šºÁ:º[WØL÷ºx3,‡L Q‚ôõVßÜê~4„ù¯D= ~€ŸýýÏÖ‘ÉTøXX!zR£Úái É0é&WJçеVÙ± ƒäE5øg“I¶O§¤´F{½nRŒ‘d” ð/AÚJo•ðu)r¥ã<XW”7ÅròšbR®Ó”ÐÕ"ÂY†8®i:À¦†ÀY(®®eÑ^1ñçqsÜQC.p=¶˜küš,Ç„KQgÞÚ4îWø×|SÿíÔ胸]ƒü Id©Ð£´ŒÄ¹ÄÂåF)'Ž"²âo âÛõÀü=üÏ·Ù¨ó9­ˆ$WÕbògjå/ ð[\^ôÇr«§ò¾~“%U¥7¬'\ÿjž3Ã:nîé¼bUý§{Z8Ç?Š{gƒ‹ èl<†Œ^ 1‡€Ìóà7óÃH뤔ÇÓáÀ2gõ È}C¶g kôœ525µ%É1ôÕØÓt,ÂA-u âûñs¥º@ïZ‡LINMC´fSìèòÃçÑi¨¹É ®˜ÿÄoxùë„®”Ÿž”G Gs%ãèÕç ÚÐë ,I¶'AP’AÔßáë…+ &Á 2e©ÑjÕ:,‡L]`Ƀ GÀèdëCÇw÷Ž*,:úªÑíû±@XTºØ³ùXˆLò£X—(4P s¼Í§ãkioÍäl8¦U„F‚œIï/,bëà¯ñ,4[ÜóˆcEÑwi½Kœub'w 4LÝ&“Ò¡áÚvÔ¬°#3¬ kEÞ@í©DùW¥½œðCueŒ’•' ç 5MIcZ>hÈWÔ9Àei¦ã×ð,{Ê}h£ÛYÚ/'™àf0ÍÞ!ΚÚËXG®E¯Q%VuA§õÈe2¬@­ÅúçqiiÅÛfÅ´§Ãù÷“Íÿ‡zðw]ñL^09ô½›ž/ÙY“lX #sN·„yo_ué8Sö£s¦{›,ŽÚHì'Љ3 † ™á·KC":ßiädM×ñXñ\Å—#Å#”$9Îè“ÿÓ“ßvlŽ[¾“‡µ ™Ö6D;ÃúÄN¤¡—1dŠã\X#·èªJd~W†cÂØç¯¯áãCÖÈEÿ¶NkZ`/¸Ðdááß#…Ížœ ¬GoÆ'¦Dâ9J„óúÔ¡C‡:(dT;,‡L „ä¿F‚@½‘”â£xÈôÖÀÙS[n=sr¯o¶æù]¾Ùôf×–˜2.ÊžVÀŠ[•ªv+´í¬}K—?Œ‰+ï¨L] ëAo8˜ÒV•¡­ÒsQ¦¶u¦T•ñ¡!ZQIÞ&™ÞVP¦öæÄŸ0|²Ê®ê¯Â´›ÜÆœ¸ü :¥çöóf%'Ì©Rp"-Ì2MÕí¡ñš“JoÕ$³äHþ¹&ÆB\f|“n·àmoIÙ›* 7…/)Ð >Füí Ó†“ Vë@$⿞J3Yfµ>\Lñ×ôíº_ÿ,–“ã’&&„š"ö–¨y¢'ºH±°Ë)§¡i´eXv®}ÛxòŒò4ß©–SB€ê‚ÍFC¶ñ›VWÆ”çž+Ä›}1Óx“êÒ û§kÇéz„9êã5ÿ•VÛ%3­‰ÿË=QrÈñ‘´äģ̑ÍòííñüÜBa/ ý€³ »uxWÕ ºà`DÒ„ ºzòv†‘/¤¿¢c)  ‰| ÞMÿÏedéN8 ßL׫ 5™‚Ä{8D[^:ò-«„ØÙ˜má.†R˜¾qŸáY‘©N¸ œeMÜg}¥…n/Bj*gíåi1HÖÄ¥›vv†œ]Ϊ –C¦åñ—gÁ€R_*b8ã¡–S ç¸¾4ñg9eoý·âSß!jÁ~¦:ÖƒP´ÁW=7¸¿ÅÏL· î~˜¶Ïnòyu:X­CKÍfå6´¼ç1»º£¤Ð±Poä¥× ˆà·¢a̓ùwq†j+—[áï1B¤ñçžüc¸‰ßn/Á×Npü̓cñ(ï"Q®Ó›Û LÕvoiO³Ø­F›^}Ι?Sœ3(_bæz§? ÅÑ^¨%ævÔÕŽ¯³~-±çf¡Á…ö^4À”eôh7y̘<˜ÊïBâxø Ðþ´ ‹¦X.F¥Ï TÅAî[0½; Vhè»ðzß«ÚD:ï‰kÛéºÓ‚Àç©vö ãëºѰÉ)aªá»…`܇qü@7O£ô"T;X––…1ª®RG~…¤Y,Ês¸ïÊ:og"37͹‹,9'J†àÞ?439Ñí¥9÷H[Ë©gC´å¥g ¡#ùu)sð˜‡giô1èLŒRÊfRÉD‚F›,2® ›È륳ڡ`9dêÂM4\7u Õýë֎펲,èæaìýê €J÷ê®Q<9“b‡¶¡r€ÃÚ/ˆíO ÑKhx§r*¼¡Ù’έ_•ܲ!œÞO¤¦6W¢¢ò«r~MOH8Y·cHÃÂf·‰4+“e1Û–4¸bYUÃòò>âi+€G'÷ õüˆó^q'6 )æ‘-ØE‡É*"Ò” EC]tMģΗÐñl}ºmü|ì*aù†õ©\ÄßIubóšÌ¶>wÔ-[¿AÅú¦'Yd•ÌüîòNåc|)E÷{ÀHf /Ùs ø©–B>†ÒVë{¯«™H?<)õ2MwœË̦Š8|~þðøBnr)Ó˜uö]£/Ý`É;¾Ò*íåD1”!²yµÇðãSèØiŽ£™”‘ZOä9zãp®D/°Ø¬/tÉÅ““,‰ëÞî=¢å L¬ç"šEþœ¼ ú j{dÅÔÞã¿§{Ûæ»E\u{9dZ22>X¨’¢k¥=àe¸%û¥:bb¶š„•Ykªó¯)¯/×ÊÆäsªËû¦uŒá7SÝuOâËêTŽ x’O¦qG@¿Ðý,Ç~ gªF’Ÿ£©™öŒfª– Kš0²½©Ûß™a»Ây¾v-}µR2ÙÝ)í¥ñË`é8QÂõ7ѱ.™‘’¸_èb1 ö•§ñ…Ë“¹Ù\•…éZIÿ‡¾‰>céjÛCÎXiºÒ|=GÙ­²óÔ±ˆ³T¤Í`Ñ3ñu æ=â燚ÍÕ&·¦ëž Ñ‚p`I–D/Â÷…^x૲Bcl,ä~]TŽÂ”/ß«0œtå² 9çÓ!çYX7Sd½5# wº»eW$¯n/‡L«CFÆõ2܃¹:ºâ0ǼŒ@×AÊ—xŒ¯‡û݃2Èzb¶ÄöÇß®«ÿͰo+ÔóÔc˜å8EQø(8°–ó›¡¡²Z`(ó&8Õ¾ã^ßAýzNA÷ü&dM³«ÇÁÿâˆÒ,‡bjæž®¶c´©(¥_¦úË1Lœ¸κÇÑÙO¯˜VŒÏ`½ ¡ªŸÜ?‹®˜Î“sO†hñ¥Ñ7°Píbùö}°¹Èa çè=ñEá0Låµ1·]¨àGÃÿ' Å“C0üVÒuæ˜ÆöÉA!µ˜/)‰®{ä©§HÉt @%“| Z5¸ÍøL „lw™¼}Ç ÓWÛÝãä±DàTDkïìD½º´nœ¿?øyíaoÊ çNÆ¢ ë:y +ê!ŽO¬ÕéGº :»S¶ë‹— ÉÒ’aÿõ–ŒxR|QT!‰O§ÎáÌÊC´B؈ b£E%¢ â|ÙÓt±öñG„#»/2d‰@°#P?$å–Ö#á3RéMÆÏà¨ý¿;\n¼×Ïò¥8‰@Ð ðÝd¾Öˆ>UHá3Öžh (þ#`uy‚wluž7C% ¨²,Z"pÚ!P/>)ŠÆö’ÇX`¯7¿ÀÖCJ—ÔŽÞ.ö¡ƒ (Iá;%Ÿ©ø¤”8|‘t6ð{¾*7J‚Rûý&SH  ‡>˜BͺŒß¼Œ@×AÊ—xŒ€Î]3{œÇË„˜å8àex©’’ë,Ÿ:gs³éâ²/÷ü#WJ‘HüŽ@½ ÷ÖÞÜæi=`o~&ÆÅbƒïú!)P"„,zÕüUuü0Õ8Ë‹5·oýQªüÜ>`åJÁ‰€DÀ z±¤Py˜ í}·rýzˆY)óµˆÈyžµZç“Fy’V¦‘+ײÿ†7ŒmÒOÁâ’  ])W" ðz#)MšÆÌÆç‡G=UÌ«t:ë¹»ªÛCÈŠOœÜƒ>‘¤/D\0ïã“&ö“¤*˜¡†Ó>åÅl½¡H¿]3Yð9° ‰€D a¨7’Òã㹘4éy¿W—óýQ¦¦)žÈÕyéCH·¤ÔnÀ“ô ÓÇO;²#º¡õå'7ŒV?¡pÍ<ê?-ù´ÁÏ–ÌJã?=¥$‰€DàTG Þ|R«·þ«¬b…ܺ¼M–êŠéêþßdÖ:6OkcdØS6‡rý¬O3vJ¢kš~Z™kò¼ˆ‰zbæF,箟  §õíùÆ’u['à+€`Àª>ù´ãv¦‚¿ Ÿ/NÅ,Ö_c2¥q˺øÚsÎlA |9Tõ]Z¿r6b•¶‡h¦L¬NjªV޳ìY(£~Œí@%òc}œ7 ÷qè´kzdcZøËɪ’©nŸ ߬Ê…žõrâWuGRJh¬|?-¬‹^Z² ÷Os¿Ô³ß»t9{@ï¡Ký!ϸgY¶Ù“yShÙ Îêì<š:¿¦yKFNŸzE›6jus®ø+)C" ¨Œ@½YRDÑMºF?Œ·¿ÅâÜ×=:t;¦®¼ß‚BeÔR¯A§¿öM[Òn”Ÿ…)¼û»ÊVµçu…Oÿ °HW2ü­˜Ùñµ×m½i:âø “9ô"”ÙÜlnv¿3oÁtÇLLõv{óå-©ñRUõ ¬€üa{KÏ 5‹1«ÜxJ[“œHvØÄMI 9P® ù^¤<Ðe$γ"-WA¡¸Lû¶û0)^KÌ\ÙWᦻ5½Nˆ®Épz"pã3Å{‹ù&ÖuF€³ JÔÍÞ”áI)C1µ{îãÖ”rkWѪÚêÖµºzx9Ž?0&9«BÉøR®éßV¨|‡t«pÜ­ŠdFTue¹§÷DôÝ>Ò:©ÊŠÇ Áúì¼Òï÷û°¤”{ÝeÑñp›­}ñᜬÿÞ6´â5y.z')=ÞØQÂÚu¼V·}­òÅ¢[ƒûÿ7ë Oe`Q³‡83}JéαL»ö È‹©ª¯Àªºçܼ ÃR­Ê® @êU(OŸe}$>5Q"ÅJš©’âÃkaˆ?#Òñ%­]b2+ŸÂr3#kUµrh%á™ÉãWÜ}÷Ýš9Ò¼ÖšÞÕù¡@ÞMXv–ƒc²ðH¾ $f'ÎËNê#NGneÿƒ™-@TÈçSÀMº(&*fÀÀçò¼ò‹K²¥` üû@ü×»Ÿ`€çè øs@²±Î·óGvOCÇD²5M‡³ÐÁXôò2“¢$ í¬Šé輺²ÜÓz"o„Õv 録©ðwcZÿKéEÀΆ ]˜T‘L9ŠØ[Àg™{9òX" ¨ê¤Pµhêú ÿ{h>¾ ?MïíQ@Cˆu¸øÂÃÃûöÿæðeB¢‘Öé16¹‹‰MÁ›Ú&4ŠI°HÜ'ÈHÈ"]µ'ǽ2С«ã1ÔóÉÖeh{!åÆa‰)O£ìÝ´Z«(פ³¥â8S˼ui_h/øå©K4»c.\3ã_zµyMr†[maÅÓÏÐÿ¬æ;Þƒ¼¡*È¿ÃK¯’|Ú°>I”¦ðvB¹?}<ªt}Dd‹óq/¿Šç¤ÄS$pOÁ;Ão|^»ñŠÇs²=Í'ÒY˜ù“´äÄ!8WEíA¨iHòS±’¶¢³AØïvOCÇ?®ßqRïK³=o¬Y/',"¢þŒuj¥õzª+Ë]¦'òvv?°) ­Î°>‘Odz3`u(scéœ,+Ó5¨Ð>gŒü/Ô'õ2ãlu¼&ßèwßýݪ¢wàåà«2ic*¥çl+â¾1[,oŸûUƶJ×k‰°«9÷€xÌ eúKFRôîðK™™¡m»çóB˜öj©Î Ýg2ó)³¬ PºvÊøŸ3´”c˜ys°®èKÛ)ío3ò‹ ·‹C]WòQ‡¼4[ÂEdyñ´‡?Lµr4»ñ–ölzJÒòÇ­oEÑÂjîyÝÑ€˜¸2r–m|­>8îùäñéÀÀ'ŽPgûÜÂi¯êöâGq?ÁØqåH/ˆI1į˜Ð쳦æ¶ŸF¬á­ä±±1p—ÁÛá™X&D¢¡ÙkgzGq.ö×ÚU"\?PÌŠ0dÊŽ‹t´¯¾¬“©j“g¬×£þr;4¿p2—óˆ|Ót»–gXMEÃA%ªýaÖ±åÕlÿÑið«˜EžK$F AI ÕÏK3æÓ°ÍºÕª¬ÿsf—RÅÔŽqÍÂ<×Â,»Îùï>¯ßðH¶X…ù!|®ùÜ›Éãw‹8,uþ¶¦9Âù¼RLï9?loêñ*cwã0ÑHvˆ¥öáLÇ*î±c¯:»eQMNsíMmÖgêÚð¤ w"3Õ‡ÑÛ çT'‡œñ0ÖÝ•EYþ¦ôljÿÃ[¤k˜Ë÷²Â®¸d4ÖxýNcÚ°8kÚrD$RÓŠµ,µZ‡¢ 2HœÜðLáAÙh[úV˨’âݰvV¬K¤æ0jÙ´Ï® ãi!Nzô83ùû¿Îb“^ ÄÚ-a NMŹØ+šƒQW:Š'2μ‰Hã;6yÚÊkA¥VÍ7®\›BÏbÉáÜ4ןå«¥iŒ›GÍŽ·Ç%¦ˆh¹—Hê')îuåV+†s°”¹ss¿äó1Í»IË´äñå>Õ iõmÉ᜷h%Ñ’b¶ãêodª[f,% ŽtX´ù>ž§fãå°‡ªf¯ýa]¶ñG`Ƈñë%¢%ëáxw/†‘ÞÃ~üW Ôšªÿ6n׫”óÆSO-ÁØý4=ß¾yšÎV€˜Pgàü-Ý®€·Ô=(s°eJ¤!!n?²™°º4Ï4ç]ŠÄÒ-0“ûr”YW ì¼ÎV—Kˆxbg²MÌJTŽ×~\ט+3½­›ðÌøj“§é÷Ã7Íêq¶àmXt¾›•’ôÅÇ%¥<Ëî–ô—Ç®ÂÆ‡'¦p8×ÃXŠ×™ –Rw9òX" ð/AERü[5§´²±îžeƒ и}븤ԋá«ò`X«&-ÊâÀT–o@åìi˜5.zÓ–hX`â“R/ÅÂd‘o œüþUQ&>k^‰¸3i)v^c§±njÔâ“l?V''-%ц·8câ,Q¾›žœð œøæ†, §¸²·¿ÛÉI7“‹JñÙc²ÁhÉ}0!¿˜½q}¥ƒ/ß`äû+騛÷`î8²¦ ÈTmy¨’!â¼Ú× È|i4°­¹ícî2ñ|NQ*Æu“]ñš>\ä \‹sÆñP´À¢rÎG¸Òɉ€D  (•Þ„ÃôKhÍGêO$Ø:ãüjE1gâÍÊ®™Í&ªFÜĉ0?k4Æ_¹¡¥nÈ„pÆ{é¥e¦Úä9©HP„8Z¡õ ëS¹âœödµI·>wTwTäq0! 0Ó )ÿ'œÓAPÁ“6Ÿt™šÚ’œÑé8-y,YyšÆ%MìKçYZÊmîÙ@Ï{:ºV]pOW“‡ŽEÜ^@C¶„å«i (±Ñ¯)úºÁr|Hñ¿"0+yÜœ‡!Ôu°L®£—°–1ïP!öí:|Dò3cnziê¤û_Þ½ÄÍŠñŒUJW†•ÒU#Ïïý†u õφt&ÀØy ¬7(ÿ sKy±š¢d´D@" H$§ 4ç ³¸×‡†thHÔ=ŽŽÖÊ“±Õ¥;™ÂyT]ºŠò*æ“牀D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$§5ÆÊ¿Á€Àä´ÐnE½F×ôst®·c: Á*¿´úïN¬>ú¿Ø¶?z (t ¤ñ‰É†…+ÿ}=!áP ËiŒ²­Ö¥æ,õ—‡µ(Ëœô±cscêKç«–êæ¬U.×9¿œ1½'׋åºîÀþ0ž«fΗm|¶­@Ü Aü¦<Òü­|,”‰³¾ÒBQ‹î` ?:ó¥„ùrÅoLpïÅï)ŸÑàþ“væ†V6u¶iˆ®k£ízé%L+Ó-),¡nì`,EÙù)éÊB•‰Ï=V¼×¸àå?¬pÚ §S6®svqðW;s‡Ü—o÷R¬Gɱ’òÝ(û9,õ~qM°"ëÌ¢B}+ÒH’R¨üÈU¡Ž,k+pI’” øÐiï©›©mTæªõq8m‰‡È•JÑseÇvæ+ë·p®¿Öæü¾ï,ÈUWB/FNŸZz8°¦k·àYêËt¾ž+l5‹4ä ‘¿iH!_‰b ’Ÿ”ò€f/žêàü<¥Øñ¯¼P)`IGZ§Ç”¨9[Í,äê¶1›VP#,~OùŒ6â1ÈTWJŸWÞ뜚Ηéš6äôˆBƒ;¼ÔQ²95My ­×V …›cPÖM W¾FCº"!æ¥L{ÆŸWjõ@¯’˜¸eÊüÈ«LA˜DÏ:,Áö Tí´W©×Ôÿ‚²MgzÀhY x†zi›•¹zÃß½^YwAm髺^|(w–Ư3η)Ì” ¢rHר­íŠ[—T•Þ“8žû@ž^šmK|,=%1Þ“<õ‘¦9™rf«Qzf}”çkq‰¶|Í+óI‚ ±¤LL3_²ñ Þì*-×^@ÈˤÔÙÊ?¦ÎépŸ/C@&SÓOfXŸ gÖ㉓΂gWÿçiµ•ïëõ™¶1d¡­±‡!&®/oì•8•ô'ÂÞë•õS5‡ãß>ÕK×ÏÖ_Ùë• my®Ï§žÊ Ó~¦ý×ûð¦3r–-avY¾/=Í_U:«UW2UÛU á ²`µr²õ¾dj•SgDâ¤3UVz~¹Hy"hÄÔ;I™4Ûr‰ª©‹€YDpÓÙmÅÙ_¦¥]pk|üj»¯²Èl ëÀäïA2âS‡hL¿ &æ™®½‚·Ò -,ä|J÷¸5å\Õ®¿‚dçÃo¦ƒF‹"͑Ͻf}æ†s(\ÿj–-I4ÖŒñ õ—ÿ™˜É r¢3mXº-ñF*‡ÂðR®×4m{àúVEQÆÁ²T“¹qþeªÚk8<}ùÌHKÿš£–h{ãöóàop&†ËFâM6'Ý–T©‘r¦cßsM¿ ÒÈÓ8Óç+fóôYÖq{pn„ø$Ûp¼ “œNˆXϹéY”÷™º‹Õœ%è«1þ!¬)EŠÂ>ÑtÖ8ìœmK z°9s昖ü½íäÿzŒ2„âðù‰seFšmü†,{î$ÆÙ\gMQÝ5Š™N³&®¢ôÏLž¬p-6_¤ç«o”ÆðÜ\z®‹@s|bÊ,œ›õë7tèPÂ;íÂY¯®÷Ïu©8îsÜŸì㞯®×¶=Û÷sÏd]…[á—l‡¢¨)}m÷°ÈŸ`¡Úl°vFhLû÷X‰ba÷‰ûB¤£ýãÖÉmTÕnÅ=0§±œcˆIáo¦¿œðó:žU/ŸÝ÷{¿º¡Ÿ+®†²,àþþœ;ôÔAV•Ô³{Ø™3¤uÌïÜN¤òOºÇ®9»'½@T v{é‹þ5Y,!WòÞ3e¶YQ K¥geV~nj{†ËîËîf®…BÔ&8tm;2uC×σâ‘öL‡“ÕrÝóª–(}>‹à½u¦LÁ}þÒ’õÛ)kzFCZDÿ‚tÏ@‡Â޶vÊ?VàgÍq¨ìz!Ïô½øÝÂ5M7ÈÅk¸Ž6*‡Jmm¥¯®¤k"ÈgT !÷uA ^-)öâBzãkS…+çÕŸI}ÇüÅøGÕß*_«:FgÙ}†[mšCcKTû“x`w†š›|,RãáêÆMÊÍxË0ÞÊ(–€g¡û~X ž,K·ùÉÄä­%:Û…·©A-Þ× YòkÊ93­ ëŒ4ýA<Ο¥[ã ‡'¥”esî4»NÖŠ-n–—/†%MØËtõä1Á“q˜ —·•ež—˜2ŽÆÃqî"Ó3´U“6o<õTIYºjvü—ôäÄqââ°¤”·Ð+¥‰sXPÑúL#kÅ+Ê‹/VÕ‚°¥äÞÐtwä“õˆÒ€l-B7ùëÔfÓ¬£ŽkL¹‹qí}ÔáƒÚÊ+‘äG»Â¯…¡|Ë[)‰{oqŽf‹ÒÏ…cOKL¾¬Ô®‘n®Ç»g''>‹½ž›2%’LªIKJ}(‚7ˆVgŠÓë¿©[Ûk%Óü[k= NµïÁDq¡•Ã5Šokî06S=7ý<#{àwõF[SÏw­Ö¡¥”Ñã{iéþ±ud°_7éyt‘ ±šp.^ ~ža}>«ìºk˜Éã2+<7q))5>Ãâþú8TmHz¬£‰8ìwÓ±Ç:¸e¤C°ó)i¶¤÷Ê¢¿•w¬—ä³÷6ÅÕôŒÎ~ê© °pÀGHÏ'å±YBB$rRÙpÚÆ•$®k`ùŒw|}žñï)½fwÔØÖ!ÉJWU; ŒBéš|F ü@½‘” ³-hš:ÔJ»ËÀ̹Ã1 qÜãk:VUö ÞÞ‹`8€ì‹Y´iÆcŸÊuåáì8Þ@¹Î½É²à÷¦-i7:í ç¤'$.@#½@5ˆ {vÄ„ ±Ž|Ç­xêZ/®Ÿ‰·¿eîò¢bC7äCóv2ôÅK,ª¯E†Nл«8wîù¼Ú ½ý° îùÐÀî~1G$#ß^ÐÖ¡[Pž¡37ʨX7÷|îdz¬ PïƒùZá@4€_eÚS†˜Ó=]o†¶’ÞŠDk6RŒС;ú@æá/Ž_’â…Æy1Êq½íÑ“¢|âJàvðÿí] œŹïêîÙ–SEc4 1‡ø $j$&b4b¢&Œ» 䬲Œî΂Šâ‹» æ0ñ£ÆóžyðP#$>_Eì‚c—î®÷ÿz·‡žÙ9wf–¾úýfº»Ž¯ªÿ]_ÕW_}UeÊ ŠÓT6´ú$^Š|ÄôÏ¶àæ‚'õ[©|åÉ7ßBAÁ=ѶUq³Qß+ÌFëVH+¥;uSP.!M Ò%X‡#çàõ®Î½¹ÁP!_ÇtÊ]Õ~nZæjÔ½kг/ïçùKXM0ÏP¾©--mHЇy>ø'¤M–³ƒï '¦xB܇`À3ɧ£< ãè— ÐÑJ¬Ý{Œ‚¦Eb éWk6mó‘æ¿^olÏk÷¶å³­C[HÁX*R;i“`mC’/)#ÐiBŠ%Í;R.mh(¾y­ç» öœk”hAoÞgd›álÐ/ìf·×{yˆ´€Ô€ÖÁ†ÄÒéG¡)JS©Áhðõ¤JÝ^í-ƒf$’ŒYšÜ!—œqFËš}Û`Óæ+•`I§Jv¶CcErq9U(;]±nCòsGô+†­Â†Šù9Øš¼ë [â‘Z½ë¹ý­PþÅÿ»ä‚]FÄéZñú«*£·_£³šCHÅý„B1Pø|Î[Úô/”R2¤>jRÛa†ÿ å\xýSUájO„G9Ñ"CÃÓÜIDATŸ/\üaï&óðO!fèUÅLŽ+¤8™·íqòàT￵þ:|ÿ˶YBuØ¡~ÝÛw&ü”Ž?¦Em¡zyEñ[¨[çì´|ãK~¨e>4‘ãVToL4ÏH|“ËlÜNÙB®|oÍÔCèa *È)åÑï|y؆ÿÞ´Íïÿסo«ëІmg«^[q™cܿޛî{R›cÊås¼g¿š{ËB TÝy!³dJH½Q… uò=0vÄŠ)õ9ACP[Wo®ÏÝðaD.z ô z•èHáyúä3 ¼UçÖzKÞshèø;è\‚ÏŽÄ«®Îèæ‘û±ŸÌÛÐdÑÞ3‹"Æ;=ýVãµPlÕz&^»‹Î[ôî­s¾ôq2ô—ÏŸ³§ Ü·etkS­ÃmÂi‘ÊЦ©yaÏ@ò(úÖ9¸Ÿ”JžÉð0”Œu¨Ñc#•-•2D¢G~‰ð¨TL¡üI ðY ãúËP®kÁÃSìRP¾×`»¦­“PrµÜ5²‘FÝשŠm/ccýþµP9$“—Íò®qö¬þY,ï/(_Xš­uûØoú:µQ˜“è»Ð޼ùåU7 Ëz v.2G¢e9ñ€+ÔóvÂ"žz,Z.dPÝb˜£¤žõj­wο`£õLÞ‚z4Pèê_(]¦ê0­ŒËíûêâÙ³­ÈÍýÐ9ÛS¶©ä™ kB[jXæØm•gwÕZKnÀ¯4ž»Ü[ö·TÊ oòË£yž Ê!#wjYÅe°w{9HKµE@º}{äð¿’jvzÎÚz³®?ˆNÔ„ztBm]nŒ›“Gc@ÃAI í{æiZçe:túÍ£mÔ-F#×7ŸbÒb3:ôþXýó½Ú€ó~YºöKL[\…ç?.-)Ùëø‡_k|e¤Y )°…0<Ü eçÐÐç¤iÙ3–^¿ëüR|àß}ðV­…½Ç¨pzéxÆ2Þ  Y Ï‱o¯ Ù! ÚTµ¾}”‡´,c1…³£3Ò+d0‹Ë[H× à+ä×êÔg!œb•P«= ùÑh-ÛÓó:4Œ¼ózàà.hQfcEÑ¢Oµ‘ »¬¨(yêð{LËZ5Óç;=,øÄ~Ìp}'ððíbòl»ºC{¶ˆê êB æBÿ†nôl¬2Wã-±§ 3U‡ñÍojÜ×øOð;~îß­F#V¶ÝEåN5ÏDy˜¦œ°1äT¡¿ÑÚÙhÞmöFz)—Þ#’‹Ç£´Ë¯T•û,)žÅ7ÙQ0Ïw5ÑÑ-ù¾è0Òt:2nÆz.ÚV`¸•£ ¼Õ%ÚÖ9ñc]Oj ‡%Œú¿Ì;_Šöþ"Íìœ>ô‰Ÿ±×ÈD†ÿî]Ü[ô”~Å¥Jjuqû=œ¾t~ÑÏí©,\Ø+«Yt?UõY¸½LûØ©ûÐT–Ì2ýÑ- Oµ,55¥áP/…§^â“‹Âð6Óβçdò­QWn›3"?^´ï8ØkieéŽÎ®Ã°éGSLÑòí,¾™QUuš’—w0’{¦Ê‹GÉà¸Þ³ý2Ž÷ýb…§³­‹•‡1Ñè!¥ªVƒÎ£ô̺ÎR2ûLˆÀ°E›·c?4~ÌŽÇÀ>6n»sÄm§À)F€H N™îÁ:Ö¨Ó©ÿhjH[:C‹r4G¾cŽ%2ã<!(óyK9oF€èòtŽ¢ŠÄVm¤Wgä‘Z 95#>2_ße‚«­Ò÷NL‰`:EHÚcø[ȵ)$çt?`[út“dzŒ@WE«<2^ßuMËx]_.#Àt :EH™8ñXß¿ÑW–š½•{Fó`âŒ@A@Ьç±ü=¸ª,íÅâïÌ>ÿý´Óe‚Œ#À$@§)vy4õ‘$Ê•TT4ÖK ’JÄ‘ãº9ñ"J<•¹WK2G›)3Œ#&¤”Üf¬‡0ñ§ÄŠ•\,©jÅÉ¥àØŒÀñ@–&½0J÷§ÿMÄÖ=ÎÿMúé2EF€`’C Ó„*V¶ž3«pö'WÄx±ÅÊÒÛŒµñb¥#œöHx4hß ìèš–oCt ¼5Ý¢åIû D Ãé¨Zø©¯‘â‘_4Ñâ'ãOe ²ÄKCß&Ö{ºÓÇÄâÒal‰äë¦ÛÙ÷›gü'6Å+Ig¾àODÞúV¡ÈÜTRš œN>‰W¤Y=”)N"u)RºH~t€`xCuêb¤øõK”_y·ãO:Чë Mê\· V‹£Ä_Äæï)wøÐ̬à2vÊ”šã½Îx™‡O{×V–ÏvÇÅŽ•¯©ª¸OµŒç±8Ÿ)¸q]:ùÄÉ+oMW9õvø€lÖõ¬It†N<þrè:×HuœÂHh0}ó¥¥|çaìß½Š6¨ŠÂж,ÃnÌ#aÏg‚èž>7E:5NDÃíâñë‰Ì'nøþøB -£õd^¹¸ÀXƒÎw<Ò¤´Ú ÆËyÝúŒKD@¡òé³ À Ô¨;女GpÞuXpKh',ü*EËdø­i ÚwB68ÔRž&Ò3Λ¹Ô0ŒgQˆ×Âñµö©©¼k;=Wß[úò:¿UÝ<¥Ù>îžà°Ôµ‡%öj}:ú‹ÆÑX­wE¢¦¢ìz<‰†-õ–ÔU{‹>sÅo†ÀÔ®„Ê÷¤£aQ¥ò„»œtôípÿC¡å¬Ž…‰?¿Üw+ê×ÛøŸ8~]ýúòå¨+q ^tø£1ÙòB¨ø»ËŽ­›sÁ²¤ÒЬU¨÷Ü‚?ødÊ A¯ª)s@s¹}BÄñÞqPž|Õ“½2¾­JX¯.Bèñ¨§…QàÑõ¨è]ƒUuzëÿ[RYþ!ʺ+¿|M¡œç¥Î2-³ºÙƒaá7ÔñÒ†48ÔÐã/L#Ä'h™Ós´tÁ¸ñnÊ÷ÅÿP4KX!~„vÂBîhVŽœAá¶}GùÂPÅBÝ|'0jw¤A<DÇqK+æ¾C#~4È!ïLá±Â ¼‹N-,¯º'ëÞŒK-ûY‡æÑ«(¥|]Q>FymÌ(¾ÝåH»%âù@.L(.iÖPÊ)ÊàS«`׆Òt%÷^ÑÈ%ÙY¹çBèX ›â• ߤAUDI^ÏSÎÛvçëâÅ·…\!?›>o­))ð.<Ÿyè}Ì'nVŽú7|õ(Ÿì™åø%z­­˜»9ÛƒÝ \.|·tU'X—HêÙ ¼?ÔU¤ÖÛ°ºäVÇŒ†à`Ô½VÓ%81ü—ÄoA;°ÁýÖ¡Í ä—U>iŸùÈ÷B%Oû¥›®s ''̹Æãד‰OLøz| pÌ„‚‡¶±/)0W”ÈáªÐG©B” á¤QØ«h™ÞÀù%tË Èü4[ÉádRém-íæ®…Z×ÕUè„& 1Õ»à,¤ûÂ@}ŒÝqÇ¢ÛÉBÑlÛ”ñI!­[ÜñQV}EeÉÑ@ïÚiú¦ºÃ½¿ýžeÝчûÝñ1bl„>¢a«;^¼ûi^ßHк^ôÐÿƒâª–ì‰Æ1dÄ ÛFè¨lMÅ.½î4EAy|èà?ÄÎÖ¸¸ó‰G÷Ã÷¦ÿJ˲Æè|& ¨Ý{ª!e¶éJ¥§ªÊ À“Óˆ=ˆ™´äͨ?‡—! o6Zj¤Ðf»íYÂÓuõçMwœ³c[шéÙJ¿~ªªN€ÉbðЋÀïuü^ƒó Nî½W¨ê·GžyÁà÷ŠF,x«p`\&Ú{ þ8•Ûžò‘c"ê̳n­€Ù¢kÝrŨ3o/­N¢þ™ä“ð26ï# $,Á”Õ k6m{ <®Ià áÓHu)œV¤g¡ø¿+–u)êìÔÁ4¿Hqí:(ÅïжŒÁ@áßñ ×ÚÚÕH„ð‹Ç¯'Ÿ$Gé"`àÙ5\qA`#JB¿06¶Ý¡0ÿä—{‹ÿ‰ÑÊŽ]Få·,E½Ýõ3î5E{Î7pp<ŠQ0¬‚-G6ÜÞÐ6ÌŸ&òHqG‹"_Ÿî½ÿYÌ]Cc›¸#ƒ8Û†ªx¥è!…•Òj(”¥Àð?¥éêÄjšB"§Š}èø{„–Nö@ókçU]V¶a—Q8l>nlQÄŸqjP‡¥MÕÕV”>Oa ì~Ý´¿é<ÏwÓÅ(}TCÁwÑ”@O³m™×û«œÆŽïöê™=Ã&&å¾Û éy¯öÞ¹ÇOL-ó ‰Ô¹ãâN<Üo*êO‚Û3m¿ˆEn7ß1VlOÁ_Æüï¨7*'*ªzwx ÒæÃ.) •G` ~s*:S|^fçy gØèæöë!Ü«)ž{M¥el÷¾ÝëðHuÉ ‹wE=Õ¡-ùv'sÛânDûTgÛ¡Q–bý¼›Þ}¤™uÄ;œG!,åÔV–úâу_Of>‰ˆ{v)Ž©&åØ ¡®‚ô0Ò8ô@Á¹óhe &A£³ö(WbàÕYBŽÅèjãN«îšð4K}e«Šú‹€ÑòúµÑVxÜHÏHSO«kœ0Lõ|¨á ïJK @Ë š¢Ý²zBêÁnàl'=i–0b"²UNBÜO)¢Æ8$ !é:ð@F˜ÐdýóEßOŽ9²1½óEÇFÉg£¼6f FÃ÷1­µnQQQPÓ š&) 0µt~ ‘/¥Ý1,shóµ=ÕÞâàûq½UuÕ“jÉ+íc!@+­F‡ÜÿaãêHáÉø¥›ObåM‘¥OA8¨ xT·Ïž}„î£Ö% LÀéJÖ§°ùëu PUŒžP&@4^ö°wÖçd£zD æ Gc&yƒ_™O’Ä’£w*'¢K¹ :/e:à×â¡%“1]PCö(Îêò•°B™)­|êCh\¾ŒÎ1®mxz´V¿‡áÍ­äOö 3NÕ³_ç~&ƒÄ²Šá{„оMFãó ¹Èm@KiÛ–÷.€Ý =ï²|?ÀtÏ–åÅÅûIq96 Èp·¹ŸRô-ý̟绊ÒÄ¢áŽGq;âÜš׻N—Šy#„{ªÏM_Up‚”7‘Mk>ÀOUþ@÷Hs3„FÒÆØ.&XU1«ˆz8?¨×k±zåV¬¸˜æ¤çk4 M1e:Üß{½˜ ŒàÈ_ú4|«…¨×`ÁŽ»Žð å–_Vu-MÍt$g²Á˜:SQúXu‰Âiz(ÚÞ*NŽŒùéê¬~ºÝ[5 醴n ߀Æöb '' ëb„½J÷îúOÏÑœ;^,~e>‰† ûwºÌtOg±ÌWö)¦mö¡¡ÜØ®A…ÑÔ­ NY¤¥þÌLOsïAaYýò^ÀôÃÒ™>ßéGì1•“¢u.S$Ó,)_&)—¥÷Zè7®Bùþ®šû`ZÆK{2Ä""vìÿz…»jç,©õ£6í?ò4mßÀtÈ0¼ÓÃÁ]½ºÖ[ú¿ŠÐnQ,c5Âa +GèÚDаZF+ŸîyeØâç¡û›{'¬¦–V^‰Ql¢ ±éE£Ñh…Ƴ#'÷Ê|L}ù5ÔÖ÷@ç°AÏ£¥Ò(£‹~uEñÛh䟩7¶m»Ð*­w³Oëù(rÃ_¨_rƒ“s\Lœˆ|M 5G®6(Bòˆ)ÐÑžAøV/ N¦¤MéŸØ/$­,SœÌËÑ^%¬†ÁV¤/¦²–‘V…ÒǪKÝ{æn=¼·éÉÆýÍ£uC¬ü4˜bäãÀ¥9° M-¢øºÞ÷qÃØ·uúuàjà·Z\{ wýE»]¼üÊ| AcˆÜuß}=h &b`˜'´(?ƒ@±0Ì;áGÚ”*<2åM£0ZÍà#ÿi ôqûÑ}8hñÂÓÅ{&[”Û«ªÈ%ÄE¢OÚŸpmRH"~8áH†O¨3F‡_G6FÉAu0RG#R½•fºwi^¤pªëd3â‹TÿÝáÎ}´xáüêÄç+#À0iG p^åhégÚ 3AFàB€VóM-«¢i^vŒ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#ÀœŒü?éÖ·Š:“IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-dvr-overview.svg0000664000175000017500000015200213553660046025766 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-04 15:57:53 +0000Canvas 1Layer 1 Network Nodes Compute NodesOpen vSwitch - High-availability with DVROverviewInternetProvider network Controller NodeSQLDatabaseMessageBusNetworkingManagementML2 Plug-inAPIManagement network10.0.0.0/24Interface 1Open vSwitch AgentInterface 1Provider networkAggregateInstanceInterface 2FirewallOpen vSwitch AgentOverlay network10.0.1.0/24Self-service networkInterface 1Interface 2IntegrationBridgeProviderBridgeInterface 3TunnelBridgeTunnelBridgeInterface 3 Physical Network InfrastructureDHCP AgentMetadata AgentMetadataProcessDHCP NamespaceIntegrationBridgeLayer-3 AgentRouterNamespaceSNATNamespaceProviderBridgeLayer-3 AgentDist RouterNamespaceFloating IPNamespace neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-flowns2.svg0000664000175000017500000007774413553660046026454 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 18:00:08 +0000Canvas 1Layer 1Linux Bridge - Self-service NetworksNetwork Traffic Flow - North/South Scenario 2Compute NodeInstanceLinux Bridgebrq(15)(13)(14)(12)Network NodeLinux BridgebrqLinux BridgebrqRouter Namespaceqrouter(11)(7)(8)(4)(3)(6)(5)(9)(10)(2)(1)VLAN 101VNI 101VNI 101Self-service networkVNI 101, 192.168.1.0/24Overlay network10.0.1.0/24Provider networkVLAN 101, 203.0.113.0/24Provider networkAggregate neutron-12.1.1/doc/source/admin/figures/deploy-lb-ha-vrrp-compconn1.svg0000664000175000017500000015562713553660046026021 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-09-20 23:43:13 +0000Canvas 1Layer 1Network NodeCompute NodeLinux Bridge - High-availability with VRRPComponents and ConnectivitySelf-service networkVNI 101InstanceLinux BridgebrqDHCP NamespaceqdhcpMetadataProcessvethvethtapeth0iptablesPorttaptapPorttapPortVXLAN 101Interface 3Overlay network10.0.1.0/24Linux BridgebrqLinux BridgebrqMaster Router NamespaceqrouterPorttapPortVXLAN 101PorttapPortInterface 2PorttapPorttapInterface 3Interface 2VLAN 1InternetProvider networkVLAN 1 (untagged)vethvethVNI 101VNI 101Provider networkAggregateNetwork NodeLinux BridgebrqLinux BridgebrqBackup Router NamespaceqrouterPorttapPortVXLAN 101PorttapPortInterface 2PorttapPorttapInterface 3Interface 2VLAN 1vethvethVNI 101Physical Network Infrastructure neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-flowns1.svg0000664000175000017500000007774713553660046026456 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 17:59:10 +0000Canvas 1Layer 1Linux Bridge - Self-service NetworksNetwork Traffic Flow - North/South Scenario 1Compute NodeInstanceLinux Bridgebrq(1)(3)(2)(4)Network NodeLinux BridgebrqLinux BridgebrqRouter Namespaceqrouter(5)(9)(8)(12)(13)(10)(11)(7)(6)(14)(15)VLAN 101VNI 101VNI 101Self-service networkVNI 101, 192.168.1.0/24Overlay network10.0.1.0/24Provider networkVLAN 101, 203.0.113.0/24Provider networkAggregate neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-compconn2.png0000664000175000017500000035363413553660046026260 0ustar zuulzuul00000000000000‰PNG  IHDRõ›u¼u§sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|eþÆß™ÝMBBï]lØÅ.vûgÁ~ê) ÅÞ(It%PñoWH8ëyÞ‰§ç »"*¨`,HS¥¤ïì¼ÿç7É„Íf›d»Ùçý|63ûÎ[¿ï;Ùç}ç÷¾£ ì ÆÊ—Ù’ @\\ç¿»si°lˆÏg®}È?qI\e";œÀá~VP·ííë³À↓b‡ˆˆ +ýwô® wö*µüá¼qI”‰ 8p_Ñ‘ D#pÕäÉݬ*£]V–÷—»nº©4Z˜p¿«üwô­´ªF*ý‰¢ÀÍKï%ó¹ßÿŽ÷5·x´Rv†ÊØp¯ÿº áþÍ9ÏÉ/¼Hk»]q ZcÒ)·*×J¿XeÙÿ@¼‹7QÃFcîQfÅQCþ2bĈP¢–;²\͹'‚–ý7¤7t­úy—E¦½=¿‡·G¨WçÅ99Á­åïþïè¡z®æ€¤.©JË:G)}¯e˜y¸RX÷*¿‘ 4‡E}sè1n«&PQfߣ´¾pã&û|Tô_ÛªlU(8Rk•«Œ`W„µ­ðÉr}­š;À Zõ)¥ªDÌ+¨RÊXeê9Sûþ6-0îÛ¦ÔëªûïO¯üeÓ£,ø² _(ÎÍ]Ó”tZKœhÌ-TîEKB`¾ÆPÆ;¦×¸sº?÷‹D®sk¹'ÂÛÃX±V„¨Ò]E©}'„륫ժ#hNƒyH€âHÀŒcZLŠR›€aÌV†±·Z%Cm0 sš| ä§£®ÿÄ‚E÷²m}£¥ƒ sü›R÷®¾ºRúUe¨z'þÒ”4Zeœ0æ†aüö’™Zë‹CA½8;¯ '¡ëÝ:ï‰qb´=¹ËÌÿÈÜÀ¢±¹¶g¾Ì‹H ¹p¦>¹Ú‹¥M`E“rEÌHà"6«h”¿rÇD&’}ûíôfë1ˆû?ÛAã\ß72L,ßavó§êpù±O‰0ј_w÷ÝmJ+¿¦J“ðdãþìüÛ?,.ÿy"im÷„a¨_ñ4®«eÙÂ$ç`¿ÿXy€Ò⮲Ô>÷×>ÊçIoñ̘ @ÒàL}Ò6 žìüþÇ2’½Rþâñã7¾Œìšºì™í/ÊÜV½fΜé“›m…‹v½©ñ0»mÄR¶È<ÅžZ>‘þ;êû=×__ŽÁUæ J«4e[§n«,±ð®Âh[iµÄuÉ×֔´›Ó>îÚx Ü?…°ß•õáMM)odœ˜Êoè?FÆÛžß›Ó7bé{­ËÍwÜÑnGõÕÆ–•áI`{Ø!ÿÀ·g™ 4•ÀȼÀSbS¯Lóü¹Û´©ÇBÏ¿`öt"nª‡Šù¸ùÖú¾s3=i+°SË\s“Ý¡eÊþkÓ£&OŸ”÷7ޝõßÓ±Ì*™‹ÓŸ1‹}ŒøEºQù“°uè¤1 y^+×GO™Ò)Tj½ŠYÜö¯9"šÝuN^Ád\?Siãñâ¼Û#Ó ÿ>Æ?ep6õ3ߢ»‡_ ?‡)H)ÄNff¦Ùó¾ÜÜZø¬”ù „è¨ÑùS†Z:t¡Õ)XÜÚ ¶á—"ÿ'$êpª}QAÞ05Añ¶¸«ü÷·¯´6߉òcá+ 'ׂß|ؕߦ-Ýù¾†ò=ò]¸%æ6EÈç^¿ë‘ä®R>ä¹q g@¬æ\?¿_›«ƒ…7"Ü™(ˆûäáKp~ y<ä†k©c,ÌцWÙZÝ/fP3y¸e‰•·„ퟲsȲÄF|(>ƒÐ6kC/6 󩢂Üb #nä-…'¶}p~4£0ï²jßúqÏJ¥•çæ‰ÿ«íû÷„3ç–Àé¶¶H†Ÿ ?צz¥çˆÛV[sf£½3ô]#ž7¶}Ür˜^óìã†ìòõ›‹–ˆ ? m;&_?‚ßÎn™¢Ýö0•ñ°azþfkëc° )Ÿ¹_±â7‘q`*ózߥèkG¢ÃD­®‹µü£ò Ž )5 ƒ·#$¤÷=ú¤³HWc-‹Ï›v¡eU¾‹+ë‹yN˜ðœÐ†ãûýRÖŽí}‡Þ9nÜæðë£ý…û„,{&8|>#¬[\¬}cK ÷Þú½>2¯ð¥í{aÆ—‡ÿ uÊŽÌŸrÒÖc(o©áóžSìÿ“¤/¼V…&_Šxcpî!÷1`T!ÜOø>K™ÞâD}Z·ç$ÐÒ8SßÒ„™~Ê€Àê„›Ý ²d¡l­sýµžSj•~(B—ãGù1Ñ…·†Búyw¶Û‰L+÷HzøÁT›XÄ }–F½ÝKÓ&Lø]&~ÈÕ¶e?™]Täs¯ÉQ„‚­ñ85½¾N†_kêùXÿdG0ã‡ve¸ —ôPþ] ®‡ŽÉ»czg@8b§1¦ZA$á~…åŸ2 "¸i>L9H?âüø¼ƒóÝ of#ø Qj¿ŽÊ܃<‰°ýPÝ¿C”Ý€4¾†8•òî†2,׆ñ¬išï¹‘dpU°ð´Ëh£îRÏ"Ü ü{#΃JÿI„YBæ“2£|›Ü²Ë1VÞ£ò G@Ð/‹s!Ž6#ÞSøÌE=÷²m»Âôw&Û0»¾‡p½Áý¢k {„ççž<¹ Äù¥øÞ¯S;Ï;âïöýÈ{B®a°•k‡Ôç<ø^á¸Á°Õ «‚¼"a¢¹¦´[ vÂbãѶS®î¿·¬¶í£åîgcà1½`§¨ÏH#]Yö#"8ÃÃlë¼1å· cÚc5Úøw']C-A[-–©Õ7ûoþç>ôÇÃGçÝþ\סÍÎwþ?(=dS‰urÝ«JaËÐárƒèïï5¦o„Ç‹µï…ÇqÏGù ÷‚ Ÿ…{u ×4ot½\Çû>eÛbÚ‡ÿ…þ‡€ þ‡¨h‡+•ªó?WâБ@*p~R±â¬3 loøñÌuÄa;ïeb²âæŸ_x…¶í¶¶§àQ<} ÿŠœvøÇÿä!Ú MÂýPb´dz ÿ]÷ÚØüÛ÷¬Ò8«®_ø±YícÛ(_Ócž4}RîkáéÆtnhÇ´,½[ûIØ­éLÓ«C…W"îý1ÅG Æ”¿¸ ïADyýu>Ž*íê¢ÂºÛ墿Žk£l#x,޵»O‰YÏêৃñh˽ðÿåO¸>ŸZ‡{öxpFW7$ Ç5¶o¸ñÜã¶úž.ü(OB,Ëz~mñ9}ZÁÄ÷ÝëÒ«JCW¢ÝÖgy3w½Çýoîµ§NÍÚ¼¡r(ÖnÈàžŽRž@£fRž@³È#åŒìpA/ɵoŸöOÖ Ìv–­óš•EXäto‡±ø!üÂmühàP¹„ïûð#Þ³{·Î(˜ B!v§ØNqAØç ˆ_±Ÿø<$2Çôf * L|>j‚¨üIÇùô5³#dÀáíp¹+°%¿ß°{yó0X1–EËS[ö!™Ð+úÅ0AïM34fß'æ µN 4&†öz=yáùÉžüÚð\/Æ$ÕFÜŽ'b£œ¸íx&Êù‹×›þßzÙoƒ·eýˆßÖ0Õ}á‚^ÒyhâÄõéYæùè;åøz“¼,ÈI_«G£²/®>Füµ•ão*].âzØWú\äo¢mî ôä¡‚ñ_ð­aÁkO›Û>Ès˜]Ú$AR`9C #ý}àrôKhe5Yž&‰ÿ¶\sË-}Çõ°LQÇ…_ÿÙšsÄ|”ùükÜ §†?µ“sð8u(KëÚ®ÖD¨I}#<ãmô½ð r.ï2À¤À›8íŠIsaÞV;ÀëºJ÷“#ÜÜpA/ò¿fsôƒŽ@€¢žÝ€¶˜Œûo¬cË-Y;"WïÉyȶÊ1îÿÕ› <.”´B–~O.ÀOä¥P¹³{ûr·jG5C{à/¦ÕmtGú!ä‰ß¡!«ê|œQ㊧iÞ& »ÁëQ.تj˜ˆ?È©ç¤>‘ADØ#Ég"ýå;LŒ:Uû›K"¯Ý;ˆIC9„N‡pó +<ù‰½î;Óüê˜$H}<ãâZNÉ.4‘éÆû;da¦rLan„Ÿ"¶Úo,þöGmëûPþJ€á˜`DËx«¼õ N·(ZÔ&N\‡zþ[XƒÁ£% lé?D;ÀüC9¦Táñd¦}ëPLùþ0­ ÏéËá×#ÏÑ Ž?Óë:ÈèÑ~¦æÈxÍm0[0=0ñ¥Èt›òÝᡠ̤ë,Û Íˆ%æ–?Zv¦÷m´KãÐcÃÍÂlebЇû@©·aªò~cÕ¯ÇÔ¦ñÓ:ôf¡-Þ ¼Â¯Ñ}£6M÷d«}¯:òµåmÐxaß›X‡ÑÏÔX_öÄÈMª»:ü ´ÛFÔïDÌnõŒ‰GHQòcLG$°=hceCÙ@˜®‡€’à´†Â4Å_„ÌpHûVص?¡¶Ay½‹nBzßa1]…²òˆÿõÁ–¥señÛ‹—œÝ¢½õ4ÍôÖ3ÙV Ø Üf,m(, +!fê]6|êíê%…ÎËÃn üu“,Ôm ë°Ð5ë°m!¸ý3<Žœ¯¶ÐFqÓÊ7•þW}Æý.&&°MßÃýîÓ2Ìwe6Üý¾õ£î†ºÝ%a0ëº xÙ×}F†š:=/o•ëylˆ·c–aÍÙI'E·Ž[ÛåȨ5ß…¹Q¸‹¸Ç@zJHUɬüDצ9çZ=¾­›3[¼bmo´ipÚ­ã—O÷‹µS]'"˜uÈ_[\3Û§ÆvKzÍò°?÷³º¡š÷MÄ=f¶eÇlI§‡áø·Èo½Ui¿?Òw[ß«·žÄ ]ƒ?ø0³i#óŽØ©ÆRnŠ2í=‡a{OÙ­&J4[•Ù*$ µ¢‹±ÀîE7¼ãm¨uœâü؊Й!w¾5áž…¼†2Õ.ˆv“0Û¨ÜómѨ¥4oo+\´ë ñî¢n^½Ú”€X—hñ\¿2»ŠT ÂúÇ­Æéu0:yt~àh„}WÖk`Aò ô¯·*Ì[îÆoèØ>˳~ㆠÚK5ئ#Ð6Ò¯Œ!ª„h³Ú§%îAô¡7ÀäQô±¿Vƒ£¤g Ü•n™ÃÍ-xZáçÈ[fßý°X’Ùw¬› ýY®{ ó99:O>ò fúÙcn¹ý˜Ž}Uò[©˜K-ß’³9}Còq]C}Ͻ.GüËꃾsTÈ8 ÿ3.- –> oÇd(<œ{^31»ÛP¼u¹õ¾D•©S°ûÖ°‡ü—¸ay$T%À™úTmyÖ;á øªÚ8úuv܈V`̽÷æïú¼%p~0eAß+²2 Y¹¨¢^vt©°‚Ç;^°£opßÈÙÛ³ë­øñ\Ñ:jdÞä?F¦Ù”ïò[,¼"ìh¤½ oí|¶)éD‹ãì0‚Yv¤Ýÿ­EK&D†•7ù4ÔñäH÷;„N?LàûJCe'ɬ´¼LI>á;Þ¸aåXäŸøLf8êXUz\ê~]Îe¡§¼l+Ò?™¾cW·vÍ7Fe±$Þ+, 7Ï:ŠP„éÄÓh“®k‚K°½³˜õ™{®¿¾¾È½.³[xsãEvP/€Dx‹Ìö)ÎÍ…ÙÀ¶êÚ{Ô×Ú #m/„]ïPIhˆ„6ø^ŽYúË¢m¹íÔáõ7cKÎ×l­ °#ÊQ°‹~f ²}졂 u¢3a’#o…=o£ügÐ3çµNF«Ua/SZ‰eÆÏyÛ˜·‡ïF“–iN–…d¦ø4«$ô5ÂÉ–{Ë0»ÙõÜIý´îØ*CË ç¹NbIøGy9ù“OÇKÏÞÂ~þ o=‹ú.ư;ÞæúôýD(y½iyѪgšžGC¶u%fó'@üËŠãG£…kÈOÖ  /Í„øךÎǶÏÂÈz™˜ülØ”—$• ñÞGÚGF¦‘Èíãñf\e+‡8mð)O“ÊoÏàÿÀÅ ^ n;¡/¾ èÝ‹¹Î‚oaTcW/g{±]ß«‘ìpí0=þØŸ^Tý4°n¨æöº©ÅþmFAî£è‡Ç —X¥OàøGYaWÙ»(+ôÖ-¼“…¸ï@ª½0«mk™H(òï{cω!I õàL}ëm[Ö,Nð=‚gÿhüæì§l¢&3cRi^ˆ.Ù¶ñ\[éÿƒÙÃ-<kœÝÇ×ç:\ÃïÜ—ãˆo“ i×aÛ·Ë·\©>›îÏÅ&UêÓU—iùñ„FˆÅé,,j«þ ÞI¨Oˆ±Å÷ªL£á—OÅ’|adẅø9ÈïX­í*< ç{ÉËjúøv•—E¹ëjSqfû´q2Êçƒ@\Ï+òA:x³­^¡Þ ¢àƪRû¥ð—ò8¿_÷ƒÀwÂ… –°Ó‹ºo®½¨þ¿o܈µ™%áIQÁİsËéhüÏÑ–CðME=oBUúH{ööõÚÐøÓ &|Š~·áeOÿ¯‹ &Ö1щì¢ÏS†9Q¢MÇb1ô=XôüW ¨vÞa0{/Z:‰Ü>Óý7­5LãÚhåvýšRþâI¹/ +°AT#ÀƒyûF7M9â¥Mߡݾwü0è¬^»B©toÚ·ø˜20êšÓ7¢&£gûé£Q‡oä ¶ä”¾¨<Ê÷î;)÷ár¿âáƒÁ¦–Ý©Þóxã‹ý¹ŸÄ˜ƒ‘@«&ãy«fÀÊ‘@R¸jòänÁr£oû¶æ2Y,–…Žs!¯ºÿþtë·Í»¥«ÌŸ¢Í2ºÙ‰ý½¼¤ ÿà:šøÑŸvkî|™õs¯ËQìÆ7n¨üâ ‡×ôþjúðpŽ N™ÚÙÖžMÚéŸ[#{é[•xsgŸZµ#öþô±-³]o5aIõbÍðØúy²·OcÊ/ýß^W6Øö„<íÛ,mŒÉÓÖ)6|uG÷ ·dNÝÙÔÓò©n^en ª.+#ßí†å‘R•E}ª¶<ëM­˜@NÞäSl’…ÅØ…&§¡ªâ‘þ=xs­< ).È}º¡pô'  D'@ó›Do!–H Ñ`α—ÉTŸo-2 ös®{°Å# $1ÎÔ'qã±è$@Ñ È¶xÚÁ6ƦyêÛå“✜ zŒÊà eåÃv÷/°Ý~³ô'Dšç¸ay$  d @QŸ ­Ä2’ 4šLknÆ¢Ö)XThb1gþÙaa'–wªVfî„ŵ²Bø%¯¯ÓûÇ–4:F   "@QŸ@Á¢ Ä—À(ÿ”¶e_‚Bc§Œ^X)+â} þñ-O3ô3òˆoŽLH€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€’‚€')JÉB’ ´873mHKëqàaÇuÿþÛ[µð×Ý}w›}¶Ó±§ŸRõáoÔ¾Õ7Öø­!ܵþ{:îwäÑCÌ~kEcê#oFzÜÉ• ß}½¬1ñ–H 5 ˜©YmÖšHÀ%pÿîÎÙy…fç~++³®RúG¼õW¸Y#ó ÿê†ã1>ü~¼ÿw ½^ÐÛ39·þ”WðaÉúÒ2¼ÕwÙÆ •ñ}1úÖ#WúïèKÉ&Zû–K¯ ÙöûÙ·ßÞ!Ö:ùýïx•m¿§¬ò‰áq¢¥~ç$@©K y\R·ÍXsˆ›ï¸£]©U:KiýW¼eõÓðœêõ‡iÃ(P†Þlbò>n™1!‡À*+°ü*ÿ}[3Žìü ´­ŸÇ|-ÓPõøŒCLÃ~õêÝ¡«ºùçÖXÿœ¼‚ËWY…Å‘u3 ß³ø±½±xüø˜Ÿ€ùýÇZþ]o¾'ÂÓK…þ^_ž“ ÄNÀ{P†$hMüþÇ2Vo^õêÔÏkø†>÷uXýæâü¾°ï<QùSöÙV«ô˜¡?]Ûöð÷öñäÞä÷v ºq|¾úG>v‰a3{y'N •ª:]}ŠÑÖ—­6[w#ê1ßU)ã Séüéü·#ÓUÁuw ÿá¨Ce¨EÃÌ›V0ñ}7\¬iŽÊ+ikk’Ä« ߣ ÊùkqaÞqâ‡ïÕ2ÏTJŸ •nƒü>ÆõйSQ¡¾SŒÕÁ‹Q®‹”V{hCg!Þ·à\(œÝ±–±6|aa/U¦eP7 é~àó¦_cY•Un˜†ŽvHåÊXÙÇ»ë„pö …o‰¶kt}ѶÕÎRþÚ²jý´Í`pYo(óEÕF€× ˜þЖ;õÕFÚ÷Y§–;=0ñ%¹08zRÄ}CýÇð…vHßí1=—N/˜Pg­Ã(ÿ”¡`è¿Ê£&͘”÷\x^<'h}ÌÖW%ÖˆH &!}ÄWùñ{ïòH,áGå.4tèm™ßñã|0Ýjt´TÕ‚œüɇE¤±gH©»lm_mzºaªñ¸Þ&Lî…iÆmªÿ2 s,ÄÑJ|¿''o2m3Œ®†Ò§–X¥³Eø*Ó¸V™j’a¨ì`hÁÿµvÝ~ÿÌ4\÷‘²Ä­ñ¸iA~/aáï­«‚…ÿv“tŽNºê4U|aƒ;éOëŠò¾|e^Á@7|vQ‘é~€ï"ï— «o€pδlûì[ uÃ!”uÛi† Ïä5³ºú~˜£0èq29¹£àÿªVvÓ0&*ÃÌÛß‘ö€†½¤#¢j Ê_ŠÁI!Ò‡<<:¤ž“wÇN^ò'Æ2JPYÔ©Ëí(ŠÁe¹m½Æ V}„µÔ¨jÃNÚöEˆ~ÿˆmZªíSßXÛùªûïO‡ðŸgh}ê÷ØdCÐOÿ>èB_zu³¢Ï¤måƒ6ùÄ¡þ¬í#çÚ¥}€¾48dÙ9×ÂÿX¡‹gW»­é÷û¤¡þÓËìýòígÛ¡¿†'%ç¸W.Àµ=22ÌÙ‘×øH õàL}ëkSÖˆb"€ûƒ!п1bôìÖÝS§fmÚP9U+㙼 ÜÐXÌ÷÷ÕÖœ9¶mß }h¸5´±³×שÿÃþ±%‚ùµJ­¾· 5:Ãçô€ÜJñ‡Xz¦ríÆß´ …¯Ï‹Ÿ8ÕöJÏòn­öQ 3¯@¨,ÁŒñð“Zm- ‘´žìöÄà?£ò&ÏéЋ,œR˜øŠ›ÒÍ„÷+˜ŸèúåøoÚA5?¨Œ?ÃOfð•^¹n$þîçõ™û?ìÏý¬&ì##ó?bãø^=+acHsFÁÄ÷°ð¸潯N÷¦½èÖ_ÒE;œ€Ão}¼y—…Ín?*×¶åfæbþý ®úÊ2‚'¿֤*–2J:¥ÁÒ«1 èai{ܼ´&íFæ6£ì2«ñªX£–샧 éX›±¤þÕú>-Õv’S¬õµ+×m ¿§Ç𜯩ÙL̬OÂ=òýô‚ÜêÁ[ý*«;ÇÛœxƒÖóqÏL¿g"\¿Íö~kýOþŽ>tI7„¦à‡'bÆ+LœXÏ$(JÑèE$ä0 @G$’ µ;fê×ÄR÷Í›«‡Xë…™êÃÃ;‹ù´ñ$ÔÁcn»}`ø5Ì8¾ê zñ0ÿòûŸ¹á‚ö«¯®Ä ðLýêÄÇSûž÷³„{Båä-þúlÄ+LÐ;—Ä|‚é;Ì~Ÿ·%lõ™özž ÷›~kîBÌAÿjÆ×/çCÀ~&èK(ÿ,œ ufnÝÀ8Æ’fXð:§†ò,†íº&Xx­³ëI«ûRÍÁøÒ»OdÌXʈÒi`ñNQ VÐ;ɤyÌ¿G¦Wï{ÈØ]ü?¦~j-Òvn¹bªoŒí ~6˜¾Û€ w³ŒéˆYþ'ÐÞýGåéFpžvi=y<áúÅ|4½ýÙy•½ät7Îhá>ðÛÓãiBzn"<’ $ÎÔ'Us±°$?é«1›Þ;¦µ$á<¾ôe‘á Ó\¢aH²¬Á¸ö}ØõUaçÎ)ù&ÌD®Žô‡Ù¿ö‘þ}?Eú™¦ZjÛJf¶q4â½v†ßõ¯9b¦YK¹ê¸öY¾á2[ŠÙÓ(Gšë>;CÀ·™Wð•ë'Gˆ»Î8˜¡ßJeRË#–4%~4‡' ÿÆ,o!ê2yuðƒë±hqF–9-–ÖìüÛ÷6”õm;ëú"ý¶(ev3‘yÅXÆ ËëÙ_§µÏXQµ¾42É:ß m;m‹'71õ«–j;·P±Ô7Öv–'O­üÏM»9Ç^æÄwVÛ…+dféÌ–´Ð¯.ÄCÕÇï³ÛEL¼¸`üçè?óTH]†¨NÛY–Yc}Os×—™ƒ“ $)zÿô“´,6 @# àÿ{̦˜9s¦'Ö¨Fº¬Ö£?Ì>¦‡_ƒP.ÿvn…oõÔ Õû…YyY ê…¡Æéê2¸_kZÉbÔ:å’kSo¼±¬6L'qYY ±hñÎ:Óï1Íˬ £ŽIC,i6•ã]ÈÏS™Æ@˜L<…Š][YúQÖ1l-Þ-pµ¶ƒ‹!èb\ò °Æ™^‹f¨['ÆXÆ (ÌÊÈ|ï¾îº ˆý­›jy½Î ¦÷õR‘éÕ~o¶sÓŽ¥¾hçLCÙ`Ð|Wmb¥ŸÄS¤sd]_z âmÕ~ÛVž…Ør\ÙeF„ý½N>–ㇹkó»:þqøRVjõB2µ³áN’Zÿ@8Àz!ô=f—£8C÷‡Àɾ;26Äõ2¾òéÜÇ#¯µÔ÷âÜ\1[•ÿþÉ•ÁMOÛØV¢ïÙhÂLÄàêà’;ÁáÃâ@ÞÙn™²ýwuÅúg¼äh˘ǽãq9Âõ‰ ;ú¶»ºa>À†Ó=nÈ o.^²ƒ‚+°õ®âœœúƒÀð„[¨í³ØÖy¬íŒÎöú™ôÿ¸8LÀª‚¡ÜÕöÒ?àẺ›áñ<ÑÔÄ=ÞÎσ¿ß²*ÏuË”hƒ´ÙôôšZÆ#ØqêÍ‚í¸¢0g íIÀî×ýq´eØZò¶œ¼;wÙZÞݲCËa–Òº¨^8m_a¹¢—\W|× Øx+:-<Ö(ÿÔî0—!ÿa­¿6æAfwÄl­'¶„Y Ì;ö…ÊÛaÞ1Ÿ"MÙéçcsÑ 1§°z CU($¦; ºüWoBïG]»üúnÏh×—ôÂõtØM×n]é„ Uœ‡z7}Â<‘ÎpYž¯ª:!ü{´s™eÆî=·ˆI‹Z¹nR§)Ñ"´PÛE˪!¿XÛáæ`ybd? Oæ4kn«më†È?Û®ªtHŸ-)1K¯æù'Ö1õrÃn96Üœõ+†ú‚çh: O⾚Q0aþ–¸<#híšþ¿µ“aýH •YÔœ[ Ga›ÉkUù 츧*1߫͵ØoWl㈅´žda ,fÅÎ-—aëÉ'±Óƺ4C?Rii¶¼ñÏÂö}G›MnBˆ’ ¶©¼9'¿ð·4÷í`Èîf[•÷BüXÊë™â¦Ý¡cúÿagžÓµU> e»ÖëK[fÛÁ}íõ0Òø¦—爿¹aulë+4J‚c3þ·°ƒÎxXü,§x$ÝflÌeüJÿ°÷à9o,þö7¼œé n0}¾,F¹/ó%í1fg©Œµ¡Š=-mcgýk›NéßD˧›/wÅj+°¢þÄŸã1|ë,<ßÿ‚z¯¯~Œ-æÖý¼>ÏV0tɦ•ÏñŽ3•wmÐ ®µ= é6dRU›hOÏÄ'WÙ…g ¯ŒGÛ8*¿ð!e~ivGì„…´f&v’݃T‹µ]mib8‰±Ñ¯ ±ëÒ¥Ê*÷Í8Óöþ¤ k_ì)ÚûÏß#9¡oþ}övì¼tšlK™U‘æ¹Çýo[)ÅhãI5×ÝcƒÁê?µ Ï C[ð0ÐÆv: T"À™úTjmÖ•"MÊ}+Û¶7¼_ÇçJ²_±lkfûŠl­OÅŒk†eFA.ö•×£1ƒ|^¥6¾³tÕ74ç` pvQAîSn¸x!J+ðBÓ±×ýøŠ`Õ ¼‰õ¤Ý&1Çû'Ö Ý»nº©´M¦!3ú?`fý¿Á`åšm¿€' ó}Yæ0Ù¡§)e’mÍ ã@˜E| Aû”­+—`ü5ØA_ß”ô$Žc/m˜7¡œCª”þ/¡rLƒ èCÈ£PC_—K×[vh6gÁ>þÔ{®¿>ª»l°8œ–#é·¥=p¼ÛIžƒº¿"ù5Å=쟰 kŽG]wíO+ƒU«°Ý(öÔ÷œ¼m+M)¶Ù<öý#ÑzaÐW¬lëc dfañî ¶ª^t-é´TÛm«Œá×cmç‡ý7ÿŒmS†`.ÇÌú«Õýßžþ°Ÿ›ž×“ö/éƒè³3õæà†Ò`ÙîµhÇLoÛgÀ´=>³¼YÿŒ&ܯ¡þㆩ™™ÿ}¶‘¡ã~OºùðH$˜ð¤ŽH€ª \5yr7]åñ=èçìbÒ—±“'wÁöö´ êÚ×7¡‘þÙy…7@äÞ[qXsú:ÿÝ­´ ϶vƒ‘7¬® ö;zß]W6uÁa´¢Êbâ7;@µ1ËNØe—µñH[Þöiªö¿ºÛ~JÙ÷Mîf¦jû³c‚­0Qüä…Q•éF¼ÛCLM²”ioc¶9J‰¶xÉË© ÏÂ’À"0Ê88Ôóäil“ºƒŠÁlI€všßì ðÌ–H€H€âE`ŒÊ`¼ù1˜^}ÙË;¡¡÷6Ä+;¦C$€¸P6…E"  XääÎÇ⊃A«32bx=VïƒKl†!hM(ê[Sk².$ÐJÊ÷?ð~ˆf¯ÜJªÈj@\˜m=³T¹}7ÿ ‹œã’(!                                                                             – `´D¢‘iŽÎŸ24d‡N„ÿNÚн‘i»È0üN$@$@$@$@­€Vj³¡Õ¨ËréymZÁ„…-]¯õ~ÿc«‚«®T†¾FiÕ×0”­´ñ DýjdZÒÒcú$@$@$@$@$°#@Ô·…¨ï ÜCke*C­„¾¯¯Ïƒ~ÿe-Q¦õÙyÃQبDÃ0fáó3Ó|yÚ„ ¿·D%˜& $ÑS¦t²ËìSµÖâs&¹BGò_wYã.ê³ó×j[Ýeú ŒP®.*Ì›ïB3=    H&9¹£l¥@™÷4LucqAÞ½ñ,¿'ž‰U z}¡Œç”¯ûéÅ×/‹gúL‹H€H€H€H€’‘ÀÂ÷ß^~àñç>iØe»aÖþú9~ãÂÙoÏW]â6S/&7Z¯ Á狹çÂäæDt$@$@$@$@$@.z#'¯ðå3!—O‰—)ŽéfМ£,ŠEübr£|Ý.¥ oMÆ%   h­ ½Œú}‰ÏŒÝìêÆEÔË.7΢XØÐûsÊš]*&@$@$@$@$@­”€èeˆð«D?;»EÆ¡žqõ²m%F³¸(6-Â$H€H€H€H€Z=ÑÍ¢ŸíßãPÛf‹zy±Tõ>ôÆ?âP&A$@$@$@$@)A¢þ¢£=ÝÌ7[ÔË›båÅR²}3ËÂè$@$@$@$@$2D?‹Ž=ÝÜJ7[Ô£;É›bùb©æ6ã“ ¤G?CG£Î;5·ÞÞæ&  Ýi¬nn:ŒO$@$@$@$@©FZZt´èéf¹f‹zìKß%(iV)™H€H€H€H€R´´èhÑÓÍrñ0¿iV™H€vyùÇ5……=ÆæšýÈsG”?ó™_øW¼èðãT¬;ëL$@Û"Ðì™úmeÀë$@$H®óßݹÔ*Ÿ”¸;t”²eçÖãÍ~ ”iÌœQûh"•·¹eñûµé÷vsÓqãÊŸ|tÍQ†ú ¸ ïHç%*îÅšãÈÜÀ4Ì<\\˜74âRó¾jÕ ìß¼D›H€Z'ÎÔ·Îve­H€¢¸ùŽ;Ú•Z¥³”Ö…è|À4<§z}ÆaÚ0 °OðfS¢DKj¯UV`ùUþ;úƽZ‘“÷t™ @“p¦¾I؉H ÙÈk¸Wo^õÊÝÏkø†>÷uXæâü¾°ï­âtTþ”ýC¶A/t 5LSÆäþ÷paÞŠVŒ•  $&@QŸÄÇ¢“ ÄN`•½úXÌÐešfÎÃu}ƒ‰ŒÌŸ|´aÛù0ÍÙ3ùAÌî/ðª´ñ;ï¹xì9^fÛÖcn7„ÿÌ0ÿ+š”÷bv~áJÛÙÈHÌQÖ(Ãx¦+÷ºÃüçm¨Ûz3¯Ä“„ßQØ*؇ës=>ϵÓý~¬ ›Wx“Rö©Åüc\?÷³—y†¡ǵi£ò FÚÚš$×*ƒÁ7PÆ Òü&1ÇÕ†Ï+8ê<×Ðjä¹q_ìíí;Ñ↓ ÓÐÑçõ惡c-¥¦!Ìi … ÷Ïöeêк Ÿ(ϘïÌìå85ÒDÌ®ÐÚ…0»¢\߀iÊXžž{>²õpÓà‘H€’Ío’½Y~ Ø„ôA†2Êß{—Gb‰0*7p¡¡Coc«±ßñò|ò €;ZªjANþäÃ"ÒØ3¤Ô]¶¶¯6 =Ý0Õx\o?3'¯à^mëÛ ^ÿeæXˆÔ•ø~ONÞä3kÓ0Œ®†Ò§–X¥³ExöÿZeªIx!Év0´`ŒÿΞµa•Ý 3äC¶|ßrñ»¾‰Ý¹ ž%Hk¦sÕÐ÷›†À £v ‘“8×g)¥=&òCý^GØ‘«¬U1½HÐTf-×j”Û´85üÇ¦ƒë>R¶ºƒšÇ‘ç_Àé%,X¾uU°ðßá11ȹYÛö ðþÌô¨‹Q¶Xpƒ˜M…‡“óæÖ#2=~' d%À™údm9–›H Q0Ó{0ú÷#FŒ€þÞº»qêÔ¬M*§je<3#wÚïçï«­9slÛ¾bôÐðE¢†6vöú:õØ?ÖÙâ÷ʼ‚×*µúÞ6Ôè ŸoÐþq+%«î¿ÿ™ÊµÓ*t¾>玲µj¡û\Q ïV×o”Ê+õK,«òFøÉ'f7£`â{Ø-¦—²õÕéÞ´Ýü%”!½rí¦©X(üÒŒ@þ5‰>ŽÈÔëQ 8N) L|ek™UYUmðDàiÌ’_ˆAʽÙþ»^/ößøkCqV[KÇÊ O:ö {ÒñŸQy“ç‡tèE7ÏìÛoï K‚Γ`…›Þµþ{ž- –þ„v¬uñ¨GmbsÃõW_]‰4>€)I¿:ññÅÔ¾§ÃýÄìá^ÁŒúÉáþÍ=¯úuó0ˆ÷~˜,<-ÝÆÄÌ=jgØG„ûG;7Tºóûax}£!ÖÛ(«üžhá¶øé³Qï·Â½siz`âK}§•}žx%È»y Oñ–¸JÝë¿nìø_ ÷‹G=ÂÓã9 $3ÎÔ'së±ì$@1€H_ÙôÞ1EÐz„óøÒ—E†7Ls‰¶C*dYƒqíû°ë«ÂÎSòM0«©÷ÆmÌ6oB€ö‘á3:û~Šô3MµÔ¶Õ ‘þÍùŽu;K|TãNÌ´OqÓÒå6,c0·¯c]y±üO˜áŸhku?Žÿ( äÏrž` ‘p‡¯ƒà_ûd"üΗb(!<¡çC;9×<>øÕu(Û ¨uñ¬Gm¢8l?× ËÕjïz¸éòH$@ÉH Þ¬P2V‚e& m°ûuRq¶–¼-'ïÎ]¶>£[Öb¨Ë û‹ê…ÓöE—+z©ÁuÅw½€÷°B¡:[CŽòOí³¡!˜}ÿ°65»ç(ÝAö ¯õÉVD±»¯^CP Õµß÷ú>‹RÔ#ÃÓhÎùCã¿4 ãvXxß§^ZÚ˜™?\áײóoß öÅN8o‹¿WÛP!´·ìy3.fð «Z uÒç H"œ©O¢ÆbQI€šN 8''˜sKá(l3ùo­*?ÁžîS•ǘïÕæZÛíŠ-$±ÖóÂô‚ ŸÊbVìsvuyÛ+®K3ô³!•–fÁ+ÿ,ly±ß?¢ªé¥©3ÔbÖssN~áoiïÛÁÝͶ*ï…¹‰¥¼žZ»wCyßAP#t;ês§‘¦¿ UG´å6¤ŠÈ÷‡½Ïycñ·¿a{ÈÆæn0}¾,Úî¿imN^aŒç§ ~U¦OýBºL[öAZ™‡(ÃóPqÁøÏ·¤ÛY/Ï®…«¬oÏÁãD¤Wç @‡Žéÿ‡…N×Vù,äy­×—¶Ì¶ƒûÚ!ëaÔý›^ž#þ&¹Q[†å"HE›—¢²ò¡#  & ùM“°1 $ŠúÄi –„H€H€H€H€šD€¢¾I؉H€H€H€H€‡E}â´KB$@$@$@$@M"@Qß$lŒD$@$@$@$@‰C€¢>qÚ‚%!    & ¨o6F"    Ä!@QŸ8mÁ’ @“$í˧FçO²C'¢Ö;iC÷6”j×$II+µÙÐÆj¥õOwÖ´‚ “¸:,: Àv$@-¥TkÒRI%êýþÇ2VW]išÆõ–mõ’Wª·Ḛ̈;¶Ï42|¾”{êP Ú6•éÍe&xrò ×XVèÞöm¸çžë+ð}•ŽH€H€H€ª PKÕí ­IK%¨ÏÎ+þshõ£Ð©}†ìÜG´çÎjïÁ}ŒÌŒtOÝæI©oÎ@¦¬¢R}¾l•šÿå÷=¿X¶òŽò¶å×\zSÞÈǧ^ Šû”ê¬, Ô'@-UŸ |Z–j¶ zÔq— ¢…³ßy\Ž-áÐ ¯ÓÚx¢O÷ŽmGžyŒyòû}»wR>oÒŒIZKmšÂAx¼×ÎÆnzª×üšYZ¼`¿#Ž,ýtÎ{פ°¯%Æ  H-ÔR[o贈⥥ÝdŨî„êî÷`Þ|Éiž]ú÷Üz«¤øUá3îÒÓ<î1Ððx|w^zsÞõ@âÃGpXz@G$@$@$B¨¥ÙØÉª¥YÔ—÷Ÿˆú©¨úŠ36Ò|Í~°ÐÈfMÎàÂIx 7Ÿ/}òùWÞp2j"Â^Ú›Â>9›•¥&  Æ –j,±šðɨ¥VÔggûÛø|¾Gúöè¨þrú0 Ñ&tJáÖ»[GÕ¾cÇi{íuH$á û&¤Æ($@$@$@ÉD€Zªù­•LZ*QE½êl\²íÞç?ÄÃú¦uJávþI‡š¶V=÷9îè±H%Yˆ¨íÞ´Š2 @$j©H"MøžLZ*ÅÌÊ{¼>ϵ{ î«iCß„Eø ǬvíFÁ» >iøÐ¾>ŒOI€H€H • –Šcƒ&‹–JDQo^tõ¸ƒl[÷Ķ•4»‰C§¬áØí„?Ÿ{’ãl}˜2   H`ÔRqnœdÐR‰&ê‘¥/=íDy±ö¡s“¤frÂQxvîÞãxÈÀ‡³õ©ÙXk  ÖO€ZªÚ8´TBŠz´ÅyS,^,ÕÍ’zI ÇvmÒµéõöGíŇ[\¦^7`I€H€Rƒ€#êQUj©8¶w2h©DõR2^Ûg&ZÙâØ5¶R;dŸ·;rŸ©'ãíßÌ‘H€H€Z’µT ÑMt-•h¢NÊã5•Ñ.Ãç£=};¥ðôf[$)?Äü†»àÄ‘/“"  !@-Õ@CTV¸›w¢k)v‰âDÄ;Œ´ÒMô?­Y¯&?ú¢šxùéªÏ.M®×Úß6« e©>Ý;59xFüêûUêo/ÌV‡ì5H;üà&'­µÃ×ôî8ÂZ79QF$  HÍÖRR‘܇žS  Î8vh“ë•HZêÃEKÕ‹³?S¿o*U];¶SǸ‡:á!Mª["k©Dõ·ú‘QµølìxDzùýOU·ÎíBÔ¿öÑêÕ)Ÿ7Må –$!ôòI´'5ñh>¦A$@$@©L€Z*¢õ¿[±V2l_µç >jîâeê?ï,ÀDé@Õ.K–6Ö%®–ЇRl,†Â»£KCë¦ÍÔG&üô«©ÎÚªÕkW_`¶{PŸnêÔ#÷Uzws‚þ÷ÝOÔ'ßü¨**-5¨o7uÑ)‡«w~£æõƒJOó©-Sçx¨Úcç^êÉ?P_ÿ¸F™¦¡öÝ¥Ÿ:ó¸UFºO-Z²B½‡8ûìÒW½ž‘ž†àîê¨vsò¨ †Ô¿ßüX}¶ä'Ù#ßêìR?ÿºQ=õʇjÅ/ëUw Î?é0µ3ÊéV¯ýM]wá‰êŸ³æE^jô÷šÑ¥+èky#!ÎÔ7š&# @¨ým—–Ú–Îù|éJõúÜ/ÔJè•~=:«“ØGý°j]=-µ7t’h¬·?þJm,)W»íÔÓ ;Úg[y4¤¥Äªbæë«O¿]®¼^:î !jø¡{Õk”‹O;¢Öï¨vWÿ{ïSõýª_Õ¾»ö«õõ$‘µT¢ÍÔº1V¶[ ·©´B½ðÎBt¯úË©‡«ßðØåå9‹œ8ËVü¢f}¸Xó‡CÔ¥¦ú÷êª2Û¤Cœ÷SY8î¿ÛNŽÿÀ>]•×ãÁõ.*ç¬cÔ¥§Só¿üÁ HBUÁ 󘹟±~°Ú ážy} Õv[ÿ}w¡úøËïG=g ܯ‡“ÿß_þ@µÉð©k.8QõéÖ ¢ý#Ç?òÏe: ivU!;y©©ß¥Í]Îr¤#  h=Üßø¸Ôhk:ïROC¿ €ö¹æüáÐPýU—í¢j))LçöYêÏǨn¸è$˜9‡ÔËïæ”qkyH€†´ÔìO–ªÏ—­‚6;J~Ôþêù·¨u¿orÒlèÏ·Ë×(ÇtÊÜP˜üRK%š¨Žqš²Ñ…˜ß"ý°}vQßüð³Â(KyLÓ9.ûégÌÜwU'¾·Ó†}1Êß¹C–’7ˆµÍ”Íbl¯öÄl~wØbµuÌr¾ün•ãïþùÓ1û«öØIýéèT(d«o—ÿì\š÷ÅwjèÕ)¹âÅ(GUZ^‰â:5l¿ÝT»Ìtuè>ƒÕŠŸSë7lv“‹z4â3¡îÞìqåµÀô$  ØâþMçˆõ‚¸åXÓ˜îó:vêÝ;·S i)™8Ý÷þªCÛ6jWÌÔõÃjh±-ÆÑòô£i)ñÿbÙ ÇR¢G—vj×þ=TÏ®0éº\.EubS/3û2›ß¡]fÔ01zJÅÝOŒQZ>X"™ß´Hmû÷ì\›n[hyT#H÷\pòa°«Z¨f²#¼}Õ±xlÍm,)SO¿:W}·òÕà÷MeÎl~xX™¡×6«zoýJÌà[…–”UbÑî–2H˜o”…VO¼8G¾:N+×nP]°€£å\|ÌšZ®|L™H€H€H  DÓ9RΜ³ŽUÿxõC5iÆ 01Þ©‡9&ÌÑê Ö ¿^®lÛvfËe¦_>®‹–GCZ*„4–`bö»•kÕ‚¯~t“P¿¬>S/>ó¦cnýGLÀ6Ý%®–J$Q_=Ük:å¨1Åô¦!'vïbu÷‹³Ádæc4tÇÌ&2ü[ó¾ÂLúzs¶cGד¯FQi¡F:±ïJóyê= ÊH¯{íãæ™N oî-\f&O$@$@$PŸ@‹ý¦GÓ9’½X;Lüë!¬¿ÇZÁT§9™êÏXwé–¯ùfÏŸÃìx¸Úc`oõÎü¯ínŸ-†´”X\ˆeÅ1CwG~[ߥGÍ|KµÇÂØ¿žqÖ8F–®ÙßãŸbŠ”hæ7Ÿ–­üåw̘¯qÄøÁ0‹'‹cÅõìÒAý¸zc/{šÊãšöm3±HÖtìçeÆ>V'÷ŒJ¿Æ#¦UX°» fwîÓÝ1ëy»ÚˆÉ,ù"œ'Öô›®†qâ=6jB]…H€H€H .í¦¥D¿Èæ"%eŽ=½ØÌ§cÃq‘Zê·eŽ'„íó!v¡qÜ–‰úêïQþFÓRl˜4Ïûò;GÏÉ̽è¬H›zyPôü;ð߬†¶—c*$³û ÍèGÉ>šWBj©úÓËÑŠÞ ý~E‡úçksÕæÒrص·Q'¢¡{tiïÔôhŒúþ9k®ºö®¨¿`ÅôÑØÍæñÿÍQ×L}ÊY´zÂÁ{ªµè±¸ÃQüç=ußÓ¯;ÁÝ{V[÷wÝþýå½`å‚쀳ç¨3[bôK1†H€H€H€E@6ñxåýEê±ÿÎÆÖÛgÒò˜šÝÿ"µ”L ŠýmE/¨ÌŒ4õ§c€isôMB" Ñ–’Ýeã‘{¡±Ä¬¹ Òuöqª[§j='é¬ß¸YÉ=â\-&çC÷ FžyŒœ¶'#f¹ì¼‚w%â@þ1rl†“­eÃÐö—Ý”÷ß!ƒw:ðú‹OjFr±E-¯¬RmjF•á1dÝFyE¥³#Žë/öX²eS\e•åØI‡ w’¿¸he×Üó»ÿ>K-újé×OÝwç5HKVñ®ÇG ÏÊñ‰ÛÖ:H‹ŽH€H€H`ÇØ!ZJÖ+š†éØÉ‡W;š– ×2"®5îEpïK÷>•£ëçÞ£î1î™3Ah$T¾W‰*öàñÖQ’s¸v¢–ŠÞõàâv¾å«U_~¿Jý¶±Tm()SUUÁ-Skîmcþ5?]éi^Õ!+SuìÜGíÔ«‹2MÓé¤áµá„RöŠQà©ùHŸõüù²Q´ëØéxÓãégxÌž¦a¶…? $t^m+]¢m½FÛöO¥¥oükÚ= PÈPÍG„~Sÿ»$T]Q˜:÷êy£¯;0#+ë†iö7L£þÛµEEkþ#&ZÑYžT'€©¼Í:dÿlã>-Û´éÍgg<ð ˜Xø´Æ{5îÍíê(ðS¶­ÕOkÖ«¯X­~ÛTª6––©Ê* lκšÿÔR[oº”õÒ E¸¿=ÿõ΂/Õ¦ÒJˆl­¼f¹2½ñëS¹uz¸ªUº²­ʲۨ—ç,Ví²ÒÔ±C‡¨c†î¡ÒÓ½µ¿I¦BPWÌûPYßàÁû´;ø”“²Û¶o—ƒ¦ëŽÁn—™awlŸifø| ©Ð#’°ŽÁ ½aS™Þ\VaúÒÓó³ó¿T–—?øÝ¼Ïš3çåTIDƒˆûdvν:lØ©m²ßØô6m®Äÿ×a÷¨{TÂБ@B¿OÓ22òä>-+)™¶øíÙÓ/þPîÓ >­á^+ÑQ"äC![UVÕ»Ÿ|£Þût‰*)­p†ð!GUx  ³XhN<È/ÃÒÊ 9ZªmV†:nèîÔR5PSVÔK'übÙJõô«`V¾Rµi³Luë²Ço!°Ñ[ÈÙv†*/ßM•”í«þ7»J½÷É×ê¼á‡ª½÷SéˆûÊ:™’ugü¤¦á“þ§K³‡÷èÓïn ô{ì9¨¯>hÏÕÞƒû™é2ƒOG‰LÀ³e•êóe«Ôü/¿ïŽÿ={sؘþì1úéûïš…ÂËŒ`2ÎÚ×Þ«\}ãIíÚwœfkÝkÏA}x&rdÙ¢ˆvŸúûãÉ£v;lèUÏ=ð"Uᓬ÷j´:7ËÏóÁ`бr˜ùæµb~&-×öl¯~oëS,ZÂy¡á:•U÷ÍÐRŸa0ñ­:øÁ)¯¥RNÔ»£Ê7?þR½ðÎ'*Í·Võìþ¢ÊÈXÞý®^š2`ÈÊZä|**vR¿m8]ÿ§R~ä¾êøƒ÷T^¯'Õgí]‘ ³óéødž7öº1í:vžØ§G'}îðCÔ.ý{J:H*€ªCöÚY>ÆÒŸ~Vÿzm^wàç/½)7÷ñ©…÷¢22˜L¾ö^E®õ¥eöîÞ‘÷hRõJ6’@ä}úÏYsqŸªg.¼æ¦ÛþqßÔû^á'Û½YÍf}wu”e…`íP¥ÞYøzåÃ/U9æØ–õí¨6eÊÏwË:,¬kŸî|Ú—Õàu%ÐR³¡¥öIi-Õ2C¨–mË&§îvÄ7ç}©þóö'*³Í—ªG¢í&è# .‰žÈ_Êñ¿Ù‹” 4‚AËy”%eMQ'}Rþ#dàÓvÄ諯‚ Ï;pÆÍ—œæ OQ,¬vk" ýxÜ¥§y¤_C OQŒúI¿O¦ÿÉνZ#è§ðmM=”urŸŽ¿ìtSúvfÛö·ái”ܧòÛ”l÷ªT'.ÎÕQ¢U**+ÝòÊ_¨õ˜ÿ¬o§í"è#+"ƒˆÏúuR¿¢ ©®¥’é$²ý]}¾t…záÝOTVæ—ª[×g”iÈ€{Ç9É_Ê!åyéýEjñÒŸj…ýŽ+ÕËYú£<=’ú¬“λø¤»Ã?T}Å™Gi>ZÚì°–aÆq' ýYúµôo™å>ÿÊNB&Òÿ“áÿ²s¯J™¥ì¼GãÞ=˜`‚¿O³Úu¸åŒËrNFÑä7*YîÕ¸’å úEß.W¯ÍûF­o›®¾éÝ^Ù;ð?—ä-e²¼øþâ”ÕR;° âÚ϶™˜tÄŠŠ úçk9&7]º<¿Í8Û3€”ÇS ¿9_•–U(y¬%eN!'òEµ‹ }›îÝûvé·ó.Sútï¨þrú0šÛ¤PGHµªJÿîÝ­£j߱㴡Ci‡úË}È}Þ¹W¥¬Ræ¾0‹ã=šj½6õêëÞ§Ý{÷½gàÀ=:‚€üV%ú½׆M"Ú¤&77•¨ÿB<—§yÔØÏ'Š“²H™ž}kaJj©”õò¸HVf¿½à+µ‹b;u|q‡ÏÐGÞ2cßåÚTV¥Þ…}šØ©I™SÈ Gúbí,ý§ýñrlEÔí¼59CÙ[ø½5þ}þI‡š°Ûí9ä¨ÃÆÔ܉ü¿Ù¹W¥¬Rf¬sñðmM=’u‰FÀ½Oe³†ƒO>i¤Ôl½«£D›”—W¨9Ÿ.U¥åUjY·¶;t†>²­dÆ^Ê$ vSQK%òGd[5ù{õèÒR³±Õ’ìr³½Å6¶ÀR.)߇Ÿ/uF–Um_ߨt’0|ø,½cKߥgÏ ÷ÜWÓ†> [“En4éçÒßÛdeŽEäDž¬½W¥¬¼GÝÔŒÄÜû´}§Î—£ò[•È÷j\I»:JféK±ïüü%+]n¶Ç¢ØÆVDÊ$;ð|ðùw©¦¥’Âv³±íY'¼;ºüaÕ:µ³àm3Õ¹žh_¤|¥å–úå­Â6Q)2[ï Y|ÔæãN<û[wÁ¶•âOG)A@ú;*v?ã’œQáD}¬ïÜ«RF)+ïє蚬dš>ßíø3FÈ}*¿Y‰z¯†•ºy§®ŽMR­yEOU`Oúµíd\“˜NÊV†' )¦¥REÔ‡œ7›É‹¥dúDvR>)ç7?þŒÅ("êC©`‚#OŒä£Ìzdôê?àyi ö¡Oä¦bÙH ®¤¿;/kêØñ$,÷C">IuîÕv(#ïѸ6?Kî}Ú¥g¯cQdw¦>ïÕ¸­õ!G“ˆéÍwõ²êGö¡OTç” eL1-•?qí#òÈH„ñú%Êc–µè‹¥âQpÙÇ^ÊùûæRçm·Rv©C+wòQìéådº/#£w»6éZö ¦#T! ý]Þlx<ýQçDÝYùW¥ŒRVÞ£©Ò;YO—€sŸâ÷Éëóõ…Ÿüf%ê½ê¹ÙGWGUUa¦ÛXn,)w^*ÕR/–jv‘€”Í2=©¦¥RAÔkeAo,-W¦gS<úJ‹§az6bÁl…#ê¥ì6V£µb'óå#3“ް÷¥ùºvì%~)å~Y¿IýüëÆ„¯ó·ËVå•òbÅúî«ïWaíJb? «_êÄñéØ>Ó4=¦¼ŒÁ}¤ŸH÷Aí½*e”²&¹íW’d¸O+!¾¾ùqMƒPÞúø+µlÅ/ ^oêÉ7œü>™^oÔU~³ñ^k3ˆ-"¢¾ÿûKñ©ô%þí_é3RIK9m.²Õ:ydä>6ªª²°ã¼®qn@Ï#Ôêõ‹`ß^Ò¸ˆÍmU*XÂÖQV­ùÔ»›‘jBG•ÿòÑöÓ“™áÃÝØJ]îCÏ©ƒ† Pg;´N _œý© Ù!•sÖquü[êËOk֫ɾè$ošf‰Û¨!;÷VŸzžhEÇ/k<ø×ê’Ó‡¡ëMDýW߯QG°[½kMõØXR¦ŠŸ{W­Û°YÝy͹MM&)âI¿7 ³- +÷B"þj:÷ª”±5ߣî½1ñòÓUÿž]êôd¸O¿ü~µzä…÷Ô½7^ |Þú?óoÌûB¹ÿnjp?Ñ¥Íw.Zª^œý™ú}S©êÚ±:æÀ=Ô ‡ i~ š‚ô}if¡xîïV"Þ«q¡®£D“Èî7Á , ©Gú·ÍTé¯ZºqûM®JSLK9£Ì¸4|¢&â>6’ŽÙXç1ÓÔ™ÇÌPO¼úG˜ï,klôf…w#)`~#êÑý8ÿ eüÒ,xIù²?©Tã»i³k{ÅŸÆq[õÝÊ_Õ³oÌS»ì­Ùkç¨éz<¦ºûúóñ®‡úB!j„fz~ÛÍ¢¿³Ýòkfqã÷¾{?¸ÇÐ+¢VÅ-ô:eŒ¨µ{&Ã}zÀî;©½nˆ.è[¢}¾[±V2l_µç >jîâeê?ï,Àÿª]V›–È.!Ò¬¹ä7«ö¾Ày¢Ü«qeäê(Ñ#²O=fKþè=wU+6—mWQ/…L!-å´ÉöùentóÇ'‚4fõÇiÙF%êó¶Q#Žb¢­:ÿ„ªªiÏ{÷4uÊw©=†!íZºòMõÎ'…ÎLþ‰‡LQ%ekTçƒÔà¾'` ðzqÎÕ°éú±QyK`”¼fç›êN)õhÅ3õáÿ[íŒÇ¶:Á3¯ÍsL­.:õpµhÉ õÞW°Ï.}Õë}¡2ÒÓ0ûµ{í ø£/ÌÆNNéjÄðCœd?þâ;çÄ“FŸé˜¿¼9ïKuÃE'©ÿÏÞwFq\ý¿ëM½KHB€èÝ4Æ€Kp¯‰í¸ãôäË;ýsŠCªÿNw‰“¸ÄWllcªé½ ˆ"Ô{½Þþ¿7§=ÄIº“N}V»·;;;ûvvö7o~ï½h#mÞwBøëåßÁ>²) 1B™“‘LöäѹâJê¹;Ÿ‚Fo½ùénQ—Û–Í¡Ÿ=÷.Ý~Õ\š1>íÝNÿ~+àœ‘éI”ϱ“|É0âïo9@ó/ÓµFÜ1=~ßµd4hiõ§{è "ªÕ*ºbÎ$ºúÒ)Ò©þuIe-»t²xùž†Q’Þ‰v˵^}&§Á𞎙Ž÷k7qÀq¦Ì½¶n‡x_NKÐ4‹wŠ…‰÷ôÞ/,ôËÿòK&à½?HgKªiú¸,ÿþ!º!½¼’)GñL­À>\H¿râXº6+ƒ,Ðôß8*“~uðí(¯¦_Ì™F—¦$‘øfSY%=}ä™ñÝXš‘J÷ŽMë.”Ðc“ÆyéÏÇNÒ…%aËx˜a©9½öCëìƒì,Scn‹ŽŸ{OÙ|p­Ýö±íö8¨¢ú(½»õ1Z»ã;4iÔ 4!û:q̤O¤E3þ—š­•´~÷)ÊL—Ï|Ÿ÷ f#x ±õÀi:ZPB÷¯¸œV\>“ÞÞ°ªê.ž–]4s]5o2hIá~F¤Z Úõ@ ¹n½þÀÃ{Êï.¿oü áôê‡;à@C_¼v>UƒÆV^Ój»‰÷4Pè' ËÄ@"}Ö0HÒwkHߪ„C¸=±Öž‡“¶”UP¾QŸ•”Ów¦#5õäD9'ÐçÿÏÎôý‡éº¬tºzDº(Ö RÑœäº&;ƒ~}( ЖÿoÖTÒÀø5ì4̰ÔÖÔK?ÜÈçyÙqDlò i7)ñ­!¿N7Ý|ý,? oÿ.åŸ/ÜzæÐr*¸€Îž8B]3Sâ)\ʉ£|îI%CºcEBóŸšM¼¤%ÅÒüBZ>ª8Wþ#K`0I` ½§dæ¹óïoi9MÈIÇ»˜.Þ5I¾‘|OùZ<dznwâÃÐ,N¡¿¿³Pðè¿´ü­6§$‚O/¥ø˜Q )4Á¥R©´«ÛëÞ¸—nW&ò'rg¸Dþ ƒ´ÄŽ Ráym«µ,®lXò­ò‡œ§Á3’ãé“Giæ„ì.m2bLbF€Áv`Ò€ó,±‡æã×Ô·z†ªÅ4©”.Á5×m;ŒE)>ôi áLuPij KfM ›¯˜%e—×~ ´1”õï -ï©<›ø<ò{šŽY0NÕxO³ :a«ÕØ2›Æû#ñž²ñä_Wo Æ>xÓål:3ô“Ï¡Ãÿnõö¸olMÀ¬ð5ë6 ýKKç_ÔfFAt´¶žâaúØéÆž+~zë~.ª|?íèA©ŸjÚÍËòìîC¬k<‡s=Ðp̃¡bœ ÜD3Èl©„a‡‹Fe\NYÐØ¦Ÿž{¼‰Œ£icî¤ å»DyBÝ–êÞÝú‡z9_ßK ^ÎÁLZ„G0ª‘› ?Ó•TVU/(9yg[g‚ØŸðËl£Ù“FC ¿Œ*jèÓ]™2¿¶°¬Zæ>]DÙ0x 5MÈÉSùì©A'|cç)!3BŠ/…ì8Ô×Ll&Œlwç¡“ÈÏš{ÖäRB½¾œO–@oJ€Û¥ôŽòv¸i ¼§<°6é‰ Íy6î3ø¨wÂ`QJ=}OyÆî¹·7áým¢«çO³w¬gÞ$ þu  “Nï¿ùsкOMˆ'<§áf5 .”«ñ}`{©©ÉÐØûf’ü'`ãKþY&±¡­ XlUmàá·ëòIƒ4ãÖÔ÷ô™XA«9pòeZ6çgtõÜ_Ò3«§Ã[ÇËtý§é»w僯|„vƒ_Ÿ•ã¿TqÕ^š5áº6þ·dfƒÙ=?ñ“7d HØuô ŒJÏH?éÉÇnöo‡²1wò(ʇ/ꟿð.¼àhñ¬ñôñö£âÔá/šùþ˜³ë[Ÿí\ü´DŸÖ.ðìI‡Óð߯˜3‘®œ;)ðp§Û¬mgPÀþî™»)Œl£^œX‹o³»è™ÿ|"ÖJÌ.0gþ†Å3aü;‡^ùp;=óÚ§â˜A¯¥Çn»‚’øü^X>(K $ðÇRbpüÓGo”~†´(ïéèÞüdí=~šù›ÍñÍ|©§ïi ¸ÐGOû” Äû,¥YsÚÐø¤ýòzxJà3…ô“™“i×MËé§{Ð…ôÔÜé´ûæå”W×@̯ /%öxcƒ‚jíµ‹…÷›¿ç†÷¸Ó”S§A}§â!Z¿÷§ÄÞoØêÛå²QQåzöË„«Ë`©¬¶zzuó­¤ÓÆÝÑó©¢.ª'„xêk·­õc·-õïgO3’·ÞÉt•gt¿ÿ8xùŸ{¯As¡­ù^ã/,š!Žßˆ V¼H‰X%CVi¯³Á×,3ðo·¯ïc·ªç°ë/¾z Ymb`.%Öþî¥utïÀv´î[äÓ»›ö o7QF=}åö+€£ÓšÀ§ìlÊþÚS‰9Éè tön †÷´ý»{ÙŒq´¶/N€"©¿`9Fâ=åx`ŸÐÏG¾Æà“ÀFx¾Ù„%F«¥°âtíG›„æžÝX¶OLÓ|ê@ýúàqêÙ[Žœº–€ ê»–:>Ÿ7À¬Á}àqÐJCÞî- ~ {ë¡”è9?OÉ3çž=êÔÁ­&»Ñ;¿÷™) mÀ;ûÐf€/'YCYá=å÷±}=ä÷t(·ºwoì‰FôRí‚zé¯Y[/§Ð% ƒúÐeÕeN‹½Nh2»Ì(g%0Ä% Ójè!Ìí:r>éóIí óê™s+'Y²†ä÷t`<¹m%`/k‰ÙÒöˆü«+ È ¾+ …qüã]O„‘[Î*K`hK`\Zò"'Y²®ä÷tà>›áZ³M¥Ä‹œÂ—À÷~¾Hä3d È% K@–€,Y²d . È ¾GÏKAܰ”›‹ýx§%N£ùS¾ ——óºu+¨A·.y‘2’¦Ó‘3o @ÿáŽï"/BtAѧô~í–]”;†ŒÜ M> K}Sa·4õZµ‘‚ãR¯î|ºçÇ"Êí„‘_À¿Vøâ¯œ7™²ÓÑÖ­ôѶ#4gÊhútçQ(*9>š6"eYM=-š9žöæ£/¯¸,x¡ò^Y²"*ʺFzáíÍø®D‰ p±ÑF…¹¢¶‘4j•x¿rûR¯B~O#*úaWX ßXZIÍN§Oø)Y¯§8¯†šwv±=5!Žf%%Ò7n§ûÆ¢›GgÑëçiQz ݽa»8{ ¶ãuZºó³mtë¨,º+7›Ö—Qq³Ï¥x‘ÙLiCW’yPÏÁrxáÐãm ŽÊ£ÏN›O1Æ ™¾” 5ÅE zÚÙÒ-ðËí¢úæ"Š6¤ @o4$‰¼„*˜Ÿû µìôzu”7Š®›¿šj-û¨¤f•×ïÃQéå ø?‘~g”•À¾ãçéÖe³©æª®¥§ÿçn²9œp?y’ΖTÑ\û¤¸(zö­M*¥A¤Ø\zwóª® §+ziy§,Y!J€]Â^¿h:8qžþºfýðÁ"æÃ{x3šàµšþøÚzù= Q¦r¶V tä[³ˆ­Ì(-‘F3 Z2Y­´áH!Yݒ±]æ ?ç&'RÊ`* ¬˜s_U-ÙáÎò\S3-ÏJ§"(š¢4>˜­Ñ5̈²*ÄKÑ Š„y=ÔÓõЄh—wlXϳ¸r/:L#m9ø›–ó4mÌí¤V·-ºÜmFJªöÓå—<à;j‘2›*ëóú®Û¿Þ*Òëâhjúí´pÆäU˜éTÑ:Ê/\+¢Ú¶Ò'd€–påÌBcá3ž£LrÚ~è4åŸ/§ÌÔ8ÐÅ<'p[çò,¸/î*ÛCÔ>¤£€Åº`»-̸}ä̺jöÏèË×~@*•–ÖízœÎ•oE¸íoÒý×­¥W?½=0»»¬ö¸gvúÆmû‰ ^7ø•ÿXW\?®§IÛöÑ0¿ÿ’ñ÷‰Åb«¸ÿ0,€ßÕuåãÃKzLsJÉ ÃTj€7ƒ‘ âp2Ûì"2ltKÄר¨¶ƒYé|y-K@–@ïH@w“¿£œ,x'9¥$Ä@ÏÛò{ÊRSg` ¿£¼š>-.¥6Ôšv'I@þjhÉ'Åû” lƒe ø>ð)ŠÔ¹ÚóvEŠŸkêè8¼æ¬Y¶ˆ\°çz¯°˜Þ((¤³ˆ6þöÕ‹è©ǃFn•¼u®ˆÞÏžÓwvîš/ØN®/í±T°¼Ci_[ä8”î,à^xd62-žv¿€Æ9žL¦ÃG;Þl gëJR«h;a¼áE>ûÎeÐÖëÉå²Ñ¯_ÉöðÊÇ7û·Wo¼Oœ×^‹ïÏÐÁ×ÏëUPZ¼É?eÔ>kw¾‚ÆRBbzüø)ªø“ÇÜåíË”/ \(MÌ0o§Òªzš4*ô´¶Ó©ÌãMŽ¡}˜ú_|Éxâ(^B’ïV–@?Kàèé"š;y4í-KBŒI€ùÓxw“üžJCÞ–$7a‚¶&Öû¹ÆCkÞÿ¬•#/ehYK@žé.[€|»,â'ã(iI0ièl•â›T£ –ý¢}¿>tœÔ %¨Ì r´häܼ“Œ F[@«Ù_]ã?‡tyáô·¼Sô‰âI8‰ëÆ&‰a©pÊ,y‡<¨—a øÁ­Ó—ÓCõÒC ÌÐw•‚×Õ9\?VÒ'Æ÷LªGç… ð½tŠRSO¥Ý÷ zÝnSì·YïTWjßüçÿ³vT¶¼èJ`<è5ÿ|ÿs*©¬œÝĸhª‚Q^ût땳èíûèƒ-‡h|NjûÃòoY²zQ#@{c›ö„sÛUs;¼’üžv(šauàÚ>Ò](;¿Ü«ðÜI*õ ˆb²•Œ^[¤*—„'áæÃ+¡™1h ´ÄŒQ“-dPÏe±–Þïû£¥pô]¥p=—ÇuÓ¨•!c©®ê0XŽiPïoˆ p©À›™DÏzÈfIzýÀò\/«5—Ʀ˜H©R‰:³]‡t]5ª‹þG è|ÐŽƒOJžæèôÞ9± ö_þêyÅV¼¨kTÃÛ?`n«êê‚òñA)n¼Ü_oæÐ«Z¦OY+ÿìî÷ããGÒôq#AÃñÐÉÂ2xÞ¨hs\þ!K@–@ïHà×߸C¼bñL¬YKê»ÎBØÂð˜ä÷4PÃk›|QUñUò·_¨.ºÑ«V‚7Ó–«ÎɆ¡êU0víJ#ß^zþàöÇ8JÂ&©F¹šcqR£ÑGknýæ:Å™”ÓM,Õ_õŽÄu‡4¨gñ¨R%²Š&L¦EUT[·‚ÒÒžÃ4P‹Ë¦HH²‡exà"Šë¥UyitJ è= ê} ßC¸Éðïÿ^ð0kà9á÷ Zl4ez°,¤¸Jl//Ø%‡åÏ2ÀWʃ?¿è;»©3ï,|L–€,Þ‘ƒªP’üž†"¥¡‘ç" OÞXŸ¼-…RºÀ8ÒèrÍ‚N©5]I%G16QÃãRF´†*ÌnSÙL‡³ãÉ>Léê²Ý:®Äd×I£RDKu«ýxÒõÒ“Á17B,¸gæ$ÑÎÓ^ª©¹…’“ÞìGÑ·½4×ÇáL¡É©zÌ"hH ko®3×]º¶g„þ‹¾ŠÓùsv­ÿÃÿ-¿Ù;-%Ã{ q]ŒÔ©°Ô¸½.«0Ð@=3±(Ú7»¼Ž3(• p¿T¢êàñ#+9–ª,tºr2Qõ”˜øv¿jìYCÏ€Þl™L™ÑJJ3Á«–¸®üâpÝù"• Ž»ë Žfþvÿ·õºÜ Žë”J/»ñY$üHIyh–“’M×]6}hÞœ|W²†ˆä÷tˆÁRo ^õ¯±«ÿÓ@õþÆçöº-6§“÷ÍJÓF õ:éáót\F9a8r¾n2üý&QBÜÚ~áØ3‡¾”§3™ÒŒ^ÊŠ7õã:r]¹Î<:îý¦}ÓB`!¯Ëåò…fk9øïglUØ|Ëß~÷rTŠÓj¹ã)&rÊ¿EFòJ–€,Y²d ô•|aõ…›pݘ`ÔÖÈã˜äÔÏO„‰– †£«X­zJ1:asåÀ~zQ-IŽîsŽ=sè™rÃúþÀR-²÷cØ€gÑç› ÔûoÞãtWÖ7˜#êyꈱ\n„&<°Ã×o.F”Ze=«K¦òʇ@Í) (¸º4NDwíÙÆ_Ù07Ø=»­l6O'«-.žÜ”etCCßô&êÀk£¨+×Y¢ß„y™‹²×7š½N»½î¢-;¾w_s%6ÿ΋à4ø º¼ þç0ßZ-Ùv$Uy¿,Y²d Èè\ù¶çuäÛflù),Õ޲ÛíPŒ:)Ùm&„A£*‡Ž¦×S½IK•Ñzª‹Ò„äÇ>h廨É>èÙm%{¹a£XKuQí^?<A=ƒÏÒ&«]Á6ŒàÁ÷$ù£’´Èz”…(iv»üu'eÀ•’IÝDÅõvªµ¢*€l†Ra7¨¬1úŒPòxµH _àFá‡^0Ÿ vP²A ¯õŠŽŽ",Ô¹Ž\W®3[›ó=ô$±YžV‹™¿ò`IZ‚À_ #ÛÅ-F¶ƒà–USÞ™ªm0S}³…쎞Ró“ã—¸(#%Æšhò˜LÊN÷w *|y§,Y²†€z«?eш>5šûÔ(š2²,¿cÅŠ §?Aý1v‹ìYÞÝJƒàó3u»=´€šµæZM%¥¥|Ð'†Òl«a‚Ý/l$]W¿‚þºÚF·\1šûIƒ¬JöªšFz{Ó>:‚Ù!žõÐéŠ0`=Íf1Ú}Þ·zìï¼Ùy½j<#“0bw8ÒðNޤÝy´ãHÅ௸üºtZîEò’ÞQ'>&Mfâ38G;Þ7¼sÃ]“Ú­}ˆ$µÓº& ½·éí;qN 8͈Y§§&½–¬Zè*ùÎož½mhПè qBËmEž“çiOÞY̾êéºSi fã¸ßLR;uA¹Ôd±Áó™]´SötƳ1øDÒãYàµ#½Ý¶?=DŠ 2ãúÌP’©U1:±°±dn•™þòæºõÊÙp¿ wý”" äÑfÓ_ùGΑû9â-q'q,Õ_8Šïy¨b©ŸgÈÙ¨—F—Ü][ß{ûõ[V~íî×?ÞÿýV(µèd{š¸AJti´Çƨì>R»  ÞƆ-Úz‘eJ×bÍ;Sj|ZzŸ'¦Ú0ýÆh4ˆ…i7|¼ý‡ ;÷δÈÏãq¹·ôþ§(ƒxapÏr–dN«Wß®ºãŽ5¼/ìÔ]€ïU(Ö8múwžü†¥1싆y‚ô1gþú÷·\óúÍ¥)G4NM}N¸3ýïh>{¦ù¹KW˜·Ø/ÙY®¼ð»r gŸ{¹ÜvÌ:m£¨è½ 2Á×Y˜I¡pá=i‹AgoGCUbÐ=ŽššÑËÚèLIÝuõ<8WFçJ«ÄRRQK6€¬`I÷/).ŠÒ’âh\v*™A)qƒJÞÁîKÞš$z¡¬ŠþòÖ&ÐkœTo¤²8ÙN>ÜÄÁvìx_ùÜލ‰rxgqЈ:­^¿Q˜Ëéá› %Qþ¹r:‹v{¶;Eh§V :ƒ%þN$€+žžCc³ÐNG¥ zÏ@ê‚õ§·îÞû@IDAT5 ö§KCŽjz( ï6ܾõÙ^ˆÖ eÉä>{¿ç?ý´¶)1êJ/<ÈEZ#?ý•W’^Ïî%›6el^º”¿ß%é»ÎaıTá(¾áÁ€¥:{0}ql z¾_]rƒuÔÖV6<´ÿ·ŠKæ¬zùƒmÞ‡oYÜ…Þ„Oï@ßßÁÇ8¢±¨â%¼»‰(5>†¦¶ÐK"ñü×TÛ3XnüUÔP%4ð•uMT×hhÇlŽy°f|´A‡÷GKŸî:†Ïh¥§½„vÙqš]ƒÑ/–ú†+h;¬C*Añ9€æ·V í¿F[Š÷¨”ôÆF1˜Pª¬hb@^Ðü³¡ºË•@Öª=;‚¼ n6šÿSÇÒ‚é¹àFÇôh+iùWO$À픕E•µ-í´Q»sÛ•Ú©øh#lªÐ×n:p’зOÖ(‹ëÎ_Š:\‡—ôzpÊOÑ3ÿùu«6$h`d†6» 혡uÁ±¸ßî[¤‚æQ@©¾¿K0Ëzät±(Þ„ïÄüi£iáôq”Æök;½¸?=B èBð!hq=Øgú7ì§´ÄØ^¥7324é=´L›™vMƒZu=y=£ÖÌÚ÷FŠDûg¯t¹ï¾ÊI/=w¬ªèô•óW¯ÞÚh­À£ð$ Å8qß#‰FÒöO¯a) ÏôŽâÛ’àt팥Ú>†¾ÿ5A=.tÚ>_÷þ¶èøøbûaz›¼÷­¸LÑS=7 ÉM$8%w— ²ô9`äÁœzæÝó‡zH¡‰äެ;‰¯'èpXÃ×cm=[ïóKÁ@žé>÷•>Î}w®xkèÐcjYqæø‘wnÏ„MÓ} ûëdù²œý C'ª˜Š}"=ý˜‡×ü…'v½òûWÒŒ.{í¥2öÔU7IyºZ‡ ðqÝEqñÞE7ßëö,¿Eq¬©yO¼·µ*]]¦Ë㬡ãgÛl¶Òšõ{@¹©ú.Oì£ ÍY‘L¯²“r3S1Ðc{ŠžÏLu§ú,+4˜°ÃÀ¨˜N](iiû@µ ÍÞ›8±€ÛÍx†Á @¿:‾ý}ÄÆl…ÞNÀÛ¿yé%” #rþ¶ôu’dÏýéêÏö Ê kèRâú‹êè•wÐÏ»Y(Ñ"U?'úÈ"¥—ÎŒJÉÊYõ“-è £DÙí žMöó#RU¬yŠ{Aa´MýÏÖ¹Èø5^÷¶z[ÝOÑÙ}¤„ï p Þ˜ôÚóóñѼv…rgØkXŠÛ]_â(¾7£p]þ7±T»çÐç?¨çׂ"ûdjþèµ—^¿æ®{¹ž÷•ÕÔyïZ~©2R{n$"RƒlÐp˜Ç ëô>cZîÀ8õÔóù>Z…ÃÏ/ƒ  ÷b`?R´ æÐ3å†5ôgò޼·ñÝ5ãòf,¬©ç5Ë•åËrn톼8æ…:“wzŸTþê…Ÿ½†¯Ï®UÿP_ äöW¼GÇÝîšä§^Ð>ü£GG8_°ôë絓ŸxÄ~²m-CøFM3š¶ ‚[éµOßEâzš}™ ÉÁ.×å>~~¬±³cöe3<24bj<-e-4»,‚‘¸. qˆx ÷§åó§ŠŒÛD_$nÛ¼4ÀÀmÓžã´õ`¾ °h4õdФ¸¸3óU¤ÖÔ¡ÎO‹«V7±ÞMLÏÉÌø=.Ö?¿£š¨U  ½‹ËMÍÍ³éØ™ËÄLÎÂéãaç0KÌBp!§!©2}Óþ´y>Y¬r‚‡^êE}’,ضÁ}^GÀ™ +¹gr¢Ïíõ„¦³?ÊT¬»âçwT®geŒ^, ðSìäAò_kiîäÑtûUsÐ7êû Ü·ïOÙv…9ô¬!H‰ëSEøLgïfËaßГþ”üöò*ú´¸Œ6–VY‹~PkˆoÏè/zäÛ”§ðäbþæA—»é~•Bõ;€†·AÓ|]õO|yåï8ïä—ŸK÷:½aómÎõ}Û{KI¸¥·qß›Ôû®9€±T»Ñ×?*¨šzƒ¨îã7^ysÎÒ«Ê ¼æ$­Å0þH ‘×ÒÂ×ã—@z!¤êyåÒS/˜R=^˦͛—fàg‡<š@€ÿä_Min‡õ.Æ{›ZK—BÜ-*j/]¨Ø%–õ{~JÙ©óÂøüÌx¶Å@ÏFÐ;^n˜Ï>Ð׉=ðl=¨¡¥³ÆG¬mtuŸÜÖYF‡NÒŸî$3ܺ y”ž°T–’®N÷W*-þí¾ØPDpP¦V5Á` ÕwSCÃÚvÈ .t1=rË4"9^<‹¾¸'ùK@j§'ΕÒ+ð°Âîkà¦$L£L6¬ìËI°Ë¿8Á=eÖZÈ›wŽò/TУ7]N£F$÷z;mߟîÀõ9ÈsÙbâzqýؽ&ÍJßßPëzÇlo°¤ðzÎyÝž·UåëGùêþ`yBÙ7é¥Ìô*Ý—j¼ªÏ5“Ü'm'¨¦Vš„8Í­Ûo|HÌO|ùÙb”ØZàkøˆï|ÒëýÖ“m5üm÷kê±ÝkXŠåÚÛ8ŠoWÂIÒsäõ@ÄR\×þL Ô³,XÈos¿YíÇ=†jï¦õÛŽïÙ¿àÚëW€ë~Àk,ª|^o\¬I¡×Àñ{„w^©ýïÀc¡lKQÊÛþ·´?ܵÍéôrä],…:*ÜnWÓùüã[w~òÑ&«Õ,ù”Ë “œYž,W–o•+<µ™!@ͯžSÝ„9ê/ÄÅ%-l¨«ùÀ½Ùír=ÿÔóÊO¡ó^Ý~œ…N$­z^µÜåtü¯Úû«þÿƒ/5Ô)<Ö[ðæ½¿téæà½.Ú>=ù5söýËê™óÕcgÌõÜ2"'ý!ƒ±ÌÔ:‘Ð=€ïÓ*¹„–þl´+6—p[Ù¾åw”ñ0UÕäÒ9Ó™ŽË×YöFý¸MûäãFeŸ: _F#ÒÞÂà–ÉðK* Lâ? æ8ÕÔÞI¿{é#úæÝWÑè)½˜†Ÿ´C»ãÀvúá¶Ã°ÛÈ#›NM'Á‡oÆz8&7ú…Â$ÕDëhbY=ýê'ôÕÛ–ÒÄ1#zµ¶ïOy–¤r€ÑnÚ·ŽjÃÙóèSGg¥ðÙ>OàïP| ‚¡¦©òÔ¡ƒ_/yåµí(ƒ¿¯< Þ­4ùÕn½æ!°zö»¼®·½ùŠ[óî]ù—I/?·¤¾Áµ…¾& V(Vã;=[ºHÞ}+/L|éÙ o¾öÜeØ·UÚß²îW,Õ7µÿÝ®®!ýlÚÿ© ™"‰¥‚ß§»b¯8Âdµ kn`7›=ëßz}5~8aƬqÙcÇO1£’´}¬Z­ÑA1`ß§O¡›s¹]v»ÕÚ`mn®-<•ŸêèÁó(Šy%¬gÏÀž}}Ë6w:Ÿ¾ÍÈ‚³@+0 Xÿ1B·ìkwT5ÿêyå8Ì/ü77~â êó¯özÝß“(úΣ–²ß½œºÆa«ü¦²yš_Ú´iÉ-;Oo¹CáU­òzŠXBO¶4àº]¦ƒ;]µwÒÚ{¾õ¥«¦OJ›¸tžòÏ@*wãÑJÅ…ð¹Zz7²a&ƒ½P0#È`8Ùe=ú+×ëÈÚÈ‘I~íG¤:.é¾|²q ;ö±ýȸTÝAñqŸâú’œ¥ÜÃo­×Ÿ§´Ô¿‚õ0ýmõgôÝ{®<{Ö É©ï$ µS¦Ï1 _>9SQ R£:¤×ô]íúÿJ<¨9o/S‹èïïl¡oß½ŒFe$w \»Só`ý)•Ùý@N¢~¨'ÇËà>Uš¬s¨@>†ÍWg¦ÑòÌ úhí:Z\[¾é•×òË g{â+Ïý\åQì·œ#Ðôÿ"%{ÜTöl3é•çO¸ÝÞÕðt3µ²øôjÈþn”+@=¸äo»žŸá˜Zò‚ƒ^i×¥¸yÚƒzK…ø@"‰¥B¼d¯eˆ žo6p„)õÀjÏ?´ÿ0~¡„&kþâJ_]éì²Iå,+^$þkâ¼KtÖ 0¸ïPKcPÀ+J¡¡ÿ½×«Zñ½Glçx} qnKNNöÜqGÞZìZ‹FñÃGa*i«¾øsëWzþ„ýzê…½©(ä2P>±ê…Ÿ=ýÔsгZƒé᪠fЦ™ãnÇrü×ÐÉ ëÂø]¢°àèÁL½a¿Ô¬‰e_ñ¥´Ä©tÿuŠ,n¸f¬k¾€à4¥cgßîì´Nå¤-¤ÒšÃ08åGÒqâºq9ê*×›í<˜IP/%6gN2úؘmÐw\³áwD¥j¦Ô”©¼ü°QÙýr< ŸmÌð“Fßß±ÔNÙÀ}7hs è+bÐG÷}eðÙ{ÎѬšy¡^ýh'ýàþëEéhëóhíO™ÊÔiR|,½¹Œ•ÈÐ&!öKhS/æÐ…%Ý–ê¼”$:VWOæè0‹:‚b[Sß,¾RÚ ?Þ±¤ÌÝŒt,Øzá{/F×Ö»çøò£Ÿù{¼S`ûÁZþlzØ«ŠN­„‡›[Ù¿1¨°2X­*µWÿ¡Ëkýû”×^H=öÅG*x'½àà¼O¸,½Añ¢ÊªïËÉXŠ…ÔyŠ(–êüR½´£†ÐûWîú Ü%kFºô›A+#3=õ|¬ÑgP?=nS$I. è]X$-=ƒzñŒ"™ìÌ Ë³/Jpeùþ¿þ•“ôÀçý¨÷Ò±‹^†öýæ§¶lXõœr ",žÀ\ï;Ùs ®ñ% tPoP™]‰ÞK®a¾=h<_vÚÌÿBÞë/ºX˜;ŒúDîÃø#’æRNÚÕ”·Þ\”"ȋƑ¡¦÷¶~¬Ž:š>önZ>ïWðL÷ @9‹4¼¤Rjé–%/ÐKënX/èòd®c¼J0èÖë|Ð"õný0»àú¯_Ž?kèåt±˜k÷1ßDÇÏÓäÜ,˜"9Ⱥøªò–S=Ð×64Ñ»[Q3\@¤È€>Xë`àz&ÙDÚ’Ú¯Us§æF¼òó`Ü/q,še‡Ar¨é{»R=l›n•E?¾d*m(© ¼Ì…›4¸×?.˜E_ܸÎv¡$‘ʶ«ˆn!›ËI;ªkiCY¥ÏصƒAA F>ÈKåuw­ ï3Я:~ï£ÿá2@ ‡ ›× 8žoq)¦áûiRi”w3pçã'ø¾Û_yþç§gÿ(ßžV)¼¿‡¹·ßˆiÿ+»ú°I؉˓0cK±D|I’K±”T`­2¨—„Ì/p›"7hôlةŠÔepÏòà$ÉEjˆL­‘^Tö Ðù7ïç”_né\l¶M€ž´pãW¬zA½ô—+áS‚Ë!PmÔ;Om¹B¥Ž€¨šw¡‡òÞ¡P(W­|d??/R©•y ¿ð€+¢)d€_¾›Î\Ø -½‚b´“©Ñ¼oDõCªOmó9ª¨9ßì'hâí+h|öµtôÌ[”;†®ÈOO˜FMÖrU>M' ?e.Ÿ· ùQlT6ÍûE€‘ÇàWúÛиGÑÝË^‡Ïl'ýýí^Ÿëè@tJáZQ)¢q§'…xA½ä èp“Ý'|ã¿âÙÃ3Û9ÔÕÞ€Y¢RŸÓ»vÃSÂßµdëÁ«6À“ ïÃéìøá¥®¹X,îa£a7 iOÀµçÌ #1#Y{î‡8.÷KËÅÉ1€JCM…Íf:Q×@§隬 º 4–÷ΓZôÿ6‘–á7—öqQýáÈ bMúÒŒTºƒ€5g éû3& ï3ãâ¢É‡ÿ¸çП-ÿpc—UàzÔ6ÐÕ|FV\ïò”[»Ä@~9êp5¨5‘òÒeØÐuÊÏÝèqx7O~åÅcy÷>t Z‹‰Ëþ/®¬wþ-ôÏ3苵bÒ+/܃ïípæŸ8qúÚsŸ¸¼*ë‰/=\(•ÆZ 2–ºXh’d,uñeúnÏÅ­»ï®Ê•XÐ,ä@K#LŒ€^âÞsßzoƒÌƒ4±L¤…eØYFŒ`yÍ  ª¡Çþ.ÓqmF&^DÚsöϒذªù‰‡Âë y-“çç.úTÊ­Îåx ';C´”ÖýUG¿°|Ü.¾C䥒ÊcÑÌ%£Ï›pXd:'¦Î(•jºýŠ—`xÛHëv>‹éÆËÿJu§òš£l“H‹¦ÿ/5[+Aky†ªNÑñsïQVÊ<Ú|p\F–„tmL¾úc%0祧Úa.ƒm ˜ÖÓØdÇ´®ðŽ€RÒR†k&ö½Ï2âÀ[¬9f7´JX³õôy WyvußR;eU‹•À} «›äÔ¹,ZUfÂQ–}í42Ñ©¥>ˆ5õìö™ŸMw• “âbÅM˜[´ôßœ2ž®ËΠçOøf1WNÊ%h:ÏÉ'ÿüÔ$šC"^ÆæÒJ*hl¦YI‰ôÿŽæS)Üi†šê01Ûñî"ø:T|šÔÛ@¾}ݎݵò̤WŸ½ßëv®ïùynW³# 1ýkPÓW­.Åë_~®Ú|VZîTª4ß“Ê8úÅ•ùÒv7׌d,\xŽ’dÔkX*øå#»w ƒzén% 3¯YãÌ€UÒÎso/zi>p¸€zܺ_ûÎ QZXFÒ¶$;Αôøƒ_7¯záço­z^¹þWÏë¾Jë•è$Ûx½½]¥T=у±DXu øfk5\|~@‡O®¥‚æýb*ŰҢißý%–2’fÀ^P¼à|ÅA ÿúú;©°|§ÐÐËZNSÇÜ!òð´½úî-~ªNE]ž¸nY ¢.†@¿áÌ>`ÃÁÏ|ôQ@ÿz®8 £a¢°Æ'éa©Cÿtؘ~ì£ôN‡é{­[CÄÈ€¥¡/½ðïÛ¾Ž|²(\¬V§·r™Ã/qøœap¸ÑN5½b#Ño|ÀŸÆa¤¯MG1ˆ>=5!–ŽCc¿Ó#3i]Q)½tê¬øE7Ì žwèìà„<œXYÃ)eœÀ9¡¾^hÿ3ºÚÿåY#zE#ßU}ŽßóØhåÿâr5½ÈA”ˆ‹ãç—ÒâƒNÙp‡F»+ªé7‡ŽS^-;cƒ1øñ±ú§`7!¥üúºmt–8&íÛŠ O=MÉÐÐÿcÑ<š ŠÛ‘²SêN½Žß·òOöÓqîÕj½¢õæ±ãÐÍø„Ó‚C?‡VKùd&½(¼– GñZÚçË=Àÿ&P/‰R°ôu•½t|8zé^YR’ä"ýîõõ°'¢u¾>‚7ÑS(sBquwÜZúJèù_éC¤ÓÆÒ˜Œ+èLÅyLë„\ðÖÿœúÀêš ÅÏÄèÑTb?в=ŠÎ”nògs¹BŸöŸÔ~O’½tí‡û›Ëšz7|öcºÙÊfìÉÅ··¼á’ßjCu WQZœÒLþ™–§œ"/©2}„¹ÛÍ0Æääø“SLjÆlÆèªfŠ3éhTZ\¯´S©/â~‰ûß;ú§öÏy'§>ð.˜7_gœ>»”Fb›½Úð1)Ù1ˆèiJAÔÝ1±p…:@Þ]…>þ+&«õñ=7¹/@|Gâ㎌IØ2–òIJ’KGrÐû#¨o/ÐAýÚßÌPøýÃGÜoá>xé×äÿE¨#g­¼|ùy“£M~Ec2—R”1 ÞQ6txŸuçð! qͶjÁÇoõ¹ü4Ñ QçH}€¸ž6gM=sêuðÁÉ銇¶^8Z^‘aº× ®VSÓ¥Л´jš?.]pè{:[2LÅòms;ƒÏÞ¶¶Ëëœn²«e`Li 6UÕDÈgáßLb°|‘Øéþ”ëô9´ðl$»¹´B(2x{SYÇ}ÜzÐNf'%PÍAMèÏøw()Rýi(×ê*OÞw0…¸¦«|}|\ÆR},ðÞ¸œÜSö†Tå2‡¤\n½¹ñ>ŠÉ¡•7m¥%3@Ûþ‘NÁ>¸ƒdµ×Ó“/Ó²9?£oÝ~PxÂé k¯í–>Æ>m½›bõìûÞKVË„ˆ\sTú"º~Á"RÂe]1û'•Çö ßs‘o{cíñ˜¨´ü«T[ EÁËÆìQ ˜®×"~€JLÙ ^sQEåÝ–·QÉÓJ¬QÃöŒ”`füÓó”ÚÅ¿—^JFÄ~ˆDúÅœéô¿Ó'-êãë–ÒÒ©AEb§ tºéêhLEEÁÓÍœ‘±„èê"ð” <ôÁÒNWÌ£c Yýýò9ôâ’ù‚'ÿûíôÆö²j°;é3ð†3s2m¹a™‚x²iŽü[–Àp’ÀPÐÔ§ç%ßkè‰F†½Øüú•ì¯RY{œ^üàjÎ}~ë[µDooy4èyë÷þTx¿á©ëîÐszr?R…¸ `ò`5ŽÞ`•²uk]Óx†Nw<°‘ 3ñA¸Å³ÓÁSÂM³´û¢µ^†àï¢ýŒð”ŠÞí²Ü8Ôòú®ÑäðÒá¢FÒšb(#]% dû“‹{±@†ÖžÀÁ'·U Àj<߸¬T ”£‡ÚúFPz>,,%+Ü1v–&'ÄÑ=csè»u–Ôì6¶7Á’š]' S¯`G{¾O…÷YÕBš°Àèýpq3ih§j;ÔÌROú6ŠºæÃo˜RýÏÎý‚Cϼû@ª ÇJ²ð@àx¿a½­ÔœžÜO`=ämYQ½û…ˆw,×iXH@¾uäo¹«è°í¯èt…œ¹ÞEètÙ[¤ýN¦QKhþ”¯…ëhÿɵÙÇ?Xó™‘°&dÝí§‘ŒN2Òþ"'UUßN)ÉÿÁþÎÎE…ìˆÎ¡ð têÂ'42m|ßOFy*ÊL™MGά3Y)siÞ¤ÇÈb¯CžEôÁöoÁÀ8ƒæb_lT&•V ýùÿу¹hZOWÏý%î=šöžxá"ûÎmJC<€{(¨#oR1Œ”ÙfþRУ2q½fk®ÿ&•Väì!%ºŽFd<šR"Ymã1ðY@Ÿ"ôBQ…¾tÍ|¿üB*LΖ„¶_F'驺ÐAãË)/#ö:aÙ&sŒVC—¦&ÓÚ %4 À}Ü+2œ™OïÑ&DJ5êá}€—”?ÌŸ%¢Ÿ²ûÅûÇ¡&®©£×Ïœ§ZP?8éà éG3§PhZ/Ÿ:woœóp™·Î&æu¿{®˜ §å™étUVº lƵƒX‘1È®{ 'ž˜šoqPF­•6.¤Íç*è'w²3²ÖþâÍýéØIÄÉhU@HEމ‰B0¨)ÒO±nŸŸ½Æ¸a‹ã€‹Ñ€ðáÁ&Ò)CJÙ] Ì‚•!}‚“÷É*èA9TD ߇,¡-”X«ˆiyµJMhêÙ¨.NY à:†Ê* B÷)-L‹ Î‰·¯˜ýcÒéb˜¿~þï)7óJD ,G¡FºP± àýŸh8Á鷈‡~+@øì úÄÄœÄî@ëÏÒý×®½ˆrÃÞ‰¼þS ª1˜XGw ~@”1•&޼^DÞqìäsLî$¦†b¢wà?Mñqë)n@ÿýáv1@bp §Þ‘·Už Q£­ª@­ˆ1ê('k‹“¦Õ ÛÝ+Ñö¯g›µè#LFú.¨3Ñã›ÀçþÅìi´8#QG=Tl¶P–ÿœ>G%ˆîl‡fÿ`uÆ—$'ЗrGù«p-@y~}#"öÃW.¤hMÛx{1•½‰˜-èµ:­pë61s •Ô6Ó…¦ *)û4ßû)*j?´ãeí‹ë·ÍÞ@[þVœ“6ŸrÒ/ÆÄvW3pÂíçq¬ÉR&Ü…Ž€Fßé4SvÚ¥þë!€ØáÓ¯‹ßÓsï¢ÌºeÍ¿’yîhñÑ#¡•¯€¾ÖÜOuíÎ{–ÌÅþ2»³OÒó98õ͘9¹™v- Ë/ï×(w§LùœàðzpÞ5Ǻ–vš«#ì¡ÂfÍ:_K1z*ǬëÙç«A”þtññf§$Òü”$á?½ YŒxß$:;Öd¿è3ÈcóÙöRÚWUKÿ=wAü¼þÏç¥&ÒgÅåÒaJŠGÛ§)(γ0¨†«NŽ\ºæl5à¼oÜhzîøi:„€ž$f²LùTRO›ö kNítZbýcñ¼‹oŸŸì­Vf­š©¦¶Ž*"àb2äÊÈe ÈK²¦>,qÉ™e > ´‚%µó&€ “ Ö)Hcc¼ƒ(©Mæ90ý ](þ>¼¿Ìíö²A±”X;oÔ·‚ i?¯¯¹ô7Â(–¹ÅVâLkwdw¶ºo¶9àݱSŒ´P¤tðÔKT{ˆÒªCÂæg ½i3ϾFÊÒ£uTÔAÌÓÖù‹”äY p;åÁ«F­íÔ Lk;5ÐØh7Å©ÜÄ_fÖÑÜ35”^­-°Ævø\—Rv3Á±g0?*ãr:_ú¹ÈjµÕ’Ñâ?mBöµ´+ïot¸`5êáßÏYpÊtšäø ”7ŽÎ•ùÊ2*^/x÷å5ÇÀ¹ÿ'‚V¿AÁøÚf[}ðù7éÄùµ¨G«ö_:·»k½áUÕ7aÖÃ%‚ðÈ4œîJ²ãóØv„éa¬¥€~Ëc¢Ñ—×QJ7y)Ní£ªhx`Õ&S`.kÄx\kAZí*¯kpgO9Rº*3•þyò ½s¾˜Ò0ÐL³p>ÓiÆÆÆP.øé;`)0±{F= |È¿zú<´úEt²¡ž•0pÁŒÄŸ`ðù›ÃÇéÒ”äÀÓz´ÝhÔR³šuPˆD°(yÚ#yÊ'ËLèÙüå`ºS¹®²†±O ^M"k@Ù_½´˜aôwnm.Ž „¸OI­î¾ 度ó4sÜ=”;âJ:¯8§Š>’g£Õk/ýÍȽ›þñÁ2ÚwòßtíüßÑ•.3ø”™<Ûÿ„ ˶ӊ…¤”ø‰´ýÈAÛ)¢8Ðl¤Ä†Êï|‚®˜õA·Q*5p7z‚DM®FÐô[a”ûÞç_—N‰ÀÚ§Ùµ;ìž>o8¡Ð¢"páaSD`;å™$n£aÖéB0*PWΛh§^h£ut>ÙDVm÷Ÿš-tû˜lZœžBa¨º¡ÔG›ù Ü÷;s³iûWÓÏö¡× égÐÖ›§»è“â2ÐpâýÏcwe ­š;ƒÆÇFƒFS¾ÅŒ78Ò“ûŽÒ÷ÀßOøWÃ^`åç{`ë¡¿_6—\°Ï`.lÈ©$Í?Øívrñ5#äÂ3Rõ“Ë‘% K ÷$ ƒúÞ“­\ò —€J©¥œŒËèLñÆA~'ð #¨ ÐÖ³†ZH§Ó€@X*«n ƒ%Í`ŽÛ(5e ôaßëá‚7 mÃ{úù`Û·ÀyWƒ"ãÓþóAöŽÃºTàœgÛá§i×±¿ 7—*×µ8WJeÔ#¢o »ÑcçÞ!^Øo=ƒ|êUÍ=sñ5j \Âô8$]³£µÃ‘=z4™Ö kê;’T÷÷¶SÎ- ­íÔJ6hæóGÄR=(&á¦"³¹{Eö Ân+ÙpÖ…¶#%æ×}´Y½rp£OÁ‘1ÿ 9ø™K™°tyÙ¾Œenðçdo;¼°-{Ò‘‚%]±vƒð™oéÂŦ¿ 7¢ì.ÐÕ4‚ÌæÓÔcÞ­ûcŸ¯Z¶y°[8VW/;¡!ç’% K  Èô›p¤%çV=b1-ùÃ!sϬõyÀñ"ÚÚœWD e¥§ý­[€¾3áz)ï Û>þ} T’rúÖÁÊh›ƒ‚Fé ¼FûüÝùírÅÁXp,夯û¸Ê 4°«P9E^·ž=àð ^iÓ±j5ç`v|·}gµ ôùÐK‰ý§wö´;*C:Ÿ×Á¢ŸFЫÑ.“›íh§qh§nðùÎàSƒþç f‰‹@¹ÈÛ²d DN²¦>r²”KB`*È5óVè&ÐWnÙAgK6Ó'»H—Œ¿fOxˆ¢À ¿P±“vû³ðƒÎ.»ymÚÿKøÿºpí˜áCœóc€¿VpÐß"òùjfßõúxo>´†JO}.[ S{RGvCy$@kß“²Ò¹u W É ™ ÌËZúÞ:ì9”åüñÞSÐÐ{)zvá‰Än(ß9_‰¢TYÕR`°91+|z mÕ ÐßÍ!ì%þqùö“ˆz¾?ŽÈ»¯EVý}¿‘º¾Ù2•Ìæé4.=´­ìÎ2R‚í n§ H]n•ƒVŒ¶zÁÒ"èù²é”ýÐ¥ÇA¨à hTr” ‰ñŒ'iÝŸ÷êÂ@㣠>W¹ÿÆÁ?Ü}XT§¼ÿõ°Søò¦´ñŸ1‘8H§D¼k_Ÿ2N¸}ê@%tôÍ©Ä1ù,YÁ% kêƒËEÞ;È%À²Ö%ü›iF°$ö£îŒ¿ê»ý&¶Ù3Ë…ò´hÆwqÖ±ñ‰ÂµÂ g¨¯#SŠH¯þBÜh­{«û¼O šÍ”èA)°Ã»GAq…Èg0FÎ@/è…ùN»#¾¹o¢½’&f& £C³Ζ¹xƒVŸyàR;=[\)òÔÁ0VNKÀhwÓ„2¸Eôé©#ÐNÕ Úùèv‘h§ýQ€GÏŽ+ÔîGŒÍ¯gÿ>Ÿÿg›Åv4ñìȈún†§ ¯MO@‰ÂŽ3TÂ?þŸy“` ôÀø1b»;¤{èιò9²‹dP?Xž”\Ï%ì#:'yÜá×µ¿èâK¾/‚)*hv*±x½>ÐÕ §ü§TÕŸ Œ¤þß¡lx½:Òp´š€ì~‡¼ÉÚO' õlð"â…”“×™(åéì`B´;FPEÅýxjš .7ë´x>ày `Ï…äQ ´Î&ùÚ©‹@Pˆ†Ñk •ÂŒ7M)®'öÌÈŠ…+Y½p ª…§+:¾î€ð@Ùë¸?U¹;³2,¡ãmŽâûÈ„\ʉŠ­Æ,2jTNJgZÀ?ÿfW  z-eÀ{W©%ôøjÌh<»ézòZ–À`—€ÜKö'(×?$ °Oh+6¤¼eJKœFó'•>ÞõúûÛ èÀÉ·dmý¸%Ķj’â£GÁ¯yxšp·;†ô=pÓ×QÝý` .öðz­÷çÍ–)2¬÷›ÍÓ¨¼âa¸ ÔÐŒŒŠ¯—#û2°×‚ÀÇ28ˆ|ñSo0øäv¥c9%5Ù#±!Pb\ÑN¿PGzxn¤¤¸(2ôìÙß?·S6tâþTçlõÔòyHÌ|ú“°o¸üƒõôÍû.*&'Úäß7Û͘i,‡ö>œ¤… T#r’%0$ù·}8HM¾ÇA!]ÒÂ`Ü^#8åúë^ÛPßèÙðƒž.uxª¾ILˉʢ©àÌûR«Æv\æÕ4"é‡H¦¬¥/,Ûòõ¸nn"½ÂTïOî"#ƒ% Ø» ±ÂÀ!5ZKõ0u8Óº8{øöx  ÛÜHU5·‘3&ÓÒMðÛ ]Ð$r .öÄKõ½Ó&¤vÊn§µ‚2bõ”Uk¡ëÀ18﻽T´Ï£ªšibI™”š–¢§¤øhD‰6‰HÑÜNµ­ áDjð)õI¼æþ”=íðj*„&žÝyÎFÀ®XÌ"èñ%êô ØØIÁG0jM:(·ŒÊ¢11ÑtsNí­ªõ» 庾:º)ƨ“á¡LÎ3è% ƒúAÿå&ÿˆBS•¥¨UÀ-áø`Ùƒî;¨¥Uuùôµ[wÓŠËþD%›àñf=¼bÝ݇àÎÿç¢óJªÐË^¥[?OÅUû|iýEy:ÚÁuã:f$D¡Ö­’Žò÷tÿ˜d#iU h¤ K.=½î@<ßëUQSól*)ý6¢q^BÉz/MI3RB,Œ(¢¡%4™ BS/Ño"–¢<RF'Bî`dLÅd¸kì˜Ln´Ñ¬sµÂ(6œ›É)JJˆFäÝ(ŠAT[|¶Î(EÆö#XÊ>>ã›Ch5ØôÆ™BúþÌÉ´å†eˆ  !RïƒFÓ¶¯Þ¥ŽÕÖ·izªk鋹£èÝå—S fÉ8^@8IÔõ™–qEI8õóÊè+ Èœú¾’´|>“€ÿºà>C gÒ€í¥fËt€3Ÿç…®*d¶VÓKën =´ôvG#·‡^ûôhÀLÐnûøŸO½*Š‘ŒeÏ•n¡µÛ¿Ó’Çg ÖÕu¤ã\7ÞÈÄX£¨7Ó¤{‘òtw-•#|Õƒ³ª‚–̤×ÐÄ$åWÙ©¢êKËxÃáh²Ý½Ì ;Ï劇g›iÔØ|)l$ ‰W{(3JA‰ÑFâbc(ÑBc¢Y jy9B'ËSN‘—€ÔN…‘'h#ÜN h§“’¹Úh<ŒA“štTÈÑd#äÞ2òwùu.ÐŒ¨³Û:ôeY1ÜN ímU´SCDÛ©ôL¸Éö§j(RšlTºóªƒyô ¼ß@a/"é>{ü4ý3ÿ,Æ^bcÚö©Áážq8pW`Ü€öù:úÍõÓ¡SMOŒÆ;¹þ´£ëÉûe ô·dPßßO@¾~¯H@ ´$‚-„±7ˆ4û¢Æ\ŠŽ„&«0äëÚìmµG V€êyE„Ó`Ç;ÚÇu²Zsi<Ü&2häzGšË2á²upÇš<ž¢ÆôöØ7•68©Ö<…,–IÐô‚fz&åU·[û9€“ƧFc^·Î÷$‹m õ¸çZh­€ jaìp%“Ó‘JVÛxYIÅUnJ2¹)1JKFƒ÷ïÓÐÇÂvlL è7F î$#YÔ‡û,BÍÏÒ×NÕ°]hm§QF;åÆ9©´Æéf;%`©ŒÖSEœžšú#™´ÚcuQ5f÷0aÖë‰]QjÀû¶k”ä`ÏJh©`ÔÑá"Œ`cqÜdó¶ëæ“^´S Ú)Þß–vƒÁ'@£¢00Ÿ^É¢‰Ôà3XšnR‘«ÑA1'5CÉ709 ««Ô@ÏõŠƒÍÁ”Ü´^éO»ª³|\–@H@õý!uùš½*I³äô>pÀÆ™ñz*on¤šºˆ ú€^èSÇ¡T¸ÑRŠPò¡{eÊôx5T‹:éÔ^øBdŸK:ßG9f.ƒå¡Qk g­3»¶Q'Á]ÎDeâÀm­´* ¹¾DPQtš2ðã¤7à— @½fòxtd³By³0˜È½()ñm1p³¨°²³çš*Ì>ðõ‚%€“È)Vë¦8`7žµ`ÐnÄ ‡Ÿ|íT´ŽPXüíTJ*+Œi«£ôT`ÙdÐàɆŸDÇX¢¼DDd妮ƒ¿÷’xCø……q†ár'ßÙ«¥SÂFãE;U B.í4šy¦Ü°†ž=ýH·ÓÎúÓ²æfSÙL‡áÊa2oè:Ýu0ÅÏ“ëÅN¦NJnWÒ½„ñˆä¬²•dP?¨—\ÙP% Ä×€ÃÌ3˜gÍ´Z?´ÓÙ1j:SŸB55·PrÒ›¡×e>¦æüí¿—v™/X®‹ã™Ñ¢Ž\_®7ןï#‰?f\{Ä`-=ón]ð0âm™òö €T¤×ØÉ9õp6ÒìJ¡úÆ4òŠhªvÔ©Œ4ÚJ”Q)V)°T*ß †9é^b7ŸQ(7®3ÈaÏm‰‹1 P¸)šdƒ КÚ¤T™á‘ât$n­] pâ§ct=ôòJÆý¸…î÷Ï2€8Ù¹ZQ!6(”f.X.&€xÖ€F$ñà‡A%žå`¥œzO­íT#f’Ø_=·SšÆÉßNµhK} /5:Td¨qSV ÜÖâñ˜ahnÆC¶€vá€A§ ݉w‰‘³’?- qõÔ:h£í.Šn1Âå·-Zé ;Úrvu3Ùq~utèô®c¨‰íFxBùN© ª¥D‹õµS4UØ(„Í‹4#äkAA ÞN£ÐN}oz«vÔŸŽŒ± ?uѸòFʇ—¨H¦Ÿí?Òíâ¸><Ó1wRê>U‘ìO»]1ùDY½,Ô÷²€åâûGÒt1ƒcezh°˜n’c ³£™Ê-“‰ªî¤Ä¤·#®±õŽYCÏ€ÞŒºäÄká5î,QGˆô¹MŒ$ˆä²Ô{à›žµÏìiD€(Þ¯mÖU *¢<´>0å€?ûf Œ=*PU²ÈlÏðèØ2<ÑAÅ«]dPy°¹àz.€³R‡–*+ï…V1àyî÷\·fZ嬃‹u™›gÀkJ,®ç†?kpÕ:Ä}`Ô<–ƒ¾ç}T$øžÇ Š5ñ,{–‹Éˆ`žÛ ? É06’Ï¢µîòV{ ˆçÃ41 ¤ø9 Ùˆvê„á¶ÀO¹Nç 8׉þÜN͘x³¸•d@ ¦H,ÌÒ´¿ÏØÕnÒ‹µ¯ºA )sê‡?µAK¥ÐØ×µb‘í/áÿ…D|³\t+éhŸ¸æš¶í”Û«Ï® åÝ  "ÐVÄKí”g—XV’†¾7Ú)—Éòï¨?%ÌnL(m¤Si1רû…ÂkèÐólˤ±”“žÐkýiÕ‘³Èès È ¾ÏE._°/$àû(òѧñt]²°fºœÐP[©Â:‰œIpY¸°Â¾¨–ÿÌ¡gsº³bU”‹aŒ&}@2rœX¾°lùã/%LÉ‘>Öf‹Ü~› æ8˜œ• X\ÅÚR\é9±¸< ¸õB /=ÀAœX Îq³tØ& @ ?ï øÚ|>—3 ”—j«‡êAÇa ®R2@+0)&µ¦`»e9PaÖàÛbÍtÜ}º\Qpû…@bQ¨[ £1Pað.À‘˜õ`PÏà„7¾{`ˆA=€s‘ÈûÀ<Ó0[‚a0Ãõ—SßH õÝÅŒ Ú˜ïw»vjðµS¤¼ºÑF m•Ð^ÐvD BSÂ-p[b#O-Ú)ÛÚòó l§<Øí3Õ6ÌóXA}¯Ýü ÷0ŇtmX<Ü®Q¨ÇÄÛjðãy&@‹ h°ÖaFÀ„F´ÍI슒“ÝDæÆ™»–vÊíSÐÜN…†íÛ©®ÅÆ 7 ½rÌ^ …F[æ¶.+.á?­Ï¤ãþ”ýô¢Z:“Ç>RUe=SnXC?.ÕDSsRzµ?T½ård DRÔûúßÖ»åßr ]-PKœÀÛ¿C/e攀€4eÍ´ æ;Ät¾›F`jÁÇ-³&QyåCtÕ‡èNâã^p“PEÄ~èÙm%{¹a£X ÔJ¹ñJÊ„†žé&Ð=üîèð¡æ:ßG$ø91°—>Ô X[mf@k1  Þ†ÁË‹ÂìYƒÉซÏ÷½T?.OÒ„«àaGÄ š8ñyL¥àg }"ë6»5‚oÏ?œ¤P4aD oÄŒ@¬^x>ªß<˜Ø£>\7àP13Á@) ¿ZyþÍmÆgÓàX‘~áÜãpÍëk›Ê‹Ú)´ÄÀ«¥ZmÒï6¡’HwÚ)¿|]n×Üæ¹j1c•šO#´êÍ.ÔÇâ¦x_†•ÐF™éÏ3&0y¢Á7‚"Ä@Û¨hsØæ6ÙQ;åûÀ^´k߀Óæ¥èÆ‘ï+ïÑÿ<ðnp;êOK1š ×£õ&ÌÆÁ¹½®–>'°¼Hm³zv[É^nØ(VƒÁÚÄQ4fDRŸô§‘º^.GÆR=p vðXj z©á1êPÞ÷íÌÒètWA­2êºttYQ={.ÃólL7AY†oJ¡×ãýäÅß>Éaû|ÃŒÄÓâ´¬ø`²œ?Ô’Æ™ËçãÑ ›TZ\TcÎ*[.>ê`¶2W\ÝàÓG "¬Ev#š­Á¯˜ †ö9Õà¥Lxî`Þ¶0È„/tö3Íõä7×[à¨B›"$ðÂã!#€\¡©ÇÇš§ôгÔÀäPbp#{‰ßÌ{éœ$ôáe¢þBS޵/T=4ûÐZ:œàB‹²Q>®Ãà>@ŸŸ‹ÛÃ4 Ö®BëɃ”áʇv• ØÅY,Z~¶+ðµT•F šîÁ¯m‡&““TΫTø4õ,_tH{^3çVÚÏyùšrê? HÏ€ƸIí”éRR;åÙ7GË€=Ôvêk7¾þAjR;å¶-µSžµâëèñÄ·ôÜNínA!w€Û©¯Š·•eëÑN±Í “·}³E¾6hÃÁ3D¬q—Ú©¸OÜ«ÔNù7×Û§-Únë T_µS–Yhý)f7,˜ÝàÁžƒzöìãŽàû¤B§ÀmEð+<ž!LQÑèäŠG®˜>ìOûï éðÊÜqñ"c©EþÁ‚¥¨—ŸjÉ’›Œ#gOù¸­_GÇš†ÎÊmÔ{âbŒ ½½‚œÂ–€ÍéôÔ7Z¼M›àëç+òËr—ÓýŒªÖûç矒­; ÉÄ;þ˜²Vš#2pd€ÈÉ÷ÂÔÎFi0mßµÙ© 4ê.·^p_îžýUc `Óýð¶OÑhèI‘y±¬¡çP\l¬Øf@*qc{ócí“Ïð“¬%°ÁO‡Ã§ù”½òa@ÏšPŸŠãÓØ³tø{í/S”çÌ’¶žó°üYóÏ3¬ae^4|Q68Ѿ—o°l€ËçEš ϰÐ M&4í‚B Äô¦)0X’êÀù}K ¯¾eÀ!)n+|œ¯!§!~Ü6•Ðr‹ç-Ú©O[ØN¹ 1T;å²Ð[­V±–h>®n¶SÖÀK \ <™:ƒw]¢}‚z© J3KâýÄ;Õß픟E÷úSP¡`ïªC(-¯)f:AõÓ*(Þ ¦xx?êÏþ4”:÷AKõ¢ –êoPÏ@çæ5wý»WÇÄÅÿ à+}ÒèÞ9“GÓÔÜ £(HN=‘€ YžŽ”ÐÞ¼³©Ç Š­LU~óÁ'~úð?óóOP¸OåÚ“« Ðs}@FwЖ{M>DϘ?<}Í” ‡ÜNFƒÄz kë81¸ìn’À¡B¥;_“Á»àÐCSÏzA¿ñké}`»»× õ<®Ÿ´ðÇZ{PatZ€w j$¸öoxû ÞR9¬aäûå²Á4׉Ïe°ÄšOôF#¨h—vG °Ç ¡Ñ\í'è4º¤6²÷—ß̹¾ì@Û΃ ñ,%-} טåÌ`Ÿó Ï7-€ýÿ³wðQUYÿLzï H¡„z/ÒED R\{Ç]×uu‹m 6ÔÝÕU÷sVݰ¢ "Ò”Þ{ %$„BBBz/3óýÏÜÉË0If2“™Ix'¿—÷æ½ûî=÷ÜûÞûßsÏ=‡óâ~aO†žÌßRÙ©é+ te?e͵7À"ú”R;o4¿ÁyžÒ™ôSæZ´}}?5ô!C_åóL<%@=ž`m”Ðs¿@IDAT=>ø<÷ãœò ÑOÃÑ¿”ïÙDÈOÙO™gž…óÂ@SÌ&¡Ÿò¾=õS®?×êûTtWú§b©¶ov¥œê划Ë÷ºçégããëÿRç¨ý¼i£¨g|Œª*³sÅàˆFõïΛæÌùúbÝžè¬ÜÂ~þÅ?~¸ð¯o£8ö­G°væ×žÙñ‡ˆµdLЉ,>®¼øLÚK-Ÿø°+?Ö­åG–×F ~¦YKǦ6l›ÊÚ:ÞøƒÏ|JpÑÚ2­½O D |b €5ƒ0½a±¬ôRFœ“Ì@³L@>_ã¹,ÜÁ>Ù¿7ߘí§âY@Þ ‚yË϶qÊï :…†¾õS®—ú>u™GFÅRn WÇRÎõ²r:ﻞüÓô/ïÛMïÌqn^ìŽ@¥6•MôÌý7¹/Yµ]¿ÿdú?znæ£×ü…vH`/?âü!Ç |ÌTi›Ël69Qš—HÀÚe}=Æmõwþ¨s¹ì*‘AƒX(‡s|ž?þüÁdEˆ€&õ œX È ûÆz)#NÏ|ËMÖƒ“¬çáU?Àõ–`¾a0!pu­ƒñbLL”(qÞLÆ<î¥TjëܳüȯIœFÞcÈAýßQ$ÀíÊÄí¯ì—mÑOÙ”ËÐW &h<€ð@ôV.7:&Òª~*Á½è¯âÐ0K ŸWo#æ“対OÞR*–rr¸"–r¨ç·1zŸ[˜ƒ_@Ð_Ð?cº—×e¿0ÝË뜕²Ÿj1ஃ©²Ÿzc PKý” CŸlÜOE¿mǃN)sÞꢾOeÿqà^ÅRvSE¹–r4¨çNÈezÇÇ÷ ‹êûÏ.Ñ¡BCß”ÀÔóm+ÌŽh²ó‹tÙyšÿÜpÃ]}Ö¬ù´ %vh{ÇÈ`÷ͶØÌMM ‹i•ž]ZÛ4ðGO~ÜùÏ^6Ü‹ãvôa—õ‘{kåÂ÷ñf¬ýg{xÃ,Ë›7_ö¢ë Ló—÷˽A®œ_ãÓûÔßW—¸0É}kjÏ}«©~ʃO˜AEOÁ î±W’ìŸÊ½!¿†~*¯)ïkÇÊgP(,Ô÷©£šQÅRŽ’´…å¸ –r$¨ç7-«S8òïè3æãUÍ6ôªÉ…½¦ ’±ìï¸~”û›K×vŠê×í ZCC1ü•jü¥jƒ²•%P¥öÌ`²ÁQV 6äü–ógúÁ¶†g.‡I~Ày/?îòc(ÓX“oGHËõæ6$eîåͶÒX(‹5M‘”'_¿Zå×”lÔóö•@SýÔ3|è¨Â„®©¯–~*eÄï4õ}ÚTo°ëyKÙUœöÉÌU°”#A½qd ú‡‡=Ø?1V¯.еO‡²%¶ ã¶HNËzù¼…5õÒ G)'ùÑå“–2-€^æÁeH’åÉßê¾AR6,.>Vþ†Tê‘*çJ ¡ŸŠŽªöSEsHÙ¨ïS…PÚæPÅRm#W›su,e@36W¥Å ”#KŸñÓoOwÜV6 ž³P´¥¸-b£ç<òÄH”Ã*Ô«®mø£Ä$¹Ìc”þ¡­?–yñžóW©y øFQ€otó‰Ô«ªœ,H,ôŽÂ¦RÓPß§MËÆ†+*–²AxޏÕÙXÊQšzÙy¬od—ØIxàõì‡ÞBVËhYh z¿ÀÀëz/¶:lÖ§e‰¨)œ!™ãÞuF±j™ª¬’Àë£[•^M¬JÀNP±”ÙVÙ8K9JSÏåH{zO/ï.¾Þzö÷©’kH€Û‚£÷BMPßVŽê®!• —€î*ySI•€+K@3SgÞ\¹‰:*o*–rñ–u6–rÔדËáY^$ëíîéìß¡µôÕp‘ØÞ($ÈA5À7·•£úF{“ʯ*UªT ¨p†®:,UƒHï&¦œ!w«Êt&–r„ù ƒw9e$€=üJûùxzº ¨á½o(¿¨T4ƒâ¢Ãè–‰C){¦óÙù´ðãUôüC3)>&\œãyÿØ3žfOÅP-üœ¹n/8›E%å ¢¸Þ2i(­Üx€Öí:&ÒñH.¡S8Í6’:E„ˆs®ðÛ¨ð"mê¹TWh•UªT ¨¸š%àòXJ‰s|á!*:<˜&ëMc&ÛñÖˆ¤®tëäaÆsßo>H»Ž¥ÑkOÌ1žÛzð4m?”B™— m݃ºuޤGoŸDy¥qBö¶H×@£ô0Þëìgb)G€z–/.(ò†22ÒÅèšA=iÊÈ$*(.§ÍNÑ«­BÔÕÔÉbO)®ÜBç.æÓ/¦¦¸˜0ʹ\ÔèV0<1ï:ÊÊ+¤ÕÛÓª­‡èÑY“¥qú½q¦jêÞW%gE¥Ã‚º_}•WkÜn$p®´\ðÚ5пÝð¬2Ú!$àòXJâœòªj:yö"-[½“.å7ñ-µÄ®£©ôùÚÝtÓ„Áôðm“¨ ßÓ/æ$=|ÛDò÷õ¦‡Ï í.Ò;àŸ—mº9 K9ÔËÑ%ï°þu9PèïC]¢BÅ÷ŽôʇßÑ7? ßÝ3Ý¢’v!—ޤdÒÝ3ÆÒ`t.¦phê•äÈ—²ŒŠÊjZ¾aU¢³*;ª2½3Ž18Q¶«šzg4ÄUZ憽5Ÿ7uÙU*µÚíA¯Jl.šÀÎÂTR%à (¿Í.‹¥$Îa‰ô‚»lŽÎ¼fûQš4¬/1à·„Vn:@}ºv¢Ç 2&‡uƒ’¢Â‚„åDlTí?‘NOe4šP¦uƱ³°”#@=ËSÙ]^Ì럆ôI õõæ2²COË¢Œ8%UÕ°ƒÏ)IÝ;ËSÍî3`ÒÃ.¿¼ÏI¶— ²¦²Ô‘% ÕÕtäê©uë ¨Aôc•T 8AòÛÌ{—ÇR,Ÿa}»Ò[SfnÔ_Ì+¢½É†YYN“…ß’ŠË*¨¤¬’¦Žê'O5»ÏÈÉ×}¼´øÍÞàØ‹²½Vª£@=WHVŽ÷.OðC5µZ¡I—Ì®ÛyŒÜëñ¹Êêjy‰röƒü„½ñ¤ÉAiE}üíV:—G¹° »iü`8›qIq¸$S&âTªP% J@•€*«Mí K¢ƒ3X—t"ý"¥fæÊŸTƒõˆŒ¹˜.å—ˆ}Ø(±oê›/WTÖÀäù2±@Ï.M%uæy‡c)G‚z…`]Ϧ^Áœ8,.­ _¯F¦1lŠcºPVÞ󊪖´Œz15uT,°¥ÕSŠOÝ«P% J@•€*UVIÀõ±T°“Òy*Ö/š[(Ëé‚ü ƒìͯcdv8’Ð)¢¹¤WÕ5G€z‡TlmA­VG{’Ó)±…‘¢²œ.‘aÂûÍ©sÙ¢£)¯Éã@?_ºoæ8ù³=ìÛ]Ûµ¡ª<ªP% J@•€*+%Ð.¿Ç»Žž!/OwxllßTÝÃCü…·›c©™Ð¾Ç6•Œn†‡B¥’µÉ„®qÁamçPÏ"å )7׳‚‹’ò*1“×–?ïI&ÖÔ?1oª"Eó‡ƒ{ÇÁ}S-Yµî€÷›X,º-‚]˜¶—‰qí)ì}£…²ÍWºƒ]å‘?o’”Çòœµ{^7!‰•¿åyWÙ·Eý­­›Ng¿V«µöV›Ò+ÛÅÕÛɦŠv€›]¡ŸêëmêÕ~Út‡j‹vRŸÓF8ªáãÒt38üJNK™XcX üs$%ƒ¶Á-åmS†Q€ŸeÁF==¡üèðÀ‹Á$Ç#8 [?Ž1P\^AÕ¼ºã[_ßúW.ûØ öçõþ”Ô½‹ˆQà†µÎm^ë%Ùw½•³¶w·ò–úvâ\¢…ˆµ1þÔÛªs„ÓÛ©•µê0·¹b?õ¯wiùÖùBÇÉÙÅûi›·“‹×ßqÁµK**©€Kðï|)~ê›=ÅèÐRÎoš8j`ˆí³rã~¡éOŒ‹ig ÞÒÚÚ7 ê!ÏW¿½Y©ò"Œ^¸ÿŠ4/ý²ñ}aAþôÄS…¶—]Ur)I äÙ̪ºº–62âK§J‚‚‡Ëó`K€øñØ»Ã^Ø^ä*õÐc wAP·rʸ´“.$Û«Šmž²­¾ÝLåz²Ù”ýÚ©Í+áâ˜öÓu{NúS-wX`*ÞÚ5–:Áýpfõöä]¦S…†`8öÈ»­óàË«ÏÅúvó¡6é§®ÒNædéˆú›+W=§JÀU$ ê>\¥%T>ÚDüb“ÖЗ”Ñ÷Û “Öл 1/žž¹ôõOû¨Q‡™_æÛäJõ˜8~…ÇRhP7š5é?të„÷­®¢››'=xÓz«ï³× ²­>_·‹ªªjíÖNö⯽æcÚO¿Ûv”*½Ü…†ÞÑuº­[ˆŠ xxAzgìpzstÃ>Kyñ„YÌ7Ó&XšÜîéxf£òûlÝ»öSWj§æ„ÖVõo®Lõš*W€ ê]¡TÚDõŽŸŽåa¢Š1áhÚ¨WéÚᥰ î✿o]7â%ê1T ºD£é£RDH"Pÿî½OÙ"+Kï•mU\VM÷Ÿ°K;YZvGMg®Ÿ–#šdjd€ÓLn¶gçÒËÓS;е±1êí%ÄŸLÚŸþ8(‰º*"‚ÿyØêH/DwôH ?#MwxC{sÌ0º9ÁñáëYcÍò+¯p{õSWl§¦ž‰¶¨Se©çU ¸’TPïJ­¡òbW ´J;úrøßÓörãˆE±ÖV„ybÞv;#fêêêlÖ»rýcÂPiEUVQTh_ºóº/)¿î qî¡›Ö‘¿o$yzø#ðÈÝtÝÈ—(óÒnÄȤüâ3Ò5tàÔÇt!wŸµb¶KzÙV[á Õíd¦Úq&¦ýt_ÊáåÆÅöˆÏ…B ¸¦–z#ÎÇGSfüøÜ7ÓÆSD½Ûâ‰1Qôî5ÃÄù#ù…tij´zúôL:¼ì@_öŠ~ÀòcoAìÔýÔ•ÛIQm㡽ëoÌX=èðà8C¬XkÔnlêWn< ̺v8MÝß(ë°Ò?‘Êîš1Öxîj<(.«A¯8"îßžœw5Š QZ%¸®¬ªª¦ô¬<H­p[Ù(¡ ýð;Byù‰t¼öìÚYØk·v1¦«ÖdÒ£4ªß/©¢ê2<-Äl„Žz'ÜHg/n¥§?­Ñ3~õ‰ŸAi7cFÅq^£s9;ĵܢSìÔQfî^§¶œl+îW‰ñ1ê¢ÙV¶†¹~Z…عVþkeÑÍÞv_¯îô`ï”u8oEŸÃ¬Ùu±hÇ¥Ëôyê9qïä.Ñâœü½&ó"ýßñq-ÔÛ›´èÛoAV.7ЇB°pÖÖ~êªí$ëÙÔÞ^õo*ÿöxžƒ,.üxuëAÏÜ“± û’ÏÒgkwÓ?w§ñÜÕvpáR}“µ´ ¹ÄA#ÀÝô=7Ž…Én»ÊÔ~8Eïrww£Õ´1º B° • 8 `±ƒ›öÔñÚºí !-ÈÖ Ó›4ȈK±zW%æyþ?Ûú…µ]c®¡‹ùGÐÎeæŠ4žcÞ˜ÇB„ g¾}¼ fY[o-µ¶þÖ–cmz6¯Qz¾?+w?JšH»1¨¿7ÅE¤íÇÞ6›uEuÌr|1à hQžf3°ÓIÙVùÅe¢Ÿ±¼[ÓNvb§Ýfc®Ÿ²ïqg–bó% g†YÍýÐàGûù—ƆF†Òû'Ϙ•}!ÖÅøÀ3’?4|åð¼å,²¶öÓÖ¶¯Aørªá[]£ÓR&ÞÃJ¥UY­É(,b>^Xd‘\íUÿV3ë¢7òr­IÃû ,5¤w‚À!¦¬6…¥x€ÇX‡­&~Ü~ß« `šˆˆÆKVo§âÒJžÔU\çȰL}7m"-ž8’–¦œ£“…æß)|žmé¾ñZzfp?§Š¿ÚCcs?µµþ°û=¾}?).ÁBãäçÑ:}"{zgì0Šöõ±X¦ö¨¿Å…µ£„ÓÇôë¶$F2e½),Åéë°™ c– ÀÛ°nãíÏÖÒè‰tí¨$úŠÕ³0aa*èÿdÕv7¤'=6g ”8…´é¹O5…¥J=úû-‡(w@1Z\†ˆÉÀILÍa)6›á²âc"€³ÆSÿž±Ð7ŃȰ‰0Û=sœî± ³ÆM$u©Ó­{²œT…:,\ŒQ%kÅ¿\¿‡ž} ÁL²4p&nDž6YµõP#/"7O"Î×Akp#0ÖäîOþÞôÖÒµ”s¹£Â`:žš ÀKÑábãsOeÐõcˆüyêjì žB“$~ó€ƒix߮輆©šä³(šù¡Å2qÀNwZü.Q¡”œ–%òØs<†õíF3À‹’ZâA™V=n˘7ÖÔó1ö&S[ Mª¦¦!Q GeéX枤¾sfÂSË t,ík î/- ©SØ@*­Ì¡í‡ß¢“«Dn×zr Sp@< îy'}»å1ºfÐo…VùS?‡m-½¿bl³%3µ5¾™YkL;ä=¶Ô¿Y&[yñÓu³›¸SOkvý^q^À‹žµš†g©¨4ƒ^_Å=Ÿo¸ÃéšzfÊMS¾U'ú™”¹5ítEÅ®²Rf¦ýTëd3¦û6í2ÛÜ+ÿºÿ(½¯8u˜™1ôRCÒ©«¾âž‡·ìvº¦ž™byÚÒOíÑNeåb”P?=®3Ö"ÄÐwç.7”]¿Ø—¦â7¯­Í̦7ž¤ZÈwrçhb÷¢ËÏfгíÈÉ£^X¬Ì³NEµxÇ_¿zãr7=akýMóë(¿==Ýé–IÃè ØÑÒ›`&Шj-a)ÖγB‘]0oØLIݺД}El q:#‡zb½Ñ±Ô B‰*qPÿÄX:,5}¬y,%™¢•g˜J `þógÙ‡‰MgÜÜô´ –M9s?g¾yãz´†ìUÿÖ”ÝÚ{tº{å–òhÉ”©¥ûíu]¶UkÛÉ^|´×|Úc?eÀi)9ÓôFÉ£­ýÔ^í”,Ø*‡²…é7ý{ÓŒøÎ´ødªø=?)‘ª p{‹“}øÇDGPŸ Z}þ"m¾˜K©ð*4,"œþyì]„6×R²µþ––ÓÞÒ1`e³dV’š‚צ°›3u©ÇR¼žÈ ‹J»D‡«ï¯Pð‘‰×s°)ÌkÌZÅÉzØfKy{ÖÓt‚"UR,” ¥hož!ð®3‡¥8múG<ø2Ÿ4˃,C¹gËŒ³Y—±øFa>­¼æêÇí ÔKa2pfû-îtÝ»4Ldd_öñOÞ9Ø.kÓ¾è¬ð’¡Àý<:U’»æÊ©^¶ã)žIÃúÐmS†)“‹ã®°³zþÁ›øÏÒ²wQèv?ºu²!Ý%hú%唈Eüûç='(3'Ÿ^ùÕla¿ö%kD2ñ@€'6íQRK<(ÓªÇ%À/pÃ-fwt¬ñnœ¤Å_ãþíL# Àžza#ÀùLÆÓçæQFÎ.¡¡ïw= è1W¤áLÙLgÙ·³0(e\*4¬qÈÎ?Š’áÃÕRáà^ðÍãYk4Àò¾¿µõo‰Gõz½D_k];]í2Tû©{€ ýÔíôxR/ Âú±aÁXP\L[ЙnIˆ%^”üIÊYñ»k`Ý–'@=Ÿàu ðD&¨èÄ>y°ÛP‹É†ú[\F;LÈà÷ŽëGã‘°àSK°”TdÊj»»5ÆVò¼b<„ÂÜù•Ço—§Œ{sXê6¬5dº”߀¥.Kù"6 bOˆæ°ß³¬“9 NøSs<R4üçõ’¬0þÝ=ÓEy WÚÇÑ•ˆ¶}ð ;÷AB´ÞfÙ.(®Üsʇ‰ËN,Îd-¢ÃMC`’³'9NÃ|†µælÆÀ›µî;‘.| ì/Ý»>0 —uäÌyŒðò„í>kéû`pÁTXR=~b1Ûö³Æ^@bvG´\Î,˜ejŠyŸºoZÆÐ~üÛÒ`°—7ŠKÖÜJÿûñFô ô5ØÅ_‚IޤK…'(Ø¿‹ü)ì½% 7ž´ö€Á<ÿ‰‘u|ˢ作Okê/óQ÷ÍK€[Gʺù”êUs²Sû©9éØïœ­ýÔÖvrzÜw woÜEó~Ú.ÌkØ>>@?¥¸ÔXÑSEÅó ¾&i+Ìnl%[ëokù®|?¯áç;7,ú¶'–Ô+N˜³}5Ì9 Zj楱T0ÓŽÃ)” ç(;²{%D G"Ía)Æ[Ep4±C  Õß},M8iŠÓváòVnÜO7صÂZƒ-6ÚµKM= ØÓ;·L*VIKHì"ìå_\ô-®{]q]¦³dÏÞgئêíÏÖ‹6Ûi=6{ ¼äøÐp«ùßï¶Â ‡»˜)˜„…±’xæàÝÏ×ÃãJ­°ýg/7L‡÷¡ÿ}¿žüû2JèASGöƒýµáe6æD­ÜBï ,¦ÑzР^ñÂŽ9"ìÃY0Ùþ?DVz®ÚÖ#¿b±f!l¼™Â»SVõÁúãnð§¾Ió¿º:˧„7™ØëdKýMXR6#–³J­—€ÚO[/;kî´µŸÚÒNÿJ>}Å¢b6cº§]4Ä 8f“%¥‰S5fZíA¶Öß<¸j·Ãvý(’’쉥Ø6Ÿ±/|]¾a¯æìI×6‡¥x°±iÿIà°X§æ'œ0Ía)ö^ÈNT؊㛟ö ¼ÆÞ}šâA:%‘õf?ü:ò$ö¼p¿â—k¶PoÎ †½Ìð&‰MYž†×›jx‘vYòz#WE¸µãʆê ÷dÊß Þ9çZa¾P Ï)þ˜Ö‘vZ«ý¹A›ËvûLò…ÁîšîêkæÝUJâÎÃÓO ö•çùzxpý¶[<Še­* ˜šãA$0ùwžð¦RÛH€µòå°—Õï1Útp!õˆŒ6Š¡Ô W.’“–¤£oè`ç7ŠÊp‰íñù·«Rp@õŠ»ÏO]¼|¾ŒwŠYЦø½qì›TYS„Pô/7•DœÐcìÏÂ%ífÓÙr±'øž2ìÏäçF®ºî ×™¶ä­ÞëZèŸñ“;GQ—…«IöJ£‚¦Ü¾ÇÍJY7ŸJ½jNRv†½¹WžN•ðÆî%•4¡S Ž¥w¢F3ð.…)»Ndò†‚æ…!ý)Ê“%)éW˜ƒ(óâã»zv£œŠJâò¢0SËî×bÁæPh‰è݃ 0GEGÒs{c–@K“ ¿)¾ Ê/ ï\‰£ÏrgŠJh4üfcVá$‚& ÄbQ/áADOŸ¦ž£G“z{…9WZF+Ò3éÒ3uòó¥[ºÆRÿÐÚq)>GZ|5ƒ æ‡/ˆ´–ü“²¶$­iy¯5íÄyð¢ØË^OL2æ€TOï: lèÙî^ijËcåYåm<xÞoø½Ve…iެƒ2/õØ5%ÀÁ@-ýn¹f œÇU‡ÔÔ;Cœìdás„}g”¯–éX \*Z¸k뤨Îv{û¶¬åùK{0ûäE·Nø&a‰Æ¢Â»d7ø×ï1„¢Bû¯÷í:“ØËO!Ìkî¿áÌ8¯™;ˆG¡™×¼-ÒŸËÙ· ÿ‡©ÖHh×sÄ`áü¥ÝtàÔÇ0E«¥™ãÞ¡Ä8xúÓÿ×Ô7áF‘%çqËø÷)À'’R2×PvÁ1,¼ª@À“tæÂ˜ÇUÀ·ñÚrè äS'|cTXÝ5m9Se`†e­p?Ê‹¢¼q=UÀ<*ƒ†¹p[àggŽõœs%°?Q…±òÍ1è{P€‘™øµnü= <”zC›/醸N0g`!Ý×b@éÙ`)Ó(÷ý¾ŽLpG¼;÷2ýmôŠÀZ.Ž6Ëfûó.Ó§gÒ©ætwañHŸDú ªú‡…Гúˆ¬8ŒJÞÞôó…J†³… H¶eçÑFx€áãløà~çøiø¾×ÓóCû‹ûà.Ÿ:žÂ`¢³â\&"±–c€«¡o¦N ˜#üœ•Cÿ7‚"î;±é”зT'ÔYè[ÊO½îZ¸{ÆXºçƆoŽkqçÚܨ ÞµÛGåN•€Ã$Àöþÿù~ ‚dÕÐÃ37X±5ƒ#g>‡oþw„ÙKW XÞ;á±™«À…¼ýÄÚt6M*«È¦9Ee空® >3àEh¯`tïÓ˜]Aiý¸ëˆ±UÌhµ5t±.¥ˆ4)0‹ ì*flb1Ó Ö¿WÜthæ·Ò^Ì($§ mÿiQ¶ܱñ™ÐÀ”—g< S\F ¨oY¿EhÇWL›`qÄV |“~ž>8q†.˜Bù~à‰7stZwî_¬_Æ=ॿP^Ae˜à=kÊÙ¦ûÚ.1”UQA}C…ÿú.ç<Ó¡ÿ fvBÛΑfkàf7­´”ÒJ Ž6ž<kXE0w€w¶'žB+½~;lχa‚—p±’?é/#âåõMðm®.ê9Uª:¾TPoE³ËÉ?¼ý¥¸ƒ],-üx•w«IU ¸¾*ªòiÕ¶ßЧëç›Ùt‚FœI£’¨'6[QRu­œð¹ªšRø>ýtŸC¼™£üâ4ãé:]€Š‡ñ·<ðõý¥@“!ˆÉ¹ì-t ¦:’²/“‡Wìûu»•˜±æ41h0ø-æÙ´ ÿÎÐÈ76Yòëm~ƒ‡C)ŸP6̰Tr] °¶úY˜½<¸y7±™Í€s&¬h22®p5Ì'Ëêñ1›å„Âf(€òÍð•Λ9J‡[=I‰·9b³˜ºz{¬òrúàdŠ‘“&1H”÷ßs¯0kSž`â:Ä ¿b8NPR4ðb<`¤/ÒÎÁ¼¥q|ãEõ@•€ K fS¿¶D¸ð¾ ÷ã|Ì bU²]W~ImÏ³ÃæÀ6ẏ‰ÙSJª:Š"CûÀ;O)ÌV²¨N[ –JqœpÒf5ä¯8]`~“WxÊXí8xöas_ïPŠ é%lÔ‹Ë2a_ÿ‘1é??樀ÛÏ×P®´öîÕx³)—xR˜ãÀ¬&Q)„…R ƒ$Ö²³¹JÀ|"Òí„ßô,hۗ¾¾)2ßKwÑÂ}L_ØÞ>1(3™‚Ÿ”£m¢+Ëâ‚u²é¿§Óè—ýz/±WöØ3æDGàݧ³¿¯ð ³`øäy†ÀÞÞØ$G%UíQÿ‡Ià*KÙ­ UP_/Êä´ ´íЊE¨c_Ø?F‡Qÿ]„ ¦ˆL›“_†§hÙ-9HA€ª^];ÑdD eWPÜI§f!ðA*@'Ý0n C¬`Ø|¦dä`DZM׎êGñ1ávkH5#U¶J vò“†<‹¤aTS˜-‡^‡[°<:—½ƒ².¢_ß¾O¸å<›µ¥QQ¸>óšw`gßW˜à0 o-Mû’nýwœø ¸¥œJ?x‰Æö‚ž˜½¦ž´åðßèPʲ³?šöÍówê%g|AŠA švq3ÅžM÷ÁößÏ'”ÖíyŸùŒÖîzF¸Ãä…²n(çËw·¸à·E&Ôm"¾!ô[ج‡@Ï&0lΦ(ìÖòhAý|ÓµTP]M;L‚ñõ×`#ß;8H€¾µ ®ÄâÛ—† ¤ÙÝâè¶õ[±ˆ5ƒ~…¨©«§O"ØûóÂV^@Û}‹|^D>×Åv‹B¥8þ÷› † €~=€?ÏL,ØŒþw˜Ñ4p9ó·ímqÁoK<¨×U ØS,Šq8“ºwId)±_zÖÎï<’ e™4Á®L£¤Ÿ€%Ä‹êL“†Ãå+0qtÛ]¸¯÷ñCzS,üÛ»ùbùÄÑöC)pOÞ‰®ÜÎ+ZöÂfÏzºj^*¨GËpÔØÿ øÓí׎€=o$ì!Eõ‹¾ÙïI£&Òº]§ä¹³uïIcõ¤Ï×îÑe‡õíJ÷¤ÍûOÑMÃ/="‘b*õ¢ËK=B³®†EzôÚÇ?Ð[Oÿ¬ KWí,*_[ÇÏ~C¼±¦šµô’xÁê—?Ý…+L Lûþ°ýI™ `ØC,J5žP|ºn¶ñ—ò>ùÁÊñÆk)ç× ¿ÿ à™T|½éAðäÓø¤ÆÆdšŸ{oÅHÞ J…Fþ__LJÅ]øÙß´ÿqžÍþ¼â ¼ŠA‚‡±žÇÓWo¼È—A»r,ú*^µ;ö,ÃkªYK/‰[>ºuù@Ù"Á±¼¦ØX;Á‹[ÍÑ{Ɇõ|MyÿžñãfÞ b;÷­Xèêé®1òðâ£b/»Ï”|™æÁ7O]ý³!ügÿìS²ˆùSáGŸmùyã½¼Ž€éøÏçϕÜHõÑ.Ä¢þs ÔÔjéÕVѵ#ûÂ)A}¾n”¡§?n?JOž£™‡ˆ½élí.˜4GÜ¡õ»Ž‹˜=wLM.ЛK×Ò͸§{\‚PùÂijVDš­¨ª¸ìÇí‡=¶œnGÀP•ˆTP^pâìEJB0ªñCz‰>Á£I&u6+W$àègPêëŸö‹kü#ÊΞ:`FC‡OÁ›´ð êŸ>/Fš£úw7¦åƒ^ 14np}É|ê\ éß(ú£-$W’0'1¹ZX„»›uí<¡¢7ZxGÇH¦ôÊ™zå5>fÀlâ|Lóbo6Ö»¾Ôb3G<8åu^,¬Rû€ΦܚzÓëMzÓt-ýæ|fú"9óZóã…&³c3æLu$ Wf`îœòº«B€ãpëÉQcI:xR©í%r>þã=è¶)ÃEa©™¹T ÅtâlMÖ[`¤®€u_I3ÆJRPõó^ƒûaÆRÐs*,$åÕ¯#¹iü•–§œþv™à*ß« eÑJàRŒLI Áh© —øe„ L!~b/ÿE† @Ï¿=== 4¼Í °¸*Ðß[&3î½FT(«_%Ï©{{J@ˆ¨‡h÷ñÓ·Û~ëMBºw™H“‡§€†÷ëNá°Ýdà¾7ù4ï½…^)¦¦Ö( ìGN¤Ó ì+«jÉ#W¦TŒb‹azSŠr.æbv “2;õØf è±ÐqLÿŽžYM…yT 缨^Ó¼Kê+JfˆÓG½ó¨0ú嬘±Ù ûëçihï{øè!¼¸¢èü¥]´óø¿Ð–‡`èFݶ6xEøS ˆG ¤Õ¸çOf5ÂW¨žP% J@•@•›J½?~ùC±õá„QT‹Ù‰ëWo¤¤Ð`¸$M¢>!AÄñx­Ùbú3ÖäVTQWØVOB`1öDôìÞCt&²Ö›zð‹]X8ý*‚“G °ë·`R§hÁ“µù©éÍK o×Îôɪí«MX°?HÏ6ÚÆ÷…}=kÞ÷އ–>㊠”ž«äʼnq°ŽØ‹‚å š­'$í?qŽ®ƒÿ òb»z• PA=ä001–Na‘ÆŸþý5@| E„64¹6ð+6 ¶¢Ñý{XÔo&c‘Çw›Ð3ï~³=÷ÀM⾘ˆúøûmèðt#¦šxР’­0yŽ6ÊÞNÊ+s¢‰êž}Qû('2oŠaŽƒ".:_éÃÑ;žÆ¢Á bIùE, }Šá!fÒçhÜ€§è«÷Š,‚ýciêðiûÑ·0óƒÅ;ž ³7#êsE¨çT ¨P%pUH€kýx>¾öÃ韈{&LùXмÚsŽËyÿ8¸/­ÆšŽÆË.Iç"zîÿRÎÒ«“é7zcëC¿ßuÐ&™Õá± kxãÙƒñ0°¢É*À·I®|sHŸ°™kÙZ,\õ@¼’`cžSFô¥¯Öï¡§ßü\(<ƒ[>*úa]cjfœXƒÈ–¿˜>†út58,a˜Ïýß׈$lX©¸ïj>TA=ZŸMaî›9Žî¼a4ìä=èÿ­6ö‰Á½°Ê:@Q‡`8n4ïz, ]ÛxÞ$)£ŸE…Ò#³&aÁ]㹉û8]§ˆ šsÝ(a¦ÃçUj­ôt!w?ÊøÁäÍåÄÞMºF^wƒèx¦?Üà7—Ìì¹2D7-…óÚà^Ðþï1¦á˜LìÂñ|ÎN?øwBK/œO‡ˆ‰©{çI”}U ^¯÷Æâ;7ွ˜zå´´—÷öXƒî¦Ózµt›zÝ p[yA)emÅ­jÒz HÙ)û©»Vµ¶gñ ¶µŸÚÚN¼ÖàTQ±¨V2p-)Ç— ‰_væùá»»9û=Þ¯Ž §õˆ¾ËľûåâaŽæû@oËkâæú\ÿê5¼oÚ{ìTxQ‘Ä|ñ¢gÞ$ÀŸ€¯j𥄬ßß8n]?¦?bŒ¸Ó·›Às a¯K|êîépNP'p–2ç^¸ßø“×"ÊõˆlbØiÖ”7ñ@AÚÔßpÍ@ºeòP±Œ7«êBYe`@oŽØÌ†½µ¤´¡WÞ«z¥4,=¶ȇ‹¨¡ QÓ(Ø·/LpŠéRN.¹˜I%• ZKK5M7qè³"Ú(kþµðŸÎ{ÞxQ&G4•”WtZ„Áò§E{­6nPyV¡øƒj ™Kïþkú[çe¯5çÂT'¸ÆÜF5åYfpÏ;1ÍzfM»Åôëv›ˆN›…´§0£$sé•×Ûú˜ÛŠí9%™“»¼¦î¯”€9yq?õ®rì"JÉÙô¸Îˆ0L' ŠÀ¬PxÖ‘ix?Úãt˜ŠìÏË7ž¾)¾‹0%aŸòìwžÉ¾[»Æ¦ÚŸø{áRÓÞj¼êô6õÓ¶l§‰£è‘>‰Ô5 f5°•y!º³¤´zðÏ¿OÃ.> Ï[g꺨æ2mS{®ÖÅÍNìJ¿  Ë"¶0Ög^Ä@£aa¼)À×){U¦äÚÜyôMQS8«©ô|žñ—9 ¦b©+¥fÅ^™îª:3î+c£ÃìZç½âÅt”]3íð™Yä{ÃyŸ„›(>z4@#Q%4<%Ï.É#~­Þ3.>ÐZ¦~å5K÷1áiL¿_Ñ?Ý €º¶õÐÔ/âöÍbXpƒ&)4°å¶4{Á›VçGþ^ö,íQ‹+¢H8kâ"ÌXUP^ñ)ø²›¶þIýB‘‚„É›Çý‹ºuG;ýŸÔìû]3è·t4•}Îÿƒ`áSžÝNšKß(Ó6þÁýˆÛ*ÈïÊñm\t‡Îžû©‡®<Ë8p6ó‘>=hJ—Ú’K÷öê›ë¨+\Z²É7F ¡ÑÑáôáÉ4#¨¿¯Wwz,)‘V¦Ãwýˆ„Ø$ËÏž§·Æ #œ))¡×Hê_p—¹Á©I,G€X{÷S{´«*Øž~WÎeºwÓ. ƒ¹Í¦™S‰§+b»HJÀ1˱ÂÁ„¹úÇùûc ÑCl™ˆþ»633øõ^U¸<ø›²`¢ƒ5ø*À—­`Ýž=þu††ÞžäÖÒ³÷A•®”€ýÑÕe´»3à+ÕÞÄ®.UjYXT¬ñ÷Ïð­£ð3þ'a#oî.?ŸpRy^¨*Ij‚YÃ$·0O:›ÿÑ•½ÉßÿˆLÚì¾ 8•†öºGh™ë´ˆXéßY¤g³œà€8Ðcnýý šô^±ÓŒìGò÷‹Zú£õ¦8ÍT‘yÓë">ÔßÈ·%÷5•FÖ÷­©SùZsþëMüb¥² ÚlÆ ~`9”ƒšÆÚÁ½î¢ {ÿ"|ççžDÀ’‡¨o*½5|ÙšV¶UBL¨h+[ó»šï¿²ŸVRhY-å9nÀôßÓéôá©41Aæ¹ïäGâpΔÿÉSbO¥iâørÉ ÃÌSSéÅEýã¶òõr£¨c_sPѪsý”pQ¥UõJ >,2ŒŽÁÇ”–¦¤‹SïÑèR,Üó¥×›Ž¤•–k”™XÛ+i(€,M,Go¼Tlí§öh§bx.ù"-ƒžÒžÚŸÆ·þ{:ìÓæc¦ãË´ót•tðrÝ™Ø^H—¡¡_ˆÁ“5dMý­øojšþ±I½ßëmmIÙ7µÇRVf,[fòŲ†S5­*ûH@õö‘£š‹•Ð븽±ø•±:ÒÍÓéógC™lXÒn’5@^ÞjüAÎvxn°ïã}”Ÿ'e•&RUUùødÈäMîË+/Ó'knF0+¶àŸ­Ÿ ÀýçŽMÉ@IDAT¡”eâ~9S~q ¼åužî£<æ]÷Ka#/³2MKǬv`6ºPýy`tç@OÊ-¯¥ü™Ô)f@£‰ßË&2­ªn¬=’€Þ\rCUôý–zÞ zg’¦t»G ~%ï\Ö=ëßšò•÷ŒJzŒzÆ]G< j*Z­2=³)ËÝÏÛ°¶…÷ü[.H6Mï¨ß²­|<5””iìc­m'Gñíªå4ÕO/•k©Gn‰%]ë««<ú—‡¤_ïØGÊšÍeÄþbÄ6 =8S¨——ø-=›”L†}þ[vSSp›Ë¿µ×Ü0Æ`ùyc‚=ú©=ÛÉT5°ùo‰¬ kÏú›øËŽž¢‚Æß-oŒKnÁàä–RmYuÒ’Ö’Æmy Æo• ð[j]õº=%à ×¥=YVójO` ÿú"q »½ AKÚmòê ½¢.y¥Åš/woÒüáÌé_Ÿñ »`:šY뻨Ô,10f0ï…­¯yaº‹¿†jk#)ÿò,EÑö9,©¸H5°½·–òógaE=#|1ƒÀ|z ¾™Ykò”÷8ºþæx•4>‰ï 6]ÒÁC·—!6C§ˆAœhîã¹³Ù[qïp9Šý]0gÚi¼æ¬ÙVCºF€/o›Ú©é:hxåµÜšNæœ+õ| mâ ¹~ë-5uÔ+Ç1 Ïá‘áô÷ÑCé¹}G„g›,Šem;{®‰kÍÑNx¶a»zö³»'ÐÄ!aº hÙüɱ®np‘§£ˆåÆòÚ5Ìæ~ê¬v*„¹Naµ\cäìYeÉàßRíNýS.ž¬Î¾ôŽ^§=¦Lƒcð¡™X€Ÿ €ÿmÒÒÅwZ¶,È$úS•€Ý% jêí.R5C³y†&„oæ%=iV–iV-~ÃëH~~%/“¹ûÉÆ*“Û,ú鿦~rÌ{ãcë  ìííM¡~•T^SMy•ýˆòæQxÄ ‹5öÍÌ‹sÿýÍèæ’\qµ¾ Ë+úQl E‡ø×ói„°[0®GkÈÑõ7Ç£‡»Mö‚¸ôØ­ÛŒIþñy/š0è÷ă 5»ž1ž7=Ørðuš=å¿ô›Ù‡`[ŸN_o|À4‰Ã~+ÛŠM¤â"ƒE¿âþeK;YP3OŽwµM»óÒR?%Lxõ¹XB)1Amª±AÂ1Hû¢JúÕö½Ô;8ˆn0¿qÍfyúŠýÛÐÚ²ýxnáÅ ¿Þ¾¼ñìþnP_‘öÇ“Œ÷ŒX±V˜OØù€5Ô hÃ˪…)Ÿ½ú©3ÚéÅG­–N[Õß#>•µ5i¯¾ô!®½ÑãÕçýcn&~6†ãCé _¯»¥”T ¾B.êaI@õm$Ø«-[ò¯}øÊ5¤×Í1šÖ˜È»ajòÙ‡ÿ´Y£YÀ¢ò…J££N[WYU[Ëw·ÑâF9]Ì Ë`ÞÇׇ|±UVúÀ¶¾J”ʯL¢ÚKòƒE6ǫ̈½ˆí²Ù ˆg büôê+øc>™_æ›5í­5ëp…ú×i«èõ¥ñfEfÐÜ7öv³|ãýÒ#ªïG«®¾îkë® oš¾ÑÍvüÑÐVQÔ5Ô‹zuýÉídÊfUM­î 6L/ºÐo­^[aë3ÊÕ±¤Ÿ2°”Y@i‘mfcçÆf¥»=;>KÍhtíq€v%±ÏôÛÖo~镾í,_­LÖæÇlCÎ&7¬¡·w?u•vjNˆmYÓrù9ÕjµÆ—RÚ Ó‘æul û±¨‡®F×hº9*À7•œú»­% ‚ú¶–pÎß,7S_3@ú$=÷ÈN-íkÄ‹­¶ªº°¨¸Ü&PÏSÆ Š=1åÍ`Þßߪ«ª©aÉkëj)R[Õt¹6‚rrÂu*ÀÕ¥¯ïiÖù±çÊ4GìÛœ]!–•¢ÊªD,°ÓRœŸzø¦÷÷Eù¼÷ü2ßÒü¦¹<›ºæŠõWòÊ€ßR2è-½·µéLÛÊ*À^á5öl'SþŠJÊõÚºº<œçþ瘝×Õjsm}F¹r–õSx©ñ¦ЍÈß‹r}¨0ÀÓ!~ì¹*ÓRzKÒÛ# ûag·ìå…ÅzàmÚ½ ú©«¶“£êoÚVüœÖÕÔrô1ñÍR^?~Ç|^ýo*ÀWJF=v„TP¯òùì|J>›EùÅeTTZA5XyÏdëוÕͬy ðƒ±?õëKñš·Ó»à?k<éݾzî‘?mF^ äMk&_Œ¼×UU”ç•VVk*Âý0%Þ2|„Üà…šz䀀#հϬAð’:„6ÙV’/ÎÖºQi5Ehk`*즩 wbì[gÏiʯNïEÚº`Ø”û ?ôîóa5éëï#x D¤CÿqÌü2ß칆ëÑr¥ú·†gÝc®­"½µâMÁAdïvRÖ“û;÷ûÚšê,œ—Ï„2‰+ ¾˜G[ŸQ®Œåý´J<§nz^yƒSU{º‘¶•ψ+³µ<¸Ã}Œw-–Â{ -â†÷V¸·®Íú©«µ“£ë¯l'ùœÖTUæà¼|Ny]Í?#û2%§eQAq9•UÀƒœeŽ)®¢âDGÂRŠjÙõðªõ ð6î;I?ï=FŰCdPçáVInuhríEx“À®NçKßm9DAÞtíÈþ4ex_h“]»lò/Ì_М•/Evƒ =Ÿšr¨s·wKÍ¢Qý»7wo³×xʘʲ==kÀkè1eZèåÔyàe¦­†w À|ÜéÁÖ½f1ÊVb¼á†÷>ûÆðÀ !ÀSO¾ðœÂƒ<žAàÁFpp 01°ŸÌ/óÍüÛB®P[øoͽUùp/hX¬Ì>úý}áÎÏ­åŠrŒB/¼¼ÜëÐVD>îmÞN²žÜß±ˆ\“›u (Ÿ ™ÄÙ{#OÌcPXøom}F¹BÖõÓʆçT§¡ºjUÏée œØí¤(‰4/ˆm/$YeŽÝE?帲ŸòlŸ«¼Olk§¦ÚÙõ—<Éç4+=ƒnð7Ëø\È4æövø.m–‡w¾±u<µÿ$•–á=ŒŽªÅŒy•\¼ÊÆ3'+Îñ€Î§NOîø–3– Dœˆ©#“Ú–²¢š­NêÚh²ÕÕ²ìÆä´ ´tõvhå«ðáN¥Èð¶5¿`®ŒÓú^³rc5mÚ›LwßxÐÞ·V+kYm­KÅ¡îz²hœ‡‡îv£¼™,ðœŠÅ®¦ù€¼iNü•€ûºc{w¦Œ¼vZѾä³Áõ­þâµKÉ…=v‚ @æ9žp•X%^F^˜fÔéè甽ˆùàò$xáAœ")²É k~Ї‹cúìýÆ-½äÛUê/ùqÄ^ëíKN}Aå÷LìxŸÑÎX¶Ñ5´YcPå¨v’l¡¿£Óéó7}ÿ5¯äçÁ€>eר‹g•yL0ð2x·åå*9ªŸþ”•MÇò A¥_ÌêOñð‰ßÞÈYýÔQíÔR{8«þ’/~N±ð¥hïæ 'pN~·¬zVM¾¾†æÀæ\ š]d«-¯ü>ÿÓo¶_Þ¾]²ãô=+y曵òËÖ좀ya&‡Åími&§4½Z¹ñ€LÜ3cŒËa)G7ÐU êÚ“Lßü¼*—b¢V9l¡$ÛkûÃv›7^€WX4“ÞûªŠfMÍ}’ÍÚYÛ:ÜÈù øó?Ý^ööÍŸ_Œ9lk óc߸¡ÝFeOž¼¹)ÃT©åà—#§a›—šÌ´Ôµnnîwœ9ŸC=ãcZ]- Þ½a£‡›<&þ ³½:›¸°GœJD*¶öÐ2há3™_P:€{&[À=|˜ØsóᎠ4 ¹Lƒ–6ô0¹a ½0ë0jé[¿@V¨øçÌú+Øpèá¨÷Ðîä÷¸ª¨¸„R³¿£a½àÑ#®ÐNÜϧ^Ð\ÎÉù<òsÀσ|6̱íŒs’ñ¬2¯ÇIó¤­Ï(W¤­ûéDs=°¡©÷'?-¶õj_&®ÐOÛºšëÔ®Pùœfgf¬¯ü­ºâYóÕWîËçÎågÄ"ªø¼Èöu¶Áoà»ûùÞõÈÝÕáwÍÚQ~2e¯–ĉÄßJ-üµþ åäw[S…·;¥Æ†´Ù‚veUÙü./È[l¼H:1¯ Xj#°Ô0ÀRJN{|ÕziÜ¿þi?ùû%Sx¸}\¶¦Ù8ªitô"áÖ𛟡–ƒ[Ä©#û‰œ|µ&_ëîÑSfî>:±o¨Ô¹sqÜÏ[#²È+o¬®­údÏ™íoàÜ:åy“cÖrH@Ï6OÕ;V¿:î×OMÿ|íî g˜éæ… *­%~ï1˜fb¹~×»ºÄô{@}/ ¨¯ƒ¶ž_Rü²²ÐK^ey¬yoÐþ¼ñ°© O—ûùùŠÍn˜O{¿§Y)GîC)˜ÆyÝ赋D±•uét±ègêßíö&Ùpf;ÕÔj ýNW°í»•KÁ¤ Viÿš¬œ}/ŸUæõ¶G» ¼‡ÙúŒ2‹mÕOÙÍäÆÂrÇóÆ4$"Œ¦õHÇííŸ3û©”U[µ“Ì¿¹½3ë/ŸSø¥/Úöý·_ƒOþV]ñ¬&W¤ôû꫾Ésçò5«ÈR€ÏQlƒ† žrs…À ½ÎgÑäNÑä_ÿ³ªÐV$–Úy6_f<õýÖ#”“â¶v=Û«yúp\¨pçúÍÏû„¥šâαç¯*PÏ‘Mn¸ÑÐGF|éXi›)£š >.Ï£o7E‡Ñ€žqm ì€üI€ùòÊ\3œ ö%ÒkV`éèråb×–Lk-êì7þÅzw_šå€ÈsPÀº×¹ßˆ)ÆÁ0…-‹Ðù÷üùäÊ ¬Ùà— »E©,//)9¼kÛ‡nã'?½dÕvýó&Úd†#?Dâ£`oИ{‘Ÿ¯¯ôb-"CŠE´õv÷Ô››­ÍR½’Þ8ˆpÃÌG¶eÓö›ÏîY[Ï‹bÌN4ôÌ£=I~¹ÎŽª¿=ùoM^‘HãUHÇÒ¾·_*ÙBÝê’¨k̸FÙIQK9£¸_Ì+¢3ÇŽ¼zùr6G[â瀟WõâYe^™g8äM[ŸQnÙöì§íõ‹´ó¤ÁŒìâýhþ $ò¬Y{"Wè§R^mÑN2ï¦ö®Pùœ&ïßóNII?§ü­ºòYÅ„wˆw){x ¾ÿÒÅcaþ©=qï|¶Á·˜L¾® æ9ý<<)ƒd&zDg<µÏí9LžP^ëI< Õ–_zV‚KͤUÛŽ @ª³Ñ+µdÏ¡{Ž<Ͷ«¶ç‡Ç2 ›ÿZ¸”ü¬ºZó¥oÕø l†]G-y)¦7>Š̯Ë^²iÓ¤9;ÓvöÑë=Wht þÇíy­^7ú¸/4¤ûM¾æP0îy©þ>žÖg À ŸW9ò‚ Ÿ[7î ŒZŽãy˜DÐß;sœ¦µ{þI‘ ðù˜ÝF2Àæ—÷ ƒ–1o¡©ÇàChê%°V—)6¬bM=—ÉÚz^$ËÀ…¼§°Ý6xºa¾ÚŠœQÿ¶ª‹¥ùFÇ[·‡þÒ&XÊUjjž«ÔsGd/7EðpÃ6ô¬!w%b~ÂBV Ÿé÷Ÿ ëÇ 0j®Zϧe@Þß7‚zÇÝ@GS¢ètºÏ‘ÿöÊó(šˆÍVƒ ¾8NcfìJÙ²W=7¿æÕ…‹5'Žo|þÝ8¼˜õ[ä} NS³^u^1¸ºàEhÿP.k?nî‚àˆä§o¾ønêíw°êzvv~¡þŽëG»ÙjcÏ!‘6>ôÌ£ðBZ¹˜–û “­ žó`°Îš&õbÃo÷†ß†´Ì“#ÈÀ‹cêïˆú´TÆm“ÒòM÷SAI:’ÖЮ“/Ò¼k—a¦„Ç”’²wd;±m.›Ü°†þbÆÙÅ«—ý÷pÃý^©ý³úù«¯R[î$¨g-¤xVW-ùè“ï~ÀÀþ{<£Ì¼=úé[GORèî"ÆKô½#BÛR6mš·3úiK²G;µT†¼îŒú+ŸÓó©§—®ûrOïósÚ䳊ï_E‡÷°U‹ü* kV¸i48vÏüýôšaÖw2ÕêϹdQ®ß}`æ|c+YOÞûjQpµÖ3èø/ÌTžçã¬EK2°ûÛ³Ÿyáß¾½+˜íd¡a8§ÿ">6{|þ.òlv5fÀ6³‡›ò*aCÏZrW!æ%52€|×Â~XÊUj×2W¨g`ÆÚ×û“…—¶ewEb¾Ø ÏÖCž4yXoã‡M¾Ì,ãÙ: ß;á&Jˆ¬5të<°NCOOGÊ+hmf6­¿Ý&_â¨zv.±39]x¹a{vW#æ‰=ðl9”b–rµZYÆÏUê¬ËÊèà¶Ò•)Àïåå'R:øí™ÐI@ Í“ÈŸÊøNeüؤ¼ÔÈ+|óù6õú$ÔèÊ–Âî~^Hw¾¶Ø=éùùº×_]ì/º_ànêõn¾+HWñLr<¤¼à–£]æ õJ £-®´màܶyç©’ÇÞpÓtV; (/y} ¯·>$Ø_ãã çï6¿´”dú[yÍÒcù!’éMËó®°7­¯éoWà±µ<¸ãÛéáùn×SvÁú÷Š'ÐÁæ³3mÓ߯„­8ÀwWÏÑW9XdªÑé´ÅYégWíX½jyiiáedY„­OçKÝÆ\ˆÌ>«<89¶kçÑknœ9uœ‰g4r´ë3Ê20í—¦¿ó ;X‹@LX]/DÖ‹x §ÓØÚ;™öKÓß®T?Óv1ýÝ^Mëkú»5yÊ{®xNµÚ’Ì´3kwü¸ê{¬ñ*@:ô->«Ð TÔÔV?Š´¡sîyô^€z|È4@ƒï^\Y¸ºßÒE𜣯A7íÁe'-ù`:õÄ3ùŤM›<6Ož\‡ßx9¹±G‹) ÁîÓClmð Zú:¡¥?›y‰**k(×…ÌnLÅ‘§CrJ¬ÄR¦¹´¿ßÔó‹„µôÉg/@óƒÀ?¾§]º•˜?æódúEJèaÔ(_ùòr_ø¡Ç5PiÏ{þQÝoj´ÕŸkÜ<žxî‘Ú ?ˆ¤Úò“¯-öüÙÃ'luMUîûl_ÿÌC—K_x¤üLpŽÃ εþ:n7½÷×:MÕ.˜àgûïû‘ß³÷_ÿ€p/_ŸwƒJŽsQI•Àð†§©‘¡‘{"ø’ž6Òù,œÂ ¦Û’ôZm]UMUu!GJ¾væÀ±}»N @9…ÏZ?桽h饬Ì>«¤h×~±äHôõ€c’b{ôæãçéåãênxFås-ó±ë¾ÖÓÃýt¿¸x›»ø¶A“Yp"óÂé£Ö®¥©™u$ à9­¬©ª*‚—†Ë™©)G’ìa°ÀmëžU½& ZoñõÑŒ]`´›×÷òtsŸ Ótà‘Y°¦¿úCB~·ïSbu¤‘{!e@ÿÏþó®N×7>"vsr+lo€/qGd¯ÂŒã錡rc?ô®J‚7¼mZÆR®ZƒÖñu•€z-å•ÁO8"ÅÂO¼+óçîVAùÅ0ÀÄ6ßl"bõŽò¯-ö˜ê沇Á9Ë ªÆx lƒo8¢n0Z¼p±ÛSúÚò¾ÐF,òи_þý—Êâ7êê çá–…¼a‚£Ó¼àðïgçWžícŸñ @/’ჹà½ü]cn<_l>'í+ÁvǬÑgÍ~›‚ä¯R;—Ïxýö%úmXàªtê¼>lÿ– ïîÙ¢Ëh£ªÉþËëEؤ†û5÷a â,TÔoÜ¿ù:÷÷öBòÙd~e]¹âYÅàe¶cøÍ_~þÖ´é³êîïïžøú‚?»c„²H_§-M}oÑŸ=γ!*©hN²ÿÚü¬ž¸oþƒ“6ý×góäÀ†F“¬Õé$ßýèn0ñ>oÊͽPèC+Øb6Óaû.]ôK|ÌjÏ]ÎÜÞwÉøö/™{÷£¯c€À—­&{|¨× L¦7…¥Ä~âysUüáoK¹*×¶óÕáA=OñâÇ¢² rsoXHb»èÚ.æ³+ËÙ#‹weä¢Ô¬55­1ÖN¯›®­Ëõ,¼\à—¼p Ãèð­}³yüõók¾ëò=æõ‚Žj6.\ä6 oËs0Áy“t•´éÏ=XuVäÓøŸ|¹28P3P`ÄyˆE´Ø› ­{ó!3•:¶ø=°ì=÷W~ù¬î]OoJÀ7ÒkÆý“yÝ?{FËs{÷[&Ù%P`Ó98e`Ï ÿæóÒŽ^Þ‹S.O²~.ñ¬vÿË3Oºöd©á]¥-Ø´õå’£ÇOº¼U)ù¼É¾l—gµ Gí<4¿­Ó×ýÔwÉ¢A½æ ‡›ûôÑÓ—,ñ¯ÓWN ñxÀ(az£yûä½óßdsœÜÌÓ_/_úŸ{q}©1M+Z ð¯íEcBC¨˜„cº”V `£§ëz)žjKKòz©ŽLW¨×#²$¯Ö®ƒÇþ~º>¹ijà.ª {ß ‹…[¨V›GÝScÞím#ß8wï¡·?s¦èëÕ¯/vg^ÅÚBo{×ÐÚ»}ôïh|½/²’ñõ<é´u¯„ë‡Îšÿè”·>ñU[£ ê|KÊܹËùeùO¾Ïâ,§W¾h¥µ~<°€žµ äå†C•T ˜—À¥‹ZÚþ“ûã“gè>G ôð¤è»­ûÛÏ{Î//¯µ§–œû®Ü¸/+Á÷eò¼ç¯Ù³ldç0r‰gµÇ‹ÏÏñŒ‰ºQÖºúü…¿ç,ûr­ü­îU 4#ùœÊ¾l÷gõè=óÕW½ŠªŠfãÃ9_XÖÔj*¯Ó~ÛŽ[3á0½‰ÖÖêú%DÆMg=¶¯‡ ~ Þ$v·s±ào<ŸEîÌ8Ôß—ùxQ5Üñj[9sÐL;ØýóXì'\V^ VxÔó”‘œ6â}{¢‚’³t$åkòõ‚…^šÙçgÈ@^)'ãÿüoÈ•5Å»àÙæ¢Þz‚¡©n¾v9Loú×V–Ÿ‚¹M ý7ÛËô Tèéû*³°ÃÆfð­":Üp¼gM&!%˜—€^½Ø«¤J Y üô½6?i€ûãÑñºÿ!¡›·zêeíã¯]ÜO:’£§(*Z¸Èë6¶öØÍ g¾, fÿúî‰ ÝËŠ=Ÿ›aÆ*v%~ár¦,ð€A«],ðôîÞi)Þñ1BP6xT×ÝvqÕªB—ÊžëIÀ)Ï*—‚Ýü8¸ÅêÊ"A˜Âu‘q=6ŠG¯Ÿ EÚ ,’åg‰g¦J ðêÝîJèB)¹y´êÄÚx:žú•âr(KV(< %i=&T5õV‹Ñun0{×aÉ"N"‚{јó(<,ÔeÆ#ÏÎ×®þç"¿O=R‘mQ%Ú6‘m[Šš{‡•ÀòåËéÿÙ;ø*ª³ÿ?s×ì{!ìû¾EPëÖªh­ j+Û×¶¶u© oZµõoߪ­U+ZÜQÙdß×@HBöõæÞ;óžI&Ü„ì¹7¹Ëïðf?sÎ÷LæþæÌó’ûê‚TS·”Ÿ‡Õޱ m‰ ‹¿ÛŠˆ¢€@Pð–ö¸gØ@úq´”¯oŽ v”xrC¶å¦´ZÂiÚ%‹õ^úŸ_ú¹5'=ÿîù<Ú«®šøõîrçë¦ý™_ÒÊïçQ•³”.Ÿð8•²uJBl?êßýRÉì }¸î7<úÚ‘6µ£Qö¶”¿MÄI šéž?‘i¬á8Ë#­-züßÖ]ºÛY=œ{³ñ‡ª[²ÄVYY°”¼©zyØ1Ön¶\W*Њˆ2€@PðÔ",©ZÒ‡  +{t£rŽumŸî4ËZê4=6~$›’D*gº2+‡žÞ¶›Êœ.šÒ­ Ý>°/}Êññg ÀúK£gwì¥J”ìÖ'Ïò·þìÀ:#¨{êÛÓnÕE» g±jËãôѺûôe·ZEÙ§·ÓûkfÑGë}®á(5Wéû"ÃéÂѤҊúb㊠O¦IchO1p.€@`3ÍbN¼ƒíëeÜÖö®ª®÷Øq6©ƒŠ€Ë4E ²àYþq®uŒUÈ9®èٞغ“•ÒÿŽAÖ²åï,êõWU'e胾QVÞ6:ž³±öÈow/¢ÌÜ﨨4“r vSŸ´ÉµûJËOÑÊÍóhÇá÷hóž—©Wý7¨v?@ü—gý³màëŸí‚R@Kì),¦*Š>«¬‚6ŸÎ£‡Œ#IôʾC´5¯€Nòö}E%415¹NvÏnßK_dž¢¿lÞFvú£ãëìÇÊÙ‚Þüæì*·oKdD ]qÎ|êž2žJXÀGG¤R¥£°6ÓÜ¢ýµË"ø#¸÷>6²;•eÖnÇ€€ÿÇY“áVT|—ÒÄ_~ ÇÙêÏuþ[ô ,™8ƪºclõw8Æe3£R!F )<Œ3œÆ$ÅSv…ƒCf†é½ùžóx–œNUTR©ÓI#âè»ÜÚ±/=År ôÔ·òV8gÈÝÔ%q8=ÿÞDzé£ËétѾ:9$²=½‘âcúÃYBÅåÑÕ(:æ Òrœ·À|KHCé„ÊÃ1¶ ã’ ЦèMƒãcèŠOWÒ _¬¡ƒ%%g]µOt¤¾-Þn£(«•öŸu 6Ô%Q_—Gµ‚âÃì“¥RÏ.(ÌÇœ˜¸g¾••ç°c‡‹út›D=¸ÇÞ3ÅðþQýo¦¤¸4²ßMtìÔ7zžÇ`@Àÿ ˆã,)ôžQR…Ô³ãìcsßÐc5‚c¬o1#w臹×}¦iµP„ÅB]#Âét¥ƒÜªFçwIæû„³Êq+ ÿ‘‘$޶.Öb›sóÏ:ꀨ¯Ë£ÎZ›Õ|¿w1]:þÏôÛ·è‘p¶ðzxXýáæ=4iÔi#Û×{¦ÌÜM4vð]t÷Õ_ò @úfÇ?=wc@ @޳,ìwK‘á8ÛÁ ÇØŽË€ï¼yð( ‰‹¦o¦^N—wO¥7%éßxÝåô?ÃêöõžW—ˆ7•n7}tåE4­_Oz~ç~ŽžÃG#5I6õMâ!úbÓ£$Ño80&¹\•ì0û-½ðÞºÀ—0–õSEe!½¶êgd·Å£ ŸŠêóÁ:qœ¿Ð>•¨ê[õ±ü0Fœ½lÚ´·ñ ã£Æúê‚tM­;bìŽÛ1b¬p#[ð9ùf%O16UU;Ê^ùÉJ½ç^ÂXÖOfE¡yßï¤'¶ìÒCZJ´¤æ  §¾yFätUè‚ÞóІ½ç~zOXÀ%ððLÇ>Œ8Ûqí'ޱŠFÏW„c¬Aslâênz£& zcŸÌ¥·‚Þ“HÓËõMóiÕÞrG•;à™Ý*h8€g;¦‘ê;Æ*¤lň±ÃW!PÁB>«¼Â_ŠPåzQϽ<ìàÊÃÉt@úì›éÓ ÞlÊ({G•¿á °à8ëÛ¦kÈ1Öf1Oň±¾åŽÜA 9žZ¤#äÔÊ“ÙtÙÇ+¼ÖCïYþæêèûƒZÔ{Ša«ÅÌklÑ^šf'«¹nÓxÖ% *B‚@à¿A}ÄÙú޳ó_‰N ²ªvNuàÛ9ÜqUh‚@CÚCv5³#«¿'3GÖ 5-UW9ú{ µ£|‘a6RݱíÈ¡ãNu»c(Ìfî¸ âJ -" ޳ Ù¦ò·¿"ývœ%Gé’%KnÄl‹6|î«ÕuŒÝ9ޱ ÓÂVè\aÜIjwú¿ãªÝ¥†œ– Q/oš1vއAªÖ¹ Í\]Ê'匴[u³¡†Þ’›É»A|HÀpœåÇŠÞU%#Î,\ú7^2¨³†clP7/*$D‹S˜ÅDŽF#“¿&£|¡¦¥‚^Ô7a¯® <”BƒüõÔË%å“rvKˆâ¯ügþˆüºÐ(„qœU4å£Úª¦Ý‡g -ŸÃ1¶å¬p$tCG‰&1±íMb”]¸#¾ÔÙYEjöºzÙ¸Û%Ô´TP‹úÚ‘¿•§&FS¸ÍD¥å£š½:ó)ŸGHŒ 3ÛÕ‹SŠQÎ,®  P—€8ÎòŸçûÆVqö¯ ­£uÌ›&ÇØ¦ù`/øCˆMb2›).ÒJ³B)%•þPÄË e³Yù$Ä´TP‹ziiy«4óM(ÓО)ÜSߟ*+{5xtöF)—”¯wr YØfÍ(·Ô @À¿ðf¶$N÷tœu’ë}8ζ°àÛBP8 :—€§Žmb±X(5’Å}YÅ”û_o½”IÊÖ')*ä´TP«Eã Sı܄#û¦ro½BùWs$kçþ•Ô»º”GÊe·h405–l<êš”YÊnÔ£Þ)Xèdpœm[ÔwŒå\þÇØ¶±ÄY àK†þ0t”h›ÍJÝãÈMë©_N)™üÈ´^Ê"e²Y”ÔRA-êåF7™þLdÖoÂv–ß?•ªœ)”—w½/ÿZ·”GÊ5¤k …‡ÙÉn¯þѲK@ü“g[×. 9Æîœžþ­ËGƒtO%ÚÄÎÂ^tJ¯ET¹hà©âŽ*J³×‘²H™†v I-¢¾ÚüFÞ,Ãìvêš@CÓb©¬|åž¾©Ó{쥇^Ê!åéo£´$g¦—UÊ,oÇ0¿iöï€@§ÇYö~yÔ(g uçpŒ­Ëk  óCG‰F ç)%&œºFh”Xê Á'‹;µÇ^zè¥ R–PÖRA/êåÓ‘c«Õªß„‘‘4¢w ì© éSÙ3;ÍÆ^lè³N¥ëåèk¦þÜKÅå“2ÊŒ”Ù0¿ „?|”B™ÀŸf¸çÁq¶ñ;ޱ³Áðg é¨j­NÝãìÔ%Ì­‹éQÇó;ÅÆ^lèG+ÐËêZ*DD½‰½ ¹§ž?EEFRdT$ ë•L#Ó¢ˆÜ)t*ç—”;ÊÊFù<޽ġ—ëÈõäºäN¢þñ&ê—CÑÑQzÙ¤ŒRV)su˜ßøóe!À?|g9κ4×{pœ­¹?à‹?HÕ¢þl%šEôJ û.Šf³—™…4ìD%;|Ç^âÐË5äZrÍ— ZŠï.žüI>‰Ó©ØI/¸Óé$·ÛM}ø¦ˆ ³Ò¡Üb:UÚ—r9ò ÿ.“I)'³¥ˆçU^ƒ£j6r»bÙÜ'BCoQÜÔ%\ã·Ü0Šæ—ŒØØhЉáI„=—QÊ*e†éך€Ï ˆãìü…ö©DUßrˆäXžz+•¥oñˆ³—O›ö¶ÛçðÓ èŽ±ê™c¹˜pŒõÓ¶B±@ !é(µf*Ùm«¤œr7™Ê5=ú G#owphI·ÄÄôB2óh2š­>ð?`M&’ÃIÿb-"¢¾ö-“…2ۺꂞgz’Ñf3S² *(­¤‚ 9\áäTù×Þ w`M~Y縮66üе›):ÜÆvóa,àÃõzôq±±ú²˜Þˆ‡9zé½Ç9@GÇÙ'˜oUõC~Îð_>]R3âìï;ª þtqŒU5z¦æ‘+_4^‡c¬?µÊÍhNGI¨K«ÅÊA>*©«ÓE¥žœ U©nrqÔKTÓI:^­<ÄÆ‘«µ”ZÊlHôÔK}«ß2Ídçs-²úç¥z0ŽŒÃf.vv¢ ¯¤D‡ƒ{ò]äæQÞ@Uþ5’¤oúZËÿ“?Iâ=.e0›Ä¾Ÿ¿è× «¶¡çžzé¡×Íoj{éá ÛrÊ8ü‹€8ÎÎ_hbÇYí1)Yãìw³ÓݯûWI}[qŒ­Ðh)?@mr%‘rkLXüݾ½*rð–訊ŠJr°ŽŠ`•è%%u–jY‹†Œ¨rCŠ –$7HõºzÑÄ6ì•|3VÖˆzÛg¹Ý"êÕ6 zýB×’žw1©‘2H$ñ S±I‹ˆ×'1»‘ýR6$À% ޳/4ån6ÇA«8»ë™Î­[«–—\wŒ­,AŸªŸ¥PžÍlžºaÚ´Š–ç‚#Aü‰@gé(a`è6h©ÆïˆõÆ !¢Y_fa/Ë"ê#ÂÃuAïpTQUU¹\ÜSÏv÷ž¢¾µõ5ôµ7¢ ¯,Ÿ¨Ä´F®)Â^LmÄ)VÄ|õ€SÕ=ôR>$À%ÀÃÚ“/&Mw¹ó6’FCX܇×8ÎŽ{øŽ’¼À­Y K^Ï1–»U¦m¹õî£-<‡ø!ŽÖQ‚ÀCƵ¡¥¿1BJÔWßÕ!.›Ãw)"[Ìnªªx w½—Þ­÷Ôkük,¦7í1¿‘kÉ?y»”ëIo½Ä{• òV½÷¾:Ò zè¿Y±@¨:Î{uÁL ޱv»¢¼ Ð"¢iŒpÛ¢Y|­£¤PºŽ‚–j¶}BNÔDäF¬ö<⬈l¶«×Å<›ÛHd™¤gÞðìn¨—kV_ô›_þÌ|}¹®¾Ìbß(Q>ÌA‚ƒ€8ÎÎ_h¾Ío–ñ3%ègkcŸ5ZŸ³pŒ5``ADÀÐ-úˆ³>ÔQ‚Lôš$h)C£ÿ…¬¨"r“ˆ¨–›D÷ÜæžsqŒ5Ln<{èÛ+êåZÆ$×3gm¶v€<‡gº? ÇY8Æü­Š €@«t„Ž’ÉuŒ¹¡› ¥t$uþ iQoð¼Ad›§˜7ÖcÛ27nF9׸V[òÁ9 K Øgkcßá(c÷6EÉA M m#B[’·u”ä -%šN±Ò¹qªß«ÍbªMdØdFÌfÚ0yæåyS6pilRü·Ô#Î*ÏðùùÒ|\W7…clÞ˨4GÀÛ:ʰª0ô´TÃ-Qß0l¯ÇY6e*H.’ÌÙm§7U8köúÅ:0CqŒåà¿é—Ĉ±0° A¢¾#(ã  PC@gI1ÝÆYÓëÂþ’…Kÿ¨€Ä1–kÇØ@m@”@ h@ÔMS¢" B@gÙH…Gœ­Nl¶òûy Ì·ë2ÇX·¢,åòcÄØ@i4”@ h @ÔmÓ¢b þL@gÙ ç}£Œ5#ÎŽ6Öý}®;ÆjT×1Ö‚cý½ÝP>à%Q¼m‹šø1€wœ­1ޱ~|¡h ¡E¢>´Úµð#ê8;lñÂlr3Óåý;§ÏXᱎE&QßÁÀq9ð$h޳#_[t.»÷>gÔ¿8Ȉ±7Ö1èõÃWZâ8«hÊÿüÕqVc]nޱFCa ~D¢ÞE]ÍtÏõgÇÙZÇXҺ魤Pž ޱ¡{âæ ~G¢ÞïšE~ï8 ÇØP¼-Qg"Q@…¢‚7uœ…clpßw¨€@p°j5ŽeåÑÎC'(¯¨” KÊ©ªÊ©WE¢1P+ÕÊrÛmVЋР„ØHÖ/z¥&µ2 àK÷<òøX·Ûu)JOMѺ±yMts×;rŒÈd*;nµæÆðH­| ]râdÔ‘™s;ØÜ¹¾ØïR”˜òcù£Œg«Å­åD:]×Nœóص-¹ŸWÂþ'ùØ£f“ùóçûÓæ–œ‡c@|OZŠ(˜´T@‰z—ËM+6íæi—.äy˜uŠ£è¨0²[ª*^ùK-(vб“§©¤¢’–­ÞB±Ñ4eì`ºxü²2þœï•ë Pî»ïé°â°Ò{Í&úKu¥òŸ¡æ,S#]eŠÍ]ÕŠ¯£V‹:ºàÉ/R —B¦¥*³M-³Dj•Öó˜7sö_²42=fM{.#ã®Êe‚ƒ@¼FZª.Ê`ÒR£„w̤ÿ~²ò‹ËhP¯®tÅyÃhpï.n×G'¯ÛB!¶Vᨢ=G²iËÞãôþªïiõæ=ôó+ÎÕ{ïM&Ä}ˆÝ¨n§7hÓ÷Ϲ¼ÌZ²È¤˜ºõ(9¦õËßK=K+v—ÃÜ)¥ò‹ê/2‹ŽE÷¡ƒ ƒº‹îù·,çñßñׇ_,œûÈrÿ(&JÁOZªñ6-¢þË;iéWßQ×Äšuý$ê“3ÏÛR^lÆ ê¡O‡Oœ¦÷Wÿ@Ï¿³’®½h4]rÎ02s—¡ˆ{$Ÿ?.ó/zôwf³õ‰øÊÓÚÄã«)µì$>•yàæP°G&%+²­ë>¹kAxÒ',ìïga8÷¬°¾ -Õ4Õ`ÐR~­ô8V3}¹q½óå&Á6㿺a2}Ó÷¤Îç×7NÖy½¿j }ñÍvr:]äv»Ix"x€ˆvé·ÞùÀœß³ ÿk¿ÂÊÔ}ï˜YÐ{í"Á˜‘ð¹nÿÛæ~…ûMüXzš…ý}\O¼cc£NNZªõM Ǩ¥üVÔËM(Ÿ‰–~µ™FöïN·^yÙ¬¡ü»å7¥p^ÂíõÛhÛþcº°WU¾åq$4E@7·á¬?ÿŸ?\iµÚç³ ×.9ò©bQ«ö›:ûˆ„“ðnš¦üí—e\Î\ ìqs€€ @Kµf j)¿õÍæ¿ŸnÐMnn¼tlÛ[%„Ïn]bè~1*-«¨ö!ŒUog§uøð ±ÑÑÑÏ'°ÉÍEǾ„ m]á_qšÌ&åÅ™33ÂÛNh„´T#`Z±9´”_ŠzéQ–(7Ååtí¤Qè¡oÅÍçy¨¼eN½h•VЪïv“£ªŠÄë]ø"´™€<7ÅÉ>òâ‹~­™Ì]Ù†ÞŒú¶ñnd®2kì\ìNPîå\üòw©mµÃY Ðy ¥¼Ã>´”ß=<åS‘Û­Ò*Žà"QnàÛ¾›Rø Çov¡ }YÅÂ^ø g$VÞx±”°[ᑳzr”ØÐ·šc„Ÿp4›éw¼Cøâ«GBXÖ€–j¯æŽ-åw¢^Þ,œÈÕ{—%¢ Rû ÇÒr:ž]Ó[ïBo}û±"‡Ð$PÛKéu7MPLæd[ ê…{A8rüú®·ýæÁñœßý6y¡ŠÈ:Œ´”÷Q‚–ò«§ñf¹óP&ÇV'=½÷›¥ãr\÷Ã:r2¯ã.ØÈ•$ž¿ðÜ{ôUV:¨ÊéDo}#¬°š âÝè¥KHér oÐ8}§`WK GáiµÛÄa½õ-‡ã@ `ÓRûŽeÓ7;Õ«eǯ‚–òCQ寮ÂR}¤Ø¶,õéúôà³ïÒÒ[Îjõ'_ù\ß'·¹äb3•=Gš?®±|Ö|¿ŸòW‡–¤™¹TYåjÉ¡­>F8FñÈ»%åº ŽSõqÙj8!Ô ¢^†z 7Y,=ÜåšÄ_Gj?áÈ<Åá§7OõíGŠB”@µ¨o¿–:‘S¨ë¥¿üû#þº_×d×ÐYˆ£%©=gßÑlÚ°­e¢þtaÊ+nI‘Z}L h)¿õò¹Hâ©––StTX«{žf£‡NÔ±?ž]@Ž–‡›“·ÃOÖo÷ÌÖ'ËòòðêǨ˜Z}•b˜gIy%U:¸§ž# gá Ðbò¼¬í©7[-)ÎRúH^"ÀÝèʉÃù÷‹è¥×ë½ô‹Þ_«þúÐW4y=ÃVþ'e)®¨ ‡£Jõ¹þ›w+³Äá jDdʃI~ÉìfÅeWõ^¼ „§‰”èÎõ^d‹¬B‡€7µ”h˜¡}Riûþ4 GŠñØ©]³ôê_jCZJF´o­Æ µä‹Mt$+Òº$PRLdëlØ~ˆÖm=@%e•Ô—õÝÅãQO>nŦ=$/6Ö;ß±®›:i4õï™Ò ö ³µMcú»–ò›‡¦Ü8Æ'#C×iÅV®p|Ò·úF4NÝvà ç‘i=S1ßKXlŸ3¬7Ýþ“sù³MmÜq˜âc"¨wZ"ÅD‡ÓM—Ž£óGU ÷¨; ïŸF÷Ü0‰†õëÆ;÷þWéYйÍb¡ë&¡ü¢rÊá#YøÆNK‰£Û¯š@Ó~<Ž~ØwœvÈäØÌ&=°Ú!øÊó‡ë×’sšºŽ‘gkçò‡.Ño\®3#ÌzƒukËãA ˆx—IzuaÏ,z4$ÿœ æ>¸ ²à$àm-%”†ôN¥‡NÖvRnÛœFôíBÃÓ@IDATÆ0g6¦¥Ú¢qÖnÝOGNåÓç £~Áïû½ÇÎ\ˆ—â¢ÂIôRúõ“ô0Ý_mÜ­ïÒ§+‰•†è<Ñm=ø¥£1íU'ÃV®ø³–òQ/LOFÞšn—J#X|ï¨1Á‘7>'ÇhïÓ-©Nóí9’EñÑ|l7Šc?¨wWÚ~ð$ÅF†SlT‹t“VSq’Ë7Ó,ðb£ôÝÍ&,¸÷½íÕóŠJéª FÐÈiô³KÆÔ¹Ž¬\8fõJM ~aè’C{Ù¼GÞb»%ÇêÇvO‰¯ áÙØuÎÊ´5X…Hœz1½ùMkÀáXÐ ÈóRD}°×Å'Ðx›@õË’0ö«ß'oWù€¯xSKIûvOÔµÃ!¶F$¤#v×Írô ü_cZª-ç`fÐC·‚¸dü`Ë Ï4„¿ çNÕèÈ0êÇ=õûŽçè/©Iq,âͬéÂu-n×OkH{yæ×êe?ÖRmûþÐjÍŸ`¼]2+~ûóxýkþÔüö裨ºé›àì8”E#øíG-¬süþc¹$o˜Ï¾µêÌöº‡œÙÎK{Ÿ¢ßí¡\væMŠ«þ$$Y>GIJKªèâP{æ“Q1Û³‹9ÏÑSyÃ/ 2 TÓXjì:ß’íBUսĩÌÕ_GÄf @ IF¯±ÌuaÏ>øÃiYÛvòsÉ“µ,·ÿ¡mEÁY pŒßuù÷†–ÒÛ.&8"æE¤Kg¦˜5¯Þ¼¯–·´”ô‚g²™óØ!½kó+Ï %ŸmØIÛØHÕT½lrŽLfóÙäÖj¯Ú‹6±àÏZÊoD½ð«½›€Ùš]îe—Ï1Òø»øÓÑÍ—KøãºÉn·è½ïÞ!QÔšNòG²ŒíéôHf󛋨”mðç¾ø‰~RJ‚˜åó(¸iüùGx*å—#­Û²Ÿ²NÑÓ¯ ±åz~éjc×Yó¦®sÖÁ­Ø |å ž¥|-ëVœŽCA Ô xŠMô"ûön0Xûö*È‚€·µ” Áï®ØÊá±½ƒ´~g ·´”‰;^Å_± ¸¬¶eÄÂH™9´ò»½t÷Ô tÿõ:üƒ5Û®®5Ú˸FssÖR~÷Ã$°¼™Äþ}¦*¹öM«v˜õÌ¿}æóÍ#1åN—!æÈÉÓú!)ñrc•ë¦5åì0+Î¥¥Üã.¦1UlÆâù–Ú51F·ƒ_Ëâ]nÀµìÄ!æ>F’žù¨ˆ0vŒUH¢êes #%±)ü:™K|'Û¼7v㜶Îkÿؽ̹­åÁy ` ±)sÿO&3EO©7̤øó.íòšÂ#¼qÀàëš"ðok©,¤ŠµÉ·;°‰q÷³JÜ”–j­Æé×=…¶8© ‘$`ÄH…%ÕQE‡•Ñ&vˆÕStLæ‚ãÙùº#¯hº¦´—‘g[æþª¥üNÔ·nSç êÙUÿ$3‚o†¬LÄ6Kœ1–oØE¾°Œæ½ô)í®‰M?”m»$ÄÒãÿùŒÞ\þY-fºhì@ZÅŸœþ²è#½·»G—3Þß—s>âL"ÑrNäÔÚÇKùÎÑ£â8èÑËhù7»éÂÑj‹-ŽçèKòÛæ_^üXÿŒÔÔujOÄ€@'h½Mýð.£n·ÞÛ¢2+Åþè‚ÛØA \Nýÿôd‰K Kt\c‡ye{â”khÄ ŸÐèÅkiø?? .?½Õ+ù"Î' úG¢F†ÛÎòM”Ò5¥¥Z«qÎg=$é™·Vл+¿§ ˃{uÕ#Þ<ýß/é™%+iÂðêc$áÉœ"ú_ÖZÛ9NSÚË8'˜æ~c~ãÍ·JñŠ6’Íj¦Çf]k¬R˜ÝJOÞ{}íº,L7HŸJËyðÞ/ÞÒ’dÀ¦ÿ™6Yï=—í’Äiã¢1õ±-óLç íMã÷"'÷è×ÉÙ»["‰‰ 0ÕP(¥k/ÅÞÜÃ8;…Ã1™›¼Žç5Û»ìMîí- Î?&Ðá=Ç1cΣ´ŸÿšŠ¾_×f,±c/¤â›éø¿Ÿhs-=1jðhÊZúo*Þ²ž/ú©þò’·î3ržù*ÙÒ¼<Žëpîׯ"oþ¦‹-»§^ºõŠ uxˆ ŒgjLKÉ1­Ñ8bÎüÀôËx\§®Éª¯1BŸ‰Yuúuê–†Î:wxŸêCøÿaÜ+öÿ••Ј£ß”öª=± ÞäÞž¢ø¨—J”Î#a$JÆaì“›ª±$ö`vSãXôF^[Õ35uÏãZ³,V7çÎdÝš2ãXð"0=§v+vüE”|ÙT´y u¹öRËK)÷ó·)wù;5hõœñ0Ybâhø¿>¤"ÊÇ=Nai}¨gúlŠè3˜YÇè‹õ²}ÛHòJšr-þr)u¿ë~*ún-¹ËŠID½$ÉãÐÿ{—4ê~ç)¢×@ªäóóV~@¹Ÿ-Ñ1ÙÃ(mú}ÎÒØ÷&ýçtâ•¿“b £wýâι˜4gå|òe/[¬ŸãùßÑçÿR»šûÅ;”zó=9`8mZ]»½e ue[v ލ%`üÆ×nèà…Æ´Tk5ŽÑ™ÚPñ AßÐ>±Ê¨¯ÛšÒ^ åÑÜ6ÕRuUdsµÀ~¯0ÛÂ)fôydŽŒ¦‹Ÿ¦˜]È‚ü”·öSrœÎ¢Ò=[(²ßP:ò\¹J õkö¼çr—ÓþÇ~EÉ—ßH=ï~ˆv?p I^Ñ#'PxŸA”Ͻã…WævQì¸Iäæ—…¬·QåÉ£dŽˆ¤Âo¾¤ã/>Iqã.bÿýxɳÛÍ¿¦Ä ¯¤ì÷_!G6G–p:ôk&ÿø:Šsýç£lÆ“D½îyTÏß‘Ù(‡èaãIs9©üà®FÁïh¼ËÙ»×An   Èzóy*Øð|ó_¤X¬ìØ:Žœy9T•—Mª£’JwO•™‡ÈKQFÐé¯Þ#WQ>å­úPñ¶”êõ¤§ýäkÏÒÉÿ>KåvRÅá½äâÞzWqž‡ZQ¦ç›óñ,Ú3©¿(f ÅŒ¨Ž –0é*Êßðe½û"åÍ/ß®ÔKÃvýrl剣Tºs3Ïyp¾s/i &Õ›¬‰]ô—“ìe¯’3ÿŒƒ[£'`€€€W §Þ+‘ €´@Ù¡êÞlWQuo¼ÙÞpô˜ûÄñ¢{ÿ:£öBbbÞ«ízÑ–uµË -ˆ9N×ëAa©½¨òÔqýÅb'Åj×M}*UÌhœ+¢?zèXÝ(aâeÆf²§õ®]ö\0…G²cî?¨lï6ý%Ås–A@|K¢Þ·|‘;€4I@­<3žESº+ªc5‹éMùÁºâ;aâú©jUUãY°¡©˜÷ÿð í}ä—,âãiä¢åúñ›ÚÈW[×3Q&d‡˜ðhN'å,›¿<ÓxÞ¼G^ $ÒŽ«8Ÿÿc¶8ï4yŒŒA@ - ¿[C‰OÕc¿o]OæÝK‡žz€Uá8ðËô,%ÎέuCô6v-­ª’²?xEó]oø%GÚYªÛÞÇù)êó»yÔÿ‘ê›òVL"è3_yšzÍz„<ò/ÝüÇ]VB‡þöGrÔ˜ïÈÁöäÔÚH;Æù²½`×tøi‰ºƒ ,Näê1ä¥>¥/1&Š&ó>žñ䃥®VõÒ82 Ù/®9ŸNåÓ—ßî¦/6î¦Û®œhí†ò‚ ¿¾¦¶ÔâŒ*“‘4W}ãXcUï¾÷á;ÈËlJôíâ4»ó7×q›(}]"ÛHªŸ—¾‘ÿ;ðدE}žõÎ":õÁb6Qõè4ž;«rOÒÞÙw‘),\w ñ.I¾üëïu'^¹®‰§ži8Þz–Ý3_,ƒ'[.?‡"x0ªM»ŽÐû«¶Ò°~ÝΣ'8k 9QofG³®‰±úTQYE®ÝÎB9éЉӴiç}t²ek~ !½»ê½ø[ö§¯6í¦âÒJ’Ñc¯¿x %ÆVÿ fçÓû« Ù¡L+£ªåä—ÐR-+·ˆã"iêä1Ô«k•UÐGk¶ÓÁ9F#ú§Ñ¥ç it»ÿÞ6(€@Gp—u)CÌŸµ£Ä~¾©¤VV4¸[BTŠÀG!EiÉq”Êšê‡}™´ãÀ ½·þÉÅŸÓíWKŸ¬ÛNyEeôÐWPNA‰.ü3Y/IçªhŸQ»ë «œnúxÝ6Úy(KØsÔÀôÓ F4ª¥ä¤Ï6ì¤OÃá¢^©‰ô3Öf›¾±íú…Bà¿¶©?Οd›ÅBUNÈÌá›n F£õ £§òéÍ囸†IâæGTÁ£›½´l=©ªFn·J/¸žÊ+tý”Ñtΰ>$£ IZºâ{}´_òhkò!yJZ½yå—ò—‚‰4…?Uɾ¦¶ë;ñ€€€ø)ÌÜêÈ]v›U/aAq9ý‡µ’ˆì+ÏNnÌîe^—bEKõdþúçß’˜ñHúœúÖ}ÇéüQýè',æûtKÒ·7¦¥Žœ<ͦÔ{é§Ž¢i—ÓM©åZm×3 ‘ÿüª§^a…-“/S‹ð7?ßDÇsòéta™þ¶(£ÀJ’·ÅŸ]<ŒFó[¢$ùœÉŸ–n¼äGúº|fú÷ûëèhV©üùYnÜ_^;‘öì¢ï—ÿʹ÷_^¦ÿä\ŠâãÇéE‹Þ[Kü¶j⺖”ëçt&ÊDcÛk3õâ‚à58û𵋬@ÀH8ÏÉÊ„ePÀ8[Uê8Æo¼¯¯¸|ã.ª¬tÒqî}OcÅÁlá`¤l`˜6ÌÌ¥üâ2šyÝ…Ô¯{²ÞC¿óàIÚ´û¨~Þ–}ÇhÄ€îtñ¸AÆéMk)³I®%"^lùôHÑÏ35²½6S/.ø«–òQï)0­39¸çÜWIþÂ1iHŸ®^ç2ž7eaiuMŠ«Ýß­f¹€…¹‘º§Ä‹ú\n^¢yÉ›k·‹Sn›ê\2~0Ur½^ût#¥&Åêo¬bÒÓØöÚ ¼°à¨r‘¹æåÅ3;OîžÛ±  P‡€Í:ÉåvU8¬VÙîÛžˆ:W î•*“MSUµÌ£–gq÷؇Ez<Ó}­¥DY‹H—€#†ý#i0ë+#—U‡ííÆºÇHÝ’cõNN—K¥²Š*êÎf<ž©)-5”Íœ¯›2†$øÉ7ÛÓes/êÙ%¡Áížù¶wÙßµ”߈zOБüåXÎÙv¤žÇ´u92ÜNÓ.=ãŒV?‹Ù\»))6’¶í?Q»ž[Xí8a×MpdGna©n/od·U#Á¦7òæZ?ÝÀŸž.àOLï|õ=½þÙ·ôà—럨Ú^ÿÜö¬‹O@´-¤­­Úƒç‚ÀYœ•Ž‚òˆˆú³È´}C™5Rs;\†¶íq&Ô𥖒‹\vî0ݦ¾ö‚ V-•ÀZJRއ^:]PJƒzuÑ£çØ¬f¶œ¨vú7²hNK;¼bSéå÷Ðk¶éfÒ¢¹ÛnäÛÞ¹¿k)¿Syò–â¹´¢’mØ›H¥½-Ó‚óò 'έë~8ÈÎ¥´æûýº9NÏ®‰Ô“?-Ù­ZõÝ^Êå›sÿñÊ:]ÈÛù»î\+&7bÒ³çh¶~µ=‡OQv^‰BSÞn›¶±í-(b‹Ž¥<ÀM‡ž¾ÆÔ¢“q€€'ñúTQ^vºÒ¡8xDV¤öŽÂÓépœ¬aÜþL‘„(ÒRÒCÃÁAįPÌž¿fM%ÚjHßT½uúwOa§×“ºŽ’È„»g5©¥²NÑAÖ\Vö‡Sc.mc=ÖØvoÝ ¥üª§Þ›)fîc´çH6a‡ÕÎJb+õ¤Qºpÿ#âH„›[®˜@a5½ñÓ~<ŽÞc'ا^[Φ-&ºõÊsج&Žnç™ï°³ìì.)).ŠÝví>rжì=F.v²Mަ+'×÷7¶]ßé…ÿ„£|KŒ²ó(ó,ìå_¸÷BöÈB…@­ ç «Ç÷ïÝšÖ§ßMÇ¢ûЀ‚=¡ÂÀgõŽ XÉÎ<¶Ž/b°öÙõ1+ã÷Ý_´”˜ýâÚóé­åßÑß^ýœ¤g^ÌŽÅŒFÒ5¬³Þ`ÇYñY”$ñîe_cZ*Ÿý%I)ûHF…Ùu»údŽÄ#ÑsÚ®gê…ÿAKù¨7nBq>HMŒ¦ˆ0+ àã^õâ…-SCIœc YÏýb*#“xm‡Ù«=»ýÃ9&«Lb&½ö2ƒ¤>iItÿí—é¡2e=¬Æ#ü:Ž’#S%Û·/²¿±í²ÏI^$l…â"­<ð£™ÌìS œ æÞ¸ò '`ˆL™»eÚ¾iÃñS.):˜0(†E=ìêÛy0GMu;óW}ôîvÎÊ“w;sÆé :Œßu_j)1syòÞ†½“ë7´O:<wË¥ºþ½$å3’t˜þêÆÉº/¥ÆÑ ­Õ˜–Æ=ü2I8rC_I^m7®ÓÞy h)¿2¿‘^d³.:Í4¤{"í=zŠsüxHÆMÖPY$BŽ!è=÷ËÍæyÃû<½±Mæm÷<¦µËÂo/›ÿt‰0såÅCD}õ$¼‘@ZL@å#uAÏsñäw?tøócÑ=•¬Èn-ΞM@ø Ǽìœ7x¯ÁXx#´’€_k)¶tðôžU±ßÖj\KÕíh5òjHwûÚ:-å7ªÎx»Á)âs$¿…EØ-<¸ÓVÝ.½­ Êç‰=ÿ{–ÓnV¨{|Ùø%Ãf“‹.ì æ¡Ìu0zŽEpŠ ‡Ÿª¯?Y¶Ü]å(\×}²ê25üÓÂüCö0á&üTWUÞšÞQ/|…³Á·¿üŽùS÷h…#ìØÙ1×Fv¯)|…³Ÿ¿þyXh€ô‚^†eu”——”nÙ°vqAx"­îy©ˆP¤VnÂoß¶þšŸŸ#!Æä…I8£§¾•,q8h)ïÞ¤¥:JÔ=.ú\â;W:gýŸŒDt†ÙíÔ;5†t‹¡m2鿟~‹ûÞ§òVù_Ž…¿‡lî¡QJL8…‡‡Q˜LÌUøÊ[|}ó¶Oãњĕ2Ú«…WÄa  óx¹B¦-ëVo9¸kÛûãú+_õ¾RC}Ëîá$¼„Û‰Ã^Zûé2ñ’®ÂWzê!êx0~›¡¥< øj1µTG8Êž%Þ«*……Ee²ÝÃU¢ÚqSĦÕjÕEhddèB.—‹¶̤œ‚bšzÑhÝÕWèùŠÝ—˜ÜH}—07u §H`+ŠY O÷ÂW8Ëg:ÏTX\¦¹]ubDŸÕvžÇcBŒ€ü=ˆØtò$‚^I ãɾ⽷?ãWa yMAXœ6ñøjSj™DfDjˆ€ØÐ‹ÉôÐ?¸ï•ÏÞ|UÌn„§p¾†ù /"0³~¡¥|w_ª–êQõÊò²Ü’ ‡R^éà(7gb<‹È”È,6a¼=*2’c}X¯dޱ®ÐÞìRzáÝ5ú€cõä!‰»ŒÖêIb§J¨%ņ̃XcªG„‹’cÂt†ÑÑQ©/ Wá[猨—¶6©‰êHQhˆ€!êžzùª%0yYV¼ÿögyÙ§N½`ÒÏ?ð³Øž%Ç´~ù{•ž%‡ÉîkÐN‡^ÂVJ”qŠe_„âíë×<¿iåk˜Œ°”ɳ§þ,ÚQ{¨KZª.ö®ƒ–êHQ/h}:¶oÏ6‰ï¼ÍC& ï[§Ä$D9Åþ[z–N'¹Ýn꣪Ça.åÓáÌ]¼JG³Ä(‰ ¯È©NfA¾"×VðÀR=½•Å|’ÍE)&öIˆÐE|ll4ÅÄð$žy Wá[ßôFÚ‚‡wVN?úuM;9=TÚDÀ°«•.=Ëâ+ÃPó_ÑÖ~¿oë÷ûÏ»üª)®ƒ&ëÕ3†SZ˜³\‹p–*vÕyæMZNä0Yµrk”&Kñ€âv:ŠïÝõÅúO?þ°¬¬X›ÉðáÅ< Oá {z†€€–jL[7“–ê(Q_{2tuÇwßœpéE›vŠaQ_çG®¶·žÅ§Ê#&‰ —“$‰µÙÌÔƒG"+(­¤‚ 9\TXPA¬ùC.)üÉ0(‘eE”U£(»˜.ÙÙn>L7¹‘zôq±±$Ëbz#Ñoê÷Ò 8n ÖôZþêßÛÉ«F{…STš! b"½õ"äEÐÏ0]ðWT”9¹×þcÞ¾|àˆ1½{ <8<**Ák1s€N1Î’Úž“bkô¡½;°÷ é†ýÇNÑ€ž]ë4Iuo½™ìš´Èêg»ôÊ‹¸˜ŽØÙÙ3<¼’îÉw‘[u³¨Wyª>–Åiü‚qۇ/wáe6‰ ç/:1»az6¹‘zÝü¦¶—þlYiƒ2•Ü“'ÞªiÏ6 F|¨´‡€go½!èåoÆûÒÛΓ}ßö-»xÚËËò ÷æóÜ8‡}Ÿýœq•;Jž6–}<7ÂÂJ&a#¶òÂFÄ»an#=ô"èÑKÏ@  ž¿ËÐRM€jÍ®`ÓR!ê…¯ñP—Q>­º¾þäÃ/{öðã7>û&ú¡»®6ɰÁžI„ªˆTI½z]=‡ed“›ÊŠJåµZÔ‹#­Û-¢^Ïl‚zÙà"=ïbR#¼$ºD¹SñIˆˆ×'1»‘ýÂÑ3‰w7·Êao V¿¿ômÞg<,¤Í@& "R%ÉCÇXÑ*vá"êʼnVÌsäAf˜èt¨ çëR´µÎ% d[%ƒ‹ñÜ^ÂÆp2a_^3 7ÙçC@FÈ߇ñ -Õ¤Ön&-Õ¢Þx»4ìòyµª¼¼´bóÚUÿ5Mùñ¬Å®Óî¾þ¢:¿<d¢ú2 {YQ® zq¢­ªªâè8ÜSÏf:ž¢>˜µ½|µd0â/,ê%¾uzözK~ù1_=àTu½œã™„ýÉÜBÚµùÛ¿ž–YyPH[íæy8–Aª ‚U„¨ç²ˆSªzTžëN´<¯/êëþ!ò¾Jñâ9&åžYôÙ’ðdpñ|ö/="ìEà˺ü&vôƹ¼ @Àƒ€ümÈäù÷-娵‹† &-Õ¢^8ËÛ¥<´åá-q}Úº~Íöøä”xýz—´éW_ xöØ h#ô¢nbRîR„½˜ÝTU9õp—bw/=õšüc5 ½õÂFŸøK¾ôÔ 'é­—ôòò#BÞª÷ÞWGºi¨‡^ýw»+Çî]¼~ùÇßÔ´‹ç,oBh„€ñ+s£MD¾<ߤ‡ÞSж÷"æ;LÐóµ(Ñ^GÔŸ’m b0ò"ÂHž32—Iö¡‡ž! @3 ¥šÔÚÝÁ¦¥Ú-êù‰-ö©Í€4~ðäAn|~•žšð•¼óËpù‘ûIV^vóåçš²±ðú(i"\Ù®^7¹as›jA_íL+=õ’BEÔK]E¬ËÛ¦ˆz}âu÷Õ릚ýu5„ØÐ‹ÉôÐÝ·ûåo¿þ.geôœIáGVà"@ËȃÇöFç…ar#bÞô†º®ûÙ²k´ù¨X ‹u&åYôé’ðd°‘gŠ1_çŒq¬~þh”´T£hÚ¶C´¥¤N×RšÍÅ8¡¦ÿµ[Ô+šr’¯?¶™2ÈC[àÒ+cØSŸ§­«>Xº2ïTVîøÉ—Þðÿ^ý,fxÿîÚøa}•ýÓjãØ xª^LM¤7Zc “ÏúPõµo™úKˆøjÿc»Ñ.‡^ÂVJ”qŠU]Î’-ë×¼üýÚUëù1»G5i£ç ?´  ZHÀxÆÉ®ˆVù;5-“üj/vlO}¤„È:“Ä1µ#’ñü¹1 c2¶uDYp òw-åÅÖ4D½¡™d.:³´”tŽ×Þªµ[ÔsŽ’¢u¹çñÇãŸÿÓŸsÂ2nD£§^Ddí -¼lÚ¾qýŽý?l=tîeWMt»œ°ðŒf¸Zt¸]‹‹T¬u½½ŒŠ×ðõ×ã‚qnÜŒFÝê¯ËöJ§S“Ñ{e`)f£¸Ý®’#{w¯úæóO–³_C>"_Z¤=d’¯(FO½´€@ëbUÄ«$O1o¬ë;:ê¿0sQ//î•<Ÿ!—Žº6®ÁH@þŽDÔCKy±uëk§úër)_j)ÑÏî2W6Z9ÚÞjµ[Ôs(ÅÏ]ªkžZ®þ„ óZ2z°ÄÞTz…ÅæÔp“ÓÔÊÊr÷ªeï¬XµŒV÷6ªG¯ƒFDÅÄÙ#£-ÀŽÔj.[QQ\QZRptÿÞÝû·o=̙Ȼˆx‰_øˆ@§‹Yþxç™D .h©Nh;_j)ÑÏìj²°žnoÕÚ-êŸìO›gÌy,“{oåÂ4%ê=ß0å“´ag*u0nRùd-f9Ö;8È“Pãó5/"µ’€0¶ž|¥7^^ª¤‡¿0$ÐRßP>ÕRº~V(Sôt{«ÖnQ¯@SþÁήKŸ=wÒ‚ysÖ4Q(C\Jo½ÑäyƒÊv}О{öä‹°—dœS½†ÿ# L%‚Þ°Á¾F/½ôÔcà¡„  CZªcÚÊçZJt³ªiW°¼½ßUòЍO³¦=wÒ•y/û­>;3cÁy 3Ò%NscInFé‘—d¼ýȺNéEö›A[¸,œ ¶"è Ç=#òôÔ‹¨ÇÀ/ÜÂ(;€€@¨€–ꘖ÷™–b½¡:sŸeßÜcÝ,iÏy£:^õwUΜóØ .Ð'äÌýJ¸‰ Œ7œúå4‰÷\QïWƒ¶Ô/x€¬Ü ¶†¨ÇãÅIzëEà˺lÑ/ã\^DðSÆo<´”oÈÐCg¯j) \’>gÞ¸èÃxºJt4ÏÛ¼jÎ2󑹿ÓTíï<,ÒÛdM¾³™{)¼a[/¦62É`-Æ\^8€€€€@Ôã'Qà ˆâƒ€€€€D=îpõÞ€(>€€€€@Ôã'Qà ˆâƒ€€€€D=îpõÞ€(>€t6•+'[4MS|]Ž%K†Ù,èáëë D–@,4Ê   àO.² q©ÎÏž©öRE3r¿ÀôVéãMë\·«êý‡Óµ!ƾúó ûV÷Í>ó¿yûsƾ'_´r¹ªö˜óÍt}nlŸ¿Ð<ƒHëÏ×{ÐØfÌŸù¤¿½$óÐÿ)¤]úðLm€±]æóšžØ_¸ëE¡r^Þ™¤¹;=}³Sö5užìGP €žúPheÔ@!ðàŒªÝ,Þ Ÿ\d=ÏóMÑnRHyÓs[CËO¼hÅÛOk¤ÝV?ç{È­¹Ÿ}ùåÞaõ÷Õ_浄˜ÒãW³h?^ßÿ¶Lâ ÏpÑHûã8_g¾ië]r\SçÕÏë  Ì ꃹuQ7h “ò¦ª©7‡þ¿—Â{pŸý x½ÜØÖØ\s»ïÒ“ôÒ—>±Ð6Ìó86ÈÙË/_f9ÍöÜÞÐòonË/6‘u†Õ!yÕIšªN5iÊS¦¬rɓɴXÓÔe¹©ód?€„ ˆúPiiÔ@! (Ö%¤h7hZ†þ›PétLÓHyÏ0oiä4Z°`¬U%íš$­Ë2VÚo¸5מÇrzD¸-æa¶·¿cþÛ`Ï} -?”^µ½Êq¶m¾¦Q7MQŽçXÈr”·õ0Ö;ÏØ9€„ˆúPheÔ@š ð§»xwÖã‹æ^(‡±iýMd65kz“G[¢hʆôô“åJDÔ»üb0mÉ’ÍÆ¥Xx›ï»«°ÿÃl1ó/c{kçl £™”²ÚóÜÖ26Ó‰«]Ç€€AÔã&`!¯¼¥‘:íñ—Ãz3Žžb®[ÕMQïd!ɼ…Ê.µ¬øk>>õPá»—Õ?oö ÷k²do'…Ôúû›[gž|Å­EÇ9­ÎÒ¨ÀXÇ@@€Ñop€€¢Úß"¥r9«v’¦¼3mÚÛÌ)*™\ec-šm¢bRt¡îT«®VíN>ïӳϵþŠ{ë¿ Uù ûÖ%޲#n¿Ú“\®¾œÇYµµû± !H=õ!Øè¨2€Ô'ðPzÅ1vŽ=Åâ9ÝD͛޻üVÿ¤;Þ?£ò°LÖðäÙ~þâ¿¿w–iÌÃéU{øøÅœÿë_»¹uÅl~‡£ßÜ"qêåXþ¢ð ¹ó°@B‰D}(µ6ê  M`gÔ·xwƒ3bJS›8x}ÿù ”¬3“éï¤jw²Ø^\{/Ü?=»Œíx>ªtßì¹ÝX‹ï6—{Øõ6ƶ–Ìÿt·s  Xº¿`×6.Ã6Võ¦Èî}_lɹ8@B…@k?‚† Ô@‚‚À¦Ÿ¤Ô(5þ㜀~æK´ö絊cnP4* ^$›z/ÂDV  ¾#PbSEÖwWAÎ  ˜`~˜í†Rƒ€€€€@-ˆúZXÀ$Q˜í†Rƒ€€€€@-ˆúZXÀ$Q˜í†Rƒ€€ß˜¼r¥…ãÓû<²Î°%Klc?\ÑPÅÏ[²$üÆ%KÌ íÃ6>‡DÔ@ü•@s!-‡/^4DÕܟÞKQxø©š4tñ‚ßp<øñ“e®Ku½¿ëŽô!ƾúó¡‹_ØÊçþ{çíéÏûF¾úâ §êÜcRLWìœ>óscû°Å gððQýùzÛŒù•Ÿ|b?z:óÿxÿ¥»¦§0¶Ë|È+ Ÿàâ]£iTNж3b¨r÷æqéÎÿ]4ÖíRŸæA­l< U˜fÒÖï¾}Ö¯=ÏÅ2€„ôÔ‡B+£Ž  ÐÓgìæA§ G¼¶è<ÏCXÝßd2Ñ›žÛZöê‹£x¤ØÓ,¶o«¿_Q覩ÏN^ùrXý}õ×'¼öZÌÑÓÇW³`?^ßÿ¾0‰·_˜ÒcàÈÝw¤ã.gånºKŽÓÜT®˜­÷ì¾cÖyüòð#~:üÕE“ëçuvõÁÞ¨€4C€Å÷›ª¦Ýd6ü—z°\dBËmÎUç]ÒKÏbºtèë ‡Õ9NSöò¾/s;g×ÙÞÀÊÆÛn+6›h†E ÿ÷Y»ÝÊT“Bo¬š2EV5Ób~‰¸QŽ“—’·ýb—,óµ4)ßò‹DÝrÈN$rõAÞÀ¨€4G@!óÉ7dh¬‰9©Nç4î}OÌ[š:wìw ¬ª¢\¯-ÓÓŠ[»Ãóx¶²°FÛf‘}Lj× öÜ×ÐòöÛÒ·«ÇYf¡\¨n,Ù9Çr”ÍmøÅ£n»~Þ~¹B¦u÷` @‚ŸD}ð·1j MØyûÝø€¬¥¯.º°æÀ›³Ö¬éMùnú +𠛯N/·˜#ÞåƒižÎª¼Ï¼õº» ÉL»ô¯& ÑÄNÎ7FSÔ2ãÅ]Æöóqƺ1úêÂû5RÖIï½± sõ¡ÒÒ¨'€4Mà-—¢NýÆË½ù°žÃl «š>œûÎ5íN>ævªÝåv—~ÍË©;« /«Þ®Ûf½&Û†,^x»JŠZ³ëŠ–ÏðÑÆqUš5†Í} Œu™máu\žk"†Ò}žÛ±  ¡BÀ*E=A@'À6ëo©š²ÊYåÜÉf3ï¼=mš»ñ£‰Æ,y)ÙQék²ÑDS•Iê.R¯&·z'Ÿ÷iýsÍVú•Û¥}Á¶ú«¿¯ÙuM9ʽþýŒã\.W_O‡Zq¤å¼3ÌVóe›ÇÍhÒdÈÈs6è©¶E}@@  8ĤجŸRIKç†fMoª*·r„œwvÜœ~pÛô‡e²Pø‹IçâÑï½|–iÌö[Ò÷pïúbÒ´?¶¾xæwøZ·HœúêsÕ_˜4Óû²<ô•!·ò¿”\½ã–Ù­Ïg€€@p@O}p´#j í' (o)¤ýqÇí3¿V¦§ŸÉOÑú}å…,c ÷7Ù!uŠ¢Yî2¶É|Ûôée³þ£ªÇÍźÒsŸ,džÇÏ-¬,ø9÷º·*íºãî-C_]°T­Ìß6dñ‚*~éØÕ3©û‹;õ\Üã2÷a»û\F} Û՟З­º'ÐÚgk€WÅÐ"ÐÜàSDC¢íPYÅ17ʲ‚€@G@O}GPÆ5@@ÚM &Ä&læÛM€#ØÔc«¢N    !E¢>¤š•FõÁت¨€€€€@H€¨©æFeA@@@‚‘D}0¶*ê    R êCª¹QY`$€–Áت¨€@ИùıæJ-Ñå¦D“æJT4%QU´(Ê̘4­zÎ6iŠVA*•˜ÍTÊ0•šLZIѶÅé,%‹ê &¨€€ÀÙ0øÔÙL°@:œÀ¯æ<9Ä­¸Æª¤ "M¨2ˆ 1@Ó´o&Lu’Ãlߦ‘zŒ_ pž[“e«Ö=~×ÂôtÄ~÷dä H¢¾áãÒ ¡Kà×óV¹´+R')Ši²ªª Bƒ—µÄØwפxsr|´’I‘ávŠ £È;E†Ù)Ün%“‰e¿¢‰'™Ëät¹¨²ŠÅ{•1wRQi””SaqÏËèta™;'¿Hq»UÝü’ÏA¿“OÿRÑ´O»Z­ËȘVº-ƒšƒ€@`€¨ÌvC©AÀ=?ï.sÝl2™~Á¢zœT!1.Ú= Gй_.Ô¿{ ¥$ÆÙä[w'UÕ(;¯ˆ2sòéDNí?ž­>‘«ðv…_ÊùëÀg¼ð ~øKýZ¢F‘A@ ä@Ô‡\“£Â M@ìâ©Ô9›ûáË‚ÙÖ=%A?¼iüÐ>”ÕÑÅiðzîáßw4›v̤»»+*f‹ÙtÀ¥iO3O\˜‘1Fù ’ÃFðõþÑ(€@˜9ç±t³¢<áÖ(vÂð¾Êåç Ô¤8¿®­Ëí¦­{ÑêÍ{ÕýÇN™Ìfóì†{Û ³w´¥àü"£üþÏw¸œ]œŠ;ŤQ2Ç^©šC3™š[s(V¥ÒBÖ“ÿÊxàT[®ƒs@@ ” @Ô‡rë£î >#‘¡™N¹æ?çÖÔ{†÷ï®]ñX¥[r¼Ï®ç«Œ·î=JK¾Øä.(.S5~¹hÞœW»–ˆ÷_=ò×.Íyÿ¸œÃÑw&(dîÍ xŸ¹±óêo7)¦ 6:Ä&J{Xø`c¤ýd1oXñð®úÇb@@ šD=îð2·³™÷«à›®äžùk§Œõò:6;q¼}æåÚÁÌ\Õb2Myþ±‡×%à/ý8šæÍìh{ ÿ Œw«ªnOd·ZÕ>iIŠ8ûFG†S4;ùês^މ#…}].•{Ýär»xª^Îg‡ÞÓÅ”“_B9Åêé‚Ù§;°¯A®[ÓÄÎ…ÙlZñ|ÆŸåÀ@BD}¨ß¨?€@ƒîÍx&ÆM%}U·é&²æ¶²²´pϳ…LªU?I3娚ûYR²f¤—±Ðý5÷j?wÝÅcéòóF›z^ᨢy/~è.(*ùÞd¶_ãt9o²˜”;ØTg û h½º&h}Ò’M½º%Qožº&ÆqDžöW™9R^Q ÛûŸ¢½G²h÷‘SîâÒr½×_zóùýé%S˜öŸæÌ9Ñþ«!\^xänåQr˜™ñT’⪚Lš:XS¨¿Åbª¹Õþ.Um•­ G´)çð’ÙÜ;ŸÉ=õ#Ò’ãcgß}5w*ÏcöÓ¯·Ñ«¾±.¦8¦^©IÚ¸a}”ñCúP\ŒWÂé·è†ÌÎ+¦=GNÒ–=GÕ=GNqqXÚ›”å¼°PMKþq÷[„àùµ ²†Au@|C@lÝOºÏ=ïWšLæ«UÕ=Zª„nŒ‰ÔRâ£M) 1”M¼Ì1ámzLxíj’ûéMf}]JW\V¡Ç—9÷SQ ¯óry¥Ó}Ë•çšû¦%û¦”«„¿|kùFm`Ï®Ê9Ãû’pêì”WXBë8@ë~د•È{•)—Ëôx7Ë€"Þ~g·® Б ê;’6® Ðiî™óä ·æü=‹÷UMgÓ ­_÷.4¤o7eXßnÄa&‰í´;­|¸pûˆ™Î®C™ôÕ·»Õ]‡NHÄžLr«=?wöëòÂÖ¾Üq6€ø?ˆzÿo#”@ fÌž{>w²? ªtÕlsÓˆ=hHŸT½¾YãT?%°‡mïß[ñv4+ÇÐ2og“ÿ?,øËì/ü´¸(€x…D½W0"#0ã‘ù™H{\UÕó"#ÂÜSÆ 6O;„¢8 RhØ´ë0½¿ò{7›è°c­òŸ0kÌoŸÍøMqhÔµ5õ¡Öâ¨/9ûž~:¼<¿ìqUS~“éºâüÖóGö'v€ òš£z à›ôéºmôÉ×?¨,ìO*fíg/dÌù¶¡c± @™D} ·Ê P‡À¬Œ¹çªü×íÖú]<~°"!%­Kc°šŽf¦…KW¹8¾Ê6ö3<6{qh’@­A‚•º®‚µeQ/13gÏ»ù×9‚Mü=7^lš4v0GªãkˆÝV7.:‚ÎÙÏtüT¾’“_|ý¸‹.-ß¼fÅúFOÀ0õÖ`(.€ÀÙfΙ÷ˆFÚ| ³xïÍ?6wIìüP‹g—[:›€|µá{DÉ+*Ó2³ó.7é’ŠÍkW|ÝÙåÂõA@À ê½Ay€t]Ðkê_Î5€î¸úBÅbAï|§5F\˜Mohô žJ^q™v<;ÿÒq“.ÞÅÂ~WE& @Ô7‰;Aü™ úÙš¦>6‘ýôŸN”‘N‘@ EFôï®ÊÌQó‹J¯7éŠ%›×|‘ߢq€ø)xùiàX M˜õè¼ËÝnu®ôÐß΂ ZC@ü-~9õ"sÆ ïZ+ªœ/jšv©j ÁÖ{ï3ÏØÕÒÒ(w•Õl·V)Ä3;mnE”Oš\1mÚ4wërÄÑ õ  _«>¬ƒø=ž|2º¤Ì½'9>ªëœ»¯åÑCarã÷æ§üfûAúϲµíÒ|í¢¹/óÓbú}±î??ÙQîCš2š ÛêÉ/I½Yd$«šÍ/M¶¦*ÁÇ:yÊ瑳‰´S¤Ñ1þô¶f¿US·üsÞœ£M} Dè©Ç] p K\Or¡Sï¸ú‚>àšÏ¯ “‘1M443cAÑéÁ&UëåÖ”xRµÅDñ,xl¤iNˆÏ195E+ Í”iQ”¶05ó³góW$\õÛv(9„$™sæ~9á/³¦š1°THÞ^¯tVn!ýyáûlämº¥zÍë  ¹7\ùóŸW™ó¶›ó£•ú=â3ç<öW6‘¹?59ÞÍ/Öæ±CúÍÚq17ŸÈ¥Õ›÷Ð7Ûilu÷UWsÚÕÙîSÃܪûÇ,Ò/4›Ì#Ù×&Mxêèö¡PÍ“ÆûœŽ÷ÕÝ/MÄ_ U6ñ ÀfE3mÒ¢L_-|è¡¢j>5Ä Ô¹éCœª àçdÄX·SÛxÍEcèª FùyiQ¼@!ÀBþçÉW¹Wùó¹³ÿ(ånK9Åa•‡ÕãvÓ¹¤ÒÂÝY÷f1ß#IŰ`¯ã Âf2Uü²S¨–çrk,pµs/0”®¿x<¿už„ø~÷QZøîJâl³Îm§¥¥Ä©ü¥ÀÜ59ŽRã(9>ZL("ÌNv6ýñL*«÷²ŠJ*(.§‚’rÊ/.¥9tìdžûÄé|þ\=Å¥)ÊZ¶ïÿÐn±¼ýlƃ™žy`ü@Ý»ÜßJ‡ò€€€Õ¥Lg‘¡?r@ç© ò`18ˆ_†ÜP*©,ƒ/ýjöÜ.¢Ÿ³ûuŽœâ±,à­RËèˆ0WR\”9&*B‰çw£"ÂØæ]ÑmÞYÈë **¶Ò G `žœZ×Äí†KÏéô¿¿ éEŒ¨‰ Ò'•†ôé¦pù[üÉ@^H¢#Ãõ©gj¢g£›å%ïHÖiÚ±ÿ¸eëþã“øKΔJ§óoü•â¦òÏæ>²Âó,ƒ€¿èô?Lr€ø71 à0–'‡õKëòëi—âÙåßÍP¥«pTÑ}O½ÎÂ^ùÓÂysž¨Â7QØYνŽ}QϽïóUSzwKVY›xN}º%QLTP¾Ã4A¤m»N–к-ûhíÖýî2¶é7)¦­l›ÿÛóæ¬i[Ž8 |C=õ¾áŠ\A¼L`Ö#ëϽr]Gôïá圑]¨àA¨ª(Ú÷ÁÀbÆœÇ.5+¦'¸ÇylJB´kâèʸ¡}(16ªŽiM0Ôµ#êMS§Œ¥ŸNmÞ´ó0-[³uDAQéjæüŽ-Â<ëŸ?œ×åÀ5@ 9õÍÂ~ÿ  UýH ’’ãåáRlÞ}„;ì4Áä”W¬ç×5)¶MÙy»÷õ·â‹¦²˜XÛ×¾¾–/óÏÈXb;áÚÿiê½qq‘î«/M†÷åà.ø°å î³™ÎÙŸÆícþâÛ]ôñÚ-×»+´sÓyâ§ {èo\y€@{@Ô·‡Îè0ì°Ö“o‡‹úÙÿ\Jyüù]tQlT$Ðnºl‰¶ôÚ±s¡×Dý‡k¶Gñ ôŸ]Ü&®M•GêQXRFÿ;ó:f­ç//$÷Ì…~{Ëeb“Üä5]ìY¹ûpèß½Éã¼±Sê!©#Dý©¼"y9c·Izé©ûï/óFù;#üéTÅþYÐÿñ„a4õâ±|‹¢cÞma±˜éÊóGа¾ÝLÏ¿³2µ¨¤lý¬Œy^Ș½Ã×Cž ÐRø‹o)) й-AÂÒÅGGvx9&Ž@ÞùSº„£~¬Ý²—¶î;æ“2Üuí…t÷ÔÉ>ÉÛÈtÉß‹­šï6™M?úÕ´Kèg—Ž'úÆhyo{Ï®‰ôÐW™£##ìš›–Ý›ñŒÿ|Fô^5‘S@O}5Š ¡L@Ñ”B·¦š\nut|úèÈ0êÍŽ…2mÛwœ¾Ùv ¶™Í6è?ËÖÒ¶™Ô¿{ŠjSŽûÇëË©[r,Ýøã z³I,ôçÞú’D¸WT:I<ÊÌɧ]èʉ#õžò·>ßÈ£ljtÛOÎ×ÏÙÀ×ùlý6â6©g—DJ¿a 5(¥%_|KÇ³ó© ›"?ª?M7¤E·ÆE?D+6í¦í\Ö†zÜ.-Yþ-mÙ{”¤7òâñCé²s‡ÓA¶9ýÓ ÄÿÐìçÞ!vV¦R˜¥ HéñÿË¢¸<±4‹Ë(é¹7¿ ¾5<6î8DŸ¬ÛÊ_ *tÍ[¯:à Æè¦Fsø ‚Ôëݯ¾£œübšïuê"×nCû¦‘„2õfzõã¯éPf.qjšµ(ãþÃzo^¡cò*Ë/{‰C:Ž›9õbeäÿoï<£¨¶>~ïÌn*½w”®iòQ±‹XÑ÷,X  Øž¤èJ6Ô'*RÄÂ÷l Š *Ò°Ð{ïB‡²;3ßÿL˜°‰)›²iœ«avgn›ßÎ{î¹çžÛ’ל” õŒRhÇÜÐ{«o}:ïê4ãì8;©$Ë粘€'ÖÔ{ÒàÏL€ ”YÒ0ŽPåÎÀ§ti2A‰O: Ÿ×¦G@³. ÀOB?yö„× ÓÚVMë‰U›ö rGaíŽ"Íå´ëæ¿ü.®‚½ÿyÌyš`c†9̹äTqþbŠŸéÏ~\!šÔ«%žê£h³Ÿà@Qnø:µ¹JŒxòNq]ë&bÖ‚Õâbjš™&¿C³x}ÛfHógf½<Ó,_·ÿQ”w“èS'SÐN:}õ -רôK‡„;{aLõè½½;ËëZ±@_¿]³†µÜbêðž4°4Êç2™€E€…z‹™(Ó4E1…zÚ,¦¤Ã¦ÝGÄ”Y‹ÄkïÏ$hÞÚ½mfüÄãwõ[7Å"º–¦Ý9„,Ñ£} ·ébÛþx3îúEWì¾i‡œÂÁ„“¦°JùX6îæ…Kÿüí6åýÌ€›wScN—ªU ·t»ÆÜX§<‘€ºcÿ1Ϥ¹~vc€ñ`ß®æÀh!úe[ ”Óšº5+‹VMê Z°»õ®“§êèiçЖMê‰úµª‰6°Á?~êYÖ&Ào f@V€¡Úÿ¾æ†ÌEøpú\²xÿóù: ôðN9-:bL²+õ¤XëñfÕÊAÚ­xŽ8”?;<]ŠZ¥W.™ Áæ7ü0&P.Èc—qQÐ`KÒ‚—d€Íµ [þA÷ô‚Hý,æ?M¡ý¶ ¯$ô“9 ß×\ÝP¬Ý¶BpUs·ÊÇ üS}°øüçU0Y™OÍ™£xjÿ)ípÙ¦9Ù—lÆãç•E"LU¬Á™$y¨^wôì`šÃto×,3 v:fšÚ¬Ùv ónÝ+vBKNšû£ÇO ²©§P¯fUÓµ'iÞÉ~žB .ÖÀ§<Õ‡-m®ªg®98tì¤yâÐWºEm7óZðÇVÓ§YÃ:t9×аNus1òí=Ú›‹tÉö¾°áÓW$ÐÃ~^Ã:䓚K»±"x*QTã<1I…`É¡ä L8!œÓÔö:¦Ážþé©GK¾\"¸L€…úË,ø`eœ@}{Ãï¤"ÏÌûmÉÊe>ðLfòsÿöÍÍúÒýO¿m#?øZö¯L¸7¼Òd”޼ÚÌY²V„ÁãÌ7 WÃM¡*nƒ;ÿ÷-â?ï~ešùÖ¿0¡S›&¢õUõMí¿•þ!¸B$s¢÷à¹ç¹±3Ä;ÿ÷3Ì€2Ö0Ð"̵kˆÑgA3ù›™„´ñ$ä_Û,ÃÇ=yÁ®¥0AÒ0+‘!´“PO€æÿ¾IDNþV†Àÿïû{›¦9V¹yûÃãMíê•ÅÇs–›÷›WÜÜ®t§Ô¿—¸¡cKµz• þB3ÒÜç’G8¿96ÿi‰Ü2.åóuE÷C˜yHÙƒu JŽ-bŸÅhÆŒ\Øf™!j„°×žçÍýWAœ(LGr`L€ ”k~[¼¹[ï~­vLh-°$¯,e1‚w›ÕXpºàÏ-ð`s£©Áö¬'Ù°+ðð’_ á™ìö=…k¡^¥Í/ïÜ®S½È>Þ³L+®Ý<Ÿ½­øžGËóç¹ÒøLæB4ƒÐ÷úk,ÁíæÕ.Í5´ëM·¤¯]¾xeiÔ©°evë{ç)ŒÇà¥(¨c«Æ ¹=õe EÑ 'Έ¾Ýò÷¶C.`c>þÑôjDœK:ÌüõOqfnʰ¾2îÆ8Ö”È;@Ó´‘òB—ûÖëzó{×._pª¤ï“Ëc½™­Ô|dL€ ”ׯ¯|.YÛÁ³1müÒf$e-†ž<ÔôêÔJt¿dzSÖêÈõÉ @3!_aã¯Õ[÷•[$4£AÓ½7w}`¶åÍ€±07ë©©'/O´y­ÏøyÕFÓ«K›«Ä€>Ími?‚ÝX¤ —›Â†hôó™šóœ68£ºÐg°¨œ6Xûmý.ñÜ#·`íIšX¶v‡éjõW˜`ýIï®mÄM—LÖŽ8‹Åæ«°ÛIxƒª"þyGsA:í1÷· ¦W© ¤yôöîæ‚ëÂÜsniÈð@|’¹ßÁ_[÷‘W%]HñZì˜ð÷1/‚¹ÝŸ/¯XS_^9®7¸‚ ¬\¸0½GŸÛf»u÷혯 - ,¬m¹¯0^‡E²=¯ki )¾*ƒó-4+ѹMS˜ù‹mûŽB.+L2ÕÚº÷¨X¾n§¹Íð†¼8Í´²kêga­Ç΃ñæú¶˜ý˜aº.U7ÂÆc´Øšv/þçÝÍi½ÅÒ5;!ïž½DS¬¡]”ÿѾ™¹±ÚÊ{° |ƒ§˜34X?vòŒ aþBJº¹!màF»1߂ݎiÍÊ”Y‹ÍË¿ûqnRWmÚm üäij‡ýoê(׫aÎ>ç/K3Y4SØÞ±nîÒF91MÅ Ýó–üÖ3>?gYœð†@ÞÆœÞäÀq˜`¥@`¢cä‘àJ~ÝѯÎûn#§·,ÓSK)T‡‹¬hS/ZÔ[^U¬¤9§p3äZô?‹ßþ(#–BÛí«@¯Ýsš•ÂΪb;F4h ­;²©§MË(ä¶Á™yÿÐfc#ž¼ËÌÏÓ”h@ïN¢ó5MÅÌDКð€E3,´cq¯Ž­EeìѽC ,Ä>e.L'?¹]­Ý©l_›ÿÐ ‚vLÆÎ¾äñjpHDÌ}Ö=ñ‘ ”öS_R¤¹&ÀŠÀ;#F$;Æ€x#fôÚÃÖl?Ø£Csyw¯¢fµÊÅ^gXñ `¿÷È1cýŽCéþ6{ <–Ç»Õ.<¿‚ _÷ô¹È™†Ï”xM!´[lÎÓ\9o†æÍg´÷AöØ(ï¦,P¨œ±f€4ö;3="}öã óý„Ù–#ÇÏ”Zû¿ë†´‹²–têܳ¨ÎœÌŠñ&PX¨/È\`¾#àpH¹G‡8Þ‰î”×0}?~-šÔ“×^Ý@’IM½s`Þ€¸Ü°ó=ÍþÒ¼æmº²í ÚÁÍøé·voKæ(>]@KæKÞo68ó³çlœSþå¾ô¯ÛÌoêPq°›²štæBÇ’(‹Ë`ž|6r÷,„?3&À|M Îñê‰8gäkªêw5Êrî;||ãœ%ëŒèé?ˆW'|­-]³Ý×Uð:ÿ‹©iæÆOd|$‘ex ®„"Ò†`í[4ÆZGå‘*²XŠv¾¯ÔRwBxþf«äØá©Ø}Ù´W/– ˜ -\%ïM´£/¹B%ó ¼68+`öæ^dVóÓÊ¦É mì¶k ¬@¦ÑBÖÔ4— _K*œ:—làf–Ty\°°Po‘à#`‚ÀdÇkÇb£"Þ¨¬Þb*ÝTJj¶õÝí‘ûÊ!ÑŸ ÚIÕ›ðé+ļßÖCÐI5z“Æ›8ä—›võE ÷„´èñJ Ø-WêºÞdˆÓÙ°<Üsh¸ó¸^ùµ~ÍjU#ߧЦ]9™±”ä½M|ï.my»yõ½¯Lá:¯ Î Z7ò}ïWØÅøŒ‡žÞú?”õ'ä錜°x6ö§ÅKï|núÖ/hþ…‰O»?ï=’¡^_^˜ôœ† …Ö˜q`L€ T,¡1H©O _»ëµWIZdW»zŸÝ$ õä;ìÙþ¢ vdÍ+Ð"¿!d ó¾‡6é˜~ÇB]Z4®‹Ýpƒq¬#ª¡N”wÔì£=ÓQÚs.Šó¸w 9•}öüEAžJzuj)†<Ü×4ZŽM†Êb¸œ–Q-»r¢,ÖϪSHl¬¿b_x}²‘ç¥C€|çÏ]¾aeJ\TÄ¥S .õJ'Àšú+ý àûg€Ààˆè¥®½[·vu1䡾J¥ïÎ’¬Ç A§pâL²©]WU4oœ¡m¯_«º©y'á˜iáCèc~¦HH§@‚0msÿãòõ¦ MD(É,>¼½g{S»Nñ²ûå¦sHX¿ÿî.¦@Oßó³ûÿsË^ ®äšÏ3Ð&;ñ'Îdú§…ù…ìe¯€OƒÚè‰B»Äzlt…ø§!  ·!j(kuó¬Opr²í‚!¡øf'—’úLkM¾øùÌJí¦çå«¶°ç…/©â¹&… õYpð&Àʈè7 Cwü£}sÓÄn+¯µ&n4+ac—Ûm å¹ñm×"ëzLÒô¯Ý~»„êæF>´[(ý‘ðÿ/Øã‡ëË×íÂn™×Asž¡ýÏ=ïF¹]27ò±.’YÐ…‹iXP8 ê—=ànš5 s¢±ϳNÁ•úåeéÓÎý nÃë¦9B/–¥ze¯Ë„W^I tnø}˾wߨU,Üggä«ïGO‹sWh‡ŽR ¡36*l çÿ½øªœ/ÈF lô~Ù*Å_™`Þ€Ç=™² ¼dÊâMº’ˆC¦7 žƒ‘ƒ '`b³Y¼ÿÛdç¾dõ6ØÄÿ•)Ãï¸À`hï7šç›7ª›§Ÿn2û±‚ªHspA¼ ç¿ìR“¾“-yÒésVô\Ê%Í0™ÙX<祈gð,;‹Iãï|îAÏ(eîói˜Kí9r\…€öC™«\B=GŸ9wá§iØU9äÞBÁoÌÁwhQî\Ìœ-^½¼<¥J噩ΰ¹qNÖÐûŽ:çì Ò{C‰ã0&Pæ Žˆ¹^*ÇôéZöú¢Â:u6C9LðÉ3çŪM{2²„AÚÁÌŇÝ`#OÁr]è_î–Mê›æ;+`.py/[»3#ïKÿÒ bfH«NBúF,˜¥@yS]HàM†é ¹lT·ºé*L„þ¹MXH.eõ·Ãu­›e.Âà4ØöŸƒÖžÖ”µð›iço6ØQÖê–S}¦Ž ŸóÃ6ì<(ÞÿòW~WÅOà|rŠiF7zâ,÷âÕÛñú‘“E%µ% ôÅ_çÈ N `ª¤‚çÏ)˜`ÅN`xLLm×EýófMëbÁh·bÏ¿´3lS²£3v¶½÷zw6½ÏP½H`ûrþXš"*ŠÛ{´ƒW wä—ûË_þ0ýr?yÏ ÂSKnÝ­7 M?™÷|³`µ¹`·ZåËÚušñ˜ïû_üj&鳦ëZ5Á_c±bÃnÞo‰áöƒMñÃÒõbä3굅 Ã*+û‘Ö Ð½ü°l½˜µà/Ø Kq[÷vfÚìqKë; X0@Ñ !gNŽŽ8\Zõ(h¹Øx-642:yÏ¡ÄIxn*Á[’Ò»k`&îŒUmòßQCTH›]Qí‚iiá³ß#iÅsrãG‹h½ñËM ü$þËÍ›4édψA…g ²tŽr+äUO+Nö#yÉ ° ›zÙ4({œÒøþEK×îÔlÒÞvŠsdÖiŒÒ¨PË ‰Ž®/Rô±à~?›jô¼®…r#q ëT/`NWnôc'ϊ݇Í¿-{Žhpû «5å4šÖ6›òÞdÇèKSgW.#¾ó²Iàò[¹lÖkŘÈB ÄñN-áN=Ö¯Ûµêƒý®Ïr¿0¢ S wfüb`¹À±+ñˆ¢äUÚiC1×.ýe!'1Ðó«U½ŠÖ¹uõºÖMÄU j _zË¡ÙÚç`óî#Fë«êI2‘+íxòœøüçU†¿Ý&«VU‚±ýÀ¸±œ dV†µ$ÚñÓç…Ëå6GšX›p QE~-Öú5.4ôò’Ò¾!.Ÿ ä@€Íor€Â§˜(»¤–ú6UR{^ײìV’kVîÐ&\Ó¿ÿMƒ w(¨zàëåî²U8Ö¶ §co„ÜW¶Œ±+8÷Š|{ÿ›· ‹€wÁ¤W‘ïÕóÞŽ%¶D±êZÝ-[Âh¿„ò« ðׄ Ru豫bw„ʰÏG2©ášŸ°!—q²ýa(ºã!ÈÇC-¿K·)kˆ–[ŽéžeÐŽ·Æ‘¤×¥!_†ö>¸QÝšZÛfõUZN¦U­ä½PŸÉ=kf â“ΈC 'Åþøú¾#‰dZ£ÀÌ( «ž'ÈJêä¸Q£ÎªNÄÊ êËÉÅÕdL ƒ@HDÔ÷uªW¹ëÍ¡°¦žŠ"øzþŸbÉšíäÛ} Ì-Þ(r†œAŽ^rL¨–ì¾8ƒÂÐoÂìˆ9(‡wwíê•hÁ»R >Íøav û%X{!`"›¤" {"¤cñøÅÔtq ­1K Ÿ:—¬Ÿ9Ÿ‚¼Ì‘Í*¸ ØlÄÀc™*Õï몣W:“˜@Å'ÀB}Åÿù™@…"08"úhŸÄã’6JâÀ K`öÂÕvæÆßoÅEEŒ,l>œ®`L3 s®Nº” åo¡½1UM13P3Áøs°ChO‡Í{2\Ž&ºq¢ü1¼öaG×]B5vÔ 1[ðtjÁjı™@Å ÀB}Åøù.˜ÀC`ðëÑw°÷§§î½Q.&PP03³­67Ì‚€ø~¬3ò¥‚æÁñ}GÀáø$ ^œ¬¤ôշû¹ ·”n­F ìÆwÜ9çòO€…úòÿò0+Š€Ã1Ó/^Û½·A­ªõß½&³ü»¢€"Þ,-òühÎrcû¾£°‘ïÅEG¼\Ä,99`L Là¹ë2ñ3p%˜ð–ÀÒ¥³´Î7ßröü…”ûìv[™ÚÔÛ{àx¥Càб“âÝÏçkGŽŸrcáæ³èÇ—NM¸T&À˜@ñ`¡¾ø™rŽL€ ø˜ÀÚe‹6Ì]ü[·ZÀ×¶¬]½ŠKäìË3rY¹|Ý1íÛ¥zjº;K4ûÁ†þ—ò|O\w&À˜@v¦½Zö“ü 0&P– `±œlzuÜ7›ÊìÖµˆŽqŽðu¥X%.š 0&àlŒê¬œ)`%A`ˆÓÙÐH˰såÕƒú÷R®oÛ¬$Šå2Ê”´tñýÒõbÙÚ†"•$C×_ŠŽø²T«È˜(ê …1&PV s¼UOÓ\ßb×Ê7un-¾õza·å꯬T›ëá#´–üÎ/Y½Íœâ‚·JãC?[Õ×':^8ç£"9[&À˜@™ ÀB}™ø¸L€ …íZ)%½Óéá5«WÒ»£»zm³†EÉ’Ó–3I§Ï‰En«6íÖÓ]ð€.Ä\©Ø"c£Fm,g·ÂÕeL€ Š õ…ÂÆ‰˜(‹GÆÜ¬}š®-;¶jbô¿¹“lX§zY¬*ש\¸˜*6î:$ÖïCË3&PšBÆ«j$»_ÅKîyl'_­N*:lî•Zˆà@ÿÒ¬Z¾e§»ÜâÍ{ÅêmûÛº·“í[4Ê7MyŠ@ifÌ]©W TjU«$jâ¯VµÊ¢rP€ Á=Ý¥ Ž.p8{!EÄ?-Ž$1Ož˜…1û-UU¶hš1Ëf—s¦8Â7•§ûçº2&À|A€…z_På<™(3hËù-þ!EQžs»ÝÝmª¢wns•ÒåÚ«E›«ê ?{™©ëñSçá­e»Xµqï-Ø+W¦T¯ä?fèŠÍVq¶‰½ÔذóàEì¼P3·¦WËýG~³#˜uÙ  c£*äfU7þœq0÷4|… 0&på`¡þÊûÍùŽ™ÀKà¹Èqm]º;~îé†^fz‹Æue›f dÛ«ˆ&õk–8›£ÐBï8 ¶ì=blß— ÅЄTg¡jè.áAvÙ=7u÷ÜØ±Äëæ‹çÿ¾E|·xÀ€e$vt}‹Êæ˜TÉ­¿Zêîꆢ¤I¡¤*†ÄŸ+5°šÿ‰wFŒHöE]8O&À˜@E"ÀB}Eú5ù^˜ðŠyË‘‡’zЏ]ÊÝš®wÂÁþîæë¨õkU“õkW kWõkUÅ¥%OKw‰¤ÓçÅÞ#ÇÅ.l˜…qõ ÓÌMaN²ÇЌϤÝ?nªcÄqëFB"œ3`7þÄ€>]Ä=Û[§ËÝ1-Ý-f.øK¬Ü°KH©LŠ N›ˆ•»á 3&ÀÊ(êËèÃÕbL ä ‰©–bÜ Ûû[Et1 Ñ‚´Õ€OØ}kukVSƒüd¥@?›üJø † x ¿ì¼uØ‚ëBƒxÆQ.w†=8 ñ'Ï^'NŸ×/¤dð”/LJh‡Sä¿Ð®ÚèO糇™3gª‹6ïþe<~CÇVâ¾]ÊüšÏ{À€I,_·SÌ[±QK¾˜†…­bÌ´èˆ1žqø3`L€  õEgÈ90&PÁ ½dÓþnÅÕNê²!ôv0 iª*¢ŽnˆX¬Y‰4ûùÝ6v4ðG1@Ø ÙvŽû°»é>Ch㜑{óKo]w8 %^‹Ž‚^û5?›rg¯ë”¾]¯)¶«œâ<&$¿oÚC ~µsÉ)*8,RtÛðÉΑۋ³΋ 0&À2äÛ)1(&À˜ÈJ€„þ…ÛUWÝZMÃæª*5U3T= ‹8Ó…KOsÉô`W`ZUñò‡CêYSþÛЈñ­uéž­ý­þ~Z×k®RÿѾ™hÙ¤ôÝu’ך½G’ÄŽ}GÅ–}GµÃÇN©4¨ñüLwL‰¾¤ðwÎ)™`L ?,ÔçGˆ¯3&ÀÊ!‘17`¡ï³Ø6õÌU©ènݤž­y“º¢e㺢ÖЖª¾ ˜q0ýÅJ<%ŽÀ=å¡ã§}‡ ¸¡„CéF¹«° Ô÷~ÁÊÿM KòU=8_&À˜¸LÀ‡¯ýË…ð'&À˜(~¦××é…4nS¤ÚWÓ5Seo·Û´:ðý^»FµNõ*¢Vuò(‚ì"0kðGkLÁÿÒRU:@V‡x—¸˜šžùךâôù‹âÔ™ âÔ¹d#éôYíô¹‹ Ö˜ |¡Oƒ ¿ëþªøµj°}ñ[#Gž/þ»å™`L /,ÔçE‡¯1&Àʈ¨æ°Ù¿Züv¨vKÜXð+šA³^dgüª"Obß'¬Ð÷#ßBÛT»\_WôÜæpô!í<&À˜(E,Ô—"|.š 0&àk´Èö¤x«[¸kè.£´éÕá ¿œúT1 KHü8PÓ›:{ ’!¼ŸQ„vF±)gt¡œ ¬âŸ8á•WR|]WΟ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&ÀJš€,éK²¼5÷5n.]é·èÒè …l á‡òÏ )ö EþQ×ß¶¸ñ¬#)%Y§Ò(+4"êÙ€@eîûáብQ~Y.ÓáXb;æ^ñ´^É>3nÔ¨³e¹®¥]·%KzÛþس¢—aè½P—VBÕ !4´­ã8nUUué¨g]K»ž¾.ÿ¹pgSMˆ^S£#>÷uYå1ÿç1­Òu½ý´1³ËcýK²Îf̨£‰”~BÊ.B×›R !“…aT„X$þ5hÐÉ’¬Si”Å}TÞÔÁç)m‰S£F¯Ï;&_½Ò Ø*"€µ÷ÖÀÐõ†+­;„ Èô¿ù)ãvé£fˆÄ”ô kî©÷?ÅOŽëümÂÁŒ‹û7$"ª9²û€RIC¢¯ ø°®­ÑgÇÓ©Ë­`±C£ìW§EGü#¯”†!§¤\4v" õÙ@]^ã¯q2Uÿ —X¨ÏƇ¾¾ûQ•)Æ…W~ß½,Ä0DíìQ¬¶¥¹Ý"&Vî@Kx¯G«›¦÷é³Ô=®7ßñ\2¤1â¢MÑ û°"§:#{“¾(qB"œ_a²>Ö>>·|\Ré„È;¸ÎB}4Íè) ã\*P?ÄÝNsNÅ®„Å:¶åu…9Õþ³©=t)GC ¿Ë0 B|ƽ™‡ŒÏ:Î$‹TWÛ±?*ª³ù±Ák `p„ó#ô‚õ)=4y§pØ+löã£6OoÒ w|P%Í}v§Møõì¹=·4ÜGåF&ã<ž…áÒÐÿÀ7¯„ú!g7Ím<‡ømñ>Û%æìJÏùGŸB½“ó®_-K  ¨8aÝõ›®¾§îR]ÓfãEÙ=¿;Ã{´´ŽC´t}ûê{꽆4ž¹P¤­ Æ w)Rù^*â'äŒlÞŒw]∅ÖÅwA•öM(óÿ|WBÉ䌑cp¸³gɔƥ„@t¬ú¯Tíü.¡á9 ôÙó‚8ÒBþÔßw-Û8vš½Köë^}WD4ÄÆhS_#þH!ítCþ48"jœWé‹Iâ{)åŠ"dQêICήÐìÅ”zE Pˆ˜ût·¾¿ûm†K¯Q€¤å*ê ßO¯ÜvÆÔÏ ýY…þ¦¿)ÐçqhKvÄy@ÓôÕí>‹ÚåÇÂõ)àÚâ|É3Q\ú¨û ·k·¯ß»5Åð (oš»’ŸÇm–ùKå©"^w ð[oµIõe¼?7š1=Þ½ÂQæAs‹L Âhê×ôoÐKOÓ~ “€S1Œ@¤¿öžz=?Ü蟅1ÉQÕj_Lvóö¤°0Ÿ™#ÄFG¢®¿;Ÿ¦p‰;t! þôi¥rÏ|pdô£ÂÐÞR½3 sEÁu+¹g^†®tüò“«ÎœMûƒãÖ®Ú#´µ¡©§dN_N¿sý?Ÿ-¸,åª8gøgVÙT7áóüõ³Î÷Ñá j‹×‹;ßRȯÜôQS q¼ÝrªcÄñKœ~ tV7tù$¾G”;.² T¡~mÿºÝ C›­F‘4ãH?àØÅôoׄt¹·kÜZWašfÄ “¦ÉZR¡1èÂè àçÂÐßA9]í¯3Åæˆîèv4•ß&ÉЦ̶¿úžãå30Cø fßÁì`åClÀºWü¡ ÕÁÉB×à8gÄW…òzôíº®Å÷–¸¾SQ”Ñ0EJóì(Í2Ýú{ˆÓ }ÅqÔkrlTØ{8šò›9U‹é:L¡·ºˆ³qÎÈÎVÖ1#žøYêÆ †à¼.…1G±Ù>˜ê}ÀЇÊ##Ÿ&8·YJõ?(ïwššMuŸý \{!3 5JQñ…nˆ†à°wš3œîCÌœ9S]¸q×_¨Ç2Ôã+_ðY,¥29Öö™—ëÜxh%îÄ]Tƒ\°^±‰±Žˆ5ÿåwß ¼pòâYÙÖÍ¸àž„AÜÝÐd¼Kï[ùÑ‘„ÙÐˆè©øhëw]«’IÕ ÐOÄsJÓ·…XÂçósö:{Òº!è_ ñ̪5ÇñdnÏh^ÏÁ0ÇØ.—ös€ÝÞg¢cä«2C#cnÔt}²°×þ‡áNú@B³ñ]7Ÿ½M;ß@ûyí³ 4žË¥Ð~0Š•޹=ã%¯öï‘ÅågÔ® n}1ÿÀó¼maN¿-ß³žÅׯ¯|ú¼kžÖ;Q/;ší|{ú vB"ÑhkÿÁ¹4´§~8ž69¢ÕKxOÜh•Gš|Ì~Ü-›Ôîj¾ë0pÐ&.:âYŠ7$"j°fÈçñ±9î{¿¡ˆ)Ó¢"&[yäöX×­ã§³¡žjà}"'`Ðô·çÀ/P.p]=ã¢Âöâ½ég¥«HGâ Ò\éKñ›6-Ê}¡MvHs¹—tšùq¯õŸI*J^xv¾À;u(å‘ÛûÏBL^}ž£áxŽúÀ”Þÿ™ÏáX³“0mR5cò™"*Ù)B>YZy w6v»õ•È|¡ ¯ª…²Fy=f°ÂB}¬9p ºƒŠg¯ÿåkžŸŒúè?EýÓ›r—´É‡p·×k.-Ίe ;†ƒðý†ŸÍ¯5î †;Æ7úàáçƒm•îuÑʰ`{ðõAÕƒÆáÝÛKýA+Å›v’0R‚Ô ÔÐyºÜ{›¡¯¦ïiîs?¢ƒj/ùœ°+Ý!ØoÂäo´h®«š,F+ã‚ ñD"l<»«•Ô)tÍ ™=ò©VÅþ’%DYׯ”cLœ:l‹&Ð_‚…ýÓqÓíבßm”^û2òÉùÍë9˜ì½mæ|ªÛý„g]4CÏΖ8GèEzöѾjZ×nÚMš­A†¢´ÛüÛ ¡¬Âs˜åý×3NùäÖþ­2¬cæ3êÖ¾E=æˆ ÙÖÊÛx.ß\¸y÷ÓV<ôsÌzÚ”þv»¼ í»®ë¢þ]·©ÕÇâ^`Ë.gS{ò·Uí'ÛjäÑk¨cl3Šãp ±gè>•£'úÒ¹Œ ?ˆ{3íž!”=wO*ÿ1Am„‚GÃ7+ºï‰££ëB _„:þ”“@OYÑ`ƒuó]‰¯h§+ô^²Ä––îš ~™ïê"Ý¡a´JOMÿÊa`˜U„ÅÇíè§Lî™Ï^¶÷c~}ú²ŸQ…ûB#Þ2XTá|à†*¤²èR¾-lR7kÞôQ”G^Ï8]§ç¿¸ú¨¼úIJÖGч¼}7ú8¡(Ïç$ÐSœìAêòVôçô[q¨àʽ¦Þ•.&â7ªW¬¿“¡¿¼îž:ßtž{üwoó5Äév°eÓ5]4Ns»žG‡¹jæB:tªÍ¥ªÜ;&|ž•'4ÍÿA'ziÒ†QØþ|DÔÎ4Cì9öNˆâŸEÔPGt‡)ŽðMf Í„û+@†DF›§¬t—AZõšýoGŽ=( ÷_HcFÃ*™WÐÁnÁèßy)ÝltÔ}hm¾g ãȧ•ªõ&¾ðBš•ÎG¹ZÍÑÖ5L¥O’Bµ¾CkÁ`iÓé88P^( VðIEC•Úš 8xñÏ×Ýâ­—ïÖ˜àxå”.”‡ ~Š{x"h ü{c6¬¹5îtꆱ ñšÃp/â¤#nšÕžðùÞ5; ·û|ÞwTKƒä³hãßëš ëü—ª%»’»ªvùo|Ç„"Ú­"¦M‹ ·f²>Ä V0f^"0{ñnæ€7Ûo@i­S©š)ÉúB°ýšÜ1Öù+í˜tx×+¸ç|×x„ ž‡¾³fL£wvæÌI¾éu£i¤ÑGÕ†²½¸ÁP”[³¥Ëò~Äs3&¯> ïõŸðÜ-×eú“È'’òJKù°Í—s1U~¿¢ žçTEùÂó»õY3\Ï#Ïaxñ=|%»¶LMK{¿Ÿ9бØÇmªÓ¸iQÿô6/<"á½á[˜Å¼3°[U<4$y„¬Ï¨7ÏA­Òç¨KÓ¡§)dƒ@g$ ÓE›‡ÇÄÔÆó[3fK³^“™Ï™×ÏxÎí?k¶—¾aÆ &7YÂ~Ì<™m m§=å)$½oµ)¸ìI±ÑÑ·È’Êã ÒÍ×.ÙNK]Ç FÎÇËÿ'°@mì¢;¥7ÚSµ½Ç;µÐÀê"Ç…Y ‰ò+µí%Ûö6½|>ëopù¼Ð1{0y&C‹ð8E}ì23¶*ž¡pßÜ´áè1sfæ,¬eŠÒâyœŽ6ú$ú4%3 B¿6Ã2ëºt.ãàEåý3žë³—µÈ<ú¨K½ì/g[}Ô-Ð/~‹†8ýÊ˵Éý)FÀó;)Ôg+ºG©Ü)\YWlåùváAòh|Ðyݸþ¾†;Í9ºÁ›ì¶ê.-”Í-zbvwR¨ºZCÒªe °‡…Pcþ6RÓ MÄÂìddÂÅ• ânZ“5õMÚ!@ÄàrèѨQúÂS;aÒéœþø´Y*ÊT+½‰a#>ÿrP¤8zù[žŸ²”ç3M¸M- L¾ƒ­ü6kÚ 5Þãûß?Jñ+¦ o9fŒ=Á@ÖUG¯LÐc`jõ)„áñn'iŸ „Nܨ|::ë.ÍüP.&=Œ,ϸj¨GÌ‹Ùÿ‘¢ NýÓ¡wqÌ8dSÁ¿Oø¤Zµ”ô³¤qóI@§ô"2þܫ̥Àú‰ËkE(ÍÔ1YeÃìϨ7Ïi¯ag;ÛíƒåCžÄ`ñãìÏ•g¤«hOšPT=™¾[í%ÕzÐ ðŒÿ­ý[ùe?ªš-KyT\~²¥ô‡¦>í÷C+Ý%Iê=]ñ_oËáø ‚$Àu;Àìÿú¶o½kUlÇ´±ÐàýÐÌ~¡tA˜·JF£Ï,ï&C5Ò0c‚öv¹Meÿ ¬r!ßu@›ÿ/Þ£!!‘ÑÿŠ‹ Ïq0mů¨Ç´4ù¤^s@VÜ÷ˆw[íó©gÿ…|§{•·"Gaðúq^q³¿ñœçÛGUªôMòÉ‹‡F:ûÀÜjkJŠ~»¡ª¯æ\Ž7}”wÏxnÏ^åæÚG™q½ìÿ–oI÷Q†$ÓÛOÐÇ¿†u*_MˆÈ³wævý‚¶þ2Ö°™3å»>QádxÊÓÝŽÞ¶5k¶ÝïË:Ãïö@äï•P_˜z §þ íÏ´dUmu5öµ¾rüx}…qL_u ¦ØGýÈ3~¶Ï{Ñ™Þìyná¦W¡cÅû/#à%½Ÿ{à (Ë gšâú<ÙñÚ1LžÁ}Ö‰aMãÿ-{\ÇŒ°Èò,*RþþM]è F|G^0°Y¯­ <抆ÆWVn`o¸œ2ÃZÁ¿ lâˆiçÛa!òVLýg~·Îçx´)ÃìÆiøó_éJòýÿNŽñ*ðÉ4÷ù{ñ|РÏ'Ïáõï|ÐôÕ§ôEÞ>Xw1æßÀÔí­taÜd3ÄS9Õgâ¯%Àä$Åp›mj[\ÐÂQ3xûŒ[i‹z„6tèµlŠHšìÏùÝ„•¼(’Ë!¨Fð² §’+'¸ƒÐ`Ô†¶Ñ¿ (uøåÿAúßo鿍É\¤µk_nE¿[¹`Ê­¥¹ê×ÜoËíˆ6½%6*rÄà×¹@¨#fÕ¨)ÄsöpnŒŠã¼!4Êß;¡¾zÓGMx啘T’ƒƒÇ.¦ˆMxFþò|g+6ÿ>Ê›g<[¦EùêMŸXú( í'Á{QÚl]-Õø:$6¶O޳!€óÝNºáú úá,Ðåé(i³Rå©úkÖìèŒÎ­²/ëŒôf_æUmÿ.}ØàˆèÑ•l±Øh¤NºÛå€)ÉnѰöT6i÷!¦kdÛ+ºøexÖóGP°-´üPl~õI‚–ØÚÐÝQˆƒ÷VF° û·áz yŽƒô+ëTOKºÐQ…')Qa¿YñŠëí̘Œá%aj«1Ë-R =íz?ÍØFöðf9†¤rûC‹¸ð’ 4¨.L6’?ÇùûaÚû›,ðB›‹i¸7”K­Í½nißl 4ŽKÀò­ÈqáþjÐA¨ÏBCÖ ¦J½¼½ÚqwpdÌ?až0öÙú‚‡·u)x4ÞäërñìQ›šá‹r¼}bß [ aý ¦Ã>„²prtÄáœêCúqØOÇàðY,N['ÖZ!&Ý…Áf¨g|¯žqÏEøÜб8Þ½Æå6þ ï4QõlËÜo7—ÒÕijTøLÊïß𮈠7®ªeJF‚«¿að< QÌA2ÅU„B&NÿE¢f2ȾΙAÊÏàec‹i}ôÄmØñt(òý 7AÂJêy¤]eÑöo„ïùÙÓ®Ïg6Ó3i¹ÿÜ{É'I‡Ò»y¼~}pO²— fÑià§õAð¢¢RU©Lw 7̶pz—[M¼é£¼yÆsË¿0ç½êËHE÷W­²}ðés®5âðq2Îf’o[ا I˜«Š×aÊ·†;S: v|*‹YpÆiþ·À kù x9\ã뚣Ã÷i—´÷Âñdwòa‘ÀVרwxvœ~6õcLµÞ‰ûý!/Ýð.Ašëð7.Þ}ä$ú%K`OGnñ2¹Ñ”ª¼çîKƒç,j:mÒ" °§8p ù6D‡1ƒ0Áå>uÒp¥$Ahžª)j «@€/ï IDATÔ‡´øý`R“!*–ÎÓYÖ"]µ¾Z/£sAù_xÑɰ§§s´hÏß^å>&.ÜóÊ4×¹cÄ^ 𣹛*]Ê.ÛlLaîð&¯ÉsG¶Ëú+ž1Ÿ>ï±ÏÊðö90YàÕCÝ»°%O-'¼ …¡­lDÅE'zFjNU/{>Þ<ãžñ‹òÙôÉ{Q÷óð`ó+LiRt#}'ÚïP+_xuúmdŸ¸à:„Á{æ€/{˜×­UfNÅ׳Õ_ˆ¶ÔC?¬å3Ñâ  ž %ÅýÓ€á',ž´Êñöh4®3mó¬Û}:¯YFo³+7ñNÖZ Me™1)îÊCñ<ç«O›w¾V~ÞöQS¢F¯E;9Œ÷õµU«ø}m¥Ï~ô¦òæÏžoQ¾{Ó'–¥>Šœ8À£ÕC¸çP ºé˜%.1ï‡x|¨¹Üû=ÿÒ´sÏd‰Ì_*´ÃòÖÜ]‡<ÇøÜD¢kÐÍ69kMgû4÷ YÅH#ZQ ¢KϽùvÝIoŒHô°Æý[¶¤ÉóK•ÁµD·ãÙíýÿ¹Ni‘á§¥å60¡ëE­ ¦$í"á|Õ,^RŠ¡îWBѱrî³¥oïU~ªömpûVÌÏÃñIÀYq:èÒ`3×êç÷Œçš°2îñtý*ÁêÉwFŒÈb‡OÙÑÂúÊŠâ.ª''ÜVw…%Y³h…¨ê™¤íç±} ·±Ø×7ÛÊë·<9$Ó<ÌWå•Je¶ãÜŸñâ¾×üúD›8çWÜʯP߿ޫÐ0C ìÛPRB½oï‚sgùˆ‰“»¡Ýi‘ÌÂÇÀ szXˆþïÂçÀ)™@ù PRB=\%wÛúxˆårµ|ÀáZ2&àåÙüæ¤OˆxdŠÏÙ’ÐÒ{É™@é0„ÏÛn®$Ê(=†\2¸Dæ%ò¬+ÒV"åð˘@Ù'P~…z¡{çÕ¤H¿,2ŠTANÌŠ‘@ <ïØ=¹+ÌY12KÀ¿²ŽŒ¥M7ej{¥ƒeWŒ 0%Pn…zÿÀÚkñÂÌÛÿl‘QÅî ¦ÈUâ ˜€¯(¦"_ånæ+¥Û”O sæe…ÀÚþ¡Ô?­óe}°ÈyÕ,8 ðeœ7`å‡@¹êÛÎÚšŽ…²?úµí²—Ÿ–Ù32@ÀOú&g._Uyoýï´=¾ÊŸóee@¦§!_Ô Š-Ÿæï‹:sžL€ øŽ@¹êM$Š:Ñgh¤\ÝõûÄ?}–?gÌÊWCÏŸÀä_ùªZpù¡¯òæ|™@Y$`Ã5²Of”‘Ulÿ+‹÷ÍubL t”k¡þúâWBS1×èT!Fû"_Γ ”eÒæç€F=­Øë(Åö¢ãgÅž/gÈÊ0µÿ Å@Ùð‰ëe´Ó˜•ž=_†oŸ«Æ˜@ (×B=±òWÕaÐXœ.Nn(|Ôyn⢂ä‰ÝP‡cI¹Ý¡· ÷Êq+.ÑϤ>¬˜ïÐm“¶gBC×ú̴ǪoI¶CòÛm•ËG&ÚýÇ¢Ú”ÛõB—òOÿ¶â½B¥-`"ê×hï“&+pt‡c¦_ˆ#6(§„¯_9§ó|Ž 0¬|ÞP³ç›oëî®ÛO—b^<~E-@VÖ¬ÔïêO¤$/섆-ÔosFÜ\t¥wˆÃÙ­ »­–V]¹Ü’%@ø¸8u†.ŒÇ‹£dEQBGÖrÝ:Þ³ 쀺E‘êàØ¨°ß­óCœÎ†Zª±% NÕziÇÏ®VlÊ¿¦:·X×=¹µÃˆ¨M†MsFdúȧfŒ îõ8×Ì3ëóÈèšn|d·Ë›&;Â7XçC_¾{dLÁ.¬IØ1ÔïŒG✑{éúð>ðOMÞÈ˪Ë:öŸÜL×\«Ð¶Š¼K5ž·ÃªªöÜôøà#ùÝ÷°ˆñ׸…k~lTxS(«° -#„D:_†q½*üœšáš‘ëNÏh;0Ðÿˆv¶ÒßÚm¤ïPU厩cÂç[ç‡DD Ö¥l¸#­sÖ1¯¶v?ñîÅ™*m•ëü›vUGÙ¡ÂÏØ‘G»Pä„iQá>3´êÊG&P^ ”{M=ïŒ^uŒÍQ@ 8e% q¼]Gw‹è²R®GÙ"@÷V7=Ž|Fk¦+R>ç­@Oe)lú íÏrõ4ñ04ßO|á…|Í‚òl‡†¸B/ϼsû ¡'Z7ôJ)6{Æ™9s¦ªëÆt)üïÆþUQÂ!ÜO¥8ÃTÁ c ]žiø3ØüØ3û„bï[Ôgí`¯ô—}¼è‰údçÈíè΄DD÷Èò+â¡(ù Ç¡‘ã®C·v€¿ ðQ—}º®O¤Ý–³ä×¼ÚFh¸ó&äucC[¯Óœ‘]Ñž\òȉ§/e³ßf¯Þç{©âièãÍ”s(ƒO1& D…1¹~6ô¯×Í-ŒÏ±+æuúq¥Ð0ºù¯Ñ QD˸‚› wö„´›Ì×Üî'Q¶Ã*Ÿ´}ºa<‹kS¼ý¡Í69¼ßµ-7.Ü´s,„žx‰5¨À…<¶Aã7 $¹@ÆXÝ“`¹çžÀ‹ï]':…`Ä›yK‡ÖÿWf$dä–Ohdô“†nŒF4¥¹ÂfóÿêçÖR“Ó f¨÷FœŸ ⌠îCI{-ê¸ÕÏOMŠŽ8hݯ,}ú,uãŽUVCK6m*¸ ðœ2„úÔè÷’‚¤ÒïkÃH_æË–fÑÐÅ#Š4ÞÌ/Ÿ¼Ú!¥UTñª¡‰) :“0¯üìÂöŇQ#·¡ -õŒ·hóžnhˇb¯í¦ó¤¥Ä@á“—ïÖ˜àxáTHä¸ÁŠMMÔ\iO{¦ãÏL`ëãÏlë4óã.é©éxÏŠ{ JmêÅV)dó£ÌÜe¤¥ò**sX¸³±Û­EÃZ¿ŠÃgòVB®§ EùHêâßÏEŽk;)jÔVzïD«:pT;Š­ˆô8ÿ·/œË­màýr_:}能˜å‡¾ïW+³©GG855á\uœ;nç#`— @–­8¡ãǶv ¼¹ ´ƒ!¸«]^Ü™‚™~†ìØeîñ‘] !ЛeHñ”ê—¶@9Bñ?É|Γ ´zc«U¶?ÔÀAÂòÔíý8GøºE›vÞŠ(1My£jóï†5m¶™ùâzô¯Ø¥dš 1NµËû…= ²ï°`ÓÎ>7¯| Äl°ÙÕþ l½šKCžqkéN K‚vq˜iŠq ô”ÛížhHeFC{«Fdü ‰'ŒÎs¸² ŒÕ?Te@´“) áÍLXžñ°š¢þ5á¡èQ€),Kãø°×Çö"ò!ŽqM0mQßÖk!}Ï3äÒ­4~Êh™Å¡¤—­s¹Ix¡A;yÆÑ¥Þíãç9 š¤Š´H!Q£6ûÛÓ³¤É—¿\ÑÖ|&ië“Cú«R½Ï—)dç³EKU¡ÜºuЇ7?V@™£˜ Áù!ZoBe¹¥ˆÃwù lI#® y¯Tký€¦ð¥Ëp ¢ô™Ê¢ [PŒz…8bÚdžÏåCîmC60 %³MAÓxçÆÙ³1Ë0Äù©Ž,Ðg‡Ãß™À%f#¯H4ä¬YZ—¹‰Ó®Ÿw¼µMQºáÅ [wÓMßrÇ r>zÜɪ¢>)[ý®ó¹n^bŽö¹ÞpyùÝwñº³¾­þÜIaa'ÑÁï y}ìM”ö˜¶·-¾'½5räy‡º)6ÀN¹Î¥|GYC‚ƒù’’Æ !’+Ñ5¼€ýUN$Á‚ÒÝýpú=@=,Ü.CÊï!¤È/Ÿ)ŽðM“£÷Ä×ßEÆ”K¡ñ4ëu)]æáÒôéÕÁjзñûU›ò%êp58ɌȮH#§ Õ‡ùÖ©£Hå!íbžgJVà8Ïñ!Ô¾-«?Ôxtˆ>644Þ›@Ž<áúòkÍ0LÃåˆñ­¥ÅË1NæÕ­4ZºM ”ÿÁ³ýâsáΦÖù‚ݨ‚Æ”ì™äd¡Éªžçø3È‹Àæ'ÿ´õÉÐülþ­ïôKŸâo1>ÿAã"ôUp…©h&ý!Èø!zŽÓ¶ñz|äÙð¢+ym=¤›eÈøÐ7ÿ[1ØàÀ„ñd"u¼³/ýeC‚a£ùäÌÊv¾à_¥MùÚp»‡fñ…x·s lGæ—K^íÐ3íûáቃa/ï2ÄÄjR\í©ÈSgóÄaT6T#ǶåY6fÙ lxìéÝ8G> 4P–º>pˆcìAÍ¥5é×®åÒØ|J4 êÇdwôkÛÜÉnŠ]Á–Ý·áø3}±áþ&hÏÀüí ÌlQ÷Q €¾ì”Ðef›R…«Š–­¯ÂZ•ÐéÍ›IL€ äB B õ¹Üs±ž†ÍúSª"Ÿ‚Ï/sð€úA.áZúêÛo¿šŒwÿÕ ñúÏ~6û-ÃF&Q(aËŽêÑ'¡¿¿ÛPŒ% ”†<+æp L·¾C™aÊø>Öù’uÎ:æ–Ï0ÇØn·6"¸FPû ¯¼’2$Üù˜&G­tžGL}^€¡ÁùXg8ÍltÆ ”©ŽÑ0 =¯Ç܉‡±aC´!Âó¬OžípĈL- eÒP Ÿo8Ÿ:}Á}wž™ætѰÂÕ̺D& x4•þÊQë™@™#`S¿ÖÝî¥BÓȬìZ•W‡ÇÄÔN½¨u²çhƒLA=]Êþ—VY„z3»2L¸µ˜E~;¯|sº†ŠƒXˆÛܺSP´/yØúé|C…TxèyË:ÇG&Àr&PáÌor¾Mßœ¥GP›·ª§†}ó¡3r?ý‘É ‘ÅçÏ¥?h–jÄMyQ4ª=éCÇÈx«&‰"¦¦6ým¶j£nm×jŽÃñt®.4!pÏCž÷[&$H=å•[>š®µÅâÛ$ÐgØFŠÇ¬²…¿~Ÿ¯¶¾7TëmÆ}èC"ÇfÔháŸuL ä @[¯1˜ûÆ4]Ë£^µCô”Ÿ*mC!HŒÃ¶@ƒØØ¨Q´¸¼ýµ§,éÑ`–°eÊèѬ©÷`ÌË8ǨCèoŽA<‡‹Hã«üj—–¢?Fm\µZ}[•*þðú$úæ´?Cœ#l 3К^Í/ïìס~ƒ:ý‹Ö e\“ÏE̡Ϙ3ÐF ìá¡ÙÓñw&ÀþN€…ú¿3ñú &$Ÿ€—š/² xI}7ƒ26í;„K_,'m‡W›=Ð:¼IH—9]ßÒí>½aÁ¦]{ñòZiN²ÿ[˜êŒ\ ûû]Bl„s]¼+ú¸¼àzˆ"æ–O}¥ÕÏ˜Ö Ä”èï0‚vÆÈÔ®hþC^¡Ü0P`Eà£páø»QÃÉîäOþV>ÁJˆ€`ÌDQí!q½HÝ­/Á³›ñçÜ“_;Ìžž¾O‰½ýh#S ¤:Hèî™Ô~°þM˜ -` ”8&8XãU3²+³Ž…è—ÛÚU¸s„ì§EáïÌxÁÖ}nŠ–œãŒopõ 'Ú«i§ã™.¿ÏS£F¯GœÙñî›PMhJ@í*ÓI±„1÷X”y fÃŽ^®côùåÉ×™`ÅN/¡w`~“i2C‹ŒðÂÜ]×v=uµUhhdLýˬï9iá*ù˜·üôÒ÷üò¡ør[ðJ¾ƒ­¼¬ò†Ž[}˜c’¹`×:ÇG&ÀþNÀš-ûû>Ø@A P_”ÛŽ²Í‹ã3+•ÛÔûö—§™(í gƒÊ›a?XÅ}¶þ›=Á¥ÛUÓ“€)$»€–#ÓŽ0§j]²wÏtçõæ›ðtfÈ<ó!Ï;øË);A¾ƒ³_`3‚ìDø;È™@ܨQgs¾Âg™((K.61!Í 0Â(øÔsaKºÓ…8Þ©%Ü)aéÛaÚÓ7eëTU~M[ÜŒ¹Q3ôÁ°Al€øû£Z%ût ‚ª¸ò)H™— 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0ŠJàÿÔ¯â…0íIEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-compconn2.graffle0000664000175000017500000001351713553660046027305 0ustar zuulzuul00000000000000‹í]ëSÛȲÿ¼ù+|óõ1ïÇžlN’Ýd7äž-ªn [€NŒåÈÂnå¿=’mÉ’ –±±1í­4/¦û×éžçÿú~Þm]ñ Œz¿<¥yÚ zí¨öNyúéð×-óô_/ž<ÿŸWwÿ³÷ºÕ¤µ÷éå»·»­§[ÛÛ;ý~7ØÞ~uøªµ÷îíÁa ÚØÞ~ýáiëéY’ôÞÞ¾ººò|WÊkGç®à`{/ŽúAœ\¿ƒÆ¶ ‚×I:O¡›¬õ‰áÀÕNØN^<ùéù×àúÅN; /ƒwþu¿íu‚ïÏ·ÝU¸ö’à4ˆ_çÛ£Ÿ£*ÐwØöhòsÖò¸’ǾûñÓóAÃ#¿€zÑy/<£‹¾÷~ýû''Ý@=ß)”¦ÊzŒÀð™Ò”™B‘çÛ£¦³!\$ÑN翃dÜu_Û£û/ýöW×e¯ÝõÏÂö¸ØðÙ‡Å\‰ÁèÞxÿMžµÈg­¿¥„i)•&Rha¤xÖÒ†yÆX —ˆ6”É?&žÅ5¶ÛõÕv¢n˜¨Tåí«¼ühÂYqî³bÉu7ÈKŽ'» MF_ó»…ÛÙýW±•+؇ÅáÀdgiô+ÿ‘Mï ø3ŠÎgX,»~ïÒ|ŒÃÓ0_&£yN§¹ø–ÓQ÷â¼·Ó O{•öiµý´ôAßoCãòqàw_pXcéQQøÃ-ÚW~”‡Â[”nQÒ¢úgÎ~²õŸÊè\#Q\®ÿÞO’ÖðÒý+?öË•^…ƒ~׿>hûÝJÏ@“ðiµÏ[¿´Æ¿Ë üvƒÃë~¥²tP.›.°WQûâ<è%eO$«Ìäpa©!èÉ•V¢œéPÎíQ0†¸†¸†¸†¸†êãBÔÇ)@炇ïsS€âMн ’°íÏ ”Ý€o¿Ó8À¹¼%rXxâi¦dQy×eFíEpé6ˆNì1®Õ­ Ø”v8ó£4{£Ú‚I¯(]­mÅÝ ð!çáX[ól¾œwâxbvs­N”{œúíë‰â'~w&wþ©˜ z?u}ÀÁ»ÍFò%ì”À>‹ÑŸ« ÃÆ”LÄB(9ã”ít‹çŒBš¹9£ò”e–r"Aâu™øšpF*¨§9c ‚ü¼ÕÖÔ~”ŒqÍ5äŒspÆŠ’ŒœñNœ±bL_g4|^ÎHð\²Ò‚J…ãm5[§pÆ:¾Ê˜ò´¦Ž0 2ŒÈ‘5n†ÐÈ ²ÆE dé¬Q2/kä†yÊq2N™ FPÞˆ5–”q«ª¬QYT§QÞÎ(–,4ÎÃgœ b¸WòÒ¹C¦ê¤•’?ôç=_ènЇáëÓ\rÂsoš™yºÓ0$Ÿ™cjA݉ãD:øÐÁ7GP_z ” Zl‰±ë÷¸IGj¡ ˆ9.õGÜÛÄ},F?NÜ\JP©8}ª!î¢8·Ð 5 !=áRû©’ !!!ð6¼ ø=6O¹OÁÜ>T*eå¼Yfµ§¸Ö–KA¬ÒNõ¡ÖÔ†JçD }hEHú8=ë mh›ÃÆ—½½ä†$û (Í&aY¥Ã\Ä'É>ÅèhŒŽFÑEÿõ±~}»Œiõª Ù]¼=ˆn)÷‡ØÌ|ÄîLnm |Q+ÍgG·ôpnN,°^n2 ö i@3†3M˜Bâ¢[Ctó“öÙú„oõÏ®·Žã­~]† ^ äÂKñ(5”-,7)äÖ+”¹›¹Ü"ä"ä"ä"ä"äV ã² ·¼ÍØ¢ ÷Ñœº£ñ\<—11wc¸œRÏPJ5ÆlÇCåͱ;]”è¢DtCt['t;^¤òVŠò4•{4è&PyCå á áíÃÛcËD!–ŸØQÈy·Þ—78iÖ$E5ëÄí÷K&еvâÎû¹rôXÌѳPÎH—ÏÅÜ9zê‚nIZ[¨] —¦3$?Ã=˜£çrF†œq±ÉÀ—Ÿ½LÌØT Ï‚Rá<,ˆÊÐ[“ÖNµFƈy7˜3®,Z³..šjìâr©RL«OÅ¥m¸J>k)æqÅo°]+cqíK§ë¿Gm¢75Ñ“›-±õ|+3Öç5ÙÌ|+³Úç5qÓ+Úp×Ɇkxv’Ì„K)[7nëãçƒ4‰Êiì'aÔk½ŒÃÎi°»`E6Àãx ˜å4s¾öº~;8ŠÌ}Ìr'¯óÀhÉ¿*]¶Ý2`£eÀ5º £”!Œ.F%Â(Â(Â(ÂèÀ¨Cѽa Æ:BèmA$÷„£\h“²L(!œ=vÚÎÜr¬gš¬´w‰sÜ»ô€ö.™¥âµ{½Õ·¾Û.:Õ·ûxÝÝq£Ó­ð>DаŸøÇÝ`€ûœV€-Ós{óµôR÷”±–Q©´ªñæ3<À½ùKõYíùIĽ%y¹î»Â3É{­Y>‡T+Úï4E‰ò¡sH»*ï~y›µ0ÏZFz„[®¬4œ2k ¯uKˆŠ[Â2‰n‰˜9Ð-n 4\ [bÜ€½ß×ÎñíxÕ~ ØçdTʸñˆ«i1Ôè‡Ø8?C?F\#ž#ž£#bƒÍlbùf6:÷Yg–zÜÉT*п—UC™ž=Ó<ôƒg0xæÁ›Ù:"Ê!Ùò9$™›C êiÎhcÀ'¨  ça¯È!‘C>xÉWfX¶tD¬ÚÎA-:"І tD #bmŒ3—± ø‹dÚ%ÃPÏZå†"=oRshJeáAH²â¡&sÍ8KODÆPx"R>¢î¡Ms„–è‰Ø8<_KµÁÁ|q`îÒÀ»J˜ÔÜZÏyã­6Böh¤Ô#ŒƒB¨$u´ž)eUS?kU†D|88ȈBt8(­!Ò³•Š6ÄA—ˉ3bœ„Ø),EK¸ADDœ=ýùÙ5Ì‘ßm}’«(þ Àxû0—mh&@ýÍöê ß_‚Šš¹\*I@¤àšd1N"áÖãN W ÀZâvª ª&ïÞmµ‰ *åÁή!•R’pÛŒ+Í9¡Ê*­u3¬éˆ1FR‹ .ëy*[2îÏó$3Q-@ÐVK 4m=G{TrxåÖ0éKŸµø"=KaRÙ)fû¬t·ø GE÷ƒAÊjÆÕGˆ»D£U]øõ¥æ;ð©•òÀyÚn&¨ítÃÓ^ ÕÆÙß³Xwwi®œ$þ1È_CÁÎý3º4M;02”¿F —z™üu”÷Ukç:;õ“àfëþ í*MN¾»¹’y¤˜ö_j`If¸ý„1 @DEjÊÐ)&âUÆ%ç¼&Å}š2îEVœÂWêŒÓt÷0nwƒ5ÚN¯¹ä ÉŒ?jf™†p)ùÌ‚Œ ±¼#ùpTö™µÚEðÖ¹N Öh‰œrJ ‘Év²å—l7.×#'`^a 5îØ+«µäUU@¤Iz­EU`ªCUUµTZl¬ |~·ó¡E [‰.ÈR±rm<á Âr© Ž' wئð¤Íö‰.‘¥ÖÅ”9³aS“œ`µ§GíEƒÐ¥ù¯ &FI+àC¸Ú^L ]ñý(IÏ(™0Ç}ò›-ôˆ KB†õåõ w ™=7éì2×ëBeÀÅ:øörÏÞ3èj·ªÂPøŒðå–À~Ðò¼ v€lë°Ô·[Âi° °Ö!Í’@PHºeÔrÃüï’m¨ÉX®T.gî¸G©ZÊ``ª6ÉÔ‘)¡ëŠœ/%g 1PCµâÒp"¤2“†¹™0!>¶¼‰à×É/ƒälÐ ÀéenäûÆNC=«²¦Dgºªœžx͔ć Ÿáááás-"ö‰ßsÀ¹b¤TÒ.ÎßÀ£ÃRc–?_જiÂÈEm(‘¦©3 g¸dñHÐ½ÔØ½„å‹,óŸB[Î¥ð8'Úa¢âÎÝåsFa .¥b.nߨ†QX ’pn¡jdIm„³T+&0²|‘‘å0«‹ki^²ôµƒ ÛÖó$¾¶'îï_ts„®€WtìnçÁmÅû“±g7=û  jyGÍF·jú©¶ýü"/nOšôÛ™Y…ñäßý.PÚ‹'ÿ—¤!.neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-dvr-flowew1.png0000664000175000017500000051343513553660046025504 0ustar zuulzuul00000000000000‰PNG  IHDR©í­˜ö sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|EûÇgöîBoÒÒ+bA±`ÅÞ¯¯ÝWI(VDä¢'¹) ïk{Õ¿âëûÚ+‚ET°£"UH"ÒRowçÿ{6ÙËærIî’K¸$Ï|»7;;å;í™gÊ Á† 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ Ä‘€Œ£_ì`û˜À­¾“‹ƒÁ~Âãþ=Û7ñ÷}„ žù”Ï’Ñ>ÿ FP¸³Þ%åŸÄþ+ž~Ù¡×…Ÿ¶ßáÊ|C.rª"à®ê!?K|)¾‡:izÁ¡då¿¶lÛò§™ãÆ$~Ì÷] S|Y-5cÇÁ†4úi¦¦„›¥Ë³fžïž­u«Û|ö,Ò‹GIÑì™,ÿ½¿ÕU8Eº~¥êQÍÐ'#Œûë*œhüõù»·ˆ/z¹…V<Ë7aSUïØneÐÎ x7:ÝÆ“]"ñq¦±.ï«â§ëê„Ý^)¥I)UmâQ¿R3×*e¶ÉögÌuÆ¡2?+sï|·¡ÜW•?Õ¥¡2>Õ½WÕóÛ¦Lé¬Ë6N7†z²8>×ç¦;íÃïïôÍl_$ ÷s'©=³ÒÒ¶…?ÿ}ÙS`v6û|JË Æ£G§‚] + ø"oGž92Ýÿ£[Ón›™öiL^Eù¶ÇoV¼e÷=*¸mœ!T È G]³iªoæ—š¦Ý?orú{ñŽD±©”H2Ø ~Š·ÿ‰èßVñE=¨ÿfHñ+âwHUqüC|ÑÓêk (­»n›";gúk{Ÿèü¨^mÙý$ª¡'%x•„•ªÒ«ûªüJ„g‰–?…ùæL¡Ô5álrÅgÁ‘ÞÌÕPˆüââ•yïóán ÌüãLÃ|ßÊ•xvxøóðß»v§™B‘º˜‰gãì6#Ü]Np“@û¼])¹\sÉY“Ó? wÿ'­q&«ñ¦j”ojŸ\=ð±êA(>Ú SWjrº&ÅíRj³…Ÿ#õGÊø(%à‘iã¥[Ê ·ìž‡1a[pû\Jñ€Zš&ä°üBäñV‹bó5J×R~"¤Ü€Æ¿BãJš ,¾›îï¥oMËY욈¦6Áùͺýö"!¡Ñ•â³ä`Ú–êRY•ûY—ªÈŸo ‚á´ê˜ÔÅs´‘ ѧ̥?äͳøû ë;cT)ùÏ¥¤û¦z§÷w†}Æ€þ‹Ð®þþé°1¾ÀQÎgá÷)YY%ÕËÞ­=ç|.…Ü…öò_ô‡x<ƒ¿w1啇°Ï†Œp3§8Ýó}ã%ÀLÊ[LÉÉToà4Ç@ýÞ%<#æú'–ªœI¹/p>„ÏÀ}Z®ùÙ.<œ^ÎAü‘š1åDÓ4nDºUs»ŽŸç›´>ÃßÔ»ˆ‰k…ÈTëߥ#ÿ>‘<*Ê3ÏCã;Px\Í"=oêvU±kêl¢ICà‡iþ‹KÒ]Ý«Ì}C¬KUåÚú›ÐÖM>ÇÛ‚€8?3ýÅpGeLb*c.ú—3„(ZÁõ€Ùïr7bÄÏÿÃí]†aic¿ ?ô;gûYP t‚@º2Û—¾"dO7Rý1ߟq‹ÓŽú¿QÞÀU¦ÿ6•˜!ùíx¬¥v†Á÷‰G€5©‰—'•Æ£êkI@E¥^ßÃÓãøH*½œ=9ý-©¹Ñ€XkÌÒhÝj¥ž:Ð4šãgÔ·>ßSÍ£vìpHNMÃtxÕ­æñ–C%DPéÙߤÕ>ŸDXÏFª‹b ‘¸Å¢%Åm¬qI÷T,X€™ÈØM]ð!?©ŒÇ›ØÞ TloÄî:ú´HÈ6ek[)ý´<)ökøF êR Cªó×R2¦™—¾5 ¨¦õ š°æe¦}–ì>ùhV@ÈlUf˜Ás˜R­¨2Åß«Ê{e–,)@)§EuøTî–ÊUVÀû÷I ê¡àg–sÀ?%Ö¤6l¥Fgá÷«h„Ð$¦ñ«^Dž•9ñ;Œ4ÿ ¡örl¬òâ­;í¤bÓÁõؤ&¤çJÍåÚbꅣΟT¸eW?¼“ƒ ¾vKτʄ`ò'Å7å¡ÂÇa½Ðþô¬¿Ð<®ñ‘„@Zï‰0¾ÃÈw$Ü^‰†æ,G »v˜ÎÊA×ö¶æi–Íæ%¸ =ÿ.é¾fnæ¤åvºœ×ѾÀ@C7 ~²ýÞ³„‚'’œî*»G|ýxv–LÎÎL!ÜÒð:ÆÁß'‘¦ šê‘^¶êT›:?3íc›9â=;ËŸ1‹üåÍ<Ýb2òtýÍw.Ú|Š©Ì™ï÷b Qfðìüò£ß?KÈ•KdŠ×ʯ÷{¸ãó(¶]›Xl;vÊ”Žzž9SjCr‚Ÿ @œwã~¥Ð´ÀüÉé´a¦A˜Hì숧ø¦õºîG9?#'˜Ó=÷;¡ƒý&°± ·-Ý­OxÔw×Nû½ºàcM_æl»Hšêz”‹`¾ª÷3ù¶^ í©îîäÇìz;ò¾À¹Ò4g"/—Îxo²ã~ •#áºw¾?íuz>Ú7µŸ¡ëÿÀíy§Ÿú}k;„±µ¤Þºï ¯Uñ ÏþKZìwèŠòmmn•¸ÑPê:Ìü B¼Üˆß÷Jh/Í÷§?ætoßãù—¸o›•é=Ô)äÚÏïáî««K.¡}ÎA½Zzuu¸öoL#?Šøž£¤æ‹¤E$w»NÛÚØ)ô­“…©ßBá¡,Ÿ0¡{2š&'Í›ìý_É/<—Â$f`>K믇ý ¿ÿeÂåEâ­Vž–ÌôÛa»Ç•6PñM¿C׋ÎÁ@ñ›rI¶?íUò›´¢h£†ý¡¹æ”Ó`µ8<Ì1¾Ù­u}ÇÅÈ%Ý®çßWõ<–#FšJV•;~Ö8Ôß·qðÚg©X¼rMoTúýi­N²ë¤§£ŠˆKZ#\SÉaN÷˜*éÁè`)ô+Ì`ÑRtt$ ý„Æî9T~Z÷s±!Š¿u_àlç{ö}ªwÊeP¿BKSt?cH›}Ño$›Aã[tCm·öþö‡€t¥4Ü¿·Ñ¡½‰6êe4S.üN¡¸D£•E‡ÿÅ_Wæ ¶ÿáWÝP7Ä‹:?óëBqˆB³Œ´ýJï e^h½çøÖŽ"¾–ø¯nt<²ni@†ôoøq@sWëoÈÒfŽ%¤ÕFÏrÒž‹<ýËzQŠUÈ‹ïég„:%z62#p.ß"Ì«Ðk5·wàn1òªþŽr ¨%~ÉNÅyæç%k¾äf¼ó4þ~@§5DæÛèŒo²Ü5€ÿ"±£h[kxõà`r-Í. 1¾O"­)oÀ§£÷‘—ÿ4ös”K*ò!Þ|dÎö¡ÂPÿA8Ãñ·¼¡ñ?Q\LeNËÑ7eÙqZ§¯dÈ×ÞtµíWd@'y#ìöïÐÆ…¼.1†nü~¦#}-¿ï¡ÞÒ ª9Ò}ž®ôÏ­¤í×Êø9œT¸%-a/Ìð?Œôþ u ¼Šø}Š:(êÒ£¶þIkçý­< 󬲟áî««KÝÜ/_Àèª[½™}#ù;~ÆŒVh+o×~Í[È kÇíwêºmháÙÞqè…¼ý±$L¬Ï,m¬«©JÚ ;BBìÅ,[ºaªñN7jÀç¿HkK´S·çéy_ÔÅŒÕß½ ¾=Q uNYtH°Ö,ÁÓÖ–:ŸÑ½aì¼ùÝ’ÊFìGåimÉ´¡A'ýfÓ8 ¸g²_ªLÓ$-Õ̵Õb§¾™æþ­Ð°J%ïÚJ¯u߇Fæÿ¤»ó-Ù¾Ô|²† ,±¦u2¯i¨yШ”šji÷è9 xJ/x ·šÛ#Nžëó†ÖKAx½k•^6”ÌÂ{œïÑ»h4{"þ“4Ạ˟ö¶e‡ÿè’¿ö/C؇næŒÕ#ö³ˆW·ë©›>¸¿ Þ¸p–Öù»U·FéJ=M~@CóAj†‚µ8N ?źª[hÚ*¢ÿ°ôx’>‹À£¼€O¸Ç 鈱¾)Íö¥­²ýZøÃêcaO›³–Ìòݾ۶¿fgz©‚ûW¸+EÒíYŠÇSAcÑ-,þ'BtkRÝ’å÷þËö‹¦Ó¶Š]ìß¡«2oCü^­=7fOœHë’-“’¸E™æ|tbÓÁîßáìlw áŠBùp)çû²ýé™vœÑ!ßQ¸u×xÖ>É-Ÿ˜íKåíSÜùÌ{ má¨û¦Üäjóª3ßI«¯Õ·(L×CH›Œõ”k¨¾AKJÓ¥7ç`à!D cq‰Žò@qú„ {ì¸cP8^ºÍ`–ÏûµmGƒ»=çŸã¡c–DÒ¨ÕØÄ’g ŠÚA¯7Rº´s°Öòû™u>­^ü&Ò{IŽñ9Åø¸™hêfnžŸ{‹… Ú}áïÙ]|)x[‚uUÇ'ÕuÛðXz:mA{ ”a¾‰8}šȸ&<¾öoªp3Iºä…Ù“½oÙöeíª8´hÛîѰÔ~¯+GACNFêôSskÏA…:©.G}kmvs8@Û]’)ÿí°®öÖêŸ2ü¶â€l9Ö¤6 †`q`iT7DåŽâÞ\*8×NµÂÑ.Ý+¾'÷¸[¹ÆÚ*=§i£ù™Þ tŒ_£Ãé#7n½¶Ü{zAZI£(_v ¨ä‚ç0„†ð©Ò~ß6¢Ü{ö)ç8T²¶:`)_¡{S l «ÚÐÈÂ#4ªóúÒr#xzsÑ¿ GÜ»#)Ÿ@@v”.­¹¼<–áÙ!†i,!á„¶HÚ[KK€Íi£kªoJ¹i%4”—O„…éäwPW¤QX&¼Â4Tœ ¤\77Ókå—ý^e×Zri"Fenke!-Nÿ4·ŒXÇ­ô"=Èœ¾ŠéÉÎwêãž6FBpúegÿ\Ý?Ü&ÍVà÷p<ßÚÃ}"ÚU›DiìXÊJ˜Ûí*ê^ÕuWI„zfÕI;BÖU=GÓ%ZÓÒgJ/üê§?ßt®/}l](¾X׺’þ¬Ámºg±PëQW®G½îní:½!Ï9ÓÊ÷U ‚¦ÀÚøßQqÉDÝÑæ±qk'z©™hñ;]Ë)+Ú•:Ê\M·ú”Z‰Òiô>µc%€1ÇûYÙµdYÖ XfWv—ÔÜÜTö«ìÎÔ“þÄq&0ºMÝ==þ‡5~´&颔iÓÚÙCZW¶kgÑ%hÈö¶m׬Â(½tíæ¿Ð >9:Ã? kÐF!Ð˱‘l26ºœw›ïñ³Cš0)!¤ªñØ¥JBªµöª°P\ŒVÃÎÒ·Iƒ¯ñì^išŽÖ~aªyÒÿg²kâr!&•%²w%æ~íƒWõd·w]´Çc¡¬TÜìà‘" ئU{ši‘±Ô¤œEþ$5×>š–†<‹Êü†ãdÊiZÂߢóq˜?Ò!M4áa q~Ž‘Kƒ„rÚTü¾œ|BF‘³‚‰•O´¨£sßÔ“Q?@Àì…ßÉ( -PŽ"LÔ' ‘?ž‚ýT¬§UšýH×õ’ %ž¦´Úöö•ÎÅšZ̈ÞV8BµÇƱæôá• Ã~'Ök¬i ùï9¡ûŠ7`Õ·(ìƒkĶ â+q´Q"¾·›qEý.1ºQŒ%ÊE;Í£~ m°ãN׿±¾Ûí*ÊDµuÝé_´÷šìg”8¦q”+§­Ü­^Þ«ç=Ž6ýg G%B«Ôž«,`L P½)5Hü¡ íÇÒŽ Ûš¯Ÿ€Öø“ØHR¨¬/ñPbzG›¢¤[{Z6æDÞÝi’Ù¸±ÔÑ2*$ºr]wžkI;æ#ýíÖ_˜Š-m»Ê{ß!Xý§òÊ¿ù—5½©ä‹h¸šË½Á+lW¡ue8jÊÒÚ®ԠÎóg,ÂÎüÐüœˆ®} :©Šô]>Ûiw-ùSšG¯š==ˆŽëÖ°fÕÿ+q§½FŒ Å…ô»hûž“(Nxoa©&»ÄY ÿÿè»UÉðß Öø,` Çci¢DÃQÃpí×ppöß­Í@´!ÈñÌ«(¸ÚïÔ×ò´:9Œ™&c°0šÖ=Bp=y¤7ð8 …ÈÓ/º¹O|7b|bä ‡‘÷ù/Ǻç_uÓøùõòíïèlû£,´C\Ü †Ê IϳxfPÇMBaY\%„T©’„x¦ÌN ¢Ú"­Ïæ›ðaŠðì^èªN†xÐ ¢iI§Nz-MMÒbÙMïQÅàEYå;Ô“m÷õzíÕ™6rm¢‹ïò=²Ÿ6Ø[ƒ)ÜOÛvU]¡mpÆoHÿþq©ïN?£¹Ç ë r‡òûk¸{«ÏQ›¸T3•gXƒF”ÝP/ŽGØ‘ì>°*õ´Í}í?—} ¿L1š6†‡Å¿/«ál¼Ék<)Ør ýÉRõ#maU˜j]¸ðY:ÈŠRYZQÛ>tU²ò)VÝ´4°èíJ…UÌÙ¶õü)þ ¢=RùhE¿£1ó|Þ/±ÛýF삦ó$„Ž£÷HÆ— >EzÏüØv$´kÅý 4°ËgûK¯öH׫ÅÊıP&­Ký':;Һ‰z®µ5º§óŸ"ˆ,%ŸqÖ?Œ>ÍhÝVåÂ{Ø€¶+ÜÖB¬ ·«ïßtDPjºQTO£—œS,žcŽ !/‰ò._zäoÇʧ:(?ç`³á‹3 PgnžÎ/Ï/ÝŒHq‚ð:ÑuÌ(®e†ÖjB°Æ â\höOÓFûü'`£Õì}h’NoÐ@ _›{nOEøÎB¾?;sâ¶o%³…{Q†Ë…a?öZÓ´Øþçzri‰Åfûwù«D»è)3Ô®”^·¿h3'¦ŸB&äùW#´'F{ÄI!Ѩ^;yV“Dhœñ»òÊ+ãRß~FsvÊ,µO¿Dr™Œç°™öbËvž?‰úö•ò_.¨p"I$JíhÿòNµ¸¾8ßœ ë”*œó£FD€…Ô’™sÓÓs±nG©£vï*ƒhϨ.ê†0ï&7Pô•jý*¼Ò’†?A3Ò·ÄN®·ŸÍ7®#áh(zÝæ{°ç,ß„Mö³}q%áüJê´I‹æòèF~>äã8,œº$–8%¹Û}^¤“^T¸†ç}øs¦¡ÌSåu`‰&Y{Åö{væÄŸ È®†P:Ô:[rãÖaôL6§¥µ7´© iÜŒ|ïN¢9G¶ö¡–ù€ÁqŒ‰eÙÛu‡câé˜&k&b-06 ªmЭÿJëãzu°l¦rc:òNœ«ûLxØõ »pMj©;Mâ\Sh˜raàeM‡B&ûÙ"¦ @98øºî!#§¥ ò‚V '×ÜÔ2-ØIh-IŠ,¤¢í°bæv¯¯y k÷&ààt y/2ÃRM¼šä&Ø—ãE(û´mˆ"~uêdtÆ”StÓ¤Ù‚ Gºÿ)°îîîoæsh‹5¤O5Çýy@-\ZåSý‘ü!;ÙB›( ÌËÐÞß2Êçÿ'µÿ•¹eûÆCí ›†@€¦§5—OqEGuOÉÉÊcnqªÔPl¿÷p'ÏŒèÓØtLI¤ghHn {_ËË?—Kéw‘^œ#YÄó)‘Eº~eA‰i…s»åÓåã\ý¯b#¿/¹Bƒû“ÓµÛS"lB:öçÐ3·pÿ×é›(^EÃٚΖ„|0°c’hIŒ&õnN÷´îg{[yâ°oÒ·´ŽÚËY€0GPÍÄ×h^¢%1±AÌZ l¸ð±„0c­ Ǻå0ëÐÏf]ÛâÃÍË}¾I(ÓW¢ íjѱe¹rf•ªUá*y¦ë•Ñx˜§Å \7/ЉQSFý<¦Û»‰6ErSk»êë’(=ñc!êì‰Ö™Ç¦ÞEXCù±„_×mÚ"«]€@]Y»KtãêÖú „ifSÛ‹AÑt¬G ÀZ–%}Ê»Ž¤Ù¦“ñÂÚ¹“'}É}UvÙéé›±1×Oaºšc/Áªê~Öð °Ú€òÐúÆsÉO ½ð›H‡æSr°&ïÃ4_·’&µI¥ E…”¢‘n»swÐ^Ùé w<;:åîbM£Û/»Ü.|© §’ãGz§D쌨á§/ŠØïÔåµeKùoÄS‡I_/¹ ÷&¦¢Ÿ 3õ¾À™¥;xÕœýª‚sé6N¼åt@ÇÁÏ?ÐáZ,ñSø—¸Ðp¾j½kb“ÖìB»«u½Í6:öï5Íu?ÄgoÞ Â`ÛÞ¾Ò:Eû¾)]±’sÿ’ôÊ£é’Tæèk=uqpy\KòPW¡õÑôÕ-µiÛ4Œ,­AP$¬#³”|u®Óæàª{Q é$Ž—hæÂé^ó”,±€ð24|]^ªwzh@i4¦Æi¡ÀiïXøi´åû¡’Èi8£7ò2Œ8D¾$î•Ô%ÛLCCÀ‚`/Õ]¨³´©ïõÈëöí7*^ëºmÐ4ÍJ jþ)wúf¶¯ƒúµrDŽòû{`6Ío•Ä çÉîž«0®’µãX=•ÚG4eχo²ªâírº»ûCá"Wc 6(ÇŒ*÷4JîF™ªFœ¨víšØµ«h&z‚‘†‹0Õü:­ïäP‡ ˆõ^ɸ߉)•ëæe¦/¨‡ÄŸ”™ œ€óçaûŸp{>7Gç‰êX?ww–c]ù3×7i-©qhh°kÓx ÷C²û‚Ù^¬ÛK†“£pTÈ1}ïa¸ÿ™Þ©KC;ꇷƹ$Æa*ôýHš4ú8AÐ(>Sçß`J-FgÖt0^é-‚…gãÚ îâd×*ÆW~Þב=Xg†?ïæš´4Ç lA#~–åFŸUÃúIþ+y'Ö]õF‹ WíbP?/sÒ7°Ÿ!f‚Ÿ ½ï œŸ ”÷¦{p¡¾‹Öw °›’éê:qi®úì]p?G¨à÷As‡•üBlC>ÿ…2ú½Ò´ëå°.1Ë¿©݆¼:ù·áã,ZÿùˆTWÔ'úð€µü&Ra ò¤aê·¢lN¢©g|ûòÉpw]ůñ)ÜoÞ£ƒùæ2¬sýœæ`õñJ_ƒŽ‡ù‹s!&¶ 7¦ßµH â°qX¯‹â¯ñYΗ敘ê²kWá°Ç'OÅ7ÂÝinLñ‰Åq5uÉöJíßñ5ñû¶-X†q+x¡ÙО¶ŸÅv­»¶aÎý×a‰×—hÓçëy_£L?‰Atž _Ï£ƒØâYצš„<¼ÉzS‰d”·~ÈO«l¡¢s¢ïkÖ¥Ítßí7Uå{²–öqŽéßdÔ _ÆwÐÝK¨£‡Ö"5ÙsÐ9‘4„— ÔÜ Áø‹ð÷bý]»´Èw‡yÐÙ‚¨ÂÌ€¢ c÷ ~ Ÿ´,Ô“œ‰5nÕ¹¦.‘´ _oÃ@˃¤È=cà5Ú舴ÕYÛ€|Vné¹eèKÊo´]*WX»|duâóǦaàMðo´3pˆ>Íã:ŒÑá_’Š®Õ>Jù=Ãû_9¿ÎÉ}uvY“½hkqF5>P˜gN¯Î=?oØÐv°iè¬3 ´dåêð[4- ÆŽ44Púïzä‘ùÔ®µ\ëüculèÏW®¦´¥&Ü[O=¬ï–#FÕ½·¯žÓÜü=Å éÞ«|óuwû Ñ0«øÒ4µ¹-ÿ@Óe¸š·mñ[øt¯Ê«ÂÝý›‹›b¢´ýhèWh“S! ̃@šÝÊÓrR$ÐBÝJ¡g\„Ýõ‘fª‹W®é­²E°g‡U> §HÐôo¡QØW¸Ü9uµ™.i¡uÃЄ¸gß?±Â9šqBÑ›hêRª70ÁTæ4];goNˆèQ‚XÒR%œ¥Û§Ñmý[÷ ’ލRk­¡¿b ©8«p2¾iCOÇ¿é€ í’<¦¹ÇÓ§²S&HË îý ÚœöqPÓ£Ä)®Œ´ƒ’δÅ@§¿ô¸Íö¥ýR™[¶gL`ßàéþ}ÇžCfL HÀÀ¿Ã0úWe*y[¨ï=ªÄ{¹¢Áð+˜Ô1–€*ä[, 6âŒæ¤5x¼qªÁg!'€ 4-´V›H>q.4ª“Ýîfsæøî ­¦cœ6ë«/ÂÙ¶s0UÄtîü¦EˆS[ZP´uï¹Xò Ü81arenÙž 0}O€…Ô}Ÿ&Àb%ÐRÞ, ÔÇT3‚Á¢ «¿c3Åz¬î˜\Õ bÚ`šÿ)\WgMNû0VïÙ}ã"@›ç¤0ænÙÝ‚ÊÊ ¾ª«î˜çËø²q¥”SÃRW~F•Ì•æb3Éì>FÇΆ 4<t°·Ï·ø°ÍúRœÎ NÇ€d¥­ 9ýçà󶮥8fè,ìânx©ãÇ›€Û­Öº\ ÷Ãf»utÄWV„â.ûǘ`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`1À±‚l˜¨+#Óýס’ÝžðWWaÄÃß‘S“¦ñ€ø–¹…æ¾#;sâ£}ºn„’‡àCO9JºïIr©]JHsŽoÒêx„]•#½™K4)ŸËÊôΫÊ?kZ|¾§šçê›z$»{æø|76¦Ô§dL9^¸’ÖdûÆooLéâ´0šàÃükB߉™À¨Œ)C ÓX"¤ø,;Ó{ }Ú2Üts! †@7(üY4¿}>¥ù|ÒŒÆm}¹A:»à ù£c ŸüüÔRÕ;8¸þ«lÆàªÜDû,Å÷P',üáY©I-_qêêvµYw›ïñ¶EÁÝ‹pøùM“SÉŽ­]ÍÖì æ¿…ðuøJ´aDr—âõ6Ã#=Ó¤¸=ËŸ1 åáHñ ŸkšÄ§KŒ4U@)Y \ÂoÛi¦ú˾¯íUêbÄÐFHÏÈ,hOmÿ ,_IŸÕ4×…ó2Ó–ÚöøÄäT¡)Ãþ]««?#­á~(S}n×~ChzßÝ ±¤¸kB×Eª+û)@w•¯†cXxóŒu#½þu¦P×rBªTÆyÐJª3R|Y-³}©ùv|iÀRÜ} ¾æEß¼«©.þv`÷>ø`›¿ö¿‡ß þ&¸ÜâSeÈ^¦0#ÞÉ ]@¥tú|Ãô‘þqšð|f§;×” ÿíJ30ÙÿØ&P_XH­/ÒN )@àš:&Ýÿúœ€wc¼°@Ⱥ~m‹—ûÒh9Þw†#iTöÌŸì}Åi¯{%´^ª›dׄŸ„˜Xæ­”do&un½¢ÌRˆùþ´×¿ky¿½®ÒUËxÕêu¤ŸÌ›ì]T+O¢|¹º²õxUèöt°ËÏB©äˆ ¸FŒl@Ø;ŸK^€¥W ýϳðΫvŠÌ=§@çR[hÛÅëZ]üípví1NÁ§œûjšvCVfú³¥ö_àºÀvÓ®ó3½¤E›Ðû°2E*ÔkáéphÔiv‚ hXHmÙÔx"éq»ÓƒAcæŠç"U¦#¥ë‡cf/š £Ñ¡mÃJ7°-Ö¢‘–Hä«ù˜¢>SÃ;1%Y2®d&Ü ûÞú.uú áB4ÔÍÐÑê\v€p^‚¾kË|úíäž´IÊØ–‰ga*¼:ïUp¿ »;m†sYÂüÏÆkQlÅ}s(„”i˜N i>Ë…ïÍ<‚ú °{k~À;Ùù,ÖûÊÂîîIŸ’ \‡t^+”8Ú1ú®ý¯ÓöY“½oP8w] òUfpGœ`àLB‘³ A=T)ØC‚-ܲûK²w 1}^Àû<4qÿÔ”(BúÆÚñ ±RêL°8áý)…öF¶?´æq5¡°*É—ÑÞÖUpòÿ§À?ò>ÿåÂ÷aé äýsv¤Fy3‡B}ø˜Gz®šãŸð³m_—WšÞ®.(ü±S¦tÔóÕ-¦Rg ÿ£®ßeÒ#žQ™SYÙÇt=¦uK 8\úóß9¾±{Éy÷!4©#?\¹Š–¡|Mv£§Ní ï5Nriòj„Õc“ aR…iƒSЬS›ÏÉ=™ªêe‰‹øÄßö q²â/]ê—]7”¯&Ú ×á¬9þ~•ÒuGV鬕#Üö êëY`Ûuà;—Ô¼s3Ó>µ½E[qÊòy²µ'EìÑýPäA'ðüQ*cž?#4‰6OÉOøqV²Û{]ŽxíÚßÇÎͺ´ë2ëöÛ‹¨N+)Ÿu «Õ•y;¾‘®n—|•÷™¹¾ôï‘g ­l˜@ƒ!€¾” ¨?šÐò!Ý !èü”ŒÀÕÕ…œšîÿ:åwI‹áâNXÒ2ŽÌÑsÞ¢w1ïW ûgÑðþŸßbM¥ŸþÐ ¬€|µvÝå{d?rK†6áÙ0µCRï›Z"О¦3ÎåšRëÉÏ· I·-¦€ 'ŸF×cªóM¬¡¼ÂÜÈÃ?Ï€ú*‚Õp;Säï8ž‡n­©W!?€Å®žƒ¦…Ôü&bØ$D£óƒtæ!n0™€ô¹0IÿÊRp¦”&âJÓŠ¤*„€ ?th+ 2[¦K0mmÚöJ&ÑšDx'úÁï¾tOæ¶Ço†Î~™Têü|ù›uÜÄMSn„ÿ¢É—®î{CÜ÷Çßyö{ÖÕ× C’f1dLr'U¯¡û® YÖñM4ùCQæ™/¡®ŒÅàè%5›âmü* —QUÙ§wɌʘz4ÊÀðãÙ!’Zi¬[ºfÛéùæÙ¸×°aç#åQ €Ïpü^¿×f‹ûþ+ùI&š2_â2òÿ$œÒ=EZÊò4²s¶e EÀP±áÈ4zÅzq h·^Àˆþ¬;|›vÞ¯l+uDE[wÏ@çñ&6œ\R çi¬aû‚Æ“©Þ)çÍNK£N{´“±.mí¼ÌôÐÔ6‚,T¦©åé…§Â¥Òƒ þȯÑ)5Cÿuì—‘¿öt¦Ò<$@Š\ý7j€[$ ph×þ7Ê;å+CoPØY~+lrŽYH‘îrÉs0ÅKkæ"ÒVyú{è ¶´kßìRß=#Š#:ŒÑ²²°¡¥=Þé„ÓOƒªx¥.ƒ$ü<+-–G,Àzµ#Ás¨“ìWŒô€ŒzE˜½ÓKë¾hÛîÑ`wI÷ y™“¾©à ŽÑä „…·Q#NCí ïzä‘y;òÎû'awÃßìÖ¶fvÃ׋œSßö{5½Bþq8Ýù>„÷e¶›ì«Ë¤ÄÆ­Ð`Š;³üÞY¥~…/µˆXöípMÓ¸’ɦnZzaYS¾#nߣ^ ƒÅ ²„ÈJýòy¾{¶Žòùß4‚*c³1•ÊÏÒ;}3Ûçë{ÂIä6šzIu#ñ§ðlCB ¦®'£²ÍB»ð´û7÷´Ížå»}·í†®–Ö1¸m:ڃנɿB*Š‚eþYzjÓ¶‘¨µG¹=ÚÑs|éß–Úÿ ³ë¡5~¿CXÔ¯–ØÈ÷v–?=Í~?Õç_aù« 4KCVËT—§¶; ú`)ÍÀdÏÁC|¾ªÛhʼ³-²Ãà+h xTÕr±¥³ÊV™“nÏhô-„^0³²èoßs2„§ý5—xÊéFµÐ YE#Í!Nûð{:B šÀ?¤0†ÚÏæ%è‘ãåÅÐ’’Zbh:né u¦ä?t¨–õ<´.ræU¥oZL…~8orzDa*êØ<ãM„‹™Ã–ç?tÏ=yÎ÷ks_UØNKÒ"·@èéá´¯í=òè øùQTh” 0íªð‡"Ç+º|Aáä9ÐÖ¢ïÝQx&üKwùï š‘ .R¦Mk‡ßÇûÈáÕÌb-5¹Äù‡¥U® ÏŸìÔÔ ÞÇIâfÚÕkLh§8Òv5Òö xÎ÷Q é4‡SÈM‰ÆT‹™ð‰¹÷§…º°eܪyzÁ©Ô4—æ"ͤˆ¶^Ö6þV¸±¦À•< ñY‰`6smBÙ™NƒÛ­Ò·ž!°;šG‘và«h`ûwˆæß:TËêµ-ƒÂ5µÊízÖé˼ûÓ—ƒÑv¨iû8íÃïÃó´Üs%½Õ ¨%î£+óåüæL ‘`Mj#ÉȆ–ŒlßÄß¡MÜôã¸>ã†Þµ:ô>¶‘¦ÙîMCL‡æuªm¯ °Ò *>8ímÛUv…6«JÏS|Óz©`ðh—ãM©ÜxÿNš®{Ÿ±Ž´iÐ8…<;ñùo%þþ?,÷LV±]É4lý!á>ûq•WäãzL„;jÝV‹(ÄG›/.—k¡iêÒÖ¢£¸À¸t>Ê„še(=$€½"óõSQ5—ÛEËH"LmÃB¥ö!4W•• Ë™&ÕÍÎõŠ¡w7Ñät»®Pºå%E¿`-#MÃ?î\gëð®ÂíúÒsÀ«‹GxÊ XäPSÚ‡¦2ÆmŸ+Äg4Ð »å­%*T¡¥„Àªh]ê$©Ìa ÇguuM„–|’ˆ¥^Ö&þTjõ¶Kp{Î(_`€Twa02>oG>i}O#'XÚÒÏD¡wy’*]¾!¶ÒhW ì–ÔÑýÈ cGÞþ¸®¶Ÿ´må)·t…aCã. Î’l7t&Om÷ØÈfkpm«ˆ×hË|Ä—Ù’ 4p,¤6ð lÈÑïîöÎÆúΫ¡â™‡é×zð¯ 3=ÐR´¢ÞýÁãèx*-˜R]çté“àÃÕ¤QË×ó.¦Î¶›ûäO°ÿJË ®ÚS,Å…˜Î|–¦3ÑÙ=ZÎ¥ÊÅ'ôL ØK,(3è´*ÄÏ~бîC ûçc8JçÓx—)lëÈ3øâù øýOhÚ&©©]˜¢|›¦ÅÙ´„XX?Á&w~fÆœ˜Þ"_èCêÖ“šÂ×suÿ…ÖZá(ù:˜ÜmmtÑýà ¬¬ë›´¶²8@ˆ>C¥£BÏ¥ùk辆7ÑæO¶/íNGŒº?pä®ñ¨ïaJú#w+×es'Mú«ªàQÖ®GÚ¾Ÿ  w\îvŸ(Ô7”‡“PÁ°¼Umï*Ò±æØ[â¥K¼ Íïc|Ó»ézÑX.²µ±±ÔËÚÄ¿$"•ÿ?Ï—þ#žÞŒ%˱thvŠoÊ!VxBµ¤· 6¯´Lb`Õ õà+l”z¾\VÝÀЮ¹¤¥0!3cüø|Ì~„~Gº‰6Oíwgß?&KNìŸU_£(óU{ÀO™@Ã$ÀBjÃÌ·FkêôÆfL»¥XWõSÐQB£J´=« ô”­-E‡´"ô †­…Xh(¹W/<~_MÔ›t!y-Ê;ØVqVž*XõyšÇ“ôAÈk©–Ch¬©Å&øU©–&äGé i}[º[Ÿ A¸#¬¾Ù¹§8WL7Ö¡¹ÁUÓ!¼~ží÷^a‡B‡öcƒ6æ2Æö³Ú\¡¡\V½jãGÔïÆ–/ ·S1:þwJ"aÆ-=¯amîÔÍ"p ~…V–ïä Ì`]ã¨0«ZýŒ5Hk‡ß ?hÏ‚ ú¶¹×¸ ¿+h íˆYëHƒy MŒ·íœWZ Mà2 AcÚ¹÷l!”ÜuhåyçnœÃ,> åe ¦‰'í÷c­—5‰¿V4×Ö®–/î5ófË IëjA}[GÀŠD>•É‘üÀŽýÕT æùÓŸŽômU˜ä*[û©-ÄAåסc2[¶oHš{Z½…μU쀖OÛáÒ»ïÞ½³èB¥¼‹°ït{’V›fðHÓÐç ü¥»kÈ?ÏÎLþuAàýÂŽ´ïG㮳'}#¦··BH½a-qIÏ6œŠ£¼Mÿþ‰ôÆÕ€MSÂ7bÜû©÷&h¦ûw%õ#‘ÆÐBÎŒg`±ä‹éj¶«p,¨¸qÀÒ‡2Aï5؆F\µô´ˆzÀQæCÕw&4´£2œ®Tv÷¿mþ”|Ÿ>÷,7x©esõKqPkeæpÓT$TN°ýŽTöóôü둾·çWñýw—t}¨›Æýxßô´7ýáh&¬Ý‰º±–OØáE[/kÿ²ºk‡JDÿø…óLå©\›¤ v4¤f(ó(×?¶mÓŒ6= ˜Bãü‡ã°žx73¼* ÙíÈ`,*Ùà×Ú{ƒ×¥ø'uL„~ý;ýÛr=Ö¶š.Ó>ò+ZmžFëŸÓ],eÞùß3Æ@€5©!x¬®JÞ´‚ü„c_¤M(èà/ñ8ËŒ ù„Ÿ'±VuTZh͙ەô"ܼ†ux Ôžàμ`>ü+1š¦>€€F"Þ9n\mOÚMh¡>¡g.Y^›F»ï[´”Àí:ò7,Úl˜æ«´ŽÍÓJ;Ù^2`ûÓÕÓù6ø¹ËÌ^?cF«˜ÞÂ1MÛb*Ÿ4µ4K‹tUü ^»›¶®DüËiÌ¢ð®Z'¤ÆQ]ƒ‘¦¡Þ¡ðó±;ü¨j_ŽÑA,ùBG›a`ò å¯ÔÜÖT¿´×JËÄWTlûx]¡±Î@yyÉùáòIò?úüi…¢nÀ‘H‹óóÍ?ô ¾XxöCÉî´‡í¸F(ûh“ž ¸Ûn"]žq>.f*”üG¹ýîÆír½Iv¨•¤q-g¢«—5ލî:Å!»®ë•a¾‰ã·ÅK),gï`YÏ9Î3pPþ½f3PÆQ›|øÂg*Žä²ê[öĉ»´æòX ŒÓçLU´ yöxÓ&¹˜Môy³×"–2»ïüHlhÃÙ0†A€6?åéyÚ·IÚZÙÆ#ú¾wÞîà~³Þ ñJm°Ù ìÚ‘mŠçYšñŠ_UþÐÚÄ¢f…²ºM6UùË3Òžm æt}Ÿþ{éZÄX^ÉmCÎ;¡Ñä dòòô®n¡ŸrXßÍ••Áº(ûv<«ºVW/ã:oX‰Ž¦Û(˜›žž[]9»Õ÷`²-ö†Ÿ§j§‰>»ðû_ûˆZþðþý·VÆ×v_Ý5š<­ÎÊž7†2_YÚØž 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0ŠdE«øÛŒÎ˜:È0³áso%U2mÿPØG&À˜`L€ $%Ä©d.b³Á¥¹Þ››9iybĬáĢ΄TŸï©æ9Áœ[…Tw%zJ)L¡ä©¹toÃAÄ1eL€ 0&À˜@l ¤¶†š 9¨«RBRl‚ôXO'|¾› có­iº®!5Å›ypÎG¦ô’R¾‹¿çµ–Ú[s'Mú«ibæT3&À˜`M‘Àè©S;˜ùæùJ©kðw”v¿ƒÃÈlÆûM‘G,iŽ»š’á¿S™â!)ÕAÜžð~K„Ø-`L€ 0&À#Ôtÿ©¦³¶Ã¥&ÆggzmŒéŒWš\ñòˆü)PÕL)ä+ÂÓåÂìÌq«ãé?ûŘ`L€ 0†J`ù§‹6{Æßž•fþÁЪŽ;vè»–²è‹†šžºŽwÜ4©4ů”|þ7ËŸþ7Lñc9&À˜`L€ 0'¨2Õx ‚Òe—Îã©'²{­ì¶æw´I oϧ)~áé|# ¨5gÉo2&À˜`›€%'A^B*ÂßüR9ªq'º©‹‹J»ø­MRXƒšíKͯA<ø&À˜`L€ 4$/A»ä'ë4¤&“òè!•Ž™Â¨à]Þ$=xvɘ`L€ 4m$7‘üd×Ù´QDL}­…T:¨¿äTù|ÄØ’ 0&À˜`L "©Ï“eÉS ts¿@IDAT]4]ËZ ©ô%):¨ŸÎAmº9åL€ 0&À˜ˆÉO$G•~™3vñµRÁ¦7}IŠêoÄ¥„“Ƙ`L€ Ô K~‚Ï{×I ØSwmãŽÏœ&Ãú6-&À˜`L€ 0 Ð'ãñ ÉSlj-¤â\Ô6ðo¯ÃO¾eL€ 0&À˜ˆ’d)’£Hžbã é~‡w|˘@" £ïºŽM÷ó4R"eLq™ø>ŽòeNø`L I¨µ&µIPâD2Fà.ß#ûåé“ñ©âk°k´=E?%Ýÿ'¾nòµÐä‚ù™éO6°$U]ŸOi>ŸÄ'±ãcFeL‚M K„ŸáÛÚ§Xo‡y=2Ý?ÚÁÙï °Gµû©DWxptí<©øvJÆ”ã…2_Båâë6'TtÁ6L€ 0Ä"ÀšÔÄÊŽ ¨5{|°Mžž÷®Pê¢fiÒu¾Û#OTRfâ,¾=š’;kH‚y£û7Üæ{°gÜ£¥ÄÔ ÿ˜¸û[Ïb°r»RÆ'õ,ǘ¨Ö¤Ö ¿Ì‹}Z/wOΛˆÕþné4Ç?ágG ¿ÀýcŽßâvTÆÔ£ S¿€Jt¤X€SǤû_ŸðnlˆÀFføV¦H•B^‹øGšŽlˆéà83&Ðô°ÚôòœS܈ 䘹àA=UÓ´Ô9™åÔJS=2cÊiÒ43°` 4­Ah_¿v‹¤‰a®À:É/0õ2QI­—iª±pw0Ü+5ùpÖdï)«1œ‚€hú{³r¦ÊµÆrƒ”g·v·¼šÞYïT_çÁó/\×ó|“Ö‡Üz÷ažié¡¶}Å4û2)ÕÓx6w”7s¤©ôÉô¬(üq ÂÏ혂?=äÞ›9ÒfºTâh„¹ ムìî™æóÝTh»©ìêq»ÓƒAc˜.Ä\¸¹ 2wNû_VKelËÄ⃳À§â³ Ëtw§Í_’f·(eŽ‚›ƒ¯_À4 q,vúg߬a:Ü.ùŒp‰gæúÒ¿‡ 6L€ 0A}&À CYÁGôÿW4i•î¿F*cŽ?ù ÁøóB k¯‹â¯S3¦œæÇᆙʼ]“jžÔÄD>¢ -Bø ñ¶„p+ Êÿóù$©à¶¥Â×AHa^NobÛý9ÁÀœoBh¿W™æ|ðþVs‰ë·ùXT{7-Ópº£ûÚ¤ƒ„Sú#  "6L€ 0†A€5© #Ÿ8–L *BCà\;bÄÈ“U›ñ3f´Ú½³h†ò¥ù~ïÕ¶kŸoñ¿sõ%KLÓ|ÂÕ ÎMCRÉ~nO‡^s|c­cçnõf¾W¤ÄZSŠÑÍ=žfù&l"n{üñ—жîÚ¡„q9~þ×ö[)Ñ‚Û+Y~ïý¶Ý(ßÔ·!¤®Òõ¢ñ°£¿¨ÍüÌ´±¾»0ÕíÍÜIoØá“ˆC³¢­»g`ãØ›óý—”zú4ꯑ®'!@Ÿ—åO{»ªÀŠõâÐØ¾ ä5ºMñ=ô~¶oüöÊÞÉÕKB44ÑšèÿòNùÊPÆv˜)Ó¦µS{ƒ“àϳ`‹íß¾™/çó~wj;ã‘Û¾2&ÀU7¤Üâ¸2êHq4©›«sFÏ÷ì)> Âîâžpº÷ù†éÐbΆöqð˜¦õu>Ãôõ;¶€JöOø3Ö!¼õøûÂ) κýö"øñ¦®÷/÷>~hÊó‚ÓŽ¦ùáîmh<ÏuÚ×ö¾xûž“!Œî-åSN¿T šU¤NšCœö‘î¥hfµ‘Òí á³…Ð fFrWf§®@º?t¨Ö£yþ´7!ì¯Q¼Š,ä^„S\Ò•]ö®úîÚ uç›N»x¤Ãéß3&À Ö¤6”œâx2(@èÌ…¶39 §ÒÔäÎåi¶:ܽԴUÊ4„¡ëâÙZÇóǽu s7¦ñ+|uÚÀÝpÐ6Ü}óý<¿‡ÛišøÍ4±©'Žëlû‘wHÆthB§Ú^«3ñнªè?A˜í›ø;4°i¦ãú|–?ã]Kà Oœ?€}Hsì|†ûßxB>5z[Ï\Ø•7ˆÛF8™x¦#ä)ß0&ÀR@&q™@´ Ñ\ mç  ¸¢™ò'e3=XÁ— b]¥ÀzÌfÎgÀ œ¿÷Ø[qF…h[AÐuCó‰ °Å©*C›­ªqB¯›R¶‚ ÔZ«šî¥!äºp»ª~ww{gc]éÕÀ2oŒoö=øWEnä»HF ØÛ<µæˆ¡0›EáN±?‚jÈÄ;!ù† 0&àXHMð âè1XhB}f(qÃ?üvÞ{²ªwMáZ.„.Ì|Ù îþ*çV·ì07¯­)g‡ùyzwxS^{«ÔiL7:T,sIáÁaMh',ð8d¸p'¡ßšÒVC,„[[:Ï—¾"ô †7´3lÆ´[ŠUpEPß9Âo!Ö ”÷Mªå³{—·,ý%U/¸_E¿0˜Ø€ô YhöÀeÙÙ °Œž–˜x§Ãö—¯L€ 0D'PA£‘èæø1&P9sÿ.OCôY£ HõNï_¹K!šwnõ=¤¥Bª×Vp§Ìk!,mì.,/LVp»…nåŽrå›Ñ˃vôóo§ÕŽÎ@ ÙáF #¬[-Yƒ[lå׿º=ø´¨ÌSA5ÒéGmîggNüI“rV Œ…0:°‚_J.ƒØz ÓÎg)ÓŽ€R÷Hìô_Döneb€ 4ËN?€=}9 ÖÓïŠ:HG9ÿù`L A °&5A3†£ÅjB ;55˜z_`Ž…úE+p¦è á’_¹•¶Õ4ƒp6V¹^—9éÚÜ„ñ7a×ú³8i[’T/")É”Á[ðþå8Îé:ŸoDqMâQÙ;Ð Ò2‚{S3;’\îEAÃìlêEbz[nWhݨîÅpŠhÓžé2IýbÉS0•þü†`]fÎ<âÀ%|ÿëçt÷ØtÿÝšÇcÐ&®y¾{¶¦z™X|:é+Ö<ây†ùJ7SB;^H×ìì̉?”ùÝ]w×Aý×+!2Ÿ ÿÊihÛµoö0NL¸Péï"Ì;Ýž¤Õ¦<Ò4ô9Hû/Ý]CþI¡Ìx7 oþ“&!r›¹Üïªc®¸;:cRWép†Á÷L€ 0D$ÀšÔDÌŽ¨¬Éé6w'/ÞÇß­Â0ßÖMýk¬¥Ì‚Àv>4”XYbæg¦ã\S5ZÌ«Š”\£«â_°9èJ¶Wde¦?g»‹×“Ø….Íu!ÎZX,Þˆ/E‘×›½ÎÈö¥ýb‡“•9ñ;œ·:qlæB£KL•!Ýò h*¿³ÝÑÕZ{+µ{ ðV,ÔzêoM©Ó³,úƒ8Óõf<»ÌªeFÐüé{Âî ©´rÂ.¹ÆX‚»’·@@-›“/}ñ¡{îÉkÑR’¦xÂ|-,Úl˜æ«ÐXåi¥L''Øaôðôõÿ†Ö{vQ°8G™úRø¹K,n´ÝØ×ºH‡í7_™`‰Jí}í ¾ðòùéË0µó™ßfL n›2¥³*vyžðM¨°ßéÿØ)S:b“Ž9wÒ¤òëSŽjqŸâ ܯ+=”í÷bÆ\ª»|ì§'ºf¥¥m«Ì[šþÞ ôHò´ÚcÏT™ÃR{œ¹ÚGm·;ɲ_¡ðð¥«íÛ$m>aÂÛ¾®®÷mÁÀþ§yЦª6±á\Z÷⋞§vÀƪÜÙñ¬ïtØáò• 0º#À²T±%°6Ü: ‚½eL  !udz&6ð‡ï6j‰ã$0&ÀjA€e©Èðxº?2¶eL€ 0&À˜؇XH݇ð9h&À˜`L€ 0Èxwd.l˘@œ Háy]J}ã,Ô8‡ÀÞ1&À˜@c"ÀBjcÊMN H`Yþ{Côè `L€ 0j ðtµˆØ`L€ 0&À˜@}`!µ¾‰sxL€ 0&À˜`Õ`!µZDì€ 0&À˜`L ¾ °ZßÄ9<&À˜`L€ 0j °Z-"vÀ˜`L€ 0&PßXH­oâ`L€ 0&À˜@µXH­;`L€ 0&À˜¨o,¤Ö7q 0&À˜`L Z ö0ÿÑS¦q6RØ[I•,…hSmj™%Ä©d®Pêw—ËýîÜÌIËYë59\¦ê7–€BmŠ\šë½¦Ò¦pÝOÀÂXÏQ •}îOë™|ÕÁ5(!Õç{ªyN0çVM“ãtSïNŸWlÓ²¹Ù¾mKÙÜãirZáÂ`Ðܹ;_íÉ/ÔÀßšØ¬ëÆ£m [Ïš9s\!²õŽMU¸LUE‡Ÿ55amJ€ÚÃ0göLê9Ë绉ڔFc¸î7š¬ŒKBÂÊ>÷§q¡Z{O €¬Iñf~D>dû3†Òµ® Â9K“Ú“†iöp`OuÜáýäö-›7«« Œ¿ù…Eâ‡Õ9⫟֪Wo’.MË-,,ùô ÿ{H„‰?V#ä&—©PØŠ €@¤6% Þü¯i>jS|{ÂuŸ‹ye"•ýúèOëK–ª,݉jïªmÄzúäÇòO?M׺0ȼ»”’ÏôèÒ¾õȈjç({vé < 4–ŸÄx ÐOܧ›X¿y{˼ÂàÕG 9%ï›%YpƒïXâ ËTIôàò»ÖK<0..S 8ó8êûŽ€³M‘R›~Ãø´s›†ÔžpÝßwŧA‡ì,ûÜŸÖV&¬š’âkáñxþÕ³k{qý…'7eÁªÆ¥‚¸%wn/Ú¶o?wÀ€ãÛÁ#[P­±Ÿ ùE.S 9÷8î‰@€Ú”]:¨æÍ[Î>üÊÖˆSÂö!N^\÷4ø¾&¸?­ µÚ¿“¨ Œfì'oÃ&©ä¿u¼‹5¨5Ëhâö÷sNÐL%º <ý´±ð…v™ÑBÞDÍ÷š%4º·¸LElj]1J P›rÕÙÇ»L¥º÷8ò Ûà°!´'\÷+ÍQ~-îO£%_w‰(¬ÖÔåö¸î¤]üý{u‹oŠ›˜oÄ8¶jÓf’ÞIø£uMI;Íe Ά ă€Ý¦4kÑâVø—èí ×ýxd:ûa°Ë~ïOëµ4$¢ª]{û„ãLSu£c¦ê•F# ¬”cçá—þíx$±)jS¹L5Ò²ÍÉÚ7¨MQJu½ä†ÔÁˆA"kS¹îï›"ÒhCåþ´~³6Ñ„TkÔëi–t6ÔOç ²©=âH<÷ëÒõ øÖ‰®ý¨}¢Ë|à2UÆ‚ï˜@\ØmJëöí΄‡‰ÚžpÝKn³'NvÙo¢ý©E½Ü'¤Š”÷¡/IñAýñ)ıM‹fJs»{ÁGšò§ TMeÊßê¨^.S€À† ă€Õ¦ –š«7üKÔö„ë~<2›ý(G ‰÷§åXÔÇDR)>.¡ÉîøÔi¢Å­>ò£ÎÂhß®•tyÜ]€S“Ús™ª³RÅ7eÔFK—ì¶&5ÑÚ®ûM¹€ÖaÚ›pZ‡T#{ˆŠ[²Ms‡FÁlâD€xº¤FGÆÐšTêTyYœRmyCeœËT ‰Å/ë7×ðíú{í× ˆ‚¢âˆ®\›#>YñkÄglYsÔ¦hÂjSµ=©qÝWøžV^AQáu]`ÍnßOÔ·ü¹[ü±}WBDo_Öù&ÜŸÖ{Þ“ ’(†„RkzF Uc5}ö+â¸ÃúˆK† ªqº¶âkMÔÈà<ÀûÏwíÍÙ¯|$¶íÜ#¦ßñ·{6“¸ÚŠ=ÝOv¯5-¡Di£?WmÊT‰WûÿÊêÍOksÅ¿^ýX<:þêzû 1ÅåO”u24µFŸüýÛÙÇWZ ó^ü@Üpáɨû}+d ©+×n§sp…glQ;Ä,A¾$Z{R£º¿sw¾ø¿÷¾@yɵú€Í“ÄIDrŒUþÈzU¸qÍy'…Àmݱ[Ü7÷¿bìˆá¢cûVâ? ¿«0hr¹¤èÝ­“¸ù’SE»6-Cî馲úVÎQéÝ0ÄÏë6‹#ìéqÚѵO÷N¢y3ZÑ!ÄŸ|# Ó©—Ÿ·pßü§˜òä–š&E›–-Äaý’Åuçô;’I„:ßûÓHYQçv‰$¤Rb©ÁsAdŠ\2ëGIo}úèŒÏ¯&‚º6g›ÈúÏb‘ä‰GVYÂ?yD ýïÆn¢L5TÈÇÒ[ ¸»þT›Ó#û‹aÇ&HøßâåâõWˆÑWža?.wu¹4ñȸ¿Ç©Ž”óšTKÀjSµ=‰©î“æó±ÿ{_èôŒqºèÚ±X½q‹ø¿w¾…EAqÝCÄ CûˆWü"®>÷$lF-³â— ¢E³$K°zòµODqP“þq¥‰]¿y{µZ¤a~Z“+^ýhy½ ©$ÏûÏ"1ñÆ D·fô-!nºø”:SiÜréi¢SûÖbͦíâå–‰Cú&‹ãô £Qò31ê|“ìO#æG]ZÆCò‰Wüì‘/m!õ»UÅÇËû÷ï/ý£Á$1ôØCB•~Û$ÞÿâG±ië±×ýĹCŠu ¿Z¹N4Kòˆ¥ß­ÆÁÕ'ˆ#ðþGðgÑ—+Å®½âàÞÝ,·}{tÕ…Q40²þR|»êwÚaoiz®~œ5J_ðþ—â›_7·Û%NG‡|Ö *°ÌÙú—~ÂáÖÔÑÂe?Ux‹EéÈÏîPB¼áGcפƭLÅ»1¸¥ò½àýebòèË,­†ZÎ+†ï|þøkwžtHq1´LÔIùãhZ¾™c.³’ž_X$ÿ|C\{þIbÛ_{•ß»¯=Çê´?úúg«NÑï6­h/_yÓ¦Usѳkëo}î6ñù÷«-ŸÏ¿[%N9úñâEu›êi¸®d Êð ï,E™.éOüG~@È'ZSîϽý¹Ø¸åOÑ •¿Ÿs¢è‡þˆÌçßý&–|³Êê×Z·hnõ;çó…÷°^J[¯/½·LàxH«NSXË~\+Þ^ò-ˆ¾É a>QtîÐÖꯪj+èÝpCéèÕ­£è“ÜY|øåObÒNBj¢Öù&ØŸ†gY½üN4MšÝ°Ä%ñÅÁ ¦mrÄ?¬µ:ÖÞÝ;ZŠ©¢½ðîRLetwüý,tv½DÇvmpÝ_´jÑL}poqãE'‹¾=:YqÙ¯m+qé°c­N6¨â-4Tdª ƒž¿†ð—?­…p|¨¸âŒcC Û'+~?¬Î7^xªÕøý÷ïёï¦WÊ™SŽ>Hœyüá˜b‰›Iyns¦kc7vZ{:ë$}T¾ÿܵ£˜’òG÷Ï¿ó:Äŧ#>EgH™ÂbnK¦éé7që]¨täØ)ÅË ¿B‡–íè q"¦Q# ¨ôîXûFÿ{KˬúsY[õíçu¹è¸?‡ÀÐ[àÌBËžâU” upk0ؼdè1‚„ªÿ¶ùÐå?o÷Þxž¸š1zïÐÑtl´uÒö‹¯!vK´öÄŽW(¢UÝänûËRتíö0hôHËš»ý/ÑŸ™îÖ©9¬Ç4P[ÁtС}­ßGAX} R~Ð’±hLU}HÇv­¬åíÑÿP4ì¸C-/ÿýÖg¢Es¸ãê³EÎÄÿ¡/#“u´ÏC¨=ëÄ,^nÕZ¸ 9ê ˆuÇír‰^èS/оèdñÕOëÄŠ_Ö „Òã/IÓ¥X:Ga“ÙW(öäX÷4Ë÷4Çô슩ù“D~a1–Ý,´úVrPU[ayö­1¥úùŠ…X ?øˆ,Ä'ë|SëOÃr­î&’&ÕN-5,q5=ZІ†FšK¡‘¡Ê@#_2°æ$L-?þ°P˜Tq÷CãàüÚ ¯dvaTM­Í¡†Ë6•…± Ú%jÀ΃–Öi~\½ÑÒuíØÓJm¬†¦Î>ñ§³º¸'¾ö_]øŸˆ~ƽL%b"ë+NGawÆà’úò´6?c xBi‡RY¨N‘Fõ¡gߺÚŸ6|Uœ9°ß§Á%ÍjìÎ+°Ü€É6$ô^zþ €ê¬‡äæ—õ¹ÖŒ ɬþ}‹5B÷´VÖ¸¶kÝmB‰Ÿ´9ŒÌ>¬“Vø ø¿DnO¢®û»1KFšÇpcÛ‘€FæX´ç¤Ù'Í=i_í©~zFv-¡äxKTõ³5g÷5ô¼*SYÒj.4¼vDºH@uÅéX¿ÙLœ€ÁޣϿg­ãÎÙ¶SÐzÍÃúõ°–¿ô€PMƒGۄײ%Õ¡íXNàŸÖäX}"Í.’¡5©$˜‡›e?¬v¶™¸ƒ=2¤Üyì…÷Ëi™ci+Þûü{Ëš ! jhdmïªóvÙºœÙiâktQH.æ1¸" *™Ö­hc»€Æ¥¤SJ½|FŸ‹Éó_E§ÖÏšª iþH†ÒÀ˜¦iMë&–þl) Wªp½º•TvÛ­?Výþ*óVŒ×ÛÖ‚vNÖ­‰Ï2Šº#ûžèh Ý68Ï8¤Á´íì+u–NCªCút·N H¹lX¥›"èÓ¡1¢Í$@¾üÁWâá¿'¹ûªwøÔoèÞyCu’ž'첦)ÄVo²~ŠÎ›´°´ ˆ–Ò´oÓÊÀî»:Š&ßìc¤M§iõp³§Ô®]ë’e)Ç@ƒOSþÿØaiûí©~z–t]pÊQÖ2˜±+û¿‹Åý)—XSòáþ†ÿŽÔ‡„»¡ß¿®ÿÃ*ŸycIè1m0Ü´u§¥ˆ!¡ù?˜±è !ó7¬©½íª3CîèÆYwhSî XsKËÚ#ýaã ›ÑÒ"÷èRÖ·íßµ¤ŸýsW´¿%>DÛVëÛ1£Iuuü}ƒYÒ¼lh‰Gøßï%nö]çþÔ™uuŸHBjD*ÛtÔkhÒþqŵ⹷—ŠKZŠK±'ÜlÀâ÷w?ÿS+gYÚâ¯VZS–>²Ôq¤0h­i’ÇUaŸ¦RH³4tÐ!oPxpõý»Î¸×wB"„טÓ!¹õc©¬SÈ.¬i#  ΨìÓZo§!íϯ6‹dLOÒ4þчô²:u§›ð{4ÀÓ–|»Ê*íçøÉк:ZÛöçν¡Ç;0¥o›cæ;K¾³´­ôì†iÿV\.¨NÚÑmˆ×D©s1Ç£'.Ú,D;Úi0e›×l²TÉÐJ’¡òÛ ›ªVü¼N¬Ù¸Uœ f¸¡eÿ¸øT1áñ‚6OQ™¬ÎTV¯ÂßkÞ¬¤Û¾ýMÕ‡›“ê%ÈÑ ›mIð; ']fœuçÃe+!lÿ)üc®°vðÓLG´†fC¾^¹!ä|ËŽ’£©Úb=¹m¢M“íž®´´Žf=I‰ã4Îx;í¬ÎÇ\îœiáûŠh=E"Ê`üÕý…¦hƒœi=*UŒf´‰† Ú¬AkWI“³cW¾eOÓ.t4޽‰£t™žõ¬²ÿh™Á h`iM in¾Ã*2GcвŸÖ`T¼ëMMëY¤5©•ù[ ûRÆMfÊ¿ÞÊT-ò$!^¥ "ër¶‡þHØŒÅôïÕBªK°€¦?^^v6)uþÏBós,f,nÿûpAÚÿ³÷pvUÕúëö6}’L&½'“FH¥DJè ¢‘÷°a÷ËSŸþEyÏîO±>QA¥÷„ÒÉL’!½M2½Ü^þëÛgöÌÉ”[Îí{ÏïÌ9÷”}öúvûÎÚk¯ýÊÛƒOÄÐ*4U˜à„DÕªŠs‡J߬I\çØt®m`ÚƒI!2`(³‹'tÁsÀŒ Uäb ” ¬“2 ¹·×<±D·)Ù$C\uQ w?ôüz6ý8!Ê0ÌY^Z¿‹–³M'LDdÀ,ÿ7ùà $ î’Ð_¼À@G™ô¡üÃ^ýI2Ï£ßæCýSÆŽâav;½¸~§8¾ì.×øH\½¥ŽG¦Òb¶)­,kìÁâ,a¹Œ¬8‰ 4ª2€XC3ŒQ?غöb‡IìË_ÛT+1¯r}Æð?Ò—H8ÝÔÆ£ b”cçcLÀ{Gl†‹/ƒu>ºìãXÈ&MªÎ¢ |½½øæNaø/4T¬Ëºý(^ÊÎGV¾M_úñß„½ fxÂ3ˆì7ï&ž—±„^½ŒþøÔÂV÷Ãvï¼ÄŒd¿ÿœmxPùá6F˜©‚B ¼Í6fØdøÎ§? cÚC³_¤0yœ‡è1dát„Öî“.>È6{˜¹IMð' b0P'ŽÊØpïÜ©Ú ~Ô‘XF(@Fá{iBƒßKt¼^_PØð¡Þ¡s†øû.=_ÕÉXÀÍó{0‰ö‘—7ð0ýë<Ñ($ìKQžÞÇó¢ÃB&© ¤([˜ÕÀ|Žö°YÉËüQ…ɵÚl÷‹Ävô³ñ£Ž¬Ûq€¾þËÇxvÿ8¾¿RLtzø…·„¿Ućr=çÓ7 {T9ACáж~õã7 øÚKÙÛÍŸŸ]G_üÑ_…'Œ+—Ρ3ì‰Ãþ/`ºð“/ßÑ'ÔÛ®ZÆ£"»„˨ÊÒ"ºç— lBÛçÁA~<øôZq h;¤Ýû ·÷9­ê|8òêGÒÌÿ“ßüÞ òûû¾uöIŒßÁè§äîÿøæ3³§M\ü•;¯M"ºØÅ L#¯›"ù4BÖ¸À^hU³Y•÷ ¶÷±ñ:ìYûw´°ÛÃJ9.á*d°§“?ÿÓ‡WÒνjÿú‹~‘c;Í[o0‚ÅTÍøTfü@Ž„Œ”©Á&eÉ„ÖÔÀýëTÊ^8HÄžm]ßP×ÿóçÿ¤;®»@Ø £#…ÏKêŸÿûGÉ@T骓ƒ$;gN£MÙ]wp×C?»ÿ£œèlkO’®ûÐF·ÿ±f ú¸:³YõÕ!=(Ï Ÿ2ÈUÖ`‡Šðà3o ·Xlï‡F÷¿û”P€ µ´ÀÒa¿Œ[î! 8aº6Xè_×»/Õçû§#Uu>ý©Ž\*Õ0§5~}kQZ“®ÏË,æ!@;пJ” "¥ƒ5XèÈ1t£‚B _€mg6„h‚Šô@«û5LfláaJtà»Ø}l£ú}A®ŬÌlú·ÿ±¦ei°ö>Ö8ºo ôHr}?&Ãt‹ðà R;ºrèºÁ*âÅ®´]‹NO:û§CÕùt¢Ÿšw ÌÐRó.«B@! ÈøÈÄ•oïz—}¢Ö‘Ýjv©ð)©‚B Àò­«ØG+V¾I­Q&|{ª™ªó¹_ªIÍýÊ>¿áªêŠes„ÏÖŽ.½¸n-™;…^á•ßà'x2¯ð$ÃÙæv^I«ð‚3XlæÄÙVöûõt:û _±¸FxÈ€ÿÔF^Í K¿^{áüW¿’qª½B Q”&5QäÔs …@B¼Ääk÷°#ôi´ƒW†zckw¢ž¸¶Ö¦ æO£õ;ëé¹7¶‰ó‡O6Òž\CíÑyöܬ Aز÷0]A£X;zÿƒÏ S/ûû^»mýãå4}ÂhQÖwU*˜ÝÔÖ¿Àñc¯n¤6viuáyÓ «Ja5(øLÅs#ËŠÙñþìsâ4Aê‚B N”&5NÀÔí …@rìáÕŸ°ÒÍ¢šI4iÌ)×/?Oh|°xÆ*&³·t_ÄÊTw¾÷âè[Õ±B@!0Óyé_,銰žW­ª;|šÆU• ³š¬XD³&U‹•µ°Œ0Bõˆs—p…/ñ[¯\*| ã£ò/5Œº O>vô?m|•xVýS¤ERSªŠS! vn,î^À¢ÔåèãHÁ)‚ÃfÊâÿ›X]!Õ^! ˆ{÷JT¸UÔ'·ç©‰£+űÏïgßÁõâxñœÉ=×åÁÈòbAPñÛb1+»q ŒÚ§5ÜŸ˜ÕK ‰@Íä1´“WzÂò£ÛxE,#¶Ö¢ ¯}¾­î¨°K¾¦Ž ب?zZ ÕŸ8ÓB'Ù¶t6Ûy÷Åü¡xï¯ÛÒ9Sú_>ç#RÞàâM˜ß¨ H%Ф¦]·B@!p+–Ôpçæ¦¯üôÚ¹ïhŸëXŽÃü_ÿÕã¼´c]¹L­Õ õC!3y8ÿÁgߤŸým%Ýðžó¨’mHõ çóĪ^bø+?y„¶ÕjæzÅ­âQHÔp¿DBí ´ 0vT9}ù£×ŠÃXW\Ú¤béÆùv‘†/=ŸLÆÞoè»oº$-iS/Qä Ñu®Ýd}B=ûí}|P1/fVlÑÇø}ç a'ï÷î½YLÆrØ5yMíz!ÐÛ è£ŠG! PÄ€&d d‡:Øuu^! ˆTÖ'EPcÏugü(’?fê …€B@'Ðy^wñüž‰T:E«¢Q( <@`pUF§DP(² ÷ßtÙÂìN¤JB@! Pd¥IÍìê¥ …€B@! P(C! HêPè¨k …€B@! P(A@ ÷÷ƒ=ÂN±É},ÏŻǦ 8Žþ-Ï«}~# w¹Š.CªLeoÙÑ;ß!©ÊûìÍïR¦wȵü/tù*ê\ìh°¤$—Ì‘U¨Êœž]»ƒÉñ~ºãê%4wÚx2™Œ‚¬¦äÅ*Ò”#€rõNýqúëKo‹•ÒQž T6—)M£¦¼²Ôã¯o£ÖöNŠp§Õb¬¤6ûXòó‡`ÐÀës}3DÂdã8[ØK_'µÔ7ˆåS{m ]¹t6»ªšGð­šm#ª=IyÕÊúzÝ—òÿý¥ wšú ëÇS@Š·]B5îíü¸SØ*^±d6w¶jø?3y¯,W«6î¡§VoËhy™*SÀöÛ­]ägaûîb—Ÿ8+*>Ìöq=‹ð _¬!ÂÃmfÖ¸òÖdMt|+ýó•ä!¾û³©¿‡ï šìä7ØÈLA²„ýd ¯Àò7•._\#4;±¾7–ûd¾«ö$´òóY µî÷—ßi·QkOÙZ'#ÄxTe)5óG«êO3’)yiÁT G Cñú|´kÿQzyc {\‹Sl¬‘‚# sºˆž{säJ>úATM¦ôçXÓ­îÓ@¹Ú}à=½f{V”'¤*e ¶c§›hÛrײ–³©µÏõ-ÐtyH¿c¡ ”}¯Æþë “ÜÑþ£´qÏAаyÀ)ËjtTS›iÄ9ZY{ØMepÙºð¼éº™Ó¨ö$ö¼Ë×; µîËüŒ–uDy‘¼”±=2ÒÑØBô¼êO3–z¾¸ H**†ä Amc›¸gÞÜMnc±Ð ê f2qA›»¨³ƒ[µ¦Ž%&Va¨Cÿ*d'(W^o€þþò&1Ä<̦Š2%µ' Mmô(˽ÿèiâYÔhI¶1ä11iÔʬ#äfS7ï:‡H&‚Ó1Û4j7WÐ Û ¬ƒFá5:é´u‚ØŠC-4Ýû=üâªc"ýák/d­*Û¿Æ`n0Ø T{22…s¾ë~tîJùyy#aˆÔl HÏn£çù&ª?ͦœ‰?-yπЩÂ.6¨Wh}º<>Öì̧ø!KÍÐ~!Mn­a[Y¤éFúUÈ>d¹z}Ë^1Ij¿=»ÊÓ»LIröö®zúÁƒ/PÝñ&ªç2»®ø XFGì3éŒu,k”«ÅvÌ>U\ÇDD=B‹e¶Ï’ öO‡©œ¶¹ÞC‡ì5´yï!úÅ#¯òhŠ?áz%ó]µ'ý‘.œß² RÝÎÝhùÛx’TY±3cCüÑéŠ>†Fµ¼ÄIín¯êO£ÉÁã¼'©ZǪ١v±ßÓÍûOf]§c’T¼åiBÚÖï>(´¾AvÙƒô«}Èrï ÙZž€šeJë”BÂ\f-»jû+Ïàm2”ÓÆâB«‰™ùÙŽÚgÐç:Ì~k÷ø–…íYø”ùŽQÕžd{®§&}² BÝA)ÿZnû0‹?]“¤JËPç.¤ï­Ýïªþt( ²üZ^“TùÅçgWS^v~ˆ'q@‹Ò`—µÙ‚´¹YÓ‹ 'H·Ò¦f_VÉr…òÔÅ_êÙ\ž€^²eJë”BTÏCû¿¾UÌÎßåº'+Ù³/s†HQ£e VÌçaÿS´fËaQ•ù®Ú“!@ÎóK² JÝïŸÑòwpÛçr nvÓÿÙLüFúàžJõ§™@_ŸwI…( †úßeRÂz ?ê“}ciã4Ö±{¤[Ì`N@ãÓ7VõKO´†:$V&Ëöò¹“)S’ v²ÊÃìÿÕgpÐ;ÎÅ=v§z⚎¸NÙ&Q³e´˜¤ØÎ#+¨_±™ïª=‰±ü»O–¬J˜ïu Ü‹–fÝðƒšÍéC:UšÍ¹4tÚòš¤¢ƒE'„€0«ö3A²¥ÌQÿÐPÇv>/±ÚU \ùpº‘~È¡Bö ËUS[§È«T-ü —ĉ–)tHÒ(ìP[;ÜTçXÀŽ÷s{¾åAGð³ºqw=×/,‡[ý’ù®Ú½JfîÅ#Ë@¾×ýÁr&Z~±’T{‡žçá]ÄÄ9Uª'ªé+ÏI*w²Ý$ÕÇÃü]¼a©Ól^£M|£3DúÃìsR…ìAù|iãeÿr¡<¹DÊ>`¼7¶×&!µ˜GfOF$˜’.c ¹Í%¼<«6Z;IUíI‚çÍc…R÷˰hù™rˆ:Xâ9oä•1Jõ§ƒ”å§ó–¤B $‡&ÐÑb6n ÀšÕÐ…Ø-yÀ¯)K–—¥‚HžÌ MC¤å†/ÛxË”VwØ+›œáÉFX‡û•çKè`¢ÚÐÒ³Ýwt¾«ö$_JA|rD—?/\‘¯u0TúËŸÊåNKC"ç‘NÕŸ&‚\v<“·$ðÊ¡ M#Ķg9dÛ©‘„îÏŽzÒ'²\!r)ÄS¦ #ÈF °)¼äK0vçø€œ±hSe¾«ö$_JAürÈ2Ïu(T ]þ¡°Q×Rƒ@Þ’T4"ÚFšÝ4’©Á0%±rêEºÑ—JYRò"i\ȼ§›èÄAoާLÉa=Œ@û;6AÁÒ£ùŠÂmT괉ɉèx‘¯C…è|v¬ª= ®¼¼]rIá̈§î–y….ÿ`¸¨ó©E oI*`ë©T\EcéˆR uœ±ƒœâ;Ol*d2Or.Wb,SOÓjšÔ·üLRó%”›Èê¤É£Kµ‘ Qdž—®7ßU{2~ãrú÷;¯ÛÂYÅ5ÛÿÇÇ®§åÅ=÷&z¬,‰¾W=?yKR bï?8ÑO,m¦ ÇYèO;<â4Ä·s'Ò·w{ªÎO%6½ofò+qô¦=õ¬hYÕñÐÈ|ú®á¯F—)'“Ôq%Fúô tÝ_Ûˆ—–§/\ ³L•)ȉ0ªÔ!4Žã|ï/Tw|z‘ƒ¶ž Òž3AjõFèÿ¶ùhùŸÚèžç:èâñVúÐlÍö×›½t÷ù6©M&´Ï¡ã¶©Ôê7ЪwNÐÍd2A›jìÓqö™ïÚ~°»b;oa î/°Óo·x(Ú ò\&ì·Ìêm;Ýaz¢ÖK_é. ±Å>ð]Ñéøuv8$†ÃÝ7Üõèº{ËìúÍ/}䉱¡Ì#dªî‹—ð/QùQÇ.]4“Öï8 úd»ÍJ·_³Œj&WŸó–ËÏ+Y=üÂzÚVw˜®½h>•9y jíØ”._TsÎ3±œiÇ^…ÜA@éÓcÌ«O.²sƒá£VÀgŒ01aÅ(‰TFäÅkÞé#ÜÿÌ>¿<÷~_cnz¤L¬­5Ú‚d´ð"\ÑˈÆ-«™ñ£¾’x¹ûäzNúúv¢?<Ð’œ¬Y ¼öuÑBv]d´Zޝo™ŠÐ§žïì‰ë•wýôµåIÕ«L!r^`‰¾»ÆM\rMV¶75öjèlÜ|ë-âYµˆŽ%1Пv)Òe¤‘Ž4Ê?šÎXÇõ¤3уbþ»cž•îyV“ù­cž¨šÜDgÝ!*æŽDöݦ0}dž~µIë¼{nŽã“¿ÞuÌ¥cöé4«k+­ÚVOOvQ¤l™,ª,2Ñ®r¤ ÇÛÃôÿÞp“‡ý‡ü> xºÈ×É¿[=4ŽÌç“Ý0ÃÂfDÏEµxïV8éÏ;™”^Ø«9þãv­¾«”¦Wšè@Sb¶Áú·'@ˆÛ”1ËæÏøÑ»$^Ù¸ÏöºÒVÊe}Çé mã-:èY÷£ãM÷ñÜ)cÉÌmÍîw‹WÛy(ßÇuêñU[踴OrfM¬¦Õ[jéXC³ØæOÏdv ½Í+Äa•¸Ï}è*Y^Bg[¸AW!ïH†õä=8ÑÎa¦ºÆÞâŸ{|LR½Ä#†ç„½gƒ4¹ÜDÖT×óFu"è_¦¢eX6ÖBÛO÷–·t–)TIRÍf3“U5ìÔÉß\³Ü[i‚wÒ©¦”Ü{¶WÆq%&ºzª…îc¢brüTmïGÞþh«á:¨G€+­=EK©ËXL®Ö}ìP™?<Ó¬\ÁGî>&œ¡¨÷~j±Ž´…híÑ^ÂyOw†…¦yV¥>ò롊#9¢ë~‰5ù†ö¿{™‹–O°ô1IgÝONªÁŸY‰Å3Ú…wµò‡ÞS«·ˆÉ™ÑOAà 3œ^‚Y—i+E¶wyÉã PUE‰¼¬öyŽ€jõbÈ`›Ù@UE:ÜÛ߇¹£A£3¡ÔHõͽp ¯R·C•©÷²}æŠ)fúÈ“½šU½Ê¯Hÿ}™“JŠ]äpØÅpwÈ1†aptv«™¾´œµgt¶1LÎvÑdo-Môí§ƒöÙ¼ Õ”þÇô{R©‰šÝ&¾½,í&6‘ùôb;d¢Ÿ¾å¡&Oo};ÊujÙXýš+¬ÀuÄ6‹f»7Ó—æµÑ‚™“ÈÂÚþ“¨`‚ñû‹„«*ÇLíjjöSÃi­Úi¤#}ùdL²ã¦É,t{2“Ië‡çÚéýÿh§ŠÌŽpÛ3¡ ­Š ¹Ž@ÿºä/••õêàº0±ÌD¿¹¡ˆ^¬÷ÓW_í¢êU÷3‰[e‰‹šÛ{Û³ÁÒbëž,àQ*ìeÄaë±jnë¤ò’Þ¹!ò>µÏOôkõó!U5T„S¬Ñˆ%œéd[X¾±º(q’:“µFßZQB•¥L(Š%±¤÷üôá•´sïÚ¿þâ‡_äŸXB¨‰7Œ—ÀÈ6_™4ôÜ7-¹û?¾ùÌìiåÎkùgâþC=/“3ýñY3íMp.Ñ`ejQµ™¾…“¾±ÊM»zj=ÊT=°ÙC¿ßê¥Y#Mô;î¨Ñaÿ÷­£Æ½£ù=Cs÷ ZÇyX“:LÌàAÎÓ ÿ66c@€uÁV¸è 6'Â:h”ÆóþLW„܈ÀjLòëÝž }hSv×ÜõÐÏîÿ(ÿ̶ö$gê~çï—_î%pÿz¾>¿ÔNßã²€¸t×}ä­Þ¡„'C;ÝÌm¢Y`§;Üßé®û©@¸‰‡ú+K{‰çPïØ{è-˜1ÊX[º¸f29íVª;rªçØ–vm„¥ç¤:È[zUy+¢>‚aÒT kºb sY+‚NÈìµ·‹å9uOa!]¦®å C褮šŠ­×þêË+»h%ÏôOw™IÅð·Ô¦º}ªe—M§¬“x›˜tF½Û&hMçp]Ùt"H ÙÌáßyF;0ÀŒúX“,ÝðàesGšiÝñ¾ÚådQÒl5ŠV¡E…OØtðë§9Ę<Õîë}w'»ÈBèàs˜Ý ÜoÕFMÜLW:Õ{Rƒ@t݇¹å–9¾pJšeú@IDATÛ¹ž­> ¬s‹¼ÇÛÓ]÷S!ñ™¦všÍ3ô1J3ÜhÅš-{é–+–н¼‚?CôêÆwzHi kQAZOs|*Š¤Æ˜Ï°?Ã?¿¯Dø²ƒïB`C‡M Ïè¿‹¿„ôVïìDyMíÑD—©ßoãrÄÛ@!SeJÎðYÝùîI6\æ/Çì’÷9²‡wùè íLR;éÛ«ÝômŽšÔèÉTˆx)O˜šÌÞ>ý¢/î÷ õÀˆÀ)áf«ºR›’:\:T|ñ\{~¿Ÿ>ÓÄ>Pc£Ç÷ö•K˜(WiÀhÕA?íOÐýT<éR÷¦躿‹½xÜôh;kô‰Ø+S¿¹™ªûz£ðÎÁtñ‚étÞôñÂשŒ¿¥£‹~ðàsò§ØÃþô¯/¾EXqÊÏË2G‡ çO§}¬UUî§¢QÉïc5Ücþ¢!yþ€n­éu´=Уױ¬¾%(4A]Wç\(S naÖxÖk¤3–±0ôjy¥‰îÃËÇòä ØâÊП âüísmôKžT%}Ë{“Ù›"ªö¥±N²0 Ow€†ô×{èfnOεÌíMM/òž‰fþèø¦÷Nu”K T÷½ÌÇà -:äK‚vdÕ¦½4Ÿ‡ñc ý ªÓa£)ãFÒë›kcBÝ—(Mj™mÏpáYÖ`SA! ÙZ¦ Q9Åd¢Ž.7k4Ôéì]þ7Ù†»„ôú¿µ w}åeýíÏÆûêÙ;A€f«ÚThå6l‚tºáõCÂ6T€›®«VC›Ca”«×²µî§ ÏGO¶DƒÛã£ß<öz¢«çr¼&©²ÓÑöÚ‚M&^ƒ<Ûƒ™ÓíNtú³=Í…”>™/pb¢ì/OÈ›xËH*Üm‚<ÛVºé§åÉո؀ß^˜úT ÿ†xËTI…MûCŪ¢PnÈ:ְ渷ð„%Í›X!üÀZ­Xå+ki†=ïòºjO$…±.ù\÷ËÍ}‡OóPû:qê Ñ.˜ô3ýìzœóhÝÚkR„8£óRw¨8R‡@Þ’Ô sXLdaßõfæ êh[7л=‡´ñªÆìš'6w‰¾G=—<%N—%^7‹Ë¤L¤Li$•gàó0?¶ñ•NòŸ9N#³Ù.µïTòH¦'…Å S–ˆæ-¦R—“ìvY­^Þxh’:P U{2*…q.Ÿë~ÿ¬c—PoîØOgšÛD[€ë¯?/z:þîÃP^‰ÃB&Ç©Á:.+s iƒ½Se‰ƒSÝK°³2±š(Y®F•‘ìl.OÈ¢dÊdE(²[h*“»HÓišîÝEõŽyLa‡'uâá ÿ1åÙÆ¦nveÅJ¨¸ÈEE¼aÉW ÷c9Ô˜mRU{’áÍÜë ¡îKÍi7Op µxæš5±Šº:;éÀéVêòøÉųî³5 }V³Qõ§ÙšA1¤+oIªlLÐÇÂÏ£‘;!ìGóòŠ¡ŽÖ¨4R›yD ¥ï¤©œµ¼“F±Öª;½H¿”%})Qo ™²\ÍWN¾ƒÙYž C¢e òaÄÍÌM[î nôÉ}ˆœáNªu,"¿1{;¨’` ãYü#'‰û)±¢4¨ÅÅET„ÍåÒ†û-üñÚ=Q1Ö|—õSµ'ƒ!–çó½îINk&ÑÒ¹SÉÀCçLP½#ÕŒ« íψeLñ±žmË«z}~š®úÓlËš¸Ò“·$( ƒ…†›èhyrĘb ¯¦éîÝ´­ø cýî,Fžœ‚4aâ”Q%"½2íÃix² ù•„èr5{âHÚ{¬™¦{¸<eOyB†$S¦Ð!CN 8kml» û͉å6vÝÄ.¡¼giiû«tÄ>“NÚ'³£ÿØ›KÄO“={©˜'bm/^ÎFÉÕÁb^9jnçFòLšy'Ú/ásØóT(ª°‡i|™J™˜––SII±Ð¤:²ÛØ®¸{ÒÔp…8:ßU{2Zùy=º äKÝŽœ.›7¼,ìÔýLüd¿ùk5Qs››ªF”òGmöä9üÍ6·u‘•×QýiöäK")‰½gI$ö >#¿z5‚jCz°¥±²õ8—‡‚íTãÞF{\K2˜ÊÞW#-®p;ͨ²3@:µÇH¿”¥÷nu”)d^Èrå`â¶pR9m8p–ËÓv.O‹3•´sÞ›L™‚œ o¨3v»\<ÉÈçõ‰Njt(Hv“‡N»C4Å»—&z÷ÑÛxj4¦vs%OJ<·YÁD x¨ò£Ñ¼@"àø¤u’8NèwF ¼VžÀVî¢P˜GM ²Ãä´x™UG:ÅÐ~I Hj •–hÚT õƒ„§EEºúç;ê§jOʱœ}¨Èõº+9•6üçOªm_sk'(/’·f|ßÌ.²ÁÍQýiÆó"ٜۛ$crÏs—ý*$ö4&B˜™ä¡3Áä¡bÍI¹ÓC]~^ŠÐw’ætm¦ZçÂŒiT¡í™ÁC’ãŠTUæêN§Ö "ý±L舯nŒ³Øâx4'oí–SŸ2ú—«ñ#K©‘µ tæ—'^ã>ƒåI¤O‡2¥iŒX“ÊDd®ˆI*fù‡Â!¡QA‡å°ø¨‡ÿ[là?LÕ¾Ãì;‘«•ÁÉZM'…˜¬bxÐÊšSW¨uš¼Ì_wk:MjÛiªûqo³yTÜ…ËÁ¤tº{•ÏÒ(‹ŸJ­A8µm24¥ ØÅEET ’ -*kT¡EE{(d%ôÏwÕž0jøúÐڹʜ½§Èźàh½µûÝ>“¡!Âæ”‡õ¥æt LLþgº¨±%BeÅÕ¨ *T7O˜JS:L꜎dIE#'B(r{üNjAÍmwVvîlÑázŸXIgd¨‹ý¦zé,û%^йžZÌUbÖ“¥*e~Tá³.ªüÇYÔÀNʼnÆ8ƒ¬AurúBc…t"½ÚP¤6ÜßN‚­í]‘€Ï×’àã9ÿ˜^e @ V®¦.!«±•¶t¦­Ü£[™C•«1\®\æ:Þê#£ŸË“;5å éHG™’D•©[û­Ùªb2üŒz¼^^:•‰*“TÖÖ ¿ª ¡ÀJ 2©…–Xheý¬õñf·r=à}'kb»ØTÕ:˺Xxe5rAÕ]Y l6À¯ÇAÅV¢;H2<°½,4£=ÚQTi/ÊõDu_šûh~\×™€#mñ„¡ò½Ú`&Û¿ÇƒÅØ ¦=ª d[Ýoíòóìû}äãnÎnw…ë‚—2…–sñ0ÃúƒÕ‹Xåofsº&îkQÅŒüNUb¯Wó‡kˆ‡j°GScbrZa¤¥?ÕKOlèÃÀb{W,w‰Æ®áøÑu%••_Ü]‚–ÍËsƒÞ#;UhZ ¡vu¼&¹FPå¯ÄCöÄÃÀžNEcɾ »Ÿ×½LAÜáÊÈØ(—:¼!êdâåÞ³Kç»9 é*SZ§Ô;yZ¡]eM ªÛ£iRññà:äIU ªDU؈² +¡½d¢Šæ:èebëaŒ°áØÎÏ—óyBægµ8495¼¡Âd. Ó£~ÈÑ 烤â7>HADq¼WÿKí)ò.Ñ0\¾ãºÉ”ßí °“mÊñƒõ›ù§¨cÝ{\Îë0\ÈtÝowûiÏÑÃÜæ09åºáà‘×h7—ΙBÌŸ.fë'šI±ËìnûˆBAÍxïÔ£íÃç+«²æ4ýýi¢¸©çC ›HjOC·æù'wO›¿ ióžƒLR“b‰=_~Ü©‰%ÒD¨¥U6ذñ ¿Ï+4AVî`‹YË£ Y"Iú¤ï“(†1ĉp‹SVZ*ŽEgËéEc‡ç’ Œc„IAûîÍê9®P÷Öƒw²ñgñó=2êY¦ o¬åÊjõRI·†eJÏr•®2¥ÉªU”_Nh+¡9£L01ä­IÕHª,óÁ„6çAfñÌÐò Õxêä#ï Ÿ;p”W¸™ÀK°%4b8°m4/Ô:ImÈ3˜5B¡‘Ttª‰Ù)B£$:ZÖ M¿+fMcˆTÕ^-ª>¦€ß;õÇ Gë¼Ì2°žXl’¨ï|))S-Så*SeJ=9| eíBÍ|F#¨¨;Ú+`„´Jm*žÇ†úm)H):P©Mõ³65ÚGÖ;<ƒ6AŽ¡%e‚ˆº#6îï!¨œXí=!Å±Ä éÑ# N¡MÀV7ßÛà'Û”“G=Ã?ѦR{²ªîŸmí¢ƒ‡O’Çd;lîW\lÞÂõ¢ˆ=Z€œ^¼°†JX‚ú£WÈTu9Sý©^ø©xbG ›HªüGƒ\ûÌ“Üü©ÏÞñÈÊ·Ë¿v÷F+ŒÑ’¨T ‡ZÇÙí¨œµ2°­óv9FÛÕé¥õ’ïCG+‡ 1{_» ¥îGg ØH'Ò›lðBÄø…ÃÁ`ûúŸ}…ãÃBËØdÇ"1OöUÙú¼”/%e Bgª\eªLá½èì 7ʳ…MWla« ¨/!1iª/IidU#Œ Ÿ¸7à²=¸|N¶Me‚ ûTÔ?Øèº‡wa1CùB›ÚmgÊõWñK"‹÷¥:d*ß!W¦òï–m Oëo{ó¹§ŸÆ)Þ ¥==!Se@æÿé¦6Úwô$uz|¢^8yn& º¸?Yöº5§Z:4"+ŸIõ^¦)]ùy²!ïe›²kãúßtt°÷ÂkOzŠVºË€Ìÿc -ôÎãÔÞåå7±©˜™MÖЗ,š=….¾‡ž¤ÈÇn\nHF£ŠÂMrC37hp°ä›²„]f£CîpãͼKlì2@v²è`á&ˆ©œØëø:M6@ÛÎdKí!û{w=µ}ÝÛ9NøHÅÆ>|¾À¹— ð< 1ee ˜¥»\e¢L U6dzpW©˜‚‘}¦jš®ƒ! …¸ j·–5ºÞ!~Ô Í6µ¯&ç£ßÓËuº ï-„öpE·)Gö×>²iõ«ùt!¶'}JO:ËÀ¡ãgiûþ£ÔÒÎ˳{¶¢bvœÏåý×ÂY“hé¼él6æÔ½?é#p¿é”¯–u{Õ´õ§ýDV?ÓŒ@¶‘T¨a@(°f"À®ÿþ—G®½ýN¤óc§šZ"·_sQUvi[mŽ §®dí6hN¥6'Y’Êi ¿RtjBóà Ȫ8î&§HS²öbâ‡õÝ=»žyýéÇVrœÜª´ØWà œ5µäqHK™~’0¥º\Ér¢½/õe*eCv6Ád‡ ¹{ˆ_jf{‹¦¼Õ£÷XûøKEÚâ3]ùŽte"ï£Û”Cu{þ¹ê‰G1Ì_¨íÉ€Å#•e žWˆÚ´ç 5µr"O4ðô[ gM¤¥s§‰ Q©èOv€“©”?úu™(ÿÑïWÇéG [IªÐ¤2¨•¶•>ü%—_u*¼ô¢Ïüäá•es§‹,™3Å0oÚØ„ý¨¢°ƒ ¢ri¶nè${gõGkrô ©Ñ+Þ) ¿åùd²> á3n1I* tlY»ú‘Þ„Ë)`ØÑ½Çq´&µ— ð…< ’¤¦¼L¿t”+¼C¾K–½Ë”xAšþE˦Wêþštä;®¼¨MÙ¾~ퟷ­[ j!·'ƒ–½ËÀþ#§é­]õÔÈË|"`2²²ÉØ"Öœ.™7•œ¬EÍ–º¯·ü®ò?Ð»Õ¹Ì m$(`x6„°Ÿ„æë«™6¯~uÝÞMë.ºî†Ù^ô*&c¥\`#Å[¤¬Ôe°[Øñ¨¡?!íÿ;‘WÈŠ%Ÿíÿ[žw¥c±2õs: ¡P°ãpÝÞµ^~qµÇÓ%;’6Ž[;oÀ¸ßBêg1EÈh™B ú—£þ¿µdÆþ¿êÿ;ö˜Ô©D >÷ÿÈ»ûçuÿ߉Ä)Ÿ¨M9²¿îõ +_xÅíîlåûT{"ÁdüÀòˆ€<Î÷è㢨;tšÖíÜOg[Ðd³/PÖ˜" ´B”|—¸!KþÉ4%*ÿpb ~ä»äoµÏ?²‘¤Fk¾PËaå&JeWW{øÕÇù'ÿ~aÖ‚E3&LŸ9×á,auØKÙpÜÆ-AoéÍ¿¼:G"ö5éóy´¥ ¤ ªèTйàCýÐ(ÊP?‹*‚*S µW ·)^nSÚ=]MG÷ïÛ»o×¶ƒ|»jO†Àl¸K±©ºC§èͽäT’¼Èépï̦ë±ÊŸMiViɲ‘¤hÍ—$žÑDÃW·cëNÞêø^¡iå=­œu$ŸáSy€°Â&my¡)ÅÐ>6|Žƒ ª•EA•)‰„Ú+ÎE@µ'çb’–3ýÉ©|i®“S)‡Ú+’A [I*d©€V ¨ü †/{ol¤#H*䀯$µ*‹)‚ÄCøR‹*&ñoTw÷Üp8jeòK쀉*S…Z"”ÜÑÈ:¡Ú“hTRt¬ÈiŠ€UÑæÙLReƒ } Bâ‚jãM,áËûþ$5ŸÉ*ð@¸ÈNCù’p¨‚Ìã7ÎK;Tù,Ÿ*¸ ñReªà²^ <²MõCµ'C€•ì%EN“EP=_Hd3IE> ÑDƒÝxJ͆ù£ ª´]9Íg‚Êâ‰L䌢;`bŠ=6\+d *‹ßT™êB(zm‰¬ª=éFŸENõÁQÅRXd;I•¹‚…Æ{hAÀä?È©$¨…j“ݱÈÎ{INq]…¾¨2Õõ«°m„¬²ýPíI’åB‘Ó$T4¹BR‘I’ˆI¢ ¡œ,%µ§Rƒ*÷ùœ¹²SÁ^nÀFnò\>c¬lÀHyiÓ[Èe*Y<Õó¹‹ê‚l7°—m öò<îQ!9$u‹B`r‰¤JQdc‰†ATíWa õKY… ù[ícG@b§ÊT옩;óÕžè§Šœê¢ŠB!Ð@.’Ôþ™' Fÿóê·B QT™J9õœB @Pä´@3^‰Rò¤¦ ¹B@! P(C@‘ÓÁQçÉ# Hjòª …€B ÀPä´À2\‰›IÍìê¥ …€B@!‹ÄJNžj¢=OPS['µv¸ÉïÇ\_mZ2rc†Õj¡²"'U–ºhÎÔq4¡º2™(Sòl¡ËŸP 0RER 0Ó•È …€B@!±S‹ÉH¯o®¥W7í¥ŽN^s†eÐ`'¯ÑÎnDà)QŸ`â5ZìáF2G¼ôÌÛ©˜ ë•KkhÅâ²X2×­ƒ!!ÿ*–¿å7°ü†¯¹a—æaýä'#°=KƒOÈ_ârÒË2/¿>¹«b‰F s¥9:êX! P(Yˆ@,äÔa³ÐžwÓC/l ¶Ž.j±TQƒ³†šx4`Ý™Ôs$@•ªò§§^ßJ¯oª¥;o¸PhW `ˆi ÿáç7PkgÕd N"C¨‚Ijêä15Sg¸AÈ¿šåÿh†äO#Ôõ*ER *»•° …€B@! ±’Sĵjãzâµ-ä6Ó¾¢‹©Í<"–W$}pƒuœØJƒ4ó›øçëtóŠEtÅÒÙd4Êõm’~ÕHùY¼ç3I-ò~½.‚‚Udä-ll¥öȌȯ—<*žsP$õ\LÔ…€B@! (Pâ!§‘H„^Û´‡_µ…-c©Öy>… :kÇ‘ Æ[‹.¡÷vA˜Ã‘0›ÌD5UZÕhùM¡QdòÕ°…Czˆqh@Œ žE²Õ¦MþþiP¿õG@‘Tý1U1* …@Ž!9…h hâ~âµ­‚ îq-Î¸Ä ÈHÇœ.¢§Wo§ªòš7}|Jˆj´ü ¨fßœŒË‚,ÒÁf°©–?ãÂHI-ŒVb* …À¹ÄKNe ˜­ÿ0Û bˆÔl H³³ƒþ¾r#}{\9V2™ôÕðJùa—Рf“üÐèFxrÕ#,ÿ´ÉŸMòæsZIÍçÜU²) …À€$JNY8³ØÛx;lP35Ä? `HkT÷;æ1Q]O¯oÙK×\8gÚ³žQ'Õhù-¾ó36Ä?˜üШš|өͰ=%òö^u^IÕS£B@! Pd)ÉSˆ„aîP(L¯m©³øÓ5I*^8‘.xxcûº|ÑÌž!ÿdíS¥ü¯o®³øÓ5I*^ù‘.xX»M_ùãM‡º?9IM?õ´B@! PäÉ’S)"´ˆ‡Oœ¥Î.»™š-Ogå¾Á2ŽÊÝ tˆÓ;}b5UCÒÃþRþ·‡ÝLMÎJ¹e¢L<ë¿Ó³WWùeÜjŸIMÎê- …€B@!fœN'Ý—û‚Azrõ–>o·²ÓûÅ5“hÙ¼i?§±©EÜsð¸pÔ?¨ÙDúØ]jí¡“4qÌAP1䟨65Z~᨟ý fs~Zu”?›eÍ×´)’š¯9«äR(ŠÀm·ÝFÓ-}¿Íáø†Ñdœ r%C"äT>«‘´5µvŠ•¤Ré¨_¾3™=Ò‡¯°4k  «ÅBf³)I’ªÉ•¤Ré¨?¹å³Âjئ›ü2^µOФ¦kõ&…€B@! H!L" ÷ÿúw·„"‘o³õè¼èW%CNe<ê…B¼ª’[,u*ÏgóÞk´Q›&`6¾Ý“¾@-¿Xê4›ïI›U7ù{¢TiC@‘Ô´A­^¤P(©@@’Óûøí9äóœÌ&£é³¼2æaýÁÒG(È$ÕçRˆôué4Ø;“="3TŸ ©H;dH4DËOáÜéôs~¤'+¢¸©çG 3KC$ž^õ¤B@! P( §?xà··29ÝŠ„‹Öž²Ýe‡»³ó—GOžÙea¡±Ú-†ú±A“Š}.…È5ÛåÊ´'’þB—?—ò;ŸÒªHj>妒E! PL˜†$§|ývîÜ>÷þ¯ýçÿ„‚Á^ÈáîDHž^iH$¤dH4ºü‰â¦žK5ÜŸ8vêI…€B@! H# §°9hXšS¾þ+c‘ó'ß¼ë®VN–ƒ·½’¢§mc®iRIóíŠdK9âá/Ÿ¢‹Gçô°È6)G¼ò爨y—LERó.K•@ …€B ¿`b9ýÆ]w5uKžƒIIprk°Ÿ9Å“Kl‰ù|â1$úæ$ŸÓIþ$S¡OERM=¢P(©G€IQ¼ä4å‰J†äÅ’8—Õ@~6P„ô£‚ˆIÌXÒ0Ô=©”ßl2ðä&ýä–rè)¿ŒSíÓƒ€"©éÁY½E! P(bD ÉiŒIù¶®/¢w[ÂôÓ nÂÀùg;èƒslTUd  ǃô/ÏtÐm³­ô‘ùvúÀ£íP†æMp¹ŒôíK¿úc;á§ÅçÑ¥Ñø±6:|ÌGyô,µ´…èÞ»«èà­|Ö*"jâT!溒Y! Pd! §CÍÖç$ßÏ6§“¿õ¹{¿5´ŸIôÔ .m¦ ÇYèO;<"íÿv‘ƒnaBú—^ºô/mô¥•]âüSu~*±è}3­ºÊ˜ˆ,‰<3X¢¯¹¬Œö ‚j³鎛+h×7}ï''¨Ë¦ýÈHñ(ÈéŠåÅTĤVÏ §,z¦KÅu.J“z.&êŒBŠèYÑÇ üE ZY%FóWÔì’ŒICÖ ë„Èç“‹ìôTZ<&¡Fúð\;}öÅ¡A~o'â?¼ÓG¸ÿ™}þèKqË´'“~GÜ/zÀa7ÒòeEôë?5ˆ³sg9ØãÑkë:„×^m¡¯~~ •›„õô™ ½ç‚bzéµ¶¨Xâ?”iOFþøßªžHER“EP=ŸRú¯ÿùţᦧÉh¨6D¨8„S2ÄŽ@Ä@ÜSFN±_É£áPèå?ýø{[ùiøìQ¤5vc¾“ CNÓ˜ŠñÆY#ÌôÚ!M‹:g¤‘¬Ü úù5Etˆ‡¹}ÇK Ú7ÓÞ³Aš\î +ÛkúS`¯c’u»­j”™L,ËñS§ßá¥Z‰,ù|r9µ9o£GY¨½#DÇNúh\µ¾šdÝ„Q¥ERS±zA#`øä'¿ãU>o¶˜¾Ä«©Œf·$‘b§=\Vâ4Ø-}ǘ²•4 o nmwG:Ü^£Ñlúî§¾ußé€?ø‹ÆÚ£<ûìƒn¾KVY*9d6³AØnEQ"]däï¢}n™V еӬtã +]ù¦9<̤_ÑJT߬›ËWñîLüYa¡ÎÎy½šüuõv¡Oß5JhN¡YEî\›‚4}Š=IUïÌIÍ‚LPIÈÆùê·¯1Z-ÿ ‡ÇÌž26²dΚ7m¬Ái·¥Ä}MF¤T/ñaâöúhwý Ú¼ç`Õ;õÇﯞ?å wNýê§þÙÿ®äÁ´6ÞØ üþB&§2ë«yb©N­™Œ2óvï bøÿÕƒ~zñ#¥4k„‰êCt†5ªÐ©V3™Í’Z^f“¢ü/ˆÐÿþòé—ˆ!þç^n¡OÜ9ŠZ™œ#´´©¼TQFþS9_€™^à"£‡0Þóï~É`0þ°zdiäCW/£éFk=Gƒ£Ä×àZ6w 6ã§éÑ—7Ž:‰<ýñÿüæ7þüÃû~ÎwyËÐÆdµÇÔÿAPä´˜3]Z‘©rñw;Q}“¦)uñp7lT;µQp²ó8ÂHž4„£3]ùñ]ÔÖ¢²’¾z€³¬-}ò…f!ïEKЍ½=HÍ­¥|o+ÿV¡0PÙ…™ï…*µ ¨ÿúõï|™ ê×L6üç]ï51A-T<”Ü1 €òñտׄòb±Øþçãÿñ__æÇ,¼¡ýT7C`rš­³õ‡HvJ/¹YsÂ9¹\#juLРUýòšÈCúŸ8ß.ìQw4hÄlb©vßá¶ü ©gTÌ“¢ì‡ âüržLõÒk­Ô•'ö¸I…øPšÔøðRwç&ø3ÛìöÏÏ™:6¢&Iåf&fKªa£:wڸȞwšÓôKÞ û‚&µà†ý•æ”s=ÁðíÕÃkGŸÝï'lùà~êû?;9¬X~äì°÷¨ò¥IÍïüUÒi.ÓmŸøüRîP«ØÍTAk¼TД#vm9êº}l ÇXpÚÔBÕœ²e–‹A¦=™ôË8 Uþ\”;×Ó¬4©¹žƒ*ýÃ! †úÅÅWqÔáP×Ã!Àåd%R6bÄ•|ïfÞ î‚F5?f·° …BÖœF“;‹ÙÄ6QÆ£•%çÌ‘ YL}õQѲĚÌèg ?sC~2„t‘?VœÔ}ú" Hª¾xªØ²´Î&2'b%)å¨?û2(S?ªÅ[¤«Ý<ÓMª—7”µ¼œ@UÈä”óôœà²[ÉNn-ùs"MÑ [ØKv«Mר!?Q¯]#×;2ƒåW+Vé kºâS$5]H«÷d ³Áh¨æ¥NûªR"¬Td·Z™'¦°õùƒdÃBÞ9Nm¥mûŽÐõÏ×etß‘Ó4at9lèû†½OPck']²pfß üUVê2œij®â$ ÁÈ´”—¯t‹«È鹈C£Xâ´‘9âå-@Aƒ6ùçÜ;3é3“\¶"QG£µ¡‰¦NÊ1ú)b!’½ò#}aƒŸå/ÖMþDqSÏ%†@nô†‰É¦žR€)Šá~£ÁPd·ð’.)ažêýÒ[»hõæZêt{ÉÄCkÓÆWÑG®»FU”ÐókwК­µô£/ÝÞ‡ÌÝ÷ÏиQôñ÷½‡ÞÚy€^Û´—Nœi¡²b-˜9n¿fYOjO7¶Ñw~÷TÏïèƒ/Üqñ²®Ñ§Òr|üL3=÷ÆvºæÂ¹d6õõ–ð_o:]¾d6in§§Vo¥gߨFŸ¹íŠÓbÿÓ¯ÜAVK®5Id*¶œ×¤*r:`ñ'%AcžGÕ•Å<âa¡ªÀñ¬&©HìG+Kâãi—r .éÀWäsR~;Ëï7°ijö’Ô¹¬:É?0*êlªȵ!Õx¨øó ±¡óM…h`VmÜþY‚á•¥EtÏ.¡oýú Z·ã­XRC£G”Òö}G{H*Ž1¬={ÊAJ½¾ñŒqÖ¼‹­ZÍÜÐN3Bœv9lä{~ozç]zníNúÞ½7‹ë09øþÿ=G½á"ª™<†þþÒªà4d-í;¬‘œ:v$Ýðžóøù‘ĸÐ7Yëyë•KY¼“ZÚ»hѬItÓå {†Ý7쪧×íZb¤b'§a¸Pì²Ó¸ªr±>y–Þâxä;?uëåôäk[‰ýþçn¥ïþîiºíª¥B‹ÜéöÑŸŸ]KõÇÏÐÄê4ª¼¸Ïë y^·}?A›[ä°‹Ž÷úåóéâ3(ÑôöyAŒ?ºËU´å,ç4©ãþ_ÿî–ûøí·9ùó¢ÅgrÒÁ×e,rþäwÝÕ}­ÐŽF#“=ž4ÅÛÜ •ä«?M¥ÁFj3ku3›ð@ºÊY“:¥º„Ð~ÈtC†DC´üsXþ­,ØØJÆpY¢Q¦ì9¤+ll¢É#õ“?e‰UŠ@â¥uÐ(Õ…@V! ‰jJ;IÆšÉÕ}âQVL#™X"ð2¬LR‹cüÛ^w„Λ1^h7&VCü¿{r5±ïÍž{b=ð²kS›6´Žg˜wòo¤KsÛÙÞ奧Y“ [×1qmf"úº=ÑãÞ¿½´ž–Íʚ݅ô&“¿û‰ëmnúËsëhùùÓéÓ·­ækùz,átS;í`›Uh™7î9H—-žÕóÞùÀ?^c²k¥›Y€s>ÆáµM{èÝgéý—-ÄþíÝÅyüs{|œÞ t5›|5Õ`„0AM&½=/ˆÿ@–±”|ÅŸœØŸ9ýÁ¿½•ÉéÎP$üX4A9å˜îgr:ù[Ÿ»÷…NP¥&dÏl6Óü)Õln¦éžÝdŒd—‹\¤鲚4£º”¬l'4#íRŽØK‰v§|.Z~|„¬û¹f×|A¤'Èé²ñ^òÇ‹—º_IÕGKv#2òÐÎCûEÎsgºXÃׯÃò k&Rk»› Q‘:ÄlW Ÿ}ýmQùè*úÅß_„M\ÔéìB?rýELæ&Ò…ó§SݡӂÌÊèqþŠ¥³™ŒÎ  Õ•TËW„ÝõÇ©¢ÄE gMä½NìÁ–Ï µ‡é#+72AÞ&:ó¹SûÚÍNfî'o¾œ`ƒZwø¤°M½ŒM nX~ÛøŽêy´«°a…ni‘“ÆŽ,#u„dÒÛó‚øP¾RVÆâOÎðO(r:9Ô/Ÿ)-vÒ?|5mÙ{ˆ{u3ýáÉ5=6¨òžX÷ pýfÍËPÄ+̘¨‚ŒóR—]uõh4k6¶÷?ø‚¼3ƒ™Ãû/×ln!ÓO~™~úo·÷L²šË¾F ˜ˆväT“ Óòú„Ñ•‚€â÷d6S~¯Ú̦tàX}þö«Ä­É¤W¾+Ÿ÷ §4¬%{„5Ä]Þ@ÇIHÈán>»ÍF»Ú‹¨¶ÍN³KOÒì®ÍTç\ȳÉaý‘™ *êˆÀIšTn¥±#JÈÎËH+Ò -¨ÃýRþIÕÔØÖE{Oœ!b “¯†AΜî TÔù 5 оæ-ž«Ÿü2W½þ@$ BC!ÊXÔ^Þ¢ö:! HªN@ªh²¤;¥á¤ª,sq`Ãô¾—“…09êÊe³å)1jçž\ZÜ3Ôßs±ûZŶN/ÕB[ˆ EÃL²á CÂö všýÃpn­›°dg2XΤú¾ÏÞÒ?ʘc‚ éºû…¹€$Ä~(@[¯MlJ!C3›È[×å ¦Ó~žˆfãáÆ/Üq5M§iZõH¯|O‚û”—¹DÒU`ä<ˆ„δ¶uéBR1ä ¢g±XèP§…?`¤@€?D[©¦ì$-êì ýŽù±Q… êt÷nr…Ûi|©‰¦.¡"—“\¼9˜¨"Ír¸_bï>Z~ĉ¸çMÅmOö7œ¡ˆ£‹‰êŒŒØ¨ÂCüÖì6õ‘§ÙF§Ý&Ú»?B׎×G~‰Wk{WÄïóµÊßQûžruN&ÀÀ½DªGY†Ho©™8…ᳫ.˜K/ð ÿlƒº`ëwvñìþÂÎ3ÜeXȳü_\¿“šÛÜt7»’aßáSÔÂ&Sxø~Rwí?Âö¬%Ü¡ OPÇô Õbè~O&Âú[÷ɨ“Þƒx¯gr ÷XËÙæžÎ0ù†‹­álanþMa—*fCWÄæ‰`Ö¤1´íváÁÃï¬eŒJ\ñJòÕ[êx²×b‚É@)w”2$“^G\{ÍkDwK^S×»c¸¹Éé9¨|¾“Ÿ aö’LÐHš‘ü3Ý÷›˜X<»}‘Ú*hþ˜.ªkè¤ë©ÅRE –qÔÄûTúQ…T¸™Â,~L’2óÞ¤r#c jqq¹Š\LTùCšå¶ ’j6©‰b åG\ˆqû|~š3q$Ùͪ;ÕE~ûv&©•dâYÿ†PEJý¨Â*ÜLa?&IÁÉàÄ2#•xŠé@§™‰¹ƒÞڤŠTY’žœüÀ ååÉëî:›(Žê¹ØP$5v¬Ô €3{ ¡½úö1[Äuú„*úô­+„fS>¡Ð¶³¦³äeô’ý¬64µ "‡IXr2‘¼g¨=<ÀÉ=&G=ÎÃêf—fC=˵ùÓÇÓM[<î3Ú)dЯ»5ñR§XIÊÂŽú¡3fnJ#Wf¥b&¦¥ùÍï­A ¿¿ï[—a¯‚B ‹€qÔo%wÿÇ7Ÿ™=mââ¯ÜymJ“çñúÅð?ÈS¼³ñMÜÚšt䎻cÕÀÆ›>ØÝ:¸ OWž ¶ÑáÁgÞän!"&‚ÁŒ¡‘ øïß>%> æñÄ.RÞŸ>¼’v×ÜõÐÏîÿ(¿=r;o°SËØToINC‘È·¤]I™Š]…àJ ÃvÞŠy«¼çÿoí¼éã+>÷¡+㯘AtØt"Hw=ÕÆÆ®l×ôÓw/&:¯ÌG­mmÔÞÞAÔÕå¡þmñÉŒ,b™Ãé †Y˜™Z*µ™x‰^·;~w *jYi)Õ"*bÒ›T ›ÆèAR!D˜… °'¯ÏGÅϱ1.ñŠ ¢þÃ&'$”ÓŠK1rý>mˆ\Rý U<ò[-É‘¿T“GhDÓÃÓÇÚ›V6‘1ßÎn®ðQ0º²¤Oò2‘Þ> HãIN•ŸÓÐ¥v ûÐÙ“'þñŽÁpy”C[P£çÎ8¡}g ¯Yoàac£™®™i¡kkÌLJÝ\Y£Çp ·1)t8¼TÉ$d. b­?çWoí½U~üÂn„Ód„},k5Åûìš *“RhPÅp5¹ S½)ÐŽ4mª‰l¬!¸4Y²Qþ¯áÿ^éb¬L´jm-ZPD£FXú‹óo”ŸwêŽÖï{•ÂǨ܂ÜbŽOÝ8<Ф‘ºC! È"ÞÏ ¬zûz𙵂¤V(£ûèµÂŽ7‹’™–¤(r:$ÌÐh ñÆÓOÿ¡GV¾]þµ»o4&úAøû­>:Ôª)ÊŠlFúú%Eì^-,>’@Ü09 îžll¯éõx…¦$‹`¦ d¢UJ ¢**ÛWbØ$šRÌâÇð;ìDN‡Ø0$¯§U¦{M^BÈ4e›ü—]l¥=vãrC<Õï½ÑEîGXPe¦;æjž¤['M»©¹§i„ÕÏž)4-jHhR#Ìc IMT› 2(¶nÛs¼ÚTø-Y1…k7ÍÝ”Qh;E‚SôiÉvùÇT›hÅòî‡É8ÑÓ/µÐüÙpò›÷hPAP·Ô2Ô¿³ó¹o­ÝÍÑðŒµžMU¥IÀ:ERuTE§P(ôF@‘Ó„i€† $*PÏÊGÿúäÕ·}F‰·Ÿjj‰Ü~ÍÆXlT7ðpñ†ãà»ÚLòï^Þëö ç@P…6‘mEiäaANY}‡™ïØ 9…&!’Šçµ÷i6° ‰˜l‰÷Šc6éÁ½éò}bE*æ,“ÿÚ¥´}·›šZ‚äv‡éõ7Û醫ʆ…6¨â‡õÀ;;ž_ó̯ñC¢,ñ“$qŒò…r¦H*ƒ wP$UoDU| ,@à/Ï­#—Ã&Ü>eArTD@‘ÓÓƒÚä!ÀÏx"û+ýýé…ï¹ìLø¢KîþÉÃ+Ky©ßÈ’9S óxÁ‰Áü¨þ|#×Âmsl4£òÜIŽR«†axh61QJñƒ˜Jr*÷2ÎX÷xörÃûäD*y.Öøô¼ïIÎFù-#½÷š2úË£Bä5oµÓ¥a±ƒsµ©ðƒ 7S›÷Œ`’T(èܼfÕã»7¾õ?,ËVAyÂo”/”3MÍÎ*臀"©úa©bRd Zǘ؇ýïŸ\M7¾ç|ª9¼¦!kγ„(rªK†J’*5© £·n{sͦº­[ê/¸æú«C¡àeLFJ˜dEжHY©Ë`·À-¼ŽËi—{†øabe™éðÛôÓ£ˆ2¶ÐŸöÿ[,½w F‡þ¿£¯eÃqyûÿŽ7ýåíÿ{¨ø¬öÉä÷ÚÙ #Bÿû›T^ïQZÀÒ¹X™ Žú9‚œ¬Ý»îíW^\ïõºÝ|6”!¸šÂ[´&U‘TDï HªÞˆªøY‚l©]ù6yxòÆËæÐ„Ñ•ÔÁþ_\·‹–°SóWx¨k.œ'Vm’I^»míÜwŒ<Þ€¸ÿ’E3é‰×6Ó=ï¿Lhkj¤ZvªÅ°RÖ  ðÌåz^¢túÄÑ´bqÐ"ÉøÔ>>Ð9ÞÿëßÝ¢\IŇÛwãK ãô°!„æ CýPƒyò ½þÔ?ŸçãצÏ[0yâô™5Ž¢âr›ÃQb6[z–¦Ú;æc‰ç!ñ}TÙ~ ­öðÎsׯE²›±ÃQl^: mo)9³éx(â†”Í @IDAT3xm2·§ÃÝÙÞzdÿ¾ýõ{vãÓ¸†/hLAJÛº7¸¢<¡\¡|%¦àUER‡ÆG]Uä,[ö¦[xéÐ6žˆzÿƒÏÓO¿ryy"ˆèÁgi)Õe}g¹N]Á>ôdÎd_UÉdÕOÛjPä&( b‰Ó½LToæ_p¢ÿØ«ér&¦òò¯l»E•¥Eb)Óœ-C Wä4eÀGkS1¶ ‚*U‘’ÀìÞ±·ƒ| }"îÃfuý—Ï+›Túa>fòmùÃ×±ît=ȉ 9ŠÀÝŸxò^WѨ‰œ½FïÙ1'ŸøÇ½OE‰‚ò‚rÑS6øÚRä94¨ ©­ÝÇ ® °j¨ŸAHUP$5UȪxFK³._  S®ßq€êŸ¦qUe‚„~`Å"š5©š mÝVwD¤´zD© ¦ø1aô¾·œŽ7 ­4ÂLâ[¯\*´¬;8žGN+’G¾+rX‰ß* ´^’ F“WœÇÊtОöhZùØX²ì—ñ^÷ «½§ë±Â 9ŒÀ®O¥—íôø.TfO®¦p?âżÖ÷½¼¢'•¸ì`rÛÎË<•S%›˜Ø¥ÍÁãgYËZA{Þ=Ñs/úÍáèsMý8ENÏÅ$Mg$±ˆ>Ù@aA“ªx/Hjé²Û>ÍÇ"¾êm¨×†äIµÏYŽßöøŒ™W~T9ïR›­ô÷>_È)H§$©ÊGùD{üÆyi‡Š²¤B P$5…ન™D`&ç?øì›tâL3Ýðžó˜pÓÙŒV @:/_<‹~÷ÄÂr£_»ûºréúõc¯‘™ÝË,œ5Qسƒº2Šœ„JÚÏP€€H’Šc©M…C‚ –]ôÁ*sYÕÅò›®«îùÚiÞTÈÖ¿ñÀÓ§_þ)ƒÑ4Òd4¸þ¦ûæ<õÏϿ¢É2MTQF@L±Ç†kJƒÊ ¤#(’š”Õ;iFàî›.éy#†àáìa$çÿö¿>.Žû÷¾ËÒµŸÇÏhæ{°_½ñÒ"Žhw/³½+6î¼á"y¨öÝ(rš•ECUhÄ@@9å=&V™*¯ùÜ¿2Aq¥H0°áÌs?Ù„cò®®³äóuïMú±ì“±¤&Çs)ú)‡×Pùþ£TS×Hµ³ÔÅË’"郉‘ªy÷‹™ò²2x)ÒLš4¦˜FùôGZg<ÊõGý+§[v“Ëë%‹Ù„ ðZ9eâúgcvæcBLuTRk¯ª “ÿ§“-8oð3ö£šZ¤¡´FR^ó’ú¨ÎÖT ù/ı¤ä@@Hjr<‘¢!àñxiéú Z²n;Õ5:9à½J6¥…¬JǹA,èØ$Çß§æ’CµÓ›Ÿl¦Ü,+-8o2-˜Y¦­þ›V¯E×ÿ£uåTߨ¢ü7ø«À¨ðôiX«ðkíXÂÇdBõªäñy5ýs²ìtéy㦿NN;ÄšUզ榦§÷îßûØ+O=…Õd’MÇÇ•Wx¦7HŠ–Ø9U†úu0úñÞlÈZìó5¶0Aµó/’©÷ÿèÓ1ÿæ‚}ýXå”RMHjJ=.6Ù(ßWEϽ»’Î68h°i/•Ú·ò~Y,VŸäRmtÒ3žªÓèõ¥Nú˜É᯽@³®²%(>vQ+ôöÝUšÕ˲bÅ*»ÍB†8ÊU´Z.jjq±þiÙº úÒµçÇLÿ®È)=[ßP½øÕ—o.߸q?ŸJ˜¢.úE*\žôÛÓÜ>ßpÈÊ“hjŠfN\¾+£Bà±Ç¦5Ýÿ½O?àJn@EŠOù ïþ€cI‰G@HjâŸHÐOøhm9½ºd=å«i^ÆÛ4Àt¨O4nÞªm§=%´½å:úó˺iÁLͲhh äoaüúoà!p# .Ì!+»#ôEδ[µÍÉîgë›Yÿ¥¬ÿŒ¨ôò?Í,I+Ñ©Sµ'˜ Öõ…žÒF|ð:7ЂB}|‰‚ø©’ÒÅ .V}~’Ê?z/`•…¤&És’š$BÄH]x˜ˆ‡öËé_m a¦ršnLŠß÷´¯µ1¾Ðø$mn¹‰ 3G¡æ7/–5Q—U5Tÿ ›• ØzÚºXU_«¯ãA…¹t¦¶‘õß‘þ=‘ÓÙSJÉn5Ó&&©’ú> ’T¢ÕýH3Q¥TÕÌÏÛÿÎfï¬ó{È.·û!©}¶4ÕÿAÃ7ê¬ ÄýNlAÖäh¾ÞXF4˜—B2vx\ˆjPÿ‚: ?+±Êsë ÈãôYžù²ls¯õï-9M¸‚"@\`Ä91 BRã‚rrVZ˜sÞöšúUˆgœÍžîÅ÷ÿxMñ㿞S•œÒ¦—T‚*½ž·hc0[ÿŸìƒŠ!~XP“)AžC5½¸x5µ°¿&B&Å:AÿçØCü° &S‚0ä­ª®ÿÒ „Yü}5I*\ý!ä[¾yw@ÿ]OÐÊ­{ÚXMQ¯>!J÷9 ·-ÉŸº°ålNH@TêOÝG±ä*VéÑãøõ.$5b$c[PHjlñ”ÚÒX=EõM.Ëa¦’9óÌÿÍ¥t€å[RÄDU!£Ñ•Ⱥþ M-ÌTT•Źp¦ÝB5<‘ê“ •t¸ú,áEB#9óHêUÕÇKÚ¶&•6臲O‹uƒêhÖædÁþ>$¥¦2ÜŸ”E„Jft+bùþ*-P?â &s‚|XP âÀ1r¹Ýš:DšÚêOZÔHëê‹rˆÓŠ0­ˆÀp‚gS1!ÁÊ2šåT|Nûâ ¤@ Ö¥äÏŠr×ÁH£ýãž{ŠG˜°¤1– ÉùéO×&ß~iô`¨[îÆ>•’ ríñhV`]på×ËÁ’œjúÃæ­þáâ%ù“öQưnkR0Ü+)Mà S’ªIMŠ^ $5)ƒ‘JèÃÝ©FÒ /È%6èiJwý#ÅMÊ%'>ò•„H&$5Œô;ôŸ¿J¡ý"ý H…¤&Ƀ1RÝ’¨QSÍ’ÊsVÛbëz„‹º^.]õ/ÉŸ(JŽ.%[ÛÏèDzO?ØÇ>à̯ÉìôC ù4’š|ÏD$JrD-Éåì È)þ˜ab‹4éå#¯!Ò–£,#ý£”BŠ'LLý“¦X.î"MI&žˆÓ‡¨d <…Ô@¿èC¤©vImˆœ ½A ’×›úõÅ|þŠÚ/"®S F‡€Ôèð“Ò‚@X #]tÇr"žmîO ›ûºêþÔ_4+PWqÙçhþ]kéò{+èâ;WÒÈéwk÷,™éò¯ï¢¬‚q¼©t0yL1ÝwË‚@ŒÖ¬ }ù3óè;·_ÑAÎîM?‚îþìüyå‚ )<¨ #R‚$%Ò ¥\Ê"`#sàùóxS _¤¬Bý@pý›²¨"*ñG  ¢b0ÓØóDûÖÿ‘Mš>2Ysiæ ÏÐÒk;ž7tçû}ü÷9Tµóe?÷§dÉ@®¦S|þ¢Fl; óB$ºDRF qY/ž1ž>ݲG³è˜O_½þB²¥íSW÷¶î©â8¯&šRZܾHØçÑèvcR iáþ¤}4}.Xfffc Q•²Çr0$Nj —†S›HÎÐq7Ñ`¥c»^×T7[sÈãl¤MïÞMó¾ða8v,ùQàüðŽgiìœRÞéT½ÿC:°é ÍÊšU8žkÂ[’U—=ùuô:ôóÞî'F&^¡jû¾*­È ‚Z»c?1I Ù“Ç´©¦«{ˆ,°¾üÍåÕ¢¶ïõ×Ó¦`'ºìÑèßCr;åZÌÆ5m¼ç¾‡û¤LšI¹çÀìÔpÌýÆ1 ]¬UÄ+òeDW£”ŽBRc¢Ô!ô¬eT_SÁ׫ån©?B[ßG¹%Ý–.v^ÝTW½MËçhHS\ª¶üZL(üÒx:!`ÏÊDóX¯U6Y²hÆuÿ ³Ç7ÒžÕ¿iSÎÑpŒlÙÃÚ\Kö“œL;54FohhòבËõIÂEàž‡ù®ª*‹ŠæùÁWш¹ð8Ñ’G''úÙ§ˆÌ ¨¬ù† È‚þÂDõ{|)ÐWÒ ˜*+$5àKÓé…€£ñY3‡ôJiƒÑÊõïäj9MÛ>¸ŸË`*˜lÙC˜ðž^H#ˬL[Ô’bÖ?RCsKÔuIé…€FP}êïg–4üøÎÏÇŽBÙÖà×`ƒSXjzõˆ¶Ú:Áço³ýã¯|ƈþÂöw_ûù/@Tƒ¦mq9‹v@•*ÎhªÝO™ù£;»Õ暢霫Ÿ Œœ:°ñ/”3p OššÑ¦lFî(vØß¦\²ŸÔðPanôQ] ØméL]s²«,ò%âç Í,¥Þ}ÓÅŠÅlÔ¤Ë .Qçlûc0‰ÄQúæ’šÁ$ ýýý† ©¿¹ó‡?»š/£óˆUÅ9 I3ÀR½  #Ðpªœ² Ë8F¨ÿËQ¿Þ~oÏ)¦A£.%kÖšùÙÒœ[_×¶±³ý3þá§j±Péí‹&õyuM= ÊÏ ÄHTØ¢Â\&¨äñú' EZ”K0IÊ ž.œG_¾n^r‘k žÖ IMŸNщ¦--AKjFF[z„~3lP¾j³eüõ²ËnEx*dvžNê“KÑ# âÑc(5½BàØî7hÌyߦae·j±NõBÍu‡hñã#ôSj¸Ñz0zÆ7éä¾ÅÔPSÙþVRŸïØ”.8g,M;œ¶ì>uÕ¶½„­³Ôþž‘c­žÇ᪖®O-‚Þ™nr­ï¨rUݯ(4ì¶+fk–±Ð–³­AžÑ $5š´;n%©­–TXTo¿r¶ñ·Ï..6mÜýô=Æ÷ܼɯe¤8ìÛþTˆCR¥ øP}ª\ù0Oº/¿ÃÁÇ’QHJ.¦]ŸþW8Å’"/bœ~´n'M$äá V6ªˆNÕ6P9^I‚@/0Ê÷'—«ð1lŸr-ÁÏ¢XRÛ£“^ç¡$Õnö ôô#«Ýþ-¾få †>áQ:@qØ ¸qUªºBÁø×¼r#ߎÌ÷ÍÕ\CËŸ¹­­»j"©¯ï9|‚žygeÄ2îØw”^|MÄå¥`Ú! ÜõÇfù|êY“Fwd GvˆÅ¬ÎÙç2íPí§ ‡’Ô {çnYèGCuðu_üêl†ÍâŸÇþ $5ŽàJÕý^‰$j¿ÊD!£ËŽ}¤I¯#Òò‰,§Ëþ‰”_Ú›Q¯äç­N)íž°A>È™i5kn ¡ÖÐHÛÕõ÷ràÉd·¦B>ÈKý#ÅMÊõ)ø~3**åØÌæ.¿ë@P‡fÀ‰¡5õ$‡];qº.&J‡ÖuüT-½»r+±odLê§’/Ý{ó‚Àµöˆ÷[qà8M)-n«Íyh]UÕgèíO6Ó•çO&“±gYÛTÄ'•ÓÈ¢dã®á&ÔûÓEçŽïPôç~•j8ú\Þs²2hò˜búü•³ÉÄ®AѦhdîMÛ^m¬±É?2d2)”—ۃ̪Šþ…†ûÁ¥„¤2±Nj¬•úú5:AÃK¸¨0›ìU¹§%µÎÏʯÐÂÜ 2²_*d×õWp½\P5µ¸Â­¦OóC>«ÉýûTpi,¸—kqÞà“ŠãnSI¿Ôàä™n ñMö¥‡¾vÝ÷¹„ðhϽ»ŠÞX¶1Pì®.¤»?;?pÞÙAù¾côÚÒ Ýjs­³ºð£3ÜRüÄ¿–RmC|Vl›;m,ýèÎkéŠ9“iÕÖÝ´¡â`¸"vÈo™ÑàéšàPÿ€‚žíwüÃàëŒVïs¨NRŒèùIĨ!©Fè/8 ¼‘­Ø&ŽD÷úè´§„˜%Šë¤§”ÆåhÖ ]nèij«ÿÖÿ9]n²Z·ÊD*CoËA.‡ÓSý{Û¶äK8èäFŽöÖ#“b5;Æp¿É`$^…HStÇÐÄÀ÷Vn£ù3Ê4ûÒûk‰Ã_Ñ—®Ë¤°‰^ùp=í:tœr23èܲ&¶Céù÷VSc³ƒ~þ§Ѥ1Ãè WŸ¯]+\ÀVÉFZ±y7}ó¶Kiíö}ºtdWnÙMï}ºÝn‰®Ÿ.Í™2F»õôË)‹Ý‘>Ç‹ ­Û±ÞY±•¼ûzzüÅø3á¦ß?ÿ>™ø=ðËoÝ¢åYÍ j,bÙ!ËÄÑCé‹,G46;éÿÞZN{«ª©„­¯ƒò³µü]ýËÉ´Ñèaµm+/Ú±açþ€\kwìç6¶0-4jèúâ5çÓ@^…£üï[ÚùÄÑþ( cr³ltýÅçv*³Ûã¡—?XG›wÒÞm fMÔˆ1ä¦íñS<¨+‘µë§Î„Ôn†úƒ•h?~À¡@P±EþR V*GíPÛ"§‚@wè–D=“ÉDSGQßæ¸Žæ$G:þqn—Î)Ž­þÉ¡HÑ tâÐcÖɃ_…;O{Éî‰ì!vÊÎÀdo&}L°Ú'|n@Üjj›´¥}õûùLP®¥ö«a9œzä×t;BqýüéLrÓè¡é'¿¬AXt»J°ôg -|R‡ ðG'Ù¾·Š XŽs'”h÷xU%Ú\yˆÉôö_=F³&Ž¢ù3ýzïᓬŸŸxk™Ûýƒåw}ùtNbŸTøÍ"Á ëî—?sv?ö?<ÿí«:ÉDÖoÖn´ûוÌ;ö¡©c‹Ù8[Û Ë&–ùÊó§h5ô¿Ð¦UÝ–†c¿W ½Dïg‘¿T{ÕTzf ~2ÓSѺÿ#ûžÊ³»›n7Ž£~‘èÃÝ |6«•_ötš­;N"j¾¦Û_#“œ%Ú× ‚zÔ3‰Fæ[hØ?e·i²BfXAc1ÜßQÿ::}–¨ /K~ìk½õö4 *TXjâ¡¿ÞŽÃåVyRLŸk}¬u¯ß–}r ЫÏ{>/YœÃþåõþT»8ѤAá=ê~ž…¹¬]sÁ4jaâùÔkËx(:Ÿ¾tÍ4’‡¼»J°°öäB3t@žV¾±˜¼u€­•‘T,IÜ]ª8pL#ž>ýn0#+ç¡ã5<llàúˆ!…lÁ¬ œ·?€KÀ9ãGh~¶º‡ËÒÙú&¶øú 2·.ÄŽÉ,_zñAî"ÁZ½›W¯ÛÇ.Øš¬'D@ÐSoðÓóbøh¤– ïõDPô/} ­NŽc„@øŸÂ5,Õ}€@à-çs{«këšbBR1ä ¢g6›ÉÎä/“ý˦ŒDö‘Ú}rï  ©Öw⣠T ñ ?»Ò!9”ÅòAFÈ ™õáþHñï^ÿ&òðX‡zú‚´ýîÊÁÚ° ÆK½ýÚú&ÕírùÇuõ‹‘®w,/G B`ê`“T?QÙV툤®Þ¶‡,fc€€…ª²†aî<¤ÿ컟Òÿ¾þ =òÍ›C³´9F==¥jÞi¨ í·°ï5\0Dît(WU×v[• .>³É3¨ GóÕ+€5¸»4‚£ž`æ?ÿˆã!÷M4sb û–fР‚l&”‡EOž©ÓŽáêÿhv…È —¸Ct– F„æÏ˜@7.˜ÑYí9tz£“‹'O±ïºÃOä³³Œ”â£ÜIöÖK=»‘t]Vîô4z[µä’·Óy¬¡Å©4;‚~G‘Jç'i~ ²%•‡Ñ³23)3+“&• ¤©Ã²8&é ZÙüo´ºùËt„gÖÇ;Ž*êG;hí:h•²Ý˜A9”¥É!+döÏðÇÿÈROú³ù…`Ѩ>ÓÀ3ÿq£Š8¨híi–ùOýúú“«¥ùdd(J©8#vŸb9Ý~28‰¦;9=Ü×àw¹ƒ}#ÿ¹hûoîdÒs´aíöå¶ï©"Ä7:0Æ—ñȆßFS„m‚¥ý8œ´t]…æ‡ùÞ§[5ž±#kÅKy¿÷HµÖÞ®C'4ßV½^N|†a‰læöà:mÜp:Í2@~§ËCõ<œ¿÷ˆ¿kO9TF?ÌUÔUÁá«z“.å‰LYüÃø_ù#ÀªŠÉcKZeþpM¹†Óèaƒ´PX°.oäHðýhíN¶:-›É<­µkË÷Ñ.––UZÝ'µ7ò…æ ê/éýPhúqØýN/(ûÎKjç¸ÈÕþ…€zâÈ¡Os ¿GûÙ“GG­~ùc┕'#ÁJéf €—C»Œâ—ežÍLûOÕÓÉÆÑt²¥”¿T¦×L6CÓ ¾x£K²bÅ+êÇÜ“â¥Áv•Šól”ͤ977›rrxQe!+dÖ­Ñ´ßýO4º¨†¿h®Ê  "‚¢í£i7´¬ô{yu ìù{–õWYbýyÙÂ8ë~„ 9Göí]Ï2ÁB°Ú‡Ê(Ç E„·ÞY¼¦ Z.aIíMª­o¦_þí-Êδk“›¾~Ëm¨»³²ÛÙ‡r-ϲ÷°Ã+ü'o\0SË‚¸rËúéã¯ðìþbºÿöË:+Þá,“³‰þý/¯käØjñ¥ŸÇ“£*÷£_üõ &‚v-~ëbŽ€_PX 13þÅ÷×Ðo¿÷yöíN7ðĨ·8öê+®ÓH,BHÁßyË÷¥ÿzúmÍŠ;¹îž~ ß|é,zòÕ¥›cËnã6Ö²•4‹î¾q¾FPQוs§Ò[o¦þø²Öîy“‚ïéÎd¾å²YšEúÿ±_+ˆ¶WÀþˆn:\üq0¢¸×Cýh¦µöá6-ù{@G•îyðáQÁS<4{I‚@!€‘¬‚˜)…wÿì˧Œ^ð­Û.‹ºßCGøy¹Ýçâ¤ÆÆ&ª¯o¤Úº:Þ7PCC#5ñ Û³:Ûâ!§G%xÄr‘nSsýáÀýŒœãÎ@~Í<ÃbP)×j¤l»‘_øp?°kTÔ¼Ü\&ªY”Ť þ³fþR‹I…<ñп3=»º–HýÿôÒGêö݇kÿ÷ÑÿÀx-LN5¼5ðæà­‡§Ì9$Å0NþÉB9wýèÁ7'––ÌüþWuÛ&>£3žªåÕÉüÌcý×ò(“#wÄ:!üSgAôaÕÑ‚•3œ„ñF~€¶O°Šêĵý=¿» i{·̨·óíö‹ Lä‹Eê®®îÂÙu&³——<†Õ5“­¶aBPå±ÿ9AGŽú‰ê}w ¢ ¥è:]§ß=»˜¶îÜSñÜ~ýÎu‚7|þá‹e½û…ÃC“p©P4‚ÇbI b!GýX·ôÍ{êØÑ—v(Ê7öð0Wû™´‘¨î·&ɪZHÍD3üÊçïø|bXÝʤÐnwP!“XY¬<b‡ÉHøõß>íÜþtàÒØ‹‡è_b˜- Œ«äÓßžÍïƒÊ¤Tm¸?`EnÂT¨ 8އþíÛèì<Ñú£ÿìØ[¥=¸ÿm–_HØô~Öñ¡v¦„\K:ªlÂ@#•³qíQ-Õs(¢p錠¢}‚Q¸õµ'™¡å»"¨È+lg)›}D;K±"¨¨»»ººóeïLfó¬ŒÎeîLö×ZZ|TÕ~ ïï’ð,©í«“ó#Ðy/q#R @`ÕÒˆÄ'o¼úÊ-÷ÝÛ ‹×äÿä®ë ݽÜ{+/ˆ"È“ÿÕBVöu´84K+H*&VáW?ˆjgud…ü€ÏÏó‡‡ÁõöIo /h áCXJ1‹CûðAÍàa>læ¥5T–XëZwwljÒV+î?>Õë­[ñöï°Œ0GéDU,¨Ý=´¸wÁp³FR!ê§GÜq!©)CZ‰X¹ÏÁïc¿Ê%Ã-L ;Z¤Ó $SVHj’='¦èÖ-OmíéÆ×ý^™5çϼ½R½û¦‹ÃWëD4,jÇLTq ’ša·kÕÉCQ.—‹I*[RÙo5”¤ê/G½êÐɬê—{üÒGÒÛ5°Õëb#P?ÚQÅ,~L’9õð÷[PQ&ÖI—#Vú÷$Ÿ®‚Þn_ë~sŒ'¿lY½â9ŒiNÆ:QÕû[OjÈý$E`Þ=µÑ/ÜŠC½›<•¤ªˆX½D`מ`ÌײҌ^–’l}…€Ô¾BZÚI°náÛ3–œ«>xwMNAþ³||'½Fê—¯›§DkQaÒÃ:Á²¨‡§i„ÕÅ¡XüVT¯fIåõU4KjgÖÔÐÈbvLhOÛØŸ –T´k*â–‚,‚˜š5ëª&?dŠgŠ¥þ½‘3ú ‚º¡â€r rÇ¿6|²TNlØÐ·ÐÇÄ’Ê ¤rš>„è0+ÔÌäGêyæ>oÃs‚ªRY7‘½s*BHê„q‘» t^»\!©Ñ"(å“}¸D“ZZ¿øÜkWÜú8›Ý~¼æ¬zû•s ±òQÒVdid¿Tœòð>fþcƒå–T¤ÎH*Çä¤Â‚ÎWaAH Ÿ8IÕ6>YõŸZïÇÞz°Ý_žèôoWe§§}­?|P1Ä ê¾Û^_úú+‹X0˜_°¡O¡oÁZ/$•AHå„U¦f›hÙŒQXS¿0EHj*?Óîd?QíæÉ®øèÂOÖ þ¨Ý• {BR¼4Ûg`o!|ë€T`ˆÖöÁ+Ï¿qî…ó«}s/ºë·Ï.ÎåeÕY“F+SJy…*4@$‚°a–ML”Ò‡øALurªïCÛâ‘ú@ÂħΒNÒ°×7´§O¤Ò¯uV6Þ×Ðv4ú÷F>´¤ë‰}¬õGT„™Z_¾_Å$)ŸÇݰaù²¶®^±‰›FBDsìѧзÐÇÐ×$¥8ðKÕI*üR¿0%ò÷AŠCÑïŵ¢Ž/<:@¿* IM øÒtŸ  “TÝ’ ro˦¯«Ü¸aïœ+¯¹ÂëõÌg2’ÄGͶ[Õ¼ÜLÅfæq¿¥ö„´ý¹ÞÌÄOä¯~¢_î°×‰š~£ý¹~=Yöíõm®œíõmn}z~,‹•ɨŸeT¼wãÊŠåk>XôIKKBÌ ÿ ÔöØB-©BRTO²_ªžÖTyÈÍ?2ê-Þ Áò‹tÔ°ôÀW>hŽ(Ñó‹×Ðïð…À59ˆ •»ññõ§ñ=„Òóɾo~û¶]iMèK0 ŸAøÂú…¡~Œáš›iéë/¿ÃÇKÆN9gTÉØñeö¬ì|«Ýžc2™ûÜ„2ybY´´yÇ®ú±ìû‡×&knihil8{hOå®=;¶æ–ñSR¬åˆ „} }JüQ„þ’Fð’˜Øó00|S?=ì¡ù#cŠª3¼0HpˆÉêêm{éü©¥e‘k1B¡§vï’Ô‰ãBB«Ä¨ ©&z„¤F¡Ôü„ZS1‹U7èÖ½gû–]¼íç{ø\ 6=Æ?Ýñ+åWz+/þùwOèDzï3ÐWÐ'ý‚ñMB **BÇ1ˆ«î*VT£¿¤«JÍ<Ëßï«øÞWŸ‘Tø¬ÏŸYF¯/ÛHÓÇ—tôÿã•´”—/­ã¥KÇ— ¡«/˜ÊÖךу~•nºt&-Z¹•Îòò¢3&”ðjSeôÌ»+y¹Ñš9q¤vßÈîAn‰‡U§6ï:¤¹&-àeL±Ê–pý`ͪª>CÃhmLYD¨«H-^µNñ2ª3&Œ¤Ï_5‡]| äaŸûgÞþT[6®GÓx«›xU-=.,ˆ7ÊAîƒ éÞ[.a¿}c—2Ä»/m)oÖ\±ÐÎÈ^øD|ãy$õ I5)“Šè¤–/x†’W\ÇOiXO–V>QEÒËøÏúæ?ˆ‘¤¾A@'™:AÕý˜Ñ/t+*,©° ‚ Š•Aè¯éêR “T¿•m O¢rñ2T^Ö·/ÒUçOf_è}ôîŠ-t3/ýÙ>ädÒ—ÌäeE352‹|ߺýr-[M]#/uº–®¿x:Õ0‰\¼j»FB¯™wŽF"_[²A[¶E—oÚ£ù]庋¨®©™ž}çS&Æ#¸Þlv/XM3ËF2!I{Tk×Ðꉗ4½Ž—`­e¼èÓ­4qÌ0:—É0V¨QTHÍÏØKO½ú1:€æNKûªªéo¯$öû§\oõÙzmyÖ%ë*:•!’¥MÛãÔÓùæíÁ×ëôÉûÿ÷T‡Ü?BRã±´<€€ø§íú'¹èç "øFIÅÔ%T|64—Þ÷Í·7Ô.mw.§ñED}Cøè+è ©øFImnÝÐgpù%õ3& 0ÒÈ<¬õQù/?ä¦ËFÇfIО 2³uñ†ù3èEöC7}<¿}Ú¾~¦²…©ŽIâ8¶¤¾½|s`2&®ÃzzáôqZ\æ×”ÓÄQÃhÁ¬2Ü¢Ù:ºëŽ{ÐÔ±Å4¸0[Û† È¥M•‡èÊó§hyáv‚yÙì‰Ú¹îGò:ëG*ß_E»×H*Î/›=I“å4äaƒò©|ßQ­Žu;ök«L}õ†‹Ú¨Ó“ ¨3©©ÉG{B†ú§OÆ«_R2" $5ŸŠÈ/trz Âò‚ªMªâ}g$µí·gŠs:çú¥z?ºUï:IÅP¾þD„縮û¡êeù’¤þ„À5¥VúŸ xìDïíé;’Šöæ3Ž>áaý—>X«ãšžÞ`W€‡´ˆ!f÷GQµèÈ3Œ‡ç‘ü‹|˜ø<Ê.“#—ø—hfL­ 熵üøw²D÷Þ| ýó½Uô‹¿¾A³&ަ/^s~`Õ¢yZü6°€;­×56Óóï­á:OR^v­oÖ¬¥¸y¦¾‘‡ø ÚT/‡âëN­Ò8ýóõû+]b¥Ü\¡Bq‚:êjåÉD ¡Tb€T€„„VXPa.Ñ ªî» rÚו›¤ø'©O@Ð7ôP¢ŠþbŠ=6Ü *ƒÐŸüRu’ºì‹žL²õÑ7&Œ§·_9›{æ=*Y†îÐñÓÚþw¾p•JËÖïd"»®ÍÛ©ýúöFCG_Kø¤bx~þŒ tã‚#†éöÕë™Àî§ç­¦ü•ôÙKüùNž†[¶?UŸ©§¡­¤uÉÚtäD =ò[4?TÈ®§,Ž­Wqâ¸~ªí{’¡M柴꟒ãÚ¥ºX"ÐG¹XŠ,u 1A$C'ª°Š„„’S &Ò'µ&&šJ%½A}Iï:QÕɪ~®ß÷ç–ÿý±…FS`¤}g¼ÔÂ?M–t|Uû*•ÌFѧ[ö&©Ã€Q>û¥ÂçtOFÒzo˜?£áº–}_'ŽJ¥#Ó &Ÿ˜È”›•I[÷¦ %Eì0‚ r¶iK.û"íÞtò¯gË)¬¨´ýŸ­o¢œ¬ -fñÎýG5‹*Ê#M`B‰S®-×Üv>ɾ©%šlg2ÄÓ'µ¡ÁK{øýñc`úäTt%eŸ4IMšG!‚$¼Úu«˜îƒ:«¯}ýÕ¯ïûRLÿØ[_¶˜¾mé${}!Õ7ýZú"”†š_ÍÖÔ?­Ã+‚赊¾%©h—¶1aÔê>ù/:baßÕsyˆ}µ~;¬ý-<)ëÙw?¥ÿ÷üš©ëûú- (+ÃF‹Vl¥¿¿¹œ—W6Òèaƒhþ¹ìÛšpþÇ> ‡ÓM˜ñYüHÏœ@ÿ÷ÖJúÎož£’¢tÙy“x‚æÇ¦=EpUxõ£õZˆ^Е ñ$©k67±¾šXT:ÊFÙÙ-Íþ»ò?’š OAdH4:!A %§ú¹v£ÿùâú¸Ñ4n®õ«KC@ïi ‡¨~S™•þ¼Î¡ýjùô°›Ž6øhX¶>¸[|03þ‰Ÿ¥M¥°˜þñÇw®ÁÏôû_ºŠœ.7Y-ðN"º(„@¶/ß~€…_¿1PÈè}·^ÊË5û¨ÅébÿÑàŠKÿqïgµUÅ …—B!}â,¯w^w¡&ƒ^ ÷aù}ä›7kä5ô:îéî Ÿ»üj„•† òûô&©¸"V+BR¥+‚€ Ý psÈÿ ^}êx£>Dz›Br+©X2ajîybEMª‡Ó0BR»Gn ‚€ Å9º`¸ßò[Üs[±ö‡¤TA |W ªA”Až0eÄ©ÄO~Kì-§ÔD?i_A éøò´àª—v:©É•>3¨žzm?U›ôϨ+—®®’uþÌ,2›…út…U²]ï<ÆD²I)ò‚€  Dà¢s`*Ô—™¨ÞuNÇpJ‰±±ÙAK×í¤ã5µtáôñ´¾üÇ2G»§“¼t)‚òãÚ}·^BÍNm¥ªêš:š{Î8Ï‹ ½¿z»¶’ï#=·hÍãû‡y©Ó­»ŽP‹ÃM#†j˨:]Z³}/í>t‚ÎåÕ§˜ᥰ°À%3Ëèƒ5;K«^ÌË®&:9êâ¦ü–oÈxñÜœD‹$퇀üœ,É*‚€ ¾|%Äšú ù'K8ª'þµŒŽTŸáeTGÓo¢­»hé4/ú&Ÿ¯á%IÏåeHMF#ýòoo‘ÃᢉcŠéo­ µ¼û«NÑñ곇»}OÕñÒ§#†×ç£Y“FiË´"ÃßßZN‡×h«L½÷é6ÚXyP+·mïúŸW–R~v)¬]Kô¿¥+ƒ ÷͘šAy¹©B+ÑX&¢}!©‰@]ÚA@H9no¡þ¯Í?ÉBöVžWyÚK««ü³Æúø 07‹æçжYÕ‡ú;cLñ :ÅDu_U5û˜ºØ×ô0MŸP¢eR˜KÖêØÂ×AD‘@FíV Õ75kçy9ÊDXÊtÁ¬2m›PR¤ÝK¦-­ ;µàB±¢&Óóé­,BR{‹”äA@H{r9„ÑMe¶Yï'êàæKgвõ;釿1@&;“¥h`ÝvåúË+Kèߟxp>gJ©–uΔ1ÔÀ°¾ûØ?é“•Ú$)Üà…¦Ø…`=ùêÇôßWË{ » 8zŠøãKôƒß¿@›wÒ®'Ó?XQ]­ІXhÂØ¶æd’Udé™Ýß56rGA@è€À]çXé¥òðPòúcnZ{ÔC³‡%îëK•NWÂVPí:tœgõŸÔd¾€gçc MóyÆ=6׫M¤Òï°>ôµ4ŸS‹¹íä"ø˜^uÁ42òìx¤QÃj~®˜åob‹*¬ªHÿ}ÿç´}¢ÿÁŠúñ§ 1.»H‚÷ÀH±±¤¦ØqA@‹À°lXSƒþ¯ úu&J2X¾.9¬©éóÚjºtEpFÿÐ!f:grFÛ r–rIM¹G& ‚€ $ _;×J–Vkêv¶¦~°O¬©‰x6µu^údMp Ô«ä%B i3ÆI1 R ‚@ú 0(Ó@_œ´¦þfU3¹½úÚMéƒC¢5}ûýZr»ý¸f¡©“ÄŠšèg‹ö…¤ÆE©CA m¸o–òl~ßÔªzýc[â—KM§‡q舓6lm ¨|ã5ùc9Hm„¤¦öóéA@Œ@¶U¡o\Ñè‰ :Ó,ÖÔ¾z,¯½{6ÐüPÇŒ ®¸!)‰€Ô”|l"´ ‚@2!p;ù)ðÈoâå8ÿ°Î¿Ö}2ÉØeÙ´­‰²%ÉľÁ×_%VÔþôœ·Ž[BQtº@྇áõy¯äÛ%ª¢åÁn×ç[Ùt0PÓ=Žü8pÒÉÛiU9Æ· Æ÷ÿòðO7v’M. ‚@ €¹S\`§{Þö;¿¿ýuï!r7SmC3¯#ïŸP­}N‹™ò²2¨07“&)¦E…} axM>^CåûRM]cÜôÏÏΤի«Öï:ÿ‚l*ÌZÞ“JîÜò4“ûùˆt)ˆÀÂ…·uý–B¾ï{|ž"þRQ-J“Ïn¨WLäìõèE¡áàÅÝ©ï!«¯Å—£ºÔL·óË{~þ‹ã>Uù}±¥øñ… ï’X8Ý'÷8 pþP]dßO®“{øÓé •ë‰<Šy©óeH#ÃHN²ùN“IuЛŸl¦l&¬—WF f–‘Ùœ¸¯uÇKK×WÐGëvR}c³£TQyR™ÊDÒ;ýÉÀVjåù¶ ‚õÛ­dW†Óü ΋N)“Ä$®7'1("š )÷<øðÇ=Gžæ7è°æ}êpÓVlÚ¥XG ßÐé4ÂëRmtÒ3žŽx¦ ©v—þú˜çðwÿí' ÿíoÿ½ð}έá&И‚@ה﫢gÞ]MJC5™ÓÛ$ªá½G1w](Ê;&ÕM…î“4ØYE¯/ÝHK×UÐמ¯YW6×¾KÐÿÙwVSmc|…döŒ$Å[@Š?ýUÅMªñ y'©Å¸—~ù·cô¥éßwH§WK½¶ê¤,¢­ Õá7Ì@IDAT>LP¿§ªÊ¢,Cõy£¹ög”áæ­Ä5üÊÂ(úÑÚC»ÙÊéÁ£é¯ýüßãjä3–’UˆÖ–ÓŸ^ZB'ZŒ´%ëÚž9‡NZŠãJP!'0ÚÙÆí¡Ýã-&úóËK òø|þ¥A#Ñ'Ü2ºþ ™Óy›JÏà¸TÈŒvÌΩZ»õ †„è.^’¿÷ÈXï±’œ‚@W(~‚J¿+6ï0\”ñ”q€éPWyãzí^”ù¤rð+ü·wÿì?ATaÅí[³J\µ”Êä@@UU&„;è_­§S¦¡´!ó"ª3 HˆphwcÖEš¯.Ù@²\^¯— c¼R¨þÏ@26Ï`+jb‚è£]S ·Ïrô•þñÂUê " $5ˆ… ‘  ðÐú•lAýM±¹\iI1ñT"Ú‡GQ ¿¾ó‡?»ŠåÁg]ˆj"Œ´Ý¯AÃ÷«K6Òió0*ÏœÉ>’ñðêé=lhr@ž7–m¦í{ŽhÕxÕPýÞAdrNâLb)Ú‡'Þú÷þ©HÎhHlŠFr)+$÷ܳÐn4)Ë1ž sl¯% „<9†jÕjµþõ²ËnÍb¸äóž}FDè`¶þ³ìƒÚlÌ¦ŠŒéI¥äi2dÓó‹×RK‹+.Cÿºþ_&eI¥?äQX®â¨R)Ü…‘/­~üpEµ¸#`ð(÷«ªaèTë;ÌUkAm¯-ä™j{Û¨’¡hØ´q÷ó}L””Ï|{ ä\ø{b{Ï`ße›’p j{ñaQÝmŸBõM-´tÃN¬ûXú¨†êopŽM¸µ½þ°¨Y®º8éß¾=9ò…?l¥æþ¬¦F£‘¾;ȼWM”jOC.Èg±Z¾ÅyPPüS{Mî Ý €an¯×GK6TÒYž½Ÿ(ÔnDÔnA.È÷Éæ=£Õ¥É‹a]ÿ¥ë+µYü‰òAíIÈ…(Ë7ÅVÿžÚ•û±E@Hjlñ”ÚÒ×¾ýÀ,¶Rá0SI5Ìßþhò)†ÁŸ½ó^kj{€ä\X=El¥;i.£dßg…|MÍ:Àòz<ž˜XSuýš[ÈÈ3ë“9A¾Æ–ØêŸÌúöGÙ„¤öǧ*:ÅÍŠj¶Z®ä•ã Æ»½¨ê‡|3+/÷r®H¬©Q¡)…ÓÝŠX¾¿J›†ˆ8¨Éœ4ùøÃ_qà¹Ü©¡ú# +â &sÒâ´ÆPÿdÖµ¿Ê&$µ¿>YÑ+žðkO6‰•¤â5ZE äT Æ® ‘µeÈ?ZP¥|Z"à'i^ª©mÔV’Šg þX ù°â–&uk$5ºT¡úc%©xê…þOñYc¦,d’:ÂC@HjxxInAàsc$ƒRdSêRâ39£RÄrë–Ô”`K’ u#öh-O˜ÂR§©«6³ñ!;tˆ4…ê¯-uiE}ZÎ3ýûTliLC@¾¨¤#á#€ÏÉ@J¶YqÁªšô rÈ€0T ©â—šôOLLF|>•ü¤ŽúÕQª­÷Ò¾QDƒ§³uÊÏ•A_¯tÛË“O·'.ú& Gã ²f «} ëcâÔø d’z!ÕY©•·eáãOêˬ´ 1Dý÷œ •ì6']ú¨Òl4Òf^“ RQ¨Ò (>ÕTY2²ôȤϕ»Âª\2 ‚@¯¨nòÙÎä!ïú`‘ÏN°’•ý3ÿUÑö£7' ®Uó°xHuLBór°¦ˆ?•µóB*}ð±ßGõ¥7ÏÐà £‘Ã-šOj.ç­­÷èÙeŸfIM³.ê&¦Úý”UPÚ£35 xÕT1 eöŸT£ÙÎ>©;e3rGÑáíÏÎ#<Èày p¨ ßÛ«ùvÑÁ;}‹~¥”sÛp‚­PH©T†J{Á°ŠKî:舰-)&Œ@³[Õç¨|#m<$_wðPÿ»{\T2 ÀJØÒˆt°®ÔêÓnÊÎ6’‡÷NŸFD32Œ4q¼vïuÐôÉvja ªx"ÂRUŸâ¤]”iƒ€Ô´yÔ¢h¢h8UNEc¯Óâ ê“§:“ qRK¦ÞE“üŠ F ÕžÜB»W=ªMžB~[VYìÔp*HZ;«§ý5§/ãDc3mȰÑp¾¶œÙ>Oë¹9ëžá5çÚ`#O3n®>D‹~­Tó…ÍÌž+8à[_ <ÌVyå«»¨K. ‚@;*O{©l@Кxá3fÒú£›Úå$š<ÐHØÕéñ[`;dH± 'N¹5ËiñP3í=ओ|þ‡šºéÚ*Ì7i³ü_}û ¹\~}G ³REˆjŠ©+âF‰€Ô(”â‚@o8¶û sÞ·iXÙ­Z¬S½\sÝ!Züøý”ýVOÐêW®×ÂNM6òº›÷p0zÆ7éä¾ÅÔPãŸíßæf7'_VõOª?ã,5¼Õ?ýÓ,ûÐÇ4Ÿê+ã`ZUÀ´ŒÇ‡°QÕïÖ®>¾>ˆ/]ÉÔõJ¬Eà#ø–5Ñ»¿R ä…­¯ŠJ•>v0šL•ËK‹ë@;å4íøó†ú¿ësè/tºÙ§…œ*ûóÙ¸˜yüçØè7«Ú¾:dL¡ --<›u]~q.“TÿoÛÅKê›Å ZÉ)T*e¥AMôä³!~)¤«ˆ=BR£ÇPjz…€êóPåʇµÕ¥ªv¾Äez°Œ0 lOP-…4 äbÚðæ½j³»L_}´q^>jÝYý±Ôjôš¦ú|e>WR˘±‚À–°Ä]lÌà ær ¹šVÌf½n¿ëÀ»¿Vv‚¸Âúª”J£j¨Ì1 ®˜ûýªàÞ@ër ô¶ðÒ;{œtK™…žØØµÍÕŽjïYæПPyYýà¾!4¬+L#„Tè;oN½·¤–šú‰?nz†}¥‹Ô¾BZÚêýj[¤`¸škhù3FZ¼Wå®ùö^'g\׺Ê0U>úÖ w‚@^©L·¾21õ¯4È800¹Ì÷'k¤ÜÇä•í¯gÝG‰ý^Oq®Mð}eNË“· ‹µò²ï6 ”–A Ÿ"ðïËz¶Ž¾µÛEØú[Bø©_þ¾çH'ÿ÷^’Ò!©éüôE÷H€±7¶¦RR4Óm«ìÚqXÒóP>ÊV´nmÊ.þ]Nênž®|L\iû±j$–31íÜu€çDðý+Ù÷•ÝP,ël&¶¼¶p-„¨Ü¬¯&Ž:0ldé!qR’…ø7U§Ý9Q"õº]]öhä×ëèu£I”Q—=ý“H´EHjÚÓÖó5Ùúê¦@Ô F»’yo…Â$·ä¬œyï±öº…Ö(Ç‚@Ô„’3¯åi¤Ô˜qnR=d6¶±ªKo -ýÉú“â‰þ½ÅIòÅ!©±ÅSjK#|noµÃ– •ô$Õ¡æ¨n—#¸Ö`=§®\ÐüûY&0qž ØçÖWLÜRxâ–J]-Ô¨“øþ$²ñçe¿Ý“µÇý®J0æ+\lªµò’7è#U¥™4B Óf!›Ï×3ÙÕ¶úd³Xc*&ôDŽɔHŠ“õ·¥„¨"dG„¤vÄD®½A@u;Ç\Y…ŠKµ‘EézòCo*‹gÈçR3WËY6jÍ@Ï6{S÷•?t!<¶7Bó¯øŸÜü†¦¦é*Gye¯ ¿õU¥"Î×Ö$ÔZÈZ]Áª]Áîšë@3µº¨´UEÔÄ|eë«£(æá‡Z thÓr,ôˆ,Š9V2©ÞÜäQ’wÉNÈg"'eZ³47…PkhŠv‘A×_5¸Ø#ÇÍÏäÕòùëŸ3ý»€E.Ç !©qVªí×hDïdÕá•9……ß9éOÃÍ[“VaÈÇ+Uû÷®g!5Ù[÷I)ó…ߨC,ž¥­[@F¸(®CS¯o‚OñG`B ëëHÖª;×9ÌZçøçÿþrõìóqÌWdŽóÊ–ý^ý1_ 8æëÉ1 ‘ƒ>B [ÉZ½ó0ºOÒIKqµ~3Ÿø¡LRñ×*ø5ùKèåuýUãR<ƒ#­.îå R¬ô»ÀÒ@„¤v€D.Ý" y¿óÚö1“'×ñL+`’š´Cþ,G¿rÕnX¾“žØ4 C·š&ÙÍVË'ˆ6¶6é½_YÇ+\Ê@\Ù÷u?ç´É<1ð½‰|Ê;°ß«ÿ8þ‚u` £k$ÖÀ!³8€cå•ßm:Á_ÒÀNR" 4æyTT˜MV‹™»«’š¤B>øæf‘ýR!»®G¸P/§ëocý]¾“ìšš¼$Õk:I–é.^’?6I ŽRKz!6²ç­9Yý‚ahé·N›Kh€éPÒ¡pÚSBÕîRåø‘}o²p˜é ÉÍ{èÐoÒÕ8w±2Ø g 鮊ÏÇÄU‹:Ð}€†r¦®]ˆ.gëëå¨È È\-´è7†Ž<° .|¹ÂÀÖW¯ÁT!®@)=’Á``²Ç“¦x›<¢œ{OP®ç4Õ™$+Ÿ-©£‹rÈ„‰^­rC‡HS¨þ“Xÿ¬¿ÏPK_^¤UÆ­äòjhÔÀØé7a¥â.’Ú%4rCèÝ ²çYþæk/ÜxϽŸßæ¸.ÿ¢Ì' &öJ–äa_1–Ëçó8ëV¼ýü>pQ'ªºÉ"n\äèÊu üåI–ƒvO5˜¼jÖW¶¼j$–Fuç:ÀBÎf»ëlëeë+ùü®¼âr[›* Lb½d¨ÈÈÊ«¼ä›§‘WRê# [AöL&M]Då‡OÓ¸–í´1ë"ö} .sšhm Îm,Ëe1h\Q.X4™!»®G¸2êåBõßyè49}»IqÌdg‚ÈÉo¸²ô”ã"Ën²²õ8Vú÷Ô¦ÜBRルÔÚ¿€dÏuæLuÃî­[=áÜYnqܤδ¿ÄjÉ‘ O½om_·ü/ µ KNÞt¢Ú¯,©á"Þou—ÃÖ&½û[ë8ƒ‡]x¥-X_Ù@·¾æ¶É<Á·s6&¼lwÿ÷QsãiÄ|=Í'[`}åŽÁ1_ •&Ÿ¥òò7ç/}d””B°õœLLô,<ÔÁ“§Î+DËw£²æÍTž93i4)kÞD™ÞzšXœMv›•¬V‹&3d‡‘¦öúÏë×ßk­ ““ƒn$I‚<ª¡‰ÊŠb«’¨—VbIM«Ç-ÊÆ}¸„ϱ⽷Vfçç?M£&ÝMt›zŽí5%‘UXPAP«Ü“”C»+^X·ìõ,g då 2à œÖ$•õï2]ûçn¾‰í­ÐLphjh:ǧÀúJìóª–±ùÇÃ8_çf$•0|^Þ8ù¼óµÕuàWÊvžÊÂÖWŽûª‘XS…j~P¢¨Ø%SÖäp»äì¬U}¸$ÕfµÒÈ¢:]×D;¥I<Ý®"czB-ª° ‚ p£‘ù6 ‡lv›&+d†4Ãýõ¯&â(WF'>b÷D»® TT¯±:.úë-;\nÕãñà*)ÎI3ÀR}¿D_x zÛÇ‹ªiÑóÿxáªÛï0јI_®÷ P§Zß1$ÂG>¨â‡õ@eù˽ú"†ùaEÅŒuÈ ™!»Xñ„pR«ëÀ2.ƒ-à:Pux÷/G€Õ•còL`tÙË®DŒ¡þhç1y=I.'>Â}>¶¾î†ÕÕo}U*T£¡2Û›[9ïÓ ¡UÈqYñŒkëšp5IÅ7ˆžÙl&;“¿ÌÌ š2r1a!:y”f4ÖÓnûÔ„ø¨ÂulóvÊôÕÓð\#•É¡,–2BVȬ÷÷ˆ\ºÓ÷ÉjRíMLTÇ%ÄG>¨â‡5^úë°ÔÖ7©.§³–ϵ>²×³È>FIRMZ!€ˆ¬’°N‚Z¿øìK³.¹üø”Y³ï[éý·¼Aæ½êpÓVe°iW\ã¨"*ÂLa?&IyÝŽ†-«–üߦ• bù°…ZR¡ƒ¤ Ðê:°‘«ÂÖ&}ðkëX¯êa·?q‰Õ\Têj¦‰¡•àÂÝ€ëâÍë£:ªÑ\˜eq ŽõÊ$Qxà¹B\Ú@®Ÿú7â7´8•f‡“2xè;šä'i²0á³q]Y™™¼”¯‹&• $›I¡ŠtNã§tÖ<˜Nš‹©†÷ñŒ£Š8¨3…Yü˜$eâ¡ü‘ù*f jvvefej2BVÈìŸá9WïIÿÊãMä²mf’ZHFžõ¯x âGqPf ³ø1IÊ̪•äÅOôô#ô'GsÓ©NúR ßurO.E€€Ô@“"‚#€árø¥ÂÏVJD´6®_öáÊëÖVνúÚë¼£Ç\^m.Íå÷¦jQšT›R§˜WäßÜ@hÂ’¬5WE ~~3*—³áð¾KW/~÷ƒææFüÊÇ’8ØêyƒŒ2ËP?ƒÐéŠ;÷p;ØÚ¸,û}^žÃÓxŽ£ð½2vOÕ\ø¸˜·ÎÇKÙu€Ÿó¥L^/å<Øä­…ÞûµÁÁ1_·s¨`\Ɇ8櫱ÂR8ôà%wÄ“´NÇ\…xÆÛ÷¥Ù“GG†Ë1qÊÊ“‘`¥t»ÝÄ+§Ñ(ŸòlfÚªžŒÕi„íÖÍãàNƒ¼Jì¾n¼Ô)V’2ã#Ín¦y1áâ< e31ÍÍͦœÞ@TYFÈ ™£ê×ëþ'ΛI#’¢òêT¼)jìôW~q~•7$#ë?Øý­q×ýˆ}ϕû+·iË¿¸"»^W1¥rA éÀ/fÝš R©½mjª÷}ø¯^æów'œ3c܈±ã'Û3²Xì¶\“‰g[ð ŽïE<^ÃÙRSßÒt¨æðî];wmÛ´Ÿ+)!…dÇêÇ]†ú„D§K¾W‹çòqë®Gîžìe¿Wî'L”c‚êwÙ`²ÈÖb1Òð¦:Ûè ³-r²ë¢[m!æ°1K˜sgfË­… ε)Ûnd¿S¸Ø5 *j^n®vŒ¡~ÌîÖŠª ¾þÖß{ý™™'BîGªÏë­ß±aÍÆ„?qM‡Hö1D@Hj Á”ªÒPkªþ凗–N^•[6nå­’¯i–VÞƒÐê–2½ _ +¡ $´ íÁ×–RQ}xDU¬¨ B*¤V×M,+¶6iñﬥ>¯§Lñ±Õµ5ê“#Øü6[Oøú|cá+«ýyÝÜ]Ügu×m ®~ë«¡Òl4W,ø^ó1&!zÿê¬ÚTº=´­úè‘Wv(Ê×÷>AcG ‰Z¿5ÑHV¶ª™~¸Ø]Uóùݺ•'UÙí*t:ÙÒêa ¶z3Kõ!l„ÒYm˜’€ "a–=d0àËV]­=›ß•-©° jÃý+jt¦Ú‹™®ú£ÿìØ[¥Ú³ë#ÆDÿúY{œäd@{ÚD.Þƒ¨6·n÷!›¤Eàªï;÷²èØÞU®-îÆiŠ?ê@3 VË®ªÖßB³ûý® ˜º.@¯Å‚ R­®;T…£¨Tɤ¨‚ÁWšò†H1×Pâàýä×^½õ÷î…Åkò~r×u‹9ú˜¦ j ˆH þsT YÙÔÑâ G+IÅÄ*žX§ÕH ªÿÁÛ‚eCø‘0‹Cûð“ÍȰk†ùq²Å:¥›þ.·—¸ÿpÜiwê÷Þ^ÊxâÝ«o¡ý-ÖP§u}BRÓúñ‹ò1@@'‹ ¡Ç † ‰ ¨˜­ÁŽY’ÔpÉ*Ú@ÒÛÒI*†òub ¢ Šs\‰AÕËò¡¤þ‚@«ëÀ'¬¶@‚ëÀ¡#»'©>Ÿf}åŸE˜ŒUÆnò2C¸ƒÀu`&Ý™è, T.—Üì:À~¯»ùˆ«F^1qËkÍ®Hb×ôyDÔÕÕ4ïX¿æqeö=óöJõî›.÷s‚’ÿ0HLM~’ÊDÕoÕ´P†Ý®TLªr¹\<ûŸ-©ìkªNRÃ5¦¶Q„ØÀQ°’†òAŒAT1´IR §þþ~ *duJ7ýÑoŽª¥ Ë—½À>ÿx·â½ŠM'ªbˆu'ãú„¤ÆT©2íÀ÷9^TØë_Œ ­x‘Á‚JPußU|kDúÍ¡ñ.¯·JTÑ.ˆ)öØpO^ž Bº¥V×ͬ7¶6iѯ­cTb×jõyeë+óLÞ*h“±õ„¯£¯ŽÇÆ$ë8Àï•ZÎÒ¢_)5ì2°Ù“f}Å‚ŠÅ\qõýÍG™È &"韑ÀgcÍG‹×å¼ÀÂ|‘^#õË×Íc1£³¨‚¨éa`YÔÃS4b˜ßårká©à· KªætÁ`êD5\`О¶ñã€%íÁšŠ¸¥ È ¦fͺêŸÉ j¨Ìé ?,¨ ¨*({vlygëê;ý=«ô÷¢ú{ècéWÇQ“T~"ðy+êW¨ˆ2‚@d€ â%…=~aã†Ï¾ õ _öúØ[4$•«ÑÚA{xAê[û_öòÒR’Ú pÍûø¶wBoÀuÀåmœÊ¾“LX.ËOb9O—®ÜÁ ¹×_ÂÌë­³a"QKˆë&mqÔ&4ì}Pi/º¿\B?‡øÁèüàåçß¾ìæÛð£ñæã5gÕÛ¯œcˆ•*›¶"H#û¥jCül9õTÿä*XR‘¢!©(òÉÍi$DÕÈç «Ú1“WÿýH_/h!¼¤·×ßô‡*†øaAݳ}ó¢ßzm9#‚ªõ§Ö½NTý7<肹U%›OŽ/Ȉš¤rÀéc\Ï S4tÒ¨Aâ…RŠ ßúƇÚ1öá&xb¯ohOßôkáÖ+ùÓV×|c $¸=¸{"Xs¤¸¬Ö·F³a°{×b×î‘°"¢‹¶ÀuàWÊîý•~ë«R‰ ÌYY—ß{)b•ðyÀ7Ý_»å£W_ZtÎÜã§|ÎÿâoŸ]œ;¹´X5i´2¥tXTqTu«"†áaÙÄD)}ˆÄT'§ú>\EÑöú†öô‰TúµpëE~´ ’œêú#*ÂLa?&Iy=îÆõôÚöµ«*§6}‰Ïѯp ý ý-šc_‡e𣩰?”ú§Ö=?ägíac¦iÀ_~úÓ³ýÑAˆ¡äÕGûÙÃ7¾ž„”êHȾÏ€ë€AñLЬ¯­1_™"ª@a¸Âð‡¢†?Ûùã¡-ëS &Å\yÅ÷`×…á’|ÆànƒÉ‹°RA}˳Û3óæ\qÍüQÊ.4šÌÙL²Ôl»UÍËÍTlf„…MjOHÛŸ‡Û È`hjz/ŽÛëÛþ<\ÛëÛþ<ÜúôüX:+“!P?ËrÚ°¿¢|ÕšÞ[íp4ë–SLHEø8Õ·>Æ<$BßË|Ú»tߣæ{›xÉ0Uyè©_>ø_½+•¹¢¶¤rŒ÷=>Ï/};k²çÒ6ÑR!’aC&R×wCåýðÉü\o]ÃT¶–±7æ„@ÌW…†óWy§Î ±Ui>×χõ*žÏQþÓÁ1_ËÙ~Èñ^‰WÜR*Œu ‡íŸûý*„Îjµ¤‚`&1¶´4–½ùÊGËޤ奓¦ /7~\FVv¾5##Ûd4!Ÿ¤4B€ãN;ÍÍ Í µwWîÝ·s†ÞC­ñ˜‹>RŠ=¶PKªÖcùZØ ü‰û;‡16¾vá~^ íO²•ýÚƒá—ÇŽ§yðê«b‚€ i€À†'g˜O7n›ˆ¨°¾²ÊÚŠ[¼Ã_Ôºt Áe÷‚¸ÂúÊ¡³´·l¦¬ŠV×|ÇÁƒ(¨;7ĕŖË[o°´ÂO5Ôo\ÿnÔ÷|[R?G@'™ØcÃýG~”Â#ÆØ`QE jUÌÐËóaxéžyÌMþë# ¯dÿÏ“à×~þÈùùü†]¹/~ò—¶ñeêÿІ‚€ ±@à£ßÛFó2£|H(È(,¨ © §X(E·¤"O¸®(\ÄŸîýù#ñHÃ'ÜOô×_>ø˜~]ö~bò!\¸ðï¶cžª]ìÆQ«˜žÿÔÂ{a—$‚€ D\< S˜´¶®¸Å“·ØËèÊu «F™¼:]ÚÛØDU5õÊу'éÔÎtj}¥R{¦†3Í¢ªUœÇä{IJZt+¨NPÑ@>ዪ[QAJCWñyE­Óð>ìtÏÂ'3T÷©Õì7ÔT<~á»P§¤böá»çÁ‡¯`’ºˆ+|íÉG~~;3ë=¤99A@bƒ€î:àóú8Ϋu@³¾ò÷ÇÕ†ò{Ý\ZœÿŸ½ó€£¸þøÛÝ»S³Ü{ïÝ¦ØØØ¦˜j0BM lª)6Õv¢`™âJ›„JÃ?LÇW°»äÞåÞÔOw»ûÿ½=íé$ëä“t’öNoô9m›ùÎìîÛ7oÞÐþœ|Ê:’C{÷QömË¢¬•›”}Û÷é¬I“ßXfa!•…ÎP-* ©\ÿ,¤²Ž,¼ÚÔ*É:<8k줩ïâäkÑvGÏMŸü9Ò”P†@Ô„TNwÌäô{MÜ ûÔ÷ÈÝâѨ–¡-›B@!P+>›žØEW|–¿WÅ€·Ûç+¦„­làÞ4;·v@€Ý³Ý{Ñ®u[•ÝK2”C^¯¿JBJeó ñkŒ€]¼ RÙµ £¬ÝdA•—¼ÍûYˆå¸ö¹X<°Ó¸½;ÔU¹oî”IOG~výŠU!•ÑUš/ƒuð¡:NlTëWƒ’Ò ! œLàLX`äñ„F¸3e lM¡ðßGe=Þð¤R[ |´=·@Ùvä8íØº_Ùþõê®mû|,ÐHˆ ,lÚ?Ö¤ÚÚTHYcj/íî}P«6¨ôN4AÔŠ1F]HåËq×?¯`ØeGtûŠß[j²ú±øQ­¸2ä¨B@Ô ~÷±Ý©=¢ß5¨gjÂ/.*èÓ¼‘Ù+9Áìáv™=á¿;„×.ˆWi¯оîÄŒš›½>e ̶<®lY¼NÛòŸï‹xàg°5¢¶vÔTmaÕÞ¶W*÷ì5àfÊüºù/ƒiÉ.$p«tñŸc©|YLµ×·÷.8ú¿ß'íQ)À¦@—K.ʶ„€B@Ô)˜¨á,~,رly/.jè:šÐD;ÔÐu(¹v¿£I ´ãÉnÅË*üäñåø›äçêM ²õfù¹þùÇôV¹FSoñ¬©•JO"WŸ<Zç›å¯LeBÓ‰#¼¬š¨„VÔ=Êm!µB“âËìôL;w»çeTdõV5ò‘¥Œuûä'é†> ;:AHm‹‹²Û B@! E€…V;@|­ðé¦B­©koJCí@rªz‚ëáäõxr¢’—VŸk_Ã^B†1òŒ&ùùz“ü£y~®Ñ"ÿ¸ÑÂlËÂ"ÓmÉÎv\YFŸÆÒ+¾ªBiÙ\!Áœâ©ãwòÄG/MydEÙ8²]1 oÀŠO•£B@! „@Yìuà@ÞÚÞª_‡«,³„kÆ-ˆ­Ý¡˜­”’Š=ôÓv\SÅ*™PõaÖ-%Óœ”yñ]9GÊ^[¶…@<!5žjSÊ"„€Ž&ðÕŒÄNEä‡×7DP̸ï&µ¬BÆ¡ ™)g" SU25Óyi§«v)7¾Çv”qzO_;ÒCîkèÍB¼„8$ BjVªI! b‹Àüg›6Ô|Ùýuþ^á.Kð ­+ϸÕšØJy€Àê…Ðki\aW™ óZ±ZF‹†Í· ›7“íôš¹v"„˜”Ìñ ­í^Ó×üÞ­*_úU—¦yŒÂõwöÛz\Öc‡€©±SW’S! „€¨gØtàHöÚ^º¢[Z×€àj͸գJ¦D;ðâÏ`í«¡(*´¯‰””yÁ„œÃ±†¶ÏÌ5£aB|ׯ GÛyï={Í0SWÞmª6îuÌ8ö"üóvÐ㽕 f¹Ïì5''øvÅX8ÒÑâædÞÕ'&M'îš4¥‹OQFÃ9˜J¸¹¢ø›a½1LC4´Ý†¢r}ïF}¯5݉_ÍM›Sõ\©¯3»ÈR! „€¨yƒÇ®`ߜ능.øÅ3Iý>_ø|í AZWØ¿´¯­JE,ÞÀ1VLuà†Ÿ9š½Ýòápçã§”c8¸ží^ ˜`L{¦ª¸3œl:`x<«¯ïôвB@¦(ꟗÜß¡ ÷Œ5˜:W9¼f׆kç};žéWÞIíI­­mÝ÷ÓP¾Çú«öq§/ÓÒæy²ôM·ÁÅ8¯Iݸ“Ô\’zTƒÇ Å¥xQÑ »˜G ᵟ#þcÌÄôU}bΔG—8½œœ?n°„€B@8!À¦Š7»ÜgY‚+L0€‹oYj¥”Sl:,!.d@ÊdÍ+„œ ÅhºeÔê|ºØ^3ÖH0“¬y ûÁ^3×ÿŒLã±›Æ÷?3MQ tûï†Àú'qÉ|`Àp®ÞÓfmlç5Š>Ïœ0 oC] o¹÷eÞ7ð$¡­ÿ³k»ùŠèqŒ]Ó3' ¼™ã×u¸;í©ö^_ÑG¨ÏÓ›»víÝ«ÔÚ6JQ†ÍT:ìïLûý}ho€^d¦¨ªJ3çL™ô@èI·¥§·#¯ÚõÌ9…¨ûFŠºnîßW›ë"¤Ö&m¹–B@:"À¦‡óÖöÔáu@-ö:`i_‰ªn:ÁÕ„öXKûj$&fŽ—{¨¶ŠØ{ÆÚO!ƒÍоÿWkv®[‹Nî{3ïðy¿Ö·Ö ô/!\ö‡ º‚è­,ˆrW?´­·CH½ŽóAö¸'…º¬½cà±Ð<÷žµöRøÎM‡€ºûSÎØÐã¼~ù³›†ÞÝÝÇqÙc5±Íþç³ü»ÐL_ßAÉÿ§µqeTú2:lÖŒ¢m¾!~¬¾4Lý*ù¯1LµýÉ âÓ†Œ­ø0™‡Ay¯¿”þ>Xj/Tꋪö²%WB@! ¢I Øt`=Òä_©ðṲ̀ºîƒ§ƒ= LØ„À,î/›ç:(1ÀÆåì'Ë2ú,È ˜(´]ea·¥}u¹Üµ»jg´½`Ù*ÅP¬Û¹®´n»Y@å¬ú á5K ‚=KñÓxì¾ÞôC£ ›\ŽÃ‚¬¿@÷–Pù˜Ö^ý¦/õ¶n×Ú4xößÍûìP¬·Í[Ø`ÛŒu)}g¬¿{Ä~ŸØÇ«³;iZS)îƒA¹f™Hj eØ™åÛ ‰QpVò{ÔÊU5YQÃ,¯“þG˜<ÂC¹mW]¡×x? ²ÈF&¯Wz늌ÓûùŒfTè?·õ þ7ñÎŒû~ “uû枉ÍÅ0-xEJE_ú E3NiöèÂÏšeÆmIjŽÞŽTéàYEIJNYA”Í3Ëî« Ûêà^EøY3ÀV3p4AÉ£Þ _SgÏÚʂ땃þ®ÿ¸uÒÔ3_IŸ8.’ó«G„Ôª’“ó„€B@ÔS\ðEßPü+Eaþ³IíÍ"˜Àë`Æ-öûŠ ˆÚ”ŠX¼’°ÎPvFÜË,á•mØt`šrûq€öQáÿÕ•qyçŸí`Ó›îéµù™ëæxsɰä71kTØ1[áSwà‘5Wò4«y¯LÜ ÚÙ›Ôh•ô„€B@!àS5¢ës‘8FЗ„{Æ/=GWt ¯÷LX:PÕͳg/(‰~…Gý±øŒh›躎QþÁ]Ú<­j¯ŠLp¬ÌúYÖ©XñëùsÓüiÊî]Eï^ỎTž#!öSËò÷Ó11BÔ>B©ˆJCÖ…€B@ÇxfÚÐ-ãî_ÜâÁ¦N›vŽ%éÍ™³Ü½6³ðŸ¤)·²]ä¸ñ‹ªò2 1Á.Ƚi?5¦Üü>OϱÄÞwªe±×vAUÚ v|þTr[¿Z/<ÀÛ„Xìî™´]¸t¡•íàRŠ:ðq ô -¦öoòžG…f ÆŽ©/ÕDÆEH­ ª’¦B@!u<0çîû®Í7µHÜy¿n£÷vØ„f²ÕcüÐòÜDÑo ÈþÕdgÃÿê-8gIZÚúÙÙ¹mgÍZåþöKÊÏBZü+¥AdÓüÜã½5‚×KpåI 0 ¦øy•l,ˆ †ÔHÝo­×î?8ÌR ÷4yJ¯ê…=¾´±èBö½oÏylâ'ÕK­ü³EH-Ÿ‹ìB@! œH@¡UªnžŽ¬-º;miC3ÛÿˆÛí¹˜³ªcPô”ËY Ë×éOØ5›÷ªÑGåANG³!ön¬^ÎÛÑ Å¦Ë‘&ÿ‚M XÒ-Aõ-ê›GôNZumRÛöº†º¹¾{s$, \K¡žÃ¢®ƒî eï_GÇö±CI€¥¸éS2t?-zûRò¤´ ó³–Ì»ŠrVN^guyÁ èí7÷¸¸˜e„¸pP#d$Q! „€BÀq0ƒü“ZB*™Ùúƒd>žýÔõœQv=ùt§æöÌV eܼy¦fÀP0!€Y’´--ÈÞM«>½ áIg*ª‹\4‹2¥ãxpöV+^aî>ò§†Íûžt^$;Ú¸2è‚/ÀåBžÇðù?˜7o^@sÉÉÄ!5HE! „€pçÆõ`Ië$´k›6l2­T®%(Œ>3óì/ 5M‚ô2¸*°]RÁ@·5‹T©3­ ŽÃÝù­ßë{í˜~u m=>vì`ߌ#BxzÆòq—I¯7o4¼ÿs³Ï9Çívßaô7û¼pËDwã_"ÿ™K ~gdzG’Ÿ*gAÒ¨+åß.ùRû» ¾› s³hߦÿÀlõd÷øœNrãΥΩÌF’’C?ÖLR»¹nóe•9÷TqEH=!9.„€B@8‹€JÓ0 ç„´´~ìãÔ ìf ƒ’¼¶0Ê;!’ÍB÷ý ØNZšÓñã—7‡œ¦° 8ëäÿGrrºc&©­öîʇ°;RØb{ÌIýv“y×;š–¦ðÈ%šõäY`$Ú"-m¹uŒ÷•žK—íq7»Ô·3½Ò'9éÛ*ÑýŸ”Ú–¼3휖MQ–{ÈXaÇ™1ã´<˜ ,‚û©ï°hX¾_ù;QýsæÌÁ‡ïš°¬Ë‘‹¿6åX³†Ã/­ÈÖÕN—'r}†Mkÿ³SÞ¨´€Êçgmút>µësoCþ‰ôésOräoGؼø)Ë‘¿½ÝuÐt`ë§”s$¨8¶Ujy^ SS5U]P©OY„ÔS’ÃB@! „@ý!ðìÌáÏÌœ9bgh‰›5L{×OàŒ ›Óf©Ãf ª¢ûVÂ/+œ˜'Žf/:ÿÞG—· =¯¼uîæ7Lc\Ïr¥™Vê2åE/wŸiø)sájßMpaA[…àIn†®ÿóiã¢Ç«pvéSvùÎà\ä´V{DUH­ZÉJçM¶„€B@!P¯@HUdÿ0À$ÌL@}ÐÝ݆™ÿ†;ªÇÂÁ¸mòÔ[tÃøÇÅ ž¥êápÑbfŽÑœ¾Êg ôøÜôÉ“¢™q±I&MIK! „€¨ŠÝN­Faùq0Mãt—âÕ! FÕñ}ĈbD,µªðç°v0óÝÉ®ÙQLÚJJºû£MTÒB@! „@xù&& 8vެ÷^BGüU˜@Üÿ£‰vÎEH6QIO! „€a(Š ¿¨ªzÜh&FlìÎò÷¥-E#8³ó_NŸüJMäZ„Ôš *i ! „€B  zÞãAF½çÃ%kl†CzWZ‘ŽnþµMz~YS¥ˆy{ˆš#é ! „€B Úñ…oðyy1àhlS©©¶;Ú—¨Ñô¶ù†ÐÊü SÁ,^IÚ¥Ï>òHÔ»ùíˆj“¥B@! jÀŠï¿^AµßA¾ð  4wm¯…«VïÇôö´¢ðc{ÑCù?It5ýÒŸ¨1•s+£û«Wgr¶B@! *O c‹_)»ËôŽsDïl HøXm¨¨t:ìj¿¿7uw/†¸‹y¯ª0­)mòž‡a]…”¬#ÝôP¶Ñ’ú{Çô¶ef“ªŽeÊÄW«q™ˆO‹Ñe—V" ! „€BÀAÆNšòGøVñ²q{Ï:êìú1"ÍjžÑ„ Õ¤m¾á÷½Ÿ™øuôüT­’íñ ¤å×—M¶§´ðë &ücÆä•PSÛ"¤ÖYIW! „€¸ý‰'šèùúØ[ôVŒ¦jâQ ýÍ´í®ÆZ%©ÙäQò¡Õt‘Ÿ)[oIGõŽÐl¶Ã$Xd`¾«WI1†yÌüÞ#¼¤qüª„ÃzgZ–÷+]W›à;”s5T££ˆŒ ÐýÞ¥l²ì8v¬«Rÿ«$šo¾Ã÷&œîÆŒV͵mZ ×6Ø–'Mñ‘F>2M… ÌÆ”g4%¶e=àï©ûÌD Âi6,žGwþãµÙ_¶ e·EH-KD¶…€B@!ƒLH¡·M~âr“ÌÑdú¯4IíT~1Ø™û¡‘] ’önkW÷ÏÓÒn,*?nÝí!µîØË•…€B@!PcnKOow¶úÏé.*¼ÉO:¡·yi£~Å,›¦;çŽ뫱 G)aR£R’B@! „€ÓÌŸ¦> ëTÎ̦~ÐxÈiy —™5Ù/„€B@'[Ö,»°Wmk¯ÇÂR„ÔX¨%É£B@! ªB@Uö•œf¶.Ywþš©Î¯#É¡B@! ªD]ü  "¤QÈŠB@! „@piR댾\X! „€B \µ}0ë*N5[°`¤«ÜˆÜ)Ýý¬É’B@! ¢A@¹ñ=éâ´LLaåûᇖÑH·6Ò!µ6(Ë5„€B@!Pw‚]þ†â‹»TRë®ÁÈ•…€B@!Pã…Ø1É!Õ†!K! „€B@º$P2ÂßP¨U]æ¤2×MjehI\! „€B@Ä3D“ªš¦©1V’]! „€B@Ä%ŤƒvÁ0Ì_NÙ0d)„€B@!P—”  7T"¤ÖeUȵ…€B@! T*RሪE¬p›ÔX©)ɧB@! ª@@QËO*ŸjŠ&µ å! „€B@èP\ÒÝ}ª’¢B@! „@uèîvA!éHwu`ʹB@! „€Ñ!0zÜ/úgsj˜Õýý‹šD'åšMElRk–¯¤.„€B@:'áô°‰¼ÂÂföº“—"¤:¹v$oB@! „€ˆhR–$c4-Ywîš«6²vûä'é†> ×êd*f[øèJ­ëÊ5„€B@!P0Š>G1•,\{§¦jŸ½4å‘u‘à5M:b¯†šÔRÓÒþ‘¸×·÷.RÌ{ü†¿=$xƒLåq…åÚ d)„€B@8$Ðe9¨ä ©·Nš²rÐ3íÜížOKû}a­—WQްA*Ì@U…Ô1“¦\šåßó 8tTHùþ¹Q“Õ_zä‘cù'„€B@z@àö'žhbäW˜¦ù+“ÌétëÜôÉŸ×rñK4©¦B*zÞ£ÆLN¿×4h†¢˜ë æ7gê¤ï¢{IM! „€±G`ìÄôó ¢çó~ŠJæN™ôtm•âã§Ô4Œíÿ _OQ”ÇF?hXëµuýª\'ª§ª9’ï¿ÉÕr¸¨U©9G! „€ˆG,)îÃXN2 s6ËMµUN˜]5©¸~ýÒ¤r¿i*óüœô‰¿€”0|¨-úr! „€B@Ätý+c'M}‚Òµ—F×F×ÿÇÓµ›É0Þb<0Å|gôCÆMNGM*’BA_á.~r·¸ET§W»äO! „€¨+–œy ×_ß+ÅrTfGåSÁ6©QRy?ŒudÔ¹icóƒ dE! „€Bà$,/A»›å'ËÒI1¢½C úI5Š ?©QRÙ; >Ôh7(IO! „€ˆW–*ä'–£jºŒnM jRcÅUµ…TvÔO&ÁªbÙ9Ô4dI_! „€ñBÀ’Ÿ GYòT ÊLN ©Ð¤ÆÄÀ©j ©<“;êg?¨5ÈV’B@! „@Ü`ù‰å¨â™9k¬|9šÁí~ë&¥.Ÿ3È]c‹RÂÕR‘N<“”8êRH2B@! „@½!`ÉO9;Õd¡­ÁZDÁI•Žd:Þ.µÚBª©˜mñã©N%! „€B@T’ËQ,OUò´JGG7°Ë¿Èïs|—¿«Ò%,sTǩؕ[f·l ! „€B@D@²ËQ,OÕhÀuŽØNìUÈ!µFiJâB@T‹;Œ¾÷ñÇ[úóÍĦNÚY­ÄääZ!pëä©PLã68÷Í Þýì³ Þƒ'Ú·uµß›–öûÂh¦-i !3‚šTÃt¾¯ÔjkRc¦Z$£B ¸/mVÓ<Ác˜rïWð¾Ñ˜‹>fb:A/'U™÷Ê”‰¯ÆŽ´4SMKS0%vtÂm“A 1-Ë"Ì­}n±-W©Äo˜þ´CæN4¨Ôên˜Ô IœQÝdìó­Ù‰‡€zü1ºàÑDÞ?Ô’èî—'MÚkÇ“¥ñOÀ$ö•Z¬K_©Õ¶Iÿ*• Ø"ðàSO¥æùó>%Óü„¨çTE»ÂåV†™Š2¾ørTS9[%:un÷úÓwÞöTûSǬd “FŒœ~G%ÏrLô;Ò¦žŽ­ÿ#R+ŠvžËíꦨt/föÓ·Tsk÷¾œöÈŽ`ÜIS 2®€]æH{Ÿ½DWõ2h_ñ—n›4åVÃô?ÆÇ¼>ßÈ£iFü…Áø“¦\ is"fX9×<„s?‚]棑Øeº]®‰>Ÿ~ ¾„ô®´Ó¬h9&mN²©šãƒKÁ§3ò³ æóÚ¸^Ö$ÌþdÂþqz"_™`:y,*/ý[«XŽ2ªôB^ñëj’½C–B@Ä?ô¤1Š»ûcaÖ)éîÿ6)%¬Otó,…”‚‹ôø{$žmbú¯Sÿ®OŽáap~“ Ð5öSÑò±“V&~:Ñ Ã4Æ©Šù2ºÆñ†Xç4åiÓ0ÿ aìEQï„еÛ³ÇNzüÚ`ŠÒ\!óŠ\Þw,HÂ6ö^Ré18±>ÓðéËïH›Ö:—Œ6Ð`ö-Ù.Yƒ0×[l·Iº¢mBZ󬣊ù¬ª¨é¢ƒ‚ñ؉é¿ÀñO¡5ÔT\åûqoÝëßÑä#*©ùÂï5‘oK·.þ_ZÚ<é;´„ ú „ô×pÍß‚ÓÿЭþ—½¾©ï‡ž ¡ýAÓ0^ïUªF¿AÞ^Qíx6ÓÇëÕ-GÙôÀv ¸|ù±‡Ö”=&ÛB@Ä/hOa“8åx?©¢IµkK–B @7ç¶o¼òdÅaÂôé)ÙǽÓaHÿî+é“n¶c§¥-x#Ë¿p¡aOC¸:;tÐb*]]î&_L»“Ý¥Ð]“¦|æ5i›¡Ðí‰nw·çÒÚÃû1’ü] Ô9j’~6ÿÍû8`àNCnÿ7'}Ò_{ˆnK{b>„ÔM~¿wöñ/âðÊ”G¿Åhø6d˜ã\žìësÑìÙÓ¡4øß+é“^œèk¨—£\¯B€='ýÑù]¬È_”í¿ Åü„î§Ç¤Íø|nÚ„ÃáÎÉòo¾“…hh¢û‡h¢?¸mÒã?ê¦þ‘}Í1O>ÙÈÌõ=‚t^û?ÙéÝ›6û½<_Þ.Ôc0D£Áİr߬YIyGòï…pŒYn K‘ „@ý!`òÀ©@À‡ªã…T|¼KB n(ÔšÔ}‘”''§h84„m Ä=?-í?4m/@¤rÇ_Ÿìz ÝןØ*ï>}òv\o~KCÄçÆó"E€:”:ªéþWè>îæG¼ùÐx^º¿ºëE‡sÎ0ÚZÊ„¦e&©Ð¬¢tŠ1"tyë %XÏHÅå¾ò\ù f—¯dŸy=ÊýUˆ€jz9ýÑÿA Üj’ñKÞ¡äâÚðº )ÚÜ’s‰žN»ï8ì`ÿº/åM/÷Hþ‹¨GwR’ÂB²! êMQN„·aȺ#WE“êÈj‘L ª€Ð™mgÛˆÎ6ÍnOs'l)_QÕM¦¡“î÷wDZm!ÇOrYóAŸ<ë´Ù8龜`bS÷®ô¬UU¥Í†A—Ý_mØÙvåóQŒiЄ>a§e艇îÕĔΆ¹iï‚öQäg±|kNúäO--$ Øì†ýAÍqè1¬o†hÌäQÌ÷tòx Å÷'œºÈ“–vcq¿PåóRÞÐ ²Áƒc'O=êÑ\_ût£…á÷>îm?¹´ Ý¨B®ˆŠlèO¢<Ó™©{•sÑ•þW¤ Áº$\2 ûÂ/Öl< wNã>^u»uÄÅþAÇNš:ƧO |Eª›Þ‚`˜oú³LR‡’¢½0wÊÃkKRŠl­Ösê^ÿÆ 2Bz¥4´'̄DŽ«LÁ§¸æ½.·g‹aøN3tÿ‹({fmÄßø*/L´uó<1<‚:ÈJÐ\Ÿéf³,ÿÔñ8Ü,4'Õ)»ƒfýð‡€ä²n´`¿|\4«¡´e]Ô Æ ˜‘ýÇ}›Ø.58 •ÓJ/šT§ÕˆäGT“ÀœÇ&~•èò @2ŸãwéÆ|¿á_[Ê9Ø®€†öðÊ”‰ðkjÞæ—^SÙê7‹218è¶×Ï™2ñM;^´–ør/ÔTí*øZ}¸ÐW´3E±×ÂÒEsÓÍ´¯3gÊëa;y;ò:ÄÐ/1…çnÅ0'+.åzhVÛñxiÙÞ*êøú‘¹Ný­.u>6'}âSðéúG»V÷™ËtŸ±å{Âî ÅTK »?’` î¦ò'¨%}òÅ'Îx༤d…5ÅÛqÍÿø|Þ}ºa|õîõöœ`_£»Ýí°§xZï¼¾¢½¦á_‚4sabq‹Ç^Vµ°™¸Â4f?4!þ­“~¦ñ”} Y !PO˜Ö V«°z…TÇ<ï«Еô §PÞÌ0ÕKYÎB î~üñf‘æ~>í¡¬ŠÒ»óñÇ›aŽñÒ#”¶O­è¤J3iêxÌ®4cnú$ô˜+æ}i³šú=…Ús>z(\2Üý}Ð7µÇ’c¹g ±x?|®vV©ááP7Yö)|=ÌtÕ¤qªçà´‡ʱ÷×Ô’ó~È7µÃù§õÜSÑ 6ø¥uí§¥í/ìÛmwEñì|Öv9ìëÊRš#P›²ÔüiÊj˜ äÒh.×é—÷•úð¯¹RV>eR+ÏLÎB  Ê ©UHBNB@Ä%ZRŸR¾G7Ð9 ¾šÏ»ìAÿ÷N…*ÝýN­É—B@! ¢M€mRí ˜Žîî!Õ®(Y ! „€B Þ ˜JPHE·¿£…TÝïQÊ'B@!÷Å¿=ĪCr&ÙB@Ô#Ñý;\“*Bj=j—RT!P—æ¤?¸×çŸ! „€¨;AM*\:Z“*Ýýu×HäÊB@! „€¨]!6©pS'BjíÒ—« ! „€B@”Gv¨%šT‡Û¤Š&µ¼”}B@! „€ˆGJÉÀ)L4"šÔx¬c)“B@! bŽ@)Mª¸ Š¹ú“ ! „€B . ¨¢IËz•B ! „€B – ¸Bü¤’ؤÆrUJÞ…€B@!?T—hRã§6¥$B@! „€ˆª©G÷+äìS1ëÌÿöÉO Ò }ÚL'S1Ûtjœ´Ÿˆ‹aå(¦’E¦¹KÓ\Ÿ¾4呟,O" mê$$²£ž>SˆvjªöY}y¦È½_Ïz9Å ¶ýzð>Mð¥dRa€‚ûûcJHMKûGâ^ßÞ»TU¹ßoøÛðôЩɉFã†ÉJ¢Û]ïÜiú|Æñì|3'¿Pô±“§îóûõ§6xnöìû¹⾓PiSÑ‘cõ@™gÊT~¦èº1»½§ýsii¿/~«Å¹÷㣣UŠ2m?®ß§#¼+oþ´¿Zè $$G‹aM¤dõ˜IS¾áæ¦OÉËš ¸Î¥ª¢¾ªF»þÝÛ›gõëª èÞŽ’jê’1“n~¡—ÖnÙK?®ßf®Û²GÑT5«°°àÖצ§†Bø‰°ZNmJ›*Šì PÞ3ÅçóýñïO¦ñ3%æŸ'rïK3G ¼¶_ïÓÚ’¥ìrÏŸ¦©¿%@)IÝGÛâµ9i©U73ƒÎ»ðNcÅw ^ãeMTÞ}¦©ü³]ËÆ n½v¤zùˆJû–MÈíŠ)EpM ±ÒdÌcHÿ®J¯Î­iǾÃÉy…¾›OqnÞO ¿ý¡øÂ1ÿb‰&@iSѤ)iÅòž)¹E7:odîÊï,Cycöy"÷~¼µÖè–§¼¶_ïÓÚ¥BIÝ|‰2Û‰¼/Y5fþó³BGö”8½‹\ Õw¥Ö£cëPβ^†óyè–+µÁ}º(šæžv˃“îG7~üARmÍy™ËÅ⦴©X¬5És(y¦tÆûB™ù§Gÿz2‹Ï¹÷ë¬Åæ…KÚ~ü½O“ ìZñRQ’½î´¥“…Tå§‚u:.óOמ¯xÜÕVü:ä‡91/ææv'<~Ó]ã/Ç…XPåú®Ï‚ª´©iq’h¼}¦(Š:íw½ eŽ¥ç‰ÜûñÞHk¨|¡m?žÞ§è ©.2DH­lû3&-Éívÿ½}«Æô۫ΩςUeÑã3·¶-SÃÆ_êßh#°Õ`œú´"mª>Õ¶”µ&ð3¥]Ë&fbbò+_|C\ÃÉŠŽ ¹÷ƒ(d¥Šâí} ¡*(¤úMR+Û,T½©r7IµýÅ¥C5Ñ V_ >s»é²³UäÖ/<ÿNìe#i6ä‰K Qû/m*j(%¡úJ€Ÿ)¿5T3L³M»ÓzÞ ±ð<‘{¿¾6Ø(–;îÞ§J‰j¦cGø;QXa­©ærk÷ò(~±A­Þ]Æü˜cJjêmH‰Uúæ‹_}ÒNK›B…KÑ `?S’’îBzNžÈ½J—4,vÛ‡÷ihw¿J¦t÷W¢«¿÷ÐY†a¶f7S•8O¢†!P̱ÅÅ×üb(¢ÔGmª´©0mCv ªàgŠiš­~þ»±Cp¾“µ©rïW¥‚地âå} á*ØÝ?•"¤†­ñÒ¬¯^w‚g;êg?¨ªO€92Ϧ-[]„ÔØå„ÓµÕ/tI Ò¦JXÈšˆ û™Ò q£K SŸ'rïG¥¶%‘PvÛõ÷i¨&ÕÔE“ZÇ­[DèÌ3I‰£þŠPE~Œ9¦&%˜ªËÕgñS}rI%m*ò¦"1…@D¬g žÑŠªu N}žÈ½QmJ¤Êˆ—÷)¦TjRE4©‘¶¶‘ÕHUÚ`ªS'ÚËFZÇÅkÜ(EÑÜ®–ÈX¨&µ>0–6å¸Ö(ŠüŒV4¥ ÊbkRö<‘{?šË'ïÓ  ­ªt÷GØÎø¡âRIIMt»ù+XB1o‘¯Z,˜§¦¨ì2†mRù¥âd;²j•µÌÉҦʩÌ&·»Ìû*sJÄݸs?x‹Ê½ö†m{黕Ë=&;«N€Ÿ)*Yϧ>Oª|ïcºHÊ+¨ú,‘>¿Ÿ`³[u¸=óÀ‘lÚø„#rW—÷|<¼OMµÄ&ÕtðÀ)TœX(µºg¬ÊêÄþÎêÛ™~~Á *—ëàÑ⇠üV9h¸xõfúè»Ut,;š7N¥‘ƒûÐÅCûV)y<3™«ýB±Gøó¾ø{šqÙø§U§M’Šïÿáî›õÛ²èï~KOO¸¹Ö¦!æ¼9žcç®5žò÷£††½uÝ çÞù‚~wÕ9¸÷»œTQ,¤nضÎ;³×IÇdGõ@³A¤â´çI•îýãÙùôögKÑ^²¬w@R¢‡†ìŽ÷É™Vûÿ뜩{‡–ô«ÑÃàͦ?¿ôoºóÆ‹©YãzÿËå´ Mš¦P§ÖÍé??¥–öðî~ &²â×uÊØ¾to²·vVùµs›æ”˜À„wÑO¤:½î¨e`×¾#ôø«Y驪B©ÉIÔ·k[úÍ#ˆ·Ë N¸çcý}Ê3NÙ/~Pv¬&ÕIB*·E~ài™Êo™åµÖØ÷ñ÷?Q‹¦ þkà’a“ܺû >ç4ê×­-]³…>X°œ†öïB©)UiS–ðÏuÎ/þ1ïxŽhS± ùÌÞ¨ÿøÚPmN#NëAœÕ—Xø`Á úï·+éö.²—ZjšJ³î¿‰@o²” ½>úÍ•#hPŸÎôíÊLºùòጨҕ™;))Ác V¯þç;*òùé‘?\iibwì;|’€ZÙ†°~k}øÍŠZRY8~ùý¯éá[®¤Ö < Ñï¯>·ÆTºæ|(bÐÖ=‡é½/–Qï.mñ®ëZ..gÜó1þ>5)?×Á6©Nzªó-oý𰈊ºzÓnúvE& ìÑž>_²_ƒh"{5*k7ï¡Ï—®£=R‡VMéòiûÞCôã†í”àqÓ’Õ[à¸úl€ó¿A:_ÿ°NäP¯N­­¸]Úµ S]£È§ãËúZµi°·4=×_|–õ•>ïóè§;ÉåÒèB¼/=»°ÍØ+ü`´ÃygöÆËú'Ú¶÷0Ö³ƒ½;âeñ—ŸýB òFöUÄiÅHÄ`£Õ¦b¤ÜQË&·ïyŸ/£Çn¿ÖÒjL‚–óú‹‡Ð'‹W[ÚýA½;ÓÕÐ2ñKú‡u[¡iYMSî¸Öº~~¡—¦þí#úõÃéбúrÙzÿëˬ—ö7Ë3¬{Š·ËûàJMI¤ö­šX¿Y‡h1>Ð8p~¯ÞDçžÑ›ÞE¾øÞæû‰5\7\2„NïÕ‘ró½ôÚ¿£-{R'hZ6IµÎå,@°ÀûSæ.ʇy@2òÍÒÆƒ¿½œ’“<(ë©ïÉ`b²Â]Ú|ñ3%x¯aÝ Ï“`~"½÷Ú¸‹ö>NкwheÕî~]éxN>}ðõ ºê¼3,!õ߯‚ u gžáüÿ¾¥¬¸ùòaV—û›óÓîG¨%*7]6Œºâ}Ä{ßþ´Éz¯5HJ´Þ;£ÏH  ¡,{ï\sá zý£E”)k-OëÑ®½p°µþÜ;_ZÂùì}<•¦Þu=½ûÙ2‚{Hëžæk-[·æ/\FÔ¥msh˜‡Q‹& -S‡Šž|nÙÀåèØºunÛ‚¾úa=mGÙYHuê=ëïS«»¾§8(†s5©NÓ¤Ù–¹jþ/òùÐm³—–®Ýf½X;µif½Øø«˜o´}º]Íèž›.ÅË®#5k”ŠeJIJ 3zu¢[~vui×ÜÊEÓ†)t̓­—¬Ï¯ÓÇxPq¨è|ü?øþaý6«›þú‹lß­ÜLk·ì¥[®:Ïzøýû«åx‘gó)aÃÆû¬!ç¹ëÜæÌËxvY㽜5R>nßGNäBêȼþÖ'‹ðòèFWŸ&}—!¿D8ù7ÐMÏÛxˆ[çò‡ÚðÓºc<¤Bï}ù#^hyÐŽ®¤aèF-O@ås÷Ãö_þŸ-YKˬû§7ï¶î·ŒíYxq/†ÀЉà³ÐÚÏùòBåÀ/¸­øØüùÈ3‰…¾ÿí° +2vÒƒ·Œ¦ßáÏû´CÜ[•{ÒN·ž/í{ÌiÏ;_UOÖ¡c–ÂÀPí“úB£A—²£6˜fºuóFøÈÙif3¬LõébmŸaõ3(DXùÁ&c‘„ŠÞ!Í¥Xæñþá÷Ñgõ±’|ããE””è¦{nEíZ4¡·ñ.ã;Ú· Ô^:¬?>¯³îZþpqzÏrï—¦QG¼OÆ^7ï¢sèÇõÛieæÒ ”žÕ/P¦k`:Ç׿WH9ùñ6Ûpýšãní[¡k~8åÁìæKëÝÊq+zVðñ²mLùþ|Š…°…2 ›…ù8øžÙ÷)nŽ˜8å$MªÝfÁ.ºáê‘gPkîYûjwõó1Þ— %LJ0QYðc†Õg¿køxE!Ü;¤ Ô,hxí÷èbñ¶ë/„ýf½§ßú̲ãÞ{è8±½fß®í,ó—vªùãÑeïÞñÐ~Ö»ì0ìÀY¼~ë^ëȽ‹Ø&•ó²aÙÚ­ÐÎ&Ðo‹{ûX¹óÌ¿>/¥e®Ì³â³Åk¬KpOkP[B#k‡²ùvÐ=o·ýˆÛ™]¦º^"öƒþªØÖJœ(¤F½à¬AåÐ …¶4.‘òc¯»_‹é±W>ÄK­«ÕUÁÝüå~è°Æ0 K›ÉšXþÙ¡¼kø¡qå®cëÀÍnÇՑƦ]ûq3Ä—ã{7ñÈÉòk~Ÿ÷K|±¶ ŸA{Uõ`uÍUýt9S€w¡ÛþŒƒL{Ÿ½ä—eh` UïÎm,oc®½ ì >çBhŒxð# ï}ñ#Í|ã3š5þ—Áä0Õop=t…ïIþð6°Gp7w!®Ý²ÇÚ7kaÙ hïÁcÔ85Åú€­ì=L\Vâ†üs·zÙS¼¯QƒÀ{üLhð¹Ë÷þ£–¶ßîêçóؤëÊsO'6ƒy°æþ{ýeÌÏ­.ù²é–Ý.ïR6ooܱß*ÿùÑÂàa`¸çàqKÃBóûè±h!s3ljïþå%Áx¼zïœÈÍÇý°Ô,£üÇ0pŒ…ÍH+NÚµ,y·uhxÏ9‘ío …HŸ{z4ù^=Štgâc–µÃ·^;2þ‡æ;¸+uwÏÇöû–:Фd¬"¤†6ª0ë5ö%n@EgØÐ<ú‡ŸAPÜFoÎ_BM&Ó5°Ç)vÂøýÓÅkѵr©õB[ðã«KÂÒGG.ïlkêqk'uãsW k–Fêë *{¹RÛ,è¾0ï+jˆRÀ(Q<£¢Ÿb´sXõôâ¹lU§RÍ3Ëk뜤›6Öpp›å¶Ï¶Þ¡µ?l²ÒÝ“ÜFïŽÖK=4NÙuþhìÓ®Úd •öq7Ò//°]Û¶9ž<|]úv8×üdájKÛÚ­}ktû´ò áÆŠôž´Ó’e¹œrÏU:í!pñ`!ÑÎSvX·uõAÕZIÜ~[cPÕÊŒíÄ[/F³l`³€?\}=ôì<âÁSÜ&OÂÝWeÏKLè–îÅûˆ»êˆsNï%ÈJÀ@Büºµg÷Ø%!ôÞùjÙÛG(ýŽë­üÜÓiàÞåv£8pMÕöävˆ´Lv|^²i÷z²'4„æ;t¿ÃîùJ·»Ð²Ôæ:2Ԥ⺎RÙžÂI+¿šÿBáî Å_ÎlÊ7F¢@àÖ` &krŽž ‚ãnvcâ(þ±Î ÷Í VBË65¬¹YT΀вõ[ñU¼.= ëXY›TþBœƒ/qtÂ6F¬!â7œÆ5\Êì/f\oºük­M•ás›<@d;åÙ?6+ztl!•h!L¸ëðÛ%¾Iùåÿ:4?ƒÑc1‰_hŸ/]6yîZeMpâDV[5=¹Ë±¼zwÆ=—¹“ص ›öð ;pWft±ç€ž[Q 4Pvˆäž´ãʲ˜@ÀKè3ÅIh*uï³F”»»_ÿß"Z‡ñ܆ٜå“EkèØt²‰ˆx”ÿ÷øpb!ŒÝ%qà÷ÅÇøÚ¡Ïe{mü>©Nàóù½ÃšKîêïÚ®%ºÙiþ¢ÕÖ~~—­C»æÀ‰ –g¢'¡ †Mi³†ì;|à4¢\*'lbÃÂìÀ‚5k†¹×m]Ëv±³IÛ—õC†¥ˆù÷3wÿsþªö9wÜa«—cõæÝÀKzlN•^Þó¡mŸ×c& Ÿ+h“Š–ãX!µ^t÷—×jØ×ÛüïW[†ßü…Æ7ÖÈb?ŠçCÃùö§KéÞoYö6<“íPyq2üæ]<â2’pã¥Céï|kÙêp|¶Ý;­gGkD2¿? ¾ùÙÛñÈH;ð ö@Àm}ìÀÈÐn{¿,…@u,…ÿìvÛ5öjDKÖ¬°/R6y]ôÜeÏÝé>†¯_ÃG® Œvnua‡ž[Ùu¾G®ÚL<÷F÷·G÷ýÅÖ@§7>^Lìo•·ë~·]kÙ£Ú„YÀdEk[ºåŠr/{>¼Ý¼öß…tÏô7-3ž‹‡ô£ƒPŠpàn.;…`Ó…™÷ÝT* Ro¸d(zEÖX.£š5j@ºf¤¥‘-+Ж:1ÌÆ«~gá~vØvïa¢—Ú-÷|)m¨èî1Èr¬ZmÉ̤)ß0‘¹é“Gò²ûïTÃß?0é?}»w|ÿo.«Fr‘Ê#0UÌ›b?hì³X#T ÁÛµªálVí8á–^¯³=kÙ-ÛíñL9)–«pgWÿ¬7>¥Õ6g¼ùÌ´{Ú~üŽàÇF°ü5U9•Nˆ‘P'm*FØÔX6Ykªà¯ì=Uc “pF‡Þo|¯?øô<ºéò³-t~‘²ÏKªŸžðkKÈà¤jëž “í˜ÙÍÏ”µ™ÛÖ¼>û‰_#ÓN{žTûÞgíaèó?ÒŠáw»:KðDWÄùáö̧ìYÖØ•ëÿùžÕ©Öd|}Öèþåå,HE“°ØvØo§m/¹,ÜÁɦkáBÙ{-\¼šÞ_65uÏ×Äû4вTD˜?›éŽÞ²EÅ‘_ñYâï2¢j'Rtï¢ÚÉsT¯âv•€ŸePUP9ÃáXü"ç® B ^°m§B¨€Êùa­Û¯ñ`Æcè¦äø¸Ïb[Ä÷¾%\Ë=鄬Û<”}þGšnKáž÷‘¦Q^¼òòc §¡ñy`0›îðÇ"ûáf¡¶u³’ºÐ¸öz8•GbWZö^³Ó­íeÙ|È=¾ S Œ G4‘Ⱥ©Â'WcGÊ—Ðjìr’°B nðG&OQ¹tÍVøDͤDDzKe{o B ðô­_ÂG+Ï|ÅBj›æ-ßÞ¡fdñPÎHË ÷|xRùSâ›Ì´&么Z‡ðåÒB@Ô.¶£ãŸ!x0ƒPB@îù¡k¦¢”©DŽ•6º?”¡¬ ! „€B@D™€'D“ 3jR£ÌW’B ΰèO¼Ž‹EqV2)ލ}ììŸúW6°»ÄЉ*{¾Ä ¦/D“ 7ÑN͵hRZ3’/!PÏðh{ö,Aê`o2¦Yùû©ü²²›ÄSöiúؾJˆM±ÒÝïXé96«]r-„À©ð€Ž%¼´fó.Ò¿mÁì8£B/ñ ??®ßŽÙÝÚЈÓ{Y®ox–·¶…Ÿ -;4¿ùòa§ºŒõžûz}>¿ÙUÕECûY>[sò hþÂ5tVÿ®ô9f~c?Á]0ÓÍÆLZètL8ÓÍì=t~_ýÖ}ÚþÂ/ÜÇòÁþSc67žúõ²aËýÊNS–$ B“Zü ‡Ž•E“êÀ¶#YñL༠¿Z¶ŽÐ»Ó*Ì õíŠL¼DƒÞPhEÆ:{`wÌö³…>úv¥…bGÖazåßßPvnõòŒg>R6!-Ë7ì ö­›RKhGŸxõ–)M!ü}·r#½ûÙ2êѱ55o\zV*6»Éؘü‚×ßûb€K«a§õ žUŠgƒbŸ©|^‹Æ©p¼ß÷¤4¢•I§æ˜,¤ÚÁt®êXéÙf'K! â‹ÀzÌþÄ3ÝðÌiÛ6·„ÒÐŽ>ç4KãÓg| aöºâƒ<3Õo®t¤¿éÐì˺p ˜ú—§tå°³VeîØOí[5¶Ìj®¹põîÜÆšY‹§æÐ¦ùÉS¸²/ñë/bùæÊ͘j˜ï]ö$à…£ÿîZYçÊ¿Ø" øÕÐI|+ :6c±UÝ’[! "%îÆÔâ ,¥$•r¤Ïi°ÿRI n«[ÑÚÀ¿NmšÚ«²B ‰Å3QqTë~*( žÕ©u3kÝ[TßÁ[¬õÁýºÛ+-š¤Z*o»Ý.±·ÁÄøÒÔÔ`w?OèÔâHw¿SkFò%â”@Ÿ.mi5fzâéGWbFžF24¬ÈØN˜®Vfî²ìRCɺ‘زk¿ÕU¿÷à1Ê‚mi_Øy— ©øP¼ãÆ‹¬ß~]Ë>é#ÒŽ‚M6¿‘› —«¤»_lRc³%×B@DŸÀ…gõÁË-ŸîŸõ6­Þ¸«Ôx:Rîæäù÷1µc];£Ž•KÞvVe)„€¨< #\°_¨áŽË~! "'P“÷“¨‘׃“bö½á† &ù ÿ0®ãL‹ZÇ —õ™¿³² ! j‹€¢¤èȲG¨˜LîR+=;•äHhàîþ«Gžíd%=! „€85Ö¦º9ÚŠ¹ƒY,qXÍ;D“ê€J,! „€B V `&jûzEy©´!Õ®!Y ! „€B þÚ¥z¼Žô•êHɹ.Ûì2à·Ñ6Ó RëUÍwiÚ×C·íý²ŒoÑnW¡mHÚ”sÛN´ëK*uïÜú./gÑn±Vÿõ½üåµ §ìƒª?h•Zäs¤<èÈLÕfÚ7†aÒ®}G(c{ÍΣyùðÙˆ™µòY+–O<.j”’LM¥Pß®í0{N3Ìâ¡Z/œÐ‡Nå/ g8‘€Ý®vbÎùõÛöÒÑyt¾A‹à4Øœ‚+•,Aœ´)fd¿®cÒL{¢î {¬-íz—çI¬Õ\ôòk·úzï×÷òG¯%ÕJJAM*fWq¤<èÈLÕFÕðÄ/žõÆëõÑ7+3黕›áœ¸ÀšaC1 œÂ÷›% ¸Šé蔣d*^úxáŒfN¢ ÷¢‘ƒúPB‚+(°ÖFÙå5G€Û ¢_ÿ˜I –gOÊŠô¨·'.B µ)ûÅ•“Wh}n€à¾}ïÊ+,ÂüßEÖ}ÈEJNL¤&©ÉÔ¼I ñ´}»ãƒ®uóRÚCŽç´ ϧÕHíç§¾ßûáʯ©©šBøüŒZ¥øÔ7tTzà}š’H ê-ïÓÊ.R Ñò #3UyΕ;ÃN}>Ÿ¥åzïË–0¡ÍÈíïBŠÞB…5à­r GÛT IÓŽRžq€þûÝ*úvÅFúå¨!Ô¿{K‹ÄÚU ±I€ÛÕº-{è­O–Ò hMk£=1)'·)[xÛwè}øÍ ë~cªBRýû†øiä2ñ(¯ßKûò¼´ÿàkêT¾GÚ6oBWÞ«“#…Uyžp+¬ß¡¾ßûvùÿõÉôPb‚³[5 ö¡ª†˜»E»•xfàC7¯ (ð>]‰÷é¥ò>”³b’ßîÏ*H!5Rn5Ï~aò¼àEEE´`E&}²x=„‰r{ÏÀ²q]ÛN˜…_Åß /èVd¨Ç)ÇÜLs?øŽ®:w ]4¤¹\øê,6°Ï‘¥³ ØíêËÖÓ‡ ~ªÕöÄdœÚ¦láí§ÌôÆüŤûUR}IÕ[ZŒNU«šR@†ë08”Esþï:³àðý!w/ÃY‡,Óœ¼/y09@J’‡ÚCÛ³ZU4YÀ©®Éq»Þåy ­øŒc·úzï—-¿Ç­Q«f )ÁS3Jž²­ˆà”¤ëÇÓ)ËΗ÷iYHlÃ&U·íÏŠÌ(uWpÁ*ª7šTûfòùü°3-BwìúliixajÞ>ÐìÔ¾“…b¥`é ø \mµ•‹ÎêKn·tÿW¡-×É)v»úrÙzú`ÁÊ:mO  .Û³à`3aSšÝûÓë/&Ã×€\Þþ¨#Ÿ>Q1“Hóu€ç¾öðä·•®ÚDÍ%C›ªÒë·SÖ¡cÖõÊûçÒ4h^;ÐðÓzPŸ.m£®µË(Ï“òè×}v¨¯÷~Ùò''&PShOÕèõêWª!±`ܲY#:zÊs|¡«/8&5}ªmN#Ï“²Üëßv}¼÷Ck9XþÏ–wñ³ÕIósðÈ z_Þ§V ºûƒ£&”8£ªðœÚ>Xû}ܵ\B~¡r·#Û Z/ÕÜB/Ôž–Æ©–³ör¬ýÒ¼=à] ¾­,ç—óÍù—à<v»úzù¸*+ uÇuè¤í6Å/&jÑ­»ДWþ “™ ò6'wá rå#wQruƒÙˮОÂtÇ4?ºì£4©nïÀ0jy€K+>'ïl"3ú÷×ËéÍùK¬2Tõ¾²ë]ž'åñ®ûì6PŸîýК-U~ôœ4†7ŽºêâÍWè:ç§IÃdÊ–÷i(–“Ö¡K¾´|ŠâHMj0ƒ'å>Nv´;Ô<ø=]¾i´^ðQZ ƒ¤*‹óÄy[¼f›¥õõCCÅù—à<v»úvå&Ƕ'¦­6(¯N™põÜ»_QvŽAžÂ3!˜B3‰®wgx€=¬†A[KÖlvåÇ* ªv½s¯Œ¤šÜË/Xòx ÕÏ#U¶†õG£,–ÏKäñXNž•oÎ?—C‚sØíêȉ\KXsr{bjUmSRÀž{eæÚµÿ(iE°½Åà¤Ø °ÔÅ€*žYnEÆ«§"ÒûË®wyžÄníW7çvˆ÷{?§Ðò[3I±èàÀ~T5¸¬“÷iù•„þ`êûZ~¬ºÝ×Ýý†éŠ…T/´&yø™1¡J° ¾ùeÈùçrHp»]ñ€)kê\çd­‚œT¾Mñ ‰í¢ÙþrÁrØÞR&¡h]Á5bãªÃöeY….ÖŠr9# v½ó})Ï“HˆÅ_» Äû½®æB˯:m´T˜L«š*ïÓ0lB5© ªÙƒ0LZ5µ;n…TÖÙ]ü¢åѸ>Ÿ-P ø54òé–€`w÷sY$Ô=ÐvUT„iQW1*Ù¦÷´¨âö¥ÜkwM° Ç~R!µÖàÕ¢›t@HÐ-s…H5=ÑͤŽ€Ý®¸Žb)T¦Mq--*Ìd2¶ï³Š©ÂS¼îåòá™põ™Ý·]ïò<‰—VPùrØm žïýЍÔ÷òWÄ&&… œòyet­Ö!?D? ØAP%‘¹/Ö𦙌µ‡b­Vv-^,´]ÅÒG#ªL›²ºõ,!ÎGŽæàsÛ^ˆ¤Z$]×R ¨A’½+¬I=µÝwh½³æÕçI Wó’mñ|ï‡#_ßËŽKLïñ“š¤iÒÝ_Û•¼©ðzæ/@ÞŽ™€¬²Pa—!fò]2j×I µ¦@­Dئ¸||¿øuأŽ;;3G™ÎÅ[™fgb,CͦŽ-TëƒöÔ)”Ô»c”´+_„÷þ©JUßË*>±vÍ"ØÝ_”œ!ÕQÅfÐQ¹Šbf‚7U, ¨(? @vÞ£ˆC’Š®›X ‘¶).›%¤Â^“ÅšÖèTõâOÈ —ªRvMPÎÀ‡à©Î±Û÷d¬Õ¤uo—S–á ÄZÝsI¢Yÿõ½üá[F  éîOÕÜ¢IÁ*<)Ë. .-ð+/xÚ©j„h”§——S‹ T·ŸÖ€.»¨M½—Ú¶ö„mSC5 Ñ5¦Æ4:rÌOß/É¡‹²)5U«•6U"¨êÔ$ÅMÌ 2Uø…5T«-„–È™å—Ѿ@C”õ7· æM\4éÉ=Öîág5 sÏnHӞϪ´‚×çÙL†ë 5JtQ»¦ ð±©â§aþÂˉÖu«[ïv™nýu ÚÈO_|{‚®¼¤1 ·[¡m» 鿟§»ý{'ÑÍ×6'~:ì§¿½uÐz†T§üvx­ò„¦ïëÕeú>Ù½·ÈjsW\Ú„.9¯!=3w?mÛ¨{nüŒrzò`}Åš|š÷Ÿ#µvÇê–¿·ötî=éåÿ[l#÷¡á»Ó/¢ÝŽZ—nÞ8•F @í[6Æì‹>Z³y}·r#¥$'Ð×_Dÿøè{:|,'\6#Ú_ݲDt‘ˆ}DðÉשu‚#»ƒRt ð¬t¹!–ü*}:Ù˜¶íð?PZ·pÓþ>zâ™,šóσ4 w2 >-ÅJ4/_§¯¾Ë¦IOì¡—þy€zwO¢g¥RNŽNKWäÐU£šTþâ8£$ïŽl;U*S<œd×KeËÂø+.mDŸ/8Žº¥ ÛT×N ôâ¥MÛCËVä¢ 5¦ÔZ­µ).£Z5N²^¨º+ (Úû+»,[þpeät;wH îlƒ2—~D-]‘GIIjðÞ«L\þ¶˜Ì£1(ôÓ—k³èp¦´…Êþ#ѦÚõXVæÊ¸];&PÏnI´`á ë¶„ï9¯°>X °¦ÑZ9¸Ý*ýæ†æ´y{!ý?{ï`IU¥Ÿz¹_çîéžÔ“H3ÃaÈQAÁeEQpuU\uW]W1ì®?\ TPA‘œ 3 3À&çœC÷tŽ/Öÿ|·úÖ«~Óáå®×}ïLu…WuëœïÞªûÕ¹çžûàCÇxZÇ}ò#Fd…tô·ÊoH¤þ&‹€Ä0Ùëp¾µ=Aþܧ«éì9§FzguíØÍKºSk[„ZZâ!Å¡7Þn¡ï}½†FôбAyÚ€ë£Çƒôãÿ=J>_;y¼…äry¹!íÙ`˜IfR™cæÌ/~ùƒÿèqx¸ìpHÓÛ‰¾ô­ii¬ëÍ ` ´’ŠS™¾n0ï ?¹™¨®Ù`Ô£þêÔãO×›Ù,_ÕJ×^^F˜¸mÞÖ‘rB†:û`þíÙz G;Xv´Xrq·þǸ· ¼Ì!H› nü>_¶¼BL¢\EÇÈ©â%µPTñú÷§ã˜Qn&s-ƒK–˜XÀ‡tÙ;-tÅ%¥´z}ìy4OègC‹“»ël1hªCßBO-ÝB+Þsð(ÿrñ|•»èö›GôpçûÉçx¦Wîâž’vêho¦––F …“ꎽœe^¹¶ÚÚÑD™ ÖšÒnØÜA7^k|ÌNŸìåçÞA±A“^m¢{ïIe.jh §¤æß'}&•ÐÌ3¾øåë6šŠØqÃÏ~|{âçòíêÒéw:I߸¯'Q;ût?mÝÕI»÷–ÕÜÆÌžY@¯¿ÝœÖ³?˜E3gòXöwÒ¦=Ƈ®ÏíâÉýýõ5ôÏ7^ÜC4?OeZרÂ1šÃT×dXL½n£ëå¦ÝôÅ_!H+ÎQ)=øõn6—\r ¿˜–¦—a®6ÌBÞyåÈj—h¬;µ›_Á5c<´ÿ ñ¢°Šœ9ÇO·ÞTI˜ jÕZ£mjŽPGG”Ær×®JÃ1£=t„?Tà:Ÿz«Sòœé“|< ]§ƒlÑGÊv9Åërá9pQ´ÅIÑ@”BÞMqb)zQBH×÷Ÿþô×o2IíÍ-íÐÑ Up³lfOUß7íåG´„<¹lÑaWŠÀî^ÎÈÎ!¼Žôò>ÁݦMöѾî÷Ii‰“?„tAPñÛÉãTÎ$)]ýE&êONˆoOðñóû'êøÃ‡‹¸2«/r¡ßA£«Ýâ¬l?ûq¢dl·ª²„N0©”V̦¶zfÉ#~yÜ]ÞÙ°‹Îœ6ž®<}êšóiËÞ#t¸¶Qœ…H#¬ˆ}¸Æ]®v“@¯úØéß=µ2Æ~´-ã­7h··ï«Ø¿´­¢ð1O·Þ8‚š["ôîš6ó§sÎ.¢«áÁÑè…W©¥?ß»S-û”¡œÀ%Ãu]]éþ…½éß[ÂyðI½éú zƒ [KKúuJã®í›?\A%ÅElí÷1 <µ^bÐŽ»\NòxÜtÃÕUtò¤“Nœl¢]uÔJ»)ìÞG®Ðdr&1 U_ú÷¥co8áX]}ˆI4±¸+e_o-êeù'Qг“®ºÂAÓ'Œ`˲Ksë}ññ‰nYÄRíììâ^’6ªopщãQz}—Iì`½ì”mtáCOÈŸæ±ÛÐél)ûŸß½6~ŸSøºËóLZüüqŒ” ýEFêOÎè¯=‰bÓÖºýã#è–+ù9ÔhÖ4þHµ4CùØžT–RC‚Ë!žáîX}Í1AD%xeEOC}Cs•—øãaSû) Àž&IeÄ"©)`8h—ÀjÑÈVÐøôÁ«ËhÒ87ýüáãº%_üf“ 1–­ewß^MUléù+w«"55‡©¢4¹ï ª¹ùc¨²¢¼_B!ï?Ðúç-¦ [wmûÓÿüäK|îq^ úKxz:UQ>8úãKîüÚ·Ÿ›5uÂü¯ÜvuZjYÉÊÿ÷üváH<»2®{Ä\Cä•}Õ)¼»ûö‘´T¼üZ“<]¬S©S=2ègÇjEI™ÅRÌ&—i{=ÇOeŸ1Ò“«6½éߟŽ}‰ˆD¸•áMg@¢“]´“ö©£)5ÕLŒT}Ý7ãe¥Áläîzk‚_î'¸÷åÏOÕÓîASíQþ80Ûòvowv0§¢¦ß'Ðï”MÛ÷n|ô?úïÚí}b«g¿¯öÄZäö:výè ÖÑ‚¹L츾,_ÙFsø#F¦l>ûò™^—СãÆÀ¨òþØåçˆçûÿú:Í™2–n½ú<úËâ÷èàq£=…5µ¤H‘Ôpèw]ÀñòO¾'Nã~[T§ºûû(I4eÜífM—±óúùóŠéW¨ë•ÀŠ.YöUÝÆþD3¦úÌKKÙ‚ÒÄù©4¼hfK(ºr­©¯:…®ìÏÞ^E­ízôo'ÅKÛz]6ë”AR5=åbßH[ð Èï÷óR@Ùrˆjä N%gx‚U¤·ãõHǾ2,fßQXR‘_Z‰'(€Ÿw[x0óü]³•ð>A*+‰}¬Žíæ2®¦ç¹çåýn?eœÓЮ %ÝçV±IÜŒé/rUr@oíI÷ݶ³“þøÄIzaqMšè5]ApM6ŸýþdJç·V&–E…±6±¯¼¼üQ‹­>­\3~±"©}@Z{2$B~ÀÒƒtþü"1Âú•7›ÉçÕhÔÅ‚´àìBÉ£ÿ1Ø#ûOçð1GØoN&42uœŸJÃtÓÁ—R¦¾êÆ2}æV#ôÒËšiÜX¨kÖk³]§¤?ª´¤úý>:Ò¤Fiî Mc‚:Nª‘ðÚª":ö•qu¥AôêêÓýðÃ,tìÚÀ.¡pHtéKŸ¹¾îêqøÂ]£ºÊ}¿>ÇHÚ¹'@Í÷‰Ÿý1ª®F—_T"\]X¾¬AŠ ÷Ïœþ©j£®Køö¤¿ë7a¨nî09}Ò—¾ ¹”íg¿?ÙRý­ž?+Kh8ýå†Ä ©éF1N1@ªšGö8~Ò¼¬‚][’4i^¬6L^9¾Îübf¶šæ¿™mÆ7L!3žsžgx¼–ΤÜb”åÂs‹…õæ#×÷ '…Ñâ“ùK÷ƒW—‹ApˆG\»ÅKšðC+âX‡‡+’šçU"mñ pL]¿ŽÁS}Õ©ïÿì(Íæ¤{îiÞwý¦1Ø"u $Ýß°¦z½^ ð+lÓ¡zr„G&å‡j ÏVý+ÊÜýêh½.~{ZD£ ¥÷ñ¯;:DÖE<š8Žˆ @RaIÎF L¸!=»@¼/Îä5™þðx¡»÷=)" \|A±8õÈ_bt¦ô—÷Tëì#ßžôwÇš±núòÝ£ÅKD„A Փݾ̹xöû“-Õßjë[hÖ¤1âÙèCðUÛ賦ÑW>yµ°¦nÚ}˜v€‡ûm±£ÿs~*¥‡€Û[ï Æ Ò±Qàée›ñ«IíÒNö {‹¨_qq)“ÔZ¯°S9¨?|m„%5~ Õå(¥[:臔Rix#°fC®+Ü1è10ûJý…ÊÊU’¾© «›ög«£ƒ<¡©}‰<àñxýûÓQföÚ[-ìëk`eº„-‹Ï/6FûÊóRYGœ ~QlÔÐTî#¯YÌqo¿ø™QôêÒfzã*cé+íâ Aþý¿ŸòNɤþ}Ý[Ï<ñ퉼"7Ä?ûé[ÿyH„¨ ³¡Äšrõì[íÍsºžã8‡*)àóø Ï*„Ù¤R5ÖåºN´Aî­êØŠZŃ¥R¥– ýCòx]°GÌâTjOóAºë(U•»¡g6 *d•|feÊÏ~u€C;SCs;yxòÕžö^.ŒQlt¶†å†;GïgÞÑ!oIÉ -wéarwÏŽ-Òi?ÏtñlãFröà¡o¹sÄ»Öv&>žer]_~Á[NW›ƒ„€, Y¯ xàÍÙ+èÝ]u„2tìQŸO:uÊ ©NñÌÀÚègÿÍ®.?Ø21>&k€ŽwvðHù¤E‹È+Ȫ0zJ Á§UwÕ±_ë!APq‚#RÁˈSÎMæ@ĽŸ¢<{>åÐ{…¶Ý‘~g”Fø5*-ô «©APK˜¨–kj¬«ßxÆú»g|¹ãùTï“þz¿Å×|ö%§²$ûÓ¿¡©F”Ûg°oCS«˜­jöHÕžÊòëemúiðëRYR{¨·C ò¼0dHˆhL08ÂË æ!/÷wR{0@uT+îâ Î4‹*¬] g-wG²;BYa·œF#hÄpÌè'j7Æâ³)#8g ¨²™E·¾™©S4¾^«*¥“lMØUËõ‰{g`ðêäËD’$ÕÍ–T¹¢ÂB1²£ý1{ R¡;@ Qj ·P­¢H°f:"<¨‚<Ý4¢ÝÑÚxΉ’‹%,utQ3Ú0ûŠº»x ›¿ù$–Ðû°O †*s…h„'B÷:8ñܳÜn¶û¼ Ø>&¥EbA-eKj!û¤â}‹h¢=ñå®Þ'\Jæm˜`ä:±¢ËÓ³âë@>>ûÍçô½Í›©WŸSîÖ—–ÓÞŠ¨oýÛéd£NeŃjQT&¨<`*Gíio0åÅ1M×}&ÐHù¤&Pj&^=ÒÑ a?-v&»f@RÑ­çãÆ ng§Pp¨§h»-k¹ñlgb1=ç>ªð„Û,¨£üÜ%Y^ 䃜rgª»¿+Ò™`töR&î½ü–ï‡LÝ2U§Hoõjú˜2 …B´¿‘ëSÁàÔ'È–©:eT×AE˜”Â3ÂñJ0R6F`;yT|U°Þ¼´5êÔÃbÕp7{ÁŒÅËqI}lÙ,rñÚÁÄ•ó)àçïhÀA¡‚Õìß:‰3Žê&¶Ð¤ï„ç%êd˵›]øÝZæ‰ÒØBô˜x öŸu»]âƒÏ{![áƒZ\¢Z(*ž/œ“(A…4½•ûp}Ÿ¼S8úCüôiæ3‡s†Zê­ä˳ßÑ»[6ÓŽƒ†qÆZ6“ §òÜþõR¸¾‰ÊK`hɽ*|PÑÅéT³ÝžJ<òz ŸTù´*KjrE Ej›šÛ_Z$ÕlLy`„h¬Øzè p—e@̤Si'7[¹O†ˆB¾u¢»ÒÉ.m 5–Éieœ˜• qžà†–}úXÃÑa¶ ú¹ñ䯔e„•ò"ȸìîOå^ÖkšZÚõP Ðh=6œ¶3U§€Y_õjê¨òð‡Ç¾~Qæ¨>AžlÕ)ITa1ŒúA,9>b·¯ªÛÕÝ;ÑááÁ <ùý/a++BVéLTABe‚õÅhÜÜ¦ë ¬±>>×å Ò‰.urx(âE‹ò ‡H9?ð-å|Äde‚‹w¨îàÇl±…*’ߥÓH6À–¢—Äø¨ñÄs#?L½ìŽç ƒ¤äs}p^²ÏV_å>ß'À_¼S‚A{Ç_ƒ L}Õ;?ûQ!³bãnÚw¼‰\âÃ,æÞ’(9•¨c˜NÔ·°‘ÅãþÙ'<ÉfUÄAE˜)ŒâÇ )w˜óG²ÞžJ<òyÍPÅ,©eIM¦,A¦Ž¶v´&”~ndRM²Aõp£… Ý貄O]­>aþÚ2º-;©€¿ÆƒZé$…˜8"‰F’JMόۮ®…9SŽÙÆ O+@ÜEYUààÆÓèNE—d!怜"ȸ ©ý:™ ð8ÏÎŽö“|*èƒ\¸rÈüœ±:Dú«Wc¸^ºZépw…ë³VŸ G¶ë”lp/b{ìƒl.4r0•}UñáÇÏúFLUÃâj°Tq “M9×#áÙ 1±õzTä Q[WˆZùÑh´P€-¥Ž+ÎÇ×*üLÝ< m!„*÷; ³þÀ"Š:,èÚw³UúŒŠ~öñ;È©áëmtñC¶d’[—‡ùû˜ÉwJ°³“gx>ï“þê€Ýž}r¸hÝ®Ãt¬©ƒŸ3~6øYÑøY„É’Sùœ$®€êù€GÌÁ÷tò&¬3•0%k$ÂŽM¼Æ1fÅËU{š)=‹%•aT>© ˆ O'\^RYù¥M»Ð¹s&'xi柳F•ýÓÐ@Á’‚.YX{¤_aáéâT!ªˆtR'sÉΨ“gÓà® tYòWhºÉh yjIÎÈÅV¡"¶¸5ј¢ñ):f¢ÊrB^øÕÉF=€#[Á´C»v¬ç|àP(pî^§“u>\›ñ:¥ªW_ÝÙE­]jcK}0ÊõNï0É—ÕÒ˜ ˆ¹ªSF£dÌ0…mSoC»Îtu±ûLÙšŠ?OÚ Ø Bk`¹D+XU‘O˜ŸAÛ®îë=žóµ†Ï+÷60@Ìw9ü‡o¹ìnïîÊgË)žø›ŠPRÜ¥}‰€I*H±´¨b ßZ£g‘Ö3eê?Œß'(WùN9¼w÷jÞNï“Ø3ÐGìg_crºýP-hîd7<~rv·%SjFÒæÎ£õQŽ©¤ž˜þáîwâ Î˸_&Þ}AýÇ;EF›Ö,{cïâb>kø}(§Dë@®ŸýTvm Ú¦vòòóácr ª‹?Ò¦ŽM—ÌŸIãF%..¾,íª®ÚÓx<òxßì¦v(ŸÔ„‹¶A¢N;úøfMû⮃ÇiÚøQ gÐÛ‰1t’—»ïõBƒx‚û¡ñB×Fü$ûªÂÔÝHF1TS:dU6Ц_žžØ áƒÊlI÷èã˜5ñQǽé,¿Í»kwïz•±X,’¨ôåÉCs•:¨«^ F2&IT ÿ°N‚¨Jr â)ºûŇ |Τ?ªA™°’v[R; »¨ƒ‰j“Tø¶âù3¬©1XÜ[b k(zàkаX°æbm¸Ç£õñ\ã|<ãÆÚ°ÞfªzKY†ãûÊwÊÑûžã]¼S†Óû˜õ1×u ·g?Èv{ÕÓñ†6îÒgö¿IA>q,]rÎ,šÀ3Eá¹ÈT¬g 7ýsÙžf ?;äïǘO*F Ú4eÆá23ÊÉ/q¼ðÂËž{úñ›îþ­/~¯üwÞàð¸Ó{ÀðP¡2#ÉFO4xMÅ ^|#‰ÆÖjͦøGÞ–Q#4¬¨F´Rt÷£¡Å‚rBÞtS0!Æ/ ‡[V¼üü?8?8Äb‘ ‹Ä<Ý[Ùõz©_Vꔬz5Xu úÊ–Q|ÈE¸Î¢‹QðÌXŸȉEÇîm£+>©!*À"Z ,±b’IRuóãиŸ +K*JqcÌÏ­ZΩ/¬§Ý½¸g6Ó`•;t¬²Ç½å;…‡õ7¿ý³Ïâ/Ãå}Ì4Xu@–'»¨í:x‚×5‹v¥€ÛLºáá¶eêø1LNgÓDµŸ©öÄT¼{c°õÏu{¯ÿØ7-©ºÖ=Õ†JÙ‰¤X½ð 64Ô¶îXÿþO´¹çüèÑ–ëwÝtqZ­Ž|°ñÀŠmnİF$¬8rð‡TÅ®µ±M¥ç_¶“òÞ˜J »hdñÅ ¿>î²-7º²«?®I7·£uM´vÅÒG;:Z¤_KXdÃ2\,©Y©S(Y¶¹ªW²ZÈûæºNAg$ÔQ, œ¨ÓXƒ°b-œ'ë±Q§}y.È-FåÖX]ŤUt÷w?{¸Iê ¢‹{É=ék*Éi¦žã®ýÿ•2åªÜ  ™ä½£ìå;eãÊÿ×ÚÊ܇ßûĬ²rUä³ßÖ -{ÒãõülðÀ[î…s±ËFîO7š.š;“ÉiUÆÛSñîÁÒ_Þw0ê<y¾›qJYR.JÙ5 Õõö+Ï//./„·ï¢§I¿ý†…Z:UTn4t²’c[ZSÑ͈)ߌ.K¶ ±%•›Û nÂZXNĽÄƒ?ðåg4´†_^n ¦™Ô!o k“5Ûöi{¶n|fÝò·Öñoˆ‘Š£ø€/¬‹Ã…¤B׬Ô)ÎW”o.ëÕ`Ô)èÙ[’²à7~œD µ&œŸpŽÕ"-±rP£$º2¬ABì.#Ÿ#`.Ýhz»Gü=3½{æ²Ü!¿‰Gß'¸¯õr`ç¶ÇW-ym%ŽïÀa¦\Ö¿~ÇÚ}¸N´M…E8¿ûcqˆºðì¢[?Ó퉩l/¹Ô·¬úß‹êCàPlz@vgV£û,Q´n <Z¼Û_þË¿ú–Û`ñ½ýX}£~ËUç92ᣊʎVø¥ rÊ]•¢±dKÚYXR‘â]q0Á?¸’´$¡Q ¿\ ÑèfÊ 1tñºgËÆçÞ|öÉÅ|ûv^`õÀ ðÎ=Ù‚)'u ¸É2Ìv½ÊuJ¶NHùú»ç$Ó «xÆ`‰•½ÖgçZàkÝïï>¹ø-Wå] 7R®Þ'¸—õ²oû–¿½þÔèæ®ï@rJÊfhní U[öÒŽÇE[„ûžþ7‰»ó/ulÊqTQÙFÒˆá«f|#)J¹N¥åC…µ\ð@[-@òœTòÇ5ˆYˆ0q‹ARÑp¨uͲ%ox÷m„œ†­Ýkl[-©Ã‰¤f½N1¶¢Œ³]¯d}‘õ ëL×)è’‹$uÀ½ú²ÄJ9¤ÞrßNkÈ–ír‡¾‰ÖÙ(ûÞÞ)ëV,ûÃÚåKaAÎï“>«]¦ë@“Ów9ÿÖ}<³ÄÁ×”+€¸ÿdr:¶º<+åß§’ýüiý{»\²Qÿ{»÷;¦Q‰4SiNj±«~v#©À æKøÂwìó«9W/ymùÖU+·_pÍu7p8¥+˜Œ•r%Õ‹ ¼zYi¡æssàÑ ¤xB¿ŸÊ-äƒ%¯ß—Ç“]cêXÌÌ…@ý,§‰„[÷oߺìÝW_^ÒÙÙ.’fÎ *!ð®Àw8tõ³š" j‚ñõ(~ß3ñ¿ñu(~?ñœÔ™ÙD ¾œã÷S¹w|YÇï§’§¼¦·wÊÛß|wñKÿèèhkâóÔûD‚ÕÇåä en-wëvoY4¶´ÓŠ »hóßZÄ8•çÅá—÷’¿Ûa-eJUÿt@þ2É{É}µNIªLQ‡"©‹ÖVk*žrxº‰ZÙÞÞ}íïÿ÷_:í¬yÓÇO›1§À_4ÂSà+eÇqž‡6‘÷Ð.§„#á@ ³³¹³­­’í;7­Ûϲ¡+ÖRRU4*h\°®~é:¬¨¬®HªNI$ÔZ!ÐüNéâwJKg{[ýÁ;¶îظv/Ÿ®Þ'ý`6ÐO‰)ÓåëwšäÔz]<9èžvúݪ‡äö²X,©S‘Ô$+„Õò%?›¬D#°}ýûxÙÎù K+¯AheÜ&yM’·Í«Ó%ÉVX¤//,¥ £èÚǂʂ:\­¨¬ºHªNI$ÔZ!p*ê}r*&99ON­7ÍgrjÕCmÛ‹%Õõ2Om°_²cw¿D ¤VA$¼@å>H¾ì xATè‹+Hêp ¨¬¦HTtáK+*jH)HjG÷Üð;p®IÖ!è/±&ªN סô¶" Ÿ õ>±¢’¥mEN³¬Ê6!¸ß¹Hú¤^2ö 6d=™Ðu¹>ÉÎ$U¾0A"¬Û  ^ ¨F+¦ðåué¬tRä4]Õõé"°ä¡ª¢Ž¶“"h »ø¶k7?‰gÞ–ÉÎ$€á¥ ð¬/Oiù‚ÕJP¥ï*ÈéP&¨¬žHÀD.ÀÈÚ°#S¬±à·álAeõÍ¤ê” …ÚP˜Èw‰|>ÔûÄ„&3ŠœfG•Kú„ÚÚÍASüÀÃжÉî$U‚…—'Ö°‚€É.~SIP‡«Oªµa‘ Ö’œâw•z" êTO<ÔÞðF@¾#äs!ßê}’f½Pä4MÕåG€§+*–™jº}ÃOAÆ|!©U1ITa!”ƒ¥¤õTZPå× Õ$¬ålä" Uý3¡0’D^úôç:• LUù‰ž$ùÞÀZ¾K°–ÇqŽJ  Èi ©Sg´D´|¸»¦Hj¦ A¾,ñâD’ÕØ]ýRW`!“ÄEî«uâHìTJ3uæÐC@½O2P¦ŠœfD•Evˆèfw?ßȶá§B>YRû*4I0úú]W$‹€ªSÉ"¦ÎW s9æ ŸÔw0I•&ERó©ä”¬ …€B@! HENÇJiÈßL[R­(æ¶Ø –T[©„P( Ⴠ"§Ã§¬‡š¦zÔÒݯN µâUú( …ÀpE Yrªë:ÏØ³TY·SÁӌʔSŽwýeYÙjm™U œ²UÉ(a …€B@!<‰’S²h4JŽž¤-{PCs;5µuP0ŠuªÆ8kr‚tóS¯ÇE¥…~ª(-¤Y“ÇÒ„Ñ•äp8ȤU’Òáªr:Hg[¦Då*¥â¤R1¨Û* …€B -’!§@Þ\½–¬ÙF-íL9ÜŒÎ#ê<ïLTLð“–,æÅžLPk ] ÐKË7R±¿€ÍŸA—Ì›I^¯Ë$¬æù9Ø9‡þo²þ­¬?bï„5u9|ñ(sú;yžy_´‘\z—п¨°€.7¸úçâLÞÂŒ“Ê_Njt&‘Uy) …€B Û$JN!,§›w¦?¿ò5³ÕÔ­$wxi‘ &©˜1;Iר:ël öè z~ÙzzëýtËU hÎÔqät:YÍÎ{æ*õÿëßÂú7ºGÒ ÿ,ªçuXËžþ.=D•¡42xXè¿tíNºõÊsr®O4òb/6pÊSöMjà”}ËFI¦P(9F r*»ö__µ…ž]²ŽÉi!¹góº,'Rƒká‘äà%êh¢V}ýæ™etÃEgÐe f“Ëå̪U5^ÿvg1í,ºš]#r¢?ð OXJÃ'iz禜êŸ%³rËÀ©¨²¤fb•©B@! P(2…@2ä÷4 ÚÊ-ôÌ’µäŒT“30“{¸åìÜ™’,±|@ŒµÎyñnc«âáÿzÙ9³ÈíÎN÷¼þ'Ýci›ÿlŠj™ëÖOLsã,ã÷‹>@3;ÖåDÿdd³á¹Ê’jÃBQ") …€B É’Sy1º¸7í:DÏ.]'ª+0[þ4hkd!»Á¾øöFª*+¢3¦DÕéÌ,y´ê‚º¥pþ é-o ‚ 9f·½eýå=órm8¥«îþ¼,C%´B@! P ]R%§@­«+D/^)ºøaAµS‚<º£ƒž|ý}š2¶š0°#ÿ Iêÿ—WWºøaAµS‚<óÚZYÿµYÑßNº¦$‹5•Ë¡|RSQ]¤P( #9…(èæŽD¢<‚}+5óvø V_Ð@g`µjëhéûÛéÊóf›¾©Ö8«}]ßßq«þ$ÔÁêâïKNȳ³àtò·­È¸þ}Ý3¯Žëd:MG]îf¢€mÅÏÌg•mÕS‚) …€B€äô…eëèá§–Ð&‰²%Ód¶6~úú…<2þ\[].÷º†1Ó[<’£øs5HªWaú9¹ ß;÷R 2Cöt“Ô#é1Š?Wƒ¤’•rA¾›2«²rØí|]ÀÁ5_Tr6®ë׌º¦Án2ZåQ£û­h¨m…€B@! RxÈ™)ýe¾y½ŽFc$U§:»ë¢HªÝKHɧP("rŒêÿŒèŸà ¡N]D 5.shºíº îÖïëfѨNa&©0%¦:íëD[÷R wÍcÊRÈRMVý1Õi>¤.GæôÏ}’‘Ÿ‹ØL š"©á¥~W( …@ÊXÉ©°œZr ‡ÂKëšGNª5s Q–ËzÝDW·ìîÇ*³qG{½i&²œ!Tì+°Ô#Ù.yò€þÊý#äbý»ÒÖ?Ea‹¿šõ,à%6ýcšŠKK¢à§âÈÉ“×Äv…ØRT-©1ýóDy3úç¶Hª[HªC œ-õ³B@! P(F Arú^wN¦Uuàœ?C¼ü²£²~ §øÇ KªI^Ÿz©Þ9Íë2¤šRØãr+I¢»ßÞI œ²wù(é …À°F Iršu¬Ò!yY®€TJ‚ÙÇ) îú' ”MOÔ58%¿24ERmZLJ,…€B@! °3v#§vÆJɦH«%Õ©Hj¸© …€B@!`WršľjS*º¤rM_÷ìãCI—d±ä©P«L·‹»ûCÉf‘ÓóUwNáV7Ë0"Â:*º'*(1S@@v‚áRÑCšBê’°+9µªr“¯GÊžŽü2+&ù²-eOGÿ|ѵ?9¹ ›qR ŠGòŒSûû;}ÐS$uЋ@ `)ý§¯?0_shW1=@m´¦S± dS"ä]£VŠêÇ8¤ÑÁh$òêïúý÷ùöˆÙ£Hk–Ê!Èi–TWÙ*rŠÀk¿./ 65ypSnôÚݹ¿+§¤p3ERSM]2dÐ>ûÙ "Ú½.·ó_x6•Q–E/öû¢e%~Íçv«8ÂC¦¨S¤+Š6µtè­]‡Ëù½»ÿýÇCÁðÿœÜvð¡çŸ¤ƒs‘„5± ÕY}" ÈiŸÐ¨YA ÒÞaÆHårÛìФf¥*¨LóÇgîÿÎUûwz4:fÖä±ú9³'ÓéSÇj~Ÿ7+ákò%"‘ø0éè ЦÝGhõ–½#7ï>ü£ÑgL¾ï¶)÷ßýØ/~¼˜AŠð’zDôa޲"§Ã¼(õ ¨5»ú¹oH‘ÔA+ uc…@ß kßq×7¿÷/šæøÉèªRýãWžKÓÆR~§}c6ì~á:wÎd,Ú®ƒÇé‰WWVÑõgïøú·¿ù‡Ÿü࿞SYU“©Šœ&ƒ–:W!¢QӒʹ+’šˆU– tõŸþí/3A}pþÌIúí7,dcª2œ¦êP¿–?`èþ;®w>úÂr}Ͷ}ÿuÇ×¾¥ýáÁ‚¨bX¬êþ (r:@êg…@®°†Ÿ1R­cEs%Dr÷QÝýÉá¥ÎÎoŸþê7¯v8œ?A½ë¦‹•õ4¿Ë3gÒãCFÔ—§ Dõ‡·|áË;žx诰 ªèþW)ENãQ» ÁF@×MK*7~<²ßþI‘Tû—‘’038®¹æ“…>Ÿÿ·cªË„53Ùª\†ly׎žlÔkô«3θàÌßifýaŽP>ªÝA‘ÓáôD(]ó Ž‘*ÞV†Ðª»?¯ O ;”€ÅÔY={Ò½Q] TÕÅ?”‹;{º¡ÞÜzõyŽŸ=¶xÔé‹.ú“ÔŸòÝd—¿ýûβ )ršEpUÖ L `‰‘ªçÁ”¨PYYR3Qð*»#€Û.¯Ïwïì)cu5HÊîÅeoùà£:gj¾eÏáϱ¤¿äE¢–ÝþŠœÚ»¾*é]ÓGš–T5º_墅 AE@XQ?öÏ÷.à™FF"Ì”J tàz¤qhªêk>~û9¯üõÑw8?–jØXS9M·©ë¹E€'¨+_PNrÍwzeIÍmQwË=‚¤ú‹‹¯@ ~ÄAͽêŽC ®G„úT6bÄå¬Ûj^‚¼À¢*ÛÞši8“S.s”;¹]Äâ΃¤EÈít¹¥ü©J-¯‡þNQÝSÍ)w×¹ôpÆôÏÔÙ¹¿œjdΚæ=lŒû”Gì¹V$՞墤ÊèêçFÇÌ$¥õgØáœâ¨xõö×xÆÓ bzAÔµ!;€j8“S.WAò°F*ô¡ÈÛŶíÿhòy|=ÄÙL6Y¯þ¾(Æ Ú?y£]¬¿·‡ V]zü0„wÞùyMAcèHy·ŠÁ+¿ÚZG_K¾ä"ERs¸º_®þ¨šCÍSb;« 3ù<æÄ©=ü`˜¼žüx,Õ5ÑÚèÚ ÏèÑ€§ ðŽÇiü¨ *ð‚ôL[÷¡“Mmô¹3zþ0ˆ{e¥…Zm}ÃH£Ð²^¿CÝáNN{üÄï%Ý$] ‘¦»{;ÅÇ _T R¡§(£ò@—ÞÅKˆÂš}õ‡|. °þ…Õ?3k ×™VTÒèõ¼èõÉÖ0k„’Ù€)bq:4­Èçv§ÆÐ$Õé•w6Ò’ÕÛ¨­£‹œÜµ6uÜHúä5çSuE ½¸l=-}=ø/·ô s?øÝsTS]Aw|ð"zgÃ.zcÕV:RÛHeÅ…tÖŒñtËUçšw>~²™øõ3æ¾uã¾[¯ žÖÕz('Û‡kè…·ÖÑUçÏ!—³ç„ßzè)ªojrÀêXS]Ng}Æòº·‰Dé—O¼FŸ¾a!3kÒ)§€¤nÝ{ÌV$õÉ¡9Ðúã= d}Ë‹—ÿ) ÇPä4î]Xá&Œ*§w·$ÝÙ@Zß)öLiTy¡ÙÝŸ®¤Vý+C'è„'Æ}ÒÍ;Ó×C>8àdRÿL˘«ü¢zÄ,(¦§Grußtï£Hjºªë펬[N~Qe… Bùß^O¯¾»‰>zÙ|:cúxjn뤿ýcýô±Wè÷|„æÍœ(ÎÙuðMŸ0JàUרB‡O4Ò‡.žG`ˆþöÚ*ºtþLºû#—Ò¡õ`Õâ<ùgDY}ãÎëÅî Lz[Ú;˜_ öGU–ÊÓlµ¾ðÌi´èœYTÛÐBÏ,yŸžk-}þc—õ*#ˆýÏ¿r+‡Ë·W’Ž‚AÅ2$,©ŠœöZEÅA4,Õü<ú¸Ç#=Á®©ö%©× ò°ÿheI¿ Ù!ªÉª¿×㦑¡Ã¶&©þ³™Ò?UÜìqn’Tþž>œ/îóùÖ"Ø£¬•ù‚ÞÆbá‘ý©¿™ûÑ6Óë+·°…ï4AÈpjeiÝuãèßõ-_¿‹.=g&QJëv4I*¶Ñ­=kòAJ»!BäêŠb±ÄßÒÅ/Ú‰cFˆÃ…^ †ÂæþªÍ{è…eèû÷Ü$~‡ËÁ÷}êº hæ¤1ô—WÞ¥ –é([i7³ErÊØ*ºî¢3ùzŽë¬ëôm¶z~ôòl Þ@-í4ï´‰ô¡EsÍn÷w7—oVbÈ ±Ÿe(ú¨fd¹Xö­£w8$yÏ»?ºˆž~c ±?üâGé{¿~–>vÅaEnëО_F»×Ò„Ñ#¨º¼¸Çí`y^¾n'Áš[TàÄáÚ…gÐ…gM§Tåíqƒwºë•ÕŠŠz–—–TENû/tIÐÀñðQ5³¦‚Öí­¥¨£‰Ѳþ/„_!WÔQO* ÉÁ=²K=’I^'õŸUSN½'¨4|’š]ÆÁ..@IDAT»)Ù<³y>ä*gKêÄêÌèŸMYs‘7‡Ÿk¾™4b’šiH|ùçÔJÊAB¤KVü$AgNÝ#ÿeÅTÅÄ Ä‰§ae’º_lãϺíèÌéãDÃ1at•èâÿõÓKˆcošç$ºÑÅ~¬õÍF×:®aÞÉûËÛÙÒÞEϲ%¾®·3qm`"úÒò fö8÷ϯ¬ sçLaËî\z›É߆‡ÄïÍ­ôÇ–Ó³§Ñç>v©pGXÆ¿'’Ž×·ÐzöY…•yå–½tÉüÓÌËpχþú“]ÝÄh$ 0–Ho¬ÚB{ŽÔч/™+ˆý{›öŠãøÓÑ`yߥ+ÙÍàûl©#„{j:òš7H~CÖ±¬Õ³äEJü Óüêÿ FômzT¿‰7H·Hl{{UsÐùßþâç¯þö=÷¼'×µÃáàg„ÏI³&T‘'w{vr´×x9ȹr¥ÜÐ!Õ¯¿—{>¦un"‡n¯ÁgZÇ&1ª?“ú§Š›M®3-©üÎL¾¡$%”%u€W·Í)Y#-ܵTäï9zÇ ÙÂ×ÌÝòHsgN]þ°(–³Ïé>&`×^x¦ø –‰»ó:ú“Á_>ñº°~ ‹l¦üB?y­áp²©ž[ºVY™ÿY3&Ðe f‰Ýl¥ÜÆ×óNŸB›v¦Š’Bš{Úñ±ûê N——ö¹†)ôliïÖá9SzúÍNb‹îgoZ$®‡uÕš¶ï?*|S/a¤Ýì*7 ±ÍÖUø°Âîc«ÊD)yE©ýAýÊZKM¤¯R–Ó1²ž!-‰ {.—‹ ø™:{b½»«Ž"žmä ζž>¨Ûï6ØÕNÓFøÈçs“‡sBfÈ.õHV@yUÿ¹Ù7—õŸÙ±Ž¶›Éæ›óOëXKþh M©ÎœþÙ3—yF)ÖÝÏÎôÊ'5—à«{)  rBjeke|B—5 !©*'øŽnànþR&}²«_^SZì§/}âJZ³u=ùÚjúíÓKMTyN¢k¸ø„Qó2ñÈ\¸)€‚ #¡K]&ÕeZ4·í;*ÈáyIþœ0ƒ›Ã‡>·Ðég½J?ÿ×[ÌAVs8Öho Ñ«§óϘfþ<~T¥  80‰Ý€ßß__Í®´ëÐ º÷–+ĹéÈkÞlˆo #rŠ/^27ŠQ;0HÐÃþ˜^®ƒÁŽj<¢SùØZQkœÁ™üµ’º¥2ݪ *jÄYK5ÅYVÈ=(!+d†ì©Flñú«*¥“Íü!^{„f·ë´Í?—£ ˜†øtÕIúzXPAP«BGió‰0µpä…3¦fNÿ^ê®c¢C§ç—v/'ò!Ó’Jšƒ-©ö²~÷…²¤ö…Œ:>Ⱥu«’Ÿ×-ºéÑ}/ apÔåçÖIǪ »²…´Øìê—çËõ|ÙÞÜÖÅDu¥°Âl „AV œápDtëÁO3> Öª¯K>nˆË™Tÿà ‰Ï2á} °!]¾~§p„ØÍ–ÞBDE¨gW ™Ø@&øº.)®³^çR”‹†9D&aˆè‘Ž®Pûi—ìîá ]´ìÝ.êlöP4ÚA•ãjy´;9ÓÅG>¨Âõ€-¨£ü:+/ îÍñañz±†4ÝýÐy"ïécÊ( 5¥¹m­´«àŒAñQ…*ºøaAÝ|<@ûÚ¼t ì  šÜtõ¸Ìè/Ÿ½®`H‡ÃF׎z©°lÊL#¶”b”¼L¡p”sœÕõÍ‚Èa–L$ÏéoˆrÁQçnut³K7„þ®Kä·3¦£ñà¥ç9ê“& Ô•LÊ!©–Ï9Sj˜TžÃ~|aõèÞ—Ì;­ÓGè?yAø³Â?vëÞ£â2¸3L]IåÈ÷€ñ¼ÿ×Q:ò$S¾ý>ÜÉiwyLmí hˆzßìt’AÒ´ì½:v‚§Gup}ÑÕ‹Š9ÔS•úZépS€‚õbÒˆ¤é<×/šž¹æVר›ƒôë¼ !Æ^…+DU&Ž>&§…T\\D…E…bÛÇz{I3¦G¥ðGê¼'îyfˆÆpON¡ËÐßgêb4¾“MÎFß~æC1+¬Ìçêæ+ð[–G’û9õdM |Xdzø[wÈM±¾çæËzì_Ã$ ün xH8Bfaé-ý°÷`ÏøcÙ…ZÀÄÖš}é1¨ Ýüpc8É“|÷ág‰=võ%¯5¡¼­È©Yº‚<œ8|pyIeå—6í>Â,z¯¯æ lœlˆÐÒåíÖÉ-Hꢅ…4mr˜ššŒçÏ_ugµvE¨{TƒÑEôÁd=“…´’ñjÑÅ .-JE. xžtÁú âXÊ.E%%¼€¨²µ~©8•NW¿y /䉼ÑÕ‰DøcÑð…ïUÿh{öôw÷®ÿW..¦ï¯búÎÕµuziO”>2;±Þ)©kokÔ#îÍÑíÚ±ž‡Ò’¤¦Y²½Ý-3ÇѰéÊõïpfrÍM.Фægu—a‚@<¡JFm‡³I'Iò˜Ný] ‹h®S_x¸alb‰07Žë8ÌHî¨Ê’â †¼=ÈñŽ"§=7‰ÃÒŸÞ4õŒ³êWoÙ[Á$µ÷¯Ç—ö¿ó·ç¸{=ŠA7¯qÑe(¦ÖVÃgÚ pN õ]TÂ!ÕØwQ88¸ªd"¡¾ã^’0ºÙ7ÛçE÷{° ‚ –•–ŠmWŒîÏ„²ãÞÈ yFYT©–ôŸÈúßqÒ;]¤qYýì½.ºlJ±«nZ‰ë‘DZ6­~w7g„ÑGXÌú–VæYºØÁ1R#±ª§Hj–pVÙ* Ž0—^o3=òÜ2ARG(£ýÔÕÂw8£ÈiŸ¥+— '}|³¦}q×ÁãìŠcÌúÖçUýü°rmíÞ`¢k"Ñ­AEEF” X8ᯊ®p/*êdkj `Kc˜I­ai„k Rªd ƒ A&ÄLPq?ÊÊ>¨ÜÅ ªèî7­¨é ˜7µü‘dÔËn z¡¡“õ¿ç?-¯ë¢£íNj hôãô_—Y,ê$¼‰ú³y÷aíàî]¯òEè2Ç"‰ê©aUÎ9»'òg„iIåÏ &©1ÆšÝ;§Ÿ»²¤¦¡ÊA! È!ˆÛzó•çæðŽö¼•"§ý– Za, áeÏ=ýøMwáÖÇ¿Wþ;op¤ÒkÑÞ¥g_‰•¹ta)«AwwXXAÜ0 ¡©¼ì¯ÙÅ$µ«›¤Âš _êLXS¥ÖLt»VTc¤=ºßÑÝï÷ˆ]òø²e:úBÊdGý¿³ÈCŸ{ɈWýÜŽ Ý8ÓKçŽMžú`r®?Ñh8ܲâåçÿÁxÂ!‹$ª²ÎeêôóÓÉ$©ü1‘71R¡xò%•>\*…€B@! HEN–-ˆ`CCmëŽõïÿD›{Î}a¹~×M'ÝíÿÜâFêè0Œe•å.ºæ²2aÑ $[êéá™Ô AƒŠ‚AŽ»ˆ{TÙEž¨6ÝFT“cªSÌ$…nwc„„‚5š@Nþ†UZ`½W"çň©½õÿÀD];=B/ï2™=°´ž¿µ„Üm•DB½9Z×DkW,}´£Cøwð¨-ŒÜ2‰ªm-©lºŸ Uåq ‡äv>¬I͇RR2*ÃEN“® °¤‚t½ýÊóË‹ËËáí»èiÒo¿a¡–¨Eõ؉ ­Z‹Õû±V0!5¬“èâ—„ ÛÒš kçVÔˆ°¤êlÜEW:Ýý¸— ‰ûÁšŠAS È ¦ˆ? `i͆•ñ3dÉý¿¹°€Þ>¢Ö€Nû›¢ôä– }âôÄ"=À‚ ‚ºfÛ>mÏÖϬ[ΡNˆ# fqAýB=³/I%šÂò‰¤¹{ qå{¯Iµwù(é)!ðGžbµ°À+Â>¥”ºÈ6(ršrQÈî~D-¡hù/|üê[nC»wû±úFý–«Îs$â£úòëÍL, 9æœV@3§÷}2hUž‘ ¤‘I¢ §’ )Œû>° ‰ˆ‚ûŠínr yr•쮥ßA÷Ì/>©Àäá÷»è#3ÙÀ²ý$ø ¢‹Ô=[6>÷æ³O.æÓÛyÁ Ö¨W¨_ ©Ý5„·ì–4š,¥óF}{ó%?`T$Õn•IɣȆï[jö¿yz ÝpÑÙ4ºª,’¨,RE@‘ÓT‘3¯“$UXRù(ˆ…wñýõœEW‹.¸àó?{lqÙœ©5:‡RÓNçPp½ÅQ=t$@·>Èùº+Œ©Ž±mMÒª†nxX61PJú¡Z-¨é’TÜK.¸ŸH%YåÊÕ¶ÝõÿÄéúý†.ªm‹Rûÿys€>sÖ©Ka¦0Šƒ¤¢áPëšeKßð.ÏØbÔ¡Öî5ê“Õ’jK’úòÿUéíŨ\mZ}µñ]ó&)’š7E¥U$‡º©žXüur°íËÎMãGUr¬ÓNzyùF:‡ãEþƒgºêüÓŬM2çekwІ‡8>iHœÿy3è©7VÓ]¾D4„Ûö¥mT“ `¦¬#lañ Ýt²9?&=‡Ð*õ@@×v‘§¦z\ÐYèhña_CMÚ&œŽ„ÎÎæÎ¶¶†;·oß¹iÝ~>Œ²†µ„Dçc]ýÒÕ–VT–ôht2Ö"i´WnæËZ‘Ô|))%§B I05볦‹«V¬ßEÛ÷§š‘e‚„Þxé<:mâh1ËÕÚíÄ9£y–'S¤ñ£Fð¹åtøDƒØïëi|ôòÂʺžóÙuà8)’ÚZ½Wä´w\2xÔjM•fH+y l_ÿþ^¶ó=…¥•× ´Ž;þéï÷Q÷uµ;ÞûÛŸ¿ûç Ê¥²ªotŸSvÁÇ?[/œQðîCÿúÃpÃ1Q$Ô,ð1A…¥d]ûX`AAÍ+* ª‘>%Æ 5¶¤ÆöøgÛ'ERm_DJ@…@jø8,L^7µqÜF™&p×?R€Ãã¼·q·ØžÏÖÓêòž³6‰øF%#Á]ÀšªÊ‹AÅ1øàÁ «Rb(ršN: SZ@Q™å>H Œ„‚ƒ"H*ÚEçi³®WT2josÒ#«Vüö ÞˆJÇÕŸ|C þ¥_+{Ýš¯h ÏDUP}ý׿}ô+¯³²^€ ¢ _ZQñÒ)I…s2Ôünÿ^Ô2²Ÿ@Ró+)’š_奤U$ŒÀn™ÚÌS‡¶u£SgM-¦1´fPÌÓ(Þsóeæ!Œ`.`rÛÒŽ÷p9U²;BÙì=\ÇVÖ Ú²çˆy.6âÜãzü¦vzG@‘ÓÞqÉòQI@@,¬Û ¨ì ¨ˆI„/;ARç-øÔ­¼-R[kÝŠýûßÛ&÷Õ:ˆð‡yçÁÍOù§Ÿw/´(œqÁ"^ý…Y/$IEW¾üˆQÅÇ öq\ú¡â[']ÓÍ‘ýì¿»×ÖÂö"œ"©½€¢)†3¸;ÿ‘çߦ#µ tÝEg2á,¦ºFôVõ@:Í?~ýÔRÂt£ß¸ó:º|ÁlúÕ“o‹CÝÌ=m‚ðgí;õK_(rÚ29;Bb%# ­ ° šuܸ³9¤êøËù˜HµÇ·ü7Ž{êo¾#P¿ô×.œvîgtM+Ô<þ‰coûI͑Ǿ¾šõ’uÄJTQG@L±Æ‚ßìoAe!‘ø•nv÷s€´=¿6~ˇ¿Š¤æC))I"pç‡>`^.xÄQDªâîü‡¿u‡ØîëÏ/™KW_x&_c¸ïÁõ†‹ÏyXGÒ^Èþ®Xdºíº ä¦Z[PäÔ†=6A0$Q…E DXOyUÎË®þyCúu=²óåÿýU>®ÒA s×ûõÑ`Û“š·ø¨ä?íbŒE˺"*Ô¹-çCöOïü¼¦ )|d4$å·yøÊ 7$zÒþ‚[$T$Õ†ÚT E$AMF·ø™x`EU)99M¯Ÿ--f ÒÿP –â}­¨¨òv)O(Øõ0o÷ß!OVë¼A Òrò!W•ARž‚kÆÝûÿ¡_ÞqŒ@Ý1¨’˜âX^¥¦hí$vá2¬ Dûµ›ŸÙΫ¤Hj^—V! °;ŠœÚ½„zÈg%$øA»ï+oÕµYbGÓZ)Üüo÷1ˆUÊköþäCëfütÓRމz W—üÙ·°B?êVJÖ‹¼Ö‘ÇûM1ÈÃðS]‘T³Õ†B@! H{ï¿Jõø _ Fô[¹áëazæù^%‡þÀ·ïùü{©ßA]™8:ªë&sT¢'~ó››S¥!ˆ€æpüVD.1TÓÐåÿᥦ#©¤í1 Äù¥¡"©ùU^JZ…€BÀf\~ÝuãÏ¿ìòïx½¾ANe¸.ˆ©È©Í +qô¨~£<Í¡iOÉmµz”ºõúç9Eü®î¤È©D"¿Ö÷~e噺š,¤Ö´æYÓ=oæ—JÚdXyß´–énz¯¹×Eµ(>PþÛC"YÃO9ò/üÊ@‘Ô!Q•vEàà±zÚ²÷Õ7·QÇ, ò´¤H1:“šäð„÷xÜTVäç©H iö”?ÚПZŽÙ¹j(êߨÒNË×ï¤@8L……å «2)r*‘ÈÓµá®~Cv~Æ^¼ûîùÆ›§ê(±@@Óža÷ARu}ˆ‘T?CÀÉ–Ô¼7¥Hj¬Õ–B 3„Ãzsõ6zcÕ&jn pÀ{|Z'yµf~à’13)̱Ç÷è¥Ô¥Ðso­£Ò"/]º`]:¦˜ý)3wI>©ÿë«¶PK[§øïr8Isj<פhš|¾ñWD™MèÂшп¤¨€.[0+kúKrº™'4°SÈ…—¶¶4þÇÏ¿ûÝe¼›-A<¸ÃtŸËÕìê'Íñô0…aX©]èñ>×ì|˜_'NÒµ…g<¸»zãצֿ;\—µWtL”zøü¥{‰êänÞ¬•%5oŠJ šlÙs˜þôÒrjlí¢‘®Ý4µ`¯wGÃd%ÙIAÝG'Â3èp×™ôÌ›ZÊäð“×](¬«Ö¸¦Ù¹{Ï\¡ÿc/½#¬Æ˜–3Vø<ľ}=OÌà^”-™]Ajï ²þïÓ’UÛèSן1ýû#§ÖmÞÿü3úÓVI…)Ê`¹æ:«/~uå$Š„NÇ}ù¹é¬,v/ε ê~¹G`Ý}Óêf<¸i9ò^Ì}\Ž€£óz–ò:½ù ÿ~5b5˜ê}¡Óºæ]R$5ïŠL lW^_¹…žzc5•8ki¡ÿá:QA€Ç¹7ˆådxmê¼ú[Ýté|aYttò϶0†þk1VGV–—Ýr‘@€ ¼b °;EcKëÿ&ë?/-ýû#§“ÇVÓáõ´ëhí>&¨s¡§ºGvТ‘‹¤ã¯—<ðÀ|L—ªÒ0@@sЋz”˜¤rÒi!ÿÍ{’Œ†& }ðG£ýævžm(’šg¦Äµèú}ƒ­—} um¡³ ž&—68®l Æ9Më:obÂÌøÍ‹iMAT³eUµêï÷y©‚­§Ý“Uå¼°@Œ«+K©¡©õ_“’þ‘ӋΞNc«Ëéç)C[Î 8‹7äÇø|™=»è¬Ûj=,xÇÔRׇÆÔyº>QêÄ=ÿûÒ !sËíZ‘ÔÜâ­î6ÄAC7ê9þ¿º† ÈBŽŽÓ³KˆFòT¨§O—¢Óÿ}AQ^4èúƒ CŽ“Äú¯KXÿDÉé +¨È E/` ªHìFþnVn¢2µ%“ܾ÷÷ºCQiÚôÙ?ßR±å+³l)l‚B±û‚iIeK1“ÔüLФæg¹)©m‚Fëÿ™}PÑÅ ªäi먢'¿KSkFRA‡œžÞúÿ‰}PÑÅ ªä©­o¦Ç_]Ù¯þŠœÚ©ÔG–¯}yqgX›ÓÄ?âq¯IÔ]WþÓk¹«ÿ<®ZTœÇr¼<²dìžQŠ‘TÊ_’йŠUR(R@ ŠQü<‚ÿtÏ ƒÖÅß—è°¨žî}šÚôæš­„Q÷9SIêßÄ#øËŠýƒÖÅß—>°¨–—ø9ÂBg¯úƒœ¾°l=üÔÚ´ûpûð9ýôõ é–«Î]û}ÝCt… ¸W@´‡ì³ñ§?=³}hh¦´H.|Ózjùßå¯Å,©©b¢8Øíl¾oÖ!~QÈÙVªf?´Å^NöIEpÍ{ ¨N\Âoú£×Þ·[ê•D.ö8U‘T{”ƒ’"0HZDXç0“T6õgÈçÕ:ÄÔ¬!ARc$-•ü­ú;y&©lêOE¾øk Ÿƒ‡·†ÂQŠFà—«“"§ñ( ÷}m’DÀ­¹¹kT¥á†»@ql€Q85?\ò ‹…ÍúÌ:íÏ7ù­òªî~+j[!èêŽ0ÙijëS&pÉ Ÿâã)Y›Û; £ñ}^cUªAþ­úÃu ’“§dåF‡ÆV•ÑeçA“Ç$—K½þò¡ì²-ãý÷¯)mvU÷Ñ:|pÁñlßSåoO¸ƒi/Kv¤ÓúžŸuXc¤’Æ]©÷œ vI©·ô`—€ºÞ!K\˜Ij &åG/Š‹‚LP¤Bvèj²êow+ªÔ–ÔŠâ"ºæ‚Ó©¬¬8-ýežj=4è-3]YQ‡F±¦¤…CÓ÷Å^Ó™RfƒxQ©zÌ:<ˆ"¥|kÕÝŸ2têÂሺºew7Öù”tëpXX¥ÉÊ/¯ƒ%9ßôw9iëŸ,^ê|û#à°ø£r¿,i* S¢š¥ü5²|¼ä M0%Ö´ýævn(’š‡…¦D\dww¾‘4È r‰:¤š†»þ©â¦®³'üí6QJ¦“¦Hªc8®î~CsÝg4ϰàÙªM‘tÌÜÎà ERó°Ð”ȃ‡€´$ #j¾YRÙ/ ±]!¶Ô#Y$åuÃUÿdñRçÛ-S.¥dë:¹­ÖÃÍé0ËŸ¢”å-UIÙõHL'y,ŸÖФæSi)Ym€IÔl!MB€œâ3L,©&y}ê9¤zç4¯ËþiJ¡.·~ªPŠÄ–T5Ó”c®]–òç×…Y/ò “¤úœN“x硤Hj>–š’yÐH‡ä%+¼Ãé!—'ý} •’`&+Cüù¹Ô?þÞ©îgRÿTeP×ÙÍBFtERíXD9’)êˆ}¤°²¥^äH€LÝFYRK‹+òš¤ªÑý™ª*…@Œ™q#M]ðeZö§K˜1Â/T£éÜO“çÝC+ÿþj<¶ÚÌ¥¢æ|šyÑ÷¨¨rǘvÒë¿9.]|Ûrz÷o7P[ÃNóÜ|Ù˜3¥FL;úðSKa.òûè¦KçSy‘Ÿþç‰5jªËéö랢ҎÇhïáZš7s2ýîÙ¥§ü®(RA€ûL2âÐc$%•¼Ô5ù@Ôá°|¤ÄêE>iµüÇ#Š›©Þ×-sÇü»vä“üñ²*’ˆÚWôƒ@:DÍá¦içv¿÷sAP]ÞR:ëêÿGþÒ‰§Ü±lÔÙ4ÿƒÒ¡-Óæ7¿JíM(h! ètxëLl¿Ak_üÌ)×%s ]R¹FÊ„¸¬Ï›Ao¯Ý)êØªrúÈeó)Ŭ©¶±•}q…y±Xo¾b«k¢ »Ó…gM§Ó§ÖЦ݇ÍsRÙHG—Tî§®±'½‰ªNw*Kª=K)7RyÜí­]A£.ä©%µËÓVÅEbkpmnËÞ]IͶ*ç!ŠÈM*gÌô‘Óᥣ;žȸ½%L<ÛhíKwÑÂO¼Ö­Éó¾@G¶ý¶½õDZ³oíÃtÉ+ÙÂ:ƒÚêwœò{¤ì©È/ó•yÈýD×s&%ÏPµiA.«+Jhåæ½lK&:wÎ3›`(L‡kÌý™Ç“ÃG­ÛyPD%X½e]pæ´”Hª”=ýMÁÔÆA f1ÓIXÒ¬np¨ž* øK¥‘­Ž¡m¾v÷GÂÓ•_®yÝÕ‚P$ux<|JK PT5“Zê·1Á5,‡-‡hýâϳ%uÂ)ÒUŒ=—v½÷3šuɲ?j!Úô'Ó «í…ºš¨dĬ¤Iê)7ÊáªÊ:ÑØbüu;ˆ»_pÆÔ~¥8gÎ$Úº÷(uv[8Ž×7Qeé,&®NN«ß‹Õ þÐØ—ÆÏ•Rœtn¸ï®oýÇ—ydwqÿ—©_‡÷¤ö½xìí7rhÍÉ5Â5ñŽßûöÿáNØ%QAŒJbsÅÃz4FRI‘T›—O!` K's·ý¾rº ®“ØOõ0w÷ƒ¤žûѧ„jó‰ âzäã/›8`^v:¡²¤ZÚ’iôˆ2ª©® ×Vn1¯kh1ÜÆÊKüt’]TR¤€€öÙÏ>P©ÐîÕÃÍó)R"²ð:®­¨¡ùÜn«55…ìÕ%ù†@W(mjéÐÙýÃ!Ùèª~mâ%þåҥϯÖ $?ÛRE¶W›ê[RÍ=[Ê;PÊ’:Bêw…@†((CMÇV ˜I!íYý?thóŸÅvñˆ™T=ù*’$µ«õ(ùŠÇŠßòåOIa:ëÆODîsfO¾¨ðG•©µ½Kl–r~ФJTÔ: Ÿ¹ÿ;W9<îßéÑ蘷GïìþvúÜG9§N’cN’ÈQ:&/<Ôhêâu;þcÆÂùŸ=gÊ=ÿ¿ŸadgˆIVÍólµ¡ë1KêèîW_‹¶ª]J˜¡Œ@WÛqòŽPÅp°|®ÛcXwpŽ9]æµ¾âQâó@l€\&N0òÖ¤1´fÛ¾Úá8RkGgãjG!0ð/uÞõÍï}Ùér¿8ºªtä¿Þv5M®)Çq‘ºº`(Si¸"ЊYýnP?FW•U•–=uÛW¾ñeÆ/7/°$˜õ†·í“,üY¨¼÷IU$Õ>UKI2ÄhoÚK…å“ÒòľרfέT6j[P¯àõÙt|׋æµþÒIì:°×Üχzîê¯,5£ý (òÜÓ&P þ¨Ö“+Øm©¡9¯#«XURÛÙG„ÂñOÿöÀ—5ÍñÓù3'i_ÿôõÎiãG‘ßk;ºb$%û"©;Ø Žž"S‰W#Ôûï¸Þ‰úâ+(üÁ'¿ô5+QEű#Q5-©ìp÷£ûcO§,µV(²‚@kÝ*®œ)bžtƒíoÿŠÚI nú+ͺøûÜõÿ¿Ôtü}q™¯h4y *¨µnë@ÙØê÷Úúª./aý~¯cPÔÜÓ&Òú]‡(íiÝ]YʵÂq¡«l¥¬Æn8>ýÕo^íp8Ì„C¿ë¦‹5Ûp«ñûcÍ`ggϺf7%”<ÙE Åò‘RÊ$ õõõÆ_TòÝßy÷µ|XZTc•Gœm‡?z…”‚§ü­—ÛùºV>©ùZrJî¼CàèÎgiÊ‚ûhì̉X§RŽæ´ø—ãå®XG#šÊáôR4"—§ <Õ‰=‹©µ~{kì¾³yïŽq:Μ6ŽÖs8)™ÞÙ¸›°XS„ èÿåUë!±íäX« 8\Õ›«ó‹ Ÿ¢ˆ:K×\óÉBŸÏÿÛ1Õeúí7,ìA,ü>ƒ¬B ERsY,ö»W³Å’ZÜMR¥”\o´£'™÷Ñ/ÆŸ¾òàÁ €0½b±Ó×_Ê̆^c”iì@ÞmõxXóNz%°B УaÚ¾üûT3ûã,õÀÖD¨²jéñWÒˆ ÓŽÿ‰Ÿó*EÙ"úúª­tÆôž„<%fNMuM­´… ¯J Àƒæ¬ž=éÞ¨®þø•ç:¥U^ëëÑÝßsb yŽZZ1®)-©RsÔ›[¯>#ÿGžwíµwóq @PU{ù§êd’T®üy︯,©²ªµB Ôî}°¤š‚õ´ìÑ‹R½|ЯÛuð8aI5mÞs„°¨¤Hb\^ŸïÞÙSÆêìcxÊסµ»¿£#FRÌ_6„ˆ÷IW >ªs¦Öè›Iÿ ÿö^ÂÝ *Ž-¾p˜D›#l5‡–÷ŽûÊ’Ê5K%…@2À§2¿ÊdòÌÕ¹Rötä—yäJæLÞGÊžŽþ™”Gå•U„õcÿ|ïžalä9³'ŸBPq÷‚X3ئHjV Äî™×w¢çÞHeÞX½Ç°6ê‘6⢫?xïÂ7Õ^ÖT-fI#ï-©½—JB%…€Bà¬äÆíròg´1…Þ)'Úì@X÷’›§µ&«.Öãým[¯þÜ…Úßé¶ù rfBÛ(¤IARýÅÅWp½ÕOŸÚ{\áÊŠX‡b]= c* W5ÇŒ¡cKzý¦!Ô#Ô§ª±5—0N°ZÚ*$Ï–³¤ºÊ’:\+³Ò[!PèóPW´4/€èÒKÈç‰ É„ÐÐ?ÉîQÈ™iý3¡Ê#«à«ÌIÇ„b¿/ê÷y{½Yeyì¹hh Q\0‰^¯Q‡&›cï³ñe±zaÕõ¨¸À«»=^|õX-©=­Ö‹r¹m±¤z.eIÍ%öê^ » ‹b‰ßKîY êxOÙ7A>ÈYèu 7«54U©¥þ=j{k*¬¨3“ú§Š›º.§€4¸Ø/otY‰%ÎTœ˜µ¬Ô $ ¨ M˜T(3é‡];~²9#™Yó l/-ß@ìÆ‘¼“Éd+Z\¶vG2—ä͹û-–Ô ¥}sβÒBÍévU³bÿ?{ßGu­}fvW«Uµ$wË–{ï…f6lLè-ÉŸÆ{B$ôòâHƆR€^ Œ .¸Û¸ã"ÛrïE®ª»Ú6ÿùfuWky%«ìjw¥síÑÜ™¹sï9ßÌÎ|sî¹çâËÝi0Ç×|lªꓚâÎLxKjU?GS!(í Ž–¼öÙ´jÛA:áíCm›âV+Ègpà”ŽÙiS {cˆjuý.7ûõ…·RÅ(ïòHé:‰ E}µfw¿®iiÉ6ž>¨–Ôš»üÏU”SÜåß:=¸µ§é_¬§ÏWm1 9ìIÔŽã÷^=¢]>¸gðÄ™K7rœ_=pÇ5Á}Õ3ˆ÷»}ß1îFέ~è¼íкž¡™K6Ò„Ë’•c ×7íØŒºvhMÉüáZß’ºmï1ºjxŸ NýÅŸ>¢Ó}ƒ”‘–B{äÒ7&\JVv jljŒÌuiÛí3èDi€ô[XþN5ËŒûÉ¢[0Š^TÆ=†¥é¿¸Q$þhÑæ¼¨'+.ô‹ƒH‰›â‚ù'.|"yKC@4<„;䤓#I§Ãž!q ä³óçhNf YØ/²+=ê+¸:¯J+•9Ç5~ä³[õˆè¿ZŠdaÀûÍ”á¢oé69U¾åõñKe -ýòû·Ðƒw_Cö¯Y+iÆ¢À¤çÞ[®¤ûn‹l)ÏQúø‹u5WÂÕ…Îú&â×>ü‚ΕDÇÈvÅ^ôäwo¤ë/H+7íäi÷×WÄ ÊG[f4ÚÕß1ÝB ªµ'„L5-¨ ¨XbΧV½Ü9™?È’kääçµâ«µ«ÇGÅ’ÇGD‹Ot(‘°ôïÒ–ÖïöÓ)oµ¶ˆ;!× oOêÓ!ôf(¹¡CCÓùú·fýóô¥²'Õß*ÓPêzärU¸#ª]Û–r1E/jsuéb’´Î©zž<]÷î~«n¡Nm³Ìê{sx"|ÎY¾™ÆŽèG °ï}¾š}\ úÖW0),£毥‚Ç(#5…†÷ËcbÛ‘þ3g•–»èü8L}ó†ËÍ}¹í²Ù*YJË6î¤Ýs-­Þ²'X—ÒgùW;iΊ-¤³†7N— êaúÇŒ¥”ÆîHw_©¹½fëúlÙ&zæ¾›éÕwðoÂC/ódV~<ÿÐf™U<¡Æl–²ôïÞ‘þË‘Â=$¥åôŸ.¥Ý‡ )­¯m³ÒUóaשÉÔ½SsÙÄ“v¬Û¶7(×ê­{¹¯ 'uëØšþߤ˩ ÏB‡ÞŽçþö©¹Ý¿{`€ÛßY‡Ì´dºùêáaeöx½ôþ¼5´±à€ùl»fT“C(`Z¿¹è¯9ñGÍkuñçc%T5x¿q 1#†ÅT\5hªÄHÅÕºø•¨ùšÊA Å! ,‰ {V«•wï@)ImvÝD^#¾Hä\ÉVƒzwȤ¤¤$SfÈ®ô¨ïTç…êïàYgŠÊÙ7µ¾µE·<ä9STÆVT-búGWb©=Â(âpÑjÛ†Ôc'êNR«W<¢_WsßCÜT\梒òÀØ•y«¶šQ<üõëhâ©S›l¶î§RÏÎm™Ð¦Ò÷nCãFõ ž‡îýü½‡éÆ+‡P{v%­Ë,ÄÖoÛÏDô“(ÿ{öªàTÁEeN“\ªrN&¥ø-`ƶQº™»o7ÂlElUýçÌå4fX/úÁ]×Б³´”É1ÒÂ5ù´çÈIº•Ipï¼öôå–½æþ‹ýÁÔÅ‡ŽŸ1õDÙ½\Ç›Ÿ,¥¹íèÛLÚË™˜‚0ƒÄ÷ö4—w{ªF×—–S ãW“ÌK7ì¢-»Ð÷nºŠnºj}¼p<[lЬªãw1yw©j»k ƒ¦ÂÔ¥î3¬cšüVO0??þ~ÐÀ¬ú|Œ)´Ò¸ 8èl¶€X[SØZ1²gZ¶Í ÎÛiTÊ{q£ä)ö·¥¡Ù-G¤ÚÙo2CvèÐÐT]ÿQ=ÛÑÒmGyÀI)µÎJkhµ?ï ûÆy¼>šYý#.¨TMêt£wέò©>x¤áî+é)CV¬ê ¿·ÓçÊÌ©}Õñ,&¨GO#ŠM® /=÷ÃIµöPÜLÙ,Çð¾yæ1XOw`2=ˆvì?J£úw£±#z÷ÁTTZ3ÿåwmþ>“t`ŸTøÍ"Á ëîw¾6Ú܆ûþ3ö>ÁD6`6TûS“Ì[w¢Á½rÙ8Ý\ Ë–yÂåƒÌê‚_hS[NT…ØÒ:¥¸!¨Öêò;B>±.¼ ë¤R|’_×C¤‰<°ï<º»Ìåñ _§—Vmb¨în¾d»öÙtŠ­ÛŽ *¿‡†9>æ!Å!ŠÚ*‹Â1XPAPxP׬$êÔšÃO9’MY!3¬ ‘èî¿Pÿ":u–(»UšÙýÕêT¥iAe‚ KM4ôWB¸ÜƒÅ`nló«\«Ã²N ²xtzš…JJ}ärùéÄIµkSÿžå癓yáÇÚ¤ÑCÈÉÄóqWt}kÒhêÊ]Þ5%XX/æBÓ±u+ót¸`ðÖ>¶V†#©˜’¸¶´}ßQ“xþŠñ“VÎÇNó`°^Áý]Úç°ópp»z.Cût1ýlÑu—¤³Åelñ dlwn—ƒ“YÞ†¤úðC®!ùXŸ\x®Öª’™àâ7Nž¶Ù¦o¬ªÖàÚRŽz‚‘ÿüÇ]îhdÿ<ö-M¡¶ÙéL(O=q¦ÈÌÇU}4»Cd†ËÜ!Â%臡±#úÒm׌Wļa„Ùy¬”ÇTÎ6–Êî[ÝCbç†)^Û®zßwµUVßc<Ø‹cIS³pçlJ/‰dðTT-qVh宊JÔ}w€¤éüdK*w£§¥¦RjZ* ÈkCƒ;¥qLÒ¶´¼ü¿iUùwè¬vUÔvÐÚuQkꙥS¶”žžfÊ!+dŒðoø³ôbú³ù…`Ñ(Th¶Wpà¸éÛªêéÄo–Èrnþ Czw¦S,ä¯p{©˜»ów ÜÚ}»v4»Ñ²EumçðUuI×ò@¦4þ0þpA r¬ª<¶°Ræù_æ›8uïÔÖ …ëòzŽÿØ«·±Õ¹Êí"œÌÃØZ»:°<°¬‚Ô*ŸÔºÈZfóñ*j7¸NVÔàÙ•÷˜ùQÔð‡j°ºÆe¬š4™³E¦Yð;±¤6îž³ãø¡+2rr~ GûKvo´ÔøòÇÀ);F‚•ÒÇvéÆËVÉ6Ú{²˜cîu§ΞüB08âs9%ëEì^õàm¬˜’3^!P?0[5µs”Û*™».S)3322xQe!+dVV‹Æ´_ý—ºé4¿h®J×A3×i7ô\?è÷qlC¬ù=Ëú¬?±þ<#L”õÇ}„Qã‡öì^Ë2ÁBER#×¹* ,©uIçŠËéù¿Jé©spÓî¼Æìêwîö¡\Í£ì½^¿é zÛ5#Íb ˆË¿ÚE?{õÝŸK}|¸Ó/ØËd’ÍJÿó—é&A9¶'^é—ðà¨{Òoþ:ƒ‰ ÃŒß:—£ ÁHŒŒ÷ó/iÚ£ß`ßÎÎt Œú”c¯~0IbB þ®(›¿çýö3Mk("lãº/–ð1|ǵ£èõ¾àY½9b@'ºëºK9¶ìfnc5[IÓè¾ÛÆšuM¸b0}ºx#=ýÊûf»— ¨zN‡“ùÎñ£L‹ôïÙ¯DÛÁ3àD ¨oÚRXå:¨]âÒ"~Ù6»õÅ!Ë7šùßÿ̳‹¡ØÏýr,Ö’8B?RL…˜)9÷ýü7KõêœýÐ=ã}ßCGøyy<^çRA¥¥eT\\ÊÁ‹x]B%%¥TÆ#lÏ–ºè¬ÓK^ƒà˧Ԛʋ§dt æÃe@~m<#I7(ÓÎ>u ?ðá~à0-¨ ¨­23™¨¦Q“6øÏÚø¥ ’ y¢¡8=kÚKýÿøÞcË΃çþö»_¡¿&§Ó¼”ðââå"W™KHŠ6è+çOʸ÷Ég>éß3oäcßžXk›N§Ÿ~úÜ!³ >¨¦üª3w)GäQq^»ÿ.ˆ>¬š Z°rÖ'aD¼…Ÿ …Õ¬¢Š¸V?ææg ÍÕÝ 0¢ÞÁÚÕ' @˜(ȉT[]µ…³ '³§<†Õ5•­¶õ„.¨Êw¦—ÒÚ£÷ˆWoH¥ñÝk×ó¥·çÒ¦m»¶ÿëS~Ì•ç¿8ÄÂ_$H9_ç .õù4Gg¯×e¾D‹C“ž2j‰ÔYºØLÜO†Øa&-'ÊÂ…µïäÑ#ïmÕ´îân®ê#i¢RÀšh!»‘DF*šàG>¿_àó‰nu;“B‡ÃE9LbAf1ó ˆ# áë¿zÚ¶åÁ]½®~6˜ͨ—F CžùÄ$Ÿö’>¨LJaA5»ûƒVÔÆ ˜ •ùhè_½pÛ±Ö÷ÏÖ݇µ#û÷ÎdùðB‚‹©ÎJJ4Ú¶¶Qá)ôŠ´ÿ ›zv«õ)}ÂTÔ­Õ·ê$3ôüš*ÊÀ .¥³h¸)‚Šºk««6_öp2ƒœ§¥„—9œÕ÷a¦©M!#û‡$°%•£íù•M˜F~µTW>¶Ãߥ ¸ˆ(ÔXµL"±dÆGÜùàÃ÷¼3÷ˬŸÞ{“^ÛýŽu›D ÖI$'7 NB¸';û€ºœ.ÓÒ ’ŠUøêQ GPQGl?•)«U <ŒÚ]«¶ð€F>d€¥£øÑµÔîæÃ‚nþHZPCåè9ýCë®-+ýaµâûÇoø|EËfÎøŒeÄ;AU± ÖvÑàXŸžÉ&I…¨;v–G…¤& -JÄ5G¼¢ŠÔ#ÛBmR—ÛyB|R¹£-q ¹…¤†€!Ùf‡€²lDxÏ;Uºmýš—µQ—ýæ­™Ëûn¿º~ýjaàQd $ÐÌ3QE$5Åá0 jwE¹Ýn&©lIe¿ÕP’Zݘ:˜µMëœ Z„¥Iµ«³Õób#P?ÚQÅ(~ ’9 ðXPqN¤“’#Rú_L>¥‚j·©õÇ}s”¿|µjÙßJK9Œ™NÆŠ¨ªûíbjÈñ8E o/-û^DÛw»èkâTP+b¬8TáÊ.‰M‰0p /;$±¤p¿‚@¼#ëHFBT¬œ7ëˌ쬷9ÿ]ú˜ŒïÜ4Fk¬E„I…u2»ß™8*k*,¨nŰ¢²W;[R¾–ÔpÖÔÐÛµoÃb^˜Ðž¹°?,©hÖTÄ-Y1µ™ÖÕÀH~ÈÍIýë"g,ô‡uÝö}Ú¾[?\·dáz–C±±àÞÂ=&–T!‘Sïî‡Ä}¦ì’sø¨ÛŒ›Šø©’š/Ëâ§H£;×?6®:7Ö¯®8* D÷ÁßD 'ögC$Í$4ª»DƒZœsßý×Ç×ßõM<¾~ìôYãë.Ó#å£ eÎÈÒÈÝþ&9åî}ŒüÇË),©HáH*Ç䦜ìð³°  $OdARÍ…·AVÛzåñÈ[OƒVËäiœþÕª »ÙÔúÃ]ü° îÙ¶yúÓ?˜Í‚a€ÜS¸·ðr’Ê $rJBŒÌ<;íÞ‡ËÊ]þlM54|¬ÎDÖSd p¢Ô =•Ó¡&ñ ¹Q›Uè‚JÑT¶a4‡ëœØW¤9\Ñ!Ú  ?[ôé€T ‹6yÞÿ™1üʱ…þ+®ºwÚÛs3y@cÔ€îÚ ž¸'vIŠKö{© 3¬pêÖJ§ž~*ÑÓ¸{÷»f¿ ñÔ1dåñöE‹ÆZÇ[\ÅÄPA!© xÑDä! H,_Šx†’WìG”R|}-­œQERç¶šæ/ˆ‘¤¦A ð¶ª"¨Ê÷…²¢‚µÀ‚ ‚*VT¡¹¦aƒRƒ$õ«­etûY&Œ¶¾X9vd?š¾h= ë“vfªÅëwÐk¶QQ©“úäµ§Ffëks æ3úˆn¿v$Í^¾‰Î–”Óˆ¾y<%j?zkÖr**qÒÈþ]Íãöa÷pÜfLº±à€é?ͨþ„©P‘¶ì:Ló¾ÜJ‡ ÏPçvÙf}»v Ô©Nç®ÜL'Ï•pý]é/c?t¼<0ô­™+hûþc¦üžjõvžúUM^âó w—v9ôÀãÌÙ®j’!ÚXÏÝïË@šÔ«YMex†ìRöŽo… ëö˜m1hZ5 $Ø!© vÁDÜF!¢ª‚┨mXË@R1t $¿ Ó%€×± ¨Ülb?\ @‚%uO€ Âú€{÷H*H)H*Æ%`Á=ƒã¸‡$53zvKæÙÚ,<½±ŠyÙ³¿¢ÉûO¼| ØÛC³–}EwðüôÕSvF*Ý6n$åpPeY”{èë×™ÅN•Ò»Ÿ¯¦›¯F§™DÎ]¹Å$¡“Æ 5IäÇ ×ÑÐ>]Ì÷–nØeüÞMWQQY9½ýÙ &Æ]¸Þtv/XE#ûueB:’v*4÷¡ÔÿÞüÕtÓ•Cé“àÙ+6Qÿh8“aL£Ú¥C]5¢`½ñÑbêÚ±5]1¤í9\Hÿœ¹œxp*àz Ïó4¦vZ¸f{XÚdeTW;¢Û.þu±¿Š¤Nì™ø]ý âÏ1 $µÜéÆè[!© Y qŽ€"! ¡yT|Rcâæp$µ©ÉêI–CRôÀ½€¤î ERñS0 ª ¬ØÆ~XTu.g%5Ðí>l` -]£9цÍeMFRm6 Ý2v½Ë~¨c†õ ø„;˜-”HEL{³%uæÒç…³ƒõôÊa½ÍÉCæ™Oý»u¢kFõ3Ï™ÏÖÑ‚i¡·î>Dƒ{åR»œtsiß:“6ì8@.d–…ÛæøKû›Ûj°#Èë®)ïaÚÉ–ST¤ñ—0e9ŹSÛ,ÊßsĬcÍÖ½æT¨ÿuËUçY¤/&ƒYiþ,b‚ ¢Š„nþæÐÕІHKª¹©ë¾ð!bTáX‹%5.’ˆQ@*@BB H+È>§C ªò]9mj‚ÊMÒqü‘Ô$à~P îP¢ŠûÄk,8&T¡9§¡ƒªHê¦ü2ºëæìóV4u3´7-áný÷æ­&äCÓ ¶ž®ß~À k‡nö@ˆ;ž“'!@êÄÝóH™è¬¼]o9•Ãëa‚ÇjÞÉñaá\·m¿YNœ†7 ÑwŒ£ÏYI¿ùë Õ¿;ý¿I—s×|€.thcö$›å:µÉ¦}GO™ù¢ÒrúÏœ/¹ÎÔ*=…Η›ÖRCHj%² eÃßÔ !Êz rªj,}RO³’šõ\W÷…"ªŠ¬ªmu¼i¤’Vb†@¼dÊÌ´RQ‘—J˘ÔíuQŸèl‰~‚%÷ë.¥©o͡칒;eváÿø›×S¿niÑÚmLdל÷ ­È¤’Ò¢ãQv~‚O*ºçÇŽèK·]3âüƒ¼…núŸÿ×ÍL`÷Ò¿f¯¢¬å)të¸@¹§0v0 ÏSÇJÒºpõ6:tü4=÷Ã;M?TÈ®R€Þ~ü˜Ú4דá¼ÂÜÀ`) šR醞ÍÇ:ñƒ,`þç¼&$U]fY  ‰ˆ‰²Šá©+Yè¨~˜&扪5ïj²0k4Ys-º!ER±V ©ZÔ¾ RKS~Ø@-^xç¯Y_Öd$8÷ìÜŽ:u£_í >:SÕžÅ~©ð9]Ƀ‘Ì„»S=©{.úþ§«Ù÷µ÷ŽÔ³K;:ÎäSDg¦¥Ò¦]©o^vèBÙ›ÉnGS áØ0îò/fË)¬¨£+»þÏ—QFZŠ9±Æ¶½GL‹*ÎGêË„§æ¯Î7sퟟœnöMúÜþê[ÍUº¦›á¥p’8ËëwoºÒ”A…—ÂqX~ŸûÑ&y ÝcÊ}áîë.¡2g…IR±Ïžd­QœéTpÊG[NºúmníÓ¼üQ+ñ öÀñ´y™‘ư©ëSþvMÝ®´'‚€ Ä=WŒJ ʸj]ÐÝ/¸/EP#Ñ6_°‚4†&›Õ$¨¡ûÙRg–­NDU™šöã8w¥§^ØVM2¨:#µ~ÆÇÒÄI”™Üü(‡  Œd ¨ÙF雨ëæw…õJˆÜ‚€ q‡ÀˆÁiì“xU/ôÐÞUD'@ˆÑjg¿ÕDL9õiAUWÿ]š¥!¨ªB$$5oV‘YA@¨ II’,ºjm|XSƒ5QFcSëo¾‹'Èk¢#ÛÌì]TêøjbÔQ›'Ie3wI%jY›¾6±¤6=æÒ¢ ‚@!pùÈ*?Ô[ät©1– ¤D 5´«ÿ®ÍoÀTðò†’TÍKjÉ‚€ ÍÎ’(·c “Çã§•-Ôšš¨—vk¡—6ŒCµ±_ì­}š/Ie‹·ø¤&ê*r ‚€  AàªËª¬©˜.3=µ”ôÆÇ‹èØÉs «î›«üˆ'ñ SYŽj£ÄV³0‚Û¬UÝýâ“ Ù%‚€ 43FM¥ôôÀ ¡sE>Z¿9X?Ô,-wѧ‹7Ðë}A¤ÿÏ™ËM± ö£¥ Ì@ú¯¼3ß +… û³–â ¨šyùóU[hMþÞ :ÿš½’ös°~œ¿©à½? Mÿb½y¼Âí5§mýëÇ‹yŠÖýAÂŽ˜­ ³hÓºÆC:Râ§¹{ªLÝ;´if ‹îªHª&>©±»Ò² ‚€ ÐDX8®æÕ—g[[´¼jzÐàÎe^ûp*<Ã3Tu§LV7í›|~?ÐÍœ Þüt)à Ûü׳:Vª œŠæÝ"u ‚€ 4Æ\’Fít䘇 v»b®f~Ú{¤n7‚§íJãFö;O&×K?¼ûZ3´7,§ÝÁ³M]:°;]6¸'“Ô}畯¾Ñ¹]޹«KûÖÔ™ «Ûã£ü=GyúT:~–Z¥;hý¶ª:@foæ™°rÛeU¯ªÉ·K* ú0d†©ÿÖ|}QÏר Cå.-¯…ó¬û†Ôºc%%A@Z0‡N—¬šiò‹eÁÉ}b†J™«ÂìnOç€üH ¡©mvFpvªSŸ9Jaýå÷o1}N“ªM} Ó‰£‡…cŒ"uëÔÆôsÅ(«E7­ªØÿ¿ßU̬¨¯­«òþÞ;Y[P¿1˜ ZRÙÙA,©1¿#EA@A ‰èÜÉNû|?™ÐÜ…±! ‹§… c]Fú‡KÕ ª*ƒýÕë·'Y/اÊÇrÁRÇJVÔœ¾1°åtõwÝéî7¤»?–÷¢´-‚€ 497\Û*ØæÆ­å1÷MU´n•Aã/í¯6[ÜÚã3èõõUƒÁîc_ÔäÖglXõ %•o€„îî¯ûgW‹»ÕEaA@A <˜&upÿ*ßÔ9_ÄÞš IÛf§Ó¤1C Ýö¾Ï#úO”Fô·I…µù¢ªëk³}R Mºû,²A@Z ¯©²¦nÊwš±S[Œòq¨¨›­¨ol¨Ñÿ}¶¢Ú­?Ú87j"Mz¤ô$kpÊ5¨Õ¢?µ©Š›µV£S±XR£ƒ«Ô*‚€ ÐÌèÔÁvÞHÿ9qà›ÚÌ!¯U½w·º©0dDÿ=-ÑŠªÒèˆÊzÝE ;xJHªºŠ²A@ê‰À ×TÍ:¹e{98Û¸©õ¿Ù/ã ýol¨ÑÿðdJâ©l[pÚ¯t÷z|ÝT>ÑÖBR튉¼‚€ qƒ@‡vI4|PjPžg æ%Ót¼Á!§N—Fôwศwh¾¨¡pÚ^µé'CHªCÖ‚€ ‚@KBàkZq¼Ð€Õnÿ¡ Z¿¹¬%©s]”øéÍMU¾¨]á [eLט # ö©¦uƒº«|¢­Å’šhWLäA@ˆ+0 ÕØÑéA™fÎ=KOÀªÜ)™¨!0ue9!ôÒöúZKš]ªTµKª¡BRkÀIv ‚€ ÍëÇfRzZ @þÙ"}±¼¤Ùë ®?楹»=AQ~6ºÊõ"¸³f4]v÷³ú KR[XˆÛx§ŠÊ1EàÁ_þn„Ïï›ÀBäñ×lGî¬2·„‘lyÙþàÞûŸéº8¸&ÃvƒþZ>ʇXtËçyögëÓ]‚€ ÐØí‘ÖòK¤«%²Æ+Z€ ÒK¹¶­úU)oXZ[ÄDV´{UêëÈÁ¯°i÷ýü× ª°â6­Y%&ÚK£‚@Ó"`p?ê‚Õ[éÃk™,¶!Kù¶¢VM•Ú”Ò ]«“Ûg9>Z¸Žæ³\>Ÿ c´R¨þ'­i]êUTdm­æj­í®O»Š GSé_«@ñq0HR5À%5á’Ô„»d"pœ! q×ú¶ ¾˜kË7F:ÞÓ¬ÜË„ö!äÑ4}ÊwŸøùD–¿u!ª±¼0Òv³B ]Ü-\Ï]ûmÉZ1€`±}¥¢}Èyf,ÚH[v"XT£ATCõ?eëDù©#ɯEë©î· Ú‡'Úú×]ªØ•dK*ºûÍÄw{«|"­cû‹J$¤DVA ÷ß?Ùa±jϰ§¡ÉÇ „<z¡a·Ûÿ:~ü]þ*¿÷0×Pv  A£õßfLÝŸJ–Š~ ©"jç@åzgîjr:Ý&QtcJÿrK:mOéêUä)ÓÓé?QÔ¿Q6ÑÉšEßlÊ !©A0$#´ t_¶ö°aèÛ?c®[ juÈ!Ïà䙃ô†ô~˜c ¤Õê@ɶ PO`Ä(ö"Á®Wôйµºø°¨ZX®¢2'}±naÔ}$}TCõ/Hs juýaQÝéDÅQÒ¿z{ñºmIM+‘­[¿ãÊ"[YyaÕjE?v‹ÅB?ikÛmÄʵV ù ä‚|Iö¤‡x“ãàˆêÅ0“ã‚@m ›ÛçIÝaŽâ•jm2âäB”¥vqŒV·)s$ºý•þ ×í0GñÇÊõbúC.DX²1²ú_¬Ýx:~Ýg‹8ÈC!dbÏää/¦9ºÄ“|u‘EHj]P’2‚À…èßzäéQl¥lÏa¦âúëÔ”OÓÛÝúÝŠD¬©^KÙ#ÔX÷9I%åN²ðÈúxN¯Ôé¢},¯×ëˆ5Ué_ÊVʶÜxVß”¯¬<²úǵÂa„cr´¦ºÈ›p]þBRÃ\TÙ%\ÓŠj³'MàŒÁqP/R<¶‡!äLk•yK"ÖÔØ^i=PVÄü½‡ aH5ž“§•åܾï(¹=žF[SCõÇCqPã9™òEPÿxÖµFÙ -è—ªþ>5–‹ÓBRãôˆXq?öÌnó®˜I*ÚqP‹䃜šnÉãºY[BR5T9¿E" i>:}®”c”Ú£µ± #Žªæ·›S“zL’Ú¸T¡úc&©hêo¬î8òANLÍ ý#!SS×Á$¯ÊŠâ'!©M}¤=A àãÎBºÖ!Y+Jˆ=È©Y´,·²¤&„Ü1¸¶Ò¤ P#èêFìÑs˜òS&DJ2Pa4>d‡ M¡úcªÓDH.Ý1ýAßê2jz•%•¿ª¤»¿:@²-4C@ð¬:ié6Í «jÜ'È©“Ž0Tx³Š_jÜ_10ðû ò2Ñ«p{‰ü± Zg|XN7Ë ’ Ù¡CCS¨þ>³C¦¡55Ýy>~ÜEJÿ¦“:r-iš%hIå+/–ÔÈA+5 q‰H©ÙÝoPb…óà®:“\³üª»?!v\Þ"T‹C]ݪ»ëDJÈ5œR3P5Dþ–®"]ïPYsÒíá7–ÏÜgPçEovM x¥Òåz5%/Ô ün,Ó#ÁHžIªAP±Èo¿n×ZJ ATwwCH^°’d /j¤ºû[ªþ1¸tnräë=šAû*+ÒݧŽ&Ôô¨ò¢jô- ´0”%•§BN8KªiæëÔ¡…];QWh0 f…«H4K*Q#¶+ÄVzÔuž©z Ô¿¾xÅUy‚#ü½†/¡ºü…¤ÆÕ$Â$Šä%ˆ¸ç‰©dÇZ’ Ô Q«Ç9qQäÿ˜\bihRç7¼††¶ÜÈó"¤#¥ˆÝé!a¨t2zÅNú·,$µþ˜É‚HT’§Hª\EA@h!y h."§€T*‚ÙØ [ºþÅ/&ç‡XRù%¡Fø IÉ#¶T:ö¹®úöR¦¸ê§§Qï+~J>HYFaéuÙæ>ìÇ2áG{ÌcI©mèºPZvB=g‚zIFA i0t}·jÑÐŒ„òIE(I‚€ PwlAÕtõºüIÚýåKlÖð“ÕžIC'þ‘R2»^к-9›Î_OËŸ7)ë…»ì$Þö®Il7|ö_œWÏ Ö¥žíHqA áP¿Á„W„hˆ. 9'^±jNºÔc«aÝͱÌ¢üÐïQ—s⥌2çÄ‹<"‡ Tv™×oàTÇÞ·…K-˜nêh³g·¢”6̺ï“’³¨ìÜ>:{l¹€°ª´oÃkÔ¶ÛxJË©§ÿ{ A¥ì ë® `µ Ðä€Ü$*ÁQ²7F~UG“•ìÑ?bĤŠëón:ÈÓøzÐ8ßÂ×½Þ1%&‚4 Q!© MN‚@Z›~T|z;?$_´ÎâCôÕÜÉïu]PÍ‘E9ýiÐu/Sçß4­®ª«ôy\ç(£uµKÖ‚€ ‚@X´»?ð±_ò~uðTÙ©n*ïk!©ñ~…D¾fƒ@jfwÓ:Z…Î^I%g ÈbµSïËJc¿»‚tkU fXYSZu­KURFA …#À±R÷+|^_ÂTñIUWMÖ‚@”p¤w¤sÇÖÔ©•=k_ –sdt¡«¿»œÚ÷¸!è*à*9JÉé‚e$#‚€ Ôˆ€¦íC_?’¦]ÍLüKj\$±y à*=NöÔöõVÝû^wéy–ÓäôöTÁõIA@.† Ø,cP^0ç!©q~D¼æƒ@Ù¹½”šÕ½N å ¹—Gýç‘#=—ŽÊš”F§­ ž›’Ù]ö·%#‚€ Ô„€Å¯3(7˜óŒt÷Çùñš%'ó©C¯›¸«Åž×BRãùêˆlñˆ@˜ú _¹ÇH nÇ£°J&a7 ¿¯LmcͯÚ.<‚ëŸÇ½kömùœõ–ý93+´ŒäA €@(¹³Y-DzÀ¿/îñÑ|d³œÿªÕ¥®ò‡žý-ä­ë©1-g5¼Ñ?¦JD®ñU•ÛíMSùx^ŸçƳ¤"› gø=¾B—‘™$Õed…å­/Ù¤ß]îÔþÆ õt(œÜý3Úï7^+)->6ëí£¹/ZnY÷ú[hÉ ‚@Ôä$ÎT$Z%'1©Ž`‚þÉþÄÐßîwE\ÿBÙ¤UñË*HR­äOoÒÆؘÔ'§µx OEÅQ·‘ª¹ªéJãÈgÊétï ß¶¦ùùꇙ= C¿Éê'Ü{éQr3YµsþvŸß?£ð܆c³^Ôÿ4{ší2u\Ö‚@KGÅŒ;º› -øÓ‰KX Ÿ_sSªÝfº)„ZC*°Òßj¸ÈjÄ·þÏʑԿ¡¸ÅÉyA’ê'CHjœ\Cˆ4°ž'\Îí„·O¤ëh}rÞ»{-䯲ø«sî›~æû䯧Œ[zZGM׿w/ŽÌ!¿ñCÃë]5kжsöý¼œÜ=X@2‚@ C K^ûls†IÃr&®PòuÌNãïÑ€ì!ª¡úãI’ã9×ú›ò±œ‘Ò?®•­ƒpìâUERuCºû뀙 “ä±ÐÆâÏ>Þâ÷yNòÁ¾¸MÏïuŸ]·távŽtA ô¸'JNMzÒÿÇIO—è”Ô—_eÏóÃl?Ž“A½ Ãøu…»bé¿:År¿ø¯Ñ‘L @@4æ¨Ô!'»m䳯7Iƒ|Iì?š“™BöK…ìJú^2užÒßÎú·óTMbTßúš¢<äƒÿl$ôo y›  #±n|Vâ>IwÜ_"0À¼ {¾Ó' ß)ôôÔNyóâPL"ÈùŽ:ô ˆ”)7¯ÕÜ#çÉ}ÃÓ“žö?sÃSþîV‹åj]ÓþÆïµ¢ÐB¦ÿªáþ«LX?ÿÕPt$ßœÐuÉâe@îhÐOór..U†\¯[›T²b S¥ÜС¡)Tÿ¬[R3½§Z]Tσ\¯{õªÀMQyÀËlÉÐ43US4Û˜6~·6¦U9WH\”dÏ»ô“ßñûܧ7»nò{øgy —ß[qvÙÌ3X^7dæ%Ԛʛ&¶šžð.e²ú}GÛ¼öº¦ßÍ”Ïx :¡Á•—;”ÿ*»üåó©Ö«ØâʼV’ мP–D=«ÕJƒ»w ‡ÍJ¾¤Ü5ö›/f@/Ëegëiï™”””dÊ Ù•õNªr’•z;·nà‘? òôb¹’¬‘Ó?~´k„$š9æ P‘#ÿ„¤6âzË©-¼‘@öÜgΖìÜ´iJ±¿-}åº=®ºý!äÚ²fõ_JJΕ²¼Ž«ˆjߪãîÝïºá)ßìp%§vâ—Õ#LV/ð_erú¯Ï·dÎý ÖçN± çö$ Í]׈{˜ôÙ(…OêÕ–­•eä³Ã“&~ä1X®~RÉ‘l'»=É”²C‡†¦êú_Ò³-¥øŠ©_ùƆV•óú•o T–«ûÈêa›¶R Œ5“¡‹%Ua!kA ¹!‚ÓŸkÙœO—Ù·û‡=´uÎ{ŒX[TÑ>ä€<vnwÍ¢ù«YN'd­”²×™¤rÙ`šôHéÉIOù_ õ_åñ‚8ÃL=— ë>û~ö ZÁ¬ôÉs^°Ç÷è²P$ß\0{=|†¿ÌåñDäRuwƒ¤&ÛíÔµC6õï”I>K!yíù1·¨šT–òtÍJ¢N­3xöŽdSVÈ +h$ºû«ëßÚs„”­‹¹EÔek©µçhTôW? —ÛÃãI½x¦š÷XÈZ‰ÏµAÁP4V_`ö©ø´J*kUVr‚€ PGð`ÑC×7Te³ÿóÏw&~ýÛVê1à;ÅþÖÆ`ûgzkëyÜ‹E?Á]ü° îÛ‘ÿþ‚ÞE7?¬¨äY!3doôKþ«\Ï3LH9wšm´ßðCówqÅmx¿™8ß››ú•ŸÜ¿bº‘e½cM¶¿wÝUY Q@ x#žñ¹¢2l7Ü„X) º¼Aôl69˜ü¥¦¦Ð ®m‰ í¸Ü÷ß­ÚÚv­›´vÖJŠbÏ â "ÌFñc”Ïã*ùjåÂÿÛ°|1,¨9ù°„ZR#ö0åêZŽeÑ¢±?®X»|¼Ÿüß`rz+û¬§ÞãBà 2†y*\/0a]Âj8R>€u–Ï•$DÄ3.qVhå® Já®ïƤIãLø’¹®´ÔTª¨pÓ€¼6”lÕhDZ2r'od’šCo;Ò|Ùuóû©U²öž,¦ã%gÈäI38Ò/š¹×­¡ñÏŒƒô¼ YøÕ.Ù ÜVvJgbš™™N¼€¨²Œ27¦«ßlˆÿÔEKi¡IñK÷ðX =™|Zäô·ðT§˜IʆG?XàfÛÆý“¢®?î# =¸sÇf†ƒ[. ¢¸\žCy,˜‡tpäëãߌÊGËË"B QC'eMÅó‚|QYY±þ‡ï¼ÏÙY}‡ŽèÝ¥WŸŽ”´ÖIŽäL«SÕDfô»×çuU8O;Ëœ>¸³`[Áæ {¹MXKAHaAAYE]ýx£E¤«Ÿë¹h€+.ô–å/´N/ÖÎÞÆ>sß`F=žÁ3Ÿ=•ë †Ï?Asž}§d­iúûŽÔìYã~tÖ_I‚@CÀoÔX2kz~¯!ÃN¯Íß›Í$Õü6´Bœ´&2ñóó*¯Ì—ÄSv.sÒÙRuz©Âë"ÏÎÄ6b 6ffIì2Ón¡t‡…ýNá~à0-¨ ¨­23Í<ºú1º¿±VT%|ýõw²þÎÈëÏ–ëXèÏ÷‘á÷ùŠ·®ûrc‚«jÞg•kSÜ­5ÃÛ£ò6Å‹ HBRâ2‰qŠP°ÂB©^~x(òZ±ã«õ›xÙÁûLK+¯AhÕ׬:‡wÕ+©g ÚÇ‚öðU 9@FU÷>¬’ ¨MbEåvjLcž>9Þ²hjzk§¿Œ}Wo0c*-«xÒÃÒz»aøo//;åä¬sAX“S²?ÂZ#´r <ø¨Åwòè‘÷¶jÚwÏÝla½‹ßフ„,t2K~+þ[‹µÓ³`a5ôÈ‘9S|XJ²®†îu,æïãܹS¥ÛÖ¯yYuÙoÞš¹Ü¸ïö«ëû»«V}(1µH*Õ€U3‰R“ bP•ÛíæÑÿlIe·€P’Z_®ZiD b£ `&)t僃¨¢kƒ¤@NüTe½@‰FìPd:›ùf®?'ÏÑúe‹ÿÍ>ÿxÆâÙªž¯¸ÏÔ=×T£s*hµ–¯]rµª#=0ɆÈñŸ„¤Æÿ5 ãõ2ÄZYo@ZAaA %¨Êw/Ɇ¾(ÕÃPµJTÑ.œXcÁ±¸þÂgùÌtãã;9óVÍï¹ËàV¼=Ä,ÀøÅ û¶Þ¢¹ÎV°…õsÝÐ?´´ÊøôºΞ7}«:GÖ-Ü÷ø@Ãï¡bå¼Y_fdg½ÍùïÒÇd|ç¦1Zc-ª g*¬,‹*<H#ºùÝnž ~«°¤ò˜iIm¨5í™ ?:`IE{°¦"n)È"ˆ©Í´®FòGÃ‚ÊøSKÐTÔuÛ÷i»·nšùÕÊ¥[<ÛÕ¢ˆjÜ>gk—_Ê·^zå…Û;þQ×ÞàEŒóL£I*¿%ákÖ!Îõñ¦@)ETÕËQYOANAU}o!©ÐGµ"ª´«òê8Ê&Tª$¬Ï³ÐÏÏ}ÉÞÓçóÜUƒu˜R¤’°ÞÌö©›}çι™°ÎcOŠl™ŸaU(µè5îü@"LW˜¹ïþëãëïú&>¿~ìôYãë.Ó#å£ ÂfÎÈÒÈ~©f?wïj`p,©H!©8ä“›3I*ˆª…·AVÍ<“×Àñ†>^ÐBý’j¯¹éTtñºkëWŸ-þ䣅ŒŒr«R=V¸¿pŸ.ný «*mh ‘GªvD2ç¿YÕÆ÷)[QñšJŒÔh’ªÚQVuDb¨+R QG@Y7ñÀa„5¤Tù¡âÍ¡ÞjÍ»ê•Ôkµ =µ¨}õª4^ O|¬b7Ëö;,³§Ø{hÄÖ€K@•+[«™´~!øš;@Xð «urÌœð$ÇÑÔÀïä¿A Lž÷Áf ¿rl¡ÿŠ«îööÜÌ=sQºkƒzvjTUeUaC7<,›(¥ºøAL9Uk–§^ m a­´§R©}õª4B…Ñ6Hr¢ë8¨3…Qü$åózJ×.^ðá–Õ+·2Tê>‚Ï?î'l«+ÜoI0ö­kLáÎ]÷zǔ¢c÷éÚŒ`>2 }IU»ÿÏýœÀ=kIµ¶þËÏ~v6x@2‚€ Š~k¡¿·Ð|h¹ºæCˆÍДր/'wg¿» ]Œ?”ýŒú ¶;M×µ¤žtí«kýR®Y €Þ ¸Û¤ò’ÉK/Ù¼d¥¤¤e_6aÒõ]ûôk±X3˜dé»Ñ*3UK¶!,~dRuBZ}»¾­€ †¦êÛ¡Çâ!_]ßêÛõ•±º¾Õ·ë[Ÿ*©s13õ³Œ §%{·o[þå¼Ù+\®r5SÀsÎðrš¸¬*k*gëŸüÝï²|ekpâç–ne>ÕàJj8‘mù?V‡Øþþj"TÈ}þ'™Ò¤žëï?óì!V~ëÏ=sC=O•â‚€ D…S“ó* ÷m<èê6~Âf’ RraÒˆ­ªÚ î œ>áÉgVhÚäF½h.l@öÄ pµ9E¤4^0Uo+^`Y… ¬¬¡aâBÝsø¤„Œ ÊmJ¹kÁ„ão@P1A H*H+Ž¡»¿QÏŽûŸyn;ƒ üës¿ìÌuE,Í™j¹Ýïó„ Ù_œaää&Z¿ÈÔ_<÷»Æ½È]jW¿þü3K#†°T$‚@#LP΃ü·1YÅLW #$~žäÙZ?µ°•Õoï¶`Ò#»a!‘Ô|À‡Š"ª ¥ § « ­XÏÖÓ ¥•ó «HyOª’¿qŠÈ)’"¨Ê—ÏeE)9YiUeœøÅsWqOϾ͞üëóÏLmpEÕN\ôr«VNwÑ6V¬1?{醧ýW+÷›ùñMžüfòQïávã8§ÙÚ\þÆäà¿!I¸A`ÑŸÚ¤9g&ò\–LXùá KÚ‰Š¥lv˜Ã½CÓ­™³%RÀ%â¼ë”Ed4¥rAiÖ¤\µ¤AD$¤3ÔŠ:XJù¦‚¼* ª"¸¼«~éþɯ§ž“«ØE´UGknŸÉ“ïñHš=Eÿ;ûÖþ*c+êQ[f«þ‰ø,‹I÷?óìõLRgs…¿þÜ/îI4¿è IZë^a+,Ú4δ°’q3ûƒu §9?Ü=üZÂϳYVÃ6ëú§*v…+'ûET1ƒ©@VaYWÛØã¦ß*¯Õ;R­y—¤f†€"™ÕI*CŒ‚8‚¨bmìW~¨ê\ÞU¿„ÁY<óü{\ÁíLR'½ñÜ/çÕ¯†šKÏšj½ŽcŸëãe·N|Ò÷IÍgÄþðîÿås?a?°—ÙñÙÚ|O,ªñ{áE2A@ €—Åœ—’.å)n…••_½kĆGÇ2oùŒ'˜Õ¥[ŸeîÎÇ KRb!‹ªêþG÷>ˆ©Z+‚Šãx?ª…³’š1 ›j%UYSñû†ÅT­‘DZÆù ²•<'ÿ¡ô4]{ôgŸù=בdúäû*V±2f7?`0é)ÿÝ©<•D”¤BþQ¥©üe°•c¨>">ª1¸ªÒ¤ 4ÙS“ú“ß{+¿³nãJF°•5üsRcß4ƒæs<ÖYJž=ã nTNljpMY! %§Š â8Røë8&› ¨H ŸÈ+¢ªÈªÚVÇQ¶A)àƒJ¯òÉ4žˆ$A5}ð}¥+XóC›oÜÓIö”ãRv¢AÂÆÁIQùñ¡ëŸuû+?Ü»0‹ŸËË¿õ}–ÄQƒ+."‚@˜÷BJGV1I#±xÆ »àŶž²³xNõYw¯ãçžzñ]P^vÄ xb!U‹Ú§Þj7B‹ G@ýV±V ©ZÔ¾5Œ8¨0SÆÿãž›‰ü¼8È}?’]üŸ¿Ø.Õg.bÞ5ÊR#§Õb?áqïÊ ''EíLJÁTG»|M¿#QýPCÁÌ•­1LþÁ_þn„Ïï›À‡ò˜¤väFþC’  ‹@+ý¸££-?§uwv¦~4“©ª{ø<Ø[À_äïXTèíyæ˜gÀésþö„!Iš9LRK*§Ž?€‰þòìÏÖGZå¹/§tð{œŸ-¨fÚƒ7>í-ÒmÅ¢¾&!©±PLÚA ©˜ýJv¹‹®g+ÜLV'ñ £mMmóCw7Ç„™eèú¬®]ú,‘ÁW5!%ûA 6>ŸjÁ3~~½ÕT9¶ÿO}ú¬ÚNôµÔD¿‚"¿ ĈðùËI£ü^Çbi¥áLZÃ>ky'Çd¥<øê3‹ß>çú§ËÆ•2"Œ Ä%s¦Xîò“ÿŸLPM·#„Ëãçȃ7<éû{\ Ü@¡Â>8X—œ&‚€ P ESR¹ß5‰Ç`|»ÿÆóK¥Fw'~ ç³CÙ|]רS²—ŒûÑIñ߯†§l -å/´N/ÖμÀã»~ >~™ žÑ4Ë7<é]Üܰ’ÚÜ®¨è#q‹@þû’(¸Êï÷ßÈþùˆЫ&aaaRû%èX`±è ’†Y3nÜb—$-9S­ü>߬z— ú»ÙFI76׉F„¤¯´dA@hZfM³÷Ö}Ó-€[ÖÌz>q\V~`/†¥•tëÂIO¸·…/({A 9!°ìÏ™YÅ¥%/ñtÎß Õ‹?d§§§eü÷•?,:º¿9å…¤6§«)º‚@Â"#süJv ¸Ž}YÇóÃyˆêÎ §¿ ØU[ ÚJ²/˜øhù±pådŸ $&¦ûë·Ø÷ôîU1g‚&ül`_výÇ“žòý#15«»ÔBR뎕”A É0gñ—_ËÑÆóËj<¿¤ºÖÖ8?Ìó9jÀ£µ ÓÈZ2æéS%µ•—c‚€ ¿Ìžb½Ú ß4v *%œÎ¶ëö^û„ë@èþæš’Ú\¯¬è%Í ¹/Ù{ú<žñ<‘“Vº†•˪IA~°{Ùܲšg½šoúǨ1«ÅŸµ&´d¿ ?Ìžf»Œ|Þ_óo<˜¿R4ôœèšþ(è?~¤¾$BR£±´ ‚@D0ŒÉúÜiÏð¾ñ[Y¹òÑüR»˜?ëXY-V낉¹9Š€$A@ˆØr:š ß/¸Çä†j2•óÇæT‹ÖfÊ„'O”U;Öì7…¤6ûK, ‚@sGþ¬gýÇÇá¿Nó3iÕh(“ÖŸïl•)dL–±wÛM³.™øÄÏ·jÚdÌS.IšøœÎb½ÉÐüOñïuth³è áŸð?“mö_]û˜óHè±–”¯ñ!Ö’@]A@hNÀŸµÂ(¿ÆÏ„•C]!6k·‹èw–‰ë2žW|©A–%“ºÝºQ»ûßEΑ€ Ð*c~Gë?Ä–ÓÞçU¡‘}Ëÿ£¶_OzªbÏyÇZà†ÔxÑEeA@hYÌžbï¡ižñ¾‚_–K­º¾$·sŸuñ>}ëˆ× [iéÖo<>¨Ùx®õÚÉÁ¸E`îKIüïøÃñ»üê›Üÿ-&¦yaÊ»ùãgºnè¯O|Ú»(ÌqÙU ±¤VD6A@héÜð¨k?c€å-^hÑ”Ôö.ÝuùŒÑìp¿á¼; ÇTâ}9üb¾‰ßD<öÃyò€‡­­›™´®Ábè–57<þóÑŒ"À~}ëØŠõ#%G/ø“‚_ƒ š>«%[ì,Ë/Ø7÷i.c’TŸ~ª'˽ç.êÅzÔ8X¥ÏK[F2/±¯nAÁƒ¾_ÕÇ ze—}ŸîMÛñP¿Ó¡û%ß¼ÀÔÆšßsßww>Ï@S[¾‰ª¥í¤kKÑRß÷DÉ)¶°V;,›5! $µ&dd¿ ‚€‰À¸§ÊŽsæãÊ…½Ù5ÙuúðHÃçMšIZ¯`¢wÞ`,ö °qy¶dX$ŸŸæ¼øëâÙS´uZg®ik,~ûêëŸ.çé]#“,m=·kÎÐÓgÚÖqä÷wi?bà›;¸úRgAw&¨Ç¾þøÀïNÝú¿°²îxbðz¯¡õbÒ¹ °#´6v†“¦ïÔ­ß0üþû¸‹v£æ7–‹Á]`ŸÛÉV\­¸Ï‹[J¬ýæüÇÄÏnP`É4 ¸ÇøÈs»¿ÜC^÷Ð 9©9uéiþ@zW³ZÞšø˜g î.6â7ªÝ–x²Ô–xÕEgA@@e7þr®‹™`QbgÕѺa\á7ŒÑlÕìË„ñ<—2ÞÎàÂ<[–q —a{’“ØÚz„ ™ÖV2ôµš-ecCemýÉ L=ÜmßÞçôýÖÐ,Ï,§y! æóô‚Åt²¦ù™ NeÑžâÝ÷hLL5C3‰©Ÿ +ËbVœš,]´òïü» ü‰‰jÈö›º-Ï0|“S)iÄMOô>óî´-S}>ÿoùÜàü¾ÜžC.ï¯8{7äŽhû%%ð1;-i¤á÷ÝÊ÷î­ÃÝ¿éÙ±TûÌ¢kÿi1töÈÖ{ÄjZRuÜ-$µŽ@I1A@š¸ññ 7,o¢Ôü׳2}Å%£ü†ÿ¶¤^ƒ—xM´t~2¨ï¿ËÜ7ò”€¸`²¸-Ÿø¥¿A³Ù7Öcäóz¯Ó¸Ï³<1àCÕÑ^~-@@Û礿uüTɯ¾²¥‡×Íþ¶º1åxö.XU¿Pç„®óïàl} Ýø—:f÷Ü•ûïõ÷9µþ ¢>¯î˜ª¹Üf‚êY¬iúk†á/‚ªP‹ÿõìWzÚɵŸ#`øo™ý¢~ àt '5ß§.¾gó5~¿mf»™AP(¹>\qÙWO„¤Ö0).‚€ pq®{àl—ZP¹˜'|>ÍÑÙës_¢1ie‹Ô%üâÉ/ø´ jãA'¼ŸWιM‹ëqW¶€nйË,¶ •¾³çŸ®éë8ÜÏÏ,daߨÐdô¶´{ßÛÍÕ—-¢ž zœ™ižæ³¬§šÑ›m¿¯‡žU=²›êK ±¶jcXŸ?«rv—ÕëÖ<ÉØ†ê€÷óGøø¯`òrŽ*-ëxB÷§ßç¾mü7’kϵlìOÅí&ñT¥ô9[þ?Ê ¬OÇ<}Šûñ1÷EÄ<øE%£ Y+I.ïòÛœí /SGae÷öu5­Ìj·¬ãE‹ÆZ+Ö.¿Ü¯1)õ“¼^× ÅÒè Ìä{mF¦­Ãü+;ì Ó[¡Æ“å@C’ÚPääþ÷=÷Ãp„=IS´A^yw¾NúÖW¬[~cè—lÛ†F8mùáà³}_Üú63å;aYôçÍYîròñ‘^'%.ü{┳'O€pÞèvWLIr'Ájl&ÇÁƒÂªõõj½ÙòV#ùUçÊ:úTŽÆ¿ž¯ûuåk—Œãû&÷NØd^3í3žumÆõ]n[Q5Uðá°Åegä’yL¥FA@"0nÜb tÚ\¹üªÅuî”ßõ"Ý;Œ-¢ÃÙwÄuwÇfãxõÄœ£ ±L^ÇâÈë*¾ÅN§tü¡)Z¥åUË·èC?šm}üýŸëu”0ÜQ¾ÆdñõâaOç“MÍØéÖœ=ùœBœÇ#ºog‹ì¿Ï¯ƒ}]uš{þ>Ùj ü1=Çë,ç®{ã:þ蹞Gãw©‘“šS“Òb7[³ØfO|¬b7ß},¦Ÿ—šB\i£BR«"›‚€ ñ…@¥Å]óXÞUÒÍ~Å‘«¹=xpÖ&™”`+j¦ᬮàíù\v0Æ3ó%/së½Sh ÚaröÎ7ݦX†0ÑÜ«‘uOÒÈËU’f³Iͧ5t°ýÀN ŠòMï3móCLQ{s½æ üÀt¨”­­ìj cP™¤(#°ð%G'·Ï}%Ì\©ù+Ýå¥ùRóm>ñýüe1——Ùm3Ú- |1­‚ìmRj¼pM*…4&‚€ D3\ÐïyšäÕÀUòb€¨ôå|J}šà¤—éÊA¦8{™€îáí½<’/û¾îµd¦í©FˆÉÊn ·2A=™äøýêGz«vîzß°l>°å4Ïb•ÅäìGR„@¼ÞгGû<¾áìNqy¥ÛG·ÚªçkXÄ×sû—Î×l¶ykimgȱX"À×K’ ‚€ м€ËÀœ×M3¼xÚʬí&´˜Ü ž«½!Úó¹Ø¿—-¥{už©Ê ,«uïÄN7‚£9íªVø0Oð¿ iCÎ €n{w¹s ǵΡĆñÁ0>Ò­ã–Ú0âkäáë»–#BÌÓu}~Òð1kB­ãµ+Çb€ÔØ_‘@A Fïße™»ïÓždñàÞý™Ððà(£;‹Ã3TQGÞnÐ{äˆë8È G3ÐŽò€«#L¨Ž°¿ã‹¦µÖ#í»ô86àîüÊø«1 Žš…eÔyúhwÍðõaÚ‡±ïÍÖÑ>|ú° :§.¢2îe\îKþXXf¾¬mFÛ/«b—Ö¥)O4èÇO ˆ,‚€ -‡ÿ²?[1³ÿ0í²å‘F¤É{îh7Ÿ××]@\{°ë@w&›Ý™4uãíz¹T— ºÿMà8gxà•v’÷œÔt*äîè“LO²•¶Ð°XNZ4û© ¹ŠªF˜W¯-~·á‚ñùË™Y>£¢µnøÚ2Íå‚ÎLÜ;3žY÷ÎLF8OmêùQàgŒv²/ñ®k½ÅbYn6zƒXJã÷^¨¯dBR님”A@ˆzrÕhÍç›úêKc.ê¡ÇVÞÅ3IÖlŽ-V—«ýË/_±%ôx$òs_Néà÷¸»óT°Ýyªî<«G0I,“§öõ$\uI£öl-bRvŽ×ç˜à±Õð¿ÌÏqû%L|Ëy0z9o;y0P9ç5•ë¤9™ü–{½š[³’Oók>ÍB> ÷Ÿ»‘çš§5Ð5¿ßmX¼ºac]¬Ü/†•øÖ>Ùר†Ái©luNã2i¬k`›(]o³¹ÝÖL¸[³­YÖlÎ×Ú-1å+-¤æÕ-ÌtàáŸmh£ûʳCI¬*×Ðu`F"B$ªÐhTçUØùûfµóZ=íØJ‰npŽïj´aRÍyjÃIÄ{mË–Ð6Ljs8ŸÁ°wC¦€å÷K~Šõaÿ\í¡Ó!k‹~È®Y]óèÓÇ*Ãýÿö®>Š"{Wu÷ä„p ˆ®x ¸®xÞ׺.êêîþu% `PnH&Øš™„3Ü]wuaE]]QuUÄ Wð¼•Cî„$$™î®ÿ÷&éa2É$HÊ«¡»«^½ªúf¦ûë÷^UA #& !°tîH¶©ÕrT˜¤¶ÒÇæÍ¹R“ÆŽE9“"v"i¥üš1ÍWmÖª;6ž¥E'†vpùt»È0‡Ä<¾oIq.º°î¾Ì5]­ý•w'yTßê.aíRM»_ÙÎ_©lvn¿­5]íïvpíRY±ÿZ˜`üÿ«) &Mz§Ó¾Jë~¸´‡tj׿›iÂÑ–2Ì·S:‰¶Žiö. ËŽé´&¾”6|iÓwŠí\YÔ1ETT´³ªìöR9í©Úƒ¾µÃ£öA«D®a‘LÄu¨^"Èn"ÚH‚Kç"ù:ˆ¢NGÈ‘^_º¦£ÄîÄ ±”,òisZŠ‹&‚Ï‘G!¥ š¥ÐW ¥À‡Æ_¦¤,EÙÄÔîÄN_;ã”g§8ï¼uãD¡1”¨9¨m†rø„ ŽH’ºbú±ÉIÉq‰ú©¥ýÒÞ§Æ!§4ï´žBU݇–QøAËõº&–<è}ê•Ç wµ ì{²¢LR£àUš¼&ÞÞ+Šd…óD˜¤FÁ©©Ù´Nã¦Í_µÝŸRiz×ix¨àq‰ø! …¶$v¡ú³]Q™ ¶µxÚ´Aâ eOÃVŸØB=‚²{!3Ñ4•±³xuÇ3`²ö’ª'ÈVÅö‰ –VZÿk+ÀþîÅá•tì*YýWUlµKìM†5wɼüI¥ñãWµÅ¡­Û`æ!þWóÛ¥{#ýÑʇ9År+¡Å þs˜ûÉÍÿP8"Ü´—îóÓµû—O•o,Ÿ&‹-g{iɾ’ÝßïýoÅsÓäW˧i??M¿b|è¡“}¿V¢òCÄ7Ã?a lLªüÂqÔ_S³|…£óÇZ'Õë{éÇ:¶£e\DJϘ¾îšÓf¬{ø´k7`!ñŠrkßµ»ªä´™ëÊ·¦×Œu3NË_ׯµ1aæöÂoî7‘í¦eO9{h¶oxªé'¢Áé‚}žt=ÜÝÕuõ>îéçÜ3ö“@›~­µKžJ}"Ë#¬zÝòó/ÚoÄÍAÙdù,.~ë'xWûê@¿e/¥´M®«Ïòò.ØÕ)¥ý¹Xcó#Xk•ï*~+Úˆi-Q¿5.§8Wª9rÔª¼ò€|½ÜεjUƨտ¨ÖÈÿ3Œ@S8¬–Ô§Å÷´DÕ4aWÝà¸kÑÕ~1#÷ÉxC¦¿[ö6ïX>U›Ú¥ý9scµ°Ž>=¹xoåÌ,*ò{G…óì°ì¼ÙÂz;}²ÿÉ‚³V„••§Ã¼SOÃçÁ$áü郄Þñ¿oÖ=€!Р㺠î>dÃ-ªÎU¶"»J“ú¸OÆœùv]áº9÷˜S»UÙ›”#®…å /DôU…¾Ì'ëJ×ͱmÕ/‹ãQÚ¤u©r¬>°½†>U ì¿uk¶~‘i`5v±ß{Aë·þÃh‘>OX*G£·ËšÒãtÓ–P>Í£eš™7¥n}²˜›#„8I:1p7Ïœmž³—äFNx§;~[j,Û`í|F[iM_Ò²œ’µU7D-ê¶7~ÕÈ1«{í†ä© Ϥ;¡ãîÝßCæ/˜ Š^!’<צ0'ó­´ÉþËl;ðöÀ$x³–ºL޳È0´[šYRýaÙ¹[Žó'M—à Ìz‰òj8Ë:¤xúM›0aßpÓÿS+ f èg $eèÇŠd#yìlsTðÁ‘æÍ½ û*_‚›ûß„rfàfßÏ#â£Zî+²‚¤yýÈ7ŸÝ+5|Èã†Àéó?é¤*)ßË&$`<+Ϭ†uuzßúdþ³fvt}*†ååu¨,«z‡,Jø<&¥¦G À^æçA>&’©7uÊ”v¢4ð+ÍHè^`Ž º^#e×µ.=kmxìpµK»i¦¯¢o*ôegÆ"$Ȥzsot,{H_;pè>qÈ ËKí‡Å÷`y¹Gɳ]…Ò²zb¨ùÔ 5à ȕˆåœŽå™‚ùtŸ9æÍS Õ&$çÖw¸GõÂ=5TN®üýur§”þÿ É8ˆãÔ´ºvI2wl{ìG»Š·ôDx€.@eœF a‹»ÿ¹iZ>HÊ‚¦Ôða€¬žc[Uo½0ÍS‡ü…ËÑ9î?gAþ]"O‘et 7ΫˆÐëCçxÃVr8»i‹³ú ÜžŽÑéÊA}’]¥¡]çñÈË¡ÿØ@¹ó¸+OeÂV&ˆâUxÿQK×(;pfñDÞÞ“Ò¸N&Ilñ&Ë¥cç‘Dœîù7ú|%¹@ÝD–q> Ä.Ø7ŸŽºmãsT½”eƒ(¨§6ß;mLÇÍ÷•ë6ÝéÊ ª™ ÷Çq§A×KŽã¼”aN=>« 7 ý(Nö$ŸGÝ´þo`|Ŷ%®pu@þVà‘ˆP‰[By(É.&ŒÓͼŸ€ ®ÆàSCz cCá;µÜ*}T²)È»Z){úð˜ngt1Î=H†dƒUª>xÁ¸ *¡Rê;wÓñjàMüžšDPCÚ€5êŽGXÀ²ÞK?Š åGœ8ej²ºhžø[AŠ)ÈÉú3^¤†åxƒ£ñ˜.µRçb|×·i•:¿È7aÆ6?¦.¡€¸ÒQ²Ãáj¾©íÍöߊØüB!õñ¢Œ{`ó%MÊ•¦ÝK„ÕÕŠû`/ÜMBÐàì})Öàþr/þ‚÷²¶âå¿xÆŒ³£ö¿žxX„îQ•´|”_‡“NüŠŽÃ‰bEk¥]Å[ï†ì’pÙZ|Á0QhuK*âK'ãæîvÚ¹ °H°#¬VÎJ¸pð¨Š/¢É*áœVTËB. Úô-Þ’;e˜sS*=ûþ¬ÊEÎ0Óßw‘™µ6(g«?€V=^d¦•ÃÂØäêÍ0N)0'}EåiY>Ö¿{,£°˜~¬#U¯ø.펛7r$îeõ§…æøm(™é–ÂÕžo©ÀGd­š7iÂwÐ÷,HÜP”ßC2Û¬·®ÄÃ<®»Þ-ªÅ „p:‘‡Ï@Ç Ü\/ÄõC”ë¬XÌ‚kö‰à56³NõúÓ*,ëw‹GŽÌƒ…³ ÆåZ·ߨ†žßÖ§ ~wã3©¸ôÌw‘y;ZŠf­”‡çb¼£œa°¨u®±Þï„ký¢H9º&K¿c«xÁ¹œz7ŽkD¢6¶(+k+•­÷–3§ç |;¾ á!˜£ yÚç«Rk¾}×èB{ØVê†gÐoˆü§{s†âEöàФd~s^÷7×Pªµø?Ô§F¼ ynR³}~ü¦Ç`ˆ•À¿AµGir*æžß‡>_춆•ErAd¯‘'tîW”– |üîÇáGx:^2þH×î¸pz Æö%–Z´8Ç»Ê(Eó~à\-PóºÏ×Ý©Pð$á>ã÷.©Uˆ‹¸DùR \ô/ÊÉü÷§¨/D‘õb¹ž“?`Y¤œž÷ BH<áù˜0;ͲÅj×ÝOÖVÜ'CD6\6t’ªéžŒTz2îvAÏ+‚ £‰6×½¦cÆèÕi¸×Ý/ãC/úáå|Î0 #€{lë¥åÓKñ€0›«Eó¸2zàá½Ü S<°@QïzÍ#¦úxôÁp¿cYspƒýýaVè*Ó4íT:V'¹¬!‚J2©……ÜøoÆd‘™C½9KlÀÍMéªR¸}]„äíô0#y<̬ÿ0Í;+躾$ ùQDþ—°§PÞ(3;€Ð"ÒÎunßA&žÆ˜0d…Ѓúܹ+ 00cîÜøïí¼°´ªÁ}zv^Y»ábÓ\Šú9x¡FCoƒ—µÍ÷e ô>SÊéÊ—b7,µD®ê$[îÁt¸æÑÃKQÕ§Vj°¢õ!›')qâTÓëSvœá}Ÿÿ¾•ÿÛ¸/'Õ'Óµ>RÞðtXˆÐ yÙL–ûOJ½rÓ|$ü?ã{øÃèx’î‘¿F?^,Ì̤=1<Ë×òœ7ñU^ ÏÅ™XPg"ˆÁ„ôɹô’wÀÓPx¿õï w¡LÖüøîï£4’q“­œ?·ž^JQž‚ß̉nÈ^È^¾ßKÁzÎzSj¦-7ÖW‡{ŒÕûÑçÆÐ;䡯 fráo´,5ã=Œ}à03ïdj‹^,Ñ绀]'móNÜÝä`2iõú »÷€xçà÷'öÓ±Q!¾SYA"[#NX4æý¸×ï?õeÜ#—×GPI…E…^æqÓp{ÓRGZnjÎÔ~ß„ëŸ=cÀ[Ç´ëжS»‹þKùdmÅgº)\&ü|)&"â{Ómö”s¿uóídùjž@3ü)VÀ¡Ÿj§­¦kÓü2!cô*ü6˱Ö%3göÛIùœF i4@¨1é5…çzK â…Ÿ{3&¼÷ÛÿÞëàr^}jqc9’¿ª¯Œòð–},9_¸îdl÷0ò á^Ÿ°µüMrio*0½ïëK}ØZN ¹k3ÛÑâ?Êà?*n ±ÛòíÆWñDTŸ>7È/¥nÇ[q¹[­0ÇûRZ¶bMo¾Ïœõly ìz]jÜòúŽºmÔrW!|Ï–jÉJa­¸!?¥i"|¢Â|Ò·Ô§ò~yv¯w^Z»¡²rç¾þ`Ê7b¼OVˆ½ý&»ýj³ýµP1¸Ï©ïâXWÏ©¢ºµ¬= áß·ïMsPm3Œ[AŠÓqú†°óq¼ÉÍæcmΘ±î—°ÞZ;÷Я@r¦ôþÙ“kÇZ+F”Ü•˜Í}iÀrT"./;Ï)©ç/ÎÉ|Ímµ!k=dò\9:.4G”¦fûËqê„[ïÃeè|—ØŽíew¥é_Qd­§?X©˜ly£ñ§k_0“x@°᥈ÈvQM¾„upŒ{þ?Œïj®ƒýª™dy3îP7º2áG|·3A«åd±¦ôuõ¡I}p«`iQ½yn€Åç¡ßZe~%°Pª,‹¼_l¶óÈ¢Z 2û/Çþ–Và~Ò¾,PÖdÿnêDð3øçdÍ¡k¤ùµIÆK²Þ’|÷ÞˆïEïGzv5#rs;í/sV»'` °ZÍ‘ûøš¦dm•û«^ŽÖÛwß]›?åÀÝóÂ’ŒQo®ÂT/fŒ}+·´"0wóyó‘?rÜÛ=w•l~F…¿ÎÕ–{NŒ#p°„“†ƒÕS½íÅÞ†›|¯˜„›*¤Ô¤–ö.ì=ä£:äV“%ت8‹Ü…p-׺‘5S}»1¨’øU0uÕ<·ÅY¥¶9«/ƒÿ¸¶rËp“þ,ìC;܉DnYSŽ›ûáÑÐ?ΣŸ¶ÀÌÜHu«cµˆïU'º!âA‹~©ßí·Jã3µáP6 ðXl÷Bm—½îÃÈm.xT¦…›k}'è…‡ÞËpe]‚Þ]úw…5ù&UÍ’Ê.FþJ÷A†òwA(nWL3Â+U½=úÎ+áòÂÐ2`}س¿\}€‡0Í®žQ«œ/‚80Ø´í*´ý÷Awf¤þšïë/i²œcÉá˜ÍüMv¡/k*YëKeÝñ"GÖzX׃á"x¾ƒ"Á©«¾kzÃïcb¨LªÕpǯÄoaŒrì—ñ~ ÄnAAŽwEiÀʪyÜzø­ž€vOv¯é¨kÚß6+³Jg 3}.2½o—ì­¼ßÿxA|y±/;\4è‰ÀØŽÃêçµ¼aB1õ!L>xÅûqbLáž› 7ãeu–IY޼Çaù¶ÅÊ»ŸÈV¼|¯O5g£û:+ÃÕ nýEX¯§¼úñç'"ÿ‹`Yt¸ü§¡³ VÒê7‡peGøyØâþõö´&VuJda§vÝRwïÛú{|/oÀ²†ÿ˜7³ÿcdYÝU¼z >½ÿÁ‘{¾‹›ŽiÛî Z% R_3Œ@Ãà~ßJI9C[ª%ܳ»~óͧ×Ö§¡oÂ'ÈŸ…îRXm~K.|"§ˆ4;ï<ÄxŽŒ¬ëI’Âç"Ô»¤Vr)Ó”kX2§ÂwÜ–w7´¡ÔÐÉþ«FdùèA$ÿùf"-xíþ§IÄ¥Š[ñJº´ïioPvw­çË=§C`á}Þ†NxuêõO"Ò’jæž^eòA6ÉîŸ É5r2'+ë{L‚¸ ¤ÁGq‚ˆuÅgÍZw~ý[làJÜEë­FÓ_`f­/òe ÇÃù.XÜFÓľpk=yÂþƬhºÂówu܆\tuÿ4¡§P9Úše´ÑO„uêmü>ƒ—aUu¨ ep…uaíÍÇêãqs«åAÑ•þérSµõQ.ƒã”‡À—ß“Ë;Œüº¢¢Ì³?>xa`7úRŒ}ˆ¬ZŸ÷#$CžYí¹qÇr:[ÓµËá¹y?$W÷ä fÝŸƒr¹Œ~·ÖØfç]„‘ƽ ž“$t¢±;ºµT0ðzAn0õõz?`Åî‹ß÷Û;ƒî±aò?êSÓ<©bîÌþEóò 5sÀc4Øûï¶çéØà|€ªãEþzÜs w—‡,ø?jPxpŒ@3#~jfÕÔ½±°]‡}¥%ˆg<×Üg˜Üst>YŸ^¼Ý‡5ñšŽi—9‰²íà\b7îàO(OçqóF¦U†×‹3ô?Uì È[ê.-EåäîLõû¯åj‘#ä‹›-yì=(zdI&–4˽–¡É6f¹Â2”$”UއÆmxVot®jÖ¡§ÑÈl¿u½›°G,ã4}åÚM:Èͬ€µñR´ô•øÆÖ ro~MñŸˆ‘ê8òIàUŽòÒŠ0±ÉpÔŠvpÁ#«Ðµ˜šæ*¸ü—CÇmN¢Xáö©ÈÌüu¯‡åuV™UFV•8ü½‰üW.ª™´áÊ6v$72Èî˜È²ñnçqm¬ÎÑRŽ¥Cþ>4ˆ•RÇ®ÿö“ó Cä#jr„¶þâ.ÛŒ7-ðz77f­ª¨¦ &–›Âwê¤E“&íA¦“‰æîÙWõéVë3Âà ÜV>»Ä]­NÅ2@²Æ:ZO ÆvZb« %þ¯>qŠWÇo±Öã_¢¼:ü'LðPú¦¦Ö)~§{nª·¯¤{P(%uL~­twYÛ­–ï¸?éÝIo ‚{—×÷Œ-œ+ñb~^?‚O"ê×gx‰§q½å*q,õK¼WºvúÒÍ‹v‘^_˜“=‹ú¿y—I3s?l޵O£µw$ç×¼ààYCKrbCE UHjiyÙùˆ% IË%¥.hH9âÕþ‚ò¿ÐD$«¢¢Í¼ÌÌÑäk\šõö·f6ñd•[÷tMIÖwÍ7®ÌÕ7á5îyø12–!Z"jÆÈ¼¼cÂúrbxês º§ÃÉr¤LMûuúGuj¦§~rÁ«8»2R/êdÃB•³Å³©“;sz¡ßû-êÔÕïó’Õ¤ŽåX¯Bþyû&STå¬Ñ£÷ã:”hé"\Ð_­Tß8°JÅ ck åø9áûjÔ‚ÉQ6ý¦B$•,ìpOWØ¢óÚn¢WÕû­sÔíÃ7ã"¨Ô• µ^¨{a­ÿL7:þÓû”Sy^œ­>^à÷~}0ÝM7§wQvà”‚'½M$`ᆰ.ø2¶Áèa˜ºí™…•1þy Ì…²K»-•;JªãiQNæ µYø@æ®°ÓóAÊVÖ|×ë­‚6çÁÝ>+z|ÖÕ3`ÙNkU[ÄÅÑRU‡Ò‡zC&yn¶ü®ç&‡&®mµ¦Ÿ"eàü†‚/Å œoÿlZoÖdH¿7„ɼq ›xÊ]úû¨?‰¾™øN–Iž•¡v¥œ =c0‘s½êÑéY±yçå°ƒÞ¹îj!ÙN?è]†Ïýb¬}º žšój⇨ÁEŒ#À4Œ@«¸ûa©8¹ánzidüY4t#…ÑÄͧ›w‘9ñ›p‚Úh¥zà6ÔZs®¢[1‹xjDÕC¾œoNØIP]¥d%u ª›w0G²ÒDÔƒÑÃuêA "Þ²‰CÏŠh¤ç ÛR+T`{Ù–À›å 2(€í&CV]²ÖÃâ>.`²ÖïB¼ãÄEØšÞñ`;$…Õ˱ÍÂ,ô _Û@N)ötNaΤ` *…ô`㉫@"oÄ„®Ï+¿/Ù‡{΢Tv~cm-_pñƒï_P‚‡’ëÒáÙ`ƒ2¸_þk‹ªK©Î¡ô!Z›Ar™$á.û0CŸ<7ûUµã Yšõ6ú30| ¾I§—É`Âî|Õ aO¸yÇ]Wâ–Ó ¯o»„–Êh-XÄ­)aÏßì(ÆçJx9–ÑËvëÆzT=ºŒ .¶¬=ønpbFàÐÀ}½åÖF«a³­ÈžÛþgq±n—Y÷HºNì„ݪ°•ú=6xë0Ò$NŒ@^3Ö~òÒ¢/ plÛç®P£8ÁloýÏ>ë¨òÈÆàf­××”sŠ{Ý÷V»h/W¤‹,Šq2ùqþö¨«G4¥Ñzd)Ös‹'÷XZ+Hr#dZ¢ä¹õxnܦ)Ö¾­¦Y‡º¦0-#Õ!¹Ãµ¾ºúùÈ0ŒÀá@ UH*v˜ºÁÙ-<@çêñŽQßC£…Ûmvõé“ýWÀZr¹¦ÔK]+[êaÛìg…­†@¯éë°Œ˜:£%Äo©$5dµkɶX7#À0Œ#‰@«Ä¤brÂfe·lüØö–A¥¨àÁ,šˆšŒù¡ñ5#‚J1 -JRáÆ™2ÚŒ#À0ŒÀá@¡K-Ÿ Çø_K· Üâm´ôX?#3R¶ø÷“iZ¼˜ÇË‚Œ#À0G­BR/_¹ 3E¿nQt#¶âlѶX9#p˜ÐDí­g›»;˜é^•œ’øzsëe}Œ#À0Œ@¬´ I¥Î`iš¿ÆÚ©¦Êaf@÷$ļNiSõ³<#p¤!pܹ½_FxËÖì׳ï§RÜ‚úY5#À0Œ#Ð ­FReâ|ô„öén$»rTùA?°3̹)4c·±ŽÅ*ט·¼)úh}WšYíÖ­ï˜j&Õ—Ïy?>þ3HZ˜ÝÓ.N3z]hXJŠ#À0Œ#pøh•Ùýîð–Oײ”£|îus1€b-.ñŒ†Hêp3ïÔ@Àþ'¶]Ž5³Üv3rs;W–ÛÿÄuþÚc¿j_aNV‹o¬r¤7Z[Tæ¦XôÑÂä°”mÓ…þ†­ì|¬…H;7%€I¯.ô{G¸ºÜ#¶fý?,ÔýHwÏ@¯à¢òã>^5wSü•ë°#YÏæ)\ýÝ0®ÏïšS'ëbF€`šŠ@£Öæ*lHþª=±© îÓPýúʰ—ujCuhvî/,ËzdvUdýÊ2g2Û¦^ˆ}ë/Àº¤&íns°r µ®³±vÑtW1ô¸Ç±«v9ó¶Øï½»TýÌ‘âLÚù'\_†9õxÛQ·bî¯ÃóùüÇÀó#{VbwðßâsǺòÍ“:óe¢Ñ&£y´5¯–¦xµeò\Ô§#F}õ"óÈ+2"7·Sõoý@)ågÌ‹³š'a÷¸¸X=,äMjL¶±òæé5kaF £5Cþi¯œßöWUe¥¯a6~ïCmŸ‰W޳ŒEÕuÏ'Ø8q°cWA{]ÃÛTRݘ”(ûQíÀ‚=çŸWvíc_t0r µÕ}é^ÿÅÿl¡9~ŽôL´ÄvÝy×QAìþS“-*­ªšæ™ T»Ìp:šØ0ºÏšÓg®¿dçqü¦í÷,Åv)=W~8ꤽ‘÷N=Ãð4œ¾Ô¾#±òyxÑûö€_‹íZß]ìóÞíÖ§…íU©õòN¦<’Ñ í·fÖzWÆ=FóB¸ž‡J«8a‹ðµOËö×òxÀ‹0/iy<òç ͬ]} £ÕIŸìût*ÝU½î+ #îúËÎ;—<È{iׯlZ¯GƒÚ$’Yñ} ­ =/—µ¬ÜÃÍiǬÊÅ+×n¤¸JÓ'ç™8¾…?`ã_øÒÚ }q_³q/*6<~[ßÖ¢ é'=n"oÌfkíVŽód.w‡osêzkD²ñ>£™›þóHãÞkzÆBsÒg®.:½5í`SöÖ„ÃçŒ#Ðb´ª%•F1øž}»4OÛŸÃbóÚ!Œª –ÆaWOp¦6¦ƒvÅ¡?´N& Ì•çde}Ê•òìÍÝ#t“Xå¨N´¶šªdãvôíoáõèCýîC 7f¼/£•ˆÔþpQ /-ÿpãÓ5%1ý+ôÖ¨jo YW |™ÿvˤ!ßAógº×t¬öÖ¸_«f E×ÏçŒ#ÀÔ‡Àa!©ÔrýcÓi‰z›“°#ÕdÜŽë<œÜ£¬Ï ©]wÕ8ç‚+Çþë–ì‘Üh°XÖ~0(Ù&“=á:c• ¯ÓÐycú6[Ÿ]‰ú«ƒ£0Eä†ÄƒçzÑã˜Qn6žÊ…837#£ªÚ2‚m·ºn¬ó`såùøãFàÓQg|µqlŸka"õ7XÒöE±›5¡åKÃÓsø¾£ÖŽ;®,ªlMáÑ—â;x³»†%%…Ð<îBŽKÐÞFDõÿÄ7;BßÓÆôRy4/}¯ñ…ŽêñX3ñ#"˜<ó÷>ZC“dý yRÈ²ŠæS¶ŠÏN Cð·áÑ//Ê™¸.ÞSU§?Ž­ÎÅ w ¬Ñã`m~îöÁîïVôèò*î=¡^ßßQ~?Úý©h£ÿ)\¯{M¿[^}”ݔҾqó AëTðEóÖåâJ´ÏÞ<>2ŒÀaCà°‘TwăÆîÛyõ8'çêñêt™˜ÐC“Ú5 £C±Xù)´;4MïŸØùÄö(¿þšñö¿éäÖ=Ô#Tm ZƒjÁtq ©ã¦‹U.Öþ4¬Ï¾NûZ®~rÝÙ¶0“µ_»„€&]à¡v1t­HËö•ÐÚ?F|»}7âþÈbÃé(EàÓ1}VoÛ玮ýÎêhhúÏ„&oÅ‹àpMŠ4X×~¥Ç½6Ží{ü§ãÎCÄ6V˜jb·n¶ó(fɹEêò€«9v•¡#Î{ Èì½#²|'Åá¿Æ<‡ ºNÕfæF²^‚$ΉüÍʵÅïKW–].éÑ/k謵»#&ºáAªÇð1!u²Õ þ®±–4d*áÜsíËA«qC *S"EÓTèÅ#àI(ñ z‰òÖ 3ý}ñÙÝ$ÛsH={k™ËF ¥À ö‘“®¹ÿ;ô†þ"ÒW×Ís‰Â°Ým¤š3ŽŠ«á¦›LÚé!•Ü)éß³FÞßÜ0Ów¡°<{ù&Dµ“¾p¹húÆOÚvoI`@W­×逸ôì¼se͇ÅëÚ9YCñ³ 23w¡¸mP¨æ¿¡Y¾íÝŒÝ\_xŸ}×R⌜þš%áÅq‰tœ!éfÞ×vÀ>aðY=ÿCæüðDqÞC+PbÖu»½–k"\0†sò<à{]Ç㡤s(j£¶ÜÍÓkÀf{ÓM §ëÂó -ª'wLÞâV¨™Xu½ìÑy›ë+sx`½þ &ŸýꤚþP3pº0Ý›{­#œ{’Œä¾vÜ~âpYNÀŠþXõ‡ËÁ®¼[8ð Õ$]Rl!‚˜‘·÷ :ÞššI]ë†6¤I©j¸·"ðì­qAå##À´8‡Ý’Úâ#l 8£Ý¸À.„Ûm ê;°8™4“—–™¹öe{*úRõhrTf[jdC鼡.M*ÅÖ=gšC01¬:9Ž=g' +ð,¤[é/Ø_W€Œ@k"`èK@¶®²mûjx5ž2d¸OÝÔ]Ï*€¥ÿ¸=¥Ö5uK›–Ó°ç¡iº“¦ßÞ✬ÇAsH÷oéE•ÎëóhP~¬ q¡ß„Üû¨g9{ ¿ÕA½>¢…³ÍQ{)&VóÈLÈÞ«îH9ÄH|ÞŸâæc¾“i,Õ×u½5÷™³Ú[ªg±.óxwåöÖ¸èñ‘`G”%µ%A(ôeÏ‹Ô_÷yY0“ö/3ML›B¢‡nÐÇÖX+E49’mß6ý'ÕrÖ×V¸\4}E~ï_ ’þB yƒCœ`fn—FD¸˜8$ŠÌ‰ßàEi›rdHЈhÊè·„e›†YÊ^òU/‘V72V¿¨HY÷š&nÉo·§+£ËÃEfZ¹›ëqìôéÉ%{+gaå²tFõhP-‘UQ¡ú-~0ëyºŽ–4éù§%ªž‚îy3Æ+«Ú­ “OVË«·`ý¼çKèZYΰæ¾Nçá^ºŽ–Âå°‚À޲‚k^õK/o ½”—í.ûpž>н5Ñæ|F€h-Ž’Ú Ó&LÀ“ µD\‚žYŸ\u^¸TýçõÉÕ§¯þÚœË9(¸ü…tÆbVý›x‰ŠÚ±E9“ÞGØÌ³ _‡dM%ÏC¥U²¤ÆãÑÁõxDmò»=gâs|Ñý©óAR­UwöB¬õÖ«¤¸¢#êB²ª’@У!C`X&7ÓòN2çrX{s‘yb° Ê‚?äw=Ʋ 3å×ÇžL7ŒŽ³¬Ý‹Q¾z-ü튓*¸ ya„¨Ú1Z9!j —+È™ô°_¶ÅÚ°:É3óq|甇÷~_r+ÎkykÊ÷ìGx‘êÏ©di}×êdh×™Y‡ÛS…RW(©ŽCÖç†a\¿ÈœôEc}põ»Çê>‰ç¥£(!©ŽêiÍ0易¾rå¢á9òym+¬âåÀ¯#ä£Ào¿¦‰¿#¢£;ðü|±/+t,]ºT_ù¿ïb̯¯Ñ®^àùŠ”ÚÂB_æÚP(™ |¯Â«J{%Åš!Æš^ Ë£òóŇ²­q¾*µçkð]˜Ž¢9®>:ÂB/Ó¼þœƒÏî•:dÈ;¼œÎÃj°ƒ^‹v˜˜îÈþò5#À0Œ#ðCG 7{³Ñ4×$ Ûz¦ÏC"¨:䤳úÞ×õŸIMû;¨ÈïÒ²|£ÇNŸž)5<Ë×õM˜•Z‚‡ï§{¡ÞF_Den TèiŸâ¹¯>‚êʆ¥#‰Ï…> NŒ#À0Œ#дª%ugqe.ÔÍ9Œi£G¿ó|~þ£é-ÈÉ|õX¿òŠ‹+²ù°¡äœ…~ï·T3_Fƒ ®/ò{}5:–¥zýƒ”rÒq"_TVcYÝ \TÌ6Gí­©Sç?‰*m¾›6aä>¼æ Á‚ØÈK`<ŵ@¢>„¼ݧÀzX½šT½â»´;nÞÈ‘•n]”¿îÈ*r5gS^å÷%—zèðiIDAT×£€êÑéYQR"§T6,;÷b˱/Ö5ãg9“> <¤mBd‰˜ûP]'칪(Ç;ÉÍší_ …Sè^7„'ð#<÷¢m¥K»ÔÅ/Ýô¯p,1m”™ßq–9z·#´›…tþ ÄïØê¬þê¼Ðä/a-þtßûu͸~nx´³™YkkÚ¾w¨7g@UÀ¡¾ÝY“G‡/çxǸ×né*5;w^8~&ÚxO›0>£FáB}›æÑ/jT˜F€`Fà h5’š1îÍS`ÑÔÔÆF’W%S WËýY¯Ðï]ò/¯ßx­rÔ(©upI_²ÐÌ¢¸Ò>pµwù MÆ‚Þ`íëØRj¶ÿ·Ð{jP³ýá¶ž£{:{~^(ù KX=*Dü×öú€Äîw,kÚ Vs„2hqMÓHO5IrY8A%AÔ{ׇ>N®!Í ƒÂ´´X-’ê§7Ʊsу?I ¶ãþ{ÜÕGè[ž+è—ø\SÂòšŒg™µ®ü­¥Nù ÓTOm øoÒ5ý¬øÐßúl_ÆÀƒŒ­—¶²ÏB?¶/¼?sHj¨i˜‘_„W„2pËñßïÝs[îÁù0ÍS b\Šj˜éïk[ÎSRè,43?vuñ‘`F€`šV#©˜ìt/XË´§äD‚µÆ•û/»g0ñåIË’d)M‡+8Çu  ¸Ä`‚u †<ô:æ2UÆ«b(.4G”"ïÆÔì) ¤ñBU}"6Û)°ÂƃhîG›óÝ&¨M¤ÙŽïZ<’*6Wgø¿M§¤'Êv•Ï–ít¯ßÿÑþýÎJ×Ç8p†øUj§¼†Ì( ³ûP»Rðªá%ÀO)^„ý²m*oȨvÄÞþ/õcÞZF’GíÙ_®>€ ,&£Õ¢éÙyç8*°Ô &¨„'F€`F e¨EZª©Ñ£ß: ´¢KKéêUòRë%©iÙ~,Aå¼ÕÍøõ–„w’µRçb¬•š y á™e©Àÿ!žq Lª…²K»-•;Jª ·('ó ’9˜”fúúŠ϶yæ„ï` Ô` <1¨§«tßê}.í5KÍÄ«œã ïë[­é§H8§ 'kicíéR{ØÖ‹°’öƤZq³áu»ký_Éû4pòSMÿdÑõ˜uúw»ûÆë Ÿ·÷RÂÛ ? O% ×ë€ËÊše³D¢ž¸²L•ý ù7j",h±üT¯ïß…È3ýiÞYAm]Öçäõ˜ýÿª8Ó`©ÎŠ×“¾®´öýoDçk1dbI´KØÐìÜÛ¤ã¬@œë;õ}Þô9Ú–µ/2“1QlMº™÷W·G$ïžgŽ,q¯ùÈ0Œ#À0̓¼¦-Ÿ,M€¤¶pRÑÛ!%+ëÆ-ÖªJ±ÏÚ KݽðOîæÉšF½¢™¤.¯ ¹±RÉÏ1i–¸zñçJ¯U@ô®^†•n;év¬[ÐÎ-Eˆ ³$y=¬©û`]}q³åÛ飯 h³Ñ%µ¨O´=+úû-ŒÔg¶K‰[­ŸDò ~¬‹m…å¼§¾Ý^j;öU¢â˜CíC´6cÁ8Ð2PƒAÜ¿GD!é¢ S8¼1µ¿´ïi¯Q^uÒžDÌëi°¾ãQ)B7â=)7'Œ9Ö›ˆûÝ+êh,Ou+­kZS1¦ÃâœÌ×””ØŽ³áÇFVÂç8ŸSGôa¾°¾ ÿ«´KçkF€`F€8tÀsZ>Ý3fÕ(¸ã)Tóòûë‘1Šnƒ´LS ¤äÇê¼§ÈL‹SIkŠÆUÈäcÄùÛ›kËKÒ™WEnz·?áÇÔÂBغ§kJ²¾kƸqeáeÍyŽ0ƒ¤dQ–PCk©n©>4†ç=æÔn‡Šuuß÷µ+2Çî¬5(¾`F€`,­BR3F­‡8Å Õ²%‘:¦Ý¬ƒ˜¸Ï‰`F€`Fà‡Œ@«¸û1‹<êZ¢Í–#*c‚Ú\h²F€`F€`/­BR1¹ç³Vfk´Ñ Ãà&F€`F€`Z…¤vLéð,-솗«ùãdF€`F€øq Ð*$Õ4{—bvôA¯AÔºx:&9bF€`F€8âh’J(`ç¤ÐnNÍŽŠ”ŸuJÙõ²BF€`F€`Â@«‘ÔÙ3<‹å¡š´~e¬ˆèJÞ\ó3Ö ,Ç0Œ#À0Œ#pD#Ðj$•Ö/Õu‘†5¯*›ĺ¾0gVÿ¿7§ÎæÔešKãh}ÒútŽÊÏO\ºt)6¶jžDú¢i?ujÛheMÉeæw¤1EÖ‰6Æp9Úu+9ªsŸ9«}x݆ÎaLëä6¤ƒËF€`FàÈB UÖI òÈQoÞ‰-.ÿžwÐçpóÇËø‹fÎìuwìö4ü¸}‘/{tx;Øu•¦É©Ê‘?ÅVšiíS‰vvJõú³¡Ã(ÌñÞï–‡Ó³ýClG=äñÈŸ/4³>tËh›Uœ_?Ú<à#Ù£ËÝ´ÛÔ°ì¼smeçc#"{ xSX]è÷ŽÀyƒi¸™wj `ÿSjbyQŽ7˦|˲}Ðwòâ°ëÒE¾Ì`Œnª7' ;]Ý£¤*¦ ïw1kqNÖãnÝðc4ýD.·Úþû•#®Åz·;ð¥y½ÈïÍ¥ºiY¾[©2„xùP0îɾìÿ„ë¥ýU©5qÉç!¿/{ CÏXhNª³"CÚdÿeØíkôíPBÅ£­[ðÙ}®ŸÍ¦gP¥UuM,cÃçðä*û½7…ëásF€`F€8rh5Kª ÁÜY9 ¾sðI ¹ fpC•´Ò³ ýFa¯M·µ sêñ8?£«ÖkE0Oªòâ}U9nySŽ©Ù>?¶]½ Äk]x=·Ÿ£w7ö]ìËî‡ñäw;ï$©´rihÃ@š.*òeýÌ‘âÌtoÎ%áõ#ϱ¿ü/,ËzƒXYfYÎXM‰g¡ï‚6ž¤Ë”ržåëQ#÷¥áépú0PO7IåL îС¤!ý[¬ÜlÔãºy\€6®t *éª~i$Þ¸Øç½ cºÏ"H^k©ïС\ÚÓ¨{6dößès­—’'‹§ã¨‡¥ˆ¿¦Èç kˆ¸¬Ë<Ìô]ˆ†ÍóÌ ß¡J£cKóæ楤Ÿ#À0Œ#Àüphu’JÐÌ9`ž&Õµ \; *)Ÿ1„¼pæÌ_7V‘o´³+ÍëàÊVZÖo@ ÿešCª‚yR·½.=;ïW&Ö£G‡…•,tµ–Ø‚åûÊ‹¸[«‚D> ¢üÒK{Ûš™Ó9…A€à¾ r×›®£%]÷|¢ ƒ…&6„ËÉ·R×'vJz’òiËSXZ_°…èO×°B¾¸Ð$i^ïf%¤­m-é@eá)š~ríâyg’'y‚;·^·­©hF%' ÝÇä8£¬'‘õ¸À—ùo·Hò!œ3Ýk÷øòºÏ·¾o }ã7Q^ÁƒY+`}íM!®Œ·ãKû7ºnllô2‚ýAjZ-¢ëêâ##À0Œ#À¹’JpÌ͸<9>å4´|´˜ö«Ù\+4íWóóÜ0kVÿݱª„¶LpÈyç¡ËDÆQRe;v¹¶È5~¶ gâGD4Aäнð$»)¥}ãæB€P+׺éf#Òõ”ºÂq¯„2ë9)0Çm§?´T«j¦ÙíûK* US²-Æ{zèºæ$ÕÌ=¤oé‰,‹¦«õÕ‰haW™S~Qª×÷' ;}z2ÕNV“Zv©Uö:å;Ž]¤²Ž…4²-WbuÆëH§h{³`=©¾«ûÉòö^5ˆüËdã‰Hõ­ÂªZ$ m4Ú ’èÈ:|Í0Œ#À0G.M"dÍ=Œ)Súî™—?`ŒH1º!à÷ «‚¤|óâv0±½8~ ö*¬ž¹º®_8oÖÀ³çÏìßäõP C[rw3Ðt3ï'ÇIÝŒ!’„¶ŒÅ¾ÌgÀ÷¶m¶ýéÍ2N%R4M…ÈwÀ“P²Tg2,¼ã`ƒ\EÖÕƒmW í¶e=ššíÿ-…€ôñ®¶×( N²œB]ªQMiGŠÊîÐÕC8Î/”Ô+*z”W<çêÐtme0ÖUˆQ°ï¶ŒÇ1Ìô÷ѼI¶5æ¸:Ü£æ¨âf”^&lÙŽÎ7;«‰ÏjÍ¢I“öе›êÜüÄwg}‘™õ_WŽŒ#À0Œ#ðÃA¾ÃŸæ™– Õü5{‡™“¾€¥ï»m–ïçŽÐÎ)\麦F`9²J¨ÕÃÍiOU‡d}ƒ½s·pdhF½.)ð×"Wé“}¿Â„«ëe΃eÐ…¾¬©C³ó^ʺN ýMÀvX—vs¸ÎÊíÅL>WàÏ‘óðòhçè3"+Ô7ˆiX#ó°Ü˜nNï∔R'°ãUÍ#~ÓUxÿ»ÙÊÍû¬×!wv}ú€ëq«òqÝІLœX\GF“»û¬º\µUºªÆÍQ·c|AWxÝȱQ<®%TšÐ;_B–ê´ûs5Œï)°/“å™#À0Œ#ÀñVKj뢣-ë¼nù«ÁVB®þð>,ð{¿Ö„67`U!Aa¶úÁ'X…¿†ðW&= vô­{M«l[˜I‰Ú¯)fÓÍ?ØãâœIï!Fsr¡/s9¢ú뺶ÚÕ•–훂Z OÓܼXCø65Vã(Ñ„•"­]— ûýBÓ»†\ÿ‹}Ys@=iÞi=Ãă§´¤”¨zVúø3k}dyðZ_!öád·Œ,ß°œž(ãµÍÕKW©A]®¡ØV’«ol–&¯@ÑÂÚþ=ÊK„­ž?½«4luuó‘`F€`ŽlŽ’j(µäêZ|'võdÕ™!ï~LªÇ1ù°µÁ(Ïqóæ¨ ý ¸Ý{`MQy&=Ch‚ìzó¥Ç¸nNVÖ÷áú‡zs¯'—xx^SÎáî¿„ì+wy§Ô,ßDXïæÉJ‹Ôƒ²ß4´¶*ÉÓÄ3:Bv G˜¹½@ ÒŸ¨Þ¦}(V”Ê2rs;“ëÿ²¾?ù‚®]ýÔF¹Uö/Ô›>Šd(¹r…9ÿ‡Ëö©ÙSúPþ6ÇÜýëɽ/;n€îMóÎ *£„zõŽ­('ë!ö¶îŸÒ´« þ V8.X‘ÿcF€`##ÂÝß(-ô{¿Åz™»A@ß Nø‰Ò(Y5aåæ(õb,:äLú$jÙkÃZ¸Ç)>ôãøÎ)“BL0šÝ' +ðÊ‚m€ìm.U¥œéŽ­ý_03ÆÿjÖi½ÖßöâüXz:ßAUiV|i ,änÇË·Ùm áɧ•î*ÿGÙž Zõ³í£'Ý#ï´êoOE à쓺†8Z!ˆ`Ã:Y´ÅZõ:•U–Ûàærâ!Cl"¦®~XGAúU„@ôBf‡Z2´k’S?qåÿŽú„c-…Ü>,G• =8á øÜ+m¨nCcò^„ÚàF€`F€`ºÐZ¢MÙe älcpÆ]UQsÈ-NV̦Ö‘›Û)ªÒz †› ÚÔ“Ìrgü‡—Ǫ¿>9Ú \Ÿ3Œ#À0Œ#À&håto.…$pbF€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€h1þV¾>»^ÿ:IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-ha-vrrp-overview.graffle0000664000175000017500000001422213553660046026562 0ustar zuulzuul00000000000000‹í]mSÛȲþ¼ü Ý|= æýeO6§d7ÙM $¹»EÕ-a £a±² Énå¿ßɯ’ìØÆcšT°‘fô:ý<Ý==ÝÏÿóå*ön´%ퟞQŸ<óÂv#iFíÖOÏ>œþ¼cžýçÅÖóÿ98Ü?ýãè•wGÌ;úðòí›}ïÙÎîîÞõuîîœxGoßœœzpŒÝÝWïŸyÏ.³ìúÇÝÝÛÛ[?p­üFråvvÒä:L³¯oá`;ÐÁofÍgpšâèc—[›Q#{±õÃóÏá×{,º ß_ÃôM»~y¾ë¶ÂΨ…­0}Ažïö¿ö»À¹£FÁ!?Gt Ò4p_~xÞÉR¸åp…~rÕŽZiÒ½öáÛ/ipq‡êùn¯ÉHkª¬Ï\>Sš23ÒäùnÿÐÅ%t³d¯ùßn'œ:K»ánÿË ñٲ݄Ó]_FA³Þ½÷š¹þ¾ÁeüóÙöÈ·mï)á‚´”J)´0Rl{Ú0ß«aц2ùíÛØ½¸ƒíÇA§zÜ“$ކTêòæ`ؾÿÀÙè³/šd_ãpز;ÅN8dòy¸wdw±ÿ n‡—5¼°÷‡£—{ð”ú߆_ŠÇÛ ÿL’«Ë~о :‡iÔŠ†Ã¤ÿœóÇ<ú–óIܽjïÅQ«]9>­?o}r4àƒöiÄ/8Œ±üK¿)üáíA…åKa„ŠJw(ñ¨þ‘³…ôþEà§ruî IZîÿ.È2ï7xéÁmåNQç:¾ž4‚¸rfIøñWÞOÞà{ù?GqxúõºÒ99(·ÍØAÒè^…í¬, ƒÉ*O²70;Cjz|¤•$gDt¨e>HŒ%‚#˜ÖlÛ“ZûÌp¡ˆµFrI¹.K·=^’ž:ñÊÏepÖ Pÿ!e§á—¬ÚïW'5­ãä¶Úô8ìD‡5­“vö¦}‘L–-×¢F´¯ÃöIÐ'Êù™†=ò±jcvT+1|“Âê1”¨ƒ‰ÊÅ^Dq÷£´×àæqhßå1yiQ49ÝÝ»YP!5—ðº9¨nŒr®¹ý~×VmWhÏ /Qœ[8 55=ÓÚžZP #DáF‹bb¬çœ­lî'i;LƒfÔ?E…’–‚³ ˆ°¤>¨ÜŠ+Ð4£aE”¯¨£AYf-'Ú‰°Ñ¨U,C«¨U V±ZÅáM˜‚õTQ*¨ó8ï¢Vpá*µÐ†cP«&QéSj(Q ¬¢(Eµ¢Œ,⫌ `w ¯Xi0q•šY­JK*|F­`tfµB -à¿„nš)PhP­X–Z¸Uˆ«ÑÂrc•åA_náM+˜6aÊ}n¤a ÒÄpkœ úh çR j-ck Â}ÔÞ9 ¯¢ó$n®LGà†Í!ÉqÒm® %ˆ"Ã9³[©­Þlf9¶Æ >Òs©æÁ˰Ùeð”Õ{‘üß‹}1tv|%šj=½gý{áLp%íðgâ{™ÖVïYÅ€ÅÝu½\Á˾HEà7lË?”qŒ‰üÃ÷ÁyÞ„Û¼‰yIò&ÐÛ}(š7Q=5±¥í ë¦A|öWc’Þx^(‡¹æè]~…‡ÄÞûBwôRÓf·Ç™èZÄÇÄ´ï¼ÞRp`k+ Ûö@QÈgб4B`”|N©ÂÛ3>Ž—J2ÃÖ*zŸLR–ÖŠ8Ÿ×ÁDDë ¥Uß¾€Îºé„9b~–̓jn¶–ß-0XF_×`.å‡É3`?ŒÌt¢®»d]wŽDÒCÒ«!=wÞb‡(X1²n¬÷ (…½¡Ç—Hp‚ùÎÇa„¶T*°â)Ü“!8²‘·qsDkMp4¤RÊùVæ#8¥9'nšGk=Á)j1Ærq$¸Í#8¶D‚3Òw^?e¥á”Ycø¶'™€!˜.Ah̽E='EÞAÞÁùªyhç8éïœm½®ÂÎ5ÐÏ '¨, =ž!V‚Ý ‚.“WH>+"…äƒäƒäƒä³äó2š­pe3F’ ŒóàŒ#‘qqqq/ãLÀü·Q;\÷¯Ã 9ùF!{Êì{¯ñØÝLH‘0óš ^C G lú+pJ}‡½ŒW ¬¾íq«|ã¦y)7Úr+ä·ÒÓë÷¦ÖøÂ­‘ Ú)«9¨%¡/‘Æ5©·vn wSÂs ç&ŠYc ƒÝ³zÅfÆ6 ¥a/M“Z]ŠÔ…Ѽ [AãëXó‹ îä‰_†F“eL>žQ<ß•|Šš%~Îo•ÍJ/pÂy ºÞ±²Œ•å ãU`%[¬”䕃ˆú ý'v®[¾Äï¶hA˜˜êIBe1O¥sÓh"Ø|óJÛbµk†^Y'å’@O8‰8yÏ:%[=NÒÂI)}PA!ƒËͲ£Vø–t ÚÝÚMñÙ‚èÁ(Q艺`vP2ª‹¤1Ê#£üù/c›èø$wã÷nØQ¡G=ÂÚokמ˜?…ø§ªådQµ\pësX­@3€×ÎA±f.c³uFBA,…šèXD©GµÕòGª–W´©-lD»û›3zQhŸ‰’”ªmOðb#Ð%£ÔRÊqkh„FK)‰!`V–攦A#X¥T££Dº2»"/„ÕZHÎ%ug§¡q©ÐXBBh\{g®Q‹BcÙkç‚Ftæ¢Öø¤ Q!4.5@­åÂÐØŸ”ç‚8¥‘ —ºœúpD+¸"\MžçÚ.1wù(-5®œŠa¾re 8´4BèYuXF`E`}´ÀjX— ¬fõÀ*Èï¹9¥Ô‚I›¯[™Xu4æÑ˜Gc~ÞX…Z9%Æwub´¢’nt^AŸp*¬qõc8Øöó¹bŒ8´Š«q}ÙÖ—éMŒ!xÒÙâ1š£ çú²¼ªñ÷öZa;›^"È=ð£8h„®hj °O>-R,¨7Ó=¨*"«J%+jJî“|‘sk€¬zÖËŸi¨| u…4ÏYWñ‘uE’Ï{fÅù‹©i¬ˆ¬ˆ¬ˆ¬¸¬µ»_zk¯×‚«Qã.’œû¥Hò¼Žž[Ù:¬™§ÁâcZøÎµ=¨ÑnÙðáZ!¥ðå×çÜP&X}ñQ,¾…<óõUš^.:Lô*nõ m½Oša硉ELÀMº€c)¹$@,Õ€’ÑÅLRK£…Ñ”bª•8ùF' â” `Ló9PqC Ƙ‘b¾TDqb®5µM!¤(4…æ1…~ŽÒð6ˆã%&=¬åœJæLzxŸœ#rrrrÎ&¦Ùå ,7yDˆ2 ÈCÎÊ8XUdUŒc 2222ÎF&v/' ²À8•ƒÈ8÷x'92222Î0ÎÁëý#oE¬jÓÓỽFÁ’”bÅû%E°¢ÏÏ»0 šAœm¥I#ìtVìgÃÔ¨O"5*cY‡~Í‹„lËI5XÀaçc9¥9'TY¥Kµ¾ËrŠFŒ1’ZŽ,·yYRÙêB„ÒÛ^¿d½•†SfÁÐ…û´®Øæ®§•Öi@—ŠÎ/îÂÿ9À!ÚÌ/p¨Œ-á­+´®Ðºšv:YÐn`ÅÆ{Êý;uýÏÂUÈÊ)»­[DE™/qË` šNJo³Hp̆yÃkurì(æ [h\¼ð˜>XÚš€þ¢¸’"4"4"4ÖC£Å”ŠK­çbWŸRQ/­ø‹Ë_C™ö¥´D ÀJe…a‹¿,¬5)¢Äâ/K…Æ%iáÓ QÙ;Ô†+a‰uz_5SÉ$h\ÄGhDh|¬Z#Y.4.éyN…F³,_£âÛž…MÔA¢Ìñž\2p`EhDh|¤ÐX™ì½¿TÚuSÿÛ¶Sÿó—ãsFÍCÆg+}‚Ìód\½‡‰‰èp’'ù8¸Œ‹Þ¦‹„¯op]fp—>ÓRK5 ¦T­ÿpBþkJ„o 5Ð[»SF`þëG”ÿšÌù¯‘à0Šíéæ¿~j„,äjRËt5}/úbdz“HŸƒ¤Ì2®„óÂkß9Ÿ¤V0F·O3v£°Øhî0š6Ÿñ¦”bV«í\Vœ² NÊ%žýLK,{Ð7¨«¨·³A|¨cFù†[ z$—À¥y˜g¾+" [˜fŒ*„:„:„:„ºJ` [êôCiuÚwþ"€5C@6-ô¦Â¹’ˆaŒÀ17¡¡¡¡n51|Ss'™e…ãæJ5>sõ$%Sœƒ„š‰¸¨­ïj PC@×cšÎ°¶H$0Fe`TÆ£åÕˆ«Ë X3 ©’>Œ É)õ]&8W" ±Ò™·½b¾”m¹UHT!Q…¼KbϾÿÝ%ìu›”{¦_ ÛÀÙrÕBó¹ƒNpE¬¢Æj @[BfO†³ö8k³ö8k¿.)¥×¡Zuy¦X×ÚΓˆª·­Ÿ`¤ôˆŠL’†IÒ¨¨Ö(õZUÕ„ÚöS¾ÖTIæ&úµU×õÜçº&7w]Ïz:ƒ««p­Ï²×úà¼ÅL«8ÍR“Ñá¼ÅÆ—=À©‹EÖÎ3Œ~Y´[8ЯZ?Ю’•`ÚUë]Co­}m]ÑHëôR­(b%Îñ>ù9^&jŽWRß-1Ú¨ÜÑjÛB,bÖ¸sHÄ| 8ùår‰Þ†(+ÙŒ¹¢u<úûMÃN½KCù¾q±²u×Tˆ8¾£‡à0õ ¸È±ç³I÷â¨Õ®"R'q÷lÁÞÝÉÚ /²à,Íž ë~õ7M²5/:ŒôÜ¢A;hå~N¯f·IúùÌ5q–(•ÜýÛebº»ôˆwU.Çßß"…ý„O¨Ô€Kœ;‚YÀ%Ós’º"Ò`Ñ<ùÕ>èS.5Ei›¼×Ô÷¢@N—ºŠãù Uïs?Jq vb¢5SgöÜ-TÑØº¿ŽbáÌßeë¨=ëç®`c%ØK†aÇÍ, ‰ÖÕÝ–‚£jí úÀȈ‰¿Ÿ2ÞÉ•T]o8)›àæÔbÇl‚ý‘´‘ë´ÕŽK8å‚J7k13æKœ—­œÁl¤˜=Ü“ºÙż¸ÖÔb †Ð`¸ç\4{Go–˜%·–×jW ¯m¯1m‘××××ÖaÃ[æÅÝÖNÔ^*¿Qß«'Ê¥r7²ºÌ[ŠÑÕs=€1._<í¥­Ñ‚*n¨QÈoˆß¤Áez¸Lù ùm øí}„Oîlk›òÐkË™/©UFh G%¬ èÖ|2nM¦‘‘‘‘×b{§œx¶õ²ÛY¢ýWGpõi¾à6àÜÜÉïo϶‚,8:!®}¿ŸEcS£:øÂKG¥ô­ó K`nQTy 4ãtà:qéè Oqñ'.þ|”«ä¥Áˆã5€º…#޵Ó7È%wKŒàƒ:êkÎéà˜°ˆtˆtn̰jãƒE²LX´ÖgÔ…Ë¡êÌäTI•âòAAAµÔC婺~·½ª‹¸X£_— w™GŒ²Öºø!¾kôWžþC°¤ÿØÐº «Ïm2[uÝ—æ¡öñ¥oyhçëE‡‰ÂõêíÃí¦I‡©÷>i†t:ƒ¯]„ mvƒåQQEsU…4á$Ÿ“´.§À¬Tw£%nf¥Â¬Tk‘•ê(Mn¢&àP/'Ճ䟢ZúÚ-U!BSC¨€¤O©¡`IPpU//æŸùxóOÿŒž¤ÒLS£ÀÔ“zº]¯F¹Z Oi0-Õ|i©¦¤3=ûTfAk ˜Ü0ße¶Tœ‚~e„È×mjî»ÚÉ`‰@ §u`\Ó#_×BV×4Ñâ¤Û\@Û¸((4¾ÑøÞð¨¼ø€F?}'j‚¨UÏ}qc—•„l–äõÃÞ‚Q ÈÆÔ÷˜„l3'°¦TµXÄn'Ì—Œ+IJ¿ðó¹`-ˆ3œ‚â”~NAË=‡w´ùáa£ëðþ\‡›å œ65ÁY¡ðu¿x/Ó¨Ù ½ï54Þ ni‚ó(޲¯Þm”]zÎæ9Å`êãð&Lo¢ðv…~Ƨ˜49¯ä´©qi—V–-/Îü<ǵ0 Q¢õ¬¥†fIû¤Â…>'?jV‹D–Ó¥Fû”J›IÁ>ÀÅ4‰Ñ‚%]Ý/Î|á qÉ´ëoŸB°Oõ`°û ¿ƒ}<Øç꺛…y¤Oç^B}žš¬õ"ðâǺwÉ?âø°LÍ0Ï56ìta0`æ7 `^cUâåBÝêËÁ³¸hÁ”´gõ¸i jŠEˆ«¸8„ü@¿¤Qsô`ƒ«ì=¹ðgëݳoƱ†Ï Ôóß$­í>¸Ô’ŽZDð$·×kؼÞ,Z·ƒëÎi2CûáÝçwÕ@Á~›4>‡ƒŽµÂ;Òúcԉ·€1á4ù? ZáêßLñ¯ á~Òu±34ÿ- ¯O““F0r9(d ø¦Ã3¯Ä„îÆ¿ç3/Z¾®jâ©òÓx´Úüþœu¢í$Žag¶Ëø…·ß=ì¤W›t³ÉC|¯]YX@¥9··¥ä]Ô>ˆ:YI¨)ÿ¼íIF_EI»Ôœø¤Ô>Î/óU»8V¹–f’U.ÆyM‡zoÃvk•ú'`}¯5„qÆDé„yÿ9.0»M®£™Ú \âçIÒS¶^,àí; ='—áHa1ðRñ.iF Š¹«>yÓýƒ2BÕ%;DxTü(ÔÔzÿrO®r®ü@#²ÚÛý.ȲËðÖû ÌàžO¹ãû$ g¿¥Ã4›~ì!]—T€Ö0df>¸Ã¦—ðž>wfhëÄy²à¼?y™dYrõ.Há †ãaDÏêï"N‚l\¬{ßá!dgx¤ÑßI;ˆáº£viàUOÔHša³öD/÷²¿›¯ÿøDIø¿/ã?ß¼NöÄï/sòêrïòð˜ÓçŸ~­}ø»yøþyyþúc¼û÷_ÉÓSöë_~zOöÞ|zµwò´ùùK|»ÿËôË^dwy<U¦ÿ(¸Ó1®9|ñQ{ü¨“ìb†sŒÙ,u稄Gôuui¥Ï´”J 6‰‘ éQ”6€MDÊÆUõšKpñ8¼r k¤ã1Œ„cçù[åP8M®Wqøq,Ï_Äa;t(2A9õþ°ý>¼'·c ®¦¨>д¤F¬Ã‡ZgO}8²*îí› 3¦´=®‚4Ë/Ã9R Uo¯‘E7³hzyg§ íF8gßíè¯n8Ôù§Üý‡NøªEé´ÇþqV¥óSÔ»v2ªïwÓžDþ$«:{ÊþêËuÐaû¿âètCîùJÒ1¤‡qª¶=0Ýóu[ŒêmOs3>9°Æ ŸçYÚ wÇöwã!CWÀ+:FvíÈÑýãfÞ´›©;‰ƒóסï²…$Ë*T¥ÃÖz ÄŽ{\<©¯e¾ýán™Œ*m«yH&ÉUY9õeéJ\£AÜ­¼­‰.›ú¡öø#å¬ä˜OkˆXt~^Ç N/¶þCðšneutron-12.1.1/doc/source/admin/figures/fwaas.png0000664000175000017500000026740713553660047021751 0ustar zuulzuul00000000000000‰PNG  IHDR”â6@ùgAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿÿÿ ½§“ pHYsêê‚Ó ˜€IDATxÚìw˜W•·ßªÎÝ“sΚ¤œ³œåL²1ÁØ`â²°€áãØ–…ÖØ¼d0&9à8€q-É’,Yy¤I“sìIÝ=«¾?z¦5£é(M’tßç™Gêª_ݺU]]uêÜ{ΑTUe&ènÝŸ¨ªêŸ×™tî:Y£ÓÉ’F{îrTŸ×9B¯ ¡wĨŸ™öå z5о:]¯ÕŸ»\EU}žý‘µAú£ª>O¨þ„ÔGÝ>¨ª7†öÇõ“ÛWÇõúsôêÄ?çÙ~¨óÉ9ëT¯g,†ãEõzÆBõgÚ÷5 íŸÛBëu!ÎO0½~rûHþË“PzMˆë'˜^¤ý(ô±´¯Qò÷äûR/ ý9>^T¯Ç¾?Ò$½F¯™~½©€×=E¯ú£ Ñ·#xÿcÓëd.øùœyý¤ãUñºöàý×ébh_èçF?ùû’&éçòúaâšÁë?”žÐý rÿQ£¸?œÛ#üýG¢¤ã™ùkF™´ç³QGãNY£1|NU•ªªR¨¢PUJpãTñyP%_5áôÊ”›§_®„ÖËAÖ©êÌé'­“òÐý ÖÖŒëÏ9?ªNO zoÐå3«—‚è}±éÕÐz iÚ&Š2“z96½$O_£>ôùñ IS;£FÔk?ø÷èóy@š~¼ªoþôR½N/k‚žç‹N¯Æ¨¿6Ÿ'¨^Qð÷ö†×ßNÒ loxýÛÀCD0:@ —4Fà÷í ¯6” èw{Ãëß¾>ß½@ ,Tà3y¥Wú´Aýø÷½>Ú4@±¦ ’¦¤é‰^?Ѿ/ê´AÒ¸^‰ ¤õQgSC ÙÖOIëãýøïQU½!ó–¯^£Ñοµ~"MO¤4@ Y/¡*~ÍPzYsV¯"ô3¦÷?TŸ'|Z–óÒ&¥}‰^/áÏ9I¯ÔþBПïñÎÞù?ýB»>/R}ðûaP•Wz…kBwîüÈWýÆ$À¤Awpî:)Ȳsôë%@’ÃëåsKRd=çêCOÿœlLN|§÷‡Ò”í#ë‰MèŒÿ ¬¶?ãÆ^¬úiý×DÐOn_4Ùê¹z)Jýä‘ó“^^¯9çüDjRNÆóÕË‘ô“ϧ¬õS$½4þ&’^?å÷¹ý³zI9‚>`LŽo ˺ðz­~Êù‰Ø~Ìzìé%i\/Ëþm$4:‚×:«?÷üϻް°ôZøñànH}”úñeQ·^«5ø_šý=BŽAO”ú)×ÛÐ_ÈñÎôù¿ ýB¸žcÖ/œûC˜ûa1ð›ÉÊÀ7ÔÝúærÅçY7µ™\HÃN !eœ†ÑE¡aIR}£ «¶"¶þ„]ØõÄO8’^Š¡ýPÇ¥ ±šó9ùüŸ¢¹¢¼ÞæäúE¿pîgË9vÜøýðÝí ¯—´ÿQUõ7Ê‚<§Cш¢Ù"FÃô@fZ-îH‘?ÒÙ’Š*€…~RI¼Hiw€—_ÝÏïÿô#£ö:å@ —&²,sãö-¼ïŽíÄYLA5’¬9[Ï[õçâôùOÐ\”±èý¥4Qª}:Á¼ˆ~ÏdTžwÐmTUEñ¹¦.óù‚ðLòöxþ?_5U¿NQP•(õÊTýéúV~û‡çæû @ \òÔÖ5ñ»?=‹×íÀçÃçqà;Ç`<EñëþmB胇^ÇÄùém6ÿóÀïðù|1n/@ 8þòìk8xbܽ I?oE þë§éëœ¯Ý @ \v¨ªÊÿ÷Qlö± olóbP¶¶u³{ï‘ùص@ ÁeÍ>Æßž}FÛœƒòñ'ÿ)rM @ ÌϽ° û z)çÜ lmïfÏ›Çæz·@ ‚qcNþöÂΨõ’$…]?É ”$IÖ Išˆúõ¿^քΦ~¶'H²†'žzYx'@ æ™ç^Ø…Ó>8Z’5ètf´:3:½FT°%I>[JG$9¬Q)ÉòÔÒ‰’^/É8ÆœìÞI@ ‚yÇîãÍ'‚×ëÆï•Ôj SJ-†ªï-Ol¼Ü¶rÁW„Òû—=vZ¤ @ X <|YaPÊZ‚ˆÁôã`(1Ôî# ‡‡èô¡“s|š@ ¡8|¤6ä:IŠÞ±8® 1§q†§: Ói@ ÁÜ2j³sútSÐu!c^‚,×Nl AÇ£¢IÒDµƒ }KkÖáù>oALH’„ÉdB¯×c0Âþ9M°Pñx<¸Ýn\.WÈN'^¯w¾»*˜e4 ƒƒÁ€,Ï[í˟χËåÂår¡(‘Ê7 f“‡j()Μ¶\U<d¾¤O™~/Ô6R$&Ú¨jØúܪ¢øsˆN_ßÐ2ßçK ˆŠ¸¸8RRRHII!99­V{á 4:N‡Åb «ÅjµbµZsÂ/R$I"99™äädŒFcÀ€4â÷>Ox<œNgàåÍårÑßßÏÈÈÈ|wí² ”¦ª*>¯ ÖX¦(^Ÿ{švÒ/GEU} JD7Ö­¢*>ühdýàÐè|Ÿ/ $ƒœœrss1™LóÝÁ%>>žøøx Q…žž:::ed/ÈÊÊ"++ ƒÁpá fŒ‰—ºøøøÀ²ÒÒRÝÝÝtuuáp8æ»›—,ƒƒ¡ wEñ¢¸½ãó)ÕÃàA^Åb8~H”‚H\\¥¥¥¤§§‹!kALȲLvv6ÙÙÙØívZZZèììyvƒ¼¼<²²²0›ÍóÝAŒ˜ÍfJJJ())add„îîn:::Äô“fp(²'8Ü(45(g‡¡aaP z½ž²²2rrr„!)¸`, ÕÕÕpêÔ)¬Vë|wIäææR^^.†±/HHH   €ºº:úúúæ»K— Cãþxš xÎÙŒcá¡,RSSÙ¼y3¹¹¹Â˜Ì(qqq¬^½šªª*qmÍ#²,³råJª««…1y b4Y±bÕÕÕâw6Cø| ##ö jcÎ Ê í¨@0äää°råJñ™G<££6ìÇ%;<œ——ÇŠ+Ðh4Þ˜ &4 «V­"--m¾»"˜erssY¼x±0*gˆáÛm?g¥O¤Ì3âæ3ÏØìv:»z°Ò×7@{GƒƒC—ä|¨´´4–/_>ßݸ쨨¨ 99y¾»!˜#²³³)((˜ïn\DÊZéÙ9ÉM#×óÿ! ¿vwôiƒ‚ùD«ÕRYY9ßݸlQU«uˆQ›”ÔT¶oßN__55Çééîaxd“ÑH\¼³ÉtÉý©©©dggÓÕÕ5ß]¹,ˆ'''g¾»!˜cJJJèîîÆårÍwW.I$YƒVcYŃÏ&mtnWIBB 9$5­$!! £R° )**iBæ ¯×Koßn·›ŠÊJ®¾út:mÀcÜß×GÍñêNÕÑ×7€,ËÄÅYˆCw LM¨¨¨ »»û’Þ_Hdgg_2/#‚èÑjý÷“¶¶¶ùîÊ%‡$IhµF˜ø]I kô¨ªŠâóLÑj'6Ž ø‚ì ØH¹2%e¨ZÁ\!†ÀæÇØýýV$Iæê«¯fÉÒ¥Ó4iéé\yõUlÞº…3§OSSSCww7##£Fâã,˜Í¯×r"aºÍvaó“‘™œÃPpy‘0ß]¸$‘dÝYcr²¬ nP©¹xvqT/ÕaÛæI’ÄÍfŽQU•¡¡a†GFIHHছo&###ì6:ŽêÅ‹©^¼˜þþ~jjj8UWG_ÿ$¯e\:ÝÅçµLHHå F!._Äw?;„z‘—‚”%;³4éjÌiÑ‚ÙF’$Q“wñù|ôõàtº(..fûõ×Ç|³OKKãÊ+¯dóæ-œ©?Mͱ㓼–âãâ.*¯¥È* .FBNÕ 2½Qëß@A’4ÁZ ±ƒéz)̎żJàòÀétÑ×?€¢(lÚ´‰ÕkÖ\ѧÓi©®ª¦ºªš~jŽ×P;Ùki1ûçZêtó}è‚Kœ‰ç۹׳¢(ŒŽú§gèõú‹æ%G ˆUñ€ªŸ6íóMÏÌ¡=»‘2Õ…)j[U`|n¤ß˜T„á(\Æ 084ŒÙlæ†o$//oFÛOMMãŠ+¯dó–-œ9}†ššãtuu12jÃh0‡å"òZ .|>{÷îe÷îÝh4ÒÒÒxÿûßÁ`Àãñðƒü€ŠŠ š››q¹\|úÓŸ&..n¾»-̪ªâó:Ñh ¹”ŠÏƒâ å *ªâ#Ú‰“ªª‚ê׋nàòEQúû­8ÆÆÈÉÍá¦oÂl±ÖwuuqúÔ)TEEQU…Iÿ_®øTÌf›·lÁh4ÝŸV«¥ªºŠªê*¬¯©¡®¶–þþ¬Âk)˜a^{í5¾úÕ¯²aÃ~ðƒðÀpã7ò÷¿ÿ+VðàƒâõzyþùçyàøÒ—¾4k/6ƒÂÂÂÀgÿ|å!ü#à÷¢VUUqêÔ©ˆ¹cÁb±PUUÅáÇ/¸Ýôôtt:³rž3‡¢xQÜÞñk5äht‰=±‡Â˜.WÜn7}}x¼^V¯^ÍÆM›¦ÍWMMMåôé3Œ9"¶—_ϦÍ׆4&Ï%%5•+®¸‚-[&"ÄOÐÙÙÁȨ ƒ^?îµ4#ËÂk)ˆUU9pà÷ß?«V­೟ý,‹…½{÷rýõ×s÷Ýwþòòrìv;‹¯×‹×ëÅ`0ÌØ<îäädn¿ýöiËûûûùóŸÿÌàà Ë–-ãæ›oæ¾ûî›Qƒ²¤¤„íÛ·ÓÚÚJooïµõÑ~”W^yE”‘F¡ÅLq`$iAçœþÙlv¬ƒèõzn¹áFJJK‚êôz=ë7®çµWw„m/)1‰k¯½“És_4 •UUTVU1`µrbÜk90`epp‹ÅL|\z½ðZ B£ª*v»¿L°Édbxxð{æ&HHHà3Ÿù ŸúÔ§PU­V‹×ëE«ÕRQQÁÖ­[y衇¿71++‹––¾ð…/̨×ü™gž¡µµI’())ášk®aÍš5¼ôÒK³v~Ž?NKK ###Ü–˜šré! J 7ß|3ÙÙÙÏN§“®®.öîÝ‹Ãá÷¸ÝrË-X­VöìÙ3£ûþøÇ?Ι3gرcǵS^^ÎÆyä‘Gf¤_Š¢bÄf³“–žÎÍ7ßDbbRØm–,^ÂÑÃG´†Ô ñÛ‡KQQK–.¡¨¨ø¼:©))lÛ¶Í›7s¦þ 'Ž×ÐÑÑÁè¨ ½^O|¼…8‹E<ÐSPU•¿ÿýïô÷÷#Ë2ííí|ðƒ¤¬¬ŒÑÑQ¼^oàÅLÅ{0Ùó¨ÑhøÊW¾ÂöíÛiooçšk®!!!o|ãüò—¿ä“ŸüäŒ]wN§3`ü;vŒÂÂB*++§”•••,Y²«ÕÊÑ£GéêêÂ`0pã7òÖ[oŽQ’$n¾ùf¨­­¥  €¥K—’˜˜Hgg'{öìÁív“““úuëx饗û^ºt)ÕÕÕø|>NŸ>ͱcÇ0”””P\\Lbb"8p pß\zƒR Crr2F£‘††ÀŸ8yݺuó«_ý ðß´÷íÛ7ãû>}úôŒ åççÏXÂg×K__?n·‡Å‹såUW¡Ñh"n'Ë2[¶náÙ¿ý-¬NUUšššhjj".ÎÂÊ•«X±råy=„5 ••TVT28håÄñNÖÖ200Èààð¸×Ò‚^¯Ÿ‘s#¸¸éë룫«‹‡zˆôôtjjjÈÎÎ&..Žááa¼>½C ضÙu * q‰ñã-¤§$a2X¹r%[¶l \·ÿñÿÁ»Þõ.TU•­VKffæ´ŸW]uÍÍÍ,Z´ˆÅ‹óÀàr¹÷± ƒ²¨¨ˆeË–qàÀôz=·Ýv}}}ŒŒŒ°zõjº»»©««#>>žêêjvî܉ÝngÍš5lß¾FFF¸å–[ðx<ÔÖÖ’ššÊ7ÞH}}=V«•Õ«W“ššÊÓO?=ß_µ`–¥@¾¾>þþ÷¿>WVVò®w½‹ÌÌLzzzË“““1›Íttt–%$$ ª*£££eF£‹Å‚ÕjEUU4 999ŒŽŽ244Ð=z·{j$]bb"IIIAëÖN´3<<<#CRçâpŒÑß?€¬Ñpíö먮ªŽiûââbòòóhoó?ÄV¬\IVV'kNÐÖÞ6mHÞf³³k×.’’“)..¾ ¾''§°eÛ66nÞDC}Ç×ÐÑÑ>îµÔ‡ÅbùJ/cÞzë-Þþö·“™™ ÀâÅ‹±ÙlŒÑoæøéFœN7²,#kd4²„*kpŒ¹°ÙÇhïêCÖÈTåQ˜ŸÇãÁbñ¿°<ùä“8Î5&¯¼òJÖ¯_$Iäää ×ë§f<ûì³466’““Ç>ô! hnnæèÑ£Üxã˜L&ÆÆÆX¶l===ôôô––†Éd¢½½×_I’Á>ç²iÓ&:::øÝï~‡ªª,^¼8`¤vttðàƒþHyŸÏGuul÷ ÁÂ"ÒÔ)í$åÔ‰‘ÒI²$3‘œH¤ \.LÌ«JHH”ÕÕÕlݺI’áÉ'Ÿ¤»»›+V°råJ~üãnÊÛ·o'33“_þò—sûí·æV566òøã£ª*wÞy'õõõüãÿàꫯfÆ €ß“÷üƒÃ‡ðŽw¼ƒòòò@í¶¶6þô§?ÍØ„|ëà##£$%%qÓÍ7“––v^ílݺ•§žzšk¯¹†²E‹ÿüðÈ5ÇŽqðàÁiÛÌä™F£¥¼¢‚òŠ ‡¨©9îŸkiŸki!>^x-/GÖ¬Yã>ÊÍ7ßLRR£££¸=jZéìîG«Ób‰3£ÑÈH²ä7UUŸ¢àõzñx}Ô6´ÒÕ7ÈÒÊTUrŸ˜I|>_`þÈ‘#ÔÕÕMy™Æ]gg'ªª’““Css3uuulß¾%K–püøq***xõÕWpϱcÇØ´iÕÕÕìÞ½›ãÇO3$ qqq:t(°îĉõiii\}õÕáñxp:QÛ ’¬9[Ï[Eqãó†I4­Þ¶$!!‡4'““·F¥àRÃh4’›› @\\ëׯgllŒÆÆÆ)šßÿþ÷H’Äm·ÝÆ’%KèîîæØ±clÙ²…ÒÒRΜ9ƒÁ` ¢¢‚;w°aìV+úÓŸ())Ááp},**bÆ ÔÔÔ°wï^6lØ@EEÇŽÃçóQ__ÏñãÇimm¥ººš›o¾™üü|š››/èØ½^/}}¸ÜnÊJ˸vûudl¥§gð¡}hÚƒ%1!ŠŠŠ erÊìÔaONNbëÖ­lÚ´‰††zj&æZÚl z5‹Y̵¼,HLLÄn·366†N§Ãçóq°æ 6Çf‹ ½N‹V§A£Ñ ËÒøóOQT|>¯׃ۥaxÔÆ¾Ã'غf6›+VðüóÏóꫯRUUEfffTSE±k×.ššš¢ÖOnw»ÝÔÕÕ±lÙ2|>’$M1Ÿ{î9Ž;ÆÚµk¹å–[ˆ‹‹›6G|â9T¨íÛ·“’’ÂÓO?ÏçcÍš5¢îEˆ$Ihµ¦)¶eTð“‹Röo ‡j)Äâ©Ædtzàâ$77—~ðƒ|ðƒä¶Ûn#99™^xaŠ÷¯®®ŽöövÚÚÚ¨¯¯§¬¬ €¡¡!ZZZX¾|9UUUh4Ž?øs4fff²mÛ6:::B> JKKx饗èëëãÙgŸå±Ç ô¡±±³ÙÌæÍ›CáÉÉfˆ9éìêÁãõ²eÛ6nºåæñÜ…òRX­ÁvR’S"¶y!ì†òò ÞuÛmÜu÷ݬ^½YÖÒ?`¥½½“þ+.—û¼Û,lEÁjµòÀœœLjj*n·›–Î^ìcNL&f³³ÙˆÙl"%ÞBQv‹ËŠXV^JY~6YiI˜MÌ&#&³£Éˆ×§Ps¦)P5êË_þ2½½½>|˜ûï¿Ç3¯Ç}ôèQ233Ù°a§OŸfll,°.;;›ÎÎNžzê)ššš¨¬¬œ¶½ÇãÁjµRVVH‹tË-·R'™Íf©¯¯§¥¥%0²#¸¸e]psO3=c˜C)D ½½_|ðGV»1N–u8SRß=z”[n¹³ÙÌòåË9sæL@¿{÷nEaíÚµ¬\¹’}ûö)OÆb±0:::å¦?A||<ûØÇðù|ôôô°lÙ2€ š844ÌÐðK7Þ|#9Ù9³~ž§-3™ÌÓ P¯×KOO]]]tuvÑÝÝ…ÓéD§Ó‘’šÊ’%KX´¨nêíÍírù½2ãÆ}0’’’ؼe 6n¤±±‘ššã´·µc³Ùâñ¢ Ê%ƒËå á,Y²„ÿ÷Çáp`s8iîèÆhÔc40êuè :rSSÈËÉϿڇËí&3#ƒü܇†8Ó܆M’'&‚ÑÛ?H{wyYé\sÍ5Üpà €?—åÉ“'/šóA[[V«•”””À´ð'ÿà?ˆË墫«‹‚‚vïÞ´×_w¼ãüë¿þ+‡øøøÀ(ñcǸꪫHLL$55•øøøs1ÁÜ‡ÚØ7 §–B¤9CF‚‹—ËuAs N:Åõ×_ÏÖ­[ÉÍÍå‰'ž¬óz½ìܹ“½{÷ríµ×²iÓ&><Íh!>>ž¸¸8l6[À`cÑ¢E zè¡@ðÏW¾ò•óê«¢(ôõ06æ$¿ Ÿn¸ñ¼òBžÁ<”))ɸ\.ZZZüdW'ý}ýALn·›î®.º»ºØµs'ã)SÒÒÒèíéåï/<ÏÈè(™™dee…í‹F£aÑ¢E,Z´ˆáá!jŽŸàäÉ âó)$%Š¡»KÝ»w³yóf¾ñoþF‡ÃÁ™æv´:-Fƒ£Q^¯£4'›ä¤Dž|òI~ò“Ÿ°dÉؽ{7[·nåßÿýßYQ]ÁÁu(¨þ¡p¯—ÚúVr3Óp¹\¹ÒkÖ¬á׿þ5Ë–-‹9PÇf³±k×® /`tww³k×®)£(»w玲­mŠ®½½N7ed¤¯¯Gy„òòrôz=‡ Lï`×®]ÛÚÚZFGG©¬¬Äãñpúôé@¤ù[o½ÅÀÀ¥¥¥455ÑÓÓCNÎÙÓ]»vM›ó)X€ÈÑ`‡1(£+Á85ÆåÁ¥ÇãáäÉ“¬^½›ÍHA°nÝ:rrr8|øpà,ùq]]›6mâmo{díÚµ$''óÓŸþ”ÎÎN$I¢¼¼œÞÞ^V­Zu^Ѥ®ñª7>Ÿukײ~ãÆ)í|ë ý}¨ŠŠOñ—LTUE™(Ÿ¨øÿÆ——/*gåxu‘h°Z§? ““S8}êT̹8].ÇŽåØÑ£ddd000x¸îÚ¹“wßqGÔm%&&±uÛVî¼ó}üá÷ ¶®6æs+XxLTÁù·û·À²‰¬ v§ ½^‹Î Ç ×“g"5%™ûî»øøx¾øÅ/~[¶lÁf³qÏ=÷ð‡?üâÜ,δv¢x}xt:ÆNFídYÌcœ0(χ ƒ2Û“9×Ë8QÙçàÁƒÓ¦Œø_Þº¦µÛßß?mßíííàŸÉ(ŠBCCÔûÝdÃ5Ò1Š:>9ò‚L3Òú—+HÒä ÂRÈ Îêå ËCë‚Ë™cÇŽ±råÊiÑ’n·›¢¢"ª««cß¾}ô÷÷OÛ¾»»›üã¨ÉÁÁAžþy¼^/ÝÝÝ:tˆ+¯¼ƒÁ0%X(ZFFF±a4¹ùæ›(,šž¦'=#7ÞØ±-FËWlcÉÒ¥µªª2Ð?@jZê””I¤¤$‡œ[ ˜Hnn‰‰Iœ9sšþišsKÄuuuqæÌG™GÂd2QTXÀ‰“µŒŒÚ8ŸéšeeeAS-ääê3Ý·¹¨$*@$R¿& =ŸÏǘÓø+àè´´ ¹™™457ÓÓÓÃâÅ‹§›¸¸8Þö¶·ñûßÿž{æÎnÜZ-Z­Y«axÔNBœŸÏ‡V«Åh4Î{ŠªÊÊJŒF#G×~6ŠâFfº“Cñy§-“Úê_Saüæ0%ˆH’¤)Fh¤´AŸº÷{tt^XíOà|e™k®¹f^ûPQQÁm·ÝÆÏ~ö³ ’Åb á=I’0›Í*“Ñh4èt:œNgÔýRT•þþŽ12³2¹é¦›ˆ=œû׿þ–æð‘¥kÖ®eýúõA£X}>/=ݽttvÐÙÑAWWF£‘w¾ë<òÛé•|ÞþŽwpøÐ¡@‰¹Ô´TrrrÉÉÉ!77‹eê|Æî®.jŽ×púÌi¼^oÈ>&$&r×]wEŒ´MMI&--½ûÞdtÔÆ‰šÚÛÛÈÊLú .6›‡zˆ²²2~ò“Ÿ`³Ù褱½³ÅˆÙd¨ײ¼¢œŸþìgädgc0‚¶¥( <ò?þ8ÇOf`x‡Ã‰Ýî$3%‰%ÅÄÅÅñ /ðãÿ˜;ï¼sÊð\“””„Ñh¤»»{Þú0“ pèСùîÆEÍßû<ÅE¹Ó–˲îì\vÅçÅçþœ ¼Î©ª ªh‡ºýouÞ¨õÁåÌÊ•+iooém f cr­ás™H-‡ÞÞ~<^/K—-cÛ¶m ¬­[·ÒÖÚvrý[p¢¦†ªÊJÊ++q8tvvÒÙÑIOO¾sÞlsrr‚wƒßCYTTÄÊ•+ÈÎÎAâa>AVv6CCCœ©?V72<̱£GCÉK’DA~.n·‡W^}mÆòy qqqÜ{ï½<ðÀìÛ·%K–0æt!KddYB7þ›¨?s†Â‚‚mI’4~}û0éõÈÈH’Œ$ÍáŸs¨( øÃؾ}û¼“@Ð ŠâEqÙÆˆJHÇGñ™š7)ôz=ªªÎJyÆóÅnw0`µ¢Ñh¹þú¨¨¬ˆj»””d–,]ʱÃdccc:|˜Cã‰×Ñœ’Â`C[§ÓÏŠ•+£ê›Ïçãõ×^§¦æxTú7÷離ºzZ¹Á` ¸¨úú£Ïó'¸8™˜³ÜÓÓÃ’%K0›ŒS¼ŠŠŠ”Áãñ„ôPªªJVVçD…+Õ¿mœÙØæv»&??¾[ ˆU ÿb-Ò ³ŒÛíæñÇŸïn°ÙìôXIIIæÆ›o!5%ržÇÉlذºº:Ü.KW_sVNÔœ`xx(êvL&yyy”—/âøñšië“’RbšÇg³ÛY´hååȲ„,k%IöO{$ ’$¡‘ýUÁ$Yž–Z()1‘ì¬LÞÜÿƒ3èÁñxùÉOb2™xôÑGùð‡?<ß§,,‘j7 “¥@p™¡ª*­¯×GnNE… Xéî鉺"Ìò+X±råO$Iää“_ÏØØ}}}8Æø¼>²³²HN íq 6·ôB+ýD‹N§£¤¨ÖövêêNGÔ÷›³Ù—¸1äz 5`hJ(ŒT.Æž3Œ¤øTIõâÿWR‚–T_mü*ÒÙ¡YIšôYšöY•_.£NÚÆ¿îì6S?s¶‰Òº릠†ý(Å:-*¢£úωâCR¼ãþÏM–bÌF=Õ7þ(ÿkTUE«zÑ©^ìiiialN‡ÇãÁb42æqãñzÑz5ØÝ2V+ÿöéOóì³Ïrß}÷*Ä;vŒk¯½–_ÿú×<÷Üs<ôã¼Ñ………<òÈ#8NÞùÎwrÝuב‘‘AVV½½½¸ÝO¥¥¥ñö·¿}Ê2»ÝN[[o¾ùæŒyÂ/^ÌÒ¥Kyì±Çf¤=Á¥0(‚˯׋ÍëõGŽ›LTUúçˆuuõ084Ö3)å‰Éd¢ LÃdÆÆÆå"'“ãPüùO^N¢?HÊ¡`XÍa׫HøðIþ€·Î‚ǼpS]*Ô&¦ÆT#[]·žÎß|Wo»¢¢"þë¿þ‹¸¸8J ²9vª ·ÇƒF##Iý8Ýnn}Û۸馛°Zq»Ý¤§¥a4©;uš§ž|’‡~˜ºº:Ž9ÂwÜËåB–e~ùË_rôèQV­ZEvv6_ÿú×9zô(kÖ¬‰i˜^£Ñ™™I}}=###€?°íŠ+®Àl6óÒK/Íȹ,..ŽÉÛ+¸ô‰ä±ÖNR"OªÑ) PÌzà"E–eòó󉧹¹›Ís‹…ªª*> ...¦··7êïÂÂB†‡‡g<:SUUœ.N— ­VCNN6E…ùXéêî jìÍ$VkpCN YÝÚÒŠÓ5FyytD¡ÈÎÊÄd2ñêk;c:F«)#j­`a#é ä~âP=.Î|õV¾÷½ïqÿý÷£ª*ùÙé´u÷"#¡ª êuô2d³g2a1šˆ‹ÓÓgdÄng÷Þ}ø|>òóóIOOgݺuŒÚÔÕ7#I ×iY±r ø|>ŒF#/½ôõõõÜzë­ÄÅXÒóÀS…ß|óÍTUUñòË/úZ­–ÌÌLTU¥··7པe™äädl6[àÚG–e†‡‡IHHÀ`0 ÕjIMMell,P.V£ÑžžŽÇãa`àìoW£Ñ””Äèè()))Øíö@å.ÁÅ$kÐj‘ ÅçÆç~Ï ”“C ¸<”‘(KSß#é‚‹‘üü|n»í¶)“ç_yåöïßS;ÅÅÅlß¾ÖÖVz{{)**â}ï{¿øÅ/¢2(333¹óÎ;ùÃþ0«é>¼^^¯‡$a2š¨ª(ÇëõÒÕÝuphVæS…È›ùâ‹/ràÀ~/^BUu5ƒ}ûöñÖ˜Í&Š‹‹Ñéb.ÿC¶¸°€Þ¾>ÞÜÿVLÇd“M8µfŒQo!¸t ïý ?»½^Á` 7#•þÁaN—¿”ׇF§Å­•q8= € mm<ø½ïðŽw¼»ÝNWo?u ­8œÎ@…¯×‡ªBAv:%…9|üãÇb±`0øÅ/~ÁÇ?þñ˜Êɸ\.ŒF#¯×Kvv6·ß~;ñññ óä“OÒÓÓƒÉdâŸøÏ=÷ÇŽàšk®!--_ýêWÜzë­ð‰O|‚½{÷²cÇ’““y÷»ßMZZ¼üòË´··Ç'>ñ :;;ÉÉÉAUUî»ï¾°ó‘ I’ÐêLL¶eTŸoê)­ƒ©Æä¤–‚f V%Ç¿B† eH½@°ÀÙ¸q#v»_þò—x½^¶lÙBAA‡Ši®Ò‰'hmm Q-ä )0Õk©ÑhÈÉΦ° ëàÝ==83çµ,((¤¤¤$h…«u]»v±gÏ^ôϵ´Û¼õÖA6nÜÓ¾, …y9rŒ®îØê³K’D‹×ˆ$& ]’ í~†åË—`6›ñx<,)+¤¡­‹>ë0Gã—%$$Üýàûüüç? E7µvPßÖ‰A¯'%%YòÏ"õy\n7}ô ²qe5ŸúÔ§ÐétFÞ|ó͘ 0,Z´(0Ï8++‹%K–pêÔ)¼^/²,së­·âõzyüñÇÑh4\wÝuÜ|óÍ<üðÃÛ~ê©§¸á†HOOçÑG x1¯¿þúÀð½Ûíæ¶ÛnãÖ[oå§?ýi`[£ÑÈï~÷;L&“0&/dYG0ûPÖè‚”¡bœN2Åùx ¡ÈÈÈÀårár¹ðz½¼òÊ+u’$qË-·pâÄ IMMeëÖ­ìÝ»—žž X¹r%Ï?ÿ<ééé¬_¿ž—_~™´´46mÚø½ƒƒƒüóŸÿü†õë×c±X“ì'ê dgg³aÃÜn7555Ô××Ïú9ðù|Øìv$IÂh4RY^Ž×祻»—«õ‚½–zŽ[n½•Þž^Nœ¬áô©ÓÓ† }>oÀ˜œàð¡C,]º4j¯NFz‰‰ ¼¾s7ŽñDÓÑ"É2oí“FµMœxA¾>ð"·|ûÛ€ÿ·Ýn§¼(´äDN7µaw»‘eÂsèºšòòr<]=4vtoÁd4šGjR"YfÌ颽·¡a£¶1?ͦ•:Ìfÿûß¹á†bêïš5k¦|Þ³gO >vr²¿ÂÓK/½¨¥––Æ•W^I|||ÄDýN§¯×‹¢()>Z­–¢¢">Œ^¯G¯×ÓÖÖÆÚµk§ÌµddfpÕUWó‘~”ë¶_ONnøJ"^¯—½{ވخ,Ë”¡ª ¯îس1éóúøÇ ÏÓÑÕ‰3%­F¸(/E >ýC~ðƒÐÙÙ À[o½Å[o½…F£!%1žµË*YTKFr"­§Ó…Ng䨱c<õÔS8NÚºú0[LÄYÌ”äd’ž”È“O<Áÿûîw9ðæ>Ö,©&3-™¸8N·›ÎÞ†‡‡éíí¥¯¯/¦—³G}”ï|ç;üíoü)& ʼnaîÉe'þ?±.VÌf3²,³bÅ î¼óNî¼óNV®\‰×ë’콿¿¾¿JÁL¢¨A°ƒFkÁ?ï1ذt¨‹;´^ ©.FöìÙCkk+Û¶mcûöílÚ´‰;vpü¸¿Kss3K—.üó$ÇÆÆ(..f×®]äææN™4?A[[Z­–²²2vïÞ¸¯]»FÃOúSFGG),,œâœèÏo¼N§ãÞ{良¼œ½{÷Îùyñù|Øíì€Éh¤²¼ ŸO¡«§«ÕŠ¢œÿK¤V«¥¤¤˜ú3g"jëêN±|ÅJ22‚ʘL&Š ¨9YK[[l^I’èëëåÝ»ýÃ’yå¨ZCLm.Œù•Œ.¹Š/|á ÄÇÇsú´?…TUU<ð‡ìŒT²Ô³Ù¬V+?gÜ‹os`w:ILŠ#Ñb"9)‰÷½÷½üèG?â¾ûîãÇ?þ1»wïæÛßþ6C6n—‡ÎÞ~Šó³ùÈG>Âßÿþw, eee1õûäÉ“lÛ¶-[¶¼‘ƒƒþR¦´µµªó át:q»Ý$&&ÚIˆÃÕf³áóùØ·o¯¿þú´õ“Û\:(ŠYÕŸ5*ÇoíŠoú”¯Iµ¼}SæRFŠÚöë5“>«Âp\’´··ó§?ý‰ôôt¶oßÎÍ7ßLKK ###455qÅW@AA»wïfÛ¶mX,233CPÑ––Fooo 2²¥¥%h_À_y¥¯¯oÞëSæZfedP—ËàÐ0ÝÝ=!nÂÑÛÛËߟžáñù¦áPU•];wqÛí·M[—ššBzj*»÷ìet4¶È|Y–9vì õ X,fRS’9%¢»/m$‰ô[>wôÜ.‹’ÒQ<.j¿q;ùË_xï{ß ø«ëx½^\.ŸúÔ§¸ãŽ;¸õÖ[éé@«Õ¢ÓêHŒ³ð—gžatt”/ùËôöörï½÷úƒT¼^-fö1‡ýFÚ]wÝEee%_üâùÒ—¾sµŸ={öpÓM7QTTDss3ÃÃô··³víZ<†Õ«WÓÜܬ¯¯gùòåX­V, ¹¹¹ôõõÚu»Ý$%%Q\\ŒÇ㡽½ÚÚZV®\‰uû,ÿú¯ÿÊòåËÙµkÝÝݸ\.Ö¯_¢(ìß¿Ÿ 6°nÝ:€˜ª£(Š1‡ÞäQUUTpÏçÃîp`w€Ñ` ¢¼ Ÿ¢æZ*Jt/œ:ž[ÞþvdIF>§l¢,Khü ‘%i|ÝÔs I…ùy¸=^~õµˆóÅ‚œe^~éŸØl6RS’‰÷ÏÑìÑÍ~^LÁ<#IhRÿw­Ñú³hµg§9X­V~þóŸ³oß>t:[¶lA§ÓáóúüÓÊd $£Ñjùð‡?ŒÅbA§Ór·Úíö@`A¯‹µ‡A9~ü8[¶laóæÍ477ðì³Ïòö·¿k¯½ðß‹ž{î¹À6û÷ï'))‰·¿ý팎ŽÒÛÛ;å·têÔ)–.]Ê{ßû^þùÏÒÞÞÎË/¿Ì­·ÞÊ-·Ü2îÁïÃb±ƒòGQ¼(.Û¸Q 9zd2P¬Æ¡0&—&f³™O|â477óì³ÏâõzIJJB–åÀP´¢(477³|ùrš››ñz½466²jÕ*:;;#F9N~PõôôP\\Lff&===¬[·ƒÁÀîÝ»gô¸$IblÌ1«éd¯eFF:ùy¹ ÑÕÝËØXø9ŒÉÉIç½_ƒÁ@IQ!gêhhlŠi[I’fÇŽȲDVfƒß ðHZµçWÖQp#Id¿ÿßyíµ¿a±Xذa>ø »wï&ûýÿNן¾Ãÿù?ÿ‡|„Äd|>z!»ÓɦM›øÀwòùÏI’°Ùlœ”DsW/Š¢¢Oš^__Ï /¼À²eË"þ&{zzøÎw¾3e™Ïç㡇š²lppßþö·!Ûéìì »¾¥¥…üàS–9üñ úáááiý\ZøŽ¡³Ë‚8^ýun¸áÊÊÊ 33“ááá@î6ðÏ£¬¨¨Ì—lhh ºº:à)ÆÄüw¼ãŒŽŽòÇ?þ‘°lÙ2î¹çúûûIOOg÷îÝ3žûÑ`ÐS[[Ç·¾õ_Ütã ,Y¶ Ó ÕÃçóáp8p8 Ê•¢* ]=½ D﵌†¤¤D²³2yóÍ·Œ1W§,ÉÔª¥öäIL&#éi©Sªõé’ýe—q˶Òçã{ßû’$a*YÆ¢ÿ~IgÀ\¶œ†o½‡ÆÆF¶lÙ‚^«Ååö0:æ$5;“_þêW|þÞ{q¹\ðÐÿþ/Ý=Œ¹Üx=^²3ü•}þïÿý¿X­V¾ð…/Ì÷á ç0(‚0:tˆ¶¶6Š‹‹ILLdÇŽ´¶¶NF=}ú4f³™S§N~ƒr×®]œ8q" `×®]ïœÍfã™gž¡°°00,>::ʯ~õ+V®\‰Édb×®]œL±ÛíìÚµkJRó#GŽL Ú‰†¤Ä´ ££6þôècðèc¬]»–+®¸‚œœìYóZN¤_Òh4d¦§“Ÿ›Ãðð]Ý=8Æb‹¼žŒ$Iäæd£ÑhxåÕ×cÎ}'I¯¿þVëII‰$%N÷DöŠáîËIÖ¸îF׎§õ™ôûðÙ©ªªâúë¯Çëõ’™’DkOY¦©³›ŒäDžzê)´Z-ޱ1:{zéblÌ…Çë£07 ³ÙŒ×ëåöÛoŸ’EB ¸Ø¥@¾¾¾)“ÕÏetttJðÃᘌÓßß?mY]]uuuS–Ùl¶ <Á–9rä¼).ÎB\œÇÃ訃ràÀ’’¸é†X¶lé”ê@3Éä¹–ƒEe¥¨ªJwO/ý1y-u:%ÅE´µµS[w*æ¾8c¼2^ª.33=¤§¶W—<+çBpäEKŸBkk+cccȲL^v£Ž1¬#£x<Nm½ý ‚×çÃír36æÂ>椺´8‹FÃÝwßͳÏ>s„·@°¥@p£ÓéHII&99 »ÝÁ¨ÍÆc=Æc=Æê5«¹òÊ+ÈÍÉ}¯¥,“žžJ^n6Ã##tu÷j‡">>Ž‚¼\òn¹åê[:éî·"Ûh´þ©>¯·Ç‹ª¨¬®^Djr"G¥··—?ÿùÏÜ|óÍó}8Á! J@€$IS½–6;‡æà[ILLä†n`Ù²e˜Í³äµTŽ1Ž1 =‹JKPQéîî¡?È\Ëìì,Ì&#¯ìØ9­ªN$dYfßž=tuw‘OrRbDƒ9ÁgcóèQzu)7—1¬9ÿšË‚ÙÚ‚ÞÖ*ÉHª ¨ ªþšfÓ>söóDJ·s´“ÛðæœÏ“ÚœøÌHa?Fž›zîöª¬E•5(ãÿúÿ´*ÐLø$yÚ¿Šäÿ¿ I‹WÒp¡Ä¯¸’¸¥[{ù·<ÿüóÜyçT/ÒS˜›IkgcN7^¯Þ@vz ©É ˜L&~ó›ßðç?ÿ€»ï¾›ôôô î‹@0›H’vN¿v’9†<”~ýä<”ŠÈC)\èt:R’“HNJ÷ZÚyüñÇyüñÇYµj%W^y%¹¹¹S‚Vf—ËËå/q—žžN^n.ã#tw÷àr¹).* ·¯Ÿ7ß<sÀ’Çëå•—þ‰Çã!#=-f9Ãcåêá´²8i*aLžÛDçŽäBd“”†7Ð9‡çtßUÞ¨Kq‚ߘõJ~ãrâÏ+iñÈŸ5g—ó'¡’ê&Í3Dšwˆ‘Â8ögÑjŽgùòå˜L&TUÅív322Brr2’$áp8(,,ä½ï}ï‚Jÿu±2ÓÁ‹‚³H²Ö²*ø|n|Þ0y(åsÞÔ&*á„2cÕ ‚‹‹©^K/£6GŽåСÃ$$$pý ׳bÙrÌó¬ì_Q”³âz=e%%hµö½y€Î®î˜Ú’$‰îînöí݃^¯'';sJʦ˜ÚB¥ÐÕEž»‡zc§Œ…3âéŠrçØ2+°§—‘Ø~„äæýÈÞØ“Ç_ªØíö˜ J êE§z£Þ&ú´4Nž<ÉG>òýýý¤¥¥ñÄOàp8ø—ùzzzxÏ{ÞƒÉdbÇŽ|øÃÆä !raÎ’$¡Ó™ÎHãùYUŸojP¨Ö¿A¨âßRÐŽPzI’ƒ”!Û:ö¬×ÒáÀ6jçÏOü™??ñg–/_ÎÕW_M^Þ,z-Ýn\n7½>fcR–e:HKs3ñqRR’gä!®Q*Æš)ruPk*¡É3g©…TYÃPÁjF³—Ôü&‰G”X¸_z “™™9/û–e™{gÃF£A–exà¾ÿýï366FOOÿøÇyñÅq:|ðƒŒÉø„gxXxìgY£ ZË[Ö胔¡ü³m‚¬ˆñ¦)ÞÀ‚KI’ˆ³Xˆ³Xðx½ØFm?~œ£GÇön`åÊåXÌ–ùî*ª¢ðÏ—þ‰Ãá -5…¸¸™ï—Añ°Â~ŠRg5æ2ºtisv|>EÛÉ[AJÃnâzct¿”èèè ¨¨½^?/û—eyŠ‘ø…/|#GŽ`6›ù¾€N§ã}ï{ß|Ÿ¦KމJ?‚™G 1ú"ÉA²œ×T56#QÌm.9tZ-ÉÉI$%%âpŒ1j³ñÔ“OòÔ“O²té2®¹æ*òóógÍk‰ÁÁAv¾þZ­–쬌Y72â}6Ž£_›ÄqKƒš¹«¬ã1%гä&†GV‘zf'ÆáŽ9Û÷BÂëõrúôi–,Y2ß]üó‘×®];ßݸ¤Q…ÚÚZ1‡r–9Q™~¾µ–ÕÐùèÁ¥‰$IX,f,‚æQ›“'Opüø1âââ¸~ûu¬XµŠ8ËÜx-eYâĉœ>u ³ÙDZjÊœµiÞ!®~‹v}&'Ì¥ØåÙ©B gB«ïÀÒ×@jÃ.tŽÁ9Û÷B¡«« FCUUÕ|wE0Ë(ŠÂÑ£GÅp÷,¢øÜh4úiÃÞ>ez‰€‡RU}S\›‘¢¶§ëUa8 —9Z­–äñj3ccN¿×òégxêégX¼d ×^s5³fàIÀ«¯¾ÂÈð)ÉI$$Ä  ÑdÌà¸És÷ÌɹÈs÷ãî£Á˜Ç~ßÜVÚ±§—âH-&¡ó8ÉM{ÑxοÑÅH{{;‡ªª*Q}æÅétrâÄ ¬Vë|wå’FUU¼ž1´“s¯'|”·ß ôâß"²ë8V½@0_(Š‚Çã–9D’$Ìff³i|®¥Ú“'9QSƒÅbaûöëYµjÅŒ%Øív^}åe$I"+3ƒÁŸÒ§CŸA¹»ìOÔæÉd…ý&%¶ü•烌Â"g+¶/qÊ\ÌpÞ Ô9ò–ª²ÌpÞrF³ªHj9@RÛ!$壙/zzz¤¼¼œ¬¬,M}‰ ( mmm444L)+˜=Å‹Û5:îDTBN/2‡2VãP“‚…ÏÈÈ©©¢ÊÉ|àŸk™Hrrb Ï3Ï<Í3Ï<Íâêj®¹ö ÐhÎ/õŽ$Ë46ÔsìèQŒFéiȲ̀6‰ãæ2¬Ú©s»tiô%&³d¬žçÜÌ5´Y{Iíl%¡ýÖÒ-Ø2Ê™£€p­kéfFr—“Òøñ=µ—ͼv·ÛMMM gΜ!++‹¬¬,ænn«`fPU•ÁÁAº»»éééÁë½|^ŒªÞ€•r—}}} \œ;ײîT'NžÄl6±ýºëYµzññÑ{-eYæÝ»éíí!)1¤¤DF5fN˜KéÔ…®<â•41WЦÏb•½–xŸ#ê}ÆŠÍfÃéôçŠÔ9‡É<ñôq%²¬A’4“R©áZF–µH²•þ…ß`tôò»a n·›þþ~222„Q¹‘e£ÑHB|½·Û͉šìxí5š›šHJIÁb1ó—§Ÿ¦¶®–øø8|9ìIZM—. … vqÉzZ Ùx%iÞáõVšÍfòòò€é=$TŒ£Ý$tCR%\ñ™0WiŽ$ W|#¹ËQe †Ñ$‘­C ¸,¹áº$'MÅ“d-zΈFc@’@ R™+0ä-ËÓ°áRô“F–T%´^ y  F£‘eË–‘˜˜8ß]D`Âki³Ûñzý70Y–IMIÆb1sÄRA£aæç!Z”1VÚO‘á™ù”$v»ÚÚZƒçˆôâ°–lf4«jΫŒi\RšöÐUsÙî?Á†¼%IB§›v/òyø¼S§$Èþ BÕò]³{&– óÓédÿþýÔÔԈᮎV«%))‘¼Ü2ÒÓHLL ';‹evƒ)첉Ýñ+8h©Â#EçÍVU•ááሩL, kÖ¬añâÅASYi]62j_$ïÀ1Yç6pÆg0ÓWy-mëîÆ‘V2§û Y£úb+ËÓ+E¨åMøZÞR Âé‚DWW½½½äçç“››+">8y-ç’C6=úT–ÛO“ë>¢( ÝÝÝ´¶¶2::ŠÑh¤¢¢‚ŒŒŒ°mçä䞞Ι3gè蘞¾È`ë#çÈS8RŠ(Û†;n-)t-{;¦Á6Rëwb£KÁåHHGai93[Ë[ £ >Ÿææfš››III!77—´´41ÇRÀ)éy3n Ùž~VÚOaT\¨ªÊèè(tuuMÉ‹çt:9zô(éééTTT`2…6‚u:ÕÕÕäääP[[‹Íf›¦1[›1ha$k1ƒ%›ð榄%ÀXr>íkî$¾§Ž”ÆÝh£s{òÁ¼º–÷ô墖·@0ŽÕjÅjµ"Iñññ¤¤¤’’B\\z½^TÚ¸ŒQU•6)‰ÝRÒw£9³Çv›¾¾>¬V+%%%†½~’’’ذa---466N6WUºjˆï­c( CkP´sTùI‚ѬJlé‹Hl?LrË~d¯˜*"\øky.´–·¹–7šÀNTEÔò\¨ªÊÈÈ###477–ëõz Cà߉¿‰Ïçcpʲh#Š¢àv»q»ÝaS7\Ìèõú =·Û=-åÀ˜ìÀ£ñDÓtXTEEQEÁ§øP|Jà³¢(¨“†_†²Ö`2d‘~ê%tŽ¡°íú|>Μ9CWW•••$'‡Î7)IEEEdeeQWWG__ßtÏKró>:c-ÞÈhÎÔ9zÑQ5† ×0š³„ä¦}$táÁ%Ž¿–·c¼–·ÿ^#jy È„17$$$PTTDFFFDCÔápÐÜÜLWWŠri?Àsss©®®ž¶¼§§‡ºººiËûËãΛûhý±ä<ÚÖÞMJó^[F4¬l6o½õ¹¹¹,Z´(l]y£ÑÈŠ+èë룮®.ð}2·ôS/“Ø~kéVìiÅsvì>‘þò+Î[AjÃn,}gælß`îµ¼‚HJJ ÅÅŤ¤¤DÔNxH{{{/Yä…3çEÕh(Ý‚-£‚ôºF¸ÒÑÑAoo/‹-"77|º£ôôtRRRhhh µµ5è5 ·uì/Œ%ç3P¶ W|ø@ ™ÄcN¢{é-‡»H­ãpלí[ Ì=¢–·@0ÏH’Dzz:ÅÅÅQ•~´Z­4773000ß]D+>Ž5ï'±õ )M{‘oX½ÇãáäÉ“að¸¸ÐµË5 åååäääpòäÉiIÑ'0 ¶‘wàŒfUa-ÙŒ×Ï\áL̦cõ{‰ë=CJÃntcCs¶o@°p¥@0KȲLvv6………X,á#sUU¥¯¯¦¦&FFf¶ôŸ`öQ%‰¡Â5ØÓËH?õ2¦Á¶ˆÛ ²oß> ))) [7:..ŽuëÖÑÑÑÁ™3gBÅw××{†á¼ ­GÑFž›;SØ2aO+%¡ã(ÉÍûÐxœÞ¨@ ¸h¥@0Ãh4òòò(((Àh4†ÕªªJWWÍÍÍØí¢4iÌ,°©s+n'¡«†Ôú£¡UU¥¹¹™žž***HOO«ÏÍÍ%==Ó§OÓÕ|ˆYR¼$µ¾E|W ƒEÆË*ÎMq U–Î_Éhöb’›ß$±ýHD­@ ¸4¥@0Cèt: ÈÏÏtþèßöövZ[[ƒ].b$ÉY‚#µ˜´Ó¯b髏ɨØGŽ!##ƒŠŠŠ°/"z½ž%K–››KmmmÈÇIÚ™×Hl?‚µt ¶ŒEsv ­ž²­À¸žº oT Ì+’$…ϯ¤Dž’6H ŸhB?äQ/\¢F ÉÍÍ ;l þùs­­­´µµEÌc(¸¸ñ,t/½•¸Þ3¤ÞÆÙÝÛÛËÀÀ¥¥¥„Íœœ<%we¨ º±!2kž#11‡²m8³çîãéY|#Cù«I­ÓPûœí[ Ì ’¬E7‘6HÅçÂ.mÐdcÎ–Û e$ŒIi\¯ˆ$æ‚Ë‹ÅBQQÙÙÙSÿ8NZZZèèèˆXëY07‡:qÇg hfw Æ–±Gr©õ;I誉¨÷ù|!íªª*C§E’e™ââb²²²¨­­ Èeî$÷àcØ31P²9iV{2®„ :W½K) ;ÑÛ­s¶o@pþH’tÖ˜@ÖÐÀ´\”Zÿ!j5JrPQ’ä u»Ãê‚K„ÄÄDŠ‹‹#Îw°Ûí’"õÏÂ"®ï æ“ÿ ¯òZÆR fu_ŠÎ@_Õuز*I¯{9ªHèÑÑQöïßO^^eeea§Q˜L&V­ZEOO§NÂå =wÓÒ{s_#yË,Ú€O~žïLbO+Æ‘ZD|g )M{иs¶o@;²F´Ô¶¬Ñ7(c&”7FŠQ/\D¤¦¦R\\¶ÚÉÃÃÃ’‚ÙCº@#]ç&çÈSŒf/f ì |:ìöw,9Ÿ¶uw‘Ò´—¤¶ƒQµ··ÓÛÛKyy9ÙÙᇫ333IMM¥¡¡¶¶¶/1’ªØv˜ø®“ ®c8%ª~ºÆL¡J#¹K±eV’ÔúImo!ùDàŽ@° çp<—ó3(U5¸‘¨†Ñ !’$‘™™IQQññ‘sû ÐÜÜŒÕ*†ô.&â»N`h¢¿üêY^Q5Zʶb˨ £îŸèm}·q»ÝÔÔÔÐÙÙIeeeØ4TZ­–ŠŠ ²³³©­­ ›†JöºHmØEbÇQ¬%›ͬ í˜a­kÉFFr—‘ܸ‡„îâY!,0BNc ²\;±A0k3TCªª ©Ó‡½Ãé‚‹ Y–ÉÉÉ¡°°³ÙV«ª*½½½4551:::ß]D‰zÎ LãvYóqi¥ôW\×wž-G‡+!ƒö5ï'©õ ÉÍû¢J¯cµZÙ·oEEE#‡I”Àºuëhoo§¾¾¯7tûZç'ÿNbÛ!ʶ1–œ7«Ç>¯ÁB_Õu ç¯"µ~'fkóœí[ „Gñ¹Ñh Óì=ŸozPé¤ZÞ¾ñ:ŸÕ°† ªú8˜£*ª0=Z­6CÒ`?ü©(J ‡¤Ã!æ‚]*Xú0 µ3Pº•‘œ¥³ê±Se™Á¢µØ2ÊȨ{ ãPGÄmE¡±±1´“ššR+IùùùdffrêÔ)º»»Ã¶mí!çðŸq¤•0Pº·%r‰Ð™Â—J׊wb²¶’Z¿Cž[@0»¨ªŠ×ã@;)0Gñz¦ÍŸ„)¥Šªz äŠb'±è‚…Œ^¯äÔjÃÏñz½’Ⴑ*Z>t@ÓÜ{Âв×Eú©—‰ï©£·òZ<æÈóf/9™Ž•wÐyŒÔ†]È^wÄmÆÆÆ8tèYYY”——‡}Òëõ,]º”œœêêê"¾™û141’½kÉF|úð^ú™d,¥€öµwß]KJãh]¶9Û·@ ˜Ž¢xq»FÇŽJÈûq'g¬7haL .^L&S ‡¤¡šˆÛíä 7|(¸Xˆìz4µ“¿ÿ o`(õìVœ‘`$wŽ´ÒN½Š¥¿!ªÍº»»éï溺¬Œ¼¼¼°)¬RSSÙ¸q#MMM477‡Ì] €ª’ÐyŒ¸žZ† Ö2\°zÖS,=£ÙÕØ2ÊIj;DR먌l@0{¨jø”w¢RŽà²$..Žââb233#æ ä û\’HŠ—”†ÝXzNÑW¹WBƬîÏkˆ£{ÙÛˆë9MÚ™Q¥Öñz½ÔÕÕÑÙÙIUU !µ²,SZZÚ‰@&û<¤4í!¡óƒÅ›É®ž³ÌªFË`Ñ:Fr–’Ü´„ÎcHbj•@° ¥à²"))‰ââbÒÒÒ"jm6ÍÍÍtww‹’ •9ü^ ¶>òþ‰¡üÕX‹7¢ÎvBôÌrÆR H=ó:ñÝ'£ÚfdddJîÊpÓ7Ìf3«W¯¦»»›S§Náv‡÷j]6ÒëþIbû!J·âH-šÕ㟌Oo¢¿â*†óý¥£)g)æaP . ÒÒÒ(..&)))¢vhhˆææfúúDP€àT•¤Ö·°ôÕû¢'çÏêî|:#½Õ×c˪"­îetÎá(º¨ÒÖÖFoo/dff†Õgee‘––Æ™3gèèèˆøò¤·õ“}ôÆR (ÝŠ+>r‚ÿ™ÂcN¦{é­˜†:ü;#ÝÞ¨@ ˜„A)¸d‘$‰¬¬,ŠŠŠˆ‹‹œ¦¿¿Ÿ¦¦&†††æ»ë‚ŽnlˆœÃO2’½„²m(³œÝ‘R@ûú»HiÜCbûá¨<³.—‹cÇŽ‘ššJUU&“)¤V«ÕRUUENNµµµQ¥¿2Y[Èle4³ kÉf¼ÆÙM³4™±¤\ÚW¿¸ÞÓ¤4ìŽÊІ$Ia_8µ“”Sêy«ª> Є~<È;¢^ ˜#dY&77—°Qð{szzzhjjÂfѤ‚ØHèªÁ2ÐD_ùÕØ3Êfu_ŠFGÿ¢+°eV^÷z[TÛ °gÏŠ‹‹)** |–˜˜Èúõëikk£¡¡!rð™ªß}’¸ÞÓ å¯b¨p-ŠV?«ç!€äŸ`O/#±ýÉÍo"{s³oà2B’µgëy« ø\xÃ¥ .v´Z-ùùù ×‡¨)ŠBgg'ÍÍÍŒÍw×çÍ…¤ š™ÀÛNVͳØÓËè/¿¯Árá†Á™Eûš;Ij9@rË›HŠ/â6Š¢ÐÐÐ@ww7•••¤¤„Î/)IdffRWWUùPIñ’ܲŸ„Îã od$géìFÄOB•e† V1š½˜¤æ7Iì8Õ93KÀ œì„³uCyÞI ~"ETÅÌ=ƒ‚‚òòò¢Ê!ÙÖÖFkkkÄ  ,}õ˜Û(ÛÆHÎ’YÝ—*Ë ¯Çž±ˆôº—0wFµÝnçàÁƒdggS^^öÅË`0°|ùrúûû©««‹êÅKã#íô«$¶f t+öôÒY=“ñé ,ÚÆHÞ Rw×sjÎö-\ªH’tÖ; ¬5 iÉ͵þ Bÿf J’4…[X½@0ØÍf ÉÉɉ*‡dKK ííí"‡ä%Ä•ŽÙë"½î%âzê諼)qV÷綤бê=$v%¥a7²/º—¤®®®)¹+Ñ––ƦM›hll¤¹¹9ªŒ:Ç YÇÿ†3)—²m8²fõ7aš½ç 2½&Á ª¾ñÂߟհ† ªú8˜£*áÒ-ü›¾`a!IÙÙÙa±DŽšíë룩©‰áa‘îb&Ò†‹IUHj90žý:Æ’rgu>½‰Þê°eV’~êe´ÎÈy%œN'G%==ÊÊJŒFcH­N§£ºº:»2Ú´[²×Ijýë$´ÁZº[FùœM†U´z¬¥›É]NJãÄ÷ÔÎiµ%àbCUU¼ÚI9Š×3mþ$L1(UTÕK ±d;‰E/DƒF£ ä ÷0ÿ5ØÝÝMSSv»}¾».¸Ø˜ãUç$çÐŒä,c lë¬çlt¤Ѷ4î&±ãhÔÆS__V«•’’ ÃúIIIlذ––ñù¢KÙ£s“yây’Ú2P¶mÖìÉxqôV_ïÜiØ…ÉÚ2gû.6Å‹Û5:îtTBN# ’c%VãP“‚ G§ÓrHêtº°ZŸÏÈ!étŠDÆ‚‹„Îc˜é/¿zÖSë(ZýåWù½•u/¡·G7ÄçóqæÌººº¨ªª [¶T’$ŠŠŠÈÊÊ¢®®.¦²¥†‘nr==½ŒÒ-xÌɳz>&ãŠO§sÅ»04“Ú°+êdñÁ刪†Y‰ÍóŠÑh äÔD˜[æñx9$£ \ê\¼/´Z—¬ãÖQNùUøôæYÝŸ31›öµ ¹åM’ZDüÛf³qàÀrssY´hQØ>£ÑÈŠ+èííåÔ©S1½ðYúê1÷72’³ŒÁâ øô¦¨·½P©E8R Iè:ArÓ^´.Q5K ˆaP æ‹ÅBQQÙÙÙç͹\®@Éh‡Ó— dO.Œùšq½§1[[é_t£ÙÕ³º/U–±oÄ–^îOˆ>Òõ¶ôööR^^NNNNXmFF©©©444ÐÚÚu¦IUHì8B|ÏI† Ö1”¿U3G)Ib$g ¶ÌJ[’ÔzÙ'^\‚h¥`NIHH䌄Ãá ¹¹™®®.‘CRpI#{dÔ¾H|wíxBôÙÍ×èŽK¥cõ{Hl?BJãQN‡'NÐÙÙIee%qqq!µ&`|ž!ù„~Ùv;Ùv;‹­V¶tvr4=·22ÑÏnÒé…ÊÄ‹ÖÄ¿)N'I.Ú¼÷€ªN~çDšô9c½½Ø‡†¦-/nkãæææiË÷IÙìK®:¯ãÜÐÜ̆#Gfÿ„Î{vp´j o¬yîsja«pÎ=ùüeE·1ÏÒw“]·s×qŒFlºÈF›Ïç iWUU…-—*Ë2ÅÅÅdeeQ[[s¹TÙç!¹y DZod4gÉìÎ9„*k*\ƒ#«š¢Ó»¨8ý¯(åx9à–eÇÎË`„KÖèƒÄÉ}pƒ2fBýhc]>è…U}}¬îé!^ä.D@¯(¬íéauo/uÉÉìËÊ¢Ï4wùðæ IU©b]w7YǼõCßÖFb[[Ôzâ;ïi*UE‘¥¡Z[ó•MGxyó{i(X2u¥òClçÐùüŽ뢫0%ð¾]bPu²?3“ÖøøˆmŒŽŽ²ÿ~òòòX´hÚ0^“ÉĪU«èééáÔ©S¸\±yü4n;é§^&±ý0ÖÒ­ØÓŠgú´Ÿí«ÇK¢Û^QÐ)>4Š yëéI,eÛþ¿RÞtxÖö-Xx8µZ¬F#ÇSSi‹â·q±Úá8Ý®Ó|þ3ú†$I!‡'‚z ¡WCëŸÿÇnFGç¶<^²ËÅ{OŸ¦ÚjÅ ÒÎb@ÒÇÆXÖßC«¥;Šzâ#zEae_okjby?qÙKW{ö"Ú²ËÏkÛüîz ºÎÌ÷!ÄŒÁ㤪á-R†{hÏ^„g–Ë7ŽÆ¥p¬r3)N'×פ|h·FÀÑÑ#822Bgg'ƒøÛ¸¸8rssQ…‘‘Øç'jßÔ邚ÏæCߘ®ž®Wþ¦«ö§áÅ7æÔ ,æŽ3gH¸È‚…… ” çñМ˜8gCjsAåà ï=}šò¡!Œ™§n‚ö¬E´å\^åiƒ],9µ»%‘¾”Ù­ƒ­J2­9Ô.£´«‘ÕÍ,±Zé°X°E˜âóùèííehhˆÄÄDôaô²,“––FZZ£££1{+tÎ:£ÆŸ‰¢5ÄÜøo‰.7Ù;‰.7Ú(œ£–dŽWl¤?%‡Ìþ6Œ®ùóö æžx‡ÊÁAª­V|’DÏEæˆfP¢*h4†iöžÏçF=§ÚÖ¸A 番ªF°Q‘8kTªJxý\”›ºº¸±¥%¦9_A8² GGiLLÄ}‘íHªÊ•\ÓÖvÑÿFÚ³/_ƒ@çó°¨ù(9½M´g•ášår…SÇ+6âÖ›(í8Íò¾^l:=æÈe#ÇÆÆèèè@UU’’’Âí rssÑëõ Waƒ­ŸÄŽcÈ^7®„옢ä5ªJ¶ÝA²Ë…æ<~#IY©Úʘ1ެ¾t¢âÎe…Éç£lx˜l»Æ¤$¼òüÇ‘DCPƒ oYÖ¦/*^>ïôX”IåDê5Œ§ñÜD¯Ÿ ƒRVUÞÑØÈª¾¾YÝàò$Áí¦Új¥9!G £×Ë»YcÄB¥#»ŒÖó4(óz(è<=߇0#$ô³¬î <:Ý…Ìj»$Ñ™YL]éjÒ»XÓÖ€Åë¥)!!¢_UUéééÁb±`cˆJ’Dbb"¹¹¹¸\.l¶ØëkKª‚q¸“„Î㨲w|fÄ9ýŸ<› £÷¼öª$Ó•Qıª-H@fòEE0s$»\TÒQ<3B” >ŸÅçEñ¹P”à/HS ÊÙd. ÊkÛÛ/™¥`a¢WJ†‡9™šŠç"yëœ }lŒ÷Ÿ93¯A73Mû…”Ýõ—ŒA  Q|·Ÿ¤¸½Ž®Œb¦Ù pÌœ\´ž‘¸Ö6£tÈJcb"ž(<ø‡®®.IIIaƒv4 ™™™$%%1<<S¥ dÅ‹ÙÚL\Ï)|†8Ü–àÙnãÝrìv´ÊÌyî}-¹•œ,_Éi#Ý[RwÁÅÑçcÉÀƒF#ý |ne(ƒò,á—ŒA¹¼¿Ÿ­sq(H ‰+W’qã ª¸zvõ„¤õë‘õzA‰«¬dÃK/a*,<¯í–.eåŸþÄÒŸþtêòU«Xù§?aÊÏ?¯vS·mcÍÓO£O­6Ž>-ì;î`ý‹/Rò…/ÌÍI<‡¹¾æ.½¢p[}=¦9Ê“'›L˜‹g/uËd.äá¯Æó¼v4F#Ú %E5F#Ú |hœïou½AÏÕ=G¹û™ï’ÓÛtAmEƒÃϳ×|„7¶ÜÅ5ݱJ^¯—ºº:öïß1º[–eJKKÙ¸q#)))çÝ_ãp'¹#«æ9tcÃ$¸=¤ÍM~âžÔ|ž¸é3<}ý'HΚ“} Û:;©¾„K _ôe‚ÛÍ;æ´dâ²_þË¢E¼õ®wñRF{¶nEq¹È½óÎù>A‘fhh6íÚkɾýöìØù™©W\Aåw¾ƒ<ÏÃËúûYÓÛ;¯}ˆ†[ššH›õýè’’XóÌ3\ÛÞζ£G¹òäIʾüåù>ü,Ù¶ŽmGŽœ÷uXú¥/qm{;é7Ü0eù¢ÿüO»çIÁÇ>ÆÆW_=¯muÉɬþ󟹺¥…+kk¹å©?q¯¦‘«÷þÝTt©/\Æë×ý…†Ø+úŒŒŒ°ÿ~êêêðFxù1›Í¬^½š%K–„„¥÷ ë^ù_Þóêï1¹.|íîë—óö-Ó=æ¾i%O}yiÌ_ÌoßõïüsËû°›¦¾˜äèCd¾ímÜŸh1åå±ø‡?Dq.‹>ýiʾô¥YÙ÷¢¯‚|dÚòŠo}‹ôíÛϻݜ÷¼‡Ü|༶_¼˜ªï}i—ߨÒBvŒ%G/.jƒRÞÙЀy+˜KJHß¾¦þþ—_Fñx9z”]«Vqæ[ß èô©©Tßwë_|‘å?LÒÚµu +W²üá‡É»ë.Öüõ¯¬~â V¬ ûÝïfÍ3ϰôg?#uÛ¶€>õê«YþðÃdÜt«{Œå¿ù ñÕÕõ…ÿ8ßþv೩°å?L|u5 Ë—Ö•~ñ‹,ÿõ¯:c^‹ô#Ö>÷ÅŸù †Ḭ̀Ç>rü8Ußûºääš„åËYþë_³þÅ©~à YSßÀ3n¼‘ÕO<Á²Ÿÿ|Ê9™¼~åÿÈÊ?ý‰Ì·½-è ®ë©§ØQVÆèÑ£sö½‡âêövòÎc¸~®ØÚÙÉ¢ó˜æ+’,³ö¯%iÍê¾üe¼ímôþã”ýû¿“ý®wÍâžÏÿE2>-y&ÊWß?šLbÈÊ‚óy ”$–ÿö·$,_Ní¾ÀÁÛoÇk·³äÁYÛ¸Ÿ{žü6Åí'g¬Ÿ¡péM´W_»êV<椘¶UU•¶¶6öìÙCOÓˆ²³³Ù¼ys`(mY,T~÷»Xb5”4Ê¿ùMVýùÏ~ò“ȳ`Pj…w54\”Ó"ýÆäIJdYø‹X.qB¯‰R? ,ëïŸóƒ¤õëA’|óÍ)ËÕIyüd½žõ/½Dþ‡?ŒgpÔ+¯dý‹/’¸f ¦Ü\²o»êûîÃ;EÙ—¿LÊæÍÓÖ'®YCÙ—¾DÚµ×" jI\µŠÒ/~‘ìÛoGc4߉ªròsŸãà»ßMûïOß?ÿÉ©¯} ]r27ÝD‚mÛþñnzíLÎÙ 2šÒé[õ~† ׯ<ÓårqìØ1:ÄXïºV«¥ªªŠµk×FLž>e»sì÷Ûöÿ…<ñ-ªp!/+÷¾{zmäà$Yö? =Zo¬º™_Ýñ ŽWl:ïýNFÒhÂzßc¹:ñ™ÏpôC ½Ÿp}ˆª³K~ô£¨^ð"i"í3leSAžYîóxxWCCT¹M’¬EoˆGgˆGoH@"·kàŠ’¥©'Y’ä°F¢,i˜\¨W’å95*uŠ2§A8süC9£55!5Y·Ý†¥¬ŒCï}/‡Þû^^«®Æ;2Béÿù?St'¿øEŽÜ}7'>ûY4&m?Ì¡÷¼‡ý7ߌÆh$ëcýz½ç=ì½òJ´ $oŠ|ã=q‚†ï}€æŸü„“ãsó>ô!t‰‰¸õVê¾òÞ~;Úøx²ï¸#d[®înN}ýëäÝu)[·N[_ü¹Ï¡ª*¯/]Êá÷¿Ÿý7Ý„1'‡¼»îò¯ÿÌgp¶µ±kùrŽ~øÃÿä'§l_ò…/0°c︃Ÿý,M?ü!™·Þ:Í˹ÐȶÛܼ½Ï7§s<'‚ÓúϪü;Ñ%'³i×.ÿèG|ìc,øaÖ<óLàÁ–}Ûm,ýÙÏX÷ |ô£,ýéOÙðꫬ}î9 >ö1V?ù$Ë~ó›@{þ0ïþæøõ—ÞÎíWV󟺂}ÇY¯÷Çn^Å•+‹ŸW—góÙÛ×#!±¼4“â~/á¿þ+™·Þ €¹¸˜Ío¾IÅ·¾Eîûß϶šª¾ÿýÇ­x½œùîw)øÄ'H\µ*¨&yãF6¿ñßþ6EŸþ4ëþþwJ¿øÅÀúì;î`ÃË/Sòùϳâá‡)þÜç¦l_ðѲáÕWÉýÀ¨úþ÷¹ª¾žÔ+®º/GS#“<÷i×^ ÀȱceÕõ¸çÉoSÕðÖ¬^’ª’ît3Pº…Ž5ïÇŸsìÙ³‡ÆÆÆˆ¹(Y¿~=åååh¢0d®no:ô˜`³róŽG¸ë/ÿCþyä)Ý_×Af²…÷_·4¤¦$'™ö&þþý;yès7^t,¹Ù|ôÕg(ûÿäªúzÌÅÅT~ç;¬~òIÿy•e¶:DÕ}÷ KIáª3gȸé&Œ¹¹¬ýË_¸®»›ë::Xò¿ÿð k¸ª¾žêàê¦&®8v }zú”¾¥^y%WÖÕQôéO–-ûùÏYýÄþö³³¹ª¾ž¥?û×´¶úÛûŸÿA3i RÚµ×rűc\×Ùɲ_ü‚mGŽ„2ï{ñE,‹Ms2Lù^–.eÓë¯s]o/×47³èk_›b¦_=W?îßçÏ>¥?™·Ü¶#G¸®·—ÕO<t8]ñx8zÏ=t<úhÌß{¬dÛí Ö1I’ÐéLg_N$µ4AŒJÙ¿A¨Za–K1êg˜õÝÝXæÁe<ñæb)ª$aÉ|v;¯½ø½ý¯¾Jü’©µwvî`xü0¸w/Îöv|6æ’’)zGs3¶ÚZœmmS¼’±’°t)ŠÓÉ’Ÿü„5ÏôîøÆŸy~ïi®]]‚ÅÐ磯ÔðÚþÀ®5k¨x–}å+¼ZZÊîõëiøÞ÷(ü— { ¶üä'Œ?Îâÿ8蜫ªï}Ww7;W®dGE]þ3¥_þ²HS’¨üö·Ü»—UU¼RTDß?þØVGù7¿IÛósùrv”—ãêî¦ü?ÿ3ìñÉz=Ußÿ>åÿñô<÷ö3S #³ÓÆÍ;~Ë»^ü鬖 4ú|$¸Ü¸âÓéXó~J·¢Ê± #*ŠBCCûöíÃáÅM’$ Ù´i¡ Øô±1Vô÷‡m+³¿•÷<ÿCÞùÏŸ‘:Ôu»úm<úJ ï½z EYIÓÖtZþû£W£¨*ßþÝ.Ÿéæ³·¯ç¦ åŒØ]üä/~CÇÉ.ylí>=ÃGŽzÅÈñK—b)+#ó–[HZ»CFC€$±âw¿#qõj|ŽÇ'ç=ï¡ò»ß8A22È}ßûhùéOé}ἓ¦Ä$mØÀªÇ£ûé§i~è¡ÀrmRºñ (I£Á‘AêWPÿïÐõÄ|â¤^}µÿø23YõÇ?âãÔ7¿‰eÑ"Ì%%‘ª`ØOŸ¦éG?¢ô‹_ÄRV6m½ÆlfõSO¡ÏÈàô7¿ÉÀΔ~ñ‹þË¿ø¯³œVüîwxm6ê¾ö5¤)ÓPâ**Xñûß3|ð Gîº ŸÓɪÇ'aÙ²˜®Å™fEÿœÌo¿d>¨§[ÖL÷ŸŸ¥ê†-Ũ?Oâ=ÖÍSšžÑ'H ãT<$­vÊÃEc2¡žc†É' ‘I‰Á8‘ôúÀ¸âvO™kͰâr¡xÿyŠ?óÜÄ/^²?ÆìlÖ½ðù÷ÜCÝW¿Ê‘0 %m'¸çÉÿfåÉ\Èo8ÒœN$TIb¨p mëîb,9öèz»ÝÎÁƒ©©©Á!M™Ñhdùòå¬\¹SཫÚÛ‘¢ ä,m­áCO}‡ëÞx óØhTÛ<úJ ý£Ü{ÇÆi¿µ•9¤'YøñSo²ëX ¿~þÍÝCl^’ÓíeÇaÿýîL»•'š]<¼ýÓüÙ•Žl0¸r%©W]…ýôiŒ99XÊËIZ·Ž‘šÜ}}Ä/^LÒÚµœùÎw¨ÿïÿæä½÷Òùøãä}àSž ]O=EýÿûÔ~ùË|€U=†Æl¦ó±Ç"cë¯~EËÏ~Fí—¾„£±‘Ì›o óíoG6™8zÏ=´üä'ì½új\QÜþßÿÃÙÑÁâþpÚºŒoÄ•ÅñO}ЦäÈÝw3tàù÷Üãßç;ÞÆdâÈ?Hë/ɱ÷¤aëÜ|ïÈ5Ÿþ4½/¼À‰Ï}Õç›P7×HªÊU ÜK‹£ðü ÊP?D5Fýy²µ£ÝüaŒãó«´‰‰l|õU}ík <ˆl0û¾÷`**"õÊ+>xð‚öz啤lÞŒ!3“Ñ“þ ö¯¿Ž¹¤$%íšk¦l7‘¯M6œuQ:„.9™þ—^¢ñþûé|ì1Ú~˜(¢Kíõõ4|ÿû$oÜ8eùðÁƒ$¯_OÜøäí¬w¾]RRà¸û^|‘Ô+®ÀTXˆ¤Õ’5•¸ŠÐ9!µññSòµ*n7n«uÊKÆä<§ŠÓŸÆÅ7>OZ¯ëên2dóëó3:)\b¡ «4 ú´4R¶n%eëVÌeeô¿ò îÞ¬‘£Gi~衳žÇ‰öÌf$YžrÜÿׯÇ^"Cyv'¼,ñÕÕ>ù^=¨Þ\RÂÆ;‘ ölÛFóCE|9Õ{œ\³ç Þÿì1yâ¢E«($»Î¦äñ˜“è\q;}•×Wí®®.öìÙC{â´´46mÚDqqq   xd„âé‰ÎER–×îæcO|ƒ‡ÿŽÎÞ õx}ÜÿÄ^ª Ó¹uÓÔßDNšÿzÿÑgoä…ïßÉ ß¿“‚ÌDR‚g­P‘Ø—^ES§•¡Û?LÊ5×ÐõÔSØêêH½òJ’Ö¯Ç:>6„býÿóAÒj1–MŒ†KÇ£b«« <ËÂᨯüß^_H›g.,Äg³1Z[;~êgI(|N''>óR¶n L“šÀ\ZŠêó1´gÏ”ã2ƒ$aÊËÃ;<Œýô¤¼²“ìsI ÚÄD®ime{_W:…¬Óa30WœÏõ8—„,©d¹vbƒ`Öf¨†TUAR§{‡ÓÏÉ.×¼WÃ9þ‰O°æ/aÛÑ£ØjjüÑoŠÂ‰Ï~€žçžcpß>ªx€ÂO} sq1>»ú0s±¢aõ“OâhhÀRV†³­ößý€¾à H€IDATüÅãaëØ¦=ÔÇššð±øp °gÛ6:þð >ñ 6ìØÁàž=$o܈u×.ÞŠ2"·éÁÉz×»¦xJx€ìÛogóÞ½8êë±”—3ZSxÛíyî9Jÿïÿeë¡C¸ºº0MºÁ?ègõÓOsʼn¸ººH\½š£ý(]ãsw:ŸõÝÝìˆÈ1›d9,ž‡ùœ]OCî]wqjüa¤KJ¢ü[ߢýw¿cø­·°ÕÖ’qà hŒF|N'ñÕÕ˜òòhxðœz³­FÆëSXVêÏRÐmõœœi·R–w6Way~pÏ¥$ËÑ[]ÚÄDÞzç;ýë4š)wá¨ÿîwÉ|ûÛɼõV<ãß{`w_×_OËx׉¹[£µµ8»º9~œôk¯ üNt“ò+N< »ÿúWšüãˆ}ªøÖ·Ð¼õŽwLñÐDCNO#w?óÿØ·âzÞ\¾%†ÚבHqºÑðÊgçaä,Á‘ZLÚéW±ôÕÇÔžÇã¡¶¶–ÎÎNªªªÂãȲLYYÙÙÙÔ<ÉU'Ï?Ò]çq±ùàó,¯ÝÍkn¡¦|CPO7@MS/Ïî9ŇoZ9%:vâÅç¿¿ ëÈÙ Ò1Wø—ÑCgzÈß¶•„’L^øýߨöòˤ]s I«WÓôàƒ¸ÆË[ÊËžî¸ñéYS^ŠBx§¾òÒo¼‘|˜¢OCV®îé/Eî¾>\ÕÞNsUÕ‚,”¡øÜh4†iöž/H}úÀ˜¬ªú&札ÑUõ!q60GUÔ5C±t``6«ÔFÅȱcìݶôn nñbZóú_z çDªràÖ[ÉûàIXº”®?ÿ™ößÿçøµíôiê¿ûÝ€WÂ3Ië¯~ðæxGFعb¹ï?ºädN~þó¤]}5öñ¡bŸÓÉá÷¿ŸŒ›nbäÈPU|N'{¯¼’‚|Ë¢E ¼öücÐãµ75QÿÝïNù¡*Ç>ö12o½÷øMÌÙÑÁ7’w÷Ýóòhýõ¯iÿíoC*¶ÚZö]{-9wÜÛjepÏR¯¼202°s'û®ºŠ¬ÛnC›@Ë/~A÷øpg0Úyä¼*ìÌ&‹­V^ÏÍE™§Ã| ŸŒ;FÛoKñg>C|U¶Ó§É¼åtII4ÿèG€?(,û¶ÛØzè½/¾HÎí·ã¡ã÷¿?ïýf-*æá/§s¤¾‹«W•ÐÔ5HM£ÿzÚ}¬…ÿóÞÍ|õ®­¸½ ›—Lfé÷Ϭúþ÷km¥é‡?¤ùÿ—U=Æê'ž`øèQò﹇S_ý*?±/¾±1N|ö³¬ýÛߦ,o~è!Ê¿ùM6¼ü2c--d½ó îÙÃÈáÀÿ…°ô‹_D—šŠ¬×“²ukÀtvtÐõôÓ”}ùËþáñôt’Ö¯gÿ72vÎw- dÜ|3®în–LšÿÐóüótüáAãó²ùàóT4æÅmwÒ•~a Ö}SURœNzÍS½p^ƒ…×{†´Ó;иcËÏ7<<Ì›o¾IAA¥¥¥aƒq, ×ëõ32g-Î1Ìõ;ÿÈêš¼¾î4åUÕýêùClZ’OZâÙšåGë»ñúV-Êâ—Ï¢4'™‹‘Ú–¶€Æ§(¤%š°ux} .ƒ§;yçÖJ.LZA‹ÓÀ×¶oŸëo~O¹Çj¥èSŸÂVW‡>5•œ÷¾—áC‡ð£MLŒxlÝù £55,úÿàÀø<ÍX˜èKÙW¾Býw¾CÖ»Þ5å%)§¾úUÒ¯¿~ÊÈ;Q½^J¿òê¾ò’V¯&ýÚkéÿ­ML“Yôõ¯SÿßÿMöwL9Ö;Èÿð‡‰«ª¢óÑGɼõVìõõ8ÛÚ¢î×l’>6Ʋަ¥ÍwW¦¡ª*^í¤ÀÅëÁ$¯­vòFªêÅo!F¢ŽU?S,^ µºÇÚÛiýÕ¯B®W\.Zñ‹ ëìãåÞqƒr2÷ß?m;ë®] ìØ´Mg{; “< Cç¤5êåú_yeÊ2ŸÝNÓøÃ>ì±67Oëø#xÏvwvuQ?UŒ‘£G§D¡ž;ô2rü8#ÇGõ´?òHTº¹ÄâñP<2BC7î™&Õé¤`4º9^³Á‰Ï~[]Û·“zÕUôýã´ÿáØÇ‡Æ†ßz‹·Þõ.Š?÷9Ò®ºŠ]»8ó­oŒ'[}=ºIU“œÝÝXwï>Äe³aôÐs¦™#~ïä›'ÛùÉ_ Œ{6^?ÚBbœ‘«V1êpóè+5,/ËB¿_y«†®'Ÿ$yófì§NÐû ½ç >ö1²o» ëÎ!‡ÍÍÓÖ ¼ö?ø–I£>ˆâñ}ÛmÄ/[FÛoËéo~óìúûîCGêUW1|èÍ=4¥ÒÐñO~’ò¯Ô«¯Fu¹èøÓŸ‚zVôþœk8hb,6ØÉûÿv‡_Éî5·ò#^ n7ý&Jw-[Æ"ɤÖï$¡«&¦vUU¥¥¥…žž***Bã¨*Ù»w_ðqL9OÖNnûÇOhÉ­äµÿÏÞyÇWQ¥ ø™¹=7½’BB !„Bï`Wì½­º®}ÕU÷[Ý]]˺êêºÖUWѵ÷‚béÐ!ôNz/·Î|ÜpIà¦ß`žß/pïÌ™3gΙóž÷¼eÚE'ìo1YyéËm<~Óç¶òÚfÞú~'¿;?…³¦F§QSXQÏ®ÌRÌV‡æù@nW,Ïe ¹æ‰¯¨¬kf_N9’,³óp)’$³¥ÂŠÅ.“ŸSJ‰W¡­݃÷ÝGÒþÃì¶1À\^ÎÁ.¼§]õSöÓO3éà ˜?ßé\ÚSêÓÒ(üå"®¿Þ¡pånM/Žb­«ãÐC‘üî»Çú1'‡¬'ž`Ì£2béRš&³-ö³µ¦†’?$âú뉸î:lmKÈGͽʾý–#_|ÁøW^!¡Í#ýÈW_u i(˜uäûQšê9’dÃbnlS:J¦=в×Jûï¼ïJJûç(ÙØÈU™§^îÝõV¢ÿ{Ö%%õø¡T:2üüøn<Óç•”0}˜,á “>øsÔhnz¯oáo¦îÿ•yÛ¾êËöÔ{ðËì«(ïw]e4èºN µEZ…¦µ®Oç ">>ýqQ(¼óóÝg“¾" ußȆðÉd¶t”šGQÓØÊ‘êc+*a^Ä  ¾ÙÌÁ¼ §0  Ó¨HM§¢¶™ÃEÇ”(ñ‘Ô4¶RQëÐ䎉ð§Åd£¤ªøÜ]ÌÙñ>UèÃÂðMMÅÞÚJíÖ­ÎÕ0A­ÆoÆ šîà(£ Åcôhj7mrŒ1‚€ÿ¬Y˜Ž¡%'¯qã4öîEÔéðMM¥)=Ý9ôJLDP©:„§òMMEHÝöíÌ?xÌ'Ÿtšm´ÇoútÌåå´ç|é7c¦ÒRZ œÛŒ£Gã=q"Öêjj·lÁnê˜.ÓwêTt!!Týú+^&`.+ëp¼ÏäÉxŒMk^õ;wv:žêÃÃñˆ‰9փħcÆïÝ÷\Úýå_ÏÜOô¨¾Ô?©ÊsòóI&ÊÁDŒ¥¦y8|(tMyeÂÌ= êëàöýûñêÆûõTbÒ`ËÍï÷ÍÙM({ÇÁ1ÓX3ýL:>×Ñ¢VSìåÙm9ÁnÃ?o ¾E;ûäÔ©R©ˆ‰‰!**Êi¿8jùrüÛyÐ6µ†ã²-ù ,šÞ;õ•dgbú:fìþ ½yp“~ÆÏ€yó¿új—,aÓŒ4:4¤mîô÷çûv«ƒMJÕý÷ÜøØ`4tÅÏ›hlì{þJ$qNAªAÌÙ=\°77÷ÈKNax Ê2u:å}x{KTc#S†IØ¢ÁÂZWÇŠŸwÔ»ôjG «ÈcT‰2Àõ”àšÆgm¥ÑÓŸ*¿¾yÇj$‰¶{cQ¤Õ?Š–€ô e¨,½ŽdY¦¦¦†ŠŠ ¼¼¼ðE¢~üaÞ£¢$Q–CÒáÍX5:*Gš³…,ˆ Žf_Â,DI"¤ºq|\á5n“?ý{s3û︃ú´ ¤*àg6³3$ûÙàŸµd~¾}׺?Qå1¦®m½-Ý Ñ  C?2AÛy\GÉl»ÙnG¶Kȶ£Ÿí ItZ! ˆ*•j‚Êñ‡JDP«\æùF–±Õ6ÐZPŒµrè²×Œd롎|0T¯^)iQ?jj÷¾“ÖFÎ[ý Ù;X9û šz™«ÀËb¥Fß3ÍÙ;˜â”«ñ-܉_þV„^æÙnjjbÇŽÌ‘$ÄAN„áajbñ¦O™|`-ëS—’5xA´MZÖN»ˆÝ‰s™³ã;âsv1˜¾àM·*4´ÇQ®±µµèY,ÝÁF„Ní'¡½@)ˆ‚ÈÑ—¬,K]{m ±ô‹rÊ÷“1í ñmH !W-Å{ÊxôQè‚¡¹N†ö–VL%˜ò‹©^±†êŸÖ ÚË-¢© ½Í†I=ðs¶£/¡ÓeÆ6ÄîgäY¬›z!{fÑáÜÛbé±@ ‹"µ£¦Ò<šàC«Ð×õ>’Axvï¹ÿúr.\ù&Å#bY7íb·yÎ÷„zϾ_ðÒÆ/dþ¶¯‰(Ü~P„ÉÞ3¾ºzØ ”‚¨F£Ö;d"$»[W^Þ®ryCç1$;”ÚÊKî9Ùž°æ¾/—w…gra¿¹Œ€s¸L›¦pò¢ò0`L1a4gÏÇ|¤‚²ÿ}Iù'˱Õ¼7thK yƒ``=º®í/åiü|ðž6ý¨ôQFE  Ge¼e}W4V{°·Ü«OÇÆ\6‰i#nÒö6öæL%´cÊ/ÁTPLÃö½X«{?!ÑZL,Ùô 9;øeÎÕÔø„ôì8»ÍŽYÝ;c«‡%“.ût9m=³ö¶X5ú&ÈCTúQ¨¼Ù‹†)°ß~ñK¦Vmtx?Ë2HGÿ—Žms#ÕÁ|™8™øÚ\n&ÐT7hý1 ŠŽŒuµóñh;ÑõHj59”%˜T ‰©Ydc#Þ ÚþGVpæòFÆ~Ü3¨vЉ&N]FCïPÞ‘ŽÂ¹]î®|0Z­x»ÙÙÀ=’ÑÏ=Œ×äñý¯Lá¤@LÔogä=¿¡ôÏ(zñmdûÀ½0B››E ŒÀXœq1„þæ2‚–ž¨/¸öˆ-:„^ &G ZT^Æ>{²¢ò2¢„÷´‰Îm’ÅJÕw«8²ì3š3z¯ÁŠ(Ëáú¯þÁ–Ig±#y RÞ÷{ïJÀ=|-1^±*§ÛCbêëÔ¸AàLjk/ÂÉl‡éÑuˆ¦à\VWèøáˆŽfûà™^”ÈÂùÌ ´pA˜ /µ¢áï ÉbÅ\XJýÖ]Yö9­yƒ³RÀqÏî ê.Úrv»Êå-j±ãB ìêÂ\ÞnƒœË;´Å½ÞjgÎcô?ríŠÂÐ ôDÜy=Þ)8|÷£X«ÆÖÒÝ÷í`žÇwn*á¿»Ÿ“åúJ¿^-ÊX €¨Õ|éÙ_z6õ[wSòŸ¨[ß» )j»•9iˉÏÝÅÏs¯¡,0²Ëòz› ú1A±é<)›pžå™f­éÒig žCøXÂnºœÀ – vagﲿ8#ÄÌì@ ßѱºB‡mîG» k*µl©Öpö3g„XЈÊÃp<¢Vƒat†ÑQŒ¸æBj×l¡ôíO©ß²«ÿ•wChK {†ºÚ×?Á…–÷¤Èåê¦ånA%2êOw÷ú“Š0©€÷´‰$ÿÞS“¤þd¦Ñ•,ìÆSP©õÈ]Œ{÷ùa/L*¸Ÿé“÷îóŒúóݸ^TSÂ5ßþ“yÛ¿FmïÜ Fï&Ûº¦±M»Æã:-ã®ñã(j?âßxš‰+Þ%øÒsz-L¶ÇC%sy„‰'Iõ\§!“$ðu©ž‡x²©Z«Ì¯ºBð[8“ÄÿÍ„ïþ‹!&²ÿuv»ïÙþàÚŒÑa¦qi@Ú§pr#¨TÄ>ñüÎt{Ý­¥t×LÖ;e¿_6`ÚÚá‰6¨+¼¦$‘üý;}ÖTû4VqùŠ—9sÇè],Iëlîõnñ¤xÚuԜ촅iiAp“à:âš IúüUta=s>ê ÑF;Å5sgl #tƒ+Pµªx!ËÈ‹YFJZ/)ÃɈÊèAÜ«OôY“ß‚,2H&SÝq4—·c¥Ùñ,I6›Ë\Þbûƒ$Ɇ$Ù‘$[·Âá‰å&<€ŸÙÜïå‘Qßéö¥Â)†(2ö¥Ç0&ŒvkµmGéÒs|ãÞ{Mÿ€¶u =〢 ð#áçðš”Øç:’oá7Ÿ?ÉØü=¶ ¤Œ¤ÒP5f%S®ÄâÈ7=±ˆ˜'þ€ éûòvo˜äkåñÄF®‰ltÇ™ jþ–îÉ»ùê¬Jè¼®»ér?ü7b/Â`õwÝ»îàh.o«¹«¹ ›­Õe9wKooÞ½Ùý\2qíE„Ýtù€¶QáÔ@åa áígÑ»/˜ç§Bì¯Àª &þ¿Ï ôýªçdDE{†¨ÓÿæÓè"Fô¹ck¬ú/KW½…g‹#¯´Î>pK»&ï§\CUÌLìªþ-ãFÜ}#!Wž?`mí Q€Aþ>¾‘sF˜Ñ¢ãŒl¬ÖòÈO¾-Õc–m~gx§&3æ…¿ôÛùøx†“åQdÙÞeH«a?ýÐ÷#µïì©D?zïP_‚ÂI„vDñoýÃmšw9t†_?ìÃTFÞyέ´Â©‰&À„·ŸEåÙ¿0Kcò÷ò›/ždÂáMh0d8¢çÄÏçý%!1}ª#ðüEDÞ{Ó€¶³; *™‹ÃM<•ØÄÌË ÚfI`ùÚïźJ­+?  à¬yD=èÞ˜¶ÃICÙS†¿@ÙÇYÔjˆyòGÚ>…^à™OØo.uK]†~LˆºCg·÷Ù PP‰Ä½ò8q}h‡ ýФ ޽ÂcL4q¯<ÞïwªÎÒÊ>æâ_FÓR7 mVKÕ¾#øøüûøuæåX4=×Ä{MÏègv»æ©¯øi%nÕÊ_šç5¸Ž; 6÷ <šîÅÞúÁYö?Ù¿íZ‚/=Çmõõwuv(öÒV_ʰ›¯D6ÔÍW8I‰¸ûF4ý·)Ô  †²?Úϰ›¯ÀwÞ´k›Â©‰ïÜT·Eˆ->ÄÈíïã[†0­‚L;OrÝãæ²ìÒGÈÙ½=¨6$ø7þ>,ƒùô°sÿØf~?º™pÃà¦6ˆ¥²kE5–ʬ•5X*«±7·(†ZýDeС ôGä6(M ?Ú <âcޤ=P=ˆzèwd?ôt¿êH e_ÍA4AþDÜ}〵k0&Ê£ÓŠˆ»o¤â럱Vö/!€V’PÙ­älÀ³â0•ñg`ör_vWa‰~|uæí$䤱`Ëx˜\g™Šúãíhü¶#ûI’Dï&6Wkù¦TOuð†ÃjžÊð$ÕßÊÅá&´Ã#ÄÍP#¨ÕÄ<ñöž³[Ò6êívZO¢”Ðþ¥}ÑÂD=t[·é¯úŒ,Ó¸7ƒš_6P³r­9CÜC§6VÀTtÄå>mpþKæàÆ|fLv{.öàKΦìý¯iÚ¨Ïu ¤ e_ëŽzðwƒØ_–±Ö5`­¬vL´Ú&]¶ºÜ1ÛªðM«_ßÂÚTíʦ và³^ ‚Fƒ&À×1Ñ rL´4A÷îkCeô êÁþO¶Àq·¨Õè+OûˆúÈ)ÔŒšì†P,]Åg͈M!?<ž[¿d\öŽû¼&%´tÉ€ö¡»˜haª¿•_ʵü\¦Ã4H42°­FîZ ‹‚ÍœjÆC¥hPŒ £ ¹ò|Ê?ú¶ßu 7R„.rÔíJ" "G­’º‹Cé(ì¡ï¶|éí é™œ@Ð…g¸½5¿l ïo/:’Æ+ 9–ŠjÊ>ü†²¿Aåedäïo"ì7—¹Om%D?ú{ö_z{Ÿ«PKjIÂ&ºßº¤/JÏ _r¶ÛÛÒkU 5+7R³rõ›w"YΨzÜ\ZgöM«U—±’MŸh_ š üÍÂÿŒ¹øÎš2 ¡n‚/9›²¾¡i_F¿ê1Øl´´ ˜‚,á[°ce•qKhõ‹èWÝÝNoÕ{²bþ dŒžÊ’ŸàÝTã|æO6Õ·N”9?ÔÌÜ@ ß•êÙP¥e°t†V~*×±±ZËy¡fæ™QŸ\Ýçv"ÿð[ª¿ÿ[CS¿ê1ØlÔêÜ’¨·¢ Úà˜ÁÈ`·[ºŽCéÝ‚ "‚âqù»+ßWt½4Co¼Ôí/{s Ù=Í¡ÛV„Éaн±™ü'_æàµ÷ºõ7òš<Ϥø~Õ1PZÊ^×+Dÿõž,e»DÙû_±ÿÒÛÙ1ý"ryŽÚµ[T˜tœXшµ²†òO–“qÓƒlŸr>™¿ŒÆ]Ü{7ÝK®&Eš–:ÂvNСUˆ¶¾‡Ýêi©¼ˆq,»ôv%Î#è’³ñœàÞ¾D|42×EµòXb#|×q§É&ðI‘ž¿ô"­öôvÜÑøù0ò÷ýП(7îB4‡0 €J­E¥:ѾXt vVS'›E·lï ½I+§ò2pæ¼þ÷`;LEGØ{ÎT|±¢WÇé#"Ðè{ܶÁDÔé0DEõ½AÀküø÷‹ÊhDãßs§•Á€68¸WåEý‰›õ[v±÷ìhضÇmý|ù¹ý:Þ]©§·?¾s¦ºÍ`¼=¦Â\q'¹þË!¸ “”a `oj¦jù¯ì¿üN Ÿ{Ù”×äñøÎ™Ú¯:ºšy—îgä¶÷0Væô©îÞçOwjÃçŠð«®bAf&óÓÓÿŸ=°KŒ}E@ò;ï°0/yû÷3wß>F\tQ¯ë \°€Y›7´x1à¦ç§§ãܳ4~楧0wnÊ{&$0ïàAÆ<òˆËý¶†&²îÂáåÏ_<,½={›ë5ø²þ Æ®(ÿì{öžó÷kÀzH2åÈCzQè‹éƒ;ÞŸ’Dñëï³ï¢[Ýj÷Ýß{JÛÍ=¬671bÿwŒ8ð=*sïžçÞæ ׆’'{òxºËè°Êïx/Nhâ–è–AwœÉiVñÞ¼–ãA¹ùäÒ{‹ RrõÒ~Õ1J:{g‰'¾—ºù•å^nï¬øà<™!n0ÍG*¨ßº»ÇåC/½”¤ÿü‡†ýûÙuå•ì¾æZòó™ôÑG,X0(}Ð"o½•àsÏ%ýþûI»è"šbÜsÏ¡ñõíU=µ[¶°ÿw¿£fãFÔ^^hüzî!)hµhz\{ÌŸÿÌìmÛÐvY®·¿]W¨½=ñ?£gÂî`Ò›j_oü—Ìqëùk×n%çÿžÁÞÒÚmÙ¥K™òùç,ÈÊbƺuÄ=ù$*ÃÀ:ô]H±=ÄÌ™½u+qO>‰ÚÛ»×õŒºóNæ§§;¯Ó7%…ÙÛ·÷X@ô?žY[¶t{¯Ee42æ‘G˜¹~=³·n%öÁчu Ö|0‹Œ›ÂÞÒ÷€øíñ_2µoïûÆIïacE‘ÛÞûô`«îídCîXQ²Éðm©ž'Ò=Ém>ù5l0ÝßÊ“‰\ntÇ™]uþzЋ‹ 4ÙN/ãÊ ‹Îì—³ho'EÂqÑì½]D¹Ni:óæé¼|ï¶»1Ñx&»×ö¥73yAýðÃ4<È®+® bÅ Ê—/gç%—`©¨ öÁeC–.Åî\|&MbôÿýŸKaÓ;9™˜ ìòË; ¼úðpFÞ|3žqqÄÜ?á×^‹ÚÓÓe›B.¸€À… ;n;ï<çù‚Ï9‡Öü|Ž|þ9U¿þÊîk¯e]RÖº:®¼ï‰ÇóÈ›oÆ#Æ[ÊÈ›oFãç‡ÊË ÑõúˆB/¾ØQÿYg1ò7¿qž[Ôé¿öZFÿéO.Zt‚³€Ê` êw¿cÔw  qyM¯¿Î†”l =øý Ýv/„ôsÙ{¨ º` ¢Ö½vM…Ͽ٣r#o¾™IÿûÚà`Šß{æÃ‡‰ºí6¦~÷âPœw2¶Å=õ£î¸ƒšuë¨Ù¼™È›ofü+¯ôºú†ýû©X±{[6#Ÿ”ŒcÇöøxÏøx¼{–C[E’ÿû_"o»ú]»¨Ù¸‘QwÝEâË/ŸPÖTXJÅgß»¥ E­†  Ç#Z´™:ô a»¿DÓZïÖºUÔþ>¶•˜Tüã'Ÿ©5"œ5ÂÌßÇ7²8Ø‚j/É.ïZ>àÅe:¬§‰UŒÆßÿE³†ºýB’,Nb{‰P²ŸhïÔPʲ½Cñî¼¶åÛ—Ep쌠‹Ît¥½H f3ãèÑT®\ÙÁNÉÞÚJõºuøÏžÊê%öHùòK¦¯Yè»ïfê·ßuû1Oâˆë¯gÆÚµŒ¼áâÿþwääضœl;–Äý‹k×uûíŒåæ>ìRÛã3y2Þz ¡-‡ÚÛ›äwÞÁ£Í^²jõj<˜øî»øLžŒd±`o=¦m »ür¢ÿ{üçÌ!ñ_ÿ"ↇ0ÿ÷¿coiÁ#*ŠÄý ãèÑxDGvå•„œ>Q·Ý€¨Õ2}åJ’^{Èßþ–”¯¿&î‰':´7áÙgý§?1öocþ¡C.SKu5Í™™È=Y Ü·\à3c2ÚžiІ#î^î–,VZuoÛ¦öö&îñÇ©Z½š­‹‘õä“ì»õVößv¾Ó¦vùå€C 2Ž‹ÚË mP¾©©.mdµøÏž}Â$ÊÇSOx : bCð2¸6QE‘Á>õÚKÞÆ1cÐøú¢öò"ô¢‹(~ÿ}=òé÷ßÏösÎ!ëñÇÇëtÇŽu¶Míé‰qìXçR¶ÆßãèÑ48@Áo€,£ BäðF÷;}Ä1fA¥Â+) ß”—Ú Q«Åoútôááö³ ÕR³q#{o¸ƒ÷ÞKúùè£-^ŒÇ¨Q'”oÚÛ?ïìö „)EWj ¹ýøît[àg]¸ë ¬¬ªÐñ׃žl>á[úƒ§ZæÊ‘­<‘ØÈßÁÍÆÒbø²DÏŸz±µFsZ„iî¯ þP#Ë26kk£d³b·wáå-Ë2’doû³u+:ÊÛÚ•Úµ~ïÔžÙë GˆÚÍ›OØW³a†‘Ç t[rrØ4s&k¨øñG¯¹phéâž|’’>b]Rkâãi-(`ì£v¨3ÿµ×X3f Û/FíåEÀüù'œ·ô“OÐ9÷…\pe_ @Ö“O’ñÐCøÍ˜ÁŒµk™öÓOøLšä<¾jõjü¦O `Á$‹…À6í¦ß´iÔnÙ‚dîxSÕlØÀÎ6!aÿw²qš#Køu×á=q"﹇Õ11¼÷^¬µµNa zíZVÇÆ²&.Él&øÜaô Š"Þ)†º}Â8n ÆÄ1n­S2™‘{0á 9ÿ|Ô^^¿ÿ~‡‰VÙ×_ckh ì GÖµ—sÒÒ˜¾r%ó32˜¾j³6oî x¼ùfæ:DêŠ,ÈÉ!é7PkƒüeóÇñÆçóæƒçóÂgòÙß.ã¦s&ÐY–yúÖEöy''3gçN<ãã±55a­­eäM79Ÿ›úÝ»iÎÎÂÝì­[ i»7£n¿9iiNM~Üã“üÎ;DÞ|³c‰ˆ¹ÿ~ç*Åì;œOŸ©+V0kÓ&¦¯^ͼ<<ãâ:´9åË/™öË/Ì;xI}ÔÉïa"ÿÕW©Z½Ú¹MâxO»Háfwcž`câŒãÜ{u‡`·½žð´Ñ5Vö³2]XH—Eª-"ÿÊ2òvžÇ)³l¬“¸=¶…?Å5kÜñ»Ú"òß<žÌðäPã©!¨w†ïÜi'µ2@’lXÍMØÌ-ØÌMØm®Mf\ØPövÎ0ôs A­Æ˜Øó¥¤@j[Öre;¨i³l¯ýkØ·¦ôtl ”|ø!ÞIIèFŒÀç´aŒ¹ÿ~¢ï¹km-ž  úË¿û€º´4Ìee.—Í›¢aÏBÛ¼°Ë.£âDZÖ;–‹d«•‚ÿü‡uIIì¿ýv´AAL_½Ú9ˆW¯]‹><}DóçSòá‡xM˜€ÆÏßÔTªÛ ^Ýá3y2¶¦&Š?ø€¢wÞ!ç™g:hËøÙnÇZSCíæÍ.…ä¡ÄsRÏ–‡¾s‡.ÅâQíXí¦M¶Ëv;µ›7cˆŽî¸]’Øuå•ì¿í6tÁÁ„_u•£žèhÆ=û,…o¿Í¯QQì»õV¯ºŠ)W3z×kÕü¸5‹Ÿþ† û ¹pv<êãòNË2üº3—ù£ÕŽ}a—_NK^µ[·‚,³ëª«hÉËcêwß1w÷n‡Æ½ÍîÑÖØHý®]øhµÝ£GŸ?ßÔTªÖ¬9¡²ŸzЬ6üš±cÙ{ãÄþñøNÊ¡ÿû?6ÍšEk^!^ØáØúÝ»Ù0y29Ï>KÈyçaÓ½ðyë­ÄÜ?U«Va*)ðßÙoÞô?‡+tå„§}„Î&„>®Hhü|zè̹¥FÃ_z±½æÔ ‹ëiçOñMÜÓB°npW ZTü3ÓÈKÙÆS»Þ‚J$ðÜ…ý¯h Ëö6?‰1ÆÇ¹nK^þ³fQúÉ'öÌ‹d6c*-uy¬¥²cl¬sàòŒwÔKf3ÕëÖuT9·Ó ÚM&—˃%}Ę¿þ•œ§ŸÆÞT*ªV­Â8z4æÒRZ ‰HN„¼LÌŸ­MÇj³óźtNŽ&)&„ÝY£4¬LËåš%ˆ›4a½ÈˆK.¡ø½÷œûëvì`óœ9„œ{.‘¿ý-Þxƒ .`÷ÕWÍ}ðÙg£2ð6ê5k˜?Ÿ¢ÿþϸ82zèÄkmjÂÖØèhgy¹Ó%hñbêwî$ÿµ×Øì"ÒAÞK/ÑœMÞ‹/û‡?tæ™4ge¹ìo•‡I¯½Æˆ‹/¦hÙ2ýéOƒò;{NÂ{L–ð+ØŽge•ñKhõ ïÕñ½u*j´ ¼™çÁ¶×F¶â×ïisi9æ’2,eUXÊ+±”Wa)¯ÂZ]‹<Èá·tÀ•‚ÈŽ°‰lˆšA‹Fßï:{ÊF`³,3±ìóó7áiiîw‚Z6Èíˆ ´!AhCц¢ÆÏ§ßõ÷¯)IðÎgƒzΡà”(=‡Á@o*-¥jÕ*‚Î: }x¸S+àœŒß¬Y”~öYaJãëëe¿Y£Ý–‚‡ (ËTüø#¹ÿü'à°±ê‘Í  Ž|ññO=ÅøW_ÅZ]í¬U“>ü¦Œ µ…ß±TT KÒ1‡Y¦zÝ:FÝy'-¹¹´äæR¹jÑ÷܃¥ªŠ†ýû»<·ÐΛµ9;›_Œ!*ŠÖ‚|&OFìlÏÉ€q|‚Fƒl\»£þ2”χ©¨Añ›>Ê•+;ì ˜3‡–‚ÎßdÄ…‚   EE&üq‡2ê#UÎÏÍf+V›ã9ihvL¸|Œ'jžŠ+È(¨dÒüT¤³Ñ‡…Qz\½È2åßOù÷ß÷ÄDÿþ÷ÇŒ¡9+‹êµk‰}ðA‚Î<{K ¹/¼À”/¾À¿mâX»ukûG쌎Ж*Ç5Ú[Z°›LFDÐGDòÅhüýÙyÉ%'ô÷@â5 ´÷š–ZÂv}FCx2Õ±³{|œÚ׫OçÛ[¯æðAO.‰01?ÈÒã TÖêZª¾[EÅ×?Ñ| s¨»íØEŒöc¶N<“]ãçcOLØ®‹dwì¦î[ÅÔ}«Ðô#°}§ˆ"¾³RºèLüϘ‹Êcàçá £ §„Žy¸hŽýùϢȌ5k÷ $ýç?¤®X¥ªŠœgžéP6謳˜µq#þû_Æ<ü0•?ÿŒ©¸Ke%¥Ÿ}FÌý÷“ðÌ3$½ñs÷íÃÙ§6Yªª¨\µ ÿ9s8òÅN¡ÖÞÚJí¶mŒºûn¦ýü3ñO=Å´ŸFÔé:hX«×¬AáüªV®ÄEõºu†ü0—•!K£î¾›qÿü'Ågd63ã×_Ixî9&ú)1÷ÞÛgAy(µ<Ýl‹8ÐèBƒÑ÷,$Ó@PñãÈVë ö°þ³f¡ ¦âûã¼ÛMB|&MÂRS²LKA²$±aÊ~ rþ}~×Ã}j×/;rIš>‘ˆë®£vËZòó0DF’ðÌ3–•Úµ'®Û±Éd"ú÷¿§zíZj6mB²Xu÷Ý»bSÏCò˜Š‹ñ™2Åù݉¨íÛj˸çžCΦٳU˜Ðø¡:¨çì ï’½ŒÜöAGz&¬©½û&P˜$ <{ØHY7K¶u¶“qËI›~yO¼4,…É£è,­ÌÛþ 7ö8 9;LÓ6«ZËæÉçðßËcÜLdwgà“$ê6l'ëþ'HK½€¬ž¢9#{@¯i¨ßÃÅ)!Pº;\P_iJOgë’%”ÿ=¾S§âGɇ²uñbZ ;†°©^³†Ö¢"|&O¦äãÙwË-Î}î¾›¢·ßÆî\¼(ýøcLGËv¶º:j6nÄÞ|lI >-­Ó%0€Â·Þ¢fãFŠß¿ÃöÜçžãÀ]wa.+#pÑ"ªV¯fÛâÅÔ´³w«Z½šš)ûÖ‘è¾ê×_©Ù°²¯¾r–±56R³q£Ó6ÓZ[KÎ3Ï G4@–i-*bûÙgÓpà PñÃì»õVed«•šKÿGû2#ƒúÝÇ‘¬ÝºÕif0˜œlv”C=36•”Pðæ›Œ¼é&ÿõ/‚Î<“è{îaÒGa*.¦hÙ²åãžx‚QwÜá-uÆ”·Ýw+V`oi!ñÅñŸ=›Ñ?Ì臦¯ÝÚ=ù¨5*Â.¿¼ƒvÒV_Oð¹ç’òÍ7DÝv#.ºˆø§ŸF²XœšDÙj¥fÓ&|¦L¡jÕ*d›ê6¶®Ì@ކº ½ä‚–8Bí”}ó ^II$<ó ¡—^ÊŒuë\†úé}x8ÁçžKã„]z)£î¸Ãù×ÓÐCýÅs’û³0õµ¹‰I[?â¼ÕËð05vZN4èÜ`.•Õ¤æoé^üpD‡ýø[R’(|þ-Òo|€ÚÕ›OªI´wS ç®yë¾yŽ‘G²ú_a/höðæç9WóÞÅ"oäÀ¼Çì-­T~õû/þ•_ý4 ×3\ä”ä”XòÖGõÎff iÉÍ%ýþû»-g./wT.L&ÿå/þË_NØW¿{7ÛÏ9§Ã¶½7u3´jÕ*—K˲$Qü¿ÿQü¿ÿuz¬©¤¤Ãù¬µµl?NÛÔtèÐ mÊ~úi²Ÿ~ºÃ¶º´4ÒŽs8°ÖÕp|Ö“OvyM»Ú¼ƒýȰþW2ˆ íý¡‡ÆÖØHÄõ×3òæ›±·¶R·u+ûï¸Ãóô(ÍÙÙÄ=õ‚JEõš5ÎûÀRYÉÞn`ü«¯’ºb’ÅBé'Ÿ ö1hpc‹™ƒÛ÷3~r‚S `­¯g÷5×0æ¯eìßþ† RQ³a{®»®Ã¦zÍ‚Î8ƒª_ råJB–.uéãNíÚ-CÝý"¤ª+~ø79‘ãYŸz!Õ¾ƒ—V¸Ê/”/ϼƒÈÒÃÌßö5ÁÕÅn?‡d¶õÀS48̨Gîê}Ä]xNGÍÊý¯h¡K§¡({­ÜVA黋C‰ "ô°ü÷=CIiE¯/àò¬,¢»b­ò00íÀ/Òyuë¶‘þ›Ü^ïÌ h>,**bÿm·QöÍ7ˆ:ÖÚZ—uèBC±VW#Y,ì›ÁÏs®éÓ5$enáÌõvº_ÔéµZ§3;Dµ¯/ÖššÛU¢VëÔò4þKfÿÆÓý¯¨ Û÷ràÊ»zuÌwÑÑdøûÈ5¦TT°¨¨Èù=?"•³¯¤ÞóØÒ£G\ úQ}©¾KD`Q` 1wÜ€5ãð€\ßP! "ûâg²iò¹´Ü'÷ðì$fmgvÚ÷x5×ö¿:œ³€¸Ww{½õ›wrðÚ{{u̯#G’< ×éŠ=s?Ñ£NTÐ ¢ µZmæ’Ý‚ÝÖEJ¡pxô»Ð…í‚(¨zU~ P²·–;È~úéi1†îÔh ÿáå)o©¨èÒÓÎ' “æ#G,cýþ¶î.—·d6»U˜ǪÀñÂ$8lšK˜(Ô~ýHÁ8Œ*ÎàÆ/žbÊ5δŒ¢a`2$`ùŽj^›p5áqý®o8!ÈÉùíg1c÷ã8ÓùÙ98fo_þW6L½‹Öý¿_õŠ5ÔmØîözuƒ§Õu'‚  ÖœÂ$€¨Ò¢R¨$´Ó4v¬©“t"8v²} ÍáþsEÅŠbR$&ƒüØÑÔúŸúÁƒÁÉ&PöÕƒu°‘l6ò_}•Ê“Èë_¡#jŸáÿ.ÖØ,,Øú%W÷<µG5gõe)¯¢Þ+€ÏϾ›Ÿæ]‹Iç1Ô—ïV4V3³vþÀÍŸý¤Ì-½ÎÞl* Û’Ïà­Ëc÷¸yH¢{—¨«Zçö6Ÿ χ+DQƒ«<µ¢êÄX¬íž&ù„ƒ„^Þ"˜È —Yàdý‘ƒü˜X2§Ï 15•¼òrv¦§šŸGDA>u³\pªsÒ ”'‰ßÞÜ×ýʯ˜ÎJÁÛÈBöæc±WŒ™NnD"‹6F\Þî~Ô:üðl©çÌõ2åÀÖ¥^H^ÄàÙm·ê=ùuæeì?Ÿ¹Û¿aLþ^÷Ô›]ÐÿJŽCíí ¢ƒ_´ßˆ=W 7=s•ÇDK7=enÊ·êŠÁPz2Ðb4²;už³gsÅü…Ž™±éÌ3ÉÉÉ!ëp&ué /È'¼°ï“|©m09™MA­FåaèE§ ‚,aõ’Yõ›[ ¾Žy¹DàWU¥—}DÔiuZ$ó`.ö£½’ó#¬Ìômä½|²›Ýìˆ!u÷Z ^,_t3ûX¼éS¢¦N$øœsÈ{ñEgh è¿p¡3>pñ™2… 3Ï$ûïÐþ ¬)å’Ÿ^£ <žµÓ.âükÏà;&b4µZÉ;RËÆý…H’{d_O=×,I⽟öòíâß^žÃüm_Z‘߯z$Ö°  öñÂV{’·’ì: Ÿ58j¡ãÃ$;v¸¬ßQ^t¹½³òÅÉ4À427f,93g2{ñÆ—¢M¯×“˜˜Hbb"­gŸEVv‡³hÊ8Hx~>…xºÙfìTãdºßNFsÞÒJÔHˆ%—¤ùS˜>}:²,‘›“GVV&i鄿9´ø~5ÕC}y'j_o,åUý¯hÛª—øc|k*´|U¢Ç$ ì”";jE¡cx(NCÔí7¡g÷µÇœ}§N%òw¿s›@ýûߣ1bÀÊ£D•âú¯ÿÁ‚—î¦ÒPÛì˜`øyéñ4hÙ¸¿Ç–­u˹& å¢9 |¸Ò‘d£$$–/x€¸Ü]ÌÝñ>Ãë^ÔøzŸt¥$Yio/éxçJömáMd©M…Ù¦ŸìÆk[–í„Ðn½ÂwÄ;hòòb×´éΞËUóç¡×wm¬l0˜4 Ihi>‹¬¬,ögfbÊÈ ¼ ðÂBŒMMC}YÃAsòäðÕô2¥Ü©Ž Kø[‹ˆö1³déù„„3’‹#.>óg›“KVV¦b"ÒÔÞ^'@)ê´ˆm9¼`a°…‰¾6>(4°¯~`#ꙵ2£Ç3¹à‚Ï=—Š~èö¸þdMëM¢FƒÔ…–NEGø˜N&t‚,£²Û(yï]>ø|=Û’Ï@6xpý¸bÑx|<¨®ï˜ŽU­±ÙÝ#CŽ™Lö¨d¦ø•™Û¿ÔþìŠÞ¦ùȲŒÝÚŠª]*NÉnÃn?ÑË[Ýþ d;]XBžpY¶õ¸¼ÂÀ Yñ ͜ɼÅg0*z¹9¹lß± Yrû’,#K2 ŸÀ”)SPµÅÚò0Iž8‘ä‰ill$;+‹=Y™X¦QX@Da¹˜†7*ã©åÐôöFX󘑒DÊÔ©Î{ÿxt: ãH— ˜ˆô•ñä1±p5¸ûk%îÝ̶ Ÿh´ ¬¶R–aÛÁBÆýû*7l@v"OÔéû裄_{-²ÝNÙ_÷ê«´æç£9’kÖpðøñG&¼ù&jv]q)_ÿìÙ Š,ÈÎ&÷…(xí5Rüó‘#x''£ dÏõ×S½nÞÉÉŒå¼ÆÇZWGá›o’óì³Èv;¾©©Lúè#*þ™ÐK.ÁRSCéGu3X”$¦íù™¤Ã›Ù<ùÒFúsÕâ$Æ„ûS]ß‚( ü欉œ;c,Fƒ†¬â^øl ¹¥Ž Ü?o?ƒÂŠz^úr &EsÇ…)Üüìw,šÃgOà¿]@NI-|Ãõ¢9 \¹h<ž†ë)üi%UÏÿƒú]»˜±z5-¹¹ø¤¤ ñõe×UWQ»ep℞¬>’dC27µ)¥NcQº˜†õV8T„É¡¢ÁÇ—]3f6gWÍ™¶]¬Ãè˜hv¤m§¢²¼Ã1Þ>>xzyb³Ù\ª^^^Lš<™I“'ÓP_OfVi™™Héá2¼¨ý y¨+ô“t†.tç6(`'ÀRÀh?X¼ä"ill ?/¿m’åx9J’ì4å—8ƒÁ ˜ˆô…“螺ððžæo%ÑÛÆ'E¶Ö ܪ„ ÀK_ïà?.Ex÷[Ên½žÑÇ•‰¹ï>FÞr YO<ùÈÆüùÏøÍžÍæY³T*tÁÁˆíV¤Ô>>hÛb{æ½ü2Ô>>d?ý4  ÀÖ,ŠÞyó‘#XªªPyz2åË/‘-2}ßÔTFÿéOXª«)|óMD]p0¾))~äüfÎ$ö¡‡(ûúëNçküü0Ž‹¸R[AÄd_ì6;ûrãÒEsâ¹rQöp¸¨š gÇñô­‹¹áï_c²Øð6êðòÐ9ëÓjTøy]YGˆòféì8–­ØMY­cEmVR$w^4•ÏÖ$=¿’«æ$‘òÍ7l˜4 Ku5š€BSR(xóM¬ÕÕXªÏ´EОܹdd¹kmîÉ}u§)²(rh\"GfÎbá’3ˆyb`^AX²x ü1’$:"”òŠ êëYõËJV‹¿ËÙgŸÐÉ àíãCJJ )))ÔÖÖ‘••ÉÖÇQÊ ¢ €°¢BtfswÍUPýäS¤v!<¬V _õáááÄÅÅ1.1ÑådK19½Hò±ñxb_•èYS¡e <¾ÝxˆES¢¹ïŠ™l>P„$ª( KtM!úðp®¼’ÐË.sn»'õaa´–”ôùœÇ/ózÄÆ"Y­¶W¯[Gäm·u(ל €Ýd¢µ¨cll§ç8òÅ-[†><œ o¾IÁk¯Q½f À5ß=OIJ?ñÕŽCÎòûs+$™ðÀ¾;?†zàÅŠgË uLL¬%îÓ E cU••dff²ñðaô™‡‰(( ´¸Í@„]PP製šP©˜9³S™0aB§Ú÷  `RRR8Rv„¹sæQ[[CVVYYYS\\Œ—7‘##»<_{‘ú†²23‘S(sÕÈVRý-¼—ïA©É½ÚJI–yþÓ-¼~ÿ¹,NÑ"ŸsI‡7³ ¡‘Ê_~¦àõ×;Ó˜žŽJ¯YFí}Ì6OãÓ³Pzòqñ­UUˆ †Q£hÎÊÀ8v,Öª¾;Y™JJ¨Ù°€ð«®"êÎ;)xã $³d kU³ìåÚö5['Ihh¢(PÛ¦mljµ`Ô£< Ý+?êšL4›¬¼òÕ¶Û½Š%æ:/^1Õò$ :(ˆÝÓg·`K¦¥"ËÐP_ï°“%dIn÷¼¼=1\;dTW˧DFz…ETVTP[[×a E¥R5*’±cÇÒÒܶm۸袋 9–[40(ˆÀ  fΚEyY9ùù¤íßOÊ{Ë¡RaÀ9*.ªd+æ<Â,\tÞÞÝ¿O›>Ý)p@VÛ@ àåÙ;-‰··KñÐ!" ó /TLDNvbvþ:®‘ŽèYQ¦ÃîF¹$ïH-Ÿ­9ÈÕ‹“°Ú¶jûãf²=»‚ä¹ó±þãX*+ ½ürJþ÷?ì­­Ø››©Û±ƒ°Ë/§bÅ <¢£ñ6ú´4g½²Í†6 •^ÊË Ke¥ËóW¯[‡,IŒ~øa2ÿò|¦N%pÁJ?ÿÜ-×—ó쳤þø#×^KáÛo;ιf !\ÀyŸÎÔo~ÅØ%‰=YelM/憳’‰ ÷§®ÉÄÂÉ1괷ų ðñ@¥©®oagæ®]’„V­"£°Š3¦Æ²å@qČݻF+t@„.Í,ÔíJvŒ-Ù& tQ3bû”C6èTÆ®Vs y" 3gqÞg84…_~õ% T*ÓgLgÒ¤Éë±Û©¬¬DE*ÛÙ²iÓFZZZÿB‚Bðõ÷£¸¸ˆâ¢brsrÉÍÉu³råJ®ºú*DÑóÇŒM|üX–[m´xzâS«„ZQxs…á‹Z€¥a&¦úYy·À@®¢°rs“£ñ3:·½þã^ž½í 椥a7™5,ee”}û-Åÿûã^x…99HV+–ã4е[¶zé¥,.+ãð#ÿê«.ÏÝtø0ÙO=Ř?ÿ™ÐK.qlËÈ û©§Ürm5›6Q»y3Ñ÷ÝGÑ{ï!Ûld>þ8>))L|ÿ}À¡5Ý÷ô³xí(ƒ˜ÉlØWÀå yãçPQ×ܡ΃yÈ2¼~ÿ¹Ì«à¾W~æó5I‰ ãù;ÏÄlµ¡Ó¨ñó2ðþÏ{Éâ¶ßêtBU¨Õz‡¹…ìˆMiw‘ÃÝ)Pž¨\;Û “íW„J÷P2‚½3f1~ÑBΞ2¥ƒ§ÕhX¸pß~óM‡c$I"/7µZƒJ)//§¼¢‚šêj$IbþüùÔÔÔ8Ëûûû“’’BpH0AAÁhÚ-Ož<™M›6±»-ÔÂQª««Ø±};Ó¦OwnS«ÕøûûQTXLYy9Å–Rah5xP4&Œéá2 ^‰Ñè‰,ËüüÓÏ456"ÉR›Ö^–%ô=sæÌ!((ø„ºdY¦ªæ& µkÖžžîÜ&>>>466QXP@aA«U*ôzÍÍM””sÅWž ¨59çœs8pà¹EEŒÊÉêîSè'a;Šoâ× _—è0÷2 úšÝy*¬ê°új±Úyèõ•Œðtn;RÝÄoŸûŽñÑÁøjÀÿÛ÷‰\ó­sñÿþGù÷߸` û÷#ªÕâå¾õ¢2¨Ýº€ý·Ý†ÍEˆ¢œçž£ì›oðNNÆRUEí–-Žåi aÿ~¶Ÿ{®Ó†`ÿï~‡­g´]W\©¬¬Ã¶}·Ü‚!:Q§Ãn³a*)aóìÙøNŸŽ6 €ú]»hÉÍå<`ÊÕ¬vW<Ö”¸Plv‰‚²:"‚}hjuŒ1Uõ-\ûä—ÄG‘Qàx~M÷½òIÑÁøxw¤Ž¼#å†$ˆì½ùf¬íÆA…®µÚpLÜ@TiA»½ãX¯vÐY®FÁeT á8a²gåzJv\<µ^ÄÒyóñ÷÷sY&**Šq ãÈÎÉfÊ”)ÔÖÔ’›—Kii ¥¥ µ½}|2Oc'„#Bðòã²þŠòrüýýY|ÆDÚkAµúØËÊÇÇd™]»ö`o V1QQ@òcG“5s6³—,bìØ8çvA7–ïÛ¼9Û3eJ z½I’¨©®¡¼¢œòòr*ÊËñõõí0 Âl21vìXBBB“®à´ ‹…ŒŒ 6¬_Ýn§¹Ù1 V”W°k×.¦L™Òá¼:o/gfQ[W‡|…ÖQèXlf¢¯• hè¹YEm3µÍ'n¯k>Ag²ØH;\êø»ˆHÃHÎØø¾ m¤µ¦†#_~Ùé¹j7oîð½þ8EA{š³²œ6”í±548í!{RÏQáµ=­ÅÅ´wØfom¥zÍšʆVäsÕò‘5uµRëã˜V7t´I.¯m¦ü¸~”$™½9å'Ô‰ t0PèQÔ¸÷Tp%Pº ¡Û¨p =¡82Š?ßû{T*---TVUcs±D6gÞ\¦Í˜Ž——ÃÖËn·QXXDMu vY"$(àÎ0(éÎcµ:^^Û™ íÒñG£ÑàçëC^^U®âx)c¦Â`ÖéØ1k6¾sçqå¼y.CüÄÄÄ06.ŽÌÇñô4bµÚ0›ÍìÜ™ÆÎi¨TjìÇ¥ ‹eï¾}ÎïALHšà² ­­-Ü6ÉA„#¥¥X'$¡Ñhf$þ~466²#­óWáä'P+qï˜f¶Tkø´Ø@ÓD/ Ë»—<•߿Ȉʂ¡¾üetÁ>bа7~6[&ŸC‹Þ³Ïu)“¹>ЉBЕÃcŸʶ.¶*ô¬6‚ àááALŒv›ææVªª —:îXÐW•JMtt4ÑÑÑ.«­ª>f[ãiôÀl6;/--ÁÇÇ£±û‡Õ××›ÅÊÎ]{:h<šœ¸x.{âq"GFQYUÕ©yżyóðòò$55•JMQa™™™”•U`³Ùðó÷#$$„F„„ ˆ[Ú… ê´ >>¾œwþy]¶Ó`0àiô #ã0ÍJ¡Ó†VÆûØø¸ÐÀÏ|.›JÃÚiqå÷/õe8¢$1)}=‰ÙÛY5ó ÒGOíS=²¢éè=™1ºpÎQ;¶KòrwuÀ±òâ B¥ÜEy…¾!I’ÅÑF£>Þ^Øl6šš›©ª®é¸ÌÜ QQQìÛ»»ÝNMM-o½ù&‘‘‘FÒÓÓ‰ŽŽér Ôjµøúx“Km]]§åL­­ˆƒ˜UáôÁ¦VãiôB§Ó2**»]¢µµ•ʪ*¬í¢  fÍšíü5*š¨QÑÖ[XXèü,öÛíöNÓ5¶Gýý©©­#íÐá¡î.…!ÀK-skL Æ_ßä—ÔKi4úعŠGŒ&'2‰ØÂýC}Ùƒ‚Öb"ªäPßJÅü®×H’QÖž A´ÛOŒâr,—·$!ˆ½ðò–í ˆÈŽlÞŠ—÷€#I–6 §§'>¾>ج6ššš¨ª®éV[Éo÷;òssÉÎÊ&7/—üü|çþÜܲ²²3æD»J???L­-¤íÜÝéÄAEöí݃jùr¼\|+(¸ Y–¤^¯#fT6»½K‘®+ìøuùwË=v a¡alݲµFÍYgÕeF£žé˜L¦¡î"…!&¶è ¿)<ÄúÔ¥ìI˜Í@Ù­O]JLÑA„ÓdüûqÊ’wï‘e»Í„J­sÕ—ìV${^Þ #Kv7}÷Kײ,;„Jde©{Ði¯¹ôööv,CÛ¬446SSÓ¹p©Õhˆ5Šü‚—eÖ®YÃÈ‘#Ñ·@×étx{y‘™™EC'y‹A ±±‘ß}ÃÄMñïG \…Þ"Ë2–6áÒÃØh/lv-Ç™ˆtEdT$\°”ŒŒtòóóœÁÍÛ7–è蘎E‘**+IO?ÔåyìL#©],X…S­ÕÄâMŸ’“ÆÏs®¦Æ'Äíç¨öÁ±ÓI:¼¹ÿ•u‚h¯Ÿ—:À‹Áf-ô#(¹"Pö I²!YlmÖr§J%÷Bo,E˜t;2½²M´ÛíÎ¥o_oü|±X­466RS[‹$uüDQ`Μ9Ì›7ÏáL ˆ¢èü|ôÏßÏ—††FÒvvîP ˆ»vìÀðÓÌ;°•bS©0„H’ä|vŽšˆXm6š{`"2*z£¢GQUUÅ×_}EëqnVÿº†k¯ ï`·ìéiD«Ñ°oÿNí9QäHI1yß~ÃäíÛðhnFáô"¼,‡¾zš-“Îfû„ÅH¢ûbWlšr.ñ9ihlî ÙÖÜ „„„0·¼œKq—7jµ¤¤¤žžÎ3uuøõï4½£?¥bCÙ/º[…V2å CâÒòÜŸÿÌô3Î`bòDƒ;M!w<„K__üý±X­Ô7483áh4ZºJßmÐë1:œIs'ƒŸ ÔÖÖ°÷Ûo™¼e¾Js…aÆ &">>ØlV›Z¨n‹ÍzÔ/;…þà_U…Uò®ìÙ—ÃG§f´±hÞ,&LHÂÏϯo&">>øûûcµX¨ohtdµi»Ï}|¼‘ìvvíÞÛ©¦( deçP÷ýwLOÛÎlîQN_|«¸lÅË;µÓ/Ƥõ²¶|Ü~ûí\qÅN{à––*ªjÈ-qs—,aܸ¼½½{\ŸÕjCªªª]îD‘ÒÒr¿ûŽÉÛ·âÙ‰§·‚Â`#ÆÆfª´ÑTYe²~ÎÆó‡­DxK,ž?›Ä¤$|}|z.\JvI‚¶ 7þX,VÌf3YÙ9TWwžÿ×jµ°åÇÄ­[ËØã¼Ãºc|æVbŠòýÂßP:vHÚàԵŶÙl´´´UPBAqj•Z…—§Cà•e ‹ÍFIy¥ÕÄÅŒ$2,OOOV¬XÁ¼tÎì— ¥‡²O¢ µJ¢Ðæ4lÅnë"lp¼±ª 8R)v"žŸ[¡rívŠ‹+.¶u+ùË¿ccdLMaîÂE$$ÄãåÙ÷4U’$±yÕJF®]Íœ¬,ÅúDa#Ъò¦UåM¥IæðŠCx~·‘H‘Å‹æ1.1ïžÛ˜94— R©¨®ªîT˜E‘Cé´þð=³wïBcq¯—­ÂéƒGk#Q҆L ¼³gV«•V“‰ûÓb²àéeD«Õ Q«EY–‘e°ÚmX,V,f+éYù44·46šI“&ñðçŸ)ËÌ·{€+ÊÁEÔjý1e£¢Jƒ,ËHÇ7W=À5"ËŠãOЉ”/.£ ʬ`ÀQÛm„^X€uÛ²¾þŠ5‘£Ð¥¦2gáâãâ0=ªKEòóó(ýá{¦nߎG‹æDax"¸[•/-*_*M2éßíÃøÕjFiY8ñ ñx{yõø®O!ÐÒÚÂŽ+·n-!eGz\Ÿ‚ÂpÂ|üÜwÖY477“Wt³Õ†—§½^‡N§%<È_oo z=MÍÍÔÔÕS\^E«ÚŒ(Š”©$Øß—ÄÄDžyæ:Ä+;vpóþý„¹±½ý(µH¯DµK+QÔ¸(;õÇêYŒs…a†Æj%2?ŸÈü|¬[6‘ñÕ¬ŒŒÂ8c&sçÏg̘1xx\k³YÙüË/Ä®_Ǭ¼Ü¡¾…n»Ù+Тò£EåGUƒÄ¾¯va´ÿÂØ,˜7‡øø<={6Ñ:Š ŠÜ·û+˜³g7êÞfåQPFÜ<ðÈ#œ?r$QQQTTÕPTV‰ÑhÀÃC·Ñ@|L4E……<ýÊ+Ç-¿ý-SÆÇ³÷p& I2ûçâçãErr2©©©„††òÄþý¼îÆööËË[ Ôk:S â‰rc›@y|VnŽm>ÔƒYkàÐX­Dåæ•›‹eËfö}ö)+"£ð™=›9óæ2zôhŒ‚(’“EÍO?2cÇô¦“?̉ЃÜË §2"ÍjšÕþTÖH”½þ%¡õÙhgÎd΂Ä‹‡±k§ˆ–æ&¶/_Nò¦ V(oNnl€oHK–, ¡¡Êš:t:-z½ƒ^ËèÈ‘¬Zµ ý3Ïðû¶ãäÂB¾\µŠ¯½ÆØÈHdå9–À­Vjêðò4¢ÓéHMMåiQ7ÚTöˆRÑPöšNp\DÃP=@2¥ëŠdYBT®vtZþdÄ#.¦ÿ• #´f3£r²•“yË&v}òßEŽ"âŒ3(Î8DìêUŒ-,êfº ¸Ø¡n‚ÂÓ×å/YÁ¢fÚÆõX·m!ý³OX…çì9Ìš?±cÆb0èåEQ`×Îhþ‰ùûö¢ê"㎂ÂÉ„ÝngÓ¦MÌš5 »ÝNs« ­FN£ÆÓ G²ÛYÿì³Üßî¸T–¹þ¾ûøá‡ð0è0[­hÔjꛈh{>öîÝ‹V«E6™Ü&Ê)6”ƒ‹,YAÖt”e°K'®Ì8rdYB ^Þ²§¸—·&П€³æQýÓº¡nŠÛÑ™LÄde“•…uÃ:FÙlˆ§PÚD1ÑxOŸ4ÔÍ8eQ{z ò4bo:ùík;šˆlæÀÇñST¾óæ2ábš›XóþûLÚ´¿êêþŸp <ÔMP¨ç«ª¸á‘Gxùå— §ÅdBoÐ#jÔõzJJK¹¤!î “‰ºúz<=ô44·"ªê[°ÙllÛ¶¿þõ¯|„{ƒœ+åà"Ë2v›•úXÊYI²!Ù»ðòv$ü¶ƒÜ3ÃIY–AvÊ>•ׯcž|KY%{Ò‡º)Ʃ晪‹ÁØ—CÔjú]—B'ˆ"¾sS©^±f¨[âV4V+‘y¹DæåbÙ¼‰ï€,ÌÏË=¥&\~ ¦u† G­é333 G«Ñ‚vIÂÓÓ“\ÀU¢ÈB@§Õ9€N£B233Ñáþ`×ý ¤”}A’lH[›=¥Üé2¸ kËÞ ‡§®0  ñ÷eüç¯3òÞ››¼“€àKÏfâïræ Ñè?ßÚ·ç1O‡Ý .Z‹…¨ÜFådŸRÂd๠ñ›?c¨›¡0L¨BCC™;w.¢(âiÐ!ÛedI¢Åd"$8˜eÁÁ'Œôv vâD<< 4™ÌØívr2³gçvA`Ñ¢EøSëæö*^ÞC‡,K]6W\žz€ yÏ$}õ¼¦$9Â#) +<ÆŒ"þõ§ýìèŒC—mâtB;"ˆ‰+ÞÅwöÔ!kC<>O7†ÈneÌ‹uS†÷iµ,[¶Œ   Ôj56› ‹Õ†Ù.S][ËÞxƒ{CCÉÂ!€î7Ž'Ÿ|’7Þú/[7näïþ#^Zqqc¸ð ùë_ÿJ}}=¿ss{û·ä­ˆ<‰’z±x&Å“ôùkX+k¨Y¹ê•¨ß¼ Ùjíå ½CðJNÀÿŒ¹øŸ9CôÈ¡nÑi‰vDãÞ{ž²¾&ÿ¯#µš·Š<Ù#<âbóÂ_0&Œê¦( 3^¶X¸þúë™0a÷Üs#‚ü(¯®Åb6£V‰”×ÖìëÃûï¿Ï‘²2jjj˜ÌAAÜu×ÝœwÞ¹”––rÉ%—põÕW£Óé0 TUUqÎ9çð—¿ü…Ò´4·Å¢ì×$RQ (Š@Ù4Aþ„\½”«—boj¦iÿa¬U5X*ª±VVc©¬ÁZYƒ½¹eØå¼®©kÂbéèåiÔãiÔ÷±ÆE4èÑú¡ @è&ÈmPq1hC‡ºy ‚Àˆë.&àœÔ¬ÜHÍ/¨ßœ†dQ&ZC‰&Èÿ%s8c.>3'#¨•׽‰øÏWTpݪUríµ×2:*ŒôìB$I¢Ä.Q]߀A¯# (ˆ¦V[ü™ŒŒt{ìQ<==©¬®åàá\ì’„Á;€écâãã¹7-Ë€‹þŽõGC))倢¼aú‰ÊӈόÉCÝŒóÍ÷[).íè©:uò’R†&ݗ©ƒ&À+Ï'äÊó±7·P»v+õ›wb)«ÄZYƒ¥Ê1Ñ’Ýr§?¶ScpQyèÑ´M¶üщÿâYxMJT42 =BÜT75a4±ÙlŒHá‘ ¬6Z‹…Vµšº&G|âÿ¾úñcFsÇw R©Ù´s?­f3jµ±Zíä•2"ПË.»ŒyóæqðàA>xñEnèg[•%ï¡C„.m(Û ”‚#£3}bׯ—Žò½3¤0lQÆw£2zxîBÏ]Øq‡,c­­ÇV×àí½ÚdÄ·:´Odžê˜ôàlç÷½ûó9Ñ1kèÎMüì‚Fƒ&ÐU'Y¯zÃ{ÀÛ]„ xzz22|½=ÉÌ+¦¡¡•Z…(ˆ´67ããiäü# lÙ}•FM°?>h5Lf3eÕµÔ44²óÀa¦ML$##ƒ|7´Sñò|Q…Z¥s ’ÝŠ½«°ANaÚ„D¡ ¡R8>…‘ t#„* 5å%VP¿/_·T§¯W#šúæ„¥öÑbˆÕÛPcÂTÑÔ¡Œ=$ClT/kVP8ùxxá…xúé§Y·nõõõœsÎ9LLˆ¥¶¾‘†¦›[Á.±gÏÒÓÓ‘D-*­O#£Bƒùé§ŸØ™–Æ9çžKâøñ.(¡ª²–²Ê* 8ˆÃ;¼?ËÞJÊÁEÔjíWtD•Ùu.ïÂdǪp%‚t.€ˆ8n—ãË+jæaƒ"ï+(¸Äå8¥Œ? § QÀrÉ%—0Öjå °sçNþùÏ:—Áív;õõõøùùáççGnI=!þ>|þÙgDFFR\\Œú©§ÈøË_JK³‰#5\ýõøùùñÁÿÛ¯eïþ”ÊÝ{QÍñý&¢¨9A l“ô„ÎTW¢W›†2ʨ©pêàÎ;WîÕäYAáÔ# øÐjåQàc ##•J…^¯§¶¶–wÞy‡Ûn»—^z $IB¥R!Ë2{÷î¥òŸÿäùª*Û·£×jQ©UHv ½^ÏŒ3ø¡ŸmP–¼“£ A¹Ý_ÛŽʶ ”r‡ÿœt:{µYaáâ7Rž1…Ó‘^o®ž¡n¤‚¡Æ[­¼ôÒKlß¾‡~˜²o¿EßÒÂí·ßNCCµ ›ÍŽÕfã’Ë.CØ€€K.½“Å‚]²£Ñª©¯¯çƒ>àýl—²ä=¸È²k5.lYÕGpö³L»·¨ëN–ÛâÍŸðÛtV^qÖ.¸Ö u«†ÊŒXA¡=°ÛÙóÕWüøÕW<xáïP«Q«ÕùùWZNVM\Bַߦ¸´”»cbh6™¨knÅj¶Â×_Íš5k¸­Ÿmê_Ø Åü®·È’T'¦1–¤Í½ëúÚi*e¹KY–:¾/ï“—?©"Q*œœôg2Ô“eÉ[átFLnÇ!LXq¤j  4$/µu”VÖÐlµãBIu-¥Uu4Ô71"?_æÎ ô´Q4”ƒ‹,ËH6íߘ²Ý†Ô•—÷±„ß®q\œY¶ƒÜÓò'7²ÝŽ)¯kMÖêÚ¶?Çg[m=’ÅÒÿ“ ~Gjð0w4¤µ®öâÏÉ‘®PU¨}½Ðø9¼…ýPûû¢ ðC?2•§q¨›¨p*¡Œ? 0×fd°téRø×‹/’_Lyuµ¢#ü ÍfC’$b"B ö÷á‹/¾ //¯ßËÝп°AÊr\ß$;’¥¥Íž²se£‹Àæ½O]aÒV×@íº­Ôþº™Úu[±76u“ú¾í¯=P3Ô s‚J…wj2~ gâ·p¦’Žqˆ­V¬5õŽIWMRËÀ¥cl}±J }:¶µ²Žš¼Œcá[PÞ¡Œ¶®’Z¥ßú  öötL°ýÐøx¨,ñ)¸Ÿ1À¸¶© Y’H7–¦¦fªkë±Úmh5j‚üý0 <ÿüóüôÓO<DºáÜJ`ó¡£»Uh%SÎñÈ2åŸ~Oå×?Ó¸k?²]YÆ?Yívê·ì¢~Ë.òŸzý¨ΜKøm×¢öñêÿ :"Ë4îÍ ö×M4lß‹¥²[M¶†¦þ×ÝCò#h<ëÎ>[YœÁ¡Ÿ^u~€å ÚÕ¸A%¢öõAãï‹.<ß9©ø-š‰>2|¨›vz RáX >5QwK¯½öäææò÷¿ÿQá<û쳨Õj–.]J^^oàHñèú%PötÉA™ˆõ E lGkNÙÿ÷ ;÷uSÜ€)¿˜’7>¢â«Ÿˆyü~Μ7ÔM:é‘LæcZû5[°V×u“úÎ)¾ú%Û%§yNKVµk·’÷ÄKb£ð_4¿…³ðNMêfž²FEМ‘=ÔÍPfÑ+W¢2_|‘àà`ö­YÃ-Àû+Wrî&„~¬ŠöÔ†RYÝêŠ@‰ãÅ[òúû¿ò’åÔQž®X+k8|ûŸ 8k1OüM€;_o§õ›w’ó𳘠K‡º)NE[qŠÑšS@IN%o~ŒÏÌ)Ä>ýGô#û–¾R¡s|çL=åJkûL8pð»¶}S✃à”ã;gê´üÔGÑëÅ//£ð…ÿ*Âä)NõOë8ô»‡3†^b«o$ûÿààµ÷+aR¡ÿÔoÞÉž³®çÈ;Ÿ¤<î$üŽëÝ–bôdÁ ‡`9SµÎå활@àù‹ð N^º‹zÑN ±Ý_wßV^T!ª“6¼†¥¬’âWÿ7ÔÍp;Þ^dŒ‹§Æß¨›2¬hÜu€Êo~êfœ4X*ªÙsÖõT|ÞßüE?œOÒw–»‘ZMä=ù2·ü_É,z‹ÚÛ“ñŸ¼‚!Æ®( NÐ)ÇgÆdÞyNy7‡ ªPk ¨4ÔT.âRB;òDPèRHìÿ[z$„?vžzŽ7…Q#I»â2þö8¥÷Üɪ3“M­X84lÛ3ÔM8iÈùÓ3XÊ«†ºnG%[8•#Tô…Úµ[8òþWCÝŒS Ãè(&|ý&þ‹f uSNÄÒP†Ýt9ãþ÷/4~>C}‰Ã AP©tä=A¥At!TªÛŽè¬*è4¿­ÐËòÃ[]ÃP7ÁmH¢ÈžIɨÏ=‡»®¼RR¦R{ÁElÛ¾•µ[6°ÿ±Ù¹x7œ:×Ý[lõ§ïµ÷{K+uë· u3ºDè¥L( ágÎa¬¿jÿZ$%““‡GKËP_ʰ zÅB¯¿d¨›qJ¡ò2ÿæÓ”¼õ eï…¹¤l¨›tRãn»iã¸1„ßv ç-êK–¢Ú¥¸'Šj${G3A5ôÅÞ¡íù¸ƒAÉÄ2D4=Ø:{&“.¹”ùó Õjû|}}9cÉ™,\°ˆôôƒlÛ¶…ÆíÛ‰ÍÎ%¼¸Q±Rp© ä”ÒÞ«%A¦ƒœ5/•³Î<›V“‰];ÓØ¶uâž½ÄfçRVÞ¯ëd§5¯h¨›pj"„ßz´’†û¨üægªW¬Ô[§ ýóòv,ÊêÂB¼` Aǘ衾¤aM§ AÛ¹¼é­Èwô:|ìܶáô}AGÂB9°hç]v±±±¹^–E±CD{•JÅøñI$&ާâü¥ìؾUÛ¶šžALN.ÆfEK£ÐŽ“B°êY ¶j"…<.»úÇ%"½ž3f1}ú òòrÙ¶}+{·m#*3‹èÜ|tfóP_Üàs2üä'3‚€wj2Þ©ÉD?v Ûvc.:‚¹¬KYFÕ(TFGæµ^þ¢A‡ÊëÔϦRëz`¾%ˆ‚N‹¨×!êu¨t:tz5I翎פDÅN²‡Èr'ñ0\Œjç+wrG˲Ü&µ¶Eeç?.Ë+¸Y8”HÓ’Å\}ÁR|}|]”rm†ÄÙgŸËÂEKØ¿;wlCÚµ‹Øì\F);­µ4 §2¾æ<m\vé-vx¯9&_£FE5Ц³Ïcç®46mÛŠqÿ~b³s ¬<õlH†Q«ÁwNj‡mYe:|Jô}ª/öš)L ¿{¨/kÀ±H~»½ût¬V”ñštê ÝîD–l jNÐ:J’턲N1ÿ˜èÜÒ¥†âD¡RVÇAĤ׳uF*‘gŸÍ™³ç Õ[âîÍÏ Õh˜4q2“'QZZBÚÎü²c‘‡2‰ÎÍCo: µ4 §*ÙB`k:3“£Y²øLt:cGχÑhdÎì¹Ìš9›ìì,vìØÎ®´Äde•_ˆÆª„SPJúµä-+ZÉÞ"Ë2’ÝŒ¨Ò:²Ýv‚ý$tlÞ[ðhyG6h…Á¢2(ˆ æ2û쳉Ýa"püOxTÓ“õ“ÐÐ0Î;÷L‹Î`ï¾=lÙ¹Þ½ÄæäTQ9Ô—­ piÒuözÂm‡9óÌùŒKHDlK¥Öù+®ã3"£G!6v4õgŸËÎi¬Ù¹€‰ÍÎÅ·¶n¨/]Aá´¤?+ÕŠ¤Ò7$ÉŽ$µ""]ÉŠnˆ#£h%“C qY0³.ÆÏÏÏ…¥ŒÃX¡ç¿‹Cè<ö]§Ó15%•”))°{×NvïL#:3‹QyŠ–FaXãm)$ÖXÇÙg]D`Ps{gOÄñ÷ÿ õy{3oÞfÏžËáÌCìÚ•FýÎÄfå0²¨•Ý>Ô—¬ pÚПl,Ê"jÿ» Ù¤&ªBOQÊaNNl Ùóf“:m:¾~~µ,mƒÖ Jt·¬×SA &:–èQ1ÔÏ_ăûY}(ЭÛ¿ÿàPw—Âi† ËxZK‰T—2{ál‚ƒ=r;ó¹Ý&Wõtx>:KÐpâñ*•ŠÄqã—HÅâ3Ù³w7?ïÞÉÈŒÃÄäæahiê.RP8åé»¶±“ 6 n@(‡)6µš)“°NŸÆŒ¤ èô:ŽÙGGO$J'}[úù„Õßߟ+.»œ¢âb>ÎÊê.S8Ͱ«TdÇd¼_)) ð0p9Ù:*LvS_']AAA,Z¸„9³ç’‘‘Îö}{°>LlVQ…CÝ] § B_†±6”µƒC(‡! Þ^l3‹É“ˆ‹ŠBTµ™!Ëíƒä±e½®ìÄ:§ëµãìa00bD0UÕÕìÚ½o%¡Â ÒèåÅ–93 ›:•ÑcÆ Ñ´…Ôíê vO÷-ú6ñFKRR2ÓR§c2µðòsÿPJ7Ò£"Pö‡®ß‹JAhï?ÕÝKW8!n¥‡²ÿŽKvÊdÆ&ŽÃ××÷˜„ç¢kÚ——õœ›ã˜À(Ði¥Ý àçë‹¿¿/‡2s(,*Áf³õº…¾R42‚}sf’8y2i¹œl9?÷e¢uâ˳§ZL?‚ýÉ8œ…‡’JAÁ­ôK ”Y¥7‚ˆª]JIê&eGa@è2â‰ù…nÒ/*t‡E«¥lñ"¼ýv (**r½š-·&OLRÔŸŸ «SUú£Ö¨Ù–¶‡zE+©0ˆH¢È¾ä$*S§0qÜ8< –mï´Ö“ÉÖQú»ì-Š*F„ ÕiÙž¶‡ŠÊ*ÅaMAÁ͈‚ } R®xz÷•Z×a‹(ªA–OÈ–£>z€ëj×ñ …Þ• Ø!‰"†àçëÇ”É)ääSTTDKk›q¿Üµ¦¥Ã@*»ú-ºw>p…V«%8ÈŸ†Æ&ö¦íÁªÄ¢Td~>{ )çžG”Z…Õj=q‚ÕöùØÆßF}ÐÉÓS-¥V«%$8æ–vìÚ‡Åbê.SP8%éD!):¯^!Š*—ÛQ .Ê^þ:½ÿ1²§Øl6ìv;¢ "#"‰‰¥¦¶†ÒÒRjjjNt4pk¯ï?`zyñ÷÷%/¿ìÜÅ´AaH°=9ÿ¼óñð𠨸ˆü‚|jªkºôêvlo÷ŸÓÛ[ît¢Û-¥——'~–™«<# ˆØ‘By2{‰à:”¼«÷ç1Kö^ü@½ãÔ›¬- Ç$GO[-6|½}ñòôA ¢¢œÊÊ*ìRGå}OcíõTK)þ~> zví9@UuÍPw‰ÂiŒÆfE£Öb³Ú FèˆPÌf3EÅE);‚K9²3›‘^ѹ–Rü}ñð0°gßA**«‡º›NyúgC9Ô­?Éè¬Ã\lo[òv-"Ê]UäB:íTpT~Á~!Ë2V› dQ¥"(0ˆˆðjëꨯ«£µµµc¬=7œS­Váïç‹ÙbfÓ–4ZM¦NËš-ÅNLaÐåc÷º^¯gÔ¨hâââ(//§¢¢‚––Öãž™öN9=yõDK©V« ðÅjµ²ik­­¦î+VPPè7ý±¢S¤‘Þ!I6D•ÚÅöÇ|g©ã—€ä.ìô\•oûMÁ}¼½ì¿TTVpßïïG«Ñ`·ÛehmmÅÓhÄËÓI’hlj¢¹¹¥+Þ50½__oJJË9”™$unÆœŸŸCÞ¦uÌÊÉê®R8 ±Û$Ä6-~€ÁAÁ´šLT×TÓP߀sšÛïÉÖqψA¿Ÿ/¥GÊÉ8Üõ3¢  à^”°Aƒ‰ŒÝfA¥Ö:·H’IêÂË»÷a”0AƒAuu5uuu€ÐáAd‹Å‚ŒŒZ¥ÆÓh$Àߟ¦¦f,V²,õÒNÌ1`z{{aô0p ý0¥GÊ;m—Ýngïž4¤MX¸}'j%t SPPÀ¡ÌCÌ3V‹Ømv´ #‚CA]]MMØí’Ì‹ψ·žžFfdRRZÖii»ÝÎþ}; )¯ê®RP8¥PœrY¶c³¶"b›Ü纕ÀæÃœîI–¡Ý€ØÑXÌj³:ÿ×h5 zì’„$ÉX­¶ þ¢(âíå…$KlÞ¶“¦¦æNË677±}ëFF­_ÏØÃYCÝE §)?þò#éé #1aGµ‘6›ãž—dGz???Z[M˜Lf,V G—À…n†¥ã']¢(âãã 2lÙ¶“Æ®ž‘¦&¶oÝ@ôúõŒÉT²H)(¸Qè{Fn¹OI d¹ë•E Fh-jfÅO+˜9}>¾¾‚€(ˆíyèÄ”fΚÔÉSÐéfpèZ{ô«ÍfÅj³b±˜ÐjµhµtZ,Vf³{»h‚ àiô@¥R±cçÞnžiÛ·à¿QyF%lÐðD(‡1þÕ5øW×`Ù³‚µëy>n Ñ3f1cÆ bcc]˜ÇÏ¿¦äË/¿à?¯¿AaA>Þ¾>È& FGMm=‡³òºÔÐÔÖT±sóz&¬]OxqÉPw‰ÂiŒO]=³6n¡u׬ú•ŸÇÅ“8s³fÎ",, è^{ïø_Æ.ËX¬Lf*•Zƒ¯W›¥»ÝŽÑÃ@cS3{÷gtùŒÔTW³kË:&¬]GxqéPw“‚Â)¢¡*º¶?í PvÔ—Èöù¿e%lЀ¡µX“™Í˜Ìl*·¥ñýòåX¦LbÚô¤¤LA¯78 ¶0;è-e»]â©§ÿÎõ×]Ϙ¸ÑX¬¶lÛ§§w§çÍÍÍ¢`Ó:f¯ß„WcÓPwƒ‚†–V¤3î`äòÄö â"|˜9}ÉÉÑhŽækèZ{¤¬œï—/ç¦ßÜŒ,I´šZÐéô= ¨Õj²sòÉÎÍïÒÎ277“‚뙳ažÊ3¢ 0(ôËË{¨""*•Ö)ÉK’ ÉÞEØ ãÃË86ºÊ¢ÒÑ,Vp†µQ„Ê$¨¢’ ŠJL»÷÷ëÖÅÇ1zÆtf̘ÁȈ‘Çåò>ñx‡3B׿‘Ýng÷îíˆ7²0m—HaX"È25”{L¤ºª•ý_o$hùL›2‘ÓgØ¥ö^–d²²²hmiáûß3>qñIIÇ&̲ë|^UUÕdee—‡$Ë4·´`Ð{¹Y:ÑnÜ©¡<>ïsw^Þ²,µ•wã°LR^´Ã ÏÆ&’÷ìCÚw€¢M[ø`t Þ©)LMI¡±©žƒ;¶“¼va¥G†º© ½¦·‹YåƒYåCÝJõO{ØöíwDNMajj 11±'”¯ª¬dï–u$¯]O¨òŒ(ôÙj%÷±)ÿø»¡nÊ)…[Êv´dæ²oéoûïÇð7m¨/o˜!c·[U§Q–ì'8äÀqKÞ=MCÖ±¼"D5‚("K]C%‰¨‚B¢ ©OÛ͆ ›©÷õaîÚ ½ Ô¹§Ö^Ÿ‚Bo 6»g-ƒòí;YõóJZ'ŒgÊÔ©Lš<€ììC”lÚÀœ ›0v‘·[AÁ²ÍÆ«OãÎýCÝ”SAîûx wb¯gkh"ý懈}êAB®8o¨/qX!Ëvì6;‚ v)÷)Í;Á;9™ñ¯½ærŸµ¶–ç ý § !éÍ7)xõU*ùÅe™ñ/¿ŒgB;/» km->õõÜ8g>¾S¦¶üÇËgòdÆ¿ú*;Î;Kõàä'V{{3qÙ2J?ûŒÒO?í¶¼ R1á­·0•–røÏ”6*œü„”WR^Aë®=ä­ßÄÆ„8 !#ÐíÛÇ‚ÍÛ”@ }¢ìý¯ar€p·†Ò‰$‘ÿÔ+ø/™Æßw¨/sØ!w#È÷'œÓ)­±‘ú;¨ß±[CÞII4îÛGýŽ4ìÙ3ÔÍ@Nà‚]–ñˆ‰Á75•ø§Ÿî°Ý0r$žññ=>—oJ ^‰‰ƒz}??—,±ûÛ4ô²Ë˜»w/¡—^Š!2rPÛy:áOÈyç¹üóNNêæ V‰,N‰ét¿( ÌJŠ$*2¤ÃvC«‰¹a\q8›É~̬ ›»&ÕÞÞŒXºtP¯OP« ½äDƒ¡ÇLj: zù娽½{|œBß)ÿdùP7ᔥ_vÌB×㉽©™ªïêK<)Q4”Ð’›ËÁ{ï`ä7â?k‡~Ø©åk>4ØXêwîÄÞÚêÜ® Dãë‹©¤ïI“hÉÍÅ\v¢wŸ6 ÏqãhÜ¿k]s»!2ÙfC²ZñŒ§>-ÍY¿ÊÓÓ)8é#"0ŽKsf¦Ëk±·¶~õÕ”~ú)ÕkÖtzÍ‚J…gB‚JEÃÞ½¯#(ØXDLEEÇŽÅRQáh³ `3sY¶†DCt4¦ÒRìMŽ%u•шwR’ÅBSz:v“ÉyØXÇq:Ú€€çoßOš€LÅÅØ[Z:ìkÎÌ$ï_ÿ"öü›å4"ü꫉n{.ާäƒØÇƒÚWËU‹“¸áÌdV¥åºüÈ[oEe4:7ŒÉ»îbcj* {ö0ö±ÇP{yQ±bi_ ÀÞ[na͘1;®È2îº mHcþò—Eâžz ]h(›çÍcÍèÑ4çä0þµ×Ð…„ýù·¿°15•^@åªUøNsxÄ.\èè“¶ÿý¦OÇ\QAãÁƒ¨½½I~ûmÌee¤-]Êîk®I"ùí·Ú–öš0}7ß̶3ϤnófçvÏøx¦._NýÎì¹þú^½xÜ‹½¹sy9æòr$³Ébq~·56:ËÌKò»ï’òÕW„_{-j¯cYg✘?ü¸Ç'uÅ bx݈Îýú°0"®¿žäwÞ!åë¯ýÇ?:Wûø¼l×]Gò;ï°è½·8cj,Z#›Ã³â˜›À#×ÍáªEã;½–¼ü2B/½” ³Îê´Œ¨Õsÿý¤|û-‰ÿþ7~3f8ºø§žÂ+) ˜’—-cÄ…âGò²eFrôüy$/[†>Ô„9ä¼ó˜ðæ›NAT¸çŸgÚÏ?“¼l™£þ6Œ£G“¼l¡—\”Ï>cüË/wx7hüýIzýub;Ñ6Jf3›gÏf÷5×`**ÂÖÐ@ñûï#¨T-Y2Ô·“ ‘ýÌ3¬9²Ã_}Zõ;và=i~3g"¨ÕÌ›ç¼Ï}&OÆ\V†©¨¨ß툼õV QQlž7•ÁÁì¾öZTxOœØ«zúgC©ˆ=…Ò³ý äœs°56R·u+žqq4gfb.+ÃgêTgY’ÈyæZrrÈyî9‡frÌ$«•òï¿'`áB"o¹…–Ü\ÇÒqÌ1û¯º´4ÇÜ¡C¾õ‚ZGt4’ÙŒµÆ‘©ÃVW‡¹¼¼Ë¶6gg“ý÷¿uûíøL™Òq§ |ÖYÔ¬_¨VcˆŠ¢jõjTz=^IIØ›š°· –ÊJçy«W¯ÆküxTF# RùË/x.8ßéÓ©Y»dŸ”´äýûßT­YCùòå¼õƱcñhw­5ë×SöÍ7ÔnÙBk;ÍIÌ}÷¡ñ÷GEE˜< \²„©Ë—£  %'‡¸¿ýëÖ¡j³ù \²„±>JÀ‚XªªˆyðAR¾úÊ9€Ÿ{.cþügl ˜Ëˉyàâžz •Á@è%—øï£ññA¥ÓòÐU³øÍÙŽÑd±aµ9&VÍ­VZÌß/[v¢zíZÿõ/Tžž.Ë$¿ó1ø¦ÂB´Lûé'F·™UØššl6d› [}=v“‰Ö¢"BÎ?ŸÀùó»òJB/¹„€E‹½ürôÈ’„ÚLJ™6qãXkj˜7ÔtÚEk½äÆ¿þ:¢V‹÷äÉÈ‹³m??¦~÷>“'S´lY§×)Y­N³Q£!æ¾û%©SG>…SÙlÆV_ßáO¶ÛiسÉjÅ·mLœ?ŸÆÐ9VÎp”uÛ·;ëòNN&úÞ{Iùê+’—-#üÚkûTÁgŸMÂ?þAêŠ$<ûlÛjï ÀnGjmuŒß}Çæ9s¨kl{Ê€9å(tC×}§>¡ðÑòÝÄ¡z·òTDŠÚÓ“©Ë;_«=<Žõ‹Íæ´‹´Õ×#Y,ˆzGDþÈ›ofÜ¿þEÓ¡C˜ËËñMIqÔn­½}×ÑÏGï-ù/¿LèÅ3þ•WhN]èe—QŸ–Æ–¶ebÿY³Hý±ga|ú‚l·³ÿŽ;˜±n†ˆl €# ’½¥…ÒÏ>s9pu†­±‘º;ˆ¾÷^$“‰Ú­[©Z¹’¨[oEíååÐP‚ÓNËæLš22ÇÒ ÐAÙåßÏ‘O?eÔw÷·¿9û«Ï(3ÔÅküx¬µµ¤|ý5à0û:xß×nÛ†ls¼ê¶l@Dkq1þsçóÀøM›†¥¦Ùbé`P³q£ãƒ ûsÊ9kÚè>´T µ €ÌÇ#á™g: šÞãËå¡_LpÛ²¸!* m` ¢ÁЩCLÕ¯¿qýõx&$ 1‚C?LÌànĪV¯À3.Ku5µmæv“‰Ê•+ÑfRr”ÊŸvy{k+¦’FÝyç±þè„K—2þµ×0•–²uñbêwîtã/®0܉¸á"n¸Àiç~”ºíÛ ¾à4~~x%%qà®»ðˆ‰!páBj·lAP«;h(w_w’ÙŒJ¯G–$ææâ7}:åË—c®¨àר(d› A©ß¹“äeË0ŽCcz:UkÖ°aÒ$bzˆQwÜAôÝwS´l™?Ž­¾¾Ç×3Ð6” í:“‚¨F„„J±»Š:Û.÷¦ø0žè"zŸ`þ(¥Ÿ}†Êhd|›mÕØ¿ýØ6‡šžP»mº°0æÏgÄÒ¥Œ}üñ^ÿ¨@xôxU4—ûüó¨}|:^˧ŸvùåDÞr  0郜KÖ¶sŸ{.!íbpV¯^ßôéT­^l·SùóÏøL™BSF¦#ŽÌ"uÛ·ÓšŸOÌCyë­ÄüáDÝvuÛ¶õxv*KY?ŽOJJ-j_èÏo®Ð=²Í†¥¢‚š ¨Ù°êµkÉ|ì1jÛ9YÑÎÞ÷¨í¯ *ß{Q«%í¢‹8p×]´¸ð–m§¡ŽËòÕ[ ß|“ºíÛIü÷¿;l—ÚÞÆýû×RòÁd>öØlÖWõë¯xÄÄvÅ4ìÝKÉÇ£ñõ%êw¿ÃZWGÃîÝÎ~µZ§À ŽÕãÓóuæì´ëª«ÈþÇ?>÷\|SS;mOØ•W2ñý÷)ýè#6Ïž­“§!y/½ÄºñãY7~<¹Ï?ßa_íöíèCC ½ürluu4îßOõêÕøNŸŽßŒHVk‡Py£î¸ƒÙ;v°¤¼œY›7#ˆ"j__À±â”ôŸÿ°0/EÅÅÄ´­R݇ÌwÝÅÚøxò_y…Èßþ–Äý«W×#ô#Ñ…"PöQTáJ†DÕ‰e;|;>z7ýÞËâï)ãÑøùôéØú;9pÏ=uÓ~þ™QwÜ6$¤ÃÑEo½EKn.)_Í„wÞqÚ9õ”æìlj·neÔ]w‘ðÏb7›{t\î?ÿéÔåð_ÿJå/¿ðÏ2õÛoñ™2mp°sõÚµ´3þ•W¿î:çöÊU«ÿ·ÙcUýú+’ÕÚ!<‘d6³ûšk°TU1îŸÿdì£Ò˜‘ÁÞ[néÕõVüø#uÛ¶1æ¯íq»ÂѬ>{:¡ò4ö鸆½{Ñ…†’ÿê«ä¾ð?ü@þË/;©®õz†#_~IõºuÔ¬]Û}pçš ºg÷ˆ,Iì¿óN-ð뮣zíZ§]¿gj//F\r ú‘# \¼ØeÈ,WØ›š(ùàš³²Ûæ4w<‚ZÍØÇ£a÷n2þøG¤nÞ*ON=luu´ÒZXØ!4à´_ŒºõVª×­C–$ªÖ¬AÔjyã4îÛç ñvTqR»i[/&çùç;„Êó裟sé÷ßOÚÒ¥T·iã"¨ÕhÚ„KKu5‡ÿúWê¶ms:uöEC9ˆ+ÇuØ ™n—¼]?Q=ˆ}æÿ8tÛ#´&í)z÷]ŠÞ}×å¾â÷Þ£øÿûÿöÎ;<ª*mà¿{§d2“Þ;„zïR¤Ú°w×ÁîîÇbYuײk[ ê*–µ+(–E×®(ˆHï½$$¤÷L&ÓËýþHé™™d2 Üßóä¹÷=çžÛßûž· ‰ÇZZÚ0ñøã ÑÑ'ù9öT2es~>ÛæÎE†Ólnñ°ßTïór’Êù±YRâ­çž‹::ºMªm­ä sÙll˜Ø´^©C¯gÏ7"¢ ÆZZÚd½µ¸˜õÆ¡Šˆh’‹L¿sg“19 V×§jLÍþýlš2UD’ÝÞ$"`M+ÉÈ͹¹-öwK‘©Í÷«9qó® ô¬1ÈtŒ&%Ud8öŠ*Úe<þ8¿ÿž™Gb8tˆˆ)SÈ~å•&Ólmá4)Z¹’ÿøQ³f:v,ªðplÍ®ÇöÈ.ªï«w]À‰=OÔ±ÏíÉ4Y}´a™);›o½E¿¿þ•¨Ù³QED IH`ãäÉÔ9€áàAúΞÍÄÕ«)_½šc/¼’Dů¿õÕ”ýü3P7m6qb“­ü> ùæ›ñöÛ¤ÝC°]FÊakHN'™O>ɨ?$úÜs[ÚèÒÓÑ$$ Šˆ`ö‰MÖå/_Αl²,xt÷/ñ?æœleeèÒÓÉyí5 î} ß¹“° È}ãÙ“3[9ÿùÆÌLÌyyMÞ[Ê û÷S´r%šFÏuA™ôóÏ(CBØ6w.Ö’ÄÀ@4IIX =sg|(]r”·gHR+ ž§¥Û ²i©B¶“Ó³ý*#æLeøç¯sìïÏbÊÌñ¼Iòø†hLó/FOqÇ!ß]\f3Ö6|Ã$—«Ó‰mOFˆw7Š }ü3±¸Ä/Ûï­ôùÛŸÈzàéV×Õ'ËnŽ~çN6N™Büµ×KÉÿþGþòå ëóÞ{¯Éu`ÊÉ!ë™g°Ö»H~à*~ý•ð)S(ûùg̹¹ ™YÏ<ƒ¾ÞÚ) ±+£ƒéTôóÖüºj+É1¡l:Ð2Õ‰Ó%ñáO{ÉÌ* ±­:»~ÊÛ°ÿT¹¼ÃûU›6>m.‹…Š5k”I€œ×^Ãi6#j4”|ûmÃòÜ7ßĘ™‰~Ç Î=FP©(mä#í²ZÙ2s&‰7ÞHȰa­\IÁ'Ÿ`®Ÿâ·õÌ3˜²³›ß;Èzæ\õV£â¯¾âè?ÿÙjÉn'«Y•¬†~šYŒÚ@’-è²kG¦÷P½};1sç6 +_³¦!ùxòŸÆœŸÏø¯¿¦öða"¦Oo2[”¿lã¾ü’I¿ü‚¨Ñ<ìTÚ.Éåâà=÷0é矙qä†ýû 8A¥â°®bÐ¹Äæ²½g¸\N­LoK®–UÄ)”Ú%©é‰éåQÞÁ£‡2â›÷Éuo~Œ$×ï=mŸ1‰~OÝO@|Lç;;ȹêìUä-y—ÕÖdÝI+Dk³²ÈªOõÓœï¾Ûä·97·‰ÒsÒJÙ¸ÿ²lX×DA’`gF!;3N}ÐÙìN¾ÚÐzƇÓŇ?í%²º¸‰B)9-üË Nak«޵´´U…­zûö&©PÌ'N´*ç4›"È›cÎÏoµ~çΦ~’Dv}qƒVÏC ecâcèÿüChú$u(+Ó{œN²žy†ÊFy}[#÷Í7[ø´~öë×7éoÛ¹çsñÅèú÷çÄ{o0¨”¯YÃÖóÎ#ö¢‹pZ,¾ÿ~"gÌÀ\o¯Ù³‡õ#F9s&¡cÇ’óúë”ýôS«èÚ£Siƒzí|ª¿p9íõ9õK\®Ž¢¼ëz¶Þ«@¶†¨V‘ò×Ûˆ¾ô\Ê¿ý…ªµ›©=˜qÚíç™@@| a3Ï"bÎTÂgLê|‡g0‰w\OĹÓ8öàbj¶¹çß'ÓK⮿”>ÿ ì?yº!¹\n}PT¬[GE}VŽ“˜Žkµ­9?¿É4xsª6o¦ª>sÃÉß±QðÉ'´QÁÇäÄæÝ‹$¹p:¬‚ˆ$5ž9…\z±û÷!ùî[H¾ûìe•T­Û\§\î?‚½¼—MN®Ý“"Êð0û%>cá3'£دóË4˜šÌ°O_¥ä¿ßP´|¦#Çü=$@vÎ÷A©$dü’ﺙ #;ß¡ŒL7"ß÷þAêÀÕ@V(;@AÌÕsõ©à§Áˆ­¼{Eöò*ìUHͦ{"N—Ħ­‡[,=2 m€¿‡ç ªˆPT‘ᨢÂQEE  i’ ^ÆG±¸„Ø?\‚­¸Œªµ›¨úu3Õ›vâ2[ü=:™PEG>}á³&6uœ×Qü22þFNÔ3‘J/Pë Ö˜šìï¡x„Ãá¤Ôõc‹åáWN#*2Ä‹eÎTÔqÑÄ^)±×_ŠËjÃx0[iöòʺ¿Šêº®²JŸ*›]$Šà ¯Ú*E+ºÁ§¢Wé8M}§ƒt4µ§]ûA@Üôc«þÿšÄ¸:«½ü2•9 -”=Y¡<ƒhë”ï/™Î ¨ 3¬óyA±E$ô`°Wm£4.FþýTBðOWþNEeM™Ó†3lpЧ]ËÈÈøÙ‡Ò_´ŸORÙzp/@§±&"®ôxÚ¼ eR¦wÒ©+Wêp|gÈÈpêÎh¬TZÀÎRS(å»ÚSAD• Ö'Éåt'ÊN¥ê(³¹ÐÊoY©ìÉÈJ™ÓŽN\»Íï‡VßQò½!sãnBBp:¼i4¢ÞL'RXXÈÜü|fws&”Îä¡”ÓyŠÐ$eÔ•]Zä¢lÃöÛvXxG–éÁ´yJåó&#Óº…R¾7dÎ\®žyñEþ÷¿ÿq÷Ýw3_¹0ŒÇâÅ‹Y¾|9{&L ·›Ç%ûPvB¯¢Ð2Ùy7ùPÊ'°' µqÊ÷—Lo¥3—®l¡”‘iŠÔ€E‘!C† Š"sæÌaìØ±èõz’’’$ A˜8q"÷oÝJ$`Öjn2q¾U.ä)ïîChëx îÖòîrä©ðl¡”‘ñùÎ9S€iÂ2331›ÍÜõ§? ×ëq:H’„B¥¦²ª•R(ŠœwÞyôíÛ—‚‚fΜɆ ¸ñ…øÄáèÔxÚ£SSÞrPŽG4/ŠØdE3ºX¡T&A®åݽœ¬å­ä¤¾'¹œHîEy{¾±Ó{•žêß¶¢ß¸ƒê Û±•”û{H2ÍpÙìØŠË°—5YžÿúrÄ@ !F6u<áÓ'Ø¿¿‡{F`Í/¦zÃvôw`ÊÌÁ^U£Räìz²9úà( ×>æU[ÑPΑ§Úƶ"S» Žø|/|‹ P ŒE†6½/¡SÆ6u<IqþšL"8tèv»£ÑÈÑã'È/©@£ @­B©T"*D\H8].ì6«ü¢2*õ¦ŒJZZZ­–7L&æýéz×9e÷RWËÛ† õ3Ýr-ï6‘œNŠ–~NÞËKqMþŽŒ—¸ÌªÛJõo[Éyê5¢/=—>þuL¤¿‡vÚQµn Uk6R½a–Ü|ÇKÎ K…ätb/««ZdÊ8NùwkÐôI"lê8ÂgO!|Æ$SÆO8-ÀËÀ¬3°Z­”Wé)*¯"(HK` •JE|Daaé´X¬Vôy…¥Ôš-("&£…ÃÇN0l@*o¿ý6kÖ¬aSi)'¾ûŽÙ]z7Úô¾þ–L7³ ¼ñF^7ŽAƒ¡××päxtº@T JíƒÝnç­7ßàСCÄÆÆrüyŒ1„ƒÇ(«·\å–Ab\4óçϧ¼¼œ«| Pvæ¾—JßqF¿aK¿ø¬ûŸö÷0<Æ (:ÝKë¨"à 1ˆ ƒPG÷lËžËjÃxäµû`ÊÈWK'a§ÑDÎ3¯£ßº‡Áï<+Yx‰Ód&ï¥÷(Z¶²[¦²»ùRh@¿i'{ç. ~ÁU$ßs‹üvñ9ðß¹s‰‹‹Ãl6SSk4j5¤ÄFQYQÁ pTKá¶m|0oóæÍÃxð0§›]EIy%±QáDEEѯ_?ÌÇÓ•W”èêL”·|ãûŠ3V¡tTé9þû{mbÞ¶‹"¯º\DS§H>¡TRFyy9ŸÐu'PP(Hüó<’ÿ2¿WZò »uÿÓ˜³óZ]_µve_ÿLô¥çú{¨½’Ì{ž òç þF»H.W“$¼’ˉÓnA¡ l39¯×ˆ"³&4r0Aâõ®žxwá2š¨=˜‰qÿ*Ù€ËjkyüœN ß[åDƒÞzÆßC–é&&Š"›6mâ /Äáp`0™Q«T¨UjÔJ‘˜¨(®ºê*Þh6Ý™l\¾ý¥—ŒÅbG­R¢7Ô6D‰gffâp8èj¯E9±yϤ÷i]DÍŽý¸,V£UÌÀcÉÉÜvÛm<2z4÷ÜsYYY\Í5Ü~ûíäååqý¼y|ÖÛÕ*†~ú*Á£‡zÔnçîÃDE†Ñ'%¾Éòâârò J=r ‚ °kÏ’cˆ‹jÑǃÇÐhÔôOKnX–‘™‹NHbBŒÛc 3Œ‘ß-åÈŸ¦ú·­­ÊTýºEV(½ ú÷m=W™”$jK³9üÍ‹ÄÄÄ9ñ‚bR±›ôÿúñññ:z”ÁW?‰¨T7mꥉRÓ7‰ô&xÌ0¯Úû‹Ð)ã0gå’yßSÔî;ܪ\åϨþ}aÓ&ø{È2ÝÀÝ.xåjjj¸ì²Ë¨5™Q(D” ZÉlfDEE«mçÇŽ#¥O_ŠjDQ¤Æ`D’$jjjX¸p!¯Úl]^ï»sQÞrPŽ÷´_bûŒ=²½ÁßCh9¦ú‰‰‰œ}öÙ³dÉ~øa–,YÂ-·Üõëãââxœº)‹€2ï6Kò½·y¬L|ÿÓFÞxûóË?^ñ#}ú=J¥«ÕÆ¿_^Î[ï}ÑB®´´’§ž{ÿ}³€²ò*~\½‰Çžz› ›öx<Q@ús¡ mýøVë=ìQ fË¡Mò]N¢ª·óÅ_ðÑGa=ø?¶½»ƒ+çþûïçÅ_ä…^`Dz{»d{ÚýõÝR·•Éí;ñѧßãj6EWc0òÁÇß’‘YW´níºí¬üò—Vûصç|ü-öF‰¢k&~Û°‹ÿ¼õßÿ´‘ªª·÷!°†ñf» cO>ç2]‹˜ddd j•ªÎ5HpJj•Šœ6¬z5@HHH“kS¥T"Øl6Â}0æÎ%6—-”ž"" ……R…B¡F[wº;cÊž„ø7ð@R×O)\}õÕX, ‡ƒ±cÇ’œœŒ^¯G¯×c6›yñÅyýõÞx#.]Ê’ôt•Jmz*‰·^ëÕ¸'OAΉ"JK+–YmvöîÏà¬I#šÈ9šCNna“e?þ²¹IÔØú »øò›_;Œ$kUt}þz[Wœ™zœKç;éÊñUõÿ¾j6””DDD*•Š·Þz‹wÞy‡•+W2räHRRRHHH`Û» ÙöîB|ò¦Êýi¥’ô?‚¨q»R©àÛ~çðÑœ&Ë·ï<Ä?m$¤>GäÎ=‡YùåŽd4•“$‰>ú–~ÚˆÃ^ç»j6[yì_oóîû_RZZÉÿ[ßï~–cÇݶ"ýÿE°®õcÜÃιŒï°+u:®¼òJ”J%ÁÚ@\’ —Ë…½Þ_zÐùçÓ<ë <L¿~ý0M8œNJŠŠØ±if³™Aƒ1|øp|¾'Gyw#¢¨<ås.€ *D¿Õò–ia·ÜÂâyóÈÈÈ ** …BAeu5ù…eÔMè F¬6;A: ¡AA„‡‘ÅÿøG´Z-‚ pÿý÷ó—Ûog¹Û™0¼ô/›0~ïøÛv䢹Ө»/›ÍÎä‰MJ…BÁ?mäO·_ €ÕjcÝú(§.Ê+/›Í•—ÍfÁívêx†œ5¦ëO’LÀÜÂ!Cغu+ééý¹öÚk©­­Ån·#IÁÁ!è µØDA@¡PðòË/³sçNbbbHKKãá‡ÆØÇ³ØÓ˜+ÎG74Ý£6#†õ'H§eëö ܯaù¶Hí›@\lÓÀ·~ÚÈ }~ïÞ{”’Ò¦ÓÇs P©ü{ñ½ÄD‡cw8¸çþ³ú—Í ÷—;¨ã¢I¼íœxñ]Ÿ5™žÌkÀ¿þõ/FÕj%X§ÅQèÄawàT«É+*âž{îá••ÌØº• x12’7_}•Í›7ó󯿱}Ó&L˜À%Îå®»îBEôz=Y¼c6£îÜ0›Ð9JÙŽæ ‚(¶À("M4e…²ðpkj*PgM1dçrôx‚  P*"Mv»“âòJò‹JÉ-,aøÀ~„Ùí³jÕ*®ópÛº¡¼wp–aCû³uÇ…rÛŽ$&Ä’Ü4iò¨‘Ø´e¼îBB‚X¿a¢ ’Ú7¡Ëg`ß$Ú@œ&s—÷-ã_îT©øÏ /0`Àl6.— “ÉDAqùÅeèkMØêND…‚ -‘á!¤¥$0uêT´Z-¢(rî¹çò¿Ÿ÷x´íàqÃ=¯B¡`Âø¡lßy›æ]Œ ˜L:ƵW5õçÕi5lßyˆòŠj¢"Ãøqõ&´Z &Ó)‹áÐÁýx承4üV)•„…SPä¹ÓKШ!¾:U2½„?‹{ŒÔÔTî¹ç‚ƒƒ Òa²ZQ(ÔÅ¥<õôÓTUUQTTDJx8ÿåÛï¾£ ?Ÿ³ÏšH°FÍ‚ ˆŒŒäÉ'ŸdçÎ\xá…|ùå—|õᇸÿ©Ó1rÊîCh¬MJm,¯GVÕ{—?üð‡£ÉÄÎdæ¢ÕDhh0a¡A„…DHHÁÁ:ÌV+›v ¯¨”ÚÚZâããùÈ¥nÃâÝ|iÉGu,ÊJ=N§“]{ŽpV3ë$Àˆ¡é„†ñóÚº€™ÞÌÙÓÆ øâæÔqÑ]߯Œß±ÛíèëëÑ[,*«ªÙy ƒ}ÙLfTj%Á!:‚‚uh4jL+Çs Ù°}?E%åèõu×é·ß~Kt’gÊTÐðA^ù¬‰#¨ªª!3ëPçép89kBÓû$)1–ø¸(Vÿ²€Â¢2öÌbüضý›%IâëïÖ“u<³§x^‚²3”2§À›z=é{öðïÿ­VKZJN— “É‚Ñd¦¼¶–ƒ™Ç¨2Ô‰ÕádÝÆÍ¼¼d  `úôé,¸éf J+Ù¾ç 5f“&OE­V3bÄ>ëIDÙ‡²ÛêI©åšæÈJ?âÖo G¦¶¶–œ¼"Œ+ÁÁZ45*Ña!éP«ÔM&*ªõTÖQ©”˜ÌdäÄW\Ãáà‡‚<Ès%%çþêäÍ5nìPK¿dÛ΃ÄÇFa2Y˜<©¥B)*DÎsßÿ¸~©I•ñ·{ç7¼d»ù¡qZ2Ø·oC‡¥ÖhbÓ®ƒ(” B‚u¨UJJ¢ à’$$—„ÃáÀj³c±ØØ¶÷0#‡ôGEFŽÉ矿ÆÒÆ‘ÓQ©Â%(p‰".AD¸±îwýÿ‡'xgM:¸!!AlÝ~€é}ضãéýSˆŠ k"'IçΞÄç«~áÊËçðãêM¤$Ç‘Ö/‰ß~oé‰Vk4ñêë+Øw “óÏ™ÌÌéã=›ª6™3 0ØY[‹F£Án·3 %‘£9ùÔ:œ¨ív” µæ:SÅÞ;(Ê=Î5×\øñãÙs0ƒâŠ*T*%¢(àp8É-(F—ÇØaù裨¨¨`þ}÷ñ±ÝÞéñÊÊîCr¹@ÑÔ_R¢®csd…ÒÌEÜz+O&'3vìXÊÊ+É+)G§Õ  Ô ÓiÐ'³Ù̺uëÈÉÎfâĉLœ8‘Šªj2sóAp¹Lì9œÅY£‡2þ|”J%Ë–-ã³e˘ïã}Ði5Œ>€m;’MJr ñ­[gÏÏ_þÂÛï¯b̨AÄÄDøûÈô Ôe2°ètÜ5mf³™ƒ™9¨5j´š4j´$ÅF„R¡ÀPk¤¤¼œâ*=J¥“Bä@F6aÁ:,X@rr2f³™Ï?ÿœ¾=Ô"­Psœ¢wKQ™4~Ûväš«Îeï¾ ®½ú¼Ve§OËŠ/Vóóš-¬ß¸‹ù7\ŒÃÞÒ®s"¯˜^^ŽÃáä¡nføÐþþ>E2½œuÀe—]€NW¬¥ Ôp<¯’J= ¥ˆBT Iß}¹’U«Vár¹Ø}ð(³…è¨p‚µ¨T”V ¯1²uÏ!&F`` …ÉnïtMÎ¥ ’JÏp¹Ÿ’ ÉÕ²À…¬Pú 3ا×_=z½ž¢òJÔjš@ šÀÒS’Ù±};{ŒË%‰³÷ßruh(~ø!ÉñÑä–âp8¨1˜¨©5¡ÓéP*•Lœ8‘?uƒB 0yÒ^{%…Eeœ?ç¬6åtº@Ξ:†Ÿ×nåüÛ®ò÷)é%˜‡ãâxèþûIII! €c¹˜m6´º@t¢BƒIMNâ÷ øß—_b0˜8i7ÜpQá>Vg ¯uºØwô8“F ᢋ.BEòóó9”»ŸÈ´±>Û‡³&`õš-|ûýzlv“&´î©Ñ¨™yö8V|¾FÍÔ³F±nýŽ&2V«§Ÿ{Ÿ˜èpþvßMè´îGËÈ´Å%À¢¥K1bÅÅÅìÝ»—¹sç2°_ IqfªôjjMuy*•*>ÿüs¦ÏœÁd!,<˜˜`ŽËâ§$%%…k¯½Žcy—U‘_ŒÃ\ƒÅbÁªP¡\¢Iêfêg¤†ÿ ¸EýoáÔÌ "‰"úàN#d ¥ÏJ?á4 åååDFFât:1[¬¨Õ*Ô*Ât¸œN~xüqîmô5 ¼­×sß}÷ñöÛoSPVJ­D¥R¢7‰Ž¬Ëú%¢(¶Zް«;z …Huµ¡Eº æœîdÍfø0Ï¢eeÎ\òQ£F1vlÂWYYIUM-š€5‚è—’Ìý÷ßϱsþ‰xu]ôöN;_^1—Ï>ûŒ¤ØHr‹J±ÛUTëk±Úì8Ôj5ãÇç§Å/ùT¡4 /áá!|ýÝz èKDxH›²ç3™ïÚȬP©Z>¢×®ÛNµÞÀuWŸGYyU“Ta}›9s(ˆKcø‹N)e”¶ºßuʘ«Ñú:ס¡ÍL—ƒ[n¹•`]8*U?ýôï½÷ÑQ‘„‡…ât:±Z­XÌ&ÒÓÓ)¯Ò@ˆVCuu™Gb¬­eO¾‹ìgžfᢻ¨1š(.«`Öä±<ùä“Üö¯1ò†ü¦ØÉQÞž#I.$§ ¡¾f{[ÉÍe…ÒOw>Ìþð>ùäD…“Õ†N§A¡T¢Ój9š‘ÁM­(„‘™Y—âA£Áb¶Õ)t5µ8¶lÙƒ>ÈǘðFÍÍ7^ŠÉl!6¦i•ZÉU—Ï&-5€Ä„þò§S±è³¦' @Õ¤ÍeÏ$½Qå™3›(   €ÌÌLRSS‘$ ³ÍŠVˆZ­$24„Ç“uö_Q7ºþ*þ‰§ŸþO<ñ啨T*”J5#¡!uå;FÜð™>ÝA˜4a8?ü´±Ã®˜èp&Žƹs&µºþXv]¾É7ß]ÙbÝ?”K&ž©Äö£ ¶_§û{Ó’†ÿo{w!¥¥¥ 8»ÝΪU«Ø¾};úÓŸ3f ûg"*@nN›²-D ¿– àHö}tM …—dE¦M›†J¥B’$ßdºK¶PzMG9¢e…Ò$PµzèÐ!† †ˆ€€€( IMr46§–º—”„Pÿ¥Wߨ´is%©[O[·î¨”J®º|N“eK5Μ>®E›Ë.žÑ#—éé„7îßÏ¢E‹X±b¦ú’© …¥B‰N§cå矣µ¨ecAd÷îݨÕj Ì Q!¢¯5ïp°|ùr>ýôSÆßüŠÏ÷ãò‹g0~ÌRë?®sÝUçátžòIºõ¦ËÒi7f‰ 1 ^Í=›Y^àÈÈxÊ ¹‹X¾|93gÎdË–-üüóϤL¼œW^y…„„´¡áÔZ,ØœN&L˜À+/¿LôÀÉÔfpÓÍ7c2[p¹\¨”J$Iâ7Þ@_—ÛÐOÈA9¾CV(ýHĈ#$ Vƒ §äÂd±2 =» žq6u~•ň¨T*Ì6+N§“²â"4¢…BÁäÉ“yèÛoù£ËÕ£Nð¡#9”WÖ0~ô@»ºº«ÌéL `6›)//GŒ ÔeAp8$&&‚Ë Š–/ªÐÐP$IÂÙèëZ­R!Š"GeÀ¹wtË .$$ˆ!õ•qš“œÛä÷Ie <<„ðFSäò´¶LwŸNµFÇ;_n$¢ßh&Ür ;¾F©TIaY%f‹c€?ù„Ç“0u(!¡¡Ë+Âl¶’–Ç‘#GX±bE·|¼µ‡<åí;ä#ë'œÀ#áá|ôÑG„‡‡£T*ÑjpÚ8NŒ¶ºÔ w>÷oŠ"æú6eÀ_bbxòÉ'ùqõÏóÞ^¦ª8—CûvsñÅSQQAß¾}yÜß;YË%ñÝêmGôgÂYçs¬ÀÁ÷?ïæxNaç;—9#ø˜4i}ûöE¥T V*q¹$œ.‰Z“‰Y³faùöÅí\† ,X@Á€K»ÃÁÖ ¿‘•B¡àŠ+®àÄúZXô‡“×ì⇵»(-«ò÷pdÎdmD")“® (&µî N’P«Õôë×`Rb£¨®6PRQMae5Ñ ÉÔÚdæ£7ѨUôIŠgèСDFFv~Lß)à´¥'°Î(À5ÕÕ¼øâ‹Œ7Ž9sæFi•›ÅŠR!’[RÊàAƒøÕWìØ¾œÜ\ÆŒÃ'C‡²páB&MšÄHí“Â]wÝ…B¡@¡PpôèQxàžzê)Ìyyç¢ô!Õ5µ¬ùígMžNxx8¢(’Ö¯?}ûôC¯¯fÍï0qÖ¸A²ÕR¦U @þÈ‘¼üì³@]­FƒÉnÃa·£7™‰ gñçñÀƒ¡^°”jìûÖpq´‘i7ÜÀSÏÁÆÈQ£0™-lÙ¹›¬¬,–,Y‚$IÔMdfŸ@.¼ø2b£#ÑjINNf~^ÏS7eØÝ·Ñ¾ƒÇÈ+22jôøºücõ£¨IPP0£ÇLÀáp°?ã8……Ù ”HÿV|ÌdÎ\‚€0oÞ£ °³YOâÌKQ)U,X0Ÿx€õë×s×]w1cÆ ¦L™BBBááá 6Œ›o¾iÜ%~}ÙmÛy‹CÇĉSê24Ñ1±DDFa±Xؾ÷匕F\¬œÃUƨ´¡ ¾æiv|ðWrrr˜2e É ±”WVc³ÙA€Ø¨4 ëÖ­ãßÿþ7ƒæ."eât]V(;…íÔQ—J?"}€‰ÀñãÇ™6m‰±.ªkk1ÍØNÔ*%«ŠšZœ’ĪO>ÆXSÍ5×\ƒÅbaÏ¡,*k (•u gm6;bF6é}¹÷Þ{™:u*?îÜÉ”5kÖMûåt¹øâ›õ„„ƘÜVÙG‹?QèÓ§/IIÉ µ¬þ-“)ã“åüz2@Ý}r±ÓÉœ¼<æ/[ÆÈ‘#IMM%!:‚‚òJ@ÂåtqÜVL J‰V@ˆ:€üâ¾úê+T*cÆŒaÀ€•–³}ÏAQ$¥ß’â¢ÑétÄÇdzýýE š»ˆøônMiâp8Yùõzú¤")9®ÕHJ•JÅÀACp:Q^^ÊöÕ{‰ W2~ô Ùj)ãáW>̧Ÿ~ÊÎ;ùõ×_Y¸p!çž{.K—.eÏž=ÜqÇìܹ“çÞAH‚ÿË|ª6ÛÑùŽÎ0Ah’Ø\:]›Kö®ªê?¶‹"÷\x!J¥­VËд>uÉ` бX¬(”"¢ RQVJˆ.?CMM [öQ &:­&¥RÄd¶R¥7pèØ $IböìÙ8N~ï&…Òb±ñß/׃VDCP©Õb ME‘Ð`´º`j¦©PJΞãkw¦qÒuÃ`0Db¬“Z³…ªCÝÇ—R‰E© Ê`Âj1óú¿ŸcâĉÜÿýä±ïh6J…—KÂæppôxR“X¼x1¥¥¥¬X±‚‚2U¿X7PVQÍ7?m#½ÿ ´ZmÃ-ÒR©v‚¸èHÖ¬Yƒºw•¯ï“’Ò .¹ä jjô˜Íf¬6Hí¥°E‹‰Ü¼" µ '8o·Ä¡7ø{n#uCRûîÄ DEE1uêTDQ$((ˆÁýR¨¨ª!3·³Ù‚B¡@öîÜÆå—_Îõ×_OaQ1{Ž#(( –`†¥ŠJ½òê2O T(Ø¿o“Ô=¾æhæ N[HHH®›j—¨¿Oš|µ¼k »Ã†¾ÖèïÓÒ„ÓíšóE…6§ÉìQ»} ×}·„ÞE­¶wÔYWèPÔ•jL¾àodÖ”1î¦Y ¤Ï¹Õßë£ÓÎå«ß"¥0Ã-yul´[rgm} ‚¢…BÙ룼O…r.`^¹’k¯½–{ï½—{•+WJzj2#÷gÒ¨!ŒÖ‡Ã$I”UëÑi‰㛯¿Æb2ñÃ÷ß³dþ|4J‘P] š Kʹû<ûl²ºi4TJƒ!%)­V‹RTЦ…R’¨¬¬àxN>N§“šêjtZ†µMoR(µ&¡KQËËyíµ×¨¬¬ä¿ÿý/[¶l!6:‚ñÃ0°O1á¡( 1Š?ü£ÑHna)AA:Bƒt$ÇDòÍ_ðÜ3O£S+è—GXH‡åb6›9räúüÃÐ E*« œ3ë\FI\l,jµªÙÇ×)«dc*«ªÈ=Qˆ«‡)pNcï¹æ¤úL>A5Ä«¦áúR®ûv ÁÆjÿ˜N *Õh#{TyC…ËÁ¥¿¼CŸ‚#n· =Ô'cñé5ç+Ú:—­,–ʀ̖””ðððóÏ?@PP’$±~ýzþüç?³téÒ†eJ•µJÅ+W²ôÑGy$/û%‰;w¢ P£P*°Øì$&&rñÅób+Û¶WtmZ•ZEuUáá @xx8q±±‡aµš±Ù¬ _<‚ b³YÉ9Q@qi.—‹9œ7k0AºÎ)”]½_'éM ¥£Jïï!t97É«VqÕUW‘³t)‹/fÕªU„††’œÇ ´>Œ:É\Czz:.— ƒÉŒV§!).š{ï½—¾}û’™™É½€N@€Z…J­¢¨´‚^xAáVjËr|¾/‚  P(Ðëk蟖NJr Zm JQѪ¿±( 8vró (+¯Âáp`4Túû”4ÁQÝ‹î¿;’ï¾Ùë¶a5e\ûÝ‚rڨΠºœ\òË{¤ær»np"Ï;Û'ãé•úŠäþò?åÝQ™$GõéóÒ<éZ¯L&?þø#ãÇç¾ûî#;;€Ç{ŒçŸQq9]Øìv&L˜ÀÍ¿ý†ø˜8t(VWó¢Z©À`0°qãFnhe›ÆƒD_~^—íCB\Å¥ÅüþûvR8p¢BD¡ ¢ª²šàà ,&SÝ·ÓéÄl2R[SÄõWMEÑÉH[ka‰Ïn\oú힘­Œµ)¿î"S€)õ–¹¹À‡£R©P©Tìܹ“ï¾ûެ¬,^}õUœ.¢X_×^’¨*)#óëu,<ƒk-%%%¨Q*8œNâââÊö`ï… ¥$¹Z±=6Ÿî†^ PZíG0öÆÔ à?55¼ÿì³¼¼DVàŽü|A ,XGU­‘*“‰…‹ñëðáäæä0gÎT %%Xm6âc"Y¶l_ý5+ZÙ–aïá.\L$q1‘8Nvïú½JTtûìC©P2vìhŽçÔÕ$.//¡o‚†ógLí’í×ú`ŸNâ¬ñü…iQø& ·£ô’Ó‰Ó`D¬óÙñð7‰@ff&Ë–-£ÿþ<þøãüÙng-pï½÷òÒ’%ˆ‚€ËéÂ%I =’s¶o'$ï  biRYyE8.4j5{÷îå—_~á/UEDW5Ù–mçî.U(ì‹Ñdaݯߠ҄1tÈp‚‚ƒpá"0PC~~!‘Q‘Ø,VÊ+ª)«·º—å3d`ÃMîôŒ3»l\f ’ÝÃ)=?OÚ«kð‘B 7ï ´Ó8úÿðjÖ$ÔPÑ Têƒ{¦_yOD\\¸v)é9{Ýn1g*é/>‚"ÈwÏË^i¡DBr9ÄSï1Ir!I½0Ê»#…Òi0"9>ziû‹àOõ')¦M›FHH)ñN*gQUUƒÍæ`ô¸ñŒ7“ÕJAY†ZñÑD„‡1lØ0Ž}ýu«9†û©øa‘Ìèò}P*Œ3€Ò²rÖ¬ÙFbb*‘‘Ѹ$ —ËE~^sg"2¢k¢]f ¹Ï¿ÕåûrO?`\‚€ÍGצݯh{uÍi­P*%lX¶ŒÍÀõËâOƒ‚ФÃh2QR­ä¾ûï'?/*§“eiiäc4™Q±Ñ<¿øiÎÊÈ 5—|ý¦Äßxe—Ž_§Õ0cêŽebóæRúôíOßÔT***8ž}œ¤ädÊ*ªq:äŸÈâ² ÆÒF GOÑoÝÝeûâuÒæÃ¼ŸÍnAÝ > >Ægc™0’‘ß¼Gæ½O¢ßâùñ©­äÚïN*•Q>«o‘% Ar!º\uÿJ®†€ºßRÓßÍäIB”\õ3Rú“r'ûp|³Ý;Ö ­†„ÛÿHò_æûöÇåÂQSëY“â‹*I.$§ Aêý»[Ÿwëñ ¥EÙñ­…¥h’Oÿú¶IÀáŸæÞ²2úõëÇÕ×\Ë¡¬JK+©V ¾´œÅl!9>†ô¾IìÚµ‹#GŽ0§~?òÁc†¡ŽõÝÃ*&:œ¹³Çât:Ù½ÿ0ùÅzôÕÕÜpõ,]¨på<ó:–z˧/°y8Eè+뤻};ªõpšßÀìf˪€Áƒ£R©èŸ’ÀîÃYTTè1™­‡ ’$2ò 1Í F†¥§¢Óé˜={6å­G‚V®þÊ5‰˜=Å'û‘–šHZj"f³•m›×"‰Z†Jeu5Fc-Vc17\=Qìš—Œñ@Å|Ñeã÷F¡tçùî-îÝÝc1RÇE3ô“W°äæSö¿Õ”ýïg,¹î?§Bj«¸îÛ%ly. Õ)V¯“ÊWÃoWëŠWS%MBp9”¼–Ê™ÔRYk¬àI.D—„ 9Á†>š(€NDWÇiãºA!6mQ—žCÄ9g£è†uŽšZð0ˆÎ—ïo:Vìù ¥´vï¡3B¡TÏ»\”ïÚÅ]ûöqË-·0qÔPÊ*ª¨¨Òcw8ÔIHpO=õ¿ÿþ;Æ´Ó¯½JÏÞ‹n¦ß¿þJäy¾­d P(7j㺸_{Y%Y.¦jí&ŸŽß°Ç}çn³/_˜nôí«à¤žÎ(`ËçŸsÑ7ß0{öln˜w#ÙyT”WS­‘$ ‡Ã‰(Œ’ŽÍjæÃ?dóæÍÜÕN¿ÇzíÊ7}ú¼ `ú”:«eNÞ öî9Ê€~qŒÙ5.!PwÏgÞ÷T—æUµWV{ÜÆÿ\Ý;©é“Dò]7“|×Ív¤ê×ÍX б—a-*ÅV\†Ëbmµm°±šÙ›>ëÖñž¨ÂCQÇÇ Ž‹& >í€T"çÎDÞ­ãpè=¿ÖzšBÙ§‡B¹çQÍv£·ÞDJ¥’Ÿ~ú‰œœ ‹-B©TòÅ_ÉäÉ“)++ã-ÀÛÆ^QÅÑ?=Bè¤ÑŠnh:ꨞíî²Ù0edc<˜AÕº->9HN'µûzÔÆ—˜ŽÜA ÎG.|ÆY>=.=ðN'eF#ÏïÛGtTÝGV¡½¡‡ÃIpް ùãÿHŸâbþ·Ó¯½¬’½̧ïCÿGìõ—ú|?ú&ÇÑ79®Kû¬üi=Çy¡Ë?6Œ<»7Àÿ\–…>Û~GÚjzGuMrÞ ©«NgµuL$b€ÚßC¼»Ö|ùþð=~´îPO­F½xßbá·%K,´Z-*•ŠU«V1X¢Rñ¼Ýî–2Ùý–Ý^ùùœ ˜ŽÇe¶xÔÆß_(õ¢€ÂÂBÞ|óM6mÚ„V«åå—_¦¤¤„gžz’)S¦””„ÙlænNeZh§É̱G^ ÿõåè† @78eù4ú §ÉŒéÈ1jføÌ%İû Çm|y¸£¬ÖöÀw‡2,eX﨔#ã>Þ\k¾üàò=~´î¼yËSÞ]Œ;ºËbÅtøºa¾/>¯ #úüó‰š=›Ú#G(ûá ‡üûPJ»÷^ÒU*²/ hð`’o¹…Ã÷Ýש~ûô!ö’K=ãÑ£”­Yƒ~Ç¿î«?ñÆcôsн¼kA1‰];mÚ›Péõÿ¿(_· §¦¶Q”Kv[q¶â2ÚøúeéNÿ'Ç­Ž“KíÉøof‹zŒB)ˆ‚¢. ^Ir¶š‡²ÇWÊ©Ðhp¸‘Z¢f—ûÉK½E dÚîÝ }ñE4‰‰ô]¸³~ûQ£üzŒ‚D7xpÃï”Ûn#þòË;Õgä¬YLÙ¸‘ôümj*}-⬵kI¹ýv¿î«?1ì<àq›R­Ögã±*nÅMúkÚ;þŠ+õÁÌ8z”ÑLÌøe Ó¶ogÚ3Ï4(“}Q}Ôé®Sn»ñ_ÍÌÌLÆ~öIóçûw_»¯¦»}<' ‚[~Ʋ•RÆ×XNz´fU(:Ì5Ü]Ô)“‚("-õ²¯Pº7^Êå_ÿâó±$Ï›‡:2’M3f°mî\~:”#ÐBVÅvsZ |˜6žÞßwÇì]° ËöSÌè>@¿{7kSSÙ–ëÓqõÀø+ë’;M&Ž<ôÕ[·6Ȩ£¢¹t)s ™qèMBBÃz]ÿþL\½šsKK™qð ýÿþ÷†u#ߟ±ŸÞð;hð`ffe5§.‹dÿ‡âì={öꫜSRÂÐ%KêÚ-]ÊØÏêÒIŒ|ï=ÿøGT‘‘ÌÌÊbÈóÏ=·QòÙw·±*n)|¡H×qÒr_§Rj”[oÅa0°ûúëÉzúi¶ÍËžyó0ìo:“  "ü¬³ˆk}J> 6–ÈéÓ ˆiš|Z›šJ@ll“~t ªë":5‰‰h’“Q…‡>iRÃ\þ‡RU¯Pöí‹R§CˆnÀTáá¨""Ð¥§7„  KOoUñŒœ5‹„?üCý+;¯ºŠ£ÿü'›gÏæè#PØè^ÖöëG@L šÄDBFny &O&dÔ¨†ñ7l·~\'QGF6ßÉã 1e ê(ÿ$¾öæ+vãÚí,Ån¼;j¶ïóù8dÎl¼¹ÆÜ¹v»…¶ c­,î e¡›žÒÏ=é{BÞÒ¥Tþþ;ý|i;wÒwáBMd†½öá“'säÁÉzúi¯¿žQ~€¨R1æ³Ï4ˆc‹Sþë¯ôè!"¦Öå—;ùB;‰¨RÓ`T…„ í×àaÃ8ú`8P7«Žˆhxéä½ÿ>Õ[¶à49ò÷¿S°b†C‡°•–’|ÓM }Ç_uš¤$J¾þºÅ~†Ž…Ód¢fÏžv‡*<œÈY³p d>ñæ¼<F¾û.1sç’ÿá‡ä¼ú*¡ãÇ3ú¿ÿm°6ª¢¢PŸJÎ"j4ÄÄœZNè˜1hÉú׿”JF¼óŽßÊ´¹,VÊ¿ñÜÞwJóñvy>]ßL¹¹(ƒƒ´x1ª°0\V+¥ßÓln‰™;—‡1ñ§Ÿ˜qäã¿ú ]ÿþ ëûüùÏÌ8|˜ñß|Ã̬,†¾òJú ß~Ë€Ç;Õ×ùç3mÇûô`Ø«¯2ñûu+W¯fð3Ï0þ»ïHÿç?ëúøæBÇŽ%țíØAòÍ72r$ÓvîlbIœ6i;wè×ôA2§7’ÝNÅwk=n×÷‡{;PïX%µò׌^¡Pº{`KWýÔ¥‰z›ã¬­eÛ…²ý²Ë0çç3è©§˜¶{wƒåOMÌܹä¼öyK—’¿|9'Þy‡° PGF>e ºþýÉ|üq²/æÀÂ…ü6d•6x4Žƒ‹‘ûÆœx÷Ýë*7nÄxì.‹…¢•+‚hò>ø€˜¹s,É P±v-–ü–Ó¸ê¨(,ùùHPg͹tiÃ_ã«ÓdbÏÍ7“óÚk”~÷))DÍ™CÞ»ïrøÈ|òI2Ÿx‚#?Þí}ÔïÚŶ /$û•WØwÛmÄÄÚ̲Ó]TüøNƒÑãvÝòÂtSi-õÂÂÚŽ?ÿOÅÚµì¸ôR6Lœˆ28˜QK—"(èÒÒð補[Vƹee ÷€øxSR¦ÜÌùžùå9kk©ÙïyRÑŠHN' ×]GðС„ŽKÞ²e­ÊšóòÐ¥§£Ž®‹|DUXº~ýˆ¿òÊ&Ój5{öàjdu:i!ªøí·SÇ«þÿšúýw‡ÆÕÛ·#9DÍöOòzo-ßÝ¡P–jµ8ÝP>Ê¿[‹ÓäYÍÎà4›9x÷Ýü>z4¹o½Eôyçqö®] Ö³°‰Q…‡S¶z5š¤$DµšªM›>:¢fÏFE2þùOjöî%ëÙgÙ8y2–z+¸;Ø++9ö ÔìÝKÍÞ½-ÖÛ**pZ­¸ìv¬%%8M&$ –/'þÊ+Qèt¨Âɽøbò?ø Õm(š(“¡cÇ23+«á¯ñT~ÕæÍ­\IõÖ­˜Oœ |òd”!!{þy*ÿ¢U«(X¾œ‘#›¸ÉtDÙ?râw¨Ü¸‘ì—^"hð`ïNg“…r/¬/nÕ¢ï,%Z­[ e_üàqY<wðêƒËÍØ‘îB’œÍ~K½W¡÷_ÎÅ®òÙSRšø!Ö9BÞ{ï¡ ' .[e%Ù/¿Ì¶¹sëþÎ?Ÿ-³gc:v {ýú“¾‚ÍqY­¨BC~+CZ&·õ6½‰½ºšÒo¾!iÞ<’æÏÇVVFÙ?´*[¹q#Bƒß¢Ã``Çå—SúÓOM¦òšçä>êœJátÒçëä:Éfk²Ÿÿßš¤$…¢î…ß͘2³½NôÞ_˜N7q‚£<¦)SpY­¤?ú(@ƒÂ4ø¹ç˜ðý÷Løþ{¢Ï;A¡¨sõˆEr¹¨=rÄëm›Žo°²{BþG!j4Ä_q ×]¢HÁ'Ÿ´¾œÂ'OnpK±“ûúëöí# &—ÍÖ k<Ú´’ÌIP}#×’“n&êf>£îb8X>q¢×ÇÍ*~ø§ÑóûÒ×Áj'±‹"ånø1[‹J©Þxæ¦D“ñ ¶’rªßêq»r¦[>¸ÜE’$\..——ËÑBÁwÞ‰*"‚#ˆ¹è"leeX 1ggcÉË#jæL,……Xòó 3ýîÝ8Ífª·oÇi6ÓçÎ;릆Ï9‡™ Š›9'Ý€DÍ™ƒ*,Œd/#·%‡eP ­Mü©šÃù|@ÐàÁ$Í›GÁ'Ÿàj#ùyñÊ•˜ss¼x11s碊ˆ áÚkI¹õVjöîmתjÌÌÄœŸOÒüù„M˜@ð!¤.Z„Ã`@¿k¦ìl¢fÍ"xèPâ∿æšý HòÍ7£‰§ß_ÿ Ðꔣ¯ÉyòU¯J Uh4Ô¨»§ä—Û.!Ý5í-¤Ü~{C€¥°ªÍ›Q×ûŸtµØõ‡?°::ºÉŸËnÇœŸ Š €B£ibu³ M~v¡EÎRP@ùš5$ÞpIóæQúý÷ØÊZϱXòÍ7(‰>÷܆¶Ç_|…N‡­¼¼á#ª5NÞG“'7, ?«®L¦µ ©ÞzÚx?µXùOüXKJºìx´‡·×Twú‡¹}|.O{Ët-e_þˆäôÜÔcü'[Ðþ»°Ç'6?Éáˆfåç#ºñrÏyòU¦Žo75BØa?Í9úðÃèú÷gðâÅ ~î9 N9Úwûí I¸ìvöÿßÿ1êÙ~à@Ý´­$a}AÑ$&1e ûÿò\f3%ß~Ë€ÇgèK/‘ýÒKÄ\x!q—_Ά‰1ffR{àqW\AÊw`¯¨hpæ NƒQ£ˆš9Gm-Õõ/ùË–1úãA8ZÄÓË—ÓçÎ;ùî»é%ŒÄ]qágEV} P[T®_¥¨ˆ>Š2(ut4É7ßLů¿b­?Ÿµ‡ÅÔìÚ… RsÑE-ú‰¿æœF#ú½{I»ï>,è·{wÝzBõïۨپ׫¶ùAÝWª²H«e„r•«×c-*% Þ;ë°ŒLc\6;ÅŸ|åUÛî²àw5½F¡4)•d‡„¦ïع՜GѲ•$Üz]›2Ác‡#(D¾\v;;®¸¢!ÀƘ‘ÑbJ®bÝ:~=š°‰ ô;v`)*jXŸ÷þû”¯YCظq«óíªW’Í99¬:”ˆ)SP†„Pµe ÁC‡6Lcå¾õÅ_µ¼@<üp“|Å_~ɦœ4 Mrî!I ÖTcVVûÇ07—“&4x0Aƒc-.¦zÛ¶&þbGþþ÷Vƒ*Ö®eý¨Q„Oš„¨RQ½m[“cPùûïüšžNä¬YH6†ƒÑ ˆµðT¤emFû︃ѣ1ffvZ™<Öýsm³“óÔk^mG5ŠÖ÷5Y¡¡Ø Ôn¤e?ñ £~XæQTU¤çy31 €˜¹sI¹å̹¹œxë-²êÓE9ÍfvÿñŒxûmÆ®\ ’Då† ¨Ãñ˜ÍXòòØwë­ }ùeF.[V÷1öÈ#33ëúâ AA zúiA rýú6]IÚ£è‹/ˆš5‹±«VqpÑ¢…²ôDZ•—ã²Z©XÛ¶ ÓbaË9ç0üõ×I½ë.:.³™ãÿþ7Ç_x¡Ým;M&v_=Ãßx£.I’(_·ŽRy¼ç>ù$ÃßxGm-†ýû ›4©I?ÆÌL⯽–”;îÀRPÀÞ›nÂi霿lGç\r8È~â7{kJy` ežÐ{ËñÐP$ZÍrÒ—ÍNî3¯3à•Ǻml2§/Eï¯Àš_ìq;‰ºk¶7"äe­ó|>Ï þïžÅvlEiAUU\Úȯ=A:Æüúi»Æ‚7?"÷¹·ºc÷{Aƒ1uÛ6öÝv…+Vø{8m2îË/Q†„°¥ ƒp‚GeØŠ×ÜV¤:smäóß¾/Ú˜¹99 ¯¨pK¶ï#!áækÜ’09Æž‹nö:hA…­¼¼ÝõN³¹!çi€ØØº)ÜVf'”¡¡H6[“tDž¢Ðé\®&ÁeÊÐPffd½dI‡–Ɔ¡Š"ññX‹Š<öuVGEá´XpÖÖ¶¾>2GMM 7•‡Q±nþòbbš|¸y(2êÛ÷ÑJkS¤ðýÏÈù׫tzŠu‰‰lëÞR ×edÐÇ`pKvØŠ×?²[Ç'sza+­`÷¬?à4yþ\òÇûã$/-¾—Ô¾‰^·ï6J¡ rf†…¹UJ ÀYkìð :ñÎèÿ܃¨"ºë0ø•Äë¯ÇVVFÉWޙỠ{UUÃÔhg"qó®`ðûÏ»­LZNÿÚ‡^o³;§»½ÙfÞËïc¯¨r[^;(äEÞM)í*“'×·ªLHÖââ6ýXz}§”I§ÑØD™H¼öZÄ€ ç—ìÉåÂRPàUàœ­¼¼Meê"ÒÛòy†º¬]¢LÉ‹njW™´WT‘÷òû^õ-‡zøý‘ýÄËrÄ·L§È}îM¯”IðÏûã$B'ºM¡ î¼O€S8ìA™´òo~!ÿõö_1WÍeôÚO‰¿ñJE¯‰QòŠ£ÿü'kÓÒ:=æköÖçµì,!ãF0âë÷è÷ø=(CƒÝj㨩åð-óúa`E2:YÊÏN»ä4=¶¾&/ZÀ wžE“â~:›ÞL`Ÿ>ä¾þºÇi½º›¢U«Èyýõ.éK“’À wž%yÑ‚vårŸ{Ë«¼¬¹!!üPR.#<Üí¨YãÁLJV|Ûíc”9=0ì9DÙ—?yÕÖ_ï“„· $ˆ¢QT"ŠÊVË.(î]´à±îèÎ݇É/è|ä¡E©d„›S{úÍ»LKA; _›2b€šð“ˆ¼`&ê˜H$— [I¹ü•Ú LëCô%sH¹ûRî¿u´û¾Œ’ÓÉ‘ÛþNí¾Ã^oÿHD„G=]‰Öé$¹+WcŒ‡2 HŠG7$Ý-y€À~)Ä^¢Z…aÏ!¯RòôÊ×®¥|Í£C*֮ſF@V{ˆ’- }É£hÓû¶+[¶êGò–¼çõ¶6$$t«ÿäIœ‚@¤ÅBŒ›ÖìÚ݉¾ôA=5ÚV¦'â²X9ú§‡±—º¯£4æHx8‡»Ñÿ¾1‚ 0ÿ¶:›,Š {!Ÿ’i:sÔmA9áaîYˆ:¢ (ˆÁÁ¤¸éƒ$‘uÿÓ$ÄN“…šm»©Þ°ãÁ •zìUzUzŸVã‘qeHÊð0T¡$'6uaSÇ£Ž‹öºÏã¼€~ÓN¯ÛK‚À–nö k̈Îò`ÚóØCÏ£IN d‚ûþb¢ZEÒÂùÄýñ2ª7íD¿a;Õv`-ðÜù\Æ$Æ6u¡SÇ6y,ÊðŽƒjvì#ëÁç¼Þ¦MÉ óÛ>ˆŒdh;iœc¯Òsø–öùë(´Ý¯ËôB$‰Ì¿þ ãÁL¯»ðçtwhHb+Vü:kd+.‹‚Ð"‹P·)”anN9ºÃš¤$>ÜaÔÞI\VGnaÿ}Àþ}Üj£ÐjŸqá3ÎjºB’pÔÔâ¨Òc¯¬Æe³»ÕŸLç"ÊÐ`Táa(#B›Dµwù¯}Ðéi®=QQn%Qö• 9!!ô­©qK^²Û9rçCŒXõš¾ImKJÔ…³ˆº°.Ñ¿%'Ÿê;0gæ`¯ªÆ^Q]wTéqÈ÷I·#ªU(#ÂP…‡¢ E†*<ŒÀô¾„Mçñù¶œ(äè!Ù½?û£¢üš¬97$ƒZMp£dóía<œEÆ¢Çüö3Ѓ’LËôLN¼ð?¬óº½A­&×Í|Û¾ "¼ím·îÁÞRë>…²‹,”PWnî@d¤ÛQ­öÊjö]výŸ{ȹ3½ß¸   FìñCY¦çá4Y8öàbÊ¿ù¥SýX 6xP.ÏWüš”Ä‚C‡ÜþØrT×pèæû±ê-”aÞ?Ì4}“ˆkç~pšÌHÙ²ßJE—ZÕœ#‡o}À«zÄ'±*llTdÁHÀÆøxÎÏÍu»MÕÚMd?ýRù‹_Ç.Ó³)]ùùo¸À×ããé–”;mÞÑó¿ãÑu›BÙ'¹k§×'&2¨ª •~ŽN“™£ ÿIÂ-×Òçïêr+—LïÂ|,—£~SfN§ûڇɃÜ޾¢40ýQQŒè ²º1–œ|\»oü‹À~î×[÷yÚ°wbÉÍçèŸÿ9Ë}%¬56ÅÇcî÷ǾÈHÆ––íAf€¢÷?CEâíð÷ðez U¿næØÃÏwª²À@öùqº %¹õ>Irµž¥§•ÌÝfÇOïŸBPP×e¯U©¼ÎeVøÞ ^ÖÂî)O&ÓÃ$ʾZ;Koëe²F­f{}]æžÀï O-š2³ÙwémT|ßýõ¾ez&?þÆÞ‹oÅx8«SýèØéemò®F~Mò|f)÷Ù×ɸëqœ¦ž!C¦q¹È[ò‡oý[§\A nfIê‚ÔŠaôÈm¯”šî$ IjiÌë6…RÅöìÛbc1xY/¹fû^vM¿–Œ»£v¯÷Q½2½—ÅJɧ_³û¼ydÞó¤×©šókRN?? S«Ryä4š8ºðŸd?ùÊiÁ-Ó>’ÃAö¿^åèŸÁYë]z Æ¬KLìQ÷GvHÙ^øª•ó û/¿ ó±ÎYkez?ö*=‡nºŸ¼W–µ™#×]¼½»&€!ƒRÛ\/I’ˉärÕý+µ>3Ümiƒì6;[¶íï²þ\‚@aPC+*¼ÓŒ% SF6%+¾¡ú÷í(tZ”!Au5Ÿ{ÐPÆ{\+æã'(úp™w?NùwkqTzï Öœ=ÑÑÝ^õÊu:†UTàEê«Ú=‡¨üéwÄ@ Úô¾²kÈ‚d·Sö¿ÕdÝ÷4U¿lè’> ‚‚Xë…EÐ×”jµŒ*/wÛ×ø$öÊjʾøulÚi¢üž8ÓÐoÚÉá›îÃx0£Ó}I‚À—ii˜ü›µ1cF bú4÷K·E·•^¨1™Û£HÔè›3¼¢‚¹99]ÖŸ¨ @“œ€&5 MR<‚—VP á¨ÒcÉ-Àœ“_—O´‹¯·“äñßpõÐ~z=WeeyüÒlŒ**‚øy—ûÇËΘŠRgŽ*=ÅŸ|EñòUؼ̟×6Qdù A”û!ï¤;œ{â£ËʼnGܼ+ˆ½îb”!AþÞâ²Ù)ÿúŠ–}Žñ÷iš³;:šÕ)¾ñ[÷„;o¹’óÏÜé~ºU¡xâ™wصçH—÷;;/qLð+#ã.zµšîí1©¸˜éîG P£6À¾Ihú$¢é›„¦o’lÓËpšÌXrò±ä`ÉÍÇ’S@í£¸,Ö.ßÖª´42ý˜w²#”.×gdoìÜ´¾B«!úòó‰˜3Mj‰q²U¿·#IØJÊ1gç¡ß²›’O¾ò¨\­;ét|2`?§¤R«”¼õÚ#]’+¼ÛÊ#9üý¯vy¿‚$qMV–Û9ødd¼Å&Š|4h_*~xÃÅÙÙ q3¡³ŒLW°>!Í~NäAv;ó&¨“A ’â LMB,WÚéM¸ÌÖº­¸¬îå+õ†Z•ЦÖÏSÝž?•Ûnº¼KúêvóÊ }1,}ºÎl u¾_¥¦rÃÑ£DöðZÕ2½— ðmjj¯Q&~èÓ‡‹…8“ÉßC‘98Ñ+”I¨{±¯JKãúŒ ”]TjWr:묿¹=»¼Œpˆ"«ÒÒz„2©R)¹òòs±Í@›:„ºŠ9 ýåݘë®>×'ýZ”J–ıЎˈÉÈxŠQ¥âÓzô4^kœ|€{ÀLæô¦X«åû>îU#ë)étüÐËÆ,Ó{ù¡OŠt=Ãr}άIDF„‚ ´žk²A›ÆiCÞ/ åAý>´¿Oú¶*¬ìßßïUdN/Št:><˜ü Þé|oP«ùtÀ*ýXRæôæDp0Ÿ¥§ûÝ'ÌED°¾Tº’9½YŸÀ¡ˆ8iœÓhIë÷mÛŠ¦èÆ’nbá× Õúîå¶!!UiiXeçh™N²?2’ÄÐË-| Ê|°=6–éé=>H­=6ÇÇóU¿~~­7.szbE¾êׯG¹‚Ü<ÿ2¢"ÃN-hÓ@)¸½¸[óP6&(HKbB 6ïñÙ6*52ÂÂr8ˆ´X:•>EæÌ£R£amRü^Å «pŠ"‡""€”ÚZG¦—ãE¾ëÛ—m±±§Å=RHVX©55hœríy™ÎSÀŠ8Üù(ê®bêäÑ,¸á’¦ %‰Öêu ­çånE¾Û£¼›óþ‡_ñõwë}¾0«•ñ%% ¯¨ð¨þ·Ì™G^PÛbcÉêe¾’žÒ¿ºš rsÑÊUqd¼ B£áëÔTJµ]WR·§ q:¹øøqúÉYCd:Áñ¾é×Kš)MˆæÅgï#PÐt…äj3G¸ (ZZ$[‘÷»Bét:yø±×9’‘Ó-Û t8]VF¿š",å—éC©  D«eWttq˜î”.Ã**_ZJ„œAÆ ê?¸2ÃÂðëËÃÇÀȲ2&””níú<2§/Ul‹eottºGÔ,þ×"ú¦$¸µ}Š“A8 Z•÷»B `2[xú¹÷9pèX·o;Àé$Âb!Âj%ÔjEôQU™žƒ$˜”Jª¨Ôh¨‘+!^]Í„’’ä©p™fH@fXÛââ(8ƒ>¸ î: ªŠ‰%%N‚.szS¤Ó±56–Œðð¥HëøÇßoe@ÿÆ•yðh¤íË÷…ÀnwðÒk³iË>EFæŒ&Øf#Âj%Âb!¼þß0«Uv9C°* [• UTh4½:ফH6^QAd½B#ÏpÑX”J*ëïý‘‘äõ ?ÉÆÄDGðèC·‘˜ãÓíô…@’$Þz?®Þäï¡ÈÈÈÈÈÈ´‹Æá Âj%ÜbA-pØD‘*†Ê€,½à#«oŸ}ð6ÂÃC|¾­¥PždÝú|ðñ7TUü=™^…B¡à¢ ¦ò‡«ÏC£ è|‡n äe­sAÏ˨c2[X±r5ßþ°§œ¾AFFFFFFF¦CF Kçö›/')1¶;7ëò²ÖÙ€›±9¿ „WüÈÎ]‡°Ùe™æ¤§%sù%³˜âÍõà ¾½zÚý룽•€÷Ĥ´éÕ‚ ¨ôÍ6dddºÐë¾Ë7áU‡þVâ;9ü3 !©ƒuž()žÊ÷°áx<ô7 "ßÁ]@qRÚt½ ¯þŒÌéMkÏ®zI-¶ûŒ–iK¦]ëcRÚôõÀ0 ™†|eU„:øÝEò'ŸBò‚дË»o& tШ™¼Ð¼Ðcåëί{ò§Ö ],ßôÕ$ËËò¾•÷æzÏî—Æ¿{—¼¨PPˆª1í)“Ð…²1ùÇ~»xp«ŒŒÌL['É?ò­)œ’å¡ +‡ÐN÷=G¾N¼«ä[k õrù“ ðeùÞq¾¤.¼ž;/ïïû½Õ'YKy‹(*IHòoÜÀmÿȤ´éK€à}A«Ým'##s&ân žî@hY¨]#«àÑâŽÍío’§[õ|”]¹aö¸…|öõyôõ>måýy¾<½Þ|x|ü°YO몵B)ðé®2 X(›Sœ»u˜„ô/IrF’B%IÒJ’K R—=·dddÎz˜E³±¿eQÉ=Yoä¡M‹Bsù† ·dqé]ò=ÍBv¦ÉÓ*²¼wòÞ> A° ‚hª]NûïÀ¿’Ò¦gâÿ…ô|ÁÍ©%tEXtdate:create2014-04-10T12:39:42+10:00þ%û%tEXtdate:modify2014-04-10T12:39:42+10:00ZGtEXtSoftwarewww.inkscape.org›î<IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-flowew1.graffle0000664000175000017500000001004013553660046027225 0ustar zuulzuul00000000000000‹í]mSÛȲþœü Ý|º·ˆyŸQN6§°v p0Iº%ìt"$G’CØ­ü÷Û#Ù–%ËÆv Øa¼µ±‘zF£ÑôótÏŒº_ýëÛuè|ÕIÄÑ//°‹^8:jÇ ºüåÅûÓ_7Õ‹½~þê¿vvNÿ<ÞsºafÎñû7û;΋ͭ­ín7Ô[[»§»ÎñÁ~ëÔ:¶¶ö_8/®²¬ûrkëææÆõ”ÛŽ¯`ºuœÄ]d·PÙ&p;Yç\¦¨½Ò8Ú ÚÙëçÏ^}Ö·¯·ÛYðUø·:Ù:úÛ«-sNQ¦/uò½Úükm?ƒ*?5 ùIâ›Ï^¥Y·üZèÆ×Qp™Ä½®{¿~Kü‹‹P‹W[}‘i,<— h>5"òjkPuÑ„^owþÓK³á¥³¤§·çßøíÏæ’Q.×½ ÚC±þ½÷ÅŒD:87lÆß£ }ßpþæ$9q&™âlÃ‘Š¸Jy!©0áß¿WîÅT¶úéx½­8 ÊÕŠìï–òƒ'£}_ˆµ²ÛP—’ƒÛ)NB•ñçòìÈéâünâß”Í*vx4Úèìa/ ~•?ŠîMõ§8¾ža°ìøÑW?=J‚Ë &ƒ~λyô)ç%â°wm‡Áe4V?¯?—nuý6T1”O´¾¦0ÆòQøÃ Ú]?Óõ¦„Ù&Æ›9X¾¤ä%ãÎ?|ÆZg*‰“zùw~–9ÀC÷oüįÚ Ònèß¶Ú~8veÐIø8íkçgø»^Á¯A¨Oo»c…}Ѓºl>ÀvãvïZGY]A‡IÆz²?0Sƒ! ]i5ÍQ*@=(ŒŠ8öäô(è´T’zXÂAuŒnQÏŠ#ª˜ žÀ’ƒ$Q®)Ê'Rb„•ª*W“v•êuåwu“~õû0޲ýè"ž¬0–Ê'[=_œœ^¹J0TÁ³üe³¼Ç¥ÇXSdBå1Z+P*éÈý5èöQWG-?J7[ú:8ÃN­šT‚¿ôhÑ\kÔP{*«ƒU9¦$®¨g_ôélôq ÇÔ³ÉHðlñ4wü©ï„q¯3þ´«89ö¨/‚0œö¤ëC¡"Ñ0ú}ãœP¦˜Â+!=Ä„鯯q1,Ê„ä˜øB{Œà†’IcIÜÿs(&‰à‚TJVFJ}Ô¤W~'¾™ÖuÒ˜Ä u×èh‰ÝÌ(@‡@Þ½—‘ÄÀÝSK6÷2%Œ ‰½<¸¹$ÒÉ‰ß zÕþS¯Š~ÕTíTË&ãêÙRŸÎ’ìŸÚù?íî%*䬷cßœbˆ¤½sø›Iôüï³ @ì<<»@gQßí+?Iu†œ1ùçwo›§þy–èáüì2Ñ:2?ÎÞ†ïþ Ò?ÁXq‚ (~Öõ“ÎYö ÿbLò/¡Ì!,ÿRÈ|Qš‹P/a,á(ÒæKà\DH1õIäg½ÄϾ´Í,¸¼Êttá·»ÑóçæÏÏ.R,œ³öqþ[üÏ÷»L 9(ל"Šè8Г.õ(A’+`LiÁÀyd‰dµñÒ—øs¯Õ Žjþ@ôD§9<‚EM1›ƒ@MŸroÅ`Ä3ïÇôþx“ÊÞ<¢)ü5ZÑhM¼ZS!|§A6bŽ`— ,/ÊÁ‘QQ^a޼ðIœåVó¨ùùl¢2vǃâ#·Rç #y ` a€áJ·-Á€¶•©{¥9cª ´8ÆS~{Þ*0EÉ÷pK&qEÙ¿':ôÍÄÊ6Œé&Š0ö®K¸áCM ð@~¼hu,@NDQwèbT†‚„ þ"ƒÙ*L»*¾«CÌúg埉ùyF,Ñ?›Ïw{\ol>ŒN2ñïvÁÊ‚b™.Øi?»j|uÃÎzaÖ [[/LÎï…Màc=ÿÓLÀê·:üª³ íϊјLé·ÚïÌmá×çØú•»™0_>3Mðš8ŽáÐ],AÀÉTJr†<Š &~„á¢SœSÆù$¡Š¸Â˜!`bÀ– V Ì$ì É(˜)ñcL³¦Ó]«Ècå ÞN’JŸ––Q“ u /ýömEüÂÓ|ñîÙ¨÷[ó8'ŽêûA8_K>©æ·Jfå¸àüºXŠþ.bø×Œ|ɬáÿT öÔ ÿbJ+L‰TØSdè4s‹ÌLˆI.0b3Cg>ɨ°h’‚YÀºÖ˜ÏPÖx b*aÐ…]€š¹/É<.À"ÄqV›Ñ¬3°3ÀË€‘äJJˆ"œyŠþ€3 °uËó;ä^wiYƒßüÖà·ÿ¬?FËÜz…¥r9š˜0.ɤÅíÁæcSÖ¦ÇÆYRËjë³÷˜Ú½Ç–-Z\Gô–I\¸Â#@lˆs†Ç„uížÊ:²®¥5Kk–ÖVõÃv1禀§±‚y‘~±Å[—™íW\€,<©æZÌY`)È.æØÅœ±Åœc?ËtÝÓòÏÒuðý.ê<9„¼ÿ¯D.Œœ»8Zy°)Ü€qjB.‚¯!-B®;B‹KEHrÿ)EHi¦g¨2Å‘ÌÃr.„\_-BZ„\× AèÑ6érªÆ¨dÂcfE´nž–¿¯\Û:$ù†#ˆk†‚<•'WjÚ¸ñ¡ã•ß©CøÏ8›OÖ—S…ˆÏŒ[Å´}Y’ÌŒ[Åü}YÒNçÚéÜUšÎUƒÀ¨˜ÍجÚl.pnï›ó& :—úì±›–bV´ê<ùrw0ŽãÐok ±d«„»q2âš÷S“æ‚;‰8›fëĹbë­ëJœÌ§%NKœ–8W€8÷£4ó£¶~džjÚíSÌ[l ª½ƒÅ½ Ç®9‹¸¤ þ qy—˜oêÄÛ"iz³púÓÆÝ[Q½HIßÌ߉¯»½L;‡qG;ä‘‘”2a.™T”æ±iò—<Sè¼}À0ØñfªLº¨‚¯&¢Míg+¥Ë]ÄhÄRæ5íП´±>HÚ¡^¹¨ÞëGî~éaf]F ÓEæl;HÚñ» Ú Ò¬¦XÕÆ.ÛÒ€Õ°ÝÃøs5ù0oæ^t9®|ЖNœ5ÆLïÄf­ö@G—#=ƒ Á|”§¡„°Úóòs4°ªÌ$_U6hâçIÚ3®lý‰Ä9äSО֕™},þP+ÞÅà5oõîÈ“TJ›m"á`õá—ˆ9ÿ0=7v­¼¢]íŸ~çgÙ•¾qþ«Ó¿þ©<Œ3=û-%ØÖ•Nn?†!3så›ÞÀsúœÎ kÔy²â¶ÞÄY_¿óhA9Fftûõ]„±ŸUÕºÿ›áª —U\ámœÅ‘B»ƒ¨6ðÆ/Ô¦Óx¡7ÛÙ_·¿‡~ÄHÿï›ðÓþÛx›ýûÍ¿÷[{WÛWGïÇçÛ—;ðwçè=þtuþöC¸ çwöøé)ùý˧‡h{ÿãÞvë Èüú-¼ÙùmzóÀø‘î1¨2­þcph’ H7T_|5Ö¤ñ¦Ïf¸FÅXoºÆ˜×30 ¹Ç]"9q&™âlÃQ »Jy`7p$Šßïh‚ˆ'ú+›nÐŽu 'Æ ¼Ï¡pwï£ú*–çâ(ÒE&Ø 'ÚïEáíÝðrßTòkM1}@´fFö“_”Vgß|8 ²q$Üñ£¯~Z1ZŠ×`¢çÍ0…©·Ý6)f°ôòÂÆ0SÐs–}_zºti§ÜýûTïEYLëö³ƒÔɨ¾ÓK艼'Çmö“}ï[×@Ùþ¯èâ›®äáþARAªrweȼÛO¨¼ G’ÚøÐ;¨Ì¢¼Ê’žÞªœ?é…%C À#:÷GN—»ßFÏW7§M»yкV蟿ÕF½ë¯›P}–>Ñ—ôvĦٽéÒÚ'_Þ6±6¨B ò)Ž¯ëÆ¨ËkW6B|ðŠÓFxhX¬mP˜gõѸ£ÑOu*§Ä«1l)¿»!(Óëçÿ©{â‡Üneutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-compconn1.svg0000664000175000017500000005642513553660046026270 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-09-20 23:42:18 +0000Canvas 1Layer 1Compute NodeLinux Bridge - Provider NetworksComponents and ConnectivityProvider network 1VLAN 1 (untagged)InstanceLinux BridgebrqDHCP NamespaceqdhcpMetadataProcessvethvethtapeth0iptablesPorttaptapPorttapPortInterface 2Interface 2VLAN 1Provider networkAggregatePhysical Network InfrastructureInternet neutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-flowns1.svg0000664000175000017500000010320013553660046026156 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 18:11:40 +0000Canvas 1Layer 1Physical Network InfrastructureOpen vSwitch - Provider NetworksNetwork Traffic Flow - North/South ScenarioProvider network 1VLAN 101, 203.0.113.0/24Compute NodeSwitchRouter(16)InstanceLinux Bridgeqbr(1)(3)(2)VLAN 101(12)(13)(15)(11)(14)Provider networkAggregate OVS Provider Bridgebr-providerOVS Integration Bridgebr-int(4)(10)(5)(6)(7)(8)(9) neutron-12.1.1/doc/source/admin/figures/bgp-dynamic-routing-overview.svg0000664000175000017500000005605513553660046026400 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6 2016-08-12 16:35:04 +0000Canvas 1Layer 1BGP Dynamic RoutingOverviewProvider RouterL3 AgentBGP Agent……Self-serviceNetwork 1Self-serviceNetwork 2Self-serviceNetwork 3Self-serviceNetwork XRouter 1Router 2Router 3Router XExternalNetworksProviderNetworksPeering SessionPeering Session neutron-12.1.1/doc/source/admin/figures/scenario-classic-mt-networks.svg0000664000175000017500000005672313553660046026371 0ustar zuulzuul00000000000000
Controller Node
[Not supported by viewer]
Interface 1
10.0.0.11/24
[Not supported by viewer]
Network Node
[Not supported by viewer]
Interface 1
10.0.0.12/24
[Not supported by viewer]
Interface 2
(unnumbered)
[Not supported by viewer]
Interface 3
(unnumbered)
[Not supported by viewer]
Internet
[Not supported by viewer]
Compute Node 1
[Not supported by viewer]
Interface 1
10.0.0.31/24
[Not supported by viewer]
Interface 2
(unnumbered)
[Not supported by viewer]
Compute Node 2
[Not supported by viewer]
Interface 1
10.0.0.32/24
[Not supported by viewer]
Interface 2
(unnumbered)
[Not supported by viewer]
Management  Network
10.0.0.0/24
[Not supported by viewer]
VLAN network
[Not supported by viewer]
External  Network
203.0.113.0/24
[Not supported by viewer]
Network Layout
[Not supported by viewer]
neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-vrrp-overview.graffle0000664000175000017500000001444013553660046026776 0ustar zuulzuul00000000000000‹í]mWÛH²þ<ùºùº úýe6›=2“ì’„ LrwçÜ#la´# fNþû­–Œ-K²±cŠ9ƒÕ­—V×óTUWW½úçg±w¦ƒ(éÿã%õÉK/ìw’nÔïýã寇?mš—ÿ|ýâÕÿì~Ú9üÏþ[ï<Ž™·ÿ뛽÷;ÞËÍ­­íóó8ÜÚÚ=Üõö÷ÞzpŽ­­·_z/O³ìüÇ­­««+?p­üNræ¶öÓäÁv“ÎÅYØÏª:HVÉáÄ8 ièÉ™V‘œ’è OÁ¸¢VIª­|ÃcDø~5ÊZ+)Õ¹l)_Yf)'R j-cÐÐø\(!•d _Ö*:)\MÒ5¯ÓàÑFsZŤÕzjçÊ¥…„ÞLûRZ"×BYaØÝ¨ëvÈ®zs¦¹s¯µaœ j(gÀÖ°ùñZ1]½Œ– @dn¼†¡—ÂmƼq ^;YØNÓ¤Q£"“cWôØ {Açz¢ùIr‡ã¸À@ÙI3K8nP=ˆâÅîäkÔ­°tþ¨l^’ .ºXF¡'Òç QL*Á,ãºQJAŸWÆeLXÃüÏ”y¾ùNHÂ-A}¾}žuÔç‚©f ‘`Ðg~g’•`hºÉÈ=F›ùõy*™%×nPŸG}õùeœIÞv/ìgÓôùñˆïÇ ö»u‘d›Äµ»•³íá¥9ï“‘ÀAHIN™5†Oe+æ;ýôXE¬¢Æjd«¼Ol]Ùª„¡dA¶ãžlA¶÷ÈVÈVÈV {ŸV©`Ì| 6:1ZXÐ=•Üð¸U¾qfåF[n…tLU_=Ùðêk'ÉTmsŠY”S¸iâ”ü¹ê»'Ýúc"q<q¬°× yy¦=žñöO¯a‚ØûfWIúÍHMÌ‹œ'¼Ïwíeµ¯¸‘‚X)¤}¢+îÇ^ÙD› ¥×ܾ€Î¦é”9b>°m¥¶­Œ\GÛ f³0Â,°l¡%åÓ`t+¡aò;*´¨éVˆéÜu‹¢ :ÆÈªQÝ{ÀŸކo‘Õ8“‚IB”Q@Pn}×¾¡V*°Ž”ÕãÈ’m,Å f bFÞA khçð¢ßã£oÒ¨ÛkÓž‚ñÝFC@½–ŽFy™y¸5ÁŒÌÈ<È<+bðôÒ|ë=ÐO=ˆuÃêñ©UFhK¥F[¤Ÿu³áîM4|~~ÖÚð©1Ýð$7¾ =Ђ0 £p!i=’.$áBò.$­ÛBR%ü¬¨ âS©…6œ;¯žÂ„nD‹lS:æ39ãðfãq4ÇÐCÚBslYÖúœ\m½X>¥ÁŒÐ>æË’ßϺíLè |dK‰¡/ÉÉÉgÈg?M.£®£ŸÖ½õm³À=Jûš*É™&šQ®0‹èÃr½7»‰”ÜD3 ™ ™ ™l-‚*j†.m=Ÿ¥­µÌ–D4—œði+N³à7&ç'7-¨Vl|!‰ä†ä†K[K.m±{6Ѥ¡>%ŒKc¨!’+ŒTP¶ÑvÙ† š§ œ)1ƒsŸ¡–é©)1aa™a¸`º8@øê2 ms.¾pL¢ÁF²ZKî²äIŸr˜½ðBßä ¦%PŠh‘B¢¬2q¦HmÑ:.£ÀMÓÏá GõÖSÝ[f#IšBÍïûwÈ·ó¨°—9÷bà3Qâlz~¯Çª»;xºáI¤ ±ÊýsóÕ4P90R@ÊAŸlÂô2TéilfgMû Â\—ÂÉ·µ Qí»ýj”»Ýr’JíPÈúN¡åÂRIe9iŸ”S»X¯êwR¬@ª´v LAµ@Ž´(íÄáê$IËSØói>‡[SØ›eBxóö¶MÕó~óœÍ­šµ¦Ëø\­Oœßd”; ¤¤¾¬ëä™KTîª:HTPuX ÕáÓe˜ÆÁõÖpäŽ;S…ºÊ®š2X ¢IŸ”¿$Éaª<*3šR…šDXÄÓÕ$Öd ÐsP%ž[i/Cî»´—$|ÙÒ^Ú- +*Àæ²L K\‚Éš[|zu®¦ÞR2_»ý¥ŒYÂ… {ŽÕ¹Š…[Á¥T Tamì‚ ·„(Î-œ†š×p¥$úR Ësµ[ž«Þvy®gt‘ñäKA#{h´äØM3BYGåâj õ±Z«©Ëw)Ÿ'42øÏGHbˆ’Œ‰¹¡Q J¥°Œ ê!ó«mBX­…ä\RwuÊŸ94Òe¡±nù†5.…Ï9Ñ ÑV¹Ì"|­QãS‡Î&sÆÜ´Î”ß­TK%“LÙ¸WQQQ¹¶IAT^j<ÙR¨L–EåZb'q;¸•{[r¢¶æÁæV*¥o]•oè-tXÓ+7néít‚E¾±È÷SeÚ ˆ (·;ž3K"Ø¥A¹ çö",鈌ˆŒO5:ZEF}ßKO¢1eÿÒÈxÛâÑÝpõÙXòùZ8¥Ô(m‰Pj±eqÁàƒ0j£ .‹k Ý4À«bíB£Bhl륅çC;ÝæjÒhW+L½ õ5gÌ€vBG¦õæ4FJA¤]±b]`uÅ´·‚â2=.Ó¯ VÄÊÀJµ´fH¹ÏKÚ e¸uV¯´ÒëKMÀÚ#FÑ…°R)¿´1z†±ò–ÝÁ4/–¯A°Å6 +¥˜Äj»ÐŽae\”K=5e«@YÝÅŽ@ù(@)Ç„fLùÚåùcŒQ° t¦ ×pP…@C5®®<Â$šßˆ”h~¯RŠe‘²â„?7¼:†!Ú!Ú!ÚÚID»V×aäý¯Ã,½¨i#é†Ç(ó%ØÝŒÖ&ôTk{`EhDh|ªÐh)Bc›ÐØÒx΄ƥ·15Ì>£ åL.>?4.¬¼ƒÁ;Oy5¤mdœžîAUô–¨ÖÔue\¾e|Ï®à¦u)¦|7%@uQÐŒ«1Aj µÿø:&HÍ“­“eªNi×Kµ7æ¯:‘ç_÷˜6Ó¦bÕ‰E²¦î×aºÉ½í^ØÏfg$r¾ð,,Cè”´OËä&R>LmnÁ6Tn†;²²Ã/)úâÆ¹V%÷A{4J2a]”‰KNTÝþ¨ŸBŠBš(RQñE*rÙUÌŠfñ´áYYYYqXÑ=žwypeÓ• Ff¸?YªbÃcÜøÎŒºäL+¬EøÐ….8ÖÁÅêÈXÈXë]ÅUÔh­ cDglUwß²2÷Hçk.•,rϽø¹–ëj-Ikˆ e'RÑ­%—–˜3bœ¿{Ak‰¦-á¹¹¹gîù)Jë ŽÛ,»Ä¤ïÖ‚%qi€>$0‰æ)çR+…ÄrµëX®–1»¾åjWv;%’’V®}¸Êµ7F• ÁÈ· ê ¯^Ͷ‰à¸B£ê^Œ*¦5UhT!ï Qµ´3È‚~§MGµÃý¯0SÁ¶Òš¹?r Ê¡¶R¹ü|©H3ŠõèîÓ °Ö£[‰zt7뵂tÛ=¸X/ÈÂG)GGµ#Õ°Œ55„j…mçÀù„ËÑå~>­JÜí~ÓJèb9:ÜpUÙ¦¡ï/ª ³ájÔ[*° a àQ1Ì’Ç”«Gl î·ÂýVk¹ßj¬ÛŽÁèØ ŽÃx7MÊšNiØž%(¶´wm¦:CÚÊ]"dchÊ´]¨“{ lÞ[h_¸ ÉÜE_[ªÍÔÞÊøåÝ ®Ò4÷]r>¸&¤fZÜ È À9"2"òÍ 9æxbªª2mªªõòr¨ª"0>{`¬­9>\j€¦èµ ±z¢[JÄZ†>Ù‘K‰¬Ê„¬§n±Äµf\k~ä'.†˜.¾º1N´Í'>LLf4LVXÈn `¼f£5îç_Ëýü÷óã~~$8 ¦zÆûùŸ[ÆÊZîùMªMGÓü©Ë‘>عœI%˜e\ 0k™, `WäÁpÅ8Ö³Áz6kà€÷\Ï×O§ <7Ka¢|LdFùl3P8¹Òe`ˆ1-|«ˆb†SE¥—ÆD¹T‰¥IO¤Ì瀈ŒƒÐkI¥(ÂD8¹kË…-‹˜ˆ˜ˆ˜ˆ­Tó’ôq «y!Ô!ÔÝ{èE}£¿óý —‡HÖ²JQÐçÝ[L5X$mÂâüµÃj5#$‚*‚*‚j¨G«;Vsýnxuq±9«.  î6œå*ºSªù lκ÷]Ÿ‚ÝîÏ5-‚rÿ[Zq«®0¨£ö‰îÚ}lçëÉ€‰ÂõêíÀã¦I‡©÷1é†t:ƒ¯]„ LmvƒåQIP ]ü)@¡0W󼊖:ݓܖ¸U˜Œ“¬D2‚ý4¹Œº€CÃT’v€jék·U…M]^xáhXS— ÁŒ¦Taâ*ŒÈ§›x€0øÏèi*Í,5 L0©gëÑÍj”` ‰ñ% æ#h+h…} ÒjSÞ¼g®¨U’: &kÒÜ×N^•€NëÀ¸¦'¾¯…Üo\ÓD‹“‹îêÚÚEA¡ñÆ÷š‡@å £¤0úé–¨ ¢î{í‹»tjé%r––ÊTÕZbÆÓ2غçnA¶‹­Òœª¬ÒZ/¶ (c$µ°Ú\Àš‘ y ¥ß‚˜¸ÍìH}®%ÈŽŸ¯kAœ`3NéçŒÃ!‹žÃ;Úü`M¡ëðá\‡ëå œµ4ÁYCÉ÷Mï4Þ .i‚ã(޲kŽœz_>Þ?Z䣥O—az…WâglHÞ¼„£ìSj(1ZXPMdsög°„Î(Å%t\BG+—ÐWm ýìü" óõóÁƒ, ?·€WmîÛè—Ä´YaE‚=´tXC-1ŠÎ³ju"†‰Î¥U‚¹2)³Š1«k³ª±˜T«¸hø2PGéj@ëÍÁ”¡.¸8X:ƒ+:!Ô!ÔµžÏ ÷+Q SÂó9§¾’\ ¢Éˆ’Ï":_kt- k] èZX-×Â03æ=ºÆ V~–ŸÓ¨[ÆèÑË=8:p£+ ¹Ô7“¤3ž:ìÿ&ic÷Vn°ˆ·J®®×¸y3­ûÁùà0™£ýøé󧺈`t÷’ηpÔ±@J­¿DƒèxÌS.“+NûA/ W3ãÌt w€Ö³0£ù¿Ãðü09襻éW…n\‡éøÊ#Eº¢I¸¿m…£hù18kˆ~Ë/ãÑzó}øsÞeу8ꄃùnãK^ÝzÚiî6¹È¦Oñí~tdamUV|ÜÑN”v’Q7dq(°·=Aâƒ,Jú•æ †UÚÇùm¾í÷@?®ÝK7Éj7ã(3qÚô^Øï•ÈsTaûF%°†0°·Då‚yÿnpé<š«ý¤°Á-~›&=uaFn.Ð~Òsp–Â=‹‰?’ŠI7:«ÝÝõnéMßœ”ª6)Ù$£òGF”Æû›¹Úµò•duxøCe§á•÷oБƒ+ŸjÇIÎÿHŸÒ}bÇKb5T€Ö0eæ>¹Ã¦7ðž¾ æhëÄyºà|˜_éH7œ¾øh<4H61Ç5&¬®¦kÔ‚Yn”¾o‘ÒD -ŒžE! ø ¬QP¿ßr n"~/ÜÂéx 3á³Sûîs*&ç÷qúI,Ï_ħ~èPdŠò9 ºŸúñõíðò9¹šØ7Cõ¦5²`>Ö:‡ê#ðÀa”Õ‘p'è_ƒ ¥¥èq*y~N‹.T½íN]Σéå2ô;á‚}íG¿_„cçÄŒ§ÿu¾ígQ:kؿ̫t~ú`OGõ‹4…‘ÈG²®³7¨ìoÿ8ú lÿW qI§óðÐÙ“N Õx W.ÆE‘ÜEŨÞð47“n¨‘u0áOz•¥áÖÄñÏñ˜¡k à¥Ãcÿdùø¤ûpÖÃÔÄÁñ»Ð‰wÕB’UjÈÒŸÃÞzb“ª øªòS¤d¶lò€«ÍnT廆Aû-IΪʪ/+wæ} À"4ÂGÃÒ@ƒ@ýÐxþR129á¦#X mÆŸç1ˆ×ëÿÛèÎ; ­neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-flowew1.png0000664000175000017500000020226313553660046026627 0ustar zuulzuul00000000000000‰PNG  IHDRTcSé=sRGB®Îé pHYs  šœÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì}|œÅñö¨÷Þ«å&[î½á nØÆô^„PB„äË?”½CèŰ1ÆÝƽ÷.w˶l˲zïúæÙÓž^O§;骴£ßéÞ{Ë–g÷™%R¤P( …€B@! P( …€B@! P( …€B@! °1nõL6ÎC%¯P(:ª’ …€BÀ(†jU …@Ç@@1ÔŽÑΪ– …€P Õ «, Ž€b¨£U- ;  ª@VY(ÅP;F;«Z*v@@1T;€¬²P(:Š¡vŒvVµT(쀀b¨vYe¡Pt Cíí¬j©PØO;äaQ¥ååt>;‡¢"Â($0ТgéæòŠJÊÎË#w7wŠ #_o‡¯¦¦–ÜÝÝùãf³rÔÔÖRN~••WPxh0…Y-¯âÒ2òàòûûùZ-M•BÀÚ8 Cݶ÷ýüëÊÎÍÓ×180€Æ LW]>VÎÙÀH¿[¸”6ïÞG2‡; éÛ›n›1•ü|}ZU…Gþï%ºúŠñ4yôñüÉ̳FAþf¥÷Ø ¯Ðu“¯ +F5ë~KoZµe;ý¼bµ`¦òÙΉñtÓ´ÉÔ59QžjñûЉ zíÓ¯é…'0|ùã/èøéLš>a c0®Å4ìyÃgsæ àïÝk³l_úðs:q&“î¿éZÖ¯w“|²sóé™×ߥo»õêÙäš©–öSiYrígž§ßr=†ÔÃ’4œù^§`¨ËÖo¦9K¥n’膩)>:Š%|ÚÄLé—•kéô¹,Ña ¡8;™î:x˜fðËß?-UHUû£ù4¼_Ћޤ3ç/Ћ—Ó»_Oÿøýo)4¸uÒêÙ Ù‚™^;iêøºâÖ%)ÂC‚ OÛä÷KVPÿž©äãíÕ¦ô­ÿ´©2Nö°Ã*Ô{t”A½{Ò7_¯WI£YåïÕ­ E††ÐÂÕëiëžý4r`?'ƒ¯iq Voár^>bh©:!&šŠËÊh9eÌTý}-W[sý̦™9ѯ ;öP·Ó}7\Mnn:“BdX¨`¢ÿþàSÚwäX«aq‰¨é¾½(ÐßNTE0â=éG¨¢ª’R;§ÐÌ+Æêí²‹×n¤Êª*êÓ½+wÒu”•“K 1Qt+«å`†ÞdzçŠ2h{^A!}>÷ºrÜeÃ¾Ž¥G#öÒ±C‘—‡UUWÓ.óJ®Ïïn½žüüDVH¶Ç‡o¿QŸõ³~¤¬Æ gÉïýo¤áúPç„xúôÇŸÅ=ì/p¹!Õƒ–®ÛDwíéôíÑ.Ô¿‰º]ÏeC]!A£îý{vçg'1·ú_AQy‘š òÃ\£•àZjm!vsÛ-^³AœúêçE¢>À¡ÐO¶ï?HÕ<°¥¦$ÓÍÓ'“ŸO£‰}ý mCxàKëÚYŸÜ{ß|O£YâÏ/,¢•›·Ñ„áCiçtºûº«Ä€!oÜ´k¯8¹ã&Z³eåñý7M›$.cà»K)ýxÁd5°W;l¾ßYR™¾c£"D{ýºq+]6xÅFFh/796•êfØÐgë8*ò-ŒHöihXWŒ&ÎAS˜½pa`c‰¦­Ù‹–L4ÕÕ5B»¼éʉÑðîœ>ŸE?.^Aw_;ƒ–oØBØ”÷ØÝ·Š´´ÿ ½-ß°™&±)«oj7‘wKí¨}ÞÙŽ®C;u†;w¬¾! ³E§?—}‘m’$Fé=‡ŽÐŸKÁl?œ2v$UTVÒË}ÑÄþ „—1‰_œñÇÐI¶AÁUP\,²Èº˜Cx1ÞÿöÁtƳ¤qâÌYñÛ° ø [%¤ËµÛv6¹¼ãÀ!:vúŒ`æá,¥á%ZÁhÝö]TTRª¿7†™þ5¬¶b¢&*×VJa&Mã[Vû3³²›$†*Ë‹ -µ‹öá° Jˆ§º²j )ØA…}ëzàè š:f eiöà±<þ¤¿ø¾ýÕwÌlkhʘ‘äååEo}9‹vìO×ßs”ûâ¢ÕhÖ‚%œW(uJˆí„HK«™‰ÂüäåéIg².ú0“r/}ø™0QLå<ú¤v¥Õl[þpÖýã–Ô_ÿPÃÁL¶CJÃ6E¦ò0Ö|xÐR®Êtøä)Qo`& ØžçwÌ´¶¶Ž^úè3Qï+ÇŽâ~=^¼—Ï¿÷ —êú<„0Û¿Ÿ'úvr|¬LJÿ6‚ÐÐ)!N0SsÚQÿ°“8œ¡‚q“µxa–êtQ‰NýCÇÄ„Ôë?Þs›˜ìD :pô¸`N¿½ùZº‹GHŒ²OÞw•s#¯ß¾[ŸtAQ1=z×-BŠšÊcÆ„±„‘¸ŠG\c4Œíƒ`ºZF‰#«œ}~ðÖ„TôÕ¼…ôäK¯Óÿ{ëÁh2Ξ×' @HP þEcÅdU€¿á¥áNí˽‹Á„òÁhêÕ½ î“&$$HTÓ˜‘BÒ&ÿæv–:ê…4*næ†ÿïÒ´ñ£é‰{ï î,ÅçÁ ­tíäË©G—Z³uýëèOÿ~]¼(˜hÄK"ÉÜv‘÷Cãó>XHAòšöƒã…œ<šÊƒ+´Ø[a~À@…¾‚ö„ŒöÏ÷ßÅ’ç112 gú…Uh-ËΦçþø0ýû+|0ÐI‚„‡ úÊSúïu<Øb’’ëĈó$àåB{Ã…Ôgiýõ 7€ÞÈ’0Ñ΃‡ /‹ß-åa¬ÿôfÓ°ÊäÁ„òB€¼²ªZœ;|2C˜àðcÝöb}ôÎ[Ĥ1ìøèo …«tï øÁÿ¸¿þç©ÇD›ÈsøFÿ~ï›hìÐAzFKí¨}ÞYÊP!qÖÕב7K ¦RHëz›š$0 ¼Ð§X*í=tT0n’Yz­¸ 9¥?)Ò•¤xžLÁ(-¥Dy^~ËVHÈ 0dÝËÕhÛÅÄÚ¿Ÿ|T0yHB¾ü@JyñýO“AA°KÉßݹ¬PSfœ×!%ôä:™3wŒŸÁ€3€ÕKIÀäÙ‡KC˜áJêÛ£[³ LPÅš#HÙò£•V ïG»ü‰ôÿ{ôw‚tNŠ'¨×ÿýý¿ïèñ4·] Ó×þFûÉ2á»Ù`v¯ódsÊOËV ‰³œ5ô0OœÏ`[ì ÆBö|c@:wáb“²Zá] óÄDÚ§¤¬\œ#C}qŸ!¡l0`J üôÃ÷ñ€ëgv¿”ÏûƠГ5¶-7:ð·ã.I‰b@—ƒùÑS§iÊè‘ìòç&\˜€/ Ú˜®$ô7Hš'3ÏÉSâæÃI´c,@¼óõlq?Ì2’ZjGyŸ3{:²p°Ý¡æðèmŠ ¶BåÆ ‚T2-E³{¨Yfóý°e=ñâ«Ú[Äq<ÛI%ùûêì—ò7^>Vª’×ð™êÔÎh7«€•ñrA²£Òʆ‰®`âeg{l|x) ÁvSHPèÈýÙæéÎÏù‚0‚ƒ!›CðýÄdôÏÀî¦%C¿^®¯Tó´÷ÉcHÙ’€¿1©L^Ç7lÐøÀµ ¶°Ù —ÒlŽ€änn»hÓ4<†ÉE«¦?yߢMf©n[~7‡ w ’h2ãíAš=>`31‚z S &ZB[Á[a/¢£Ø& 3 ìÚÞ^—¾:hÃçѤmßõGÙà~M`ñÚ 4r@ã`Žk­És=:§Ðqî‡Ãû÷¦¬‹¹Ât”Â6pôMhNrB¨§1W8h’ðøÐ’!¸¶Šµ)¼÷'YÛƒ©^! ô1Sí(nrò—ö ;£9¤0@Œr†U×{0#“„ŒÈÛ½±øel$ÑÙØ Â8þK(†¦%ÉØ$˜¼ÖÜ÷pöŸ›Å6,” /ö`vg’ŒÌöµg¾_L´É40C UmWú!V£²CíÕ­³¨Ô}t,¨êxù`ƒyVk{”iû†=˜€1À” /ê(57 ÝÎþóç?Ȥ“ÒÿРœo~ñ-Ýtåä& ¤8ø¼nßw€N³ê2·]4É_rxÇÕÓ8/Ý.50BL.⃠'ôØá²õì#¿¦ÜûžI0`˜8¬Á ¿µF Mjo¶‰BRƒKœ1òòôýØðÚ•Ö¨?ÒÆ@ “Â2ž„4dl­Í£7KëP×Q¿HfŒzp½¡ý`b6Péeþ¤5{ÉúÂÆjKy¦[gL¡¼ñ}=‘Ð$ä5SíˆÁÚÙ©‘»8¨¤fÀœæÿºÖh `Ť&Z$AªcÒ$:©‚Äòˆµ6DÑå3û[vï×>fñ1ÔÆzþƒôûãH--( @LžÁoHPŸPn9ɄΉÉ8Ìöƒ‘bò ƒ Î/Z½ŽÀ„[²-Ë<`ªIsŽ¡ö>õò›œþ6ülIÜð­eÔÚÄ‚ƒëBÚÁÒwsÁσ¬Ñ.tµåÂ`¦³Ç}/Ì~O5Œ½ófÆ»NLÎIIê½öY0ø c5›)‚gÁÁc'Eß=»4zhŸÃ Ÿqöœ˜ø’ç1èþñ…ÿ ÏkÔ_¦ ¦ŽÁ3éZjm¼1a‹‰Z˜ž@b ÂCÅ×î±ì}œ'y!ÔH€~ŠDîÓ-LÐ6o`¯ôY ~ –Ú±¥táºéžd‡Â艨P×öó„&ª`CüfþbZÂL“0p ÖÒ·¿,ª ܰÂêüÅ‹t¿H LX¡ÁàL) éÁå$W[>¤}ºw£yËW ‰6SIp3úòÉó„ô û›®£ü üOµR\ƒº%'éý7S;'Ó¶kï“éËoØU1aIî\½ºuÌxɺB½Å$ÝšÚáß+Ÿ³Å7HKy€—$Dؼ¡ÊÁÙ¸´wÛª]’XzÃCþ` xÑ7³?0Ì Iü‚c ž0@Å$Ž¿ã>»LO¦*>Q¸œ¹6wÿ£†‹ðylzíÎÌ}îc­YH‰7ó¤,¼_5ØæÍÉðÿày”ƒ8ú£d¨~1ÇÛ°´Ÿâ^LòÂf?ñ–˜¸Â¤ÞE fæÌ)‚äga£n©ÍMב÷5êÌ,|Û’yF÷çºIYØTîâÕ@ð Ô^¼ ÿ|ûC!•À KºAŠxˆ—â}úã|zìùWÄ à§KKëw¨MÓ’c¼TP —qBZzäö›y X$mš°£Á£v&I°±.b×.̶K‚T;˜­)3d€ð•Ìa{1fXá¿ °§_{W¨ù) tÿ׊—ÄT:Ö¸wH(ð'”~£H;|d¥ó»­Ú*étö\ÀÀ ß\´ÜÔÜ®îa_Òÿý0þñæûB#àâ ›¨ôá5…ÌýØÆ isxÿKg÷å³P±<ô«y Ä" êpÂl?ÈÚõ‡+Üäö9&‹`v†ý €ib ±ìp Ã;…Ù~­· „xÏ`À\Á~± 0xØÉ íøâ¢‰PýŸ{÷c±¸mÔR;šHÊ).¹ñ‹Ð0¶9¾<(ÉE(Ã:Ƙß!\sàïøásO éH’bcJ TàÇcH*†³¶¬1¤DÌ ^0Q-#µV¾p®†TNÖ!]Hã`(xyíM($Ô–šaƒÃ„$XC²U»@#€ 3ŽóÆ€lH˜ BÛ@ƒúo ¤&ÊÕ~`J†d«úkó1'cýG›FKÇLáj…÷*.Êx[·”†±ëæ´£±çœáœS1TsÑ2TsîW÷( {!p©ø`¯œU> …€B !àr*T6DŸÂ !E …€BÀ™p9†êLੲ( -Jå×¢áÂǘü’ë®Q L8(r=°.WŠ\K§ ]¬ð-Ŭ?֒çMB±ÁµJºìÈó†ßðË~«æ¬™7|ÖÔo¸”ÄðŠ*iŽ$Z.PÀ¬,fÄÝÄ5Ñ3ÜÉ2ÆæÒß*ÔEPkÉ÷Ö߉Yv¸3½Ç®T/rô{séáþ[øj¼*à’w´ÄضùòjÓnéØXyð s.ùm)-\Ç2àó<Ð…’à*„ˆøŽœ ×þþ„Ÿ;ŸþõØCþÀð…†+UG"—g¨h,DÐÁrÐg92¼%ŒRž5hyxÛ_XÿÛƒ/êŸÿó†ˆ5)×ÿ[Ò á‹ØˆŠhC`ÎðKE˜µ?óZwK Ë5ÛºÂÒle" qvËËxAÐTFsin4±ê ~ÅØõ}ý+·Ð‡Ñàg ¡“µè·LŸ"¢a!&¯'÷H¾¨úñ½×_-bL òÒ:Â儯0BW"^|ˆòj0¬JÄâ’[¯š*žÜã5( ¯úZ-ú”(¬ÈBŸ4$¼;¥ˆ~ÞªýÅ2ðÒo¼X¥#·ÌÐ&‹hïx‰¿çvñÍÏg µJF(¿—%ƒÄ¸hŽ¥[Ž|pX–¡þA'ŒÕÁD?â¨ýxi_xâ÷‚‘~>ç‘BùÍ_±F¬Ådž|’ÐIªl wZs™)ž5L«U°"æ¡Ûn[Å  ‚9äx¡xYFñòÝáyhÒe#ø%‰L /ß)fæ 0h¨œyàq Ëze&qƒæîÛ¶ï þcx_s8 ‚f€i€°²0E(K™)ž3Ñ–Ë#%!°öd5÷ôC÷‰SØÙ´–€`õÎÿ{ôŽpߟW/-¤(ÞÔ«Û°ÈãÊñºØœÜ›—î‚°J L„¶ƒB¨¼t^Ö c’AzÀ¨Ñßаu"÷c›ö¿?x¯È€=VXaÕÑ8Žÿ æ¥%Ä'@º€ú`°Â„‚X3ÀGß̓=úp¯¯GŒ0/lh8€™óS¿½›±‡>?v˜Çá ±6ÅÃ3ˆt‚™ ?že³U˜§@

ÀÄ Ô³§Ï † õÌÁGÀ0ÅL LNÖªãåÛ>tb›“– "¤ ö}j iÓób†—}×AÄ`õ剣‹’DD(D"’d*¶©ØML<™Â/>$àÉ9=0C½zâx™½þqØôŸ??ftÉì=×Í¡þ¡† ‚ó%¯‡ª=òMŸ0š =<ùÒúب†¦ô žw_{•x&5¥—e®.À KxÉqqbIs%·?ÔWHÞRBÅòèŒ5ëÅ>^x¸¶®–ûH¤ âÏ çrÐA½Ó¸_Žé›ú‡º€9áé&‚¯Y³SFä43i .>0ç@åÇ . Öä@ª ¥æ)-–ÆbÒõÑÃFˆ3l,Œ&úOg6H‚´/û½<§¾‰ÚC…TFø-öjˆ »¤ž?ù{½”©ÕÓãÒª§qŒÒc,b >&e 1Á‹@ªhø}–CÀI‚4‹Žª•Þä5|CýÁjøK~*¤ ØæZK‹Wo{Z=ÿø#BúƒÁ‚d.Ì!qº§ a‡%i™ÂQ°æð‹›.4HdGØ )H»;‚Ì*yk LÑÈþòÀÝ¢ 9Cja2é±»ocæR&b²bcº—žlŒïŠ{ÀL1@ʸ¸ðÁaÿs•¶ä]S‘¼08J¼ Š_öaDô!ú$eDizþñ‡³CðØmµZ“x@ófl°ø÷‡îgÁ\±5¶ëA_Ä@ÉŒS˜;¤o ¬µ;¢e †„Aäßÿ$Þ“?²ù ˜ìÙÑ¡Íaꑃ¤]hkŠš"Ðnl¨²ZáÑÙ¥ýaÉ þJû:Å?ßü°Ajt/“¹{±t…m ebLvAµÃyT®t¶oA•aGUcj¬¸ÈÿðòA¥9°¿°«Éó­ùFF90h@b9~*SLZ˜›V?¶³¨ù>ñGâ‚Ú2…Lq{/¶Ö†˜3•ŒËÒ<1ÂÌ¡ý À4Ěńè³9óEPáÅÁƒ*ÔYØqá ‚º¯Â9H ›Ù6˜ÆLTR› pN;»ß“¯Ã® FŠÜ‹–q´-ø>÷îÿÄ7¤?„¨+mØ>õ—yË´ñð~Ç 6I0…ÀÅ ª9<ПÑïP?ì‚[uO6Qæ‰.ØKALþûÉ—2 £ß¥Œ#ʃ6¿…$lÁL€ ua^!™¹¢¦\*¦5½î’¿°ô‹|"ÊŽQv,ì<¹œUp0Ô©c/Ó»aBâ¹w>¦çŸxXÌîÂþUu 3ë\AxqÁ@Ÿa³ÔAH ß~“¸fêf¹Ÿyý]¡ #äZkhÛ¬ ®c©/æÈ}…ß"TJs.Uo~1‹þþê;Bb¿rÜ(¡–›ó¬á=-á€Ý°U6¤w¹†ÄÐ0-s~c'QCÂöÄX~Œp}/ðn›•lÆÅÌ,`”—óäÚ Ûˆa‰($Òó rè ö‚ÀDÍ´qMgªaGņ‡i]™40p©î£,Óù9h {õmÁäÏWO¶]-“P1q‚49wéJ±C€´mâM 0Àg9ô`›5°Û-úú6< зÐ&U< yŸfþ@¥®*:,!¬½ÞFß/^Nà‰,b¨˜€‚Í[÷*R(Í!€íÓ1IžÑ‘È"†Š}ÃõîÙ‘ðQuU(Z‰&«áÔ‘È"†š•£¤S;öìG‹mб%±Ú›V<6éUõÚ±¶!+h²p§ìHdC-*-¥PÞ×^‘í€'Åÿ~•ÞûæzñýOéé×Þ¡ôã'mŸq 9¬Ú¼°¯½%ôê'_Ñßþû6Uñ>õ’jjkégž'ì)o -ß°…~\²Â’Gô÷Ó³~ÔÿV¶E3ÿðMïHd‘Û”rÚ·Oר°s-cÆñ×~C1‘á"Ó-{ö æúÜ¢Ð`Ç j g-XJ£ o‹Æb*¯¬¤EkÖÓ5ÇÛDM.•UU´y÷>ñÉÎÍ×\Q‡¶F £ñ ‹ª­ÁWéëX´z=]5aŒž™âìðþ}xÆ´Š**«ÄM³¨ù…E”Öµ3Ý>óJ^±@+˜×1ãËÈ /‘J9°¥¦$S\t$]9ö2Úwø¨¼dô;œ%>tÍãhÛ—A0KTUw¬•9¢âêŸÓ  T~§iŠÆ‚\Í3á[÷î§#'O‰“pSšÍ“?PÛ!Iöc)0mžyìŸ Ú“~D¨ÿâ‡ÿv±mÒ'–ÂN +“7‡ò†wA#¹‰I+0-”ÞÒf ;è?ßü'rooæRªv©úàÚ6qÀ¶Œ5âZ×±ž]ShÛ¾ƒ‚‘‚™~>w>{Gln&WuZ!`{L‹¶Ï_å`اMo}ùùúx‹Ùýdž™¿÷†«ÅÝD1³ÿÌïQr\1£yøö›Œ¤dú”§4ª™AÂ,€™xÐøáƒéËy iùúÍÔ)!N?ÛÁ·w·®ôGzþ‰‡Ùcà*úpÖZγì`¨SÙkβd”ÿò‘Ã8bÙz‘Ÿ©úÀÎûÚg_Ó3¯½Ë2s=IÓ£w¾žM{õma~HŒ¡;®ž./«o…€Ý°(Ú±?zþ»²£f5ÿ"ûM±]“O†‰­¼¼R¸W™«æË4>ùñgê–œH`Xå•"üš¼†oH¡ÈßßW§^k¯á~9i†{ •†ð‚ceÔ>×Òqsõ}!7WHÅpý2$Lt¤ÉÃðºúí8:ϸ´w:{•³º ¥Hƒ³?ÕŸ¶ìŸÒªM§¹ó¸G2S£ŒÒ¾‰ßm¡æêƒÉ4D„oŽ#muÞÞ(†joÄ$¿A½zRxh°“”FC!Ð>P µ}´£ÅµÐzXü°z@! 0Š€šå7 ‹:©P(,G@1TË1SO( £(†juR! PXŽ€b¨–c¦žP(FP Õ(,ê¤B@! °ÅP-ÇL=¡P(Œ" ªQXÔI…€B@!`9Š¡ZŽ™zB! PEÀ!ŽýX ŽMèö9*—–;ýN–X+àG1¼ÕGŸÔî"°±v ¦Qt]à¤+¶… ÀêrEl¯ýÛÞ aW†Š »6ïÚK?ñÖ½ºÅÓ”i”Æ5üí]ïVåWX\Fg.äÓŽCôìë[èºÉ—Ó.âÎqA]\½-\ oW(o{êߎÂÛn µ–£­àX•ØDî·Mâ ×nÔæ(ÌÍŒŸ>Ý8 s=ÿ¿ù"J>ö*òà !®Bí¡-\kW*g{éߎÄÜ.¢^à ¼;å–Lÿ~ÿ —d¦†”Nÿyì&Â>ñ¨êè ÔÛÂpwµ2ºjÿv4Î6g¨P- ‹Šiþ¯kééûfr¸9×PïÍiÔå1–¶þu¨£³ïAÞžÛœöR÷X†€«õoËjg›»mÎP ~妭a>ž’bÃmS ¦ ÓE¯® ¢Ž¨«3S{o gÆÞUËæJýÛ0¶)C…DTÁ·ÎÈ !½Rœ¡¾6)ÃÐ^y¦ QWg•R;J[ؤ;x¢®Ð¿¥‰lÊPa¯+ç3só‹Äl¾©J—ñVÇÎ\0u‹]¯aÛœ‚b³ò„§Bo˜‡º:«-Õ’¶0«Ò¸©°¤Œ2Îå4Émt&+¯É¹Öþ8•˃ÿyñ).­¸$ô‰}Ç2ÅÆ†Ú‹Ç3³y߯í©vuì ýÛY·)C­©©R[)û†˜¬óÉséÃ9«LÞ£½¸xã>úyõNí)«ç–pÚ;è½ïWš•&fGË*ªD]Qgg$KÚÂË2ígföõ¢MŠ»ïØ™&çZûcöÒ-4kéfZ¸n=÷¿ŸiÙf펯DÕ¼³êÊ­Å·6wf¯ ì¼"í©vuì ýÛY·©ÛÔÌ*Þ×½¾^·Ç»9•Æ–ÆïÎþ•wó gi$—fŽH©biñ†½´ëÐ)á!pÍ„A´a×–êhPÏ:|*‹v¦Ÿ¤èð`ºkÆhZ´~•2ƒ;ÅLÏ^3a0]d)yɆ}Bêœ2ª¯8ß{ž¡~©I4cÌ}ñ±”SKuD]Yå·´-,©¿£î­cÜ!9ž:ŸC+¶¤ÚºZª¨¬¡Û®!$Í"–4¯¼¬Ÿ`ÄÃzwávÍ£ü¢6Auaƹ›î¿v<ùx7¾SFö¥‘ýº‰Áúø™l*PIÍYMâ")‘50UPVN!Í_³Sì©%Ûý}/1:”Î],ä ËÉ´ŽûéÆ=G(>2Œnž2‚÷ïr÷:m›:{ÿÖ–Õ‘Ç6•PѲ³™[ÉÚÚ::r:‹g8¥ÄGÐr– ŠKiÁÚÝÌ\ñJ¥Þ¥³†ú3ìÕ%‚ý³¼íÊ‘‚±î9r†;s>]Ì+¤& £¥›tÏ/Z¿—Üx³·±ƒ{ÒÖý'XÚ9Ké'ÏÑÓFÑš‡Ä³²Œ—õïNcõ?ÍþF]-<ÌNØ 7¶¦-¬­Í“È/*¥ó9BCØsä4’&òܼ÷8å–ê%G˜JÊ*ixŸ.´yßqúø§5¼%vbfŠ¿Y´‰þðòWbOëÇ&œ:Â[ÅŒ49&B˜¥Ðο¬ÝÅ}Oç“,Õý/毣~Ý“È×Û›Ž²ù*Ë6Í.º…iVn!­Þ‘nsS7ýºó‹¶|óš³b½þÍVó“Žð>€}1†ÍŠœLLý´r‡øl=pÂdÁ`±ÚŽÁ6+§ˆÛ»ž0´÷h&õìK°™Òîç…6´qÏ1Ö†û„‡GÓþÔ3%Ž–oÙ/Ôþž|õõöb*Šþ÷Ójš×`ÛïÕ9žêÙ1¨g²¢Â‚ ³S¿ÛN3dÂþ9iDòbÓŒ±ÈÃ݃mQa4z@wŠ ¢é£Pú‰³tÙ€Tѧ+3Ñr^îéÎvÓP¡žCb¸uêH ã ¢Á,y„ëö«¿rt? féà†+†ÒæýÇ™yÖÑo›"T½[&§£l^˜1v ÈCÛ¶±,åŒl¹Ú¯MC[dÖXƱÉF’'÷‘nI1ÅÛaƒYo¸Ö›ý‚Y0K„½X*,`iô–©Ã)>*”µ™\úÍÌ1¬ý„ÑúÝG Þ%þ~>"ÉQÜ×`Bì߆q?òd-H×Yöà·ÇÐŒnœ4”ME‡Éß×›n˜8Œ¬øÑÃ7^N»Ÿƒ=lµ¾>^báÇÎôS4ŒûlZç‘¶ú×~p†L“™¡‚äœŠåšØLñ‘HÓF÷?§Fö•§/ùžÆ¦Iò…“¿ûvO$|ŒQ,ÛjñQä<`aˆ©Å!Ò~ Ûº$LDi).2TÿSö3yêº1’÷ æÚ0y‰ã©<¹©¥wf/bùÌÀ‡4˜ µâ£¨c à4 µcÀ­jÙžøýÍ“Ä$Gzd‰¹Ñ\Оë¬êÖÅP›â¡~uPN³)“Jx¢©-Ó•¢Ž‹€b¨·í;|Í1 àKðeÅDçɳÅL~§Øá;Ë|Iovêð€)ZD@1Ô!R7´Gf/ÛJçÙ_9—}U»%E³H mOÏ¿±œõ‘›&Ò+_.¦hVÝÿvïŒöª“ pZ·)ÔU%©Ð#°“™ç}׌£éct›¸€Uw¸uÏÒ»ñŠºžÁ÷QÌT˜:0%¡šƒ’º§Ý!·¸u»ÐIöK…³?èØ™,ÚÁŒ+ªà~çêŽøí®Ñ\ BJBuFRE´>wϸŒjªkØYçlŸÌvSøAŸeÿÑÇnÌ[Ýø)?dëÃÞîSTj»obUAcÀy&/'…Dš~â> í½ÚEÚóêX!Њ¡6‡Œ:ß!@l|)¬€Rù­¢JC! P0Š¡ªn P(¬„€b¨VR%£P(CU}@! PX —š”*çmM]Kz&åñ–#Lk-!Ž\áííÃáú‚ixZ¢çÇ!ØYŽ@›ÛÃAm.ƒØ¹ˆÄ_ÄñUËyËvæ‡ïi‡ì–KpârUL•3æé [/lÙ{Œf­ØIÙÑtÆ­+•úS¥ŸÎ!ÛR`}ê*( ¤ˆÒ·ž¥E›æÐ­ÓpÞGÿµŒ€5ÛÞmˆóØÕôóE[DÄþRŸH*«÷¥ òâÉ„z v»Hõ%äUS&ß=mÇ͵Ï`몘¶Ü[:ÖNÏP±ý1¶¢øec:íðI%mæSéîKøäQ4ÖRÕÂÕ”W\Îñ-ûpÐj×ÜDÍ^ÝÖÚía¯¶@¹¯ßG 6ì¡ýÃ(70–¨)ÔÇ«œ.ÝK;_ù–^xä:JG{Ž£ÓÙ…TZ^AÐhz%Gó*D|V/϶½F®Š©½úœ+åÓ¶ž`㚢£mÜuXlˆ¶)d U¹µN"5UÌBÚ´a ûyѨ=Sm0[·‡µÚ"¿¸„5Þˆ1#› xß°êêZÂf£9y´!dÕ¸y5SCÝéJw?Úë?œz»í §ßK5~átšây0ïJÕn>äYZEÛ÷fÓ¯{ÖR€GÝ7sa—·VØ \S“€©‹zœ–¡B*且ߝÚC;‚ÆÛ„™JÀ¨·û$¯UùÅH¤P^E£Ô‰ŽîÛ^íÑ–¶@³6³pã>ÊõJ ,·8ªä¶­qó$¿š* i‘™jk}Ào0…zu¢ÏHíiqŒs'¨×äSùì”Ö%‰~Óx‹cWÀô’Š«&pZ£a5¯³^µ=]ØL!¹ØšJÙ”û,òDÞŠš"`ÏöhM[`£ÅOÙHs7¥õþWÐ^ïþ”íO…žá„ôrø¸Â# i¥ÌøeŒ™j+ò佩XêÝu¦˜>ž¿É¢¾ãì˜j멎ÍCÀ)*Fî Þzç±,ÊôhÜȼ*µþ®L÷DÚyô¼ÈeP¤CÀíaI[€™®Þq„6:O[ýǵܞmWË&„íþ—Ñ6î¯kv¤‹]t[ÊßÙ1m©üêºqœ’¡ÖÖÖQ9ÿ¡ò[cÊxÕ/= I¦ ¤Tä2(Ò!àˆö0·-À˜ò8 ô+¶Ò¿©ôÖlßZ6+ìõL¿l8Èý§’w]5ÝœSkâÒÑÒrJ† ‰£¢¢’Gúj¶úÙ­M0ã\Ëê¾.o¥öKàÑæ¶EUU5-Ú°—²|»P™‡nëpYn{—x„RE-Ñ©s¹ïò rfLM[]j§d¨Ý«ªª¸è:í·PYó.׋¼[’0ÌK«}Üå¸ö0Ý(LCÇÎQ®G„S€]̓rN~!ÛR«M–ÇY15Yhu±Eœ’¡ÂÛ‘ y£ Št8²=Lµ…^m†™Æ](Ú‘mæÆ}Æ»ªü¼Ü©†]þLõ!gÅÔ‘øµ‡¼–¡:’Ÿ!oS/C{hxKê,Õ¦Ú>œ••U<øòìæø®Ü­òyñV?Ïû³bjI¿P÷^Š€ã{á¥e²ø ûãÓÓ)&×cû¹ÑÌToº­¯Hçé1¼•EŠiGn‹3T˜Dàãüé²$/êæA ó¥ß ö¥¤`º¹·Ý?Ðz‹3 ½ÂèçCAìj)%»Ó‡3‚(Ä×äKOŒô§ÑÉ^bÕ{ÓEùÍM3®Š]ÿ+ŽÓ„´(òôð$wfð­qôo.¿Û¹?ß3@×§C¹¼/^@ýb­isù«óæ!Ð.ê}y=6Ï!](©§{ùÐý|¨o´nÍ‚£UôâËý̓OÝeˆÀÈDOf¦ž´)³šâCëNWÓѼZzeR-9Îç†úRl u»ÒèÚ,â´øûOÌ@·Ÿ«¦´HÊ-¯§/öT0ƒ $/7Úp¦šþr™‹iÈëՅΆv¡¾%éÉÙèp€/Ý2€èŸã™ò«è«JEр館rsŠI=#ÜéHn-]äþ;¡³/©å2êbN´S³2V7YŒ@»`¨)¡´ô8¼ŒSQe=¥„šÏP§¢Îšƒ@ «ùùåÌòùËý™zÒÝóŠÅãÅh‹0ƒFÙRº+î ¡  K]¢ÈÆ#Žýï†X:x¤‚¶žÚC~u¥”éÝ…ƒß˜v·cS'%‡¸Ê*­ª£÷¶–ÓK,ICâ-æß‘þîÀÒ*lKTß°Ž_À([²”^»Žë™ SÑå³7öòf J7*{QæÙ zó´¼Úü7úwQ•® íOïp9#Ø´Õ/Ö“«u°ÓæsSWÚ‚@»`¨…µÐüKéïF…/yK€-¸5„B¾ÄÍ=ûà ŸÓã·_O‰‰‰üœãgœ ËY\\L™™™ôú7s胧ï1¼lò·þåŸcò6qñTA-ueutwêæ—ûçSYCˆbfuŠÛ˵ÚÏ×—ÂB¹½xèŽë•}œâ*O‰å ¦ò€Y„~ãFS»úR'fZÏ­-¥gÆú3#õ ªÚzÊ-«3‹™jóc-õ £ã§/PBt„EkûµéhW”¿Ž9ïÍ®¡ñ)ÞîçNýbt8[Sm¾êØr¬gè±N-¡º³ƒ´wý¥ÞüêYv'ö7rW[ 4 š=Ûܶ€tŠ˜E%TK—JoÍVÄFz•m§p÷RJMŒfofª-1TgÃÔFÐt˜d’¡¢ Køk›ÚálÙ2œ—Ÿ7^Þnÿ Ñæ´*Ö̇zS`u.o´w©g¯&Œ¯:IQUgihr …„S``€R›ËßQ˜VÖyü]Tÿn®eÚvÞi*fp“B½)¾úLÛjhÁÓñ5§))Ì[8Œ«×°°w{˜Û`ªÁìRàIÊW6Ú^GlH-ÛCÝ+Р8^NáaàïÇ‹š—šidåi:V@ô—¼ ¡uMÙöBÛéóqJ†Š—×›·ïíN‘Õì"¥b÷Sä…<‘7Ê H‡€½ÛÃܶSºyo/oÞ9œyCè±…¿]‰YMȳñ–R÷ïºJ‘G|åIU¼„RêNÑÀh^ E1±ÑÂË××ǤèLcyyn‰{0*p§[*Ëk-­¿ºß4NÉ5°œ244zFzÓ€’õlKµtrÉtŵW‘öàâÕÔ#ÒK䉼QE:ìÙ–´*ÖÍ&6¢S Ex×Ò¢ÕÔ§l+…Öä—fÉ*jƒ{‘Õçi·÷àâ5f3ïÉ•å@IDAT_<]•Iƒø¹EËhhÉ*êW½—ºù—Ó xJNŒ¥„„8ŠŽŠ À€€û#0M‹ò¦—¦GÓ¨*¨t§ßÎ/áUW‰: <Û#Ù…k ã[Bb ;l‡†„P÷¤*,?O…KÅî§ÖÞ°6Z0ìø OJMŠyÂYÜÒà–ÖÑ<¬yokÊi¯öhM[x³ÍËRÁȪy‚ õË+(¢óE™Y–ÍîPõìJå%&­Ü˜u€a{ð=aîåTÏËJ‡ÿJ»Fóf~ͨöª¯¤”ŠÃ‰´³%ÛÙ=…}Ô×ÇO0s¨øÑ1QÅ‹ B‚ƒYÃi9º™£0MŒ ¥·ûGÒ·‡=èm•ôî¶rJÏ©¡W'r Œæ{ZkúMó©µß+& ´^¥ýY….(.¥P–$Ì!¨C¾>>Bê()¤Þ¼¿”癋D%ëx÷ÊX:ï$öšjíö(ðÀËWu†¥•,J¬¥IüBDEêÔ5ÎÛ•¿°¸Œ'ÐZ~‰Ì©»­ï±´-P[¶G[Ûëùüý¹í"3õæ¶ Ì§Ð"^îZ^Î^5TÎÑóá òçàÏ>ÜV¼â©”°+Wé±ö?Ç3–*Üý9¶ªK²u„‰±`6 Ô³oi=u‰t§ÐÀògû(]äÈK_9XKh°ø ‰ÙæãhLêMã¼Åª«•¼€á.޵ð!‡ÀÄ‚ Cr¥þmXv{ÿ¶)CEÇÂ'' Î\àNn&CB•ã™R!y4l'š“Gç NSLÙyª­C÷vV׃<Üë)̳šâY ЉŒ¢¸¸‘W˜µ Ô-ˆcrÊúÚ»ÍÉO–­5môm×mo ]Ù…=ÌÌ­°°ˆJKËĆ‹ˆ™ŠÉ+`ÿPoòb“N5ŸcÆÁm~I1K EÒƒgÁÙ6Ëÿ}1þBíßy®Ì²‡ÛáÝŠ¡¶ÃFUURØ¡IºM7·g–Ú3[§ÌK1T§lU(…€ë t<·Òu m£’*†j#`U² Ž‚@„¿. h~¹rR µ£ôzUO…€÷×mž_^k£\'YÅP]§­TIN‰@˜ŸNBÍ+SªC"ö—WTÒê-Ûiÿ‘£t!7ŠKËns>§ì¹f {øQLD8õIíNã‡!?_3žtþ[T¿±]µ¥ßðb)Aµ¼½zG'»2TlÆ·y×^úiÙ*êÕ-ž¦ŒH£¤˜0 Òµ;zcX«þ…ÅetæB>í8”AϾ¾…®›|9ØÏå6”x¨~#‘°íwkûTõÃ$UÛ–Ò¹S·C­­­¥6óg ýá¶I”­[aáÜð¸fé0@áÓ§[e^È£çÿ7Ÿµ€RšxÙ—ÙîZ"¯úDÂöß­í7RÕ—ª¿íKê¼9ØÅ†Š—bÃö]4‡%Ó¿ß?C1S;ö‡Ä˜púÏc7ÑrÈÐh W!Õo×R–ô›ÜÛ©œœr\©Ÿ³Í*Ե¢bšÿëZzú¾™¨Ô{{7;0Œµ‚Ÿ]#Úmâì¤úã[ÈÜ~ƒe§ äPoÇÚÁ%°9C­®®¦•›¶RZ×xJŠ wpu;nö0±ôêš Úmâì¤ús´9ýF®ÂºþŽN6e¨2***èpF é•Òѱvxý‡öêL‡Ofˆ6qf)Uõ‡w•&0ÕojXÙÙÕ°†p‚b¨6e¨°•——Sn~‘˜ÍoÒJþ8}>—ö=Ã6@ºšWLg³óõ©œ<{‘ KtÁNgåÒÎC§¨¢²©$vîb>3÷óŸSÖÝ[e(¯¨Ò—©¨¤\Ÿ&‡ŒÑ™¬<Ê)hº…D&ÏÐ˺{¦µçàQ‘SP(ÚÄ™m©æô›zvÑI?yŽŽ¹ ‡ížÛ€%®£­+«j¨ŽãuâøÀñL}ÿ‘¡Égä9[—–WÒ‘Vö=<‡ºà»ªúR{8êºïX¦¨³¬GMM-9Ýú¾nªßìÍ*£òê:êáKˆÚÑɦ µ¦¦FHC¥ìw$ æÅO~¡笢å›ПߘM'˜qýyÖ0΃ü¿ÿãJZ´a/}2o­˜m—‚Ìwß±3”q.Gþßs~Ý&^Š&'­ð³¸eÌð¡5 mœ•Zê7y€þë[?мU;é›E›è™wç_æ-ûŽÓ+¶‰jÏÌØ{xð^ò_.¤¥›öÓ2þüç‹…MªýËÚ]´qϱ&çä0¦wú˜<×–ïO~ZCÛ¹_æ–ržG[•Ô[³–ÓO\ïùkvÑÓïþxI?©fæ¹rëA·¤4^ûj‰üiñ·©~óó‘Þ¸.A§Û°©ÛT·ªªª69íoÜ{”¥µrú÷£7²¥}µ`ƒ`„¿»~<}µp¿X@çs Ù‘=„â¢Béàɳ4ed_ºl@ªè´xÖÏWg,¿jÜ@ºŠ[ññÿ~K7NÊRs8ý÷ËÅôøSXz9+Fý¡½;ÓŠ-9Y-K¸5tÛ•#…á9/OZ´~¯`†W K£a}ºíñ‘¡ôøíS(·°„ž~çG‘ÖªmÅN‘%ehü©Ž¥)/à—;‹ëRTª3òËsòŠÄ}(& ®ßM…ÅåtýÄ¡Ô%!Êh¾Í„ä†6qv•ßT¿Y²aaúÑ[&Šj¾ðÉ|ÁD†°Iƒe-÷»Ì ¤&³WC§ÏçÑ_î™.¼KVmOç·’üýš.vØzà;}šŠ…÷ÌMË™ŸÉΣéäëí)t´ûmWŽ$/OOÑ^Ú¶A_;Ë÷ŸÍ. ;¦¤%÷Q&KÀã÷¤¨° î›çè,÷W”²f¶uÿ Qÿ _Ñ§ÑÆ_±¦6°g'=C-æþ3‹5¸)#û )ù 3c”Òî-ÌHÑÿVïH—Ù\òmØo*¤’ªZÂdT—ðæß³KjÇ'lÎPÛúâ³D¦µ•žc¦'GÜÁÌD÷33ÝÏ †sP w¨¹ž½y"wZ½ýPóÍçÖÀTø R¤ðJMŽ¥¸È*i`”†ç ¦CzY»óõå/‡1êEo=u½ö§[Å@pðÄ9qÛˆ¾ÝÄË Ÿ9ÅŒ³ð¤7ÄY6ôíž($”èð`q+^Äýl&À@ÐÚÅh“¶¶‹,·-¾[*˜¤@I8i0) NK¡õ,qå•QZ—8aGïÁLçÕ'nRãf<è3ÆL/%>’‚D{úxy‘kEEeå<˜•‰¶.å×µ ÒLã¼FöëÆRž'­a©ƒzye/¨p‹*|Y[9| }º%RÆùFÓÏà´ÎÜ"õýO>ƒï¿þf:½ó—;™Y§²cŸ¸H‡÷&–¤A0³Á6æ>´wqsÐpôHflÄ9ôcôýÅ,õcɲ—»92l—¯wåŠ[oê§¼w$fÍ£'ïpð÷pîPñ>ž»Z¨M«w*ŠÉá«ë…º/ìGx¡ÐA¡Ç2SlŽ‚üÉÓÃC¨E{Žž¦ ¿æn½ä|?ft»xâ j[xpà%÷àD^q©0Qq¼ص ñ€ð¢j jT/w7w^ݤ›lëÕ%ž~f顸AzÆýýY…é#’UÄ^bÚ *ôÛß­ Ù¬êB¥?žy‘M8Ãd_úlf“‡;Ë Ì¿>›¿ž5˜NÉØ—WVrѵ!vXÏ®%<­’Y|T3¾H–p˘¡ú‹4 ÛÏ‚q‚¶°jÝ«s÷¯ÆW éb†„Áöÿe,…ç²O×DqÿÜä`¯?Óxõ>€ë~ðD£ áŽÄ5À’9ÜùkvS ÷=P—Ä(®O¹0›yƒ¹÷êOõ<‘5¨g24gÅ;¦I¦ÙÃù¬êÊ.§¸`oº²GóïX³ ´Ó ÿd2·n¿¬\KW]>ÖÜÛ…­®¨¨ˆ6ïK§c˜ýœöF_oºŒÕ¦ ¶ûaÆö&~qÒ¸€<¹ób² #nT£JëœÀ*U«Òù4ah/¶£uâΩMQwÜÕDtÊA,Ñ@õ£xÛ;aWõgµ1…%K<‡tñžĪ”3ãœüb‚JÉØõtH¸WGnBRá œ—¯0Àö‡„ÑÙåD^V0JØçJÊ*hD¿®ÂFØ‹ë‚k^,5@%ƒ£(à™êÄa½té_Z½fÏ,X·›%¨^L>>Ω¦Á~jªßD„²Ô•BÙ<9üv¶WÆ4HðÐ^<Ç‘Œ!$.wƺ_÷dÊçÁÌðÚ C¶Z€â¹-"9Mh!±Ü@ɱÔ‰M4¼¼YR30UØbqhRLÄ%mËvüð@1ˆ£Ýΰ†Ñ•™XLxˆÐ:’¢#„-½[R rÙÀP!AfœÍafMSGõmdÝ©G§8Qöà_Qmy!ý‚ùb³ÏåÜÇÁLÑ—!]£«»ó{уèþü^œã2ÀL›kÏ”x‚_Áïlµe<¸À$Ñ3%–=²¨÷Á^,¹¢ß’¶ß¸yzÓ#óNSqe-={y<õŽi^±”gæëj¿ÝXŒ7Û˜öÀ3ÏÓGÏ?cv‹‹‹)33“^ÿf}ðô=f?§nÔ!ÉwÖÒ-TI¯¬9O=£ühÞÝÝo. KyFsé¸ÊùF}ÄUJÜÊ9 G'a?…tÍRޱÙׇSUÕÛýçù‡¯£sìiϸCYJçŠkèýMÙâ±?‹5ÉL-M»=ÜßÔðÒjÔŠ:ì9r©/¨a207À¨ooÂ,5ÌŠ™Úù–óCÛcIvYS„EpW2Fð(°'ÁTsSk˜)TÙ¬¼(fö§²ÝttŠñy{ÖÇÙòR •[+n°X ƒgÚAðÿÄ ©Ö»àûô}Γ˜Í—/î+JVN¡p}BXPD^êŸk#€Åè'²¯ÀßnprÙõMOnÂæ ¥<¾ ¨ß˜{¬Žvgñ¤^ ýk’ÎKÀ‰‹ë¢)•¿ö<Ó w˜pþ`æ+© ÖÁ‰Q¸PÁçU^uòæŸo+o0q±õÀI±Ô3Îð-Äî+Š:X±UQUM£yâ _Ûd°Ïj_½7üŠá+E(»œž˜lÕ¿›ð 3–”ΊZ±GÍ:ªs-|qj"…ø^êüï¬e·g¹Cm@{*ÏÔÃ7ôÏoÌ ³¤`¦¯|±HÌ&ß5}4Eð ®Ö÷S+^Å^ @±´«¨c!€•OpÖÿyõ.ö gï’4;¨;÷¥ïx¡¯v¤dôÀÔ‡gÒ?úiµ˜u‡›Uo»”þF':È)ã•gà Pnê¬T}m£j8°£ÂÝ*‘W¨€° ôä]ÓÄ7\« îã©MûŽ §íN쪒þ‡â‡úסÀb¶õèݱà\¥©/?v‹ÀþÊ’„olƒ[Ü“°ì&hGÎH•5õôð¢Rªt÷£´07z|„ZbjªCetàP]È+aòy6ýþkÇ1Ó¬Ò3Té“­ó?Ì-(Áš·±š?…ýÁ€±8¦8JkW˜^]kŒæå©pÜÄÒã±,}ž8›­÷C–}'.ŠßÝRÈÏýÛøŠõý@àŽi£„¿ª³2Ô¿þZJû.Ô_])ýmHyq°EÍ# *cƒ½—´„έUíq LKû$!h…$¹TÔØsòõÝ>@-Á1Þ°`-çÕ^ }ÕÑÞë¨ãÿn,§%Ǫ)ÐÛÒr¶P°÷dGÅeòU³ü.ÓTª  û!ðÆærúdWy2‡xcjù×5Ùk¿’¸VNŠ¡ºV{©Ò*lŽÀ[[ÊéÃíþµÉtYRc@›gîâ(†êâ ¨Š¯°&oo© ÷·70Ó)4©«b¦–à«l¨– ¥îU´cþ³¡Œ>ß])$ÓÿN  ÉŠ™ZÜÚŠ¡Z ™z@!о¨âÈýO­(¥¥<åÅѵ^žìOS»Êˆií«®¶®K1Tl€·†ã¡îI?DÙeTÆûá™+K%\Yü¹¯D‡úSÿ´žÂ}En‘bM°eY჈0s™¦]ÐR^Xsm7?Q×Ôål©íáz[ÛØ«=dY·¤gòæE“ãXÒɹßx{ûð–*Á4<-±Å~SXQG/,¥Y5äãFï\HÃ\Š-8Uu äà{ç¬ØHQî‡(Öm;õð>O¾>­›y¬¨âàÓq´gËÞ7¨'Ý0q ç—†Az[ÓR²¬?¬ØÎ›¼9v%bd¶ÆñÛa”ñ’FÕ^Ê‘Ùoâ=¤¬UÎÖÔÍÕž±f[ î¶lYÖY+vR¶G4qëJ%>ÁTÉqt-%Ÿº ,)¢ô­giѦ9tëÄÁFûÍ©Â:zhA1,¨£¸@wúøª@ê®–”ZŠ·ö~§g¨ØRxùÆ]´lãNáû1»_Ж¿UǾnÅäëYL1t„ ëb aѤQEÐèV%Ê¡¬Ø’bñ†ý VË´6-<&ì‡;„W×xq97v°Ä:q·VÔ<Ön äd«ö@Y—lÜO¿lL§í~#©ÄÃøŽÍ×¶é•Jw_^ÙäK¹MAµ…Tµp5ïQάûèûͲãÕô÷•¥TZUO=#=èà樛"iù/§Fm#oT7oí~ë÷†U˜©!D!îY45èeZºa§È y¶†dYæ-'¢"‚ÚÌL Ë€¥Œ 1a‚Y“Ö–Ó0ÝöøÛÖm̬Õ²¬b;ë€qmf¦†í‰ &C¦Ð/ÒEÿ®ä(W/­/£Ç–”fze7oúæº ÅL kåo§e¨P yó¼U[hBÀûäëVÒÊ*¶üÒÉÒïOœòDÞ–,ëÜU»Äb½¶% ˜y/Ò ç­:æpt£Ö”ÓÌl\ú6{µ@jk{Ȳ~¿jmOUn–«÷æ4ÒÝæ?’¾[¹‡î˜}¾Ø].&ŸžãG¯ñl¾¿—ZNjŽæÜã´ µšCáaõH÷t ñ8oN]ÚtO°Ç¶Ï¦‹<‘·%$ËêÅ6S[‚FúÞ3ØXZNKêäª÷Ú³-€Q[ÚC–õÛLKX’´%•²á¬[å>B±þu,•ÒýlÃÀmYgOÛ)*Fî Ž‚~ðØ1Šsßi7 ã9¯ƒG‰¼QsH–uï)·ð5繶܃HXû8¾&02·œmÉÏUžuD[›Ö´‡,ëÎcYtÖãÒõÿ¶Àü¢W%{æÑ—3¼©w”S¾ú¶¨¶]ÓtJT1›Z^^ÁñE+í"JÄ! çWˆ¼QsH–»jb7J{7ïê‰ü€‘¹å´G¹‡#ÚunM{Ȳ°‰©­“PæâŽ|ü‰Õýú*ÕoÌÍÂûœ’¡ÖÔÔPÇ­¨ánž‘·aö¿²ÆCä2˜C²¬•l&°•íÔ°˜mÆä02·œ†i´Çߎh àØšöe­­©¦J·æ·a¶f;aæ¿–û©ê7ÖDµiZNÉP¡aov³÷·nZ§6ýBžÈÛ\UZ–µM™¶òaKÊÙÊ,\ê1G¶€²¤=Ëjï^^oQ9]ª8Aa’¡bE‘¹ Í"osW59²¬–”Ó89[šŽl `aI{8²¬–”ÓÙÚØÙËã´ Õ’ÕvÖy[ÂPUVKÊimŒœ1=´™£ÚxXÒŽ,«%åtÆvvæ2ÙgÅÆxxùÑ€©ïÓU%ßÀ8ŠL¹œj*ò)óàÔ}Ä“”sz ]ÌXiãR˜—„¥Ο?2ú^ñª¥IÚìþé£Pú‰ó´#=ƒV³/iiYøù[\VIÃût±YÞ*á¦LÚ‹NŸÏ¥Ô¤ê’M›ö§¬ÜBÓáäÙ‹M¶¼iú¤}…øºÓS£üháÑjŸâEGské›}•4ÿH-æ%¤ õ¥X^‹¯Èñ´ 5(ª7åœZ-Ð<{d.¸qyxùÓÚ/ÇRyÑr÷ô&ŸÀª,i{€¶4üƒ|é之"ìÍŽm…?™·Fü>‘™M}»%™ÅGsWQph(ùøšï Ìòœ•«ÍÎÑ7¢¬/~ú‹ÙE¨¬¨ ¢‚¾ß¼ È±Á´aÏJëO Q¡ôàõèÜÅúfñF:ÎmqYÞ€Ï ¨g„;f&z±´Ž:…ºÓU©þäÇ«›ž[[FsÒ+y½ZJ‹ò ¬ó\ýœ Jí¶íbX éB5U,YxúR§>wÒÁUO³Š¿Š:õ¿W4\Me„tvx#bgT¸;Iêê?$¤¢Òrq ª~«ŠlÂñaSE`^SSKòŠéµ¯—P$/íMˆ ãp‹Uàïc7ßbS5îêAÅÄôöÖ šñ]ý‹™é-½u1K‹*ë©sˆ –c C{]kjiáIòñ!Oï@fª>tñÌòïJþA:iÏÛ?’J 3ì…i³ùä±7bœÂ_µ–= z¦ÄѶ×I äPmy¼•µ¹ôÀu(1!–‚‚Í}„|ászüöë)11‘Ÿ 2û9{ÞX\\L™™™ôú7s胧ï1;ëââÊ<›EoÎYßâ3˜Ê/.¥@ÞÒ9+¯°smŸ«­Ã§ŽÏû‰­¡«,\†ÜbÆ­¸!£°–¢˜¹ƒ®îáE³À­¯žÊ\¥£Ý÷(r<íBB-º°—üBR¨ª,‡'¢fÓ˜ÛVRBÏëéô¾¯Ä$UME«ûYGÒP¯p öe‰ ¢l–Œ$áü¹œ|ùS}Ûó9­átV.°Ùä±['SŸƒ5ŒÛâ|N¡KðÿÙûÀ¶ªsÿŸ%Y’myïgdaSF¡ìöQxmÝ{<àuüén™¥›²J– „$dï½í$Þ{ȶd-ÿ¿ïÈ×–mY–dMçœäú^{î¿sîw¿uÎñ.û#µv&99Ð -]xñ¾º /t¢FÑ=N#Cøjí¹5˜uýŸšG>ù¾àTm§u¶pê=¨>»*üHw×àÄù*Œ%HCsžü×Ç}ê5&?m;Ü'Nþ'ÎUc|Q¶Xáù÷6Ò‚åZZ¶ 0&? '©¯"!´‘¸°Æ†ùy¼rÈŒ7KÌ  @;—ÝiY«µ¤_•!üŒµ½é,ʈK*ˆ*Ä”ÄêSpjçïÂtw ¶ú‡N99êþñòwxˆh5& ³ùÃQfxº^–„zqçP—(‡ªMPujZÆ6Ô UàÍû´ê\€7åki(¶‡"ðÒo±Tž îe_p †Ó*µº.§ÛœûÖ.–7ïSɽȨ›œ"ò­dýGmkÛbÏuSíàDqY<Ëæ:x”ºÆëh:`ˆ\l,ä7™@åùROoÚíiÂÑŒ™?ý¡ÔUOýh°·†z.'Ž&—Èq<¸#– ò–Ι)©¨°Í ^ëûå\Nee¦¦‰©‡¾T®kNJL´>i(‚‰fqy\®·õ E½Â]cê¾à6ûÓJ]‹R´Èµ†Æø•k»€ÂT­7A¨IPù¥Ðjµ˜˜—†Û$´:²ƒ3kæN¹,.“Ëæ:x\ëÚi¶ÒvÏÞ-LíMÞîÒXh¦—ãk=Ýå5ÒâBÝŒŸ¿ýáZ×LkMйTm'ÍåÈqÜQïÕf|å¢4äV¤×ëBÓÇfÐ|ëöaîò~6¯Õå¼×·?Œ1‰¢L.›ëàMp­ë¸Œ8Ô5´M—Ê:Zv<›¡÷¹žJ[|í å¹pœ}­k(û‚ñN¸ÖuR†³Û¶@ÛåtÔ4Öœï<ãL̈½(ÆM ñó%¿ÔxâøšišŸ··”ˆ£i˜)ÉÉ_˜Žlƒ«ŒßŠ>•¹ßõm_Ca¢ƒVÊerÙ\oBÿºæÒ4ÀŠš&Á¹xó¼·i˜ó­¦…; U~Õ“Ëi¡YZq:ïñ¶^ÁL7Üq¬¾à6·?ú›BC–´|Lœj`ýYoº°uò5͸ æ˜*oïØ°¡rä>s|hr™”Dïþ`qH¯Ó!55mí˜H›Ñ¡¬[ÚþÙš(ˆ= 6ïów¿)ö`¿Ü:‹Äü‰È5Äbba&233D™\6×Á›Ð¿®“¨®1T׊†fâ²µˆ§¥ùxó>ö›bˆ ]&Ú_ËlA>1é‰èûSOn ÷A"ÕGéoÚŽ4Jý†;nÙŒC û£ÿ¸™JãFCãm›Q›ƒjm¡Ø¼ÏŸý¦Øk€ iŽ¥ŒÄüjì~onw´Œ®k¸CÐ *œ4C9­Ÿ¡¥é ¼no,¡Dz.+3öŸw.¸œRßˆÊæÉ8l ‹C «X¦Í;k|oÁ]ô”Z•q´¬ßäL-²3Ò››-Êâ2¹l_Âàu5ÁØÒ «#¦gf‹/ùª©i±ª.È}llê¹óÈiê Z@†ú„‰V¤®[àÇÍðú‚± t >n. »£Š@a¯h;|é)ž@5á×…4ybÜdk|G˸‰„ñìåð±ÆüRãRQ>>Þ}µMÈÏJõ*~–u™i©)"=ëœXO6´Çf¢Æ¬äÿgw»UÉñÍ?ÏLºìÿ”Å/«Z­¥zÅ÷'8Ræø˜psY\&—íKN]+ÇYOµÀ/õ,«nÄ‘Så¸vþLŸŒnƒÕ/˜ñá7žÚèþಂ7nhkk1¾ ÃßÑ4n<õ_¨î• :ïz¤¦$£8#O¾² }é"ŠÎÕ–†j¤šœãi5x:É»JMmAécÍÎ-”yš åÌ×uÉü´´]¯+¿ø,êEùHÁzZV-0gÊÄ”Ëô'ø[×þe£ž-møùó+09/Sô…žVVb "5„kܸÃ#ýáZŽ7®hDÿuPß*,qĦЪòFЂ§ñƒ§ÿGºY¬?é |Î<œ€9T&~¼¯¸²eo7=í“Uã>çÏ)“'ö‰çLWˆ (çÉ:S¿8~8ÁŸºV^ êÉRÈF¥%Š>à¾à>áºFj׸ñ„G úÃ]rܸC%:ã‚JP" ÁTdeea‰ê]ö2üþ啘6¾ §EavêÆ*'‡ªDÅ_Y¼y+\>Ü…Œ5ÇÝmA89Oæ„ØÚ:\BêZˆ¯uu}¶ÿ5çåO=Ù£‚ ;Ÿ!1¿ Ù :L$µ ÷÷s¨lsÿz÷7×ëêq3T½ýí¡òåûœ·Ž–ä»7cÜSžþÖ3ÚÇ'LBu/¨•+V†ç—¹Ç¸T[‡ó¥å8u®‚¶q “¦RºŠìÃmü­Iξ÷Ì›ÃÍ*êžgU§QCKúkbß'f&#;+yyy‚ ò*ýÜ'‘Â1n"“`Öo¤Œ›`bäMÞA'¨ ·‘––&êÃ\!‹œÉIMèèè¢û`Æ%oà6M÷ŠfS2‚7Àm¹É/‹l ŒïáòøƒÆ}éÜ©aXÆRøEx)ã&Ü]t‚Ê äœ_n§XCÆ%â’šiwJÞ;ÈLóÒY„W8Tå<,`º—å}“.–À/>+FÆ™u¦,:ó5Sî‹h !7ÑLë9ÇMáñ9«T®¿Ì™**¿äLL{KÝKûÜ„”vÔ©S§¼9Âcøás¨L@s>³ÍñÑB9n¢ ›@Öw¤›@bãK^!#¨\)…Cå—›9Ö¡ŒK¾4Ä5mi÷‹‰Cum?ã̇ÓèÆNÞÑGHû·‡Ýæ‚=n\˼¯GÚ¸ G†” * T:Ž_`se9„jÜŒÄdKB@t³.¡FK–'H< ªpä-‰€D@"à ’ ú‚–L+H< ªpä-‰€D@"à ’ ú‚–L+H< ªpä-‰€D@"à ’ ú‚–L+H< ªpä-‰€D@"à ’ ú‚–L+H< ªpä-‰€D@"à ’ ú‚–L+H< ªpä-‰€D@"à ’ ú‚–L+H< ªpä-‰€D@"à ’ ú‚–L+H< ªpä-‰€D@"à ’ ú‚–L+H< –û=ÔÇã-“¹vîÁÁ#ûQÓØ‚Kmîç‰Èw}|ÀMÞË.^ƒì´dÌœ6ËÎCœ^7 ŒH$¾"Õáp`ÇþCxsåJdªKƒÝ˜ ­‚^gôµ½"½¹+--¹8°u>Voš„ÏÜp#Ížõ{/ù†|H" OPív;½Møxãz,Òÿ Iªša7^c„^cD6N¢E•ƒßéBKk®Yº4ª¶Y62‰€D  D´•‰éÖ=ûñÎ'[±4î©€Óþè%«ªq]âoÁ沸L$‰€?D,Ae1¿¥Õˆw>þW$ü ú˜6ÚçÕ3œ÷"ÝßðöÇ«D™\¶ ‰€DÀW"– Z­V¬Û¾ ªcHVWùÚ.ŸÓ'©k©*erÙ2H$_ˆH‚Ê¢ÙlÆÑG«Úëk›üNŸGe=qX”-¹T¿a”J.Z"’ ²Ód2¡±¥=$Ü©ÒûÌ 7´vˆ²¥.UAEž%oˆH‚j³Ù—h¶Å Ž,ò¡ lýï´Æˆ²¹2H$_ˆH‚Êâ¶Åb[Ÿ}_ZçGZ.“Ë–"¿àÉG$9IP»húS8 —ÍuA" ø‚@ÄT_ ZröLhãÓEÛu YPiôâ:£èrÄĨ}ÁDS_Ë÷©™X" ‘D$AõiŽæå_ó,lFL^úS,¸íu\ñÀN$eNEJÞ<Œšù€¯YÊô‰€DÀgFA-œr7Ê·B¥Ö¡pÚ}ØñÖí8wàï(˜zªŽ¿Ñsöù€D@" ðAPSr碹zl–6XL(˜x2Н@BJ1Ú›K¡Öè OÌ÷™^" ø„Àˆ ¨zCìV5¼ û>|:C.:Û«Ñiª`Øè^œ!Ï'`db‰€D@"à+#‚ ¶ÔBBò(Ñö1s¿SëÒ& áü&aœÒÅg µþ¨¯ØÈô‰€DÀ'FAmªÜ CæÑð3{žF|Êhœ?ø*Ž¿‰ÄôIh­;Jl‡OÀÈĉ€DÀW"~=ToT}z%ÆÌû*‰úÙ0ÖCIÝ{+šñ9œÙólÏoy!H‚…Àˆ ¨]]vì$˾Ã1pºè‰m¿„¥½.XøÉ|%‰@MPiû'¯[øÝ_‰©/eº+OÆI$/­CÕ©0ÑþO¡ ¼×”V-WìÞ²‰ÀHC " j mMÊG‚^ƒ{nÈ0ç² T¦R~È –I$#ˆ%¨*• YiÙ¨°ÍÐåTVVZŽØý”‰ª ‰€DÀ"’ 21Õjµ˜X”Z¹=9²}i“_i™;岸L.›ë ƒD@" ðˆ¤z½©)É““ƒ­íÁÜeð¥]>¥å¼×·?,Êâ2¹l®ƒ ‰€DÀ|"¨¡âÚÔj5âââ’’‚ £ œ‚UÆïEŸÊÜïú¶¯£(E/Êâ2¹l®ƒ ‰ÀðÍ^-÷´OlXRBšF¤$×òÎ 8ÔÔTdeeaí/s® [[¾€,ÍqÄ{Mù»= {°ˆ_n%ÄüÜädL,.e¥R™\öÅ67¤dN'-Æ6$&Ä_TpøDPs²2PVUt‚Ê=‹D"ÜLP•mSjõ¨lŠÃáÎ °8b`íâêûj<ê‚ÚAÓP;ká0–aÒx ˆi^^ž(‹Ëä²eH†‡ÓŠÜÌŒáeâái^¾¦¡Õµè¤m‹øHIJBr¢£òs¡ƒÄ'‚:müXì;zÓ'ŒóÐÌÀÜR¸Ô´´4‘!ë4YONjBGG‡Ø÷‰w&õue}a½wèPuä?°wœEÛÉÐæ|ùù_—%¹ÓÀôŸÌE"°÷h ¦VX¬6|°v ¶ì݇iÚ¨ºÌˆí2Á“ŽN$£¹Ó€q£òpï-·!3-5dáA½|Á\<öäQQS‹ü쬠W’õ˜ñññBüÖét‚cmnn†‘Ôf³¼3©BP•ó`•RÜ øÌÄyÒä§Qüoh+_ãÉßãö!óæIw\uÆ`õ“ñ‘„s§GOžÁÝ7^Ðf11ýêO…BÝI\»qªV·ùwÆ% ºr2y¢ß}è³È!NùЉS8~ê8ZŒ-ÐmÉÎÈÆœ3QœŸm¬O¤Ðm™C„ȧÝèv:Šÿ|´|å!$‚gyw­1ošÇb?QéSù¬ìLêcõ…Ó>s¿ìÅÜh[õFœÞøuØÍuÐÆ¥bêUO#oêý®ÅËk‰€DÀZÚÚð½_=…/ÜyæÏ˜êÓ½Im6;JË+Præ Î_(¥íÝ-ˆ' õÀ‰2hažþõÞÄ®jl㱽㳈Uw!O_ŠtûèUFØ¡E»#U]ó`B*n¼ò \}É9ywËg‚ÊÙnعÿzûÊP˜|Q¥)LXYÌgΔ¯ùð'0Aåƒ9Uæ‚mæþø ¨9µBd—’·S®|’ö£ZäOöò‰ÀE‹K¯O¿ønXv –-œç Í-øÃK/ÂÔV‹,ìGrÌy¨I·{c È=âS¾Õ¶ HS—AËÐ fG¶™¿€V[2žý¿ïCGÌ–¿Á/‚Ê…)œêÔ c1gê$AXƒmý÷·‘Þ>WqôœØô#˜•⑼Éwaâå¿B\’sñjoó‘é$ìùÃ">ÛWXÌÿÌõËýæL/TVã©çÿŽÉª·ˆp )Œ',ËQ«½?ùæ7ü&ª~Tni§ÅŠ»öâÈ©3¨®­Gk{»ß\cH‘óP˜VŒ×mÆ8íVhb¬ô=T£Ô2g,— Ã‘ìáIyK"pñ!À’»S²­Ù΢Óúç%ÃVú¯ýì7Xÿr5%aópç-Ðåß„o>ø_å‹ úUb”×ç™R>çÅè ù˜yã˸ìó‡P0ísd¾‹AÅÑW±ù…™Øýæ ´_Õ'QÜ:Yu‰@d!PUW£§NcŠnUØ+–‡í8pÌ¿=è$A¢û S1ㆱì‹g0zÞ7¡Ñ& îì*ì|ãjløëXœÞöÿ`¦MeHüG Œt§iª³¤`³úŸI€žŒiDcSƒ_¹I‚êlû+; O,Àä+ŸÀÿs—>Žøäbt4—âä–cý_Fc׿¯FeÉkpØÌ^ä(“H$®œ/?Gd0&F"#Ý??{IP]{uë_¬¯ê¹«§õfÿ˾t ïþùSï[UןûÞ¿kŸËÆþw£ªäuØ,îŽ{2“‰€@ ÍÔAnMíaG£Í‘¦…X8k¦_u Ìô¿ŠŽž‡bU½ë¼´·µmVLÏC‡e>}ã+˜zõs‚€–~Í•;QuüßâP©c‘^t²Çߊìqd=4„n÷èAWÖT"š­T„%lÚãêØ8̺îO8ºþ‡à™IùoCk} êÏo éqò&Ý.ì•%oAŸ‚©÷àðÚïRJÏó˜ÖÒÊs÷~êj¿g‚JÕ‹îÓªõxëÏ´â[—åལÍxëH“xZ£MÂ&ÇíXrÿv\ùðyL]þ ÒG]A}×…ºÒÕ8²úËX÷§l}i.ޝÿ.é`W÷jô¢d™D"pq •‘Võ"w½Ì‹§–šñì¤^ëìhÀèÙÿƒÂéŸEBÊhñÈŒkžA[Ãq´yí3hªÚƒ$òÌÉ{§,{î½ýñ:tÐlL‚$¨^ ¦ÕÄ ÃêÀãŸTâ×7¢ÓNSaí4•-1õ6´[ØpÖI ÿrP‹Qs¾Š…w}‚«¾ZCž/!g­¤ˆCKÍ~œÝýy Ü„5ϤaÛ?ÓD‚ÿ¥¯êZÒ½ºŸÅáEõd‰@Ô#0•|XssGãXçu^µ%1sªàF»Vßü´5žîyŽ9Ts[ÕÂÖÁ7ê.¬GbÆ”ž4ƒ]Üšô˜¤~ßÿõ~U)ò†¬K||¬JÓ‡g!Û Áª-X:&­ ;.´ƒØ¥£QÑbÅÉúÞ/ë[ó§~V»…ÔÛÉÝj.¬CsÕ.¡`Á™¿¢©°2§!%g>’sç‹3ÿމ‘ ]»t…¼AXÍMèl¯B'?>®׌gÎ,¦e9u˜ ]xUË ­MHƒˇnï[͈QiźS£Hc7·Â1Émúþ‘ű;Ђb¬Þ²·.¿¢ÿm¿%AõóæÄÌ8qqÉ(çb0kO;Å~&¤ØV ³Ío“`Í©V\5.©OŽâdÏ4t¢8ÙK‹/GZáåŸÂní@SÅ–n»­Ä½¶ÖþMäÁz¢¤¬YH&"›’3O\CÚ$aëSˆü!ˆx\[:jˆPVÓÁgçµÇœ£  tŸ™Œþá¦D-vtÜ í_Á ‰¿è»çw{K)éFݯ#b¬?¤nâi$ÑŸƒ6! íä•ãmȉً³¥Ë(¹$¨Þbæuº‡eöI«×¨Ÿäœ^WÙjÑSäÑïõg[ñÔME}Ò>¿»/“!ë©›ñÌѾº¿3Ȩõ…× £Ø©×aw«ÖÚýĹîF s¯Õ»ÑÑtšˆîvq(™ÆÄ¨G.[‰äk Æ®“¡Ž½¸VGW0‘çà!Àª(‹©V:,¦zÒYÖÁ¤£u泩®÷š©Íâ½¥>VŸ]B.ôd¬eƒ­Î‡#Õ©h=j²„g=6ªµæxÜ%*;úOL¹üçâVÉÆÇÄ™õ«¿ì.¹Û8M:÷gñ%É¡º…Ósäã׿÷$˜•—@KƒÍ&»0 ¦ÄõÑ^”…Í¥m˜W€ÚvžÝVƒ?Ý6 Ïl­ÅN"® ‹D^èhu«ÅâP2·u¶ ¥z ®ÌÁ¶ÕE{Ó)ò=+ŽšÓï+I…xþ²ñ)ch1—b:¦7štH|.&?Z®3±Ô2\´°!ÔJDÑ•@¸¦•×,õNJ×v«oº}–ª˜sÔ&d±Ì¡£ûLq:ÿî&žtO¥Öõé ^\eõš7q]â“ÐÇ´õ¹×ÿGí¹5˜uýŸ{¢÷® ÙŒÝ¡¡l+¶¼z•°òw9l4Ç€„Ô1h¬Ø©$òÜâÈG^Nï{>äÝ $Aõ©AÒ}ý§ð–sm¸fBò€Tçš,(LÑ =ëë‰+ÍD’NÂd-}»À“>!¾Šüëž1}ž×è’É[à*q(7ººìĹž‚±þ˜ °m |>F„ö$L­eâ6*É{Î*µ–m‘ ®q‰…=ž¸æÄ™{ éreˆLXD欵³Yœ×-tÝLq|vV3Ý·´álìC@D\| j±qé´VptŽÏ‡ŽDh¾ÖuÿÖÆg Bª&âåoxû£°$þ¥!‰)çßÞte‡_cÚäf¦"¿'Ї”!m"ŽoyÜkÏš®.ÎÚ—âŽÅ‹|nŠ|{|†Ìý—»HL,—wëU/4w⋠ꃣ5&,Ÿ„òØSÞŽ%ÝúY÷¹÷Ʋ‘*ô¨|`§{ot9ˆ˜ž³·L­çœgÒ3uÐaj9'tZí¤BàÃS`1Œ_ñ‰3]“Ÿ6.ldãû] bù蹦 ©"dè‹@»µ½ûèp¹¦8ý&ñØÖsŸ®±tC » ¨ÝÖÙ·i$x)@IDATt‰Ðê™(fPß1‘L'bé$”®DÓõš½SBŒíà#+îŒ×Å] ‚êMh®ÞëM²ž4U¶I´mJ†_ûaI‚Úcp.ZÌv2F%ŠÌu¤{e¢š@:ºìÑòwÀݳ҆W¡[uŠùî2rØ;aeâÊ+i™Û*É8P)ÜKœçJÒ…Õ€¹>ÚqÊ]6nãx[Öߪµ‰ÐÄ&еAˆY®gUjÚM–8æzz¯)^£#/²ÊÒDˆ˜ Ý£³Šºî>ǨX•¢ª &Þ1¬¾ rû\“ßo}X˜5g—ß xæÐØÍ† rìÚí}=gw>Ïx ¢È‘£rÝK ;¨ â’Xʈ%©EC2qækmr÷‡Îü›þ؉³>­›h:‰'c©ÁB;rhTžîCQw#Í”:l»ß¹ýNçXó±PIP}Ì×äß¾¬×ùÈ•¹øéÚJšaåÀcWåõdu‚& LÊÔ÷üÆë«HôáÃS`W6>X:ÈØÀ‡Ð¹ÕÁÊz5ºÇâ¦9™ðò5‹—,j2·EÇðx(O5‹¾{jþPhâZTGÍú-Î|Íq=÷ù#”ÔM» b?‚Ù_ç}ˆ ^ã´ä$ñ±m±çÐñÕƒ' â3íð Í”zðŽ›P”—ãWIr=T¿` ÜC«LXy¼?º"º§¥ ÑÖÒF"lqlýÎB¬5gh‹Çð ¶‹swœƒ8Gæþººh{>óoâ({ã™ó$­3oFÜ&ñ tî{Í‚keþµûZœ7ËÜ­š¸_­8˜[ëí.ŽÓ(ñÊY›®Âìĵ³©ÏýÁ~ð–F+Z,nëH¥œ–¹Ógaéš<“ä´u ö¬7ñ’ zƒ’L#„Víì:t«7¬CMS+t*+R´M¤èQÃH»“vÚµ˜1i._¸¿ýûË(¤ ýæÆ½å±ŽÍŽ<ì0=ˆ³æàþÛnõ˜Öß›’ ú‹œ|N" µ M¨©o mßUHOIAFZ Ô´A #múè“ÏB#ÒPBÛN—ÙµCC 9Ò`Qe£Ò>±ä}r÷Í·`öφÙá4JÔá 'Ÿ•H"ÓçËp®¬¥e¥h¨Ü“±£f xÂå˜:~ ²3Ò{ˆp°*=,KHÏ6Ò'Oƒ7ÙbÇ\æ¿«q2_‰€D ¸ð6Ò‰ ñ ~ÚÁ‡±ôpkÊ»”:w*]Œ³;Ëp|ãS3:“.Y8ܬ½~Þo‚ºûÐQü磵˜:a,®¹t1 s³‘œè~¶×µ‘ %¨C Å؆²ªì=ZB¢÷s¸óú«1ÆÔ°¶#>Õi­çµ/Bü"¨vîÁÊ [ñîñ{«€P6R–%f¤ø˜FÌUyu ~ö‡¿¡ÝD+F-œ¼B‡È9A!¨¡%¨>{3gú¯÷Wᑯ<$‰é*oK.6 r²ñÛ~S0\L+ÂRlj©£ͼ6M Qð‰ ²Î”ÅüǾò$¤x¢>’ÅH¢ ¦ ßxà^üû£5`šŽÀ‹ºð‚¼V/hªàAݸk¯Ð™²¾T‰€D@"0ùÙ™¤¦á q‰E¢h^…-TÁ'‚zäÔÌ™êݾ,¡jÀH.‡§ª×56¡¡¹EL[Émõ¶mìð-æó{û€L6æNŒ#ä®Àëÿrp·^j°êä“Qªº¶^XóƒU™o/lø{ï“HILDk[;tÚX|öÖ1yìèÞDa¸Z¿c7MÓ›ë“?ßïŸõMÍøé7†6Ö9älv;¾üã_â‰ÿý ñÞ¯¹¹fëN´ø Y’} [÷ÄþcÇ©üX,˜1 Ó'Žó© ¾”%Ó:`I–Ý)Ãâh÷S¡$¨>q¨­4#_p‚‹¿ü«‰püð‹ŸÇ¿öEüþGßÂ-Ë—á¯þÍ­Îíªƒ[÷¹3gøÚÃfó}OSg'VnÜâ>ã Çî9| ¬Û昮\4+èCµ•V6’!¸°åŸ}ÓÃz9ÔЉü>q¨Òi?4Ccå†-øÔ—ÑÌŽÞE§ΜF ~ ÌÎ">+”þM-­‚k½ïæëÉÁ:k‰;ˆð+¯ÄùÊ*,?¼Öä "(± þëÓ7aT^.>X¿rÈ>|ü”ð!œ7}2î¸îjZÐ9O¾ð*>GÜpzjŠh0ÿþü7ãÕ÷>¿ó·—ðƒ/>@×]xýÃÕ8Jª ¬ô4Ü~í•(Îï]çU$îþsíeK°zóv,™=“Ò¦ºÞ׃µ‡o¾³f=ö‘cNF†¨“šê¨„Í{öcí6ç^AìTγ8v¦Ëɹ{ñìâÖeófaÏ‘Mÿ´òw`'ÍÐwëPÍn¶H l+{só‰Cí}L^ ‹Õ&ô¦£ óÁÄ1'3LDÿúÆÛ¸ë†kðóoUÒßrnØ×H÷˜œMºn‹ß^½ëI}ðÐgnÁxšI²bíF‘oMC#Þ]½×.]‚o}þ>%}¸[ßU^] « ʾ…Ì•Þ}£s‡Ö‰¸²èþÊ{+ÑÞÑo=pŸ NO¿ø:L}WRÁDýšK‡»J‰ê9{jÏ6âÖyó¶»n¸3'OèÃY<~mÜ*pøâ]·cãνØE+õ·\u9.›7»'úÔ¹2EO„¼‘D¼È?"Q°F™I4æ§ï»#¤k5w8Œ±…‚3eýãÍW-ÄPá^çh;úÌ"”dHÀ2âܘs\8k:˜*9¶ ÅEÈÍÊÀõK/Ááž ÓRœ›f¤¦¢ÃlûÞID‰<—Ç:3…(+e¸ž¯&‚ÊF¶%'\£á©=l]¾dp¿tî,LãÔ‹qëwìÎãSÆ!ŸèL\Eéö9Ö'oþÁ¢'ëNùcõêŠPZ^ÎK†‘@Èß¡"ÿȆ?2ZÇ Pg/T XçÈÉ3$ò& OqA¯hÍ¢¾N««îp+R]ÖuÔ¨ÕD0²8ÏÆ %d¹¨2H¼_µy›r«ÏÙáè]ߨ,¬í¿øÓ?zÒZ¬2ö8§üõDº\p]î".—'†LÝKÙ`5X{Nœ=—]Ú“Ëú0WÌ¡–ˆó9âÆ?&U;íáÄjw¡Š ªÏ¾ò:Fåç ?êø¸àn9ã®2.´ÄÒžZÚfFìK[hkhϳ`Ÿt¨Á®ŒÌ߉@ÀÒû¹.3Æ„ðù7ßÅ#?„<âÆ*jêzàj&«7áÌ4§n’7Íó&(-§-¯©%n¶w²†Øb„âYoËÓûæ yù´_~÷«‚ûãû̵jÔž‡ÔtòM̧ö±¨®OíɤeÚX‘Ÿ%’ׇÐM ãt:ÜJâüÒ/s`µoöÖ?0Wü‡¾A:âåÒí¯?8#ü7ëQÛJÀzTCFð×:ÔPlÑçÅuO–:E»Ã7ÈøÃb;s’3ˆ ,!g#ù§r8XrÒ/wªý¤›dî“g³°ž’u¬ÒI´?Ñ]6¯žÞb„ÑŠ‰׃W8Wt–¬ýÉÓkVö¦wÅ\ª«Ã·§öð½¤â`q½¥­M|h”\'-Æn²à3!åãÅ·WwÄåvÏùC2ò±J‚Í9>8½ #}¢ÓÁ»ü†"xf'BQYÆX'xÃå—â™—_‡^§\bYæ¼ã‘–9QöG}ô©?¢(7—üTÛðåûîÏP:Ê›ó°dµ[â9,[8/¿û!ÖlÙ!DdÅÚÏŒïÔqcñÿhñ‹Ç¿ýeòøþòÚ[XCVv&¨×‘Ö›Yt\ÿ+/ ùÞ[DyžÚÃzÞ'^ø'}â9ò)èc£„ #æ<ôûg…úç‘ßËÊížóYZ#³º®Œu½\1{:<òå‡zÒÈ‹‘‰€Ž¦Ÿrà-ÒC|Z`ú‹>Ž¿>þh(ê%Ë X̯£•ÊI¯êÎù96“©S¸Wy+æ+À>ÿæ{WT \‰LæÎK/2ÊåÇëê9½b4ã4¼’z2ù'»«£Rž7çÁÚÃ\tMCƒàŠÙõ«`‘žƒ¢òè_þá¦%뿃ÒÝObò¿Ãèùß:GgЋ”x‹€Ó äÞÈÂyð"Ã]¤†­ß|ôƒÅs:…˜ò5×QÑoòïá„ÁÚÃÆ´ÜÌÁq„t8¨ìguñNµ³#4ª$¨#{< Úº9S&!-%iÐûò†D`$  Mp3-íµ!iŽ$¨!9ò qõ ˆ¼ÚÉIƒ@¨9TiåL¿É\$D@ßÍ¡v„†C•5¬’D@"+gˆ¬ü’ ¦ßd.‰@" 9ÔìY%‰€D :P©µˆÕ%ÓV(äØÙôFH5èË$p" Xú;C`é—5œ=-Ë–H‚Ž@¬Î¹Æ…äPƒµ,@" éhtNk«¥5èM•jÐ!–H$áD€u¨lÎÅ„‚Y—°8öó\pÞ„îÈÉSbÁcc»)âw²ä¹ò‰ qȦ­>¦M/6v‚ÌN fÞÑØÁÄãbÍ{¤ŽoîOÖÉ¡Ú:ƒÏ¡†” òþ2;öÂ;´õÆ”qy¸vÑdf§ÒÂñQ1Ž[Œ(«iÂÞãçðØ“;ñék®Ä"Z IEë‚F[ˆö¾ˆ6¼£¡¾#i|»â­Q8Ôˆü!#¨vZ•h-­UÉ›È}ýÞ«i‘a§¢Øµá‘~Í„ŸiãòiAæF<þ÷b•üå—,‚š ‰–0ú"Z°Ž¦zŽ”ñÝsE‡ ‘?$¬¿À[iwÊ·ˆ3ýßÿ¾)*‰iÿN*ÈNï¿q'xŸxn·1ÂHì‹hÀ=Úê­ãÛÎ=jDþ T-[h/ùŸlÂ#ÝLËÍE‡xï®cúÇq[¾AÜö{´Ï;·1œ[æö¯›»ß#¹/ܵWÆ h߃µVÑ¡òÞRÁA'¨¼üºí»h…ù<æôî3솅*V]L›/ÚÈmä0Òû"’±ÖºEÓø ã+t¨A%¨Ì™iã¶çÎaÞ”âÁÚõñ󧌦=˜Î‰¶F*—z±ôEÔ¦l@4ŒoO°*ëëL´cfCS«°æ{jtmåqº,4«j{ª‡r]¹x37o{*ÔÓ†yÜÖHÕ¥úÒÞ´9iZÚ:p®²¾OÑõÍF”U7ö‰ó÷Ç…êúøW‰ÃØn oÅrøt¹ØØÐõæ™òZÚ÷Ë»±âú\´\GÃøö„¥²}´¶’v*‡j³Ù×ÖN~§)‰ ÛRZY‡¿¼µÞc×›m;Œ÷6ìs ØõúÝÇðØŸÞÆOþü¶89d¾lí0[D[¹Í‘|é‹H¬?×é³®ÜÖ§zL`Ÿ.ëçï7>Þ‰×>Þ7Äÿûû{X½ÃuÇWÀJ;¥®ÛuLœ]ËøÃkQÛ|G×2Cy ãÛ*µs_4‡½ÓS²€Ü ªÛ‹™Ú×½««ËëÊò–ÆÏ½ñ íæ™FÜHn¾|6&ŒÊÁG[aÿñóÂCàÖ+æ`ëþ“Â4gR1Nœ¯Æ¾’Rd¥%ás7]Š•[¢Üy"Òüì­WÌEqÉ«¶&NÒˆk—LñüûЩ2̘Pˆ›.›ÕSdzåuøê]ËÑÖщ6ïÇ%³&ôÜì‚ÛÈmd‘ß×¾¬­‘ï Ü™s<_Uµ;Áî°ÃÜiý×/œf+qš×_2CâSÇq#šZÛH5†çü÷mË Óö¾×.žŽÅ3Ɖõ™²ZtÌêÄ_ßÚ€Q¹( I„‰*‡êú¬Ø¸Oì©¥ô9C{Y)¨¬k!ƒå5ØLãtÛÁ“ÈËHÅ]×.¢ý»¢Ç½ÎµŸ#}|»Öµÿ5¯8ÅÁa·ô¿ðßAåP¹”ÁæmÍívN^¨&™†â¼t¬!.¡ÙØŽ6 â:‡f*%Ãd¶a&Á)cò‘dˆÄòÞë Âzðd æ&Ô5¶àŽ«àãíÎçWn9„ÚìméÜIØuä,q;()­Äý7,ÁƽÇųJºíråd`Ó¾“ï\ñ[¹çéÌmõåãá)¯@ßó§/]‡`ä×ÔÚŽªúf!!áG$×-PÍMKNÀ„¢äf$‹qÃù*ýÁœ+C¼ÓÆ€g±±¥¸ï†ÅxæûŸÅ=×-ªm½"ÿ­Ë户’¾”T ³'aþÔ±bŒ™I²bzÉÌñô±/ÉXa'âûIB`ýb6© ”ðʇ[ÉÈ`¥}Þ“°aÏq%Zž#6L½³n¯8v=ë±FL`O“ØÎÛêúVêï.°éЩrLÖ™öN\Òжƒ§Iêjußñ4©8kvb¿Œ¯zm,IT™øû;ðn·nÊ詊N ^¯ÅËiæËŸ¹ûOœ{ÖÕêu±bâǾ’óX@cvòè|‘·üZTgÿ†B‡12Hfj®!‚ÊA1±S±2çŸu¦|(!-Ù€.)~^—1]‰p¾TJP^8å÷ôñà£X¾pjÿ(ù;à‰!ž&‡(úSÖ­+ Q®?”JPÆ™ò›ÅuwAI'ˆk·ñ’¯¯#ã¦køÃk„A¬‰ø¼nus­|È>z8TGðRCP÷,Y"¾z×ÕÂF+=ÇÜ«.Lî2¥QJT{I>7¢¸@ª6*Í"CÓp«®dˆ,Bi”’5²ú^Ö&„°>)AöeeCgiE°äÊIÞµä»L·zÔN!¬š,*À¨TÒ«Û¨?툉 ž/°$¨î8™]t ðÆê]¨"åòUW˜E^ ÙØSrNüæÉ_¹s9~ûòGÈ"ÑýGÞ’µu,yöA¥í¤cHÿ¬TÇþ`UZæ+.ûˆx>tëå¸ñ2§a“óãYw_¿çj²ÒÇÐŒº6²àë$1.Бò|¹}pˆ .É“ªfù÷"C ‡œÿ7Ó: ¥ä—ÊÎþN—Uc/ZžQÅîwÑu©Ç沸ÏEÿ`†à’ë`Ö\æ-ÿuÓ%°ÑjbYiNgû"Ò›²tù~ãžkh«›8òy8Œ䣑„@W÷L9É¡FR¯ÈºŒØùfšNÊiÉÙJZø$M® tDà/¯£ 6Dq6wÊe—ÿådD0¼¶2Œ\ººÅý˜ ‹ûŒ ùGî8’-“HEÜ—U‰€D@"0Lººœ©`úŸ*U”ª‚„f¤Àá ÑŒi¸0ª\IPû!Ä[/ì×eB¬­C8ú/™Z„Oñº¼´æn(B´b l†[†â6 £TÄs¨¼ý1oEñþ¶ì‰[Œ6õðWóéTéÁG²hoåà h4šh}ËiôožïpF$<èþU_0ÁÚ}´/оO§âg Ö0ÖšßÝ?ÐZÄɶÔî:Dãî~÷Í»Ä4ÔÚWª´º™6ï„A‹éc³1‹v}Hb7ßþÙ»û­˜ºkK$Æõp¨A\EiwDThÛöŸ¢mK¾â&ŒêdpÞØºIq±X2{¢$ªƒ€ìþT_ð¦y‡NžÇñ u¨oéìÅibpìl9v$] ³*~:£[4éØa¸óÛ6á»O½m†hÁÔÇfETòPM;åFG,AeŽ¢…Ö§ü÷úƒØ“¸,(ÄTéu&Ô»ã#vý6L¥ ÜRH‡&Åç9Tý1œ¾P¸Ð­ÞƒVU"*º2I¤Ï„-FƒGšÇ IL][½'á2äZΣR[L‹j¸Þ¡m¤Qˆ½™¦*Ô=ÿ>®^<·-›åÓ¸‰Lû¶::u9¬¢â¡ùC£ ò£¬4Ïzýžâ ²HÌOö#ßi'U—ÅerÙ2ôE ”ýáO_0§÷î¦CøÇÊ=Ø¢]„íÚŸ ‡mbsÄu«ã¨‹8ÎJ]ñbª ÓEš×Zm¾àzWí>‰7×íï³{®’n°s¤c:X½£-Þf1Š*«µÁß$1" *¹Í´ô¾ÓÕ¨P÷î쎬P`ß©*Q6×A'áè_ú‚‰éÒ®Úq ;â/C»jøzv_úžU;.Ç'{Oaï±³^ÕHÇÔ—öGzZ›¹YT1V—ôªF$AµÓn£&“Í$òÂå-Š\Vs[»(›ë ƒpô‡·}Á„‰UC/¼¿ûImÓ©Š K·±ªâ€~ÞXwfrïcO!’1õTïh¼gíì&¨ú‹” Úl6”°Û¬¤ Ý Âg;‰û\6×A'áèoû‚ÅæÕ;Ž Q_£:ø/Œ§1ÁƬ¶N;.TÕÒøq®p4XúHÆt°:Gk¼µ›CÕ\¬*s oùêù+œîesdp"¾þðÜ\/V •”5¡Z•Ýe!¹†|]­V§!d°JE*¦ƒÕ7šãm;‡ÊâR8 —=”ÈÍÌ׺‡³?<õ…"67ÛС¾ÁÁÜ´Ö6Īc‡êi E*¦Þ´1ÚÒôˆü+‡ÊƒmTPû”Ëöô2µðÌ<œýá©/ØÕIªƒÎnôCŒe¾å,T]VÈŸÙ¡ìa4H"ÓAªÕÑ¡ù#ÖÕ›,HRáÿ–&àË+HWáæ Z¯·có+ŠSè÷D-’u*¬8Ù‰Éé´1øû~³7YË4>"@4O^cÀ7¶ÃBêÃË caÐÅà_‡;‘¬WáÖ‰±H¢ßëÏÙ %SèSuxä“öa)u˜{e*o¦—loB­—©…ùÜDcåç›;—¨ÆÃótøáÚLÎÔàš±ÎÙSzâ2ÔÚ0=[­e6l8çY„ϰTc|Ça\19 ±šÊ Íà~|y<ÖžµâX½m¦Ÿ¡…!VE˜Z`%Ì©ÝÉ/z‘ßÛ^úîâx쮤maéßC³ô¸{šŽ©sêèo–'`_•ëJ­xäÒx|tÆŠ‡çë‘cˆHÇo›±éîŸNÓyÉŽWÓÖ…;§èpÿ ¦g9¿×ÿ5S‡|"\눘>{=õKµ S3Õ¸jL`¥ÉJˆAº­Ækl¿2²bF¶ÎÒáStDô‰í&¼JeŬ±áƒSü⊄!óNt4‰4çš,´øN¬XÀŸ™S®-.Ðà’B ¶—[`úŹ:èiö×?˜Ñ`ê 8¦®õˆök…C …ÛTTs¨“é¥üó^¬Ž.übKÓ^=Z;1³è…‰£wvT² -fר‰ Q£ºÍ³ÁéÆ×Z IÖC¥óÌ•ôh)·`Çìp®OtÄý zNzÎùò{[7Gg;l--¸Æý›”¡¦é˜N¼þ²× ½ì¹Ý¯NZðÁ=Iøôd-^!bÅ¥ˆI*⾆®ÅÏ~_‹µwÚñ/)DÝ»…hþï;ùHL¸¤/d£'qÿ«ÿ=GOœƒ®lâFThGêÊL›Åý˜áš'’Zâ†q:\ùŠÓw‘ãxܰ ÉAuˆï ™S{’¯ÀRÕj|ïþdæöuãZ2ß>8ÉV^Qo‰ŸnÿŒJU£É䞈wÐ~ëx'^?lÁÊ{ñŒ˜º-p„FÚ:é«HAr¨:˜¿Úå­vd’¸WéœYÖ'uZ| ~ðI‘Ø´¼[´Ì2Äàq$C…ïIFA~ŠÛ—x°gÿçç/â[÷ÝŽ‚‚z®—S,}¨ãF#ÊËËñä«oáÏ<àSñFc,½üf<íáåg\³˜@üX¥“+ñaÒGn-·ç èccAýs¾yè¾ðTQ^Z/>.i©)D¼ÒɲnAlíiäwžÃæäÜ>Ê)ërÙoµ$¬—ÅâL“Yï=QWâV½ vZ£=6§ÎW!'#eX í\ |Æ'ê.°­€9g“­ ö.ò* êSweE{œÂ¡†‚ FµBñ‰ðÅÄqº ñ$r¾r«ß\‡gv˜D’QIj24 ï%vW–ŒŽR_‘‘Ð]xã¨÷M×áÍ;ñÒÁN´‡Èœâ‘ºáõ‹üÌ¡¦¦&###M6-Ì]ìN\æ®"Ž9ã²VÇ uG\ó©Æ^âÉ:wæPk†P¹h#~G‡‰ü™­Ãòá««Ü…Ouâ3¤ByûÎD¼pÐLúëÀ`ꮬhS¦žJ‘ˆždëç•£5¤ïrŠš_ü —U}—âÖAŠVr:VÖ–GëIl¢8yÛëHœúº^±œEP%°noÙK-ˆÒaíBé1™ î®èÖÅ( ý8k4jÄéõ8QÕŒóuFl'ÎÔænS—¼×œµà²QâDíˆæªS~ªûã«$¿Œ8Ö54Î| ¶đϣÍnÕ_ãÔúH±Á-5.FpÌ®˜²~wé‹NLÛŒ©/mô´›™f\š¡ÖáQÝWWŒº»ÿü£¤ äùÑi Ê]B|s—=sBl°â0–^àŸlìp—LÆÒf^;Ò‰B’Üî&¦&ûÛmu™îò,ÎDî[_À®¤«†$¦œÇŸ÷˜¯ñnè§ècð éã½ i¶ZèTvd$Å‹;åò˜ë||“ Ñ5³Æ‚‰)‡@c*2Ìm¢º„Ü´&jR :O÷ã(”øþgv‘!¸0Aõ&ì'·©@öC=^Z£&ª^ÙSþl”úãïˆäßöùæ·<ªó4ÆfhË®Sj¿v­ûÆóÞqÇÄÔµüh¿6·–‰&蓊BÒï>Ó!©Š»Bú­êë.IÀãÂQfÀ¤ Cçòx¶¯ùÐH6ZáÁ§+Hhô϶Ø|hƤüt¡†`ÿ¡Å}Ïmì_Æð‡º¼á×x89˜ŒN‚—T8œl¼~6¢ ªJ­Ž6N Uà ãTrO©AáexÓLPm4í4)^‹$k=™‚zIƒ6"H72¬U(6ŸÀÜ‚8¤?´Á ¸Ô¡Š 5¦Žì«4T›CyßÜMPõ‰1Aå¯:ý‡ž|l öÖáÏeÅÑ,.{hÎ"dÕ {Aáèoû‚‰jªAÖuÎj߬ ͧ1µc/MSÕ"?+éi©Hˆ£IîõÉ\ÉpaÚdQã[;ÈÝ*,P…¼PSëQfÜÅNPÙ%¦(E‹\«“eEOäÚ. 0U+fàH‚Ú‹8cêþð¦/¸^¬§ÔjµX4&ÉÄ¥.4®o¢çMˆsøn¤Ô9œ“ªËŽtk-æµmÄxËqÌÌTaT^&²²3––Bî\4ÓÎöäáÀ4Çz14ûÊ{Þnë^xƒS4§‘:Tê=ˆü’LÌKC¦µ&$\ª¶“沸L.ÛÓËÍÌŸº‡º?¼í &JZ2þâɹ?KF¡2aqËjŒ7AœÃýÔNÎzûN,l]­Ã;CãÆÛK/n]Ë[ÞÃe-+1Ë´ãc0'7EùYÈËËAvV&Õ'Á#wÊy…Ó,Zïà…ÙŸ¥Þ1w¾iěǼo?×;ÚB¨u¨!±òóÀ÷%ðülvØNI1Ð|o-lu[°3éÊ í|ªí2cžq&f±þË Êæ:ø|m£/y2­?õ eøÚ¼IRR"2ɱßÒi!Qºud¤Jl;ƒ<ã9‚® &²þóò~<9”‰¬š¸ËdYïÉ‚°¤u•p·òä!ÀºÙ‚ÎRŒ%"]׉8Zበ¹N§E¢!EˆøÌ™fef ™êÂñCá.LÇå&áï Òñ×c´À ]ßA+YÙñ(M æé܃…¡Ú3ØsáŽïáPC$òûF5üD'ž8¾fc;R‡^µ‡‹à)…qqzÚÎ9ã ÓÑB[õª[>ÛIzTÖÕÍnÛB˸i0¡0C”Ées¼ -Æräv.ûæí3áJçk_p=CÕþô‹ü ññÈÊJDLKÄÌ`hBZ‹&3Í ¢ÙJ¶Fðaü]#ÿÓxÒÍ«h»9ÇV¶Ú° õ4k2P›Kk艓už‰¯½ I´ŠTºµZ,98†¦æ§'¥‰±É|žöš”œˆÔ”d1[+Ñ`+MyC|‰i’!?¥·æÚðT^b±Šf‚ñò‹z7!šÆ·ë{e·´ÁJóø5Z2êS]oíÚ |+‹†xÊjš¼&¨,éuÎ)…mí˜JöiÊꀶÍ4èsÄŠB¼‰›¿ûM±ç¿¼9–2ó«Qd°cba&2‰ÃàiŒ\¶/"?·-1N'ÚêÍË8„½ÏÉß¾à‚Ùè‹ØX ’hýæúâãéCœB›-—ÚÞÞÞ½?˜½gÆŠ˜ƒä´¼MIrk²[h0V#ßRNb[»hn¼*Æ}Œ qj2zÑ”ZfX7šF§DºæÙY:'üñ§ƒu¦œ··ý ˜Þ2Q+zøÃ6¬§Y…Ÿ{׈¿Üh3³\GV4Œo×ú*×&£Ó * ?—t‚Ê'\Hv9ƒéã ”¶yæ—$‘žËÊLïÙŸ'¥¾•ÍÝQEÜE qvâ$| <¯„LD$º¦±"/S‹ìŒLäæf‹²¸L.Û—°óÈij£Ao_(_òDZ®—¿}Áå£?ÕÜ6e RIC¼2[ÚyçÜNòSeÂÉÞœŽîY\gζ“TÍ´,arS3Ò‰°ŠÍiº(/\­Ri QëE¾ñd±OINBZzªXˆ%t¶JL˜}!¤®} ˜ÎÎÑàõÛ“ð…÷8D뿲^õù›ˆÐöz(DÃøvÅU¹VÄýPYø¹\ß(‡RS/Ïü ãRQ>>Þ}µMäVâë-¾à$VñJBxà27lhA‡É¹ðoÁ/Š/_*v5å0>޾ÆÄ‘2gÊ„›ËbQŽËö6”U7âÈ©r\;¦h«/Ïz[F Ò §/¸ü`ôG û‚‰d\[ýc…€we?U×qÂi4tpÙ|/Yˆì)0¶µÁD šðÌ+»ÃµJ->":L@YœgÎ”Õ œ ú9R0å¥ ß ¢ú¥•m´f° ÷¿Ó†WnK ³DËøv÷Ž()}ˆœú¹A%¨LYbS1-eöä+«ðØ—n!¢ï®ýâxð3wÀÅ+Ô©©-h#},ïvÉ/ŒBP•ó€Lº#øâÀgQ/ÊÏ@/ëi™¨2gÊÄ”Ëô6´´uàçϯÀdr—á6:ž BêmÕ¤n_p†ê`ô…kƒ¹ž|°hÏãB9¸\åàổ²z‡Ç•¹Ó,V‡âíŸ*/^ÍDÕi|bΗ9JÎW©¿k™þ^G ¦¼Üå+·&âK´ÀÐNZ´†Åÿ?^­Á³/DÇøv‡¿âƒ:bD~,q¤¸OIIÁ„Q0Ñ<ç<ýo<òÐÍ(ÌIs‡Á€8g¬Ÿrr¨LüX4ã)‡<ð}dPéeèuYaÊ\/¿Tü²øÂu0·ÍˆQi‰¢mÜFn+×7C ú‚ÛÈþT_xÂÛ•€öOçüPkÒ é^yUSJàûl<â³/cCyÞÛs¤`Ê©¿Ü”^µmÕûÞnÅ iY4¾óÅ;ÉãÛÖ¦æ³":>¹ØÝí Ä•âA(8ÔÔT²ÂRǨÞe/Ãï_^‰iã ±púXf§i¬â|˜Û`¢Ç–UæLyà»~_ÐQ^&ÒÊ ãÍóì©À ú‡Ï˜_†ì&’:ƒÛ–Jmä¶óÅ󦎃¥ T_pþìûb°vúÏD—Çý[ˆL•ñ½ÄQŠSö<4©Óð‰j>–%Û#~|»ë8cýmȘâîvPâ‚>„Xdâì™è°q€CJmΗ–ãÔ¹ tZ´L™ïºÐ  á!S~étl!fDb‹'f& 'î¼¼<Ñ6n#·5’ÃHé‹HÆ8ZëÖ|ß“bÄJÇLœëŒÇ“%´Ø4ïÖ&ˆ”öw‘¯q{ã ¡ž1¤O Yµ‚NPÎ(-Í)â;E÷8r€n¢UÍ;„èîj4YË},ˆ‹fld‹'Ãs¤ü‘àƒÛÉÜ©ÒÔ‘ÒJ{ä9p¸ßкµÿï°¥´mÌ£ëð—OGÞÖ>ƒ!ÐÑtš–îD|Êh¨c½³Ù –—/ñA'¨\&DL„œb ˆ›knn¦ÊŒ¤5ûd\ò¥qHËŸùcÀ„“ëÏ:S&ª|ÍqÜÆhÑÜÑ€o´Õq¨ñý\±Ÿ{»Îñ‹õUxäŠÐ,Ô<\q?1cÚp³òéùT®¿È¬ÔV8T&FLL{K¾¹?ùÔÊ$æÇæP™€r[øÌb4ÇGSˆö¾ˆ&¬£¥®ƒï,ß¼U‹ÿúw)^Ú[É4=ûÓSûîæ‰m4Ö9õ§#– 2è ‡ÊDˆ9Öá—BÝ‘\>ø£ÀD)Ú©+^ÑÞ®m‘×A`°ñ=7??½:?ZUŽÇ?©Äü‚x&‡AoO­nS R™#”Cum¼Òq‘nÄq­óH½–}1R{6°íº}Z*6•ñщ|e9^½{ŒÇÅT[ºï¹…Kä.YÕw\å‰@€øÙÕùÈIŒÅÞŠvüu­­¡Áa· ŒR<}8!mbHk) jHá–…I¢d½¿º¾@hŸÝVƒsMÞ-äê·7” ‹¦Ç§Ž£-B«š5Ô½-Ë“D1KŠ `ñßJ{Xÿ~suD¶ÄXTÔ+Ô).TÔˆ²RÈEà›—f‹E¶?>Ù‚U¾o#ì–õèOClâvI‚ìÞ•ùKFY <8/S´ê×"Kí±ð‡Ø•‘u„ vÙ‰@(øÂÚÝ•+¨v\p¿wW(êᮌÖÚƒ"ZŠüîБq‰@Ä!OkZÜ33]Ôë߇#¦~fc9L­ebË“„´ !¯—äPC¹,P"02¸sz*ù¢Æ`õ©V4›íѨ¦ò-¢©ùKÂRIPû,T"ý°Oêåca±9ðîÑæˆhPc…“ ¦\–úH‚Øe¡‘ÀÄ¥ràT‘šÊ·Šj¤J‚ Ý!ë ø‚û¥²Ø¤¦ƒÖ5ïG6K+Œu‡…3rÎ<_š°´’C ”2#‰ÀŇ@‚VE+Pé…£ÿÁ0û¤6Wl§-‘`bªRëÂÒ’ †vY¨D`ä 0¿ A4fwyxݧ» RáÒŸ2’ Žœq-[" 3r+⟨7‡¥|¥Ð¦nƒT¸ô§\IP•Þg‰€DÀ/xæ‡ÆŽð¹Nu9lh®Ú%nIͿįvâ!IP¢ÌC"p#ßMPM¶°¡ÐZ³v« †ôÉ©?\‘5\ÈËr%#´8'Am #AUô§á÷¹;H„¸cMæNlعGNžBMC#Œí&²Î…×å"Ä­8Þ(1!Ùéi˜6a<–-œ‡8}x,žn¤7F´7¿áŒ­Æ¹‘¥%ŒnSŠþ4-?<ý ’!%¨‡;öÂ;«×cʸ<\»h2 ³S‘œºm^•†äs‹±e5MØ{ü{r'>}Í•X4{FÔî%ÇMhF«¿ãFáLSã³ó/ëOί ¥. Xƒ”2‚j·Û±vë:vâë÷^ü,ç ‹Aê%£‡ ø˜6.å5xüï+H hÇòKEÍv×Jóå¸QþÙßq££]jðkÚ·„Ʋ°v¶")s:ôIE}o†øWHt¨üRlݳogú¿ÿ}“$¦!ìä‚ì4üúwb }ȸ¸/¢%Èq¾žòeÜ4v8Q©ÝºÔP׺æô QdÖ¸›C]ô€ò‚NPY\ki5bÅ'›ðÈC7#Ù Åû½äÆü$¼÷ÉFÑÜ'‘ä¸ y;nN78ýOó“bÃRéÚÓï‹r³Ç}*,å»t‚jµZ±nû.L›‡Âœ4ײåu`Ë”±ù¢/¸O"=Èq=ä͸Ù]îÜen¾sÆT(kÎÛt´œƒÎƒäÜ¡,ÚmYA%¨Ìe˜Ífœ8w󦻭€Œ ó§ŒÆ‰Òs¢O"™K•ã&tc›’<öÍáUû9Ìëž‚*~„èOw:ö¦•蹘 TÖ™L&44µ k¾çªx¾{¡ª‡N•‘Ð)®Ö6QQÛÔóPiEZÚœ_Ê Õ Øwü<Ì}9±Êº&"îU½ÇùÀî‡Ãu0™{·Ömm3õ”ÅÆ!w¡¬ºõÍÆ>·ÊÉB¯´¥Ïaþ`ŠúæÑ'‘¬Kõfܰ›]Ii%N—Õô ÂýÞÐ%ßç¾î´Øàp8¯ž)ï?ÊC<†”g”¸`ŸÛM8éçØãç¸]|¶Xêù­‡O—‹6+í°Ùì8yÁÿ±îiܔԚaì´£0E‹lCÈlÜJÓУ?~qŸ+T‚j³Ù7ÔN~§)‰þ‰ ë@†¼·\ç1záÿôæ:¬ÜzÏ¿»IXÛ•‚RîáÓe8WY¯üç·>Ù-^Š>‘øÁVÜ"ø,5pßDjjÜÔÑú‡Ïüï®ß‡WWnǣϽ%|™w>ƒÿ¬Ý-šu¦¼V`¯VÇÐØøo?‚Õtüú¥û4ûýMû±íàé>qÊ&LÏu1%n8ççßÙˆ=4.[Ú©ÌS~eõÌkkðµ{ÅÆýxä¹7Œ+Ïu»ŽÏJh&Fã‰WV)?}>{7ï—8–¾´8Ñç|‡û€¥£-4ÝT‡ŒâåÃÍ. Ïõ“¢›Åb–Óþ¶C§ˆ[3á—_û ùQÆà•¶ Bø¥Û—á•·¢ª®U -äÈžŒÜÌ+­Àµ‹§ã’YÄ ågãôZÖ§.Ÿ þŽ}ëwÿÂg®žO\s~÷òGøÖý×âè™ ñÕŸ?u4Öî<»ÃN® ÷^¿ÌQô‹Õ¨±rË!A ¯Z0 ¦uÛ!y)øÖ}×¢¡¥ üáM‘×úÝÇÀ¢R[hâá nН? —»šÚÒÚîTò+q5­"× n9€£ ·/Ÿ1ù™nË,’97î“Hù=›U[ƒ­Ð_»Ûùýüù‚ˆÌ#•,í4îŽ1œ5¡ˆ¸8.T5âÜ(¼KÖï)¡o'âãúNvØuô,N_¨¡‰&-bbÄ7_Š5D€Ëj±·äôZø s¿ß{ýbÄj4¢¿\û†ÇZ¥¯¨mÆý7,ƪm‡QNðås'!35‘Æf%*h¼òóʇõÀ‰óX¿»é)‰¸fñ4¤ÒxxîOP”›Fc«7Ó˜0*§Ow~öÆ%È£±þׂܿ¹rµeÓ¾¢­×-™ÑCL™“ýˆ>æ<Æ”°ýÐiJ{c ²`³wáîkbóþ“ô®œD^F*îºv´±ýIÝ+=ÿÎQ§”ø™î…¦•rBq®=ó -£–C¥‰ E‘C–T•;a¸/n]c Æf bÊ­™Pœ+½N‹idd9Lb¿<ü2q¸é²Yâ þí'^Ã)zA ñ}_‘¨û¶n`‘‘ëÉâ9‹ÌÁ®cpžT9lÐY2s<þ³fŠs3P@¿/›3™$¬zW$¹lÆ­WÌ¥eÞ¤tLhY<ÏÏJCq^:ñ#=íT.~õ‡ø)f&ÑûÐN’Ù®#gEûãõbLs¿B’ÚìI£zª‘ÆÏk$Á]»x†à’ˈs™Û½›)¿ {K”bœû›i/)v™š’‡iÙ¡'h=â~X÷°‚NP‡ûâ&Gæª+­$¢§|qç=BÄô ,Vœs0ЀúùWnÇ×îZN¤öWÚ:ðÓMTèÄ\Ò’0¡(¹Éhë&”ýãXLgîeÓ¾“˜N/¿îÂè¼L<óýûñÄwî‚cg+E²EÓlj—Ayæ<^¶Â3ç xCTú`úøÁ¡d¥%‰¤ü"!5üÁ}2Ü~QêŒóPõcâÃ\ ø:¹[¥4wr1¶ÇÕÔÚÉcr…}"ßûÁ5î$ÂÃcÆ]`¢Wœ—ԤџºØX¨I*jí0ÑǬCôu;}pù¾»¾á<'SY‹gŒ#.OƒÄ5òGÝÔi¡ *1©BOŒ€XWÎ|þLW€sU½ªŸ¹“GÓ8ÈèÊ3|þáçoÄ~ðY"ÖHqXÜÊH5`ù©Ð'ÍÕl¬›gâ>êÇ6–pfM,"lòEcû×ÏS–uTïÁBÿ~ùבôΡ÷ÞqØÌ¨?·V¬.•562 R Æàè †jˆãÒ€`ïooobÓ†½Ç…ÈÄÕ`Îá•¶q_!8¬?âŠ(‹Å9D I ñШÕB,:xêã½ÿÊÎ B·Ÿ _LðXlKK2¸-¦ÑØ.T­´^뵘ãáÀ/ªk`±ŽE/UŒŠf79Ũ)còðqÆnî™ÓÏ$1–U$"6ÓÓ‹1°ýìëkñ‰º,ÒŸ)¯#ŽÓe†?²¿zá\J*µŠø¢_/¬ØBÌ(dö¦ÎN/Î>èÏgw ü8KÌ™åe¦áË ·ƒj¼È£ßð³L89ì$ÑzÊè\_½¯çwž8ðÇ“õÿ«‰ =CÈic D<ÿ‰Q>ö=1½,Þ'PÛíe$Tœ¹KH Μ?¸+6@=c 2©=&¡6câÍÄ}Êèt }U"Á*Ó›|Õ?¡àMBNóþºMøÔ•K½M.tu­­­Øq¸7-åõs® õ:-.!±ÉLz?¶ØÞI/Îd44xÙØÅ_ÜÌnB5yt>‰T¥›pÅü)¤GEƒÓ5GçõxyPÎ!ކEŸyôÏ%}'ëUãIl,&Î’Ÿã|ùì7‡D)5ãú&#X$gÎ8†ätæ68­3ÄN…nPYz¡`Ýg̃]1ÔñËÊ„’õsmf,š1Vè§P[ø^,q ,’2eBÑLŠ"ªËLqæ?°yƒÆ|°ùqPS””npuÈ „àëO=›ôq]Ũ%ã‹à÷‘¾2»›ƒgéEM8.& ™ãâýŽfŒ/B}|˜ÞvÅ<­k3ò¨/2(O–Brh p(ÊIÇ(RÑðÇKKœêe³'],ßg´0;}@ßä?-Ù >âÜoe$aŒ%"––,¤ŽÂ¬t¡KW˜ Õ *sç*ê‰Øeáº%Ó»9Y&ŽÊuOJЋº¸Ö—¹_&þsHís%q&¦<–™»æ¡®¢÷b"} gÒ{QIu`5ë\'ç9x3½G¬«í  «$&çç@5fМBœ+ûþÁuÜÄjuøÊŠ BÜÿÎÒ(+ö÷†ûJ3Üåá.îä–ÇÐV Ås¿†p¯0åZ¿bã½V¦}ñÑÇñ×Çu}ÞãµÑhDyy9ž|õ-üù‘<¦•7"Àœïkï$cA ÞšñèC·xÔ Ìa`Ìÿüü¨@IDATE2’ÝŽ‚‚$&†Þ2;°Fc¢aÜ£o"Øö˜ùé_ßE>Wö‚¸ýªy‚ zSŠë¸YYjÅc«+PD®R=8±ý¤-×ü|¥®Ïvm57aÝóà°[qåÃÈ©ßÉ` –>”ñ½òH(K•ey…À¬‰£„þ”¹Ó,ârÜY_½ÊH& 8ÑØ7<~ÿò§QIžìÀîP¾†f³Om­}ëÒÄÔ×¼½M_yìUØmÈs]DS®_Å‹·-aéžè Ú¿‰¬n`¥~¨[©YM ‰i¨‘º<î›ÓÄé±^ÖSàIì®ä.°GA(« XÝä1åzþlCÚmXDÛGß8ipûD0ÛTvø"ûÂ鳿ò–•`ã7ñÏŹ'i_¬)ëÂÆó&$êÔøÍõ½´PVÙX{­5 KGöø[BY´WeI‘¿¦µdiew˜4:ØrÏ3©X¬c'jì/Ê.TìŽó{šuòô÷î3oØp±ëh©˜êÇgö-½šÜWd¸xà[f‹—’አ_»ž#ŸÕé=ÞìW̾Ò< åÀÉóÂÓƒ­KfŽLŒe¤¢fV'àùcNן,ÏCNb¯ûW(ë¬p§ySî#Ãlxê੽’ v£sYêÙ7ô{O½&*[I™˜þö¥•šü¹/E:Yp]}?]¹ÑO‘s ljەáâB€g>±³þ{ö“g@y—LÆÒ9ãi,½N t4ÛiN —ΞˆidIÿë;„Õݬ¦º¸Kõ$Œ ‹vëÿoï;àã*®õÚI«Þ»%Û’m¹wlܱÁtL 5”—HHxéå‘ò„¼„þyyP¡Ç€!ÆàŠ{—å^$Y½®ÊªÿÏ7«»^­W«»«­ÒœŸV·Í9÷›¹çž9sæ ÇŸEm¼ÄÉò¼ºrœ÷ݤGoO'Á~ ÊœäÝ}ð%*P`‚îV~p‘Úzà¸pÚÎfWâ(ä¿…& °­ÇâŽçzLM}â¡›ðWVHøÆö¹%Á= ÓŽa"@ïȉ]Té{k›©%$Š2 Aô³ñ>c³òØêh«£è”idHœä3>,*£‡êFž SÏ£é÷^»…f‡E *>yiIfÿÃÚ†¬ywóW°ß 0&ÀTGië™0Ž€—׆óyz*71õxkŸ'˪,~ÈJÛIMäÁ› ÒkÑ>4¶©6œé¢°ÞúÅŒpŠÔønØEéîgLü: óK’•«k/Y·u×× 41µO!­PH™*jï>%ÜO„ÇšàoK˜0‚Ÿ5-âÙ^ 9íÕ±Në«ý—öµÓËûMÆ» ·Qrøb_±B¦æ2ª9õ OÚà`l?õWòÝçÆ_‘|I$ôúÁvúí&óÔæG—ð¢]u>E¥ìàKlUéá‘ýk(TÛÿãäSÆl —Õy(é¼q¨~ù¥Y˜þüb=]5V™Jíd°LôÙ=…gø¡ï©5*²Ëo†Ü—ŒpÞ,ê G¾0 ӟΧÛ&ù>ÞCYÑ?©ÍXÊQ…Hú¿®!)Pýºz$sï!ð¶™>Ö×Íÿ Ó;&û^˜"0ðÉm¿ äÍþ±÷Àp±$)P]NÞ&. :Òã,H!PAÐLýC˜U}‡škPxt¥Ž»IðçÏÿJ b¼/9ê¾ÃÅTÕÐJ­¼žúXYæj€+K8›„’bÂiò¸á¾¢,‘âΊRx…"ÂÌ!dšõD€ÁÊœk,»ø©ˆºŽ žàs0>†Ãõ¡Ö0ðV}(¼n;\Ê‹6qLrÎ4rn7¯E³Çe Ún:x“ïÚBŸœèN_N—ñ­ÍÔºÍÜö¸8Ìõ®ƒÚtº™t¡Á¼Âªó]¬óõÔ~@TøEÀÞÕŸm¡ÄàbJ ÚIùšrÒiû¯ª$S¯ƒO§Ò¾m3xÝ º~ÙE4›c\ÚéU›Ÿu:…×·>ÛÉ ›¼9v%bdºâøå0ZyJ#‚j¯åÈì7òRîâÓšçáºïκFž¬…××>ÛMU!IT”GÍÚ(jç8ºÎ’¶ÇD‘ÍMtx{}¸u5ݲlºÝvSßÖK|ØL»+ºx~~ýïÊš™fžÐâl™žH_{f5”ïäÒÉ”Ñ73*><”îxã$ÍÍŽ¤‡/N¡œXÿþÀÀï*–þtËúdËnš£{–¢‚͡ÆRº #匔LG©±'…°f£±‰.¹hªíjÞàKR|´ù ÅÇFˆÙ2®æ…û „õø±CxgWó¹…°‚%æ‰#¸µ¤pw] $OÕxýxËAzËaÚ©ŸKÍ<+i(Ô¬#üj)‰ ÝÔñÁTÇAÉ/½¨ÐÒnö°ý.Ï€ªlî¥ÔÈ`úû•‘”ç_mêÄW FÍø.Í6Ûs»yêÖ£—fAúØúr“õ¹I£÷ÞýÚm m /T÷´@ÿ'·SÛ†\A—ž µ›w‹²P¦+¤ðº†—œHŒ7 Y˜Úò€©ŒéɱBXWù´Íw8{º.€™»êCáU,g±pÈÂÔ¶>!Ñ´%z½¿ù°¥}¿¸·nÇ(„éôÔPzózƒß ÓÆŠTsæs ÓFSÖÔû™×.zv{5ÕµuÑâ<MÐÑß®cSX®ŽT›lÛgÇ~+PÑjä9Îï®ßF‹#þJº f„¼ç²öû—…2Q¶3¤ðúöú=b 1_Û™ T¦E¾q¼TÇjŽnä Ÿ*‹ èdÞª €4ÔúPx}sý>ÚiXDAÎwïÕTòÝ>—Þàr\SÍPÍÄ þÒ=StôÒµJŒð?1 h§Ù,LC5úå§e”Ï+ºÖ¶Óoœ¢ƒæu²æçDÒì,ÿ‰ƒàHöµN…‡5Ô‚StH¹šv3¤4Q!•lŸ=,ÊDÙÎÂkÛL=ùk8f°q–Ogž)PÓz³.€ÑPêCáµ’m¦Í¬Iz’ܤ”MG‹‹)2´›þ²2’~0OO<«Ô逸®˜%$TG9Ü݇B£©›–°fz÷ŒzîúQô k«¥<*ígä—_nGA/:~œRƒw{ ²4.«èØqQ6xPC ¯xÅHe _5÷ % "aàøšÀH-ŸC)/PîõE]WêCáu÷ñ * ¹pþ¿'0¯Ë ôà:zé -Îö›£½ç<ζSxÄdNº‡4á¼X&'ºŠWV}àÝ3ÔÐÖMú° ºx”VšµT{yøêœ_Ja4µ­ÍÄñEÛ©PïyíTšp­Ñ$Ê×óŠ™6Kó*鬷 ¯XU3!Î;‹ÞixUÏvÅFjù´æy¸îû¢.€¥+õ¡ðÚÀ&¦æˆ¡ B©­O v‚Ú(.¬Sx,¨ißjóvWº¦ÊÝtîÐ?yJC£f}ŸLƒUD7OŽ£Q±Zz„»þÆönqþÙU9î*Ömùø¥@íêê"Ç5uñ7È{‹0úßÞ"ÊpyŒ^ÛÙLà)Û©-mnç%4€‘Z>móŽÇ¾¨ àèJ}(¼vwuR{Þ+Õ‘ÿî¾wË_ÛMÑ:îâ³v:jÆC¤Ê¦»Þ|É«3|z«Ž{ºLTüÅEqù #û8@Wÿ›¬¾|s.µuöÐíìÔßÎf¥Áû´>àÿ|F(<¨!_òê Ÿjž%ÐÓø².€3õáK^áÓ[mâäö'¨­©D,ob‘t¼Ù¡?,8ˆ¾Î#ü_›OZ¶©ú+ù¥†ª,}T&M¿ê%^ý0”´‘)”;ý[”½HÜ›G£gòf<Ȳ)³ðVÊö-µY»=ÝŒq£hVa®ÎN‰§¥³Æ‹24l§½ˆ§½.ž9žrӓĹ–Í º ™¡@+Õ®œ?‰Byp /bõÌÕW(?'…gM‡—Ì)¤Ñ™æzQ®{sûÈÂpš—F±ú —ôkÍ&'&˜~gve*:YFWpz_ÐÜŒP¦¡´µ´“n¯±I'&™;•S’C‰{Àô¯¢vúû“˜?!1„–æú×Üv5¸ù#êîl¥4Ž&›>îg÷¨?mª¤<`€‘_vùÕb•8Nìü³X^¶xã/)"6×rëþOû“/ýÜ•8K¦j¨ÚG¸ÇÔ\nIçdv§ÂÚìÍ­&á? €…ÓòEѧËkHW§¡¯_9_,ÖÖÁ×N”VѼɼøÛ ôÌÛë)*&†´:õ3l²X;[ýùƒäì—ÁëoŸ_53í&554pú|Àg4*BG§ÎU‹|ëŒ-4el–¥ ôv¢ÑÉâ\YU½ÅÇJ·¨?oÒø„PÚ\Ò%bþ¶Ë$ºº˜wÊb õÖ‰úÿVsPh,W²ñl'$Óg'½ÉåÐÊj8·•—†~BÂôT°è ªâ)¦Ï°;Ô»‡èö×OÒ“âèþùã{O¸jP0…ÇdSg{“½çç —þž¢ ©xÓoÄq§ˆ9/t¼ÑÍЭDø¾Bt#ÏÏÏbS€_v{ D„k…¶:Ð=ò¼k`uZ¸œÙ£<îÚ‡ë4<}8š}ƒ(%Þ<{ uÏ÷y›²cC¨©Ý¾-ÿ½#tíMtËj#ÝÀÚ+ºÂFN›ã¿ûöð+ZgV|žOgȤ?l¬ Ÿ~\JsÙ=êÕ[ò¨žçîßòê ¿œeïyWCå»ZÏ’."‰LƲ ž-gÊ=4™v¬¹•º:[Äu-§mi›©m#Ê,¼ ´è®­´èέâ2¢~7Ví·Mêñãr~)c ö5œc¥•”ŸBÝ| ¡û_QÓ@±QáT^Óèq¾Fbí_¶…c clKÀÿõ;铯r¸ÄnÚ{ô¬0˜Ú»ÈØâýiŽªº(;Úþ+z´¶›~ÅV»÷Ì®váìíô`u`T›áþ°¨‚‚Å¿§æn «i§½å­ÔÜÑM7±»ÔÑ-ÿ;GVcS*Ç” ±?”•Ç?¦¤ÜKè\ñÛ"Å®÷î°¤ÜøÏÅ–}ìÀv [j{óÐã©öËXÅ^â²êzÑ¥?[QKGÏTˆn­mh¦§^ûDtï‘„Ñþ£g¼kçGÛ¼Œ$½±Åt¶žµØÿyù#Fnz2û¨.öVvSRDáG0è?o;/Ô·–vÑ¢4ÇÊá…Dš ÑÝßQæ¸WãUÜK{?¸C˜ëRóWQjÁMb ªˆçæ_WÈÚ(Ç6ÐÓ5béÎi ”8b*@ä¾ýfPqüßb5DÌûŒ"bGÓ¡/~:X2]‡Ök¸P+BðIT„)F›ÛÚ;hß±ñ2Ò3Þ¼ï8Ï¿W÷’"¸÷¶oû‚àÀþßÚ(?Þ¾]S0!LAcÙ4ðû-ÐîìÛ\Í©üãÿÉm¿§º’ Âã¦pÅßhõÁzúñ¢Tºmj<í(m&øž®*Œ¥åcx? ͧ=u­JMN>JslëïU•\~tªtžJTSoä€&ƒÇ%€pźY’<‡>^›öUUÀÖÇU¥óT¢/Ϙ{-ƒåü@F6ÓÝô_‚ÕI+_ 0]‰o£~X"Âò]?1ŽžÞRI¯ì©¥Ÿ.N£ôè=6üñyýZC òÁl~_”é Còä-ÎO(ðN‰Þ.ïüSõt³ôß·QOwåL{r–‹‹“RõôàEÉôŸ”AB¿XšFß_˜Ê«"øŽ×ó\;·ç×U£®¼ ž·‹÷iB\3êk8”í¨±§ø†@—'É>Þ¬ p0”úáY~½çm£öŸÈ=g±x_°×";òåOÈXsˆ"ã „Ïi‡áCàh"ï_9.†~Åáù@£xñ½¤ˆÀë@û%ǰ#b&`¯ÄØØJz^PÏ„²"Ù¸²•™3ƒ•«ð® nNzžåiêàè.Ï>=Í“?äÀs»R ¯:®ÇÈî&jõ|?”£ç‰ ¾h7XÁôô®§X ‡Ñ”+8Þ)Gãß|¢‘V¨§Vž…¨|Ž,UÂQø_Ú]KwL‹÷‡&å4~+Pü61&–Êj¦PJ¨:{—ÓOosCi×JLˆ¥¨à5%FO¥ m¼B©çjÏÊàòP®Z>muX o×€t¥>^³b4t¶¶„jC“=^'©]g)3^ãõvÓÕÞ@û>¼K ¾Ž÷KŠJž&žõ²üh¡z²®FÅiyESû>Û"¡ŸÿóË~#^ FCùiqTÙU@M=žolÐNQÊDÙàA YóÚÎNáÀדÔÁ³|P޳|z’'ÉÛÛuçvµ>¬yMì¬Zª'qŒäå¤QŽ/ÚÍÁO¾%&ßÄe̳8ðÛ>k8Ç<-LÖ‹¥N`S TR'5†øtÎjQ¡ìÒ¢Ói)†WøÌKˆ¢Í-w“©Wýl gÙEÞë[¾E¹ Q&ÊjÈš×Ñ zª®mò˜-6Z8þçqר8çò,ÎÖ…rŸ/¶ÎòêͺC©k^ 44µyiz=+ùÎ0~Á]ê0¯·›<í:wø Õhòå/³-Í+"ÇÍU”é•§ g¯ƒP¨%,)¡ç)˜1ÑÑ4&3ž{uô±ñ‡Âžª6µé ý®oþ6ezhlf‚(eƒ5dËkjd•UÖ ÍEÍýjÓ@ó­¨n  C°K|¢œFž!¿Ê@¡¡¶OÕðj}ض›ÌÈ^º¨q-kªî!»éì¦Ï)ÍêõvSuâßT¨Y+ÌR“.{žôñ,¨ó£Ô‹šçóE…¿¡¶wÖppg}ض› ÜnB¹ÝPóFªK¡ M&/-åÒzSð€ Mé(án~eEv»Ü¾ñÜ®´cõ~Úûþ׸­õÒØ‹¥”±«Õ°' T4œ¸Èïsâè Õ€b<ß—”ÏëÏ››cjêè\Ã8:`Ê£ŽžêaÚœõUëå»:IÌËцjh\¢†’y *55Y”…2Õ,Îgý óÚFÆÆvŽ[$æZ[ߣf!Ë‚{)’ÝÇòÜÀ綃ǹ."½>(¡æY­Ó@ º¿Ý ­.ÀŸ»ëcàvs–’[ËyIxEw;4ó¤‚(„ñ른ÐNJí&qHíÛÙvÓÑRI;W_I]ÍTÂJËÊ9?±®Þa½ïQŠ—B .e¥ÓÚû±%Ó“bU*¾àlËŒ‹éasBW<:²‘5¶6^h KávÛ]ª¤xã¯Å=›gdXˆ—5„§ªjØ}K0C †ÆÁ²`—DÙÎÐPx¨3Ÿ!?wðYRQG•ÒŠ™“tˆ?Ož÷U»qôLî®”å¹vÃK[‹ö9äöíl»ÁÚP;ß¾J,g—1ŸÞ;¼Ô¬ÃîšGªÙð®£Ø˜hÊáèèO¾ü1ýâW³P´?§ÝÝvB7»i¹n`í*6¶‘šÙkj7/¡Œ©š e‹ýj³'‡¶;/[ÁçÉ `§…P…f aŠ2]!Wyµ-Ë|66·Ò£Ï½GãÒE]è8 50ðWòU»±‡‡'êúœáÖnö}x'‡äÛÁQàriúµïÐóEµ~Üa¿ïÑ· EÏZ` G•›ÁA?ŽÓžz“~vÏU”™§ \sf ÂëÑ+KööÉÓ~yÕí6Ž—ßï< W yÂfŠîÎ…\áu òÜÅ'zøeóª¨Ôê¼ú+ùªÝ8ÂÃ]õa¯ŒáÒnŽnü•¿EaÚhšyý¿)L˜ÎùöêHí9 Ts—B0–’’’h,wÕ{»Kè/}H…c2Åz>™É±ƒV!-;ÌCè¡ûÛÅ#ÞX ?{Ä×!ø²=B~øAÂh+öÝEÎòê¨\Wù„G¶8ÁÝüJŽÐR>›]P¨ h¨î|fGÏàÊ5ð½ÝnãÕÕú,_\GÞδqGyºÊçPÚ͹Cÿ¤ã[å祩׼EqŽX¶×<*PZXX˜ˆ —Ù2¸TUMgN•Ò±Óe¼E¯³mßê*ê×D™ïüÁŸÿåj{º¨Z^ÍSËŽÒVßó£)9)‘ÒÒÒ„@E”~Ô‰¿“/Ú¿câIþ†ÒnjÏ®§kïì_ö4¯<¼Ì“¬úuÞ¨Š¶gîâC+D—3:Šçð¶¶Š®û@ƒK.#×a¾s|‚ç&¸Ì›‡oÄ‹.$ÃÃÃ-Z>h¨×Nx|Òn”ÂGàÖÕvSsæ3ÚõöÕÔÝÅÓFg|—²¦|c¢wþ‘=.PQ^p¼Üæn .±–ÔÀ«Sbí ÏKG^TR¶çYtaï¤ù¬›4R/[eP8ÃfŠ®3ö!LQB^o7ŒùJ»©>õ1í~ç:¦&!HÇ-ù£9 ̬¼"P ^h¦Š†Š—Âôüà’û"Ÿê¨&LÌZ×xAðႆ  ̱Eç¼Ùn wòël»©:þ>í^sƒ9¶éôoÓø¥O¹“€ÍËk)*^nh¬ƒ .¹Šê©¾G’†jpÆÏ<è'ïÀ¤¶Ï·9O·ë2Gâ¾ÚvSyìÚóÞÍ,Ly…Ø™SÁâÿ‰pÙ}f¯ T…¥âð‚x’ÐÍ•4|ðV»>ˆ¹ÿI*޼ÅSJoe›.Ê›ó#Ê_ð˜û à[u `à%ë@Cà\Ñ«´‡ççC˜Ž¾èçR˜Ú©@Ÿh¨vø§$?F ìà?hÿG÷ðà1Ge»ø×4zîÏý˜[ß±&ªï°—%KÓ»þL‡?ÿžðÄ)XøåÎþQ@ðí &¥@õê²L‰@ ÐÛÛMEŸ}›Îìù?áŽ7nÉØ×ô{À¹ïX”ÕwØË’%~‹@W{£p‹ª9ý…ð‚z“V¾H©7ú-¿þ˜¨þR’‰€Ÿ ÐÚp‚ã™^A͵GH™B3®[CÑ)3ý„;ÿfC Tÿ®ÉDÀ«`¹ç=ïÝDmu•4™f¬zŸt†‘3ãp¨`K:Tåýa@/Ûük:¾å7b$?yÌU4åŠW($,bX<·B To!-Ë‘ø)X²dÏ¿¿FµgÖóà/9ÿWÂÏ‹©Hr)PÃK¦– +z‹éµ³P…½tê¯R\Ö¢aõŒÞ|)P½‰¶,K"à'ôòl'tïÎú ÙKhÊ•¯’&<ÉO8 L6¤@ Ìz“\K\FÀX}€°öSSå^ÑÅ3ï3 ZÊ.¾Ë öÝ(êP”÷K8êŸüêq:¶å×"RTDlMºìŠåÕI%¹)P݃£ÌE"à×4×b­ô.j¬Ø%f=åL{€òþŽGñÕ­@ì×çGÌIêG•!Y‘¸ަü«ßÒÉmOˆ`ÐáÑ9¬•>/žÜ t_~R zX™­DÀ×T[CEŸ—ÚÏ­4{ê7©`Ñï¥o©+F T‚+³–øÖúãthÝw¨úäÇ¢øèä©4á’¿PLÚ_°3¢Ê (Úfj§/¶í¤}÷Pe]#µvô²Ë‡½úúµ8ùîÏÿû‚‹XË.\DÉqÑ4¹p*-š=ƒô:íéä ‰@ !ÐÝÑL'¶=N§vüA¬B¦‹¡ü‹¥,ÖLå¾wj3 jOO}µg?ýëÃ)1ä0¥Ð«)'ÖèJ¦^56¦ÒÞÍ3é“ tÃÊËiÎÔI¿ö’K`È›žî:»÷¯Â§´£µFtï3'~òýŽ4ú„€¾@z¿¨ÝÝÝ,ô6ÐÚ/×Óݳ\9d|uAFÒ…)™ŽRcp ½øN/56ÕÓò j™å!!3pz©ìÐËttÓ#ÂNЇ‰Ë¼˜Æ±4:uV€?[`²ï×ÂtóÎ=ôκÍt©áO¤ jv;ÊÑÁœ÷,°¿Káš7cªªnGYfènªŽ¿GG6üŒŒìŠJšÄk<ý–sWº»(™Ÿø­@E7¿±ÉHï¬]K‹#þêaªàA=Gû,½½VC…cGSLt”ìþ+àÈ­ÿ ÀSDˋߤ_=FM<Û 7¨±ÿ†ÒÆäL'Šÿù­@íìì¤Ï·n§„à"Š)÷8DQ!•”Ø}X”yåÒ…„uà%IüØH˾ÈNOPkÃIÁ’žc”æÎþ!eMù{v9vÀ PxðK íÔd2Ñ¡#)3x—×°Lã²™F—Ì›MaaaRKõò² {`’’}ÏÒ©O’©Ù¬TDÄ¡<^$/}ÂíRÚÍÇçüR ÂvÚÖÖFu-T¨÷¼vªÔ4áÚ¦VQvxx¸¨ 0rëUŒÕûéÌî¿PYÑ+ÔÝÙ*ÊŽJžB£çü„Rò¯çcÙµ÷j…8Q˜_ Ô®®.¡¡šº‚HÏ#òÞ"Œþ·w‰²Á´TIo €pzGW AZWºIÄNÓ‰9—PÎÌïQâ¨K½Á†,cˆø¥@E—¿££ƒìúìñ»e¢lð I"àiZꎰ}ô%*9øµ7Wˆâ´є^x'es“ˆØ1žfAæïFüR öòô'_ 4” $I<@§©ŽÎ~]Ò†òí–"¢'ò¬¦û…}TF²ÀP;~+PÕ ´èäÉÔf,¥ŽÖZ ÑDP¨&’¿ôfçëㄬ…T[²‰e÷ „²Õ–?hf2D€ÀH}õÉØÿ%ª:ñ80ÐFS nilú<‰U€#à—U-¦¡Ü'/š6½ºŒ"ãóiÌìÿ¤®Îf:ðéÃǤ͠ȸÑtzïsj³—é$CB »³EQØF«N~H]íæñ€ àJʽŒ…è”<æ ‘.zCÚnhš9þfª-Ý,¾ö)£W|óŒuÅ^Ûãòâ·iÖuoIêGo8²ÒÕÞ@•Çß禷©æÔZRb²„g5ÿÎ}Åöÿ.Á–dŽË˜GÉc¯£Ô±«H•åc^eñžF  ªŽ—½íîlSQ§ÕG¦Iª1™Ðí-Tsú!@kNÊöûK²à0JÈYJ),@Ñ×F¤X®Éá@@ ÔÆÊý­ª–‚Cu¤ O ¦¾`Žnª=ó9ÅÁPÈɤµ#¬†û5ø‡6Uíšgý¹­bÛʽk Å~¢+„¯h|Ö´¾,÷G-PëÏm£¤¼óϰ§†i£,Õg}lˆ/à€‡,3O,‰ììúìÛT¹ƒ(22œ È8‰…¼?‘·yíòD;wÉSÃSs™šèº×—m奖w÷³ƒâCµîÊ_,h Ré+:jÞ=ÏÐµâø‡”;ãAÒF& W©ºÒ­ýP±>ΚtØùt¿ëÄeÎ']Ï)¾ür•ï?ë´Úˆ$!X#ãÇSDÜX~¡øÇ[½°‘IÖ+ÞïénöOO!@Yµµ¯c¶Rdü8ŠååCbÒçŠeDð‘•=®YßñÐ>¥ÛV¯² 8‚ñȖǨ£¥ÚQ˵Â壌Œ ÒkºÉX}8î$ÿ°åãö–*þ­c;Ú:Ë=ØÁ WxL^?!«[MxR¿´òÀ{ ÈHKýQj©;JÍ<3 [ÌPj®-b‘Î~Œ„ébYhÎ6 PÑÔÙ¬‘F÷K#$!à×U®×Åëè¨!µÂÔºÌPm ÅfÌ?ë2LÆ!\›k‹-/jKý1¡ÝkŠXøY'û¡<é@•-Fz±Õ³íWoÈ2oqžË8|Ð÷Éê€ã|kà ‹°T(g{«ý)üA˜‹×Ŧ™µÏˆ¸|uÊT;øµ@Õ†ôP¯ÿä­)XkJ2øL*!“ݯ2/ˆŽŽÈ@­,X•—¹¹O+Â¹Ž¶:!hí [ÔKpp(iÙ¥ fü´áÉb„þŠZüøû°ßÂ=l$Ö–ïhå^~-•}û•læ)g¬oÖ8ÛOóì6ûñ0{vNaža©ôЕ ‹IPÊgõ0~)Pa·Â/BJÝ©¤çõŸ¼A(+’ËTÊw¶LÌ¿6$M?Û{»:šÄº?mMg©­é ™ú¶X3½ÍxV±ÏÇŽ¼aá5kA¦‹ÝRLc å•.Å–µkó6š·1â:<|M½=b`³ˆº:Œ JE`*ÛÖJË #G¼ãƒ“ËB“&Û³#­§Ú¿$‰€ð[LIqÉTV9•RBz ¢Ò®©””œ"â Bp¹“B5Qb ^ö‚¦­©DZÇ&€¯c; ¡‘±p±ìs÷]Xü`Ïu†‚C4ëÀ@¬˜îÒ·Å~0ÛÅV웯ãÝbbÍOÄ6À1Àë€Xd÷À¼mOWŠ·RwG‹yË‚š;(¶ŠÓ»3|Ã6 4~ÐÖ•-|<ãÍBÂ4ˆ…ª$‰€/ðËaªÑh(?+>*cw§žd·¬vêhh§U]4ËDÙàÁ›„e, ðsL½¬Õ±@í²¼í4Õ³×HüÃÔGó–M ¬ömù¦Av´Õ:ÎÞÃWáøŽn6´yw>Ñ" Ai»ÅÇH’D ðKJ:Žbc¢)7%…6—ßC‹#ÿì±…úL½‘´¾å[4.-Q”‰²Áƒwùû´5Ù×vâZpW{l·‹¬²ßÓe>{¥å§ëía›2–™5v˜bøC“ ™·p²œãkÁ!:‹ÀT§õv¤Ùª y~x"à”Ôð–ÖBz½žbbbhlvµ´éㆊÕOݽ`´ßÍ-÷PVŒN”…2Q6xn-8L?ÜK>#à-™á/8%P£""¨Áh¤ƒÁ£ü£„†KIIITÀëK.¡Í÷QRh1e„í+¡º:úÏtñK;§ˆn~jt4åçdвb¹L”=Ò‚G+Tf>"h46“!"|D=»S5%)JÊ+=.PQXÏÉÀ‚KJƒbªtt®^OÚÇRGOuö‚}gz),¨‹4Á½ξ¡ã£(9)‘ÒÒÒDY(S®%%à–ÿ$CB²"51aHyÚÍN ÔÂ1y´ûP1M;ÚãÏ©h©qqq¢,Ø4ÑŽª§ÖÖV±îVGu6²>lèÎcà +›B#…ÐÆeIíÔãU+ !ì:t˜ ½ +ü N§êÂYÓéOþ/»2UQz²ç§RBð)Ë9kµZ¡±644‘Í&“‰°2©"P•í@à*nPØ*ƒ^ÐFa3…PÅ>„ép´„‰òªD@" P€¨ª¡’ %‰€c¤@uŒ¼*HT# ªj¨dB‰€D@"à)Pã#¯J$ÕHª*™P" 8F@ TÇøÈ«‰€D@5R ª†J&”H$ŽÕ1>òªD@" P€¨ª¡’ %‰€c¤@uŒ¼*HT# ªj¨dB‰€D@"à)Pã#¯J$ÕHª*™P" 8F@ TÇøÈ«‰€D@5R ª†J&”H$ŽÕ1>òªD@" P€¨ª¡’ %‰€c¤@uŒ¼*HT# ªj¨dB‰€D@"à)Pã#¯J$Õ„ªNéB¶¦3d¬>@mµ¦‹£È„ “ëtN½½D¥•T]_OQ”™šLZÆé|¬o¨®«§„ØX ²>ëßûm¦vêîé¡Èp½3:DîW™èlCµuöPBD(MLÑS´.Äé\Ë*«¨£³“rÒÓÖscs3é¸=¡M!}}£‘’âœ.OÍ mííÔÕÕEnÇD F#EèõêQ‘HØå5¨—Éî•!œ,/~ƒNlý-5±0µ%CÂxÊý#JŸp_\š[Zè¹·ÖPdD8夥RM}*-£«–.¤ còl³W}ü˧ÿF?ùÆÝü…©¾ÇÕ„Ïÿk Ý}ýÕ®Þn¹oëžýÔhl¦K\d97\vZYxþcW-½²§–ª[:û=VHpÍÉŠ¤o_”DSÓÂû]³w€6óÊ{QnfuwwÓñ3%tíò%”‘’d/9½öïµTÈm)/+ƒ~÷Ì‹ôµ«.£üQÙvÓõäÙsá4)ÌP³ôþ}ÅGùù{hÚ„‚AÓ–àÅ·ß§…³¦Ñ¨Œt»Iù™² zùÝè¿¾Ä ^·~nº:šhï{·PÕÉ삎“Æš"Ú÷ÁTvè%šzÕ›¬¹Æ˜>X¿‰Æå¢ϵ¤ÛW|ŒPÁ÷Þx-í9TL7_±B\{_Œ9S'ñ×?\\¯mh¤Ù“&ÐÕËQÑñ“tŒ_¬£§ÎÐM+—[òêèìbý.UT×­äž®¥-»÷’&,ŒæMŸBøÞüÏs/Ó÷¾~+…†˜µ¤GÓ‰³¥t‚óC×_ºL4Zìƒ/ër׬û’v("|µº»ºé†Ë–QTd$=ú×çèÛn¤ø˜hzú¥×éÛwÜLÛö¤5Ÿ}Añ±1´|þš8v4}øåfQî¶}hÙE³-|¿ýÉç”Gó™Ç@§c5íô­wOS k¥ö¨»§—6Ÿ6Šß}³é?¤8ü8rœÂu:!ò3¶´Š1öŸ8Eo~ø©¨×+—, é…ãpZÐó«×aw’ëÖV ~ðÅ&Úuð0k»©tÅâ‹)ŽëÍ^^Öõ…ú¹zé"JIŒçöUKoÜB3&Ž·ð‚äûŸoàG8­ºt©(Ó^ž Ø"=ÚæÖ½û)˜»Wß¼åzÑlïÃÇáÕ÷?ÏÙØd¤zâ×ñGeýW;¨®±‰V­XJ_í=@ÁÁÁâcòÜ¿ðÔÒ”qcéòE ]ç>¡¾žž’laïËßß|›®`ì²RS,çË*«élyAw¿Šf)ÆïwÜfCíén§ío,s(L­Ñ¨9½Ž¶½¾˜º;[¬O_°ªôÉÉêw~Âè\2qÅ%ÆÅЮC‡E7_âýGމJ†PZ0sýø?îbmöíf¡kjï ;vÓÊ…ó¸œ×Tvóý£³3éW}“M )¢¡æðWxûþC¢Ì“%e¤×j-Â'ÑõÞÁ×ï½éZº•µ™O6mií•{5k҆Ⱥ‡5ÔhC$!?4¼:¾ÈUµu‚]ù6l‚õþ[o ÷×m $ÐH÷Ó×W]%ÊÀ¿6l燃0=Y×N·¼vb@ajyè¾g·WÓ#Ÿ–ÙžîwyšÐ y÷ÓõTßÔÄšUšHóÖGŸÒ7n^Ewßp5á£ÔÔ|¾ýÝ|ù ¡]ÆmÄšj¹Wt„?Äÿõà}4e|>mÞ½oÀ¼¬ë+-)‘ %‚ö>Âr2µ¶™¨ê{-·›Ÿ?p¯¨ÛÖo0Oq¡ï_·|Ì~ÿ½4‘µÜM»öÚ½‚òRVBðž5¥P¼HxèØIV.N‰{;AiI ´ß ðö›ïÞÏf°Ú‚mø 6¯)í ïØ3¯¯¦Icû SdV›#Þ-vh¦8ÁXÿs›@-Z÷5”ït Цªýtð“o9¼§‡^wÛl 2\§§ÑY™B09u𯱠 â¯6ºx¸¯˜µ‘dÖŽ>+nG„™_]…æL™ÈçréËí»ÄKˆ¯9^>˜Ú;:è é©üÙÒdnTѬi"?ØÜðU¨\åÞ‚¼¨¥B¨.àîÓ ®ÐšÇñyWü€è9´ñ‡ôÏÿSžÒŠ*þ0†ÒÙsåTQU#´LÔ‡=‚F B%’íøð½ÃÂ9.:š Ù:ÊK©/h¿¹7B;ša¥ C(¢¾¡I§²P»óº+æiÍã¼é“Åf h›ŽxÁ}ÂöÉÏÝj2 qà›[[E¯ ‚ô Ö%sg²¶ÄûxñÇ}З̙i1‹½ñá'ÔÒÖf°H#©?ç%KÿóNµÔSÉþ眺GI|®èjª2e•sÖÛÄøXѵ²>‡—Ý$| §q#E|ßᣢÁš:ÚE£iåŠG壻5™»1 ½Î:±¿nëvaCƒ–Ó‚B¾ø’ã+>yÜ…U£éo{uT®’çØœl:]VN§˜4àÖT!H rG‰¦ …zXB㣠¡ðøtóWÊ©€Ý¾¹¿ŽNÔš\âÿ+¨£†” ]Ùzþ0BàÜÂæ Ü{‡èêÖ³ðÐ@»Ào(S/Ì€Ï$²¦–Êm½ ØÙò¯‹ìáÿæ𣼔ú‚†ÚÜÚF•5uâCe°”­W¯ÓZŽc ‡yZòºü (0I9âE$äù¹Ù´qIežÆp f¨Ä8³¹M 8±rB~PF@¶ï >]l¶‚¶.É>n¨¥^¤Þç´ …T`éþç•à ¶ènì`ÓÆŒ;Ùu ^Š–¦Üko óË¿k´!›Íž.>†jòB7ÿ£/7Ñ]«®ì—5>¨ù,¬Ÿxö¤uÕ’…ªóì— ô\xaõ{BPÉÉäÁÒ63d›>lø² ˜Ÿ^}ïc øŠ*jjéa€ˆÆ²÷ì¬P6Íž1P²{Þ-nSŸ=@m®k!¡:Zñp«ÃJ€fßQ=5¾pø‚ªæ…Á@A /ä‹QJh› ßüåïôÃûî´Ø2Øwq ráߨtÕåƒòÑåRºŽÒò5øš^óÒ±!=Â’ÑQô×k²Ì6p@غ­IœgÛ*>bΜà¾gmƒw5/¥Üvþà£]ôéâ´«yڻحóWʶÝBÈÆFGYÛëòxpì«@ƒß×/EOOg¿cgz{7 §$Ä«ÎÚªaŠ ¡ñ)aІõÊûÑ,Ö^¡;C•«F˜¢E˜;Sf ¦íânøP©kª’/õíMè¼rß@[{ØÕ¼”2ìµ/Wó´wŸåCá==ICCÀ-U™NÍíÅ.s¢Huù^OÜÅnN×ô´ž(g$ç™é܇ÊVîÈÃ^¾òœDÀUÜ2(—¹ÀÕòÅ}jîÇ@5a´q ²çf…´°ÏtÍ:? Ttvw‰Á!Üc[6ò‚ Š$×HŽ ¥Ì˜¡ù,Îʼpú&º¸å?P~ïñ䨓žúÇkbpêDI©eT#¡ðQ¼hÚd¥h¹u Lí(=ïXïL‘šzdYéBûëA<‡ê©¼Ê;¦ »!\ê0ážç)Ìð½üýÏ7 e¸Åa þ˜ Áäó‡çþ)ÒÂA0˜¡;L*‰È6r¯õ¶¤¼’áߦù3̳Ù0v/;øoßwH¸òÁêÉ_¹ }!Ûö ûèŸyF&†Àë^!¯qijmfçþ©<Ø^ˆ?x’ÀõkãŽ=”ÄnS`:Í%gؾ·”ݱ¿H¸VéúÆž}ã‘í@Sw•2å–è|  h˜‰£–»”C\Æ|¾w`'õÓ<Ó Óê¬!4êo¿‰:y þ„ ­cWLáÃ4Q¸¶X«1joKöòÃÀ¦;¢Ü{o¼†î¸ö Ð. ÍÀçU­MÔ¶=œ³¡`ú(\p0•ñªe e³Ò…cƒ6˜þxE&š;÷ºÎæ@)ßœc®g{ÅB€Âq]Uôd³óþXLaæÁJÌhˆ áç« €ÂÅ-–mê 8ú—ñGÙ6½¼à‚|´}S1c£ ìà_+LÐáb¯})yÙ¶Wø+oÚ¹—0çþ0O«Å>üÿíEÖzò3žÇÃ0(ƒW˜-ÂäLp¿0`b\ñnÀPÈ1%$©CÀmU‘B³oúlÀ®»-;úèlšÅéu†LÛK1mN±CÁ‚N!\ƒíІ3ÙËÓ˜_ztÓÊ«jÄ œ»á:ƒ/{74tý% yÙ‘ôäY¤S×çåè/WgSˆ ÁnPS¬LBàtÕŠ%ôwßÑvÔ´Jž ’Âü„öâ afæÕ¯Ý¸U¿Iˆ‰¹ }9ÊüBÿÇM׉g;rê,EpÇã.öÓžÊÓE8ºÝr S]‚kL`˜ÏBÐ!޹ãSÀ€¸0#Àä¡mÏÖ¬ä%·fÔµb•hEÄм;vPî̇ ¾¥ö(8DC9Ó¿MóïÜE†Ä‰ö’8<õ‹í;Åt>Ø“ I ‚¾¼¸çdÁ÷62{„ˆc/??Á´VØ\ÓX»™T0†*kki<Û­0•¬`_…÷¤¡#°bl½}ûhZÊ~¥Q<ÇEýù’4úûª‚f;aB‚Ÿ`Æ“5A ,`„9újæÎÎ.¡Õn੽ÙkÈçk á:´@ÜA|”1©Ã¶}A㨽"@bRˆÀ*<ŽÌS²1i>Ô fA¯´i”gKÊ;A ÊAÇ|…°œÂS«1þ1"iuð»~aN˜Ïã£yR@.¿[ÖÂØ6ylFÀ-'²h¬GIDATƒRÖ`³ M`{jÎôïPtò4ŠŒ/`ÁYÈvÒ”=õ~š¸âÿ(yÌ5,pÏÏO·¾ßî> ưÐ01/¾š‚~˜q#}2 Öœô41¢‹¹þW.]ÀBQ+žXKXh ±<§Q{ÐåA ëü Õ¢ øä¡áøî~hÔ ­/ZN_#»<Ë“N!˶ÔË bèú‰q”¯¥1 :—NKò¢¸{ŸÈÂ4]ÄBÅGR !40ÌìÁ?8:*RÔ>’èŠÃn¨ ÓæØÛjž(aëpf°!ÂüšGA!ë4˜ Œ™W¶~ŸHƒ€è°¹CÃݺgÍ›6E„Š´m_²šöÚkÇ6r{EhÂËÍt´Ã \n—;1´¥MÃܲæOyG0•‚øò…óYxvŠòšš[9ŠšÉÒÖpf”¡Óh…ÆjÏW$ÿYpËL)KnrG"0B@¸@+I#)PGvý˧—H܈ÀàÆ(7&³’H$Ã)P‡síÊg“H¼Š€¨^…[& g¤@ε+ŸM" ð*R znY˜D@"0œu8×®|6‰€DÀ«HêU¸ea‰ÀpF@ Ôá\»òÙ$¯" ªWá–…I$Ã)P‡síÊg“H¼Š€¨^…[& g¤@ε+ŸM" ð*R znY˜D@"0œu8×®|6‰€DÀ«üD¨åvèIEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-flowns1.graffle0000664000175000017500000001136313553660046026775 0ustar zuulzuul00000000000000‹í]mSÛȲþœü Ý|º·ˆyÉÉæì†]L’»)ªn [€N„äÈ2„ÝÊ¿=’mY/66Ø``HU ÖÌh4š~žîžžž7ÿúq:—~Ò âè—WØE¯?jÇ :ûåÕ§£_×Õ«½}ùæ¿¶÷·ŽþÚvvwZG´±±ñ~ï•óêüö[➆¾x³1(2V íÝ'Bb¢ÆŠ¼Ù6w¡ŸÆ›ÿô{éèÖiÒ÷7†×ßyíoæ–Qn×=Ú£bƒg3%zÃk£nüý7ZsÐÏ5çoΡC’s!g’)ÎÖ©ˆ«”–ð’ þógéYLc[¡×«·ÛŠÃ èP¥ÊÎvQ~8àd|ìób­ô:ô‹’ÃÇÉ/B“ñ·âêØåüúvâ]Ý*:¶·?ÞìÑ( +~ɇ·çã‹&Ë–]z½ý$8 Ši2çl˜ÇßrV#ûÑfœEµöq½ý¬t«ëµ¡‰QùÄ÷·æXö˰(üa&í¶—úÕ®„Ù:Æë9X¾¦ä5ãÎ?üÔzg‰“jý^š:ÀK÷®¼Ä«VÚzÝлnµ½°vgIøqÚÎ/Îè÷j¿¡tÝ­Uö@ªe³ ¶·û~”Vt4¤6’ƒ‰Ù3Ò Ðå™V‘œ1ÑášhLç kMÈšC8w1V)É4UZp#\D¹” Æ' LZ %kß•¥«I¼ ù:÷º~“€ 1ŽÒè4ž,0™ŠW[¾ž8»8º-r•`¨hÃògÍå5—𱦠Ʉ J3Z©PHéØó5÷~×Z^Ô[oùÁIv*Íd¨üåWÍÄFŽÄ§t³*Z“Jê’üŠ~ôÎ"?]£Iõb2¼ƒüF諽¼Ó §½»êË-•hx½ƒQ:’”SDõèGŒ Lã›U%˜NŠ'Å%ÃRâF¼T³ô«/¿wîuâ«iÏÿκΛ&ØŸï[7Ì­*³,pxU 4úÁ3.CJL­Ù<Д0*x1Ðzâ@.‰üäÐëý^é³Zj¼ö¸¤T„æÈÿ‘Nž¿å«…d'é)>²ÿÚÝ3Ð9Èq;nÇž¹ÄËÿèõOào&ÑË¿O Ò“ðøŸF|¶Ï½¤ç§È©ÁÁ?Bù¶y{PáŸÇ‰ß¸>>K|?2¿œ„}>Èàcù‚ úq×K:Çé.ü1É>„2„°ìC!óAiV„ê¬cY޲"PÛ|œŠ˜vƒ$òÒ~â…ÇßÛæ‹48;OýèÔk £—/Í#žŸö°pŽÛ§Äùoý??oÒfnÍfJÓ5>\F8Ö\hª…Ò²Ùd3N¸e3Ëf–Í,›= ›©ùÙlŸì‘6™€ÇüðÒOƒ¶7+Ës"ð½Îä.ŒÃôœ.=ÍÂ̆ n ‚ƒ¾º‰ ¤áS¦X°~‘1‡› t * *c‚þ¬ŒÞ„Ú`>CmL\®#ˆWÆô˜æf¬­º&ùîhsc”sAÌP*='!$(ÕÐ VsR ;¼ Pý»iÍ|ÞL’ÒðŠP“Ê´ëŸyíëRñS/ìe®Í¢ˆò¸;hÚⱄóõäKЩðkö¨dVz€Î+Ê5Â-Eùо‘&Òðj—Bâ.Š>bVÑ(E_Ìí¶*;Cï¨è? #€ƒ>/SÁV„J:3vr°§)Ò «R­ÄìØÉâ ¦%Á¬9`ÍkÌgÈE:·ªü&yó$µÎ­'éÜbOѹeyÍòšåµGÈkb‘¼½w´•BLqN™´´ölh<É5ÿ”œ}SyaÌy·|^ÓR²â–Êòšå5Ëkóñ_¨½¦˜k8LHFÆLè5‡*â )± NÁLzZÄ6póÏAmÃUꙩ-sêÓU§6aø©†#¬ì2Ž¥6Km–Ú© £eÛl˜º0g#Eµbvùí .¿eìþ¼–߬=gIÏ’Þc$=fÃñnŠá©FS,!ÓÛ†ãUƒçšê$žD)UDê†p<ò,ÃñV{åÍFãÝ"¯¶F¼èh¼g†Œ5Îâ‘‘ù0ÊÕðIA¡¶–® RjÊÒœq=©6!Â5>LNÁBê̱I¸k`t]cœODåZ˜&ÔÖÂÍBçAϦš2$mƒ¤Ÿf´EÚ:s-]¥ãÖ:h5‚Zs0‘.çqF% (S¡R“Šÿ *K#éFذ„åÄÚ ·¾Iý}.P¹Ú> •·€Êškñþö“T×j¥lSlÚMª“!„s °Ž°Òr¥Ú/¯~pГLO’-# éèÓŒ[ù‚BQ“ÌŒ[ùÊBQÓ. Z_ò*ù’Í¿Ç(w%cLVÍ•¼ÿ¹åì0J²¤dλ$èœùÇÝÉfyÿN’uÀÊIÞîb&„^Û7©Æ·Œ··`Qª)p¨bRc.XóuU¤‰E™®³(&–Eï΢X[µ,jYÔ²è °hF£I|tüd9´;èÛ)Ç."@†X2­¤T&j HÕEœcÌ)VD+œÅWi¸Bù‰2H+‚0…òÒá8ª ‹ú½Œ»î/6+G©9‚³rpš#8+Ã$q7¶Ÿ•äÕü)šÞÜ‘›ïàö{P—âmÚž JÉŽ'ËýÃ0ÇÝ £ãŸ¦Þ àç˜Íï&üi DGè æUœ|;Î!V8›gp³3/õ§cë@¦:¤”ßÝ-•2a.™T”޳ÌL ¼ø„HabD²ÍXºhÜ`Éüý•ïø½F¬ÞËÂÍ`iòÁeTέ i‡ °o÷*,ÊHXîJÌÌJô"pö6>z†]I QDݳ,ÿ#Ü-yôÓÚOõ8›{ØN¥ÔSucp­W`(sçtc:¢)ÃMsº1À„0¹4¢6°Üº1¬cÎÍTl¡…ˆÍn~.…•|Â908ç@O&R”ÍŸÃ4Áˆ3ÅÒsÆçpJ96wÇÔR›¥6»gjNnÃËN‚a¶gb´if6k´Yf³FÛJ·»oÚó¦–¾çM"}ÛM;ÇN] ’.–& ž¸™sîj³€Ž4‚¹Ç$½¹ö3ÙL‘­ÑĸÂlÛh…¤ Û™÷!å|@+€ÑR Þ)}4{)¼4õ“hI»/¼§¢¦ƒÙ]ÁwDH¹|„T‹DÈ›öÖNGH»3÷1¸³,DÞ "ÕCm;«žWÊ•õŽ#²kN£Ë{IÉjJ¤n8ÎËÁü`†×f`¶GvY_½õÕ¯†¯ž,t÷™&®4)~`–*P„ÍÎê±eñUT»bÜëÁê¼F©åµ‡âµùí'¾Ü½gR …q¿³B습eAË‚—Œ¥©¸ËiÍÖ¼{D§5³êÑJÖ¼³æ%6KlŠe#±î鬩‹Kla'2ã†Óðø<ÚÝp–ž 7°á|ƒaÍa#²î„‹Ïi)n«Z?Þ³ã&žY|µi!òÑC¤°¹PˆK‡H®ux=n‚H21œÿkOE¶§"?Ò U¶ ÓјZé5GqQM…æŠb¢UÔ”ÂÚžŠ¼”xjÏs´ç9Z®Í±î\àÜþ•;ÇñûI²b!Y#N-¸6-ÍVysÅ\)oeyÓò¦åMË›+À›;Q/õ¢¶ÿÀ4Æš›1 m”S&ëé•ã᱊ œ$¹¬a„•²éOúA‚„ }rÉ W7ɈåNË !‹ Ž­˜jJ™Ã÷8s)EÁ T‚íÐd«²æÀ8¹Jac“CHcB­­¶€#ñ´µÕ¬­fùÆòÍ ðÍaÜOýûq(>³ …š ²ŒÌZäaN"Šº¹q VÍ¢!c!â6ç Í9ø4r²‹Q¨Dþ˜h fý]k—`/¤ œbŠi«¿/IV·ú»Õß­þ¾ú{ë*HÛç½ÒÒ°±ºzF†ÈR4_‚¢ˆaR2A×2D)Ä7ë4ä>YjA)§šÍ|7áúÖ{~tD~ÿþõËÚÜùò~³õÊüú#¼Úúmz÷wÁкËð`5½ý°@’H74Ÿ4¶ôâuÍp’áÑtš7i¨rÍ]"9q&™âlÍQ » ì.ø IZáϺ`&â¡ äæ7HÇc˜ ‡F\æT8Š»Ëh¾ŒåÙ‹Ø|ƒ"tCßëìGáõÍðr_•³¦¨>P´¢Fæ¬C ­s >i ·¼èÒë•”–¼ÆèèY7Œ«z›msˆà š^VÙ(f§ùœu?EÁ÷¾_˜çSžþSÏ¥A2mØ?Ϫt~ "0¶'£úV?I`$²‘¬ëì *ûû]/aû¿|ˆÇtº‚‡þޤ„TÅj¥fYNg1Ä›h’T¶º¬ƒ’Kå XJþFéúa?,ºV^щ7v¹ ¿^Žïžöð u­Ð;ùàñ®ZH¼ªB XúÐ?+¡×h Ö¥‰Í5!% $U¨a ¾ÆñEU÷tyåF¦Ðg,Þ^#4„7ÈÇ‹Æö‡_Ra4þSöBðTƒ’Ⳃì¼}ùÿZȳI$neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-flowns2.svg0000664000175000017500000013356713553660046026662 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 18:08:42 +0000Canvas 1Layer 1Network NodeOpen vSwitch - Self-service NetworksNetwork Traffic Flow - North/South Scenario 2Provider network 1VLAN 101, 203.0.113.0/24Compute NodeInstanceLinux Bridgeqbr(23)(21)(22)VNI 101Provider networkAggregate OVS Tunnel Bridgebr-tunOVS Integration Bridgebr-int(20)(19)(16)OVS Integration Bridgebr-intOVS Tunnel Bridgebr-tun OVS Provider Bridgebr-providerRouter Namespaceqrouter(15)(4)(3)(6)(13)(11)(12)(8)(7)(9)(10)(5)(2)(14)(18)(17)(1)Self-service networkVNI 101, 192.168.1.0/24Overlay network10.0.1.0/24VNI 101VLAN 101 neutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-compconn1.png0000664000175000017500000032700213553660046026457 0ustar zuulzuul00000000000000‰PNG  IHDRCN“‘µsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|EûÇgvïRé½(UPAE+bÁÊ«Ûk{_õUÀ  EOr ¾vJÂk}ÕW±wQlbCÁRB¯ ©··óÿ={Ù°¹\È¥_’gò¹ìîìÔïÌÎ<ûLY!Ø0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&ÀꙀ¬çø8:&Àª@`¬Ç;Ìﮬô”EUðÆNë€ÀxÏÃíò|ùÜnmûLOÒª:ˆ‚ƒl ôœ)¿¦ÏIKZÜ@Iàh˜€«ãçè<žÏ][Ä’þÊg%t™ïò«å³ÒS6:œðiOfœæß}¸_úûh¦¦„[¤îþsŽçžíANkõ215ý:¥Ì–YÞÔÙµpP`†¡>„U¥”&¥TA·ëõò¶ŒŒŽF±liGª#qÒÕzמÛsl»H?Þæ™~H‘Q8íÕ'ÀÂPõÙ՚ϱ)Ó÷‹âÌlcÑIB‰(+`¿>œŒNIÛ+•|!¾}ܽL˜PPk‘6ò€n{üñèâm9÷(ߎ ~¡ÚRvLáà‡S%¤¤}§iÚýs¦$ϯí¬RÜEÛržFTî„ôô·šKçR˜o>"”ºÖæiЉoŸk†þÄ/³›ëç<ž éV$šb¿o´R"YH_¤oL$¦1Ü4•–‡4vß‘ž>à±äämùÝá[ÜÝêO铟ÂÍÙ¹c{&Ð\ hÍ5ã‘’ïÄ”´ÛüÊ÷èáHÓŸxûNJínüã5Bx½uÿî¼¥ã<éÇDJº:…Ûræ˜B¥A if_K)RKÒ„œÊ ð<BQQ]¤ó‰Ûo/)wó%•é€èm}t²wÙ-ÉÞžuw$„ Þ P?gK!³Àý-¥äZð>¿9›M?ßæy¼U$¤3d¤\(¤\¡Ž„‚¦a”h—_`>Yß™IHIOÅËÚ´úŽ—ãcuA€5CuA5Ì0G§düÍTþÇáÜŽ%¹›+yšÇ#M§wêXŠ|9ÿÁ0ɆaοwúôÜ81×馹'¦fœdšþÐo×\ú‰s<“ÿ f0Î3õ°NbÒZ!RƒoÕÊ5†Ç.T6ü¢<ó¨§ ·]+E` Jʧæ¦%¿ìLZbê´ÁÊ4^F==¢ÐÈ™ƒ{×8ïGÊyæ”d‚zEJzj#xvB½<1%ãÒLoÒµf8aà%íF!ä7á¸e7L Ò °f¨JˆÆð1Fý E¯iâVLÍ„èÍÇÀ½+Ñ཯ÓÞý¾Id_™™7ožNqTæ.ø>ù©Ž?t‚’†‚ë‹k%Ì­p•œJ¢{³<“ׄâYé)¦T+sÆq«*s*ß0‚®7'™i“–IÍuE(…ºØãQ•¶-4ßë` $.T¯æ¦.ïÕ¤>{<ÏÄT'mT®UZº»(.%ü3ÇNj W'nÛO8m@Bê´£¡]ëmû©ïcMêFuW–?¼¤¶lȺZYúø~嬡©âé«K×µ³Ó&/ Ž‹®ÇzÒù sºÙõYÞ”s¡ž¿ “—¢aÊs(?N»qž»FÑð¿ þOqÞ£säg$#ýþ6­ÜÂ5ovü`óË\oê%~¾Ã±UfZÊ‘ÄuLJÚ™~!¦ G²ÂGÚÖ‚…U¦˜Õ=×›rù³ âì‹s/úúãpì‹ €Af#Ž»»úóx®,&·à³7Z¡ô1©éWb:Ô?a= a·&ˆïýxwÜx&ì&÷uiF§x_°æ iÚÕÁš!;^¤wÒÔ.Ê­n¯¸ ÔI3Isi—Ÿ9 ßo –­ºî/C^‡ îü6}lÿc=Sûø #×Cðë‹2ÉUR-פöBfZr–ínô}éçKÓ|ü–ÌMO¹Ñ¶>"ÍYÎNWB¿w®7é;-`:3Ó›úD°{Kkä>ˆghîõÃo;X¯¹äÊPíð\ÍÇ‹ÉK(kƒý&x2ކi—'Àÿ¡¨ÙpóæÖï%´[Ï‘Ð~Ìò&›:uˆ¡üãñ„^€g©-üÞ€ºý\pÎk»G¹ÅâwŒ-9ü쇘ì7ÕË º{Äÿ„Ž8°¾=ÏÈû¦ªÚGصzJåOb…u’–1•hKüLSôüdÙª'‘æ©Èw[ÔÑOáðKÛ {„–¡<þ¹Èã ø}ƒ0Ò‹æb½ek“¤ÖáK¸ë!ò:Lîl‡á<Þ’‘ÑÂÅ °;´mK|QsJÒ‚„rõfŒgj¯B_Î÷x^©\ö‹ø}Žº}8ÄÚ…ð^Nˆ 0ÉÐ!ø•4|ú´5™¨¯«‘×ËLŸÿgËg;§AÞúA¸2.eú‘~Óÿ X\‚ò]ç廓ËÔ§¿às3ÏŒ‹“·PÝC¿~Ì}éç»9ØuUÚ€X÷Ζ(›tR¨rÕñÒŸ©ù•7…QÁñ’P&£è>¸!ø>im ¼þö}cô?Ù÷«R7l?t¬)c¼$’y3òôkW÷)¥ó£`ÿæ$>…(ðR#¿F}z õe'zÃý­BúËÕ/J›È%@š6 C€4xXåáF{5Üžnšò°ò~ÔaÒ”·iRœ5Ç›ú…}ÿOFŸÏüäEÚÆí7À~®}Žûr‹'ãp.æÿÅtju£59wzi“oä½ —$¦¤“6â9ü%A§ q‘¤0B𬴔'qãIˆßãx¼Q·g¦—_ºMZ*Ÿ¯ø?Í¥Ius¦7…TËÐÛæv1£“}mw+Ô”ÉR—£²¦¤¼oÛ`-Ž,Ú‘3öÚ÷âÐ^úH[°Õ@ƒašiè°{hºv^ð*¿íÝSèt`´K³¼IoÙÞI¨ñå™$t_,Œí÷ÂÞ“åİaØÜ”_¡RˆÇl÷ö±¸ÀD§ªÜˆóå`mŸíÆy4 c*®ûCz ÏÂuö³@å²Åðz!HѳRÎ@®Œ‚gpCs¹Å©³=)¥óh $]Žy¯ú•ÌÄËÈÀàTê>†(þ•ëãÖ­£ozèž{òÊEP‰äü¸Ç’'®GݧÏBpÌBÝNž)誴%+Ö®L¸/ýBå7ß߯²ÒËjÉu¼rž¤g­Œñiâÿ°ìÙ¿ ìަöÉÖ ’ÿ¬9žê;*Á"{ˆªÖ2⢺ŒÑÆÌ …©HíüîîþhŸmÕÇâ<ÿ­¨W»â]qýZÙ»g̈ÏÝ[4sÓHxfÓˆ°f¨Á KY ÔÿëÃM¾€[ð[ΟTO:!ºO ⸃ÎM%“¨a§s2ÔÈ Ç=©\.=ÅnüéÞ£žñ{ñ’?Îá&…ŽåŒ”³œ‚Ý·`)_§sS :¨ÉòLÚ€F“ZUÇ­Æ’ó‚¶bõÙè»â }!TØÒ}4”J‹‘ÿ‡õ[Ü;oÕ‹HIHM¿¹"m”®[Â^VÅ™Î8¶‹0„bi¶^A'øZÎ 4:Gã|âÊêÐr‘ÓouÎ Ãwòä`—Z*QX4Ç)¤{Èò¬¬)É¥‚¹w²& Ù5”IðLë!LÃÉ •p<uñ†`AˆÜ‚ bÑBjâ1§ D÷f&%튎׮Fý/Àå=·z¦w#{Ţ­£0ÿ8ý7…e¯ Uâ.è¾ã²DcHsóöǸZßä|¨\ººRð È5/N‚$êÀqÿU§ Dð|†/ ª¿¶aÇ•<•œaHgÛ0xuu! Å”¦5?)0¬&ç#-=öåøJµ%1…<Ô¸ j C[$TçDOƧ¼ô\Jå÷A²÷p÷­ç½ù¶uµê†í™ŽÕ`L+å aê€øJº;^êÔÖªbuhIðß8!²£2ÄPÚÂ’û|hDJ;ÆF”æFŸÔÄR£Êhcgø2-·NB —öF¨°0ç}<Ô›Ñé÷ÚîKïn»1ü¾3ÑpÒX÷ç³=“×Úöö±»>i)îãºïø‡ÆNY£ ½ŒžëÒ…ÕiK!zÛv;¢Ò[5v ò—ëÔ04°SÒrc‡3'%%»›ëðÓ¡ »iü y;^™æ\,íÞH Yð„d3Îõ:+?„hp‘´€1…v)™B|ÝÓ§§µÌÞyFà.þoØq"ZÓx0ÿÂÙA–Þ¯â â>§ÄË̪x•.yPÖH_X¬«gEn¥2/¤y[4t!ô‰„ï|aøÖÝ@ttïws¥L å÷~˜ãM‚6!”Qg[¶º+¤ õDRÒ”ókT_}>ßpr‹¹B_£L1)¬¡&g¨´šå6 RäºÙi)_:ï…:7Eñ©UQ¾nk$œîH ‚@úŠÓÎq~ëR{ÊaWzŠ¡`¤“–Œª£K-'šö øN«êžK·+:Ôç±cS3N«,œš¶…E!–Ú£3m74œ‰ïdtQT¬„°CyV—Ø÷éíïYt45½T‚›*× £Œ ƒ1lÖ<£[0T:¿‡`|i!au'¯@]އЋ鷓öÚyŸÏ'WãLvãN55¬˜‹³ ¹è¡ ÑÇìpr„„nРÅB»iO -&þ ?Ý”¦õÂq£å_Ñx7ŒžÿY玛 /žw‰íUTAN¹]á¸-¢bLÊC9cQ»„(¢p£ÊÝ aÑÕÝýÍlcÓ^Üú[´i­³&MÚGÎHå¼ooÑ%Ô¸·jýZ°×’·5 ­¨§Ç¦zG@ó5‘^F Y¶oñ˜ {®Ý±Q˜è´1|'N÷@uJËI(Âà%èdwŸ3¨ÿ·Ÿ._ƒžý· ¿Õ@Bñ¶Õ˜ƒ¡£qNIx×Ößò?zÁµa]U–ýǸe Ì‚¢²Y£œ¢ƒn•»¤ü&ÜŸn Λ.ŒÎÍNKúÊiw°s0¼ý~%ue´T“ú´9i“߬¨c‡ú“íÅy´†*E=IðμâFh ·çr5•z¬~¶%:×gPFSý¢˜„æ$ÛÞ0Œ@ú”x¶¢ôØnéI§—õl Š#´A8›À°ÌÍ’aÜ^xÄL¿òÃs4®Œë´ž3”Ñaåï ¥¹Br å¶2;Ò´" ¨žDzžÂKÌàƒnÖZÃ6 ÂôXCÊêîMì“ä®°P\ |PŠH¸E:€õ0’høæ½nßw 8íêF/b•¬IÝp¦-LÆù‰÷¥Ÿƒ‚| õjŸ;N;ÿ‰¤ò»«Ópæ€]‚zø¾2ÕcÆ—‚¹˜·õÔÁ6¾t¦‡Ï#€yIj&)R’„ÌÁ=ÃαR=È-Þêúnb ! ˜ít‚ _–Á¹7­Ú97ämÞ‚öàÁ/'Ø´õám½Œµc±’/#q1r¿ïr;ÈÜœâÿCŠã‘®yB Îßa˜àJ¬ú9 „¶Áß°"cŸÇ+p Ìù1 1‚®ï›: º#üw®¼òJT§ŸZoÕXO‚¹ÁõYtºf½éZçÕü÷ŲU4)Ý…ø¶‘@\•`Né×ÏÒ VÅO°ÛW_}Uƒ ÷ZðÏoš÷»=Ø5˜¤c–ëY¨§énWïîîSb0„9CBoLðÀÄØ­¡ÂÝ%¾ëB\PGwÌ¿&Ì€)¬ç€ÂŠr»ŸG)A`×ÚeˆCB’ •ö¹PqÛAGИJºÚ ßåî}õ뺮¨C… =C‚èÂܨ;Lð¯Eƒ9u³ X~¦ýòvç=@AûÜ. ÂS„ÐÑ65mìp‚]µn_!¦BÁp{håƒçÛ¶›âÍ€{ímŽ Å(º.Ú™{2µð·À~>jR7qþ‡Ã`¢”ßÒþa̓jí+0¯u†á<·¦#¸ÜXIªÍF;À}:6¾„ ê}»Ò—¶±N?|ÙX3ÔpåCÂÐiZªó×ÃLÆQä3fB C›Ý_wÄí-!ˆ­¡i Eäû4Fd<$²Îªðïþû…òxªàá Nu·x$ƒäÐþSäcö·û’a´ƒx/½5Ç“òvõ¾“]i…5¾ì›hŒ?Á¹G Kuÿ´@VãŒá ‹? ƒAƒö8]>î¾iÇá­úWìüMC-ë³à—χT%œ+®¸¢JÂS¨°)ŒO–¥ß|Ë¡CjƒÝÙ×Jj+æ¤%f_‡{¬HÐi/îݼYz÷£Ci°°üBë€Z¾ò€Põ¤gâf¼•Œz|>´ƒÃáÿ úè&VöE¿ÿéÌô”õ óÀ=e ›xÓosÀ®ì4±x€ðw`HËÕʽKìò¡Úª|‚Ð)`N±k…(Lš{€ø·P£;ÆS~Õ¹©KC$æÙ<üƒ`3¯.ã­,ì€@þÖ14¡·"÷¨¯½¬{ªÜPÖ3dù_WÑBµõFÆÍ²'»Ê jÃ_ä+ü©Ðm‰öÂX©v…† €u#Ê´}¶B¿õ|ÃZ8!4Zû#=­ùbCˆB$—×i`=7~ežAh$ÄHhÐ/„cfÚ¤•HÁÀ<ÃZ´ØÛIȘ«6k¡nT¼ôCËyöd[‚±¼[èÙ—¦^eYÒBZMÚÍÕ„½×À¼#>æ öl†¨´hÅ :'tòªcξÂroëÎdQC) S,;Mxìy0N7Ö¹aþ­œ,h4Ö= Xlï"†m*uãÒ¿…4ê2Z.Zjß@'RÐy)YdW˜.S:.Ÿ­jrŠýù–à‡FíCCaˆã34ríîÏ8ótdï;'FǸ㱠¤ô¡QÃ08Ãò­XÙr ´ ÎhhF“FEútß4Н§#›€²Þ¾ýÞ°Cza€p{¹U_ÝÚ·N'Ñ[a?±T/óxæE¡Œ¯ ¡7¶}\ÈN¿¥ç¦ø™Îá÷ÿ*œ[ÒY—ú)=‘Kè´È(N(µŠ€“³õ{ˆ41xöÆ^T·“èS.YÕlðlXõZ²Šê¹p¹B ®Óïy·K¸Ê” &ZãÛvXEˆE x9Šr[A‹#ʦ³úu£l8¿B[Mw`ù<^ž»çÑFôòçÏÜgà.ÍaŒréÉ+l*ɦQ`a¨‹Ks‰»¬w3%îÇž)ÿ µb‹6‚Sv,‚&ãt4¿v×Oɪ(ɤe¢ Üœ÷IÈzÿß;íAûa§ëLOÒ¯hÌžBЦ8ßÿl¨7 Äh/g˜uu'ÿ‹ÎÎ@›t1Òu)u|Zlù94ɱ" ‚µç‹òͦ4âmßÒ¾9Ók¯rÁ¼™ñd/…^fˆ’¶€å—hOEÏq:\øãµøOaTr¾Žîcò*´Nå¦é÷S™£ã¾wŒÇ;4ØEDä48±µx=3Q¿wazôÝ žôãœAÓ$Úœ½E’‹zñLð% ³èH_B§Úa‹oÕ½(yZmùÊA';#À¹µ2-0ϦǧËVMºŠŒ‹P'ζ§kÝ¥'¡Î¢ÂÝMß å†6ç™Ù"Ô½º²#á_“î›ÀÕ‡´%…Чºm†’¬zŽš|íI*ìÙžäåx†·Bv*üé`´r¶wâN·Xigib1@Øç“¸€É—K S¥øüŧa®ÈOЬ…Do½IZéõž‹c,£Ï»é§<œN•%Cbê¸)hÙÚýa°Äð:Þ³aý…ÔK@*ï(´&_$ôt>w¢,{‚ãgÐuÂ'ÒÈV[ý{|îAL4±«1òKñ¯D'ß Ü‡û:âº-~ÍÊdzï]ðŽÂöŸ*Ã\Œ:ü*¡3U°2ð”É1ÔÁ¸\Q)¡À@È|Úo·¢>L&õvp~:”»ƒÙ¹\ò^¿¡æ›J¥a¾ËéÐÜ~ Áœfºg ?öÑsŽ1Áaж(Ç qñIÿÛ8ÿåÿ:öýðÜ î)ê8·±Ï2ö²ªGCߌÃÎôÓ±ÉX 7ÔÈ S6`Öý“ÖA@ømËPlÎúžÅ§ñò‘§cwxZÌp ‰µÛc fë8pOˆ.úä%Ùfú6<—#ɼKç ÙîjZ7ìpªz¤Ec<øŽO}‡——i¨Ÿ_ÓšYlöÃnã‹‘ç/±2n)êÇ:„Ý:ö0Ïq8ÚÚ|,x´ªñ±û†%Àš¡†å/¨áˆwµ8Zr’²o¸Ã0¡û\`"ž’}ðfò½.EVë\œå¹{gEÉECã:{pÿsq6lh%Ô 4€Ô@·ÃЇÌn®CNÞ/ƒÂ¢ íÄ¡NÀ$Ú)pëGÃ…U9â~L@¾ýÀŰûÝž`\Qܵk¯?c½!â-Q“e÷*GŠWÑI®€»ãÐña‡_qG ½øœ#¼µ&vs÷?/”àŽ¢S]k…!/Ô*µhWÔÛ¥q ­\ã|à^ù3kcD©%¡SKq%X¢5ïvºÄ÷Í&iº$ 5¢X¹&’‰;ºì˜³ätß\Ω£¡]¶Q—±;1u jØÐrw”Ù£ÝÜ݇T´)% ™è„0aØÚ7ë·¬´¤2Ciá0¤ ¡¡:ž†¯Q¿Fà9ô¢£Ã~3´’þwìB|Êï.å êÕlMº†’_ÔÁQ7ï$Ç¡.àeD>£âKVÕ÷^§6Ýõþi$HVIuÚäG¹¤ûŸàñʪ/ò™Ž·”G1ok°3”GÉË\Jw-,¹£9A(ïwéi,@;µÎƒMMêFpXU¹žãI^¬Þ†|¸P^!­.Ü´Q,µ'[3šjf@ØT´"÷KÝ%ÏÊò$ÿX•xØmÃ@=dIhXʽiOŸ.rKV+TšO‡@^Øn›U ]7ì´[Ïø¶œ.†[tt -Ç'ÚoŠ”öÇN#™@³"@ÂÐèä4¼ XåÒ¬pf™`L€ Ô€“Õ{eL€ 0&À?†r˜`L€ 0`a¨ðØ+`L€ 0&Ðø ðÒúÆ_†´ ãü«×=Lš6Î`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L 9ЛC&9‘M€¾©¶7*ªóñ'Ùáû¯>ÛÙ©åÔ5ÑÉÞoO>B-]øùOáÆOÁt©=GŒº øëO>ñ…ë¯)¹»ÓóH›cN>䇅Ÿn¬J¾R3NræùEK¿ø8¿*þØ-h¬xêÆZrM Ýã=·KHI2!Õ»;?ßÜZ,Ô_ ÉÞèø>šþ¯&ňʂǣïó.Õ1Jh]šxŸ÷â„”´¯÷ïÊËWJ¬Ù··h®—£n=u«gz·pÂhŒnB•o/ïV¿i~•0mZëpóäñ|î¦ù¥0 ’œ~B…ï¼ÏçL 1h¼cc¦Îi÷NŸÞ2ÏÈûH(õ/)ÄšÔ/t¹åIJÊ4!U®ecª]Ù†wýmžé‡Ôn¨‘ZBjú5ÊTo(! MŠény¢&µ+Q¯>AJ[w÷n¬×NjSÒnÊ6Ò³‚CÓ¤ûU4òwgMš¶ÆÕãa@lž ¥û9gxÍ¡þ8óËçÍ‹Ž£y•wDäÖãy&fsnö{HÌ¡.é2Ë;ñ7G¾ÁùcŽk>­cR§ë7&-A#4J™æs|í®'ßãñH³Ýw8¾8O©š‘„¿¿!U;‚S6Û;ñØÑ¯JfnZÊ,§‡æPœùåóæG€…¡æWæ žãlsóh„N×4-qVZA¨Â´NÍ.M3þ hŽ|Ð&ýàQ“‚)áoð&¸Œð§¢ë€ô¾kJZoÛ}Bf¦á.Æõµˆû=È#w %â Óü<á¾ô¶;„ƒ´V¦_ê«×¼@:Ôã6òBX´ÀÄdïé°ÿP 3V“2IH-l÷ ì^ BV8ªø¸Ah¾Òä¿`÷Üyþ'¬ûþe ñ ð‹ [óPv£!<}ß­{sœ A“}Uù¿a^=HË­p¿Åð/Á{dµbCeƒáb®Çse¥‚S•ÊNˆ°ón™PNÂ-çÛ<Ó·à}3¼½6 „æ@JÝè–²uéyä†ÿ~¦²¥øþHqPzÀ}¶nµué0¿a^G×eŒá¿~ú›-4[Ø> ì;“›ŠêOW­ÛbÄ{¨iú©üË<+×àÞ‘11ÚÂ27ø‚ D(Ö EhÁ4åd¡‘ Áfí•W^ 9ààæî3âsöÍPB¾2×›rí“<ÿ»ÙX´È4ÍG!| svÜRÉ>.wÛ³<·ì'÷4æ)±Ö”blŒÛÝ÷ ÏÄMdNæ•¢íûv+á¿ —o(­ÐÁ¼žéM¹?`#Þt?@¿ Š»aG?±ÙX} :—ÐP th¨Þ“’ñ½_ùß…uA¦7é; „‡y,@ã’dÛ%z¼ LŸøÞ'äÿÁŽ4FBmÚ1ÿq¹µcgy’.qûÔèï_˜Ø:×·rF˜sÓ’¾Ä„ô®Ð³ÜíŠz×Î?…‹r8‡ÝÝ])7:´)OÓ½ƒ™Ìô”WpŸ~¶y}tJZ'°¿È¶pŒŠ×zÍLJ"™W ±h -•çËÿò|y·£÷î,eÔ‘™i÷®.±ktª7i'ØvZî¸E¬„ð¢1÷lU¹›!,ªZv"¬@þ{u9üꄬ[4Ì€ŽæOh[®:à6p¦\úóN»9÷'/…Îc§)e/ÛSW£ãÿÙ!Y·þp2ÄÒØŽq 'L‡ó2§RèËÑywØâK¿ÓZETænÕ. YúeÕšز>åûAȺ!r>RÞ’VÚnawX|žé-„¬[Qºö_ÛM…G¿<‚îÁXõ qW±ìÂ˃¾pÊ$Ür†àr9ÄÀ/*„ì(Ã:B«ôʻǘ”ôÓl–vU©Þˆã9Û.ì£æúžÍvÙæªQ¶Ÿ±žôA°;J׫ž™@=`ÍP=çèÐ µ„na±Pª/¹ÓÝÑk‚ÝKM[¥0QÄo‡áÞZÇýlǹu A&o¾›ƒíÑäÀ®U°}L;÷†`;M«MS&Å2èTúBè)Õ(Ùö%Gh6¥«ŒiïÞè´ ·s¼­ïC:¢l{ðéÁ'š–_m;:¢S$ÁAóïÎ#á­”G8a’ÿPš«× UHG^26ûOÀ¶Y1ñÚìÊÞèiŸüÝyWA£0éí…òlôu 84Ó,ó’¥Lž­tHmæn‰"a”æö=!Δ›_Õ*fcñ®<Ë[Eÿ¤2­²…¦0¬zUÕ²«B¬$†S&á–3i:!å½SQÞ«bßUKú|³™¾‘4Að·ü¢Ü®…ÒmóYƒÿ$³*ÁmVÚ¤_P¾Å0踴ÊÎ0Lh™ä®.Zÿ÷«;g F L£Õ`©àˆ›4”k¡½é5oÞ<=ÜŒËh£ü¦yº²ìð¶í FóÚqn8Îzªûýåž hâ!´¸ÐyàPbT öeéQ š¤\&]toÆÝwç—º©à_<:§¥˜Ìú`™Ÿ&'éšv£#Ë =„fQYÖYÞÔ'{chãdì΢|ÿ_4O«"? ž‡:ìß•ÿ3æMð²Z:Í?J„ß§*ðSižKüÅ g. ãáñã ?ؾ̵Ëe ØZTN-ãÎyQµ² 7V á”IÊ9N  jnC¡êyh-¯ yS¤ D}¾ÏÌ á [‡JÊ}.žçsi>]àÙWAx{Ù9lÊÛ1H"àŠ¤ÄpZš¬žZìWâúO~Y}=r|Ðù)¦Ð—bËaæËp[ºJÉ"eXvPEhÖ6¹ü<£+Â,Õ¾Xá+5¦¤Í±®¥Z ¹ÚŒ&°Â*¬ù+Á¾!”¬AGY0Ç›ül𽺺ÎJN¦á¥I·yÏ(òå¼db{t–¯†êД¯pô ý±pdæ”äOì4aþæ^ÐØvU<®‡ûîÁ~Æ>ðPG„ ÁÝnæÌ}7.X¾j#„©›11ù¡¬ÄDKP®À9‚ª›²«0¾7Â-g]‡zFõ¿VŒCµÅ>òfsõ9PvâAu”ºþ\u×]í^ñùö<â7Š®sßÔP=•Výðª›öÇjB ÜÛoMc¿L 桞Eo´Ã$Ðíƒù‰é¿} ÞŠëʹSæuL6v‡•ZÊ9¬º…á÷—™<Æ3£†5 ýº44%¿E÷<’4%¥v8IHv4†aceÙgNûpÏ&­œ;ç–dohA+܀ʸ ÌÑ*öûiˆ­Bó„çöäñqäµýVÿŸGUà°7†öwÕNþܾOZ>tÚWÛ×Õ:‚'ü žsdú‹K‡&+ —´˜³t =‰M;¦”ÑÞ…òTGe*ªŠìÂ-g¸[ùûÜàzæ Ã^[àî ek»ŸéIÂöb‰ò«Ë±4Z!ñ}¦'©Ì¬íöÀ±âúcÍÏ“âeÐW(å¿ šß_ç¦Mþþ€_>c‘O€5C‘_FM.…ôÖžx_ú,wM‰¢1Oe†Ðå÷.¥mÇ2ÝþXNŽ Öú[4a”&9c%ÔXÿ<4;¢¤zÕ/¢¢Lé»þ/Ã2â„Ò^Ôs–Ëß›˜š¾;Jw}æó›M£èQt†péSí°[·‰þ7VºRFÁGHÛ.wÔÓô 6ýÆ,„ñ{Wý”ÿØn«tláN—û}ÿÀfJŸbEÚ$ŒÌ-§ÞPˆ­wt kyªŸsôa‹>YþÇnlJx„¬»4·ÛO“É‘îëi˜Qéra¼ˆÙ^è/<ÊP&V©±m£îýaï†ÍÆ×É žiÏh¦1dÁ²U7ZB+$¢ê—[Ÿnøü×çì+zcœ'}¢&\Û}†ÿd¥Ì)àYÑÐgit]ô¤ç³ÍôKPW&¡ìŽ“šþ¢_h+5i¶Á*Vè¼¢úSêAês¥2ð¢ {á Ø0ÆE€5C«¼šLj1¼òiŒ+êhdècün~óÃ4~ÀÛe¦©Ô…xñ3;7-û©±ÐX\U¤äŸ†*þÁ .ÏLK~ÁvW[Gôç…ØHnö*šTè+Þˆ›DØÝ1tuV–'©T@xèž{òb㬥äë ÉyÛç+Ú‚ï@½¡à{w¼v*­x«Nšhy³#ÇðÅJ/˜ªh„ù˜ç1¡:á‘k>ˆÔîA:Ð7à°ù¢5„AÈ8Ò•ÏÿÛ~_Þ.Ãô/ÄDóxÍ%/|d„H7ש™Øzà!fÿR>ßzÌaå³VQçT7}äo–gò̉: yíãó™?ùг±íöDÒ¯F™,«,lš37=åL¬úÓÂr–0ï ~d y—)“ñ)œº*»ÊÒè¼n9ÏòÜ»Û7 … QM·úoÎE}8ÆÏ¥G½LuuvžÊõíÍóåßlß uŒsµxL[á×&Þÿ¿PnœvÕÛM‰&h%êlw£jý™´ãá#¨+Ь²a Oà¶ŒŒŽªXw?é™h­ ª(E·dd´Ç2tÓ¹ËqEn«cÇÞáà¡,o F]¤¢¥ßFT¡^Ùê*úˆå_ú¡Ã÷ßT݉¨¡ÒKÃO –ÿÑKÄjùg÷ë·½6¦Ý5Ñj§½ý¥};££á“­5Ñb«5T*1!ì êqö€>Ùµ‘.gð4$ui•h7œ^ÊÓ¦†ú–݇v]6{<7–sPbQWeWQ|¡ìÃ-gú”Í_vç'½É¨~‡e}ó/Ç×nfzÊúà{µu\ìp±ªŒ4C¹xvεíøÈ †KIq:ë…@°0T/‘r$L ‘ãñõûÔ·Øôò Ú®¡‘g‡“ß ð0Y3,tÎ2`L ¶ŒóL= »¨?ƒáá•]]“+Úw«¶¢ãp˜@à Ôu‚•eL€ 4mø®ÝÕ¦TOú|F;LpÿQºôkŸtiÚ™çÜ59, 5¹"å Õ„€îw¤4Ö…šQ“pÙ/hj´úG¢À¼ “Þ×Ñä÷¦–?Î`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0FE@ÖGjǦNâ7ýç"®žJªnˆ´e}ÄËq0&À˜@ÃPBäJ%7#ëuMŸ?;mòÒ†MÇÎʨ3aÈãy&&Û—}«ê¡Ä!R S(¹ ÂÐfDº¿|R؆ 0&ÀšC- uC_ÐY)¡ )6¡/x¬»»û“Ï…M-¿œŸÆI N„¡„”´‘À1¿‡”ò#ü^Ôâ´÷gOž¼§qbâT3&À˜@MŒ:µ­™o^¨”º¿óð‚¼áÎò¦~\“pÙ/¨ µ. %¤zïT¦xHJµo·g¦§,¬„rL€ 0&Ð4$&{O7…x¹9Jjâ”G›FÎ8•€^› Bê)äëÂÝiTVÚ„5µ>‡Å˜`ŸÀÒ¯>[üY^šù‡CK4áø3ÎÚ·tágß4þœq+ZÓ ÑИRòøF¦7ùïÃP1&À˜`¡ @’‰)鯠³¸]Æ%è_2º&Pcaˆv–¦ i¡ºN,‡Ï˜`M“õ!Ô—”|­ if’s±j, !g=igiÞP1b˘Æ˜ˆxV‚¾ íñ‰å69®šæˆ¾5†0è»3l˜`L€ T›}® ž©OaÃê•@…!ì+D]åoÕk±qdL€ 0¦Gý õ%ü!ï¦W´Ÿ£Ú&‹øLr™`L€ 0&Pk†* ˜í™@8Æ?üplÞî¼n­ZÇl}èž{òÂñÃn–Àèdï·šTY™ÞÔ§6%;`L v°0T;9”*H¼Ï{±2ÕÄý»òN"¯ûöùñI—_1ÿû(·;õIÏÄ&5ÍãQšÇ#ñmÊÚ1cR3NÁª›EBŠÅøÈåiÖN¾AACh™a‡¡Yé)C‚nÕìRªc”кÔ,ö͘ˆ2&À"‘“Eb©4Ñ4y<ó¢ÐñFöðuê+Cv¨Î¬“{åÛ±D˜â$hšü§ÔÄ{J©û³}é¯9Ýâ~„}‘0 ¡6H¡Ó‡?AÇ|2¶¿AèSQERj÷àøtì'†ñÍåq„s”_ùÿmJ• »g„.nDçþ3†ôKHõ¦Ûîtá‡5ÈÔD{ÛÎ>šJt¤{tí–²i~é¢a¿Ÿ1è¥é~Bf¦ù[ŒÓkòáá.|Ž Î0ÍÏîKAn*3šÐò¥&ïTB]HÕ¹“ì½V*ÿgÈÿdâüRÇ6†(þ!15Ú¿EaÜéy¤*0€p6 á{„Ôn…Û-†¯x „Dd©¬ILöþ‚êGÈ»Ž<ß ·ÃÅèl#›w¦/‹Š¯˜ˆ@¬ŠÀBiªIÚ"V B§­I}U8yÜl¬¾Â@h-:´oŽIÉøBË»‰)dz“>°Ã‚ð­ÓuB–·9Þ$hQäŸJ˜W‡ƒÎÛ~²í1ö+]–Ñ$aY—e/EÑ¡¶Û’ãAȲr»ä,q±¦¯hxÛ]Bku5hü섬ð ÐAÃ"†QYRD[ϰt¹ÇB†‰…f쑊üäæŸŒ|t…Ðò¤ÓÇ3Âç™ 4tÜÓzÓ=„uÊéóLo© dy‰Òµÿ:ýÒyñÎÜS!ôªéЦ9ŒŠÕ(P™§8¬ù” 0&qX3qEÒt$•i-—‡¢[8¹„¦§/„ž7*p m…:,øžrµÎvÚ¡ƒÎñû…ÐMW™¥úXÁ–ã÷A4"ÊéRÀ†2׸ˆ‹Z½oo!¹í|¯&׈½ŸXhu~u†Á¢®5ÿî<ÔÖ8ïUtžå™´š˜$ Ó=Žã‹Øè#Òð@,5J‘LèîèraJM[¥0¡ËoÄt-~È«|Ü;MT«˜Å»Ên…ùG}È ¼?ˆ¼LµÝc˜_I¨Unvø|dL€ Ô†j‹$‡S9—k­ðÐÈrBL…ž•ò…¼§ìe9ÍIæý YžÄr^ü¢Ø(gƒ?å´¥¾ââä÷Ê N!üCøp‡²e­U<ý¦_,sß’ˆ‘UZÖÕ•2s©®Ök&y4|{B²“ÑFy{œáóƒl¦1ÈKQ™táâáñã RÒ!^0¦D> ñ@özs¢ÊJJpærÝ×|Ƙˆ<, E^™4Ù9 ïÆËWmDÇy3&?”•˜X¾Svæ^ª¥¾ ­Uª¤ª°æ9ƒ¬ìš•®Án òÕéd)i=ýÐ3ÑÂŒ-8ÐeÀHÙR{U@IDATƒp &1¯As¼Éφã¾27´ºî–Ôi7+ß>co„¨³,ÉÊòj })¶vf¾ì‹=eÂ3,;dRû³ÄžòÚ½Œ\Œ}ࡎÐÈé–hXrSSÚ蔄piKæx’­ÉáÁþøš 0&ÉʽGrb9m›À•W^éפ¼†‡Ä¦S0t §•5%¿…ƒ‘˜ÜÁy;!uÚÑ7ceÙgNûÚ8Ç’üsh›3,¤á îÑÜú·d?ËsË~ oíÃÀÐNw•iæH§Cg²aÐW;ZÕuÎ-ÉÞÐ_×á]ÌL›´Œ§!¹·ï §¯˜ŽñË¡‰*„@tÓÞ:WæuVÛØUB{ؤI×N·¦¿ølçµuîrc(lcàS˜Ά 0&Ðø°f¨ñ•Y£Nq=éùl3ý¬|š”˜š~<öåyÑ/4tàf¬ Âk-«¾h%•hÝ&úßXý4Ja¡;]î¨5¦élúYF~犯òŸ:€“í[5oLêÔ\º¶ «µFaÕÙÍ\^rîI„x@ër–Üß©éú[Ê/[c—é‰p×68M¤ÞÆÔ4¬‚»Èl¡}_¥?â™°[´p§Ëý¾`£ŸO±âjT+Ë”(ê 'BÐÒ±JÌV8×]õþéÙÆW@ ;BJ©¦æ‰Ûo/š~#Ø?ž;¢¤zÕ/¢¢Lé»Ù4Õeâú‡½åö`šnøü×çì+zcœ'}¢&\Û}†ÿd¥Ì)`_àL­°KLIOÃä ©·Xs ûÉ|e˜'à³' ©ÏÌJ›ô‹ÓŸ3&À"‰k†"©4šAZh(Kß/Á„ÝÑÐuE'œ%Lã;ìJý¦ÛÞÁÚäK(èí±qò"œ®ƒåmŸ¯h‹ß4ߢy6îxíTZUÛȤÐ2ðÒ¿ðݯ¯Š|ÅÙðzûsØ<°ŒÖC‹wàÞ"ø}Æ:Ó4¾ÂtåšÐo N“Kz™Òo*sžÊõíÍóåßLn²&MÚ§ÅÈã!´¬„ñ‚©ŠVAã5«ä&‡Q•kK Qq EAfnZòË>‹[W)ù§¡ŠÇÐàB——g¦%¿`;'Á…ôôñùÌŸ,ü^%õ«è2Û}$ÜnB>/ÅÄôoý>ó„û4† ‡H¥Aņ 0&¹ð"[3ƒ=T¾ ð{Ù0ª õ-»í,ºl>ØNÅ4µÃ—~èðÁý7Ñp[Uâ×-êr.Üþ›42ßV1­G1lÓÁ„.ÚZ3vwìàJYïÜ;Tœ÷NŸÞ2/Ç×nfzÊúàûóæÍÓ,ÿ£—ˆÕòÏî×o{]åÑï-í1ùÙœ=yrÙùCNG8§aÊx¡™–6+è^¨Ëñž‡ÛåymÛ´ŒÚþàĉĔ ‹÷'aabG‘H€*¯]#1}œ&&.†ðÑÓÂuÏ]ÜŸÔ.O-|+vɘ`L€ 4A, 5ÁBå,1&À˜`áàÕdá³b—Mœ€úµ.¥¯nâÙäì1&À˜@†‚€ðeó%0×›ôNóÍ=çœ 0&Ð| ð0Yó-{Î9`L€ 0&, q5`L€ 0&Àš5†šuñsæ™`L€ 0†¸0&À˜`Íš Cͺø9óL€ 0&À˜ C\˜`L€ 0fM€…¡f]üœy&À˜`L€…!®L€ 0&À˜@³&Ðh7]›:uˆßôŸ‹Ò멤ê&…hÙÜJR ‘+•Ü,”Ú ë®f§M^Ú\Xåï7ÎRöh®åß\ÊšóY?JÛ!Öëš>¿9µ'õC˜c‰dJòxž‰Éöeߪir‚a]¥”ªe\ŒÙ¦UœŒq»›–«Ðç3÷æä«ÜüB <¼‰©é[ Ãÿh«ÂO<òÈ„BT<´oMÆÈñãŽÉ‰Ù›Ë¥ßi•¿Ö¼Ë¿É”,g$"µ'éÔžøýæ#‡Dò„Çs#µ'l˜@“%Ðh„¡„”´‘[ý›ŸFÿÞ}@Ÿîê„£úˆ£ë.ãb¢õ&[:•gÌó ‹Ä/k²Å÷+×vY±fÓô‚wÜpOÊèggxç#¿Æ,Aé'4äçÜ‚˜‚¹š)» èÓË¿òºÁ.˜@U „jOD»{çM“<7=5ÍCíIcnKªÊƒÝ7#5$†œ~æ ÄkéÂÏŸ¥c]Bã•’ÏuïÔ¦ÅèKÏÐÎ?e<¤S[áv5Y®.°”†IˆÇÐ}äὺˆ¿¶ìŒË+ô]sÌ)§åý´èËïJ6ÆFŒg׿&Ý7ÞåŽzåÏå_Zì|Âê„@¨ödAñ5CN?cÿ_}þ-"­³¶¤>ú“:Æ6z‘>´$‚xøø#{i÷^‘Þ¯G—F½.3@|&Þp‘~ü‘½¥®»¼áÞ” ˆÏ ¾¤ei †ÒIéuSú)”.ÿÆPtœÆ¦Dà@{Ò }…ü÷ÍIŒGþS[Ò”ŠƒóR‡"Y’PÍž Ð t„êæK‡Ë(wYuˆ2r‚&NÄ‹¸¹ÝÑWßz×ùH DTÞ‘.Qú(nJ7¥ŸË4Ø0"àlO¤Ô¼þî¤ó”ÆÐ–41޶1ˆXa(!Áëv»Ÿ:¤sñÏQ§Fz‘eOܺul#Zµi3{àÀ[#‘¶@‘é-I”%Qz)݇tn«¸ü#¹¸8mÍ…=‡Ý;µU11qsÏ>ûŠÈwÄöÍ¥L8ŸµG R+³æo'oó›f·¿óC“$i˜ÒãÒ„l‰oÕJg¸aË.1&ýY±aë®àöݹ"{ûž…Ñž‰§.5ZKê6jÀ"i¬ß*÷’tES:k«üf³5o~¶T¤Ì|½\þW®Ý,žxùá3Œr÷êÊ"é ç~þý?ñð?:èó„obYi\ñgvÈ$‘0ôÅ¿‡¼Ç–uO€žOMXíI¤µ%uŸyŽ¡I 1R ?Öp‰ªV¡ÚÌØû_ý$:¶k%°ÏFm[¯a)eñµ/{E±®³íõÃÈ`i¹Ã-ÕǨ’t†á•T‡ÀqGôﺦÞ?gsÊà~bÄ ÄöÝ9âÍÏ—Šw¾üQŒ½‚¦±•7º®‰‡'\-¢Ü‘ÔD•Ogs¶Á$jû%&RÚ’æ\œ÷ˆ´–†.]s C/}¸D´kÝBl†–gÞ.ûvï(.F`Më÷׿âëåk,7èT- Vâå#ÄŸþ` Ké·^.È|K\qÎPqÌá=Äþü"ñì; ÅšMÛEÏ®D'Äí4_/[-ý´JlÚ¾[´ˆ¡â‚S‰SŽé/– ž-G…ùמ’ˆ‹åùˆN~Õ;·žÓHkKª—öÕì DJgHakhÙf C9y…â-¼•FG¹Ä?Ñ ïFãÿþ¢eVEX³q›øèëåh€O7üíTÑ.5š$¨Äãxìá=-ûÞÝ;—®ã~{‘xÙâ†Q§â‹ñë,!Š*öù©ñ¿ùe­ÕÉô„»WÐùù©ÿŸ|yÕ¸_{þ0qê±ýE×´9´ÿ}±ˆq‹;®9WtïØÓ˾¶þ•h\ìÆ«”wm…_Ípœé°ÒÆš¡j’ ÓÕÏ]ûöã# ¤ó?\,NØW\<ü8ñ„ ˜Èp›[2ÊÆò[ìó‹mZ Ò2½¶à;‘_P„úú­õœ? ¼ DlÝ•#~þc½˜¿äñí浅 ‚0¥aæ+Ÿây‹—žu¼eMvE¾ÀPÞ§ß­fï—œqœèß³‹õlÙ~)îñ’3ò¤Ú.³rE BûróÅsï.ÂsÖOŒ¹âLkhn!òǦæJÚizfÏpÍæ˜@ˆ4ÍýPÕ) Z q-4>dvîͤ ¢F^×4ë¸fÃVqîÉG‹#{w³ÜÒ¹%ü´k/œ›@ž}âQp¯F®5|¶óNÆP€m.>ãX+ ÒîÐÛ)M%Í5ò·_}ÞRl‡}-û1—Ÿ)ZÆE‹aƒ¾8_ìBØíÑéÔ¢!ØæLÇH0ÎôD’€ lê% Ç@Ð?kè+®Åвüaž´=•™‹NŒ—€õâaÔÕ-;öФ›FA+Ú½ ¬CÏÉ+€æ³¥Ø÷@ý'½ñœ$\:ÂòLÏ•Óüþ×fq€Þ Ž´¬×lØ&öí/œC[D/ô<ѰZw|‚†„82¿¬Ù$ÚµŠ·„6ºÆFžâ'¤÷<<ßlj…€óÙ­•9&Ð"M"4¥µ‡§G—v¥µ€àAI©ñ¥Æø¨Ði>ÃÂW‰QhèiŽC(³o¾xéÃoÄŸ›¶‰6-ã0¼o½;Ý’FˆL‹ø€J¾oäöêRõ;ÍmµÒ@o±¶!¡mÓö½µ- Ejã©é²‹£IõÛZ”jd‚3MB‡Ó¸].qΰ£ð,,CŽìuÐ9ugžp$†„‡ZÍöê'ß‹ÿw¾xø®«¬ s`ûŠaVkHù¤A^4ztio :ä¯7†¸iHïµßc®X ïmWC·Äoë6[BÓÔ§ß·®­uÞˆªœÙÏm3È*g±)ˆDa¨ÎyÓYE†æõ? —5O╿ü¡ÎÖpX°ûO¿ýUlÄ 5ï¸Ë-mÏCÏì$ääOn#³ “Hób¢iºóš‘!ã+xµ,ênø±ZÉ©ÐScIg…ht7*š¨¬c>½(†_¸\º5'Ç™9²_ðíJKúyÕK;Ôš™ƒš{G‚Ï¢ŸWYÃV¶ F‚U(CsòhÝ®½ûKoï†vÕ64éÔcú‰UÐEC3tûÕ#ñÜÒ.ØGBR[h†¼·\f;ç#`L H’hð÷µMÛöˆ?0!š:†¡%@©á&Ó¥}kA“>iÞ½ÙÒDÓV-â0U³æ‘†(C có17iæPЛë¦m»!u-ðFþÁâeÖÐÍɨhYq8ñTÁMCsoèø«€ªñ95 ÉY—½³ô·77¯J™è×£+„!!a茆ƒ¿\ZvoŸwþli;ïºîÍZhp <ù¦øš¢‘Ö V|Ñ49û‚Å'`Á@T…“3€3ðüÑœ¼Œ§ßµæÑ|¦_±_º³)Ð*2V£Å o¸ÐZüp1&]¿óåOšûÏ’´ÒvØ¡ÁóyuVý:ŸÝê„Â~˜@D Š\#“’öåM=ƒŽ50´*!¿V7Þ“òö€Ãz?áçÕ ¸ê{¥y=4!ØÐr–;—å’¦ˆ&EWÇÐráh‰høÁiìyE¡ÒàtW•sÚänÙ¯«{á±­øíÂ/?š…êǯ¡Li¹#4ɪËuwÜûØàýŽl¨òo(‘¯á÷Cz–‚öÿiHSPˆg”Ó<ýöWxRÖ¢ÒºÒKÍýsÞ´# Ó¶É…fŠVkÒJP65#@íÉ/¿¯]þü#S¯CHµÖ–ÔbR³ ²ïfG Ò4CQ !´JÆ)Qb«+‘_š¼ÊT(·lÇêƒ@¤Á‚wZœ°ËèIhû Ë÷IÔ¥D«k»!&À˜@(, …¢ÂvL€ 4´ié ×=ýöBKêÚ¡ 9LÛ†¸MF8¡L€ 4† =G̘@m }„®ybmÅa0&ÐL 4ì€f ³Í˜`L€ D†"§,8%L€ 0&À˜@`a¨ s”L ¹ •]·L}«0‹›K–9ŸL€ 4B, 5ÂBã$3ÆB€6Dô›e?áÑXÒÎédL ùà Ôͧ¬9§L ÎÐ’ö%ËÿËWoCöô!UÚ´Ô6+þÜ$¾_¹.ÉŽ]Þuk·wú,ûlѽ±WŒÀ97I63>2&PX3T¬9&&Ðd |¸h¹ø;O;ú0ñ3>›ñåÒßñ™_i~—þö—6è0±xÙñî—?Zö4„öö?Šo–¯ÇÙ“¡RZ|˜@}à×°ú&Îñ1&H`%>AŸ¨¡/×÷êÖAðã4œ:XЗæé»~ôaWû³©>Ãã®<ËúN™Ó=Ÿ3&Àê“k†ê“6ÇÅš(ë{~ž[ÇÇ–ûž^LTà±øt }†Æ6ôý=ûcȶ™`õM€…¡ú&Îñ1&HàÈÞÝIJU­¦þˆÏaÐwüœféoë„aøñåù Ö¼!ç=>gL€ 44†º8~&ÐÐ×ìsöç‹ ÿO,ûcC™µˆ‹±†Ç&?ù¾–'Î>ñÀÄê2ù‚ 0&Ð@xÎPçh™@S"нS[1ߣ9@ô‘T{Î}ì¡ñWYY5üX¡kÞ¿N9¦?V–õoJ8/L€ 4RZ¦FšN6`‘Cà`Kã‚P䤘S˜‚…!®L€ Ô*zÎ?eh‰á16L€ 0Æ@€‡ÉC)q™@#"@ÃdŸq\#J1'• 0æN€5Cͽpþ™`L€ 4s¬ Qè{Jô³óܶ«Ê‘Þ”mCçÎkÛž O ¶Ërä,k.û†/ãúNשú&Îñ1ê`aÜìkýæbåÚl±{_žØ‹eÂÅØ-·T$*=©"è9(:Ê%ZÇljv­ãÅ€>ÝEϮ텆¹ÜAV‘g-:·ËÝćDMS‰ [v‰ßÖm»sòľ¼|,7P9ja3({bHÆ>VDË ícEî»=שÆ^‚œþæJ Y CÔp‘ÀóÙ÷¿‹ÏX)ròŠ œ(áÒ „æÚ'¤8°SnM+ˆÑÂ4Z ÃŒïã;N-ã£Äˆ!ÄCŽÑÑ®RÁ¨¦ñ°ÿÊ P¹“ä÷›¢ßÏúâÇßÅŸ~¹yÅuRþM¥ì½ß¯Äæ{Ä–ûÄöÝûD~a‘(öùñ3DŽ´ÄÞ3:JÑn·h-Ú´ŒíÚ´‡tl+ºth%t]o2/\§*îØˆdÍV¢ÎpÅšMâ¥C T$bc׈Ží—áø“Â:+3ÓŒ‡ã“ƒÅ; ‹Å—?þ&®9L <ìPtš%ÕYäp©äóù,-à« ¾ƒ\\/åßËÞ)剥øëOøýµe³´Fézê.}˜µŽÏú‘ZM™ñ`Á(Ú£Su]Š>Ý;‰£ú"†Õ‚R\£ŒlÁšëTiuà&Ðè4;aÈ~ƒ[ðÝJñÖç?Š(÷vÑ¥Ó»"&f}½ Zññˬ_aaO±{ï(‘õf‘uÚ`qÖУ„Ë¥³–¨JÂ.wú$Dqq±ø_Uÿðë(ÿm(ÿ÷ê¥ü[Ù†MQXˆ«~¿Ô_-ÈíÞ ^«DëÖÀo+êìn2R¬Ä0hmF'Q\tˆX¿µ¯X½a›xçË¥bè€>âÒ3OñqQ–Pde c~¼¼äBs»?¿B•@œšµ# Q4m7×ç0×)»”øÈ?f% Ù}5ûMBñq+Eûöo o² aHëÒ9SìÚu)´Dé)g0@¸Ýÿ›ß´ü#¡ì‰ ûh3ðRFô‹Üü1÷Í/Ć­{-^Ú%¢¢6ÛN«t$a0*j‹õx! ãóu¹ûß­4Åoe‹;¯i §­Ãü½5·‰õ˜Çµ~ëA?£-!@uj×óð:ˆþ=»ˆ~‡v±1¡ª®„#®SU*zvÌ"ž@³†èM÷—ÕÅ[_¡Ž^ið"AÌJÇο‹÷¾¢#æT ê×ÈhN›š r'A¨°¨H,_µA|üm@jèò¯Ï²§ÎÛþÑdñ­˜ë³yç±sO®Ø_ø's\®m-âñ…yJ6nÛ-:u|EÄÅþVó ÁíÞ)ÚµýH´lñ“غ-AÌzísQ•æo‘‰rï‚ð´Q´i³š ýJ΃-†ç”K˜øþÖÂïk‡¼të²»CÐý ᷓ&ŽÂb…º~æ:eÿcM†@³†¨ñ"uÿÿæ/±†ÆH#I†ÒãÛÖQ¼¶à{Ñs)ZÄÇZªZqƦú¬Cc¤Ú—³_¼óÕÏÖÐX$•]–=埄 š,¾òÏMâg|Yþ×u›J… "ëråch¶ ü¸¢y@n gµÀ<Ÿhº-Ú´^X'‚xÉ?7†+ãâ;÷­æïuXŽã*¤+üE ÂQQÑ¡˜ÌÝ_¬\wœX†Ÿ¶­bŨSCîc ?ׯóÔÜë”³Üøœ 4ÍB²;ƒÏ~øUìÃdiš#ÔPCcUJO»6Ûo_`>ËÈaG•Ϊ+UEii*öv¹Ó¡‚‚B±è§ÕbaÍФò¯‹²§;Ði›âÛŠß­€(Z’"Ì÷Y-:´[-¢¢7[ó}*b¡”nM~ÖõÜz©íÚ|€g`~XóB%ˆæ-ÅĬ³~mÛ|" òûr‡‹ç?(Àœ§âšóO½»uüö¾>®âúúz³UÜ»q/¸l0SLOè ˜@R!8=J:„|$¡jèÅTWŒ{ï½Y²Š­®Õî~çÌjV+y%íª¬V«ýV¯Í›7sfÞ¼3wîÜëy¯|¥ÑÔ¹ÎܦšÂÆ\7td:rªe1–PsÕX°”¥mÌó·tS¤œ?v >TÔ²àf¦ËÅ’ñu½S*T »A«1EªõßZuÏ5ËMIPΉ"yíóeл)€’q¤-K áÙ ²Q» ¬1\-Ú^pˆóA³"M+b7–g}Í W\ÜVõ+-!yE—É“¯~!×ÀMÈ”qCš=uÖÛ”ÆÔl áŒ@ØÏÁè‘ܾ#yP­‚.„[q3T+•ù+…ôb?ò[…åßü¨± &†€®wbX8¬ÿòJGH×KëÞ]f®–³ËúûåO¯|*‡ƒÉˆw%;ëP€Þè7 íÐŽ·I²3Ÿ–ÛywþyoÞ*L:i $ç±M‚‰kèÈt2äP–…9ò¤¡PÌó¹}”~I†† 5£Â41 †œ"Û2êõß’º×å%Ú¸ë ü÷“¯ÅqTºe?­Ì84°º…+Ù2ºüO’’–¬Â™³lSÀ„HcÜYÚTX5Sƒ@„ý4™{ÊÀ!ù'±ÅJEѶ3¨ØÖ~]fþ˜ÏÂâR5·ŸG°­¡øéWÂ$’®w’®";YRîWýg¥;/ÿD¡àpTJaÉAY¾ùÙ¼·ù ÷}²Î‘£ù é+iÝæÖ½þH³¬Gr ä•O—aŠõ˜dv})äÛ{£€´ÁÅ´”ÏÅ鈗O— Vœu—î™é Éþù ln›š2ö™4ì;ª4U'%'t˜~-'NîjV #¬ÑÒ§ÛdÙsx~“÷7·M5™°‰`3Â^2ÄeÄÕ®œ,…‹ˆS¢ú¬'åŒËñãÆ¼³ &†€®wbXY !üˆ«¿áÃÅÈÛ î‚®Év¹dâãXïï­uâñÃ5cÊó’—Uç|CÍ©{~¤µé€w0ärUB òª!B €œ–ú)P¥|¾l³’¾?BKÚTqi޼øñÅ2wå/%)¾›Lõcé3N¿îçËÔ1³|^óu²9mÊW:æœA œkÉG̵£æj¬ ó™®®tGõ:~kl­–*±W9 Ƈ§ši2–ì*ó]ïz'†\Mf‡ëâêo((Ù'Çó7Kná6rÃtÜë2Ù´çIOî/ƒe§”âòY²þ¯²íÀl•ì%Ÿ@üÍ’œÐKFü†|°è>9gÔ@¤ä–ioˆÃe—¾wv£Y´îù!§þ •Ä7b)ùGì’öYPŸ-P^¤XÛVX¿ŽWzyî… n§É e·¥mÊáªBÛØ®~¶èd¹pü¯¡Ôžˆz+–á}¯•³G>ÿm™ ®‡AÐG Þ÷Ý*÷]»DÞ[t¯LûsIKê ã¬ß•KÑÎbmiòKeï‘…òŊƉQ mª! Ìyƒ@8#Ð $C\YÓ<½›@Gõ­ÙP4‰cÞý¹¶æó;zZÄŒØñG²Vܬ"qÚŒS\Vk¤ÜpÁÅ•(Ÿ-{HŽä­‘«Ï{Ftœx[ºœ;ê'Ò¯ÛùzãS’wr'lú|¨î_¸î ùxÉÕ~Sÿ©{–“:,T_³m? &–BGh]Sèô×#£ ¤´¢ZÊ %aöçk½65Íщ6U&Ý»Œ•+'ÿMç­Vm*D‰mÌbq¯ MŽï!7Ný¦zO™ôcRTzX殄¨cª=­Ùþ’_uH›ò+AÉ ft Éúø1ŒŠŒ•/|ù´Q= ÒåçüYúdNF‡æ]‡¿”kSKJJÊŽI¤zLƒžÒ™½äûj”h»L r®$s»K`gf$Cþ¡H¬Ü?Q:I†ý»ÕëÜ‘?[L²të2Zéxì†~FÏŒñ’©Ïso‚f™’ êy‰Œè£ŠÃ›9öê3Ðh)Yäxáµ=–¿ía·ÚoꟿuïþÀ ácN2´çh>ŒuvÎcMaZÿzuuºØ¢­Š”ø3Xji›Š‹I‡ÿÁ§¥[úhIMì#K6>©úaý¯Ã*ÇBè0ýDe±¢ªHnšöšôè:i•:w$o­|°ø>OŠÑÇØ“ɡܞsMíøÛ¦šJÇ\7„+a/òtbÖ ÃYísTïpVÉñ›TçôñÒÊоWɽ.W©+ÉÀèŸÀ°_®Ì]ñ ˆ½»Êyc ðÉ5ÑI‚øWóao^"÷.1䈞ÇNQÈùZ^þìùϧWÀs•$ƺõ~ŽcêL‡ã…[%9¾»>Äj•‡yNºãgݳ\îi@»äœB9a;::7ЧuºøÕÕɰ;5\zg¦)üèÖ½¼–´)dœèSVn}Nþß»dɆ'îIqÙj*VWB¦Y’jÛÔž£ ôåæoýlSÍ€¹Ó бkÉ®š@?„¼Ïé´78ª_¹íy5ŸO)uJúvŸ"÷¼¥WR–ƒ¥»©ýx¡‰5«HÔ‰þñÓ­;ßn3Q½Ðø5§þoø“ÒòJ¾ê0=±Ÿ©\[³ß™ÚUuuËW+ú[÷ü€SzH¨S0*É@}FÀጓÜ߀þX„Œé—‰wÌýž5|GÝ+ÍmS¥'䓯T71œÚ/Cû\é9Ÿ†¶ÅÀø:8‚ئô3ÍÖ ÐÙ{ÉPkWh|\†\7åùþ ëäÚóŸ•.WGbêL‡<¯å²$JqÐ#Ἷ JJ!õ›8ì>5Õ1nÈ]0â˜%»Ïk°p…§öáƒë”^™1í–¢Htƒ‘¼À³ÃéÖ‰ŠŽ°¨»ޤSé<Ñ+*z˱œ{ÅQ%“eIb¼ õAË×þ-­o ¤ö[¨ÚÐø!w«65qèL5mÆ©±†B¦[S{a…b¶Ä¢M™`0´°&CÍ‘4é„!ß–ÌôáòÏ÷ÏÁRÙK`+dg[¸ÚH‡T¬þ¨´c™üQ}ªÙÛ¶(K³3â7¶VÕŽ ysþípþÙGf^³X¦ŒyD¾Þô7ÙuhNƒˆ”WÉÚ/Ë´ñ¿•A ¹²,ÐÐPyxž?N—EâMŽ…Lyùà@“o0þµç=+ݺŽiðz (E}èÖ½XAu:Y»âì¿Èã~HrÇ-(¼Lùýs9Òdd·$é–š QQ‘ÊåMœ!“5ªƒÆîñçÚ¾£‹eÞêßÀѪM±oùpñýJ±¡ûwž ?sÛåëVÀ íÓ Ekô|[•§Ñ‡š‹F ì§Éô£9uà=ª§Øº²êFcݤ´,Wœ®j8}†±ÊCÞoµF)#•´¡ÓhÛ°Z|w7¬Òƒ¾FooéE§“R ¬.„óÙÍÇŠÅe“®Êï šú³8Á»NiS ×>!ü5Vm{Qø#Qd£%оÚbiù ùïgW)I£w|}_C[ïü7Çœ7tV|÷NzåöÕ_<áQyê­Q²£ü+Îù«üøæíXA´QV@(-¡çN®9óŒ»ä²Ô?ª)YkNàÛ‹³*±°õg‹‰Œ¢>HsRª½gÈPòس–†ÅµQÛyïOR…üþ•æeƒŠÄÕv‘ ¨Ï”cQW1¾/–¨»%>±yéùº«)kÒõï±W®ËC Ã»Ž½#ÑøÐa¯wè‹åûgºçyͪ>ê6ënïxÉË»Qºe=‡ó?Óû½²ÎÆRîChÇ'`xò Ùr4jÐ7¡Ÿtïp>6o—.)äšóþ…é¹²ãàgrlæd¦ ‡>Ì^Ù°û !Ô¡·©Xtpµì?ö•¬ÇµÓÉ¡Eöœ&Ãú΀é‚U°íô®Ðjsv—QdÜ‚E rìÄzHäþŽ$k‰¨N¿¡m—ô÷%-õ,Qï+%%ãdý>‘Í`Õç II.Åû²Œ¹ðгþ ;>©ž¤* ÝûfJJOIy™CJŠ«åd¡]NV^ 1x7[+BløLæËƒ€A u0d¨ ëêÁÆÇ¿ÞŸ\#8ݽByE‘¼ºðºÓFyM<Æ\6Œ¥$B‘‘jÊÇ!KÄa»PŽæÌ„;Ž×q¾ù«Ët¿P¨ÓB÷»w=S–nzJÎè}•\?åEH'¦Ã\À.(qWÉší/Â2üéU—,–í>¡¹V8ˆxõóž²eáM{ß–s†?(‰X1µ`õ£žkÜ=¬!}®Q‹Fþ& „QÁGäʳŸ„K”w±Âó¬´ê‰˜þ!ý«µJâàû¿Êªn’wâLqž#C¢–Kr¤E'©“6[ƒ€A ƒ#`ÈèkTß”d ÐQžÙ0Q:ý{\ c†\çÂ6%ù©_|ÚljŒ ŠÂ”‰Mö>WÒ‡ËáÜBÙž!GŽ= @:V€œ©{ÀÇ÷¼’ó©ÃTâw®ýZÙaÊ…».ç A‡Ð¡ê•1Qé3Ñ^Ž6 ÈëÔ±¢eïèÈ™0ôY oªÙìy)\ç‚!ËaRV^ ÃûÍ9+)Ó>†}NÛ‘µ4ÄD…'ûg¡Pý€—\-ߺêHà¢|âLÅw.š ÃßSÅ%’_P(ÇsòäË ¤ÂéŸí¨–æ×Üo0´-† µ"¾e0žæî?ÌH"7^0GÒÓR%)1¡Á¢?i1Î__ù\6lݵíÕ¿ýñAæà—8—âÀ¯½•E8Ù@ÍÚtü²n}ð¡¿:pÈn»‡’„Îðáb[‹âGœ„)!!Nªàƒ-#Å…j§*€£ÙÒ‘˜­œÔ¦$Ï—ÄÄZÒ(²öš%Þ\À}úJ¦a}¯‘ Îüìé¼  2ž÷;QUå–¦2¸˜4^®’â³!arë;*=¨$QH.)îÁ4ÙMrþ˜‡!=ú¶¼üùµ [-k¶%’œ<ö¤®’ÇNÀDF†Ê+¥m&  óÖ·b}¾üaeR¿“4ID€$ƒSdÑ )2/‰I ’„_JB‚ôI‹‘ô«N— K-SRÐ}šÐ2; WHRZSVY ÎéUrƒa€t¦ÈVlýHÚé+ç†ô¾.FùzÛ½¡úaëþÙJj´~×J©x+ô”Hzè—‹çÞ˜{ ¤F#!•:}UZý´ü9¶Ùö«hGŽç+Œ\™g‚AÀ Ðù0’¡ÎWçA)ñÏYyä^gÃKh‚’‹ð~É¥jšÌFkÔqø9”û2¸çØy°HŠ+­Ð‘Ù)©©Ÿƒ´Ôòk2åU…rÇåCÊ%óVýF%Á)/ZÝþîu+¡,ý–2>zùY‚UöËdË„æt(«qAºÿÚe’j·|¹ê×ú’g»vÇeòÈÉÌ«£lQÐYZ—6Ê LSÙ¢’Äî(—e›Ÿ‘òŠBÏ=-Ú©Q¢Kþˆ%W]yK³Z”~+ÜL…ô‡¾PËé[!9“„AÀ àC†|€âÿ)‹ÜsÕ:%m<'\u)lëÄÚÒä;3–ÊÞ# ±Ä{–Œ|»Œ;ã[ÊÞÍÁãËdéæ¿ËѼu ÄV¹ïÚ%Xþ¨œ5ü»Ê»ýöƒŸàž_(?w?­u®RßeÎÊíRZY)Ù¯¶xZÌ;Wkv¼$'÷zŸêðûå`«§œ9€zûmO78(ºdÒPOWö—èÊ%'=ÚÖ"xªU>úê{°­´FáJ·´ÇôŸO§«cZ=¿ëŠÏЮzJ°ÛU‡¯hSƒ€ÉP# 죗SöT’à™¼ ¿mÜó&ŒË}©Î¹oµÈn,%þ×çJÿîS…î8âà •bmNÐ3u‡ Ë2—½úF_gµýFhP,ã1ˆÐß¡¥òò€”¡=àÍiN¥£jÖ¬»KòÄq?ÔWV‚0"R÷ºf}]FEÅ]a±]ý)AÒé†ú¶¶rb¹8ëÿcÜ[³ý%•åS¥Ge¬I¿>÷F,k·Éä?ô…>è¦û­¬ÛùЬ‚Ý¶lË@I†ö^_VV #…G$)a9”—¹`°õÂò-ÿ„ë™]­—`;§ä„ÃÖù×A*!ƒ»§+Ý+JØô¯-²wÙY„>W™²›”šØW†Ã“-*Y§¬vT¾ÒUžÇï=`[Ô´1ou·«mÿ–ýohóvåɈÙ1tž µ¤së•u Ì])ß¼äm8\MƒsÄÞ>›ÅÞ£‹”Dl $Æf)"¨âÒÄ~}q¿ÏD|œÔyç6hÁùˆ‹‡ Á…ÄùÚK/õ±aìÜוøÞÞ£±Ð„}Ãbq]‰±ÏÌá,d%Ö=tWiÎÃ÷”†ÄhAUyyÛ2/¼±k|%0 È%Þ¬»C¹+ÕäþÆ=oËE˜ö¸ã²Ù°-Ÿ-Höå,–³G~Î)?–WçÜà3cÅQ])ß»~rQ°píã>ã5tÒáHºVh¿`y¸Æ)²×)ª·Z,%N—cl> Ê`±<þBä`_y½ñÆ- kÑúº¡ ÏÙ@†O•Vÿõ³Ã‚g »_þ÷å7”~Ø8ø £7z=•Áøi˜&ÕS!yE;ô¡_Û@ëÞ‰J¡dÈÉPl”6z,ð¡5Zâã6úõ¼ÎÉnOú°ê*C2ã°Ú3Uââã f“˜˜h r"A†Ðêý ´©ü“niu~ràSŒ:Óé:(ò“·¾Ò.’¡ÍûÞ×—Ô6Ò"¨?H» ´Mé瘭A 3!Ð)ÈGuIq1˜BˆÃ¨Ù†¯ÖöIc•Í©’÷ÏTº!T„Ö£ú&ÓË|½½J¿«¸:¼5ÿvu_}©‘¾ÞЖùc>ãc¢ÔÕ׈´¡{[ë<Êö†ÃQþf\¶HÛõ4RÕ}ÓѪÎ'^ˆ¼Ïy‡Ïúë Ii?úö©[TÔþòJGmoÝZia:ÄNÿl‘V0»Àê¿°±ƒnNqTþMr|7•#NŸÑ?Öè¹CítP‹eçþOAF2Ôè}#¦Ìü Í­{'VxqY8Ã{¦ÉÚ}à6âlX._êï£Ã>^qé™RXp„“Q2,Ó&½³R`±;¿x‰‹‹…»M†êJdë£Û·´)’:¼¥òýÂu¿—a}®Výƒwúœ*3èvL‘ ”O–ýÈû’èvÛEºw#›v¿YçzCÍmS ¥gÎÂÆßü0(µî¼zg¥ÌXà¦Á§£Ñ’’Ðh"¤#ú³Ì7P"Ä´™?æ³[Z:îÚ¹~n0¶ æ…]R„O|Ùï.?ÄgÞu×þŠKÄÍP´x «Æváw¨ÂYü¯ýèŽr8¾²|…s+Þ:‹çÚ;èz'†œúHOˆ ¸þwÁ®N^ávyàº2}òÓ²ûÈáªÀoOŸÉà'Ð zí´bÉ[«VÍ8ÿ99ŒéÒHÃßÐÒºg™{¦ÇKׄ()(¼T Š.Á£k‰š¿ù§x••½ál¦äç_-±‘Ñ2:;Nzf¤À1k¢²Ô2DëÝ4¶©ibØPhi›Z¼þÐA¼*WH6¦OµÛý¼íÐ7ë’2v¢¶`Zl>­¶œFãŠ3úHccÛô'´´Mùó Ç „µdÈÓy¡ËNO„/%+ü%’øx®ÒÍÀüÅ VÒ“ãj:g|Îj$ÁÌñ¬™®iõŸ÷³{«)jüÄkÉ©IQÑönÌs;šÂÉŸßë¼ãéWÓ’bJûÂJÞ»ê(VazíûõÓk«c¿küÀY¡ ’=,W@õÏ‘ü?» ®R cr dÊ)¯Ï¹ã¥Ê^ª²¿nç«j«u;ö]$ýÚ8xü*jsëÞ ½ÚÒ®9†wK”­GŠäø©s¤¢| ¬O†© ºW¿2ÔA#9¡sW^v¤c“”nP¤Å)½“¬Ò#-(QR’“$9% ’³D/©P„D¨mjÃîÿa5êÛÅ@1¿L˜á±Qª.ÇôÙ_íWu¶9-}^²áÉ€ÛUsÛTL˜ƒ@'@ ¬ÉëR~(øÚ+CÖìvJEEo¬ 9rÕË|•—ÁÙIêæóÍ2„Rxä›' }åçû·œ)ðu)èç¼ë$ú Y D‡NÁ¶L€õ_”wÐDÈûœÞÇ·Á²jnÝSÇ…„ À¶˜±AÊÁiŸþ]*%6¢TŽ–¤ËñÜ;pm?¼×¯Äµmøè·ÌÁ©*bÍ?*$—xÄØöã#ïvàê}½µ÷íö.° ÔÏ:)‘'µhGBÅ „Õž)••=¤²¢/µÜ$ Îj»C—<;9V DG¶‰ @)5D(1R!è ¹¥B“!–£5Ú%Ì$B:`µ¦ÞõcX»jn›ò##&ŠA ìk2¤%îQs¤Œì—-[ÇÂtÉÊzJÁö©P'>*ÌWL¤Ke'ããæÖa`Þu9B&³!ž—®wbI²Ð#Õ&9%§$8g·AýÓyit‹ -©{–•Det{®—j¸ä þCRL¹/¶KAeOÉËï#ÖÂrè3miÚ-±1{ýÖŸó.“ËeÙë&¥¥#¤¤d,Vi‚„Åì—¬LÚãl»àrEHÞ‰›ðìLŸ¡^4¤@ðO+)6‘Ôx›"ˆ$‡Ô â’!NÅ×(N»ß³¦‰P[´©’ò”¥mdKÚ”OpÍIƒ@˜#ÖdˆuÇ‘3—Ìòc%êñ²eñVtfH×.þ)!£ 0?Uö ÝÓy¶µº¥v¹¯[ÞŒ|„Ë3¼ë+…¨ K\{%EÊž¢ŒV¯NgüãÝæ¹]iIÝ»¥ Aç…}®,Óž×5Œ‰® 8n-*¯–ªh)).‡ªvA*rïÆqlsñžÂ…L9¦ËÔ@Á òAârÆ@<KøSÅ^•)å°ÚL‰ÉGœ¥Z",Ur çò ®’´ÔOZUò¤ÛcUU7¤%¤*’c.¶¬†Tˆ‹¿HTb",XM¡¤ #Í.’–òq»èqNŸS6vH„z&GÈ€¬$Ïèµv…KÓ"üŠ^çtÑ©RWUe=åw îunÞÁiϯ‚’NKëßW½WB:R ¿]=ªa&Á‰é£ò¡b?Þ>õßšuï.+õ†¢=SªZ™Z“‚ŠŠ Eˆ*+«` ± ä ÒN§¹%IÕ`4UÕt°ªŒKaê‹+•})ExÀ°”œºwnA¥m’eÛéÀûS-IåvÌx´é+±’írŽH8‚x@êY‰¤N‚é;LO[•Þ¼ÄêÑ`‰xvyËÀQcòWmÙ›2äÿ°²*ðŒèбCf ìÜ¢¡Ù³´\ K*¤J¦•Õ±bwÆ¢£o Áfœf‡ %Oê\$ÇDÀ¡k4FÒ6t¨±J"ä¶’¬öÙákq>óÞÒ]N‡ãÔæÕË÷ -–Jw`5(´ô -¾ß;?Næsâ´KO"ßI-©ÿ¦ê’~pcb*$ Ä ¤?»Eª ½«vØimqÉ@0ëÞ]f÷Ôª"HHj¤¢é-%BÕx¸ÕÓeü¨û’ yK^ˆ‰Ÿ¥ÉPE%t’Êba×ÓqBi²å–:դ˥ä:}Þ¯þ¸Å¯¾ô‰ï§·(Ýâ;Ao×Ì“&9ê~¤Á|ñœ{«Óm9 Òí€Ïás™7_}IKÛTQÉAý(IIèåÙ¯¿Ì6UÿÙìO\NgÑš¯æÓ׌÷»[?ª96tB‰ y¿T޼£GÞÜl±Ü¿ë`Ž ì•Õb@Ý#:Œ”¡£àŠwsôkªÓäÇ"¢÷ØØ I×#fèA°çÔƒîÀÍ;O=ºŽ°RÙ#Kõ<›[G’!J„Ô4™G*Ô:ŠÓÄoóîÖƒ»wÐd-×[ëŸ7ÞÌb{ï|è¼9˜_K¼¾¥õïO½S¹˜ï8…ôVªw‚Ùžu¯I‚ä[} AŠø€s @Ä­ûçvå¡Û:· lïÌ¿þi¢B’¡I¯1 ñ‹«TD¨BéfUaå¤CJ/‰Ïr“,Æ×ï’N›[ïô£(ÂS\Š aÐ@iôÖŠôÜĨV¥I’~Vcù æ5æ§µû’ĤÚdfu­=¨ÙÓ8³My÷'û¶o~gõ¢ykÝŸ„Z_rfæ„AÀB‘ QBÀŒëÛË?ÿß«ï]|Ã7`RNn>–_èºù’IÖÖÒ!bÇÂŽ–£Nê )T3}À-%AìÐZB†x?;x~7ØyªŽÝ£Ýº:ŒÛ’@NQ"´kóú~øî<¤§°Ä–Ò!î_âJd¨N½#oÌkì‚ß™‡ ~q¯húw×CÛ×;ò«È·Áª{>«½‚&hÞiQ-ÒçÜDH¿K¼‡AàÝûµÇÞçUÄýךm*ö”tHOKÕ»ž­Æ,XmÊ»?Ù³uãûóßûSd†ïf¨ö%¬ÌŽA B ±·äG‘NÃø²Qk›óöëŒ=wJ®óìóîúË+Ÿ'ÐÃ5~X?ˈÝ[d‡ˆ ‰ ;¥d IÉ‚Õväu;pä'  ;/ݱsËçyvuœ€öŠL»\îÊUT–vTÛKV-üòM+–nF4c ö‰'‰/qv;í|Ö;òD#SQ t òsŽåŸ2ízÔRKëŸx·u½O]¯Üê_k×=ŸjA—ÕW¾4Ò×4Fú¸£nYŽÖhS¶ØZ¸¨¢~ÐxiŒ¹mí6U¿?¡ÎáêÅ ÞØ°ì«µÈûPîKêCfŽ ~!ªdHK†øÒñƒ½ö«…+·¯Y½{Ò%—_ìpTOÁG? +16Æ•’o±EE¹‡™~»ñHõ;ìúÇß}úUÝé+õõù@·tYAKÍ4Pˆ<*´wÛÖ%Ëç|úuEE}!ðG é ’[þ¼%C¡F†N«wä—mÔJb·kÃú½“.¾ü½É¨ÿÄÖ®ÿúõ\ÿù8Ô¯ëúÇ'hnèPÔoCõë–<á_ï.òì{ïÔoCõ½ã²ï«?Ù·}Ûbô'‹ÊËKO!­ŽÐ—Rd× àA ÔÈ3¦õ†8'ÍQ».%³B©Væ¿ÿÖÇØŸ7pÄè¾½›˜›Õ2SÕH´£…êjØê.+/.+9Ut`玻·l8„2èé&J€Øy¬ù±3#žÄ•ú9¡2E†¬¨Ð`½ëë xŽ…½3áG²hÀ°Q={<(.!)%&.6±3Ö .fFŒ)}uqÖmܱOïc«û“ò’â»¶ïصyq€ÒÑú’`Àežf„"ÒS&| ©ML"¤¥>úƒißµiýüöâš’ÔÄÕñpØiñ".l°Oé‰%B$CE5ûìÔˆk(M‘!;*øUïˆiÃ/ Äo~üX°tÆzG±M7F?êüÀ“)åjl}‘¡p&EăAã¢É§À4Y$!"1â1Ïk=!}/N…dÐe2õ’Õc2Dò‚ð,Ýè÷.œú’ Àg.„2"Æ|Aùrz¿¨üHòOÅêhü4ÒºE$AáL„P<ˆ‰þ#ïNŒ‘q˯…ºDYôSï(ÌN'F 'He×ýˆ~ï©/ „æ1P'C_~Èù¢r«-{“ M„:«Îw'¦;2n5 âõŽL½wÄZ3yn-ò[+¡&ÒÑýƒ~ßtßN}I˜ËŽB†XOúƒ¯ %$?z5‘·D¨³H†4.Ć?b£úãtä`ê½#מÉ{Kàâ‡ÓŸàG[žúbþÑÈùÏü¡ê€>pËwŒA÷Üê~„[}žqL0„%‰ é Ð/&_Ro¤Õ…NðXè qÑÇá¶Õå3õn5kÊÓÔû«{Îò¶ÅUÝ 6V× çó¼ÿ™½]Kñ´øunnüÀ“¢éw­ñ;ÌUƒ@!ÐÉP}øÍ‹[‘Îqlê½sÔsg.%§©<á‰×’S]¥§zÌšéšàæ+žKØÑcïsfß `ð­cão|Ï `0ÚWµÓŠ& ­Œ€!C­ ¨IÎ `0´6ðif™uGq¾ÅeÙòØóÖžxÉÖ‡çô¯µŸgÒ3t6Âaš¬³Õ™)¯AÀ Љxü9ËâÇŸ·Ž‡¾€üTC6t‹Ó^ùgœó ðøóßœuã=Ï ³c0„€!CÁe" à"0ë^×yM?±ŽzQÓÑM ƒ€A µC‹:§ÍAÀ `0„¿˜Ðõñg­ÿ©Ÿ'H…¾õÄswÖ?oŽ ÿ0’!ÿ±21 ƒ@» ðì³ÝâŠ\ùi‹+õñÿ&¦ëLDÛí UŽÊË-ë úœÙ #`ÈPà˜™; ƒ@PÈ·¿\ª¿rYd ¥²d£~8|ÅòùÄçÎY¨O›­AÀ  † ˜‰n0‚”£ßyúÓ³‹í}dÖLçoê>Ÿ«íÖ=eŽ €0d( ¸Ldƒ€AÀ Ð>|ÿòÝtPý>ý™·º&Ä•ÆWßu×þ ›`0´ £@Ý2üÌ݃€A hüþ¹È³±Ôþ““…'N³šÆC©úÅ'^ˆ´L˜Â# ÃJ5E2§8KÄ/á~cµ§„)„ù¡sq¼ÛsÎì !`$CÁe" öA@Y†Âô¬{ª¿Bj ¹¤Þª£Ú'Wæ©ð@À¡ð¨GS ƒ€A ̈Ïìq Ö§Gÿù[o]ÔǞτSŽoF¸¬+õ9³5GÀ¡À13w  #@jغ·ÒQùüµÎÄTÙ“gÙ‹ÅòÜC÷Ú×=Cæ0BÀè …Qeš¢ áÀ¬{£„ÿþÙØ^«}pœ+zóï);Þ¥6¥3´=† µ=Ææ ƒ€A Åüé儌ªŠÒÙV±¼ó¿ÞUC‹Õ-N×$`0ˆ˜i2Ó ƒ@@à'·çEX#~êtÉ™åU'·=þœõõÇž¼ÈåúéÇ;@ý™,†6æ% íú1¹3  è¹~öíêÅ?Ÿé¼';ºw_¸áxO\Ž™?ÿÛÍx!j‚É `h>† 5;s§AÀ `h ã«-.—PÍ!Òâ—Ó‰uf& ÍFÀè 5:s£AÀ `.—ËòøsQS,â¼­¢ðÈeÐúR¬OÿìûJ‚—ó$ƒ@ø!`ÈPøÕ©)‘AÀ †üù•Ä®q<&ëK q)?øþ­§°¼^¹&M0Z„€!C-‚ÏÜl0‚ƒÀOo/ÉÅ“ÎvŸ.ÎCÍS P&Cîxy¼÷;Iõ´¨˜Þbsî{·(á¹Ù´©“6C ¢ÍR>=aïþ!û‹ÓKlμ%2¤?nTê¶ÞþƒGÎŒŠ‰¹t¨·X-Ù˜OðÊ·Ùõ—EŠÅé¢ÿ.§ë‹ÿñ7tðØ;;Ó>ü¬s­##ðkOæï™õ»žƒ ìè¾ýÄA§ÃñÅKþ¿5x¬¿ŽØ_1óˆpB È?r$@S¦\×{Üðl±¶ï:®,*&ÆÙœ)Iq[T”YùÖŒ–Wa·;‹N•¹ŠË*¬PÀüÝÌ_>šSmw<QàúûsÏý¦I²£ å`ÚG(׎É[«"På•Ú ÞÙôD´àÝWX##~˾Â^Uý·Û>óÑG/–!#š-OæA`!ÐÞdH‘ 6ê–ïþøâ¤”Ô8]®ì¡ýº»Æë'#t·ÄÙb‚)*îÁ|Ž"‘e•²i÷YµeoææÝ‡oÍ´~ÿî‡õíÿð»/vr¡LûÅZ1yj3~ÿJmÒ?ºíÒÚƒàìùê+žÈÙïû·õxæ+OþásdÃ_¨öÁAÉ<%,h/2¤Gû|~ôm?úÙ÷m±ñ¿ë–‘âºéâ‰2°W¯›ÐŠ€TÊÄáýø³ì:˜#ÿûbEæ‘Ü¿=ë·½ðø¯ŸÂ£BiÔgÚG+Ö½IÊ >úŠŒ#.×w>ô‹Yÿùã£ì+è$”ú‹@Šgâ|" F>¯´ÝIý¡‹Â#lß|ð§?ztܾ–‡î¸2D¨ížlRVã‡ï¼2‚˜Ã öŸ¿õÈo~ˆ l ¡@BMû0íÔ "x÷QQ1¿¿ó§?g_Á¾;Tú‹AÊd££#ÐdˆÏTDèš»f^—ôk|”]ßžq¾%:Ê̈«AkbNì­Öˆ?Üñ“Y”É·G{¨_dÓ>ê#bŽ 툀w_m{ìæ~x²£ Q;æÌ<Ú ÐzûãÇçqj,¦W¯AiÝz<Ù#3ÕuûôÉ¡ ‘h=T;PJľ{FªËf‹{þ²Ë¾¬»Mx£eÚ‡7fß B°¯èÖ5E’SÓþ1räÙIÈûòöì/B“•ŽŽ@02 E?ÑøÅNºüò™XÆ” ¡#j¿fDìo¾db×3†õý^Mµ95í£ýšy²A IØWÜré$«Ó%Y#¦žûn`_Î>½=ú‹&ók"A ˜dÈ3êGã“ÓÓî> ‡ËèR]m—uÀºˆ±ÙH†Úk´gÚGÛT¯IÕ Ðjè¾">1á>$ƒ_{õ­V&“A€‹ yúmç^zÕx &º`ù¼Q„H;d]ÀQæ ÷|o²ìÑži!ÒL6 M!àî+$ã²›nG?n¤CMáe®w ‚M†¨tÛµ{)4¨;B¥NKÖë$.1ñ"·½Èi ­™"vlt_‘Ò¥Ë4”„ïl°û‹Ž  É}H",2Äçð…á³-*:¦{blŒ‹ö,L X´ö-Vkoäˆu¬¶ALû &:ª¯@ÿmˆì…ìj½¡`ö%“ÅŽ†@°0ŸÃ¹e¾81Q‘)ÉñfŠ,ÄZ ÜžX-ðWSWÁjDÁ´k &;Æ`ÿ —™ˆÃ>Ýè 5–¹Ö!ƤGë„(Baˆƒ¯±°&C•UöѼ3É:±Z”C\-öFuªöáÂÊÒòJoØÚ·WW t»º§#D>žJrNœl÷¬ò½Ý¾ÿXƒù˜·r«ì>t¼Áëå‚»¯°Òy6ûô`öbSÎ #À†Œ@Òņ?<þ‘C(üü™w%¿¨XåÒé™™&WŸ?Vz`ËpðX¾<þâl™õ­éÒ++]ã¿_ýó]9°—\?z„"üP½ùÅJÙº÷ˆœ*•´äåãê)cåýùkä‹e›T<Š™{g§ËOì.)ê\Èüsyˆk0ˆ².vH·É–lá,WÞøb9ÚÆQÕNbmÑröÈrÍÔ±)¿}öÐ3C¾yùÙžÇäœB{O¸qš¤§ÄË;_®–r$"Â"½³ºÈ·®9O’ã<ñ¹Ã¶<~h¤{fó¾ªÙ¶ï|öðu¹MÏ‘pôÉî"¶ªœˆÌ^¼NN‡Ì¼î‚V{®~o™ ÕjÁ4p¬ í×Mn»âuìëA[P?ÿþ`‘<õ“o¨z©gîŠÍrî˜Á¨+ E:{Pý¸î׃Ù_tvàMùÛ`!=òçV½8؆"®çŒ(L*'KeášíòØ¿gÃeÅåÒ§[W¿`çhý…÷Éþ£ù´Å!=³Ò0Ò-ªs/‰Ö÷nºHŽäÊ'_­W€{gL­§½Pïúâ~[‹!¼Ÿ²í£%õ¶ñ·7æHµÃ)÷ßxd¦'+éŸ-—ŠJ»Üvå9ræ>²hívùÆegC‘Ýý´µÛHlL´ú€¿øáb©²WË#w_©$Kû8šÇ-{ŽÊ × ‘„ýëùò³;¯”¬˜d•í»®†ƒö6jiß¾ö|é’’ {Ÿ·ç®3úvSƒ_x=£· ÿ±o"ä+~g>WÓWðõ~‡Û¨;3Ò¦ìÁ@ dˆåð~YBr‘oXbV?ØÜ‘G_øPÞ·F~ì§çè=‡seÃÎCr+Fö£S¯P$’!ïið<£ S%oÏ]%å•Uêƒç/öu}++úy܆dûh ëv”c Æ?¹ý2Da°~RT\¦$†ÓÏ£ÈÐÇ È{÷ÄY¿ã€ŒÔ’ «ì;zBI’ØFõnÚ‡Ûã"û‘{Èœe›!…‰–)ãÎóÆÆsråõÏ–IIY…üüÿ½#Ãúw;KMU½úéR9t<_2Ò’@ìÏ’~ÝÝ‚¥vÉ’u;åpn$ÄÚ¸úP.ŸYVmÙ'k·ï—ŸñÃÜeºSy|6Ã©Ò ).+Wû{äÉK„õ)­³¥¬¢JH H–òO– /_ƒÐôWÓÙ_ ‘Ø4HhVo݇²¯” èMÑ_Eõ•ožcú®1Õ«Ÿ,Uï(‰â L¥çä×ê51Í5ÛÈC ßéï».N].^»K6í>‚²Ÿ'$»ïÍ[-y…§ÊbG>¯ßÛŽ\“wƒ€R~kÔTL;<×ÿGb™9:y‡’Ü軾XºIuæú¸¼²V6drBœÒÒ×ëo‹1 ñƒÅ²$+· X®©S%åJ’RÿJWHà4W–nÜ-7\4…ƒž)2^㹸Øù`ÁY°j\¨LRR#^k*\=eŒ ñ¢„gÒ'9 Ä)Dè($VÚ <»IDî»þè×ÄÈ$è4=õÚJŸîH^‘80Í7´_w‰ŽŠQJ±¨ ð]¹öŠ3Òù™6q˜"{$”hmÁ`âlLGS':C$€õÊM{ mŠ‘ÛA0âQ>§ŽÔlôàÞr!¦µ¾†Äjôô&ÕuÒëßK7ª#Jw(Ê€„I‡úùö&,$¤<þÁ7/‘3údÃl:7YåýÔßêrñ½Ò×m3M/œØ¼û’e¦'bZ4Q•“÷^rÖýh³5B`L“qäÐáÂILaPÉÕ[ôÎ)³ú Ôº` ˜fã¶ñàR¢÷i‡«Ž2%‘~QC:£î‚ñŒv™’NGÕ$Ç É ±j;RN•‚”Ñ{ŠŒ)E$yæ”Ðÿ ˆýÜ{ ä×÷^£¦²ÔÍü£Dˆ!!Þm׫Ò_aÇþE^þ;{‰ç2•ýç)"Åwá/W©…»°¢ê{7_ä‰ÇN/ëp²¤ ’¢åŠÀ¤ ü…P '©ñ'„tÏp&Æï™éÎ>ôùôt§ÐDJ\*1eÕPøþ-«÷–‹þòògJÚuÏŒ)žèÞùöœÄ‰!C¯òF,ÒSÕ9þbÈ)4NEÉ-¾Ï$§SvÌQS‘«·î÷Ä犹0aÿ.‡yýuê⃠`¾$Þ¿#àëè‘áw>»wMSÓ\%ÃQ¤¯ÀÕ,w`Ê ´ƒ’Êèº VVõóô6XÏ ÊszàÃN¥áúmcóžÃJ2HOà Ý0-•åêµÛöÉžC¹r$4õ§Óî¾úŸ]#µ:QT"½@äØ/PÊ§ÃØ3zÉgK6 ^‹,L—T¤5ĕӄSÎÕŠ-{dG”Qrä=§ã™­AÀ ø7d ¼¶i.¸Rfù¦ÝP,M„.Cº²áÂÕ+þvîT:}íÓeòÜ» •$€é¥g:MqÖß4M¼ðAàAHRÞømã½ùJú?”\}ï0dˆÄ‡ú/\EÆ€¦%[vê¬Ù«5««Î®3e놿û$iKÖï’Gþþ6V“õÀ´×4¥ðü ”†i¯ˆÄeØ}3”TD+`³­S™™Ò£‡ï¼ÂçãÎǪµÿ|´DüÓ«°©ÕE¦M&¹…Å*.§ËXv®¶â”ß_~xK4H†n¸h"Æ7ª¥ð\•ùík§( S}âTçÆ¨§Ç@Bzŵ®QÑ뜦0Ú[µu¯ŒÅ¥Ö¯b$bSQY­ôª˜/?ê]uþe{ŒÊèOA׉×8åN]¬@ú”:1ƒ@›"ÐbÑæ½¿ø¿…Ìásþr ·>eçTŠ ³àäÖ­>ô·QCù‘ŸËÖqO‡ ìü¸dž:)üõ•Ïeëî«_úÓ£W#ßTpàœ€£ËÐéÚ¥!$C P¸œ;&ºuÇ0Ì?Ö$9:°ý2h¹?üŠâ!e’ϧ„ê×ÿz_}à3ÚH©–6¬¨ÓÖ[–…³çÑQµÓkúšÞ–CykÏ@âGê;%D=õ–ÜrÙ$¹ì§íDQÁý©ŸÜª,óÌ©5b¯L4]Šß¿â6ËÁ˜?»í`Ó7´S ö›¶ïÝøò“OÜŠ,äã×*ý…ß“v*±yl¸#к½j¸£ågùøQéhDÈÏ¢™h­€@sˆK®âýAn…¬¨$|åG“ ïgPñ𶑍ÿDû9lçY˜n,4D„x?z?íM„˜OÚIª; Ïs…Z!¦ôH7by?õü8¥’îqêу€A ´0d(´ëÇäÎ ÐmÈ—°qDKØ$Ct#óã[/í´Ó>1ÑQj*}ùÆ=°)´]lÑÑJoˆv˜L0:† u¼:396®Äºñâ‰An(?ºMü™`0t|Ìj²Ž_‡¦ƒ€AÀ `0´C†Zž¹Õ ÐàJ³ ;Wî¥ËoƒŽ;Sfƒ€A 40d(4ëÅäÊ ²8œ., 8d®ѨAµ«¥J€ö…¯ÝÑÐesÞ `0´:Zgèýùk`‹d“̸p\Ðÿzgò­ôMx7Á `h}rá³ëù÷Â.W‚²ÛC—#40x¼à”Т3³~熩دíbòpmÜxÐÁ+ãÒß—¨ï†Uë½³ä‚qC”Uí¹PÔNˆ‹V–ï¾ê<åä¶õK`R4 µtxÉ Ó}òÕ†:fòk‹gö ¶@€«¨h ’žÚŸy{žz——¸p­,‡3XúYó&BŒÀëÛàºBï¿=w…²ô~œ·ÒÊ4­CÓY¬- Æ ³a q¨Çð¤ºÉü3 m„@í°­ÐÖÉÃjŽ0߇±3_~¿¸ øåÙ_£>¦ì‚ŒØSf\0N‚Û» 4ÑÏ!FbDË%Ä4B÷9¬ýÒsöE“†Ë„aýT18Š¥Õ\ÚXa\vÖã:æ]‡eÒ8œ[ ¼r_vÎHå£ìЯ [Ïá9;:Ú¼åÒIª“?x,_Þš»R/LX³={Ô™‚Ñ1]!¼óåJYÝ Ús?´¯²jÛXÔæŸA °­ÒuHŸn]äçÿïeo‡eû¼ÿÆ a—'J¹¤Ù6Î@wõÉÒõÓ&¨÷r=<ºï‚x¦‹{»À!ꀞ§ßS? sl0Z/¢µç¦“eî«ñ2í &ÒmÀÌë¦Èp’JñýÚíûU”*‘£ß$z¿ù’IÊ‘#M÷/ݰiNTx¡~{î*Or‹×î’Mð-uçôódúycä½y«•¿!`{ýóeÊÝ.ŒØK¹õà9¿‰ðycË… OK7îR#`^KŠ•1 G?½ý2…©>« ¾Ž>\¸FVnÙ«ÈÑõ˜Ô††ò n2ÿ AB@SŒ‰ROÔí–.*H„8eF)'½œª‹ø×55Q!GÁù+}x™`0ÚO†ªaî~ôàÞJ óæœ>1œ6q˜ò°Ý%%Aù'¢ƒEï@_Bcàú¼3+³ú”êPÌ?mÒ0).-—œn¯Ù›wÑ釬‰j¤›¯Ök1¢¥%Z†Ó£÷´‰Cá·(ÑóˆqíN3H®Û÷å¨k+6ïÁ(¹¯\޼Œ‡dŠz åA]4ÿ AB`¦´èNc%´ADT? éÛMI‰()¢_°úÁÛR³÷5Zl..-ó>eö ƒ@›"Ðá§É(b¸ áþï…•7ðˆrÂó'KÊäõÏ–+¯ÙÔG(ƒ‡pŽ 5yªvTK5ü Ñ̯š|蛛ʃŽg¶¶F ;<º?ñâÇx/rýEZõqgC‡ˆNN—¬ß)¿ºçIJˆmÕôMbƒ€A >ž é‘pœæå(±_÷ }Zæ­Ø*‡ròåÑû¯WzB~ù3Ï5½UÏQd„åtYãßvEíÊOJAG@K'§þø/kèyæ¼AÀ `ðÓ¿úþÞ‚ñ¦Ÿ7J¨¿³"| O•bd‡©,«ÒÚsø¸¾ðv :é[ö¨eÁ”ÒÉ-T„…ÊΫ¶îƒ4§Bé qÚ &¦ÖÓö†]e/ô™¨›D©Ð˜> è¦(­¢äiÎòMuòÃ)†µÛ Õñ9ÚÈ]Cy¨s³90*ök"ÔÚ#ñ7D¨µQ5é  !6’!žâ¯ž2ÓbË<å=ÜòŸ–ȃzú:]dÚ„a’[Xì¹ÈÎõÓÆ+ñýS¯ÏQºEô¨}ßõ`Õ™M>Åòþ—àÄ’6V(™š…ixüôs¤¢Ò®t›¸¢+i.>kì$m–O—l„TkZ™£ï¡¨¿¿Hþ†g1LÑ_¬k(]SO×ÙÐi™­AÀ `0  #Сɯé*®ÚâO®ÂzôëÑ+`ô5*&ó§¥1ÿúùúÓY©uŽIz¾sÃ…J|_^YÝ#›gdüë™×¨eÅVL±ÑöƒÖgÚ¯–ýŸ+•Uv5U§pÅäQr –æs„­ïÑ×hÌî¡;¯À=Õv9a{Å-ij,ú^³5 ƒ€AÀ:4ò¿˜R‡„rŸ¯¸$.$%õC}#súº«F¯¢>ãuê5b¢}WQCyh,-sÍ `0 Ó+¡Ó‹×þg”þP=íöÏ•ÉAÀ `0 ßb}Õl[„§¿ÿÞ -JÃÜl0 ƒ@Û"`ÈPÛâkRï@8vBh³àd©Á>u¼ZhŽ3™S°¢1=9^†õï¡,¢·4]ïo‹2ùûìPˆ×ÑðÏ9~·¶¿¼rº^lï2y2hv a„€!CaT™¦(!@w;ÌÌ_µMæ¯Þg¿•PfwI¤µ\¬‘'ñÑq»F ,ÕÓc»$FœÕÉR팕­ƒ©‡X®¼´Ó Ek†ºeÚ¦sù{¤5B,±ªOik>1ôÓrŠK\ðwÔà ü‡ÿ–µ©CÇ}[Ô'âÁlS¡_Ã&‡–#к=qËóÒ)ÐæÏ‹~%úÁMð_¶KÂÉ묻§‡tžMæNG€„+ô(zý³¯!ª”ØØÝÒ5}¶;`“ªâô›ZáŒÓi“òòÁ°G5JÞŸ_) Vn‘[¯8GI‹8¥Ú’P·LËP¦r,ˆ–t¸ áJDk ÓoIÞBå^'ê½¼¢JJË«€ÿà¿ øŸÕø‡G› •z3ù0C†@™ZÅeПn7QC’ Öá<Ji¢£r%+c¶ØlÚZ R·EÒÓ߃ԤåºAæœä+3óYÉÏŸ!ïÎqºœÊ ( ‘¿R"_e¢áÑ4Hƒ¼ÜóšµN?ïeFz²•ÿÕ­†GoS¢òM! >hÞPÔGBùÔx‘Öž‡ü0¼=w…ò!Æýgß] ‡ȸ¡ýà¡{Oy•S O–ÈYp,9–¤7Ô¸¡ ýŽÔK)Ë&Â&Ñ~¸áxõ“¥Šlu…‡o:¹¤ÈÞ„¶G@“êm„k”Ù°ÎV×.o¶ Ò%& c˜—¬U„™Ræ·©pz™6* ì]R j ;}„‘x‘@~°`] ñïømJãb¶Έ€!C¨õ­{ÊPXŸ>wÌ ¹òÜÑ’ëÏ tŸ±÷H®r¸zæ>ÊI«ºPó®7®Ÿ6AÆžÑ[†@2´ R†õ;Ê”qCdâð~p³q†pÊ0¨w–L†#K>#>ɶïwÇWÍ¿6C€ƒD¨¤´Dw%¦‘Ž+‰P›=0À„)ŠÂtÝ_,ƒNQ•Ògj*‰ºeZ­ÜÀP"dBà7¾Ëo|±¢™ø‡G› 9s‡A |0duy Io‹Ò) ±ª†KË+”ãפx·µi:Uõôf­™àª :oe(€sØÄøï¨jßÛñ$•ZK¾ m‹I§Æ*«ªdáj¬®*«’Ô”ÛU"T¿Ä”¥¥Ì–“Päž¿z«Ê/óÝP8½L’šgÄ-ÓÁnèqaž¸¿“P:oþ¿M…}%›š@À!D/ò;Ã*ŸJ9QT,s lé)‰B³rË~Et(ññ -Ð G¬kàÅžJšEPÒ¤3†Ýsädq¦Ý åh^!¤QÙÞÉ™ýVF€SIT–®*ñ\¶yZ5 eé@‹Â×Äw#@ü¸únñºâßñÛ”iƒ€Àìˆ 2r@åiþÿxGéQ@‡ë.Y¾qÙ$èDÊþ󉧶Gî-£õVztŽzÓ%Õµs ûß·]q¶Þ•Œ´D¹gÆ©‚ž ”öHŸÝ%In¸h¢’2ùK¬<‰š€ðHPìv©Àê½Ðç¢AEÚ ÕÀ¼1´gÕ»[´›LÃÖ®.ó]&wû Õ2u¤|± ´70ü›nSSÆ>"“†}GAQQu+S7É—«~-'Nîj<ÖhéÓm²ì9<¿Éû›jSM&`": F2äUÑ yg© Wô&w©#äë>C„š„®ÅÜÄÁÅi»š"+Äôd„µ¬Í *¶8ÃH€vˆ˜Ç|¬Pd¾G©2Ÿe²X¡+d”…ZàKÝáïg›*.Í‘?¾Xæ®ü¥$Åw“É£~Üì,÷ë~¾L3˯û›jS~%b":† ù¨ä³FÀÈ«‹+Í?Åeögôu/Ýo~*æNàtÉDô¶**+¥¸¬B¬'ý½½Ýâ1'¡ÐÏ|3ÿ,‡¾Ë^¯0W[RO¯½D†¿mÊ᪒ÜÂí²eß²fûK2¨×ÅXeêž&Þ÷Z¹÷ê…ò£›·Éͽ•¦}Tñ- ºß™±6φ«ó÷_·\zt'—N|+^û©k—L|¼I¨kSMÞl": fšÌGEŸ7v°³-;Å¥ù&§¾¨jÈP%ØíXQfµ„¾]'汪ªZ‘!æŸåÐÁw™‚'ºÿÆ •ÃYæ§„mס\™³|s¦óê½¥$´o÷.²ñ 46Éw¯¨¸Tò O5µÍ®QÊþ·©¬ô‘øA±ß^&Ý»Œ•+'ÿM6îy ºJ‹dÒðïÈ üWžÿè”Ñ%Éñ=䯩ÿ‘C¹«eÁêǤ¨ô°Ì])ÝÒGÉÇ_ÿ‹3Ü‹=¤±6ÕØ}æšA 3!`ÈPgªíNRVN'é)%*"s5™7©u\$rÈ·ž&cYB¡Lv”µ;H¯Ìt傆G©cÓXè×#CYgoŠ 5–F0¯µþq1é2ýܧAbhc¬,Ùø$êÓC¯×ÐʧK¢ŠXQU$7M{MI€ç­RçŽä­•ßç ¸ì˜Ø“ 5ìÈÕ¹fÇW™üµt^?-slG  ÇZ5eRÒ ’ þhg_žƒŠ&= M“µg™Ja5ýX^‘ú ì•)#ôTdˆ+%/‚‹™LXW§­;Éêmû¤GFš\vöXyŽ– YÚs8O>_ºQº`%å%8Ÿ —'KÊ”ÓcM–’â䮫΅nM¬lÛö¡`n‹‚Ú§³ZVn}Nvþ«ÖÜW“â²1}¶ÍS´œÂÍj?9¡»h2´çèÏõæî4T¦æ¦gî3„† …[šò( ;òÚëq‚u*ÄÉ·}$æß]wîC©Lœú¢nϾ£yªÅÑ@év—9Ë7ÉÀžY2mâPÙ¼÷ˆÒ¿9w6Ù]SäãÅë¥ dŠ+änºx‚"8Ÿ/݄՜Q°ïUâi¹†õ“¯7îRVÃ/9k„Í-”Í{Žx®·õN[à_ZqB>Á´VýPpj¿ ís¥çtZb?µÏø:8ª[nœÕW™ŒdH#l¶cgÈ´0E@“~¨xÌãH‚ø§]m¾õq{–i`Ï ¹aÚxyð–‹$ ¦&Vlrûë+.­U[ö)Oð»`Ó‰„§O·t¥[t ÊëÕvŒ™æ+£¦=á ™ÒŸy+·ä®¹¯ÔS=;“•›÷Ê:9=vâ¤ôë‘é¹” â¿ïØBؾʒñCîVÓg‡ÎTÓfœk(œÜ-)‰½$R¥Ø˜”†¢Õ=ß@™êF2G΋€‘ uÞºû’{ÈCG"B¨Ò÷ú•¤ÏsÛÁ"¸¯©PÎn•qBHÝô̄ݭ’–”çÅnb‰eê¾§¿Ž(ôuYr@€t $)~‚‰ÿ¾£‹eÞêßÀÑrá¸ß@’vX>\|?¤f%°yä{¥à®ÃseÔ€[äëVÈÞ£ å­y·7 OcejòfÁ Ð 0d¨T²)¢ÿd¥;/wÝt8*¥°ä ,ßü ¦|Þó?‘z1ûd#Gó7¨\½Kîp'¤>ôñV?\4iVC—?þZâ`ÉùÁ›/®Ås¬ÝÓ8[šú!˜úAõŸÝÇ ×>!ü5Vm{Qø‹‰N‚µôÚUt\qöûWzv[iù ùïgWÁ}HJø§E4' ¿ð=ôðûvÑ z´†ÔäÃÅÈÛ î’¼¢íB[.ÑQñÍ*(­Ϙò<¦4²šu?oÒÒ f'ÐÆ7ÒõL‚Í¥à åýý¬áê<±þþèä˜cc¢ät€ì˜6;{ÔØËI¾ÝºÂ–NûÙª“YÁÂß›ùÈÆi§**‹Ð6jíP¡‘­ñŽ4’¼¹dèpÉW•<–/[ ôI ´E°XLÃw - %˜h8‚LžDzr<–Óö^Ùé*mó¯mÐ0÷6ðg”ì“ãù›ÕJŸ!7L—Á½.“M{ÞAýõ—‹A޲ÓFJqyŽ,YÿWÙv`¶zÀ%0†—‹Õ@É ½dôÀoÈ‹î“sFýD*An™ö†8\vùç{µn[Ë•wþu<ïsí4K¦³RgK{HË6í–I#ú«©²µÛ¨Õf:Ò΃ÇáÒ¦—|÷¦‹°š,WÞœ³B>új­\vÖH¹ïº©J§ëýk%· XßÒî[o¬uf¼Ï…þ:Mm½óßT\sÝ ÐÙèôdˆË®ç¯ÚeÎMXâ[©|CEZËÅyÚ•­Ö\#Îêd©vÆÊ‡‹ÖIRB ì´ — àÔ5 n;LM8mÆ@«5RÄãþ³eÁàùrõyÏHá§û•¿©x[ºœ;ê'RRž‹ÕPOIÞɲu߇Ò3c¢,\÷ÚWðVDµšÿxk^ƒÉ.Y¿„h’by[Íæ ¥å•òŸÙK”WøÊšÆŽý9ÂýUAJDs ¿è&–êÿÞþr¥Þ5[ƒ€AÀ Ðftê¯ð¬dyå“%UÀçnéš¾A9ò¤?Ÿ¶ N§ ¾²cJa”¼?¿R¬Ü"·^qŽ’™¥®m…:Ó L¾wîÈãã,ݺŒVDg7œböÌé^/ycîMr g™’ êy‰Œè£ŠÃ§p:íÕfàïV">^¸…§åXþFHw«ýpý§ MCå«€%ðú¡ Ò& öF Ó’¡/Wl‘wç­ÂôU®deÌ›í@Pê‚D+>~ƒúUTô–¢éòÌ[2ã‚qÊ¢/—$›Ð|Î*èùì^Ê9tt«8²]Ú+ÅU—J¼Û”_‰s%Ïœ¯á]ü7 :Õ=‰±n½Ÿã^FòŽn…Ë„ZŸs‡rWyˆ_2‘ ƒ€A Ýètdˆóæó yçËÕ·EÒÓ߃Ï*·nP°kƒ,3óYÉÏŸb&â„2ä´ Ã”#%j¼6Hz N퓘ŠÊ/Ú…)©r¢p§ïS†«Q¥å"åÐÀÕ•X¢O¯þÕÅþ¤t†¼Ï» s: ã©tÛIOì+Þ‚«[Á@ž÷3;AÀ `0´=Š ‘qjìÝyn"Ե˛mpO Sù8q“|°@$V}G ìiQ nÞ¤‡dçÄ)üjHOsWÒ4Q% ^¦¨ú@‡Ý' Ö>.ý{LUóvnX—¦„ùì•9QhU˜úFÁÎwƒ2 ƒ€A@!ЩÈW‡½ ¡è¨\% ¥6@ •ýxWyã‹e2wca«%K–;Kès†-zØpûÀ¤4רŒÌ]ªåKyþÃ?(IO`äÁ‚Õ\=$9¶¿ÄFö«+KìåI²fÔá]{Zgµ£BÞœ»L?ç)™yÍbáñ×›þïísL·ËŸ×îxY¦ÿ­\<áQyê­QífæÜуd+\fs’@J@IDATä{¹¾h0ãõ.Ìœ1Už{¡R®w©Í¹ü~X¿nòÕºuž5 ¾ÐhÕzç·Ÿ¯:ÍAÀ `NC†¸Â…«ÆŠ°bŒ:Bí55ÖPÝ0?i)³%'÷[2õV¡O&N•…›ÑÓŸˆ);tpÃâ†/ëP‹¸†A`7Ìb­€m û[-´ž’_kÎl(3÷„ÃÏÒ¼»¤à§¶± 0 Jêrª¸Dò åxN°Ä—:Ô9ù›|ºÓÈ-Ø*ÿž}±Z.ïV’®Mô½E÷êhu¶sWýJ­&£öœF;âPZÞ°Â2}=£·|±lSüó =%AµGJWƒ¸f/ê‡Ä¸X‰1+1ëÃbŽ f Ð)È;p:윿z‹Z5,eé@ëƒù⪶Åë¢dꙃ=SeQÈ'éVrxÏ|N#41QŸVbjø[Ÿô é ŠŒ°ù„¸©UM>o ð$—ÚìÕP`jç0qx?YŸ_ÄzÒˆ~XÖƒi¾ål=ü€M?w4¤j±XA—"/Íþê´ÜÒÝÆ>ÙðR RšrÖˆ©Mw¤±GÖn¯]„@ÿeô+6¨WÚ‹áÀu3$bvåþ ·î%\ªî˜Á*ÍBØõZ¼n» éÛ]*°Âl9Ò#9»ê¼Ñ¸w‹L„ #’¹Þ°Ïu!ôê O•ª¼ëLíÛMÆ é#.§ ¶Ÿv*‰=ß{?KÇm¯m¯ÌIrɤ'$.&]6ì~CM±æä¯—½GÉMÓ^…í¥ïÁ"÷•=ú(Ú÷jyé“+`Ûª_ƒ¶­Ú«,æ¹pC S!J…öɃ"m•Z>Ê•˜·AòòÀµAž ì Bd éé2oÒC)¥=øÖú&=¾aÒçòÕr´¬TÙcf¤ŽîrÛå×+ÒÊUÖaòfƒõgz›çà  ù #±1ÑòÿÏÞwÀÇQ]_ßUïÍj¶Ü-÷nÀ`lŒMï$˜BKh’I(©´þùÒÀ’PL¯6ÍÆ÷nËM¶e¹©÷º«ýÎy«·Z­º´’vWïê7šÙ™7oÞ;ÓÎÜ{ß½%w’mû³¤?ˆÐ‡+·6Û§¹EŠ8ÝyÍÙ¶ïâcÊ„ôòê'_K!½Ô]„Çâv’*£QƒSdÇlUÿ~a|îÍe2 )&ádUf$r›;m¬¬Þ¶O.™9I‘¡q0eÏC›-*F«Ÿ=mŒ¬Ýv@ˑۯ>W‘ÚÓ󦓿¿½\âc"å|˜ÏþóñšFÇriZ¯-^<ã)ÂRYºõiÀ+Èó")‚OYAI&L®ÕXw…“ MñMÉÎÙˆû?°ÕØV½Ös`ƒ€Ÿ!à÷dHk…vef«€Šáá{½ú²}‹_ßÇeÈ€DE„ø°ïmíIO鱬Ñb·9L[ŠøÈ8WMÒòÔ£Ûé"è0ÊîBÒÏ]v»eOQŽåàûÿ :¾woUvOýÎçý95~dbJ¿ >WÄ«a¼T0ZÏÒéúzjG»=Teƒoh;Ъ×u螣OVWÔuɶ繅¥*}¡F†‡6¹¾&É´6ŒÆþÚÒ¯U…ÙH£AíNNa‰"2\¹5ºíª9ȯ$KÖ8B0íï¿lh‚H²H†(ëweª¨ÓÃp}g#7Y-Ÿ:ž+—A3õÆg…èW€Ò ‘ ½ñéz¤2iЦ%ÅÉây_æœëHèšš#á!!²àüéªþÀÀ†ðúXjC;þÕ¡½ÁØßk½›ë:'þí¼¦˜a>>z(bT} ®5røÄ*E†tÝ»ûnêÈÊáWHv;L’âFËÒu¶+¶•®£¥yk×TKû˜õ¾†@!C6å4ÈÈÒÝPÑÛP¡^BµµµðM – díæƒ¸'$ Àj¹áž ±ÃÓíƒÂ"ìéb±ƒIK‘Ì•Oëë·5Òƒö´›¤ØdØ•’¶ûÖ[×GµT5±N¤1·29ÉP—¥9¬Bñ‚+©ŒírÝÝ]Í#a!õ®Sõk®?ܤúuZg¤fcwa4öàú ñÛö…¦èh£") ÑrôT!´u1ˆ½T"É 1R[9šÿùîrgyæK髜›SúÅIfö)ç6ký±™òfÄ dµžd‰d‹²}ßQ™1)]*àßD2æJ†N”`Äe´2™%ÆEÊ41'¿Tj¬VD«Þ Èšª¤þŸ>–ëºÖ–ëжáßÎk*-iš:,G$RªjŠ‘¼ß]‡Þ•ÙSîÉœ*CSfªmÙˆY5aØÕõû5$Çum¥ ´ò¯#×T+Õ˜M¿FÀïÉMdô!)*«€@Ç—¤·ŸQ¶³¸¼RåF µ©¯h~-{R˜‘=_ÅéÙ[§gŸÔÊ>"g>ç?w«žù´—ôØ,»‚ëv7CzP%S.vVÝ“ á0ÙØìÀ2Ìk 1Ûf«‹HhWÚ#Ž>Õ >•ºN–3¡™‰gôµyf&’#-Ô MHÖdœˆÚÞØ,¦Ë¹Î“Ašn»ê\)E×ýÈvï.»¡ýL‡ùìî󠥪–¯6:^ø;þâÞÓæÁ‘{§û.²vû¹dÖd±b¤=q¤Ëan´›3䆋f ÝMÌNeòáŠæM}M*tYAmƒÐ1üÛwMéèãtú?‰†K 4rIΣ“eçn„Ùð|E†vzGmkOl+g%Í,tôšj¦ ³Ê Ð'hßSׇ¡¨ƒC%–Õ5V¼0<—k¬;! °Ô€YbÛهΊ+éÉ+BŒžâý’W´¦#à°Òò*yvñJéJ„XðOÿ[ªÊóø4}QVlnÝä¼9ã0ÌAyÎòÜG‹Ë¬ç½å›•YŒíÐRòôä¿?Ò?Uº½‡OÈ^ ¥×mÐ…¶ÀœSF—Ñ„Fq=–.×Úœ8²kÿ}×T.î¹òÊ<9}Ì÷0²ðIŸ!÷ÊT6ê&˜ÈFÊGk¬šÚ™ØV®}lÏ5åZÞ,ú*~M†øÀäDÍûÃÓÛO8GÅX¡ú×mgû[2—°/Ò“©"2+Ò£ˆÏ¾–IOË#%¢ik‡ÅHOs]q’t„Zµ˜p8ôÖ©œpÞJ†˜¯ŽJ¡~1áhuƒÏ“î_ó}b"Ôö‘!]O[sw"äZ¾£÷Q{Ê»!×cµ´ÜZšµ´okë‰cHP@ñoÿ5µbëSrþôßÊ„áWKÆ‘¡1;Ѩ9G>TÛOì‚yЫ3±­\+mëšr-k– }¿&C<±ÚLÖÚÔ/¶—Dˆ“ëËB“~iæ+-µ=!=8tQ£µŸ§âô,]•'{3k6ÿë÷O^LJðíÎñàPóx‡¦§½çÉI ã¢3m‚Wrž,ÇJÓ1t{Hå¢ko›Ù¦ÊÊt™él/-_º/¬Ç±ìÞ§@ô©FùÊ„†t0çH{׉rïµEhZó¡c8Évÿö_SÛ¼&Û.†F(NãrÏ5ëÁÃOýgx£uüÑZl«&…]V´çšr)n }¿&C$Ž ç˾$ÌS–[¸_N•äJµ5ο¤é@W“gpÂØt„Ʀ•OW-ñ:Ò‘Ê;ÐŽž-Jm#xs¢zPP ˆ–œòZÉ/¼\ú§>ï57ëìÁR€6…ÚexrŒj¯n»«¯XË}²IAq¹¤À £Ð½BŠËz?®R{ šø…Z:‰û¯)»Ý¦ˆnq¶W:Ûª½×T{mÊü¿&C?g!Ÿç¨1:K!Ò»çñ÷­kª³'µ¨¤Ü^[SSÐÙýÍ~oCÀ¯ÉÁÖ>C>8(\Ì{Ye'¿~þ«ÆV+‹Þ>Ñ¡CxîiF›…ºm²?ûsY¶ù1¥9ºðÌ'0tû„$ÀO'}à|5<öƒU?ÀðöÃ=[÷°Ìžú€šÞ[qwg›ÑWös!t¸îèþ½[Ó†¸nÇcÂL뚘8ŠŒ~8Ô¸0½‰k¨‡)­J‚`2I@DîJÄꫬ +|y¬0_‘¸tUÇcV´ «¨`»„[”Œ+’´ØØh‰‰ÁDB„v²½l·«‰L·£c}B@Æú>Õ¢/Å‚oµ67d Fú%ù³4Æ¿®‡ñ÷kª£çŸ÷%>Ò,Ù™6`_×{·£U™ò¯AÀ¡NE]]­œ*Ü¥¶žÈßÞÈL¶~Ï?ðµí½•S¸G†¥ÍQdˆ…Ë*NʲM©ýnÿÌñ Õ²ù×"®S4²íذöÀôyoØ•2ÔiFâÔ¤€\¨T *ø¦£Rˆ\\Á0™T(Y%A.£áUK­QWM¬®½e;xEB¶mÿDoð{Kƨ³¡IQm9ÒÇð·yoãïí×TGÏ7îKf­+Ú¸â æOá=ëzw´:SÞ à2ÔÁÓ‘,M\&Ÿ²'‘D²?^¦EÎZrQ ‰R´E±‘‘k,[¯6ó¦ð¬ˆæÌ¥`Í:°)‚%.ØŸuRFNmºG;×hÒ S•=’ÏlGˆúãÐÅf••U_""[ĦS t…ñ%Lá(0¶#0ä DˆÇth…à#Ó5BÊLæÔ 5vœV•¸üëHŸhÒX·ã ”UY¡qr˜¦Ä˸ái.5úç¢7àO’í ×T{¯Þ;d[ŽÚÜq¯êûÖÿYu{A2å|C†:xÚ¦½ Ù¸'È¢wf*?¡.|³Q Œë£%>f˜T#ŽHIÅq½ÊÌ›" ¿*ùP%¢£NÍêßÿtðˆô ^]òuÌÏo½< ‰V;+$$!­% ƒ²rÿœ*¡*:VãÅåHRç1í>5=4}9´BŽÑm4‰ÑL®&šÇ¸ímKÚÓ§B$,ýzw–TÙ°ø+Aë5zHªŒÚ¿­êýf{oâïk×T['½¦Ö&¸ë0Œ¬xåヒò¼WyÏòÞÕ÷1|C†Z9g…ÈêÎD]ƒSΔòª<¤<(&hâÂäÀìb•afçè däþÌY ‡ÃONÿ–ËÛ,“F\'Y'¿Vu8 ˜…æàW¥&BÕX®®¨(-Û¼zùËçο÷åVÙo»æÜ.™Ë4yP/Ghlš‰WDH9W×Чš¡ú(Z+Ô™0UõJ!'ùbJF¦ Œ$ŒÃç©¢³4I#ТC#Ä6¶%ú%Ï~4×§ü¢ùtC†Ø˜=:D¨N¦Ž"£‘%žÒ™>µÕ&oÚ®!Ô8õ4þ$B¾vMµuþxÏ-’íëV/*-U6WÞ«šÍP[ší^€!C­œæ Ú¼÷e™Æoä‚éÊŸÞ˜ŒÌØ/Ë¥3ÿ(?ùV†œ„/Ñ:ø%D uÖ’»ANs«\ÿ†ðæÈgëqn3 -" Íd|°"RŽ0—Cå–U_m‰OJæè5ò¶Øoº|–¥³"¾õPu#.kí5B5p¢Ö‰qm6ø ñŒA"´¡ÃÂcª á’¨â1©bœ ’ `¥-rŒkFȵ-õ©í_¶ù€ÔY‚$ Dˆ2{ÚH10¥Ë}r=¾·/÷þ$»¾zM5wN©"Ú¸çåȾ=¯®_öÙ:”ã=Ê{•÷,5C† #¾‹€!Cmœ»Ï6üR&ãËÑj­’£9ëå¹wf©!÷Íb¬¬*’ÿ,ÿ¾öc”&©êÍfT±óZ‹‰ÙrLa˜B¿|gñ@O»Ñ'ò íߺ𬀮úñ%©¢9“˜À\¦H4':1.µ&:9nWÉÚ­Ì^ÔT © dŒ¤ÈñÛáXÍ6uVH¢tŸÊ«jåýÛ¥ £Æhc­—Μ$C$z¤Omcoì§1uàÓ3ø+²ë×”>_ô¢iŒ¡C»Þøü­×øqBO|Þ£¼WyÏj3|C†ÚqÞj­¼çKsDȵMjFÚ€&CZ3ć-½}Õˆô/ß]¼$ÿÔÉ“§Ïž{ýÿ½²$vBú@ûã‡[&¦§u*_’$"|IÒtE- ¦õ(2W'ȧ'S;TëuíF©•‚¬«¬²Z^ÿl½”W×"—-¢üUsOƒ97±‘F¨+}j¥ ^·‰˜P4Μw'þþrMÑéžÃç9jŒÎÒuÖÚÒ-«Wü{óªåÔ1™ïON®š!ÞÃF >‹€!CÔ‘ ‘q™×–ö2Z!€aÄ·ðk2äúʼnüLu¶XŸ8[6[Œ„…4JîÚŸèDçéªÒDÈ•$ñK4Sè¾[vcÚ‹e'¥=Â\ïƒÅ¾!©D~ï¾ÿ¨Ùk?ÿðýçW,]šÑ70½ôšÌðþã¤ý÷x¿‘ôh³5B$BF+Œø~M†\OULD(‚ŸEÀ/$ ~üØñNaûØÎÈ>sjÜOÆtʤð!­óÁÌG2DçjšÑ’6¥õ9"”zëïû¥Sý‚¬+?[ú4ˆÐNþ6bèú^#¢é‹÷ ï7’!’"f©çÄ{‘Ûyo1ø}âK­ÊÔxY‹t̉ìäÞ*lsb¥ÆG:?½µ­ÝÔ.ýPæÃÖu™`>ˆÕ(3Ì•s5æîd¨O¢˜˜˜ ;øé£‘Q#€¥nÇæöþû«?̓@»à=FÑ÷š&C4é"#þæzí'¤÷Å*#ßFÀïÉö¹IŽ‹’ðä›ìÕdˆí£R¨_L8l> £|û2ëpëùÕÃuÝÕöÔ¹!’! =aÑ¿%22Òrσÿ!"*jšîéñ£G]üâ‹Ã¡ëfnhÞozâ}çJˆøQBÄ9'n3!€`Ä¿ðk2¤‰CJ c&Ê–Ì:©ª‚È¿G¼îL²]••é229R1ב¬oùz'p¯kp÷7ˆ]>¤9תyÍ’é‰$¨Où ýè׿}Nã ßJÊJJž\ôûß?¯›¹A ƒð£èûM"MŠôo½ÝQÚü7ø~M†xžWÄÜ.PÆ I’=Gs¥ ðrIM}^,üÐñ©³«v…ÚexrŒŠ£ÛÍ>ôaáƒZj_í4­µAœSôÜñËÿ?òÇ?ß‹Ö7é®!`äß~ÿàÏÓ¿ÍÜ Ð 4â\O$>zÒë:QµÙÅ àø5Ò’ F† G¨©Ceí~»äç_#I‰¯{ÍYb{jj“e| óU×çªrD)ÖýðšÆöNCô™hŠ&BŽ_}€ýîO»X‚\ˆý¿»W¯¼O`æ. ÀûK‹¾×ôo37ø=~M†xöTÚ!æƒbΠAI±’W\!ûsÆ‹ä]'ýú½Ý«"j„H„Ê+ÆËÀèI‰‹T‰;ÙV¶9HEJö{…Ggn´>õÀ~ü¹çÆÙ¬uÿå%í˲ztrâwY¼˜Z3#ƒ€AÀ Ðúr˜ÉH,˜)< ™ÂG ˆC"ÅZ9\8^j­‰’÷a¯øÑG(&»ÚÚ$I°Ë øpG&sf3G[Ùfâ¿ çØìêã<þÒKýêJ+ÞG7bظ k,X@ÇV#ƒ€AÀ ÐEüž ÑÄDBÁ åá ‘‘RÜ;é©1€äƒ…Ir2ç{0¡( ¹ßÛ­qˆGˆÃçËÊ'KeUºYl2(Âb E†£ œG¨¶²Íl;û`¤o"ðüóÏç–V¼‰DÁŽ!ôHAf±_qÿÂÛsú&"¦×ƒ€AÀóô2 ²“‡Ág(*2Rª«kàŸS+¬6‰ *•ì¢j)¨&¹ 'Hw³Y…cî¹o¦±Yc‘$BÅ  Jª‘¤ðŸ0Õ®èè(‰ŒŠTËl+3ª;F”2äùKß7j̱Öý>­sê[[h‘¼ûöí¾ÑzÓJƒ€AÀ àø=âiàh,:P‡"“7µ.4‘Ùl6•¥œÛI8’+«¤´ªVÊ0À¬¦ÄÎÌÚ£¹žº$Tì 6+*Ø.áÈ+J3µU$h±±Ñƒ‰„md[Ùæ>>’¬K¸ûúÎ?ûì¶:ûu?p?øàÝwÓ\fÄ `0<ˆ@Ÿ CS´C u`6$Bšà8ˆÌhAÁU%1µVÁpeE”êêê˜âÀ#p³ <–&fÁÁAð ¢Ù.\¨"Š‹UË$Hl«Ñ yzŸ¬äÉçŸ\[[÷¢c¤3ý„,ï=t÷ÂßûZg{ö¹_ÕÆ6Ð Z*Ï¡‚LcÔ)3™S+¨Êwù › |eË–­Þ½çU4<ÁÑxKV Å~«{G{fÑ#¸.ƒ¾÷î_¹osÿýäsÏMž°§§®ñññK´¥úÝ3ÏîÑ¿ýÁ»îZ«Ûùè¢Eiv›}gìð¡©¥‡Ž<ÐZ¿Ð^ˆ}ÏÕûsþ»¿-Ú3÷ú‡ï¹û6½¤2ÖZkÛŒ†ëufn0Üè3dˆ'!¡hM ”9Œ=>:U CUõdˆÚ!›­Niˆ:K„Ô\ŽEMM_­cdMb4“ED„«‰æ1ng[ôMVïÎø’g×÷ÞdùÖ/îZXØ4¬¶ºÿw ²òZÔ‘ß•z<±/Üò^³Ùìס.'‚ öZ$Ÿyï—\R ²Óúa,r->QJŸxöÙῸûîÌF…írΣÏtïÝpîyüùçÇØ¬¶çPÏH„ÂÜ}ëãÜsç§ÜÖœ„¼^c³/C›~„þÕ«^-×Ù-–ß4WÞuÝ£Ïüýl»X3,ö¥V»å&lûµëv±ü“aÞ´;ï¼Ó{BÌ7j¤ùa0x}Ný@’ájŸ°0E‚hžŠ‹‹‘~ ñ’Ø/A’“%%9Q’““$%5YMÉ)\îØäا~ÖÅ:Q7Ácñ˜<6‰ÛBbf†Ò{Û-Òsíyæ™7¢@ þ ’ h4®ÕOào£G[­Àöººqy3ã0—çAf”ùìá{~ûŸŒ >‡DˆuíËË»¾ÎbO‚ùm"8úµ QAb¾7ˆå KýǸ¿ùðÝ o®_w&ˆÑ|è3çÀñÿïžÇþñn ÌÁ(·GpÜÁ CZĦŽëدéÿîº 1à%ç‰E‹T[è…ß鳯þ¼ii÷5¶[ð¹ðª%2ò-‹Ý~½ÆJ— ùËÛr­u?ÒëÌÜ `0´…@ŸÒ ¹‚¡µ/*B5LWôR¦18MÓÁZ;YÓ‰š‚‡®ëîí^&ù¢8ŽçðQ"á „ö‡&3µ ó™nO»+6ýBÉ—Ùv WM>+ÞâÔœ´£·({â¡{þ›Eq½¾ýè3Ï=ÿÇ7Þÿñ‚•o]‚«î9K Ê?• '¹ƒûr f¢Ü \顸/þúó…wìúù½w4ìj|î{n=ÉÐ6-ƒsÝ<,þïÇ·ÝV€ùJ“'-ÚdËxjžZóO‚fçuè—®ã~V«}ˆÍÛm9D³/å9ù÷ ýÁ­7ß\õ»gxbÑ?f£Ž¯0) ª´Ö†ý¤®ºvó£û'òíØŠô637 -!ÐgÉ!Q!!¡yŠš:LëQd$@šéyK@¶´^“!ÎõÄãi‡j½®¥ýÍú¾Àã‹Íï¾G÷Ö ?¸áÂVÄåºÉ¹¿ÅR÷èßÕZÊËC±® ºóà7ÒŽ(Î}íôòÈ~ý–ée=•·Q/ƒ¼”Ö‰%’¿áü<š¥?úìsp•“E”——·ªu°X^·ZêÖâ¾úÁ£Ï.Z` ü™®»¥ye^ÞÕ ‰ý²+*7ÃlÇbIuvÛ-˜;ÉW>tûí§@‘í毖 Ð¤ÖÙEn6b0š ЧÉFC’Š+ Ò¿Õ†NþcýZô±ôo37¼øâ‹aÙUÿÂ…W¡X>Ä0úÿu‹Ô´w‹ØËAÈ¿ÿàÂ…«›Û§9­NsëÔ¾ð-‚îó'Ü{× šú ìùm:iÿbáÂà 4G_ô÷‹ák”öà=w|õÐ=w6×ç:{åt‹=$hWWÛ#j-¶åxùåÈûoº©ÜY Ý}çs Y·Xkj.u]o– ƒ@s2Ô *†°4ŠYÕm«¨úˆÐèú”Àtu—§>~¸ÂjŽz©¹X>BôˆÛáh¼ŽÆ$1IIRÓ"áqìÔäÿ_>þ8´8óÈðà`Ë6n,²ä}ä†ñEÛ xÝn·=ŽûíML{t {=úÌ ƒDªF=x÷ÂFea*û²¦´âØíe×]YF›-DÐÊ·`s4ª!Wp̲AÀ ÐVUÙMJ›ƒ€Gøý³Ïކ&# êÅbù)FuÓ?=5zF¬ö—@>bpœ~$!.·Ö–Åø<ö¼½™……©=‡ÂCËôÿ r%F¡mÒ˜Ê6·§Œ~{¤l"ZßžÆÙ¡9:¡' xûÌ€ÿs'MvËKÀïæÆ{;~aØ=̆–@ÎT³Í4ë >@—w<ü»åDñï>2‡óf„#TÂ1Å`ê‡)õ;?|àÏ“Çûã/ÂO#Þ‚À_Y"»Ùøâ½m*ÁD_óUÝ'Dä=f¯P‡°ÈW½5/|ø0{^˜ôµ!ÏðïðÜ*¡‰.¿¦&ê'wܑߕãRCÄ IŽ5›ÿŽ#ÐŽ÷IÇ+5{Ú€1“µ$SÄ ÐünÑ¢9bs! *lˆ»Ó ¬»Ó(öέ·ÞZ…CUýôÎÖýuÚjŽ!Am!d¶ ÞŒ€1“yóÙ1mó[`Ú± Ðÿ9;h——áT¼ÅùÛ, ƒ@!`4C=µ9A ÇŸ}þFn˜¦ÖX, 5l5Kƒ€AÀ ГÍPO¢mŽeô¯ASÄÁqbö<ÝNÓº~37 Ö0š¡Öñ1[ G ôðáQé@U±EN†FG=åñƒ˜ ƒ€A ÝÍP»¡2 ]G¾Bö:¹_×d±üÁ=` Þf惀AÀ Ð32Ô38›£O<ûüU7?ªŽ¢x{Âß 4ƒ€AÀ л2Ô»ø›£÷1à+ÔƒËbyæž{”õ1Lw ƒ€×!`È×Ó E€q…0‚lºêŸÅR ñ×¾š~ _BÀg¨³NäË®Ìc’_\&E¥RSãˆ%×-¡{½ôŒ††K\T„$ÄFÊøi2¤¢—¶ÔóÍZøÈ§ÙlÖ‹Äbl·Ø‘-]¢=ÏÖX]Y;.4ÄqËUTV-*}w={S›A “àÙYj±[Žc÷#KýîHgbÄ Ð7ð)2dµÚäË {0íVˆÉà£ÃÃ$:*LBƒ}ª+¹º Kª%ëxž”VVÉû_m‘Øè™{Ú9 <ÕØ#Çñ’J,?úÑÃJÂʾxŸµÎÚß`±GG„ÕÅÅDX‚ƒ½[ˉÄZU6d6Áœ1 !:r€—`kša*$˜+*©°—VTàþzìÎG;a³Õý¿!ÿúë_«Hå%ƒ€ß"à3 b×ÁlùïÇk¥ ¤\FI•‹fŒ—1CS$<´] ²ýö²c•Õ5’qø”lÙ{TÞ]¾Y¾Ú”!×_t–Ò 7¸o“"2º€[îøÂʰÊÔYŒ>À~Æøá21=ÍÊÜw^/ëveÊëv©vJI/éõm6 ìs¨ŠŠªjÙqà˜lØ•™ºó@öS'mÇïûÞÏý½=ùë¥@¤/)ßûÜЗ;ìdès¼DÞúb£¤ö‹‘»®™-ÃÒúŽ9¨=' áÔуÔtèXž¼ûÕ6Yôæ2¹òÜ)2oúx DÒ+’"6:ð»?ÿå}ÁOöOе_wÁ™2rpªÏ©¼¶íËrÂ?iÔ`ç²Y0xøÀ3' çdÙŸuR^[º.åXNч·?ôÛþñØ/ÿ„öÖy[›M{ ]EÀ«ßˆÉ"Ÿ¯Û)o~¾A&Â'æîoÎ1D¨3N¢xϵs^ï.ß"Ÿ}½Cjk­bƒ‰†xúˆìPã|ËÿDè©Ódz҅†fæ”H^a©Z(ã†ëX:fÉ›àýö³[. <}ìP¼+,ÿwÛƒ¿ùÚË{Óç>H¼gÓ¶ÞGÀkÉ_Ü4½õÅ&™”>Pn¸xº„àEb¤mˆñ"n¬Ü.Û÷g)BTWWç „H™ÅÐËàëïýÉÅÁÁ¡ƒÙo»æ\‹¯žÿ½GN8OÚ¨Aý%8È\ÇN@Ì‚×#ÀûŽ÷ïC }êæŸ>xÍw‡!D^öLÛ‹€×’!Žûï'k•iìÚù§µ·?¦œ Ä-%!FÞ¡,+¯t"—"Þ¸Èk2x„3ccââ L‰·ßtù,Ÿ~èA ýÝŒ|Þ‡iÉñö°°ˆÌŸmúàµï_Ä×´¹wðÊ‹™ Ž+,©+gO6¡N^#ü¢»êÜÉR\V)Ë7î‘êšáˆ<âë¥Âë‘~l¡“Î;÷ž:»¤ÂG(ÐW5Bĸ¸¬BNå—p¾[2b`²Z6ÿ ¾†ïÃo]xf ‡öO›<êûh?ïU¯|‡ø¶¦½½€×]È4a8§,Lj(~Egé®]$Ä8~½û°Tb~ ñõBÿ!jh?âðÀðÈèè»&¤´û¢ë;˜ëü9l@"B™³àsð~ä}~/Ï{•´Okn}î$˜w ^G†¨µ8|,Wi38BÊ—eÕ¶røx~¯w8–UTKæÑSõÚ!«7j‡œZ¡ùW_w&@KÂðyŸÈfÈsžÿa’œËfÁ à«ð¾ÄÇTÊU7ßÉhêF;ä«'Ò´»^E†´VhWf6b㈊#Ô¨µíüñÉšò³¿¾-o}¹¥É¿i©Ú¶÷HƒG“Bõ+¬Ð dn»\Kû¯Ø¼_‚صG@ƒPUcmOÑ—a<&âÉ>W!†HMm­·i‡Hz´V(,!9eb#ÙG¨Ã}õ¶Žœj ÃCú÷ó¶æ™ö:ŒïKÞŸQq±çcg£ê0‚foDÀ ÉMò‹ÊTdé®TŒ Qé:\ÍAGO"@¡#mG{Nƾ¬Sòñší)Ú¥2$]¯|´VJàÛÓB£©»iKh*«UdÈ«†Úk2Œþ‡ ޵3Þ‰/KRÅ”C#G Ã9H†3»ƒ€¯#Àû’‘ß-CÐÞ³ÆTæë'Õ´_©8½šÈ§N§L±ÑНp <ˆ)} Ã<±}¶Ò61R³«lÚsD¾Ø°W/ ýRäš¹Säâ¼ÍRyU<ùÒõWÏ*>¦†«Ï-’ĸ(9cÜ™1i„ªŽþ!ï ¶OyeZÏ!šŒ‘ð¼ùù&Ù4EÅš¹xæ xáƒ5J+ôwWª‰?¿ù¢VãÚöö.ÇO„Ù—ªjh†0R/,ÔáHí%ÁIÊš¡Àà ä¸ØH$Ÿ–c¸F´0êtG„Q€ÃBBp­t†šZ[¯< ã8¯ÿÔÄØ&]GÚY½}¿ÌAú˜Èðž'½Þܶ&`yá ¤À È-(è¦Q3T…‰÷°×ŽÌ@ÛŒZEÀ«"P×aød¨梮惋°ŒÞ_vì?Ö@†bþòs&©´•’ò*y$åâ³'È ”x¤³Ø*ëv’©cËд~räçºy§ID8ïy‘¨ˆP8¦!ºó$Ù}è$ˆÑ™Ÿj_h– C"Î æŒGlŸlÉ©´Çý‚:-9NΚ8\jÑÇÿ~¼Ç‹CÔèÁ2eÔ EÜØä¬bñV£ tðñ,©¬”j¤î "ÎÄÛK„R^‹94Ð…\cc^Ò!6ƒÁµ0zz[ÂóñÉší² #)Ë@\9<}P bFÍPZ¥WlÅÀ‚=ò‡û¾E3…³ºGÿùž LN›/Ÿ¥RÖlÛûWÊ€¤x¹tÖ™6–ð òΗ›déZ‡Æ“_ù4ß-¸`ºôOŒk(ä¥Vl[MîüÆyMj+(-—Oï´±C»L†\ûÃO 1QrþYãeæ”QMŽ«WôTÛôñümÎûqí9¼ž÷¬ñò·Üû×W¿ 9y*R²ÍZÇÜU²™íYïad¹¯Å°rŽèq•ŒÃ'™˜>@â¢ÃeôÐTÙqð¸ÄF†K,2‡¨mŒ×C‰ —Y“G S|”ŒÚû:9mOLPù0‹\2k¢L™&ߘ7Õõ0jùœ©#ñâIÀÃ:BRcd/Ìp|á Hr|9LŽwŽžké8M*íÀ ¾l9šÌjmˆHMlzYøVçDÍ"DhRÛ¾—וÃç;wo‰ìÕ[åãUÛ@`&Éc÷~S~rãÅêÃàéW>Ÿ—U:ÂïÇu£%·°D²aþ=mì0Ù’‘%_ï<(·^1K¾íJ9gÚhðx]´Ñ_öòÈíWÊ·.:ùþÊ„äÀÓrë•çÈmWÍñtµÍÖ§ûs÷‚ùøÈH×?]¯4½ÍÆÊžl[KmðõõxvèÞ»ú>öõn™ö÷Q¼L3ä0“yâÍ:F᫺æ‚L˜ÊvfžP)*ÝÌû³r…Ú¡¿¾¾¼áhåUœmЗ3$~M‰q‘j¸¬“…j9­Þ$@MQ|¬c;7”àKŸf·#'ó%D‹±"P¦%ié8-•o×z`Â8Cl/'/Š7ć*¨õ„ÈÞÊhWO½¢M­Z4™Ö¿Ýçµ ¨ÌÁ7{Ú™{Æ8µ¹÷mWÏ–Gž}KVmÝ/ç1V™œ¶ìÍ’QõÁ¹Ìk Ékå=$éMŠ‹Æ²Ãñ¼%"ÄʃQbš‡¶A%ü –‡ŸyKîüæ\yù©Ýzüû×B[š ¢¶¤¿R}PÜpÉ IŠ‘?ýw)|’\9gšjóɼbùëkŸÉw¯š-ëvTÚÇï\z¶ÚæÐle¨öž3­±Ö†ûýçã5rç$Ž×_4C†£^ÞÇ͵GUèòO÷‡«¨Ý°ûdçÊ( ÿ‚·A¢/âÊ-ûäžëæu¨mGOÈhìvg‡é1Hiy¥ö“»€QKíviš/ªûTß·^óaíÇ€›®u#^só¡ç˜Ð[,{B‚ Õ;,&«c²f+jlÜ%44Hi{~}Çeâœn¿Ì½˜úÍf½ôýAx~yÛ¥rÓ¥3œå’¢Õr|!(ŒåS’¥eÕ–ýrünºHî»~øŽòz»ë¼µã¸–ëè2Q­SDˆq†³óŽÖãáòú‹’sõ`E³|ž Q“C’A B°Å¸èb¬VºýËËšûŒF7ŒI¹IŠ–ã9²”0óvØ’qD&ÃÌJ ã$ÌOäÉ¿Þù /þ"æ,ÜÂÂhMiv ¦/¬( ç3¯¡LÃ×Ì;¹òâ{+02En±©€Ý__û\‘\³ý€³æM‡¥ÄŽaø‘Q s%ûTPó5fáËfO‘5 w®òÊG«%<,X~øí % æ½W—¬un¦ÆÕµ=Î . ÕÀnëÞ#²zë>¸MÐÀ&ʈ4G€K¶ƒš/ŽR½ôœÉˆjÛ¡¶½·|“"AO€’tRn¿æ\5o­Ýª€ÿÃóCß³®÷°÷ØtÍŸð2DõË™/mO ý{6 à ëä—¦»ŒÇ˧ ¤\ˆT:>îˆ “¥¢`ÓÆýlèËAV 4,_mÚ笎>!ô'Z ÒÃò+·Pf9]€š ¨ˆ08L[„£ÔøÒ’ _F™Çs¥Ç¡– ¥ãè}:3'¾Ô9±îL=ݰëÃÔ«®ÉÎö•‘§µÄàziKôHB^#€ÅåŽúèÿCçãøVŠqQ¡‰ŒBÿ¢~ûÙ‡xR¿|î¥Ùá9oNx¿ðî ù墷d-ÈÌEgOlä¬MmÏ×Ì•ÓÇ SZ^Û7]6Sý¾ú¼Ó”Æè`ö)9sâÕŽƒÙ9ê0›÷Æàaê⚣#à(ýKÎVÑç9ÞÙ¬rF®YSFc”R¨œ5)]¨q%t®íqîè²ÀûåÕ%ëäÏÖ Û2kê¨Fm¨ª¶Êý7]"ó¦kâ£ÔZÛxˆ½GN)M\Xh°`ÁgA FA´§Ý.Mô×E×{×_ûhúÕð*3ñnéáÝÙs1jòÀ@‹L9„£i-ü¿hÆxùtín|=nW¤äÜi#e(|‹ÆÁ{ý®#òÄ¿1š f‰ï^q¶œ{Ú(8±îÃè³ 9kÂ0åt­k½õ¼ÿÕ6Ù Vú+¹FÏž—ÆëŸm”_>ÿ>œ]ãåœ)#…Ú ÃÌ€c5ÿöÿÕm—µz}¼ÎÌ„³…—dgêôÐ>~õP-)kÐ Òÿ¬-Ñš£Rm¢Þ‡~B¼f(tЦfcÌc±1‘N™.;vØùí]W+ߣ¥kwJTd˜\pÖ½ÙenWÚ¤ùgN€Æt`Í?"´âc! ÚZ¥ôS‹ùÅå ©ê^Ù’qD&£ ¡=š©‹:ç$m®#ê§6Ô·±¼x]¾ôÁ*gy:vgcð}ó(®íqrY IñÑ{¾¡êÙ¶ï¨<÷æ—‰:´óxú d … °9i­m,ÏçÀ2¤³áËŠÍ{YãúÖÚݽ>"ú¾í#Ý5ÝôW¼Ž yhŽÊÒÂÑ%¿»ëJýCʃå÷ß¿Æù› sO­&¾t¸#¿(ŒÍsï‚9J[Ãõ”ygŒ‘sñÕIbEÓ„«L7TN3Ds 7t@?ùÙͪ!ôôip—+‘Cìâ³ùµlQC¢[;Žû¾þ÷Û÷}†Š‘W ñÛ’~ð?ã5¶ë`¶2{éòôÙ¡“ôü3~D\ÏXÛögI¿Øh§‰L—çœ/ý«Ï;Žý9r“œåºÕ±LâÂÑg-IpPÃ5J“îÆÝGœEOÕ;†Ç€hQÎ?ZÒÝ TQ’ )?$Ü…#ÕÖ‡H ––0˜ª)÷A«5Ø-0¥þ8rmÞ¯¹9 ËÄôê:t,ÇI†ZËo×ZÛxŒ‹Îž$‹@®ö!héù –Ó‘ñ[kwsm3ë ïE ñÛ¼Û©z½ØeæÒDȵáÐÜð!«…¾HîDHoc\w"¤·qÞÒÛéœéúÐní8zŸ®Î½÷`»Ú/ÚŸq‚´DÖ‡eп››Ó¯ˆ/ÚUðyùôë0C•œzRþþö2¥µ™9y¤s·i CÔÀpT™6‘q#µ{WfßA³R üÛœ;vrÙE ÿÅú=Š˜}öõ.u¯ ¯÷É™>~(Ö—A‹zP¦OÑìQFa”&Í^k¶íWu¬‚#³ÖCóàÇ«·©2Œ‘ÄöwD¬ÂOÓ1{ýÓuð¿²ÉàfHYsu¶Ö6–gÒètøF3u´Â38ˆ®m4»w½Ýª"ÿùç—÷²ÿœÓ“Öhøük­Tmã‹Ù ^Î=ÔÛÞ9 ­cg/šQשwÀñàQù2ÖÒŒ&Pos_2sü\„dƒ#¹Hô®ož'Aõ/`–ç0ޏ¢)N;ôr}Ì®KVïÀHÅ ¡™fƤ‘rñÌÉÜÔ%!ºöü3›h;F­ƒFУܿ(M+ކe¨Õºãš9Í‹ŽÖ4Y½üájEø/=gŠŠÏÂÔˆÝù9ˆÂ¾FÂH6 û7þ®Æ\µ¡…ô£zì_ï+Ì8Òîú‹ÎR¾K-o´ºµ¶± ñ~wÙ&¡ù¡4HÜ8’Œ>Z-µÛå۩ѱüê‡c ƒ_Ý·~u~Lg:„€W‘¡µÜ6x1Ìÿ¦EÒÒ¿[šSûx1™9Ñ‘ž$ÁU#éºßoš! ߘ†pªD`M·oIèÍ©9áñž{è–&›æM ç㱪]Ô”ºË÷¿5ß}•" z%ŽéÍ€ªÔ¬Pƒzጉz³ò=úíÂkTÛ¹²¡ýͷǹ#ZëË‘¸¸‹ëºÖÚ¶m_4C»åqÄ}¢ÿR5–>ÿÖrŒZÛ¯È}¦šo·ûÍoƒ€AÀ›0dțώi›Ï"P]㢂ßZG¥9ÂÑÞ:ˆD{÷h¹®´‹G mCKÖmo«—-µ#0™×šÔ£ÀѦ¢ï*½Ùn×v˜eƒ€A s2Ô9ÜÌ^Vp5AÒ4iÄ7˜Ž¸=X_‚‡áЮ6äsÛc2[¼Žç³ŸµlÛ{Ñ’u²©@B•ö•é7(4ßxÙL]ÔÌ ƒ@»ð2äÚÚ16êø†„‘¹øò¥ð¥ ¿js‘Šþ@Z´ãìí0‘¥a´š»|h´Ho~±Yþ·d½ÊdO_‰æÖ»ïÛÙßLÏPVU%IÈTn¤g`ê-Ô z£0»=5…”p¤þ(­¨rüÀÿÁ©ýœË[ Íá¶´¤8'Ò …uäe˜–9ÅX/…¦à˜‹µðÞ+.­TšØG(SµOÔLQ“æJ†x¼h—ë•f8-ü‰‹‰T?bcä’YSðâØJrfÄ `0t¯2“±ñÔT IW·ŒÃ§:Ú–5$E97¯ÚvÑgËdÅæýÊlÆÅ` f§_¾q¯Êؽÿ(ýŠÔK„}:]ó%HÓ[ÆG?2ÄWp©Ê?b`’35AKë=ÕâÈ—EB¤#×16!O¡Û|=ôyaBO ™<ÕÛ„™è=¥üzö:Miÿf›HM µ=WÎmøˆØŸj‹ÖÁß'Ä„$È]˜5„è |ñhŽÛ¶7K¦Ž¢ŠÍ€Ò®ÌcÊ/ˆþJî2fØe*+)Ê-,(ßY„>|Õ55ÐTùÒN7Äiêv2 ƒ€A x•fH¿¤“ã¢$_ªÉ5Î̽%£1üòÙ“áù#Ì8bìÛ)aõù•œº¼çè§ÿó©¥vÃÅÓaþŠ“/>SÞ„õ“//UMODFçÙsø$ú”%VøD$ÅG+$hi½§úÍc†Y$&E¼Ö¦*gMtÞ' 1QŠ$Ž¿ 7™”>P9B3‹=ã€D†x-sàÐýÁÊ­òÈ¢·•ѸáiòÝ+ÏÑ›ÍÜ `0t¯!CšQmŸžÑip`Î<)‡Žåɰ´Äw¬¹.>{2‚7VÊ(¼ 0¹ MZœªªkUq×ít€æT^Y£´DAAEÛ{ÿ¨!û,Vo6¸£Î8UÁ™Y*noi=·uUˆß^h¦Ò¢®pj%¾ÄYcÞÕc˜ý›G ̬Z£AÍâð´¤æ öÂZúüp¢PsÅZžüþ½Øâ|ÈÓ—ží§|t­SïHâÂÉŠ(Ü®&hnÿù­—êbjž#Ï=t‹sÝ͗ψ°³0 ¢ñcŠ&³ï\r¶ª“æj=ÿÖ+g;÷5 ƒ€A #4<;²W7•åPß@õ²”qC’$˜w¿ÚªLMÝtÈvWMUKÂgš¹–! ÒD¨ñúÆw½Í• éu]™ÓD÷Îò­h‘ÑÁj(FêiŒ‰·‘îC€N¾ZN„ê­âJ„:ÒF™nßîD¨}{I"äºëÔDÈu½Y6 EÀkÞ†ZKÁ5ãï„#nÈä!ñ*nÉâÏFfu´ƒ}¹üâÏ7*üDÔIˆeÈYH‰["Dó¾ŒQwö}PJƒ3¯ÖuçñzªnÆ Jé×ÔG¨§ŽoŽc0<@O‘!ŽõpšíGâਯ=¼´C1ìwPR¬¤'aD×lù/†£SÓa¤mˆñÚ~à˜$…Z%>"x†Âa;DáJRDœ‰wS±´yžšîcÖ4‡@jb¬SsQŒ0 œüA.œ1QùÆùC_L ƒè 2Ä—k#±Ú¬•UµµMÖk3_Öaxy‡ÁIsÔ€8'Ïcî¶Lù5ªÌüh„}„þöÆ2E„ú×HrD° “p`I<‰+ñ¥ÎÝL† w \ç:¼É9jt0ó£U¨yóWíP«7 ƒ€!мóJ7w¢¶ªº°¨¸œ/ÚFª ¾<ø’V/ïÈÈ©F ¶ôÔ (’ÈóóÜÛ+Ò?£Ì˘¡)* a77×ë«g!Ÿç¨1:Kƒâ¦WI\x`=Žá,‰'Iñ%ÎÄÛUŠJÊí6«5×u]_Zf°LNZ\—õºŽÎÓJBGO>ŒhÉãZ¾ÞÑz=]ÞõZà²ëoOËSõuÇùòTÛº»×óã+ç«»11õº‚@O’!¾eÔTYQžWZYma 6æÒ›š#Bð²Ãú¨ÈH©Æ‹¾¦¶V ówdP©dUK&b£ð¥ÏwyÊÅ ½v¨ëê s¦Ø`ê2àÈw8³ÅTK|pÌaA uÄ0::J"£"Õ2q%¾Že dˆç‚礶ºú8°k`~ ¤~™2a)G%1=ÅDa.()G|© Göô."ÁX<9ˆ>NY¶~·dd^/“úË€3j ã #áÆcÈúä&ó–—­>_Gvƒ1Š ® ñ›jÆÃyšœ ^†±'›ƒÅç],>n¾a,b2 îßO™½©íõ–óåÉ.›º Ý@O‘!>¢ôTwtÿÞ­iÃF\GÓ×™ÈRí*¼™éàKÿj2jA„l–«3ló%žŒèÔ¥U6)«©Á÷‚ªrçÃÐåÃÞµZ¿\¶À½'=Å<<Ð&á8›Ôø„„„* ‰P,F4ÅÄ`"!žÄ•øº›Èx.𲱜ÊÎZ°ô¹òKÜøRåõÄXÕ¸~–oÎHZZ^¥v`Lˆ‡×ŠGú¯±f*®c¹Þ;ªŒ­Ã©¯³ÙUü Wn“˜Èp•´õ<¤’!Qâ‹¶§…ç‹„çË ²|Ã.)Fê Úa«–ðšR ±Õôt“zýxÅ!r$8Z*‚Bå£ÕÛ%&"DΙ†0˜B1ò•ÏIMŒz½±¦@ 'È~±rNhÛŽ kLŸwañ†]™1 Cž®Ní^Úux’i‚ã J0£qTT•Äà«ÛŠ\HŽ/{<ÆuAÞSM$^Ä% À1t>‘é#‰—5B$Bq±±j™&2Ž&s× ±-8€Ï^°ü÷wà§ë9óTS½¢M‚H²©]àˆ» 08ì÷CpL¦Faâܾ.¼÷5š1´Þûj³|µiÊ?Á5¹ë Œx¾vbÅ«¯–ÂòjTš%Ó öÊàÒCjmÈeÖmñÆcTƒ eE“ £‘ü¶FVØóüéJ«ç0‡;H‘7¶Ý´É àMôbë0)"„9ÓÑ[³ì_Š€ èOÁH¸®¢IO¨=Dì‘|/3@`½Ö&ŽŒª„v¨ººš#«úŠu"GÙ¾@Šô:G„/j3H„ˆ Iò‚iŒ!e&sj…š:Nóà…cÉ;qüU@­Ïϙ߯ ^#V˜[kÊaÙ¦ ùdÍ.DæTÃÄu>-¿ép;BBÈ´2œª¡•),©Eo.“«æN•ùÓÇ«kN_ƒ]B¤Ï×çëvÉ;Ë6Ëð¢2'ës ªƒ]ÜH‹(^µï Y>x¾@‘¦´êç1NøQ©ï}CˆZ„Ïlèãô"ÄÔ2h"DÝvuEEiÙæÕË_8wþ½/°Ê~Û5ç6²Mè=obµŒ/U.“ E„‡+"¤œ«ñrã×~]½_‘&Cþl1ÓVSl0²4¿‰‡Ï«¡ô $A­}ûãðcÙ»uÓS9¥87$«]—¡ˆPb|Cfyô×H+0¯¼B‘w‘ùÂ&"%}Ô<-<_LûîršøOÂoë#a$^Ÿ½X>ýZ¤¬'¬úËçgwœ/¿Ót¬O!ГdˆÚ¾h™¹’Ñç*·¬újK|Rò»X¾FÞûM—ϲ¸jˆø²×CÀ•)^­âËN•­Mi†00Zi†4!B½~+ÄFMp%¥fˆ8Q;D'W>ôH€˜Ó©%¿j„H„6î9d9vèÀ¿V~ݺãÜðñ\ùÒ¦1j„ŠKÊäý•ÛK€P#d¤ã·œübyué:$OMév˜i;^Só{ð|UUÕÊkKÖ McÔé8Ä­(,A˜`zHj‚Xb’3»k‡;^»Ùà àô¢É…/Xê¹I„Ø/ Sè—ï,^ÃàWœÈ/´ë³šó!rhA¡š/|˜Ë ƒ“ÖÚÉšRJ_!Cì+lÔ‘ © ¿‰‘ãwó#Jè#DÓ5BGî{iÉk¯Ð<ÆsÂsÃs¤ÍdXô]áu@ó)}„èc¶jË~)ƒC0SIÓXçÎ+q‹‰Sù%òåÆÝrÑÙ“¤\«+;W­ÚKŸ/Ö[giúÓXç%n3.“¾¡®ûygŽSÏ ßˆ#Õ¹ž›½ C §ÉÖ •¡¹ 0‚)èËw/É?uòäé³ç^ÿ¯,‰Åˆûã‡[&¦§9ãi-ofš„¨ý`l >DõD¸ìïB<(J;T¯%"6Ú¡Z¯×80އÏsÔ¥ë¬Ö’íë×,Ú°ì³(ÃóÁ‰Z;­òyZ!‡ŸP9âmÜ—­Fgiœå.ñãè»›÷Á™/Y0$’ﮊ>_+áÜÎQcÆYºkˆ?â¸io œ5a¨znòCÉSç«k­3{¼ ž"Cì5Õ6ôE¡ÏµLÏ'(µB²míÊÍû¶nÞ?ãÂKæÚlÖÙxaÇà…nµÇÅFZ‚ƒovwâãþÛ¥¨ß-jB¤;æþ›ë™ö„ѾPØXà[U’u`ïgk>ùèƒòò’<)ÆÄ¨€<'<7~á/Äë@i…0„¾ Dðб\©DL!Ÿ7Òu"ÃC$ÕÄ5£AµÆ¡³5ëóÅúJ ½; Ãçttàx4z°:Oc@`fs3º¬ëÈšü ž$CÚTFÍ ‰&8Š(UV–×BKôÖ:jâÔ¡CF•ˆ€F:†È¤ÕZ[]SYU\YQ–—µïÎŒ­›ö¡:K“1 —i"ó­ãåjSA;i";ˆ—,•iŒ#d¤ëGâɈÝÃÒ’”Ö¡92ÞÞ#éóÅúP‘q„ŒtâHЩõ¿ö©>‘ÝkdˆŽžåÕñŽ<‹û¢«ç«S`˜ ^Œ@o!ý’æË×u™/d¾œÕ(3Ì•s5æîdÈ"€ÒŠè·’ÆV“!šÀ4á¤vˆÄˆ¿¹^û é}±Êw…&mvá¨CŽ&«­…梵Bw/˜§žÅR¼à÷ÍAÜ—JcղЮ KK”(ßšÐÄ1{Úh)*-ï=2„6Ô Y°c4§óΘ^\Ïëëí\cvä:«<²_MQÑ2ð–ŸH`D”Ø*Ê$áœK¤ÿ7o“àø$)?°S²þþ¸TŸ< Ýu€LøÛ{’ùôý’öJhÿÁ’ÿù;’0óB±UVH¿¹—ÉÑ=%Å›V¶vZ»eñ¬ÂõÏû@‡"јwæ|uK#M¥^F 7Ȼ̗®¾Mm„«‡!W"D2D¤',iâ«'bëJˆHBI€8çÄm~£B_”h³ _Ô Ê v¤7õØ|o’ˆãpíÀ„WU)‘£&ÉÐüNò—½/%[×J ÌiéþEvÝ÷M4Ù.!IdÄÏÿ,eÛ$ï•?KÕñÃ’tñuR¼yöù@ªŽê½®WÞ¼8uõ|õ^GÌ‘ ݃@o‘!Ý>AùôãœÚ ¾¤µiŒ$H!mß0Z!€ÒÑo¯&Dšéßz{;ªô"ú«—ïU•¦ HOö¢‰NO –§‘ƒSdbú E†Rãä|¤³`ç‚’rDÅ>*~)“Sj"ÂI„È=Ð,ÌΕ%k¶Kb\´\ˆõý‘£¸¬B–cè¹&K±Qrëç¨ìò{—å3¤I{L2qÖ˜wFÓ ÷e=½AZݱ ŠN¡?|T"GL€†gœXü<žP6I˜}©XKŠäȳ¿Q»ØÊJ$ý‘g$jôd)Û»U­+ß·½‘IÍn­•šœR¶iLzQ-£ôó~èêùêÅn˜Cº Þ&CìïS¾œ5!¢¶‚䇓Öi¤çØd¤ˆ)E½^êçÄWOz=Ëø¥8_°€€_ÁüÝ[þ÷4}%ÅÇȡ㹠ërŒnËqùôë*Šó|ÄÛ™yLùß=U ý“âäÃ[¥dŠN®×]0]œ%kv`4\°äaH»–éã‡ËêíûUº‘ gL”ã9…²óà1½¹ÛçêB¶|;¸†óÕù:<¶'ÌvhQr>ümZ!ô!¢„ôKnäûSq8ñ>)Ud¯Z”’-k ^öŸøªû÷ƒÆÚËšhšcèU¼ iô š/lŠ&BŽ_½õ&ÓG÷½9ñÔ¢±Õ¿ûÄ\?ô9ï 9(Y’×hPJ‚Áy{ÝŽƒª¥åU~y‘ÔƒàKtJùþ ÐO2’Š*IBª”¬“ùª,‡CSûóÒ_dkð#Ò˜½Y'dýÎLUvÒÈÁ2|`J’!ØSøzªFþYKòåÈ3¿jRC|ƒfœï\š:X-[‹ œëêjè†çÒÛ÷ƒw¢bZep àMdÈýœôɸ;æ·ï"€¤RâóÁÊm*èGñPÒ¥ÈÌ)#%!&J ‹9°¶a SoNb"9¸Rä3¤6#'ó*Ê!Ô$… ~Œ‘îA tëI¹ìI¾ôz8B¯’”+nTf³²};Z<`%ü„"FN€ðH”vÎÔF ïC@ûâx_ËL‹ DÀ[4 û õYºv‡0œ&BìÒùgW¹½þô¿¥²øËõ­ö²!($NÍIú5×—u޳û¹ºGK¶}-Ùÿ~ZR¯ºUÆÿõ]‰>Fýé 8BÛ\#ò–¾)ÃFË”—WHüYó›+Òkë|÷^ȸO!àÍš¡>u"Lg=‹ô “gëîJmAÈá&e0‡C‹3cBz£ê ŠJå´ÑCà&µð[9 Z˜ÍΞœ._lØ#±ÐU x^Ncez‡hœ»ÒOÔÑ•ãë}ÿç/©%ÉùèUá-¶r—s¿´ÍמÖd·¢õˤhÃr”[Yƒ¯IÁXAk±Æ™s#ƒ@F3Ô€…Y2t;Ô­Ýq@Κ8B~tÃ…*%G›iÙ—uJrDñÞëΗ+Φò©½¿r³¤%ÅË]ߘ«œ©ã¢hr1Ò›4"Bm5Ä£·‰P[M4Û }£êëW€é· ðì_´X節û@ˆª¯t÷x/å•ÕòïV©¬ðÕˆLÙ{ø¤š˜¬Z"Ɖ¡<þÂj®ÿ-þ¼u“›.g惀AÀ ÐC†ãa~zMhZ:XU½¯ëöJ8H1 Ï#`ÌdžÇÔÔh0 ƒ€!`È,ÓTƒ€AÀ `0<€1“ySS£AÀ àç„¥ •Øiç`xVä~ñn«Ãë=…%ˆé1Bé=Œ žGÀh†<©©ÑÏ8cÜ09cü°÷êÎkæªÑbÞÑ;$ÄFÉ9SG5©i>r¢‚ô~$¾ó™ðÌ{íîQôÄéõAÛ½K£‚  8öé×$jÜ4 NN“ºêÊFÛ=ù#|è(õÛÊÔÿ®–É/|!Cï{LB8=yS—A ¯#`ÈP_¿LÿÛD€£¸"ÂBÛ,ç^ Rqè´îÛºû7G¥ep¤ôp=VtD¸„" H_jXFÜÿ´„$$w‚˜ÉgŠ%(XýùAÉ~á)düÓ„:]e‹;†§ —Ê£e÷¯•ÿ@âN? c/n±¼Ù`0t¾ûTì^f/?GàNÄòÙD§c‡@†ù²r‹#gÿÄX¹ãš9j8üÒµ;%88PN;L^ÿt"~UN½ÿ²êå ;”ªC{%8%M’æ_-'^[$IHÉA÷Ç7$Ù‡ròeÈ=¿”˜ ÓeJîzì•?;Mgýæ\&©WW‚ã¥âP†|ú~±•‹ZÿÛ$(&^Ý:ëï7‰GT°z‰pÒR~`—D*yŸ½­W™¹AÀ àŒfÈ š*ü~0/Ï+’—>\-Óê#A³w¡¡òÂ{+eËÞ,™qèXžôï+qH¢:´¢ŠÿãN„¸*²®©£«¨Ò$QÓÊëH¼êJ„X–ÛRQçâ/6ÈÁ£9rƸá\-ý㤺ºVž{s™ @ðÅôÉòâ+e×Ál9wÚX•îcâȪì8¸Ìãy`Q±Š¸rö´1²vÛùhÕ6Õ® i8oú8Y ÷ņÝ*Y,ׇ…ËÀäxyîíå¾G„ØHHÒ|ÇÃR°òc9þÚ³’{úl«®ZªÊûï_äð_ÉX“ÎÇö©³œõ—rüç$íÆJhŠÏ`•þ×Ý%±“Ï–oþCJöl’¢u_Â}Ç®ö/Xñüxj¤ò`†dþñgr ^f^ˆÔóÔq"GMQú”Ú#‡ÿö+•ÓŒD(8!I­ÏÿüÉ|ê'>8]‘-µS ÿ£b$bèh)ß»­…fµAÀ ÐYŒf¨³È™ý¼š§¦Ž55)0ððTa±"Ü›ZF¦¦d,4* –¸ýÀQ?"Mb£ÃÕò䑃d´6ùÅeÈ2ÿµ:¨®‹Q¥Id(ûD±°Ô¡Aºíª9$KÖlWÛN€ˆ±î¬Sù2!=M­ã¿õ»2Õúa%;):ªãÐñ\¹ìœ)òÆg…ŠÜP+D2ôƧë%:"̹oZRœ,>‘Âf•㹎”©‰1" Ο®Ê6|mÛTš‹sä¬ÐmAã춺C?=Q‡ë™#çÃÿ©Uýæ^)1SfHÁŠ¥â°CÓWqpT!‰*%fÚ,¥Í©:-&®'™9ùÞKj{`x„ìüþ•Î$«Õ'²*Û³Ymç¿Sþ‡ SBáCTyd¿ÄL=Z£÷aÒºT¥í8üç‡Õ>z‡X³&×´V”â-«$ŽÇ|÷ßêwsÿ†ÜõK©-È‘¼/ÚïåZn —{?Œ N rBaü¾XÝï)©³¶/Sj¿Ɇ©().F˜>9!Fjë£>»Ö»Z¢+fO“èÈPY†¼a•ÐÞH¸JJB´2;%²®ü’&uýóÝåÎâiÐȤ@3D¡†ˆåµXm¿”"˜ÆF rø»PcD²EÙ¾ï¨Ì˜”.•5ŠŒ¹’¡S%’­Lf‰q‘'9ù¥RcµÊâÏ7(â§Ã¹D«=RíHò«iiw½­µ¹ë~Ì×VÈ‘S]“ŠÌÝÎ ¬% ‹-8[ƒ$zÜi5z24:8÷ Åh1-¥[DH¯sÓü5èö_ Ž) +¹0Í%‹µ>YHbŠƒ€¹å‹žt–2›ùý+ U¹•iØ J[9r‚ì}è–(«žÔº‹+îîÛÌoƒ@_CÀ¡¾vÆûhÃð’-©lß°ä‰éƒä’™“eï‘RTVÑ"bðª­C2U˜ÂH„Ü¥~<êëÚǺêµAîå\ó•uÃ%g+­ÍÇk¶ºnRË»—ôÁ)r÷‚yHöZ-_mÜ£Öï€ÉìÞÓæ ý™ÜeíörÉ¬É WÅRÇj µ\+6gÈ Í+4Q%eòኦÇs¯ËõwZœË=)‘¨/'8ªËUÖUUµ«šÎìµµ’óéâ´Ú‘·5I†Qİ1²óÞ+”ŸGi±Â$Ó–»Ø*ˤ6ÿ¤ì¼çJ÷MM~§\q“$B»µ÷×w(mR“m¬¨žqA š¿6Š›Í>‰€!C}ò´÷NóËWOax؆ڌ€f4G®ˆÐ·†¢3{¯Øì0­pMSœ´Aå´mÌ&ÍÈŸþ·T­eš««™] E*¯6e4Ú䚃Œõ¼·|³2‹¹æ5+«¨’'ÿý‘s¿Ã0‹þp•úqøˆÝIgt!ú?q Áè2šÐ(¯|´FonuN‰g ²=¥a`=¬¯2(LªƒB%ÔÚ: iµ-l¬¢‰ ä/Ãâk‹ ÄVQª²Ê÷›u±”n]+e{¶HXÚ0©«©’ꓵ|-T©œ´k óP¯Mb&Ÿ%Qc¦HÑÆ¯TñÒí딣tÊå7JáÚÏ1*Lc_Jñ†¯$ñ¼«$ù²oKü†Â"$,u”A å*,“†ÐÙ/>-(C$Jù>‡YÕµlsËÄ‘x¦âú×÷‚§ÎWsÇ3ë ¾Š€!C¾zæL»[EÀùà‹"ý¢B%3¿R˜ß+2¼aDUs•p_W¢Ñ\š´ÎGÌž<øÌÎi®ˆs&BÎXh«}î‡h­ š¹ïÓÚoâH«ÎÔçK¶µòmmÓç‹õ­Ý%YÑÃddacRØVíÙNç圥‹eàw*ƒnû™lûî<5òlÈ]ÈÈGžU~?ÌHŸù‡Ÿ¶› 墾¡÷þZ¦¼¼RÊîVþC¡ 6”‚UŸ€ÀL”ß¾GÒnºOh²£?SñÆrüÕgdÀ‚…2ðæŸ(‚vòƒW𡤋ÐÑí½¿Q÷6_{Z£ß-ý Žvܼþé4Ïœ¯–ŽgÖ|C†|õÌ™v·ˆ€~±â"t  ”¸È` ´H9|jZ#CÏ¿½¬M"ÄÓŸç øÛTÔ›ZlL6lÜípèíÀ.½V”8†Ãñ»¿h¾«»ô‚u=_¬/"8@$Œn7:þŸ¿4˜¸ õq' ÿ£F816Ðñÿþëì˜è0§|êÇ*vP`D”XK‹œÎΙï.ÔäpÒBmÍ] ÆXWY®W;æ`ŒGÿõ{9 ÍNPt¬"CŠEbëÉ·_PSPlœ¬ËÔ¨´Æ;‹ìyàÛî«:ô›8†ØÕõÏû€÷CWÏW‡` |cHö‘ešÙ1ø¨þ¡   I T£¤8R¬%io\f÷$b{ècÄÉÛ…øq´Ù¸Á‰NŒ‰wWÄõ|’,G£ˉÈ]©²Õ}5Z!]©.¨¹ÑdE¯oï¼ rÝ&4+ÌrÍÕÍõžïi!~Ä1%*X]ÿ¼ô=ÑÕóåé¶šú ½@מ`½Ýzs|ƒ@3hMü$A!B‚ø9ña¥ƒ—Ãw¨™ͪ6 nÅÐ ʤáý¾ÄYcÞfÍÐûêóÅz©Z=h®Xÿ?{çE™þñgfwÓ+!´Ð;Jłػ§xžzç©$öûÛà­GQÏ‚•Àw6{+M" ½÷NHHßÝ™ù?Ï„‰IL ew³å÷~>“™òÎû~ßÝ™_ž÷yŸWuTsvŒ€p[Ðæ|ŠPtó{/ßùÈï¡¡íu²{ã8#ˆ¡`l5”ù¤TJlç—´¼"##8–OEó”mT3FONnþIóÀ ¿'Ãq–Ü<$@çæÃÎÎÂW8 Ší%ùžÞ¥jBsÛkH¶a{­p;•Bmíæ÷^¾ÿò;ðV{…-XTZÀÜJé”´DŽºÝÄä*|ÅÒÐÐn—ªí%ù÷àûlMêLßµ¿¢Z¶“X„„—p“ï¹|ßå{/ßùx«½J\nCÓ5q*ÿÝÖ²ˆ8 ’¨²YªP¿=®’ÒÜܼBÙ_osƒt½ÈKÚáp˜/ƒØØ*åy)ÇŒi;!†^LØ?Çs$—’bÙr„¾uâ#$]‹õºkóXêݾ OyÉ _«Û¥¦ëk³¿ºö’ûxØ µ‘:SnT³kµ,Ü[›ìÂòñ’®1±5Ò¨uR4·S4ÏwÇCò½Ü^¹Ç y7;AUJ¿û-W:Š À †¸qµh%E…‡ò‹K±BÔg¶xáVörU9†[†¸{,.6–…‹ãé°“,¿ÔÓxÔQıÚWlÐÅ]±ÑÒ•qÒ8DáÐ.GH†Ï˨1q–Žà‘x§¦ÅQ§´¦kò®Â·l„R½u«‰³¦öêÙ.•¢ì mØgÐç]þ@mòwRçœ Ô6›Oâ[ÛJ!>/£ÆÄYÚAµ‰ñPjB”ÙFññq^o/ù]ÊïÓU\t Øx¡¼ Pˆ¡šÈ`¿¿ È•æ²sãúUi:Ýøëæ=t&Ïž^ß$]/â0*þòŸ±›…Œ³bôÈñøˆ:X¤ÑQ—AGø¥/ÃŽUEF¢q|"^‡[Ò9¢¦ñs^K! \Ü:ÁFS(™§ôHHàE^°ÌS¸ ߆v‘YŒkj¯,\“¢´õÐ1Ú¯´6_úÆ’¢Ý%ãɧž}>Ü’Ëæ "{<;¢˜ûDZjá¡f1*ûr‰5(–}Ó^ò»ä¸UÊ®-›—0wëwnM€ú†ˆ¡kÐ ­Žõ@•µ¾zé¢-g»$oÉš­ ,†êmr(·6ðKÛŒ˜ÌBH^ð’Ê^¼ÜfëjÁ˜ Jyq+ä’aÐ<?ïËN£¿2Ú.†§‹dkLr´’ã¢Ì®±0ˆJJL$Ù–.2ä «…÷díÁ#ØÚÓÑ‚:Zì¡RGW6"©°lÚ6+¿¬Kì.¿5Ã}ù?l(ŠAÜD”¢êç0(.Rº„íláô}{ñïÒ0t=wÙ³%4{Åß®jŽ[€€o@ ù†+r­=ŠS™!Ô\vnÞð-‰»~ÓÎýÔ¥m‹ÚçVåLKôDdÄ–)!±þˆŸ‹tñD²Siq1O½À¾D1,ˆRX‰åH?>öþD‘›«Ü*h?Š‘$#º„—M-{± =¦Ï w‰EÈìv)· 5Üqº*´Ú´Wtt ¥p{¹¹½Ø‰·QÚk×Çï–½Í#Ë·ý±Ñ˜í%¿ÇÕ›w+{¶oýœëjý^+þ†ý÷¯€ò:RdXò¿½õ`•I²>äžý^JX •¹ŠÃ®ÆŠ !d±´¸ˆ¥Gº¾Ê¬ e£ð¤KLº]¤ûEé“ãÂÑ)Ú+×ñ›Õ09)ÑN˜gc´—Ë­ÿuCÓò~øü“/¸€ò[µ~·`Ÿ;!":€ª.œìÖ•òP•‡«„âu/ûaîÛê ïzãóÆ× þííSÇBX/y›Û,ˆd[â®ÄDG›BÈt®vñè2v®ÖûYbÈêZ«ãmƒâôãF¡r‘(S6H¤bé>2ۚ͢QDPYà¾2‹°ôE †öÊøM¦6Mñ†jó´[ŒüÙ^ò;Ü{(—V,üaZA/,û­Z‚ÈúW[nì@'1è-å“ÿ*-!$S•›ËŠŸæÿšœÚLþ½Š>"ãÏWž«Ô×B$/k¸X¬a÷ò—î/³ i¦eÈ`WC– åf6æÂޏb6b’˜4"E9LkQÙÈ1_Y„*2ôöʉüM 5o‘Z±è>ßöw{‰EH„ÐÒuÛ”mëW°tÞ÷˸’ÖïTþy‘ß.,C>oyÜÀ— †|Iy×–€ÕM&V™9³øø=çÓ¾gI"&ˆË÷9jüñâjC}ˆäebF<–>û ™"ˆ»Åd¤™,b ²Fœ…‹b¾f·—XD ™ ‹FEeŸyÂ[þ,ìü™¬{Z{%Чùñ”Ò$ÙÚôËÚjƒ26¾m/ñ’®1±mY»êãÙ¿?“+iý>å·*¿Y±êB 1¤à%1¼mJ%»ÎC“F¥ëU$0![Y ªê$C3ú ÙG§ëÿ¨xÜÚ~êõÈ.›s×®›ô#ï(ÚkíŸ8E«“‘a·§ôyÇa™+ÌLœï7Šb5j„{™µÏZOœÙY×]ï+†2st†>ÆÚ5€@X†ðM°ÙïdÜ`,yާIS£[ó0Ⱥmí«iíq¹oãs¿Ó<%·TsN‘®åŒ«fÿïvMȶ64×G ) ~w;@LCø"€€€Œ¼£t‡0<2)ÛqŽ•½G/½­3Ÿ¾F¢©×˜ é’b\¡DÞÍ뛫žÈ'³Èºrâ4GߪǪ~vDE­sDÇS ÚPõ>ƒ”€Â7@|D@Q”÷tEne¯q£Bê»ÖçšÖ¦e]ÀqEW<:¢d ªý³*«pthE}P×=¯™Â©ÒÁÊýsÁAYt6 U>‚O ˆ!‹Ö  àmöˆ÷Ø‚s½–‰¯Gµçì; ìzîì“ÝFÑõÛÈP¦ËyŠ¢N7 í/¯1 ñ5Ò>cu³ÿÉ©ÿ¼«â1lƒÔÄPÝ™á ¨Q·—lUÚýä´¬AäqÝÀÒæÃ!CæÊ{5¦Éo5Ià)®%Ez|¶²Ö0ô±†bÜTÝȲ5òöKóÔë±-ؿȚª¦Æ¼q@ zCÕsÁ^ð öŸ~O×õËØBt™j;yYAQÞ,šf8”È‹Øgèr‡9Œ ²dKîú+«è‘;Kvpï×d»øY¶•V=ŽÏ µ#1T;N8 @êE Ê9ƒ ¯`ËM»‘wdžtD—AÜEfW§ˆ¿µpWÙ4s5%hbô}– ã4î:;©3u5—c€@œ!| @@À‡¾½xÇÊa %Šâ¬Ô•ÅÖ¢‡&LQÒËooSÿnhzêè;< Ë÷ñF\Z‡Ï vmyyüÔØæ¤Wæ‰ý¥¥î—v,]ýòܹŸqyd^Kù»x¸ˆ¡ k2Š€zûÈÇ/&›2MUÔVmòwr6PÛümJ¤§´1º§¾)Kí‘´3¾miÒ­ùÎø¶Y]Ïî{oË^î™þÒ¿¾á‚ÊDe–( ”r£ üÿU@b@¡@Œ€Ø^lwŽ~âA›ÝñEriNó+7}H—lùTért=± °â6Nq„ƒð.Â'©äh³¸Ä¤o}è±¹DQ¼8xÑXÁ–ÅŸ@*€ª„@€€¼¸Õ;F9Tõ™N¹›•k6~`kYø›ŸN”1àŠ |®Ýô¾MxEEÇfÝü·G+ "yÖC\«¡@Bb(PZå°¨ydô%ªj›Ä/vã‚í³».=>H'# œ„—p‹‰KøÇ5͸Œ¯±,DxÞŸ އ-ü8¶éQqH꥗Þ9µIÉacðÎï`ͨG3 ·äâ#”Ú¢åsmÛvmÂYDò">¢xæ×ƒ'. }øa„~£† ,DøØšõìp¿¡¨-ÏÙ5Ï‹PýšN¸»{®Jª­ùÀË.Ëà\¢y‰¾¼@`2$¨Hb¨" lƒ4&yÙ#"÷·åQcðjXS?ᘔx;çË ¬C CŠ«C˜ÄP7.ªADÀ´ Ý0âþ3HQ›óðyX/¼ÐxÂQ±Ù›žwÉU8;ñ‚uÈ \‘Eè€ ½6E@  ˜b(&>þBÞ08ŽP0Ö!àÊ,…gjZëó¹pÒU†¡ö×J(P € „V@@äYd#Um'‘¥GÈ;_áÈ< GDdçXÑ2„g¿w#—!€Dˆ4$ªAN@žEvžs¬eŒ»Ï%/6&óTl{3ÎR|†¤› £Ê¼ÈY…dxê;a-ºëNW„X†Â©µQWL ²µºåêõò§µ®Y|ï3H–Ù)ê—äÚϼKq§ô#G³4ÒK‹ë—Q-®Šnß•ºþsõ}ûG:í?ßSûÿOj¤ÄNôJjw¯”™€@€€ †@1@ Ì ïÂñ­C,,}†"šHØú¥„ÓÎ$Åî m/Œ¦ÝÿyŠH×ë—Q-®ŠNëHÅ»¶ÐÚ‡n Í ¤ÓS“A—ÖâÊjO©ØM!T-"ì Wè& ×–G½A  ¨*õzéSÚý¿g©ÅuwPDJs:ºè{ÚûÎKdhu3Ù´ uyüUþì¦Õ÷\IJDµùëÔtÆP2Ü.:8s:øì “N›ôÑT²m9š§Qê°kiß»¯Rê7›ÇNyv™óíÿøujwïã”Ðë î5Ó)wÙ|Úóæ å]g)ç_A-®½ÉM©hÛzÚòÌ£¤å瑹ÿw’=!™Ž­\D;³'VW©Ur~üŠd±Ráæ5×£/þö#kÖ ^ 1äˆÈ@ pD¤¶¢¶é™,RþMzI1µ¹ó1*Ü¸ŠŽþô-]ð5‹‰~´çíÉä:´ß,tê…×RbßsiÇË“=©)µ»ûqÊ]<›Jì& •äï"÷Ñ#´ïƒ©tlÝ2r¤¶¤æWÞJÛ_ü¹s²‹Š·¬§Ãß|HŠ#‚:>ümZÍBé3Šíz* ¥'LA“ûöwÙ¢)„MRÍý{ßšL"pÚŒeŠ­ýŸü·F¶¸Šißö.ü¶Æsp@ ~ †êÇ W0ÜŸçÐÁ/Þ1K˜2äjJèsåÌŸIEÛ7˜ûж¬£’=e“Á&ô;×´æ”ìÙAÄ‹ìOxíÿôæ¹¶èZ}ÿÕlé)2?—îÛɾ̬[n~–?¾x‹ÃªÉ>DÅ;6QBß³M1ÔdÐ夿Óö2Ík¬ ùž®ÃûM«•ìËûe%É=O †ÚÝõ¸)¾_{ÿ(ë~Xƒœ˜ÄЉùà(€@(Úº¶¼ÔžcGkt:VlvŠ?¥?Åu;šœsQù5‘D1ºÓêû®2ý„dô—•<ì”À][U“V\@î#ûiõ½WW=ô»Ïͯú35eëÖgºiMúÝ Ø Ð`MÖ`„È@ X”H;9Çó°x[\¢Ùµ•»d.¥œ{)Å÷@b)ŠnÛÅôí©mÄIÛ}ô0ç«QÂi)®{ŸòKóW-&[ŠÄÇ(¢iK v™é°·dE4kMÍ®ø©QÑì«”Ré:+ƒ¦C¯¡48)@IDAT°ïýl²EŘ>H⇄ à]° y—'r`2Šëà×ïSëÛaÇê‘´òö Ì‘gíîK]ƾbŠ#ññÙúô#TºW­jrˆókŸ“ú¼ñnYkú‰£´¤œ³XÀô¦Vº—Òþü$]vâÏ”·t>íþ2µ~7µþËæ@Ûÿù›TÀ]rSê%Ãy …ËûhÅÝ´ü†þ•>ã€@Ã48ÖDz渹R„쬱çË @êH@&•H‚ }4óÓ¶®Ã§_±Ù·CÇË0ñ·î4‰d‹‰#O~n%gçÚÖE‚1Ö‰Zµ‘=>ÑCâ|]1Ù›°“u9*­â~omÑù:ÚB ëÞzá©¿qž2„î/Çx‘h‘/“ð> ˜¦»‚À2vMŽ ƒT5Z¦ºËM}SBH2ä.4O^NµY×´¿Ú“±@À'à3ä¬È@@@ X@ KK¡œ ڤrRh×Ùßµ;ÎX8›‹¿ïû@À€ ئAÁ@ l” ÍЊJUGùç°!àʺÔÃãñT7›,8û;².CÁÕ^(-„4Ý­,rÄá%íÅV.tÄîÒÒú;Cy±,È •ÄP ¶ ÊáG@^Ú{K1J©=2üjïƒ GáY\TÈÊ»Ç 6}ÀY7ˆ¡àn?”B…€¼ »w.à eg|‡P©W£ÖC8 Ï]›6H#]WXµl¸9ˆ¡@j ”“@ù zîýªkî#[št“}H $ 5wiÞ¯Knæ¬$¦,弘=.!12M‰Š€@P«…ù²>rààôñm•}±­‚ºB]xá'wmÝö5—Ås|±‘ðF8Nb_Æ&`Y*äEí™ÿéGÓuëÈ‚ÖçëÕÑØe Êû 7á§¹JsœùÙ7\ ×ñEDQEëPPÖ…o€ò6Qä Pb©µ+'ç`þÆU+Ÿ:BóÚCwY=h 7á÷ËÂÞ(*Ê/à,J/"Š„3,C  , Cü¤Êç%ÎÊk¨«›L^Ö%?ÌúlÁžm›ÿ³%©³ò}ûK XˆjGT8 /á¶eíªO~Y0ï¾Rb É"± _± ¦2”x2™7 üJ ÁbH1”½¼´òk©q3P#  yI»y‘wáÌwþ7}×–ÿå»ñI×ëuø1•$áóq—táµeͪOfüþW|z!/b’µp¾V7o\jÉ%Úp¥BBž€ÌÝ ÔÐÐS9‚þ•g\zÑ‹K¿ÿþ·) ”+.còOš,öÍ«WmµÙí»ãÒ:žº©Yï¨C±-yÎwE‰óä“'? ÷$q„¶%v¡%içKZž¥ê±¥ófÿoÑw³0›|^ò*,"ˆäÝdwOœ˜l¸õr$€—ý0û.'ø@ƒg­·©¶¯=ºg¼^¤_Î¥~Ëo%Ç@B€å7$þ-òâïiÛ’9ß.Xûóâõg_zù•ZÇNîl×6QáááQî"#Æ] Dênþ^I¦,‘HÝP‘MjŠÇUš¿cͪù ¿ž9§¸¸PDXƒ,!$ÝNÂS¸¤âr‘¼CXéªv~§Èg$ð'¯ô_Ã0nTéÊF:°^ôò¯¸-/uy¹‹’™\#x©N UR*|Ž×Sr„eˆ2³>äõÔœ¡õ\µ¸XbÈ´šñe"EÉZxÉ~ËOȺ–wFâw…’‘9þ¿\šž¼\&ï^#€ß xõ¡‘>6ëÿ ÝxŽý‡Þ'Gêm°ù½=qC5¢:Äò#V YDYkKÉqy–Y oú6MÔd•u‡›æçœjmûi-¢ÆZD UD" EÉZ9&–µ€KÜ‹cþóLÆ Šª<˜=.óù€+$ 6¼*†„Z™ ¢gØ0´šã=¢°ù.¡¢ à+òœ²D‘ŸŠ"ÈB–©ÆëÏ´ê*5ó‚¦{¬ý—}8ÍÚöÓÚ²ðˆÈ‘mKY¢Èúl÷S±jÓGˆèE¾¢§¢Ò#Bµg‡3}CÀ'î2»ˆ‹;•‡I¶åxy[Q¿|uÔ¨Æè[÷ 9ä  àoò¼²„‘ˆY¬}Ö³ÌZû´ló.NÇd3 þúŒróg²Äe’µk±öû³L'½—Ä*>oÜÌÝc—ˆ_4";kì7'½'€€ øìÁát¾µÇ½ç>Èø7þߥ5ñu³~ÀPŒ½|SqîCànùòëyD«Ïžgå79¾1bÅ˃¬}SûÜ;ßÚöך]Ê+Î]Lþºm½îÃ3g)PŒæGˆåënnªdà |„ê…ù€€W¨«+×ñ/ù3|ì™»ÇNì¯éÚżݎ—V¼øû?)¾%€@¨¨(* _׳¢þPUµ\˜øú¾Aš¿L¯±ŒÐ ¨øê¸QË‚´(vì)B<ª ¼–\Þ¬\ øò ž£ÁÛ”(9˜¤Ï @@@–ÄPØ6=*    †ð=kCaÝü¨<€€€Ä¾   aMb(¬›•€Âw@@@ ¬ @ …uó£ò   Cø€€€„5ˆ¡°n~T@@@bß°&1ÖÍʃ€€@ á;   Ö †ÂºùQyˆ!|@@@šÄPX7?*   1„€@X€ ëæGåA@@ †ðkCaÝü¨<€€€Ä¾   aMb(¬›•€Âw@@@ ¬ @ …uó£ò   Cø€€@€˜1£gD€ Å"`©Ú 2   &d«+DI£Òõ‡*iB¶²@UÕI†fô1²N×ÿQñ¸µýÔë‘]6ç®]7éß1mGÞQ´×Ú?qŠ:V'#ÃnOé1òŽÃùÖ~Î÷E±5½ÌÚg­'N‹ì¬ë®÷C™9:CcíÇ@ Œ,Cø&€€€ØlŽ÷ 2n0 –<ÇÓ¤©Ñ­ ¢ºmí«iíq¹oãs¿Ó<%·TsN‘®åŒ«fÿïvMȶ64×G ) ~w;@LCø"€€€Œ¼£ttdR¶ã+{^z[g>>|ËÚWÝÚ0œ*)ÆõJäݼ¾¹ê9|p2‹¬+'Nsô­z¬êgGTÔ:Gtì0Å  Uá3€@ˆ!|@@ÀGEyOWôáVö 7*¤¾k}®i=aZÖDÊŠGG”lcAµb¶c@¥sÒIQÔuÏk¦pªt°ò‡Gÿ\pPMC•à€€EbÈ"5€x›€=â=¶à\/‚eâëQí9û»ž;ûd·Qtý62”érž¢¨Ó CûKÅk C|´ÏXÝìrê?ïªx Û u'1Twf¸@jE`Ôí%[…v?9-ky\7°´ùpȹž]<ù­& DƵ¤èOÏVÖ†>ÖPŒ›ªY¡F>À~Icžz=¶ûé'ÊÇ@j&1T3`ÿé÷t]¿Œ-D—©¶“w‘åÝÈ¢i†C‰¼ˆ}†.w¨‘øK¶ä®¿²ja¹³d÷~Mö¸‹Ÿe+QiÕãø  P;Cµã„³@@ ^¢‘3Ø ú ¶Ü´yGæIGtÄ]dvuŠø Y w•M3÷WS‚&FßgÉ0N㮳“:SWs9v0ÄÂ×@|HàáÛ‹wq  öZ¢(ÎJ]Yl-zhÂ%½üö6õ§Ž¾Ã³°|oÄ¥uø¼`×–—ÇOmNzqÅC”‘±Ìýä4ûÝš¦Í«t@jM£ j '‚€@%—7cCOYðåA˜+Ç@ÄPൠJ PGécŸì­çÏ,€þ¢éz* £M³d£SÛæjç6ͨ]ËJIŒ3N³®õél¢Ã¹Çh÷\Ús0‡vî?b¬ß±_w¹Ü,Òb"㻽Éÿ^qÞ[PëŒq"€€Ï @ ù1n à+éc'œ©*Ƴš¦Ÿm³©z¯N­Õ=;PïÎm(2¢ñ]"Ū´mï!Z·m/-]·]Û(— TJ¡¡Ó¿Ù õ¯lçc;}Åù‚ÔžÄPíYáL!pŸsR+—æù—¡7ÆÇF—sª:°wgŠŽŠV_Œm{Ó+7Ò¢_7ë†f”êd<QŽš®«¸Ÿ-R%|¯mÍXÏÚk³ø4±åjµÒ6uIvF†»â¹Øp#1n-Žú‚@HwŽï§zhv|lTü}¦¶nÞ$ˆkC4ùzgÖBbaó~öøÌáVeœNCÝ«OÌÝi×ÚT:‡EOoKôÄÇFkíZ4±%²8s ¸˜hJˆäíhŠŽŒ ö™"G#7Ç ðx<溸ÄE‡rŽÑÁ£ù¼ÓóÚíÑÌ8s,’ŠxtÝ|îÑû^q(³[ÑèN§Â>èH > †Â§­QSwOœ˜¬ª- »ÇÁ|ì¤)~+ÛÉÐÌÏv»R`m¿£Iì¾x Ô*´ÄRõÃkø¥ßfÔm—Ûãc¬CA½žùã*úlîr²«¶ARJÃs“M±ÝâѵԈ»Ö±UªÊàJ»V©Ô¡USJNˆõJ}sÑö}‡hÃöý´~Û^}ß‘¼2q¤¨yÜ}÷ª¨ÿž2nôB¯Ü ™€@€€ ðBñ@ X ˜ÝY†»1ïlÑÅaWOánžŽš®%Ö¶N6Õ–Ï]:uCÙešÂ>1ƒï¸v0 8¥Cm³øóÄŠó·§ßÖ5ƒƒÅu6N횦 8¥Ú¥5ÙíþéÌ/,¦õÛ÷ÑÚ­{iÙúíšËåaŸtu[¥²#bÕ7_=úPÀÃDA ž †ê —T&ðÈÓOÇæå¹/àÈÍ—ÚmêUMk%gp7ŒÑ$1žš%Ç+¼PSŽñ“œC|ŽÏÇÆÁ|$®Ÿ'±€8Ž›ò Š)——ü‚"sÛüœ_äiÓ¢‰šñ‡!¦£ò݃ûÓ?_ +(Vx$œÒ§[[³»«1kär{hÉÚm´påfcó® ûyØÃ{ºj·=þšsÔöÆ,î ¾ 1ä ªÈ„€ø¶ìóL¼Æ ýn®ò`2舎pèÝ;¶T{vlM]Ûµ ¦Iñ¦Ð $!WÍýGòhÁ/iþ²õ:û!il½zÙ«f½‚ÔL@†xïuo|„{³Òt#¥CZ3ãÂ=•¾ÝÚÉHa@@,EŸÌYÆavŠèÝ¢Úè–W™‹Â ê¨bÀã+„Uo¸Ç9¾Ú~›£=ŸÂ~-ú…{©Z7óæ-Wظc?½þùíh^! a#‹ƒG> ŽAÔ€(j%C•pà€@U2ùéw«6=¦áŒ‹Vþ|Å9¶^܆2ií{ß,¦ŸVnËàgIñ·<5rd>È€@°€ ¶CyAÀþ6~|ó’bã3øwÆ™½:ÒŸI1<»;T$ Á#ßûz1MŒ•1ö˜aÏ9Ê©xÛ è †½…P>h$¦*¥ù<¾ÓíWgëÃ~AH Pµ[÷ЫÌÖ4¾Nµ zuÔ¨£5‹ý hB.^G Fy@  Üå|º™‹…CU:?ðÇ !„‚±ý\æS:¦‘|WUí¡é:sì~.nõ&àŸÐ¦õ..ð7û'LHÕ]žyv›­ëý7]¤vnÓÜßEÀý‚”@JbµHIT—­ÝÞ¾vÅ,›?ûÛ ­ ŠfÐMf Žê‚ÀÉdŒÿÏÃ0ìo7]hƒ:-¯ŽÀ vªž³tNŠíììq£Wwöy‡ûi)ÙÙöÈÒRÕîñ¨…Çb•V”^‚ÉvëÆb¨n¼p6„4c²n%2Þøã%éüþÝCº®¨œï¸=ûêÇîÜüÂES³ÆòÝB?ç{Çdµs+j_þ]ö$ÃhK µåPmÅ–lz‡»à™{ß½Ë9Ì‹Gø•ð|ÖKûY4íç³ö+m6l´‰§ÀY×’F­‡h*ûý`èµPCꈟé®MA:þá[/Á³¡:HØWk?ðoÏü‰3ÚΞ2nôÂZ_æ'¦;§Ävøžãïr4—èºÑÄBéi’«$'ÄÚx›ç°s˜óØÉd¾|®øTÖnžü×ÍóËÉsÅ.*(*¡c…ÅÚ‘Ü*.u•»Ç¨ŠšÏCâ g)Ñ4#{̘}Ö½Âm ·pkqÔj  k¥/ñôq·^~6„P Œ°»ööîH3¾]̣˴ù*ˆ¡“ “‰Ž+­h‡ïÕu=1!6ZëÑ¡¥­sÛ”Ö,™ÒR“(2ÂÑw¶)‚ŠK\t çí>C;÷‰ß°cÿ°Gò.¦bz6}lÖ‡6›í±W£¶VWÜô'ŸLTK<Ýt]Ie S2 6^”D]QxÎ:ƒ XäbIVBбO1l»ÑÆîÉ£FfÆóüvÂC/°Û¥¿¸kìľšîY~Õà¾tÙ¹§ù垸IèxyÆ÷Æê-»×eËìúµ­ 32Ÿê¢¨®YÜåÕ‘ãy)çõëFþŒîžs¬­ÚLs–¬ó–”ºxŠ• 4J]¥xb3ŒN_%ØKÓ´:‡œç©{ŠˆÔ_X8-Õ eYÑü—Çgî¨?-ß\Ù•é›!W¿Ð íAm ê×ÿ ù~èÞ°eJ¢²zóîNâä Ö†´„XM¨À}¦AjþµcKIkˆÐžëÞ‚}z"É ›N†……Ê~?%ü'©f+ÐCqw‹‰Œl}ß(í[¥6¤õº–»ÞÌ‚ØOÐþô›_)ûçò(ÀC‘ºA»]ÕÛ4O¡–M“ÔM©eJ%ÄEqðÕŠå¬Ñ¼æ®6âizÌEºæryRߣÇxÉ/¤C9ÇbvîÏ9kç#].ÍÅ%äAkY}j(¶OÅÁ¾z}up„ÓGÁsèHŸnm¢2þ04t*†š4:/~XA_ðL÷žÖÍ>|øp­Ñ äňÀ»ûñ‰g³¸IUhO\ܵ̑Y1âb#õ”„85)>ZIŒ‹!éÝRûô°ã²éÓ#~<…Å¥¼¸ŒR·[¿ñ¢3m]Û±njäôë¦ÝôõÂ_u¶L©Ý;´¤.ZC|’šX/Ñþ#¹´vë^Zµi—¾yçÒtqØÖ0·Éñ o?ó装 ½O}¯‡ª/9\!Bà®Ç³®Õ4ã£{‡£Þ]Z‡H­P@ ðÞ7?Ó¼¥ëЧdeÆBy¼Q†ÿs>—T¬>Èž¿ò˼ÃnÓ{vjͦJ‡´Tjß2E|{¼q«Î£¤ÔMËÖm£yË6èì»Ä¢H9ÆÝ„ãÒ]';ÃÅ€äׄn2¿âÆÍ@ ððh•ólªÊô4D¤¼æ êmÛsÈPTeePWâxáÅ‚jxÿ…ÐclåˆïÕ9ôì@§um«6̯9èÔ½Q<îœ>]eQù{B_ýøküÊM;ŸÞ¯o¾7=s\FvÖØoêžký¯€ª?;\ !AÀ`çÈ&‰±<†âhЩÄÁœ|Ú¾÷°”fV€©ÞŸwì“==Ú¡YuîÓ½-]q^¥Ujr½óÃ…• ˆEíîáC• Û÷Ñôo·9p8wVú˜¬1Ùã3Ÿ¬|¦ï>á?Aß±EÎ Ø—¡mjrBÀ< öÊ¥/¬dÿSïŒÆ-u¹i=?d’¤<{÷¼£¿qm‰Ú_;ëÇ•ì'Cš£L­ýUwfFæ„ë=†¶$.:ºãC·^¬¤_7BÈGÍÔ­}Ksû¶3{uR 2&²…èYÝêwÙÌðw%Ã?0’RØ2äÏôñìet×øÿšËCÿšN/¼ó í;œka÷Áú|Þ/â\é•"­a‡Íßý–V=õÊOD™”gw5bȪÇ7‹VWÊûµæ˜+í¬áƒ5ñŸðu²¸zKdž¨¼2ƒýBªÍ£§þÌüîÊ7”HŸÞ©MjäØWÙºrÌ$ßàáktÛUçÑgœÂÿуc²nòíËr‡òeܘÿ÷žW⪟PhHµ’bh숫關I9Ç èóù¿TÊŽT+}®ï‡~ÝÛÑsÿIÆ×7‹^ÇçéËVÒ±‚âžWÝA¦Ñk̦Ü|Å"IDíÔçkì»>®IìÁZ-¶Jtâ¡ïŸ´LMTï»q˜¬U Êr_?l€ÄZÒ›ò”ŒÜóu%|ótðu©‘?€€ (Ûsò :x1ÃZeeçÈnfd]Ž®[ÄCŒßÿv‰L`^+S ,üu3Íd‘!qLó"Ç¿_IZçÈ×u-ÄP]‰á|1üð:îâQäæûµfùtî?ŸÌ§Ç_ýЗœÝ»Òˆ¶«÷ãy{ºš×Ý–çP¢fMÌi D$H«HB\4õèÐÊb!ÙÌâH¬ ’_Õ´y÷AS]ÏâåŒ^hè€åâB¬C}y¤P"ç'ÁïDpÕÆ¿FÄJŸní¨; 2±ÂTM¿²€¡%ÝuM¸k° :±öˆe«Mó²98Û³pëÂþ(­›'S\Lmä€tyþ¨¶˜ ì݉À÷›e‘òó|Uæ-®ÚbŠ©agö$Y„ÕOl²’Ë­±µ¬?[¾ú—[…Xoš–3±RÝuýP‰*l^ïµtNûxž!méñxFFÑ€lçèõõÎ0.Ô÷þ.©ç°XFj< ‘¿³ºÍ®¬òu)ÐMækÂÈœ€ÝîxÛãqMâî'ÛÕüâô_âù X ;³—Ù“_ù?ðv¼NGó5×¥×\Ÿ}Zgúàû¥t ‹,*ÎàX/bU’.­?q÷“tWÍ_¾‘®t pŠyõç(ÏÁ$©‹ªé¾nÙºÄÓ#˜åâøK¼ÍoÄ“„°G¦qÓ>¥Wnbaö›‹ƒXh¤ëmâ¾üí–¿þmßñ-U[ØÊ%Ytä:uaaöÏþ¾‹'ÖëXö­ØZ¤™Ñ‹¥›ÑJ"¬¤ QŽYI„WÅ$œÞüò'³ë±u…k+žSÛmÏŒþÕÂ_9Zñj·5žýñ–öÑO;Ç(Þñ|¯mA|pž¡Sψ‡Æ"¶á¡—}P¾pÈRœð¿Y´F4é˯9Gm÷uaò5aäNàçß÷óKòóVnfŒÿÞcñ1ѦøUB‚¬¦@v§ŸÒ-Ewm•Y·Y u*',y¿÷t&[S$úñNî~«˜b›ÝeÖîŠiǾÃÜÅö+‹©4þ¾ëiÈéÝËŸ@´T¼^¶[ò¬âƒû÷ Ub-²RTdIW˳ßôÛòPÍdd žË‰»ÿPw¶xÉt"¦VlØIÍSHÏÅ/HÖ<Û¸uÓ’$ì䘕8:²µi®EÜýåŠs):Ò΢èÇJÇêúAºådª ·ÛcãHÓ$u€žl_×|ñ|C¥\¶r©b‘Dò/ùçBBY°ï™¡*ÆŠ({|¦?J1äʸ8ÃÐ_-(,¶ÍYø½âãÒ¯{{ú‚«[¤$²oP™i÷£¦oˆ¨3ŽûÈTõ‡KKd„¾fߢGŽ™~5»Ùâ’“W6šKD‹t–w7Õ1Ô‘X£DpðÜKå-~Z×6t˜óçïRµ'ÝJâß$IºýÄZ³‘»öĉ\^=Ø2tèè1Ú²ç ÂbHÊ+V/™Uü”iåùöêÄÝm,¤ûp#/²}j—¶åÇkÚèÓ­-Ý~õ ³Œ?°%©¾I,tÒÕ6ìÌS¨MË&ÝxŽ®‰·gKFæø%<ç-éS¦íœ6R~åvT¤]üC@,2ˆaÌ+iÊ‚öëŽÔ„³^t>Pù?bÈG`‘- }ÏÝ|ûxê-s¸º8‹%¥bËÐmW¢­ìèü×>"q¦–ëzóÔ â'ôÄ”Oh¾àQXÝ*^VëížÅûêóûUò5_$Ù÷?àÿöô[4rò ’É0%Iyx¦píö3=òü»f7XJR<5åE^–Г®³îâ«‘•þÈÝr2–ÄPzîí¯La(ŽÜµI23úeçžÆ#ø›¢°6×T=GÀEX]?ì }û•¶‰÷_¯ÜpáÔ¼ib_îj|SÝsxû]c²n®z]0|naëò ×o÷Çß/Óýe-óò‡¦U±6|¤+T|Ñ+ù"6ÖS?5ÞåQž9¹ù6þÿàöáq¾øÀ¥þªcŒÀþ*î ÐÒŸ|2ÑV¤ýÊÄi™w^­Êö@MâO0yú·f—˜ˆ‡ŠI†çGs×ÔÉ’Œ(‹b«KŹ%ZuUkÒÉò©ËñüÂöשܕ%×»Üþ«p×`ån­Úä-/F±(ù*ŽRmÊPõñ“úlîr}ÛÞÃìr¥Ì5¢•?[ðÅ™®âÚ³ßG>_ñ5_CAÞO–$tÂ's—Ñ?Ò¯9Ù©^?.ß·Gž{—»í jq|$¦7n2ùx»hÛžÃZAQ‰"ÿAûZWèå)OŒžÉÔ:ÚiëV*ˆ¡ºñÂÙ Ò2œY§ú©UÓ$Û=7^ Jž@JbË•D~çà/:#Ї²T! ñ•¦µ­\ìŒÎ^ÉÁ˜ø¥Ìñ¨â9dÙ”ÕgU¨(†DìÌã.#‰õÍÂÕ$~gç³›øÄmá…ÙÎ5cV%ÅÇO°l0o™ŽöGÌî×›.9ËtÀ¯.æ”4Ìdñ%V½Y?­$XП»ž¯æ˜[Ö?'Š%á,ùÞK|ì¼ÄZ»tívî®Þ¤‘÷ZÒ‹…BV G`ê¸Ñó UíËû6¾ðöׯG<ŒÍÖW  w–X:2…‡DZ®O—R…¬°é'â·õЭ—P|lôIÃø©Hu¾8ÅK:|´€fò´Ù¯ì~öM“Yâôë«tõù}©ͬkKF^Š£¿|ÿ%6•ÄŠ*dç{ñƒ;·O7F‘4ðÔÎ,¨rL¿:)[u1§d¿ÄÇ’9À$ø¨ø§­ãîgIu‰e^àÃ?2"óŽ+m,@“U]‹»„}Ò£…8C>lDd ÁJ@âzÜã|y€®}–§‡¸}î²uÊà~ÝÕ‹Ø)Y^jH PtòÎkѳo}m(ª2){\樺\Hçf<žu%iô?~)'Í/fƒ‰á³–šbmUå±áxPN‰tn%±®ì>˜k}4»•Ë?ߨoK¬K¥¦ï™£,ë«j¾ÞþÜ„»ë¯ÚßöÖ—?w×{pþk½}ˆ!oE~ "^qÞ[ÀUI¿Û9ñIÙ”ùÝÏkÿ<‡EQ·v­դȰïŠAÿB¤Ú¨†ˆƒcD)ËÖm»ïï“&MxjäÈ|ÝÊ'ÙÞ3&« ‡ý|A׌kcc¢´‹ö¤óútµÉ”/¾J5ÅÚªz¿(Ž%éÿþtQùDëœ%KRÕ˜S²¯¦ü­ØXY³+P’t›™É­ŸÆk¯‹!t“JK£  ^uŽÚ:%+óvüÑ» žÝ°}ïúÙß`ÜÔOéÑçßÓæòä¡’ösB™èô»ÅkÍaêR.”£Œ€8ów(îXçÒ`b’1&ë&M¥õ¶«d´×Äûn°]|Vosî»Æ¨‡Ä×’xXâø,]dÓš™Ó¸Hžì—n±Õ[ʺ¼êS¾ºÆÆªÏ=êzM.û1IâAeûêzmm·ª %œ @‹hË”q™j1¶³ø‰ôoAÂ_é­K왪BÚ&@(/ Vèäë˜.¾ˆÙâzû"ãA/=,ˆÎóEþ¾È“…P¦NÆ;l™ˆúgÆu6™ó®±ýÕD¬´JmB£^|Ÿþóé<ÚÌA8Ÿöò$¿ò›yà©79vÕâz;¬[jËÜ«æ)óùñðzWB|IJªÇ¼ñÙ'ŽHÞ(ò<écÇÿI1ŒÙŸ4™çS®bO è«Tq¸ñÉ–ÙΡ½ÅkBHîé˘.¾ŠÙr2Vy|\ö§î½‡~ÉâúÚÆ,Gmî-‘´%€ä9äó–ËÏ6£…׿:#QË%˜D1·’ÄÙ’d ‘·ö×wí‹ØXu-‹D‰ÿïÏ Åõ4û›¬ëõµ9ßgŽ_µ¹9Î÷ŽÉj×wðÐì,ú÷Ž­›EÞ;|˜:˜»<ÄIÓ—©âpc%©YögåÇ•›Í—€8Êt2YQI)ÞjžSÌ–¢g/å—B¤ÌsdZ‹ºµoAÿýl½Ã‘ne¸ñ!ž£L|Y$ðbn~¡9‰©ÄÅYôëVÊçt2t_Ηî^,sI|#‰ÑÒ¥mszÿ+—ÿž/àYãeÖv‰¿Ò“§Éôóê-4…Ë9d€ø{í;œKS?šKï~½Ø¦-«ï~ý³9§Ø/wÒ\Z-#{äº×>˜KC_'ur¾ö [’XxÆU{ojýÚs˜ÁbsVz-#Ž1-þu‹~ôXñÞå?Ì~3Ëg•éþÉ“#µB×÷Û6‹Ì¸~(ë߇uNc¯<õLÕr‰oPuþAõ-«XX« ! ¿ „¶È+(¢Éï~«•”ºFÚ†ÿ£WGs±Lb*CˆõïFnžîBDUûVM9^J3®Ë!î^»ÿš¢Çawp •XŽwÒŒ¶ó„¯·]yŽé—! v$¯€^~ï{êÔ&Õæ/ûò ‹eUžDŒåp¼I2á§Lý!Ñ­oæÉ`%~Œˆ‰û"êZöC‘{I*áùËŽäýæ[,±yä~â"©º{ˤ«cæoº˜æóPotcî¸Êxßé“þû¥„د*¶?dϼÊ_BH¨C …çwµj ÜíÌÈó‹O8ç*“B†Z’p2ÏÚßž~‹G­ agüfõ‡ì'ÿûÝ÷ä›°n7]3ät³úUcºÔÄ„C ðD›ÉôÏ©Ÿ˜V¥sût-?U,C·]9Èœ?ê¯}d:S‹Ð‘ýç÷ïnŽ’{äùwÍ@‘͚ěl~2g™9Ь9 &™ª¦äí35ݧ¡û¿]´†Ö=ªCµ?ßмüuýs=Tm‹ºD7ŒO%nÏ3oÌÔeÖx¤†ß¡Ìë6éõ/4±Ð®Ùº'‡­Acâ"ºMÉýQÃïP·à  neÆÙ > ~Bûô Ëš$Äõ~"ã›?†Íú µÊR4Šˆ¨.Õt¬º˜.Õ]_Ê£ÁNð± ¨”¢x¸²tYÉeÎ ¥T æ'1ˆøåP§áËÞŽ1c•¯¡k±Èýãµ5—G›15+óO ͯ1®/‹±E“y„Y ;°ël55»Îd„"ÒÉ Èl÷;xTæ¦hý¶½ú†û$9»"*« 2^ieoýºÓù×F›bèämˆ3@ ,Œx|ü¥ìÝ:sÄuç“ÌÞ"ðÒ»ßêk¶î+¶FWÆgîòV¾þÎçÁgŸ.Ì)ü³¢¨²ƒ|§ˆ‡Ö»Sš­o÷v_*Íkkª—tã.[¿cSåÓM—œéóûÕTŽŠûgý¸ŠGn¢Dî~Nˆ‹f¡ï 7;ýk,æ‹9 †X@ÙWÎÃkÕãѹ7JáÉzi=çñ±ƒìï¼<î±5ók¬m8P7yÜŒ€¢ëމ‰Ôúuou¬m‚¹8ÒÂód±€F¾2~lÐ !ié6ãÕöíʾ놹ÝÚVlØyݲuÛSÙ÷Ë`ÿ2­SëföiM©MójšWïÀ¤â?&:Åš²“ceq×­ÆABmÌ‘{íCºï¸fÒ˜–) \úéÜå⃷˕¨iF2ÏaXndaÑXÈ\¶³pÜÈh“¢Ø:b臗G¸8Så…æÊ Ðpw¿§OEûÕA3mTÃ+|J`ÛžÃôÌ›3užéýË)Yc®ÁàÓ6BæÒ½¼Ç=~ ×íö·¨’z–k´ŠÂÓbxØQ^á@ ¶ž:#š»geª ‰-ḫÈ\»<Ê+(¡cÅÆ‘c…zn~RfI‘ÉIETÐO†¡¨Ú£>Ö=%2üKÍS’Œ«÷³õéÖÆtÄ·îé˵„§X¾n;Í_¾Qc±ÆâLùµI‘‘áv:çØÒÚ¨f”êr:‡—Í âËÂx1oˆ!/ÂDV ¬œÎ{ÜK.>«—ríÐþÁZ ”;€È!ϼ9Kc?¦]QjLßçæPñ|Z”{œ;s<¥ž¤Q'·Ý‘­%ø†­Ø<&–“DîEbxã¸V18š‚Æo•rt†,Žx¢±—ÏßEŠºÂfØ—½2îï« ÉŒÇÇ_ÀÒòeöÅé­Ö¹­GÇVfl¬”Ä8¯ÔO|Ùr8ˆŽB¬S›vîÓöÌ5Ë­ÚÔ¬âÆOÉû•WnÖÈ™@ 5ràö Ø$¯dŒÍò\4°—z-O¹ !pèè1zú³ôÂb×aÕ¦œóŠsÔæ†äj×Êïí_Œ8¯E ÏðáÃËæY©G%%Ÿô±¯d-u‡¸œ-Ræ¬ÉâËÔ2%Ai’«&ÅÇ™‘É##"ØŸ‡ç-c‹”™¸Nf>ÁSêró40n*dç~±üðÔ2wÉiÇ KÊ]iØÓ¹€/YÄv*?ú'‡©EØK †¶iP0ð/ô̬½½;·nyÏð ü{cÜ-¤È´#O¿1K¦QÈSuǹ¯d\R ÐʤO™â0vçôQ O?ÖH}¸˜XÀ´cáÒš{ãj5Žª¨ùÜ“¹ŸÍT{8ô'[¨ÔŠal´«öÕ©¶‘ëœNå÷áДG]‹U®úêz!Î-üâ·ë¶ï»™‡–ÛjvZ5Fm¼M@J>?ýý^ò ²Ÿÿ„·טŸøìðÁ%Ç—Jç‰P²åäÄi¥J¼âQÍÉÖlvÍðx“ï±/î´j—s^ËÌ䙯‘@ ¸ ›,¸Ú ¥ŸÈ;þÞûÆ)[w]?TpÀ€ìsèAv‰#—~6ÿéss›û³Ç™dÕ@qA œžrå(° –ÍŸ½jÀùCpÈÿ+6îܧŸÖ¥ "ŒµÀ·£Œ€LT*3Œ/^½EáPȳ"ȸâÕ¬ÌÙàÁL–¡`n=”|H`ʬ[9êíÔøØ(ÛˆkÛ»´máû!ë@' Ѥ¿[´ÚX¶~qÀ½=<\ûÞ©Y£? ôr£| PCµ¡„s@ L ¤;Ç÷ã(º‘a´Ü¿»rÍùýjœí=L…|µWnÜEß-^mðlã Ç¡ÉãÉ#ž'{³§²E!_yT0l@ …MS£¢ P?÷8_ŽÓ<9YþbžÔhO9€«õ=;ªƒúu¥v-›ÖêúÆ?ç(3Ožþ­¶ÿPn®këòê¨QGyú騂BWã´•âðÌæ%š¡”(¥Äæ±6·wØætwù¹¨¸$ˆ¡€l ‚“ÀÝ™“ºyÈs‰M¡Kyâ×Á,>¢ì6UïØº¹ÑºY’­Uj2µj–Li©IäMR\ꢽ‡riÃö}æÂBHgAÄ~/ŠÆ¹yºJ¯\Ø«ë'–ßË]c'œ£“>»Mó&Žûo¼PaaœÀ¹ÔÛ÷¦W?˜£6cØgæÒ ­ Db¨‘Àã¶ êîŸ<9Òu8¡ë+¤œÇ¦‰^,Žb¬z'ÇÇxZ7ob‹Vb£"(&&Šâ¢#)–YÛl*y44M3ײíáíâåäÒ¡Üct8·€çäëE¥.sh›tÙm¶µÜm÷{ÿÎv¨I?ÔÔí3"sÜ0VKŸ§$Å9þzÕ [Ç´T«hA±f'gúlÞ úyõVæ*;yrÔ˦8G¯ ŠÂ£ ` †¬APUì§£ÜýÄ“íÈcôÒ ½—Áâˆ@=TUmaèF2ï‹®mÝÙÓ×åPl{426êš±‰e¶*ª±9Fùñ9çC9µÍ'}LÖ¹ªMyGÓô6ý{´§k‡ög§ãøÚ^î÷óXLÒê-»iѪÍÄñØ÷‰J`L p+~IDATÙ%j|+{«9-ñ{pCC!Ò¨;±$9Å).ÍHQ w OkW Å¥:Èeh¶R).¡ºŒhwÁ”Ñ£÷s뀆'§óõ¨½ž½#ùa8Š»ö"Né˜ÆŽÕ”>ÝÚS ÌËVX\Jë¹ûoû?­Ø°S+(*á°?êŽû35Òæxõ%çȽ §€@ ¼ @ …wû£ö Ç Ü•••¦Ó6›r§XŠ""ìZ÷v-ÕŽ­›)Û4£ö­šwÁù”—Ûã!O»äÐn^¶í9¤íÜŸÃ]€†Â]aÇ4ÃøŠGï½sA¯®_XþO>-20!1& j‚ÔŽ€Ù76kˆNêõÜ7D×ôîr%û0éM“ãMl©É ”šo†ˆ§H^xiZ“d8? žÅDÖDš®Qû:—¸‰ý›Ìm¶ðБ¼Óÿ)'¯À8tô˜v$¯Èfºù\æ¡îùÜ•¸\!c‘úÕ½;ÿ TÆAÀÛ †¼Mù„û'LHuÑy†¢ŸièÔ…-3ÝX0uâ%²áUÄùù0çµuÓ6v2ßB:­ ÅX>e\æVou6¼œÈB›ÄPh·/j àb=º7s|k·jk¡*z{/‰x²Bj‹š…OÛr„G1ɶ‡Nžb(¹ŠMÉÕemØsZÚSwÁñÙ „,A@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@‰À½c²ÚÝ5&ëæ@*S •å^ç„®#ÏúC • eð/»ozwK“õC1†KÍC)æÕ.U1>-kìl_×6=3ë]…”_¦d™Tӽ܊ڗH{†¿]Ó9á¼_ÓŒ³Ãxˆ|ÎPwgj8WÞ+uW©»BÔFUÔ÷8¿¥¤P/ÝPfŽÈ÷¤Wò?A&ŠAŸ*вà§ü¡ gÖé™ã&|AQ@%ËWšVÙ3eܘ7Žgõdzæø±dÐ÷N˜ðôË£GñÊ-ªÉdÊøÌéÕì®]nºD'%9¸ Ò‚€„C+¯mÞÌã¦!ºB§*ºÑŠ%‚;³Ž±õf ‘º¨ÿ®‹ç\÷T‡óü§æÒ;ñúȈ̬i¤*rwL7Òé~îOËËÎÛï~çä„÷±I\žKÙÊ“d(ô‹j§G§83—ÞãœØÙíÖfE9C^tŽÜm•ñî±ÎÓtýr¤žixMVZ•=.s²Ÿ1c†í»UþA†r+wÝ%°Åj¾BÚg†uññuÆØ¬»Œ²r´å]¿*Šíá)ãF/”Ù®ÓÉ8Ÿ-No“¡?cqºƒ"ú½’5rÝñËÍÕÿ·÷ðUkû3»{Ré½YPÁû©€W„ÏŽX”kÁk»ÂU’ R’“àbÎI( (-Åî_¯¢X@P¼þ½ˆ\®"!€€””“ݳó=³9ObÊI9Bð_NvwÊ;³Ï–yö}ß™±ó «4b+ó'¡Ž³°BU”ç3žp/wòN˜6­á‘ãÆ,ÆÄMh—‹s±Ü£>"Ib\ŠÇ‹vŒCœo˜ÛÓÛ#BáÓ¸ÅÉò$_íÈš#hÛnæg¶¼4+>Þñ œã™Éò&?(’S‡ù»çâ¼w …-ÈNMž/Ód¨è§þö?Áãio \+> dSjûÂf>Û¨™ÏÊëm Ñ mï€ #ç …³]–P¾êÔôüµƒÿ·(ì 9Éà:?­¼ÿ´Û½ÿ$7唫^×?ÕrÍ5C­®…Y“&=åH "ê9§%úf@ë>èÐÇ¢/44*~òÿäçÀb_¯ÛrpýÍ­ŸwE°™ÝÞÙ ¯åõR–Å¢vËm™Ÿé =¿ÂŸèAÅ·ËxŸyl ˆ‘Êe$ÈÒNnXÃ-S|§ÞnóôÄmqÉ©Ç Mó~dM/–ïa=ˆ2›²ôø|˜âÚÂgh¯“¶bãödì? õ—ªî1Mã~Á,i®³œ<6,‰se¬KS¿0üF¼eYÒ§ýE’.’F mý…°®ÑÉRTõþV¬g säçC]Ì:x’Å¿óŠQ jïŽÐ§wœ¯OÈ•yA„Þæ…\Sâf³ ƒM7ò­×‘ÔOS›¦›âðÙ ¨¾XWìX?S-Ëo"üÆUÃõôs艻t]({ ï?Q‡©ìýå:” -ëvÁø[8f Uû-– ²›¥i‹|~c®µ„)6Ëãž!ó  å^ƒâ´âÿc¼ÞÖÖJ!øk Ya'BS³´+üÌ?¡Ð¼?îJ—ÝÊ YöMëg;Žl>’–©¼¢*‘3&+øÝµ*Rá.î¥s!Î&Í z~d܇ûè›vZ‡—t}ha…ë A’~Ÿyt«Æ"®+KªƒÅ÷ùb+∠ƒý±ë#ý¿²,^h}ŽÃj‘¡¸ïCø°é…0ù.¡@å €þéô ßÝÒªu¡Å³ýB 嬠õh‰|"–°n`«‰=–È ¥\Ù<Óp¸îíÊü¼±É¬. Éè¼ggéýR’—‹Î‘­·™3z´OÆI iù¯Ñ\J·º{c ßœ+‹ +ÇCñ{Ž3k¶6zlƌأ¿úî©È_z#ÄÆ•§³S×f€ü´BÜçd„&F’†Y™ž$›H Miˆ&]ˆ;—«Ê€Ì'ÜKrån¹p©±Ú= Èt´/õèÑÂ1–etÃa.´=Wph™M;7COüAæ‰w{<Ѐ¬–ó|}äNä‘ßlýÑ_e:Â18¥/L³öwíõ§K ÑQtÜïY~vö—?¢Ïj’gä]ªºøC8Æ{礰ììT÷Óòan<ˆˆZ2´e3,;éá^êØq0i6/ȳVÛ·²½ÉO§Õõþ¬š4)(:6Ï/ü÷HÙ6穤¤ÃŒ(F›VáCéÙŠ>é!ÿ“ÐÜUU¬”D…kü–Ù_Q”xà €h]MÝ”cïÃqzæÿJr]ª@4g£Nä0O¶Ù@äÔ¡Ø?\îY„í#Ü_üá•× B©MÊ1¿˜-,1äâg r¨!ðg@à´q ^?°Ý%…~¶ˆPðÅE¯Ò&cÝÍ­_Y×ÝþBN¯j_Ú—øMñ6´"³¡U飨l$Fx-]Ž/rˆŒGGx^Pæ?žô]p>\@¤ºÊ¸­Á«hÛYÃuÏeòøˆÒÂ,¶R‡Qii-AÊš ÎWǃ”ÈTŸÙ yÚCA3/ö÷ädé]ô«+¤I¯8pv¸rÅrç°’mŽC„dž'Çσ¦ê °xCy ¹]ÐyX¦ù´SŸÅÅD™†Nù<¹-/ Ür¼¹ûÊ4nY |9pY,n•š¢|³àZtèû2t÷¦8ýÉh{kH\, š¯ lôéægý_úüU¾õ.dæáëYj×ÂÒ²";çG¿Áu°‰P5+б,1=-Ky/3³]L5ËÚÙUµÉk™©ÉÙžäû\ÜÕí¸ˆ™¬PËè:·€ëäÓÀ¼s›ÊET¨ç}²óåk_Çûð¼†ì¶Pý„À©ŽÀi¡úf@ûnÐF|°×pqŸÈÙ-t}0×õÓR•29[ —›‚óeiTXÊ´²×ÀÉÂÖx<ò“Â=Y©îל´ºÜNÏŒ<×dEŸá€¼Õ* <Äö-^¸ðÂþµñ%’&+Ty:ÉÖTæ/6B÷^lâId»þh¸î|y¬û˜¼G¡É[óï;˜N"[Ê‘Aj%öšk¾T™ªG7‹ZyâPþF…GôÏôL°MÄ “½7À<+5žpm9N¬¤’—Beþf2K¨~`ÅùØð¼¦U©]„¡xËg¥”gŸ£iÍÆî_qOÀs6¾t³GO™Ó°Ð<º ÷G8û¿ mfî—× ]Çß™íqÛš[ÛgïÛm_ávÄû ¥äcø|r>_jc+ó”mxtæÌh`µ7ÔzŠæ<Üë7ã^—¦^Gë)³ãígvµ¾Ý:Ç•h?íÔâš‹{Z±N›s­í’ô¥Ð.!@”E}Hýo>³©ÅLé$\K"ÀA°Û¿þ:C7*ð%þ ï§qzÚ_‚ë‚i§^¦ß;q\áÏáxðÃÉ©ñ¿FâE'-x;çñ û@” „É{Çû+ÑøØ~<ð[‚æ¥L`ÿæ¥Núop¹ºØÇ {3:ªÂ×%÷KLˆR}Ï‹ýeœ:cšÅ®Æ97Ügz@稶W?·_öœ-ö3ëFô}„Êl2$;cœÏt¬ýœòr ß«~ÀÃg´m¾;8¾¼}”ßmÉx8oÿ&…¬x=í‚òòÕ&Njr@„Ú!»¸ßûì8²EvÜ5ëÓÚß‹ `§àt/ýÅüó@^Q5íZi=·'èégƒ} ßkÜu%^Ãï¼|óć²S†6òs8¯nHŽõÅ  5,­ù'ªß"â<[6Oнa~§¥ˆ\À¸:(L "ä…¨è`³¶ÒßÌåâ×#½uÀß,Mú ÏÑMÒ~kh…E[¡Ñ~Ÿ¢)ý¹ÆïÀÙöðþ,§¾nϦi­Åɬ€œ ài8 ç61arÚ°guóøƒèè{*.øsB攤UøüÕÇÅ\¼|WÌ÷&ïqÒ‚·R[„/Ðç@¦Œ›ìý†µo±†ï=Øf•øRùŸ†Ži FîìPµfoš¬ JX¾~±yž7ùÇ༵Ýo¯%’cxצx £½RÛhÉŸí3gœË¹ñ׌T÷B)Áçxé§ÄMÚØ1£Ì;¶_ߟC±3 YÞ‘f™WaŠ4E>…Bçð× gΟœqÐllg4_Âöþr=³¬áûŒ3úÌÉZÙ6û‰äEÀåjaX‹FèózÀ§éDeù«“v˜å>N²NI:ÙáÞgµEî‡Ì•¡¶E°#%è çgøLãaô’;#µÆ¯:åÑÑþÎ_ ÷Õ8´}4 òm9ß 殄”ô›@a^ù,UúÍ•ø¿ùŸº¯K_$éëæÈ—[ËrDå÷A𤷆¥¤ÿÈ„ùÊØY«ò7ƒæÅ&pS¡Xp»æeé‹g‡a)ÞyðÇËtŽ¡ ²µ Žóž@Ü"øÒõÆ@‚<[’4ýŠ6a´ƒÿ„ãßR·Ü2Ùti~ž¥=l1&]ëEÔuß>ë‹^(³ÒPx?ܽßËg+D?Á@õl7FCŽs USq)iO¥K@$ûNŸ8ñ¸“‡¶„!Psêµfhý­­¥³ò?j~úå—Ä×»bøýÓËO­›Xù5éj4/sCXæZŸq,Df,ÔéÏГÑ)IrðEû<¾~ûs¦<çÄ—·ÅWb^’ß‚|ÄöøUø™Z™GƒóB¥•»2/þY†yø0 Â/'ï¨Í‚óÕžMbbø-hûq ÿh¯é)°DÑV8¸wäã«WjKv±ÆOp/!z¸1¡ùµxĘÌßFk»p´Cgó¥Cœd<:¹¹ÐNdaˆÿ,öÓÁ£8ÿ…ˆ^‡õ™^ Îh5×ä¨iy¶:å*ËûÔóÑR;0º²<5Mã~¿4¡„L惽€:8~Äj—ÍÑG+P¾¿Ø…¸?.Ƀ¹ž”Ý(¿d¡k–Û ­$[fÚƒÒÓáðÍn©/ÿ~åâ|ÜË«‚å5h¹)ø×9D³ ýÀ‚Åáb¥å3¾÷]£ L]PgSÇ·MnA ¯Dz‰f5(¯½+}Ö YÛwÂÊï-MºøO‘º-Ë ±’æ8ÜÂÑÊÅr7?A™OLQQ®©Ö/Œ‡!s„âRï ~ŠKÑB€¨)õ[3d²1øb-þ”¬)–}7 lsáÅKr+5mV$*Û“rsyiNèö¸ÌLÛw¼qvðè³ îÅ¡ü• eå¾ïÖõ†eGbä×j À;NÁ€JÝ–'Í$"Âï ž›‘Wþ* åƒïÔ™ÁeG‰ãAòù¾#mŪ‡¤£µ“'à€}½ì@* >Ћæø™‰=ù+ !à¶svId`'à·‘.‡Ç75Üm’“¯,VN|Ùóh’.sÒëbë3|#pŸºêBVYû×´,­WRœ¹ºlZyÇ.­i×*4^¿óC&´(E¿“‡ù¡`V²ß!\eÏ|gÂ<4q_þÚ»w{0©/]– «ÔèµË;t(Zqx+fÄä Ñ߬"?°ÒõÙG¥êû]:g‘ˆûþmNš|±À¼æwŽËÝr x°xŸ\‘ž ÒÃ[«‰k÷YiQÐȾ‚4*ÇôôA¹ûeYü*ý:T¡þìì—Úr&ÍêŸÃÄ'ŸbÂU*„@M¨·dHÜy§úuÁêÛK^ž59û*ʘŒ,W‘­ÖÉø—Z †ÂªäÍÕ'æT•§®ÒçøSEò‚G¥U”'”øâ ûÜ¡dýÃòÀ«æ®pÞ§˜[@’ÈPMNø·ñ[ƒËJ"í3Š.´T¶EÆ·U®\šc­¹Ö}0uÂ}pz¯L³¶šÉ^Áò0YèÙR#ë|Ù@+³˜µÐào&}_‚ó†cl ê¾Vú³U$8ÀºÈJ½3Î?ñ™b1 &UÅ6é‚~šã_kxïjØÎÕþ3)Óö´ ÛO0KOúÞ©§ØO•;ñån5eTŒKÁ|LÿÙî1 ó—NíB –”z°k)ë-þuáÚnxy6 g¥xI]Nù$ûôG`jfô™~QØ1œg ²um8å3Mýðm1,Ù›Ø@‹ÎÌc…­ŠLC‡éi;kßr±¬[Ž>„™ó%Ë/çºbÝ#bJ“§àöÁðû,´(+aŠŽI_Øçß>LÅ©Èãè…X(þfÁ2k»ïb®Y¦0†à¦BE”É[5ÎñÔ\X–‡¯r&µ ÕOPʨ,HÂ?,%ín˜·—Ãéÿ—´¯²B”F•"Ímý 0Ð_ö–sþ:Â~TÁIE€…ÿ¬Ó§Ÿ^¶›€ãÌ¡u[ž™·ÆÌ]%Ú`´ÙÁêšú<|£ä‹ƒM¯eñ‡ TjJFã75ÇüùˆÐ§Ðº¼ÍL‰I:³²rks,§à*¿ mä|§oÿ±ã…µþm=¹H—ÃÛûÂôµNå¶óuÀý54]M®ëzþj'/´Doãcí|œ—í/$ãCõüMFÅ{Ù©I«á'7 Þ¦áŠsR !@„‚žýúÖh…Ñì©p·þÒ#ÔjÍ9î‘üz…ÀÔlõè°^ w£¹+¶UÒ?O w=röoÞHøäˆ¿ÚÖS9eFëyßo¨@`±OÝïýÍ*È^ëh9²1¢Ç¶`=”7ß–4V”j原`É¡¤|„!ê/Øæ1|¹Í *AB/íq+<‹¹UB»§-éYêKX/„û]1m' ÍË w=$Ÿ Ózk&ƒ:þHØ/glj…åÓº8Á÷ ÂwGDµnþç!Ü'Bò B€8IÔ[2Ä™º5ì˜ þ:Â~TÁIE@¨ÛÂ^¿`?Žî¿Ãöz¨B€ NSê-jÞ¡æ§|ã|’P×Ù´ 塆IþB¾OË+Oq„!@üÙ¨·d¨ã‹?ÂÚÏ=™mø³Ô]ìÌC¾j„ ³ü5Š „!P¨·dHB 247\P£ÛØ}é¾âùEª¨$>ej7Ì'ò ¦É¿¯Š¬§D2†Gß…óëwJ4æOÐUSç‡ë4¡Šüé²NW/ —|’K„ÀŸzoÖY7 õ >u}±¸ª ¸tqîÒPäbqÏÙW¾ÄÔý¹mŒ³üÂ… U̬›'Ú+@Ý:`J9óŽlÆ"“·&¤xc¦Þ1_ËY R‘˜¨.‡i|ÖŒj*ü¢WX4Ìn#°JѰvÚyËrÌí3qžXËŒ* ŸñD²½ÄFEr2OúO|ŠW® Õõs|Ëz¤ÆOö Ä/!>í:¤¹Xü=ùËx·ç.Ì®ëÁ ‹6.ìÓõüq­† &”§4i™Êëpú¿«tlí°Æ×¤x¿¼žB€ jˆ@½Ö ÉsVWB®Ü ­Ék¡!9_ˆÅø-\m±$â_†0p®ÅÊ[¥öå ¬_vµªEöÑh®iÍîÕõ…˜Â?½IC×í´ä Ðþï1­ÿÓYºû£” "‹U©Ïk§%.Î1¶ÝÙ°[bQÓ. Wï´,ö´$Z•É‘æ®ðWc´Ø®®Xå¬uÿ(}Z‡Ì'’— ée-¦òï&‰œWÄhªêâc®¨®p}êúñÆ­½ó míÐ"¢Áõ?P{IA8[žg¾¶]y¯…MøŸD0&jLú“œ*&!Pï¨÷d¨ûâŸw¨‚c-$Vù‚Š!^˜Þ¾‰ŽnbvÆöº#›ÿ¥Çç+±ÚÛXb~°$*ò !ÊzIL2ôñ°ióäúw^ˆýƒraU{¦]Î6`êþVNX^!²vÕ$¹*µLÇq¹€¤ŸcþX¾ ÄeçÊo·]Y•¬³´r¶þè¯Ñ.—t4_WdW;uoE¾ÙÇ‹­(u‹2 Ìlû¨T©µ¨‚óÓ~õs)ªz'QŒúÂ}º-R4¸OÞ[UµkXM†ØÌ²ù°ôÄ©)ž<í|tÔ[ʦ;ÇñÉÓ;í5¶åËÉ8¹Kö¦ ÜϦM+µx.êûhxJz÷à¼Îþ¨gž‰Dúü¶;qåm‘~?dg——V8iÂ>…ˆÜdÙvh’va3ñWÊKÅ„m ÿ?àK–î_üõÀ6÷À ñ 4*5ÿ‚å|ãjÿ ßü/] -a ì2t,›Í<{Ñõ¶oÚ~=J åcfYO›ìÝÌ,ÖZŸÝóõ ¹Ð&b{´MHN3Ø>˲EÚÚ›@X)xæ[t ×AN'v¼xm3Káíx‡f‹*’3BŸ×À4ޤƒtu9vÔW’v>4EË5”ÚÈU·Ñ¶›Øqæ<œ ~÷m©LtPk&=d~65K»ÕbþE˜==¶Æ9Û©EÝ0îŸÇC¥¦q×&+ZgcœCž¤–°Ð,úŸ¶Jçåû­Ý+k‹àEC`j]2-}⦗ÊËEþÑãE©ˆ{¤T|9£ôgùý£ßWÆàtý…¨½ÆÞÉ.iâ­eÀR³Fï¸bÕRTo«]õ\Œ©§už§'m«3Á$ˆ jŒÀiA†äÙw_’»pÝ€6?àCùUt4çUtþèûyV«h×£g¼ùsÈË ŒJKkY˜ïïŽòW‚Á冱"Îbfì!Øý ’´z¯å=? ›…">m§´·µ-r=§¸ÏS–`w0n}¡¹úÌIšøÛR \R–@dòT®ŒÊð$­uâœmEr@„F@Ã#²=É×ʼøÒ~×f9NÁ -²å‚÷2=)UvhAÅh·LŠ3—OÍŠø_‹¯á>íZ]¸O_kÓdøèû µìÏÄ­Ð ŠOö^‰2kd9ŸiBKÅßÓõÁEÐ U( ‹’*{ ï‘\܈5»ÞEÆ2dˆ?#,öhBJúK©‰ÿ©Pæè£Å¥L¦hê~¿áZQÞ}æÏ÷‚´ý{ž'ùGIŒrŒ½ë²¼É]œüRË…yÆVdz’–É}؃¿â ‚uÎéãÈe✤¤ƒÐ,ÍÁÇ@/#Ïú +G²=)WÁ—æè5š9N~ Tæß§§ýë±=Ž·Dg%ªÿŠäpÑMá s1á¥ìõ¶ÅËÿ¹/ƒÅ”Ýè8:A$ÄRÿm¤Ûs–Œ“ ô#rÒi[·LŠ+úïåzDó¸W~QúgLUû&Å[÷V‡9²SÞ9ìã.€3·xý·ãò÷öYi}pƒl÷6ž©Üa)é=‚sâ¾WyÔoù3ä}œVÞ~Vê¤ï"]E8튈ÅE¸+mÍäÑFG“8³Lî–ÐÈ4k¼ˆÐ‚kc4‹…ö•)…Ö2?Ô(´/7Ösµ$B2NQÙÍ¥„ú\<»¿šþ¢¿ËøŠüûd3­ BÁsîMS!s«$Bv’iÎÁù¿ÜÞÕ¹Úü¾b’d¼mR´ØMãw‚œ]Žš¿"Ú~~e:|ÿ6@Γ!@œ Tùò:Y6tš³Ãwé’Ü'»G÷:Se¼Þ¤ÓðÅö^äßb ¾8¿ÄïUM‹©v¼ôý7v_²ÿËêÔQ’Wˆ!Š¢¾\rŒ'ÇÏÃËüýÞß¹a/ÒÉ4løxã¶øR]‡/Ô¾Å4òï™a}ÂöÜ—ìÙŸâ™,'x_i &㫲 3þ„/á†yxkÛÕ¦29xÙ·“ö8È^Ë Ä[xñg92¹¦¾ƒ‘iýОø]•áIY…×ó\¼È¿…üor ï~¸ÃÉOÛºG wïU&ˆÍ‚Ä8뮨×àžôà}÷êü¶à÷î×7ÐÁŽuEDtvÇ‹^î‡Ì•5m‰¦)o€ôÞ! K‚ž~6ät!ø¤*yRË -Ì¿d>8ùÿ Äãà2`5Z¶'IÈÝë÷&§Õb¿WÄŽË ¾4;5q\<¤Ežg¿ŠÊ.ÐÝçë‰;rÚn“ÎV« Õº&Ûßû÷É4<{WsE[gçãÚz\Û¿Oj­×1Vy;'jW´ª)ÿ‚ßhƒ¸Ï0ú@ݼLPZªHÍ5•ÀÈ(±l™ô N:§™¬,’üÍ7ýˆ[ø•M®“ch„..OFi •/Dœ­ŒõœëIÞ-óŧ¤]΄5þ7Bµ _—ƒd¼­¢ÿvÛ6hp22Ýn9gL©yc$&AÜ ÛdÀ5Èz|Ü!tžÄåÉŠäd¸m“Z7é§!Ͳ'dé“~Âþ%#ÓÒšÏKJ²})0ì~Úüäð)O¶´Ú6:’ Sž“Ÿ¶áC@^GHÿ<ð+S‘L’¡ö>× ôÄ]¸_~Î5=×@3펲(Ø/­¸žÒÿmãØß0’±'H|Èu$:û&ÐJŽ•æµàÜ.ÁG1ñÅ}úÛ†Qd§U{Ÿ‹ŠÐ~®¨´,à¿<_;Gšà'üŒ7pŽËnGðA짃ò\ðx2ùl±Šüûdj[.L#5n²÷E¿eŽà\yEÆçX9—£íó¼žyÒ J9ñSžjŽø«ÐHŒ--Ø„Üæ)Áåi·{?ÚÒX>Ó¥4ÆNÚ„ÀŠÀiK†þPË©lÊ”U*| Ë¥ª2Ù6;å™·¡{Ûƒ¥ÔÈaš!¦¬, ÃØ{áÅÙˆ-–#ª$*ðÒ,ÌÒs⪔S–9åÖ!BN\ c>àÓötC@yÃbV 4èÓëãUa½ L`a$Sì¼àmðZ°×Ú6Ç‹‚ËK?ø$=c˜E3ÁïjÅÞðŒìõ qämjž®Êg%p‚¹° ‘öžS?ž§RÄ̉/»¡§Ÿgšþñ±ÍbºH-R‚Ûs¯Ÿ ÛLV‘Ÿ”Á¬§Š_†6ü¤j|F†îþJÆ ¡œ€êx¦Çݳ¤m2×|ä?§øÄÉôtľ|^íÒØÆ4Š0Zthx—r*¤-!@TŠ@ÉÃYi.J¬6ò«[U”¼|'Ks;afÊ—|Ó†®áL‹NÛôl ¯].ö€c3ï¥hJo9<¿ZÕ•œjUJ™ë+X«MñSÒl™’Ðà8dÇshj~Ègù6)_Ó… RßK"T„ò²efjÒ¿8¹hÕ` †ÇÏãõ¶.(3îRŽ’ÄLæÃ¡ÊY ’P›çb«¿#l¦ß´^Fûò Ãé¶ØB'­Ê-çó„!^Âyÿ€iþ­ÖhŒ¬ü7¦¤h9s@¯—2lÿ>Îþ}G4|À„-Ž×+Pÿ `˜“cnʘ7 i›XCín}ÒУõw´õlå4QXêFšÆn͘â^—â½9Ï<±uK\WASü“¬K«îðEú¶øˆþ„ÀÉF v¯¬“ÝzªŸ N;¤Ö>=].åÖàyxääŽr¢Òêž°”¹Ï혌¥œi&œpL[¶¹ºØ¿ï©ý’ò¥F¬MÅ@‹ rÕªÑRK%Ó@Êfb³>ƒóä± ÃÓÓ›r_#c¾>²ÔeΜÝøXSl“Oî?¶Iu©ý2ôÄlô N*¤:©ðSå„!P©a–œ6Ñ0ýrtZ‰æ³&DHÊ–ò°) åà'“ìP™üà^Ŭæ‡ÚŽrú ƒ‰ëà´½Ú)/·AÁQö¾4É•,:xüN˜ú^%"T:&N¤:yØSÍ„!pŠ 0<%íj?æò‚OQ;˜ø~ÁïË& \ÏIâ„Ɇ†ëóü¶ÃINøøö)Òtj!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@§)ÿ E¥ÉB6-IEND®B`‚neutron-12.1.1/doc/source/admin/figures/scenario-classic-mt.png0000664000175000017500000010720013553660046024467 0ustar zuulzuul00000000000000‰PNG  IHDR˜»gáåÌ‚zTXtmxGraphModelí[Û’Ú8~š©ú÷"Sø—df’lU25µlíf/ðÆX¬üOŸ–ÝmË'0ƒÇ& Ü€[R[R}”¸qî—»’­_Ä”û7vgº»qnlÛ².|iÊ>¢ôz݈0—Þ;%„‘÷ŽÄR7Þ”¯S•¾òViâDŸ¨I)¶én3á§ßºbszcBM˜Ÿ§þíMÕ"¢öí^BÿĽù‚ÞlõQ˘M¾Í¥Øø¾Û™…Ÿ¨yɈW¸Pç6Q lô¯åîžûz#i¢ÝøPÒORò'rx€ƒ#^˜¿Á…Þ‹åj£8Ÿ@ˆð]z>p{?–ðk®EƒÖjO»®k¦4ožâ£›èÖ-àh µô±Ùgcî?‹µ§<m“å^¸TlùçL%4æ{óÂîCl ¥ÄV>ó‚w{XªØæw„– £9Á3ÞzÀ/K®äº`«í"ºVŸ· ¬.Ò&úHd¾yÌ;‘ ü@±‹õÆÐW[!¿Åúµ¥s÷JáÄÏŽJnl6Ÿ‚ÝÀG!ÕBÌEÀüÇ„ú>G'½õ|穯š|{×ÅǰìÜ›múY7†ã‚éP›8x D _”žžu8ø_®ÔM*Û(¤dfŸ…–_Èg­¤ø›77–Õ\r”Šj-62Ä’Zf&çœö-¤Þ—ƒò”ÜgÊ{IÛÛ"á„CaÁL¢+ájmp~Ö„&Ö]?…ÛŽüO"èˆc"öxj•€+7Ôôw­3­fvšòZQf"œf‚ ÞmþÆwëPjCèÐ_í’6bò¿Ml–c.ùô7b3x¦ßS—}¨‰êÚkYN¡XLõ ÉÔ^ig)/¾ºnå5u7RÏXw{°Ý?eÄå‹§sÉmXéôÛÁüªá?ÌÌŸM˜;˜–4sšM*›_+h€_œí.zÜ6ðLÇFW<á±kâ™jD-àgcàù ›¼(€çEÙ*sDõù£äÜaaïW:Nv3RÍöSÑïZd=Qj.i¡ÈÚ%ãxõ^¢ ‰v0ÄnÁKä3Õ‹Œz\ª¥µõ ¿â¹ÏÉ*‘ðœOV/1êÉÁ¹Ñ¨'–‹z¾þêQOK‚$!,(6ô”œÃ_ƒžÃBëupãÚzš¿;õã8 2[¦“ µy'A³¹ô §KF‡¼„…Þ­‘ sã+ž‹ðL¸0ƒžÚ/VÆsþBÅ%=987zàTo _rJû¶‡´¦³LTŠÔ+™Ák/¦W.V’¸³'ò ´ôX7•þE,²®8š2Ž:t—õØ\¢…æzÐß ÷¸‡ïØéöQ³[¯™þñqM ~U>J·ãÈ–Çqä T,w§›aA'Þ§ÛídnKfÕl'{'â°ô?ظ캀Ý6x©VµBKÁ³Ê1¹`ël`wK ÚÉÀîeî¹çÕì²÷œ:¯º€M^F´ö‘\êÛ¯¡œè˜l¢6²Ê½ ˆ¼4Ýz:ÔE¬ÏèÒÑÂJ|}µ(HÐ{¦ -ÎÃú…Ð×eÔ @!>ãTËbÅи6ž¹²œ/™ÙwhSÉýéÑ#<&ÿ8ŽDœü‡Ûyür}7% IDATxœìwxåþÅ¿»i›^rDš ˆ ÖkWõú³¢$(°D,(°ª A° †ŽôNz@ŠHJvi‚€€ÒCÏùý1³Ùì.[fvf7ç<Ïç¹7³³3ïÌóž9D(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢(Š¢TV’ˆ¼*"ËDd¯ˆ”‰È¯"2KDî“~CÓU_ˆì´÷ãý-Þ¯ÞÐÎ(ÆèñREQE‰ˆÈ-"ò§T Bά‘Úz PG¸Þ•ªçñˆÄ¨6:ÏR3`6‘l;Ážz(Š¢(?tƒˆ—ª!豉È1§åËE$VŸaê¦@¦ID6Kõ°~·šô ÆÞMDعÅaùÍÛš£Þ½’žû¦(Š¢(ÊGÅŠÈ6©œ¼ÿ‘Ö"i=RD²Dä°Ã:ƒ?L]HÀ¼Òá½§þÿ85èAj>î¾Y0)Š¢(ŠòB¥râ.‘sݬ÷ˆÃz%N¯%ŠH_©¼ã¹MD¦ŠÈe.¶3Ãa;·‰Èã"ò‹ý}Ed°ˆ$¸x_ û¸ED~‘Åë\("Dd½ˆ‘Ý"²BDž‘h§íÒ9¼÷‡ÿÐÅ~¼{„}œy"²CDö‹ÈRé("æ3ŒýûöŽŠr¾³¥úiWÇ›ç°Ì‘‡ÞçË5òå8δï•Ëš:íãS‡×uXîÍyöõx(Š¢(вk¡TN´ƒ<¬!ÊäÝTDÎwXž*J@tN‰H†Óv'ö™nÞW$U¿PÈ>^•ÊÇüßÚ_¿LªWé´=¦YªÞ®/Jˆ­øù.ï9ÓØMNë83Mªž;DZÏróž/½8Þ3…<_¯‘/Ç¡uÀtuž}=Š¢(Š¢ì2‹r'«bâ¼Ùm8Þ•[(ÊÑÏE¤Ü¾ì¸(ÁªBΡb«(w…Ö:-¿R¥}pøÿáÁ1䕈H™(UC§ãöü ˜×:¼ï;û²×–qñž3ý‡eGD ‡ƒD¹ûç*H9Ž¢T ¦‰È&§åçºyOÅñF‹ÈíËsEÄ"Ê߯‘/Çq¦}0]g_‡¢(Š¢(»Ò¤jÈ8Ïéõ ¥òˎ̵¿^[”Çê%°8>òíí°ÝaË'ö…¢%,|ëðZ'•öqXDž‘ºömÅKe²Tª>Šåð¾ÿsXîoÀêð¾žöeçKÕ`ãü˜ÜÓØEªqÇ; ·9,Ïs3öŸ¤²~/ʣߊ×Úxq¼7;,wìAús|=wû <`:Ÿgއ¢(Š¢(»Î‘ªÓùO]áôzí¯ßé°ìC§÷^êðÚ:‡åŽ{¦Ó{ú:¼ö†JûÈvzO¤ˆüÇNª}Yºˆ<(Ug;v ý ˜"²Óá}ŽÁÇñÑëNïó4ö8‡×vHÕGá&QÂÔH©Zupû‹NÛéðš»»žÞL_¯‘?Çánß"Ìl§÷øã9Š¢(Š¢ì²HÕàø_§×Ï0Ÿwóº3ÿ8lÓùËŽzÃᵊ€è>îqsì·‹òˆz£›í0orxséãðÚ(§×<½…Ãk󽇧±» _¾L_¯‘?Çánß"Lçóìç(Š¢(Šrcï¥3¬[ñX³"`f‹w1¤òÏù0ÕÞ‡ˆÈ@§÷‘QþƧZós/ǼOD¢Þçi쎡u¶—ãFÀÌ>Ã1:_#ŽÃݾEÔù¹£|=Š¢(Š¢œ4\*'ËíRÙ‰tVS9-Uæ“ï}Ãþ^wTÈ×€©ö>ΗÊ/jü!"÷Jå—ECa 3RDþïCÊ^޽¡Ãk+\ì÷,Qý×ñrìjL_¯‘?Çánß"Ufs§×¾tsŒžÎ³?ž£(Š¢(ÊAIÕ°3Iªÿ]Ä¢ü›äëTÌ–å8½'A”pÒ^Dþç°Ü×€©ö>Ú;¼ö‰ÓkŽß.$`:~Qe¿(vÉÇ/Ø8—§±GŠÈ ûk'¤j»@*ƒóO^Ž=€9×a¹¯×ÈŸãp·o‘%¯9~YÉ$Êß¶ô5`úã9Š¢(Š¢œ4Lª†Ì_Dé >*"Kõ»q3ZD~³/+¥×h‘d™â°~?‡}ù0ÕÞG;‡×Ö‰ÒLåo!:c 3Çaýܬs“Ã:{ÅûÇûã^/‘z"’$Ê£æŠåŽ_LQ3`:¯Ÿ¤òË9þ\#_ÃݾE”.mÅk+Eù;§Kõ?ÑämÀôçx(Š¢(Šr’E”"D”o ;‡Ìa"ÒÕáçõëû0ÿ'UÏ[„‡uÇ:¬[ñ¯éœiì"JuüHü,ÕÿC5¦Iªÿk@Žwz}¹F¾‡§}›Äõ—ªÆ9-÷%`ús<EQE¹Q¼(w„^å±ñâÝ—j‰ò÷{ˆòoI_¬ÁØÔÜGÉåßÂvþæq((VD®å_˜é"Ê¿¥Œo4Gˆâ‰×Eù2ŒsÐòõùrgÚ÷¢|xè!Ê¿¢d’À _SEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEI„ˆ¼§¡(ÇñšÓò;Eä{‰ôq uEd§ˆ$úðž›E䘈\â°¬žˆ‘‡|Ü?EQEQTHÉ$"ˆÈny@Dê‹È="²KDzè8® 0ŠH#‡åÁ ˜&ùLDÖˆHŒ(!~¦ˆLµÿŠ¢(Š¢¨°Õ¹"rDD.wZ~—ˆˆrÓ$"™"²CDÊíËÿc_ïbùAD¾åŽÝw"ÒZD~%ä %Pµ%`M‘S"ò£ˆ´°oãûÏQN?Ç‹È2ûvvˆÈY"r«ˆ¬·/[,J vVCQB© ”ŽÓÓ1™Eäeû6ö‹HQwEÀôf "")"ò»ˆ¼$"÷ŠâÏr³.EQEQTØè>)‘hë\("‡D¹³y–ˆL‘PUÌ¥R9OG‹È:Q§;ÌkEä'©0ßÅsû8"ÊÝHG9ÌH)‘OEyì_áMOÇ´XDžrÚ^Å#roÇP¡Ed»ˆl‘×ݬSãåM§AÄu7!Z*{‰rÿg_ÿ 9)ʧ€û¶Ïýˆ–¢Ü’~_c? •óRQ>ÝâÇøƒÑÉ ;CÚø3B”OõãD ˜ê‰žÕƳÚ÷Uñ¥‰4QîB5r±.åZ Dùïý*§åwKå]»·D¹ÛW¡ú¢œç:â[ÀÜ(Uï`î忉kDäg©ôf[Q¼í0_ÅÛŠ‘VRýK3ŽSD¤±(¨ß–Joz:¦ "ò±Ãk×:œ oÇ ¢„Õ¿Eä&û1‘‹\¬WãåM§ÁS7ÁÑ$ŸˆrÝ$Ê£8ˆÈõ¢b÷ŠbOýˆ–"rB\*JX]bÛûþý°:T`bgH[ƉòÈ‹S=ѳÚx6Q*«ØÏÅQæÊ;™Dd˜(ç»(³(׿mû:ME¹Žw‰â¿I¢|Xˆß&D¤«(^,Jµ!Ö¾C"rµ(×ó{ùE*½y@”?ñsž(7©î·ã]ûºî¼™ä°¬§TÖ("ÏpL7ÚÿVûùøÖ¾n¢cˆåŽí(Qü^ñ­òÎe—7OÝÇ_`·‹r’“Eù„:M”;™WÙ¶ˆç~DKQ>ñV<ιS”_Þ¿‰Èlqý¨ÜH *0±3Tý˜Ôô'¦ú¢g«“šž5‹F‹ÈÓnÖ¡Ü+JD^‘m¢„°Ý¢Üá«ø@d‘GD¹Q9;Þ]÷6`®åD¹cYñ;&R”Å¿OŠÈjû¸bD¹uXßÜ#ÊãfˆÈ Qî:ËUÀŒ%_ü(•w×Ý“ãÝõƒ¢ôŒ7JåÝuoÆÐE” ZÛaYš(ÿôt±~–7OÝÇ_`g‰bàkD šŠÈ×"Ò]DÆØ÷ç©ÑR”O>†¾Ó¾ßqÿxÄH *0±3Tý˜‹zþdÀT_ôlõcZ,êx¶¶ˆä‹òHõ¯SÆP{Q>4PT5yÓiðÔMpü)J‡h¨(ýú¢|rZ*Ê'Ïýˆ–Ry§SD ˜+EùE9ÕŽÙñ£“A®ÂÎó1©éOLõÕ@èYçcRó‰öcúHXI2º0)·ò¦Óà©›àØ£©ü„ÚA”_š›Eù[Q@<õ#\ÌŠ/ù4åVúÕ~Œ_ëN¥ŽØÒÖŸ ˜ê‹žÕƳ™"²V”.j#; Ä÷o°SÚ‹“r+o: "î» Î=Šíë4·¿>U*?M‹xîGx ˜""ïIe×ñkÝÉ Ô;CÚù“Sѳê{v˜ýugøW<(Š¢(ÊŸ¸©P=[ƒµeQG‹µ0³­µè‘OÖ—<±ÊVüè.kÑÃÇJ 2@Œµ ó”µèác¶âGwÙŠûÞVÜa¨µ0³í–EùW(*ÌÄÉš 5ѳ5L[u´¬ËÏì´¾ø‰BkaûS[—¿zhoé§ø{Ë$Û™Óû¿}GB€ò+qzÿ·8¾«o™„½ë>9µå›®GlÅÊÖ—<±j]Af‡-‹:òOQTˆ“5j¢gkˆ¶,êh±?úª­ø±}Û—÷:|hóxÝÑ.xþ½e¶/ïuØVÜaoiQÆ šEQE©ªÒÂŒ»lEþõÇšwßUZMjÇwaûòž‡mÅî*-̸+èæc#´©)= ú4´©)>u=Ú„ºg·,êhY_üèˆ :•Þ:E÷°CôãðÖ)ذ cÙ†’'&k~7Ós£§,þ^CB€òƒ«púÀ2ß]¬ô0J? ›ûBáCMé ѳáC({¶´¨íÙÖ¢·­ìùÏéý߇V‘Nù娱ê­#¶¢Ç¶ØŠÛ5TÝtìaÔB¹‡AŸÖBÙ§Ž¢gkF÷솼Ìf¶’ÇvìY7ô(­!Žüµvð)[q‡?U ™Õ{úÿ‡J‚ƒÒÃè¥_ñ/Tsѽ/ä§èÙš‹Ñ<»eQÇ[Éc;l½ƒ 1.û¬Ÿ©2Ùà µ‡á£èS >u=KŒäÙ-‹:Z¬E6îY7ô(®!žøkíG§l%­÷Û¯•=Œ^ÿœÞ¿T÷ÿ‰þ”X¡mñ/DœÑ¼/ èYâŒÞžµw˜²}U¯z:l[Ù먭øÑé>›­²‡1ì¨Þ¡†¿ÖQ¿‡á‡Ø"žÐ¤/ èYâ =<[Z˜q׆ÊNï[ \NˆW”ï_Š _?uاzGec4ôþTGŒ‹j= ?žñ½}JÏ_ ¦g·,êh±=úבߧêXHèqxË$ØŠ;üáÕ£òÊư£zb|þZ;8°†Ÿb_ˆøBÀ}!z–™`yÖZ˜Ñó÷å¯ÃÁe Ä~_þZ™µ ã•3šÍVÜaÊöÕ½Nèý Ž„~÷0ûBÄWôð)=K!žµ=rè賃ßâGÿ˜ [q‡}? ýÛÃØ¿D÷ÐBB‡òË|ïa ö…ˆ?øÕ¢g‰ŽhíÙÒÂvOm]Úõ°Þ…„>[—v=l-jÿˆK£ýÛÃØ6M÷ÀBBÃ['{ßÃ@ì ‘@ð©/DÏ ¥g×—<þã¡M£K ˆ¿7Åú’ÇW¹4ZeCÿÇ$4ñº‡€Ø" ŸÒ³DM´ðliQÛ³mE/ß¿X÷pBBŸòý‹a+~¤lË¢Ž)Õ̦ô0æAïBB—£Ì;s#@±/DÅ«¾=K „žµf¶ýmÉ qà¢[—½rÈZ˜Ù¶ŠÑ”F·Ãz? ¡ÏÖ¥ÝÜ÷0ûBD-<ö…èYb@Ôö¬­ä±á{ÖÒ=”ðaï/ƒOÙŠZÅhJc,ô'$ôùû·ñî{Š}!¢ûBô,1 j{v}Éã«oXBˆ*ü½y l%/þ×d•=Œ¥º‡ú”ï_꾇€Ø"jâ±/DÏ¢¶gmÅî:¾s6p`1!ªp|çlØJÙ÷¯Éþía ÈLƒ­ËzTïa(ö…ˆÚ¸ì ѳÄÀ¨éYkaûc§÷– ÐPQ¶ù3ì(ø/6Œ‹‡5GHa ãâ±=ïr¶¾°Nï-µ¨ý±Mf+yløž_>Ò=”ðaﺫ÷0ûBDm\ö…èYb`Ôô¬µ óTù¾ÀþE~³{á=Ø4>‡JÎÆ‰å€5’¢ü» pby#ì+¬ƒMã㱫ä6”ï-öÛåûÀZyê_“)=Œ‰º‡>üýÛxØJ:.Vã—`UŸŽ‡Þ>Të ©,z–¨šž--È(\î,níÓÓpzeà» HˆS¾ú|l›–‚Å7ä‹Ò‚ ük2¥‡1zCŽ„Çw΃­¤Ã>¿Û|ûBDmªõ…T=KÔFMÏ*s¡_±}„MããpzEc`õù$L(_Õ›ÆÇãÛG~{£JÀ´¶?vzßBUFÙæ/°£àv2BÊNÆ8lýЯëzߪ= ľѼ/¤²èYbdÏ0w-¸û j«š0co^:¶ç¶R)`dž*ß¿Dµp¹{Ñýìd„0•Œÿ(Œù·£ÜÇ åû—Tía¨ ö…ˆæ}!•EÏ#{V ˜_ûÅÆqñ8ñM}`ecfœ\ÚÇÅùí*³´ jý…Å·²“FTv2Zûì…*&Sí—!ûBÄÙ›7ä µ}JÏ’Pñ¬âÏ~aÍ`E#†”/oÛÈ¿½¡IÀ<²~(;aÈ¿ŒõC 0Ù".¼©V_HeѳÄÈž 8`.oHÂkލ0«ÿ9_ÙµàNv2”½yµ±=÷2Ÿü`¤€É¾Pø¢j_HeѳÄÈž--ÈöÍ÷ kŽË0Åš#~{C“€ÉNFø¢t2â 0Ù"®¼©R_HeѳÄÈž 8`~{ S 0ÙÉ_þídè0Ù"n¼©Æã•EÏ#{V ˜%~aÍ`i}¦(Ó?o¸˜Þý‘WO°“ÞXsÄ'?.`à ½ŽÓç–„¯g˜ßœKÂcLtˆ6"`²/DÜySÇ9*‹ž%Fö¬âÏb¿°æ°¤ Súç sqÀ°“Þ(Ó{?.`à ½ŽÓç–„¯gù…5G€ÅuI˜¢øÓ?oh0 Ð v2t˜ì 7ÞTãqŽÊ¢g‰‘=pÀ\t SŒ0 Ð v2B9`à ½ŽÓç–„¯gú…5G€…g“0Eñ§Þ¨0øW*`'#¼QJéÞûA›€é}„}¡šƒj}!•EÏ#{¶´ Ø[èÖ¾þ S¬9â·7´ ˜è ;ºLïoÓ³/TsPíqŽÊ¢g‰‘=«Ì¿°æ° Ž!é® ­ûžåßû禠Mb4¦ÏÖÿ8ôD ˜þyÃEÀ\0ìd„7JÀôÞ† ˜8‡DCo†cÀ4À¹%áëÙ€æü³ ‰0kû÷þ9Éh“…é³ô?=1fÀ4@w€hƒ1f¡_Лáj}!•EÏ#{V ˜ù~aÍ ¤¶!é_Ï„Ö}ÒqlD.¾ _=óãÉQxe`-œ*© ä¥aì‘8+B QfÜÒ>å¦áƒæ&ˆbÒc0}J:Ö¼ƒK“"‚ÔF112(©íyÛÅéXÿºW×2A"͸âŽD”æ*cÛý~<îªg‚Iu/‰Å¬¯ô?_®P¦ÞÐ.` ;@´Á“}!âΛjô…T=KŒìYÅŸy~aÍ 8Ýô¯gBëìZ8öEšDšqããÉØ=' ‹DBÃxlÌOÇÞ·£|N,rG¥aÇÇñ¸)!}rÒYIÊÌé81&—&D¢ß{©Ø3%3î2£öMÉ8\œîqÛǾŒC‹ÄHôù0 »G%¡W.~2e“qGRžy3{¦¥bê]H¸0›òõ?gÎ(þôÏ.æ×£e'£]¬ò BÁ„sÇ ÷{µqÒ¡7q³%Ž«ú¾?zD!î‚$l/±/+©U/Å⺺fDŠ &%m;¦â"ûëy©x"Ñq_‚ˆ¤Hd>—†ƒó«ëÐIx¦E’L‰0ãüËâ0qÌY(Wé¸Ë>Cㆠ°©³½€;:þÏì ¹€}¡Joªñ8Geѳô¬‘=pÀ,ªeH”€™†c_Äâü¤hÌT–û<MëÆâ‡yµð×›QHJÁƒSQVX ‡§¥â`^-`f¢0§×Bù¼Tl†ãEµpjv*ægF ¡e"Õò°í4üðPê¶IÂaûxþ‘ˆ±R±ù¥H¤¶LÄžBeyù´DÜš‰Qõ?gÎ3`jÔh—dÆs¦cÿìtü9! yY1¨ÎÖF¹½7¡̪ïûã•(ÄŸˆíÅg%µ±"#Ñ©Ñè—Š kaM¿Ü“f•Yµp|þY@n*žH3ã©÷Ó±vmì›^ +{XPß—?¯Úé(Ÿ“‚Ži&\ýp2J§×ÆþIi˜ü@$ΉÃwyê÷©©˜Ó? ‡J´9¯>w2B9` ×â ö…Tòf8Lœ[zVŒàYÅŸ¹~aÍ 0Íô¯gBëÞ©86<ÍêZðý\eù±á±h^ς榹)øæE n?Ûslî8¶ÙiÀŒD´IŒÄôiiÀ¼Ìn+ÏÀÅM£ð`+3âZ&â }[®·Šù×›ÑâÉdœ¨2®Tü”a†ˆ ) &¤Ú‰±DâƒýÏ™3Š?ýó†vS£>@»¤tÿ,ÝaY:Vµ5#î’Dì,® ÌNRæØªïûã•H%`ÕÆ©‰‰¸ÞW«l§6öö·àò+°© 60/O¤™ñâ'ëä§¢÷9&\õJ-œvxßñQñhj‰Âôûœ›‚^×[0u’ç¾ÅÑáq¸èÂxŒËˆBÝsbðl#®êjïo”Ô†íÉH¤^™„߇ŢqÃxX ôïu#`ú× a_ˆ}!¯úB*‹ž¥gìÙÒ‚ `O®_Xs(H5$ýë™ÐúûÜ¢„¾9ÊrÇŸŽHĪÑ)(/HÅ‘Ñ èÙȄ˞MÆÉé JÀœšŠm/E"¥q,–MVÞ¿ÿÍH¤µLÀA§mUÝv VßkF½;qÄ>žÃŸÄá.I(}.g߀CcÍMõ“$ÈÓÿœ9cÍ¿½á"`.-;í’Ìèþi­*ËÊ>‰Eý”,š£ô&n¶DàÃ1Uß÷ÇË‘ˆ;?Û Ó±?;Éõã`õÔw˜›Œ'RÍxqXå¾þ‡K"Mx샪ûÇœt;[pÖ% ë“‚óªn딇¾ÅÑÏcÑ(Ò„¦·& dx¬/F"å’Dì*JòS1¨‘ÿ÷N-ü3Ì‚Æ •1ëÝëP¦÷~Ð&`ð8ǽW°/¤’7Õxœ£²èYzÖÈžUæ<¿°æŸbH”€™ŒcŸYм^ ~˜­,wüy}§$6¶`ñødì͉G¯†&\ù\NN‹G›øäŒKÁo#rQ,¬³Rp|BÞ¾PqQþÊKñ¸í²¡1h’‰$áàÄ hjBËNIøgLnJŽ@··ñפDä¶‹@ZãX¬Ÿ«ÿ9sF ˜þyC»€©Q ]’Ý?I«²ìÄÈ84‹BîL¥7q³©jwò_ÎÇö‚ZØöR$â›%`w¡‡}ÍIÂIÕ·qɽ‰ØUP}ý“Ó“1í) Úµˆ@jÂÆ× IDAT¤ ç5‹AŸìT-Röç®oqô³X4JˆÆÜéö팉G«¤(ÌœV 'ÇÆãòTåµ²¡4nkžþ½Ž˜赸‚}!•¼ŽÓç–ž _Ï0ó’ Iÿz&´~; Ç>QBß,e¹ãϧ§&`À•fÄ‹@"M¸¢M,Jg&³1¸¥ fK$& GÏ&D™M¨Û8 ƒ^ŽÁõñ&ÜÚ=ÿxØ6r“°¶K4Z% ÄdÂÅ·Äâ—ÊzÛÞ‰Aë:Ê]üÿ4Æ„ýÏ—+Œ05ê´K2£û°Ô*ËʆZP?%‹f¥3ps´¯ LÆæ1)v’±¼SbÏÇöü4ì{; ‰çÆÂš[uÛå3“ñÕs øif0; O¤šñè[•ÛÙ=Ëõ˜ÊsSqd®ÃÏsS°äÅ46£ËÏ}‹£ŸYpA] ÖT¼?744ãÁ~©ØÚ%ç\›ˆýi(ûØ‚Æ baÍÕ¿×aŒ€™ëì ±/äU_Heѳô¬‘=«̹~aÍ 7‰„)JÀôÏÕæ¾ù£e'C ˜)ËR°ê3â.ŽÇÎüT`znމÀÀ/«¾ïn‘ˆk‡íy©89&WE›ñò”*ëìËŽBJb4ÌLf%*ÈÖqÅž7"‘Ø$¿;ö'ò“1êZ>—ŒúG?µàº•ý ¤Âúx굎ÇÐf<ÔW醔 ‰Qæ<ý{ÊßmóÞšLö…ØrçM5úB*‹ž¥gìYÅŸsüš#À¼D¦(þôÏÚLúíÍxîÝ$쟚Œ?G'¢à©(Ô‹4£óÀd”ç§Óâ•€9¢êûþè¡ÌÜ /‹ï1#"1o÷L€ut–÷´à¦XÁ%&¢,?˜™`˜ÉgÓ©±q¸5Ö„kÛÆaÙˆ$ì—ˆ%]¢Ñ0&ÙŸ%㤇¾ÅÑObpaÝÊþò•_ÖÍâÍHK‹Fñ4e™0-°ÎÕ¿×aŒ€9Ï/Øb_È«Ç9*‹ž¥gìÙ€æÜ¦¨0KFËNF;KÕNäDá~I8Q±ÎÔ8ÜcÆÀ/ª¾O ˜±Ø>ϾlN?…ËÓ•oƦD í£ñØ>ÇþúŒx%`Nòj\ûÅ¢ÓÅfœ­ü}Î: ¢ðæ[‰8žç¹oqt˜=`ÎrØÞìô¬#H¹9‡ìËÊG+sŽþ½%`zïÃLôZ\Á¾JÞ Ç€i€sKφ¯gÎö kŽsâI˜¢øÓ?oh0 Ð v2t˜sý‚Þ oTë ©,z–Ù³¥À_³ýš#Àì8¦XsÄooh0 Ð v2t˜<Î1À9$zSÇ9*‹ž%Fö¬0gù…5G€Y±$LQ¦Þp0‹†ŒðF ˜ÞûÁpÓçhèÍp ˜8·$|=pÀœi!aŠ1¦ºDÃN†î“}!âÆ›jô…T=KŒìY%`Îô kŽ3bH˜¢Lÿ¼á"` ;á0½÷ƒ&“}!âΛjô…T=KŒìYÅŸ3üš#Àôh¦(þôÏÚLtˆ† Ý&ûBÄ7Õxœ£²èYbdÏ07޵àä”(` 7NL‰Ä¦ñ  ˜è ;¡0 p‰†Þ Ç€i€sKÂ׳Š?§ûÅŽÜ˰‚˜IÂŒýÌØYð_¿½á"` ;á0½÷ƒ6“}!âÆ›jô…T=KŒìÙÒ‚ àÏé~QfíM££qzr0…„ §'G`Ó˜Y×ÛooT˜{ †ŒðFù·s½÷ƒ6“}!âÆ›j<ÎQYô,1²g•€9ÍovÝ€mã¢Q>Ù çôd3¶ŽÆîù·ä ³ `6Že'#LQ:‰>ùÁH“}¡ðEվʢg‰‘=hÀ,ß5»ç߆Íc-80Þ„ã_™€I$”(ŸdÂɉ&o¦1ì*¾) Oh0wä]ÉNF˜²‚; ¯3@Àd_ˆ¸ð¦Z}!•EÏ#{V ˜S¦ÌÖ;󝯆1J·”„¶‘flcÁŽÜËpdÝÛªøA“€Y¶þ#lÃNF˜ño'Ãú®þ“}!âÊ›jõ…T=KŒìYÅŸSˆ²_}"ò/Ù¯>¤û˜ŒŽ&{ °«øv2Â¥“ƒÝ þç³´ ˜ì GoªÜRYô,1²g0Ï ¦ï¸˜ùªPþ×lì^ð?lËNFˆR­“Qr«_^0ZÀd_(ôѼ/¤²èYbdÏ*þœL<à:`ê?.#£YÀ¬ lÃGØYp6Œ‰Õ½c@üédÄbGî•8bè·´ ˜ì Õd4ï ©,z–Ù³ ˜g†Ów\Ì<â%Ù¯=RÕp¯=¢û˜Œˆ6SÿÛÿF†s|Gû€©ÿ1zÖwT ˜»'d÷x°ª?{<¨û˜Œf0`zfðádí; ˜úBÏúfð`ÀôÌ`Àôm¦þ·ÿ çøŽöSÿc42ô¬ï¨0'¸˜úËȸ˜¹ÄK\LýÇe40ƒ'kßaÀÔzÖwÔ ˜_d÷hë0Ûê>&£S=`îÉU•²õbGþ•Ø06N÷’µÚŒë!xñÞJÆõÐLê–Ò#°al¶Ïk‰Ã¿ööÛšLÜþ72|œã;šL£‘¡g}Çh³ÌÚ;òZaÃX~©7Ô°4cÃØXlŸ×‡×öPÅ.æ<ÕØýõØ4>‡JÎÆ‰å€5’¢ü» pby#ì+¬ƒMãã°«ø&”ÿ9Ãg0`N־À©/ô¬ï)`îž+çû¦Ú|_tÊwŽ ÈšÌE7bûô4œ^Ùøî┯>Û¦&cgÑõ ˜ú÷KŒ ûB¾£}ÀÔÿ =ë;êÌ ~³³ðlŸžÊù>Løw¾/¼: _h0”öǦñq8½¢1°ú|&”¯j‚MããðOiLýû%F†}!ßÑ>`êŒF†žõUæ®ñ~qä—7°i|,çû0ãßùþ—7üö†‹€97`v•´Æþ‚ÚÀª&$ÌØ›—Žíó.ñÉF ˜ì ….š÷…T=KŒìÙ@æ®âë9߇){óÒ±}nscÌãâpâ›úÀÊÆ$Ì8¹´6Ž‹ ù€É¾Ph£y_HeѳÄÈžUæ8¿Ø8.–ó}˜¢Ì÷±~{ÃEÀœ0ÖV4"aHùò†°ŒðÉÚÌ ~þPx¡I_HeѳÄÈžUæX¿à|¾(ó½Ùooh0—7$aŠ5Gô˜ì -ûB*‹ž%FölÀÓóÑp¾7\À\Ö€„)¡0Ù _Tí ©,z–Ù³LÌKDT˜³Æš#À·ç‘0E ˜ÞûA›€9Î/Ø _Tí ©,z–Ù³Š?ÇøçûðF ˜ÞûÁ‘êó¯ÙcÍ`i}¦XsÄ'?h0ýûDžPø¢j_HeѳÄÈž--ÈvŽö Î÷á5Güö†‹€9+`¬9|s. S”€é½ 0 Ðk!Ú Úã•EÏ#{6à€i€y‰h8ß.`.©G”˜èµm0ÂdMÏ’Pó¬0GùçûðF ˜þyC»€¹¸. SŒ0ýë„°/Þ¨ÖRYô,1²g•€9Ò/8߇7JÀôÏÚÌEç0Å“}!¢e_HeѳÄÈž 8``^"Î÷êÌ™cÍ`áÙ$LQ¦÷~0\À4@¯…hƒ&kz–„šg˜˜—ˆ†ó½áæ×ÿ!aŠ1æ(¿`_(¼Q­/¤²èYbdÏ*þÌñ Î÷áâOÿ¼á"`ÎkŽ ê„ ‡¾HÂ3-"dH„ç_‡‰cÎB¹—ï/û4&ÀZT˜›‚6‰Ñ˜>ÛýúÇrâÑüÜ8üP ÿ±ûƒ0½÷ƒ6s¤ÿŸ¶ Ðk!Ú Ú§m•EÏ#{¶´ øãK¿0ê|ò«D\"‚„Rp`¾ãkgaa3D"1pÌYêî׋ù?Ô°æˆßÞÐ.`Î?+$(Ÿ“‚Ži&\ýp2J§×ÆþIi˜ü@$ΉÃwyÞmãÔŒTÌ韆C%gs’Ñ&1 Óg¹_ÿØ—ö€™¯ÿñûCÈLôZˆ6a²¦gI¨y6à€i€yÉ™“p©I I1(œíðÚ¼TtJˆ)G×Vw¿^Ìÿ¡†1fIíàø¨x4µDaú ‡åsSÐëz ¦ŽKÅ€ó̸@:PR¶'# ј6½6PT Ÿ71áúWkáÈ'±hÜ0Ö¹iø ¹ "‚˜ôLŸšŽõ¯[pu-$ÒŒ+îHDinm‡æçX0¨]γÌÉQxy`-ÝÏ…·„|À4@¯…¼/¤²èYbdÏ*s„_u¾?9>-#ñp“dØçp”ÔÆ‘Á4nƒë’í³8k^ˆÁ¥ÉAj£Œi_¿ØÅ>·rš™pU×Z8eߦíÉH¤¶ŒÇ«Žóÿ÷ÛÝÓ+ ç]Ÿ€÷nŒDªYP»±_~™Žrœ7—ó½ŸÞp0§Œ5G€âôÐ`N º-8ë †õIÁŽyޝת{#Ð蔥aj+"ÄŒ§>ªÌLÂÝI‘øhl:ʆYиa¬ùéÀ¬$åÌŒtû2-#ÑçÃ4ì•„^ML¸øÉTüýE‹àÊÇ’±{N–<‰ÄÆñúŸ /Q¦÷~Ð&`æøûBáj}!•EÏ#{6à€i€yÉ™“ãâÑ29£:G¡þI8hŸÓ—ý_®y*i8*'ÆÄãÒ„Hô{/{¦¤bÆ]fÔ¾)‡‹ÝÏáºD"å’Dì*JòS1¨‘ÿ÷N-”;Ìÿž¶»§gÌbÂYÉØ6%sˆ@\½8üœ«ÿys9ß«0ÿœ0ÖŠj… '§'cÚS´kÔHÎkƒ>Ù©8ZT ‡Dã?ã±yf2žª‰®7˜qi§jA£º±øq^-” µ qƒ8Xój3ƒMOÃE n›$¶ïçï‰; ‡¿ˆÅùIÑ(˜©,?öy,šÕÕý-ó¯7£Å“É8á´ÏcÃcѬ®ßÏ­ü¹yýXÝÏ…·(Ó{?.` ×¾ÁûB*‹ž¥gìÙ€¦æ%gNމCËähOOÁÈf´Š>¶à ã°yF¢0sÒ€y)˜Ý.Wž‹›FáÁVfĵLÄÁB÷s8r“ñAC3ì—Š­]"qε‰Ø_à8ÿ{Únö¼‰¤‹°· r{ýꛑù^ªîçÍå|o¸€Yìy#‰Mâð{žÃòüdŒºÀ„–Ï%ãdn2†žoƽ÷E¢É­‰88.W׎B׋̸§w Ê RQ6$F ˜óRé ŠÁ¦¦`õ½fÔ»#GìÛ=üIÞé’„Ÿ[мž?ÌQ–ûÜ‚æõ-ºŸ o1FÀd_HØÒ®/¤²èYzÖÈžUü9Ü/Œ:ߟ‡–ÉQ(ž™Šm]#Qÿ¦,¸?Wf%ãäL{Àü2Û^ŠDJãX,›¬¼oÿ›‘Hk™€ƒîçð²‚TX@½ÖñÚÂŒ‡ú*Y rþOõ°ÝTìy=–s,øe®}¼ÓðpŠ/KÑý¼¹œïýô†v3?%$856·ÆšpmÛ8,‘„㱤K4ÆD û³d ??µ3Ã$&ÜÔ#§g'âõÿ$:c'(ÛP¦Ö¹)À´x´‰@θ” A“„H ø '&`@SZvJÂߟYм^ ~˜­¼ÿØgö€i€óá !0 Ðka_Èà}!•EÏÒ³FölÀÓó’3'GÇ*s†2Ï_—ËjGࣜ`F‚0G$ã·ÎH¹(ÖY)8>!o_(ˆ¸(幟Ãç§àØç4‹7#--ÅÓìûu˜ÿ=mwÏë‘1áά옘„¹÷G îË¿™ÀH¨0§Œ5G€¼äaß XtºØŒ³£"&Ôi…7ßJÄqûëGÞ‹F-1£ÏçÉ@^æß`‚Ô·`ãåõ²ÁÑJÀœ“ ÌNÄà–&˜-‘˜òUÖv‰F«d˜L¸ø–Xü2#Ç>Qæ,åýÇ>Aóú1ºŸoQ¦÷~Ð&`²/ľ†}!•EÏÒ³FöliA°ãs¿0ê|r”E ˜Ó“y‰øâœgÁ†9ÉÀŒxÅŸ_$ãôäxôlaB”Ù„º£0èå\o­Ýq*×õ޼d`vzÖ¤ÜCûu˜ÿ' w¿ÝݯE"¹I Þ¸ÒŒxÔj…Ÿ&¡ÜçÍå|ï§7´ ˜¹I$L ù€i€^ ûBï ©,z–ž5²g•€ù™_p¾÷=¯F õÒ84ÀX¼šïýô†‹€9%`¬9ÌK$aŠ0½÷ƒ6“}!ö…4ì ©,z–ž5²g˜˜—B %`Æâ ÆâÕ|Œ€¹hö;È~õ¡3bÍ`n SB>` ×¾ÁûB*‹ž¥gìY%`~êœï}gO¤^jÁAŒÅ«ùÞOoø0³_}"rF¬9̉'aŠ!&ûBì iÙRYô,=kdÏ0 0/ ç{õæd·ø0gÇ‘0E ˜î}âŒ6“}!ö…4ì ©,z–ž5²g~âœïÃÅŸþyC»€9+–„)!0 Ðk %jd_Heѳô¬‘=pÀ4À¼D4œïõ˜7_ÛÜ}s¦…„)Æ˜ì ±/¤a_Heѳô¬‘=[ZlæœïÃkŽøíês÷$·d÷x°JÀÌîñ Ëõ¬9̈!aŠ5G<úÄÃLôZˆÁûB*‹ž%Fö¬0‡úçûðF ˜þyC»€9=š„)Æ˜ì  ûB*‹ž%FölÀÓóÑp¾7RÀÜ8Ö‚“S¢€i$Ü81%›Æ'„vÀ4@¯…¼/¤²èYbdÏ*óc¿Ø86†ó}˜rbJ$6‹óÛ.æD·¸˜Õ×Û‘Û û'˜©‘$ÌØ?ÁŒùWyô‰3šLö…ˆ–}!•EÏ#{6€¹cnsÎ÷aÊþ fìÌ»ÔX³¬´76ŽÆéÉÀ.œžMc¢qä×^˜ì  ûB*‹ž%Fö¬âÏ!~Q¶ö%lÅù>Ì8=9›FGãÈOÏûí ó+·d÷hë0Ûº]wWáuØ6.å“Í yNO6cëØhì.iíÑ#®0\À4@¯…hƒ&kz–„šg ˜Ø>» .ǶqQœïÃe¾Âîâkò…f³|çì.iÍc-80Þ„ã_™€I$”(ŸdÂɉ&o¦1ì*ºÁçp©]Àd_ˆTEվʢg‰‘=[Zlì7å[?Àî¢k°yl çû¥ê|ƒ]Wä l¬]À¬ ÌÚ;ó¯Ä†1Jׄ„¶‘flcÁŽy-íÅ}—F ˜ì …/ªö…T=KŒìY%`~0ek»`gî%Ø0:F÷ù‹ø1ߎÁ޹Ípä§çTñƒ‹€9Á-®¦ûõ‰6,ù~úæfýË’ïè>&Oh0Ù"•¨ÞRYô,1²gÕ ˜áNv÷;ªf îwè>&#À‚0`²/D4î ©,z–Ù³JÀDÎ@v÷ÛæíºÉÈT˜»Æ»%»ÇNóëmXò}§€Ù_÷1yB“€É¾PFó¾Ê¢g‰‘=Ë€é ˜¾Á€‚0`²/TÓѼ/¤²èYbdÏ*þüœ×SÿqÌ„“}!O°'ä¡0Ãz×70ƒ ¦o¸˜ãÜâ:`º_Ÿhƒë€©ÿ¸Ü¡MÀÔÿö¿QácÿÐ>`êŒF‡Þõ Uæï3ݽ“?Ûè>&#À‚0`r²ö'iÿ`ÀÔz×7Ô ˜ï“3à:`ê?.£â"`ŽuKvûæý×'Ú°äû~N³Ÿîcò„6SÿÛÿF…qüCû€©ÿ1z×70ƒ ¦o0`† ˜œ¬=ÁIÚ?0õ‡Þõ uæ{ä dw¿ÍÉŸ·é>&#À‚0`²/ä ö„üCó€i€c4:ô®o0`Lß`À A0ù8Ç|ŒãÚLýÑèл¾¡nÀ|—œ×SÿqsŒ[\L÷ëmp0õ—;0ƒ 'iÿ`ÀÔz×70ƒ ¦o0`† ˜|œã >Æñí¦þÇhtè]ßP7`$g »û­Nþ¼U÷1™êsçh·d¿r_Õ“ûÊ}×'Ú°dMߪsM_ÝÇä ÌàÂIÚ?0õ‡Þõ UæÖä dws ˜ÝnÕ}LF†3aÀäãOð1Žh0õ?F£CïúfpaÀô Ì„““µ'8Iû¦þл¾¡nÀìOÎ@v·ÖN³µîc22.æ(·¸˜î×'Úà0Ç-~KÖôý—-ë‡è>FG´ ˜ú÷KŒ {Bþ¡}ÀÔÿ½ë ˜Á…Ó70Cç€éŒrGSÿqV IÀ4Àí£ÂÇ8þ¡yÀ4À1z×7Ô ˜ýÈp0õ—Qq0Gº%û•{æ½×GwŒÀâïûâó¯x ]Ä?ä=‹ÁÅ/¡ä»lÞ:ܯkêóèoý±xõëø|~7ݯ‡7tx7nyäòé4ðnÝÇtf½ˆ’•¯ãðƾA½¶ZLÖFð,½[3¼êóèo}±xõkô§æþ| ‡7fýú†tÀܵyuÁW+‡`éo_ã—]?•ùiç÷X¾eÆ-ÿCŠ»b׿a ˜Á¹Å¿ËÖƒ _ÀW+ÓcZ{lÙRôvÙzíúº}œ£²ôxIwÕ ˜}ƒÊ.ÛÛôgÐýùvP¯qÈÌ£;F`PQäý:?ïüùÖ9TÔGwŒ¨óèoý0¨ðz,¨›A…/àèoý‚:Q‡[À¤wk†wU ˜[ú£›³éO½ü¹9;h×ÙEÀÌq‹ë€é~}-)ù®7&¬Œw®!AdüŠÁ(ù®·O×J›€©ýíý’•¯Òcºxì#”¬|5(רíã•ìGônÍðn¨Ì’•=èOÝüÙƒóL .~ ‹6à‡?V‘ ²hS†•t«spñ‹ô˜në€Iï†Áö®º3;hП:û3H×¹zÀüãK·d¿rSÀ¼ÇãúZÒ?ïY¬Þ¾kv,'Adõö¥èŸ÷¬O×J›€©}„ÓÙcAî„i0ƒw,ônÍðn¨LúSg2`z¦onVoÿ–è@ßÜ,ýfnïÓc:{,ˆìª=ÎQYÁ~IïÖ ïª0{ úSoç:‡tÀ\¹}‰ªtnlÁ£ <¯Wú.®J¹ñqû[f »†HH¸¯¯X¨úØ+Y€™Ÿ>€Ë&Â,Hiq^/) ÷Q“&=¦£Ç0é]z7(žÕ#`ÒŸzúS·€9Â-®¦ûõµ¤on–o[¨*Y-¸|T®çõÖ ÀU)—`ÀZ·o„ÖµÏG·oKT·#K—½„‹âÎÂ]DÞÚ‰øè¹Æˆ=/£7¨·%`z­´ ˜ÚßÞ§ÇtöXÙU{œ£²‚ý’Þ­ÞU7`¾4èO½ýœëÒsÙï T%«‘—œ‹ÅÅÐø’ÇÑ»ûÕ87Ñs­æxxü ,Ý4/\AÔ®EÿŠ;éq\Ó(&‰Díÿ>ˆ+аì÷XT˜‰†-;â­Î£öy×ážË•÷Õ¾½–ÏÆè¾­Ñ¤V$DLHlv+^û:_ÇÖ|Lùøn4¯‰JCÓŒ˜h›e¿»Ý—#…_\Šø]1ë7û²Ÿ{ã²øÆèº´XµóT“&=¦£Ç0é]z7(ž--È~{;hП:û3H×9¤æÒß‹Uå™F\6r?Œz‘ɸô•0·t†ukŒØ¦Y˜¼¹Kí‹+S.F¿Ÿ‹±dõk¸*õlÜóYò~ÌAŸGÎFlËΘ²¹_fàœÈœ÷À \4 Ö½‡[ê4G¯5EX´$ M’šâéIc‘÷}ú?rRîy%¿cáüÇÑ(¥žš:sõÃc-âÐèõQXàa_ŽÇ°¤t2f¬™‡%¿céïE(š~?þ“vý¢Þy2FÀ ÎczLGñ‘]µÇ9*KGônø{7”&ý©£?õ ˜_¸%û•»æÝ××’¾¹YX²µ@UžndÁe9Ó± ¨=ê¥^ƒ~Q–/(Ì@ý†bäú,ù%W¤´@ŸŸò0}àH¼®'æmQÖ[üC/\žÒ¯¯ÊÇü‚‡pNòUøS¾²ýuqsfèù]>¯ŸŒi+¦aÁÖ,Z7ƒŸ«‹Øëz£`ëŒÌ:é½¢­X²5…%o¢çø1˜ìa_.ç·™˜ðé}¸0> ×¾7 TW"1HHN@br“c×Ï~=Åù¢^ƒû1¾E¿ÀÍušáÕ¹X´~4úu¾M×E£V—ãÆëRsÝ;ÈÛ2ƒþ—‚†¯Àü*có¼/çãXðý{xú–t$\pžŸö¨|žŒ0ƒÓ¢ÇtôX;aÕúB*K޽þÞ å€IêèOÌ3Ì…[æªÊ“,h•3ÅEíp^£0¦,¯òóÚ·pyÊEèýã,LêÓµîÊF^Å66MÄØ¼Ï1oÓ\å·Å¹ +·±ð×¾¸©NS¼¾z¦¼Û =‚O¾Ÿ……[æbîðH¼î-än™ŽáOœ…Ú™ï¢À¾Íü¼gñøÀ1ÆÃ¾áë_?ÄÃM“páÓoaÚFuÏO†˜AzŒCéè± >²«ö8Geéñ’Þ ïª0ß ô§Þþ Îuv0‡»ÅuÀt¿¾–ôÍÍ‚ßf©J§F´úò+>„ó݇/¬Êò*?ÿÜ —'5ÁËËg xig\’Ö~ñ f®ùý;7Fb‹'0vÃ,ä=€sVncÁ/Ù¸©NS¼¶j¾êÝ Wda̺™(Z™G[FÂ|EwÌÜ< ùó@ÝäfxrêHÌ]=O¶J@ã×>A¾‡}UÃLÌÖ‰fbÈÂᘰØÎ7#1o£zçI ˜Þ_«P˜ô˜ŽcÀ¤wéÝ xV€IêéOÌ3Ì’Í3T¥c£´1ùâ¼F÷âóRey•Ÿ­CÑùº˜â.Å›«'câ—÷£e½ˆD ­Õíx}ád”lž¼ÜûQ¯aå6JÖöÆu.Ä«+§£øûl´¿*‘±Ho~²=„Iqhõámú #Ü„&iS<Þÿ,¾üe:J6Oq»¯J&á“ÇÓª\#…†è²tšjçÉ38qè1=ÄGvÕç¨,=AÒ»áï]uf¯ AêíÏà\ç˜E›§è“û –¬éí53òG¿Ü¬Þj1#ïq,YÙ]súä>£û¹®©ôÉ}&(רµ}ª‡gé]ý ¦wÕò¬âÏnAƒþÔ¾¹YÀæžA¡zÀÜñ¹[²_þ¿ªóåÿ󸾖ôÍÍBá¦ÉDúæfÕô>×5½¯{8 ÷5¬©è}ÝC½¯SM¥o.¦W3ãWDôþÅLô>×5½¯{8 ÷5¬©è}ÝC½¯SM¥on°ù à"`~æ×ÓýúZÒ77 ¹ÇÐûS0Ñû\×Tô¾îá€Þ×°¦¢÷uô¾N5•¾¹ ˜^̹Æè“û –|÷Ž×hÓÁ NOHïs]SQzlÁë„-YÙ-ÌàvÜô¾†5•`zWÝf× Aê‡0_ !0g¯It on–O×J“o‘¡?Béì± õ„\ö…TV°×5Lz7T Ù€¹éÕ AêìÏ ]g󷸘î××’¾¹Y˜fý”è€0½¿VÚÌà<ÆÑû\×T‚ùÇåã•¥Ç#H½¯aME·Gú3ÈSïëTSaÀô2`N)ª!ƒ0(ûNtýzˆ†ûø}½O–¸uýÑ¥ÍåxêÍ6x¶`°Æçeh ˜ô˜ŽcÀ¤wéݰ ˜ô§ŽþdÀ­ZÖÁÅ™·àž§nÂ-·]‡Ÿhˆ+†ôÁWë ×}gW¾vëE¸â¢æxá›1ñÇxèŠVxceo¼p×uxë;íÎËÄuƒƒúp­4 ˜Aú”Méè± ÞQ©öi[eéq‡ˆÞ ïª0_ ô§Þþ Îué€9á×÷5ch¿‹ÑzL?LøuÞ}©þoJLøîyÜ~õõx¡{s´ß~~OÜv :wsüù¿xò7ë/{—ÝÖ#~í‡73›ã‰â>x;³!îšÜ~é—ïn€V-àY0áç·ðÚ›™x¥ksÜ9±?&üÚ¯ßÓ¨òµžwã¾kšãù%ïaÂ/ãÁ+ZâµU}ñúý— ËÒ÷4=75)`Òc:zŒ“Þ¥wƒâY=&ý©§?u ˜CÝâ:`º__KúæfaܯïjÆ#®À5CÞÂØ_âóQ7¢å-­pÓƒ—¡å×ãùîÍqÛø>÷ó«xì¶kÕÍñçëðògnÖ_Ö—Ýö8¾øµze6ÇcŽñVûsqÑ-ኢEÇNøhæ=øïÕMqÕ­âªÏ¡Oæ¸cb_Œûõ]äÌv|í Ý­nhÿ¶iŽ‹[´Â«+{á¹»®AÏï´;/ã~}×0½¿V¡0é1=Æ€IïÒ»Añ¬5?ãTùÆîÀ¦—ƒý©·?ƒsC6`öÏËÂèµý0æ—þš0ú›ÇpÛðÙZm¶¯É˜—>Ž6wÔ|ÌÆ˜Ú÷Gè1=ÄNXµ¾Ê vÇÞ­ÞU-`e=i{ ØØ=(П:û3×ø¤í%X 2ŽUý%"ó“’.ñý[½¶F¼þïÜ„çŠ{k¸5yï ¸rßÑt?ÃW÷ÂÇÅ/ `j{ÿ“âçè1½Ø§k¥öĬ¾ÐÑ /cHQgz,Ø+쌣‚ÓrÛRYÁî¸Ñ»5ûªÌ¢öÃ÷®~ØØ-(]ßþÔËŸëƒs÷®~Ö‚ÌAÿš,”&¶Œm¶Rü>^ÜÃV÷ÀðßÀ?õ$*2üÇ7ðÙš×0ha|TôvmxÏçë¤zÀ b_hÛÚ—0¤¨3>^ô= vÆ®_»íNJÕúB*+Ø7z7ü½«¦g­…™m·.~ì6vE°Ø¶¶‹ÝŸ]èÏ ùó¥ ]ßß¾~äຂv­ÿ5™0‡¸ÅuÀt¿~08¹m0¾Yó&†•¼ˆþyYè›KÔ¤^†¿€’U=qô÷üºFjÌ`÷…Nnè†oV¾€aÅé1­rÚö<°áB¼â´íyl(i_æ¶¶Á€I´@‹€É¾Q·}! DÏ5†gm…™_m[òØ?z‡:lÿæÑÖfŽpk*%`rKv÷Ûæí×'Ûiö/¤°/DÅm_H#ѳ$P‚áYû‡¡M­xâ6<B<ñçòÇOY 3×{üÐÀI´@«€É¾ }!DÏ’@¦gK‹Úžm+jÿçÞ•2‰[¬y꤭¨ýö-‹:¦x6TA°íC·¸˜î×'Û>Ôôßxf_ˆøÃûBŠž%þ ‡gmÅíÚŠÚÿù׊'Naýs Ä‘=+ž8e+j¿½´¨íÙg4“³lí‹Ø1¯6ŒµÀš#$„°4cÃX ¶Ï¹‡¿ÊS„}!â;gì i,z–øŠ^žµ·kh+ÌÜ´mÉ£ÿ”Ûô5DN[;cë¢GʬE™¿žñÎe…Ô˜»‹ÿ‹Mãcq¨älœXÞXs! !Ê¿»'–7¾Â:Ø4>» .CùÖw 0Ù"¾àU_HcѳÄôö¬â×öS7”´?røÇ§€õI åðOaCq{ß?ì”d¿à–ìîmœf·ëîÌo‰íÓRpzeà» HˆS¾ú|l›š„y—zôˆ+´˜"ì ïðº/ѳÄ åÙÂŒ»l…™{¶-yôŸ£kŸÖ?KjÇy¿/êpÄVÔþÏÒÂŒ»|7JóÈYØ4΂Ó+«Ï'aBùª&Ø4>ÿü˜e¸€)¾ñŒO}¡ ‰ž%ž0¢g•»™/­/nÿçÖ…>ø}'œ¶ê€ˆú”ÛžÅï:)Á²0sO@{U ˜ï»ÅuÀ¬¾Þ®Â˱¿ 6°ª 3öæ¥cûœ&}âL°¦ûB¤:~õ…‚(z–8ctÏŠ(As]Af[QæBkaÆ©ß>ò÷ÞUOàà÷pü—§q²ôi”Û²€õÄè”Û²pÚš…ckŸÂß?tžŸÚºðáÃÖÂÌ㶢̅ë 2;lµæÆqœø¦>°²1 3N.m€ã,† ˜"ì úBA=KBͳŽÚ²¨cе0³­­¨ýp[qûŶ¢Œ]¶ÂÌÃÖüŒS¥ ÆÆšŸqÊZ˜qÌV”±ËVœ±ÜVÔ~¸µ¨ý#ª~¸Q+`ZsXш„!åËÂ6Òlè€ù¯ŸÙª±ÜÒIôlÍ%T=KQ^IÕ€¹¼! S¬9S„}¡š„ª}!EÏÖÂųuF)ó=·dw¿­JÀ¼ù¿Ýý¶jŒë!À²$LQ¦{Ÿ8£gÀ¬ûBáCPúB=>ÔÏR”[ù0Ýñâ½|{ SB1`:Š}¡Ð&(}!ƒ‰ž mj¢g)ªŠJ 2€ßßu‹Osiýà™çñ›Ðàò$Ì™un`Û.ª6I1˜ž¯ÿ1ª0ÝûÄ™ÒcLŠ¢(Š¢‚(Uæ7ç†ϤGà‘ÎÁþüºØŸ_L¨ž˜p]:ö. `Û…éJÀÌÓÿÕ†“¢(Š¢(¯u¦€¹hê3.;—.;˜Kê…ϤG Ó§u«,ÛÞ#1ç%cý×õ€Åu±±w®M7ALf\puJf+ëýóy<6IƆ¯~.®ƒZ˜!"ˆIÅô9u±{pî:× “˜P·efMSöytT".j–„qÇ nýÝχ70`REQåµ”€90`¬9,®<“NŸœóïϧ‹êàËÿšqÁCµqdq]ŸŒVñQè>°þ¿½ûŽŠêÀßþÄ^bŠÆô¦ÑdÓL²IÌægÖmYw-IL‚ƨŠXb‹ÔØ`f+Vl¨ÒA…)Økl€X@l±£4 Éóþq‡Wp‘ Ì0 ÏçœçĹ3sû‰·ÍŨVXò—zhöv3œM} …3Í…r£òÝr¯ã›+G0cCIDStkä€ïƵƅèÖXõ4|±1Žn| Åóâ){¼ðI¬_ðˆÍ×GU¢̪ï,˜DDDuXzB/ grµ“*€þч"ÿ{ ¦@žvþ¤G}d|뀖ï7ÃåTåó%áñš‹VD=ŠÂ´¦²6(ï•{× Ÿ¸9aMÌ£È∦¯5Åó8~_×»9aAÄ£(mˆ§ÜœcûuQÕd„Ê=í,˜DDDu˜R0'U;¡¤¶y(ò]s{üglK[Þ Ç–·Ä¡à&øö;¼éÕ ·RÁ–ÔÃsŸ·ÀõÒïÄ5Ãg0}is¡tCÖzå½Â`<ù´ùu¬¹`F?‚_>¯;4q³GS7{4u³C}gGL[òŠç5Äs»bW’í×EU£̪ï,˜DDDu˜E fÊ#E¾kn~ºÖe†µÆþ¯ÐêÃfÈOiô~hù^S\1¿_²²^sq²ˆG”Bù”2“•÷.wBã'Ý•üÓT)˜ëA¶Úm>hŠ«¥ÓXß s[àʆGP<×Ï?îŠ=‰¶_U &U™E æÆÖE¾kn~ÚVå†e t€kÇÆ¸¸±5n,jˆW]à7©.¯mŽE]íÑôÍÆ8³¾5®‡ºâYGLn‰‚ÕM1êY;Ø?ÙYI­è&ø¤¡#BW¶Æ­åðAcøLhŽók›#¶#š=ë†ÃI­ÍÓ{l¿.ªL"""ª2¥`N¬v2BØÐê¡ÈwÍíÑOӲܰ ?:¡AÃúˆ\Ó XßY#àv±ÃÓrEò*óg“Z`ÍßÐXn:a´Ÿ ^¾!²[ Íøš=ì8!|MKäú» kk;ˆy±–.Q¦Y<ÇE)˜ñ¶_UR0«¾?°`Õa-˜ë[2µ4,˜DDDTeJÁô¯v2BHnÁÔÒ(³êû &QfÑ‚™Ôœ©¥aÁ$""¢*³hÁLlÆÔÒ°`Q•)ó§j'#T€„¦L-R0«¾?°`Õaé ½€ìŸªŒPâ›ÔX®¹à»—íÑÈN õìðl§úX>·1~o‚[  £^ëç†ëe¿ó“#š?Ý91eÆ冡-âêˆäÕæa1òT¿ö#‘z˜ª|.[íÏ]ѳAÙ÷íðh{GŒóo„[æiÔÔüX3¡rOû &QfÑ‚׸Fòû*W¸7µÃ[½\¾¬./nˆ•ÿ¬‡†mêcçÚÆ¸µÀ¯Š@œ0sþíï]à ºèÛÊ4õñdÃzx­‘zNhôÿ‡ßXÓ—Ãár˜+ú7³CÏݔ׫ázlc Æ Þs†%®èéfï'+Ÿ?· !âú;âq{ žÔ¿ÇÕÐüXy³`Q•)sBµ“*@l£ÉÙõñ‚³Ö¬(3|+FuqªEpk¾3:¹ÖÃÈ®öhözœ\§|æêx4Ú9ëJ¿ç†­ÝíÑ®» ŒŸÙ£E\޹cz®ðha‡¾?»•^²¸zÿŸ ®¬vAO7{øjÊ~Ï Ûÿe—WàLLÍ̵£̪ï,˜DDDu˜E fŒ[Ídµ |´êàˆ 1®8µ¦üû·B룓›bCÀ½™þóCC”ĸÝ.tQ·Ç3°¥=†jÜP¬uÂSnˆ]qÇ´Ö¸(…nrÃrÃÏuD¿± ñû*sÁ ,ÿ½¢'´mìýêš™k‡“ˆˆˆªÌ¢3ºaåÖr¬þÆ=_²GS;´{ÁF» 8º!nÍ«N ë!aeCüúƒZ6u@âò†¸:ÎÍŸ®œHeù“ñÄãN8шpÁÈ6vøçhWü^vZ«À£¹úNr½=l+Và€¨¥ ðèéfßÀòóws–^lP±+k`~j ,˜DDDTeJÁ_íd„ °ÎµFòûZW¬.ózµ Œƒð’“ÔS]qk®“RèV¸k]°¸³ÚuwFîØzhþ´rÖºë\ú¡ò3ŽÎN‚N{4ø“3.F•™ÞªÒBçr{zaÎøúƒú8å ¬tV f@ùy,šîˆ¶ ·þüÔD”‚Yõý“ˆˆ¨³hÁŒr©‘\ð³‡ÛÓN8±¶ÌðÈXðŒ Ów P<Ç\è–+ïÝœí„w]ìðýçõÐì)'äD¸Ëëã‹&vðßg*9>ÞO¸ÔCÄ’2ã wV ÝÄÛÃ.rÀç~ ð[” °¢¾R0g”ŸÇíÿ´ƒËËN8é‚[VžŸš &U™R0ÇU;¡D6¨‘”ÌsÂÇ΂.ÿrÂæ`gœ u†qp=´¯oñš¸5ÛQ)tËnçà—öp´wDΚ¸2¶Z´pÀöð2ã^YÍ݆:㷲Úۡ¯¿ùõZg$ü¥–Î7¿^®ÌïrÆå0gœ›S îõðx=; žàŒß#­n¬u6:{$,+óNù¨¹Ð­®¸·ìðh7'Þ1Þ´íàÚÑ¿FÜþžGs;ôýÉüz©vqÄÉ5æ×ËœÐÓ¹ü³)ig¬›æqZu~j(,˜DDDTe-˜õ™ZL"""ª2¥`þXíd„ °Æ‰©¥Q fÕ÷L""¢:,=¡püÇjçÈ"'Ü wV3µ-7ÃptIƒ{ÚX0‰ˆˆê0¥`Ž­vN­{——Ú«˜Z–ËKíq&æÅ{ÚX0‰ˆˆê0KÌ¢½ýpt¡#~[YgjK~[YG:¢`÷W,˜DDDT5–*˜8>gã: w±#~_i0}~[iœEŽø5ñµ{ÞX0‰ˆˆê0¥`ޱH~?ú~M| Ç;áJ˜n,³V0S~_a‡[Ëíp%ÌG9ál|ÇûÚX0‰ˆˆê0KÌÒíuÇ™˜µP¹û˜yx’9ßY p*êìî{ßû &Q¦ÌÑL3ÞûÏåª>ÞûÏ6Ÿ§1,˜DDDu æ½…³jaÁ$""ªÃX0ï-,˜U &Q¦ÌQL3Þûý; æû6Ÿ§1,˜DDDu æ½…³jaÁ$""ªÃÒzÇF1UL…ó˜¯-,˜DDDu˜R0G2ULÅÓöóõ …“ˆˆ¨cÁ¼·°`V-,˜DDDu æ½…³jaÁ$""ªÃ”‚ùƒÍR”9ññ 1qÁz¨õ|þí¡Á›ÝÜÿþí¡±ùƒ§n25„gðÃÌeޱúöeÁ$""ªÃlU0#cÃ0kÝAìϽÁÔ`f®;ˆÈØ0L"""²[Ìѳ7bã/—°7ç:SƒÙ¸ïÆÍ[Ï‚IDDDÖ£Ìá5/m*v-Âîãי̎£EðÒ¦Z}û²`Õa¶*˜ª@=v-flU ž“ˆˆˆ¬'=¡ptXG¨Ç¶#EÍàö®øÓœ_+ÿÜžµx³ñ{˜¼ëÇŸy{¼ˆ†®ï`„ášÅçýötŽ!``W<ê"qDÓ—¿Äˆø‹ØjÁi¨õVß¾,˜DDDu˜- ææÃ…ÍÀö®x}öÙÊ?·+o4~wÜãø÷Æ£k‹NðÞ˜gñù.›äù=иU/Œ‰ÉAòæ˜øy;8uBTºå¦Á‚Y{øÇêî;xÆ”D¯Ã“<òýã•LŒóÈŸ’¨>>9ÑSç3 «­ç‘,Ã3ÀØ]­5Îð I;ìdÌWiõ%Þ:c¾_HÚqߣN¥I¹—m'e~0¢L^ªä;Eä’ˆ4»ße¸G¯‹H†ˆÔ¯ä3íE™ïáw ÿTDv‹ˆÃLÚËô©ˆEÄÎ ã&zðÙ²`š2 ,ší]ñÚÌ3H‰ާ^…±ªnxÜÍöÍÞBïÐlöïÆ÷ADàØúïøiÓeD/…·Ÿl;qBË7T˜˜z¦ŒlŒòEûŽc0êÛ.hÙöø{ç¦Ê÷Zü?¤œDè˜^x¦™#Dìáö|o » ÌGú,Ÿú-^jUâØ/ô˜¥{óaÊÈ»ë´n'ë×'`ÚÊãÐg\ÅÆéÐ xÞ_‚èC–[O,˜?ÿØAÝ'%xäNMòÉØ³©G±#g3öœÞ…9›‘z$kv/( HöËŸœàyÚ?nP[Ï3ÝÏcwŸ cîðÙ›ò—n<Ž”_.bÇÑ|ì˽ŽGó‘òËE„m8^2jî–|Ÿ ÓiµÆX•m}^Dþ""Mˤ‰ˆØWò¹`‹ÈSe†³`ÙšR0‡ÖxTzX4ß¶wÅk!g°!j8shŽWÕµã´Ñà¹ÉX¾¯†mkð§Æïb–¤êçã&íñ÷€Ý±¬,‰ IDATˆÞ´ãz¶GƒÓ°b_Ö¯õÅ£Ñö˜yÉÛãða«·ðƒñ6&NÆÓnoàÛmÚ…Ÿz¶E“¿Æ#ñP6DFûÆo¡ßâ DÅE ïKnxÊw/’+™VEË«{ö"]1$á’EדR0­»}Y0­gRœçÜ©‰>E±WbÏé•fש­ˆ=¸S“|ò'Åžiëy§{ã«3Î6ksÑÚM§±ïÄJ³;»k7ÆðY›ò½µÆ?ÚÖçEäÍ»¼7@Dvˆˆ“ˆ4‘"ÒGD6‹RæN‰H+ùXD›‡D¤­ùûDd»ˆL‘LótvŠÈ8QŽœž‘næÏÚ‹ˆÊ< "²OD^4¿WÕ‚yED–ˆH¢Ü.”wÌŠæÕ©Ì2‘ "òWóç‘["ÒBDê™Çõµ(e±·yü." "òÈ]–ûßr»`¾*"çDä£J–…¨v±eÁL9oÑôÒƒN#)rkòwü¼Užᇶí¼1wW>R¶®ÁëߟMyÿñu¸½†ÈýÊç6š–âµÆo`hÊU$®ñA›FŸÂÓ5eüÛbñA«·0Bwecņ“H> Ûaú·Ï Á[«sà溷G‹%"î@>R\ClÔ2Œ˜sË*™ÖÝ–gÃöƒ˜ôÕsp~ní±ÜzbÁ|xMŠ÷ŒN[¸%׈§¶T9[rÐ¥Ž*ú9A•`ëe ªñ 6Eü´xWá–ÃØu¼¸ÊÙr¸ï,ò N«l[WV0Dd›ˆô‰¥d•=Ú÷¨ˆ\‘^¢”°¹¢”+GQŠÖMY*J±êh~=VDÜDd¼ˆì7öQ âÇ"ÒÚ<žpó|Tµ`^2ÿ÷´ˆüÇ<¼lÁ¬l^Ë.S°ˆhD)„D)žï‰rd÷¢ˆ<-"Ï‹ÈUù‡(%{¹yýØW°Ü¥`¶¥öªd9ˆj¥`úÕxTz¬ßwÍ¢qÒt§1 mÛ©1k‡2<>bÚ=饼޼¯5~cMç1§;ˆ4@ÃFMÌiÇð]ôEÄ­öÁãmU˜iÇú-1øsË·0,%ëwÂøþÝñ|ûgоã_ðþÛ-Qÿ­UˆÚwSþÒOzïAB¹y«|Z·?w‘QœtÉæaIÉZ<Óà eºZOJÁ´îöeÁ´¼É‰ž:톅i'6bÛIã=gKn*4)?äMŠ÷œfëe¡Êùucæo/4ÊÇÖ¬¢{ΦÌü¸pgžoénÛúŠüïõ—gE¤¡ùý΢¯_E)H"åËØ Ù(·Oÿ¶0¿÷¨(Eë²Ü>íÜA”‚ÖØüúUQŽ&:›ó„yxCùÙ<^‘{+˜D¤‡ˆœ1O·lÁ¬l^Ë.Óÿ‰Èó°Ã"²Z”#™o–™ßÑ"Yf\íDäš(eóÎåþTDÒE丈D O•S]cË‚™´÷ªEóÍ“.è¤=‰ØÕCÑöI/ÌÜ® /÷:m^kü.F.aɈNhöI"KDZ+¡+w!b÷UÄ„ûàñv·Ç‘´)ï·| ~/béØ7Ñð…ÑL½ˆ¤½W1ã=¸½޵{EPŸ¶hñï$¬33je¾³ó*™Öíe8‡à¯žD‹Ïn?.ÚOº¾‡Ñú<‹­'̇Ì€ö“< 9‰Øœ»ñ¾£?‡IñžEþq^üã©’-¨¦Ûû§¦¸†´ŒÂûÎú_®ÀGg*òÐ+ÚÖçE)dO•I;¹]‚ê‰rZ;©Ì°²eìgQJéå2)å_'É¥‰(óð¯3ͯë‹ÈQŠäVQŽÞoÁt0Ïoˆˆt—Û³²y-»L­D)ÔïˆR4ÿ#")"â+"‹ÌÓ›m_)7QN­?SÁrjžn¨(%´ì5¢DµŸ- füî<‹æëv.x50ëV EÛvjmU†—{m Gg·×à•p 1qZthú þ°+6îÀ8÷Îp{Áó¶ç!j…7/3Žø´h¼×ò- Y ‡u„k§˜»ù2¢“£ñù+N°ï4+vå!j™7mô6¾žŸÕ’ñu‡¦xJ½‘•L«ì2D„~…f ßE¿ù‡¾> >ÝÛ{ó±t§åÖ æÃgRÜ÷ÑKwèJL9I¨n–ï.ù9Qiëe¢Šy£gÅ)I=˜êfnÜÑ’!ÁimëÊN‘‹(7e›?÷†yXÙ2¦å_)QŽzÖ¥hÝ­PÞùú;QÊ\Kó{=äþ ¦ˆr;O”Óñ¥³²y-»L¢”j(×t¶‘\I唸ˆÈQŽ`–j+Jyl]Ár*Ê¥Ž"²ÊœÊn¢"ª]lY0cvæY4}Û¹ c@.Ö®Š'Ú©¡Ý¬ /÷zó|ûf+ع|„aÉ¿ba 7:¶qQž9Ù¡?†DþŠ˜yˆXîÇÚÞGŒ1ï¶| >I—½1ÿíÜ õì¢ùó=ÑÂp¼äÖ¯ŽKǺ§<ºžjê±k‚vÕ Øt1;ÏßuZå²=SÔÿƳ­!öñøû£áŸ|Ñ¢ë‰óáã;°dñ(è³c«ä¬5ðX2^ïÓ¤Ì$ÆŠr]Z½;&½ZDˆr#C¡Ü¾‘£"Î"rB”¿à—^ÝGÉTU­¸#X¥Ñ—Äï¾b‘Ëabv^„J£/ñÑè›Ü1™Šî"o*JråH\WùVDv‰Rò‹rjýqQŽvž‘ÏD)‡?‹²-K¯Á¬jÁT‹Rà\D¤(…,M”õ|?SDd¤(ûÛQö­Êæµì2‰Ü>Úù¥yþމH‘(§ÓED^åÌîæq­åFŸŠŠuÙ}æ)É‘·*Y¢ÚE)˜Cj<ª@=Öí¸ÂØ JÁ´îöeÁ´ÿ˜]7ÍÛx<–ÊŒõC ücu/3™çE)íÊ k$Ê_ÌïKÕ æ¢œ~¼ Ê_®¥ªû(™ªzè ¦J“ÒuTèö5U Þbÿeî-JÁ´îöeÁ´œñzwç‰q߬JŸ‰ˆÌÙÕNøÁ øÇ *¯ww®`rýD¹k8HDf•^YÁl$J™ûD”;Û˜ÿœ'ÊƪãÅÍÞ<¶dUFª›™›F—LJ¼ê.“j#ÊÁk"òv™á¥³£”¬“(å+W”ß­.å"Êõ–=¥j’)«²‚Yë9ã­3Æù/?T27ùWT7?…í/ñÑ+ÚÖŸ‹r„¶tt,ó^QîÞ.‘•¢ÙQ¶Óó²C”õþÊ]ãnã¨H=Q®“íPÅ÷,9EäC©|}”ê(Ê~ܸ‚÷ˆèNJÁô®ñ¨õX°ñcƒ(ÓºÛ—Ó²üã¼è;èFؾ©X‘xßYº&Æ.ôÐþ.“²å/ô3rûô¸ˆR0+z¬Í·¢œ]Á¸æ›Çõ´TíQ2¥*+˜"µü‘3ã‹^ã íº\ÌL8{ßÑÆäB­3ª¦ïÜÖíEùGD_QŽöN‘£¢ü¡©(ÞS”kq7‹r¸tœ¥h="Êîò¿ÏN­leÙ‰²æˆ²þ;Tñ=KÍG=Qކ¿ZÉú(å,ʾZ",˜DUcË‚ºþWÆaÁ|8MJüÏj$žÀ˜o>E· lÌŠ1àŸï Æ·ý»âſĻéŽWþ>~ƒÊ¾ž õWw~þ<÷Ñ×x÷7ñì‡}ñÞ§ÿÆËòÂÐÈcðû[<û~tùsW¼ôÏEðŸ£Á‡]ÿ‹·?î7{΃wÿðÂ{ÇÛ&„–}oFÿ샎oü oÿß¿ð\ÛBµx>êŽ Ö[/³Ïš ¦u·/ ¦uŒ×»;Oˆ4ybü÷!ÛF`Áþñ˜ -Ã11~páOqçþñèAá¾Hï¬Ò¤NöÒš Æ.IÇϹ˜±KÂKg*ôÒª²­íEyžg¾ˆ 0ûQ”‡ä—jn~ÿñò_•†¢œDÄ$Ê5¿m…“¨ê2â{•üže›‚Æj™æß}Añ9õu7üŸî8‚×éñ·£ß7ïãµ±éŠÞ‚ÿ¼ãޝ¾.ûúkôé}ÇçÝ?Â[#hå¼üÆLLŽ;¯O?AÏ…™ðýÛÛèx ÁqôÑëxù…gñBw|òßÁèÒå |öï·ðêȃŠ?õÇ/Þ~ïnèøÌ'p_~ ÁëôøÛ+ŸÁsõ>¸ÐÞÖ[/ÁñgX0kñqßuñttR ‚£7B¶Ü=#1÷—Q˜³g$Bv G Ñ§dR ÿ¸AÇïøYHzˆx¤tQk G½t¦‚á¡{0&,ãVÄU9·âÆ,=Œá¡{K¼t¦µÖp¼‚Ÿ…¬HK‰åšÔ÷Ë ÷•ŠúµåZÖ¶w¼W åþýÑ8*r¯ÓóÑB”‡è—^S{·õ¡‘5¢œ>gÁ$ºI½‹oexYê*P_×U5ã½ÿ†.“³ Íƈ¯>ÁÇÇ ]?wüß|Ó ïO=íº-øow|Yîõ×èÕë.Ÿ_1º,Æ”Ø#Pwï†ÿÎÏ„w÷·ñQÀQhcá»ÞÀË/¾OƒC»n/Öaà×ÝðÁô£ÐÆÆ÷½sû½ãñI§nøzéIh#Sñ×W>ÃàÕ‡ñýÇÿ@¿å÷wÝUU£ Ô[uÛÞÊð@FB¯ë¶Þ·k»ñzwgÿ˜]'Æ?è°Ü |ÿØ%þqƒò'& :<1~°ŽÅ²vPŽf¦tUk ¡jé°JcÈ÷ÔèKTC¾Zc:¬ÖtU,–"Ê)á}¢ü¤ç7)uå!ù¥¿ÿý¾Ü¾n1@”·J=+Ê5‰wžú®l¹×‚i‰ùø‡(¿».RùúØ(Êiø›"ò›ùÏÅRþ DT‘Ìä^oüÞ&30ú”Õ2y|¼6b?¢Ob’ÿ÷xéÍxû“žxé•ïðõ×ÝðÞÏYŒÜ‚ï¸ã‹r¯¿Å€1wùüò¹xåE˜Ï¿vCyPwïŒçÞü+:¾þžÿ÷2ŒÕú£sÇñêÛ¡“{ †ôë†?O=‚ÀèS˜Tö½xŒè—_ÿ:wù/<ÛƒWîEß?÷ƒç*ë­—ÀèSV/˜7~̤Þgm½oQ…z‹òӛϊr#ËS¢Üeí Ê .çEy´SIåg7E”»ºóD)jˆòØŸ8QŽî9ˆÈEyÌUeã(û¹R÷Z0«;v¢ÔÒÇUU¶>ZŠräó Qn,Êåê§ã‰èN™É} ×ö¬ñ‚é¥MÅ´¨“˜uÊ*™¶d1ÞýW8~Š´Îø­2ÏaaxïŸ+¬>ÏÖ.˜×öÄá¤>Ûm½oQ…‚¤âGN•žv~G”G4Ý¥¼¹š‡Û‹r7u®(GóbDy(½ˆr°@”GU6Ž;?'rﳺóÑX”Ç•Þ5þGë£O‘݋̤>³/îèd©j4ãæ&Â?ü(¦¬=i¥ÅÁÞøbn¶§aÉdc˜Zÿ·êt&,?Œ1³“¬ºm/î茄^3l½o‘d$öî‘cè{µ¦ fLÌŒ ݉Ékr™̨ù;;ǪÛöxÊy‡zò·‰ˆˆêªô¤m2zÿö[¦åYc):äƒÑ³“0fIüWŸ`j £ÀÈYëQtÈÇjÛõ÷ÞÈHèU’]ñï\Q]‘™Ü{çÕ=j´`"ËǶýˆÑ³“0*t+F‡eb\x6Ƈç0̸ðlŒYvÃçnÇÈYëqr×h«nÓ+;û#3¹Ï[ïÓDDDdc‰½{d§~‘WÓYž¸™é…Äø™77^ÚT¨õŒã¥MŨYIX»nžU\–æxê—×Òz~oë}šˆˆˆl,[ïØûRÑþ5^0¶$ÄÍ*WàâfÙ|ž”„ÌÄÞWyzœˆˆˆDD$3¹÷°\ãW…Èòs÷$Äͼ£`δù<=(É1|Y”žØË×Öû2= ²õîΙI½Oçÿ2ÀæEåA fÅÉÿe2“úœãÑK"""*'=±W÷¬õŸü–18ü=SA*,˜À|Ù2¿e FÖú>E|4U(3±÷²\cßB[—–5,˜ÿ›“¦¾…‡{ϵõ¾KDDD(ó ?GÏoý¦ÄÖÅåA fùœÛüuIFbïÃ<5NDDD•JOêÑ&3©Ï¹‹Û¾)ÁáÁ`n'!.䎂bóy²U®ìì+3©ÏÉl½{[ï³DDDôÈLîÙ>3©Ï9åH¦íË̃L%¶~S’™ÔçdzR6¶ÞW‰ˆˆè!’™Ü³}fbƾ…¿g3LÛÏWMå·ŒAÈÑQ”‘Ôû \Ñ}Q®Éì³*k}Ÿ‚ü½ým^plº\0ó÷öGVroÞÐCDDD–‘žØ«{fbï ¹Æ¾…Åûؼì°`Ö\n€ú/ 2“úœKOìÕÝÖû"Õ"ÊÑÌ^^‡“ûœËIý”ÐûË̤ީ‰½Jާ~qíâöo·»n€[éð{¦íK fÕŠäoq}ÿ·¸¶§.lýº$'õóüŒÄÞ72“z§Jèý¥­÷7"""ªc²õîM2{÷ÈLê3;3¹!3©×ÙÌÄÞùñ½JÒz¡¶eIØôrsIØt›ÏSu’ß«$#±×õ̤^g3“{mÉLê3;#©Ï¼‡ˆˆˆ¨†¨4Æqe ¦Jcgëy""""¢‡ &Y &Y &Y &Y &Y &Y &Y &Y &Y &Y &Y &Y &Y &Y &Y &Y &Y &Y &ÝÕtý‡*qÜÿ$P¯gÁ$"""¢{ö?G*ïL""""ª L""""²(L""""²¨ ¦¾Âk2§ë?´õ¼ÑC€w‹‘E±`‘E±`‘E±`‘E±`‘E±`‘E±`‘E±`‘E±`‘E±`Ñ}¨Ij£Ò¤tõ˜¡ÿÒS£÷Têý”’•ri€g žcˆ­ç•ˆˆˆˆ@ã‹^Ãä¡!›¶xé ÅC‚MÅ?-Þ™°j_þ¬èôâyq™7çÅeÞœ{¸$$ê¦,ß‹1¡Ûàdºå­5\6kÓ>ß`ÓBU þÛš¤6¶^""""²÷Ezgi¬oéˆY›òç'd•ÄïøûN#ãÌ­*g߉blØ{K7f—L[ùK‘Îx}Hˆé¬—Ö¨2v¶õr‘•¹/Ò;{i ^ZÃõ ÈCù¦CW~ú¦Å²ÿäu¤ì»ˆ™ë2~ó I+b:«ÖûûhôMl½ìDDDDda*MJWß Ó… ÈCE[3óqðÔM«æ—ÜëHÞsÓÃÉWiRK|uƹªéÆö¶^DDDDd¾!FÝðY›‹âwœÃ“7j<¦ô<„DúÍ[c¼á¥5òˆ&ÑCÌGk º­hçÑBì;qæ1ÌÃÔ¿ÜðÖ¯« Ñ*qœjºþòó«šnlï`ì® 4øú†¤…ù…¤m:sÓ>¿´œ!!¦³~!i9~!i‡‡ÍÜl²)B¨÷SkŒ=<4Æm´Š‰ˆˆˆê_qî vn?R€½9ט$í¾€a³6Á'ÈO1DhüÂ7Øçd¼84$­À?lwÞ¬èÌëËSs³íWÄï<‡¤Ý ?p÷]BÒî ˆÚr« ¹˜µ.ãæø…;ó|ƒÓ®û¥]ò I ó˜¡ÿ’GI‰ˆˆˆ,Ì3PßgØÌMEÛ² °'ûú—ÇŠ—•Vÿûäe{®.ߘãÁ«Õ§ñàU,J>V2añ®•F_â”¶€G6‰ˆˆˆ,` &©—ÖP¼aï%ì>~½NÆpà*&+ñ IË÷ NKà FDDDDÕàdÔi׺±óX1êz¶)ÄÒ'nù›Š}‚M‹Ýém½}ˆˆˆˆX>}€”.j±‡*Pÿ­§Fïé`é­1ÞмŠíG‹sô¯bÆÊý>:Ã)Í$"""å!é)]ÔZ㌡37íóÒŠÕZ}ɨyÛò~Z¼ëÚŒðýkÏŠ9\n8‰mGŠ˜ ¶!ç–·ÆxC¥IéjëmJDDDd㋾A¦8/­þús¶\Y±é,RäaKVs‰ØtÞã ÞDDDDuŠg@jwoáÔЙ›òCŽbãþ+Ø|¸±PVOÝò 2ç㌈ˆˆ¨ÖSM7¶÷ NÛ4löæÂ•ÆSHË,d¬mÄ¡>ÁÆÅ¶ÞæDDDDVã¨ïã£5ÌO8Szcå¤ì¿ ï S±:ÈØÙÖÛžˆˆˆÈâ¼µi^~!›ŠÖm;ᦆ2;æH‰oQgëíODDDdQžú>~Ái »/A0Ÿ©ÁDm=o骭÷""""‹¨Ijã­3Fn9‡”ùL gý¾«Piõ%|;ÕÞAÆèàÈÌ’û¯±M¼ƒLÅ5Iml½/UÛ@MRŸ ´ÂøÝW°~ß5ÆFQiõ%|\Õ j±ÿ¸E»Š“~¹ Æ6‰Üz×`Qí¡ÒÇEFâÞ«Œ2'î†oаõ¾@DDDd*qœ.ò0ö\el”á³6çówɉˆˆ¨ÖPkŒ=ÆÎß™·+LÍ'41>:S¦­÷""""‹q_¤wöÖ Öl>˜y5ž¾í\Ð1 ·òÏéÃÑ©QŒH©¡ùª¡éEn½ï S±§ÆðŽ­÷""""‹òÒÇ/Þ]½ã j:}Û¹ ãŒ•.Õ\ø6ÖÐ|ÕÐôÆÌßyÍGgœkëíODDDdqÊQLÓÉ€Õé%QÛ¯ &óe;t˜q«Wø¡Ý‹#àûÝ'hÓÐöMßÀ¿tYX»i;¾éØ"Ç–Ý1,é…ü€×Û6‚8¡ùk}QÛ¯`õRo´}y¼¾zÍÛyàç…wçö+ˆÚ~¡âñüÏô.Ye¹'†ýrÓ'ȸÛÖÛžˆˆˆÈjÌ¿æsrÒò}7"¶^Bä¶Ë5’/Ú¹ Ãô¬Zî‡6ÍñÒÀ,LMÇO:ÀùiÌÜt‘WâÕF]0lýeDÄÍA§ÆíñɤmXœ´ ~Ÿµ‡óK“1kÓe¬ óBk‡ÆxìÓi·4K—Ý}œ•§ìô¬±ÌAë²  ÔÃS£÷´õv'"""²*¾‰oHZÌ´ü9ñÙˆØzÙêù¼­ :LËÁÊe~hÓøo½A¾2̵Uaºñ2"6(…Ï/ùæŒè ×?-¢-zÞ©z‰IDATÊçÖ$.F‡F‚GìE¬ óBk·n‘|IÇ]ÇYùxnOÏzËštÃgo)ôÖšª¦ÛÛzÛY•g@jwo)Ó7$-_™…%)g°fË%«äó¶.xej6V,‚ÇÚzbªA¾bé<ÞV…i†KX³~:6꿤3˜þU;ˆ4€«[s±Á«ø*ü,V,ñB›'<Ê£âqV>žÛÓ³Î2—fÕæ‹ÐFeÝòÒo¨5&[ow""""«S;ûê6ExiMW}ƒÒ ~ Û[¤[wKRÏbÕæKIosÁ\¦”¿©zex¹×ÉJáóM<‡!¯¢i×,.‡é(mÇ´KX¶Ø >q—q”{]ùxnOÏ2ËøGY´ñ FÎÛ^è£5¦¸/Ò;Ûz»Y*0¥ŸZc¸2|ÎÖ‚àècßtÑbéÝÖ/O9Ž¥K†àñ¶ž˜’ª /÷:i:4쌑ç°<"/5yÝÞŠyq[1¼o'4|n4†‹XºHGŸ¸Ë8îx]ÙxÊNÏ’ËZYV¤]À˜;‹¼µÆM,™DDDTk©¦Û{kéÃfm) ‰9Ži-ž^m]ðòÏDZd‰r:{rŠ2¼ÜëÔ­èû§–°kð!Ô1§s×ñ”ŸÞy«,wEYfº€ñ‹÷{k›l½í‰ˆˆˆ,Î# ¥‹—Θ?cM&–›.05”¥Æóøaîö"o­aš­÷""""‹Q;{éLù³ãO`©áSÃY°á ¼tÆBñE[ï DDDD¡Ös×fa‰þõ‡™•‹qa᜖ï7sKQÀº“UúÞÖ)kŽA­1ÜTk ×ÕÃu/­é’Zk<ë¥3m÷Òšf{j #Õcž'"""*C5ÝØ^¥1õÖ™ö©4ú¿Y[ó~Ý]T™¸*~3·¨5ú¼Q‡ˆˆˆèâÚÝKküuØœ]þ+còšÜ:“ÖœÀð¹»~So¨4¦±¶^DDDDTåh¦i¬Zc¸>|îîVÇÄÕ¹T&¬<Ž‘ ö—¨ ÅjqZ=<4ÆÕõ֘?dÖöÂ1K2á¿ê„M3nù ™µ-_¥Ñ—°X=¤Üé= ^^ZC¶—ÖX0lΞÂÑ‹30~E6&„Ÿ°jƇç`ôâ øÎÚ^¨ÖšŠÔ:ãYU`J?÷Ezg[¯""""²Õtc{•Î4ÖK« Produced by OmniGraffle 6.6.1 2016-10-04 15:21:58 +0000Canvas 1Layer 1 Network Nodes Compute NodesOpen vSwitch - High-availability with VRRPOverviewInternetProvider network Controller NodeSQLDatabaseMessageBusNetworkingManagementML2 Plug-inAPIManagement network10.0.0.0/24Interface 1Open vSwitch AgentInterface 1Provider networkAggregateInstanceInterface 2FirewallProviderBridgeOpen vSwitch AgentLayer-3 AgentOverlay network10.0.1.0/24Self-service networkInterface 1Interface 2IntegrationBridgeProviderBridgeRouterNamespaceInterface 3TunnelBridgeIntegrationBridgeTunnelBridgeInterface 3 Physical Network InfrastructureDHCP AgentMetadata AgentDHCP NamespaceMetadataProcess neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-overview.png0000664000175000017500000034635713553660046026234 0ustar zuulzuul00000000000000‰PNG  IHDRàšgì®sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|ÅÛÇgöî’Ð{ï ¢¢v¤bEÅÞ%¡ˆŠBrÑ“l¯" üí{C°`,HQi ½“z{»ïïÙdÃær—\’»ä.yæó¹Û6;å;»3Ï>óÌŒì˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜0HæÀ˜g]O6Ívçôr8”]ϹׇsZ9mà2 ŒS$úºËõhÛ·»«]ˆÍϧ9·Fb8ÍL :àaÇê—ë[ûN±¬£](ϸØ„ä´u]k‘š<+ÿ‘âÇdaM¯.„#bÌpM<`=_•ýÊòËUóÎÒ…þiª½øo¬JÂå^_ÌmBÉЫËΑ#GzÂ%奣¶”©µ<>Æùè1•ÍSmæW&¾˜ç» 2¿Z½¼S¶Æ;S_íJ;¡2aW×=µ©L­å!¶îz¨<†yÙÚcô®d¹3û•ç—¯3&À|`Ü>Ǽ˜;Mƒ)ÐÌøäG~ÊH™ò»÷½áp\ÛÊTJ±½\ÍUU{f)§¹\ƒÕêàœŸ­]„÷«·pØ¢«#>Žƒ 0š'Àðš/NA-$ ¦¤.úÚµŒ)SJGL|Q^Žw͉-/_óæÍ³U6ÿ•½Ïåz)†â-/mÞ×é>ïs5yüÔ¤I¹øJ0ø‘ÐE”ÐÔ‹ËKO ¼k2Ÿ•-SÊ7ÙgÓ¯<¾®ò¬–¸O—¯ƒû¯Âûdª?M.q­’q—úe• >(·”F?1U˜±ŸpÌÓ.×¼¨ª¤Ç ‡·L Ü TªR ÷Lqú˜@(Ààg„ÛpNŠó8˜ @9YèŒóº\‘柔z5ÌTîŒON=Mè²ìy3¥.ç+ŽèäٮɻÌ{h;ÁõTãõÈ2ìî€&qóv£“ÓÏÖtÏ Ð”.˜“š<®'8SÆ#òqB(‹|i¬©Û-õO¿»^¬¼è餤*›Ìd¸îÛƒ|æàÃ"¶žcO$#ÇLk!å¤eô˜äé}UÝ3ñëÕë/ÂÀÉ&àq+¸¼B~ýñ£kã]3櫇Cøƒóvêîø]0ùY¡ØåÚê) ?¢­ÕÅ»é(T5q Étg¶ÉZ-Tجoƒ_ãÓo¬½þÖ¥ñ®ôc…êÁ1yj¦{[¤3~—)Û}³]S7™÷Õä™]„r¾¹îmMG ¼é#0áÁ´qâ¯ÁïÄ,u[pÝ+º˜j¡Ïé爯›MÚo˜•2u•5>sŒ+­·GÕæÙæŒTçùt>eJáº\º’åN»ÏÀˆLu±Ñã‚4þ‰4¾„wå9òcu z3z »rÕ¹½z¬ÅóGÂó•º{w_<›æ¦:»ZýûÛGøQRÚïЄú³Ðõ‡ðœ|˜áJüÛŸç}¾F;SÎňÛiøÏ¦°Üní 05€êBf:ìQ7¨jþw`¾Ì ?Ö8áwŽgâÕð4nè8ã±8l½n–ž£ßç¦&cpd¡«È³aÞ Æ£’§Ÿ*tô¦é2[:ìWg¸¦l¡ðœõÐDÁxo/Ît¯o…Þ‰òÞ'…üÏÿ‡ílI³].©™iá-¨ X^J‘óP-Јõ@CyŒwdÆy©_!.ûï@l††ü3ïBø¶á8^sç/õÖ긣rmî鿦y á;Îð£Ë¶æ¹¨–2°_cL‚3ý*óI&Ekaó>ü”Ó•šÛó„¢A¦ßšÜ"톂y;dMG ¼Él(Þ™¶‡ŸÁSq,Êc ʃ¸æ¢¬n€]ÅêøÓŠ5ëR(ˈ™ªk·Xã²î«ýòVô!j¸`—)zÿ£6Èt§ÍGy< !´%òÿ.Òþ!Þ¥¶ˆÿYÒs^˜‚ÂMM(mš&:a 본w:ž¥&Ðfƒçá{«ß²ö!áÅÎN™ú+ž£ÇF´Pµèc ¬{¼¯UäùÒ¤ì…|e!ûp¤Xçv ý]üý¼ëþØw ¿gù ×e"ô^‡Ž¨z§EuëÃè:ÿš×*úl˜÷U•±1¨XW àºØå>Sø.¬[ –¢œnÇó´eö:ÊûcäÛƒ|…ªã¾ÍRàmm"ÀðÚTšœ—š# ‹ö¸§*ÂvÉœÔÄùfBH˜Ø¸`9’ã¶»3Çâü“æµÊnið³ë¡$¡uöX×c‹†Bãy¬AÔ€ÍII"A½Êޤ¹UOÆ#RØîñ ¼®ª(XÁåËF¢ïxbòäl_þ|ÓTu:Î÷„Àô^Lˆ7ƒûp‚Ÿíjj*þ©¾îƒšðÿ \4„pñ ™l˜~`îpOÞ®ƒ;p­q”]>ûœ+i½y-ÞõDs]Í} ÇŠÝ!úÏr9©Âpô1ƒžw=ºœ?gÎ åMEgÞº­Ö¿0líWï8Êã-³U'ÈóÀt¾¨ï¸~.̈Ì0F%§]+0£ðè¯a>îîO¹&ívÛ+RÕ\xN¯…¹Ç$oÛg2qVù:ĈZÙ Ëß¶²eJá<\@å}>ž¥·ð<Üf>…=FÙŸ"—'8Ón†ŸWÈ §i)f;*6å‚ÙÓ’–¸ÈÔcÉ[t‹†Óòw¸ÎÊò¤Ý…S3¹½¢ÏWFŠóY„û,>ÞW`ÛOЍ»ç¤Ý¿Á„Ò/q~—ÎJIüÑ ®@ÕÁWo†T>-ý4óçˆ ‘€”Ï[…o Ùè–ò}Úפ8…¶Áp³]Iàå½ o3Õ]ð? “º½!0L‡²6ÛwV8]vF·ïJËï{ܪ¶a-Vì1Ý¿|†«‹¦8¿Bÿu¾‹´z#I¸±7ºÃ¶(Òzµ±;h˜7úŠ’à™t^ ÇÛÖë|ŒF݆‡!ÖkBÍM$¡w½k¾Éòöž!° ½§²e÷È÷Uã »0aºétî´Û£‘/WoêÐtýnbj³ßH6üÖ»ç¦$½ 9úy<;M°À‘1¢˜´‘Ц~f-v¨K/°ú§ýEl ÿm Aÿ& ÿx_·W¥Lñ1ÙÒ>ò¤n·ÛœÖçLˆÐ¡4‰â‚<¥˜õQ¤rk¥„oíl …Jñ"Ž;Àr­H§^:_® Áóe³¸@:εƿC]<€Þ¤ù$|-4ÆãÃÑaú¡}ð8yȉjÞ`1¯Ì³a†gn+Êx¼ëÑöx¿ÆýÍñ žŸâ#L]ïH[ÅE/7'õþ Ï»¦ú|ÿ½¼ò!ˆ8,€G\‘q‚Õ€MØJhŸÌtÚmâsÚGCÙÅ<Œ-lŸ ùá‹Ñ-?F¸µ×ÐGK›¼…l¶+‡Ôm¸ö—E?]¶„Ò馞²3 2 o„üç¶±·ØZúº(ìr×e¯k—бM*/x7aŠñíx¤~¢¯ëÁ>‡l4…©È½°Í¾B÷tl_újͺM0yy˘‘fïF©¸ýðVy$Ô(0}wÖÔ©ûK݇6»ýy:ÂºÈ¼Ž¸^¢}ðÜdž3·M/<§KÃyÞ×¶²eJa©÷¹xÈÔéÛY®©Åff<ílSVáZŽ»Ñl1æys f+g§&~fWe;7Íùž1h¨õ8ŒC˜`XA¾´Xû"”¥ß$ƒç¾(!šPð†{|¡_âÔdæžAE—…زûtJ;Êø;óC¦²ÏFq˜Ø ”1âÕhÅSZk/kE—·f¤&~d «hßø8€¹Ù¬1®é]}\çSL V †•`A £móŒ¦Fí"Ÿ¤EÁudy[n®¾‚Üsˆò£|rδ$êê­Œû]ÀÇZo¤nîbI?UÕ“ ‚›ñÕšõ—CêkµÆ(Å^ÊT–¯}H×a2ƒK²D·»Õ/„úm<¬§¨K]‡vþ;úøÈôd]Ž‹%´à8¾’n€6t mÉ™QtÆ­šG÷ŒÅGËØÂ+Ö­A én=ëk?Á•ÞKӴ㼯EÅ(ß=—˜ˆ2Äé-·'È'4œ¦; Hñ´ŒÏv:ip¨Oç7Ì’ŠÒ.ýjª[‰)›²Dšw!.Tžmí>ÌT·À¹ËÈNØÔœß÷øãqä_Mꑆ¢ßó™ËÉÊ–©„^4S¢|Þ²kìf©©T6°IУråRYa¬ÁQ‡7 ÂÏàÑ»Kï5lxè@þe(£¡ü|>z}~¸ÑÁ~¾ÌÔP9`a&˜±‰³Æ>œN†xß1ÀÖ™v9¤á}çõî¹ü›5ëºç.˜Ñ»`¼ÿxcÎ¥0ð®›âTöÙ pL(c|¸QsÞE½w ^ÄÔÙ©Î7Ì0¬[Ù±ÅÓú–ÝýQ¦Ã=ªºåþ‰¢(³Nü²¢ôÖpyŸ „;(=Ø1& M܉»ƒŽ5 4DhÇü;  t´UB‘ù÷]ñ+d L¦íìý¯@ø+Ⱦüëß7Üæ+¤æÆ*è /ogÜ"ÅN·"^Ÿ×tE¾N÷@žžF=w¹m ¡¼ÿ(gÚLðè+ËZÛÏ\`†ûã_ÿµA#_ØE¯ËóÌׯ;4|ûaÞâ1ïó·Õ<ÚuxÞóþ¹³K åþ€µÍ;]ú)Šãä8Gý&øjB3Þ”%|SxþxÃN»½ŸntzÇÝă¸žÃ’êº|SŒ<â¾Ê‡\žq¸6/ó¢*•©Ð;ÅÛÍOùÐì+yTFø^)õA Mk…ŸA3Ÿ¾¶”_ô(ÅÓ5Ä÷=c´gÒø"´YÞÏ`?_ÏQWhíªb0Kxpúi(»v`ô }øÈ)0ÏùëȾì3ð"l¶N£X•gƒâ!(cTYÍ`Ôv5ì¹.Á;yk¶;ûEÜ>ÂÄë>qêMúNU蹌1XX{ìOdz€qì˜@í"ÀðÚUžœ›"à(¨gjYcý%zíBm¦´`…Gw¿‰†*V— 2Jj€GŽNN»Õ‡÷ œÂÄh†ÃôlArÈß& J‘ºß|"O†–š·R!˜ Ž¡©˜Jp’"䵆6¹žl9“¯ÍHJÚnM&-pó“­àUŸ†Y¯UfBÁbØ ?áý+Os]™¸*t"Vþ¥ììトôt vD9êr‹Ë5²Àô7ÛåüBÚ_P¢ mï=ii4&`(ã ”_Ó_YÛª”)fXYo„-E S¨²â«Žk±Ž¸{ÁÏ“>œf‘ÁsTj–Ÿ`?_Ö|wRÏåˆÿ ^@Œ™a]^ Qø°£e}ÃÜ„üÚtùm1èy¾ÏÆ{…Ã/é\±«Â³QF€;RÏæ|_:ZŒ£g ì®À8‡ åÝ>7eê ÌÛ~#4âgã¹SñœÞík ÆòÂáëL Ü °î%Ä髵 †‘‡ÑP6w¥â;£ú0ßç…ÈR×Ñ4§@%5wnjâ'QvÇdŠiž¡i¿üÝWùóÚ`º†æ¿U> ¯;5a„Áâ ²¡õºZxˆÅy|§™) ç¹Ïa§æ¤9ß!mrÙ°\Jaa`X¼¯0kùh¥Á*” ý*²ßö™§ýã¼Ôò¾Ž²x Â’ÌWÕ«ss5˜¢@©Ë—½ýù=®B™b:Ä庠 Õ¯—žŽ©éÂÃïªT Ç h:z™”ß)«äó%E…§Hµµ¯pÉÌå²È¨+J¿…Û RøçæàJº'ƇŔ¤/è|”2ÂQD±ý7WõÙ0 ðß Æ‡]†+!Ç¡Øi¦£”ìcñÉéZ¾ËHI\Ž&̧Axz”û`‘E€ðÈ*/Nmí#°ÖÈ’G7 Z³7êÁÔ+¡íò)€c¾oÌï+µónO¤ûžu=…Ù=Ɔ×íö¼A(­áUvÌôéM0mˆFôV4Åʆg½Ï˜iBŠvÇoV¯Ÿj½Fû£é—@ظÐû<c(^‡Âó²O|ò#'Žu=WŸ–Å.kÙs›Ý–…lïåL¿ÌW¸4U…åëZ$œ3f“‘’£i†„Oz?´Z)äó{Á¤½ó+_#Í#´ÑÃ! f5¯xûów\•2ãJü q¾‰«qAŽçezö¼ã¡éõhñïó¡>6fð4϶Þ7wúН Ï×C³íÛ)J‘6Û£ï;¦ô3ÌOLß… â{”;4Šô†Ä)qߘ×i[ÕgÃVEöŸK™ò'zé°‚/Æ`èžy4;Šy?½»ÞÏ(]£rF¯—¡HvÒ ³cµ‹@PèÚ…„sS§è²3l›ñ—g ôK£nT׫|Þ&]huçcŒ©˜é ô–?£ƒ¹0õÃ09,ç.g£Ám‡4ƒN^E«a1™›¬ãf§$ÍC~†Cp¿>˳äaÜ—d½·¬}ôíaGú©éÚ4;ã¶ž#žhÐëá8ÚïÛ|MgÞS™­Ý.ï÷¨úBÌ]‚iøÀ”æ{dzŠ&<ÑÏR (žV¶3—féK ¯ݽƭí3¢ÏÃMØî‡¹FW”GçNKúÂL¥|&á–ïö|Œýï!Œÿ S–#ˆ°-ü\€^‡z¤ö ?ŽÌ›#hÛΞ83†´›ø,uñ©èúÿ ÏØnÓ½U]½yv€ódôüã-Ø .óqþB<À/¿,»WÁ;Ì[YÉ2¥¢b•©îÏixö.QxÖb)yšÆn#´º‘œN˜^opÔI³{Mé˜C{ÆfUm‡Ðǯ˜*ý|)³àÁTºœ€w°òºúð–Ö¦P–Eæ&úyô.6hä(~®¦Eÿ/CqŒù¿õ¥…½lG¯Ò^Už’!Uìc6^D7ù¸éˆšý ¶—‘=¹®©gjKNƵùHûFœ:Œz¯Øº‹¦(ígöí`×9K9ûf¡!ÀðÐpåP#„ÒhTò}üý ûÈŒ,’pH£ÿÑÈÐT{×`@áÿaôi%»a‚…«Ú9ÚMÄ5È¢G„“ÿ¡¡j‹†*Ý{1òk¯?ÚÍmÐÒM•œ>ðèåíÑL¦ Hψ£5 ²hðgˆXé!žò‚.ã:å+Ð÷Cc¼ñ F‹œŠ OÃþ ´pG;GO ÀÂ<È^n»º‚†Þ|Täw ~ó釴~ Aó „Pó¹·¦ ÌR¤ý4ŠþNÇ30iåÈÛ1ص3ο„™Ÿƒ:›†WÒC~HƒÚÚÚÛß ¤ò ¾eºÃTç~°ze{-"ÿ³w $³ÿ ±½ú)àáߟï+•-S ͘±CËSñüOóçÁs|Êò!¬ˆŠyr8ÎýžžÐ}ûÎ’qv¶kò.©È2í˜+ó|eLKúe•h̾ƒqÈ/>:5ôpu÷<Ûÿgðbýð6}EÛ£>6÷!À—0?1ÏWýÙ0CªøÓXŽAþ¦:³œL¦PÎ`sõÍmxÞÒPÎ3içá½Ï1í³cµŽ>´Ù1&Ƨ§·pçÊö ë+4Ã!QÕœ2Q÷>&ZÄnñ¥½3“ m )hÌ3â±SåÔÍ‹E[ZàÂüÐà. ®Â)Ù¼üÝù÷m솆?Vö]zuÙékŽs¯Û"êënW7u’vwtkÑc½uÐeud$Ð2õ—à %GtÕtÛ¡F ô‘ônTôù"VÚîœîšÍc‹iXo ìôÇ%çkúÙ°æzöÔÑJñÈzÒfË|î¡É;ICnõÃûL 6`¼6•&ç… ÔÀ†6ô”‡£ó3®¶ùÊ6Í;œí>²ò ÐV»Í°¯4ñ9&À˜`D€MPø9`L ¢ {šLÄiˆýþ„oÊPžzääÂŒI¿6þ•qN,`L€ Ô,€×š¢äŒ0ºA€º¥Ñu÷LPh¹òic]µ¶æœºÕœéWy°r#LTܰÙk½ÎûL€ 0&Àjš› Ôt püL€ T˜@|ZZ‘«}!ܘf&[0`kl¹›a&‰ŽpØçv`çø9©‰ïU8¾ 0&À˜@ °B¸4`¡#@soW—^¡û\˜¥`VEgôÐﺲTïÐìÓŒ„wèRÀ!3&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&jw¹m;6)µC¨ã føñÉé§Ç»žhÌ09,&À˜(Ÿ/ÄS>#öÁ˜ðI`\Rj'·ÔgbõÍs° PÓP±þ ìŽñ®)X¡3<-d”å^rDHýùŒÔäIá™JN`L vPjg¶8WL€ 0Јw¦ ƒð½Fò$±IBȳ¥b;CJe²®ËcuÕ½:áÁ´!¡MEåCw¹«º"&Iéx¥ò¡ðL€ 0&P¬¯ 5¾‡ 0:M`üÌ™Ñù;ý­K}oœ½þЮ‰¬@ƺž«¯ªû¿ºhÛÖÑó8—kdõ:ï3&À˜@Ý&`¯ÛÙçÜ3&À*N `×Áx]ˆÎŠ¢Ä{ ßÚó®qGâ“R§Â,åÇ,Ïúxœzó¥.·ÍIsŽóŽ~Ÿ†0ß}njòÅt-Þ5'V¸w?Š8†AKÒJH±Ú&笔Ä­÷"Ìe0¹OqˆÍ-„ÿARêïAßÇæ7Ìv%ýQ¿+ýXÝ­½ƒ°’f§&~F÷ëR¾:7Åù¼é¯¼¸œ)3O'˜­\aÞC[äák¤3zNJÒ‰D˜×F9SÞBÙ975énóo™`u› Ôõ'€óϘ@… @º< &'»çLKúÊßÍiÎÅRÈL]gùYûî˜àzª±õžû}´ŽG¡2^MçãçÌqèîÝK°{ƒPÄgeï…&=VÕ´oãLL~,îx˜¿\áû#øÛ¨(r’MÚ^À~wªÝhñW¸«zn„ÿžZ}ÅäG¼­LÄ­ e3ü_6ÑõdSó¾1®´Þw0òzvƒÓÁ¦ÐwÍlˆ„+]ßdžã-`L€ Áp~ ˜`'Ð šiDËv~tÑ­ÈÓ+Ј'çªG®Äñ æ¹Gà|=›ˆz…ÎéÛvÂÿÉv‡ÒçyWÒoEþ^åLÝ$4íQ ¸†]$Ùlò‚ÙÓœ ‹üŠxgêûRè×éº>Õª†½úõ8ÿQÆ”)M¿Öm@qKÛ׺¦)ÙjÞÜûݯºõËñA²‚´Úe8µœÎçk‡ÏA^lºâðû¡BþØ1&ÀêÖ€×µçü2&Puºl“‘]å¤ï‘R´!0ÙøÂðb˜‡ÜPâ>)®‡Ÿå³RXGça¼qþ³߆whÓ`§/ÙŸ'Šþpþ›ÙÓ’Š…o:-ù ´ÑG;Ó º„äô3…®wÁ‡ƒ!è›ç­Û@âÎH™ò;Ò»C Ï ó^Þ—#áßBÛý­Ð à…NÓ“_ºÇ<Å[&À˜` 8?L€ 0ŠbYC°.çf˜wèÛŠýH áW›3:5µÝl§3óž´´V99ú˜ŽÜeú6¼+„êz°þËÚÖ`^¾ûÐL-O^‹sÿ—›§_A^Ubm¨Xè0P3a¯À@É7ÌsÆ¡ÛjŒÜm=ø³­Ç´ïrI ,_…@=Æåš‡Á-´L÷’‘ÐŒ¿4räH·ó8и‘ã¯H¸';ð5{¸.žÖöþôw+™îõ‡ ¤¸¶î¯æ¨GNƒfø¼eL€ 0BlÎO`L ‚©/Ötqk|òôKqë'¾nÇàçi ¦*Êwæõg\w‚`ü!ì \×Äu«?™5uê~ÓL[6BΚô²y®2[‡ÝöjÛ“”¥m8OTh¬[H› xÿ.и•zâkO®.¨yý¡å¾æ'ŸÑ¼â24÷_MËÖs7!ŸŠÃÅpÿÈù `u”Û€×Ñ‚çl3&Pymìý!ÈÊõ°ƒNõµ”;Ít¢{ôt˜’ü}Þ‰=¼…ÞW ™î—”ŠAŒ:-Üó²5%ÈA>VÙ´ž¯èþs®Äõо/E:®Ò<úH쯘ãJ,aÖâf q“ù ”ñká9Â÷90¡ùÀ ˦ËO0ÛË©öß¿?ﺇy·L€ 0&PH€5àü$0&À*H€´½˜p´ðˆ÷…;ï7hµ§ÃÔâW»ó”xÄÉÙjöý°LiíðÞ&míί³Üi™²Ÿ…À»cHïî KØd×w¤É#î›ÜR|“àLŸ‚¡:˜¾0¿ Ì¿O‡ý¸ ƒ9]Hî+HÇ´"ÿæÖÿíŠ[ùZÚMȃÛ8¶ØÌ&Æ÷9LOêA¿YHù²ÿÈø `L î` xÝ-{Î9`U 1-é[˜bœˆ –á÷ ´ÙK0ßRØ…? é•¢žÒ›æ÷Ž‚ì³1¿÷k°#§{ßðÐiŠ@%FöCxêºöº¦ç¯‡ß…ºÐ&y‡UÞq¬½þ;„â×8Î÷Vyþ+·¢è_ÁÄ„†.xjÒ¤\3lZ˜öá?Ð5›”Å‚¹y·L€ 0&@#zØ1&À˜@• ŒOOog”§“’vV9°¢æÍ›gûzͺÎæs†öè±Ë[XV<¾Â©É¸}¥‡Ï1&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&P½duD7&yz_æ9quÒ¥Þ‘6¨Žx9&À*F@â°ÔeîÚlSl g¥L]U±"×7×S‘[vœr&À˜@UÔDÛ2Üåz)&Óy—ú=Bí¥šÐåNàYˆôHU@ñ½L€ „†*¡úÀÛâ½m¥ëBRlÃ{ût;G»g]®ÛòBkÍ…ÊõTͱ瘙`áB &Ú¾àñΔa€: xG)åüÞPb•ÏgMº?\`s:˜ðO`ÌôéM´íb]×oÀï|@oïQ©É_ú¿+²®p=YåÅ©eL€ „š@u¶}AÀã“S'èšxBJýhÒîž“æü!ÔÀ8|&ÀBG !)u€&Ä3ˆáx©ˆû2Rœ3B[õ„ÌõTõpæX˜`‘J ÔmŸ-˜` 5ý))äûÂÑòÒŒ”Iƒ>‡Å˜@õXõã¢Íý†\óªÔrŽ6|R¿AC®úaѲêOIpbäz*89&À˜@m&ê¶/hpêÎÕu9~0'5é˜À¤†`µ…„o™àL{/ö¼ÞE¢9 ×Sµåiä|0&Àª‡@¨Ú>%ɧLg.™G‹[YøUƒ „ã½ÆûTý‰ßÜ¢÷>¼YFj¸ž*_bL€ 0ŸBÕöE§ÙNŒ—°ùÎp%äøÌŸdL â ÐûJc<½ïÆ,G”#®§"¨°8©L€ 00"ж/(8M5ˆ/„<à2ŒžN zÏé}7¦ Q! –ë©`å@™`u@°Û¾* à´xEá<ßòºPœG&À„€þ½÷Æû@¸žŠ€Bâ$2&Àœ@0Û¾* à´Â%-²Có|‡97N`A"@ï;½÷E+Ü)ÔÐÃõTèØrÈL€ 0ºB ˜m_•p@ïD+\ò";uåñã|2!Œ÷ï½ñþG®§"£œ8•L€ 0°%̶Ï^Õ\biù¶#«ªáðýL€ D¼ûôÞÓûöŽë©°/"N `L "«í«²Žy¿€Ø‘ˆ Æ‰dL hðîÓ{OïØ;®§Â¾ˆ8L€ 0ˆ ¬¶¯ÊxDЪæDN|òÉzÙû²Û6l³ã‰É“³«9úD7*)õ&Ìó>qNг/!‹,r¦,V¤|çf‡$Ò0tTrÚíR×FcљӠ9œ&Àª‘@m¬Ë«_D…¶j¹"õŒ9©É/ÔH8R& Àˆ·„S‡ëšþÀ‘½Ùg’ÿƒò=Xyï/ØÈ¯ˆr8’Ÿu=RS—KW\.©’ÖŠúÐÝ«Aõ±Þ‡¯À“püõ\(öG'§ŸAt‹…K2Rœç˜Ö¸PéÎBzNËHsöµž¯ò¾.Z!Œù®r˜`aM ¦ëòê†ì¶#Þ™úÚ‹¡6›rÁìiI ½ósOZZ«œm‡b“—Í™æüÔûz•Ž¥~².”ÖU ƒofÕ@ ƒ0«!™áE|rÚõ¾?€jXU¤¸Ýæ§+R©KùRߨ¹¸G¨s‘©¦nïz´}¨ã©±ðuqvBrêØ‹Ÿ#fL Ö‡º¼:!'8SîÈTÓ2B§æÑgß÷øãq¡›Ãd‘N€5àA(AhK.Õ5íÛ3ÚÙ’&[´Ð?#ø £p!&ÿAŒNžÞÇ£©µWø¦¬K1O×åô±I©Ÿ<ŸæÜêŸ_aL€ Tœ@8ÔåOuÕî€Òè2„°»j¡øº[~ŒÁj>˜—†«|ùàsL .`<¥¯y„S ¹­½çT‹ðí7äxלXݳ;Æ"ðš`g˜¨¬‡Yż6öÄÇ­÷Ç'¥Þ‚ ì"Yß/«O"ÀAðß’èŠÐ“g§&/¢HF;SFiº:öóÝî¯`öâF˜{`Žq.…{†µµ;oÊô¤= ëíë„Ð[D·lÔò™»ïÎ4-v ŽÂîÝ¢R“VšWۤ✕’ø£yyi2ýyov{’Ûí¬b<\»Äûº¯ãŠäš¯;uØ{ƒ]O˜¹ÿôÏA> |… û÷¡(‡$ðì?»áÿÓ¶öö‰.×my¾üó9&ÀŸ@Èër{½qºš7S }0êlÔßI{‹»…8«»óG3u~C©Ë¥v‡mÂó®©Mj¨×—Ù¤m²¦iíáç(#z¡ÞÙˆU±^†iÞLÓßX×sõÝîýKÐ½Ž•ûÞ1ÏÓvtrÚ­MŸ87ÍyR|ZZ‘£ÏÕuqâ=€ð ǹè2mÇ»ä?úœüùr°ÃÞ¤é2 i}(>9ý­Œ”Äå¾üyŸ•œ>PjZ2ØôFÛåFÚVÚEÔ”çSXkõ[”þ§q®?~ˆJ_â°Gߣªù¥êì@óÄPF ª ½âþíÁÒ9©Iû2}´¦‡÷™@E ° JE‰yùw¹æEá%%[è¹.×ÈR/¾—wAþu÷î¥B7QÅ©(òf©ˆÏ`/÷P¦;í½þ¥lް/GÜßï~§¢2˜Ž ³¹GˆÏïr¦t!ÿi[óóŒ{¥>¦/©ºTfËæ¸vV¦š>ÿˆŸÅùûIø®PZŒÀÊþ‹Ÿ3Ǽ-¯ò„Æá^¬–«jÚ·ñ¦ .¾ùò—¦b?>v¡äHEN@Åx1uûðRâTEò‡‚ûÑ‹1âoŠMÜ„c.Œé‘~ýöâ !)õ¤*iÊdü~‰Ó£2ÕL^ŒÊ3!P-u¹šû5ê-RØÆ£ÿ ð×ëê®w |/_Je2¶ÿƒ0y¦ªªï‘m¶ßñÝóšÔ“pî%a·Q}Óǧã“SIËl8›ðཷ¦ˆfæ9s«é¢]£c‡”¨¿^ECæ‘¿Q»A?½¿Ðõ€ësòìÃiBÆÊŽ-¦C^+4íÄׇ·§F'¥Þ uÏ"ä?2q~Nä±±* V&$§c«è† ®§ë¹ÚJ´gg Mp ©Ü¿ÛUwÁR|˜ KG] ù@½>w}¡ ­&HD˜Éˆ{?ÚÞÎ,|åÉ{Á#Àð*²Ü.Ö÷†@­€ËR7Œƒ`z¾èO°|Ñ8Ú™¾•ë§ Îô‹æ¤&Î7ƒB% ›òùøO4Ï%¸R¿ÖÜb…[È+pîɹ)‰ßc¶Ž6ÐÜmúô×ÛL¿´Eú:CãÒ»­ã˜³­ M‹5L_ûú¶Ý£ÛÉv‡ÒçyWÒoE~^åLÝ„ øQÏ$â/M¾Â5Ϩõ0É›Ð>߀FgF¼ë‰/3\÷í1¯{oÍ_ü#4Ò¸§âþWç¦:ï4ÃA%ÿn¶;åQ7~æÌèü]‡ÔÏæ¦&_^tåeØQ®ÄGÔ‹ÞåwôNÞcL œ TG]ŽžÒyÐ>§qxuY,ê’›!<>:~œyÂàaMè)ÛÕÇ»áÜ ·câìõ;ÍpM»£‰©‘6OÁº¢âi)¾ÙÇ*Ï®¶ëA«ó—õ2Ö¦8V<û²©1Ùh^ó•&óZYÛ ×”-Ð8'¢;u&¶o`¾×Ä„2duæOžNÆ}6‡©I)•üV4Ŷ‰]é¶¢!ŸÓÍ è¥0 q†e^à-`A :êrÝÞ(Ó ò‡<°'´iv£1¯a­C7ª4ï:^Ê-¦sµáà<òÔº§¢õ¹™_[‡½q¢ªî®«˜¢èý÷ ³Àâ[×IÛ/lŽèâ6¼*e½ŽJ×£ªÔ>þ‹ò*ß7¯›Û¨†1[ ö–\z#Ð| çù=ô¤¡OÏr/™„©m3bâ”YÏ$&†`€ª™bÞÖeÆj]På¼ÛíT@ó)K Î~ÃÖu·Ïkº Á“%µÄðøø}÷åøô_“Ï=4¶d­dÞ[Á´˜·ùÚB›‡ž¿UtùX‰Ÿ"§ÀXú65F–¨Èü¦ÉWà^çÚØÐˆÈe{gÓ #ß~˜ú;oe­ÄPðZŒ'ß+¡k¢7ô:Ä‘Èû3Käy†ýä°œë3&ª¡.ŸóP|®/Q ú:ï}J—Rm¶»  >ù H!£ëï0ýW´>÷Þ5(£!Üž¹]Mãóþe´Zº.·Öã°÷6ÛÇÔù¥êë''NÌ |ÒuÉL"Vv9Êëà9!?dz‰ìÒ†Æ{L xX^E–çöê¶õë5ë¡%ÕïÄ`'Èæ­Ì ¥¾ Ýi¾5Rï|}™÷óbÓ‚Jk#*»ÜÙ©I/3™¾Â"½qÉÜY »q«Ò!C„*ÓêÌ´ö›¡=2Ok‡ÍÁAHÙŸWŧ]Ù=ŒveélW’1X©ø"ï0&±"¡.G¯_oÀ¹9ú:É|3m=ЧÓu±)¬Òa¡“²­wO¡yÉ{ìú|î´¤/ a~µiº'_ÿÊ;>MØVa ¡åÈŽ¸¶¿ÄuÕ8‡L*ÿ§¼R}]Âyø‰¨¯mÆçHÑ•Šæ##)i;n2Þ53=ß}èMÀ|Hß-mÂY"j>`&PêkºÂ!ÔñFŽéÁˆé©›KlÛ= æ^R  ].‡‡a@ˆéºøäGN„và$̈²èèÙŠìIª4DÇÓ!ໂœä‹F¯Ÿ7.)Õ÷FÀ Ìãs)SþûG`ù1Ø{—º+ÀüÙu ?´&n ÓOu…3hç=ƒ=»ãgøÍÖÝ:œ²cL ¶ŸºÜ?Q l<Ï{6Ô»ƒÈüMqØŒiþHÛ ¥ Ú kHEõÙ0ë9ÚÇ Çí£T»Šú<:Ö6Š’|¡j¿³A‹‹i·j|h°Õ-§ wuíF˜nm#ºš§ nÇ…aÞ‹ühž‚¡Þ÷V6ϸî>„$ÎDÛÞl‡çŸã½Ãåc&PU¬¯*AÜßÚ–øj¦–v9f昒œÖs­¾á „C­1fÿÀ M%#ÜiѨqôÿa¤÷¥ºš»SßM°;¢6jšû$Í£>Jóï6¶³ÿW™$wb÷Å_­Y·SéÝ ø^ÅáðxφânÐÓRß‘&¸oÂÄ­ß`6)VWë"¿ ò:º÷\Þi¨êq[Ï´LuÝÕhFÎG…^B#hþžKsn†½ßkøx˜ŠÙd²¢mö½Y–šv/ÒWb*/še Á™–cïé(¿Å!Þ@¼9ºªŠF§ i{.#eÊïUÍßϘ@õ‡º¼œ\Ç`6‘yXxía»MÙ‰™¡.Ål)wBÈ|Ó:g8Âø‚îE˜žp‚b³}¤{d#¬vùü5ñÂûǘEåÌÄu‰V_ù1./Êö”kÒ>‚úœì©Q×N„þÕÂt EEަÆEý{ÚÑWQ·îŽ’ú»¥I÷š¦_ ¿›L-4æHTu{n9t0ÿƒ±®´aßåV=ga‡ihGKšù˜Äy ™¸`@ìq"fWž'ïxUצ¢wuO½&Ñ›éä-Ö€$™C`j©Ë1Pd¾–Û ²Èšú3„á°Q¾¤1¸„¢zbòäìz±òìþïc·;»GÓ>—ÿ GœÒŸF|W&I¤½ýñd„Ù«@è›° O¹¦,ÁNKÆ”)•ÙÂðŸ¨_×ôüõÐê/ÄÌ.“*“§@î1*d]Þé­M¡{+’¿vŽvcð¡ðŒ¾ŸËwdꚺaA—ç­Þé ),2qXÀ@©å·ö;º†_D£ÒWê 48예DáP——ÅM %¸ñhžzJˆ¨§^Á"`%zä”z⨗C˜}ÊãVÿÓ4õGè›w+Âv›wøv[ÔÛÔiº6O?ì>íÎA}*D¨ês´•¯!Í ½ÓAÇsS’Þ†À;šçkóuùªüºõja“WÍIIzݼ‡>60®hÚ—®n·ök! Ö¿°]‡|¯6ýÑ6Ð|@øö ÝJÓÝžµGÜÙ{UÍóz⻼ø©I“J õÖxŸ T’ÀÑÏÏJ›®ïèVh7Ñ–]á¶íû:´­³ÊZ‘ºw»Ó: <©ç6C€¼Ñ®éÑpuEd°Ó2oÞ<Û×kÖuõ”œ¡=zì fþÍ“Õ_ ù£é§vˆeíÉ44Ot=Ù4[ÍnÒ¸AÔ®Çxà°5ÎÚ¾Iï~$¥µ¶?7‘”?ZÄ¥&ër++<ÃT¿üõ$R}¶C<Ò±µ8c[YJ ®¨ûZ4·;7[æ¶¶[¼ÿ£6È>änJ=‚Å'‹vj¢>—žÞ ƒÞµYS§–´÷J™sÆ E3´ö^×¼ËËqÝïHo¡ºe#EÔßa˜¡xÂÇuž@Ø´'”31u¾T¨C"éݤ´Ö¡Gˆ³Zx†'$§>\[Ø+`! ¬ö„MPBP8$`L€ 0&À˜ðG€pdø<`L€ 0&À˜ž%P9H&À˜`Á$  Û vݶ!˜arXL€ ÔÀkŽ=Ç̘`L sS? È#{bL "° JD'’ 0&À˜`L ¶`¼¶”$çƒ 0&À˜`L "°ÅĉdL€ 0&À˜¨-X¯-%Éù`L€ 0&À˜ˆ,€GD1q"™`L€ 0&Àj ÀkKIr>˜`L€ 0&À"‚ àQLœH&À˜`L€ 0ÚB€ðÚR’œ&À˜`L€ 0ˆ ± ñŒIžÞ×£yÎåNºÔÛJ!Dñ &Râ°Ôe–Ðõ-6›}Á¬”©«‚|Åeôµõùê Åx–=êBÊŽu¾žb³M±-¬-õTõŸ€wÿöŒÔä/ƒ[*~·ƒŠ³T`u¶~¨²ðÚwÀ¹·ÎU?|û2mCáPIOÔuùJ»–ë1H¹ðìÞ²}Ë&Âa˜ï‡P`)“ÿu @IDAT8ÓNè*éÜZlÚ¾'6;Ï}ýÉgŸ“ýëâï.ò–B8—mq1†l'TÏGu¼ûÁ‚â´Rb¿}ʃ펨QOÅq=Uºä|<‡q‡²óoè7pða´ËJßÁg˜@àw»šŠ ’ê‡`µ'Æ—G5ñ­L4²P@Oö;®³rÿ-—Øztl]™pêÌ=Äç[/±õ;®‹´ÙÝz¿s2ïÀ>¶èK>\—m ”D=5@§ÂQÒûDÞ3zßè½ãzª|ŽGŸÃΊ®‹'©žÇ]áT?•Ÿ öQ› ð»]ƒ¥[Wê‡pÀåS\çCóý85ýÎe”£Ê û|¤ª/jâD¼ˆ›Ã~Ý]÷^ˆØI§ò‡FŽË¶ú‡R1EÀóQ*Íax‚Þ#zŸô~Ñ{ÆõTÅJÉúR=Oõ=B‡ú©baßµ¿ÛaP¢u¡~[<>ÞUÏáp¼Ð¾Ucqó¥ý¹R®Ä AÜÚ¶h,6n<ë„No„ L!¼¡ï.Û౬JHáú|T%OÕx¯!|Ó{EïWûVMt®§*GŸ¸ÁlGP}OuCåBỘ@Ðð»4”U¨6×á*€+ž¦r¼GÓÚ^3ìt} ±«8âvÝg(š.Z÷>wà8„Î×d¹sÙV¼(CrG˜>!Ék¥÷‡Þ£hz¯èýâzªò„é9¼öüÓmTßS½j²~ª|FøÎÚ@€ßí0+ÅÚ\?„cEGÚn›Ýa›pB÷ö:Ù±«<âGã4PH»…_MÙƒsÙV¾(Crg˜=!Éc5ža„IïQ=z¯¸žª:aó9¤z¡ÕTýTõŒp‘L€ßí0-½ÚZ?„£®Üx÷§bžØÖ˜j^vU$PıÅÐ+®9AÕ¤œË¶ŠeŠÛÃèùEö‚f±†¬è}jÁõTpGª÷©þGˆáØ6'£J¸àw;\KéªõC¸Urƨ#:ê|Zdó|×øãàVU,4é?šƒF§³¬GâÙ´e«!ðƒ_MhÁîlËbV—®…Éó È­²zŸÂ¥žŠxå¥Ñ|©þ‡_Ö‚—Œ¯“¿ÛÁ¤‚°jcýni›/AgZá²&ÙÉÚ½_¼÷õJ±~óa³IÑ©usqÇåD£±†ÀýÅOkÄ·+ÖŠ#9y¸®ˆîZ‰.\´J¬Z»I¤Ž»2bÅ‚GÑ ^´~Ènïˆ;É …VŸ+ÀOůº¾¦l‘gvaò|XR¶»æ3Lƒ™ë)xŸè½?:Ï®ŠŒçõþ¾üüΊð꬟ª˜z¾= ð»æXë‡pÀI#oŠlƒååkT;ÿù«E[So¿DdçæÓâ6†ðMÏèg?þ&.ý]\5¤Ÿèݳ£8x$WÌûògñÄk_ˆÔ±WŠ(G¸a¢q£8¹c·½%’ojÀI'Æ´Rfu¸°)ÛêÈl¤ÅÏG$ +|† {bl{Kâ ”4R½¿ïà¡6H/ àÕY?E "Nghð»®A µ¶ÕôÐ…“£ôØ!Ä85Ú°ý—µGÛ¹h‡&{vj-†q‚Á‰LR¾^þ§pʱbð©½D³FõE×v-ÄW ‡³sÅâß6„Ïâ´O›TêãÙ€“ }%Tgù‡MÙ"ßÕî¶ï> >_¼æL¥£Þ¹÷رç`é Õx& žjÌm¥£2žaÜMïO4½O5]OU:'az#ñ¤úÉ«îú)L‰p²ª‰@D¼ÛdK2HU…¡ªžªQc÷Ö¶ú!œTµ$pÝ@ºÐkTø¦§ëäž åþCDEÙÅà~Ç/{¿çÀC3~\RÒuÍ7-š4Y»ö=f{x)|›6–t·XÔÄSô³³lÉ̇z"F 'Âü@¢TÏ~ï[Ñ 6ZÜpÑYtX¦û{ÓvѹMsMV¡sÛvíŸ~ÿ«¸ð¬Þd_"¢OøUx4H¸òÜç«û ŸêÎjeâ+~†q3Õ›QE¼*ßS¢:¢:ë§2R×ê¼ÛfûDüêEG‰V͉}göî^ŒÔ—©ê¶ûEêÿ>ÆŠÖ‹.Pî‘Û¶sŸøíÝ?Ûvxû–MÅåƒO½º¶IϽ/ö8ŒvEˆFõãDïí¦E5Lc‹#²ì¼øñbÅŸÿŠI7^`(-—B²ì6¶6Õá$€SáÓW¨ â`I %$EÙ^}Þi"¶^´øèÛU†­÷µçŸ!N‚P~æ&äêÇ’%GIW/FÎ)¼^òJ¸6TæÔ¸™]¼Õ•¸”-Ùß“¹Ð't ëWl Õã°¾HL¹õÑ:šÖ)ªwÛðsBÿ PÖjôù(…5ì©ð.|wðÕ¼¢ †y„&úÂú¿ºë§Ðä…C!y·a2!Æ_sžÈÆd kÿͯþ“ع÷ „ç¾s!ázæÛ_ º'Ýx>„ùhŒMÛ.h‘=Ó}RqÎ)Lj [wоY!ŽAï}ßã:›—‹·ùªX½~³hߪ©øùC.€‡¤­EõC8 àæW¨DI थ¼äœ“Eßc;‹·.|+Š¿\4ng<̇³É„º¤;’“/ŽÃƒ®®Hcg ßż‘ÞêÒ€½l‡ æâCé–KûûD¿tÍF1ñcÀl¯®mÁ²v»M<óö×"/ß-žzs!ìžqñ9'‰¯`^ôà¨Ë mÂ{_¯¿þ½YÜëEÐ,ÄŠoW®5*-ÒLì@%úÖK16`¯h‚¹— 8YôëÕňÿMœ§ n/zK~üu½wÍRéz{ár£·„®½ÿÍJc`Êq«ø~Õ߆ãKôÀÄ@s2=0P¹’ÛºcŸX€À¡2§±v|€thÝLŒ¾jp©8*z¢ŸŠ&µ&ü[ßãb xhŠ¡¨þ·òuýšŒp¨‘BÀú¬õݶ+6ÃŒ•@ôÄš¤0úmÑ ¾Ç Îq_ÿ¼Ö0o½“@R\˦d¥uÔ5ˆ‹Û67~kІ,C›çKÿmý´qQÿ“Œ,ÔW¬)/«m!É×çÿ$¶îÜkL4qÝg¦·dÎâ„þª¡§‰/~Z-öÊ6d¦áÐÎÛЦz·±iw]u4ѕܫMõ}õ…“3_„°IS|eÞ>|€ðx4c f³Æq†¹ÂŸÿl+‘Æ]û‰Ýû‰Nm »ŒJ\ ¯*s“3m«Ë™q5>ª®ÚOý_æîRa<œ#^ùt±èß§‡}õ¹"&B?@(¦ÊáÔã»þ¯€6âÖËú‹Ž˜é†lµ3a.BîÌb£¢Ü×ÁL…ÜŸ·Ą́!ÌE4ñ,„÷\ï7AhîÚ¾¥øß‡ß‹-ÆÉÂÇ™•üùï6C¨o®G«›[ðåü#®»ð å0ü›='n7„ëL±ì÷J­S›fâ/— ÆÇß­2ïéã¯F÷c[#ØQ#Zƒ¯ê~M=UMwuÜo>ô ·º³:ò_q˜¬«3NŽ«î0Ÿ·¾Û$Sû±µ¨!Üù¨ó†IˆùûÝK¶Ø‚ :ÁLÒ¾Ë*"Ò8Ó nÔ®ør¤õ>©GqB·öhÛTñ;Ú4Ó•Õ¶¼öùQ/Æ!î¹þ|Ñ®EñÖ‚¥æmbïÁ#â/–ˆÓÑ =|à)†Ò‰I¾ÚØâ›ª¾c–WÕCªÁ±!°5êH+Jæ¶ìØ+ö û‡´˜äH²Ûlâ< È\üÛzñå²?Ä®}‡Åú-; yGh"Á€MÓ‘ðö_æžâßÃÙæ¥šÜšnMpzœÄøäc:fIPõvTÁ4m'N9¶¶±´*¨¡Õ&MDh©É‘ 8­´Õ¾Uôhý–(×Câf¿9ãÄnâïM;Œ¹à7ÂþŽlÿ7nÝe<W=ÕÐzß|ÉÙ‚¦Hú ¦ËËWÅä›/CNë%⊴d£÷Ëß› müè«ÎmšíB4ï3·Ãõ§×ɨÐèão¦Ã$·nóN£ÛlÖ{tl%rò ŒŠÎ¼/Ûš|>‚üÁ|BŽØˆ èuEõ$›c‰`!·ÄšI<’SŒ‰¦2~{ÁòâßBôpZÝÎ}E·öe+öÖlØ&f½û¸ÿéy†øygo ÂØ§^ûµÿeŠ“ÑÒò¶‡ÐÿO±?m Í÷/”[ýO>Æ[ulØI[N¦1¦£6˜Úºþ}zŠŽP­…ÉWkú¶VÔád‚„2 N$(‘¶sáO¿ãaöˆ.èÚ¡}$`“»èìÞBõ«eÂÞj¥qޏ»`N`ýò$ûÑ—?3®Óߥûˆ‹ÑõSs®æM{‚wÒ€“£A')¸²dõ¤Gß͵ÿeÓDNñó£Q½|ô\ÑÍ|óìè(šÝ¦>¨Þ4ºÞöZèã:·ëðÁE®fÈ1]ïû  0]÷-K< tžÌš^ƒ `½˜(Øó½×¼Çº%Í7¹úq…]ޤ%!׫kÆÂúá—u‚*Ãà¹Ú÷|¯‚Ç‹zOÒ_üT$Þqiq=ã+ÆòÎ{ÀSyññu&P; ïݶò9€Yr4{šéš5jPb½s¦y½A\=L…LËvøw˜SÈ8qË%ý6Âa/-Ú­\ûŸaêøÙ¿‰KÖæ"‡ ü“‘:þÚ–uE (êI6)œ¶í: šåƒ4ô¦Ã.ÐêWm¦3¬Ú¾-]J5—ã2Ä¢êM Ñ÷ÃΗd;š <3¡X ?žu¢ñËÁ×ዟü€/Âý€™W,t]qn_A¿pÕÁ=äq©Ð@ØÕÑ Ù®íhºóBG6ÔM tA¤c¡á^øÓÆÍÇvi+z`%šçý·u[0н¡a·G³ÝÛ-9 éäv¢'ä„nGWn% ƒ·£S©‚ü軕Ä—Ànû\o/ÅÇþæ’¿³¨ÌÂÀQZ ŠzbNìÞ¡øž턼ìB”î`öB2à)Ø+^Øs¯x–øŽ0#PmÏØÒ5 ís‡V… –@8´‡ÉÇÚÿ¶ÃdÄcôÀûº‡ÌJÊØIæ'd'~Ú5r¤¼¢YĨWö, âô×¶ÄDÊ?®fh·­ñ› 0í•Õoˆö«­ìB‘þ’’e(b¨X˜¿Ð|}V,)¤±Äd¿^·wd—5êŠAÐtÏ3ÞøRÜËEžÃ;Ì1.¶qtÅÁ‡¼l/pIþ#ÖlØŠî²FÄ4s͘ }óó_8×Sä¸Å.ØêÓÊ¥´j)}L‘ QC b!Í4 ¢¥‘ênØÇæ‰FùÓTP4¨å$,ºDŽ´Ý4(“IŽ8·Ÿø6{ÔÛq"¦*Ï|LGh bÅc¯Ì7L›ÎA—]EÜ"¬¾Ú6ççô9i³azÌÒ‚~EÂóá·¦žI »SÕÆ†ò’v‰¦5ýݹÝð Ò áÎEcL>þî£á$S'꞉Y›^ÀôbÖAÅ4à‰4ëó¾úƒ§ö‰VxÞÏ:©;õg€-/Ží{ÀÖs™ØŒ0HcGÓžõFCïo@VðJ˨ÿMÖÁ –Cbþ ˜Ï›¹õï³WTL/Kæ¤Ä¡ÙGÈœ•sõ1Mn î¼3O+þúOÌýà;cðd}ÈY»¢§¬iñâ€å…EãÓhŒ Ö'sÓÑz'+þüÏÀýµ-¤Ð¢Yßæ/YñV§Šq±F›iU8™áyo}µ±ÔæVÍÕžú!mÀ«V65p7iÌw^&JîszÂHRŒ’ºÅ†:Åø²7À@ç>ÁÜ÷<þºx`æ<ñ;ìåÈ‘mö ¾Ç«˜Þ7ãmCxi†ùÜiNwZ¨€lÙÈ‘YÊ>Œî&í89êÞíPcÐ탳އÖ}¥aZd 膧2þHˆ¢Qèï~µSR*ÃgéK´0Ô/oO¼:_LËøXLFº7Âd†]í"@y©7‡7c /=4.…•7Í„sõy§F÷/uS{*&¿ q¾Ï±1áBq>þÞýjÆ äÓ%cð¯¿8hÜ 4¦™nÀ`a²ílÓ¼p0qY²Œ€ù 0ƒÀC9"í…OÄ+Ÿ-ÆÌY‡Œ^ORìTÄ‘éë]˜Êp;f"™þâgÂùüûÆG5ÿ Ôý !›ä|iuTgü½‰Ì4sŒÙZ|µ-dž’på ýŒ9Çï~ì5´™Ë kX¾ö}µ±¾üÕÕsá¦ØrP`4¬(Œ³: З‰M×gNÙg¦åBØìÓ¡ÐHnHkºkÎ?½ÈTH]ƒtÞÛ\…¦¤ŸÕÑ4ƒÉ£†B;UlÖz_Sžz|WH]‹ƒ ).éGÎêßÛ¥uvÒ­†¿Õ˜BjÑŠ¿D:4›¤%mþœ÷¿ƒ†ƒ¡Ñ7<ñ_­!@”æbR{dcœ_ŒFf ó¸è¹9¦pf—²uPqë"a™¦:rÚqƳÒæJ´Ôßÿí0ø(qЀcšÝàîë΃mèQÓ*s@™OÑbW4aÆ YôñÊŽ 0B˜¡úòC“˜u¾Éò„îí0ÀÃ$VAcCÓèš.mÜ•æ®ß-=ó5þŒz…éW^ÛBcܦ™¿ …~ZX¨ÐÉRi;rH‰tøjcKx¨Ã,1Öá¯kY§¹R}¹ªØ¯…zMïôj˜Âj?òí™lB"™&°«}¨‹ÙtÔeMKPS¹“IÔõžiÌ}ÿÃ/ë™^ >µ—éµÄ–z{¾@×ñNŒW0ç¦éÇLç/šÏ—œup—5 ‹p"ÄŽ „Ž@UÚªòRHÛrTð./´’×C™î’1EÖ à‘U^œÚ:L€L\¨“VþÜ{ð°±(iP*jG^‡FTÖËB½<ýzuÆ|ó«1OüϰoU*o¤%'ûoš:s2ƦPÐýO¿SŸ¿8Ì©3Ip7Óe È*00&1¸m©™¢b<Üiñ–_Öm6¦'¬úƒ $ˆƒ¨µÎÇ€ú±«»hš²ìÜ<Ñ S]ž³¦oa–DfP¤%·*¦nêCÙ9øP‹5º®¿\ö{ÀÐHè&áœæ$qî©°A?““c†¡ÊÈ 8röȘ@µà¶¥Ú‘‹Z+€¸h•1Å!¥n“VXDg`ßcÄ™ΛL ìP—/ z+ÏmêVd[I°Õ¾¸¼ûø:`L ¢hq°·.3–§¦…=¨Þ¡)2É™ƒŠßÆõÿ›x†_ 1[Ï|,=°oOc²@â# ø­—o.øI<4ûc±'Zq•Æ>Ѐ,šÏ> KP“£™Ž=¢Ä8ˆ@â`?L€ 0ºL Ö àT¨4iÿ3&À%@3îX^YæR´"+ýÈÑT–ô£QÞ6™ÞžhàÕù˜/ž4ã´2Õ•ùëslGãG3¡Ä@nüò? Ë:ï3&À˜@YjµnWlÅtOŒâ¥è h‚aÁο[õ·X„ù¡iŽNZBžfÊ Nó1ÝÍ»IݺKWoמ†8+¾úé±vÓvÌv"M|Os@[áÑòô_,ùÝXEñ2L}g6˜eÍÅKƒ¤hI{Ò¢Ó,”šÕ ôsí–õXð5&À€·ðm¦×{À“)8›×+ºõ7g±¿ø+>ûgL€ ÔE%U"µœ@ßã: f‘Ø a—\S¬xÅà~âÞ/0–œÿüÇߌó4w4uÁöÁ„õ·^ÖBysô„´TÔýzë¥ýÉëi)«[õ×&1rØi†ÐÿÆü¥ÆÊUtÝß\¼´2â› –ŠÎ÷žë†a‘‹ŽXð¢p*/žk×JV ŠŒN{YÜ÷Ô[ÆrºÖ«.Z)èÚkŸ-±ž®sûd6•‰…[Ø1&À˜`áM ÜpŒÛøIÚÝ‘½$9štž ÚÔÍÚ¨~=Ñð¿þË2¦ù";G²ånÚ(NPw+ :"7ôôã1Û@K,ÔRß²ÿü'Ó8oþ]6¨8åØNX¸â,Ã~ü_¬´"i]wd6õ+³ ó1·!‹¨nlÔÿ̸n?œ{&Pë „“ U¸†óèžœ<·›Ž«ºfiQˆ…›˜?™-«LŽV[µv34ªšažBiúÙl¥£%¡ýÍ/–‰¶í4fخ̩ºŒÀð×¶ycc—nꞥ¥_Éô¥¬¹x®,Þøâ'1mîGâÔ^]±ðÆ™!™k7¯À­«ªšk¦Õ²-æn9–»42™þü²vSñâ#ÿeî¹X¦+z)L§z<~Í…V¯ß*¾‡éQo,OKÉÇ œõ;¶xñž²Ì…è#ˆV ü ã ¨›ß“¦X¥Œliý™ Qš¬6bH?ô‰æðî‹´0ƒzõóÅâàá\c:9ºN ¬ÐÀßy˜VŽi28ó;ƒ /¹²–÷e6u"òÈ.hJ½'˜O;7õTÐRQ=…ú¸pòÂô—âÙâ$3&ÀJ7 ¸‘@ÍíÙuà`vÐ+Þ¥k6«vhÕ Zç=¦~Ç‚gˆ4¬,8B˜áJËÞÆéo–ÿ%¶îØ+RÇ^…eç‡cYæBa»ð¦Âÿ]û —§AK4@ª]‹¦ÆŠu4/iÕŸ˜x­sUÉU¢:·m.o¿LÜ>|€X½a«!¤YçÚ}òÞë„ù;©gktÚ?p([wççG¼}‰À]·Åè© «Öþ+N©U—O½þÌ… ÜnЙbÙïÿŠ«†ž†…FšaååÆŠ–ž?s!ºöñw« Á{úø«±:`[:%hfre™ ÑŠ‚o/\Aÿ81Kzÿøëz1ãÍ#Ð] A¯Ç7‡ð/V$÷Ã/Äï3(.ÐG|ðÍJcÉ{ºVÖòä¾Ì¦èv¡#àÎËߊz*t)ÿ©žò¨ja×aø'—SȘ¨4pÀIPÌ:œ›/s0{IUœªy 4ï@ ycþO† s1–ÿ¦AEûjÛÀœL;~Z³±0ª"±¿5¦-Ü”µÛÌhÉoZ®aýX˜(†Gšpo·è絆°D+Ï‘yIŽ­`®ú‹·Àí1{ÉÉ3ì¿É&=Ù®íZf/ó¥üýáeîâwYÇÄ‘xæædï?Ê¡ù+ë¶°¼ÖLݪG¬ß¼ÃHßJhÃûbA2M±ºòÌ…†“¹ÐqÄð§GƒYKaxþÌ…(ìu›w¦J4ð–Ê6'¯ÀÐZb2DZoZ0‡–Ÿ'Mw¯.í Ý>ÎÐnÓ fülÜjhç[5k€Þ“V‚–ÿåï£f%æÒá'ã£ãÌÞ= S&ZpÅŸÙ”• ï…€ùîèô>£ž JªjA f=Eõ?²Sò…®ùã,0&À¬ÂÉ…Òe4n;·mYܰY³{Hxú ]­é­Ðþ˜‰¤½ð‰hWO@=úªsé»(»·3„©‡ç|$bc¢ÄpÌZB]ü¦Ø÷XñÖ‚ebÂoˆ›/9[ „†üåO‹{ÝXžyèiÇ‹]ûÚdÓ2Ïd–ðଠá›}s•9sñzð0«¾ôñÂs¼a…;ð‚9×.q„)„ܺaÝoÈŸ†_±aæ7R¶4“ ™¡¬‚àMû$<Û¹aNbæ!s!Ò|“«Wh;žÍ8¹²Ì…za&œoW®5¦tûá—u⌢9åËZž»i‘¹S;ô€#³’(LéÖ®Uã˜þâb°Ì¸[5Ƭ߲fN»Ùµ›n'V¿4¿¥Ãy(“PH·Ö÷F£÷©]—n×Tµž iŠ#(p³ž¢úÉ6YGP8©L€ 0À „“nV¸úwŸ}ð{÷Þ'ï]ñç¿M!€û1 );“´D7ýü9„&aöÒnÓtƒähygÓÑ<»´Ç` @#£×‚ÎÓd¨>ü–ÿùân74â4­àWYÎÛlª,¿|­RŠë'Üm¼Kô^UG=U©ÔFÐMf=Eõ>’ÑõSaç¤2&PƒÂI75Tùª?|üÁ[¿f Ù@³«8âFü4U=°dþ'_"„‚¢ ÕdŽÝÈqôÁrb÷ö0‰=:´.•p2"»z2úäûß™ â(\Ó\èÞ'ß6zÒ YÓѪ¨dýÄ«óÅ´ŒÅäo‹[w› eí>`,Ï}÷c¯a&“å†Fݼ7ÐíUCO0³ÊŒ7¿㦿*žxí ±¦T82›Úºc¿a6E&:ì‚JÀ|WŒú !ï½Wô~q=UyÖf=Eõ=Õû)¢ë§Ê“à;™¨KÂÉ…¸“†‰*ß‚}ûv^÷ÛªÇä)§NõÓÅú#VÊ…­«Ž¸‘PøË’ï^ÍÉ9|hT+ýHx Σ÷^ª{ÔˆAHþQwÏõʺwh%1¢Èäú¸?s¡Õë·@þ—HÇŒ9d×M¦KsÞÿN,ùmƒ øü› ÉáSœOÝ{=mŠkôÅû4çü˜«‡¶í4“N\½˜Ø^.oép_fSÅóN0×OÌx—è½¢÷K8ô.®§*‡Ø¬§¨¾§z¡D\ýT¹œó]L€ Ôeá(€“†‰*༿ødqƒ&M^Äþâ¡ß|iI&ìÊ&@%jÔV®ýOþóך]üý¯¸ƒæ§_~Ä—8GŒŽ´VØ•g.ä/@2òåh¾xšÇ›ìº·î܇Yröˆ‹Ï9©„ת˜ Y"s%s(ëùòö}™M•w_˜½/ÅõöwŠÞ¯&-Z~„ã\OÌÒ˜Ýɬ§2ÿÛøÕ÷¸»ÎÔO“bŸL€ ÔFá&€S7/5p4%5nÙóß|å­ ®½‰Òyóö½ûõkÏ?C!m#;ßÈ–’ºÃIóýÏŸk>^ôÑ» à“¶ 8m‰+ñ%ÎÄ›]h@îÌFò9f­Ù{ð0cŠ3JÒÔ‚ìê Rõrƒ_ô¢ñžé‚Lú.ãzªüçÁZOmýgý+ Þ~LO¸~*û`L –WÜЀƒ1 Ѩœß9uðyÛµÓÎó¯-h|B÷ö:f‘4• Í‹\×ÍŸKSxÑ,4 LS݇Wþðí[«—þø[CêÖ%–ô³j˜X@Ýùgž èÇ®Î0ðõhDág§Ý½;wìè7`ðu¨§þŸ½ï€ªÊþ?oJz‡¡„ÞKè"¢\ËÚ]qõ¯[-«[Ü]ë6w-¿uÕuW]+ *R„Ðk-Ô ¡„PÒ“I¦¼ÿùÞÉLBRf&“äžÏçÍë÷Þ÷}oîýÞsÏ='RÕS5¿“‹ë)[Ñî­ÿ¹mõе|¥ªŸjÂ¥öÚ 1Å"Å}[k¯kw÷¸Øvßo¯ÏÜ”çò7޲K;KØXB#F®¤×ïÛºåÀÄY×\Ç.õ®b¢É/SÔ£"Cµ ³¹ÃÙˆ[*+© ¨\/.·à®Ùí¶âcö­Ý´ìÛÕå奒t2~XàLx×6eÿÍåU¢ð꬟¸pbB;wzwÚ¹ãð„³§òÿq2×S¾ž²ZuD EÐ"ÔSp‰šypÅÆ¥ß,)--Bp0U?ùË×­ÊÑb@°±gÅ Žº|¡°” Jʨ’ç ¹h¸k£ÅÙùoÌÈà6724„dp`¯®"B5‚" ¡"䬵ñ÷ç®eB£ƒ\A®¹²v¬øâ“Ïyÿ›#RúuïÛHpHXç€à H“ÉÈ_}‡ á‡Ãn­H4X+†Ò2M+/=—šýá¡=;260/–d$[AÕ6ÌO¤ýwG¨øq•(<Š@½õç"È9w~­¬ ÿ†÷—÷:²g~‡…ÅGšŒ&ŸÙ™ “%ÖÊÈ,¹í“5+Il6kEe¹¥°¼¬ä\öáƒ{ìL;Äy«úÉ'/@eâ+@ºA²Wm;@©;öSaq¹˜@ÎéÃÂ(õu(áš²°¤œN°g.( ¿Ù°›"ÂiÒˆ¾ppGý6 ÷Ë’Œw(lªÖ_¿w-“$Õî _Wâ»x9ÀÏ!4ä¼YZ(^Ë{x³}Êea}®ŠŒ|Ü iÜ—¤ÎcËË“¸UƒÉ 4Ü ÚrHšoq¥ýf”(<€À%ë'Î#˜—ÀC{Ò÷ñr·[­~ºîví ùÌK>Ñÿ&·½¼–|`…EÎíQõ“—WÉûLÐçÑ.=õû÷H Yl®8 g|‹âxøþI¼“#BZoï•[3hÑêtJèA_5™z%vöQ ÚF6ð6²’XŽžÖÑ´áþLÀeåJÛ} ¥ð>Àk1Š×µ x{&âÀƒþsöü[¯öLn04jdz >ög™••·f••Áä:*À f'Òî[ÜËûJ æ#à^'¹oû]ý^ã!ÏÖØóÎŽ¬c$.’€£’U?y{•ªp‘ï-´põÆÁáæNO!å*¹á€ÎÉ£s§Ðü•iô톽óÂNÓÆ ¢@=€µá¦EÂý™€ã-¢—îò arÆ„æÛ|K[qïöL¾ùñ„è¥6›¾®¨ôg?ŒYÀ/2„5á=ßìÙíÁYûýš¯@c‡Î à¦4ß ‚…€hõSxD'έ±ç½`#Ô?X õ’\«ú‰AQÒö€æ{ÏáZ”š.È÷³Æ¶½‡h¥£“"ðZÊe¶ NP…†þ!ðRj©$àjáÈÞ•V«eÞíYÚ¢¼=¿=[¯ä¾¯É:t7u#ùà Ö+„ü-JTݬP( …€ˆp ¯ûžAvàçÛòòË(Ÿ#jB̦Kl$Í?ˆºtŽÇNæÐkŸ­¢Ço›F‰±Îc8ñç—‰”×N*®³Úì´˜]ÎÉæ3Q!4²_Íœ0˜–nÜK©"Fûxf·Ýâ¢èºÉÃ(>¦æ,n‘‡€#¬MŽ0ïÑ¥²YYhÃÛ³J{Ѐ{øSh»ÉióçÛMFã®'Ðõ[3G¸Ìµ¯6 …€B@! h0??_XBˆp ¢ê+3¨ýìŽ+é¦)#Iç±¥|ºšrÎ\htöÐ&üÝVˆ“ËÄz8=}ÏLNkõêÚÉ•È=ò˜3e8””ÑŠ-û]ç¼¹ÃODδT°œ#ŠgàÝžEðvöv{nMßÄ=ÆOäciz•ÿxí{&ƒ|XµV( …€—p8t²11,,-áå½”MɆ…±Ç•HÐ+î»n"%Ä„sx÷½u^[×Áã§ÏÓ¾£§éªñ9eŠfí7l¯±H1²­òÁZñˆõ¦}Y§ÉÂdØD¥1³‚pà ¼Û³(”vùvOkš}Ž0C!}ÌÑ1£îâÇü ]>ªz(…€B@! Pxh±@3[Yi£@öWÝZs!}iÍŽC5Špðø:Ëný¤TTbʇSNž-ý’g6“Ãf-È'€'DúB€gy©ƒ±­“\³Ä¼½š¡øY_¼=•‡ ÞiiÙY)#ÿÊÕůqС;ž×gÍúL[ºQè”( …€B@!ÐD¤ ˆakKXp OV´×ÐP§n?(\øÉ²Y˜ÌJ1`-:ì¾ë“Òò útÙ6ÊÉ»@ç JiúØÂI}×{ü8ãjc;uïŽ`‚¢¸Ç¿ ÿH0¤SìË¥çÏ<ÈßsOÊL:zöÔ¸doøGéT) …€B@!ÐvÚXÁ½ý€€Ã^:(ÐLAÕ~FçÝ<ù¢I˜á°@*o„9‰Áh ËGöãÉ› Z3ˆ€LË[ktk‚|;XûÍ®“øK{Õ€+po}I­œnÂòå¥:^’Åp8´_2%HB@! P(G@ÂÖÖ#PúáÔ‹=†4V`Û /(™'ÎÖ{K(kÕoåè”ã‡ôò9ùF¡€/F˜v»Èw½…m'o/±¾Gè—ð/ŽKtÒy^ïê(*x¸¾kÕq…€B@! P(F$±5¤¸¬‚½žäÓÎC'èŸ_®¡bրϾÌé^°1åÁÄËØè0úbe»Q®Ýý\»o{¿ÖNß}, Ûîû¢lœ§2sôðùÌ[Îô3}Ê”7´ÔTÿ‰CÛ å0•¼µ=ÿIå3ʵû„l»ïËkÔÚ§´¥:ÈèSdTf K#àÞnÕÙŽ]: ß\Q»ÝñM®žÏm†;ùö|ÍO–'çöÞ®·E.»ž|n´fÐf0õîA­‹¦SãÇbšÿmøÕ×Rnyãøö½o¦¦.*ã/^ø½RS-™)#ßàøµÀ×£ëŽ_òªMpo§ïUGȲRꎴ~ça²–;ñ"p8GÐjÍ ÀÔg‚n V…*øO¢).·Ð×ëvQdx0M5¦ ȹ"â*Oþ´É:¨Ú0Q¿].÷$ *-…@Khl;ÆùTÕ~-ÍQݯðÚ 7ÜÿÔogÌïêG×Aɉú˜ÁÉ4´O¢Ø‘‡TE§£ÌRA{2OÒ¶Œ¬ø½™'žï?iô£]†ô~ä“7þºœ?3Ä‘µÍÿWa¯|š mWc£2GšÖgÇŽUþñÖ] I¼­V+ed¤/¿O£žùй°UÐ3Þo‡Ñê~"ï-çнŽ¡ôƒ9ôÕš´–;)·ÏϑҺÕÊàÜ;Lªm¶zùÃêwôó»fVï¨-…€ ÐèvŒ‹‹Ñ]% v€¿p õ|æ÷?Õ4ߺÄFê?¸zõ힀ãJªàN’ŒE;œË‘¬¶Ä9týË»~þôo?üÛ˯ñe•ݶn-82àÖ56Šâºv{µ{÷~1œD`!éïºÑݧ§¤ 2ó ù†ÙI›•Ð×ë÷°ÙI8ÍåиJšŽp‹ç  Ÿ-ßJ‹U‘ð¦A¨ê ¦á¥®VxºÚ1Îæ³þÊ_\8,ݸ—žz}­ÙqØu |³™¬J¯qLít<üñÙ6Æ îõÛ1wa›o£Ò|6ïÃn<ÏÀÚîøñ³gÏãT‚Ê:¾Ê®ë'0yŽê¶›—ºgï‚é‰Ýî6ßåìÕc}úa*âл7L¡4ßÍ„ïÎÃ)¿¸ŒVmß'ðÎJ.‰€ªƒ. ‘º@!àêjÇ8ç^0$î÷Ê9£Ñ@ßoÝOì­Ì7€©\Ú þ8 S`PÐcƒ{'êjÂe˾%ØÒ±7 }/é÷sJoï,(°•:’þn _ÙŸCÓæãŸ·,—–ßíÔ~Û„ö»”ýzï8|Rx;Q.[†-ðƒ×˜u;ñ„ÌAXF×\6”Vo?Hç9®Æ0žo5gÊn' dc¥Ø+ÓèpÎYn;ˆõêJ³.BA&ª´Úé›õ»ÙCÙivê@4œËpí¤¡ÔPüö%µRÁÐÐø“ÍÓÜ=6–5uñìjÐï{·þ^}eqâ¨u¾|æõcøš õ–’ÿ1ñ¶ ]¨®OÍNIé]ß½¾8îÒ~³»A »T˜Ltùˆ>Ô9ÊIÀqöÇîŤ} ucÂ}„5ÞR.ç"4„F ìA×^>‚},Ÿ å›÷òŸè%ñŸjÖeÃDм¾b+÷$/WpÙ¶jÊèâ 6î:Ìö!q4½è Ìž4Œ.Ñ'\Ô3Ýuð4Œ!|µ…iq^žíù®ñ øs@whö»xåsŽwƒšY›Í&¼ X­ õ§O“‘iãhæI3<Ä'MP€y{Ö,4óuy­jfy|~ÛÂUil«zŒžôæyƒ`¾þé ºçºI4fP¯ç¼µƒ²,Ûä´o…'Œ˜ˆ0ºŠ' ¡ž¬O–¬Mge‹æÝ<í¢K.p¾dM:¥ ìéQnåzë—¯~F<1í•Çoõúÿ æÈ3‘íy;šÔnÇøù¥ þ»Õ*c/ƒz³GB4mÚ—ÍöÖgš4WéêqhçÁlÚÏvÚcõ¥,(.ëH¶ïÎ/,¥ml+.¤O3˜ÍXÒæP?&ò=™ÛœÍ/as…3±Å„NLº„¹KTøAþ^«=ïcóÄ!lÒÞí7ÌQ b4:$ˆ˜“ [ôãÌkötššôéG{Ù6½ÏN?ˆ.ðNØ–×W†N‘a"ͺ~€#7Ijÿ`ÜÚ'Ÿ÷ëßíÊcä¿þL•¡LÀÓùÃñ€¤íÏ¢Qý{ÔøË²»¶÷—¬§IlóôðÜit’{¦k™(C–s¯õlA1=vÛU4sâJŒÃD·‰zvéD?¹ýjÖ·;uŠ ×c¸f䀞ôÄݳhxÿ—ñòò úhé&ºzÂúã#7‹cŸG£ÒPþ"ÑÿèÀÌ•–1_×?•I2,?h-ŸàÒÅIÂyB{Õ;’eSkÏ ;:À[I½x­ª7Ç6pþöóÛ}F¾%$<J¿ùÑ ôÈ­Ó…‚>íQ‡Ö'÷Ýp9k§ÔwÚ+Çw³u˜¨š¼æ•Œªýf]:·eU$Í›ùmÚ5Û1.¦ÏxŒ$†qQahd·)0A[>ƒµÎ’‡àÞì¥ žOþöÑJzíóÕ4Ží¼›+ðB’Èf!ï|µžysAc ‚Ó–UÛÐ ÿù–^ø÷·lND†8I6òêžÐ‰Þûj½ðOÞƒG¹@Ò!Xc_Êÿ·ßþk1+÷³í¸Ó£ Î]?y¸HçÝEëéÕW²­¹Ó쥾2àžúÜ,À¤QD°Y˜G2ývñúîiÇýIÍr(þ8±ö¨À*ÈC<ŒÚŸ{pÛYËsϵ“±–íáÙ½1¡4jsvßGé<#xæÄ¡ÂvüÍœ/(¥±CzË[Ľ‰lG>gKAÃqåØìVÏÊ’„æn€0tmÒ äDÂ1‰?‹…5“†ò—é¶dͼ¸ i.÷ý@IDATòÍkíÖܳÖuKÈÑtJâ¡s–Á~ÿ¶%y4õ^TDÎ….Llj"êúÆ! °FÿƉyGÐ04×U^­ƒ\¹´Á |3¿ÿ×"š{ÕX‚ÂàcV"İFë+)àò¬wb,]sùpêÙ5–ÊÙƒÑóï.¦;gOõ÷ß‹ÖR$›Aað¯×b8þl~½öÉJºþŠÄ^..BÆd0º4½p}¶mßQ:q6Ÿ‡êãé×o~Ión™J ¾ßΓЊèÅÇæÒg˶Åȯ™(ÒúzíNJM; Ì.gwmîâ©‘È­{ð¹D6G°ÒÖ½Y®QVx°X¼f·!ÙTÆCì°eVÁÃù³xT5€>çΈ4Ì ¦DW"Š×¶ß®ß%0€ör›*Ü6c¼˜çþ\í}›?Åí?¯üßzµéä›_¡pÑ×?1šÒ³rÙ»Ô9ªÏ]î,VÖÕ–ñL|±H'˜wãåbR'F*!ò<&7Jƒ8•W» ›BØTÄ}Á{® ÚR sØÍ£¼Ÿß9]p h³Ñ¡†à éß=Ž=§ŒeÀÿLJÏ®è©{f8ŠûqœfŽóÈÜ)âÍHƒ¸Ci¨ â‚Z?Àïàñ3”nå2°ûD”喘׺¥Ýìú¬çØHÄð©T}.¼£‘—á… ë™9gÄ: j(E&/ryé½oÄ[,h½!³/Î~.{ÐÛ VsãòãÉy7OeS–JúÃ;‹è=nd@¸!0MùÓ¿¡gÞø‚>þn£8f³ÛØUO¬h ¾X¹Öî8Èþ5ϰ͖“Ð7”¿HÀ3?xçç3gÎ`ó3W²º^ýÏvôþ†$„L ÅAY)x?玕ªZ‰uÇzò&=­×ê &•Â/Æ$^éù¡ˆý/Z&Ì.îf²‹áço˜BðáZø –RTR&|wŽ  Ž/V:5ÙŸ|·E4Ø£]L¾q/òÛÉ$uû±_ð}{cèÌdSYHäñægß 2{Ó•£Å1”«¸Ì9¤‚Íÿ¾^·St®å pw×àÇS#‘Hg/kþà“¬°yd;wàzîÉ{g3)ºL”÷~ÖÐG†‡pÝXh ï½n2]7y¤èD 3iÛa܆àŒäÑÛ{¯ŸT/ñ µïW;ÆéÎP|˜¼Ž8 Xõˆ¥&«‹Ø½Ÿû·^×}9&Éwc®½Ô5à;øN$ù–×Ã{‹$ßòÖèÓàÚÚ$[^SßqœG¹%ù–×c]_ܯn SwROJínæÎ¨ItH%ÆíÝY@uWÇ•ÖÝöÚŸ vÛ-Ý,zU£Ø<¤¶0ˆ5Ѭ¯mÿ80Yó.nh¦H~³Þ]¸F\ד}_>sÿõ\ÙfÑÿ¾ÝDÑëChÎÔÑÂþÃ8OÜ3[4ÜÇÙt¥7ÛRAÊ_\à™I0άp^ÈuÊ/‘4<\¯ÏkÔæÏ¯n9=“ç%S‘ÄP‘ïKBÕ¢ ¾‚ÏkuP£ro#A»vçl§¦ù ~•ºƒëKþZºQýÛGËèôÙzæë." 2ؤ‚¤[XÉÏ ·Í_ÓU/Ö¼?tÓTyy5ÌAPoÿ°ªŒÐHô­S’y"Ï##‘I `6€'÷£|ð“Ø™c´µÛiÃWóÀ^p@ÅŠ*ÍÞÌ6YìÆ“ÝÂÅ‚I£ÀdÆ„¡âºú°íÆóŒ`%&2”œ¦KvÌ8Hm,È!ˆb0ÿÃ{DÓæÌ³4ŸƒÕÜ9k¬/Šáñ<¢¹Cˆ9­!óWn§3#àõ5ز|þ°ÎÈ:%&§Lš,BôÊ2Ì+ ×>[%vaÇ׉½4Láw=ªŠ¼Ì!~G°–Kºz’÷ùú-àÞ 8y½j…gòZ–ðúQ—ÈaãJ¬%´km!6ž‹³rK† Þ;9˜´à]jù–÷É5ˆên›£'ó\um}Ò¥smÚsÄu:ŸÍd¤xb$f70g„-;FN!pŸ 8LSP,e³œÇNñ(g›¢ u¾‘Ÿäîoœ–"‹Tc]¶5.R;î´ä¿{‘c%Z¹ÅjÅñéJÄ &ŒAìŬ_×(D“óò‹iÏg¨Ï&ܽÀu6ß0;滓¹’âB`ÂÂq?Kà \/4à².‘X±];SGõÙÙa’§ÛܺþÚ«u;/žëá‚ܾõì½®§ùÑMS\ÛØøÉ55áp#ˆ¥˜m ƒƒœ½0\w»#Äbá‰6îöN¿›7Gh~ lêMꯙ4œf0yGÃá~ü½¯ÖqC&LOPÉžcóß½µö1ÉCS_þ(ƒ‡¤ cQ±8+M[Ș Îþ1nä|~⡼• È`õÒ¨[|zQúBc-f~×î•ß1c¬°CÝÆÚ¬E\±À “'íù<ù°gO¦ÙÎÒòxÔÖðAä>4’R¢#‚……Ü¿Ôõc7ö9Œù6-Äÿ~~¥,ቑØúÞéß\¾ÿõV@Ìu¥¼F®mìR0‡‹`žÍöŒ£bÔ±{•2C^Sߺ»Gƒ 8&[öåIøù”‚ÿAKG"·qy0&.ÒO3Ú?£46' g­^){¾šÊ,Ù}žÐÜËüG²½ø–Œ#ÂÕm.[.ûZFç"6º: м¶ö:¡S$ÏA:+Ú"n^j¸“«}m;Þ¿¸óàÃZ-ù…¥pg{n$3¿OÆPÁ®àwÜ'!‚ tŒÛó·¬åï>žíô»‹hÎõîÁâû]Rø¿ÃÕàövrˆ'\’£Œ¢ eY…c0…1–À¸_i‚âþ0E¥ºÝf;ë~¬-oû÷,¡!©KÜÉ·<_ŸFVÊò:¹®`ÍP»#ˆc«<œ?}NÄM€»@Øgß1k<{WYH+X#.½€¸§à9/ü{±Ð÷cwm·³ 8üƒ@_J0·ó~>`‚r{ ÇmÈ`-OŒDÂû &ö»×ó¨·1¹f(·ÏÇ$ÙFg[w”Ú<Øx_ÏxÞ2}Œ˜Kô÷—‹sÁÑðá[¦5Š€_ÁšóO¾ÛL?ýËGt7Oî„.%A•XÊËJÏ—Whp {|)R¡À1ˆ‡…†ŠàqíéÊ#;¡¦b:QPAY<2Ïè …ñuìã»#Žj ¼|;¶(añ—uØK¨àì*-ØBgø?<àfJˆŸ@ááa*ð®ÀŠKà-ïïÄZQÁ&³â=ÉSmv­¸_Ýœ©£h%îyÍV@À¡YýÅg6ªâõVQµÔT[Vʈ%¬‡¾yèvŒÂgàáÀl4Q?vÓ­rP›€3È J‡¸8ÔB  “ˆúÌ"ÜGf÷î3 ,R@’±`ÒaíÿÃ?ŸÕŒ¸£¯=y—¼­Æº¡² av/¼Ñ½\ˆÊ‡ š ðþ€‘+9ÉÑ#‘ðO^—`TöðOþýsºëÚ‰ŒC² Ùkvžcàõ$Œ—üxî•¢#í`(ký$×p¤_[Þ¯;»{drÈ%A@oN kGÎáƒ;{õþÌJjûåFG “0a¯ ­•É7bXÈø Žq%³Øb§¶ÂªäQó –RclDßÑ#ä‰hl®mâ'äu°ÑNŠÓétáfÆÏN“œø˜LÁgij÷SH8ã \omó¼ îÈjgNd¯¯zOxWmZ÷ñ냟ñ[¯çã\/®iPÓˆ–‘ûœ°ËyþÒwµÿ+@¸%w7LK¾\µCL¬…w)Ë·ì#‹Å*"ˆ%ÆEÕKÐåõj­è(Ô&ß­ñÜõi½9‰¹= ü%e”Ï# 05ÜÍ“H»ÅŸˆ6°Yoª€¬+òÝTÔê½Þ|Ø}϶M™c¯œQ¸-#+‚ xµ–Oº´àL̦хi;É9›¨˜àÍÃB섞Aα4fä¦ÞR¶ÑÀ ¸ fž'5‰º%ô£}ÙŸ‘ÕqŽøÓ?qam9XH7Ný³0?”ÚÚo<:¿ †O¿úõxµpgmgTÄ6[xUpÏ!À‘jW–q'¿jî«jãó¦L ‹KM-ñ\m/%øã=ž¢¸ÓôÏ/ÖP%W¤ðj{}h¡\¨õæHf×M&ÜŽ¹Ž« …€BÀ/ðöH$:Ì™L›waŸßØ¿r¢Å‹È~ ˆ*O"A¾y Gî¶ìÌÃË8(Ì­‡ÙcMm—’h꤇‚ ‚˜£CÅRØ|=ÊY ^QQÁr›pØà$áÎk;—æ#èˆ/#Õ2³â*.}ù÷zŽ2ŽHg ·Ï¿¤¼¢­ôõæyôƒ«þMa¦®âzjÕÞÁÞÌÚ¹Ó§>áCò=áµi©VãµéÇP…o)]Ö¥ŸÍL¾“û•#™†›KJ‹¦ršKZšn[¾nÏx( žL¤`‚´âîüjv©†ð¿J ¶€/F"í‹¿G@jSAì@¾1c¸r÷‹—÷ìÝ{æ§ßmê¾ë´ÚîA*A(!RˋɃp£ÈvÌ&à–*-8æt4-¸Äm˜•/é=¦wòïiöÇ´çèd Ê/9@Ÿ|3Ý:í}Š(pÅ\òœ»?É_ûÕp¼#¼+ùÞx³mŠ"àmó½y¥Ôš®­`;ð‘HÜáaé;4OgŽ`Jîn£Âƒé«5»¨”ÝŒ)Q( v´©’|£r¯e¦ÎÑ©Ë'§_£½¿xýèæ)5T’KJ±Í*plƒ€‡ ò]Áöý•"ˆÛˆWÙ‰Kíw{¶ÇhDb„ðò˜¬ óào'Ó»þœztëG«ÓŸã‹ml²•K-¿‰nœü/Ž>Y¤ñÁ’õú)ž_upgÚŸ.\È+æƒè á]) ¸@¨þ<ûæ—t¾*=fB#ªÙfŒk0pD›†Â¨-'»þ$žÿ?5ý3¶ékzááÏ~¾§­î‰#•¡½» ¾‹}ýöHèÔô„Õ …€B@!ào€ÌA« rgy8!và°Ðßçž ±oÛHi|P[°F¿ûºI54á ˜Ò]ž0³`¢)µà0=©äIÈNí·]hÀYÁ%ìÀ% çdÛ­±0›€8A ß言ŒÃƒÜèAwP|l2}µvUX‹Yã]JŸ¯º‡¦~žÒ÷uÓ·ï?ª<šùïuK¯g°,¼àá])Î ´k¹Œ]<Ák;_¸:ƒôì³×ÛãC'wNXŸ•wºœ+‡`î÷?9fLRâ¶m9íñY/õLqÑáôÊcˆITSBƒè¥Got¬ëyòé»gÈMµV( ÿEæ uÖû$O2›ÿV`³™íÜFîßSv$ 0Ý‚]vú|¾~ÛŒñ†ºlÂÚ^²’ɦ(‚xóÄKLÒ”5a‚é(ÏŠŽ 4â àbá}`äÜÇäL%wDwÍ\(ˆwQ)¼ØiŶ_Ñ…üYzΑNï÷é‡0=Ažr^áKš ðfÛe‚r‰w¿®*Á6îÎw”[*éùwÓ³'¸ìüþ½h-¬b¯cEèúåìnðDÞv†ØŸE[º´‚íÀ×rs´è6Äf~ϟˬʦP( " øöáCdö÷§"»ÃPÉİÔ`8ÿuÁ¹W¶~½¨høùó£'O½ý¯~9¤O7}ÌàdmhŸD—Ÿp© ¡„¹´¼ð†#í¾Aº%ñ–ë–Ù¯o¡¯Ò†9)S—Ôƒ†öz6ïû9{B>&nމ^jHˆÓ@¼á‹»ï¬M‹"à—x}¹ç‹8²Ùq6uKFM=@Ü?ÐùÂ1A@&QTRF<áWüé>þnØ“n¾r4‡+Îãè—áò2¿^óßcOœ'=À Ep¿~cªp …€B@!ÐR2Gø½Mw<[Á¤9˜ƒZ— G>=ŸÿË­Å¥0yˆÜµiÝŽC;wž0cöT»Ý6™½rD0‰ÔÃõ¨ÈP£:g­‚Ô&Ûµ÷k]Þ®v% —U{Ç-V«Ž¨£²ÃØh¤ß^×éMkHXQgœï7Dä±g ¹¯¿àx—waŸß.ì¿ñlþFÀŬV»î(ÅKáòÕùA£à¾’}9í(»¢+*-§¸˜pÒûÒ³ÚÑÃ?}^D)›>n¯Š[o>ÑQç!1 ߌÝÖ5îaïµË]¦Ušv%þ¨dj\äáü)« Yñ¿Œíç”x# `­ŒÄÙs)«” …@ÛE@Ÿ2Åt´¸à]öé}Û"3·?f2lùO~á©°‹+¬måå¥ÖU‹æÃÛËû Ù³G¿‚ÃÂbƒƒ#MFS ¯!šÁl6•ųî›SEãk°Ù­¡§tÝ 8ÂÜÂf³VT–[ ËËJÎe>¸÷ÀδCâŒöGž¦g‚Bië:µ„îúïýWÃÙçáø/ßÕ.ÌOðøþDÀ]$Ïaµç¡GÄåku>mÌ@š35EDt›¿býõÃeô·_Üì.;÷œ¥Ì»y*}´t#ýáE"LUZ3(EAQ©^YQá Õ( é\Wš÷{§¥í9’2<—?ú/ë||̘|8½æ-žÛ!¬-"È·ÍJùÛÖk?—ë8ï¼´CÿJÞŽ aÏkà[~>Ï^òÁ›¦Gîû©ý5vQ8„‚Ãôÿ{þ-cѯ¶Ã¼]ˆ?p ÖŠŠSŽ(C¨]ö>âò<„í½Öï<ÄÞ1ò)¡“Óïs%‡¡•‚ã"ñ%êÙµ3=sÿõ´}_ýïÛM½>„C<–—út §¥¬ôl£2Ö)•¯½ »n›ÀÛ^#àu•'Øl¤ÜârBˆæ`vY¤¤eÇöIײ„ÔÝ …€B  p*%¥séù¼o˜|u=’¦½s:<ê‘—wìA4ó‚5"zCh†@À9|Œ8eä$ÛE¾‡Œ6 8LŸ…:'&ßçÓ7hdòݸ67µ‘Ø‚|cD_ŒÔ—?b+Y¶$`î57[?c`‡ñ1î9>xém£åWÙò~›$àznÎñ :ýdOæIâ0°­ 24‡9¹8”p -Û´Gh±ãc"Ù¥YLÌLÛŒú÷èBÛö„……ãø]‡³iÖ·;ÅDì¾/[ëA€#LI²ØÍeÀG/—:‹¤k†M¤;ç+AÀÿ¯Î =xPšF`j¦¬óåtàØÙ?Ƀ¹t̤€#h°ÛÄha‚Ò1QPO­P(œ?¢g¹Õ¶ŒëÅ~Õ˜h/ôIÛùëª}iBI%/ãc ßÐ B;t?Õ0ŠÉ÷/˜Ž í‘ÝJy7~y"ÛžË×+qb $¶’€ËIÂ-›–WV 2³gòe|ñP¾ÇÄyþ÷Â;žýQ%øL›#àx!úšofô>òü¶Œ¬&à®^ek ½q×aÂo(CØô-ÓÇPpS+;câ0ZœšNO½ö9õIЧ±ƒ»ÃNß®ÛEÿùj-û¹4RrbMÕ¿5Š/òduP´wûæ#|½ysÕZ\ãþ£ôMìHO‰÷ª¸È7ÆêØ<"ØÌöÊGœÌVÜÈ#rgwã¢Â” ¸ðTI(m¬±#‡;¬úR&ß]ðL0¤í–þ–ÛS¡D+ˆum hµÝÉ74àà)ÚìÇ÷îçøë‘ÄÐ=SÓ[VïÏÜo;Éç•T#àÎA€3hÁAÂÑÙ‘këÛ/–•þùƒ°éVKé~g}ø\ˆæ°.|é£ÈÑ¿º³0Ÿ÷Û¬ø¯ñBΞ:ùÙ^M{äpv.Õö¹é+´_xôæ³3¨Ûw÷öáµí»7oY9ü¬AƒzaÖ`ZÞ: üx¶¶–ypç!?tY±HÌkdŸì0îÌÒlþ؃XuÚûôå#cª¾ÆEÚq‘o®¾€"faËÓÐ?Ã`ÏQ¯D1ÚC9v¬d€ßÁã¹429Nà “{‰yÇBB=­B@!ÐÑ8–2|ªÍ®/âv-Xp}ˆvîŽ>iéõ™4H…Ö’ J­7ˆ·$߆{bœ”ÐÕñ:7ªN»YܽÍt˶MÖl䥤à‰¯;7Îr_œâî’¼—ߘc'ëf>Æ7'SIÑǺþÜ5šö®i“Òz̰n¸¤~Í¢/ç³¼óŸ|·Ù“–Úä[–QžZ“|7àç°Y 7|»d%— ví[×µÖÒÒ¸ª!ø—r‹6^n{c ­·tÒÿ©pÒß5œµàF¦î¬áêÑù·×4ñþ­ÙI!&Ô#Ö…1ðV¢P(:Y£†ßjÓé;ù&­@ÓW÷Ù±«>ò-áY ‘f(°Q†) üS#4zñO_0ŽêÙÇñ~`0±…8¯s.ä™g,]`ÝËç‹ÔR/?ÆXS` [{` Ì%Q§§ªÌ`…æ½|LŸ˜ùÒÛø£Üo‹kj‰´üÐmçJö¥m}õÔÙú`Éz×Kh‹ ·V™ðK[—úQYY >l ë`‘$\b~Qyb zšB¸#ä53©w†© ¡j˜4v qÿëóWr 2%MF`þÊ4ßðÑl6èŠ>&1or‚ê…€B@!ÐÈLù87vŸpÑö£0õIÉ;v¬kÂã ½„’í'ÚQÅŠ?¾mšÛÉ1ßh¢`§°¿†lƒ-`Ú;¯VàóhwÕr1.;o‰#¯%7ZoÞ¿Hxòå—ì´øE× MÿOʬMí:Ñ66ü‰€±÷ÆåßlÎ9rðÃíûjï.X£û»&Ü_^9p^À-sﮯwn\»‡Ë&*‹ªµû‡^g±5LĬ®u¼¬çð½LÀ¸æ d¯'¼Rtˆ‰bm´›'‘~´t‹Ò„Ër‰5Þ?ðÚy‚úĆRRl¤Àøgé§þÉ¨Ó …€B Í#Àvžg§ÿàv¬Šïhû‚ {lەчÓ_x×4Õ¨Û³x0Ùz2;À8幟WdrÚb4_­ëÅ|O.èÜ4J~õÐoÃx/ÅÅl>ÄAJï¿øv`¿FÝìgù#ÇG ‚(zHß}ú¿Çíÿ˜É¤þÊ—8`Ó¬¤~€ÏËÿYâ^‡÷î\²ú«/¾—Xò=qà |3>þ:%ÀìpiÀù+£Ï [7¯ˆ4AA bâDÁ¼Q\ˆ:™+ sþjaî•B´“DaóýÆç« žozFP¿®QOà |1Ò LPÚÉËV¡PÔ‹ìd¥Œø/Ó´gåE<ú·Á6)qÛ¶y¬¹k&ßWjvûfŽpKˆY˜ÙfCà”'~d9ÚÜ4Õ}—F6ß!æÈ;Ø~ðp*ÿÃöàþÆg/ù0þ4 S`É? †°ÿY„]PÐòù/uù”<ÇÄÉ÷ýõÃï"‡ô馜¬ e¿Üþâ'œËÙj?ß \ðv‚ —v›µd[êÊ/ölÙû3‰c oOì_d_ÅÇjHÒ¦]'3S†ç°QPŸ;z<ÎðwÕ¸ÈC;0‰14›Í‚x‡††P?SEEOdµR¬½”©Zè<Ïw~kÁZvûÏÞQºÓ€žñÊO8¿øù†«AxÁÄUØÎ÷ídâÀ;ÆXOth€/pÞJ …@{E v€çsj‹‚oKZ¿m`‹D‘ïÁ×â›v_AÁKïšoÕí¶­œ˜‰yÊÄßþÃOxûÕ'îÃü•€K 8H#fìX—ºõ@ÚöÌñ3f_m·Û¦0ÑŒ`"¡‡êQ‘¡ZÙÜáX…ÅjÕ1AvàçÄ;kÿ¾õ›—»Áb)Ä,À°Æâ®¿Ä°Æf(:8±[BØ{‘€ØvŽ5àl«Êä»’MN¬d³ÙÉဢ¾œ‚+Ê)ßj #9gÑ ãë#‚ÙlÅß>e æ]Axù"6í/áÎ kDåØ@;u‹ ¤Èˆp £Ð°P'p¾˜¬¸wß‹{êÇOŸ£Œ#'éBa)”” Iîç}±{æ~W6ýp©kÛÛ²BÆÈKTx) £ÁɉԽK'og­ÒïÀÔ`§wrßkóçCÁ×"Qä»Eðyìæ_=hMá_†—˜üÆ™¨þüKï}õ«û-YËÄË ù#kÛÂ$Ø,Cc Ÿ›00ðDBZµðó¯yûû¾CGôêÑ·ÿÀà°ðèÀàà“É ¢îwf­ˆ5Ùía(˜Õ`,. <ç©BÚl3´¬¼¸¬¤¨àø¡ƒ‡23vaX  :0èåƒpV-˜‰ <+ð­×ü„Ï áþÍf&v·b‡'e‚€¿%Nxáf˜„ ûohl­L¾ívI¾ùå O)2UZ)Æ^Fåüå#Ù,•tÞRÂT6÷^(œŸ%)Øxb#[ÀEó¿#Œÿ%áAFÖt ÂNLÂ#@ÂOà |•ù‰w_&w„ù۵ѪmûiuÚ*,.ƒ«3 çˆð° 4û¾ÊíÚy´ë¡ñŸòµ”UPö©s×B_¥îd|Ú˜A4mÌ@þ&Qµ+Qxì¤ílq&Š|·B&ЙFþñ¼¶ã®v)3Äa«ø ¯oòh&^LÌ÷­Á¥ZYI"aÓƒZ²+Iέ‡÷ì<È z:x\‡E^Ç›þ!CCBºÏŠŽ|¥aoÿA Ïä}pÁfƒfÚS¼€‹ Þ†–dšoð‚ªmriÿ} í7&”6Û„öÀê£ø^¯‰Ó …µàLüoQà•'9gžbn©°‚À>ÖÃ<ý‚ËÒÓÑxL¨±63±ƒÍ|hh°Ð|ƒ|GEFŠm˜ŸW¥ýöÞW‚oß"´Ýìú“ò™x÷ï‘@³& V¦RU°W›JåЂUÛ)uû~ºsöÌΔ(ZŠ€3ÀŽã;þ+& -&uØiv6Š|7:¯Ý8o^šõå·M?¶“}ƒÈD§_ø—iê³ól«½–©öGŽÇ“dÚZIªÁ²$1ÇqL|€ÖÛ¥!çmpˆ¼Ç¹×Š¿{ÊÊöψŠÌ3ºs1ÌWGEŽüôÜùå(’d’|Øv©ý†šoñ&i¿ùz ‰ÜS\”zËzV­?OÄ à!<x¯ˆ$Úzé¡ÎǃövË0Ÿ€g”rÖ¢ ÛpÖ2"â(Hƒ‰8¤#qi>â ¼ŒØÎóÈc¢-l¾Ùìšoa‚âÒ~«É—^ùh9Q|ƒv»ƒ¾ßšA_±ßõ„NôðU“U©Z€³‡£‘ý“ĂɋÖî¢7>ûžn¾r4M7¸ÖÕjW!Ðx2F›â°ë x¨¶±vŸ8_©Èw“àòéÅO?dÛøâ¿ ó(ý"cÍþ7棸­”ɧåiJfþJÀñ – ”)÷A0¡áâÏM¹_jÁOTV~36,ôÇ\>rÙ‚ü‚ù•h±[.o˜•/` .mÁÎ7:߸ÔÔ’Ì”YÌl{ób托ù~¯ØsºB@*A(!RˋɃÂ=!Û1[˜€[09“ ¸µà€±£iÁ%.ÐhWk¿Þc`nú`±ÀôxW%žE@j½1Oä{1Êa}ºÑÜé)ÜaT¦ ¡è¶Þ2…à«þ‹•ÛøR®;XÍOh4u®NÖ x·ïp%||³Ÿh°c¸¾wÓ||×™6*ò]/4~sÂh|Êæ°Ìá…pU2âåwLؾT€¥V/¿?pI.AÝ·A$A*A¾¡ÇŸ®.î7Zðeù__q;ûŸˆâ²vº?6¶ß[¹¹ëx»¹< IÀ¡–pqì㸴û–÷ò¡Fɾª·¸Ò!쬼JÀ%¹iÛ¬wjx(„í›A¾ÅÍÊJçÍ*;q©ýnÏÖ( €HŒ <2Z˜— ƒWƒÂ}#wT@¼qçA¾qÏ! É7:‚{2shɺ݂|ß9k¬ç2iç)¡“"ðZJôå÷i½ïb2>ˆFÐ)°cžé ßHO‘ï*XÛÈÊdþ³ÕZöÓ VÄ¥°mø 6OYæÏÅ÷g ¸;n øŸa M.È¥ÔzƒxKò-ÇÙýNÝ·»¼øíA¡q²¿@S4G½’Ü=ìý\ö抬w$6’„K".÷åùæå£vs1q/OŽÄLcŸ‰ÔÜʉ†°Ä[ØÜ‚|;'j‚ A: dz:±qÚǃ„ó„YAÄÅ6s‰®UâYð½Á줂Ga0‘°]AÞ~õevÒL˜¡ ŸsÅpáãÞcfLªL¦š‰eG¹íO=»?ÑÅlºÊõ¼ša#ع6qýú|×±l(òÝðZéÖ'ï+Íe-ø»¬Žû(‚]·Ãù…"àz œR“+íA¸±H­·$Þrí¡¬[žÌë§ÎÝ›°Àlpºõ›q§úH R–k¹€‰ÊEkA|«Á¼‡pÈšO ¸È³JB s hy1éRÚ}ƒtKâ-×¢°íôGÏ»4ጰ‘“2åñvúø­þXøÆ0êRÉä‚7ï;&¼À¦YIó~ð“Ê®1!S}ÇÍDz½ßi)OŠ3™0IˆÍ¡{ÀjýÁ­;ÖcÎS‹E‘ïCØj æ7l¶JAÀ¹iœýÊ¿Cº>õ@Ù©V+Ð%2n+p÷ÇÄÒ©ö¬&ßò¿#ß²`šñšãVì ·¬9ò“ÓÓ/ÈóÍX )¹ï‘uòìÙ™YK•1ñá¥Ë‰±c;uÛºõ¼GoB"²AÙ„€a‘â¾-µ×5°"q‘ûjí]œÚo›Ð~gq@¨’² áÕû¹vŒÔá!åÓå¹tìäYJNŠæUãÉÕS6sU\ ÜS¬;>zìÄéGÓòó1ç©Å¢Èw‹!lÕžz âà‹okk˜\Á‹Ñn·ÜÏz¾U Õ@æN6ÓÀmàX˜Ôúb-M/ün=tçÎFÍf„Ö’½¸$ô[X^÷ç®f£|iÚsÏÁÈÚ5¡Åf¯ð¹¼®Çñtj~æNÓ 6Å€9F;_ܟیׅ“:æ9ÐÉÚoeáè£粦–„ŸoÏåÒqSÐ3^à™‘uÂ9·Ã­ƒÝqQQO^§+mïÌØ{àI&ßhë[,Š|·B¿H€™Á;ÕÑçVoûßV{ àþ‡j%2õ¿»~˜}kÃ~Ý¿EÓwËÚ[Á Eæ­Ö ÖDÀIÀí<ùÒ*ÌOl'Œ}¯Ã¿µ’–#1ô|AIÕü¯èZ^P•Bk# ï)/ÿǃGŽ¼î©‚(òí)$[?ƒ1z1ëE„Ý,÷ᇽôŸ ž­_ªºK xݸxíh¯Î]çóÇ‘‡ ¸Aï~ôhæ^ËÌc kpEX%¾ñ„"sSk…€¿ óLü…¸Ã,.³P‡—Wâ9ÂÏ‚’23ðV¢¨@q`àÉ¿ž<ýMíãÍÝW仹Èùç}O=p®X×h•,^Yy½Üö·µ"à>~#ÚÒ¥<›ñ-™­®;—Ûþº6²+BWÙtßOÄtå­6­ˆ&ÿÚª8|Ñ[ÙJ O Vâ9€gE%Ù‚ëÖª·žK]¥Ô¨0™ÙÙ#¢È·G`ô»D8òÅbW¡4ýJ×¶Ÿm(Þ /Ädø;Â?7kÁéò¬q£ü®º>(8œ‹»|P}שã öŠÌO¤ \a Š"ˆÞyÛg‰¹wrQ©vtùn¿_€F¦unO7ÎmÛ¯6o…×Ñ}Cú)6CùBf­[í?‘Ûþ¸îš–vŽË+ü«²Ufè±””.þXNU&…€7&(0Cpôž•xIÀ• Šç±U):Pä»} Oý¨bs–B<%WÓñ~'¨—?>±"à­ôV ºá5WÖšv{΄ 1®}ÜдòX§ÜVk…@G@@jcÁ¹á ÅÁ$\Ño/½y1Ú æÈˆQ/墒í (òÝþ_<{ÓÙÔw«|R›n!·ýi­x+½^;vlfæiÈž÷àÊJËC­T”Æf›)/Ô z_¹­Ö Ž‚€‹„3õ†vûJ<P•X{>u•bGF@‘ïôöuí|Z6 Tp †Z;àè…ÿ¨ÆBÿ±»$Ô]œ[G¥¯~qj«! ‰akï³ù%”wÁcóÏšüÖ¾ßv€rωûv>I»hr½¡5ðmlÙÔumE¾Ûæ{kv©5ÊrÝ«S²kÛ6ÔþV|½b>Ï:sê/¬ñ‰ãGº$tÙ†·bÑ.ÊZ3Ða½:Ü"à!¤(<ƒÀË,£üÂRWbf“‘žÿñ ´bë>aþr×ìñ®s¾Ú!^¾yÅD„RB§Úy(Gh©‡÷ëæ«"¨|ÍF@‘ïfC×voÔ´c\I9˯éIþø Š€·â[KÂ#)ÃßâíߢºCŒWþIÀíFÖ€»¸2AiÅïFeí{|­‘3¸']6Ì©´‘ÑNpÕh®$|ÿì­™£¯qoÍgUy{/’ožçÇ–ÆÕâ¾]}TmÕ‡€{m†m÷ýúîiôq´W‚:…7úF^¨¸Á®++¸$¬´è¿âžš™í”&Ã%aò–»ëº¶5jZfYÕ×ÌåìÝšeAÞh˜Ýg÷íÖ.›·ó—„ ù`Û}ßÛywäôå7ç\{‰ðà@êÒ9ªF&‹×ì¶ç7_9Šö=MÛ2ŽÑ¸!É´xí.Ø3®›<ŒÒö§ï·¤²ò êÛ=žnš:‚2²NÑÚ‡ég·_%B½½~í=r’™;…"B‚hãî#”~ ‡½u mÚ“EëwfRq©…’»ÅÒ´1ý©{¼ïç‡K¬k vMDÀÃä[nÌ“‹<&É·\7±¤îrɱ– "oÉEk60ºCs #r`žÐf'äÅ÷"¸I. ¤Œø‚¿¶Ûq}•KÂs¯/¯+Â#£FpRâ¿KxÖ¸qñÉ[¶œñUdƒŒÉoð¿œ}ú<í?zŠ.•Rai™ÞáÙþ³¯ž¬‰ùTUïX˜)*,„b"Cipr"õèÚYò&ÂÙ–.GäMü •VežÈ£Óç iDÿ$~ÿ]©ˆIóç+ÓhÖÄ!”M‹RwÒ–½G©?“ó3ç‹Ù~»@ú½™'…)KVNßÛË¥˜¨0‘nTX°¸?:<„–nÜKßoÙO÷]™8§~m ’oT· ÜÆª¼Éxã} оÒ`4&iFC‚A38ÿD|RIã``užÎ^£ÿ§u‡#ÛRZºâÓ¾ºïÆp;rg¥ÇMÆS&ÇìÙ'JpSîõյЀû éòKB;Ùg—„O$mÚt¡[Zë&bò88ÿ#l6˜¡x€ƒp8ý/;¨¢ÂJ©;°†î0–” m^xp!|u‡‰HXUaUÈ!»Oä^ âr }½nE†Ó”QYk9@s¥Ç—Ú6euÚ!Z›îôüÙ‹;VΙtуTZító´Á4¢_’8·5ã(8íÓUìƒxï9rЦŒîO¡¬QÏ:yžÌ&•Z*i"›·>qކó½Ç¸3{ýäáâž½œ.þAæ{³|ù–}.ÒQÔ…€Ÿ"àAò-‰·™Õܧϰð±³g>>›¦8®cõð GTDˆ!Èl®Rø)(~Z,‹Õê((*ÓYÁ`0þæ¡_?¦¢¼ü#[v¾¹~ý7%\lIÄ›ôšI$›ón/+št³.VÜG@7” \fŽÁ. õ&œÒ%áË ÝÓ:ç„''×í}¸ ë½YI¼­V+£Ÿ¤/¿O£&Þý{$-Ý€žñàÍ"´‰´Ë9,úcg(ý`}µf­åNÊí3ÇÓ>ÝÈ`@û¡¤­!0´w"à$Ö!Lžë“L²¥Î>+´à¯–*¹,Tû$ÅÒ±SçÉÀ¡;kÇ{%ÆÒ¢ÕétŠ5èýÞ')NÜóݦ ‚‡륌üí`´ {lªNSm)ü‘o|ð¨<Á‘ÐÈÞpïCWÇ'&ýÉ\üàÞÝô1ƒ“¹³›¨…B3®¤ùˆFªÌRA{xtn[FVÜÞÌ]¶%Žƒþ-¸÷‰gŸýïŸ_ø;ggå¥ $\wŸxé—\©Ç<ü579¸$ätîg*]679ïܧ`‚â¼â E’o+Û¸Z**håÖ úvÃ^‚Fð‘[¦(ò-ñ¯gÎÉ£<¹x-\µƒVnÉ&<ÀUIûF`0›`NÄz&ÎüÿÍø±SçÄC÷e÷ùÂ:~ú‚˜œ “­î 1<ù2› y¼¸¦ ¸\¬AâáqOè¢>'êׯðùÆ3‚|ñvëŒÉ÷¯Gì¥=yϵF&ß|X‰7ÆOÝ{­˜›‚^bþSÎï¤ñœUww=¨öfy››v㦹9¨û…\²¦é-yq•KB¹ëkž,qD„i½ä¶'×0;‘ä{×Áãlƒz€†±)ųÆR€¹ZÛçÉ<Û[ZÀ x·E©é´çpŽ áíí9ÕóÔDó™ÓòMûè·o-¦Þ[Jûy‚%$š'ꇷÍn§Ä8§wØxç—1!wšŸ `Ó.x>ùÛG+éµÏW mTÍÔžBÀ?øý[¦išÝ¾„ûŠb²+³²Í†À)OüÈr´‰%'‚e4ß¡3o»kfdLìSLõoºBSmPÑlÁåÀ˜{&á/Üþÿ~1““ûioåoÁ£kAQ¼v«2Añ´MO¸Ê%á3¬7ù£KÂ`Íœ]N•Îó‚c{o˜T°ÙIaQ Á]ZB§pš;=¥é`ª;nyùÅôÙò­¬åL à`MÙ„·ïâé»/6!A±ï¾f¼«ô˜x)'_ºòÆTžp‰¥¤¬‚‚Íd2V·UµMS®7°H1™ 4ïÆË…ö\Nj?¤º {å±›ä¥5Êâ:¨6­€ÀS6^aÖìó=@¾™· O'°ùŽ‹ëÖ))¹ïKè°Þ}Ý$œSÒ ûSçòõ\þ™’2ehZZj!ƒ_wÃÞQ4v—Œ‹ ì¬÷¨sË¿~«kgÿ*W‡, \ò¿|¾|ø*—„r·Õ×]CCO³-±Ó.]§x=%CB˜HØíaó]Î^=Ö³ˆ"&7L¡4ßÍD„9W ZÎUÛQ•)J3Álc·……Ö ßM)¾$ßM¹G]«h ~Æ8)2Âá òâ×Ð~_víõ°«­ØÛfŒ7(Íwk¼]gžÀž x.x Éá£Ò‚³ïÔFݰ_nûÓZpz\¸$tÉé’Ð÷Q0\¨¹¡¥¦Ú˜'‹1mîYjÇÌöÄšW4Ï©ý¶ íw)ûõÞÁžàíDM¸l>¦¸øÇu;ñè‚M™¢´ Nu·B@!à'Üþcã¸î=ÿ嶨¥f'x"wí·°ýî”p'{’Ò•Íwë¿p¼¼‹àÐG¹4¡€=j½£þ ,Žõã½DÉ5²DÒ°=bÛÏ~÷³—„ü]±KB1Sº$ôŸRjzŽ,Œ^ipúI“š¹vi¿ÙÝ …]=yVø*†W%-G8•X®ePZðæa O2ÕKóÒPw5‰sã®VWu4®½Ý0bÈÇë"߀OpŒê›6cƒØÕ`½$¯£aÞÚÏ‹wÁ À¸9÷̃+ä ¸ÕR^m¯G´cÞ¼4xPñ;QÜï^ kÁÙ%au±„KB¿™}Ȥš€k0d'·óäK+Áüäpæ:?ßJZŽpžˆjçIxŠ€7SÂÚëð6¢ÄsT°OòSuuWîžËM¥Ô ?2á ý\vhªa œc¯˜ÖŒ —âöªp!|xЮuéÞó2þötöó]uZ­Z¼ ¼“ð¨¨é\¼«ø«~•,/s–rÛßÖ <€¿µã”§¶K¬£™süçéÝ4àuóD¹œwàó›5àìzQ.Ã8Â¥ ²ã tYÃ~Â1nè@À·’–#ÄD±¨ØÒò„T .3$H‰Bàbt€ºuû*‰õÕðR‚8æ'O5ÏúÍãÇ.¾ºIGÀ…`[Œ/ÐÔ5<8P‡oj%þÞ¢ŽjFcw.QƒvàìÄâ:YjM3,•Ûþ¶VÜßÞ—. Yáö/WÑúã®íÖÞÐè„«Í#pDÚƒ{4ð ŽêXÊK‡—Wâ9ÂÏÂÒr3ðVÒ< ‘•K{ )±X‘H•´àXÂ#`<+Q¸#p,w‡C|¿ »à-;NßhºùÍW*»_׌m|lX U$Ü`îÚî?Âï·î£Ìœ3Í€¬un‰Š1Œ†„ªw%ß[¼ü¯€¡<*Ò£ê`QŒcøºøÑŽ"à~ô2Ü‹b 2¼Å5ß–. ÝÏ·Ö6;²ËqåíW„0‡&(˜$ˆÈ—V«ƒU¨•x àYÉÃûÒE™¡4YI¼™‚ wŽÂùÛ%:p¬í4`MjßÝg$±ö]î*'Fäû‹U÷q¦¼6+å}ÿ•ñþ/?´V·G-{p!p,ì½Ód6{€/\•F¿ð_úß·[GüúÍ/ŹŒ#Õº..SÅÕžÌK_WçÍ|pÅ–½tðxn}§k?pì4P·®)5Þ‰A3„qÁðžêä¯vÍv»,8×ÕËüÕþe¬ódáÕºõ¸È%¡ÍîZpMw#àäiªùXICø­pó“SwÖƒ€ìè(”zjà°$„PÌÙ·¶CÀG…šÙ^Y£ôƒÙ Ü©N5ôƒ9l~b¦.ìû8KÌ{¿º®}" É·Íî4÷²[C­‹>4ü2u™í¤‡žD[.‚„sGû^•Ðà@ÚÉÁæÜ•!ˆ\[fiüˆZÆ‘S´`Õv¯–‰ƒè¿õÅ**àÀ]­-Œ—|Wrí*ÒçŸÏ52 wË<Ÿî#¹ík¥fôÇ·RU&£Q{ÝfÓ«zsÚ9&<™´iÓ…Ö,²1È‘cwF¬&ކÙbpT>Î…„pÿÑ•„—Þ°ÀZxטƒà(i<žuidâÅÄöß&“‰âCŒ¬A:ÃfÎ)—™‡ò¢+4q)}\o%Úä[£:~ì¦ãé[ÞiœÚ¶ñðI2‡µO>¼>Iqtˆë|÷zv%MÛw”0ÙpËÞ¬%ß´;“¾]¿›lYhPrWŽtõ÷pô£@IDATö¢ÔÆ:É·‰x1S·è 4j´0u'UZyôFI“n‹Öì¤@ Kî":6ÀYbÞäÕ íÚä;"¤+™é—ìy(Ú[v’Ôaíuqp;7œ#ÚîØÌ•×vÞÑ¿§k…¬u~Ézš4²/=Idg¥Ö£¹”_TJgó‹è¦+GSÊÀžôÃk&^ô|ÓÇ ¦ÞÝâ¨sT%ÆESÆ‘“Âä.)Þ°g—Î"/ÜX_>%ê£/¿0˜µÞ³‘¯ÙˆÈüOeÝìl” J³¡óÍpI˜uæÔ_˜ìÆ1yê^å’ðKßä^O.ºÆ³Ît§ùI¥µÅñz®lS‡áÓ¹¹“?wsäNÛáýZl•S³#'ÎrEEALJ”´.Ò1(0‚à*“—¸ˆ`*­,¡Ý™l’ºt Í>Z4Š­[ZÿÏšoïÝL0%FRÏ.1Wà ¸2AñÿwèÖG¾#Ãàt+ÃY¶JOíÅ`6'ÁÿàPö¶Ï$ÛX«ã‰ø pÍûÒ{ßT?{%Þsø-ݰK(âbÂÅ=6»²8¾¤{™†[¿NQÎó8^XRÆæ)›éȉ3LîC˜°—ìÔë“úò©ïz/w!Á3È~ Ú|˜/-~ö¡ŠC^ÊÓcÉ*î1(½“\f¥ ÿÿW#rpº$lUÎ_xàBt‡1®j³Y+TBÍ•¥÷RjÚ!ËÃc7OY#™WÞ_F¸ÇÿõE(ö“yôÚg«èñÛ¦Qb¬SS€,ìúð»”v ›µ6êÁCp³/*<1ÔHw^þ`å–¢wͮ҂i`¯º~òp¡!Øy(ÇãQ+ßc›¼¹¬±áabßÜkãÒQöabhæ!\ïÐЪà¡Ý ö]ßÝ2èŽrAÂóò‹iÎ#”Mxl¾±ÙÎÖÈõ‹¥¡=ãžÀøJ”’P§Ú! “o¯>0ˆ›ûâÕÌdâ˜K2Œ5Ú°ýÞÅmÈýs&ËS®u¶AÓýü£7»ŽÕ·æöß{u¡'î™MÅìWÿÉ|&.ïÒ9R¬Ï”Pw&Öh_Š˜ØKù~Ë>ÊÉ=OÏ?r w„Íô—–ÊS­Ê碋=~ Æ$L‘úKï%ëÖŠ;%›0êÆ¿ðÔQçìé• ЧõBz~ç’GÜåcjð È`õÒ´¤´##Ë©}–wæœÉgßÌ3„Ö;¿¸”î»výò‡3Dÿbó>™ÔEë1ƒzУs§²=^ÚÂ6rY§/ºÆSàmãw^ëQò-qöT;R:Nn`í6kÀY{J¡a¡&¶£))ÄF˜T¾µ`-½·x{HÉQ~«>øùÀøä•ÐЮa4¸G¬ÀxWà‹ox+é8´"ùnUG²9Ȇ‡Yk«S?6%©-°‡½6|v#R,H³ôÝÐ)’Îó9˜—”–W°·>_Z&4ؕܶ-߼Ǖ\רh  ¢•[2„rj%§¥“¤"Fžöq› M¸”8žÌ‰ÿã¡ì\*ã|p_}ùÈ{|¹Öm•Ï3ù–ÃÄkŸžg[ïËü››—Ò€79Þ—„GRFÌçLxDÑ. ôajf¥Sž<À„®Ep™Ns×=Yc mÚ^út‹Éìæ!8„_Gc)‰ä +÷]w™ë²a½»Òÿgï;Àã*®¶ÏÝÕJZõf¹È¶Ü{ï6Lµ ¡c0½‡HB¾?’| ùR!„„Iz3cS ØÆ½É–{“ä^e[]Ú~ÿóÎjV«•VZI»ÒîjFÏÕ½eÊ;sï}çÌ™sójs!)!žzuMÛ®'x‘Ê!5P,ºöÜ‚©Ä%ë÷ÐwbA6 ö—w—²”~ äE3Ç‹ËhÙ¦½´Ÿ§M,1²C—¬¿wç•Se/Àì*q®bòþÔ_ç³”žôØÜKéò©#é˵;„Õ” Ç¢>=êôæ‘Î{_n »ŠXÝ¥GŸy^8a½ºhýà¹7y&8‹.4œ0›‡U”ã‡ÐûK6л_®£ç¿¥É|ÄMíôï™›Æê.Ç\™Q3> Òo”·ó} d+EØ>¬Lj¬Ž/©;´Y.jÍžm¦ÐP&§ÛYÛCÀY÷ªóGDÀ}ó,`ëÜn™¾‡üvðôÝ)&Â{7XX§Ruôé°À/ÇD&òOÞ3›­Ø*tón›=I^^oû É@¨`b·ŽÍSMf•›9—Œ#Hê¿Þ¸‡¥÷ ÓÔKDý)8EX1p;6r'ï&笢ƒEšêÆíViuR¥]ã6¬¦’sÕüØ´¿T·´ò°§þiI½=ñöŠð˜„àÒ$;^£ts ¥³WÖDøbæä;-5UÄ¡~ë2JúÝ^-ùtFò}ÝÅã=àcå‹OÜáùmæYÝ—v·ç7"³Ï%6¨”˜ÙV>)#$ósôä=W ©4îC¸rúhšÉ]¾«ÄÁÚÓÇ ¢i£Šo‘¯0„j.p¶ßpóÌÉä.·&Ö¸4•。ü­;Ïó'V¾X?ù]ÇšPæÌ´&š!L & ÆÉc¹èx–Œšm6Ëw9»ß‡0K¿Ik×iÝÍ%qMCê÷ÎàŸpòJò‘ÃshÁÒÍtíŒÑtèä9ñréë5ê4WèqCªýð õðd»ù<ˆ÷¶µ òu›†ji€´þºcxae õë‘I»Ø™L=̬x}­¾û9&ç_®Ý)ÆBj¶>ô‚s$Úqz,é‰î(ð‡Þ2Ô'âxf »T‡nx“ðLö™ óš.—ûZÌl´gXR0Ï“ÝÄI¿öÄC‘ê#°d¼Ø³ ëvóÌc¢ÄÒn¨ï@ò "^'ýV‹/CÙ.á–vg$ßmi¨4|KB·Ü_À3éK¾½¯mŒ|Ëó±>ª›ÊGÞÊýo_6ÞÂÆ‹/ªÍÃAFÓO‰l¡Ì2¨i+T8C›?8áøëî\ô‡õ9sžÓæÏ¯£Â¡Í¾.u·”ÚßmׯK¸å1™A;þ¡[f e‰w%« ì`=ll2Ü6krë&ò\s{è)#ú±}ñôÉÊmtÞèþ”zÚì¹­;çû9ënCßê)Xa~ç“ë­V÷\¬"a€”ÜB·RLÖÄ›ÛëäBMHÀÚ›€›ëÆt”™‘Þ.xJI¶·~(òœ¶Àû{ë¾CÔ“½o`gl¡^»òòK©´¢:ÔY”~µ”>^ù(_ëú9ì´áÍ?§µ«Ð1 ‚¶ð"%o!`ár9›$|Mþ‘;dǘ$ÔXéªV®ôVpBßǪåßÃêw(gj½³ël@7†{’W·* z(ò]Ž6ýÈßwX8ùº‚½\¾ùéºeÖ^sâ–Ÿaçs_¬Ù&LðÂL-T{±7ç‡n¼ˆ`õëÍÏÖ°šãYÊÎHáû¦ +aPgùùßЗN¢Ï×leOÍU4~Hºæ¢qb¡ô_ßýZxÄ|áí/)†%b¿}ôÆ6•¿-7;]6Z°ì^*¯:&’Ñ]&ÇÖµú£çÜ«ÚÛ’xß«$àÜ­Í& šþ²¼_wéÉx»ìAÀepZMÀeÞ{3/¨¬´X¨†í(«Ðv€#,½¤$ĵ=1•‚B@! hE¾›¨…§!õ=°èß“Nm/8êIáãåyLÎcè™ÇæÐ°~n?\¡8ÿƧ«…ëúÜ:“rº¤³©Þ:³e•ôÖç«iòˆþtÍ…ãhå–},e?"øÄá}Åý×]4žî¾zº'¯Žˆ|²êq:Z¼©6kN_yà÷ìn6Þ bžŠ€ÌöN & Y§ VWC¿°püx·©v(ˆ&tÀݱ ¼Í\ªF`Ÿ‘hâÅkD{ªuÁhJàpŒÆ Ée¢md’Ý“6ìt{LFª{:á°ü5°wWª¶Ø‰®ª±²wèbš>f0%³ðeʨiùÙÒ:sÙcçÒ%“†Ñt^—Ô›}Sì.:&$ë½XÕ:àÙÄoG…o7ÿžvªÓû6ÒMTQ1¼¬£Êì|• J°mÇô|M9~ÀÙßßE`>W'×õ´¶äé!ßl´Ö#RÌ&ÖWÖhËÞÃ4vpÓž)Û’og¹wËÞ#”À/çì´$¥ÞY]ÕS!Ð(ò|Ð7í> Lš~²"Ÿ¾X½M¨‹”W[„ŠH÷°~ÝiÙ¦Ýâݾbó^A´QнO +L¯-^å)T{Ÿ=zº”2R“ı\&Ø2$'ćÕÚ«­ûß¡µ;ÿ!‹Ç*2wÓö=Óø÷!ϱH(á-Øa& 5^„É,\­õpùfUpè´Ám5ö]Ì1²?ÀîÚûæÔ½$"¼¹Ú½øÀoï¡“4¶_¶À*÷óv/ŒÊP! ˆZùMÓBý¤O,Ú×­^ýí/×n§Í{Ò´ÑiÖ´Qô/˜ÜÇïù˦Œ ‘ÜB«ø87½ûá­— é¶wéTWÂ18¾‚¾XÿSOÑô¼„.ð4ð%žcÑQ*(ÞŠ0IÈ”*Õà‡Êl³ “„!¯•îry$àü(·IRo·g<¸«v»¬î‘ÌRpölùÑò|²Ù!¯O4fÜ~›O ±14,·‹ci£:ë¬ê¤P´?Š|‡óâ’r:Àj$³¦¤kfŒÛµ¬—=(·;›$< 2]ºq7 è™MçLݳRÉÄßP„~9Ù”ÄRíÏVoj'øì( Lu 6!¨Ùwø$U³*‹$ì"áÿ;]²‡>ZñçéþîwÍNלÿ.OôÑÕè«Qˆ;G8&ß& ØwMo=—ÒXpáÆ;6–bc™|3iì‘à¢SçÊiþ×r†ÌQíA`þ×y¿Ñ¹é¼'Nà œ%椡®Q(M! ÈwSè´íÜ&Ùqü=ÄâKï€E’{§²ÊjÊÉNgiø!úãëŸÑÿ½ò1ýøÏïRÁ‘SÂ~÷ƒ7Ì ãÅ¥ô3¶xòý?¼Aï/YÏÄÖ;¥Æã‰æ8š1~_¿þ§g±Ú¿0ÈG‹K÷Ò»_Ïe¡[¥H99±͹èUT˜ƒœSx$žóáMÄ”¢#Lê àìj[- 3„¸˜7¦ë>î©c˜‚xÇÅÅò ‡·¸8JO¨¡*›•¶ð¨ýóõ4çÒ bŠÏíê§t`ÐÜf'R¯.©Wà œw¡¶„‚Qƒ¶jâ>uJ! èd(òÚ¿’Íbó ÓÇ âÅ•ƒØjÉaZºqýŽÍB¯Ûj³Ó?,§Õùûi@¯®båÿ=|½Ç¢˜™¿¯î ÑË?»»^²ÜtI½ß7ÏœL×]<žiíòÝ-fÉ÷Û_ÝÌeuËöâLÉL¾_g)~×z劦J­Ù& u§Á#g–ÖÜ"L‘sêÎj‹Ýîù ø¥ b<ïxv— —Ùñqñ”` L“MÉ¿Ï_ÆÓqg¢ ÅBWàó·÷—±™ªcÔ'=–õHxWà xc*(›]w85”¬^[5r^R(:!Š|‡G£»\.öª\-ÔLv8NŸ{¼Kâ]G¾½Ï4‡Ž8,¯„:4 ß±)4÷Ò·);}H¨³îÐô•¼Cá^æ¦Xú§Õ¦=ÅóKܦn“„ýóò¶/‡ú)ŘÍöj·9#6I˜\ÿ¬ÿ_.»ótiYHG •C“É$ˆwbbY-V²Z­dg—W]œUd" eÊÿò‡+hpnW¶ŽÒ›†ôéÚª—ŠÿÒEæØù†©AXI*èÎÌŒaÇ;)”ÄXO h€¯TAñ­iiy•n·ÖŠ|Oªß …€BÀ E¾½ÀèÀèèA½éäÙrút%ëy—UPZr¢ZŸÏf#%@çûoÉ7È÷%o±>{CɤÔ)Ðr*(Ra~]¯µ[Žý3۹Ö$aïššÊÂZLtÜ6šÇ$ïxEU«f‚ “Hnnà‘6KÀùXRb"“oëÙÉáp² &8¼ª!³µ†Jì*dý6MXôHâëS’̬¶Òùº2Ü—WÖ°Ó",’!2ñ|V—8'õL‹£Ô”dJNN¢Ä¤D'p¾°0¼½ÚmRS]…é Žäæ}™Š·C'ÎÐN^øt®¬ŠJY_SÄíNžº×“åóo|:"{f^Ò’(“§È‡÷Ëi`‘!Ôåè¨ôŸ8K;Ù®2žÀ¥·­¶íñpEr°ØÊé ëêêú­¢FC,2†Ñ¿?ÚÁ¿±ÕôÑþIhÿDκ̰7­Bð˜9ua‹ÄÐù¾…%ßÝ2GEbuZ\æÎÇZZ QäÜО& µ¼<{Áø1v~ ÃkNŒ>gN¬6¾­ ´©;uôðª”ÌÌ@Ebòˆ~žË¡E˜Ðÿ†ÄÖÎäÛé”ä[ª©X(†?bÎjªa÷C5.#9ØéÀYK%A~î=IFmDògÔØˆ°S!úØy`!}ºæX`áVÇë¬ä-­x”õ÷z& IÏ-:Xx WñÃPT“¹±XM‰´íFgSzàRš bçXññ‡ï\ÿà÷naIaú“÷\eð^e—°œ–”R^,æ YÙÂ܂řLÀÙj‡ >M .qDj%né·Ûz ÔM CŸ`TOp¾±Ìr¸\GùêÏÁÅTˆ°ˆ£­d»qT_¤Ôj ß‹˜PŽГÍeŽo˾剤ßðnû½g°¹Ì<úàë\tIØðë½NèhûØì§jûÀ[K´ÿwûcàâÒ]t)·?ÞSRÈxjêÊHA`ÝΗhùæg<ÅMJèF7]üzÔ[;ñTØ'¢¸ ‘þ& ‹Æý'«qüuÑ®ïó.$œt­å»¥Ï,lŠ€£(¦‚ØÙÎ;]±7?ïÚ¸‰Ï¼¾x•~ÿõÖSE‘$\M»%¼±”ÀúÍ ßb¦Íæ^ Y«'.ÕP¤Ú 2Œ¶€ÙIÀ <3Z¨—`€SƒÂ|#T@¼qçý}Ô€=œ4l^½üõêê nK²Ön’„+ 8o$Hòàö‚#´xå6AÀn›=©‘«Õ¡ÆÀÀ[àÅëB|“ÇÒãÔˆRG@€Ú Êò­Ú¾±VöÌ»ý.ÛB]ÓShäÀ^~ßWþSRgÂX_mø_Ú¼ï OQ»¤ fòýÁÙNg Š€GaË›b]/³IÂ'Y'ƒÛ7t& ™zWIø :A¥© UP@î,+?_´*9=}Çïçá~çUÓ5) Á”æò@¥‰BLXpK¿B®ó CI›*D¤Ÿ6bc+Ž€·Q“¨€t›„TÜmñøùH¾A¾7í> îÚöÑ–U¼2 ffÜ›…÷h#HÀg ˜uA_¬¬ª¡yL’ïÆ®UÇšF3§K*è­Ï×ÒÓß½V &›¾#<Îâ=„2CåuP¡uÈö—½.èɦeÍX¯]‹s[‡LtÜåpXháÊG¨àèמ åv›F×_ø/lÁØso4E¦Ö¬­‹¯IB^Ò)øÁ®*sÁJ)qv¹ ÍIÀ!*©ƒâÈ^Õgo¿öά¹w Þyâl‰>w惯N¸[ÚËž2™TB\o¡s òí^¨ 2„ÐY8êê–j»õãñ±22ÑF"ÎÄÜŸÔ:ßP;ä»pç¶—.œÿ'‡$àØ£mÐFR…£*x#€þµ+ÏÂ`!a›‚¼åò‰JíĤÄ1ð¾öÂÑÂÆ?,ˆÌœ6Rôß$Ñî—¢ ¬%åÕôë|KáA»$ 2¬×þ›v±I½‘žY¾(¨^§®Bµå,Í_v8[î0¼ïutå´?ò3Φ»:yP˜u ·´€‚‚×JÃ\”){—v¾ajÖN°àÒå°WlZ±ì­kWâ­ˆv€G%ì±yKÀÑf*x!€>†—6&ßX¼n×Aañ:­*´àË!ËÙt#d6Ö[Ÿzpï”}eE™UÛ·_Ùþ«Ø…úEã{ò}×ö:.ôïo“w¼ãJúœOžÝΒ¼ê˜'³i#¿GŽý‰çwg(¥=@˜$?z3kgŒãÞl³UCT“„µf‚œGsp\Q5ôÀ¡k i+†ÀÆË¾ZµkÃú=Óf_y›Ã»ŒIb*¿xõ$¶ê‘žbÖâY­Â7ø¾Ä|û^M¿}?J¾¿e]-v»¯£p²ÃøhN§£âàž]+Ö~ùÙ²šš*IºËøzl°…6AÛ ”ú ƒàÜÒo‡~±C¨Êj«°êá{úÝr`åÝ%'éà±bê×+;lÕÐPFÌ|Ìž6¢åUw4ŠÚï¡“l!§˜ævB…HTEÁ·Û!¶}GLÒ T–< O¤Q("þ`e žC¼LlÅ*XxVÆÄûRš:üV1‹-ghý}»"€+ x€@EâeÍð^]þš({LÖSAi^Åð–‚C9Š~B¤[UUîúêƒwÞçߟ3~P¿þÇ÷7.=™`Öô˜Xªˆ;æ0@U‡ÓaµÖÔ”ÕTVž;´oÏž}Û·ä[¡^)7È6H8È7ªBúÍN¢,<«²€ ‰!}º¶0%uycG๳è(åöÈòHA»¶£ŽÉ>€2ª¶n+Èöß}à¸hoIÔ‚›ShRCßÉ^ºq[ú`Õ´ŠÑG:Ò WhjÚ|ª‰æ,Âfåwe9ãP^ic+Q:}»ùCš>f Í7„×zÔ­Sê¬D\ðæûRÄ^r“„šæöÎ1›T¡Ë[ .È7ßîMÌ­{òó¶žâÈñq'¿ÔÊ\Îÿ8zâµ !ï‰Øvi§‚K ¼±Iý{ b@´¥Ê $ß ßJúÍ 4ÜäËÉ‹/íBý¤„=b–öUh;Àdåli¥Xß•³pû0Ë>€2¢¬ªíÛÞî2`‰çélY¥xưæ'û€,¯÷³"^›"ß B  ”äm%¥ß à Ü áÕµ°Çy\¯B#à#‹…¿rÁfEµ…Rؽ¼ ÁC ™ñ,­¬8oH@Ã)È>€2¢¬*ñ5Û‚‡)Eå„˺8Wõœp±¦y‹V³ ×pÖùÒ¬Éܺ”¢+¦xtµgƒÚ„Ò$¡FºCŠYù5Ô’¾$‰!Ÿw$°ú§½r.îkJ³óËÍâÒO/*)û¤ö:IÀå¾A}Õ@]³Ô'àP/ƒÞƒ„C¿q\ê}Ë{ù Þ`ñ¯£–€Ã½-¡¤$´¤Û{§¦â!Ç‹¬­6v²%ìû‡_W”}eDYU.À‹›AÀõÈ{È7{AýhÙfåˆIÓÌ n¿7Çí„é³Õ;Ä`ûâ‰Ã°¥Ó¸p›ùj¦J­>­Þ ­†.2n ¥IB LÚÜS~`ZÚ—ðu•¦î¤”„Ü:)1ñ¶qBP?9l±¿Våpçã Ýrã¨ Í |圱dƒlg¹Gç”ä›Aðð±•ê0… +( c*‰³Ä<\>ƲQù«ÏÆü5!0IÈß HTEÐ\Ô–¾„¯¡«G¬á{€¸x¨`Óéµ·åãÞ’oEÀâÍþ“ {¹ÑÈMk6!u¸¶[ÞK(l.S€ª° ë:ÌÃEúéÝÔà+í^›$?iâó&1—>€"º숉a!öC—)GL­í ÞN˜VmÙO—LF†Zò6§vom›º¯-¤©©tÕ¹0C & 5ë€K! lk• 'MHë:?xœ˜Î2õ?sšÐQ–A‘o‰D`{lá–H´r/É$`ø­BðUðð÷®¿Þ*ÅZ¸ÙñŒI¬Ã <ûP‘Y™¿O9b BãH'L›÷£)#úË7ðê çrÑ®Š"§õƒ£J"œ€IB$£ŒüjË-:XxM[ËËl8*(uÅpÚ~Àï^I²?ºaë>>)%¶ØKµ µ oìÂÍÔµ|ÄÄ$)gòmµ;¨à¨xÔÅuÕÖ:xül£çÂå`8ãÎeCû—TÒés°,Ú0ÀyÐ7l§ºÚ‚% áð¢’ÏY¸•å œ•WZ"Þ W¸¼ `!¥Šû$p…›zÎáþ¬µµ¶YjÙÖ¨ûÛP˜$Ôu¯E˜ÔâE˜õ*~húôtGuÅ]ò Q£d\íc§KéÅ÷–ŠêC"”Ç^»Ò¢ÆpÙwø½óåFúÕw¯"ÛÒö +6ï§É#úRŸ™¾§Ôï0Cà÷¯I%e°JÂæx÷¬ºæÂ1BïØ_Q¿Ú°K—;®˜Òà’R&àKÖí¢‘r(!^Ù¯oP3ÜÜIpĵ­vÂõùš´m”N˜ ™€çvϤx~ÏE’øÀÐnx•’€7Ä$jÀ$!¿5jõ¶õ ÇÙ–Ê ¡¼Ÿ¥­]„)’pÔT|—ÓHÄÖüÚÚ'oë2™¶Ú+:Ž–ÄÜ:s=rã…tá„Á”·ûmÝÔ/$#ûçÐÓ|§Qòí÷¦0=ÑѸ{ÃÒQe™8,—~xË%‚¨•±Ôõ˵;½‹Õ ~óeý%ZBGáÞ~P?ÁâP8 j«#& €àªÞ»~GN•PÕ3±ÜXêÃ`û³5Ûë Åý7>]ËRX¯ ~N˜0C 0C΋pƒ…@ð‡2Á*™J'èÀ$aÁ¸Ñ 8ᛑ8O¦}Ÿw ÞšÀú ž7 ZÝ—ô3b +J•eЕô[B¡öa„>”u[û,+=‰rº¤Q¯®¬{ºŸ8+¦¿?Z–OÝ»¤PIy5­ßqî¾jªP/X´b+ýøöËY‡Ò@…,¹úˆM¥UÕØd¶–åGÿxq-Û´—öó‡ÒrcŒzd¥ÑWNj ؾñ ¾&3-‘®1VX*hšKœÛ#¯–äÑåJJˆ§îY©b;rêmÚ}Xey–%ätÆV$β¤üÉ»gÑ¢o·‰5 7\2N\÷ÕúÝ´v{K.MbæÃ»¾›v¤ ;Ò‰³e”ÈRG(^2aMÞG ô¾Ù¸—ªk¬4°wWºþ¢1dn'©¹Äûp Ò<œµÕS–ô8v† yг‹¨æ6XC¼eï‘zÕÆ Û·-N•TÐGKù¹fÕß¿ö æ6ºî¢±„Y³Å+·ÑñâRÊJKÏüÔQýEzM½ @²?ø:ö)æÙ5¢a}{ÐìóFð¢H¢y‹×…mßÿkáJñNyò®YMæS¯ðþ€¦*«…àc!ìÀX­&/Sð&ቾ“1FíEY+~®`’0Cþné^ó²‚Âl¾Õ¼¨²lßßùó´ÞÉþý¾ÓÒ²¨ëÑŒ>œ[÷¥WlŽjÆé-ª oœ X{¢K'¡ìôdÖŸtB.©ËþHÇò¢¦ë˜@Ÿ+«¦Óüá–á˵;Øù…‘ž¼g¶PmÁqaŸ—÷ –nfo„&ºïÚé¬ò*ìËûÔ¾ý€>÷΢ãBeÄlÚ¨~žÌ1ðzuÑAŒgO!Ž£OT2iF8q¦”½4îf—ß=è²ÉC „[†&n.ϧ Ç¢ß9“_ÁDÙt|—WYè}&c“8~ÆN2A_¿ã€¼µSîå ’Y[1Á×ÅÐ~ÝÙ–ø1žÛ ŽÑžÁòþÚ"=%úädRJ²™n¾tMí&ÙI q4‚UŒ¾ñÞ¿“ñí,Uwëü7õ.ˆáÁzNvè&ÓM<‹²ußÚQp”ŒÌÆÇ ê%Š„>†¼šÊG\РØí.ácÁ×| “Š˜Ë˜¦ NAa’¥›‘¿PÌV{Íý­MÙ`„¸;°l°õ*(ºþút ÿÐæÏßB² j¯hG–çíeòOkxåÖÏJMò䯌³zÊôÑèõ–²™4L—_1}$˜C7\R_ßÒ·þì™zýX'¶ñÁÅ"½C'ÏÑdÖQM2ÇÒø¡¹,Q+óè#{2W‘#°Ÿu|òLǬzÇí4˜Ûß;ôbûÉ·ÏžL£ †÷)Ö.äzÆXè6}ìÏùƒ<‹‚…n{e³G×xê–‘LV¶î°çà JON`]ñ”Æo0ëo/„?´Î¤ J0$óN‡Kèâï¨UCA{À»nßYõ@öש‰fJMJàÁµ`I¤kFЏ/5ÉÌï‚þ”Á}º““ÕfК{àæóÇdì Ê`rß•×ìå™1Ì¢õè’*Ò†8òBð—8ÙÚ<ã ä(”Ö‚¨î k¼M²ýîGô9sžgÒ Ë"- ÁPA98qÌ4v$YúmIH —[Tu±B  pß5Ó… >¢/¸‚¥ÑùI5¦²ý¹E?|²D “Ã* еLOK-Äo¯ÕÛ …½Ýu,áËDwŒ÷¿Ê¿ñ÷ž8W^ï~ÏI ç1™š5u8ÁºÍ'<òÏWÒ/YÇK„!}ý/ÀCû÷`Õ% Æ$ԙ̬–ò骬ƔJØ2νWŸ'Nï?ÌV>X þ×÷–ËËëìSÕéT1)Z1APAzƒzue©¯“Šx ¼£èaý†:^¡¥m±çÀI¶Q¾‡ŠK+YÅý¬ƒÌ6÷.(癨´:y–R˜ÜC;ŸyÁ_>þ®ä8faàcÁm¥ÎN´ÚoµÚ@ `ªk˜$,Ð’Ì÷î–I‰æ8^øµ[¨ØXJ·‡õÌUh*ª­Býg«€`Á,ˆx—4÷Àª¹ÒôçÅ}0cÝo¨"mðÒãÁYó†öê+P9‘axßîÂìDCò½òƒÇÏÈÓzÜ‚ ¯½‘Û©öËq/ÆôN¿©¶ÈæÅÙX€gjc–Wr[A=ÄÆªß²©Cš{@â¿ðD ë*Xè-TÞ ….:^LX;`gU%ùÈ{Z»—Ä;Ø8·¶<¡¼¯URËPH¥Ý>À$¡Õ¦=ÉÃLîn“„ýóòZdψI·ÝëUÔâ¾ä²ëñ»Lˆoøá^’»1¿iûZíÊE!vÀ®7Bw&ÔPI˜>ƽà*‚Îdõ…Eßn¥üýG„ΩÔá”é}κŻœº¢ Ýwò"¬>¬‡zëÀ 1a‹³eþ«ÐŽ€~¼©Hs]Í,ï›ft—¯ár X©Ü@ x.êJ…@ó(ÞǯŸ›$„¸Çx¸¸'L~§>Ži&tz™Õ^j:䪪ˆ@cDîš1…¯Bð€S¡Ø˜:³zá¼ÜZ–’wY`-Fµ}Ëð äj´¿©Ö¾¹¼Þwy,Ð}áøñ©ºæ˜«éÚ½ºË>ÉýÙ‘aÚ\× †oK¬Ö7;r|é¶²2èßt»=Ð Ôu 6  xÀ‹†[a’°hüغtýç¨îr=Æ»©›Saö!…à逌7^×ç‹ô5ÍCÆ¿’—ºF!nÄ3 +¯¨³©nå‹ÄòÀùKïìÔ°/z"[…8|º,ìËi„>z—ÿÎ_©«Sj‡&Œ™ádÒÍk”n ™Ys»Þ­Lêóº¥×4“6oȺü#|ÒÌÖ$ÕW€¯w—ú¡.þ5õƒ›J-Œ€IBž[w‹òt}K FR\“Vg…è4W¨i®ÇeÚ¬õý^Ÿ¼¼ò·Ú+ÂHãäÏ *-ª±Ú½ØQ>àXYca·èqãp-4ÚeDYUÛ¯•Dûóó”ÈÖ<ä3Ö’ÔMœØ‹×4ý¼h˜Bö·”I÷íXÛ䕆•ãïñzä™ý®º¶o¿¼­¿è».ÿ ×yU´+Š€·+Üá™L²ÜY:œÞ $åá{‡«n©5Kü/¯½óðyc{°â&™Q3¾ ãj¯w$)` 뾆 ”™½QÚsP9¨ FÛGà OŸë`¤Ì4d¹PFÕöÁDÖýÓìôµƒÝ¦r“–ûíð¡×ï?ú3‹ÓvÝ¿æ4úzßÃéäóãú˜)!¹û€Í[ç2ñ^¢=ýt³ß+ï4T\! ” J(PÀ4[c’Ð`pÙ´pmÐ<1yÄwo·èòWK,Öä×ë –~oö½FýV„#’ 0'¶u lS9-ÑÄúÊmaWßc÷ ÇbGT™¶ì=Â?Lìl(™ÉÖg»IX¸TB–eCQV”Yµ}pZXƳMìÌÔñŒ5Õ¾“žÞNVúÝýãÍW¤ i;ÒqCmàè9Vî~ÛhÔÿÓwc~¾<îûÏ×ìNtPNs\,;ÄI£«.E]3 £B´! $àÑÖ¢­¬L²ÐAb/“„M¦¦±õ&y¿þš”(°qž Ô”×ó4 ’~K0Ô>"€ÔÛÈÄ[ ëÇÄÄP×#íeíŽ)7ÝmiDà·÷ÐIÚ3Óƒ1ð·àÝPV”Yµ}Û[I¶nV¢x¶äsÖXH«©î{cfúKY1¦[ ¤§ÉÜ™~»x´„ïÛ/»G›·<Iä[Önä¿õºvÆh*­¬¦¯Öï–§Ô>ÊPð(kжU§e& F—Üšãī̛$à6›å.žÌåÓ´Â>W]»ˆò"F0Ñ6XÕÝ€”~ºÉw ÅÆÆòf¢žéñtºªŠ>ZžÏé.â"¾²í\›ÝI ¿Í§„¸Å®µ1°Îóv.Žßìdyd@Yw9#Êþ½9ªíý×Ì Ùþñ& êž*ž­¦ú@ŒËUÏ>7xº4ëñFÓ¼œ± 2¢¼—vËL[ÅF‹WnÞh‹xºqçAš<¢-Z±•†öé&¤ã˜9øfãnáP n篿x¬Ç–û©såÜ?·Ò1v°•ÎÞngMNpmú\-X¶™N—QfZ"“ý±Bõ«Œr}²b;;MÉ ñ4r@]:i(ù;Ñ@‡AáÃOÄ tÖ"À$!Oü£þ,ÑÎ-,ÜwuSXœ/{aº_¬JgáÄêÒÒ^T:xuh¨Xd r1L A¼ãxz8ŽI¸9>Žz&k„Ýü¯7EFE¬”ó¿Îcü*hBÿlJàÅÀ8ïp Þ}eE™QvÔA…Ö!€çÏÏ®‰ây³Õ\`uk™ÓõéöÛM—=1jèæüßDùöEðÈéR¡Ž˃R›<-8zš.ßB£æÐV{;tòœp¤”Û=‹n¸x/ ¶Ó¼EkÈÅ*9Nv®ôßÅk¨ºÆJ×_4‘õÂ@IDAT†& ïKÙÉ"‹K7\×ßwítAô‘&·yûèly%Ý{õytûÀ@ ©ãâ¤ú×j”¼ÕÐEß¾& ùÉÿ>×ò#5Õ¼$à̱ýJÀ 'Ž›ÅŒ^ø·f)RYrrê<iªã F€—‘îÔ]U»°òãaRýä >.ŽâÍñdæ-;ÅLU¶JÚVpŒèóõ4çÒ J@#Bò ⺭à( ËI¥>Ý3®ÀRæÆÔH6¤—øö”ùLY•¨}NÜöãUÛØîöß$ž›>é±”“•"ž)<[þú€Õf×+ †#¿:täûm¶BÎê,o~¿=%àËä,H(íÀW1a~÷Ëtäô9:SZ%$Ðr0 Ìn¸x8ä^s²gÞͱ4ç’q¢ ÿ÷ÂUtèÄYö5¤SIy5ÝwÍy4¨wWO«Yªâ~ç•S(‰¯?4—þõÑJ*á~l`åùÒŠjqߘÁ½=÷ø;î¹ Ø€‡KRoÝàÍAðhnÝVÔ & ­6íI^,Û0IØ?/o{cI9\ºg…Ÿ¿/AM׫A`Éú¿³—/¯tÿRÿa…€ì¦ä²;O—–Uá·ç €ˆ¡ÉdÄ;11¬+Y­Vêé°ó*ˆA&N—TеŽ¡¾9 œí…Ue;²0Ðùy8URNƒXò9²O6O h€¯TAéÈ26–wc}ew8´½ð(æú¨¶o ¹úÇÐþPÛ:Í’ï^©FÐ-…’¸ý›ë%åUú9—^À仪~Šüi qð&ƒ¡¶oàåUçÄê"Ý(5ÑÛ’"ÑV=‘¡´²†ºeyÔà©Gm¼„I´ =³ÓeTì ó§]§÷¿ª›µÁ‚ÏÜ—LB–´¿É‚„îY©BªµÇë%ÜÆ°Ÿdò¼n=©yãî9%EÀ#¿!Ñc½{­w¼Åµc“„'÷Œý!“fa.½ˆAuijxÒ;Á"S,?ȵ¼N%¥öàöqã†;\ŽËð“$g™ÁõÆ®«½¼¥;Oæ|#âÞ¿[š–º^!àAÀnµ¯¨±jÕL°XÍÁM¾ ,ád 8KJLdòmã©a;0'å¸\[n¤òåWÐàÜ®l!£70» ‹žÄ;ivžajVc°p5Ž­˜Žì‘Dýy ’˜”(ð®À×È$?¼þúÀðÜ.Ïqöžª$Õöwpßö5jÔ?Ý@=YòœœÔlÀ³ˆgÒR]%Ô$Ï¥}ŽzÛy fH4ÇÑM<“â/@=K†¬ÔDÚ¶ŸgÞjCqi…ˆ%±zÔPŠK+…~·øÁÿâbÝ´ïV?Éa++¾áFVe™>º?}ðÍfzû‹ ô“»f²jP,5vÜ÷ÞÖþ–6à»$Ôl´6½H¹OðHi©ºr Â}ßOOÐ ÚL¦Þ¹0ë†ež“µ‘7+ËšmnG%VSì¹3É)¯>pÙßËZõÛEz%ϳ`Ú}ˆ ÓW¯ÿù é1À¦y«PU7¡ïœ€gë‰ïÛ³“•ïwìÛ5`–Û-Öî:,”i†ròWæЪ­…¼(³+­Ø¼_¨¤ôî–)ÞIq¦Z¾i/]qÞHaQ*'8’…›WME‰¼Ø²èøÂií9p’k& ³‡ý{v}àú;,ी 6í |å¬ôÃ5EÀõe–KûîwŸ6;3´ÇXpðCÝ`èÆßu=Áju%[k´X‡Ý£ÒðÖV©“£{–—Lñ›BíuF§#­wÉ™ó½®öš‡­ _Ó¥ÑkZqÐcrUÄ™õê¸8ƒ!&áWß}êW'­vûßmÚñ÷åËb C%oEê–Nˆ€ø°ûéG;Ž{vã΢ &àž§Á#e¢Ko9 ä&f¬¢ƒEšêÆS¹•V'UÚ5–’³^å¹j¾Ö“T§€Öá´0ñ>Av[ ¹¥ä°ðËRê?|.efÎä;-5UH@¡~ë2á*ý– Ö\ˆ5R/¶&QÂSê%5²:j¨´¤† ‘L¡}÷¥•‡=¦%õöÄÛ+¢±tÈĺ½Y±:¥Æ)Ùld]ÿxp™E»cHàgQw9å;6­ƒî·|¯K"Þ.Õ‘Ä0;-‰­öt¼xèv_uÁhA²³eX:¹uÖdН•rßtÙVõÙB|s ëWè¶Ù“Xµ$î˜=™>à…˜¿ýK[×gðí—Óîƒ'Å •ƒpváÁñìóFˆóþŽ tÌŠÁ§BŠÙ$ßLÁ; W¡]‡ç‘•vÒåSŸ`î-ýÏÅ—¾íH0›-”É}ÀÎ}ÀÙ}`IÁul¶€Ë&ÞaDÿ„­ªÆF†ÇÔ:¯Fy|Çå¼!®ç…Ï×±µlžµ$¾©ãâ¦6þsÛ€?E9Éð±` 89sÂÝj²µ1›°¼]ð°lO¡ð3ÜÿÓ_ýPÓ Ȩ(Ó/Û¹…z•œé\b4GxBæIÏ¢%ÃÇe»RÓÜñ£'ñÆŸ~ÿ"ßX,—R“ÆRGî>">îß.\0ÿƇ»ù/Ö¥?yÏU†XèNÔ0 | Ü¿kͲº‰… ¸¥–|aL‚AM¥3ïZˆ<¸àcÚ½Û},¾ò ç‘ÍQL&V«?[µž¯»‡mÿœFe\'ðŽ‘"¥˜êMOs›•«;Ú˜|6РZ„gFZ‚ÊÖQ$°Þ/6¨á¼o€å~].‡½bõg‹¿æã]ŽM’p¼×Û% lhcÆzÿ{ž ;ð¾äÛXJi,Hâí{Λ|{ŸówÜûš–ÄѶXŒ‹õ=’Ùü(÷89“ûö…–¤ ×*¾­$È÷}O=ý8“ï熞8ªÏÞ¾É`râ½£‚?xpBw®]jü|ä}w÷ž¿¹í?6¾õ—çþÂ×[yƒ4\‘pà©ãRš†»£´ôL宼 /h§üßë‹Wé÷_¡gà+‰ƒˆ3 G6ŒÌfA¾ÅM^ÿ€š ò}uþºúo¥ eÉ`âÆk Hø/¯½çÁý ÿûO¶Ò+‚TI‰Æª«:µ Ð0ÊÅÞºfɧëR2Òßàø]ô!éw^5]“’pù1Æ^JÆ`Bj6^|è–~;…œMp xg‚K²]NH?%iÁö;9O³¥˜ké›ÍÿK•5G;bÏ{_Ñ¿?ÙH—³ŠÄ°>×0Ü‘"¡$§ÔaÙµ[ЖàÔ%ê'ÖTÀ ¤Û$¤ân«7¾ÒNHGA¾7í> ìغ8 »ht R LÁ&Ix[%à toGLxÎ{uIe;ðÕµvàuå ¹á¸mA¾á;¡KœƒÒÙj T“0®xOèŒ+à¶ °híz™"àí wÀ™fϾ-1Þ÷¯LV;ä;à;Õ…7­8)E׺v¡wïAëÞw–OÊÑs[_Úž|T$ª@¿€T w o5_¼û懗ϹJ’sOœ-ÑçΜbðÕ w“pö”É„zà‚x³Ê iÊ…šÒRJg!àŒW­DÓ­/¦•™m£Ô” h@ï%´|Ëï(ÿ›¸”g JiÑÊÇx!Øbš5ù¶ÔÐ~dQ  ÿ¤äV´0ëæÄºŠefÔ· ]w&ø1<nlö·šëü6"õ†Î7ÔN ùÞ¿#ÿ“å/ø†“Ï#ï!GÏ(žÕ ¿ËNG¯#.ÎG”åA”ª4ƒz¤ kH°Ò¢|¥¦ƒ´ï§™&e'@-ÉíÔ ÎÍšrÄÄ•t~V5CäœU<üÚ o-cöð¾¹ †îÐùVj'­k$àvùÎ͆·§Ìè:åŠ+<üò¾8%¼¬±I"ÞºÄÕ]ÑŠú>ê˜)Á‡/ûø%óß^8îü§]Ó.¸çù7¾H1 §>qx?mä€a'\JBA&0• ]J½onI¼åžÓÚ Éör6 ¨’piš‰fMù›O»Š>[ûÿ¨¬òˆÀcÿ‘%ìpCÄJÃíÄ{™VÆ‚Çö õº‰]Øù‰…µ,¸t:ì•—ýÁöõkvp™å³'nx&ñ[ÎfK *¿ ºÝb-ñuÄÅùyTP|qÁ‘P¬¡”²neHÕ¾6àM,Rìf²P[Ä*,â∩”19¼ˆÄýý®ŸIþR<ü Òî–¢=Ö­¨—mk è„Ç´Ì{9¥Wx“ x¤¤³m¨»£ IÀ¥|xâ‰Ý¼rù†=y› ¦Ì¼âr§Ó1ƒIB “ =Ù§§¥&jñ¦†nÜ|ɶïïhÏ»>’€Éc¾¿åq÷þ'd ù<2þVü”ÒðOVþ‡½{ÝΊ,^zõo û_¾mîû;Ô€Õ^^àÆWþõÞ·Í}#HšAvád‡±Ä»h÷®Uë–|¶Úb©†9Ylxát{lÞð`pùæ´]pöã눋×ðÆqõ൉1t´Ôª|,¾6àa„%Í`¥t“‹UMbÄâ[,Äm‰#&»ÍzŒ“–í%ò‰ÔŠ€‡WË é÷œ›Äb¢®05¨BÛ`56O˜uþ¬«'®übÑ*NQJN¤$¼í™¨¢ ô Ô g i( L x-ýèýO8þÍÀ‘cúæ<Ôœ”œg6§ÄÄÀ¶Gç šË“b³ôäÞaTÌi0`Ó‚0Ž?Äñ9½¾Ê2ÅT Úè¢|r¸v9OŸqöܹáÍN=3ƒ3$Ùm]Œ.§Çö‡Ã`¬®4Åžfö ¢Ö‚ú„Ç¥#GQ_Y’-ÛöñÆöhÇx§3Ñär&t—u÷\­ÛO›ÁXi7k‚©ÃÁnÔªk*ª+ËKíÛ»¯`çVL…H50HºA¸Ëj7xˆC?À³‰gÏj[ƒ$sØ#_çá‚}[zôí?××2rÏpøwÆ…5Ùl©Â¬'ÃæÏYªcÄýѼõóxäb¸æ©¼7dfÆéVᑦ([îˆéô±£+9ï6óÎ2¢âŠ€‡Ws žœ|GtØù¯âEfiG°½KNÏ\x˔ҼÀñ « ðF@~ˆA 1#ò-ŸEIÎíû·çïå­ˆÏá=Šë°Éë8Úyœ¬Œ+Sãâz¡ÆåN×á·Nžú{kkŸ”j0Ýú]ÙgMa²¢17æô\’m2.Ù9ÿ?ڇǸ@È„¬Ø˜Ä›22îM2=äû˜Õ¾þ½sç:¤~ƒ»¢ÿÀÈjÏÊZ¾û÷?½,ãrË+ý'%% bŽ›˜“ËÇÑë…j—ëd‘źqCEå–3G³¡z7öÏž-ÏóÅq¼§‘$ß à¥µqr<› ËÁz#_A¾yïØ¾a;I—\^Êê0©Þޏø\œ 6åŒ+6ÖB)¼ëAÐý¤:ÒèL³nõ3·™A¨çµÖ7÷Ùe‹>€'T´Ú,¢ƒ"àáÕ|î½Á —ÜÉNÐZvÂOÝ—ÉÆó£¨x¹ïˆˆ¹*I i“¤Ú›˜ã8´k!õöHÈ9.É‹¼‡EwÈŠ‰Ib6OÆ(aCeõg¼k5I«,sÑ+¤·¦_fØpñwôÛx}VÒÍîNÃzRï³a…áýOÞsa í ýâã3oÊÌx8Þ u‘÷[¬Ÿ¿uæÌ§ò·Ú <í2,!¡ï)=cM“Ø aåp‡N5'l¶¼-UÕk7WU ~’@ãyÃb…J<_Rú$ß â(0¥ßœœGšŠ¼!”¹·),ø‚Ç&s}qñ¹Z)¸‘qÆÕÑŽ˜PÞŽRÝH®û†#¦3'O¾ÅuA¡­ÐwdÿáhäEÀëÍðñŽÑ Z÷¤ê*ù!¯Fhi’,՚ѓÍÅú¼À™7…q„¶g;„d/zù8p è@ÀÑŸÀAѧ: ùæºÒœ¬¬‹ÒF¡~SãrYYY¹Š·ùøê+׆yÆ­·?캵G.]i¸1†§^¬ß3d”6òƒÿjó¸Ê¦&'ç^—™þ87@ÊÃÁŤñ¿oŸYêþ©þK²L&ë)SÄ%ÌH2hýäq¹çŽ«—¹\»v×Ô|ûÙ¹Ò N§ìÿò’Pìå³% 0òÄó .uÁñìá<žÅ`¤'É7ò°®þtѧ½}|[cIñuÄ…Œ!Õ…4AJy¥Ò8匫.ÁpÄDºëÜÊ?zƒÆ mì> ÊÜžÿܽ§=sTyùCmlFƒ¦%Å9ê#î”`žÆ˜XLMCú~/Õ €s›É§¡Bô! ‰>øÞq| A@¾½t¾<êŸá®qqñ“.“#ÙUUo²ÙÅÓŒKPBé9'ýí·ô‡+o2|9éBýGlá¯;f‡·îû}PÉ*ó'#Öw¹–ów«hšmMEÅïÞ(>³&(ˆ²DžëÓûo¬'/KÞUã~ú˜Í¶äóÒ’/×—Wò>¸|ïÊgKp,ôxÙ{“rH¾½tÞƒºNñ ÿ¢g»ºšLÂD‰ô#¯ž9÷côã§ï»NnÝhZqûCÎ¥¤ë7¹uÃ)%óË­Õ9ÅÔsiWŠ©2”[^õØ+'Ooá2túpUFF÷ïd¤]}†x°Èމñoîжr§sé.KÍÂNœ^We·½Ý<û๒ž3lRç d {l¡$](ÒG>RòŸ·bé†ô.ÙóùØÍ¾Ž¸øX­>¸ÑC4±ÀPJÁ•3®Z¤Lg|qµÔÓÉ#_Z¾øÃeµm#ûÚ,¢ƒ’€‡Oóáƒ-6˜b ŸbEOIðÑæÚ€$ù¥ˆ£§•¶& 'è'ØK‚à=˜“}J ƒ£þš–fmN¸ÝPk\ä¸ÍþW&q°Ñ’pô€~ÿúуO?èÛ“^qƹ„W™²þ•TÕ³Úå<÷âsÏW’Ì#$Ñ1ii±OtÉœÙ=.ö–xM;Ÿ;¬a™WÙÓÙF=”vœu8Þ}£´òÃNœÀâÆŽ òÝ+Ÿ/IÂ%—¿åùP•å@^Rµ1»õõ‚w?¾ô†¹xžolÌê(n‰¸rÆ<êtÀ ç.NÄ&|%¸]̓˜C•G^뾓ÈÛÓñCE¯|úæ_ãshÌNH ¸ì;ò¶ˆÛ+^M†‡› ¡CI⬰Îј²üHKI úSc}*êûÖ¿{÷¸7™Í¥Ö6ô‰E•eó8Ž™…¬¬,íÞ/»O3™ôôÝSOÐÁçD^Ž8—Õüü×/icì‹ûÁ¼,ASƒ Ye‚˜ðÂCFåcîˆÕ´›¹ã‰IcÉŸÖ]ç]»c¬H„K$ {¹áù’›<Öå•Ï5ú0tÏ=‹«™„Ã×q×´ ïfG\i¾Ž¸P8HL·™BåŒK’jìåæ¶†â팫î5éëˆIw9KwåmüÓš%Ÿ.ex¡zЏhº Š€wô~3®ë‘~/Q'Ú€ð•[’Q·vb$)ÀÁ·?Eý3üôðáÆ.1¦ï뺗A{þõcÇ`&.dáÅb.OIüë0ßAN&¬Ê¡œc©;VÏ<Ì™æ"c¦}§ÿPë´ß¼düþÏv¾²Â„AÂ+ÆŽÍÈ68oÑtý¼@m´o‘¸ºtƒá>~™<Çä{½Œ‡ÑÏ“ òÙ’¿Ûs¼¥ƒj9«…2¸Øך=yy;§ÍþÎ,—Óy);âJebÙ¤#.ÜèëxÉ÷7®‰Ö I¸¬Ÿïo÷uÄär9ËŽ(Z¼úÓÅó+*Ø“^hÍOÊ¢uÈ^ð=:25öêA¦‰cñš'ë’e¤WCu.œƒRí çÖ‰à²u$ièØîˆ7ÝÌD¢¿˜[Ö´³©™Ù/sA@^BN^~ybå™Spâ³DžÈEÓ>Î=×{îFs•Ñ^SüiúCBÍL§ Íàzó·¯hsL¦„‡ž¸§êdH Õ‰êO?m8ðÉÂK\.ý>~ñ^KºÆjÜýäª7 ñPäU³!æÕœü–ñ*jÈÚÈ+HŽbD 3HÁå@ÚCÌ««+¬_/x:዆Ž8 ÷ÀÁ#͉I™±æø´c e«Ð2tö*l±Y¬%ð>z´pÞökwqP7ÁJó“-+i®V< ¶2Iù°·êvó½·PÜôÉTzïºß4z89öµš4k fJýûïÉž·\§‹I·„tæY|`ÌwÝLæ›®¦òÿŠ;÷TÏ.j.QDX£R4aÌ“u•ÒþÒmÉL‡$œ8l—ª3§?åÄ'Öe ½Ò¿ßÀG´ùó´\}䙿¯“sóѾâˆNרíÕçÿöŸÆÇ~ö óíº{#/vpÒ¤¾N§õîÂÅÝÃõëå[–,ÖðñFMŸ—»)9$³¾×¨ß#õ2à(ã#da~t÷–å¼A’r©†ÆQD@b‹A!=ÀøÊE° à¡6?ÉYt\P¼ã°o,gAÞB,©eÛ¥Iÿû#*üä¬>ÖX9š=f;’í‰ÄPå³còg&tAKJ¤¤ŸPæÁ~~ÕSþÓ´9_û§p,îõ®#}¥áÌŸÞbiøM‘& ?2uªÙf­¹žØ÷:Ö‹˜­àÝU/°víF–~Ïcþ÷NÿÍyî•ÞÒðzW«" ‰!¡w\™ Ä&.¦[IÀ±¸Rr@ÂA,ðÇAÐ1’÷r4òƒ"à‘߆ kÀŠiÿyªÿõ&™ç^KZV&ÙW­§êרB˜ÓIÉÿ÷‚;ù™Ÿò˜ÓA¥÷ü€´¸XJxðŠ:‰töi]ôÕ|ð‰H;á±{ÉUp˜´î](~ÖÅTóÆí,q.õ¥gÉúõ ªyÿcJ|üA2Á‰‹lëó¨æ?ïx$ìq—^@ñ7]C†Œ4r¤Šß¾@zy%á¸ù–k‰RRȱyUýXøÖ-)Ó©¦Ê_?O)ÿøCÝ S(Ú—îzJfÊ^î^Ê]µªDþæþÀÄ1cXêû9q»!]–ê:™ø?ÂäûùüøÎSÄG´4¼pÒØ ätÝg³ÕÜÂÔ†”C+f&þfŒQÿOîÆüþ°PÇÛ„ˆˆ¡7I!!ÄâÌNm~”ëßÖ\圽I8péÆÎ|G]P<êšÔ]!C×.”øØýTóÞB¡’øÈÝdß³Ÿl+Ö‘mùjŠ1„ªç½Ãê#XãÀö–˜XCŸ»êO/‘–žJ‰?|]m$׉SdHM¥Ø;n$½¤„jÞþìÛw‘Ö%“Ì7\I•Ï¿D®³üýe"ïÜ€¬Ÿ}MZŒ‰öCrî)"ëWË)fè JüÑCdýv Ùþ»Žbrº òmÈHÇk¸vV‡I|䊛}1YÞ_ä®Dí×Ébª|æE2tм^Úê‡B@!8ãÆ]LºsJíVM3ý)ð»¿ù8]®ø-lŒ³P×Âsü·ôÝœ¿0T"M~|üø¬Ýy;K»ïÓ.–b €Ÿ¸û‚W™ÎëçÒkyy &*„)uÅRXCð&¨È 3PAAh0Ká>¬þû ;·Äý[nÀYÆåyŸÛ£ã§"àÑÑŽÖ¶v#Y~.ÎÅ]>ƒbÇ"ÛÒUä(:$Ž0;¸UPL“ƲÔz 9Žž â ÇãΛÄRðÅâZÍOe÷ýŠô·º‰ë8¯kbq”cÇOÞ–>ã×ÛõìšM®GÈÄù€Ç^<ôª*ªúÃßÅ=òË<<°®Þ Ò°oʧXÎÓ—€{2P…€B CÐÈùSùådR8¯ïÆA_àÈ*.7³7Í×™BÊFÃ#ü˜«ûæå­jIåÃ]®Ï™c,:°&/b¿·šœWóËÑäŹEUyà±x•-(¿Ú'/_Î*tèò „’„ãÂ-õ¾Aº%ñ–{>¤BÈ×ör¾r“ÇšH"òO)ùmè· Ø2èeå¤ÅûY¤ÍºÜ¦‘C…¤:î)Üâ·K¯îòvrìÚë!ßžƒ^¨–$>zŸHÃuî23I¯t«’YÆYpPo¯[È4vù¾Ô¿ü¦î0“z ðC p☉ºS¿D”LÓFcìsÁ.eáø1?`o/pºn"£ÑÑÍÊݘ×jU‹p“†N3e©÷î¿‹_Š=†^ï=®xëÛÌgó‚óúåm^lŒUz­F@’BDôQoÂí¨M"àý±—Ø6yC´T<ÚZÔ«>®@-“8¬÷í ˧_ÔA 6̼ùq×^A†~¹Tzß…ÞwÊ~鹨UQA¦µ³ªž£üé©¶ë̹€-·xݪ¢ …@;#À&¿æÉR×ßé³aÃÏï6FÜ–UÆ=ÃvÅâIJÓvÆÇÄÍê¹~ýQϱVF:Z.Ì(ž=}#Û&¹W·ë4^ m ëÔÏKJJ}/{ùòú a¿AíX:%iìXÈ£+w©·]µRµi'ÔGx¡¤iÄPÒ’“„Úˆ}í&Š›q/¢.¬›ûôbënM¦ã}ÒÀ:ázI©;Ýq£(fø Ïiû–,Ï øë¯$Cv–PIÁ¢OÛºMdè–Í :g é¼!=ïì¹OE ð@àÐä±ÃØ«I‘Ÿn2~¬’é3fÄNûª7ùfÕ‹U&sÒùÁ ßÞå„4<6>{$K˜ÿÁy€@a<ƒ_\oýîíã?ü71ð—žwÂ~â'Z0~Ì¿*Ïž:Á*{¯ò¢Êzä›Ëp’I÷sƒiÈ€ÍùçõÛ´å?Š|ûSVDŠ€GYƒRX±²´;á¡;)ým^ti6S[Lq äßï¼ä KÕÍ.^Pé°9§6–TV°?ãy dœß#/ÞUP(õP¼þC¬ªvê®*«1qeÖ(HMb5št‡ÃQÃÉ Œ½öAÊA%£ˆ>œ.ûÿð/j¦Ñº>y[—µ¥–n;NöæÖ‰æEˆV˘Û7/ÿ㶤Œ{›“†¿úTÚßÎ_Ò§wyMå~‰$6ÌS;Æê9¯Å™ ÿíµvsAÃóêˆB@! ¨C@-‰£c †"¸ìÎÓ•ñ žßò¸Ú·Êx³n³ZÙLKƒ pn‰:  :4}z:W”X Æ6I¿á`‡ý™|î!ßn;—õÝÔñä[Ö{–RŽõ)ûÅÊ™î®Ipx“oKI°²+ú÷˜;`SþÏùöFSÅ ( ¸?d:ð¸Ýj=^”¤YL&Š·K¿‘X Ï8VÇÅi–êªâ¯Š*¾B ÝpTW~Ÿ3Ö;XR½½ï†¼OØzG«òoÔÁNŒafîú¼ XSiU¡üÜÄ*c‹^,˜Ez÷>§{TºÖ_xÄX•ìöƒp,·œŠoÚK£7t?ÜWæ fÍø&«˜œImÚâ'EuX! P4D@IÀbÒÑGô“G­f±¬VØ¥GG—%*òŽÀóð¾=Û¸BxË-*ê§*¡60È:ÌyÒÕ Ï°ëy<7- p°S8~ìïyÿg<ƒâfá`'~j°L¶¨@M\\8~üÈÂqcþTX´ÿ˜îr}Àe¾¢Ë±Dã¬÷Ñ€™{ˆúÛ✴ñü£½ß{hëEo>ºG ±šÀTR(ü# ¸l:⌠†ß~úÑN—Ã~vWNïð:¢Ðáž'ptÚ¬e;6­+䲺x8×îýøª| vG êlñƒl¿Úí@Ó ûõð~K ÑžvZZ6y=“îT <ÄÄ{ƒ®;¶±£œÇ¹ÞYò<t“Ó°tÂêž·%Øbg6f7ü™WŒ·ÉëÕ^! PŠ€"à"úë¼I¡óÌ©SïeuÕŽ¤{¾¡/AæüŽ ¿âê9½6o¼£°æªJ Ö! –þþ¼Û Ó³Úüùxv~ì, ¥ƒ@ ©<ƒ²ZÌ›üJ8Á8_bâ=Ñû~¶brˆ%ÞÿgˆÕúõÏÛzIÿ¼ü·ø¨õ«Æ¼hºt×›¡ð¢é]W(¢EÀëM!$ñÛ… æëvÛÙ%ÃǹìF5ËÙšfnÀÏeµ”­þlñל¼ü`“Dx« Px!PX¸ÿnüÖê¿iÇûvíþº×éf£µv–ñ…³ê.Ö^éßoà}—/·ÔkßK»{Œû¿EÆò+à&â·ñf–¥`Âma‰÷»ìþò~W_ âý˾ëòÊóØ7çESIýÑRq…€B )o ö='%² ‡ÿŸ½ó€¢Úø™ÙÝô)$!ôž ET`‚í½'@ÅúYžRÞ‹D±WŸØõ)6)JH¡HMH @Ò7»;ó3É„MHÙ$»Éìßì´[ÎýßÙÝ3gΜk.(8[¼/mÇ›gý`E¿:Ç©‰ˆñKݲñ«ÒÒb AHoRÑ¢*á*ó&ÖÌÙ™€k@ë·Ý,žU{‡~߯ +VÕýÆÖ•ìÈøË‹² /tÛ‘6µ©VôÆÚ²å¼>§ …~ÐïD¬ZU¢î7´Î2hŒE’7 òAùÈ'ØùZ¾6TΞ玒™0à´vï4K¦øõ~ÄZùÆÎIhÝÿ ÿôn ‹Œì–ºë±æ*ßÖr“5ÜàÖ;ýöÿ1¡ÒÏÖpkJ¼Í˜@m-¶€ ²•Æ×®˜÷›E€~¼I1¤àߤ,ÒŸŸ×ªÅ_ÿ<ø²Q¹ƒ†^zï×ÃFÆœ=-÷>™%ÄžÉæ8áˆâ|S¨AŠvB/\ZÊË‹“׬ø~϶-{­8Wñ$®Ä—8WþYâ§f耥RšU’ iŽºjÜ€ k¿*ÁŠõÞ¾ïÚ"dz ;d‹ôæõ ü¨èæË‚pN°³Ù–ò-É#'&ŠG–þtM o4–ÞŒuy^ôµ â%z|Ö19ùxe{Í2z×+*[ÃëEÃ'˜¨‡@‹p¬÷røCsçÏþùüzÚáöPpÕNJ#þ¡€ÇŽë¶HMIví¸ḵÝGe s•Ñ(û•— ž3îºW2êô2ÍJ“ì 8Ál,/>²gצ­«–o.//-E´âª5m[[ÀYG ÍIô}·”˜ÃÑ~¬9åÝ¥ÌC³æÆ[,æëÐ:ÚYäHü’*Ûh±ÿŸäÒW¹¸•zzåçùùÿ4eÔµ ŠPZÚñ›²’XúþQFIøÞEF…Á0gÊuSÊâ¹¢*CÍ1¨ûmþìçS¬Ô†“äk.YŒ÷f,ýiÞFGÕ.‚¾ëexü /ê’’¶®91Ìk×iË>YÃ_ý<¼¿©ìÌ+¦—–vLæ ‹äÿå´ß2›BË £ß÷VK§N­nkÊÌ®ëªwZiÃãßJ¢s3LÀ¡Z¬€ÓªY2Ï‘J¥ñ(é—•Ö=*'·rô&e²€pÑá"â‹„ðûOß-ÃíµqýFw‰ëÑËÛÏ?ØÓÛ;@¯7¢î”ÉÇ\äa6ã´×•©\gÈC«öyu¿¾µÙ„s†ž?UTZ\XpìÐÁCéí"ëY¶éŽ,ݤpS=´«ñ$®Ä—ÝOBs}ßÑZ*êñûßÜ:\´œðä“oxz?ªà ümì@n >ÆrÉßX&x˜M-vûk n¾Æò(ZlmK½û×É’gxaA‚u¹ ½A*òô¦e‘þ+¦L1GÄ7::¾—˜x/ÝÛ”ÐÅÄ»ÂXv+|£U¥ßº0*ÚÛñFgQÎó›vÛ¶UºGá´VJLüÄëÐÁ“ Ý*ã]¶,{›K_S xXLíNáÒÚ‰þBÔtò u«µÖ:©Dï+—|*ÇÆ‹9’,¼ÙÉ£Ó»MÿÖ’—Ûa­E Å 8Y3&Ïœ}‚B:¡Ð¬€·|äð¥Z‰¤?kR¾ÕU97Þ“vz´JcHùhQóá¦s¥††Þá¡DRo-*Y±¡°p· ½ ^Ä¥š nÓŸ:)Ûdù&å» j›”rRÎÙý!´$)ßwNØÃšÙ94T–¾{â¤gf^[¦/úHÅÈèJW1@W1ÁËd¢ï±»&å¦ÃÊU,]Å^=Uqü‰)3gß·0iÖª†Àd 8$ù¾ŠŠ²;1ºJàÅŽcœ¤ó ½‹º$§ýÕP]Ž<‡}¹&Çt|þ wŒ*:.Çæ„ÎEGO3Ýó»uRÆß¨÷„,ÿhÈé‘åßy^vEÖ÷?—xÿÇ/'ÒM<ýŽsbnE Å ¸BKÞÆÆW§ÎHº|Áœ™ÜŠ c:«*“ôË­*ÕÖŠ9§øµdõ®¶ã¶òC‡kµ n:GúöܹOŠ÷ Ò‰ÝQbaT€ÿí岜»½¨(£ž¨?تòMJ5ùvÕúMp²‚‘"ÎÖo„`DßsI–1ƳðŒ=ês:è{§»ï¹?¡Ó^):/_ý×NˆÊ?ëtßCGŽÞ„@Ÿìc´(ŒaŒþˆ³þËQq}•ð7­Û¦Xâ¥eÒßQ-»O¶È}+Ï©_y¼ò¾ï+AÅHÂR!5•¾ûm–°Oâ;¬¯—Ÿ…K¯‡%Ù<öµFƒnDâòÐ"äøF¦N£Âó½Û/›<ãÅg?šóï·0;ý–sbnCÀ. 8=JÌ6Ÿx_„ywJâ‚á §’ï-§– #õO…þyÔ}R0ÉÂK ¸.¤€Ó8*n*¸vÊ~£$Á—gξúd‡ˆÿx‹B'êÓÄ É&IJÜYR’ƒûu%• ý“[ ñ"6¤€“ÂM ¸ê NÜè<ÿÈ#„æ&ü~ûH¦3ïâ³õ¬H}Ç÷š[‹”£ï)ßúIÏÎT”ï^9'(\¨ÈáBa¼9þù»®*\è¨À›־۹¸à²v—”Ê7¢òmí;¡Tˆ×Ý!Dþ‰ÄϺ¦¦Ö÷»Ðpãö=+ ìO ;Ö±‡å+²ÖzIýÙ¶oC®TÞ À-‡ëÖw¾JÎêöúÓ_þûÒT%ü–+ušûÂj°‹N~\ø#4ë^¦3ŸâãéÛÑBÁ_¢Z°›¸«*—ôkn½MŠ$)•¤|“œ"Ô¥€;"~-d?ŸË{njdØ;zYÁ~ù=öÔ[§Ï<¾¯¸˜ÜHÔ¤^[*U'÷õ…”pRÆiŸŽ«~ßjY<Ä©)(LÝÔ™s>Å2}pçæþ›ªòm¸ó‘§®3<_"åûÆ´­N÷½Ã±l“D7)•¼†Éû#:½nôðœ!Ir; j´jÂ}¼™¾A·(65u“z\k](®%Ë7)ßcŽ®à±o Њ¬ëXRÂçÝóôôŸ½öÒJ¬‚Œ$.€&ÔÉY™€3°ëÆ”YIOÈ’ü&Æ] †ÐIl ·Û¥ <âÆÚÈ"D )ÝêZU¾U_qS»Ž+Öת顈ˆžãC?Å[8jØ(K{ŸÎ:y_fi))Ôj¢hu!ÜZ §›RºiM cË7Bhn"Ë·rs òœÙðÉ…³g’µÊiÖ‘°èê0ŠÖvHô}3ôí;4ðÒëǦµ+-ýÇŸèØòÝt²4qÎqF½î_¿߬|7­¾ÓtÓB>ó…Þ>°=:®gËyU =qŸKcRv~¬Eåû"ZB„G1šK$ú|³ò]ßàÚpœn\FžX§C–:èþ(!£’]uÄà,L Õ Ð…n×DJ8ÞìÃJ?’@^?efÒJtGùJôå8á-BMÖ^Õ’«ú;Ó-ªÕ[µ|«ë5Ø–…'¥§/ÙÒ¯Ï~:âg짃k~ëÑmÖ´=OUÉE<(ÑZ]èFE]Ôc”‡S PœïÊPƒòÝôÂ%ù|cãÎn8bE›qÆìôÝ"ë7=…òöññy&Æâ.[6”äNÿŒëS2éûïf`mô§Õ¤\:<U”%ó —-&ò ïŒ,³ü:=‚µ½ ýŽóoxËÑr &`wœúJa¥0j“¦“`t”ÇÑ-å ¹D’¦ÌH:1ZéõpÍ=NÔð5*ò­Îƒ¼^ùV;³7®Þ—vtౌ®t Þ§|þ·‰c6ôès’öÑÕ©ºãh™¥CœZHúá¤)‘4ÉÅùFÈ'ô3ôÂ¥›û|«d醗~7=¯ºåö¡‚NŠ³ÒªçxÝ4»/†'Œøûcÿòå;¯lê´ê† ’Œøõˆ PƒœìCY ž0üæ{¦^òóg ¶`­¤„kõ°O§¹·&àœˆVýY¿†›¯)³ÁI–kq» .‘¸øãÂÉN¬•Ok¥ÔNÕ·i5kû>TVî±”ÃIKŒ)ññ5îìs¦MsÝÆ)²D*ÍpI“ìpœï­X>ñY¿½BÂÂÇà™â|×ÈÅ;Í"€éQžlðô ÿŠ\T+h³êsP!å IVŠóí vÜ®Zd©Œ¿_PàÕØyõ‰oµ¡Åí p‡]š€ÃpkjUâ©ÖÇx› ØJ`Á„ g…• ËÊìwWÿµ#öš}»§j,"‚­Ýá|ÎK@Q¾P|zÚ[Ôë;û2Æ·f%ÌcJq‘§t^ºbuäæCn(ZS¾Ôk «—©TÂØÖ$''; 8áÄ´LÔ‘¡Ž¾cjô*­]vè-WÁøE¾œ€€°xq…º[pö¿ªÄEoó’Ì„„N >‹è:ÈýDõÿöÒôa~奬|Ûq|‘§¢Ð¡Š3ñÖZª¼PFS±åÓ¯&ÉCL2þô”‰¾k̸I9³3à‹Û™FËeEk÷yTÂÇá ½ä"Æp‘eÓŠô#ÂÜ w½u Ðï%=5$åÀS'ˆ~ž3+àvâ‰/‹"qÖâÿ“r Œž?ù°ãÐ+US¼óÃúŽiõ°w·¹>7% Å87 îvcP ÏÒ úñ˜Oy‰•ðh¡¼tYv|¼/¼±ò|ž ´€)Úªû¢„ãõÇÊw €ÖW´êEr²~ªÌëËÚÚÇUyt®ô²{kCl¬=œðK½ÑÕâ5Иø|ž ØL€p›QqF-èš’²S§ÓM@w%Lþ)•-ßȉ‰|-ka€\[ºÆH) •p׉8¤©a«¼±!ÆZüNW^|óåÀKFù^©ß3-^ì;WíNøâv§Ñv‘¾F'ïX)Êðð…îÈ7f.ûù û¼ÅìN@µ~ÒZQØnwÆJ…hµfMÛZIÕrUɨ¹\JŽ*¶ô«æíRäÎ0*¬€ó¥à”bv¤}„“ü¨ ±Ð§eÆ|ZÝç5pk…€;تJ•µÕ!MljU.MÀ±£*gZsb.I€ÿD\rXÝ£S±©»þƒqÏ¿T{‹–“y™ƒLT÷yÍ@Àé]T$xÝ:¼n‚·8¤J-+^Z–­Æ`èü ä²qÐá¶Éàݵ{sŽÚ½ìr©ß3G‰Éõ26'À x› Ð1±q÷£?øT‹pæŽÏ3âãG¶¤N.Ël#Ðtð Eoƒ÷¤;l«Þ Ã%ƒlË[O.R¸ß ýz®C(ÈåÆzrÚé0>–¢þ…,ÿô}8J¨5UŸØ^0xq*øÄô´>\ï¶g‡(ðîWïy[Ntöt˜0t~ èèÝaÇ$D=ð ür ü|#tñ¿àÛ½¿cãZ™€‹`ÜEÒ]»A1Âuþp+Çw×+ ÕúÝêVOÃààsï-ê aP?|]Tů¼%ó?ù%irIÇ$ÁÏü_üx^6Ôž ´:÷„oUYHqzeâ4|JÐ `ÀP8õãÇpâÓ× 4c_ÃZpVôöÐHȘû8üõø- [Ìyǃ-¨±FÑVå^£eÞa$à¸[b ÍU3kÑëÒ ÐêÏ×-[Ñ ¥ÅPb„ë¶eK®u^Þf- @Š€õÒ‚ªpª¿¡ñàuýÕP±}xýízKË¡bÙoP¾|-z÷ßGï1À‚>y*RwCé{ƒ.ª#tݺ€tò4”¼¿Ìc¼‚>~Šæ¼ >÷ß b‡p(yãCð¹¯Rœÿ ×l€²ï–€ï“SÁ0°¯¢ŒWlK…²¿Á¶Ë”¾x^u9xM¼ Ä °dUê“ ‹Ž{ßy3@@˜w솒÷\¤D­f øù`=¥P<ûuø`^õñ¦oÔx ³éÅ[¢jü›þô£¶XQS¦ƒ)7¼ºtƒÀ# øÐ8õýGPrx/tøÛréµ`)+…v£¯‡ãσó©¡Ý¨ë!Ïé‚¡p×VÈZøXŠÏCà+ ýè›àìš Ó½Ï(ç<Ã;`ð„ŽÿxB¯žÿ§²‚ÚCÑ_)pê§EP‚íRòê£X±}czAÅ™l8ùÍûp>exuŒ†ÎSg€OtO0ædAÖ_Æ2»ktÇt.•ïǪl]îù¿êý&mTF˜±Û÷¬Imsf&ЊØÞа¹)ÇàáŽcË5;†€èå †øþàyåH(ûïW`9œ>Þ£øi[NŸóÞ ͇â×çƒñ—ß!|ä’R(š>,ÇŽƒï´ûª…ÃCÁ?ñYE1.[ô5HÙ§À¸9Y™µJ©ã÷Mx_jÆvŽ@ñKoAÉkóÁãòàqi¥ÅZß«;øþ߃`ÎÀóئ)y'ò-†+Ç+þ€â¤7A×µ3x޽Ø2+:ÅsßÉhª–‰7ê'`@%:òîGA*/ƒc&GûpEñ¼£>@IDAT¹¦©À\t ¶ÿGßý¢”BB¡ º”œ[ódÎ{ ÝSºAèU·( è<¼Á¿ÿPˆš<ò·®…¼+áÌŠo”sg–ÿŽòª²]qî4œüê8ôÂ==Ñ7|Šrœ¬åݦ¿ ÿ@Èúh.œ]û”Ÿ8¢œëüÐ,°”ÃáÙCÙñ 茮&%¿>—(7åãóLÀ °îΣïb}§áh œÈ1Â]l`]¼;eŸ/†ŠMÛ ì‹ÅŠ»ˆ¾_oÎå)‹l4*Џ%ëh 7ôŒƒò•¿ƒt¾Œ«×ƒ.¶ ˆ¡Õ„È^üZÌ7lEåë@%œ¦U”ùœÓJ¾òŸ–ƒyßa°œ> Ò‘ãÊMðÀ¹¤J潦?S ìûeJ~òC·äžEe~;H¸6¥¤¡Ò~‰rŽ?ZFÀRRY æ@Á¶ß!ï¥Ðo>c ìè!Í&¨ÈÍâý;À\˜ƒGBÅÙSŠ‚Mëó;7Aа1Õˆž^ýå»ýÕ»Pr0 ŠìRΕȨ¶r“E»`Û`Ê? E{Szzâ×c€âBrâ‹·Py_¹¨´O*ä~qý…Ü|>έ[ ÞÑ=À#¬cu»µ7BFŽ… !—+rÔ>ÇûL€ \ À.(Xð– è–²sEæà£§ëÂÊîTÇĺÇ]pAæÃ™J¯H©¦$x{)ëÚ†þ}W¿§.øÖÊÅ% ëÒãsJvSrZíb5öɵÄ÷‘û¬ÝR^ˆíÚ\\éJ¢kß,éG…ݺaP_Ða¹À·“.F¥žSË ”f¨®Ä„J¶àc“±£uõquÿÿ00·‡ž¯|¡ºh¬H)o(‘Å=xøUØ„ˆ >Îe&êp[§Xß©\I-?ñ€¾ Ê5×uZbuµtÓàn3¹'«©~=B—‡gÁ±÷_P¬öêq^3&p1VÀ/fÂGœœÅÏŒ%É0‹ºR#ühLjÚkNÞ5ß •Û–$—UúiÍx ÌèFR#¡BEI®hØýÃCŠ1] àþ'¿ï€yÿ©®F**C7ô ¯•È7¬é÷=Që ï¶”¹ŸØš,eÅ`:w öN»©Þ"REE½ç|b{CÄÍ“W’¢ÝÛ lìÐé¾g%žÜ](yuè%Eü»É¹Ÿ”fìW¶ëûðî±Ï½'¿|ò6­¨/gL Š» ð¥à’bRwý[„¯ÔÎqŒp•¯…€åx6_·ÚNq?1ï? 2Zɽï¸Eq;ЇÜ#a@“ºCuÉùÊ ˜†Áý1Tà…ØÐ¦{Ñ"¢Ä ÃÚ+.)‚§TlMÁöÂ0ŽøX¼¼@ âƒM¢Þ¼Ìe'€O\_½}qñóÉëÑõ£„]P¬m}P; ‹³­‰|Ì)™Ðœ\HÚ¾±²(º¼Þ£ø¢“‚îÙÐÚNqËîVÜ_"n½_)Cn.ƒFT–³úôŒˆ‚¸ï+n-%éû”„†\X81&P7VÀëæÂG]€@LLÜ}#ÜÒM»`Úšª¼hôÙ»à÷Ì4Åb]4ç-;wŠ'üã'à=õÅgØVDÆ_WÁß Þÿ¸ È\Më6ƒqé*ð™t;}úøNþ»âaÚ¶J?ûóOÀ6AÐïa—Áj1^;ˆÀÙß¾ÇÈ#=0®öv•‘$#“DN|~± ú/X  —ÛÜúù[0òI*ô~c1ôzù Œ˜òcuY ZÀ¾7 ç~Ðçí!_Ȥ(*RY d¾ö,xGÅBß÷Á8ß›! £¬Ÿºu F_tº!:zÌù¤z è‹>휘¨“@ÍoQYø p^GF ²Âf|ÎÚ›zÿy‚`“’rÐy{Å’7•À”™³×Q™…I³FѺI‡ehŠ¿\Úáñ÷ÇŸ}»GEy¯;·­Ç]Ç%ÁßO‰|bÃ[ðõQ¤ˆ(ÍI4Azð¢ò:ˆþ¾ø¢g‘âž`}^ ‰ÚÄh*ŽHß ½2ýR>y5‰ü,È)ž|4.vˆvDã×Y} ÜûÌÌ%+Î&\Ÿ~A‰m¼x3rà–Î7@ 5h]Z–’bôã®ßåÄ:¿õ6YÓ¥*×ëãê¶>CP¢»“lªéE“íP¢ˆ(ŽJËºÝ GÄàÝŸ¿9ïþ€^lÐÚ5ਮs½NDÀÿ'JoÙîDƒÎ¢6ÅÝXtGÉ¡Ò#\–•áaM¯K0Ö' ÄÛ®5)ÞÍU¾©õ*ßtÒb©õž:^´TŽ;Hù¦¦9Õ"€c@q¾k'ŠHÒå›êiHù¦óæÂ‚‹”o:NŠ·#•ojƒp'¬€»Óh»i_9F¸›‚QïÙœò\¦Ä’˜V”•ÂÓê÷¬Žœ|ˆ ¸VÀ]c¹M$P#|Ã_z§®ÿ<#>~d«áìîE@U h-?|0®ŒÐH÷¢à ÞGâyúDͧ®²vPkÍ®V‘‹d$Y³ü£›]¬I€XÓ™éÉxFZsb.I€p—VîTc„Å‹+tþp þÞï«Ê‹¦,ó’Ì„„•åónIÀZ! ¸Ô–=ɦ[*Œç÷uìÌJ‚. â(™MçÖ-ûqVgÍÛµÛ¥Šj™HFÉb:—ÒƒÇÞ.hˆ¥d®ÈOÙ°–æ¼§ïX5o;5ÁÕ0M`\SÃÁ´&ŽÞš´]¢-|P¢(¤ÐL4æãGŽü–Ù>\8ÜÞ%:ØV ~Äñ\nî7(ƒrƒƒkâ­µT} œ;ûM–g!Ç—Ÿ€´tˆ¡Âòøñ%X}·´| ´´»\ž (Xç Á­ pŒp·þ¦t^µÆ©Ê7MAX±yù/«$£±`UŸÁ’I§oJ}œ·Šq#~øöݹ K~$\UÀTæZa¥Ê£\$+ZlÏmê4J2‹ü@s‰ØCÉdÌ߸ô矱úniõhn7¹¸ˆ+à!áîF Þá&ÐÔÓœ˜€J€¬Ÿ¤‚@ótKK‹ŠwlÝøùYÿXÑ/4NM$@܈ßÁ=»æåååaqUÓª\¹HÖC»wÍË÷në;_ÅcßÄqW³;b¸'yÛü¢¢šçž¾[Z¾TÑyÍZD€ðáã®B ÎáG¿í*ýã~Ø…€ê~@ÊA9.e´ìÜ´~gƾÝ?ïïÐIøeà0™-á¶±&NÄ‹¸<’¾hãŠ_èåKâJ|ÉʬUœdS®’™dÏê&¬í:VfK8’±1+bFìŽÚÿ¿í¬Þ†Eé;¥õkÀÆr6&Ð0VÀæÃg݈@eŒp1Ií2ÇWIðºŠY9Iù2áBŠMC»’ßZ¼2ã/E —¿>ZbŸp¤Ò@">Ÿ¿RBå[>žqèÓå_F®'Ä“¸_â¬E«òE×ÉN}@ERþ¹ûmû„ãÈ5’ˆÑOq$bväÀ_ß®Zü5¹ž(ß%\kýh¤w|š ØF€±ÛƉs¹ ·²sÖ|´ v·U—¯~¢CÄþ·sNÿå&\²›ñ—_9‰:–ºáOim‡DÆ ZÈñ›~GuGì;Ž“Èœ ŒŽí¾·k7¯œàv¨¬ B@y)è%-sQêVLçû`D¬ïÙ_Þн¯€ÚÖù]Û·¼µaÙOËQŒóU )ád%7­C«¾Ò÷îÎÔéõ'ü:Æô?ÖÏëŒoYƱ÷3áØÓ½'Šó}$0’;^*'w.”Èb!>=Z¸ù·ek‘Ž:þ´v¦k€Ö Øëÿ„þ<81&PE@Yž0ᾌÌÃA–G‘… ÅÏÆ671(·'@J!)‡ä§Jн}G ¸ò4qןwJÛqxøµãF›»õ¸z¢N@1ÂGĤ¤tÅ>sŸl&€:„âA>À¤t“ò­*VŠbVVVbúýçÅ¿âñUÝû êÚ¥{ÏžÞ~~!žÞÞz]Ó§N%I`2vBíEQòz}~©Þõk?á ­Ùl2VœÉ=_VZ|6ëðÁ½ÒR¡à¤lÓ —¤|‘FÛäz@\µê~‚¢)©Æ5pS'ßÐñQžOyB‘YJ²R üH2øE䟌èwÈä×Nòðö Ôë}=ñ¦^½VÔº\~m¶˜Ëyg ËJŠÏe:¸ïàî™ØizÊáÌ×€ËwÐñXwÉ9õJ™$‘•XˉxP"V´RM¾ÝĆmò÷¥…¬ž¤|;‹õEU’4%>^šØîøÝ"ÈsÊ-rP‰™œN$À‘2ž,°üôÉþŒ_K*ѸY?%QÆÙ| T6生®~ 8å °ÐÚ!À ¸vÆ‚%ÑŠ~4!a¼Y6m@ÑüP ÊK—eÇÇŠLM-Õ˜¸,Në ¥’JJ¤h¨û¤`’up/\Hù¢ßYÕM¥IŠ×?Û·»º³‡Gw,OIZs>ïT¾ÏTîjþSåBÊ7Ý0/bC 8)ܤ€Ó÷ˆâF牣æSÊMáCeó‰÷%âQïFÅ[À; ‹¿{¬øµ'ŒdÙ·Ë5 y è²×@ÃÝæ³L q¬€7Έs¸1Šžž0h"z†/EK¸NyH©lùýÄoEWR,8¹'U± ¥Ñz›IR(IùF×%ðÀ¥.¼QE|h€oøþ·«™*¾ÝTX¼ ëÓz"”T.ªNî%ê )ᤌÓ>Wý¾Õ²xH{)å†Èö²d™fy2JGz7.~ˆrf~…øÌ]réfÆ^U¾[t h€Í©ãèr×€Í8#h„+àâÓL€b„gøþ“,¬¤!ߘY#ü¦ãÖH¹ åÒZÉ …œ”J²|[+ߪ¯8éÓªN›u'áé‘I>¢HŠj¨rÆÇÙ§^W6ë.¢µ£ÄD]ˆ‘µNŒHé¦5-tNÓ–o91QÜ‘2ÿ~T¾_ÁnSÇ(¡^†Ë¼“ÞúW&®8A74îv¹¨~'OêøÓÚé¯' _ƒX×à °HÚ#@1Â3ãu–di&IW#ühLjÚkÚ“–%je¤<’’AkRÂH¹T­Þ¤x«Ê·Íþ¿ßÅtfÐ_‚eI˳üY\üXVii޲搜ª²Q0UW÷Õóší¹›¤&ÏŸ~Å[ ‰Š÷2^xtÐϧŽV§+»]Öm9é¶Ë\NÊŸÅÖ8VÀ5>@,žvD§ìøwf htE¹›¤Âõ¼ÌÁ²bvìúN;R²$mD@µò© YAIá¦Eµz«–ou]§¨_öéÖ9Úà9]ÍTò›Oe#×gJªòEku!6ê¢ÓlŸ¬ÝMPXu8È×;]ÄÇ/=µ¢–ðÔ'º±hñ5P«^gÝ%”Ô±¦µ:þ´VSNLÀí°îvCÎn.´xqŒðæÂsŸrªRA %Rܪ•·ZÛt¾F5j”0¤(ÿ=d_å„ î;&èþƒÛäÖâl‰X¨Iå¢îkv­¸›$ÏÝM^F±ÚÝ5oô[æFûÍ{7½¡ñPûÚ¬k@³`š'˜S^Íë*—bM#À xÓxqn7'À1ÂÝühz÷Ue̦’‹ ¦¡Ö6šÌ¬xÃgAûù¤’9âŽMðì© î&Mi­I×@S*æ¼L€ 8/Õ'Ñy{À’3V&@1ÂE/a*HŠO.Å—e%FxX+‹Â͹#Ãv•@F«keÂ)[^‰MNKV÷yí8än’<>|F7ùÓÚ×›ÜMt¢8.aÙé¬|½'×̘€Û`Üm†š;jO1[vÓîz¬“b£?8¨1Â}ìÙ×åð}A2Écoý” Â_±¡^tÞ·]/ÉÝ$u|ø,‡ð[<MÕ•îBèn" з_ﶘ[fLÀe°î2CÉim]SSw€(NT\°q«áõ‚°™¾ÜûÞÄ]IèzD¸WX±¢!?c›ëæŒu¨r7ÙŽOàw7XÍ…ü—éõBïøe¹/4âë­á5`L ÉXo22.À. á¨8=|áHuŒð ‡x‹ 4@€\OÐŽñ¥+»ž¨$³fwÇpåZ™hVÀ›Æ‹s3‹tÛ‘†ôsÔU1ŸV÷yÍê#À®'õ‘±ÿqv7±?S®‘ 0æ`¼ùì¸$¨&›ºs>ºþJ=P#|¢ºÏk&Pv=©‹ŠýÕçn‚þ>KÙÝÄþ¼¹F&À'À xãŒ8h”*ßrLLÜ}ø‡¾Ž2ÓË\NîóŒøø‘æ nI€]O?ì¹› YvúFŽnâøqà˜¸˜+à3á#L Y(F¸ÎnAÝ{_Už83ù’Ì„„ͪ ¹,v=qì⻉cùríL€ ´œ+à-gÈ50j#¼…¦6ð‰D.•!þ4 »ž8nØÝÄql¹f&À€,øãRØR¬€·” —gµpŒðZ@4°+ÈB6.‘ØõÄ1£Àî&Žáʵ2&pxD™ˆï¢3M8À x`qV&`+Š.ˆÂíÓ™ÊpŒp[É9,ß1äð‡æÎ­Ž÷ì°–¨˜]O€ÓÌSìnÒLp\Œ 0&PþCð¿ kráZX¯„w™€½Ħ¤-çáö¢Ù²zt¢î7 Q*•Æ·¬¦–•fד–ñ«]šÝMjá}&ÀI€þCè¿„þSZÚ+à-%Èå™@8FxpZñÔüÙϧâ$ã'Ð}w+6[£)v=©£E;än’r}øB0ËâÓ¥xµ2A€t(Žãè&*^3&`OÊþ—(ÿ)-¬˜ðäâL 1U1¿Vóáx^æà#\ÒZkYxÙ_7uFÒå­Õ¤Ú¶+H&ùcܯzTØÚáEõ<¯m#@î&É7„MÉr™N¦pŸJIA(H Œè;xé©¶Õƹ˜`¶ ÿúÁ0ß¶½Tý9Y¯Ÿ Ÿav!P#ü^ŽnœÍ®¤£¡ã{h!ÍÂøìïNI\àÓ슚Qð"×LV¬06£*·-¢¸›¤| |ˆVï ¾ü<™ŽÛ^Üq&ÐZè?ƒþ;è?„þKìÑ.+àö Èu0FpŒðFµÂéÄÄ{˱™É¸ôÓ™OÉ*Ý ÍrÔ“B®án"Ã`µ:v7QIðš 0GPþ+ð?ÛèƒËäªÿ’7Ù*@-–’+`.B sÄ .²QÚŠ/qDP—P‰8"{úë¶eK®‹tQóݘ2+é Y’ß@X †ÐI §–:JhúáÎL¸ÇûÊÊ6Ðõ$¼Ã`¶~7NœÜMRR?˜,HÂÜZï2œxöåÀ®¯Ä½›ÎOGÉ9˜h&åi)l@ž€‘Íž\8{æ[ͬê¢b¬€_„„0Ç8?Ø,›×c+Š?0*‚ÉÞ‚nTdjªÃAÇöÈùj¯TÂá5Täöb|ðÇÌ™¹Á½Èˆø0*áïSÝJHJ†Ç&§%;¢-Wª“ÜMÀ"€7.Õo¥•î&ñôñ®4ÚÜ& MäóMn'(]A„§í©|SY׿¸³T.N #aà8 þ *gºÊ® ¿ÄÆÆÝŠ®*JÜpï¾&º7eæìkPPÉëŒÊñJ\¾}Ä_ç?ÿ|¾=¤¨'– yÖUy£%scSÓ¦Û£nW­ƒÜM@¶¼²ü@õ –ØYr7ñ1~ÁÒUGžûÅ´A€â|W†”ïÆÿçëð·' %›¼0iÖ*{KÈ ¸½‰r}LÀFéƒNÁ)z¨ÙññÖû;üuŸ×Ž'˜ø‰×IÓÉGp’žÇñ†¨þØJø†ûiY³ñDZ¸%üsËÚþùÊË‚ç}üJ]~MªIQ¯äT›½ŒÔïtj‡Á§S¢õ– ƒzÞ"ê,ûÛ÷;ž9"Ë,è˜ †×L€ Ø•þ¸ø)³%ã$;h”)l-E;¡.íåó][`VÀká}&ЊÒÎI®¶ŠŠ‚ðLLjÚk­(7UEà¡Ysã-’åZÜí‚ x$þ8ú7Îðôý‘—ú+ŽÊËèçòíËvfµ+jn}®\®cÑqÿ‘'ÖÇ”ç×àãßé즨QéžÁìçíÊ÷ h€*àE¨€g£(Çh’{Äùn¬[¬€7FˆÏ3¨|IoЗ¸¾‹šÁ/$…æ¸#fÇ®ïØ,Wí@ìzb\v7±çbLÀ5 Г?NL€ ´ŽÞFàÔ,ÝPÕžp'&¬Ã jÎ)«mh2|Ñé?<™ŽS+ ͘@ °¼‰À8;p#£Y a3À{Sý苜'†1))Ñ×éõ¤a® D7ùEç/Yþ˜s¬áø,`LÀ5°îãȽp#ܹ‘]Oê?v7©Ÿ ŸaLÀ= ° Š{Ž;÷Zƒb¶ì<¦ýxM‰¾obG å¥Ë²ãã[uÚt ¢Ñ¼HìzR÷±»IÝ\ø(`L€-à| 0àáÄɈô,KPVôë·`+·Ÿp‡ÝMl¸p8 `nK€p·z pŒp-NMÙØõ¤&*w“¹8™ÎýÚ«ú?†'өɉ÷˜poì‚âÞãϽ×(n;Ò‚(¼¤Š'Kò´ÌøO«û¼Öv=¹0µÜM.Ìd)eÝä'ÞbL€ jëã`L@[H¹ËLàáÚ•šÒ°ëI%v7©y]ð`L 1¬€7FˆÏ36$ O˜à‘‘qx†'¼¢J £ 诊MMÝÔ†bqÓH€]OØÝ„¿ L€ 0æ`”æqãRL U‹Wèàf|Xµ¯ªAOó’Ì„„­"7R'ww=!w“”ëÃÉrY°»IW dL€ ÔO€-àõ³á3L@38F¸f†BÄ]ORo†J÷û&spQžL§ÞaL€ ÔO€ðúÙð& )Gãã[dóŒ,áK‚ ${ ºQ‘©©¥šÔÅ…qW×v7qñ ›»Ç˜@«`”VÅÍ1æèššº#£LTâLc52ÈCJeË7è'®k~­\²)ÜÑõ¤Aw€Fô¼ôÔŠ¦pä¼L€ 0w'Àpw¿¸ÿNG =aÀTàCUpAÞMI{DÝçµã¸›ëI}î&øôe‰è)>>øÇœcŽ£Í53&À\—+à®;¶Ü3&?ð%´Æ>¯vQ„gbRÓ^S÷ymîäzÂî&ö¿~¸F&À˜€5vA±¦ÁÛLÀIĤ윮(_«â¢2>/sð€‰ê>¯íKÀ]\OØÝÄ¾× ×Æ˜¨[Àë#ÃÇ™€Æ pŒðÖ wp=aw“Ö»ž¸%&À˜+à| 0'&pdÔÀ K!lÁW2{Q7òÁ0"&%å wKS¢»ºë »›hêrca˜pì‚â&ÍÝtMÑëÒ D/a,*Þ§¨‡›9D–M+ÒGŒsÍ·n¯\Ùõ„ÝMZ÷ZâÖ˜`ÖØnMƒ·™€“àáŽ8Wu=awÇ\/\+`LÀV¬€ÛJŠó1ÈH8ƒƒÿ‚VÛª¸àÂ/±±q·âtö‹®Iñ.v=—cSwUGžÑ¤Ð¥¸›H–—ñYÉ}8¡Sõï?>AIñ1ŽçÝ@>͘°vA±H®† ´5Œ¾\äiäoÌà8 be’%yZfüÀ§Õ}w\;“ë‰ên‚Ê÷Á‹¢›T¹›°òíŽW1÷™ 0g$Àpg5–™ 4“)œ#ü+\ßIUà€[ÇwדzÝMágÑC|bð9ÇšyIp1&À˜h¬€·tn’ ´%yÂŒŒÃ«P÷¾¢J£ 诊MMÝÔ–rµvÛÎàzÂî&­}Up{L€ 0Ö!À xëpæV˜€¦92ØTZ¼•ð^$º¦ä ‚aDLJÊÁ–úô«¯ú–XÚ‰¹E6·e¡ˆB ¾à¨ÃFtX·ˆ6we-€T!bå‹Q.ÂãÅ: L¾pbásÏo‰•UžÄ\“©Ì+ì‹ ï0XX±ÂØXÙÖ8Oî&©ÉNÅñ™ƒî&ÁÕm Bþh¿íÿjܻ隵Z6Þ`L€ 0› °n3*ÎÈ\‹@Kb„?˜”ÔÑRÃ0^HôcéŽá {"ôD¿ò {PBż•ò“²Gñöàˆ,È»ñÒ4ƒ!x÷‰ÓŠ[ÚFmל<o>vmoi½ö(Ïî&ö Èu0&À´M€pmKÇJ }ðàx,ëÑìK ${ ºQ‘©©¥Ö ?˜øj˜l./ÉÂå:Q¸Ò"I+Ï r ¯—%¼}â/¶òOðõö?Z{y‚—ˆ¢ˆ ÖŽ¿8"jêêÌ +L¸˜¡×´—–C~Q)–àºòΗH9ç åŠ YÍ1 2V•‰Šù¨¤¯ðÐû­y7ñ±ÂÊs¶}Ò‡T.íÅÜÊtó(Ê˱©»ž·­´ãr±»‰ãØrÍL€ 0­`\k#Âò0V&?p<6¹Ý2T%÷—ØØ¸[»ì2}Enñ ² M®£ó~>^–nQáºØ¨0èÖ) :†…€‡¡ª˜å>WP'róá$.GNž‘=%W˜Í¨Ó fY6£åýc¯ð€ïÞ}ì±Ý2´èzÂî&¼p¸j&À˜€F °®Ña±˜@k°Ž.¡…z}ϾëR¢{ö“d©]P€¯å’ÞѺKúÆ@§ðÖ«Þ¶ÐOŠ8ìË̆m{3-¨ ët¢xÎò|½îùÏ?Ÿ_Wa­¹ž°»I]£Äǘ`®O€p×cî!°‰@Fü€¹GCž[Ý?ÎyùB.ò¸Ë ¸¶©|[f:xìlÚqRöEñ ú‹ßÿaÒôeÖ25ÅõäÑÄw,Pf2YÂÐÕ% }нt‚l”%ш.;å 茲NÂmÝÙx.+1Q¬Ûjl›ÜM,eòË‚,ß‹î?Õ¿Ãè“Γé4FÏ3&ÀœŸ@õ¿ów…{À˜@KLž5ç>A’ÿÑÎ_˜pÍPèÓ±%ÕµIÙ¬œsðíêmRÆñ\ôN7&Íú?¤>ד‡.½´=TÀ%’ — ½0iÆëªëI™‡ì%mŠë½ÉèéÓË,YB©Oh5—;G„ÈðeÒ_/ð÷õ®\ðEÒÜ6èõÊK£&³EY›-ÐvaIœÍ+„Üü"8“_$ŸÊ+”ÔF•H. ¯Gÿùµ:ƒðû;ÞÓK’eFŒlÍxžLÇo3&ÀÜ€+àn0ÈÜE×'ðè;ïxZrËb,‚%$É€–X=j•ô‹Ðƒ$dQÒa ’< A’ãÞ§ÞJ|²@¥ò`bÒ%lÜ«³nò-£”%ê9g]£’ ~ø]Þ}èxyT~~¯~Y‡÷íïÔÅçHH(z§âo‰í¦ëÙh‰ oèDn—î—Aú‰\8t4öÉ‘NçW*ö–+¤Ø³ûÅžyû ¤ì,E„aw»çJ˜`ÎG€pç3–Ø LI\à#˜s/—AìƒâÐõ¡'*r=0¤_*6k‚(TèñŒ ÒI4æ†zt]^|èV1ÐÏÇeèDøÍ¯~Ãþ`´ruí|a¾H:¤O Fo¹0·£;|¾¸H–=‡OÀ΃GÑ‚.C€¥,»TÔ¿¤÷hÿESÃ(:Z^®Ÿ 0&ÀO€pÇ3æ˜@‹LK|©{…Y‹Ú$Y¾LõOö÷ñ–(övh°?Zt ×ä:Añ¶1"ˆotr¯À ¤ÌçÑ:K YiI1¤í¢Ò2˨ø^º‘ƒº·HN­®0™aþ÷È!BBŸhˆÅ°‰mJ˵6§–NœÎ£0Šexã´Àà+&½?}ú¹¶–ÛgL€ 0Ö!À xëpæV˜@“L{é¥v¦Rù´nO’$©+î$õ‹í$öŽíÑ‘¡Nï§Ý$ .˜™^]Ÿz¶ì¦÷6¡'z ¡o-LœZc$ì:w‰ 0&àöXwûK€h‰À#3gGW€øúpß¡®=ûvë(ìÑYìÓ ‚\Ç=DKÌÛZ–SçÎÃϤBÚÁ,zZqÝ×gFê¦/jjhöî·Ï˜`¶`ÜvVœ“ 8Œ@¥â-ÌEw„Ûôè72¬¬xÕÐ>Ñ.ÐamrÅÚ"/nþô{ª”~ü4êáâfQ'þs~âó™Ú’’¥aL€ 0{`ܹ&ÐΜ=}ÞòÐë=®¼¤·~tBOÅ—»UrQ'&°}o|³r›Åh2qzŸ)Ιù•w‡EgL€ 0:°^>ÄZƒÀÉó"$‹iN«>¶WL¤üÏñ— Á©ƒ((*EK6J‡Ž¢ …^¿ªMœ8£¹pbL€ 0W À ¸+Œ"÷ÁéLž9û*ŒMòN¯ ¸mL¼îŠøžN×رpöNøam2¬Ù¶b³©¿ôïµgÖt¬\;`L€ 8Š+àŽ"Ëõ2zLý÷œ18ù¯BuÞv¥>48 žœ|˜ ¬Ý¾¯Þ.ãõ‘†ùåL¾*˜`ÎO@çü]à0ç!0åßsF ²ükdhÇÿý}¬.ÈŸ]NœgôÚFÒ˜Ž¡àçë%ìM?Ñ»XÞà—ºáUm# ·Ê˜`ö"À ¸½Hr=L Sg$]Žþ¼+;´'åû:Ñ×Û³‘|š TèÙLf ¤Ï?zÌÞëßÏl˜`LÀy ° ŠóŽKîDhbs©|0¼]@àSÿ«÷óaåÛ‰†O¢’Oø¼Ï–›fŸË÷Õ{÷|3ñÿò4! a?á±ÞõbOQ–E“É ˜ &g­-7F”%&Þ[nSEœ‰ 0— À ¸K #wBë¦ÎšóZ¿ïšyÿ"Îh©uqY> I{^Xð³Ì[8{æóÓ-Åš²`A—s¶—Å$Aîƒ*uÁ?Ø( cÓ·qb-ô7#Wþºþ>Hˆ%‚g02RæÊÁ—oÓe‹ ìï ë°“•ôºÙñQ&àŒêý1pÆÎ°ÌL@‹¦Îœ}$Êë/×_6P‹"²LNDà³¥›äm{ÒKü½"^{æ™'ÝåD}2ñsÉí¨WÇ›¢1håö¢Nêt¢ìï+µ òÓùy ôÄËËü¼<À ×Å–DåZáA®E&3˜p))3BQi9—–Ëgò‹,ùEe:YF½æ7áçlc¹(x|³ éÙÃJüÁ˜€S`Ü)‡…vO¿úªoQ‘é@Dˆä ´~ã³³ˆÎrj”Δ ¯}¾•8Ýß$MÿQ£bº´XÍl)‘f ‚|¿$É^áí-½¢#uݢ cX0„‡‚(¶üï-áp® ŽŸÎƒ¬œ³pèØiéhÎY|[p¶ÔÕ2èžZ8û¹=µaÓù©/¼%H–è¹ ²,ÈB0ˆˆÖx´@H`B¥¾ ïrY:!êõ't!¾Çß}ì1cíúxŸ 0ûhù/„ýe♀˘2sΓhÁzã™{ÆAl§0—éw¤í BO¾ö•Ùh²,X˜4ã‘¶“Äu[NL”ñNy±þ|À ]`á“FëÐSf½ÜO'XVâ0D\>¨‡xÙàîÜj0JÑJ¾ gK]½mŸ9¿¨Xe+é½w ¦ò1(ÓU¨˜ÇË õÀŸÚB z  rn¶È¨ÄWZÖkæ,¢PïOFW˜TNØ0?qÆîšyx 0{`ܹ&Pú϶¼”Ù140jæ7±é»F|¨yæ~¼Ì’uúÜê…I3Ç6¯÷.õhâ+Œ&ÓpTCãñO°‹(êº ‘(I’ÂÐzìQ“Ž ëD¡-Åùxó“‹Kg_¯öOÜ}#ÕÌÚŠ{Æ 3¼þù éxnž eB™eÁÛÓÃÝ1å „”Ö¾>àƒ®/´Ô¶Ê“ëKAQäãÌ«ù…%p&¯¬íò‘“g,è £§î`ßOÊ,AVK"õ3×Xߌ´bw¹)&àr”/˜ËõŠ;Ä4@ Û4g„ r—áýã4 ‹àJ<<ôxC'{»RŸÙ—ÄÄ?ô'¥-W ’tºî\Unªˆ öôè«è'£¿¶.Øßý¼­Ä¨¨âk¨q“ úh hu(.«@í.&“|ë•C„¶T¾IvO=Ü2&^ü#y¿*ݺÀ@—íuU®å”¥Ñä} ñW«Ìd˜Ó R¾/3öfœˆÜ›qrjE…ùáinÖ”Iï|Åߟ>ýœUÞdL ‰Xo"0ÎÎl%€pÑì-JCûưõÛVhœÏ&ç‹Ë$ÌxÞ¦ÌnœéáÄyfSÅ¿NY6߃žÏÁø¤e@\”V ýµE ØTBšyrLJ7.‘‡& 1 ŽÁb‘t»‡ ;D8šóŠ©Tzaò¬¤×<_æ›zùp~&PI€p¾˜€£È0²Kd;ðóQ#8ª®×Í”–!7¯iˆ;ܬë6w÷‰Ä7ƒJÌÅÏY,£Û…GBï®â>±Ð+¦¾Ýd…Ûæv]5#½<>¸WZ„œ³°rón¯m{Ì(.4M™ü龜?zqæ®Úwîpþ%rY®×­ P|!ªûàÍ}Ç~Ý´ NææÛm|;eÆŠf×—ºÿ(¤ì;ÒìòZ)H\sÎ8\œm{3± Œ‚¡—W8¼1'l`jbRB™T¶£~<;´o¬×‹Ý"Þ{Óåз[GôgÖÜ×Ñé“ë ñ|îÞñBTxp{°ÈßO™•4·òÅU§ë ÌÚŒ[ÀÛ =7ìÊž|á­@TÂ}ÚâÜ­œ²rÎÁK‹–*­ê1æph?\;¢ ë‹dXº~'´Çc.­¥ Mûÿ[ ÷Ü0†ôŽnVuÉA¹$H¨U^íGtÇöð¯I×W×üW&|½r+¼ùÔ]ÕÇêÛÈÍ+B^³]úZ_t\å쎜h©ÂdÕ[ÿ²`tŠ]&ÎÜÞLîxîÁYs&IfyAp nê­WèíŽZ¥Ï]#ÛÃ3“Ɖ߯I?’÷=—-Í!Ø“[¥qn„ ¸6¸À r´G Œ$Up _› ÷À-WÀ´‰cåó›•‚±çñ°s¢GÓoüßÍV¾‡^(;†7îNo,kçݸvîÓQ?ëþt¬|;’veÝôDáök.ñ#¾pþÀ”Ysp|«Üp lwqä^hŒ€·Áb,E}—üuÛ*……@çˆvÐ),DqñØqà˜b'yÎ/†¹‹–A†‹ïÕn5 KÊáí¯ƒ»Ç Tb±ûs…zÆý7À/h9ßqà(”ÍÓ<þ>nxch3œ&\} ìÑgô³À÷k¶CÚ¡,%ŠYÅo»jN"r¾[½]™P$å1 ŒJèÕ(4Ø+ùHñÔ£ xy.*sêìyørù¬ûFs€;¯1ø‚Ýrt IF×Oü¹+nºb0,Y¿þyý¥Ð£k8x4>_¶ÆŽì#vÇ OŠà/ƒ©·V¸‘«ÇòMiJ˜¶h´ö—ÐàÅÚ=óý”|?®M!l˜óÈm5äÚ›~þ÷ÛV|T™Ý⿯O=@–FŠÌñ΂¤Yj4èæ;Ï|¥—E6}×-ÞÞ6J4èù¯­5/‰®'ÏȇJBW”Eª°5és[ÎJ€-àÎ:r,·¦ ›âòÑz+Ñ,vmŽªŒFSa«é÷íÁ>1¨”‚;®ÃÇ•PdÞ^EáVómÝ“]Q©É8‘ +·ìFE{(Lºq$¢ããí©d#eÞˆñ„)-Y— ÛÑE„”ëÛÆ$@·¨påx€¯7 êÙžùçX€ŠúâÕÉ6ßœ\7¼/`|føucšRWí/~ÝŒ7xü®k¡#NˆBÖ~Jýã¢Àe$Ådî‰S„ €Q”ó{ÓO€Éb=ØwJäË^\V®<1ÈY²•çpøÇø(kºÚ¬A9ðŽõùýo×"¸ûi²÷Âÿ€ÑCzÛMù^»}?*ôÛÐñ[øuLÿOY·ÇÛf0¿„7ZžøÄ‡•ï6º FÅ÷pæÎðS–¹ÃÛHn– 8VÀj¸XXg!˜8±ãgœmC|é†ðêgËaþwkQanýâ*­ÚÄp`÷.pÕÐÞ0rPwåܾŒ Úáý»+Ökòí&Ë.½TxI¿åå5²F§gòý¼}ÊëJ4C_|¯hwiEÁ'«8¥ sI/´ ûCÿnQ@Sl8rª®*.:f0èà¦Qñð{ò~8}®д^ã2)Ë#öOÖ¿?•§X³;…‡`œg„ ~\ç%M¯èŽp8ë´R~ß‘l¸"¾'Mï­Xµéx\çp¥¯ÛðÆÃë#k9ù¦ßre¼Â#ãDeYª€ÂØM¹ut ßõb”ç½o×Àeƒâ”þV ÚÌ š(…¬û‹Wo£Gü?Dê:Þ6qâDK3«sÉbÓf½Ü}ðoºzX_?Þèqjô¤‰’,H!m#·Êœ‹?§s®ñbiˆ*¬›pF¹h| PßÄéÅÀžèj1}4UX:F‰r5ùc˜DÕ‚1ËáǵÉ@Êi6*ßÁ¾Ð•WJwä ²aÇ!¸áòŠ…W­ƒÖf³ŠKè¾qñÿïžÃ'`Åæ]p•zšøCÉo©´š+;|‹¹`|»j›â.¢f?xô”¢<¶t“zgüó„¹Ð_4­ÈõdsÚaÅõ&ûL>Ô4¡Ek§Ñç™|ÀëJªµªö9²úöïÞIqËÀ)©á’>"›\>¸Z{»ÂÒ »PÞ®¸gDY)ÛqÅ­ÕgòÑJm•Hç'ÿï^Ñà™{ÆAúš?ûö·V9ß$£÷×…×>_¡X´Õ^ž•?aOÜuM› õ|íuÏ®h}·À&t»! 9N)=ºDÀnœd„ä&)ÑMBʾ /ožÎ«œó&ó«©®›*rwñDdµ& y ŸšÝæ5ÉñÝêd]c|Éõ8ê–ÿÀ)çñc¦Íu¸SF 7ØÛÃSoÁ‰ctîÔo-õ•Þ/Á9ÑE ^7ñ_'´$Ë´J€]P´:2,—ÓøhÎÌ-8Õõþ ;Ò¬…N“F  ùGÍ> C1t!¥§ó•—iêêKÐwœR]J<ÍÌ·cÿ1ØtŠ5¾ _Æ4£¥»°¤‚pªor©XµuR¾©äONî dÁVSLÇ0ŵd9Z×é%Jz §ÍVOCD»@ìÇ|qÔ¤D!ëxçˆö@¾í½QVJ=ºF*ûä¾ †f¤—Pi*nò½&…Cÿ).)Ô^C‰ü[¯L@ÅÞ>ýuæf¤%ëvÒ âgƒ>ç'@„„G_éÔŒªÜ¤ˆ”WQaÍèÏÏ©u ÐS¯Ö$ÃÂÖÉ¢NغÐZWn 8/VÀwìXr' Éò;¨ÈŠiN ¯OlG0£vdh.•±ÂÏ¢r»è—ðؼ/áÃï‡kñÅÈðvJ¤Å£0ñš¡Š¢ýö׫`öGK`'ZÆÈR|Íð~ðÛŸ{á©7þ§¸ŒysÒßð…GƒþÂÏEE™ú·QŠ»Ì ŒLòؼ/à;tS©ò´Q\JŽŸBW“×¾šð‡)ÉýEôBVoÚï…î)j¢sôÂéoî†Yüˆ~åçà[FÕ…E-£®éIÀý8QÉ!ô—_³í/õ°Í댿#yŒFòÞxC4\–ä7ËM¦¬©3“þ˜úï¤ÐHQÎm®ÐÅ3Ê¢° ãÈ ä²Ã©uÐÄ[+¶ìçÞýÞ²¯qAßê Ž³0qjiëHÀ­0ç'À?äÎ?†Ü HLüCJÚ²ÑC¯¿dúý׋ÆNë‰&®™þÞpõ°>pͰ¾5Ä¥?^oÏ ÑTjœ´Ú1V˜•È%¦PMd-CQŒDrAVÏÙc­ÎÆY[>RÆË0¤µ¥©m•aë~4µ|Kó“>Ÿ*à“KÞùbZ’<íäY¿·´nW(¯|Ç,[ãÓŽÎÏãwŒ^¼ut¢›½!èŽuóèøF›"Ëüþ#9Я[Û<Ä ¨?ɨ®ž _O†çßû^Î?_¬èø•þZÐëæ,Hœ¾¯žì|˜ 0:8þ—ªŽFùpëÖ}& }ío¨Ô>€Qý» ¢F§Ã¦È'ŽfãL™iPRV÷\?ò"eÙ€Ö][ºÞ ¥ºf^ê·ˆA¬•¨½ÚmR[ä?n@י榺êln]Í)G¡ÉýæÊ!½ÄŽ¡!€a%ƒ1Úʤø+®ŒI¸òŽU©ë–™šS¯«”¡ïØ Qc—”߉O›„A=»ï¼C»Gy:⢞UnL 5¶cÂÓËË£0âNk'RþiV\rÝò×­í•}½z²U `$¢ø”fÚÐ+¯¹nðåW–Ýpå¬ý4&öj‹ëa®JÀqÿ†®JŒûÅšA`ò¿çŒ$iYL§0aê­£…?í…K#Kï+Ÿý ñãZté^é~ÒŒîr ¥“ý×»ÑEtsâßñ*Þt³ìï‹“?]ƒ{uqØ(X[Àwá ¼¡§\'X…nV^ø„hTBO —–)~>úGct rÅ5‹Ü»(šP}“GQ][vÂ0–=•ˆ?T'MdE?QŸVlÙ¥Dè‰ïÙn=¸úiÍ»|Ón¥Þ1‘p7¶A®P»þ0ºBâûz¼®=a”=Qä£ÔýGh²+éhÎYQ¯·‹fyâûsf³Gý\pU5MT®ÚKîhc;Öÿžž0jÌ΂²›1V¶®[T„Œ±±µ”ÈÒKè îÙ´xƒ %Vm) =E莑[â¢Âôk(´$zÙpª"@/ú¦à ¨ô¢m*¾žZ=”øîö‚dm§÷Hñ.ƧF7\>CzšðEã½0'b¢¨@¤l—£L4…Á¤°Ÿóÿ®Lâôwô÷ÏÇЖ[vVvªkíö}Ê ÏC0ÅÔ§——㋎e+ßÏ>ø~¯8Ëk8¾`L‘|ΕÂk_¬€kGô…«ÐelË®Ã`Ák‚žšP¼ý=øBõ× SfŸmèg/ÕõЋÙôNÎ) tÁõ#9åfóä¡W^½8yÝڼꌼÁ˜@ Ž}NW£)ÞaîM`Á‹3—êd}BI©ñøë_,—~Y·]=Únªz÷ çï=)sÞq•âWÏ&ð ãi±š­t%¾(øÊ§Ë”—‡g}ð¬CKµ£ÒM£)V÷›0ü'=¥ ™U14¢KŸqŠ ß¡}ò¯oò(’"ùÐÄOä_ný²ò@ wUhå@IDATqI/檓gíϬŒöC vÆë§ç¼©ï‹¾æôò3¹áD¡‚N‰|Àí›^©¬òsŸqÿ :_o/œ½öœ–žuŒxñ)÷&Ð|ÇH÷æÆ½gÍ"ðAÒ¿ö?4wî s‰ù-Œ"ð÷µÉûÈ7TÄYüìê£Ù,á¸Ó ¥Š\~\›:AwÇIÓ—9]'(0E‹ð>%.(.Cð찧¾]ª&·òóõTzD–ðºRC“G©ùI‰®ÔÉ´è¸õäYòó|qÌ]ôë…"mxGFOÏ0Z‘îÓ_6&d˯ôA¡šwôBox‹ ¸$VÀ]rX¹SZ&0ÿùçóQ¾{¦Îœ—„1²ÿ³jë_w’"Þ£K¤€!ñp‰¬ŽG­å~°lÚ põÐ>hÙ=hÁ¨ÿB‰XGÓf$u1 ò;’E¾ÑÏ×ËBÑ|FŒÓQxG%rŰ%54yTò_™Ju½ø[_ýäsN3Ö&Mû›-Í·JžèÈP¥Q2÷Ç VÀ[…:7âløñ³Ëë2$={øÃ§ÿ]ž8áʇŽfü}=)~ö3o}kY—²_}EÙŸÔeÓªcikB0B!@¡¯ÜC‡ÖÝ‘'΋pw,“g$ýÃ,À~ƒÁ0þVŒ?÷‘Ût¤€;Rùnˆ9ME“Dåc¬yr9klò¨†êªëÜ€îQ@qúÉwœÂ¢5<ýøi%kXH€â¢D1éK±mz_ 5Ò¹óEJ3ò)«5Úã6˜€3`ÜGev)¤ˆ/˜=óÑg8Íû”:‡–êÙ1ÝÌÊ9Îù²ð…/[Òv´Ê½÷íZÀ0ovõYÏÍ+RfË´E†æä¡øÇ4¦;$š\ˆ’Iª¸Ôú[_§ÎJz@þ_Rõœýà-ŠâMáòÚ2‘‚‰á#Ÿw1,Z²Q‰ÇÝÐäQM•µ\Ü4j0ü²~'<þê—ð¯w¾Ã™TO(ÕPK øÝªíðô[ÿkµïCʾ£ B™o€aGSûÃù™€»hCO1wAÌýdxpÖœIhœzãY í#Üpù@häßxÁfä œbO¿ÿœ–½]£5ü÷§õP^QÜ~u£y›’á“% -t×_6°)ÅlÊKñŸ~óðܤë!¢} Meœ9S)N4ô¯‚(<¾pöÌwœ¹/Í•}êÌÙ÷âMë" ÿwçuÕøïÍ­ËåÈM:Ñ 5Õ7y”z¾©ë"¼Iöö2@íɈ(2 ÞÜ+QYšZgSó9y_|].ãì˜/.Lš•ØÔòœŸ ¸ ¶5 ¸ eî'¨‡úÇ ‚éc vZí¤;®*¨Ó¿×SÄ^ñ'„`x²ìÜ|Ø‹‘b;†ÂøË`†P%ÜÙîÃÇ•6g¼÷=LÁiß7§VŸ+(†;Á´ÛÇ€günõv8~:ÂQ©1 ›2 ’5ŽÂ²ÈÍS¢2Œ½´?Пt2†Šóô0Pü`¸ãÚaÏú¢Ȥœ'ý÷¸{Üðê©ã?þyúy)q‘©~мãbÓ‹h1†ù½7]¦XÉúýæ×¿UÇ?^„åü|Tÿ`A´`ÞA}¶®61ñ¯sö›=º†Kg[“OvëºnjÏÚjݧælûûÖ=ÙŽz}4§Î¦”9w¾>üþ †Ê<)ëBç5¥,çeîF€pwqî¯fL™5ç. :ö¾DåI“m ïß­M‡B´šá4ç8ùGwøçø°tCš¢xO›xôï»qroOƒ¢”‡‡å_ºa'*ÁÞÊ1òq5aè´A89Þ@À.TدN†KúÆ€—‡|½òOHèÕ•"# oj.´ ôW¢8ü>îý°þáýc•ðlÍ–ã§ò”²v|VéOB³©©°¸oWîÑD'Ÿ-ÝCúÄ`Ì䮋Ӷøzã~´2É-Ê­] ¯’ù|I™Z…².C=c0«‰Úy]mb£B|‡)¾2ÕM!áèfä¼QÙ€7×è§Ñ̺¸D g)ÕŒP­(È))û I–µ86­ˆ¡M›"÷7¿úÍ‚±×Ï‹²þšù‰SKÛT nœ hœ+à Ïõ|÷Ýwº5»¿+KÒC=c"åûn¼\¨ÏrÕZ½§Ôî7BiîlA ,Áåô¾VTD¾¼æ~èKjG¸Üh†¤‡Ç)l¥Ú]Ç\Ò _3)JûRôG=päTõl„ÇÐíeÄ€8¸jhïê.ÑcòTŽ­ëUb WjÊHíJÛ÷f*õï»éò.Öñ›â‚Öÿ)·ŽVšÜ„ʶ_™¨ñ•µ¨äeŸ¥À:˜dápå†{}Ê’ økÙÛ¢ì^›ß[ŠyŽ3~Zp6ÝRÐË×ÎOü×Áæ×Æ%™€{`Ü=Æ™{©¾óŽçÚ=‡¾EåöÆ®ã.`íÚfRvFE[Mä¦a2›ës}Âu‹ » |cAr3Y±yœF×°Jßu ±¨T9õo£á«[àÅ~†!½cwr=©/ÕÙ:¯:Ñ Ë+,F·“Ê·uÞ†¶q¢‹N÷íÖ±ú˜Öâ+W VÇÆA|á]‹Mô·×qÚåÀ’jAÆ0~‚õä5.ßñ6î ½kA“ýºq—ŒïàÍŸáÆ‰ÏºåM`7ï„XwÂAc‘“Y¾×îIÿ•ï«qZjaX¿XÍtÄÓ£i?4³ŸšÈRNþßÞ¹gЋ`Ͼý­zZ™Íoú}7âôà™ðåò?!x“ºu$TŸ¯½aY+½r*¬&59‰¾êª[‰Ÿ·ì?•S»Š:÷uX—õä('r .Êg1C‹ñ•/ÿmÁ;&aUbâ½åuåqõcï%Í:2eFÒ'8Eü½1ÂrGâäXûð}‘¯WnµœÍ/ÒáÍßW:1ø¡^œVìØV¹v&à:ÚÄçÔuðqO˜€íÖì:ôZ]¯~à–+4¥|ÛÞƒºs’¥»°¤§ÝöA_m3¾pyaÞ r)¡—-‹KË¡\gÅ¥Ã'¡D¾ãG³Ï(¡ÑÈu¥®ä…¾çƒ•øã䓽fÛ>P#GPþž8iQAQ ¬ÞöúsÃÖ=Šõ¾®øÇÝ:‡+>è9ÿßÞyÀgQ¤|fwß$$‚ôf¡Z(Jñl¨öîÿÏóì—„"xê!$yƒ+y“€¨Ø€$¨xžE±¡(¤HGZ脞wwçÿ›7Ùð&¤¼IÞ7„ä™a÷òÝöÌ3Ϭp´þò-ÆñW?]´ò¾ëwXÿ7¤¿r~—fEu]§†Ržœ üëúlÙÚ-Æ#Ç5,°³3¡Ÿm¯öxMro(í¤vº$pÂ!i]–JeFDà?S¦„egnÁòÌmÿsÿ vÔÉ0Lªª'7QÚ”+\aeIó‰<ø°.ÏE[Ù[DjÉ+²—ÚN¹Ê À½Ü,{4òeýËÕ«krS‘å²u¬ëßRãÿæg?2•ñ¦$:ß©ëòëky‘ΤÛTEÌ0M«C‡ÖÖ‹ÏWúŸwŽgœúZçúT/éÂs+< mà šìØoþ¹ïlθ€{Á¥xd§íÝýƒaÆpMTŸ*Ou!§ ÀO“ EÕ<} DÆ»FÂÉõ´ñÝR¢>}[C5¯/d§`BÊ'f~AáÏ© qWBCO6^G×iéæÒa ãLSôAçÏ:ïìöŠtoÙ»['Ö&S ÒEæª;¥kLqÿÍWp9¹ùT‡¹‹~;ö”®:¹l&CËÎ1Ö!`¹ùnväX–Ø$ÓÌÌ·Àµt°HþVKÌÅÕûiúø]§º T>h(È¥¡\IjG½%íäÃÚµ´àádÕp½­5U¬>#ðQnAø6Tæø ß'_-],Ýð¼+ÿFè®KLúkÃŽ}]·mï92u‹¦¡&ljÒõ¤t[ÙºESÖ¢Yx©™Î— ç>ìÚGÙ®}‡Ù†íûLÌ‹ÀäDî1ϘõÙŽ¨»®æÕyñ¥l_ÒHû«%kå‚U¨_£ª¼9FÚÀÏ>†Œ˜Á™´Y–€KÄoƒfn S•ŸÃ"š,›úÄ¥èûR ¥!D J¤¯% 5' WèK7öäÜ|Å…Ê-X^žð/~\;À‚I*gQ)®ø™þȳ±ä1\Oì…yÃC áý L*.·LëL»íð”cÑ<Ô:£YSKº+Ò¯¸\„J®$)Íœ¤ +·RàÎÄÄãcYy汬\‘[àÑË|`ã„Â?n®}™qÜ}Žz#¢Y¿eP_U.åímÇ.;[ØkcóŸöÆQLÔE=^Ò§ç“d>Ú”'¨À«Ç‹RjˆÖ“Î·Üæúb…É+.ìQ­s)1(€\ îaÀÞLs9./ ÅùN`¸>¥e^À-« Îê*ï"˜è 0¥&.D@ânއ`|,aóÌ p7ð;Ÿ ¶O0 ®tés7SØjMq¬œ®?µ¿léQñIQ¸õ*LaÀ³yA—Žêyðà#WXmߪùIs#ÊžïËïL¬+ýðË•dåÂW›wí7áGº´ áž‡~CòÌDçR_ò¢4D€ž àgL%4bÑÎg»[¢`ó}ðòq9V‚¤@jC@Nº„é dD6uj}gZttùþkS0‘ñIWq!îP|\’zÜé@ oµlnµŠhª6oÂaŸÍ‚à—_ÚgÛ>ñ=Æý°;*ÄDçBL"Î/,ôøÛ?ŽyY¹¦\ ñÐÂUáû¡¬_‚Ä׊ôYŠ>ö }Œ¶D€Ô$€×ë@µh à­Y‘™qí_z)w ©xñ™Ú|j– ¬€?õ×çþ(=Q|çh~ó+cÆø1{ʪ Àöš?úLr÷BSôcÂ’¶i]9S:+Lœ%¸ˆ€ðRQu¤FZ÷<˜“C>{¡ÙÞ»î=pR²~Ð79mý+ú¸ÊÜW”9Å"PgH¯3ÔTPc%åLüõŒˆ°~‰#ÿ¯DCÕXYP»kF@j¾ßúâ' 3 hrMŒ«ÇÓå,¹jî‚íÛÕ|Õã:Åä…AP„7ÍMÓ£sO—vP=‰¨˜yA©˜ !~!…ÕÿŽdd \³yëÛ£d¾—_ò¦L69éï£+Ø¢å`MÀ«jÄíSŸE^)öegÅ“$3x3©yD Q ·húòSãë‚@{­ÓLΕô÷¿ùÕ”Þ(_ÏÎcSßžoy„o…?7¤Oëh¹o_ÈQ"@ˆ@ý'@&(õÿQ hg –àó.êy&‹þë`zîÀ5 dþØ‘Îà;ÚÈÎ)þ™LIˆ›Èò(o"@ˆ¨[d“Z·¼©´FJ`å‹¶¸j°ºïpæUÒ]Xïnk´àG#Å×hš™•Ëþ÷åöÉ¢•X¡ÐÜÊ5~mÊĸE5”"ÐHÞH.45óÔ€¾hÀU×4‡ÞKɽºuæXûÔWŒjpÊ ÈÅ]þº¥ÌYl¦<æFïLï õøçsúécš D€404ÞÀ.(5§þˆŽs9ájì™¶-›‹áÿwÚ qPhœ ÓdÒÃÉ7?ÿn<š…• ù\SG§è16N"Ôj"@ˆ@ã @xã¸ÎÔÊzF rBâXã],S×ì†Ëû(7^ÖÇ/«áÕ³fRu* ›WÀÿ¶‰}·ü3;'+ óUX\çéÔ‰ÎÏ+8…¢‰ D  ¼]LjÊéEÀ³¶‘ÿ¸û{ë3šš·]ÕOxþ9§W#¨¶>(t›lÃö=lõ¦]l實Ûm*\a_+B<›âŠ_ìsF”"@N{$€Ÿö—pº>!ñz¸{~V«O§¶-Ìkž¯¼à,CMnúOçk‹Ž;”‘Ŷí9àº7lßkab¥m÷1a±ÙÜ¡¼”ªÇn8ÛHu'D€š ¼fÜè,"àWriê¨ Iws&&Kœ×$Øa]Ú§›2¨_OÖ¾U„_Ëòwf¨;[»e7ûiÕf«Kç¶0§éíï"Ni~yù…앾µªÆ[F„Ë?Ö*¢)‹hÊ,ôœ¤f[ÚrKï9y…lß¡ ¶ç`Ûw8ÃÂ1Ï,[UU÷ Ëü/ܹ×ôéùcñB+§´]T8 D€œ:$€Ÿ:öT2(—@Tœë ,{86ⳄptëÜÖº¤OW¥W—N,¢Yh¹çœŠÈìÜöÓêÍ쇕­£Çs0ås΃ŸŽº“·mÙìTT) eÎÿùwöÉw+¥þ2Æø9–e¶FŸ£B÷5Hw˜ ¾Öæ*ؘ¬³˜º2-aüï©eJˆ §%ÀOËËF•n F%%µtçŠ19o¸°¬n²Íí[GXté¨\е#ëÞ¹-Ó´ºõ$z$3›músûc{:[µi§e˜–âp¨ßcÉô©!Á|Y~¾ØÞ«k§&#þ6²øé•ÖoÛÃf|¸Ø4-ãó4Wü²Eº>;èÛz¶á¶Ú)VÈÏ7ð§à³Ì11§Ë©D€"H àH<”7¨¢õ¤ó™Ûº+Ê3¯„ q¦jæ9[òŽmÎP:´Ž`ò¯cë¬IH_*-Í*ŽËa;÷†Ð½ŸmܹOÉÈò¼3TE=ó‹ÙÌ¡LKÓc7ÚFÆ»F2KLëÞÙìáÛ¯øæÞkmÞ¹s^WTåfŠ”öŽË?ÔõÁF¹'S$ D€Z ¼–ét"@*'0JOêQh˜kLyØVë  ³û™mÔ®˜DÚ­svv‡VÈÀ´f÷£lÏ ¶kÿ¶u÷A37ýÆ:ë³>w¨Ê§é±›+o %D€"P{$€×ž!å@ˆ€t}‘¶ßøùSX7+*¿Æ2­ò4˜½Xg4³ÚœÑ\kÓ¢)kÕ¢|l7að…ÎBC`¿îùsÀ2¦øuí5þ1©Å6 “å°\øêÎËw³œüv<'ÍÌaG2³°N¶yôx6ôüF±Ê›˜T¹ÕìWd²PsÍŸ®?µß‡êS"@ˆ ~#@¸ßPRFD€T‡Àp}Ja¸¯̼²tw¸ó“yw˜‚„U'ŸòÒböd”ÛbœíŠÜr¾“UW µåš4=:·¼s(Ž"@ˆ@] ¼®HS9D€øDÀ#˜3w+n²\°ð¾Òœq®JCm£rOFØe\)à‚cŠÈP„rÌÆ1î8œ6~|¦O…Q""@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ@£ ÀëK+wõïßµP˜C¸Âú0Á: ^Aø;Îß&˜XÔä»Î?ÿœW_êKõhÜt}‘¶ßøé!+Ü1;müøÌúFCèWk+Vl¼‚3qžŸxŽZà¹2gQ×õ Ó÷ûbïšúVo×gTœë,“±+Rïø;ï†ß(=©G¡eõž9Ñ9§!´'m˜òVx›Â¼¼¡œ‹þŒ‰3QV(<ÏÔNÎù º ö¬#¬åMˆ@Ã! ê¦lØï.a™c …q‰¬‹°J׃'¢  7{[ÿ¾oó`eR—¥«v–Nåû¯¨8ׂ‹aŠào¥&:?({¦‚GÇ»ÞF©!Zó{^ÑÇ/›†~ì°Áæ1–Æó­A£ÞàëÿÖéŒü\÷+—ÿ…§©uÑÓƒ½âçÈÞ˜ÌÍVÜÒv#ž…ô?ÿu®/6jrUíçIžË—äÝ Ÿ§¸â¿«I~Õ9'Êéz‹U©®¸ÉçæÊEŒ™Ïá8 àå@2Mqâ "¼>2*éuíRa˜1îüœ›ðSöCåIò"y~¶;)•Î-)&Ò½Òs˜þ#D€T@@© >àÑÛ/»è¬­ý.\,Ls^`ỊBÃñŽ. Ä[\ø””«H_þa…‹—@Î_^‚¨ ÉWB«q#êt“”í(/ ÅUL Ê™ Gƹ.«8…ÿŽ@ûÖ¹þ9­¸µí?róÜ›-&â p·®ªEx†ÎeK°¾fåÍí¡Õ«AÀó„±³ÂÙ™]m`/Kðy‘΄I5È­Z§pÁ>…æñ§jTÏGë®Ñ΄¤zV-ªL~½UÓÄ4忾—âç­øþ¨•îÀß]B˓Ҕ”ÔÔ¡•¥§cD€4n§DßÖ¿ÿ"ßZ½ÁUÕÅ¡¡ ³Ääm.úd÷¥—6©îù2=´fŸCh8ox|2´c¥Ö#ˆù°t,ýª»T.Bª‘¾FIG8'÷„Ù¯F'7°“dgtù­m§ K¼U\Ëê6ÏÜZŠ<î©î¹EéùÞÔ„¸·Ò“Ò\Îkç‰èÄ>5*)©Úu©NùÁz/%!vIuΩwiÝìtXZÔ»z5ò %Ï 9Û0Ž,ÇótuQHe8žÉè#|ßÏ“_•攈 '¨s”ýúAûl·¦ vÚ!n‡YÊÇ¢ÿÛøÊ•î“ZVI^yœ³¹–e>Œd£í¤Q“&5Y† o3Ã~QÐu¡¤›‰‰ÐÞ†˜Žø[;À÷R]ñ¯Ë¿ðB“ì#¹«¹C¹ÖThÏÿ)=Dý¹Cûtqذa0Ce ÚᮈKÄîe¨CòøVpíå™ 1ËåqO9FÒcŒY·ã'L éÃn…|΃îJu=µå©É“›fd¹§¢ó-=w ùŽPõßÓbc=¶‡‘N×k S¾†Pu ò¹iòafðAG­ç„½Æ–ç8òû…óUµÅ”éú¨lY¶ 0½ Ñø|œ‰Ÿ¿s®>™šû³ç˜3é.KX×BÓ¹UŠEg!~ª(o¤LŒ›?Z¹Y¾‘9íêm1þ´àyŠÂÞMMp>-Ï·ƒ¯¬dúŠê=!qˆiºÿ‹kr¶È´\åQ̲fhšò÷ézÜj7">iaYo(*™:1Σ-®'ö2ÝbN‹fŽÏŽ—5RO¼Ðp i¢ÐœrÓü0-ì?/ê“yDËv3q54­n­çÀu€ƒIÁ—®ìyŠw%ây{Ïpîci[œ!>™[ìßèÈ ²Ë“ré7ó3[H‹Žö¼ë¢œ‰c!ž‹NT°á΄HSðG±ÛíÞ!6cf‚sºGE×À>no‡»\­|kŧ–g¢g§kÈ[)4î¼Åh£|ÇÕ8à]ØÇ4ó%½~EìÃÙ‡jœHˆ@ƒ$P§ð­—]ÖÆâæ'øØ×Nø>q)nØÎŒju㣠ö |¤ÿ1úå—±_xŽqŽ-šºßŽ“[]Ç'‘±½WÃÕs°ŸŠzÊè¤$Ï0¿jšà(z`¨òclç²P~`Ê´ó™¿oyHæ!ƒ¦Ko9çÚ­<”_A!—[frÑQÆö®ëpÎH¦9îgÛœ Mý kÖ2ôb)|Ët¾ç¢Œö\Snu88Òó¶î\ë};y ²ììs~ Sù£ø˜ŽIwoÞá»PÓ´!VGàÃiº3þmŸçJ‹å\y:H ê ó[˲¾­Oî$Ó@8Í‚Y`«†ð>ˆÞahîHýÙv/?=:+L ¿ürWF†9†¶=éÚøÊª²úµj*Í¿LYŽüë \ö#Xdš»^ÖWÔÿï¨kËwÅ ÇÑ–L)|דφð½Û¨qÇ师‘˜´Ø-×Èþ×§Úí7 aN“ÿ©šv^íbϵ°ó”i=Â7½#š9þ]—Â7æÈÚ ß%í@s-ÁÞüí–Ž}í¸n¯“çY,d{Ñù¢=3…ŽzÞûðG”~«Œ/0ŽŽgª7Wø(æP.¾Ö2Ärbàt=f+®SV¾aÜW”GÑÿ¦¡âb]šëy+Ѳ/X»Å‰TEæÐ‚/`LYŠç´Ô=XÙ}%K(ºÏ+¿Þ2¯÷qeÏ«¦¶HF[æ Ô9òÖš…ýðrÜOWŒÐ“»Èrd§Â÷ø[*{_#㊂õh›§SAþQß ¨ü谜˖ á<Î#¤ÛÉñ^(ï”ÆÎc‰‰m!|/Äí<¯± ß‹]­fž4‹¬•ðms…Þƒ9ï ¡ãõB"p‚@ÝjÀ r_Á ©Ý‰âý°'Øã^|ÑGgÿºêg_sCxG‡sAº‘˜Sxàø8Ï£ñƒPúˆ¢ð)ø° e{Õ+òCh³ã ò„ÔT¿fÇã ;ZñYÅ¿?Cšù(KÚ·{ÒLן’‚ýóvz˜Q¼`÷úÉÉ-fÄÄd Üû 5ž›ªß%Óü[Ÿ:3ÇýlnFaü\#m«! ^­hZ×=æO™&:Î岄ø^jסUÜ&ã ˜~Ÿ–—àÙgl'ìq!n Ú0B¤lÞVäõ)v§‘“_ø€OMuÅ~$ã èø€G @E.j˜öYW™æ?S¦$dfæ?fYî¾Èw>¢Ž!_¡r3ÛÖ Ëtå…ªXUVŸ™cÆ$CS™ƒ‘á]„«¯!lAy“‹F\w1®Äì(ÚÑè"ÁX@ÓÈ¿’u²Üæ“`°{¦+NjeøãQg¦Á¶Ã<éFüž'#Á¢+W•[ EÿRþ–m–[ÕPET|ÒKú±pÇP)ØËøº«oéÔ÷FCüD&hκ>€ëºìxV0*Ðt„žØc5Í fõ†äíÓôÿ.9™‹Ámš·{e̘W4:a^©9”¾3ô¸µÅéýzy¡ÛŠÁï‡ð÷::£±õÜ`–y¬à¯Ð˜Ëgöä Äc¸Þ/Ù#JH0÷D\¿Ú‰+»¯ÆSNy×Û>¿ì¶²û¸ªç£OÛ¦yxÝÇÇ1±u£0 yoßk&KMx&õO-“Ý…ýùx/Dä¸s¨þ/ü.zv6sfBÜKò7«è†¡³ìÄ}ÿBI‡°Ì5(JZô¿4Ê˱€íG3½5¦ýŸ7ÿðÚ+ßÙ~ ø\3iæÄáȰdDÂo™SFD€œ¶êLßÚ¯_&Ìaþ&Š›¦5ù^Y¼¥VB뛂ó‡qÞR€€iÂ9ŽÖM?-<šÝµl^#â“û›ÂægA£,]ºIÛ¾R¶›\ãëËœ·âî™v\Tjªƒï>r»Å­K!Õu‚ ϧ* Xs¤‘BíËbC¤ð(ë—gä]†b~5x§Ì²soh§ò,Ãx u—Qu„†º0EQºág‘ÎÅFÏÁâÿPß=ØÍ)¾=±vc§»üñ¸þÂÙîoE¾Ž\P>ûÒöœPô_º-|ËŸÏ›ƒ‰‡„Å›z¥ñi·2VÕ¨O©²Àn>Ú5VŽj8”|1:&@³`–øÝÚMƒt}öÒtcÚ§ÚÆ`ò­w&¯ºâw@(Ú*„%5ü×à(4첃qR0…[ ï#‡Ú-¥ŽÝÌý4„ß°“*UÛÁ.úmÅô{Í;¾d…û«ŸiˆqOgâý&?£f<7æ^§ó9¶ð-#MaöÂýupúÓ±¿C/IöÜs×ˈP-ü#û¹ºë’ºsÙq߸¾‡`Ö´p¦+¾ä¹#G£òsÌÀ—:ÀøïxÆßW•\ïÒy㙬ä™÷ùy-“)Λo26Ñ3¹eýï¨ù ó÷>Þ #Òä«ñdîKÑë¢ôçZ w^[ÜÓ ¼³ÁHÖ7š´hö³¿½èXékà•ÞÂ(Ú\䙃N¼÷EóJÒðw'¥¶hn²c'nF?6Ï©þÂìN³ž¶'ÏÙž’¬`õHHå‹—â✒ ÔóBÁçŒrHIˆYUÏ«JÕ;Å´:+Ÿ[cŠ¿ƒ~/†A;^xá9ËW¯®Næ0'xÓp›±Qú¤3M·ñÎ}G ÑzR©l Mgc<>þ±ê¾v8”ÃnÃ|ŸßRéTSƒ‰É‰€'¾¥E¿!ü¥ïÞ¼/b®õ9ÙÁU3Øp3Ïp½LÛ㲜+÷º÷C°Ýb1+Ö»K´cœCHÎãŠR¢/®Á‹–\ò°#ïò^òjf ˜$ˇÙÌ'ÐÀoûÅáU‡PÓíhJ©öÙñ5ÙVÆÊ×ú”-÷Ú¾=~ùví¦‚‚ÃY—a¢î`ó‰ÔþÁþõS˜Vܹ×Ü*¯FþÐÞÝ~MÅ~Ò,§l>D Tz?t}°qR:Á=^u~4Ýæ ø%5”uVÝqv„éλ?P…ÁŽÿ1äí“ßÃfYŽ”„”‰¥å8…ÄË+@Ð6 PžxBŠâÄ:•ò¾Çµ›ƒgäü\£ ûÑ'|£ì92­(T뙢Z¥îQÜùò¢ËPûªâë]”UÉÿ•ÝÇèÁúô¼–dvbçk¼&Þ’ñ½†gç×ôîùÓ‚5›µýfò¥híPÜŸ_Ë䡃#j˜|³R÷±§[o¡c^ÌR¦-{ dœ èÔ÷Ásÿ<Þ£QQñ‰ÿHKˆ{·èHãúßä™â¥Ð,­ÛÖù™ûþ¼_¯*½¾4BÀý{›m0K|#­xd´ªókz\ÎÉ(027i,èš²s¼óÄý2#/WlB àÞ`Š÷ñØÆ¨ë2ü,ù&—“Œ¢ˆó2†C\}µ¶-+ã΀€Œ¡ý”Úõj à3ô˜íxÙýÈL#ÊÒbßàòêAx >xS Ñ›!ˉUDzLؘúî-e¯{3Ì=ÄeAµç4=v³Ì'2>ñ»žÃ,˜ÈòŽÐ^=ØDk²´Dð.>Ž¡ñ HÞJSØ!{¢añ¡Zm¤i †þ¡·Ð&u¢ÓÆ®QžÈÃÄ ¨V÷•/õ;„çÒåHaÃ÷ 1ÙïjP½ uyÈÓ…‚d*ª–‰øö<Žÿ Açvï†>ªOîPà.¼ÀR‹ìk½•»¯)£C"¤UМÿÃ÷Ï•›ÎÏ‘†‘³.ÊNˆ¿ÝÕþ¬~ïÛˆü1øWØuŠÒ“ÎMÓcKFl`6/*¬ä7ìÃ_–õLƒž-dâJM°Ë«Ï+O?µåè¨9¿Kw'®pâyx=Hh§9ØgLéʹû¢”„¸Ù¾Ö¥¼thÌxÄc>Ûªjg|h°¼a 2ņi‰Nß…0ÁDþ·B{·@šÑ”W–/qUÖ'Üñ Ë2š€ÃÕXôeqIž —và°u¡×ôéùc tTº/L77·ÃÇl˜Ê•“ò4õmæ¶FF:cµ&©9,¿M¡áÖa±…ulýYIžUìÈ¡ØÈø¤{`.0¶Í¿ÌHˆ• ÐɸÒûÞ Daf¡ÏÔ[È{Hï.ë Ñ]þÏFÅOŠ VCwYàA¾Xq°+ì2SŸ‰] Áú†$^…@¹`z¢s·}Ì{+µâè|½.' GMHüulõß{è&Ü Ñ¥Òùë>÷δ‚}_ž×"!‹ÅKLöŠªSŸx"ÂÖxxÆ#ëOìçÞ>Ƥççqwᡎ%Årþ2òyó"Ö‰Î-?g{_‡ç¼Ÿ^¶ß%i+Ù‘«aâù$ÜÖœ‘ú´Þ’*9­Aš5ëì}…;/dcð¹¸BNÆä܇¹œ/MsÅý×®„ʵ؟ˆ¿¡vœ¿·Å÷Ùç{ ò“žÌ~8åR‘D ZêDÇ,þó ÜU«bÕM íʨ~àŽÖs˜qðU®T¬™@Í“ð{)ÝøéJÐð""?neMP*+}ªþÄQLjœ`23Úf,aläBȼÓÿÙçá{ÔäV6„‰èÁç`vUk±ëP8^¾1°Íœ&_Q‰‰·ÁÏÈ ˜|ƒáiiÑ"µgßã¯V8\çM ¬ò&¦º£)¸\p“Èv™Š&'¼í´ëXÕî_¦x9Ýp€@ô1F J @Uo¯ª>RX‰ŒwM¶,þ1ÊÉ…·—è4L’Ô,1ßÍÄL ¸§Úšn]Vˆ‘ŽyhÓ=V6ß.Cj^qîmf§æ9R£„¿%0MºaF±»7;mUÛ™ ±ßCÒð&qQ í#qákt¿WÕïã5}¦¼ó¨h_^ yß!ïYÂ2–XÇ5t<(\ý{Šû«}žG°v&¾gÂ…‘!©A¬0ÀM챬Â4Ÿß°Ý ñ mWUö8FǦÛ'Uu_Ùéü±õåy…W¥ÏŒló^žíÞ…÷Âqضw–eääkÌïx^Q•‘v]Úií1q|O¼w–Ùº<&'ˆãÞƒ‚ÜÊvzïù²MŤ×xû\_·¢s›±`w‰ad¼†sþîëy§{ºt3½ÚPj$ÂßmÂU ›ôæ¤3‘ïŸÕÎ[ðwq]GÈóJ\`6Õ.ÙÆ4|[oÆ·DŽÐ&UæV«ÑøK-ú]ÞåËN2T%ÍB[†þît×*<è&ÛëÖð ‰×ct%é»ã¦Ú„¹F1‘*Àw§$‹Ê\mÊD“šrÜ–dP¼S”Ž}WL—c@ÖÑÂ;h.œ¼l;I=m4¬±{žñƒxGL‡»ÜÇ<óJÓò\áÂü°#ž™m˜l/ÛQä®tÍæ_Á¦sñOÈ8ð-øï˜éÒAe.ReÚÊ®<î¤kSŒ~Ä0‡ö6#ò.—öë?OQ뺽ÿEO‡tÀ‡æ»ví¡ñ?„90ANxzþ™¶`W“R àò1ÉÉ­^‰-åÖ3‰,×Ü¥h|ôƒlçéLº £Ô©´+:{Û!Ë l_Fûfaê9ÒNï­4ÃAfí[¼&yÊát”@Bö5EÞ€ò³¡‚‰ _® G ³ÊÉüðì´qohL›f²Â³ 4ËQ”L\¾tÔ3“ºÂ·þfØ=m[NX/8p|Ï5my,?;Hqôš–0~}ñš «¡Ma…;fólëZ˜þajÞ‚ÃÕ¨ËO²~¨ÛBtòá7=ËíÏByÃ=‹ryŽ'| ½âŽaNB2 á[RœÎ½ò\ï€è—È÷JÔÿUxúš ñÞsyè4\'ÓŽŒsu6àdŽdÞ„Í2 á±ÆdÓà½lBÊÄØ™?óbsŒïÅ¢¥÷„iM~ïôœ£y#12v:·¯_rÔ‘·ÑáoÒQ»¢­üFÉ|¡¸ÙÄøÙrÄ×à{”S%“'¶a¤nÒGij_iBZÑ5N ŠÚ¡,ÃFFãn–f]ð’psZBì/Þí¥}"P'p<œu"èúrJ!­–…H²(%|Ë, ó-¸dÇEûÖk¼‹@ò6Ð4ÁûÎòŽ/ZÞåç¯ýWõqµ¶ôGv{*ËKj·‘®V·,§¬½½]v=Þþ™‚ª.Ú_|/öWYºþP>ò’•†Êî«JO¬ÁÁªžWoïB5Ⱦ䔢‘—€8ò()ƒvjN®$}{¦,ÑIjz1¡¦54Î×côîr¡(×–)y[zÒŽƒw">.ºU…fwFS°xáý8Ç3:áû6h‚ÝÓ¥ãÇå`I°ÜB.šµ&~Qô„`:¹£·¡*jL¥|s[‰ Ì’=;ü'ŒèÄØq˜'5 åSíßcO °¿«8nF–CÃ<ßVùy>É.:ó±þÃ³Ò ’ì”[L+SëM´áŸû¬¥Wᜅn…_‹Q€RøöÑEjqñ¬Ô5°#傸P)|cÔàv¬ØK·7Ú÷¨\(âˆgªQ¡ãáË ¤ö;€U÷d-”%pzR vü¦_¡‡ e¼XnÂüi´}N³ü‡9”èÈZ£ó'M®(†O@QŽ0HÇšŠgÊíK1†õO‰`´k_“¹Rc± ñ»e2ªÒ­*„WøÖ.ŒÄN(V=€í[©0¹ƒV·”á»'gÊbï2Â[¯Ë>1¸8àá›k\,6åí†Ô>¿ìZòuÞqP¤ï€ÜÛ3Mo”Ù‰OíthË™8¯‹ý»ì6E[‡Îɾl+w0æ&}‚yTw©Šz·)Ä¥©ËB˜¸ E{¿’çúâ"Õ.£œkà9{7èÇB›ÿ „ï%vzÚou"€cg#èð.7û¥}_ €€f)µëQz깊uXúÇ0œµ„òk±Ó\q;Z8e~úPp¿[ì/­¸²1°ùSîD ~heµÚz„ïƒLÆÔ€Õk:t ¿ïò«.Báã¡ .1A)ïU¨{¼ã¡ý®Ò­jxËÐrŽä¾2"Þ5æJëóò¬ë…ªþÇ;Ÿûr~‘•{â7c—vêT¸àè&LO(ŽõÑÕfE.0½ó.Þ/UÞIÇ=«X³ßa’b“ºxØWÞ{âìnñ!ûEò~|[y[5fÉ>+)“´ß„P>s–† ›ûdž˜Q¥‹T™N†²× (ÿ ~=ê5 üžîr½_žÉMIZÚi´êD?ë¬n+·oß’‹žjh Hc´üÇ@å]Wù-¯Þ¸ì‘S •(ºßÅ•$©ý!E;ퟩÚC èèôܤ4þÚ:0Pí…@¶tذÀÍQ‚f¸J·ªrŽ L6ÞÅÄÄ{sóØZh˜õvZ¦íÛ 1¿Ê;‹šN ä颀oz@\ãz—é½¹ÿ”}5V%þÖ;Þ{Nr… ×3Ò;Ö3p)œ ÛãY^Í¥›Kšìw'Þ¾iGÇd>¾ºHõ.³ì>F¦ylÀ®¶f¾øs@WÇ#QÙüèwÃ$P'8LC ·öëû9Þ(Œ*S? TÞ”/¨o8S1kÌÀ Z*ÿ é®éÿÙž­þÏ™r$õ”€àø†ˆ€ àìdþk¼nU1"ýºÁŒo`Šr–…«pZf+½mñB˜{Œè ušµÏ<ЋЀ’FøâjÓŸ v0ÇTC¸„}õ$Øh¦ò6ÍÓ e_¨ÂƒU‰û×r\á6Q›,È9ï .w(Ì.ë$'_b²åPçÇãÚ,.ž;Â|u‘êK»"š:"3ŽcÒí‘¾ÄãŠ/çRš†O£-uþJ JB/~ù9+WÒ$‡@¦|럧ÆÔæ÷V1x"XÞ”1¨‡‚X˜4ù¨Ü¢¦õ†¬¯ðvMO÷å¼bM¶t«zܪî†!Åï•ÛI·ªÞÚ× 1+¡EÞ íöùÍ›}PQÞ©‰N©ƒ¿IpyÂ÷"h—?Á¹ëísÍó_É”8µ’Ò”g`V1Áßµ€›¼§b"­)þη¢üüéV<ø¨g¦´öôØÅ7Ë-¶*W›åžT‹H¹xUP>«ÈÝ­?\áúÛEj-šK§6@u*€ï8°s¾Y¸ãW-üÆ¾Š»­\é¯ü<~¥Y†Vl]i¶ò%ç‹û:§“•’‹Tæ3XúwÔ®QŸå•6žÖ ð·\ìy?VÆ€°0¨ÿç–U'O¹JìÓO_mVö®N~”–œ ž1Ý;Á3ÕÇ_åã#ûKKÖoPt4uhýÅ”ò! @ àض ʼn/±[ÚåQ hâc¿Di1ôœÅ‹«ôùëýðøÄa¦%^s8ø•Óõ¸Õö1iW†ýÛð'‡!×óÎmþå=tg§‹ž8Ã^30qãÔ>âÝXQk›}ÜÞú3ü­n q8…7Ã^íQ, )g‹`:ÌT,ØPʳ®;bâÇ:Eð‘F|Ï®m©ZqK»·0Ô —eµ ãÑý¿îDˆŽs]‰¡÷AX«OÑŠYÜÍ÷~èDŠ¢½Ù³g«˜M _ªÁ7c¥¯Ëá4‚x‰K$;½?ÓÐ]—ÀFmï+ú8ùß¡9Z\Š:^¡6awqaMö “Ùckæ1,^À{EÑn% 5Ξ÷îgá58ß‚k¯Q5¾£ã'õEgð0Ügú¥PóVøv&„ï»ÁíZßRSªÆF æáüíLs\ƒvï®eÛ·9XÐ`¾kI‘N' ”@xA)Ë®ëŠÕóvì{±aòw Aî[öxe¿ñá41áãù.Luò_~©¶ªƒiレ0nCt|âbïr QƤ þž½tº"Ø[¨[<Ò”Ò.ü}+&{ˆ]öR¾)ãæÃÉÿ,{•-;O¦3Ýì^ø}’3¸´vߨeHߢXØT÷—&=ey´¦ÈÖÚý‘ð´ÙAë6/ä‡4Œ _IDATÝØò|<£,_QØØ”‰ÎOdå“útì*<ã¯#ÉP9A ÏöûÒ-Xô×­ðœíf.F•Fh=Cw.Cün¬TàBÇ5 uœ=¤OÏ'É|Knœ!öá Io„÷gFθWª=o ÚxX³¨§îÍÌhœ©ÕD€TE Î5àv…ÎZ¾f}—®ÝûcÈ; /«Ív|…[Î |Dg ®]Øõ·ÕãøÊšÙÓIáÞ©ð!Q*ðB(»ì(ôLvB8èlÿ¶··:@˜(Iç‰çbO>Ëëd§‘[¥“¶µ²sÀÃ4¸°*¢ô¤s¥p‘¢õߣõÉв‡XçVI`ÏL_ìÕ¬ áuîñ*G˜ðìCºXÆÕój*|ËÑ‹ñÛ¸Úê3„êðÓûRš?ÌžµÆ@øcáŽn´˜ÏÒÝ›ï‚Öj¸Ú[áêß,‹½$…ûÊò‘Ï8FÚÞ ÕÂú8”+ñDÞ'ŸÔ‰ÎÏqè-0rÍLtö•·œÌ…NÂ$ÕÁïdŽ>°êéóíÚMƒívжqˆ}8ûPl”¸#C7ã]ê |Ëã½çý-†„o_Q"Ðh œ ¸M»xéø™ø=sÛÀ be?h«&¿@–þ¹8³mø˜. ÊWV®³BT˜LüSÔøv¤] ϧи ÄFšó@`Þ³³‚»»Ýî°ß÷ü™¸©CqŸäÎwB“µRãA_Ìß!ï! D€ªœRÜ»r]—¯^Žßòï”èÃBmÝÔ.\eîf&c¥„^Ï1…+IW”^4ª(Ö_é,q/]ñ˜ŸØu“Û‚ƒ™¯A‰ÿeJbüwòwT|â¿ dmL›8~þøpg"ä,H>è- @2…FB ïÿÈNâœâ¿ Zýañ¾G a=ˆó%’79Á£ý·ë¶\‡¾‚Yʷ̲^Šœ¸ëèªî˜®?µZó#X˜¢ýpgB$LLöY–uG°GK]\.Ò°MÁd „ãkOw–U´Ü4~3Káx§3æT”ÏH}Z¸áÎH†€ÞûxfA:É=¡Ÿ_\B©\¢u»‘e0•ñ”'ÇÇÖ”JD?=§*Øò¯LýKªå  èúŸFK Þà§ú @;¸Ú÷®v= Ãè‚O14be‚ÐþäÌcEA×…‡ÿgñ`e¯çÙú!]”ž*ŒƒƒÛkíÿåwt¼k :ùi‰ÎgKâ-qÄíA8Uǃ!= M¸ÔúdQ„’²i§Q”Ô:?×ìøò ÈÄB!ç·Bëþ v¿ê Ä~¿×J<½õÍB‹:(¥V™IÏBÐz?uü_·¾ ÒC^‰wHó,€aïÊ­aÂê}£S\±K¼ãå~Eù@ø‰®§˜ér^-ÓÁÞ{®”äË H†ÎŠø4ÕÿïòŽS D€ÀÈI@‘KÙ ñiWZD„?ŒÈ¹r_ ÐâÝ(÷SÆKÍXDTü¤Þò÷~+ñv뤉ˆ¿Óq÷¡Û¡ŠûÆ^"W–‡ Ÿã¡ÕîÔÁ-ÛÂøM˜ÜÖÔþƒ xÜ> ;W¾mH´õ+‚<ë^XZ„{nÛ«®øò¯Y³`xb×Hß÷XR/ ÕkZÄøk{õ˜ë}ãYÉ4å1Ö©õ´Wõqé•U ¢¿„ç¢HÙ!•é¤v»ä9­(.úbâò ™>*1Qš¼\/÷e°˜²:ËsŠ~¡›-Ä—ø}ç¨8×Y2Nvª¥]¸}œ¶D€"@üM€4àÅDSbVA¸“nlZ [ÕBDonÝìuyØc]-Yv=hLZ{€YÆl¤ËÂd²®©Ã‘BÁ½^”yË =­d»s’!”‚Ö}/Ê/:À”xxx¦ømˆ@ÝâAEÑò.ì¹±cs mþ"ÏÌù»j±/¡uîn«¿]›¡áù:ûîØw/Þë^²‘»­ïØîC¹QNú‹ìÔçÓÞyÙûJ¸ê4²ÍYèîÂ=Ÿî6޶Lײ.ÅåôŠòÁD¸é¦eMGÞÑL žfkÀñ¼~ ÷\Ô§?ÊÐS\ÎÅ)zÕÍÄä¿=Ýx&7Ä8ó<ÿv=hKˆ D€ˆ€ôê`kÚì"¤F ̤«¿R¡¬–ÌßéJF?ˆÀiD@Î=€0»ùQgB‰¦9:>éRÄß#þ9˜Ž”˜{H&†·y4Õ•´Q×g…Déϵ’yËd¾ä3Z¹YEYŽJJjé}Læ;\ŸÒ¦¬_}ï4´Oˆ DÀ<2dDy"@lÒ}fº{ÉÍ¡Þ>]Ù*;«<ÇpÂî»=ÌRBåý'¼”¼–¦‰Ý¯Â‹è=¦µ9KzS±ó¨j üäSU9tœ"@ˆ€¿ îo¢” #â“™°ÝÆ,Ê0ÿ8Œ¿eáŽ×å©ÁÌÈ‹…¹lCÜ¢SUþAŠ·®:è¤6ÜùT§LJKˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"PþÁ>#Oæj7IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-dvr-flowns2.svg0000664000175000017500000010401613553660046025514 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 17:55:59 +0000Canvas 1Layer 1Open vSwitch - High-availability with DVRNetwork Traffic Flow - North/South Scenario 2Provider network 1VLAN 101, 203.0.113.0/24Compute NodeInstanceLinux Bridgeqbr(16)(14)(15)Provider networkAggregateOVS Integration Bridgebr-int(13)(12)Self-service networkVNI 101, 192.168.1.0/24DVR internal networkDistributed Router NamespaceqrouterFloating IPNamespacefip OVS Provider Bridgebr-provider(5)(9)(10)(8)(7)(4)(3)(2)(1)(6)(11)VLAN 101 neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-flowns2.graffle0000664000175000017500000001150013553660046027235 0ustar zuulzuul00000000000000‹í]mSÛȲþœü Ý|º·ˆyÉÉæì†]L’»)ªn {É‘e»•ÿ~{$¿È’ 6˜Ä†qRøE3£Ñhúyº{zZ¯þõí"ò.MÚ “ø—ØG/<·“NŸýòâÃѯëêÅ¿^?õ_Ûû[G¼õºQØË¼ƒovw¶¼ë›Ýnd66¶¶½ƒÝÖ‘mll¼Ý{á½8ϲîË««+?°¥üvra ö6Ò¤kÒìz[‡ ~'ë¼€Ó­Ot~í„íìõóg¯¾˜ë×›í,¼4»ÁµIwâŽùöjÃþ Ã83g&}^m ?«À¹ÃvA“‹–G•‚4 ì‡g¯zY —üzè'qx–&ý®¿Ÿ~KƒÓÓȈWƒ"¥ÒXhŸ è>U*òjcØtÑ…~–lvþÓïe£Sgißl ¿ Ú_ì)㜮{¶GÅ×>(fKô†ÇFÝøûo´æ¡ïkÞßœC‡$çB"Î$Sœ­yR_)-á'$&üû÷‰k±mEA¯Þn+‰Âq‡*Uv¶Çå‡NÊc_keב—^NqšL¾Œ–Ç·ÓàjÜ­qÇööËÝÁÒðÓøC1¼=ó9I.f˜,[A|ôöÓð,O“á8çÃ\¾Ëy$ê_Ä›Qx×ÚÇõöóÒ­nІ&FåSD¯)̱üð(|±“v;ÈLµ+a¶Žñ:F–/)yɸ÷¯Zïl#IZ­ÿ>È2ï¸éÁUÕJÛa¯×­vÕÎ 2 /¯}áýâ>Wø5ŒÌÑu·V99¨–Í'ØvÒî_˜8« èh Im$³g1¤A 'gZErJ¢Ãá"K¦•”šÒ5 î ¡‰ÒIÆ'V¸4†#éiŸ±üœ]Ó$@ÃAÊŽÌ·¬^ïÏ·­†ÒQrU/zhzá_¦¡tg;ñi2]¶`"ާÅäñ¢ÀIéàè|ÈgTq4†Ã gS* ‰¥lª6W „QQ9ÃXÄKØ€ û]·‚¸W©#Q>P㹨©‘ÈMœ£Špã‰( ¹&”«õiE7 uç¦]}zçA§Æðwƒ°¥P,[ó"¾ý¨ˆÐ”)ÌœfQ…±ÂšJŽ _ϬYp†1çhôâ3+Œi $8z)§d,JÉÀ’ƒ§ELb…°”Î6¸U‚™Æ«,Á€ÊLÍ,¶LHŽéÍß,¶‚I&Ø4#ĉí}ÄV_r®£ Œ)É4©’Î4¸¿iÀ4r¦3 –Â4Ø¿4i\×,l×=ñÏ31iŠ¿‚9Pº?(†(9PZ Pj™èt½gÒ˰mê>‹½Âea!Pà§¢TÕàirYgñT¡f…WC¤"´lÁð9l"¸º‹íƒ‰äL;ƒgQíF(Í@êÀ¬¤¤Ô Ÿ0%çk0x´•Zª×åÙ<1!v¦ë9bB†|IcBH\í†ñ ðSn¨¼ ,›Øý é…Y),ªìæ´K¢œr©°ˆò2Ö•“,#+Çc=›´W»âaõÒ¥ä'àØ§ a ÿˆõº V6§> j¸¼Ê*{XÊžÖVÙË5¼ìþ‚˜¿ eßp½}SȾQš¡:/ÂX^„£¼Ô¶oçEÄ@Oì„idý4ˆŽ¿¶§+Ž æŠã@7¼Y´ã{h¢ÀFoœnb ê“ÂW äÐĤR%}NmTk)óÅ9é ͱ"XZI™ã&ÇMÅòÁ ã&1/7J¤Ö‚iBÕéGqÀˆÆCša.4˜^Ž›79núÜD|ÅìBÒTjŒÄšèëkÅGH(!À¦²c‘Ó0¾AÍÁNEXÃì”G3ð%e'¦ôÂØIÏÉNL)‰¤$˜Pm©n99Žyr²9 Z _2Š„ó­2ÇPʬìb^",¶Ù [û cæ‚QÓ\/GTJ¯–\ç(Ÿ2Á¸à,'æÒt©ß•'@wžkÏ €fl–ÐJ3ÊîÇë-sž$QçámX5ÐÅûà,6Yùvv>›¾wóYiî- QÒï,Ûn F9S*•žs·èa”jh«97^p©˜]„­ÎZǹØç8Žų/ãäo®Ù<ÊùÎG>m¨|õs/¤¬ ÙŽ@dÖµç-°b™´ ¼{RbÞÿÆÿóý¶MësØY úiÙŽs 49'øi Á "ëºw¸2Èm$ûÂ8p>~tŒ· Æ{cbdçMó°ê¯p¤çHoeIÌOzShÇú„–=–é :óú­jp>(±Á.C l#$سBÌ·áxC6®“à97J*‰ w>1g8ó`Ÿzh§!ÂNf#4½¢N±FÍÿ`ró;«Ø“sV=Ž ïÎCå(è)P^$qá M4¦ˆs†µ&Äò H*Ñ[ƒ›’Až„YH`g†ý,3LÎÏtôAÍ0—€É›#6Gl3pb£Ê·á îR"åÊÛ£$6ò‰íqìÒwdæÈì)™X$™UÆ”pÁsOd±Œiõôü‘΂s¤çHoõH.”ôöm’e›BªîÃy΀[! Øy&¯9^s¼¶¼ÆÒ˜ô}8cîfb{r;¡œ×Ò#ºU!:õœÐ÷!:gÁ­”GÝœ#3GfŽÌ~™É…†ê+êW)Ê>ZÒ¦ %c¤¨VÌYmËOfbþ”ò‰n xD»Óé9Ò{Ô°K`që®wüð ,ä]X-}A¥Ô”3¤9ã`5ƒ àÛÇqF"5SÓ(¥)ûÖÊgÖáÌ…¦`p ü4óW,7¥¹üwÈ_QÓÌ¿Â!ãâ‘QÜ’óÔvP34Ö÷±;h¼gÖ3öðÐÈï u ›ï¬O‹g1ÁØP"Œ2™Ï㯙}Êä#6ŸÇ_)°Ñ X˜R¸¤g‹U5wȸjù Ù¢Q±Û3:º|³G³rΑBv=ŸÍÍÊ4ÁpŽ!¤çŒfå”rlÏŽ©ƒÆÅB£rи@h¬²?4Ò»B£´+|´Š5|Cv‹¼f¾B òHe“sҩȈ5â ‡P!&µCF‡ŒØœ¦ª4’‡GFrg¥±ny^ÆZ®ÆiÐØˆ«œøDA-Š4S\8sÚ™ÓCg¤?-‡¸ðmbp KÊ(W\’ºK_R“%”o•­(X€Ô>…ÜFoißÊ8– óŠ»ô© Ƚƒcào ‡nÞÙŒ[ELܸ&™·Šà¸qM÷8µLáPjøtrTDCaL–-ê0ég&õö‚ Óëmsü³»×ìèÙ×4ïÚíOµ=ˆ ß¦ ë#°EP{—:•¸"™‡*W-jí˜iZ*f}¨`k®y‚øV§B4mÐΈ&Ž@°sF8uêÔè(­ýoÞ›4ìœ-yž¤_—8›-Ï*qj<&Nüª‰³<AœÄ§#NGœŽ8q.-q6mŽÅÒ>]Ô>à a©2·8›Boà«Ï°¤äMRà^¹ Ûh d™cm(sì£ÍqDÜ¡²8Å'Œ˜Ùêtœ¿ÇÚ{L£#ß™Éwõ9÷´Gܶg²«$ýâí%ó³I®–äÈåŠx"¹"èÓ{|ärÇt8 Õ‘¤KÑœ*‚¸T?hƒùÙ…Ô7D7<>zæTwÈ3á‚]âj)Ò‡Þ ={T©Œê² >Æ,¨\<Ú,¨Náw ¿SøWLá_ìs,¤ò-…I ã’HÖ@lyzoª}‘êcª@ßÔ¬Îk”:^[ÇXpö ¼fooý®oEI¿S¿ÛŽ :t,8; Ò‡|Î<÷YëqæÝ*å'Ä™wŽØ±9b[bãn=çùo\ÏYX®2I–d¦ç*›5È-è¸Ú‚ÎAe&h hÑÙ'¸Ks»Ðoþð)ð¢òòHÑqxjʲ;À«CH‡«ŽÂ!äBR<8Brµ¨œŽ¸I $3'uœ_B:„\ÑÌe„ü´  ªP©5OqQM…æŠb¢UT âµýãJK·|Á:ÔíwûÇ7×íwûÇ—vÿ8#>ðB1©1 tÜqê|µµi]¶Ê›K¶Úº¢¼I”ãMÇ›Ž7o.oîĽ,ˆÛf ÓœTI žG5lÃÒ·¿ð≊Tcõ#yê§å />OˆK¥áðÔ¥Ò˜/•ÆVrÑígfRipí„r®$RX#MØšÇü<¶R2DSšé¢lFb¦”aV™`S´¥¢tT†ŠaÑCÓË¡ÑåšúQB¨prŸ¶9ÈvÌiœ,ðÖþþt#6RRwÌxë^ËD§ë=“^†m3LAÔ;ž§ñZ££48= Ûž…8Ã\ìùF+égç^«mâ Ü ËŸÔêh4y×GuÆ&w^÷7¸´26nùèh«´¡‘Ñ +5¾šTyÇ¢;ðü'I«X*lQìJ®z¶Ö¸x3 ¥ã Û;Jf(?¾úüªúaÇôv“ö3ªØ8j¥ÒÃ^x2F•)§É—å‚33’Ãæõ¤Ñ. àpafÒŠÿaL÷(iµƒR/F«wÅÊ]pmÒñ™G®• ýÚ ¿mº%í“–úíi<\/~_geÐV"Ô›­Csuk³Ó&8ô„iúߌË 3µ T{´¦íä}o‡½¬"XUæ^¶e‚,LâJqä£Jù(ïæÛø,Œë}é$Y­3Ö½“صÚ]Ÿ•=á?J+DèÄS Çõçè  U7œ©ü¤°A¿L“žº° ‰s”ïô´ÎMÉûXLü‘T¼O:!lÞëíÒ6Jë­#áaõ¡—Hyÿ°#W;WÞPIV‡ßYvn®¼?@ï ®`|ª÷’ÌÌ~IûiÚõÄ õ*@i˜237n±é ܧ/½ÊZqž.8{­7I–%ïƒz0ž%î ½Ó( ²I±|fxR„ÇUœá]’†%qA¿Ã¸2ñê'jƒÓi<Ñ›Íì¯Î»ß£??adþ÷Môyç]²Éþýæß;­·ç›çûÇ'Ÿ~Úg[ð½³ÿ>?y÷1Ú„ã[oùÑùýëçO{hsçÓÛÍÖW(óë·èjë·›»¿ êÀ}†Ç¢ÊMí€I“N€tCóÅ[cûa/YØ ç˜P×›ÎQ³{†ª!×Ü·ˆ3É‹N1ì+¥AoàH*P¿ßÒ;Í%›iŽU˜ ‡V|È©p”t¢ùI,ÏoÄ~l,ŠLÑAMÐÙ£ëÛáå0¹ÚŒÂ³xÕŠVÔÈ‚uèXë¨ÀGaVG­ ¾ zJKQã”ô¼Ö¥Q¨z›í,¼œEÓË+[eÀº ç¬û!¿öÍØ¨½áê?ôÌÛ8 Ó›†ýã¬Jç§0u:ªoõÓF"ɺÎÞ ²¿ýÖ b¶ÿ+†¸¤Óyxà%H'jE ²{có…\Œ•^ó$©øÀGÖÁ„åU–öÍÆÄñÃ~4fèZ¸E'Aéð8ú­||28í¦‹©kEÁÉ;cÅ»j!ñª 5`éCs6^£X·ÏEܧåW±¼m³mP…ås’\T•QŸWÎl } À.î5ÂCÃbmƒÀ
Compute Node 1
[Not supported by viewer]
Instance 1
[Not supported by viewer]
Network Traffic Flow - East/West
Instances on same network

[Not supported by viewer]
Macvtap
[Not supported by viewer]
VLAN Sub
Interface
[Not supported by viewer]
Project Network 1
192.168.1.0/24
[Not supported by viewer]
VLAN Network
[Not supported by viewer]
eth0
[Not supported by viewer]
Compute Node 2
[Not supported by viewer]
Instance 2
[Not supported by viewer]
Macvtap
[Not supported by viewer]
VLAN Sub
Interface
[Not supported by viewer]
VLANs
[Not supported by viewer]
eth0
[Not supported by viewer]
neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-dvr-flowew1.svg0000664000175000017500000014571213553660046025516 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 17:54:05 +0000Canvas 1Layer 1Compute Node 2Compute Node 1Open vSwitch - High-availability with DVRNetwork Traffic Flow - East/West Scenario 1Instance 1Linux Bridgeqbr(1)(3)(2)OVS Integration Bridgebr-intSelf-service network 1VNI 101, 192.168.1.0/24Overlay network10.0.1.0/24Distributed Router Namespaceqrouter(7)(8)(6)(9)Self-service network 2VNI 102, 192.168.2.0/24VNI 101VNI 102OVS Tunnel Bridgebr-tun(11)(10)(13)(12)Instance 2Linux Bridgeqbr(23)(21)(22)OVS Integration Bridgebr-intDistributed Router NamespaceqrouterOVS Tunnel Bridgebr-tun(17)(18)(16)(14)(15)VNI 101VNI 102(20)(19)(4)(5) neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-compconn1.graffle0000664000175000017500000001364413553660046027553 0ustar zuulzuul00000000000000‹í]mSÛH¶þœü o>Ý[KD¿¿Ìf²EHf’™¼°@’Ý)ªn »m„äÈ2„™Ê¿§%¿È’ –‘‰1™Â`uK-©ÏóœsúœÓÏþùí<ì\˜dÄÑÏO°‡žtLÔ{Atú󓇿3ø¶tÓç=ûb®žïtÓà¼õ¯Lò&ê™o϶í·p0ˆRsj’çèÙöø×q¸vÐõS8å§üÌ“N~’øö—GÏi·üFèÅçQpšÄþ÷~û5ñONB#žmšZc¡=‚`øDHLT¡É³íñ©ó! Óx§÷ßá \:M†f{|ü…ßýb/õàrý³ ;i6º÷Q3Ûb0>6Æ_¡­ú¾Õù‹sä\HÄ™dг­ŽTÄSJKø I… ÿþ}æ^ìÉvCP=ïAÓ•º¼y9m?~à¤øìóféUh¦-Ç·“„SÆ_¦G ‡óã/ÿr:¬éÀÞ(öä)›þ’?Þù#ŽÏ˜,»~tá>$Ái0&ãçœ=æâ[ÎzÄáð<Ú ƒÓ¨r~\=Öú ïwá“ö‰ñÃçæXö˸)üa'íK?5å¡„ÙSŒŸbÔÁò'J~b¼ówÿ*£³'‰“rÿw~šv~‡—î_ú‰_îô2ôCÿê ë‡•+ƒL¿N÷¼ósgò{ù¿¡9¼êW:û å¶Ù{w‡ç&JË:y¤ò$Gs`1¤F ggZIr ¢Ã‘')!Š â¢â[ª±‡ÆpsŠP-9¶Â¥1)IOøLåçÌï›:?¤ôÐ|K«ýþóê ¦u_V›î›Að§©iGé›è$ž/[¶Eh}è›èÀ3§‰rv¥il®ªÉœ-Êa"¦o’:ƒu0QìI†ÅKO×Å<¤(qAg~¯ø`[=w ÞÚ9wù)—æPi 3¸T|hö-Ôk*%GIz‚`ZÙnÿx„uãnìÛC ±üÁðþf=þëèfXz £“(€Ï LŠ:ãIöïЬ‡qíþq”˜áüè41&²¿‡CŸÙEóï1\É~1ÞG=s’úÇ’ ÇG}?ée?Æ_Ù?Òàô,5щßDgƒÔ9Ꞩ!AÏ$Ȥ—qòåÈ6`ÑÙ9…‹î~ŸC4ùãú ¢]•ÉÙwW §E!‰2a.™T”Ž5 CăßÀ”¦La&,"a ,•¼‚p8¼Õ)ÇY‹ˆµ,ª¼6á…IA [V,Þ7ÅR‡+ö†ª÷¹$ݰ6o C»v*ψs¡EÞä¸xxt³ AJÊ)¢zòOnÜõ´¶+Á0GhM󤶹dX 2½Ÿé9aë•»q™dßïÃÙKTh¨l]Fl9ñ°–ŒqN±½Ô[j…–bPÁQVjíB6E(ÏùÊ‹;®{0A³ÂlöMÔÌói&ˆàº®CR߃%Äô ïÇ©A¸®ÞÑ5ðSðÏ47PÔê`E%Á — ­™U’kÊ"ìá —”bÇLŽ™FÌ„Zb&FdCfbH‚@’a–‚FÌ8¼ÍMJ°¯˜pÄäˆÉÓê]$JF%–š‚l¯¯¤‚´j"ae¾"7!øO©äÄÆœ7 'Æ´”|]É ë¶Ì&Ô˜œ¨`Á*BIzWìD¬ŒŠ(a ,(ÇN·ó·e*ç»ÎÝ–ézjÚS/ìy+}sÜmŽo§F„KsÂåëE·&=»×\Ë%h¹œQŒ¤ðáò{š ÌVÌú(Õ:3pÌ´ª5¦M™V2B€la ±/ájý“œ€ v§ðê‘Ø­#ZgØ:¦MoSˆæQAÀ~#‚(©¨r1Eíq•t1E.¦h½cŠ>½ÝyßÁÿF©zjzÿ»±E‚4Qä)&cqÁEe€÷8¸(óÊëU©z%,w¢É?¾°–yÙô’Êŵg¤½ì™J@Z' C0Ð{‹!ƒÔа¤ž´ò*4@ã°åQ&‡nR"­¾K^Ög H!ÑÄGKÙÀ§„Q!ngŒ?=0çÁqöV¨é° zçŸÛß×$þÑüì…G…,•-Œ‡½õ4%èzTªÇ3Í¥flÚ“,ŒgZiF =ÙýÁ³%Ÿr.rÓ;Æ‹›î™ì]ß³þ)gBÈç1UÕŠoÖ÷ÅŽŸ`ÈíÙmÐLú7€Yð®Òï7en5 j"׊ b­“Ö–&ܳ_Î"@¥e<7ÿ1õ`6ƒ=Ž1RT+¹ÕášhL-GëVÞ·çÁË:8ÀëNO GO–ž¸uú(À@.pCz²¶…Ý*n é à ØÖˆ*GOŽž=-LO{gWðŒü°ó>÷Ú_$><ËaNc³ÖÞ°Ëgëîymü^óvýŠÛc†×°Å^ _ÝDÄfÞyS‰0H%ÔúÑ™G)’DTPÉx)2nÚ„Ø€@Q8ÂÕͽ«dt=½ÜgXå‚i.„àˆêf°*$=p­ª4.•°B·„F¶zhTmA#±Žv"=Î5&X¸B359a}‘qÐUÍ—¦«‘Wsz/c?\Íw;Ä€mDÚfI3g±fÐ `Y`Äš9‹•bv[,‚¤pg˰ê4Îva•®VåÒ°Š±u’ ¬Çv™ŽÖhœó+âVêæÛ}ر6!%XJIAJµÓ8ƹ!Ð(4¶ dõÐ(Ú‚FJj4NÒ©ò¬ ˜¨¸-ËÒè”ÆÍ@Fú£Š…ƒæi¡@Δ¡T6˜XqÏÖhš+Š Ì°lƒz¡à[Ì´²òG‰°XkqޱdXP˜WÜ¥k¡†ÞÄȨÛ@ÎI¹ ©Íߤg?¦&é¼÷ÏÍ ïwÍÑÞ³|d_“lh7×Cß aÜç¦ësvÏ^&„YOH ô‡áÞU¶è„¥]NžîØœ×Ò«h¿ ÓŠÑþô\bÚÑnC¼6B‹…#PG Ž@®‚Ñ:üÖy‘½Ó5"ÏãäëgÙòäì&â„nH ¬‰³<Û NâˆÓ§#NGœŽ8×–8±äž„»¢ˆI¬–l«Ãñ8ÖB`£0Ë\¶Xz¨h‡ ¸SúŽßi¥ˆ;YL›Ãmu^Õy››I74ë³»ùš'â¬Ýöæ C|›.#šxÒª´À˜Š)ÉV‡jæ!Ì%ã’r¶\÷UÒf£mÊ­?jS¦ëÒmÜŸ+Úâs' N£º•ËÚýa×wCÐ9JRÏœ¤þ1è#¥Äþ5O‚râÃ…IBÿªå›ÞåÚ…è`€y{h›°ëŒíÍYqZK·=ÙÞžŒ)ù@·'Û MÇëÎXw;•µHJX1ÏfžÉ(˜¹²÷›X‘>¼­9×;²ÓQŸ£>G}«¨øÀò*VMûy €hËæ-T[k’]ùÁy}S–é8e«å§ÜM¢§Þ™Ôïù©ôx/‰»fÐfˆ0íþ4ÑIußDEÜÆÃwZ{€¹íŸÜöOŽ|ù¬ù¼|½»·–Û÷κý;Ùê¡¹ÃøêWGnÓ¶üêèÍ7ÌyÃîûâ¨p‹£­âãêGùÒu“ë<þ•z?s× –@W ïérA9GöË&—’°³ \åÄl9oÓm˜Cž‚é¤4æ`¹©œ×¥…Ú!Ôy]œ×Åy]œ×Åmº½¶›n——Õ˜ðŠ{ˆj*4W­EíNdTluñ¬.…@jX­Ù¢ü=%N¢q:âtÄéˆs-ö¿¤~Ô5?˜§êv–{3§BQRÑŒ§àYº­åoŸàKv[Ë»­å×bkùž<˜ä"èšÊþòŸÞ¿¹nwÂï-Ï=iñ“X!l·ñeÔê:(Œ5fY°–*† ë .}Çïtß;Y˜-¨Zæƒ ’nh\5Ø•UÌ[íêÀÂj^@»Œ36#­Tƒ{™×X2DS`óæUO¬lk§VÜVø©pz…+5±œ¦p]…Jªž¾ÎÓYÝá}®; Žšœ|@FÞºÝø¼G`à :~Ôƒ?£ÈtÓà"H¯~ŒöÁ<\Z\®®VgØÅ§Á:Tˆ)Î)³ÆSù;!ïC>MîÛhRu)si4ȧÉ<ânª.5GOf=+m£ç†ºôh‰¥LDlqr.„àˆêfî?!)EXh!¥læþX¤”âXÓ‡çþ»ÿ^¿Y¢¦@—qÏüðõ)ê• é—I.«ãTÎ$ÍrFÇÝCŽã9Žs疸ǭãF¦àê8nºðžåW0H‹|1y•“£}¿ c˜\a°é©ÙºÓ‰3Š øoœÔvŸÄY–˜;[ãËí5m^ï}Ê[G~p/Ð~z÷Ù] ƒž¼»_̤c-|Z Áñéç\&‹ÎÝóOÍD´êÃJs–9‡†» b¤&Y ùïÆôヮ_Å$ˆ7àõ¯L2½ò$À¢¤ÕØ¿ÉìÏ[Út°š€{™®6߃?u„A× ƧÀ\ÞxÚyFÓùS|' ÎýÔT&PÉ bvƒ¤¿ ¢—Á -‰V¥ùŸµ=0 ï³%|'5¸JíÃl˜¯¢Ób©âñXzqZŒ òˆmÈö[Â=Æ cz× JŠ5‚§ý `©,Ô~VØ`ˆ_æIOUØFáD Ú@zÎL!)Ÿø©x÷‚“ ›úeáMOJÀ¬|ŠôS‚:„þÄàÕù»}r•ke'*Èêèð;?MÏÌeçwÐ×ýKx>åŽïãÔ,~K’}æ!׌·†)³ðÉ-6½€÷ôe°@[+ÎóçýÁ‹8Mãów~#˜Î‡B\×è|'aì§³b=úáYžBV~…×qüG~ã¢ÒÄ«^¨ ¤Ù«½Ð‹ôÏÞëßÂÿ|ÆÈüûEøÇ›×ñû׋½9xu¶söá#âøøóoa÷tþî}øˆÿ8;~ý)Ü㻯øá!ùíëŸß£7Ÿ_í|…6¿| /w½~øoÍIz›ÇcQåºóï)˜Ì€tÍéóÚóƒø©Ï¸ÆŒ UwÊrÏ$OIsHÎ…DœI°wÙVG1ì)¥AoàH*P¿ß0;÷Í›©‘Žû0ö­Ò·Ê©p÷WqúY,Ï^ćÈX™£ƒì¿÷! ¯n†—ýør&ºäÕš–ÔÈÑ~3S­s¤>i wýèÂÌ(-ysPȳaX-:WõvìâÉ"š^ÖÙ*6­aßQðuh¦Ž†kîþãÀ¼ŠÒ ¹î±ZTéüD`YÏGõÝa’À“ÈždUg¯QÙ_}ëûÛÿ帠ÓMyxä¹Ifjº^6ÄV‡`š¤`޶:R–¶cžX3¾­gi24Û3Ç÷‡á”¡+ àû…ÃÓ$¸âñÙµën¤î ô_+Þe ‰—U¨Kï›Óôš>â1¬(BJq…‘d̆MÛw*=™ÃÙ€¥&¨‡¹PÒÆêÊ©ybÄñyYSõ8ÉüÓƒK£´}>ù`j¡¤&¼»F¸-r¹B:㛂[ˆ¦Ÿý$ïùãÿ–V;3Þaneutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-compconn1.png0000664000175000017500000055613413553660046027151 0ustar zuulzuul00000000000000‰PNG  IHDRKXvCsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|EûÇgvïÒ¨"½ ‚åUÔìÅòúZxUì]Ih* $9É]PA”’`_ý¿¢¯¯åUQìbAÅ‚±AZÉîíüÏ&6—KrIî’ yö“ËîÎNyæ;³3óL[!ø`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L F²Fl 0&ÀâN`¤?pLÈžü oiÜK jŠ·RJŽÊžÑßÒŒ5¥miòëù“'ÿ™@Q`Q˜@ÔÆø§w-1Œ>!~›ô­‰Ú![dL Ñà}åƒ 0†&à÷¿åY/>ì¯ ë/B—EžZÁgÍ©0:+Ð+$U_´Ÿ;ÛRëO=¸ßw_|q¨f×u³‘‘¼R)«U~ {~Ý|ˆÎ•iªW`³-”MJ©¢s_[éþ¼4-´å€ õÑ,M )ÖKÝûÓÿ„± ¹ºxSÃ2Ãü—j°@ [ô¯Pì¾mæÌöwO˜P+ÉŸ›üÓ»›%#¤H~,/pû‰ •WÄG=I–P—vkò32Œêäº)7·ƒY"[u×ùý×í®Îns{VlšÃ„P³M©ù÷`s‹?Ç— 4E¬,5ÅTc™›,‘¾é„DI^¹ôX¡D’‘ÔòîËÙ*•üW‹}ÓnŸ5~ü®&É8žá ²L1½D©SÁ þµ_ÿråæt_à…!úµÒtÓœ9ÉŶ?Œ¼éÁàsùYYë㽄ó’â]²aûeljÐVTˆ½e©t_ÎÇš¦MY05ëÕx _b–ÌEƒ¡¤-Ç»q?ÔHiŸ²·*JIJ$d WJd i´ÇíˆxòÖïâ£Þ¦aÚŠ›\³‘øÔЯòØ]hÍ@:]»NœKÍj¤´J(ü€ 0&K€•¥&›t,xS#á˹)¤Œéhü¥¢ñ÷F>B~…x¤)a @cðxô Ù¹¥ð´QþàåóüY_4µ8ÆCÞÑÙwýÅ0· µ”RüfKðû\HÕQ(Õ²3nŸX+J—ûo¾¹ŠøìÛÕÈÜ Ð†ué¾`6Ò®ÅÂ@ö$·yS¿Þ½aûjì‚sùô\/QB+֔ꮤ:ù£¡4Ç3ž£²= ¥†"ÍWpÀÑñHßxÊ_g¿¥|åÃoÈÛoÔÙø:œˆò陆,Ÿh¤ Øëè]:onÐ÷[|£Ç¾3&À*`e©"¾cq!0Ü—{ž¥BsàyH ™ÕÕ“u—ß/-w`7ùç´.6¶?ˆiXÃLÓzõöéÓ÷Ÿ1qâ·æxm(ó^4Î[JMÎÈ›š5)|ŠšßÿHÊ:±¡c¼Ø`úÝù¥~gW ÊíuP(>ªô  ddçkY¡k¡¤lÔ<úÑ ü“ Î(ÿ´ý;ŠI? Q™I¸ÝºÞ›ºÜß=òÃf£(ò8)I½ëÊ-žî'þÀ»ØåÓØšw”ß?ØŒgxŽßÅ…ÖÙPدžì˜ñ™ 0&ÐP´† ˆÃaÍ•Í÷—"„i)Bhšƒü¹áŠ=»ßóv<» ’gÑ é¸u§ÕhÅ¢E‹t ƒü¨ÍAnêâŽÂ ¥6aÕÕ.-î‡Br”í^O™®(‘9­‰È÷OZ]×0êê.=û®CÑû¿_mÝc½M‹Ú¸¡ô­ýúÚÅHÙѶJ.Ф(ѳyþÉ«"åáð°ë•O”èRêŸÜîomïÑñЊòR´îêónÐ:¯hé¯=š.í;\Ÿ8•ˉiÂ(Ÿ>GùtDùÁ„róz\D•G¤:¯AÔÛ)q®MþqHï/¹w›ÕçÚï_”³úÂn™¨@ êÊ£‚+¾aL jØ Ý²¬\ô]W…5¬Ý äK¡NRB¿}a óÄyâØR¦i]Ý,"¹åŸÑÙ4‹ßÆhÏæü€ïøp;ð‹¦ìÍÃPÛÖÞcÂGîFúƒB¦µl¾ÂÔ:,ÄÂf.Dë¼ßAÄÕn ›§Â¡ÈµAãq;¬•¯eÒ49Ù̓Y#²c°;Âۦ¶…›q…Z0/ù2…ƒ5k‚gk¤ƒ>";x1–] ãcÀº ìbZ–x©…7íÎYþñõV(¼ªL-¼ZÜÐ|ÊgUÙ«Ê<ÝŸ{ 0CX×"DÜ{ÀŸØýHóê·ER¾(ÞÄéeolAëÓ„©®DüéÞSÒ¶‚ ¦@Ò!½¿•^WýŸòyA(÷Z¡¬Qp{üIƒ?%H³Õ¸_,4O~~Î$š[~ÔõÝÐ<ÚE´ÉÖΑòp!䈰~š]—ü^þÎ 17/}¹€eÈ}q@¶ú+Î}‘%%Y€p_ëæé?Ê￸ÄqSÛ89îÂÏ4’h`Í’&ä<©éZʤr($¼ÚáùþÌïÃíÏ <‚‘ k‘ö'"- máË95$Þ5a¿Ëðïg¼ù‡÷©ÀëIº"Öï»#ëHÿ´>!Ó¤õYñë‹øî@×Í MjÿÊËÉÊwì¹Ï¥e…öY~ kÄÈìiM‡Þž³ñÞïÙ¯‹Ç†û‚· _ΖØàöÈÿòcxö´#…2AX…Òëæteøfô³DÉD”‹GºwBìáØ‚Ÿáê¿Ýô¬Ñt^”ÄL€ ÔŠ,Õ [fµ'€ã ä Ûüš%²G84¦ßC›ª«cÈÌ9Ð ê‡ß©Ã³9hÐRƒD‡Ýgà9ìS£Ðš‘žx!=/Ïë¸qÎÔ»^`_†¿X7%;Bž§!ÕÑÖêŠÆä¨èÿK {Ç>-%öÁ³”2†š…’ãßàþ„»~ …Ô³¤ºÝEº–Zûwà}W4%D…_ù›»/×âI}Zéo•Ù(mÔ©«*»¨h2ÏûïhGxãh3ŠOAKÉËìøuðöf¥F·i¨3è9”"L1+=Àµ™9÷©Þ?Z!Œžh8}]j&·á~EùÏR:vÇúgµÅnnKÀ ]J®·ç Ç*Hs `‡—Á;¡¬d…,õoøß¼_AøÏÂ-Öµ©›‘Ų—Ú‘³âÙ*å-ÕYéþ»i“¨ _îP”>Aü·#¬ËÓò£~„ìZFè 4~O©É3'P>ÁëÓì÷fs9[%VÖäžž£Cà>¬©z—P,°æJʇÏ)½öƒ,c°«B¼êónX–èµäË•Àßix÷Aú¾ôz§®ùÝyç s)^óƒ—âôòã¥+™òØà=Q©øîV”ê' £ºÃÂúÊ9“±^PÎ@|“…i=D YunŸÕ&XRŒt[‡²ªô’Hÿ²w {3~÷ä¥Î (J_"^‚ðvàÄŽŒâ#Èru|A|.Ò"ìôC!3p”oúA!+´ù`hi9©ºšVI©t³á¢#Å\Œ2a?¦Ýæ(J£ý¹ý•*ù~]ÿÿDþúòôó+„rnÊËKXQr“äk&{µžº{ØG&°× ž`Ôuò‡hcŠJñGØ=ɲäþ•ݨý¥%oÒ¤8mA ûmç9Uª†a½‚ ômÍÆka¾ÐyFçm;J&ãt&:ÿ—Ò±õu´y™Sƒ¾È,|aO£‘¹û@ã,ËV¬Zy®ËŸ4i›ó,=;x£²¬…–²¦ašÖ1T½†!ߟQ„Q£Eð놢]hì qŸãs.Ùe]R^4ŽþíŒúhBÏ ‰Ð‹hœMÇ&mÛ´JšæÛyë3FЖdd ðˆ#¥±û½Ù¹7.ÈÉ|¿¦pH±Ræ®G`OóxÅ óý¾òµ\h _„5{O‡”̃"Huù9>ŠÛl(àWbtíŸHÅùÁ¬15…ï<'…»¤04yhs OZ÷HMܱµx Ö½ëاs}Þ (e9«§¦kg…ïX—üî–Ë}M£¦†Qò xx4©nÌ øH´RX6Š™ÖíÕ+NŽÇág©ì)†ÉZOÅ.‘P§ÄºPÒ†ÖcÖxÔ6 /<OHÏ |‚ó )’nÎ VÜN=Öï{ÙèôCP”phä2Ÿs"FyË(´žBùq¾07Þs¿óÌ9C9êcŠ’Å(D^kÓ&ù†hvn¤‘;Ó4—À–ø;?'ó=Ç¿ʽPû"ݧ`´{ªcNgqÒñÝ ·_3&{µêŠ}ðì#h”­ð`úÆoÑÆ¯R»ªÔm%wR=àV”èù\æJ„q ][Jfº{|©€ϤòxtŸ£(‘ÝÙþq[•ÔÇÓ5ìøè\ù i!)énE‰ì´nôPvÃa;Ú^¸²»0%.5±"YÂ6ׄ*³'Ä‚@æÿ Ä`:•4Ðë>ùÏíFOyöš¡0ïéV×me 3]Ä©îÇ¿›KOB#g_)ÄSù;ôÌþÝ=G×PNDXEIí[-u»­Ëuºÿ®ž£bwZª6,| !}«(’r‰‘·yùS³Ê% ÛV¥ü-z®ë"O´nÐBTZŠü8,¯ÑC¾”«¤W»VÂÜ•Yªìɧ݊…‹©†4ú‰é†ª¿¶zÓÅÑÊR{ªDõ(s÷‘[Q"3j¸æ}¥ú¾ˆóxï® W”lêßËd¯t2MãðkWx¦ç•+Jd‘FìQ–2WõS¥ÀË ðîØk©ü@YqòúDîÿ´ÞU¹©`‡<ë÷œýHSl(#îs+J¹™™›“[h—¡,Ú…Û ô° ñ£”…ø¿Sõ.‹FQ¢ïjfèu¸i/…~ ¦áVì¨Qª'y«yéý©xÐw¸hý`ES¾cL ÖXYŠ5Qö ¸”),öt™lþázTÃ¥eÛEƒ$ât5áÑžäÁüœÉ/¡QºÜÞ`7ÇŽ2NE€Öm¼5ß?ùgÇÜ9wÓ'-dzÜ÷wゥŽyùYª·òý·U’ßn (ùÙ YÖ~åö«¸ÀZ¥ °a*°§ª¸­Qï*ZX£#™Ÿã³ýtž£1ß#½‡¡!ÿ‹Éˆ_º²ŒhÄ¿žîþÕ±Gg+Íó&ÂÁÞ`ô|£}WzXB»€®,!ÞÄtš7àGYðÇ)¥Oñõ¦£©1 æo»•Éò絼РóD„‘ ž)ŠÊé‘F»G]Ø ä«‘³ã¦®ç>_AWÏ'aôòFä‹ïA4‚¸Î\»†¶K¯bSs(<]jòŽ XŸ…´§í Õ¡ŽY<ÎÅq_£1» é&¦¤Þ\Óõ}7Ö§¤ÐGŠK]ó{$¿—O/3Ÿé¹Û¬¾qrûUÕµ7%1ò£ZXfhaUöÂÌcžGbÿ¾«!¶Ìº'/LvûöþÌÌMx'ž¡òÔ0Œ“#ÙÁ”»;©Ó!â³2C<´Æùïm‡¿ŽµH=4…5M®Q,—[»ã+µæÓ:*—9_2&Ð@< Ú%êñÅZ µˆ|OeÚ»{D3@ºbî;ÚürudûÉÍ©‚Æ‚ùŸà¦«Ò´Þ8¯±Ý+Z»ÛþBžÿ³¯]ÿÖ™´-1^¨¤]Ûw‘ݯ]áNR"r3‰Š¿¤ˆ ¡<<»ÓðqÞ«ð(ÓyŒi(WÚ×J<©¡1/0ñ;<¿Ó×&…Œâ«îMh@ž& ñ¦l݈žÜ‘{ýÂGjiÊÚq£îÌ¥†ù Rš0Åp(9[NÐÙ+VµU*4[SÅsšþBòŸJg„ý*ë{„ìõ2ä‹¶²6~¥xeiš…9²Ì¤ÍBS:%‡=ªtKñMŸ´•C÷Cfk¹§ø¸Ÿ…_—­Át$õðÈìÀ`ŒVŽ¥ ±.njñþÙØêþLÚÁ‘Ü‘B‚ z#M¬ BîÚ†v„üûW~½IMq£Ñ:¬ ¥ø%e©û ×8(Õ SÓäC•Öz¾PÇ?¯Núºæw·Ÿ6ß?ô†™ÙÕãû¥Æ-Ûë'wØÕ]·n›œ¹}kñyH“!˜"{Cøˆ—Ûm¼òH,ßw{ª®¹´uåM™¸ë†ÜQp]Ë©Ä@ëç2,¿LÒ<Õæ ²ˆ|Q²Ó,zÞ€Í@ ¾'Ê=p]ÈžîS«7€²ù|¬£úïÖ ø tþü;3_‹TNºœò%`1" ÅÈö† 0ª(IÊ –6ˆ^UY©d^6õψS,ºŠ[+¹)3@ct#]¢ríâØÁµ6íêtfÄvßC/ùŸhTRz Ç&ǯúž“¼ÞÇiäÊÉÔðÝ㟄²$lYå+š¾†^í»EŽûCÞ|Ä ÛúZó+N‰)]sdšb0ùqÇ´£`¯âý}³'©cË70…h§…-‰pšš®UœSY„¨L±RærÏNyÑ8<¾_¿J#xѸsÛyúé§5(‚Ï„ÿ0ú7Åm/škjaÊç›ØÉëbÍ#EaÍ–:¦ØÜæwÜ¿÷í/]À×kßW•¿ $Qþšè‘u?¢‰›=EÕãÅî…Ú|d©ö-X´ËZ %úÑô`°ü½ )ð¬~ï†PöFUŨ¾ùü}ûË•´ ‹ùwuÀT–c^ß89þÔt¦‘e¬¯K'{ênçÄ»„×£ŒØ’ÎtÄ7Äæ}ß,>îLœQ†nªNÑ„Ušæ»5F8Ú l4SÃUk,(ÙåÎWÐÚÑHNh}_7oÖRÓ®Tl:!.…¬ÅèüùSc/ä†Í˜ˆ-Ol½cߘˆ@€”¥ÁÒ²§•®;‰`)Ìè/tÕ#•¥uÞ:àñú07¥·hÚMeÙJ¢ñ² •,~4zﶯjñ¯º†C-¼±­>àŸ¸=ý¯Až¿aÄâd¾=Ò8;ÑõEcú¹AßoÑøY¶I@v¦…ç˜Ã8î#·¸§Ñ"?–TÐhÑ}Eú™cŠ˜ÍŸ¦Ùan18]4ꎻþŠ©‡ßîÜRHS‹´2¹­õ¡‰?¨‰Uíjãvذa56†kòüXòepB¸=´]W‡›Õæ~ß÷1F®ÅÚ1Ú…˜Ž'÷žÖÞÍb³¦Š¿jßÚ„Ov£[Ùnb£°9ÆD|³ì¤õXä‹kD‘8›¡œ@küÈ¿x¿±È醴Ãfaàu–j’¹¦£¾qªÉ÷sl–±ïóÃ`{}±aÌó¡x·‹Ývè:žy$Vïû¾âöuëd`'”Ml¨Põ¦V[Ь(O™"”ß_µ{z…l_¥‹a˜—z ¬k ‡a|A$We ò“xödéöâ¡[ÀøRôÀ=öGã³×ªFrËfL€ ÔŸ,ÕŸ!ûÀª' µ—ÈQFEóAÒáÙ¹'£a‡EürS’§åÒˆžÎ;#>ÝÏ6õx~užJ¥J§‚Iq cÖÈçG(|¬#¢]ñDÈWÐ’mN×ÑøøìÚSË~XÿeP¼¶¡õŽm¨ñDÉsÁs‡·cK{ÊYÓ•´w¹Â§CQ:ÌiD-&£Jä?·GÑïM÷ yrK£oá¿9Y‹ê+G’§ÍäFˆº:~Í?~‚\ƒ\KZ°î˜Çã\Û¸Ñæ´«ZWOÿƒ‘(Ï@ò%¦åwdk w£^ùv’D~Z¾i´‹¢#{UçŠSyðiÞ·’|`{>mo޼PXþ°ì"žy$Vï{©R"?CYB›d„ÇÁ¹ßÞöµ¢éxu;°ÄÌ…S}ÿ‘Þ£Áî[°ûÖØ­É·…9“?Yð]‰‘¦ã¡›PìnŽô™„šüáçL€ DO€•¥èY±M&P'´h <(=ªÃöm»+õö»=¥Ù°ìcªm¦ ¿³&ÄmǾ6­ó*™Á€¾cƒUOT¢;‹cÖ–ÛñèË`† u!m[nÞHÉZ¿€†ë¨KÒé¡Ô Cƒa[ê¾i7®¨AÌRåPÈo{4Õ~¾‰FO»ô)¹g¡Aq0p/¹7nHñ¶x aã4)Ä)¶[M¼êøQÓnv“l ±Q¥ my©Ö°Hßd©ÉÿD}^*²y#þå¼Ke•¶ÒŠëö´¬D“ŸÖ`%yô¬R¹Tÿrùà݈Q~ÿ”d¶Ì’kÊe¯ê¢âäšvÔRe›Yjº=RÜÏ÷\×1ÐŽ›84iF|×bû¾+Ú¦\„LŒFF8¨Ã Ùå©W[ÁJTFèÃ)!‹¤{5ÏÅð¯%4¾“—‹fj>òs2±[%}+Ê PĵS5ûÂ6˜ˆ†+KÑPb;L ž4¸•&^@‘™‚o¯<iÇ9Ú~ y—ÂÒIÔÓØM?>¿ª`i”Š>|è~NJáßSj¦ÍpoKçÏüëC¨UÛ–…9mZ¥é<¤¨Ñ·šÜ~Æëš”Œö< í×+oGuO»þ=E½Ïî0i”bdvî‰UíhFßâ‹óÁëOéI²G<÷šV6JRãÈ ÛòÚSðœç¥ <ñ),ž'ÁF¨…Öâ çyMg,²þ…ì`Ï«#­7À΄ËÑzŽâ¨ÌMsÜÛ”“;Ú)q”nKºN¤#ãŽàéUõªÛßÉQÆ|’ãu/¹åÖ=z& cyyÛp_nDe~tV W¼ãLï½m»[¸²kÓ²ìw†Þ/çqC¼ÑæwG¦HgMӧعMX·ðŽ ·ƒ 7Z;f '',çlïä&é»eª†ÑotÌÝçzäû]Ãæ!!Ž|Äê}Ç·~ó?6cMÑmá;mÒØÐb6u <}$VSvçæLúFIí&”E^¡B‹h—<'–ô‰„H»OÚk¢ôÓØA³êû©½nCÊB]X_цäÈãñL0Ls¥¹Z³ñ8Ø›¦(aÍÏ #8#Žh‹Yh$Cì ç3B%'">Ÿ#m~FZ‰bçc÷™8§‚ç[]õãït‹H[Òc§®ñH‹9hð=ëw 8}®4±[Д½ÃK„ú«×܉|Žï\Åé°J¬~ ½ùßÁT§åNýAuA¯ý‘øÈíÉv¾GÎvßïF4ùÝ-Søõ‚œÉŸ£Ì˜÷w¢eˆwÁ—òó7h´÷Æ{|ÔnsÞ QÞ Òq —Q÷¤Üdŧáˆ8z]ç<¢É§°YÉUÈOcÁ Ê¼_ZÇü@VŽ#C¬ÞwúvQFvî¹(wÞP¦õ>ÊØ§Á|ÞÛŽØë"Õá¤l{'ìXœæd=Œ°03@]µÓ,| çóhÊ©²Ì™Öû‡ãÙËx WÁhÒü±f#¦‹~ÈÏÿŽô9ˆXÈÄ~0&PJ€G–8'0"@ èž–‡jBÎCk0²r jÂѨ¯A# zå?Ñ¥HÇ7…Îoø»EDCÁ3ä°þgÂl>*nô´ª™ØD€©í°ûW^WO÷ãhj‡Û ]Ó±ƒÜ‘Xä?vChÀc7:1 w,–çÃì{g„p·ñ¸§Æ*zÚ݉¾ÿôM+ ‡FoÐ0ùäKF<ÑPW×£1;˜]‡F;5žÔ¼Ú€Jr„G0û n¶ý„é‘Éž¤ç÷„©E=ÜPCß~ºWCž¾-ˆ/áÎÆ:¬Ã?郑”æˆßãHoìhfDƒý64zŽï/И¯¸E»ã°1ÏR<¨}øür^Dù£4`á>'¤IFWoÿ³")óô=,MzŽ¢i§°{4ü‹¼é³ÓM’"/ÁWyjÜ)¬>Ñ×…w5ò ¥ëq6kKÍ%(ÿà={G÷ÈÓòýYŸ¹Ãhˆw#šüî–)ÒõÂ@ö$M—4jG vrYô#â;Î¥kðw '',çL;UJMV»î¦.yÄþH³Ô2¡¼£Ü#Þ 0!ñ6'\:Çò}ÏËÉü»ü‹²ö+¼ÛWQ‹0' ˜n(Sfwõvèþ°[Žú\·n“<þ8žƒÝî(<ô!ÉgqÚQZæ© ÞÅ9t ³6H÷)(ïéš&ÀâHeL€ 4šFá]ûgC;ÊvîªQ ôªþ‰FQô¨êÔX'4µIyuïÜ)“~rÌjôì©xE¢¥ôímZ©ßi!|4îÃMÃ{ÿÇÛï6$z¬­4å•y™™¿×&¾ñ”›¦­á»QݱëðÆêÒrTV GÈ«µê"&ÍÐñ”¹&¿±‹\«¢%íCÒÓ»2™ž¶Ø)°²^•?”fï|»Š”È4 \N:x¿ ´¶¤*û±6¿iΜdkÃöΦWtðm»!ö]­üMáÝ ©¼»·ïê—"R×ÎòßR¿DŒSm󈦛Šö·ôžÒ:õÇði»51¨Ëó›rs;—¨©^Qñ;]uñ´nhz©Y":i!™*u½`î” ¥ü«CtØ `L€ 0ø eixV:wtÄ1ûʘ`L€ 0r< ¯_0&À˜`L€ 0&Àö`ei ¾bL€ 0&À˜`L€ ”`e©_0&À˜`L€ 0&Àöà­Ã÷°à+&ð°SÒGØY-á¾Í“ðàX@&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&Àš*½© Îr3&Ð< Œ»÷ÞÔGžÐkð¹g—|°d‰Ñ<)p¬«"0<+°ìÈ“«åï¾õyUvÂÍ•RrkRR§AÇžÚþ“÷ÞÜþ¼9ÜõÏj{ø‰'üôÝ7ÖÔ&¾uuW›0Ø.`L 1 h8‡Í˜ˆ–@ÆóÓ}9ìÜ\X¤”Xµmkñ6ܯHÏ <4Æ?½k´þ°½èøýªiÖR®„Ö9šXŽóßÛ.Ý| =;°¥¨Èú½D¨_‘Ÿþ€ÂµxxvðúhühŠv"¥í.£pLȲÞK¿ë®6µ‰SUî"…QÙ.`L Q4ÍÊ0Qè±L€ 4ôìàåÊRÏ*!LMŠëu¯Ë¿„„¸ÛRÖÍšT `ož·†â´ÓˆfcÚßhäý[Jm4”kq?+×{A%?TèK*4LÅ#hð]‡Æý°{ÖÁÝvGd®@cõM42ÿDÁ{ ~“mMQòiFvî±n»¸þ †ÏfˆÈEƒwì‡âð~c¶î0gºífd.|‹aOG|ÇÂîkx>¼À,xÉm/?½R–ÀÇÁœ¦5~©Žú!ÜÏÈ/£ÐzJ (–R¾%ój½ŒßA2¤eäƒT4ðGA+„?Aø7òê*$þ3Ê7ý rWR¶G¸çˆÆ;H;rL†½ipמ¿4Æ—³Ÿc—6P»¬O¡0¥Ë/¤6ñ^o%Ba…75!u$ð]§Úï¡lŠZå©ZÄqŒ:¾éyy^äë÷!ëùºÌ­P<ÓLËz+ýŽà`w2²ƒW"ï¼3 é4ln%…Sêr}Hê¤Ü.²íK5‡ÒéXÚyÊ”+Ç»/‘n7kÇ€Î4}§áàý¥mîrWUþÑ•÷ßH”–ºÞvãúg¡Ëñì ”í]—1_2&À†,%LR° L€ „X/V@ã<ëIV†?‹t¿Îü yuFkqÖüw„/÷“ ½eçì¼@&5ðí=ç}<Þ}zÎóÞIh¿Z¬ÄÏ–#S¼Þ¾÷û'®%ó›æÌyªxã¶-J„.Äí³dæ:háiÙk¶œ³Æå?Ã} Û¸þ™÷-ðOØxÛÌ™-¶o-ž©„|jaÀw¹ãÖïëŸëÌ¥K125 Ý1hÜ¢}Z~”ÔBë=73“F”èx ½ømÀƒÜ È•\¼qûL4œÿ‡^þ¡d†ãQ({ŸÂ¿‡Ãã‹gÕúYÖ"Œ*L…¬?/ÈÉ*mXÃ!5ØÅšƒÑ°›ðÝOáx¡ôTýÿ…AßÑnP’Þ3TÉ·¦4΄yù8(FiX“ör^ +Ó±Ÿá¼nâCÈÀŒFœD¡Qx3€NR&”—sûevŸžØ1&RBËŒ"Ÿðô((­?_|ñÅU*yŽËÚæ©hã@þGkW­Ý4¶÷xµ#æù³¾(“í!ä³_…eMǽ=h€›f ížÇÑ…®üô`™›_±qE0º9Ù“ô¢“¿Ëž…ŸC^ËÞeî¤<_®TnÝn\óT]$=î Úü“ø'˜_ åóVwÇÌ0‚+_¾?3s¯öΉï™hZÐ9Ä`L A „ä$Æ ÖG'¡º# o¸%ÛÙ‚@&zãåOJX—VðGªWE‰Ìdÿ‚‡_ñûÈݼÿ曋áïûÐdzTp_zó²KQ²M¼9”–Q|2ìØQrî»@©y ÔIé¿° ç¢!|Ô¨;ïÚÏý ±~É¥(Ù K½ »­h72(ùcÇ PŠzh:Fµ\‡JÕ0Ò›Ò:ÞeŒËšý¬hÏ]~F†ø‡ß ¾ýö<©ýUiúÈ PZº…»Výq·Ù‚)YË‘þ`˜¤·cçÀì­¼@¹¢d?JÒµ:vª=Kq Ò8>y GG¾hìB…¾ i÷…KQ²#”ÎIi&en´ó*öÙ.Eɶ[ÛùìŸàÇRäÏ+*¸•âr°_V¶V©Â£jo4σÈÿí ¬•ç:öFúƒ`ö]•/ÇŸ™`M€G–;8|&Àª$ •µŽbä¥k•–\ÐSß ¼ð‘ÇF ÔþÎMÙ¹ ìm7±] e‡ë~††ývÜ·v›Ù×R®7Kk‘ôã¶­»É¯^ö3¥úÒY÷&¯ ·+5m¥Â¬i’l?;ÏÑ ]ã\—Ÿ¥ö'ÖQ‰ba&‘Ö?õ¡3œÏÀº¥itM¦¨‘‚‰¥Eeá—“Y~–Yx’ý"e†0±ø{Œr½‚iusШ~-¢e—azö]‡Ja^ Ñi]Xw1‹ýg¸h8ß]da˜5úf¿Âm¾?ó{l\q(dŠÈµ‚2ö*¦‚½5rÚ´}*XtÝ`íÖÍÊ2V  ¿äü/”ÉšGb³¹Íe­üræm·E#c æ°—;*»¸wܸݠQãÔ:4ІÒÖ{Ñ¢Ez¸ïk‘§È}”q°ƒŠÆ.»à½Ü¾öµ&'a¡ÚufŠ´§°A©J#O“„ê$ë­!Š­ÝòRòm×nu ò´©¥éOÕÅw¸]îgŽòÏè E·ðëÝÓòêâ/»aL€ Ä“,Å“.û͘@½œzpß5¯¯X¹ ã±fænš V­‡R-G¬t4'Ü¢T=Ñð‹M»Ëoì^ÐÅuk_î*R'Ñ´¨ß茾þåhc «HöÄíŸdV~˜¶,k?•›Ey¡)mƤ„ðh.ðg}¥³zY£‘xð"ý0Rp¦—­¡ëpo¸Ç´9Â:cå ¸ù ?à»Èyžî¿»=öWÀÇOÑ^®ÛA\»…;yçÝ0b¨z±»Þû!%®YòÕ×À‡Ãý©pßyªBø¸ÁºžUP˜v-d=þÌ}‘Ã_(qŠEå3g ÛJ­®ï÷ß¼iü_ ‘’²tÞË@ö…ù“'WÌÃQúª{Ú=eÎ ™ÅÃFÜ1íS¼×½”¦?¥s¶Æ˜h•zDE ” 0&-Àפ¼ƒ¦!‰µ›¦–öFG°èÑ6ÖØZÙnŒ;f8Ó404êä¯7]Æ1¹Ä”½ÓI)p{N¡ipšW_Fæ)Z¬@û½ýæ•n{öµ²®„2±¦‹Ø¿Ò½Jvà <^|kJ*Ca€ØØ `=â@S»ª=h t§Ñ˜?9’ÅMÆÊ.H»d4¶I¹Ús„v_Šô¨{gÒžAgìñ”¦#– qßWumõèø(¸­Â”Æ;k\Õy*\n¤í¢xúè¬@¯ðgî{™$>ŨYqÈ´*ç³r‹¥kµJB¡Ó·Ì mô0».žµ ›h–{UÅEUùÇ^(Å¿-!†)º²~»0gò'UxÃÆL€ 0„ P÷Ê*!Äg!˜ØÛ tÖ3/°‚C±÷$l‹ïôèÚ†³ä\K¨ÑÀ}rž²­ÑØ…ì:Äáqȵ)Iª§C")É’ÆØªüBL™»ª.S‘h§½ _0‹“¦ÁßÍ+ž€P¤LëH%´£…ÔçÒZ‘ÚÆŠÞó» »žcµÔÞk±;Io#ö)Zg®»S"ŸJKQß—Z +d ü´‹ßÄHatðf­Yg6BYºò-Õ¥w“© lu.®Fzl¦Qº¯>Ý4B×lßVüì(p¢&< 3tœRÖTø[ÕÔÊò h„2ãŽàÈþŒÅŸ Ï ÌºüÄ£´ØÞº?†±„þ}¨‘òT¹¬öEKoPî4®Â‡¹ÞÀ‡“0”ˆm½‹÷CÞ?*¢¥ÕOöh]F‚Àèêx(7Û¥W>'B² òÇQÈcoS|N?tÿ¥KVü°z¾Ê×­š×rofb‡çú×Õã{ßÉ*€²öòôï§ ØÿÕ<×óH—‘òÏ,ÿø-¶]©/”Êüù´7ò 67რ0&Øxd)±Ó‡¥cÍž}§[OÅZ—áèáî‚n¾°ÌÑØ[Œ n…bÒ×t÷„ …©iòÜÿ‚ÆÝó†Q¼>dYÏaTço íÚ}α«³Z.¾ÓôkÈ ½Wl”@˜†àcø(l…Ñž…9Yøf“‰5—+ù“©J¾Ç¾ah¤_”—“õ¯ºÊCŠ"¿ñ½ d¨e!Ãú þ>Œ)N¥Ò0šUûã'ý›øa³€Ej‡±µÐ(ºQˆðÖ: ê[EEÖï¦aþåäÄéÌ{"…Bi‡écÃÀä7<“âŒóØ ~ÒäåHn¢1#%kuNÃèTð>/åÂ7ƒôËVé÷jð(ojÖ)ž¤Ca6¨#BÖ˦e~ŠQ<(G^K!/#OQ¸î#Ò¤mZŠ„‘oÀü_–*^‰¸¿Šôï¶G×PnnÇ: úðñxÊ ÈÿïA¡Ê€hÂÙÛ¥KmÒ÷à¡~ÅÇi«šJiåñŸX=Ívë‘óO©¤e#Iß .ÝdŠªs¾/õÿ3&ÀâOE|0&ÀšúÞ¾~KN¢ó:¿ÿº*•¿_i›Œ`“ë¿6š^] ÜÝC=ûÞï⮞Å1kkRÊFçæî‹Í¬º®ý¨JVÚR¼Ð,ܧm«¤3&N$ÙêuÐH ·íæ}¤ìØM}+,4;y„VrâÁû­–-}Ü´8y·ŒuœiÊe ¡Yå#Ž µ<ß”›ÛA•èÞü×Uå´!òTUa;æ´)Åë+~è-Rµ¢!ýúm¬Žÿÿô®R¤î¤µGŽ{÷y„ZoM´þý}¾ûy}¯#åòï ,íÀ:¶3ë»gL€ Ä›+Kñ&Ìþ3&°× e S‰îÍËñMÙk#Éc1$0Â8ŠF@itˆ~&†^³WL€ 0¸àixqÁÊž2&À˜`n£üÓö· |@YŠoºx&?ë~Æ×L€ 0D%À<$jʰ\L€ 0&ÀöØlâ2,^{À0ÌvØ€ã3|Üø {-Ô^7Ž`{?V–öþ4æ2&'JèWx”þcœ¼go™À^A@k©/»¬K±)Ç/Α{EÄ8L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 4E²!„™=m`È ‰°z)©º"ÐV .‡Á˜`K@ ±C*¹Rü¦kú«ós&/o\‰&t®÷†3‡Â˜@âØÛÊý¸)K~ÿ#)FÁ!Õ-B‰îR K(¹ÊÒ:º3ñ’–%bL€ 0X@¥ÙÊRWÔ”šb-ê‚ûºy»=à÷_·;Öá5¦\ï5&}› 0D!°·•ûqQ–Ò}9g Á¢bì)¥\ŒßZšöÒüÉ“ÿL”„d9˜`L áŒœ6m«Èú»Rê üÎBÚj„>åï½ùÛ Ó.y\ZE`”iü SNÛ¶üÝ7?jŠ1ãz¯)¦Ë̘@ChÊå~ÌF–h ‚Ròexøl^ ëL½Ã”E>˜`L€ D&EIfø‚O¡²¸UÆÙMmJ×{‘Ó•M™`UhŠå¾VUdjcN‹Za!M½Þײ¢Tzl— 0&Ð< Øuê Äþü–Õ%M×{M"™XH&ÀŒ@S,÷c¢,Ñ®wöfX£”ïÏ(J°taq˜`L A PŠè&ªCìTTÎp±¸Þ 'Â÷L€ 0è4µr?&ÊmMq1oæ]&a[L€ 0&°‡ÕT‡ØŸšØcœØW\ï%vú°tL€ $4¦Tî×[Y¢ï•~GI>‘ЩÂÂ1&À˜@€²ôÕ%v’°R– Æõ^‚'‹Ç˜@“ ÐTÊýz+K!+t&}p–¾£Ô$R†…dL€ 0„#@uÕ%T§$œpaq½„o™`u ÐTÊýz+K`Ó _cßÀœ­C.a'L€ 0&`°ëÔ%¸éÕp½×‰EdL ± 4•rßS_ŒJª®ðc]}ýa÷L€ 0&м  >¡º„ꔄ>¸ÞKèäaá˜hBšB¹_oe ßUj…4ÙÙ„Ò…EeL€ 0$€ú„êªSúàz/¡“‡…cL  h å~,¦á5¡$aQ™`L€ 0&À˜`Ñ`e):Nl+NÆÝ{ojº/§ïm3g¶ˆSìmŒ Ï ,ËðåÜcoÙ;&À˜ˆ#ᾜ¥éÙÁãD̽æú&æHÙÃ:¨÷4¼:„ÉN˜€È¸#p¾²ÔÄ› %Û¶‡ 4}‹ÍB>Iòz³ðOÜ«ÖÁùýJóû¥«¤‘{†esó‚¾§s:È^²Ô¸…AßaéÁ`Q¤*%ÎF¸[áÿQ¶]%s05îiº¦ø cÓtÈyìt´º/u©ùæçd¾gÛ­áŸ×ãÉ2ŒÐ`Sˆù°zN ÖíÇósO––•0€©p?õˆ¤Ió¿s»/“ÿ>˜€Ÿ&ï{=É·˜fq‰ÛžsùðCÀ< €ß›ÀîÅ®žî™~ÿu»;|fL€ $:•Õ”qÕ•÷( G™Ø+?ýwÜQo½Ž2?9/'ë$÷Tj”¥¨W´ Y¨»Jë‰hëA”égtõø®*ïDY|™ªCrÇ6ï¿ùæbwØÎõðì@ްÄÝ«_¶À?ùWÇ<ü¬Iõ+* ê)éÙ¹ÿ—Ÿ“¹,ÜNø}´õ7¹«M}Sßz3\N¾g5àix5âç1#à÷/JBÃü0x¸Ðï¿8bƒÛÙWƦKÏ«•@IDATQ_E ‹¦É«¥&þ‡¹ÓS Œà3n»xÞ~Ÿ#Ì]¯£rX-…~””%¨,.WæÆ§ (½Ê¨XJm΢À?Ö4Íghn¹ËŸ¿„TèKª,˜="tqÿ_`Êà}éÙêM³]„àF °4±¯cæœ-%:Ð3º÷JY™‡\4­ð L5 ЕÙgô<=/Ï‹ø½Ë+â… õV¡DšiYo¥ßLvj:4¡IMŽUBý¦7ÖdDVà ©Bo"þ"·àçCÛš¢äÓŒì\{ýù1Ö?«­Úe}ŠŠüøïR»ëM£äC(‘ˆRÅ##+p Ùň»Ž8…Ý×`cxYðRE›|ǘh|µ-;k*ãª+ï±Æó7Äø¼qþ{Û91éRgÕ`ÔSÇgÜ1­´# oòÏi²ôBM©_ÉnmëA¸=®ÀÌƳP~?/n¯JQÊð' ¥n×ué¯NQ"9,!ÓdÏÓ ô}',ëA’‹Ì«:j#wmê›XÔ›UÉÌæL *<²T69õbå4ê“5©¯ŒÆóu棡@‚QC\£ÿáËýJÍ‹¾Ü³ó™/;~¡ÒIèÕ"Œêä–™=ƒº4( W£ñ>//5Ú1GÅ·Ã*g½9³/Ì~tüÀù€ž–½fûÇm-3ûÏp_ÀDM1n„æ} ü6ºìV{973s3,,ˆÒT%äÏ r²¹¨µ›†C¹8ÜãÕŽ˜çÏú¢ìÙCïWTFÓq_^ºÝ¹¯KÌ’TôV>‰x^¥nvºÿî×òý·ýá¶ã\Ó\óí[‹gB–§|劕ßÿÖ?×™K—bDm6Ñc¨‡³Ð(D¦ì$eÒAy9·;|žC/äŒ1‘‚åx+nš3'¹xãö™àü¿…ì¡eÅÜýOáßÃáéTî/˜`D 6eg4eÊ{ª‹"–÷éÙw½ŽéçZ¡¹û$ØyŽ¢l e¥üÅm2T‘ó`dÔ[;N¤N'¥yi ¯¨u=(To̬ÐÕ{ÀñÕuJÒ®x–¥‚èݺ<Ò¦ ¶û€Ÿ©ùÆHàÆ©Þ/0VNÂó©n;îëÚÈ]›ú&õ¦[N¾fÑ@’&Ð@Bò@ ãë£ Q]„QŒ7\Š’ílA £0ò'%¬KÃýAãÞžÞæ˜cºÝ›t­tYa$ÊÂ6—¢¸‡c·ìü²KQ²¼9J^ªeŸf·^·¥S$ä.EÉö FhÄ@ª k 38ìwXz¼#Qé¦bdmVUnvì(9ñ襆zË¿° ÎsAé¨QwÞµ=€_ç ÞÊ ”+J¶ý$]ûg¹Ã²‹’?vœ¥¨‡¦c4Îu¨TâŽNëx—1_2&À@mÊÎú–qù9“¾Byú»¡Sœˆ£ŒŠ‘¥·P@¾…Î8R–JËLvÉM©AíëAøé«JQÂÄU¶íÌÞ‰çˆál×5óý¾0R6²geøsŽ`¯Ì(z¹kSßÄ¢Þ¬Zf~Â"à‘¥È\Ø4¤²ìíÀ1‚Ñ5ï1RÔJѳUØÅh‡Ú?ü™ò´)p›¡¿=B·<¶"Ç|ÛCT!*N%rµÛ=]§µHúqÛÖÝd·Wø³úÜ#ô>PŒR1*ô­Û(4UC m)$En•ûYU×ùþI«1’“‰i€sp~"/½˜Fˆ0Åb¥hMèÞäJ~JM[©° ,dšÄôgüWù²ï>’Z§¬)Ù\è6XÿÔ‡ à|â2Íyˆi|¤[!¦Üÿù̘¨/hÊÎX”qÑuÖ)$oºÿ®žÊ0ŽÐ¥¸ Ó¾=(#ÇŽñåì÷@ û(:ƒ1po*‘ݺԃï>ÎLò¢â¡ToÌBÀ¨\޵RùFwçõ´Í4Í-ç+3„éxê„?…3™cûZÊu}Ëzs´|Ū'`÷To…Ÿ2ðx¨Žº@VRrª A)#â3% xTiä%oJú®HöC¢ÄŒdn†Ée•Þ £¤¤%ÙÃ³ŠŠU¸cºWÊÉ8’F½ZÀÓåØÐaF…Ÿ&'ajÄufЬÕz]<>ŒÉ ¢, M( KDv2Ù¬l®—ÚÅú$‡i ÜWZ|ï¸q»ÔÏ=‡%ÐÍæTˆâ…5Z7`½ÓÂ=¶ùŠ 0&Xj*;cQÆ¡ë ß*’hÝ’4óQöoéì9áÝ®žÞFé¹£DÊsiíFl‡¢±¤¡*ÊrôàE¬çNU±GËåºÏnÀí ¨¬ŽÂñ‘®GQ_ÎóÞ©4mäåò¹¨@þGS  Fä_–<£Píú æõ&íQ–b\¢^˜u®ãÓ}Ám˜Ê~oFö]äåLú²¶É´pjÖ+P¶žDm—*V{äu<ªÜQ×7±®7qù̪#P©½:ËüŒ Ô‡ÀÅ_Ò¤¼ƒ†ÑÅÚMS1ÝlO‹;’ÇJ.ƒ…3°iA{÷c,–=úÈaØïM·y,®±ˆõôð]~ Ã)4Lóêö\êUÃô¹m˜xvŠ;ÌÒõ¬3ÜftéëáGps˜Ñ®t§Î DVÃDq?7gÒ7`|ÄM½˜n')Z¬€Žƒ‘!óJ·¹}­¬+1moM±ÿªÒ{Iq=#ü„V¨dH%·/¾‘…mÚ … +ø`L€ 4=Õ•¢e\UåýŸ¯åïwš EéD¬*Ÿb®+ù”Ž“0UÖ+}5Ïïú0{ŒëAtÐm¡Ôéêé:ž:-Ë\dÏD¨C’%§écáG±0-¬•ÅØ™û¨Ü° §QÕ7ñ¨7Ýbó5ˆD€G–"Qa³¸è¬g>^`‡ÒœéŒìà |—è‰ÐÐÀ·Úb8l¡¥a׺é$@›¶É÷`÷¶s•¹k1¾G1ÖãMZeYÆaVÈœeåû.úñÆAÐìò³hDö´;=º¶;&‹]ónDý¤û›L÷uTgcKñ±š®?§B²Mœ{û„Ë„Êïy,འ»øcµÔÞk±;IŸå¿E´ôåNã*|èè ì‡9äž/•(Þ ކ"¦£÷ÏîW4÷]ôþÁó‡aPÓÎDV>ÒCÛÇÏ^öƒç¦$©ž‰¤$K´+Ò…˜2w•³(ß šn¡k¶o+~v”?8Qž†:N)k*ØW˜êH;b Ú,NšK4¯xá)Ó:¤G ©ÏݳX9š°&À˜@èªì¬MWeyoGG{ÝÖU¨'¬´¶i¯91Lñ¶x©ÈÜ™ mãjÌg~Ô1§s¼êAúþ]º?÷eXŸbýÑ|u•;Üh®ïÏÌÜ4<+0³)/µ˜•µ‘»6õM¼êMGn>3Hxd)6‹šî€­½‡bCáaê‚Fz¾°Ì±­êb¬µ¹Љ½  p÷„ …©iò\þ‚˜ç £x}ȲžCïÜ'ÞÚ Î†X +…–Kß Y¡÷Š’T^³Ñð W­0j‚)·àÙR(³B†ù zçÞC¿Ú&Mè×…ËãÑ“þMò[ÊZ¤v[ ¢ÉNþ¤IÛ´9JÍ7PBþe©â•1{S#ƇûQ›{[áQa„õôÁ“…9YÿÆ4Œ‘xti±’?™ªä{L=&tyûþË ‡C¬›: òô1 ëóR¡€’úeˆw¥)¤à‚Û ˆçØ8cYȰ¾‚¿£(•†Ñ,>˜`‰M º²3Ú2®ªòžb®i˜®¦mà³xÖøñåN´+F¤Þ¥gº”åJ¹‰g=˜ïÏüå6}“ðJt¤]OáÕö@}þOÔ5¯†»«Üµ©oâUo†ËÏ÷LÀM`O7€Û´ט³ú6YG/ø)tæƒ Ô†}`N_¿¥G'ÑyõtUå–¦¸m2‚=N>¬ÿZšÎW•½ú˜#/ï€û{hD‡Âû]ÜÕ³³8fmuJM_ÐÌ-Ú{|¿‘"X]ø·OŸÞªp»ÑnnÐ÷[¸½E‹鯯ø¡·HÕŠ†ôë·1^qt‡;:7w_,\¶æOž\qý’Û®id ¡YöhXسH·´€¹Ð,ܧm«¤3&N$¦|0¨4•ú¤©Èt¶TkÑ”qÕ•÷µ°ÌACÔƒu•­:wµ‘»6õMcÔ›ÕÅ“ŸÕ@³(O)’ND놉]1Ä €|¼##;pgbHÃR0æG ©Ô'MEÎæ—ƒ8ÆL€ 45M¡<åixM-W±¼L€ 0&À˜`L€ 4V–3˜`L€ 0&À˜@S#àij³¼L ^”Яð(ýÇxùÏþ2&À˜`L€ 4-¬,5­ôbiãH`a _5çƒ 0&À˜`L€ ”àixœ˜`L€ 0&À˜`°² 1&À˜`L€ 0&ÀXYâ<À˜`L€ 0&À˜ˆ@€•¥P؈ 0&À˜`L€ 0&ÀÊç&À˜`L€ 0&À˜@¬,E€ÂFL€ 0&À˜`L€ 0V–80&À˜`L€ 0&À"àï,E€ÂFL€ 0&À˜¨ ·ôs÷µcÆg&ÐØ”Kºvß»5ïË&«,Ìž60d…ÎDòõRRuE)Ôª¹%%rô©ä:¡Ôj]÷,žŸ3yysa`§Èrwµ%-üHq¢_³?š”²ä÷?’R`ŒÑ49Þ´Ì.RJÕ*-ÅjÛ:M¦x½ÍnJánð¶n/R;ŠvkàÈÈ®7ÍÐìÖ»[Þ?kÖøÝÈÝ{S&—ãÆÝ›²=eçM>ÖN­y§³/½@L „•'A*OB!kV÷¤î÷ûý×QyÂG#àz¯2ôË«¤$QûC?唡i½2:%5eŒe©ÎͽR9åØ$ ¸ß'ͣߙ‘øÝ(1ïûã»Õs_xáá"Èì(N‰(~ƒÈÔd”¥t_ο‡Ö=Œö·ƒûtSGþ¥8tÿn2-%YoR‰ˆ­ í._­*Ÿ|ósç¯W­¾«å®[®àþèÌÀ«»©gr»"B|ÎÜ•²k¡fÉ®÷éÊ韘ù‘¥jÚ"•'3Pa’ÿ†‡îòSy²7uÀ$|jq½We%J^µ•$Hé½lÌ­g´n»Ï7_ÕwbÆ?gM_ ±CøQ{²YõV4žtêµDnù»o=Jçx¨0Æ)%ëÖ±mËᜢýíø²{Ç}„×Ódt½x`)÷“8£é#èÝYüºþ´ÂÝÆå‡báçKßù¸ÌbSläÐ ì¹~Òã<Þ¤‡‘þ-8ýË“/˜@\D*Ovî*¹|àI§ìüì½·–!и•% QŸÄZCÈÉõ^Í)Õˆy•:ñ¨ýäÅ/ùªñ“Ʀµl½ [§}¸Žª9ÙØF‚Ÿ~[¿9mW‰uÙ€c/úâýw?‰—Ø QžÖWv[›¬¯'qt/K+ qï ƒzk·_sŽÞ¯gç8×ô½&>¯=GtÐ~R×½3®½Ý7±¢ v*à›ÂQ^‘üЧSH:–qo"°§<éºBÞscæã¿¦T–4Åäàz¯©Ö€y•ê'j;Q½šrÅ-Æ¥¤¶pU‡Dc' K`Ïû´Ÿôz“ïºvB•ý”ç)ï7•¶dÌø&²²$1õãLŒ(ÍD!¤n¼àd™ä­÷@XÌÀ%²Gĉx7dò\Løäm*™¼¼""¹I~NÿDÎm,ÛÞNÀ]žH©Í¸æ¶Ì³çfYa6@Zs½WÈ ”WË¥¡×eœ¥)\GÕ#ÑØiÂp¿OÞ¤”ॣǹے +w<KXe)=ÝŸêõzêÞ©­¸úÜš‹Ä&n];´­Û¶È!G·ŸŽÂ ïãå‡]‘¼$w÷Nû(Nÿx¡f™@ôè=ìÖq•’’¶pÈa´³WÂÖÑÇ*±lr½›ôˆc^¥æ†ï(íåY¯a¢G‰g»ŽNCˆ) ˜É+TD$'§Ãä … Ô–€Sž´lÛæt¸åÑ¥Ú¬lŸë½ÊLbbüêÔQ´î7µC·î§p“$bOšç}jÛ¾ýˆMïB³™Š—Ê w«´ œmBÙ(qE%Ž­R“•æñô„”4/Ñ2y…Šˆä$y9ý7O±dÍ—€]ž |–šÞ+Ë’¦˜0NùÇõ^ŒS/†y•ÚJÎz¥oRr7®£bœXì]Â(oKêv[Òé(K4=".-’¥’&»´m–h²Å%ÊÓ¶mZHÝëéˆðÜ#K‰Â¸4ÝK{©SHN’·¡Ø4·pŠK ñý¯ë>Ú?üö»ØU\QÎo.ï~öCÄglT>K]vAHͪŒY®÷â–¼Q^¥4¢)§”ß“¥Ž2LSPy^׃ÜcJm]'¬» ›·‹ßÿØÖèòÕT×¾ññ·bÕš .gm ¶™æÑ;Á ½ Íf6E4‘»@Ò„l•âõƤ±¼zýf‘ûð‹"ó†sEÏÎûÖ9®·ìT°`ëÜ:ûј‰§.5Úî—†ë-“W¬ˆ g¬Ò¿1™7vØÿ}s¹Xþݯ"0ú ¢|óó:ñÐsïˆÙ·].è‹Ý qdÍýؼu‡õNuÇ{tÉ™GWù>…B–¸ÿßKÄ5çž ŽÚµ®²q—PÂV! Uâê4n%“—§;d³+¢29«ˆ×—À_ì%¹µá%GÞãë'y°Ø¸e»øï[ËÅ ï|&F;Íy\á¬ëš¸wüe"É›HET›ý z¤NŽD)Kšbš”— Uï‡÷ð„#ú‹_ 6¡Áü±è׳“8æÐ¾õbG Öϼ)&]{ŽèœLŸôK¬#y•ò:åsú¡Pj¼6ÊÚ [Äœÿ["°Ø^Ü8ôdtxéeÿ^,|öm‘réñ(À]Ú·_ü°º‚²ôÅ÷¿‰Ãú÷€r¤‰—ÞûR”¦˜|ý9¢pW±øuý•¥Ú¦à7?­Ͻ½¼Á•¥Há^wþ‰qkºßø“¡œ¶?­ýC<½d™8 éчô‰ˆ«±êÚˆÂÄÔÐÎÿÎû@ïÆ^$ZK¤´@*mÔÇþ“¯|(Úµi)ÖmüS|Þ™¾Ý:ˆ¿Ÿx˜èݵƒÞóo&>ûþW±»Ø}»wWž}œx{ù÷â“oÉI^ñá—«ð­‘cÄA}ºˆÇ_|_|‡Þ4êe8¬_qÁ©ƒì^µ/W®ïÀÍ€~ÝÅkèÉIIN§ :°¼œz~þoñGâ7Œzí Y†þ+ìö°‡ÿõòb͆͢#³ËÎ:Vô|±;ì Nižh™µ•©à˜‹ìÞÑa§%? §ØYT,}á]±jíFÑ ½×¶ûøàËÅÒÏWе·ˆ–©)´¡‰8û„âøÃû‹ÎËKWÀÝvOà;V¤¥òzH7¿º]7¿ ³nœjt÷zÏ-Ak¼‡T¯ÐïË•«Å§ßþ\®,-ûúg¼+_ˆ­;v‰ýº¶Wœ},ÞóÖb×îxðûþà>¥/=ôÜ»¢M˼ïÅ(ðë¨3 1ëÉW…GÓ½¿4cÑk‹ÏøÍ¹:%gsˆ- ÕÃÝ;µÃhóNñÞÛÑ—œ†ú–f‰Çë¨W^uZ:Ûugcvè-þà+šZ(®=5T~¯Z³I¼òþW¶²D#LÄßY´ŸoÞ¶S¬þ}³8÷¤#ìû_Öý!ްyçoÿ^5¢¥ºvÍO(—)M©ŒÍzàÈÐM\Žr–¦ÂUÕ¦©ªÌn‰›¾\‰—ÅS¨§¨uô!}Å¢%£m´EtBÛè¸ÃöG›ê (,‘Ã}êÕe;*ÛõE¸ª|íÔ=UÕ6¬°Ô6£YJÔf|ããoÄ/”%â.wÿ^]ÊëZRRijù“¯|`×gÔyˆQšòi¤¼Râç߯E˜~ž†ú™¸ýê¿¡¾Jªò] ¯AnÁÍyÜïÆ^=²Tú¦5Þ)‡^–5:¨‹…í…»ÅsèÕNNòˆ«ÑàÛ‚ÆáKK¿´½¢¹£‹?X!†~´]õD£ŒU¤È´ÀùˆzÙæûuk/<º.zvÙü§ˆk1Uè“o~±•,ò¨Ä00E¨@|ôÕÏv!Ö ö襧ʄ¦=€Š…Wüí»‡¯KûÒž¸¾ô¾HMñbøüLÑ­Ã>P¨>¬K«tSVÀ;ŠR9ï*4Ì·^5L”7ÊŸTy¢'Û„®Ÿxå}»B: ªd©à§cw‰ »¥ÓçèyÈv[b„ì©ÔsF½ÓEèü¿ÅËì÷dÐÁ}Èj¥ãwÌ#ÿ §W?üJ,ûæg»Á±D2Ì}ê »R¸à´A¶1™£¡ƒ*¥ŸÐ>ô”¿ ªØéÝr û TÔg{”º íX‘‚EŠÒ¶Eâ±—â=ë'F ;U “ä]Äú¨¢Â¬¿Ç͡òò/žõ^$¤[ð~­ù} êšvöãŸñ~=òü»PZ:ajÑq¢ )AÔè„låï½ã×öEbêSj°ù—ýlã`º5âéx÷³ÅW« P?žd7Ò©#„:Tè z˜¦J}óóZ»³¥ó¾ñŠA^-O'ˆß¨í&*ÃD#ÜQ”l ÿÏÞwÀÇuU韑F½[¶åÞ{k§9ÅNƒB üwY!daY:K–M–²”¥&$ž8$±ã¸÷ØŽmY–Üm¹I¶ÕëH3šÿ÷ÝÑ•žF#iºFÒ=ú=½7¯Üwï¹÷Ý{úÁ¿éã‡+a“YªBÿ°O $À)ü¢Ùá2h˜VA»zûÅÔª“=ü뎮ÉÍJƒ)ÙP0qiªÿ¯[4]•ÖMÓÝœÍ÷ž8§˜ŠÓÇ*Á[fZŠÌƒïk`æbná÷0>6û~/ÇWM}ƒªCwãš7t·þ©¼þ‘áÙáùß!h„¿ØâV­¬¯z{¯µÏüc«$A"#y fé%eUm¥³ÌÝ…§äÑûo•OàJU¯ÏBCFÓÈî¾¥¶¢`ý&¢ÿö(¿1Ö4KùE}&>áReP›D"“÷G‹K䦥³ÛTØ”€‘9„“‰¹4ÜxùLµˆpÐÓ—©àØYH<&ëËrDzyª Jð(ÝæGF{n~œ_ºw9&®öÐèT…ó£þü]×Ã6I–@êóÓ¿®R~¹°G#p¢×xæ>ÀZŸ^]ˆb½Q‡Ë ¸añ õê-ÐÒ‚Ù÷Ç4ç×Ì…à”<…±zþb¥ò ¤?„/ ¦?Õu МfÀ_¢}üóþñøN>wçuêQgV(:yNù.QšH8Z\ bÀ³RÛD!¿'ší’­˜<Þ—ôŒ ÂN¦Ž€$’ NÉÍø¾ „Öo7,ÀB4£Òt C(Üã:D“­› d ìÈ?&éX{>B@áÏž] é}©Þ©“>þQb>k$>KÃZ…ŽžVZ¼Ü áÆóœ+nºÂóíÑzã‡ܪ,6|‰SÏÜ¡>Ì;;=Ãù“ýä iШS KÞpÌÄ7MïHPH¥Mðø5öSp¼î½Be-Ãëþ€/º†Ïæ`ž=«M#uGÓœÅZÑÕœÍ:P÷áÛt°P¸añtÌbΤѲrÃûRt¢D惙ò~¯wº×ÚW(õoê ¼©Q Í«ïzká¯Ójƒ¿¿ü‰›dڸኹå÷ þcôåÍ‚/áôñžµQïèé[ÒeDy¯¿‡(¿¶w^ḵìDæ7Ç óLì|€“ŽŽCb?ý)6î9 iØ\åcá«`Jmž}k»ZH²ÁùWT׫ÅÅz/5J„ô4ÏÄæ€ÄDG÷¢)‘,QŒ¥àÈÔ¹P)af–ô8žu;üÜÇj½ü¬~ß¾Í:¶¿M£ãÝ*.pV`€ˆåKf* J3» €r=¤tæ@Éà“O¯’§¾zD°ÌY]äU£d›&«WÌiDÐ ‚Œa<Ì!(5}Ïl†øÁ{–«k”P’©zìÿP¿Õ¿Xùí5ë‹Gú»í‹u¥:GmTò[§æõåµ»”€A›¤’˜9´}mçY¿Êªp–ÞàÂwÙ¸ZZä0„Ž4“ÚuðdÛmŒR¦šš¶GÂ8VÝQë/oüÞ¨®sxŸñÞ˔Ķ = §WægœŸÉðj&•ÒLùW_¦L®ÿ¶j»üöåuòÏ}H™ÿw*Øë„/ºÆëõ³;š† \Ws¶.‹‚- ùGÎÀÄpST+AÏ;]«}OWûîÆ5ø.þ®¼ùK÷®Pfx´Jzò/o)  ¾s™*‡ÿ¬õn;‰­åÓ*X }g¥í¦CØG=ºpP{˜‘¦„íþ|KÖ÷˜ãÈ` ™¥È´ÔR*MðºF×Zˆè/ôÓ š•& 4·ó†ww„ C$cw)mÑøh¼Á—s:¥u~ôœ04$'yêôeD°ñõ>}_hûÞ›à«w_©g`­Šå»}UÖ7þxÔò8.åw@Ÿ +ðüšŠIÚ ÿj—(ÕìH ‘1Ú¼÷°ZôBÕUd>úÒNœþ hB¤¾PW]6ÄY)Lìj1Óþô¤äÑ;" ~Öì (,ä:G¢•÷`½«¤ÙÔöî:xª Œ˜F S\«o ÍŠ4 £”/ •­1–-˜¦¢´ùº'1V×}z1ò…j ¡i§àŠZ= `Ù¢ qž£ÖåM{A|’äÄv<}?÷œ«?{Ç5òoÿó¼ òÀy¶'èj­ð~®'š¦«9[—ÃÀÐ_‰f†_ûô­ÊôóÑŸý]ßÖã¾»q­ö·Mú~îi±@Ž+èz[ÏñX»[\Â:6t û¯ºÕ:‚×çO#oÁ%„VG ƒ9ÞÅÔÆƒ±íé[âó½ 1õDí_Z$J¬Ì^Gö™Ò 9„€ üp#Ò AK¾hSM§tª¹)§´‚¡W¹ˆÐ¼ˆ’€™5ªr)e£ä›Ñm&Œ -W²¼ é C,SËÉ/ ÐÛxïí÷GŽ÷ '&äg/µm•5uUfò˜áj±Ú Ó<š›ráµÂÊ{•¶ô«÷Ý Ó‚òç7¶(æÊz>¦9}$h?O'e~[yƒüóU`Ù4Y`øVš³2°Š2sëvAó4QÂw"7“ò=@óÖ›ù,ð¿ââÔ×òZè¶ô½ù–ï¤^ÃÙ ¸À`( !M +ç‡ww*s¡w¶(Ë ®M4gp–ÝHE@?À5j+ >K"›Ú j“èÂor´W; Ž©5•Òq2WV³$>׋(Þy¿u뵪߼t†ùÝ+TІs+”fŸtÄ­WÍm«×hI·¬ß]ØÁ4 ý´ðs#Í2 ¡úñyÒ.¤h‚×MÓÝœÝÖ€Öjªëê¡iIUüVoÏïp‹÷{;\ÄîÆµ÷½þü¦ŸÑ)D¤hß‘Ónw´êª ö{.R+µk­š4У~XŒ;*Ó yÒsß’'[L|G‘Þw­b‰ô›}—ߊüÞÑ,pâx*iNDŒ¡O[î¼\¤åZHÈÁîËOüUÙt_‹èvz}³<ôø3*:׋gÊ…Šß­²œ¥f‰¯Ï¾½U¾óë—•ŸÔ?ßy­2b°ˆ§áÈÜ.@3?'!K¡ZxxK¾n±X§à[CO’øùñŸÞh«Ñ¯×)’\ÛE”ÊQ MÛöa:GS:š¸`ÐA˜aZiÊóq,ùÞo^‘w°è¨WÖ"ýˆ æw¹ëÆEÊl„ gO@ 5æLcèOuù¢”Ðé *$Øh¶Gíì¿Ý› Îr‚B¼÷ äuÖMÛª÷ô^s½ À³l„r©uþ‹îºG­ÄGnX$¿yi­ 4D¢’ÁVmÛ¯B"3Rë?}x™b”ظ›@¤¿¾þ}¥…à÷£мÆuß(£ßѬëɯܫ¾o:øÿ~OüÆi"FŸ\F׋:ô£±JmЗ?q³Š²öæCNŸÄ)q;¾0VXÓh2F `’§ôDL˜WA`Õ ËOÔÃ¥!å¡dÙLmÞ{D¾ñó”/܃cÞMÓÝœ­ë©÷´8X?7¤`TÓkLA$ºvÅ×{õ³Üw7®ýY{¬eñøˆI É9×Cíë«NöðkÑßWíDtåãÐ$kóïâc¤÷èÇG_uÖ‹‚xšNÞŽ5›keÌ|K=´±¿^æ$|î?~°žüö‡ßZÆ}@k ¶ÌÏ|í?^›1iì‡?ysÅÿ(%f´§õNJ àüµ7¯SJl>:&CËäPKì|ÕÁ»Nþþ~êé·eßÁ#…Ïüì¿Â3%ØÊ°Ñ€œ^ò.l½mýŽ ÐÞqØ}=ú³¹3&Oï­þï-DÄò{™Gņ?«ÙGoÔ—!ŒItYá¯mR¶ ÚB­-…ßùõ+Šx°&Hdä.F›¤IƒÐ0Àù$¿èøþ¿üä±ûPRØæ’0®'¡5°‡§ÃX϶ù¯·×=k“}}gú:-+´Å…>§÷ LŒÕÄŽ„1×4 ³àO¿¶Û}Æj[áE1µFÑ š¾cݹt…Ò3ì¯`žíªLž§f‘s4S¼içlý<÷l+Ëìj òõ^ëó<în\{ߩ߿‘Iµâ¦Gú¼Ü ã"D“%³´¹³(¤üé#÷µ}7½ý-'aøž:¡6Œói§²Ãu"Ö4KájWHåtŤðÛ·2J|I°ŒŸõцç»z?¯0è Ä ƒáÍ(i\0xJ%̃ÈÔ1¯ Õa­Za}5Z  zÆ@WߟìŠQâ5_¾$nibn ü  5X"ŽôŒ•`Wí¼i$–닦ñgζÖÉ[¨l½Æc_ïõ¾§»qí}o¤~Ó×ïd xžô XƒPà·é;F!à ûIƒù–4&¢¿ö;‹~MÍ   |`€I×ÀðÈCf‰ÙëéCÕ+æ>>êgN    ´cÀÌÙí¸àÿïC×Èöýlj¹Hä ßsˆ f)6úÁÔÂ`À` H 0*ÑÝ+.òió˜Á€Á€Á€Á@41`æìÎØ¦o7±‰XІ›2µ20000000000ÌÒ€ìvÓhƒƒƒƒƒƒƒƒƒƒž0`˜¥ž0d®  :ª~ñ±¿¨(DAb4000è—`ª”}Hh(0GåŸWnô1s¿Á@P0ÌRPh3  øƒ†@eBJÞpµ¸T®(ïó=ýÎCܹSÆôt›J²¼qOÇdê=>dn0ð€ ðà…óÓ`À` p 0 Ý6DòÙ¤XÏš(G‹KURg]Òcg佂2}üp¹ò²©*Ë¡“祴¼Zþ–×¾ðÑëpl¦$3³7è ”Æ3áôw>÷a•¤™÷Q0ñ…ýYúø |g#ºzÔœ7ˆ9 \¨¨–ß½¼^˜™I^³2R¥§õá"ÖŽc§K岩cdÝ{%~ŠN–¨ gŒ“…3ÆËQ\‘RÓSqíœ|öök:嵌9d˜ Å$Œf)&»ÅTÊ` oaà-dVwG,™=Iö’ »‹ˆ²¹­» OÊ’9“d˾£²rÃuž&z¯­ßƒp©Geþô±†QjÖ90ðÏ¿³Ý¿Í]1Œ†Ìž?}œ”UÕÊ/_xWÕ´§õ× ÁNž»$|}“ä ÊiÈýþÕ R]Û Ù`º˜ˆ|ò˜árý¢]&´U…˜Ý`Àˆq»A޹d0`0à ŽŸ“kN“XðÆ,dެpëUsḛ\q45Ë0Ui½ÈÌåÜ}C·‰.­å˜cƒƒ®?UÖ¾W(ùGÏÈìI£:¡¥ø|™<ÿÎN9]Z"2S–Î$ËNW÷=ûÖ6)~qÉ%)<~^† Î’û?xµücÓ^9pü¬L3LîX6Oå,ãÛ Ðx‘ÚúF„7!Ÿ¸å •”Ú­ÞyO:/™i©Jèñ«/–OBµ¤¬ZòœFù™ò™Û¯mÓ‚­‡0eí΃R‚vêØarË•sdüÈ!ªnç/UÊsoo—S¨?5 ÌÉ3gòh)¹T%ϼ¹í)C9™rïÍWÈ„ÖgÔƒæ_ŸÅǦ^;¾ù‹UrV6ƺ>”ƒ‘:‰1A`"o˜8*O®CDxkË~9­ËLA£ÁÙ2itçg¼Ë0¿ ºÂ€Ñ,u…sÞ`À`Ào T×5HFj²º?+-¥CÖqžLNLT×R’ÚBž ÑÄ|  ÃÀhÍœfe‡¸\ý3ñΛ6N¾ö©[d.L•ÈÔÔ7:ÔKªëåÕu»% ÍÇoY"¥`j¾÷ÛW`k—ûnY ¢ô"´Ã?ªšzåHÕ¼Éòù^/g/TÈÆ÷«rVo; !áðžåróÒY2rÈ ¶ò_ßð¾d§§È=7/SÔ-òî¶2Ï·¯[¨’G7;]ŠIãE¶ã[£æˆO ^WÍ›†-K=÷ô?¶(-ÁC¿ ïÉCµ­­ûŸVÄΞC§:9ìî.ùµOߪ•Gö÷…&&ÄwøM-‘/HNJT‘Æ~øEíiØ~WjJ’|ò¶¥pžŸ®Òÿ{eƒèûJ˪Ún¤ U Ê¡ÖàÔùKòöÖü¶¨}Œdö÷Õ;Eðú4”GàýV¤ä$©òeDú3<·­\sÐ÷1ð_Þ­ÁõƒË‡‚žÖëõÏÜqç¡Öÿ?xàζߌ–7{ò(eõ@3SÁ` r æióŒÁ€Á€Á€Ý…þ¶2J–GÌ¡Á€Á@°#ôþ]7.–Ž·iލAª†éƒ,мiõöü ß0wÊhaä±waêçhB¹Ð21$3Ú«ó+eÄlj.š©áµrh‰¶@šÏë[÷¹]ašíyL¢ê™ft[ტ ¤¤D»¬Úº_ùQQ«u*&ŒŠðÏÉò&4e|¦©Ù%ŽõLâ¾ò‰›:¼ì6„ë¿ L ¿GúYáów]gý _£¥~Ó$Êcå9Má7ú1o=ÞcÂǀܑSMG4Ó¥¥$Ê#ˆÄ×ÐØ„g<Ñ0yš°‡a²K?F ó„A×@Æ[m½Q4ímID'#œù÷¿p'|¤šÔ­4ë3`0`0`0 f)X6ï00000ˆ2Ș„2Ò<é¼Ëôf”¬×­Œ’õ¼f”¬ç¬Çéˆzç “ä +æœÁ€Á@$1`˜%ØeÔn¬Çú\ {Jö4ðØú[Ÿ7ûÞÇ@¸û-²öµéûÞïãh×ÀŒ©hcܼ/0@FDz„ÆB•úUÂ=¯ô•u*ÜíîKƒ¢¯ôQ_Âi u5̰¥?ÀSç.I²——WÕIem½4ÁL ej;½¸·•O¢Ój2œÊJCô‘š‹0­qŠ˜¶~–nnºß™¼Ž¡k™ñžÅtL®‚s4™Û@/}Oô¾+,é1®÷]Ý××Ï›1Õ×{ÐÔ?ð6ë G™¹ =¯ 4E·;bkt_T­´w‰Hâž @Ò’3IKŽlèÈ(ôã€f–ø!’!Zû^‘¬…#jM²Bc4:mÉÒ—,. Ÿ C¼4HrK…ØÝò8ܦ#»úõ ¦Ê²Ó% aQ5ã…>ð¯`¿sòe¶xlí×ï)’ ï‘Úõé{ëÂår¹åÜÅ 9©J.”W©ŒëŒPÅÈ[ì›NäÌpÃ'!^’$ŽÛLR9(;]F!§Æ°Á™ð£ˆï7“¼S~Z10;*âk>Ýøþaå3GCø¸x‰‹· ÄÍaÇy¬Ø n Ö_æ¤zcÓ>É-yÝÂéß?M1Rý] Ù[ý3`™%Ë™ü™·¶#j½T$äIiê )ÃÞiKˆXØÝÍ’Û\*yMgäõ{A¨–{W,BVôÑÊ —L“Èa@3IÍÍÍJ‹øüšÝŠIŽFÿ÷ž·2Hõ Ù]tJÞ/*–È•ÒÜš´’½åŠK›]œ0PÈà²QÐ`“Dwƒ$¸› $€S¶Å.‡ç }aÍ.„ÁoD‘D$ NWÁBâ,® v|`=e𔺆&ymÃÙ°»P[™5i”¾T¼DªÝŽYÒ‹5; Eü}©‹ÏÃéWJ•½cÆðH!œŒXiâ(µe9/É”†|ùí+åƒWÏ‘ÏT‘Œ–)üØ×ýîtBûÑÔ$ëvÉ›[ ¤^õÿü¨ô_ë{.ZÜ›eÍ{ȳR‘KñirÉ>FªSs¤Îž) qiÒ⇖ÌbrK½¤¹ª%ÓY!µ0{=R¼[…$^ÎÍVy™=+;‘Gi4¾­¥slr+‘ãg.´¥ %áħ!ϯÞ)—ªjå·/¯“›¯˜#£‡åâ]§UFåÉs&àÞ$•ª´¼Z½÷=|û_øèu*7Toá%VÞ«ç•F£èv{¯Ñ+4ÝŽxÖv IF–š›%啵òÊÚ=Ê×úÆË Ã{¸y@1K”’çcâ~uýûŠP.HkO ×ž"v™Œë1³Ndå¦ý2ÜÉcÃDŸ¡c€ýÎI¸Ñáý‡‹eÕŽ¢˜èÿhö=#½1˜E |Î]ªK5r›ø!žœ°…¶ÃDŽDQZR‚ÓR\Z!Ó.Ά…Þ^%Ôǧ˱”Y`žÆÈüºÍò+HÆš\ÿ1ÜÛˆëUñC¤!9]šâ’¤Ù–(nHÔâÜ-Ø\0ók”Tg­””ÖÉñ³‡À*AÃì‰#効“”,MþÂmÞjÆ”WGšŸ}oÁ_vwáI•Ciö.Ci£ñ~ÞɾgûÉ$1˜EÁ±3²÷ði9pü<45fÒ Îø$å§×âŽSŒH<‘Ä–F0#ÍêŽâä)a”ôû¹¯Ï”s Ð6Užòxø¦Í”r{|ŸüŸ¢È@e¹Êd|›Ÿ‘}Œde¦ÉHιxöõ-…ã{ècÊÚoæ¸oa àø9¹vá4Y0}œÒ‘YÒ@†éÃ×/iㆫSd¦–áÞE3ÆËeSFË—ŸxVÎ_¬Ô·wÚ§Âh¾7 ^&A»KØwè”äfeHqI…d¤%ÉöüãrïÍKÔ5yàîŒy•†‡Qˆ4Š÷|ú:„Æ 0³¦FÉ@à Þ.”UÉs«vÀÊb˜¤¤:2p,v~ÂJ¤ó³}æŒ&×î:¨‚9ÐG©·LïºBës8e¶¤Ön‘õæ­XÒ®B¦FWõë‹çu¿ÓG©¡¡Q6#â]‚N_Sý‰¾çäY„ZdÌhÞµH ÄPf*©CaN—íñ7ê ÕO¤Ä–&q 2d4àhòl9ž<}\>Wa¢¶ã2CRWHþòæVy¦—Ÿ¸e‰Œ1D1MÁ~SyLEc ˜wDÕˆø™³UB¢hAׯÂlNÃEhȨùI„y8Sj¬™4E–š%_PQS/Ù` ƒ²2åÖ«.S>‡ü=’ïžÓò¾z^h4Šn·u®EÀú(Ó»àF>ñ–“™ -n5¢<”›—Ή¨Izpµì{OG•ô±vzˆF§Š<ǨgÑ æ(šX/Öo ¤oÔ„8!ycÝ ‡ÝïÄeò&½wø¬Âo,ö¸úÞ³ø¸0v\rîB…<õì*%a:UmÚÙ’q ö ¥fouÐæt'4p#0k´%ÕØäƒe”¼Gë~)a¸ìJ_Â…R\î§žY­œÏ‰›`¿«8¦¼qk~÷] L?BöA»‹n.@IDATLMóh}ZÝ}6hÒ¨¡BíÓì=T¬.úæÁ7âü¥Jip4Iª0ÃÓ@ÿÁêÚv­õBnáü{õ¼É`4]Î a…‡¦À{=¯0:î@¢Qt»õ½ëðõÎ0Ñ¡} Ä£nÄx2tdh¸ÔO÷{fIK.NÀ›N⥠£tÛcrÏú1DóIÔ· á­¹¨± Àîwâ°±Ñ!ìÿF,î±Üÿ¡ö½f”˜;lï¡“òøÓoËÉÒj)“´+c™\@Fúû 4``Šé×ɘ÷½´v·¼üî{Š™ ”aˆcj •þÞÞëMWÖ?õL䊻m.ÍåjëòÈOž“··æ ÍÆ#µÅt˜éQCûõÿy^~ò×ÕÊ/P4oê…qÈÃO>FëòNƒù]Š|ë_F9“—ÞÝ­o5ûV èye Ñ(ºÝÖ5ºyÓRÍØˆÇмW†Ž ¡ýÞ O…'Ω„³Ì£Ë êz¶‘ÁÆ!M"zÚ©(X³¡Xnk$ë¦ûù”h‚w ó×Årÿ‡Ò÷º½d”òž–?¿¹]jl’Ÿ¾$ºÚ¡Hvje3_AÚb™Ðxaã©p«+ñ‹~åþú1i”1ºÍ£1Š‘¨ð•ûnVI£¹¦hŸ¥!9™òëoÞß¡ÖéÐ=ò©[”`kLyèã+ ÌC€•„ö󼞛!?xàN•ÿ…) d²…ehÊG¸ò²)jS?ø?=¯ 4E·Û:ŸR–§ÇMWaœ:U‚ JÕQoŽ'"¬ÇÕxÐ’†Ž ­ ú=³Dé1C —!œ©Ó–Ñ„³¡u…çiu¬gEMò!/M’ÇdÈ_‚.uèeè~'¯ ¦!Nñ2è®}3‡ÚåÅf¨[šîtU‹ünO£¼vIUƒ„+FÙeÿ—Ô5u¯! ¶ïõ¢Ã¶ž½P.O¿¹C1J{Ó®Œùñ$Jƒ~ìxò,å‡õ&$å³1od^®ßöÜÁŽ©¯.M‘šçñé¨v¸¥à¢KþsS½+wÕŽd®_:Ú.NzptWH°cª»2͵¾ú ‘ñ¬Œ’õ~oFÉzÍ›àµ3²kG¾Êzû€>ÖóÊ@£Qt»­kt<üN£•p–ÁE²ÓSÕØ£ßÝ‘Ódõö=šhS»:~ä`¤¶¸Ðí¸¥0‚‘&+AËõ³D<ÆÇÅ+Ú—40ñmèÈn»¬Û‹ýÞ Ò,. Uø£ä¨Þ-Æý¸ØˆÉÕPŸrÑ9?3·X0 û8tÀü®[ ýÿÕUuòù7êäÚo_›*i Á™¯‘°ýù-é’—æß§LßsÔ¡Ñ_X³[šZâ$?u‰a”,ãÁzÈPåÍb—··Þš{\õ³¡Œ©ÒZ·|èoÕòƒ õ2,=N\”¢‹ xõ»%Q>æihšMö—ºä»ëë¥ã~ð«?™%_Â8zdiªŒËŽ“GÀÄÌ{NJœ¬ùT–l:Õ,ßÖ:¦º+Ë\ë?àx¿cÙüþÓ >Øë¼ÒÔÒÍuN±¶ÛºFGK«¤qVר¤Âá3$þä1y2{Òhe¶6l0Ö|$ue®¢òê:äg<-» O /Ø ¹e)¢ÃÄô‹ÐL;sþ|û‘S,CnÂùá~R…ˆ‘Œh¬5OYÐ^}æö«12E Ož“õÈ_Æ )ÑâS+CG†Œîè²ñ!W7ð´ª—g ¨V Ðò»»Ÿõ%±¯Õ§ÝÝk®uÆ€îwÃs“ úŸ¥ÎⱩkF²V|-¿ù@ºd€ÈýöÚzÙ[â”'oJšîr‘ÏàK`Ž® ¿zjýŠùÇ#õÔ¶ùúdöé{VÉÄb"W5C+Y’8Ö· ì[âÒàWázhpý®1ÅñÂ>nÀ˜º,Ï.?^ž&ïŸoVc*+ɦÆ’ FfÄɯoK—êF·<±¥A΀AßuÞ)%Øs<=“ïð«#S~hn20 ô¼Âo´/A¨sŠnw¨kt8pF-+ýöš \%ÔÁϹŒÍÓon‘Ãð9ºñò*ºµ4§!¤åÏÊ{‘pù„´}lÅbIEwBÙ mÕ¥ÊÚ¶j-ž9A ŽŸEdº"™?u¬LSMÐýD|B³¤æ '¢”‘ß‚0öÖ úÑ ©rÅH»À¥EÖÁoàñ­ Ê…šjÆçÄÉuãåxy‹<º¦VNÁï%P€N¬5‚ é|†¬;Éw å÷çû5¾ØåŒÓB©J€ ~LO&4s@ØÀßhýI§,n—Q™qrÿ«5²ã¬Si”nœ(ž–ˆ{<“l|šW63Eþ°·Qù&<´$E¸=l!tü®)NFºþ~?gnTÐx#)Qáï@€&PÛÏ4Ëc›ä@+#¤ýŽŠÊÚ™ßÂK. »AàjFIŸ xïgß³]†f¹X^vºUß7ÀHn©—!ÍgdܨA .à‘¸ë BSTÁZRþ„¹a-, T! ƒ/Mó4h暥=ç=g7žòŸ)ÒåtÚû9¦:=gN  DíóJD_þÂCœSÚÛm]£[UêᯭÏm‘[ Æhå¦}*Ķz2it¢5N–A™éRQåTÙ,ÁмŽpþR…¯ËRr©Ýô¾f‰–È’>óI®l×a.z@×ï™%ö&J @M¸xkþ¸¯QùŒÊŒW„ÎUp¸&³D ô8Ìe¹©qmQ°Ô‰þ™²º¸UOÁôÿÏv4(Ÿ%kÑÅ­ÂñÐ ìmô»<ÞPÜ•¬1˜}$ÚL=úÂ3‘Â%üŸ{£Vµoß—%/I•_íjwO´k–¼ñS Çüçòå›W§È–Ïf)8ï{zúÝU{xžÍñ|")Ñ.¹Í¥=ç÷õŸÝœ.s‡µg¿ôq#‰ûü/äHxÃnH“G¯ò?¶÷óþüžÔ/—Õn‘tw½Ì™.#rÒ%ö茄Gç^2MÝAW}ÐÝ3þ\Ûr¦ž›ëåŸç'˪û2eú»|uum·9¹ÖÂîH™SÖ}:KþÁ!‚Hµ'˜º˜g *‚ù ¦ð‹9jÛ÷ùlyãÞL¹cª'p°xdÔ_úg‡´%{C©S0ÏÒo[þQY2{¢|å7)A£åi8Œ(ªdö_?¶\n¿v¾48šåõM{däùüG®{ÈNn^ÖïˆÔ>–ñ©6‡«Ü~o†ÇÁì±jÊ`FSãh‘áæ^‚vÁuÁ•ˆz6ŽØk-Ä2¯ß…°ÑŒ”vç´$Ù‰@Ô,ºîÁÖ?˜wö—g¬¸Ãð¨aú/;«ÒuEðQºùr¸°Ôƒ¶ýà[ͬøÜá÷ôäöeò寙žµþúݾöÖ±1fPŠ8J/Hº«Rjã³}ÝйU‰~¦ÚÚÂÎ'@UöÂÝ*‡Pç«íg¸ “¡óL­ÛñHB¼»Y¾pa0䟯wBµ :Tâ!Eì)l¸®—µOSO" ·®à/ûÂŒd õ§^þÜó«çßíò¶Í{ƒa:Ö&Œ´ÞX×à?­Ü¬¢ã9Ç‘pèd‰Ú˜Œ¹ Z& "üè+Õ^ÿ{aÍN}Õ½ÆuT_ÚÏ^ÖPú˪ør›\ñûJyþëÆ4Ùý¹Û4ŸÑ@ÿûf'É®KU&[?ÜœYÔ!DQ»ã¹j‰‡Ù_\’Sâ’T~MûËEF]>}Êã÷­âá¨>ÂédgµÈïºaZº­ö›¥ÅQ'®ºjqÕÔÈå™È²•ÞíS]¬£4t­|겚÷½qª}ï›—séUcírÿ,¡ObŸD­; iëdzÝ.Ù›qmÈþK‹!m<À&$Îé‘ÌÓGg&)MéŸ÷:äÎgYŠ2EýéMéˆ Ù$ï`Áþü™9$NNÀñŃ!ƒ©áš1 rÛäDÙzƉkâÍ<’eºn|‚|pJ’¼_ÒŒÜVÍR †`6Blß5=I†À¬5 í¯w5v`Tuù]í¥Î—£î¹’íº(ÃO‰óD‰ì:U%õCæŠdäI¼½Yá˜Ñ t}òµ3oUðOû:¢ZVÕ8¤¹¡NÈ£ÑPÙ ÃIOðY† ¬Œ’?e²^ ‚Á€&ž¨•¦ïcñù2•_†9eªàÿè@Ο€>0ïJ´~>Ôvg¥¥Â<)MfL)Œ¦…VfÊûqó»g t• 0Én“G—¦È äd7¼y{¤ŠÁ©®ÇÜúÌ£/p(k‡MÅN™šß)êoÏoïÿwh†§«–6¶ú*Y¯7 €ƒþ‰Ã,õЯÞZ2CËŸ®R“‹/‚¹ ’žû^®é$%îá5æ²Á@À ±AÂûjš”%&&ɹ:»ŒI¯ƒoÎz)H¿\êâ‚·^66Q6c1ÍGÍx|Ù0»ü/Ìo™˜$¿‚ßÞÝ/ áñ›°?½¿QJëÜRéçk\²úX“|ÉV)døÄK5mm»Á ^=ä/€¡‘a“ÿn †¢o Øm“’°¸×ËÇf%©$ÀLÒú_×ÿÚ¡&D¨t*ÀÀXUOé.[¼”Ù‡IYú0É€ömFÝ{’~ñ}©OX"î´ÁbSâÍv&I×Éì ú È$‘Ab`̇Ö#÷ˆ÷H-òDzvÚ’¥1.9¨î¾p/ ’ÜR!vøRþcó~IGä°ëL•e ¦KRRÏÉ }•iÎuÄ€5 ¯|ùò5÷þú= ö ‹Ð§W=…95ŒÔRXÄL‡+ÁÊÃMH‰Ñ,GËãÅ;êoÇ7˜_†Yòc øÒ øb”¬E*%¶>kŽ ˆkÇAÃ2;]RR’•æÈ+”|Ñ5!!&Érå„t™žÝ,gàpZx±AT¯— ‰£ålÒx© ƒYÞKˆø¸êh³Ê;õÎ'³`f“ÃzºK(DÐ@..¾ä=æƒÁ²Ä?³Õ¨¢ ¦%Øäþ˨–éhžvã„$^u)¿ÀrD„»¶øßßP/'¡¥ºÌ×™êÅ4éw»'>ÞO¿FÕ®—éÎByø¶å`6|â™9~qKº44Ø¥ºÆ-eåMRZ’"köÅÉ© 4†ÁÖÙë+ [ó!änc2tFí%Œ/Á92K„dP}4 Ö Ó[Zm༣þª›Í?ƒƒ…Ã,…q T`n²IxLc¦¶Ë·®Ï”ÜAY’™Ñ5ÁìoõŸzúmÙwðHá3?ûï‡ðL 6fTƒ±›¢VÛí¤p"Ê@Fz÷S’‹mØ}=ú³¹3&Oø“7ãgà@&¢¸«kì l[@Ø:@Ø–÷;–š%šß‘È'C•ŽÄxM0  †ÉW-§Ë×T ¥DP¢œLž ÆiBàm}¢±•1¨iµ4Èò¬á0±{ôÊù3|pt.!kð-d`9>"¹1Â[I­‡ñ: 5YÔõ<øV-ÌGå+È[v?´O÷B[Ť®¡@S\’Ož&ö’½rêü%?r¨ræ5„[(X5ÏÆ´6‰Q3™f`Ýî"yskÔÇgÈáôùReñ*“ +M¥6F¥œ‚€+¿}e£|ðê9rÃâ™J;®Íó"^™~ò_¹éÄ™bi(¼è”ÎH^Ó°ÁHy4*ÌÞ`À/ fÉ/4ùwÓ·×ùvð÷ïis—Á@` ³D¼Dh–³Ô”†¦N$?EP˜Ú ÃtÑŸ& Ý–z4]oq(Fx¤¶'¢d¤ bÀ 2B7ÁÌnLð~ÿ~£|qqg‡ž›'%Êáï´b¢]ù-y·˜öõ“! }á ü‚Àœ1T?™¢ ˆ2ùÂÁ&ÙvÆ¥¢Æe‚ ¨@ЕP¡ÊNþ\äli™Êân¤Ü¡bÔ<KЌ祵ï”UÛ åRÂH)L'-0M69Û ­îôú÷¡eÚ§\£nX4C™$†‰þšHðþ‡ì+®—j¤€ƒªßÝá+ ý’JkÝ2.»½_Çâ˜s3¯ip´óRú”Ù  tƒÃ,uƒs)x L{<ÿE_›ýXð%˜'{€öYRfxÉ-È·”ªr.‘0ªotÈ¡âj©£TnÏ“c©³¤>.´•(ðÂ]™0ý³©p׬ßÁ‹.Ù}Î%Æú¥Â&y¹¨I~¸,MVL€)BX8¹ISµöS™¹ß"?BÈlo`•]œ"o"Kioóé&yôzùù-i’ MËûížÆ°0J|·Ûí‘¶6;¸$IÜÆ <ÿ®5'š:H‹c¥n¦± M84:²ÿp±¬ÚQ¤¥‚´…½Zq2i¬ÃLÈWnÚ/C²ÓeÎä1Ša¢¶¼¿ç-&¹g´ÕÌ™.¸ä8ÒŽ´À†ÙŒ¨Yæ#5E\hÀÚ6"wÛr˜3¯;ShœáñZßé |Eý 6‚oWï0ç ú: ³B’œzãã™r;¢ÖY„6ªÄYÈ…°ÑÄö „ø®s³¢^óÎ|ª`qÁÃ3Ûâbb’þ6*ݧ™%š-,mï6élow %±J»ÔâÑ.‘0*-¯’‡ËÚÜ&…i‹A ªšËþÜž[‚¬C~Ÿo­«W¾Hí2J‘ϼVÓ¦Yâ}ל¬„‚MI2u‡ÓÕHÚ÷Oy?ßÑ N‹µê¿½Ó®‘½€°üÔÐ÷ö8·Ò.±ÌÛž­V¶öVæ‹çC…ôO²RáÔóM—±4µyðòdøj¹ ³KÓê±LÓ;j”ªªkåµMù˜d(R¬TŸÚ­µ5òš=2f° ¡…@±RÇ@ëÑ%cd4-4€û¸±^Gþµß~¹ä0nóôã-]Gåõõw û\3ïÞø‘ƒåèé `ÝÜÚß1`˜¥{x2ÂlÞƒðÉ?À„D yB[&BžûßÛÒäošäÇ3þõ5õòÞ¹þë >çñ£C›\õÿ t,WHé'ÿ®cW>2 áË ‰ Jdé¬ûÆŽ#Rå´ËþŒ+BÖ&y·»+‰£öEâý¸¼¥–‚¬Œ’åt‡CšŒ ]E7£ÄÂG:NH*rcä¤{˜%j•bM³ÄzB×Ń¿0À1̈wôQ¢ÿæfD¼c~˜Ãé {Åô®«JSÃt8e¶¤"aôzøR­X2³O……1¢ dü 8™’•(ãSZä\a¼œl—u…2¥ò•M?À褽]«%¾ƒÉÔ50¨ƒì Ïqïõ×zm O5T®[0Í0K­ã{h¯a–ºAÐ ätaÎi`ööø‰mòr°ŒÈˆ“ÙCãåcHâÆiè?oH“…HN»íL³üÇÚz¹ ù ’ó—ï!·¨ËÛ§&ôfiêû× ~Ù— ™y¨™úÓ¢©6g󯋙}ÝìŸìŸÐì´ýää è/prbB}½£áüž0õñü}6›û¥¢Gæ|{×´'|‹ö¿¡˜æ8›íO…Ìú Ëì À(nßEØëœ”8Yó©,Ù‡Øï!*ÛÇ‘Cë“s’áKc“Èô›= ²¯Ä%\”V#BÜã[ëåsóSdtfœ¼ ßlðŒh´™’äÕ;‹¤‘òÓ—†•Qz¡ÁOTzq/ÑhTß1ÈyA²œe2}\ÞÒNPDê•‹GÚå;Sƒ0¦è•—nƒ9ŽS6clýîö ùÚê:ä–òh¡?‹H·!úßÝ/0‹È˜l›¼|w¦Œê…q)|˜r#‡VÉã§T‡¼Iï>«¢ÞE#˜C ­b‘oKþq¹vþdhÊRܼ&в"yÖì“Sg]7מ9tÉ‘ÜáÃÔ‘ßÿ¶Â¯¤ó\(D‰Ý3‡Øef^¼Ì@§›FÔ"¿?Š›ÂV%ŠõõןçúÓ=£†’[–‚y‡í‹wß ÇÎ\”u» åŸ>t¼µe¿?{Q5÷Že󥮣vÈä1ÃdÁô±r´ø‚,™3šÜfÙ]xRöêO¨ðm1ÌR7CàΩIÊYòÛëjä—È+3ÜËE¡‰Ý¿¿[§d½œÞÖ!ç7ñ{-ˆjNˆ¹©q*Ä1‹>]Õ"‹F¹¯ÒÞns¹[îF}¿Ë:ǹšîvÛlÛxÜä´ý\âä/öÑñϸNµ|Õmsþûû_šñ/3rà§«åû‡¾6çÞG鉢Ü7ØSã®w%Ú›š7Cõ×ý_›Ô'tÛgÀ`ÂaÏF_}ËLé6xj{½œCé¯^‘",L•yÓGh$˜è¿*U~±³„pœJκ&«EN3E ²&ŒÀµÊþcçUÄ»ºøàó+©†zýû?lèOàvÈ´ú=’†èSGvL”IM]$àû×¥Á‡Ì-ßÛQ/·"jàrUŠ$ƒÉ{)ý½Á/4³Äð¿4áÕ,µÙ?ßÙ(C1·üËÂd俊츊DûM™ÑÁ@›V !Âá»xh–¦ŽŠN‚xKiÂ(É©/•“¨ëäq#T”ÏÞ ö°à7çRëjË.ø Åݲû…ˆMÁ^9TU°]XìvÅ1d·7Àò×@ a  ¡ôO—–Ëð!ÙòBÜ×#É,׃,D𥹻†tDŸÅÒ« ç'À„‚cÅÍ#G o!µ„«G'Êå#‚b–¬õ×eûÚ[ £“ç/(Æ©<~ˆ¯[͹V Ä»]2»n§$º›dÑ,r"2°%ÚÝ}kÖ> tL1$úج8åߵɭؖOÌjë“•H¸{Ϭdôb\vœÐÜ—šL o#XÆóõóª1 A+kýu¹fßÿ0à™\ìЬLðŽQŽ9Šy”bTÝPÇ¢“%2näá“Äiwßc¸ÚÒ‘1Sds/¨©¹4M3F yœã},ã0F]ÕW—]]åóºîÁô“õÙ@çÓHá„yǪëelÀ‹K˜]y©’ü‹º±a÷!9qEžÃ9“GËØáƒåH±""U]¿ËÕ¸öûsc' ôkfÉú'`â—À¸ü=Ì‚ÐÈ?ÙîIœÉùòÃÓ“”£¹“V›`ž§tøËÐ:ØÁ`̓9×!KÎës]Û݈ÊEîÌÖ¶XN‡ý0ÿ‘9EÓßß0ýñƒ³ì6§r×Á‡g¸ž˜"UNóÍM'œÕNŒ¿4{ÞˆÄaß¿ïÊNô@ õµÎ+ÁÐ(¼+œ÷†:§XÛ­ëÅ®n±øWé󱸧™¨7œ/«R§ê<ëo"„o½ ôa¦æKƒ/¼ëkfß=z¿7»¯_Ø®¦AzœÜâÌþú2¤½ß„YÕóweH"¨âo­¯—-eü˜Á¼ðÑ ùÔ+ó+ïò œtùÆû³…ѽžØæa¶¼ïëêwRK#TºI]]Žüy›íY·Ûu—‹CœÛ¦4Av· U¨»¦ð‘Ù‹ñÁuXN`†7ÕW¥ îž3T{2& â5 ùåçæ'Ëg_«Uþi÷Áwé›W§`mmGÇøœöÅ‚#"ןwÚ÷-ð‰£#7¥È) q`²ãeXó¹€:b ÅU'³ê·Kª«V¦ç¥Êøá9’š–*)ÉÉ’é¡ö•èøT׿SÇZý½èsT…ìd²äN™#šÝ]7.Q1K+wütÆb,i U ã*Ð1¥ßcö}œœ­Ì’æwuØã:ç<‹µÖ5"I4¥údòX¶# NQBu7 “ÃFŒžüØ®—m ‰ã1]Ç{JÆÿ®^µï.·m·Íݲ§¹òܲ•Ož¨Ê_Cß°+‘8}Xüäa¾Lë‚­s04J°ï õ¹HÌ)œO«cÐqŠÂ‚•ùš“¡„V<2m¬CÓÓÿÊ@è̹éÌÔ$±»±a"†Ë )£Ê$Ùm˜¼A”¶N¬ËŸ®j clJóñ—Ú™'ú±ð9o­SOïeýìâ´¤te‚Ð’wRÂs6GÓë-n›+Á澋u’›–¾¬¶eÚ“?‚Ÿ/òÂ…B¸ðr»;A¥ãy.–€¸Ó[2Ô| ÒPÿŸ€ÿÞ™ÉBªFH¼†gx¤I¥`€IôÞ ¿Þ‚ÿžÁqýøDY S¼Á0Ó›æêÅ‚vS*us7ÿ‚í{ØÔ+I2¥É³ÇdËž¥2ÊqLÎ$MìæmëÒð¦S2©á¾-¥d;,[ÒÓÓ±¥IjjŠ$%jf©)ñ…!=ž¸dLv¹¬¾ÁA’àóÖ"€’7¡å1ÅK’IHÊûïk;†ÅÒã*AgæBKõJaÇë¾êÊsÁŽ©®Ê3çcÔ4k3<ƹ ‚̶Ø_ê]ø:››•¦œs™n ¿µž@3FJ]ÈcäÉe¤ò©iy,l©$¥äødެŒ‘Èn˜ïJKÏÝ»û_FèÉ›¢ùltˆuÛ Ò^"q=œsŠ÷| iÑ•–½å•5²`êXÉLKFî=àp4Ë…òj™>~¸œ<IfLõÂ?²·ÚÀ÷R«D|r\ùó õf]û»c ‹úc;ll;X,¹Í¥R ÔÝÃãOc_ÏõÔÖúˆA`–ø‡I#ÚýЃÓÎ!*^%¦«ø_}šu^ÿ™ñӞ̿GÜ®?N{"ÿ1,dÉ®×\ºcßÃSÏ" í¦©OäïÄ3¯"rÞzjg¤¯k¼‡4åÈMO’ãe õÿZ°€á”uH¸º õÿúfì<딕÷fJ5õ™AÑͬ°Úÿ»=]åÚ¿”w;j߳ͣsÓäôEDP«=MZƒO™‰áÔ{ 7½qÌhw `vW!i0©6‘ s³$+3C23Ó%ÌR œué³Ó×î¾µPÇÔOw4Ê@yûÔDY†ºÂ+Ð/é›W§J!ˆ=zVxã°£m\ÑçéÝVß9ë=¾ŽCS¾Ê4çbÚ ó,Ññ´¯€fôXw-Ñ÷®;£BjŒ|2FÞw{ý&cÔâ>Œ)qV†]>#¯"ÿSÏ+¡Ð(‘¯eûÂ5§èv{¯Ñ ª–Ò{–5‡‹Keî”1ò¯[Žhxäï«wÈÖ}GåÚ…SåK÷,W ŽŸBb{]%ùés\i\·÷¢9 ýšYÒ„BŠá¹J¥@™¥@‘Êý¬m—s³R[‰7|’½Á0=2çFïv}uöVœ›:ûWûsZÜñÍÞP«ï)úÚìO_þ?G2I“”Íᡯͦ$® À@}©íG„4¾Øï$€ã`š–FŸ›ÒÿÔÜððYÉqRZ…|úU$`Ñ]×ì!@þvÀãt¯M˜·;åkê$5÷4F¤Û÷qXñ™cI%§Ñ? ¾8Ï"ÙjÝ1axì£É³šwh„±;ÅÇC;;B‡‘Юe¸*•É5£¥‚AÊì¬LÉÊΔ̌ ‹V©{gòpŒ©: r(s=ÆÏúOgw@“CÎþ_g«í<ÇœÖ^ÿÑðWÁŽ©¶ ˜ƒ>…­!‘DóÜjhúP hÒÌz³þlK=æÐCÃéà ¶šRq”Ÿ=[p¤øëõÖm…)#²rê(‘è?½É,1ÙŸVnVœpðÄYµ%&Ø;E¸+8~V¸ipÁlïGX©öÚžxLID2о¤…4Î{­B}üÅýšYbßP«@B’Û¬1¹â8Z‚Ü*—$óM°^9 ò& ÏT„¯®w¸\C³ùÌéHѵ¸ãK“=IaB}Až·ö;™ú£ Oƒ{uiÀýO+hFÉzNÛ°Ø“8 ”Q ¶ï=ùHâ„ ’“|Zš•Mì”ø:9[[#sê¶a¼çÊ9˜ë_J¤”ᓈ1`BnS‰T&äJ“­¬QÖ=ý²]—¤Ñ–*ŽV? Ö!þh©®jÉt–K¾#ªg`%F3ó’$¾Iéÿš)»•QÊÈ€V þJ­R÷ÌŽ1ES^2JHú Ž«`Ç”¿õ1÷Å&Ú&|ÔÎðw_|¯#=ÇYg«sÉÉZ;܆º7eÛ4cĬ»nGðÝ<°žÔøÒýN\’™•“Œ_öd„¿ÿ™ƒ©>ÏPúžm%#È6ÒœŒ>8N8šÒŒ…™Ô ¥5ðƒs”IV}üõå"¦ò„!Riâ·ÿžu1ê^FK… m:+ÚŠU”A2c{Ó¯²Þöã8¼wfýN0Eí¾^0±G2’Ü’ m`Nü‚À@’y$^¸Q³DÓ»´ÖÀžï¬gF)cêˆÁZø“DBS‘¨)3ºhc˜ú£D BÚ—è;>5Qâ-’|oèŽ1ò¾7Ö{Ï+…Fñn·u.©­•òª:ÉœÝ 4Öû1–êGk„òªzh•âÍKúÀБ¡÷Pç™(ô2cªJÞ˜Är*ÝO*ž“éõïKAÚ˜©ët&È„d|ƨ H»“Tt®öpÆáÓÄLƒ#\k¿3Òø‰×±™õr¬ΚÀwAÚ¢°Õ‚Ôõ,Ú¢~y(}ï‘ÎA³ó;2Êô¦5zf“‘ÌÉ)+œRÑä’Ħ“2}™â‘(5>Kêã21+EšmIÒ—€k` u‘ T€dwƒ$#š\ºÒÞ”á*˜1 Ë4›fn øá4¥~ŸIgÃ/ÁÊpUÉÝ'©-52,©Ñ)ñJ7¼P.¼I0ßHIˆ‡–È3Öõ½'£¿‰“ÔTj•À,A"“Äsd¢üñSÒ}Ä}¸ÇM<#¡Œ©HÕÉ”k0(c„€'J[­‘UchY±z¿u^H4еÝÖ5zŒZ£]Â@ ƒuÎ@`(¯¬E` §\1cÖº$µzÒb:20Lv¼;Ö˜%Ú ¸Á£®±YÙª„Ü»ZÕ«Í”Æ $— µ8xö¬ÌD@©ÂÔy½ªa¢˜„Íàæs2.'QFÎT¦T$æXg¼áPŸ665»‰vÚ Ç–}ÇÑ;¿:ÔÉér6„ÚÿÞýNó4j^†f¦ÀD®VõçÐÿï¡ÿç÷Zÿ‡«ï9FÀ+Á­Ýä†çâ•Æ)AêêêUrÊ”ä&¸@4HL¤Õ.©CløFgL×jÁxÀæš½Ð-Ø$!®EÒÜ’ŠXòiØ3vK Œxl³TN'MBèò‘Šñ궸.¦´ÔÁ¤î"4X§a:YÆ,ÒÝ’ƒ~äBË6jç`Ú½{ðàѲ‘QJ†‰‡YÔB¦ LΓ±$S¥žÀ$q ©ºF8Ÿ À CòY¿Ýž3×#ŒPLîȈ¼ˆ”„&Ø‹ž†–çw{åµCÍ‘üiýO–Ž¶Ë†‚Ü$Ar’.Ï—"óÇ¥ËÜ‘Ðód?ïye Ð(Þíö^£Kê›äRE ÊÎ0&?Æ¿Ò(Qª‡`tÆÈ,á8 7éG5úí-±Ä,µ‘j-Í® •Uuü2³D©³‡ˆJPÄ2%Ë³Ç U£”ž•µÕr8eN¯ø0ѧ€&ai-Õ2:+^& Ël“~·GèêÙDÈŸÑYY]çnr8àõß ÚðÞéJtNtzS££2Ôþ÷ÕïL"$iål7"ÅIã9™Ÿž#½Ðÿáì{O[é·”Øf²©ƒ=h¦¡±±Q1LÌ»ÒÔÔæÁ‰pø4׃8˜,'¶&'B CID¿„GYÐñûÁFkæÁˆ÷0 *ÁŎĈf0!NÉD˜ªóõõÑ/“ó¥6.Kªâs¥.>Câ`ت±rAóD?„¦hqJ¢ÛáÙàw”„ðþÔ°20Cbk^¬D(ªäO†eàN¢Öˆ&ª?D~Ûšq²£n0$`SíNò0È<¦ä’LŸUŨ;ñ ¤1Õ^8Ÿ475•{Ý×é[öºn~FV¼`¬ð¾ºªN*lä®™‰òmDû\s¼¹- M Õ¿zŒ]¾‚DË0Kã²ãåîË“%wP"°Äw ¯Èûûʽ¾æ•@£øj·÷]ŠˆnβJÉÉLëáº{kÌÑG‰¦‹ u>Žº?ÊÔ‚E}r{«î}å½±Ä,µá¬Ùá8WÓà°‘CN¡ x>H™‰¦ô´4ÌM2sìLÄ6),©•Ëj· ZXž”&Œ’2ìýÍÃL½˜£€¡7¥ŠÁìC"ÓQÐ(ed ä´2Jk“€“°cBâ‘øl¬¯»J9Ñz–õ µÿ»ê÷&¥YqÉH0‰Õr®¡ÆÓÿvô?BÊG²ÿ#Ù÷d\ H²0K0Gs ´+Ъ4´2K`˜á…Ô §šå{tÚ¢þ†±ÐÚÓÕ¼Òßi”®Úí½FŸopKiY5Ö—DDÉ£9=üÙB¤‰Bë±Øxšy”œQïÌ!šÜ9#ÓeâÈÁ¡#c£Õ½W‹Xd–Ü%§OmÉÌÍ}(ÿèY¹|Ö„±ã!$íÊo…Ü6‰;:À˜ 'ÒãÈK_{A1/$Öš%IEÚ g"¿x·yo‘T$䯴Å’â–QÙ‰Êá<+‹y_°‘aBýM’érˆGµ¶âÃEûq?¥¿zóçñhÝ£ëäf=GŽŸø±Pû¿«~×ù;x=þ<"<Œki¿ô´úžíáÔÆØ@ëB …Ô¨QH %þfÆq%Ú7ë¼&šaÒÎ2YžÂ%47Ê|­•aâ9â’ŒRÊ®ohPÚ+Å4A{GFŒå: ±rÀʼn“;5Vª£ñ{~vh°Àì$€‡áÞ£5‚†ÈË÷(L5®l j•<š%ßÌí´=έ-ë*(< ýü>­sIS¾ð¥ç“ÓÇŽ¾‡ëú»õu«9ׇ10 >BFÿddÉß| ]å—ûöÚz¹Z£'oJ“SÕ-Rp1Slò¥Å)rsé¯ÞkÝÈ7·ë¼Sfôïïz4U}¯zWóJ§QºjwçùÔ[·”a!ŸäI—AŸÕÐçôˆwn˜_àIBϵÔbŸßæ¨Ìx™0$Sràã):2ÌÍèsÅų¤Þ ÿx¥`òÜyeïf ŸFhÐ&Á£²3÷ßàÇšoñÑu RQÛ(p‚wÀµ§í Ã4iJ€&+1Î-Y°Á΀Ä<fBii)J£äÉÿ’¥ŽIjs!Ö=TÝÈ·Q}`×öc(‹­ÒN+B}CÈÏ[ëÓÂz^~ãÍU¨wf(ýßS¿“('Až”Ô(ÃÀ8Ô‚¢¯m¶ISK=|xêaª:hö½§ÍÓMj[\а( 7@H@Iï©Qrâ[à^›ãq‘¢¶HËã7bÕÜx˜Ï;4³Ôèh””úil€¹´Xšóh­ÚC3°º.Ÿe«?îÕ{<&³Z ÄoÀÃìyÌéèsÄPß<çÍ©çQ†fž´yž®¿nO¨{–G\°n¾æ’PÇÔéjp•­0 `WÍ1å]Î'î––ÊÝ›ÖÂ5ë·ë}«ùÝ1𠘞ÌdH©Áè\pÉz$ç^0Ü‚,NîGž¹HÌMÒåÃÓ³Äf"ø¤¬x¦¦Íd¯¤®E&;ݲë\!Bû ÞB©rOóJ¥Qzjwçùë´Z£±n’ 8´Çô³4­kpxò<±¢ñ,¦§Â¬¤T, I 'sRì’“:2¦‘…Êųd]t]Ïýû›í#Å%2y̰Qá‘`@ÒîNwš‡Ý£ˆ+“I0íIIi”\-q‡ AJÚ šÀ ´"œ Z:ç‰ØåyB=C‹DÓ;j””^›V)<ˆ¿GÏØŠzÕ %¦7+¾YÅÞk=tÝ\¬/’ÉÞjÿûÓï ðIܧ‚‘È S¿™½Ù÷l·gC4;2…`š\ ð©é!ƒÄ½gƒŸŽõXçžÀñÎúëMk«<ÚOÙ¼ÆûÈ€i©Å(5*ß°&iÆdïñ‹â»Vi;êã7ð=jƒ"Õ*9W!Ž[¥æ”˜³šô»ð.n¤˜ Ʈ¶£ö­Ü»uc>nEd‰¶M3L±¤Yjëw]OÖ;gÈPJ°oµÿ£Ýï¨s#€µ¾ç{ý=.ý¹·»{XŒÇor/%µxÌýÈ ÑŒÕãˆÚ®¹²–§ë¡÷Œr¤¢ö½çœ~—õùÞÊ”MR®¼¶á}øÜ¦ñon; ÷Ýv¥ |¤…íº?c©þý¡.±Æ,qþ%ÑLqcÎÀÉ«_xöÕùW/»Ð²ôšÏ<ùôÛY³&r/š9Á6{ÒÈò0qP‘qáÇ©œà¡éQ’p0Hí„^Gõ ôÀå^o|Ÿ69Òç*Ôëfæ=a8_F©b0—³¹ö½õk^Ìß±õnÕx¤Xøäoâ—x¶®wøÙkà³ßQŠÙÀð­++9qѲïBÿg†ÚÿÄy¤û˜ä{ô^÷s¸û^½ Æþé¶úª–f”ô5#ý»¯îÙŽpŒ©¸$*X=À /Þ ñ¥qÌ}¸Ç”÷|BŸÇ]×=·oÛ¦=¨çXžK¼Qf~‡u`”ªV§@ž1÷z0®y¥;|FcNéîý¾®E£Ý¾Þ«çte!½Ì}X^xg§J$ߨÜ"/¾»G> †)+=%V«ß/ê«Ì’Ö,qQ&Áœ¸gÓúE»w]rÓ­+\.ç20™@$wvVš-9!!tµLk—ztÞ¿íy=ÐõsÞ¿õù@÷ÍÍîʪ:7¸¢ŽŠI:^xpóöÕonil¬g@nÄaMëžÇVÍR`«Žhf©S¿ã}£qdüŽìÛ{|ÉŠ[¯3xú?#ÜýïÝÏÞ¿ƒi»w_{ÿ¦LóLßÁ€÷òþíÝ’¸„ËÛNýú¥ mÇÖï1äýÛzo Ǿæ“E…1UôÈÐ@IDATŸlhh¨«FY}a. ¤Éæ^ƒ>~ãÜ(!p±Î%Öã`dCô»‚)'ÜÏèºDªÝá®o$ËÓ}4aTžÜ{ë•òüj0L°€ªFÚ›gßÞ&Ÿ¼u© -É: ä²cYb_P_Oñ*mâ)Ť CáÅÁé_Ö¾òü8~wòìËÆ ¾ˆ—6Üà˜Ú#2FÔ(‘Yªl=&Ñ£ý•ø\,_ýŽ 'cKcx Û Ô~¥¾3u ¦<~ÿuQûåS¿ÖÇQÚ÷—¹$J芽טô*#âÝíæÞ×cå·uL@+øjõº96¨˜0rˆ|䆅0ÃÛ¥|ë˪jå¯oo—û aJI¢ŽÁ@81‹ÌÛ§ jAôìo%¦yžšÊ§{¥yÂ^Ï´úœê·@|4£¤}½ˆ- ¦f‰%2M±¬UBõ˜~ט0{ƒÏ7 <ôǹ$xëïHF:ФÊÏbXÇäD#í^2µ‹% L'^¶@^^·K™e^¬¨–çVAÃtË•ˆLK²Ø@¸0«ÌÛGÂYÇ%ÕOþ&3À™ŸÌ’Ò2`Ïvh†i 0Jh®2J?Ä ™%2Gd–´ïñÆëÄk,ƒé÷XîS·hb ëÐdá¯EœKÂ¥>R¢6Wâ>ÙŽþXìn¤Å°Å¦Ä™u³ciOKJo „ÔGPmªi0Ы˜:n˜Ü~íÐß]šK¢€¾Ø{E££ :Æç¦'Éñ²Ém.•ÒÄQ±WaÔˆuãj6b˜%þµ4ˆÉÊšJ ÄfN)ÍN—¼¹yŸªÙ±3ä­-ûåÖ«æÆXMûnub™Y"V¹€sñ¶.ä$¢ÉPDfe”´o™¤þÌ(¡y ˆ½GV"‡8"ƒÄ=7^‹uªØ¦ßÛPa0J¢Ôv=èï®?Í%QBal¼F3t]RyW#;-Aìñ6Ék>³Ìë–sÁܬTUoÖ_·%60kja0Û¸lÊ©©mM{«Šî=\,™'~ÕeSb»â}¤v±Î,i4’ÐçBÎ=5$d´6‰L’f”ªÏ’•ÈÑ„÷šIâõ¾¦ßûb¯™:‡ eá*¨‡rôü ¿7=wô§¹¤ôŸËÔ&©Äç`”˜?Éχ§Å‹³ºT²œ—¤Ê>8¦Ë:å@³4ax¦ª¯®»S•5•1ˆa \=ªTaÚô´ªåÆ=‡Ã4gÒè®uߨZ_a–ˆMÍh†‰2GÜ´6Ik”ô—ú-h‡{½7zÓçú:ØÍôi¿¬Üï}½?Mýýǃ³t‚I¾>+.sðBäVi›çÜ%§ÖýåÇOuºÙ¿üÆzÎà^Ï#Üëó¼Ç@ c@kcÈpIJLLÄ– £r’å|mL®Ï—=×H‹òÅÞ‡8·K&7äK"üª¦ ÏRõe½YÝ–Þ¯¥©Á@ßÁÀ­Wýö®¾Êê|¿÷fïIØ{ËPd)u Ö£uÛâj­ÖZG‡­«ýk·ˆ«jÕjÝÙ{ÏaÏBö¾ãÿ<çæ„›=nîMΛߗoïœ÷¼ß¸ç9ï"9…rðøiÅô·+·KDh°ôèØÞw:á…œúXÒâÓ?Üü'qÀP>h¨´Íó­™( MZ.z¿µ­uÿÌ}omwÖô§: Ðï°õqÇ'bqvPÚdERîò“û­Æö9åËÏ×¾q¶.Žj—–—°Z-0»óS )((P‚˜B‚ƒ¤[dìËÊ‘›%)l”Wô€¼„Ùsd`çÅ#ù%¸#ÿì‡!##úI€Ùë§Œ’ÿ|³JÏÉ/ùf$?öÑ€‘@Ã%)×N>¿ü]¢YÞK\ù˜^kÛ¼²5˜áµÍ;gzm$`$Ð&$ÐïÅíËû¿´sT¿w°¿6‡Ãy³8l/áXyÿû¿´ãG)þ¼ü€ÙhÓ  %Ð_‰š¥ÒÒR±ÛíÊ‚áùˆÀ"I/pˆ‘èb çؤ© ‹­Áb·4ÍÐÀÏi“ Gò| Ût¢tCj℆+P"¢¢"$2 €ù$¿äÛ˜àUýø>‘!IûIFvždåHI™– ±êfÂRš?F‡‡"|{˜ êÕYºvˆ«š‰L:[f¨Í9€¾MõñÆdÉAXl*ý­ˆ–ˆÜaÐ?6Mç}¨fœv'î“]¾ZºY…Ÿ:zL5@…Þ¯ª+C‘ƒ‰²Ó9˜V"S§ö1Ò«K|UÅͱ*$Ð2o@Œ˜CFFFFÕK`ø?÷¶/,)|1åÑ!w¸—ð⎻V«=å—ƒÞq?n¶Û¶4( rŠ3Ì…”8Ф?MÝ‚L¡°°HŠ‹‹%Ò8 ¾9 šK%8TJÚ„Žíȇ·p³M‚"åCÍ5JMgµJ&°ƒ»Ì“ö•÷殄©rK•öqÛ°Þ ™¹kÒm‡#ÏE?É+*_,.–%ë“äÇW\¤´Mú¾6iƒUTÆgÏ"µHÌ[-R!@v ÄE‡#jb 4”m$U'A ‹J$¿°D>_¼€r—ÜzÅXuŸ*—å>s0ÊΕ”'Ô鯖o–»®ž ´‰U•7Ç*JÀ€¥Šò0{FFF^'‘¯Í+̉Å!¦ÿËÉåö1–"g8ô—câþM¯cÚ0Ôâ PÑZt]ûJ)ÿ "€¥"€%ÎÞÛl6h –0wÝ ¤;­Û£¦ˆ¦uäCGçs“ÐеÐüŽçÉŸ!—¾_—„Ï mK—Äø9ðç:äш……mSKQQ7ÉÌš.³>.R¾/SFlö{ÄçÏá"€´¯–m°÷“„¸H˜hx¤ÿ¾ÒcXHZ´!3§@^þh‘\7å|™:fP•ݘ>n¸œÎÌ“ÓY¹xïKUHñ;®¼HM Ty9X.–ÊEa6ŒŒŒ¼Sy¹™—[Äþ;Ätèc),Ý®¹tZ,°Kù.~ÄÀ…»ôA³6(“€,"j,n,…†„( T\\ß—€%h–Êüš4XjfÒ“þºm+4YþþÌû€†v ˜¨]b0‚$çy%^ÓÖ‰²'Pøôûš$qqŸC“Ò2ÌÐ^“ŒŒk1°F¦j§ChòÕ÷Jk“ø²ÿ_/ß&¡xFb¡M2)·j~+$ãã¢äLVž› (ì„ å sÞ§Ïë§Ž’·¿^¡ü½Ò2²å»5;åŠqCknÀœ–ÌC`$`$`$àåØýè O/ûçÞ9Š‹žHùÕ§+³›Rù€Ù7(“ˆÅÍA®+NàBƒ¸´Jv5£¥YÒ€©!‚d›jO 5Kl“Ú%:§¬ (m“+òÑ(¹¤L™ÓôŽID ”Ú·û¨!âoÒkÔ§o”/—ˆ$ÄDÊà>]š0i ÄçqG꙳b»Jíb›´/­¹2JÊët¦àùÙ$‰O ÔQy"62L®š0\>ù~½Ƕ=‡¥S|´ ëÛ­5‹§Ñ}3`©Ñ"4   4¿æý¼½¹ŸfKƒf%…· µ-½³Gó9/°!C­FZ@?"\à·¤@’2{"P²$A{€}RcÁëpµéò“"`òXcÛj JóIJ†D×÷ᣮ4JÞ$j¸JÓÚˇó×HïÎ Ð2‡_“°ÈgŽ@)¹€>Y¸@ÚOi”š¤ò6V 5qéн?wµüáÞ*mneôéš ã‡õ-ø0õNIŒ’ÄvÑ•‹šý2 °d###‘@ÿ¿ì K˜§l…Ž)'‹ò®Ûß lø[„>ŸôÈ Té†a³…$ µL)4}£–‡´Ÿ’IzÝVõl¶Ò.•i™Ø¦ø 7¤îÖz ï£Þe!â}”ZÊô®:ù’ŸØè9r2ýnY¼q—L»p°ÒZð¾6†ØošÞÃt)#Þ)%cz×0©Rn1‘¡’–‘£ž§ic«I‰Êµ1àÑ´3ráØí¸Ÿ-Þ$w_=^Ò¨\ÖìcâÇÁHÀHÀHÀHÀG$àçaÖô'9ý¹œc§%Óf·/ß7FµH€`Ř\¦q*àý‰à#¢ý‰¸nèâ^®›&xÔ*±] ¦ja³Íœ&0ePƒÅ“ óTs¨¯€éÃDþ–oIQ~nä¹1 Z÷›>sŒÌ¸fç5X7Áê{g*–§ü=áÖkºGW]<¾‹Aêâlä­š³b[ÅŠÌ^¹ X*…Ù000ð^ ôÿ[rw§ÅY°ë‘A+`,e/çÔâ,ÂØ×„Š*ˆÙ¨¯4xri\>F.S9˜ÎÑ|®‹®Ë€£Úïµ+’Üü õî+ùË+(•à—fœä½¡äÒ*Ù”Viÿ‘4ÉG(ì0˜÷j¼(ÇÜü"õ\UwÂCƒá¿4¬¼±½‡OÊvøŒ:W,+sÄHÀHÀHÀë$ÐÃÏÿ4Jü´«Ü÷¼¿ïH£?Â`Öå­ëu\†ŒŒj’€Ö®$í? ›š›Ý5oñsä|&8.%¥¥5j.jbV÷›uËîC'Q¯¨ü»÷›u0^mƒùæè+²¾=g…¬O: #úw“~Ýkm¦gçx€Šµ–ó–îÏWm÷èü=¤{‡vŠõØxÏ]a¬»õ}4š%- ³600ðb `¦’SƒË¹L|û@ð‰Œ‚+Åé˜ùáK;ÿ1è¯Iw Ï’ òàÅ÷ϰf$P•´9ZmÙª®mÉcz^›‰Wu<ê~ózæYj¬®h³gô½§²ÔÂD­ƒ{wQ+˜œõ’1ƒ$!6RÎääËö=Gdcòé+—!oQ(‚'<ÍÔ¾£§ä»ÕÛ¥]t„0ŸQ‡¸(aRÊ5OQÐ^ÝyÕx‰ ‘äƒÇ‘K a×DÕÕTòJDÇ{ýó¥ŠGæ`b¿ÏÐÃSìzm;fFÒkoaÌHÀHÀH j gú[¬N§?æ¥ýai¬¢-0Ò¨š5sÔHÀH Žà@Öµà{…Á9´J̳DÖ]ý¨c·ËËóZÖá`òÀNý}Kü¬ríÄ7ä?ó®B¢ÙÔߊúô¥>eÌP/ô÷ó“ðà`Ƀ_R´@ž×»BMg²re$|"•‰Ú±ôL$çµ+`±€" š¦äJ?“[á:oØ©¯ÜÇíÒq9ƒÄ9ð×Z[f¦è }ñ4Þ–ôà—A'¬·ý≑AA—.uƒÙI«XÂ=-œÖОÓ"¹pþ>³àCõ9ÿßÿ÷ôFôËÁ“y>ZÃiúP£–¹ýÉ“\â¶Ûì›ú[ïÄa8\Ïû¥g6¡Q_ü^4»¼|¹š¸ÐW„!›Ã7!ùEˆü•_ r³¨»ÞÐNòk ôÇà1Tb£Âd LxèßÁ˜<¹äÃÿú^œ=R·­3y$-c§¤g&Ë€¦K¿®—ÉŽ}ŸÂ÷&X&üÚç|sò¡9²dÓs0§*‘>].‘!½n’­{ß—©£þ û/“ø˜þðÍ —›§~(vg©Ìþ¼îÊkÍ;×õ%÷kpy}›«syæ$Z³#U.ÜK™âmN>¤¢åé öNúôÉ0— °µýæž'ÿðØ›Ïÿþï¨_Lo™20ÏG½ï°¹ÀH i$PÅ·"þ˜ÓùåýæÉwþïY~+˜ŒÄ›¾MÓñVZ‹Ö&1ñgII L²RdÞê0½J“Äøo$8øP³÷œ ,,l›ZŠŠºÉ™¬éòúÅ2}üP™2zøÃ7D›ç5;3^Ú€ÓáÒFÔ•= Ì¡\%ßox`i»ºŒþG!AÑr ¦yšÒ3wÊ0ø5ñœ¦}Ç]ÀJ‘@MˆFhôуz(m˵oÕ±¦ò­ñ\KLçèpü£‡~õ0€Ò³Èlyìö+ý”Z£œ½ªO”ñ¯ï¸Ò2Ç÷¥»Ÿxúa0ÈgÁ@ªy>¼êi1Ì´e ¸+‚þtǯžâ·‚ßnoù^´åÛSkß5P*-µIœÎ¿_Ÿ$߮ډ_I’ðºG€Re& Î^S<|½|›â‰ü͵SùŠÖ·O“¹ÃikeCÊ¿åÛÕÊç_'i™Iõêèòm/ÊŠ­)J¼˜~I¹ù'™­Wy]1=•‰Ïi²ÙšÆÌR×gÖ­_ ë‹w6Hu4(00잇ö¸éCKJ¹%4Kü‘U@éš;g^ù{ Ú÷\{±7 Ô[ò^x´íÀ?Q2ÿ\œ“üùöGŸLþÏKÏk3òR©1ó|TˆÙ5hI TúV7ÜÞ{taµµg‚§Ó!]ÆH~Ñi÷ÈQûÕ^`N´I 0AïÅ#úɼU.-f—î=î;xðg{vìpó´m½¢áÀÔ“ÄöЂºvíß±óß:'Ä8o›>Î%OÞ·¶(ûNñ1ÎààÐ7.»ìGa8åégÂÕ¶y>Ü%b¶¼DüVtl-Q1±¯ 26lñ]mÉï…—HÆ;Ù P¢é]1Lï²sò±k«2½‹‹kx>ž¦î)y H—O¿ß ùÕL~É·¯’ÖÑ_èë?“׿ºXþú¿òÁ‚²xã3ÊOH¥æêã ¿C‚Ôí2cò»rË¥)?¤Å›ž©¶¹Ââ,Ù¼û]ï¡¶¨ÈxÕö¢ã¡éˆÈ®/a¼#WO^ßËš¬|¿î‰2 û¹Vl·]y‘„‡7Y;ÍQÑ0DDEV?¿?¼ñæÛ›£o¬Ó“š%Š˜SF4œ ¹àòËgÂA&>Jœ¹4Ô2 ìoš6Æï/ï}×!~PŸÉ<ù?pBß%.ž$ó|xRÚ¦-#zJ€ßŠ›pߊÄÁ“Æ?°}ûê—P…ö]òô÷¢žÜ·­â4¿³ÛÊG©°°HVnÙ+y…6å£D­Ž·y‰ž#'ÓïFйÉ@µïË{7)S:FHC€wQm|[-þÒ.¦¯$D–¨ÐþØàꌜÉÛYÛ¥*˜ÃŸÞëZm9&ªýbùLå£D¿&FÂÓÄ :°ƒ>Æ5£á! <ëÙòîe¼mûÐÉ É/ò&m„ £Þy"˜C}eOžÈÛê4óéƒ` )nñ*s¼kѾcñA!Î ƒ‚œ#Ã#_ëY"9ÐÕÞ[ Œ:Ä‘„ØÁÒ!nˆÄÇP`†×fsr9€^U{eõ(áî£T—ËJm¾5ÞeÀªu;÷ÃdP©§„ ˆUxh¬Ç±Ã'OËøáýÔ ¾k‡v0ÛVAA˜ø¹ì¢¡™#Ë:Ÿ¡¿/Ü[üq|ò­ß¹O6§*¿æ†©£ ­Ë–¾+BRÚkw"?U$LG;Kd8’Ð"ÚðÂu»ðüö—îµ}àøiD|L‘ë&Ÿ/K¨öØ©LÞ¯î{¤ää*°´:+UFè.pC‘ýÇNazúì|Óøá}¥W§•öûõ;±.’;¯p~FúuK”šÂž—3ÝL̽TX\R„ ,ÁH5?öò+ïXú[35ç5Õz ,¹k ‚ÇÿàªQˆ%ÐŽáÁ y‡p/,)žpÃO~6ú“7þµ\écO0hžOHÙ´a$Ð(ûVÄ_vãm£æ}ô.³(yò{ѽh½U”k•"¼ƒ¸ˆÛUxpoíuxè69•Ñ[‚×>0Q¢ß’Ö0yšç¾™W3~<çc"}$F±X û`,ë¦êªÚM£&`äé~´öö‚ƒ8­ÀNéÕ9^^FÞ¤ @¹åÈÆ]˜ÎȾ#i²=õÈ9¢ˆ “÷ç­‘˜ˆ0¹fâ–”0i,Î[+™¹ù®a[<ÿöœBàÔ·ki©ä´|·FlÙÑðÕÏ–@Czž EXüýHZ;¨W'–÷é"«¿‰f€JCƒ“Î ¯}¾T‚d¢Í‘:¶‘.‰qòœùF2îŠ-{¤C»hÙ‹„¸¯~º¤o-±s&+;-¾]l7¶ôðÓO?ý2–V=aæi°ÄÀ!í;užˆ‡ÅÉšRª¦.#f€þVD·k7ÕoÀB[O~/š¡W­§JX²#°C©ÒZìaÂYæQòV"oä13üÝ;µ‡i~€ 'ÎAesQ~aÎhâFj`”WYŒÜ&û«`Ú‚~R•ƨŠÂÕb¹`ðìpž ï]í^pÂé B‚[«â[ó__¶ôu®5œa[â°5lÈq*39¼Š á)‘0Dlcî4@e4MÙy²v{*ÖœH\"  "íÁóG D_(æ#½ùå2µFªÌŽB»CਃiÙ (±ŸÛÍ) –á”C'N+ó-‚!Ü9}œ¬O: Qa!JƒD°DJŒBàÔS¨òNê “¼_>Võ#íL¶:Îë“ö×˧yK%Ï•–uyEÜÈÉ-ÈŽ‹ŽÜëçïßUuôk—p'Ö³Y­W_î)°D+í¯Ô)"$ȉ|Ÿh¯Uëf޹U"BƒÅL‚áºW¼gœ-ö™çÃR6m 4Ô·ßïüÿ®¨Ž£;:9xò{ѽh½UÐæ]%%Ð,!\x6Ì~ü¬Œz¯/ y#¨’ïà W ‡¦LVÛ{@@ØÔ«}ã;8ûË€³uF¨”ÂlwAžì9yÌrÄ?äæ«úv×ã®+®lÔƒä>¨ ‡=ªQõyêb»=ÚŠ¾æî}©ªÊ|åØ9çRâÍ©L6¼Ô‘¶í=¢nw‡Y^ld¨§4¡Ë'h*vùñi_œŒ¬<™õñ÷,^N qQ²çÐIIˆ‹†Æ(Mwo7+· *Fi©¦®„ÒÎäÈD‘Û±ï¨RºBšóE@$3 d(î?éÀXŸn òþ·TÚW$[ý¬X¢âžåu½è~i¶)G;BÂó¹júä­7¿{ä™çëmZ÷ÕI)×¢›£-o¨³iŸöª{ÄB|5 ö³ú…ðX«%¾A°Cõ%â=A4Æâä½Ò÷­šŸ”&ë™n§M<ü¦3(MB¥6›øÃž¿©fˆÂCs\“–ÁÜNIlײ3«|oùÃÕ?xUÑ¢õ»¤x{w!Nh»äúVXù­à7ݓߋ¶+ô:ôœïj–løV” lxi©ƒO—‰Oªh±"ä±´ÄÅ7ù×}©í[猪‹Jç AüöV&üøTFx”7…uî¶ýç—§"k«¾Àç»4×,5 ±ÝcdÍ®Ã0ì'aa4K£ ¬”ü9&cÂmÞÅ~ë%6,@ög*Ó¸ºü>¾7wuy~³RmS«úü[sÔ6ý{öEà„Jt&rô"±m>g¤å›k6SÝœrPx­.ÏmÝ.¯§à[_-W¾väCӮDž‹&÷v¸M°¦ëÔe"€„öÙã»@ÒýÒej[ÓÄ]ãsÅ~65eeeÙò³³ß Žþ ëvZœ fÀR#­Í¬ÊÄxÓ¼ˆžšõ™d@%J‚vEº$ĪY‡ÎX“ŸÈPê“wO—®°'Õô»ÙŸ©Dz×ÃáÄìGó×Ë.„ª<““/±Qáˆ-WÃð‹Å›dþ×LÍX8àšqéheûªëóеkæ÷ÉÓš%¶é•ÏGSÜ—¬œùpþZ<ÇÕs•ûØ!½1ã4jxùÃk_b/?‚²¦t¨ï7ûsy`ÆTØP‡!ÉFeàçÇÖvr÷5$*"TWk>Ë£vW3YNT±ÃÙÛä'd0œZ=M)O(s:Î’8óGçØ™×Mn2Vô{Ë ]+"4Döì(·^q‘Ú¯ª¡$ÜŸÃFýïÞ¢îKå2 ×íTQ–Ú:XrÉE}Çõ{ëÉïEåÛböÝ$ Íð\€ ­ªP‚[yoÚä ‘|sqpjëŒtÙÊk#šÒ9–øä®·Xü6VŒpYjåK›u_ƒ…xøÉ„Z1èêÕ`‰üQ©‚Õ³`§¾BÒýf‘!¢€¯ Ã×,Õ¥½ªž!÷ë*ƒ÷sUm×¥|mmV®·º:ë[Oåz)Ç`Ü(>WZÖ•Ë4vѼ¹ï_sËB"ðêŽ{nÖëÞzà§[[¯7^ÏYÁæ&#½¨VÕ«ÀpÑÐ>2yô@9“¯ò=<÷ï9òë;.—îÛ×I>|àßübœý2˜‹ÑLb‘­;«Âµb?»ñe.r€øÓk'U(ÓÒ;臾Wz­§×š‹5Ý×^û|4¦ó|6þñᘖ8äþ“aó%©Pã3êNl¥oE2:†e¨Ñ[.£S§«5†.etð9cURj“'îº?&Åj†«2Pª/IûŽË—K7y,¤½úébyüŽ+%1È¥Iºóêñg'rëÛ‘ZÊßóË¥~0ö=-Ÿ,\'ý{tT“U]ÆÜçý²j TUù¶|¬ì[ÁwÖýnîïE[y­}ç·Æµ D!¾7¼k¾tC˜ç‡|£R\š'iY[$-sGƒóuˆ*I©VÙXö~ööG7>^z†²CœlÌÎxU¾zËo¾|vúwn'[ö;Ű›W†y'_……½¥O|L;ýÏä]÷£rÿªÛ×åu¿u]ñ¡~r,·!ï½Ë2çëe[TðˆêúãmÇ)¿"˜ßïßà{T—>m\µ*÷Êëg¼ã@­ß^®ï®Ëµ¾VÆ`‰2qÿ1õÊȈ°`Ò‘‘J>òÙ7¿’Ïm’_Þúƒ:ÝÓ}¹mÏÁdX?ú=cæš%wb2/ÖÏ¥ÞOn`¼z5 v/çÛú~yŠÝ×^ù|4F[v† r–„ˆ:]ÔÇŽ¦aÔDéH:}‘k¡6âó¸ ‰™ bÁšpš”‰ç÷WΦ|^?@èTš<õò§*¼é-—]€Ÿ­K¤e({î›p¡ôD”Òêm{‘ÜrЦ:<$Xý@^>nr[„àÜh\úËG Ö©ö~8y¤¼;g•$CƒD­£]‹œÜþ×ÿ¾W ñoÌG‚;«<÷àõÐÈ®SöÙ?¾Â¥YcþŒoWn…Œ ¥GÇvи](ía#Îào =»~êh™·z›dBƒ;²w¹:˪ˆ¹-¨æÄÇ¢õIr}§Æ—ò©Ìwßnäcôá÷]«ä¾½Ì[ 9« ˜Ÿ)ØÁë×Ë6Ë–”ÃR€÷8”íã×ÿ1ÜçÐ@Ô³^¶à"…L5P…“­Š??æþîúxWZûå€ CÎNsßW¨ (CozNŠì©RPzX\¯TìŸ ×=Tã#ÊØ`|»\yŒxarêwRX°¿@É{²ñºõˆZ†Jç2°[{I>rJÎdN—ÄÄ×`>é=,;œН@?§ôŒTß5ÍwCq¸÷›ßHæ×ê éùv5i€ ø¹ð b_!ºúA´¿`¨éø<5æÕ¥ÏiÇÒÞìԣ˃øÎ ž¥ÜòüþóØ“·ßžQ—k}©Œ'¦>ó£ÊÙŽá˜a>t¢¢½ëÎ}ÇTèF†oäRTr6ðcú“¨¨ ‚igW‚0øòBj‰O”Ï<õ½_Ç‘Ž?éëBÃÁÌñÓ™Ò¡}´ò×Ùm‰ €¹F"ai@Ó|&ã¹g]¨cš„®Ý±_ š~ÌP›¦Ìþ¢‘o⎫ÆÉ¤QT•ïÍ]…ä~òÐ-Ó¤ò=|ˆ$‚ûÿ\]záyòÌýשÙjN \4¬/@C)ÌùŽ+P1r@7ä‹è©üªº¢½™×M”;:uB§Òæ›y1t>‰Âé•m“˜t/·ÀõƒÄ}oC“Ö«sLæÆª=,íðÍð¯ÿ· €§—2—¥Í7OuDÀü¼aÆmôà^ªhU|óëç 7é}ØÅó%< SÝ“«‰unJ>$A};´ƒ¼î.hȨñ[¾y¯ìH=†¾OP`øóEáô[un]Ÿ®õ{ë£ì·N¶Ë“%Þ‰¼‚tÙwl1œÝWycŒb!‡ÍÍ2mÌórûesä—·¤È]W|'—]øgÞ÷Ç0mŠïLÕ'UVÚ‚µ†…Z‚&VÞ½¾©ñ’‘Ayï!òC¾ú´ Ö+@Ïä]÷£®Üêòºß¬+>Þ0ëæÄoS™kD]ë4å\8ƒH~# 﫞'>W ¹Gu•çì—žßÒRUÞé vææßU×k}©œ§4K•d¢L½*ó®]„ÑÆ‡Ùµ1c¬iþêj°§÷ –US:QQá¡ÊOI«¼f•·¾\ªSÂh,WŽV­ïDåkÛÖ¾÷?õ¹ÌØMMLeÒÇHÌä½9 n@Dj£´ ÏñX(‚B|¹d“Ê~Ó´ ”Ö‰çj£«'—fÔ­AýÔXÅ(‡Æ«²’“hÞG rïõ“Uî‰  Éúûç+¾c§²”yÌ@d7D˜ÓNwî“|W~xÅH”4?SÇ R` ƒ±$L6Œ…¹+}I áZU@‡u;ö©Lì·€h¿þTк ë×M¦Àl–´ ¯d€Â Ê@:èöoþêíjÚ!j”Ü£UæÛаrÿ?š¦> èÑ¡BVw†‘íŒ~ñ½У“jƒæ¤ˆ`D^B\„ZØOšUN»p°:oþ  Ô,0B£¸!UjŒj¾ºÉÏröÄ}i²¨mgЂ… h§»´’ÓÐ ìM$rúF‰‹û¼E5LÔ((å ’ÎVI€ÿlÀ y%Ïä}¨/Uî·«Î ‰ -”ü’b9…ŸÅÓˆ*á5¦úöÑ“å•F ¿µÌ×D3I>G½GÕò¸8W¾@ý&æþ'±¼Ã"·cõ"·[y,Õÿ-ò gÃDŠNøî¦=4É«àA³3>TL;wí·ÊÔ1ç©T4²G{9yâÞy¢35 4w«LϤ(äz €V†¦xG ¥t7Áã9ÎÄ\ÓäìñúçKä÷?½F™Êñ|MD)<̯¸k”ÝHÈÇéÿÌYY^ƒ‘MÏR@‹ï§ßo>aèÔŸÝtIy9nÐ|U“í}Ÿ,šF£ÿ™pQW§]‚”Nñ.@Åúº$¸øÏ€?!’¢+ê ¥‰Å0‰«Ž~~ó¥ê½eЕ¿¼;OiË~ríÄòâî|—Ä#©k¸£,âðíi€#MôhêÈœ|Ÿ JíøåØsø$úžÖA]~9åÛ­t£Õ¿ËÞ~ßøþ6”hÂvÇåsÕåv{±dæ–µ;gÉÎýŸ7¨Jjwºw‡÷`q¯Š–‹?$}º]†KXh„š¯sÍSð¡Úì¶Â¢ÒRoô3¯ÍÑ<‚ƒ‚$k};F«¤Â3AK€¼@Ñß´ˆ}”2`XZÚ^CÒ%&Ƀä•i0Kó^•Þ&×Ó´hÝ.€¾ yöþëñÁ—RêJñ±‡Ê‹ë 摘ÐT×>éò\ÇB“FíAŒ;¹óí~¼CYóÓ0kè í¿ ÔjÑ¿«Ì[¹ ÷õ8d‘s¼! ÔúØr¶uâÈþBß­VK®@=úåÚHÀݯ!Øé«åHaI&ü ]¦n{ŽÌ‡•E~½{Ö³ÓÅrñ°_× ,E„v~݆J\L ´ ^i¢®äPZTœ™•ß$`‰a0÷%X •b¤—è)Ö,9Ù^N¦ßÉÛTLxmC™ÝøÝ8wò­Þ7¨š ˜G‰áÁóò‡"Œwoñ·Ø¥K¨%äVBÕpðGÉ+yÖ&^ÕTWíáêú] ‹R[©´·çKò]Ÿ¶©‰&ú܆Á”“ØHqRm½måó(1<8£Þ1˜CœjúÆùKWø“5Õ=ªJ–Y9ùÎÒ’—ßIY§ï¼³äÙ—_}z¥_ðÝîü1V,•ɧU­h EfU^´.I¨YúÙSëÜÇaýºÀÜ&Ní+å&DãyNVt°­ì«RçJMÁV!š¼Ñ íÝoVÉ-˜Ø.R™ÂÍ[µ]ÆÁç‡f\štT<‚íÿF£Ev©¨uÌÆ½vû>UœÀª1Äë—#59l¯g§x˜¿Ë·«¶É ‡DjHÎëÕIiœ–lLïÓùÒ€# çj"Ö‰~qÆ‘~SÔ0 éã |B€ÇJÖMÄ?w¢©ß÷[‹Ö'ãšN²pm’2Ë# !úqÉ÷›™ÜY]ˆà•_¾Ç÷à€ú]¹û‹Ñ¬0ƒšIÞÀÀaÐ…Ÿc/¹~Bb‡HnáIY¹õ¯’|hŽêÊ´1/ üN|˺ʰ>· ²çŸeÜÐ_àÝŽ•û®] -íR™¿îIoîv]x#8RKaAþéÜÂb sæQãÜr+¾ÁÐ,¡®ð°0•L˜þ“¡)óÏ•£YÅr¦¸LÓzãÛɤ­âçŸõY7ÆðÀkÎ@±Û¢°Å÷ )Ö¿DÚ‡XŽ‚_ᦶÉ+y¦µ ûP_ª©ß6ôÛ:»PBŠ‹$³Ô*¹%NÉ(`SV ƒb ä8ÖmH4 0‚ûäŠé‡ç¡}C:GITd„4å=ª,[>ï|îK ñ8k†§Šùù9ßÅmS` Ï-üñc3fÌ@ÄÉÖAu›rn}­±ŒôµvG*~"àK§rØÔg`ßNñÿýv¼þÙR¥Ià@ëc‡°T£äÛÆÉ‡ ‰ùp>žÏ+_8úQópü‰ÜiÄ€î2Ú úßðGˆÄ‡¤Ô£BŸ9ªÙ]ÑáÆV0 u¯£®Ûq+·î•'þõ ¢áu†YÝTÉþ˜¯‰D`3èÞk•VEˆà³Î` Ô>ýúŽ+ªlîbDÝ{çë•òЋï#§X;™:zS¹ª,ÍñØwF‹£Iá_¾¹B37\2-¶«Pߌ*yÏ'* UCÌŒè'H"`Œ@ÚשB£Õì0Gs§mص_FÀRûw±8eSTlS~]ä‹À>IW]< r”0XÆßákÅs„ô«Ï7¥–Ìa#H€fy$æ6²Zýå†ÉÿAHç™·æ1L˜\,WO˜%™ßDГ˜(ˆ“ñC•¼ÂtYµýïräô9œ¾^:Æ •oV=ßß ÑΈ)J¨×qdïî­zôº‘A\èÙXâ·ƒŽøôÛ¡Ö¦@É=× â §¨TòàYâ°A°2EÂ'¦Q¤jã´•?Xx¾YefG \ÆF‘ŒG0GòJžb‚§™­­ß<ïçW$þ03‹µ#a-¬­ ‘#Ø†Ñ¸ÍÆ„²Uƒ4êù[IâïU$4b¾N¼G$Úo b#Ê׈`?hù¨ñkž{¤-ûÇç¿g–£ûS7àû;!4»{vÖ+©x{ãLâÞŒ jæ»_ïËÛ,áî=÷Àu5ÞC _}êŽsÊüñ¾Š×Ñ̇NŽÂ}Ɖæ8­Ú$çé˜îˆ„_’N¸ÊÈrKUô•Ÿ5Új?PÂD†«bFÀjÈýYfT:.šhæ^w~@¿ó éNkw6[6Ñ>{ï@´ÐüŽí3hÃï_ýB%Ù­ÜÛ£6õY¼WÔŠéijš®oœ6¦ì}À24.÷^?Éý4 AÍef ycþð¹÷ÇîŸ1…«s¨º÷V¬ŠïÊǨù;¤úáu—;5L/þgrdEàžêgž,à`(xjèî»aŠ™”eúÇN·oÖFÞ(ñC~‰w6J:¶¦€P*|ŽºÄ’hh>\x£:¹Fi”úv™&ƒ{ÍPeØÀ€0yÿKFLs™ìåœÒ¨¾r$}7v³><¹ 9·ïذ&uô”iÙ’öG,U=b¯G ü®1*Í«”ø½'¹Ìôü-®H"ñ`Ã÷‡š—¦ OØ–0°6¢Y`ˆÒV(EGE©m(òÚP­’«g®ïy]ú]íR)úˆ~G`¢®º~SÛ²}ÏQ9“{Öl”æâCªÿÍÔ¼øÂúì=‚©7€ª'î‘»\ð¼;GÖÆå‹’qœï‚û»Áú1\¥B+`3Þ@IDATÆ­¸ ç Xr Ù®(>Ðî@©âY³×Ö%PPªM.l»Øk+_×óUñ£A’{ ÁÜPô¿bþ >ç‰0=­‰ªJº|]üŽÜ’¾ÎÓkú•U–;5k<Î{™øa&xÜŽðåôOã}ÒÄ“!#_‘€¦M‡N®’ï7< äŠ$’¨ØOƒiž¦´Ì]0Ç=kÒz$}C9PÒeZÑÚ¾( „5£Éاîdª3öœØ]ãÜÐ>kPs8'Âg“ø-¡OÍÝ‚P¡Ú%åÓà`GN]`r•å$mCˆßq¿gäÁù 9g{.­|”`zG’2ñ*×*5,°Ce›ªßô)eêç;SZŽ:ðvùè÷ï†"«åãR‡ ,4]gîe–òóºœ/®[ÜöEÉž|H× éë÷Èñôò,u@ÂÀ_þømÖ¬ŒÚ¾»¯™ üÇè÷Œç¾øQf*CF¾,åÛ^T>Kî}ÈÌ=¤vã"zʱâÍeÛ=dßñ%åÅl¶æ :PÞHËl…páàƒD: •¬úöë]{õ¾ôÃïÖF>~çt+5ä%È®}WHñ ø,1B™v‰¡:-K}xÑmqrç¬ÆÂ™&w4ñ E€*.4¿#ä­©¨±ýæoÒZ¤œ(°9$$ÔåOË@[ýºuh*[¼ž–¼GL±çÜ0xÙ+æ|ù%„Áw@&ý~Èã÷Þ»íÙ—_Ùƒ}±´nöìÑ(·¶Å…× °ÔB4U ´v ÐÄtÆ¥cZ{7ëÕ?úVÕ5XD½*6…¼LÔ"åÃiÌ {eÉæçýq4¦‰’ztQµœžÉN•}oEζ˜`¿IqVµe}àgÇ5P*ÆvqAAnÞæUKßµ^<õAvrÞsíÅn:å†õHˆ FÔ6“KËÓ픊aÖ[RRS¹ò(1ô4µK æ@Äã¶¡îO Ü#j””6&°Ú“üáú% é€ÈgŸïß¾ÀnÅ|u%§óJœ ‹Ï“Ñ,ùü-40ð¬)oÆ%£dhß®õj8ùÀqY¿s¿ä«W·La#V!FµûÓ{Õ¿ÓégvÉ¿ç\ ÿ™ð2ß$Z߸èóe?Õ›åëüÂÓòŸyW!H@´Š¢W~Â77ØY ‹N A€ÎˆA‹¿øä;àÚ¤]u"#ÓyÓ´ ¬MåÃÄA2}‰xß’IìàJ® Ô,‘–x½K[äò‘"`òPc»j ª©µIl³*ÒíÔÖï˜$ÎGz‰‚R‡„ÂLt1¢¬éÓ¥ªj}úŸ’'ï}”hzGÒ”¤¿ÿ유V‰Ï>ß¾g?؉êÖmUöCx Cá‡üéµ×º>>sæaœòi2`ɧoŸaÞHÀó cq}Í=Èeò:Õ`1¿R€¿U&ŒèçùΙŒj”C‰×‡Š|ÛüNwUƒ%­Y¢ÒTEÛ^üå'ße¤Ýeòùäà‰ÓHB»þ’rð¸ÜuÕ5HÐu˜µ‘€‘€‘@ I€*ú-Ñ_ƒ³ëÈr£ÒÞ¨HÛ֬ؼgëæ½N»|’Ýn›€Áf$¶Îˆ gtT˜‰«›Ìɧ20ª¼¾êEz0®/ª¼¯·ôZC(2Jð»Á ;j¾HЄ­Þ¶_--Ícsµ_ùžTÞoL»E¥¥Î¬ì|'ÎBÆ<¿¹‡ö¤,^óÝÜyt6Ì.[r°æ³Ïw€ï‚K­‰ wB¨ù¸= ,!ÖE8gÀ’»€Zb›6¶sW ‰çy½„¹l   4¿Önß'WŒ*›“ʬOÉ“wMW᳿Zºá³cd5ÒÎÝ^;ˆÄíO`þ< é¡}¨îWÀ‹‰oC‚ŒÐMÙл×a¶Œj–Qg—šËzãÙ³¼7¶hªnºk—þN3©€Taa~)´Lsq|AßÁûwëÛ¿HxxlPHH”¿ŸÕÉõšŠ»6PO@` _·®;"íD•-xVœ'NœJÏÄH¿ t¿Ùºh³ÛŠŠ s óó2ïÙ½k÷öÍûѵG”+5JKMܦ žöWª UÂqE0Þ\Ï:׎Ó9®ì°O¯*Žf|°+ƒ‘Š3Ô_ äíÓϽ' )ùîœU¤P*Ë¡°g½vòù*Qæ6äEY¶)EE´âl6ÕÜ ‘̤¡ß­ÞÙí ¹«Ñe‰=9 þñ‚õ*Ç ËN5°\£µcïQY€:ަŸ‘. ±rÙEC„¡+ÿŽë¦ŒB}Ûåˆ#ûw—›p>‘!/\/GÒÎHBl¤ŒÚ[&bðH¢cݧ߯—­{«¾Q{ÈõSG©™øêxðÁÛgXöQ ðY9 »tïØNžzùS•oˆ]á;Â$± ­}&;š¢ ÕC†Õ®LS×O­Þ˭ȱ÷ÐIUg®m¡ÛV¾Æì  T/ÊkDMså©|ΛöÎ h”’¦œ­ªúS~²e6(BÓœY×ÂvQ<ÎYÛ =;¶ì²Ûì”‡ ÕG=úô‹ùñÌ™(•]g_¹háÛó¿øbk}ê1e+H@ƒ>×\´_Ÿc‚"mvGR­Z%”‘_?ð“äggÍæ5‘h ï‹³gÇÿê¾ûÒyÎWÉçÁÕ²7L=_hzG‡ÚW¸þPÏvÅlõ„‘ý0³ËëŸ-U¼±˜Í.A’Í]ûam8eÊâ »ä­/—K×Ä8Ô9qûS1û½¡,-ß¼WÙsÞ1}‚dçÈ{߬’á0)Ћо[#çcðxLS¤«cd$Æ0ƒ>}ü0•ÐóÛUÛd`¯N2¢7‰DfìáO7M#Ûöqµ…y&´ýjé&YŸ´y[KûèðòYúêxhSsrÐ B1;F”€N6äúÝ¢­3) Ÿ@‰D“¼µÛSÕ6'*Sû˜”xœ¶øö2GåÊå̾‘€‘@Ã%Œ‰½œBW¤°†×ÒüWÚí‘ÈWÖø\EÍÏ©TÒ¹ÄÁ&™ÜçG³ñKt¤á‡c,vŠ`É%¡!4`Èøî¸ë!h–ÚñzŒûlë–-ù€’+éWC*5×h èg˜@‰ê >Ë|Ž –Ž˜ Ê>ã<Ïg¾ZÂ$‡`iîÓÂEaõEµøÀ ŸK6„ÎÖ¯›Òâ|´`<~'#V¤©cñåR¦?`"”´ï´8}Ê ]uñpé Ÿ ׄZ!úU„‡É_ßûNNžÎ–ÄvQÈ^|‘V:ÃQ=B-<¶3âÓhH‡0‹Îz§Ž¨öÙ&‰ jÜð¾j;iÿQÙ-ÁRtd¨L=‘‚J•câœe[$åÀIó£u;÷a–½‡\^Ü©6ÜËšm#æ’À€{j\×'æ`"Hâ»ãNzt.š?¥7ÕºŠIpu<<4Xr1aÈHÀH a FF/Á–bw†"(@0&'8ò>"ovG¨„aòEóí}\–s¤—4ºos ÉRyðlWK4A(u¥QãÇwºâºžòó÷/JR²iͪ?ÌýôÓ u­Ã”«R®êÙgXƒ%šØiàOÀÄ÷yœ`Š@I_‹ÍjÈ"ëPJ%§Ã€¥j¤ä¹ÃÜxéyæÍ¯dÕ¶½yyö[”W Ì[+ûަI4œÐ3s $,¤¢é0ÍæHáeÇ»$Vܧ扳Þ{0Üw4]6î:¨Êó_Z5"3¯›$ÿ·ZþøÆ—2j`O—¸!V]X´CûhU†ÿ:µ•ÇO«}šî̓¦‰3ðñ±êlGU²¹¼‚bh¸\|¨øWºœY 4·:µ‘ÞúϤ]®¿dt“6Ç ‡÷æ®’•[÷Èï~rñElRéšÊZ»4Ø`ÞFÌŠ ’ýH [ØO¶ye÷ÉüÊ¥cl8¸> ô¼’YS,rp©Á’»ù5J*JÖJ\8(Ñ 6 ÕEÓ~øÃžc'MyϱJ¸¦p×ö-}õÁkër½)S«øüê…ϰ;`âd×\x®FΟ%§÷ˆU«ØÙ¾¹åóš%-v’‹G/á»Ô³S¼>,‹Öí’#'3äÙû¯W~J/½;¯üœÞà·ì,ùY´y±Û1†î„IßDÄñÿáä‘gO”mÑwãÉ»®Ú/ï»FbV†ÂÿÉU. š)MéFÁ+•NôWУƒüêöË1“^$ýã#UŒþPLlw ÇÜIå=¨÷²fÛH ¹$ð§ŸÍPUO‡F–ŸA­!ºhX_áR¹Ÿwßfù[¯[~µºƒ¡ÁeÂE=áP~Òl  T+r „a9ƒYñ{†ä¢~NøõZ°DÞ‚0‰‹ U|ó›¢ûRmg½ã„žeçš³î\r\¥A’JzPAÀd¨¸õÞ{û÷4øßxâÊŠ>tà¶ÿ½ñÆš:\nŠÔM.4sV[¤“Mz_?çu«¥%hC±²ä#drªó…^Z°Õ€%Êwú„¡Hz¹O¶ÃDhÜ0—™]fN>f¦CÕ ý“¨aÒ§kƒný“Ö%íC@ˆŽÒë4Ï#¨‰ ƒÏÑaéß­ƒª;6r»0Û¶&ž> »ä@ËE­ÒE0É£)¦FÔvQsµ`í]\­i¾´9ù êÕYù61zsÔTǃñYª >³ã p0Ó\ĉ.†ŒŒê'j“\‰DýTÐ"†WN`:’Ó[ŠŠºIpð¡úUØÌ¥ÉSaaoé×!Rñ«yg?|„8àÔ³îÚçƒÌsáGR/ØTÛ\ªA<ýôØvñC° (á·&7ëÌékßxñE£QªAn 8¥Á×z!0Ò‹>Vïª{àΓϼü #èA; ‰}óÍØGî¹çL½+ò’ ZXb`„«'Ž€ÙÝÙ‰‡‹Ïï/ï|½Rzñ}éÖ¡L=HÒ3s$~F££yÐß?X | B‚åÞë'#j^°|‹ðåoµ\嘡fk¢[BMîÿóÃRT\ª|«‘‘À¼aþšòíÊíЊõU'4c3`Vøï/–É?Ðé‚Á½XªŽ–´äÌÚHÀHÀH mJ@kc8’±Hç˜`9™—#™Ó¥CâkbµÐª¦åÉáDÔLðä¢¿ä›üë¾´<—uæ@,9Ð$¹ƒ$½¯N˜ÕKàù|‹¿u.„©ý²%öË_úío×W•9Ó ð¹Õ¤Ÿa½ß¨5Þá¸Ê\ÀJŠJJ¨]ZѨ [ðbŸKU™Ã1"M½»$ȳ\§€ŠŽà¥Ï!öpÑDmΫOÝ¡wa.SaŸ è¾¦(ó Ââø>—› ý~æ5*l²&|4 i*j¢nŸ>^rpçáŠqCeB“óGA_£‹ —Çî¸רT¶n3RM<èkÍÚHÀHÀHÀH mJÀ Ÿ]šŒ$ÑÂ!€)‰]#ýe_V¼dd\+íÛ¹L¾[ZB䥤4^†uŽP<’_òMþÙ§&xú¸,êÄþ³³^‹@wô•( Ÿj9ãou^úÄÃnªS¦·I  )°ä«Oƒ%רÞÛÄÛ ü¸ƒ”ÆVO`CÐRÙ ‰Ú¢Ê ‡m91ÁIJUñ@ÿ¤ª®Ñ<úãGä¬IŸ>^ú¼Y    ø„Ô Úîtä•–ºÏò6ˆym†GÐ$Á˜Ô Á"‰¡NÉ/$§NÝ(Ôê´±íS§oT¼t ”Ní6<’_òMÍRS™á•”:ˆ†!5xi´Œ[Jn­¹Ýg_~u‚SlóÑG”0d:eµúOzâþû PòÑœÁK.r:èM_\û´fÉΰÊA•Hø߆G####f—@ùÀÝQjOÏÊÎç~£T*.K?ä. P ),,TŠ‘­¸¸X:ÛJÅé(”´ÂRšÖNb£¿ñ¸}”hX R—(?é‰4Ž… Ž|k3¼¦~VN¾³´¤¤²¯D¹Ü›¢ SGã$ðÜ˯NqXœ_Ά²&L/§aêyÊã÷ÿ$©q5›«[R°™ÚírçNéÒ’¼4¶m–+Á®çÖó?»¡†攑€‘€‘€‘€‘bóÏ-,¶0Á3ýoJÚ¬; #õ„‡…(•¨$ì6$fï„49Er¢°œL¿– ©(³ @e7´9E m¶Æë˜G‰áÁõŽÁ¬éc•ÎÐ(ED„K‚$‘OòK¾i9Á~4–(KÊ´¤° ­±u™ë›GÏÌzõq~¿…ಎX“}ÿO1Ð6ä˰8-Ç4ÿN‹³£ÞöŵK¾x× ÏÍ"C'N«„Åg²ó% ‘ ™,¸±ÄŸ{š•D#"c\T˜ŠnصƒŽ„ÚØÚk¿¾9úT{«ÞSÂ×ä?,—Á´\ô—÷ªþ<·tŸ4fݤpžž,’ô›d÷SE½NœüP ~þÙX3vãÉá »- &¡ ×Êb—„§tŽ–¤¨¨‰ŒÄBÀ>É/ùn*<ʾÖ#ûR™À”Ú$£Qjümm²žõêt¨:?ÁMQ³ÀÇGœþ~“~=sæ¾&kÄTÔb°‹ÿIWD}¥.7`©Åî„iØH `ŽR„m_¼!YmHA®+$_ÇHÔf –"+2Ê«Th ìR?$¿vœg‘|µl  ¡ˆÊ8@&Ÿ?&'UˆÚª{ŸoLF¸úBå/çoEt)?$©lœ…OCÙjÑë0k)N;¬á‘@—ò ‘)£6»üôL •ÑÆãg“>¦×ž|¦t›fÝlPƒøes¿Hê3txƆ¤ý±KnOBýÛ-×.|8ð#PÂJ‘ HÁLÏŸ Š$ßÀ¼b,¥)ÈfVÀ¦þ­ž{…?X¾;Ð$EùIDH ü’‚ŒB”F‰@):*JmÓÑûšJ«Dn K§ÓáÈÚ´b1µJÎekž6Ô‚x~Ö«×âÛü?Üå<‡þ€ŸÕ2ù‰™3¶ [¦é&”@ßöái»Óµ¶Ú’ȉ PÙ—¨ ò@UM;RóÃ-ÙDòãòÖW+äÅ_Ü(«·í•¥›RˆvzK²dÚn€(0EIÚwLÞŸ·Vå¿Ê H´Ð’µÍÒ<ŽÏþÎR‰+M“„â£òÅâM²x}2’±^¨´M59qïCçg$#²T\t¸ bm“–ˆÚ«.á ±°¨Dò K”ü—@þ?nù·–gÊ«n^ëeÆ}o?uüØG;-–û÷>)}º&6ª×A»ã sOø ?MÝ‚L¡°°Hù2…0Åa2ßE‡ÃU–ß”†’þž1¢ùðÃd '†Ø&A‘òQ‚f‰%e†W®UjºÀ”áÎÔ£–c÷ÏA?trMwy7´{æ:HàùW^»HtÐSÜ÷\}Òhò>®ScP<—©5ù‰ûî:RߺLyÏHàÿ^}µ¬x‡>ùÀ½ŸÖµÅ3f”<óòlø :cñ= xéÕWÛãÚôº^ïMå XªÇÝà‡ÝîJ¡ §õ¸Üõ p0À{¸h}´ [%ß/Bö„_$Ùþíš;‚°´ÀÎj‰²–¾…;dÖÇ‹åÚÉ#•¦£¡¦'•ûÄDÉ q‘0iiÐ×ì‚j¦ÃB‚ÔBËÌœ‚f‘kx¦šé˜j«–TÔ`~Ù—Ÿ}rý}?»ñÃïÖÆ<~çt+ßåÆ¿)Z{MãÚ/ )ÿ "€¥"~ †Ýf³©o#¿'Jš_Ý5E4­#::Mî裢šßñ|C¿ºM½.)µ dèpÚíÙ+æ|ù ŽSM«“΃¤‹›uC$¼Â‘[ðÇggÍ.ýÍ÷ý_]«xöåÙ·bðü6Ê«‡ÁR‚§üú׵ŽÖXî¹Y³‹÷Îÿ7ÞÿûÚú÷§W_Ú366™`¤¶²Mu¾Ä.ýa£q#ê«3XbÛ˜Ÿ9‰ŠXnƒÙx¬ X¢0|•’ö•[öJç„ Áì›çõêÓ¬"˜ií’“YÒ«sÅ™>æ‚X¸.IöÑ1<Ú$ßOQR[ïwž@2>.JÎdå5©ü}ý™jœTÍÕ €Ötp oËÊ:·kÓú¿YF]ðÇwç¬tÞsíÅ6ÇÓ€I&—†'PBCBPRÁJJ– Y*ókÒ`©!Ê%4¡Hƒ%+~'ù»H;æQ"`¢v‰Á’xœçëóí«MΔÝñSY²uÍŠ7óòà„ªÆiå€I˼¶jÌù$ðäí·g¼8{öÔb»,yö•WJ~sÿý¯¡¸:õÜ˳ï‘®´5I²ú(ÝmpÔ&<·ó6»ão©……Œ–ávØ+7ñ²ekư­¢ê}_ZÍîÖn€7¾X&×M%Ùpìÿdá:™q逥×>[ð  é-ó×ì¨poé<ß³S{¹phÎb ÄÐ]ùÀ,ݘ"WN†£‰@N¦ƒÇOËŽÔmr픑’•[ /¼õüõ‘›«Ì¡T¡³Óh hPÁÙÓ©GdΊí (%…ßèºSAy„Ÿò/—l‘„˜HܧK Uõ‰@©]LxcXjS×PR^§3›FþJ¾üLµ©›ï]¥¦ƒšÎ¯^0wmdlÌ{ؾ]>çmÓÇY£a"hÑ¡¸ H¸ÍðÜ.ü&–@ËêÒ*Ù•f ³ÇjÂO&ðQob›jÁ¼25Kl“Ú%»!X#@b^BW˜pk“j””6&°HÙùéÆe‹˜£§¸l¡|)g£Y‚š‚~uß}éÿ7ëí)Å΢¥0­+ùÍ÷¾R]½Ï¾òê½x¦^Áã¥áô¶ëÔGò“ÓÕ]ÓV3”ºÓ*1‹šˆ·ñr¼N»œVËs¿½ï>Èyö¿ Ç‹¡Õ[·Ìß>x߸^y¥§Í)oÁh t9Ip»ã7Þsˆò{vÖ+ qí ˆ­2 é6ZÖwª«›åŸ›õêm¨ÿ‰2`³2ÈüËǸFpQ*Ò“.V§_HÃjiù«ÚLRÚšD½kÿqØ££ŒÞW®?Lb£\΢âRÙ,]®™4R ‰Ð¹SfîŸ:ZFôï& YÚ ­iëîÃ2ÎûŒht1®ÑæP}»%ʸa®6b"B%å`ƒŸ?w6Ìv- Y yù…òñÂMÊôŽ%o!ò’o¾[‚’òˆU5ñçÞ§OnÄÀÃOi”jºÆœ«ZÔÄQ~Îo˜ü[Ë3UµtÌQH@›áq0OoèÂïþ÷þç‡ö$€A¿óÏïÌqÐÿ¦±D D ARH0ý†ÂTDºèèH‰‰‰RK\\ŒÄÆDK\lL£UêÓu³FÀc»lŸ|hRcûÆë)£?½=ÇA™íÛµýóï?ûè[.,[(WÊ—<– „¦"¤-V™±>üÜ+¯ÜSU½0½û9mÌÆ Ü”,²),8`ò£3g T…À ¥PÈëI€¤UýâÛõÀtÂk;¿gQ˜<þ «“¡Áã ”xÌæ´üËê´¼Û7¾]g‹Õ²ú“<î"Ë(‹ÃùòV]ÿ›û﻽¦ºUy‹ßV?KÀôqƒú÷BûY%–›tM [[øî)rZ=ÆGÉh–pãr0ˆ‡öGS4¢e‘òaÏMg×È0×¹hwjMXIœ-³cPN:““/açæÉDM!Á’‡ú 5¯*h^R 3“¥ˆ—[P¥-bzW]O©aÚ2XBóVÉâ»dÚ…ƒÕ¬,7UQå>å)³QczW•´j?F¹ÅD†JZFNƒäßž©Ú¥dJ4£0‰«ñÌUÀÁ=ÍÆ‚|òÁ—#ÆOLwŒpç_Þû.ê¼Þ£õ´ îÝ©Áy˜´–I'þnñ7Žß dË´¯¶¹n±R¹† ûlS|ÐÇR·û5Ì£ÄðàŒzÇ`[iîÆåK>ܶfÅf”£óÊÖ”+åK°Dyj„ž={4´ÏàZü­÷=5sæ±?½öÚ[©})4%O=pﻺzhœE ˆõ>ôJë095í‘{î)7Ï*?g6Ê%€wä‚g¼Ãx7?‡_ûëLJ<2cŸårzûí·ƒö ù|ffˆÕâüêÓU¦ *u‚ðÞýëñû~šôøƒ?…¦éU¾—ÕÖýÔý?ÙÎÊ_{íµ±8—b°t3vk5±,g¨ò†SM©£§Õg5KgGï•;؆öûC«ôíÊ­H˜W,ET>yFÎÔ‘Ä"„€h}ÒAhŠú)‘»XÊ~Ü©í!0¥Ú´ë€ ź°¨3h.1§bæ+&x¹hçø©Lh³:œs­9Ðtà?ƒ9”àž2êÓê…Qï<Ì¡¾½ Oämüæ&ì§ø ©Á†{]•û´fçõNk/ÝËšíºK€òcôÀå[öÔKþ­á™ª»”LÉf’€KZ³Ä>gÛ7¯Xº>eÓÆÔ ¦]~©Ýn›@ÉPDH3:*ÌàB%dŒßwª¼ï~®®Ûü~¹Så}÷sõÝ.*-ufeç;™p¼Zì¶Ò¼)ÉË×.øvYaa~ê£ sËÖÜv×,Uì,Nª»žõúX§ÃöwÜÞŸã±ùÓæXöçÿ{"üŽÃl*¢Ü-~nÖëÛŸzà§[a2ö8€Ò ºv\³2 8èr”å½1Tƒ [š*»ã€v®Ô’ŸÏïB°t¼ äBÀÿNùùE ËŠZŽÿåõ×ã°¯4w}ââ–ès\×T÷3³fÏ´ˆóšS6{^­@‡¡z#È‚w¯ìCDZ–!Ê¿tHïÎ’‚°à¿yåS€£ˆ ~×ÁÇès„yþfŹà¼^uâuLð¾ZºI~ýÏ‘³Â!OÜy¥º.±]´¼õõ 9–~F®€¹Á˜¡æ“€KcSZ¥ýGÒ„³i¡›¯ÁFÖœÐYbhþÀ±SÒ§[5 K[wªÜ§|„ÂfxðÖB4SÝ sÖS™ïx–Â&ê#ÿÖðLyVʦµj$@³úÓ²äËo-(È“Å_|ü ¶õ<¬G·>ý„„GÄ…„Dúûœk€‚­l¶Òââ‚ÂܼÜÌC{Svïݹí0úL°É$Áµ\ø!¡<)WÊטàA"§ýi«Ü÷Äý÷oB·iPLî.-,YôÜoL|â'?ÙÿÒk¯ÿèÌŸž~öåWþ û;ÝðKƒ"®üÕm·ñ~ªMH{V[žGH¨<èrŸºÿÞÑœH©êšs¢æUS÷ ³Þìmw–þ*4¾Ý`j°v`ÕH3<÷wÞR>JF³„G“¸Û§“[.»@9þù¹å·sX¿n2´o7e¦@GÕ§Qç.Â Ž‹¦[¯«7%>6B~ríD)ŸŒLx©C»H¹á’1Ê\Ç 5Ÿ83ª´JÈ\_Ä8ÝJ™GÉ[Iñ™Ï«[ÇvÊ)š¦+zF¶ª>qò–ϯ'èþS$ uI¹0]Ý{$]¬Ý©ÞšÚç³Þ£S;IEùšˆýœ0¢ ä·X¢)ÏúÈ¿.ÏÔ/džÈ=Ã]¦¼9ÅNI:e—çVȾ3´ª? ÉçØ.þ²ì ­Šj¦Úž©š¯6g=(rø@p€Ä%<Š4*Ý»cën,ûq”¿Ý,ÇE—Ãf›!Ê‹r)— ¶©=â@œZ ¥¬²m(ÊÕ˜àA¥Ðø¸«9Fd»0ÜìòÛÇg‰í{¥IôC ûü^=®ÛPZ¨¯ÓÇ̺áÀïÔÁ›­'j8“°ãXÍñÂ+¯]‡ýOYë_ß|3fŽÈoT?²;mƒ`z—ÂûK3¼S6ÇPCÃ~¨tÓN)÷qÁh†ï¢O’Kn·Ñyª" 4à©ê|uÇÜ}”ÜË ä.æÙv ;;”*¼L˜?Ú,ÈNßL g›¢äùÁ³Ö“ g·í9,›w’® q*7#B\ÔD=;Ç«°úµ¥šêðÄ9Ê‘É3ë#ÿº>SiyN™ùM®ô‰õ“ûG‡ÈÏF…È/æs¼þ4¾«¿<|AHÀRmÏTý[7W4£ôÀŸZ €ÜAÓŒ…Ú¤rͶ ˜Hú×^ëü¯gÎ5Pâ@޳” b|©¸P£DÐd´JBcé…Y¯rø;íðMÚ¬}fù#|ƨº-²sbÿ+¶Zý Qú ~)oÓbù¶ShðuwΘA0k¨ $€ŸªYN›ó?0™;xçw^ÓÈ›NÛÛˆ|j Î/*¥ÏÞÕõmªo|ì¼=é?Ø]“nsÄaðñ/Øì]Zßz*”·8ƒµÖ>û T*ô´íí\ˆ0áb›´ãCúvEÖt#î&j •Ñ\ÍŽœ! ‹Ë¤‹¹‚Pdñ~‹•"kdCkC¾ƒƒ÷ýЪꓵLkYƒ(šôÍþN  —>]dpï. ,ÑÄôäKˆTN¶ï9"ˆJ%ãcå²±^­ÍÐLí;zJ¾[½]ÚÁuŽw@®#†ë_º)¥\óíÕWG`•I>x9R”–¶I;RMet@¯üëúL•ÒV%î–¨`‹<>I9-’W┫úʽOñaÙžf—§—Èál»Ê•µàÖ(ùù¼1þ ?¡²ÊF9WÃ-칇ÎæçŒýw± Ž³yâãef€ ÑÌ•f©lÕ+بª‰ó/)ª9 p„'{!X⤋6DÄshgüfÔ3>ÔäÀøÇj-`‰ùçvH*®M5©U’|ðšµ£üd÷¡ãâ0GŽÜ`3÷¯õÔ©U3xqwÚ°ë ûž æÑ‘ôÓkà7fàï®kûôË’õ0#  tø Iêƒà*‹×máÞ92à¡c'iÃ΃òtµ®YžVkaî™rÈ¿"}ªGS“ˆ4–gÓéü½z…¾Û\@‹÷Ùé‘^fšrc(Ýøåiñyif ´BiÕa½¾8d;iÅa;‹2^˜ŸC'ó‹úGI‚)­O•t:^kà*“ç6úyðÏ@‰g}Øö¶8°TŸA“ììR.,±‰ËG&^ó>—~Jò^RT^ LLžÒß©;š#ätw¿ø",óTÖ7“RÒ8xü¤)SîqÚõIºSß`¸ þKiòwÙšöM´Ép/€’ÿîSj£ò9r$Ëõ Ù¬rÁ ÙâX*Ï$J€Gÿf B(–¤0ÔZI ¶%À&x¼°f‰“-r4>î$‘²-|?´©S«¦€­ y5A«µlýNÑœ¬œ|„ïÝ-´§ÛLƒ'Ú¶hŒÀ)‡‰C›GÛˆ2™!®mÓ¼1±öèkä5â^“O¿í±Ñ1hˆ˜šY Â4OìàßætbҪî£ì-?(’åx¯Kj“÷uj߯$À„‡¼–€@j“ø¥à…»VCõYbÙð\XFr[ʇù"I}íÈ-x'P3?eÕóabÇá|å2Ø_éµO?½± +û—IÉiwtŒnô=òö|Šnz¯¬߈O™ía&ל<¡Ö TZŒëgŒ¿>ð¡ÉYáGñ¡$Pi ðÀеàˉÐí°«#ŽJ\C€{Á7㻢¶øG›¶Ak4gézÚŽœa(±X®¹¤‡Hêúö—shúoËK•TÌ"™XGE²6©:äÚ#6›ûzC(q÷žvR{ø"Ijá†é¹…Sq8Q-Re©¸6U¶LuH@©Yâ™YVCòŒ0à…ƒ4´E¶åÀò`¹°æMj”*ÿÒ °†HŽÜü§€À—>—8ìµøÝ01%åQ) ý¤²}»wùaÛñô¯ÎJšö>ÒS@IJ«a¯1~á[S–ú„m²©«)úJ×Õ(¾•Š‘€dà—ž52¼_g¬òàV¶Aò-÷ùœ?µÉ„\P¡f3’:ç#ô¾‘úžÓQ²,Ö'NeQdX6˜)8(yÆNÂÉA—ž×‘E„R»ÑÓ8üŒ{ju§å¿hŸš†èÁó‚¨5€Òà ³¼50½+‰v#äxl¸‘šá¾H‹(•ЦrÝ«.ò 𘧆‰/ž~: êûâÙ^)ž}áFi“ „о/¢ M۶‡`˜Î匋?h 0 Ð4fbrÚƒ²ì¨¨(û¢M›§ã³z§<†¸¬ÉÐ(äA±<¦Ö [¯§¥5A¿3€èÇ ý¬ê¤PXª“M1] ¸ÁE]Jhi$ïÞí”Çyí/Ħ¥ëwÐ%çv §î»N„:çhy’¶í;JÇdvÔ]×ÐÍWõ¢¼ÍøsµŒŽ¢¸Ûû{ˆ µÈËk}]“ò_¼ßF//Ê¥áì0çþpêm¢ÎͦDÉ+‰~Ûc§ívZðú‚C”‡JkSyîW×øø‘28‹4?kkÙf^—ü¢øÝ#óo†\ Gû1nÁ´á&Éíè‘#÷Á=u ¦èþobò{ç¿3{v4JßAò·Êkð£ÿÖØQq£PrK¤Îl$MN»eRZZ÷ê`ت›šÉr5ÒÉíº¸fûgEJJ…àHeßÜ&ö¬ðÀß3©÷WåÓ[]¦cTßX­;æ(u\‘rkúž”ióK¬rÑšmL;Èóô³ârò è㙋 = iëž#báD°Vh™Ø?‹iÒG3ÅZþ›>¯t“>y?¯ßX’G¼”DŸ®- ^‚4ÊB€I±[òÙ~]¹NúME˜ ¸žÇ‹Š”””*/ _$äì¡w&½÷ÞÂ1#FlãRemÎö6DEædîÞ3su×zÔö*ÂSöØ'ÎˤÖÑqqk=«m"D |Ö!°, Çæ˜)‹¬I¤í¯¬qôÛu;ý„z7yÖýjJJ›Sû!Ë»ññ “S×Ai l‡§$ækµ d3zÔˆž÷±­Ù›Ë}´O%) µVð T…Ö埈>Æa™ïèHÿB^›y»l”ƒPϾR€Q£w…ŠÁí.« øZFU´Ç×:+r½<%Ý›_è«äy>üjJþž@©<29_q TSm*O;Ô5JJµ#¤ÔÔ–0±{ƒõ0¿›ÛÔdS©ï´!Àauüм=ý(1wzpp^îñôYà÷ws«i1ÀçÞ/ܰ ©¿yeò{ƒKL{ߨö‘Oéßh&/„¾[ÚÏÅÅo¯ M× Úà±ññ¦M›fÜqüÄ-vÝ1Ïøê’ž1úV'Ù.g*°$…Q××ûgÐFä†É8M§²rE®n“ïCä3%Á^05 ¡ÆêÑ!–Z#|²¢ê“‹ßëÙƒ¤ ›Ž#‰è ; «è‡-Vjeà霦F:ŠˆfÉËóiö×€ÿ¥~!´÷Ä"4ôÐîAô$’‰&ô & ’N½%”lÛü´(Ò[i\ñ^Ôû<¡yŠªQž²–Õx«‹ò÷ä_¶I­•”¦xÀ»õxúÍð¶f2Žwس;§BwAÃô?„tèÚøÝ8ï?}dA¼ÙøÝ»\J 9éÆaà?Qî{®ÇÇÇ/D²ÚGíû,h(®{>!a«çyµ]¼^oGôÁ‹ÆJpkêP• ’NHNYß°à…=#S§A§©áþ—6!9õM¤?\ît‡toåϯ†óóc~š+³ä´»`µ›PlÁðaZ—èÆÿ¬hh÷Âû¾C‰µ‘cŠ¿…ëð&¤.îÊ>Ö‚4­N÷o†gG<ßßþÞL¿.ßDYÙªdcGÂá|ƒ1H]‘©¼;@EöðÅ5;Óɤäë÷Õà4°O7л4øÇP‘ÖÈ=çD»úk•Lððã„¡™0•úòâ\ÖÚDo\g¡½™NÚxÌN‘Kçq€£ã0“JAí'4k»•z·0Ñ›Kóè`VÅ55ÒXU‰’€’€’€’@ƒÀîã§;#Ìw‚2|ž”üÞ¥˜ùÆçiÙx˜_}Ó»Ÿ_}ï½0»Ýù €Ò%ò>…Ï(‰Aº<æ½÷ë¤Éi 6ÝùóËÉ\ûBâ£;¼¯Qû^ÐõPøŒ5ñ:Ú¦h_»X×ô}Aº¹Ÿh7Ù¬¶EßÿӱÇåûËêÿLšñ¾ èÈ È‡•ê̳rŽ”W¦L‰À3|š¡›‚ †#ùvÇ´­ééýqn^QÙ¾o s6ç³%Ý ^»a%gÉæz%àGÇô(}ãÎô鬥t: &W1t4¤e`m×8ï_õI·QcÛQŠ)8@ßÿ¶’~[¾™¸¡¯Ð6Ávµz*U¥²m°Oô@O8"õ„ÓFø-„Sý…Hn ‡~È¢eíB£4°} ݆Ģ –˜,D×~žå6Ù“ysÖ£Œ]'+f†çãêb%%%%%/ °&‰5¯'¦¦^‡Äï÷b¶y‰A3-ÿèŒgœcFÅÍž”’ò¤ÃiûååÔÔ/ÄÇïñ¼OmW@º1M„uÇ­ˆ^¸¡e¯Ææ—\49³^5âoÞÆsýŸS×G`3ÅîtÄz†Ñ`ØÏ# ?bPÄÚ J%2›“-W„GYÅQWyÐh4*°$…Q—Öó–m¤o篠\cm ½ŒN›¼Á|õ´†ØÑÀX±DØÓ©sÞzJžö p!]ݧ; GUTq Ø”agLèŽØhóÚ¼?—vìÏ¡Öô„ŧõ)¶2pë_©ìÏ<ÚP„b@”iKF‘†hsºƒZ „³¤•‡ín $©µ’€’€’€’€’@mJ`Û±¯#xÃb›Ý¨u4ÎIÁÄk‚9L3ôó^P‚R”áOA°ç Uj įJç3ßé“މJ©ÞíÐÈ¥£iw°Ó‚S·œ˜’ÚùU˜£\‚û—B£ñÊ•”üÑõãÞï}¿Ú/YŒpÆ,zçèÈòj<Ø«h÷¾Vº·‰²q£õ@kx5žÅ »Íy©<dUéàzvA+ ¯½î2=6’“§…žÐÓ[ò!ô,ŽªèqºÎm68Í¿íó—o¤oæ­ ô€–´9ärjUgnçK`€¶2ôJê–»Z7'¼áöé!“Ò2•.IE{N9aêÍ ¬á[´ë=§HB‹D´¶|rä“#ÇNüœ´ö1Ï—å Ÿ%O.ö!2S;ø$­)t®çíß‘+GR¾KÁ$wÕZI@I@I@I@I ö% ¦;tûbÍ`øç¸„¸¿˜¡èã¨cvG>™ÁfŽ]×®5´’¾'ßî\€áÒ9…ŒSi#ÆŠû°¸†°ÉÞ„ä4xçÒ£FM¿Újµfåõ!ÝуôD¦‹áCƒ0³ùïáþAÅ•¡ŽAº†¨xºÛ´ æ‹íºö*Eí µhÏkË©{ãÞ<¿ÇŠÒëŽJíß SÎ%ÅrR?ÑKG›Îˆ´'×¥uƒK ”ØôîÛù+PÚhqk–kí™1Pc>zäý°`5ÅD…Ó¹Z)ÀTøDšÖUö!o†ÆêxŽ“éD¯/qÒ•mDBÑß÷%ï:öÐqè‹à·”‘§‹Pϼ¯HI@I@I@I@I ¦$O8&aÓuçXäÕY:&.n)"ßÁQ›ncŸ%ƒfok4ÆÂXÜV`ÓÇW˜PáFÃôá3É+ÆRÚôéÓ žÆ'Æ}ˆðÒˆ+=Ÿ²³¯›˜°×ó"ˆCQ#(ÄÙ¹äjM&“áO›Ýù"ä QiˆÕ`RbAi³à„ÿIÒäÞ7êѽ\>û¢AÛS¾ˆS^L¼9mZpÞñŒ8”ó(’»A‘çe9/–C˜®÷òlEŽ—Ïà£Ä¦w¬Qò'b~r aôå/Ë(/ÏJÞ¹jü‰×êà%²íùMo~®Só^»j_Dï&¿æu¢¾Ì¤ó§œ¢›¿Î$å²"æî´ ߟ’ë«[¯èŠ6tß¹ôä%Á4þÊjYyíaæ|Fü”M­#ŒôËýôô%!‚§ù»KK§~ü«õù4öŠ`Züp„ˆŒWò«Í2aM[•f¶\›Ü©º•””®8<ø„””íZ/—?Ù¨™îv:ôo9KåÏ?¢Èuc¾„ÖÉé´;Ý@ §íšîgÍ‘§“RÒF 1íÎ$ø#±¹•<ÍEª´Oð©œ#}£ä9Žˆ÷l|ü1¹¯ÖgK€MÕ ›Ÿ”’º‘æ6ÂÌ.Flξҷ#Ð@údÒlk5or=³Ùu˜MúFºSŸ óÊÝ9Ç3Öb ÖÛhÔz—d^çÔŠ‚`\¶Ô·šüïê£YbðÁQïN#âû(Õ–é]I]€ùÙ|.…d/¦ßVl¢ëúžËvžõ·iÐ;ÛƒvÛí5‡£ŒÚº#ZJü ôÐ5êˆÙ+hް”A‚¢ŽŒÔ©‘:42QÇ( Ú±oÒœœù”™¥SƉ|:zÄH;ø†rj(.¨¼u |”nxãpà¹V̽ÉX?ö3ÔƒÅPüžÞø+O™¨fzíc›Rÿ »Â7L}‹yüê’€’€’@­H`bÊ”t§ó}˜wC2YýÕ?¼áùGùàéA‡îø 'íô©¬kãX`Ø •Òo`´3‹ñ‡ tòê|ëÍ| ¦/DÂÒ¸¨ñIJß0arÊ×ÁAï ìCüaâä”@›Íù3€Ôµ‰‰C³½ïWû. Èàžò—˜`GBCµÇ.À9.9­‚å6¯6Ü}n\BüƒžçÆ$$ü‰ýkä11MÐ믧¥E‡Œ'¡U³¼Þ÷Éë ý‹„´| @¸§aFqá44asÂ#Â_ËÊÌŒ·æl…ßÓw-à± Ã_Cðˆ “”ñ*îO,® u¬d ”ê—TòmežÁX’.Õ>«)’;ô–²—Ðà@IDAT‚!M;6fĈme2çç4°ÄZ¥=´eçä!EÛ´ïH†`ÅtFðžÙû}åVÚ}è8(lÄ‘Hö €¨À<»3­º º;löù0ïh&Z©i¹¸çø/Í+«Õ›6ú ~Ko¿æSÜÞ%ºñm¬Á˜+eRò”þ£ãàƒ e•£Î×? ÀüîZw« Ú"÷vÞh `ÉA§²á©h®Ö„³UÑ8ó™q:¹Æl fèy \ä4˜´–ü·[H›Þ­ Á¡u2t÷EGÚÍ• ŠˆvƒíM!t$'%‹‰ Þ´pX»|¶°úíp9¸·ÛQÔã¼Ï›ÅÉ*$ÈéY­ÏÅÖÈ Ì£90茺Šk_ÀmÊtø?d^YCêM‡3\xrЄ)ÐTû?GvÞX¤c¾J“yûÔù1®¶mNw2 œt ³hñÓv+=`$ç53Ò%-Ä9ÎÙu3Lñ˜JËí%.(åŸ/}ª”bÔ)%%?•G¥›2eÊ=ÇíŽooÚüùñãÇïƒïÐH™©ù™9_ãÜ飂d³/3€š˜ò~O§ÝÆ (Z4K£l£f¼qLˆße3_KMmjuP¿±£â§ÉcrÍuÂÙþPtK´Ép%ö…±9~/ù‡mþØQqòRµnxpƒ%ͨ-¬ͯýÑI5K‘MðƒšN!°C¾Á\͵UMñù† bó&ŽÞgBÞ ´¡ª“ÕZ‘§ˆÍvœpÐN,Û‘£èïìóètôŽ,M\Y]:‹B½â‡Ñ аa£Á`ÜT (ÂÙ\5"ò¹”à#P™t›ßfæÍDáÎ1Wj;¹M¤U`m⌖zmMžäþÊä ~šF…!¸JÞlØüÌäŽåè@®3K`ù~}éS;‘ÿ‹)‘O#G[–6 j°8±Ù]ÿ¶,ÍÜæåÉí%Ë(níkŸ*® uLI@IÀÿ%À`çÇ­ÇÓ?ÇÿèÝäámÇ3>C.¥¯¡qº«ÿþbvÍš—×Ew:æâ³Þ¸°e™0›<&~„ÛdŠ}ž’>øÀ¢9m¯#ê]𸄄O¼¥`6>@>¦Qb†©žzæz·Zí—&WÒÒÎCøs¡©p> ¥ÕcGŽ,í–:q®èk]'ØõI'âÚ– ¬HNê ¶æ{!5|‡Cf+øe°Ä¼s*J ж"?Ð,Ì\ÿÙ8bÛ /NÓÉ}+¢º=§òÔù4o—•N;&átT|] Š´“Ï0h†W‘û“ÁØ«E“P˶gÏí¸õ™soÞòÌ9/lý繟o~ªû*/íQñEVóQÖÈ¥‘yÞøË`;Z͵V¼xÁxleqóí]šl¯¹MßSü„j“NœÊ¢È° ·˜)8(@ø;‘IÝÚ5§Ð3õ9§=Á?É߉åÈòôEþåíSÛñfä:éžA"7×?Î3“Ù “±)^?äî:'ÆH?‚%ÏÜ^lÆ÷Ày®ûKËíå)çòô)ÏëÕ¶’€’@Ý•›Áµ ¾ _¾æ0·{ÿÎ;ït^Ö½ëƒÈs£/Ú´å_Ü28ß÷qÚóùsXØÒS˜ý¿fL|¼(!O›¤ä´ïǾÛ`ˆ@'¦¤Üë-ø=¥c:uþ¼‚{¼Ï©ý†)‡ÝéÖ*A?a¬‚®V÷Éës]÷äÙöWrù,ÁxŒGAuˆÏžìv<€%Ù —DBSt’“¶:hf;xû„xfÚG¬ŸMç^ ‹p˜Z:7ɰ±i£ÍÅ MË̲ °kåƒà,¯QŒí Œ-IŒµzœy @ðƒÆáÁàºèI¦Šk”K”36Kð™¦{òžšXoÛw”ÎëÜšFÝu ¢á£ÿÍ]FKÖî «zw¡Çï¾F€>ƒ%áðš`²u°MŸäïKŸz{Y>C¾­›»Òœ6:’íÒÀIÖ~ÙaE>®Ú|Ü.r‰ñq™ÛëÕ‘Û‹CÏs¾±Òr{Éòx]VŸò¼Vm+ ( Ô} 6,fw7C›4{RJZꘄ¸ømÛ¶ÝS`±sòYÝaçÀ löÒN ×`æ•kßõÿ².Ú¸åü¤´´ÞÐ ¬@Àˆk¬NÛ¼IÉiÖ1‰qßÈk_NIiùXNl%©uÖ€®i7ŠYGˆŽt?ÕiÔk°ÄIšáÕ9°pÇ@‰iÖÄí‘ h;@ÑÎJ€"†]­\•£Íqn¢X–-YF‡÷n[õÉ«/݌әXØnJØù+(g‘T ‘ÈHNDäu3‹YG)žNþ>žyЂ֫mS‹›_ÆÆ²-ÜH×ö™mjb¤ƒYV~»¶´79yôñÌE0 |0¯›vK`´¤^ævw$^$9`¶7飙r·VÖ¾<¿ÀJ|”¿/}ê›Môýæâ÷¹6þ#òŒ¶rãsSÏNp_Zn¯3 ðÚ)OŸòºEí* ( Ô À$/9ŽnD¤9ÈŸt"Û͘” Dv¼ ß`|^ŽkÓÀ±#‡¯ón2›ëÁôî-Ø%?ƒsw?Ÿ8|3B_o³;æ&MN³÷ãÒÒ:YÎÙøN=?(`Џ&N™ÒZËË…4-¿±Ñ0¯¾ˆ¤^ƒ%©‘J¥:¦Ybÿ‰6:qÂJ‡ rioŽ š"§Ðù¢#óEE™ŽÎɽcY­íðjUôf±6‰C®óÂ!¬MÐ",€Žæ8©SîzZv¥ß$&6èÁGÁkß4\ð+y÷ôU+©MÇrtâtÅ4‰ÄLN °Å0Øð&o ä}ÞöYóÊòãõ‘¿/} ³°(Évûò³äKn¯òö)ɇZ+ ( Ô/ p2ØwfÏÀ‰M'NN»IhgÀl8D´R£#£áê1qÃKœÒ›|pRO°Õö…øø=£GŽ\íÁ‡þ3;´²Úõ'5“!~l\ܯõKrª5•€Ó¡ß{¥{Ïo Ú+Z–¿ÝW¯Á Û ˜üMòeð³ Z£/Î&4ˆ÷@†Œ²4ÙϾ™ɱáFêÄZ"E™¨}ÔÙ èì»ëשq%ÁùKÅZòÈžšÜU´Ñr‘_4œy±83©s üXÌÌg wÌ¿l 3ÊÛŵ©¥E§=YLðj‚@ Š|“Ë 3¦Ô£‚ò¯hŸb›m;ÓÏ7ÎK¾º¼}ªäÔ%%º.J’Ó®w’þ=C"¾#4ýêçãâ¶ÊöMLK»Ý˜bœ86qÄ>Î`kBrê{úvŸàcM«_N~ï&»nÿQjî·+R` hºs¨{2_ÓÜæšõA:õ,ñCªk&x‚gÉ·×Ô³'(r™ÏÕ PÄï—çÂ"¨q¨jÀƒ¤ ˜‡€QTHåX à rˆzäüM›CzÕš†‰gÿyPÛÄvˆbà i)äÓî˜ÏÛeµé8"£§ŸÌ¢F‘aµªaªÔƒ«Á›…F @‰éV…ü}íSC¿ÉªòÖúÚ§*År à~Ï¥RÅ©›•”ªVÒpÆ?´ê®¤íÓM†Ï¹Ó³¦AA"HCW]·Ï˜09uµfÒ&­0šÞuZíà›ôÒ ÂFø…ÄO:µûFy–¡¶¶8¤%9é2þ$`î>/<<ôÛú$‘–*óÀúÆšhÝ1Ø<&¨9bkª¡uŽ  ®-ÍÔµY0unX5Eg Öî°çåÛàÔ‰ŠŠJ\š¬1X2$™ƒ© K^ž™š†Øà“1€”^ÙY´=¸gû0±? ›²F©YˆN­¢‚Ì'óË|³©83¼Òڔϗ=ãE…3ðòÿt}¾•½}”ØôŽ5JU)ÿºÖ§**Ç|«M‡?¥·‰ÅYïrEËW÷) ( TNÆ0¥¯1x|Lwã“2à…‘#÷x—\|ÞEèñ)ÛÓÓÒíú4˜Úm1Xõ¡ûþ Ú¥xÜ3IÞ§€’”„ZK ^òÝpqÙ5ùñûïg¿÷zCõ,UV£†w…ÏïBf¶š¤N0¥?0Œ7²Px›gŠÁsMòP[uÙò Nž:S)°$MÖÔ—A’ÅBùTPP€²¢9È»”O‡ò¶)ÝDG32EÀKp £ïøS&Ùžš^³ ‡ç¨wì_eBDÓV!Ž*—]êS}§2st›Õz¢¢÷«û*-”xN&ynWºpU@•IÀs·=÷«¬ï‚&¦¦Þíp8?Ãq1ƃ÷"ó€âÞï}­ç>‡Çþ{ÈÉôÑ’M[îwèöÏqïØ€Ÿ ÿ§7جÏóú2¶U-C@~rÚ³OV¸˜?"Û£iFî{õŠê5Xâ'%}–|}jˆ4MïÝŠ•M½%”ص`à§§‰Ô¤«C¨oKÅ-Øc£×–°i—N/õ ¡£Ü~Bœ\r2<7/›ö"|·¯„(7ï•}¾Ö]K×Ë—TÏËÍIÏÊ+ÐrnBÌ ‰íð×è0£ŒP‹@ÉŠÈlÈ]m‚+Â`äÑI"æ¼0€áᇂ¨ ŒZÕ¼FÝNAÎ|€3|gÐJ6¥Œ4Y):Øpd¼……¡¯…ZÄ6óË|s?n‡¤ò·)_´) }2mæ" å!Œz)~o²žú¶v"Ñ,œ’B?ç¿ ©‘ €¹Úä_wúTEž5¿—ü~"±åÜ/ßÛŠ¥î)¿ø‡@{ä¹—z#ÇÝuøjƒ—¹9º²rR,¿ küJüâdã‡ç0Þ’½¶‚‚_?}ûå•`‚¼TË»“49õüÔMEùF,è*Úb0väÇy¿ƒ¦é.”wGîþýìóTX}×°sµáÁ'_¸0 (èÙWñ5QøÊS¿º¦f%}P÷S¤¬Ùçt8æL}}‚ì§åê£IÉS.×uG7Áµ¦»´[ç95ۂ꯭jFƒÕÏg×`wh"‘kï&zsiÂ3» ƒ®MGôõú=ëAZ‡ýï¸q°Fwõ¦ÖäÓ„ßsé‰K‚Åòôœœ翎U(_H^;÷oߺ¦e»w­ßq.FBÓŠ›°q<öWb @É3»ËT/ŸL0Éj‹¢K»×HÒÔµùB;ÀÑç¶"jÝå­M,ñùcÐ,½¶˜Ó!=vˆ½@ ûê_±ðl±ëÿ^º£ÏÕ×þ{ã®p€¥ #·&àƒM°(Éx.ÐaDXãäHÙ)I€ÃðÜYëT•Ú<æƒë“@'¹‡ÌAlL¬Qb !¶@q4<½…&@ʸgÔ?¯ ŒJÁ·¶¹ê«Õ(ñê)º¸~úróží Ãó#?{ëÕ_Pm±}ô­©S#sròîä“Á¨MqmÕ¯ÿ ,ùø<£‘ØôÅ«B¨W3CΞ˜Pq2II;<@©F!µD¤3©™’שõà„JX3BµïÛ±}’ÉݾïujÝ쌋}Ù‘ (Á€t„Øfbmû±©GÈËËËwù209œ.=ÌŠ‰k+šxÎd€Ýóa4œ(q.­|”`zÇ%a†çÖ*ØAâñÏ—6ñGzÙú”SÀ@—IclLácæQbýÜôù3¯ }ª¼=€ßG ö´C{wÿˆ{ø]•ïm¥zå­¿\Ç?†GÇüûIM3ü§yt„~×µóï ë¥¡>5“ÍÉÙJ‚'ÿøúzβ¦ß>ðôè}öæ+ï ­ì+Äï¿GE 씇&&§=3ã·åd LïVÀÜøÚgFŽ<;Ëuy ,ûÑGq!ÑŽÇÍÁ–ÿkÑ4RõÕ²eç·W×Oêú=7nÌÇÿIzŒŸÕGsròD‡ æFá›;L\Ü&¿m`%S`ÉGáýã¼ êm¤k>?-ü”>r¦Éx»¨¢ ¿¶‘ʆßÈah›•(þ0ðƒ.~ù£a]<{ÆÜÖ:^ûÕ/…v&W‹äŠó>ƒ )LRËÀDHq|Ä`í?``k‡v ޱU¦]’õ±¦ˆMë\Z%Wt>6¹c3¼`±°ùŸg~Ë¢ò´édf6ýµi@²æ‚=2uiÓŒº¶m^Vñõæ|mÊ¿®õ©²ºÕæ ¼N„Á;ýçÌ~Àõr€Çï®|Ë*F/]búÈ /= ôZïníôoº¼R¿¥W§ÎÖ´xòïù‡n4~:s‘¾bóî¤ûžxÖøÅ_û/ø` Ö2ù˜&NNy@é?²øÍûËd2\$²§å±*^K ÄQö‚ÀÿJÿV}µŠ¥\ËÅyõÓWzv¬öñk0¹û(|ڌێ§?á†÷ºön-³]mÕ+°TŠh÷žÂ C€‹à·”‘§SV“šC“”’'.k@½š›è·ÝÜw\ÄçïèDkŽØiH× Z~Ð.ÊçÕºX ðÇA%þ`äæfe¯Z¼ðSÃUGñGåÑ!WUxVU˜ˆˆmh|x›ÁRHp°J"øƒ•}z Y‚¹ž§)žœ­+–ó*•Üà Z2€%ÎûÄùžExpÖ.q0I®D´.óX•Õ¦ŒS™4÷ï-Ra$KX€’“.èÚ†º´ŽEW¤MeñäO祥œjZþ ”êZŸ*ëùñ{xèø)Z·lqjV–°éäwU&5#T–ËwÞðgÆ\o0_åÁge~÷ÊWºª6$À“âÙ~G ˜^¼uØÈí?Lòs!/< à ˆ2)irʼxå…øÝ[`üü#T}7Y‰Ë?‰’| QõÕ"áÔ§-¯~:ñîħ¶~ü÷SÑGw?qÆí¹ÍøÖî›0ræ¸QqõIî¶(°äÅÙl^÷Õú|{E0‡é]ßOÑ—ìðÊ@ ­EÙécshQ¤õXyØN÷ŸDú‡Ðq€ª¤?TÞ¶³%{ÖhñÇ^,°<^V/ú}uTtSžÁBø¨`†U«¨†‰Í27kex[j—X£dE—VÉ!4K:¦Jد²fx\/ÿ±f‰ëdíçIb°Æ)@h›\‘ïÊ£Q‚,ÜTR›¬ÐŒ-XµÉvM  Äte¯NÔ!6¦ÒmrW^6„ìkAþ †ëjŸ*î±²F©p\Û»móWËüº ×ñ;Êï*¿³üî*°!T’ ƒÝg1›CÞgs&Ö(U²*çtŒå¨P¢BU(¢¨ð šMãXËäé§F©*À×'®S|ÇÊ”N9.ಲó è¿.§œ@kÅóÖþR;¥ªjS9Xñ›KX&LRμ®Nù×—>å¹Kç`N»-kõâ?>^µh!k”ؼ‡ßO^<5KÅêp¢òI@ÌØ™ÍõèÐRWÁÊ'´úpOþñ7mé£=ïaa“t^0ñwñ šœú&~ПrÔ´ÙáíÚ ñ1a¬ûv6DÅõüm¶D4nô0ó­úª¬Ã—Ê~ºqç¶³{7 Àø¬»9¥>ýè£õ:I¹Kî§]ù“:hÊ¥¢‚ä Ø‚g­Ù&šíùšÖ.ýsÕ¶5«¶÷½np‡Ã~%qáüêaÁAzd„…ó¸FÆ|qÉyïW¤X9`—÷zïËã•]3¯V‡]h@¹^^¼þŒ¢«¢Mgèç;ÞòöÞ¯nö½åí½_‘ú½Ûà½_‘2ùäÚÐOÎÑ9á,øÔðžeíݶ工¿Ìš›››} —°é/™XøåwUê°©¨‚3öw¬äÃáÁ5, ðä¾iM®¸þæ‹þüeÆ"´^jl¥†‰'¼´IÉSÞE0‡D)¼û?vŽnPúö Iû¥”Î2Ó“WTñZN` ÍRød~ÁwƒûæV±\ëTqÜO#Âus``{f¿ +ëôuªd¶^ƒ%O™„‡‘IÏÇ‚¨gOŽø'1&ŒC,ÿd°ú¹âÁšôãAœÜçÁ<,ñÇ„"÷_iª×à~´5ŠöÄ“O!‡R3ȧíþ:çu¥ ¼¯HIÀ Èw_ ‚øä÷Áƒ#L¹… ¿‹|žßME•—DMšAkÂÛŠ ØœüðQSS4Ýí¯Û¯_?SRrÚ‡øu¿WЧMˆ{ëš|ÿD –‚Œ¦¦Ì¯äI­ŽÂCƒa¦íj¯ÍfûâÕÑ£4„Ö7°ÄZ™6Í¢h)t6¶¥£±~ûl™?ŽÓ,Êâ6Ãó[f«‡19hãÁ˜ç6Ðx°Æ@Éý1Á¶7Xj?àááᦑÏ=“b í09ׯZ1é×ÂÞÝuDýW([…Ÿ=÷»&Á›ØÉ L œxŸK’¼‡UPü{ŋѠi¡Uá{YA>ÄmܳÆ"MEÊá0óµÅïhF&OQ³&g5áTf.-^·ú]Ø•,Áþe¹ÍÏÞh0†€i$¦víÚ ¸ýNJCdC0–y@i$Ö5ùî¹û(óÅü1Ÿí«õ¡ÉçQÖzåæ=¢/öîÞ®¬KËu~ëÞ#ÔºY# Fz oÚ´ë ¥#ýÝ•½ºxŸª²ý}G2зOþ¦µßFá²ÔdŸ¬²6•· z–(ñÒ42É?(ÆvÀ¯ÁóÇþUÃÑ]¼3ÿ Œø¥ãA¯yöŒ·<ñ`5Jâc‚5%^äËÚ e±X´Ä1ã^ í…¶ :´oÒô©S¿‘ûj­$àƒø=“ ¿k¼0 b`Äï\ó6Ÿ«ÉmTWï‰gíxµòûÅ‘@^²Žü½™²só)Ô@[ÅÐ}ƒúRÓFáôÓkháÊÍôÚ“w‹o©|IüH±MÑ?nºœ¾˜½”Önßûó¨EtÝpùùÔ«[y©Xï;œA“>š)¶9 itd]wé¹tɹr¾çŒË+¼3óÕHï ‘·8«ŒY94ó÷Õta·¶~–\ÌrÊ%2ÅÄĘÿñÄSïàÛ?H6qT“Ç$Œ| Çø]­irõQ×÷ãFÁg¹y¨‰>öÓŸkD_õfÊ@o?sŸ÷áÙÿ{ãn€ '–Æ&K§Øª™DTߨ¦Qt×uSK¬‹#ò'¾ûõ¯â}»¨ðÅ`iÓ®ÃÕ –æ-ßèfíTFƇÓ?~³ûn7÷¹ú¸Q¯Áƒ ׂ7€î±QT°ë(EØÓé´©‰ß=Oæ+ š¥¶M-d9y\ùyd;üŽáêgˆeüaർqŸ• I%þ!gª•Á†«êšûÿÔKÿ÷2‚Z\+kÌÎÌ|%õÕW§Èýººýò«£^yáùÉu•ÿ:Ì·|É÷M& šä¾<_‡›êw¬óo–X؉¿6¸ãA朥ë鎫{SÏέétvM›»œ^ÿìgJJ¸] ¾fû¾£Ô¹°ø¥ã'3éÀÑ“tËUÒê-ûè¯ ;)áÎBÛ÷-qÀÇí{ô¶«PY¼f;rx-¥ º´™UÕöa·\áújTU5XÏÚG4jd~ôÙç’&Ó•îª5í­±‰qOç>Tƒî>Š:Å·×C»P.6j¢]q~g:§ƒËjè?ŸÌ¢}z°·­°–´\ «äE—׉ú_ԎȤﬤ¿¯¢ø;¯.¶Tþùô=ÐÜÖΰ}ÝŽýt$Ýå÷ït8mÓ?ýx*õìò;R,ÿuý`íH½¥ÆyU\É?Ô½M4mÚ‚:å­§U¡W’Sã÷Þ?È€\Èr׋(x훆‹ü?’onC&~y°&Ïn³@xñ|Q±[ÿÁÒø7ÿ;*00àAn,êN~uÌ艮½’ÿ¿øæÛÏt_zê‰I%_å:óôK/³|Þ¼­‹-bY×YÂßBeeòWc 5œŠäGŽ×rá÷M.òXÑHͶTþŽÕl­¨Íf·Ó¼e1ÝU ژơ4WÒø”oiÍ€‹º “¶Õ[÷¹Áo³)P÷ö-èÇ…«„–¨{û–‚ÿ’fÆÅIücmUëf…VjŦݴjË^¡]‡™ö‘wô§ï毃ÇIÝIG2NÓW?/¥=ÐJE……ÐWž/féßþbµkM·ôs)×ydzîßz%-[¿SäÍ»ÿ†KE•.ÍØÁï½:K6ÄšÛÏÀpõÖ½â›;×k/9G˜NÇÏ7WÃrðiñÏŽžd6_èQü«ããG{ìׯ¦ì£¼öi@RS},ýƒ&¶Ê‰Š°P[ädZ0?óµ4!ÁeÑȦ€?˜IÜGºµkA_¢5B¿?tì$m€†¦úÖ Wœ‡û£Ý}áŽ} ]K'3sè®mé–þ½ÜæpK×í Ù‹Ö Í,¿¬• )‡™g˜ÅL±1QbÙsè8-A9Lk·í§%k·Ñt¥ÿÍ]F=;ÅÒ/¢Où †ìÜúxÆ´ãÀ1jÓ¼ 5 ÷ò?«Í.€OdäX)„Íö ï¾¹˜ ›ÝA WlqŸI?yúð¾;Ù4[ö ÷¹úºáS§¯kB&DUF$^m£ÈâȤn¹«ýª9ÝrW‘Å™I› jŸ9€a7Î<3ï²~ÅpÍ3Ã6 ˜Ø,ˆMòxáVúTðºÞ.ÞžÜ@Éé_lZü'‡ð,³Í/nÃL%kçʼ¶QtÓIW Ê)e^[Å× ¸¯³¦Ûèõy¾Cò½âwŒû ¿süî)ª^ ð £Æ‰}x`Õ­]ó3ên¹h ¾xðÈÔ»[;Š=b›ÿ­À9¯s+a±ÑëÃé§èÃïw›¹/,ec/ü˜ØGŠ)ãt6%ÿo¾Ì –ËátÒä¯çQ’l?€Amûئôê`s¾Ø–K¾wå–=dði‡ÁmfN>eÁéÀÑÄZ \2ÐZðçI¬ÚNëw¤‡nº’nºòÔXkÆäÍç}ÕµÛ2¦µ%,Ô ”ìvÛ+ãG%Œ©®ú|,WŒ}ꫵÙÇdûò­vó4;A&z}íѬEkÅ9þÇ}ዟÓÅçt€6µý¹z›4|îtV.}2s]~A'Šƒvõ Þ™?p¾+æ£ï+Ó|˜ÄíÝõk_†¸¥¼'§]­¨9õfG;¿}›tƒ6q||ü¤äÔwat•ŰpÂäÔ“ãGÅ_>mÚ´ÀíÇN¼©kú­8œèÙqññßs•IÉ)¿âÞ—5%ãüŠñ‰ LHNYûúœ¤ã#¯; :M;*á5¾~Ò”)]vGÊ鄟ZŒ\Œ“Æ'ŽœËç ©\í‘«µ’€’@Å% “;¦ÐóY…X‚Ít:‡cêð?bÐÁ³ßQaÚAÚàËÎçØ¿é‰{¯¥1`üWÚ÷t5´3· ¸ð ÿ&qaá?ö)Êͳ¢¬tjݼ1ÛÉ¥‘âÓ¬-1¤¿¸’ÙÓáÓñÔý×S Ùïc 4Z ’XÛŃ٘Ugà´ ŽôìÇáý­Ü²ç°˜á¿ð¥¢L†_Ì^"¶ùߘñ¬}Lã0±pPÖt±v‰É“q šþ1_œTºî0¸¹ÙÙ¯¼<ú¹¤jª²’Å–ßd´6úXEÇa±ï+ì'é§r„Æ”A•¤ó»´¡«ût»‹×n§ÍÐ@±¿Ýú¨Q¸…zum#ÎÓ1VL&\¼²ˆýŒø]ÊDJĦÂ`FXô.0»í† Ý@‰ƒ–xÒ–=‡DŸï×»›8¼f²lBË´ ïû@±Yl·v®2 ¬6q®¤>]ßâùeÞþ‚¶VI"5°ä2Ãcàa "3>[DBp³7È@C†j=ãÂrîà3¢;c0CôHçè&‰;ŽŸ¸ÙéÐ_Äí Ç%Æ?6arÊ„:ºâéG=ÁEnKO¿G×´hòebZ©­Ã©ÿ5õØhiNýi™îx>aøf€%Ü¡] à´/H7÷Óí&›Õ¶hâûïʉëÌFã±|‡c|Çè&K¶¦§÷×tÇ¿qƒ'Xâ*) 4 ÔêLX$€SfÖ½‰Í|xÀÅÄAš5Ž µ+J.‚ç 9«¤S˜±gjåáðÞ &K'0»Î¦|lbµzË ³ùÜp™¼Õ½æh«˜Fî}Ž$&‰5WÛö€kŦ=ò0q4=IžüÈcU½æAì×0µâ ’Ž=úú;þýNá~­öðP©úk£I9–´æ` ÞäÙ7B‘j†Í¹_â{+ˆMÝ$!š[Ãà )/4Kž.·ÄôßÚÿBâ>0ý׿éÏæÐ›ÿ¼Û]¯âˆûÊ^hXûöìä>ͦ­ ܘºÁ$–µR¿¯Ü"Þ ~üŽ–§Ï» ,Ü`­—ÝášÃìrwî?æ} ïWªW ¿«÷`‰MØp b±„PìU;6 §@Ã)Úu2›ÎÏ^L'M1"J^F@LµæaâïA˜Í}wtüˆ£G(ºK7¦=—8쀶jf'ØkõËBö'êÔ^IM]é ­k®* üŠ*T[JuV<ÐÀRþÙúªjicX2p´°;³:Y.;œ³9ÚÀ‹]3é|œ#È­ÝŽôanýi±ëþ+Χð‘ÄÆ>›µ˜Þþr®Ð<›E»«,åä ”î1(€ÜOœ8}JòVŸÖµÝÇØˆ“µr‡o`îÆæoÒ<°²rîÙ©•ˆÊ8ù»¦ÿº\Ln³ jyÀƒx^8*‡=çþÈ}°<ÄQEœ»ŒÛÇ`fÓ®CâV~Oó ìÄQ#YKÊãHöIºùª DÅõyo°ôûª-0ÏuÍ2}{v,[õöš–ÜÚ%':% €\@ fz&Ž@—Oáp´D8f¤LIu|e{óÀu¹ê3Á,Ðó3€Q0±F‰RdD„ØfÅÑð”V©²R¯»÷¿2eJk›Í9U¨!Ñ ôŸÇ&Ä¿Z-BWÝ“k··G]…¶ Ú,äx“ÀÕ#GŽÌeMÖQ L¾3{vÐé]{Û#Q½3tJK¿3åû2”P¦:¬$ $P9 ð·iœÑyam›åñ±âèßqCÎ:|;x¼äÁbƒ}™J"æ6ö¡bOs}Å‹…¿Ñøá·`à{˜*y³Å`Ê›â~\›ÚqÀˆDCc“&6_òtdçÀœ×†}X˜j!ë(ŽYneÖUì „ªæÈfLÜöâ~…­¾RMõ1)¿ä”›îõ½çÍ~rVœÇVqQ’gŸác Ïsj[I@I@I .K€Í¿˜½>a®(ü¿­ß…Ô~UŠ”ªJ¬}É™ÿZ·9•¶ˆi ÉkÊW¢ºª>J`RjêeÀ鉲mš6>¾R‰geYj­$ $ $ào8š‘)¾¯ÕË›NeæÒâuÛEÂXKpQŽïëüyÿè~ùËÊE´2¦äoºëš‹KLÈëÏm©¼q/_]+XìËÎáº9Jc}¡-»Óê­Eñšn¼òüýÎûÙ6h°$…á bø˜'H’ûòÚŠ¬=Á¬«"å¨{ê§8pÂÜüÑñ Õ´Ÿ&üËúÙZÕ*%%ú&ï[IœT“‰“º6 E²Ût’t–D3ÿX-¢Î޼}ÀY—œ€ÏLä­aS º–ŽdœPú ~Ð. Ä~w_w "’EÕVu ö$ÀÉcßÿn!5Ž¥«ût§ˆ°ÚŠä®G‘(˜Cesøìø;û W æ’dž_¤’“¿÷ÝêÙ©µèŸ»Q ùšùË7S‡s^¢o¢¨p mÙsDhmzwoK½»·ãËü†8ðÈ,$›–tQv ÐKax¬UÈ5aÈM4.íË<Îe*Ó96Ÿ«ÀâY–'p’õ©uÖÀÁÜüñ+Ü¥P ™š‘âê‹Dþ“–ÖiRrÚõ¥=ªJJÅK 2PŽ× €œs^%OŸ/XdÓÉ®B4¹"¡1û”{R³èHX"„™ZÇVMé€,I{§G]) ÷JI•c4¶k»æôá¿#©³+ȇ¼¾6×lEõý•ÈCæŠåÄÀ®ïnµÉ’ßÖ}f/ð[6cJõS¯¦¤t±:õgÜ­Ó´gÆÅÇ¥˜wŸ(߯Ë)):túž¯Æ$XÀ9|žôLÞ7é¦A£GØÈÛ5EVu…1ë]¨ïÏ:¹Ý6§öB–‹_fäoZV#OŇ-LàiÈ8¦¦ùõäQm+ ( ”_&älY¨91šèïM»é€CçÖÍÄl<'˜åÄ®ÞF‰w]MËÖïfï÷ßàÊ’ðœË®Ü"Û^ÑëLÔþ#'è—%ëhÓ®C":— ~¾­š5I?y–ÿóÙKˆ#y5mN÷\ß—Ú·¬ÇôÇNÐ×s–‘Á¢˜‚‘ŠùiÖølSÃòKV]Y]¸ô¼ŽB{Ù¶E;ù‘›ˆëâÄ®œx–.èØs8C°À!¸[,ñxÇVå‹dØ!6†úúÿü¼xmßTÔY]mò¥Ü…+·Ò¡c§Ä-¬¸­ÿ…¾”Ó®Uš¥†ð”UýVV§ö0çš´Ðè÷q qT†YäDZ9.1¡5/šÁðìúMϵïïÀC3hƒ‘‡©}çè&šö 2Íeò{*ÉCe:„ºWI †$P€°fë^Z¼f}7%µiÞ„:´t%³äd¬lv·q×âpÄ øXV®k–gâB"Ìó»´&ö—X²fû\ÿˆÙo6e{ù±;¹…87|ÈUbý٬г§'î½Ù(ú jƒöÉ ¯æüåJ!æ@ºoÐe (ÕÆÃ(g2Ù1ƒZ¦Ü|—&”A7%&6Éc-/§KÐ yFaÊ* /nÆ¿fÃå&BK%ƒ†¹ÖÒÆÎýÇè¯õ;ܵ_‡ê¬’@•KàÙ³ƒ4&Ê‚5]½2Ad9YëîO„ëîÎÑ‘+d9à+Ë©kî¯>r?­tŸ#ÊÆ·%”÷åZû"0CËœœü_í6ç<ätžŽOÏ¡7Þ{¯R£=» ¾a2å“|ªµ’€’@ÍHÀ]İ âs9ȃZÃq¾<Ô¹m3a:·dív1[„ žôÛß›©clSºâ‚.Ôy™8¬3S{øD…†˜i6B³é×¹ag…ããxVYæ6›z%NäùÀ —) T¦äüë÷(ù™ôw’÷x%yÜŸÖË6ì¤M»‹Þ[®ìEÜo•-¥Y*[Fê %*•@Öž= ÀXQ¨FG‚ÂB9ÈCõ“®Á­Hwy­¢¶—“?èh×­žÃ_Èê¹ƶS/öœ´l]Ó²Æ&Äõñö¹JJN;£_vœºýn|ÆŠ|¹Y]«$ $P£àD²?œ!¢iunÓ Qà.¡‹Ê™S¦€›ë}úÓb1;Ãç#DÑ Ž}@~X°R˜÷±Ï¤¸;ú s¨‘·÷£Ïf-¡±Éߊö²ßI¸!—W`<àüqájaÈ÷pÈåû] ÅT}•Å­øÄv.lß©(½Ñ{5ÕÖÑ#GîC»ùI)©in#Ìì ÝÒT¶þaÆå#úùÝNÝ91)9e;r&íÏÉ·Mõµ\ݩφŸÔîœãk:õ6µÞ̳¯å¨ë•”ꦂŸÉ3‚žlûÌʦv›w"NöÉy›<‰ARu¥5n]:££ÂéDðS@ÉóI¨m’‡èÿZ˜Å“L·ôSs¾>#¥YòUbêz%JH¾JÏ»o×´äÄÄ¡Ùîý*Þ@ΦñÞE"çRü´iÓ„†jÌI%8¸ ñ‰ñÁr›×î>7.!þAÏscþÄþ5òظÄl.×!Ä1|hl“í—7Çy9ƒžOHØŠîTáãGÅ÷<ãµ£$ $ $ œ‡ÆG22‰Ó3NgQ$‚'°ëŠ äœS͈iåæ=4gézwe1ÈŸsïu}‘ã©z5Yî Õ†’€`-,ûrX&ÎUvçÀ>"Y²E5øËXjð]@  ¦$Ày•çÈŸTÓòÍz§¦êö¬§T¿$Ï +°¤¸'+p›ºEI@I@I D phãÚ oü7òðüºlƒ›?ŽÞwÏu#š˜Jn¡¨ ¿’kc¿ým…;ò²2 ·È%åWŒÖfê,XÚw8C8€fœÎ¦SPÏ[ ™\VÄuDú•d“sVp$“FêÑ¡¥È˜^É"ëÌíñã_¾Ðá°_/ÞÖÈ Ô‘Ùj.a¥TgëÎf&LH^wbÿ©¬i#ÆM¨`iê6%ª•~;³þJÝk4ç¤NxÁý°jkR¥) Ô üµ~ÇŽñlâîk/.1gTÝi™â´>K`Ö¢µÂ\U¶qðåç!zd´ÜUk%P§À’É´8š;{2@âpaÁf 5SÔ‹ NfÐ>ØngååÓŒßWSDXõ¿°+ ¸¨@<“PH{ê©7Í™æìÇL&ã“v§½¹†„>a!fgdxˆfðoÿ;8ç#¦6ŒÝÅ#‰ iÑ(ÌÒ¢=Õ”:.|›Í‰ˆfzVn¾ï×Ä‘ã'v8œoÅƾûÒKì—¦HI aI`1B™ÿ¾r‹»Ñ­bÓ]×öæLîƒjCIÀÏ$À‰›9‰$6Y=¯“+7™<¦Ö¾I Î Œ;г—Ò $«ë‚°¤×÷íA]ÛÆT»C§oâ¬«ó ¬´eÏQZ½u?ý°p•øqç°­¬m2 ÊPMŒø =;îº÷íú&J?_7`1^ï¡£×Ý£ë¥7v¢?~p„*jDqm]ôæîÓ„ÎÙ‹ï rž› ¶#Qlnq%ýî­ýtMçQË…7j8©ëz}çI±>È[ùoï=Cµù‚s^¿%–ñƒý/äß Ûü>-­¬ë¡M‚è·oíãm×YQ gLè^zï!’]ç#€àœ·n7´fæ&vb#ÿ`pÖœ(eqâÎ'7-UDI¤æ^‰€1—Ò9S?Ñ·pÔH·,ò¬½–,¡cÓ»w÷Ÿ§¹Yéœô vÂjTÇ––œ€pÛ~4—?X‚0G ½ÞŠF˜Ýñu}òk߸?((ä'L” ÏmYëç«÷¿ðÆÀÚÄTeÆaK#Vûx xîðüá9ä$Ê?æ›ÿxWßE˜¼æ.¹®"ç®]§ô”8JbÎ /ï .mi9 H uBLÄ  `ûq6a;™[LËçf|¶¾ öAŽ$˜Öö.š?=ƒªj›è?_ÛE© ÑÂïö_ÿ÷±“hÓÁsF¡6γëÄJIˆ¢úæ6Ê¿Ž8$DË¥g®Ñò9Yt¹¨ÒÔQĶ?~ÈäŽIØÚÅ3h'¶Ÿ/¸ŽÕ”[ÂîíÄ~£45=Y¬“? ~†M¦¥Lc“ÿ'6,!$ÍU¢ðV9ÐÙÖݧx@˜K)*\CO³É(B…+q^û@t»×x¦wOn\䜫g¥·ä¸(z‡ g;ôAÃÂäå‚64{ö²è¨˜˜ß§'Ç>ûð*Ÿî”±)“øÛ)Qø"xÙÄöÒÆO¢wìµß_Ä×[뜑L«æO£Ùœ'6*œýcï3˜¼!ßц¥³82\0](¸!¦[õͦKyhõZ8#“ƒÍ¢kìk$%…‰×gZI³¦¤ñ1×y>Ö-žI÷,ŸM“Rĺ»æfûÌ|rÏå—s´×xš8ØéZY5­]4Íœ$FÑeùÐbå•Þ 8+kš87“†Î_+—›iIÎdÚ̉9ùûbZâ ””™“Séqµ ¿_% oE ©­ƒ^å¾rGÒ&’ˆÒø k¯%;÷Žyå[zD½kjí¤GÖÌS%;ï9F„];Zxïй|êîé!Dôb„öˆ¡¹w¯ý*[M¤°R€¯j”pÛZÚ;Mf%lï>5= «•(|<‡üàÄÊ©ió¦ý _žU¯ü†ø¸^\aó\Bš jçè«R2SâÅ"¾-§rKÄTYÛ(7›¾Ýaœ¸µ½ÓØ™ÃÆÌÔ8Ó>0Q†Ÿœ”€Ö A0]*¬ sœçhżl¹‹iŽý"y1S›S3N¯7™“ÓiѬɦã2úë-Wì8r‰.ÝiöÔtzt2a2¢¼ôíøL&…™?ˆRBŒ×gRñJ<‡«”×}è`~ÇájéÛc^sîö¼ øÇSlJÑŹþ¨_/ô_‚öv–Èò§ Œ|~vVºÁ}”ÌïJiÕ€-ÿä Ò]™’šã£–} )•^þð(ýû=¸zÅbÔþMßûÍ;Bþ‰Mì¬ÉÚ…38 H*ýèÞ§ÿ~}/md³>©ùYÊærE b^viX;e) Aè4þÃ/¶R›ìEs’v)ˆ^‡ÀßùÕ›ôÿÚÊ©5ÈöA_ãýCçEr{yÌö¡zhõ|ùWÍ^‰Lî^ã(È7 ÕÊ“ÜÞåsã••öñJáCç5"µJyeU<ªC"’=•Ûyâ*k¦Š/ÚÇï^0¨ˆŸýy·ÈÕôùÍ+„ÆeÐF‹?zÖÀ”TÖq=ìó39r¡˜ÃïN¦IŒ¦ Åú[ˆt69 vþ-A>*à‰Dl÷$./ÊÁR$µJ¡qIÉ8‚‹y”äƒnÔeØÜ+Qø:ü\bÔݽ‰¯åOsgHOJÆÏ>²Æt5ðQ’c£èÅïδm¨…ûWÎ¥GÖ/4‡ýÌËÄ|›>óà úç4~“°Ö(0ÏûÍw?#ÿŠùJöŸÂ™ÞßÿÕ}Âä.(pð·s2‡û†¶ QñYÃ-ä§ó” JïÕ²Óéû.R³øŸþyMçõ­_ÀvÛÍ"7l·.uɬLºkîTQø§¼Ï¹:ºzÄúÛ(ÉÑ;ûÎSqåm ‘kçþ•³ÉŸ?/o?Á¡XõôÒ¶£â¥þÝgîö<Ã]÷PÛ¢϶N-i»»ÙoIG¡!Æ@•ðA%L𥀠À¤˜èp;?›^p5ýU¨æ6"ebò€C³\7š98§¶›àülÀÙ }%çÂGÇ.Ó|NÞ‹‘cG¥›Û{9‡+†©5ÙÏ¡‡¡õËš88Œ°µ}Õ:#ìâ»±€Â6 ÿx†½>Ô&×Q‰gB ­RûQx“À·éí½gù}0àKº‚C›#Ÿ…€7#€ ¯³éBçCð-„#:"¼½×#àedÉ 5¡6ÑpD8„Íœ’JWŠ«ÈRI5=¼z.Û.¥VÎßð“˜ûW̦‰FtÛ¡Ktúj9-˜™A“Òâ©’ÉÑÓQ˜Æh/ÂÎiôÈÚ¹ µ†‰ÓÎ1QhÁÞer3º{Öåpn£*ªëODŠë€@ZR 烘B:~a¿öñi>_ ûeep§p¢ v¨r?@†;Øa”?À³µ«‹º9 +È>2Iß(‹rÅîèhá†ä?ÿˆÐ  Ÿ'KH'!ðí8=#µ²›ÂÔvûø¸?ʦ-Mýç?lãN~}ú¦âqÞüþ=úêSÙ¿ œIú91ŠàG™) ô…G׈|&ò€G/¢AYJ(G¾úå7?m¹Úåÿ1À°ýðEÑÇ’,!oÊO^Þ.êàÏ# ‘avø3®äAëM&±û¿m‡ùZ>%0³¼€½œøzõ‚éŠ,Y3Ì<ŸþäÁ»à™Å³ë£.ÃTYmr#a<¨ƒŽœ7‰ŽM¼µç4ÝèO¬‹º­fs¾Õ §{S5U]w ~$SF˜pˆ|ëT‚û; r٠lj«…&h–¤6Æ‘â{õ}4''Þ=pC€Îãd#w6{Ù®3aP±œ!eNÖ±~:û']áü ëO΢µÜñ4ÈÍáIWÍ›JÝœ³h&Tï9uýšn ¢ÕÀfW_|lµ gÙ‰\Nõ s­^-®­±µƒ’9¹^!›ù-ž5‰sGD‹ýà³”g ù8Ôyà/`¯€!žžµ g`íamz¸˜0)ªdï5zÓqu-¦êØc‚‡{óß[÷0±ícÛû»)™s“”pŽ­;O±†P'ò” ¿Èá ô©ûWð}4žùN`z‰Ü%/pD$ªûÞçy®sÀ‰è~B.+‡BåB~þçØÁ:‡r¹C‘yœ'çÏ=¶VhvK«êy¤ø4͘<ý§X­ò¼Ìþ†u¢dõµÒ&¸}ÊA<»ò9°{¶©µ“BÀõ Yç›L”ÌÚ¯]4ƒVZ GîúÚ¨3(lG ‰û‹Æ¨wPà}”áh3ù›§Ä}x YÂ%K3û ;üâ;é[Ï< Èäþ3ùVë‹È`ãQøý!ŸYÌådy<¢®ÙëÐò€ÇëlÆ\Ãß^)›–ͦ%)W‰BÀ›À@éÖݧي¤GTþOnZJ“R[Gyó5Œ¥ºy Y¨²óî̯-ü‹¶î:˃i wâ0Êd.9œ(ïlÞuBN¤%l‡N.¢ñad=)6‚ý—:ÅH~0ûü ±ÂÉ&r=¬¡9ÌáÉ¥À'~FG™Ý·"‡³W ³?¹š¤ˆ°PèàGˆ²wƒ}/`ÆIˆŽ¦peìtÉe`ô{¨óÈòì™_h“Ði”XÛSŽ Ž‘-ÌÇÄu )‘bžY^®³eŽìôˆbh)r4¢Å3'Ó‰Üñ"…6Jšàa^®ðØÆaržÍçìÞË…Ö Ûœ% $x¤rür1å3AY’òÈ:£Éž¿“\Oh¾®2y‚æ'9>RL }0!´E»´ûD®(ùÃ`~—dFXzX›ü؃‹¸C4EìƒÑ9) “øÿwŸ¾W|@¢LœS rºÀùZfN6š¼ÊwÆPõ½÷®9òðñ87vÇãõ‰k¾Áæ¹yl6ŽX’mÞ—/ߺºÆ6þVëM—΃4b’+Ñ€!ÁÁA<¨Fñü=ÎáA(¤ÙPâ]ŒÅvj a<·Ù2¥£Ú,è7â÷Þ¾³ªZÌ ë¼Š,ázщw¦LçŒóplŸÃ2éÓa^>:K÷Ý•ÃfB×hû‘\AZÖ.Ìdiˆ8“wƒ^øGÃãˆ#ÈÍ´vÑ4‘ÃiÿÙVãO¾J²¼{¹œ_¦KÅ•ì•6È×é.î<¾¹÷ýàŠÎØêùÙÕÄ'Dî.ü€óÀÇÿ𹇆=<Ÿ=sI’œ³=u±8fLuºZÛ´ŠÑlviÀ4¤ÙRÕÒY83“Mã. mй ¶IGP˜Ç½Á#¬ÿóÞAúá_?jwsk¦›™f#]RZøóáÜRd~©ˆpc$¿n„®çŽLQE k†êèܵërWªm 6¦•Vþö“÷3<øÿýâ•< r’¾¸eiÏÙYF,ÓŠþ…2NR ÉèNˆ0ØæI6gr' &z0¬®kA؆;ZßþÓÅ™|nÇâµÙkÂû_ǃxåÀ¹|‘¶ßÇ@Ž˜ÉßKâ=f®¦ÅRšØò“5ȽlªÞ×Kº@ä‚ÿ&ßÂLX‰ûOíÔº²í¢[ ÿyˆj§Örý:¯#KθdD•“@ÿúü#ò¯0ÿùÙßl1ýÇÂzæ€ £Ô0Bä:Fð¿öÔ:Bl{¬‡lX2ƒÖ.˜&ˆ—yîl[Êš©ÅìLŽhw–Ñü˜¦~n-ñì#kçqD¾.ÅPçá΃smæ=>}‰æækæYåGsQéIq"j!‚˜‡½¾ZZ%‚/LHŒÅMHŒ¥þp!¿œJ9‰ò¦'î¾ã4©¼ïç9Áäw~õ!ȃ­'#äFJ•™Ý¿\­ëpbm;Ê…yâ:v²~Œm°Íe4D#ÅÐXt™ËP!‹Sû} 1P‘Á7˜ËBƒ'eጠÚÉaË ®ß¤©é)ôíÏÍ„3€{’CÕW«æ oGÏ< EBt­fnûð%Œç4ˆ´‰ìñ.ÍÇ7)@Þ;pŽ å5‘47§?ÎxÇÇׯÚéÈ(«v:2FÎÜÃkLžFÓAr&æeÁŒN%óõøˆ˜ûõÀÊ’(ÉýAÌ’(Ém˜[#Jr;:• JR†;ÜÇѹà>&¿Îȉ$%¼?ì¼üoë¦jÌð ûò\eßäW€™ÛÎã¹´Š#Ø™“0DÅ;z©ˆÛO ˆ‚‡s bòUpP{ô¢ÑlÄÊVÉâüb%LÀnqÎ(˜Ïå•UÙzèˆû!lþé¼R*d2Í 49æ&sÃ?#˜d@t™5¹fڭᎱŒ ¥}§óZ©}œc &¤ ÙÁ÷n=›˜Æ×Ž|1R©¯,cŒÏÇä³w€ýéz…ÿ´1›ÙÈ\êľB’Èc8¯¤ŠvŸ¸b ›|Lˆ"g«,eGèÎSô//mcŸ; ›‡N§]ǯØzø°û=±q ýå£ãôË×÷ˆ6‰A‰çY+fK€D郀LÂLFúL {ÂþÈÿòæî3töZ-dóÄl6Õ•›¶[O¿|m·¨’6Ã'i3‡jª¾ã5À»>š?³XVâ…à=/; ˆùá‘ËìÃBq¬M²ë…µ÷l•BØ)‰—Yýû‹ðדß59ƒO=[ñsvÕNí¿—ªÚ­GÚß‹³õ j?…À8DQÙ¤„phj{vó_zÜhV‡èzÖ`M@^üþçm ôÛŸ{;ýˆljžm m¿ýÞg•âòŸ¹¾ŽG2<´­–çüÊSLÇ#À‚ ²€•0c3ßO¾üäa ×ÅÂÙìUZ™ïg*àt=Ô6ìgyNkë •[17[HsL aú÷?ïäüU+8ØËA–à Á1¦}¸úâÁî:ý˜>plã„àÐ@+í’c·z¬·SøÇz*j§ŽµMˣǕfi牫"¡,@ÐðèPzR ;nÏ¥dŽ€¥D!àLÌGá3 D! P¸Óh=™Ñr|„ü‡Ï¢M*qc*Ž4Êi2'$ðÀ§ð5wjtüã¢O·S#ЉX·±O_1§ÈØÃaõG"ÀÈ89-A¤ÔîF¡M¬Y8šÙÿÕÖ”Õ7ÚmªŽ±á÷7ÿ~DZ²5š£‹ýý§6Ð£ëæ±íq'í=?V.M]‡!`¹MÆ‚üyû±þð¹cájÔ5(Æ&ÆNh/vÐ ¼¦¶N ðóWy”œt»‘ß&À?€8œx/[ o%£GÀÚéå¢ úãö£t&¯œÓHdÒôÌ4C]Ñ”ô$º{ñ¬¡6{ÍzÕN{+Æ•f ÐaTI91!K÷ö£WHË‘mʪëélÞuZ6{ŠÈC1sRŠÐ:],¬¤ýìÌÙÚ®egïXÚr÷Š6Fªml¥m‡/Sum“pü¼ï®š99•êÛè݃8‘g gFgb¶€2S⨅G/v¹B¥ÕuÉÑ¿æd¥ÑÆ¥3‡\ïÜ[­Js'°k—2VÌð0âf0ØçÔü?龜W/ ÔÄ ‹š+.@Ï):ñˆØ¦íî¦6ìàÏ'ÇŠ$ÄFÒŒÌT‘ ÛSDQ31Ø œ7´KJF‡€7´S˜§"á:¦lNB>'k"å—ߤ”„ÚÄ>i°:BòrDE=—_."£Þ¿b‡ß¦¯²fª´ê6í:‘+rÞËëS¹_ÙÂíâ'KG2w’Ç?»y5E…k(ÿúM¶R RyŒ-ûöVíÔ>ܬ5îÈ’9•uͽIŒ&ýqû ¤-ëç³M¸NdüFùï¸@¡¡ˆ¬µŠN])£m‡.Ò×?±Ÿ/¢†Övúü敜³¡ù¼¡Ö‹êÇ'И9RÍnã¾±ë!/Òþ˜ Á-L>>–KKx aÏÉ+"‰ëä´DÓ};r¡.ó C?#Ø '¶}wÿYzîÑu"T7>NùœøvˆÅTxýUó‡ 9J*j)›Gúî^<“Ÿ•SyjA! „À×÷“¥n~N`£Ìît2?ÏCˆ´Z'sK8ÇSÕˆ§ ¹ØF2YJŽæM(·×CZà‰ÜsÀx+=žl§–µÅ :|‹ÊoÞ›:8(J›=§®PöÄæ1‹®–U‹íÊÚF1è·ãÈ%êd²¢üô=KEr'€×p߯žµŽR–r¾Áã¹Å"„?œß¬k¢«¥Õr³Kçª:^¯"K°ñÄ„\Ý.òóèàÄžoì>K•uÜ ;„f삎àãwçÐüiÅÿm‡. §Ø'7,ÿÃØ±ó·£·¨_ÒM­ô…GVÒ4‘‚‡$ë³.§ÞÑÌLzéý£ÔÔÒ!>XÍüñÀqó§gÈC†\oÚÁ x±ðuâ›)qÆ\‰k0ωÔÉ$a¬È¹k×éñ‹yô¬‹^xyýç?|RhfAˆÊØáy)¦‹<.¬UíåÑ×%9“Y;/4ºòoát2üćå¦-ü¯¾¹óÁœ¦õLîš—M[™˜A“»h椱¡º…€Ë€¦Eš7éyÀÑð<Ñ™G"ç®nÍãïÜ}wÍ~SHŒ=œ¬^0¿¶ûwxš¢Hœ%æê{:ÜݼMbBíÉvšÍƒà‰ü½BŠˆ@Ö¾ž¾R**ŠgÙ4/˜Ë9@ |&Mˆ§‚ò[""÷+jľ™œ(Ú£7vŸæo Q›„ ²=VÜ¢3WËľs³3hJz²ÛÈNªÚ©€Þá¯!K²aáŠÂYÅYQ×âðÅ ULV/˜Æ&s)ͪQs™ÁæwRO#eR&ô/c´LJzR¬\s¨eÑ8ßÚ{Þ´Á$n±ÉÞ†%3HËŒWwž¦Ô„h&f …ißPëM8af„Aw’#sÜpUD?q‘FÇQü…6q¬LárüR1¾ÕPzrŒ CݽˆfLJƒ nˆ}ÐÎA ) ¼o,UñÈÜpÄ×'6.Z§K\N1;¨+²4bj›B`iÞd숲¿¤´/0]ªih¡Ú¦VšõÉ Âl.·¤R<ÇKx¤=B"Oðˆ{5´ãkª¹Ùé´ûäU6cªµjÞ„+Å7v.›L­Zͦ†zºPPΓñ3€„k—d'x+=ÞÐNyø˜Z™m?z™Êy°šBHÖÄdZ9?›â¢"Ä@7Ö²Ÿš5yäV}“µÍTÃÖJR0˜ìf+ ÕN%úŽÍ½†,™_FTXµ³¦> Δp~I?µqÑE < Ñá”[< .½Í£Þ®Ÿ­»ÍêVø#I‘Žý_d¼4޶g)O0AZ5o*½³ÿ½¾ë }ç™{E”"kë-µ÷?pl×j)1l01´·´’žÜ¸DŒ¬—TÍ“ŠÙ±µÑít¢âV=MaßF|ókZIϾ?­ünyFàP æMûÏä3ªfQ0Ó“røBp˜Çï˜ÌÔ¹É-s *±vË ÇØI$v4óT;µ)Ì#BCE…KÈ]³³íÖȃæ1l9Êßµ ñ-Ôq?oż, éä ‰”o}€pPAnú£Ú©s€ö*Í^š˜’¸3Æ‘èp°O |‘^3O¢íG.‹ˆwŸºo…a{jÓbzŸƒ7üÇ«{D”½Oß¿”Íëbè3÷/£w8ÈÃO_Ù-ªŽéu¿Økøš*XÕÛÇ΄‘tÿÊÙbûPëuÝ8gp Ei‚ÄGªg‰µ³Î¡Ê¹8vXF§ÒÀÄ~_—élf÷ò‡GùÑH®žÇÑ#GÌ!ò³~ñ úû‡ÄóñÝgd_ÁúÝÛû…C7B¶ÂßI‰B@!àLQ%\¾1×oÖÓÞSylžÔlº¨u싈ˆ±p<ïer$¿CÖÝHæM·ØÌÒÙeÙG &w‹µz»»¾|>O·SkØÁïä•Z>gª0Ń-¢åI)bÂ?µš_{zGëšÑ^ ûÙ7ïùÇ× â÷>GCFTdoÕN¿î» QgùÒDÇ £ÎÓÓbébYP…"˜3äþ³ “5APØÁ|;Læ0iÙY5” œ¹Ìž:0uðËZ¦À@£¢õýÖgîŽïØ?´?2Úc5“–ƒ-HÂ…íC­Ç6GªäBÖl¥E\ýyÔøg‰¹£çPÇ[G žÍ8¥(4“Eõeyö‘5¦êC[†BDzñûŸËCýl^·î[9OÁ>ðozxí|QÚ¡”•l–ƒIÊg\!Õ\! ð íÏ’¹` qÅÜ,´QÇ–ÌšL›Ø|n(ɼI%û 9µÞ~÷Öþ!w;v©ˆ S©ÐZú¤!HØŸ8ç LÒ»9€fe&Rkp¶¾$Ÿ§qµ$Jæõ g›VI”Ì׃$I¢4x½uŽjN Ì÷·wíûÑ/8À&D‰PåÁ,1ÞJ\‡@|t¤©ð³ÑUÓJ^Di4—d$êò˜;˜%¹^Í ûðæä¨£Y2ü–bXË>7{°ÕHCKMHŒÈ`Æ S_o6o2¿CÞŒ»y=½eÙðá±$JæøiÙWÉò:`V.‰’ù¾Þ²lY_o©—/ÔÃkzËRËŽ<òiBCh^f,!ñëÛû"Ëù¨ÞRÇ·÷øMë£`&žÁLÞ‚ƒAìa’˜{K}ÇZ=ŽTŠÔ0Éÿj®P(\:D“+Î`_™%•·9­Fýõ–u-õŃ :Ÿ]øz|ó3÷ÓL6÷Eèq˜7¥1‚yrÙÄD„:ÆÓ$Ξ®‡/ž_bgœûâøN%Ö¾Scï«©u‡óë)|̸X9·zDÀÂh3:õ!¬âœ˜Mõ-”‹„v; ì,º˜G¢ÕY-D­š8¥Ü’jJ ÑS,Gï ቉p¾ÀYF ™Ÿ¼G˜+q›RDÃA^"LÑ*"¡ªC /FtKÓ#Y]Œ¸¿öñ Ó;ëÍÃ}qŠL0o¨=d(ó¦î$(Id$Rl¦ˆ¨nZŠâQ{½^/:¾èü¢S5Þxchð (¥pîäB£¢-–a‚‡hx–Z%`Æ÷‚á34ÚñÞþk~ÏÆ¤’$„C;@­L’àTÏɃ‘' Ç»àÙƒŸrˆ}pø>ŸOŸºÿ.š•~Ñv%V¸_W9ÀËë;³©›’K(Ks™ç…ìgLøëÊó{{Ù=†PªÕO§ªîyôѱ:v±€žØ¸Thæ¶x7¸Ã5ÕÛ‘Rõ³äB˜ÑÙ¼òQþ¥-ëéÞ?ä‘o2:¹9S&ÐQ3—ËrDÞ;‘P¢ 5 §S€Ÿ?½{àìH»Š|ȯYRY7â¾jßDÀd Èôñ$ˆÏÙ¶‰ô%Å»9AêSðçÈÎHáU"IQˆ!˜ áF„þ«Ðš° "»u±v©»»›5Oz1 ŽNL© ã4É~D´^І€("á£Ä¦wÐ( 3<“VéÎÀ¸Ü!õ«¿us+Ã'ïîÙ˜´ ´=›söôôÐÁó´óDsÞ©äø(Ö¸ N8“®h¦—?ìÉ·×/‚6šdã3ïÊû5Þp/׋Á#{Ú žpœ'¾ÅH “jK‰d³t$W¢°_´e7±Ï”ô$ÂûV‘%›!ó¹ÝñæZ tÂA”´çøÇîɘšuÏÖ]§¢¾ûìÃþ–!ÁAÐù‡à¥küo )Â~7Z&KÚ~²íLªÐöÄËYTÒ?hŠ`ZgÔ*£Âäfxð[Âó;lŽæ‚ĵ|ú8 ^Ó‘ÞYÂ=½’÷Í|wŸ\–oën&JÎ^£Ý§ò…¿\Ô™o*ɤøhjds„÷\-£´hKöt¤†9•Ø$ï×¾Óyô>¥´À>Î1kåï^2‹0è$Ÿ}WܯñŠ÷Xºî‰ÉñÂ$¾‰Fç‡;ÌJ„톤r äFÂs¹ûäUnK´xædzsÏiñü‰·½¹÷ ¨˜R%Z9?[äKÊ/¿ÅZžBºkNò±ð…›¿qDËú‹ffRIE-Ÿ;U´MäCèûô¤8œšÃý‰`úêS¨´ê6í:‘;NQ»—í²ô ¥D ÝmíŽzÅíÆ¯½²ý˜á¹-kuY%ÀG^,3aÂ2ò…i4‚(‰àÜù…¶ ¯?Û²$KcÙ"¡"1b =Æ(2ðAxp*œI%HÒp£ËÀþ&ûÑ^:ÿóÆÆº6.d÷jÌh–@¢A”@®s‹*hÏéA”b#Žêgd@(W}ѶC)9.ŠædOÚޑݸ_WŠ+ùÍíœ(ž@Ì–!ÁûÚy% KøŸ’žH¿~c/Årp—G×.2‘¥0þž¼üÁQšÏhΜDqxÕ|&–ÛÜ,‰Ê¿Åß•3WËdêrÑ A²`Æ»uç)jjL¬BÙÏ$ìÛr>Å%Lš’9]HµŽ&ZK/²V{YÎaEòâ» ÙyYE¤ÑVÎdi›Þ•1‰ÂµÀ¬²†ÉÕÉË%TZ]G_|l­X7“ÜNL‰§?í8JKÙgz?0Ù3?—ØQýŒkÂ5&ù°@™’–D¡MÙS˜”µ¾£/Éú¸_Z­ŽÞØ}R˜ÞA£¤dô·¶ÎDÚ~ôe¦Ä‘_tÿû‚ß–ÚåÑ—®ŽðfLßñ 1:Þ§7š¨Wï†æêâ€7]uÍÉZ HEMéy0¼ Q™@IDATŽîˆ¼ˆg4·¤’r¦¦Qt¤F,Ïã“U ¦SCK;?»§ÄqUuMb¾®©•@P E¬­’Dé¹G×ñ`^ i¾š÷Ç·» O&I K3ye✓Òy$¿\ìsãV½ 9Ð(¡=C«²ôÖž3\÷Pq~Ò¸û6¤7o·ˆõ™L”â£"è¯X!¾¡µÆõØ(Ï%væ>è@K¬‡ÙUm‰qn{;¢8·¬>|¾µš· mu.·ùÌÔAØ[;µ”È}*<+Þ$ª:ïn¸‹,áM8ìh@”0¬„7ZÈ÷ßÞŶa›o54>qïrk>Lx àŸ#&A’ø¥ S/qÈx!K¸V£)”ÑŸ „)€?ÀH,3‰2n¤´7Lï Qª,-úó®7þó;ÜÜÜ#Ü«‘¿®¼“7 ÚÌ3᣷c‹©ÀG š%£G¸ÅF…QmC+8wî[1×iy¿P.‚9ÀGI™Þþáà67d;ëü‚h÷–ÍKäûïS%ck÷5„¿­½0^âY£Ä šStðè8ˆDÇßXK¹Ä9ý6¯YH‘á!tðl¾Ø÷2kƒÍ%9.’GÜ›(!&Šjø}‘ÄÚhó²þwÛ!Óî+æeñ{9š|!9>†ÊªjMÛôü‡4·u2‰Š¯&Sè°Br‹*鮹YÔÉïv5s²TËi’c#…I^BL8û”Ðds®ÆW?>!Ž7ÿ‘ç2_gm¹ë³+)Öp—ÛÔüN¬áek;½³4÷­¹Å…´5H0÷µ¼YT;uÞÝq×–dIj–‹ÄxÛØöö®†ÚššÅkÖòÙͪzÃ’œ)~s²Ò„¹.>öpX†ö@ÐÉ’öÅòXù²Á\NÀF|ë$È£„ðàˆz‡`}z}k?{pïÞ÷“¹fÉçADÛ©†V©ƒó&+ªæ*˜ƒlöÍÌ\Ž\(â`Ü g…gÓQ‘÷ëû" ê æà¢À8ž/  å³'‰÷&Rœu¿«:Ú]hؼ­×Ð#¢Ëé³GÇY»P𣠇­rÑé8½R!HRe~L‘f³™Ü+ç1ºÅDg°Ùù¾r9‰IÕs®e¨–Š9—¢¥Àïé©MK…Ôí&ø5!p+Ñ•Ò*úÚ¢ ŸÊò˜“¹%ôÀªyLÖZx Ì˜b ª®QøG}á‘5¢-üOlŒÖ÷ú(ÊÌÊÖcÕ~C#`k;º×oE“¯ˆj§Î½Sî"K¨5†‡ÐÒðÆÂ›:~ô°DÄË'^(ºt¡ø®{XßÛ«_Ãú(îð"5!†˜èp¿Ð  «C¡–ÄÈò?—?f„È\,ÿc›V§34·tp–±ñc߮֊’½'v~´½££µžwÁP ²¦âžàÞŒ %´¡UâáZ&ŠåÕ·ÅGQš”8Žì¹a–\³Ø)[j,ì-YÞ/”×ÖÙCÓ8<¸ÇHçHy绲Ä}šÁ×N\EÇsYï.A–aDe ]""\%üž|gÿY1ø&¿£GÌHÌ0I äÁ¹ËìjM~ùún±ç·V–µc.\§ël2'÷Ç>?yy»i×v&Q/pD¼k¤ 6býOÿô‘i?”q}Ç1ñ¿àú-N`_3¨LlØË$ä; –)ós‰Cü ¥Æc3SbVCì¦VÛ€€=íÔ†bݾK#%Y4=“óÝ…’Ž}è­ ¸»Rª:qw’%h*ðV‚v  DIöö‘êêêб– o½=Óæ,˜”9mÆ MDD\ˆF8ô[žPb&›z½®»§KÛÒÕÙ^_Q\xµàÒù"ÞÄÁ@”šû—a‚‡{3†LðzERc˜à•r'Ü¡pÇŠ¬š?Mtn³O€»ÅR˜(¿ü&Mf_h{ñá³WŒd©W”‡„³È£¤Äq€#ðDûGd°PúÌfÌŽÞ/Çk¦Jp¦(^A¢4ìÇÊ_[äLŽ,Éϱ9Y±VÇ´¤XÚÄÑ0ëÙä­´jø¼2#•eY¾-û›%Ëã­ýªÌÑ–#ËŽ¡ìk•Äok¹MÍmG@bǶ1£l§¶ŸÃ]{q ùÚÓ›Ä3H‘žÕN{ÜI–Pssí’ìY™“(h64<…]¹x'ô˜@¬„ö‰çò^T2 À¼1Áø‚I³;ô²AšÆ”V‰¯G˜gbÄA@¯¥½K$˜Î Ç9K¾Â!DcØöÒÆÑrŠ9YÂáŽô†Ï™-Éíð¡AÒ<˜·x‚,Gäö‚pÆu¡cf¯àx”ƒòBý:=–pvÚÊïÑ”…_—¡ïn¡–º+”ä‡ÔÞXl×¥…ÅLbÅPjk(°ëxGB¦êíÏž‡ÐÇï—£õRÇ»S”¿”üƒH©˜'…Pu[¸`Í ¹êv}pè¼M•‚ÿÑ[ûβß>'ÎDØó…(bg®íî¡S’®’˜;‰ñQŠÄ ø¦ºw÷Ÿ3 ì1Iéå&¹½Úæéh#žÕNÜM–pè¼ËÖ„N½ü7/|f@–üfz¨Ÿ4ÕSD‰Á…HlA”`Z̯ °ÂÔÙ?{lǽ6DpBç!æ;xòçNƒ;&* opÞäú˜EˆàMÌpâKÉíàû‚°©ÀY&„îÚ†Û&ïÊ ñs¿¦Ì¼nÚö:¿ý³7ƒ¦.ý;ÊZö º´óyó]l^ÎZú÷ÔÙ|Ýcd  õoáö/žAÔ ÷ËfÔŽnGƒÆ?Æ”ðS›Du½ÔØÒAÉÁ2À Òo`@ÉÁ Fg>+Î[Ïí¼3޾$ä¼olé¤PVÓÍÊL4aìÈ Ñèk1vް§zûÕƒH{ZT;uÍðY’xtÎÍ—ÑaGç]DÉã¹þÀsK²¤Hƒ2ŒSˆÄV’%<Å’â«â„ÿX/ý”ä±¼Êwf˜ðQG€DÃÓéXóa,7 "J!צlÎ2‡žA–É f,ÈUGiDsâîC&·Kˆ‰¤{9é]*G‹jiï¤Cç L™Â¹ É£Â5”ý&:W Âäºã2gOB÷ ¼9F G+æ÷ åŠf9ÚRœ·¿¡¯‡Úê ÄM3Vÿƒ#IßÓF¦?FS—|BÓ©¹öåüu¶\çáeZûÌ1ºøñ_Óôß§ðØÉTyõuJ¶™z{:(mÖ“”øÿQ]ù~çUÔÆ’ùGûÇs S-HÌí¹_6žVíæäˆ=È’ˆ+òìq*‰@J 7Ðõ6&Lì_‘À┌$åÖñ÷dqv"›s‡|³Ä|t¥ï½%fª:¿¨vê|LQ¢'È΋N9:ñæz'tÞ¡Q2'JÒ· ½°Ñ÷Äø q(ÀUNÀÙœ0g$Ì1aÛ˜Ñ(ñµ‘f]FÂÄ—\ LëD:Gw°UDsB®¢Öù*«ø­%·ÃèÛÓ÷,h׉+ü‘¢zþhKYʉ狼c÷Þ5‡nr8]$Ês—HR:’yáHõ‘÷ åy“D%Íå¦Ã&‚ºNŠIYHsïùoªÊ‹n¦Él®·hóŸéØ«wóÃf Md:-zèOÔtëû1µ7—RæÜg©îÆ~ª¾ö6u4{îÒWhð<`rô~yîBÔ™GBß@îÄ#çr"1y'– 뢎žnºÍÃdõM.Ža,5L#•=·‹‘z&˜0ÌN §‰‰ÑWà œ·’Ñ# Úéè1îÕN‡CÇñmž"K²æè¤£w„9´èÄKÓ;$I”¤ý”z+1(6ˆìqJ|%a’¤Iþ—Ûm(Òwv‘£æèw#"GÌÝW=1‰Ù xbrTýéô•RQ…¶-‡p/gGû@"¾G“&ÄSAù-²Ln§|hÞà$°eœ…^ Fæ …·èÌÕ2±<7;ƒ¦pfqw’%Pà,1—õ²ñG‹rDa6çªÝ‚Cãi¢˜¤ùŸ£’3ÿÅÕê¥ 3']“Ñ}ß§Öi›iñ£¯QLêb&HgÅºæš ƒLöúXK¥m­¢¦›§]U]›Ê´xð<8z¿l:¡ÚÉcHó&tæC™$…jB9yt(çš eß%eÎGÄ„IßÐÌ9Ó™La|R‰5àûÓED8›LÓ&Ä<+ð…fD™áYCnäuªŽŒ‘­{¨vj+Röïçi²„šã;ŽÎ»$LÐv€aBPN¼¨4KÁ¦ÑýìŸ_9ÉõØgLŠ©Î`ÿÝÝ|å§•‰Ñö£—Eèføö@²&&ÓÊùÙÇY䛸C ä` Öæu[õÆŒ–ûÔÔ# ¡Q0ò‰¬òîÑ[#¾öŸyà~Ù_†ÓŽd"jèÓÓõKÿCµåû¨›}˜ ¡©ÔÚo:MKýU±¬‰J3‘¥úMÛ½iøŠç€Ÿ‰µ7ÕOÕÅy`Àx„‰I £n ÞÍ>tz%öv°ù†–êõ!"¹4œÒ‘ .Ýmªì¼«v^IÈOƒ°Ëˆ&” î‰L‹¤Œ¤(Š`,'p•aøí r^m}·$ÕN»wª:†ßhö²$ë,;ðèÐCÌI’ü/6¨›žR$¶òÿ¸˜ËN!æž"N¬xèÜ@çZÖaÓò&OõôÊŽãÆ”¯â¹éŽyW¿Ã(ˆU5G¬²”/H’ç,|UŽ%F£ýßÓUOWöþÇ!PCjöC¦õáÑSÄrO'Ò•¥WW@ïO?Þ‰ÊØ«•±ê/BÄ#T|Dx¸1¸眃)¦Ñ³‹4ÝZjÒùS[ø=eµ?ûÞp(gž7郹m/*🌹ŸÒGé1!I‘‘.ð”!øÉM‘%ûZŠj§öá¦Ú©}¸9z”7‘%Ëk—|KÔÿ±…lÜ#BCE"Å ÖÝ5;kÐZ&·«f$®—VÌË¢ýgó)š5M=¬eBÇbžÿßÞw3·‹ÄÙ‘;£ GÎ/-:þaJ®_z™}™^¦À(Òw›åƒbÓ]¿Î¸ã°ÚÒÝT[º‡‚!<)è,Kœ1W2¶¤(ÄLÁè4ü™‚ÙŸ òº82§ðebÂÔÛg4Ñ“9ÓÆCAç’¼hD Ø€ %6½ƒFI˜á™´J*°ƒÎ ?ªŽ ¢j§#cäê=Yr5ª|…€$á±²I¬²–ÜNÇJ¼ADiÄ*_aa1ÁüÛ«ç! ÚéÈXªv:2F®ÜC‘%W¢«ÊV( …€‡,tðÅ2kR° ²Æþ8 JÝÜÁ˜°˜5K¬µ6'KcYÙ¯T2‘HÖ¸²)L€ƒC»„` IXíèØK%ÎC@µÓ¡±”MMb¤ÚéÐX¹r‹"K®DW•­P( "€NLïdgK†!€F©‡ƒ<µJýy¸ÆQxy`"&¾ Ͱv 9”@*A‚„¶ 9•àÓ¤4J®jʪ¬j§Ccã®-Š,¹ iu…€B@! Px©‘A à·$H›ÝÁ,X~”ãËg Ú"£?S“"&±ÜO’ÐaUâzT;½cÙöŒØ¨vz'B®_£È’ë1VgP( …€Ç£÷ètÁ¤ Út¦w2*:žÈ’iäž °‘äz߸qVÕNßpà‘ísÕNcäêŠ,¹aU¾BÀ qÑb$³¡™ÃN+ñ9Âc§RÒ¤ l°ÔGUyoÇ»ð*‚¨OßÍXc$DžJ=.t¸ æ$IþÆÁìˆâR%.ãà²}âåýPíÔØ6åM“¸ÈÿjîzYr=Æê ;X³p:gˆ÷§wœ½c›å ˜„LNK ’Ê:ËMê?#0må÷(5ëA:üçU6á?q%µÔ^¶›à ÁìªOí¡Û7‹\Kz]§Mçµg§ÔìÍ”}×·(,:“ëÛF •'(wïשׅ紧žêßF@u¾|ûþ—Ú«v:^î´÷]§"KÞwOTƾhóUNIO¢õ‹f(²d3bCïèL x‰N¾µ™ÉGÉÐ;³%!c5ç  ¢Ë»¿ærÒ¢Ó6Qù…ßSmÙ ‹šHK}ƒ2r>Må—^¦†j“B@! P(ÎB@‘%g!©ÊQŒ{–Ïf›c¢åRvF -š™I%u´|îTêæèTçó¯Ó…‚”žG÷¯˜Caœö«Om ÒªÛ´ëDî(Î4ÎvemÝÚgŽQÁÑ¡©‹ÿ†B#&PMÉGTtòglj¤§Å›_!h†–>º•3ÆëèðŸV`(Í\ý#J™z?;¹wÓË/3AyQ—³þj½}•4ÑLR>Eŧþƒ2ç?'¶­úô>ª¾ö6•ÿ-ÍÞø”¾Š œØ³îú>*<þc“æ*mÆ4eñ×($¦úÊ£D•Æ{ØÓYOݵÈ n•( …€B@!àYrÎê, A„k‚É¿ßi3˜­§¤%Q(‡«Ýw&²Ò“ dêji5µttQem#¥&ÆÐŽ#—¨S%¦„£µ?šÈtš½þgTzîWBó3kÝ©¹öÝ*ÚÎÓ;až|ºZ«Åá³?ÅþGwS£°$š½áß©¶t'u¶ÜD&{Ù7™¤ÔQÉ™_RÃÍS™F“~‰r÷ü=uwÔpnšj«½B•W^%£æêÔRs‘ªòߢ˜”E4gÓŠóÖœÜÁæt“! ‰H닎¿@Íu—(gÝOh"“±²ó¿»ã’4Q”C ™w3ë£êü·ïØG­P( …€BÀ5(²ä\U©F@Ú6箌§?|¾ÊoÞ¦êº&š›=‘2S¨¸¢†Z;µ”¨ë¥ŠšKrïnkGÎêŒ2ÌÏ_S¶‹®_ú?±*mÖ'(!c-Ý,x5;yb]Km.u4Íð8PCmù>þ_Ö?•°–é*có7H`p8þËêíéÿ;“ËáOM7O‹ÿøfq¬Õ cbÓVŸO “Ö ²4aÆÒw·²ÉÞßò^ÓþIL|´í7©¦ôc±®îÆJÆ9­¥4.cê>žÉuÑñŸ2q»m*g4 àægÌ•( …€B@!02Š,Œ‘ÚÃǰքÉ[Ÿ~ ³ê—t«¡ET«³«G̃9χ7KhŤXÃ]nnn~\—×AÁÃínӶֺ˦ýzº( 0Ìôß|ÁÏ?âÓ–SlÊbš0íaÓ¦°¸©¦åÆ[gMDÉ´ÒlæuРŦ.!-kšB#RMæt04´Ám/~â¡ÅZñôSIC…j†F«ôÜo)*q-zøÏ3™ò~×tÜH zC繓™ã>Rj»B@! P(Æ+ÞݯwE]·ÓåNxk—Îéå:³@NïÌâ\^V_oŸð¥ræ‰ÂÙ7«¦ÏqŸœ^]—MÕ2ôé™D÷Ы¯°Öæ«Çôé´V×Ë•“؇)*i6~e¥ðSZöø;r麛(:1Çô_.èumB³dk?CŸNDð«¯8ÌZ2Û¢þÉsi Qh -ש¹B@! P(¶! ¾ ¶á¤öòA0r.§Pî,öúØ©ð¿/\VcsÅD†QTx(iB‚¼¢ÊÀxF……ŒQ)Ü+”×MaÔcuF‘w”ÑÑ\ÎÖp}—¶Œ‚BcØ.ÍŸ#Ííf­ÒcŸ¾‚ÿRdü ö-štDZC­œÀ¾KuœØSÏDf ûD-1íÚPq”B"Rhò‚¿¾N0ËC@‰:Žn_¤Ió¿@È¡ž(4S¦û9ƒ#EÙI“7²¦êšånCþŽÀí_> ˜+Q( …€BÀ6Y² 'µ—!`ê’1Óu|DÜL¨Ë$UÔR]S+}íéM´yíB¯¸Àxf¦Ä™:áŽTLÞ/”g0øQ­~º#Å y¬NÛL7®¼B3×ü3ÝýÜE¯àØ¿PkÝËý:ÝûÕ2‚f(42uÈ2,7Tä¾Â âèžç ({ù7©üâ@Xï›…ÛèFîŸiÚŠïкϤ«~ÀxùSûHŸü9!xÄ&>îîgÏRâ”–ES ¯e[Þ¦_Ê£œõ?á@Òµ#ÿïŽý†Z'Ú?;òðSî×PçSë …€B@!0ÖPfxc펪ë1u1€àÏÊ(&<ˆ‚ü¨ƒýÂ5!GéÝýçLuÈ+«&LRz9âÙO^Þ.ÿr»éOÛQhH°+nÚàÁਠ¤ÔøHÆ{ p€=U’D å ?èþáPщ˜DØÏhbwá£/ˆÜIA!‘ÔÓÕÄû5óm)•y[ “”¦›gDB\„$×÷´ËÕýsåþTpäG¤‰¡žÎF^o,»ôÜoØé7ÏA ÚDT=‹ƒ)ïÀw(¾#4KHJ;ZŽÁÑþñàypô~¶j…€B@! Pø2J³äËwOÕ}H0Š :‡ÈþJ,!%<€´Ý=^C8†¬üP÷¡‚ qˆKV#ê2+#Á„1ðvDÌï׬Œ$Ö,eQ½>Ó‘"‡=>M’(ÉáÔÓ5@fäz[çw¥# †^&Jˆfh$J[H¬GøñáÄ¢ü€c2 ýã9Ï„£÷k¸ºªm …€B@! K8ÖÃKH¨k3HM:†è$óD鱡Ä-¾±¥ƒ}—ÆÌåºõB€[cK'khî”T/p–˜ÛSy¬¼_(7,Ørµ“Þà>Zö\—'nÀ/Ø¿O´{´<x½_ž¼.un…€B@! P¸E–ܸ:Ÿ[ðçPÉ܉G'1„Í×B¸£¨ ¡Œ(Òé{ A”ŒÆævÆOOK²’)Œƒ1_à ¼óû…rg¥R[_]ìÚâH±ãöXàÖÊøMŒ ííϳî׸V]¸B@! PŒ;Yw·Ü+/zÓ¤ïÕwiu:‡t?Ò¬ ÃÐ Õp$9ž’¢4”f NPPßÄ~"Å+±tI¥€S}S;ãÖM³Ò¢iRjœÀøBSá¨Y—åýBù3ù<Õú:Ûù´Ò0ÙxW¡Q^À ííííϳGgèíëE–^ÓskcÕn …€B@! ð)T€Ÿº]c²²wЕmwssKÖÛ­®€i:ñAAA¢³FÝÜÑïîî¦t½Ž£GwQ-&}C3ÅF…³æI™{ Õºà£ÓEhä¦%‡ÓœII<Ñ ¾Ò¬k¨ãmYoí~ái(‚ïWsk‡A×Ó/s¹ãY6ߨ– …€B@!à‹(²ä‹wmŒ×YÛÙq»­«ÛZŒ06³GŒo æÎ|(—ÎD©‡zt:î€÷RGœ nÕÒ­.Õ6´ŠHsá˜ê“?Â…sA%„GÔ;sæH‚sÓ"hjZ…G„ <+ð5FXs ³¡îWNf"çò£Âëü%–P:GÉK,¤`¿á“ÅŽ‡[ˆ{º:kÇÆê …€B`|# ÈÒø¾ÿÞtõ&sžŠ¢‚Ü´ÉSŸ¾RRMËfO±»Ž0í‚C;ü50²®c¢ÔÛÛËÉCûD™Ø¬¥ºÎ^jê1P“ð$΃Àäóñ&}z»·—Cjóy”8—)¥GДÄ(Ф¨(žÐg<+ðuÔOb<ÔýšÌ÷+&4ˆÊn·RmûªíÊâûd ê¤Pÿ ¤á#ÉÉòÇÒ\OÁ¤í‹ g‘G)IRB°ž’ÂüÙ— Ú¤pŠŽvÍýÂsÉQý*KKÎ2¦ò¹KðŽ»k¹q«žòJ«…¹¹½Óg#†Úsãä0LT‘ü;>:‚r¦¤QFj¼=Å©c\ˆ€j§$L©U;ua#¢hE–†F­v+²Ã…yßÕs§J—m¼¯ål^Y“%ù-u…LÚ îÔCS¢1vÌÙL/ ´”¢ÓS{7O:?êéë%=ó)tBÇ› Z`XQksb5*L¹ ¡QЉŽ&,ÃÑÕœ¡U’t¿‚9ßÄŽ.jj×RS—žºõÒ4Ômä¾²·Ì;[+Lç ‹œ×É´Á… ‹ Ep´»ˆ E„Àä45¤®¿_ü\ }}Íç(äK4v]xŪhg"€:~ç8›OÏæQKG7Û<(¬·›4œÏ+x„Pöά‹·”ÕLåÁ‘ÔBº@Ñá!´aÙº{ÉLâ‡M‰Û°ÚNùÝê×E!~(ëv{<}B=–¢IËß>ÑN9áø†¥ªºú¾(²äj„Uù#!`ÞÙêåÅTQR¸—“h>Q\QCÙ)#•1ävIŠB Ád72%hàg²vzïêÒ _¦0î<Ä3Q‚橯?òƒ7ä5ò✴$‚ˆtÀ+Àߨñ6 EÂç…Mï Qf]&­’ã,/Á–û¥Ñh)ž}ÏÐÙã ¹_×®¼lªzöÚ5-»cÁ“÷ ÏãÕ’*¿êëeÈš,ŸWógبs؉Þgx¿A‹´õãÔÔ¡¥‰m´°±2ÚÊ)D?þ:Ÿ–Pv†PEäd*‰›Nï覃g®Ò_=´Šr¦¦[îªþ»óvúúÎãÔÜÞ-L°³4Ê[Bn2ÁÖÎ3¶Ó³ÜNTíTâãì¹"KÎFT•gÐ ÈŽ—ž—õÇ?Þ¾/cjö¦­»NE~÷Ù‡ýƒƒìÙC£ît4ÿûCгߖɒ¶¿ó€½½ K0C3’+qàÿ‘¸@SÓ:£–ÂE&w0ë‚y&˜ßa;pt…øÂýŠÐ \ylLôÀ7-yâ~õèz‰ŸÇ>CooËÑíÛvð¥âY•Ï­ô{n{Œœï4¼ÛöŸÉ£_¤8m=\qˆR;nŽ‘+tÎe€0f7ˆéVø:ž±ž~óæ>z|ÃÚ¸,Ç9'Q¥ ‰€e; ¨£UaÛUp Äà³;‘ýw1!¸Ï•ÎͪZ`äÌ¿Š,9MU–=ÈQitºÐù‚JOgg{×ù£‡^ó_¿éùW¶3<·e­Ý6q²c‰¾Xf„eä ÓhQÁz8:èë÷k’di,s¦~¥’‰D²6O˜œÀÄø Ü´=ͤ$ɘØÔ¨Q–®_¸_ÑáWž˜à>ß ¹ÄÈ÷ ÏáÍÛÍtéäÑÿmoçðˆÆgU&ù£–¼9JwˆÒ‡G.Ó”æZW±ût^QGo­ˆä£…oÑ¡ŒôÎ>ÔÒÀ&O9â}é­uöÕzYk§iy´@óûdªv:Ü}E”ØÕ/м„ª‡”ýÛY²;u¤óÀ¨´$J°Ó¥G®Ä&&a{3½G†Ï>¼ÊÏ^ :—2Ä542¬8̹z8<¶Q«Ô+F_ üQÄË[&ç]ª÷•lÄÄ^ Ð,h—àð R ‚$´MÆÈw®Ò(™#ãí÷‹c]˜$9%Ñ´ìŽwß/h”@”Îå—û•\}çÜáýçù:åsŠÁ <»J³äŽ›?ÊsÈ(ÞqWJ*iûQ#QÚx}ç(K¿»ƒP¯}“î§w÷¥ÄG “<<‡Jœƒ€µv ¢´$ìMçœ`”B)ðâ<{ª:ÿ†+²ä|LU‰£G@šá¡ã…xÐ]ý“æàïìgÊ‚¯Òƒ·š Ÿ¸w¹¿£>LøÈÁ?Gö[$I˜¨€(ƒ@À2^È®$ß%1ñ`düï߿ݽc¼ï~E…1£ÄÇÅÊE·Ìe'Í÷ >J0½ƒF©ôZîûÞûc¾Hù|âYÅ3 ­°"K ‚· Þc Jíå=g„é4JJFpk£×v§}y‹Ð¼¾u„5µÓ½g(Ê¿Vh”¬í«Ö 4qí‰ôÚGÇèGÏ?®ÚéðpÙ¼U‘%›¡R;º˜ð Ã];:b0ñ å I–‚}ðîÁ†š[·—¬ÛøÄ/þ²+jvVºaIο9YivåaBgMD9‚öðÂ9’Ÿ{\‘%à"'`#>ÈuÀÃâ÷‹ã^˜/Ü)À"ï æÎ¼_È£„ðàˆz‡`}z]Û¹#·^>yôŸÏf{ÿÏ*žY<»ãÇÁ/Öï3˜Þu³yñ¡sùÔÂ9ÓࣤLïì»{ÀmeåAÚú¸ˆ"xïŠ9â¹³¯4u”DàŽvÚÙÃ>J;”éh”sh˜æ„l§cí_Pít”Ø ·»"K᣶¹ I–¤f 1¥`žÐFý¯œ>qµøò¥²å÷<°²W¯[ŸHî$"5!†˜èp¿Ð  ‡U–Z$Ëÿ\1+²./Ðò¿\ïMsËûcùß•ueöæ|ñÝî<•Õ²-ïå«°R«Óš[: H8ËXúñsÖ^^äÔžwuu´òáx.ÛúçX6×,)²Ä€x‹àY@0‡&JˆöyêJ©ˆz§‚98v‡€¢<,‚=à¹sƳçX­|÷è;ÚéÕRõ>8JìGø!ûÁsªÚâà#Í>ùƒ7¨ 7#3ø>À£×A¤ÃY9ó&fN›>-,"*&$Lr¥D!àfgÐdy¢‹W Ëå²/Ïõz]wwgW[W{[Óâ‚Ââ«—‘L Ð µôO NxFñ¬*%ÁÛÄ8ZϹȘ,•UÖŠrK8<¸ÇÈbFfÐõêÛ4eb’°Rp¼ÔñYÂíT«§é\‰ã¤s”¼óYª:¥(A‘%'©Šqs툒ÔI"“@•ä].å Tì+÷ãE% ×#ð™ŸùýLžåßþç‹rÙÇçxñ¬™?oÐA£²ÔÜ¿ %ý•pœ/AÀ4Z¯Ó‘–M* oÔˆ„³È£än ›:“füôUqZƒ®›ºk«©æý?R㸾Ù'‘s–RGIõu¡Yº_€#øæ•UQæ„aЧ´K£¿VÛ)'œMô ©_ûÌqÒDMä 1Pw{-Õ–ï£ü#?$ƒ#ã'®¤–ÚˤïÁ8“û8"y¹j§ÎÁ^‘%çà¨Jq²“†kI€ÐƒO:fX 7—‰çRó$µOòÞ¤D!à6<ÓksÞåI²#‰’ôÄó&µJøâC£Ò¤´J ‚·Š±ÚËt¯©­“4z­GΖÿ×÷HßÞB ¥Œ/þ#5Ÿ=Äd§sÔúÓÔoý|ïÒV»Ÿü¡ÂÈÃÆx64·‹€@ð{UdiÔ·Rø# ’y; ¡NBþ OIÕµ7©òê«7aM_õ}j¬:A5%;F]ÿ€`ZðÀKtò­ÍL–JF}¼3Ž¡~]ª:L.C‘%'©Šqè°É¤ èÄÉÿè¸á- ²$´Kk’,IM®$L˜ãÄzé§$åUJ¼DöÔ÷“%$ÛÖqD¼`îÔyƒ„M™!ªÑÛoB7á“_¡¸U÷RÍ;ÿ'Ö§<ñõ±¹ÞÍWEÁŠœ»Œ4“§Sã±]Ô|î…WeSÄÌ…TýÚ¯¨çvÇ. xv÷èÎÀ[Éè°ÖNÅëeôe9ûh†"â¦S}åQQô´åߦ Ó¡Ò3¿ÿ§.ý:õ²v±èÄ F «)*)‡nn£ºò½ÔÑ8“bY;Uxòêj­vvõl./_ÙªÚ ×°;*²4,%ÈV@óTxüºUôØÎŽNbÞR›KMž1Ã3VDµS‰ƒ£sE–EPïJðÆ‘„ £Ùè´¡Í‚ É $I¾c•V‰ÁPâvÜ~FçžÏD>o’0IÒ$ÿËíÆ½Õ¯W" Í›ŒQ¾uL <.~þÔvå Uýñ?¨³ôš¨ü"£I[Qlª^×õJØøa›”–‹Çä¢WÍ%YÞJF€w¶S?Ҷߢ+û¿AõG©µGÐ2…ÆP{C¾é"Ûn_¥‰9ŸÛäÊÛ×ÈE¯š«vêœÛ¡È’spT¥¸|éÑYÃIúR€a’Ú$I’äœ7)Q¸ >ðe‘½iÌå„çMNr/_㸨»±?Bž¥>¹ÇÍó´ÜzówÂgɼðKÒ5ÔQHj¦iuHJ¦ˆr‡mRúX;æ•"´xÆ‘{à®4K¶ß%om§u¯èÄO]ü’´í53Õ´>,fŠˆr‡mRúzaøâ…¢Ú©SnŠ"KNQâd‡ 8ˆ$JÆÆÿrYÍîDA|]ÌûÔòYóõk—õ7uD™&aôÿ½UZ.§˜åë©åÜa®¢A,7‹eë5ÖÞª` ßG‘³’®¥‘z;98£‡4;â!QïÅ×:ŠÞ±Ö—Úéí9¸Ã}ì´GhjË{‡²£¹œ÷룸´eÔÓUOºnOë7Íò mPíÔ9À*²äU)îG@¼ÜZuF…À@ó©D!à5˜:¢^L”VåËÿN“ÿöß(ëû¿¦w­—OQÕ+¿ÇÞ¶ªÛý6¥þ›4ñ¹ïÐåÏo ÞD³÷Œx3õ "£;«¯´ÓüÃ?  {~E‹7¿"Lïê+ŽPÁ±òbuÚfºqåoÖº£ý/Í#=“‡DµSÇWdÉq U …€B@! PX €@ ž\d±và/Õ–ýâ[F%¿mèAÐE£ ¨ƒ ì ×a^õòÏéæk¿æ%Öœuìo¾Ïÿgï<à«*Ò¿ÿœs[z¡÷ˆ `)˪ûîê®Ûl°þ]ÙeÅkT\TûªàZpWìK‘DéM©¡%J’@HÏíç¼Ïs’ss’ps[î½yæó9œ6gÎÌw&Üùgæ>fm%°ñ“kZ|„†Øí[=­nŽο£¹Lj §ÇêEÜ“À"oy$Tœã;Eáà "Àb)ˆ*‹³Ê˜`L %ÁúÙyŽRKes¾.™käk°rwfêÏã`åå/`¼€-KARQœM&À˜`þ öÎrH¾ç—^%hõ{Ù—â¶©ó˜IÐï©×AÛ©3h£;µÍ+×ã'Ü—¼· †/Þ iï,ƒn·Üã•t9ÿ÷Ç­0àê']z±¨ÑC×^]ŠÛR¤¤¿€‘·~ úð.¸ÅµÍãë$Ê{®Ÿv·C0î[ ïȇ=N—pŸ[–ÜgÇO2&À˜èÐbF\)¿y*ölq›CìȱPyp7äð’Ûi¸ú`Ô áPôÍP¹wÄ»E…¥[Ö€­¼ÔÕ$8^èÒs ¸ê 8{êG·sßµ×uPzz;Þ8Ëí4\yP–í`®*‚=+ï‡êÒlè>änÌûãPˆ à«N»’Çñ2K^ÊÉ1&À˜±£ÇA×»7A·_ü¤Új8›ñœ]û5D ¼R|´1 í_+ Hþ¢¹–ÒR§=½¹(òPÕÍEH{{œ˜ÿ¤üî¯`HJ…Ò–‰% ”ƉWŸÀ#ºÿi&Dô&|¾tý28»æK%Žhƒ”?Ì€¸Ë'€,Ip~[|òú0èqï£Ðéò‰ [-P²j /_¬<ãüOî»Ï;NÏ~ÿ5$ÝýDöOƒŠŸ7:®óAðHès¤^ò8{rôùØÌUwàÜþqI£`èø¹h ê ãþ´ Ó8¼áiˆŒëi_‚˜®iPS~¯=ågö¢bÍÙ»j* ¼úŒ×N\ ½®WÀPûVÿYYÏlðµé?Dy¾àÈ›ù‰GÔ†Á 1ÿ€Ä>“0ž…9Ë!{ó @×M‡Ä¾“Án7Cîþáäž÷.~bÏ»Žkçò6*4­! €Gä9¸øó€‡áù“6¿‹ 0&À˜@ÐèÃ!føUÐùÚ›¡`ñ¨9qºß;ÄðH0Ÿ+‚ꬽ`--†So= gW¡”,õ¡Ù`GQuì…ÿcþqH} a¨”¾k2ô}ò °UWAÁ§o@9Š0Óé“(¦(i˜ sÁŠVžòë û÷£ˆÙ€ÂéQÐDÅ(i'ßý0Ĭ¼«`ñëPsdŸr½ë w@ìˆ1ûÎ? ðË÷ å÷(ƺuo•tôÐÑ Û¬P{üp«ñøfàÐh# Kê8HôKÈÚü jÑSVô˜ª‹ sí ÈËüH)PÚu¯€EÕÏK«Xp†ŒÑQÐðèî0ò–ñ~doyJN­Sæ)‘˜¢4jÊN€¥ö,ã¼¥ßþJNdÀ ±Ï‚Ö«¤AV¬ä·£xú²¶ÎòŸ”ë©i¿EÑ52×ý rv¾ ¯y"b{:Þë|Ð)qôH»GzEhUª:—å|›ýH€Å’aó«˜`L€ ø’€ а)ưà| OCÑçï¢ã… ðó ÕAtÚ(I%`A¡$™MP}dŠž ‰Ž…¨þ—À¹þ¶ŠóPºa„÷ú„GÈÊtrÁãP¶m-Ofƒ­¦l•eJ’±FI·då0ŸVÄ” ÑBÌ%£•ç;_;Îoÿоý7œßºÊZ¯\ÁySŠð*È…êC»ÑQÄIˆ»ò:Ç;›èâ»)¢¯xù§`=¶éíVω§^«qpn52ßl–@C¥öZ×Nmày;=¶}:^XÇvÌC‘:w¿ ÌÕgÀˆBÉn3BYáNEôèÂ:A\âepúðg`1–BAÖWha á1=ù%aD¤¢c+ òì!°Z*Àb*UÒ°YªQ|Sû>„ÚŠ\EL ¢ºô¸Fy>eà˜ïàø®·€„Nññ åzÊWŒÖ/[ç và>­LSït> kØÐ h‹èŒùûÆù–KÇ60p;u‰ÔÅ#ñ0¼‹3âL€ 0&Àžu@›ƒF„ ßñ0Ôœ¨³¾Ø*Ê•”4†ˆfSŒAEÃíz=œî¸oG1Þ³XÐEæ µhX^â÷AXRO0ÉW¢ Z:ƒ2äψÖ-ç@b*zÈHeX`çkntÜ2¤ôr;U¬ßSo@Mv¦"þœï¹r\‹<»…5tì›ãîJ:5Ns¼¨–IuVO¸”£E‰‚Åx^Ùkµ‘ʾé?ñݯV†Û »~ã– ­HÑ]á¼ åÚ¹Ü:!îˆÐä ¡÷uÐgÔ#Ù©Ô–ŸRîŠЦC•%=Ab*>åJi£ yÀ­Ž{û:޶~~¨Kê+Œºm±b+Íßâ¥Õc“ÜN[eäêMK®’âxL€ 0&À‚Œ@¸Ngpž„ņÁfv;÷’ÉäÒ³özwÜ4¯öxcQC"Š‚di%(øh˜_åþ=û~Gq0lÑZå9ÙjV¬XúÄTå\ýG¶Ûpž’JÖ~…ÿyS½ÜìžyÞ³Už‡“o<ƒÓ£äfãµt‘8‘gL„¡¥(|Ý J;…°Èa \kkͽFBë‘+Áf©Q¢ý„C𚊚³DÁnk-»K‡sy›aÇ×w¢õ'&Þ·[yN¹H>Û[9Wÿ‘%^·@îÁÅptë\õr«{«©ò~†ÃõfA|±àªX"ŽfäÉí´U¼.ßäax.£âˆL€ 0&ÀŸ€ó§Î‘:t— @^t㎛·Ja.8¥ ±£am4¯­54¤.ñÎû•ëä!f~Åw1˜ÑÅÆƒ ‡ÆÑ³äTÂ9TeîT†×Å »ÂSûC§Ñã•Ûå8·)~Ìd8ÈÒD÷ ‰ Cª”H¸žSŸG_CBw(^º"ú ‚ÈÃÀÜÓù­GâÙ31N†×jd¾Ù* Ú©,@±m`«Ï¸{³ö|DĤBXT’b­)?³[±>õ5]zGŽºöïrò"}|ˆè æš3 Ñ…CŸ5z–DTbÿ)8,o,DÇ‚„>uÏbœÛ”<à ËYšè^Dl¯FÏê#»â|§;À™†¨nh½zXyGÕ9×çÖGyr;m„ÖíKn£ã™`L€ G;ô"ZrbÂu eÈéì›Nhù®M`ÌËQÖ.ê=}.ôZƒÞî‡ð}q£å0ü?[ÑKÝc8äéÂ!‚Í‘“-&(^ö‰"’.ýð|L„ÚœCލùÍÇ9F%Ðoö;0øÕÏ!öŠ‰Ê½ÓŸ,€Zž×ö¿`Äç;aÀó‹@Î9º&)ž÷tñ Êó_ühK¾ûÿœ£µzLÃu"$tŠRÄRsCÊZM€o*šk§:Q‚ÓÖK}B¨øä÷PUšãïÝ —Þø&.ôZ {WMƒ¨øèùn+ÜøÐQ„!°¡ºô~²Üý.ô¹ì!¸îýʾŠbÜׇ#›žUæ4ºý¿pÍo×B7ô~G!kËóŠ%kôíŸÁ¤‡OÀ¿ü¢“꟪ÛÑ:K=‡Ý ãþ°&üi§²>ÔÑms•¹O"¶rBÃõÜN[AÔ¦[< ¯M¸82`L€ 0À$àè€bOƒs@DFÙwÔB¾” E‘ÉTSxÑÌ|ø6Gr¢@›dF´çW#ÕSe}¢ì§ÿˆÞêbÑ^_cröphú ‰¨›+Ežñ”€CÞœŸUÉy¡ñ‚›E_/‚3Ëã9IñV§Æ£½ål!d?s/ˆaáŠÉ^S÷N²fåïŠó z¯­ çV5bG#š{¿sú­¿üèTÑ£‹Â•ôŸÊ¼µçø^c*3âçÜNÐ ZPÕÎÙzBmn㇚9ÛøI3ºENhSƒd·Àš·†kZjÏÁö/oS¬JVs¥œ=lþtzÌ‹VÎm–º¶DíÆùY5Í]KïQ•}ÎÏoÀ tûM®ÁeÉÚè­‡´ã«ÛÑ"¡Xlõ魯T´~9ŸÐ¢ÑºU†Ï5 J)¶…ƒø¡@ƒ/»µ¶QÚ;!~Ŷ~0¢·Ó‹±rõ>[–\%Åñ˜`L€ 8²&i‘¤-zlÓjµÖ%A‚-=ƃ ;i¾öê œŒ$5JšD’C(5ºsñšŸDn½[ ’ɪPrŽCÏpj*”œã¸sLܶö˜áZ†ôìê`L¼9´@‹í­K™¦[Á&û¦Ò áÎD’C(9ßpá˜æ'5JΑÐQ…’óuz¦Î Ec¡ä‡òÙV¡D܈_¸ŽÛi#–žð_¹‡ùq&À˜`@@ýbOb‰D’^.®õ(”ô(˜¢( ‹‡ ©×BVƒ.Ä­,¬3 ïÝÂÃÐ+ò%Î*ó +P;fXeÖl; Rê {w¶cƒ÷ÕÄ­RJ€á½¸z³Y,y“&§Å˜`L mèÓ2nB+Ÿ˜´˜kæ@IDAT]OPÐb'žD’Á  &ƒÁqZèj°Á‰Ný`]¯É>³0¹žÓàˆI%âEÜú%DB®± WâKœ‰·—C}{PÆfy¥Mx!^ÏSëíÔ¶¡ðsí]>³0yI@%A%âEÜúûº ޶©¶‹€bá‹Ìðœ%_På4™`L€ \œ€£3l—íµ&«•Î=ê}«Ã›¨3†"),< Âq3à !Š#å,JÇ¿×o¹&£Ks˜.^ŒÐŒAs”hè"YäzÅéa@r'…'q%¾dñÖ0<“Å*ÛlÍú½v´‘v¢|Áûm¸º«§mÕ•vZ`Uµ]`˜á;—æ0µŸv-ÍQ¢¡wdQòG;µÛíM'Q]ÐFÚŠ—3ÀbÉË@99&ÀüF€:•ÎKçcŸf¢ÉÔ O_Ö|âÎ?Ntì|Þü|5  HV{IyE Õ£G혆8Q'^§Ó)")22Ì&3˜Íf°â|ž®öÐ J!Vôÿ%ô¨Êƒ~ç³!µê¤Gë04Ü6dŽÖQ"÷àäõŽœ9Ð\¯µšQÈ’x’ø$¾ê0¼6$ßbÔòÊœ¦e¦Ùþ¬&s™§mÕµvj†R[ØR{?tÓæ@wÝ~Üg{´SÀÃu1ƒ´Ž¹'¯wäÌAs½Äû©Z,ç]ÌfÈDc±2UÉa!O@G÷?ž>J…IØ¥ì ¢„ƒ—ê\ù±ø[jso{ðéžë'~:@ßKÕ ÉE(‘r±ƒõýâ×çÒjˆ4k™6O~ª/¾†:Ê…UF³P‹Â&çĸê:¡"è±3†éDEF¢P²€mµÙìhY¢&b„pÑeVŠ¢SQ@#k­¸¦­ ôö–+¸›¯@΢ÑA­6Œº0$CÁŽÃíн“bc¢!:: "£"žÄ•ø’'7âíi :§º7Ö֜ôԿ_ÚRpä‹òéi[u¹bÛ¥vZjë­ˆÿÃ7@-„‰XG–@âã—¼Ø@&)VYp–ÖQÒ þo§£ñ Vm~)w{¿„ÅR{׿Ÿ 0‹¦NM·wÁæ“ALÄj¤p±RЂ¹]ç^¢‹Û±+€·ïÛÀ ¥Ù"GŠ¢6⹩ÏÀ„à‹¥¬T.râg>}“(j^&¡4*ü‹†^TÌ×Ù'AYÇë.ùtÔÐgo¿wÚ±¥½¿ºþ½ê°<_gƒÓo±Ôñ°œ?_R•½o÷+Âe£ç.^±E~àÎqµ}UP_9FK “XŠW„’âüÁb©sþP?¯Iµ*ùsD^¹¾A€tíß6‚nÄ®7*9D$ZIP,ÑúT´.•^q»NÖ%ræ@"‰®Ó}êØKoªã³å°gë†ÅµµUÕ˜&}Ø MLdYRÚ¨šGÊ/å[wý_L›W/ß÷!?ß‚ü‡[ÇžX˜¨#ªº¸¦Ž¾êVœY”,èä¡ÎªdW,K26Kª`Â|ø%œ74ˆ¥n‰]ýòNb£lèýŽ,KƬK´†‰JH:ÅÚTçùΛ%»ŽœŽÎüßÞ-÷bõ› ÷Ô¨MÊßj£6ZŸO#å;®kYîô´­K;sò½Ú‘ÚiîÑ#K~ZÿýÎúºÄ6ŠYó]`±ä;¶œ2`m#@Ÿk5 C{?‚ï’h޽k@56q¶BÄõIº]9eÊ´¼÷޾†÷¨ÃC›*šÔè¼o_TÔ1&Ëu˜kV}öÉ’›îþ=ý>ÿ¡¨´L¾{Ò•¢7æ0Q‡Tub@ó–‘„®ÄÉ„ê¢Îµ¸ÿç,ÅêÄR|ç8,ºïñ Pg-ª›ÏE‚Iƒ¢’D“rŒ"Ê›Ö$šãCCïÈ¢tüPæ²—~µ³Pƒ}±§=µÕ Lm#Âm3†›áÇÿaþñ?l<¾ÍmUe¨í4Æi:]Gi§'³}¹î›ÏIrÅìù.°Xò[N™ 0¶ \-~Õ}$½ÞáïŒui[B&6Ía"Žr§^÷a¡âFÃhhS¿ã!‡  vDËæ‡:$†5ŸúÅè 7I—_ýЫŸ®é”Ö¯»sfÔµ~ƒm5ÖÓ¶JÜI¨b;Å‘™ŽÐÚéÞ­›>Þ³eY”½:êÅ,–|A•ÓdL ­«Ò¯|ärÄn䜃ç£P¢é×eìM·Þ¼fùLQýb­Z˜< §à T$di¾ Yp¥Ðü¼þû-‡Ú™uõä›oE×Ò7`G;;’rt¸Aî)„霼"à¨鹻é¶å¹ñš†o#ï}³±-z—:æÎ¡é¹ó½¶›¬V¹¼¢F¦\‘©`·ÛªNeÞ´=cÕz£±Fí|V`º´UâFuOm@ý¨‡šm£˜;Å$ˆÂoÏÑ}{Ž]5iÊ,çµØVc¼ÝV›¶Ë¦çþ 5Ä©×Êí4÷hÖÛ׬\[[[]Ž\ƒ¥ú¬ 8U»ÏÞÁ 3&À.F@KÑÑ7àÜM›Ý¸s±§ù~³#ÎÆ¹kJ÷ña7nêWkêŒÊWkÌJ‡ÎÖ%ê|’<åo ¦¦Rúþë%_âùÊAÃGHí?0-<"ª‹><,V«ÕÐ$+裶‚}³O¨ÇÁ¼·Ùmf³ÑXa¬®>Ϭ£öžÂòÐ ú;$aD‚‰:¢Ô!¥c‚§ÎW ´¿ÏÛ(æYR(­heZ‰çk\2¢Wσ…GEu6„‡Çj5Z^ú–’íÔ„í´ÒXS]šw4ûpvæúû Æ6ê³öÅbÉgh9a&ÀÚ@ ®ƒ(Š=õBÔÁœm¶Ö£Ò:LÈSÖé )“аú$ÞÔÁá8”'f‡, ªrî š³öíÞ[ÞW,O¸§z¤‚úLÝYýû÷›ÞV³¼ä Ôã Þ«b‡ê•6u^Õ/‰"rGY”H(²U ³§„‹¶QŒEε ( ã–Ç!ÓF‰Àï^B©†b¥jòz`±äu¤œ `n T­ IaB…Úùs#~¤)ä)htQ x¾ì*s pÏŒ›‚ ŒsꌒåudÔsê`“È¥Ž(‰^KôûM(ªË J˜ÿ¦¡¬é… =Wë„YsU«’âÈÏI,ÕÖoTÇtŸê<ƒÚ&)jù(ß­RùC¡ªuJm”êÆ«Å’WqrbL€ ¸A€:z´iDAˆÒ –Pëø¹Ä{O¨!NªPR‡xgú¡ä8ÔŽ u>©#JjJ΢·©X •¿³XÎ`êß•Z‡jG”†Ø©¢‚ `:§ëê<%õY¼A-SGo£T9ÁÜNÕv¦Ög(µQ¯ÿá°Xò:RN 07Ð×q vC¥³ç_>¢ÌkQ-jÛ—/ä´Ý'@ê¸8wbÔ/÷dQr½ÎÂ7”þvθ/`ž¤úS7ªOçÎ(Õ' $ÚÓF÷Ý¢„Ytn£u(‚½ªíS­ÏPj£ŽÆêKÞ Èi0&à êä)yŒò$!~¶y¸Æ(q¥ŽuÓÎ5ýHrLÔy¦ú¡=Y¨sí,xÕºT‡T†Òß-¢ìAýÛRëQLj‡T=WïcyÕ¼wÄ6Jõìí´#´Q¯ü]±Xò FN„ 0 (bÉÃ4øñÖ ¨s[˜uëœé.uf¨S­vFÉ AõØ\]†’X"§ÁÔŽ(íÕêQÝÔk¡PÎŽØF©Þ‚½v”6êñß‹%rL€ x‰@(uö¼„īɨ"‰9{«_S;ÖÔѦ ÖeÝYè9x ù<¡ÔÎ(•E­ÃP(WsePË×QÚ(1…vÚ‘ÚhsíÖ¥k,–\ÂÄ‘˜` "ãúBB¯ë°÷#ÁéCŸƒÍBN­|4ºlf\R‡>àºxx£;ÔøµcÀYô(kî6t^Ê{•@¨·Q‚ÅíÔ«M&pc±¸uÃ9c…€G–Ž×<Iýn†ŸŒq‰W|k ¢x¿ÛG«‚1¿] gs7‚±ê4جä¤Ì7!©ÿmÐÿªÇ "¶'æ· Jó·Aæ÷»wÞéwß”˜SeL€ 0&XÔ‰¡•+Î `@ý°"ßZ@DFLY†ÈD·ùvI ‚¨ƒý#ÿƒkÔQ'n'ÙâƒVSœÜó.üøïË`ײßA—Ôk!uè=-Æ¿È uè–º¿Ht¾Í˜`L€ °e‰Û`¡C@aÜ·@Öæç¡ï¨G ,*Î䬄£Û_Æ!l6uÛb ËÐå·/I¶ÂƯQƒÇ¦CbßÉ`·›!wÿ‡(PÞS˜ 0*Ï„ðØT)¿…c;æCÏá(÷ÆÜ³ 'v¿i×χ.ÝÇ n²CÉ©u½õE‡å*eÐÿƒ>£þ‚-AIk睊`5•]ï{ùtЇÇùÜMphÃSÊuçÊ8—¿ ¿îŠ¥ö˜k‹Aë…™`L€ 0`±äC¸œ4`þ'ÝÒ&¼ Çw½© W2þE(/ÞEGWà¶ â’¯€ìísÁXY d.5í·8ÿh"d®û" íºyP||5ÔVä*B¦ÿ3Q¤”@ÎO¯Ciá‹NÞ—MƒÌµ3À\s$»ªŠ@þÿ@åê}¨8³Nù:%Ž„KnX ¼÷Ìöïp8]oE¢º)×n å%û`èøBc'vÿë`á1©›0ºôœˆbL‚‚#_]‡/0&À˜`¾!ÀbÉ7\9U&ÀÚ‘À™kàÔ¾+9Hr7_…Yߢeçr­¢8jÊr”ã.訡øä:_ô³C(9.:Ðð:² Å%ZšÂ¢’Ãéh`@kJôh|k+ÖÕw}çH äu;Eëø®w ¦ëyë'Ñ©7Zÿ¤s>fL€ 0&À|D€Å’Àr²L€ ´»Õµå/dɆ.¹-{p1Zmæ6›aÉjjöºz±ÎaŠIHƒ‹¯Qæ)]ñ˯Õ[`5—AlסŽsõÀf­R,K®zð“%«âÁï\ÞF´’¹æõO}ï™`L€ 0÷ °7<÷Ùñ“L€ šrB'Aç”+@ÖI:W|"­Jw@|÷«q$¢ãáÜ¢^.—,,:ç.•€„‹¼ÕÅ%v<[š· Q‰Ð{ÄTe® Ë#‡%'ÖÍEê5ü~ 5”ô‘]Ë”ãÁúrAk:iõÑJÚ ½¯GKÕá¦Ñøœ 0&À˜ð¶,ù,'˘@à /t¹ÃàkŸƒ!ãçÀ‹.…¬-ÏCÚÄy0úöÏñDsŒö¬z<œr©y™‹a:q¸ñÏYPQ’ '÷.‚Èz±U˜½b/ƒW?ÇÌ‹ñ<:X%8GêØöW€œG û¬"àNì}ÊpÈŸsè„Âkà5Oƒ>¢ :¤ÈCGË!çç×£ð1`L€ 0&àC,–|—“fLÀ÷høœcZÖ¼•Úè¥{¾»¯Ñ9­ttÛ\ôÑ ã¼º!v{VÞ¯¬¤3D£ )Ãøuó‡ÈÍwÓh Ц†²ÂŸ”qÉ%¹ÍR­^®ß˸ÓlÈÚ”ºðN`©=×ëÒ>¾ëmœ‹ô6 ¡xtQ¥xÕkò0úñ 8O(–%Z”–`L€ 0&à_,–üË›ßÆ˜@hnNÍ "Ë»áB¡Ô’,ÛQ(•6\p:jéºSa,”œyð1`L€ 0à9Kþ"ÍïaL€ 0&À˜`L ¨°X ªêâÌ2%@cÓpêÆ¨…l1Ûµ`õŒ•q€Ì¹]«‚_Θ`ÁB€ÅR°Ôç“ „.GÇÝ.Ûk­²ÞqºEö_ɬ²A¶ÙlÍùRgÎþ«~`L€ )KAZqœm&Š$«½Ä$Çr'Þ‹•k’cd«|âÀ˜`L€ ´™‹¥6#ã˜ðêÔZäHÁ"‡ùè+YâH<µ5ç°ä$BÕ­càÒ2&À˜p“‹%7ÁñcL€ x•€Ò‰/>·„bÛ@¯&ÞQ#ŽÄ3ÿXö>d ᦊ%Ús`L€ 0&À.B€ÅÒEñm&À|NÀÑßðÝ·$»µ4ßv)wæ½€8Ú­æŠ?oÏÁäìõ›ƒ·^ÁI0&À˜i,–Bºz¹pL hÕCéÌ——,)±öÎÙzMæ1£Ä8æŸ8™ù³Õoª`"Þ˜`L€ 0‹`±t@|› 0ŸP-Ô‘·mZöíÉn)Í4Ý*ÙdÏ_Š/ nÄ­Jå[W-_‹e´Ôo$šˆ³Ê90&À˜`-`±Ô¾Î˜€? ¥ƒ:ò–óçKªŽîßÿJ¥”ûLwR§žC 7â·w󾁵µUÕø¸¹~#ÑDœÙ²„80&À˜¸Åödªp‹ºØ‹ø>`L ê0<êÌ›6¯^¾¥àd·§­C…]Æ»d¶0µBÎéq"^ÄíøáÌ¥{·lÜ‹·i%ÚL¸_²,¦X’…h…JÌ&À˜`AÀc±$ÈB!nÉQÎ`ÁJ€,HÔ‰·âFûšUŸ}²$ÿøÑ±ã/oª*ñ&¤ÒJ >›j¦IÄëø¡Ì¥?þï«5½7²,Ñž¸_u\HÂ\®8CL€ 0&Ða x,–\.r·‡æÎë°¹àL€ xJ@KŠe £~õšÏ?ýbß¶M/—™:Un©½¶ÿ ç[/ŵƒx&Nˆq!>e昪Ÿ~\»ðÇ¥ŠP"†Uı~s¶,ï€ Êoþ–`¦r*cœ&À˜èдž–^#j2l’íE©VºÓú§éñóL€ tXê¼%š_C–òî ùyý÷[ÿ´3ëêÉ7ßjïÓ÷†]¿Xè…9L¨t‚O;V°ÊzÙ$ÇÊ´à,ªÁf1WåËÜ´=cÕz£±FHH…6ÖF<‰kÀÎW¢ßYQ‹¿)˜O~$ðÐì¹#ívÛM ©² 'ãT´_¯¼jEUã•Sg¥lpœøéÿŽªh¤ ¾.—ú5ï¾ðÔn?½š_ã"@h§[jN9r;uV¯ Ž?p;õè&¯ñX,Ñ(Îzá´,Ë÷`Ú,–šæS&À\&@Ö"FÖ%²zkpS„PMM¥ôý×K¾Ä󕃆Ú`ZxDT}xX¬VaYö«`ŠÔVôÁ¼(¡Æ{B=ö×Þf·™ÍÆÒ cuîùÜ£YYGì=…ï¦!vd="aD‚‰„Ryý1 ÁSç+ç€ Êoˆ§¹“ê—ªfÌXVVýˆF„¿áÏ$A9ÌZ#EÚj½ÝâQ'n$©ª`œÛ»ù E£—j´‘²I!Òà©Ï<_„Ú}AŠ.åíôô{éÿ 4ÛNõBµ.V Z0·k;Où½ÚÀ ¥úPÆíÔíÑc±¤äUÞAž7í™9×¾ÿâ¬M~Ì?¿Š 0Ð"àl]R³ˆ2gíÛ½·,,¶byÂ=ý`ª?šê3>¥òÐíÂÛê –,•¨Ç~Ú«b‡XѦÎõ"ˉ"uØY”H4¼U‰~;$Y¾ µñc˜_¾#@⟛5©FWµHÄäUyrßóÙZuR0ØÌô¢£åÿ³Öyѽáxç‰yÑ©óЬù›:ë…ûΙ½¶£‚i‡r7´S¶SYLîªË‘{h÷C7m¶ L¾ÒìbÛ@À…Çq=½yE6n§¾l§^Kôå¥ÐvúI†·¦¦¿ÕÂôiµ¾Ì4§Í˜@H @V $ Ôsô…77š´Db‰þ£NêèøE(á{ ŠrÐÊýv¤r!¡DCëT«‰%G$˜èÿaÚˆÝ'Žð7#B²ž} -yÉÚ‡ ¸Œ†èoDsß“ÿø›F£{)ÎtN¾&#$Õúíï&¢`„þeY´ E‘ɰ¥ûøÄ²ð.«P0=†‚éµ`(Cç±Q;‹åa†ï ‹6—Û©SÅ¢`„ºý´ çt=i]½ÄJ©·S'FÞya¨$J|î8ö•¦où1çÓ-@Á4ËÆýßTðí´»îpmÄB %ß¼1DR%>×F¾¯é®;ÈíÔGuêÕ?ú©³çüM–ä×¾]×?±…ÉGµÆÉ2ŽC@ùʈÅ%+mz§½jU¢Ž ý_¦nxèÛðÎ !S}ÃïÉÃÔc?íIô¨Y–TëÍI"a¤îé˜î¬EIù¸ò¯Q˜±ð…Y¯c^ƒ"`§ye- ãiÀþ&èoH÷›¿•k$iê3sŠq šMJ_?90&À<"€ÿ·8žÇÿküö%Ò Mw¼÷§Ÿ[é8ñÓuv-3~z«{¯ÁŒFáú5Éö[7ZG åìiÔ´‘3ž£äSž¢FAÃSiØjxDdÄŸSÑë;sp\+QhqÌîþ7Œ¶·€ü0ÑJíÖí4½Þ±3Ϫ‰æ0dzЇ۩g(OûD,Qêõ?‚óñp¾²˜dŸ„Ç=qKÆ-7L€ 08 gáQ¢.<,Ò·Ðú Š¢C¸¨×x߈@žíF-›K Îò:JØøêÄñµþú;îºB5]É=8Ï G]Š'þnú£ÿóæË;1E²zspÀí”܃sðœrÐ¥8·SÏQ*)øL,9ç¯þÇq·ó5>fL€ +_¼Ü`Ù ‚IþÁŠ™óíç¯õaº]‡dZGɽäø)gÈ‘ÆúÊ:ƒž>ï†âñgH®7ÛNi%×çX­@ŽÜN[ÔÆ{NßGÛø$GgL€ 0&À€Ú %ï‘á¢V›f­•iý ž ŽÈ“R/ÜTOœž'ÜñR¸ ê…™Öâà9âˆ<¹zŽRIÅ’—@r2L€ 0&À€ý®«ó•Â4:mB„µš¿Ö{±b§¢TÏ™ûQî±½ † ÜNÝcÙìSÈ“Ûi³dÚ~‘ÿÈÛÎŒŸ`L€ 0&¨èw†Ø“sƒF£ ’•;¡^¬-≞Jhî5qæ~”{l/h§:ÁÂíÔ=–Í>E<¹6‹¦Íù¼ÍÈø&À˜`I€:›´‘eILè; >¨ªzï›ê0¨Ú© h sÊ—|9TŸ?grVúŽL}ÊZ}4Ø,Už¾‡Û©‡YÍ{gL€ 0&¸Ú>g)íå|Ï#.IÐê!ö²1.Åm)Rç1“ ßS¯ƒ¶SgÐFwj)šW®ÇO¸ .yo _¼ÒÞYÝn¹Ç+ér"žh{;÷Ç­0àê']z±¨ÑC×^]ŠÛR¤¤¿€‘·~ úð.¸ÅµÍãë$Ê{®Ÿv·C0î[ ïȇ=N—pŸ[–ÜgÇO2&À˜$~ÿ‚3â*HùÍÃP±g‹ÛbGŽ…Êƒ»!ÿƒ—ÜNÃÕ£ ‡¢o>€Ê½Û ~Ü-Š(,ݲl奮&Ñ\<¿so.AtÍZô®zΞúÑmL]{]¥§·Ãá³ÜNÕeÙæª"سò~¨.͆îCîÆ¼?…G—±ê´+I´ÇïÜ[ÊH°]g±l5ÆùeL€ 0&Ð2ê9o-ÇtáNìèqÐõÆ_AÅîMÐí©¶Îf|g×~ Q/…ÔŸmL'Hû× ¨@’¿h.„¥ô†ÔiÏ@DïA`.ʃ<A5G3qfŠio/ƒóƒ”ßý I©PúÃR ±DÒ8ñêx$C÷?Í„ˆžÀ„Ï—®_g×|©Ä aò‡wù% ÎoË€‚O^A=î}:]>d«JV-âå‹•gœÿÉ}÷yÇéÙᅥ¤»‚ÈþiPñóFÇu×9xpíŽåLÀ¹Ò±G!¡Ï zÉàìÉuÐ{äC`3WAÞOpûÄ%‚¡ãç¢5¨3ŒûÓ6Làð†§!2®¤M| bº¦AMùI¼ö ”ŸÙƒ=¢bÍÙ»j* ¼úŒ×N\ ½®WòHiì[ýg@ç 0øÚtˆ‰¢<_pä ÈÍüD‰#jÃ`И@bŸIO‚œå½ù ëƒÇ¦CbßÉ`·›!wÿ‡prÏ{”ýÄžw×ÎåmT,hZC@›Gäq;u€ôà€‡áyeL€ 0&Ê4úpˆ~t¾öf(X¼jNî÷Î1<Ì犠:k/XK‹áÔ[ÏÂÙÕ_((Rš vUÇ^ø?0æ‡Ô†Jé»&Cß'ß[u|ú”£3>‰bꀒ†©0¬hå)ß±²ÿq?Š˜ (œMTŒ’vòÝCüØÉÊ» ¿5Gö)×»ÞpÄŽ¹ïü ¿|R~b¬[÷V«&zèhmV¨=~¸Õx|3ð h´Ð%u$ú%dm~*J2QÈ<Z}Ô¢5¦¬è'0UAæÚ—ù‘R ´ë^+ŠªŸ—þV±à ÿ¢£ áÑÝaä-ãý ÈÞò"”œZ§ÌS"1EiÔ”KíY(ÆyK;¾ý%”œÈ€AcŸ­!VIƒ¬XÉoGñô1dmå…?)×SÓ~‹¢k"d®ûäì|^ó4DÄöt¼×ù Sâè‘v"ôŠÐªTu.Ëù6û‘‹%?ÂæW1&À˜FEŸ¿‹Ž~€ÂÏÿ‚VÑi£P$•€…’d6Aõ‘=(zN€&:¢ú_ç~øØ*ÎCé†Þ{ èRÅ&+ÓÉCÙ¶µ`<™ ¶šJ°U–)iHÆ%Ý’•KÀ\|ZS‚F 1—ŒVžï|í8¿ý{(úößp~ë(ÿi½r=çM)« ªíFG'!îÊëïlz ‹ï¦ˆ¾â埂õüÙ¦·ù/¬‚c;æ¡H»_æê3`D¡d·¡¬p§"zta .ñ28}ø3°K¡ ë+´0 …𘎒“0" RѱPyöX-`1•*iØ,Õ(¾ÎÀ©}BmE®"¦Q ]z\£<Ÿ2ðNÌÇwp|×[@B§øx†r½ å+F뉭ó;pŸƒV¦)Žw:5lè´ˆEtÆü}ã|‹ýL€‡áù8¿Ž 0&À˜@°¨9Qg}±U”+Y×"š-B Š(n×ëátÇ};Š¡ðžýÀ‚–( 4_¨µ@Ãòï¼Â’z‚éL¾UÐ@Д!F´n9SÑCF*Ã;_s£ã–!¥—ãØù€¬býžzj²3ñç|ƒ›@9Z”(XŒç•½V©ì›þßýje¸Ý°ë8nÙЊÝeÎ *P®Ë­âŽMz_}F=‘ú@mù)宨1m:tQYr Ñ$¦âS®D‘6 ’Üê¸ѹ¯ãØù`ëç7‰º¤þ·Â¨Û+°Òü-ÎQøØOX,ù 4¿† 0&À˜@°L&—²n¯wÇMCðj75$¢(HsËi ‚bñ©Ü¿²gßâ(†-Z«Ä—­fÅŠ¥OLmô¼l·á<%+”¬ý ÿóf£{MOHp‘ç=[åy8ùÆ38=Jn…σ˜€„Ö#W‚ÍR£Dû ‡à554g‰‚ÝÖZ›pØ]:œËÛ ;¾¾­?ñ0ñ¾ÝÊsÎE’ðÙˆØÞʹú,Ùðºr.†£[窗[Ý[Måð3®7 â{ŒK­âòÙM†ç3´œ0`L€ 0Ð&`.8¥ ±£am4¯­54¤.ñÎû•ëä!f~Åw1˜ÑÅÆƒ ‡ÆÑ³äTÂ9TeîT†×Å »ÂSûC§Ñã•Ûå8·)~Ìd8ÈÒD÷ ‰ Cª”H¸žSŸG_CBw(^º"ú ‚ÈÃÀÜÓù|‚jÏç@DL*„E%)Öšò3»ëSßQÓ•¡wäx¡kÏñ.—\DK§!¢+˜k΀F}F<ÔèYQ‰ý§à°¼±?úÔY<‹qnSò€;€,[di¢{±½=«ìŠóîCd¢º¡õêaåUçxn]#P~z­AowCx¾¸ŽÑrþŸ­è¥î1òäšÃ3Ùb‚âeŸ("éÒÀÇD¨Í9ä@–ÿÑ|œcTýf¿ƒ_ýb¯˜¨Ü;ýɨÅáyýgÿ F|¾<¿ô(àœƒ¡k’âyOŸ þF_¨|Kηxê» ‰ŠExè×Ýy«A®Ž1g¼¶²0¡opÅ[]sÏŠaáŠÉ^ÓØ—29Ÿ ÷Úªpn•†Ø}×ïNÈÓwÙõѼ9¿À¼UâFc¿ìÍ哯5K ÙvÚ#¶tð˜È›}À[iÕŒU†mK Z}´rh³4nKêýÖö4?‰\ƒË’µÙh]„bA²Ñ;9ŸÐ¢ÑºU†W?'N1ð?hÐâe·Ö6¾îâÙ–šûàŒ1)àÛiÿê Ís–(ø€ 0&À˜p‡€½ºâ‚ÇÜIj"4?©µ ™šŸ›B®Ài &КÔ4¸#’Ô4h~Rk¡%¡CâJuBÑâó(ÂZz¾Ågø†Oð0<Ÿ`åD™`L€ 0&À˜v,–‚½9ÿL€ 0&ÀИÇfÃÅeÌ¢®…q> ñ‘ë,¢^–$™Æªœ]˜cªTvÊžÚ©UÖs;Uéxao• ÜN½À‘’àax^ÉÉ0&À˜hgt6-&symD ]ç9Ê^ªœ]¤l7ÛœW²½€»—^ªÉ8x ï«é“"])%˜¤Æ.áCµðþ*—IŽA¯úÜN½Á›Å’7(rL€ 0&À€©¶æ¬©[¢`&WǶÖçW`ö.KÄѤ‹¬•¥…—¹ È,§‹ßýóÅá•FûÍ»<Éh¶_UV b±\& 9 ôBkëA! ‹ÄÑ"G V3·SoT‹%oPä4˜`L€ úr¯lyG³2Sz÷½+/º7ô/Ë œiNˆ#‚Îäçn­g¤%ñ_¶×Ïîb´×N’EyÒªyÏÝŠž±;…‘³CÔ´yêŒ9x|=ÛB]ƒ nÿå2´ÞD¹z¯NY,y%§Ä˜`L ½ 8„fD:¸kÇñË'^_q¼óÀK<ÏÃÚA޲d·žß¸ò´ø“ÊÚÃTCëñõõ 3ž=}… K“ЩöíµöêÁJ ¼u+ËnÑÒ[¸IV+œ<[TxH—`¼._wiŠ%n§6‰|Û¥ÜN=dèü8‹%g|̘`L x ¨wÚÓº?Ê–âø÷šCþ_Qd2$Õðè1w«—øåE§ ç NQÏÖ™·»É†Äsë^ ëc±Z&¡~¼ÉX’{#‚ #8ÍJeYXw¶¶üwµfçÏG­´6X Ñ'-"¶ÓsºžÐE›Ûì£|ñâÎÙzB‰µŸpî ·Ó‹Ór-‹%×8q,&À˜`Á@€¾ß«BɆǶ­«V¬ëÑ»Ï [º¾ãØW¢¶…4ƒ¡pí•G."Šü$Éf)Û¸ô›¯œ;ÙKÚ+wþïæÅÆUÕTdY±™-æ”–rf" §ŸAÈQ»ôæ™–#(ªôW-–âp¯Ã­®öésC¦éÖèk#ßµBó ½¶ô¾Že ?I²s;õf{`±äMšœ`L€ 0ö# Z:H,‘P²ÐV[[mܳuóGO¸áÏS¯—¯;µš‡9µ±Žˆ[Yx<ùyÇkååçÈm8ñ%Î*ó6¦\ÑÉ1Ãê/^*Øí“d'UVU^‹%[´œ q$âfèÒ#c½§ÐkŦ&©:¹°n©k§ûLwʣ¿àvZËÕq«”àÈžŽÙN]åÔÖx,–ÚJŒã3&À˜\déP…¹¿S¶}Û6ˆëšð¤]zôš,Ë['°…éâ•H%JÇ;õòg/Þ¶våŽz¦Ôë'Î!kYZõfTWÁlœ$¡õ3Ü‚Z§S‹âH€rT6[dÖa†eS¦O׉#b|Šþi.ÞNá.yxØ·[˜š¢»ðœ,J$”N[‡v¸vz! ï_a±ä}¦œ"`L€ „0ì0Vañ’´ˆÔ ¥/öÔ™'ÌäpÌ81ÉpÅ€¬ÕÃé¿d¢PÖ?V¾æô&ç0!ÍQ¢¡wdQÊ=zdÉÚ¯>û£OâJ|‰sȈ%rÌ`:wúJ 3€,ÿB6Ö nQQ¹ÈT¬G’˜1é±Y!YЊ;Ü·šm§øDøúe_ÿ€Ö+Ò†ß\)u‘‡¾ySË,iŽ ½#‹RP¶SYˆÆÒ´\Âö¿Ãb©ýë€sÀ˜`AD@ò’02@³L½UêÄÓ„㌡1ÃSÂ…éV Vã?µÇw©ò”œ"­èÿKMjUžÜ÷|¶Zu’×aB`´Ž¹'¯wäÌÁn1UíÛôÃG{6o؆·iø] qÅøçVôÞ ð°êC_A¶N’\óèlî$YtêÝBàœ8´NÆ¡uúèf”—×uÏé-<ÔâåFíc×0Üèýº ˾Y_z¦èì¨k'ü¿-öûct9rí~¡›6›×aB@´Ž¹'¯wäÌÁnÅvº=hÛ)}xÚ…[ÀK[5œ1&À˜P¹ ÈÝš;7îݧž* °<*ÐÕ7t²”n²K#*,TZe°ãÇz7f={|ǦÝoo.É0 âôƒ³ïD_­•0hþµøõö©¬™Ãv~5s¶,‹Ú¬™iϪ÷Õýä7NYL¯cF®ÏšyIõ:í¾šùHÂm‚ ×â»C‘1iìžVç‡wè‚Cýlvé+ì­Âçžq~Ž]'ðà¬òQˆ\8gÖdןò]LI2Š$Y–/§·`ÛB7Â2X$ÁTRk_òñIó'«OWÓ<›XÜhŽ@$n4ì‰:¨Ê—|ÜS'µÙöŒ×C9Ðß3uÜÕÎ;‰I²Qžþ6I,‘)…Ä’jY¢8?àBƼ¨YBÇ 8´çþÜLŽZÉdþ_@Ž23´Ûû·¨ÍQ¤¶ˆ®Ä!7Ê/·S„Ð$„T;¥²M5g5¶Ñ´Esf÷hRÖ€:eËR@Ug† 0P!€Ë>íò]X‡X²ÈÖ_aOtÙêéýÍ(„p$—P+”Y_À8kK¹¯xóXÌI‹E•°¼ésƒç¾V–ícG¥ Û0A° |õÀ5UîÅx -Èg—ìo ¢¸‡à4}”ÏÛB@ÞÀùyÓž™síû/ÎÚÔ–G½WI’Ý®ˆ$5mtÙlÒʇ°àÁÍ¥4Tĉ$uè}ͧÅ@éë¼ó}õ«~GMê*”ˆ ‰ b£Z•H‘H"ÑV¥m º‡WØÏ\¡:f°I5ƒ1¯ #æ”Ç?Vì~Åz$ˆ7Íœµ©Ž yé@¨Ä\msT/ÜN‡D;m(NÝý߉žoÂj¬é½@;g±h5ÂùaL $ˆ:ñ Éj[_úg Ç(åÇN–¥»p×s ”ßĞŒ!¯úäð£C÷6\oýhçôþ•ƒyPo×[ã½Î±e°ÝŽ_’—P¢ë¢¬Y,˶Ùx¸Po8‚ûë-rí¯A’è>÷¤èRÞ.´~-8oMMÿª…éÓü:ܱ%‘„ö8‰ýK£WæaéÔ/÷Ô©cªŠ²œXr¶.©C ÔN+Þù r¡Î9ýͨV%K$ŽH,QÝÒFú€°(­Y`èg·Y' ¸æQ¹­àÆÖ3`ež­G8´Ný]#Ç ¥c‘Ú=¨í’2¢Ö·ÓÆÕ¢r ªvÚ¸ gøf„d=ûZ5ó’µ)o7Ü Ì#KY/œ+&À‚œÀ‘¿9†CñJ†¾vx eóÐ7¥Ú¬R¿®#†¬S‹&¢„˜8Ÿä½tY¾*]¨ÓàRÈúûài¯è¦tÀá4É Šœ.åâq:Ï|¬_ íq¨_Gê S‘½ÒÓï5Mõƒ˜ð*°žýEñ]ª(öúËœtE$]¶<Ÿ¼õ©A툪-µJJdYR'Õ7K¡ÜNˆ•‹Ú ¥¡Š$ŠHL’`¢=ÓuSÄS}ýÖ,ˆé öš±´æZ4o³[-)ôææ2‚•FŽvç:­N»ì†üH¢Æ¤Ñ„Ô: ¶é|LܹÖU—Ê% Û©+- ÿ¯¦ÍzñcŒ;·)ô)î:°X èêáÌ1&ÌОô…$ÙïÂ2l¶Û¤_ãWàoU‹• ;>Úì™iËQTÝÿÅ«‡þŒ—þE×= r öx鋸"@SS#Ø[›³ Få} ,œ3{íÔÙsfÊ’üþø~-ý“¯,Lnˆ$µ4ÔÉ¢–sgKL4üÎY(‘X"¤nxÒ˜¨1rîˆ#H´§î¹ü1ãzÈ1ÃÚ¼¥ÃqØ,ºó–'Ù­UôÑE"yAÚªcHk»ôøÞÇŽ.x¿.p;m¢ÚFUFÓN[ÎrówÈ¢DB ÿ¯Q˜±ð…Ù8œ<ð‹¥À¯#Î!`AJ@Öê¾›m;Z¦/yõ }Ÿh®(h§ãP¹mCß9ô­Ý(yÔ!ÃÎÒyœ³DsS”`јcd»@sV8ø€ÀÂf½Ž‚ ½tÃ|ÁV2ÇáO÷æ&DRÓÒªÚ“…„„õH ©‰$µCNÇ¡¨óIAe£ &µ3ªž«÷ëbûè_rÌ`—8‡Cž´úä×7cæÈÉAK3lÆyzaÙu7Ôé>Š~ª¥g‚áºÊšÛiCmT;mÈVÛŽ”9J8ôŸZ'”f½Þ¶Ú/6‹¥öcÏofL Ä dÍ| ­F¹_Ì?8­J)w?š¶1ýÑ }dæÜÁó2ß´í ð. =ñ$äâP¼¾jØ+Þ|õœ÷Þ' ¦Y/Æ”I oDOkpHÞÅq¥»ë0yQ$9Xý2­vDÉbBâˆ6Õš¤Š$uïü|¨«PÚ«±Q7õšOÊMŽ*­g®”D‰¬G¿@Ç ƒZ|‘v¬ý$ŽdtÌ0ù±Y›ÛÙ1C‹Yõ âNB•ÛiLâAAm´WÛ(íÕë' ­£Tç\¾‡œ9Ð%Ìà”`±(©0Y,©$xϘðÐÑÈÿÄ/i_·6')"æ’5U÷á¯.¼H¿}îQ¿Æ¡à:KoúõP‹l‡ûÐÚ´Ô½Ôø)W мôôX þ‚CLþŠCó>•k$iê3sŠeA.ÄŽ.9 ¸ãþOz@IDAThèW–7¢xg/Éxž\(;‚$h¤úíNº"¯Ò3 o|6•þõRÀü:RÂüw¡¤”ÿ6Ç¿Q_ÄŠÅáIº¬¸.šœÎç­ùqH-®yd…0K©­WÙY[ßó…öa¥&)œÄÕ´-Mn—_ðU66]n§øUÃíÔÓ€TQ(ì“iÁYZG ?ŜƜ?F΂aŽRÓò³XjJ„Ï™`^$ u_š$Ë«˜äC­%Kë ¡Ûï‡p8ÞFåk‘[¹G^õÐÃ7ö\{&Zµ,he:Ü[oø7Îðæàcõ€ùøšùÍž;’æ›àqOÜ’qs Äã ‚*’:µ.’hèœO‚sÌY@øäe(QƒP«í¦ÉŽí¦ËéÜE{<^µäP£Ù€‚Ø^iO¬ (ä˜aÕÉ¥#±Î1‰¯Á ªóÀ.È+vÂrpÈ&­2ÀÐ{í”é9žýà| 0ϰXòŒ?͘@$ b©VKP™ERPUW«™]÷zd7‹Å„VDy.ö|QÇ h9Þ„Ö» ƒÖ°¼Î1C«ÉóM&ÀÚ™Ãkç à×3&À˜@Ç! Š$ÉngKRV;9f(·Ÿ¹ ] *k™Íµ­:fÀbî#q¤ÅŒµ%„3ir¶™@ëX,µÎ‡ï2&À˜ð˜‹$¶k+_5 ìVeÍ£r[Áõ8i½Å¹G˜Ñbuh]„6zÕ„å¸,ù‘@Çe3ÓÛµür&ÀÚN€ÅRÛ™ñL€ 0&À\"À"É%LiÍ‚˜Î²½fº;ž„Þ)o•m–dÕm ¥ÆA#^ØAn½uzíòfXПŠ u&À‚š‹¥ ®>Î<`L€ "IX+-çIuÌ ö›ÐÙÆ$»µŠ\t+ŽTÙÓèiŽÕ­y$da½¿¯sÌ@1}æ°°Ñëù„ 0ÿ`±ä?Öü&&À˜q,’‚§‚׿™hM“$Iž´úä×S0ç±NËM5*.¦y/l’Ñz¦3¬¨s̠ʨœFqù„ 0Ð"Àb)´ê“KØ`í@€ER;@oã+É1C…õÌÕ’(Mdù¶Z¹v Ð2¯TÙSw†ÿ `GwÁ{q˜ \è9cÒc³¶²c>`Š‹¥UÝ\X&À˜ð&IÞ¤éý´V¿l¢Ã1Ãu(Š ägáqT÷êbôZ·†œ3hccVÝ0­¬Âá˜áñtïgŽSdL (°X ŠjâL2&À˜@ `‘HµÑ—uoGÇ[MµãdZ‡BçI¶$“8j6 c´mG3ºõÖ.Ÿ4Ó’Õ £Êš}„/2&Ðñ°XêxuÎ%fL€ 07 °Hrœ#Ç kò–^’]qëm®©v8fhî•(ŽŽZŽDÜ$Cïu옡9J| 0g,–œið1`L€ 0f°HjJ;]ZóZD’l5+â3LÆ!u±-e¥Þ1ÃFZ§‘ÂVÜøDmaƒõˆ3´Ä¯3&Ð@€ÅR >bL€ 0&Ј‹¤F8Úåd×ûÉç*K®–d‰†ÖÝj·¶˜‘:Ç {dZ'ŠbÆMÎÚÖà˜¡¶ÅÇø`L %,–Z"Ã×™`L Ã`‘Ô¾UŸ1_?È&ÙÈkݤ’Š¢‰¸¬¡Å p†ÖuÆoe…‹¥ŽRÓ\N&À˜h‘€'"é• ýÁ*E>>Õº¯ÅðFÖ¯¯5ïÝz™d·O´¡c†«0‚Ø(’Ó Î=ʦaÉ1ƒÖûvÌà‡™ð)K>Åˉ3&À˜@ ðD$©å²Y,Ÿâ"¦6<£^ãý…Ö¾‘lÍÊÐ:ãÏ'ãк˜ cÕ]!Ç x-«‘ ßÝø8;fh‰_gLÀ·X,ù–/§Î˜`HÀ"‰ŠõÒBÍd äLä¡sj¯jªm]·]²DŽŠ«J®$Å1Ã-V3´´ l½c…5€iòc³¶×9f°cÞÙ1C»T ¿” 0…‹%nL€ 0&Ða¸+’ÞÇŽÿ´i…ôÚ%’5 }M–ì}$ÁžŽ ;´Xúþ5ý`«Í¦¸õFÇ pÒ‘¡é”#µ±ášGErýšGÚ˜˜Õ7L+«¨›¥„ŠêqBÉ 0&Ðþðÿ*L€ 0&Ð+_ý¿›ŸùÿѶÀk§¸ªH’eùòÆYLX‹Dþ¥Ë–çã<†Ÿ„§˜Ì¦¯ê?nè„ h¸]³áŸ …ŸÐ<Ý‘¬Kä˜Á\[;ž¼Ö‘cüÃP34 ˆLDlЭ· ëVL~œÝB<¾Ì˜lY ˜ªàŒ0&À˜€· ¨"  ´Y$©yùûÿ|_Ü»=góïñÚGêõ¦{QŸ uë9f°ìÚ:Ò.Ûë:f¸9ˆŽ¯M¡EâDÜô½lpÌ`n“Ï™`I€¿ˆdµp¦˜dlY äÚ©Ë›*’ÚjIšÿAXO«dù«(h=ñ åˆZÒ—ê‡J`]ŠÖ¥ÁͺôÂð³Í¢ˆ#AojÕ1@)Z6м#­dXyãä˜`L x °e)xëŽsΘ`M("É.¥£%éŠÆ·.>ÜNëÿl²õî¹ ÅÏ9ö¹'q.Í“S-‡þ¹PÜÓ¬K䘡¤¢dŒ K“ÐqÅ-&«i@õ¨~Žp^ÒnÅzÄŽšÀáS&ÀB[–B¡¹ L€ ø•[–üŠÛ¥—©" ;÷n‹$õE/¾/¾Ö‘ƒ83­¦÷+AŸ}êÙ½¼èŸƒí²õW7¤5ëÒÜEšß<ùÀì/ê¼¹©©ö^uÌZŽ&`nõ-åÝz¢×º @ç ºØ˜5uŽZŠÍ×™`ÁM€ÅRp×çž 0v Àb© ·ðJODÒGõ +´åÝÒçƒéSr“hh(žÙnþ!Yß3­ÄVØÒ8ô,NÐj¦ËvéÏ‚ lxúA{‹s—ZÈj@]^??º‹Ð1ƒ$Óðº›Q µê˜; [3€î;vÌPUÉ™aLÀÇX,ù0'Ϙ@è`±Ôþuê‰HRs?oqT‚ÅT“¤ÝQ‘qwNÿÝùJõžj]zfªô]›»Ps‡,KóÑ┇ÃÎR¯0n`kÖ%5@Ù“cãž­£©Î1æëJ€­õŽÐÐ:ZV ëµ¾Î1C ”†óÁ˜ðÖþ£ô_.øML€ 0 "Àb©ý*Ë"Iͽ,§‹ÿ\øÜaQH’4M§¸ùñ{kÎÐ}².Yìæ Iúžƒï½÷”‰®‘%ªÈš7]dÿ^£ÑßöÄýíúzÕ›áÝscÌ3Z †ÊÑ\À΀â˜AqFgXyӌڢæâ9_6/'Á"Z;e=:ø¨óu>fL€ „K¡T›\&ÀüB€Å’_07z‰7E’s¸6RþSJ©/} ½-GéZþ–Çï5£8M­KÎÏâqƼn‘’P:…ß$\÷ˆ†Ö934Í/þøÛp…0Å1ƒF#fÜø÷Y;Ú2Çjäû…5UçvàмCY3/ùMÓôùœ 0&*Ø^¨Ô$—ƒ 0&‚T‘ä®w»—j&hòÈ›]³xd(›ÿitW­¡Ër«±ä×6«eëÜ…º›ŸšjýÙ Ñÿ“¬KhQZ¤Z—šM£/®š¯"H6Å­·M*™€YQ3 Pº0PP7´NÈ ±knžNÃ)¦ðhú…ññÊÐw%ÚÒÕ‚FsêÈŒ!{ÔH5U¥o˲ðŠ®§ÒeYLL„`L ô°X ½:å1&À‚ž€§" ‘Z1S²IýeÁú ¹³Y([LµÏ rÍMèÝíKÜ>•eÛÒ—jï›ù€)cî"í½K¼àT³ûû"9f0Iµ$YžZìÖÄf…Q]ÆjÑsݽ։’nåäÇiØ ûüE³>xþlFé3Lc‘l·?:h~fFÖÌaÏ“8ú|Á¡íÙ3Ó šwàî¯^=4ûù¢ r&À˜@`±„•ÆYfL€ „*OE’—«tc—¨O]XdÍ}â¥ë.}ò~ë~§ûõ‡B1:x]äåOßW}–.bÜãv›í«W>2à&ó€Ýõqý¿#Ç ¦][F+CëpÞ‘Qª¾Âá˜AÕ=NÙBasý6dà<¬ YßkCcŠèpöç»ápèk‡†£M*çÐÃC«Õ«è6ýAïËš™¶bò›Ç 'ÍÆýCæÊ@+ÒNŒ³H‰'B†M†IxÌbIÇ{&ÀBŠ‹¥ªN. `L 8 x"’¾ür¨þ׿>diTrrÑú’JÃçÐÊô²Ýn{ï_`]Âad%(.ö«B‰Ò Q…î´¢‹ðfäH£·øä$ãÕð6»çI“Œ?o¼QuÌ d¦IŽ0ÿ¥˜‰õ2z­kpÌ@‘hT\ŽKùôZæU6»´^0ÂoñoÕ‡0•î †t¾zzóÀù^—Àþg<%±¤42ØçáÉœúK¼cL€ „K!U\&À˜@pðD$QIÉò²ýØÆmèŒá¿ÏL“^s”^N¡“ƒ±tžÔºu©‡¦]°Æ?…’³cÌË-6›©?å»qdÃ[»TëÑM3gí¬sÌ€#¡–kSºàPg›Mz-RÏãÉBäK‚,ÕH¦^x-“Ї}c4ÿIÇjHë1øçÌÜG¾òV›3¢îÿ†Kj«™û7¤¦¤À×%â&À˜@y"€¿Á\˜`å—Ì©¤×B#Ú©¥®i-IÇ»¤7•H®!ËT¦ZUI×+ÁÔI¾hlxŸê-%Vö’âÉKJ XµÃLM—.(²~N–•sߟx²,µwš³«wIð‡é™snR:þAZ(+Óºüx>&ç¹; fmi)«uYú)d„:õK¯¦º=cUp nhi2s$=)+¦¡º¦WÓuÕ‚^U/O¯Þ~)ML¥ZD`† =u€Ì€µ…Ú¨v¦‚ÅfÜG«4Åüó#ïd9Q§YGëehÖ ëm¬¿ÕŒ'dRž8<¾íÁüÆE‡ƒºÉãé£o¶>*ê´™½®J!裿£M«û7É&Ó›Ù#ãµ™qàU]¦ŽGÞéð†£¿3&ÀÊ –ÊË•äu0&`;gާýjrU×û¬¨/4}4|xÌfE­^Ù_¨äkòõò _/òóö$o/O2›d’! áÿy¼KÇzʰÚ(Ýj'ëÍ÷”t+Å'¥P\B"Å'§k0cÊ´dY‚-”´4}:ûÕÃ[Zõip°8À1‡Šø^ZBRv–S¿® ¥$®ÄÜÒ¼J›IÇoüq=8P«ä^#£å7 Éù€$Ú+IŠeÒû†ìíùY˜Zwo顪šކà–ꑘ!p« įlÔ¸Õ†v¹}°òh#4F" „•äLª«4”GÞ.Ëw ÂÏ“‡ôÄÁ×;œ„¿Ñ\ÌáŸòèÊ8ÔzÆÁ0׳GÇw˜#Qï>:°ÆB+µ¡ÕÇGZIvÛFhZÛï×ꚣŸv‹y<ûl[;?pøp~gL Ü`a©Ü\J^¨¸-Z¤ürðøƒ’¦¿ àMò‚À¢×©^IoÞ –ܨNuªYµ^þTÅß×é RÒ2è2RÖ\¸G1Wãé•8ýÜ¥8 •"æ¡ÈÒnlBVtó·ó"&vú\¸Ã²’²/úÕýUõúÏ0[Ûf$f•}…ŒH_Q¦véBÏà@ûÆìmœõYfP5ëC„À ès°#0CžýKt BÝz¡=’LžËz+56Ïzù¦p¤Úš£Ö˜‚±^@8ŠS^&¿'ö¾Õ$^4ƒÉÜLŒqêð¸öóZ}tèqŒx õ̧Kj=ëÀƒºJo@XzÄQ§ÍŒ?Ú#€Ã÷0·à~>C·|Ãù 0&PN °°TN/,/‹ T£-sýTõÆxlG©šVÃÛËCëÖ¦±Ü±eCjV¿ù@[t·ŠuÖ阫täL,9«8Õ0 TdeªÓ'õL“~²XÊï“ø²’²_稨º>×)v $ÜgRL'üͶ#ûyg}týúý„#˜v>Ó:Ãä-¯þñ­ÇïB8ÒeeÕÃo[˜¡pÑöZÏ<øœ‡númÿøÖ§}·›y°;´§ó ñéÞzÖþü.úÐ㈂÷´bV†z£Ý9‘' ¤ŽÃnú-Å(Ujýúv¸æÝ^úýý´×¥kÉšxzÕÑï5ºFÅø¤§Æu¶›L§Žm]dIG?üΘpG,,¹ãUã93 N@dˆµG¼"ÉÒ4UÕ«ßÓª‘Þ»S ©m³º¤À|ÎKBR*m;p’¶î=¦^»‘¤@Û´O#Ó‹Ñá8k¾ÂqÎØ±ÖÂnÂ5nö~}s–7÷L¹xêuOÉï‹·ÿ–xç ¬ÙðyÕ öªf‡æHøÑ¨š`‰ÎC‡p$­"Ê«33Ðy>§ !‹Sîа}_PATƒ%æð4EuÒÍÉIN›*Mµ³Ú‹˜×{ŠIV£ŠÏ‘ØëIgýü;Ôß$Ù@vn¼i‡Þn·2Ÿ¡„6j¥BrÄ¡qí¶äW‡3&À*–*ÒÕæµ2r@`ÜŒ¾I‰Ö%ð%ܦI]}X¿.RãºÕÝje;ž¤ïÖîÔ’SÒ ’ƒ£#BDžš‹ÐJû°¥]·õdj-F-øWÕ–¹.´X5Á£*ê˜E'8n‡ ‚QH0ÏÊ€Ö#æUÇ¡È8¡KÒ ¨¸N(&Ó‰šÔëŒÅÒ_„¢.qq!©Ä ÉÕÁò9~5(=m‚€íÑÃfó*F`]6//N`†\Ãg}E`…$É[Žk! mÌý=2¾ã_2¿ïÿAÑ•Oo·®íÌCk¤}%/Ãwn Þgy»ãFø0MÄýPëèøŽoeuœëƒÐ.mx¥Iz®Ãü• 0&Pa °°Ta/=/œ ¸·,³ª¦ki«Q¬ËsÝ+Ýß¹¥û-âæŒ30âßË·Ñï‡NA¸‘'FE„LϾ˜@K”¤]ŒÍnO#¸*ºÛ5Íp¸2™dÍß×›*ûzËþ¾^TÉǛĻ—§Á‘ìv5ó¥jdSUJMÏ +qIÚUøUeØìYª7(áRuMÚˆ þ:É,­«KÁ{‹jXÞ„$˜!í÷-=…ßL؆@ê#ß+qâ¡=BRØÂfÈ~ ûftµÕ4m·BÒ°CãÚÿÞæ£ƒc ·‚féuÑG›™GC@jtt\Ç â;£®˜÷RÌü(‚Fü~d\ûIm?:Ô÷Ç×ðIj+êpaL€ 0;È÷€;7åL€ 0çæumÖC#~1Éò€QÃÈm›Ösîàw¡7lv)úûõ´ïèy;6æ­©AÍsòùëØ¤kÏ#0Äð…ñ†Ù”Ö f5hϪI AZ´ÚÕªûŠ3夔4ºr# ÂS"‹½N‡ÏĪ—®Å#…ˆ(%@ÖZóÆïê*u¿·X^ÉWÃPž„¤_>ñnh³Z‡d Hô ´Gþù²E`D‡['|¼ÉkyÿwS.å[×É'ÚÎ:4LÓÔ}ý«wNM¾öd8áº?ôŸs ™ÍJ‹ŽëpcX#„ݶS<ü–z í$„¬µ»¶Ÿ¸¡¿ä¢c,~gL€ ”W,,•×+Ëëb.@ 02²Ž’.5·“ÞBÖµæØ«5‡ÙX˜4ÕÂ{36¥ft26ëÆ»ˆØMËUH±šª_À/Ÿ/!¨œòãL|´ï=ôèýYûAXeɦ „—ñ/‚uœŽpÖrS˜ÓUF(s­kÛ&r·¶©I½dR Y¦dÐ:19Žž¥£g.ÑÁÓ1¶ø„d3|ª!8ýÓd–¾˜g Ùëh^„$˜A•®õ…ÖNhDΣ3@ó¶CGФ¬zpœU|Æ¡»S 1Š‚ à R „»‹0ÃûÌ1øW¼åûN>%MOHJÓRÓÒ i¡j?õƒ '³Éäè¶\¼[¢–ØãS¤Î-*ÝÛ7£¶MêLîÖ⎞½„@Çi÷‘ÓF)\Ÿ=MãO­pjyD[sj2Ù²X£Ð¨,ŸíÙ^²Û Ó:GÈÁ•`œ;—iZ‡°Þ^•)n`†ÒX[Ç—|3¤«;ñw¬¤KчǷÿÖ1ŽHKʦÃo´ET<.L€ 0&à ,,9ƒ"÷Á*0Ñ–kÛì¶W ‰x &B=¡%’}½<µ¶MëÊ-Õ¦ZÈoTBR@%¿b›Ž!±'%@€òö4ã•oð1·½ V›ÝH†k2•®©¨€Ò|w;|ª¶ì>ª_¸rC °ÅSÏó[¨aâit%¥ã…²â1­Ëç].œ´Ì e¤ „Ì똨V~ëϘÁ¤›~ðÝ —6Œ°á¤mE@‡‡D@‡üÖÅÇ™`L äXX*9Cî TH–)­É¦½-Ò_ÀŒÄ¯zûæõävMë~5Ø€r)Gö9GK×ÿŽä»ITÕžã“‘øþÇóv•%îŒêj¾’°¯ü½B0 „Õ¦nÜó¿ %:˜Ö[^Õ¸a«í†²ºÊZ 3óöÝñFaêr&À˜(>üÿ!)~ŸÜ’ 0rL`DHÄ}°œ›–G=̲~_§–òÀm =ªTŽWÍKD¢Ý-{ÑO›ö©‰)©ÌFÿ”ü̯GOœ˜p7­éÕȪنˆ¤°˜Ëà‚3à;á·Îa«|t¯e˜án°á1™`LÀ9XXrGî… ”{£-sýì¶Á!>ÐÏÇË> {S¿®­ÉÇ۳ܯ˜“€0\µí­Ø¶_C°ƒKÑ Ñ“CÖç¬åüoëçÖðKO‰ë+Lë =Š( Íóÿ¸Ù!@m‡€´JV”UCÞ²þ~73ä7O>Θ`®M€…%×¾><;&àF…M¹_“èߺ¦5x°W{é1D¤s5ÿ—UÁ&!BùÃ&õÒõD±¦qÑá¡;̰r¦gÒ˜ABä:îGÿ9­ƒ@´BVé•׸R`grᾘ`L ì°°Tv¬y$&àv,–õ¦û–i˜ø[Õ*ë¯<ÞGiŠPÖ\˜€ƒ€Ð2ýsÙV}çáÓS䨨ðàQ%ÑàÒÓAP‘ëÂ8f€éÝ&aZg&órWÌà`ÆïL€ 0&à>XXrŸkÅ3eeJ@J±ömÿ?È30¹£'t¥ò²»L–óÁ–mÜC?oÙ‡UJÿ¨gyµ äÂÙQˆÀ W“öõD~)aZW`` üD Ó:I^Õ Q«Mî˜!ûÚù3`L€ ¸>ò•¬Äõyó ™€[X´h‘²æÀÖoðtÿ™g÷ =ںży’wÀct&-Y·ëå{ÄUÌäÝüf³b¶Wc²[aV§¹œ°{#0C^™^ñD}Ik¡­ZåE^+ûOHAÂUQžRt(¿!ø8`L€ 0§`Í’S0r'L ü‚ÒÚÇ¿Á“þáÏî A©MùY¯¤Ô |·æwZ³ý!TÞ "C¿ŠÀ ©q8r˜A"d¡íB8"YYõÐ;Ö%1ë+õóL€ 0&P® °°T®///Ž @`hø,8Ò¿Å‚RÑÙq è| Íþf•zò\lÚý¾ó>/Ý.÷ã¸9_>9D`5à—>®%å[—O0&À˜(C,,•!ìŠ4ÔØ)SjXS´6$K ‘š¥¤ëuÆ·26L•Ä;n<ž+ø.\t=煮x•&éR<Ž^Á÷˰´‰5™¥ã>¾ÇgŽŸR‘Þµ…M¹W×Õ-ýº¶‘ÿ4¤çݘYÄ%$“%j1"3ì¡®Þßß¶"ø‰ßòF¡=Rtó Ìp">À˜`.B€…%¹î> h#šÁéú7ƒdYî®jjíìkòô0«¾^º—§òòx(&YÆ> Ï’ñÅfWuDÕÒÒ­V=%5ƒRÒ­¤ô÷§"KçUäÄD“mf–Í =›} þ\2cçÌñ´_O>XÙ×»éûAOÈfvi,ÑŠÝzéú]´ú×}4È÷còU⅓ѵNÖäU ›´ÚÌ*öýÁ«gL€ ¸ ›Qw™4ÏÓ5X,ºc‹|Ï8DLë!fU=À_m^¿¦R·fÕ«Q…ªUñ§_ò„ãwQŠ0åINM§ë It5>™._O  —ãèLÌu{|R :“tÈ[; OM‹Ž^z§¾Ç͘ᛑ¢d¨z€®Ùý3VªŠ ~VUÑSÌU}c?}ýõŒ;õSžÏ‹'M}ãù©M“ºåy©¼¶2 ˜œFæ,Òk™Žÿ@•ïÆ z3år ËC0&À˜p*–œŠ³ât6ÚòamUµ‰ ýëÕ P{ßÓR¹§UCªZÉ·Ô!\½‘HO\ ­ûN¨ E–W˜kø?)„1S¦T³§Ò:©!oµÁ¹Žš¦7…–ª D–˜³¾è„´sˆM|¦€»$EÚeò’vÏ ¾^ꋺËŒµL¯oµÛN÷êØÜô×G{ßåÙððå…ÀŒ¯–«§.^Ù&ü–¸0&À˜p;,,¹Ý%sî„ß>Ý?>ÑÖ 7B˜ÈÔ‡€Ñ!yká»P™ðÝD:Ä(zðýº$é×áftFt/šMJûçî¥ôlßÔ¹“*Bo»Ÿ¡/—nÒT]ûU‘MW; ?($ÇÔ«øiu«WVjU­L~¾^3@òñBðaO©p¤RUÍxeØlt#)•âS(>9U¹rC½Ÿ¢ÀUÝø}È’|P'íÅ$ýT‹Bv6L–q׫†EL×5ýÝÉ£ž¦šUýïú|xåƒÀ·«¶Ó¦ÝG£ÂC+—ñ*˜`L ¢06ƒmÑy½£-sý4ÛaÈPÒWQè~ ­„pá`âåå¡VöñÒ!ÉŠ¢ 8ÎH2Á—HËô%Ê d‡#}ôðR‡æõMïÚûW˶Ўƒ§´f jJ­בÚ4©G j ,nI˰ÒùKqt:æ*:yQ;q±&4aù'Ç"üñ³Ù#zžå]ä{qÿ’*üØ•¦õjV÷ׇÝA¼—!ðÓæ½ôÓ¦½$5¬éds™‰ñD˜`L€ ’ K…åîÕF¼ù°¤iEH…'±Ù÷ôõöT›Ö«¡4®WƒšÔ­NÕá[TÅ߇ ãÔŸa…»¦BKãé2XìªZ"áèN IK·Ò¡Siû“ú¡º"ÄšÐ)&SØ|ˤSwjïÊçG„My€4uƒ0¿»¯S Wž*ÏÍÍ|¿v§È¹dŠÍ?l¸›­‰§Ë˜`‹@Ѽî+›r±Ú ÷"J:M‡6¨«¯¯—Ú½mS¥{»&$¥â.03XƒkÝ:%Ñ"†ƒ7Løºµm"^’ð™Ú¼û˜iÃî#²[ÕgaÂ6Û¤„ϳŒ¦ŠnW$]Œ°„z×6Møá‰Û]=מð…+7zë®=Kž`L€ 0ü ¸ÖŽ7ÿyò™"xÓ2»JŠ-嚪 «ZÅ_}¼ï=ß"Ū»ˆÝqõljT¢§v£A=Û)?lÜ£lÛw|¼ªÅ?úá£QïÏVÕ->Â/­s€¿!¸Ø´[,”'Y¦RÓ3èøùË*©´®LæÁ˜`L€ 8‘@–¯Šûä®î2 °iÒÕÔ½p9zì™A=(|Ô“J¯" +œyi*ùyÓ‹ÜGo½0DòñôlFdÝ5ÊÑË™c”E_0)lQ#Àße¥Ø«ñôó–}„†NY~†ÕFGÎÄ–¨/1Ÿ‹BKâÆåײYÄÆ]GÉf³+ŠYZX6#ò(L€ 0&ÀœO€…%ç3½«=ŽކȾÅßÏ»þø¿•õlKŸ}WçTÞoÙ°6…þíQ¥j?]£ÞˆŒ¬å^k–„ÏZY–%ëvÑÈȯ·?ú}òÍjнoLá•8Z¶qüâ†Ä åЩúôÛ_øØ^¬Þ„Ð&æc˜”åêÁ±ŽÕ¿ÌqfÁwëéëåÛrËï‹äÒ3J?öƒ«³„ÐüÖ#Ž_‹O¢[öið‘\6ϲ· º|Ž 0&À˜€+à]´+_"ÎMäÒeû ?ooï‰/=¢4Fà.eC  ï¾6|*ÕRSõie3ª“F‘äô²Ø@çžm•J>6býù¡ž—˜LË6íÉQíß‹û¥KëF4ûçÉl*«c$ú¢Ÿ7ï#‘„µ¨E&YðÝ:ŠGèúòR„&oÁwT»¦¥‚ø˜ò².^`L€ TL¥³{¨˜,ïúª­©Út“,5=|€,6¢\Ê–@íê•©WûfÊ–½Ç†¿5kÖèÙo¿]ôÝsÙNÙM–ôs×’Ë<¼IV WjZýï—ßI„lE˜Œþzà-‡"‚k<е5õíÒŠÖîøƒ¶ì9MÞã$„Q>ú×J,©NOèF?lØM»œ¦ÆNÍê× †Þ¿™+´hõvš<ê)£Ð`ýgåot6ö:U«ìGOôïB[4  »ŽÐ:ôŸ¡§U£ÚôpïŽÔÑ"ïTÚ5­Ga:¸dý.zé±>yVÿu?Ö²e?%§¦SÛ¦ué/ßK&¸ˆ}úíC«4û›Ud‚ø‘û;Ñ/ÛÑ{#ž¢ïÖüN{Žœ¥w_J•ý|hýÎÃ"L>MxùCS¶xÍNÚ}ô,L‰º·mløÒ‰`'ûާmûŽÑý[Ó±öŽ-êSî‡'"rŠÑ˜? $Oç«×oþ¢µÚEh‘wà©y‘!çó™`L€ ¸ Ö,¹É…ºÓ4G‡D4ÀÅ| ›J9÷¦èNmù¼ó4¬SMäcòI³5t^¯¥Û“]ÕN\KHQKw”‚{‚‹<Í™›v¡éÚº÷==¨;5¬]ÍØð S5‘×K;œŽ1:¼t-ŽŸ»D­×!‘ kå¶ýôìàžôòã}¨aêäãíIV$†0Hõn$!þ Jrj–^Ô§sKª!W”ª•|éÉþÝè‚ ¢B[T8ë11×gu#!¾xÕè+û 5¹ÀútnA#Ÿ`ø>m‚À'ÌcEdJQžìß5sε«“ð-†(»‘tׇŽÞô¹:tâ‰#¢ÁpÇ¡Óô`¯ö4äÞö.Oæ‚âœXóa0úfÅ6êÚ¦Æi*g•åðÁÚ~ð$!©´Ó%%rúWËU§*"pþ%jrÈ/Yò&À˜`nJ€…%7½p¹§ oŒç?É4¸W»Ü§ø{¸xYTÊÜÍ—áØÅJ—NÆ'&KB€(Ë’-Ë—K7Ñ{ó‚ÆC÷u dýÍšÂãtÁF¿1 Ã;BßÓѳ—¨fÕJÐÕ4„QQhUD 6Mê‡аœ€ð$ˆþr—®‚Ó3nz´oFº·É>„v©së†ÐàxSKh–„@VóD!ÌÜÓª!!²!Ôåó!ˆ sÀªÐø¶‡À'´EB3Ö VU£zcv-àûV¿Vùùxѱs—éJ\"%Cã&‚³9sɘ‹˜›&uŒ6¿î?i[ˆÊ("3¬¶A`s«M…¶­+4g]³´JB[%4oBË5ò™«8ª—è}Óî£4yáêåë I2éFE†þ·Drc&À˜`.B€Íð\äB”tº¤·«ìë£ÂwÆe¢š•tMîÖþLÌUÚ¸ûˆ É‚'LHr—ù{˜åo±±þ`Ûþ㆖¢ìæ­àží 31á÷•½4‚–N?ßÌäÇЖˆr_§æô’¾!e/„ŽÐέ”0™{æmÂnÓîcôXßNÔ¿{[£ã?7SŒ œä.KÑn×á³Ð jƼ !ZÂ\î6â»C úÓƒ=)üóhë¾ãÜn }BÃ#Lû¦~ùó­æ·Nß:vó“ºNBK&º@>4jÁí‹89Î0ÙkÓ¸.´M*¥@fŒŽ"/a¢(Î9Š̲Áé_?o3Lëgk›½NQ> mÞw¿ìTÏÄ^i Öz˜Ì¯|f™à> вX®Ë˜`’k–ÊÉeǾªŠ·G9Yû-ã÷C§hÖ׫U8jœ÷4ù»Ó æZ‚A˱ióî£Î ?WÈÅûûx>>Â)· $ºð0çý,G$V!ìós—®CXBÔö›Eô9æiê mÌWï s0ïË^|a–'Êehm²—³±×`ÂwÂV/Š|íêß­uæé„šìíÅç:5ªÀ·ª ¡Kh›ÅËÓƒ YšõÎs·^o?ç8}Û{khŽÎ]Šƒyáej Y‹µ akïÑsT«Z%þˆÂ/I¼C““Õ^h¢¼<ÍÆ9ÇAs®ÔYBø{éÑ>äíi‚дÕQ­Xï;ÿ8M3ÿ¹‚2%Ú£KJ( JÅBɘ`LÀ… °°ä§ˆS;z5.QrV¸å"Ž]a«Ÿ„YÔ¼ÿ­Õ¿€96ð[t٣맖×sîÄÝŽNs¯ÞH’ÇØÕ‹FÐ¥ucú ÁjW« ߤL Ô˜@ ß!dõ¸é£“;pÐÔ ù.­‚oÓå뉆_Ïhlâ2£Ñ ¡æ:Â^g™³Á¬¯(Eh³„@²ÿø­¸Z60Bi‹àV»5OhdDf…BÛs ¦ƒ"È…ÐRµfIøÿœ¼x…ÚBXóZ³ß`b×¶I½¬é´os>PÂ<ñ^âsÇwv•»§UCú¿a}9n†&ª¸¥yýZ†æ*¨²¤{H³ïz/òl`XÄÔ@Ë´;O¤¸s;&À˜`eH€…¥2„]šCI²¼ O³å­{—æ0Y}‹'ö"OŽx²_˜r%.é®%õfIÂoÄ™EøƒÌþ÷JmÆWËéÀñó:"MоÅÀh˸kΧ¬úСÅbY–·"7&6ê®^îíÔÂjz@ƒä("·Ï—?n¦×?ü·Ž[=š˜ìEh–^~¬/B †÷|O"؃hסy=ÃO部¥4åËŸE®Uöf…þìãåIÃúuÉ2Í …/”8ö#r5½1ãß4aÎ"Ü3™÷£˜O?Dú[-ظ¿5Ììª!ç•È{eG ‡ (Lóâ`B(´NŽòg˜ý5† (rHÍþz¥!8Š@…)ëÖ ¡}:!ávCh,L›Üu„fëOö ÐÔÞþ³ôòã÷S‡¦õ@÷.&*04üë×BÛänÇß™`L€ ¸"™¸Ó²*æ\ñTw³·§ùÞ‰/?¢8"f• !,Mùr¿ú˜­ìNãüý‡MTOѽÿž;UuúyFyé†]ô~àNë[ä’ù !’OC³$„TÑ1LÙvëšö™OUßoÝ%lxv ##"êQ†| NÊ•qÉ"´µ«–?N]¤9ÿùÅ0¹ÂEö"ÂW{ÃôíNE´ð‚Ö&û:ÅuÍ­ºS?E9Ÿ”’¡œ¦r¢½Õ†-È+åa.:sñ0@h¤J+TQÖ'êÆ'¥Ð†þóˆj³á©Éƒ:¶˜2|øð[ÎTEí”ë3&À˜¸KXXºKàKcXñ×.K¿ùùxW{ýσ•ìÎßÎ/·°ôÍŠ_©*rÖˆ¼-±‘m³!‘3F<ÁaŠBf± õÁ&öÏCzQä}É+÷ŒõœWŽ˜–êÐFäÁùbVÿz¾Ô~%ÂGE›Ýn<߃èhbó;Žý"¤²0“‹^¼ÁÈoSÅ߇Ú5«gp¥íÂ<Ÿ@.ŸÅ,B`‹ˆnÂË‹ÐdŒxª9||\e-B»r&æ-Y·Ëˆ*'4\\“@Br*}@Û‘ ¦†k|M>šmy;3&ºkN™gŘ`Là6EŒy[|ÀUìØ´>¾kÿAË­Võ‰Í»ŽøÃTLÅJ㉳ˆî%ü"’enÝwO“SS$Œ0ÆBàB„ðOùw#jY§– ¡ìjP»*¥¥[iæ¿VÐûÚÓ 5ÛALÅÓñæpf?Ó>áß!üODš®mšPRJš!$%§YÑGg‘ÑVÿvB(ÂцG!|¤¿Ây½ÂFÿ 7{vhŠ0Ìž$rñ¤ãɽȽ#òñø#4³³ŠŒ„&|a„éV×¶áo’løœ¸ó“!”ìD…VREhnW)œŠ1„o‡i›#1­«Ìçq‹€ŽtF¸ôêþtðÄÅF6Íþð½†}³cʲQkJü‰ 0&À˜@‘ °ÏR‘‘¹vƒ(KðŠ\¹NÒ¼U¿Ôßýx‘öù’$"i £4‹ð×øËÐûŒœ3÷vlAGN‹Ü0„Ü1U]U+û¹dD™ürÏ8æ—WŽqnX¿ÎÔI6³çÞÇž8ohjUó§– kQm$šˆ²&œö…y“Èc㬼2b̼ŠèìŸÉTÝY·$4cñˆ§ÿýgZõë#0A^ë-ëc"ïR؈aôÔÀnÅ2Y+ëùòxdä‰=¼?B‹ë¬öDοÄ7`L€ ¸¼cóºÕx²¹ ̳ŒIƱ×F[¦~l·Û!4¼Š0¿Föˀʾê{;(©ÜÙ¥!4FŽ"4:Â4NøRˆh_¹KarÏäÎ#úÈ+÷Žˆ(¢‰ “»œÉJD;»[Ehׄ?ÓÞ£ç“ꘂ«X,Ò­XÒwkREwì”)52Rõh˜¼=IØ£< [#GO»âêœ@Û¦õè™Á=äoWþöPPhø+Qa¯àHxùL€ 0&à&XXr“ UœiγL:a±ècm‘{ ¯ÌÜR]$²t$Ñ,NŸµ!Ž [¹g"'¿’;GŒ¨—WîE– Í•ŸÐ5¿îÊüx§V„vË?†¦¶ÄàGÊ|%ðÓàà«èâÉÑ–È{òÚ²lãÞÇ¡­ÔÚ6©£sF‘Uhð¸0¿Oøõ©gc®½¿K_¹ã„¬“ë0&À˜@ù"PøÝmùZw…XÍÈÐð±ê”O5ÒÛ"Ÿ‹:¨G;êÙ¾©RšÑ¾ò+òᜉ¹j„FŠ&‘{fëÞc†oRŸ{ZÂÌËFW²Zø,§tFî˜í‡NRÛ¦u©9Ìð„Ÿ’0½QÅØ›v¡ðÃÂVY-ì&·Ž6ϲ×䉑–Èöv«}ÔÁ†Ygq*ùz«CûtTúAãä å’´ O ÿësO #I«+Ì‹çI`pÏJÔâu.êpd saL€ 0&àêØgÉÕ¯P1æg±üÝ+04rªÓZøë´ |ª?Mù”""ÇÝ AI,áé½9ókÚuøL¹gбdzfPwjT»}üÍj3õŸFðˆ¸ÄÌD£B0«[£*Múôô囋Ó}±ÚFT@€H\>þD±:p±F ,!£"CÇHr•ŽF¾Óƒ¦‰Ko¢!sÓÒõ» 5@z†Âþ@Ç‘ U$–-Ц³ J#OWöñŽ ‰˜{E(íšÕ%h‚5˜æö¯ëå52&À˜€û¸Ý™Äý×T¡W0Ú2×Ïn¿±z?t_Iä5r•ˆa"ØCZz‰ðàÙK~¹g²×)ìgÊ[äÙñõö‚¯TÎV©0AôöòÈÓ‡*gÍ’»p9Ž"¿X‡-š:©ä=ºF#Þ‹xZÑi0×èÖ¶±$n”fN/!,uG„Á'úßÙ¼Rôˆ^¼ž>ÿ‚Ó%A½4òt9®¦ÄÆÍþ–&¾üh…1i|oþÛոĥё¡Ãø 0&À˜€«`3FÔÅ6Mê’È!–œšN!Ÿ}gäézî¡^ !.è™þF¡+q‰4eì³ôåÒMF¨úáö4æ!òný´yMõ”ñ=öZ<ýgåoF.®jÈIö(ò­GòV¡UšýÍ*2Á÷.òµgŒ|]Ë6í£ðÑ™íRñ€!òóeôÂ#÷‘È•רÂìôßË·ÑùË× &·ôÜC÷áì]ì?~Þt…¨Š‹M‹§Ã˜`L O,,å‰Å= ŸÕ¦=/’Áºš äžD‹>kòü³Ek´˜k ªBòÓN˜Tô^\«…ð}£tZ¤˜”*"d;þʹµve1c+rkýÓÆT£Ï êߤó†$ryÕ„ošð[ûeûA#Ÿ–È}%’׊¨‹ç/!ßWû¦FH{“¢PCäëÛµ¢5ªFÂâÆÈËu_§F¯«0ßûçÁt¾Of“™ª!Ü}ó5é ’ ¿üXï,AüzB2ÍýïZjÖ †Æ\¬?!Whþ4Aq )¡ñüìÛ5†ì_îE"_XÝUŒL€ 0&àÒXXréËS´ÉéªÞG´¸¸”=8lhçÿováÊ ]’é™ù“ƒËÎAª”–ñ.„L­‡ýÈgúËÕ«ø—ÒH…ïV˜þ HüëþÐ]2† ÁîQäÓEKF¾®GºB(ijÿ„°î""ä5FõjС“ aIhšÄ5¼ŸáªYV}‘§+Z!G¿Žh’M0¾ð,L90òBÈyý¹ÁBR/«‰¨Di -]Q" f[D¸cžÂôð þ¸ZkÛ²ç˜ þJ+? ¹ÞÕ&ÊóaL€ 0&À­Ç“ÙòG÷%òM`XDõ݇ÏÍ:}q =ÿнJ‡õÝwA.6sá³ ›X¼4fVØÓ.F ô‰ŸE†v±©k:"Hˆf×çuiÓˆí{O±úpåFtkMÿøq ½1ã߆IœHÔ|åFf00b;"ØÙíšá?ô$¢ä‰"òtmÙ{ÜÈÓÕ®Y}zíOó\bvMèÈ©š¼p)„oäkE+·0ê ÍÒËõ¥oVn£÷|/r ш§€¹_Uê‡d‹Vï oWýF½õ(9ÑD~©ïOhºªøgÈk`/O3=ÝÚ°m$B­‹"„°v#Ÿº-|~^íËêØoNR|RªY–”ye5&Ø`L ¤ðKy$d‰è&iÒWðwh çqm`϶r—6MZy\oi­I˜B޹J‡± Þ~è´ ƒMTô•ɬ̘g™T.Î:ø…MYïçãqø¨§g®ÌÌ@IDAT%uuôíJï"\·2ò*ù+lž® D³+ˆ]rjyAË”Ý,S ,‘‡ù–_˜ÈÁ„ä¿EÊ“&rŒ‰âíé‘×ÒîÚ1›ÝNȯd‡ÙêÐÌØêwm6<0`L€ 0Â`a©ð¬Ü®f`T”Yºpí¸@¼cWµ–Èg¤vnÕHOÊ[7®[¤MXq/œÓENLƒ\%œùîÃg)1%Õp㒹,N|HÕT#ŸŽÐ4\à Ðô3¯ê8sUI…OÒ:ü`¾1+þßjy½Ü…> ‰è ]ÙF‘ϧGT,î-Ïíò ðýÚ´ú·C"dx_$£Ý’G>Ę`LÀ% °°ä’—Ź“‚¯€4êý)ÂlìoˆÙ;Žû>fµMã:r£ºÕ¥&ˆø%B{{9çiôÕ‰F^šƒÇÏÓþcçµ4«Íð |º?uiÝȹ‹+bo{Žœ£¨Åëa='ömù8ÙßÐt:Œû[`Ë´‰|”-Ñ'&äßÂýÏŒø‘—g½ýrû²;£û_Q×XÁI„MŸùÕ ¿£Ñ!£]cV< &À˜`…#À>K…ãäÖµ`6á€V‰×Ø9s<Ó¯% €¦g|4úí;vI™„à é•ý½í5üäÒ‘«Uñ£*кS%o¼¼`Öc†Føéd¾tæJ"sbrÅãýJ\’v.öºžnµ¶DÐÄÄCàX …ï1Æû_,ÙØ-¦wGyp¯öš)•l±aƒ£¼¾ãàID­S6ê^úóRºRS“Ôº¤IfEÖ3 Ùj’é†Bþ§Ê£æèN\! lפ. JwÅç M ‰zñpÂN²t¦ŠŸiB¡rE&À˜`.B À§ë.2GžF)k™Sɪ%uGBÛîТ´F´¦²$7×u­‚WR½ ©&YŠÓtý$|yvk²´K&e×ÀM>\Ó´Dùýêûv~ìÜeM’éé¨É¡?Ü¥©ð°L€ 0&ÀJD€…¥áãÆÅ%`Ÿ¸x­¯®i÷£Î’$w†ðTæz·Â¡s¤ª±Ã¾î’¦ê‡qS•%}?¬ë~««L8d±H°°ãr'¡×áSVuÄSýîT•Ï3| ˆ(~ó­ÓŸŽÑñPä¹á!‹ò­Ì'˜`L€ ¸8öYrñ T^§Í­½ù2–)LÁ »®¤jõ4I¯¢èäs@8†›¡Q2tK° Ôð¨:1+’%YO’t%NR”ØÚ4îZÞBÑÄòаÖ¥o8|:v6»ŠˆÈ… •€µµx¥‹øÅJ//ˆ`A©¨ ¹>`L€ ¸Ö,¹ÖõàÙ0»F¡ÃŸCèðo^~ü~êÕ¡Ù]›ìžâ’iÁwëÕó—¯#=”„ÙŸ»çJxÖL€ 0&Àn(¤ÿ­ü‰ 0òI``§–‹$Y:¶xíNUD:ä K&wñÅOêÅ+7RtY~”¥Â’ãzL€ 0&àêXXrõ+ÄóceD@é€íÔ˜äÔ4é«e[n†Ô(£Áy·%°ê׃4ç?¿è°hºÖeáän»ž8`L€ 0\Ø1!þÊ*2ݛ֟êÚw€)öZÂiVj׬^EÆÁk/€À¹K×iþÿÖ«¿í?!ïð[2U,zòÛW h§˜`L€ ¸öYr»KÆf¥K@xæ…M™àA}:·¤¿<|/öÂü§¢t©»OïBˆþqÃڰ눈vw ¡&ß„ÙÝ7)`L€ 0ÂàPáYqM&PaidXÄlM§7š7¨©x²Ÿ,r`q©¸Dî¤mûNЛöª)©ÈaMs=Í•Â>µ¼žXq©ðÊ™`L ¼`a©¼_a^( °È¿BpŠöA†àg÷P8J^ `ºiÓäÔtÚ°ó­ßuXIŠ,Ë[Yþµy–½nº$ž6`L€ 0B`a©Ð¨¸"¨˜‚,SÚ’ª®iÚ½­×ÑžØ]nP»jÅ„QAV-²š¼p™vÝ?>Éú‘< |›Ô«¡õíÒJ“Ùdry BcrðÄJ·Z©{»¦.?ߢNpÏ‘ståF"U¯âwóåO¾ÞžYÝÀ¤’lv•2¬6BÄCºx%¯xйzC?9NÇ9vvÉÚðòJ,©ägZñá„ IYð&À˜`Œ Kì‚ór™€3N›V™Rì/Bõðšªj­¼=ÍÚ}šË½ïiIuk8c§ö‘’–A[ö£M»Žj×’ür^~„ ì9uœ»Ù™Xã„Oþ«ÙU-Gþ<“*é$ḤjÚmóMŠrÌ,wÃiUm¹Åz‹e¸õn®…ÇfL€ 0&à*nû‡ÓU&Æó`LÀ=Œ ›r¿ª«#‰¤g ¹ð¨ä«¶oVOiÛ´>µnR›¼==îÊB„YÙÉ Wiû“´ãÐ)Í.´&&y“f×?Sdinãz5ªûëÃåâo Ð}¾d#í9rÖ®›¤ž–¬º¦7lÔ”$½¡$*¥Kº”­Q:É” ÿ£c>ÞòOBB.ß• ă2&À˜påb£àœyŠL Ü´Ì¬.ÛÓžÔtiˆ,KC ÅðƒæIoZ¿¦Þºq¹~­hªRämr> qÎÏÄ^£#gbé(^'/\1$Ì%•4ú™å¹Q–à?ÄÈA¡á¯¢úç0!¤çy¤œ?Ÿ²ê1=ÃFŸ/ݤ óB¬udTxHtYÍã0&À˜(ïÜx‹PÞ/ ¯ ¸/‹e½)ƶµ—.ÓY§¡:é÷@·a˜‡™MŠV§FªW#@®]½2Uöó1üjüà[ãçãi|öñºåg¡‹`êGBS$ÞÓ \‹O¢«7’èz|2]KÀû=æZ<|nìÏH3™”ÃÐ$­”HYç_Ù¼qæøñ)¹i†F¾¿+K—6è¥Gû§‡9w—þ.”EÛöŸ eÈ{””JàüÒ‚ÈЯ]zÒ<9&À˜`nF€…%7»`<]&àŽÞš5Ë;ýFF[USÛÃ,¬rÞ¶G Nø^;¯õH’¬Ë2éŽrøßä®+Kr†l¢ 0­; ÙáÚmö1ym˜my;.wݼ¾‰‡ãÓü|=éñ¾•û;·„–ɵÿ,¦Â7iǧiýÎÃêåk ÊFHJ¯-°„Ìk|Œ 0&À˜(>×Þ]Ü’ 07 ¢ë%¥i5 ­š¬Û«Á§¦Ä£jøÃT >5f8ÚX¡ÊÀw+FøLVY—’aovFóÒOE_‚p#üqŠ]æu%m¾ªª½kV«¤öîÔBéÙ¾)Uñ÷-vŸÎn(ÆiážÃghßñóš"EÚq¦-˜ºÄÙãqL€ 0&À˜@&–øN`L€ €@`Hijðù«jz¡\B^B  ©yƒšÔ°vuáTfœ®"ü÷ùË7èÌÅ«ôÇ©í•\— I»ó»¯e³´5Iev9x &À˜¨ÀÊî_ÿ ™—Θ€û o[¼ÿÃÇg4¢…˜¹ü¬šÔ«IukÈ"@EÍ€JF Šª•}Š•_Jø_ mÑõ„¼’÷8øaÅ^OùŽ´Œ ›"Æ…–sÙÃ’I_µàý]%Õ¤‰~¹0&À˜`…#ÀÂRá8q-&À* Ñ–kkª­/Âp?¡¥'ü­ZA€òËŽyŠ4/ÍÛËL~Þ^’Ù¬(tÓ0Ðñ†`zjF†––fC€ «lSÕÛ|± ¹Šƒ/×qhŽöàóRôÝU+øôõ×3²ÇŸ™`L€ 0²#ÀÂRٱ摘(FZfÔ$Õ“ÞŸZiðµ Ð%½ ,åàBå°ø ¯+)SlÂZ;¾ÆãhÍ_í2tÈÉ]kÖd¸ç*xÖL€ 0&ÀÜ€Éý¦\´ï{²VMÕFH’몤y.%˜eù¤½vƒÝ¢wÙŠÖÛíµC"^Ò%}¸8#éRÞÎË’¾lADغÛk;÷H`hÄ·I{¢"B¦ç׳M’;©3qþëüêTä㪪ß'éúÛ`°ønr˜õy¥ªzjGôú‘§®S’Lòißúö¿>ôD‰7Ç¡áÍt¢9b¸OU¼ÅâÃú_Y,¯¤—æÚÇZæTʰ'5‘Ç€yòJu]šŸ–ªÅ\.—æ|ܱïdßžjˆ±]|-ÐÕ3Ú”ZŒn Õ¤MŽ¡ˆ…v?=¦P \´N ºÕ #C·¹èsLKh£bìÛ>Ö5}¸$‘ã`Ž:ü… 0&À˜(˜@¹–t‹EÞµsþXÕ–ú–]¹ ]¥Nº/v¥Ï` ñÌÎGjn%“4ªÛ—‹iª&]Œ ùçMÔÓC#ÃÐ÷c¦L™178øzÁ— øg£"CÿSüÖ.ÒÒFAt p‘Ù”Ù4¦F+ÿgM»"4‚ÕÅ ”ò.:yáÄcº®=65ZÞ3%Ê<*8ȶ=ïÊw>ª(U¾™g“Œš F‡NocÓ­Hö+BãuçÖÅ«a±HZ¾W¼Ö.Õê)EÒ7¹ÔŒ ˜LŒmë·8]UVh„®Ñ‚ªò)&À˜`L åFXÚùlÓÊ»~Ÿÿ?AƒóYk¾‡±OíMvÚ±ëÑÚ£»þtéïùV,ô íT¬Zµfx¿>"4âs’¥Å0÷jeÁX<ÙO€ILaž”nKœ}ÂÃÐTÑ%Ú#›h|”%tçhËÔæ6›ºÂËlîÿ©eÂÇУ¦ܯjÚ<2×è©Û¯ÎÁãýÑᡆyÕ¢E‹”5û¾Á‹0 ¬5Å&<(,b$6Ob ÑïIRÞ‰ þUŒ:å)˜‚õƒãk˜îÌÏnfòè’Û|ʨ§kƒeI^«“Œ1¡ùE–¿\09d•èK”w§O÷¿‘d› qàaÌË ÅÈ*³ò¦"Ã""1wp,cDHÄ ¼ßÐei:¶ÖoFG„ÞŸÙƒ˜Søhë‘Öèd˜NB  £utdè«¢ÞÈÐðª.½†Í°îÓÐÎ_:ÏÑG~×ÀqÞñ>2"¢ž–®ãZI³!Œþ×qÜYï³Õ÷N‹¿ø/Mמ.jŸ` “Jû–È(ùÝ LKVÄ5…¶dzi!z*èÚ¶DÞc·éœ³ î-øI«|M¾ã>¶¼SÔå0=]ÓÓ…¢Q„Vã¢}Ëo )ïª^k“¯§î—%¡QïçG¾9DÓ´©øØë: ×$]Ó2pˆÓF)èÞ {M3ëÑ IÓ{Cy÷šjÐ:/•M¦9 ,“Έ¾D1Öh×>ÆÇÎÐ ]Áo`~¿þÁ§þéö„åh;@°ÿ'îÕ4Y¦o «'F„ˆuñûÛwl~Wñû¦FŸuР΋Šþ® ß¼¨üÖ¬YÞ`µWò7õГísñ»y÷â œúÄèìæ Å–‚B#…dÔ©eàðáÃoÓ™ÌRDMjñÇ%íxü޳7çÏL€ 0&À˜@! È…¬çÒÕ„Ù¥%o,Ž tkaº6°_îz¤Ö›·ŽûÓƒ¢¥F^§2{Ðëª[°_y›ÊWe/é1q<Þ¸ «0¥Cf¹¦ýš]ß,‚̳L:á!)ÝnOý³Šªk¯¢ÍÁL³)½¶–Õ'×ì?ŠÏ/é²2ÜlòlG$oÃišã¼x7%‚±y{ßÃäÑ Â_°iýe¬ez}qcBÈÒ‡êº:çþ¥˜Lmjšz\qÞQ2ëa,Ò^Å‚/I˜;ž† ·t´åÃÚŽz”–bÃWG2É™ÍÒƒØãÕ²¥jâ‰7™”€©X |…¤Å¾fßʃ$Ùô;6‚}FY¦6u,a2&ýŸX§|ñÚq,³hOcm†ÿ „®× (…cò_B¸lM2´$ºbTŽê˜C^× ë4>¼Y ‚ÒZÌqy© J7.®Æ}PdA)Û<ñ€CŸ¹PŽÈv¬X_³L¯ á¤=ŸäwíGZ¦6† ´ Ž˜$soüÑ{¤yª=y¥Ø´ë’´üGgŸDŒ¶m®Y ÍTm¢ªh¢77IðDi‰l_±Ÿqp>ù™ëˤLƒ ‰®¼³÷Qн“YïÎ×ÔQ‚Ò?0ÿ Ù$•LÒ3XmwÕ¦F;ÆÑÀn×¶b1kðûlK MÄÚ&Œ|oʈ9ïMò5ù=Š©º$÷ªO€Ï4ø âþ»%ô®ÛTøµp_¾$„EÑ·è—ú›tíwñ½ ß¼8“UK=Ù†¿ t~^½?e¾8ç(Y‚þvT©d~3/AIÔg Ùk± ·‚-.&À˜`L 8ŒЋÓÐUÚÜ4½[Œ§¾œ1'llfíz´æÙ®?]YRØþÐÆ”%²#©Re;i %…bwòq´eܵ¬>$½¥gÍʵ?}ýuÃY_hˆìšÚ×d–;Í·„ì¿Yï ø:õ¶Ú´Iøþ ^_H¤Å»ñäzÜŒ¾ ñÏ`õÄÍú9ßtý ²ødaø$cc†“3 ÕÄæêGEhrB±iœ-žr‹c8gPtS(3ÆÁ±f’"?59ägG»<ß%ݬøšžŸ?iÒ qó OHHCÓlâZ\Âøû°©î‡'øÍOðƒB""4]ß(Àì$êXQ7Ch(D(‰xD·Ûâó©‹êT±M€Põƒ¦’Ð ¬zÓ2»JŠ-¥›b–þ†ïP€aM2-\âxúþžºûB ÅÓþYY›É\×@´u˜LVKKÑÖ€íwðI™ì8îÌw¡QB}œÒ§¦‡LRÎL R?/J:Ýh?Ò¡Ám¯A†ÝöîÓ“R³‚äuíq=Þp”×nŽuøµÐð£:6õaˆ8ÿÐS)\ü²îeU É·B¨÷mö9j6]h5dÓD}7"lêYÒí;ÐÆ¨z§{šCÀC?9~WÙÇÉùYÚ ¬ø]eDXä\ü¶¢ßíDoCX;M¥C]ŒßE˜?ŽÄC!TÅcNº"©ÉŽ{Bß*ÍN¾e™Uu¶åí8dü6µ` /ÄjÛ@›µ6Y M鑹‘¡g ù›wLé44£ï8¾8*vE ›ò (u 9èà ’uø 0&À˜p><Üuï²sç‚qØà pÖ*°)„µœôÅÇkÖ*lŸxÝEµëßC«ò1„ÀD¨Ë2ÃÉìGZì”ÄwUWÛãIõ•yïÈ>.Èjl„Œ >&¿¯1ŸF£,½DDJ8w›¾µÙÛˆÏc§L©¡-Où7ä<'eõ/6u¨Ó!í1+?ˆ„©¥Ð( []˜ f‰âêÊ÷­r|-à=Æ!(‰:3ÇOÁÓö«º&ù‹ïè·X¦ivû'Žñ4IŸ ÎÁ쪹xÏ« Ý*•h8'i6 Ò*pYĦ)ÕžÖW)v%ä` e&|~t\+yMö¾ 9[ ºÒú?N6ºu<ç5¸uœ4h»–¢ÏlÂC³wÚGá£TBÒms08çÃ(Ï[×í¶·°Ûa>f£¿cº«ÉßÔëSËë‰Y5ó¾öí„2«>|víO@˜è‹h¹ÝˆFM*|ЗÍôEö6YŸ%½•Dò†¬ïøàày0û÷Âß;ù^ÓìÝᢜý“tZÜÙ*uÀ˜ŽûT¼ãïJoœÏ—¯¸ÿ ™‹MÖRû‹ûOàÓ¤¬Âo`9~àB°Ç­©Â}µB|,Ìo^Ôæ¬Â”÷¶¢ê¶×ÐçhÙ¬<=qbÂmø`L€ 0&àT&§öVƉ°à6«þ6ÑN-BèHW¥Éè4¨PK…Ї³×]09çž[–èböóØüÛ!X± Í1}d<°×ë"ž`Ãßb±ÝF/¡íoiþŠØ—¹Ûˆ~u«bÆvŒdEƒ?É­‚Mlºc€ ²fPc |.þ¸U‹>3ëJL¶ï—-–þxØ^pA¿9ƺ­¶$yB³”&ÉògŽs˜(k²çžÌyþw%6¡ÿЋöˆ§ -{~@‡V[Öì;fº¤N½«ùn¥héCŠ=»Q𪬢+z4|P8e²'r_GetÁã#lžfùùèð<7ªŽúE}QïléWr˜Cµ¼êƒ¿·¬âÜÓyÏë˜ÙÐñf€‡¼N‹c·]{Œc‡&_£1|ÍpŒ{URè œŒ‚ùÙ„ØÔ­Âùã ,¡ÐåU$Ü«ZŽè{÷Ö¯o]wîr7ëòÞÉïšæ1jŽñn;/‘'ŽÀ½*ü€Œ"îU˜ïágZ@‘ðpC“^Ò§^Âm(ÕR&mÕ¦xi0ûÃý;6Æ1­_=æ7ïIÑ• ŽÏ9Þ%jï›aB8 ï™YŽ ü… 0&À˜p&·–lVi ž®ç0ñq<‰~Ú¥÷:üxå²³úÌÞbì€>£~ eJëhKðÇ9¬g0,‘²¾ÃŸé ø|³§­¤÷5éô²£nö÷Oß7¦giº]zÇw:ΩºðÀø:Ïòî%˜ùÅC>«59Ôa²æ¨êôw<™ÿ›ßê&™® ÿ‰<È iŒÍó­âSÕwcr\Ь=â%MJ=Ó¤ÍÇKòJýˆ\YA¼¨+0½C1 „†Ÿ€ßÕ`|ýÕÑ |¿C°Ì°Õ©vÚq,¿wð86~Ä{¿áñÿWA–){£,ÁÙ…ÉüšêxfxpªQ¨ÊE­$Ñ“S¢=[f+jÓÂÖŸ¸ò×þN6k;MÉô«#÷þ9FÛ¢_Ò¶!t>Ì>ê<{ý\ŸOB@÷iVA`’ÆV!ûd–BÝ;Y­Kþ2ÚaŒÝ¦§94hÙ{X/æL· KÒJF Ì{2Ä¡%"òÆõ1êVïK¶Èç nù×5×Û$ú)ìo>û˜·}6Éc}Ìú ä£Ú³Àq0y[>À˜`L€ 8€[ Kp-1ëI´Ódv„ÍšG†. Ç·OܵÑÝÀMBS²žlÚ‡aÓB<Ÿ³ö¤WabÓæK}cF}¼BP<á†Íÿšy‘¡ç粿 m|K¾€°õjà{‘»©^õ-ÒÅ«C±‘Ë¡ÃÃï騶½s'SÕÿÙ)ÍK×2º{¨ú¯"{Ÿ%ý\Ϻ.ƹÓf×?B´ºðÚ¦ÐM±öÍ$ÉÖyAxÈ"Ñ?ŸÍçM«ì0+šýöÛiØnÆÆt"ªPQIZ…©ãGhÔTò1¯ÇŒ"IsÐÏ;ˆævPoPm]¼ö iÚ(ô;Ç=ÏQµ ÷…“CƒËýºM[<Ú2·û40u•ã´k/b3^*.½í/èüýR@tjRþûtôˆÐÈI~&ï¨J¯iµÛ,x pœêÕøQTšHâ_iªð›£®>9…+QÇQp­>Ç5\ S·QHŠû÷Xõr+]³‡ã|¦ÂÜ;Žþœñn&ól»n{k˜S”T³rLÆÕä{"ùáÁ›1tI¼?Ñš›áÐÉ[ñ^“¢§|ãOȤõ ö?AM†ÒGÒßÂþæE•OBB.›òLTWÁj{Öü jÄç˜`L€ 0b€eˆ{–]×oÁ¢I©Î^§K«tÀÓ\é lmØ(nͰ%^‚ ó6|mþœÝ|É0¹C”7l#‡ÂÏ#o›“Dd¬`<ßAa5¿¢E¦·²¯a†g@ìø OögÛìq×u[ÚUlº¨²R5{=g|66”>Òã˜{"Ú­†I]š¦[ªº>ÊÑ?"}ýˆ ÿ)J¶ÃFõ¼ã8nÌ•Ø;·”ÉD!Ž×6ÕY™°.D¬ß‚•8®Ïà«æ³éÜÕ¬_bËP#Lœ/JÑÔk’`·ß(H3Rè.§~]Yøït+tƒbT„Ùf©Ý§b:75ŸÃï©{ÊyÂN¯H‰eF=LÊ—˜‹0Gý± übˆ4(4-¯ã5-Æ~á:îÿõ¸®KpïÂ1£æÞqÔuÆ»£.)ÒØÃxHr2ãrb4ºkq¯öpôóB;¦u—ñ`Â!;àØ.üžª èØj££.~cßã¾F´ÉL%q¼°¿ù[}äÿiaxðFÜóàáÁEÇükò&À˜`L $°7pϲû±:O«ššµ‘.•UHÒùî?]nX*}gë40*ÊL±I•sDÏËv¾¨-–¿{%Ð Ÿ›¹|› S*ÝCÍ(hc›oã"žÈ\ã:•|•ë"Dîæ"(€¿,ÛKÝKllÁWOþsSÖß§D›@ö ¥X^È&¹,î¡k^½VêÊË5áu0&À˜`L °ÜVXòQäßáp•j¸° .R=Iß^¤ú\™ ä"ÐÄ¿å~„ÒNÏuØ©_á[—OòW§Ã1&À˜`L Âp[a©Í’‹×!)ýZšWL×åŸJ³î»ü>üBýêÒ\)ÌE—•fÿÜ7`L€ 0&À**·–Ä“%Ù)¹pò¼ø’t¹J?Þ„æ ‡…€,—â}J”L¾þÿ-Ê|¸.`L€ 0&À˜@ḵ°äå]ík<µ?Y¸¥±–DÓ[|z"£°­,–õ&‘7¦°õ¹^Å!0áU»ÐPî.C«4gÒ_n”FßÙû´XydÿΟ‹N‰lƒ‹ÞŠ[0&À˜`w“€[ KíþwÈJ²it)ø.í•êÔÿ¬(&ƾegÐ{‘cŠÒænÕ}Ë2«j`hx³»5~EW$\5)¦QX·Ý™kÇ}Êì]cÊúñ®÷¬ÜõF„†o z/â±Q¡Ó[a#8÷yÇ÷ Ð[\´KÉXÇÄ{`hdÚ]xwútÿìÇ1ÞêQaS»f?æø23ܳ£'wb¦õžÓxþZâþÆó47=É;u€žržešs{ÊÖ»{|¦ßû»ìkL¢ožá8ô á¼b ž¹|™¯ë½Lô{†aØÒxŽ–y”ˆ D€ÚI Î{–¬Ý턆AÙ7œ¼ m9_Çõên ÷Vh¹ð˜´4—Íø\mñ!ŒŒ· a<æœwÅšMÒ{s”ªžªÑ}k®iÍ–aA¶-Rš4tÝßFóJ%k£Âù´tÝå7 ÄI%+Š5p]ÐF‹ÿ0ÃØü7Áì–jµ‹ÂÕl›M“Ê]892ô žŒ7"µ¨®®(åZ¼É~DzÒÆy?BÑë0Æ|süÞK¤¡3~|c(šãU¿‡¹êu…â×uÙšM½œë mõðÄÚc°0 Œd–SI©[5¡]c.¯H{Á”y‚ó~'ÚØb¬½}â8ôÞ;ùF?½èKÜ#-3:1åÊàš76ÚP˶R¥a\j?=iôÚ—\ °ô„ñw1^.ü,kit¶†8»Dí–BØQy¼ ¥q‚kÏ4h%=5JîqûYk0ú—åŠì) %™§¨ì'Í¥ÞÑFëq>žÃ¦•ÿÌ/íY•eÌ´G …¿ yW(L$Aæ&i(ŠLs®ÿõ¶®ðÔñOñ㘗Y´ÙHMãÀx»J0~êK-ðÿ9¸NJD€"@ˆ@m&P¦rS›;_²oW~´wЦ(—ÁøXR²¬´chb‡Æ=MÎmØ !}K«Wjþηq&¾I×cs”(í=(Eý¤!SXFŠòƒ4\Rõ¸}Púþ`,»A¦µµ3ö÷??jÔ1]‡/‰³Ÿl&ÎpÎ!¸ˆ€27:}ôè#²Ç}¹¢¤ZœÛvß%k늟7_S–œ´qžSõ¡‡ë»\ò;?ß#Œª§sŽà­È1{ãøC»žº“Õ3 (Õ ¡HÞ\‡ö«@|Œ5WqEtÁ}_·³\) c5Ù]ÿŒKÊëQ –ªiÊ<.ÄýҠ鯧üeçbŒ}\'Ô¾°íÇáÅyK–Á  †ÉcÁõ`õhs| òEAænËß?¸¬ ûí¹"¶”»½à‹æ$Å?eذã0jäu–b:[÷¬™¥ÇoÉh½™üðŠá ¤Ïª,G¨'W´ïµ¸öîCàY- <7J|/£Þ¶úª¦¼…gõvî˜g7b¹—Åòe„ôrEh®1òýE@þ‰¨'¶à ½sL["@ˆ D v8%Âð‚Ñ^úQæz÷ýù¶3/68û_(D2 !qøpeÇél=~B¿veš¾km¦À;¤,c¡®00ÇÈ8z¼šÝ1¾“á €1ÐÍa2|/¸Kð!ùL|=Pþ=ÃÈ·ƒË*¼ÏE;Eh»Jk‡± 4'ÆõΑ&x–Åxç¸ä bAön¶c¿¼7Ê+ –ö¬Êö8ÛRaI1cü¯Z¶9så2?Ãθ ýh›cd/“þlyÑøMɈûBsä÷@'‹Â$[°‘™ÌWÄešÇ³}i, .]¢F?Z,ûJ‰"@ˆ¨SÎXr0\²hï:ì˘ù¦wC¿³šâ%¸ûxnþQx‚B 2&ÞNNn™›c]Åèh]E(Ÿó;äÛx[ÒFIør·í?ßÑmBŸ·QÚ¼5é±±æ3¼` v?ãö·æºqF¨ýEà¢X(!”Ãl•+ƒS} _Õ)Ü)M ¥ð‰9>ïõ²*æ'}°² ÛoP-ÆÒÂ4_â³Áù´_óF>‘÷+΢þ±”77Uó͈¨¨È¬AýöÈ—I:Ÿ`'U9)ólf÷L\©ö\Yâ óȃ°æG016P¾Ì[š½ÛÞ|Ž·—ót0'jºaæOÆxÂÊ'\ínKˆ³ áGSÓ0µ«òF’ $ŽÎøwa`ÿàù+f¸9ù%·õ” LÓŠ‹jÙEz¡ú{|[\ÂðJ{V¥ 7³_È|1ú°CÕøÄTݳZæã£ÕYðbKóyºõM a¾TêŸWpÃÊô‹ý"O>ŒÊV¦‘Ï1J#Ú"@ˆ¨Šþó®«®>IE¦ó;»v~ç·Ìâ†RÕÎwÜ~‘5ïbÁ†­rN‡ükÔ(âe(o7<«Oi²—%_ A„¦5}ÓÅ>(¦ÉÅ4åÖ®åÌõQázÅv‘-ìè=-0çAzä|¥@›Òäpq‰ÂÙ²NŒßߦ[œsÀ³°Z§TÞ ß"ß3Èã;GfÈP-9© ”þý3 Èo&|";ó„¡T}gLJÀæã>߉ç´vyV•%ñ¡Ã‹’æŒm¹ |Ú‡j+Îj1æÌ%0_äüœJ'‹¸NJD€"@ˆ@m&pZK5v„x\QÔ׃åOŠ‹Ë†’ôñq+û!nâÓÞ4ý´lÍæ­xÛü=™z|À–od†ýÛ¹ÿ—¯oKl¢ol°œà}¥êE8VfìßÐ5†ypSÛÖ*œL Ÿ…°©áý;.Þ…šîÈäšú>^‹ß„þ,Ç_T_âxéý"ÜY?Cþ¿3 ÿ>žeÜïÔ§í©A`–ß»Wr VÁƒ¿38qqîýç/:Áÿ ––iI ßWsŸÑà#¼ èþŒßfp¾Ü—S” €GEz…ª’°øÝÍm²0í×Ñ·Õ†aɸùNY™[ÎgÂOûž»EX&} ÂKëÃËú ÂW×Ãè*šÛXÚ³*åãü˜_d'd˜›Öe˜þßðÌ,–/äË<—áE†2Å9vbY~pc,u¬çCRßg›Y[‘/½Üç"¼v‡,“ Æ×øø¹àˆþ%D€"@j+è6”j‚€ ‚´ îŸ[äyyŽØÄä«0Çi˜S´ÆåRîš©'y_äÇoå¢(í‹”±'¯¾óH9FŽÌrBçÂ>«\)ŸÙzg4ºUz¹ä¹ñÜMÆf+<Ê3¾ HIiÊó³ôA…¡“%C'O®ßøhSìU–æÍÝ{têRoJÕãsdЖ"@ˆ¨}NÙ9KÿmÔcÇ~¡âõ±a»TUö%Ö–mÞ‹0¨°P¥Gs1`P¥§kX¦û:LoÄŒ–GÂõ»PáÊM×G8ÕÊ”ã(ˆNƒàm°¡$ó •Ç}ÁuhŸüÙ¤¡íMe˜–\]o˜sþÊJ²m¡á_düK9øsIJpÏ*0DÁ2»ùÁ†K2TÕ`â,*ñe‘ìÌŽ—^ä“’ ù+™™¿ÿØx×ñJ%ÉÐ1 D€ÚG€¼mxÞæºÊ Ö›t«Âµý³“⬫×PÓýÖõϵLsÕv×üôÑ£Ôôù**ÿùB]·cÝ ¶`7cœ^Ê9oǘˆ`‚Ãv;gü;¦jováÚŠÊ®îú¥·ØÄñ—ØÌ¼†«üÛtÝóïê>/É«ò~r®íMMŠÿOÍœ¤"@ˆ¨û´º Å¯à‡´6‘ûdû}û†!(i )œ­<Àþ¥ø÷>fæO\ü<ÿ’sUïg~¨Xb½Ï·g"ÿYÁÙÅ×X0¾NUØ‚ÔqÞ÷+ ¦ÒUmÆ3fýd,•B1+ê‡ë0Kç¹öJT©5ÆRçùëÝÆ1hÍïkG`<¶qº/„3RÛ®0 îb–‘ÜaÒÚÕª`ã~‰ë²È©[ÑmÝ×ݶØ]Âf½Ñv ³W?HO].C,Ôx‹ñøæ ÛìY_ ‹åBn­0–b¼¾·ahþ'Íç™PQN§K}y?¹°¿Åõ’±tºÜtºN"@ˆ¨0¥Â-jqƒ%“´{ìÙ` ‘U³eyº Ýô:Û¶>_5êÖÞÏfï 74FLœuäpÞL„ò¥§û½Cƒê~4 1e¡ÅÌoûñ¿—:γ4¨ì´ÜàÐÑdù—Ÿ–â¢;M\w‡aYó``×Q\v–÷š–Ý¥ãÄ·lŠë´½ì…5lûï0”VÌñ{¤ÇÕI“œÊl•,»§ÅؾT=n_eÚ×d›4¿÷­š”_-² v«ÍxÓj‘EBˆ D€!P)âFzR¡‹'©÷[–ý*DTÍS&Ø¥ùy9Ë–¥5íySì¡RC¶ŽÍ{óžÜM¹Æ”ì¶œ?íñýÓ²Åh”-…—i±ÂÅû©¾Ä9N]9f·¹ê[•©zª/áã‘&4˜«ââ\,uEªÏÎLH8 ÛD{}/1…/àBtd6Bï¸8’îK<ɉñ&Î&WÃc憜e‚kÓç$Å›èëð«ÛaÜÝîôCnÑFGø`»9>ïÿç<¹~ÖœŸ¸K¹‹™ö(÷ÿÃ8[ùôîÚ~j¿~ý '¤BÙ²_g#g-‡§%%|;Æ£e¯qΚ€É¯²6æµÄ0Ûž­iÊC³tÏO2o@brOÓ¶ç**˜6ÎðBõ×ý[†Xд‘«Ûó£F¨û/5 !•ûËá͈¤¥QZÔˆ©úÐÃRF¬7ù^›‰ëáMx›I0~»¹˜[2Ú!Ë„07ëõ§âXë}I‡˜àëpêÔÔöÂ)kzûó.Xʹs•N¸¶öŒç/¿xêÚ«×=Û%¬a ~÷ï¸sj[Ö8 nƒq=Òö„ºµ,¼·Ìñ{¯ ®ãì˺8÷ß]‘÷ þ>Åø-zvJ?²}¨ûŠgïÜÆ‹p¾{e'Å$úR¸ÍÁP$ŸŒ»5éIÞé²<à˲&0!nÁøi…¬­š¦Ý9[ß&ËÃõA–§@Ÿ„}éy²¿ŸÝ²[zl¬!óc¼þ8\G'<ËOÉãþÞ¤hKð§±{>Øl ›='É;K–ÉTÞßy]BØñ̥ݗ®.öÜH¢‰ D€œ~ªf\Ô^K&DÀ€°_EWªåZ øt1~)Ü¥Añ¹õVK%>T=EáŸ# ¨‹,œ¯„"30¸^†ýõ-PjÚÛZóÏd> ¥P³5×”;\.~3äŸiäØoŸh#Z3KèP²ûÀðzJ©Ç¡xžœ4-BßcÒö<’_†“çpÛJ‘5ݪëcôùV9ßi)6ì€ò臓/·ªe§è Lë=l?`‘¼³`ÊDc—¯ýõ §n@É,så9·æîYËlÛ^6XŸÐÎÝ¢á*ÔŠ~‰rE])ÿÚ(W¯Äõ±Lv‹#õúa|°(åP HÆýõ”¿ÀPúšq¾Qã®kбh„<^cf}"Ùu!¯¯ÖLô᪦]x†ÖýWGžÜJ\t¡ûìŸi(]2yS[ÛäoÃЩ’¡Tt=‚g˜âM]@5.GâŠò&<ëñ “žÑPMʇ'Zi®¦³`ÀKÃ|·¼¯õ\Šîç‰ZEþJ1Û±MºÛ ®ŽB¹nüÈ ¡î«ÊÜÿ@ÑÝ9ƒ)˜|3}z^$Ä20Yxžk^XÌD–ñ‘`v7$ñ.-â\6ÃÏd£“åeõÁ‘ál úă¼§p޵—Fàv˶?¨?/ 16Ðã;Ë4í¯`¤.ÇózSÙhŒ¿QýÇ$GËrMmš‚— p… $¿­qo®hߣNzÊy²Ž® †Ò“ò:”ÝÜ ó ’à€ÑÉ`t=ß—$€š[Ïåê„_Á4 tOÀ rªã·¥¬ß¦ C))ÊÓd(£"@ˆ • Y«MÜÏOƒ%¤òWÙ~B¹ñõî¾£,1''(IçB9ÙqrIAÔ÷PЛ֧7Ês{Uä°¤º¿ëlݳ&PÃAöƒÅœIDATIz;]ÍÁ[å«¡ä_¯hÚù©züo² ­ó®¾”ž"¼ßhÃE‡ˆ3·š1dH^à8Ä?³ô‘™È~Á)BÜdS뤤4?jä}cB*kò-4Ë4¿¹×ên«¶A:AŸ˜æK|¥°ôCÈX ÞÅqÀ ÄÛq/XLIó%¼+ë@ÙÓ¡¨Åæšæ#s† IÁÛêlššã’u œ~ÃèFìN a†é»Jn"&›§ÌŸ??¶ÀˆxÛΗÈú¶a G?wÎñyýFÖ/O{“6å ¶­bJ/–õpîó¹ªÜïÔ"y, ƒ¹UMoó“§Añ¼œ5põ.ÍЕuk"·óááEÊ{uœ÷á†7_Xÿ$d…5îå¹R“¾Â¸ú»ÍYÊ‘#¹‰¸/k‚O›å÷î”å凲2Ò,}PVL¢?»vð½ ý[öRYnÖ`×ó£†È— _à XúøA½€¡ê¾¢ÿÿ²yþ£¨“(ååí=z'Îcˆ³š$ƒSçÒê©*ÚåA+¿áYñª•§Áòû\¸Ô(ígÇÇ’ÇgIàúŒmòeD¦ÉØ0\û:x|úŒ-ÀsÑ I§K~`žý¼ ~Gá…Û(LS>Ûv[)ÒÃt×µ sH/ÚÒgõ)M²ìnª‹ÿŽ ø)læ‹M“ÇH/Âs…ß(/ž¥ÉE/Âü~D{“zã}MQ”»0N¾+Cÿ"@ˆ ’€V—1,~^ƒƺ®f®ÁöAI““Ä¡§—L ’h\2×9†ñÓS~Ï‹Ze÷ë7D*@‹Í€Ä†KÃÅʲîT\ìZYâ»@©:n›æ4(€'ÓàEÂK^ådKxÎP’ cÒÒ\|ç»ln_ˡŌ&è§*ò˜ì+úÌfC‰š‡»¸)ÆGÕ£ð½¥ëOäÊö¡×8Bí r¶ÌªOn–ed·ÅõÞ¾®†® êB‚е :°m¶FKœôìÝŸÒž'оW–¯Ùäÿlͦžº>ÿë sä©ã ›u–« ìE_âv(›[ |Ê·úc Œƒð\…œ'f CZ—zAꟼ„ø…S6\n™¦Tvk Ùúiâµc¹Q–p„§ÍƒýîŠu›o¶ …~-BÅ®—á凥ŸE††¸Ó©ár½d³é†qèÚ<ãèo){±ˆ™i¾‘_¹ÇOˆûŠgæeŒ;ùŒŽ)|FÃöõ´ÂP5§rk3ã‡ý1{ÜèŸ`,Uz CH†c(I“ââ²±É~aó†…'À ¢)ž‹……ÇÒ˜?ý8Ï9µE›¥ ¬V8‡ÛöýðL/U˜XŒ¼·ñba@†™r=ž®=©ºw]Œ>©…0ŽŸ‰çdy°,<ÓŸÂ[;þó [ÏAþ¶‚²Ð¿ø=¸Ï_Ú±Ò –CûD€"@ˆ@5…®ý÷@ZƒjêÜPö;2Éu](ùPÒ¶@ÁèªLæa åŽ¿Ùæ¼Õå*{ÛÃ2ìÍαd¨Ù¯PvVÚsé8¤^tþ`$MUTåf[‰ø1Pÿ(œívöCma`¸ÙÎý_`Å0E(_©\¯jì¹àºiIÞe°c`.Òýò 5ÊîT¹òrp’ûª¥!´ïD‚ò;§ å13R†ð¼÷¾nÿîæÚ§^ÉíM—tøöQ^ÞÇ®FèÒýø¾d…¹S vtÏnkËUàŸ{c— Œ°½Þ–oá‹'È@Û`ƒ¯®÷B݉³N0¦VZ†59DifaµÅba˜Õz2ÁÚfÛpWyeJÎiã¼ 1zAéþÜ4™ôtH«½\ã0Üy`(E¢¼µó' Ã%=(˜[t7S´ëp› ‘¿/FK9?'Ý×Í#1÷‹7èëõŒß&®å¡†ËSÒ»˜ShTÉS¥ ô¡¨ÜÁµ{.ŠÊÎä9×?˜“7Raìž“êÏø×ÕKz\ñŒÀãÊÜеãJ9Î3­”«pæÞømùD6‰„¿'’q˜ÅžÀë”ÃÈ*z6Jýý\†¿‚çkdŸ¯mñ®Ð D€"PôŸi]Cñõävõ»o­É~ãí¬Tl¾(y¹ç™<ß#0@È׊àréÝ;÷C9æiN~kåšEö*‘i}#Âëþ޳—œ2¼mÞÍ«…¦°ý΂NYE¶»ÍÝ H]ív©gê ›eÛèDÿCµ®PT „~‰GŽ›Yn'›ªòA[ö‡žÃ{p' ¨X·a¼™% &v(Ì+°`ÅõP:ïDû'þ>æŸMáÂ:‚üåEÆ&«¡@3žÖ'´É3ò;ÛjÁÜb' u )ƒ#]âÐññx¤F@ŸªZuçáþòN“ÖÝ]Ýrƒå±ôZ ƒóÃ팅$xxÄ5²^uŒC0ý¢äßI©ð[NÀ»ôŒ—wcô´é³ôØ2ÇÏI‚ 3¤gcùM|Ì÷áœãl ^¬N×6†¬¯°Íxqö =¹ƒól8õÊ3†ºÙbüþ‚q½³`IȶœÁaÄ\Áe‘Í¢¾Ì:˜Ýpé{ c^m«Å¯ì×ÛðZ}h1ûV¸lo*B‘døæm V#•%V³fYÊ®ƒ[;÷MÛßcg6,ò`HoŒŠ×lK<ƒ7ÎW¸#O(þm5ïg†ÿLÖ«Z%µÒ¼ÿÚcN<Ÿsã²Ô$ÏüPç•×À¹1ËÌÎÅ P¾9:1åJ.Ì!PÚŠ%W$Ÿ‹Å#ÆÀPŠâ <^UL¸Ö PµŸ¼EÕš½c²ãõ„w¥Ûfú½¿c~Ðwì˜Y×v=Vü¢èt —ó–âÀ8R¾9OEA[¥ýŠ ks+¼,ï×ø¢ºšúOfØ£½þøZý´l–{F¾ièðqýÊÚ¶ü°¨^;Ó<ž½Ñ‰ÉCxÓRÌcùnvRÂÊ2šT¹¸ËÔuäüµ* #÷ `ð„©‚yb~,nÓFëñ{F½ï¢ä²ßÃŒA›çê‡%û ¿%äf®Ìú¨]Ò[‚ùiçê´f†Ü–9~J :–^Q“™ŸÂÓÛYp%=¨¨Øn[åêeÖW Þ£ûǰÖ-Öª»vPëm•GUúPìDA.æš‚ù‚ã¹S?£qFÞþ¬KUüf9ã†ÏJü$Ê•úÒ CC¥c~¥ÍØhˆ{_×±>’”÷à…}ÀÎã‘®åE§â|:ä ÇÜÀuùZ»ÿ¸/@îôŠ=Mº¢5~`;÷áyfÊäÓ D€Óœ"8êf2m!‚šM"`x„<^ %kfXëV¶ù”Œ[˜Ÿ vf-sµüŸ’ó‹Üš:J³\ŒàCgIp)8  Eò;á]:†7îŸî6}˜K”¿Éb@È—’9Ev3eŒÅ¬4(h‡˜0?@ÿFàØܤðÜ /Ý\õ•7‚Ë*³å·'B•{”)†yðæPì‡1’j)j3)O*ˆš€k{Þ¤]rÕ-™¯Ùb)Œ,ÜC%‚'ót½_>æz-ÆÂíz å©Ðcp'Oð¾|¡‚ëìƒþÝ'øV,@qLØö <×ÝzjõCŒõm,ËØgv§“åOä³°½"oa+­õr¼Xhóò[ǰ’õ±4ú‹Ïéð.Na;ö¡$_°,Æb0‰Ž¼òlå‚'Xï~Ô…á%·”ˆ D€Àÿåu3-™ Äa!„€¢W“WÙý:W¯^_˜áÎ!¿Idææ6˜‘°?\½²ÊdÛs¨u£(õ€œ0^VýPå2ìkHJJ‹p}AøR*&v7ãK” tµ%'ÜV^°1è—sª2\¿6O÷xö8y•ÙʹV¼‘È“oà+ÓþÏnÓqâš'àÝ›[ÓçÅw¹Z¯Ô93Üyä‚ÆÑ£-l³å!¹ciu«c–”-½'Qnw~¸ûnü””WÙc„ÿEF±ìzòåB(5Ñyíî\Õ‚ußjN\ô¥¡¢˜U]¡QÿM„ýŽ7*ÔõQ D€"P1uÖXZ4QÆlQ´LvÅ.»üµûŽ|Nå\„”¿Uí­)¿Y„¥¸b.~Cºîùwííé©Ñ³ _X÷(¼X¯ÕôÕDEÔ?ã?CÚWÉX¯é>’|"@ˆ D€Ô5uvÎ’Âø>x–j–7–>U %„¿õŠ|Wc5¸GñaÛWÓt/J5;zÒfµ·¦Oƒ7fã.È¥á)"@ˆ D€T#:k, EYøüjDq²((¡ëOέ›9˜ᶸh†å„Ÿi­õ81A¼n^NéµÛ±>7¯f#1giÓ½xØPÑ:Œ:Jˆ D€ZD Î†á ¡+K&ŽÝ7÷…j‚©¢p½Oœ=¶&d“ÌÓ‡@‡‰k·`Â~©é­2 |£kóð‹WY D€"@ˆ(F Î®†'Ãã°üõ;Å®¦š®•ºÂV5ŸŠÄÂðFâí½<ÁiœÖ(`Nˆ D€œ®ꬱ$o–ºŽMÍÄâqöÉ-#òCäòt-tÝ•#PÏ5  ͯ\ãð­ ÷‡Í#:¯ _‹J‰ D€"@*C NK}GäoP8¯þe™9³îU Ô†”$°ip§ ÎyÑGŠK–W嘫bDUÚS["@ˆ D€Ò ÔicI^–Ö¸ñ|-j{é—Xñ¬´‡¹Jùk*ÞòD‹ÁúôFºŽÏ±–‘Ê[¯ 1EÅ‘'¿5þ|µ¨qˆù]šÙ”UA­šG…¨Jcªä)a€MÛ8¬ë—%ókã±ü>Vmìõ‰"@ˆ áÔÙ‚/jñ$÷EÌ6VVÇbP@ßìgýÛ°ë’ÔS.0 ë®°ÅéI^ÓŸÁÉÉ-ór,9—ªþšp®øÒ’<ÿtÊmyëÉú¥Ë‘%·å‘íMëÊT™ºÒÖdðr£i=Xt_§ù½ƒ‚åÉýþ‰þÇñ WÚºz¸B}L³d}:OàÂIα…ù5V›ð5Ë.åœ-i}E—;˳ ^´Ç7ùIº/qX°dŒ‡UXÄd‚b»6›"£ª*·¦Žó,uêô÷&EÛœ_€ñ=j€wBGK¤û½:åÁ[Œ•~–-^r¹øµ³tÏONYìÿ¶g#hv¿`"?8¢[e¹üHnîÞ£S±Û{ŽßÛÞiC["@ˆ D€Ôez>jKGÃõC†ã©Üu±]áê•U†¾—úüå¾GË2”¢“¯3Mó=œï¤¹"yÙöÆ”EPÿʸþG`Ù¾þúÄ3Jž»¼õÂ+XfYòÐt—÷ÓT÷Û\(9\S@A½*Ýç¹Üæì"(Æ×ˬOhå÷!ÆùïÁù´_y¿Œ¸èw·ÆzÂÐÙ\y)¸‹œ½{ž»þ=å1”äy4îší‚1Ppfy±wak¥CÀ8‚×k›mÛ3týiäW(Å$úü¶°ÿ†~­ n(½–¶-^æ,â¶tŸ÷UQðR§Ê:Òš·ïÈ—”;ƒÛÐ> D€"@jSÂX’@o‰Ë_çŽjp) ¯%ï ¢*÷iGó~ïXeÝ Uuý¢hõz3…m*YWpqwýúìu™Ÿ>zô|7w‰°òï®l½pç –YÖyû{ý=Ñ—-³ô‘™³|£~IÓ6ÈöÒ0Äõ¯Æ*ƒåå™ù3EÅá ΧýªX;´ë6ÖÔÝ Üç~XïeÉ3Á 9ƒ~Ȧ]X2¤}^ÉòÒŽgûFm‚Qr Öë¿Æ©“gš@ÞB]ïç,<±¬,ßmí.ò’:uËÚº˜öfZ’÷^Ô+ö­§k·tÇ’é;Ò|#•2^+Á:Õ'7›¡9ʸ+ZqE¼T–|*'D€"@ˆÀ‹À)c,I€½Ÿ>v ïH»Ÿ¢ª=1i!Œ#,XÎvÃ\«h Û÷a½¶nPaª·Oþñ€·æD|s¥´Ù4goQ.ç;l&Î*:ÆNyëÉ6¥«¢ò •?Œ¾½ÜNî¼ BÜ¢1÷gNYt¢ÿI\Éš´¤Ñ?ã’ÂÎirÚжü6=Õ騦]žRUUMXö›ç†mÍÙ> ¬ç].Ö~ãˆ.3ÂÖ-¥P0eîi¿Åöƒ¸û'–4ç"2R‹L€ùöXŒžÜéD½²÷f&^/n„ÙaˆH6·nÈwœÈÁ»rÙqéÕbéI£×F¸ò‹µ)V—ˆ D€"ð_& ý—Ï_#§ï3”áq«VÎjÜ4;;ëZ+]áU‘óDÜØ…Gh«°ÕoûÆåÿ(•<†¬êHƒÆÎŠ‚¾Xì?<>ÙÜfgË/o½à6áöË’§ëóÝÆæÛ"\âJÊ·!k°¯’Þ&Y6Ðã;˰Et½3]_².W/_†^ôoH|¨ãË*kaj\‹£ ýüea¬nÃjw«»´í²ú~¼Lg¸Þiš2Ï2¬UXtäÙL6þlÛ4Ïm£õ(2aŨSõ¡‡cý °gAÖ áä•§L±E#ÛÙÁuñm´lfñÆÁy´Oˆ D€ÚJà”4–Ø=9„ý……Nvág2«Î4K”…ÉôrÑ„¢¤ÞPp[ö£(•·^Qƒ2vÊ’·ÛÜr+®ôë@èS¬þc|÷`^Òü¬–½œlÄQ¥aøôÁƒóñÇc¼~–Ñzsõ‚rNFÛéiÂ΢¿“¨TÇǾfëñÛb¼I»2M^(W"änA¨E;äb$1ß“ÇÀS„‡¤ Iá!¡aq ¢¡PE±ç¡x9"@ˆ D ö8¥ÂðjV8ª2¤wÆé ¼ZçòI“ØË[Ï‘SÖ6¼<ëaÓ Á‹õø®µ,¦GÖWîK „+JNnŽ0),@ –Æ&úŽÊ?œ·Û¹ï í›Ëê•×vÊž 5 2 1 °ã2Ý@IDATxì]`TEúŸy»›@B¯ ˆ€ŠŠÅ‚{={/§$+6H6º’‚§œJI°wú?Ñ»³Wì(6P@‘éHÝ÷ÞüßKÞò²Ù$›ìnØ$ßÀæÍ›òÍÌoÊ7óÍ7ó„`Ã0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#Ð`ÈK‰bQ¾¬“ ¿pçe{ç6P’û=™;}w/óûû{]žoܺPRJÉÑ“û›šÿ0Mi;D’üuÆøñ;C…e7F Þ°Û¼[ˆµÓ³½ëã=¿ñ”?`Ö¸Ï÷¹{“˜×_ùÍ#„K¹ µˆAíu:&=«—!Õ!à]Û6©‰Mgè·ôꫯ6j]¿©Ù7*e¶ÎËʘQ? áÅÒuõB¶£Ó¤”*¼X± •âËMÒŒ‡Ò裙šRl’.ÏÊ™¾·D#åR]¿J5U3ô  ÷h0M$S½ÙÿRB ¨a“þŠ’¦Léôă‡o ïwù?°T/)Eâ?r³ú#ÊDãÕfñ]OʋѭÃú¼ÔTMùº+'§³^&[wlôùn+©)lsó³Û¼.5/ÊžÝÜÊIy-ãå}üPC”åæësOJ$X JP/áÍÜ%•üWrǤ‡ž;¶8€šZÜT_Öñ¦./SêLàƒ?ÀøÀœ…Ë·§x³Þ>°ÿˆhOîzúéÄÒÍ/ %OJvö›yé雚¶¡ÊCå.Û\ð òokÕžÂXL—°7M•âÍüAÓ´GgNHÿ(Tüh¹•éeÓä0L8æ£o<ƒ)‘ uߢ©2}­ÌðPJ¤ éï„בÑÂ2:[Äw½u¿nMBäú-ĬˆiUkJ Íɨ§[7ŠüÓ¨ÙH°ª„=¢‚@£dü©ÞÌ» åYK dK±ªûA¹ˆ$)aÄÀ6+›;÷î(ðÐcQ¿±Ì½iKùƇµhÛŸÖ›Fl#>Œñ醟šr_‹mU5-êŽñðæ\b*ãiTƒ!…LïîNŸäóIÓY-wùžnSê/x¢Þ«tÝüè¡Çï;ùá‡÷8Ã4G»_éO‚Ñ´’šœœ;!}\°Üç{±ÅF±¹K¬°ˆÿÒrÚ•’(-4/C(<®ÄJü%5#çdÓ4nÃÝ¢¹]'Îô_\¤Ñ¾‰}»ˆq«„¨ŒIp¸HÞu—ìkIu„œ×l˜>C'†ß;ìbmbúb'ŒO/@ü?Øç¦Ç*-'ݦÚלed{íhµ‰Ÿ´?&…Ñ—š&î„òVN0Ó'¿g|wÀïjt®ÿ¢suÙµ×?ŽÜk3³gÏvQµ… ö§8õ‰GtˆÙÓ‹Å;)vA 2Ø¢íj1%˜é“;í!V§‹<hJuIÀ¦…DèuÅœê7LòQ éÓ‰!%g‡búä7Ý7~E¨6ìÌ@$í«<}Ñ­œžÜá¤[;&Ñ­©-…7’¼“^D¸éD®.í)’2ò‰­HŒO?c|:6_ÿöÁ€{–°Æ’zôµ²T%jXy¬«Ü!ÚíÁç›I~ªÉf£p»ÇCi –bšf®rµ<¨ó¡µ)ƤdL:J™úB²̇8÷•ioU(ù3&© {33Ú<a‰ñ/F³s3ӞŠŸOiýÙ@L~9¶Ž®Àg ⿈•í´Š÷À£< 3MJÏ5I®„õ…þ¢‰ð<ñÃ8ZŒxK5—È™9Áû¿@¤j,#É>_šæSˆ7oV¶÷¶j‚A×!+O uº®‡fe¥½2ïA[É$­»‹PñGû& ë¥_@D½=/Ë;$8 hѶÀÓÀÐhׯsR°De”/{ ¡›³ÍâYYP<ÂÂ\ˆ6¹™Þà בÞÌ3 !&`ëÁ¢ Î*àá§°Ð~ËŸ•å=‹ì¶AüC`ÏB¹Ãól2i|ÜÃÝ´Ïwu……ŽÇNx´A=¸Ffd_­ô›á|h·Exˆ~Å{Éž¤Çžò˜RzÕ™oöýPf|åÊ£vV]¸Pîum_#¼Ù÷eNÕ49ø>J4IŸCèêFàóÔ{Oˆ½wl³‘«Q¯ç—Û«ÿKùÈ7rníш{8è$Nê}Þ?š;//sm³L]ón÷ Í­]I ¦Ð5!Fxò=i­šSŸönÓE>§åfe<È`…%ÜöDÁëZ¦à´ìw’ðø±Ç¯ 9]j®çL¥Ó8dvLž/m™Î~ŽHÏz½áV´¡ÓІæÚîö3Å—s˜Ð è È0†„pùðûNó¸pN6këkw Ñîï”Gš$¦>’=}îüŽBÄÄQ®@½þèQb|(El»Þªkúm»ÍK(÷åe¥“¾DÀŒÈ˜x‚Pú‹À¶PzÜWÙ šTïä~¦({ãâ…ÈGW䉸³¶ÿõp¥Ï¬m"H¤[ÕŠèT•4£6¦Oáh@¬úk¬t[j¥ê$r³ •~ø9"#+ƒ3u.¾âOœ99%#ëí”Ü\Ç~Òª'ߟý>èBÏ@vA~^G®þ>Ôã³PþG ÞOOS‰öð;T)ÿU…zá·è—Ãy-⿈tçƒÖñ†¡þ‹FŸâŒÊ.µN_‚|w0¾ïÉÎFã­jÆäätÄÿVøÔ¾µëóŠåt‘º©jŒÊ.Ó}ý‰>áA9N!EÊʾԡåuVy„P°W¯Â@t¿:‡üÁà!Æ.7Àµ¹Ù璉€ÙFà·Ór“b9Ò\D?è½WGdd_‹0¿ þµ¨£Dà÷¡\ª%~ÇØLߦç^0ÞtÃTÿ†¬ã ôÿ‹Áz ênÔÁw´Ês„Õ,Ç[ªóR|O‚YX¦>í+aeˆS©à}«ßl·ñËCÅ vÃäöïÐAxî˜tAGAÊçQg¿âý`àx'T*•«>y·û†iŠ^Ÿ,\þ,èND·G¿øùü²¾íݦ‹e¢4j2&ô‘ffއ~œŒò& Ý|ž&5Å öKõæ\¦ÿ#ê÷RøA×IËŨóè]aú_Àì‡Úqjëk±èï)“&µEüH™ “.,p¤úmèŒ[Å@nÀÞÆÂ”G²/´óh?íz«®=Øáª{Žôe ¦ÿ!àƒÝšö€ÍôÇørú+U6mê¯ v¢}ý ùy }ÂÀ87ãå5ÍénukWv¹ÓŠõ&7=Tð{ºiʾU㨾ҔwiRœ53+ã ÛŸˆßo~€Æp‘¶~Ë­pŸeûÑs÷ž²ñxœ‹Nû-º´¹×Èý^ßSíŠôÂwï2¢Uæ?ÈÝiÀ¸ÒÑØþ'[»oË7n·í—’‘}‡2ÍY¦2'B”ˆ}¿ê÷üò|©EXÍÏ­Û‹ŠÁ…ø»MÇ~–›×)˜ê¿íÕ¸&\9†0ÞÁÀð8$Ûµm0Ñö³ã9ŸÀîc¼4¥žÌIÔ¹ÑÿÍÅ(ǯ u$òLÏlg\tª³?@$‰FH“—é}Ϧ¤gýˆçñR$Ü›]õØIüþ²ç@Í­IuGn–—‘eh°Ü"¦TÑK@ÚmPþñÒ%/Λà}ÏOƒøÎ=eßÃÿðÒ­£à>Õö‹ö+ïOR3²00‹¤¿äë‘9wÌÌLÃàW³‰¤}9)_*ÛTL&o„ÔãŸÀïüìô;aj²Ó䱬иmh{²;©¿SB‚c€É{v•Â>úWNåÝ43‘VOÍ¥|Ê¡>íÝ™/§½®í)¢29vÚ¥²¶1;·™€Ó.—Ó{£‘MuCúKµšH*½øEÔÜqê Ÿ÷;;&WBêuCÉ\,\ޤER8}-Úý]ê^Œgc÷¾hå¹~–c¼³&^¦zº'ÿ¼Ï÷d_gÛ²Ë gµí!&ÈB]×?s+ü.ž‘™öµ¤LÇDU¨ŽèB 9Áv§'I\8 îtkÊö:Í0÷?ÊbÞšÔÖ†› $åaUyÜ*ñ¤zÖÉôÉš/m9Ò¸‡ì¦’iΙ8 hð“Êívym¦Oa§úîÛ¥¤k,ÙÆKϪ†DO-RœLŸÂ´i“ðè %ˆØŽüTä¢Ä å.fèÕ»),wM sU˜™Yiï‚!Cd+ý˜UßYàÏÓÍ¥-;ŒóérYŒR^q¦ÓýO}îéèÐ¥¯!ÏK1c¾Ð)!;˜ÝiH«(¡Së¹Î¸õ±ëºÿ š ȧCd`úD‹fèÖj%a¬,¦çMH0} bMt¤ü\©A!¢E͉¶3´ò/Àá{àu˜asi’C“¼êö#o_Q˾Peê  jßÌt 07Û[‰éGšw´™cÑïn fúVêÑÞ«C¢.í)Ò2U—ôK·‡ÆŒ·£`þ,rFú&ö®.N%w½8 xar+_w2} “›•F’Ë÷Ñgúkë¶^])^ /ÑìïtOˆ©ÔÝ(×^w²ûÆàñnVfú¿©?£_´Ç¶gF¨lÕØBD {üº1^¤p]ƒ­¾Ê‹¥zR4ÍCØT6tÏéÛTvmºo†ñW0_Kd'õmáW‰i…Eç )ní¿¡hÍÈÿ:ÏF4ÌÞ[üÙ=ì0ºá? ’ö9?Ÿá¿Êv·Ÿ=\ãæÃ¯ ï‡Ü÷ä“C©>Ïó=P%ÿÖyj%¿¤Ð†i«Ê+öö¿E§‡h\ í}üpgšõ¢ÓcO[®ž‘éµhÚþè 3ÜÒs4˜Ò‹èx‰(_Š2ý‹Àæ¤ø²iï<`Ì$÷gHÃ@¸aX‘`¬*7¦Ðh›B˜B|±ï§ ÑVæoZî‹¿ë¶žHŒ˜áœüëhAÚgWD™V—¨Ò-+I!ì¸n—°&È_­8Ûqêûœéõæwwz:¤Jw ],VÇ“dg£¾a=Ä $=qÒŽ¸}9‰Ehï"Nù“Äݨÿs±íuwmÊ‘‘æiýD“ÓPÙ®o{E«.í)Ò2…J?ØÍ*›’~©dS7*IƒÃ:Þ/"»Kj•&¶¿Æ:ú¤BNêípÎg4û»®ÉÓ06%X“jn‡t¹ÝÓ)}ôà œù°í5µ; =߄Ԡ.kšƒ½ûƒ4%oÍËJ{Ó¦Ân-B E4c”obŸþÍÆ©Ò Ï¥¦•öÎ7 =•ni)燓_%ewì'¡ýÉu¡Ã'†t§Õ”ÄV"Nw¥i½ñ\oÅW´× ƒ£8ÈÏÿYvÇŸzÆIì •P\PLaux#ž¤2„4Hr;eÿÑaj7àÄ/"ìD\dtB§Ù1 êºÑ²+ñ•Ãv·ŸÓ³^ û_Gú¦Œ3ü¥7#È] Ï’~ñ-ÄÂwäf¦ÿ‹ÂÒ,úXü”Ñåвˆ&ØÆ¸ LlÇÙûÿ颸!ϸ"»ËàO"6Êÿ™ôDÚÑ3CÌf΢ß{ƒ†ÞÝí]]—co-<²¼Î‚2`ê Û…(¥zªuŸÊ›òh¶5Ñq’qc‡Á)FtúÛ+ôž­Fed ƒi$Pºz$òýß\€ã§çÒI+^¤í+8ñÞk+m7aŸø2LðÞÃ>íß¡tw&ˆ³Z&Éçÿžž¾¹ éóŽ©åÏUh:êÛÞ$DÛS„er¦]“½M»Ä´‚]¥— N†cîö`É–3®U†…Ë{£ßš†2Fcíô/·›Ö8…6Þ·ª_h—höw´íŠt%¡!MW1nÍF‘í‡çÁT¦à£¦µµ›(ÚEÙ^½èu <‡B4kf–÷ÛÏù”=;ÿ]­Ûz*ÆæK ]ÿ¸½Ë³òf<–öq¨qÒ·©ÙµFU UÞˆHé#ì|WˆwÐIBŠqº‹A»ª£…N³…üÐPºÙa`·Ó>LüÜ?œ"Àlu'Ì* ùØjÓŠô™àñ¼L+r0ÚhßGO‚ñK…Äÿ±Ï­ª®‹Åjã qP—¾ÈoÊ–ˆ;ƒ®wݺ|^×qëLê##\”ûmê¨ ]Z}Jâ<Ç„ì<àý,+¾K«,jÛG4lÛ —“¤ém¦É_ØpH¿~U$+u‰Oa_ýu “š7‚ÊPúCÐé? ÷¼Š©8:4Ìû$|ì…Dë֤ﴊ÷a ’VñÇêþ/x’ˆÑŸDùŒ|œ®ýȤ㰽ñnL¤m†µ¡Ž'QܺÝÓy»ð~Y~Ým]â^uÕUuš(„¢M4>Y˜ý`°ÆáuÁnuyŸéóþ€•ݭе Ó „éXŠ>¢öU—<„[¶ ­èÑPŒ|wbÜ‚º¾íâQ$.€"쩤¼×Ö7¢ÑÞëÚž²> (ù úó Àö¯¥~?‰Á/Cß. ®SwÏv±ÝQHa êìÉ{´ú;ÆŸßÁÐ1‹ÁZAZ$=숾mvÆX;xmí!NÉŽÊ%®Â¾ÆEîÞZè/|~U¤t¾bñð*¬¯–ù3îÆ×B‘ð`"ð´t»(lS6kÅ/µò½Y|dŒ´Šk«˜9g íAKnMp·š2¼ß¾Ü$¤ïÁ–«Û½Æö•JYƒD݇Ùnûùù"¥}wÒîø*Ý ôDÅZîd×`A:ÂZÛ#‘Î>ºÿ÷èĻхq4 >J^ <÷xº´²ÄúÌ¥¤µŸåµ³Áô‡s’tD¼Ú'Út‚éoBçLÂÖDrkHCƒIE‚33ÓgGšwÛo‰V=ÝmZ Ù¾êZ6RŒ$íðîîþÐþß@Î;—馯óQ{¯k{jÈú “<É÷S{¶—’æ;š_¡¯ý¤ïÀ}=úD+Rh³Ý£ñŒZ×ÄOV~¤ì]]¾Rsr (£Œ¸"ÄqÜê¢UqÇƦ̚àýôtì~vNʽU9ÌÊÿ#!˜àéÈËÝ¡Ž.Ek¯Šñ“Â+0pÕ¹`wI•U˜³FH³É –›&|=Tg ²ëæ%ÁNôNg`ѱz¢Al9@œ´!ÆíúnXIª+è¸SÀ}?Y»¶y›öÛÁú¯ ›¨À ¯"FݲcRH¥ÅZ²Y>Ñr‰ŽÄù ù˜y‡”GsÎCç€Aç=§Ò^ Oò{HÓ|œ%…jÅÕÄG6ZŸtšF“úÕ„µS/»¥ÿFé\fYx³Þ"ÎÚW(`iNp»ÒËýè¦ò¥ö~{j€2ÙðÑ“N ©¶ÜLõ4z…¥ýï Sn—Ö$ m)Uýjp©¥¯E«¿'j­ç“˜+ð+«]¤AjDF*k\C®kôÂz¤ŒФΣ¹éÆÖ"ŒÐ¸‡%JƵ›¼Ì4œº¡»X0ª £_í1ˆFÅø nÍ-î§–¦ü(Îö>JsžŽÄ@‰c.N3À®!yÕUÍè`­xbèØ|ú[ym²óL}®/í74’çÑBÚ•/š8±}0mštÐ]Áî±x'ŒUø«À£Ó&ÿò‡ÐtéôÂkÁ_%¤•Á¨ŒœÓH‰&T>è¬7°¸xí”î„J×#lÅ1Ô}ôÄQKÌoÓ)¬Ä—èåPœ§#„‘¬%jû‡ñ\Ma ¨iBU£i®G©Î1¹yh¤/kppú6C°[<¼ãƲ³é8X¨¼Xç°•ùAŽòž&žÚõƒߤžvÞœOÝ4­“$Ô¿l÷†È{¸íÝÎS¨g]ÚSC”)8–Fº¤{1Tg0Î;‚ýéÝåv¥¡#âø¯|€¾_* ©í›Ö*ȯƾFa£Ñß­…–”tSG(->|zeTÆDÜʨîGˆi?”Çz¿NË·DIí.¤ëʘMÚþ61:¶œò³‰¢ü¸7NÚ³¯)>Ý­P´7ŠÕøph¬üâöÂíEWbyޝ,BGA#WqkÕ<1ó{[¸[ÜîdÜ•ÊKW5*±He?AYé50Ïß #ïæ/4q ¯‚Ö,´ŒÝ¬ÁÙ/!Iï/2#üEú^c)& tŒdVníÀzá8Û°2©hÅ{3^¬ìÈ^0LýNˆŒÇcBB=÷…à´JM½Ž‘} ùmØËŸ¼æ£¬Û¾3°:W!E ñÚà㆘@UˆõÕÙè‹[·õ|L4Þ­ápÇù}5Ïš T ÚE“¯ëß„Aì^Ôe/äí3¢ ®á̤tÃÜ'ï‡M¿ø Ú¸”þLÀz÷Á%ún”AT™€Ám¿hÂ{ýFÙiØ;üu³ ³ì|ÊÕFü%çâÙx~ÞÝ5ä1gFã¥}™ef?Ü ÷ òÛóÄ|SˆÕÈg7¬¦NÀ…@gX++·œÚÐy§½;ól¯k{Úõár·¸Ëô—žEŒ38ÿôNG‰ÑÆ¢¿âÚlã-Ø¿Dÿùwÿí… lw9÷*çÑ÷b,Ä]¶©¥¯Q°hõ÷î´{qÊ©è¥lÔçžñû'¸«`+ÚÍ@]éW!¿HpD?'Ý­¨ÜðÆó¡H÷¦½zá?𼄶µp}û”|ó›cà÷>:á 8íÁr¬X¿Û˜¢òõïPG´£–±8"ÔèVü„iF'»[E÷]ãu=V¼'¡VÇ ‚oAcêƒYä.)RpfýÒ`&æÄ Æ=üèþçÂmš;V›j ”Q¨3u ë/»»<…ÄGÎ8dŸ––¶šð'нèk §@«^< E-(ŠàúL©–ÙÊoÁqcñN-i©ÒýKItœŽ­¬“ùò—ˆr‚騿‚1=Ìn¢FÿªæÑV¹ô„à¶qWY41¡ õ ÷DwÂ[ûÒÔÂó}ºdGji`ŽÀR\rÌÄêþ}ôpubVÆ8Í%ieCÌ'p"pÇdw•ë8ÃÇ…]Š×1¸Ðí†Ç¡\¸MMÜSÞF ´…Iê$µ»§ÿyÁÓxi_.áY‡öBõz ýèÓðóRûæ_ºÜò¬<_ú'Ö ‘÷pÚ»3O¡ìuiO Q¦à<Ò‰|E³Æ}jº“C“îÁ´ý‰öt"ÚÙ½T?Vß–´p‘/âf€J'%ÂékÑêï¤H‡1ôtÑgÐŽúÒxc±¤L‡‰;N*œ¦ÿTpÙ£ñÞ¦mâ(ŒYËÐï.‚áƒDãõñØS>æ©lôŧÉ·¶GE^ÉÞ, x_ã7$ªñlØÙßï{ì{™k+V;1xµEÃÃýX÷ÃhLy\žiŽ[i»ÕF‡ü-q‘èc*WAÛÖêÏš®Á ‡^,èÿ›?þèTâ—XI˜I¸Ô7?7-íϺ”7Vù£»óÍ­E}M—ájѦåÁÛvº´½SRPܯ…h¹!øF9;L<=éšà¢=e éî€Ó%Eº»N¥iN÷_ëòàóÅ7!Ýûr3½ƒø#õG;8Þ“ÝÕô»=ÏøÞPÿœE'f´Êçó½Øb£¾¡¾—ïóÝVÜÅ•”Œœ…+aeM_Kœ6\4‹ |Ffä 1Lc..ìù&/Ó‹ïÅ—ÏYÅè¼3Àtçe{9Ýõû|J£ÏP†¾¡Â¡LGKSnz©éYטBý»¶ðšt]˜›•ö~májóOñ=ÑIùK>ŧŒÓ¤†¯&‰®nWëÕwùžnSê/ø -­Åç4ñ MÙ±•+qå^Ñ{ø„°º§ÕF»&|“û| ix¨0šwçfe<ƒvÒaŽ &Ý¢×VîPý ¾8Žòe¤ûÕ<·æ:}FfÚ×”6¾é~7¾éžVTdv¢L ßÒ÷ÝçkøíÌÌôÙµå/Rÿh–ÏÎKªwr?S”>™ïÏ¿nÌ_G¹~£rµkí¹'ž¿öi—¡¦§Ï÷¹{£ÿ›/…*¦Ï¨­)l]ý¬ …2_Çç~7âsÂ'Õ5~s ß, r•’š‘5ïÓnQ°¤z3oÏ׳O©;¢@n¿’Hðx¾)1ýWÚ™À¤áh0ã |S;Mhj¹íîÒÜ•¾Ãn»×õ)õ⋱Òo-¤gD.Vùvü”ô¬«ð‰ÑŽšæºxffÚ<Û}„7g"òaØï=¥X*4™Lß ÿ%Ø­Q½Ç¨‡ƒA¾žµö.ßã'Gc%nèòfLÖWOŸ0~.¿HñfŽAÝü ñßR¸^Çs³©Ô`|^z°)ea8ù‹$L,ú9}²yçž²/&H»ÜâkeÈž&ÊÉV÷ÆÎô oŸo˜>"#k¬&<ßD‚p\k¨Œ)Xlöã÷šh^Œ_ŠÙø ïÄÑéYoOÏö®¯šð}Á¸.Aè­áLjßö;‡©d(Ãn·ú|†Ïûí­§ZO‰¥@w×ÃK„·¬”än&tnUi‚1++íí}"¶m›5ÁûŸˆ©ÄµóÚŠ92cⱆ©X[¸püéûíùúï×@bE’t1ËÜ‚ñŠáûßèøÖ|T™IMy‹E?ß½Ç8M(u°¦i·äf¦¿\‘>õ³˜K/j*k´ýfeziµ5ƒ‰Äß ùIÓ¿D‡c‚xtÔˆ7BÍŠñ{Üît¿ß9ñ ÔíEáÔ/öȇc°IÇÐs,:>ÄŠêìÁ¥Ñ\Jvv7Q¤faE|¨]X‘`õ£d& Aø^?ýÅ™V²sÐHÑÉOw h鼆o&mž••~7…Oñå&)ck&6μ7h.GøÙÝÜiSœ[  w üÏéîöÞ”od?†|^‡ tNìÒ¶Ë3wß]êLÛ¶£Ód S wy\×Íô_c»×õ‰ò~‡|= yD™é ¼CñÕ§I¹ÙÞ¬rñ¥ÿ.©Ô0ä¯'ÂíR~äJÖÆÏ?~'íÑ©4¬Ö®&<òýÙ?C¼ Á‚|+ýÕ‚;f%› ~ w—“gf{_áÍzNS¢iŒ±óÀJ©³‘N_%Õv)´wò²ÒIºu3"#ç iš(ï@”ͺÿÉ-ÆMÏzx)%†•áÔHê>Ò ×«×R&;OÕÕ9Ú;Vßú Wê÷‚p~ÔÅ6lInX›£'Mj‡ö:˜§öXÑþê–Ú#¶ŸÂÙf“¾â"Ô´Ún ·Xos0ý€W°¥¶:¢ð£}ÓZùý;¿A›ÍA{BÿÛgFfdßj˜ê¾YÙÞ£kêç(ÄÌu/_ %eîµâ»Ô²€[ –‘ÞÌ¡&Æ#ôñֿߥtÝ“[!£¾ ü[Gû;Xu~ ]Ró:1¦1}äÙÊ“"öèO‚ÆPÔI'”âWM¨Œ™YŸ9³P[_¶ÃV×6h< ?%åËÎ @ ßÖ2ÆÙôO·KþƒÂ?føÒaì¼eeSÐæ›Ñ„VÆr/Ë…)Ù××VrÚïÆ`ó!:™ ûÌ÷¬gD¾žÿÅ…l® î/£Ñý‰×_°GE?t¢àYkávÉ}¾';PX2¤¬¿a˜( I}dbù$î´Ÿt®Ð”ZCáhµ£ü[ç9ß„ó%¤q³ÔÄ»Øo~ ò 0RvBÜSòõœ©˜$œ‡Žþ,üªŽé§z³Æ ã!—Kú"aúéòœ¦ÿ&þÈçXˆæ?°üTÙqLÿ#¥É¿Âí]¤{³^hÔ&;™»ž~:Ý÷Hë¼¾‰úMÓŸ‰0Q“èX Uü™žuƒTÆg8w¢=܃Ÿ¹j§‹²ŸR3rh»‡æ+õ®{gZõµ×µ‡S&G^BÖ¹!]ËÑËW©R=Mý@Imªõ¡ë{/О øyöoª»ašIÏêe‡³ŸJ˜ÐB''+ceÀMo÷a`~gÚn¡žá–Ç% TŸhj¢c0S‰Î̵Æ~nÇ«kùìxÝ<ésÑ~6£y­¶l{„x¦fdß%¢Oá…î#Å,ù~ÄýUºä& ž’›ëÁ¸A}ê|wõ]ôÉûq’nšŸ§<’=ŒÂXcú×Eb¯ÿKÔ‡ýp<êc"Æ¥NØC{ïNof YákéËTé²mTø°¡—Qnê4ÆÙ‘Obøô#'”¤ÙÔfµâ/ÓËZb y3İW8ŠeW§eJ°tKÁtœwgee\VêKXÍýüBª7ç‚ii–rÛlÌf'`õ²Ê©\”’1iŽ2M­P/9qߤøPTù:d"úî%púžÜKÍ=§ÑäBižOè}£þÇ„9«È#íU$œÿ7қ󣡌w(m§b&2½!ØÝsèŸïê2¢Ê`²s‡i*0Xíú™Òi_1bƒÁ"“ˆófNðV¢W±‚r®¢þÜ»H%-IË3ii´52ûtÐ!PCØÁ}Áoö!àûW¹WÉoéÖ‚QtpI÷ ™™ã® ŠL™’\°«t êúµYYÞÀÄÊKÿܨÏkšæT”å¤ÔG¯wÝG#»uiçá–É)ª®ÎGd@fª»Ý ïTÙãW"“×÷qºd¼]ÆT_ÖÓ¯~Ô…¼n´ò´ )| Éù.©RòHh©½VVdŒó#‰Â›Ø¶ûVäß–Ç*ÿ[Ÿò8ㇲ£Ÿo‡{È~_‡òâÀBÒ;Hà&`Rü Æ›? Ýzº…§MÞ3¾» œá¬Õ±ëd´½· ɺÂQÏÙáÔ†­#0ŽãöhÇN÷¥ÿRáþ<¤dk„i>Ž÷Àbu˜%Ö÷s³ÒÓìøåõ!~ô IRÊ@}ÔÖ—íøô¬®m8ý®c\p|~ f5S‚DÙ*¯t{F±¶zñSÕÁW¶mÏ©ÄÒ\âEgÕRƒ \šCœîÁv:ކëŸRCm?¤y–¬Ÿ#òçXÍã/7¦9ŒÂRœru%µOLßrž™•†Y¼\©„ymEÌ}%½Õ1}ˆØTê#Y—b²3³ûѵ1Ó}Dk·aíÓp'мþ“”¶48×N9¼¨£+1ãÿ¢^L’0ÝU~ÄxB˜={ÊNAþ»a2HR•€!å%Ô×4´ŠÁ£›tpdu [oK]Úy¸erf¦.uîŒçVž—ï3MŸv¿ Ë×ÞNw(|^‡•½a¶òTÚç&ìqw8 “ÇG7M–¿£œÉM®-SŸòØq#}†[¾àt,ñ·’g€iþ†2M)ñl@›œ|ß“O¶´Ã*}‹Õö0€Mu0}ÛÛz–oóÉ_L¿Ü]ÔR –((·+¬ú¨”HÅKu}9ü¶Q1.TFØ­^4«¿PžoÜ:¬ÜÓ Î{ÏWptëC«3¡çÙ{¸}Ènb2VªmwUŒ6,E´—íVݳsˆåÔPòOñMê©üþc]R<`JåFü{I¤ölVÆjL†A^e­ö),üA~þKöæ„èìîö´·gøÁ^D°7˜þ8HæC· ¯j€\dh x´Šv^ †YöÆ`ÝLÚb¨$!¤X)*èöA…ÔKáõ¸“¢ÀÊÆ&ܪVhÛ+=•:„Þ]žÄ•Üñ"5m¹Bc1têfU}ë>˜.˜ÚX¸YéZ~RûÒžêÚF¥èá´s´°Ë ^Mü«±$µÕÖ9½¨ÏáXånLÜœîXÑß‚÷·óÆÛít'ûtߘ½xdáÌû›ô 7 CøÏÐGσdnN½ÊœH=ßÃ-_(òИ ÷óFú²„ä>Ll(ÜQt"ÜΠðØécB›ÍåIXNï¡ úY0Þ–Àˆ}ý®Þ4cGáAxÚn›dÏú}¡Ð†«©:õå0ÛF}Æ8g^ÙîÈ¢7ÞØÝÜÞiØ/¿ƒÆL(ù©ûwú¥Á*$™80úíWVa†«áCÙ1ÿ®§}þ"½ðR%ÅŽܧ~A-ß¿|O™ßë{êå"}ï1èèS+ÑPªR~~JÀ]b« ²™öèèB Š•+Þ”·cpý?Ì®ÇJb¶;f„ XG U°!QíÞíEß`2“„({ÎoBJ±y¸y ˆzë‘\¨(HÃ, åQ›ʳ23¦×.Ø_&êUëÆ…úBc‚ŽU7Õ½#ALLúcúyLÀIš¿ìaXjkç6‰pÊ¢Îm¿šžSx è‰¬)ˆHõå 0ýÆ éÒ­)`Å7Ïš8ñ¿F¡>Èßðsì8u)§ÒS)O¥÷0^Â)_mdfúÒE˜Û!þŸÉú´_Îay¾´eh«I7Axªmë˜'£Ïýe¾W*¥ƒŽ|„ÞBÒöZÀ„“ߺöåPãA Á`KǸàèü^š-ã§½µ1“î(Sþ~}W| VÞ$5¥­ÀúM·6qAÀ£­¥˜cCY/9´/†˜ÿ]  ÌÊ?€ŠÎ9…ªx DǚǓðI€´Tó1èxwZÊ5ä«õ;ƒÚv”m*˜ýØo6n½3ŸL͘ô-ÎÌ/´ý£ýÄ…<£1Éè#JçäNH” ÚÄØË뢵ÕÀªgIVK ë­ùÐÔf‘•ÞÎJõŠn1ëËQãêS¶æ'j"ׯâ´ÌqK°W5 ƒÆ0•Êàöü€!¡Pùfj6XÕo”Äh•ÌL¯7í¥š0ÎÄèrö×"Z—’o£#Ÿ.•µ¿¿xºï!:Pn”¥Õ~Ž¥äd»á …Á£0 %©ÏεZ1ì @ÝÝÝÇb²Ü4õÙt”©Öˆõp0ª½Ý´S>·IÌž=Û…ò^g¿Gë Ü¡ý¯Î Æ*ZôtZtN^„úÄŠK¿ÑénÙ•y#V;뻉¾–(µÞu_…pä5µóº”©öœ”k——F•¾P{\RtÃù%£zÕž ×ÚNt\ˆvmm#Ô¥<´m‰¶Ì¡Ît(p;ÇéFöêúyp¸h¼Å—Ó%ÖÒS&ˆŸ×RC7«¶= ƒ¾@§MÎuJ¢Y7×çœáõ[wør®Q~ó']ßAâþ›Â[—p¸qòAܺQÿ6º /j¦>hÎÂå·YLަ6Ùº^z+5?ÆeCk¦{’úÑØûíÕrµÊ›õÉ‘„æúmÁ¾ŒzÙš Õë†HH0¥ŸNK\-¡›*+XÖ½î듯pâT×Îë^¦êS;û¨¾s?Yôûœf¹Ìç~ˆ±Œ*ÚýÕG›Ìœ³ Òîáqk/³®pÖwÿýè]\½ÜÚ.\¡0x΢ß/B“êCàPœz”zòœ.¹Ws¹ÞÄ­ymq çÃH§}pjîçÁ¡Ã{§tçíå\©\P¦Ž†”à eþ}ý×6­?$J4‘Ä6ݳX Œ…ÞGôÈ7òJ7¢Ý•+·¶òd˽þ›p±Ä§8ù3âÊ…¸òâ`ŒAÐ0]úùˆV]L¬úr¬Æ¸º”­9‡mÖ+~ªxk V´?ˆ]Ù ƒ£.c r;fÑ—~õ½á7ƒ¡¼à ©´À^›Û•ðo„y Ê7³Õÿ®Bè•MSŸ`©@Ê5>5vl±í>Õwß.¬ ¾"?—” ùc´°e’uìm5ÑõûK7á¼ó›`œ?z’µSÃ] Ùi9Ÿ´_ˆ2ÝÆ|#˜Ø_~ѲwwŸš éÆlþ …ƵÐy{È*M$œ­4l:$)Á±GS’Ŧ¡>ÐUÙ2ÔÃ,liìÛ·Gá9+3ýß(Ë(4—kK•\Yžž¸J¸ä•Pœü—3‰úÔ½3~4í5µóº”©¦uڒ„áfÔã¢`­tJ³T$êðë€N:S7¯”é_„¾øÞÕ Gã¸õNÔ¥<Ø’¹4æb2÷”á×WCö5F‚­špÝ\ÖšúypØpß!­Ø å»›q†á]Lî1Îé+õÊúòv6-\Òõ&tqÔX‹0&|>–jš†uJ†”!µòx,b–ଙªt9úùGè ¤ Z/«¾Ë1®^mf‘0±e¤ W¨¶o×:aKu÷gÓ½Û…þÓ²½káN;nõgtÆÑý7„scY842 f> O~Cä¾n¶ÕŸßõÙ¬ôu»c|Œ­““Ó‘."¢}æØ¦ÔpÔ£U¦‘¾‰½5Ñf[…~Ô @GÒü\º–Ü2Ù“ïdŒ¡ ·<´õ¥é;:wr{×’^D(Z¶[,ú9”ÛËRÑÑtÅ3ÒÓ7ÖÖ~ïô=Þ]Š–{ƒÏûÛy¤-HCz‹–ZÑð~ý¶D£ÿŪ/7ö1ÎÆœŸŒ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#dL¨•1qaç¹—’ ß‘­ƒ‚ð+#À0Œ@D@ ±G*¹E[ëÒ\ÍÈ?¿ ³Q)fŒßç{±E¾?ÿN!Õ=B‰¥¦Pr3ÿF$º·Q¡Ä™eF€¨`ü­Àø»ƒtUJhBŠ àïáéñ¬Ïw[I½ˆr¤ˆˆ ãOñfžƒ\ÍB%÷”R~ˆß+Z’öÞŒñãwF”[ŽÌ0Œ#Ð(5qb{³È¼P)u~ça1¸‘—•ñq£,P#ÎtÔJFÖ½ÊOH©~Å,ïîÜlïWÎ:#À0Œ@”HMÏ:Ýâ=Bjâ¼LïÔ('Áäj@ÀUƒ_½Ê™¾zJ ùáérq^æØu&ÂF€`š4ó¿þlíñg]ó²4‹ÅêìñCÏÚ=ÿ«Ï¾kÒ…Ž£ÂEmÅOâ}¥äû øßܬôk ÞÇÖF€`F 4`ú2Õ›ý˜Åå`°Ø?4NÑvÕ¢Aù@g‰÷…§ó­Ìô£*Ó`F i#`ñ ð ”r ~³*xIÓ.t”.*ŒŸ´÷-E>ìéçùR‹â \œF€`F€ñ 0¢»ˆ‡X'ÁAž{£ÂøéÈfn²"_coœF€`âÄC¬ãß Ÿ|³K1bÆO—ó”ŸÓ—¯4;ô¸ÀŒ#À0QAŒÿâ%O‰ E&R3~º‘.ç¡súÕ%ÂîŒ#À0Œ@M!^RqËkMAÙ/B"füH¿ÝÈÇ—óDX`fŒ€ÅCÀKA¯f CƒÝi*t÷>hÐ=ÌlF€`z#@Wº#2ñ61D bÆsûôÁ¾{?†•ĤF€h€Ÿ/á¸Å¸²£!êq™<#À0Œ#ÀD füÑB’éÔ º¹ëžìì®cÒ³x_¯^6|¤ÙÅM?4|Êœ"#ÀDˆEýÑÈÓh~Üç{²C¡^<ßw¸GxÚ)éYÛquçOB“³ge¦¿Ð”Pñù”æóI|—$:fdFÎh?ÏÅ'N¿ÁNN³n@ "="=kD§ƒó²½ƒ‚¼"{U¢+ŽÍ0û ^ñï/ä›qº=þxëB½ðC¡Ô_Á˜žÑ¤ëB·Gž¬¤ÌÄ{4%w55xòõ¬µwù?0êåRbHjFÖè¨Óe‚Œ#Ðdà“­Úø,ÝŽqOþ»ÈÝAné4=ë᥎œÒ×¹þîxoÖ‘5L=úLŸÐ‘b6vK&ŽNÏz{z¶w}“Œ Á01E€LáeâÁ䛇a¥º¦i©Ó3+1ýà Ö{Š/7I[3!$?Ò€Þ¸3b9ÄÚ³»¹Ó¦8EçØ&¸G.î–c”^ò´j¸b!¶¾îÎw ±'IùK¦@Â0áÚH%ç¹=®{§ûÆ>]NCœÛÊt'$Ï îéHσŒ|çBØ™¾ñkìL¦x³¼_j»ÙOˆØ¿Ç«^‚ߌ‘Þ̦Ò'_©ßÿ öÆý ¹ â÷3ὙÑ×t|ÏòX¤¹qßéî>0Íç»­ÄSÝÓãv§ûýÆ0]ˆsQuáœî#2rΦ™´S?0ùÉ-ÆM¬()Ùw(eŽDžû#_Ë6ñÊœôlûˆÊaÓà'#ÀÄõÇcNÁ‰€¡NBŸuT¿çΡì>ßìåß:O˜â&!åKš&o–šx æû³ß¨GÊN`J ½xŽj.úèÇ'`¦×+}Ëk`úŸbÂP*¥ö žÏùŸ¬ëú´÷ cÑPîÕ ¿"æ ]ƒ{…&&à6±ãL¿ñÓhßäa…Ù +íûÞ÷ÙÀ Äíƒ Cº–ƒÖlËWª§5©e)©MµÞñ'5=ëøˆ<»P¾{‘™á<"_Ïë&LMhEñ”P‚I_oÓ­î92=멌ÏPþHëü¼˜t´ÓEÙO©9';ãa"ô2ÍYðÿEs‰›v”î§-g8²GZŽ`züÎ0±C€Wü±Ã–)‡@Ìy0˜Îª«¯¾Úá]Éi£þÇb¢XéXþo¤7çGCï¤zs.ÈÍJ{ߎFŸ„IÅìYÙÞœ ·7° M‚òàÍ`ZÓs³ÒÇØî`T{L¡27éSÛmÀ€ÿ“›å}Ôv雸>ÿr]/}nô ÛÌÊLûZðÝ„©îNt'¼óŒïá v仞~:±tKÁäïÝYY—U¸¿”êÍü “›‚ËgÇs>Ëô²–,¼ŠrÞ L55Å÷ÄÇy¾¶9ÃØö¦LI.ØU:E ùÚ¬,o`’àó}þÏúܹ¦iNEº'‘¢`ʤImÕ^ÿxÄ}aï°iÜë{êõB!&VûL4ʱÛF Ö`\iÆÌѾiGêþ!±“‰zUwpFDè T`ªµ@…ÙÂJ•Þî@IDAT( NS™¢“€‰v9„ÙÂ01A€L`e¢Õ!  õ¡Ä-Ÿ,þã„y¡ºp–»TóÁg{… #UOÈ—‡ô‹Ð±¨Pï•WÅJn¿ÞÁôMð¾*Òì±wRþbƒ/V›Mi+°Æ­͛éK_PmÀ0=è”ØŒIw”)ÿ¿¾+Š`ˆm ×|!taÉžpÜð ‹n¹aŸC[I¯˜ ­Ey…,1{à±›Ül  äH¾å&Úå°éò“`bƒ@••Ml’aªŒ@9æA]^;Y!”ùXªwr¿qQò{°­sˆ™:Ã¥dL: 壡áÿ™Ó=ZvÝ0.rÒé›Ò2óXÅp—rÜÚÒý€,8=B \§¡Ì0*ë¸=¸öGýj„“F$öi™ã–hRNÂNÁLš:iµèœ¼½\þF§»eWæ`è뻉¾Ö„Ç­LL¤!üæåÎ°å§ Ì3n"å¨DŸ_F ªðŠ?ªp2±ÚÈKMõ§>’=Ò4ÕJ”.À™÷)Â%t+m‹iýq|Ê®7gfŽÿ¹m»Ä¿A ýb¥ˆ£e÷º= +LÓ´ièÓ±"]ÖÍ5ä¹ÚÒ««?è’Èû¡ÔŒì .÷g~Ãìlê¥S!ÚÖ…Û؇—Âý9‚¢Æ$”g²LPËŒRyD÷!M0×}æì£úÎýdÑï;p4î~|“à~Íã1H»¦ïÁ-©ÞìLlæODùÊ4x̶Héæ Jh' éš–—9nñ>JáÙº¹úgçë¿_…iȹ $茣ûoK)Ð9L;nã»·Ô=‘—å…´\*úžPâz&-mku$(_[üÙ=<É{¬£nÕ¬pǽ5ÑfÛtߘ½ÁAË?\Tؾ]ë„-“~xO°´ßÇäät„bž9cüøÊûýA ᜿ûOñÝg8d}8Ø7t9‚²Ë¯æ'¤ò¨¢ìÊj$Yæl2! Æ?"=Šû¸!Ù‘`bó“X Z•&‹ú«bÂ.Œ#À0Œ@“E€“­Z.#À0Œ#PÖꯊ »4S¤ð¼-¥¾ÚqV¿™"ÁÅf¦Œ3þ¦\»\¶:!›õЈ@?6Œ#À4YXÔßd«– Æ0Œ#ÀTE€ULØ…`F€h²0ão²UËcF€`ª"ÀŒ¿*&ìÂ0Œ#À4Y˜ñ7Ùªå‚1Œ#À0U`Æ_vaF€`š,Ìø›lÕrÁF€`ª0㯊 »0Œ#À0MF{ϨŒ‰ƒ ÓÀ÷ÆE/|Ò´;>3غÉÖR5Ã7W÷às±ñ ×u.|/}FæøùÕeç0à6H¤I#S„X‹ÏSÄcJÓ¬îFÅø}¾[äûóïÔ49ßoïFW«¶Nja¶k“$[x<ÍNzQâ÷›» ŠÔž¢ xd¥fdoÒucj›’VÏ<õÔXú–{•ï±7Íf\ÿRq›ª?v³é!4¦dÓ˜bæS&øŒÏw)lš†ñãsçüil|¼¬Ç€>=Ô GôGõí!“Z$ºš@=Ô·Öd§¨¤T,^‘/~\²ê€_Wlx¼¸Uñ=·>èñÒ”¬@ØÄ'!æ6vjî„S&cì½÷öq¾ÛŸŸä£1…Ç“FÞJ"fšƒN?óVÂ`þWŸ¿DÏX Ð÷áéÿèÑ¥]«—ÕÎ2PØ¥½ð¸ͼ%°h„Çà#ûÈC{ ÖlÚ–TXâ¿þ˜!§þ<÷Ë*rg &·)le‚5¦ì-.»~ÐéC÷.øúóï<&ãICð“ ¢6Ë×xËòZÄçñ¤. ÆYØxfü¢¥s±ÒŸ&¦î¸ü ™à‰X@gðÇ&;„áE¸y<‰9×ÝyÿùH‰˜?ÕwsfþܦbÓä˜jGÀ9¦H©M¾å´óPäæ>ž4ÚZ[ÆŸ’âkéñxž?°k;qóŧ6gfUïÆE¸uïÜN´i×nÆ‘GžØ„læ_oš9"·©Æ\{œ÷x@€Æ”]Ú«-’f ~U+ä)nyH<à¯yˆ×JÓŒò.Ã4»_sΉ.^éׯùn×w’f*qÀÀ3Ï*‰ø‘bD¼Ö{ý ^,nSááÄ¡j 1åÚsOt™Juëqtÿ»°¹Ž'ÕbÔ<â‘ÐêÞåö¸î=²ïŠ÷ô#kF„á˜ÜºõHPj‰_á‹_s’¢p›B…³a¢€=¦$¶ly'è5Çñ$0îWñÈøµï~øÓTàÈ^sbN1k8vþ—kND"ÍqÕÏm*f­‹ 7GhLQJu½ì–ÔÁ(?¯úY#ˆ7Æo­Ì<‰ çÒå<8§ßÈàŒÏ쎄g‡.]ÏB[àלféܦâ³Yr®1ö˜Òª]Û³›ÙxÒˆkm_Öã’ñ#{½éF>\γ/§l«7„cë–‰Js»{‚‰û›Óñ>‹ñ£ÌܦêÝ‚8"#PkLÁ-5W/ø4§ñ¤2ô-Þ?åÇ%4Ù ×ðÆ[Þi—g»]Ûdéò¸»à͹âos›Š å––ùŲ5›" Ð0Q_û§(.- ™Øo«òÅW ~éÇŽõG€Æhé’Ý@Á– 6‡ñ¤þ€ÅQÌx«(Ê[²5îÞÚþ~ú´ÿˆ7?ìû5[vìù[vÆQÕÕ-+„§Kjtü†Ä(ÔQ›Ë¾\LÚTÝÐÿÐÕõ‘%«6Šgþý‰ðëzƒ‚ò22û%ë7öoÿ'žüç‡5ö=Ü%oåñוù!óHŒÿ‹Ÿ–…ôcÇú#@cŠ&¬1¥9'õ,ŽbÒà/†½%–UBEéG«pï}ý³èÜ¡ÀÖh‘lp:JYøÚÔÖì'¬crýfƒ°j‚qݦªf7þ\Ž;¬—8òþëüzì!G÷ÃN ¶ì(ÿäýí/ˆQW‘ŠJUãriâɱ׉O< gUóÙ] àgM¬Q¶æ0ž4™*Œ·žBÈ63Æ¿pùzñåüeb`¿ÅÇó~-ÄÐã§w¨U©‹ÿØ >þîW±aËqP×ß«ó·Š[-]\|ú±â¿Ÿþ„³Àò‹ÆŸŠ¿ÝIxGƒvœÒ”1–m*NË•lQ[Þ¾{/æâåB!²¿òÁ7âÄ#—žqœøúçåÖD—+)ÓvO ]´7+.MtO9º/Tw¤x}ÎbמB¬âˆ“ö Éô‰ÀŸÛ Ä/˜4o±øÞê+‡Yt)?KWo¯~ð­tx/A_é$Cù*S'óéKÄJLÖ/zœè 9õuÛü„ üü¥kÅC·^ n¹hˆﯗžfMFbÝÿì<4¥gE¿¢1%ÐךRùšjYâmÅo7ž˜ã}éÐcÅáw}°BŸ‡Õ)Ýÿ +ݵ›¶c ê'†Ÿ8 ·Ë%:´MÎ …hB@f÷ž"k€y竟:B Nui|•Ñ Ã@šà4¿®Xo­`ºvl-èw@§¶bÁ²µâÜ“r‹ÔN“=gz6uc—µ©—³ÁÊw &Àg .ïß,üC,ÅDú¤£©1}ê?´òâåÀÔw‰.Z‹³O¬^šE“s’´[aRÖm×:ÙZ4`ÿ ä« Yì1¥ ©i%ƒ ^"PïîDÚ_/ó]%þõþ<Ñ~n’øË™ÇWÉÓÚMÛćß.÷\Ž5p|þãoØ2ø¡Ò´%T´wO÷]‹ð]š&hU4tÐaHoP•ô¢ã»-”èä©4BµkÊ· :´ú¦É-µsÒ“qšUXÁÿ¾v“èÞ¹½%Â?ö°ž–Ž‹3L°ôjŽÄTsY^I³ßú¡ é!tîö]{Þ;° `›ãæsZRC<"ÿV\؆ˆ}ÿ³sÁOF`ÿ"o{üû !’ßÞ¢kŸöðIù怎m)‘.­Bvì.²ÜÛ#Ìö]{ÊG[¡–_uh‹a$´WI«Ž…Pò#s,“¾_²RüŽsÓ´!¿à Bu4#poÊ«á¦\¶ª0Y»9¬7ú¶ÈÖaåO[xK÷,Á‘¿B(Ò‰þ=»ŠdÇ%aû©ÿÕ^ Æ‚û\#¨¯x[ñWˆŒöÏÊÔ0 ñþ× Å‹o}…ãK.ìÿwC+´ýÏÀJüÿ>üNÜûÄ+âf( †R)‘6qR‹q)‰^ý`^XU~õ9'Šçÿ÷¥øû«[áiôèþ=-íä¾÷˜ wZ9µÝ‘Wž):·oÝ0U`Øç3Z£ ¶_ÛTcBí»Å+¡·2eßÈ¿ìáXhßžNÇÐØÏ“¸žDédÞûêì×—ˆ«¡yOû¤x÷ÆœŸ,½šT›o1y …=òrÍ}êá’šƒÏyáK?€ú×o¸€ IJJu1õ•¬>¦AÒF:4—œqlCõ¿pŠÐx”ŸÀrŽ)'ïÍ8§Ta™oæD /+c(=#0$»£s=mn{ÐûÖ€¾½Ž{Óy«Tº¬DÃÕ´oï4´š)Æj! þ¶¡Õ?­FêcJ¡MúÁíÒ-dÉ-[ÐI£¨ºeáo,ý×ß'ß‚â·?R (ƯnK;Dh$&nÚT#Á+*٤սĿàþâu R\RV©oQ¿~hêlqÝù'Yú;4¹þrÁ2k¢2õ}-ý¯Ùn4AiLY¼lÕ¢—Ÿšx#2•ñ$Šü¤Ñà¸?2o+þýA•4=îаv2}ŠX_¦_7t:4`¶‚ÒF 1"@{åñ`‚'Ô¤øG:¤\»G i;bî8°K‡Ó§|sÿ‹‡Úã<ÄМ'–)2mF€`ö4I¿ý²ÓÅw‹Vâîþe¢EB‚µÏÎÉÕ+ÜÙä$˜#ÀŒ?æsŒ#/ Àq>ú±aš3•7±›3\vF€`F  ÀŒ¿T2‘`F€°`Æo#ÁOF€د²Ý˜‰/ãäLÙ~Í'Î4u˜ñ7õæò1:^GW±aØ"ÀÊ}±Å—©3Œ@tÎ4ëý±N ÆWþè#:ç:4ë]¹Aü¸d5®Âî&†s¨uÅ5]‰ÝÇî–á¶ËÂâRqýù'QåWF€^ñ‡‹‡c¨ ðÁÜEâÓï—à‹~}Å/¸Z—>˜SŒ«°m3éq>ÙûÍÂâ/XÎk6n³þû…(Ø[,ŽÁÕÖlF þðŠ¿þØqLF€¨Kp}îdžOS÷ôQ,bôNsÁ©Gãë•­obÌÁአOºÚ÷&\—͆`"C€Wü‘áDZF Ž úœ/™¶É-+ÝšGnt±™–‰ë–=ëzu«ü)kÛŸŒ#P7˜ñ× /Í0"@_§\ˆ«réNü¿¯µ¾èç$9éjëÓ¾ ðe>ÚçgÃ0ÑE€tñdjŒ#P ôÕ¾‚½Ebì“ÿ'þ¾®RhúF}øjü³oˆ]¸Oø‰|n%€ø…ˆ¼Ç™#À„@.íÅ}7ž'èky_¾²÷øéóÓOÜw­Eèb|&×…OæÚæ¶KO·­üdØ×³"$ÄÑF€¨ Õ}“h8™~]hrXF€¨füµcÄ!F Fƒ?ÈÀ€²_Œ’a²Œ#à@€Eý0ØÊ0 ‹‰ú/z\Ã&Ê©1Í^ñ7óÀÅgF€h^ðŠ?¨¾é¾púÙÆi·Ýêú¤UmÈî|·ÝùÙ´ˆv»r¶!nSñÛv¢]ïTR®ûø­ïÆ’³fÏøíŽiâã ¦©ÄºMÛÅRܾ£ Pì.,ÂÑ"]ˆ}ó€º×kÏOLpã²’$Ñ¡m²Ч.#é(4ìoò ]wHC »]­ÅU³KVå‹» Å.a+ÃQµ@s XêX¢&Ц2öÓ‰@cîv½óxâ¬Q¶ÇÍ–ñS¥ÎI—ˆ”âžð/,_þü‡Ø‹[ÅV]¶%Z aWTêÌ%ŠE s§p«ñî*o…ËÎt¨:èp‘˜èL¢’ÙoP»"æþÙËÄg?-{bÔž¨€©MÙýž…EebÙšbͦmÖýÅ¥e¢¤â®þö­“Dû¶­DÇ6Éâ \Û{`×öÖä˜&ÉñlìòñxϵÄy³h–Œßfø~¿ßZÍž3ß wzºŠÍIÄvÇWÁÞÿv‰(rµË['v»;żîhB±9á@ë×Vß&ú/yÿûJ\|Ú@qÖà#„ÛíâÕÌk!º ØíjÎKÄ›Ÿÿ, ­ö4¤AÚ•$Û” ­€·ìØ-žk®Ø´m—(r·›»<Ä­½Pý»VhÐÂ(ôÍ¢“ÿOñÑw¿Š/ü!nºàD1°_Oô}:3DŸ¶QŠ 5Hð¸ErKhƒ-5{‚@ÏX»Œ<žÄ a¦+š ã·;©ß¯cß¾ ¢Øß0 ,Û<=ÄÒ¤c…)£#Ò¯KEÑDc~«ÓÅáE?cõ¿ÐÚû=넃ÌÞÿ¯ =ÛðØíо"÷¿Ïì×öD¥ß_mŠp CÏrLîÛ×}çÙÙŸ‹…ï' ¶&t·ÂÕôGa½_ìJù®>"?±H6öˆÃ‹ˆçÞúZŒº|({ Üñ¿N,^¹Qü‰É„¶M3¹e ѧGG|ýï`qLÿž"ú5ÑÞ*(/£)x<±QçgcB Ù0~ÇQ'-)-‹–¯}¿Ì¤—$¿_ë‹&”‡# …xçëE¢s»VÖª†˜¿ËÕð“‘ý F#LœÚÕâ?Ö‹7¿ø9.ÚAØmjƒ7q§~±X¹~³µg¿ko±ÅðÉŸ¾À·c÷^(5ŠEɧaRR¿¯ë‘å—ä!bðžÏÄKï~ƒÕ½ßR¸-½‰ý ‹“,üZ‚p)Û¥¢µ¾KìZ½M,^‘/Z$&ˆóN>R Ãg€í u4š'Ñ@‘iì/šã§NJâ8Zéï.Ø+Þúz±(ÒZ[+ýý|pº$u´wx}ÎqH.–ò‰)£½R N—ß뵫’¿xõ£,ñ>Õa<™X´){¥K"ö`öŸý¸z2ËWúReZ’ÐtU$öë-BS:tf¨7Ó·ñÔ¡°Õ}€p•®ƒ ¯XŸxômïÊÏò¯úŠvØJëYº“²âë_–‹—n) F*Mãñ¤2ÜüÖøhòŒŸ*¤hO¿¸¸DÌ…æ~aq)öôß/âýêš­Ò–·euùa÷š°ÛÕg?ýf)ò-o5$®Úå>ÚmŠõ¥"ô#’pÌ[¼R®D±1¡¯Ø’ÐCjmªìÙ{Tt¢3̬H:R¬RG@0mŠ&¤gB“æ/ün‰õW·<<~*¢ž9¡ ´LÍ-¾_²Ú’ª…Ïøy<©'ä-Îh²ŒŸV+¶hŽ/ÒÂõû!ˆ’†q,ëÑná/+tiðµËË4™vxØuQ.IÒ£ö-‡ðR¯¨º¶©ò¾ƒÓ0Ø&+€ôiÉšÍÐÞïe)ëÕ?ñ“ôv¹:Š ›w…­Gã¬wOâ£9õG É2~‚ÄÍ•¯\°—W-ÞúCYÌò×°¶*Â]‘D–"Ç»]Q5&S—6Ee$G’²åk7[}©1‹øƒë© Ê~{ DK—zQ9Ãécv½óxŒ&¿76š,ã·gè46“f¼I+çFT;WXù¦üÛeiDÙo²YµëÂâùñסMÙÛ$)[·y»uT¯¾7ïÅccHÀ -q•/)êC§z­É8ëÇ“šb¿Æ€@“eü~ ³bÀ §sÇU…çj”â*ßÍ 3vÔÌ*âˆ0Û•¯|U[¾â/(ÄÊ7òý?{×àfq¬G]:]?wŸ}î½àJ3½CHÀ $!„„I(éï%y¼¼¼TBà‘ ¡ŒÁtãŽ{ï½×ëw’NíÍ·º•þ“uw:Õ_Ò®ýßÿë¯3ßÎîìÌÎî¦j<~¶‘1ñ ™<«_¿ gÈ£&ÊX÷TEò]Õ'Ý£¥îÐ3y­ø|¸°vÓ¢×[&A©HÚõF›¢'”7¹†C¼2¹CCý¡Eh8È4GÆëÇ“'CÜÛÉôÓèê*.c=ËKY&±Ï¥oÞçOŠÖÄÈ{ÅŸ84êI…@v(²¤o)Ùx8’Š_Xý~Vù¼ ®™U&x7¥‘ý~m¨Ú³›†õ)¡^eE<-¶A,á› Þzò ³ª™{—º·‡ä­x¥£Eþûk‹és‹Â÷vòD Ÿ,¡E·—…ÏÝ4ÎJsn.ż.)Méà'¥ÈËRR¦Š­úÑ…E´æ«´ê«åôôì:«ohŽûlÈøÄË¿ˆûÂ-~¯|—Zå_á0Ъ¯”Óø>fºv¤•Þ¾µ”V3ïËï(§Ç®q’l=ññbºg†#i èÙC£y ì»™&×ô"³Ù$ÖÃ0òbXü¿Ë”ê|ÇÇ®e£ùŸ/ã:~uÌÆ¯UÐr#õ*2 ™QßúßÔù¯tðÓù×Ô½!·Š@ËÊ+´Oú©ýÌtnµ…þ¶Î%^„Jù¥›Øj(êXj_ÙÖF¥6}|tûaI|VK¯Q¦™/ɾV+SE¬ø«Kt×¼&ºæéòò ”ožRvÙ’)ð‰Ô»ÔÆ Ù ¯vw"Y–;<×4­>ê£Í'|TïÒãk<4ëo tÇÜ&:•>3.´úÞVºéö)6BC!™¬á›Ü>Z·?°ˆ¥¯ã]­Oæ{hŸ %DÖö÷žc§?­r‘všŽ Üúä˜HÝqª5@/muÓí²ÌWµô'óõlî#še³r‡n9øê4;½²ÍCu®Pe8ª—‰aÙ£R’ÉÇ“ì=µÞC¸ÿÕímòt÷ÛOùèÏ6’‰½ F/tjቇŒÉ´ÓÎ&ª>{ì¨_þí“,a–4ýõ÷uÉqÃpÀ릀§…¦ò00£Õ’ðû:ÊTî|½9ü®·w·Ñ÷g…ªd /ç.ôß Z©‰%×dõ‘Á±$m\üèB ,æ™­Û—„ ôÊ.•ð4·ƒÝ;é”y€˜ö:Lh‚%Ü8¾e¢•îx-ÄóÒƒÞð›N·lõ³eRôhì> ÏM´ÑïV¸Ã÷õôàˆm(´ AîÝ<±‹›»šûÍ 7(L/U›è×W8…R–ï>Ô Ÿ|ØJ.žWÃ߯AŽ®ò4óïzñ4@Ä&”®e!C?WSG 1ð³K‹èïëYÑŸñp<±ÖCÜVF#«L´ótüS 'D˜z¨ HF“@’É1½Ì´íT¤Ð=¿ÙÊßMì-<#m9飡&²¦³³òŒ¯ª¹†@´Lié?{ …Ö‹È[&e J_*~³Ù,§<*ñ×ÓÏ6-™ «0 »åd„ÇêR]9ÜB?eåçGƒck¤á¼™Âc¹ &›°XVÜT|6Y‚.*ªß× oɾ¹gÏÃpØÎJܯùîÓm´¿ÁO DAxë±æ€ðˆŒ©JžÿžQ©îÎW”$Å‘³6³úh_}|sæïã [ep™‘vÕF*¶8>¥n)º’©q÷¥ÃÌô¹—#€TÉ”‰›úÿuq•–8Éá°‹ué£!‡K.pôÛ¹ÿ¾Y¥tò¤—VíæE®Z¶“%À+]²òLf‚!e&ªm Rs[Dó}‚»Çîšnýí/uÑiW¤¼à2uöÀÔUWµæ¾tÈ:œª]{è¿/"ê׫ˆ,näDuö£ûå/׋a.—™g1 ÒéZ^áó˜ƒÞ]o¤ýut4”þÊükë“ÑÜøì;Ýð\#UÆèÒØÏuÏàòäº::%F](8RW’òºþ¬ô‘ŽrË;žt¢#ð‰ú'®øG³uó£KK©ª²Œ+éâN+éxèÁ=?5ŸÖoÙ¹õéGu/ÿ<ÆÛiÞØùMZÈ×Ö ü1ð™–Þþ¾:nDÍôn½š&žéîr¹Y˜é‰×Ì´¥>±wu&SÓú›é/+¢ÿx·•6Ó¥B¦zB)ú½aíÛlV!{EPB[4°òßGý½h‡c2³îÉkÃ÷¢lD—§ß¯tÑ_V»iLoýùºbª)7Ñ-hÏàÞ~üL*ÓQÛá¿y÷!êU^Âc̆P*¿)ßþ×p‚û~v©“^â®DyÂó4ˆ÷'Z‚Ôê ¬¤˜ñõ§ HmIÊSQøú:ãkq#Úwžh‰¯¡ ^®þ±d VßYá=´ÌE¯ïŒ¸¹L&e*äæ7E(~9ìvr9¨ˆ·š 9X/‚²;ÒØ“ <Îe^´èäåH74xqŸÿyƒ"vI_.SÇÛËaô3‰þn5ˆ`:Å«,z9^3òÁÛ‘‰þû´×'ˆÜÇ;¦ØiÞgKňÐOÃ9Õ! €UªùÏŸêúD R²ôIŸ.¨B‹Jýö4ê.Õ°i_ƒRüÝaU¨×£ej0ËÌ\Ñ/?ä£MÇý4…G‘ í­÷‹þÝLËT´Åït:¨µÅMÛNŸ$—ß@;Š&ó½5 gÜÜPxN¶p[ØÝG²o`¾QÎ&óˆ™K‡X‹ˆüï«K½c j>È S÷Ú¬V¡ø£Ýý 3ÙŃ{™ÿá\Ÿ ç³gOJ{Ue¤§n,¡O¿ØD[9^ ]#Ïmò´ß¡v äPŠ?NüØ7–-²xÒn½£`{|™±â¡IÝ£?´2u5µU±"¼b8¶ÈHûç·Ð|ŽðÏ´LAñÃõ-­~»ÍNÕR£ËK›3)Ù{v×±²eë~<—•‡}4•»8¾Í‘ìÀ‘ôóØãa|2Mèm¦Å‡÷0È÷h÷6î3°…y Äüû93Ò!ò–Jž ì‚3±…‘;Õ‘B ~”Å?VÂ*éîö׸%M%…@<ÀÒí.eC¦`ùBáÃŽþïýÇè¸e ©³ ä¯}&ҷݼŠìïìz"çËxu¾2ßi=¸Wxæ>x82Ñ¿/é}¯—°u•0äñʧ0øF%…@êH])NM){“,È¡}Ê^›±iéÏØGÕ‡ºE@æK·7êðI;ö]%(~ ]ôú¼tº¡I·9 >’‘G$ŒpmäøØ‹! Å3 ®¡«¤Å®øºzMÖ®iéÏêú@ kI׉‰«bC¹6¥x¡‘Ĩëú),†bá~UmŠÅöº:Î Ú|€â0ehÕºd¹ë©L…¿—£ÊÛƒÞ,I ßK–þT>?µŠý4yP99ldåh~ÌYůÍ_í7cWõ‰!uœKtÔ.¹Dy´Ú¹¢¶ôßWíÖH¿g¬ªG2€ú‡í<‹].¤žÊTHñ‡ÜüÁ€ŸûÂ-Téã™ûr±=´ôpÖs€2èVI!׊?\H9nn¼ªb^J‹+´ªöaúíáʇ¢/õÕÒ@ÏšÔ²Œ¦7~@•þ“„)§.§ÊŠR*--¡¶ö±ö€ÝÆÝ5í}ÝÑ¡Íw”Ï\¨O0IÑè¾NšÐç ÀPLÎÓå‡ýtãó¼ìs÷Ów‡‰ºž›ä­F‘-ôÒ7‹È]ñTWØÉÌ}y#[7’1˜ú¹¿Ð2’‡Y9aTÿ²p¤1è—¼$únõ\êy!åjÒ°þbéÚQœwz’'pœŒLO(6”;/Òãt ¹W™“Æó„;ef? qo£³ߥAž]¼¶}|AޏÏÍl|&6/Š:ÙÜж—Fµ® +ú)Í‹Äp½ÞþÔ·ˆhj?; ç@¾òòR*+ã­´”JÚW¼Dæ;kôEç;ºr¥>™È£¼¬œn=«˜½84Ìy‹E¢n}¹)äúO6Ôó9‡@^Oàƒ`3+NPðÀrq°åRSÚJ»ëilëžw|†.2 ´8yˆÑ¸êA#èÝ ?ž #]0Q Dh媈üfŽèC ·ayZËò4]7($#S!ë64W¿Ãag¥_$&òñs„?"þG™š©¶ÙCÇZZÉêÚLÃ\[¨Ñ\Aõæ^Ôl*¥6ƒ|‹ð` d»Ü{š*üÇy~|¢"£\A3 oÝDçR° 7{W€bž¡î­¼b`ª,^~[â܇¾"‰‹–’âb*á~ý2Xû¬ôaí£>€å^ãIÚ|ÏÅúä+3‹é¼ÑvúñbÕs; küj±‹ÖñÓ¯.çx‡ÈñÀ¡îÉaô¦øYy±¬` Åíå%ñ¸mš ¶Ò5'¬®ì\¡ëSêàÁš‰ZÐxVÚZ4•g$ËÎð9Xe¨ {yÐ + ìU*èD…ºaYÆ[1u…•»Íôù|ˆÊkö]=–×R*S$Z®†ô¯ä¥][hËáÃ,OÄò4%kò$èK‘LOXÄv{@Âa&?(}X¿°’-f•9<ÔÌ ÷Ôz‚dcÅ^Ænv+±3‹J,~öÈbPCÏï=I“[–ˆÕþZ%±‹y ¸ó1<ÏÊJ¿¦ÌÀ‹í .Æ$]ÜxFy/*bo»öáÞÇ^ºøC‘üñ)}ï¹XŸÌª±ÑËŠé·[i/Œ„u¾0'@ºÎ)I'ãøƒ:…‚˜VQ[§Äñ¤º%ÛèIñ‡ë‹€×¢¾A,¾”âUP\på… -ÛC‡ªyF² ¢“ÝG8X©‰v:&Q[+™LèE—ú%qäˆ~¥Â² 7ä† ¹ú“¥«¾±%ØæñÔÇxO÷×rýT˜·Tɉ%W‡ô!nX?LÓšiGä ´¥R¦ èD`_—«u† „a.•«§­­.¶ª=ü]/{|¼š\€XP7°”Ô*,pÀ¤TFc¨ÊAÂîó“©ÕOFW-ÍhzŸì1°ô^ƒ6^ÀÏ[Û™\¯éÍc»yùÔcÍ!yb9:nI¯<žtÊ”Tþ¬.ߡߡ¾üñ8x—Û-ÓBñsããþ¡Ø‘€•p‘s¼ ð Á{ÐÖÖÆÞ„6ªpµ‰/mÞ:*õÔÑ`÷qOÌ?¬§lácñš^ÅV*fe >lų—Lö¿£Ü£q‚Iz¤k>Ô˜F¤ë(þXßî,ßs±>a1¥ïÏ*¢á•fzpA qvˆåoaåÿ¶ü§öëZ5È:¥ÍÕššÍX€«siC ëÜMÛg»|qðØÁýKJ«ªîݸë0=aX—7wwQVTèσ%G09&&AÂõ«›NðR»FVÄc.^®è&ÜÀMnf[A*ãZ¬ÄanG§Ó!,} 1*/+ G‡&é¹Uþ æ€qäYWý›V-ßͧÑâ‘UÔ«š[óñ0å2º“++— já¥d›Ý¼œ¬<^á ºä´÷)Á9S2¥UþP|PäOXÑP¸Â‹&”6¢òâI4„ÅÂ.HTVˆ–†{–¾põ‡­ýÔõ¿M»ìÚþó€ñ‹rÓâ({zNËcJe ŒÇ#W‡›ªØ²…ñ©«lÉøÅ·¥ë–´ÏÆ+ø9B]g!¥CA€ÀHÞÆžÇ†ò„8=({Ø8%Ü '{¹Ã3è" XóÜè@Ù5 +}¦G4.ÄwB³^JšAKªÞ ŸOõÉø>fú÷§Jè«s›iOG Ö/·²!¤{fÚ;@'ë”ÃûöÌå …TŸtÀ!—èIñGX£B>œóÒ ŸúÚ7>óìüåß¿ýz£•ƒƒ’I(¬P¸H¡Ê(±,úþØRpsÅ×£¶ŸR[ù$ómù=T^Òýˆ¨}Dí>}T€Øàâ 7ÙÔæõãø¼MKÞ˜û.¿a¼Øda õw$û!}?Ÿ6™ÛÙ’«lɾ+•¸ˆì·p4Àî>óÃRg…-˸_Ð* ¡†®¡ly>rxìä)jãFÝrŸ¿h µwÅÉwclš†âV{¿=[ÿÒ½/ø^ºS¶ò| <T×'KŒô¯ÙÅbf¿ÕGQMýn¥‹ø ÒÝÓâ·¬S‚~â¹s^ç“…VŸrýž¿´Î ”|õõ§š·¬^ñ[ÃŒs~òϹ‹ƒw̾(©Ò, ”ª¬ˆB–w¨¯J?ä²lèƒÔ*þvã£Gù-ëùmLŒÊKºI¡ü¥µ…ó¸ŽJÏ$›€Û‘“õ´zÑ‚gZ[›1”¯­}“…Ubžì§ôú¼ä/-2¦eÞfJ®¤XÈïfZ¦dFãûhðÿvEoûX^2Ü ºC{Cø> —1(m¸è½^÷Ïýû²«eO&É«TìÀZ4 Û-ü¡Æ|&Ý{IS¦òüd"ïË8âÿÉOÓ×yZßEB‹™=ö‘[Lß|×4;É:eݲE77óÖPR(õIºÅ*cïדâÓ(í"((ÏÒ·ç-/­¬xŠo£—)ø…ëg’±üQXQIÈB‹cÁꇥà"鮑ÉèeÔX/LG¾%6ŽP’*-ŒÑG¥e/ƒpJ?Ù„V9 読{ »6­Ÿ»néÂüNŒÛ‘ðΑڕäiJ«L³LËU6dª+Ùôà4âIF¶"C+—A¿…ü\DÐm»7@[îð~”‹P_Gƒl$ãžL'|3ë+¿üýµNº›•ÿâvåÿèr­Þ¼7h:¸×°wÛ¦W}øÞjÆ»ë“L‹YZ¾§GÅë Š ËH¸æÿûé—¯¼é³/½ùèéºàÍWcLEŸ¨2iŸ–”•¿Pøle ò‘€Òê.G¦¡ÇIVH¡ *4ZX(\‘¡ ŽÛ¾¼·ÇÑ<€þ7¸÷aéïÜ´îõ¯¾ô_XòV?Ž/p.ÅŸv™b,ÞÙk6r%å$S2ÞRÀ6ð`âáyAžÅ/äa“8kBIÞ Ý9=/ïÉæ^6>Òïàü#¥;ï-RùsÀß’ƒ!ËqS?C‰«ß¦Õ/ýè &uI!Ö'€?ç“Þ?J;*iH„ ®$ûÛ/ükÎÔ .>8ïÂÛóÔü² #ªƒ3Æ3L10áqþ(@Pº(@¡¾CT<¡¾GimH…/÷LK“,¨Ú ß”Á~ò|_¬ycj1¼‘¶äóû¼Í+¼ûâÆ–nâÛ$Žúw". «|6ú·<ßÓ=¦5Æ ‡˜œ‡é ÏÖ-‹—¿ýÆ·»µ•߇ 6µïq¬µø IñgM¦sÑe„½LÉÊU´ Eÿ–ßQûì"ÏÑ¿¡.:¯£'òNùL¬:űëíÅ'G~v„¹jÐHÜçsÁ­Õ_úýÖCOÞóÿ,´úDB•Ó{½)~€)ûdÑ„%Üüè94r€½ÿÊó¯óñ{#'ž5´fä豎⒠›ÃQj6[b8å›ó5ù|<]««©µ¹±~ÿŽí;vm^y•nm´Ä¡èÚ·FÞOàZ(ýû̪HJ¦$j¯èY§¸š›êöïܶ}ç¦õøö6Kñ|ßàžÿº©¤÷XnÅŠÆ^ð_Õw>¾îПïXÂ× ­>éÁܸ¤GÅ/]³°ÐàûƒÒ—Ö¼¬À½;7®ÛÎÛ¾p6yL^À%Œ £%KŠsôãÙ¿_Ö>³+’’)‰„Ú+ºF f}âm>Ýräï÷ÿ¸ú®'6Xlƒøv爙õý×Ïßñ‹íãß…TŸt`\Õ£âlR‰Á:•Ê\[yã<–ÂÊ{øÊI>ú•ŸeA“J_ÆFiíÃ⇥¥_¨Ö>³.’’)‰„Ú+ÎD ÛúÄu`CsÃ’§¿Rqñ—Ÿå›+xëG½j^ÿûͳ6ß3uJ9‚€^?àCE ' µ&åo(6X´Pü˜R Š|ˆîÞ‚Òg6EBa.Púp·/`ÅEÂ(ûú®ãþBMR†À¿Ä˜(™*T‰P|ke¢ÓúäļGOºü󖪚9<òÒÂnÿÉ>Wà_ƒ7˧ .I¼”L\Ö+†»@@Ö ²|tYŸìùùõïþõ†;ù}Oâ°xý³mú þ¿UÒ?zVü@‚!Ô ¤´Ð`é[y“J_Æ@áç³ÒgöD&rFÚ Œ ì±Ç†kª5Î pR2ÂAýUhu‰,]Ö'Û¿3éo£~³q$¯|ö¼„úÁ¨‡6Ïßñíñ‹µ/UÇúD@ïŠ_¸w@IDAT¢¥Ä–+”šVáK¥_¨}üÚÂ* ,öRáãºJP2Õõ«°u„,²þè´>ÙþÀ„ÿó›Mç°Å Wϼ ™ÿŸ£ŸØ6yû—Ç ¦H%#+ŠJå&•?,Y(zlÒÊ—–¾Üó¥¼M² b/7`#7y.oHcÀH6ŽdŒD!ËT U¯ÈQPd½½¬K°—çqH<@pÂo6ÞÆVؾZÎ}þC uÞGùâ—ÚoQ;"KŠ_B(ˆ$•~èWa¸ù%¯ÀB&‰‹ü­öñ# ±S2?fêÎüC ÇõɦoM<8æ¡Mwó"æÿlýß>ê¡ ¯íøö¤9ùOþp”‹Š?}YiGŸW¿‰" d*QäÔs‡À¶oOx–ƒý>Æ…æ³`žWQøë„G6.ÛtßÄãFŽ0 ·¦J …€B@! H‡¹äVøð¶ú{µùè‘„_¦L;Jñ§bõ…€B@!߬»h}ÐhÔöífÔC›fä7×¹ËRü¹›wŠr…€B@! v|kü{põª ˆçó7PðWº!NÒ¥ø;À¡~( …@ÂX¬ßcå2pù_<öׯKø]êÁ´! Ú U/V(……ÀöûÆl'ƒá/’k¿!øË›žbž•t„€Rü:Ê EŠB@! ÈuЬö9Ð/4‰OÆoؿ鋹ÎS¾Ñ¯¾å¨âG! Pdµßy’;ø!Ièûìö/„IÕ$˺ß+ůû,R* ÜBÀìý(»ü±$8:ûGŒyxóÅâXýÑJñë" …€B Øð~-† =#9 _‘ÇjŸ}”âÏ~(  …@Þ!`6ÃA~<Ÿßì1¿ÛZ•wLæ(CJñçhÆ)² …€žØ|ÿøuìî_!h mAÿV=Ó[H´)Å_H¹­xU(D€Ýýa«Ÿü”»?ƒØwõ)¥ø»BG]S( „°Qïóò½ÍâÁัn™ðËÔƒ)C@)þ”A©^¤P(Zä ÒBy.àó_ Õ>{(ÅŸ=ìÕ— …@Þ#Àsö‡?»û•â×AŽ+ůƒLP$( |EÀ`ŽXüÌã…ùÊg.ñ¥.å–¢U! Pä΢‰«xÚ¾VAv†~lÛ€c!ïÈUŠ?ï²T1¤P(ôƒÀê; ^2Ð2IQ°Í«ÜýŒ,í•âÏðê³ …€B P’á£0¯Aš>VYA@)þ¬À®>ªP( V4Ç$·ìö¯‘ÇjŸ”âÏîê« …€B `0 '5Ì*ů#‡JñguõM…€B@!P@øÅ ö. ÖuɪY—TõŒ(¬ó¬]ëY{ܳ7©»s  †`kk.©Ã8Pe)N òð6mÙIyY2˜'ƒjürgâ—S,å¢â•Ó—¿ûàtƒÑp«ü2úóœÐ%9…¼"6il¦@ð(«ûý^ç>òóÕüRT/ØR^y%M°þ^ =<Æ/Ü÷ƒi›í Y¦Œd(ÖÉŠ¢t"4P“,SÁ@ð­'~õà*þ^ÒeÉ4ô“OÎSø¥“õîîÈ%ÅoøêWtø+ ß0[L÷Á~,@Á’"{ ¼´È`·XT·E÷ùWw¸½Þ@}ck°©Õm4šMÿ}ç~zÌírÿnÿªM¿_°`Æ ûy“€¼â=If ðQ^L_|CQÍô ÷Øö¯«2•$ªyð¸¶Lñ,{?A™òyý˜jƒýå/º˜E4zœü~“—Ú‚ª®î1‚©} W¿ñKßûñUF«åñ` 0`ܰÁã‡ÑÄ Ev›)µ¨·å¢iu{hã®Ã´róž¾›vúéèYÓïé?aøÝÏþî7o3/\á„9ÄZÚH Ÿßn¹åëߺ²´¼â`°¿*SiÃ;×^«LýÂØ×øM®ƒïxò—?y‹jwÚÇÏšÕÔÖ»-dð{gC“ùÄÿ¸º3Åè]ñ ËäŽÿøïû ã¯ú÷. ~æÊ³iäà~8¯’B@ À?:{Â0l†ŽÑ¿ßú¨+³—n}àû?~êá_üßÔÆªB¶þ¥•2oel¾iw82 O¹*S ˆJˆQ¦ú>Q÷:×Åß}ügÿõßÝ£²Ä?ô…4Ð`PŠ¿#Üÿ¥g—‹¨¨¾üƒïg¥ÿÐô±C ß½íc&VúI}0w€||ï‹3A^X±ýôs÷~ç~¦ÞΛ…7È{!6¥Òv`lT™b4Têm™B]Œ:™êQY ÚHþÚn?ªnH+zVüÆÛ¾ýW¦_r¼cöE«EyõÓ* yòrÈ ärST\ú_7Ü~çµÌšVùç §q³r.”>°&ªLź‘Ж)Ôɨ›ùtÜúƒãŠŸWë; @Í.qg\†É4^sÍçœv{Ñ_«ûV¿pý¬B´Ò2 yþ}r3 w9õPýÛÁƒGU2‡6Þàê֫ܧ#À+x¶`¡ÊT:`.Œw¢L ìSDÝŒ:š¹Ž¯,"ŠŸ§ïÝ_hé—Ëø2-³ôCÉ›úŒú qŸ¾IYú™Í€|ùäæ–«Ï1rrßs®½öNæËÁ›•7¸Ž ¡1)ÊR;Ï`,T™bDTJ”©›¯:Û„ºu4¿$®² ¦D>hØ9VGÙ@@Š_X(6»ýFTUŸ~6Ä"¾ ù•UU~‰¹‚…RHV¿(Kí<;*Sù#ÛÙâD–)ÔÑLC·4vóóÈkÒ¬ÈXœ-ÚÕwCèMñ 妯|c& K_²WV™’Å4#’#C¯ ®þø þúú Áê×Zûöï†^ªL¥YØ äõ#ÔѨ«™å.­þ ¿Ù2–ïíÕÍÉßž¤,þ,ˉ.QIɘœ‡Çégõù|@ryê=°úbæî~ºuYYñõ\ORñƒWxWe*׳T?ôË2…ºš©ê²,ù(pa„rƒ²ö#`díHoŠô˜Èh¬ÁŒ|Kª’B Y G%[Ðbµ¡%©µøõ&ÿɲª}>T–BÞ ;xªLi!Rlj" Ê×Ѩ«ùPü–%ž©/¬øyšõ…‰~S=—::ͬÔ}¢Go=fŽþ< ¯Þhë#êf}!P^æ4˜,æ>LZ“põwÛ7©/zL(Kí¼ÚÀ;0èñ[Ô N@ºš/wZ– ¼$ï%ò†``‘qðÄuÉ~W=Ÿ<¨ô”Ð1ñ2i¯Œ¡èß\º>X¹•š[Ýd2iÄ ¾ô¹kÎ¥>•¥ôúÂu´`õVúõ}7£8ŒÑO•ªûTÒm×Ï¢gÞXFëwäç]4 w]7ë,š:ž¯Pz}Ñ:ñù[îí6 =òíÏÉŸݯܼ—‚¼>æôqC;|÷ÀÑÓô³'çŠsF£JŠ4nغõºóÙ›á_ûß ÇþýŽÀbFÔûpÿ–=Gé©£µeñ˜× )BTTØôÔðM5.¡²â“˹à=®o ß^xw%;UϲBÔ«¼„n¼tM;„Žò¹ÿþóº÷³WÒØ¡Âï{cÉzgÙ&zèþ›é£M» J‡§x¥ò'5z0†€…ïÅ”·ÿøòõ4¸_U‡k±~œ¨m"4Dx y¬Ëi;çóûiëÞ£¼.HµøFw2Ÿ(!¯¼¿šÞZ¶Q<Ž!s•¥ÅtÅ9ãéü³FuúÊÍ{ŽÐs>äºä³d1ŸY•¿óÑ&º`ÊhQ¯uú’d/„êêNËRÀüª\Ö‡Ãlþþ§ ¹c%%‹ŽŸ?SZ²G,*e±aøGºÉ€RFAûÔeÓiÒ¨ÁÔÐì¢çß^A=õ&ýôîOŠJ÷ì>—>Ýy¢Býכ˨’i:ÂVÓ&¶À†ìM×]0™ŸïMëw¤Wo£I#«ém¶²ìLÓÅÓÇ„-kð‡FÌÚíûÉÌ÷¥Ìã•çLßéî¼°Àð÷Vl¦½‡NÅo.]¿ƒ-ˆ1\!}$¾ý©Ëgëï¦+f «®¹ÕCm!íâgjú÷¢>%áϵy}ôÚ‡kDƒ©•»Š˜fv¥Ðw¿p WlÖ„é  þXÂRΰÏפå1nÏÆü¥ 苟Š;ôا.ŸÉ.ã“ôæ’BñÃò_ÄŠ‰'GØnh¦ÇNÓõN·Ç+}*KØ{‘®€îJÞßX¼žVr÷”Íj¡eëw±÷àšÈ²¿lÃ.zcñ᱃w Þ:(É®dm¿«ºo%®o<Üó™ËÈb2‰ÆÊAnôôeù?oò.Oc¹ñ{‚@<‚ÿù»™÷‚gx<¤Ì£¬½ôî*ZÃe Þ‘ã†Ðl6&ÌüÎîÊi4f£)ܰ±[Í‚çC'ëhÏE:¢éFy{žËâO¾6[x,Ññ¯7—2½:òHbW<ÓÒÃÕÒ|öpna/êfn´âr~×§.aÏN=ýÆRÑHCù¿åêsi×7=HRÖ:<ÂJÿËܤFÃnÁæÆïêpƒú‘5â®2HaÚ+ãS\èQ9ŒЏ”H‚K³7++([$žÏœ•ç>qŒ?kÛ­´ú'±u·ç¯|È•HSøžxÜÜÿyº!òÊ'*P/ÐØâ¦9¬æÊÎL_àÆ@mc ¡ÒBjóz…}ùÆ=„J¹¦•Pƨp‘®Ù)–©ýâõŠÊøå÷V¼ñ$Tˆ€µáæ>Ä™‡‹ÇðÍ­{ˆŠewgÀ"DÍÆ …݇OÒ O%Ð'Þ¹zë~úÛ>v¾xîKŸ¸@4Œ’¡W¾¿{YI¥]Îz@Sºní1¯pÏ©éVú’0”•C'Bk«@ñ74·ÒÎk$x¿dƒ¸¦oáÞÿóËÐæÝ‡äãÝî»’÷I#‘“:¬_4H†ìE M­ô¹‹iÖ”‘t{Ý@÷BnŒ u%køÎÜ…kióžC¢!ݯªŒJš2f}‡¡“¹[â…wVâUÜP1¨7„œâ»—°1€¤•y+¸û ë«Î@Ë6rƒþõâ¾îÊ©¸Ióåh7 –¬ÛA/¿·Z4ž‡D<*Q,ºñ~ÐÂ}èâž§ç-%+ûÏrè×IÇN7ˆóøó*{- ðþ›„á‚s_™}vôÔ¼%ä°[¸ûæ*È]–ÏÎ_&Î÷àÏeéÁPPß—å;Øðù«:²qW|¼7¶¸¸\œéZF¹@Ãn^ÿÞ0¸÷‘ ¬àæGƒá0?¸ý:ªà¾ýÇþý.=ú¯·…rЇãÎä–.,èJVĘ5åvã®CÜî$¸»+ÙCÁ3І9¾Ó•¬áºÛãc%-]6sœhPÀÃqÙ̱¢Ñ?iÄ ò¢¬!>¡‚¿>w|]ÑiÙ†ÝÜJ—Ÿ=^lh-eO„6uVNµ÷àž…gç$¼ð6Ìš2ªC·`4ÝÚçëØ0@ãÞÐÏ¡6mßê²D|ÑH®ZÝm¢q×âòˆܬ³Fs\Ι4‚àHĘÑ~ïß¿Ùün Æ9Ž‘ªjµ½¬½®Ž³‹€ž\ý\ed&¡@#5që?:ÁM…„€=XëÙÅ_Æ€´jä3núÉ]7²»q=Ç l¢b§=n—º|‡ÜCqG§Áý°®L(¡2F¥+]w8 K©Øª¨=l ÒÚÁkÒ£âXµeŸ¸Ž?ÇOÇgñó–+…«†ßüóMÑúÿÊì‹ÃïA+!Xr?¢"º PA#å.¸!ÑE!ƒ¾€_²ôÊo%±Ï˜Ü%AcOM˜§rîšjlñœñ=°:ìÖp¼amÚ#Ðv:ÎÖîÄð3èÞB£–7¬gÄ|ÿö…¯wvЙ¼kƒkå³ð>!.ççOΓ§B8ü«3Y“7ŠG·LwânŒõt¼¶1Ü5áó‡¼XòžX{Ä3@qj»ùq#e1{pM¦XåT^ÓîÑÝøÓ{>)Ê8º þôâûääÆ¿ ަ[û¬ô¾ æï#¡UÅL™Æ ëO¬Ú*•1Ò iû>Aá=‘÷âÙC'ê;ë$Q¬ªÊÜ/n®HX*2à‚Vóåg‡úýq­çõ;°Û¯$lÕÈû±Grã¥Ó¹ôíæBÝžÚ[b#à Îçó‹~xéFÕÞ 7W)VPúea]yvß¼kïE\ ¬q(ÊèÔ™¬Éû`ÁË„.6#¢+ã;·]+Œï>úœ¼Üår oÁqKuê\“)V9•×bíÑÐÁ(й÷ð‰°â×Òý\öÀ ¡s0w‹oéÙÄù«Ï›D䆼!Wp·ÄDöl Ùm¡:æ>n¨ n7$Ä…¸ÿˆºZÊšxj¯×ý ÆuHû+N–[¿ŽûuêÆŒ )Ùùœ>>‚B á_Ì}io/ßD*+ù/Ü/ ocdB>,TD÷K7?®¡õ «–1*E(nYøä³]íGîÏŠŸhñú¢?hªÒî§ühónnÍ5è·ý‚ûž–9†*æ ½xÓ˜!] ª…n‡­š1Æ›£î7E0%º`ÉÈ” ½òjŸ  šØÝÿWŽ]AÀÞ.C`¢ý¯å7 C^¥›× s˹Ÿ .ã ;ö³ ½´C#B¾£'{xÞÐå†îtá›èÇÆ°AÌ%'Ǭw%kÑß„eßÈ]{ðt îçíå;Ü‚ïÂí W:¬ûè„Q;ú…¼C©âxÒHáᎾµÛß¾€_`†º´èZÜ ÜÝÃÈö8"huÒ»Œ <„2½ÏÖGT÷ÃûPOÉü0Ž!@×ÉìñŸø&ê³DÓø‡7WòÊ}ÿ)Ÿgó“¾92>w£|HíÓŽ@×&eÚ?Ÿ½\{þ$î?3Ò;Ë7‹þy4Ð÷u×§.¸¤ nÐßç®Ñâ?ç|Dãrù=¤‡—À†€SmƒØë ˆèqXÁð†Á’F¿s²é"–­gç/§ûz†¾ÀÁ¡çqãüDúÒ½À;¬dØ!ö¦+Y‹¦ã߯d9DWF\4mT‡ø400Êç½ F4|GhÓÍWžMO¾ºPÌgág÷>âWnº<1~Oô¿O¼&¼I„Q±æÇÐ~_{ <ž{kØÃ±CD\‚¼ŽòŒº]è^ƒ²GD?ðºó“s}±”þó÷/‰Ûßø»f3¦òéø÷þ€ÿ‡k †0v9K&ü9þ§Õ™B ¬íHÚWø? pæ/?ýÑÅØ'‘àsðVzûw~øê¸5Ó¸õê$^ÿ£.t{.V_bwoÁ¬uèûO4¡/Cý´nÕDßýÜ} Y‰âè÷Åûx¢±!,ï>ò<Ýr *²aBñÈcÃQ=òíχiK'½?5ŸÖoÙ¹õéGu/ÓuŒ·Ó¼ÁA¤g¤C–äA —%æ ý>ïwa8ƒ¡ŸÿJ÷¬’B •(Y;ÍÉ<3é1Õ3o÷ãóÜ!èRÄ°Ü x¸`ªR¯/=ö-Vúçã}Ü@óMæ{RõnõžÔ# ê1UoŒf8”³Ƹ¬N)R†€’µ3¡ÄäBØÒ‘ªïüÓ$ƒÕþtÅ q×åƒ[ï·&ôKýÕ#կnjP4) \CÀg°‹†ýÂ@;Ç@,8xÂ/sB£W)~ä8¦}‘WCSI! HîwÏÏÿÉm=~雼fÁ²«R×ì)™ZMFÓÜÅJ¿Ùj ÞªVàë3=\UŠ_¹€À,•›HÂÜGOÖ'ò¨zF!× C×IˆAÁ÷îR!—¿=¾J:i¯ ƒ îßxÿ¤=Ýa¦®gÕÇŸý<`øÓ¿yœ2†Ô\Æó~Ë5Ê1yÊ%¼R&ÂÊ~Ï,¦Þ\¿ý [4^q?vþþÚ"žyp¯äU!æÀD X¬3€æ†;íâɈFò8a,Ú]4³ü–Ú+rM¼XÐJ^Hs œÏsÒcÄÖ¢À£J¶ñ$\˜˜ ÛhÓžÄ ‚˜Í÷b–Àm<½m=GÆO7„·¡¨zfùÓ¾#Ÿ7÷Ñ×ð‹ÿë;¾7åo‘êHÏ(‹_'¹ƒyõ«ynþ>-¼6w¢ýŸÚwªc…€ÞÀ¼H¢\ñ$R2Õô­d‡ßX™oyûrº}yÊÚèÔ¯*rÎÊ hÄåZây¸è·ZhÇéÐD—&ž›wtãò]¯<ûãý…†E®ó«¿Nrp/ÌÑÀý‡ÍÜßx„ûâDZK1V’ ®aÜ,fÛ ¹ö+Äí}ÙŠÙsè$aÁœµÛö‰sòv–FÌ»ý‹¿…Ö%Ç’ÂXTä[?Ë|—Víì=ê¼B _À”Õpñÿàw/r`^‹|M¯±Ê_ªÞ­·÷<¼¬•žÙñBÞ9ÍN#-§ôF¦¢'N”Å'Pé¼íöO\~=ÜïÚ…M~ñO‡¯Å:ø8¯Èu5¯ hâ~{$ùaâ#ˆØ—ÑÊçsü6™n½îtÿÍ‚·ë9FF[®´e.óÚëÚcÜû?wÏ?]þÂòèà·Ëx™æ5‘.’kFXé¾sôðΫ÷ûY¬‰Xú—³Ò¯®pêlE_7(W7©Ë …€B x”•þŸWG”þeC-ôÛ«œdVZ#çÅAeaÎg¡b@! P¤Ç>rÓŸ4JÿRVú\]¬”~jaÎÚÛ”«?kЫ+ ý!ðË%­ô÷u‘>ý‹‡@é+K_9•8EJñ'ŽzR! Pä mþ }÷ÝzkWdÈÞ…5ú¿kœdiÎf œ¥ø \û …€B Á {xÞÕG}a0®a¡__®”~<:PŠ?*3÷óâ›yZÜÚ†ªonc€£néñO ´³ò‚åÅETUæ¤ñëÅâ=~Qš8ÀS–nÞs˜Nóô¥XŒ¤Ç?#ñ7[ ãýÁû»+6‹…G0)‰Ùh"ƒÉ@F Í Úxáa ²+ÑÇ ÿÒb]6s\ÖùOoê(d™ÒÖ'ï­ÜFMX0‹‹‘Ï`'·ÑN~2u+Á_&®—ìSdºEy*a£âò™c{\ž6ðÑ]óZètûÜû(ñߟUD_˜lK2õX. =“etP@1ß6¬û½¹„­{õ5õ¼ßNVCdK*Im Úé¸o4rO¦WÞ÷ÐV¸Ÿ»î|á0Äš4•z×f^®ô©yK…uµªÊ‹Éa·’1tw—»—BmcþWÓ+¶Òç¯;7+üGÁ¡~¦B•)m}òô›Ë¹ÝJu–¾t¼h,æ½Ï`Iºg¾ÂôR•÷8õõåé}.O·ÆYžÞÞí¥ï¿×L®ö.}+7öâ1úW O­gR¯Îd ‚TüPø~€Þc¥+¬OÓ šU4—z™÷§=РdY/¶S¾Ú躞~ÿ¼›f_:]XÀ˜ÿ; «”½ôÞ*±6y_^€kg"¡QátØÄ†©Të[™ÿ÷™ÿiå?¼Ú7 U¦:Ö'ë¨ÅTB;ŠÏ§s¯´‹Ç­Õb+ó¢Q®Ý–'žÉ›~ÃSðj#÷ËíúÃuÅ4¥_Aª„´ç“Þ>PP¹,[åpEBé¿¶p= 4o¦)Ž—ÉlˆD²f*“ÐиÀôgZëšÍJ˜( °»n<¯ ÇNö4YÝÀ¼¿øî**²Û¨’­ül좱чª­oL🩼-¤ïªLŪONYÒÖ¢)0¤Æ¥ß9BCcuñ…4¶um§åéXs€îŸßBëŽG‚ø—™èÏ+¦!å™1:z“º7=LNËBŠþü ;ÐÜE!¥?£è¹¬(}™hp€4@æ|°†6î<(º @oªÞ WìKï­J¿WEö”¾ä ÐFȜ֦•ùMµO…*SgÖ'J³szV”¾ÌQ48@h‰.O‹xiöó”>¦à}éÓ%JéK d_0Šî8(ýf^£þÅwVP©ñ¸°ôõ’Ïð:”Oпç/#÷ƒÞT'Dé?Í}ú˜[–¾žè±ð¢BϾõQÚø×¿ùBK¡Ê”¶>yþÕ½K_/ ´´Kè_ó?¢V®Oþoy+Gî7S+dPpw>}w–ƒã1úÅV„ô©TH„âG!…{ßÓÖF Vm¥†Ö6šh{=«–~´ÁòŸh›Kõ<ªàýU[½©Tþx¢÷ë›]T^R”5÷~4ßò7,ÿŠÒ"j`úÒÁ¿üŽÚ§B•©èú¤©ÕE;ì³jéGç*,ÿމÔȆÎ7ŸZK\ÑLAîJDêSl¤§f—Ðí“íѩ߂@Þ+~¸äÈׯJßårÓòM»Eô~&ùz*C  # ­Ý&èÝ ?Ù$1xŸ=ˆÞÏT _Oé] oáÚ)å¿§t¨û»G PeJò-ë“¥›ö‰èýLòuŸ+ïMµæ¾¬ÝÏCi½äÆÿyÕfšóéRÄת‚û•÷Š?Ô:÷ kÏÁãÔìöQ5GÕë5¶¦V/í=|’­~_J\þÀ`¿¯©ÅÍÑôV½².è}Í­ž”ò¯k†s”¸B•©èú¤Õí¡ã–jÝæâ Žø·ó˜ÿoÝ9ÕÌA|ETáP®}ÝfX†ËkÅn{½äæº}ÿ119Æéë56L ´uïjcº“µú%›÷â÷’§¯WÞAæ©â_ϼæ*m…*S’o”KYŸ`rŒÓ×k´1ŸRO_šd¦@мˆzåWÑ øýÔçnþ:ž†ÖF­i›œ'>È»¾ ãüm†V1m.èöûýI¹ûC••ŸNó9ÏÈ—ÎÉyºæ,¾« tbÚàTðßWÕ]=A PeJò­­O0#_º&çéIžtv/hö`«*OT€çóZñÃ-ʼnÈc·ÇÃ.t7Ù ºÏf;OÜÀA9 ôƒD“Äë³5`¿‡ÄƒÎTñßÃO«Ûã@ PeJò­­OÜýOmë6ÚTyŠC® é–Æ¼$Ÿ¹Ôè3©à?&(êdRªLiù.Ôú$)ÁQë ¼Uü@9\XY µŽß9“˜T(?ÉC¢tËçsˆó«)â?QÜÔs#P¨2á»pë“Î¥B]É%òZñ##Â…5MJßhJOT/µ¤=YÂ{r-¥’ÿ\ã=èM·LaÇT§TÈ”,“éæ_¼§š&õ¾ì! ÿÔìasÆ—§^÷5×í¢KNÅ•#iÜE?¥òþS©ÍUK‡·¾@;—?DƒÆßBƒ'ÝNKž½ŠŸÏ=…{Óš7]>ƒN5´ð8{Í?Ls%tøê‚µ¼‘¦FÏYpÆuuB! E“5ÝsÓeôô›ËèØ©z*rØèÚó&ѽXŽÌ4éZ³m?Ýrõ9tèX--Z·CûxÎ_?ÊF÷Ì´ÓµÏ4ÐÈJ͹¹´O_£™Ö÷ÓÛŸ/£O¿ØH»j9NI%…@ È[ÅŸêyEÿéT5hmzÿ;ö3àîƒ6zÿñ)TÞw M¿á:ºs.âÀð™÷Ò€17Ò‘m/§ ‹B¯H„ŸDžéŒàê>•\!÷¦y‹×“Õl¦m{†oØ»œ.;{<9U'Ù9ÿ¬Q4qD5mÜu(|O²©ä%YZ ùùTæÃ¬É£è +t(}á¼íºó…ü¼¾xOÙ|JtÏëżvÃg®<›VoÛG­îÔ Çí /=¹7ù°pãùÞsìô».ÂhÝr;O¯ÇéöW›ˆGÞ‰´»>@ î½´ÕMœã »¹!ª”j~RE—zOfÈ[Åø Ü‘-9@‡Mÿº°êaÝ#Y•ÔT»|mÍbëÞl)æ…0|´Ý“4|ú7’VüÚ÷Èw$Ç=Ñy“GÒ†EÅÛÊó `B ™f°õ¿ûÐ ªkl§VnÞ+îOFñŸ¨m ¿¾¼€ŠœN²Ù팭…' J®gjôÈ!cÿçwx[Ò]h{7Ïù³'ç&Å6be|üÛ-V¹´Zëê‚LÓÆÖгó— zÆ(–e~Ž—ÌFÔ¼6<^K§êšiÚ¸¡´hMrÓmËò€}O“öÙïð¹ëFYÈf"š»=Ô)çùóyZ~¨]ëkî~b­‡>¸­ŒFV™hçéŽØhnëöPK·7«òäjÒ¼†¦#s%½ÆQÓ©Íá“»W=FÕc?Mc/úšyãóttÇkTl¸Þpr#9+†QºúÿÃDdð oe)?ÝxÆK3¤­Þº7|íØézª*+&“‰k6•1èU^,rÇjC25¨_:QGÓÇ!t)M9¨ƒüe™êÇ2˜/iT/mg%îooTØlùé§—±'ÀAÃ*"eçXs€êÝAS•×vZ¾dmNð¡Ùd4ó\×Åý¨¥>¢Üꎮ¢†hð„Ï‘£´šnz†ß*Å­â>•ÕÄñvýßbf^â´Smã™®Æiㆈé@w<f¤¶Ýò¯(- ŸS -•¥NjuyxFI¯8]Zd§¡Ü·ß·ªŒNÔ5Ñe3ÇÓEÓF‡©kj¡ÊÒâðï\?Zf¢}ìÊ— –ü2¶öáI¸r˜…æ}¶”.j‘—i?ß;¸<Ô>© " šqç(î/îr5 ß=õcO />øÛ 0úFšqÿhÅ+·PÝ‘ÈÝ|œï ’½d5׿~@R)+}¤F^ÖW›ÌfMUCK7ìÒžËÿâD{Nq%žHêSÉM—N¦ªÊ *-)&‡ÃÞÁìé;~j>­ß²sëÓþê^~öo§yƒ¹éâ-qÿ)?¬ÃsÑÁLä*Þú}þÞï>:yÜȱÜz5ÿL<Á ïr¹©±©™žxm n|=OðiåÉÀ&ÈÑÓ ôÚ‡!¯ÆÊ#Näý[ÄËyí <“/©±‘Ö¸õWóñ꣑†õ 7•Ðì±6zo¨at”­þüŒJ T  $)Ý-Pä$¬~ìͶRê5è‚»¿­õ4í[û8Õ_GýF\‹Ëd+îà äi~ÉýÔÄ=R [eÚ4‰+f³ÙHëvО¦âöûšZS ^¦~ä%X0K+Oè÷s‡LŽ#€§I&Ü›Oòt¼%@}œ[ð»ë4¤,R=÷-6Ðñ–GQb¢ö DˆHV¢o(€çüÞVò°ï,&¸õy©éô6ê;ìj2YЍ¤jŒØj/×eCž¥~ŸØçú¯×OÍ\QW•9;°‚ ¾Í{Žðʇ#­áÆEªmˆvxPý(xjyÙeg‘M Û»§~½ÊhÊèê]QÊÖþ Úº/âaƒ›ÿ4%Í—´—]÷CÊ# ›+‡[hb_3Upß||9»û— Yûàyw ì¯Ï7ÇT¾äfîñ¡\ýqæYã©-TÚ{Bøîí‹ÿ—‡í}“.ûÊòr¤?†îß /ë=‘ZêöPÀ×Ñ5~8؇þW™†W÷|¯.¹fåyìûó}¨Ø±* J X`>Œ€¬ì?Æ^³#§è½6ÓESG‹ÆÀÖ}G £Cdê_UN{ŽDâHäù\Ýo;å£kG8ÈÄF?üjX±ÿÇ,;Á²ßÍãõŸÞà¡¿® ÕýØÅÁÖSª<åj~ën¥øãÌ‘]+K3oxŽv­z”ÚZNÒ©ŠÍdqßqiMVª™òÚ¾äã|snܶhývúÜÕçÑâõ;¨¥Õ#†ïÅfâ!w3' §÷W†úfsƒ;Ee¦€—hÅ–½tîäBñãûmÞ#6¸øµÆŽø¯*wÒsïF™¦7Õß{}G}mºnä~ü·„”<½Ój –¶Ž.ý¯LµÓ»{ÚhGCùRM¿z_n# \ýqæ_ñutdÇ4öæOh•>.ôqè;ºãÕ÷åú#'êÙ­ˆÎ9¸KVÆíO'ë›øÞÃ]Þ§.*–¬ÛIåÅEÔ'j˜žV饩c‡ÐµÛÉ•ÂÉ{²¾ú¹ÄÅ|VŽФh¥_Yd  jÌôë¥ùã=Œp«Ž²…€²ø{€üæ÷¿×íÝG¶¿BØò1½±xC·lmÚ}˜°©¤è åûÓKtw½òÁênïÉű/£ö;£¿¶5HW>uæüݯÎ+âA ¯-~LÙˆ' !òQêÿˆèžÜã ÚÈb2jh×Ú=y-hÇÀÂCï0AH.$Й*þsß\£±PeJòÚ‡êS02$O¯ùhfUyÒkîd‡®¼Uü(œÑÉÆÊÔˆ¨E_×Ëow°”ìÖHÄ/èŠÅOwôjŸqò‚( t¦‚ÿ\à5×h,T™Òò-ó õ‰- 1ñòº}½‡t+  ñ=ª<é6‹²BXÞ*þp!å®~÷Åyl0ö}¸•~Ü7‚Nùj²xWM mp¥3L/B$/]=ëš|ïïc«+Å,{ra”XÏdóèÂøîšJGJøÏ&/ùúíB•©h¾e}ÒÏÉ“ë°Å_æ;¥»,M mH¥]•'ÝåNv Ê[ÅXÑ*ÇҰذ ŒÙl¦%²²Õ¿Á}=ù‚ìªÓI- Éf Ò°>¥‚^I{2ëÐk1WÓ›çC7‰©tìþÓS=µ<››•§2K%ÿzâ1_h)T™Òò­­OÌlõlÝHÆ ~fÖ-  Ñüª<åKÉKy«øe =¤ôÍdµZyc¥Ïîî¼8Fc 7­uÍN’I¾ ´4úЈ^²ÛA§U4T@¿ä¥§ŸÏI vMRI^Ÿjë#+õô½é¸¿–'ýñú¸²êeKÿé ³ÐßY¨2Í·¶>©vr0j ‘ƶž9}u¶ä´€¦Q½¬ªMÑ ê]F#ûðZènX27Û–?¾¥{[9¨/üÇÀ­]Æ gb‹qK^œ’üÉ}ʘ*T™Šæ»c}â£^Þ#4¾eeV-Xú ´d¦<‰º:å2–2aU/Љ@jÇŒÅüDÜ'!<"ùƒþV·×‹ß‰k<~Xºæ øí¬ðí¼¦;Öuw¹ìÔ§ÈË‹„´Ñaï8^î³M²½N½ÌûCdè/úôáÞ‡÷¡_QU8} ô‚nXëà#Ñ ƒQÊÉËËžî«k#ßézªàÕôlü­L'ôéýK?]üKžÜmÞ Ïç‹,ª /ä§ò—%ɦÏïs¥¢Lá}…*S±øŽ®OˆîÔæ&Úé˜D æ^þŒìѧ÷>,ýt—'0„2Å -i—LßIí9uœèQñ£‚>ÒäòàŽ.â~éDS¨ y™O¶øù=ÅN'+ý6jck×ÇV&–%r‘ƒÏÕytÚ7T(c+#µ’ÝØÀ“_¤fø¦ Ƭ˜œãôͬð+ÍmÔÛadEo´•”“³Ø)ŽA/èF4>øH4u…ÁÆÀin¢CõªmóÐiÆŸ2ÉC–xŸªàIyü¼þ(ö§obŒ+ÍÜøJ3ÿ ryrµ¶ ôV‰Üp9Ÿ“ä3ÞSQ¦V¡ÊTW|w¬O\¢>1²"†2†ßÒË5ŠÇh'¿!5U.¦ ÆŒ|˜@ÒŒÞÀò Ô'²°È2…ºšÏAÎTÊ!R#…©cXTTÇX\ZUuïÆ]‡éì Ã’z;ÜsˆæG,i¸¸ý¼N|HéK·¥›Ììv®ô{ÈÅ­uW€—å¾w7»à¡¤»J®¦CáËŽ’êð±ö ¤·ƒÜˆ ²Ú¨Ø$‡Å \ù°ìÑ )++¡ÒRÞ ü™NÐ ºA²©; Ä0G—›šÜ>jæIÈÚ¸=ä÷Â¥Š:™$ü6¬’`62Î?ä(ÈypçöuLZ{BÎÚ÷¼Ëˤå1Þþ™T”) U¨2Õ߸n2Éú¤USŸp·Z ¹Ûú$IÔÖ'Ù(O Q–)ÔÕüSÊZ<ä«{t€€ž¿žà‚×_Þ8bÒY§WnÞSÉŠ¿kÍÛ ˆáV:+R1,+}©ÈB…˜»̬ä=nn°]Îï%q††+‹nÞN´}í“á{ªÏûAø8útà{²â°XÌì^G÷ƒƒ`éCé——•‰c45œ¬µ/iˆ«ÕM¥Œ÷… þãÅ@~§«}6ùg9 üþÆ+—íb1æ [XÞº¢;G¯iyü‚÷™—]ÕÀX”&[¦€I¡ÊT¼|'ZŸÄ+oÙ,O eŠS-êjþ©•·xYP÷e=)~ÀkLTT§Žyv“ÁðõŽÑÈÁý’‚H*x[ÐJAgH™£ÕŒþs¸Óéïb‹WôÕ±âóB@{È{W î'XêÑ  Á ÃdDÌ{ ø›!kŸûôÙ½K_¸úÃÖ~rA}Ñt¤ƒèoig›ÈϦ]‡ ví|‹ébŽØ¤ò‡¼åk —%fPð xâ™O§¢L´B•©lñ ̳]ž@ƒ,S¨£ùg!”%°WIOŠ_¶!H¾…¯¾üìì;ï¹åÙùË+¾ûõF+O<“LBa…ÂE’­eωa~ÜŸîfÅïFÐ_»Åëçââ±x‹ª*Êc¯ü'¿ îûµe€Æ\ýEE±ÁÅë 7Õ)]tGg¶øoóú‰å'ðù—¼ñÚÛL'6°ABΤÌña^%É—(KÌ™à >âJƤ4e ˆšLI)Éßø~¶Ê¾-ˇóס޿Sù^–ÀvÞ%=)~€ +‚ÔV[{¢iûºÕ¿2LñóÎ]¼cöEI»üea‡-ñåm¥"‡C(}ø×Æ}qükÖ~g^ÿ2'È¥Þ½ªä¡Ø·ûႊi>1ãÜøhp`Ȭ~òAá‡&í Yú 1ÕIVà;•tF§dA~7ÓüCnŽœ¬§5Kü³µµ 3q$”ؤòÏw‹_”%É70Æ‹.ÿz*Êò]æm¡ÈxFÊ4ß¡oŠO‡¿éò„¯Ë2…ºu4Ÿ*„²>þêQñÃJ0¹½ùÚâ’ŠŠ'ùøz™‚_¸~–!Ë…UC#@õƒ†¥ßÆ~èßFð,þ þ±Æ—Ê?V¾W”DÎöí×;ò£ý߇öÂâÇ7aõcŒ>*K({‹ð„"øAW:S:0èŠÞlð«Ôª­{ »·lxeíâ×2à ã4!_³|Wüá²Ä¼ þEEï>sø÷ìT”)~ïT—+¼·³” ™ŠEK¦ËhÈïÚ2uxï®'P739…R–beNŸÓ›â‡‹•¸FEÕòÆ¿þñìÕ7ß :¿pôt]ðæ«Î1¦¢ÏHÌÄ%Ì.¡ð9 /¤ôC€2ò¿+Å_ªéÖ¯ª¬`2;&| ‡¨ ÅÆ¿ÑýýÉ{;¾!=¿Bô¤ƒ®(”Ù¯¯µô»Rüì©'èE'YP±—¾)ƒýä¹D b@IDATèç2ñßN]ÑŠo I>±O5ÿSŒáEˆ4F _ÀçmZµðƒg×/[„á{!¸$±Ç¦µR Añw(KÌ¿U‰ÑéãÇŽM¿ð’[¸L•¥¢Lñ»E>çƒL—ž¤L”%Г‰ò„ïœY¦|V,ýãÊÞYÈ— ­,’¼JzSüWöó£?­JLyeb[¼eÅGÛλæºë¹ÿý ®à˸K¶`y™Ó`·ðÀø¤h%ý;úã4þ饣/‹ß²°Ê‹Ñ¿åy½ì£yŽþÝS:£ùþÝÓ÷Éû1-f£ÃÄ4L£Áï÷5íÛ¶eá²·ÞøÀåj‘ʾïÇÖÈä r…¾ï|vó3{"Å,K|Eô'qÃhÍŽukvž{Õµ—0vr™*å¼Iy™%Ñ2ý[PÛƒ?Ñ2ý»¯Jë­Ñ|FÿNäãѼFÿNäò™è2…a°vmgé›óæ¶´4b¬B-K¢¼ØkÔ–nø‘.JX*¨ Î/”: ^àŸ}žÏsÖ´QƒGŽžà(*îeuØËÌf‹k—”(~ÜiÂ`&o^·iûy¬öéG€çŸ÷x\®WssíþÛ¶íØ¸vn|XõPòPþ¨¨êÛáæ—ýûùlí3›"uZ–øªhpÉËÖÿ<þýö¨‰S†ÔŒ3ÆQ\\is8ÊÌ&sâÓf†¾¯þæÜèóù¼ž6—»ÁÕÚ|êÀÎí›¶­[½ƒYPe)—ò1Zõ¨øA¶ÖR‘Ê\[‰yX ×ó¶ïÞ£‘ ,ÞËgø0½éó¿4üN~áÙß?ü°Zñg£ VªâŒÈ@‚< I¹•ÜøR™Aù£ˆß8/ûõå³|ª`’Ä)—ÊRÁdN–•åAʈ*KYÎL|^ÏŠüC!ˆZ¡”– ,}­Ò—±PøÙPúüY:†?*eÈ„Ü #Ú 2e=6\+DKŸÙ§\+KaÂÕAÚåHʈ*Ki‡<»лâ—è Ò†Pb/g#“î}(|©ô3ÞÇÏßÖ¦ÓÚê8­@¤lHå/+-ù[^Ý­þJ<ô^–TNeU–2‡µ.¾”+Š`ÉÖ¨¬°`ÉÉ $iåKK_î3 2ÉTÊ ²²Â^n ¹És™¡&·¾lÐ0ÒsYÊ-Ds›ZÈ’,3ØËr„½<{TÊrIñK¸¥B ‘¤ÒýÊž›ßG¿²J™C² “” ù[í»G@b¦Ç²Ô=õêŽT" ÊR*ÑÔù»rQñGC*+¯èóÙø +J%…@®" §²”«*ººG@ö‰ëžPE B@! P(É# òª7( …€B gPŠ?g²JªP( äPŠ?y Õ …€B@!3(ÅŸ3Y¥U( …@ò(ÅŸ<†ê …€B@! È”âÏ™¬R„* …€B y”âOCõ…€B@! Pä JñçLV)B …€B@!<Jñ'¡zƒB@! P(r¥øs&«¡ …€B@! H¥ø“ÇP½A! P(9ƒ€Rü9“UŠP…€B@! P$€RüÉc¨Þ P( œA@)þœÉ*E¨B@! P(’G@)þä1ToP( …@Î  Îd•"T! P(É# òª7( …€B gPŠ?g²JªP( äPŠ?y Õ …€B@!3(ÅŸ3Y¥U( …@ò(ÅŸ<†ê …€B@! È”âÏ™¬R„* …€B y”âOCõ…€B@! Pä 朡4C„~íG?Ÿæ÷û®&ƒapÐ` *éêÓ‹ZöùC(øÕYÐÙ½|½Éüö®°Š*kŸ™y/磊 ¡#HlˆE]»âú»v*6`÷¹Et-(¸–]]wuíJ±`Azïéå•™ÿ;“ÌËKH å%y/9&3sç–s¿;oÎ=çž{®²Ïwjª¶àµ©¯¨,­Ä ‚€ u…€0~0íqãf:³¹c5•òêÞ$E!#BÉÕ#ÕlÅFEUÒŠ0óo¡î8¯²Žò’C/Ðã ·­¢Ži£'ýu¿n(/´‹h÷²Ëu{aeù$^A@&M™ñƒW“zÛÄÉÃó9o¨†šœhßb´·­¦Ö¶J„R¨h®‹ËsN:èíN»½ýÛòt}vŸw×Cw>æºóÍg\ ðŠ ‚€ ‚@Ý!P%i¶îªo°’¹Ý¶;ûóx{„ãóX5½õ¨7ipä?•ööÕ¦_g„qÙ\×ÅuÆ*‡[«ší‹Q“þ:•6Õþ¨3¼¥`A@²45FÃR>KòöÛ™<^Óì϶³§)çFÍÑZÚv–E¦î¸Îs£gkíìkÑÊów=ñ$3¦é” ‚€ ¦ÄøMÕ>´ßxÿÃ#ìvÇS`úÆ Èÿ*6Åt`«Z ×Í40-Š¢>{ë„'.A^îaþUQÒ ‚€ T¦Äø¹­ö>}Έ}-N;hœâü_È0W¦%N=d8Ž7† »>´6¥¾©ò + A@j‡@Sa.ÜN6dtô»à¼û ÒÚôs|¡5¤¤_¾Û˜–~ÎÏ5ƒÔ¤¶ýO[BoSéŸòpȽ ‚@!Ð Kõ7¥ïXBÝÏzŒ¢›w£æÉg@ E:¤}ùа²!*¾#åÙdÆÉA@A ‰¿ ½ÃnóÉTç[ÉSO m+fQLÂI´;õ]Ê;ºÜ¹ÌÇ…¹q6È›l%—³ ‚€ ã¯B7æ1#'Sê·’ûÜy´sÍ;ôû¼ÑtxÏRŠmÙŽXa>vÄ´ÂY¡¢¼â€•G΂€ ‚@C# Œ¿ =àóäS¤øè„.þÔ¶ˆâ¹ý˜Ýé”á¯bþÙû‹ù<:¾³yÎËÜáO/‚€ ‚@( süUì…ìÃë(.±?õIgŒ£Î§Œ"–üÓw-¡´%OøŸÅ'ö…êéÞBœ\‚€ ¡€€0þ*ö–ß^ Ó¯ú/mYþ7rç¥Ó¦Ÿ¡í˜ãwçg”)=ùu0Š6þ8­L¼Ü‚€ ¡€€¨ú«Ø YVѾMŸPûž4sºç¦ÏÚt½Ì´äß¿éÓ*–,ÉA@úC@$þj`¶øÑ¦Þ·ñcâC‚ ‚€ Š4v‰ßèþÃëóxŒ¾éà1†O÷åH?í!M°'‚€ 64fÆ ƒweñÇćZoq†Çí>RŽ®§»½r+‚€ ‚4fÆ Ü…ùyén#ZqÎcž…JÓfÒX_ì< T:A@MñûÕå»6mXƒå ·{ÈvÓÆ4îÞºå7iѲô a‚€ á…@cgüãä³¾vù/[}ž¢¬ÝÞþ|’iÓ½î£+~X¼Ò’ô Q‚€ á…@cfüLÓ‡n1ÝÛ¶~sÈÓU9ìír=Å41mûvíúÜ¢çÀv„ÍB ‚@x!И?÷„ŽÃbú¼už÷ÇyŸ/ôy ³Ö^®{ ;§ ‰À´0Mº·èèŸòˆbz-Ú¹A@Z#И¿%)3ód&êæ#??·`åÒþ•­·¢U…×pšL Ó´ú—Ÿÿž››ÅKù˜^‹ù[m Z…A@ðE 13~î–”-¦_„kóXõÓ÷©[Ö®þb§·²¼à£!%®›i`Z¶oXûÑòïñ­ó‰ HA@¨=µöÜQ4d$Õž”:)ÁRõ3ås JŽÈ%Ÿ~¸È C¡>§\–­·4ú9¾P[ÚvÖ •Êsú¬ÞgIëº5/þøƒy42½L7k,„ñ ‚€ ÐÈ0”X´po#oeƒ7¯ÖŒ_1”}hÅÀoIÅ°Šœ§3}V¡ó"~û·Ÿ~´$ãÀþôAç½n©ïθVö-F{Ûj¥µm#E(u³³¯Óç%{l½Ï†|°7ÈYñÃ×ÿ^ýó+KèË-93½L7Ó2S E‚ u… ‘Ëëªp)·Z3~³“£õ=O?ÝìµÇ?bÀZŒß’ø™©2ÓÀÁmWS—ý´vóêUÛμøÒ³½Ý»9Ñ5V£Pò §’¥Ø7nkØUp¡>Z1EzsŒ½[ûyÉ—‹>)(ÈËF LkOøÌG Ä/Œ€HÆ‹ó_ž·5±õ«zm¼VÚ²Z3~MÕxuï4=_¿ µ¼WiM ÷Àšççys–øÙ”_ÃaÙ7è……ù¾o?ûpñ·DßuíÝ¿}Ç“»Ÿ—àˆŠŒµÙ"y PëàõzŠ÷žˆcO'%g‹ªîSÛ«túà^ÊfÌê/@Y%˜N¦—íDÍ$‚@ãF€yˆajO …–†¡ÜóçgNÑ ï™`Íñ)ŽÇ $´©¤({Òwáã¼[³9×¼îšx(h®* A‘fGMžº[!e휔É#ªZq=§c&Ï ŸÕü18âp$ààNä9¥hüŒÓ˜šœ9OPðA9Xj×{uT;žÝ׸=ÂN1¨É ÝCa-š»D{jo†w?ÒXÒ?Kü¬êÆ$‚@ãF`ô䔯`wÕç”)í²¥£'¥ 1c”j—餴(¦E1lpön'·ÎòB#š…Çþ€ o2V“ª¼iÓš½3ËuklC:…±š”2|í9 ƒÎ›=mò÷!Úbî(‹ù3£g†Ïð‰ÃœûÇÙÒXZÚàd©é™óáë’¤Æ_q¶ñ`\4up¢ÆH0ÿˆÚ¾ivÏßæz—!Åôy~_‚ 1“RÎÕÉø¼tâÓ&ÏhˆÆŽvÍŽ2¼ésÈ0n²S/)bƒ–¨m£Dm;9”ùÖ§œ¥8Ì?†òôtÈÛö{zê9zK€ýЦÝ<û¯“•oƒËµÄ¶V$Ø(²°Ý›ïr) &ÔÕ†¡ùÛår½íÜçݳš‘LÅžxÖט|ÿÃйà¶Z’?3ø¨’ƒ™>˜ñJýóF¦´ò™‘³úÞ“§£¯ÒïMjN—±ÔïÀ³>»n{‡×;;XÕ_ú¦áF‚  “ázÒcMH¶µëîrÝ΂O½—ËP÷{¦}ÁÇ=K”®Kɦ°Âµê!ÃÛ~/¼Ò—§'ú00Ú,Ξší¹BWèrL Ñ µ >éÌW8xÀXÒHQWú`ö“O|«Ž,ŠÓÔÙß`05“¸Ñ“§^ ¦5þovʤê³Õ@‡ÛËÌŸÕù`µ¦„ÏÌÞbú< àx~^žñ×+‹q—güllÈŒ½ð™1ê%ÛOÙ5б¡F Ô©*¥Ú4ÛíÃ'xxM¿A@%<>fò´ÿây øÞ¥sR¦|]W ={¶]Ûs¤ŸÏðB}ñAÀ¡Â¬`7”±­a_0q€óêÁ ¬j<†ƒçŽõq°00ˆw*y¾–¶­ZŒzšƒ\H˜00¢(SO62¼íuŸ¡¡í»E›’¤=ñ^}hjÂÌ*Edô””‡ Ýxóý=ñ¶•ü™~fþÌØYõÏ3{ël1}~ÎøX.k˜é[Kü–ÔÏÌŸ‡”î™÷Gt8©­g–¦ÐÅê…|˜:y=&&vÊ9÷f…Új -Aš#À’>yÒßÁ¼þõŠªŒ›3uò‹5/­òœc]ÓÛùèÉöP•±A`q0h¨N¾…_NWV}5C»5mno^¡ A&‡@ž·à>ÃP£û;>{Þ† ãÈ¡x ]¿#ØöàP.ïÒGж^úˆþ'ÍÑXù›Ë-’ûë>ýÛ×íš÷¬ú篧G%[ä,‚@cG€­øaÁ?Öû«¥‡}sUÅG-µ­6Åл1Âøƒh=”wÉø¢-ÜéPÝ`8ø kP`@c<饂óžU>ž7Ca.éçzè©B»Ÿœ6ö‰mm©f ;JÉ„¤šlT…!Ñz,ï …;/}TiǪ åQ üž"ð°Áà*çϛ÷ܓ۾œ®ºæÏtt­Gò¤*A@êË•%Ú¶Ö_u\S\º(†‘ìj„ñÑ(oè¸ÌLhž<í¼.šªÞ€%€÷—ƒ:Â…À_|÷fh~újºzïÂWbKv²ÉY0F@1:ØU·×¡„âV1ÕÇ•g.y»êp1ø[õs?G­Ý¿xyZŸ ú-o4—¯Ÿutó*žÑX p†--: 8 qg¹ós_üòYe‘ª¨Ú#£>vNõÜLYÊY@’N ìªW*ð„Mµ!a¬ú õXUU´·kSNEyE⯕ëýjï(xÜpñ#E›¡˜Ø©S¯¶Xr-4Ÿ ï`sY A—èºþww^îyÓ•¯¿zN³hfd[+œA@à+ îrd~ßÁŸäàU}Ô׎ÖþÁ‡ý[¾>õñƒÝÂøƒh=”ç+Ð?ï=3íôªTÕ{dš{Äßÿ.}ĸZ³Ç&Áà^ °féT†È6éºñz¡§p¦V`eÀ“_ͰŸfZÊV¥"I#‚@"€oØnìz§y Þlµv!¹ûÕtîÍ߃—³HGt+:óúé‚;ÊúrÄ´¡³F~FÃÆ¬£‹p´ïs“YqûÞ7ÒÙ7~ëêyO¢¥yw¬²SToÁ7M nÆ\<ë¥4¨ê |¤'VÆZ€žÏ¯;30®üõ%ã³@ ðƒ5ÍÙ‘wÄÂàÇÀAç6àT0ü?ë>ï¯ìÿò9õŸó§k7Ï!*èÖ¥åi”{A@j‚€¢ªßáë¥À›iM²ûó(ªº5‘¶þöŠÓ)¡Í©4ø†yéŸ1õ§í>øq²;›Ñ²¯¥]kߥÞCŸ¦ÈØv´g=6¨uÆQr«ýi«z‘«·„£¬â#µ=è]®¹A÷Í"Œ¿ª½Bé ÒÊWtlè x ߨî/oHîñ\ê‡=^Y\ã½áì¾t¢þ"ClFd; Æblú ŠôO”ßštãf 4þésìÔÀÚyÓÕæ=§]¾ä…„2ƒräRzEà¾]Ucë÷Zíbš|ò•¤©Ú·±XÃÛ²íø}íN}ï˜ö´9ií\ýÝÿmüñ)rçgP›nÀxÁK;W½E' {LžEt‰XFż¨t´/·C·Ï³ñGs ãe¬ÆsaüÕ+„’æªægü=g¬½ CÓ~ jÂýT乯 P‹¼#é8Û(ëê7àáÅæïà à•K5.ާ-Ù&€åh$3/¡sê ÃCð&õY¾;+ƒ=B+ðü\·à¹˜VåÓ˽ õÀÈ‘#}¤h®L_’º¡hh«ŒIìIÙë¡ù,vº{íû´Œ_UËÚÂÛñ¤ÚœT½Ë_W>®±ÅfRYé©ݬ \î:üÏ«zÁ[úžùù_Î2¼é³ªš·*é„ñW¥Kæ›gIü}^Lí§“>ûu?o_©ý* þ†ùú2Œ?7gíÜž3Ö™ë\ל!Îa›€ËÑo1Qo£)¶pô‹p•ËËïOL Œ…¿€¼zÞAØlÁ@à½yÏ©÷9Ý>hùì•8Ê•%·‚€ Ôy/uu\Õ|êÒ(-ëKfü« ÿ@£Ìæ¦U*;:¾ åen?aZ{D¬™Æë)]>èõÝoÆç›e(ßñ„eU– ÙžF]?b)?Ýrÿä©+KWÝø²C˜êæ–ô ‚˜{žnQ<¯ïÍ÷ÍUI}pý„ž›Îxis\¦»°—Ⱥ¥HÉOuÁiµKQô³fîŽ<ªgžÕ)ÂþÇõ ¸×ó©Ò Å·aBŸ¯%F%+‘†é?Íl™¥8W7ô 1>“Pì)«Ì;„ô$ ÚÕÉ(iEɇ¨­ù |/Ãòsñ†‰}þÃÔd{ G`P°pÍÄ®‡ºÏHÝùÁóiý[e_ õü÷_=ÐÍ”Ø}†q;ì^¬n ßS€< JZð\ëh¯’1˜tšã<üOï̻ä~0žÖ}¸óéTðëw<XƒÅ pVSíñ±k/Sò‹©.a’^ þvh¶¬Ö¯·5¾ m ¡Ä™Ú@«È÷æ5¾ æ9‹2¢ C˜ËWJ®ÍS@Šg³ŸŸèzИ¤a÷’|¼Ìé罓¨œ†Ÿô©øñc™ Uˆ:ð³òÏÚ#®=~ý—"-.uÂhŸûpƒqв´o0e³¡Ø¶vjßuû)(_ŽÜ ‚@ÍX²ä|›{ùòæªÍÝÂçÑ[bZ±–³·À@½Ôë-¡Ùkßg 0Ú–ø•¶À}âW;>lŽß/¢øW‹À?ß’SÉeqDIœÿ¦j<‰~¿ýý(w?ƒý c¿j(û!  gݱÿâ +ŠK=ù/3AËéƒ"? vöÔªÕ*'=’º]Žj4|ŠJw@Hâ¿Üôó34àÒ9tÞ­?ÏïoøÁEù%Œ>>±/åÝFº÷˜O­?U.0¡"#Ú‚öT%}UÒ˜U•„’&´8cc˺¶¨ê1cípüïÞ8¡¯¹pôú¹†¶f×ÚXÞr+–¢ŽÛ0±ßEœ¶ÇŒÔ bÿtýÃ}þiåµÎ=žO»M3èWlý™S‹8oõøî{­g^ÚœXä)®ꎴ ½—Zñ59ó>E…§*º>…¾øJôÁ‹Ø?Öêš¿êøÔìFÞ­ÐpðÎ[áßr»BêNÃfß5ü¡<|,ðY’ 4Q–¼ÝÉ©=ØÂíóµÀï ŒÛð3rpè–:,ÇÀÔÁÐfè¸'Š·˜x]Á†: Á¼‚‘D½xù½yÏ ^Uª:ÒxÑ/}àHvUé3å™þºîYÕÍñ#õvð¬dõƒëýsþ´˜¶-ŸE{Ö+POTŠM>7OŠ?7ªAçÞ²Kü¦ÑþMŸž(ûqŸÃ_?ý” ø> Ÿ“2åëã&®âC‘ø«T¨% dúLF¿™Ðü͢󃑊óü‹±ÃÃÓ`†s8þü··;÷ιùŒ²Òž7<Üûî3Sáƒp$é÷˜¹¾o~QÁø»2e·šÕ]2}°ÂÈ.|lvádzšý:©ûìdßwQçË*#?8þ°ž – 4lT[ä=_hxãUUOÀï#¿xHÖ|NÀ¯5Ãï¤~8J$q0òüC;£ËÍ?Åç’(¿šÝº/IQÕÂà7ÆK€aƒ™CæµJÅgEI‡Æî®h‡"b¢kÏSQ­¬¹/ªjýf:ÃðL°)E¾îßjÕʘ×àoX:•º ¼ŒŸ—ÓUD[@\ú0Út½Ì4è«-Óç2÷xûé¹É¶ßÖQ›kaüµA/„ò®›Ø{Ù1äP÷´ÛæóÀ‘Üa€W¦ï}䘴%ª[ùûõ÷'1|Þ÷4E½îáÞ_usC¬qij¬×Ì´ÞëÆ÷N³ñTƒ}oTÔªq3­¸êœK¬ü×#s­¼;´í陦K'L[Qvþc9ªèyUãòá u»?ºWý»Ë52hÓšÂø«Úa˜îÆ }ÿ1wæºUk趇ÉÇOñ*ü1ü*k˜ê•i7XÏ{Ì\s>NEë&ôþŠã6ÞÙ#ÿ*|¬x>>­Ï ©==>ºß·SÿƒWÉÙ‚¸ 9]°BÉÇÄTã£Ì/Ëåê®í[:“â= M^BØFE‘¦#®;¢½,ýœ(°Ö  Ò¶AÂSM”pƒ¸â?8p Int¯$x.ÌÂH/øxus ™ø˜gB»’‰6®•L,_Ì´Z¦™9lô‘l™vR!ÌæEÑ‘…EQ䆄IYÆøkµp­P4¾¾ˆ3¯£`8ŽkÞ,Ÿ—Ä×…™7˜x1ãÞ±}]$7¹ŒJ©äÆ|ÅðÌ:s:‹Yûãüü´FÁßjó|F ˜TÀ´)CWé° &ŽÁD†¢©‡m>5C·92†Ë:zì»z/¥žä-"GÚÄÞëüItåZ|™Þóßó…¡tÃï€yIJsÕ0æáÃR€Êî2éêø¦Ä°o#ªáã˜ÀK†TÛÑ^ŸÞn‡;Â8©>_Éø¨ó:àd|“‘)‘YÇd®$ùŠ%1"Ö: ˜üªSsÉbI^„gñ´ƒŽACpÊEe¹H’‡zsÁ$r¡-9—Ü#jŒ‡Ë-àì€a3®q&Ø‚(8|¸æ3§—ÜãØFçhà… ܈¶îùŒôÑè:?ã†æ‡Ë ž¥å€üeCà=®ý·þ‹ÒäÇä-}T£+Ð[ˆj²pÎB[³Ìk .ñ~dáýa Ï-–ÄÁÈ}È#´Ã±î˜ŒŠWÄT@´hIy•-ñcaã§,ƒ`ŒC`6ûÄžL+‚p]¼ì‰ï1]ŒaÏ{ ÚËÎ[6÷xX<˜€5Ê‚°X|FN|œ`f‚‘Ê2Ÿ™qÅÓ0*ÊfÌxHžóGÙ¼/‰³#®ø9ÊA\£ hë¶™a3ƒ.fÚæš!Žçk0s¨ÍÁÈ)KSÜ+Yš®fŠ=Ëçh“ué[j§·nTˆ¿1£'MMmnÛÝ뜨7Ž7xüBBà)û Xš'ÿ¤V ëßýLÓqé ùXÛ*E|ñ¯´ÙÔÇ“á[Ϲ_’÷éúMø`}ÈôÙm°ÇCYáÆô¹%¶{pÉÇqðQ¼ßÌgµ„±D¬˜h ­-kÒcº„ò40g5ŸQŒZÉdž.yªót[«¼KÕUaÔ(ݘôJx¾?\d[»·÷yv_‘C§ó£gÙœj•W›”•wØÛ‘–åß„YBg¦jS‡¿ìzô„IM*ª ¤&„IžÐ@€­÷uŸþ1æVaЧ ÀñÂú }fR‡é€•š¦Ý‘6®÷ªÀx¹nX3±îƒ>ö=;Û#lEvòùlEšÏŽ„MS »ÇmØ»aƒoÌâ`¬æPàQYUÁ!½")Tù°PÄ@Bå{Œ$ÌÁ =±°êÖa†K¤‚ŸŸ‡tû;1ãðŒã`c¢aŠÀð(<Ýàu`i†Ï§z UõšêÕm6o‚7ÊÝÚ;pôrï±e ƒŸÔÞÜ3yzwŸáþ-R9ufô¿´8õPÈ7ˆ7â…¶ºÏâŸ×fÕnñšëñmuE¸0þºB¶•;â¥ÍŽÝzawf?¼ql^öæ½ÿ–ÖÁçñ}‡uýý‘r!‚@"0Æ•2ÈðèŸa¬ÙªcÖɾ#Úb3•êå5"(WoI Z™Ï^uŠ(“ö· E*¹©e²­*eù’è€÷dÚå€]L`îb(³löfÏrÝÇÀu„ñ×´M£`ŽÅKtÒú }j-–V ‚@8 ðà´i­ ów¡„º(V=ìëæøAK¶­……)Ï&?°9Î^O_J-¼ÄWdĪçÇÌRÔÚÖï÷ö„ŸgMË|?ÝЈýöUϿ暴¦ìÓº¹Æ_7¸6™RÙà¼Âè ÷÷ä5ÂA@)Æü9årÌKM‡4ÝÓ¦¸}­µÍZ Û¸;ÜO‘°°)…X²b#Li°±¹£ßnO?ŸÛˆÖTÒWŠÚ£•m›óLç?°’¥º¶ÅPäëñ°Òå-Ðcáb\„•X9¤ó.«ÛØÚýârÝ^XŸ  ã¯O´¥.A@Aàî)OÀmPù_ 󕕦ȥ_¤ý£­íñ/öûžº ùf·²o28?U"•œÊ²Ïs÷»Ü§òü½Ï«DähŠ}ØkS_qLÂzŽÆ_Ï€Ku‚€  ‹À½“RÚû4¥‡á3’!}'ÀXµf¨9ª]Ió%µ\?g̘2ó£¦¤Ü 3Ö`ÓjkmÛ¨&Ú¶™Ëj,d=°|-¶z-0â(_oFÙØ™x¿·eøºÀÓ8ÛÆ* lvíÞº4Ø«¢Âø«ƒ–¤A@h’Ü7)¥#­òTA 0¿ëø¾£ú4o”|÷ˆš†©ê~‰„­ÄÆÏÖû¦!æôç¸ÆÈ^” óNJ­‚€ Ô)ü}ÓËß{sÕVÖz…c ˘ªÂvuZP?/ÙÃhp¾Ìé¼r)‚@#DÀœóÇ÷Þ\ªÝÛw¼&"ñÃÃÎyŠ×é+ÿ:`òLA q Aï_üÝ7¿ÿ£IUj…®’_âÇ~MWâg|윇×éW 9I$‚€ Öð÷ž¿û%Yú-Õ!^ øaÈÞ¤çø;²G>qÎS×GÒ ‚€ ¾˜ß{|÷Ñ‚ŽáÛŠêSÛ†FaÜg«~ÓËæ`ßûˆ YŸÄe©•;A@` Àî×Qÿ›N0(. ±Ù×auYkÆu€Âv®#¬zKˆA DÀ·Ÿ¿ûpˆPV§døÛÛÔUýuв.„ £&O]:zÊ´»Â…^¦sÔ¤”ec&O½3œhZC@ ø¥éJü ÖRqH"p÷”§Î†ÁÏRlÿô#|zŸS‘3'0›×0Z>}δÉkÒ—ËP].îÃC+ MýÝhS]ªFONù^цišzÉë´ |þ§MkŸ¯P5åŠÙüyù絺WŒS° [µi®U’YW ïê‰?\;Qè®; :{Ì””{뢂½Þ”c]ÓÛÕEÙ Y¦î3^ŸðÜs¼¯·A@Müª~µ‡­Ä>¡ÙABUC" Ð\¸ö~úÞI)íƒIÆÝSžÀ뇃Yfh”¥|ʆ²9Y…ÓBƒ¡BŽA @Õ¯h¶Œ¿ÖÆ}Ç#‚°Ûl“<ßPl\ýnÿPP0G> [_LÂN°H:¶|ø<ÙÖî —ëöBÎ÷ä©£tÃûW¾.òx¾ÁÆPŒº#ݤïˆMC®ægV=)e!¦³§N:7pÊõü—H=øFʤ8íh×ì(×>“ÃY'”¹ éç&Ùžx.pJåÝŠç'Û&ß¼×7íIÐy#‘‘èhßêå(²ê <š’2•t¦Ùµ_w=¾#ðY൪;tC™æÿ—ÑSžú÷œ©O, |^ÑuUéæ¼£§MK¢|ão¸‚U?Úm޽Þ"7? \.yÒ§Ó‹¡Îl WkŠ:ùµ©Oü˜N®&‡@€U¿WQ›ô:þ&×÷Òà# ’š¯¨ÊCpqy ÞþïD9°ç÷ `¸óÁH5ù ŠúyFíõîõ;†ò)Ú&¤™k–¥/©Ššb(ꋘ£Þ‰¸+ƹf6·ê¹Ç5­˜ôP¬»={ÌŸŸ>ÝŠëz)e\«ÆŽs¹æFžôŸÁœoÆv[ï î[°çø˜sÿË^Ï´­|æYQZ"ïà½Þ§^Ä á0ÄWÿHeL;™=J†ñˆ¦)®ã1}.['%Jéø4˜ízÒõ¿3]f•ü©Ý¹^H0 ôå ÷Lô‰‹õ~à»ßëqÿ ­ x{i={¶xüˆ˜›8`Põ04,Q^]_2úÏÓ†–¦”+A é!€ß§_ÕïÔU‘ø›Þ+ ->n¯;øû®oÂÞ/ŽvÍøzŽkÂáŠòŒ}é%GÑ¡ìçÀ`¾x#eÊU%iÞµùr0à·ÆL~êÒÙ)OÌ{cêßšÉU7pØ">ÙõèN;zÊ3} ]Wó¼…çâöŽóz ”£,ãr€­^(S‚.ÒsÎáÁ…¡Ú¿átû¼›ïCš>6Šè3+åQs»MD|÷ä§~ó¾Ï­º9- d:)dôK¶w?ÛåyŒ´\œŠišv—®Ó0Šù¿Š ö¬tÖeFÎ3Æs+å.Ÿ×øq¯gÓcxfj7¬4çêÐçɃfCi­(=gO}dsI9Ÿ@‘,'㙿hcOú(´ò›]0Ë5iUɃ7GMNÙÉtÜûQþLr!4XÕîÏ!"ÊÆo*#‹#Âè/þà# »ùn)6û=`¬‘ä-x¡²Z܇s†€Á·W5z;0©B6¤ègÆ—¿ž3õ±TÈ­òo=CWAâ_‚ÌK N3ã/º>”Óržâã:H½‹˜¾ýzÊv•­é,ÉYz2”É•1}ÐÆ˜?§\‰ÁÎëÐÜûúÔIÅŠÒÜ•]™x½æšü 4¯‚öIc\Oõª,1P©2Ý éhó’Ù)~¦o¡©ï–/¿xúBYÀôÍ$ ±6†ò ­|¹š~‰ßÛs@تúeŽ¿ ½± ÑÔ9®ÇvArB7è%œÿ5;eÊ|0TÎ%Ãf¥èz¦M÷ѳÐ;9ê`æ~Íl«BC‡~ ¦ð "ñ‡g¿…ÕI¶É¯‚ñü6þú½®WcÀ= ÐÅ\†ñÀK0"{6ðÀ<þ˜“~#0}E×J`-¼Òçù¯çJ0÷#mlC¾O¶uÿuç¸åržë†$} ˜¦Ÿñ›e•£Ç_¾Al¶ºÒ •;³èrÃ[0u?d³GlÑuOÝç…¹î IÚÙL_Õkö=0ÚõÔ †G_ŽùzöepsUóZé^~â‰t¸75ý?‹ãв’Pºmvmº×ã»5;«è÷º¦=ª’íÇëlú_ÑÆ«LócŸ¦äznö(´+`«`[mPQg`{`Õ Íp•I/7‚@AÀ§¥ŒŸÂ[Õ/yiC¡™¦%¼¡ÜuŒô âf§Lš'6wBÚ¼Æç1–ù¬F‰¿‘t«4CA@¨Äù%~•aü¢$‘‚€ ‚@£A t9ŸHü¦S¥!‚€ ‚@ÅÀ°Ï/ñÃhvÅ©Â#VŒû£Ÿ„JA@†DÀÀ–¼%þ@DÕo!gA@A 1"€9þ»„ñû¡ A@A q" ”2þÝžÎU8÷žÐ.‚€ P_”Îñ'& ã¯/Ô¥A@A ¾Xòv''üó;¸^E¡¢¡·ïðïRß´£>‘øƒ¢”!‚€ ÐxHO÷ÏïcÖÒ>w’øêÿW•7Z Ül)ð:ü['-¨ ·áØwÛéU–PâA v¸UOùŠË€Ä/Œ¿vpJî `2ú;q RTe8X~GR•$Å Ø”%YÂ8É!ÝØoèÆ.Ýç[ðöŒ©+Ї ¸_…ôÐCÀ§¥? ã½j¼)£G»"}Í•±6»ö®mE1b£œzB\”â´ÛeÚ¦ñö}…-+ôxôÌì|#'¿PUmÚ“c¦¤ð¸½;¼~׫Ÿ}öV>2Yƒ€ óK¤ T Õg$”ü¬^ ë5üÜbQõW­ß:•zÇ£®FØÿnèzr¯.mÓzw¡¾]Û*QN‡ÖÐÄIý †€9ØË/,¢Ô-{é·´m­×nÙótR¿.Ü|Ò£cÞ}aú|PÆß+H"`°Ä_2™f Týá=³&Œ¿†/B=ecµ¾z×O>¤(ê³I‰ñÆ ŸAÝ:´‘yüzê€p¨ƒ?:£O>”Í»Ð,kµ×0>¹í‘ÉO¼ólÊ‹hƒ‡HÿáЙBcH" «_ÕßæøE=’¯™I”Éôï|Ü5LÆ ž•Gnýƒ¦º e Ž¿Þöß»ÝñÌm'Qvü[—cƒ÷ލÎ{@?$þðÂøC·ÿÔ['D$µpëÁ —ß›äÄŠoÖ|V¿~ƒyƒžÚ“Á~ô^à\ªêoëøå#zï3y­UïÎcuÃHœ¾&’~èuR8PÄïÍ—œ©êµé;ôœû@s¿[8d (4†Fã'6î ï Œ?ôúûÄæp:ÇöéÚÎ9ýÐë p¢ˆß~¢ccîÝìrT¤þpê@¡54PŒæ!†*ª~ 9SÚ¿~ÔØÓ Ãh%{"™×&] ¿Gp3ÚjÄ ·œ DêoÒoƒ4¾†ø¿ªª5,#d²‰Ä2]ab2þ¨ØØ‹Ø9Öé‡uBMX"Àï¿O -[CØÈOÔýaÙ“BtC!€³ŸñÃLæHCѬz…ñ Éà”Ãý¡‘ªvd|¼>[‚ P[ø=Štªf뀲,‰_~ûµVò7”@ÆïÆßtz¾^ZÊc|ð'Á ¯|˜ëò¦QIB|´·¾­ÑZfü2Ïß4º]Z Ã…eüÅËùà¼Ç>n¼÷W)¢Vó›ª~UQbà{Ÿ¯%AA€ß'UQcP3}KÕ/ïXPЕB3‹^}¾Týæo…·äUWØ»À怄ÐA XÕ_ò’“¬I¯~D™9˜ë%Š‹‰¢>'µ£‡ŸA6[ímرŸ:%µ$§ƒ§kvíÏ §Þúœ:·mÉèü…Á=½?ÿzááÿóÇÉEu€ë‘b¦Ï.¥êÂ'é›$z¾Û?¿ïgØ«ù¹åÇ:¯2”Íý|zÜ¿M¼õ2ºøÌ>ôÓêM´|ýŽZ×áõùèõSfoœÀƒ“ü¼fKp ”RLJÞ«@i¿NÞ3[hLŠîgühW£`ü"ñ‡Öj2þº").ÚI]Ú&šÇêM»hùºmtfß“Ìê–­ÝFó–®/ ÎÉ-é¦KÏ¢ÄfqTP覔¿fÞcW@3훟|Oñ1Nºâ¼Séåÿ,¤Â"½ðþ²©*M»ÿ:òx½4÷ë_é÷;M§õ2œùý¯~¦v­›CûK?ü¾‰î»áB:©]+³\ëÔitþ žôñ’4 {Ç 5 ß®Ø@‹]GY¹Ô½cqv?h  Ì&C»qÍ…ƒÐžÕt’=:Òy{Ò?¿\JYhß ^Ìçè=­©›÷Ð׿¬¥=‡ŽP{ÐÌuôìœl¶!!6ŠddSêæÝÔ¦eÝ~ÅyÔªy,ñ@蟟ÿHë¡Q13Ø¿[{ºæ‚Aþ6ð`fþOkLº;´nAc®JÑ‘s3oéÊÍ/¤^]’é¦gQâë Xï˜0ý:WŠl|è†ÞÂj>Ma¿”Û"¿Õ£¡s®óò‘¬\Ú}àµM,ÈnÛ›Noú=pkºù²Á”fÏ ;R™Œ4éÝk7j¢ìÜ|ÊÉ+$fœ§õîl"wõÐtÛCÌëïWn6·‰½íòséòsÐÿ-§ô£Ùæ³läûüûß)mÛºìœþÔ¦…ßv™¸ä¬>¨_§/XU&ÞºiMWDÿé0o_™tL/v¨3ç8Ù`¼øþ| rºÒ…gô¢E0lÛsÈ,ª2Z¹íïÏÿS-èÁ/¦~Ý:P‹øX3·á³ï~§„˜Húã%g‚‰Ò§ß®0ŸÙ4: ϘkϧÛ.‚­r·ÓÊ ;Ìg[Qç?>_Jڴijs¨O·v&ÓÏÂà„㇠èFw_í=t”¾Ç ¨Ž‚Åøë¨x)VhdXô+¤ˆÄßȺ·Ñ7‡%lfDÌ{cŽ8˜+‡e©[)&ÊA·üálóž%п½ÿ5mÝsƒƒff\E4M5%a~ÆsümZ3ñµ[vƒQ¶£Ö-b̓ãWn؉úúšÅy)åÞKÉQ¹M€îf¯< ýóúCtÇ$HÙñP?HÒ˜iž ‰Ÿ,í[¥üsÀô½|óKõêÜ–.8­§ùøHñwàíéD´ò”O‘ À!0$ÄF›ZŽËÎ+ ¯Nõ?vFo“–ð©hÛª¥mÝk–ñ+´*‘κãÊsË4'uËâÌ© ™<íÑïÀë’ÁÅxù – A@hJ½ö‘!ªþè€F]eYÎVMíæ|J÷ô¿ÅË¡NnëW%Í΃*Æj4‡Œ,ÄWÀø}†+ >Hé›°'he“³ 4,~‰_3dŽ¿a»Bj¯1¬¿öÂÓhöG‹1~²©ö¿þ¢3hÁÏkèƒo–Aš¡»®>ßo‰>|p?úìÛßéÑ—æ3âÓ{wñ×Íö,ݳÿüBÏ»‘®v½ûåô"ìxÞçµï¾îs•€?c5.®……þšÍ»ü9Ø÷<Ïë?9ûŠBÙWžªiiïOP‹Êh‰rÒ¼V›FvLð è|hK¬À8,Y¾íüÉÔšüqø™æ£óõ w>[J>÷ñÔʰÓ{Ó¡£9æ3Æm; )yšâ£…¿apå4W-°½· ?øæWsÁK.¹ ‚€ ÐÀsüjãøYíW«0zòÔo¹€9)SÎ糄#ÀºèHq·Oœüi¯®¿ù’VÓŒ¼|uE¡SÖwùçnÌ™³®=Pïóé¦v Ú…òóÚåó×äþxôT·¼Êhe‰ïˆKVxýÃ%ÐètÏõšË+‹—8VæÐˆmò aÆ_¶ˆtÚÍ髾`œg¾;ŸR7l[óÏžþÊËÀÁF8J—kàF‚ PUšÊ·ÿËg•_`Ôw㢪Úà½?W£PMWv"4T©ºê Š˜˜UyeLŸŸ—ŸSç8f–ÌÜê*žêÖY­vÛñ"•áUÓgºØ1ö…Êâ+J+q‚€ P÷(µ°fuUà™…œðÇÿª…wÛ„zA N`ûËØ§N*BA tàuü%œ?ʈ€q_QèÐVCJJõ—5,@² M ?]:ØttÔÔÚ-íšìæƒün7¦åŒó;\r´1` Œ¿1ô¢´AA è,}-™~1Ÿ4([ùA£°‰ÆôWE A@h ääú—òÁ»J£XÃÏý"Œ¿1¼Ò†c˜ó¿%æûcH„ UD@QßÎ|Üt1î«â É‚‹;¾aG6*&ÎØÐá¬s?ö— ÷ÁàÆ–—¼ýv©c§8ì¤ç¼å †ë^ÞÃ;Ÿýú}°Û_3¸¼Í7]ûòÆ@¿¤n¡Õwc©â´0=fÂuî÷¿o4·Ü¿«<ï8;ò.|ì©ï<ø$ ‚@1¥køaÝ/¿¼‚@M`¯xÓÞüœbaß¹mKú÷‚Ÿ±[^ºYÜŽ}‡éÿ}kz¯ã}Øд7?£Bøè……þfÏ^9¬Þ´ä{ñcw+á»ß§ûÀì››yç@öúÇeLýû§æ:Þæ÷•ÿ.4½rk°¡Ð¬S3øÕçÝ %‚€ àG@/eü°îk4Œ_$~ËE}!À›ø86¿ßû-»ÁÑOé^¼UïÍ%;n€Û_v t-¼r`ù+Öo§=;™÷ý±6âíoY° ;ñoâC¦½¸èHZŽAÂ…§ïÖÇ„+ÎPQQ'MØô·°–òaŽŸ_5Š sü¢ë9ØÆ–÷ °ïk:&ùíiè0ÜÝFÁ“ØEpV;œ+xkÜÊÂQlÝkíÚÇiÎØîtKëàé ‚€ ƒ€°3Ÿ¨úG"*#гS2€äΛ÷ð†?ë¶»cUØIíZaƒŸs›_v'¼zã.P²o}khxŠ€¥ùß7ì°²˜äðnyÖ4@øÂçéfö,åŸ3 ¶Ò-eüþŒr!‚@Yü HÉ¢ê/‹Ü UG !.Š.‡j}æ{óMW¿¬Ú¯,$%&Ð ØçµaŠM¡S{t¢3ûv5“;£—¹Ã ïjÇêzËŸ>û¿ŠÍrfô-%µL Çn¿ŒþtÙ`¤ýXúg¿üÜx‘iøWY½/‚Ôû~¯}°DÆ/¯„ P.ÒŸ†ŸÕÇÜŒ†w«;‘mwû•çS,ïþLJ×ç+³y îñáõ";èñ +\Ýî.9»?ið‹ÏáŒ>]̃µìGŸž;Ò<ËA@ŽA À¸6ÂÂøH"j"`ÓxCª‡ÊÒ3Ó¯(îh=¯lS빜A@°€|Ð ¿8"ñ[¸ÈY¨5'c]~r«fµ.G A ÈøçøívU$þ ƒ+Å5aØA@B«~E±ƒñ»CŽÄš$Ëùj‚šäA@hÔðÎ|h _™Ù£ÑHüÂøõ«+A@¨  ç4Ãô~±“;…rYQêe¬&†Paü!ÔBŠ ‚@h  gøç÷AQ£‘ö]qÙïXHP±k¥mÛKY¹æf6nwñ×2j­)‘¬/‹ˆ°SBLµˆ¦Þð¹ß!)ô¼å5õö×´%Ÿ Ð(P|~Æß˜6èá¾Æß(ߨª7Š×À/þm=-ú5;Üa}»A6µ€T[<äU½ ¤4ÈAº7ž¼z$}úÝï〽>tvƳÛî5´Úÿ œå`‡?4š¼Š“ U'ù¨âe‚'hj…5`éÔ“Í(4Û‹AÐ0xlèöWH¬D ‚y)pƒž²k¾ƒ €†ïEºbéøp¦Ã7Hºnè銪¤Ûl´uí}·†*Œ ÷Å UDš]i[÷л_.…t_H‘‘[(±Åjœ7Âåma¡ ëN*(èN¹ùýéãÅE´ä×4xÕ;ÛÔXNuê¬òrsûÿùåÏðýŸGGí­é`TOÊÀÙ«”î P.K­om†‡ZxRë¢=hÿ Züëzºù²³¤ýµnŒ 4b8ï)ÕvòþÒ;¬í_‰ïU!¶_¨“Ñ †€‰ 鉊¡ô ½nã7@óL¨Â#Œ?T{¦ŽéZ¸,>ZôTð‡¨M«ÏÉéÜYÇ5σŠèèÕæQXØ‘Žf^N¯Î-¤k. @/ÓÏ~}RÜþ唯ÅÒÆ˜³)ËÖ²>ª5#ÚñÞÃtrA*Ú¿íX¯í¯—ÆJ%‚@X#P*ñƒå—™ãwP«ÉEFúOªbÌZ7¡ï¢pk¦÷…[Õ’^Þ›~á²µ¦ßú¨È4jÝjv½1ýò¤ó`£uëÙÄt|´h9}º|pËË4ÖUlº-™–GŸ[oL¿|›x°±"æ\b:ê«ýåi{A@¨Ìúù ‘p]fKÞ5ÛäÙ"Ô˯ßgoŹC;Vh÷OP©c¦Çêmf2ÑQi”Øò¿Øw¢aW¨pýLÓóÉ’•”ºy7vÛƒò¬˜iûWÐa{[J‹D:¦é2pýLÓóÉ’ßë´ý ÙN©[7°ég¢E3ÿaëÚ:§=Ø{—KQtë>œÎ¢ê§Þª%­l¥ÿæô#쇨E‹ÿÕ²´àfgz<éß ~¦®íZÃÖ »í—)sûßÅœ>«÷×G njYÓ•›CïÏ_F®£ö×’DÉ.4)Àì[YºGò l|ÏÖ%AB¹Ï{À¸Ïf±é×´n;ÿÛÛ;בT µ¸‰¿à…SV–¢Ùz?–ûÍ>opI¿ ¬çÖ¹×Ì´ÞºÏ÷î{Xq¡x‰?{%È4±´»co:åä¹)&juKnqL_n¾‡¶ƒ^¯×kÎ÷×¶«ý¹¦ÚÛÕ¶¸:ÍÏôååµýuJ°.4Rà©¿Tâ70?Z ö"ÅȱîÏëÆ÷NÃÚ~ÏÀÙFÝ­ ¬°†×Âøk\¸d³¤Ý´m{ 9æ:ýP¦ý0ë·ï#·Çcj*¸ 5 ígç<¼N?”ƒIè VûC¹­B› ªà»ÁŸ!ÿß¶;ûû0+÷.éÆ[½žK9à¥ÍþÁÁùK [ÏçSo CÉX1¦­¦O¬0þ22sISᑯó+¦OSóM·Á“ñ×ny_`ûÙ#_]:ç Fû™>¦“Ý&£ýÁ IÊš‹^cç=ÅSá eö™æßwÃø¾_ÃHêX Ì/*X‰9ýìîÏ­9´ÅÚýøÞ\g³Ó ¡Ž—Ìñ‡zÕ’>VsóÚøLµ©ZV-K«ŸìLgÔòl…ïtøLu¿ªÖlŒØ~váPu­ýáÐ^¡Q5|…E~I’¿_Íoѹáá¾?áš:ÿíí΢£6¥]»vîF*>+M(Ÿ…ñ‡rï6‹Q½`üEn/©Áó½Ò*-BUÜ`ú^“ñ3í܆š†ÀöÓ÷~Mé©J> ·»((í¯J}’FÊ# û ûØù§÷á°|/^¾®™U¾¹IXÍm©ºùNÁà Œû,O~5¡¿©·?œú[hB L0–2~:Vâ%ZkB‹Hü5A-ŒòXªîš0Άl&ÓËLŸnCMCSoMq“|‚@“F@XÃO¼†¿Tp ÷ù¸_…ñ7â·Û’xMa?Ü$~üÐØ÷“mµ£ºëù­|MµýøÕ–¦ uŠÜ‡`ǽâ*p*3Çø°Þ™[%Œ¿N_Ÿ†/ÜÏüž”êQÀ ŸÿáWÆGMƒ•¿æ%Ô´æZæ RûkI…dš&eýô—aüá¾3w¨Ìñ7׺6Œ³¡àaFm1íÚÒPŸí·k EG`!~-C0Û_KR$» ÐäÀïÏ?ÇMcã¾pß™;S“{¥«ÞàÞ¯¦1W}‡:Ök¢Ðy§>Fݼ‹Úµ:Í,¨mâ@Üï<æ¸æ¼9™H߸‘Z&œ\õJC(åå';hþŸâ±¢˜¨Äh•Þ¿6–~¸=þ*Ïhk£OþG¿I å£(ΡÒõ½"̸ÚŽ©N"A N(5î3Ýõ–ÔÎ;óqDÕ_§/OÃ^IWUít´tõLHÞ:9#âéÊs^¡fqÊ4*=s½7ÿZœªÚ躡oÑþŒU”WN«·ü‡Îð}¸äšš\Ô¤-5ÉcÑf·ðL'½òkœtÒÚF/ˆ¦BËâeCÿ6͹<†>H+¢É‹óiW–N9E:}¼ÁM÷œIWt O7úý”Í\Żڴ¥ŠUH2A@(EÀ¿Ž_#µŒª¿4Iø^Y¢\ø¶@(?.Ì0jÂ4zw¾’lp$³vÛÇfùŽˆ8*òäÒGKî*SŸÛ“G{Ò—û(g l§A«¶üÛL·,íul³;Œº—ÉW•‹öšÐo•o•aÝWõ|ÙÉvr`WàÏKöÉ-5z{Uý'õX>êÔH“ɧüP@ky)LŸ‡^,FxwuX3ÇAíµiUÛ+éA @U¿f·—Qõ—¦ ß+aüáÛwuJyb³žt(s= ÅŽ¨²rwÓ'ßßCßñ·šÔóNZ¿ãs*(S³ØÎôû¦wËG‡ôýv¨ùOÂÜ|U¢º¾w­€† Y¤Bý±`ÆfÖŽ˜2à°–þA ôÐ,å+±íÁ¹Œa_ÏçÖ<ª+J¡Âj~MOWu5ÝaK·µöNÙûXËßm®0þ혆&ëÐÑ4ê ^Q4¿_e4išƒtû“¹tϧ—}÷c£’(ÊÙœ]WYöŒßpØK—v$¶É³ ü*#ô™¥4sx4ýãª:\`˜jÿßOôIÔˆE^ëKRY)/¡@À~UÄ_úÛ5:Šmz“ Å8vω˜ÈK¤BOsï.#¦ûŒÔ8Ìý¿¹ñá~…F;*§BåØ4é'iÛ?¡Á} ~']o2t ŒÌœôÌ»¬[óìóÑKœR&κ9«Ï}´i×|J?ºÁŠ ‹ó›°“®îé0ö,¢ç¬($>ƒ#ƒûçåš–û\[Ÿ öâwë)Nzî§üÀär-¡Œ@€»^Xø•Qõo˜ÐoNyÒ{>·®vy¿û®˜øªüóP¼—å|¡Ø+!@“®{iñŠ©Ô¯ë  æÄsÝ‘Ìkú»´=–¬|ª¢Ç!Çkð§C]Mψ*·žÓçÆ8ÉN[ŽzéËÍeµ !Ýp!Nhâ@ª÷÷‘^VÕMŸçSÛ÷˜‘ú–N^0{ãó¤}únßï»À4¡z-¨öLеy÷7ÄGMC~a½þñ95ÍÞàùo÷5 ŸAkÀ‡A@°t¿•5€74Võ— }g­iæÍWÇìÝ-xòRL\ËûWŒIÎ'¦0þ²}Úèîx+[>ìð<£aÑ>Ãp]ƒ5| í|®i°Êàöktâåy5­'˜ùl†7hí&]R– Ð$Põ“^ºAÏùoowî?œ;ÎJÇc.ÿßä´÷ÞpÏŒpÄD8öZid˜ÑÎÒ}Çn.SÅ¢ê5™Ï‡½ÊZÔ¶¥ªÄæáö;õ¬ªfmÐt½íw”¡!°-eÈ ¿ªßn/õÓðpîMÿÏÞuÀ×Qý¹{]O]V³eË–«Ü+6Æ€ ئ™–ÐB¯N  ´HL $$BBäKHèÁªmŒ1î]îÝ–mIVo¯ß7sO{ïÔ_“tïÞ®~§+oïnæ¿{;;³³³hÒÇþ>A­Äž;‡ýqû)ŒåQê“|e‚ÑP–=ºðàò‚æ5 .ø£]e4ú¼ä x} àóYA›;§i‰d¢è´›£[5‰£„!w%7x ·§ÑDôÁ‰üÛ5J!'‹# oÐ̘Ço² ©ß?\—œðaCƒs“äõebLßLI(€O|“ Ï]ÞÌÊM{®DtеŽPt[W­s§ô‘¶˜Ÿ“ßï8CÁnߢY$ˆ> ½krÒ슩?RbÕüg¸K Äœé#»ì~¢<£É—ËÌÐë_›`*©Ú˜&³%€÷Ü»k*à§þ¡Æ s’Ép£Xæ^ýz(Åx ¡G[Vj"ØÌ"Ô5Œé wÏÿDô‘²Ÿ‘lC‹šŸv¢?ܤæßb6A¶ûX¸ê–ûˆ>òGˆÿÝB4 G@'TTíT´}쀟ÂöƒùùÁ°?n} ð[/›'I1/7cžÔ·.aƒ =’›t––× 5þAàpäwÉû"}(ÑEôå§ÛA4 3žìàG+dù;¡>ŸÝÇøž—i¨Q§x´¹ØÑEôõOÇá˜(ð*^⟞e³Z`|ÿ4°{k °aS¨ëÒüÃ6‚ÝW Oüu³\’1bþ»”`þpŽ€¼*Á/µžÃO,ã²»ßî~hôe ÏF‡¤Þ^Ÿo;òyyØ‹;ûÇ $\ðk«¤h< ·À¸R¤äáúÐàñÖÁç«ïzç1\dz?8ëA}ã(;uukþ¤éõ # /I„ìT;XPè[,fþ&0¢Ð&ÂMt/=ƒžEÏì›™ƒ³ìÐË] #ê×õ¸æOš>Ñ‘é>E%8å6ñ:#üæ{Î>@á!ÿÍpÃÈ$x®ÞšýÌO8q€ˆ±÷Yj¡ñ³Ëloèë;dÄwÑ àUŒÙyƒàõ<Á~Óúž{õk§„¨A–“Wò68ܸ¶k¸±r›žC; §‡}²ò.¨i< F´î›q~ü„üqPÕ`†C•#Àíé驟¡–}XuW÷Ò˜~9š÷Ý8¬–“ Aß4ØlV°Òf±Èš´õh˜úIðÓ3éÙCz§â;1"_åq_W {m£¡ÚØ«{˜V½…Æô7lCM¿Fæß’g‡#‡¬ ˜,p ÖO®të—EÆ?{Ãå–¼^oËE”:Çòñ=G žp5nöQ´ã—pºÑÈŠFãpÀy(èÏõñž’P„–ÅÅFƒpqæØkvÆx\ðk° |noiUu}Tÿg«î‡åÑY Pð 0{Êcdå•`«à`e&œ,½ Íàû §ùÙl»»tž?ÍÓ§){uõc Ñ1Œ‚ú&xQÓǹûvÒ@û¹`2™S¸ÅDæ~ê<г¨SAÏv:œ0('Yæÿ@eŒ­û*Ùò4¿rSv—Îó§yú4e/Ûu Ò<%òê½<2ÿ#S’ ='þ½ûPø¯=ðìwNøÍy̓ù„ƒEUM½äv¹*¹—ßÈœÃÏ$?.½Ëø–ãòÿqÛzôå‚‹Ÿøª”n¼z÷mÃjYžXÚsÁ¯½Ò’ÜNçñÚF§Ð€*Ç¥ÃMËÖ?»|XQÁ€%}a@ö,Yè»Pãííñ‚ÝX ǪœPáe(ˆiöŠ(4`þjÜG/Î<… özRph!Až§o@ŸntA¦MDalEo‡¤¤D°'Úåc+òm–¿?to¸ø¿(?‹žIïq:]èOМÑUi 8‡»ùn°€SÄ@BhjV2`^ŠÈgÂà<Ô°ÐèEjü_Ð/ ÅïìFGLƒ >ÞåAKˆS^)0\Z¨Q}r56¢bê÷qü>Ž€n@M^‰Ú§vb¥Õi¿å¡šÎLáD¯e æm6#Ú;ãî”Àa~øt?ހོ¦z£ÿÃ¥CÃ[kê™)Ø·Ÿ(×±¦=îxâp¤ÀÊ|øÉ(ÑÝ#ê0mºH\ðk§•yùgm4zlùº¢é(øQl†–öý ¯ûµrÓ°þÀy“ž@‡Â¥8 =&ÌýBMáè`6; ÙNeÜ) Ž å9‘æMïbBØ„Óê¬2½ÛdMŸ„~jJŠ|Lòê÷Ïã™ýVd*Z?>3Vøò\ ”{ðý1¯ÌÏãKë!Û.”¼Ð?Y¬G’äóU­_ñ5 AÒ•ºÖ ,~#Ç`ÐÐlÖãÇ9E(ø5v?¬Ò ½ ë5ü¦  œfï©ÇßÛ.?Ý{ä$ î—äí5áÓï~†ùý»O¯ñpÉ/£À¥)m4¹Kvÿo¤‰Óø7™Ô-èøÖˆZ¿ÓéÄ‚5^¿EÀçóç ·@B—yדÐ7 $ôé}~mÇôѼOš¾lêW´ýè8µÉ/—ßOZ¿!fø7™ ðç “àºÿÖÂîr/x°fÜ¿¨>¾:rƒŸŒCõgû¾cÂñÃÿ‡0P+&×/ÜûÍ=xÀG€#   4¶&{2zÙ4›Ê¯dŠõ.øµS‚L £†Ù³â½wÅÜ»¯}oáê´Go™#šQt–<¨!~üÍ\¿®—³¦&æÃg¼ÂÝï'@‚—„.%¦“ÓMs³àø·¿£Ið“ÖïõúdÍ?\¡/¿Hõ.ÒàÉäî×öýödz'SB‚MÞÈO¿­ÑN±Æ¿Ý Àë'•(üKë|Påà>þo_ŽŽ‚€Ç…¬?>tç¯þöÓ OrÚ`ŸշhÃ̟LjI¿––⪪²ñ¨¯Ôϸ»L7¦ý–ÂKDzöœ´0j˜]¥µ»7ox^?é¹7?])Ý~ÅÙÚ¼­y ʪvÉ V¸búëè­ï_o‚.„½ÑŒµÛ¯}›!Áf“…¾ìøær¡¹5þ&?&øCµú7)ûÊ{) -iÝdƧÎM¯#­ŸœîHàûƒöø5}f)™‰Ò¿Xä? µû—fÛá†jÁ‹¢zËI/ü~UübZB§¨P½9^V[×|÷jmm5bèY¨®ñwŠ ÏOxªm ¦¨íë7qÁ¯­²¥Æ˜4~ÒÌß~ùÉʤ´´âñíðH7Ι&´§ùoÙ÷>l;ð!fõ§Y§= Yi…ìTÙ“ð#ó>‚lª b2ñ»\¸l­¬í{e½\åq~&ü•y@ï‘7t‘%ŸÞGZ?9öQ§ƒ„½I¶ÐÔ=¿@+[,ò?.Ç??Ãϯl”y~s‹Æç˜`ö ¶Ã.“¦OBý΃Âá=;ß[»lñ¼‘nvàFu‹êüOÑ›«ú*hŒnüÚ*Z2¿R£Œ¾õrC]ÿÅ»ÿ~ïükn rºñDy¥tÍì)bË1ÿÒÊðÕÚ'0‹?xŒt5;msOfo¿ðÇÈv$ˆÑä/ |tè#ç?æÈ<ÿ#üD€ÿ}~Ÿþ|?½W>nøDOw¥Xãÿ–1VØxÜ KÜøå²z–™ ù)Ímþ4¦Oæ}Òôî*ú`Éß'?iû4þCŸêÕ1ªk¸„­$ë{½dÅÔò‡Á¬ÉÏÈÇýcÚ/ A2Á“NÎ|Ì›Ÿ„=ølßÙ3[þ΄9íÙFïcÎ~ìZËûºãœÞMXáÿ¹s`:ú©öB½ ÇûÖÁü+“ÑJãš²GÞûäÈçó¸k7}·â_W.'MŸŒP=¢M­ñë·UCFyℌ€$åªîá¿ ~Øõ –ÆùI’“–Fö\úe‹WîX»f×Ô .šƒcï3±OAÁ%e÷Z„ah5©ÊV¨¬¼^~w9Þ~j)ä[ž‡úd°êÔò\ý›Ž[òÛò>Ư‡w9j­Ÿì¸äÎ/KÎúúßâßûÏ?6vÂq§§LËï»r£èxñ´ÒŠòrÔìÊÙ%¾×!9ižäâ”q½ˆµ=BT¯ZQ²áëµ»·n<€—H«'!Oš> ~êÐ1™ùÙø>×ö ž8jPØç¬û×øÕàðãnA@­õ3uYÝ!pÝ»yû•7ùnDjäß+ʡ蕧ÿ`1ÈîébùKºý1<ºðCrf$bq¿‹úzû¥7 ú$à™iŸ4}ú\ÛGxât„€à“rXØ Ñ¿~ã×nÙ’ð'G,JTÙ9™k·Ü/]iM€>ò>hüô…¾T‰ç\è(:N> 9\þÅ ͺæÙç°¸M†Äô‚ÜÛþ:íØßîxÙ&ÁO‚ž„?­ÆGÕªKT‡xâpÚ@@­ñ£ë3×øÛÀˆ_êzÔÂ^}ì¼üFCßÞ}}W³Þéá}Âßöùv"I44À?Ûw=¥ü ݉€\ìU>/I9ãº[ú޼^žPpÚõ™³îø¨ì«¿ÁSþ¤ý“À'ó>×gU/ñÄà´@@ãÍfÔø™ÞÕ"—N¹Æ¯íB¤†šM½"mMžê7~ŠïqüAžÄívÁÚ7ÿbz Ûxæ @Ÿ }m—k¤ÔQ½N¼uÿËù~†`4â” kêô;Ž‚ÿZü„=µZ´Qáš>‚ÀG =Ö¿6ÁTZ½1Cî à=÷g•Á}óÚËó×¹à"¤†›ê¤ïÉ?. ¾É2Ù¸í3þãë3?›ÔÍl”k¸TÊí“«²Äç<¶ýkÿ _aõ“åÜ!ó¾³gÞ™+ðÁLàËyÃ}¿#”:wdãìe¹ÝÄ•ÉKaž®;Ë\ðÇN­–>øàJØWùáSJKî^ûןÜÛ¦å3Ïö±Ã§4X޼róª!Ïo~ñzù‰)Oã~*n,O(Ïåy9q‰€àòäª>]ïSsÁCÕ|_õG7båN$ãÔøZ\]þ)tÚ¦q\µ WÇwœÔPÚ)“ÅôK·Ûw%F[² ¸ŸRøÂ¶Ëv>0êãŸÇ³sâ\·ZßÇÖT×ýTÈÌ4·+Œ¿ñF+®©þd€^áùçÖžÂsd–b™xù¦ XyûŠ~6âˆ( ¯`¹Ë £.?{åRçË9²øž#ç y?7¾=ú‰O.ø¥­é£î#?Eû‘¨ÒŸ4Y3_Ô4Áœ¸nEÀ`~ƒ/¤€=fyØ–#E·t+üeFƒ“4~¯ÌGÅÄ TÖ×Ðã[óŸ3R1äë“ÝXBsµyâÈ=0¢{„¿ À!=Ba|çüˆ#Àè€Æ/‚îÇø¹àï &hå§ qóÑž/÷Hqlÿxº4îZ¡Ó¡¬RÖŸQøSˆ^Rû {qÇlíPÇ)áh€Æýe>Ư墊ÚÐñäî¯ÂksçnÐod‰£ü(D¶>”S=ù7ØmèBÃC™‚€äuŽQN¶^uÕ|¯êœr‚B`ÃÜÞ 8=i·œ{]ÞQAÝÈ3qâ×WLý«…küqTöšbÕ'IÁ/ ›5E'&¦ P$¯¨W1Å'–#Ð5¬z!⤤5=ÝuÁ=µ§ºæMÚz*ŸÎ§­òðS#@ „-Z$‘Ó#H ®?z#äs29]‰@•§LÑöqeË£+ß§•gsÁ¯•’PÑŽ}J-€È5~6ü04DõGRu(C{ ÏÍÐ%Ñ«šÃŽ}T\ðk¬:¿ñF+:d !²p:–×’š½]c$rrb“d hüð1þ*;Nj×#àó‚÷ÄKÔ>B• þ®¯[!½¡B*ÉÄHkò‚¸?ùÀUÇCzÏÌP!°õ¡A¥Ø¬§K½/‘{ö«Àá‡q~*S|Ìá§Bç‚_cUßáó¦0’°Á®bÇ|Ï´Tê‘ÁkN ÷9ü>Ž€ÞÀY/Š©?^æðSrÁ¯±šlðúÔ ³Ò`kŒLNNl! Ô#·Ó­®_±Å§–#mTsø1ÖE\Ìá'¹àvEŠðy>AR7Ì•>ŽßÎAÁïm^¿8:8G@ÊS„cʱθà×X£éIüê[cdrrb¬SŠà…f¥â‚“ʈ>8œ*GH¥'›Mƣу6ŸhÔ&YSõ“'ž›àõyiÙÚ|\V±7zd&u~—ösTU~Ó;5åk™Ðº†Q3î|ü‡ËµO5§PËls— +7fÉ$¬ßòüY?ý –é –6ô]¨ÅÎ1™gDâWŸþņ`ïåù8’4Oüòù's ìH.CFíÛÀÄ”àŸ7ï k±»ø§¢(<àñyr)ØBR‚Õ—šœ XM&]X/¼=’ÅçMJH铚£8ŸÄEäLF#(wûÛ/ÃVX`Ì)ŒúKzà·ÛWUÓ Õ68Dl~3÷‰ßœðz}/æ™óþB‚Õ"O}ëºã»{aÁŠ·äW ÎO†ËÎ:¿;^Ëß¡cŠ>©ƒGý’ÿG烩}MzáVîì78œ°m_1¬+:³}߱籸ï¶GçÝößÎ[„ŒÆE$6½hwòá”<Š™+Iܘù ã˜ü(ôïÇ5F~Ÿ›•WÏš ƒûå e_ŸÉbNVsºj”c~ÀZW@ö%™õ÷é`ç&, MØ{ä$¼¿hMvqiÕgwüò©‡ÿþ›_½„¸ùÂÅŽß§_¯¯/û2ð«ˆ+Á¯uó¸àúðÂÄÂþâÃ7]l@¡¯ßšˆœYMÁïpsÁ¯ëÂî&æj¹—lÕú'(Ô>=ÁšóI©p¸ª¡¾¡”ýÄ÷8Tåo“ÜÏIÁ„ËõÅK¢vãšÙ“ >IÊí3fÈ=È7ù4qá/ >1¾EÀÔGÁ{-~Ô2Œ&Ã}#åIzÓo«nf¥f[•Tîh+ ¿Æ §ØäP€ÂLý•µ…ÚjG,6ÛOñ73nBüô~ZÂϰ(‚ß ñ¼‡Ð¢à¯¿÷‘I¸\bNÙ‹Ë4;}¸R9¹àW àa °«, ø‡eÄŸà'ȨÁUز/»iîixʵþ0ê‘ÞnY¶lºW?ežâ¾”¤q§ŸÊRk‚_ÖöMól ΃óôõVß‚â'+- øK+Š‚º‡gâ´…À®ò€àš³wÛb#¢kÔŽP{’˜š2ĵþˆÐÔÇ; «I¸Èò£öœ8wCSˆ+}ðךüHtŠÈGósã1©I%üñX¢Å³ÚÔ?,Mý„#µ#Ôž¢!O)z7÷0qœœ>²8ºÀ7(´&ø‰ˆB.†áÕmÝV72Ó†¢†âg¿¢æ 4:›qÖž@IDAT•5VÚ¥Áíñ€Ó~§•îGsh»ÏÕJÊkà䩞ŸIe³ëЉvaüzíØw´¤ÝßÃù¡¸Öå þ9ü6wýRâö“jOƒ@qٙƿ`„S™tv€”ñ}43Ó{²£5Û}ŒF„$Œ½—ãûTbFƒúôÇÊÖã™û‹¿†‘? ŸZ¥Šaþ’u(ܪPpôJM‚ËÏ™ ûà ¼öäk àg?š…z+÷~ñÝVXüývøÃý×@IE5|¸d=ì9| òszÁm—)I J~:øå+ÿ…IÃûÃe3&4»ÞÖ‰Çë…O`He¥SÝV¶¨_k뽟®Ø¸˜ÌýÁ9Q{ß‘åðì??•Ÿ‡ëF 6iƒá½á†‹Î:o+8ÿXð ¼ôàÀdlýÙ-^³Î7õÍnëö°®-=èNîcŠk6jODiº ~*.øÃªU:¹Éü ÑT>ý)=•T먣Ü]ûµ˜´$,‰®}•öŸ>¨ïÌ&Á°÷ØWm þc%ðò{‹Éy n¿ìl(X±qüý£å`½æ<10r{¥ÂæÝGš þÍ»Ø!}QЋðù·[ÀåöÀ/n½êpèÄ©VB?T´Šö‡Ë7t»àoë½·\zf—}Ó·_~6v´aÿ±S0ñ†+ ÛV?,Fþ¼m¡ßVþh\[vÈ¥’Ï]ïiy´eÏQøfÃ.=8¾BËÕb†é‡ÁY㇢,…w¿üêðË¿|ˆ4ô]pºlnû‹Up´¤²Ò“‘žÓ¡ O¦üèU[öÂJ¤ñXi$Ú¬äˆN ‰¨I¯Ú²5áaðŸ¯ÖÈï#kÆ›Ÿ~;ÑlNÚõ˜Á}áŠs&BqYe›ïýÏ¢5€3Dàú‹¦ÊïZ³ý|±r3TÕ6€޽ຠO‡Ì´dy˜ãq´j~_®Ú•5õ0aX¸tÆx°!m%â£_Nôï _¯-‚ƒÈ; ~§%ÝCòsáä᩟\!w¸v£…åÝ/W!NN8cÌ` ÕlR†Z¨#öÉ7aÓ®#ÐàtA½1yøÆ ÁfÆç¬…M»ƒ;zçL³¦ŒlE^­S‚µÅå:ü…¬PЧÍÿÁÐežâ€Æ/Ê|A ¥ÊOÂ^Þ°g÷‚?#e ¤%õ—k£ÛÓ‡O¬jU3‹K+a &ôY†Â¹²¥süÕu p ¸Lþ™„ 22MS‹šÿ"Þ_­Þ4ÎLr¹Ý@C «·e~n†,˜N7d¤ØÑ\…»Ü!™1É“à­Ï¿›Õ„ó¡Of¼·ð{ùU hex; ³N OßõYý;´œ1vZ"Ü8dp\ óeˆÑ`€~ø¾¹?˜7Ï™†+²„»µûÞšzÔ64Êï" Þøß ˜—fù©ÐàpÁŸß_"w (uŠÞùò;ÞáÒ³ÇË&âí%ÞëwDÞ×b°%7œ6j œµ-ºé=­Yrž·?_öÔ):UU 'Ë~ôÌ ;ÃÃ7_7]|†|ß­h¹ á—÷Ê+ÑÝ<ç,¹óöÑ×롬²õš+ޏ•À=£²i×Ò§Þ¢]{½©]!¡¯´5]ûFþt­"€_¡2Æ/Äö?r­2!]Zk Ø![ú¸}jý,í=F+Œ6O5õ¨·žù`G­™„0 òÜÌTÈé•dÞ§´5Efæ§ó+gž³§Ž„Ë6À¯^ýXÖVéz0éÒéã`< d’¸: LM²C }ŠšFÁSh¨†HèN;ÇÃ-0- GOV@9 ¼}¨%Ó½¸Ô2¤$&`§ …h âr{eŸò-è:¥ó&@á%›ÙÉRQ´¿¸Í÷¶äaͶý2^7¢08|€üÜÒŠ´RœêÆ͇sOÓÆ ‘;;±ƒÓ^Z´j+®·–­Û£Ð²‘…––Ú¢›ýFÖÖWœ;Qî˜1kûü-ò/£pÍ:Åqsû¾£²å#;# †ôË–ËvcSÙ²ûi¯ßçÚ¾.ô›¡§'(hÁoâ+x¹ÖLýD žAýfš¯ËXì<ôœ;á×hÞµ*ؤ¢XSïTÎÙAj¸6«Yq"ÃÅJ€LÜ3§Œ…ÜìÓG±¬²Yýâ3ÇÊfmb¯´ ~}çe²9^ÉÔÎiú”íþ·µÚ¶ÒîC'e3ö¿?]©üLS¬Ž•VÉæ~²@|ˆŠyÙé°=Ûï¹f¦’0òšrNÖ‹w¿\-óAüWÖ4€ÝÖºó£Ü : Û'+]¹Ò7ÛOyu=Z)ü—ósý :Ã)`àD³{{éÞkgɦþ |îßüR¶bÜqÅt%»šnå"0ëK?ä—a‘N™,b'ˆ†h8…¬:Ô™"çL a {pÙYNY¿ãË4sAªq5¾¯Êâœ||_…W.T`Äãáªòl•îâ,âGÐÜçÜÿÈ x`^\A¡EÁWг}³&ËæþÊÚCrÜþí?†±ƒ¯Un!­pç¡ã²ÆLŽz,mG ˜ ºFZùgßnFA²¬æ€™Ÿå§=Yn½ô,xäåd?¿î,™MÁU«ÅŸï>œ]@fú–iÚØÁ(ÐJÐôm¦¤Í«9-²ôõšh-(‡gîú!ú˜à(pƒMYéI(0ý–º‡f4PJ¶:SÁò$ߨô/-ä¯@YÔt«¯ç¢†Ò©ª:臲xÔÔù‡#èúøaýàË•[púßqÄ"Mþ£åš[)ê˜>a˜l­ ¼m¥wºÀéñ)ŒÈ2ÀÐ8Ø×6üG ÖWšÏPÀ™PGa^`ÝjöƒÎ÷iÑóŒrM¿2?ô&åêÆÝo(ÇtpþÔÑP‹æþ¿üì°w¼ÉÁ¼ý/œ6FÉÛÇÔs2R`ù†ÍÌü4ð9 rö£qfr¤Dy#It?™ñIÃ&3AŸ,4±[á‹ï¶È×É NJ;`Ùú]púè0qÄÈH¦Wí'zf2šÀEtf$?µ™¾å{[>…†ªjëÑo§lj_¼ºH6ý}á$—?Œ³ H3ß²÷(vjÖ‚ŽžGå‘„%kŠ€¬Kp¿Ú¿‚†.êN˜Ž{dη«YCÿ‡5Eûa7:7’€,ê1~÷ïm X®èÔtDSœþÆÛœ8,xøÁöå€&GX§²u ô!âÆûä£^+6?‚¡J+wÁÑÒ5@–J¤¥ßwÝù²·øs8¯œæñ“‡úx íŸË!ïÉÉ„ü4û³„ $í;‹pv€ÛãmòrŸ*›¯YžpöäC°ró^øÅŸçË3 îÁ©…äŒ÷:´Q<JdQñã+dm—9’7?yà“Uà‘›/jóÕgãì}²~öû·Ìòç6q©•ó¶õ^õCHð_9s2:3n•§ße¤$Âí—O—-Ô 5ýsÁ ùò38É7 ØtéôñðŸEkaÝŽ¨á÷—ý!ؽ„Ãé—ÞY$wŒ¨“CÃ3—œ=)'!ŽßÁKï~%ÿFC:TÞTî”–tî¡”jà¢ÁÜÌ/ƒAÿü³…šÚ>¤¨ào>) øA8L#Þ}¥;z9=àõgž˜NûÙsm¸%ßòÐãÿ>(â7œÁãôsë¢5¿€M{Þ‘–\vÖ«­˜ó àö¢Ð´˜Cïˑ̣)dáÜÛŠÕòØ'ÁD¥FœžF‰M‘ûçÿ¾%µ§ÕM•ßO–‡_ÿícY˜uˆ¬dêo+µõÞ–ùÑ£ŸhëÉDêp©q'Íÿá—>€k/˜‚“ dáþ Æf çË—¼žÆ$åDÄ%9r²kôÃmŸÔÁª£þñý;Æ[àÓ›bòßÿ_xk!lÛu`ë›/>w=r_Ž9FÐøŠ7žÂ@ Šmoÿ–/žƒýÇè 8%xÞùž ÿi±y§–Lý±‰`7P=~ÈMÊ[vYˆ}ÅÊ9; 9Ýj®³'Áî½=?ǯÕBŸò’ÀgBŸÝKNUµ ²Ùz5zÞÓ=9û´'ôé™m½—½‹í{Z臠%îÔ ë4¿¿‡%öጃ­8¥0Õž|:høD}m/.ÈÄ>õ®ÉÍü¬¼ùž# B ¿r,‘Æ)tõ0þ0êqŽ3Ó†¡³Þ8R²5@/šþÿŸñbÓ .Ã9K0†À?q~=…Ü¥é?¿þ|ÅtwÄÒ3,f“2yõÖýrFrƤq~ŠsÐYziMÀAðÜ3ôNâýúÎ0ã¿Ç#S¿À…¯;@çïˆ9Ö¿6Á„.M½›÷¥&Œˆ»¨}Ä;ü1TuÏ÷RKúàŠ}ËdÓ¿|ÂÿÅ5_£¦¿ 5~J&ô¸g2ùÈòÄà´D ª¡ˆBuÉr[ÒãçnDºj™YÇç\ðÇPáf¦…Q~s?‘½|ã³1D}ç¤ÒT¿-{Žtž±EŠç¯Ž Øâg]Ÿ¢/ ¼ð}@ÛÿÑ(3ôácûº.sÎ\øx\ÅÌ:šùã3qÁcå~æØ•UúŽŸÚ ÛöÏ1Ú'×ëóÒ̾S6 3¤_§÷Qàžwwš/–2¼¹×A¨ôÏH³›˜;kû±T~œÖîE@Ž}h=[ÁϽú»·ÞEü¶d{o˜Tx¬.zU~Ö×럂‚ܳÁž^ô¹ˆ ŠòJqñš¿´WÛK”âЊt¥®Ó¡¸´ßO®œ¡¬C@¯/£…v0Æ?­êGy‹ËªäHxû0 ð`\føœ‰…râÅ8{ 1Á,‡Â½õ’³äem£L~·>îXþ´¦Ayç¬fó)ùG€#@€@¹8ø!¾Ž¸Æƒå=mÌýÊ’½W5,Z÷x rÑ6É4m|ay)ÚWæ-g¢ >ÿ[¾VoÝ'¯;`26ï¯Òï´f%:ž¿x Tc\€Óq{ ËKátiAZxp¿\y {õÚò1øïñ¥ àhrHÚË·ãžü1XŒœänD‡Æú³× q¬ñsÁÏjA í+\púó Å{0¨ÏÎß)ç±|0uÌ y©ZZ²öȉr9 ñCíîºê\\ÚwTàºö´-mu ŽVìRÇà‡ç†¡pó¡CïÅen{áêw6œ#OûA}³[jõ_ø È kŠýR߀Jþ³çØö¦ü7\4U¹¼þG Γ—6ã@¬¥uÇÝðÒê@[u;šø'䯱†;§W.wæÐ€Cúà*<.¸©?<Ü4u×ÌIOBvú™&¯Ï­˜ N­:›‰VçcB?ÚpmûXúå >x`QPÀJ$ðï;ÏÙ÷£Áÿs:G@òI,¶0Øq<î¹à×A© ¸ü¬¿Å”$sS]w>[u¿8ã,$ìøªN¡ð§”ž À ³¹¿ ÿÇI ~4(rÁ$n<›†HMʇ‹¦¾ P¸÷èb%Èr‘Ä$Âåv×6MÝ£{œ™YvÞgÉÂäD÷"4~kü=WüÍQE`H¿Ù0¹ðNå™ßlúì;¶D9籇À§{œðú†@¬Z€gJ×½’ä÷4‚Jã—@8ØÓôôäûy Ò}4a¼xæÍŽ[d ú”ƬYò_ÎÙõhí§OøŸÚÇÊÖ!í>X°â.¸nÖ|Èí5&èWDƒîä?h&;Ȩþ)@Ï/¿8ó•o„ŸLìºè|Ñæ› ޵²ï ZðŸbI¥ñhêÌŽ‰uÖB¥?î?kì|äŇƒ©-ŽV{«¨©‡jœóítaåôBÅ—­¢‹ÁgŒbO€ô; /èù¹dF”Fuãú šß!øÁôÿƒ7^ •µ‡ÀãuÀüe7ÃM|)‰ÊÖf71 ?EŠ12^=TÕ5€Ëå°.M}œîâ¿cAžh‘ÿý^¸çË:p7yó É0À ³ƒä(¸lŒo½Ôýà¸æ¹âU/äÙª<Å9Ô ¡æ>¿Ïœ£úYà,Ô2[ÁO5x^¯œN7.q» VàïÚz‡ìQn âühÍ¡bÚf~Ö8ŸWÂ(r^ø|åV ¼c…†Áô …`±•N@›7‡xÑfMƒ«Î}ÞüòRŒæW Üç?_ß7ž¿¬–Tåi„ ÷¥ëvÁ²õEPSïDÞ%0Š «‘sÔ<å†$°€Ï“Ÿ­‰3ò?¼Kø–L5ÿK×ïIJGí‹Û#XÁ!ZÁ Ñ‹†g€F°ú*Á(9dþí68gÂÐ6ù/«÷ÁŸÖA­ÓßÛÊJáõ‹“€Vß‹FÒsÝ>üúC ÊW:›;ö®šï_ÒR¬ÅQ\ ~&ðÝn·¬áÎ_²…ž…¢2Rq13Pˆ§®H>¬}Ô7ºà“›á\&öšY§ÁÈA}Ž!+@4RZRøá9oÀ»_]'Z0À—ß×Ì|W^Ö—0ؾï¼ûåw¨Ý;ÁfÛ™[p¿iŒ)Gƒõ3|>+46Åûcò¿ùŸuþÕïlë˜ñÿö—«1˜OTš²¡$a8”ãÞ#øC·u_¤×Œ’2Ü%í:&—ÿò{àÚY“þ^\Z÷ó:8Qç÷à·›ú‰úu?Ò2â÷ëÉçUûP‹k~*ݸüLÓñx¼¨éº`Ù†]ðåª"0ãr¯ÙXð]Yí©Ca·Yä͉ÚveM¼þñ ˜sæh\†v„¼T,ˆ”Ž>½ÆÃ%g¼ ¯ø1>J‚£¥kaÁ7wÃ%ÓþK×ï†Ë6âœöRÈÉú¬ÖѾ.¨û©Sa·o‘7‡#*ªæ ÿNäLÔùo‹ V–`dÀË6A½! ö$žÕÆ^meú5êT”˜óä-Ås †4nSÊÚøáð³¯ag) }¬'ïå ì0Íü‘&Æw¼ÔýHñâ÷ë Q X IöèwìR¸Äàg ŸãÀ;Qè/]·­Þ V ¤£–/FG¡ ©VТ3Y)(üêPûÛBÃOpî¤áòÂ2ÑþCó/@“ò°tÃSèì°ûÈ"øÓûÏÀ¾#ÁžP!ïîèŽVfêläd¿ååW ÿ~WŠh󯦕Õ%kŠàcìôœ2õ ãÀ‡~=‘¨³±!ñ,(lØ$—ÿüøÞ1Ñoqx ÃñNí¹õñou¿'Ê”¿S›`Û§hüØÔǽÆ»²6˺Ud⤆Ïát–݇á«5»d¡ß+­g„>#Ž:Du@>ûv+lÝ{D¦“èV:møípúˆ»dÁßÐ0…~,ô3{ý§Ç„>ã:DuB>ûvK—ðÏÞE˜Ò"@ –o’…~‘}b }…&ìteØ 1ŸÚiÎ ùÜpß \QhaÙ"ÚÇsÝ8~³ŽPïã{?j\~jøÈÄIš~u j×(`MFQÖôµR³Éê`Â!‡—l€z\cžè¦ð?sìÃ0qÈO ²ú20›KdM_+¼dy0á°Ã‡KÖu ÿ„¥Ãá†w­•Íû¤ék)íBzêÅ$æÞwàæQ¦¨”?¯ûZ*eNK! ÒøEŸÈ5þ+ˆnz1™9ÉsŸÆô°rÓ^¨CǺ4\òµ'Ìûí±M´¤%'@ ýåè{@ôÝD¤‰aàtÍÆg¦`‡çó×ô[òDšzê§È¿«Ëø_º~‡ìÈ·Ç:ªÇ5ý–üÓpþ„Q8÷Á }\‡¢Rþ¬Üã¹î·Ä™ŸÇ'ØŠ`œ[Lv.øzÝû5ÿ¸~=ÎË_¿ç˜ì½OãëZKDÍ,Xµm¿lðxÍÓ×j"ÚˆÆ]‡NÊt{½ÞˆÌý~à•£Rpš§¯åDô]Á?…ï yúZN2}Q(Vîñ\÷µ\Μ¶îC€/ÇÛk] ~2u’à¤ètäÍ_]׈s£1HIV&¢h¬¬­—é&ú‰p༺÷4tipžpiTßGóü‰Î®àŸ"òuep5á}Dg¤ü³rçºnðût†€¨òèç¿\¸:üƪ÷ ~§£åá&bt<­'¢‘œü¨Ñ&úi pÝKϨÆp´¢¡&ÜÇtë}¢¡ºKø§0¼±¢%bþY¹SŠ×º eÍiìzšÍá—øT>B\ûR0ÌzA¦Nfî$'9òlv»}šÖö«¤õ»]^œÒç‘-Œö{°{vŸßêáAÞ£{?XÂÉ' XV]À4cï‡ÃW°÷x1 f$ü«Ë=^ë~°Xó|ñ€€4q‰ ¢ñ1~C·‚Ÿ š™;IðѼx9‚ «ß³NK´Lýô¼XJœš†ê•·p†zxÝ¥ÚÎiíb³çKFã>vÏ{Ý ~¦õ¼#Ïx6¢±$úÐ^!ÓMô3^B­¨ì>YæÇšàçü‡]þêr׺ê·Âóëe¯d&bSBÓùÈiÚyþ}Ñ'§¡q¥[ÁO0( –ý!Í‘ÐÍî!ÎýEÄù¨üå¿u?f¾uNh—!Ðè¨ hû¸*Ÿ Ì ßSºË¨ìþëZðœJKBŸèVÑN|D’"é8DòÞHîåüên¸8òº.rü>½ ú|ŠàG¯^øŠ”Ý þH å~tAs’v§ †ÂK8y †è,*λù=Ú@ žë¿6J€S¡Fí¼ŠàGc?üMàèVðGCË¥`:ÞpäôJUêÒô‰…ðØ­s ovºr͈‹ë̘T÷\}(ÿf·eÂÏ¯Ý ½R‡(yÃ=‡—pîQÓ7gˆ^Ÿ‚³!üW3í"¼ûƒ$øö–u6¸w² vÞ¦l[â¯/W7Âk’)nPÄ)^BÉÛaWž7 ëõp%KAŸL¹~Ïœ2R¹¦>è™Þ|Lë/ïkÏŸ‚u¡gÊ^M?Žc| ~®ñêkÑWttD `` ±ic†ÀÑ“pòT•CÿšÙ“¡p@n« í— c÷ƒÿ-߈«¿­†aù9pú¨A¸Êœ6ï93&¶º'˜ ÚágÏæ}ê<"® Ö¸‡0nþˈ¡¬æ¸úÜ7¡0ÿ"u6ùxÚ˜ÐyÒš?¾Xõ œ1ê^ÈLŠ¡wË`˾÷aú¸G[ÝÌF;íÃMì¡ÞoBiÿ³)VøÛúF 0 c³ðß+“ WBk1žf`ÓI\÷ßZy»éã:ùuïrA²E€K††)’Ñÿê{Ã//+ú÷΄5ÛüNÐTŸ¯8g"xÛ‰)!Š"\4m4†™ö(P¯Ü´&ˆKN‡Î¿š~åü€#"(Á/È5þ&üt-øC¬#Ͳӂ9 óauSÃg5qá/.»¾Y>:¡ÎÀ¾c¥pød9œ(«‚»à~þаÔpÆãÌ´äV÷iùˆ—‚Él?ð±L¦Åœ NwüwÙí­È¶YÓ¡¢æ 6úuPZM!%0›å|kŠþƒòΓ;­nÔð…‹†˜Àbøt·K¦rH/¼±Ù ïoóŸ«IOµŠp¸ÊQøÓFJt#zk‹îœƒÔáÇjq‘œ¤¦Æ+pµý£ý8ÆO¥kÏ?Ž–V(™I{:t¼L9…ƒÚ†“Øx˯:$—Æþäž ï/ùŽ—ÃÚÿCú]Có/D ÖÈ÷&ásü®ÃçhíÇ’zdÙC7ÏSg¡Þ%A¿Tÿ½4€ŽJñy±’jý^ªÿU¨±w” ò² Mùs0CÉ6iÄyék gÔ6Æý•Lü€#ÐŨûüSù¸ÂÏ ç‚Ÿ!ÑbOæyšŽGZ¼ÚS¹E6ù”Æ=ô·˜0aX蟛 WmU²Ò°Á¦]‡”óX8¨¨9½RuJªÓUeU»`HßóåqýÔ¤~•: ;¯)÷¦% €M{ÞRÎcáà šùâØ|0éúÑVøæ[Þ¹z¤ìfÖó;øåã¥CÕ±#øÝn/ÔaÇ7#ÅGKÊ;„à}œÅB¾0,ÝzÉ™Pt ¾Ûâ÷£"3yuÇv/ßs¢‰Šù€©ŸÏáo-üÍàœœÂÆŠÂüæ¢ãyëw”rz¥ÀMO“Ç6KѳùÍϾƒÊcGfSšÎtRå(Õѳ´ò[ie ï?͸ÅÁ¯=Ú–nø œ1ú^¸ïê­Ðè¨À™Áž#_ÉÙ“r‘ÿt(©ÜÑÞíš¼¾ë”.dòÉc~ízî܇Sÿ,˜™Æû]RëûÿÈLP'Âé‰-mƒûÈi¯³Ô²SLãû´ –¼(Þœ›‘ Žû-a=‹ÿΈ&‚$ b_†Vãý*p¹àW¡>t8]¨µ„ÓÇ j&øÉYéÙ~ªÎ Å¥•ðÒ»‹pº.£‹u:}ô`Ø}ø”U<¤Õ¿kõ¸èà˜ŠóñG¼Rž‹Ï謪= ¿}«;•÷´™Œ6p{š›uOy7v"ÿ»šÝ£õ“Ïö¸à'­py¡>ÜXÎøõ  Mnù_­lηšU¾ &ìÜ4Ö ¿_Õö8õ3´vüí–ÝpÝùSaå–=r< Fß?þ·‚¶¹õÃ¥Êõüœ ÈHµÃ–T®ñŽ@7" hüFÉŒ‚?c¢iÐä«6:M’׳D}·y/¤¢Sav–h¾sK¡Ÿ`³@zû/EçXK>Ÿ–nx§é]¤ûÇ«;ã¡¥ÐO°f@AŸ³aÙÆg;»Us¿Óüß}×Wšƒâž4 µÐ'†.h‚}•ø|oë¹ÿšc¸AÇK«ÐdLLÕâ§ OÇö‡›v£(öøšIžQ“|û×”4ü&3šˆk˜ùpý MÚCDq¿ài*ßßþ»¬ƒÿÔ€þ¯Îh@çÖÞ¯{.ÚÂMäì÷·Ï ÷ö¿oéA7Ðnú­´ÅjúbeÀO%>^¶!œÛø=ˆ¨w4(Ú>Î:áSùZ ªkŸ-šãß·à<NÕô‡K.{F¸÷÷ä}ŒvÚ‡›Ø3½¿'ïc´‡Ã¿úÞàë1öÕô÷üÅ1‹€Ï˜Ê‡L ™Ÿ'5ºüm5–ä|¬¬£BKÇD£ çC«S[ü¨oëX} CÎù¤Ðc¦·õÜ®¾&I–.áß#c|FÉÿêrgeouŸñÍ÷ñŠ€4”q.HÂvÌ÷~šK£b%áçÅÁ['¢ÑJa࢘ì8³ÀçíÜK;Н ûQ^or—ðoõœôÂ&®n´øQç?žë~7…ÖÀ(ÚŒ$Ib˳˜Þ…{Ý ~Ò~Øf5Š85˧i­Ÿ´}¢Ñn1)tGZþÄ2Æ$ðúpŠ¢?¢Z¤Ïìªû‰>¢³+ø7Jk_ ̾«xV?—èÃå ¢Â?«÷´×º¯Æ–Ç’¤~Ñ(rÁߢèu-ø•†ý²)ÈHF¢礣÷µ†½Œ‰6¢±wz"Rè´´(· Où9éø\ XÐÏèÎŒDÑÙüSìÜ wIw²ò»dú¢Pþ¬Ü©ÅkÝ|~ƒ.¤y.scÆgL₟Ѵ׭àW>ô £øá".<’j7áØ©€ñôµëiM´YÐ2‘‘’ ÓMŽYŒ—e×é)»ž‘›‘6³ˆÙÆtz_Of ú,8פ+ø§¥–³ÝÇz’½NßMô‘?F$ü«Ë=^ë~§@ó ºEàËß?7•' 1ˆmßñ ïmZbU·‡Î˜n?AAšŽ>mFlLF#äØ @Áy‚Yu/t8#»ƒh"Úúg&Êô2ÚÕ!QC}ƒƒáý²PãG~¨é–üDÑ×?3¹KøÙ/ÒPãOñœê~B} ÑEôdÚ#æ_]îñZ÷CÅŸç×¢àQÌüÈ×öÛ(VÝ ~¦õø…¾Ìf3n&ÈK³‚ ¹® ¼hRÕJ"Zˆ&ZÎuHnŠL/uTˆ~ÆK¨´²û£ rQë ¢ròŽaæ4”ˆ¢Ëb”º”+®§0¤qˆ’WCÜc'éŒt™ÑÚiù·,÷x¬ûš*\NL·"€~R…ì…ܱ!Ñ|¯[ÁOlЏp­-Nßb1ã":f\5Ìý’E -ë…ŠªÚæhôàÑB4æ$Ê4½D7ÑO|„›Ô$ ƒß¤A¹¸èP”—_î#»ä>¢‡è*ÌIîRþO” Þ(lØÔ%|„ûІ`Gº†çأ¿ºÜãµî‡[ü¾ØFÍüŠÆ-gì…Míøµ&øI—Ы½ÞávG¬3s' P«ÅV›l¸e%Û 'AÂed]pª²¶G5Òô‰¢¥šúô©lH#ÑKt“¶ S?àn: ï“‚ñ×G@Ù©«{\ó'MŸè zº“ÿ^îbQ¿¾Ç5ÒôGÔ¯ƒ^îãQåŸ×ýæ­§Ãå–¼>/­œ%·1Mûæ™ø™.•G?pþ¶ UK!{Aïs{K«ªëé<|U—nFÏœ&“Iøv{8Np:çqƒäk„¸žò*HÃ¥sÉù«;é“yŸ4ý¾¸|ë Ôv‘F¢“:(D73õ‡KW[ŒêŸ…«§y`OÉ|w/HOý ¬ÖÃá¾"ìûhL¿ÍûnÔô{‚()† u5°Ç6ª½Âæ#ÜiLpÃ6°ûj¢Î[åou_].U5õ’ÛåªP_Ãc¥ÍiqŸÆ2*ßg1á?_œ§eqjIð+´¹ÎãµN¡…tšæÃMþÆO3 P+>'ÑnG¡ïB“²[^6´®¤g®qÀ‰F hR+š×í60ƒH®ð]hž>MÙ#ï}rä#ƒAi"䡦Ÿ””öD»L'ÑKt“W6ñnjƒù™8·[€]'%8Yzv4öAbÂÜïF ƒ#Ü×uzÍÓ§){ä½OŽ|&Ñ×£üï9òq§”{åOuŸUFjG¨=q56h{.'#˜ïÃFà‹—3¥Æzyql5ë.¼·QÛÓxÂæ4²µ(ø¥“G—œ‘ñ³mûŠaòÈ‚ˆ8$“'9ÉÑø>iÒnú^¯Ùø#øÑïIf”6x¡Ò%A9 c²3 DŒ!:£!> ÊãÅ…Ýi²Œ‚Ù6€¼T $¡°OII‚ädÜHø#D/щ™Ÿ×ƒT\Kö@Y œ¬+€2Äh&ÃNOŒÕ¸Þ´G ìõ¤ ÿ ò<}£àEþ%äߪ þ u¥² ¦²wãL §ˆ„„è} ÃKùLœ‡ôLrÛÈ”ù7wÿí•{GuŸú˜XóuS÷é vDÂàG÷ï[‡§¤åsMŸ€ÑaŽaŠÛ.ê5:d1*,E¯e‹ 9þò›Ï?.jü¤é˦~EÛÌ©O~©ê_0ØlÈ@ÿ7v€ÐJ¶Šøšæ<2½`pzRPåÜV&-”=µÛ÷Šøi$e6u[ÓéüZŒ"€öÔ€G?×øÛ-E- ~"’tLùãüfÁçÿð'÷\ýÞÂÕiÞ2GŒDøÒƒ©$K‰$ÿyÓ4?Ow àw ÐÛy¤ l vÙ$ß7+²3Óå{"ùÇÞG<™ï‰6Ë€Ìúä{`“72ñÓïD_´S°à'ç?/.Dfáp…>£ŸóßÔéë¡ò¶ÜËiv N¦±&$ h”`Ôаᚑ¤ž,{—Û Ø~ø$¯·úÛO|†|—þQ´gE‚¿7š£ß¯²Éí<ŸÊ׸Zü¬N¦§ªêTÝŽ k_&MyêÍOWJ·_qvÄ&ÖÊ ¿æm†›MúÕÕuPÕè{Rj<>˜øt~':‰Æh'Ößía ;?ºp¦Î4ð5ùB0Á*Œö^ÎÏ”?ÿ³rß}ô”R÷ó²Ó ?Ïoi µÜ©Þj¡ì©Ý8^V›¿ÿöÿêêpú 9­0áÏÚ"—' €#´Ã˜u_šúIœðÔ- ~¢záôaÒê\õÕç«“ÓÓÞÂã›à#nœ3MˆDó§M#áʦú‘&-w÷‘RH°'ÊZnvF2 2@Öv™àC:BNôNyÃqRÒøé¤õÓ¼zjˆIØ›d+€ßƒŸèêÊÔ.œbè×ö½²ÆcfaÀù×FùwVî èàŠ3\±ÓK¨Ó&Ž„ìŒ¹ìí=Uö¤é“Ð_¿ó pp×ö×óõäÖd¦ ®ñ‡[°½oÕ y¶Jwq>‘‡j“'¿ßà}E¥¶gÉÒ¢à§.}œÜ¾ÿöG³®üÙ¯9Q^)]3{Š1j”äèf$„qœŸ„ÝÁ“U²™“´ý©ã !=-_KÚ>3ɧ!ý£÷Pòkñ~¿þðÔ±CÐUZ~{IJ÷µÄÀ‹¼Ó¬Úˆmæ.œm•{徯¸ f´@¡C+Å´>¸¿\uÂ-wº¹'ÊžÆôɼOšþþ[?^úñü/”ƦÚj[¨á‚AÐSª‘JhE>¦9qU•5Om  5ÁO–>JZ4>V2ÏY¿šÿî‚ñgN/õM=ë–?¾µ0eä aÏó§F‰„.5„d^?RR4o“Nãšã†( W4?zÛèÌÙ]C>»=µÄ€,äÌÇÆõ‰oÆ;Û‡J$½ƒã“öœÿž-ÿ¶Ê} LªûôN=Dv6¥r ·ÜéÞî*{š§OSöÈ{Ÿù|wíúËÞÛòý·‘ jCêšöÔ¦PÛBmLø½y¼™'í!€JËFšüùø>£½V?Ĵ>æß._»kÃú}Sf_8ËëõLÇ<)Éf‘RSì‚ÕdòK˜6˜ æ’ËëÁŽ€$œnçÅZóÚG+äÛ"iøØ{YØÞ9»®•}Kž[ž‡J'ç¿yÕl‰G¨xF;?•¯-^´@QZ³ý ¬-:‘Ðg4¶äµå9ËΞÂzS„O ΃<^»îà®+VõÅ7õ5øLj?hAÚÓ¦Öø¹àG@ô”DŸ4’™qPÍB?/âöÊWk‚Ÿèdãü4G½u2óÓ|:±¡¡–~üÁgxüõàQcäZhKLJ³ØlÉF£)ìfd<¨>W@mTÚ³ïà1§ [Bž8q€@^””DêHCMm]íŽc'µ¹nq‹²ðx0ÆgCcmc]måá½»vïݾåf!¥4{ôÕMu¨-¡6…¾k&ð'½ ÒH&ëQðo× _]Á‡?uÓØ8?×Ðg*ë¸÷nÛ¼·øñ@ùhcùð0øtýïš…¹ûÒ§JJ÷üûÅçÿüÝ<'G vHNM5ÿü©§g,ýô“·6¬Zy˜k|Omµ J»€Ç¤Õ“'MŸUÓ1uØø>W ½%,Ô‘Œ'ŸÑ€‚ŸÄOm! EÁOt²™zèL˜«;tBŠ‘–¯Xð˜„?%vÿ¬ƒÿHG,:d˲có¦oð˜ž8ºGàÜ‹/+ŠÙZÖP_…þŽ`š n&ô©…§±{j˜¶O?iúÔàÚ>‚ ç´è÷Ùv¯TÚŸ*6þžü¼Á»¹Gû%®UÁOÓGM3%*OvN7õêIð[q#ÁO|ÈøZèc^˜uùå“M&s2{ÜîŠå ¿X‡¬a¡Ë\&ü©÷N¶Zà3¡Òÿä3Ϻ›¦ÑKœNçâuß~»“ŽyâèÓ¦MKLLN>ŸñYzâø?ð¸œk|Om%Ö.0áÏ:ìœýîÏÍÿëœÏ9’U îÑßy1ÇŠà'N¨\郦™é‘ §iùLÓg{ü©ítî¹çZl ‰Wú‹= —ão˜“œxâè™—^~2É¢ØþÆŸþ´<†˜fm<íÙFíÛØµb‰“!ŠÆoŒ¨ñ“¨à©=bIð3ØGM9%&ôýgAšùϾôŠ+°ÍH— À6|ÆÀ÷½#`µ%ÜÌ:½8ÎE^¥DíK¬M`ç|G,|!9Ýë®Í•Y qæC¿ØÏ‹#Bg5K.ÃúèEƒ0—bÑS¸%¯ÏŸ?Ÿwýpðÿ:Gà™W_=MòJcd6¡Ád߯c^ÿu^îzeOð5(Ú>j;aS õÊrÄ|±1ñˆKøÝ_ÿ:…þT?Í‚K°ZÿKôsZ9!à“na÷cCùþ£sçR ž81‰€ÏðèG5Ž;öQŠq)øÝ’p3þO»õÖ2vÎ÷=#ðò_ÐØ«<ŠoŽùG ö@÷ì€Æ/qþ`J0î?zñÏ70pDü‹ó=G@ïÔ<2­]iÄ'qxüî¹+õÎ3çOߨ§òI®ñSÚq'øŸýÛßf¢ðïCàà´’Ó‡ [ P<G@x%é&ÆÖÿ7Ù1ßsb´Ú*Ëñ37õQq'ø%( z5¿3cÆ šÈG@÷üþÕW³°ÎËs÷1`•ä3¹à×}©ë›Á¯~—Ð-XþÙY¸(ÓìŸ7Õ7ÇÑá.®ÿo_{-ù/gÐM†±c¾çè—®CÙLžoŸ¸ãŽƒzç™ó§o¼×(Æ!¶íEì˜ï;F ®¿×ë½$‰bü“™#z3oëþ+G@?àXèŒ R͵}ßÇ,’×7–V¬-ì˜ï;F ®¿Ï'ÜÌàDá_ì˜ï9zGà7ýûh4‰Ê$މ6¦¤$Î×;Ïœ¿¸@ øAØGɸü4wÇ7O÷c&¸ vÛ»QÀ?‚#H>·âÛ‚ÚþÇ÷^}MLΉät€€$HÁo¹àï+õOq#øq|Siø@>{즛beAuyñcŽ@È|ðÁ¸ˆ•@ãûþ$ˆÿf‡|ψUÖ¿Ö;Wj"Ó/€7²ùÐm…‚ß?w_Pæî þ+H|x6Ž@Ì#°¯´b6Žïg71rüñ»î\óLqâÒº²Ñ‚,Ãpøj÷ÔŽÅÚz=V†q!øŸùëëç¡™?OFYJÏ>ôËCœ¿˜#ÐÍxÕÜ}Ax x,ón.þºè# y¼Š™-ZÜÌÄq!øqýÅ̉ë6¿Íçî‡PCxÖ˜Fà•W>HÄ:?‡1‹Sq3?ƒïcÁï¬JLpÁBiê^ð˱É%¸Œa"˜ ï°c¾çèJ¨˜ƒó›m~>…-ýøÇ;ôÎ3ç/^8öù ܱ/”R×½à¯9p„"•%(›|ß/çÎÝ @8ã\Ä;>IåØÇÇ÷C®ºü޲ЋÐ”(£"ÀN¢7äúÁoˆa0RŸÊÌ\Ûá²ä¤7G§hcW0ü:7ó30‚ÜëZðû$醃ÈÍü ¾^~ûídÓŸÍX•Œ"ÑËÀà{ pìÃ1,.øC,QÝ ~šÆ„s÷/dxÜÌÏ àû8@ ¦ªöRôæ·È¬ °á‰¹s÷ÇÛœÅøA`,cU\ð30‚ÜëVðW §.i6iîÜ]Ab³qb¬û*3¿ÀÍü1_¢œ†ÀÂrq+K> vöýÞ©eà¹×­àÇ•øo~QäÞüAÖžMüöµ×RYŒÉläf~ßÇ<>¯SÑö1pÏŒD‰ý\žBAÀJfæÅ0Í@K£Ï='o²ÚÐèv~ˆ?âB%<é VÄÄ«ÏuÆj+vZÕ{–Ãåñ\ŽOfù\€uOÜqÇ<æõŸû{u=·z_¹4Q)Bœ­_Ÿ½ÂyD±(øåﶇçMDa6Šü|…\ìóáœerƒ1 ¿y|ÓíöÕ–”UÿãŽÇžb?ó½NðT>éŠûÃn§sñ›/=·Y£8ô´é­Ad‚ž¬tâ÷ýb‚Éb™Éê?:¯úg¯à.§g„ÅýâB@IDATlÂ#€šÚÆ ¬ûËäþO7Hh⦺/ù¤#>¯wÑxšÕ}½ÕûÖe¦ü ¬o_é XüÂwγyÓ…{Œ&Ã}>Ÿ”C&ž¤«/59A°šLʰ…Ûë¯Ï¿I‚Ř4$?÷Ì΀à¿ÇìÕUÕ4Hµ ôï1<9÷‰gN:9¼~û+Ë—/h@޼¸±N@ì1觘>ÕmÃôé—%äOy·Õfýi»õ›}‡Ç­ðš™šT•–\ \àº@ ­ºïvyþtjç‘W>ùäŸT÷c½Þ·_NLb?šL†uþÏœ]áû`ˆÁ/Þúȯf‹fÓÿI>_ïá}¤I# `Ô >B‚ÕÒÌ„éA¡ÿÂ;‹üÕ¸ýŠé–l ž'ö;{ 'lÛW ëŠdoßw왡Ó&Þ;rà]ïýå_!K$Y Ö8”>mºö§?Ÿ•œšöWœ¢šÛQýßuè|´Ô¯eg$Ãm—žk´“¼JF~ ¨~ÑKt‡Tÿ›™ù;ë.¦ø{zV÷}䌚qæÝH…Vë}Ð}ñ¼e ŽfQž>ÞòÙ9}3ÏØ - ~¢Éh±Zï9(OêlL_ÍÍþâRåtP_>¾©€ÇT¨¥d¤ßŠ0Ðô­jýr½o¢ÏNô†Rÿ«ë¡¼ºN.i£Áýs3äcþ/~`ußž”øcDA«õ>èj|¸¶4r­3jMðËZÏ•wÜs:*e㔽 µ²º”VÔÊŠ¢ù¹½Zs˯Ä%þz$ô:óüK¨á ±~­i?jmßê§SèJý? êôæ£Ð7 ðç‰#@uˆâÚ_põT÷µVïC+ _À±O¸c_hà5Ï­IÁŸ”4“‚óà<ýæÔvp¶ÿX@ÛÏÏI“‘7|ÀW?Q=¢ú”Ù'o:2Næ~ržÓ’¹Ÿ ~¢ËFt†Zÿ+Ã[ý© ßgXÄûžÕýÔ^½ÎC,´VïC*ŸÊ£CRóˆ}!¡×<³Ö?ÑcQ̧ˆ|4G5ؤ6óäñ†/XÜâ!Õ£$›E2™-Ô“TküZ©ÿþzï×ȬD'Ñlý§i|ÿÀ¼Ìx(VÎc°º/Œý0;ÓøµRïƒàÀŸE’扖}»A0[¸©ŸÆ^k€è1b þ\ Ã4mrÃW|Ja ü üÀ@jŠ]0˜ŒÔ#¤Þ$5€Zòr–ë}]¢“èÅó Ífq¹)N @Jb¤§$uÏP]ÂÖäô¤µzt,zñÙBtvõWlŠÏ¿¿áDÐ7óŒ­Z¸¶º3ú¨¡“Mž¢ $bìý ¾â²*pºüó—“qþr¯Te½žVTRxWŒsþÿì}œTÕõÿySw¶³ôÞ–¢ ¢€!ÆšÄh1þb41Ö¨ù“ØbOógPc~1öŠ¢€ŽŽôÎÒawÙeû´÷?ß7{gßÎÎ.³Sß›¹g?³¯ß{î÷ž{Ï=ç¶÷#½ÑàT°‘¾Ÿˆ÷•£ƒ¥•aƒæµëéã%k¨¦®!ìóDßD>`ÙØÖè‹o74[d¦µ÷â}òdµX³9\Qù WÄrožà Ê=_£1âŸí‘ÿͽ‘Yûì$ˆIF<^/o’}9J–­‹2±ïðÑVŸ·çQe¼µ4@–,ŠJòe¹oݰ÷U¯c4â#­}F”G‚‘ +ï«Ö®Êx÷&k@Ï–ýœ/×Òüe ƒ­V aÔÿ.:“ºåÓG‹VÓ‚é‰ß\¾à ½øõêRD×ÿúrÍV‚ÒBåQ˜—C#‡ô¡«'|'%Êè‘—fi÷l<Æ€7H¡IgDgœ4°Ù{±^ÌZ´Š7!òÑMWœ×"¨òªšµp:¡å¸"ï*ißЧÇbQ(/ÛE'èA?™z6÷Æ4á¤ÿvýŽýô¯™ é/w\Ëã,ZŠ×g߬£sN¢á¯ÿ.9ç¼§YSå‡ Ð( ߀Ü*eMã3bHvío’ÿ~Ý[Ê¿> 4 _ÿäkÚÀùåíÊrÐY#ŠéÒ §jùõÇ39oºÐ¦œüìpù1úÃsïѯ®šH sèÏ—Óž:kµòÖ˜Ý:ÑÏ/GyhSèÏϤÜ¥±#Ó§6…ú4ÞùŒìK±¿´IÉbDkø××?%¯ÏOÿsÕyÔµcæ}y}Î×TÏ+þýä{gkeaáÊMtíEgq#8ÒÊM»Éåth¾—>X¤u+Üû³ïiƒ]ÜèÖ+}|q#Ë‹Ûã£-¼oÀ{_,§[¯¹€´¾æ ¯0&¥‹Œ·†nc ùÒË[*å¾5VÃÞgŦx ¨–o‚»°‰›òØ.Œ¤øÁ¸ʈíw÷ê\xýB¦ñÁªùü›õ4îÔ¡4ảZ¸¹ô—£þ÷]ZÂÊù¼1'ðò¦´jsIPñã\TvPô¨yj {ò´_[ ‹Ч[GÍ[°|ÃNBÅ «ÿþgߥ›~0A« aE=òë+é`Y%½>ç+ÚÅÞ‚l5}oÜH}bú ï0ØŸ½—Œ?U‹ ®ý¿¿ñýŒ­«o¾Û®uWë)à±Ø¤ñ{Ωƒ›±†ô£‘³jón‚â<ÆàÂ3†kï¼ÆñöêZDeÕ´xÕúÕϧpã#DzúõèÌ^õ´“gP`SœpßW×6p|ßПn¾\ó¬lfËðµ9_²§¥Î>y±Û1è"Þs°œæ²'Ö§Ãn#{bz3n¿dŒÞWfIÀñ_3ùL çÍi–ØÈ.`] 9kTm‘}˜À·ôü´Ë ±‡û÷ý[Pwî¯Yð­ñ ™>À„;®»(èq9eº¢ª–ÞŸ·‚.wJ°¼}ï¡à;«YvNÜ[ËÏÜȆ‡@X΢¡¬S¬˜YY]«Ýîݵ#å5*ý—f.âU5to¶úvÝvúhñM^Ð0AùÁÄÓØC·†Ž«¡QCûÑ%ì@Y\³e-\±‰F êEŸ~µŽ²øÞøÑC¹lÑÂjKÖµÚø—f2ÞZJõrÖÚ;†»?ÿÙιµ5¥'iŒ)äËS ¿%jòrŽa0Ô®J&IéiWe ×;¶âñö»XªWO¥¬Ô0ðé„þÅ3¸ý:óî}û ¼:+Ç]â1­be-*»¾ì>…{Æ{óiýö½ÁwŽw²û`™öJ–ÝʼÔ/°òì›_P¶ËA—³÷–¼ñ¹¶¾úOØŠÙ/¾¿Ps±C¹v›öþ­Ø´‹Ü¬Äû³ò=VSOUµuÚ³½‡Ê¹â\­u= Ñð%7dô´håVmËÚë/§Uì°ÀàÍ!t¬ß±—¦žs2uc 0Ay£ó&7 ê¹ÿ´Æ®‹pß»=-ÜBׂzåã/ÉÉÊþZîV)­¨Ò:"ެÐþ£ÜBèÆËÏÕŽÿýx)+1;Ýví$êÉK/Ãs'2jå_pU :Þ¢=ûÕ¡‹[È(Ýý¥G©;/q¬5‚YþAP¾èJÅå4’Ÿ°ÒýôëuZWv³ÿ*kê´F ø{ ”WÖˆKMv^³”–ÙÛvªÖ …ÂA¶6ìØG_·CkÀ»ñ&72Ñ(µ%ëÚ müK3o-¥BÆZ{nÈûõÕGOçêÞ Ðwcï. ,ظ–ÿ£@ÀˆŠ¿]ÉØ­¯øz´\´ç»õA¡ ÜËqeQeMÀ*9õ„¾„þÏ]<-ª’- lï+*;¸<ï½a*[ä9luN}íÓà*i'” LŸøÏlzî­/]' Â,²ÁŠŸvùͪ߶簦 ¯˜8F»¾Ž]­p‰BáŸÎÊ|ˆõ VnÜEcØÚ·ŽtÙÜ—ÿcî“…~þéÃDTÚqݶ=š…Ôµc æeŒQ©Ã!¨¾ÁKw^7…Î?íÄVÇ|ÂVùÜ/<Ù:i`Oê–¥ ¶¾‡Ò@#Œ9 ñíf^[c–ÓÎ[-wÕöY°òâK˜¸ƒñ;r+pÒlaÂ;PÆ ‡ØHEÅgŠœOýø–>ÜßÞ¡,ä²Ì‡’¸‡†(Ð.ÑÎõž/ܸò‚ÓxÜÊpš9÷û¿¯YáÚ‹qü7rH_MÇž2X+?YÙëé’ñ§Ê+>î¶€ÒOÖõa„ž§Œ‡¦ÌüתÅß40DQ¾4ŠRŸ#¹ú£ª”K6¹|­ÖKTÕX©é!‡û¹Wã`¸'añ®awh{„›_¼~ÌÛ®½P³|ßþl½ðÞºç†ï‰ÇÍŽ°ž†öëÎîÌÓ‚+âh¸nQ"¸XA½uŽzwíÀPµæÞîÇ ™UlécP<TJh ôfw½ >¼x‘ xÐÏŠÆÃò »Äm¬AÈu¼ñ·^s¡ÖuQΊü©—çhÖ÷—ׂhë{(oPŸFþШéÈžA'èNó—oÔT.Z¹YSðx¶y×AÍýϬ%âU­A´÷pE³ïƒc;‰Jîb‹²Ù×QÇïcO×~ÝìŽpã[ô1A†1¸5”ªïäbm#Ò”*¼HhléÝüx†Á¯ß;g¤æ‚Gcðyö‚ý¿i—jòŠçí%ÑM¡ÿN_Žy=jhœª(ÞãrsƒW؉¬‹ïÃÓ\ÆÃ%9j¹ XBïùÕ`ŧø¥âÖFRüH„‘‘Y<š{R7zX(q=0… ‹.z¸î¡ÖèÄÓýþ¸«tÍÖêXtó‹÷Åýï•ÕõÚ 7X˜!JßgK}üáH?Ò]  ƒÌ‹è¿>ÄËg«4fØ@îÓÜÀÝ ¹<+ŸÐ¥î¼õêWÜç/V¶ XÏX·}ü¨¡tÙy£ÄífÇH7@ÂGèJŸÂ {m}ß½ t·ôa¯ðÜŸ|ÖzîyÚñ xÜÁIÅüÉrÄò7ÜÐ:ž2C8í¤F²ÖίózT<àñ!BqbÜŠ‹å¼- t‹ÁK„†© u\6àIÂN† Ñ^¹q'mg¯Ô?8O¼<¢Kàg—Œ£»ÿöO)XñC&¡¨¡1JïÑ…{Þ–¬‹Fw[aŠgi"ã"9MÇÀÀV½œ5=3ðç2û \ýY-v¶ø]»fÛð¬µÔZ†g¹‰A¬ÍïáQÍ |<„=ÈC ŠJeÉê-Z¿$¾ KÞ 8t*+~XÖÝ/Üüx¶™+ʯY¹¢±+hí–Ý<> ?¬ÒaEr„uW0P |Íãé†<ï$¼:mX?nœTÓ·ë·ÓiÜçŽ÷릹À1Ý ™%4Ä2ËXr]3³—®ÑÒ†âë¶7w÷Š02ùˆ¼$Û‰ëpG4|ñÞË-e·ø>­›i)ËÍœ¥k¹[e°&‹â;4‚s™’ã/ЗŽùð%˜·t£á)a$6ø`ÆDJ˜•ðæ'ßÒ² ;èT¡­ßmᡯƒ·Ð(²Çˆ~ >»éŠñÌ÷—tò!_†ýòòà³HãOç÷öñ`=AÀ2º»m^ÿä+nøÎÓ¦Üa|V„Å:øO„7{]ƒ[Ðm"¬ö±€<3NGS;sÍ–z•§ÞsýTm‰Y¬†6ãÝluºè§ FžAs =ýß¹´fÃÖ¯üõñÛ8œƒüÃpx vÀèÏTúƒrÏ| ¨Ûo»ë¯'Ÿ8è„ãÉÿßßü,8vå—ŽçÆQÓø ç¸T˃(¡øÛK®‚™2úülo×X¾o+¾TȺd¼5L ûßmÚ±öågý1¿7¹cÝßë4û1Ëÿñ ¡ŸâÞ˜çŽ)wûŸjõeù bâ¯Á"Ž:öõó÷{unÝâ×Ç‹7¥0`Q'Bé#lŒCˆE!£mMé#|Xm°¨c‰áDCà+\%þé£<À£õ7îܯMúz‚ÂUéëÃK—ó*ž'¬ÚY.;wÈmwÒ¢Qúˆ2.?ÛÃ@¬ß·W*d]Êx[9ý3UQƒ¿b±H‹?z(›}Ùd‚5»mü Xb ÜøÝ:Eîº6~êÒŸCtgäÙóâ-e•UÚx @<‡§pI:>‡x¼‰ î £nÌŠ0ä1þH ÓùOæuªõUkƒ°¸±ÙЧïÜÙ[ òk Ó*þËРxä>”¿$s!0éÌᄟ¤ö# Wü˜ñ!ɘH>_ê}µAkŸCY>ìªõ~¿èƒ”_6"`Zm‰Q¸‚º¦‰ky”¤;‡Ë›ä_¿ Rº§[¦/sP-Mn~U.Ü׌7­â?¢suvie©Ù¸"a`˜æô«G_& "4 ¡?ø)ànKá¬Ô#2 ¯’ÈÀz‚ºòŒ³’Ë‹Y±L7¾yRÐâ— ÷Ä7wM«øZñaʦ¦‰0 ñ4^Î7ÜÂ'~߯Õ1ŒÄ´ä¥U°#Vwa ªXªÕ ü –ò‚µ%þÍ»JJ?Ö¿5Ì¡¨4F¤ÌáÌŠÛf"ÌL>š¶ÿHEÓà&#º:±Ú²õ;µ9ÿgóšó˜€µî{ðz›x;Ö£ÇÆ5zÂó^T{×Õ7hëîcŽ4,öÙKÖÒVÞŸòö“ÎeLDšn¾r‚¶w»žwyž<Êt›Ú`¥9+¯KavjoyÁ¾Øu²”@XŒkò™#hã®ýÔñÓz±»#&Š—ØI‹a+¬Ýý°o†$c"°g׿ӹ:ÊÒ¸ShëÄßÔ2&§æäÊ”?”¦‡çÔƒ0÷=›-Z£ÑŠ»´µç—®ÙF³®ÔØÃ.gXãËÖBY‡žc7»^¼¢_gvß>úÒGZ—vÄÃZöo~ò¶N§Â\^ôg‰¶Žúh^»`ÑÊ-ôïУSÂJlbE7Ä%Z±«ßÊ»µ%s±Á#ÿúˆW:tQÿ^Y‘ﲕôæòv¨Øêt7oV4s~€o¸c?à.¯yó ,¤_v8ø±C{†¥vÿçªóµ†–vCþK弬³ X¸é@Ñ””}làºÍpkxœ1b¶Ãe_ÞÅ–?6, ·kgkßËûID@UÇ‹ØT‹Tü‹xMiñ7³x Zñe9^lœ‚]õíÞ´@ Ö,‡Ÿ }?<úæ«ëšvSëÛ¸ñO%{ ÐÏ+<¹¼úš?ëäÁš;±`Q‘a{ˆ µcUmïöð áF!ïØ&‹éˆi‘Eù4eìH^æ7ðKæo?Ž<& ]Ã[E§E[^BÓÞ(®ZC¹6d€m÷ÆiŽÆeº6'4-™zþ}ÎÇ`?¨Ýï\©X$*ݦ´øõŠß¨Ï ÞÙì^Fx妭Ÿ?\bÍûÐuï·ñB•¬€«¸±€¾øy_ ÀÓ–fņDØ•q|»n‡¶·,yììçååy¿øv=É묇®R¶»ÅfCpnàuÓ‹xg7ÐèûiqžsÊ m ܤm­¨çIž'½Ç˨òß^4¢)/9Ü€=Æ+A𦡻 Ëç®æíµõBÞ‰y…r•<&ôïóÆ<ÚŽkœc[.¼»v£ËÈ MiñãÁn‚°Ç¸ÑîC¸øïýÇ;Ún{O|‘šn¼ÍîK¼iÏ_^›«m¸£ß¿^ŸÎ›¯<–rWÀíO¿¦ ¼»x\`“(zl¬‚Ý×ÎÒí<(¾…¢¸øÜS8üOè¼QŠ~slÖ‚ÅϽGw<ó½û/”%Épèå_ï±1£2myÁn|h ßþÔëÚÈ}»ïóä«ÚN’íÙ<(BVåkI@@߿ϖ˂$D™qQ˜ÒâÇà>Aps‰°ëÝ“¿½Zc V¿¶ÿ ¼ùñ¨;/=|å§k®Jñ-Âüç}×7ûtH~ð.×\þúñúïE>®C¿Ç€XQ »ê ¨ónk«It;œÍcð“d Œ,ÿíE(–ò‚F1ʺ¹Ð-zàÆK´./ þÓÓŸ}Uð²0?»Ey >”')G€§xNLðÆ< Ĺ<ÆSZüú~o£)~}ÖÅ­¿éy¤ß†*ýHÃUúúïðL(}ý}yžz0›;‚Ò-Ÿ"•ùÐ\J_ÜUúâ¾<Ù+vòüý`ÿ¾b—ýû‰È5SZüÕuM«âåÌâ%“FðÆ5RáÆ‚`ú[]ß4Ø3d?ýsN¦0¬î]§ó#mô1ú÷'ÿ¶ö@$ßÉwÚ‡€)?ö”N•‘$h ýHõœì€{»­÷å3‰€™à™ãƒüjýûÍ6ŸÉ“˜0«ÓØÁÍ:j]<“G‰@:"€í¨9l¦l· öåQ"ЕšæïËþýðÄí†é¿‡§ª j«ŸZ¼#tBÀ'å?²S¦E‡€ìßבàSÓ)~/¯ 'kÇK’džq!Èn3]ñ¬Ë£D èßgÇ~ _¡Í²¿Dq»a:_¡W·ó7™‰7aÝõ¼ñM9o„RÁ Ø`>~¬ÄƒTÈÁ{ æfó’¸9¼š^/êÓ½c¬ÁÆýû’e´~Ç>^F¸š×¨%wcÚcíe3KúãhôzçñÊdÙ—Up¨$îž_õO¡«Ú4¾Xkš<†"`:ůŸò%7±-¨‡ûM±Aͼåµ tx ²Yxü€U! AmÅN~^ŠJõ©äõû胅«(?×¥­ÚwÞèší{Lí c&ö/¾ùŽ*k8µ*eûÈå®"‡¯iöDûBmù¶Ûê Ž<ªµ:µôð^çñÂF©NKN}Ìò±<ÅB™.û¡Øéñ˜¿l} <ðܲ,¥ŽœJ%Ù¨iPqè·í¹ö’“¶«T¯ºe!—ËÂi²,ðÚ! ­Š2OœËcü0âwê¶‘Å®sÑ ¹Ÿ½°î±eg¯ŸåtPG^Æó‚-Ðþ ,¿‹GjxJâûóVÐüo7Ò§ž©y’=Pq=oôó ïRv´ºžzW•Щ囩OÕNrzãSÁ…ƒ¯Á椒¼þ´­h§¿ðF?âM‹àIvúÃñgô{Íå?:o””ýæ¹Ü¥\4PWÛ6*v­áãfr(MS(›Û•[Í¢CÞ!´·þä@Yàe¶453ËÂg3:x**NklÊúm®ì/ˆªbX~Ý*æSüì2„]ê¢!(| ’Âzö,\Ísç­Ô•7ðHÆ&4hP` "~èF8ÊûŠ?ûÖ<ºœWÛúýb“œhÒÕžo°cà»_,£¢ú2º¸du¯Ùߞϣ~ŠAG7i¿9=hiï œþ/8ý£“šþ¨â±ƒ lÊÔ^’²ß±æx°'Îz˜ÆfÏ¢N¶ÝÍ_LÀ½ík´_©·/}Ww1—…úŒ, žŠcXékÂÍUäʉ·T•%rd#Mµˆ‰ ±³¢Æ fh©ûx°“5déÙÖ’"ZöpoCé¸h ïpç¤"¶òy#»¤]xc‘òŠjVÂËy3?MýT¹÷CÓ-®ÁÏÙ{æSo³šˆô‹xÌ~D÷PNv`GJtwaÊi[$e¿9:¡xTÖºé$çG)µô›sÈÝlùŸäœE<Ë&Ê×ËÁÑü¬ùŒ¾iEòûZB3!ͯM©ø»å³åpù±ày¸¸õ0ÏÍJ¿®®ž¾Z·S½ŸŒ|áøiëxÂÌ‚E«¶hü‚oð+ 0E £÷“5¯½|ƒ/ð·x忏¦¿½|ýýnEA´!ÿ"ß3Yöƒ@ñI(_¯Û®ÞOÆ@>=‘œƒ'Ì,X¼jS”õ múLœËcâ0¥âïÖ±0ˆÈñ …ïÕ¬ý{Q O¥Ëq9‚ßí¼U³Õ»sß¶ú½qqùƒ]Þ1¶nŠyÊž‘ üUqWG<ÓoäôFÃ[W}÷ —”ýæè†âQ]ï¥^<ªÞ¨Þªj=i]æÏocgçù",ŠMöï 0x4¥âïZ”„äÐÑÊàyèI°…ïñP}}mÞ}犓6O?ô]£\c ð¸qç~r3ß±Zýƒõ;öj‹ó`ž¾‘ üa¡x¥ßÈi–7Ìt¨<¼ü‹|‡ eªì Œp ‡ªÍÓ׿g¤s¬! ðBé\ê–-9›}šVh÷¤;Ü›Œ”éÊ‹)^ŽK›L©aëxßá£aó'PØ}<¨Ï£¹ùò2´VÅ’°ÅyÂ2ÑΛ˜çoåU±l.øÆtE¤#Z”ñ”Álo}B牖Gýw˜çïb>ã•~}ØérÞM§øwì=¢ÉHhÚD¾g²ìë1 ‡‡“j¶8>îhÏ1ÏߩԦuYPÿÔ >Šòqð\ž$S*~ 2¤o· 0vî žëOàÚƒâÄhöú†v›Õ“E·ä©þ]#[x¤_%Ï>ßà鈖æòTGLR¿Ëf>ã•þ¤2ž¤ÈŠòs¨SaÀë…)}Ûön³È÷L–}=(áðȲ„÷–è¿Kõy/œÎeAQÕ â·¨Rñ'KÞL«ø‡÷b´Ýâáˆ×~&o£âohp“‡Gö'j)ÞpñG{<ºyUBTÚà鈖Xå0žkïGËO$ßÏx¥?’øÌøÎðâ^A¶7ìh)ÿ"ß!C™*ûA€ø$6Šß^ú¸âyÓµ,Ìy&«;3OÔðR¨®ÀÖ}~<±“aµŽ€i¯.¨C^Ž–2¸û·ñÀ==Áµ'Ü{$‡‘ͱ(P}ØÉ8WÑha¾…«ii/é1ˆæûöÆÏ÷ã‘þxòc´°† èdi+Ë~-Z¤Ï÷L•}Ž=:w»§nxXÓü³nß['®å1±˜Vñ–áÅM•ßßnháî=(OÌãç ±hÆ1tTVà;^®~Ó)þ8¥?ŽYb¨  xwÇ>Ý[;C¶­l>&JÊ~óì’x4ÇÃWªÒäæW¤›?©YbjÅ?ê„þ<ï=°|/ƒ-ß°+žhåC×cd¼Ÿ•¨yÔ>·Qø/0¢¿Éb &.Â=fjô yñH„0™öµ³Nä}Õæ:\XÌJŸï™*ûA`øDâ¡GÃç_>ÝËÅõñÁÃêûI8šZñgóÔ·sO„iñê-¼ò]ÓJfÁÏj-~\›†˜U(?‘†hùß›(夯)ýÑâf†ïôì̃\»k¬"Ÿ?ùjmÐëÕ”ï™+ûú<”xèÑHýù1ßó¸zsvó¯?ÿŽúÝ©ç*s80µâG6:¡u똊mn_›óµ6ÅOda°ÀÇIé[-<á÷m¼Þ~<ŠZðkx'^¤Øx[À˯0Ã…Ïô‡ ?]îM<}Ùw§Üs¨œ>äm¦ ù‰gþ‹°CØM{Mă•÷ÉÄ8XíÙñ€ƒõcüꂸ0‡@x¼Õ%Á`ä4¾ É: ¯Á’{œâ™rÖˆ`åw´ª†^ûäkž¾×4Ø)Úh°˜Î?¹ˆ…Ôµc>ýdêYt×O§Ðí?šD—Œ?µYEwÁÃé®ë.¢;ùýË&ŒÒ¶ Æšêw^7…wßkZp(Z^Rñ]Ñ9ShØßfòfYrõLƒÿô"òêR:ù¥/¨ßo&‹Sk°k¬Ù;t¢!ÿ›F¼Ð´ð–­°#|e eõÖößHEÒ>NôõŸ;jH0˜ÚúÙ×ëƒ×±œ\9q Mtpàa€ìCÖäÒµ“Ϥ{®Ÿª••Ÿ~olpìÁ5“Ï sF¯šþxêÔÑà³î ¦£SŸqtþké„q Þ³9òèÄs¢ ~¹™éô+Þ¡Ân§jÏ{»†Î¾åƒmÜ &Una_æ÷V«åq.ÉA -?óçZâGŽ£}°ˆv, ű'¦=Ëé`iu*È£#G«éù÷ЛŸ~Cƒût£áSªóš£Ùóðù7èÞò¶N4†¯1Û`õ–š0ꄘøÀÇÑXmÑ|#…Ußãš›éà{/b.¹z º=ÛiÃíWÒ¶Gn¥ÂÑçRѸ‹´×s ¡½J¶‚"ñ¹vôV”Q鼨絷4»ÍE,i‰&>3}súðtÚðA–—mØ¡m½\ÍëVDK½ºQ¿é›ï¶iAœyR1]~Þhò…L-­áÝ1”UÒ .ÿûöšûåZm-¸T÷© Þ£ª¡ø÷òêË7%i¿{Bÿ¨8õéJ;4m,¤=ˆà ¸øEðzØW¢ý¾hì$²ØT¶hŽnùÒ¹´ç…G©~ß.ªÞ´šj¶­§ÜNÑž¹úÓáY¯Pé'ï´àáÐ/SÁèq”Õ§¸Å³ãݼã(©m&ž6ŒFî|iǾÃôÊì/i3¯qá‹b( \»uOpš`gÞàUÞ2Y?†‘¡{mþ² T΃kñì7’á)¡œ”rcyÔ‰ýµëöü‹wÞëËFœŒ¾…ö±âvóÖÑ ÜŽCiÙÌ«©®jo³d5TÔ”~ù¾¯©¶bÚö1uè1F{„Ý«_¢£Ýì›H.ôüGò¾‘ßá§Á–BʇŠ2=úÊŒœPó–6Ší×®`«Doa¬Û¾^ã}­­âßx•³H [ÿ¢Ïþ`˜Ýϰƒ^7výï;¨ò²]TÁË Â9\° c¼ n]ƒ‡ô«ˆ÷Œ|Ìê;ˆjwoekŸ§A†57Ÿ²û ¡šÍk´'¥Ÿ½G‡fý—ûþm!oyÊ‘¯ª’²û¦Ë·E" rcÊØ“é ¶þAÞ±ÓãÌ+ –y{òz¨¬içˮԔ:Ƹ„’Ýnån€.4–Ýúð‚-á²&è@YuÓm*$î›í˜×éDª*mêBYûémTyh-)–¶Ç»õ:›*¬ &·òÈw”Óa¯ ØZ9ø “NTõÒ`r- ÷%JJ6-Kq²9ˆs|°¾oº|Øhq#x´ð¡¤«xÜH Ë¢ÂS‹&”¦ž}2‡UO˜BÊrØ´UÅ{Ø']L3Ä=XCòã3ÐGÄ‘ècV÷¾Ôp ¾Ð¸úþòä)?L¥_|ú(ìu=‡ãìÞ+ì3y3¾œwÚ‰t÷³w`ùnϘ ÌËÉ¢òc­,ñâ€)ÌÍ¡LMãNBض‚0æ¦(?W\šòh±eQVn7ª©ØÙ.þ{ ¾”º¸€6ùhð»Z- …² úïeÒÉܧÃX|î?…ªúöúE&¥ß(iM;Å`ájÄà»ËÏE諌†òy# Xë¡tÞ˜¨g—Bzƒûù1E‹^?¢ÙÁ£›ëùž „“Ÿk.ÅïèØ­õƒ" ÁcÏŸÜF9ƒ†kýüª·)Áœ¸Ùêwtê扼•zv.¤ë/>›ÎQL¹ÙYíŽ"Ÿ•>(œü‡ cjÿÏl­Ï1ÀOt¹ãÆ6Ê’™É•ݺªý'£C÷14|âôÝg¿cÏÀêàwõÕ‡ø\¥¬¼@W`ðA†œ¨^¯ÞÍ?{ØUëÝ’tC%3-¿@îÇËXù_=étÍÙÚ4<ñ¾þˆ }òB*Í3x€ÓɃûÒëŸ|Õb“÷ã ôV![[úJÖS{¼ z>Ruîf‹ÞVÔ¥Yô]¿ušp m}ør—¶l4{YwáèØ…ž¤ä!€Çpß:d<¶sØÛvIë9«ª 4xCå_ÿN¸ó ¶î×n+Ѻں6î ˆ0ªj›ÊJ¸ïŒ~¯¾Êš={lõGByN Qÿ›6/}„öoiîÉvæ¢L)ÔPyù‰$N³¼Ã«©\*xåÆaspÄyL8i­øz°zNâM}ôÊYO-5°¥žS8 ôQ‹ëì‚~4æ’W©lï—Ú€Ân£x:ß(Í_¨½›SèXÃÿ2>y"«?;;F5¦Û­: fgFIoËÑXFá,Å|”reW~w¶\v,Ó Kú9̸~ä¥Y´k)}¹vý€«°µä}»~³›3K³€êJi þ¯n÷fêpö…<‡Ÿeá~'_ņŠB½~vg3ÎW^)Êq³ÛÁ {Ç®dËï@u»x  $Ó €}Âjo‹é\WsÊ`Â|~Œ%Ø} ŒÞù|Ypl ;ö›ßÛs¬tåwÞÚ³nÅSȑ݉ºœ¬ýÄ«çðÔXá_Ðù$ª9ºƒüޖ݈âÝt=úT÷•"m\•|:åÖ0#§Å ò˜P¤âo^ †úvÃN:óäbMñc]€¶háŠM´˜GP[,J`C Æ—Ï1H³þÑj&*_<—º]ñ ê4þbž‹?“6ÞuíqÙ?øþ¿ ?=u»ìªøfÕ•HůÇÅèç‹×l¦M>‹–¬ÙÒl%ÌÐr€i®øaŒ‹—÷ÄÐO½ìËÓ];æÐ›Ÿï4zrËß¶eÏÐi—¾IÛ–ÿ•Ü5G‚ïùÆEÁsœìXñ¿Ú¯ÙÍÆ ‹ÕA}O¹‘»÷8î©?‰´¨–·xq)IF #\ýÑbºtõV*äAypaFBðh»6¾œírÒ€^iÞ²‘|n¨wTŸ—ö¾ü u<ŸWÖäæy4„}òGžIû^ù[4ŸËoRˆÀþìÐ÷ÒÈAMk´ÅÜûz¥wO=¡Ÿ6¶N·ep[aùYåÁÕZ}ﮎšÍnÅS©º| ØÙl˜¨#2à‡³wäÙU§‚5®Nü®‚ÌÁ@ù"-þ62®Ë¾;¿7Ú~„é€Ïñjff¥Êe ¿hÉ[YNëoiZ’;Úpäw©A`ö’µ1Eüþü1}o´×Ï»;&–öo~ŸðËDRÏUÚ¦œx•hîTéæO©¤µÅ)EM?mÉyòG³lW’³<Ú­ïÑYÜ`[¤®X7»Í@à3^é7CzÁ£È÷À13e_k8<¼düòàUéQT• 5’7¿¤T"¶Š=”œ¬LýÜitYŽæ;…KÏñÒ¡ÿ&‡×6¨µ›c!ðôŸt}®Ïw‘ÆL“}‘n[ãÞ_ Íçõj¾éËÂÇO9³™?RË ¢ú‡Y†;ƒ˜J[Å.]< ϧú mõÃÚ9¼`<)Ÿ§&Öñ d 6c/ þÀg¼ÓO,ÍV&Ë~¸ü ”Mn5°XQ¸wR}¼G³—Åï êc7ÿì±w—V¥ÛL?í?ZûâW”c×–ï5ò`#ð†Þˆnr‚|Ç*¤Hßn¸Ñ­PI^ÿXƒKè÷à|Æ3ý eØÀ ¹Ç1Se_Ÿ=-ñPèwˆþCƒ7UMƒ² ª?À*ÒÍ/ Hé1­° ³"Áüú|—ûˈ7,1î*‘àÍa³PÇ|sÝÔh‰VJ]xÓ!—ÝBÛŠŒ[Ñ!àÏÉy¯ôG‹›Ù¿ùÊTÙ×çaX<,~Úë9Yÿš¡ÎÁ›“f. sž´aC&PéðÚü…¶î ä e&m° sW¿•ûö-¼ñŽ]²±Ž¾;¸Àˆ‘ò]l¦Ò·ÈäCDZÚË«øa íC{w¢=y}è@Ž1× _à¯OÇœ¸¤¿½x¥Ëû¡ùž‰²¯ÏËVñ`à!o1•zûê_7Ä9xo}ŠÌ]T¿ï'PEUÞ9ëö½uâZS‡@Ú*~@ KǪ)|+ÙxT»·í‘ÇV?§º+ó±KÝ(^À“êЀ.ù¿‚w¤#ZÒcpbßÎä²)´´÷òg;Ñhã‹ö;ð³´÷xrZÔ¸¦?Z~Ìþ>ß3Uöõyضú×Ö_L^5ò½ ôá&â¼€'§ÕÜeaþüñ6î«.|Àæ×— ³ýD¯QÚWR¿­|(O(|‡ÃÁ?;ÿlÔ3GÕÖ/¯0Îð‚EPurRVøth|ƒ‘–ö(¾¸²œ4²':šUD úLlop }ü”gu¤âή¸¥?¡ 8ðÐ|ÏDÙ×gO[xôÈQ蘿3­ª»\ÿIJÏÁË1*îdî²Ð°|ñ$¶g:Lv:ît×ý S ¬Œ<ˆ€Ñ?lpþ)8ÆLX>{‹Cá;r²2u:Ô!ÛF^ªåt¥G«RjùÃÒà¥Wž…ºò§> ðtDK¡ôî\@Å]rhGa1}Þ[þ°ôÁøIDúÃàÖ(c3í敤ß¼ˆcÌ „æ{&ʾĶñðÑ>ï0ZVûÔZþ°ôÁx‰{YP‚ò7Óãîœg(ÝüdQ^U”éÆŸK.!ix/¾sÆb©‘OõÕÖ{<¸Ž^ãñǽÅŸÅ ?‹7qñ¯®.‹ûú=¼ ›Êx¯ oY…¶svK&¡Oî}XúݲUêÝÁ¥ñ>Á/ø†µŽtDKá0Ü£<í bªà]ÃÎÞ³º×D¾×x´¼„~‡>ý%½Ç³¢cÂÒ/â¬w{x+po¸þŠ܉w“|l¿×ç­‹UþÃå{¦É¾>#ÁcŸçDÞB¸p~Dl»õŸ'ü}úpïÃûﺲïóùjCÑBîBžÇt9ûoEùjýÑï‹@,V»ù;¨Zð™)G#)þ æ~ïpEe 3&ŧ÷"G¥—““M õ ÔÐÐÀÊÖC}5d§zÜã$ìF–Å^—ƒ\¼Ø#â@˜§){½A†6vnôÎö±¥Ïóus\”Ë<‚Oð ¾…«?ZVZà¸[>9,´«¼ˆf º‚zW•PqùfêSµ“œÞ†h£;îw˜§){½|vÞ¨£w¶7aé U«Q= Gŵ‘žú†£±ÊkùžI²¯ÏãÈðh 2o'ZRûsêjÛF½ìkø¸™JbvÒÃ<}LÙÃè} ä³+( ñ¯ 4Ùw»Ëõx$ú\©¯¼š«:âáštÕäÛÝë§ ?rŒ¨øQAﯪkPjYIgs¿t´(ìr°ÍâprsrXé»ÉÍÖ.6ÓÁ¦:Duäj¨§£ U¹U*ãçÐùy*ãA~^”ÇçS¹[;˜ñ({VøE6n|¸,¬è³4Þòòr)'7G;¿à£ñ‘Žh©- z09¶*Ú[Ñ@¨§¦ˆátyê)Û[EŸ'Úh[|ç¶Ú©Ö–Guö,Ž»`XáÙÜ O?AžêjkJù JñÃc£àIŸ±Ê[ùž)²¯ÏØÈñpkuA™·¿¦Œ.§Nª¥,K%Ël|,V,ŒU±8æéÛXá'ª,Ùw×Õd<„Œé¡IÈ9×t¿¬(ÿ6N¯Z«Œ>1šâ×óÐÞ’%ù;Þöݶ}túð1e\|܇>NXÒpq³Û«Qé‹î€z²±Û½ÈWGu^n øy‹Q.^/«A>ÆJZC‚ËV·)~ʵ³rµ+š+–=$y”ŸÏ?(æü‚oð+mšc]=UÕ{¨šu½Ûb£GU7FŒ†J8riêptn}Š` Ý¢rÅIT ¸)×–ÜôCŽxç8eÏÖÍ«™´öDØJÊÂ¥6¡÷ôüøÁgÏþ«ü/ßñÜjMoÙ×çZûðhÐÕìdüñꂺª½Áè\y½‚çú}YÈBYHp] dïŽm˘½œéÙŠëùœ'#ü~ÏÊém°Ør_%:×8d`±!`$Åʽ÷]ñˆ‘eËÖï(bÅ“æ ¶ôY‘jËá²ÒŠ,PpW€ 6[ý·Å½^Êãwð„n3 Ôàñ‰ÊÇn·q׺\KJ¿° @;Gc#±cµö¿‘bàpÔS>cÀ}áZú‡Á¾E¯‹(¨ço ž‡;IeúYŽx:±ïØwË¾ÚÆ¼apü‚òŽß$ÞÓó¡ñ>O;R%ó‹üGšïé,ûú|L4›W½Œ®×Y÷ÏCO’Y ûªß_±|Ñ™¤È½ªz!ÒÌÞ½÷&ß~,©Ý "nyl#)~p kL«üJì}¢Ü²µä êÓ­õDðD(x§ê •§òÐòFÿ9Üéé_ǯÖÿÉŠÏçxüýci ƒ0ª|X-sÀŽ3`ísŸ>»÷aék®þ µÛ >-RÝ¿D`€5ÁKŽR~ÈϺm{•’m[?aþØŸ£ý4ãsÈ›((÷ÌŒÆ#øå…w®ŠUþ‘ï‘–ê¼Çg"ñà¡AA W’‡ýý»w~ÀŒA®.÷³ÿVì¤úíú%z_4N1 fOÆŸIñ ËÂé]ôÁ{¯_~Ó¯®y}î×î¹áb‹kíÆ@(ðP¸ ÀÀuã4?îO¯gÅ_A¯wÈ;žÅ);">Xðp߬ýÀ,Tpõggþ‡¢@IDAT»´\üxþâMñÆ ‚»+u(l}§³T¥ßíñËßïõ[:ûÃO™WtÒâ'*A!s"©8 4¹oäÏ ~û ,¾ùÏUþãïí)Uy߉Â#Wʈ¹µò,<„ìópþÊųfÎl”«„˽R¿ór¶—ŠñßqÑžùtgS=ÑV¾ÈgÉCÀHŠ©†åát——®Ú¼zÅãÊ©c}yÖõ—Ÿ“ôˆ¥ª³òÇ9æ÷g»\šÒ×þ¹Ýㄵ/ºÀd¤ÔhìX:«¨Áx1eV?òAáí Xúà1Þo *M“Î:¶`W$AÄ›ìôCnö© •K¼\[[…! ˜®€ŸPþF²ø5¹<‚_ðm9wâ-±Ê¿À?“eŸq R¢ð(È FA¡å!ÙeAÈþÚo–>WUU!d?ár¯ÔÇ8ÿ‹k4òÌQñÃò€Ö/žóá’¼^âó_Ð{¤^wñX%Ë^LC«_Lõƒ†¥ïæ~è߯à?Xü*þXã åM~!NíÇ#Ùañ#ÎÀJ‚¼t07< ìíš 0‚|%’â‰A¹³‰×®Ý´ºZ°žŠôÃÚAÅ·|ãNeû†µï¯Z²p3†9üøanä rf$Å”{æKã|wèÜÖÚå±Ê<óù‰ˆR‘÷1Æ/%yM±‡+ÉÀC/û»·l|ýÛùŸ}Ã\%Eî?yÒ1Ôë󜧡 Ï¦fýÏ©iEž£)~´Qb„µfökÿy}òÕ?Ÿ×(;ª^=é K<úüQµÕ¼X颟_Sø< / ôÓýxX4æ>3 B< (tœBñk?¾F pô'ÞÕ>Hð¿?±aP€M©cQqÚì(Ò”¬ô£_î}XúÛׯý`ÞÌ·ç2C5üƒÕƒ#ä ò93Š5ÒBî™7lïœ÷>ó¯€þ~<ä?ùμDDÉÎûˆ˜ y)žxä놹„+‰ÆC/û;7­ëówß@£1irïóy„W¥.¼»¶iÚOð<1FUüšÅÏAhsßøï›c&\pÀÚY7?õß¹…Ë{©c† PN*îõ<B(]|¸ßa}c0Ÿè××[úñPüˆOü§ì'î¥BbÅ G7¸ƒÃâ‰tâïôc®2¦-a3òù½žªå‹æ¿¾æ«Å˜¾¦ 8â§·ø¦ø›É=óªÍEã¥ìÐÁƒ£ÇM¸†å¿ Vù5ß™¯ˆ(y#Çy)^xp¯]•‡DàNöW-]ô+—,€¥Ÿ4¹ŸÿlçܺšÒëDSZ±XŸ ôÚ!‘'BÀhŠЈ~~ôÅÂBÃ:ºÖeó?[²áÛo6uÑÔ‹yjÖ\ÁpARó\Nµ° GɲëFšñÑR¨’½Ž&\QàÅ·¡×â¾QŽ¡i½|ŽçŽýóÝ…â´Å14½¡×->ˆð–µÅ wXì†yTØâ¨Úµiâ¯>™=¿®®FTz•~˜H y‚\¡?Ý(n~fE£°rÏO4· 7bVnY½r뙓¦LàtŽcùÏgã*ÿ¡ùzÝÈg»¡yzÝ®À’ürhúC¯CÙ9QW›¶VBÓzfk×ád÷–Mó¾šûñ§µµÕü]Rå¾¾ºì:nEçƒ_nÛo˜r§w^k¼Ëû©G@'ª©g¦‘áö„õƒJÃù5 SSsÌÿÙ;¯¿Å×9jpŸAC†»²s;9\Y6›ÝÉ>ù&MÄ/IJ,çñÂ"†Õë6ïçÉ:òšö uu•uÕÕå\émÚòݪ]7Üø°ê¡ä¡üQ¢"Ä9Üü¢ß(Ö>³¤Q«rÏOµF7fé”~}êÊÍ-rº\6+¯…,)¥ òÀ²_ϲ¬®¦º¬dËæ ›×®DùK™Ü«Šú« µ¯*ϧ-¥"aØÈ¨ø–VÑñÖ™PæúбaÓêkø·‰Ÿk>¢‘ YGºoøTR¢¸}j—ˆ°_öé§Åy’ŽBqCVðcC 3PðµKJßÈÖ>³§Ñqåžß¤1'7r6ðo3ŸK¹× Ký¿?¦$£<Nîç>f›À«¨å€BUyjoȃձ%£*~à…JÖÂ.®Q±£e‹ ½jPüH<¨ECO%%Tl€#äJî{aíkCùÊÊñƒÜà9äÈÈ$ä<Šôo)÷Fε–¼%²<¹0„Üûßm:kÿ?cï.EC[’0²âÂJOŽ ¹6ê™Ú(>†*~Ù`P’HG’ä$äBT€pã  å"®q_ôë‹où–!I¤Iʽ!³'b¦Q„ì I¹ÜX¹¸žT¸o_µZlìæGq“ddŒ¬ø­taýÀÒ×+}1 _*}!Ét0ÉñA&Ä2¢¯!#¨}pÄÏŒné3‹A’r„´'‰*Bæ…Œ¤Tî;öâ]¬OºÃîWIGÀèŠ_À‡J‚Ž#,7Tê½…/”¾@©ø”$SY’ãƒ<€„lå/*Bq-žÞ6×Á»”{så¸MTy0ŒÜgYªìݬ»‰¬á}HžTÏâŽ<³(~à'Z¸¢„%'6 +_(|q4*îéÈW²÷Ý ŽâÙ?qÏìX#hÄH¹7WN&ª<@@B¾q2£¸wJC‹zXŸfl±›Ù¤;¼‹¡ öûô;6ƵCÉ çí'øÆ!Á•AK$3#0÷ig±ŸÔ[‚iP,wIk?ˆ†iO¤â7mÖIÆ%‰@bð{ݼÕ.9´XZ<õ.¯É/ÉìHÅoö”üK$ 0ç ÛxÐw)‚æé{ªÍbûm¢‘A¦©øSºŒR" Unñ«¾g*)ÿ™t‡g…¸–Gs# ¿¹óOr/HâŽÀœ'¼žë‰€ÙÚ¯±«Y÷Å=`ÊŠ?eÐˈ%‰€ñøüy<‚ßÿh3Eyì»kåšüA@Ì"¿ùóP¦@" Ä †ºšg¸o¿Kc€%…ÖOÆ-p!ŠßÙ ™H$©G`Γ¶IäW"8±(–›Ïº}o¸–Çô@@*þôÈG™ ‰€D@"óŸíœë÷ûfˆ@Eyõ¢»|³Åµ<¦Rñ§O^Ê”H$¨¨­.{˜ôõE Ñ—%ç7Q&?44Rñ:{$s‰€D ñÌ~Ê~ï¾\¡OQ,·M¸£ª4ñ1ËR€Tü©@]Æ)H ‚€¶å®Ïû/fGÓ<}ï#vñ¿nö$ @@*þ€*ƒ”H$fA îH FñŸ~Y飬¬›Í»ä3:¤â7ù•D@" 0=s·^©ªê/EBT²ünÊ­u{ŵ<¦'¶ôL–LU4ÜüÀ££|~ß$þ¶¯ª¨=x€O^[á̪Ú|<íþž ‚aNx[Ï*EU°Èn«ÅúÉsÞ+—ÿ ƒ“¼e´òàóNf3¸O$åaIÍ® óÓîï· xrb”²ðù3YÜî†{<ŠÿÍ)wùä–»4>JÅŸÆ™IÒ¦OÿwÖ>Ͼ[òßîõ{»³²W³<µþoâð¹#öu¯Úwn[ñ¹­-G­·g[8ž‡§Ý÷§~Uy¦—£×ß§O¿¡¾­oå3‰@’P~ûÛ§³ŽeUÿÚj¡ßhå7§q(Õ~—å˜b£†ˆËCGË®V˃—œþ:¾êVsRVÖ¿5̱{׆7¹Rl¹Üo£¬ÂiDåI‚ZF“J¤âO%ú)Ž{Úý^xÀ³ç%.ö={WíQ–o¦>U;§·ÁšÖ´J³Á椒¼þ´½hH·’¼>ïw—üæç÷Lÿù¿þ<ýŽ“ë!I¤#Àz,×ßyÿ¤gÕ ÕÒ£³}›ÚÛ¶†ºÚ6+¥>ÞåA+ n5‹y‡ÐïÉÝ{ŠßïM^Yؽsãc\ØFiî×ç퇓o-?–täe„)A âlJ¸“‘& Vú¿UUeva}Y·‹·¾K“·  :º‰Xé',NŒðâC¼…õG»Z¬¶n¼ïOØòSÊcBÑ—‡A2gûÙ=¸ÝîpÎʳé:6û_t–ëe¥·} ±ÒóI|n!lĸgžR𔲠õë“Ú4G_Qî˜|—ge|R%C1²¢5C.Å—G% ôéé[-—nyÇÚ½&5ûo Þ˶¾mlw<õ‹ßÿÊÖ,0ID"ƒ¬Ù¯¿ëþÛ­Vûã½ìë•qÙÏ[;Ùv'2Þ°a#Îq93¬½ìëZ0__Uý/ &ØÚwÊþˆkyÌ ¤âÏŒ|©TØ­>‰-ý'VlSÏß5G±ù=âYJŽˆ|€^4äñŸÞñûÉÌäR*ÿ”äHFD Ù‚ŒÙ¯¹åwÙíÎGXé«£]o*6%uåqƒ𒈲0ûqç@òz?dr™•þf{AáÏq.)³Š?ƒò{Ú´é.«EùWQý:·äsC)VðÓ¡®Lu:œ/Lœxe.g‹”Í ’Í$'USúÇŸ^W÷\¾õ:2ë=Ôð’o9¬:ñ+ sŸÎ/"Õ=›•~g`ÍJÿ°ÃîœrÁMG+“Œ½ŒÎÈÊÕ™$,¾"åתbéqöž…ÖT[ú¡i?c÷.°2Ý{ž<ø×üO¥|†%¯cE2ÙrŽ8ïÜ_©dí6Âù‘5•–~h‚Àˈ¬YVžS—²0ûoÅNŸ·j&+ýÁZ\ ÕYÛ÷'þ¶~GhÜò:3kfä3¬«ÕJ¿éSU¢¦ªOÿxPƒ/ðçp:°f¸<óÏ0–Øñø—Ï €V˜KÈ–+;'û—]xô~*úô‡xo±–…ùóÇÛ¨nû+<_æœÆ8ýŠj¹vÒžoŽÇƒ|ž¾HÅŸ¾y«O™åÇ·Þ=†-ˆnåy¬­ý‰—ýðtÅbíÌSö [4Þb( ˜«_·láÛléÿ œ¢Ü>ånßÌàµ<ÉH¤âOÿl׬»Ó1‰OTž§oèƒ?ð™[Xp3*­~Cç–©˜ÓÊA£Leuéz>äŒçé6à-Ú²€wxž÷y þKEye¾§¦Þåÿ«¸–ÇÌE@*þôÏ{QáõÊ|‰ž§+œà|²5֗òóOºûcU~D9€L¹,6[‡Rëò%nž~¬°ƒ7æ±ÝeaùŒÙµGvÏb¥?Eð X”ǦÜå¿C\Ëcf# úç?òØJ¥{¶§Úù >«Òù¿)øNQ2u å SYV»­K–R‰Æ€¡‰ylWYXü¿U˜Ã}ú›¦ü‘çêßÓt-Ï2ô¡JJoPáÙ,¤ä9ýÃWtÈ ði±Z0¥Š_öóI±" •2å´*–\»â6|yЬ,ÌyÂ1âXÕ±÷9}X )÷L¹Ûÿ˜¸–G‰@a”¾ b穆¯äôÙÀ[…ŠŠZ¸úMÅ¿>-ò<åËs‚†¤ƒÝ঑§HÊœ'¬Wûýž¯8mšÒçyúªÅ¢Ü&•~ÊeÏ H‹ßÙW¦ @­ìú3MEH½ÖPÒÇO6P ÈÿÑ#(yâzÏL áÖË‚úÖ•Ö9»Þ}Ìï÷ÿ.BU¬ö¯»è.9z?ˆ‰VÜîºrí»ÜŽCiÙÌ«©®jo‹pZ{†FÂîÕ/ÑÀÑØ|2r²¨ÞlÍÏÿ4·¾ª<ëÌÎ:å.ßË‘‡"ß”4! ûø›°g:²ú¢ÚÝlHø}º»Skn>e÷Bû¿ú¬Å³¼ac´þÿÚí´gž²C䫪¤ì¾ƒ©¾d[‹÷å ‰€Èët"ÞñIÕµŸÞ¦+¬ÜœÚzVyä;Òá>²X\´¸k>BbßP]¯üwóNûã½ÖÀÛéVµ,˜†%_“HÅ/e ,YÝûRÃ’°Ïúþòä)?L¥_|Ðì¹½cWêuÃtèÃÿòó#Ágõ޳{¯àµ<‘˜ ‹-‹²r»QMÅΘٮÕÂP(» /U—o‰(¼î¾—>¶^³r‹w;ëÿc}$_’´€tõ·N&?rtìFž²ƒ- èù“Û47>úùõ#û-®*¾÷¯T³y-íã›}çf«ßÑ©{³{òB"`\¹Ù­«Ú3ËõÕ‡8 •²òzDV½?»Œ•~EÄÈ%ÇA@*þ㔩ÝlÑÛŠº4K~×ï_G&\B[¾EëÏ P|ï_È{¬œvþõ¾ÆÑGâ)¯Ú± !þh‘Kóïêvo¦g_Èsøy¥Pà×yòUÚâ^?»³YÊ×ßr Œ:G»WüÀ³ÁgG¿úœv>}7¡ßß–ßêvÉGApä‰é8Vºò;fªÄÂ|A瓨æèò{ëc F~+ˆ ©øc‚/}?._<—º]ñ ê4þbž‹?“6Þum«‰]y¥6½8ìón—Ý@ßÌ£º©øÃ$ošmËž¡Ó.}“¶-ÿ+¹kš®~ùF`-‹p‰}f±:¨ï)7Òæ¥‡{]Þ“$ éêOÔæŠHõyiïËÏPÇó/Ñ,ýh¸·QþÈ3iß+‹æsùDÀ0T\Mû·Ì¤Þ'\5OÝŠ§j#ùli>&êå‡(”ÀeÂg•Ë~Ñ’·²œÐ I"¬ŸwwLÉØ¿ù}ÂO’D ÕH‹?Õ9øøyíR}ª¿¦ÁbǹáÉmq¨>¿¯†Õxo<žoÉ ¡ÐË’êõyë<ªÃðåÁ£:eY0´X™“9©øÍ™o‘r¬ØüßáZ{nð:ÒRñ^=Gõ¸ÝµQ›0ïMìÊ3!ÐBvÜõ õjA‹ûâYc¥^Í—eÁh™’üHÅŸ™I< ûëíÙJƒÍÉë){üOw]m`òtÊ8‘§3õµ5GÜjŽâV³ ›Lð¦ñ(Ë‚aóȬŒIÅoÖœkßêÁ=»—²y£”äæ·ïóä½ þÀçžíÛ–q¬°È o•%SŒyRK¶lZ 9;äc‰û¼GY‡q¦†,úç¼VÙ-üøýõ~Ÿ§l{Ñ\–ÀŸßë>ºbñ¼Í̤Æ{ãѰNdòqJ.N(Qì R7÷WÞ»ðÊkí4ø„«fªgïYhé^û^ãíMúôáÞ‡¥¿}ÃÚ÷ç½ÿölð×ø¯àVšTü ‚¤˜hQ84Èškþï|¡’ªÐð‘Sù;©#œY:Ù’»›$úôáÞ‡¥oš² *yŒß¾˜rE~œbVüŠª@c´¾KKJ’%#Õ!&”§‡¨èàFÏúôí×fžzÎøÃ#Ï8û†Yƒ®(èSU¢,߬ô©ÚIN/Œ¡ÄæécÊFïc ŸÏ]_µbÞ§¯¯ùjñJ޼U7Á+xï†qÃ2/’̉@ØrÀIÁÂö¼;¿ìà#£ÇMøÁßÏ󻨷©½mk”®¶ÍäP³“æécÊFïc 7¨Z±ØTeßrsŠCfs³âgøv“¢v½ùÑG;<BáÂ’ÊúÐay㾋ZŸ'5QQ‚Ä7+þï²5»ŽH(}1Þñ k?>ÒÚg$% ¸—ƒvpšeõ¼_U's•pg;Ò._5qQü=í=ÿ±ß»÷×~•þ>múŒ3ŸŸ~S­Ò(YiB•¬h°:p % Š_oõ åßLÃó;åÙ›Ýnk|‡ˆ Jî{aíCñCÑCñCfð/ÒÒg$% ¸–ƒvriê²Àõ{¶ßsäïŠB%=l=ÿÑδË× ‚@\ÿôé7ÔO»ÿÁ9M³ÉsäÿTUý¡¢`ÜŠ$ƒ! *(Vý9”-”.”¾6ðPÅßLÓwpg¿ItDûßüŸ—PüÂÀÆ”?Žà÷E¿¾ø–oI’Ä!q)r&äYÄmº²ÀõºrÓýÿ§wÿ¦ Þç£$"Ь"•ÿi<ôÕ¯>Ãýýo“½óõÒòÑ„~­ ÅŽ>}üº£Púx?>m¢×Ç­W×,*!ÎCލèÄ•¾ÂCÅ e#~xkL’D YÄ\ÚÁ¨(8šª,ÀÒ׌:R¯T,ÊoŸðþ¿´#ÝòUƒ!WÅ´”?=Éÿ:žã«ìó7XŽ7gù/*>(y½ÂJ_˜õ-deöùö‰à¦|QÚSœ‡QÉ Ðõžhˆ P<×^–ÿ$ID ¦rÐ>MY´>}¢¿s:‡)ºC*ývä¸A_mQ™ÇƒOvû_Èá¼ÀÓ=ú°Ë.ÿ^µd[>–óüãnB€ˆÊŠ?qOȈ8X8©3ãitî'G03 ‰ÊGñƒ’?q/Ü·òžD ™™e ¢rÐMS0O?0eOý»ø'£OŸÓyãó=ði;Ò+_5(-*óxñ9}ú¿³öyöÝ‹ûÜÆÕ}/?/öpHUÔý)sI20ÜeäN[Î4xÕtrãêglj«Fþj‘8=r×O00v†>–×Ã"I9hóF/ \PsÙSÛ‹²až>7ÿ÷r½ýW à–}úíÉic¿›”Zøæåóû&1}Yñ÷àH[³–ä®7®úû¹âÆ §üz¡8—G‰€DÀœ°â¯j\†}7g{îÁ{W˜3%’ë¶HŠâo‹ù̼,›Ú%hÉùø°”%óf¥ä\" È Ї%I" H$ A@*þ Éh™L‰€D@" ©ø¥H$‰€D ƒŠ?ƒ2[&U" H$RñKH$‰@! e¶LªD@" H¤â—2`hTuºeþüñqÙLêx }æß……Ç{G>—H$fG )ªÙA’üGÀ#Ï+ëÈj½ñ÷?÷~%Byú?®žu õëòz ìV³gÇ]ªB¶ßOóÿ?ñ\|ôÅ?Ýë§añ§àbAxþÈ e-)Ê·üÝ/ÄûžÑ¡À§T¬ºoš:@ÜÓ}ÑYì÷»ßæJfÿþ&ÿ}âÙÃ/ÚÎ'Ÿï¹ZOåæ×iS?¼ë¦†íÁçÏ[¯â…'_´‘mÜ]Ó<«Å}y”H$fD@ZüfÌ5Sñ¬¼¡øü?Ô³\ïn¸’•ï·NÙ†­xÛ$^'üJ^îYyô¥¬0Ê\=çÏ3lcÛ  ñá#ÏÛÎU}î÷8¨%ú÷ßzëJ+ù}ÿ²;S¹Áp¶…¬÷y÷?Å;̰<¬¨þkx¥¢ïÄ=y”H$fF@*~3çž x·ÙíoúUõØË[°Ëç?T¬–7ÄukÇ??o;‹Wùß¡(–Wü÷u¡ïY,–;üä{nÆŒQØV¸M²gem´»r&rx›õ/n«zÿ4f¬ä®¶âþ=Ó¼ŸðåÞ~1¿×ÅöÚ½Óü—ó~^\K’H$fG@*~³ç Áù‡BeÅzøÑíšeþç®>¬D‹Ï8öóã±î'ÿõd±¼®:²ßåmž¯Ñ7ð­ßâúšÝýkÊhÕoÖ×UÆÏÏ&¿þ]ŧöàKšÝãIêÔú^¸wÏ4÷zÞ]R…×AÿŽ<—H$fE@*~³æœ‰øfÅù&ùî~¿Òp+Ú÷&LXЦýô[½\¼‹ïEÝm½?úýO«Ê8¹Û}ÁÜ É·)ªUU\¿ÃO¾˜Õ7HØ‘Ï~MÈ·5ܧ_rO^J$´@@*þ´ÈFƒ'Âîx“•ø¡ÏGVüÇwó×=p{:î÷ì^ùðóÊVΧ©ð„Ð}7Öbcüá_Ãß›Õòø¸—ÌP9[ó¡»E橊åèq?–/H$" ¿ 3Íl,ß{Cý.æy÷cÏ?x{þ~Úý —Eñ_ÏÊ÷z‡âœŠŸU±'UýÞ/wÍ ýöžÿðOVÞݼ¾£SCŸ÷ÚbÙÅáï¡qÂŒ¾—kŸ¸'‰€D Š?rÓÀiQIyÓ¯ø!UyGQ¦·i™?õ’«7¢üûxçÎëwâר×>Ï×PzEh2ž¢Ønæ^ÿ?óà=þ4rºççž5üAáŸg8NÂW~áÁK¸±îÞUJ‹?r囉€‰óøM”YffÕ•å|«¾¡þ)‹b‚n–´w;ÏËŸ&n6x>æq¯µh (ÊüªÿwüÞËâ]q¼÷FÏŠGž·ÌRµÝV?/#ðS?yÞâ9üUªêÏ"²sw„[-‰€D@" eS»¨â—ˆ` tH‡LƒD@" h éêo ù,£¸ç¦£••`™X‰€D #Š?#³]&Z" H2©ø35çeº%‰€D #Š?#³]&Z" H2©ø35çeº%‰€D #Š?#³]&Z" H2©ø35çeº%‰€D #Š?#³]&Z" H2©ø35çeº%‰€D #Š?#³]&Z" H2©ø35çeº%‰€D #Š?#³]&Z" H2©ø35çeº%‰€D #Š?#³]&Z" H2©ø35çeº%‰€D #P22Õ2Ñ#ðËéöS}¾É~•Æ*ªÚÙâ÷w$E-PI±u«9Ð/ÇSE¹îjZÓu̵ŠÝùÅ?§ßy8âÀ勉€D@"t¤âO:äÆpÚŒvÚ{ä&òûnSÉR ޳½µÞ¼†J«Ã× 8}n¾£R­=‡jì¹ÚÏk±‘¢·h®M±þù¹¿Øø)•J$ÌC@*þÌËó6S|ËôÇzx< úIŽPù&KÏê=”ßPÙêw~²ÐÁÜ´;¿?më0ØWg϶*¤ümÆC÷ýFQn è—=Ô“,ýT•ºªµÁª*Ç([Y÷ܽ÷ïÈ£D@" $©ø‹¯©BŸ>ý-ÇϦ¯-~ïˆñ%ŸYTlo7ÿ°ü—u;“¾ë2’XùßGÛ,ò{Á΀˸1Ñ;\€<Ðd§jQÞV¬–ÿ̘þû áÞ‘÷$‰€D >HÅMÊM÷?>ˆ¨a²Ÿh´E¥ÁD~3oõ+–EU|*©_¸k6õ‹BéëAø´ÿ÷Ô]…ý=¤*v›êQ{+±ô¬*¡‚† ÊöÔ‘ßb¡k•º:Ñ¡œjIA?•n-¼ÃByÏó=ÐþV‡žy.H$aŠ?,,éwsÚž@>õVì:—§ÖWT_jµû½|¥Rµ=O­Ì*ôw¨+£K·¾cvùÓ—½Æû‡Yc9éÈjâ±mYosцN'Ñê.§ú|»O±X~=ãÁûžoó#ùP" HÚ€Tüí†Ì\üöé§]Õåµ%U½1×SíVºÖV|tå¸k ™@ {Ÿ¯–ä÷ûÿí]|TÅÖŸ¹÷n½ƒ "ÏòX€gWžõSTŠ "5ÙàJvCU)I}Ï.ŠõÙAìMéJ ½Ò÷Þ;ß6¹ËMØMv“lH9Ã/Ü{gΜ™ùïÌœ3g–(©IÃìëje¦)S„!@Ô!Hðס+ܬޙ1#îPvÁûÐ÷9s×råì?1UÈ~íw?µ¿€ýÚ¶·\'0;Óã|°öç˜rH„@Ý@ Ê&ݺQ̆™Ëžç^ü.gâòËÿúX9}ïïX{™ý:â:ælcºâ`;ãÚŸwÎÅ}7ýòÕg+êHÖ)›„!@Ôjèä¾ZýóT>s ÎÔóW_¸í åăë+ÏèÆPš2oÁ½u0û”eB€ j$økÝORõ éÆû±5.¾÷ŽïêüïÛ¬p?ë³M@‘¹«êÈB€ :/è' €€!îj—·Ël•W?ŽÍo››%÷÷Ÿâr-Ó”–¼B€ Â@€`ÕÒ‡\³šaß™³7×›ß6F/”˵ƒì·Æuá7 <„!P›¨7¡6ƒ\“y+d¹§à,|¥UÞ®šL6¢iåà" ì;-|ÒõðÁˆ&DÌ B€hà¯g?²aò–²HŒüzQ2\ÿË67ïfbŽÿózQ *!@ÇüÇø¨îä12–wæ2ƒ×éðmÎÂq¦)ó«+âG„@CD€=ûÕSÛ"‹t(ºiDJƵ(Æ5G@ÞjlõNÁolvû©Ã?±Ÿ½ž1ÙùNÀDÉ“ B ,Hð‡Wí'ž—:nÌâ»þŽ?¾Ê™mÑgë1ûmÆp“^£®ÝY÷Éϰ³^ú†ñìRÖõ!S¢q±Ÿ¢²N÷Žcg>ÿ%;ã¿_°Óç¾ÃÚÝ0Ø—¶Ö¬%;óůYLçÃÎ˦fÝØÒ®ýå};˜£Ñð°PB€ €àKÝõô]h£ð75í&Äø¦û+U9ªïpÛ0¶óÍgps¯Éu<åoÝÈÖŒ¾…mHÅšõ¾„µ¸¸? ¦ïÛÅ6NÍVÜ×íýô-ÖáÖá,ªu¦ÜÇö~öëxûaç!¾(›Å”g wäÞ¼oS¦ž6Š@„!p$ø‚¤î{Äiqiøa.ézµQ ÅTª@-.ºŠ)Žh¶ïË}ñ÷óÛº` +ØþËùó7–»a5k|êY¾°ïü—^½œé‡°ìß¿óYÔØ8_Ø®wžgM{_ÌbŽëV>Zåía·®y^½zÓ{,FÏ?A˜ÞL)N0,NDL„!`G€¿zò>Ë5z¿Ð”›F·Ðßì~«ñWÓpAfÊÃp1]Nby㌌èË:µq<‹íz2Ë]û»?(®{OÖúŠ›ØqC’˜Tòe\8/¬ÆálÛ¥»Ÿ6œ—ãýÅnüóµ‘7Ï! ï›tˆO8è-!@G#@‚ÿhLê…Oæääe‚óËó×rü@öBÏûÕ­{…\¶˜ö]XáŽ-é» Ä¼ûw³½K¬·“¿sBs4mÆöþ~©xàݾS)¿p>bõ<ÖgÛ2Õä¼ëý»kÉK´„!@¥ Á_zõµÀãüVÄG;í7¨1ª¼æ6TÕ²Fë;"ïx׃,î¤Ó}óüB÷úÃÿ{û}ðål×»/°nÉO³&½Îó‡aÔÕª½ÿ»2/ÇÚÌ¢ŒBÃæ•‰OqB€ Š Á_k‚Ëõ\ ?Tô1®µíÚgë2vÆ®ŸC.mFôZ‹6¥èÛ^û¬Õe×±õžXÑÞ£•#'›íÁâ>³0¿”àjÙ†I~UqØ©ÀšxcÆBW>— †Ž@ý8奡ÿŠAÊ¿Cß¶æþs¯Üü>ï’½)U`﬿YL§#[[]~=ëxç(¶í¹™L‰eqÝ‹§ wogñ=Ïc‡WýÈç>Å@nó+ؼÎÏ8º]gVøñëþïʾ˜¸„€ ^?Ž$¬, *"@‚¿ŠÖÖè )žÛMÓ¼óü¬oX¸B_–)ÿﵬù…WúöéË~­¯äìî[ªÈ+‡ög­ûÿ›ušŒƒ}¢°ÚËzi¶oŸ$t´lË´øæ,ÿ¯âÅ~¥"‡ñ†Žj‚Y xáA¼ˆ” †Œ þzøë»\BÙá<¹UÞ>³×î_*5³ÿ«X»›îg­.½{ñßfŒ»=(Rk“îömáS¢¢™YPz@.ó9øÃg,KÕÿŽ¸Ž kTEáË‚f„B€ *D RB¡B®DpLØa¦]³ø‰gíú±Ò¿¯0t¶íùY¬eßë|#ý „C~Ê }­i ælû‹³+Œ^ÁÚ§bC¢È‹m»¤"Z 'B€ŽøƒcSwCLÖ»öEÇœ­ámÞ/Sâ쟾`ò¯²NÏÞÏV?Å¡ŠNÞ;°±ÅÉ8½—Ï5zti“ByStB€ •64 êVyÍö£ÈtG¶ÛÕ­üÉ­œÛÿªóåB^¨h13„Ð!@„@e Á_ÔjyÉEjöí×}ƒÎOí1UиÇ‚þ±é®±UÛXË7Ê!@5 þš@¹†ÓP¸ø^&ùWÓðoūᬖ›œ3•yÕ(v ºÛ×ÎÜÛNºü?Lˆ³£üž7¬[¤6):T©òîŒkÏ>:þC×¢ÖˆXõ*µ@é`ª"_eM·°¯¤‰ ° Á6du;‚< '1Å}~øæ—〜S!ÚKmøÇè;ùßqVï{J#öBºÓ¹=Ñ•v+2¾u˜/ØþµÚmÿZl­¯xÍ€<€Gž³ÿgËÓØÆæ' »»ÁÁùUsÜ)›ë6’”{B€ ê&$øëæïVm¹–úìeÓÛ¢¥C5 ÍÌŸœœh~}˜sÚɆYô"¤wï(Ãk´ÏÙ¦vÈÝÆšòÍÙ;L äìpTSv8:žŒnƶ6éjjÑ* ‡™ªÌ…)ßC£ûjûùˆ!@a#@‚?lÈvi1öhÚ•¦!pa\cråøÀˆpÿ\QØkí•î»\ƒŠÓ’/!@„@M!@‚¿¦®§é$x<í/ki<+þã°æ_qhÚ_zûg&&ÖýËêéïFÅ"B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€  0"ÙÝeh²û#0#\i݇Lrß8”| B€ BA@ …¨&h~¾¶M/Sð¾Ü½gãQ‚±CŠ`™Â¿1±÷~}SvUò’ì¾[p1Hòà‚çã±Uáâ½twÊgUáJܧûUÎø¯îäiÁè½\9‹1c&Â_ FÓý Cü“ 1,nÈ8„RöeÏuÉÛ³µcâB&Dw´©æˆg îÆsµª*Ÿ_9Úûçͬn;—k™¶Sÿz0ÓÞ˜?qâº]šÈå>Ñ™z5çÚ®ôÔ‰¿F.â\8¦‚_¸\Êòåéw¢3c¢':)üƒ;ò3å·)ÏË-üé_mE)šçŒw³ÖJï°ÂNA7×™se¦¢:þP6† q¦>¹À2!l~aD@ºïp…o #J­#Mt¹{3]ܘáNIªu™£ ùX6=®]¾ÈŸ¿çï{ÑŽâý°”ˆ÷’ÆtÝ`LW¶¼?CyŠGŸ0wÀ¨ …~ÚJ¼ ä¾Á0ÙMH³ç,õ}t÷'3ÜãÖW‚]XQrâ–GYfTÿIðA}éH.ÌïL‚?F Å[9Vý톎ݗ/Ÿÿ=æÑAè—ï@Nå.¯¡¯\>°Ýcâ–[Ôòc åÛ3R“ŸÏô8§fºW0Î=Lðq#ÒÒZ‹Qþç+é©IßT¯cÆÃË®†¢$GŽäj!ÌP‡ä³¼µh+ »Ðž×ã T?.ò7®ù`ºvap²òC`I[hìhìÛ T?+B†ÉE+SýšàL»¾üØ 'T*Îu§5œSIk+ÇdÄÿËÀ¶ý¼^ï›äMÂL“~ÎÿâüÕ·ô¸©Çë«sÂåQšÞ|ß“"óD<÷ qºŸÁÔÂbX!N†¹a$æ²3Ý)gtÍŽ/ðš+AŒfš Î~U466Ãå\>Ü5¥›×k|ãp\ö´kü6‹ÿ°”´>†iÎcŽÖç }ÏlŒ„Vd¦:gËðE‹©KV¬}JÇ]˜~ˆçŒ}É™ñnÉÀÌbÁSÜCEq>ŽƒçJÎÕG2R“¾“‰Î´M&.EGû¨™èð{;XÔÙóÜãÿð3°è„y…•¥‚IH£ ¼—¨Šòlúää-ÚqÓ¦59pØ; xäË+ðÇŽXõ¡¹IIûRÜäãøIv÷Ãó€Pø4n²‡ @õ±xÈŽ ÊÁ@~\ëÞ™‰‰^éŸàôŒ…¹ù([÷Éï¡ÎÔ!†ààõD”{³PØü©Îy2Lº`¿Aqè‘ÿ‡ºÝÍߊςbõÚ‘†õ&Ý¢~¸yñ|ašC*YòP/¾øp†šØ¬±0C&yú3Ó¼Gåê¥óS“¾²Å}1Á™êÆìÂüWÆ'™®Ä<[XÃ|•Š3#Źaþøµ«Ô5.ø¹¦Ý%¦)þ¡](0¢¹2?ï{ëGv»ú¤§«d¦¼RæÃd1›Šó#Ú3ƒ¹ àbþÿ>Ã}¦ÊBýÐ{PT®(# lä^s˜©‹¯°à쌹®‰ëÐÉ.Ðõ»ÀcJ1ty¼qVÉNÓ í1¿ºÝ [²b½ïw E½Ù¡ª[uÝ{—`æTøùf7$Oè –„QÔh‡¦~ë5¼‰¦i~:Ò5í©`@øÆCA „ùOõLEUïjÃÎõ+’‡tÅtH‹™]!h“”¾Ò(#¡”¼=Ü5ýøy®q;%„þÛú\S®AÅ0½^6Ý›g¾Š +4µù]ìï n…qޏÑSÍB–×LÞ‹†¹¦œ0ß5q“Ë%”í^ϽHCW¶ï½ñJ” ó&LÖ¼o☄ÈÔ”M[\hx¯‡R å .Ó¥mõÁùÇve³¤î~ÃT6sÁdg©5$>»*β\è;&2‡vS¦kBž†´ÿþô^15jêÿí_:šB¼NªJBß*æé/ÍÞtè ë;”'Òn2Ìåé%GãCRÜÃ1²M@~2Ó5f¯?>Ý£ÛÄ_‹EŸ§;Û%-ÒºXӔᙓ“ßÏt%ý aó èWyÑpŠÝBÎÌ»-cf̈ƒrr3F×GPB ¾òì‚Ô‰?IÁ+…FîÏ[ñå#l(ü© wÒs\ã³0:wÁ;§DÁ$™âD(#bú"3Ý5ñ/—ë2ÝPö?.jœz;:¿d™š6I•$¦38¾â=ûIDATé=C>1Šÿ'‡õ@Õ´™®ä_æ¹’Cåpƒ?t”'ÎsÈ-B™ Ÿt=|ðiרC²³v ]ï+yl7¦ôÁ#ÊÎËX³q£ô{È5«âõVü#ùí+“Â,p'?%•X@æ(LÄtJ+ˆ¤ñ¹2¿å-ŸrZ&?O,Áë <ÎÉö°†öŽyú‘øî­®rC9Ì|š£w¨üP¿O‡¢'‹£œo”ÏÅøý{ÍsMÜák)Ç~Z»rlStÒ¢NF{ø´DÑí$#¨†*)º‹/”p¶KcQç«Õù~f%/.ìP¨Ž¨‡#üx¼g Ÿé#ÓÒZK_~M&Û¯ßí0Ó.ƒÿÅY*é̼4Ij ï…ˆ›Kçv“ʽTœ~%ÊݶDqfRqF~ PðÅPœÿ­5íÇí'üv>ÅYòʬeRqnY¢8—°–Š3óYñ|гà©PœŸ……ñ¦°ŒÅy¬•`ÅY¸0¸ê//Pô¯9Vü–0É3B?“)Ê$ôË¢Sÿ¿ÑjÎéÌ;½Uu¦ˆ1\ZBå‰ÂÙ†.ÞD£}£å¾ŠÊF`¥½\)ns|ñÓ£Fù;Â8fúÝóMZi#B›cŸ ‘ÊN€Åj_B^º s¹Ï—߇ÞŒ°=lKå·ÝÉ HsŒ:>·û£SðóØõD Ð`´b^#G(òãÛè„ÀVœèÇÙþÊ?ýæz¿ÿÑ/YöÏ3ÇŽÍ…ÐÞ#Lî›nßžè¨òM]ÊJó´ã%EQºÍ®Øñ>6ë'¿¸iÞŒécàò°¸Nvdyzþ¥èðv¤»’W%¸fâ·mÁ±Tç ‹Æ'P(â—­ÙØåH:¥ƒ#þ°Dä™oƒg.¦`œ6ÿ÷ºtfLÔ‘ »D*~ãúsË–] £OùN*·ø=[£‚l FÉ™²mç„’ðr•ãPÝ>›1Zd¾{üZ{¶çA*”¨ŸÈp(é¯#ìÂ|q¤Ï£Þv’’æï7 `üŸÌ™&dÛùøßë‘â ëc?(ÿE»¾.35éé¥Á Paã®.$~¾¦íù0ñGd¡¬ÒD~AHyåì ÌI÷·Ó¦O.-?ŽÑ‚ÍAa 4+ÂòìˆL)DÇëÃPŽ‚17½X÷²»Añ=ÄóÿA°>[6ŽŒ-ŠT¬S0˜¢š¹G¸Ap2V`%PÈô(-SÞR¶ÆF7Ç!Ô,Û÷® £|ø–JËTüÊy4Füù°̱Âéž4•è_‹_þÿ¬&ÏûL¥ºûFÆ•Û/ïyò×K~_§í4¦à7ý «øFû±LÕsÑ»Ï";'¡ŠB9Á¡”`)ÃÊþ½=Ç! R<·g¦&Ë5 Ò˜E.ŒêU{á;½à§¯îßÿ”Ç{Ƙ1y0“7 Ngʰý2\*ǹzÎL©Ïw9¿·”ã (ǵ¸9ÞÜŽ¨ƒRѽXÒCÑ…‰ ƒµ òÛrX›Ráo>,eÊ9†Ð¯CÜ.¨Í¶û¶42)ÜÑV_á¸~_H&û`áMÿËJ#Àó(Å[tƒ)ξèX£!]KqÞ€'ÊçWœHÅYNA |€þæU´©aYú”ÅÙéSœ…7гz”â Åej‰â¼©8ÀŠ3¬ÝÐ^1=ëó‹J~!!€>¸f:ê‘‘J V„ó¹¶}ÈæÉpóun?¢wJp¥b‹F„]âOËÛõâ{ÐÎÔãÑ]¬ ñ+Ìþ|úÑq;Пå _b÷7Ä‘Î7ïŽuèÚdLNþÔþ77uÂj{¼êx†kðµR¶Çž–|÷Oƒ`õ!´(-G\l‹¸/Pæ&;t÷ÝèPÔŽêį $éÞ5˜y5Ì}…Ê|‚_*G(ÏtRWᛆ.®…Þö-7Ûý½#þ*бXXx¯0Ef¢+í´@tõÝï£'â[ÛÛ#UN˜GUÄ[*µø· ó7¥ÚE©x‚wÇ·oŒüý¡ÞZÊ1ª Ã(»X9¶+ºRù´ýÝŵYvžªP·Ù¿Ë¾':=ãu¡Kḉd8ÊÔÑXL­qu>dòÍr* BÿF(¹»æ§:¿(ËËúmÅŠ3/Vœ­¼cDý¤¢*WBqþÙâà)çˤâ 0¡8óÅ—÷:ù+ä' â ü+Î`ŠâŒ´®ïçÐêÆÉűòC^ ñcõ}T~ÞÞë"‰§0[Ày$ÒèÛó„UÁ.c^szBÊÔäh5öïBýð}èxÏUì"+͌ǒ>G§s°‹9dKæyœ[­0ûSv˜r ”œ3Ä\Û/¬c«¯ùö=`I,E'8̸âA¬”ß j-^×Y~Œ0 ÿeˆ5s=οí´U}ï¨9?Ëòz–{uñ8Vݧ¶Óœ_îÐgœÈ¹÷¬ôÔäE’?†^_A §$LÚÔ2‰Î=:ë¾2›’·JæWÑ )r:åqD:Ç:ŽŒP8Ÿ >`aÑ*ѹå{lûÞ+±Ð`øÎ¶v„R¹ ¸ô^sñp×ÜÈ5¡Ä«/4†‘+ÛST¤ÊAwÖ’Y1'ô{¸ ”À ÞK˜ár=7Óå\`š’v¡)Œ31Á}—å/•cì>xÊñô"ŸrÌî‘aRÑ… ºDÑu–Z¬gÅ õi2s’0ñÏ—qän•ƒ‡x•&Ÿ›Ÿ:ñgXVæy7ÃCNQ´Î•WøŠ3­4(Îr}LÀRqöíJ:*çœý¹GgM*ÎÜ„5Á§8£­HÅÙ)cHÅ y¶çï,.á)Î|.Ö¥‚[,ð}-!#ã²pÚ•&=ë652â/(Ø{6L☌œÃªñ‹#Å]Ž`£ñ×Cˆy…©Sè=´B{4æ¦oMw9´Òõ™õÑ €¹Í…– g³xG4ïß!ô>a[wsc´ý°¶ßÝg@Ǻ>Ë«ïß3ß˜Ó Ema§«ŽwŸÀŽå×"Àè“íº;û°×B ³øc¡Ô»° lb9Þ-è¤ýJ *Fô¢»¢¯Ü—ôí´öK ßÀ¼Ê¿·”é/ç^aÎÄ*tcÛ²'å—JÅÑmš¦ÈðpœèÜ&K‘­ëž '^½ ÅbÓH—[n+L£ƒ£ã²‡²ômŸI™ò—kQ”T ¡ØÝ Åï-Ôér1¬•W©ƒþ Ð`J©´r ÿiP¦ï…B7Ê\c¹&Dn”GY[ñCy‚ϯ°&œ Oô{À5­Ãú<«£”¥JòAû›k…\Lwi´¦ý7ÞÁh¤â eßRœ/•#øDçô“†¦x|'…úÒƒâ,Ó’øX|¤âŒ¼}…6w”âŒé¯A¨ßÝJ-8”Š3cƒåŠ|m‡\¤‡EÅPœEXŠs³&Ž!H·5ú .È54P‡"ï~¾¦Ý=0ï¼9‡¹Àƒ½ÿ·»yäR(æ,Ûq¸©ßü]Å1RŠÉfbg¹FûæAƒ±“˜ˆ2 åžú`4Õå_\ÆíããÔ}r`Y¾Ã¦LiÞDQôéãÇ.ηܒ×Ü›´Ç²„·¡Ó~0‡Qùù‘Ä£óiÆšR •ë¤öz`WŠÀö0n„µQQx*v›¼X62¶n&c*Á­põ¹cÅ />Ûbýÿ±0{7‚…v½EáÚõò˜Ùâ¹ø‚œ(Åqº}º«¬ñlñÃÎð–VM(Ь1”è]G¶Œ2&·æîËÛŽ°ïa€g@á}vŒÃéž§Ù 0jÞ´U†§½ÈóÑ©@þ1Ö²5E ÜÎËd<Ùnôã5àt~»Cà×Yú'&»Gc=Àã˜臩µ¥ÒOö P¦ö@=ùÍ~N† âôL„°êÜ2ìZ€â<ÑZ ¥ü}…óå˜{TÒ[Nú#¿ßË¿ôƒõ²'Þï9Sï¶Ê`ÑÒ³~#P#‚ù5íÆÀ¼'_D]ïØK4þúëFD!æ„@-@àýé|ãI‘Í _8p¼y8iïÜh’WÕ{ªCÑ•yéwÚq|kNBZ¶{¿Ù†30¨NÁGŠs°É«V!P3‚`›G –ÎŒtÉIðGaâ_[¨ ÁQã3ýÇUú4ÀÚUÐ|`¾|ú¥¼s›³hž;(LP¨‘Å}0±íÅ*҈‡ùµ4Ú(Äļ!}/„VDGü˜'ø´Ò±€41Å“€¹ýÓ€ß}XП„þ±ø(Íc‰@,îcÚ‘-o‘*,:)ÿ¢H¥A| Zƒ€à¯ïX¬úG­)oufD°æ8ƒã×Ôó0·ÿuu²&^„@]@ fFüm;ý³¶âÈW¹À&2Û^¾Œ gâJÔ> ”¿Ä²Á‘Ì™"hSõoÉ Nꤕ쑬8Ä»Ö#P##þÞ™?{±:õH¢Õª¯G’?ñ&j7–í©(byâìç«ÆlŽbLÇ ü²tØg‹X"æ¾=çý厱„‰1!p,è3<ûFý/F*m(Ò‘l¯‘Ê6ñ%¨1Áÿÿí”ݼBžÂ&Q˜Zá^c;S¹fÒÙÑ`O—Þ êDÀýøUû=÷Ø;¿¢×_ªÎ¼/B€¨=Ô˜à—EæÑÀê{JQ}üžÆhÿ«p8fé_/OœäNœcE+oéö£Uú”níEàŠó·( ®T¯+R˜6˜ªžó0|'ù¹2bCÉ¢<7?ºPiÂá'õ-ïÈÙ³£K]]1…µõ.¿¦ÃE8WÿSÿcªŒ çKcµÐãõÕ!Ïu&¦L=ÇîÊ3äãÚÎ󫜇3{åQÀ8…+=ÂIû:ŠÀÓ,†T5ûéã¦åî«Ç/„ÃKKk˜âÜA±ýœzœ'oͼÒ*±ûåï´uJx_ð…sõqM4¸%’ýmsc <KËN ?œî·÷Ù_V¨ Ä xऽlßI{ Ÿµ 5ùU;?y™ ε_…[t†gxœ¯ØÃ訋Ô̪~2½ßËúzùµíC¼ ïx[Px¯œ}ÕœÝÒã…Ð…¾/á,ån²ûG¤LíaýY|\èÚ)ØÛûOt@°(Á y»Ðu²³ÁÕ¿÷AYé‚Î!Çpf1æ(Ç?¸Âaê`8®ËÒAëöA–¾þ ;гýY®‹kß’iã“ñhÒ¯Ø[¼$ýîaœoþª]±n¡Â¢¯Èp[?t’ç*\V%•Ü+ìtò=XZvºPøÉk ]lÚ5~ëÍš£ùò’')àÍó[œ¼·Ø® ùl.”¢ÏíéÐ;!P—¨QS¿Tïww.SÔ¨s àü7LYa=ÑÁ¡“šÔ»÷ðñ®£Î‘//¾i´Ô´wHS%:¢)¸Ôâæšó4ó?q¢ÙS™®ä_ Hæ(t¸q¬±£[mâ»YÞu·áŸÖ¸Ð¦§<‹Ü4ÙS²3*Ñã\ô—bµ¸^Ž8£˜wtMë”1Ùù‚ž‡bá^àqž!…¾¼àêTÕÁo`Ž˜^ò~úOW¬½Ì*=œ»ÌãÌá¸ø]^8“6׿jZŸþcŒÿ†×Á´—aºñt{\Œ ¥âûŠúÒ_ìy´yƒf)·tå†sÑŽ¶H¡/Ò'' å·‡œâ*Eˆ`iÙéBágxÙèø|k ØbÝì˜îtnÇ"dCÙqÈçÇϽhŸ¿BáßbO‡Þ ºŒÀ1ü°sÞݶ¡÷{;/„À»į‚Èy>…ç¸âèÑûýÝ©Üå ÿ(À­ûp“•øNž#®Äio¢‘’B¹$m\e¹Âé®±»1€Ósï46öÀûy!ï2Î~Ã…m¬ü¢ƒ‹î ]4AÞ@'Ãñ=#§tƒã½8¾ BzãÒß×]Xy9‡¼v³‘ÃQÞ?y½}¬4ìO‘§÷Ã÷»fŒº•Åè^Üt÷ÔyE+¹ŽÀÀ±Æsª×ux&ÚÔÁàØ„‹tìÚõ´Ó<âý>ú£H¤ÅL¶y#Mé@ÞAÅ/,aZü¾si¨¸Üì ›ŸÎÎŶ–ß©,mð´ŽPVÄÏ·°J ÓÞ8«ø-Á•vŠ´¸ùÚ?¼¤ò’ f[¥¡ï±ú‰²Ñ蛨sÔ¸©ßŽì0ðý²üûåºv§adÝ'ûžN«=zÜ5.á·|©ßDZøOOywm•nƒÃ`÷ “9skô\ß@¤ý§«Ö_‰ô?„ùÿS\û®]ƒ–ñË:nò¾H´?;¬cJ—žìr/KGß «ÆæìFÉÇ®^Ô#yë_k/7˜y!„Yw(¼ÍQO ,†•á«MýüêÑÞQ—ägõƒ%X¼¢ ¿EÎëˆÉÞ‚£ï)¦ˆ‡ÕÊO'3‚úŸ‹œú¯® 'sñÛn~{Úèòù'°ó•‹÷ wÊP¹xØò/Ћ2×F/HLôâFAË›ž„@Gà˜ ~;zg¿³s ¾å_·#ˆhÞ#ÓÒZäç Ñ_Âg-(âü,*º>ì $}±Ýôìüý@¬pZÖAéèE˹>Ì->n v3ãæ‡Qš£ïÓIãìLà‡q²ÓR¹22ÝôÍßâ·`| ô‡cä.p}祒óùoK ":IñN†;å¡@áäGHz ò­}ù¯òÏæ¤ —.|ƒYq¼Ðþ‡’±ÃyÿJy•yã¡ —¶>N ß/Læ§+æ.šUMJÒñ3Ÿ†Ögæ·³+Üý 4è÷Ó=)ŸIÿ„Ïý°bü™9yÂrüñ¡N˜ãÃtÆ+¾‹=:½u cfê¯i” óÍ;`C®žãNÙ,ÿâã£ÊºÜö³‹¥ŽQQ´¦5›pÅéÝßv¹K“{±“ ÷4åAÖ©õÜ9®ñY–w 'æ2ßÇb§! %[˜äh^Îïûhƒñáâ …³å’FÞé‘ÿUo“)›ÑUo}£çyß7ŒHvw‘~.—P伿NOB 6 €ÅpCA=ÑÊ‹®ë' Þnµ¾ýO¡ý…vw‚õ-ë3Úd­l·üÂz–ï¸MŠËÚkíÿg癘âž¡_€sû§ûýMq#ÚZÂÉ?ä},† 1òŸç§¡B Ž"Ð`?VÙ߃)ƒçí¿Ó̱cs¡Àÿ/ßȽ•{}#Œ“týÀoXe¼1!Ùý¦úöƒ Q¼æglëž?œî è³ó±¿+U'¦)š1ïž-X1¼Â«ï_›Å6µ+ÎÃ4Ç#àý Ëo@Añ¯|ÆE"o!ïW ?KðwQº;ås¬E˜3ÃïàÿK–׳›çxo¶çÞ cÕzõöv¿ÒËø½Lar'“Ö7L©õ—ï©ä4U³„”©=å÷NÓs¬f«¤)ÞN'Â9;]yü¸wÏuðŸØ•z´© ÅwêàHN´ó‡0ƒ„&Öæø3±ï^,²­ôÎ ;z'Ž%µÆÔiÐ€Ï ”ò`i¾ƒ0_‹aù¹sÜÎÍ’.1%í\Ý9‡ý\ùöϱÚÿzé/.ù}Ý:ŒÌÓ3’“߃—üó»’¹Ãë]®çb²Ø¾Æ™>²Oš!¤g㓞ì›8c¤kvüÓ®Q‡üÌð’éš°³G¤¥µœ›”´O†a«ß äyæ°Çf¶6ÛÇÈÀt„ô'GÔÒS'þ ¡º8K_»u¿ùZÝ:~¡ÌŸ7×¼M" ¯>«ãêÝÌÔî0à(»ƒÒIÏ.T~µc!±xÒb!wäxs§@Ø“¥»·#ý’ %m,¨roŧ'!PW€Uœgy¿ù]s¨×ÍsMÜ Mç!\Ê´…©q+׸Ƴ¯®ç·ôƒbÌ[°[¤eò•Œñw<êÙ±C |·çùiÂxÁE9'¢÷˜-£àê[ÈX\ÂÅ/´Nÿµ_ÕËIqSÙ-H{ nsm+.Š .nÜT˜ßEÔL¥g²Êì•ÇWåtŸ±òKÜGñê‰Q1‹>uRaea9{vtÑ0¯A[ꉛîVr…ýÌâ´BQάß4*‹<ùHLñÜiz ž08­þVeóWñäí™…zöZE]>Ï=þêä]_xY¿g¸m´¾”¿®”C9–š©]•¶à±U†a¾‹kfA0ú%ÃÝö½îôŠÂõi™Ê3iÏ6n]™|+\‹Ç%<®¼ƒÎéÁY.ßy,Ë»ý'Ÿp¨ Óã¨Ü±i¾"y­%Kpº?­µ™ 1iEÞ=ß þÜ ¡tZ’zãuNFVl÷™+=ŒnLLG]?mê(åX4fL €¢ýü¦¢‚ͧ<¾úžÊ‚P°ëP:ЧpyÕ:…©ih­»„É®íPжÒÊ”ˆÛ0ÀlÛy¦Ç™XÙ¼Uw¼–ldx.Ћ¬êæ]üêZ­Î²¯Ð8ªS-ZÕ¨–-»TûnÝ—OÂ'(*:¬û¸7w`Z¦vkR‚þE8ñ-ZUmöò<×ÙˆÓ‡;§êEk¸¾û.|Gl48ß=~-øË¿:ë†9§¬³¢³ëJôxÚæç›?º3 ‚é8ºº’õ°óÙ}æŠSÚ÷&ÚÇ©áD†Ð^㹓g®¼±YTÌ?Œ:éP¨ñK®³¾ £‡‘éîä%ñ‡Ê;yÅn–î¾TáP"j™s¹|Ó#“jY¶Je§®µÑR™§C ÆFÆ9ŽïÖñ&ºàU¶”ˆÛ&ËO¦.PMb¼SY>2ž4Ùa àW¼ž$¿i7šL\ óâKL˜31rêí`QgKº¡ÎÔ!†à€ìD,ŽØ,6AªsÞp×”n^¯ñaŒÃqÙÓ®ñÛ$é|Wýšæ<æh}×÷]-˜9$Óíì/Ã-Z¤.Y±öQŒBï‚I6ü¾äÌx·¬hîòœ©ë擈r¬»‘¯y©IOÊ9χŸx¢Qξ¼ßxí\‘£ÏE§?BnFI¥:M?C¹Žéæ,Œσ™w5&<Þî×ë¤' $mÀ>—˜âŠÛHŒÜŽƒÇJÎÕGÞw‰“<} Ãû_ÎY³!Éîõ’˜«<™æ|MSnçJþMúÉ2ë¦ù¬¢òá““}Ö¡.Ïé†W,nïè=}üøÃ¾2yLïìl”ë7øÇqZܘ']”<‚ýÚ"Ã-¡Åžt|kýÎèž`/‡¤y*9yWBJÚµ™î¤†8ÝÏc¯·>=OµælÓ0>E]mQÙrËk|Ñã‰Õ}Wî±?4>—šŒ}kqüu>P¼òê°>1Ù}ñvÝíFýŒ5™ù êY¡â`·e¸œËítò}¸kz;]÷ºïËðÙõòg¬˜“99ùýâðàíF†ËúúÅ0'žŒ9Yß³Ñw*\¼•îN±”&•›íú×ß«Lu5j³ím…£d¸ÇùÚÀД)g™¦1,¥B¬ >¼Àòo™F¨å–´ µÊ²“«yjÜÔ¿ýº¯J }?D‚EÂ|5-ÃtÎÜO[ÎË®iÐàOÉFIy !,`4ÂõUÓNm£»>!Åý„~*ž…€?…)°‘œàôŒçš¸ñ躴øòw:”U™®Ä<És+pÉŠõN¼ß-uC‹î>ã[ý©V¸|Ovw†ÐÿÂ|‰3µÊ& £?tRÚŽ¥ZøýDw‘ã}‚|æÏW«óe˜ÝùétCŽßf±¼‡`Ê ðzlÉÊõƒ-ZŸÐ,‰såÑ(-êd”ÿSÓ4?éšÖ)ªU“¯A÷0°ÊŽsÄýCþuPþùdo¶¡³«, ¿¶Ölø:?éùå«Pþl)ô‡º¦tÕ½â[àø§Æ¢C€w·<=ç#äQƒÿ2Ìr’Ö'ô1¯Ü,ÞñPY¡oÑe¦&ý ß±4ÌÇÛò¯/ÏÓ_ÙÙ0ôG¥…þ,Ä™PÚÞ9'C8Žø“#`€ú7Ä<©¢¬¨ÛãDµÿkn”~‚+wÉ:Ö÷´îR)?Êy½EPš…êpD]Âcùœ) 4EñYÔBKS´g†pAÉèaÚ×5‚ó¯ÐƇÛË2¿½ e<ÉÔZ~VÒŽºiÜŒ’4².BG]fÛ  \ÅÅYÈøS2,´€†½1Zkú’•/t¢'rUùF¬¾ÑƒôÇèà a¿`Aj²¯aÃkOFñNŒÞŸÀè}!gæHøO‘ôcf̈Ë>Xx3:…ëå÷QNˆWžZ:ñ§’°¼mö-Öc†|[…¼»Äo1Ë„0‡â;Ó¢Ãs3,ؾ¾BÈÎÈp§ÌƒÛ0ü„5Qç==aÔ!Ö8ÛÑìÇÖ·ÜZ$¼ùm!r–X~ò‰‘ñ'áN]¶fc—X­ñK¹zÎÌa.÷ùó]ÎïAèC0íÉHu.…éÏLKk]k4‡€ü¼T“»J!Ô#åæÐþé tÌÇ!ß'Øã©Šò²ý;Ø;×8Ìû¥Üfà Múìa×-r¼¹1ú¾é],ý ÔQF9Xf'Êï@Î4ÙÇ(çX¹º{מ)çbÄ#0}ð!Ïg+Ööq¹}›¥¯?urIüÒ’`ç5DzŠÕ(4½àïüP˜JývzCx¥Ò0Lq¨ÝÒ'Lȶ‡5¤÷SžXuÊÛ'e{úì•Ï­ÕÓg+Én˜ÑæLI5rÍ{¡$o÷® %ð‚’9ñêp°4P¢Ào‚?œ‹oÑž–hŠú€a‹ l\i9¿YcÇm `ˆiòŖЗü3““w€ßºOسGP¦æFŽq-Fó¾öàσÿEô€â\ª.ûƒ 1¶x¥6ZúŠ 5&ø³³÷ÝRm#“Rp˜bäì[Luçþ#»Aé‡Ö¼WÉâ¾ÒG¾v¹\—Ai/v±cæBþA÷b,¡ŠB̢ߚ`Þp±îew#ü{­ÿ2óY¹R’\ÆE*L©STóÛGhA)bÎäÛ£9ís aÜFɧïýÙÛìßÁÞUC+•L›þÜ2Ýg¾ÄÀ[ŠÂÖØxÌq5Ëö]êõŠ3ºÿð銵……{ÿ“™âzäï-9Êï˜‚Ý°ÝØ ‹TЯg·3ð‚–‘R8úrQˆ) {},õ”J”³Sðý•á5žÀÓg.Þ@>`¡IŠDQ1%å-äcÁ[Z–Br%{ðêšñ‚é-\·ÃL» —B ©Kd_‹˜Äbí­p숗ïóS'þ åâ¤í¦çÔ»‰=:$eÊŸ-Ä4ζ[|­'WÙB´® XÆïÈûFNW­Ow9´ÂK?E4ãRmÊbüô%/ÔFË"Bß‘@ÀÞÑF‚ÿžB >òQíoñ¹yÙRü§Ú9ƒ¡êoÀèþ |~g¥aêâ ÷Boû–›¥WøBLe¼Óõô"&.ֻǢµ?Ÿ~tÜLä _ÿåV˜!|‹­O)$ÿ€ ½ÔZ$çˆÀ‹4™q¦„&Ð&c²ÓšÎ(•’`„véíaRÈc”´‹¤.E~¯EüâßYÁÞkӜŅ‘ ÿ%Ö<„Uà:;c¹Î¢Ð[ÔÃTÙvÿ ïš22Ö!äç‰_a)g#Ì J[ON}|Íù†©‡µ‚?( ¸Þvés›ú|ðñáÄ›ÿè˜= )žý˜«é‰xK«Z‡KFäÃå¡Ä¢°a‹QB§ƒ÷W%ÍöÊ…ïg™_‹æ·}M9íÆ•g¥-ýÐö×A•í(¼*yÄOúQ † ù‡‹@~yæ‘f܈9ŒRäüð"–ç³1vóÜ«Dç–ï±í{¯„`†Ö?;31Ñ+ÓÍx,ésôƒºÎA§°džÇ¹5P~ä8[vTXp_Â$Ï/¬c«¯ùö=0Wžh§w0Ç,]xï@žŠaSoÓ4«pOΙ*cQóS“¾²ÓVÇ;FÜÓБ=˜%GÕZ¼®³üaþ«(×Ìõ8ÿÆ|úì°Þ».ÅÊçÏýi*\ÎóE±b/ïuòWÒ<ÑQ9ii–±® %ƒT®LõÓjê‹ÌkâôLl¬5ÊÈemа:#§õ¬cëwýt¼ÈûCRÒnã¦ù1vü <*ÈÂ1 Æ´ˆ¬ï‘sBÄïØ›÷O$ðY°Dä¢Ï"Ý8WhQ_fºÆì…Â{<¦‡îF=êÀ5å/Ruxè$ÏUš5úrÖèÑù¾õ4Ùm ”ú,~UISZúÐÞþkr ;'*¶´’jÇBåê\Ý4~À:˜”èXž®5ò²ÜSäT_Uò`O£ì;µÑ²ˆÐwe€õ:òNˆÂs" æ¾#šFfªsLã™õÎb[ödCè/B™>Àâ «l>Ã9Výc¨>«Œ1 î°= Õïàó Ûºû Ì‹nUÁªy›“[±e®?è®/|cá®C‡aQXjq®¬Ú^±%Û’”9X36Ë«ï߇u { XÓ Em!‘'±a¾cš)ø›P\¶Ai(ý5S`=„莸>3¿ô“‹»0jüs '›1Ì¿^"Ó•ô'‚¯……àÆ\=w+Ó ¹®¡vO\m)P2~(nAjÒøM3Ló5¹g?”8õ…[N#Zß}8q!Oý ê°V&ÛîfÊz‚úPT„).ŒƒOÀ*ù®$ßtQ¤ê0~óÛs÷çn‚ÞŽõ4ë¡hçbeþ8™Ùª¦¥©Ï– $Þ›”´/rº‡Ý ëFba®¹k|véºH®Ž<K“Úh0dÈ? S"ï¦d( £z:Â)éɉ¡mCªj>¤iîMÚSbj¬;—빘lv v–kôþò%LÚ4ª€Çµbç?(/NU¤ù·%êøä‚«,Çú–r1TUÒxÈ5«…rÔV> 1îÉ3W¬„Ruz$ËŽ9ð¹Žé%R–ëäQ¶†÷PÓ¹îämGV%uk ÚÈé…`éF"Í£K†Œ°`—5n|ȾXТ‹T¨ZÓ3\jDð§e(ã AÃŒY׊•˜ø³ÏìÙ”ˆ;!plÀI}aY*µ»£ÚsÄÙÖé5¸ÚùCB€8¦ÔŒ©Ÿ³šÑé$ôi]¢Äkhìy‘Næóˆ§é2B€8üè@þ>:éêõAG¸¥z97B ö"€u5Pß#ßnk/”3B þ"P#‚?&:êçÈCÈk È—‚R BAÊtÄë;NFµ‹záŠSËʽ|ÛÞÁ2Ì¡ðõª#ú*Ü1~!òq6®éÛáýæ&fwåñ°ÓÉQÀ(ïQ÷”V]ù(þ½ù Mz••ƒ‰•gY'ÐÑߊûˆÿ¶üjóóχOýKS”hS«šOÔ-ßèkBú2-tø¯2aüÛž®YÈnAÝzÇ‚]mÃΧ¼ºQ^;)¯}ÙùÛ߃ő#YÜA1-&V½ mè< Ò¼Þ¢ç}qC¨KVåµÝ,=-B¿]Ç…ç!«-¡/ïÃÀQÀ]Ñ>/B[¼·]¯ÑþÁ…Å»<œ,ù,)ËB\8P¶m\å—×jø!ô;Ð;¾J;‘‘Œ|6íús?z©3Ô¸à4huQ+qö5-¨,Jè pD§:`b‚ò‚¾ùîñkQI÷á ­t uÝ×IÉså-¿@O©•£qÞÍÅ0(w”¥QT6~ó22eÃÊ~«ªãE‹é‡»|×– Cã¾¾Q#æëXä¹øÐò?FÑõ’ÎÁ´—a¸¯zÙx¾åè=Ýô?+ ÷|ÿÀ˜yšü–é@P¼bfÁž‡€¼E†Íu%­KwÝ-ßK\SxSëÃz–Çâ‘ϧ]£1î¢8¢Ÿ±ûWV]ùêôôŽä(­¸ë+Ëmã£Fj“>ëÆœ¼7d<ê5èÑ7Û•i´“»W-á´ +Ž|–WoÊk'åµ/;û{°8KW­¿t+ŸNJò­MJŸœü1¬‚]¥9>”ºd¥¬íJ>yŽuÄ·Ú¢·}GYïò‰Ž§ž£ç+ãÊÃÉNºtå†sÑò·d¸Çùêˆ, ÚE9½ éd‡ÇM\YJÙ†¤xîEþ+ò´Åž½× ä]ãNž°—”`&¨ª‚k\Ù†02`¢²½ 9b{MLЗ„ÏGŠ»æ_CÛt$ž‰ÑÊ‘Nêˆé7Ü-Þ·Ìý6Dzù݉{¿¥ùÜï¢b”ï¡)ÿŽË{J]²ã'°½H¡*ÿpIM©F,;Ox´·ÎùÉ9ß‚ÖÞY~ÏM°eÇñì¥ãùi+zñ²«‘DÉMk¼ƒŠ¿ÁjŒý |éH6 ®™­SÒ.ÀMkAÑé,Ôè7f_>;}fê„•ÑŽ¢RåµÂË «Ž|HE ¿d¥WêY L 3ï°"#uÂïÀªÂõ ¥xã?Æž¶J‹U`¡á3a­( 9;œíÆ™ü‰Žé9à·‡Ëjà"\ì>iÊE2½×ÔãPE»µ×.ò·ÍpÚFÙ<ªµ“`í«,oûw°8šÂ¥•Êß.ä¨õ"~ÛÐÍß÷^¦.ÙõÝú_]pãç¾\3ï‚§ûYL{$Ê5-¾¸Û,CŸãât¿ “ÿ£H÷LÖX}ÖÎ×z„“f=MnvÀßßæ}þ\l+`ùä;ú¸ËÐVVÉ[­8þg™²I«z¢Á¬s«4Ôµ:ÕNüejà/ÇDð[˜O¸ßx¯[³›OÁ W7£s~WæÃÿO½éލ¨S 0ü߸Á¹;-á<5My öf©ÝuM銸ÇwÐ.*†Á9a^ìÎTß–Âù+\˜wÛ©"MmÄÁHäÁÉî.ö°PßG<6/2½T‡ ~¹ÜdÍBåŒn˜ËÓ ¼näM´§|4‚Å+ŠÈµè½Ž˜\t,GÒ1 ¯4MóIŒÞî‹ââ?­£…òyø «ðRÅ|”,”툫l.Êb2<ÙÝ™™bHLëxwYÚºò½zDœµc{Žu4Rºr…;Ñn~AgÁcW|„ö( ÷·TšwÅE<ØqT ‡»¯á¶HŸ¹_xõA¨ÃoÚG¯ÕÑ6ìÙŠd;±§#ß¥Õ åá¼s0•wË’k±˜R¨B7JµÓ²u©,Ÿ`ßœvè¨wæ%Ð31÷Î:Ê.x_ÒËQ7Ò~¿áE˜çv©Ï ŒYþŠ)âñ —jÇX‘ËŒbkž0ÅXSt”‚¨lzQò;Ú—Ç Ò¥àÚ‰À1ü’Aƒ^7¤É>)QÜ01áÑQJô \U/Rõ2˜ÈΉjÚa§Bà7¸°Ò¦L™Ö|×ÄMТ·íÔÝ›ºy h‹í”¤)ëäà ¸ÿ|3­Áœ_ âm%BÅOî©sîñÂJ‰UyGw¶~ÊÀ/rÑ:Òæ=Á› ¿ÇÍw¸kz;Ìí¿ŠTÿgu°ìÇbÆ&•yãÑÁøÓÉLM~YÎiF·o‹yÆ òäM°h­gE<,ºª<«šíú†«‘þ·>s¨-#0ÑËÉ#³GŽ,²L×Yí×–ºç ì\ûHOÏÚ±½Î‰a­ã¹ƒŸ“û¥šª^¬(Ž“›4éÙtÝØ^W@IXøÝèÎùU)!‡2 +ôMR™†PÄTÌû—qUmvv‘j'ö4ìïÝ/D]ÿ‚÷•EMF˜×".Ë¢ T—¬°Šž–¡}oÁ<þ„©š'âtëz†:Óþ…é¸bµ¸^XcÐíð( Éñ ®ðý(‡¿ÍÓ‰&PcÈBaI¼*>>ê{ü@eKHñÜ,þ̜Ʉçм¡s~¯Œ%ç·Ác€¢E¿W¹ 0Ñ™:²ì¾s¹ï8OÏ}ÆM›Öê…Ø±ñ¡•~0Læ&%íÃh«‰ýqöb¨9ü>±âÓ3X;cˆ4àý†l¨ªÚ6ì<+ÓNdü!δk¥éÚÎ+Ôw9÷nï,Æ•™2N°ºdñ“SÁöþ[4rÁ±|·vp¥uGÙ´â-·â;Œ¥Ï³h…nž‡°/åw¨íËN'×­ j³„”©=%¦ç:˜úWÍŸ8ñÚøØ!õ²ô—®¼²a×Á€Rí„óLìî¸Âaűéÿº€Öu5,7ÏãÜ “ý~ÕŸÊvRШGc„—`!¢uœÉŒÖö}Ê2,ªMã÷°EmîƒOÛüüÒýÜüÔ‰?£!¿ÃWØ£þ(­éÔBýÐkÈßræ-hŽÕô.¹ÝÊO 'ßvà4ä`\æ£ s2]‰~’¼ùwa|òOä£;Êô¤?@Sb”ö ò¸8K_»arGÚèÖñ %—³GqæýîÈò~ƒ©å­±Üúƒ°\óJø§áµKzêÄ_ƒò°ÑÉx•qååÃÐÅDÆŠÖƒï˜`ù8¸ëЭß¾c£¦ëLC¹ÇΫ¢wßHVˆî˜{oÁežýË8åÕ¥¸øFäìË{%÷@Á… ý¡¼4TlxÅKhK^¯y˜«ÊXI“N_Òõý Ð>¿®:þöÁÚèÛŽko‡åñ>ŠŽ«w3S_ž‡1eÃ5uÙçê9½;h`ê³Ø•W6j'Jô$ª9Z•¦¸PØb´…©¡Ð–¥‘Ö‚²–I#ñ™¿ËDy6eJs»w èìqB}–b|JŸ¢(¡¦CtupÚ‰pxë¬õ¡–XÖÁ²u>”¸ÚOyñ†»æ6.Û’œƒ·‡…Ú¾‚ÑÉÇìüè ꉓÜ×ÉmTu,Û”]B F»x| æj4UJŒ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ ÚŽÀÿ±‡×‘ÐO©IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-ha-vrrp-compconn1.png0000664000175000017500000064774413553660046026014 0ustar zuulzuul00000000000000‰PNG  IHDR4êEF(sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|EûÇgöîRi"Aõ}í¯¯½‹ {{}%¡X \ô$—ê+Ø€„×^^Å×ö·ao *ذ#* ‚´Ô-óÿ=›lØ\î’»ä’\’g?¹ìîì”g¾3;3Ï´‚&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ t ²Å…£Â˜@' 0:ÿCW]úùú/®ªèQn“(Ž 1uá-Ê÷Ïo(ÐkÓúUéúÎÂçý½(0ñ÷æŠo¶ñ–¯¹ñK4÷Ì'ÑR„åañ'à¿—ì#h÷¼ˆOz…Vu_`ªhb•›©RV×¢`î¬hì·; ·¼J+E¤lœ·ÑmÞÔëëÓv¬4ªFJ‘ühað–ŸcñG7¬ÿÀþþkÅš]q^‹ÛDµ{]AAo£JvMO÷þq×Í7—6$§cWŠnkgÆluÛgž4 õ:üî¡”Ò¤”Èÿˆ”/+ ã|!Ô Í4&ƒÂmÍ%ÑT¶±ÊÉ~såo+÷MÍßñL¿±é=*EEOo’Úr_vöºÆXÜ2mZ×-åVoÓg•åä¬&ûÎ;ìvKþÝ;iÒŸ½kMu®\÷­â¨=ÿ1|øpÓ- _3öH€šö˜j,sÜ ¬Ÿ 2tãgSŠŸàùß àº{ïM®ücóChåù2óó_t*ªÆÜµ‡ç‹PYKÅV1ŸW%„,Ö¤xNS¾ÿÌ N ^1U¦>B)‘#¤Þ ŽGÆìAsPQfMJ]²i³u¢ötCÑ«,³î„’q…¦m¼ öžpìvä<éı¥Ï‰ž/c•/Vû-Í·9þ'Jþ.·Ê´LëMS—ß#>{6§M›õ©–P£¥!¦Ãîx²ï¼ïn·†.D–?¿rDNp%”š'Ò½©÷MŒßà¶Ó·áÊu¾õõRåúj)ä{šWÞ1;ómh˜|ÏÚ­=É22D#pßõ×W ‰l)ôÓ³ÿH4ùâ"¥ÔfÑJÌl!åqžžê ËR7Jÿ<+< IaIù!ü[Fü;MrÏŽêèy²^¬ãlèù2Vù°ŸéÏÏECvjœ ¶˜wåïÖŠÏq{íú®”bjšþÚP„3 }Jªá¶¯VÛùPëFÊ÷Q>XýÿC}ò9Öì„‘ÿÀV½ìGa«µzÑT·®rŠÓãï}x†N’ËL]-Éôçe…Å÷L =àšöJ,cBÀT³3«ËMHùš+*º?Š‚9£CýÉœ:µ»Úb< ÅælKGe,ľ¡v»/œœCŠÌ ÆìñóØtô<Øm'z¾ŒU¾†ì£A~:(>‰RÛ¹h(·V|hzýèÜãLS]‚óW‰ÿy"F¢{aôãû¢@ÎõìIY8'/§ÎˆlÍzŸ'Ÿ¡ºþÜœXÏ4Ñm¸r}ÜÝw§–n(¿ ŠÔdÌ:¸73wêÇEy¿ .2%À#4 š0,VÇ"€Þ/IS&:B¬Š&NÜ$})™5qÙ33P˜ÖX¼æÎëiJü‰[ 07©1ÿÃ=N¡pÃ=kÈŒÜ5ô¼½?#¦Ñ¤Y¤x6…i$¿sêÉv®[êLk(î-å¿ãoôq‘ʽ^¢:¯«V©“Ñ`Ý££ƒ™Ûû¹©ñ¡<Ѥ¸×Œ¶(K\DN3eÙ @B׉ðý %Zª¸Œ#³Kù×T·ÓÇ/GçUFž^J$ Ë85‚xlÌ–@Ó^æ„ ÆZzé>ChÝ óü»»&¶¹’_cw¨™9ÁóQ+]“™<µZwLñ(F3æ5Í—œ;;póZ·´´Ø´ÌØJ=¦kÐyŒû™s=2·àpK™¢¢›WÌKæYþ¼ëЫ6FíÝp#*cr‚;éR½ŒðõÔ4yÊ=99Íž"W¸éOij °´Tߟ]!F™#c5í È2rTî”ý eŽ{û륧 Çq;ð¸\ÅâÞËјx<€xÜç¸uÎY·O·,1sÊ÷ƒYÜ}ƒfÃký=‡ß^b̯B|…ixà¼î‘˜:PFaW¬g”|-Œþà*)Öþl;Ò¼]qop(ø»0Ì|4!,ÖW @xŰý‰æóÜ4;0iù6—‰})OÚJŒ?ÿRH?>+7¸z‰ézL›üÓ#äÍ' óýAÇ ‹òöÈÜüá˜fx9Ìy{ÉOÝ3ýAL¯¦ûÒn7Ïßqé<*0egÓ0®ÆóS/wV¿¯…Ÿyk‘®‹½Ò{물IŸ;nñÎ¼Š„â‘ÞKÜæÎs: äïcÖ\¤ßŠ¢ ™ÙÓ}Š×!In%÷)Ö Èò/0Îr)´‡3¼ýî Ý%¯±|Iþ†±ÄÅíq²wèÛ+ML÷A^ßñ÷B¾%JhÏÌ æÜã¶O×±ÊjßVdµ“Ñ`½†üÃ{q*Âû‘®éÐ49É4ÅeHò= û]='ßÿqõ“ºÿG¦ìbÆ+wmQžÿhwùçØ¸£¯aT¾PÖ#MwÌ3Â¥‘‡{‘6fn¾Cî˜0a‹óŒÎNš"Œoæs±)Ò4¤Ì&>³'û_ ·t L1Çlo”b$BªÃ‘'öÂ{°×ß#òùs&çÐF4Ú‚|ùÞ«ÝK¬‚£áà½PG£t1Œ g*¼dÒëy2ôyC÷³ýþbÄu üï»ÎüuWØý®!ûîgÍq‹<ù.xŸµ·Ÿ|ÍÚVé j XF&+ü»¢aø·Pw¶¹T'¡¢ÌÆõ3h\oŠâÔkÏ¢jóà>ÓÒ+†ŽèIåòn†„úéÜC™I·í(ÙÏ1KÚ¡{®«  ŒÊòœç˜Ó™²UR<7{KMÆC™!Ç ìF1ZD«Bý¤ø#àýGû§ínZæ[hø¢‚TXhŠé-šf7ž,%¶³ÙIÙ‹üs˜ Ÿc™â%ì«u8*Ö/ñìaœ7JK܈Èkn»îkRÜ„¡/Åå(Ün´Œ‡~;,%{¡¡ø&üÇìé)wÜ‚Ù9PfÁœ¦þ€5C…ïg¤Ó¹–n~5ÒŸwŒc7ÑÏvÞ “'Gæ§#7<†|8ÍÖÇ5!oDÚ¡A&)‘‡å ô"?«iÚ!qÜJéaZêi¤a_äáׯó`š>×—¥Ÿ4eäÍ4ÌÇn³©ð÷ (NO!Üø{Š¡Œm³F(ŸŒ†²®‘­öÖ0ÕdéF ö!‹ÿ”A‡òO«_N*y‘fX‹²y«qr­ç5Øš¦kAù«ó,4GÇ1QÞT•Z#þX×"Wƒù#ø}ƒ|w¸0­×Ð9t•ÛzC×È—¶’âŒÂ„Ú5ÍgAþ4JÓX·ú¦rÔQ$Ìd#¦%›åVhv'7ÊÏÍ¡ñá{&èìÌ›èB²|L ÝPbG4þ&iÂsZa0»¶N[xþµ¥êST:»¯Ö‹G#^w77n´Xvd ÿbôP£QnÍFÏè|»1³nÍ¿ÊÅ1¨¼_*ÌË!ŧÙ-„Õ ³õV)<7„óÕñΆ¨š‡ěݻ'ÿ«±­ˆ?²{(Ü y·jR1;˜û¾ólLîÔ=«”ñ2 ƒ3÷›ýϺ¡t+MŸpž¡Á}CÅÚMèí=’¼òþ9Kg™»z)£üaÜk^Ÿ8bVÀ_»¦€”C(ÏšJ¢·¯¢¬,ÑþêéFãðzH¾Eøz, d9£iwCé¾ÄiPvÖÎÉËÍ ñijIÒ#O/šìÕy¾-‹Ý+×móγhÎPo’^K/ ø;öIÁ/6Šÿƒüz‰0¬l˜ÓˆÆ,.p÷’­FéT„ÒùºÐ‘“j»£} ®yq» ½FÞzf#-©ŹvGDRÔJô§ã=ÿö‚RG aÛv |92 ‘äGØ#šøÔq¨¬ëàï‹¢‹ïJš:ë<ËÌÍ¿FYÖwd{<49öÜgÍ«=‰Eô(gÔ¹(cÆØ›¸, ¿VsÅÂ{—qT—Ñ?¡äÊßfåä”`Ô%*wd©9n1†uDu@Ö—QÈ™@‚àšI£r¦[™¡Ú)Ÿ£kKŠÒ9mµ‰—Sµ½¡Wý‡ü¤^nTªSÐÛ¶k^ì)&1…¥ä L«Xìú}‹ðŸø̧ðg¾æM‚ø=ÖO%zÂü(QE«Ì?PfrЈÁwOÔ ·2CÏÈ›øÚP·Ñu¸JÔ¡d.…¯Î"[jh€ÁKðÝEŒ Û£<á¡Ñ.Ÿu+3dq£]‡ ŒªÝ´ßסG·eýìúAž³c‘DÖÁpCé/»”Û‹$ŒÖøEÓÂp7³hrN­2C–ÜùŠÁþa6`X˜—½Ð­ÌU{ú—&«{½Ñkï8§ÞmôV¿ƒ8ô^c,<É1wÎï~ûóñÈ÷óC·rBFþ{Ì­Ì›Yœ%8-¢kt8ìNçæ±Ä%4ˆXäVfè9­ƒ@|¯µí*åohF¨ñ¸§)„x_/TúÆ­Æ¡~ÒGAQ® bðeaÞįCŸ»ï=[¡Á€“8Öm¾Æ˜•U#ØxÇ~@ö<Õ½öÈž.¨Ä‘£,©W×ùn·Í¹&ÿ’Ó<™ne†üëÖ-é¿£eA/LiÝ)š0h**ü[€2©‡þçæSÜnFîÜüŽÇÊ4OFurž•¼#_¡LB‘&&P>ŽÆqsÝBºñ?áþáõ&£¼äƒ ´/¬Ð´¯ôbiÛðÔéqtD÷z„Ý8De>Ø1‹Ç™Ö¢ Å” {^ü(¡[£¡ŒÞõ+hÍKÌaHå›>µ?%w@ÍÚrÓÈî!¦QuQƒ Ä5íöh+cG6ø]k^oØ^é”>Ýæ¢a9ö3X˜µ#»#þåÞÞ+s猊úWûZÉí³šóitöHíÁsûÓJ>¦ Sª½Ã=·Øöƒ> ýÏÔXµjã¬- u'wè^B̈]¤†³ôÊó1d[>î¯õ{¯FÆ ·¬¨¨¦{S˜—¹Ím3KU›)iÛ }ö^ªšpÌ:ᄵÛDÃHqq{‡ï~„ek¯]Aï<”ÅÁëÅýÜnZãZôK;e]žiÐè )±Ï ü³Ò¼ï¢‰¶ùPû­±k gtê`Íšíï ÌênOtüú})áé [ ‡Ž|8VštVò½pô;]”ü€üD˜1ägõ¹ÁÙ:£\ʨ¸e•“¯¸×ê‘]÷õ]w¡Ãè{úa]Úïø&L%¾Yó4dÐç/ÁzC§ÃÁí̾nª[•=1ôF|ïæ&(1Sp~ø­%?-ÇÔ¹{HC¸Ãþz²H`ôÂñÁ˜@ HJ±V…óÖ2’Ö QI5gR¸çÍ1âÿ«ÊËÕTZ ´ÇåÝ¡=À1øÿ ãþÝmŸ¦Šü!€/cýƒ5ã­%KÏ‚Rs|¸/M'iÞ˜¦-Ø=´+×¢ñ&õY·M\PwÐö55nP¯CÜHѪ=HqÂèÑû¤Ì›%gáAQÜŸK–ÑP£Þyû e S•Á©e*s4FEF;϶­!tF×.ÛÌÂ_Ñt9˲êõø'¥hï?4oüPRûWè6®¡®ÐðyyçŠPóH÷Ò‡…¾:=µ?Úy»Ûõ,ƒY*z§?°J)Ì‘â“õD²æäc°IvœQ£5ó¶|»±ê˜ÑÙ«´µ³ò²?r›Ñϱž£+b'4ŽBŽØÈ!…ìÀOé¶›áëÿÖ¼ÐZ‚3hÛp§‡ý¦;ïLß´±ò,ØÞÚ­{r½žp’gô­SŽ@úîe‡¡T?ø …Píƒpñ†à±q8b‰K༢¸Î}Ý›¸\©ëƒp[–Ôµ¿»ÙyÙ ÐȦõF‡ÒZ¸™Á ?ï5ïèÈ/U¾Ôê5$ …Jé„üŠ)¶â°Ñ·P§Ê&%±ÂYðcà ûìöé;K–õPʼëè½}‹üCÚKg¼×oÐ9^ü ›—É”ë!'¦Ñiµù¹±pÓ½éÏbz66§¹ó%¼©™næÛ3nO¥J®Î$…޾)6{Ò=»Ïš4é/·Õz×Mv«z# î"ÿ0ræñ&Ü#SÄ´©€cÈg&О°BÓžR‹emW¶Ó³ÑðΉ«ÌX´-Ms[-ê9Í)ÇÂÖÑ8œ„ŠÑHM‘w„ÚiÎ}ÍÜòO Øœ©£b<æío~¾ ~ÚÓÜÜ~÷X,&¸¾^ýWªtja®nddçxTG¡!•†­Q-u*ô“ÉPN¶Kò&½T¥ë;£m<5÷>h1|Ò×{èCä|Q"ò¿GZí¥¯k“þk¥j?iåÖiXìÉlu{$yßu×?ï‰ô0ÄüÙgŸÕjâ×yb ëO†×îíVilšUQfÒIèFÃÍZ…†ÝŸhäVÚÍ:Ü]ç éhPd¡¤ª‘r«Nî¤g[6W ³t¤ßC¡SGÜ<;ºM»" ­EŒF‰„Ý´$ê>¤¾ÂÄÏfM‰‹;¸¾FÿÝêè6­>BC2â,Bž¹Ç”:ÒÜLfÚÊõÃLú®ŠÏG«¤Ã§7Áú0ÃCáÅ’¬[§òý‘fPGÖŸ¼S¹vÓV¼„g ¼ké݇ÿöúáÑà6އ&b©n xšž‡|I£âgŠR“:MBÙ3yî`RØúywÁ”ÕÈêë°›]mç ÞÏ×Ààd³Ôº®nìD›ê¹xDu‡ölXŸêI^ÑÐ(RC2ð3&HX¡I¤Ô`Y:Ûn*ˆo””©zh`‡mˆÑnB¦¨º¦ bL.+·î…õ â+­wJÛßRe} Z…Gà\O¡‰5þÝÒ=ë7mÔ©wÖŽc$™ÑÞI¥F¯Ç‹¦•ë ‘ Ì?Ç0ƒzžÌ¬Ô«fÚa¤gDªÈÙ¶ ÜÛÍ·^¬§v®*ÃÔŽž.¯št‰€ß@H›B㛿…šµæ=¼Œš‚QŠRãä‰LPËeØ¿–„¢JòeÅ{9’LçŸ~d :ÄÙ}ëë|»ñë~„FùïtOÓÚJŒ|ÚØá(þ•ßæþ€_õˆKÅVÊàn÷tíñ‰‡M]ŒÄƒËpk+4ØJúÒ{לí”ú“0…æi ”ãE¹›!<;gÛfØþ8x'd¸ úë…ãö§¡ëæÄÅñ·ÄWBS W;÷uÏyâ)km]óÖ¹ó¤y7·šS±ÕñeÍœHÊr=ò€µGux7$òº0S”F]îÁh ”P{šçst¶G]ýyóÕóFß:õŸøÈã÷[7”‚—vEQ »vKi²ÛÜC£Î‹8õ}›‡œ‰)`42ùò)Ùø“sáU±瑾q¦Ò1jiMÄvàÏ Lú5Z÷ѺÅûPŠõ‰ïFë/Ûcí…+4í%¥XÎOÀW•jV‰­Ï´H‘E×åŽP Â>¦°•èKŸÂÓ4Ì›8 ˆQhL Çw.^Ÿ—óHXGÍ2¤Æ µTÔS4 ŽzØk¦“õ¦oò4ÐkØŸü ÷|«f{4… Ä"fp”@ùY'RäOá¦QÐÇä ”­Îצíx_`ª†äk씢ù°C¿„;<•Z*˜nP`>òhr:®5Ÿ&ñôìúC<×(ÔŒ¬ÙÓYÂAøCì…Ã,+ÃÂkl±ïœÔáŽuCØrXy1Þ7¶(/çÑP¿°±NsFhš—Zyt‘ëð ò¥mÏë]^k¿/hʦ=‹¶ùåøöÐqP6lÞTqx¯qt6&Ò ûîö)¦vnÂ(ÆH{šnv: Ž-¾º¢c%_4…:Û¼Ÿef;„™„¼ßÑ'°8Ÿ3¼¯”èÅÔ‘q,íÔ‡kLㄦ52Ý,Œ³‚~B™DSØn4u“:£N c-¬Qs܆õ ™@;#€ >˜HÕ x¹•YÏÌ@þ?Ãˤ"îFUbüD£áŸh,ΙÌþ¿$¯ï_hlÀ–¨÷чðÂû×Sk(¹ÆB¯šãKˆ[Û¯ cëù!æöíÈ@ð 4hÃ*|#S¡Wþ>X|Û6OÇ"Ÿ¡žÈpÊÌ6¿åBº®4ªÐî¸G•¡ ‘µgº·Ëô¡Á9“ýÏÍ ä|Oe&z–®ö´í)±4T™!sÃ0lÈÄáaêú®4ŒóËË-L=ÃÆ˜ºÆŽ×ƒ&†5»ibómsãbhXg„ xdî”ý·] <¬í+YÎNsÌ -V{Œ(ömÐlY^ý\^¸ecåiôîAi}"\ÚEò‡FvnïÚåÚm'¡ŒÚ ¯ºó^Š/O•: “ã Û1¶_šˆzýLÔñ‰$d3Ìk>Îú╊­øÏ„WG .¿Îš<éã¦x›ìí>™ÒþŠ “QÍqu l‘ $(Vh4aX¬NKÀ^|‹u4»ÎAkÐÃV¡Á÷f0ç\Þõb¾û2ŽÒö¯Ø½ £4¢‹®›OÒ‚þ:6ñÓ—¶Ã”žð÷hT¼›ñ=†g›èU=g©¦’!¶Eš”˜ZÝC]c‹¾}béÂ~^Ï! ¤©T›Ëý2s§îM_ê¦/‰7ôáGדFTÅ7ð„m\Ò;ɯpa¶34( ޾R³ì$ú‚;q¡_¤ÍZ*^š¯zú5Zé‹íîp²üw슑•)n³Ðë´4ù8òœÑ7úû9¸¶0¥ïÑP{¸ÿÌLCÑz›Úƒâ«V­›J»‡Õ6ñ¢¹q¡`ñÎŽ¦E÷nhÚ61¨åÒð]”mÓ$Ýöšsi†6ŒfI£¡‘ü¢ÝÖÐ8ÿ’žlI{J =<É~$slÙU=Úb*»lÂ÷«ìéfŽ}»3GŠЈ?yõ(z›Óµôwœç£Ocþ4ù¹ë÷p`EÖÄÁ‡Æ'kF+cö’¶Çú˜I¶–¸‡ÞÓh=iŽÛhÃ`{L Q Ä¥“¨‘c¹˜@Ì”„!ÿ/"¹ÃÂó|êÝŽô¼Ùæ€2óvŸ™„݆ Áñ¦™uÅœê0GSäl4.FºÃ¡†aU™ù>y™{q4¦šÍE|΄"tq‰¹àv¸Ëq»mè­1å¤vm6^(1ý0¯~W4:Rqõ ÚU±Ìón(âðÅ%rþ—H£ýô2ëS¬eyy©3÷Vªê4ñaMq2òxØFmz4¢µ['ãÝ@–—o†óˆ9Њ‡ÁÊuÈOà »·‘ÆøÆHðT¸êEˆ>ÂzcÓbQíª¹qA\A\—ã´‹ñ®?y¾ÇÈÅ›6UœóÝ!ã—ÂÛkVsdŒävæmûõß•¥‹ñ~<„r¦Ô#¬oB×W@¦"LÑÃô'y.®²ëzE Ç1Gz×L/S'P¹Ñµ»¯ötì Í_D¼Ç=¾?£60åt›“š«XâSÏq úiÙ[ÁUŽ¢ìózl§©^MÎ~SóhÊðÂX› êm6Éïæ¸ä'›3ö@€GhÚC*±Œ­F|2ûEúA©èÕ’ÂPcÛ†¢a'V¡2» Üÿ~¿[æ,¬>¯¿¯ÿ8<³Ü2èe&5û¡PúqH²—æí2JU¶GäívÛð5í…9Ü5?ÈsÂèKŠ%30ù$ò‡5ö¸Á§Eyþ ±†*qˆlÁ4²é蹿ëÄ‹®Þ#Јý œ«ùh ©þàƒ54øà}?Èú¶ë¦GCY|5t$cœ¥IïAh`·½ƒ‘ÆÒne8S˜ƒ`þ°J·Ö„ ³=˜áËï=¡¤ŒtCϱ½ËW5)çÁl1”ÞX\¶P~¥f:V‹E‹Fð!Œ¬HR<‡P¸”¾à~*Ô0UðZÈôIÃxFÚøè‡/°>ήÝé É1x† ([3™nA£|KŠ7é`)’k¦R…sYóã"ç¿ïnÔu:~´QÁÍRäa­°ŸwÇÃB?„dÛ啾ËÁã3J°Ì'Fø>̾¡®S½]'%òas¬êï…Úiìž>x ?~µíIõ†»ÓÅq›Œ] k( QO7#7±Äg[ñ»¢7 ÃSÕ²ˆE²—6ÇwŠêë3d qó˜@ÁnÑú׷цÁö˜@"@ÙÉ`‰H€¾m¡—Ë»uÑ–Ù_fOD![X¦‘Á`Ëкö“°æc&ôÚg¡æŸFSQº/mm§j•>&é.´~ÞôúœîéÛ4|¿Œxišð®=jÁ„ûÆN8·‰hFñ¡æ¡ ï87ë¶œEÔàqËZó-—_Ð`íãÕt‡‘ ]jrŽûy‡¾Ö´ìøYb\fnÁÁ¡kdh;o,šëKŽ¡©x…·Mú©CóàÈEMïÒÝØð òÆ`¬½zeNÞ¤EQ;f‹L€ 0V&ÀSÎZ8ǘ@ü dæçgˆrë(5»’µr9¦Qm®åè_î ³5Ø.öºÂ`öÿâ/Aâúˆéd·`mñ°ÑÐ0­@¡ÿ5F´ Û‰>h¬îD aöŠ×·ÝÅ3c¶&nLX²Ö Eæczgðîô¯O.MJ×kéðZ#n`—+47m9fL S ïì¬6ž…†Ø±˜††]ßD:™5ÁY#”¶P Øþå–Zžè iÁ¹eXW`Ǽ]ÐXÍÀ®¤¸¬F°"Iªgîæþ–èq`ùZ‡6ϸ ŠîÁÐsË¡ø.”¾Þ3[j·µÖ‰‡Â˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜è4<&¦Q&ÀÚ5|[FnLJêsÀ¡ÇöZôÑ»›ÚudXø!0"'øéGUŸøÞ—Ñ0îî»S÷9ðˆ†ž~JÕÇo½¥Gë®#Ù˜ÞãG½ÿâßYK¼2s ÞÿØ“+?ÿͲXܱ]&À˜@¼ hñöýcL€ Ä“À¸ÀÝ=ñ±¿û3sƒÊʬ5UB-ÏÌ þ‰Æë¼¹ùWÇ3,öKˆ@@µßzAª(¡õ&³n ž™éÏûxëúÒ2¥Ä²M+7á~ òփצõ‹Æöh'\ú–ë¥×š–õQæÔ©Ý£}ÈVXÖÂ(Ïv» ç¿û9_3&ÀZ‚@û­¸Z‚ûɘ@B¸eÚ´®¥Fé<|¹üj|Õþ>MzNõúä¡JÊ<|É|‹†A›„¸SlW\˜¶cˆJÄ(dææ_¬,õ¼ÂФ¸Úã“kRŽ|õuï%nYÑq;~åÏûW±‘_MúžEcঢ‰£ù †P}ÇKé{Ôí_gÈ?îøò5`‰AÀ›b°L€ 0º‡SJ¶¿Ó^éÛfpÂ.Ÿàú×=_ÆÀÈÜ)û™–Ñ¡•ŒÌœ®,ëQ(/3ú{rn¤Uƒî3œŸ¯¾öÇfâyî Hµ.T²YÁ ?ÁŒ~1sòü3Ý:CþqÇ—¯™H¬Ð$NZ°$L€ ¸[%C12s”¦iY3óê(3.[u/Gä--+ ·}0‚£cTg±W$M Q†¦}‚é‰Jj-K½¿ÁþWR“ÿ.œì™zð…²2áûþø­RÎ,ÊóÏpBô¤+”úxÓ®ÅÒ}ï(¡¤Ï?ñø’ý–H»˜ã‹üÓX:“üµ²*uÒfpY/…ö²Hyà5SëNÿH˃ìø*™‡ô}Ö–GˆñEÁœŒÌ Ðiµºdv ç[7—Ì@Áß•n=ƒ<–3;˜ý ½GP #Å&Rþ‘™o™ênæ¹rvÞ¤:k›F¦ 2uó%á“çLö?狯™`±Ðb±Ìv™`­FÀT¢áY~ÜÞ»>M˜#s‚—He¾‹FÜ_(ØnÀÝ첇!ªgåâÇž¦wYʺ^“j¶ÔÄD<ïåf.¦åÌÀt¤ÛÑHZJm †«p?=Ë_€Æ|Í!e/)Ô©[Ò©Ñ/49Vhb²”⟖n.¸£vG 07Iéë K a/Ñ4y9Â{›ÜV¬çÿÏñÒ>ÛþŠÓÄVýØÕawüŸ‚†h/Èûêµþ¼ÁŽýÌÂBü]€ûKö+Ð)n„ÒfXÖ{™·æuìÁÈÚ¸Ÿ¦ô,EXs«åP÷b V Ÿ­Äeå‚ùëJX©š”ÙBj¹`ûüI™!Ha€¦3ò—B1ˇŸ†G™â¹Ñþi»ÛaÑ¿(e$«´€]•[‹¡$Å(Y®EZ¯6ôª…Ø7Q|PZÀ¾°1'Þ¨òÓRiK|£Mçëî½7JϧR©k¿Á&ÊÌlð_铲 yé1ĦÒ}EiK?¤É6-ð‡¢º]wïš´yiÓ°.µŸ¹ÿæ¥p³›ÕEsæ=Á¾Y‰”2´~ îË2¯v{E×xW.ƳÝSR´CŸñ=`L mù†›î¼3}óÆÊ;•ÏÌ ú/vlcáòã%Æüù–eÍ€qˆ»ñ-•ÜÙëÛnàÌÀ˜­dÊ•JüjI1*Åçr_`Â*2GCñ™Êµ›6(až‹ÛçÉŒ4Ò»¡‘ø\aÐ[µ‰èq~ ´¥)¸ fô%ÆÏcÐ@Ü #E{¹FŠ^é/Xd*óe(J§³_sü€¿iX×ñF>²³¬@ðmK‹t!φܵjÝüÿ‡×§í73óUÝGøƒË±X{î«{áÉn~ÎÉËþ›,d`¼ãúdoÒËNüÉ_¤Ãñ8mèïõ_åÕxˆž5vÌÉ÷ì¶Eæ#]U}oH}Ìk§F##ùSª—^ ¨”I»æÝòsß/ŽÈ nì¤ÄÖÕ?­K÷ÁèR2Öb-­ÿ´¾IK¥…m|£MçÊu›GAyÙÓ#½û‡Ž„ÔÄl.FT&ãùuv^NµâZ?Ê⎠¶dúƒÏAa¿ïÌ$÷;·P@Ô‹áÖÚ4”0ô8òÐ¥Pot+’0ÃH¨|í¾ììzÓàˆÆFL€ 0ˆйÂ`L  HñwŒÐ¬ŽF²-[ªCC5#÷»íÛ —•|ÍǃFß>u°ûzš_w”2¿?˜ûÂ[Žß'îÆü}×__‰žÿh,¨ã7šò=å6£©f°÷i'o3WçÁý;.eÆ~DSvÐXü£n³[}¥¼žÇÜf³oËùcZRrÌ1Éë"4Þ¿r)3ö#È?ûÛ=öŽeœ£ñÓe½Î¥ž%h€÷Z­çµw·ªó4¶›jò¨ýC]F##”ÃÓÀâ½Â`­2c{“äÑõ¯Þ½)ÿNfpU¾µI;G®¨âe:Cù8Lß Ì8AFuÆèΣHï#ýùG:ìQN¥#ŒG³¨Ïš÷?x7{[KOwÜŒ äï³==ž&øçxÂg&À˜@ ¡á¬À˜@B€‚R‚Q”~Q §Ô²çñ%/ µ/5m©Â Ó0vÁ³_]Ï‹]×ö%”‘Íè. 5G#n3̺…š§ôôýj¦iâgË4¢ahâR;²ã˜×œ1 H®:G·tßJ·õ’£×|äHrÌÁgg(/©#üyß;ftFö'N𹡔°ZÑøIîÃAúz÷ó—‚}Áxl™]”’®ÍЦg=3wêÞR—+Ë^§´#üï)··0W,4¬(eÜ *I½õIÝRVV­/ õ²Î½T–¶±‹*_µTÚ9BEßhÓ™F¡©ýŸãwsÎZö{%VþJ‘?’_ÈW—`ð«ä¸}þöVaŒžåMüùçSaŠ«àÔN;ð0Ú#×÷Õv{5FïØ:`L zJ=lÀ˜hhìüŠQ”AsçÎõD¼L6ôzv=Ê6C¯s²û”„r÷½ëÚp]7xé1Íze(Fchá» @œjU-ƒs[{V‚Þב‹žÝyÓMeµv"\ ›ŽæçX }GŸ&'z4í*#E֙Ɵ‚²‹‚¹~‘&cšÐˆØØÊ2s9­[jÈ ¾t½²ô%PfC'{ÊåÍ+±A€ »=p”2¦ u]îÝãÆU@Ñixz¢×k+´XjSO‰ õ¯ö¾ÒÎñ;šøÆÎiRX`Ðü£zZ¡z £‡çÓ:"•C~Žwæ‰h¦€†“yfÞça´¾¬úÝB{Ú=-œ;6cL€ DCÀ%¶Ã˜hmØÕk©Äo}óó»Áõ–ð|ŽOŠ«L„Ý¿êÈjØf³Ð~©c‡›²R#ÞÔŽ‚Ø^*u4FzV¢ñ‡©>‡nƒQ…0‡TѸj=G¨k(ËÐØ-ŸÌy$ôYKÝåäÐT­‰×î-¨Ô7?eaël4xŸ ×(¥†p‰¾ôpø¸(è?Ï‘)3pW/ìõ€8nÓ÷œgQžWÀ^ÿP»£n¿«7Æ üFö÷Ø=†¬|{ÉÒ•Pˆ®Ábû»Š²²ê+Àn[(íÜA4vm:#³ý†|Fù?.‡Ó«t3§Äúù :BÉW½¥ÇóhS=÷x{>£ëM7ÊóGÞ:e1Ò`'¥5Ý¿¦ÊÁî˜è˜êõ.vÌhr¬˜ho¬;<‚Æé2lŸ|{–ÿŽ]’?¥wú´cÑ;m\ZÏž².E£ze†Ø¥®âQÏbì†ižæv52pç˜"´”˜kÍ•üMìí†|­!¶ØÅT,LiÚ¯ÞuG} ?iG·ÆäÃ+KQûä¶X½f©Ê4iºZÄã¾Àõ›Ç{×íט¿ìÎâ:}iž'cDíö̶=³âBÄ»éià N¤ ÜáZfÕñîûp×4º€]Ún¥i\bÕºÉuFÑÂ9h¡´ T$³hÓöæC‡šÏÜþb ÙjØk0mû²±µ¸X¨Lu¶]ÆèŒXTÈ®3½Ñ±»í9ÿØëÕ¤xJðùJ™çböû9y“msËWL€ 0¦hz¥Òô0Ù%`L QÔ{žukþHl¥ü?%*¿Àº;…G.ò*m-¶€Ý [cÓÏ‹´šîc‡®«°½òcØQi]’TÏš")É’ú5p.¶¨½,Ü(B£B4` 2[1ß’•›¿!Éã}W7­Þ–Q9 ?Cx=S§Ý{$ÿ;°®Œòym¬×—´Ì²ô}-Ó˜ ?~ÌðþÇnLç.¾|¹U¿ Ûy;¥MÄ,·¯Ái0†¢°«˜åÁ±@LþÁò {ï2ÿ­%?mÀ‡'o„¢t£æó™´A侂¦ì)ü0]¤¬­0+ö4”…°ÔŸ©Û%ÿ.œÞ¾œ•%Fp-š+à~¾GúÖJ¿÷—#Þ뫇¯Â¹lØÌëóL3tóŠÍ›*ŸÈŸ  ïZÝ0SÊš #M#¬õ´¯'û±b+ÿ,䕉H»Fææ?i í;MZ=°;6 ÐÒ°Ãí'Z,íj¥‰â"ÊtF¾ÊÇîzW £üM¼74Ëû»’ƾØ7»?¾/3BBÞ| yv*vØ;¶^N¯HòLŒßЀ"'׬Aë0Àâ|pZ¯ß¥ôÀùl™|>âþ…×”cf`Ò2¬:qÝY×­/+õªbl©oæx.BX_7æ'É…­¤ÏÂzžÈ?Px‹„e|%n6*¸ÑÕL?-•vÉè~m:Ï Ü²[ƒe¡#*¯WçkòÃ?ÿ¼ž¤§)"ÏÎU[ô¥zÙ5γpç4o—gÀ´~=Ò½éÿ gÇm)ÿ8vjFd¾Cží/STÜßI'>3&Ðù`ô™&À˜@û p]AAoUåñݘ`ïVIê1Ûc‹ckÖ¤Iu×ÓDr£y¦?ÿF4ðïÂÚÌ`’j\àîžFR…§±]¿¥­Óó½ïn«šº¸:œ¨´qÂÛK~$Rµ²ãwÝum<ü¦¯¸k¢ÛŸÎÖÖ$û_¾‚Þ†.»k¢Ë{ÚY8a˜ÑÇ0+“+d¼Óƒ¦W¥ Íjd”!ŒDÛŒèÕžÕô}K«*¶=©{ÕRiW7”†ï¢Mç@àá”uzqŸûƒ9¿Sþ õõ–iÓº–nÖ{>ï'e³EŽÐüã‚ÝÎh„f ÞaŽŸ™`Í%À Ms ²{&À:P…¦Óà3&dêêS¥£­À›à;aL€ „%ÀSÎÂbaC&À˜`L ^F¦ìbéâaL7ü.Ã;)Òw™âûØ@'#À›t²çè2&À˜h-Y9Á‹°˜ê~]7zbÓ†/¤×sIõwnZK‡ 0Î@€šÎÊG&ÀâJ@ ßÿIiün}B\bϘ@;' uñÌåÖ…ØÈá7ÚСG‡ÅgL€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ ÄL@Æì¢ FåNÙß´Ìapº“’ªíÚoØ `L€ €€b‹T²—+<šçYy“>ï,`¸>é,)ÍñdL ½h‹:ªÅš@àá”b½øZ!Õ ¨}w”RXBÉ? Ð” Эí5‘Xn&À˜@[@eÑ M?”¯}”šbÊ×{úûúß\UÑÖòÅ;|®OâM”ýcL€ ´¶¨£ZD¡ÉôçLsPÑ”RÎÃïI-M{uÖ¤Iµ>ö™ 0&йŒš2e;«Ì:U)u ~'¡ãèwQÌ}³£àú¤£¤$ǃ 0ÎF 5머+4™¹Á±ÊwI©¾Eâõ…ùþ;[r|™`­M +'x”%Ä}wO©‰›Šòü3Z[†x‡ÇõI¼‰²L€ 0¶!ÐÒu”'žÑª®|Ôt)äs·ÃéEyã—ÅÓö‹ 0&ÀÂøü£wWpÜI«ìo­À1ÇmúüÃw? o;ñM¹>Iü4b ™`Ñhé:*n#44-@)ù<|¾0˜s¦™a L€ 0&К ÌÈ,þ3(€ÏA1|J{œ~ÆõIkæ‹ 0&ÐzZªŽÒâZ° æÐ43áë}%+3ñ Ê~0&Àb'`—¿(‡áò;üæÔ”ϱ{ÔF.¸>i#ð,`L ´T…†v3³7Àš™¢@VY+ðà ˜`L *‡Q¸_Gå²½Ûd{‰hÌõI"¦ ËĘˆ–¨£â¢ÐÐÖÌиæññKlö‰ 0&ÐTS¹loßZÛ-×'­MœÃcL€ ´:x×QÍVhè#gÕß™‘O¶:  0&À"€Bó$•Ïv9ÑVâ<àú$qÒ‚%aL€ ´4xÖQÍVhLËFͤïÌ´tÄÙ&À˜ˆž•ËT>S9½«¶³ÉõIÛ±ç™`­M žuT³D~'|¡úþhfkg 0&Ð0»\Fù [;5l3ažr}’0IÁ‚0&ÀZ–@<ë(osEURõƒ%Íõ‡Ý3&À˜@ü  Œ¦ò™Êé„?¸>Iø$b™`q%¯:ªÙ ¾;Ó1Ûר±gL€ 0&(£©|¦r:á®O>‰X@&À˜@\ Ä«ŽŠÇ”³¸FŒ=K\ãî¾;¼rÓw¦'®”,™›Àˆœà§Yþ¼¹Íøš 0&À—ÊíË2s‚‹WÂú’ÈÍ¿íƒÏê?a&Ð:š=BÓ:br(mI ëÖà™ÊR¶®/=”䨴±ÒDÁõ=ÖN-JòùrïLèPS¥ÒŠóLð-|÷xG;iöäœ7Bý½!?¿OY™µFóÈ3 'û_}Þ¬{©þ¡„Ö·Y~°c&À˜@™[p86Š˜/¤XP”ç?;"©P1ÐøŸ…ÞƒŠòýû‡>‹æ>Þå}4a6fñÜuÆ~Ù }Þ¼Bì½W¢®c–¹Ö=_0fàšfìèÎ3só/†2óÁÐàD%µ–¥ÆÀÞß`ÿ+©ÉÓ”+ÊÊ„šB°ZH9Ó f„úᑞ›-ËÚŠÂ ¨ìöÀl„e°ûìÞëØx ‹®ÿµáà«´Ï8æt™›¥i©qsòýûfæçgˆ25G)q äÙ²í*™‡© ÏÒõÞñBÈLzGì¡@IDATØq\‡ð^îçÝ1;¸ªÂ¶ÛÀ?Mªå–’ùõ¶ÌÜ‚ÿåeÚ€uûQf 0M™ëò0ùíDð„)~K1an†7ûÎÐô¨‘ÿ8<?¥ø¼É7FeUh8ä¯Ð×MCND\û`:Çשùgåej—ï™`mMÀçõæèº9Ôbd9-y*¯*ïQ®޲q§¢`îÙîp°¦åm”•É…y9G¹§¾!Ô+Ús‚9דýhËmªQ¦ß¤ùD•¥‹[æ1¨§¦¢ž ºÃu®aê¹§pÿ*ê¬ÉŽy¸sSxE+7…‡:ú¥¬‘7ðúõH!ä¯W×݆ҞóÁâEïL >@`n ©}ñdN 0–&¶wÌœ³¥DozF÷>)«4M>†8Óº¯0­.H?(_Ðó¬œàeì{`o,<}Æ#Šâ¨>(k ™&öž‚BÿaYÿ!¾äo¤ƒž+}ÝBa‰ËHIC˜—ƒÓ+˜W}[±žÿ?·»±é=T¹µ•í!P Bj×B¾Õ†^µ ¢´íÈ,,ôÁß0¹DÀ?TF7âKòi†e½—ykþÐm6ùŠ 0&4¡•¡l«„:ÕîðjD¬ÆÊë†Ê{¬9\ïϸ»§̨@>uÒ E‡×áY·N©îìÂÃë÷vC½p®¦Ôr²K¹ ë{ÂÏ“ Ì¼ˆrxÊøñšæyü =HSž潩¿o·©¡ÏCïcå‹ÜPìnÁÌ9Tßjqêš9XpŠzD]*GcéjŸï™@sðMsèu`·«ÅÒ}Py$kÒ³´±hÒºÍ+ïDûÌœ ÿbÇ~ ðÞã%ÆüùE™†ø!î^-©äÎ^ßvgÆØ[~_ëÏ{£R‰_-)F¥ø|Cî LXEþ\wï½ÏT®Ý´A ó\Ü>ïø]sþ[º·ËN3ã6ÖÜ?7Â4 Œ¸óžÙ›×†Øxû@vöz<œ‹^°ÉˆÇ¯³óræ:–!CråÚÍw¢áÿÊœ`îY5æ@ùZŒx=eë”Â`ökŽýpg)TjQV–>*¼Æ4Ô‚b}))q“ÃÙ%³ãç1¨äöÂ×^®®Fú A‘{Ùf©^ŠžAÙGÊ¤Ý ónù¹ÆÏGä·`¬†ËÚ`Ôªu# ”ýÃëÓö›ÈùªæÁƒà¶ŠÖ4Ü×Vֵ޸‚ 0&ІªŒªTŒ˜<…ÞþKÐi5#3p×›E›þ 'R4å5Ê{*¯Ã–÷™¹SßFƒ]+5*Ž‚) CW(÷åb”ÉÉ?Fö{¥µåHêäRšÖ”ÆTn“}(H9Äf1þz›ÅÐs:FM™²Yj¼Åéî=’ÏÜÜxc,¼(Œhë›Ì©S»«­ú$8y uý5ä–tª=‹zè÷m5]w7»Þ¬öÿ3è@¹æƒ „!`Ê¿“)ú÷W‡yZÇhË–ªÃ üd Á¿ûA 0ÔÀÁ(¶}ûÔÁîgè™zÝQfÈüþ`îo˜Þ¶¿Oe†Ìï»þúJø±#è>äxÍ¥ÌØ|^9²¤ZzåÑ!v›|[õç–# ¸ @oÔÃnOTª†ÄNZ‡»Í#\ÛïÚ¬€ÿô>€øçd öˆ`—|=ñ~Ç¥ÌØVg³1ª"QºÐq‹Jö4¤Ó{…ÁZeÆ~”äÑwì8gL—»©ú•K™±;Åej 8vù̘H˜ée—ŸÒë…ò.UåÓ#ÉÕÜòº(oâ7(O×Haã„0ÏB™ý û÷ÐaF MõaYCÉ.¹©6ˆ¾Ü&û(wß ·ó¥ýL*Eå±Yj¾‚pe_Ú©wÝ|siu8 ÿ…WµOÑÉ-·¢®S¢¦{¹%°ëa)^q›57Ü~ñ5ˆ†ÐDC©ڑʲ·bÆèJ¿F£¯Ô²ãñ%/ µ+5m©ÂbÓ0vÁ³_]Ï‹]×ö%”–ÍɰÃu?C¯ÏfÜws›Ù×Rþj––žôó¦(sÅN¡ÏšzuA;“[DãôNqüÁ4/Rø,ô²Å–ÏÛ#Û06œ© SÏÔ‰ÇËÚ3üÅåùZƒº…QÄÓ9¾|ιqÎIÝRVV­¯[ÿAÙÛ•h*âñ½cÎPØhz…fn(%ű^:’>˜`mI (0ñwŒŒgcºð½8?YÌgü£ÀtŽx”×¥¥â1ägf`ê@¥ëûy¤¸ Sœ½j,f ¦N8(C¡•Ø£3d7ÆrŶrFÉÉyÝCÉRÌ ˜…²ù0Ì”8uz`ü†º¿‹†ù­Ü˜)Q]×y|¨ƒê¨ WRÅëñHÇ/>3h°B ¥ÎhÇëýUèu ¹Î ’Ɇ^Ï‚GéèѲY§ç•Py=»ÕFózÆPtì^;÷½ªª ÝãYƒkTl7JùÜn#][R¦S‰^º{±®¦®†G¦¿ErΜF¦FÜš?R˜Ök«à-ÙûÔ¥úVØ…;”€yž)¯2ÔêÝãÆUdúóM·9¦ú¥Î"lð¤Ûœ€5#E¶ÀouBâ&À˜@“ dxý`áÅ(1gcÓ—½ ý¯:åd<ÊkŒd¿…ÿbZGSf”ž©¤ØÐ×{Ä‡Ø FÃtá-URœŽiV•[ÿ‘ôu"}¹:EÖ«O¿ÐùÔ×AQøëMï¹eÚ´î˜0a‹ó<Úsc¼jý‰Jn-…ê*+ŬWß(K”BÖÚ#éPë_0(°B¤ÎhåØ=†¬|{ÉRô¸¨k°ü.Zÿ‰ƒ%<Ÿc–±°Êä@Øù«Ž=Ã6ƒê¡ýRÇ<7è¥Ëõ¦¼LEfÐtVÐÙ[S@C¾ŽBEÏP›ô#E¥±CSÚ2Œ1 áÕÎä|јýhžÏ™œó:Öë<…Ð ÌJõV=7R}ŽŠr§zæd Õ@hšîµM×þ¡vGÝ~Woô½yHQqlÚ° ,ÊgsqÌøÌ˜h/h‡Ç1¹S¯©Rúº±±M’ßVÆÅ£¼ÖRÅÛf¹’[Š#à÷é(s_¡)ÔÄ£Û¯ KžXªÊ—cDBóù’¶•ß±•Û "§Ñÿ4o—# 4ц6_nÜREÓ¼0e8¶£1^¶oQÊÑýTcÊ ‹ê›MnI œõE}Skt¨õŒ/˜@êõpGᆭtÇ75)±•¤ÚY¬Z7ÃÞÛjŒø§ôN_‚6s”šKC¡|³.EA·2Cì÷iL˜žvBènaòª4ŸÇ^´I£!(„QðZǸe£¯CÃìD·]£Wn5üPÇÜëÃwwd©ÒÔÇïHNóŒEeY) óÁÑ'è>”ürœˆÅ¯½ÜÆX°º7t°}±ãÙ»µæ°‹ëC?Úi™UÇ×Ú©¹€Ÿ´Ý cr‚á•¥P|ϘH0äMüõÓTõcP5íSG¼Êë°å=<›í÷£NûAæ±(šÄdµÓ=Jþ ë£0-{(êšof\—ޥܮ#týÄKÑÚl†ð f8ŒD¹áH^“ê yQÐQÊíU:/¥)t«Î®£5õé±ubC:ÔqÇ7L ‰x„¦‰à:ƒ³¾žìÇŠ­ü³°«ÌĬÜüðÝ–'M¡¡"±z`G,l ¥s¦ÑÂý¹ùWÁÞcØÒq]’TÏš")É’ú5ØŠù\LÓº,š­Ÿ›À4ÃÿsGæN¹ÝëÑþÀÎ.§[B]ƒFûS3“Ü ÔÛ(°OÁvÎc5çEeÊîÅFþØÛ.4LTP/aD}*v;Íê¢}”^‘äÁÜåµYþü<,–™‚øUỘ®%Ë”aˆºƒ…ô<°mQh¨‘ïïËÎ^7"'8£DUÛ‚D5v³ù7vŽ;]åóæX¯/i™eéûZ¦1 ÚžÃÿãØõú<Ó Ý¼bó¦ÊçGò'h»V7ÌÃð€É°[wj__¾Üª_†½ƒÒ&bØék%*#-†‚çAåpüå3`L Q dxvË/6~:­ña(kGÎiwËhËëåý†ê8kocrÕe(•­´io:R|é¯bÔ$JÍååÄ1§s,å¶Û]c×Ey9O¡8³ðY‚©Ÿ4¥¾‰Ä+¹È÷¯@õ8t­I¨óK’=ÞyU¦Ú¾ÄÈ¿þÐHRíK:Ô:â &Ð  ÷ɣ땫MËz½†‹|éÚÎôrGÊ>ŽszðvÖuëËjfPIÏEˆ÷×n¿‹&Nܤ¥ÈÐø Ï–ª\ wo`×´ñn{|͘Hdv'™’(ŸCF·!t´åu¤òžâ­i˜ ¬m–2oúøñµC4j‚‘é™GÊZE‡ÜÄRn“ý˜_ïëP,ÇR—gBGã£ñ§!^±ÈÝß×è<ŽÙØue,Dl°+Cåˆ6BÝñ=h t>4ïÀ:€÷ÉôìCg>:.ú(£gõ†}Dß’@ઈø1ÛcA 5kÒ¤ºëiâˆùŽGþ›Fh¸{˜:°¯8d•»¡vÑŒ ½{yý+HY }î¾ÇÌ®¥›õžÔ#å6§kZ(Zj”n×£kÒÚ¦,Ò õ¯±{Šß:=ÀÑûî¶Š¦6dŸ¦¨¥ ÍŠfGœ¹sçzÞ^òÓ ‘ª•¿ë®kó»¡pùYâhOet{’5qSœ%sˆ¦¼n¨¼wûËu,åv,þ¶´ÝhåÆwæ¼kÄ';ÒzÛhêŽhÒ¡¥ãÆþ'&„)÷IG˜ÄDÅRuDÈs[²rƒ·wĸqœ˜@< ´§2º=ÉÏ4b¿˜`•@¼Ê}žrÖYsÇ› 0&À˜`L€ t¬Ðt€Dä(0&À˜`L€ 0ÎJÀÛY#Îñnß”ð\âUžŸÛw,Xz&À˜`L€ 0æ`…¦¹Ù}›˜Ìþ¿6 ˜eL€ 0&À˜H(<å,¡’ƒ…aL€ 0&À˜`L ¬ÐÄB‹í2&À˜`L€ 0&PX¡I¨ä`a˜`L€ 0&À˜ˆ…+4±Ðb»L€ 0&À˜`L€ $Vh*9X&À˜`L€ 0&Àb!À M,´Ø.`L€ 0&À˜` E€š„J† 0&À˜`L€ 0X°B -¶Ë˜`L€ 0&À˜@Bh·Ö•;eÓ2‡æNJª~Rˆ® E¶„QBl‘J–¥~÷x¼ófåMú¼‚å ˜`í†×ñIªŽ\ߨyÄ4NRì¬í‰øäö¥¥ Ô¾‡B¬ðhž7¸Ý·x»Rh‡SŠõâk5MŽ7,#CJ©º¦¥X=º¥ÉŸ¯Ó6U躵qs™ÚRV¡G0+7µa˜3ºUt¹oúôñHfä}>˜`‹×ñOïVßÈqãîNÙœ²õ:¯×3ÖnOh»=ÿÃ>¶÷0ŸÚ}–¥îîïë pµû:íÑnšLÞ‰kÌ’‡ÐFï¿ÇÎýÕ{î,öÞ¥¿LKIötÚÔÂVâÊ**Å7ËŠÅ¢ï~íûí²UÓÊ»”ßpåÍþÜ|l,üX±éÄ™„£Î:®+Z,µ;B}ƒÉBCý8¬<¥|ŽfÉ~{ìÜÛ-–eØã î=¼íã±(û®. æ¾Ùa¶ /Û…BƒD§”¼3c‡îâ‚»ìK…5 Ô‰ƒ÷Ú™~òçß׈§ßø´OñZëå«'Þ:ñ¡©“gÀš‰)6|0&À:,®+Z>iÛq}C AêűojFïîŠÛ-Ÿ_8„–!æ=ì[¼vãk(o†R3½eBMl_mM/E”Õ”¸û€Ýi·\qšÊL‹Ûö¢Ÿ Wžæ9`÷Á…öWÞâ©|øÑH+‚mŸD,`ñ'ÀuEü™6êc;©o¨Þ£úÏGõ!Õ‹T?r{¢Ñäe í„À¶÷p¦”¸›Úͽӵ÷Y¡‘ÿšF#3(|Ô5ç-“|yvYôoq"^ÄÍçK.¸èÚO†kRj(½;]&žÛdL ຢ -Áëªï¨ÞóQ=Hõ!·'Ú0³pÐ-FÀýR»™ÚϬSµ÷V¡ÉÌ ¤ú|¾wìÓC\~ú*Qâ•ã‰[¿Þ=D·=fíµ×ÁÝᯣÔÄ+ö‡ 0&Ц¸®hSüµ'h}c+3TÿQ=¸cŸí·'j“Œ/: Êßýwè!¨ýLecŒbÄ(%ªB£™=åu¦eõÃWÌDL¿·‹N:D³”è»Ï±GådühÝT¢¦{ƒñá‡L€ 0\W„i«Û¬o¨ž£ú.™ê?ª¹=ÑV¹ƒÃm-ô^8ì`µŸ©p;M{/#J£1¯Ï3v¯]vT¼f¦y¯ñ#Žé]»Ž„O¤­'_üxÔ ø`L Ýàº"Á’.ê;oÕw©Tÿq{"Á2 ‹Óbœ÷ÚѤӴ÷Q¡Ñ.½~ÂØW»/¶fæFw²| ÇÞÇŸ}ÁÁðŽGiâÀ”½`L Í p]ÑæIP_€©ojGgjê½ÞÜž¨ŸVlÒq P~§v4µ§ËDlëÇ~¢EÒîUñ%' £fâ;3qpgô8Ïž;ô9ñOÁGi:cFà83ŽC€ëŠM˨oÜ£3)Tïq{"A3 ‹Õbœ÷ÚÓ¤SŒÒ$¤Bøƒº¦¥X´Ï6Í'@»¦&+ÍëßhÚoãÜ|¬ì`mGÀi´r]Ñvi6ä¨oœ¼Aõ\*Õ{Tÿq{"lr±a%`¿‡hG#zƒðc…¦ Ò™,ÐdFni‰¦lµŽøÙ£{ºôø¼;ÀG÷ 3Žbö‰ 0Ö#ÀuE뱎9¤6®oªóFõL„ª÷Hž˜#Á˜@;'`·£ÑžF4H¡éðí½D‹ ÉãÕ„ìšâóqÇ—‰xz¤Ö^Ò°M9ãÝÎâÈ—½bL U p]Ѫ¸c ¬ë;o@bªç’©ÞãöDuúý±~³Xóç¦Ø³•mWVéâÇå«#†úÎgß‹e+ÿˆøœl#@ùžÚÓ0éí=Šd¢¤ÀÐÏ£„Š›2óûêõ¢à¡—Eö¿Nûnß丮ݰEè†!úï°]“ýhk‡ø‚,qu”g’ÌT[ËÆá3&À¢$@eýbª+œº KZ²¸ã† …¦‘ÕÇ ï.o,üV¾ï®â²ÓwŒc>SClPF/‘’L³šw¼ðîçéÛš>2 Ïvâüø–Jó<†ëÖ¨ÏÚ¨¾©Íˆ&µo’jäh6³öäAÎω÷$κ±_þðKaZ¦È:÷Ø:æ-uã¼sä?½o]ÓRÅ;÷—zx÷Ïþw¿–ˆ_ü@̸ébáóÖo¢¾õé·âÈýþ&vÐÇ팯#¨iOwŠöõd$ÒAòxмÞVÓ$ˆt¯~ô¥øò§ "MSŰE*!(swŠ!Ȧ’bwL€ $4&ÕXŽ?­¨Û¼øûå"-µyk6 Ó³ÿ÷®Ø¸¥,nà0eDäüë qáI‹M¥åâÿ>ø".~·N}ÖfõMuÞ¨®ãPßůƒ4.ðÛГ«ÎŠy¨ØòÅë¨é©r”™ZÞðŸGhâ™ýaL ¥ Ô–]±Ö°o—Õ_ü°\ì>¸Ÿ-çoÅŠrLsÙ¹¯Z¹I9‰T¾oÜR*ž}k‘­uKOÿÜ}'qâ!{‰ûž~õ†.¦?õæMk"ÿÚóìQý¹o~fw†y½qì{Øv) ª‹vìÓS¬ß¸U|ôåR1æ‚ãPçÐ2Çm‡Wóˆ}{Ú¿²Š*ñÌŸØaÐÍxîíÅâ t´!Zvü9Ç ¼xèÅD ?‘vêâ³o¯|ôµ˜<êñÚü¯ëÕg{£®¢†ækó—ˆ­ev/ú%'j+yTŸ}üõRôŠÿ]<óæ§v½vþ m2ÂUÕ7µybÙu]¤ì\ÆÏ¼ñ©ÀV¾âÒSµSÊC~ñ“xûÓïÄ—ž$ºwMï/þÁn'Ñ}×ôú§§ö Í¡vÖ;Ÿ}'~[µV¼×ζ ¡yk·2Ä\ä3Ê«†÷l Þ•‘/+íTL£B~¯n²T醭äùã²J¤¡­…-]Å-—ŸŒ¼›¿{)ÕkÊH÷{ÑaÛ{‰6Bã@o±ü¶¹´B¼øÞç"9É+.ÇK½as©x<4/sÞÇKìaý+Ï8B ÄÔêµ#e#çýþ¶“ óÁ¨ø¨Ò˜±=†nWž~„XôÝo¶"DþTéºøÊÒ'ßü*Î;þ ±ìQE@•iZâ~T|ôr^rò!âˆýv½º“3ñø« DjŠOÜpñ0Ñ¿÷vPzÚæqþGiîp¦3L€ 0öFÀ)Ãb–{o”ç_þô{m£èó~ÿDÙîîÖi¨|§Nªu·ˆë.ò%y¡ãÏ¥‚*Rø©R¢ó²ß׈a‡í]ÛƒG½hTÁõìž.è ¬ÎqüÁ{Ú•"½d´¶æ»_ŠÅa˜ƒíg³Ÿí²Pï½ÔÔ³F/Ýõ€^°mßÙ)-¯¿¯#Ï;óL“Å!(f<ùzﶈí{К®¸NÆnqÎq“˜=bL€ Ô'Ф2l·}0ºaŠ¥(ÿ6(C,Æhͧa¦»ƒˆT¾Óˆü”áëQw´WuÃŒÜ @=A­¡é[ÓIõí²•öˆFŸí» ú‘ù?®€²·m—fGŸ‚¶ðknþB‡[`ö ¶Õ%-hCmwôoá’_l%Šä¤ƒz£ž93:ÂÕgó¡¼ôì–.hº{í²£ørž„zŽ*ÝgŸº¬Ê 9m«ú¦­Â¥8·«#\;eßÝ4jÑ(Ï]½Ž|·3JºŠÞ+¢›7ÐQL)Ù42³ÃvÝjí†æ-Gá' ”ÿé~ì%ÃÄßñ®î>8Ã~ÇôW÷.ixVÝž¢MèhìÝsüè$ç&••íM"*4-Îp †ðƒ†åi螆0Cñ ^€0‚óáKÑ£¶¯Š)áŽM[ËÐcõ‰øeÕ¢zþÚ\fâ¸íÒÈ ]Ò«çfW¢ç¡âtì„JÏ}ü´|-õ’9)^«ÖnŒ£BÓrSù™ù̘Hd4…¦E†®iÔœK4òâ •ï§¾/ÊqC=ÿž½@ÿÒSÇTšºå9ùcZ–XŠÎ±_0½†Öè8í4å» Ø!¢2CvhÄþ؃ö‹1c¡Žr¦ Ó”8êsoRC )&ô,ô° KCÀlÚZ.¦<ôê6k!M Rrb;¥¾I9b£×Z¶ÃµSÂ…š‡hQ>½7´Fæ9C#.ò'¿®¿èD{Ê͈ù7” š}2âœcjƒ‰”·¨“—Ž5ÿÏÞuÆQ\íwýN§jYŶ,w[¶\pÅ4ÛÓ»é 5j!”$HÈc$$„!t Ø€®¸w[ÝEî¶d[V—®ßÿ¾9­t:Ÿ¤;ÝI:æÙ«ÝÛ2;óÍîÎ{óx"ï Þ‘<) s4˜øC«˜g“ȼ{7—Qƒ@$ 4>ŸÏŽÃæf-ü\&qt«w õèŒ4aZæ{þ²ùt¸¤Œg×nZÌTø’^wæ}`º*=]Õ88á·Ñà9÷'ßã¿AÂþN NýÚ"o!D?!³à÷ò?žB€ YÅÚ¶Ö¾ï0C¾ƒg§gN)Ì„ÿùù*zñ‘¼/ÛÐøc&{ÆÄ,Öš46¥œ¬×ÁÍ£e2êõÂ÷Œãoþþ…Ðôôå"`@©—ùÍ S`€c¸7&ÐÂÄXk?Ï$ÖÐøk‡rŽ}€Â@!÷]uèèòÛ¸}÷;ìOA+Úz† l ¸F_6G4¾ñY™g¼G¾h@ ¡B¾7µôl)æø§ØÏ,“ß;L>T±à­Ð¾çBv(C@‚V¦Žµ'Ðú‘Ióö‰q³Æ˜Eö6©ñ½oK¿Qö…“³è3öÁ©³x|cFa³0ö‚)3Lo°=vX¦(b(›Õí=|BÔÇá§ãM¾ãLŒ`:V›C0aÊõÑãMWÜÓÞˆØF´=¼P›YC­=Cž kxòw0k_fñm%-Ù×bñ0‡äqdîã·qP˜ÉB‹óêûK„€c2ê…ŸdŠ—r϶ÖWž–ð›ùråvŽ€9•nåfÿþr5GW[Ê3×NáCpÓ,ÿÌPPÈù<~û/x¢ÌDÓ'Ž Eksoá;žÁ÷óÚ8bÔvŽà¶If6 s}4BÖí66p, =ÿàõÊf@ëÖž¡¯Wï ¸™£ÝAฎŸO9âcÁ-1 Ih6/b3Ê@ ÏåG‹7qd>ÞÁÚTo?fðHðCƒ¿1&0Á ÿ´k¦ë»h]åy]‹@Ȭòý¿|a%šðÖ‹¿šu}6âýÅßóÔ/¿5tÀ¤ŸÞqYŵÿRø¹˜þχø}¡zž ðÎW€ÈeíM¢9#kkÎÓ›?uð>/˜íWþ»ˆvæï)xïµ?>Æ×•ðRÆ Œ¹¡¿ nê†/$tÂøîðX×N+Zû¾·t É`hàmNS|×Í&cØ'¨0[fÎ_Bh[Z2±ö7ž¡ƒ«™Q…ïLvÚK]8Þ4>\÷d^Òoìéׯ6²«ø‰öb)×µö uVj<¼Ÿehjž~õcº£ÅNfMÞUÛ EÛWŸ¼½ñ=ëÈw¯³ÚßÞûà=Ìß{pËÛs^¼–ˈX~/Àï~›0H ˆZ$ )ñfpi{…\‹€þ¨¥ûû;Wî“H$ŽA µï{KÇüù$ øLŠ;‚Z<¼@ß{ûÏpfÜ%IZ{†”s:zÈ‚¾õ€ƒý˜.çÐѰ°ÙÅ9“2R{5 3¨WG¾{ÝnY~pH&8¼äÙ‰€D@" H$]ˆü›ï½nmàðå«Y3ƒ𣹄]$õL¤@Ó3û]¶Z" H$‰@·E¹ü¼óùuۆȊ‡H‹r–FÉB$‰€D@" H$ž€hzF?ËVJ$‰€D@" +ˆÊºs÷¡°–) “´)д5yD@" H$Ž€Ó…({=Ùüˆ@@ 4Ñ ²‰€D@"Е|¾|+=ø»ÿp‚ÀÜfÕxóÓô¿oÖ5Û'H$Mœ(¯¢Ì[Ió8ñk%'ô!ŸßG‹7RñÑ“ôæ§Ë9ÁçIŽHf¡ù+·Ñß?[.’¿³`MS!rK""R  @y¹D@" Dñúõw;©ªé¹$I$ €HcF¤²Êzã“eâ‹ÍÎÑÇŠ„Pƒd˜½cY°YA‡Oœ¦Iœ7æ lvr˜eIp! £œ… IYŽD@" tk²9jÒÑ“ôùŠ­t×ÕçŸÑ$±|wÁZ*8p\äÀ7¬?Íž9Iä#s¶jk¡ˆº´”µú’™½m…Dô!)tûçr²K=ý’ýn¸h2—·‹NrÞ‰œ9ý¶Ë¦Š\‡Ž—ÑÇK7ÑáÒÓ”ÆÔÏ7”fL)ʳٜÉ}í`_' ™¹ap™Óå¢ëgN¤¬}øY¶³¶æýò¾k¨oJ!í'K7Pº$ÃŒkH0›kûå‰@¸&gáBR–#H$݇ÓEg f”?Z²Ño[fMC2R… M¿Ô$ÊÛw´Ùy×LÏ‚E¦z „\~ÞX6Ç@³¦f ¿‚’S•âüܽ‡iì° JKŽ Ó{'°Væ 1jád½÷P‰–.;wL³ò'ñLøùã‡ÓUÓ΢,\ífm(1>†.š2’R’âhìÐþbv¼p‰8¶1wÏ ¢+¸.“YCt N –ê Ê?Èa‚ü¦¼ýÔ+ÞL©¬!ô¥ä„X~6ãiKÁ!ØHs3_„äïPšP”×K$‰@T tË%gÓ ÿü’ÖîÜ#´$Jã*kêèý…hß‘RJŒ‹¡òª:2³öÄ›ú§õ?cö÷OoþŒLqv³À²ïÈ Ú’ ñòÒ²*º”µ(ß»üaö¶zÛnºzÚ8ºÍÑê“’¨lR¿”^´ÿØ)ñ;gÏZÈ›ÒÓUÌPƉ}§ƒ§0Êl¨‡rqkuPΑk‰@ ôc²—þý?×Nºñâ)-^rÃEiÞò-´`Õ10­Åóä‰@{M{P“×H$‰@Ô"¡aúÄ‘ôûÒ î—ÚØÎeóépI½øðÂoæåw6S6t:²)ÖÕ™†ÐÂÀ|mÆÄ,ácÐìþs·I£Ò‚Õ;é#ö³’‘FŠ`TÚ áÁ5'XxéËu…ÿ™‘ƒúÐSw]Áš =ýÚG¢Xøòè¹N'9•7µUïså¶D %þðèÍâÐÕ¬™$R5ššAóæ³w7» ÚÏqð†ÆEESÑÒfÇå‰@(œù¥ ¥4y­D@" H¢hF`ó¿‹Íi*¯ª¥øØ þ2ÐÔ´—à/³1o3uÇ…Ææè‰r!t)-ûô:mcƒÞ㛀{íÜsH„ÂÝÁÁ ÉÔ— ‰©b?h Z²!§YµFò9Û RÁþc„û(‰[ªC³‹å‰@ Øÿo“p¢ J’„©¡ 7¢²<‰€D@" èöÀ™þÚØÄl}c[¦OÊ¢ÿÌ_CÍyýWzÓ¬)Ùt¢¼ºñx07ΚLÿýz-½úþáð(fÞ8“ê-6ú`ñáocb´Ñìg/ÎAùнþÁ5 Ñ£i Nþpø_¼>—¾Y³‹µKÃ…ÿRŸ›Ù„î_Ÿ¯¢×ø^ ©c†ðLy¦ˆtæ¯2(€‚œ\w½ãiÖÙM¦”uYnÏA@ 4=§¯eK%‰€D ^Ö—`ú…E¡¡ýÓèÅGn„ÙI9g{, A+âmrƒPµÞ¿c9ÚÓC7]DNDPoµ±/ޱq†NûØg2è•â×£÷åÒ•ó|x×áÊóÇÑ¥ö3å¾3àpÈ~úî+ù‡0÷ðj­7”@~^Wð3+I".¤@.$e9‰€D@"Ð#ð$Bm0„¾äO˜Á9î?u€¿LkdÐûò[ªCkeÉc‰€D ’ðÿu‹¤ʺHü#k]o‹]ïmÿWȽÎEÀ2ËsOl{ÿîܚȻE‰kð : “H$!" ?"t§¾‡½·ýœÞæ.˜(äqœkú­ì—ë6hXàM¨,Ê>PeÝfaò‰@# /Š ƒµËkQöwp5dñтƎß?zS´4'êÛn>€u'^¢#Úß]šîÔOÝÓ@ê)FIyñrƼâ£tº²–*8߀m”®¤i#X½Îi`±¡êO0ÇP¯3ÜO$DC¤)àxaåB€-<³šëïypB\bÒEj¦¿J£NW«Ô±¼_’D ¢à‡×í"wÛå>îv¹Yjk—~ø·?máJ:9Ÿ™ˆª¼¬ŒD@"0 ĈŽwèx™ˆ*wš#ãUr:ø/…ô¦G8/Ñáí¸'ºøÄ†~ÂJÏÑ 9*"ø¾lð}}{Kž¯»§G 4x!´,ß\HË·ˆ¨2Ð8TF²¨Ìq´nL¿h¨žŒ®rÒº-ô5G¡‰5›hæÄœ‡`$ ZÔ[ª¦ì(>Wd³T7tèØ¸)W\vl|ÜÜu©Œ—;.Æèâ Ùj£N×ð‰b4dÓº%»ÝUQUç®®³¨uïîÿå‹¥Öúú¿ìÛ¸ã5k¾®áFA¸`#I" èf€€ƒàV«Vn+¤UÛ÷PMm½°)7?i¼Dg·¿;=ªc¾ï´àû¾ún'Åï›4’fNÎÂŽäùÂÛ›=V Á(wïz³>W±6¦\—F¥1£¨Œ×USÌÿðÂÍê·’í¥”f;BóWïàßnºí’É4zh™ZI¦ex>ŽÇpíÝ÷_’Ö¯ÿ+<•–=$ÃhBc†öSqhÕðIx‰@Ç ^ê:‹•rö¥ÍyÅ©üíy!{Æ9gNùÐû¯¿¼ˆoËÓ·B¨‘ÚšŽéYªD ì(‚ŒÝnÖ»ULŒv? ¼DW¶?ìÙz÷Õ—«¶ÑŠ­…tûåS™ïË“ÙxëUth”Ù„o7åqèíT«‰£Ý±çQ¥¶w§t<„¥R}†X§hx}½õùjºú‚±tç4@”Å­S*y7¦ ¤J/1·>òøÃq‰½žé—–ä¾…ó) ËL—ÚFR÷ByMÎ=‹jÏ¡úpñÆT—Û=ïî§ž}ö?s~÷*·ÆÎ‹4Aë^ÝÚ)µ}kÞ #ÆSŸ”ÄN¹Ÿ¼Ië(|„Ãád+›`P¿Y—Gu‚Ÿ˜Ð)üDWò‘ÐþÖ{(²Žúë«¿}º‚®»p¼ÈeÕÃy¾°uVR(/á·óèóåÛ褶/m5O딿ƒµ5vš¨ÇüÕ; B–³LUì<‚9#§ËI™é½xí⤃hÒ¨A¢Œþù%Ûãiüˆô—¾¥ÊjÏu»ö¦¿~²œ’âbhHFš(Kþ /à#afgÑAZ¼±0"ø‰Îâ%"µýáíåŽ-Í»¯À‚/®’BC Ç œxX,;½¿x“03+ˆra¾õ©SÇÑ'ßn£Ú: A•ÝÃp˜‘Aº„ÏŒ)55#¹ÿàa/õKM¤;¯>_š˜…ùy“ÅEx¾û²)Q|bâß&NœÇ5Ã{ ŸùÈé¢Vk²›ÍØåú™“è¼³†ÓhT¼)=9î¸ê<ÝÚtí ³& óéc‡²@³ßûô3¶û§%‹}™é½©? 7{•òØGt¢åÕÏŽÆ[XøQ‚Ï53&PFZ’²K®Ã„ÆdŒÍV63«¬ª¡/¿Ëãv$ñÉKt‡ö‡©«;¥ôU-ó}àKÁŸö0ž/ì÷³bˆ@²|K¾°Û8¦Ë53¾= ‰}·i U×ÕÓJ6€].ꌺ÷j¦9ïªkîåø†)·^:U-53=ä è¡ÍÄó}ÛeSÕÌ£¦švÎà ƒÔÒt£g¡š£YÅš`!ë¡ÄX“²)Öúôjü}ŠcSгÉÀÚކåCUˆÕ•³6F­n’w§s¤Lï{d¦{ .—»Û‰€ÂG`l®¯·ÐŽdV[oã6ÆïH¡Žâ%ºKû#¥©‡ÒWLþ´‡ñ|@Ô9=B ñÌ*8DD1Dé¬AõŸŒz¡~ksŠÅ Ãáñ§ ¶œnx>FgE;Î 69=ýûÄ-}fºaoÊ*žs<ï&sÌ#|1´”RK4Š]sÁÈ}©¤¬’Žž(§z«ò÷o±"C2Ré$ 5ûŽœ z6;ÛYtˆÆg ç§±&¦jоl/<ÐX†M}£9Ú¸aý…o „˜‹¦Œ¤ Æ£ŒÔ&¡©ñB¹V>Ú™Z6 ܼû¨¯#‘Ÿè^¢;µ?¬ßÁ…)}…ˆ·=ˆçëT£^ Qfö=)L¹JuÍÍ:Õ EýêxÖç×ׯ¡ {ˆÄ®4˜º4=óÒ Ÿ=™C37MC†€©¼T"ÐÀóÎ ÙÔëîz`×W 4Ý¡Ó¸Žœ‹®ž>ž^yoýö­/YyÔ?!JÙ-—N¥¿}²Œž{sžˆZ6uÌPqò¬³GÑš{è‰W> »Ã%Âøã€Š¿‚NÊ¢¿¶’þðö×lÞ¦£Û¯<—>ýv3ýìõ陿|ʦgUþo(÷†…À˜láðëà',,¼F2?N^¢;¶?,ßI… ¯àj€çª‡ð|‚,L¢š1—7Ž4 ¦f á¿…–Ž2>üáÑ›¯‘áC@á#oæfû˜ñ „ŸÈNÕÒ§7Á-ŽÈætÓáJýc›…¾,²µ«r:ŠÎí¯¥Uá½u '/ÑÞö·^ÃöíJLÛW㶯Rúª`ÿ1,’Òx¾¶AiÇQ¯¡šÔÉGYe 'Ì4vhÒÌvàÆ%ˆWŽz–Wײ 44="8žC×"‰¦Îhìg2¸‘·C’D § €ç=.ÆÈfÕšLn3Þ‡¨ÿ>GSßB˜ †Z:ÂŒ?‚¯•FÓü‘0±6Hfü]#÷……À˜ŒPÍ•µÎÁÁ81^BO,®¥¿ª¥¢ÓNznz ™u훫» SKOžÓÜG«¥û‡“—µý-Õ1”ý]i(õmíZ¥¯À§öž¯58Ú},ê54°GÆLX%;YZÔMŽ›íF¬.´¨ TÅêG|c²•MÍjy †Ÿ8Pé¤ü“NhtÅÐDºxˆŽ¾(´Ñà$pF§j¨´ÆEol²Ð7{=Ú›çgÄP_“‘ ¦›GèOëëéáÉFJ2©éÛ;軃vúÍ*O¸î–°/jû[ª_(û» ÓPêÜÚµxžÀ§öž¯5B:Õ AEUjã¸þNÁ#‡×9ZÚuÂIµ¶Îã)Øà€ì6„næ:³@£´#Šgâ0íN5µ†µs +¸î˜³Ÿ}ã3šù=ÒiÏü¤@ É/>.oÐZØÆs¯V©cù0Þ…æÓñ-\#wG£÷‹¬ ÉÚ„„€2þb,ƘŒ(gv;[~¨ÎüÖµu£Ñ)x­‰jínÒòÛý÷«b©Êê¦ç–×Ñy¬}™{©™V¹(’M*úñ¬sÑ_7×ÓÖm9î 1lÆö‹eµTni›' /Îö·…O{Žw6¦í©c ×€?|jÏàù$ès‚#ƒ¾E×^ ¨JñRK°Wýóå±tó§ÕTls{yH磾ø€öõ£2 ­hi4Üt©ixzî¹ö¢àÝž=\|ßõÓ©wb,Gc:EŸ,ÝHYƒúмþ žÀQšF?á_˜ñw¾Ü×:üî{¿Øî‚' õ:Ê£ž„€ÂGx„æ‚ä'eÁ$Þ¨¢±,ŒäñéÊšØGKñjºû‹jÚxÔ!43³ëéú,½h€¯™°/y¯Z@ø]Rë¢a7m9æÀÏ€(¼D¨í¨¢AžÔ•˜YÕ€OWú xK ¨hðpx&ȉMcßâÙ³^Eo_K2 aûT}“€c \£qæ: ¼D¨í?³R¡ïéRLC¯¾ßð6+Xû=Aîl¨ÖÐ(-oÏGßÎŽø§<_“V—7™œ½½ÓÂÌ!±ºXCEeN:Ÿm_çxɬ¡™³Ö“å99FM÷o_ ‚žý` “¥ûäÚkgŒ§‘l6·Aذ@IDAT˜Ã;®ßµWã†÷÷9«ùODT‚–çåwrHÙ JíGŸÝ²öcñº]¢HÐ̤&Å7h³;éú+' a;}…ŽÂÇØhkFŠkö*Qðc7 Jì0Ãmðø ¨(wïa¡ñIKŽ#,„°­ð ‡Ã#ŽË?Âä@y<I… ·òFñ£q#È5ˆ¹½–Ì1Ô+ÁÌ>nýÄäpB‹Åšü Á ïéJ¿¶‡ŸðW“æN² Ù½ ôò:M¨£TÖδ’y?GI»-ÛHé|ž…MÏ*ð£ /îöûÃ$Ô}…i¨õlíúp=_­Ý#Zõ&œ—Âjâ_sØÅ éZ:Á£4þ°xTöz >vzŨ¨_œšŽV7ͺ„³>QP–œ‰²¡™Åš=a­­¬5ñG˜Iô¦¡ýÓD$²ÂÇéþÙ¶èäk~|Û%ÂäìtU-Íe!èƒEë釳g4ÇYí·½7 ç 8gì°ÆÝ0]ËÙ{DüÉÌ´;0CfõÄ8³Μ\×݇JDõ-ù¯E”·Nòýè&-Ë7Òr6é¬f)¼†ŒÚ”¦¥¦k¨žŒ®rÒº-ôõš]ü-0Ñ̉#8¿ÍH2´ün{„›–®—û»+ $÷UCÿ7ËL‹nO`…è¯[êiÙ~ÿßsÔx9ûÞÜ4ÊA+îâ(g‡ìtÿ‚š®oHÕ@bAÑU‰j¦#$Ý»ÆhG*¹ø½Já7óÞì&ÿôß èÐÀDö'àèhÇYk*uD[B­S¯Ó潄±èè+J¯óÿÚj˜‰ñnŽœ¨hÖøb6+:xœú¦$Ñâõ94>+³ÍÙÜ^ñf¡ ÚÇþ0Þ¤k!W"£Áÿ¦ŒÃ4+tšÍÊšÀ÷\¸f''Ž=FC2ÒÙôl¬¨ƒ†g–¡EBÂÁëg6ô¦\ÛsÖÍ‚ôœfwã–bò —…ö÷n *ÖÆ”sçÒ˜Q"‘s ¹JÚÓ|­ÛNÉöRJ³¡ù«wÐÊm»é¶K&Óè¡ý…?I¡!ÊØ‹he#ß(o±…ì3sí‡UÂW·ŽyoÅÝ£ =æÂÞ—qÄ3)J0ª©ÚÚ>¾"Øö{¾w};b;1íˆvFîÑÆp—õ_;<í}0V°Ý3a&÷eõ>@`fÖ‡52§X3ãäçõ×ÑŽTâM8~#ÇŒÚKC³³ ´‰£— ŒöR÷öÖ¿=÷”×tÕu´ÿè©Æá¡™i´÷ð :~²B˜¡å{´"(±íß]°†3‘fíË,*=]IK6äµX|IY%kZN MÊÎ=‡)“û¥¬}…©B@ç€5B åí;Jµ+]Èÿù¾f¯ä©ãGdÒÆ¼}TÄçCc ޝ9›RŽ\K"|›á ³tc.ýíÓTbÑÒŽØóh—y*•ê3N¼ØÞ¶@XÂ}p?Ü·¤^Ko}¾š–nÈáÐÂMaÿÛ[¾¼Îƒ€÷XÜnKH  ›Piñð&öwý½Æû<ïë;¢ýÞ÷ ×vGc®zú–£`í»_þnæÜxÛç÷¨3`JöAŽ…ž½ÀD¿b3³sþUAïçXé¬"Þz’­øŸÊLhÒÊ *Éíc ôÂ…1Â>öÅÕ–…™llûسè÷5ðüƒ×7n²1%{rN˜ßþã Žvf¢él‚²hmޏôkž½E€›/žÂŒÂaÿÓo·|oÒ“ÏLÚ‰ˆi ~ìï2sòHºhÊ(ñ;?в@pA>øêLå€ù\/´7«ƒ^ýßb1Ñ€døÈ\3}<:˜Lÿýz-½úþq Yмq&¥xùïryŽD 3ãÍ ¢"ÈÆ)]?*ˆO.UÓxÐõPîQ©íM[c§ÑÈºí¬­Ù)ä‹xâ@Ç]i‚¦ $׉@´" š6zöE³8wC=3X$l\!°\üßJ¡"ö—l³’… ÛçUSœAÅ*á`æ\Ú¨ˆ<Õüî‘ü¶ïÁ/l܈bX‚‰Ö›ÏÞ­üä¼1qôÓ;.#+'‘…Ó0èª Îëk9a'…à°¯8í+û°Îdÿï2½aÛ·þöÁ™ÿ…‡gS½ÅFJ²sRº9ï,¤;®:—ƒ B˪m…ôÅŠ­"ªYlŒ‘ºé"µžC:›MF6GS®–k‰@d!a&‡µ—_¬Ü.„™<ó¤.¯ „)Ô#›­•|·‹R8—ÔØa™B¨Ñð÷B’D@" ˆV¤@@ÏÖûñÑó'Ìx%…o4ävg" 3yO÷òfpàcƒÈiå:úTE5íâ¼6©½š .ÈeáF’D R€0c±ØéýÅ›¨V'43‘TWhŠ&ÖTs˜öm4¤_ª€ègÒ§&¸^B®¹#UNÚwÒA{ŽÙhï1 ©'û²P;32Wy¶D@"(R  ©Î+gë2µÏQ/€âå)n€A¯£{¯›Fvíãœ3…dÔë…Í%ç´>º[7XV>*€©,.ß’/ìfß•®23k `Ôg·i ÅÔ¬ t/™šÝhv&Ã:7G­ŠësN˜Ã‰k0âw•‹Žó>‘LÓn!—µžœµ6rV;èìx7I}Wså/‰@W# š0öÀs+ÎŒJÆâeQnrf`‘$è®@;ã`óIDC43ø®D"¡^¨ßZNl;}Â0ÒjáK£âèg=‹w«8"˜Ë@ëØ›Vó‚ä–Òš"Ÿ^Y'‰@ðH&xÌ䉀D@"ÐP´3û9zm…C3gG4 ¥º Jª+¥\ßa}ÂL4ó×]Iö:ìrÙ‡ºíöán—s8wÌPµV?`‹VŸ¾¹FE~Ù>>˜NKáÈ¥}9·OŠNGñ,­F*+QS©t‘ Æ=WÇ‚:âÄ9:Á@ƬW‘oˆímœ,<`¤@0Tgž対O×|PE°µõ¦Ñ©Z:7CKÛJ´ågÌ ’*8,cE½‹’ƒ¼Nž.H$ƒ€G qRÁ~ŽÚÇ@k@Ú¢¥w$PF¼ZD;É9É–°Óï9ØŒ½ñü$¨É UÑnNÜ܉úq= ”ÐÀ~)¤g¦\Ë9¤º›ÙÙŒnmÙŽœLI»¼¨ˆ£Šˆe¶­µ”(°à0Ö*6mõŽ%â34Ÿ™IGÜ?êÏ}„}Õ¿glkØŒ¼¾ÞBUÕÜß§­TZ¢£oOñüøÖžQ¸×FEçö×Ò*îÿžDC8…ÅW·ÅÓX „–L¡'éÞ Fzg‡•îo Ë߯¢ 9ûž9?†f ÑÑ•ïWÒà$-}zSÝðqåŸ<ó™Ç»°ö TowÓôÿT6K“w/-VEWsÙYBú¼‡“ΨR/<;?9ÇD÷sÝà  @©,Øþå23IÓR-ßkÎÚzú(Ï*Žá’®¿ÆÇñ ]ðveã~¹ÑyH&H¬‡%kèÖl˼ºN\9˜iŠHL^_MìûÛ•fú0×&²ÿüÛ:Ú|¬éEä–¥übßýE MÂ/z¶ž®©§´¸že*Nòœàx‡óјM&9ø«åž‹ÌÍw¦Œ“Å:TÆ€óÌ|V`eæÇF“8oÙSç™D~²…{míòá)&:Ì9Òv—Õ·y=òÔ žÂa³ÙÉhpŠPÓ‘@ѲaÅåZ˜¹Â|lkN& 0M ×x+$ÅÖP©Ü1*«jdZL£ÒŸS-d²ÀÒ?NMÉ1­§ããî ]©¥Ç§šzœ@³ï4UàåbP¼š‹‡èiÙ~;ý}«…®®§§Î5ÑSKj <Ö÷ÇèÇ kXHiú™u„è²q¬9™š¡£u‡Ï¼è™ bè¯ÚÖÐÅÔ4÷’ÀIÑ}éÉsŒ”hR !çÂA:z~F ­9d§£ìgu 9¯_n&‹ï̶o!òw‡" šVàí£¢Ÿœm¢,~Áv•:èåõzi¦™úòGpLª†nጽø®þî"³¨ð²þry]Ê/îJž…yc³g T eo/qÒÎò:š³ÑE“ûë鲡zºx°Žz·ñÆõ’$þSævw‚nÞßÍå>‰@7F‘ú¤¶²¶ž,êÀ#ñ•Õ»)‡Ç,3™ºv„ž Ð`vùifä.á13Ãßì±Óœõ̱Ñã 4Ÿ¶´cñ¢žgxä›Zšk|³Û_<Ø@sùÝ„ÆËõ# ‚û×v ï­¡·YÓ„wÚ&I]ƒ€hZÁ}6?°¥5nznE5½qÇóOÓмB+Áœì™eµâáż¢ØNÏòïåü¡‚v3>GØá„h)“ûsŒŽClzñœø°o:êË üQ˜ÜOË‚“^ „R¸PË?A `³;éÃEù^.:;›2Ó“©š™´oÖì¢É£Ó’õ9"áeJR-ß”OÇË*è‚ñ#hsÞ~ºëê󃸓æV¬U©Šs2¦´á2hqPÜ(,¶ÓÕSžn«t>­cé[\la&x ó¿`^¡œ5  ã¼ÿ• ut¬ÚMO°™ÓÓ I`Û6&0ý0½ú 3ýilîôà$“Ð,Ù£/ ‰?‹÷ÚéG“MB“±ƒ…zhkjlnZ{Èó0ldÞæË"ý…ù,¿#05 „Œjš6@G]Cfæ™^¼(†ž_¥"«£©ËóO9èë=*úc¹îÜšŽùÞ|Ûã‹k„É¡÷1hnŒÌÆ!žB*чûôqƒéÙýlF'©ëžÓ¶ëÎøhcѱÝpÓ—4°¢¦°LþLå5§Æ8ã!WJùŽUœpF;ªÇ4ži9ÊBL/^ƒ0k`ñz¹”kZ[kÝÜKG?<;žvV™hí í8Éy<.Âë¨7/®–ÂMkXÊcþØ’€n˜5‰*kêé¥E¯üô6²ð îêmETÌÄSX¨éÍIùÞüt'ÇÔÑÔ1C9 gC/o[mïÿŽr¯D û# ˜œA(†.d¦k(OvMì£Z™··{lï¯ËÒ3³e£·wzšA,ìÜÀû ЀÀDÍ]W/ÎQîgc à`|3A  ÚÐ^‚EÍñjkÌÌœmÌcÝaüÆš÷5E ‘"•Óê^{ ´Ú€Ì[ËÁE,j*æ{Ñ÷/üñ°&‡…ö6 ×)|„gÍ~Ìhxœ”àRRë¢aÌ x÷ÛŠ-D*û_ld!÷Qp¡Qh! Ÿä{L/` ³ûé(¼„Ž™ïú+e³ö¾žû)èöïe“³b¾/ʼ 4³{ÌͼýÈ–²šKhX èB—±`ßXÇèYÒq¿ÀÍÛœû¡éZôýDºû,A£,Å5䇯½ªã¾L0zu–r kô•¾Á¿ xK ¨h¼ 3g,7º“ú·±3¿‰¥þ?mðØ*㻞M0Èx“÷löoç@p*ÓòË5žUÍE8oz—gpY8G‡Ø^óú~ºkJ,Õ» ´d¿ƒóK¢b5€×K 7ÞèÉí@–™F矅@@DkwìNÃi‰ädfçú™)k`²Xí,Üœ _Þw õMI"›ÝAŸ,ÝHñò‰@Ô! h6„,¤@þ¤„™´g–[iía‡˜A†¶&‘"oçþNàxÓ(=3¡M Í*6§ • ùTPm¥Þã£wùŠ–yYÖøH¥SX@`9Æû”±Çûš€¶}´,ŒÉ>!¸œ©e9£¸Ýgìé¼þp2°€q:Tzœù„Ë™ÉW3¬4't»†·-|O*:íV"Êï@Ö /á}®¿öx÷Ýöw~{Ú!ãXþÍÂüö%û綦É1ðJð/ZÏfwÐJžŸ©Ú(ߺøþ¾šËƒpùîõqâ‹°®d§·@ƒ°´ykk=k¹ŒBä[N[¿+­Aü B1¬A-nд)ûBYÜVŠ1&4á÷ƃrÃ/>¬¹ßs¢bg|Œ´n /¬MaGÉ@h«@Ÿe5åÇ7Æ éÿW+ëx0²ÑCüR|ÂQ7îüÜ£ö-+—%+OÞ¬¾;‘Nð¬ÌËlw(¡~Z²’Yonv üy`o‹åt›`OŒpÓ¬0ù£Ç#`44L91&«ÿ9ŠBØü Tk± {û¸=!V¦ÆV0’랉€" 4ÍÓ†"›ýÉg€ù­‰M¾îx»–MqpL!kO«ì ~ AÿX¢q°DÓ™£5Î¥%-Kð·Q"†Ù*O‘¥êTÙ£eË–ï°ÔåjömÎ;²þÌ*†Ô¢oï7–”Ug>9vkð5 Ï&†X7?á{g˜®#’–ýëvö¥¯úI¡AIÌ­7Ð@vTßs:0Í.i‰—PÊ eÝžö/fS¹GØìì¶ÑzñŒ+æf¨ÇÝãŒÔ‹ý‹¾7¯Š~~¾‰ž›Î¦˜ï;ÈæõøÖÖ3Y0‚©& @§jÝ4c–5'jáoæ}Í¿Xºýxž`Ÿ5ßIhïóümÃ<ÚGDTA%–í¼?”ëü­E_1“êï°Ü =B ¤; =‰Öç¢d{)•ê3‚jâÇÕ¿XÊ»uñ+…–ÆÂBËÈ7ÊËúÞgMì`áð싃úá{–ždnT7Þ a£=ÂMºv$iâtI±£ÎKªÉ_[â[¦üݳØ{¨„*«ëX±Ò±“4jPžymHFrB,¥$ÅÓ–‚œ˜/‹vî>ܳ@’­•øABA¸h5GIB—ûµ‚Åöò­G?ƒéü9‘!„l[†ïWËj¨ŒÔtÂÁ-;.sÂÉöÂ×":º D C˜cŽæ1ì•ÿ®£öœøî­?Îç{`¼)ã¥õ†X™S•5 Ü_ðéý¼$,§P–^fs”¹`ø‰ý¬m¹-ÛHéŒÌÐûÄyfûKyÒá¡g³©!ˆïÂ=}:sž–°ÙYo6IËЧySDqbá%Ú(¢Ùa¥íX·§ý{ØRÏî÷F…ÿbn†°Ê³ ókëÚÁWXð_t{¼Ð¦¼¾±i2x8gb‹,A~„§³'žýçVÔ5 >ྜ(3)~-J# á¯_Û€®r$°5ÌÿndÁ pï$¶ YÂËá ¥¯À§_IíC êå%LeǧO³ X Q õ'”@˜i‹ü]×Ö5¨ü}’ãMâæԿ¥ënŽ;ØG2aBzß{ÞüÀm«ÙꬭúÜQ¾ï£C{ø`KeËýÑ‹À6)û÷üïèè‰Ótåã(91ŽN–WÑà.šHó–o¡«vЈig—;$ö#€(es.6Ó[WÇ 3³µ,àüßÚ&m©¿’?àtÏM3Ñ–&Ò³Us‡„n‹*xvyG‡1ˆq‚³µ&Ê(Z–L!¨°Ð O~u3s¸¶î®ãc甘w=•ŽHf„Y/ç$^þúCgùÕ(ã0„ „¼Žg4ìì ?±œ£ÔÝ4ÊA+îâ(gÜÏ?âHZ0_À9Zª¸^cæý× Í›v°ùú?¯‰Â+̱–µÑÎûÚ`y ïk}·ÃÑ~” Ë’‡9¸Á"¯åÏœo¦ÎFÇúSu.$ãÇSôÃPè%Ž&«„#ˆ|+o-¬W6°? ¢ù 4¸v)k‰60Žð‹–þÄÁš¹mêXziM]cn›`Ëò=}þ|ª‚µï9òwÛß«m—1g(^ Û¼ŽÊH"kq)%8NQ¥¶wÄÔS©ê•Äš©fRsFgÔuWÚ¡œ×ÒÚW¸Y\loŸžùQ«ôæÉZ,I}_ñrÎj7©>Ñ™TóòÉ–š›–À¢ý÷\;­±5ð™ÑÀ™ Ú˜7Ÿ½[l+Î1€Æ  ‰‹§¢J0!å ¹–ô Ú«™F¿%3+øÌÀÜ{ Á°øÒ·ü]_ÆK¼³ßs[ú$€µB»Ò’–¥¥k;{?ã®97÷3¾ïeM÷vØoµ^2bή§ 0à1¬\kRßÄc‡Ï /)ã¯ÂG(cs:c謜Ÿ(cFýfNõs¨jÖDÀé®/ªEt.EËöa®G8UÀ,ëßÖR kã`Š(…ÊKxß'\íG™Þh‹wùxþ}éŸì¸E!oKe_Kë{ç7•çïÝ»çË&Kš–Ê8Ì&™¾÷¬`™;?÷DSƒ@ã¯7Þâœ:X‚!¥¯F N šç æ>=áܨhИMÑá@C£¤PþáÓ4¬>‡¶ÅN#Ç|ŒRs`þau9<°©ipj¼Èæ¬Ô»=IÐ ÜÜ6Ú ÌZ<ý᎘[[£ïmä7±¡án5«îg°3Ã^§ú³n"åiè¼z(ÂLkwTòÖΑÇ$=5ílZÃÇÛg¦µó”c`¦“&PBôÍçÎâœjýciDßxŠ7›Ä¸èõy^Ö_ ’ǤfU|r³Ê¯oMÖÜÜ8h󹬕YÄð=5æa®‡ový^íÖÞVðä¨\>öŒ³ÎõïÿyGÔ՛в…V«å|t:‚¹Æímqó¾ý¦3þê­âÁ}Œ0n^õ gûýµ³;ík­¿‚m‡è+æG :­àOCáù‚½w4žßšæ¹Û·W™YÀC‚Éh  “È쬢‘uÛ#ª}#ë¶‘ÙUÅ š8‡ÉÕëõ¢Î¨»ÒŽöVÂÍ(])*]tèØ›Üf=Vð"9íëX{î5€@¸qÏ ·ë {ë( 7+†¿œûpöyéí½¯¼.ºèO³Î]’­‘ôY+0‘ó¡e²é˜N™þï‚vø¿m×yyW–rk_8„§}OöMž ﬗw=ÇÂI¯÷³öåœÇf^û…ýµQûýaÑÚŠùPNÁS£rqŽÞ­û€W×b;ܤŒ¿ ±YÏæAz½–2Ì.1ncü7!tq]¦í¾÷ 7/ÑUí÷mW4þ ~|)øSð©áàù¢«@Úi ­ðš?ÞZ ð2pd§þ) 4ŒMºzÛRvíf‚„Ü•„û£½íÇ„­rZ¢™m)õ¢®¨3êŽ6„‹jö®/?ø§[>Úýó‰7Úr×e¨áÖJ)Ü„ áè-'µW]qþ¸èm l™D@"б¨Ôñ6·j®r‡Å=™gì6=¯R¹Xˆ¹Ÿ÷O4ÇÅœ«1iÎaÍðsã_ß“RøäèÅÌì×Yì)ÊuÂ3F()FK)‡¿ÃÉOÀmæ;•œLõL“ÖZÑ‘¼Dg·¿¥6FËþ¦¾:*øQð¥àO;‚çkà§•w"Z ôÛŽHh…§ÛYg±ºÅo“šïTT¥xPŒÎ!c2Òð¾‰40I/>BjV ŸšæWuÎ/ØNN¨^-ê‘ã¦þI&2qýPGÔu†´Þ“3ßpÒD·Ãáh Â'øÏC'w?9îÍ¢'Ç\htÇôU©UKáÆ9ù[" HÂ@Ÿº/Þåt4²ÿ”{ÊS¹ÜSÙÿe ¶Yër=çcù¤¾Æ2ØiqóqÕ±z«å‘†cÅ Æ6ˆ§<‹Ý.WÏ/"˜«±«MI‰áÚ3B˜:œŽúPù |„« FJQS²ÎÖ¥üDGó‘Þ~å9èkï¾ ~´#x>`¾éŠ` Ø4òÙÝ«`ë‘>4.»óDE%çïV° ò=êR:N fs Y9¿ÆÐôxÒ«+8Œ` U³–ʵi"úY™.-à<5¾÷ ä7â#D_ší%9JE"­¾1JKŒ!3Û5ÇrýPG|,QgEýHÙ­SQUë¶[­M1¦}NÞõÔмëoXÆÎÙ›jU×ßÀó[7±ï4^û÷¹™“ó[¥úXðSþ”H$3Xùüó7üÜépÿŽÎ`Éd* ''r¢wÿSr;·ðÈ_¬v«^së5ÛqŒ‡ñbX†ˆóøVåæxUMŽ8‡hŸ[«†ÐÓ,0€Ýb-•Ÿh‰°Z­dç0Ø)ÎZÎKc!NA×iüDgò‘Ø~åYèkß¾Ò²ÕÍàd6eéŽâù€ ø>§ÃÑì}èxµ·Ž‘(Ѐñ>V]oUÕ±àÃv…¡çET“ž…#—k6“Õjã¬çvêëp’Y[MG*¬¤¶•RRGpbÊN²rlK§*|ðhÜ‘YXÇI3¡‡Y¢ÖF)œLÊl6ŠzÅÅqxÆX³ØF]QgO¤³Ðä:à<ëëjO1–•Å/´ 7 ž§Õ¦‹€Q Ü<~Šòö¥Ó•µTQSGV™!zš¶ÄØÎëb¦ì!”ÙÇ“¸2Ä¢å剀D Û!PøÄ˜¯Ø?æéì9y3ägŽOÞ„F¨Iý¡›\ç>5¦gÍH­Ò|ít»~Ïþ7Kí%9Ñx äTÍiï“Ü:ÝMîz% £2ƹ1î…ÊO´ÆG8˜àYp®J=™¬õTnç–ðd%&,;‚Ÿè ^"’ÚïÝç‘¾í¯¯’ NÊH4PB|uÏ\¾ü4ÿÄûõ>Ž=íî*^"RÚßJßGÄ¡^:}^`£ÓðXƸ®RÓØ>ºn»0o×<€ßÇtü4ÿô~/p8*)’ íø:~ìƒ\•êG{•аÌÐ#+‚‹Á­'·Ù#¤àYƒ L» ì„_ÏZaËBÓåÑà¸n„ LZ{HaT%uШáÏÃÚ"¾ŸG;Ã9›™A3#ÔÚ™ð~¹{¨íÝÑbˆçŒÄ"0æmàÝ.ê®Â §ÓEË6å±¶dÕjâhwìy’hÕ¡â¼ú ±À1p8ÇŸããå4{æDºhʨnͨãý¶'˪è³[yvèȉª4ÉTnNÕš$ªÓÄ’ECìwÕê3ÇéêHï²’ÉÉIÌ8¤e‚å4•ç¤u»öR" 7×LGSÇ=/åµ3#Q]ka•»ÏaÃ|hgù‹gÍò>¶ZyP"Ð yæŸ/É‹1•ÛÉMÏ~ã Á®§²öó¾PsÉ4ò\–ó0þqBÌ›Cå'ºŠNÊ·««x Ô¡+ÛûwŠUÑ9ÔŧvÚ_Á|#ó}yUìaµÏ@OM ä$s‡ò|ÀHáûÀGóÏù½î€;êI $,ß±úËyÌ~à‘Û>X´!éç÷\­ÖëBO‚‰—Q1ñÁÇÁóÛÎÙÀ>+h,pòkÐÒ€ñ ÇìŠr/h\`FæÑÎx"®Á¼ êǘ“X`j†ã¨[¨SÆÏår8ªÖ~3 —‡Xð‘Î ætÓÇnMK‰Íø¼V©ÝÂZõ‰ÊQñyÑÏίjõa8¨0ܰw†03õN:¥ëG1ã»$Áj¥¶7må䮈CÿÙ²-¬9t±Z¶èweà C³;¼àŠïÊîƒÇéÍy«ÈæTÑ!c3 ›ïK.Ö×XÔ1b)×¥‹F,¹¨—ý °ì¥w¿^GûŽž¤[/9›rT°ÿ8í?v’Š–ÑQÖYmþC"z2›±õéOÃ3ÓhÄ€¾Ô75±‘Q¶žò|‰@ ˜ØÜä­«Øœ˜3¾¿}m,Ù™åžõn%e§jéçsÂËÞ:Xá¢yVú_ûY2=?#†J9É $5]8POŧ]ôô·5t°²ÝóPV·;Ÿ§Œi‚à†ˆ1ã_æ¡—ðx*?ÑU|:¥«x ï¢+Ûï]HÞF?=:MMŸÚiÃ1Žã§ÑÒA«^Ýe¤ç.N¤Ì^±Âó…ïãðfåà£y×ü^$cJÝ"I A;ð¥ø¶Ó§OTíØúGÕ„É/½»`û¾ÙÓ[ŸÖÅÕmò1€À ¶ù¡Ã6âǰ]#„0€"áè×àg£Ìü«¤Q&¢•ûò  4ȉãÉ3ƒÐÌÐÒ O2MfׄJÀíØÉ Ú¶vå»uuÕ5\FJ,ŠPÓ82æÊý ›| ‡ÍPî”pãrOw«^þÇkí•¥‹Ê—ýu]ŘF‡—a‚jÎÞôà»]B˜É3O ï‚, ¦V¨C6UübÅvJKЧ1 &Uáèÿ «sÆéÀ ïÁáÒ2:Áš—åÕT^UK^8F*¹x Ÿ—8“ß=-ZŸOU®Ú;•ƒj˜Î(/”n6/Ó¥‹e …­Tvìæ:UÒþã§…‰(ÔúÕšªV÷# ¿Ë•žì¼¨¸žjþ¬h8º É]G%U5¤ŒvÕ‰gÏycÓ¹ã†RoÆ?p'ymd"à`!ÿë=6šÄÉ-_Y_OG«=ŸÞSuNZ²ÏF/®vÐLZ~~~ -Øm§*«‹’M*º%ÛDÿÞa¡VÕÑcSMbùébï(¬‘ÙÞ.®U#ÁõcÆ?Œƒêé³~*?¡ŒçÅGK…PîÝÙ¼„w*uèÌö{ß?’·}ûéÑ>¾Ÿ'–óù‘dgš“pê—ëuô§+bhBŠ)ì<°Qø>ðÏà£y×ü^$cJÝ"Q ÁÌ :ÀòÝÂùkâ’’þÍÛ÷ÑøÐÀϳÎ<ÿ,f ¡&X°q?±ð344¸´4ˆ|…„˜ÄxB4{|l‚½‡ïùÐñPo)دڗ¿ëóíkØ!X<‹…×À8{FUÞài-;Û-§ñ¦ ìWòz±¿ÑUù?Íþ︗KclªYî$ýî¢{³ð‚D 7 кÄô RoxÁ•|ÙOs9ö' Vzl¥Fв¡okjëéã¥[…™43‘B¨KLM5½¿h#=—‘ÆB.ü§B×H¶§}À Nûl+œ ‹ØdØxž‰œZK`A8¸²Æ‘cêØ<ŒXÃS²<6ß ·0ãÛŽC†ÔÏv VP©®?0ö£*m×/p­¦ÁUÏQˆNRŠå(U­Ë¥…ërhûè\;}õI‘Z_ÌåoÊ÷\ÇSìî0,v6].8å9?‡}hŠËñù%ÖÀ¸éÝV2s˜môèÙF:§¿†ïõ|šO°†fÎZ|º‰’9ßÉ}ãƒ×xj9¦ŽÇ¥îXG9¼F>‚·Å؇q0)%3f³Cå'€agòè¯ÆþëD^÷õG]Ñ~õˆÄ}¾ýt 5ÙƒÜôÒz;YÝZB€ïÇ–9èÕ+44m`x¬q€ƒ7ßwtÿÞæÝ~ù½HÄ-uŠ4F19C¼\|„j¿yÿ.»õÔóÎãeåî[/ª—O <Ø£ ƒýh„ #|, Ìx€Á…"ÐàzOGð ÿÆ}=¿=‚ ê*ÁvffÐÌìËÛõåò/>YÄebJ¬+ðÅx7ª–Ôì`Äô¼Û­þpnîû|tèWr.gÞõ ŽÐ’Ïñ(SF¿šs_îOÆìR®ò]z%/;ïñQùܯ²‰n4æÄ±Gh<½ÏbÓîÏkèÒ¡:ºtˆŽzó`ÞBÿAË3¤•[ ¨º®ž}f&t‰™YKõ‡¦f·i 5kiù–|ºôœ1bðÂ3Ó„gKeM=­Ø”O«¶ïxY5f*Óô§ÓæTªSÇ‘EcjQhй1 ©8´0ë´×ÅÚ˜ ñ³X¨Ò´éÓRyºJô™bÑ»ë©õ ¹÷î£]{çÓùㆠ¿&hŸÂñN¶T¹¿{!àý,˜zŽ\Xrf ÔÑ“Œ40AC‡Øä ¤÷zï÷6>Ø_Tæ¤^1*êÇ“ ö·E—…Œúæï¥w[Úº¾?ƒà6@4,ÿœÇCÏÌÇ5áà'<ãzÇóè¥Ï:‹—À=Û¢Îl[u‰”ãþúé 6)ÚÏM-µP¹EÅ‚†]TO¸XCWlWHÕ÷æûïÛý΢ÿ S³Vø½n±Gª@#44Œ˜pwÎG“/¼ø¸kʹÍýï¢ÄÑC3Ü“³«Æ íRž‚ÎQ‹I¾²Ò’’IÓ.¼ù‰„Pù ŒáÍG Spe­ð áæ%Ä ‚üÓYí²Z]vzKý4¡¿ŠÞ¿ÉL?œ_CGY3ëà§ôÉ%œóŽÃ<lðš×3ù>GÕ®Mëþ¶yÅRæ˜Zç÷º œ¾q¤ 4h.¾Ö`†1å £…†;iMþ¦…ç^~åÕ‚øbfÜøÁq³ý¾;1Á¬2êXg&ò^|{åW®óý­ìvm±ÛÝÈ€Œ¤a\G•Óé¨>P˜¿zýâoVÔ××*B ¦±ÀñxWàÛlTäw«–cþëF¾¼ë:–é®ÒƨϳXª¿Ï,u-O…¿5bî®%6[ý'lv”XWWÐ;knÞ¥n»ó\Îü1Ý•”óðØrVjÍæ„"óW^¨jQ˜áó›QÞóSÅöŸ¼|œ8þêa±“¯š:`ؽµúdŽEçéÒö 7íŒChŠ—ŠDS¥1ÍîI?Ju"¹ë~vx6 z1Pvá™V´WŸ~»™™ø=TÃLAüyBÓ÷Œô2ás³7f rô¡ìŠmôÇwÑc·]Dƒû¥J¡&Ò;¯ë‡ï÷€ô$ZŸˆ’í¥"ja ·‡Ó?‚fNf?š²z7Ïp³ŸŒšNԺȤUѽ~ÌÉúĪéÆQÚQâ ÙYÚt”S àƒ ¡~¸»°pwÎö\i˜“A áB m˜›)þ3͆E†ÉÍÆò(ð t³v=’U“5'w8ïûL3@ý ça×%|Ác ðá‚ÇG;§ä›êä\:ríuôÎŒîÙ%[sofk —`²ö<«|øÚ ¨bû‚Ó¼|5ã±§/‘5jäà‰çÓ¢½6ÖÒ4 â¨t š|<…v†7ZXƒUt°Dȱ㬕ùÕô:ç_ô¯mVºw‚‘b³³rm”SÚ|.hëqÝ>Æ@/\C'Yðyq5>íêŸäxYŽ=G¨i‘`ô„°Ã“~vÖÖ|Í¿— 3~à€áYY¦ØØ^“)A«Ñ6·Ñ ry¦D M25…j[Ú%霛óJÖË9Åz·é¾ó2Ü!(2ªítëhƒXN×¹iI±-(á¦&àegæfåÕulblÓ<aT?½)NÔ3¨‡9Dê?¶YèË"Èí£@“éÁtu,«¬õF~$˜B†S Q„¿Xɯ„™ÃÆa,ÌŒj_ã¢ô*[_n=—¦T¯â` XSs1÷`¤P¥=Þv³a€ežlPÓ¨Œ$²—rJjÆŠÙØ¹êÏbaÙå¯[êéŸÛ-â7øR% A·Ï«¦8ƒŠª­g÷=ßû7ê•Äš©fBT,O¢féR8¿)Þ÷Œ°í6ù®/Â1x20Ÿ—"Þîi|D„uYÏ©Ž.þº~ýç>}ïÌl´º,vh|I í9ø—ï¿ã²²ZsR^þðð{ÍËŽš_‘*Ð`tt:Sù †ÓTøAº…@ƒv@“ƒQOf¸™‚\ Ì`jOÑÎ@ ¡š,À Ç}_ÞŧOdÏŸñöþÞ+ïÔ8ØgÂèwYër}ÉÖœeYsv}ƒ`/Kq>ÂÚõ'ØÁÜŒE ±q£/ƒMÖ˹wY©þm>t¥8!„?p„ V¸™˜¦¦™.:»·•ÜŽ»ºŽs á°þ‡F-gfâÆl==dz©ßÛ©Ö®|SoL°Éô,ÌHWr46Fƒ'¹k¸˜hE˜AÔ²eôͺ<á·#…ÿý ßš½,èiŽl§ü}G({hÿž4Ã픾ï¢'‹†F H¡üçi'ÈÝÆ9¥Ü#ªWFµ†“1qÒ+̨ÝNV—#¢› N#J½Ãõ=i«Îr\áPe¼DH>"B:¨§VÃÎÖa‡_»å™¾ÿ .uÐyÀA—>tâ€Ç?qzõ–¿8-¬’mNÊó2¿×¼ØèùÉÒyøøxoãC ÔÂÂÑ×¾M4 6À¤à¢<àP#(j5Nðû!ðàQ®åÍæä-ÌàHƒ/ÌÕYsrf°¦ó"Ž…%Ôl^¦-Ù’3Ó ¹G)µ¤7«Uî— Ì`´;ltÖ>oY¥P?ëÀ…+m:`!—¥š†›ªi“Cq3ƒ(¨tRþI'vÐCébŽ´öE¡³“.œÑ©‘ôîMú†Íâ@H„WÀ×d$¨éf¶{ÿÉÂzxŠéŒdz­ÕÎî6›Õ>œ¥D©A Q"¾-ÞGvf¤vÇ WñQYN©>ƒFÔï¤ÂÇhÄÀŽõkŠJ£¨QІ‚"Tš8Ø„ìK³ç¤Hî¼VåüõFBÙöÐȺmdvUÑð4ä9Ó5ä»ðDÕTÚÑžr»á5Ê)ùˆnØyÑ^eZèðŸnøMÆãŸ>¤O|=Ú«KÎ<{à“Ÿ[ιþ%‡U5  <Ëaá÷¢ÛHh€7:çÝ‘ø0I³ì-Ì(¾6d¢Y˜áæ &ÊŒ¼r`.k,8Ö¾‘‘/,|jÌJ^at¼¸ˆS¾©¬…?Q£º—ÈÝÌ>qÌ’‚†s8JÁ46c+jøÙ!«…›£v‘øÑͳ”ndòJêÉlrG!šF§xf]¡Ñ²þïïœí»ŠM?ž[^Gçejiî¥f:X墼‘ïÇ,Àœ¬sÑ_7×Óžr—ßdzmU‚ƒ34æB‚‚LH(äÑÎ 'í«®¥ÜâÎáÂI(ÕÇÄPÊŽæká!ž‘Tš-0²juèý͘EsÛD˜hG ¹Ëú§$ЩJž_;q”är®™˜ð…„n”ìÁ43fzÛQ‡wNK4‹¤Í¨+ê¬åº£ =Œ$ÑÃ:¼;5éB̹î×CžY\­Iês'ê®IHŸ>à™oêýñÚ_ØkE4Þ¹ò c­286ºí#j?|%ïÓ¬¹9K³æ<ÌQb.:#º™›nâèü? ÛMÛ(È[¸)ãI¯ «ia¾…¶ó#ãbX(†eÁ$Þ¨¢±ìS“ljðVpÐÄ>ZʈWÓÝ_TÓFŽ6Í̬Ázº>K/”ofû’÷ªÍÓü%Ók«áÓ ‘ÈÂAÞßöp€;DKÍá(:ªËPós£ç$œ±ÆdÆÌNz§®Á¡Ç1…QÝÏ6N19ƒp`4Èh2Òð¾‰b¢€ÊÑN»Ç46`Ÿš@ïÈyð™™43é1nêŸdâ$½¬¡ÁÂuE¡]êa&gÞÐI> ¹Qìûý¥¿ú›ï¬ê˜„¢bš˜¤Ë>³¨þÈ+Wþ¸¾¬La”gXáï°O¬üVŽ£ˆGÝE AÇ(L;: ̓âÀîBYx³ÇhhÐVà¢,ÀFY”}8'¬ô¼'‚Ù£Y*˜žh,9QVw€Ô®ÍÊM²_ËËtÚœ™·<9rÍó@ ³‰Çqºq¤ž.ío¦â£VZ¸«ŠôHÁÖ“˜ŽØé%vâÍeí (Íì‘— ¿-‘씓úrhU…•¨=¾6ÊõX³NFDhƒ . L× a@Ä·SåˆÁ‘#4žàâ‡üã~ÖbRq¤ ÌÄÃ\/\¦ß›É4¥ t°‚ÙCV~§†¦ÇsBÌ *.¯¡³8An¹6MD?CäÂöæ©  ä™Ahæ4ÛJr”’†¿[}c¬™‰áº™(–ë‡:¢®¨3ꪶ7zEð9ÁüI>"‚;©§Vmï¯/øé°—¶8UZýƒÀ@¥™Ýÿé¥Öòwï¸ÿd^žÂÓ)k…×ÃZÙ×S¡íîNÒQJÇ¡AÞ‚Œò[者B .Êï_>>ò@¡ç. ½oÆÙSø÷Ç ‚÷¡NÛVxö´š‘©¡%ej:8@zmc½ð¡ñ>ýG< b™–¦íU‡š F䢉{BÒ†PËC9BCÃæwV«jêa±É£ºª;¾þ¡¢øõIö4Èš/ÌvÒ{™EÔ<à<%õL<šÐÈ>4±f³x§llÊÙ—^³¶šŽTXIm+9¥0:ÙÙÕÓª6†õ}Ó¸dpYØîšße~1“¨µQ çµ1›¢^qq±dŽ5‹mÔuöD:ÃÙãI/%Ñã…È`Ï/&ýhÄœ<©zXÔL­½-éî÷í3ýà“›oVåù¬Êwqm¢£‘ÛÅ‘ï틞Ìþ”÷aéRR‚p1 eN‘ âÞ zy‹¦ ÔQ*kgVhh|ì›L¯ÚêI®ç{ž÷oñ@3Ó®z£Ok‡01p?ÁÅ¡¬5ñÞ·–ÛŒûQkfYò)Ö ¦s‡§‰Yí>³-Ÿ`²_*ƒ^/´ðKÃû¥hï 4¤r¨øj‹“jøÓ`sñqW­˜BE¡ÊÃw:öéã²´y%Vç&畆I41²â(>ž5¬¡A]=þ_MÚä†æÈ•ÉGÈ'!b(|rì²ææ²Å³[hjxTºsס‘þ}CxV%ùA ?Í’»$áGÀêpÓý_ÕÐÿÍ2Ó¢ÛHÉ#±lË€Éÿá@IDAT¿dzÁ†` µ%h°x´4NJ0²ËË4½íÇ©6 ͹ýutÕp==³,x'fmûýEfª`Áïk¨¯‰2â5´ðûñtî¿+‚ÎÉÑTJë[z·•ÆÖ¬#³³Šâô*š8(gâõ¤Q{|ÀÌJÁ¦u £ýh£–†…¿Wf!Å#ì°Iš‘Å,ÏfžpúÅ»Níê€{)•N§e?˜À™š3‰ bBŽžë*µ3ÑþdÊöE Z˜17GÍ3!÷£]üû,äð'Ç}?ŽGK[ÃÙ)ЄMYVD À/|»ëhe#ß(oñúBö™¹öÃ*йÎð&zt¡†Þ7™^Óm…Òå(CaªŒÌü$™8‰gý~:fDv‚¶Ÿö—;iy+BRòãØô†Mò>Êó˜¼)û}×<ÁÍQ>Î4‹ÁDšëHR»|oÝ`5÷ïŽÃ5¤7ÇSß>žÄ„` %IÁÅàÖ“Ûìù @s˜vØ  }­œ ¾kNLâyÿ<ç¶÷† B¤2Ô‚6„ÜÏ£aŸ63ƒfF˜œ5jgzt0ùÀJº B̓ ššûÐþnÜÇ¿ë‘n× N¨°h:dy‹ÎG CÓþû×þ?{×XUuþ¿·WvXaïöv! Ѝ¨GݳRW­£C­­[µ¶j­­mqU©QDd¨È2ÂÞ„MÈ~yyóÿûÎÍyy Ixïå%yIΛ{ßç|ç;w|¿ó-»‘”ÊÉôÎv-Ó¾tŸ›¾ˆ+b˜hxTóŠÕqàB˜Ô?A) FÎV.éf"^*6uq»=Tìt Ë ‹…žáìtøt(Ì£‘‹è¨¹µt¢"Crå*"ú]€p¯–“íŒt^#òø”ÀA:@‡„”5)\ŽùiËI ic$6ßÞ¶üu´y~flÕÐÈä¾f¡ùÙ~R$"†sÙüÌS⎈:wšK»›„¦‡Ãk_ÕÓBïlpÑáÂòk"êDÙÉœXó ¥;¹Ù,ïÐZ±y7ÒKij¢af¼†‚ …ÌÀ·9<2ƒ65ãçNÓÎp^)DÈãeÚãhØÁ퉺HÖЈI hi8 ƒ)1&¡µÑ"š)ÍL4\V×(ÄžAªŒ¡Swnnƒ×ϪaFM÷N¯—·ÙñX¿%ñEmÃQS.A4 ªeÅÅ:ä @,ذy 6Kasž=nS‹ÎIÇaž¥ó죶î}ð©±ÐkOh'ºFEûÉÈRà P´,U•gÇ:¨wºžfÂ\ì4@PŠ­´yÊû²¤Z¹Žò}mZ¼å?é£-.‘uÌËØÇé¡‘V@à±…Å´¦xµ-ÇÌ©mé~Zºn'Ó¿›*ÙµmW]ß88 µ ìÛÂÏûÑ ƒÉ ðä©¥‰´wü,sÑÚÓ|vø>4”=ßb@GÒiýê|ÅÅøäÀº):ÏЩ{&/¨é‡©8ðfõ}%ëü­ffÅ'ÕõK•4õËoÕšâ@½s€… žÍå™[hìH˜èFæ{ŽÀ¤9+ëu%¤+öPž×„p°QÚi•õŒCY³ÏÌÞ<×ÑHooÐêË…%=Ü\ÒÕHO/uÂWÈ Í’£_†µEHl€–«Žz¤hù!ø… ø¡²ŸKİÆ1„É·ãôÓ¯“Ñè†e˜bh˜¸\S :•¿³î!R †¬ÙÿО3í¾`S/Ö˜pB[1R+#Œ\GÊ4 hx-¾e¹/ÒzÕùŠŠñÏuSºåxm×D—»d%”½í°¤@óûUŸ¿mµí‘¾Gã¿uK¡4uË_U»â@\p€…ž9fÛzvæÜr)v¹i_±ÈütÊÔ†öÙ2É©OˆšîH@zC?3édíÕ4$³¶»écít] ] -Êûð'hiŠÏмÝn Ó3YVf{Exì^éú7üd²aªÖþ9²pP†ß/qÒoγ!8ÀfÆï™SH™-ô(üqŠp<ZŸÇlbUØüŒK©»ªõLiibÅݦSüÌq‘`Fö0Z0#¯çúe‘mÉßj­8 8д9°é=²3_Þr9’Ò|-M"–Ž>ŸwnæëY£³È,jÚ½¯¹wåDÍç©£ŠÍŽ&ƒŽÎ…ÿGM‰3 SXðZ˜Â°–Æã±A0wÓÑSù´îp ¹Úš0’NÜ¥Oáë‹,.øÊüz¡>.HÜb¶h¯¾4n ŽúöÏÕ.zs})¹‘ß'4fÜo`&& ûÉÈ:ø*„Ôþb§›xID$48˜ ÑÐØ÷†56‘F–“mV·NôåR²Ý*€ fBJuuW©ýÍ t4÷;@õ_q ¶Èz¬ßÆ>/mžŒÏë\˜Ÿ™ðìuúfŒY˜´t¬.Äè;¶íÆ{mÚR¼S©èSh\s©Ç1ÛßT ϳFÁXæ¸ìrûiIÖBVZ“06*0So$ =‡÷… NVZ,¨ªŽÐúx›•2˜ -¡m„îvÛêwRªç$ui“ô`s"Uê›Ûï¿P§3ˆ¤›Ü6¾F—]¿å?õMG<µ§M<†¢%n8À‘·ž¹ÐNS_å¶dú¶¹ÜÔßgódZ{o ýë²ØF3ƒâ0Â|ÞDÚšy}­¾'>va 7!šù Üëa×?oÍNrù ´)aTÔ!›+÷m~ 'ÿ¦V:»¶ÁG‡¨wû4áQ[³¡¦ÆÕÅÅÅÅúåÀöÇ2ßŇéÙjÀ¸»÷K›Ÿ”¿›ÛZšæ6⪿aq !…×ÂÙœC ÿI Ù߃ËQü~e¥“nŸ]DV“Žî¦>Ö.QOOžo§qkÚ&]_‘±]âǪ“…p2¯‡ŸÈ£c9´Çš‰Ä±ÓBeðÒûè{S*-=‡©µ;›ze$’ áx• QS]ÕÅÅÅÆËõÿ?|“þ+{Ã?ö|)k¢üÝœÖ Ð4§ÑnF}•B§¶Ž¼ã'\Žû‘ü1@kxiÏi-Ûüd½ç0À'qlU¶GDñâ™{Yæ!i䌭ná’uÜG#Û™Fž[Ó:”þšÎ ÷ƒ΃Á8\óîìãâÒP¥z$ùNSoçJ´©Oût‘¨}‘ô:•¬°z®©#ŠŠŠŠõÅŒ¡ýî…̰Dk/ ×|ôÿÛ¦®õÕ~¼´£M¼Œ„¢#f`0P¹pÀ!C ö¾rÀ§fÁ­IôáäDºº·‰9¹cy{øpû;N{i@ëòÈ\•iªê·4š 0‡–ªúz<ÜmvdçPÍ.W)|/bÁ“pÛolç%úòh`Ñ ²4¤c²¦`1[DâDjB‘lc뜢Wq@q@q@q Ip€ØÍÖ`>pˆ;šT·W÷ÙЩGÊMHšDOkîDEÉ©æsÕQÅFË+òBXüµ3…ê×ÊH÷±Ò–”ÐøiùôA™Z¨[{—ÔòGªsŠžv ó÷˜F«92FµÂwF˜›‰ìåˆ —–ž#á\ÞìÎa³Á…ß#騷uPJ’ƒl6«5œ•ƒ+Ä h6;æª+(((Ä”~Ñã$‘n2>LÂ>rÉ€ÂÂSoÅ´‘8¯¬\úŠsByŠ‘r€N¹Xÿׄ„‘Æ@øYã÷Á̬=rŸ´AžN𘑨ibŽÃܬ}’ž®ém$…&|×ÅLƒZi|W3 Z‰„ᦓZ:,¦ Ýá^{¶ó´`ššš€¦Ö‰fêR²|ùg»¼Ù7a z:„™Ù:J0úi`[¥¥$PB‡j8A©4Íæ–PUPPhØùx¿5{î {¾´¥üwð@ÓÜP€¦iŽk³ï•2 68\qz‚EèaÓ=šïH8 Z¼ßK»r¼´äödúËÅä£ñÒêÃ^šsc͸.‰¦‡ä^‘õýxÌKo]™@ÿ˜è õÇ|ÄíÃ-‚6èŠÛ¦%€êr0îõ‘ž×­¥,F°/úž"áK¤í4†óuä§ŒÒý4²`!exPKk€úµ˜IN ä¤DJLtÃarù°É™ÒÐ4†‘U4*((4ìx¬ÿ;¦{¼ÜëÕÍ‚¿›ðFü„`jÂLV]«_Á û·°7̃R&ä_ÑQkO67·‹ §Ÿ®ÿ´’­zä:ñ‹\'·Ï.$¢›#y$—·hÑϤ;Žô¢¢ÙÍZ²Ç°*;‰i3Á4.=Ù^&0C\¦eФžªÎ•õˆ\4zd¹‡–Áa5QßFÚvÂCýŠWÒ ðe¯µoÌB8WEG¼íãü2­Ý‡¨]é^2Üdƒ¿L{hâÒ`’’(%9‰’’))1€Ðl˜‰·‘Tô((((0ºZ¬ïq»FÁÖ| fr­äÕ}š¡ë¦´u6e)@Ó”G·÷M&‘‰$Œà3ò§dï)Ê7¶›;ù®Š~0ÌTUîx u8s}$…iJ…ö¨kFRY$-€1îG¬ ×ed_"„¶Z-Â'$Ñn£žé>:’M’û0µt¡c–ŽtÄܙРɱjZÔcó 󶓦¶1­·ºÊÒ¼ÇÉè÷Ë`'YHO>†ð‘ÝPê+‰2þq¹Ýà§Ðʤ'˜Én³A#£if’S’ ¡I‚É™¬@ùÏTÇsµ_q@q@q@q a90ï=J{½ºýòº×!À©&罋 sþªînXÊê¶uhê–¿ªöà€ÔFh`ÆHf̪³#wûT+òÈSçfZŸ8šüºØ:ßsŽgÔô²{”l&3ü|zf$ z€I?Xh¸®Ïd4 0ÃÚßìõúξB§”’R:Q=÷~a~UdH¡Së|ÚÔš õ)PE>˜E-Õ{’Ú¸P:£½íö!Ð’uˆ¼²®HôåRhº¬æ:«Þ_¢¥À‘µU Xìx ^Øg&¹L3ÿäð}¤Lͪa¦Ú}Í¡¬½‡)'¿ˆò ävkæ§‘MuœQ­x ù}–‚û2=ÙA™ÝÚSÇŒô3OT{š-vü²÷ŽÞ/myž³ï2jîêó×- ·ýªßÇM•) Ð4Õ‘mæýÒÃÌž?ü¬‘`S!´’œ´'¯€ú8×S–cx̸„dV4î½èœë™ÖômÄ ‘éeº™~îG, ®Ëd2 @Ãî^„p Ñ& ü,´”\7åÁ’Îì=M ¥yÔÙµ|:#CcSˆÅiH@2N+yt&òê!À?~±˜ý¥Äš6åJâëp€µVÜ<(Ah†ÎäÁu§­cѵ up[­`BÆ‘ájk“z=ö¢ÿÌÐÁAäl&½ðàc6•k¬˜/Ь¡IL`¿M3Ã`†µ[±Ô˜U \ýhà ‚Åk¶Ñ7«7S~Q)LFdÔ—ÞÈÏfž‹Ž qô{“Éë·Ñçßn $ø^4¢ÖG<ã±hCÕ¡8 8и9°ýñ~ïõúëæñ°¹…{ÃùõúÇöïv<Ô»I†7U€¦q߯Šúj8 MÎðÌ»!w9ìn«$ÌÁŠˆœG(³x mƒ¶ ÖššjH:c7kfÌ´@èäΩfj×"IÐÉô2Ý 2b)@s] hü~³ÐBpä3tXsƒýæ"•˜]d)(1{¨%Ûç¡"·ŽJü^˜j¹a®—S“Ò#ØG†8f=Ì»Œ>ø¥ø…o ¦ˆ €•ÒaÔ׿h%åÛP¶¥+å[F¥ý‘1ˆaà”î9MÐA„ç.¹cÚÙ¼zS3ô[tð§¢áßFƒ±ÌüN¬á{„µ1;¦9hfÆ×ÆÎüOÒ®ÖM‡Y{²és—Aã½³›Z¦oÄzî›Ú…‹¯‰C~¿•JJzQ‘s }¶¸”–¬Î¢[.?OhmøžWEq@q ™s Ít¿.×}>¿]0Ë7Á‘Ë›"W iŠ£ªú$Xab…xL…‰J–BXoïõ@3QBä:BCŠ i—m@D>5±`/ṵ̂éûptH6P÷6ðÓ€-i-’Vl}5¤PÏÚYØ„ŠÍÐ$ð+v:! ¹„9›ÈxÀ+G™Yšßïò’ê(¯_G>¼!}.˜„…µœÔ‚ (3Ê™In›óà°y[W³—Ný:ß1J/>í‰ráÓT·}‚ðyñÁЀfE§/œÐ.JÉ„<=f+sùz J€f+ æezNšŠöízøÁØ”æ0¤Ypá>JpÈ Dô™53¬½/ÌЈ±©™Íª^ ÈX5-:Â×*áPÞ1j]­Ê¢™ß¬Á=s‚Ú´š-èªN‹ù>KÇF±¸\(7o½>ÝE׌M_ÂcÎqU¡â@ãâÀŽ»{ö~eÓ¿n &ñ• \ÖëåÍwq4´ÆÕ“³SÏ€Œg%XB·ƒ;ÕFµ5ÕæíÐßÕ^ÔT°Ê‚«‚+;À³)ûŒ¸=á7Òµ¹ÀEGJ iPѨ[‹èg9ðñBÀ®‹Âyf8<2G3ãFþ‘ˆ³=43lâ䀉ÓÉô2ÝL¬i©e`P£ñH ˜ÀfnŬÅrÚÈ@ãðc~ñâ)ミ5~V¶°M®X$¯¸>©1 ’›h1à… 6sã1°˜ÝHêé¡|W€Š|^˜·…¦ê¨¬*¼5Þf(ÑÙÆŽ7Y2rÙÊÌ eà- ô0mØHx ÍÅÚ^3ï5&M+ë1¯sê¬ÆÀ~¾VäÓEk¡ÙË¢ôôY0kÔ|eê›~Q­[O¥œœk®ð¼ü4~DfpB¡¾éidí)y£‘ X3$7T†‹H¦Ûþè€o{½´ù5\ô0ó ¯­¿eþ=kQÖÙ›ã ÐÈ ÛuèoûåCMËÅ€4ð…È€h—Д__}/Ä—í(àÌ€?0ÿí¿<³mGô0Ô­±nG˜Á¹žgáYóÁ‚9 å,Xsáã‰0±:ðÌzŒT'œÖqrD¬R½UøÄ‚&vŒ·@³À‰=™ó¬ÉhiCxà³ðÓÎçÈu’Ä t2½,PKð BëàzYHÇцô5b ÅæV f\Ðf¹\.ò@KÃD‚$Ì??´4’ÒtMH`úÙŒMZ{>Ÿu¹ËêFýh‡MêôÁ´Í‡zÝ>ðÚöIâ›ÐI€'–°¸ b1£¬â¾¤ù˜¦…èkX„–(’>WE¦‹kÂôðšù ÷K…òMm+„r€ï{63›ùfZ¶ø$ôpƒl3˜tœºf/YjõïÑA<' ˜W%oT`‡úÑX8­L—¦Oyâ´?w"^[=ñQMòzüo Ï—6–~‡Cg<~±0ˆ1Œsµ½Ó°~À–ýALmð$Ú­þ”$»Îj‚¯*sÀåñøó œB§Kð³SžþÓ1¯Ç÷ªátào¼ñ „Üq½á)`³6§˜Á V¢°À*Z˜[Y,.jãñRQ©Š<:rÔÉpÂ,oÍÚv 6Ù§$@É%Úà|oa38-*¯˜h¥6­ÅÙµ¸Xf 84™…oþ¸³i‘B°æ3RJvíée&U¬à*TGÚ5À¹HšÍ°X[Àm2p>303cÍŒ09 jgb  :º%@àµÔZ˜}ìWàâC.®2º‚˜³„‚Yk@¸¿RÓ!L¼¸ðµ,²f†ÁŒÝ_ökr—B¤¦cE¬A ˆœ0¡¼Ö/‰¦eÑ€G²ÓÌÄ,R;Í kj˜Ï t è#ß •ëá! Ý_¯Ô~ÅP0˜çhfyˆdÆ>3 efJSè6Ó“–2‡Ž¸›¯ÝJÎé/î}¾×›qQòF3ü&Ôõ¨eºí XÑóå-ÿ :zuäk»¾^õ‹ZB¶FΤ†4r–„Û7ßúèoaµ9žmÛ*%pÃ%#©GÇ6š$ØÈ™OäÒÈ~]yÑí:xŒ>ž¿ªõá¹_Þóäÿýú­çÿð*hePS>%OÄ×’þˆ3à­ö›Á |(Àé3"…j6Ò´µg‡lO¦CF]có2ö™a‡t^ØÔŒ×·Ð*äktÂ!¿Ì¤ŒAˆàŒf4s3 f$èà:¸0íAÐ(ã:9ȃŽ*ÆÜðoiÒöîÎ"Jú—#‘%´~YÚ‘&cÌ36cþi¾0ö¼fe?™&.’NÙg±SýQˆ€|Oò3±xm&&v<ˆàêú;•ébú¾Û`¢±C{‰g³™Þ÷üð³¨äú»ýTKuÌheºT“å©<·k2Þc¼äº]©¿¨crë¥zMÊ«—¦‚È— {^[n~øWÌüß°>]·M:_o6)…LSu´ÀH¿¹ã ô9Ëk·í{éî'žÑ½ýÂ3CsMÔðG\‚±ß,³ðË‚5 Õ"`̬BMª¤0bùöˆ”É÷A¥‡Î8 ÚÜ.ƒÍÏšìãý|œédªh€D›ÅÕ@Q€ #Ì ôÊySú¤°$ù­­5S1î×!5@¬A‘@¦HúȘ`E;~jÓÆ3’’7Ò†ÔÒ˜2Ÿ dœÉkdj­8P[ð3°ÿðI*,F` „fŽç’`ßH'sºÓ>ÐÛ£Sž-0F<ÓcÚ”¼c†ªêâ‘Èt¬©Ù/Ñ Íé/@ô~iÓ{Û°.þzE hXZb0c½úÎ)í I`0sÏ56œÏšÄÙ ÏgƒšoüÉmï½ôü×èܲ›^aáZÌìcÍB.o‹Y}€ ¨Y¸fÓ*©‘€ø.j)¸GÑ €H,\kÂ7|U„:¯5-Ó!…ïhÚ©«k˜O¡Eò¢òZžÃýåRy-ëáC$5@&aÚf’#§Ií9¯$šô4»¸L¶äeÙøI-K˜òIþ2å1I‡l_­j˾Y;“µ7÷y“;j[e^Ïô1Ûö¡Nm[ˆwP3ùJÞ¨Ó;LU/ˆD¦c¿D=» ï³ øàâˬ{ ý8/^ú-õ høåÂmZ:vì™Öªmû¿µk*43Ñv@]W;@+¦;š“ç?zR÷æÄ‰7÷ž7ïdlÚ>5,è²Ð«9Â#Û=̺j`F À³°\¤@-~DøG Ôš¡ùí°ÀÍŽèܶØnDZÙ¹Žèh¼gpÇÚÖXñZÓü0ÏMˆþ¦Ã¯H4¡m𵡋Ìx,å~Þ§Šâ@]q@4>ÊÉ+Âs\‚÷ÈÙ“fÞ÷“(9¡HBˆrDRÜ•½ˆ®ùîûèÂ;§&vÆûÃJ's·Ÿµ›LŸA卵ü"aÖ©…$o6y•”¼qÖ;DÐÔ8®LgÔëôúüY˜º5ã½vn¯—7]·ã±33?êóëÏÓ·lOÆYýl£.»l D—Öð™1(3³†»…˜÷?0ÒÀÁZevy¨lŒš´¶Œ…_ÒìŒM¿ØôŒC'&ÀA¿ÌQŸõc±p}\/×Ïíp{lvÆí3LOs+r ¨„ÆJ˜áÁ Í€Ð˼H¿¢Ðµ4Ñcs5^XÓÅ‹‡ Ì4·»¨þûË ›'>òŠœ¤7ä‡MÀ¦ÝŸÐ{ó®¤5ÛÞ¢Á=o¦^01e9oà#¸~bØW3ùÅ%B -5¡a_ÜxOTòFã;Ey-8®L—õhænLþS6…yÄ?gNÏ*Ϻ-4¢u}jh‚³%à#9=í®~ÝÛT€†¿[Øþ’Ç"kÏa4¯`aõD“4= åvåY}ž} ÕÈ„n‡^Év(X‘íEr}s9WòF‡(iœ ˆž*ŠñÆÖ&z9ü¸Û‹€È+f).Í¡£§6Š¥{‡‹©_·kiÛ/‘ûÈJã†þŽzud€¢Ã¾9´dÝsл©ÎÐí§ôã®÷iüðÿ£ÝÙßÀß/—2;_‰|Nňz-\ý´Ø_z‡`/3«í܇fP”¼Ñ Yu±j„+Ó™þ?¹º; ë¤AÜÕwÐÏ2àËU×ÿ{ëKC:[b½àÒ+‡ãåÝ¡™›ßÔtœÞ<¸©[_÷³‡F€D–&›ÝذPͳürÑLÂdŽ’èÖ².^‡‚›8½ œ¬–µÂ¢Šâ@¼q€'8xa-¯£)½™Z$÷"··X\>zЯ©o—«hUÖ°ü›úwL zL3ìÔ¥í4aÔ ´ýàW´uÿlÚux•”æÑŽC_Ó—Ë¡#§6„E’*}e?º°qž¤äÆ9nŠêr ™nóýrdþO6‹d ¿ëÿ¯M©òwc[×—†F¾`8€­e»öc Ü8ÏLccXS¥cÁwÀž˜x1ú¸‹Kt_í¦Ê$Õ¯:çÀ_/vÔyªÅh9 MÎ"4ÝÛ]D-’zP‡VÃ…ÿ˪¬©‚„þÐÔlÝÿ­Þö¦ø–ÜZ™ëiéúÄoÖà,]÷Îù ¨ˆ À¿¦þóûϤ=:Òµãá„âAøßO毦­{Óé‚bJKN ‘ýºÒUc†Ðg‹×Ñü›ÅyŒœ;e¤Óõ—Œ Œ)b_<üá1Ñët  EúÐð5ù¯_<ð^Ñ 8 8Ð88K@°çȸŽý Ðàí]Ù kdÄ©üÝøn &³‰_Õ  Æ ÄYÍB3Ãlˆ{yãlc‹ã,Ó ïÛ™®;´Bus¾Û°ì£)“ÇUØ_W?¤ìÄõsxýD»úvmK·^~žø]U»Y{ÐÛ³¿¥W¿‰8Irå²pÕº`p/êÞ¡uåCêw%„+Ó=£Óù{¿´åˆvû!WQ¦¥ù›ð±©Tg<ÿ¬¯Yxn‡…d^p‡âÌÈÁ9o`zúgWÑÍ—ž‹(0DϽ=‡ö9)Ÿu͹·>û–6ïÎPIÏ=x-Ý-É!4A 4B‹ >.U• hU¸œÊ+¢Ž6‹È¥TP¤™Ìñþ!hsÞ²UG¨[û60= h0À޵Hc†ö¦ŸŒ«é¯kÆå¬ÏG6ëõÒæwfî| ~޵4!7 31t 9›l[ºÿÈ): ÍoàìÆš‡n6qƒzu€Z3‰¦ÍY†(f£„4Ïj4®ð‚‚„Ýÿ¦p¢´1—}‰…­9ûIÉÂÛ¡¿åþxY×Eÿ#í[!E¹pFóú,¡ãïãTŸ|Qm)4B„Êå/àFؑڜfßáSÁ*:´N n‡³Ñ½ck! =™G0—ÏÚ›¼Ì‹÷3Ë:Ãúv¥É ¥g¦Î¦+³hÂ9U›±ß _XÆÚ¸ëB7kæaÁ «Ù`°Ä›8ÁÕ&k0ÑÌé1dÉ‚_s±«”ÆÂៃ8B’µFp¦UY{D˜hî çß1› Ô2ÄÜMÖӴבËtú€áß>ò @MÍU™¯d¥e=š©9ŠÇ1³ª†æqLp]‘¶|ã.Z¹y7¥''Rg¨`ï¾ztD7> Aß4>øj½1s)<}âA¼ôÜ ÐÔ‡ã¯^)À3èôû"ß;(²Z<¿ØI¥ôOsÔ¥ìSj1)ÙÁþUñ"g¿³_ -8×yÿ£`\bžd^ù_Õ3vQTyöKÊÆ‰Wf³‰R„/œƒ2ñÑíÔ¶EƒÓÙ; ÎPPP¨È•›÷@¦ÙÜùÌÏÜgcDfÚŽœ0Ͼ9ÑÎltáÐ^ôõZ.½¹ßýH< |ýÅ#„œs5rí}ºh­ð½i“®iUBÛ`ßa.åuÜð>tш¾¡‡kÜæ<~œßoÍÖ½ÐÈt¦ÛÏçèi®R/½úÁ|˜ø»Ê>2W^8XäüßÜèÕµOìBðókÇE$×jfÛ~Õw ´4k # c-˜`¼ ,øg¼³AŒÐsL®qœ:BýÏSwœqγ÷U¼ŽÕ©ýt¼x°83Û{ÊÂjO¥ú”ÜhØ5 ò bØ´°‘[–®ßNßnØEEˆêÂzD¯ÎJ.½•|"Oíi5P Yý¹d ¸hî²M°U¶Ñ8|Æ íC‹1njßRx5ÔwÿãJž¥¡‹ÜãíµåѺ^ȉ±:-ÆŠC”rÔžqÃúà#Ü[€PMN]Ó¢êWPPˆ†ÕÉ4?¿vl°:Ž(Æ‹,l¢*ç´€¯Ê£ðæ‰=ž”ãÂŒ¸\…„¼È2ïH^*—êd'y^exå}çêIçèÍŒ/HŸÇšš¿¾7n½â\$í*ä®oñ-Ÿ½dˆj–€ÀN÷]w‘øÎ³<æ°Y19ÅWªtô_L芬ñ˜×½×(@ãšØI,ô„‚™&Ö½FÝ dGžƒ%p:½‰rÙyµ>Æ)–}QuÅ:·9ŽälDÍ¢ø!JQ¢8P$˜©á”:?¤×#ÅE¨’±UïçÈi¹Í!7!¯MûVi€ ç²ap£Jd°é>rú‹^™Š€fH¯W7ØñËþZ¸ºÈªª·³ë^‚ª·®¨†ªçk%Ø/ÃD\.—‹!¹×Û_ü@ÇKMôcÂy´É1ŠŽ›ÛטaÊ(qÜ·y¬ÄHo|ö-\¹YÐÅô1uQâ¡ÿuѯºª³ª±ú÷§Kháª-â>ª«qª«þ¨zž½™®ó&%ÚËMfꋪœü]H08 9CÔW“ªÅ:倿Áì°Á ^ûhò̬£ˆ†vßuãê´ÝæRùtÉ#]à³`½ë‚Ûqº¡LÎât`Y±ã Ÿ¬™`0SêvÓâ5[iþÊmtÊÔŽ¶Ù“_WþeÝÊ7¶ u £©s´5…«ÎEpl4!âŒô¯‰â±ÿ±ê[}Ô:VŸ-^/üªÆÌŒù8ÕG_T ÓÑF×_4 NÉ tãøÈmí¿gKmÒûÓøáÏP«”¾˜eÞG›vBëv¼'ˆœ0òÌ>…ï]7Dܤ{hβ_à¼ýwÂç÷Ò†ïÓöÃïS—öƒhHŸk©o—+‘– %âºÔŠñÂ.n€x¡¹1ÑaÐëfø|‘\3@ºk@ûÓñL¿ÒÐÄóè(Úb f\H„ºqÇš¿j»3YŽa fd§H1 ¬æ|¿‰6í:(@Ó˯ýe뺮бš½tmF¤žXS]÷AÕ_{DȃÅÖ}Ÿ –nx¾\öˆØ.vqèÛ¹ôþ‚É´óÐ|ºhøÈjÖœªÖtº`ÐãTTršÁßQ‚­%üë¨:!\ð‡}Žæüˆwàïè3†ÐgßM¡]‡à^®:÷HT©‹šìŽôùxe8Eg¾½_ÞÖ3ž;¦M<Ž¢­Ö`¡Ó gBÖÌäÑçßo&§>Qhfj]yŒ*`-Ó4cÑz*FxL¦7VÂrcèŒØX/ÕðXc¬>Dԗ˳qªâU#µâ@h@Λá”']>[Å Žçf‰ÓŽæl¢C'V‰íÂâc´fÛ;Âlwö"Ò댈²y^°º"ç1øo=G[ö}Fë¶ÿ—:µ>'x,Ü )¢ÔDDìks.´ŠšQÀ 05f.½‡^ût(-\ó{:zjc¸Õªóš8ÖMië„ff¾ì¦?àa-MÜhâvhaµå€æ7â'7ÀLI‰‹–!’YqI)í´õoPÍLå~ñì?ÓTè,¡¥p>gz9[mý4Kÿ+ó#ž˱*(rÒâµ[c2NñÜ_E[Õp ¬ßwfxڪϮ~o÷öÑ­gÓ}?YAlbÆÅ /Žy¾/²œÈ݆`3é_uVuy^åµÏ—@“Ó¶ÿ£»¯X€¨}OSF -Z•<×UšÀô.½7oM=šVly¡ë³åaµV¨–O½>“6éðŸ‘%5© 2·"Éá¹ë¬k¦étXL"Ÿ’Õ’Lƒ{ÜL·]:›¦\ýÛïAJNèP¡öÑùvËÂÇç£7Ðæ=3TT¶ R?B9àC2ä:ŠeÚŒÚn ˜m¹0V•vðÆNÝSû™œ:êK£4Ÿ-^G?î]d¨ÝR=ÿA4¢¾Z^aŸúѼ8ÔN <³ ™„÷!Š ±è›"›Ý¬O®1mNh8j‹t×FKÓû_Ÿ¼®m[\ã§Ž­G’Îø(Àam Çÿcb{dßûΨ+ÉÞ–vÿ)µHéIºÝ@­uœqb5;˜>¦³mZRláÀ˜4KMì,|rîûÉtÓ%ÓQÿõd1%V¨éÀñ4wùc\CàaÚwäÛˆÚ¯P™úÑd9p"·€Þœµ‘ÆÖR~¡ærÁé>™¿ŠöâùŸO‹weÞ™_,]OSg.¦­H›ðÞœeM–'M¥c›ï UDÃA ¡¨Èua¼ö­Qf*ÇŸ‹xEHЍŠâ@4žÃ4{„¹Ù¼T9i&癉×"hÛ÷t×&Œsc켎KUtɱڶïMÝ…Û®ªmµ¯a8 ÁûŸd¤'’ͬ61%0ëZÐÉlúõðu„?ËʬÓÈÌûèáë7hèÏðaÉ>¹††ö¾“î™´áž[ÓJ˜‚ER˜>äî¥ôd»øV2í²¡õtl=Š.;÷%úÅuëiÒù¯Q×¶cp^yôG¯ÏEYðãùä›[éŸ3‡ ¿ž“¹ÛC«PÛ͘+7íAä¼ÎˆÄWD¯ÏøFpÂåöÐwëwPÓ£c€ò›%ð;MÃs6€ÍFäQ¥p@X¤ÒïÜŽ³ ÍC0ÎˆŠ„œL„í;|2>CvØÛ'Æ¥^Óæü@ÛöÅDG{t kÆ Ã ™I5,”Ùåj±—œB0‚YH@ü)/¼ÕÖ©bi™Ò›úw¿Ž2;]E{+Ù´Z73°ì2€¦sÛôÔ??É.™>¼2n¨…\¥hkNÐïî¹’Ú¶L…‚—f,\ÓÌ8Õ8»«#ýb$¿( ±¨¯½hôž‰¾nü0Z±i·PiVf´žŽé4eòº€gMÖ>Z¿}¿8ÍzXíù#ÊO'Œ¢´$½3û;Z¾q7êGJG…î»õ»hóîèg4M=X¨WOBÕÊk?üz¢Ó¤ÓÃ7^BztÄŒ˜&³ÀóÉÂU4zH/„äìKË7í¢ùÊ%Éa£Á8¿ºm" ìÕQ´å„y—Ï—®£ÕY{À¹ö¢aÔ½ƒ¦Y¨Žq‘úä ƒY¥05+ÆâÒ‡oïþØübúù—Å´ã´~!lÐM˜ÚŒ¢˜ :úÇÄjíïQsÁ!¸jy¦›éç~DSjÛÿhÚ<Û5 ÅÓ³Ñíq¾ŸòaVQ›qжmu]ýs@j6FÐ5Z΄ˆa¦° òxqÏÌÈâó•â9¯9lr¤`†éaº,ÆõÌH&³Ù,hfÚe?dûÕ­¶4²ï½t Ü=i!èó3LòUÔpŸÌÛŽà¤ÎAÓßF[÷Q¡oÕÕ­ö7-ð1[ÙZÊ1¼¯S›t^Q1dþ.%Úµïpr‚MìWâŸæ@‹e˜W/)X°öïÿ¯M©ñHuxRVY°ªJJ9 ^·ö­„ʳ]«TÊÚs¸ÂyW^8À¢#ÊöÆ¡]Ò§•IlzìT¾8ËîC+í©5Ì zvlMmZ$š>á4.$F?²/µJ+·E†™‹ó÷¤+F¢N=;¡-â’’d§‹Fô¡–©‰4 {1›±}ß1qlÕ–=˜ñèB—AÓ3¢A<\ª£ATx ¥É;msÔ0„D ·ìÏ÷ÑŠl=÷½“ì3wÓ^Ø]S ôîÕ‰´öÞš{S]Ö½ál.M™¸/‘”Xô?’öÂ=·¡x.}‘žç#î-oÐä,ÒqŠ´=u~Ãs€µüš©³4—œ¥9Qušéaºú´I"›ÕB‹YÐÌ´s"--Sz‰èhN^-„öí|øP>AľA{/¥/¾! C>ì ûì üTiúà¼\¬qY cžn‹“Ê%=9²N­Ý¶_enV™Cñû{Ó¯ÚƒºM‚B8幋 ƒã‘ÚFÿ¶‘BÄ —Œ¤?¾õ9ý°qÂ^–¿°ó^õÃy+iOöqJI´Sn“¶ò°˜<(l"Æ%¡l8|†þæ•U§;C='híÖýâ8ÿ9žS ¶§LKÌ[NϾ9æa]éæËÎAVh½-˳1·k™ÇÊSâ6S›÷ÃF:~º €¼>/„YŸPÙv,£CœŒ?g£Až§ÖȱPfrÆ €ù „[úµÔìÈ‹=2þO½" JôûÅN:¯£‘^žà ~Ê:á¥t›Ž~1ÂF'~úךÚ•ë§¹»Ü4¬­‘^YQB‡ e šIá{šé–¦L5Ÿ]õÑXõ¿êÚk··!xZ;Š«¿ZŽó[•¦ÏirÆ€Æj±@+ŸF§ò‹iëáL¢S7Pzú,ä’©YÛ.—¾^ù›pO žÇš3ÅÎLêÔX@IDATœj¦v-’Èj³ Z™fÖÐÔdr¬¨Ú ð¯a× Sî9´eïL:xœ'µw¬ÛòÏ2êÔ‘:=ý*½_˜¯ÿ,{Ÿþƒßòn¨¶Zu Qs LÈ^xçKÈ(>ºöâÕöeòECiÖâµ4çÛ©WçŠÚ¾j/Râ‚K80ÀP&F§÷ÁªÜ¯†wÆAiô€FòAÃ…CûÐløÒtmWnËûͪ­tèXýéþk…ßÌKÓæÉK‚k“IZåœ3+l§yvkÌÐÞÂ&´òq¶}ò®+vöÒû_­ Ôevøãˆ±§ãe¾æÀK[ÐÊò5ûÏôé’A¿ºý2h‚\ôë¿"ªe_3hbs¶Ðr6BÏmÎÛ,dj @ 4x~€ƒHáÌC&IV €OMü_–î÷ÒÐ #µOÒÓ³ iÕa/}µÛM㻚é'½ÍÐ0ÏPØ\ò~!1â²í”W¬7£Ž½¹Va¶¯×"g1ÓúÂf"áy ßcÑö?ܶ"9¯!y +Æ'ºqЍur\p€ŸC&“‰l ‡úwn%4ª;gBÈoAi)_†íSËN±ÏLÌÌ<ÐÌtH6Pwhg@ÓÈ´2ÍÒä,회vXÜ –BçQ„wþTÈÉ߬Þh¢V©-SR[ø¦ô¦[ þM³ã“ǧj³zÁ3ÕFcåÀŸº^> –.yG~ªXóŸ§î¨Ð-¶¦س“˜pÜqà(íØ¼Âqõ#~9€gw=Ë\üš¸+gJîqGbøM=P¨27•ù¨ð•¹Å””`³Rì/ÚšhË`˜}­ÊÚƒ‡ð¨Ð–>‘+@;ð¯ÙºZ—ðŸa•+«øeÙ¸ë ]Ⱦ:¬éÝ¥-ìî½T€Ü(¬5b Ђ•›åébÝç¬ßv€8Š·#WUGC…‹Õ `pÀ³çòA —5¬ä[ “³gÒµ3 ȃÒf{Nùlü¶S>j›Pþ­;ê ‚™pÛ:ã<¼3˜n LÎ8Æym´ý£‰ˆOiPžFLmxðë]ò:¼+ÔY™ Ñc ˜r%8äHpÀ)¾% h—€„VtìÄÝtüämT\Ìߣr³¬ºè7×Ïíp{Ü.ùZP÷T=uk•D‰‰0wmL#ÓÊ4sTPîC¬K¢=ƒÎíÿýìÊÅÈqóŒ1Çb"É¢Íæ”5†f1uø§›ŠŽ<÷†nÖóS WOŸžYþ¡Œ5Qª¾zåßWáÜZ|߇ª4.õz-t3ÈÆH+“³º>;^ÚW³Á¦.Ö›Þýb=ü×÷á¿Ò‚ÆÈDÄ–ÂàñH68ÊØÿæþ@¯~¸@16d‹þùµãà(i¥¯:ú¿Ÿ‡|¡!ƒ ²°Æèµ Ú–Gøúp¤5l‹/9§?Í_±…¾Z¶ Ú¥ž"Bˆ¼æz˜Ð½ýÙ·ôw´ÅeTÿn˜Ùè("UEφ¨R‘RÐäu¤åï«JhëÉŠ•ƒˆxÆ¥K²ž~t•o{°ÜÄÄUái«Úù±’kÓÿè(¯ùª†äiÍ”Õîh4÷WíZTW7$Ød‹}C,p´g퇆góÐ.˜8I±šhïÉ:VÔ•N"˜N€ š“ Æ|¬£”X¹¿þ€™|ÞdÌ”Úñ-‚_ÎG­mjŸb¥D€˜d¥IJ 42­LsíÌÍ*SQõï¶-ÐÜHY;‡î*É{ù“^S÷š°õÆÚ„ùšŸ`ªé'»s·æ>÷†þ#]À0íÉ)¶[S¥™p EJ’ð7n&ÝmôÝl™jßtäTŸ€"UÏÌéYæ¬ë3c÷R‹‡µÉ‡¬\8š/²pt°?=0Y€ ‰Ccg{^da­H¨Š”C †þfàrßu 3žDÍr@/g$þ0åjRYs59û ¾]Û"¤ô»É¡xË£á\~þ@š€°ÏrÖOÒÁkv ûõ—ã-c<ƒ'.5Ñ NPêŒÛr|t²ØOw±ÐKËý4º3ì) ùv9 ©Üø<˜¼á >~49%*,Õ~W>¯¹þVWñ´Io]tŽ…æ#…%¥:NäÇ>”õY$°`À ¶ñ¾çm4v›M€NêËy°8túüTÝ(rÞ5­ýænçyNC®yLy̯ÙF‹×fQAQ)ÆŠó—•A!tTòˆ­àG@/ü²>ÿv¢¸Z »Ÿö÷@L‰¢’úê¤5Ø%ÆjézJcÕŸÆ ï#&#Œ°(†Û¡`ŒeDYM]¦M¬åÊï|þ=ýõ—7Ðr$ð\ºn;rÏLŠ uªâ€â€â€â@-9—~9ks÷ƒrÖdíM ‘£µ¬>òËÌ0Ð`ß0àG#€ LÌd’\Æ02km S§µÇáo Ú‚ö¹]í·d˜ž†(‹´Q›×¬`Ó2ÉKpÌÎFÓ¯n+:sþÆËŸß0gúÉ{øu#~·“×¢²~¨ò%¸éü! ÂGyZ j={Ê”#±A ²¡0Ö<–<®Y{#ÂêÐÆ”BS¶›Z¦oÄzÆŠ±Zì ‡ì.)é…té³Å¥´duÝrùyBkSŸcßPý=Gë®ÆÐ±š…±Z¼f Ýzùùb¬ê®ÕØ×ðSÐŒ1?Ê3ÆÇ¾©¨jT€&¶ñƒË³l\äCÁåêTÅÅÅÅØp€_ÄBX>uôÈG[tºwù‡Þró‹èœ=hdnháÂ6´Ó®¦H„Ù1#âyÿ‘SôþÜå0µLK¢ÞùÀ&®rÉn©µâ@s–9dFú(3f³ë£!ÕF}r€\”ݧOŸ(Üñ㺿9™GÓæ,c¡9® ƒM‹¢™‚ÍĤ¹X„ëкBÁMCwšyÏc°þ‡¥ÓœÎÂ"ÐÃæ_¼HP#44/¿c뀰Ìs£¥÷ëO=q¯ïÝ'§ÆõÖNzîI<àÛBëËÛ¥¶ üìÊ›ýowéúng-Á÷6h-zjÄÛR˜g™M»Ò$Õf0Ó²Å' fdH1 LËì%ëÅ$ªÔÊsb±Ž×þÇ¢oõUGèXÍüf-̳£ÖÚÖÍÜŽ1à Ñ 0½4’ñ´Þº÷õERÍ ÷¤+.DiˆHÁÅU꡽‡OÐÕc‡ÒÐ>i 40¡Å³‚kÇ !½;QhhvAûÂåÇi ôØIõB\c–†KÏNmèüAZ©ˆ|±}¿v¾8¨þÔXÂá|'‘î ‘<³¹•niÚö@*Ó¾bß>ÔJk~–B}ZiËý©t]ßò¨RC3Œe×)³•¶Ý·eõ9–þr±ƒ6þ<…F´«hõºðÖdÚt_ uJ.ŸkaðS=r\¬kUž„I=½1)&v¯H+Ÿÿø9VJ±éè–Y…ôñ–RzfŒÚ%j¯¼A­4óºDjaWº›êxçûùg!™í{Xp.þêÃ÷>:´g绬/¾;ÇÏþªÔ-˜Çþï?ó|OÖ¦Ù‹?›ñ5ÖÐðšÇ†ÇˆÇJÌ*éô:?@Gp†é…·Lƒ÷Ò#¾#Ýq½øv‹Ä%KÆ”,ygå‰{=kžšâxæÔ”DzõwÂ…f!. "ÀK»- Ï¾½—þ1c(-Xõ9ÉØ+ü"ÍÌJŠ{éÚmTàtSjÊ— ª™©L=Ïþ§¥Ì¡|'X¼v«2¼L6©|n¤¿Cÿ#íSCžÏc•бâ@lÙÃüçRdpÇ5 Ðàîé íÌŽG-¤”NåÒÁc§Å=•ž’H ZVgí`…5/¡/å*Ë€hÝÖ}"î|^“J‹€Ën¼üó 0aËÅ‹4Z¡Œ*¯W;£ã!×õÒóËJèÒ D¢Ë9;Ü”ïŠüÒÆHÏ\h§Î)Zt[2ýÛ\nêo¡y7'ÓZÌøÿë²ØFÓB°PÏçM€`=ózÜ3÷¤Ð³cídâ°ÌÛã¦ñ]ÊAÖ¸|½[»—__]B%Ðz=4ÜF?íg¡¶8þâát®·ÉÝÿ½¶„&$UæÑÇ›Ý4º“),íXæ Š@Ò_±ìT±\ÜÕBÓ6ºÅ=ñÒò:í Ð¥Ý5Õ³… ¬”¸-U%$ áä袯?þß'?.ÿîÅCGN¼ü¿¯Ùñ6°jË^âÜ(ªÄ†ÌKæ)ó–yœ}ôdáêÅ ÞX<[€‡B,b<°æ±á1 š2 4›jÏ€ÓÍL ¾#±mÇ]È33ÍëËÙˆœ3Û^xÃp3® –ÞÐ?ÁQÏ䎿Nkí˜:uhùKªìÀºu¹¾üÑ·à¹Gýçý 'NŒ9ª§®ò2±v¹óiýÎÿÑ´¯¯¢©³GÓ›þ ™ŒW81ä›ZqÎ+T‚ÉÈ[öˆhfìo…iâHkßmØ.è!ÃAmJcêmúYß×ʱâPß±§º¤ßj´Í.â$rÁª.‰CÝÏ‚Ô1= Rý€îíi;B2ÿî_ŸÀ`Ö6U39cb&_4”f-^G_~¿Fõë}canöùÒuô›×¦“/À'î¼B\צE ½óÅ÷tøÄiº¦m ˜T©ø½ f>ð¯w{èd1ž1Ìrø=ŸµHqEv‘ŸÖ¢Þþ0Ÿzâ›bÊuiƒ£ØÿÊJ') ÐcÐÞÜ?ÌNS¾äï7bŠB¨ò|;ýÂ~k˜;ý|˜–ôЂ=ƒ£TZš{ä ×Â'ïõ½Íš–ÝúŸÃmïŸßxö/~½qÞ€Y°RùÈy_>÷¦~ Ñãq¾ûÜTô§¦ø>ı[ ãõ!X‰«¯¶¤$v¢ó>*–C'VÓ–=3iûÁ¹(Z ¾ðЉUbY°úiêÙþ€›k©kÛÑ8¢×´3¹ ¡ÚYNšÉyfâµ0mL#çÏëÔ¶E0¤8¿¯"-AíL#ê¤}lÈóåXeíÍcUWÑéjÛG³Ñlðøø±ÆÓ(@S[†Öåõœa¹ªÂÏ¿%U¯n_¨ÏLè9 Ì„r#¼í°@ ªbùâ. Ñ#ÁH¹¹~:~LO§ëÉâ\0®L;Ÿ³¿"ëûs .¶¥}OdÙqÚKZ'4äî×L£Õ\Ñÿ*ÒPUç[’"íÿ<˜—] mÊ;JiœíßZÏV$å…5P49%zþ4O/9{Þ‡‰Ý-‚g·ÒhGÚ@“•àɑȊQ—,Ç‹ôƺh¹¬ô9Ì#-ù¥Z]¶Éx»QG{Ë4m‘ÖWÕù–@)Ù­å~‘Uñ½ªëÔ¾˜r TK#%µP SºýÇu±lG«Bƒƒ5ƒ9£$¯‰)QM°2ùp2¿yáƒÖÀ°„#MÌ0˜©R;ƒý„ÄžÊëH¹í‰Ÿù~÷ä2lî…Z}çö¼pتßOмOÁ¤å½N?'ÌÍnÆðM|ò^7ÆÑG/¾iåó{K~{·'*I‡V#ˆ—KFíÉ^|ÖK™6¦1‘X="f${åüHѼ§4@ãõ°¹]¸ý?+‘QœÐ<‚ܰ.ÑÆ VyEξ¨Ç)¬Æjq’Çï’ïMÌI( M-XY—ž3 ;Â+§Å´Aá\À‰iCM¨2þ‚žaNVEÿÄ\ ûcˆ¨Y0íä—nVËŠ ¹)๰ƒòêÎ0»–§Õ¸îó³{‡Xé®Ï‹àüî¡[Xé© l˜T”ßz¢.©Áçþ7zÚuš¿ýá¦ÍùÀa–9ã».œ³¢éÿ|˜Ê=³³û™ØæfÜ^ôí®Áp¡š–©W$Ðìíš¿JMôL‚6f34>ËË‚ðdÁÏÀS»YÛX6*/oHMF$µÇεE¥ŽÍã jB£¥uDä´oö…?唜¹%Æ*à¢${«3ª=õÍTMuª9žËß|C±ÄÉ‘=ض|žµâY~P˜"(ü¢cÞ²ÔÏê^©a@ÆA Ïjð¼çãg­Û¹.å·S|Œæþyjj²9Åá~ñmÓ©“˜ÁoÀw+öž{C·5ÑëL—%áÔNó¯Á`¡>®‹Ó•CYûfl|JÇOgkàý«¶¾M?l|ïæn”‘2þ6ÝHo`eTxåóï w. ìq#Mù<í<4̲ÈJ×vÒ…ƒ~ áš™Æ|=r»=dµø„¥ ÏþGZØÜŒm®ÇUZJ…NWDý´½pÎo(ž†C[4çðXå9Ÿ™ßÑŒS4íFt¹ždÑ4UüëªUq@XC’0zH¯˜7?´Oç˜×Ù+4Z,º|k†myi'šýn¾æSEG+ƒ˜PiÅ3QrIs˜hoN ¥{ŽÓqsû*jh[nÌÔ´.˜+e$jòÏq˜¢µOÒÓ5½5»&´‚‹5P3Ñ»Àì¬LÒ}šuv­…l™iãjÚ¤:‚tËcѬeßyMÿwåhfg7õ³Šèf¡æfO_è€)ž–ù±? N¸fz¹ùFÏtaBPD›,Dá¡yÅL̘Oí¬2 qû4à¤×.MŒás‚Íÿ® úf‚"È› .Ó4îl­È±êÔ&UŒÕÙÎWÇë”RÐf:t›…j~̰ړت6`L E{¹•óVVJÐÈ ƒGþÍûð0˜‘×b³¼:ž³…Nän£>×M¢^'Òfh…Ò“»Ñ%8i¨°ä-ûñ¡âŠ'Œ|ço¡ä„Ž4¨ÇMôíúa.÷K˜È§Ñ}×,GJ‰¥4Õ“5Ò ×¹B¼ˆxH8jk4…¯ãëД"È‘ E¹î†, ÅÓºê³^W óG¯às´ãTW´Éz}CKíÑæ§QW!<º<§!× Ð4$÷UÛ‚-zõÒÙÆ?9Ü”ÚözcBÚ„Sºø,–VœÜ« ÄTfeP˜Øà™Ž$› êwµöd‡ hï÷"׊—–ÜžLßùÿADÒZ}ØKsnL¢Ìþÿ}UI0ú™lÿÇc}e‚0¡âÆßìg+¼Â´qn£ô$¨.cá]]ñ¬XôŸküà~˜–Éèf¼ï2D cMØU—ƒ—–9Á—dºs5¨}yá¢ò/>ƒ#ÚP Ÿ?¡å«]nú?D„ã¼0• ¦•àã¨ö‘¿ªþ†à ¯Mtç¶q¢]¦ñ@~Å{ªr{áþæ±âüR­Rœ¼ 8‡{­:¯N8À’ Ú¼æAæm¾ÑXÀfÍL(˜a˜Í F.ØTå,`¾Ê…yË ƒ–j™ÏrÍÛ|,âóÉÀ”l k_ >ßBtôQ·.ÐÁ 7|,«Ü“·ýrhe60˜Áqzê÷–ç§êNêüþжº|0ÊÒ"¥'ò–ßþ?{×Gqõßõ¦.É’{·,wŠ70Ř ÀG „jÄà!@€PB1½0ƸÜ-˲,Y¶$«Y]×o¿÷æ4§“,Éw§“t'½Ñoµ{»3³oþ³»3ÿyoÞ )ÚjØ‘û!ìÚ÷xÜ6pc© ˆP™KQp8ë°]ÒÂåg¾)æð|³þ~ŠÚ—ŸÏ|*—äÃÑŠ,° Áš1ñ^\Þ¡½°=‡Ë7AÎý<¾Z{7zOõzc=‘ ‘—KŒü“émôÍ 4È4¤¡¡|ÈË[$u¸»Ó@± 6ž4퓘SOÁÞ+”øŠÇƒ„Æ˜Ðø àƒ^ÀEQ¿ûìîÓ@q_‰ŸÖ˰Hi `HŒL/;˜ôͦ¹Oj±·R°Ãì®-xW9TkûÈèmîÉk×ÕâDr5š/¡ç4òºÏjÁ‚ó2¨cNÖ7¡ 5Dd–õà²z1QÞNˆˆÔÎ?’)54CúY|ò’ü²,í$=î’LÓÑòSÆÏo´‰Íÿ&Kpn mþ!¿Ê™/á kcûbÓ±_Ij$±‘¿åõ²<>Ê797àYÚ0‰ÿ8$õ>v¦Ÿ}êeíÃÞâZª(žkUjõk^Þðââ¾1Õ•å#ml©—¿Šdaú§‚¡©3 5ñd8ḛ5çKؼóK(ô­—ØmfL¸M¾âa@ŸI‚¬ìÃ90û ¨}yïû+áÐÑõB33jà¹9ü ‡rÖë,ðÎgó}æiµ ÅàŒ%vg|À¡L™B ÒäÌKj°ªÛñ<Jþ¡¤éNLC‘74²®B­§@îÑ‘8ØŠökJ¯0¡iƒz>ž+Þ{v×eøQL% dK)ñÐ*v×HCµöþyÃ…&@’y==ieÈE%m4R‹Äê€ÌÅF6dÁÖØ™àQQ߿ġå:6’Ì´–’Ö‹£òCfÔ8•d"ïfÃúÅ y¥ì¡Úц³ü­•3šÎµW_Á–CÔ•5 ¸Hî¸Á}}ÏX¨õìý9~@Ð+HlIjHk@†6"0rÃCqL{í# ?Ó´—á+7y®ý\B¸úàÍîÿ=õºq½F¥hþò¦)lu3 ñ©W£Sv‘[uå±K±—?pc9|öQB9Z®ÑaXê\ˆÑL„o6ç]9p.èšIËZX¶i’•"]¬É;†W‚fh2”Tî†xKšü‰Äe“ÌøNy@-’w}/¹¡òРW A–Ÿ8 僮ÐÅhúΊטvV™ˆ(Îó`ê©ÓdòËXQ)}eåã„‘‚·ãˆ,ùYšG`ÔÄœ€Äà\殫X^»ãûÍç ñÜ0q܈±§¦‡6—Ij(¼dF z½7nZH·XÁU[c¶B¶å¤°¢GkÔ4Иpd±xj`T#$§^0’_–%˜,eš®.02Fkܱ ÛÀâ®)Ãú¢-»¡CõÔ6b¢t§uÛ¾o»"1”ÃÒþD† x¯®ÇARO$¶òw§î¼Áv€n°ðUÕP'¨½ûŠ#~ª^:P?×™È&‘=WÑ`˜?'¸ùª Åÿh•µ‡ÄÏäØaPhßÚx<öýè‹ær§=ö%ô?@9InYÿKË´²üô»»C·bÚI…'T%Öt‹e‹}‹TY÷xÌš¡É‰£€IŒJU‚=ŠOUe_¿tíkÅ‘D,` Üy‡Ë©FµŽ ƒAóp3 ÑlEí š7Ù‹ £~䘧¬©iO(2G;óÍãÍ¥ÚKC£ýDfú8‹ |ìŸ`i”ÓKÀH~*G(¡«ËŠŒÑ”Æ¿®F¢YàÀ¾ñâ¹¢ç«#õÝßs@È(‰"ú Q"+‹Ù ÷Ýd;ˆ§_’—¾ÓÏa­wÚÈ߬§;e/;š²C×Ñ›6¦çÇœ’q+ü¸õ/0<}ĘS`ß‘ÚÌúXõ>˜2êˆ5§âäq+Σ9±í›xèÅÈÇ>%á.›…ìÀ…®Â´"ž0i¸ž¯Þ(”e”L†J¾ýò8Rö¬¡‰”šˆr9ˆÄ,þûîÓ]Ïå홓áЖ 1 ¨?üÅ=ãV,ðª¾É­jœ„p³ÒªÔø;´Þ<&”&WÔá4"‘1šŒ¸šÝ9¡ŸÙ‰£l8‰Ä”ºZÈ3MhN”/{š‡Aff¤™I1+00Ñ$ä#9I^’›4,¡š2EzùÃaWåá_WCõ0j@‚xžÂQO-Ë`s8qÞ¥'x®-3âߌ@/@à¾këJÿþF¸9sV„ ï^€\è`àƒå×Â…§?·\¼ Š Öfýò/mS°<\3gâˆ_Àí—nDg+`ñ×¶·7^`L;¹ÖU0Zj&q ¾=|· ³gB4dœ@"Ð’ÄàÈB«sbZ!1ÂdÁ}"'99W $áÅi³WVU×wˆÐÙ.&FDÆb1ƒWx¶£}§Ë }ÝõèúÈeØ Nª[ •ÚþÂûY…®ÈëÔH\ÚÚÓÚ%äî·¿ã$ºJÄ“Ì.Ô̘Q>Ä Œ$'ÉKrK“³¶òkï|$–¿=y#íZ˺Ң¦lX²á§pÖSËrWÕÔ+n—‹TùNm™1ÿfz(äò¹3‹Ö‘sòVöôÛƒÚ¯ôØnxíËspòLã\™¦×þ“•7—®ÞZo~sz%ïhÇEàD°å 6~"t(J$bÚ¡µ‘8ÒpŸýÆAcQyÝ`Ñ4ÑZâijÖдQwqº Ö:P(V֭•€Éç:…¦OLhb’šFÜbÌèwÞÃÓaPjrh™usª IŒJµøwg¬DMŒ?‰iY ‚Xn[C}Y­Õ®j@bÆy ¡o‡^-VG6b1‹ðŸïÀ“]èCßëEÄ &»*è ‘ Ò 9qÉ »ÚnUxø¾FqÁcC…¦nXJ²"KÐ: ¯IÆ(d‹EWÏ1qLòÒªÎ^Z¡)©"©ü¡Ô_w¥i­®’qAºôÄÇÅB¸ëÉ¿œô¼ÓsïtØ ñ¼|ü£ð1#ÀtÔ¹lÚÂ/¹q&Ø05óϯIvú¬üÓGÀô™€ ÐÙ˜$D‘$Ö!$íÔ$ÅÕuhn¦ˆh•¢:}EFsW§z÷À2O-°{Ed,êÜ.ß”ßÿ´jq•VêкTF°Q‡6ŒS4Ø™5zÊq¥z|¾rÄ"¹™{òX8sÚXìjè0‰¹§Ýª—7Ú“g"wÁ¾½Û ~UÖ¾B8eü°v·w‘̮ȻÍŸ!͇ÉŒ¿ëJ¯Y– ´H\“Ü `EmÕ£ÁU¨àòÔa™ð—ÉkQ§ˆÕü´Èébt ˜Ðí3\ÒÄÑŠ…8ì,Ç©A9I^’;Ts3yÿH(¿”%Ð}a­à½"zÎ+êªà­'¯#zS=Þ:W/‰5j°žH{Öyõ$ËHÏ;6fªÒÂ#«ñœÿ{!£ðž`F€èzœ0FÞ»F¹ò8’ö‘Ý“îd¤²÷·¾^ÕµõP‰¦F%æ±Ð™>Ÿ)‹ý|º| ,ÿ)®¹`ºÐÚШz¤Zì²ÏïáQë.¥91ØÑjßœ¬¥&ÆÄL}¹Èl·VÄîú}&ª@Z Ô‹d÷®¬ŸÖí=ù¬sª6eˆGB2(>-+™‘£KÞÎ>š¤iÑðÌnC²ã=.‹³ûI{C£$á $ÝO "°F™Á™Äˆ?‘™„øxqL$‡¼œuD;#厔òKyÙ¿¿½ÉyѽÓijU×…¦zB‡H(»ªžd ñyLJN©øñ‹È¯+½MìNFâ=#À0Œ#ÐÕ¨`šfÃûâêk»ºúöܯךe³áã6Cƒ&rcNï²Iá.®…¢OM6…kZ¼¸x9Ì?s*œuò¸ÊRémÅ¡.|•>5fÄãkŸÐ˜bÏÇÞT 1€ãºörb;$Æÿ u×a^SñÜÿùŸo<¦ìi£Mì$5¦ãðþ}ߪ՚«ò ŽÂÈA^ýx>è ‰‹Aуbñ–„x#ÍO!³.ò|fµÚ¼skÔ¸=^s4¹rGˆ u)Ç1’W»dº§W;ƒsfÐÌŒ43”ɧ Ý@K€º³ü-e ä·Zß‹´U]"¡žè9ߵüèÑÿa™é= ÷A¾]߃`F€hN“•j½<ޤ}¯#4ÔAýá§løhÙf(×¥¡ÛÞÉaqÛJ¥Òjõ[bf¢ëÞm‚\yš¡eˆÎ¯ìd…’o0i¨Ç´µØßîsÂ'uS !A7­mÆx»þ~9IbüR"y€Oþa‹ 3Ù–iÝ%Šï¾´Z=É‘ŽZ?ûqôOŒƒÌ‘;•Ôø“˜ïö; ¬^ö›pÒ€ £À'8‹ýÃfûýÌÉü£û“™Ù–[à¤$25óLVvñæ]¿Qòì<>©xTПÙX'ñ7 @#ÒÔ‘£•Ĭõõ55ÛׯþzÆœ»ßúròóguÈôLvêEÇ5'DˆÐ˜q~‘»ÝN pî 9 @Ó4³³P¬Ï•3èà­ÑÜ YVKëâÐz8záš™´4䀈Œw1M¯f†Ò„3H¨Ì]UþPå×XšT4}ûĆšMÀé$Ô#u7Ô=ßEeU—µãÉòòâžÞ69 ¸9"#À0Í ÅOõ?ö;Ê«ó`Ï¡¯›Gè„_z¥Ñ[]'dÞY*‡`2 …*Õ¾mwŒŒ¸E5I¶¦áÐn««nM^ËÞÆ93dfFš™H $×Dy÷ÛðXz4I¢y¡k%Z–­mÓ<¦VA?`vÇ»§õƒêÕ߯X°€FŠaA$Æ?§ºÚŠÿ~mϵúJGZrrl~ɱºÅh¼ö<ÎNžsï¸CHdTcŸÍš3îÙœ˜Ž&ASŽÜÊÑ$ Z‡Ã¸eÕòŸûöû¯DZ¥\{áªP55Ôa•n‰ÜбÔÒf†ž ¯vÆ-44dhG˜¶PÝSlèi‚44tOÒÒC"Dbtb®†×£Y¸53þrwGùýïè±&¶É«]ÿßÒD&)^wÕifˆÌlÎ9¨:z8ÿ¥_~ò#€žzè}ýáÃÄF rР=ígÀþ#Ë#G¨.’$%9~}þ×ðÆ’ó¡¤âÄÓ/c‡`[i„²ÊЗ:Éús8ïÔ§aç¾Å`²•wjI' ¿fLºG,xZUW[sÿ ?íþO§Þ³+3w©”Ó|­‘ëºòÞÁÜ«×q'ofÕèÉŒæÌf$’ɳה‰¤f-,ß¼ΞéÝUÎ@IL²Y ç ÓAÍÁíP•Ÿ½ã¿=þÞ³fˆ*ÒÎÔÕT ƒcÎPm)kh¸ûòhv¦zAÑzÇı©Œyvç»8ÿr¼×*ܨ'54äÃ’z¶úe¿ÿùÜK¯"•ÅeÅ•ÊU瞪îèœêÀÒ¼A.ÐôL|>Èi€tàuëŒtРÜÚ6ï¼"5$TtoqŒD‡ˆ ÉÔAÞ«+ÊJy4–&s»ä¤øP²:ÄÞ‹M×ÔÍ™!33ÒÌ:ðÊ×ï¼ñ& NÏ=i(¥†† MÐµÉ ÈD`XÚ,˜5é^Ih‚­‘Ó'ÞU5ù"4ÃÓÏ‚CG×ÃÒŸ ööAÇOïw¬Ýù±Øiæ°Ë`öä!ûàg@ëõ„€–5sdc¤V+Lhº³R©SJó"~ؼGx3£¹+‘H.ò¶¶r[Ì™:Ú×Ñ•®@d–Äœ;B'§¡)f¾¨ ªpO(MÊæ£+¿­Jý™­¡ræ·Ƚ7ó™ÆY¥Q<äú/ŸÎi´šÜ÷úŠrç¯Ùiƒh ihÈþX§IÍgSfÌ.òœ6ë×ϾýmÂøéÊIÃT™#ÒBZ§†ð$2AX2#m 9ffþš™pºŸÜèžÒI€<‡eìÒÐUå¥Pj=Þ@κ"d}о3ê‰Ö™!×ÌäÍŒ(wÕî-›­[ú5 Ù’©=÷ôü¡½ˆ˜#ÀDé}§Áy§<&cÜ6(\ßm|¦Œ¾¦¹bLý  d=¬Ûõ<•mÃo‘n½d ü¸åÏ0}üoq »A°§àkLó¶SÞõñ"«„ÁIs.bQSú$Žá©³¡°|+¬Ùù—o‡Ó3! Ó­Ì—ã’¾#?@æðËà4¼f6&ÃÁ¢UðíÆÑ\¼ F<& ¿ ¶ç½sOú“ˆksTÁˆ´¹B(ÂûÓ•·â±‚×@¿„qPY{57À–\GBS%Ô5õ15ð\ÄôÀîC_àÀò¸nà¡åIIÇjÂÒ YE"¿KÖßçûµ5ïmÔÖÜ úL†¼ÃßûÎGë-¨Y\^{–”_§†ˆ-T¯ÐÐPG5¿° êê­èšyœ¬—ˆÜ—èÒ!±¡¢¼#§ŠÎ/u¾Û ’Ä|ƒû—6›Ó<•ÔÄø“˜æ1‚ÿEýK¶ìz³¬€®/ÜŠû&KlæLgMÉœS€fg±¹7Ž©¥œ±Ÿ¸Ø¥x.ÃÃoéwöcžÙYðÁ³9gàO- ÓS¡½=MìÜm]½bÝž-[²O›÷³ópŽË\ìÆcÇS‰5”„x‹Ê¨ÃÅ]:Z’—–¿CÉ^v–eÚ–¿åùHØ·,oËß]!£ZwŠï6ÿþx¥ï¸³ZÖKËß¹¿ÍéTªªëZ41Uy<îꃾ\ûõ—ÖÖVÒ­tNï™›±vAàÀô$ªê@AéO0 y"|µön°Ú‰âÕÔáæSP]_(FõÏȼ /¿V\‹·¤ÃÜiÂŽþ"ˆ1§bGÿwB{蛨‡Æ‚¤dò¨_" y–l¸fL¼Ëw'|ôãõW¸ ¦Ž¹öþ€@BEM–¿?\pÚ"X±õ)(®Øçœò˜<òjX¿ë_ Ó˜aè€Ð?9ChEö|ƒ¤Ï#ÓÏ›³ÖîxÉÈœ«¹8f)$ºvÖIñmŽj¡9Ëz1lÈ~ ªj p:R”Ì›þ7$MÕðþ÷WÃd$Ÿçœò$¼ñõ¼vñÒ-€tW÷ýèÅ• ³±)\Žb»¸'ë®Ì‘ZªOh¨SFÚ™ìGD÷˜Ö™‰ä äîyÎÁ"< O“вƒÕ$†ðC"²uϽ¦”lÉþºg*ϽwÂt~ôŸÕ×íúÓžû2³0ëGUµûB<ý.]óšy½­˜£CñhvEŸ)c‹s(B“·3¥–…Š*ÌÑjíË>~æÔ|1vòI#i²Ä$ëMÆ­‡X8D?ƒ¦”…ر;/OGñ^q»]6‡Í^ik¨/;²?oKÖ¦õ»±¬&üÔ4¡–j‰ˆ¿¿p9Œt¾ 4tB«1Š-OÁîüÏÅuúgÅü­öJ_glÊy—k° g’¨{`Hêéè,` Œ6ëuYÏûÒ› Ög |²ò&¨·WÀ®ýÂUg¿ñ1qêÂa_<ÿƒX$ž¤Ú°ûe¨ÃéP‚Ä:”´‘FåqÿŒ:a"(ð•<ŒÄ}/!4n¨¨ª—ʈ[ O^V+$ÉYQ]'V¶§µRÈ,Šòî"1‹²G¨Ô3z+C—/V4; v 'èP33?BšÑÏî|=¢£à‚Kè’™Lhpñ%Xìq{nÄCAhÈÛÙè…Y ˶í¦ÙÞbˆj×=™-¿ djãÄYþ&’CÃ&´Ú¢1gÛ¦ÜH nÔ„ö¥Æ„"Q ¯ýŸ”æÝçŸY £t/Ÿ_"äDà鹦gØŠ"4ä 6z¾é:=ïF B ¶·e@Ëaô†©oy:èß³¦üƾÛw ¼:ÅžŽ…>€Þ¹öúò,«ÊA3¦I¾ß(Š5M‰Ò´Vžöòj-~8ÊôØ.ßmlÇ)!2'ËCRg ³¼_Ÿïןn1¿u‘èvø'kv<çÔLG-WRì049Ë×4j´âºp†D(9–Õ,þà”Ó+µÐ É DÂú¡™\k„F¯‹ËÏ|Ž”nUÛÊ$ï©®ôØ×“¡5Üåµ®Üc#v¼¾ _ËãHÜ÷xBCæf4Ñ» ØÔÑ1ˆo׬ÍãÈó–Áà†¬"4%;èérs2ßë§[yoÂ?ó¦ï¼cä‘•Å$/Ô #Ʊÿ?‚ãW·j©˾"ƒLfDÈ<þㇲ~‹$æk|,)ÓÆÏEÍ̉ÞtÙ ¤Îÿ1uøèžT‰ÂaîéùmIhŽo}0‡¨D ,*¥ö>·$º|~%¡¡wCs"5Dnè7'ÂCd†Òp`FÀˆÏkÇœ–¤$O€é¿÷—] ùÅkp.Íõbˆÿ' )~¸…ÄØ¡PV•ëûÈÛF}S'94Ä GùNÑ…äv`GÓ12Ïû÷§g´ß… ܵTBsr°h5¼óí|1çw—mÑÝn;¸Ü6œ§4´Yr»“Æœ@ÔÏÑŠæd§YDüA¤èò3߀ô¦öÅšßá™à?ãTWfcÇIrKÙ:ò{ôsY§2¤1jìíÙÓ‘ ;9m/ 4 >¬n°;pÁDÑ÷ídDýûéUõ6X´¶Ö•9ÑY˜u Çk–:cNLkâgß›±f̳»·;lÏûgÞÌâ»_Wñ¶›41ï[ݵOáû«'23õåýñu5õ ´Íw^¡úçÐ\ «q¦k!¦Xš™µvçèk@@ÚS'މàЋ€ ·^’úZ‰‘rè!ârг+7z~ýI =ËDbhO]£çœ#ÀD(4b.7£V nÅŒs%Œ8Ï•Æ%NŽUïƒ)£®®}]n+Ä[ˆDdŠFfL™èúךÆäF¥Ÿ{ó—€ÅÜOhgv6šŸønøAAÙÜ3X :ŸÜ¤k+Ž,;íC)[ù¶v¾±JÅIõ¤õ ÏhÞá¥0qÄUpÒØ… ™‘kç#¥›ZK~Ü9-‹±/šÅ¥LpʸۚÅ!¢3fÈùèlàG¨³• ' ‡Ð4Gäˆà‡ÍC=žjâÐå@IDATÔÿTáÐÁ?1iÔ.™ùoHŒ _¯»ÈE52w£¹;YWqfCд@ò9ŽKù¥/­ –чó%ëʃMhhþŒw[ì»ØŽÞ+¿Ò [*ëAmFo\¸n €tÅÐU$¦¥ü{îÿ*Λ™xÐn}—£|çã‹á•íw ­Â¹2— ø/z1{µ®¶¾-ÀÞtXNy4:hœ"Ó2×þ¦Nž$54‚M@"#Ɍԩ7µ'Ìš#DQ c["ÒsKA>Ã’ÔHb#ËëÞØüŸ`"_g:ò€˜c€V°ZGƒÅ²# yóŽ|ò_Àí—n“û?^qz6Ûÿwá`sÔÀªíƒsq²»(,Û WÎ}Gtìó®…½˜G dC$0 )¥n"c¦÷Žòûçw¢ãm{ß‚sO~î¾j7,Yw/ìÜ¿͸þ†ó^î…³¦ýËå8O%PBCšðJÆmpÚ„;p˜wУZS½-ÛôG¸hæ ˆµ×ây׉útå-Â1Àm—¬"“§³W gã±ü¼’Ñt s6 2=ÓBžŸ­jNœè|kAÖÕà”¤°ÏÖî칊¢~÷Ù,¡Ñ¨à`óèêø=šÐ˜Ò䌈MT"cÔB“¹$“ Îe€sG|.–»£,)S3tË®ï*ÛÕ(œW‹‚ €¸2æ…œäÜߎ w'”*NŽ`ËyD`h“ZIdä/qèЄùh òƒC{¹y‘›<­åc¹^€¯3­ -¬Fï6 \Y£àˆÿÄ€ ­Iòæ7'›Û‘ÀP§üÝ¥WˆIêŽFó¦m{½}Féà`ÑJôŠvWcšrx Ù ØÃKŽ7 ¹i*,Kà¹4¥¡ô¡–ŸL¶ž~{ï¶Ÿ¬¼ÙwL»|"6yroÁw°·`)b/\3Óùu»^¹m¶;j…ã:OŽüÐ9 ,óõÅÅoZ'fCö¿î-]_W£º·¿¹µ7f$¬ZQ?”ˆ8¼òù,ô+ò û¶ Uµ‡š•­åõ@~S]™ôjHMŽ¥qëê)ûçÝE»ç`Ë•&Ò ¯†þS2—†:2Ì};·G©¡\&Ê Íã;¨.˜`#cѽAŒ u¤²;š–ÔSŸÉ½¼Nq¬Æ¼¶ÌoOøÉŒÿ-d:„ðµ›øÑâXžã}t#@óL¢9Ð3+ƒ|~åoÞ3Œ@” @ZïBÈᤇFNARs¸fØlƒÁh<pIhíÿ ÉŒÿ9yL‡‡®`ÉdµŽ€Ñ©qB^);•#”Îò~ÅGfüÓ4ØB3¥ù2í§«õy=­™öò 暬«©#úùž±Pë)˜ûž0®â¾FÆAýÞ{‘nnF²†ötËRFÁÞGj¢@V‡'âÂZÓŒ0)­ª"ˆŒm¹wty èg¤NͼÈ_Þn8¦¢í¦½4áá}tcáÿ(E{]ú?ŸþäÆ¿Œ|Ì0Œ€Ôj) "£×ëqÓAz¢tjTT^…¦v†7›bε 6,ÇP&ƒVQ©ñB^’›ä—e &O™¦«ËŒŒÑWÖ•I¯‚ ÃRÅój=…ƒ©/™‘Ä\*óD»oËãHÞ÷xBCàG¹Yã#ÉX¤=@Dj¢­Gn,#À0Œ@ô! V«Ð­°FƒA&Hza51(N‹Ë+ôƒŠŠùa-™£ýëãSÑìêÛ ó%Y(ÓØ”8!#ÉKŒä§r„ººü¡Èid]4"Ìè £õ. êëʯÁþ'yd û·Ý¸æà–påÝ™ùô BÓ§£. ²gŒ#À0Œ#ñHóN±w¹]V›ÓÙ! ©4¹¢§Ñ`£É&ÜúÅ™ ÅŒJ2 ¬ìÊNÑÔŠ6ö—•_)d’¨ÇE!Ñe3ÊHò’Ü4òª)S4”?Pœ"!ž]K‹Çuv’ÂRO-Ëfs8G!{EùN´ŒÒêo¿“p‰Ž—åq¤ï{üšŽT€Ý:ä‘I2»"B Ã…®‰ÈX,f°Ûì`·Û!ÝåÅc…ë8p–ô¤„¯‚šSŽú¡ydúFÚ¢ñÚ™”‘ä$yIniÊÊý"½ü¡”©»ÒP]‘I iÑFõ·@æ~a«§–eªª©WÜ.—ÿznǽ-ÓŒýûιndÐy4;«…$Ý-ãDêïMhôPͶLhûÊÏb„væŸÇ€­áç¾UžMTð—³Ì0=M‹~è~ÌwÂÂuV¨w(°`¶Jê<0ç¿Ì¢‡ÇzÞé¹wÚíEþçOtìq«îð*t0¦Þh\zãDÉ"âz'4¡¢ìr«àë<L …Eë­PXë%%Nd1»KÜð~–ôHnþ9Ï;ñ÷'9vHF÷ÊWf˜àõí6xbeÜyªIlwçópª8œŽ`F€`C€F¢ÅV°wÏδ¡Ã¯ÌÚW§ŒXêVb‘ÙM®§ù3¤ùp"™qã¢Ý´4º«·Aiƒ *íC¡Ì6 „jUh´Õ¸§eÔ:<ŠÜ®x4o3ã€-ÎíQ¹¡¿Iô#Ä"‰‰…¸8܈Ԡœ$/ɪ¹™”8RÊ/剆ýqu…¤3=NÃúÆAbbçÔ“Ä…žw W=|ˆÑ¡wá„aü?³†;ìpˆˆ¯V¥~þ„‰"(š6*ÉF„9å´ì @V©àB—2¼±Ã†'fn…Τ„†®—¢†fáZ¯gZóÿ&e2Þ3Œ#À0Œ@ç"à#3xÏ®ÍöŸ2÷¼êMÙâЄ¦¢ÀŒ|Z $ôïÈg”c+¿þ4ÏÉwB^nuïp(÷ã9·~IöÝûZ¡'™ÐY1}-jøã,3LIÑBi½úǨ¡ÊÖD~÷ù";Ièü.-VíÓðy;ŽÎ0Œ#À0! ;n´÷¹}/Ø—û=.ˆyY^ÁQ9(%°œZ‰%‰‹µ$ŠÅÛî{œDhhÖeÀ øV«MÌ­1#©Iöx5881[äÖóqi.FÇHšæô ÆïIÄEÌ™Á3if„É™O;º3€–„RþÒc{aû÷Ñ4ÏêS ì7 Æ üY$3‘POôœïÚwDUVTøÖ!½þïEËj¿I;ã´+7xŸTAàŸm5bŸdBdå\7ÑãújàìwªÅ¼™wæÇ6ËahbÓhÃ5ÔáÜšbÔÚp`F€`NG€\IfÈ̵vÉ—Ë yö{ßnˆýÃõªõº¦v:Xi¨SOD‚u^½¿‰Ì ;gœ¯bCBcCgN$4.— 59a–Ö2#e”÷# ™‘‘Òë™—ÑܳÙ$625£ë$_8C0åÏÉÿvþt´zúWiaÂÈ+`P¿SÃ)RÄåÕõäpºŸsº7«\ùÙÇ"8ò]h·#ê´Ã$3âÁÆÇzyî=ãŒ8`O šv:T…"¬á“pM…UZ»RQ#SŽš7^8} ¦¤jaùÁ&5*]¿lœ¶uÁü1ø©Ð%òhç6|‰`F€`:Ž 0ÓF8"34qÅÑÐPgݲzÅÿÔsξõ­/×(ÿ7V‡LÏd§^t\±÷çÕ”èÁl2 2#8Âa€§qž$4ÒL å 8ˆ‚ì(£¶ ­‹ƒ$ ‰‘ÒÒ"2tž®“œ”&œAÊ@en«ü µ°bË"È/[ ó¥2› ‰pÆ„»!)6ôyLá,Ggä%¡–uG=Ñó]TV»·üô÷ªªrrÙLᅱ渢[”ár{®–x>,£iÏ„¦Ú"S²÷²lðð <ŠffÓ_«‚wÑÀÓs-°åæDÈ.uÁÑÀ t“(Öbü*ÓOÌ1CŸ?¯ê'ò~¼gF€`4-ÉŒŶ}ݪ¬Ä¾ý¾ÂßÁ' \{áªP55Ôa•n‰4H—ÎD.H3ãp÷3Òθ…†FAŽE„F’Ÿ¤AÐ=ņÎtICC÷$- ­1Cä‚HŒNhm¼ÍH®Î í•¿¢ê¬úé~øÝ1q8‰ k#%1æL}‰WBg‰1ùvW=‘f†ÈÌæœƒªÃûsßZ·ôë  =ûDêé}hSCƒdæ ¤âAù¿Ì¹g¥ºÀ„æUöçÕVxvÚ~⃠ ",g¿]-Ü9“«æ–¡IЯ>©…Xƒ 5:Ç_oŸ3Œ#À0Œ@Ø ŽHSGŽFÉKm¦?ÿ褤²¸ ¸¢R¹êÜSÕSCXš×"ÈΣD{ñ^2ãu =¡u”РÜZ„†H‰ Ý[#Ñé ­ Ý·µ ï%Ë_T¾¾XwXÕ`²`kbü°ËP3s/Ê¥í¡kíþ‘xŽž ^lº¦žhÎ ™™‘fæÐÞœ÷–~øî'(=óôüÓ{@ïC«„fô¢¬iŠ[¹¯“ =›©ÇQø M•fm²(óÅnÌø.â“4ø˜`F€èh$‘:pÔrS§ŽÖM w£´nÅçÿXq´¸ì¤Ùs/{öíoãÆHWNʦʑÒ:5Ô%2AX2ó"m 9 #µ2’ÈÈ=ÊteÚËî)ÈsAgÜÁt_*ÿ¦=¯ÁŠ­A{èÄ|=Ì=ùO1d¾Èt¤ü³Ë’d}о3ê‰Ö™!×ÌäÍŒx\ÎÚmëV½±uõŠux{25£çžžzè}h}„Ý Oâ5PòÅÙ÷Œß!GÛž Mk¬¹°ªuÆ»pVŒ#À0Œ#Ð’ÐH uîˆÌ`7[LzVgm\·+oÇö§žsþén—ó ìÆbÇS‰5”„x‹Ê¨Óy{¥mÜ Ó-;ï-’GË8²³,Ï·ü-Ïwíç Á±ç³YÜ–,ÝTêDÐ(·ÁªÍI¸­çÃQþ®-WèwkY/-‡ž3ª\œN¥ªº^¡E3S•ÛíªÍÏÍY±á»%Kq®Ø1Ì»7zæió×ÐGhÆ>³{¦[qƒñ¨óêV©´‰ã(ý×ã =Há|˜Ú«çÇ~ ïšRö®’¿½²ñ5F€`F J óš7@s¨aÖáF“]åÄÍÖà^ñÅGËW|+GdL8xÔèQ昸ƒÙ«Õêˆq8C¥vðàÏûŒ•DE°ZSl‡ò^âtZ±öÈÓ¼.—Ón·Zk¬uµ•‡òrsò²¶ĬIC¦ºq«Á==÷ôü·9 #ý´3ª7÷Ü3v/ÆÚУ ?С*X#ê5òëJ«¸@‡¶°þÁ¿,þçù˜`F€`š!௥¡Æ”ȌԺH²C¦8dŠ¦Û—½c?nÔ1¤¸2rh g«GνX¹šüÈ8ùû`Ûÿ(ùÜáø7aÌ!üÐsMØú?ä…!òBš"4UÇDräüJ×,ŒY”}5zà;ƒNâ°¿ÝL<Þ,Bþèфƿ>,Ftm衺Žü`ðØÀ¨ç¢È¯)–`F B>¥–$ÅŸèÐynbn î¥GŽ&Ê4x‰ƒ?Wݬ:3sªr1žób¥€sÓÕŸ½ãAïXÇõý“òqhHP%™‘sÄè–ÚÒÐf†ˆM»Ú™ŒEÙINûïO$4/äÜ;îü­û^AhH»g6€V±á†îU¤}ŽÌ@òiQKh1Äø&•E¦¤,#À0Œ#ÑPPºõ¡N¡üMAÙ&B#´4¸§þ$5LfŒ–!!A£¿þnÏÍ}úÃtyÍ儊ï>Q=·n¹ç€<ÇûNA@>¿DfÈŒŒžkz†‰Ð!BÓиÑóM×éy?.¸<ž…È;û‰ *ÕaIõÇã"Eá‰OhˆÌÐ68% Öï.€dg ”èÓ#¶ªH>à„„†þºpPÂ1Œ#À0Á# ;Ô¹ó?¦uþ¤÷3é0 %¡abÓˆùäÓ4)]éyTo„a§ÀZYïÿGýľÝn2sâÐ9ÐsKA>¿’Ð9™$æDjˆÜÐo:/çÍÈ´xÊÆ,ÌšíQ”äoô8þ›ìÛ3ˆ E}èÑ„F’äš‹+èê ¿óHD’æû$Ç›Ñ"­ò‹ºr&5Qÿ¢qF€`ºêÔI·µ4bío®CæþdFε!"ÃdA pÍmšé£'zþ†]‘xï´mªR-~ù)ÍÓUU¸’(‡ÎF€ža¹ÑóëOjˆ¬‰¡=mt­UÍ̼æ8¬/‹œ0Öç‡{îÉü {DèÑ„†jˆü{œÒÀøAÉ`ßwâ]åP­íqHr%¢†fXjœðg/å¦2p`F€` Nu iOpêJ33"2’ÌÈ— ‚òÀÓê[â“<ãâℱ<{E¹êÁgö|ÐF¿™¢q/ôÜRϰ$5’ØÈßòº7v‹ÿHfÁzÕxºZ­ÑÞÙ"JTÿìÑ„Fj6ˆÐJº†¥BvA9Œ²fÁ–˜™àQ‰÷3"*P­¸a$ʥתaTj<èõzß꿲!( Á0Œ#ÀD'Ô1”#ØDjhD› mR+#‰ŒÜã¥ÞæÍ‹1M¿°þ%F¹;Á2V”i®~ö×Vy‚÷]‚€¬ÚËÈ‹Üä¹6…Éx&{œKñÜïMNƒýð@Î]ãŠÛL…z4¡¡ú UtµHhôhnfFÇ'è«vÁ؆mm™1U6¶a+XÜ50.=LF z!3ÉNeàÀ0Œ#À0aA@v©CHA’ï/ïoyÜëö ^4 Õëê?F"3AÍ“V×Õ›®|ö‘†RyŽ÷]Š=³2ÈçWþnwO pŽyf×+ (ÂÅ6’¯É¹{ü+ª{ÚMu{¡ñšœ¡1 0$5 Ê«ëawa!d _ˆóänÕÔf†ÈLg IÔCZŸ80šŒBV’™´Klruï Ì0Œ#=ÕAŒžb/éS/kÏñ¨ï!™Iò¥V©þ¬L¾ã¡»¶F‹C”!0zQöoP3s:‰®¦ZPß‚¤ÆŸ EY‰Z·Ç2×"R ÓéÀ„DÁb1Cæ~àr¡¶¹¤¦ÖÕÀ^Ó„n™SCsfF6dÅSã50"%bP>’‘d%™Iv*F€`F€`: '_Vߣ¨ÜE£&ß|Q½í¡›Üolé¬Ûr¾ˆÀ˜E9™·ëy ìO>}oÆnù»'í{ ¡QƒÉM¹b,°Û1¸/µ*È9Z“êÖB¥®?”èÒ¡÷¹N ­3C®™É›9Т9ÙD5¤£f&66,1!#ÉJ2{=1¡éI/—…`F€ˆ¾Õßâ´–½†&IWÊù28Žz öç?x“sS¤ÈÉr‡À„…G-vOÙbÔÎ{rTϨv Õþ²'¸l¢&v'4Td²EN 8Ñž´N§Ün7 õx Á¨ƒe5 ©+ƒ,i¸p°]m·*|ðh<6Б›pTôÑ´˜¾&Òô‹$&>>ââp#Rƒ2’¬$3››EͻĂ2Œ#À0Q…ÀS¯‡9m¥Ÿb·¤Ù|Ee¾ü¡›êqa<ÑŠ€ ÊþŠ2†äGÍLhuW~sÇHZ«¦G†ðõØ#¯Ùji$à‚B‚ÌÈQ" z½Ö[¡²Î•VØ]Vp*V@¾¶@æŠ:ÔéÕ Ä4kÒà<23 Í ‘™„øxqLæf$+kgÂ?gÄ0Œ#À0~ˆù2.û{8Èê›/ƒs,^Âù2wÞr3Ï—ñƒ*êÑ ÀuÅs­OpÜ–ûû1¹¾ß=ð Wª7¯–Ftò X¼s¡hj ÍQ!Ó.: 0™ll·£Çn 7o\ôRõËù/䩌dШi>j‹ÄýŒÞ93¨¡!ÍŒ09óigØ@H€s"F€`F€h¿¼¢¾‡wŸæù2í•G=³sŒž¥ðØ}#÷žÌwäï5„†*‘ D4¼¿‰ÌèÁ€sVlVØ 9 p»‰ÐxPc™7ò»i\ÈŒŒd käÍŒÌËh^Ùl™šÑu’#À0Œ#À0áB@Ì—±•½Žýš+dž<_F"ýûé‹›Žy*c·Õ"J£R펉Mþmô—ìÄ%èU„¦‰Äh½„ßb¯¶Df“Iràp8Ð jhpž?¡ –׈‚¼¯µAZ-­‰ƒ I‘2/#Dd¼‹iz53”†#À0Œ#À0á@À7_¦Åú2<_&èFFžÊçPë–)¤QU£h®ÜrË€†È®s¥èU„† $¢ ]! °F—ÎD0ÈÔÌáp —Îä4€44èõChhBÕÒÐýĆÞHCC÷&- ­1CdŠHŒNhm¼ÍX3Ó¹<çÎ0Œ#Àô6x¾Lϯñ± w]åV<77•T}GÎ}ãv5ýîÙG½ŽÐÈê$â@Dƒæ¶‚óh„™š˜yÉŒ‰  ¥é¡¡ôÞûyçì©Ñàýé¾â‰Ž”‡âr`F€`F ð|™p Ùydü={’ËíyÇà½Aïí½wü"[êðJ×k Á(µ5D&ÈŒ4&ä@𙉑DFŸî!ï%45‚DñÎá‘ç‚Í—ã3Œ#À0Œ#Ð<_¦-dzÖùÑÏïà´;¾B2C%C» ïÁŹhLhB†Ž2Œ#À0Œ#ÐyÈù2O3—ÌGT ÿàMì’¹óïÞœ3gë+U}„fKIÔ*å6$3ïÊß¼oŽ›œ5ǃ1Œ#À0Œ#Ðí´µ¾ Η™ÊëËt{õtšóþ™gpx>A2s¾¼‰Z¥ºwϽ^‘¿y<Q«¡)(®€ì…PQ]Uµ àp8Eéz“#nƒ^ 1fHŠ·@Æð4œÚçøæ3Œ@€ÜöèSSÝn×yh“=HQ)Ф!6À¤a‹VU»Æ—×ìGžXáûÑEøý¨U)ª"¼Ý!ZóÝKO<ȶ]„=߆8âåqŸ‹ñw×7êD2†ëºFÝXt¤|,€G'óô¸-EvWŸZ5¨Ÿ¸ù!¥@£Ñ~Û›¾Q‘ÐFɺè¬=¶Aêºòo2&6IÞC¯Ø™•ú g>òÄ…ÜFITŽßG¡q¹Ü°|Sn»‰QQËd„Ø#tQU”ãk"„3•5v((*‡Z« ¾X¹ âcÍ0gê8󤱠C2Š^Uç1©kTZ°w¹7ò›àWì¦]säƒÇê‰SŠExo ×Fu²Ç M«`0NšL÷æ6ªíˆ½ÿüoÉz8VS£§ÀyÓ3`Ìþ`2èÛ.]/¹bµ;`O~ lË= Ÿ­Ø +·ì_œwªÐÚ¨Õj&6½ä9¢˜ÄtÕ¿¾ï‘së µ¯ªõ€¾º}Ê@íè¯ÍUéU6ÿ4ˆl{DTÑAr(F(q†Ã®‰)¥Î ‹]‡ó#OÜðÊŸ]Ú#JÉ…`¢|çÎ)v~§À§ ¬=¬ ?– ƒjª .{¯ÿFÙµ(ˆ û“F§Äúk½©öNü¦ßôß…þ«Öƒ[4n£Ú~?¹j›¨ÝX¶1^øà0èµpëü™pÃE§ÁäÑ™Ì4V*‘:ƒp!|Èí¥~„ï7ìÒjy<ômãÀè×Þð‡ÇîÖé _ƪËúŸa~ N3½¥¨ÛHf&D€p <Â'FU–¢(ª%ØÁº‹bÎG€Þ5zçl)æ} çíÿ\5²r ™éü›GÁƒp!|l•ýé›NßvŸ«£¢× ÔÜFµJËSÜFµD$Âxo/Û¸ >Z¶ 2qŽÈo.› CÓŽSÆ_ª^|†ð¹ýòÙ¯ÏVlCR“N§ Ün7zþ‹æ›^\©á):xѨ¦î×÷?r·F£û[º.[5ÓüЦöPxîÐCs!|fZ^Ö¤ëv©ñZÔHjOŒ#~T^2‹†Wå©/Þû‘&µž¦µqh Âç’¼5ëö©èÛNßxŒKsoè›-ß*n£Úªàœç6Ê PÄ2xê|“™ÙÇ?l #Òá—óN½®7k™OðDû]&œ/ÂíËÕ;ag^ 5¤©aRãTï9¤†‚ÞuÝ/~{Ï<Îð$3Ê4Ó*­ÊëL£÷@ZI '‹pÃQã…7þaMLŽ–ŽBh…æTŒ@×# ¢w‹Þ1ìœ+gå£ÒzøH5N„áFßxúÖc:"5ôíôo·QTr;q¸Š` y-ûß7ë!%9.Ÿ;µjäKm!@¸õOŠƒÖÕ[}¤¦­ø|¾Ç" ÈÌøñ§ÄÇÆÇ¾§)Q&?‰ô."+ƒp‹S—€F«zíæ›˜"RHŠˆRèÒ¨U¯%ÙÊ`VÁ2þF…P„[¢µbcc_¢o>f!IM¹uYn£Âuon£è!Џ@šòfVYÓ?Ÿ9‘53!Öij.ž5ªë¬°bsØžS"–QœŒÞq²§6L8sÖí¸ÈtÊÃWØçQÏPê”p›`üR£ #w’êw˜GD~CC)§aº5½SŠJ=àôÃ+5¬™ ­6·3ެ@_/ø­Ço>æbÀ-’çÔpZU·šª7·QדI”}®@O]äÍŒçÌ´úÌ|’ð#7ìÎ+ºwv ©!|Ùô,`£9"p’&¹4™-æ[û¡73ž3Ó±*%üG~߈/$w RN͈o½Sƒj ž3Ó±‚ð#é›9‘&™Ú€HœOÃmTǪºÕÔ½µŠ8BCÚ™üÂ2¡U Ï]:ŽáX×`‡‡Kµ4.ö|ÖqX£!ßÈ×ÜK®Mʯîxà$Ì.â¾£a("gÁt%jz—èB×Ìü ò„#}óéÛÙEª–†Û¨0ÔukYôÆ6*¢b©É>p×N±ÎLk-çÖìØùEÝ..­×Cxæ: 6›N'kiº½V:]êHíŒ1©_ÿ³ð„‚ëÌtú{à GÂSgГs€Hùì ÕÀeìˆo½KôNá:3=£TÝ\ ‘ð¤o?ŠbÄ-Ò´4¢Þåâ6*ÌÏKol£"и¡¢ªbMÆ×™ùfÝ.xàùOàãåÛŽ{DþúæwâuîO\hšµ'ÿÄñÚÊgÕÖ<ØÚ¦@¾#e`s¸‰tZ§&ñ¬¬mfgNAhØsÐ@FWÙXЄP“Z«¤WÕ+¼ÎLx*‘pD?â Nm¤9¤_/î(tBµáWKâ,1ï„»p–Œ@E@¾7šÆw©Ç´» Öøí—d†Ú‰yw‰D÷•2pÕɵЛڨˆ!4T§ÒäLj5:RÏn—2‘xìj4;Ë/®§Ë Côi–ížübHŒ5cÜ€äeôÈÚ_ñÄǘ‘ ¨…ëhZ ’B|Œ Î@r“c‡¤‚Ͷö¡Ö¥ ç§TT×Áùgd„‘ipéY“›Ý‡~̘<§&A’¥þ}â MÚH#3 /­}Þ/Ñ禺­ûˆˆ¡þÃ/› 1 s369 ĨIGï65 †èxGðQ#¨—(Æõ-üXPFÀûîhp ’:¹ÂŽ€øöÓÀ–l"å[ÅmTØëº• {QEyD©-À>7úåè¸)å7j`p:ÝpÍÎv(†LÔÎà*ÄÍÊ›WP&´4ϰ¢é|ó(MçñhÏÁ£°|ó(CÇ},⑃‚£•â8­—œÐDüÄxïuºPƒóWÈ„íÐÑ ˆC²D‹]š1N[¡­û´?ó„ªGZ‡†`öjÅTäCOB€*Tn¢êæJî„ÆwHâ,÷ÿxu‚œœ%#¡È÷FÕø.E¨˜Ñ+Vã·_’ÞX¢îüVùËÁmT'>^½©ŠBCõ);ØázË´¨];4v¢ÙÙîEpÕ¹´dDó`0h…ÖåëÈûjûHÀ«wÂÈ}ÑälÔ᜛?¿¶D$ê—D¦ôÇj Ðâ•uhÎ&ÚmyP\^ ÷_{õZxéã•òÒqûöîs\ä N¾¤CãÃ:ˆä5ºðo0"eD.º \Z‰uà)8&#ÀHøý‘HtÞžÚ‰3í#!øËÃmTçֈĺsïÒ͹GÜCDîpšï²iw¾Š–æuàŸÆÐT$!õ@kÆØ.á ,¿¨\Dé—•HPÈœ¬ÐDú:Ô´9˜M·VnÙëË*%9NÌ{YÄ…â¯Þ¾O˜¸É¤‘‰1Ñ € È{Ú!4“¡𝑖ä@QXñ>NœãÒÖ}dšP÷>ÒfœC•‡Óu*ò#) X»…U©4œ~Œ8ù÷2â‚vã†ë¢Vïˆè`~QoËÈÉÎD jÞ!ML$Í8R/» LCFu&&¾¼ÕFòºÜ¡©mA¤ÊÕ*ØÜFµ KÄœŒ8BndFJÁy**È™.—l™ÿX$4çMÏ€¥ëwÃcÿþž|ýÈi\{f:èŸOý÷[xéfÐi50kê(XDæñW¿ZŽý}Yž‹ùìBMyE+,«ô͇¡Ó3‡£÷3;<öò°tC̘4Ò—Ž\LOÏ_®Ú ¿öµð>ÖÞ}| ù€àçÐ̺n-Œ:íÝÁœ0b“Ç·­H©£S/ü/èM}pkz¯ÚŠêyj”ÆÎzæÞ²·l˜uÝ>õöP³ãtŒ#ÐM øÕ0þÅϾ{læÉ 65™‚œÐ/âÛÿ©—ß š˜xPi:ÏÈEcŽÿ÷˜ôΘôÖjõøÀ2j‚Ÿ$ÿí4çðÆ ^Î`Ú(µF}‡œÙ!‘¹ê|ž¸óÞÆ E§f†¼…É ×ià‰[.‚Ñ ƒ¿þn¾ï7Ì™6Zlu ¸^'dh1Êß^1[hMè<…³N³&äˆ&ôû‡“Ç icƒ59-ÝN dÖF‹g’ÉYËðóYaÞixZ$s{÷i™¶#¿Ã‰{Gäà´aE ËG;Gœ|4TåCmÅž ÒwÈYPqd=ì^ùHÈy’PQÐ5|m1lýúF¨«È…ôqWÁ¨é÷CÑÞÏÁZ{$,ÚŠÓ帷%Ÿg¢.{gTZ= ¿ïØóàu`+<<*­â&ž¯<+¾ )@©M蔨ïØÿÔà8V ƒn~\u+ä=þ›@³h/^—áÞ†]~ÿ>ƒgáwþ(Ë_Þ†H'>ÍmÔ‰1êÎÇ÷¬»QiÕ]"«äÖ‚ 5(þææ´ÔètÀ nÖÖÈŒÌKßbíöî#Ó»'K3‰3“™`Ñ‹ªøÔ`øo¾ß°³aPæµPvp z¸ìµPõ&nïÀð“î€ÔQÛQiã.‡œ•BéÁÀ’8ÆŸù4ÄõõUa÷Ї¡êèV ¼ÒÇ^‡³ß±3ÿ$âºlUÐoÈ\!ã¬_¯ƒíßÜ*žÓ±3@\ò8‘¾0ç8´óMG­5˜3ƒ”açb<íûrW?t~ìŒ2|Îc³Ã¡¯ÃÁ­ÿ>®ì¶¾ä;W^°Rh¢´\®¡ÉÓºïzûÍœ´•¯2Œ@k4~§‚¡o–.0þ…ÏáÈ›‹ eþ Oî•~€¢w_G<üO¡ùØKøÛ »~s!¨ôFxý=pò™ 8Pºä=(ùâ-‘íÀ›ÛÁ\ÐõOƒ¾s/ƒúýìW Ò íš;¡ï¹—#9ºVìû]p5èú@möf8úéëP¿×» ·1}˜Ð²X†GY¾÷"To^Æ´¡0è–‡ÁJó—ÓZG| ;—Þ…¤e›güY'ŸMŸ]-4!ãf?)Î{óšsž‚£û–@ñÞÏDuÇpþÊ£¾ò8Ê dß×°á“K¡ôÀw0fÆAkðz¤‘¶£/F‚ó_سöÏPUô“È{Ðø«±Ñ9v.û=ìÛø,Œ>ý!0Çnµ²R&ÃÀñ¿ ]1jgjËC×.µz>É0]Ši4Ýü[½ŠÞÿôA"?m&T®ùNÈRø¿BþóÇ}ÏÆë“Ï€C/>E‹ÿ-ˆŠ¡º¸¦‹K„Ô+o…ø‰§AñG¯BCÁ>(ûæ=q­lÉûpø…âØQQ”çÞ?Ý jƒçÖÜ,Γ6gÄCÿ]l<¼ú”ÿð)ØŽx5Cƒn{Ü u÷ÄoÀzx? BÓ²…˜Œ“¡®‘((no¼Þ^Õ€š÷ÊâŸÀVW,Ú—‚oˆÚj£è¢)6¦þì¿Ø†UCîš'¹Š‚‡Š MT‹ÈD yë ’·a!bé )}:Ô–íÆy_°Õ`£Q´Öc 3&@bÊ8²û]ü]…{>DMM˜â¼‹È’&%wíS°wÝÓP]²jʲÁ騇­BäárÔaãsò·¿ Õ‡Dc¢BÍgŸ§ (ÒFÏG9¾‚ý›ŸGBô9”ì÷vVú ÙZ j‘ˆ+Ü€û}¨­9¿UøhÄŽH•Þœ„ò}Üj>É0Ñ…@ÕO?BéWïBù²O¡aÄMšŠË ù¹¢ t®.g«8Ž›rTmY…&h‡ .{‹0EK<õ,_5hö•ûØ "?We9ÔíÙ!®Yì÷iaHãRµñGpâõÚ]›…I ¶(fôDa2väíçÁ*Ed?z4HpbFf ‚ãª>&L×LCGƒ¾_óE¿}BàAÒó ᤙPô¿çýOóq+´ÖFÙ±-±"™q»¬¢}¡Á³µQ”5 °‘µ@qÞ—ÜFµ‚u¤jÛ6*Ò$eyF Û¨BÍ "-´ÚÖ'Ø’·2RÛO˜»HÄ£.éŠíÓä8àD¶Ìý†žæý, ÃÄüÊC­1ˆM‡NjJ½ftžžä´S‘HMƒ£.ôžÄÿæ¤á¾cÿƒµïŸ'µÔ‘´‹Þš¤ŠÃkü£ð1#ÀD vû$vÕT¢Ö¤ua4¡?vÜTA<’N?Ç—Æ6Äw\»g;x¬ ¾ß­ øåï qú\üþ¨‘8¹è#…ÇÐ÷é/¢×ïo’‡NÄŸ†qÔ0äöâ:ýs××€iðp”úÎɃ˜1“`ðoE-ÒŸ >o—<Íû6Ge­õÖCù¡Û¸‹÷4·QíÂÓå™Ðt9ä|CF zðàW Á…ói(ü„æf-‰Gê(¯“Îqi;¨ÐÄl”¬† ÍG-J2œyÃÒy\64%Ú,¹âqáyÚõìEíO Á‰swïzMÓä3€ M ¨qF rðØl GfhŠÓ ¥K?„¢wþÙjÅÞÞ7 K†ƒ”‹-LÇjwn„~ó®‚ôªàª­ySC}mÓüw#A"s3ÒµLCFÂð?<…(ß±5ß´•¯5"Ž6Šã(¸±i;pÕ66Ýs…Mκw¾+#У¨CÓ®øþ“æÔhôTÕoZœáÓîffdbÖwðì€Ë¬ÖÀ`î öú£ Ñ™`ØäÛš¥%¢“2ò|4A›!ÜE÷æa-Á¹6F]"Ö³! ¹’6Çi–Voé‹óo.ƒ¥bú£èvqÚòæ#©ÍñF€ˆjl8ùmcQ+3E¸\&-IÕ¦Œæ\±ãO.˜MƒF‚!ÅkHa¥ƉóhÈd,yÎEÞd¸®\]^xlVAx CÜ„Sź5u¹;4GÂq¦QŒ75Ú-É1òá…[ý¾ÝÂe3¹m&“5Á#Ðpl˜ã1&Uhæ¹ ÃHOÑ«44߬Û%Ö¡J1ôÞ/.œ9ך‰‹ôzbùˆFàpÖ[0nÖ“bm—]Ëî…#9‹ÑCÌ-@“.i­ äé¬ìíÙâøDÿHspËK0lÊm‹yS£¹62ä¬ú#L<ï˜vñÿÄ)šSz`)ìYó8zV[']ü®0ysÙk`ë’›pN¾LŠ.×50xÂõï¯h¢‡ª’í8—ç)1ljF G!àFIéw¢å^ô<öì¸á,ámð­ÂÈGÿ%ÌÀÜõµp`á½b®K …¯Þº=›mq‹>D³±Záˆ`àMФt¿ü…A7=ÿøD8& OiÖü½pà™ûa0:ÿâ".y:ËÞ>_hvä}i.6!N™#6yþࢠrý2ù“÷"Prð{HÏø̾~#”Z›¿¸–Û¨±‹–h½ŠÐP¥ÄÇ˜à†‹Nƒ£5°ì§ø~cüjÞ)ÑR_,'#Ðe¬|Ó;ŸnHïi“ÁãvÀ·Ï’?Ťü’ýKqä+ÈŒ‹9Xýö,ÔÚÄŠß.G­Ø·ÌKœÄ›?û¥<û}›þÐå2¹eV<Îf×h½˜ ^Œš³˜;CÄ…Íí¡õeÈa΋¿+ñ¬"®É4Atý‡8’ŠfÔ¹ òïF Š S1Ÿ¹j_¶^>µ™ôûÿzW³ßG^ÿ[ãÄz2,#ÐŠÌøÇDÆÿ\ ÇíϳÁŽ@d„t\Ðæ½(µ•¾Í4|`¢ýøy€äÌÀB ’Ì´•ÞUãäiy½5"Ó2ÿ/rÀÍ?Wn£üшÞã^=‡æpišŸèµZp8]°ïH)|¶bL™“F„CGÁûK7ÁàÔ>pé™SÀjwÂë_¬C3\íóÀ_®ƒ«æÏ™'g …~IÞ‘è—o£A7^|† N”'…•[öBEMjˆN‡9SG‰kí‰ø#À0Œ#À0Œ#À´‰@DihTÈ.hëÌPäýï6ÁáÒcP^U/4$jµ÷ž§‰KLåøÙŠí`1éáò³¦‘ÌxüŸÏÖÀ¡â ð J²²¦nüùé0jPŸÈ ¨õ!"tí§B ÆŸ:v0¼úéj¨¬®5–­ª¶A¤›4ºÉ\§­ó¾LÃx@ðJœ;ë0ŠÍYÙMøoÁçÀ)@@Å€GaÚA ñïR;ÑøRðÿNÑq$„H”)p ³ ½§ŠBãß¹Öi5`GIgµF 3&Bó°ˆ·4÷Q?MÍd¨ªC%}äOÐx\‰¤D†ô~‰òPì÷)CóKïu1K'ÉA1š§uÒ°a¹Þùf#¤ö‰Z2ckë|³Œ;øÃîp¦‘¸ùgå»ÿy>ŽjŽk°\¸¢˜SÑÓùÎ1ˆj؂ީÇSï—ê8Üý®ñ!#À€ïq+î»ZÇߨã1 ùŒC­W\ŽV}íûp9óŽ%<îþÜFu ÐÖR÷¶6*beXŒz((­ö?¶c‹ÉWÌm>qÐ?s­FãûÙ'Þ;óšº*«òNjŽ1„ÙE,«ªócd"ƒÞ éMhn–†^ÔZ†ËÐt팉Ã᣶»ßþ\w.˜°¼­o™¶#¿iP¬¾W[v¾¨Oë´Ù+mJ 10úWç@NþQØ–[.t(Ð71æ>^\oë|¸ÊM8’'Êä®%†¤†þ±×=8ŸˆCÀ×P džÃy¹ÛÓ†¿²Ä5êvDœ°Ñ&ራJެAÙ%ÖÑV –—ˆÄûCïR\rò±CadåžHX@IDAT+ªe éEß~,ˆ7ù¢}$y¸ sôÆ6*bì`c?R“cÁlÔaçÿpX Í¼ÓÆm­r ø_'ó0ÚlèáŒ<—ù‡ñÃmõV‡ÐÖhQBahZ¸ïšs„;húmÔ{Ó]‚ÞÐh³á|IŠèz[çéZ8‘(½V .$¨ Î!"œ%æá¸çQø7n”̵iý¾“æœU}X71 Ï£é`uvMTNáC€ð#é›'Å÷÷²=ˆ”o•¯ÞIÆÔd±¡xÛ~n£|ÕòAom£"¦G+µÔÙ¦Ž÷„a©`6hqáÊíbJÈ5Û‹ÒüOÑõ´A£‚ôD#è‘`éõD¾´‚ÔHÌ{1D=µèrô‹0jÌh2šcí’/–âˆ]ÕNÛ…—Ò\ÛØSw¹7ÂÏãvT¬úü"4„/á,1÷-9?F '# ßñ­¢wÊãrT¬IŸíq©ùJÅn„ŸÛa¯¢o>æ!¾ÿ¸¤o•¯Þ©füŸ®Wýáæ =¯ p,¢vUÃÛÏ€Û¨Pj+¹·QCh¨êh=ò2Fo3:˜6¼”«…—5¹@­Š{gª—mFüj =V…žÔ è„@t!MøÎrýÞ‰N/5~I2cÇc{CCmݶu«ßªñôƒí¶ùÔ ü?{×EѶgvïÒJ€Ð{ •"vPDAQ?ý@?ë/Jb§ zšD@,ˆ$Ø»(bAT@z ½H ¡†ô\îv÷ÞM6\z»”»ÌÀfËÌNyfofžyßyG¸ "@¸~û·oíÂ…3dö „s]™õ¬`‰Dp@­#ßVÑojÿŽí¯%{7bkZmT%ª†p#ü¶®[û µùˆBoÿq®3mÕÚy ‚¾•"¾}…ÿÝ»«ú=–£ùV³lgÒÉXšÚT}T%*¯Ôç>ªxݧÊáXÚ[×Ïdo<Ûf£ëz²†Ê ¸½<=YÛæ Ù9X ÛqðcËsËøâKKGø’Ì™Ùqð$k棱àoæííż讄/I «œeçØ°¯†F  Q_O×EÀçS'–#‹Ž­®ÙÔ$øÖ=ìnÆîÕ.óúŽ›¸ÍuKYC9§Y/ê(NØBùÉ#ß_»|ñŸHšp%|ivY€ œ@ h«è·åôk×ñqÖvˆvñÕܤŠ6ª,\I2CdæP`G~h÷Žï©­Ç;z»sh«VÌ4_©¨JDjzê}ÊyÓHZðXã˰Yx9zèðÚœÌí¬[¯;DUV­_ò}¾¥KpTÛ Ž ¸œlëÅ‹)E ©@Ñ@Ûl6ëp__Ö³m0³Ûílç¡ìLr*»ë†ËôE÷"7ùКR3#ÉLS/…µ ôf¾Ø<ÔXžDl_™ðvtS3 ï«Q¤îËë:Õ ´i$@mI6›=ýþ›ŸÑ‘H,4ìŽTµ±æù“ÔØt^‡é#“šIfŽÚÿñÏ_}Jªf„'áJø*g¸N ¨ EÚªe_|üå­÷=db:?œì¨]s|Ô<ƒ,¤ W´f†ÔÌH2s(aÇ¿þ€6>·¢ ÊZm«H“šžv?×´1vÕªç?odAËx1ÉPTþÓ?{ù U°Ç…Öˆ±oo¼‹«¢*®¶‹>}T.&5AhŠ Ÿ™q6-ËÊ3³­°fvÉæ|.¡‘ …„Ïý|}™{¨„¶i‚=T8Ûw:ÍÿîÖûÃ\Þ¥5ëÚ¶)ó† U}w´Ï ™f&kfd€öÏlåcgM¼t ýýý˜¯Ÿ¯~M¸¾¹–Î.ª ª“¼}5ê;¤îP~c`Hh¨c£ý`LÔá?”Ôûºëÿ÷§òXƒ`óA­•i;ojÚÇ<8MæÕoG6üÉì%YŠ¡Eª¹Ö‚þxwão«þ2„%޳žy]týÆM”^ P Šm«0qðuß7ŸêÙçÊ1K:ÝØ:í˜ÖáÂ>Þ:íó´“Uýv´Ï ™f&kfd@ÉÉNÛüëÊ/·¯[Kfš©}"•ØZk«–Í6÷Çá$Ášwáó©Û5/X»ËgáÛߤ“w 3¢e:ÑG…¨& }°úqlÿÞ´'ÆN¨D]Ù£}\‘-Z§õ$Q°ÙlLQÖNUY L9>›ÊŽœ8£ÜIÀà‡z€Ÿwþ&•"só+Ì?§¦gaÓL«¾ÏŒD¦±‡ûHXƒä£˜ üY@"5À“p%| «›Q]hh]’Žý+¯žÜ½zQ·F}Õ;LݬÀT¤ER"1DfŒß›>€@hƒ´f)ž¯ìÜóò¶m:wíêíç×ÐÓÛ»Iö¾$>EWqXäã)g63òkS½.䨞ûÏøAÙí6kNÖ…”¬ÌcçŽØ·kï¶Íûž:Z(Ðâ¡kR73ÖÏäµPx"œ@@ PJl«0‘ fü±hcã½^W²À¦ÙMÚr»w»àí¯æxø_È–Ì4ÁPoÖ [­Î¥d¥§_8ºïÞý;·þ‹Â“ê+IŒk¥­2¤1ii©ÿC¼Ši·clçíÛèÓcÏBj”?`+±ÞÛöQ([åœè£JÅ­& }°ÆAƒ+ý8vpß*lòøßÇ’X§Öùc=³¹R™yjLóÍýi4†Ö}º”'¶{{g³FV+$8v†Ef 4*ŽÜ°˜—Zhwð4Ö¿¥2ÂK–ˆÌ@²¥cCªfX353’Ìè*gùÒ™¢Æ¨v<ÁÏ&žü:¯~Œúr Ýác(½ z§€ $M0È Õ«Atè¹7OtŒ»qìÃ5‘]Šƒ³ñ.]ÇÝ}¿¯i»–r ¾´ï_´>AÝ^B Œïœ°¢ƒ°¡aCÄÅPÛ ‘4ŸÎáNB H[äç!ëìýhC>2Ë®ÉÖŒÓ,'í4KÎQ÷­<‘ýÉÁ4û¤íÒmU±«3mU±Ò‡Â ãÐ¥12—çÝ:Ѷ%wxÖ!Dþe‘z‡Û÷Qù¥/ßE©÷òe·vBÕ¡¡’ƒ(Ð Àþײ%«[wètó—?¯÷áÑaRaëe4H§:9¼çÞ™éa¨™ege³ìÐL'ÍxÒáýÛßþ B§ÛNOÖ¿TÜšjÜh½ˆ>h©ÑÕÌ b–Kfr „†\}!4TV"*€F'4DjdÜFú5ˆN®?Á{ÉÑšR3#ÉÌÑý{¾\ùÍßÁט…¦:ƒ¶Kp¹ÛýHŒØd v€HŒqÐcè&üxàáJî§õÊù¾Ýäˆö!ê§È·Ì|XØ+#í“G½Æžr(áAÎÀÆ(ÐÙX¿ #¬þ‚ø#TMá½Í<ñø›bBlú¸„„2è÷×äØÔQ·ýzþ(žSûä¶mU9Q4ÚŸjk«j@SVQ²ÑÙíû¨²ÀÈó¯öz/g>êt°š"4T4  N8Ó¢ZCÍÅüû‹~;Ÿtêl߃þ;ûÓŸztl©õ mÏ{vl‘¿O ¤ÓÔ«H AF 53GÉL}"4ú,°É%|D`r×ϳîhŸ2ÍLÖÌÈ€j·¥mýû·¬ýýo U3ªªcÚøá‘pn†€ñ{4: ªs"0t1\ê×tvY÷҇ʒ#¥‰¾^Úl*–˜ÝýÅKÒ®û_VgæÊøÖél„qÏò‚‹“@@ à,t©Lâ‰Ð‡q¢GË@#uÅ’¤7Ñ—ÑïÚ$ܺu[e”¿”3aAÎh“èl´St6žS˜ ¹–Æ”•7*‡1‰$ê=·^ 3£~VïeU„+ù×4¡1$44€Îßä×ÒÎ ï:°}Ûáþƒ‡^£Øm×bÐíO ™¿·§ØÀ—{™ÍÔ q…ÉKáû"/¸Ñ"-Ž®ð=ùeÛlÚÅ” 6Í6\QìiÿîÛóûúËVbYˆ1ìÖSôÌÐÐF8÷FÀh©#$G”ãGåx­pÕ?ĪoþôªÔ¿*ƒ—Y›ú}Œ¼ã?QÊyerüÞ \\µ¸"ß:Ie´Ä“‘ÊD"³—Æ"ÊxhldØÒÓ‡uõƒÜ’¿I·o«ÊQqNk«ê€4¦¬âŠz¿„ÓêýR”îuu©©þrQCDL›t×I@zû$B6T[ÔììLå÷Åßþúûb¶¦ch¯Vm:wéìãèéãío2aƒá*Œí«aÍÊJÍJOK>z`ßž;·A$$‰!S+›oU¸â…šBÀè*“Ÿüwò:ËQxÊlg?¢ ó=Ëa º I ­ï(¼æƒÖ~ˆ£l ŒÅÿÆ:±V¦üß¡YÏ ËgœyÜùÌE*ºƒå3—ÜûJoÑ£(¡8F¹™A~™“úŠJ¸B€¤2,ñÄ&¨˜½„$uͨ¡g`pý\Ÿ%IõM2k(/î˜ Ic°æïCHc5U{mTAÕ2ZÃø߯!C&ª£™qǯÀ}ÊT]}TeˆG‰¨†Oy?¶9hȾaæ&ÿWIIM‰ñ €@@ P]ÀòÙ`åZ>£48矨º”å3šõÒ'•˜6œKü¹øè¨7ª ¯êŽWô'ÕpÕãR™ªcXR BS2â¹+#P}”S œÛ ±YPuØ…=ž®àšW®'‘w€@ÀÅÀ,èXˆhtËgT‚(W±|¦ë#3ö6²Ê%6Þ•ÉŒñ‰þÄ@¢îõµ2ªBaFîH*Igdï%IoaB€´.„« ÆÚ®iÅ®Á m+–ûÅûø–Ê*˜€.¨%ª»r:¡!œÂ£¢ã´bÑÖhÔ~¦™NÉGZZÎ}jj j‘¬@@ `Ì•,Ÿ‘ ÿ\³—ÚXÞŠõ?ÇP‡£âc¦®t—ºýIݪI!•q~}iŒó11Ö j²ªBC0ÒÂΓ¶“ObÓÍg`s«%:ZFQOk\KD¢´©£p€@ Î! abù:Ÿø°@édeNå&e]Æ£[Ï)­2êJf1õí xmjŒ‰#,U`'о¾ÙÂÜb®«­™)¦¢?)JÕ&$#Ñﺣ¿t °^ô3RS$“²#øŠ#›š_yÒx&ÎåC Øt0 ­ÇÆæM¥ýÁWЬiÎP§³]vê_[ÿÓ9šõN àÔFUm„Æñ1S§÷VTåpìYar¹bðŠÐ*! M•à/ õ aù¬¾Õ¸û•WHeÊ_§BS~¬DH@m" Mm¢/Òê<¡³º'ŒÝí˜QaùÌ qíJlrS•4¦õ4òÍÏÀFK‘½—$½Å9‡V¥p+f›®Vìjxi–Ê„4F|'ºƒ€ 4u§.DN:†@—Ù;†sÙ;¾§n À1{K_“æ3MƒiV}=ƹtÏ Ê÷Žaĵ@ ® ¤2e×Dy¤1h¾’¸<_¬)OB P“BS“h‹´—A w\¢OFÚùݲÌHx®Ç_…3.,ŸFDÜ×U„T¦ôšÒ˜Òñ¾W@@W¨%‘G€@ Æè2s×+Œkm÷ïùpI‰ Ëg%!#ž×„T¦äZÒ˜’±>WD@W¬5‘g€@ Z›¹·•ç¬7y˽Ɔ&•–Y>SÛz,sDC\×&B*SýòJc˜I~wè8ÛÖ¢1ˆ'@]G@šº^C"@"Ðõõƒ™ªÍòõïÙ;;{ws»]}ˆkÚÃØ{FÓ$þ©b–¾÷Èamv]î˜1aùÌ q]TT*3ë=¯6ª,['>šQª²6ÊâŒ4…4Æ(Š8®€ 4®QO"—@ Ð;N3g¤íÜÊ9ûZÓøuà0½`Òök™ñOÆ÷ØHªh9+Œˆ¸¯n**•1òCÒ«jà À,qéãF)Ÿ~®xÒW¬5‘g€ó„ÆyXŠ˜7@ ta‚GˆÐÇ¢t™½s$SÙ.›þ£©ö Úü²w|Ã×Âò™„8W'ºT&û|”¦ª“‘Ž)?-Î×xjldØÒÓ‡óŸs1}|¯¦j\6ÿ¢*9¿›LÞý&=–™XLÐ:ûHHcêlÕˆŒ jAhjr‘ @@ à*X°jæ«Ù»^Ãná×ÊÞò]Ý›t?»ãèÎS~ܳûæñ]ÎQ9(LÂ7Œ3‚+F¹„å3 q®*+•))/ÓHÏb…ØM®¢z¦Kc5yõO¯Âå‚ʨ°TVq/ps¡qó Å*‡@ØÌ$ß~ök¼}±Y#¿Ç´]v×™;@í•}®§X¯|ë@ÀEkö—XK³°°ÄFX>«îâ­’¨ŒTfúÓ€ þ³vĈoò wá4Í"M‹y,I êªê™Æ®5q/8" ¤Â @aöij•8û ¦›$2Cþ 3w‚¼üH×d %'ë/ε …É ùh_‰°ÏÒ59MÓ€%´ÈÜ;ñW P1H*“•ynȚâM]Å ª\âÏôY’4°$3MSÇJYô|i©qnQÍ#U6ã}ŸÒÂÖ´Ic–½&}œ––šˆßÐ[šÆº;æ¤1’ÄGûø6:I /Éìr·Y»ÛéuÇwŵ@@ à> ûÔ¥(‰@@ PÍt™µãd6ÝÊl¬¹¦)Ÿj\·o|è·”l—÷÷úËÉʵ…Í9 ËgÕ\)n}e¤2ŽÌŠóoœÃÒ7cíÿ-S"rö:ú¾ÖUÏTÖyJ„úDa¿š¼'iLzFÚC 0… ŒžÎҸƿ*ï¾1¤ Õѵ˜VöŽ»ª&Ë"Òj!¡©œE*€‹#@ИƭªÍÞOeö×î6È IkxrÎ:ES.¦OßëŸÄ,ò¯ôƒ3li£~ºò5óe…É{@a**•™óa`à´xÓ ŽñŒH;Ç$iœÆm.\8\vô+|=ùñßò WøyMÝ;JcTU{³0™Ñ¥1\Š(KS8¿_ÏN ñêß0ÅîÓõõ== û‹{€@ÀõׯCQ€@ €¥³H0’1Œñ“’,ßµç¹î§(ÙˆH̤t¦ÅKônš¡©>lëDÌc#cÚ`%èµóc£>¯ªc™k™Ö9GU{.x%jQËšÈNC ë¬á 3ƒ¤À‘ëžo•EÙ#sÎ\cӰǻDžþRZ–…å³ÒÐ~U±`öÎÂ&~)ÉçÖI’4Ž[ Ä8ÿÄÌ=æqÕœfåéÿpÍ<´,Õ3#/Õy†ÑŒkSÃAÀJ¶TƤ8/߆_ {6½¬¼€ñ®³H­Œ‘$’ưç!•…ý¢~7Þ }=¡!Ö íöõoÔ~sDˆNô ?q\K¶ë]»µ–ûðȘG4®  @§—9Ç%®-™3UW1©ÎŒ…GÅ|…E¡[ãb"g””ŽK—3¦Ì‚¿ 4Å€¤(ÚÕТ³‚Ѓxt ÌêÆãŽÝT³nÎYÕnç¦ëv?ÓýÀ¥Å_Ý2>g/qh+”ehÉ07ÛŸg~¡,þ ñ´>!pI*c/²¯Œí+³ûÊ@Äb¸×¶ôÎN95dòãöïa|ŸccGœM3=­ªÊìŽAÝûÿ›ñoC»-kŒM³þ mÉè¥2fûªgוfõÌHÃÙgC²5kк‰ŸÖÆ0þ%“åù¹‹û1ÇÎ F{E©Ç”¡{Ƈý`xvŸ½ç:Î4ïf}z>úû@nï>;a=T@÷ŽKìb—„çC/tµsuFz2õÙ@@ àúˆ54U­C‰uEÓ 3´dÞu㬇ªñe£¢¢_­jÔe½.ìGtd–®.ûGXbúDDEO«Ëyy8"@æœAfãY˜—É¿ÿžrã}aùÌ@Bœ¨èZz7çâ™ö°xöáôÒŠ ¼[ñEFØÓ8_w0yÏKÍHš®¾Ô1°{;Æ¥o1à¿Ì窃Éß 3Â×Ä™¤1d©Lµ¥¤µ1H³™OÛ‚>4Â㛨F”d©ÌÈ«zTyýì—d¹Ìx¦I ö£áV"3ô Ó­¸ÿ=#ýÜh# 9“㙦D8>×€ë# SꟌ‹Žü$>6êÕø˜¨›1‹ ýå‰c§Mkä”èKˆ$.6êËùÑSþ*ÁÛ5ÛØ­è˜‚\#³"—ÆÈœ3Ôd~nÖ»ÇÐmϵ»X&½ã5 ½}¬¯ µÀÂ’c˜¡Ô¹h#âòŸiZôò™òòïÅE½A€¤2›†5{ESí`üÒbuÎ×x€0÷Yrú-CúR”I£¦ìÁ³ÓjYŠš½fúù^#L``£IˆoøŒ÷Í}èÙˆ 9‘£”ϰqf?ÉìÙaJ„’/Ù0Þqö™¤1ËgJO/ÁwCó'T™º´ &Ic8ç&ÓC'j½‡LTâo™p:£¬|ty{oˆÊø}xÞÚ7Âs.ïDü—y뀧ñ f×?4ô~ãžÎ{Æwÿ¶9…qGTĵ@Àõp{•3møpy³m}[n³‡p®z Lõð2 ýæÄ…ê«>õ ÄýŠ’£vÀùü¨¨˜÷˜ÄAµ© SÙSÐMK‰™zÅS–·²m©30e4Ò–@³­’‰Mˆ³DmzÂ2½£Í¦,÷2›¾m™tÂÈ똩Ӯƒð5&ùÀVÍ:*2fÎÉšÄgp•= bxIp@znã­›ô‰ˆ°Ñóð¨Ø Tv‰|ŒîGGER4N =; ÜG0„|wAtÔ<ò#WRäú^ú;:&¦…š­¡®øF’º '(€@Þ ðÜÒìßæ(M 4E{ý«Y»Â »Jê.FDdù,kãšNÐó¿>YÝòÙµƒ'Ú¶aÄٽȓÊ|äHdh_&±É½'ÍE\¸é.í3-ŽS%yŸ¬x TµìϦÅIwH¬ÁP=KêY„b·C½,´ãåÉ#³×Õq6ÖÆ@SìÚ|í›%&ÅK¼ñç¹F­P6Lv%Xål¢dæûì6þ' ¼L¿I2ÒÑeÖÎÿ*YÔw¬¦H<¼6\ÌɺŒÖÎ8þþ0Ã0q[SÙIá„\3‡®_œÜh–¦ÍÚØx{ÓÅ›²Ö$k6ëA•)(š¶Ú®ªÿdfæœßt{ð~ÌŒÍB§ÒµÊ=˜âT™W^Ç¡5gŠfÁLѬ¯yLò⺸ßjO]rÓ£ef©?º¯ª][K ÕçY&D––m·?ä˜?ESÃ;»â-™ Í1Ê—­Þq aÑ$y„Ùäʘô7zÄWß×ɌƦp.½äaòè‚Ns•ªª«ž²ÌÐU&ˆ63jïÀïSÙdêlêWd}@n8¤ÅÔLJ)²C:G@¶~xÂòZ3#MÌ"Ÿ&i˜ÙÌcÝfS[¦úù›ä é( Ö®ðE¾fß¾ž¦ƒ¸dÚˆ™¼kÇX¦·§0‹&ÌŒ¤rJ'ÏÝHÏrzʦ“,£'Af¢1ëý`W â@v"uÒcGŠ«ƒ|o\<Ûdæäq™ 3ŽÈˆëŠ"°ó‰°d¬’ùßwg|—/8¦(®·K&ÿáø®Ò=H¯Ù¯~×Y¹1åʼ]™¹ßÀû5²wÀyðb εþ*»¸ df`žêÙÚCÉ{&] _=W‘ÆÜ6QëS–4†¤š0Â1—ÖÊÎqÂs¡ÛöŒï¹*á™ÐcèSwžÙºû6# $2¯kv>Á¸ßðt§Tô/.ÏèŒ÷?Ýó\˜NzŸ‹k€@Àup; ͦaÁwmÚ¸g6v}@\RÕ`Ñ ˆqœ©Ïoº½é—ÌÃk|ŸïŽž*)|iÏ1Ãæ?ÆÆÞÀÎÔž`2Q¨¼o.ÿ= l<ƒ4{ûé§­ôŒ$-vU¹Þd–z½k‰Ü‘¹&ǦҢÐGq¼ü=…ótò?s¦oÊEë1ÃuÝqšö ô¤ß\=‹?u7&áA#,$"Q`͉‹™ò-=ƒŸÿˆ<⤧ƒg¸,Ý÷JäRã½bÏ0—#ûšîwòädòGþ¢SR²ŸQU[/Ü&Aêr5ÈÑÉdê0ß2ù_ £jÚšð¨èó,c! ÍZß°&¿Ï‘¿6c,1ýé>d~g¡bö Ý;º§¦Mkb„.ðwÇç(^~üÏY^oˆ0- ;Bñ# ·Û›+]-šÖHÓìK¡ªö@ï%I0mYÇÙ¬ùâøÆüW Ž‹! /0C„º#˜œÂª“¬ z½Ôk?ÙmìĽ´ãa† ¿Céj9²4‰I²Z`Q%˜J6Iº³2».¾GßÃNÿî¼ÇtškÖäD‡ûÓË@»Ã}±—ˆ·@ZEqî M—¤¹†òCî UòÜš{Yìߟ±Þæ"/'í1wCêtÿ=»ü¹zû~S’2ý*”v8ØÏô¦“í`"À3_Gœžk²f…¤ Úg¹XÒ³Âu@ÏÈt…Ù @ááScïŽü"×Gü”Ðwü¾œµó3LTpÙGº&al÷üiñn3wÞ†ßï’Âà£\¸÷ùÓŒß1Y>[6Sz+oSj8?‹P»aíiZùS!ë*‘Ê8–*Èü‚t¦SåMQ>z5.(ì…ˆäÇ0‘£2NÇÆóÓvO¯ã0Çœï5é±si¸¡ÃiΙҘ®³þkêu&m¼®ž‰\&Œ Mï:sçâ‹9Ù´ˆ¾Ÿ÷3Rw½ÝíÍÝJ²$øÍ®t™¹ë#ÆuãS©°yñÝI× õŒ]ß]<’öH@åÉÌ%LL|ºù¶æ×]zT=WXÿfÖZ†[¦XÃõXIÓò×c}Íû¸ñdTt;ƒë‘ÁŠËÑÛ/M<…qTô‡opôW4–/y™g™˜„™«‹˜Ä †:Ù*ÇãèßsÆ5°„uÖX–ØYÇ´è:_f¦9»ä|ú®A™ýOÙc±‘[È“×êjcœ-V˜z+F|7i2Ó ‘>”ç Ö¨¬EºxXm͹sñWxW\tÔ$©©Z|„eZ÷âCЧ’P²”ñ-ï»o\ÏÿÐàŒB^õúqo¬øÜz fÌõdÞ7CŠÛ‘Ôbc*bùŒi1Âò™#B®w­Keno]гR×ÊäJÛÕòÖÂü¤ò¨Ru˜ÜùGÊÎÔ¥øE}«öÄÙÒ˜î3wŽè:wO#Ðv3ú‡[m™|+TÍH½XwÜÇ¡Ÿ §’ªh’ö1³)£r}‹ÿkòàïï#ž õ —'4[nk:ÜûœUihd=`@à‹½wtÑÕ¦œoáxnêÙ~Ü¿1›úZøÔW{’Å3¬eyS?ÙÄó;®¸—§üŽg¡P)_=/6 3qE]Þlïû´H?üÅØáqqæˆc0K¥E8†F\3€×HX{ô Ë;~¤²5êÅØ!c#cÚ8†sÆu SÔ¯(ã&›]› +dHâõZ§ÑSciS3Ýar-¤6Â_}µñlÎóÏgȬ…1€ðì{‹vÏà`ç;H] ž£uä>¦ÕFx¨¤½…8%ËkTn”ÿ6¬=ƒpoVÑòÖr±à•¨E0ÚðžfS6¥^"ÈÞò£ûƇM"ýò$2“¬$/Æo@iïéÕ†~Þ1¡ãÍãHqЬƒ#Ëgxþ+½‹‰ðÝòÙet/œk!'•Ù„þ„DõºÄßA&¨žî³$i`ØRl’™ç†c½KÔôxs_ãe&ý† }‚*Ï ó%¨žý"­sŽïW嚤1KgJŸ”´o ÒÛ\‘}cŒ¼@å8“naDæK´Û™²&„ªv\×™»f“¹e}ï+¡³véXÀÌXÝ|¤8ãFœd€ÖÝ÷â,ÔO\šÐx cÌ7Ψ†ªk™¡&SGTmŽ$žæ€»0l±aï/«-5 däy¬ý¸o¾%ê#a¨Àz¤9C±ßÀûÆóâÎæ)Øogªº’?sQSX ¤#Ï9†Ô«óLPƒ¹èCçØìÎk¶¬³X§2_‘䆎áœq­~òžr²êcYª–³ÖæÆñË~òb Þ³tÛ1DÈ'kø0!Ñ:K2×Pøf¦æ«!M ±[ÿ ùªXS4³™ñ0=‡;›‚ò“Êà2a¨p'§µ ž€:I±Û“ŬŸQIâ\. ©Œ8Y½8¿ßƒ°¨ôØò§;å/ò·1Û`,pÆYX>3pÝse¤2ÔÆKœï„ÊóâØxé£ôþA8ôÙeZ =ÚÇ zÕ3}òçõ÷N“>….­ý…pe¹ñ^eÏ…¥1LÕÒJØ7¦,Ke><âE ò çýN¦IS¼s+Ú¯v¦´a Í—c±d£#9Yioh[̳çIiv=Ýóný3×8@áèĽ@@ ÈG oICþ½K]Àìòx씌ºófJÓ™·_Ë>ßÎ8;?•ÜIªÀN¥5ÈWêbBˇ^),ÙgŽåù ¥Eõ¤eFˆæ¡XiO˜ÒÂ9Ã/·ŒÉÍ|åód< pœc¦Oò—$ûk“&UIï›L/Ù¦œ5¤:…Ó÷š@€ÖÓØ³”í=¼»9’™®sv\Åìì[f2_³÷¹nÿ—X>ëjX>#4Òë™waù¬8°êÐ3’Ê0U!õçžF¶H*Ñr¹ö•!kf*Ë&“ö§¹_ÀÈɤ$cŸ™¿a\b"-ô§8AxÞÁ÷à‰¯b1¬tÎ oî”ðߤ=iŒ4+z&iŒÆÕ®jà #¢"ûÆ%Ÿ‰Ì@ ø¨ ßý|èZ#d×Y;ÿ‚9óñ{Ÿ [×mÎî+TE™¿o|Ï~äßeVŒÞ¨oár.0|œyôÚ÷X×4zŽõ¡c ýÌWM3âg€@@ ` àÒ„VÍva¦³È,Q¸*Ÿ%þxß%§K•ŠT9 @@ àvôžµ¯q†f]Ó±½ÂÑúH”¡) ÝÊ{Çj™–5jHëF<×}¡ªFá1È ‰ã2H7eºÇ,þç0ð ] W· ©LVÖ¹©P#ÙK†v8_㩱‘Žêeeåü·ß˜þ>ðG ¤Ó#L’é>»¢ Åäš}J¸ŠgŒ½³°‰ßÅ‹çv ãN4™=ø¨µÈae¥Aþ$Qìé¡ÿ$•änEÞá, dìK&Ë󇎳m-â_Ž]fï’ô…™kwŽÛK¯Àœò*™ÉÓ&„ꪕX_¶Iâò(]Õ þ½^ßתXA ù`òcAvæ÷ŽÓÌé©;Ü7!ìÃr$+‚õ—%4ÛïnÑ2ÇjËWQªŽúCGòmŸŸÎ ¯Ž¸Eœ€{#€]Ë?A{FÒØ? gÿÃbç2—J×c;•¼ç¼AX5€l0nð§F¨ãœ°|ö$ÖP¼m R),ŸhÔsU¥2%•‚ÖÊ@z/1¾PãZ¯)á—ŒÞ$gRø¤•‘Ê”Gµç8™5þâ– §‹HÒKÊoIÏaüâP¥Ž5yËWCÍ, æG¬ãŒß3¡çRz÷á N—C-3_ ™ÖuŸ•p¯™™6ì˜ÐõHIq‹ç€@À—%4‡5½:¾?8ÆéלéûÓéöNWD(¸=ÃjòŽc»ž‘é{êkÚ™<9ªŸº šÂ`&\²ìßãã ,}MšŸ7‹) Lepéž!”ï ‡÷5‹€3¥2%å|Æû>!Š’õ9Öö€„&˜ÖÚ”¶´ç5!)-}ì3Ÿî}²|,ÆÃ ÿ¢½ãz,¢wH5SÉTöy°àÎ;&4«2*-ÂO po\–ÐlÖô)Ì^’¾mu:{ߥg ˜®ÎÄDÜ@ýA Ûì]cãÀ“ÌÿWÒœ¤‚”¹qÍJHq2 5&fºvðDÛ¶úƒTÝ*iuIeŠ+åÂ…Ãåãé?…Œ™Uam„å³Lתš^ÚÚgJcŠË¿ñ kgÞÂ7Ü뉎ÃàËÆÝãz~føu{}çL^ÚïÆ~4ÆsqAÀe ÍÆÛ›MÄ¢Èê°pV?Þ¢•GŸøÍ—v,+à+n@Űhšôåë;§1•Áú;°žæhi1Ð ;ÌçnÀô|G RsÜÃçï g3N—öžðs.5!•©jŽË#Áô—å¸Ê®©Lé›ÿjö®o@júÀ*[ $4 *xG ”„€+š'AhòõËK*`Ÿ M¯ —ЭŸe*Ÿ”pì[ó@aSÏ—B¼r¶å³pKœÉv¶3óFŠÊA™©ÚÓ0•ec"CmjKgXžÊÁ…ʰ@¨¤Ã; –ªR¼˜Ï ÚØ¶`NÝó®&¥2•A°.IcJÊ?™r>u>ýcXm{‡Œb”N<•AÀe ÍæÛ›ƒºÆâʺ¼ïœC}–žÑgEËûŽ'JB‹ ¿¤åðÿÆõœlÉÛ€³¤°…ŸWÆòYxllsɪõWTÞkq:ƒHÑÑUU5§ì;%I<íäI¢a•ê_Ľ[‚5 ·ArmIQ:¶ –;´jÊ:´ f­š6dfSÅ«àRÒÙ‰3Éúqää¶ïèi%'Ç’Ã1ÙÃÖ |ØBùÂby4»‚Q×Xðº(•)¯4ÆÌäùÂ`D}*"!€@ !Pý=\µ–ˆ ÈÞ¨Ž$ÀôRüäß1–TfôS_í¥©ösÈ m|Wç È̽y³‹‚ДY»"€@ n!àÓ÷ú'aù¬³¦òGs®à{rnþZQ5àï£ôëÞNîÚŽµiÞ8ORR³yoØÀÑÖ©•ž0TÛä#‰gٞÉò†„CW½ví)–8{TTt7yÏŠ·ŒG»YwIe²2Ï}„ê{¹‚¼*£¶¤2†4F!h?4/#Oƹ¨4« „õ—–Ðl¨©oN2;Ýð¦Î®;.IÑ}–$½Xžx#¢¢ßP¹´¼Ve´@IDATæ17K¦gÞ‰~!Þ[¸p¡¼zǾéЙ¿3”-1{jÃV»!¹sôÔØ˜I} $¨ ßzBÿ<‘™øS\eAš¢õ…¶‡76{ qG…˜:.K´xå¼ á²%‰M˜ÿJ”¾EIñĽ4ekÄÔØ÷‘AH7 Ò£¯âc"£#^Œ¦)Œ¤N™È×y“™E¼k‰Zs/ºÂ|¾ÈãݼŒ1b„BåN ¨[Œñ©Û7—bò Ìí×_ƺ¶ ÑUÅêVN ææàñÓìÏ­ûÙ†]‡¡’Æ’5U3G•sgÌ𿘖ƒ=Zä`ÆÕ`ð:o™kVÄke’fÅ&ŽÙš¬ZÍvíüõa]Ž•Ô¦Õ%©ŒÆTî[o õ—&4TuØæ~èiîÔjĆšA¬g¯OËÞ)9<.ά?»›šôàö3Ø™ua™Hùѹ÷ÄEGÝ?æåYMT›u¯ÉÔ:˜5ÉI´í߃…³—ùdMÌH´Ç.• Š1?:raሃ úÌÏ’=)-Ñûv‹jò5Ö2Y[US~Ö¹ÝîÝL.)=ýcoò–|6Û<²d[¦²ÎÓäqãÛ–I'ˆ€aä³ùšOáÂ_}µK³o“Ì|˜Ê<“˜-›v§ž¶ fêjòN ¨;„O½ŸkÚÇ‚üùðA}eCRwrXvNNB-íë•´ýG“Ðñù˜ly¢´5)dÜ€[µ~P«ë‰É•Û{¬4AÛïQvj¹!ÐÆÚ±®ç¸¢i{a¸à&n‚myhg|†§-ë“Ú–ÊÒ˜’ö)*)oÉE8€@@ àþ¸¸Êc}–œþbãíM!…ÐuJuqž…ö¾^Ÿ&•o×âãçoÃ.Èëâ-™c¦OÿΞ¡l…dfrÞLàõXZ»)¯£>ƒu6P¯ÈðKRR›ƒ°œÍµ4‰…OÙ¦2-ØÈ?È„g Óµ/X^hgìõŒ¡͇ºÊ|Ùa-úeûþk¸ó©¥Ä÷Jä/çø™3}mLÙ˜c³]‡Û/tŒ³–i„QÅbÕK>Θivþ#Ì¸Þ Ah ÄY Pˆ˜3j\óº¶kÎÃïÀ¼=Ë=ž¯¹¿”…ÁAìùoå߬ú‡ýòÏîÑS§‘—h#ÚÒ %S½„e¨,IWc7ù¦˜‚ÑIkÑ4HkÑ$H ðõf~¾^0nàÅô³7óô01›]Ñ;ŒØa~ÍnWYjF&;›œf:s!µÝ¹ä´¶IÉ©ƒ­V¬í¸úóÐGÕfé'¥Ôc¬Eú Ö(ëÜOMöSÒáêy9JcTEéFå§r$&UcüËKkcHh.¶2ðg€@@ ` àò„† ÂCZFh‰'°‚v‡Q°Êœ1ógE2¢÷’¤Êû¾¦©ÿ‡ôéØmÏÿ`¬ùª]ã¼:«˜ª¾9êÅØÝ˜쌮êÈ<ËÄ$HuγãgšŽŠ….ú,Ýåé+üä9¯µXˆÌä:O'–†Þ÷L•xoÙpQIñk™xšMkAŒ§Éô€îM² ,H¥ÿ&ù.ּѡ¹ 8ÆûÅ1úYª2uv÷ÞJªm$}¡u8ˈ”³øx¸Ö :ãºD†ôϵ,õ”S_|«2éòÕÎH úøˆŸ}<62fÎ;±QG-MJôšáÿ )FqJGà ËôŽŠÝ>„sé¶SöÄ .žX—Áü½=Õ&Aþ´ÿ £ý_‚qøùè¨Péû½È (]K°Ö‘£K-.¦e±Ôô sjFv¤!P›R®éÕI"+bîä@ZXh‡–Z£¾r¿íYÇVMBÍ;¨°HéÇ}·æHް?·íïqìÔù¹™öŒÙ0œò¾—åÍÈÈ é| iLÍ×¥HQ ¨¸Ï4ŸCÝAJÃ7 k>õ!•¸ ÷ùëS(ÔÊ !•*i1ôY’xÌáõr_ÒÞ37=:?zòVÇ— ù“ ²Ê–Ú¹•ž‚ôLX—rëc&c}Ìï'm} ðû!™ô.òô9é¿DF #ÿ0ä“™NÝ‘>¹=]ùᯅ Y"âjÄMæ«BØ•‰%Å3zê´kU‡x1Ï §±MP9ÛùI¸åÕÖÌnûÖÕ.ÀÇBögxTì£HÄÏ[Cj3 ›÷õwÅ€@ Xôµéʨ)Äï¶jÖ¸Ú£C ©{ûú0žæbß]§“Ù[ö²¿¶ÀRGͪ2>Ãd œ=Ï26·m-¦$Q•ô‡±(ÞúÚÇ`h— ­qô×€@@ PQÜ’Ð!axÛf6kNs;W< «‘l’ÿmõÍ ,þ¯>G¤ x÷Aí–¹1SPJP_¸ kn^»Øs›pê{èèæ·ïßÏ|øµñ‘‘§JÊ•®ÆÂÎûŽ4î<¡šUVG±<’Ÿ»yéæžã%ï†4“iÿÅÄS±kc qg⿼eBÙV4ÝEQ"€@@ P=¸ÊYið„~óoüé¨1÷ò˿·©fY××M#gØïF'w,’T*° HO|¼iÕŽ}7`Æ.€Ùš”ªâ•·»v66£3ÊQf<%‘ŠÀ‘Ìнa®…Š"ðDdL+gÓa:ë>Iæ¼_h{éæþ¡,V·Š†OÜ &AlÔÝØÍ‰çø÷¿nn¸ïè©w~Ù±ÿQ‹å±±ý|?¼š¤1ÂR™;Õ¸(‹@@ àJÔ MmUȘ©Ó®S4uÔ½°ã;‡c} Ÿùý‹Y²'³gMŸé/˜Äîùz¾%rWEòn™ÕØñT$MV P_ÀFµã·7õí&ìÛ5ÀZáê'°Ìƾ\þ·š•ÍÃ<ó6Ð.vp˜¤Ú$¤1€ˆK€@@ PBSàŠ¨×G€¬Z3µx¨‹ÞÕ¹M3í‘Û¯áý]¿`¢UF€,£}´x-K8œÈÚyüÃzy.Kå’ö…‰Éqƒ'Ú¶U9@@ ” AhÊ“$ÔGHÊ ƒßÁlzûnì-‘‰aáŽÀš[üûf¶üï°X§ýÔÜÔõÝ¥c q-ÕŠ€ 4Õ ¯ˆ\ pUÂ#c®Åº²•Mú™Ÿ>ÈÔ´Q€«Eä»X»u?ûbù:²Ô½,Ä|Í]Ž›#×@ò" €@@ P¯¨¶MËê5ª¢ð€K#ÓëWc?úUMú{Žhˆìn{¿¸tåÔÑÌ·iÞˆ5jàÇ·í?Ö9];Ñdó¿.­£YÙn‡€ 4nW¥¢@@Uc‰éƒ«‚xÌH´á¢pò ЪiC˜‹dlÿÑS}{_Óá-kÝQž÷D€@@ ¨Bå¬jø‰·7B€Ì«ó û¾Æ~Æ?4Ôà'ÈŒUoeÎg?+ûŸNóöâ]ߌŒ<]c ‹„ªŒm§ðôËoû3tO[Ž{æj’Ýlã²$e7µ6ËÊÛ>¡Êéˆç" sñ± .Œ@DTL¼Æøã“GÞÆ[7käÂ%Y¯MΧ¤³ç-RU•Íz¦6ó"Ò.ˆ€Åò›)‰ýÝU±i—ƒ­ô`šÖbµÖØ$· ì;4@ùbóëRÆF\“8OÇ– ç°™\"×xv–;„ÍT÷s.ïõòÚ2çùç«uãî‚%w!PÊV$꣣¢(ÿõ–«BùnìS .JZ-À@ƒ¡€lÙWywòääjIDDZ.r%¯Ê½0½~güf]ô ©‹à«6jà+7ð÷åþ>ÌËÃyzš™‡I¦Í¦ó›]a96;³áÈÈÎad²;=˪KNS’S3dÄ©§@vìÈÔ6Œ®–Éšù«y1“ö”+“"@@ P%¡©|âe€@À°X>ôJR0°iû⨻$3 ª‚À±SçÙ´–0I’Š‹Žü¬*q‰w+‡ÀS–·¬¶Ô—!‰ WUÍF>”nmCäN­›²ÁA¬i£ ¤¦r‘;¼…¸Ùù”4v<)Ç9¶ÿØiõðɳÚh\’äßA‹žŸ=y«Ã+ù—OYf´Ì¶çtå\ fš‰O$> -B¬,‡©Ì†Z6®Oq™ŸàªùDsS“ãBõ-Bq!ЄF|@½G bjÌh JÞ}æþÁ¬[»z‡À9Œ›ó•-3ËúY|LÔHçÄ(bqDÀbÑÀF¾1¥œ¯jÙ2gĈŠá?&jF¬~Y RÑòÚË:K×_Ñ…µ$£ 5ä² Åù'á0[µ>ÁD zjwúIÞ2íÙ7¶÷ ¾zCµ­;$;>…³d6É*=S•’Ÿ‚a¸’¶eÛ)Òf‰ÉkK"Lßw÷E@÷­[Q2€@ Ð,êè§ímä×ñåÑwW}º¶iŠ õÙŸý¬wçrV¶lc-Ó:ç¨jϯD-ªlâ=G@b—7 ô¯3dæÔÙ‹l˾£lè5aºÉت¢{ú|*¶ÚÐt=ýÊÄåìüT&ÎxçR9zWgÄXzwf˜1—dΖ—²~úFL}µWj†ýôËmú„¶çîïUX¥ª~ã¤R7 `Ý~ Ø·ÿjņ ƒÇO5´¾ƒzu/Ö×8 dMBÀí -ð¾Àô ,ŽW4[ÈL®ƒÜÿõ?°0¢ÿãì¢56^úš3sì”pëþ¼:¡xdi½’D›i—¸¶d~ÌÔ_+Q%‡GÅ|r¶5.&rFI¯Û¸t9l¥Ì‚¿ 4Å€¤(ÚÕ°­I³ƒ‚Ѓ»?"sÍ'm'ú×hQ¿ÿu3ƒN½ž&-nÓ¼1¸kŽÂ'Î\`KÖle·\Õƒ™äªó¬%lÅÀZa÷ÜX©2––ŸÈwé‹”_ ÿ n˜‹!‘§1Ó>få±gW]ÿ¿gÇ–•Ê[E^2Ê1äjçÅÒÒ¦rýü÷NE’¥ó_‰ü½´°õÑoÔÔØû˜¦|äïçm ¿{ÇùúC”™,»{èV‰Úœ•ëw=‹5JÁHøI\$"¨A*¬”Zƒy«pR¯}èÙiÚi=6Ïú}jXY€àxbêòa¦å슓, ¯øèAb]1Ù× F¾Fz› ^ÐCÕø²QQѯ–•~Uý1³õ#tÿÿ¬j<µù~„%¦ODTô´Ú̃H»þ"lNl@¥ Àæà5ì|ØÔQw²ûn½’]HMgD<&+o+}ýè×±ÇïPé÷ËóâÂUëˬH˜„C‰ì»_7yîꈞ½ á {ÖÕËâìü£o„I¤Ï:µijžúøX'#ÈŒ³1. ¸û¦>쮽1äa÷c]ÍӅÈ{€«#à6šØ÷L7Ùm9ßCôRá©V3D7/Lþöªw6¹g숳é«X~§}’÷ΫáQ±S‘—ÇN›6ó)SÎW,®ò‡Ž‹ú²ü¡ëhH»UeØHL8@- %ËÙæ²Ìlk§n’d}s?ÚàÖ™Ø7«6²,kŽž€¬Ûy-[»yÃ”ë ½»2ÚGã—v³?·îgQßÁŒ5³?ý™Ñâi¨–èqì;zŠøú°+ºµa·_wûzÅFÿ=xÛÕzÜëv„ô`ÌÀf±ÖM±ˆÿdž&öÉ’¿ØžOé&c{ujÅî¾±óÂŽée¹¯_7îa;ž`%IZ(Íeî`é™Ù¬{ûöÀ«Ø©ó)ì‹åëôg‘s¿e¡Zõ'¶jCÃæ¦ºZØ·«7²­{²‰ÿ7”Ñâçß6íaÿì:Ì&ýßm, ï‰÷ÿÅ–A°zuûõ—±>ÝÛéÙ¥xifúüÅt¶x½÷¦"Å€Ö¸$ë~že—³H%< ²®X· "{¾Ò™%«—ã¢;HŒß<8?1ü&‰¾;áj[¯îÉŸ<£%"ÂVs©‹”Õ‹€[´&¯¾gº*?aæÁ«*pØ NI>·ø­e‡<=ô`F8*-¼{EÉQ;à|~TTÌ{°+¹³R]0vz ºi)ñ1S¯ ]Œ³m©30;Ò–@¬ïÙ*™Ø„8KÔ¦',Ó;ÚlÊr/³yàÛ–I'Œr™:í:èeÏcæ&Wjö³oaܳ#>:ê-òÏ3yùÓøCPƒ Àìàœ)‹uU;#œiA-7­q»“sy\\ô”u$"jÚÝ*Ó`@õ9v-žå¼>fæqż˜I{Èßpz8M½’©_4¦LAmà·‹?pìÄõEŸi¶9 ŒC/3ŒI­0ûÈÏÑÃ,Q,ò1Ϭ£"cᜌÍÇf`‘ö³Ø·áºKiEOƒÔë6ÞºI£iœ€©¦®ñ±QQ¸ÑQÑ£?‰Ë(÷l·öî‚è¨yF%ÕáoœGÇÄ´P³5ÔŸÂHR7áܸI“Rñ{°_Hɨնð(åDb<͹kRÛúkÛ~vÏ ¾lˆÂ×+7°~¡íuÂðͪØî#‰úuÒ¹vàXrMO¶ƒè³ÓØS÷ÝÌNc°o6寅}*ðSÉÕ½=tâ ûxÉŸ¬/âêÝ­-;“œÊ|½=õn µ·ë{w!ó·,~Ñï:¹¸ºW§2k¿U³Fz|߬ÚÀÈ$má=4RÒ2õ4ÿscoÄÙD'!€dÀª,?ë„äÿ†]Ãü|¼°‘ Æh­ËI¨Ý!Ù²ç_¨ËilˆV¿X° óµhÙܯVëûv<¢‹Nì½ï×°`øQ9¨Ì$ñjàçÍn»®k†á/¤\š§Zöçv¶a×!6ñ‘¡ sÎ#3Dž¾üy*cwxµe£±e‚WÏ@ê-›MÞOޏY™Zªû½»òûI'Ï @VÕR6D²§#àò*g3Þ÷ Qe$"U"3²˜L;qøuã¾’çÁôžÊ¼羯5gŠfA‡`}Íc’FÏ­öÔ% 7=±ØXf–úƒÔìPíÚZZ¨>Ï2ù øiÙvûC¹qäþU4õ1¼³+Þ‘ ’Ѓ÷F†ÿê¢pýˆ&É#Ì&ÏPlµõ7ÊóªáOgÌhl çÒK&.D­Â«ž²ÌЕؑ&ˆ6TÓ”wà÷©l2u 6õ;à]ç†CZL} y˜"{qRñ;‚ÆOX^kf„ON³ý çÓ$ 3›ù`,qjjËT¿"“4eY„Øùš}ûzš â’i#s׎±LoOah'h™‘TN4À7Ò³\§Þƒ²é$ ÄèI™hdêÀ®LbqÁEê¤Ç¬Š«ƒ|o\<Ûdæäq™ 3ŽÈ¸ï5¾q|F|íä]Ó¥Lƒ´âƒþ`/¾»ˆÑ¬>Íž:’;n¸B'wâŒÃÙ¾£IX§À:´ Öú”_2€A{·<"Aƒöó3ôÁÿå]i¾¢ #éI|FÞy½n ipÿù]ªÇÝ8ÐO—%:™ïWÚ…y»Òœ‹ .«!A*ìHrÓ*}Wtmƒ³ëõ2$uU9„ç´ÇF§ÖÍôõC´g›ýÇNcóÀT–É™¤Ýûo~Ò;BÖ­]svðøväIeÆâgZ‹‹NùÉg[ílÂÃCÙMýºç“6pF¶e´hôoÔÓÌ¡ vÀ¯WþÃ>_ö7šy¶\• 3&_ª­[½J“t˜ »wPßn2©[ W;äxEkX;9© ªZ•tF‘{Ö;ˆ§±3âÊCÓž€Ôçë·ÿ‘ÿ¬” 4Òþc,±aLá ìLí &…~óxËøsù¯q­³gpƒfo?ý´.ù!I‹]U®7™¥^ïZ"wä…{úÅ×äØÔɸÇûœ©Oá<üÇÏœé›rÑú_Htî¢û"NÓžÁ.co.ˆž¼1Ïo&L0a1Q…ÿœ¸˜)ßÒ3øY0ðÈ#Nz:xÖËÒíq¯D.5Þ+öÌ5³ìkºÿÝÉ““Éù‹NIÉ~FUm½p›©ËÕ G$“©Ã|Ëä)LDdLŒªikHõ`žeì!„!ë–ç.’?\* -ìÕì°NÇ W¦“¤&ÄçG¬k¾×+žµÌ ̰eô‘ÍüqÜC„2IlÁ‚èÈ7énnDT¬/ÈV¤V¯ç[t)T¹AsÿB=°QV†ºØ~» 6êG?qíÞ0äÈÉsÝ­9v¹fU`4]mlЕ=XX§–úßi2@ÎÏ7W‚bµåj‡\Ý«#ûö—MìA‰m ý`æ–¤;C¯é•5;‹ÿî7H7‚؃C¯Ñ¥,ŽqÒZÖ|ÐÀÞÑÑæ|_,_Ï8­oZH;’’Çp%]ÓõV,¶_öç6Ö¿‡>‘t¤I¤Þ6ý‡æ¤Púùqѵmsvèøi}³DZ_Ñ©M3öˆvFgÙVëÖ6„íƒTŠ\+¨ë®Õÿ³wðQ[æÞÝ4 ô. R-`(ö.–÷|OßûH¥%¼’ ]QiI°½gyŠbE@,ˆ Hï%@€!}oùþç&wÙ„$$¤° g~,wî”33ÿ»¹;gNÚ¥0$ýñoÞ Nï|ý³ÍÔÐסq2Wx"š¼ÉQœÇ—{ví4ÌÿÎ9‚µ´Ï0¯·§\…ï/§S‡ÀÊ ;èïßT]êòS7 ™¨|”Ê'Y}ÇÍp_‰Ó°â7÷œTØJôV”4Ô“.6tëH'^ZÙMP žÇn'g9Ì •–q>^*©Ó^ˆÉwuTÐä;üÖ“´CD¸ê¾‡õµë§y¯¤ût03¨Û³ùt˜(0«†–”?–ãôÙOör#´i ÙÑ]`(>§žÏpH ²©Çå')ÒZ)W—E÷;Åaf¨ãÄ!C2¡R°ß2e=ºÝ®vd›ºþª3ž)­aT§(J‰¿jè7Ç¢§MÃ4ÁÀÉ9Àe6°¸‡$6Yzv08{µØÕQÚD0³VsPœGí ÔwبF.X³¹S†zÇÊ… ©Ñg ™ U@O@9gO·xQÅURAªÎT/"\üó®kmÛ’VM!ˆ„^\"‰¤Òbé_[ÄŽ½ÁÐäÿéF@uŒÔ¯ÈÑyG#¬¢©nx˜Ø—v´h±˜ÿë±´¼ý„}Î='%¹ iÑ'#ŠŽî¤°Ð[óòs ÿgð£Nõq×.ÀìØ›Uº}¢ $O”¢ëwˆæ#áÀ!jgö+Fì…ÇI´®È:áέ-ùñßdÈ–èŸw^+ÂC]`B~*Z]®{bž’g-°&þ÷RõÛ£J«'Þì·¥ežër)&© r:5´ô»%kðƒ¯L'-S3 •¨j4C¦ƒ¤U“,qå¸7Ü—–‰¸ aóÑö—àíÄQžOóUYŽõV¤(¤¿àulÐóжÃV.ÊíÝ@¾ÔBÎÒ}âŸD o¡`sÿfÑ>v]žj+‚+ª™ylTlá…€Ñs~Êzå0ƧRQ¦|þ"]°sñ§}švƒî¿+!ƒyë¸fR†‚9Ég ™WàÊôS -ítè[,öb^` t?Ö<ëÆnc-®½Æ˜«°xÉ‘ßÒxBÅ<%LfD¾5uÁ$,ÕÊ¥¬",ÿΪè3(h )•ìær îÏAð±Çœr¾žÍ¢øý¹è÷uù†&A¾l’:\ÜåLñmÙŒP¢ádÒªiѹ]Kôû¿úþƒpøh¦mxOò%«6ÃfF‡Ò3¡ºA °GÙmKjüʘq¹Tñ`ÏËa›²EèÆ±×ÇÚØêaäÐR0‘愤”hþ¡:FãgB½ŒÒ9Ðì‡mÏæÝ©¶MIÍ(Zü¨“{ÎcHCNÈn(Œ 9% uu…¤ëD‰’ÊÝÊ;m‡'j_R=áÿûºíx-áˆÈ°v™B¹(J{¼ž_IN·rK¦éº%é;À©z •ÕÏø]Lýp¾…?ñ_C\õbªw<#Põÿ‹WõcVÊII—¸Êßï!…åªJ8}´—U}ØÇ/…\àŒ(mt—d-f3Ô¤n⿇}Í–i~ü´'~|ž°®sYâ §màuò C÷@Í*ÛÒåõ(÷Ï6¶d$o§iÚнPi; &£YÒ(£žUP[ù¨®­ÁóiâRÄþiZìŠbG€×”Û̘SѨÎÂŒ´Ìz{tï?Áب­]#÷î-MÄÝùÂæ­°z¸ÉR¡f†DL¤?›`t3nqhÀéfH‹r}-ouÊJºÕIñqCúŒô.èì?ÑÚèIZÌñÆ%àò€e“wí;ôú²5[ýž²‚yQWÁXŸ˜‘»{\ìŸæªM;mCw]7í š÷Á®¥h"Ç[wïŸ-X.fÁƒÙ«\Ô¹¸þÒ.âí/~ÏLx1qšˆž—Ÿ‡ÇKrŠÒ+zOv;Á¿“ºÁcÚ=˜çpeL H틤9 y!fçÇňÉÁËÙphÐS4FL Š ”“—çgÖH í«Å+ ±ii“% µ}ë‹Å¶ ÙáÜqí W6~‚œÜŽöäÈ ìwHòSÞôH¯+íùo‚$ öM—AZv¥ðYPóMøï”—Z(±Ÿk\œp²€™•ÐLë¶¥”ù91tC 7Ï'þ¾^Ìûu‘ž‘¥Bs!±…ÒñYMë]è°b£poF 8¨± Íaeå%°8^O£q…zÖu•H®©›ºž½zÞŸŸ9>*nll¨±=W?úoˆ .‡ ̵Nã¤c~£r8WZS°AŸ7-Á³Ó© ¼’Ô¶'o‘~ÔÈ„ßEë&?ÊÝûo‡ŠEt¡v–„*õ b¿lR]>ÒEv˜eæ^bXk¦&x¶¶­h¾µËó}Š/a™O·^‚²ø.Ï¢=ú„öRú.JŒIôÁœ,5vlýäáÃPÙ¤Áƒ³a[³;á¸ýÔÙ(B!µ¾—ÐélážGmí$åk ó<¯­¶Ú4þRì>p üÔöÝ×Êc˜;c”gpénùÌYýµ©—ÁÆ'¯µ›ºu~{Áê OÀâê¶-+d|_•‰<~ѧ¸DÈèã$RKŒ}¹µ¯¤rFé ØÏ8é1¸B¦Ù™º[î ·ÌN/!éu…è}óå¶D„*#æÂûÔÇõ¥~EçãТkúMƒþÖ«h¼°u³?Gá},<ÌíZ*bß¼Ãv[MÎ œDs Lä’™>‰¼ ‘z­—¤V´'®Ù)+ºriMŸ“MÄDUÀD©p,!–¯Ý&ý¾þʇŽ~²W$lÂûd@’7îÛ“£6õ»±[‡o¾_µqã§ ~?®»áì¬ê·øõ²sϴ㯜Ëê òZÜ\ÖáàL&þí×®z’رÿÖOÌÒl8aˆ£úò$«M³!x&GtýÐëåéÇmk6ô÷h„ÊÞØÔNšµÀ$5¬`LiPÍ‚š\ÿЏ3çÚÒŒ¢ó,ˆˆ¼©Õ«“ÏÌö/KßÀöåÍӘĠMdÿC’›“I4ç“ìz2Õاqýº¶ä)¾ßj¿‡n$)ÓÙ0Ùù’åp@R·ÄާI…ý7f™ýS±&Cõ)ØTÏNeWb¦?þÞöX™_‡{®»X’—Àæ"ñU÷ë>óϾ#Gÿ)â?‡¦fDeŽË´SÀÉý‚œŠ™3!IyÞ‰EŠ+ý¶cÃ]½{”ZéÔó Ú/–=GëòŠV±4í­°#âPÄ$mpZidžÖƵ²BŒÜª þ錟¿ÆC-#ë¨Éy€Sî\ûÓ°ž¢è㇠;ê”Ì•\/7ôÅìw¤:'Cƒûœ¾DL¸*žß’JRßnPê#`c0%HàIìOqÞÙ­!±¸¨XÃ÷`šïé<r@@AB?ÿa9ÎtðkE[JN6†õ`ÏË$ÅCªª(¡ùsÃN±pù:Û£ Ù^‘³ŠPµ¤€µŸ‰b/Qà×ø›§ ¯$í¤8OïÚöÚ.Ó½õ*±mj»÷@úCAi?ÇAr1žð4j ŒìȾùùOÛ.ìغÝsÃÅpB‘/},.À,ÙžMFL%Š'EïŒ\ˆVe' Þ»‡!°E3±^ + Òþ;¨l¤™Þ©B€š _ Í ¦ÀÕŒ#P@uñvÄ‹š ·ÅáÑݨІ#p²„)γ"À®ðdÉÔº~$%$¦6SÆåçž©vëÔîÆòWÑE24¿Á+àˆûD‡dÃE6gä)ð¥AÚöZÍý­P×–Mˆ ÿ™-"ÂC .yXfh÷þC"ößP:Àsì7ú?¶cŠömšÚN:ˆ1£²º¡vûPx)üß·KÄ㈑DR 0;ìµ™¶ª)Íáßü"®ìÖAÜŒøO?ý¹nÓOÜÝ]4®_ÇŽËTѵ—ÖŸ\©ÿ®Æ§g帥zÅÔøá•֞뚀@U9ƒÃÿƒU°‡«Z:SåkàF \ FÓl8Ç»^·¶¿ôÎ7æç0 w§ X[\Wú›ß×^ØYÔ“BÌÇN¸'¯|N"ï{Q÷ßPȑȅÛÙÁ\¯½¨“íÔb-¼R*)À¬ŠïE؃Q" 2[Õ‰ñÆþë.µ~½ð0C°úYU#Îô«ª·Ê«¢UX–‚(ñUë@†æv$ú*Z“e E`ºwØzp½\'}ûËê|¿l­èqÉ9JOœ¦’'F <œÕº‰x çeâœØƒ­y$)Áóayú×ö¶}=£ïÒø$C’Q•’¬’ÖÅxý¶½ö<þóåþªˆ°P±+õ°h{)JçwÈw#îo€ y tR=8ÝÈõåÛã•7À¬C£ª®$IêÝórxÏ—»’»Ä_«j,¦ËT5–¡i,ºý~@üN^¨ªÌ؆拠ù\ÏÇ` C ?”x²¿6&Á§›/|·dÕcóû 1^ZI¸–ç┳u%E›²¥ótª—t –­5R¦yfhÅí±|úkpç| ô›¡ ví…Ubª*•°¶èxN§g»Åï>Üiã0\Åyj+‰¾`¶¨?‡æ©¸£MI±Œn¸0Cc£ÁÿÕTj¬ÊYtôr¼â|^•À+Rý¨*é3mF€~Èh6)>öq—ËÕ:ÿ/¯ß–²Žb¸ÄÏø\ yåCãHo‚!‘=Å¡x-ä<€Sð!ÐãâÎ*6Ã÷ÓÆœ|³«ÞõõþMêúºõNra>úé]dÛR•ÌLi+,äõìÖÍì8M³úÓV3Ëóbõæ|õ±Òè”TWZ€YrO^þ6À1ï; SI´*«œ\ÛIQvTM¦Ãœ*j,Ccf©“« 8xKøeD_iÑì«jh¦Ë0Aˆ@>cãªD\ ×áoÓ±ùUå¼jÇžƒ¢oÂÛi¨&2pžòá|‘X/•ió“švTìN=tÂñO¶Åß X2§Cê©%Ýg\s:¬·¤5Fyâ a½Û±]‹ÐQ}ïS{]ÕµR”4niåÄp´jÚÈòúæç‹íx0Ñô)û r.0pü;bæw¿‚Ù(JÉuf)€-9Xµq—ÝH ÒÚô—Šç_ù Úþ–¯ÙFŒ”O†+þP%¯€kàF Æz9s`$gáýr¿s_YW©¨×ÅôÑW=¦Ã05¾q O`Có2‚î6¸ü¼³äÝ×_Tl,˜ÊX)14£ßüRÄüû.`Ÿ'$ùú§ moMO?|ó Û–§Á[Ÿ/Mq‚\‘ ”%Gñ7žŸôþÄ¢E“ú%5«5å´Þ§Ç¾ƒ˜¿rDr‚gl­YX9‚ ÉC•û¿×^ØIüíö«ƒ"†PàôIBBA^ã"eçæÙM÷ËíO&_4À¬C#϶·‘ÕâŽ}×¾4‘ðÆ—Ø>Y“½q¤ɉ¨ÑÔXuÕ>À0²»c“Qi¾Uñ"›RÌŒ¦Yý hÚ ÁÅÏ9ˆ¯}Gzï3em²Sunq‚xý<µêA Ú3¾£P|¯†y]§¶-ÌGn½B¶jÚ°z/…ܺ’1r ¤%«á9©=<,ÝÑýÛ í׈M³rc~\ÚØ)‹(œ,ÿ´b#Üà6‚ºL†XüÇñÔÃ7 7‚ZΜ»TìÄfÁöÄÕt@,Žsìè´ø»%«aôœf{]ºíšnb+¼<ý¶f« q‹_þÜ$éu¥m]4G\Ÿ{„÷õ/°A½Jœ‹9”ÈEnýºav\º§ßþ¼RÉÈm›7OÞÓ]ÐI8Ig&½?ÇãMô³ –o¹‚º‰¥«7CîO1ªßý¶*NѱGx¨Äx!6 ú‚Šâ÷æ™"<ˆ¦UmSÑ´™!{Œ ¯´?£¹ fÎJ«mè2DA^‹¦Êbdº%9)ÉþÆéWY×ÃG3Å´¾7°ÙïvÕ÷V]¦ÃœJjæç!öõz[9Öê°fõ[ä¦ùMq)%j±«úÀkŠ/a8Žêz¡ìúÀò(OüJKÈ¥3¼žÿsʣƎ­oeè ìl§,ðŠ“ãÞ†i½îvËë¦i±+ëœü€Ñ£›ædš?Ömqá¤Áƒ³òò^i/7ÊÔ3âTgsyûVv{Ã)¦ø¸¿6þ°îË[ÐÊÝú"M{2§²Çaz§/}â"ïðPWuëpJTuÓ¡J¶è÷õ¢;ÜÁþ㎫ŗ‹VbdžêÝStëÐF¬DàÀðP·Íä4oT_Pû/ý¦"Ü.#ì.êr&“+ÄŸ`€(öÆåçŸ-ÂBBÄûßþ".E,nºTlÚ™Šxõà®6Œ ÙEWпª[{A±9(‘îýT¨·Q ŽûÑžtþ©Œì œ”ž‘%jÄN¸¼E]vÞÙ‚âu¤J‘uÂq–Pð¾.±ãoPã#™…_MÙ`xÒŽdæÂÿEǦŠw¾þÉŽòÌc½ìx!ÿÃZ(^H°¥ûÚǶ¹UÇ|RÌ× Àh£^Ww :ÉLu¬?Æ8‚¿ËIïgÀ›\–%]·LÖ¦üxŒ@E8%?ÌtÑþ$McqÊ+ô‡‰™oÝaÍîîÝû¯|ùrÑŠÜ+–ü@XÆÃÅf®xêŸO807°¼¸/ÓÔ¤"¦V„™!z`fÆ)cåêµNô$òÓ´¡{±¶¯öè)ÏœDwî‡ÀÌ™3UèúOAô¿ÿun×2L‹¾W3s\»ê, ƒiRÓ¡XWuë(Öm%ײB´iÑÆÔ!6Bq,ÂÀØPÊÉÕÅÜnÇÅ =ý‘ÈŸ#š6¬g3A‚ rHi;ÔÜ( `Ï+ÎEdôz¶„‡¤ ‚ýÕƒã¤âbp8uE¯KWo±ÕxþuÏu˜{[;°aEâoŽ]–x!EçsªîSv†ÞèdN§«eÊz´^b¼9U?t°ðúWÆþ´ô\ìSnKŽ^êž¡úgÈ#2'@—Ð8KÑGÿ~Ìë¡—˜fÞ;Ô\é”—é*Eþ¸:4x ¡\4eȇ–•·§“ƒH€Æ²Lñ0¢Œ¿x¢q¡G|5ºlA`±9†®ÿíµÀ>Š*ž· 1¯.NŽŽ.ÕbÖ-\ïO‰¶&:.á‡@ù©JïЦõÏ ò>#½Óºl†7n¸ÓsZ?#ÁÓ™î!%ús{Ó²Ì,Ì”By+Ù;!z¤÷.Ì+›¨,´ïçr‹èi/Äþбß@·žÀþ(ŽÞ>@Ûx›Ž6º‹ÐÀ·u‚4 jÖaÔ¿Ù³k§ÉóV®Úd`}úÁkµRŸ{H-!Eßø2vj÷¢OŽ¢ˆ!‰£<Ÿ½¾qc.2-Æj )Ö| îB;I5t‚éË]‡ç1Þyù5ü?#P>è;8åÆÿá»tß×](î¸ö Pi ÆÅI¤–åÓu[:‚ï»S\èÚ¡M3[]Ì)$µ²oàµi_ZºÍ°P¹näk¼F?pƒxŨŸ‰ËÎ=ÛV#U³’Rq18ÛBbì¿MKÏ€šY£“:•7ÁtMc—/Qè‹v=¥÷ë·ï% L«®Û/Õ?¥ªæÁÃ\êï9>Óƒ«¶?£Y5~úGs—ü%¾\ø‡‰ßËíBUîJÔbÖœ¾ˆðÊk#µBBã<˜ÿ—»)¦ Å\åïø£-VíÊik_±YƯËÛnwÈù1Qæ¨r13 äºQH+µÿÈ1¶$…|êcSÞ¡¥ëÚy…Æ)îFŠ'¤Pÿç ‡SK>ŠS¡IH˜²%Šû×=°ljüð¿hOÒžÀòÀ|†/« î7;’#iPü™ï„¾ !:·sú`+ræÔÓå íæ ¹´Ÿz&!¡yÒ(Ï—꿆xÁü\0]ó,¡±¥"ß‹pÕéæ®£\þçñÚ8›qº9ÔRäÇ0€½DV<è®OŽ÷¼2åz’ð´A¾»ê ½c7v¹ýÆOñmxÔfSµ®Ú’·‡ð.~•N˩ΰôÉôÌZ¹Îh‡q¿=¿CˆDmH*šä |q|¾?uàÄ”ú®í56~†ïü}ÿ¼ëIÆð%ð å¤\ñæ¡!å;ƒ qÛ6öÀ$É!û²«™8èÑïÁ› MèÌVMDÌ¿î$E!u´ÙPg+-ÆàPpê@)Ïwìì%Ð3ZÝð00QGK#ç¯SA+7€2,šÇŒòòs çC^«‚--]¹¨\˜<|ø‘`›[uÌg²6lÆ™±pùzëWHí8U=ÄDJþÜølÁr›­Yf„zq33UU@Ͼ ï¦\SïˆúáäÄ”0Û/™–¼õß÷]'/;÷¬r÷Ö$‰IÏÌ êE€ñÐáà˜¶ Ù¾ü¹q‡èÒ®¥èÖ±­h¹R„††ØK!Û›m)ûmã}bìŠ3\&·3š7´ãß@=޶ Ç3!wÅä`Kº´C¼}°¥i)ѱø‘uÂlµ´m›‹ùh·îrÓ³rÄ_[JÿSŒòlœêÕ‰°ãyœß>¸Î5hc¹ïP:~så[Áú©ŽyɶMÊ]:¾ýÅâ÷J—ä|‚˜XN•‹À8ùöçÕÆ†í{/ą̂ÊSÉ£b¿©ÜQ˜#<àåZ{Óð¨<²ì/ƺŸxJiù— ü/]ʇ–®ÿeStoo(£;¹ÌCÙ÷A¦Ñx·¾ëw03¤1ÕÔ2Í'Ðoa`ßWcc÷õ}ŒÏ“Hù· E‚ò ðz»é̓ÀTŽ¥ÖîNËüwRU,³Žÿ> F£®î;4Òª®éGr³Á8u†Äf5ã1ÇÒ}ñQ#Þ6L½?ÔØÞ¡rKQæâ¸èÕ>#ÖStBÉV‡yÂ$nD]GqÊmDS‘­L= ;,¹º€™¡*hªY)…&-ä.˰ò¥Cv þ(;P©¼Ç4¬gî„÷°ÚÄÌ$Õ¸ñ>æü²Ò—•âúK:ÁCZ¾Ö0 1žÄÈE³Û¥ bÒîzÄÇøß·Kijßÿ¸órŠP, dèýÅØñ5:´i..‡'Qž<¦Ñ)1%%[œ‹:·ñ7>˜³D¼4èQô;K¬Û’b«¾ÕÇ<;‹o:Æ|94+1S/䯶ã…P91Jçõ½?h$k4'¨ûØXîm©vüîO×D*ÔøÍ¸×§zå«E+þ½|ívã/UÕOWl*ºîCé™¶WBx$Ô÷8ì¢ï›Ê„–j«$v–SQt¹°#P«šê?Q± Ò…í)æèÛÀ&µn­Ä,"¶Ô¡áåå U‘O¸,ë7jhIw„Oø~ éÆÄ!C޹óA]k561Åò>q(C'§'`ó»Û´¬s8ÓØÌíÜ÷ׯtðùôü#Ù‚BMë]&ç`fúél=¨+¤9ŸÙ\ò!Â|)Ï’³¡¾¶CuÉ ðøfðjœv› !ƒ¹ÃR¬­”Ö÷P_J`ˆ2U© HôÆü”_’ÿtÜè«ÀøÛ%¡ÌLûÀ6¸o¦R·ÀöœgHrš}8{êY­š˜°›9åGÆm[6‰±O8Ó}¼ÁŸ§Ì•]ÛÛ§ð™Çnq²öµh{*¼ãÚ D/DcÇ¡€ £üÀôBô½¶MTv Õ‘ÿÚŠìœ\x µ»ÎË¡A }ró|…ìv¨ž$;äY­÷Í—ÛA?‰¡qÔøF9EЧ¿fR‘£ ƒ¿ttÐÉÿ‰rbàм‹›![gG*TÙnv5žìõ÷uÛá9nŸªJñbYß«';VMè7M{ŠBÔÿ´fíK;’8åùµTß¼éŠóò´GßN'F€þF6ïÚ'6Aâ¹nûcË®ø£& p¹ Ú"‰ÖMÞŸqÜÂ-š34•òœ ¥1¬Ñx|¬iØ¢—’úÇzÛø¤èÔB)Ô’šï¦ç=€®ÿ ìNôúÅé§[Æ,¨ÌÙ‹Àú²æa »óó«g™n×b©û^ X84"Wûm?åð@IDATQNDÓÊV)¬³üí¤u^ ¶D&*!¡¥•möÂæäªGôo0;ÿm­v| NÝt!qc.›?â·§=ñgåZ²›³1"ÂPq뛨 ö ü#P²e?ôÖÝry9zÕ¼¦.H`JJ¶)mèoÌafˋ˗æH€¼©ƒ£856‡™)nŒ’Ê‚‘¡yf„iŒâÊ»u|3±¤ÉŸ†å3 ›µ³¡RüàþCG‡¼7ûçK>øv‰ E (ٵâad±Š•†ÖÖÝÄë¶ H4¬¿ßqµ¬_7¢ÒhŸ,!H®HmÒ‚ksÙ ^¸íV‚²Ò';Ç'ɰöuŠùÊ” lÚŸ<£YCóìÖM ‹.ÊÔ›1Å#ðî×?[™Y¹BqÉ'nŠo}ú•`BjxÒa•)Œû×mKyð¯Í»:ˆ£¤Ã#šëL’…Çv9ÞLIëÊ›ÈVl羃bÇ^|à¢|ÍÖãÈÑ,´È›…ùúg‹Bú?t£çç·Òáql¬¹§=Øh¯t¼Ÿ9åe¹>5ztã©11¶´×"ƒtEņ§4slX³È[ú`D^FûÍø<¥‚^S¼qöI’­N†¶É^Ïõ=M{+,E¬›ôÂsñrÇ;ŒÂ+Ž`Eô¹™šV 3C÷øq€ö˜0§Õ³gxkkçÖŠâZl†Ït#bRÔØ±õ­ó~´ÝI÷N*˜cN²ö¼Sd_ ˜›BnOûjšzNWyF³g 5æF  ¸ö¤á»)TòƉ¨ È-ñ§ß/‡ð[ÎD\.ffÊ ê4ï°µèBŸÉÔuÀèÑMs²Å¹Ð$hoâsðHÆÙd¶‚ ¦!¤ P^ðÐ œÏ|–4¨ë~cü°aGËCŸÛ2•1ÛyÂÚöØmW‰ë ¼{U]¦qz"°|í6ñú§‹‚K.h¡¶¼Ã9@:=Ѩy«î7ú8 y ê\÷†ÑŠV@iMÔ17ˆTÔ —õàØ"R!² )°KáœmÈI’¡<ìçäå H…l]HRdì?’‰rŸŸ+‚ä)*e?ôÄ|ûÜñôId81Œ@ù`†¦|xqkF€¨…ÀE„ÐSÜpé9®Þ·\Q WÈKª.þܰC$ÏúšRòSi| 96©®±yœÊG€<€š†y1Ô§/„ÇŸöŠmqלKCh¶…•4"$Dh®dCêrªmð¼iî}é.¨Eo„Øn½TÔ¿È.´¤þ\Î0åC€šòáÅ­F –"Oƒ‹Ö«sÕè§r„½q-E…—UH2óæç‹à!_ü¦ª z¸'. n[ƒ /¡)aãê)9ªíÏÜâ³róBàÌÊžðüóY¶Ÿ5hY÷÷@û3+›<Ó«Åw*ØËÀCÕjÄVV~²ê¨wuZR‹—ÏKcFà”#ÀîIOù#à 0Œ@P Цٛ8QÝ>sîR='—¼¸rbNŒÙH¼ú¿¹13°œ‚`†703sbܸ#À0•‰Kh*M¦Å05¾žø¡ø>÷¼ögˆ§z÷„Mw^O¾Š@ðCñÖg‹ô#9¸™¨¤øØÿVñLž`F üÞ6Š©ã"F€`N+–-Z°õ’ëz©iGnJ;’!­ü¤øV †‹%×»ïó‹øxÞoëd»”Ê-Iñ1sNC(xÉŒ#ÀÌÐÅcàI0Œ@° °|Ñ÷‹/íqSØÎ½iצ¤²ºvh#]*kçËó9•󀩌X¸|­˜þñÑæux°Jh©¶~tâ¨ç„#À0ŒÀ)B€*Nð<,#À7Q±Þ¡Ø¿ŽnÚ¨žèûÀ jëf ƒ{Â<»*CQêÅÒ¿¶ˆ9¿¬Ò÷8/xò;—K}jš6bS• Ê„F€`ÊŒ34e†Š2ŒÀé†\9÷T„òT¬†½®ê¦Ü~M7ìéÃi»ÞìÜ<±è÷ bþokŒô£Y*$2«L)^œ1Ê3ë´…Î0Œ@"pZ04ßNŠh) _+Aäßô&ušl½4:…ƒá’§ÄOÝØ—eN‚kÞÇ5¨kÜsýÅêåç Ûš`›)ϧ2ðéºX³eX±~»X¾n»A‘ÝUEgIk|Ҩع•1Ó`F€¨\jåO26rö×RX·„u“°D“"°Á‘‘X›_È7o”³­H}¸öÄߪH×þéñ#–׈ Ÿ‚IjÚ×^ýÇ'ͺî™ìJõ<€Z4$y@C„ú}Q‹¦ Œž—Ÿk36!nW-Zåé¹”ý‡ÒÅæ]ûÅŸ¶‹¿6§˜y>]Q™ŽHð«nùêt-våé‰ ¯š`š@­chfwÝbYÆ+€ÿœ²< Cúí0¥Îˆž?z ,}œ6Ñžñ…•÷¬%ÅùBXõ-!WÃvxVâ(ϧN›ª¼Bækø•]–ïy¡*ǩɴŸŸ0¡Î‘Ã9Ò­ž“¬Å¬«Ékṟzè°$ê…„ûK¼Æ¦kXˆË¼²k¥ûÅE°ÛØAûêͻĬ·Ú4o,ï¼îÂSh%ÎLˆ˜üÁ\ïsÙ¸~]Ù¸A]ѤA=Ѱ^„€t^äù ¡†ðé†ÈÌÎ{ö»R‰”ý‡l†¦¢ªê^Ó0?–ªü¬¥rõBM»A¯Ä)2)F€`*B Ö-.XÐÕ½tÔBŒ§Ëƒ~ã]ؤü_–™q眉®‡{=¯/*Kÿ>#½Xfî{`ž°ä›øLïæe8Ñ{ÆÄï&'x¢ËBçthåñÎMözn>ÖÊk¬Ý`¯ŒW† û‰Yý4ï•yºÙ6ÿ°|]èÙ­›šW]ÐQ9¯}kÑ(²NБ•“+~\±Q,Z¾ÞMôÆÍpÚ’]ÇnýÇ%ªPµDoÌWCÇ«wè¨oÔÖnƒ´ÇCà9îõÙ©11©O÷u¡ÈYÒ²: S Ò:’ì;ŽIˆòÄ·“•€.Wc-! 3×’®×fÄø-:ÎÛ×2Å`ÀîtæAWôÑ *wÆ ¯çÿ˽ürxÆÁ¬Ò­Ü#tsð½Ò¨¿@ÿ³žÝ:¾Ò»woÃi_@›æÕe«¤TŸC¹_¢G&Üd¾ÿ`¿Ô˜l¤ödE Óœîr)LÓbíÓÐ~q£»ë¦ù¦¢ÊþŽÑm_-á|ÃgÍjé¾tü°aGûk ê>k"H\ ãÜLPšSÇUçùW´A‡‰n´gôý¦°z`söž°Ì‰°ºÔ-B£Tï$RŠö$$âÞÕó‚NQëpÚð•(ÓGŒ8„ö¯Ò'JÝÅÒ­^©‡ŽÞ–úÛºó—® E óÌVM-HCÔVMú´nÚPD„ãuS ‰\ DÐí{Š `bÖmßkíOK·¹lð@5v ÊTü½­v†ƒýÝ õ[÷¼1ý£ïEŸû¯¯v†Ë™Ge\._'f~·ÔÄûé%ByØ¡©i½ó'·ÊìZÙ…¯Œ#ÀÔRjt0,ùŸü|FVŽ8x8CÀÆì摌l|õaɇæ(œ¡,”Šœë®ùSã‡ãâø”ä{ N\«6íLœôîñø×(-ÁhÕ¤´zónñÙ‚eæ®}‡ e¦|òØÄ!CŠ}gפuñ\F€`Ê@fhLmZ7+ãÀKå_vé=ÀÔœ©[ûIÂW\KK˜gas]èä?°¶;±án<@{-2×}ôm+KÄ÷Óºù=åÖ?±[ù Y‹Î‚äâjl@z(.WûDmÄ6¢ëõˆu!I\ ‰ÙlÓ–V§Ðfõ[L80×¾/æ¿iÚн(öãu¯—uË÷TbN1lè} £>hcÛíÕ¹k i­¶ú¤rv¸š ´ù)¨ÿ4æ@ús%îm†R°˜”äù˜Ú`c¥Ey¢stýñŽä$Ó†åHR¨ ˜ oÁ¼Ü„ì8M³”Ý{¿Jœ´Ì13gÎŒÎg4¬ž`T¾¡ö¦ÏxóÜ9ÃkÏEkŸöįϵ˾qcnÃýlj‡±ÛÃ-÷ò|M÷” h34ª®ZQq£_Å®ïbQ×ݳ$f4¿ÿÏTM{2TˆA·™t¢Ø×ëm-òäù°µ;?=+çü£;r:`7ÞUMð7‰ïð ƒÜ@âiÂx=*T[ J¶'[Ði‹é–k[Š˜š&MëD‰¤Æ Þ¾çàôQ3¾htí…ä]×]$"놟¨ë)«?š™-~]½Uü²r“±;5Mu©r;þæ$üÍŸ²ÉñÀŒ#À0§ ÍÐdgü? G›JO`2þ8®Éøk‡(F #¡bbÕ/iPôS“×ýÜ™½{L'µ3ÝfbÄsÄ\ÆÝŠ[\Gý!YèŠÃÕlS×_³`“„ê” *^tÚÚù ³Jcf¨cTR’[îœ$LœjG ÑãÙAésÜ÷”¤,ÑãÆEª9Vc4¢É)Bæ Ý•«Š¼<=Læ™zXn+Ñ6½@ŠšI±EîK¿… êGÏj“æfù2_\üÇÆ§~Z±Q9¯Ck Ïm₎m‚"ˆè®}ibÍÖØ¥Xë·í±p¬²oÏéÍ•N–ŒEékçZF€`Úƒ@fh ÖõDU= H!"ÊC€þÛEÇÀiy_ÑrçÞ2Dg:=uTš¤*Þ@YT¼†íÉú‰Ôª6&jü #I zÙRQ¦Ø÷T”ŸyÅTBÿpÊà…‡6A%&2€MÙ¹a$Cj®N°UªF¨î¶,u„{ç¹Ñq aó 61_bs·*•kJ$Š ÕpRá€nx°ü¹B¡ }ª(bM~©ýÿ·¥¦ÜÊÞ|A§_ç®\Ÿ›{àèÕð w/Öû)a[¡ÏMKÜ·ÛØD#äôìÚai2¸Ñq(Múð…“´r¡løÞW¢›U)º ób“*àý… ñ#pê(øË"f›>[ªkFÒÓgúic^5tý_ë·l¸jã®z¡nUtlÛB´oÓ\thÓLœÕªi•38i°Ú ÆþìMw¦YÙ9¶Ô LÌZH±¾tËÿL5lmuáÃã0Œ#À?›ÁàŸmÀ ¿ÑÊ'²/(ªô,~jì?’ 9”Ïf^H­ªx›a¸¢êïCU×·S´a%’œîÏŽ×Ï0ŒÀéŽ@¡fMCWr/ǯ*MØh_RÜÓ¼ÃÖBÅjŒÙgFÅ% P#”oŒF2”]iç;S_…Ôàhhóz$ °I °ñÿiXÏ@ürIHıÍyk—çû_Â2Ÿn½#Ýø.Ï¢=ú„öRú.JŒéÐ8ѵ®;b]†ž™ƒ}C'´ÝÐ'nÌeÒÒÒ†"0¹#ä›p80ÌL©ˆ7ëN&µŽÃ–åxMÚ¤º}„mKâó\bXk¦&x¶Ã^åWqTÇÚz@gÿÿŠ$;š!À8âÆn'¢¢µÒq~б¡ÖzCr4ÖßÖ¥¾+|fÿ>ž„u]áI™"§YžîÓ°éÙwQ_øÛ ójlì¾>q£•¦9žÕ~³ø]¸š8 0/lîÚ$èsÕѹs­n¾`oÝVbx³ÜÖeŸAŠÒi­„SÓª[Qpà@%¼Ò6¨Ân¡+œlT„òGÝH÷ 6ð¯(ºÜŸ`ÓËÐ`ݮʓ%JÆúC!e€}‰©™æ;"3¦!"M±äÇ–»éÉ£sçâRßD¤êõ(›é¸c¦z2àJH¸[dYÓáwô»Ýº—´ÆÜ¨ZHm©MYÅT€ôe¤!Œ$0OÂÒ³Àl< æàÀþ46˜±Ï0HïpõîÀº“ÉÃõñ„y+7ª4Mòéi‰8’…»h±ÃP\÷‚ÞödØ«ô‰óŽ3Mù ðʪŒ&^—iÍñ k¬’I éÂCíl6hf!£áyêÃg."&§<ý¹-#P› G+Ù™Ú“hǾÈÍ‘i¦ž-Î<¼™>Ûû¿þ9¤¾ùiÀèÑM,µ‰Ïò5„;ö†Ša50¥ÕUr»F®í–ÈB*MꡇatXqÉÃpY8D„tâÐå+#À0Œ#P^ tÊÛíÔ·Ÿ=N†Mù±Sü*šRó‡\½¼ÔM3ÅlÑsrêNމÙ_‘iºšØs¨edõàÉžNÒ6bà˜1MJ› ŸDœˆ6œá#{žJK¤f…¹ ›Cܶñqolœ»Ç);™+lÈH+—œœLîÃ0%#0{¼ëxBü‘ZàÇaUÓÅÍÂUûû^ŠÅ—~•z]ɽ¹†`F€85Ô\†f¼2›÷ת6€£ß>Ì"iI­IÓnW·¼1Y‹ý½Ö,ŒÂ0FàëñÊS0zÉwP¢ÈwZ,lú5ìþ> ÂØ| †æ¡ ÂF€`JF ÆªœA}a;$4• Gar ^b¬™Â-ƒÿ.jd °9º^¾þàœo'iff‚ÿ±ñ jEvÿ˜&®À;°…3äY=Óƒ¯Œ#À0A…~¿jf²ÂCª|C M•Q]èãA‡ ÜK#03Ï´t]û|uËã0Œ@ÍA‡D:³•BY|óc÷r¯“ç+#À0Œ#LÔX Íí³w}=^®†æü*T*ßaTùê$œ8*vÆ£'F€`ŽCÀšùúÍÖýïSE Íßœ†8àa _F€`‚ +¡!¡vövU¡‰ï wýÈYUEŸé2Œ#LÌÙõy00a4'ØËì¼upzü”“ÐXÌÐÓóâ¹0Œ#ÀC F34"¼Á ¸â9pl9•˜SäÔ›£QÄnNŒ#ÀÔzà,įn†Å’º%¿ éb†&þŸ`F Ø¨Ñ ÍíÓÒß`heƒJ§“‘f£„ʦËôF€Zä1ûÈhþ y¦Æ/¡QT…mh‚öáñÄF€8½¨Ñ =º;†oA?â•øó„P½vØ£¡9@{-RÓ¬â[ÖveKyèQüœ™3gª¥ÑŽÒ’"J«ç:F€¨%XVWg%R‘R^JÙÌ)kÊ64|eF€*`*RóÓì×:„ŠœÍ_ õ-Y EÆŠòØíÏŸˆNmLŸÏøH*bvr¼'ÖiOѳs³ŒpOºè ¤T¼Iñ±ï:õε¬í¨}Ic9´èZz}<ñc±AÙ‹(Ý‹ Ëxx… k¸®Ÿ“L=˯n†Õ­‘R3Mß1û”±ýLí{ì¼"F€`j µ†¡qžÈmÃŒ/n;óÁÎRU‚´Ï±YOwê¯(_/9A¸Bιc¨ùø C3Ëüƒ¨ I¥´¥Ǩ’bF¯ÆÆ;Í”r‡)¬6ÇZÁжŒí¨OIc•—Å“ÀÜÞ ìGy[rcY½\"ä{§®O\¿°’•IñÃÿÄTKµ±qúð•`j0Ò `hä*Z‰à·ÇÞi5x™ÓêÖ<²GѶ|Ï0µ¨›ï_™6C%Õæö+xS2Cãˆ3Œ#À0Á†@­“ИT'zÊÙrÛóú·ÓôzÞ·<Ÿ™)Ú²â÷Ó´§2ðÃO†öþ¤X²"€‚y:–ÊÚîXÒs'¢·[ßt+(ü0\w‹6M9#Àò? ùç^0 /ßîFˆ”– ÜáÄ0µ@g–²Ú^§u, l2CSk>/Œ`š@­ghªûÁ$%…¤θP7kw:÷ε¬íœö'º–NÏøÇ ©›EÇz¯3 ¡E„+$GGûˆþS£G7óÕ´æDÇyÓéƒâ&bgjZ”'¾BäN4®gS‡teýÅ’/¡Ð$òŽèäùÊ0Œ#ÀµRåìT‚ QÆÇº”ÿÂ^ŒÒ&6¾œÛw(<Ÿ ë}¨N㈯& œ]Z»~š÷J¡»M÷;Îé@àÚÛ•Doè¸qõ§û®i©tzÜéÛ7nÌE¦¥O‘.÷¯Æ÷Ÿ¼N‰9ˆ6õœvtíëMm庶»mD…óŒ@íA`þİv9Fn$­·ÊÚcç-Ñœ”r òþ÷DA_F€` A€%4•ü(B\õÇBãüJÄ|YfæW8Ðȳ±Äæà™‡rºÑ%µ£:C·F"¯åKKíJ¢fæ~ÐøZÓz#`h~2Mcrg Ý÷+$/{ècÏ×iÀWF€8mÈ1}ÇØÏÐâñ¾òKhL«œ6_^(#À0Œ#à @’M³ 1Œ¶J—Ó àZ\»ü²™…lqŠt³o‹kW½âúr#À0„À7ã•á_“}fW^sPùíÎæ[~»£™EŸe÷¶iï”ó•`F€6 aÀ‰`FàtELÌ{pò­_*JÔíCŒ”#s[ÍåŽhxÑgÛS9'F€`F Ø($A¶Éñ|F€`ªKXç:#¨Šü‹ò–Öƒì+mf†5Æ…Ÿn%æ†#À0Œ#”0C”…'Å0Œ@Õ#Pàš½³3RDx;ÕÊ?×6rʤ‡àåÌñàó•`F€˜¡ šGÁaF zønbø™°þ/uŸ£Ë§«~†LOZõΊGcF€`ʇ34åË[3Œ#Pk0¤¯‹³Ä¢±¥3t¯*ÂÏÐ@:à _F€`‚fh‚ò±ð¤F€¨z }9çØ(r“7 £±“‡‡âSqbF€`‚fh‚öÑðÄF€¨ZKøš@ eI¿„Æ’¬rVµO©3Œ#ÀTfh*Š ÷gF †" _åÌ”Š_åÌR,?C§¬rVCŸ/O›`ÓfhN—'ÍëdFàxü—tûUÎS¨œY¬rvzB54Óü;â4ÌŸ‘;0`¬‰ùrg• ³»!Dj°13´¤Ïÿʽ êîà·šB–ÿÙW÷æªöŒ&U¼8}OXæDèÐ_ U“—Àœ‹ñîwhÐ5*Î;Fš"ÌÌS}<Þ×toer¼ç5»Î–&ã„eõÂÆ§Ê6»\®»§k#¶P}is úÀdÏÉ2oV¤2ßF žA;ÔÏSåÍÄQ±sœ¶ýµ„ uÝ|÷a¾©XÃ4¬ë’¨a® XósÈæ¿ž¸‚MÀJ(ÎlžáC4fΜ©ÎûsÃRà²x vèÏï¥T¦%yc> ½™ãK|oƒTª%ÅŠK IÒ<¤h'ÂB(r ¥;Cop>z;õΕÖeYæáv=¬ jé–3g¾ž>,Kj‘zdO[ü½áë.ô†]79گˢ.q[»wֳѸdæætè >àðJF€`j5ÞËY· ›š™¦ñ)~˜+ÄÌ8OtnÍLÿk¬s_ÜU*Êû`(Žõ~~„:ŵ3óÚ´”.å.·[Þ‚Mxs_–ùAqm]î†Ó°IN@Ýî:î:—…¹#{×®¯'þFì>¢@3ºA½3,©C»¶6#a‰lÐ_q…tÆf®išshãΠ6ØèGJaÝnYÆTÔ½£º\ç¨"äTÝíßÑ¡3àµ×B±Y`2ò˰Ž÷®toeø¾„DëRK‘#ܮгÀ|<×\ ßFu'šµ Lùsÿ½cŒ5L£¶Õ0ÍÏúkã‰Yýc½mÀÌüFrž&ϪuÁa}GŽîCõ.µá0³°ÂY„_¨«~O0‡`ǫ̈žÒ÷+×wÇ¥9žï?5mÍÈ]0u7¸,ó7j“«§ :]¡^÷”p+W‚©YiêÖ⧴Ѩ>?Y-…ai8½¾ ¶ÿÆ|À\N¤’f&Y(ÊÓÌÌƆï‚ÔŒýa#cs)`Ü7]½Ü€ºwg#g–txC‡Î=_F€``D ÆKhreödlníoelJkP—I+?^7¨›-Ù(J71>æ'037¥säHN\”'þ —%_–àÙIm!%¸ŒCÅåjŸ¨ØFehï…mÏB´mýÍTæ¤iÚSQq Y¸7_ÑvÊ‹^Á]ˆ wFˆ¨»kü°GQ¿0° ¤p-“HÚ@åÀE‹ò$Dçèú㸵%(k/UåΤQ±_;}1§E¦Ìûîã¨,w_úÝÇgµiü¥Óƹö‹Ý]7îªâº8À£×^!bí&e™ƒCË•–[­ãzlúˆ‡¨ LbHZ„x²  $¬N¡Íê·˜ž¿zÖi ¦{Ô¢zLÓbWà@³+Hd›ºþ*˜›T½\¤“ŽnbhŠƒT¬|>ën§.Ôí~Ýu“}¾C×åúÒ·ÁMð…šäº‘ÚÐÆ=×ÙŒÔ]Óž7¤%¤)BÇ«í:8“Mk¥\íWå¢rÌõ ôó‚ÙYpûO\ÿ›í?µuú›Â<4L5|§Ø¾–y…zÙ7)3Cw‡ É„gµý–)óÕ^„€ç7«!Öõ¹Ósm‹yœíܽ&j±«¡N¶'Ã̺AÓ¬OS| ÷«Šú°aÁ6À°îGûùXxO ó õ5,ã|ÐKöBÌ*04~ra~üŠHÌä¬â˜H™:@½p@‘˜^?Î0Á†€uÌe3¤0°ŸÁ·¼ ™†ôKhÀ½3CãÃWF€` E F«œ!¶ÌÀ*D¶ûy“þº°4ú½{÷6’Fy>OŠ÷Ü€ñ]$1À@†bž Õ´)ÎŒÌ+ŠªÜb*¡ËK£éÔ™!º–ÎÇòùÜ$‰€­Ë½Bq]íGcËÊû ›öáÔ'W舱CÛåSgÌ‚ëßC¤ ¶<þ´OÓnÿu,Õmñ16âõûÅyox&!¡9ÖÒËR•7޵8–桸Ë*N ¥s8F9¬%³PAÑ)hÌUëRT9_ÞûŠ6-t/ÅwÒ”7í5Æ\…5ÉæêˆŸ¤">…î^09pJgÇ(ú–ú€–Žyä]ðÌEy!Æwǹ±d/œv¿AÛо^ok§˜¯Œ@°!€ï~€„¦°‡3x5‹ôÏײ޸óœaF€`‚Bµ c±Óê±ÀríY¶ú>l6«,ùL“¤?+N4m‚!=€¤Äº†ÚBz°»ô&.Eì'‰Í‰úWÆåg”Óç¸TëæqHiÞƒñq”–ôÚ4-z/Ôc*ÍÀd½z\§R & œ ®÷MKü-+[¬„Pgi²ƒSÛb’"6 FN[²+™ªÅll1MzÒs¤S4g¼ŒB¨ÉÍ-Z翇ñ2ònÿ=2p©ý­iZ/Bª”»O5Mš°ŸYbü¾×—ð(8Ïz­Ü­QømX*LßQÚè.k‡ÔåfHµŠÇ"p0äñ=˜šì‡t©¹‘c}•”tCr1R®"Ýø–¨vð7ÕÙÔ TÎòŲ uŽd_}Iª­œF€` F Æ24ûÿXs1 ºý?¼U2tµ®/Žnt\Ü6›¿´r]»=%ì×:är^Ê¢Ðv,µoíò|§e>Ýz †üñ-\žE{ô í¥ô]”;³8še)£X+!½w²6lIRto;»_Ká£+„P“³ž£³Iu5úHÙa–™{Yˆa­!;‘ÒÆP¥ò†.ôïpr{ž%•ä’Ú¶V®ž †`Ïg¾¥%Œ-›¬Rw¥u UÃ6“ýJEæPÒ˜nឤ[¾'À°…¨&I6«Ÿ’»?ãBUˆéñ1‹©žÕbì¾â(žOòðáö©r¸>/ÓÊ|Õ÷*¶·$ÃñTãÀÅü iOæPÿ›ºž½^П9>*nll¨±=W?úop§—+nq-µ)kjPÏÝçPºo™Ø™Jj‹ƒËÚÛ1Õ\äì Š_ 5¤A]ÆØæköðøÛð¿Wá0€šêx(<#À0Œ@…¨±*g†yLe¢B”Ö9@Ï<°˜—‡q¿!Eÿ1WÕ÷@ð t–F¶rÇŽ§v$ ònu…å»Ýº7Û´òÖÃ~£_ òæ-Ÿ8/Çç›øT0L©Ø?,Uù°#èyA§ DLÁ~e’OO;hù²÷KÓL4ùž M±Rè{nýÈKjN Ë­Þ桞ÐÍ߬©†i|œ'ršPŸŠÌ¡¤1§y‡­Å:oÃüî͵äf8-8 £æùÀór§ZWý^›¶ 8é0>;©œœà²kjpc·Î ¶Àè´…¸|û*'õÁPwä½àŒ|–©ÿ;¥½Î †Ç¸G5ÏÒc}Oœ?lØQ©¸DËh¸o¦+'F hø~RD+¼›Âí ÁîæèCEÕÊŽ©œI‘4ç‰0Œ#À0% €=bÍL‚ùT(˜eYV~A»®®zËcúȵ±/=½‰©7=”¬E“‡²bÔŽÜbÏ¡–‘uÔƒdì^l£r’¢NHH©Š•Ôõim\++ÄÈ-.îMI}Ê[U·ˆ:"3¬€q8®{UÌÖ’#ë4—§µ¢ ô3¦a=Eщ©8nBe,ÈfGë'kÏ(cnÆÔæLt]§F>ƒ/ůw µ® œü²»š„·Â© vkñ—~¹wd`ý©Î÷‹s Qzaíà…­~Äü¥S=7Ÿ`FÀV]> `±™æÐ¡yUãRsš—V=ûH$ª6•ÄÐTí¨L`ªA`öxõ_P™µ~Àîë½Û‡šiÙÍ&ÀŽæy*CÀÛ¡—|µ·Êß³ã—×´·Âvûv? ê3.„ÐŒ“ûÀФàG,£¸>\Æ0Œ#pjÀoH]04­ðÎnZ=»ðÎ~µµ»õGÕ¿²gVcmhà*ù`eƒQ ½#ÅIgŠiÇEŒ#ÀÔ,¿ý &¼¹è¤á ìíb¨]ž´¤³(Ý“½‡Ã•[Rô]3п-æö-ߎP"”¯]½Ÿ,mîÇ0Œ#PuÖŒ™e"ظõ7ü®LÀ»|Þé}ñ»ÊµÆ24Ò%ÖY…œW64 WFïVU02“dF JÀiY‡°bÉMNÞ¹Â@½~¯ÀSë *Îû,Ôß&ÂÞm5ŽøOòzl¯„Î\ùÊ0Œ#¼<½‹¾‹ó×Á›îdägãÝþ|r¼ç•Êœyu  ¶VɈ½DÛ•Ê â2ÛƒVeÐbŒ#Àò˜„FºäqÈfü6)xž2 M>3cMÂ{þájv5‚33_ ž#À0åG€ÞáÒÝô*z§CËj½ãËO¥ä5–¡ù«÷yyüñeÉK«x*”+N…)0Œ#<ÀŹ_B#̰ã$4˜©ßË™!å)ñrFjf¶dFÈ’¼±—æx%xå™0Œ#À”†½ËéõáèOïúÒÚ—§®Æ24ö"UI¢«*I0–ýmÍó~­âL”`S€À¼)õCSŸ†Æ)YF¯!©E§XT~ TÒª]BC0§¤f&ÜMŸ ÀÅEçÈ÷Œ#À05ûŽw;fÿ>3 Þù^LfhÖ:ÿ'Hi¾ª0 Å@pÉÅs#À05#;Ç/­LqÒZ›Ÿ¡QC”jghÈ›ì|ÚBÝm KfjìW'Î0Œ@‰л Èz×Û,KlYöŠÍÐÐ2C¤è“Æca®Ë¾ö[‚{|}ÃsçÍ/±Á)®xV›ÔàO‡gˆ€.L¿‡3¼7³Ÿ¡%áÆÏÐèÒUí ¹fÆ;ø[¶™©_0ž2#À0eDÀ¶©Á»ÞvÇ_Æ>¥5s•VYêV?×uç9“Vö6uù5\Â…TtÎÐëû©eãºÖ—@¨O¬w$4 ÀåÜàÀ&ˆNÿ£¢ÈqŠéÞ [yëTU¹5qTì§M_¸©3¥ì¯ÃúyÆu6,ßgÉ žsœúÀk߸„Þ†i½îvËë¦i±+œºè‘ 7Y¦9=KÏÜñB±!yó°7%è3g_:yŒè9#ÁÓÑéÃWF€`¤euðëoIòpæ¿sš@í˜Ûæpµnµ244S7õ3È5³BœaF€¨•àðê=8x‡Þý ¾Yã%4ô„×ê6OQ¬û°Á¯×3ûCƒÐ°Ûxò¬œ’¾9.éþ[€‡àSÃå§Ú¸3;§¥ÒÉf`°!Øbšæä“Ñ „ׇÓ2Eà¸U}ºÎœ9SE Ñ7¤½#Ùë¹FU”XhÁ'RÝíµÈÜÔ# 1¡tω`ðKhðþ8NBciš‚ƒ¡:Ô—‚Wž;suf tª¤Ø0^4.Å™©’˜(#À0Œ@Ð @ïzzçÓ»¿¢“ª °ö¹n³UU½¿Â–)àÌGŒ¯[ïü[~رT¯>Ó½ÃÖƒq8íI¸Æ'W×ó¹¦õÎ+(ƒ€G™·ÛØ ¦£|É-\ï'Å{îG/=°çüU›.ÇiêŽ$ïÐTnK,qÞ íåF“µéBºû(îÐ×ûpž`@pã·¡QÌãcÐ,ÿë¿~u3¨žeàçxN ÁÊÏ·C4é}4³òeŠŒ#Àö»ï|Ì«]EçVkbÍàóþº íù—(Šõÿì]|Åõ~{M§.WY Œ1ÆôÞ@5j0ðW°MH€@H ´ ½Wãnƒq·1î½ÛrWow·ûÿÞžvµ’U®Iwº{£ßiÛìÌ›ovïæ›WeScà€˜xñƒýž]sœ°ñÞ¡,¿Eñ4v_×Èö®¦(WWçU¯ÁÙwÌcEKIq¤<„¡À7ç>Þß<ÀÎó“\˃Ì’š ¾MUÔŽ˜3ÝU£EÛSNe¬¢—'?¸:ÉYYãžyå@"SCcw;ÓÐ8}&¡Áwh³š›qçhŠÖŸ}ÒQ‚€ ‰çów¸­mñ>4µxÿjŇs¯ð§ïSkFÙ4m¢ªÐPüTvDÔ' ÄY³ÓŽ‹4r}½éÞ~‡k—ÑØ±Ãa{×çñ}—›«ÝGOtU½ÞcgWß§ØŸÍý]þ-L}Hó¨/àü„êk¡íÙT-Vn5Ì?4±OÑC°†VªÜ%‰‚ÀœçÛ¥•Îæö‚¬TL¼ë=ôÛÜÍ÷jªIh«Ù fd¸þâBÉ ‚@Ü"€ï}þηüö„ÖÔ¸#4V6Ý;x)ŽùÑôÏÜ?lÃb@{ò¼SÆ©dó²ssϨa"ƾ4yÒož4å$p4.jXBØ”£(¡V‡kéš]‹h„·°d”›A f¨(Ï7µ3˜ÐÙ®(¹Ç}'©žê5h§Ù MÌ‚'‚ ‚€ Ä4qerÖ¼HÛÞÅhà|•óa VmnV[§ív„¤{&jº£míËkŽXð®§‘Ú!Xm7%ɶ×8'[A@êCÀçSMÿ|wl©+ŸÝ²¨&ò¡© $9'41< ŠÉЈOÆ6¥Ø¿~dê/0Ñ»¤)벆BÓ: \shÚ{˜Á¼Yºå8'}W_Ö—sÚŸ˜7àxso}y9ÿÒä9ØAÖÍ<1„óç©S/ÉÙqž =É#˜1540';ΆRmŠÛ‚T½Ñ-ydWh2n}äñS=i²ÆË"X#‹Z+ÄàÿŸü/·ž fŸ'ƒÉßy1fhñÅð`ëj¼ê•I#6g ZæzË“ ‚@ÄÜ‹¤üQËþÂÔ‡w£ò£püGt3å8Ó «`©­R¦àËé8“4kž€öû¤zßãY„pþ£â°ÝÐ}’IK@€Q'¡A´Æ$( …Ð`È6ºhtê-L¹=ÒBÜòðä_îõN}9ÒåF½¼&Â+êíˆkšÚ‘K¯Lydlí‚8¬3Îf=ÿÌï_†cÓ\¬*O‹j÷¡ìqƾ±}yòC‹±?àæ'žÈ|ùÁ ŒóÆöo“&qè;YTÓD¶‚€ `"󨮯¤ì4ö­[MSM f‰…ÐXÁ‘ýè! Ð{Šó§Û'Mù¬j21"²`¢ñbt("…ÅR!M„W,5Qdj# „¦6"-à¸.2ÓÄA ŠÀ'¦+pz²+¶]D²f²©ä6Î"oEÍ«r$D§Ã1ÉãñqäB6õn4ÁLm"L+'ÁŠb8žåCX áóŽŽÎåæþ¼üæ©Ss¨T{ë#œ÷"VXç IS&#ß©Èßíå)\f­fm3%5 Á~ÆñÒ Æ5Ôó.xeʤ»øÜ͹/¥h¾C“a·q6üg»£ÌMÈÿ^Žã¡'­ òKp@IDATÖ¨s®ÝksR¥ê¡GQàx˜Ì<ñÒÔ‡§e[·Èß ¤î-œûò•©?f½V{?¼•›ëºù‘©¿ÂäÇ­¿/ Ø _‚ü•µåàã†ú¡®ürN¼?’A@â 8ºmTÉ 4Ç'« *¢¡9"9ld+UlÊÝÐ2^€Áôu‰pˤ)×`°= ÅnÃ}èLÇ=¿ÞëÝû%ßëT”Jœñ<þ`SlSø²ëÌíĹ‹yÑjÎËé¶Ü©Cqí  SoyôO~òƒówæ>—z®Àò;8_nî{.Ísh!©tb£¿Ž:~¦Øè ˜oþß^ÏÔ8% B™ç‚Ì|‚wm òþÞf³m¹nî2ƒÏì œ(èäìû„y¡ž`ñ Fn»û5UÅÒÊ6;Ýl_Íý=¤i¿¨-NcýP;¿ á špГ{A@h|ûBf«Â¢B=ì;Ö )9÷÷…GëFÓ‡FQÅä¬.Œä\ó#Pé­L†Æä-ÌöÿTSµgoÎ}jú˹÷Ö¹†ÜÏ=—Tq°ðIâù¦Û—VIû:üe–X¼vËßÿüC}…óìúˆÂ¶'OzÏhïÌÄ€ÝVâ-g³ïOø¼×£¡eˆÞ•ÍÔØü›*Ô"˜—cñ›“Éíón¾y;È5ø…)¬çsHßúðãK}šïs®û¥)zÝú¤Iv»rî‹=ü~¢Ž·ýéO­|%Þo@œdf%]–{ßÕujB¬·ƒß¨Ülî®{þ€[ÞxeÊÿ2ê¼;÷™÷K<%»@ÍH?X±0o”A D@®% ‚€ Ä3%e¥¦ÿ Q»êk+ iLM!ÑÐÔ”œoV`é¥U‡ó6†dò–=SŸ•‡‹Æ‚¸töàßÖR¶[ó×µMå ”pûÑ”zK.¶òhÇØùˆ-`ÛëÙTT©ÐE0³z£Ô[|42ÏÖ(CÓjÈc^Óç«Ã¢óyŸãä3òƒ8eb4ˆÂJUSÿvÿŸÿüí_x èÅnÃ˨˜ · *©nßq“šJ%ÕL‘è³0Ù@@M IA@Z2g˜Î0è¡©;a*F#AêIÎFŽvÇ#OüªRó¬ðxóq)‡fєɦٶø8‚ŸÃ¶ðÅÜI+Ì AìØ’i¦¯LSнåcQöEÐÌ|‘›ËAÖô¨]_“ªœ]¢•íÀ«bs:]3Ì¢m9N7óØº£`RϬ§ÚÇ{ª¦8ÒÆ‚4µA¾•ùE•læumC÷Ôu­1¼ô{”&r;ùÛA)W;aSc鳿w.F¢tÙäŸ  ¶óI6A@–Š€F¦†¤¥^BSÓ‡F¢œµÔîŽw¹ŸŸüàZ›¢<1ÿ Ck´×á\‚!w‰æÑ~]ã|xöƒ ™ÑÿŒ,/>üð^ø„¬·‘oÈÌiˆ@ö‘qÍ®)ŸaP?NÑtÿ™Õ/äÞŸg\aYŒòÎFЂ¶æ9ì ÐÀ(†!âÙlëù†öÑ.}S a+"¼ÝŠûrëÓmS]e6ˆß ÜM]l}äQ/·Ö“›«a,©N°ž£ ú¡Æ}r „ˆ€hhBNnA ¥ ó“Ð`&µ^B£Ù47,Jô„ЯǙ•´”öŠœñ@޽ïÔ½ÞWa4~Ù¦&æÅÜûÞòðÔÉp–ùB Wb—ÿáz©æUGA¡r)öç '~hA>Åóþ¢]¨¦Ù¾M-wÙ«}Ul3a\uŠš’•2Ý@ÔíLýZ“dšŸqhfã™Ô½ÈÜ^ÁÈÝÉÙé6”½Išú|…§r¯¦z¢ŠÉf»©¶LöCíûäXüÎIA@ˆWæÌï(]:~„ñiݺtºzme]í]vaöÇ €570D±Ñe#??øI]ùšêÖ™ËeÃÌf½¤ÐÓš5áÖgÜÏfX‡¬ïž«¯¾N=-#*wnîG-êR?l‘–KÊA@h"õ½/&gMÓ?Rª 1€GõšæfU¯¹ ÿ„eõ'øÝˆ†lA@b!41Ý="œ á!çh“ÐÀ1¹ABƒšLBcSUY‡&<èånA@fB@M3-Õ‚€  ¹Ì$4¨¿AB­ Ö¡ñ'›Í!„ÆC¶‚€ 1€š˜îNð@ (“Ð4´¨&×¢X¢œùìå,<äånA@æB@Ms!-õ‚€ ª54šÚ°ÉâßšMsÔ -*MJA@BÓ8rI–ŽHŠ©¡!‡­Á°´ˆ‚fþ&ØÉÓbBͶô>ùA@ÂCÀüñ ¯¹[A I©ÓìLÚÓ ŒJ5¡ñùljƒyå¢ ‚€ #¡‰‘Ž1A@ˆ4kßäÒü«ó4Þóî,:ÜPXTÓüMp:!4 %×A@bóÇ+f$AA@"‚À¾ýÛ:˜)tëÌÀ­þ„ à=þ¤U6œ×È'[A@A Ú¡‰vHý‚€ 4>Ÿ×$4`2û­F«69óºì¢¡i0É ‚€  ¡‰…^A@h4jÒ(¯±*4E1’¼eBhL® ‚€ ÄŽ˜B„A@ˆ< u‘©J y`œ¬±…‘™ÍÈáIŠ» lNgšÔÕÚ¯ƒÔ‰€ñhðEÞ·×yC :)ÏF ê¬Õú¾ÅÛû‘.B¥A@bEË1†± ¡ÉYÕÏf²·Å›œéƒÔ_ÞŸ;R±)ç€Êt#›’Ò–ƒ=ó"©¤“ªíÇó±ÓSQ1ãgÿ´B³?-m€eÖHÚ~v÷F8“’Î2ž)i8/Iˆ94…ŠŒ÷PSµoþõ—Üe²¥½M‚«š&U A ú ™ir†ÂF}hjhh*[,¡Qn¾97Ù×Z¹Óá´ß­ªZ†žâV³2R·ÓišÕE¿‡ZŽåš_Xª•–ÛlûoydJ^yYù?v.[óüܹŸ”¢%¼n‘AnbµaLd¸ÿíãÇ_šÒmäà;ÜÉîßÈ3«Ý%rÕFÀú"*åcüz=¾gíGµ¿¿ürnòWMIÕ¾3þ…ÐÄK A aÐLBk«F54ø-4û•)Ž–èCcûÅžcs9_ÕTµãÀž´QƒzÒÞ”w’=aƒÈ4\6JË+hõ–½´tí¶ì5[öLé7vä9ƒ{Ýþö?žžŽj<øÄ&2µF®È 8絿¹ç쌬V/¨š–#ÏHä–’šºÞÃ'lÙ¶»ðÝ÷«×þüØ7¢%~w‡ žš°!”A@ˆQ,Ddn”Ð@‹cšœ¥U8[Ò¢>óþ«‡þx7âü%§]¦vÍÙ'QŸ®ø¼¤"bH' îÉeó®¼á÷>úæ_ŸxUUâãÅ'V´5†V†Ç;.Èy—;9õ±Ží³ä ’Z&u¼‡Ù{ûß÷¿úøÿ=‹VÅÊû×l›³qÍV£T$‚€ Ð,€ äTWdo”Ð(Zu”³ŠÌü–Bhôë/ÿû;™§Fè¡Üã…v™ê¦Ë^“ À?pÓ…vÆ$aÊO{ßïP‘'><¾ˆ6¡4È ËãfùXNyF€†¤¸AÀúòw ¢q±ðþ5+ÆBhšn©LæC>1ÙFmv%«QB£khªnh]Þb44¶ï}è\›Íþg Tµ_]~ºârŠu™ÑïM½e¬sÆ>%-ãÿ.ýù-ç£N+©ij*ŸÇ8:™a¹X>yF‚K®µT¬ï!òw"Ú’Pcü„jlK}PEnA@‚E`Î3YYðå%Ï“sß’ÆÊà Fžòî– ¡±wÞOSÝî”W:g·Ò~vÑØhk ønËØwl—Eí;v~¦k×¾­@>læe>SÍ ×Ëõ'±<,—<#ÍÜR]³#Àïa§ö­4þNäïF­÷¯ÙÛž0 mvd¥BA@¢ˆ@¥¯ÔjsÕ¨vÆ/juP€‚ŠÌX'4L^ìíõ¸“»á3cÍLô8ÆþÚsÇð:FÙcÎ?ÿH’Œ‹ûŸæ&šú³QU2ËÃrÉ3D$Å5üþ䜓ìüÈßhl4Þ¿¨`,„&*°K¥‚€ 4-šOµøÏ4¾ Kc59q¬g¬}>Éí¾spïΚøÌ4íóHéÜÜ™mZÿùyv8ZZýÙ¨ª?•å‘g$”<ñ€€ñòw#ÚM-i³Â)„¦Yá–ÊA@h4ŲYS]ƒ†¥²šœÑ{ïÅ2¡Ñgà¯úõ£±C6B37· y:±Öâï ¥íiç^< â³Écski¬Ú·_¥­<#-ða‘CF€ŸwþnäïH’Z!4!?.r£ ±Œ€…ÐP€‹i/FíÓ­)éég±œXg&†EM,Ѹ/¸OÚuê<-g³3vÊoΕAh¸Þd–Cž !)¡0ÞCþŽDÛóý‹ÎBh¢½T,‚@Ó!6RíCckœÐh¹¹æïF„±Lf4–ÕN6[·ô·Êk2HŠ ¸/Ò““4§+‰Y¦UCc>_M,©ÿÙðk†Ü,Ë#ÏH£.ÅÇú{ˆïFþŽ„`Lhšëý‹qßÀ¨!+ ‚€ Ej®AÓø¢šJn®ib6óܱœø·Ë¡Ø”œ¬Œ”¨üŽ-_¿ƒø#éx²2S»ÓÑW˜i²ÉYsÚñëÏFU½I,˃ã„OŽRÞ႘ơ¢ÒCvÔo!;kÉ:Ú²û@L·!V„ãïFþŽ„<ÍùþE­ùÜHI‚€ q† ¨³ 5‹¢4®¡Ñ›¯ºžÍ#-w¼CÉË+¾ÇZâÁ)ì6EIs;Vwí?B¿ö¹Þ›M¡ô”dس#ÝpÁ©˜Àlôö:Û¿tívýüˆÝ뼉“V¹;µÍL£³Æ ¢SOèvñ‘Çë%„x »¬ÚpŸØmöœ7ÈŒaòÂ`eíÛ"qÌåó‡ëãñ‹åäAÞ¸I“žÿF ìN—ž1¢F›>Ÿ¿’|ªn¹bBóMu`}~}ïÖnÛGÿúd={ïuät?D±x 6¼õîb.±ÕTâ·ørù¹çïH4¤¹Þ¿¨bvüÓUq¤rA@H €Qc£…l‡ \¥Ñ¤)Œ7uB³cÇþ}ˆEBÃÍàYx;†ÆA±‘_]v:µÍJ£­{Óû3Sÿé¤Á=¹¼˜N¿¼t¥&'Ñ¢·Ò;ß,¡áý»Q¸&T_~»’ÚµÎhBã:B?©àç‰?ÜgÍ‘üφIjt9š£Þ˜¯ã痜ִt²‚yïNij=øžºÉL=ÅËé†ðG6çû×4MzMM“Â+… ‚€ 5xqC=iªí¨±ßоÜ^­jþ¼´$5VŒYxŽâ¡i|×m¨{Çv4kÉZÚ¾ç Nh¼>½ñùZSžIÖ§ ]>a$¹“دœhá[hÚ÷?RAquÍnC·\yF ‹KËéï΄֧]|úpzí“ù”–’DWŸ}’žoÉš­ôÅ·«è±Û.'ÈLcýŠ3GéeÊ/¢ý»ó.d·×=æÏn“©ËͲ/Y»VmÚM'í­kX>œ¹ŒVl܉rIŸ•¿üÌ‘ä°Û‰ëü|þ*š|ûåº ¥å4õÕÏéú N¡í{ÑÒuÛ)É夅«¶`ÝŠ14¤OgÝé¿_}O»!ÆêÚsO¦žÚéõ}¿jfÆûÓ»ÓÓPä½ê,žT‚<Ü7™1û çšCCcÖ]%Gý‚&Еw¿YLªªéÏ?Có–oÐûrúÂ5xÖ]4~dwb?‘†žáù+6ÒÌÅkéžëÏ¥Ìôš»l=ÍEY|œžÊq j¦úÞ;–¡ösÕ·[½‡gŒß~6îÌ£·¾þžŠK+èÔa}Zýâ*=^úlÞ Z¹a•VTR Ú€ˆtÿÏΣ”dÊYB+ñn°vs¨tö˜Á5K€£ªïÈæzÿ¢ŠhÝßžQI*A@Ä(3 Mzš; Bƒ:ML¥§$V Ccü@Ž–a ÿ.:å°Õ=¤—~?€®9 *WŒ§›.KlN¶bÃýÚVžÿ|þE[\;c0ÏÚ#±ÙÖóïͤ$§ƒ.;L?]PR¦ÀŒãX/,)§¢Ò2ý ÷éºm{iÑêmtåÄÑÔ Ï?“Õr<«œz†OÖ›`ÊDïÏ\JùE%ôñœ:Á®‹ÌpYõ½wu=W|ŽŸS­Š÷þ÷Ëïñn9éºóN¦Ã ÿyGª}€ø]^¾~'ÝÓùtã…§ê÷ýZ(&YóWl¦Õ[öâGNÍZF‡Ž²8‰˜Œw"®ÛË?Xq ¼4N¦B@{ï*û×;>Ȩ¨coËϧÛO* 4þ tŸ¯2ÖiP ˆ¿–…Ïö²©YûVæõ‰' Òg~yÐÄ~%k·î¥S0#¼dÍ6 ò]ô‹KÆñäo'zý³ï0ôÒ]?9»^ K›ªFÂ÷fìp¿/ÌÚm{h´ClnSWzéƒ9TVV^I—AsÔ-ÇO@ÂmÔ IJsâÁÞ÷Ð&qž†RçìÖº§uf* ’–”UÐ6hnn½r|Œ’h 4@Ïþï:<81ºì‚Á¾ÍHÕBŽ/5iŠV½MÚ¨¦(ü’ñÃiL/YÇšH&Ãúvi°*ž`MßSo|g.Ú¼t:ë¤úµ ½wµŸ++é8VX¢“»zõïž9s0ѰӔmdíŒw53-×ü¡Û9¨§5[vëÚ§ì6éÄŸm3õ{Ï9yˆyí4÷ûhcý+* H¥‚€ ´d¾Îûºµajƒ_²c®)óSCƒA|Üý>ÜuíÙºéÖQ ”žÆ`Œ5¿¾|“ ŽHvóåg4\£¾÷Ψ§¾çŠ 6'~ÿ8ñóØ&+]ßç`âÉæhl:·÷à1¼»©:9óá}Û´+ïóAhdw˜ù9Ê›¤øE î~°â·«¤e‚€ †€Ræií׳@ߢP æflîÑ1PZYéw ¬ÊæÌölcëŒT}Fš<œf-^G»óŽÐ”Û¯ÔýfxæÙHiÉnZŸ·ß8¬±MÅë§0…yõ㹺Êè*S,»ÍFÖ¡•°„Ï=x´ 32ãºuËAØfËYÐo®9K×° ,+°˜àp9ì÷Ã3è¬=b/ˆûì9ØðcàNò¯jkȰ~;œœ('Ìvß5RS—ßHõ-ï² ¦’u¥Æža&wîÇsÛŠ¾Y¸*ºâë£aøk¿wF½õ=W9Ъp:œI˜y2)/„›‘˜ÜýÝ*ª}Ô«s˜ž Õe°C~Æèmå#»lýÓ¸ÅAìKã¶k¥a‚€ °Ø|þ)M? dk‚djhTÕW÷H§fþhñÈ ŸàfßÙ$kçþÃúŒ.û£t­2ÝbÓ– ˜­Ø@Dد€55FâHhì'0NÐGaÛ¿~*ì7ɵ#¶™ÍXÞúz¡nÃÏç{wÍÖÉÇþCùº ›”ÕN«6ïÒM¼~€Ó2kg¸žÆÒUg‚)Í^úaã.=ëà^áô¼K¯ƒÍoxh¿§O×›~·j³î{0oùÆÅw@ ûé>l¦Ó³S{2pÓW Véffl ´fwa¦ª~2MÏÂ,. Û£Qg@‚5g¦ü¢R8l~˜Ø“z†ýA4¾£‘{Ò]×N¤G hú¢µõ_ß{Wï U˜,¥§ºõ¬U‰5hŒw³°Yh ‚]œ?´¾xçx‚ÁHÃûu¥Åk·ÒFhXcë9›‘/þ·úw¤ñNÄuscù+®—Æ ‚€ ÐT`nÞ$40¡Á}^C³£jõLÝ6•ÐÍP.GnâÄDd¨tæèúñéˆîľ0¿}ò¿ºÊÄуèà1¿ïk]8*Ø's–Ó‡p‚æA?;Ó[ÓEˆl¶DˆË¿–FïeÖÓxì•O?™NѦ-Xm½E'Ͻ=]'lºÃ‘ÕK½:·G$³z(ލöDQ{íÓùô÷wf`öÚ§û\5Ñï?Ã~ t€åþ`ÆR½½l’c¤Ó1{ýö´Et÷Sÿ£ŸÁ¡šý…8(›pÂæuL8qtªA·^nÜ"Û„oþ)÷Öˌ݀¶ =Ã_Îÿ8ÀÀÕˆtÇ„ãÒñ'Òˆ¶Ç¾7L”k§úÞ»Úùê:¾e¿‹PåK×mƒYwÓç‹óòóÉþkìëÅÚHžàÉŽ4xåÄQx–гoMׯ±I(û‡µ³øÍÕUŸœk¹0k“$‚€ GLû³ý©op“`ò¿óïW¯¤yË.È^‡èB8¯Íe8âãýë¹/Ryn~xò\.ëå)Œçm=‰íž’ñÉøù}:°w·‘¿¿áÜz²wš#<¡škßÉánÙg… M#–5æ­ˆ4–Tˉ^·=þ8 ¾7½ˆµ#õÕiÔÈϘs¹u-DÈ×üÕ· Ð@BÜVÏlsUeË)™Ãà†þúæ4Zµnóúÿþí/¿ÅíyøÁ‡Ø^(8Un"™Ïîaç×ÿöþ¿ Øg@¤ž‘ d‰‹¬u=ÃÍÝ0~÷؇Ìú.±¦æþgߣkÏ’ßSþç­Ø øgï½Þ|GÙLŸçT˜ŽúÞ6wûšª>~×mÙ¹ìßON¹u4ÇûRSüÞo´lÑÐ4 ‘dA e!àS ¡1T-¸†­4MÎ4UM¸ß‡†ˆ…¾Ò9f£ƒIÖXíûx™`5Tgí{ê;fúRC׸þÚd†Ë •ÈÔ'ƒœo¹4ô 7W«øÝ«-“>Ï Á$”£þˆum:·¯Àƒ‰x”Fb!ÌO@lVaÝI‚@¤§ÏH!)å‚€ |óÌ_³ ¡Aäfi”¯(¹zpòO¯"»¢QjÀwÅIÆA»÷P>}Å`oņT^á¥^ÛÑõçŸBÉn= …+Î…ò~¤CXHpDÿîtí¹cˆ‰Ø®ýGè½Kh÷£”Ý:ƒN֛Ơ—WéñÑ3—Ððuà°Ñ£ö +'ŽÂêëõË ß(ÿàgmM÷ŽmiÒ?>лäÛ|ªJ—MAý»çàYö@[sþÕÅÔ±]+ªôxéýK(]²! „&0œ$— -ÅW™i VS`ì´U¨Ä\X“””€î‰£L[MWM©Ï$;±õèÔ®Fëv;uÍiCãFôðÑËÎÕq§ 냚G7£áÚOÎC³—®£×>™O];´A™£iÑê-úÎ 4óWl¦Õ[öÒM£‚’Rzó‹4¼_W*€vˆ ËלEv¬„¾D%%9 äTÓgÀß±˜.:íʇiÏW VÑÀ^èÄþÝ(#5™†ƒàü䜓hÕæÝþº÷¤wÒrZ²v}òj‡™r§ÃÿÓ_Ÿ íZeÔh· !àNò+s“«¶¥å¬ÖS7<ÿœJpNU5JOqëÇ™iÉúVþ ‘B@LÎ"…¤”#‚@L  Z5+%AŠT?54^ŸJ'ôë¦Ï(¿;}qÐMvx_ºpÜ Ô äj´Eœ²2RèÌѨ]«tÚ»‹>;¾a{ž~mñš­˜AïAçC–QÐâÄ©>ô‹òOÕ ÐLä—¬ÝN­3R©=4„µS›Ì4<›´lý؈¹Ym„ä8\DC.‚r¿ 1„€Ï¦¥Q•‘™¦Pq¢™&gª-ñLΘ€pºæì“hò«ŸÒ‚U›u-‰aAq)½õõ"Úºçe¥§Ð±ÂRJ…öÄšºdûã1¤UïÒ¡æ1üØgËÖ=iÙºæíŽÒ9Т\wÞɺÙÛü›è¢qÃè ˜£)§î¥vjך¶ï;¬ï¯Þ¼‡¾†ÆæÀÑB (Óõs^Ÿ—¼Ð$—V@SdƉЯ5$ƒžAþ "Ð &dzí <×>ºò¬ÑõÞuÅ™#è£ÙËèóy?P¿îÙõæ“ ‚@(¡ 5¹GE@ñj©FUX}Eh@€L“³Dô¡1º”IÃé#Ð'ð¥éÙ©½qšf-^G»óŽÐ”Û¯Ôýfžzãkóš±ãtÚ]}kWŽ7„`- ›¯Ñ_÷1¨qØÜmäÀîôùüUô.ülzuÎ&ƒ¨Òðð=A^:BVæaì?3 GÝwãùЕÓý{W/–}y\é"QYSc2Xóʾ POÜyµ~é"h&áygšš±6æÅI7Õ¸µŸÃúvƒ†F¥;÷ÓÆj\—A Žÿ¦ §4¹WA ª(¬¡©NÕ&dÕçêݳ© 4ÕÉjºV}6AöX3Â6ÿ?œÆHÇ K(#-lº¿ kjBMì/³xíV êö뛽é¤cÏcú9—ÓaHrUœ[µy— ÷`íLÿ‰51…ðÃa­k€¦/Z]C¬ȳbýNZ¿}q=ÆBˆõÉPãf9@€ƒM~3 eç<ÄB’ iDCiD¥²?½þÙwôÛ'ÿ ÿ•¶4qô :x¬È¼ÌG{óËôì[Óu‡Žbv땨¬¼’Þþf‘îo“ž’ ´Áð³ÉÐópù¬1zîíézÔ(ŽÅ‘ÖØÉŸþ¿Y¸†¾úîGh—úêþ7†Â}³‰`¸‰×ªt!@DS´ÉL¥A½:ë ½†[nSÜßío 9›¢Lî'Nz_!P›|êÙ)fûÊ/mËÿ/„¦å÷¡´@˜ŠU›œ½ëÐT ¼̇ÆPvà5Œ<ˆ07{ézšµt‚:àUÂÈÖ«¸©Üæ&5lh•vª ·z˜Z9}:o%¥ƒÜLÄ‚ªF '"ãE+5Wû£Õ¾Pê­ÑWsWè}uÖè4aÔjÌ<4”úýžè=ý‰Ž¼´_&@@Q ª\›£œž4›R‚‘WUÒ:Ê™‚l†à<¯«²vë^ú/]-„6æ˜3›¤  #Øz•êÛ •ì5‡æ¡6ž”]±‡>ž½œf/YO7\p²®µam^s¥hµ¿¹Ú‰z¬}Å ‹ÎéýYU_E¢|)ÀyA@ˆ' ఛ„¤ÈjºOµ¸¶¼üѺè´áÄ ~JŠ˜Èp”ºYKÖB[ò•ØÓiSÚ©TàhÛäB2Q:àê¬2½‡©oÙjzþ½Ùt9¢õ -¯•ÔÔ)šíoê¶E²üãúªt ýãÝYtÅ™#ºzP$«J貄Ð$t÷KãA î°°©Áih2“%ùe~H$ÊYÀÆþCù4cñZ²af| låÑÙ¡›¿Š;;Ñú”á¤*‘1- Fb&PËÓÆÑ€Ò•ôá¬e¤jª¾F“š¦ÐÖÄZûƒÁ*Úyý}ušÞWÌ\ q4ÐAMÒOÑnks×ßô¾¹[$õ ‚€ ؘšÍ\”³Þ•íAÄä,€ç¨Ò㣩ÿúœÒS’¨G§¶Xs!mÛsH¿sǾÃôÊGsa†TF¼¶ §þë3*Çâ™áÐý˜åëwèyWmÚM…ìwÄëŬX¿“|ª$¦5¶*Ö¸éA#öÐ˘üê§”žê¦áýºa¦w&€ØpúqËnzáýÙÔ ŽÈ½:ûIŽ~AþE c0Ïþ2?nÞEŸû£NfÖ¦ŽŒ ™1ÆDŠe`bõÉœ•´zónÝŽådŠÕöG²M]–µ¯>œµæŠ{Ì…s›ºîx._M<÷®´M=(@U«5>1A$åý÷}Xå¤B¿E#ûö›º¿@Jå%BÖM»ò°&Œš“‘t*4.ƒAT¬©C›LºáÂSuÍÍÆy¸¤ÐGÑIƒ{Ò˜¡½Ah¶[³·ß%»~®k‡¶Ôäf󮨲ßÑÁcE”‘šLË@~ŒÄÄçâñ'êšãœl#‡›Y1™).)£÷f,×ÍÌX3+‰e)±¥Ó[ÓSYY¥Nj")[¬·?’mm게¾zóË…T(xM-o¬—/„&Ö{HäA ¬ëÐ8|¶ ‚p5˜Ï5IPi‰W´4`_„mZr5ïËJK®qG·œÖæñawµ“xjr´+U&~f.‚¦æøsÆåcÐÆ ؃qH§@Ö:Ø,MRÓ Àƒy63«¨¬¤¹ËÖSQimr‰ªf¦vKyöSòýš½l./ˉÔÚ‰v6WF_±‰)GÇ‹T?5—ü±VšXë‘Gð0MÎHƒ“°I©&4j¥Z]V°å$HþÝ;Rüaö«µÛ8–íñde¤ÐE§§¿þw¹°›˜Õ—8JÙ5猡¾?Kw>±w3¤·ž}âI郙éºdî/c·ûç9ï#ûÓKÎ¥œ¶YôàÏ/ ë/8y—kkx°z×µgéê«W·€_;áÕµ3Ûv Òò „f®i^~-‘+ဳ3µ*=@Û÷¢>Ýrt­žÝzÀ‚–ÖþÈ!Ùô%é}U²œv ¯zviOáôSÓK»5¡‰Ý¾ÉA@ŒÝ0ÓSû¤°]:T iGû%M¦þ@Ã蜓“ÆOæ,‡ÆÆÏ ~ɸãîÈcüñú|z~#;üó‡MšxÑ=Žˆf$ö‰9÷Ôad¯25cÿþ°–Çä4#$=qçÕÆ-² ¦vÂãA@‡ Ò}¡ð¢ñ:3±štÙ ãzDØëÖ±­>H5êYKl¬öK]r}µvÛ½¯Bí§ºÊN¤sBh©·¥­‚€ ÷À2ÆtÒyËrOÐ Ö0 ?¥Ñ4UM€2™ &Õ—¿¾Ä]ÎãËOv»‚©Rò†ˆ€@ïC0nnÆš1¯ântÑÌAíôÁUéz­•>v¨ôÊŠrútceH’8í ÒÅAóv4þZóÚ',ã‘‚b]n—Ó©åPÂ8‡ÚþÙÈMÑÄ´ÑB¾löU~14®þ Pú)dâäFñ¡‰“Ž”f‚€ 0gÎxs’ “³ÞPÉš©ÒQÑЋa_¬+sâ€nÁÞ&ùc6·â&G¢*¯¨@0€r*W’–øžoJèÖ/JhãQ=zz ¥:ñv†Nëê {O®t¢¡bÊm:,7˪Óy¸íoHÆP¯E ÓPåmì¾r››ò œ~j¬Žx¿nþøÅ{C¥}‚€ Ä=;vX¿ÓC"4˜<Ê3²œlšš`Ÿ^XSR|!ÀØD‰A?x`èS'; |´î„ÆKç÷΢³z9é“ •Ô³•]'8ƒÛÛé@±JÏ/)§¯¶øµ7¹ãSh=îéœi£«&Ñ3 ËèöQnj•l£™?ˤowzèóöÃ\?d®Ðåfù¹¡¤pÛJÝ-L“+Ôë>²ëëO…ÓO¡Ö/÷Yüâ¥MÒA@çÁbÓÜ š–ÆíRê@IQ«}h4ÒÄ䬌äTâ ÀäÞ0¹òz½ ¼¶KhÄ`p;¿Ù`‰G#ìc^º0 +4ztv) íËÓç¤ÒÎB•ÖôR›d…îL‡JUzai-ÏóÒ²ý^3¶?Ì*¡cåÉ 1ƒÜ<óo´%s&ã¾?Üö7ÅS L›¢\¦ñœ˜ÓOM%SK*WMKê-‘U(vV: £–¦hG«\hðKBhÀ[.%†É•Pïã‘gP ¿Ä$íÐP‘µ}4w‡—Fä8¨s†nú¤ˆïõêš™‰=]tY—Nh¸‚T¸Hýß"bÄ)¯D¥>^–í \ùj ’Ã1e ·ýºðþML#ܳ8£¯B5 4 JÐ!4 ÚñÒlA@ˆ?ŸjýN|ÔcBQl09ó/ħ(¢¡±@#» ˆ€1[ΆCd«¬éN·h‡þôm­ö…Svªß…yÑêE/×öQÇ´j׿åÐÈd&È*ÍìÐ/ér³üF[‚™ù7î §ý¦0܉&¦lFÍ¢r¨ýT³ Ä<²þø%&ÒjA@â‡ÝçôV0~ ICÃ&gÕpˆM5ÁííÚ„ÖnÛ«G™ÊGT,ö¿àì`¸v­ìNîr9)+-…Úd¦Ò ^©kN›ÚÙä8‚˜ƒzôÏžóq0éo‹Ëtë=»ñŒSøÈüP^½?oWõk[Ò”„µì3‘á?}°œÜFIƽ\N(í7ʉä6ª˜F²!–²¸w ¬-§e7@„ДdA Ö¨T4Ó‡FÑB#4ªÍ~”TÿH ?®brD§ó2³—®§X³‹ìƒCçr#vúT‚û0¹ÕÃäÐxÞJJ¹™8zM9€œXÜSRä0š¼DZÄG‡`BöË“è©ïU×ÝIí¡i($óvDI»v›: _9LÏòð£‰Ô 9Ò통Ëh.Lk×ÉãH=_‘”©¥”%ß|-¥§DNA@AÀîÑœÕ,¡…m¶ÛÕ£†–¦)BhÁܸ¼vëzãË…TP‡m,¸x e€¾ð"¯1ÑTÉ¡y¨çeWì¡g/§ÙKÖÓ œ¬km‚1+j*ù¤Üú¨!¹ù‹búóÄTšv}& Ñ ËÊhÖöj Mí»gÃ÷æª^šs#¢œA“sóçŵ³$ô±`šÐݘ~’A@âÅ©!N«¿)˜™­dÔ@k“ÕÌ#Et¬*‡hh€Ê¼4sñZúpÖ2*µ§ÓÆ´S©ÀÑÖ¼Ö”;L–¸:ëŸLïaê[¶šžo6]>a9z ñŠã’ÂC œsŽV6àyã]:^Ž ð™¹äB8ÿ+TZÉ]ÕéίKªªöŽ âÙÕQ¦ÛFE–©‹ãrÖ"Øö›¿þš#s%1LËj–k¸×”.6äÛ.6ûE¤A hÕT(!†mîÿÙÆ"T¬ÛœÁº&míUƒd9úzz‚3¯¡f.¥CŽŽ´,u\³‘™Ú"1‰Zž6N—ƒÉÕ Èe„ê­WŽƒC€û¹úܽä.©Ef»§>7ÁDŽ®–ÝJ™«¥úºõþYÜUÞD{Mi‰m>gMU~<—+„&ž{WÚ& …€ÍâCª††ƒ¹’%0@‰˜Õññ ÍÌ>œµœ;;ÑÚÔ‘¤*‘ó“©£ÊFOqý,ËóÉœ•´zóî˜qânTxÉ ‚@¡ <¹U˜BëõYä 'F’Ih**Ë…ÐX@5v9jÙ›ð™a3³õ)ÃÓ1±eyJléôÖ´ÅTVÆ A†fž‰!!*ñFí-ò…J9†š$¢qƒ€õÇ/n% A P-šP£œ1nÐ>˜„FUmBhj=LL8šY"™±ÏL´53µÄÓåÙ”<„RŠÐìeë蜓‡°ÖM|jjUÇñADÛ]¨Ò|v#´ò®c•´ãpí,Ò5/‡),Ô䀑~›T%;#˜`¨EÉ}DÀ‰çšcÄáæ#XôqEÕ‘î¸Lr"*¡‰ ìR© ‘GÀ¦ZÖ¡ CC£qP€* ¦©¢¡©ê*C;³vÛ}ÑÌ#ЀÄrÒåƒBfýö}Ô­c[Ìp(ç–®¥ñÒ¾”²’c= Léɞ藞臞Óò¶ι~ÙÏ;À<‰Î{«ûÉæCc±Pl/']ðVõlå ®J§+Þ+¤u‡ªfk,%\.øE&•y4:ýõ‚ÑßfÜIÙi ]„²wÂ|¸­½½ÕqòE²þòî““éfÈvýGE´|¿ÿ¹áEOÿqn* Évè>TO.(£w×V·Q;Ý¿á:?7§ý»À;ó6¦ê˜gà­‰C_—•iTXd£#¦g™5G`û§uuÐïÆ$'¡ÙzÔGü9 ÅJhÎêåÒ}iy9ß×E÷’L÷M/¡>mìôÓ¡It××Å )c;¡»“ Ê5JG¿éì¤ïwÓC§¥Ð-XÄ´±”‘d£§ÏN¡nYÇÇ˺÷d7e%+:É9£‡“rǧÐwXÜto‘J'€äóPnC)#‰¨¯å_•Ðg?ɨ‘õ¬žIôôÂ2]cÃZ›Ëú%ѹ½]ô¯•åÔ·­þ M?W¬m’„ÐDw©Uˆ#€ BsÚ?®¡;K€ÐT §¡©Úò‚ÐT ~¯/x`†õ¯þ™€ÙÝKú¹tBÚ–û13}6f¯y0ôÕf=‰A“Éù®¤›µL:-™æìðb&Z¥ ú¸ôUå/à¢)óKõÁ_u_¿çƒ[Uee…NhXvnCs¥ŒAg¶I?áìŽIg9²²³»«+Fë=Ø4ÌÔ²„àÒ‚ÐÌw´m(k›b«Úª;/}wß¡÷Ÿä)ö6øt8å·÷ÿ­_Ç>mOê½áέȋA@IDAThæ–a<¤½ƒþ0«„ŽA£Ài?ÎÿuQ)í+Òè˜9Ý>’5 ¼®-úÙôêôgÃÜé֑ɺF`úÖ†åúÍ1öï›-úͨd]“ñÞÖÖc1Ñ»ü”u1þ§+éç§QLóØÔ,”é¶Ñ¸nNºýËbJu*4åÌʧP…·ú_wØK_nVèÀòû]ˆRWeJ[Wùâw߃@ÖüêdÍ“0GÂcrÌé½*Ó³›aF')zDï ^›¥fA@âü¼B‡`&ÿ¯­yøŽæP÷!\ 'ð;ã7'››&g¼ %±Eoø|_åKp7f£y†ùÅ¥åzq·rÃl…è¯ KuÇóSº8if²?ßTI3·yt¢sý7ÍÝYIo¨¤­ÇªX É£1ó‚„Ð툄 G Û]€ˆaÜ™¡Ž1è[U<” :î×á¦ëß¶Ê¥£ vï0òoÃýúÏá6¡m6Ͷíš{ìÎUË£nÖÂ#Ñd|jN¯›—CÛa¬ª?Á—Á¾!yÝíeûªõNsª´íá±ÏÄxØÇÃH_ƒ¼¿Î¯É;­‹‹Nêä¤PMµì–ÂJØZïA§-09Û†gõ¬ÞNbB3±§ßÜÌjv9Z&ú¬aa¢H:Ĉ}gؔͅwˉo<6A³j?ù_WþPÚÏ$ãb–×ðìŸÓËWWTû´pô8ö/Z³;&ñc»:umTCrñµ‹PËòÆeézV±ê/À•Ðð…Ž¼¼ Z.·® Ò3ñ¯ *dw2´@FJvu[•¦Í8ΖûÊÿ6#Õ…»qM¶u# „¦n\ä¬ -Ìb›# ü¶Wÿ:Ù’ï=²ìÂìJ „]˜PÏÚ}Uçä.ïï Ìi#ȺZJvC³¡+Ô2mã1qˆÁC³+hÁn¯nÃÚލeuî_ÈKW taZ=pšl8-£RË–CÚòÚ9,¶ÑcÀTŸ–Å 0Ç­Ëb)·Ñ]Õ[ ú¼»PëVE±m†ÉÙ&ÅnßÚˆ–¥Ñb£Ág’ê'¡¡Ê2ægIˈþu=|iØ´ûÊH=ZU“‘îpTß|482Ê2º]‘÷é¥ýßÀTî˜];ØE%s3nëMÃÜÔþE×}THŽM¦GO‡æò-ïñkÀ`›3¼ FlªÆï§Ã%ïá€æÄ¦›gê'«þý Dê øñÜO«Iš5O}ûlWRÃ9ŒÄA%fm®?Œ{ëÚ&i”âά뒜 !4%ÙA@ˆyà³n‘1dB£—¡Ñ~l»ñþîG³U?ŸÀÿ 2P=ä Žlö |c¬‰ýdxæ¸{Vu7uÃ>öøš‘*¬=jœ rëEky¨äh¼å픳ú]0;ƺ,1¬czu˜c#bØÜïÒÎMë6¾ýÌÔ;!j>Gð)ćAÐ[ª–eü¿·»óŽ ÚpïÐå(«Ù@ãã†JÆNäÐ<Eºc!·CÛrí 7uÀ@¼æJ9ÀÓ˜¢q¼Ë¡™ã„Zðßßÿz¸h:ÌÎÚÂ$m(Ðkýš;=c#ÿX6UPjRš)w#·4xÙh;oCiÿfDåc³³ë»u‡ÃÜŒÃ*ߢóç¥TXŸ"¼'Ó®Ïе)Ï-®~gú"ú™¡À(‡òøth=K`öèœR“ø´NQèÛžYtLÑ ¿£QL̹ŽçÎM³PFãjã[6ÿ»„ˆƒpÝ­ñl:Ìä"‘ô¾ÒÊ)#¥}±HÔ‘e¡I„^–6 ‚@B  *d'ãaQõtb­‡†g~ùuB£x¼ á ÃȤ&Ri>¾žÕÓIs¶;ôAïÏÞÑð ‰…lz¤‡†(<¨k,mÁ=ïÌ(!{ŠƒlÐ Ùœ˜µ‡'} ‰ná$m¬Ëƒo^§…g¨yp^­Kª.m5|ö¨žˆM_}2/õÇû:”p ‡ Š3ÑŸ`·KuM»gæÑZŽ×&-‰¶!Ê\Ï:àêPå³Ôáª^šs#¢œ¡ßƒHZíîs¬ÑRˆÙÿ¿aðþˆ„fM?`íœW/NÓûšÍ±f5¥Ëz/ËÆUÇÖ 4üWEȬyÝ7îårBm?×Å‘ÚnGpƒi–M¥ù>“€°#û”Ý5ÆM_ÀwÌH:3ÕØ%&GüÔÏV âÑRÁŸ†£Õ&4|ó h‰Ç1ƒú>ƒà š™Ã°—âûÓw¥æÚ6¦`!î}Õ­Cë°ú)Äêãæ¶à{5nš. A ¾ÀÕ§éóßhW>4~T´êHg6M4Á£ÂQÊž<+•^¾(M73[€îŸ4lÊôöšJzt\2-ûuMš]J!$t8©>- Ö¸pô¨h&HeÀÓk>„ ç²kîr ÿS«³Î{nsÒ×wõ ¯ñ4ÌÌø÷Ín§¬T¬ç³ÀlÏž€ k®þ H7‡*‚&‚ƒÍÝøI‘ޝAJßYãoŽ€£€ýaf ¥`ÖÜ“X6'Tm2St¹ÙìÑhK0å÷ðýá´Ÿëüûârýc­ÿ·Óª}iŒó¯"2Œ4àùcÆn£Û_~V]ÞYo-íçŸÖëše–½-j×™™Ÿ}ì¦Æ„¦®Þxkêð'ØÄ}•ärRN›tôQhýlñ˜_M<öª´I‹ &S«m™B@CAèfóg[•µhBÕÌÔ5¨2ºƒg—y@Ç>3<ˆµÚös°þÔN3·UÒ,|2êð¨×8f—6Ùéžã¢žÙnêÞŸV.}1Éú´,ƽÑÞözÍ-ð ;$f43Û6Þ7äv°õÛ+*Îî÷ä÷aØZ#å˜#ÙvÕÚ;j yY+a‘á/Lêp8('ÕNÞ”é=L޶WË¡·­É 3Ösƾ‚a3œƒ%3,S+hhzædèò²‡ºþP$Ûo´­¥nê¯PÚdôÕàÞÌg,Ô~ ¥þxºGM<õ¦´EÅŽ(gUã% þÂ"4 DûiHU"éÏ•áCé‡Ìê3HÙ<È­=0nè>}õ„jÓ:™2Ò“(99I<5tOs]ëÿõm†´ïŸÿþÕŠ¯ÿS?>Šùék@XRðü>¶ñ¾¡ÿ†™Óv»†ãÆO7þfûâpÚFŠú¸Ms\»þÞk@vò•ª÷àüƒúÕþ34~2ã —Ë…“:·r#¼0V¶/]M+ÒÇ‘ª„÷ºÕ™C—†HÍmŸ²Õˆ˜e£¾9™º¼LÀX~£-µëjèØ¸§¹ÛßLñrûª/úÊírÐО9:QµŸâ“pÚ˜m85Ƚ‚€ Í‚€ÏcÐ6[χ†,‹kbPÉ>4’°è÷ç— yêÇþF!ƒž_›†…D6ºŠ4™›q~DjzÊ)ödûÉx~þÜævîü Îvg¹§qkj°¿zý}×ð9—æäõn.áý¦H6¨Ï Ld’’\0rQ²;‰ºe(”ªÒ€Ò­–ÍÑ&ü§k¯¡k¬"–%ÕWH;¤ê2²¼,7ËÏí%5wûC‘±%Þ3 t%¥ ¯F÷nO))Ia÷SKÄ ’2 ¡‰$šR– QD@qX¢œ…©¡ÑìX\ÓH ¡1°mè(¶ŒJMyÚ(À[®ó^’‹E2AR.Ãz,ï——÷ô•k§‚’ï+«(¿ƒó2ñ*ÔÓ¸Zhhl†¶†úuë· ZœŽãssyÄÎ ,óãõyËÊ=DN0 ¯ck˜\11p'%‘;Ù -—›Úg$S‡ÚzöÑ ’¥Ä3îÑJ\7ËÀ²°9a§¶ºœ,/ËÍ3ÿ¡š2µ„öG ÷Pêõ÷Õ2ôÕ^Ø)¦ ­õç*Ü~ª-Ky¥GSUŠŒ÷¡v–¸:BWÝ)DF@Qarf¤0}hœšÍ$4š˜œ¨Ê6 rJ?yCQ´nƒžYÂÊ¢jcàÿò^$z«ù½ªún#MícCà/ÅeÿŸžk"6@/=ß‚ƒ‘šåŒÍÕp=//é’ãBŽU–Wäç`’0›]1!p::‘IMM¡4|RS“©sVe»}:‘8±xžîSFU!ÝÊ~'Í×eàt½;dTÉ—¢ËËr¦L¡Tëí¥MѺ§º¯öRßìTÒ½=ž£ÈôSí6å–h>¯×êWÖ{P»üX;šXë‘G€¿éCƒ9¹°Œúmª „¦*b–ÍQ$¹M0˜››«õúŠ}^m*NŽ3rò g@ü°w4ROÝpßP6=«‘lŠýKŸ¦>sµ»³Üáó oµÆ†3C5³UsØX‹cÀQyiÉ¡¢² ¥´œ. m‘Iÿ€ÞF.7ÊHKM¥ŠŠJªô`,òÓIUÉUXNûÊŠè„âtÌ‘­G?;âÌxš à€×.áp¿!‹8`NÖ qv†f&=¡žÓRu9Y^–›#”q;BI±ØþPÚ­{j÷Ð)zujÑ~²¶Ÿw~î=æÄ”õz<î ¡‰Ç^•6 ‚@B"€ßIÕPÑ`*., üÐ/w[zA6昫nÈNöæ}-„W6Ü3ä D'»Гk'€}KÍh³„ NIôŸ’Â5÷÷ru.žÜe8uЦjÉ ð»u÷ š5à©5zH›¬ø<ÿkÓ&}ZÞáâ£V4§ó®d­ ‹¯èÉ0±ÑvmÚðc§½®Y½e/4˜ùNh‰Í®Ø¹žýgxFÝ2ãó¡ 3œøzº«œ"<³ £U)DðPUØÜäƒ=h$’]óR’ZŽ…=â™_rÔÑ.Yƒ¦ÈEé 1™™é”‘“ÈÉò²Ü¡š›2ÇJû yZÂÖ†¾r[úÊŽÎÊÁšN=ÛeP«VMÓO.ü¼sÈó¼Ý;à\\kfŒ6Gæ 3J“­ ‚@Ô¨´˜œaœ–†Fo„?ÒYÞW9Ùf³~^þ…„ÀÉôãA_ÐaxCª,†oÂØþ¿Cļå·t,eQ—ߢx†>¹áœ [å-ŠJ+Šm=\ïu“3¾¾þÞÁá-§õþÍxÿÆÿãÝý7bI<pƒÐ¨k–-ÚzÒÄs –®Ý–Bƒ×"´dj)@Øá‡É 6zòöa’æà åÔÁã¥â {ªTKÉ«•Š.äªk “=Ô£ 6y™IvJO¶Ãÿ­›¿±f†ÉLVf¦®¥a?ŽÊŽvƨ£÷åÇkqÎxŒËq¹B—Ý*DD4XÙ¤*…éCÃ¥(š†µhH'4^òvÂ)!4Uð»a3“¿Ÿ—¦/®¸­ÒУ[J|äß𻡠û?½ús›jp»~¼¯ÿvì…zÙ¸ñ–AÖ?»¶lœ1¯Ü¼+útíà¯,„ÿqIÒ\¤¥rxGÀSØ?…ͺ’à€_VVs4˜·Ô´¾”58pÌÖórØïP“a.ÆÇX»}z 1BL\tŸhhX3£›œ™Ú™ÐƒÔ–5ší¯-K¬+`ëË÷{éë-Ð'2¿F_­ÎwÐ'ÂĬuZ³ô?çk¶ìQíÛû.pâw€<ã«Ð…-—š°!”A@ˆ 'œ£H¯aúÐø[¤ìÂï੼qTwÿ9ù,ÉN¢—/„ V|ÿ÷%i˜¹%šøF jï ?ŒM¦þmí´3_¥ÖWÐÿVÔ)w| ÀZ$=àqFwm;ªÒý3‹igßÄ)Xb-?LÏþØD21@™á•\¼ ¾ú|f×^}Îz{Ú¢ô~‘Íå ]yɃz&œ˜dø«Â9Ã_¥„¦„ÆBãõz¡ÉaB£²ù~O8ÿŒúXãÂfd,‡uͨ á“õ›šñu–/’)šíd;𲬉Y uë Ò?z¨\…c£ÝA¯oL"Èæ•]3uߦ¦ê§J𜫚qlÞ'¾vïB||q4ÐqBhG. ‚€ Ð’°ÁäÌüÕŠ„† Ã0 ÈÌ(S- “XÕëSèËÍ•4²£ƒþº°Œö² ÒáRMßZISæ{iH˃cSèóM*¬P©M²B× J¦×~(§ÉóJé·c’õÏï¿7¦úÔ˜…æA“¦÷•¥¥ÅeË¿û?ÛgÝúÆçßi¿ºütèUBK©`² ïƒÔð>¯÷’’œ¬“=`@e¥0@­ò³1M(¼†µ@œŒº¡m¡áuq°&êeRÃZÀD†Ïóu&|O$“!Cs¶?’ò7eYÔŒQ‡l,lÚU¡§WÒ¡ ,jjwÑ[[AxSÜtÿééXL³iú‰Ÿï}‡òiÝò%ÏäçæÍüXµ4M ATËBUø¥rA@"‡€Ïi/×¾¸HM÷%«p …¶Y èiÙOÈ]¨„2@d[úõ‡y\Õ áC³í/ÞXUA©N…æì¬¤;OrÓÉ]ìôÍ?á9 Í“ Êô¼mRlô«áìZ2dEþÐjŒÚ] žAfXÝ¥~ø~þêVíÚã‹é#Ò~vÑX%TM ch„AfÒ`„tfrÁš™ÊJŽ~ÆÚØ€bŠAã?0ƒÔ„‚ŒÙðÍ` ×ÉZ^»„É“§®µñG4‹´fÆ*s4Úo­?–÷­ý”ƒ~z±›þøm­; r MÍgÛTæPé/ç$¡ß"gÈš&3ËÖoWvoÝøÆ÷Ó¿\œøÙgRÏïƒÿK;ñš„ÐÄkÏJ»A á°W¤–z©j_¡”°°)[ám“Є†*FrböÛn2GãlðÛñÝtËH7uÇÚ!»`rÆÉe1ÚRE|øüÆ#>j¢P§t›©ááó%"-91°²&k[¬çãdŸdÆÈ9Ž;ÎŒ?És>ý`hwäûÓ~rÎ[¸>5Œ%ûµèä~4:‘‰™ŸÌø‘ÐÂ%4»Jëâ÷ÛaRcÇóÂuëûèç¦ÐÊp½u%£®æh]õÇâ9ãÝòcCÔ}ôêU6šRóÍv<šè¯Y;úÍ×eô Ò óa'ö™a33ÖÌìÜ´þíéï¿õ ågžŸ~ø}B$ ‚€ ´ξ÷¶²¯þòG]RL'‡+²ÓfÛæ« K‹²šÐX±LuÃÌG-°ž zŸGÕ–L vyé§Qëd}ûóÌåôhUíëÑ=ËFňž´Z›`‡øu»B[%˜zb(/3pÀyðáA3|Vm1ιŸ~8çHÞþC£ÆO¼òé7§e îÝY5¨§2¤w§Ö©á,“ À²™kK8€á7cÕÌD‚Ðp}Ƈë4‚çÐÆfMÍÕþfmT•1œŒþà-÷ÓÓ¤RÇ…ô:4²¸H ÷úèÆOŠuߺV0/ 6ñ:3𙣙qÕë)Zùýü¯øvî÷(‹MÍø¹ççŸß~Ì™)ìÇe M\v«4JEÉU¿ú‹R2Ã7ÛWÏõN:ÿ®-lvR<üæ½Ë—ý³1>í×^5(mÐûkùÇ2aP2R’È¡•ãÓ"ÿ$vúge×(øÑ)Ó¨ñèÚ€Ä,Q)axY‡9YNš®˜D?äyéòþI´d¯×¢0k¼R–Ï«“Ô¤4s€Õø]->‡Ah ?¯ü>¸ðá1mõâï×l^õö1gŸªÏë‹a:úUKONÒ²2S·6€a¦Úä¥öq(Åó³gMµ­×¢½_»½µ£-_SÖ_»_Œcþ¦íîHK*ºèÕ¯9è¥ó_ßOç¥n 4…ׯS¹Ç£å”h¼h&0U|>oÑŽëç.úæ«éðãµ™Šðágž?V "IA … 2ÃëzèSòéŽClv2¡QrsÕ¥´ß2úâ‡çC¬¥ù‘÷1ñÀ„?Ý:´¦…ëvé+µpuŠürÞ^]N“ •yäô:ù_ùô¯ôËÝtÌÎÞ^SI«øýlŒ9üëõC’hò)tÄgÊ|Ÿžx%yž—íˆp±¼.†!à%´Øœ¬Æb0ùÙç™jK²ºË0ðQËËK}s?û`öÜÏh^ïAútëÛ¯oJZFVRJrºÃáÔßä—$DõÔ9c`Úž6£ÛâÝTòÕdú ¿Ÿ·OÞŒ<·çkSL^¯§¢¢¬¬°¬¸èØÎÍ×o^ýÃvÜÀš&0¬6æO!>üÜóóŸþ3h§>[Á[I‚€ q€ÆÛe 5­¸)•å•LhŽ…Õ,…¶â‡W'4(7a Ax’<§M:¢I9)Û³‡%4ÜS¾-£§•é 2–c˜ñ²2zue¹~Ìj§ ë?*¢ô$…Š*Ž¿^;íc–ý}Úd¦èŽä,»ÑŽÚyãì˜Áb3žöfÃdÆPod‡lпܲvÕV|x`Èy|Ø•$4 mϹcpë3}-)6‡ÇžâX›}^ëCNy-Ù'»¨‘Ÿk~~­Ï0Ïr0yaÍ “™üª}&9†ÿLð_¸¹¥%19ki=&ò ‚€ Ð~ žÃC˜þ ?m3‹ðó0ÑvØÞï|m§Á]ÛPÅ–<Êô¦GÛ€¡(«5˦g¥PÈ ËÕ šž9ºo‡!7·!A’1èãYjƒ¤0ØÑáóü~°6ƪÁ12îÁeI‚@d8üÍó‹ÕŠ’£mϽë²;܊ÕÒþêÿûµ-9í…£ßþwk­ÚŒ/ ƒÌð3Ìß$ü ÚÖаf†‰MÂigÐfÑÐ0’A@ˆ˜œUýüi>-ìHgÕm3~M±í78ÙC³ÁÄ€£J í™Ckw¦¾e«iyÚ8R•jþ ‹®3û1Ì»*!&²i>ê¹\¬…‘“Yµ.‰?–ÑŽ:+¿“<4($?ÆÆ1yf› ®¥Á–'x ³4!3CRÓ"ptîëK4OùÁ¶ÞûMâ:'aÿ6rºþ|tök›jÕn<¿LfØŒŒŸk~†™Ð0aBÃæÆüá盯óóž0I44 ÓÕÒPA@Hð«Ç?pzÒlጪ· …&,¡a@õð´ 4¼öG ŒîÝžæ¯ÛGJWÒÚÔ‘:æ‘ú÷裄–”® T_! ìœNɼØ"ÖGa™z4®„«ƒ@ÜY÷yÀÇ?#ú™0 6¡I(°B{Úä®p8¶àCšÏ÷Ûö—<ð9\mð%ãnwÎ]÷ÙÝ©8ôÕß×£l~n9ϯAhØœÌ æüÏä†ù¼á7cÜ‹SñŸ„ÐÄK A ±àš?© 4²¦~•54Lxuöî9­épA ­Û»—¬OqMYy;¬™a2ÓÖ³º·rQ§¶äæä!+Ë̲'É™êxh2ÁáÁ›šYÉ &1Æ»’¦E ÑûyŽV~Þzü/þõÿì]|UÖ?3¯ç¥‘ =Ô’€E¥ ¢ ØPwÕÕUA×OwlX–uPAPwÝìm]Á R¤Yh - „Þ[HÏ«3ó3/óòIxI^’—äÜßoÞ½3s˹ÿ;ï½ûŸsî¹ êZ)¢ÒbØC3ôa1œüòrÂBÏ®vгìKjèY&C1t¯Yif°¿j`B£!Á1#À0MŸ54"(µ^Cc1¶«"#(Bœ2}ºHÞÏšTÕí™k)0àŠ$ Vk$ǵV7S„ÓÇ¡Qìµô®ÖššêÊPY~Z3Ó½$ ¬rtÀ:»Å†C(ÊG2’¬$3ÉN}h¦žYÔÐlšjffDd42Ãkh õ‹À¹åÿÌ1Dw¸-¬÷è¯ñKÚ 9uhØ€›þmŒŒ¹ûðü‡·—J£=éшv®Ý¯_ფ5&4A2,#À0@'¬%Úž蕬Ökhhß™”±1g°Îָ˺)}û‚v(çÑ@ÈÚØêðŒHÌhÊjµ‚Ãá„ÄN­ÀŒ{Édž*‚ËŠ~…\C œ6´‡Œýݧ¦&XÐ>3äš™¼™‘=îX×B„ö¨™ k¨U•‘d%™u¸“|3&41í ¶¶ šVFc|ZŒ·80uÀÉÏžÞ2%nŒ.6~9>ŽHj ÜÔ}àç]ž_>îÀŒ1Dj4-F̉Àh$†®5ëÀ„¦Y?wž`š Þ548M«5¡)ÅçÆ­)í’eZGÓ, õŸL¶È)€ÉhTµ.— $I‚β ‘f8[º¢3*Á )² h9D3HBàþnuŠL²í¥Ðj §1Èc •Eö‘FCááx©A ÉJ27Cs3²Š‚61¤É "/¾Æ7­fàF >Øÿú„=_I!ëŒ?a{­ðû©oÑnIÏ9»FfMí­ij´ç·>Dj4mî¶Ñt™eF  # ¨®;Õ*² žîÇz®¤ºJ÷¢Y z]5^- ’Á 2ƒ‘ˆ0:èPlƒÜ";äÚÜàpÛÀ¥ØùNÀnjûË`ˆ0é Ì¢Ãu2dgQ53Df"#"Ô4™›QVÖÎT ?O«„‡oÖ'YÏHë97s„"»× fœ6àŒÄtU¯×3Fìž’¨‘šú©Q´Å„¦Q  É0Œ€(n¬V:ÁÆwΑþ•ºD.A@Og¥•*J×Känò·=Z˜#(V.´4Eu€¦]&\„o±Ø!Úá—Ë ’,!¡‘ñðäÕL« ”f.FÞÖHHëyP[¤¶gö¬™A ifT“3¯v¦Y:¨.¼œŸ²&'¤Å¿‘6Bp kÐÔ79ZH²¼*ñõôS’v A$š  …`Ú"€/ïóJ©hUÛú¨¼ È{µ:‘0õ D½"D&(Ñðœ™1‚ ׬Ømv°—·ID„FF^¨!Y3´¶HãBfd$y1#ofd^FëzBB,êA¦ftŸdãÀ0 =IÞ•8/c¤[’Âß("6.VÄÏδ穞Woê^Z&4u1·À0Œ@½! ˆBšš©í!ù¡Á­$3dukªVIª·ÎqC±  ¦‘ÔPšMˆÅ¢’ràt:Ñ jhÐ4Í—ÐT—׈‚Ö®ˆËôzr! Û$RCæe䀈 ]§ûDf¨ F€h|d<™¸MÍFJŠôþª·À7"1è oyâÜŒ«2&'žo|=ª;‰™ÐÔ¶\3#À0õŽr™<­Q|«BeÌÌ9ç¦%#¸jº¼?ÎÜù£C´‘[³D4WÈD4—ÎD0ÈÔÌét©.i ih𠫪¡©©–†ÚS´%$ µGZÚc†È‘ƒªµñx4cÍL³~<¹óMZ7Óóõ´±Š è(@1ãop¼[–¿óVöÈåOt§ý”8 Lhø1`F  ! *Bž¶þç¿!4D^Rƶ&ÇÝqN®Ë?ïLÀ4/N-}n4-­mQ ®£QÍÌÐÄÌCf<ŽHCC¡6„†Ê{Úó¬Ù!R£C2Eíªi$:š<”—#À4~²¦$ÿÖóõô{ð'd!þ‚ˆø2ø€Óþ ÆwáKJ¾ñw³V=`BS+ø¸0#À0Á…€¨CB#ye ¡ñÔ&¤ãiwJKàNĈ õSÓÖ™ S/Ò˜ÍÌŒHŒFd´Ø§¸_Ijƒ‚ª¥)ÕÖP{š“íº_•q&F€hTdMIú:þõô)hR<Çß‘ xN.ô§6ªŽÔ‘°LhêX®–`†@@’E“³ÀhhJû‘ñ-”V$×Ñ”‚R>ÒH ¾DF;WoÔðC#5T\k«†Uq1F€hdì™’ôFüœ´8ü]ù³*º¢Lé9{ס¬§zÿ«‘u%àâ²ë“€CÊ2Œ#ÐpXDc¾Öz LΨ>Ô  †¦4@~ @¤Ã£Eñ˜‚yÌÂÐLŒLÅjpøÖåKnü…³0Œ@@àî)I“ñmÆ7ZWdÞŒŸ›q³vÞ\c&4Íuä¹ßŒ#Ð4èß¿LC¨Nê29+ °†FÂcF€`êéèG?ZŒ¼]„lô4‹kjdé‹^³3Ö£Aך ˆ`š#0lØ:7*ŠÕpÿ/¯¶ «yme%ÝmÚíE-ËsEé´óÞkÙ]N1Œ#ÀÔ'w°)‚ñ&$5Ùj› Xp·«%Io¥5Û™ÐÔ×ÓÇí0Œ#PO Ë¯–¦X_ǤºAØK]Àúg®Àfgõ4žÜ #À0åØ;5þœÁcðúÙÒ{­p۫ʼnog„–ÏÛΙÐ4‡Qæ>2Œ@³Bxz [Bh@¬—¨ÿ<˜Ðh`pÌ0Œ@ þDò~tØ>9ÚÔæ¥—»Dþ¨Dið&™Ð4ø°Œ#ÀÁûÆD—Ü:pµ+Þu4¨¥áu4–kbF Fì~*q3:™¨Fñ·ÅÏÞõŒvÞ\b&4Íe¤¹ŸŒ#ÐŒNk•E%FK×6t:¯†@f MmåòŒ#À=S’?Dá-­*E€ sÒ®ÓΛCÌ„¦9Œ2÷‘`š¸‡£—Ðà–Ò#4:èÕÐ  ¬¡iVOw–`‚6ý’¦ |?«2¢CÜ×÷‹Ä73:³Ì” M ÑäºF€ÎhbÈ39룿j?®¤±SÝHšÚm°õ9š¼3Œ#ÀTuÃ·Ñ ÜëiŽSi4=‹v»ä/†®UôÕ¯­ñ•`BÓøÆŒ%fF J¥Ìä òLC#,Z$áßd–Ö¸[v²Ù™ÇŒ#À40éI>- Ât±ï&QE¹êDjúßX¬ziž M½ÀÌ0Œ#Pà>4^“3|K0 §>lÊrßúë·Ä0Œ#p)²¦$ÿ†ûÓ¼äͧÀ³=^Ïá=o¢ &4Mt`¹[Œ#Ð|Ðè59Cr0 !Šæl[Ë•¯(Ksª"ð )Ȳì=$I‚Ú¾uQÝF€(@æÔ¤WðÚjÏuEùÓ¾oe·*Ÿ¯)7 »º¦4`ÜF€`.…€¬×§jq@YKh¶øL£™Ð” "t>q2‡óùÅWTN§‹6$õoB»àgŒv$LF=DXC * ½º´ƒNm¢AE@×­êáÉÅŸŒ#Ð\Àß7ؼ×e“wàR þ&µ)qÚßG1Œ#ê^4] Q‘Èìlo @@Äu4ò£T_s%4DH3³zs|»vœ3´ƒÌ¾ 3-«Îxåë[BjèµP²µ5;Õõ:#.ï› ] ÇG_œÕ_’Ü×㤯#æ´EKÁ°K•áûŒ@C €¿·…‚"œÀ¶ëDÝŠÿüý¹ÔKÉ‘:©mIü¼ôû7lÀ_lÜkYZ›‰YS{/¸TÙÆtŸ Mc-–•`?Àù¶w"+íü,æW6ôt¶ІA ‚B&gÍ.™IË> ß­Û®’™ ë€Ç€ÈÉ‘X °äç]Ð*2zw晴®aˆVƒƒR±Â“OÎ5˜‹×ëuqËî6ú 1Ë‘á!‚Ù``땊qã« Œ€Ýå’ó J”»ˆÏíŒI/Î8)ËÊÜv†vÿš>ýuÓãŠDÜódÒ¯ñ³Óæ"!šJ÷e^C§‹iMEùã5&4qÔXfF€`.z¹9ª™ájÐŽ—È^­Û}û>’•’òïBT„!qj»óÖvíû|s¼ÉÙdW ‘»Ýÿ]±Šuaªf¦²¼ q4Eý‹ aÑêmе]kÕiy@#OhÍ<Ÿ8ñþ§^m3ÛÞe¡m¯.m•Ë»@r·vBˆÙĬ¯™?  ûê—¸Ä}ÇakÆX\¿7û”tâ/_øûüãÅ••õ¡MËÐOž+ºý0vÇ<.›4ãßU–¿±]oö¿nmÀX^F€`üA@àˆ–Ó´t baútÍR´ºÜNW³ÑÒI”$=™íVì5'7˜™™†ù˜45{-ÉPXbƒu©Yè}ͩʬÜòù›É9Íwô|ö¥É£iI›V1Sî½þïΑÂÀ¤.€d¦™ÀÀÝl ÐóJÏ-=¿ôǶŒˆEGfËÔÙ{5Þø¯vCv ÿóVv“`óLh´Qå˜`&„šy îP“3‚IЉ^Bƒ§Í‚ÐhÚ™ƒÇÏBq‰NÚõCò•Øpåuº\ÍQK£š˜á îþ¿)c ÓL$3ÊC·Œ¶. ꇗ…«ô<ÓsMÏ7jjf?øìôÑXA…¤Æ`&ãÕ4Êd…m”=a¡F€`¼àzïõ&X}_ »41’PIárJªSÍ䬘ÑÜFÕÎŒ¼åNz.[¡kæ‹&uþçbô¼ãË‹Ø{žx†Ê¡Öx=}ªœïgü9ñæù°¬=驲tãHñÓ8Ɖ¥dF ÚIK_àò‰žTØfÏëŽÑöjWäS`Æ»úëžؽJ»4`Aª+elë_° ò¦ƒ¯Ãåá}Dé¦jb¶uݧ•›ø8Ñ´ìÏ?©kfh¯MÍp#gt”«ÑÃÙOx„›EÈ·ËåoWz®ÉÞÄÉŒöšb•Ô4G MeÁ—+6«Ú‘{n¼ vî= ëS³ w÷ö°rc:˜MF: '\Û/^-þÁw 4ÄFy¼±oIß?ü¼^~ôVذm¬ÞœSî¹"ÂB`]J&¬Ãºè<Ìzñæô­£Â¡cl4ĵm?mÉ€ƒÇÎÀÀ¤.ª ¿íÜ ×ôí _®Ü¬ÊÒ£SXˆijG§UmÌ—ÿE%¸ºOw@3*Õ|’„tºÜ°xý6ØžuJNÁ> k/xú¾1b1b=[`ûžÃ ×ë`øå½`Ô•I•AÓd¯ã÷Ý÷;Aé2{Ör½6˜`¶Ó!<ŒYDEF'ÍKKH29³\¶ =­’±­Ô,#À0Œ€_à?Ø^-£(ËDhjæÏïo˜9_œ²ôŸÙŸÄX}+Q@ôšÉŠJh|osúК_2s‰ìꌤ:dæRõ5±û¾8žãø nA± Klê§Ë»‡Miàö‘W@§6Ñ*©°;÷[C³páº}¹_¿Œ{Ëß­×éÇ?ókKe ‹I[ÇÆ¬Çúo¢<²â†QY»•äëŒ@`¨|VØvšLmFCÅÓ@(‚/¹9v&ï‚>ÙØsø$´mÕVlLƒ¾=;ÒÎôä)n….íZÁ~\C㠸ƥ¢Ðµ*ÎåAG‹IÕ yLæèz?lsù/;ÑÍó èÚ>MÏz«2èPÒ" íßSÕþP^*UP)H¢(¾‰¤ðcÏ©ð Æ¯–Þ êˆ54A=<,#À0µCÀ`0x54X“_š“ïZcöå}µßmÇê”ȾU‘M:Q)3;“v߬áÂq½#@“6ߣÞ†ó KààñsÞÃí®Þ†FÝÐL‹´)'Ïæ©k`2óvË-Ið º_Ы VnÊðÞ/Ÿ u/‡OžS5);³BÇ6š–•ϯY ³šUçÑül5îAãëÎ9cÿq(¶;`.ø'³2+jp´Ð7¾#lÎØ{pOÒØ?“ÛL×Ð\à@ƒ§Ê8RˆX„ÌPUgáZ¦î=æd ®²@ܬ˜š‰p,#À0Œ@íþdɉe¯‰%XKšœEÿ87<êúÉ竪Uq˜Wî*ñäÝ5s¾ Ã¿E®ÓµáLцÓEL )Ó&É÷{ëÑᛥs&Ì3Ì{Œ#Pï¹Z˜þÈ-ZÒ¯øŠÄÎ…{¼üîwèíÌCúÇÿ¦©e—nØä``ÂuW¨„cüÐ~ðÕêèÓ£ÄF{´*¾Ç4 íp½ËðË`Ľ|oW™¾ëþrÅtõ|52qнc¬7?yO³;ÜðÆç+TÏg¨Y€Ñèú¦!}ÑÑÁåðéÒ_áÿ®TïYÌFxäöáÐÊgýŽ·"N\€ÀÆÉl=æìú^œH7ÐsåýBé`Lh‚ytX6F€`j‰š(K_UÍÎúPU‚d#-Íæªª6ÉE÷½ûÉùÙÁµ!nÙ"è$+ÚÓ‡ˆŠ‚¯CËÜ÷ûþtZê¸Øsèx %­wŒ‹M¼lÉ©Ê_ÛV%@½7¨½vá¾3ÅN¤‹:B`Æc·UXó#·—½g bthL´Þyþ~íZâZ•É÷^§LFÏTqì5—©÷oÆüthaè€tùœ zãŽè9Í·NïÒDyèrùkƒ/ëWõÉ+å#MÍì—ýc¯‚ËQS„¿9°~[–º¡(y5 1ãwŒPÍÔlèÒÙj1“Gg~" Wt¸AR ¾Èº£÷ìSOìz*ög0~VUoÙØä¬Þ æ†F€hÔxÍÎYö{&íÜÏæ™Ï>ÿpñéçþh?0íÁi»Y±¼ú®Å»š—ˆjtÖieÜŠ2RKs ê^3ÿ 1ÖúÿÛµ;óÀå"%F zhd¦z¥››”—C–ÑY<^'×Ò¹è:zß±Ó° ÷Öiß:êâB{Ù¹a2S½1ÙýT"¾ÔÔ=hÐì,Ì!œ_½ê?7khêsn‘`:A`è‡ͧνdŒoïœ\k™Æ-- JåþUµLÄo-ëf*:~p¾ ?ëݿ݄&h'u‚ºOÁ1-;: X…~·—ž“ƒ€7µ{Í9¶Œ «Q€o*¶F~’‰­õðÜ` ôl©ƒÃy2|“é€ÏÓ<{L§‹dèÜB„aqF8p^†§WÁáü2­˜¿˜Ÿ„ÏWN€Þ=†Ã€Ñ¼B@IDAT¤Û >n8­x!¶¿ur>F !0 ðàøkaÓ®ý¸çL˜FuͨA5úykÈ®mÛÈ?CÞ8ƒT@¸£ÏƒVXŒ M0ËÆ0Œ€Ÿ$ÌÛ5òTNÑplчë‹|‹‰ ì’ñ/‰ŽÞ¾÷ªJ¿ú~Ë0YνA‘•[ í…w ¢ð­ 7Íœö€ý€û‚âŠÉ¼DpØÞÁ–4¹vûø¸È¾ßºÐ=Ò%šÇ‰[`i¶´ÕÃÜ68^è!%çJ$X¹ß ÿØà†áHZžKöº À!C´E€;-ðÁ;ü}} üùJ‹zL^Q3«Yvž£?Âás?BDxKèÝõ6Hêr®)èÙ<{Ùäè…n›éàP7àÆ7X³JhðýúAsZh}MÝ´VûZ™ÐÔC®`C ÿœ=-‹çë²®tð§¬É½W^$Œ¢ßàT/£¹ß„FrçlÂB'Aü/èCŸöÇ"Ü‚ˆ‘ý¢&è€oŸL¹1Ý;+ñT/»KnÄ8¨ßê‘ÜþrKK¹™Õ•#sUÕáÂל™ç<ä/ ×ÐÈõxO8]¤À';`5àΤ‡ðø@3 ­KWìóž3¨¡™ý«gþ"ÂC}ÍU5Sá=½âFs74sÃ×ÌnŠmç`óîùêÑ*2’»M€^nBÓœ˜ ëà‹Œ#ÐüØ;µwVÙi™ø{ž€¿ú!9rîhDá»`E¢þyƒ –‹`F†@Âëé÷ƒsˆp«²\;Û–<ùÕUAX&ˆâÂç&JxÈŒ¥ø^Ë¥(™5‰@DF Vô˜d–=¦aÚµšÄCã ðÅía°ú¾˜~­U­Âˆžš´°¯”øÐùž ¢BhVv_ËWUl’íÐ1& nò.ôë~JZ|ºgóöÀš”¿Ã¿¾¾®¹vZ nwÅ„µªvø^óDàù·¿†{4ÏÎ7ƒ^ã¯iiùY"F€`š¸QÜ#¸ÿ üsQdIšÿú®Wâç¤=ÒkNƘ¤yi ýçŸ) æG³3-È~™=óð drÖë­ÏpÃ?C¿ïOíF À>5»aö’óÃü,Ú(²‘¦¦Sl šÿC´ë´ß2Ó¢´<ƒËqM„Y3š™E#‰9S,ƒE/Àƒ˜“µ áö^&襃[{š`Ëq·Z‡¿ªòa›±-¬ª©œGË$@—¶Cà¦kþ Oܱn¸jjpˆ´”i \îصïKø/:xûëðóŽ9“_¶§‰¿ís¾æÀ™Üx÷›uðÍO)zR(,¶áþ1›‘ Ÿ…w¾Zƒ|žEdvX¼nÌÿz ì>p>ÆÍ997™S’·áïÆ¹R)[%ÌË Z¯ Lh‚ûYbéF€¸¬©ÉS­‚±‹^_RDøNTÄóøcÞGR¤'ÜnåÛ¢‚œœžsÒ¿  ^BƒgûEhaº,€ø÷’b[äu]êDËÌÎDéæKeo,÷‰ÐÑ:2ÝÈ ÆuÌoÑóì |‘f‡çI+óÇÐ〽¿Íö3ÃÆ#= AÚé ,¤žtÃ=É&Xrw8´¶ ðnjõLÁH>ZïnAºâ‘ÝCjÕÚa1¶4E=Wà&”ç1Ï£–£ñÅD<:<ÆEö½Ú·ÇÓá>ùú–~uè?ÛàõM6ÕDÇŽÜåß)6xo»]='ÇåC>’ {¾)„0“…Ž‹ï—Ïï{Nrµ@ R\k+ˆ¸i"ÉŒ](íC™6F+f…«’W“çv@ú¯!óð(±ÓP{ ¼NÇê­Ó¡[‡‘Ôù6èÚ~èD£–…ãfˆÀU}ºA$4qm[ÂóÿúJ݆`@M2Ü2¼?ôŒkv‡ µ5gà…‡n‚¶­Z€Óå†E«.úéj†è5†. kÔÜ¡Jª(Ã0~3¥f M0Ž ËÄ0Œ@  ·š½æf\ÓsNúÔâœG}«°¯†m¢eº_¿ÿÏNtf<7Q޵¶è#êŒw¢©U_EqM° gÇ»Ñ ô• ÈŸ?¿­×Ì­¯ið¯8ùWͰ­vÛon[¶­¸¯P,ë•9СW°V`2è¡»- DÅCýéŽÍ…Þ|1NIÁ}iª&+Õ%3$O÷’4Õ»Y—Öá '¯l¥rS.Ú´¼ ®»âïðøí©p+:èÑñú H‹ŒžÓöù¾Yÿ0üsQX¹å8qvÇ¥ªåûM³É³ŽÌR—ØËftŠV{]Œ×h3Ì0Üä’BDh¹÷-êUþFA„Æpÿ±!Ó´ ÂÀš ‰`è9/3$i¾=„DcÐy)7ßÂÿŒï߇àFh¿øÖ1ljá¹e¯ 'qêܯ‡¬š=«+ÆÙ¾y*K—jW ð~ÁŒÂ/¢R”rÙòeû¼e„Г&ðnE/,Z$¥Œk½eúÕ)Ë2™¥Pº±MCCÄ@¯×ƒÅl‚~q-`cöYH(ÙÖíZ.Z— à¿߯J¶U.€1f0› `ÄMIf’]ë‡oþÊÒ¨C23Z=ÎÈ8ø=¤ü ÉËvo»3_5C#S´aqÜ•^äâ#Í´ì£pEbØ’q¢Â­Ð—ÜÅu5¾!:"÷< ‡”ÌC0¤_O67ó'ÈÓYSöâÍ“¨ioƒ¿é‘_½‘"£;çà Aɲ‚ "–†`àC çœ]óÉýʵ8IMQÿ{\[Ó§Áçð-ZV”yR{µ4蟫w÷/yIPDôz#O̶­¥eƒz 5uR¡ªñ-Œ–×Þu4ø'H„¦Ñºè‘qýŒÉd„­" ;štµt‡Äâ­ÕÒÔ\ Œ—ÖËk½ñRÙÕû¤™!9ZºN@{tïiŵ>FUV’™d§>Ô$˜ŒáÐ/þ^¸ïúïá‘ñ?ÃÕÉO@d(>n>!·ðlØ1w<š;Öñ‰£»õÔ‡údádD šÍúàX³%n¿îŠJ{xÛˆþ°vën˜:ïPP\½g»ÒJùF½ €/ÂRµ†\’ÒOKSÌš` –…`?@ï3”jN¼%äô×APú„†µ³qR[ͯ÷¾"¢§3Y¡ÍÑ‹¨J_{oú™@ P¾©»gÆN{Xú°2ïÏúÂJçyÚûF1£YZòÎq1û,9}ÐÏf‚2›frFäÀl2Ùb†m#ÁåB;²ÜЯ¨²-½ý^SÈNÒš23#ÍLlˆZXÀ‚ò‘Œ$+ÉLLÎ.%WdX'¸æ²©êqôÌfHßÿ5d^ W¡·¨ÙrÒÚ£7LéÑ[ú?§CXîtŠ›Šz.›>=Ãû[:tȾëè¡Ëñ-ûSH†n–$÷YP„¯DA÷Œ¬HŸˆzýdšÂkÁ$(KìúŠV@'(ʵ)ãÚv°äÄí~cŒÉd‹ØÓÚÒ~¹™$Iª õ‡HCk› í!NïËÅÄ;Ô€^ßj÷½°(›T¸IÅÿ{öA×N_ñ“Ÿ9½õÆÖ«ðÚõ8DYºÓ3}óÔ0M´@‘¹ØîBc6URÚªYÌ«¥A’ #Df4’â!;h’¦'Ïbvǽ6Ün·JvhÝ’Àj¶Vqv’ÚÒÈ•ÝG›MfÕÌŒ43Df"#"€ÒDrÈËY]jgÊK‰›**Øo^W–-í[þ5lNLlõô]ʬ3Èø (ã ki9'H¸®Kí´)š9_\„ÏÒ'Ï>ôÂÚØµ|Ý|Î0 „€°eU;ãt;;cš M 7Ë0Œ@“B@'ꎠ¹×?t¢¸Q4K›Óþ”œK¤ýhò¡`ÀsÒ§ôœ“&£÷³×Ë:.lÆ ¥JhÐt€ÍGe÷ªNM{X&2‚MÌK`íÚ¡ú{×/DÏYŸ?÷°ôM…¥Eáœù_O÷dA¹£Ú/+]Ò™¼üâz%4Ô¸˜#(V8¤9¡5*dÚeB;+jijpܨ¡ñhph 5%6Dd(§2’Çׯ ¶ÛóhgBT33Ǫ̀&g^íL`œ¨ûñ‘WP¬8Ž<߬g•ƒ•xíÇÙŸÄXÝösw fñ>Dd^ÓU„â3ùš1>0sÁߎ"¹ùT'ê?{æa§_¤Û·=N3Œ@€Ð(B#KJ¬}K€[¨Uu¬¡©|\˜`†C`÷äÄŸ±õŸi?©D¹É‹ºM®œ›€×Óñ؈s`Òxƒ(ˆ[$ïFJå>V½%*OlÌÞ0׉ä>ûôòsW¼6#ÖløîT nb@¦ ñ[ÇÆ^qù§òGèr8NÚmä‚ëDê3¡ 2AAÓ˜ÐÂ{rçlBYìHh쥄†´4’„î ¥ÑÚ" ™‘y´3k'V ±¨™šÑ}’µ¾‰½¤¸Ò··¥fe¡L½ú®¥½¬8îE’w’ßEÜLsË®ih’¶AþÄ$[¿¬ÈEx}õ­:íiõ%®¾éêÔã›W#´tÒ¾ç¾ù‚!]ý†~ù#ƒï¸û8ùÓŸ<µ4: BT MP  Ã0Œ@õH˜“ö¾,¹F#؈äá –ÔåOt/]ýa}ºð°­r~Í7Ðs3ôþmn{ËU“‘yPµÂ̺‡¯1š[Æ?meÞü6vÁ~3VG·ž_L›$}LvXt̶ulÌW8»û£§™´4 4Ê©£‡ ŽþsÚ¾ã00©~ÿ_i¢¢‘5çm‰B,•̨œN4;C Mé:mb[]ë3¬^ Z»"jƒôzÚÇ³Ï ¹f&- 9 "ãÙLÓ£™¡2õh,°Â‘½Y»°Mä(Þ£BžyØv oÌ¢cÖÃåhćχr'âÓÊ[@+°Ò+œP4ÉÍ2A? m×yé7ì«ð÷–«Ç„6'ÒJš¸#'s óà 8_P ù¸çŠÃ‰¾9š†Ò!4Ñ›jÞ¢"¬Ð«K;èÔ&Z}iŒësœËw£Îû_¾Á`=×¾§(­k‹ õŒU"UÛ–AOD«„Uâ÷PÍ‚¿óªÌÛ7™Ð4èÜ$#À0B &:ô±utÆý^ÊBVYò¢Ôu“róÑ1eIÀ¿&}žû¹àüõ¢ŒU\˜ù®þ4 ú« êîvÙÏ>Š«n-lmqJµÝ«ÝZܲ`‘·¾]—¼„F¸+ãŽÄɉ‹24§9Þ|ÕHпª²~é·ÝûôÍÙšq MýÍÚK¥ ¤æ Y5+uéLZ25s:qûRU;#©|gO“}õ¨F_½YµI+⬮‰¡¶IKC'"SDb ªÖÆãѬ>53š8øhHé)›öã5u¬Jc-[…ñs][ñÆV4e|rcöÏ7 a¾7h‹¨ê7Œ XÛÍhªvsÑÑý¹h’ö?ЋŸN{н±Â ëá"'‘ÒÀ9.X·- 6lÏ‚Âb'N^ÑyƒhÃ=oóqÄý„BvG€[¶ÀÒ_vA˜ÕÃú÷‚¡ýP;èÑÆÕ'±©ïþ×ì ß±úáçn5Á°‰0üòžêw¶>Ç)@>C_f øcåIÏ'šà –„`j#PžÌøU€ëh…ÌÒp}¿LëhªEhp9þ{ø‡fFµÃ¿q‚ïEAÿø´‡]¸“4ýÝѶŒÌà ô]|rCêØØC8•Ãv£mö³7àåïè^ 5¢ÒÙÇ¿L„?e9Ý;ÆÖ ºÚ!â@“ZÛ¢ 4=S‰Œ:Ñ%2ãq@_ 4 ¬IÐ&@žö°xƒÖoË„»F] IÝ:¨d·>ÈlCö?`@ÖCE¾cõýz¬Ò߹Ǫ½ú]­Ò„¨yä²’þ„E¤ÒV„&€`rUŒ#À4 @Çp?ÉŠ/‘«½ŽFTÄÉ¢Q¿÷éÙnᙨS}œˆ+)cc>ÃÿÂÔûŠpÆ5%4T5¨N–×÷õ¢Û}üÎ/~ÜÔâÙƉFƒŽî×{ ²A„‚&’d F2=¢Ií ÁjKh¨-í ö4'Úµzï<6ètI€c ËnWá¯Ë–¬ÆKDJèÐHMÕ f¬(<ù@9x‡ŽY˜»(nç¿ßãyWŸü]å—çßþñŽðkÞyáÛï>7îË¢æhüh\ɔЉ&…kS³`ùoéèâ4ĶþÍþ¾Ñr5Q²Zwª‡ÝÞ Îçƒß:`Ü5}`ĉê3XWÄ6ú_Ž >­h¬þóÕ?¬ŒÄ±ª«q 4(ø]ôübBh€¹>F€`ê! èu›Ñ¯®Z§Úƒ«WàÙIÒRÏ<Õÿ’8Åÿ°^(-qcæ-í¢¾=žã ÞœôJM”ÝyyçŠv§n™'\~åËŸ,ùEyèÖ!õnzæ• © I šüѡߴv­:1Õ¯­-í¼!cÂþÄÙƒ+0¡ ®ñ`iF€(¸ùäèÎùƒÄ…F­baÂ"šñã»\Op î¡Zº.ct«ö‰·~YÝ“Æ{ZÍÍÒ¨4Y¦E ¶ÿ÷Ù7‡÷fþ'ÖÊ«-‘i=‡ºE€0~åÃ%2až¾cÉÚï¿ú [TÇcÒÒPšÆˆÆJYㆯ2r<¯ÊjÖ{†¾Šâ\wB:Ñ óÀ«ï· £ý(íO OgÏO”>?I[l iïr ÏHn Rå aËî÷àÃ¥cà½%#aóîwpíIõŸÍÌŒ43ùE°øçª™if‚%,ÃøjõV(.±{<ìˆÔ4†þË8ø#‡6V_¬Øv»+èIä+ñöKPªõÂ[®Lhê\®š`†D`èZEoôç£ ÝGä//EÖiç¨1¦¥“÷©WÞÓ+_—Q1‰ZšàâŒV¸ó¦¶ñåóøyNbš$“}Mœ‹ñ(Z¹è¿ß¥nøé­£'ξþéð¯/W+›ÓíÂ!0–„)aK=y¶hÓêå­ûþëµØ‚w,0McBç4F4V*‰)ÕШirõ‹n¾Æ-BïkÛ1{æñ·”³scöúÌY t´N¦Ò@›sΟßßà›aæ_ŠÏM\þ÷K)×oX%Ž?u\øR’B.°;—·׼̄}=¾\}¤ü\n³ê@¦VäÉŒÖÌЦ©¿lφ"›ZDþРš™òRÓÛÿ¨È%PPâ„u¸¶‡ä%¹IþÚ„ÆÒÿÚô±¾Ëjc•_ä€5)»2NuÙ‹©Ð»@­;é;TÁï· A%5 Ã0Œ#p½gïkíÖ9áÆ™ƒÐ$`ЩÔô$Ì”…îO#z52TPtk%…¬¶È1€2TMèÃ` yÂe/~‰Íèi“äÿÓªí½ôHîÖ±­—àùmtͩО#ð<¥«hvF¨š†¦Ó&<ŒÛ~^·%+5eß•£o%Iî¡èu+I”f1)‘VÁl0”-BÁüCÀîr)yùÅ mš‰“[Ar»ŠdîþeÓÊe¿Úí%ôæ–‡ÂÒ˜Ò¾uFm–EɆþ_ç.lo±åÿ÷”™ñÜD÷Ê󼆊봉í¶hÑ"}vÞîŸ×Qþ´‰ÒXÌZ >‡>ä?;ѩڡ»ð'Ï gCðÖ4º_>¬øJ:°àý{ÿòXï¤wBÏ·`ÏÑ}È‹OnPúèÙñHêz;tŠ„U]üˆx´žu3Ÿ¯LÊÞ#ª7³úpP¾o—:'™ÈÓÚoizÒ¯»êG‰cMCcêMûØ崱ڰ͈z©>j3NuÙ‡½M§yÇ&4u 6×Í0Œ@sE çœ]/9Àöölq£ O `{Æ„DU#R—ÑO=¿cÙì¿åá{óHœi¶Yþª)~Ì3Ž=åóUç|Ö‡æ8Ùé¼É îÝдè$™•ºáU«Ò)¸' (*¡Á¶Ä=iþVÃ=iÈ„‰©_H@oëiÆ&â¢tXóíBšÿÔ=ù²ÎºÇ'XBÃZ˜,–p½Þ@ćC5p»]G‰­°¤¨ ïðÞ={÷eì<ŠUh¤’TD`òKZŸEcBcCc¤š›a NI‡Î’pË–Ü 8¬BÂò>iZr„màópà•/¿&‹úOЋe ºmþ¥Ä•ÿ’¨¡:£áC‡¥š”{u:ýŸªéâE"$ÀØÁOÂhÔÄì9² Ò÷‡Ný†yUŽ…$§Ò|¥aÖXHê|$v¹ZFx–úxµèžÙŽªƒÇÏ‚Í!©®™/j0H.„†ì„³9ÝàÊÚ=®­×_MÖ]5ÆþÉ0ø%†6Vô\uC×ó´N®&ãäWcµÈä’ hÕ¥)<ïwºU´(kh 'WÆ0Œ@à Û?yæºaÂËþ¶.Óåe¯ pJw“ZFt ŸڄfÆ»ÆÞ‚â&d·(NiC¾Ý ÏL|á7j`Z½öÑ÷ò„e))™GѦ1¶’œ»ñÞÇÚýjÄ4Õ&Ôd>MdF{µ®‘WvÚŽ=xÀ{ôGùèÐòa’C5 Ì [/¾˜&- ‘ÒÌ¡¡…Ô&’CdšÆÈÃ0Ñ,®ÈëŠ^’2Ÿ{XzaÚ$ÎéÒâ±FéªClÞûóh|žžÇçió?‘ÛæïŠé8©Ö»\%͘¯ûí(H²Ûö샮X¥ßÁ ·@R—ÛÔƒÖФø2~d†¦…ÂâS°1ýmõˆî­æíÙqè„P\dïRÍÍöãÄ“6ͤ}fª ±ÑÉpÿ KÕ,’ä€Ü¢#° ëNÇvkt¢âÚ†ýÇÖ\²8ÉF2f:qíZáZƒêʹ&e¡‘ªÝÿK Yƒ ‰i Äõ«ˆ6V™O@g+rû^“qò«±Zd2Ú :{)¡QÖÐÔJ.Ê0Œ#PHf<öc¥’ÞH‹‘%¸×wC›²p¼œ¥ë¨ûè ¬ER¡|“= ó̯¬þŠ®¿úžá Ir-À…Þߊ¢þ~ÏÓ3ç}vÒôŠŠ€0};寨·qÂú eÀ=ÿŒQM ×&ÖLk$Å—èÐu ¤•ñjp0M¤†‚VÆsÆŸ•! ÌI¡Wµ„¯¦! ifˆÌT¨Áë0iÒ‰’YŸGt²¶hU‚“6µÞ]ȱ9R÷¦Fz\‚ÃÒWæ·ˆ0FZŠrT@s³ßãð™6щæ“ÌX ¼#§ÌX ìÆNˆ‚áÏš)µáO ‰…AIRS9iH2¾‚݇ãz«2Oâ§rv«¶¼ [ -GA„9òÑ+µN,Á7éÄå.¾ßðØœ¹Ð§ûÝ0zàLØ{tîÛCU/ti7†\öŒ_„†d#s ‹q Ì&I]tNoÿ«ÈÜŒ6‡¥zÈ»[uû_ÝöüÉߘú#WMòhc•“_¤âLx×dœjÒvuÊ:É ¾žÀBø½»àÿ¦:õÔU^ÖÐÔ²\/#À0 €@ò¼]]œnx·™éƒ$å'”tE#q'–‡" ÕÄE\G#{þ—` Ñ®û?ók æ½ÌóžæºþKˆþ]›Íõ¾ù Áw÷}·ÝÔæÚ~‹qACÍ5¬ÙAÐY;§É6Í8‰ÐG"4ôŸ§š¥aÌdA¨Fа%2C aNø¡¡Ù9m- aO÷+|(žû}~®G™ƒ90IËk(úmÖ»ºuЏ'Bé’1iBªËùžáZ©³2°pa¢××Ü¥ÅûŸ}HZ2ã=c’,¹¿ú®eÈ3ÛNzj¬Þ'½õ§cÄ€—`ÿñµHn¾†ìc«qrIÝÀNàd>ûÈصg ¸VìÔƒ êHùä_8_tNç¤Ã™ÜLH¸cÄwihöÑF!ÁiÕ m§à—s!óðµÒÑgaþtˆí—uÿ¬ßö* î󰘣àÑ[ƒÇ×ÁŠÍÓª@Ôå£s»JDÜØÚèµ&ÊQy"4‡Šñ º24¦uÕgQWùÅ6皎S]ɦÕë9JK£†¿ÃÁ˜Ð×x°4Œ#ÀÔĹQN·¼gìÓ2¦$-ÒÞ€S…ýç+¦¯Iœ—qYÆ“‰;èÚèÉÎ]Ëg‹ç‘øD!±ˆ]5ϘpÝ“ÎLºW—!qѱó©7Æ|ŠÓ«IÔ¾ý%-MM 6Ѧ ´ošf£4Á&2Cr[]¡abƒÀT´Y°†­FhhF¯‘F"5Dn蜮á!2£•ÅdÕṉò¬Wß5¬u+Ò½8}¾àäXÂ%Kò]Š |ªUµ?/ëF|;¼È ÕøüCÎô™ó…³‚,Ó×*‚ºµ©g’‹ mßWpøT @'ØmÅØ¹" ­~SDš(8]Eø^w ÿ7ç,€åŸFS£!póµoCî²C¨J«9®é3=©_w½GÏm…#g¶@Ûè>ðï“q ÏùK ¢CA—SB×ÍnõÍ?™ŽÑQs&­ ih¨òšær¡Áã¬ð’2Ôu†úÆ´®ú# ĶæãTWrùÖ++rkí׸ÑÒÁ3¡ –‘`9F€¨%Š"߀“•ÏwOMZ(\G³sÞB¹ÝN÷0ŒêœÐP[`ßœÒDœ'â#ܼ}|lÜÔ5ú ‰3M´)¦‰4¥‰àЛ43¾d†44Db´“.᪄-DZˆ¼ÎZLiºW¡f¯Wžyص 3Ðá„'”:Q÷?­J|ÆïDñ}O3o/lšŸ{®›¢à  w`‚É®jFút»µ+û`kúBض{ 查šV[×ôž‚&_жåe*YÙ‡k`:´¾"QûòŪ;‘0mT53=:Œ†ä®Ô<Ô £Á Ÿ}w«×<­°ä$¸"zÀÑ3›ýî$"#t)SM‚fræ!58¼XgC‡†Ä´®ú®UMÇ©®äÒêÕÉJ+úr—†³Z"Xbï[°Är0Œ#ÀÔ YÎ* ?0æ­lÓò'º{gwd††º?ƒSRãžÉÉ)Ô.kp‚¢\è9ïýû‚ûutÒÿÛ“™)c[¯B™Fá¡Ãù–×½s-šÔ´knM+CDF#3Ú””H ‡K# Í`5|i^£„³–Öî_ºF?s þ'үϚ¯^1[RÁ^t)¢Í5äçž¿ ¹éšg¥"ÑaÝÁj9vÙB³açlu ¯d¹…‡ÕÓè°.pܱ­4ÝöŸXëÍævàkˆ#Crk}ðV^„V–ê!í7thPLë¨óê—¨”tÖQµ«VPZá# Y˜ÐÔM.Í0Œ#PdB6]QF/š³{(N;º¢’ƒÛé½¼{rbFeenxÚ±ékBþQõÄk·Í„®í‡y^Ûwì§JE>Ÿ¿úõ¸ÂBÚàâq®£!ÙUu 0Itÿ«–ºfwë ÓšIç_©` ‹•Jªqž÷˜CŽWš¯nhj÷jž›eF€$ÓAÎx*qMæÔ¤÷2''ëKfçdôª¨-\IJT».ÈÊZº®c"`h:ò–ÖNÏœô2{íbàbšÛiŠ53)ŽýÃÂ;²ÁÃS÷ 1FôòݸµÁ…òS·d‡/×Ü-Âã`Òø 0´ïsðkÚ›}te¥5d[gs³à±Û6øÁÞ¯M¥ù›Û Æ´ÎGœ^z©A/ {´t°Ä¼†&XF‚å`F Ž@ó®'Ðͨë(>â» ’ŠEšBÍãB"4›¦Õ‘l†HåcW.Ì ÕUÒ57;ªŽšâj› äò¹.»U›7æä­ì•O;V*Þ™ó»áý%£pñhéZ™2žøÍú‰•+¶ƒ—ß„"UïheðãBuûSÝü~ˆP«,Áˆi­:TIá`ýTÌxM\“Þ˜¥¥ƒ%f M°ŒËÁ0Œ@"0t­‚¬‹ó±‰Îî#ò—¾Mµ ïó+Ú_©Kà”*îǹÆDßûu™îóééb49{Ok#ù쎺ÔÒhÍpÌøM.Ë¿‹ù‘Ü8{Myü(eGS3ôöæGNO–2Ù˓߅1£oùYÜU§ùå­kLk$”…4¬ýÈZ¯Yâÿ™Õ¿aÔ(þWän¢;¯¡©×àÆF€h¦ôž½¯µ[ç$)Ò Ü·cЩÔô$„‚ÞªmÄEÔ|a0)Õµì5q%Î\î ë²ÛMZšJ×Ýø– DZ4éþ%9¥'‘ØèZŸŠJ½©uïþ‹Ïì DÝ\#À0Œ@-°K^s3¤4A§¡Þ±ÉY-ǘ‹3Œ#lôœ³ë%ØÂ#Ñ“ØF\©ò´Ð ¶û𙕗Y…¥Šä!4ø–ÍkåóÔÕy¿oNFÎßâûãÛ© t¤ôFjº®ÚäzF€`üE@¦bž ¨/Æ´³ ‰™ÐÍP° Œ#ÀØþÉ3× ^®NmŠÑ² lÅdÇ"¢IÁUkçEF{²n×'øÊ'‚á2¸nCRƒ®àÖ7Æ$õYz:Ý7§F€`êE€A¨AWzrI­ .Ý"¯¡¹4Fœƒ`F…’Úì°Zá†'ŠÎ¢qôV*„ÿ[z»»ðújUPËÌý~8¾óDX‡œÒöÜ<†´4F€`|Ét•&‚„ß´t0ÅLh‚i4XF€`t£ìu߬È2îÀ^¿!µÍ•‡ÊZTnß1.6±ìœSŒ#À0õ@Ÿ¹{Ú¡C€ŽÔ® @qb‡^A¹¾‘ M}?Ü#À0ÁŠ€ ÿZ M n\1;ƪ×G|Ò[|2´ý9j‹LÏ\Šòb}´Ëm0Œ@ÓB@tÐ)ö*Üû/г- ¬û`4ÔëÏeÝw¨´»ìTÖ˜°eÑöî ºÀ„&膄bF a¸aªs7šyÖ­(`‘áܸú–$%vàa­M”;Roi“ sÌ4Vt¢º¶ÞXů•ܱÑÉðì½G &ºl]yU¶‹ƒV-|œjU•¹’{‰o†;†!æ–`1µ¨$W`.÷î:AÝìtò]™ðÈ-¿À½ LÅÁR‹"]íE 47#ù˜ÐxG‰Œ#À0"Þ=jp¯‹;ë‘Sam‹Ð®a µ‹{^ˆŠKžQß2p{Œ@ èÒn ë;-ÐÕ6Éú®îó$ÄwS«¾um?ŸÚ+·¼Û÷~V«º.U¸}ëËá×]o¿¿¹Òö-„¡}Ÿ«¥å¥Š5žû‚à]O‰fÉ?«àìå,XG†åbF ÐáKœ/mz̲·¢Âoxâ|A}ŠbuuKî±dv†.¤oÙ~sÛ«ú~"hß Ö'6ÜVãC }«pýÀY`1GÁ£·þޝƒ›§A¿øû`@Ï!ÔÒŽœÞ¿¥ÿNœÝŽ|^Tßô¯Mý Jú?ˆ íYG–b™Ð¥9ºËhäa4bQP|Z¢¦k›¡püÜ6øe×pòܸ:ù HŒ» œ®bHîv¬Úò"ì;ö$w½®Â{!æh8xbü¸ù9  F»w¸zw½ vd#/ÿ›š×î̃níFª(Þß®Ó ÞŸ­#{AnáAصïKHÝó±šG¯7Èþ/A£Õ Kw^ kRþÑÝàú+_ب$8_pVn~^•µ<üË6>å½´-ûS¸æ²©Ð¶e_È>ºÊ{½±&’ÞJëêt(ªºŒÖÏt6šÖå&40khëSÆr3Œ#PŒzÚ‘n›·SÕ¨!1‰¶ü›ë ™*«¼lñ šÕ}¡e’ÜîzÛGk“cF Päƒ#g¶@añIøá—'!5ëCµê‚â°~û,øïª  ×™apò“Þ&#¬ía䀿¡váSØšõNÚ'àäÝ3I÷fj¤ +’’!}Ÿ—«–mz­m‘ÈüYíMöñÕ`C¢²çè*V'Îm‡Ð¸ñª¹°3û øfÝCª9Zßî¿Sót!йí50úÊYHú–ÁîCß!©Y 9ùÙ*ù ¼Ï€bûYØsx)|¶ò6Ø{tŒ¸ü¯`6F¨u ¹ìHì< ÎG°Iä1+ c½g!üoÕïàlÞ5ðÒÊ⸘«UÒy2'(×Í«ýªÎ‡Ë¾ ~ZþDwGuÊ×g^&4õ‰6·Å0Œ@£@ ÌìL‚ú7;#ˆtzáy4opR55W§Œk=žÒúFŸCä×Úo‚qæ$+F¿Å(*9…%'Áå¶ÁÑ3›áN¶)ÐÄ{Ï‘¡ÈvŽœúM˜“vF ™‡@ÍÃðËιp2' :ád¹:AQL`С©Wv|UQƒà[«ªvÿ+jÒî̇7= {±ÿiûB\ê›gÎïIvBAÑ1«ûyÔ¶ ‡‚’*aÉG¸ÿøˆïxƒ·Z"ƒëRgÁúm¯ –g'œ>Ÿ6¬¿Äž£Öát!™<[3?€¼ÂÃ*/m ©Ë­@Xÿ–öO$DߣL+pÝM$´kÙvîû/;r }ÿ"ˆ‰J„ˆÐÞvË'ÂBÚ¨Z M»çCQÉéò·«<§±2êuÞ±ª2s=ÞDç0cµæðÙüAKcÌ&gÁ8*,#À0 ˆ.`^è–¯”Šp]}o²IíöýîÔ¡ÔcÞF2ãym­³”;îX",Z”vp¸¸é:D€&óåƒ IBÍóv¿ü½êœé÷,$ .òÈ%I.5¦´¢xñsù{½ÕÍËD3¦Ë¼çþ$$)µº ²VÔŸ 2”;©( úê|Ùž¹DZˆ”™Óúî+F\›kU³¼ûoð™O£úØ7ì?±Æ÷ô¢t7\S3(ùqˆ ë‚&g‡Ôû:Ñ:Iupú|ÚeÈCÉCš!- kfrùEGµKÞØhE'£v'6lŸí½îo‚Æ*Ä\F’+ÂÝߺ•/ñíŒPw‰ˆ ëd–DæL¸n„™¢‘S2®‘ñ„2Pö£T³§¯Wµ3‡‘øøH6’1¶…Õ+·¿e+ʧõâšô¿¢:+»v±jƒ‹êIëA{»d]©:F¸<áA0葤YZyó7è‘pXÍ­Ð ì–·ÀÀ^^P”ˆNϸ s›kÔõ9Ý;Œ‚ãgS‘ œWÐøã€.í†^PŽNH£v˵ï@‹ÐN°9ã?@.ªÛµêQá].Ê[Ùm¬:ŶPǪ²|õ]¸×§Í…>é L2¡ Êaa¡F€hX“i!Î]ÔWhÝ1rÅë–ÊÇëPÔ³áM¬þXi±y…Óë°9®š¸ïd×xˆ¸€&ÜbÀµ)2Nû\”·² ÙÇVÁÙÜ,u¿’qƒß‚}Ç×¢g³MðиŸàþ–âZ™Ï/*züì6¸sägpëp 'Ø{±ÉFÊ™èp J]FÆü-ÿÿí|ÅÖÀgwoÉM¥&¡÷ªõÙQJ(–O¬ÏöT°¢HQ¬QQE±+è³wH(Šˆ =ÔÐ[ZH»uw¾söfÂ%$!åÞä–3¿ßfûÌ™ÿìæž³gæŒçuÞ¨¿g~çÛ^¿ósˆ,–À¿y+ëÜr˜>æeÅúiìòžØø[¶³‡ÿo-kßüªóeSr=0ÿ€±qQÂìÑ7‚Ñ ëcmÄ¿­} Ë,`ý»gøŸ3¢¬@t´1¬QLGöÀu± ·ìÔƒ4à8ÏÙd âõûoòÃåŠ^g"Ÿy^_Ö6¶•Å$C¶H¯Ÿe•QÕcÐÝ,ž3î†,IÜ$Iç> UÍÔÇ×ÃãN‰"@ˆÀÙ’ÆZ¥L“~‡£ø£&C׌»aýâÙWù~¯ÅœCÖôñã5MÓçÇ/Í#FÄÿ¯×ü¬ ß—N%„:eôXÆÎÈŠ¢¯cÃìp~{f³µbaaûÏ‹©Ðzœ}¶p$ ïŒÝ‘Þ}½äFÝ!Š1‰ùRPáÆ´÷Èr¶à¯qÅ×èÇ*óe²ZÛ³±%òâP Q—Êä!®÷àýÕ­?vÙúï-E–lîòÑ%Û¸±eÏ\}qPþÎK€UŒš¯Úò޾`ØfŒ<†0á ~\J§ï~»í¬C8OÌ?èÜK‡¾> Qè¾Xx­îý‘eƒÞ>x3p˜ýK?f6Eéya¹¥ð¬[éóçÛmÕ»m#ouÛé|åTõ¼Ó¦Þ Ÿ³ôXÐô˶Œï~îÀ¡ªfêãëÉCãcÀ”= D P À÷èJdçünÇ¿mµŸúÌÏo‘$Fý\œ¿SûRP‰¡J½2ŠnÈ(Ðõk^Yc'N€ˆgÆJ£Á¹SИI3bßsí~Ù¸î-ð<^Ñ6Êrd2)œµÖå²c=ª“¼YÿÊ—ÏKŒÏ{0r™0fÌðL[%°ÖõM¬cÓzúó„ÏUMÛ©´Ø6‡“ÃØC÷-÷Éó¾½ßÚÕF[Ý"ò2(òÛbÛß×dÐø{ ‘|D€:$0x¢c öY],‚Éa-ô åY«’už·#ÍN…r¦=ºvx<ΕC‰”IÀi³ŸÊ=]x^E®Ì›‹b·+4ŒF£n(DD„³HX""܆Cc „G6Âø g#–•sËιƒöÔç©©(ßšœÃ¹K° , Ë䮆¬E¸Êš×ƒÅ —[¾p]^”[t9«N™þXÿêÔ£®î)ÝVLmÄ:64°öñÑ^m§ÒõËÍ+äªËu¬ôñŠö‹œ¶ÑðµÈ=!üßÏx<ç# ˆd)IH"@ˆ¨C@»€À÷À꺦ςœ¯Ò†ÇÞšAz‰ ]Ï.H˜“á{TW‚Q¹þD }±Ï·Ú¥"›&T¬ÞÄ…n…^†hcà¡<"#"˜Ý*ŠY²•@IDATNær©`¸à +³À±SN™åÛaL[{è­Éa¥"¦NÃÚ;§ÆMLuÅ@÷¶pxÁÐ’TÖÀà`-22aºlQQ‘,"2BßFyQnŒP†õ¨Nò§úWGþºº§¬¶Â.ŠhtÆDG1o·“g=ñyÇçÞi·ã•2èûÌâÆ‚¼ÍŠ| ¦†Ì˜DMÀ4 Jˆ¨á ¾µŸ ¿Š‘ðÓØmñtãEƒ':…צօ’MÊhÍ¡n£äá Ö¢OƒÏ׺ T ?Ð  ×ÚÁ];64kÓî¦Í™‡ÙEÝÚV[^ìv…Apü zhœ`̨ª0f ®¹ ÍÆ 'kÑ´¬¾Âª)Ìc[\8ÀÑ‘¡Z8u» ¤H#gˆ…]•° Z11Q,”åh4j@N”å®nw3¬¦õw8­Ìj?y–ffŠffcðÍ“+ìFŒyl£Ö¤¸ ­‹ S|ÞN¢½ðyǨ”Ù‡¬„câ§Ë\nê3¾Â£v$"*! ‚ˆÊA#HК"@Ê$0à¡c)Se˜F÷Î0•«ø¯Î š æÝ]Í&ƒ¾ZÜ¿[›œvM܉¿do.³t0TÅ ×*.›×þyáÀÁ§×f쉃¦z. ȨÄK‚–43°Ò“[Ù‡.i#„¶±ãb&—‹EiÐ)¼7 Xº/ôÂ_”ˆÑhÐÇò`73üâÆL½˜}ŒrVºõW5Ë<øÌér¦ç’A1³®m®I?ã {§("¨ÖgÚ J€AY[í$ Âó?¹lÁ\üŸèù^ˆKÎZúž+›öo~®ä $¿Ÿ>F ¨Æ!ƒ¦¤õhƒ"@Ê# ÎÒTv7CíæE3ß2®H _Þ=¾<ž8ÿè»é#ân]ñ2øµ†Ïàìc>jÔÅÒœ9¨ÈR ]ØÿK7f`a¾]2w-†ç÷Æ]²X‡–ñÕ&# 3tûân#¿ÆãøìÖ…‘ϬVtG³ëFª¹=86˜jbØ ‚Œ #Ž¡ŠŒczÀceº½30¦º™¡gFïÊTâ©~0½@?U­¿ÕžÇÖïøŽ8½ätkÕ‘–ƬW‡Û û_CœƒgÓÚ Ÿó-™‡¤ãG|dÅ»pfò£2pƒ1s<¥ôS˵(7×4e4,"D€³ à\›:Mú ˆKáŒIsÚ†5võª“Šß8²é½UݪR,‰éÖàµaSêD *Ô å€ *qhÌàÀÇ_©ó–´l×~Ð7‹þ‰~òî²Éˆª—P©GC“ø ƒîõpÎ0^Å­Ø q—FU5¯yiDyèq9óÕßuͨ ‚…‡[ô»š¡œ(¯7SeëŸsr7Û¸í]fUO1K4 ´J“†=XïN·3ƒ ÖM§ºl'‡SeðœkÞìÔŠ_æ¢Aƒï¾âÝ8‡|Â÷&×íy½‘à,Œ¶š¾a\›Üs.ôódÐøy‘xD€! Éòë\ÕРÄïO›ÕôåÄ1GŠÜûµÿ·ç¼#;Ò†Å'sÆÿ«KÄùóÐõìWèzVgÝájŸ•XŠ~‰ÆŒ¶íEEùëþZö¹Ü慎?Ÿ¿’ß{}¿u=J½®¸‚çÄí)1±p‹E7fô€‡;`@ñ8á©Nï³bçL‰Þ&0hp^œǤ‡’ÖÃHƒA…†Œ{2M·geôfÊ:Ö¹¼úoݳˆ¥í~›Éf'‹€±#Xçnmo`ÝÚ\'tfoŠä7y Ô‚Q]´>ßGŽå²Ò§<™“pШÇ÷¡\º_ MÔJ)±oPÁt¹á4‚­‰ D BCÆ?ûsê´öÀEmAIis:ëNØ~¿Â›||²Oßû§§§½7 乊2À(ì¯ êY/ˆzVàã¢){ÿ$€Š~‘FE΋—õ+—¯¯ß8ögؾžÍeüŽ—IÕõÔ Â* £q#B:£qãgÀíQu Ü È»(¿Z ËÔˆ¡Ž,½4 4bŒúX wD3o{f<….¯þ&“mHŸÍÖíþŠYÂ݆ŒA‰dý{?ÅšÇöÕxæŒÛuÕNè™Ac&mÛ^éðÞÌÿý¹pÞJà‹Ï?¾ø>”iÐô™u$¼ ƒª€I ž²W6MŒ÷œ»F?È  „V"‰ ~@@’’µ…Óå7aL€û ç¢öüˆ» ë@F)9YKÑôßwm!b`ig-:ö6ˆrwˆCEÖ=|QsÂ‚Æ *gÐ%‘™ÿøiÎ"ÐÛ°ÖÈ£'Nñ›_,×tL *°8®E7. ë™nÈ@  ¸Ã:£‚_ý×ËÁ„† n¢A£/°e»÷ÝÁĵú >üã–Å]‡+¥®~ˆí?ºÆô€ö U­ÙŽ »ä5Þ\—¢&õ÷a5¼šµ`_›í„cf°›zfîÞùÙ¢o¿À®føÜãóï¾e>|yÇîe’t°­)ìƒpq &2h±ÕHf"@ˆ@ oø1Líñü<փŽ©Ó Ã@”u$Ž^lâü#Ö ‹»âNá9þrß]ÑRS²æÔ¥\Tv@ÅÍÓCƒž:´¡G;þãç9‹Ndge%^1à–×¾XÓ­}sÞ7¡­Ô½}³jÍSƒ ,¨Àb70ô–`ÝÌÓ3S…^(ʸ –)‚ˆcµMË=–»•ý¸ô>–Wt„2_@‡æƒØ‹§1ƒb)1äjRÿÚ®WuËC˜D{àÚí„óÌ`hfŒf†4—+oÓšUï¯]úë (Ÿy\<=4ø^œ•.zkWt®Ã6 ¨ø¸ôâ±°›f@&2h²ÙHh"@ˆ@ÝÐC8O—?„Á‰n ´Ça]§ ÊqAJö·k‡Ç&5s»[.> <7«ÑØqïÓß"€ÝkpÜ*gø¥Fr0Œ ßø÷ŸëvnX·ë_ƒ“¨ªë P£AñäQ3¯!…ar—¦ÒÊ{éýêd/”eqoé}q¼6×û@EºtÏʤÈ#ÙÁ£IìßΠeóFýk³^5)«t»”Þ¯IÞ6§“çž.ä8i&0•4UÍ;¹ã×U Sææ‡¼OÃ’ >÷øü—;~æ´Ã>òh× –Ù´O§;õÀüCM`¶IMˆ¨3’Ùü³ÚÆÁw=–L3ö4É ÑÆê6EÉõÊ×r/c« ŒX¨Ï4õÇ]´¿¬ÃÛ™ûÕ±n‰léž^4bИFŠnìX­…NðÖ¤Àñ%»÷nݪcçΖÈÈf‹%Æ УC©ØË´IÓ¥ 4ÜXO\¦i&õС!9y¹m¡«Óvq˜ÖÞ"Ð].§Ýaµ¶?°kÇ–íÒÑAã 1ƒÑÉp»›‰ñ3‡ܩËÌ­M4—:Nìƒéùe$4~6‘A°MG‚"@ê†@ÒXë¡”iò0nA œ\ «›êFš3¥vž·#榹zP¬€_pŒ¯”˜»'ÿ-¸bÌ™«h+Dxzi„1ãiè ‘ £=˜yçæõ[aÙÛhüè^X‹{`“’'øæräñÿÄ4`%ÆLQ!;úã§ÎÙÛ7ýrÌóZÚö aà3‹#†Ï0.¢‹zfИ9¯wæ{ 2‚kѳ¶ùæq]¿M~÷7‘A¸mG’"@êŒ Iž¡2×-(|8µxºé¥Á[êL â‚ûÌÏþgíðøÇ¡#?€ÄG§ˆÿ»Ïü¬Oõ]úJPùÃAјP)û¨âø4h0`vIC}HtK#c`”•.î/·r=ÌhfÄù“ÇØšOÞ”?8yLE®”|C@<¿hÌ 'Ÿk|†Ñ A ¡ ¶žÇçýœÔyú–0QÍ ú øç-Éü‘dI*óÚsnöãdÐøqãhD€%0d’s]Ê4éP¯ˆ¤2g2Èêþ‘¬c¡û.Èz'mxÜÅÐ?ü6Eãü½õ×6ÛÐûçÃuÞ-®ŽÑ„ZñB DåÎs>Tüôèg°ÖÀº´AC† @éßÈ;÷âã ò¤Ü+}þÑ ùkŒîFÉ'ð¹Å$ž_aÐ`w2a˜£QƒÆ îãq1nFÜ ‡Ü)áÝŒH§U{WÏ wæãí÷X.Îòš š@n=’"P‡ŒÌì’\#Ñ 1®_ôš±çñÎu(Ò™¢%e4ôÌè ÝâºÁbq9]?nÖ2±GÊSg.¢­ €J*ž !8¨ü¡gÆÓ˜cmðyÆ…¨WϨŒyR]Ÿß!€À;Ÿ·+Czò³·µ•å8Ä¥´®9|vłϲ§QƒÏ21¸ÆÏ•ëmqÙ´—áÿa ¸Ÿð“EwÑô2hºùHx"@ˆ@ÝÀ@)S¥Ÿ@‚ëѨ~Ùɰ}]ÝIt¦dˆnV´~xóëašÃ48 ú@[‡dÿ!mtŸ!‰³Óñ‡ŸRh@%O5ø•@ÔЈ 14† ˆÔ˜©Á€$ç»0ÕÍ%âL³³cãå??|âÚ'ŽÑÚ§ð¹Å$žaaÔÃFì‹óî«Kýí8cK_®ñ‡ÅaúýØæ»Í2hDËÒš"@ªNÀhL–\ÎëРåÚEÓŒ`w´ªgäý;z/8´+mDì ÌÜ^)>ô>”t¯÷K£€*†¨ü¡â'Æ!ˆ@Â+#<3bÕòˆã§z5häü¾ä·%À3¿¬]>fÞ7…8nƒRí ®Å‚ϰXır¥é¿”ޤoùn× vx¸mŸÐMŸ·«Ü›ì4Ö`$. DÀŸ {ܱ9e*D-†! gû“È s2áãõ  íÒEä<\SµÔ#›vò'9I"P^ýÐØ‡9 ÓÀÃÚ_”]“æÆÔktñä{í™â­‹À¦ý[^‰/G©¡=] ôß7çFˆ²„‰ š lTª D ¶ }¾CfÒ'¢\MUgb÷±ïë„9‡N*Ì4 ~ÚO¢<0ž¦±SS—l¾¦iÉ g“d µIà•ÙÊíw­ÏeËâr5è–ôìäÑÚ Ýx¬ 6e¡²¼G ËÌMWA›N9r‰=»}Bt±lk2h‚­E©>D€:"`4YžH^qñ]¬kWøÝÀSŒ|]φ‚œº¢‘ÏZÚUuIÚˆ¦êKê„~pxu¶ü:Œ)û¬{÷à/£Hòð§FkSÀ¨}˜R € 4ãUUú>Ûèz¾Ä¤%;ÆwŸˆu©¬ÌdÐT–]Gˆ ¸ê±Âl‰KSÄE`,¼°èõèbß_Ö}d­‘¹tüÈãäŠ$€wfšºhûÈNQþ"#ÉA|I ¢ñ2OŽVú²lÊÛ·’9—]VõKøÇ‡%azÔÌ,·»JoŸ+Ê"RZ¶éò&ôÕ}îësWÁ‹þà‚”ìß$E¾UŒûîg}òµÜùà© ÷GyI&"à-e—÷àG/ã-Âu›Ï7¯o~ÆB tK!i`Ñܶibûœº•Ê÷¥“Aã{ÆT D d$ܘá˜<^TXcüþE¯›ľ?­ûÌ;:WáÒè™8ïÇ5×·ÇE”£ "DÊ/óÔ}Ú(/ø ÝeÆÖ+À˜y^Ô”ü—vŒï¶Tìóš š`n]ª D  }B_|Ó‹æLќΙu F¥ŠOÍÇ0ž¦Äƒ›®pžâÔý¬Rôè¢@!@ãe¥¥ª/gÇ;©Ìõ5Œ‡R0èb¶ìæ ÝüÒC^ýZ–'4峡3D€"PMŠÁøØ™î\ìê…S•‘ÕÌÊç·%.ÈyÉÒ㢠øÂyt?[¼ë¶öÑâ­‰@  ñ2Úr•—监˜ÆÍ°fÅwcfãmÉt9 ‘DMˆ44U“"P›†<îÈ€0Έ2¹¤½³rj#¿tßw~öLI–ò€ÚÎÍ_’6ªmLÉ1Ú F€ÆËXƒUSܶ̄à& :¤(wìx¤ó‘jf·‘AÍFB"@üŸ€dˆ|CIÁëÑ"O:é×aCçg¿%ÉòC ³®\Ĭ…+ÖlÑÔÿi“„Dàle— Ï<=šÓü2g£ è½Î36?ÿ`•ÀH“;OX$öCeMM¨´4Õ“"Pˆ<žw’IrÉ-Ø ÷§N3ô«e1ªT\âü¬÷ œóý%F ç=4Õ±jãȦª”]Lꈎ—ye– _ìÏ_fòíå:‹Šõ.¯mJ‚À+%caÜÌwÛ'$”ðA‘~›%4~Û4$ D ð $MR¿ƒ±4¿`MÀK#1®~´êõæ®YŸ”ìÙ’Âî£Æ©ËÍx+‡¦þ•>"îb–›d#b¼ x4 èE†,›úÒü2‚Hp¬;½±¹‡¦Iß–`ÒßMFÞìóÍ”×zdДG†Ž"@ˆ€W(FË`ÊäbfЗ«}®ëÈK^ÉØ‡™ô™—ó¥Ä”  èÅpÞ ²ßÓFÆóa±”5¨6/Smtwc—™[›p[†«>.þOí 7‡]³ìî6¶€«Œ—&ƒÆK )"@ˆ(›ÀqEG¡ëÙ™(bðõxÑëÆ Ë¾ÚŽöI9ºX–•ðÅSŸ”݆sUû9mDœG7:ÿ‘—$ ]4^&tھϬ#áš BãsÞ¢¸Ö§ L¾~l}¼bè8»¦dМ̓öˆ DÀ†MT?¯ˆKô¬ažÕåú_Æ÷ &åÕ,/˜w4M’—¬{Š36p¿•6<îôÑ}Œ^-Œ2#U$@ãeª,À/Oæ\ÎÏ;ñ%xf±*Ð-Ö¥Ø ¶xÕj,>45FH"@ˆ@e˜eóhø.îÂźíß»í¹ÊÜW××ô™w(3Lá—@°€ÕBðÖÜËŽücݨøÆâ­‰@m ñ2µIÛ?Êúúµ-Ó ãîuBˆÊøà¶q=Ü“‹ƒ!º&ƒ&DžªMˆ¨m'ØöÖ“E¹ð•qrêtÕbߟ×ÝçådÇ´ê^¦Ï…œ8§VÄ×®Þ¬§8Fk"Ph¼LmPö¯2:Oß<º™RÁ–ÛÇwûPì‡úš šP¨þD€Z$0t‚ú.¿)3MýrñôÈØZ¡ÚEux;Óž¸ çNø*:ê ÏÀ FY+¹V¥¿»ÚÓD  þû¡r‡Æ]+!ÀFËâÛ4š_¦ ðÒÎÓ7= á™_)]’æÞ2¡Û%û´ÁÈ ¡‡€"@j†µ°ðCÙX((eMT^øtá‚Þh‘`®š’¬ O£Ä,@ãÚÇk‡Ç~¾ñö¸ˆÀ¨IhÄxUÓ>ƒ'L—¢*’<œæ— ´Ö¬¼¼gl¹S“¤·<îø­)ìÖdIÒ?ªxéM2hBºù©òD€Ú'0`RaS”Û¡d·—ƒ³A‹¦+õµ±Ï¼£‹ŒŠá"&I%9»ÝyŠ¥¥]×½äm/ ñ2^€€Ytš‘qƒÆ´ÿÁWýƒ|ZÆb¯]8¶ƒ=«ãS‘É ñ)^Êœ"@Ê"0l‚ëWøqþ¯8^Ž—â ™Ñb?Ö=çÙ!IÊ…`Ô|,ä…ztæ*[>,n´8Fk"P4^¦&ô÷ÞŽ32†2®}%&΄fë#£Â“6MŒ/ ÜZùNr2h|Ç–r&D€  mýÏÁgÇ•x t=3ô6ÿØÅ,*¸ÅïN%Î?RÔwAö=²Ân‡q5bN ôwŸ•6,nAÚõ­šøÐ$PÀ ñ2ÓT^´ËÌMW1¦þcôŠCÛKÛ"L–ÁécÚéÝ\½ZXdFM4$Uƒ"h¤ç¨Ìv 'Qv³\Ö7ü»N4žF0ï3/çK£lH¯Ó&q ”‘aÌnË€‰8oÇhíØýõ±(°TË#´7¿ô¾ü†Ó¥}¦©,LƒÎ™U/Wâò/ójqÝõnH°*)èÛRéðß´aªƒÏgªfaÐðd¯Én gž€óøÌ‡Z»Wêù&(•ÂD"@ˆ€¯¤NWFpM›'ò‡p¤/%=¡='öi½÷®ÖaÇOX_•4þ(xÎüÆJìGÅ"?pÁœ¬žÍ»šm‰¥{&%'J²4¨¶‚ðßM$΢ª™ŸŸß¦M¦c%É^†B‡£ñ6•1—æ~§Ýþëço¼š×àX4\à‘ÓXEÒÛj¢®w<6¹Ñl¾Z´?Dv‹ ŠZzTÂ!™Z•ÈÎ`¼bá·E¨y›¦ÚpFÒä‹ö‡ ~ÿoZr.^ð’€K£Ÿyi ={ʳýq]ÝtæŸmus ûˆ D€Ô@ê4yxfJæX`²|Û°‰ê×5̶În_7²Éªª}ºF[!jǸ$Mè» ësqŒÖFN¶¨ ¤G…=:^<(-<ÌY¤E¸ %“êЕ¾ s²“Ť"¸Í.ƒ‹Ñóìvç;ûÓ¶¼»lÙÏEP]aÜjíQ7ŶUú÷¿6¼Ub·‡ÌfãâýMR¡f‘ó$³‡\û»˜Y³jÑÜÁ#JÚ_UÙÊIþöìÙÉV`†ÆM@%2hª¹HX"@ˆ¨ˆÿ~”òUæšìúʆxhjvp?`èD×ßÝçÏç2F%DZ­'¦s®Ý_JΊ¼5¿dm-uœvÏÿóÄsƒ Šò—ä¦-óðv'wH-ó÷2³‹<Ù fv ª ÛÝ ?Õ‚¡kGóóòüæ×–B',°9C40¶tCD5ÞòðøAQ1Ñï!Ó$Ö˜É[6Jq†Ì$éΊÀ¨¤tð0–íêĺzòg{èéªq9Õ{?žúâb( Ú€IdÐLS‘ D€"P=óôŸý"Þï!ŸÒçr£&Ç,›/8Á¶¿2÷ûë5kGÆ^-©Ò‡8 §>AƒÒ)½ÎdåE , ŽÓZï¦'ßûÔ I’<­í8¿ôàr¥IáBS£MÙÊæýÕS–†’ÍZøÜ¯ÿç,qÀâ‚%P¼5Â+ƒAAL·?þäØ0KÄ‹ÑJ6ïa^ 42ô¿¨’ïÒqW+¶É6BÍÓb%øx2é£WžJ ”vgÞ2hBÎ]ç»GŠr&D€šp²0uµíöÍ ÙèãL y¬]µÏ_9µQ@•è;/çW0\ºB—³©nCFïbç ¦©[Ó‡ÇÝTnAt¯®ÔÞ39y33ÚåfJ×îüŒ™ó40{×횣 /0¦ÜöèÄqp N¼i„õ<äêÏIowå Cù±ÍÒá³É˜9OË¡±wEÄ,yá{ƒïÜí~žšUí44UãEW"@ˆ€ ä«ìLR®Ã.gX tï~šø†óä€þ½B/LbJö“²AîÕZ!¢×Fãü[ñüOÚˆ¦—‰ã!º–ïœðÔYV¦‚rÎî[(4ì=Eé|òBná‘ÑÏ_{÷˜$¸ÇÓ¨9_uyßmݘA¹Q~PÎy¢å;É QûW¦aòBnøþà{÷ôÿÌÊÔÛ󚪬gÅi›"@ü“@Ò$×_2—ïónXê´gzìì&ޛ雒ÓObò]$@T ›‹¸æú3mxÜO®kÖQ¡µ3ÊÛ¸I“™ØÍ¬WØ\j€RÕ„Ü¢ån6›?Ä÷ î÷×v¯jÕÎ{}ÈTô¼$è"@ˆðCžP¿€þ¯œˆM*¿xf?°·S²>cá°ô*å ‘Þ®u:œéÃâ>MËÀ®e¥¥GåU‰Mhóh‚cfÈ3Sivg]ˆÜ.;´L†.Žq'%“XL°à<=þf$èí^,ŸE—WRâpÌ yf€J5rë6_Ñ)ÀûYøc»W£fç¿… šó3¢+ˆ D  ¨>:Ø÷¢hðb< á'‰ý@_'ÎÙs»¡I²Ò êù%hw"äªAc| Ó\™à±™½þÚøÖ^×óȺˆÁd2>‚ÑÌ(Àyhç4òCŽÑõbþ—âWzõÒèí^,_Ê‹ÑÌ(Àyø<§‘rÄ÷ .õgïÜyjRµÓdÐT]Mˆ µD<4<®^ïCq)¢Hð`LM™&?$öƒa ãkôMɾɆD¨ó¢N`Ý¡¾÷©Nm'Œ±ùhÓ°¸¶â\­õ¯ô£î{äB&Éqš9ˆêVgUAŽ’bhtù‘}AKão^OïLʉòbhæ:ƒDëá}Òß«ñÒAD0U…"lǤ;Ãc[ÝAJ}‰ñ·NSî ººÎ?².qAö@Y–BÝVˆúé† ã÷Ø%ކÍ÷ë†ÇþKœ ‚µ®Ø†GE] 癡TsÈy6nÖ¼?ä†ÝÎpн?u?ÒÛ½X. ʉòâ<3”jN9"O|¯ 7j÷šW®œÈ ) &D€ÿ 0àî}6EŠ FÍß(„s–4¦}´hºr£Hè])úÌÏþ(² Jž´´$wΠo<¥r¶ º¢­^;"îžÜ»”rB=Da²Ü*ÌY¤Ñ¤™ÞiJä<¹Ñdn9zzhüEïs·»Ûs†rš¤BN“fz§ý‘#ðÔð½Ò߯à/¶wZr!D€ $0xbv¡1¦ÞPøê¸^¯ (÷×¾L®ŒÊ C¥.˜ŸµÆØ\)3å 0l~õ¬'tE»iüëô´­ûÒFÄ=¹ñº¸XÏó´zˆA’¥&áÎÒI¼ØpÀSRŒ|.p v9ó§ñz»ËeF9äÓ赡ä%ÀSÆ÷ ²ó§v÷RíÎ͆þyœË„Ž"@ˆ€¸zÌ©Ó%rxj¶¢xà©12®ý¸pºr³Šë5‘ú¤ý ›A&&uÃæ@À#*kÆ5þªÓÁaÈg˜¤sDymPÅE‘%)Ò¬9I¡õÚSV ðTd%²ÆŒèzTלKÚdCeÛ„r%G]ËåEúuŸòÄ÷ $ñ—v÷)2h|Š—2'D€o0!ÿ¸l´\FÍnÌMÓ¾Z8U~Лåøc^=S²·€as/DEkÁžíï¨Sg!Ÿa’Îyik·L;Â>wçýxzˆñÝH™õI#qäŠF*µ¸ø‹Þçnw·L Ÿ.'ˆGÉ«Üï•?µ»W«ç™™¿<Øž2Ñ6 D€r WtÔÀ-W€ ¼¥ø"¿›:]~¶Ü›‚èDE;Á¦X·–v;w+KU/^ãlLÔ¹míð¸5k‡ÇOòÓi¨lë t¡#ƒ¦T#zcÇ›A>˜)áí¼k‡§ºlÅrÖ Kºµ,Åï•'ï². ŠcdÐE3R%ˆ ¡E`ÐEG¢#£al ûGÔº^½óÔ¼*Êqœ GŸy9_&.È¹Ü ™:‚׿U0nŽúšó¾Ð-oªñÝiÃcÓ¡[ÚäõÛw8뚺ÝÊVÝJÜ¥£®'8ãÚ’§<¤‹ú¶Ekß–RǹÓCTÇ @Å"@ˆ@õ\þàéSŠ‹ÝÏ–ˆÀ˜y4u†òÙÒ¥ý±›MÈ¤Þ í¯ÍS},ýZÂ|.Ã@}ý ‡'ø ~ðyÅÅþy“Þ-mdü€ŒQ 8Æ¢.“¿(ÙuÉÀ—e …Öß8û«\¾l‹ºÈÛßÚÝ' È ñ VÊ”"@jƒF?kÕº+D:“¾/)Oã·[×.ÿiñô8œ%=¤’4gŽÚwAVjß970KD,7w2IšÁìž üsw½[šªýa-:~<7ó¡kÚÃ~æ½ñÙ/¶•ÈhÖàò$Öä†û˜¥uÇZ‘IÃidª›¥+ŸÿË 3•²†Í/aí/|ŒÅ·VÝ©Ò}ST•®å‹É  åÖ§º"@‚€@ÂޤIÏÝŠû,QðF WyΪÅÓÃÚˆc¡¶Nœ³ç47Ÿ÷]=2R©×BÝ^›Ÿ€“Õ“7à¹]ÓÞFïÍÚa±Áƒó„ƒ›>¬IFM«Ñ—ã¦ÿ˺½û‹gU*ÜŽê~!“-5³q[?ôk2j4S"cÌl_ay59©„G²÷>Éz}¹’õúüOÖñÅXDÇ5ÉÒóÞq÷̨šÛ^)¿ß±Ž—#>e&K#Xê—wY£áԥߋìª1[aÉ`ýî\ÉÚõy¨ÆùB^áî A|‘4¾ Jy"@ˆ@­¤dmØ$í~PØ_ƒQÓCÕìkM5 ÇBuÝyÞŽüÄùÙ_C·´ëMõYcY–F@H×w »^fLšƒ‘s#ŒIzScjZúÚm¹àÁù–—ÒG6¹>íÚí¼8N •,X|û…^2˜X»‰3˜©Aõ§ë‘ FÝó"–5÷ìЧ3XÑn=zxøj~H¶„3Sã¦l÷«²ŒG¯c\u±¦7ß_ÝŒ‹—Œ£©n>Þ¼¯VejÔªëtéS5’¿qëìÄ¡¿ÙÖåϰ›¿¬Q^Ý̹ÊìùGÙº”{Øõb·|Í:ük"³D5¯è¶rÎéï•`]Î5ÁqØwŸ‚ƒÕ‚"@ˆ@Ò$íé…Ó”]œi€Ac†pÆ A)_ÐÆ%MÔÞ  ªøLÔž_dBæ Š†ÐL FÅ`0dÀñhÏÂуa•ñóö•\Uƒ%}D\xr6I2ÛŒ7JÌa 3e&ÌÙ—åyo­mË2ëöÎ/ìÐg¯³øëïa¦†qìÔ?¿³#_¿Æ€ÊÚ?ý–îéðÜû°ïd[Á$Skq÷xVïÂ+w:XNê7,{ÞçºÈ-F?Ål{w0c\3ÖøªëØÞ™“Yìð3ÉhfÍn”5<ŠmŸ|‡¾Žv+3ÖkÄò3ÒXÖO³Â›õ<š·Õ½,m»0DZ#ìð7ï²Ói+XX³6¬å˜§Yx›ÎÌ~ô;ðÑážMg¡ržÈcflɱÜ~cÍï|¼dŸ6Î&ÛöjÖ²ûìØÞßX›>0—=ŒŽÏtã~“D–ÐÿUðª4`ýîZÅŽí[ƶ.{ŠEÔoϺ]ù_ݸ+ÌÝ Çžf¹YëÀ¼–u¯ÈúÔѬÓ%OÃumØ¡-ß°ØÖWé…bÞ!ã9ërE2‹nØU¿ÿð¶ïØþMŸé×Ȇ0Öù²çX|ÛÁpÆŽdÎc;þ|‰áñ.—'³øvCá5²³ý?f{×}pve`oϺ÷KŽ?°\÷DÌ0¥L~ÉaÚ(E€ šR@h—"@›ÀÐIê§©¯·K.×\P¶›Àb`;eªÜ«u›.bµÀ®¡w¥ï‘’½rD ê}žœ,oØðaUU/MìR0d.ƒ¦UéA—à û.]í<Ç™‹¹DR+€ÝÝÀ«ÓýÌyê;úǬè@&;¶ðÝë_ìXê·,oój½ŽÙîÔ££UVŽÓëVAd³tÖõõ9¬Ë¿`Ç›[r+–·ïgÁéÎÞœËÚA€s\sèÂVÈö̘Ä,-ÚÁ:ó`ž™¿ Òxuà¡ôL8–ÇP¯!«wÑÖéåOJ–h0¾(U@öÞ_Yþ‰í¬ÿÝ«YÏAoÁàþ¶>u ‹lØ"šýÅ=°“u† zñJdðßö‚ØÀ{7êÝ×Ngú8m[ñ<³d±Äk¿b—Þº„ÅAT3LÛW¾¨{„ú^û5üÐvÑÿýÀ¢Àqé‘pšV=îfýîXÉܵZŸ?gçªWõÀ—Ñf)g¿A¥NÒ. D€Ú"0ú™—–aY³§<Û×¾LhÀÀøÐ ªd…nè„Eü'ilÁ1_–My—À!4È?úî‰ÏüÒÒq• Ǥ8í€Âó‰d0Eé›Á¬ª ÇË`Xfø(Ræ­Š1z° òb?“$ÙÙ¢ÀKt BÇ̲t}SÀs¤:˶ʺM[Yø–em’öÉô)×À1hw!ÚYkoýß'ÍYXi‡"@BÀÐ'´÷@©H#f“¨/tCÎm…[S§)"€€8Eë ! Ù­g3X-îrêÝÀpìKuREÆ æçÊË=ǘÁãhÈøÊ˜Áü)KǤx3x2Õ1fð^/Sž1ƒçÑ)mÌàq¼Ç¸ ‚g ¥ê3XF¨%2hB­Å©¾D€" HšàØÊÂÚ](1é 0lÜšgà‹ëg)Ó¤?R^3w$TD€"àÿÈ ñÿ6" ‰ DÀG0ÂYÒÚ8ˆŒö—ÃÙIulZ8]~>õ­öæ’ã´ámhHr•k…vÙXÁçjoüù9dD’ÖÃ~錑sñ╲èk—ê²:¹ ·)y‰€“›¹¦qìÏ(X{)gÿ̆ ÿl’Š"@j‘ÎIcb`û0¸&-€33(É̶{㢩†µ(N¨U¢ÀjN5§ÈY²*|YÏBcwØíÐÇêœTלÏ)ßa³çÚxÌ9ÇÏ‘œTš€GCtr—çxÀ æKM¥ º"@‚™Àà‰Ù…I“´‰²ÁÞšÕ¢®`ØtR™úGê4ù³ß_·4Çií=N»ýˆÍ.Ù!.¥š@ŽÈÓVTè©ÐÖ*!´²EŽÈß«P©94¡ÒÒTO"@ˆ¨!ã“&> „Ò}ÆÖ”„'âœßasÙvA„´W~UŸ&©ÍJ]ijîÿ >K¢ÚT꺨bÈQç¹s;½À/ób©øÆÚ=+dâ@N”7ÛÕ©v%ÒÒ#òÄ÷ ªˆœƒ>‘AôML$D€ª¤dmØ$í]Ùhé ÝÐæ”Üaž!Hëdgnîî…Óäqß'˜JÎÑFuèJíò”Ÿ24ÕybwƒN!¡|UTUîAŽªÓ~zKÚ?»á>ŒQ¬s.^W%+_]ë)¶{ÓÚ}šËZxÐÕ“Úß Ä‘#¾Oø^·yÐs%ƒÆ eAˆ ÁI`ȸ¢£Ð íFIVB7´tQKÐjœ¿¾ïÖ©S•ÛÀ{Co(U‘€§R«ÏÊþî@TKéhDÓ*fC—{@~ÈñàžÝ¿ÂqœoF,ž¼=o©ím]Žá™£'ÿ[òôíÒ3coÐ>2X‘ãl/wµªmy‚ª<ä§s„÷ *†mï/íîSÎdÐø/eNˆ Á@ i¢ë¤‰Z_Y–oúìuM¡5LÓø%xkÒNS’ÄqZWšzt…{ùÏ?ÎÑ\Ž+›÷×\0ñ ¥ª@nÈOuØNÿ•:ÿ7È\à"Œä]gé÷a­R¦Écç¾,¥Ü>ıµmSþZý(v¥ÑÀ,¦Ó«™ì<Í6Ú†3§ö¯N#!·M¶<'ð}‚<ü¢Ý«S—ªÞCMU‰ÑõD€"’ ë:Qý¶u›®]dYz<6Ç0lzk\KI&mL™®ÜÊ¿…³¡Sª˜€ørŒJ—+7÷xÁ¶õëfž²4dË[^…ç(U‘rC~ëþúó«¢¢+Üî(^„Q#˜W1çê]¾tiÆËR§ÊSR¦Jlª}„|C‘ ,ºÄ h¡xSpQU×±S;mÌSãøÛõÔþÕ@ŽÜò´X¶mݺ™ø>AuÒîսƷjœe@ˆ D „$ܘJâ[©o5ø”ÙrŸ€îfÁ~8"€ˆh=`ð¯R÷ý0‚̈16ýä’Ç¡bI©lè1@¥ ™ÚW-Iù'ºAý/X»Nw²ÖCy¿¿I˜URÅÐ3ƒÆÌîzí¥Ì-çoXµb3òôX/rö¹‡æ×7--Ç`˜eˆuÍò`™œ@Œ¦ +gOåI+–®—6­Ø¤‚¾¥þÞy¨Ô­çHÆnâ½ÂæJ‰Úڭ„ž4f9¤ƒ»w|ŽïÜ€í_kí^¡€µp’ šZ€LE"@ˆ@ðH{# =½djø»NÉ6Aâü>PÞ"õšrÖ‚¼›ë:ü<„{~ÓbŒ~oÀ¸Ü²æ >0U«*Øè¡AÅË‹uÑ·_Î4êV#ëØåæSaõø¥—ËM C&ú, ¨ZÂ13ØÍ =3»¶lX°ì—‡t–°Fc·‘/röºA³ô“ÖaÖc‡®€®—CÀ¢â°Ùº@9z*ífC•ìß!‚༭Y¦%ϾcˇÏõáXcXÁbYúË¿ƒ¥#±n½†åixó¹‘a?œ¢T3ƒÝÌÐ3³ç¶o–Ìùz.\çóv/K–º í„óÌ`hfŒf†T§-Ãß¿²îÏe«€ŒÏÚÝŸ©“AãÏ­C²"@ˆ@ÀòxÞIöÅ´YMgË˺WÓøxØo©W€³(4tT§ógsCȳëšÌ¡îhº‚жðР2†J­ ”³5ÛÓÓ2/œ4ÈÕ¾cÿ­ZFƒÌÜE<ÜY ™5'ì†V²ËF^dŒä8i&X&’Ëa+Ø»yãÊ–¤þe³ \!x>ô5n{zhªeÐ,ÕÈÆŠ®€gz1.ÕÙ ò-;Iº²Æœ-V$âÁÛážâk³Å=x Ìv‡ã¨›ÊhœíÚ¸aÏŃ’.uuêtYŽ©}¶¿I*äaÒiÉ(9B®ýÜÄm<†ã¤™Úßž¿?só²§.1SøÿÇ«íùL"ƒ&`šŠ%D€@ 8æ*•oÁ€è÷¬kÿ¼zùÀ8ÖUÈۗ±KO9¿‘2UþB1f‡¸×»Aáøt½ ‡¿ÜcP”4öÇOß/€íß;tïÕ¦U‡N],‘QõÍK´ÁކOH%— æ~/:–_T—»玙a § ˜ÓÅ v‰DžÈµJãg~{#"Îå´õSï'i¼ŸU+è ÏmEÄ6èF¶¢.kØ|ù€»÷…6 Ú©å¦rÛ½ø Œ4uÙ¼þXÆØòö =[´êØ©cxdt=s¸%Ê`°„fû[çY öžÚ¿kǶ]›7ìV^k÷r[*NAD""@ˆ@à0`*‘ŸCЀ/Î0€AÒc@ɻޙŠkcøXðÚŒ-öÚ|ùÓåž>xµ­‘Äž_ë1ú*3ByJ¯”·°ìsú|Xãµâ:Ø ™„¼K ØF/ /ø… ¯…Û¨ìŠñ3x_™é÷×-Ílª£ŒëÏd?°š:‰ õ›JÝ ÐOáß%Y^d4™_ý¨õ€Û€A‘ö‰[Ï·Æ\…1Vn»Ã5ØÍÆÛnXöÂ6µ»Û@Ån„5jw¸?h4AÓ”T"@ˆðGîäú—ÅÓ#cU^t ®Áí…¼Âk“_÷7¿‚Îö1&ú—«ÇœBå4’PÎÑ› ŒO…ãx ü*_âÁmTn1‰{Ü{Áùy`BV¸ 1€J-²_éÑCƒž4fÊõÎà|0vÍÙ!…M. IDAT\lN[;¸¾¤cn—Jпœ4K&/1÷½ìo·ÁŽ"`ÑÕNÔîçGçµv?Q{4Ûv$9 D€Á r@äi൙¾hšñJ.¹½6`Р’ŽAp tjIÎÓ¹v0n`0µü}4«?ï²'Ž£’Ì •[TÐ1¡'öQaÇ/ÑhÐè_ëaú‹Þ- Ö¡`Ì@5õ$¸ %@ñ•­ 4`Рci›3õ­öɱÿ˜è¥¯&³‹á!»æƒiçÊMÔTWÚåL’—×Sâÿv÷Å/+÷Þjœ팷Š„¡Fíî*¸TºÝá6ä2‰ šijª( D€ø b¯Íï ÏïèµÑXÑmæù&Pç.2‚qƒÞ˜C™ÇNØÀ¸Y$Iòn6-Nk=$® ¢µPÚP™õÜFÅ•t »ì•eгaƒ<0 .B±ÅîdÂà³FFFÚŸ»ÕÞ¢M3­g˜‘÷‘Þ—[w÷‚›ÜÝËWq‹Àó7ŒƒY®€£šZ¯N› ùŠâ|ú¸‰B¨Ýõ&>ë²Á$•Ùîp ?|ðyã¥Ä½p(ø4Á߯TC"@ˆðcÅ^›™ âLì äP7j’nÜôbƒf*͵œk×2«¥N•2p6‡AØ’©õ ·ò)®è5*a¨´y*pâk=z¯<1Ö ™`6f zzB&bAFê5^Ø]ëW÷ˆç=͆ …E+Ð ”rœBOÎ_\––dyyÃÈžkǤg=³Ü{}t ¦v/®hsÁÈÓ¨Áw\ã‚çÊ7[ád°&2h‚µe©^D€"pN°í¡§ã’:ÍÜŽK몮 c==+šMtJ€nDãá |xo–a˜\Y2,4Þ¾£ØäyK m£R† ®ñ‹3*mÂ+ƒ†Œ0fBf Í}CŒÍZ7×:Õá],&ÞËld½ ²«‰ ЀÁµ – 2¥8” ×Ð5ŠAùÛÜûÒuî10q: õ‹Dí~n3`#altcö…a#öÅyýâPûCM¨µ8Õ—"@‚@Ò$ûnôU\R^3w”Uç0˜Ëfcg0+»ÞýJÔ#º§%Á¸œ$ >Ò.œ&Ÿço.I«$.¯Š‰…¯ïz(iq} ¬Q‘CeM5ø \@G/Y`3x<4ÝÖ(¡©³k˜…w5YWYâ `°t».J¯(,Å5 ®Å9XgƒA³ 袸F’Ã׺çHòÔ‹—é×ûéŸl÷ ÚB4®Å‚ï„Xı ²þSdÐS ‰ D À oß UÀeæª×›[rµ£ýÁs3ô›!`Ìtò¬h7 AíœáìœÜ£.0p6p9fÙøO±'Èó6Þ *p˜„1ãÞ PcfÖõ#ãêç'p•w3È­4ô"@ˆ€_€_iŒâÕÄ/„ñc!ÜѦØB¶pfXkÍáÑ¡ƒ{)plì)>ìàx"(¸‰àÅ ®p †ƒÞ _ñ7ëk&o2ÆDm 0Ñ£Ðýù^Lý¼ü¢vЬµö°°¶L‚5„ì–XnKØ—ÎÑÄŠ½-žmˆÛpøüÙÂ8´™$­W$eÍ  Om•¤d°Z &«{œ¦Ýƒ¿jÅ%ô<®iîdÐÔ” ÝOˆ ^! qédT2Þ+™†@&CÇÙöA5?(^Ø’iæªìº„küðÒ\ Jð9³¼ƒf×_ç/ƒ5$•9rsYÊ4i?(ΛAÏÞ"1i—Ì¥½FŰçÊ–#I7ÎÁî_”<,]Úß`]ûw+.«meŽF ,`¸s4ZÚæåçÕÃËUá[ÂTËϬÜ;Á Sùd€ÇÚ€mÑdyK¸fÞ<`Ra–û²â °7ÞÄd;i“$üˆ•VSÉËùPÓlé~"@ˆ U#0úé)OÁ×ë—”C£÷'O>Uµ»éêò,Y¯žÝYp±ÆµK€/9 Ãø6,ïú²Žƒ’ãWÀ}{ÁÐÙyì‘ei/(ÛL²’£ºb²OÌÆyP‚"­œÚ(ªÀ˜Ç55^ÓxœÌy—XÔºƒñx˜*÷Qk,0@A•2…üv€!¿  Í¸6šŒ[>fÝAªÌƒn=¼új}µÐu¼ŽÏÎ~ù™WjB€<45¡G÷"@ˆ€×(²²Ø¥¹^ÖŠ´aé—^Ë8Ä30\/Œ-*^tK¦†7UGwpô€É»ÃxœpžÏW¢_uæx 0dr;\ 0~BÕ`­i0ù!Ëa)S¥"P̳AÙφu()Ùú 8žž\pRäƒT Á¢hRA’óy˜±À¡Åx3ì4zM؆ ‘·G8œZ¤¬h‘ k¤ÄxpœE‚QI$È Û¬Ô)®ØHƒ…ÅŸæ',º ;˜t‹»Úú¾¾é±¯,ãîmÑ@¶¼^»a.` *{Fuß}&T²¸¼.ãè[³ Aëà&€ÿëá‹l€ÿý5­)½55%H÷"@ˆ€×Ü÷ÌKÁ°eö”g†z-SʨRа¯_Õ‰«jwïÑþ6vŸÒÇ}°xP<|¦3@Æ.°p †gÆ#0FJ†5÷‹Ïé׸ÏËh¬Àfȉ’ÖJ‚2Q’à ßײ ¦ÁZ–öæ=W=VˆF%"@Ê 0ú™) áêöá”g[”qºJ‡ÈCS%\t1 D€ø”—Þ„¸écžžrŬ—ŸYáÓ²(ó³¸ç%ap—³FV+”rZ«.µ­Æx0Ú‚.ë¦`t W#{ËY7Ua ÔGê—Ü‚ îèÜGKïã9Ó0t¥änolxz›²À²Ê£Ëí}R¤,®IÙ²AÉÖ -žñ.•=W”ˆ(‹þ×8„¡gÒIJÎWõ˜Ï¾¶TUºž"@ˆ@rò'aG\‡v@ˆá\ÉØø_³“Ç•À ÆH\Åq'¸€Ç"Xè²zP¢@í×»zé]¾pßÝíËä­Z¢·ò*„ü ÀƒSW”QÇa[*+¨ ä<†=æì$xT²…‘b6Gg xè^C‰<+œ;ý ‘ë554|·­¦ÅASS‚t? D€x•Àèg^M*ü@Í5åé›h€´Wñúefi³ú­jf¤ÝáÒ ÅÁ$»‘K2¬Q`¶í&)N÷¾ Û²“KŽo%MR …Þã—°H("À ;«4æ™—¿ƒ׃A“4{ʳK¼Q2h¼A‘ò D€¯ýì”Ç ìðLO3‡ßEž¯â¥Ìˆ µN=3ÌyìSèV< ¼¢ãf¿ôÌÞ¢Z¡½U8åCˆ D ,é+þø'±ÿÀÓÐUèQ‰ŽH¼|àÖô?ÿØ_ÖµtŒ"@ü›Ž™áZÑ e?0fÆ{Ó˜Á𓇯¿ÛŸ¤#D€„4ì~>æ%t=[ËWr¸œBóÔ„ôcA•'D à<3îÐÌü6èj6ÆÔ±ïóV73OdÐxÒ m"@ˆð;(à°óðÃ0)ä£0€»9ü(B -Ä-ñ#ð#Fƒ·ý®ÅH "@B™€;ð‡aBZ˜gÜ'‡àö›ÍŒÍÞñF€²Ø’AS:Fˆ ~Iàg_í£jê`®4MáG,Ê/%¡ˆ !J š|˜@öT?N˜üþK“ÓCU›"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€„ÿˆQFàH‰‹IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-dvr-flowns2.graffle0000664000175000017500000001162513553660046026326 0ustar zuulzuul00000000000000‹í]mSÛȲþœü Ý|º·ˆyÉÉæì†]L’»)ªn [€N„äÈ2$»•ÿ~{$Û’%,°ƒ!U1X=/M?O·¦§çÕ¿¾]„Î¥Ÿ ‚8úåvÑ Çºq/ˆÎ~yñáè×uõâ_¯Ÿ¿ú¯íý­£?Þ:ý0¤ÎÁ‡7»;[΋õÍ~?ô76¶¶ƒÝΑull¼Ý{á¼8OÓþË««+×3Rn7¾0‚ƒƒ$îûIú}*[‡n/í½€fòÚ§ºßö‚núúù³W_üï¯7»ipéïzßýd'êùß^m˜oáb¥þ™Ÿ¼F¯6Æ¿Ž‹@ÛA×K¡ÊyÍ“B^’xæ—g¯i·üzèÆQp–Äþ»¿ý–x§§¡/^mŒDJÒXh— è>Uyµ1®:ïÂ07{ÿÒIÓi2ô7Æ×ßxÝ/¦É¨ÍõσîDltï#1#1_›tãï¿Ñšƒ~¬9s’œ ‰8“Lq¶æHE\¥´„¯T˜ð?¦îÅT¶zƒz½8 ŠUŠìlòã'å±ÏÅ:é÷Ð/$Ç·“_„*ã/ÅÕÒåüúvâ]Ý*:¶·_î öd”Æ¿¿äÃ;ð?ÇñÅ“eË‹.½Á~œÅ4s6Ìå§œ•ˆÃáE´gQ­~\¯?“îô½.T1‘O|/|MaŽe¿ŒEá3i·½Ô¯v… ÌÖ1^ÇÈÁò%%/wþà§Ö;SIœTË¿÷ÒÔùºwå%^µÐv0è‡Þ÷N× k-ƒNÂÓ½p~q&¿W+ø5ý£ïýZaô *›M°í¸;¼ð£´ª “$µ‘MÌÁ…žžiÍ)©“®3¤¦\I-×а«•"LbDˆÖåbbÍ!dZ{šÔ§ÐŸs¯ï7)ÐxÒ#ÿ[Z/÷çÛNƒt_ÕEýAð—ß GéNtÏÖ-˜ˆÅ´˜¾ž œ”.NÚ°#ðO©©&G%ΚKp†1çM’æŒZU :^ºÃhØïûQÇ‹•Òe#U”ÈtMMtnª*Ä3Q‘)ЉîÑ58V®¨T¯Ô” ă -éÀ¤«00Rʧ‚k*“¥Îç…ã4ÃŒ²ò•Û¬ö½rÇÓh]»Ó ¯›2U¼žØõ§98÷zåù½Ðº+,³˜º«CWQåJ¦¯ðsœ¤§øæký×íŸA“ãnÜ=s‰!–ÿ1žÀßL¢çŸÂÔOOÂãSt|ðÙ=÷’Ÿ"g<ûÿùĺFÃAîŸÇ‰ß#œŸ%¾™_N¡ŸÙÐÍü¦*»À¡ðqßKzÇé7.ü1É>„2„°ìC!óAi&Bu&ÂX&ÂQ&¥Í‡À™ˆ bê ’ÈK‡‰íš/Òàì<õ£S¯ D…ž?Ïîk€…sÜ=%ÎÇÝÍ=#ücåã{臞17A'šhd&á57áø'€êzÉéIP26楢¨Ëグ´Ð>ÁÄåZ1B5—†i˜éQ.­IÖ¾[ -&€GÆ-XBi„ ËÏÍ \m¡-K¬wü‹à${óÒ…lK ©ºxïE~Z~^ÃåÙlsóYÉ­X `WŸî”DÃó0LYŠ©žüð25=è‚Ã\%uCɤ±$φM‹O=ïöœòÆÀ:>oš_Uì6´rëÑel&?xþÑEJ\[²yt)aƬ˜ñ\Êw?¾¹$ò“C¯ §©³fV=Öœ A ú$”.™_@tô¤D¡ÿñÿü¸ÉcžŸÎ(á®a.Ž×‰qnéìÉЙ‘Îr§VrΑB03 ›q3ï–i‚gŠ!4?Ÿen.ã”rlZÇÔR›¥6Kmí¨M,’Ù8$ ‚`É´’R`¶*ÛQb˜jWÀD¦ ÌY 0Ée…ÙÀcUê!0› ‰Äü®kClJ3Ê~2±µ­Ç–KlæñÖŸúV{õ§}o4()§¨äÖ‰¹Ñ™`P:7$K†%hÎ ÿñŽÜ·ÌW…–øžñA»ùÆò ­žO·Pâ®ÐDcŠ8سZtf·á+*è6c‚>.—îqŸlO|üQztƒÊ¬gÙì)°Y:›=ò”–ÍFlÆ,›Y6³lfÙìÙŒ.’Í4¸a0ÜZ*&€ÀmZnÓó²b–Íî‹ÍD{6£ d³Át”–«P[,Ä! ÄÕbõ •ÛAvõÍ%º–DÇItL1WÂÝ"!U Q¦I¤Ë¹FœQ ôÇ”²nÛ£tÛð#+¹>Øñš°^à-oVR4©,±Yb³ÄÖŽØäB‰j—“L©¨Ö Ħµ%¶GIlè1£à§Íÿ#…Èü &¥¤Êú[Ú²´Õ’¶Ôbãü©‹™ˆ1Ì•¦œXì©ÐEvךݵfÉÌ’Ù=F8¢…²™à®š(è²d\™l–Íž›™e!ë„Y'Ìò–å­åó–^$mAï]Àd¥SœIËøDh‹“G˘ExH­àƒ?&iËŠ4€.ÕJ¨–ÁÔˆ‰ˆ`–×,¯Y^kÇk¼=¯Í`“-ï¼2™ßùᥟ]o^D6ù9fBò;ßëµÍèWCì‘ðÔÝÌÈ:¯/ÃI)ÄðÕMœ€1u|P³-‚°ô±IyA°L‰1ƒXrW¼¢ˆI¬– Jsîb¬ 6É4x ‚Ï*]—lh›Ü±èÆàÕæÃB6“djt {ªÉòÚõϼî÷)ñS/d)Ÿ•³fV2UÎÔŽ1˜{AØ®'Ÿ‚^…œ³[%ór 4Ø:³g5n÷–8`quÆ®žeà*¾-®J”gÆkð† ®ÖR>ÎBÆÆÒ‹]OŒr.ˆÙ‡®tË]O J5TƒUË PàW3 *ˆ…ÆC#·Ð¸PhäˇFtWhœJ®Î‹¹dRQÐQ¬i7«…F ……ÆBc-Wå ‘éÛB#ÓÊUZ N˜š½æÔ¹’.:8ê !fAcD9µVãÃÈåi¡ñVÐhÑî&´«&š_Ú©Û¢˜(®6J²—ŒŠ·»†„Óà“eÈ'4Ø"ZÒ™¯µt•RSv(g\[¨,ÇC2 pE¤‚%íâ!5Ф¸q›ö5ñJ1*`ì‘´¯Œ”Y#r¡þ5Z>¬Ê[êԮF – xÉ”±6¸ ö ˜<”"³„`ö Õ kõ$1­£\ÄIN5neÇÀªgÆÅ’¶ ¸3KïàakyMæ% Œ·Æjè¢ñšýŸÄCE€žkÌËM1ט/B2Š0†ifb¸ª¹~Œ¤ ®áJŒ‰´²&+ÃÕøÐñêïË$1¶*‹œC×g hÆ­<†®(IæÆ­<˜nVÆc«î7¶JÑÑù†(­Â˜¬Zh•³ÿ±ã$ñeÐóçMôÎüãûîâ³¼w'ÉzÔ·›a<½®oNòmÝiKúVDÊÝ2‘R±æ(î" ¿i®(&Z)ÚD¤‚®9R¸œ{Wƒ× 4*-‘.b§¨¶Dj‰Ô©%Ò Ò_ÃØKaÜœƒãç{Þ…?è­•žý{¦PŠ”kV¥Àœ óŽç6J•È2è"v§*Ë –A-ƒZ]Ý̸ S¿çÆð‘8+È£_“¬k÷Ì¥M[b½ì%BSàH& —bé"$MÔ8U¨5§úÿ©™ÙÊ‚[óº)Ò~Ö‰AÒ5ôµ*'$®öÂÐÊ•87êß¾3wZ™Áš¸Òd€UŒHIÖªU¾àBÀÊURÑ,) Æ`k‘EªhVøl†1‘K‡åÇ8=ô™N.LýÇä¶´cV™nŠ%¿£6ßaß«~ÜmºÏcm†ÁYÔ´¼ÙÔð“-¨»N=ÿ4õNÀ~&æ¿ñW³ŒÓA#Sçã¡cÌŽÐgë*N¾\oQ|e®káôÓZ –Ÿ0-‘A~ÄrB`bPnQèn(DÓÍ[²(ôóQ¨ã‡§ë?¹ ºþ…ŽsgG8÷vŒLºp€ð³\ì¢ Â*™‹M¼ør\‚j*Ò ‡ëì<Žt©OÝéÆÉÛüaO&y´IœWvÓ‚]X° 6Xs>g²ìʵ WÌ‹úE¥ˆ=µcQ-éYÒ³¤÷IÚ¬™7fÍ$ËÞb 0vÛ-–M™+kçFÌÜ{^Ùg.ÌÎ/iÒ÷¯ÂäÌÔp5?ÛëvçúcÝ Y³ê~ÞÍÚfLÒ°A“dï¶+ߪúþL-mPìÞ]!kƒb­oƒbWÀŠ7Û3w %^ÄÑ*îЬ¼çhXŽ]D€ 1ØÉJJM¸‰b™>tg$jο~P8}ø(z: #++¾èSßÙ‹{þ*æУ-±\šƒ˜ÔøT n <ù„\Ë!6¡ÀJ&ppq¤Çîæ^~¦AÔE® ¿·3=2 Ø oš|€!"Èœ?xýÑÍF"x.˜óë_‹4‰Œi ,>ùQ6ÅÀÂR æJÂßèÄC¨»Óõ#/ b‡,Ц*'²²¿%A¯ P“ç>¹Úé{]¨dÒÀ(¦ÅUÓ/ ý­ü'N‹OBQ*Žun{ÄWSªoÆâ\:òúƒ£xùâî³»‚I;Ø»_üIÁÆQ+I ÁI-3šÉ˜¼3¢ŒÍ‘79¬]€àbê'sˆÿáûý£¸ÓõJ½˜Ä9å1NÞw?)Zž,BU8ØÜøMÓ%—4¦7Ešf\?€?ç¥ÑNtýÁ|ÝøøW7V;k‚CoA™fOñÍ(¸ðR¿6*êc®vÁŽß‘9Z¾¢å4U…lÇ È2mVÄÁ|­È‡Y7ßFgATïK/Nk1 a±‰jÛõ£³Ò’Ø$ÖxåN+D()gÃ*Ê·è  U?˜K~ZÙ ‹_fiO]ÙFK®-ä =s¿´N›Oü‰V¼{lÖëíÒ“WJë­#ŽàKÎ_ríüÃŒ\­­¬¢’®Ž.¿÷ÒôÜ¿rþãÓ»‚ñ©Ü‹Sþ[ÚO?J§¹0zj¨Ò0eæ®Ü`ÓxN_sÈuž­8{7qšÆï½zṖÒÚ÷¨¾Ó0öÒiµýÎð´ •·ð.N‚¿âÈ ¡ßAT™xõ†ºqÏï56ôf3ý«÷î÷ðÏOùÿû&ü¼ó.Þdÿ~óïÎÛóÍóýˆã“O¿‡Ý³-ø»·ÿ>?y÷1Ü„ë[oùÑùýëçO{hsçÓÛÍÎWùõ[xµõÛõÝßsà.ÃcPåºúÀ¯I¦@º¡úü£±þ`¯{lŽ6¦lö¦6jÎÏØ>äš»Dr.$âL2ÅÙš£v•Oq$Ø‹?nè‚™ˆ‡þ%›ß a&Cp™Sá(î/£úi,ÏÄ~ä™aƒú^o? ¿ß /‡ñÕÔ«×kL­˜‘9ëÐÂê™ÀGAZGÂ-/ºôSFK^âŒô¬fõ'7õ6»ip9¥—6Æ€Y¬oYöC|ú…g{ÍÝøo£4H®öóŸ‚üÔÙ¨¾5L‰l$ë6{ƒÉþö[ß‹@Ùþ/â’MWððèUA2…TÅ›ŠÍKd/aóâG5ý~gâL½Ly•&Ccêúá0,º&èÄ+].ö ”¯O‡ñ_wó uðßùF½«¯šP#–>ôϦÐk2hÍÁŒ»Œ—þÉ,¾‚kéâ& rÍiŒòÿm±Ïq|QµT]^5ÔG\çA#x4½5¨Ó³æJßæ0 ¬8EDÒ©>œÕ §øì‡ k¯Ÿÿ?‚3]ëneutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-overview.png0000664000175000017500000062220213553660046027110 0ustar zuulzuul00000000000000‰PNG  IHDRà-xúYsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|EûÇgöîRiJïE@@¬ ‚"v±bW,$¢€¢¢'¹yõoGHxíåUì]E)‚D‘¢ÒB¯Ô»Ûùÿž½l¸\.É]r—\’g>Ÿd÷vg§|wvæÙgŸyFL€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ $s`L€ ÔewÛg´+r:» ›us–}Êæº\—`Ê~—ÝqºÛ)¬Yé)KÊ»n‚ýÉfyÎÜã¥E4×tÛ_CûvÝ0räHwyñù8ˆdÔæ•[³ÌNKZÉåä²1@X‰Äq˜@$H°?ÞBså/”l¯lbm\“¸?žœ4)?’ÊieI°gÆiî}Ǻ¥»›¦kJH±]Zl϶OÞ®²Ž·ÏèPè*#Eô+™ŽÖ‡+ŸB—ë!ÔSšÛ5 y<®|‚Mw\²£³[ªc”’­…EØ~NŸkB%»\ê ”©™RJ“R*ßò%¤¦ß™ëÌ} lâDn]‰«Ö¦!ÞC¾qëËïÄÔô›”Òg9RgEJÆgd´tÉÆJXWö¼Ùí ­;ÅòNn›žŸ•œ¼=Rê)å 6/…»Êc‹”2q9˜@U °^Ur|]°Û•¶Í™~?¤ŒIÊ™ßÚ£ÂÃ/§¹ûrõÑÉŽÕVM»{VZÒâ-X„g6þ™g¢‹væLVÎÝ“ÜBEÅÕ葸¦ë*!%íGMÓž=-y^¨«RävŽVJ$ él´Ç„:ýHM/ÑČ"¥Î18Ó?7ý ±`庽 )އõí9:T‚¸?cíé':* çò„ÔžÕ4õµÒåña}×_üúpŒÚzáΜAÚ–žþa¤°yú“B©…tí»7=½ÏÓÉÉ;Ëã½Û¹´½K¨¿¥S~8ÃÊ‹ÇÇ™¨ûX¯û÷°Þ×`Œ}z—m®ô×”Pƒ¤„@!ä—Ðà®’JmUB;Òd?@èVîoR¶³ |Ønêª÷`¨`ÁΜÙà3 ÌÜÐ’~ýù`V¨)ÕAIuäÓ ˆTðQ¤\„û´  ¥BBJzªzüGê”R'êøq©çt9Bæn„¶ºÜçãïW!U+pè þÃQÅná¾ ¡Ó¥îF^RjÚ„¬´äÿcý¨x[/7ÏÞsO!^(é«@óvÎ$¹É‘UO%ŽÎËןC¡ðŦæB}}ÖjŽ çÄÂC€ððpåTCDŸ×ebJú{¾O† ³Ê"l#g9\ë›|ÂCéCÃø â'mÓ—ÄùÿøÆih¿S3ÎÐu÷(‚»4«å´Ùö©}ŒµOïÞJLùGˆTßSÕþ9-™ï.þÂý¼ ÷s¹¿suù˜S¹žÀKM#©Éÿ þS|MCìö—b¶‰­Â]GLîéA9`ú²$ÜyERú0=á)OèÛsuë‰çpÚÆÕ‰)Wf:’Þ¯nz^__Ÿµ@ëÏñ˜@¤Ð"µ`\.&@ ½¹‰„o)äÆö¶ö§ù¾)^Ö´äϤf=BÉId'NÇ+ ôÙº²8þΓ åïxeÇè…¢ªyV–¶ïyh˜O3Ž)9ןðMçž·OÝ`·KÝ÷ÚpþNH}ôhƒ»›Çý=Ì5dOLüêÆ¥{‹¶zª‘Ž%æ1_ᛎÛí·2Q´ªíëHT[Úׄmß‘cÁïÙís£‚-K°ñÍRÍ;×RSÏåL{ªj̺ÑŸ#î™wMŸn˜ƒÑïªjß•µñª>kU-“¿ëªÃŽæ®øK³ªÇ˜1£1=§U½ž¯c¡$À 1”49­ yÁªuÿ¢ÃìˆO飽>¥—›OB²ã]AWiR<éH`FÄä¬[09+IHÛ5šÅ²Swü¾ÐŸ‰¸Ý ÜgCjÿÙ*m–'àS: öŒ^ÂåNÇPzŠQ&\‡ÃË5›å~.ÙW#•ðR1åºæwš’Mñž-•ü\³E§V61‹ò’öÖc,Òz㬴©+è˜o¸ËžÞ×íÒç¢|›²)çãåå>ÔùqÔ/ eHôïûåuàØÕ°žÖo–9Ÿìøåî‰:½ˆôÊ|aâÈ’B VÒ’8'-é;“9Ê=÷âYc0u횆ë‰CShsGÉD3M“SgOKùÀÌ—Ê1©Ž»1ct$âcÒ­h†k£~«¤ÐÒ¡EüœâŽNI¿W(“ åÃQñÚLW®> ¢ð@\~<òÎÁþŸBÓÒçLK&ó„°0‹Lï(üV/Aû?Óó“ú*®•Ÿž²ÅŒkn;B³jWÓDeÌ•˜ŒsW¡ŽýqÝÆ9Ž”n—Ú<êÚu-5 ³˜K&ò: ýêë™é)Ô—á…WiÙîŒQèÆâºÞx.âËÍøý¥Ð¬YYiS~§¸˜@M` xMçü&°ðÏ¿;£3ïˆÁè`;Ë™/t¡Eƒ®äPïøºG¡ó=V ×Õº³p:`hË1pIñ::ù\#Ü¢èÇ1¥Ÿï}¹OŸ!|ÿ„„>q¯6+ÂÈzLu§û·1)iC̸æéö€ q„ï$쿸ÍÑéŠAâ üN ²¢!‚À¹œÊïRú­fú¾[—[ÝJqP®=çtÏÀBeà‹ê¶–®Ç`u©oÚ$ ¼—zÒ'›òÒ^– °]‹£ÇÄXýJgMæPý_#bm{ƒ{'0Yí¹ZÄïU%ºÚï9.¹ÏƒéÑ|¤ñ ñþˆ³÷êCc êtp,Ûw!Ÿ¢\ý{Þ xnÇ@û2þ~Çõ…[ÿ<1%í63ý0n=ÌóÔÍÁäQ•öå›~~^ÞÑà4õ=„stÂ÷Ÿ%|ÝŠL³* ãì=•*Âó!nG›ÝOÏRùi¸ñ,ƒ|­¯ð]•²Ó³¡´ÿØ”½Ýº{>ò»ÜÓ.`–¤iU­½ã)¦gŽÚ°O¶=U¥N>Y–ù©çêq±qrú³ý`yky}M™ ‹ö6Û™þ9žÃx)m…tÞÁ½ù}I;Ôù9¼||€tñhȳêçòMxôѦxñŸ§tõ,îF/ÜÓ¥(ãh‡ùô‚‰9+ sAŠì̾B×Eçù+×=‡:Ndz{Úß׸©ßyEõ»‹ úôbs'êôg[ÛÀGÍH8þ4渼€ßÇ ßãx¼¨ÿéŠøwcjŒÑ7™ñyËj’€µ&3㼘@0t]G§‰ Õ?NªŒÖ¬ë ÜEt•çZÚó Ðú<„NøÒÚòÎ,{b¢A ƒÂ4 )º[ÍNÈÌ왕˜ÿ*ž@«rå¿„_šÕ&Ͳ§”Ø.c ¾ZWîwÜJfâºã½¯3®V¢Ê?U–KL-§Átÿ¡¢wïíÎì±8ô„¿¼VË+Ò¥Ûÿ:|vžäËÃøZ°rÝõ˜1æ©—)hç'¦:ðÒ N‘΂ÅcR3î¬È®Í5ßé,Ò//”–SWÀ} Ð‰ß‘Þ $¨Í´'­£sü¾aŽ7foɳö{H³]&{I°r럂üâ¬ôÔËDÄ<×áSçb^,båµs¼\²±?ÖªPÖ´BéãIHl£²¦L)6ÉŸÒõ9Hï?`÷š/;ùWõîs†[¸?!! ³¦£¦ÿçÁI .7T«}y¥:;%%?ñòAš`ÇÜc„5öŽ,ûý{¼¢U¸[䙼ÙwúaLæ™4ã+¹s9ªSv4Ôn.Q„ Õò«¦M£ïx|òd¼{B‚ýÑíÁ¶wóZÛ`ÚSuêä/oó˜’¶¸§“Ü„ò‰8ö2ú·,ôÇWÖ>Ìë*šŠýó©ÿŠiÕä6štJç _ï®ÜOÐ7\Ž—Ö[pè•@žµ±öÿ´ õó.s]xnÕyxn?Çsxïçptjúux+^›h¢û“öIeŸa]OËE'Í¢]¨g&ôq‘C'´Ùyím=Ñ?z&àËÈh^”ë¾éí·ÆõôÎÌ(ìy‹ˆ!&PÊj‘j£œ'ðCBS÷âÛüœö{¨¹x`„0(ZT<\‘v°¥#ÊCÖxË8Sø¦sô©zNZJ*:êŸ1xt‘[vÝTêW~ —ˆùŽ·ðMq T¿k 6BõÔ6ï6„ŸR×Ò)Ÿ÷¾é1èJùíëR`‚iÅì†!zµÜáZvoìoV¯†²·EUáóìßtžê¥ÅÈ+ÀãœëMã þ?‘@êOëþ¼ý¸jòhhÏèã÷+Á'šÃô^6èK@IÀùsè4¤óJVqÂW'h\¡q±Ú5¾¦d²c²ÞY žyÑq–oá›Î7iõ?J ÷°Å6×’ÎÞׄz¶#éS”Ÿ»¥Z½©ûsœÙ`žiØâ–—YuÛWyéVå¸Rè2ÍÊ'/wš3Pêpuʯ HkgfZòõÞÂ7¥_•ö^ª\^?‚nOÕ©“W¾¾»ºÔcèLK^Á“2í±ÓÁ磾ñüý&ao>÷ÒfµZRLá›â>eŸxÔ&Ñ>â¤Ð6êç¾’éJ݃öØo½É÷9œ“–üŠÿ<ú¢£ry©þÊ&'iR¨ðM^ |ßç{±´µ¼Ònih_(mU¤:ç±Ü[ø¦cÔÞ`¦ÂÂw1 ÞÔÀk‡;ç­•MÉ€5xÅŸÇ ÍŠÕ)`¿Z:àûì·³¦N-1uð> Ïsè7ÝAÞDZ ý¶HíŸãÆO|.ÿžv°èÊ þÎ[„e®¿ãV‹øŒŽ£L]ý÷=†‡õ%: ë;çÜz±Éƒ’Fó¡¹¾çËûÊçݥɳð€E¡x§¼>Öbµ>OeÁ ¿È_™H B/±þΙÇp-°öþã0Çe8þcmz‰·R…ηg®Fzñ ž÷´÷ÐWB:Î D 6A‰”;Áå(C@ }sñÈìG“]&ºqf 6±e—a×-b7—‰%eÙcÅ‘¤Ò í¬(»˜×›vtŒë÷XØYŽ5ÏÙzLe`üajìœÂ^TŒ¾µÔâº+j¯…4aЪ<´µµÿ Ûµõb^F¶–¦†‰>§îß^ÔóKµèòâ˜ÇIûó€Þæos£};3) ÷¬òð¼ãÁ5ˆu;Ìe¦¸…· ÿñà®tŠï1áìNh}_§TBѾ*/Í‘•ÕMvjù´Ú¼{^F¸]®µ¸×c¡¦¬Y$}å+(‡¢ìQšõ×#¥+½WÕö^:cý£bs´ÊÛS(ê䛿¿ß¤á[øÀWÏ¡_yBs¿ Wó%s" øŠƒëþç›æ6—r&ìô…ŠÊÏɧ¸«}ãøýÂçýIqÿ'ÿö›¶S6nédÞוXûúÃGß[n{ðJ3“<ÏC‡ü4^ÒÚâ´ ŸM*köF_É07çrHëŸÁ¼ðiL윈—à9°Ã!¨{¯Œy— „’” ˜@„P%yç@KµeWCXÄD'ßÏŽž4t`ý«¶ËsÂãÂöÿùo[ j6㸒çÃ4Âß_whZöæ²cÙp”³¬V¶l¬Ê ;äÿáyØyµyÅ¡œ¢+02ÇãÜ\ßOùfÚ’5Û‘ú >Ô¬ò Áb%êôB×A»¯­Ön14iÍÏ&t‚#ÒÇò‚ºø€¶0PøˆçˆKéWážCgR™pÝß zt>Ø€AÕs¿å)¥¡‰=Å«$’îÖ¯‡ê»¾ÎܲBy%I 2—™“žò¸èØŠÚHÚR4•YwÛg´£kCѾ*+ƒ÷ùÊêFsÚÛ’¯Ä‹Êx_Y‰û|¥Û­ Ûâ?aºtƒwZ¡({ AfOþCuÛ»™j0í)u2ó­l‹9Ïã™Y Æ=à½æŠï´YÏ mÍ€vcöÇ”ÓÑäñê‡ pôBOi‡òyÇü“Fy¥@¿â?Pÿ€~r7õ©ÄÚ7&r—Û̸¥ÜêmüÖNSg¾îw ÅG÷­°ÚàG›…>­â§c1$¼ü8^öo¢hæÂ[&~ÖðgÁ90ª±âo‘k1Y‹´¼ —f.a9ÎÐyIeh³Íã%[%[–ìûî¸tCs޾XÂÚĶWìubPTy\Éf5èððÃBÙíA_æ÷‹M¼ävŠ1(Ð͈ðE‚ äMÅ‘_*ÞVº™mOù“GÁNù è$`6¤$ôŒNv,F}ÏÛá~´4íÿˆC.š ¹b¦#e%l“–‹”î€ 9?ÿ[bÿ-Ô<:_í@‚4}dÖUP¼ñ¶`|š®nþðŠ>ÂÄAßt´Xñ¯ï±@OÎMÛ¾àuz‘Óy®}%í+Ð2P¼@êVüõ&¢¿9:uú)°GºB"&Ðéo@ƒxžƒ{)­P”½²g#$í=ˆöŠ:›@½c2óE.7^tä¤D»cnŒÍ½%ôÃ>_·vãP°ƒÿã>§«ü3”Ï;„ÿµpé¹5úQ…BÛ§…Õš£O×›‹®%ý¬×÷+‹yÜ{ qø½_fVʽi>†vºlNÚÔŸ¼ã™ûÅ~÷ÇbÂëƒ;oE'àš[±¦òEà?È{2¹y o™@M` xMPæ<ªD`Vrò6ˆ ¿Q‡›s°hl ‰¸…~Ń‚¶X[[æª2Z3†Ž®ž}¹ÑA¯+Ж–ÑO,o0¯éölÝH¨æ› ˆ6æB[¸ç®”ÇRj¢ì¡hïÁ´§š¨±3 šÐìhåR/jÎX?â7ž-¥Ö×HÑ˼6„ÛÐ<ïš Ó4Vv¡¿˜‘ɤ*Àfï “þâ–LºÛÛ^”™–´ vqäoª¡en㦹¤˜¿~M¨WÂ3ó¨Å­ÁÔ½˜GVûòÇ&+- žtÈï4Z›p÷8'üe¯n{¾=…¿NGøÁ×fßÓ&®ý¸8ùi#1€Pt% Ì0xÅ7?š¨DîË|ÓoÃ×±r΢}hc?£­fÙ“W!ͰC=’À`ÔýßBáùåCŠÉM÷cð³ÁZÜТ™iT´Å§ãé<†ê³È‡±o\Zéù“€ÿ뻟1´ì^‘èÞÕk¯l+Ý¥/$äÑ‚&—ù‹ŒÉ—7áËż¬ì—Ö¨$’Ú¹KôõŒCu¡{€Óî´/­òOÚR¨‰²ÚÞ=%*û?ØöTuò.%MDÔ¤í´ 'Æ$ïsæ~¦=éO<ë/àí§YQžûeÚ^ºGäŸß¼†¶•=k'TÏ»1‘[JZD§yÎÂ'|Û­xЇþ>ô'EÈö!Ê;¡iÓ˜±Hs ú‹Ëhõ_3MzQ1\Pš¼¶®âIÖ`^Ò–½Nó.¨ÖÉ…3aÕ €väÁƒ…OB³2Ú-ä7°£]ÍÎJ$™Ž·:ü¾Ûaÿ€EÓn†Vdn¹ÙI¬D©Äh¬(w:\S}o{÷Lø¯%Ù.LÖ¹/³x3 rûÏ“0x`UF÷GØÿå¯:æ¶∵O¶¹÷Á>y¿k ü(^JÄ…†® ž üiŸia!§»è,Øíþ 3“ð¶MƒPÝY8 ÎÇ6ÐÂv–”-°Äê„׆`æ{¾eê²l=}'¾L §sxØþûù‡§ü‹òGÜ·Só\¹?£|/â% ×"ôßi’(¥gµZ';]®(ÃjË®3ÿ[h¾ra³>÷®?¾Ž<Œhþ¿rPµ uW¸xüfÁªµ{ÐFW€[6ÖëÜà-A}<«œRhÂê:ïÅq"©})ÝõX¶¾ôDLRûå¦eìAð; ž…AÀégä-o7x5Qö@Û{E·<˜öTuò-kfÚ”•£S3`?â9‡Öã¢â´©Î<÷©¸—¸»×`‰ûx)"S°fÆ3½y÷Ð"i<‡×š—ò¬yâ†æyooMš,íÑ/$Àçþ)ècç£ÝvÓ×¥\× ß´áÙœåHö˜™™­Æ–æ±§ÔêG ?š˜šñ=™§èEz¬^¼ýËwðà´ŸþE6mñáÌ›!eKž°Ê§ª‘5_ʪE€5àÕÂÇ×ê`a‡šs”Ë d/„P€G]ƒ¿ l†cÊ…Ýá6%ûV(|£°ÐÜ~ƒY„ƒpÃd\ÿ_Nš,f1Ìtç[/òí¬Ië©<–`°;Íȃ%¶·C¸í‚ã/©x½Òü¾éVý·å%äm£?üMû R¼V«çdSX±SÜKÀ/G²¡Klgëyù9×; Ä™oþ–Òöž¹oni¢~B¿Á.¿µÃ"ó\e[°Rð“} ®üõ1(_:^ žÒ…Öϼ–|‰·6:‰¿ ,µ­ße,¸U=!Àþ†uµ7R¶¤m‹÷ åŽF»Â Ú†®@oo^ßÔlZ_´¥2_ "¥}iš$S£Cž2«t´—ghÇšâ~=ŒûLû¥BÍ”=€ö^ªT¥Ûžj¦N¥ËØÞÒ“V,W#k¸ÀìØêÜ£ixÜx9½ÏéÞgZŽÀ±¿°VA©g5gJªçú´‘áïóY<Ý©ý},Mâ…©$gCøù‹ól{òjt+põ‰~]éo“öÛ"l´xÙGÈ÷L£ïÐÕLÏ Ž"OVßY¬òÜ,{ò/TL 6` åÀêñ-‹òµvÊrÔzßüÕ†LTHȃ†ÛŽàŠC Väí/êÙ´‘ü'Ðå é:21øîÏ $8ÆiºkpŸ®;}ýÙR¼H dóžw¨¨…[Z†U}žËÚlS Ìj¢üd"ãE0nî*öVà7۱ɎŽn›Ö¸­˜úW±—¿ñ"á µ¥ë×·(pJØêêqÊ&³3“’v0Hù"¡}‘ã*­5·Œ•Kö̇'ï ¤ü‘Pö@Óž"µN†Jžè¦+KNÓÆjGe}X ÏZ ü‰c·ÏÚîÚØYZÑmDuUŸtHnåÇ¡u ô9m\6ÑÒ*´§h¾5Rú¿òKÍgÀÂ]æ:6â$€“öþwf$L€ 0&À˜¨-l‚R[ä9_&À˜`L€ 0I€ðyÛ¹ÒL€ 0&À˜`µE€ðÚ"Ïù2&À˜`L€ 4H솰AÞö†Wi¸ÉÛO)K0Ýsë=ט 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&P7 ÜmŸÑnl²£c]*}BjÆi öÇ[Ô¥2sY™`õ€¬•à:0&ÀjƒÀ¸dGg§TÏ%ÏRBe”AŠèX «m|–}JÄ.üd·/´ns.=,¤z>Ë‘:©6øqžL€ 0†J€—¢o¨wžë͘@µ$¤¤ ‡ð½J ÙBl²r Ô,§K©MVJöR.çÊćÒÏ­V&a¼ØnêRš˜$¥í•0fÃI3&À˜€¬÷…1&À*"0þ™g¢ wæü¥¤Úom4ì)ûÄÞñÇÚg6r¹ö!”h×ÎÖ³·Ý>²Èû<ï3&À˜@Ã&`mØÕçÚ3&À‚'P´ë`‚¢‹¦i ¾Â7¥ö¼}Üá„dÇT˜¥,Þæ^—€CÏAcþ‘TrkfzÊ8ß÷ióÝç8R/¦s öÌ8áÜ=y ‡–¤µb¥Ej)³Ò’{_‹4—Ãüå~Í&Št§xñ‡H©Þ…þ$‹MÞ8Ûž¼ºT|{F/åÔßFZɳIŸÒõJÊW礥 &4ï›iY”üÞ^KeØÿþ¼ýæ9Þ2&À˜€‡kÀ¹%0&À‚$@Ú^¸#Üâ=á,ø Zíé0µøÕjƒŸ·81וû,SC;|…¯ÉG;kÊ‚mÎôlÙÏAàÝqnßîóJÙd7²¥ËÃΛR|˜’1Suྰ°+Ì¿Oƒý¸“9íA÷”cZq|s[þåAå­-Ð…~3ê Ç5‹+1³‰±ÅÓ“Xá·)_.?3>Øh¸XÞpï=ל 0jÈš–¼¦' ‰åø{Úì¥pÇ· váÀFúg«õ%_à¾Y}6ü{¿;rºö _\j1rÒûC)ýu]®CÜyJè“|ÓªìwœµÑÛ„›à¯Y¼5þ•Å&oMSóabBC¿|rÒ¤|3mZ˜öá‹èœEÊÁÜ<Ï[&À˜ =˜`L ÚÆgd´Ä‚3ÚÓÉÉ;«Xqsçε,Xµ¶ „ù¼a=zìòÖC•¿tj3oåácL€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜YîJÞß­»ÏG^•TíiãšÈ—ó`L 8JˆCRÉm¸j“E³Ì›•6uEp)ÔÝØÜOÕÝ{Ç%gL€ T‡@mŒ}aÀíö—b²Ùw ©îJtRèBÉÀ·!ÓÃÕÅ×2&è„Ao‡ç¶µRBÃ+úV<·O··µÎn¿­ <¹Ö^ªÜOÕ{Ι 0&)jcì ‹ž’6Pç`ï$¥üohqÚg³¦NÝ)°¹L€ ”Oà®éÓÒóô‹•R7âï¼@oFìÑYŽÔ¯Ê¿ªná~ªnÝ/.-`L Üjrì ¹žê˜ tñ¸”j54i÷d¦§, 70NŸ 0ðHLv Ö…x9'5qVZÊSáË­fRæ~ªf8s.L€ 0ºJ ÜcŸ%”`<ƒšzR ùž°µº4+mÒ†P¦Ïi1&PóV,þfÓ€s¯}UêyÇB>iÀs®XôÍòš/Ihrä~*49&À˜@}&î±/dpúœ«”ü ¾ŸéH¾f'0©áÀ˜@}!á[&¦¤¿ûJ<ÞÕEsî§êKkäz0&Àj†@¸Æ>-ŧ‰LHg™[ËQ,|‡‚*§Á"‹€ñ\ãùF©þÀßœâç>² YAi¸ŸªŸbL€ 0¿Â5ö…D'o'Æ„KØ|gÙóüÖ€2&Pç ÐóNc<=£:T#î§êÐÍâ¢2&À"ˆ@8ƾàäjo_ò„Ëj-\&&ôœÓón¸ SaI–û©°`åD™` @¨Ç¾j à´x…ÇÏ·|£!Ü®#`XÁ ®Eé¹7žÿ:„û©:p“¸ˆL€ 0'ʱ¯Ú8­pI‹ìŸïçÆÅcL Dèy§ç¾x…Û¥¾d¸Ÿ [N™ 0&ÐP„rì«¶èi…K^d§¡4?®'ÂxÞñÜÏÝÂýTݸO\J&À˜@ÄåØg­n-±´|;¤±­ºéðõL€ Ô-xöé¹§ç?â÷S‹¸€L€ 0:A Tc_µpøýn b‡ë5.$`!#€gŸž{zþ#>p?ñ·ˆ Ș¨B5ö…Â¥NãB2&À˜`L€ 0H Pm x$T"ÒÊ0ñ‰'bs÷å¶kÒ4fÇã“'çFZùªRžÑÉŽ›±ÐÒÄÌ´”þ˜l¬r::%m‰&åë86»*iÖ…kF§¦ß.•>«>žZÊËedL têc_::‘™ƪ4©²2©/Df ¹TLÀC€ð¶„ć#”®<¼7÷ JöàB7–¾þ“TвÙRŸ³?V[y»]iv»ÔCX¥’¤ t·Âr¬'•À>ÃôÃækïcáØ“š1^,– Ã¥Yi)g™/Þy¡Ó…òœš•žÒßûxµ÷•h4JÕ»ÚirL€ D4ÚîËkN¨ÇŽ„Ç|ŒÃ,í‚ÙÓ’çùÖçÞôôÖyyúÍ"/Ëœ–ò‰ïùjý–êD%´6ÕJƒ/f5@€MPB9!5ýßïC5ìÒ¤¸Ýb“§iR©¤œ,š¶ìQVå&“írloŸÑ¡Üuý„Scëz5¸üL€ D.HèËk’NbJÚÙ®ô¬pä©»Õìû{,>isšL ®` xî ´%—*]ÂöSí-É“½´Ð?"ù÷=Y¤„ §ò““:ý$·îª¿Â7U]йJÉéc“?Ÿž²¥||† 0&!Ùq+:°‹d#[‚8äz AüDWkB¥Îv¤~C™ŒII­+×4Ú/t:çÃìʼn4÷ÀãJ× ogM¹9Ûþ¬·¯BµŒnÕ´Õ³÷ÜShY(í@¥'œ»g S“ÖšWZ¤–2+-i±y}ee2ãùnmVk²Óéê‚jœ»Ä÷¼¿ßÁÔš¯;ì½Á®'ÌÜÿBù3Q"éÂþ}îC2xž„8»ÿ“vÖIvûmþâó1&À"Ÿ@Øûrkì8å*xF 5ýG.úŽo¥µå=BŠS΂ÇÐç AŸßD*¹Ìj³LxÞ>uƒI ýúr‹´LÖu½âÜ eDô;°,íË0Í{ÆŒ7Ö>³‘Ó¹)>og`éì·Íã´“š>Ê­«‰sÒSú%¤§·yjŽRâ"ä{é{æ¹(™†±ãŠHNñüØaoÔ•LGYNHÍø_VZÒþâùšq¶ÔõT°é‹±Ë‰²ýlQSžw<¸Æ;nqùŸÆ±AøCVj©Í}¯ËUX¦Ï´`0\íJŒ‘ÔP±ÈûGŒË2Éù3}ô.ï3` ° J°Ä|âÛís£ð’-ô»}d™ß'º øÊ¹{™ÐÅÍÔqjš¼EjâSØË=œíL·T|)[ íKÄaçwˆëDÜ©è ¦£Ãláâ³»SÒºR|·´¬Ãñ¹ÆµR=Ó‡’ÚSžß²ΙíÊx ÿˆŸÃñHøª,FbÿKÈÌ´¡nKëF:ap¸˕ǹt}aÂCéCK®F½Ê+SI?;šÐò¤&' c¼˜>û‰RêP0õÃKÁøŠ1ƒâošEÜŒcŒéQ~u{©Dñ#1Ùq-Êÿ%:i îÉÄý ‡Gg»²y5X_Xü› Ô5Ò—»ò ßØ,…e<úñùx¿A¹v½ áûkx…Rj“±ý/„É3\.×»d›í…ï8·rÿŸ.U2޽$,â6ê¯`úøtBªƒ´ÌF°7®Q}uM47™[]‰–tŽ~Û¤,Bÿõ*Æ2üÆ úƒÐû ¸?§È~‚.dœìÔr:é5B×ÿK|ýD+uhL²ãF©Üß þûQ‰{ñ—‚:6s‰¢ŸS3Œ¹UtÁû“ÍT¾þ3ƳÓ1&Ø…ÔîFÜí.gÑ2¼˜ JGB õ@¿>W}¡„ çIH3yïÇØÛ……ï#߈Aç©ûã_eÙïßcž÷ÝZ¿„Gmª;§âúWç8Rî4ÓA'ÿN®3ƒå‘0þ™g¢ wå<¨ŸÎq¤^^|æeØQþŒ—¨}ïß‘+y 0H&P}9¾”Î…ö9£˜Ã»èËâЗÜáñyôñãÌãéB¥mw=v Ž­÷âvl¼µQç§ì{ý« ‰cì==Û>y—WÜ wg&%íE„¹ÐúNSBþ3;-Ù£Ä)¾*˜þÜ_Fsb³wÙwº]ji¶sÝÄ›æ/.#[ñœ…¡,o£.Q°Øí _ÛæZ²šÿ§ÐÇžNÂ0úe|5­¥Œê™ö€ÉçÃÑ©ŽCIh/É&Ðz@èÇWM±¯½5å6¯/Ñ/–$Ä;L ÄðÜs¨·ìE×ã{{`騫ñ†þµ—ðm\6Û‘±üoß×ù¦£¬–W½Í~8yòÛ£ãÍÜûx…ûJ¦x ßž¸Á—¥¢<<æ-ò7/áÛˆŽAÚbÑŸ„×R×û-S©¥~Hm´WiµÝ…>8V¸òŸ,¡ÌÀê'ë16ÃçÝ,ï$ŒANŠO½í94ƒ@GhÉ_ò>®b5ª#ãH‹`u@ ôå ÓLM 3Be‘¥¾~âë›q\ŠÂŽfÜâíç^·qÈf•ÏC¡«; Ïö‰[­ŸA÷çes3úëYö”åðJ2šõäD{FŸ²Ñî°Ò´1nÏØîrŒƒñyQyñe´«l_nñôã°÷6ÇÇôùeúë'&N, ¼Ò ÁÔ&Ž)"Nv…9Êëà9¡0Ͻ‘ìÒ¤Æ{L tX^M–çô9fË‚Uë %Uwb²ÇãdóVa’R­Àç4ÿš ©:A_Wáõ¡<â² ÓÚ€Î.¶#ùåPÓ_Zd£7.õÑ;‹”ó§ë@b¨€Ðez‡ë­ý&h„,ÐÛcs°TR¶ÁëUÉ!Mi ‡ª-›mO6&+•œä&Àê,ºÐ—ã«_[_Àùyj0ƒd¾‰¶nèÓi‹¾ØVé§'HÙÎ÷K¡yÊwêþ|δä/ a~½i†»PÍ÷ÍO–XFCèy²Îí/uÞeC%µ¿‹S]©¿.îzäñ–è¯-ÆëHñ™`ë‘•œ¼—No&£Ð™ó&`> ¤ï”5á,•5ÿ`A(ó6t ü‚‘#Gº1cú!úÌ%¶îžs )Ð’? ÂpL „;Á#!!õÑ è(ß9Ìž¤NC¹Ý¾*ÄeA½höúyã’þ_0.X`g¦Mùì…åÇ8`ï[æªëgU::~hMœ:ÜO ú9GŽ`Ïjûqs•SaÂ)&Àê ÈéËË'ЉçùzA¿;„Ìß4›ÅpóGÚf( HЇx§TÜŸ ÷>Fû˜ô¸i”7ÂÑŸGÇY&@QR(\:æïÀlÐ+Ä´Œ_5>4Ø®›¼{v•~Ì ·´Ý=æ)èÛqb¸ï"?º»h˜ïµU­Ç³ö{rPÄg0¶7ßáþû8ßtù7¨.Ö€W— ®ocIz5[O¿ž9¦$¦¦€¯Õ7ÜBƒp¨7ƒ÷LÒÔâ0ü€ˆ¦Í¢ÿ3½/U®ü/áún‚ÕµA×ýt·ëytšµµ üoUŠtÞ Ý—Ì_µv\éÝø>ÍfsûzCñM7äeidK—‡7Ãqë×ð2ÒêJ% »¢þ§a0°àóžÝ· ÕýÝÖÒ3=Ûµö #ç£C/¥‘´~3ÓS6ÁÞï5¼”¯”+/ò2˜’žcïé¸EšM¼|ó”K?NÒ23+mÊïÕ­_Ϙ@͈„¾¼’ZÇÀ›È\,¼öˆÕ¢í„g¨Ká-åN™ozû G è^÷„4‹åCå–M±Ú僈w”oúÞ?‚•Gá‰ë½‘¶8¾ Êò¤}Ò>†þœì©Ñ×N„þUO9P¢â@®qÑÿÞ†qôUô­»£¤zÇ-¢¢té¼S×ÕU0ñ»ÙÔBÃGú —Ó}kÎÁÂ÷ÇÚÓÔ„u—Óå>ë8LÃ8ZÚÌ'Àz Ï[ÉÄbÅ‹˜]î‚ã\JŸŠ¯«{bŠþË,'o™@¨°<$É®¥.ÇD‘Ñx[n‹Î"Kè®!  åûÐA“K(«Ç'OΓ—`÷_|9…Ûݺþ!Þü²ÅkƒhÆwUŠDÚØOFš}Š„Úˆy*5e uY²¦L9¨ÅȆÿ@Gøº® ×A«?ž]&U¥N\ctÈJÞé«M¡kƒ©_{[û»ð¢ðŒ¾g:‹²•îZ†4ã“ç(ßrÐË™¸¬¯ÄD©ÜNýw|~ƒJ©4hp80&P DB_^7)´ ZàÆ­»ý”O¡Ÿz‹€•ú"§ÅŠ{¡^^aöI·Óõ¯®»Cß¼[–Û|Ó·Z¢Þ¢±HWú\uÈy ×™‡þTˆpõç+_C™çù–ƒ~ÏIK~ ï]Ð<_W¨äß.UôúÖk„E^™–üºy ½l`^ѹ_º9ú¯n¬a¹õ^iÆ£m õ€ðíÆ¸•®œî5‡¹{]º{¾8ÄkVyñ““&•ê½3à}&PEG^?«˜lº¾¥K¡ÝB[ž ,Û÷ul-Úl«heDú$¸Û™Þñì~=·tˆà±O&{èSd I†º,sçε,Xµ¶‹ˆÕò†õè±+”õ ´NÞñ­¹ŸÚ!–w {Ð@Ê<ÑþÄѹ®Ü£š5ŽÚõŸ<äg}߯KÏ~]*k}o7u©~ôÜ!@IDAT´ˆKmöåެІ©ù?ú’HýÙñh§6âô­)mh‚ºæÚײ…5e“—okïdKö˜1£qnŽóhú"Xr°x§6úóqÍ1é]Ÿ5uji{pŸÂ‘9g¼ÐtCkïsÎ÷geõ ®ûm-]NÙTvf(¾‰ðïO bÆ*ˆY˜Wh@êÒ³_—ÊÚ€šW5hÇSq GeL B5ž° Jn'ɘ`L€ 0&ÀÊ#Àxydø8`L€ 0&À˜ö‚¨œ$`L€ 0PPÂr£UYÖ‡2MN‹ 0Ú#Àxí±çœ™`L€ D`Ž#éã€"r$&Àê6A©·‰ ɘ`L€ 0&P_°^_î$׃ 0&À˜`L N`¼NÜ&.$`L€ 0&À˜@}!Àx}¹“\&À˜`L€ 0:A€ð:q›¸L€ 0&À˜`õ… àõåNr=˜`L€ 0&ÀêÀëÄmâB2&À˜`L€ Ô,€×—;Éõ`L€ 0&À˜¨êìB9©ÚvÝ|oCŸêSû=°¥('N|"&'æðx«Õ2Áè§4î§¼ú©tê§t]=ÑÞÖþ9»ý6ê§80º@€Ÿí0Ü%ŸqªAõuFOHI¾Ã½íEÈ”íûtk¯N9®›8¡{{m C›¨+I/y…â÷ Ùâ§?þi³zÃÖùòï59eôË9æ¡":þ"Zç{¶æV/ÚGØè„6a|„ž»óócòçhºl×§[;î§<ŒýµÃÇПOÀ³{–#õ«ÐÞ N „”?Û!ÅY&±Û?T[xí?øœQ„sÅ¢…/Ó6ôD¥ä+í[5k4úÊ!Ú…ûÊ­Ž6ky–’4‰ñ8õønòØ.mÄÆí{âr œ7œ8ð¬Ü_—|÷cqĈÂùÞ–ÜÆ°í„«}Ôij*(a.+ ÖÛ§<4Ñj‹zýT<÷SevŸ“[x〳‡Âø±¼ì|„ Ô:~¶kèÔ¥þ!Tã‰ñæQC|«’ôh≽»hÜz‰¥G§6UI§Á\C|u‰e@ï®Òb±ýgÔ)“Pyþèe‹Þä#%ð½­…;Q‡ÚG-Ð :Kzžè¹²ÑsFÏ=wÜOUÎñH;ì¢)%ž ~WERÿTy%8F}&ÀÏv-Þ݆Ò?D².ï˜b?šïÇ0¨©;¯<[FÙª­°¯Å&UsY'âEÜl¶èŒëï¾ïBäNB8ÝïHäøÞÖ\s(“SheÊè9¢çÉFÏ=gÜOw—¼Û!õóÔß#…H蟂«Ç®oøÙŽ€;Úú‡ˆÀì±6›í…­›‰[.ÄrâÖ®e3ѤY³YÇZS$a áUH-t—ð½ Ëꤩí£:uªÁk ᛞ+z¾:´>Jq?U5úÄ f;‚ú{ꪖ _ÅBF€Ÿí¡¬~Bõ¹ˆT\s-Ç»u½ÝµÃO³Ð›‡à ·ë/8]Ó•hÓ÷œ³Ç!…hü‘á|mÞw¾·Áßʰ\¡í#,u q¢ôüÐsMÏ=_ÜOU0µÃëÎ?ÍBý=õûH©6û§ªW„¯¬øÙް»XŸû‡HìèHÛm±Ú,ŽïÞA‘-‡ª ~Ä1¾qã1H…´KQø«-{p¾·U¿•a¹2ÂÚGXêâD6Œ4é9Š¥çŠû©ê6Û!õûH­¶ú§êW„S¨ËøÙŽÐ»W_û‡HÀµ›îyðø‰mWƒô@Ôjpº\Xç¦|"äe­Õ2V–y1ǖî¸ö4Ä­M-xDÝÛʸ5”óÔ>êò YñóÔ2ú©º®²2Gê÷©ÿGÜH›*«Ÿ¯ÛøÙŽàûWû‡Hóãg¼Ú¢£Î§EvÈÏwmµ‡m»÷‹wü,ÖmÚ!,):·i!î¸|°hÚ8θ¿ø~•XøÓq8¯ç5ѽckqã…gˆVG71ŠüÁ7+ÄŠ5…cÜUµU…’|Á ñIut«Öçâ ­–éÄŸ5é#[¼R9]bêí—ˆÜüBò­mßÔF?]ü›˜·ìwqõ¹DßžÄÁÃùbîW?ŠÇ_ûB8Æ^%¢l‘…EãØh•cµvBñÉ …VŸ+Ÿ 5¥¾7;¸Z¿·¨3/Ò>¼J±»f¦É̱ž'z®ÀŽs¨&£¢ßßWXØI‘JMöOÕ,=_^Ç ð³á7°>ö‘ö™Êcšl‹ååkµlÿnÛ#zui+Úc›žÛˆá§o4O2IYðÃbðɽÄÐSúˆæM‰ní[Š;¯,åæ‹%¿­Èfܬi¼´Ø¬­P¸ü™và5É8bîmmÜ í»ˆÏ–¬„9SÙÜwîÍ;ö,{¢D@û¨ÁÚV9+O.Ö€ÓóDܪœ_X†€Ñï£ÿÇ Àk²*S>РÔ‰g›ÌaI©N 4\.ú^÷B}ë"KUëép­šcl¶ZØNìÙZîÕ"*Ê*†èU²ê枇 Íxï®4F -š5-j,¶íÚä`íO‹Ô¡HdN8Ýûšà(¯Þ[2ó¡/WâK„ù‚„<ÄìwŠÆqÑâÆ‹Î¤Ÿ†¿6n]Ú¶1Ñ¤Ô _غkŸøä»_Å…gö%s R}²èWáÖÝ"ñªsJ¯ÉÐ>j²ºUÍËhø˜žŸhzžj»ŸªjE"õ:âIý?ÊWÓýS¤"árÕ ?ÛæøDÅŽ­›7g÷?VœÑ·{Iü™ªnݹ_8þûÔ»Xt…rÂÖûÄûïþÞºËÀ;´:Z\>ôdѧ[{‘<ó=±÷À!Œ+B4m/úöè à•É0-ÉÈkçÅ‹ŸþøGLºéC¹èu*,»¡cë[ÿI8I%ôgQB•–PÂÒ,*NôšóNq±ÑâÃ…+ [ïëÎ?]ôƒPžs âH‘\:ÄÇÆˆCyžó¥ÏDÆ/¼øWSø6= Ð1?zÙ–™ò ¿ß[²¿'s¡Ó?F4iœ a—Û aý1eÔ%¢M4¹I¯pÛˆ³Â¨Z-¶JWëQJÚ0JBýfT1¯Z/X}+@qÿ_“ýS}CÈõ Ž@ØžmhlÅøkϹpÖ°æŸmâõϾ;÷„ðÜ?à’pýÌ[ó Ý“n:Â|4æ¦m´Æ‡öë!Î:ùX±~ËNñþ×?‰cñõ¾ï.æé’ma‘K¬\·Ith}´øqõ?aÀÃ1ÆÖ§þ!’pj$ôj8HD­ÒR^rÖ‰¢¯.â­yËEÖû Åà —‹fãrÊ%3êÒáp^¡è†¹Áx±¡{Nƒ[Mâ ˽=€l˜w|€¥[/äý²UÄçKVfûtkgL–µZ-âÙ·ˆ‚B§xòÍyPÍkââ³ú‰ù0/zhôå†6áÝ?‰_ÿÚ$u4 qbáÏkŒN‹4;Љþï‹e˜°W…‰¹— >Q èÓÕÈÿM§n/¾–,þuwí¹eÊõÖ¼Œ¯%t6&öÞtñ™è·ˆïVüeh1¾Â˜hN†à Ì`t®¶ìØ'¾Äà?Ñ™Ó\+^@:¶i.Æ\=´LÁ¨Õö|qkþ Oö<;xŽj_QPój GOÿ_ÓýS TŒ³ˆ`ay¶­šÅ0c¥z÷„K`R}±hHÿÞ‚„ó@‚׿­w ))´:š> ãcD—v-Œ¿UC–cÌó'€ÿ¶n3Æ ›¸hP?ãeë„”hÊ+[ÈDòõÏ¿[vî5M\Á†é-™³¤@õ°SÅ߯ûsr ™i´óŒ©¾clúÝW)tU÷êQÿ@.R‚ù*qSk]7¡´Å[æí# ·[7&b6oo˜+üñ÷V3Š±Ýµ/GìÞŸ#:·ó|2*u2B~kìLỄw ¯$¯Pß[ê®6@ýoöî2U9x(O¼òÉ1è¤bÌ5çˆl˜-‚PLÃ)Çu5â_mĨˉNðtC¶ÚÙ0¡ð ¼Ø¸pß×ÂL…¶Ą̂ ÌEtñ„÷|ï7ChîÖ¡•øï߉ÍÆ)äàåŒÌJþøg«!Ô·Á§Gïð9lÁXý·¸þÂÓEt”͈o~9)r:!\g‹å¿ÿctjÛ6oõƒñ¢@i|ôí Cðž>þ|~lg$;úʳ½“¯ò~-¶*—¹/,iÃÈÓx†ŠyÕ`FVÅ}„7ï†Qq®emðnka}¶I(¦ñcKñC.DŸÿ#LBÌ¿ß}d‹ÍpÑf’¦ð]$Ò8“7WüÒz÷ëÑQLŒm.ñ;Æ43T4¶¼öÙRc÷Þp¾hßò(ñ¿/—™—‰½‹7¾X*NÃWègŸl(H‘äoŒ-¹¨;õ©ˆ$œn‰ù TãöTÿRÒŠÒ„¹Í;öŠ=øüCZL $HY-q&d.ùmøjùj±kß!±nóCCÞ šÈc1aÓ $¼ý›½§äïÀ¡\óTmnéž›œi[SÁÌ3¤ùãílL˜%AÕ7Pst“xqr¯ÎØÆÑ¢D†V›4¡¥¦@6àäèˉ¦Eë6ïÄ}͇áýæôŽmÜaø‚ßû;²ýß°e—Ñ.®vŠ¡õ¾å’‚fh—3ºÄä[.çžÚGÄk-ÈFï—¿6Úø1WŸ#Ú¶8ò ѼÎÜŽr’8¹wg£C£—¿µp‡Iaí¦ÆgC²YïÑ©µÈ+(2::óºlk«}„ èaOÂlô´¾3앯á LÖ5œ-g×@ ˜í-¬Ïvã8™äÁÃy%˜É•ñ[_þPò7_8½ÃÎ}Å1*Vì­Z¿UÌzçkñÀÓs ñóN?Î; cŸ¾Ú¯ù7[œˆ±V—ìÓ­„þ¿Kâ•7¶¸ Ütâ±ÆÜªÓaÃNÚr21Á4Ö :©§è¥Ñ(‘ü±fülÍû‚¤j/ 2Gˆ´@`k5 DÚÎyßÿŽÆì]ñi‡&ô‘€Má¢}… êüåÀÞŠÜÕ C€»æÞož$pÏxùSã<ý»ôì“ÄÅøôSËÁl¸µÁ9äy’œM:IÃÄ•¥+×C =’Íš·n"§¿øÙìGN9V¼Gžoþ†%AÞmzà…êMãÓÛ>C Ý»K;±/\:ÂCŽ:BxßM€ºwlUª-Ðq2kz 6€±1Q°ç;r­y÷–4ßÅ{>9’–„BŸnm SJkÑ/ku†!DÇü qÒõ"9“ m9„ó [NÙ?°?ÛðE–yO3Có¦K­bNÂ4Ï7Ž…+dò\~€]4L!ãÅ­— 2Æ›µ¬h÷óš SÇOý&¾\ºÊ0ÉðO GRè”7¶¬-V@Ñ—d3Âië®âèâz†Þ p! ­~õ<µ˜iU°­ýCÙ»TAÊ)¢€/Évä <žP¼ ?žy‚ñ—‡·Ã?^„7Âý˜€YP"t]qNA‘"Ç´'Ô\ÈTèlØÕѤÙníÉÛ¢' õQЀº R/h¸ç}¿Ú¸¸W×v¢X"?ï¿­ÝŒYìM »=òvCa´ä$¤S؉/!ÇÓÞØ§¤að ´b*u~û3ñ¥°Û>Ç7JÉïò|É_/*³0q”ˆ¢/1'tïXrMõwêoû¨>)„Ž™/e¼ø‰HºãÒ’}9Vv,”*Ë‹Ï3úK t϶7£e«ÖcCÇÖ‹÷¹òö;ÀäcÍ¿Ûa2â6¾Àû‹Gf%•Mì$ó²ïq)¯È‹}•=“8Ë¢=òÏ„†ÚmïüMXyã•w\Þ/K ’>£FÜ iÂ}…o_„d—5úŠ!†™ÁSo|Uâ%Å7^ÿ® î5‘‡¸tp?ã Õú-%¸És ™}ý㟂f€“› ÐpS UKéeŠLˆèEŠ:šDK¶ügï}ÐQÑý'WP4©¥OW€MÚnš”I“$É鬈J_;N€û§Ê‰Çv2æPMӦʮñ>Oyu‡ÍùY' –¦pYVÐ÷Ž‚ý¹w!(g¸“ˆx¦ÇSËn 5”~Äs¯!œMø„­¹à^–Ì5VoÈoàK*CùC#¸É 4œwÆñPå‰9ï+6ÁœL?~‡É Ío 4ИFs¤.€âpÄ“?Ø{vn —„ÿÉ”7¶B‹¼¾}¾t¥‘w‘Ó-VÿPÖþÆØ€. ^,šõž¡Ñ Ó¢~X5Ðuiú;ó0fÆr‡†"-?Mr¡É<Ô1’ªOˆBmµ?¬ÉÔjÃôy—ÖX {ÊcðH^z¨íPøèÛ_ ÍÍ5 ûБp›úüûz{õ!¤YŸ‹v¿~„[ã…óÌ~ÝáU§·‘Feylßs“­–cÐßk|2'¿Ãô<•çÁH4$ÿŒþßd’9&P ³½™ÛJ¢vú@NžHácAf$4‡Œ¾z’&˜@¦¯wÕá\ŒÓ_üÔðÐÕ¢Y1NšÂW áGÙ$§ÐäKï@ŽèÅ€üŠÆ–Ä«†æ“äsœ ÖǹÒ;)¿ûþÆX2Á¬^¨?ýC¤ àÕ»/µxµ£aMcœ5y ü™ø»>ÓeŸY– a³O4 …frÓDZ3\{þiŦB²ÄlÄ×\…\ ÒŸw 7ƒ©£GulôµÄ þ\žr\7x]éfF1\\’›K Þñ}ãQYg'2â­„ ©o~úSd@°"ᬰÈ)2ßûV,Åê«!À¼ø_í O:dã&5ÝO;ŸÀn“&†9Ìø‚C®(ÇaP¦ù$ ÓOƒéz|Í!¯>Í›zÜ¥6Áñ“zuס¯ÄW—wæÿ$N=¾›1i¸¢ÓtÉ‚$9 &À˜€_ä'˜Vg¥Ïº§Ï+ [m_¤1ÏÉÍ×’8Ãvô«å¿ûMÏßAºI8§EAvâ« ™;mÅDÎêxDð—cL€ 4TõVþÁ7+ —tcÉf©5f ŸÝÿXØRvè^“{9Àhfpea+–•ý^6è ÒŸqe×óy&À˜@ È­æÿàµçPn>V¦‹5úòQOÁ׫Ïð3N@?¸ÚX}õìþ= ›ñ@ò! ø¨K‹7¿ü^<<û}Ãî|ô•gó ÊóˆP“s!©ÇaL€ D2z+€tš€4žr ±4ê6ñ:V!ܹ÷`¥ëéÚÏÿ*ZÂÕN 8ŧ Ï~ž}þϘ”-ßìíùÀÛ3¥qú Ç´OnÌè<øNˆòõ8@ž·ÛMòwO5½CEyP¼“zu2þçŠhÃMÏ å{DðN÷™`L "õZ·Â'·)@÷„€¾X² Z¢Þ†pþ-\h}Çø´Òá±Xrœ\ÕѧÏaNòÓŸ¿™ËVn€ ¯ÓEo,þê'KÅ|ú%—ƒ´òÔ•ç 0–p5/ùm&<ýn,c~|OÓ I¡"_¼4Iê«å«±¬ë>Ñ®í¨ ´RUø}횥æ-`u‘€¯ðmÖLS¼ƒ)8{ f¿¼ECÊË?˜´9.`L ¡(­©çú÷îb¸áÚa—ÂÑX¢üŠ¡Ä}7]s7´Þ¿Çi± ú{ܺl„ò†i i©èóë¨K«GÑ®ÞaÅŸÅÈá§BÿŸ/3–Ž¥ó¦/ÞÉ·\(úAsE¾xó •§¥Éß„Ý.H÷Þë‡c‘‹Nðßëñ$@¾vÉgõ½7œ/Úc)ZòµÛ½ÄŒIYÜÿäÿ nÞ,>øægãÜkŸ.õ>ÜàöÉl* ·p`L€ 0&À"›@¤ à˜·/”[é¹N'í‡4½$Zõ‰ Úô™µi£X,ÉÚFü‰‰F4‘’Y![˜}n¥Õ) ;í8xh…•Bö>˱^6ä$qr¯ÎX¸âLÃ~ü,ýJ¡<_¼¤I§@ iDCk5ì´>†«°pøÚ-(r*—Ë•ì Æ^[*BôÝ—–µ›¶—*ïÏxñ‰Ã SCd6õëÚM C8ë_êÙ;¿üpôSá¬@¤§Mý‡QN“u¤™Ëǘ¨Òß*«”DÈ.*¸u§{׃¹ôÛk}Áêçs ˜PhŽU)Ð’Þ+Öl‚FU7ÌSH#MKÙlIhó‹åâï­; ¯û±Ä¬éªËH ÿÚµhfì’ÀMŸgÿ…N¦/ùâM¼j¨xã‹ïÅ´9ŠSútÃÂg„Å×îœ\UTXxÀ,«×¶„»×±ˆÜ¥—#2ýùeÍFÃL‡ ùoö‘!»á+…\nw¹æB´„ûw0=êÛ£ƒø “ÓbpŸ† èU²zfEæB[vì´៘O@Ÿù­0iêˆe‚É–¶<“!*s –ï½ò܆i-¢Ó/igà êÕÏ–ˆƒ‡ò wrtžX¡‰¿sáVŽi28þ‡Ã†—BEK‡û3›:uä2ež“¢‚Âáè§BVâ:˜õSn—Ë£¹ð”¿ ÷:X-.2`L  HÓ€tn;”_(ÉL#”aÙªõÆŠ‡[7‡Öy„©ß± Åé"K{…f„²²·qøëþ[vìޱW‹”;G`Yf°í]¾]ûsŒŸ4i‰&Hµoy´±bùâ%­úã¯w]]z™VZÉ.éöËÄí#KE“ åík÷‰û®æ_¿ž½³ xŸ8Ï‚¼\ï-àë#)â À]»¹ÄåãŠ5ÿˆ“a*dèËŠ J_/Ê3*r:!@g‹å¿ÿƒUþNÅB#ÍáGùcIyº¼¦$ÿÕ¶ÚùÖÍãëIkѦESñË_GÌJÊ[:¼<³)o&¼æ³£èy E?’RÕƒDÌ~ŠúT§ô]êÇU`L€ xˆ$*—1¸}÷ÙôèwÒÞŸþøçèÓŽïVŽNÚ»þ÷ÀL$ý…!àÄ ¨Ç\}Žá¾‹bŸÐ½½!L=’ù¡ˆ‹‰#ൄ>ñ›áìþ½0ñq¹˜ðøâ–Kг¡!ùã%âÞÇ^7–gvêqb×þCftÃv›Ìšõ!|“ o®2Wž/^7^>_¼R¼ôÑ"aƒ¹ ÞC°Â x¡ôµ ŽJw»sVÿ¼üoXÇ_‰QR:²CžlÈ eoÚ'á¹W—¶†9‰Y…@Ì…HóM¡Q¼Çv¼šq ™ õ'œ…?¯1\º-úe­8½Ø§|EËs]lîÔ_@(YI4Øí[eü¦ñ1XfÜé2&¯Û¼fN»Ùµ›B1CyK‡“}<‡°ð~ntzžNvÁA<_MªÓO…½Ôu$ê§öQÿ"›¬ëH鹘L€ 0àD’nv¸´uïÞ–ýöj)Ç®‡@B!ƒ WœÓ_Ð_y¡Ið~BÚmZEŽ-ïlò³Û¯'|íÂ|Üàçw•¡'Ù;œ‚ÕèèÂÈá§AÃYÚçnE¾xN¼Ü°ûÕ¤f”fº¡òµKüVoØ*7oX;i»½þ¼y›ÙÖ‰-i®ß€=> '÷êbl½ îm.D÷êñW¿ð>mìûºj£ƒ•-Ý}Á™}Ŭw¿1´ïçAs}BwI·É™¾xÆOßü,p‘éH3Næ3´˜Jym×|©ó½–‡€÷óRòÑs¥Y,WWµŸ {©ëHf?Eý>ŠL|½yבZp1™`ˆ4Ò΃Ûw¾÷¦Ãï…Z'SpSøö—>)MáÛ<ï+|›ÇÍm”ÍRJ6“ÀﻆyÎfµ–{Ž&sVÕß.q#~ºËùÿì]|Åõ~{§rê²lK¶å*÷Þ+dŠÍ:„@HhLIÈ?‰CH -RB1Ÿ.Ø÷‚‹,É–ÜdÉEî¶luteÿï›ÓœO²$«ÜV§y¿ßÞîÞîÎÌ~»óöÛ·oÞ+ذä›\Ÿ½r’x·8Ø3Q¼8­g_j¸ŸT—ú¸ U?ëu¹ aûÊ­»©GÁ¹tx_#¾Zàoº ç¿Í™Ù"Õ¸ƒ#¬ ôG]uIu·©ºöUÛ…€[?ñÑ¢/¡_¡ùZO5ªµ-ä ©§ ï¡÷¹Ù-Z?µØU3 fFÀH\Z< |íùùgŠw¥lyýØé|Žh±ÞeFlf°ZZõÀ ø¥¬[ýIi)g"ª¨œ@€³Äœ[Žà…ep¯Îì>b¡Þ].ü:w!øÕÃ]èë5iw¡úÊ•îB³^ûL ôÄ Y)HêìW>\B/¼ó=óÆg´?÷¤ÛeXÿš#ž<þ—8’ÉfaQ—ÇÖw~ËU£©GVycÎrzäÏÒ+-åÁ–®ñ +nS¹'Î ·)¸è(ñ*²¯ýÄ%‹¾„~…þ¥ôTã±–z úzŸKjÑú©ñH¨# Ö„€‘\P€;,LP¾x¸•o\¾ø‡è¸6ñò½4Ÿô{®›¨Á¬¤n`QÂCmÛîÚþŒôoÒ6®ÝÉG ¤Œœ€/pn1ðꩺ¼)™›^~qÇd÷J¯. Tw!—Ï൹ ¥ï;Ìð]ô'Ž˜¿n¸.ý{ÞjÚ–E¨¯v—!­Jù¨óõYw`æ–Ù?»Ñ½Œ˜ó?Ÿy¥ðmG$ˆ0 »Ø¸6_,uxMnSî‚Õ‚7¨¢Ÿ¸@џпڴ_Äë×+=U˜=õTnöÞ¡ï+1mqú©þg­öT(.ŒHÀaa‚¶òTöígÏŸ<ó8]ßv<ïœÎiáMñ çã[…À—ŸÃa‘ËÊH[´ú«yßó‰ ,y+8–/pn1œÛÚ`¹˜»PmÂ]¨&A¼xÄñ†_wîɳ%ç ]séÐ*»6Ö]¨J!¼w%™ªú¶ºÖkr›ªkµ­A ¿TÑO¼Ž>¶ê«/¿×IÇ«Ò5JO1 O=uhßîO—Ï3ŸiUúé"©Í …@€#`4ŽÏ¼xÀ!$”q OVÎ G\š|Ê9ᲿúÑ·1ƒzuÖyУ†H&ËÖÚñs Q0àÒa·o]½âË›7f06G|ÚžX¾ÀY¹ö0õ È=ÁÑHsÔš¼‚"NÆ!J"´ ’Vƒ@ú‰ÏJ(˜_vWå8~ztòU·°žŠVzªê}Q]O±ï|QêÆµïo_·z#ï©ôSU¸ÔZ€ ƒŒSò\S¬õ4<£saÙs½ÖƒZÑ£pi‡RÆÃ-„•ô–=)Ûö›2}²ÃaOf¢ÍS Õcc"4Kpp«‹Ãf­¨àøäezq™ZïœÝ»Öÿ°|É«µŽÃ˜€aQåËžpEÀúÊ”ñƒ“’V‹€$àè'FºÔ„—Þ¬ô´œq“§_Â/ÂYOEµz=e³éÈФEBO9ìE÷î^ýò%ËÙ‡þ,ã¦ôS«íRwâ Ø˜ñÒLNúv¶ „ò9“v»-º¸î…À;÷1#CÔ®˜ˆpBŽ“þ=:‰yÈ‘‚|*Š»n¼ ² ý,á_ ‹-ÜOà`beM+|±ˆ—¿ï=xXn½ûö‹ŒjÜjLá¡v[t˜ÝÖ–ƒG3(%Ú¹òŠÌÔœìÕû3Ósù‰–nî‚Ê Á¤'pmQþßÜ^% £ P«~ªl “_~«¿þråê¯iM¯C»tëÓ·OxdtlhxXTsè©rsD ^¨£ä€\ö×ÜnçÜ–ee…eÅEçeíݵ3 mPúÉ_@Õã@ºA²WnÝC«·ï¦‚¢21~'ŠÇñDEZ(”ó„´*á—ŒŽýp„±‘pñ†F‡õæ|'ý8é`°pµ”d¼UaSy²F¼#ðn(I$¢´€|Kë¶|øÙX‰ïå)‡· Ëϱ¯Ü[îißîÖ!Á#qÂQ¤õNÉ;û5/Ââ ÷X¹A¶aYϯ\ÆCOúg% …@è—~â"-<óKq6Ox¹ÙôS»kž|Yžâ™Å¯ÿK.ûi¼ ·Ýº›—•~òøªÿ €ñAüµK$ïÃ8¡¾Ý:Ð4þZÚ¯{B£Ãû§åþ©öüÆ“ð½ä¹°ó\†U”Çð_)óòÎ~ôX§ÂM¦Ž|Ò!··»ýÕ'_°: áÒº 8,ß âÊúÍ (Qx¥ŸL!P•nð‡@_C$ù–c{ »•~ШŸ–Ž¬Þ ß+¶dÒÂU©œq;š~võeÔ#±]K?5¯¶Á †÷í"¦GÏÐÂ5éôÞ×hÚ„4‰Ã熄„ˆ,Ñ­ÍnTދŠk.DZQ° ž*ÂÊÄsœ‡$áO¾ù\©„;ýš¢¢7÷B¦· ¢î³:u˜öÇÜ£ÿæÍxÀáA .}Áð®J ¦!Ðbô“9,ÚóLÏy®øxYêmo¸½É¯sJ?ùxU¼ïp“ïÍ™´`Õv¹)f^5’T¨äº±ÇËÉ#3“iîŠZ²!ƒCî:èŠÑ(TakxÛ?¼îRc«‘ ¸TÞPÚžË ’ • ßb€&Ïk"àLÄm*(:yUtô„…=læ³e—”›ŸIL\ô×£G7ñf<äð¢¼àv"ý¾Å±¼®D! h<ž:ÉsÙpúÉÑÆó,O{®øhYꉋ$àÐCÀzIé'¯Šõ°|ïÌÊ¥…«Sù¾sÚÿTµà%Eൔhùæ=Ô–iáHcp3ççh bdü¡À¡¼=¹´‚cpfO’|K_qï@&ß|zB€‰þòÉÓÿ˜›Ôch¤Y»Ä¬i¦ëâb~³ÞjMÞ”—ë7°Â •å›AP¢ð"-B?™£ª|?áÅó¯«(¡Ÿx‰‘' ‡NWú©.Õ6Ã"òmµÚèóå[„Û ,ßJŽp;u®ˆ­ßÉ ãH‹q…*D„¸£º€KüA¡È1‡%ÊÛ“xKò-¯Xk!à<êºÂ¹±¨ø‘[Ú¶YÇVð(3i½þÑ¥ã#Ãóòf3F’x;% …€o0´~2GÄzžužçŠ—¥Î‘Ø@É :\.Ëí>lŠ*Z!à=àzâp8iå¶]"1|¾•ÛIãðn3.Jÿš¿–Ö§fÑ•c©’|·†0…-…€ãêJKŠ$á°ž€pc’VoI¼åœ7¬ÈœþòÑ£…3Ûµùu˜Éô¦C3ÍÚ?jÈœ^ÛvdìÙ«S Ãê'Sh„'R”íqë'® ˘ »å$ÿã¿”(Z°~ÛívZ—¶OD;Q.›v퀢ÆlÏ:Jãuƒ1ƒ‚à ®¼+JK"àò*KÅ E‘äÛµÖ:ÜOä¹ !‹n¼åí»¿Yø#~;¿„ÿ!§éß¼<I@*wQ3…€BÀ÷N?™‚1\Æ-ð½ö—xꉋ¿êVõ(¼Ž€´~8zš ‹­4}B¯×Ñ D„”½‡NpíÇS\¡ Í? Sºl´äkÅ.­*˜ËO›­aî>ïÙ³g;ÍÁ¦ùüážÃ¢OÈ9ü^ײúU(š æ×O𥔓õ£[?1öÀA‰B E#à"àÚ}à˜H²ƒ8ß-UÊmð3†Gö<¡l&àeœ´ÇÆIx²´D x _&[÷¶ïÎ9ôe§N¿AA|CÿéÄäÉs;,_î¯Ø¿Mj¿:X! P(FEî' †yÅ„ —ˆoíyéÃetŽSÚCM²cûš:nulçßqôT>½ùùJzü¶+(±ýù1ýh™HíÄÁâX›ÝA_s î¬ÜSÂ=6:œ†÷éBSǤ¥3huÊ>±Ϋs|,]wÙJˆ«ÆTl÷öê‹d<‘9ÓZ^.2ŠZB"Æz Æ  ¸·ï…]ž…‚^bê}'ÁoKóNÿ²EŸj¼B@! P( €€“­[v&à%e"½¼?›4z@7zòŽ+é¦äá¤ó·¥¿}¶ŠrOž­w`MžóíÎHy‚‰õPúå½S¹¬aÔ£S[w ÷¨cFòPÊ/.¥ï6ïvoóõBt¤…J8cf9O63ðdQ<À®n§””R¼ð+yZ:é³²GŽì*×Õ\! P( †!‹ ðŠ ;…r¼jJd¸…CÆP¿èÇ×M qQ´˜ÙÔWÏ£]ŽÓÕãús øŽÔ†­ßüˆIŠ™]ÕPÇ0¶Š_2¤'íÊ9NV&Ãþài³9ÙM r•.(ÀÙ?jØ“<4u½h™®ßÆVð?÷LIÙi¬–ªÖ( …€BÀø€t»&n«¬²‘a¡TasT±P¯Þ¶·J+[“¥œæ„7ÑlE‡ßwmRRVNŸ-ÛJ¹§ÎÒ™üºjL°¶ý½ý?lÝNA¾b‰9b‚¢(ˆW•ϩ׶´ ûG[Èwñ ¾©™’Ûgóß7èéªÓR( …€O„ÐN°h &K’‚»ä¡›/»`¦ÜJeõp'1™Mtéð>e4µ‰¬7dð£~à†‰4U}¼t3‡ùsÜR&êGÝ;µ«w9þÞ±¹q÷åù‡w¾§‡¾ç²/±3BÙžß°ì¹.Ú×mkzföˆ¡_ðÊmâ‡þÏgŠåü#?SÉSäN*ÏQÎ=¤`Ùs]î£æ†A ùõSÕHf?"ã©jÔO~l‹ªJ!Ðdª?wš\`= øå=SêÜ3‘“æ¼üØMìóÌÝU‹ §û¯Ÿ ü«^Ð3‘д ƒSs |ãÏ)Ü™þÙýÀÓMÁ$§ÍEÀ‰n<4ztR·­[süÝÞ¦Ô';$h 8ÿaŽiŠtÀg K8)B)•s\Ö _?šR£A­|½Ä,„ßÄF†S\LÇuM¤nlÅP„¼Ù¯›$Üpï3ÝóįF‡†^-õ”‰´ú›¬¼t*ùÅëÝ%]þü «Ü+~ZÐ5*’zJwêËþû—ÙøçÖO~j†ªF! ¨DÏ Oò­€iZ×~úÓÙaŽ8í± `óLÂ:ðM¤G…[œVG³·:v+G­g?.½¨Ôj2™ÿÐo^Ú2;÷ØBæaÇPÝŽ9Å7Ô¿Tÿì ·I¾Ó÷¢å›÷Ðv¥¸sÚR׿~×8/à¶pu*íÌÊ$¼~G«½ê‰t§ ßè_ègJOÕ¹ÊÝ<õô<ô=o2ò3©a'¨öV(u `Tegš6í΋%ü?Úè÷\7QYE금µmnÚÇR|§Î¯wíÚ'nîéÓærÝñ¾ÛÙR×ªíØæøän'åìvRPXL‹Öïd·“(šÉ™¹”4à–MŸ/ßBV«M‘ð†CXÛЛøzŠ~…þ¥ôTmP]üè©Äø6:ô=ô>aÔçÒÅOFí¡ð@`éÆ zî­ù´f{–Ç¿D.þæ¯L­òŸZi}QÑl›ãöx ™ØçÛ¬,Ÿ»1ÆCž„qÓ§ƒl‡ýPRþ ¯;„+Š®_uxäÈž+Ý»GÁõéuáó]ÆQ=Ö§fQ!gþºá²aÊòÝH¨qýg\>”ΕÒÊm»¾ÀYI“ú‰Ká) ý ýK驯cŠûô¶)cÍÐ÷Ðû\>u*£Kã!UG³ÙDßoÙM­Ì@­RM1†ófP„u)Ôbyl`ÏD] ¸lÚm_Kކ¡g~?—ô΃YY§w ¼,TÓ¦óÀ<Í®9@ÌŸmZ-M?Úeý¶ ëw ÇõÞžuTD;Q.›†-ðëÛ­­KÛÇ2ð€LÌfåÊÕT…~âãCyŠˆiw?ú—ÒSM@”•z*3û(øk<áC¨*Q´hútM g E [kùš»ig‡×ÝOE%VJêÜžCÉö¥® q´‹SÆoÚ‘C}º%ÐÚ”}dæ—Õ©ãòø('ØGá–`º|DÖ§‹ÀÈÆ_¿Y»ƒ28jXëùK†öâí½Å¶=NÐêÔ}tüLáËø£úR/®ëå—Ñ5œÒ~Õ¶½”Çy5†ðx«ÉÃø9aÙ2‘º>+÷4?;ˆôèDÓ.D– ª°9hñú¡ì8GV#Êm¸vâ`ª« -úBú ñx˜I„uiæƒaK]‡TV/\ŽZ»K§^?š‹³®¨ø@†àœ6?Ög΄5¯ÙÄmýæpƒV©xàèi*±Vˆ¨ÍÖ¨ªR ‹­W|ePVðF_\¡Ÿøhô‹«?i픞j4žUŽÐûÐÿ¼AYÁ« £VZ,üÕñ:&¦)»Ñá“5Gþ Y(ºé2á†ùýæÝât+8Á¾Ã')3ûÝÀ¤û}¶|+mÝuHÝNÄöÍÚnh6gä´'èÖ«FÓÕcpƒ”WÀùúøA¿`Mu‰o#ÒÑèÑQ¤´Çç Kéëµé4npšÈ„}§»ßuð¸(3ˆI8²lÞ=},Ýzõ(Jß—KêXl[¶)“Òx}ÂОt Ÿ_ÊtöµµA¤~ª `Hu5guŽó]¥±j¥qGàÙ>±s2—ö“ÃG×°ÊáJ7”vÙÙû®i\ÉÞ9ÊEÀ<øÒ&ÜO²™€ãq¾•4à<‘9Ôáp(ÞxH%ÇàË0ô'¥§fõ#¥ž‚þçmŠ€WH­7;Üßñ,mP;ìlô˜ÔIX›¿æ±5I&ăzv¢¨ õd«ô¾ÜSUôôä±ýÅöqnÏK$[C¹lxo*f—ÍSg‹D±{ØbÞ¿Gjß&‚’ÛR|\ç9&¾|b‡#§ÎQHP]:¬µ‹Åp —`Àþ˜=˜´÷§ÎL¸³Ùâ-åR®£[Ç8Š‹§„vÑ´—_ ©ûÓàÞ…%øIÅÿµµAl¬Çà•87ëzo¨]Œæ‚‚3ß-ÝáÒIv²üOï}C‘á¡ô—_Üæ¾q¬ÜFË6eðgšÞt÷µ—êÂx³1ˆ¿ª„„âÆr²¸¸¤Lw|¬›Ÿ3ãûý.ÀáÍ:R–+áb~³œCp²HÎp©Rå6ÅÚ÷ŽÈ K8ð6á{¢’†"àÒO•ð`îOèWÜ¿öDnh­­d¡§Xï—[­Ýø”AÀ·{Ìx+A¦Að$ƒÁAf*gët}D~q¼žÇ3½ñÙ a½6›«ªŒoÙš¼ƒÝ.º“̬›a±Æ$.#ˆ0×ÇêNíbÄz¸Åµ·ëõœcgègGN粤œÎ/‹wMKóWm§×?ýކõîB7^1œÉ¸ËÄZJ‡¸Ê=yN¬²ßú,zèDEG„‰gs8?Oìv'•”UPçÊvÉc/Ö¹_Móò ;Ÿ{U\°Ÿ'î5×’ÿ3"ÒLZGN/ï5†€ èà›yï!¼vr_¯m»Rx\9_bc"´ã'ƒâùLqÂ!)Ee_^ùºwók$‹í±:-¿9€¢±31/gß¶ž¢9½¼ï!Åx”” œ=»÷jh%A'Agâ©jŠG¿jgî§“„ÞÏË/€) 8{íà§æ«jZ L|Ÿ*hÐÙ&pD¯qƒ{Ò·lôƒ·Ð"\¬Òð¿~`ÆDêÝ%ž6¦ï§¯ØÛsrP%Q–b,Ouqâiü$áÎR}{—„6ôø®drž+"°Äl £©ãŠÝNWZбr† 5 œô‚`ðöž©ÂïûŸóÖˆÿƒ‚L"8™Jr/þ䟋µAîWÓn’Q!­«Ë‰€ãŽÂÄ/AZ¤%8øÂ;¬¦«Vÿð:”ßø¶ï>è&àÈXÆ„/©ZVÅM;öÓXPÌo~ø³ÑÓÆ ’ž_TBs¿Û*H|tD8èß®Ñ H@IDAT½tÇX>BËÈàNt–ºp§švÉQ,ï_|·…ß$ÏŠPpØ·*yTÑÚéY|cïÇÀÒ‹„é‡Ð%Ãúð€‹šë¯ÇiÖ¹ ð4›Ìá¼ÈCÐÏrrì>d“ïQ t¨^H3ùÿÿÔYˆ6âÚ`‚eÖn·‹((6›“""ŒtkúàÄý\dh0ša ƒtAælYð¼nýÄe ŽþäM=åƒ6·¸"'ô?7\º ÷ó¦ÀwFªÁ†ôf4Q‡ëG‹ò¥vòØ~”¶÷0íf?í1º hò‹ÊÄ<†ý»Ï”ÐVöÒˆ»~ »±¤îÍ¥>Lä»3·9}®˜‚™,G…‡‰t w—ب½O:—ìb÷ÄAìÒÞë7ÜQ âkt¸…¸O _ôCÌkúww¹šôêOì›Þ·{.ßBgy'|ËkkCÛtëš8[­ÔžÛ |åTóÞó¯Ñ^7Ð3«[(]¯Ê`&à©|ãƒx@RvçЈ¾Ýª¨ö×ö¿oÖÓDöyúÙÌ+ˆÓ$ÓZ&ÊåüÖz:¿ˆ»íjš:a%¶Ÿˆæ|»‰ºwlK¿¸}2 éÝ•ÚÆD‰ýñ¹fx¿îôÌ=Óhhß®‚¼—òÃÒ²rúdé&š<~ýáá›Å“eoù®«~Qh“tà ò€‡›ÙêÔ?•Eêš~·\ö÷\º ¸H8>¨¼FþnG ×'_t€·’F!àÒO®þÃýHô§F¤ª—þ‡Ž2Úó©ŽF«M­I »uˆ©=]þÐõ=wõ)lu–<Çõã(Uˆ|òÚ'+èÍ/VÑXöón¬ I"»…üç« ôüÛ k|¸¶¬Üº‡þøþúã—°;‰…Ær‘lÔÕµC[zï«õ">9Î $2ž-ö%ÌY~ûï¯Ùи›}Ç{‹ÿñsýeCE9ï.\O¯ÏYÁ¾æ.·—ÚÚà>°†àˆÇ~ÛÈPá ÇX‰u »Ì_F23‚ЉoN̽*?©}‡NðÚÆÖð{¯(ˆµ¬h' ý˜˜³px1JåÁS' ¾ãgùÓL^~ ÔS"æx+œÀ~äWñ¨c)ü)•®ÓŸÃêÙ8#aúfM*! >Ý Å€¤Dþ„$:‹•-“ºê—å6e£'/È7ϵöy“ÃC_æÐAü&01gÂðnIS5¥Ž† Eäš8æãâÄ Á†¢ö¯k¼ß¸0‡‚SRo–œDªìOõ.@íX?*õ¿Äs¥êÚˇHBµÙ‘ÝIÖfD™ªM¦±±®ºŒcâ‹I 8ÁC7^*|Êñ¥"·cp£ 1ˆÿá¢òòc7aQHvñ\`—Ú{¯/ž¥VæW!ÕüSw^%8¬Ù1Á³Ò·k¹üù½Åò/wk¦_2”?7Ùéù«ˆ³ÞÑ]Ó/¡îvç¡›'±E{#½ðŸ…4z@Ý9}¼ø´×”¥Òé$ÇÿÄHdˆÝaçx·‰â“Õ—+¶r9q_ó¤°ªc{]õc»—=OàüèîÝg²F ûŽ×§q7Ô´rº…—_õR=õ.FB¦…b€ T õ.@íX/ j%Öõ:@íT©Ÿ0WÖÙêèxw]bíÝRUi & €ÁëÈ£€©ç¶”²ÿ‡w=CÞÈW!Éwšç>D¸º`ðhMÛou’-÷­íl¯­ÝµµA–)çÀoï¡“Ô%:ˆ ”˜\øã@`4ŽkÅëßö'KoU#Ø=¤ºП‡Ú°üÅGn¾ ~ Ö¼ûš þ§?}´x½»`Ø$üùû¯§m»rèã%›¨Íúpš1i”ðÿÆgœgî.‚ë?û·ÏE™34‘?ã ¶'nÜÇÙu¥'ûRAêª_ìàù`8³½y.ßÓP4g¢Ã«µß 8ê–ÄP‘o á;Qø6Û*ý§É¥©jCÀgÏÚ*Tÿ+êB@ZcA A‡pؽݹgh!Ç×~d椛±¹MTx³µÉ|¬Nãíunc¡öK øg‰y]×¥%okUVά)\B0rdåÏ‹‡LNgØÏûû-»x?;'/)£ýl¡†À¢}üt¾È Õ·[G&Ë®LP[wàÿ ÷•P„èaKw!gsŒåô—ÿp>P>Ъm{x¤rOÅÚFŸ˜PWýžílüò…®=¥ºißåÂF#}ÜÑ£;4¾üÀ=a—y ÜëÛ¸3»°?5®œúu2¯NpD‚æ’ÅëÓŸÔ/‰ÛX÷)Q´&}&AÃy 樞ñü•»ˆær¶È–( ¸¿¼oªˆSÞퟻb›ðè¥Q»Ë„2ù‡¾À¹¦h/ÍÑN_Õi$ ¸Ï-ÁüÖ:˜ýº÷9žTòÌ!ì¢rCòúšýµçrÜœ“Ç ¢^]Ø?;—6gd‹ø—8çWŒâІZ².Þÿj-46sD•xJÑ——ƒxå`cU.ÙG¸«à"Âõ¥Úü|ùfQ>BÂuåõçqÖ^¿¯n€©©g³G]Ëî Wðdr:í3¸®ùª¾êå¶R›™sL N?8I¤è•çpôT>½ùùJ± ?>¼L%óµѯ«øï%Nñ;ŒÈPOò8#Ì[îFÀÉ£ >×O²®_¿=Çš¸kà?è“·ž»‹3Þ¥ óÐÍWÈ]ý6Çý‚q,íb£83^Úšy€_H4j@_·Áo¸ûúDTù†F@xèq ÅœheV› ËUî?é‚‚h å q¶¯„v ;äR¢™œj>„ÓÅ+©X¾A¾wðÀÍá:ÅG‡QG„³`b\oM.(ì×®3gB`sy½ê®Èà[DÀnvž¼gY¹ýׯïCÙB¼)¹rÉ5ûÅ“«¬#Œ ¦¢+¿‘¹Þ°ÃŽ“µÜÆ7Èùð=¿{hl°s˜ø†ÿ pÍÄ¡4…É;H¼çÿï}µŽ#¥D דPì‹ûïþµ€v1ÉÃËAmõWidÓV*1–X3àº6Ÿý¯+Ÿê Åo§‚‡ûù©i'ç‹£S÷äRÇv±BÉbäwõ·ò;¦ŒáP•!œ`á -äÏiÃT›_œ/Ú×2%Î 9FíëFà‚¾ãÞâåDEš4ªŸ(:òã.u=vÄZ ÿý/±äUçf @æªHEYyÁ¹‚Ò*ÿaÅõ<7Spp° Œ’xp÷xBwgö:u®f\>Ì+>áT Àçn'—`qPçØ0;F‘Œ%ð¾ÒÅó´ó Kt‡Ý~>Mg ׈F#àžX7ë2|µkOò-·ÃBU“Tž/÷)ç”ëùòÉgR÷£™=¥¶ú=÷ñÖrp¨¾°¢œÞb-¤ñ”|hâÄ6ÝÖ¯w¥ÂòV%-´Œ$ßÍ œîæ—¯O¾Ý,|÷ûqÜSOi×&RD³éØ6†Ò÷¡ ~«Éã ”(‹@ ë Òö”Ï—m¡Oïâ±(éûr9aÇ>ºtx?ñ5mi™yõ˜ó¤qÊèï6gÒoœÁº†ÀÝéÙû¦SLd8»Äí¦-9â+Üê”=´’]ð0½/‡GƒA Gb{Ïf¨e…@@#ÐæÒÛGä'ŒhWV¦!t0"‰H‘µ&ˆþ?2"B$Ø­=Y8ŠÇÞ“Åô¯ùk¹ï$pt”®è!¡AqÂe=6Gœo„ÜÎÑNöñ€Ë »Î‰áÊih|¤À0**’""#Ä2p¾0\JãðÀµ(*+×lååÇŸš™c œÏcƤ´‚÷¼Çn+ బκk*µoS•€û³é]6¥e7”-üQg,›¢ƒíÖ’©\¿;F¸?Ûb´º@¦ƒÍAÔ‡Ã4!^+ÂNU'à²ÍGxŒÄ3ÁÜ¦æ ¦"PÈ_åàö©à—xDMÊå´Óc8nð°¾ÝÜyn¼b$»¼µ§O9ßò êÙYŒ_9ÊÉÂ@ê‘Ld>x\„TÍäÏçRÿ`Ë<ˆ¼mL-X•B‹×¥Ñ£œû@‰B Ðè8ýÙÄà¤AvV”_uª<úè6Ûºz\n¸¡`&ü•a±µq_D‹œ_!–¿šçœ.¤ìæŠÈxádBÍIvðÅ»µ‰KWÌämÈH~ÂJÏqÈá P:b¢G. èè(Š g<+ð­qÆù ®v"÷ÐÆ‘KkùÒúîˆf¾fxÀÝ:yl3·âÂêùn^Äÿº¦ëðËQœAáÀ£Ýáv×’y+·‹µˆá.eùæ]dµÚD±ÄøØZ ºÜ_ÍC`éÆvñœ9]¶›€·h®8zƒêqíÛr¾^pŽïffz⡱ÃtÛœº«5C?Ûþ†H&RìÅVqO>yÜ@á.÷Qs…@SHˆ‹Q‘.VŽgbºòôãœË6º’Žõëщ£@%?ï4öÉLàñ'ÈÜ{èøú–-ï°¶÷ç}VmÝÅþå[İø‹µCmW´$’ß?`9q¦äœ¢ý fî´‹L5‡cgIÆwo]÷ÉéMá‘ Ý“žºôÓoˆúå¯3UnR B Ùt­ƒ|s(=v;±–YùåÖEÀíl¼™ž™„·ò-ï‰ ^æáV¼åä¦Îš00”^Ú¢Ñ9;I£ß¯·SžÃFŒ‘‡‹9¾ôñ5àKå<·fá¼¹ü'8¦ÿMApq‰Õ6{¶3{ä°ïY; &êÖaoÕ<• 8’)y†Œ £¯Ö¤S ÆW¢0È#°!mŸÈc0‘£¨”óbDe@ÕþlÿxñF²qŽ‚)"þ¨\ùÃŽý4´ëkÎÙʨHF†0ˆy›ؘª|„7ÒY«¶(†@ÿWw;~¦è¾­ûŠ#aº6iùkÑïN¼{ß"kAF?Ç—–—¥¬[ý‰iÒÕ?ûð›õú7]^¥Hr R)–¹,ƒ€‡‡… ò]΃+**øyê 'û‰Ã . ¸t[iXë[ÆÞ€"1BzyX¶á^|@»²»É=‚éé•6Ê)à—S½½¥‚Žiô¤âȾB€ý1_µ+eËëùùg‚?UT±‚»vl¿Š€·À‹æ«&³B@ZzAÀy?ð¿ùª.£—{ê\'ɧ«Æô¯ÒÔÁ<  <3uëжÊ6µ¢hNêÊ#Жãw#†·•ÉB³Bà–²ˆYÂ:Ü+Qø}ÿþß 9òCˆÈ‰0‡r*QÓÞÌ Í©({Á¡Ûgñù¸ƒu3Y\j.ÌÿyÖŸ¯Éãÿ Ö1¥m\»³MûøE¼~=Í'ýžë&jž–pL.O¸Y0ÑD=L¸žTðK°Ëúíp¸+¸$á\nÀ °¿½Ãœ`GŒo¼¨€ŒÇñúœ™AôÔŠ2ÚtFm¢{*èx±“^½*Œæ/Û oÛ}@ËÍÞûáÆå‹à͸.<„+ 8ƒ°â™¡ˆàßû£)cE"Š@<é  ïìvÜß,º~¹>sfˆ6w.nöV'ñm¢èåÇnºà¼#8Þ÷Ÿ¹ÑýMûÈ¿¼gŠ\Ts…@½øã#7׸ïÏn™äþƒ'åàK÷Ÿ¼PW«•{íeÓX¨žâˆL°œËH>—qb1)žù<Û"·«¹BÀ¨ô}mç¨ì ëÿ˜ÿp·QÓ yŒàS{Ÿü_þöÖPžð¼³òTV9…­úêËï™6æ{Íñ¼súmSÆ™jò wY{9S&H&“pA¼Ùâ)˜`ñ†ÒZ8Î/&°ˆƒ€‹‰×‘kÝDa¼þŸë£é÷«Kiî.ÿøáˆ¦}xJ˜\?¾o÷§ËçΙÏEášàÚà)!àå’¡½iÒètŠƒÆ#,××k¶ÓÏg^çÝ}Ë–ûG ãïÎz/ÖdMà]'«NJ!À4%€$ß :µV‚ÀÀ/2C쇜¿Õú/™|»­ÞÌ¿ Ñè'Oέ„ŽV u6ž@ôJxÂègòàÕ_Í[•wâøéÑÉWÝòêGßFóàg_€5|5’qÂ¥5„/³°òº1»ÜN@º%ñ–s.;`x@„¼Òlä Lù?öÁ…ùåx3éeeôåANÉá aÚæÐ ço™³’7Ãõ×××× ×¬E‹rA¹ÈåìsB1žÃÔ%ŠRþ»p-'¶°Ð-W¡ì¢°œã}áØ»]8ö.,SØdtá~òë‰^h'ßáð_e% …€B@! h) zug—ŠÃ Úãü®V3é+æÐxOï™5øßÕÎCpiáùƈ@ð$ÓÎÍ3²ÒÓrÆMž~‰Ãn›ÈQ9¢À1£,ŠÐ-ÁÁ.ÆÉ;{Ju²]}Ýsß@[–$\žWõuüoµÙôü‚Iv­oP§²½£,ÌÜMÎШ¸ÄŸþãÿŽ}ü̃%»×ãšxZÀ—Àzi@yâC‘y…”ÆÙ*‘6u3ÇM®LN”WP,bñʦ—Š0_xëóí&Õ¿;Ý|å(ÚŸ{Š[DÉÝš{^‰±PIàÌßá–3ñþ9©9…ø¯}Ý`ù&ìšûº¶Ö]¾Äºu£Ð賯³ï4ºTu`5„þ—XWÛ¦VG ßk;'Ûœô ?åÚɽY÷­ÒÌA÷ïy²ÿAù_µ9üCà[ ?X[9zµ0ÎV$§ÕZêXýõ—+c×ÇõHìrµ-±3•EFѱظ#z¨Ç*i v;ç¶,++,+.:w(kïî¬iâoxn@ìÄ;ä¢LZHD÷N÷¼ñ·sË߸é̪qmÂÿ0Éî&ƒÝQŠ·"n_o”h¸¿dWÎQ:À±  9°~|\g•sY»ëª_ÆÌ=t<&° ËUcÏ»Õuœ/·qJu}Òðù¦º¸qÇŽþ»JãXLü‚Á_…ô¹ãÇÇuÙ´élõƒ¼µBX]ø+•ó%ÞCÉBø³¨”šp—ÛÔ¼FªôìawØËŒ¢§jlq üzŠýdA~¤\€»Ü æ êÌæ‚s^ËøÓA¿åç— Îüˆa2aznϬo°Þ«ë~Â6¸6À Žc¡0åJ’sÛÔØ˜K†D„]å8wšœgOÓ± ÛöOOŸF|j% G˜[7¾¼l=õÕËßE·/ˆ2åi\G-(d@ÛiOωë}é”}ï<ýP×uäÍ-C䛡Zë´9Ná“„uÅèþôò/n¥7ž¾ƒcçv W?ZF6ŽéY“ ½³”‡nžDeqà…ÿ,¤÷Ø5›šSò Kt~Ïÿw¸Éf³N¼Ø1ÞÞna¢XX„/MJ¼…’ !²…ï!`³–Ÿ3ŠžòÞY5oIÐS»ýtó¶BÕÞ@”“9¯fÌåô”³%ùæó8¢™éò}Ozý"ä[ž²$‚Ò ^ÈðÜ„*ïš6m¦¶‰½7ÊlÖ"yP!?Õ÷}‘—÷>oƒ{&C5]‰Wã… 8ðäÆúØGOÏ«8¶û×ìŠ"ˆ•®™Fh}&|5ðíÌpÞ/ ÄHp (ˆâ1ø•ZËÝ䯿šc`Ò p±žãì=uŽ:´M©°'Öø¿mL„ø¿{§vôüý×Ó¶]9ôñ’MÔf}8ÝxŨfi>pže¥%g¸¸™åTc{ؽžkÇF]wbþu;zñOXdådá Ç ˨Œc¨†q8'%MC8sRˆèðxqÓJkõG˾££?MOµä«#õô?Ÿ‡xè¶äóQm÷cß̊Ω°~ŬdZWD„†Ý‘úxЄË;îC±~kB»„›bbU¡ëÁåü€,v:Ž|œwúOeN'¬E0fJk9/*©[|u€U˜K9¶øàë·þ¯çï×–˜#Úü 3ùËü[™sþÌ/ôisoÕp\‹£pñp;yäðúè¶m±sÿQ;(©Y†å0÷ÄY:“_LË6íá¹âb8|°˜™²û õíÖ‘¶î:(#‹ÌMéY‡©ÿ?¤wWŠ‹Þ!â‚6׉G nÈÍÚ›Æm€28WÎ/h¿ir¼M×½­“6ü‡$ÞLÁEÈ¢¶‘¡”“WF{ž¤á}»x¹¶ÖWp䯷<Îý’ÓúPðÊ{ö'úSbž?2‚žòÊÙ5s!ROAÿsS$ÖÍÜ*U½ÑôÆÎ„såÖoù–9SSÓ^½cÖ ggk<’©á‚{O’p±üTbbÔõm¢ß²ëzL9é.Óg??~ö‰VÈ=\U< ¸"âucL!gIÀáúƒ/’„c^žý»ËÞéóJ:Íë|”~õŽÃ;ÿÄKωõüc$Ž‹!¦Õ‹æïì5dXÞÖÌœ8&àÍz3oLÏ"Lˆ†2ˆ“°ÜrÕh «ü”?eÂúzu*=÷æ"ÛÜŽÍ q°Ú’uéôþWk ©¢“ã)Ù#ž®ØÉ?Œ£ÎY¸ wnÝ´Í«œÜxWoŠÙ©m’N6üÊ9šã›98Žóº¸É7_eëGƬ؈`öWÖ8åûaEÀ½€xêÞ\þ’LÛF1g5VùµÁ E·¦"<û‹èCèOc®œRÀý+º¹õT \è)–³Ðÿ|>žxÂé©sðÃ^?[j+^Å·‹+c»+0~fÏÓƒ_ýt“*Äý‡~®¿Ü·oÈTKè‡Nrv³±%Ãf¦²mEE÷­((ØÍÛA¾¥¯8¸J³ò®¿¥ˆgÿ–œ´$–p9DzcßÓCÿÑ畉L¾Ÿçu”žíûêŽm{g ™‹õ–*F"àÀoâbœ9~ìÓ M{4ëð ªôÞ_`×–CÖ?z@Â䙸BnûÝC3„¯¸IC(¼7?—¤ÞŸµŒ[€“¼ák´tOI9Îiéóð+÷’ˆC ácRyò‰È b®LY®lY áfÚ{è$€=Ã)³Ûù¤ÞÖP(ðÛ{èìÕ¡2ñY|eh çîƒstë'.[ô%ô+~i¼µ9õ”ÎÓïEJ=½Ï•שŸüÞ8U¡!@Œï²ÃÅ $ùÆ`K¦¿÷sˆÁ½Õ`¼î9ì}§®u²‘;„}2OÙí?þÝÃë¸Oò-òŠ€×|ptª|Ù‘ý¾:Gûòß|úêÎáL¾§‰#uz¿ÿ_wíÞýÌ€ ±ÞäMc„¦dy!ìk¿šÿ)‡ÏûôÛœpé0²Ô–¸"Xd{j>ˆðsÚíù–|½œ1Ä[%&yƒKÌ/€—õÎ&ù'G¥ñ™Š´ÆºÈ·+=-RÕvnc¡P³F V§U õ(Û¤æG×áš4  ¢!IÝÙÇ$æ/Aíá€ì+PFè?¢/¡_¡µ=åq.†Z”z úz¿_à,17T{UcŒ€ý°ó=~N%‹Ö` ž®Ýµ×‹äåföWfÔ7›™Ýñl6=1)c÷<ÞTÊ[z"Ä@B55 ‰°¦ðÿ†а|ó:@܉ÂÌQwð'Üýøƒ‰x„Cs,ÀW×-ï·ùØaÍXámH<ÜΞ=U´7-å/ÇNçӇ߬w_„šSÿÖ„p~Û7¬þ°´´¨˜÷ÁI’ð-à(‹Iš›€WÄÄß>„m b×ïPtÂn>¡Ô9Jñ×ç®Øæ“z½Ð¹+R¿"Õ3žÂÃC¾ÀY†É ôó÷Áù¹õ—-úúú—ÒSG[ê)è{è}.é¢ú©ñµ©#þ¯fÜÃäûN÷¹pr½Ï þ½ì‘ßbú7KeÒ´?÷II‹×å ¸|žzú,ƒ@ªéâxb&qôì÷5r¾´'{ä™L3˜Ÿ€Ï€…÷*uÌ÷B‹üò`DË.„uÝÒ¯×=°ÿ½m»hïÎ_£ÝŽûÁœ€pËÞµcaêú5p‘J7>ðεpvœá˜.áÁ‘>³€£é‚n %K˜…ÂxŠ£œ•v"ýdéfe ¯¼›áú²t ãv„úwЦîã®À_€·’F!€þâÖO¼,úúú™ÒS ÃÔSOAÏCßs õÒO «IíHôyeo;Nv÷š<'~>ýs߬!îuùSæÙ#F\Ëìî/² fwó“®›ñr½r’ 'è5Õ‰æ5nþÿÙõÔÀL&à÷¹7èú5üBö”{½-íIŒ‹€Ÿðp+Y2çŸæfïû€núË|ㄯ ’Ú>/½ÿxegîX¸rÁÜo#Oxcĸ^ðy‡ÿ«"Ý“’@ÚñfÊ=Cï}läÈvUvðâ \"@ ƒƒƒñŽˆ§Hž""¨sl(%X‚„¿=w•ð ÷bÕW|¾ßþbíÌ>B}"hp÷xÆ1\à |3ðVÒ(.ÐO\ŠèWègèoJOÕWO=ý=ÏGÖ[?Õ¯µW " QÅËüLj‹scUv 2ºíÓÞ<Ïý#FŒdò[Uáã:6‡„†ß¥Íž ¢¨Äì™5h¯{Y6ÅIúúÿmWo¹ÞRæF„)pÂÎ âáúíg}>zÒÕÇc&üüÕ¾Ô«³>z`’6˜ãr‡³«BkÄÏE/DÀ€K§ÝV´míªOÓ7­CØA`XT9Dz§…©Ö·NŽzR±İíL¿Çó1vI‡|–½-.Î\˜ ZøzFFDP9Ç®FŒu»ÝA‰s5¤ÐJ'ÏÒ¿æ¯å° ¥+õëž â„óÅ@œo„DÔ \ å8êƒ;ERO¼!ð®À‚oô\£~âÒ¬>håÂ¹ßæP‡½èàž]k7-[²ª¬¬D’nd—„Á!À¸ß‹¾Í3¼›X ®“ÓgœÛ"Ü"‚xÐ*ü¿a±µ1ùv8Äi©±Yl*µÒ©R'åäžD†ÜH&–Ñ‘a|œoeÑtŸý ½|aqó _' áA«£9ìeûhjÓ&Š¢£yŠŠxWà«ÜOš|9jÔO¸EQ2¿ônß—¶=kü”铸?^Æz*Z驪z !QïßûÝÆ¥‹¿)))Dr°Fé'à­¤u!à(sÞϺœÿÖî~zðwÞBà@ò°XG¡¾„Ëë Š×8û¥9tZÇu[šÈÇ[MRåÔ@ÊCšmÀ«™÷qØç-üø b4ñ³×2‡ü§ŽÃ µÉˆ¬ÅÓÊ„‡> rÍÊÚùÝ—Ÿb Åâ~ÃFöéڻðÈv!a–˜  àPf!†#á»=:Ìnk‡«îÐL…¡¡G°ì ±;ìååeeeÅÅgíÛ³gßÎÔƒ\.ÜK`åÙ ÇÃ-¿roñÒÿ8×)ì[÷¿]Š}رuîÜÄn+8Eù$È7H%¤1ˆã©a¦•:ØìT\î bŒ´•Ò¹³¥F¼ô®Æûð®Üx팷hÔ&,ˆÚDZ„ÛN“nïØ˜Â2üéCWeýöÊŨU?q邜ó˯­á‹y}yŸÁûwëÓ¯_Xdd\hXXL9ÈïŸì¬A‘IòÌ-öâ¹ì—9¿ÅÛí¶òŠ2kAYiñ™ÃY{3ö¤¥ì㺛¬ŸüÒ~U‰¡àÎwµl«¾wärSçúÈ‘ÁÙEŽùüÅw@eYl¨ º¡ç–-¸W•]³¦r|ð¿0xMtêÎ9+êç›ï ƒ£áň yZ™$©ö|ð•³Oçiï+,ä<YÇ‘Ǹ֚ñ7.((쑎 /ò[¼!; Šm,,ôF§®¤§+à%}çaáÑ–Ÿtq#‚ˆ7ÈúÍûsRœmN;Š#$†ŠþH¢ª‡á:=X¹á· ÷‰P YÆ)ÕËËË)œIx[Nx 9È­â7`¶ÎEK÷D2^f|çùËc¢-|çÙí–oo|MpY¿ÕàK/^Á‹ê'®+Œ§P~)ÞÅÓ^^n6ýãó—ç~jÁk^¬&Ë­a.;#°ò‰~ª¡NõW # ëcä)šÌA+årSçÙºý¿\Æ$”Ã]7™îë¹-e}SËUÇû‹ÞþOVíÔ½l+äD=Ÿ_aÅ`Ùg}_sÓk0*Ç™AiÚ 2—ë`„°ðâgá çK9r†!ßÜ:k·Ÿ+v8×t žŒõI1‘—0ߌe/ˆÄän%Òú  ÿ8LÀ Ûc½¤ÛæÍ9j£Å— ýÑ£;ôغէ£`A*A(! ›®õÊð„ìnbenencn·ÛÙRÎC0Zù–Mâ‹6ÜJ€—ŒãÀÁaÃÄòíÀQ‰Wú…Ê~ˆþe8ýd‹ö<ñsž+>^–¸øD?ù¸íªxƒ!0ä¯'"Ø 1Íâ‡|éî'÷F9ÜàKj÷îóeiOõÚ–úÙùuµddv<Ó¡¤ï+;žceó1ÚÉóÇû¾µç½õ;fäv£mF&àRyã¡æ¹Œ%È·°*ó¼&n"¾³´ìóaáaWóIhl>6:´¹°ä$·»±< ù€«àÿ$ Ç‹ Öñ¿ôû–Çò_u =bîäj&`OÝY1„g>%à’\‚4Še&áXF|ðð°0A¾ÅÍŠ 1@“ýI«ð@æáø‘qFáš÷à+8\‚xã¸î€|ã%^E@ö=Ãë'sDÏ÷‡?«Ô1#Ÿè'Ï“RË@¹%?F<Ñpªš×Ê& ?ßîcòýÜù‚´wznO{ãüºZj pöÓ9ý^Éx‚]fG±5Ž-uÏp»Ÿ4zÛLÀ8”·§"—V&X¾EôžÃú-}ÅÁ4 Å6VäçŸøiB»M&3ˆ¬ég Wn.Ìiê§``"'`äùF ݘc¶z[¾y_·°øN¾©ç¦æ ËÝ}´²(Ãå<Ê… ™°|WT : ¬ßa‡Ÿ:,à­Á lÄÄ·8,àÀVpÄøÆ‹ H72°ºBš”åÛG÷he±-B?™£Ä‰„O_ e%<—ºIbäýäQŸZ pôr Ï4!¬ë›Ì]²G¿Rwêïˆ;ÕUì·={ö~˜¶#x˜’–„?õ¾¯fþœŽ¯*ÛýÓáofý)õñÞþ084ª&ßÄ®¹a‚_™Þ P%ûGÿ ;­üÌ}¼F¿é¹-õ=÷ºZö>1x ?€bW”(Ça}’QONp?_™)){ø‰åÇoãfg¹þ¨Ÿ›ÐàêtMs[ÀÙõNYÀŒ :@! P(š‚€“4øó Ñtm‹\¾ØÁ'Í!4½Çê´|YŽš.iOöÀuÞ3dÎtV+aijU¼Ù®Šù®Zþg+øàƒ#‡Nj¶¦Ô«b}¿ÜÍéÔzËe5W( …€/èÿʮ˘By¸lÒœ=O c¨j«_hKuÇ×|\/ìÃóR2i×õø!í`mǨÿv=9?Pש÷5â*ÞLW¥zHB;i7SSêY­ÉMÀùR(¶z¨vS( …@ƒèóJæD'9³J¤‹g/îÌØÐ°Ÿ×Ub…;‹ò?bò=ûñ—f'ðÛ{nMÛZ×qj[" QŽû¬t½‡{Ù@ Š€7ãÅhQ! 5Ê’P±ŽS\‚¡æ …€B@!àUú¾–9•“¨,eB‰‚™D7™ƒ¯Ýüxïº*Ê5ì¯|Ì-rM3=‘´-•­áJZº'MðVw\ä„[RHBöSw[ÀIs*”‹\[µY! P(Ž@ŸW2ÖŽEžä›‚‚'íy²ÿÁºJË5ôIvø%÷a¿ï—’RRÝc­äÿjÞJ0›{œi;eÃ,¦%­´!üfÿ&+š)8ýÊ„¿í²iSƒ³|ù>SÈ~r–WVÓüpÆŒÇ×°3L¥x.ËÿuÎ÷ŒûÔ°ì¹îÞ ü‰.Èù‹RuÙŸí¨O]æúì¤öQø¡ÈÇ¿¶14Ïù7ÒʺY·Ò´ É{žè·OþWÓ<{Ĉkù¸¿Š!w¼· 麿¦”ôšv÷Ö-©ß{뜽YÎù¸ëÂy®7¹ÝécWYPD“ ôAŠ€ûÔ†‰„Ùß,Ìf6Ù“ù¤ IøNCÊðǾIÇMs0Ñ5s[;ëÓ¦…jK—JFîó&HÂít:ÉéÔéðñ<Ú}à-,¡‚’R*¯à(‰^í¾>?¥ÆUPIó0 ¦ØÈpŠ‹‰ I‰Ô­S;AÆ!o´ 8Jãç§x¹d›Äæ°;m¬†Ý›ò—è÷\\wM÷<ñ«‘Á¡¡WËþo"M¸Ë4¥‚Öx,'&,âïÇùù}HwêËþû—ÙÛ<Í›ôDÒ¨Ä^Y›ëo7×ÅÎJ"{ä°·ø>yûV†$4×RRlûG=ÄMLâ¶š;™ÄË"ÌÚí+ñév8œT^n£ÕÛ÷Ðú´,*(.c¬ˆ¢Â,i¡PÖÔ­BÜ …ƒR:râ,•YiѺtЉ £äýéŠÑý9WDÜëw„öÓŸÎsÄi›ŸàÁŒ±nqÆF‡k–à`CŽ©Yãá5Û§[ÇK½ŽŠ*P!ÐvZÛ:3mIš]7à ‰²M3¥®ž±uîìsüˆ5Èt¥ö»ˆŸœ ûéåpW!Áâwáá¦ë:®óêWd´ }Ûœœ<#¼Û¨AXÂ,¶”þ-c.Ym6g~a©^Tj5ñóþ…‡~óâ »Íñ†ù¬þÖ;ï̆7À×½>gâ0;â®á9sª!¿üµÖRŸËÕœû $¡ýEnA$+’°{JúªælQMu³³Ã~~“ñ&§]‡¸O ¸$Þ6›2sŽÒ¼ïS(Ÿ‰wßnhÚ„AÔ¯{……†ÔÔÔVõ_Yyí9x’R÷æÒWk¶ÓZ~I¹}ê8Ô«3™L†ä„-ñú˜îî·SL!ÁïêNg§I‰úèI4¸W¢n 5¤r— ÿçmp—‡¾ý»?~h:eI6è%~NÜ#Ë`Z6«×¶ÔÏÜëM_/äu¢nÕÿ›jCKð¼þÐÁÐÅ\F½9+[½{È:uÒÉe#Íë}2Fjt ¶…}¬Ý1K™„ÿ$wüø0#«n2<ßÝëzo¸”³ÛIAa1-Z¿“ÝN¢hæU<È]Iƒn qÑôùò-dµÚ o‚¦iÓXÂÿÓ9¡~Ïu«²ƒ†•¥öV´Zæí.§ÉŸÐûéVrT~ˆÙ~dŒ…¾º-šFt¨Û}¯SûXŠïÔùõ zíÝO»Á5í?=·¥¿îEpÁ‰Ð Ð®]ûÄ¡NÕÿ½ˆn#ŠÂõOŒo£CC'sõâ­]%É]¦p/h¡^'b ötS8néRþV’“d’Ð0ç¬ñИóѺœ_nú\$é>ßeÕc}j––Ó — S–ïF Œˇҹ¢RZ¹m—ÀW¹¢Ô LmsüÀaÀû|›•å»^¸©nRŽÛiæÜ"ú¿•¥t¶ô¼ ÜØÄ A¼}¡ÕÛ}°Çú*7q ù=ûüÙÊahmý!Ô©úS mú±¸þ·Mk†.†Næñ)¼>‘~îÚu-ǽl EÀ t1’ï*·¼2$¡aZˆÁ¢|Û¡ALäÂÙBç­Æ¹¬ßvaý.á¸ÞÛ³ŽŠh'jÀeÓ~ˆ³.m]°++xýà„^ µXãH2ºòù®hj/…8ï¤Ç––Ð]ó‹(ãÆLº¤c¤‰^›Ä"¸ž4DÐÑ÷õéo.ã/¥¥ºž± ¨ôîQ))Ά”s‘}E¿ç}ByŠˆiw¿êÿAÌO›åõ‡Næ*ñ…¢Î(y•Ä.(îOç!AÎí~jjƒª©ó$T’ÚÙK $¡+ƒ…EHB/ìbtÍm·9¬^±‚»­ßnÐÊ¡ˆ=M%Ö ÕÃ;nÝ¥ BJa±UàŠ¯ Ê ^çý ¬`3|l ã”À¡ëci©³@µQ!Ѐ•ûkKéº9´"K\ÊQak÷Ò;£iZ¯Æ‡E_´†X(7¦MÞOŸ¸ïùýûQI}­¡²9µÍE¿çh åÒ©×f#k;ÕÿkƒËÿÿãZ@'C7síu^÷“i»‡°«’páe×ÞÃO ®3™“ÿÏÆU£"àÍ…|-õÂÊÌÙõþ'7W†$”«Í>çÑÄGd#t‡ÞY.7eî"à|iî'ÙLÀ‘dq¾•4à<‘9Ôáp(^7¤âAu5’ìpœïº÷V[­„|ss]ýqÍÙYîöó,×÷ ÄûQö÷oŠ /¢O®êÙå»ÇÏ ½JÖIÄPŸè÷•e†µO윬úÐóîòúC7suu^w§Ý1ÑÝ$Mßì^6ØBÝ£ ÖØÖÒsém{…Cø¶±V! ™˜6Æù{ßî' †lçЃÈrÉ.U’ï\qàˆŒ¡yÅgà­ôÔŠ-Œf¨2\=ÉN­g¡6(|Œ€•½K>Úa¥w·[©°ü¼7ªß9ˆž™NýÛƒ'yG':*,T? Ë&BÂZmå }¶©®(®~_i MD]\gÓÞ¸aJ¼ƒ€¸þ¬“Ë­Ön\"n¬Z¯;À¼–-à•¢­”KF›ã” î?lßmØ„.(º¦{Å…Sú’½’€—sVÇž¢9½¼ï!Åx”” œ·’Z€N â¯P9½¼ÒµÂ¤6´V*8”ÉÇL¼¯þ¨@$Óñ$ß}Ûšéß×FÑ{7Dy•|K¬cc"4spP<¯ÃOü¢þÀòØ‹ÌE¿¯,3u ®‹£6ûèdèf®¶ÖëÞ÷¿{¢8 Ïå²iÁ-–ËF›«ŒÑ®He{ ’ÐtÞœó‘5™€ÃýDº ` ¢ ØlN Vg¼ykÏŠ »Û˜+¹—! ßiîsÒtç7uÓ4¯ø€K¸¡ˆ—"‡>¹ÌòEx+©%Ìüù²)ôZ 7âî¥wЪ­»©¸ÔJf³‰zuIàdNã)žãÈA6¦gȧ¦¦Ø¨Ö·+B“Õ»i߬M%‡ÓAÝ|…8fÏÁãÔ½c;²„¸vÙšy€U°“F èQûNÕ¶¼÷Õ:Úš™COÝ5ÇÃ.‹@IDAT•úp"_ ¾î>pœsÒ-_I¹]§O3*è¿©Ö*¤õuàÈ&¶ÐMýC©Z_5‡ËeQ Ãä C¢«ß»Êc^$êà¢}# V¦Ð²M;iâð>t×ô U*ù¿·çÑ™ü"zì¶«h`Ϻ¯kS¯ÿw›3èÒá}E¯ÒˆVêÛgj8Ô»¹ts­×GjÞ+9Šn¢ùÞ­Ü»¥yãÆõn‹Ti By3Þi£Q6›Ï¿Mêä5 887"t8ù";O£©¬8ƒˆ+ xIKFÛ·¾h]-YŸN×LB|ôšu÷4j§W>ZJ¶ÆYœkD·‘–óØ/¾ÛBC{w¡~~3ͼz4Ðn±ýñ —Ò3’E @ZþõåJÊçøøÞà–¾ïuNˆ£-9Þ.þ‚ò2³Ñü•Û.øß”ØtAº¯d‹÷ËJ«ïx&Þ¿¾4Œ–ßM3ø“|C¹¾Rñ9‚„¹ûlÎÙ³ ”i®¬£ E^üЈ°PJãlÏžºøà±3TÊÀê+¾¼þžmðeŸñ¬§>Ë•ºÙóš¹úÚÞD~ÄMð€]“)èî\Pp^”óM2 ‘ý|CE0erpäÐIÝSÒWßîÿ¥pKtnaÅ9Q1ßßu¿ž×³yP@bbê 묧Bªgj·z À÷‘ gÆ[I­HÅ^ë´ÁÆn_+6gÒe#úѤÑÄ©µ‰¤n¼Œ~óy´>-‹®ÝŸIe.[ 3‰CÑâuédb”®OAã÷ÇÀ½ãã%)÷dž°šß>u<%%¶÷,z·\5†­ìét®°„FöëN7LQe5\DÞüt9Ý?ã2êÙ.¾DËÈ ml¾}Ú8œmuÇÇEqùQb{Q‰•^zÝsí%Ô·{GÚËVím iü"1qXÊc+âk/£‡n™Ä‰½ö ך['¡·>[!Ê{}Î2 2™ÄK Ü´c?}Ë_0¼kB[qþ×4}ðõ:Ú±ÿõâ¶MŸ8”ºwj‡MHھÜ8,XìóñâH #¾(`GXñÑŽ#§ÎŠAæeƒ÷B—p[Q÷’õ;ĈIÄׇp&hÀ}MÊÒ»3-ß”ÁûJÕ¯W_Ê>rŠæ,Ý$Žùõß¿dki"ÝÁ_-š*'ø!ûxÏÉ(§¢jƒ+"5zpDÝ: ä‚ÔñM­·ÇÃx(û*æMϲüb˜ìÕ%žö:I{àJ¸5¥°»¢}l®öòVÓýq<£ÎÔpýÏ/¬¹'ÏŠLȆöâ{¦¿(uÍYº‘ï™rºdhob—÷ó$ûÃo6ÐnîG&îàxá½éŠQb¹¦>SW=¢2ßþÈëU¥«Óöc¦x‰ÂͱfÏ“ýVÙÁ`+~¹Ñ vÎ-¦9"ñIû@6Ø! ãW¯.æ‡p¦dg96rdÍO"ÙèzÎÝ$\‘Ãz"Ö¸ÝÔËM½póƽ^5÷Ngò‹…•»íiEíÛDÑ1vù€TpˆÐ}üðÞÆ® ±âcé“%›Ä ^lÿhñ ³Ó/î˜B‰íÛЧßnÂßB}ç“¥hì žtÃå#h“PKOIhMÁAfA@äÿ?ìÈD·{ÇöÂíäßóWQfö¹™¢",Ln5§qHd“ˆY®²A6Š9«.§±¦B&ëE¥eìÜob"ßCì㤑tßõÅ2Èìÿ¾YO];´£û®»”1á……+e·ížk&ˆHBøZP›Àê â2ˆ]ì;íüö¾0®âZûÜUYõjÉr•-w˲eÜÁŠ)Æ„ÐC ”„$/¼ü ž¼—B! ¡ƒÁcÜ{ï²$Ûr•mõ¶ý?߬fµZíJ«Õ®´»šc_Ý>3÷Ìܽߜ9ó.¤®ÞÀ:ØBs§Œ¢Ÿ?ºHŒòÁà»’-ñÈ{ÚØ!ôÈâYÂÍf=ë½.:K[‰NLv¯tzgÕ6ÑHOŽg×LJIŠÏ1“;J‘3UVÁã=óõJziëË |#ˆÎO§ÇÑ껓é®<}Wƒo<¦`þ|W‘¦Ç*áÈŽ4fh?ÚÍó ¤ìäíüaä®X{jžê?)>–Ær'÷÷, 1ÜÆÞ[½ƒ­ê‘:…zÝè¨ÁÍåƒx)‘ÔŸÛ×Ëfð;0ݨŠi÷ѓߙÖò‘ipÝ¢Þü¹@Ͻ‰&O^ `þ~IZp¿¨1p‰€’P¦Î-NPÊý®[Ûì_<.€Ig²nC†Û÷säÎ}Ç›À¿ê¦ðÌ%jà!x%JÐ@[{! q-Y‡â™º²’£Ò: ¬ÞW Ïf íT‚õ¼A³\b=-%ÆéiòèÁTr¡LX å½ùòiöÄ‘Âçù# *]e"[Ów3Ø…”–UѹKå4qTŽà°ÿÑý7P*û~Ã÷§7W ŒëF ìC§/b“3ÏýôqÃ…UMÒ¿§âþÿö~츌r àMÜ|­ð/Ÿ;y”¸p~ºx†)£‡ŸkwvX䟥|ÖÂgÌéKÛŠtN0À‡‹ÝÈœ>”œÇ”~¯íî=éi ¢¡×´¤8D|¤=Gíz…¸yÆXºjD¶èÀ t.à ŸÊ÷!/]*¯¢Ûf§q#Ð×¹Cé*s&动) ¢ãz¨ð¬ÇwÆS>®ivÖ~¡Ñp7¥Û}Ó4:›0êÎÊÛ×|"}½QÝ×9%aá¸üUüA™Ë p,sÎ?ìœÜ=äbÓJÙ™a8Îòü$Œòpe›‡;b‘]¾ù ­Ûuœ&²EkѬ±Íòúík+©Œ‡»X8U„b?[ZA~g-}û+³ÄÇO^ p»bó!Úuô´°0]uegÙ?Òò:¬óúJ*¯¬€ ).–?þY´ðÚ1âj/[ôð,c†úÅ+Gd‹í«<$¸˜0óý˜.ïˆÞEáÂóO0 ‹NÕ,>â€GWÁ0u_¶;KoŽ?ðË#jmM­A´'Xq¥€³÷ ¿siìÎÉf°+…ùÕÉ]gw¿ÇŸ¬Û-Ò„U;­ð˜ INŒcëú\ÁH‹ÞË®£§ï¿Q¸žlb7¼ëìO²~5»®œ:™ÙJ —ù¶¶.«ªa·“4ñn»^—•î8”À t<ðþÀ…ÄYv)n.Ÿ®ßK+6퀧Š'µÂ}f`ï ¡¯÷×ìþáÌ<ñÄWOÄ n/¿~Õ‰-­yÒ¬?{âíVy[Æ;"€ØŸs´Êí5ЮóöŽ€szy=#顱14gP”05;Ÿ Òmµ«”¹·]9_Œ6”ËîFF“…Žsgqï±Ó¢ÆLΗ‰jmµçœ¡å›öÑEîÄJw-ŒÆ “ A;‡àÅ;&¥²¦ŽÝS¶²kÓEîÜÅqû­sŒÉkœ×žòq¾¦¶…²¿k‹ØêÀÉ®£Fº?ïzXëØKÒ …W¼”ì‡,þÌiˆ‰üÒ>X2eÊ3ý¶l±›®üx»“Ðè¢CåÍš®yw½Ý‰5ù%ã‰ÿ·KâØ:uˆ­i·ÍÌw|K.–S=ô¼òêZºÿÆ)Ì=G®ÛÃðÃ4íšÆ„‘Ù<”žCÅßv˜G®®Ð/½]ü&üïëËe²-Ö­åÓâb¿¿Í²¾Dê ¾ïáG&v4­*!1ö%¿g€• J”êï$AIÈèRŒerÃO3ê¿îï<Ú•žFlo›°€Ë½N_ྕ‡L Ï^v佟-Þ†±Of¹ûoºšrúfP#£õ¦b¶žy Õ÷ë™Jײ¿&>¶»žjq),¿{}•ã8€Êo^[Al‘ƒœ»TÉþ ÛéÙ—–Ò/_Y&νþÙVÇõrð?¼¹†;v`ðÑ{iÝÎcôöÊôìËŸÒ¿–bÒ[™¼\­•Ú­ø}^Çî÷“K˪$^ ¿³¿5¬ã˜¨å,k·À~Ðð§ÆpyNŸL᲌­n˜ø‹ÞAºöE&± 8,Øp?`råÖ…Â-®-û™e$#5IŒ<ÁŠ¿mœÙ|‡ è-öù݆ÿ·«€ZÖk<'|³ñž ç{+¸#¾š'¤–±Ï:Òƒ¥Û[ØÇhÀü©yt3»é`¹…}̇f÷¾´Èã‹GÙ…egKz’}dé£ã\Ð,‚E È[næ¬ôd¡sLn…+')©²Ðï6ÖÓŒ×*éWëÈ|íäæaÑôÑIôÊ„Pßž3$ewŒâ°ù‰†²+‘«´Ö>\ë–î*vƒ F«¶p$×›çf ƒŠI×1ZÃí͹£ Á…(ɘwK¸×w÷yÊGÞÓYëÿ,ޱiÚsMùÙ~¿ëáA•MûÁ»ôÝÜàU]ç• ”„…Wå¿À=¼ç‘+¯çÕËW‚æ91C¿™\ ˆfë°Üžo™3…Fäôâ‰Wg™¡ C$²ÿÄYºéšÑ´çXó‰^Þäp‚}®‡œ=Ýcf똆ô·368_×ÀVu)üíÃyðd6ƒxòOß¿€>Y¿O Þµ`¢¼¼ÙÀ’ ®š­Ž[Ùʆ¡úų¯–ú5;޲õ¾¥/_³DÔŽÒ@+¸þêÑ⣻zë!úðóP`ýÈí³ÄäCy+˜`‰ýé‹ f„®Í'iÆÄ­7x‚×ÿú¸ìÜGn“·z½†oê;+·q'7. &ö•; ¬ïúè(a݃«XÁñNÁ¿ëß»n¨0ÉTÑ1|gåvÚq¸ˆÝ]ð{Þø§3»Î¿>ÙHßùŸ —±9s©´¼ZÜêîi-™_g¬Ï_®y‚ñˆ}øŽƒQ±èŒ|ý‘‡àþÐbg¤¡EüS³™ÁX®ë) 5k©Äß¼n‰@;CyXøãœ—Û‡>X»›n™1†N±… @w š°ÖŠ?îÃÔâ[‹ZÊûôÀ»˜©ž8–ðö ¬õ·ÎȧÚÎéN‡™ËÖDo~··5ú»—18_¹åpÛq÷ÃíMzê¥Âl¹ÅRÏÄNãêß -4‚V“¸½jÂ-µ‡ùϾu›pKÁ1´S»hô·ß׸m_=zÇìfûÎ;øÐÿõG÷8âI‰}x¹UXÖT\ß•[™* ‹LFtÍó¦"t–;9ˆÏ­³Æñ!t*"Ä)ö¹ãº‰Â’ ŽwÊõ>Ð.JêEçôn`jB,®:D,ÎzG¸ž Ó‹÷3ûˆ-çDÜ Â_|°É í#)8îül°ò?ÍTaÉÇdQH‘ÝLŽé­ tª²¥› &VÞ3&†n®§…¤j;umo{ö,Ñþþüûù£ë'<µwõv8;px‡áÆè,h‹Sy"1¾‘²ó,Ï£Ã7(ÌYp Êõi-™f ×ƒ»ôM?‘ùh6헇˭‘ûÁ¾V¯_°×Pcù@IX8>ÿ_þ]ÐìXHã^}ÑÅ` ¸ —ÃKºÔŽ¡Ý¡üÃab«sۃ쮑Ç|¸®YÚÒ ¼÷ù.ºcÎ8K‚»{0L˜œÀ<¸|ݦuZ[‚Yè΂û61Å~ ·,¦±ÌnùøË}´™C0¹sê覯8ÈúdØ­‚ØçjüBG­•¼Õ€qž®Gû—€ÕÝ5MÀÛÝÙŽk-__RvçW +?†é!˜8‰à?à[óÞÙ,¦_t_ógø¡R3½sÈHŸ¨ÞÅà ÃêôQÂÚ=­”s–j;„4à©} þ¾åžmÜ|;_ë|Ëó®ïLkùÈ{¹Žˆˆûÿg’jG’F½Èüü¶àþÖhÓ%¡Ùh‘<— ÇëÏÀüt³t›4Ç¥`N­ÆSZ×ZÀQŠÈH;óÄ+vCEé+ó&¸-·§ƒç9ˆÈ??ÝB׳0ßi"š»ëág:J®»SŽcüa(Íʆôeæ„a0„g›s‡áÚ±CQý®æ€ yìƒIOmòu¾×õÐùœÚV”\Tyš˜ó¨\Â7Ý[8ðÐfgyõãõ€ÃJÿ}U_öŽ€6÷;‡ ÀårSŠIzÐÓ×ò¢©o’gPÖt‡ÚR þéÚÙü™],K«é´ÇBùD–kÀµäÛÁBI¨³F°xã%›Ö¥pYe¦~kŦVŠø¾Ú bŸ¥‘çµ(ÈÿX²Iø÷áà"'Ùµ’ÉAH–/2PD$ÚÁA†e÷ä(wEÍ’Ù´—Œ°ÿ'|¹íe°1 ‹€x%JÁ øšbQâ›Ðy뎿äØ ½Ë ûãcFªe—W–AwŽ¡›†F+7Wå¨ý×@Ú5w¥èÓÿ*„GxÞ<öýQ]â ËàËZp_´Öµ÷t9%alLL©±¾ÑÍ*,਎aìƒ ÿÐ<Ž`Çhòîš]ŽsþË Ñò²6)wÍŸÄ“£úÈÝv­¢'3ƒÃ æ)ÿtÃ~f’DÉ¥±Ž4zq¾ËÙwþæpOßë=×Oj6YÆq±ÚPPP` Ô2…ಽwØ@žã*Ñüû·`p}…#Uæ3·¥pÕ@Úüïü/ÏßèKQQÚ÷BñYÕ[bµJÂÂ¥K ÙÇaH%%áËù›6Usp z¶ÐÆòW:cFBÔwfׂ©£YÂ’üóGnvìÃí·O4±0Àºí¼ï¸7fŒ·Ó‡:s·ýô=óÜÇî¹ar³s·2/ùM׎ö˜ƒà>€îû éG÷ÎçÒq" É¿—m£í‡Oµàðu.³kW ïÏ3Ùû7ËWí( ( „—(çÝ+ -|»ñ¤9©t³™Ü:<Š’ôÍ'Ý…—&ÔÓ( ‰ž¡‹ŽqÌºŽ íþƒOæñ¨|è‰à!VgADIx‰U'Ð_]]]ow:öª‹t™.Ëkåð¡µõÂOüS«!pМIÃåiµVPèæ¸Rg¥%ì^à]\Ñ|7TÅÖî¹9Qtç¨hšÐ[MªìÍ.‰žÓÕ»R×–X:?ÊÙî…#?Èû¬+ËÔ‘¼ïˆöºêÞà $D»ùÕbA´‹â®RG(å;’9Š1ñòóíGW8,¸:Wøƒ‡Òs¨²* ( øW&¦y\[dfàm  ÌÛÝ8U¥Y&ðíÆ¤Ê…Ã¢(9FY»›)'Ìw@e¹ãP}'ïåh¯D£Ы‡[JÂ@çí.}#¿Ÿ7 f¦FÎ^›õPb]ÉSî® •c €‡JM9•3( yÚO¹,’Eg÷YàjÑ´øœLÈÜ8ƒƒ#`é ‘zUžJJ-5nX»?cÿîʆ–ÖîøhnÍÀ;šò”owKvƒ#ˆº#Àöí™FÛ9[ 8¨2ÿöþZzú¾)KßD}Û•ªþÝæ*³Ä5Ak0_>uß®ßÝì9ükWÖ˼÷RQÁvYWS2ÿw¹œ{¯ÙÈg@è*èß8Œ®ÿi?àÑNܰîôî¿ÜTJJJ­i ´ÆJ7Ò’£F**oIˆ{Ç÷ޤÛGêiÞ ÅdÒš.»Ã¹½ÇO3cV]ÏAvþÍgKÙ)áàsˆ ^ÐÔÂõ±_Vº"Ö¯s$Ö’‹WÑi¿:Š` ƒ;Ëÿ㨵·Ï™HË7ïãH͵4Ž#cÞÌt™tõ—·×ˆ€<Ï¿¹’"yÿ—ßÞ¥jÛÏö78Ê`ª8ÿóâßÝ|Ôq D7ÔVˆV( D­Bñùebêi ÂÓwžhMp.@Š?3Ža XUÝô²ù3íîšVGØó•V±»êL=·Ò€?5ºÀè«iÆk•ô‡-õ-Àwß$=>!–VßLoÜšH7SàÛŸuªiÁê=fH?BØz³ÅLNœq<ÊÇëv àýë'ÓÈ{‰‡n›.οñÙ&Yõ;_›ÇAÜR™ªw‹ã¾+•5ôŸå›hÒ¨Atóô«hÞãle/|SäBn9Žî[8ÍqOWll?k¦Ÿ¬­sdl,­(þåÜ9„ð†à!\y\tP aþ`É”)M\wòD€Ölý®I³ë¢Ïp™†tÀ:†¹¹ka­å5jí›ê FAµ˜§W3}S¡ºKiÀ' p,.ú¢ØDß[YKS_­¤3ØrÆÌ~¬M¥Ñmì× À àýØÄš£>ÍMêÞ[Õl<9R|–ò‡g‹¸#súÒöC…¥;uQ¸¤€ùkHÿžT×` º¶ÞÀÑ¡/Ñ´üa”È¿ý“G&X˯TT;îÍ–M³'ޤiàC˜â·«£C/¯!Ì€¤ëêhxÕ¦¢®*¿óU.(þÖh'¦×•”„:M+·6ŠaÌÜ!îßLÚ§ãá®ô=]©§£'/ÒØaΞ;Q¹a”ôˆªÊÎJsøÚ‡Ñã©GQ: ì¾`¦ÏØÅdY‘*œá¶½¨p¼›Ò/Š'SF+“ «½à*ÐÎ#Ådeúéú½´bÓ~á.RU× \DºGæô¢/v¿íëw@OpìäÁ´õÚÒŽŠ‹ÑÓ™Ò JK¶GZÎf€-%1.&¨\?Ëêlôð§5Tm°¿?™ñ:š£;F§¬¦–%äC„ØZð«0çâv%%!:Ç$Lv‚ñ€;À7‘àÓ¦‹ˆ ”ø(öWÖhÏ±Ó €;W¸Û{Ž•°ûIõJOäi_À°(QP🖚E œå'Œt}¼ÝɈŒ“*3P(QhKp?лhw/ÿöÊ-h÷Ñ“4uÌš?u4½È&Ÿº@×MEyƒíF«½Þ=ùµ¹ÂºíœÒ€Àg<Å`¶Ñ#ËjèL•ý=ŠãQ¢¿Ý@ËWƒ±¸>—)8µïóãtÃ]( ‹Ççϸsﺀk‚-à¬Êñ»ì3G9aõŽ`à§‘‘‘Ô3.‚0´V|ö2 ìÓÔKøs…YÐß1þa7‘Bí:†¾•( ( t\ ¿œ­ÜˆPYRå~2eïDÝÈ!áò2(-¢ã™ªº.•Wñ7ð’˜P w)Åç.3%a±àkw¡Á}3éš±ÃHAQ“ísúdR[µ—mÚG‹çL Äø8:~úû‘Ë’2µ–kLØ„‘×'Åǰyt§mÐ5ø¯ÕuŽh¯:¶ýan<¡óº¼eQCúˆà!]}D‚’p\þkŒ‚Å£ðHÕ·yµÛ¸ 8>7ù< SZcíà;’¢££y‰¢¾©1TZ[K­ÛKß1Sø¾òyÂ1m£ÉBK¾ÜKql ÍÔèØ@ÏRçáøÌê™”­Æù¬ÀÀÀÛÔb¥Ì;5r¢ð›‰¥_4°A¶ž¿‡˜|é,˜$ùf7©¬©£>™©´ä‹]b¥Åj û‘ÛgÒà~=éáE3è fMù13ž@¬siŠ휦óv|¬ž©r‡Ó»«¶ÓÛ+·Òï¿ûU¯ ÔöϾ¬£5EM–îÿ¾&ަÏ`Sê—!P­¨ÓˆÒ½`6ZçlŽןùé€ÁÒ4 “:hט:)Bo½>šp¢ùe×SßÄ:*,«¢÷Ö줻L èã„câï­ÙE˪éš½(Ž'á c=ë`RP¢4 4àµ0¾Œ]KàÓ}ì²ÃôÐìþD½Fs2U»—À¿›ƒU*Qèn`ÚA,®2-(O®Ê û4­Ýq˜~Å4ðë6MôÒëhÓÞÀ1ògߺÍAhËßW»°KÇïk–ì£wÌn¶ç¼Itë¬q|Lë4À÷Û›¨½ï£§»òôÍÊN; €‡Am‚’°p\þ*öëšË ÿî[ãÇ h„(›NWNÖÆ‘w.(Y²YlÖÚ“ ÛŽÏ“tA@ŒÑë)&–‡¼xÉLŠ¥Zc í?q–hù6Fßi?¡Ü,`ùøÞÏTU#û$óLö4¡Wèpw.( F“Íl6×£Ž\–PV…*»Ò€Ï¸Xc£e…vK7‚司¦ÎbëÜÝ×dGR”êܺS“:@ XÙê]^]'&\–\,£“ìžrÃ5ÍA{ðn_A:ÓGü9ð Χ¦ÉÀ;í+w¨\­x¨ÔTÛå%á\q™fûS>Ûo ÍJ""*ÈjjLÝÖV¨,€:!V“¥´¢²¶‡K€aðŽg_5Cƒ õ5›Èf­ ¼´¼šn™ž¯|Â¥2ݬáó½„Ýv.²ïàОñ”7 Sèz…~¥ Šë­Uµ6£Áà –t:ï¨;§cjSi ,5?îU…&ZÍCàû.¸·tëy‚øtÛ׿aú‘„}%J]¡1CûÓ…+UôÙ†}t¥²šRã…Õú¦ %yv]½s¨Éò ðýë9ñMVºPz˜v”Uðv(+˜/µS~TÄöËž™f2ÔËûr Ê—œ\Ss¹1XަÙ9¼ÈÌd0œ«®7hu °A‰±p[·ÙÎÇâã|Éh2‘Ùl¡>Üîj òo®§aÙ=™¥? Г|íÙ{QÔ¹<ß k &®ê™G=¯w âÉ«ñ ñBŸÐ+ô ¦èÛYP¨“†ºÚKÎÇÕ¶Ò@wÐÀ‰2Ý ¸¼=¹—À²=A÷‚ÁQ4{`4Áò­Di 40oÊ(ªò̺Zz÷P“Ïwwߨ/ÀCµÕº”»‘’ð/6²=Sl¶DdÌ€ðž+WÖÕŽË™‘Æc_±ìúÂ¸ŽƒÒ·.¶ %§6%¥§绕L•ã¸n˜$ÿoXÀM ¾- óŸÚiˆp>‘¹OKë¬TTrQMàÈ–I ±|_÷kÊ/_USÏA‹ `¢¡hv>ê›A9I”ššHII¼$&}B¯Ð¯;÷Ôêïôñ£û¹BP‡rqÔÚðM§Î_¦C…g©¬²–*xÂ|4;[òkØ}5ÊïËïßè¼÷DBT¸>¥$ÆQ:û¨ææôiA‰ÖÙú@~  \]Ä–îB#aR¥;÷ä¾ð鎢ëxB%|¼Û#§Ï_¡CØ+ØEÀØX÷¨Šî"И¨ÿÔ<åòdB|Q¢4 tgðçï¼_cä¦$°èDJB€íÂ«Æ >l<P´K3gÆóvM+(@Ý—Ÿ}thȘ±Wv*Jcîø¢Á* ë,XPàà bç WŠ"½¾²Lfª1X¨Æ¤±•¼ŽÊËàÿæHÊ~S7ø 6Aâ2c8Rl$¥&À}'–t|§$'‹m¸Ÿ@¯î¬ßP×…Íj±TܹµwFD]5®y¥Ä[ pG†;fžu„¾Øu”*x¡£˜ÈuÈõ£ïÞݱ=›Þ ¼W-µU:Í~©Õõ ôñºÝŒÏš0’fMÁÂΡåCƒÞ}žA7nïsÕîA7:±ÓúG À=k`$%éÛGÙ‰;Ô=&ÆtwuÝwv]»Ë¯Yý¹GÔÿL®ûYãG°[œ‚ îtîÇ,üB>óE}p¤{Z¾eýªÖ/5ëN§$ÔpÛÀ›jkká†â €;:Ë¥sgß9¨i0Ǩs˜[ ´õ¶h²Ö³à¿e¸Oèy‚f=ÄáÇ ''ÂBŽ(a€ŸptT `2¾"tðç‘Ö €vÀí–oqŒ&Ø­ßî'_¢ž8£>ql5' t&ç:C–JZÑÚÚ"¬Ýo­Ø*&E Ë΢Sr•«T£Þš\¥Jèõ;iGï»ëú)Â*ÚŠj}>…ü¶3ðç61­™‰.óè™;;Éôl¶rŠk_ÝKžaj¸-TVUËnrY4_Õ}3u;×ÿGkwqýev«É¢þåïZ³ÔNXj žŸ\YCëO5v'·çJUÜYa°Ý™”„ü}«•*‹°Z—ûnÖøú p÷å’Þ»ý[OÜÉ@%õéûoÒEG5YÁ*¥U?Êö}€o¦'dw“à ÀaedÖ¶”€[»ø–:•zEn%Зdà†}\\¬X¾qzt°¥pX­fSõ¦eK×ðyP=`‘ Ü=bqM¨›ï£ý¡~¾ý}̼ëYéIôÈuתÉÂ.íó5Æë'1Yxý>záÏiÑìñ4gR®ËÕ¾íÖm´þ´‰¾(6Ó—§ŒTÕÆÚ5µ$v'™Å”à꾚-Þ°|wDÖl;D|¾Ó^÷·©ºw§Ëõÿå>úë»ké6¦º›=q¤Ûß(wé¨c¡«t‚^þð%|bì²h„ž~>+.ì'\Êçu^+î¬0ØîLJBͦ1Q—Ýêlް¶6SZSñÖ™+*.×ÞµýymÂ䟽¾t£í·Mw|ý$¸hÛ Â± +À·˜ i4Š šì:Ñ €‡³¼Ñøm× j `î%Ð@8¬à˜p àã8ð ]º tîRíÚ°î?uuìLN„ñ@,„Ëzs½Uí³¤Õnߟ0 =¸/ÓeŽSt™m´D·}ìö‚.óý5; Ma¹nÛiI w’/Š´ö¤™¶Ÿå‰Ûº=ât4'þÜQ4‰}»;ˆ¹E±ÐP÷ïs¬U÷mÕTÓyQÿ‹íõŽ‹Õf¥9\ÿž~«šîT[¡ª"Ý÷Í¥5tÖÉýë± ±ôøÄ˜P}¤—[ð«0°éþÂFÌ€S:[Àu6›7p;<ÃæUŸmMJK}ƒ·ï¥ÉvÏMÓ4i X”tyøA–…™°|c2“Ýúm–Gtð!Äî݈…í°€C7°‚c¢:*ÝQÂ*ng<ñdùøÞy¤X;qpßÒ½›×@8-„{€2á®å¶ŸO‚o´Ç'Jhé†ý€Ýµ`bÛ7«+„ð¾ }-'¶ïbëq²×î˜D +÷ç'=ÆA&À}Ý hºªwêý¨{´¸ ìߪîÛ§\çú_òÅꙚDyCú)Þ>5†ÄÕ»xþÅ£ŸÕ8F¤Ðù}vFÝ>2|ƒìxS1 €{£¥»&gáÂe…KOIÈX°Fb^öÃnÍ J€;ðÖ¯xûßÎ]ü5ĘýÊù+嶯̛¬sõ Ø„¿³™ì.€·òø¶OÔ„ ¤»p<«ÝRd÷àŽ t$¶˜{²$Áçn'°|Üûéº?øœ“õÁkXÁ±:Âh…à¬w‚6ð]S[Oï ð˜$,ßî®UÇZ×F ÀñÿŸå[èÙoÞ"Fs\ï0²C÷Ö3<¹µ˜ÝKNš¨´ÖsÓÌÍŒ ™tÃ{4¹·¹¦ÙÑ}Pf¸á”ø¦YÿosØóÁ}™Z6ÆWo¾•RÝå«–sÙ§ÖÔ‘ “2X0Çâóãéšþá^¾=zR¼=Ú ‘kµgŸµ^•ß”„ŽI—<²- 8Þ>€:̼Ѓÿx̪÷Þ\rÕ53J­S¯½ÿ÷o¬H5¸¯mBnŽ–7¸à —ÖpJ¸SÀÊ‹I—Òï [o¹ætÃV ˆ°‚7Zá9)SwVx¾A5¶L¸´˜M5;Ö­yÿÀ¶Íù:Y¨KÔ öQG¨«ðRà‡l¯ íÁíÄÀnP˜HXÉÞ;_;A¹´W‘×ÃzËô1Ž0ˆÌ›š':åõ6Zǵº7• “·ÜI›Ó¦ôèŽQ)3ZÎwpw_GŽ¡  ¬åUuôû|ËÑ»Ž¤Ù]ïmVÿ;3§užø}ÃïZ8ˆó7 ÏÓ¾S²Þ^Ýk ?8âj”‘A/Ý@#2T :R\¶”p[w%¡­i&ÿX¶e—\ZÀø0þ½{úíGwí<1yÞõs-ó ‰I $m ±zJcîX}T˨®?b®ûáVÎÏ#A¸<æº/7˜L6DEÖÞEGoܺjÙ¦††:PHbA=T7®±ílWœâ,hg˜pidð Fž­‡O Æ ø´*ñ]ИCÖì8J'"shÓ+í¿È“«=$™kg.™Å {[Òb;ј&ÛÀ:¦™D™UÝ{¨¤v–õ¿qoÍ7Ì1‚çé·­Iwú¥hXN1õ&xàe Œ˜8Ú³c£Ó‹×)‚˜ìd¥….5èhˆC-–bS{ÒŸö£>Ivê6ëן TÜŸÚ ¢´:…’P³†PÿÞ´ÀqÆácX[ñÙDWXÇ“iíGï~ÊÛŸÉ˘=d؈A×ÖÅòÌKv=©Š¾dˆˆ@Tâ¥ÌfŽ;ZW_]WSUqêø±ã'í+á[aÙF'–nè³²q©â5êuƒ:ò<ÆÏ'»«Ø­ßfaýF@¨š:ƒ`õg}\*çל_ðÌ´Ä€>&RŽº@¯o>O5Qéð³j–ßÀ3—DÓLf/ñ·?w³ŒÚØA8yö’ùX0uTW‡ÏéϹs”›Ó[¸Üâ©dý³n‡d÷£z¡äŠ‚w {-ëiÝn«®g€Ùµ1QOÞ¤‰çÆûÚK‹»Lµ d+- ÿ{#–¦å¡W gW³¦yJ݈+îMk Ñk:’T»hVo¦2®Kˆ¯+À·Ý§¢ œ› ì=ÖóÔ‰èIIóГf»áÍ‹—^¨2›aUÒ> @çÓ²óƒ|è`–oðŠÆm€réÿÝØµâ#J„ðÖoŽÒÚÀn=‹øÐ ÐÓ+ ýæõ•”?¤¯à‡në3[ÙO””rÚYm]ê÷ó'Î\¢¾™©Ó]võöÃâ¹ï¾~²ßórNz„>SM©:2…4ºªw”ܰtàzW‹l‡ŠÎ´«îÛ[î5ÛÐêmGZ܆:yîá…-Žúž{ÕÖÔ–0.ëÿHñ9ÊîÝCøÃ %À:edc˜&ÓINŒ¡^z#ÍÉŽ ïnñ&Y¿]#Û@EM%úX÷Þ&)>†°@ØSV„o‡®!o¯ÜÁ1¢iáµcÄþÞc§iõö£ôƒ»ç¶Y­µ;$fâÉÅK×遼ì»É«Ç ¦éW ùtƼO•Ì,„w,Foñ¬–Qøæ Lñ{©xà½k!ð÷l±~Ù¦ƒâ7uÖ„‘„Ø Y•Qïž¶õ«o]?!¶%¡±þ.~¨øãÁ4™9~‚›æµ×K`Àç¼ X÷õôßMˆÐ4ðË&ËÆ}55ù8ÆŸ%—k>¤Ä SˆÔ­àp/^„Ãû8.ý¾å½|H‰³À¾cnà°’çÛO¨‘) aå®7˜è†iytääó‡ö£Ô¤8Ð'JJ+èN¦Ø‚|°v7YŠ¢o™F[Ñ’u{è;_™Mõ Ô?\·—îš?‘ú1 ÿë»ëhƒê ¹hïñ:q¦”Î_®¤|ö±†o$÷öÉL¡Éy9L fáðéÛH§°…ª?!D©„osjbœÈ € È© eôöª4~äÚ/“;ÇÙ’µ™¾×uÂ%¬ 2™5­Zl½\²nõç2]5¼¿¸¿­?zþ¸=CßÁ&²  Œ(kWHU=^Ù&A‡©‚Gc¤´V­µ;¸·lãÎÚQn‹wÌ™ :˜ Ø5¨—p=‘ér br3x°¶ùüèŒ((ï¡â—ZñníÌ¿jÛQJgצÑCí¿á¡äûïÝ»¿ªk~AÜ—E €4WJB=ÁÙø€³ÁD~"·×^…[ ±ŒÇ¶é±>}ârb¢oÖoþè¬mx‰_ä [.¼©¤ @¯rnA8:>ÝXcÁ¹Æ®o)i¡P,°€ƒ‹,(þˆó¦Œ¤! dRw9EE<mäÀ^”œG˪ u ²~ï¹a2ÓGÓ¸ÙôòG¨¼²–.ðuðMG:ÑüñÊb×—QŠÑd¡E³r¸–Ç®;DægÎr´ûG®S<ð¾¶gø{ hö¤‘¤kßèt[ÇË×çôtŸ¯ ÉSzêxj %¡Õ+øºŽ•û¨fÞ"¶Qt¤-ákf9”›ËÄ-‘ÙØ¯ÅʘÛfµü‘c‚ ³å[p¡ñ6ÿH„€µ\PYr‘ÇÚLH]`·Êâà Ük³•"ØQ‰âè¥Þ|Ä!±û ÜG\î&Eì:‚Ú7_MzÙÁ°¼þ¼Rð¡ƒKÊï™/˜N^üàKyªÍuæåßß œáõ$J` -%ÚËg’×·X7êÙ®ïà±~Êòì-þ´(y§`ï<28uÔÎ]©j‘o[uà®Ý‰hºÌ:2etŽpArNÏÞYÂo™xÇ¥Ôy01tx`¡ß°÷¸â÷C£ð»ùweò¨bâ-¢:cÂq¸»¢H«¢Ô¨’f €’ÐQ>M»¹pܸþŽ}7ÉÁ}A£âöº È[ëØ¸È;tdëL”¦;c¥7ù¤«Ï2|—ÕÒ¶àßíìç­|½-Í· ð$(ÉLMQáÛ ÷“þYéÂJŽÉp;[ÉÑSð̲ƒ”Íû iüˆþ4fh_‡ï¶§§ÄäÈ„¸ai‚:ÜG¤ôàÉ”;Eç. ßr×gšÝSL’Û¸¯P°U¬ç ¤pIAùü%€zR×þJÓ_éÈrumYrLÔ½x¥š02r¼±´¼²ýGrõ&L²-d LXÄ/0¸+I§ €7þ1wmZ7™¡<考«“Á졤ã€1ézÅ«‘™¦ ç`«ÿŽ?ió¼3¿4¿Gí… AIÈ6j¶€7ŠÖñ¨ªü{ÿ]Gr½{èü”•( $(ô‡adN/f59E¿þ³ p´ÅN¥»L¢÷y"&øÄ!=RhØ×çò‡Ê&x»?^¿O€gø¦ÃJ¦w2%o½³z'ýô¥OÄ}×p`ŒËnq1Ñ4…'g‚ ãc¦ {æ76Kb(3²ÜÄìëvãkö‰ £_›?IXÒý©¦Õìü°ÓÕeËçNVwœžkµ mœ2z }Áõṑ'¿ÿùnzùãMüÄp°”{˜ÿüß%ÁÞƒk ¸Ê¹Ÿ*x¾;K/Ègã¾Ô7#Õ1#yx“¦ä€/džÍ£w1Ìä 4”Þ<[k×(ÞšvÂ휟) ÙÐä°ê=¸[­M{­ÕbhàWÝÒì.€á‡o»V¸¡H&–9GˆkÚªƒ¶Úæ"ÜËuë#ÚY!MˆkÝÚv¯¿rF;`ù惴n×qÁ÷¾ˆi<å·¯­ï0:Ýè|·& Ø…¬IL}*'P·–—k€®Ö®mï9ÉÑ9P¼† e{ŸÑÝõÊÅVÂô( ù›Z„Çcðœf²Súü´šNkàdmúû"[íÖo.ÝëÌU^æC2ꥀh «¬ž±l‘¨ro ¾åqøcHüó€ïf¦Ü’Án,­‰;ð-¯› Àwkâ ¾[»®#çºJïîÊLeAù$øvWÖŽCdB€q—¦×Ñd}º?˜ô.i(ÁUÞQxŒ6b¾uçç+¹X.¨I½U\È–m>àíå>_'tU1@”€¾–é]Añ 4”þЃ²€ûC‹!’† $?æß@‘yɘÿðµøüwж“¼Y–§Çd²™bè‘Ó´é(òOÍ.P;JA |(›– (Pc0•ázžX ~n|({¦' )‚ã„¢,5Óµÿ¬ ÞDº(FÃBØGÎjj «¡–®âIxãúyÇe±B4½g'Ç.Y&¸ ýÀ‰ÒàÜ/äepß ñ|û ηøá; (J?ßqŒê˜~»Ý63Ÿ.–W» Øu–ã,ݰŸÎ]ªnjFf‹ _Hs>âøµõFÂqtâð\üv´'@WkùˆÛùe©¯µ Š×`§¡lç£y¼\pª Ó¶ÈW52ÿœ_¹Sr 8gŸ-àvãÛœ–ý+«iËîÚu4Lµ¯KiÀïHa÷Ñ•”.(´:Nø°ÁS iÏõ8Àì~â,ÝtÍh1V&YUÛ@ïräZÇB4Ô%tkÛÁbË“®Ýì#ѨÁ}èæé£épñãD.¸y|Àô£ùš;#—ÙŒÎP)ƒx)í ÐÕZ>2Ív¯¹3`ægèä+ e»Ÿ©•‚Ä´ÐJ Õ)¿j”„<®øšL”‡Ô`÷I4'pNÀ§Î³±$óœ±ÐiÏ;¶Õ†Ò€Ò€Ò€Ò€Ò@h@ZÀ…Á¸ÑjÜ‘bYÌVÊc |°Ñ Ì6ˆ®;°wfÉ=y^ÌñÈÜ›RciØ€,:PxŽ’™Ã»¢#ub~HÏ´$q_rB¬˜£‘Æ#`#ôl6'J.‰h©ð]¿ž'ÚŽÒ‡Ínî{Ž› +»WOºsè‚;’s€.ÌExÊGœôñlñ xµ3 `”>ø˜p||4··ùšÜ¦¤†Œ@Ih6Zn¤$d`~º½§{|jKY¿Á/+gÃvìY㔦ÚTPèf•IÏ/L¦¤ÄŠå‰nÁ ‹¤WUGÒ+Ÿøôs×Íj2üWBÅŽ ÒÚ¯'™˜Rþ‹ÎSÞ >Áü×ÎRpš)Ù þ—wÖ5n~IÓqÞ:ÊVïµ;(õH±³× íž¾P.®ëÓÃíñTæõ—ÒÞ]žò‘éù²†NÅkÒPúòL­Ý£~EZÓN˜žó%¡Õ¦cT„ØÚí‚b[¼8¢¨°Àa×4ï(QPPPPJ $úK"Ùz=b`–nu¸è}eÞ„Iëõ‘ÂÚ æ¢¶Eû„ý¿‡ôË oÝ>jØgü¯,¿ðD@IDAT·e¦%ŠuYUõá  °2×0°—Òž]­å#ÓóuíèäøQϾ–%Ð÷)”@k8XÓ”„…Ólß(™2%¶½EÕE4ù€ó»ÒîÎ\qQÁm<À”mÏW»¤%$ÿ§½eP×+ ( 4×Q@æIÀý{’ÃÕ+ O \*¯¡Ò²&ß^ç§ÍÛç;ŽŠàNÎÇÕv×iþÚ;Ÿd›/QNûdLçÒäì%h ñÞâÝ®®kà÷÷²¸Ä5`—™-Ý5|î!Fvgù’©¥d¥'‰ˆµxÂ6|mØ{B¸¼Èóí ÐeâIÈžò‘é©uÛh7hj;IuE(h”„…K?*â·>‡Á³¤$l# O"q¸ h>„¢gßoõ ¦Ù^¸n]Sw<”¨Ê¨4` €iàÏיּ 4só2áh”·ÏºJ„jv—=¨ÉÞZ¹ƒžûæMÙ’J°=Ü¿îÒWÇ:Oº„ȧ¸ ôê‘Ä“ëò `Ê“¬Þ~XX7ïæ:®RÁ|ÕÖÃÂ÷4xJ¼×€?-ßιëŸÅnVå éë–öqðùSriÕ–Ã"@hI§_5„¹»{»€]ÓÇ ãèhMfŽoLÜ”2Óù„lí-(m@úsã|{tµ–ÌÏë@éÝeëh €wTƒ!z¿?( yø„ûØváÞ{»\PŠÇ›d±™e”ƒó!ªJUl¥€kàkó&RZrb?ND Ò/Óclø‘{¨§[ðð‚ª ü®ÐÅ]=f°ˆVº|ó!Z¹å–ã)£;¯ÏžÎªãÑ€tèH¸Œ&RÀ·ÿóGn–»"¸•kУ™ã‡–š:ƒ8ÆHÏ‘p Ø5{Âpš>v¨ó˜@é,G ñóÉÄ–rWùöèj-ç<Û» Ï©çpßЋàímát½+%ᄱÓîØó¥·hqbAáNy»Úƒo'ë·öVζm½ÍW]§4ÐÝ4ЃƒêôÉHakVÐ)fL;¬}ôÅ^ê•‘Dåì×¹íàIºï¦)½àé?à0õø·Æý{îR¥cŽÐæ°–G°Ojï)"ú%Ü>øb7çkÒy2×-3ÆRvVZwS}—?oB\ [¾“ÅRr±Œv9-Êpò[¶ÃÒ½lãºÂ–ò§ï›ÏÎýb"Û¢Ù"°0­Þv„¶(Á›õÐYv²ëÃöC'é<pŠçÑb˜=~8MÈ@JI×iÔîĵ^à[îI0’¦×yþ\· Ë9ÝÖòq¾Nm»×€çZr½:FhAIh±~»=§srAáû¼¶€Ÿ0¡wÏɼ´HÝór[­•”Zj zßñ3ôñúýdà c‡÷ÁëØ©‹4gÒpÊLM$#û‰K#(¸£##éVÐe•u͸Wn9HQl{úþµ‰Þµ`¢Hûqh—Þ2]’™x8®þt®Ð:ÄôºZ¦ŽÎqõü¯O6˜´ª¢M`òäüå Z³ýåæô¦ë& n)õ Fú9¥áJðƒ{æ‰ö’;°·ß’{z"ñ»o˜,"¬‚{Z‰Ò€Ò€ÿ4 ¸ÿt’)’ÐQðFJBÇ~:[¤#O¦ôÜ¥vIÇ`3=ÁcLâz¶œ¯ÍÙ¶{¿Ë%jWi è4ß˦Å “¢:CÖí:Æà{/mæ‰XÃÙ¼‡S”Kr0Lc7W¿^L¶jûQø1¯/¬^9½Ó©¡ÁÄ4h:aE?u¡Œ&åäaîh7"›#ëU:ü‘õÌx–h¶ÄK=*_Ó•åÂhAgÕ}AI)-ᑎìz¢çzÆõï,ýxTâë &ј¡}‹mð?œ/š5VŒ˜L;Øq x§Á„w¦$¶²g1K†'×AäHß¡¬ße¨`Õ€;  g PUÝ9ó†¼yšpA þÛ‡ëE4\|ž,ü2tßsý$Á1|7û¿Ï1ÁE é‘’@ØY%PV˜`Ç“>39©k¯²“.”åBQV”l4€g,°XË΢§å‰É±ÐmIv¯4Á÷üÞç»…•{ö„tŒo ‹MXÑ?fÆ<—•3`´ã±Å38:£gîé¶òôõPê¬:+Ùq7Û¶%%¸GZ­Öï:Njº¿g­Ze0á8¨6”‚Sx|‚ÚOÇÃþXgÄêI1ã;µð×S¹r;§}3T¸JþÐ~„EŠ'î_€îû éG÷Χ”¤81©ðß˶±‹Ã)ÀñpoYòå^Šc¿ó‘ÙCßÁ&Îme=v¦L”ý±ÅÁ[÷С§Iºæ˜øh±Z„‹ @osÎ;‹'îiçk:²-êŸßŸæ®ÎÉLï–|Ï‚± täY½¹&Ð~bUäÒ D'¾ú;˜³}Ò¨Ïÿž¯ë8Fà*†zDÔËÛ˜ñ&½‘%é"Oš^‘/Ïò{ÊlDÔÄè†'ŽÿJþ-øtýžRJ‰ÌŒ“7¸Í™8‚<÷æyÔ5ž5|¿pžËªÎXLIøG^PjMœ·[wA±ØÖo¾ö½¾Û¶qä¥6”‚\Òú `àÍ » 0hìg%|èÞ[³3ÈŸ¢yñFò‡/?ß~”^Yº‰ÙPŠhÁÕ¹‚ΰù•Ý{oÍ.¡¿1Ù©L™§ú…ž¥Î›»÷©ËòÈ6€²¢Ìöºob‹ñ>Å®½rÁ”‘”½½j½µr§à‡ø¶kà­³J‡÷:”M11x§¢ƒ¶ t–Nd>%¥üàñ¿ÿ‰3LIÉ|ü£‡ô¡|v}U(ê/»W¦š¼Š'›èUæ…‡;(þ¹t3Õ1'üm3óibã<¤í‰ãÿKæš¿RU#ØpfòH :OÇÅIõÇg ( ¸Ïª ¿ÛKI¨³è",d÷¤FÓÐÞ)BŸÐ+ô +s0º¸k(»Éd¢Ü‘Puï]#‘õËwVœú¥ÆŠw ïTW¶¹ÉÉóRS~|º¶f¸ë“8ƒÁ@bÒñü’kxtjÓN&ÇÇ6+Ф ÅÁ ¦•ÌêÑDÚ»q»œA´IW)÷[ãøŸ=a85°¥ýßlHèÕ#YXÕáÖâé¸LÓkaŠpÓévÖ»?ò ¦4¦Úð­,è":w·Ûâo¬ÚŠÿ"*bŸÐv M¯1Ôß͉¼â.¡*³©—ž'—ž¯iAHûúèÑ1f+=Âì'" ³Frw»ô½8æL€mç}/nW—( x¯| £¢¢Xˆç‰‹ 2 d2›(ÃRKQÔ@WÊIËAılE> g§ñ„{ÿ4%xžA5Ö˜cLi¡ÑôH¼“D ¬KèèWº t~)[ÏÑSœ•DѺ :YQ­êÞƒ [Ô?;Àö‹3³åuÛ¥m GÚ‹é©_‰ÐýäŠÑ”Pâáäá@bÑsÆÉ¬Z¬1:$¥Gr<í/à‘·F¹Äí¿}¸¡@.UÔˆØb‡ÿ´Åñ;»²Lc¦¥÷™9çÍÛé©{ç‰hªîŽË4ý±†{bt÷òŠVÜ-§sÓ€ûÁ>;^Óiózg“NëÅlÚÞöQV¼²«¯¥¤z{ÀsDÄšÛ×ÝÝö²Ñœ^cáM‘£š·è ×ëöÔ×eÕ×eà¸U§5œOI{â¡ù‹w½Î—}+ÙjÈj;ϰû”É`Xýú GLø¢cÁ£ØxC‰Ò@G5`_:v/a 8ûÿ&ÄÇ3ø6ò$B™Íþà¡ÙÕS¬¡žÊM:*,¹(€&FQøú$f7¿Ž–%”î‡e«Š-u5ÜYs^c3ôꛢ§ä¤DJLL ø„x¡OèúÃL0Z¾Zk½¹ ÄG2_{…ŠTÝ;š¨kýó\KJ‹4 ¡øøQï]Ùö=‚ ^ª·X§–s¶%h]ˆÉ]¹†r'ÃÞ‚Íhwô×ï..)àñÇï&ß®cîøë¯Îckyp9qæø¿iÚhŠçÉ–Eç.ÓpNë(ÇHMŠ´‡àGGâ鸻2ùrLaʈc JÖ¯\|I+”îQF¸Àê ð’œ,¬àdÐk°Z¿åCzÛ¢£(‰Û€™Ûê?¤Û»E\,ÜM÷ 5øoÚÐ…”Ô#WªÅ㺩 Ø;¯ÁÐ Ç›VXXðw¶a·1=îE“ù_O<µä†ÈÈÿ‘ÇåÏa_+‘☱ÅߘLEX܉+¿¿¼î"X¸£ÃÁ¡œeÔ Þ„¥¶Þ(¬á‘~`ñÄñ+³¥`iàQ+ âq½§ã8ç[‚š¥ptac7©säŒi(ŒµÒT&tßøïçžÔ4ÝïÒª+m×ÚCýÊ/7Ú–š.ìÎ[Ü ¡Üs§°h%©=hUîU™Öä”îþÞÓ?}ã¿ù3ëÆÈ Û"•5¼;·=»Úz[4Ùâí^NøXÀoî`F©g ¨ð gŽe;³_Û€8>œoè+Bßy9`ÝØ-Ÿì÷Ën'°| ÷‡õ;8'_º¶îØæÝJ%TC5å'„:*N}FÉ)™—ÜÂP,Îc(ž‘Ÿb­¶ýšl懹Žï¨ÅfÛ¾¶²æG?9uêÏàæhËüžã]Ñ7vl &7EðxÈ|;_¦wâ‰ãß|;ßçé¸ó5íݶaºHý’"Åèb,@¿X ïpÀƒ·vñã {ðGÏ~—Á÷ÿŒ8ƶàÀN]”8R‰' pç„îÙ²6byÞxÛ‘^}q×w~ñŸ?ý&~Âhk¸rIñ¤&Jÿr:»ÀòÝì¬ÚñJ¬7íRB’MëÙëùþý‡n;}úø¾Q¾¼°„+QðY°†ImPèáË·‘'bÂÿ~â°€ƒŠ\‚pŸ3¡±ð(?,àÒjà‚Ž @w”°ŠÛOBÉòí¬þîÙ2x²ß/éÀšï“Õï¾ TvôÊ›ý[³m‚]ÝlÏ>«+^úñcõ㯸M½ÒÎñgàñÁ»÷~ÔX—-¾¯f‹¹¾Ád·¢YímíɽÒ8Smc &â@LãB&Wã³wÉÊ9xà3“¼ão0š¸e­u*4ê(d¥ém ÙG»‚ã…ÈÌø„U§ëŸoåvâ[CosíÖ½9yFÏÉ×_ÿð鿞Sð–n(!ýòú¦u—¿5€2À†œh?p¼yâ|Û'jÂÒ]8žÕ®»<€ ˜0|/¶˜KÝáÚPùÝ¥ ¤§M¦ø¨_ÐÁµ?´W[í*=ü‚°„ËzÄ;éŠ6P4q옢¥KþÎïÚDGy`|Ñ襔HýSéÛ¶Á|ïQL †òŠÊÚ\v¸œcä ÈïûÂ3ˆ©Jáò¨Uû ç L=c@Gë5|EU­Íb6ÃO?,Dðà«FôÆ#ù#þD³¨ —« ø„C…)épJ<ë]LÆ”2Û&}íXöêîn¢ùaØ€»¬¼„!ý¾-ßÝ €C/rnä¤Ly,œšGwkIy71½`9ÛükQUg–Ñ¥£Ù4h·Å>ô‘uu Û@É”)±FcýO¬Û¸§Û„o4íPD„öЀí{¶ˆB¹ÿÀ-–úºÚËÕõ­Ž¹ëãØUJнŽ[ÆÈÍΠfñ8v±Fb’ÊrZ{ ”‘ä=<êuÂ1?xCÔ“S¡¹ÙÔ@C³üáVjaý^üÐù—ª'¨•t\¬Gé {\3á„ +>ÙÈ)J?pi ïx&*¥Ö€gà-÷ʼnnðº"õ"÷Ãy-Ÿµ;´a¿I–º“Trð-Q¥g÷¿Hi½†Qï!7‹ýÎl…ÆÎ1ëÿÆ/Ý §öÅߺ_"Ýo´í»ð»ïIà›/°–ÛÛgà ;œ8K“Få4»õŠQø+;ÇÈ#\)LMXt©ŠŠÏ”ª \¬5× LÂÕ#ÚL™q:ö¡·OÄö6ê‚Oµ‹gNã.ë«Y݄ڎàÁUc€Ç%&^Ç6ð|WñB³4¬G8òÙ2úôÁO€h™ ¼HjB¼ÈJ”¢ Æ’¸J4$4îm`Ô¬_‘¡ú ].Ù ê㻥$$÷£Ô^:¥~þwàÀô›Ò’f³Xïi–¡¦­Ó"¢´}ûñfÇ[îH0‡5FE-vl91qö¼Ê‡Š’€7û£>[‹Aýj9.‡V/çH\s=U”×óhXËŒÃýˆÆó%y`€Ò™žA˜ôd&$bp]0þ¶•­ûôì7ç: Y5*\U³è²á²›Ùñ[Í€'œõi‹ŠÖ÷áD1Û¤¨áÐw7üYä§V¢4 4 4à hZå_ÿm}ïª);.&fîþôAšrÇ'Ì>À9xNâí჈{†Ñm:?‰ ùo9ƒäæìÚûÏw¶8ƒï€ß¼†‹¢ùô‰‚•ÌÞrGÁé 4¤s!î´[Á#¨µX±± ”ÞHEªbt,êàà‰3Úåóç0Ô"ë)ä¿Ý €ãm  ŒÔtZ¯„ºZl+ñ“ê´ˆ¨ÈLN}àhûJǬ%JJJÑ@dt_øm~÷&2Ö]&SCíúä^š¼øcŠŠIéHÒnï–”ÔëÛY=Ö7*j² ¥k²Q¿c‹‰ÿö Í›KÝÞèþ ´¦Ø|ctÔ¸iÙ'«ú<÷­[“ž¾ÿ&]tTó Óá˜ëUܾ¯b…4þ‘zéH,0¦pX™þ¤|ýÇ€£ŽPW²Þs ½•àÁSgø Á¡Ó´½ÅÜô“žpÔ1@¦œ ©bØÛ¿lûXC7žb€çTƒ`;Á„K«Ù\µûæw|±z=×ꋳ<äLç 2iúÕ²‚…Iq _¹„É#©ÇP HÀDDtúü:R|ŽÊªj©²¶Nðw‹ñ¦Æ_q¬¶<%!ŽÒ’ã)7§e÷îណZ L)FÎø%ÕW–8è ®ù/Še?ñŽÒ–ÇÄýé̹÷¸ÔþvA@zð‡8¬­p\w¸*îÛ²a÷ñ½» ¦Ì»~¦Åb¾–AbƒJ[b¬Þ–’¯ÅDE5Ö:ßå$®N×}§KÃn ÛY\÷q®Ád²!ê(‚ì°n4«ÅRuúı՛—¶´¶¶ê2_RÉ "—¢NP7¨#×='Ùù¢xçëƒf-.–’ÿú2íÚOÖÒKdãᣀ ÅcsbïXHU?xŽÌ‡Žù+»éÝ_…Pé( „ª$ð61ßþ¡¢³ôÁ绨‚÷°ì,Z0u Гbõ êÞÒŽ»„>þr7­çNÊWçO&vgCóá¨Î¦'ôƒ­àòÛà Ìñ¡‹åEüÀžÃ¼àC°Ž"ï±ï©¿ž4ÐØE–lè\úßC¿ÚÒå–oñ°³~ó3 F¬•‡ðòò`K-[¡~ò=ªúîO9ŒðYŸž{™–|¹^|ÿ ºeæXš31W€ðp´†»£'KÊ„[þãWzB7*÷õ¡´²(Ê}€C|ìÀ1A.*0bJ7¾Yí©[€o¸•@çÐ/87@x]ãÝã<ê"lDYÀæ*„':¤¼ò<Õ½üoŠýÊ-¤õH'ÓÆmT÷Ú;ÜÏ´PâÏž"X°ýßܤÍTqÿwHãCÜÃwSô”‰dcK–á“Tÿþ§"Ѹ' ë‰Ó¤õÊ ˜ù³¨þ÷)æ–ùâ\ò‹¿%ÚõTÿîÇÿ݇)*¿"V2nÛEõ¯¼å°°ëç\K1wÜLº´²ž¤ê_>O¶ªÂñدòÄœ¤$2ïÞOµ/¼J¶j¼wM¢±ÿ¤­®Žj~þ{Jú¿ß5P[JJ]¢ ¾Müûa`ð½vÇaZ¹õfkîâ9ãÈ™ª­K 䙢sòØâôÞš]ôÑÚÝ‚‡iΤðá®ô„eg·úžÐU.!Ÿó6@ ¡`GáµsL XÀ%—k>¤Ä SˆÔ­àp/‘€p€qìã¸ôû–÷ò¡ÐÀC¿Ý>®gÅ?ñ ªg‰p‰ô>2- ãú­d\·‰"G §ºWßb÷Ìqàñ4Öðç®ýˤ¥&Sü““aÓ²ž¿Hºädоûv²•—Sý›’éÀaÒ2Ò)vÑ TóûÉz¥\yKA1–­!-2Šâü$YŽ‘aõ:Š1”â¿÷¾ÜLÆn¥È>Y|ëÒRÅñz.‡‰Ýa⽟ô fQûŸ4{&ë…KTóë?“®—_,àÍÒV;JJí×ÜN¾8Ôöþã§iÕ¶£|ßµ`bûë¦w “"ôµœhɺ=Ô3-‰ò†ôLᨒ΢'ô“îô A"9!,ßÎà[úŠËÑU>­¤ @¯ržA8ô Ð5œ +Ë7?À¥&Âpmܲƒ–ð¯;‹~î Š7šŒk7’¹è”8Àl)±» DMËVë=d>sžˆ×_=‘­àKŵO¢ª|ð9²ñD"ˆõÜ~}ld>xTìãOÃGË@úIºž™d-.¡(Î଄vt ‘ú• \q¹/ÏÛ¯³¿ €‡Y…:?¶[ei19s#ìË•7BXªõ×Nv\ ë×˱m>|̾6àZÿøƒ" kYéÒÓÉVcw%‰`ˉ“|;ÝBQcGQß—ü§_4îF<ÁM­¶”BCp=Ó |¾ë¹3¾qOUÕèkó')·«–ð[¦¡¿}¸žÖî9!î'ßéõù†¼‘[vŽÎûûáq£¿ybüøQlÙS– +\¥VpX¿y’vwî‹Ï^¢Ú£`õ«í¢‡CJUMƒÐ+F ïpIO×C<¢©¡‚v}r/aB‚ ’[¬¥Û„Z{§ gÝ…oco¥A+ÞŠrÂõØG líŽ{äJ}“']ÆÆR-3¦X ‹)ñ?¢´O^§¤ÿù)ƒåt¯U`ølOÖL¢Ô÷_¡Xž°)üÁïÆ¤OÃÒUwß”ò¯?SüC_g[ŽLÛv f–Ø»Sꇯþö®¾ŠbëŸÝÛÒ$) MH! EÅú|úì{GE@£4)Ïö‰(X±Ë)JU)ÒCHè- „ÒÛm»ß9“ìÍ%$¤Ý›ÜÜ;óûíÝÝÙ™33ÿÙ¹ûß³g΀߷ƒ¦l½ËlJBÔ-…iEø·J‚O@2îOës>5.úçô¸è±éc:5E6ÏËpF*¸'_™ùIpZd‡ü|·– Ç/}©§k_½|ËÞT8™Ý2ÇG“V5£·*g&àt¿(î Eu…idIþ ÷„²¤Ìj-w¯'G qp”Æáæp¹ÈtÄb>‚ZèÜ‘÷_RÇ¢·ç]r^²à(ýê'§,¢Sôö™ö[ðD·…èû¾RS<ýýKòÒ‰~ÍßlS.˜e¦$äÞð²1QNÉ‚¯¡dá· z{‚TP%»üçå@›èç~Ò&·ˆµòÈR½]µ¥­)¾L£¹P$IY¤f¿Ô ^–émã^|%¿ÊeH‹>Ž×ãêz/7Ï¿·nÅJóÀp]*Ü!Ÿß¨Gï'´Ê¥NζÅ";Y9ùðÑÏ3pÕjÚøxÁ¸®Û-̦€;u~\›o õå&›÷‡þ½Â¡sý4o iÅЋÅŒ€Þ"NjwæÐÊÜ:sW𶵜€·èŽR¤B¼/©Ùƒã„ÍÆ†ËÈ·µ ÔêHù5Ë®-Þ:{SK´ºóϦŸœˆD|JÇàxo¯Þ:QZ§AøŽp‰O¹ N é‚ߟ*(+Ö#!ÿ[ååZ¸2lkrvSëÂósZ’„^p ×ë P‚›./oËpÿÍýÀÃ] ‰‡N¢k¾èbÓå믎 …«Æµ«‘|Û²•åx””1œ oWÌ=áà7áÈ–wXs³,ïŽuÍË®Ð|ÞFF€pî|Wmúyt`ü|zF ¶3ne¸™ÓââpiOi˜,KÃp5ºëðó¯‡>:$ä#ÐÚo„±LZ(Ȱ\¥–uÚ™|È*?ä8%d¡˜ ˜ð%¼ xzÚöÒÖß Bý ¸/ì=v¤f1-øìÅkáÁ‘×À*tyx± ^äæºHúésyàë厓»CŸ®`Ñoÿ@Çöp˜ς!'·¾\¹•­ÎYŠ6ë+6ï…‰ÿŽþ¶EHCs”ßÐwI™â{tbd_1ý0¢«Å•›÷Áô,Pã„ÈA}¢àúØ.LæoR 8Ðò Kaç“ðÈèÐ9¸éZs®Rl0˜,&(Tg\“hõÓ9f,”ä§BæXljâ‡à !]q‘68NŠ€sßj}F*ÜÞñ`/*1¶¸>båD&%íLJ~/jÏÞ]üU¢0Da&š¡$[W„uŽ,÷Ã)R3€L‹ë³ ù¤×Dw¶NÇ9Ά€b‚BöÉä\1Q³G;OŸ¯˜Œ§ÓÒš'Àˆî×+¶»›F ìf4Ïø ÏËõF¸ë†XCòûÃÚ]@¦,ð8ép†¥ZûÓ²Ð²Í aíÀ€&nDš•?Ø_ÿN-ºa½cH ä”2R¯dÜyð$9yî½)†õï«¶ù¯p­Z„œÖï< G3ÎÁMý»A¿·’­É{åE‡ðv¥@î Ûv¼ÖÒä¾yg-çü€#àlØV}álè4o{”g˜es©^¥¦sî™ÃF} WidÔÜ‘¶»z°à®\–,!Ÿ‹+·É¨Óm²$ÜŽ¾R®GâQÁ 0*¨Ps._m6ÀÌÔØèm¢(ÿ(i=‰Ú¶-/óÀp ˆVlø¹=tH4IÐ-[·ózX1B&jµCƒüp‚g•'&ÒjÿgDV*i®s K`ü×Bd‡@¦ù>˜– ‰H¼÷‰„¿ÀÉ3™VzºJìÓ¥síg]åü¢RF¨Ç¡Œ(”Ñ%,ˆ¬+áȉ3Ð=¼=ú{²-(Àö§f3ÛtJ£GMõ«]oSV6Úþ[*0w 8µ»»'dÝÆ8 E€ð†"Ö é%£9§ØÍƒpb]ìæ.óóò#µã§0ßÇ´í'‹#ðá8ߎnŽ•©Š<•V}é¨_# °¨³—ßÂÆµÏ,mL…xŽ@  B¤…èmÔNnòt¡}]oèäÏZIåRè†dX …%åì0¤­¯!¾@¤º­ŸtlçÏÌWh’èÙ‹p÷±–tÊÁ©³¡•2h¤¿¯'»Löôì q6ö"WÂùü 8“É ™ŒØ:Pk¬m-ÛÑå)î ·ý2 ¥˜[BrOxÍ=ËAãæçèÕçõã4Ûÿ{4¨xž¸d£^Ÿ]êå%”k4€¾«kH£‚áXªÓ e¥%0{¾Uî"†¥ ߘB߯iµ¢ÏîYr1çv<¾…ǧ&ÓŒãÓÜ+Œ2Ë0*½(ÿLjßè¯Ô¢öóλvÀx8­…*¤ØÖ ~MOf^“\ÕÂ4•D9 q'ÔŒS¸WŒ‹ÙTÌ¥Ž¾* ¶ïK_oÔ^Wòê2I£M!MRBÑ´…4ûÅ•Ä^…ÞGÔ¸ èÁL^ªç¥óš¼¨Ô”®1qö·1uiî<î ¿„KïÉT޶áî ãÇ|ßÜUáåq슷·+¼ ÎÈá¹Ó§¶àÒ`<Ã厄gæñ£)x• +ΕûË3Ô3¦ýºu%‘I)?àv«›J "z  ªÙ•aû6>à塃’3Íù?)©ÌV\©y`I>š i™9ÌæüìÅB‹ ¸’†ï타o»hè3ü#‹ðܬ°ÿ¯‰–s~Àp8wœ^´Â¿/Ý/™Œ…†YˆœãT³õÕ„p4ôû·§bí•UÊ,xÛ¢Evíºµ{ïg‘{R®W©u‘4m7Ï(²±0Éø0I–I+ÊKG"þ ™³(×ùž#àÈ8šF–´ÏÝ>'NÃÜoךíàÆønÐ#¼‚€“éI×°vlR&­0Y[¸=¥Hφw¿YYçó€–„W­ƒ¯fÚøEË·Âó—Á‚_7!Q¯i‰’Ãö{GÃÝö-¬]¢âžPIAî ËW(§|ÏhõpMœcu!igA¼˜“ó£úL¦[è˜G–<4Â/½m;!ó葵˜Ÿl±iSH¸]Ü TššLFM÷['‹óG¡;ßqXæ-ø0­XùC†ŽhA; á­Ô¸>_iAýaXRR¦á#à°¬Úl[Mšp9ûÙ;kJ“kºÜÖ^¸ÿ&(ÇÉd‹M«HZ‡Çnh}ÊŽ£»vڔЯGgèÛ­qRiu{nOw<|ëfšR޾Ï=Üt–2u"Â.{g»oEB™{¼ãy¬þ øïáiòiEMàUåÔŠ×€× M³_P4²DM›—/ýQ6.®ë+Uü=©1½A¸~’^Ÿ¿uÕŠu(ƒ¼›Ð¦póÆˆ¯3M¾ ß²œLTtj]gAÞF’pÎ*£«j°/sÿêDMåMqšLÀ§ ôïÐîÉY³*VMpàìÔL…€3 8–A¤±xÍOßþœ²móìsZ·Â®K⯕†tòoͪ´ƒp!|r´Ú¢]¯[ø÷2F¾ Ã"±r³Ö€·(Wú.<)éHTRÊÃZ­Ø Iø×HÆÉ ‰œ÷vƒd–6¥ÅöYq".®›ßÒ{6ÞqÜc=2Zº.¼|ŽGÀ5(Su†Äü[²Ñ„ýwkT0`ÂÈ¢]£õ¼•Ά@“g÷©DÕZ“dš!•J£œïœ  h½Ý#{eÒØËV%nX¿åЮGŽ5Ú9 ={ø’邇^/{•— :³©šÌåäA¯RË´b(-²ƒÿÆ‚I¯/Ê8¸oóöµ«6”••(¤»a L%O•ðmvó,óŠ¡ãö=©˜àÑŒøøi&³a²,¡m8£Ø¾Ñh#>mÄÉ:„–^êžÆ;¾ˆjÿWl¿ÈhfÎã‚<äEDYhǺø‚â2ØËÕ¸:½šh­/ñãV‚À‰ÒEžy«^.O *«Tòý¿ÏÓn}Mz³•4W“#Àh2_0mRÒ¸)ÓNãÞ(‘ð¦ßXôfO¦¤§/于‘ë’’Biýÿ~üÏÿè×5¬ËU½Ü=¼ÚjÝÝ|ÕjŸ:.EÂM&¤ÜçÏ”çf;räØþ䓈 ™—–›È6‘p"ß´z%“ù áÚâæ'X‡ZC§ÄÄt¼øøÉ~ý¦›Í†7±_›‚&jªqÿ$èKþ“ý®Fçþ~ÇíÛ©MÍØxà4ÿf/œè2¬Þv6âb;´¨îôóò€ëbº@|ÏÎ,®¦Ÿõ»1×޼æ²ËùHÀ×í8WG…r~:­'âõO¥_¿›"´ñ÷†»©Ö¨ŸúÇ\Õ‘QÍ?´žVðšº:M&à @YømVçN˜<ýºÏfLÙìê Ú ýÖZp…T[sý‘”¤½¸Á²˜†÷DÖi£ ä©8sÎ_ƒaE‘j"ߤá&Rª˜œæ›È·Ck¿±~—…Jâ¦÷‹ùLò<² g‰dðF?â3ô†Ò'Ócû¼±g/½”5[ qŽ Ý‚·ÙÄf+”ä²øz¹Ãc· ‚â2=$<+þÙ½p•J÷Z4ØÿÖ·…gw¸lW5kßùX=ãëWLAhx,ÈÒ—«ç©OxÅ´¥Y+ ã4›ðPMèÇÙ¦ÓÏâ‚#ÿ7>á³ &”6²><[D*‰PR ²©œÁ$ ¯;nn¸§~$M9pW ߨL\ˆ|“Y‰¢ý&N„›H8Ý‹´ntplU!bWò^¬ð°Ô¾1#P >· ¸2tÀÆüœÛçQJût¥æÜ®mÃñí!ÏÿÚ`ž Q‡~l׸pŽ" E eã)èP žrì4œÁeá#pÕÊß6¤@p ä–ÂÎ'á‘Ñ ùH&H’wÝËò¬ßy¶ïO7­ú÷ gqÊOöùذû(?uhuM•Z„\à‡ÚÉÉ-‚_7ì3˜¦Ÿ'ŒÚ(Yù¾…ÈÏ7šÿo…nÜ‹w–áÿQw´:Òá§ekÞÓ]sËKz2çã#àÐ(Ó&U2!áQ"„ãpë Æó_ãçiW"MÂî ™r©J"“dNq·ó¸åàF‹ºÐ¾¦Ò8ëVS{­ñ vN„áF÷§B¾Í9Fµ®µ;yudD—>"ãñsÖªö·˜$ã4KyCŽ‹£2»6®q|£ðž¸«÷v)‹ å(Œf8ˆËÅ'< «¶€Aþ"\TZD°fœƒ›úwƒ ´I 8Ò–S8s!þÜuzF„À°þÝa÷¡“,^ùY‹KØkÔ¿þèèÒ©‹~`D?¶ÿõï=à¦ÓÀãcã €/,ÛHÞLyp$þÚ¥/>~JsÖ‰-p†îm$£á5ïùð7%Gê(^—° 'É §O]'ˆð ~&¿g”?“¦¬ÆydC ²¨˜V‰$M.™SM3LZ£^!žôD¤Œ6ëc%ΙöÖí£cÚƒpQl¾ /…|;´Ý7Ö³^AX²Ä±'e‘ŸÚí*üÖñø¦Ë4úHŽÝÉ,%M6§ØÃm!g×4¾iœÓx¯W…y"§A=ô ƒek¾f•”ëajºW¢éIÆ™\è׫3Ðê•JÐãRôOÞ}= îu™]wjæyfªr× 1sUG¥dcû´¬ ‰štZM3"¤ ”—™Æ½´Ügs¡Ïpðr×B\÷N@Úò¼ú°fÿ àlÿ’Z /~bÈ4ƒz þ²ù0øÐì*™Š–ü¥'ŸeÛú»×©[`¡…Ó¦|0~êt%˜'˜rº¢­èsÜ&\A§I{"YDÆiO¦4‘P1;!Óe¢¦òBUõt‹N 6D°•0RŽ•ë”ÖiB›; ±1Ï¥õ‹YŒöáŸâíWÑ8¹‡l–7¢ÿðÏ<Û´{¥ýºuMf ÌæÍNP~OA^¤qî4@ò†Ô "„ÕZ†€ÞHC;ÁßÛ^{øf´¼’áЉ³°øíà¡Ó²‰”Trçà6µ.%êl„úY*juL‘]:ÁÖ}iìÅbǃD›BÚéó¬¼_Ö'±súqÇ2Ïä‚¿¯§%ÎÖô2¡ES%Ô„»rï«“îûD@IDAT¸í5câêyªÿÈ’ô?¼MÜ®Ï8yx¦x¸*?â86%àÔ4F§L;„‡‹P#·iü”ékðOä{ÑCücÁ¤IyŽÕüVU"œD*N&D¸i£§£²á!;¦½3…€Ó^ÙeS✈ܕ¼[¾çžþié©OcÓñí¼±áä%÷‰â 97ìóPç]ÉÛ ùù®p5(?@.‘B#Nãšï†bé¬éÝ(Ѧæ DF»¡™yC9…ÚiòdBl·k äŠ0é(ݺÜZ‡¡ñWÁâßwi¯‹é Ý:·g—u¨§0ÍOBƒª<‹´ãOaI9„ùÚ±ç=âóÒ?抯à›Ó©•øÂöЪ9âÉ‘¯Jo9o«yËZ36'à}žNHøêª,cÖ3øÉúyY’¿•K$iüäéçdAÎF¦Hv¹<ØÄ×" ñ¾\Ue¹ê\ø1ÜÒpÔÌ:WãÐæ ÷ªÞàWVrà–}IQas+²ËQh>»õwÜyêïî}2L¢hÁ«6ñ˜À‹V¶5—˜Ú¡ ÍÍá4òù‰4á’Û|׆šëÄ V67œ°x¦° Êô¦¶ fœPIæå#ì=– dŠvàõ ‘™ 8Ù~‡£©É.Ôr[‡­)iLƒN¦&Dì2Ö¾ xºëà¯ÄÃ0zpoðôpƒôì ìÀ:¿- Çâ²rðñbÛR¶«È5QzoÕl±>iN‘ð7Ñ=áQîžÐUî€ÖÕN»p‚ òa=ç=9uVœY2ߌÇp ÁÍ7l„€5ù´&¥6ÏÅ´òÝ= ?õ¿îPߌԠkè¢1™Ôø-VˆÎHëv!'`ut¿#Y¾þd¥p/&á{\-²Ãý|_ *׺¦oüAlÚxé ýb9yŽÙWÛ ÒZøÓ_ V‰èù$î Ñ];Ô«¸NÁLS¾ä¯=Œ`ßߎ¢Ç%£w•ÕÛ¢iË ¢O¤û¡‘ý¡sH[xpDøNÄ|wñZ–¼­Ÿ\õŸáHޕܶÝŽøÒË&˜*XÛ¶׿ÞﺧÊvmêŒP£s÷„®Ñï­±•v#àÖ`T>Ä«Œé¬/òcŽGÀ¦,DiYññÑ8à+|ÝHÂJмØúWoA_ŒLJ^`Ó¹0§G@!„D>UH„E.Šã©A{e’Ñă&8Ú#ŒØ h«-»ÀêÁ:Ž\þ‰4Ùªk°Î4ysH\W–…H÷–½i0éá[ÀÏǃ¥ùnÕNØu(ƒpÒ˜O|p8Ó¼SrchÏ|4'‘j ¸7#ù æö,ÓeºÑ´ê£€»…²¼møÿ×_j¸{Bgìh'hÙóÀà8¡‰‰™I)Ãð!þnŠá«N–¥Op9ûïÎn¿™dN†%oN¤õV!‰¥Mv×jµÚy¨˜ ÀhCíÈA§Q_â9E©«„òKʘw“ÔÌÈ—[¨SéFanö¹‰8wOØ8(y.;"ÀG¸Áå¢9-‰o92)åC¤M±h5`qbŒv‘_ÌÙ™Þ·ïU-Y?^vëA@ÑÆVo5hµZÜ4ÐÁß t*~Û˜Âl³[O‹z„³‰—í:_¬ÜŠÞPÒaÄ žÌas¶ƒlÚ—mJAÏ.jèÌ^lg󿬋3•uã+å*ú6üïãî ©c¨-œ€;Qgò¦pjB <)éHdPÈ|¢“[®Š€«iJ²1—²¿W‰â{ŽÀ• ó 5C"Þ:tɧCîÞœC÷|KþÜ}¥ìyÌQ¿}¼‚¶ÝãÑã MÆlî°äÏ$įúF‡‡ŽáK8[û:oî:9Ky·¼dÜ…ŽÂDÓ)T‚Ó¤L‹{Bgi"oG+F€ðVÜy¼êú" ¬^­JJ$ü‹I º,DŸ?£IÊèʰv_nõ-„§sj"àn:¸¹»;nA>îÐÞC†}©Yðýê­NÞRFšïïWïBÜNC÷ôÆÀp%|IÎMPlÓ3£^5ÿ* Â+Š4rOøÇl1A9ç{Ž@K!À xK!ÏËå´H¿ÁitýÑ“Åq¥x| =Ÿžv|åùAƒ¸w"¾¿ 2‰ b¨Ñhñöôô/Ü<=Ý¡ƒŸÚ¹™ Ÿ¿d8ºMøekæÂgþ/`ÚièÚήî„8z0\ _Å¥™«å´Å‘{BüÏ«úò[èžð~§m0oX«@€ðVÑM¼’Û!™”´ßO£ë‹-–*RÑoîˆÂ²’-ä=E‰ã{Ž€5\Dw~¨GÓ/OOðôòoo/vŠ$¼£‡ ró áÓ¥›áË[ÑCJ&ón-ÇUÉÏ7áA¸>y…ÅpuˆôìÈp$< W—<ÍÞ<ØrOˆˆ®W$’{ÂUsÔƒ”s¾ç47Í↰¹ÅËãp®Œ@åRöw¥Çõy×qšJ©‘„÷.—Œ;SccGGíÙãªnCÉV·ªEž®Œ¤k]%³ò~Böߤ±5`6›ABÚèºwi9ä”JžyŽyH!é…ÄÒÇËó¹Þ#‡–—/D_æÅåzæç[‹“V;ø¨ "Ðüý½ÁÇ7|‰!< W—›ŸÔ9®*Ç)³íföÝuå ÷„ë?ó¿Ç˜Ÿ¿30÷„˜—¯š£ë?òU}Z]ùùuŽ€­p½C[#ÈåqZ1I{ßLïs ÔçØ -𣠂´ùDßèûÃw§,oÅMkLÕ-r³l.-7霫!­´hÁ‘(J8£È7îX¨ ç´š$MÒ,‡öè{»Xo†b£€vᥗ[Ši]Nò&¨Áf¹ àï®/7f¶C_ˆ|ûùú²¯dOOÞe¸öÛꆫvˆ«¡Ê&“Iq«j}Õ2v­#«›Wð×<·QåfýN¼Ö3µdê5ïù ¸å¥ÂÜêéù9GÀžpnOt¹lŽ@+@ bwò·éñ1²$ý†d*I¸jÅ—¦ÅÅ<㪋öHFsN~A 'à5Ü¿ ÑÖÉZ=+xi¹Én™Ì't8A³ —T×ëõà$¼T¡!—𦢀÷W R+J1!O&„—J$Ûyür€ØÑf¶óh¾Cšo"âUÚo>ùòJwB~a‰lÔë󮔦®käžpÍ{šÛÌ&ÓFÔŸ»ãÝØU2-=øKÏá=ï=h¨+?¿Î°œ€Û I.‡#ЊˆHLޜ֯ß0þ@Š…%pÑžÔØh¯¨=)s[qÓSuzÈg•é…R4ð@ó .E€H%J D6+Î+Ý"^åHÀË‘€‘€£Æ5å3Sqò­ ¥àBm2+!¼ï1D¸ÉæÛÃÃmdzB× GjF€Æ"ɲÒZõ‰Þâ”­æ Wˆ%÷„ÌQ¡{Bi ¾ ¸]Ÿqò0MÒ|ø Ùø%Ž€MàܦpraÖ‹@ä®]ÇN÷ëw ~žým•k|ËsÒúF{FîNIh½-kPÍÙCýÜéS[|Ú´y~?ºÖëß+¢A\!±B.‰4²c$átLþÁ=ÜÝùÖã¤CƒÁ€5à•vâ wf%8}   `$â—Z9”ÌK"ᤧ —D¼)ž®ù¦<<ÔŒE¼„ÌãGS0M:P8íÈ=áªÙâD32£lrOxbÔkRBƒ…ñ F À x#@ãY8Ί@‡]».æ 2¬°(>‘†R;eI~+5®WTÒ^‹/]'m¿å¾ñ÷¥û£zG_L<˜€œ³¢:œÈ¢â.È£â¢H&i¾ c¥öÛÌ4à8É—™Ÿ($¼‘NEذ §œ°!-8ùø¦"ݦ¯ðxÂ5ßuw=ŽE_ä ÷'nOÅÔæÊÍ2fë–pyŠ‘¯IÿE~Þ™ã*®2÷„ÇFM4ÿpyjð-œ€ÛO.#ÐêÚ¸±øÄ!#¥¢ü_‘,d ’áå´¸>ž»SžBbÑ(S+†4kìá~áLöá™ã§ÎB—°ö­¤úÍ_MEsËVÊ$’‰vàÌì=£Ð$Me¢¦â)ÅU8õD6öñDÂUø¢BDœ#1W°kþ^k]%Òs¾h×ßëþ½Œ‘o‡E¸±ñˆ{k ¸Í8µ‚Ü‚, {Š€fT­ž+¾¥œó=GÀVpp[!Éåpœ² ÏŽ‹»³L0­EÍÐ`|êÑ—õÏqÙú<'[¶ž>i“8±kÒ¶ipS%nX¿åЮGŽ5Ý¡ C¯ ¾ôuÀÛ]'ûùz nZpœŽG !”2­:K‹ì ÙÌfSÑÉ#‡6o_»jCYY‰Bº P&m…¸Ñ˜¤±IcÔ&æ'(ç’PÝ=!N(NX=GulÄ«æ/IÈO8M@€ð&€Ç³r\ ¤¤Ò´¸¸[A0mÀ´1øÀTI ü{]Ôž=IN‚‡µœ¾’ûF®KJ ¥õÿûñ<ÿ£[t\×°.Wõr÷ðj«uwóU«5:œuè’$œ^Æü åIfÏ”"­.Ë$ŠMþ<àn4¸™M~Ê}U¦Ö\(W«‰„µhИM&S[Q®h/« .«X¦Rå•«5Dy¨'&³I¯/++(+.ÎÍ8väȱýÉ'1+™—–›È6‘pÂ4¿ò˜ÌOûo«v ÕÝ¢OŸ¯VÍQŸâî í·K åÜ%»7š#Ðx"“’ Î\ssi‰¼ÌuAîÜceV||ÿÐÄÄÌÆKv¨œÖZp…T[sý‘”¤½¸ÁZ3 9+f}J‡j”½*3& àÚvžîá$ß Ëù_dŸ£—¤&ÍX½= `ðUžî÷)uÎ06|uîü¯ÊyK샵ZŸ;ÚܤVEX—Ÿk2Z‘—ûóÉrÃEëx~\' ¦ñF›2ÿ‚^Þˆh+&'ôÒEDÜîÚo,ƒîžPA‚ïí…'àöB–Ëå81Áÿ$ŸOë=J0Á4G @\n6þ~~РÁ[·ÒƒÒÒÄQ ¢ œ9 íœ;nn¸§ÿRÒ”w)òí†îLú{{Þ «t»·¿´|’ï ˆC£Ã­þÑ×ûxÝ«¸h6í^|!÷ úyfÖÛ¸¢f£e6ª"•™nòóëxG€ï‹ž¢ª—µœó&Óº…çÎv¼¬ÌY^8­›×ÇÊË”2¶Næ%Ô×ô’K$œötNñŠÝ·’£ìÈ=áš÷4·™M¦ø/àŽ+î ‡÷¼÷ Õ‰Ž@£à¼Q°ñLŽ!ž˜¼)½oÌ8\ñð:GMøÈ+—¿‡SéÜ =è‰X“"äDHómM¾[q"Þ´9}ÐjµÂP_Ÿ»´•ÍÍ2Ÿ3›3ÛðÑÁãÛµ}[¾.€I–³¾9›3eæ6Vfcó…xxhÞi?6D£‹2¨¯Y0ÉpjGqÑ;³Ng£Ë:šˆ+e£qfMÂiœÁ¥=mt­E^ìÉ=ásTèžPZ‚_üp²¨Å=áÃX'8B€ðFÁÆ3q8 »“§ö¾ $ù ŠCá“ÓãcwE$îY©¤q‚==øNZ8"ŠÖ›ˆ·B¾]Êüó°ÐíÔêHl?SôeAá'xبI’·µmë=18èC’iÓ‘ç¬Ï/¼C~~*ÉoÎðIdd¿^îº9jº(åÊ‚`Ê7™>™š}öƒ}-¢WêâD{S”ñ¥p…ˆ+çÊõŠÔ-ðKî WÍ'â>ŠGe¹'L1Q"…#À xƒ!ã8êDÞ:fjÚÊßâ…ÝŒOT$éÛ̱};nßÓìä©zÝlxNdAÑ '­nÅîÛZóíð>^^O* -“¥E¿f_£4IøhŽÄ†gBàDlì5h;”µ m£E7`v± m#Nà}É÷[–|‚ðydRÊ,˹Ð[˜ó\™Ùp¨‚|+… ô"qgTRÊN¾LøÞrO¨SéFaÜ9/C[ «þùÄ×ß:?æ\ NÀ¯„¿Æà4œ”ùj1É ’YúâD|¼å³¾Ï÷­ ÌlÂ-µÆßElKÎhhkNÆõ*ÉòçJ>¼gÖEzû5Ë×’ô~1}ÒW.Û²ô!–ïEuÀvàX*›é%°­"U÷y‚(N¤¹ JßsšŠwOØT]/¿ÍmÀÇOþ‚, «Ê<\Nøú…Ó§ŒølÚäï8ùv½›‹·¸õ"@ã•Æ-_Ç8¦ói\ÓønH«È8’ Jü𺩜ó}ëAàÔ ˜$²-²(Š3RûôÇ?Ä—0f;Nùðáódøž=k"£¾i3âã#Òb£×K’´¸ùþIvóìÆÉw}‘äéꋹ'Ôøù݃/x)Þë:ÌËVÍѱ•bë+‡§slJÀù–ä÷ñ\ ꠟ͘²Ùu ä-å8'4ŽMà×è2îý†’ðÈÄä?EAøÆ‚ŽÈqqlò%Ž8<Ærée¬¤®¢¢Â¶ˆÄäzÿ¿§ÇE¿‚÷ÎÓUfD$¥X< TÅ7íH2DÚ7úU“d<€_nnR¤á—Ø“ Š##÷ìý77RPá{[#ÀÝÚQç–g3Nf'2ÚxâݒϦOþׄ ¥Î oGÀu ñLãšÆ7sï i½V¥} <3ACbÔ%]6[‘±†Hâi[ô€fD–/‚3ê[4¹M™æ(éÑÕß‘IÉS•s[íÓâ£ãÓ ówƒ$ÏÆòÜI.–eÆùGïy¶ êµ;yµ­Êâr8µ!ÀÝÖ† ¯Ž€M8M¸DÁ‹A>šÀGðOÏ2żzüœ#Àh°qãkOŸXUŽûz5¦Ã®]‘½S•X~“‘ºª~äÀàŠ¦Ï㟺'U_ÂöFîN!3Ã:ÃÉ~1dYúóâûË»9"(øQ[>#r† ñJ‹‹þÌò|¹ëc©”{T ê™´÷åöëÖ•XâùGÀÎp÷„vØIÄÛ„€“·´w C÷eÏqÍ·“ܼ ñÏÒxg^ŽjHS[T„ šäí8]GBæo0”½Y[Zï8ÁÅ{V©‘¨g)ÇWÚgˆ2™åx¯‚†Vš?ªÕ¹ß!¬^­¿R¾†\K‹½µ°(ïj¼é=ÏÜ—¢ÉÓÄȈ®ý:'%íiˆ<ž–#`+È=!Þõ¯*òð}hÕ‘ÿç)€ð}ÅV“q@Wƒø§·†Û|7I.€#àð0›pïäb´!•’’Œ¢•<ø@z‚MìS"øÞ!(,Ì’^˜¨rôÕyÔmKêªèé~ýÚôÒ*e$ª¿sT*Ýœ”›[WÞú\'_âèâògœä¶_:ZåY#j 'Ú—Ï–,1[ÅóCŽ@³#@î qÌG8ð?ïmtOȽ@)€¸ø¾ÉpZdÿÑÏ·ð½‹cÉ›ÏpØxÇqÏÆZ¾Á—#Û^™EgÔËBÞ11©1@Ú)™’H’ðº"^àƒ¤¤Rå¼ú¾buSùcK¼ ,=æ%ËyN ‰öK‹ùdÓf¼CºWeÇù‚:6jwÊäðË«âùGÀñàî ¿š³†M&àÍYY^G€#àà„9·JkÐW4_˜GÃö'ûÆÜަ$=YU¡Pð‚ùµUëdß¾1¸ÊåÏHÚU”ûu—¨î¯k²fmòhá©H>Œ“Õ&à˾ã1¡…"ÏDÞ6fpÔîÝjËËã9ŽŽ¹'Ŀהzâ¸áî 0\lÏ ¸‹uxs4wÜä鎟<=±9ʲUã¦Îx WwlòD1[ÕÇä¨Ý=¿B²VÆÚ*Clzß>ý\¡Ý­¡èæd’RO´µ^¾1%_9·ÞgÅÇwDÛlš€ïEñ81÷„¬ó}%m¹u~ëc’•Ûg…$IKÐô¥½åš ,Óiå{Ræ7–Ô[dñŽ€ 0â5i.Ž++“.9»'t€Žiæ*pnGÀÇO™¾~Üäiòoθ¹¦bžŸ1£]ŸðæôÑ5]¯O\B‚ìp}ˆ¤*5W1õ©¿uš'¦ÎDxŒ›2m }·¾¦#¹_€ä>I9·Ù^†v(«Áu¶Yù.(¨Ó–-yhò“ÒtI†ÿ(Ç|ßr¤ÅÇ܄㽠!¡.ÔÚ÷kª Úgû–K†U˜–yIÁ›'‚zdÔ¶m95¥¯-NNHSãbž+3áÿ†Õ¡-¨TwE%¥ÜÑqûÞ¬ÚòóxŽ@kDÀÚ=!¾pâòáÜ=akìǦÔÙáÈ[Sã¨y%³üé+sçzÚº~¦L{<Ë4c¡­å¶¸<M˜:ý©¯¯€ÝP ²Å­¸{ˆŒÙ½P^À@s’7, ወ;ÏYÎ+ä¸8 ÚgÿŠÄ¡We”Wȼ#<)éHõ´W:OïÓ'}å²í K4Y³B‹ŽFßdžä¯ÑuLܳôJùù5Ž@kE€»'l­=g»zó‡í°¬E’°œI5-”ñÇ¢SÞ©ºrù‘F­žl4š‡šàÕ[/OqyL}ëM9ÇO1'ý=õïŠXÁ6~†õ7\.ñ¸B?Ô”žÇ5 ²éMë³s=K9qíð{q·Žyh~$Ù4Y)Çó÷Û’3”seO>¹‘4?¢œ£-ë\ú½Þ‹à‰‹ÞP†N ¹»"FL~z$¨f »’ŒJ$ßsœrO¸æ=Ímf“i#w]‹Š ;øKÏá=ï=XãsÉÙ1q•öq ¸{ZÁC œ…ƒê0HÒç ¿Tø²½B¹&Oÿ’Ã5øpR!i~;‰Èô¸,SMv 0~1GÒ¦ˆ‚86IÂÕ :â6’ÒY‚@¢ÙšXâð'ZD’6¤wø1ŠbòôÙü7¾, -'<Û| ð3a÷„©3XçÅ㞘÷$ßËPv*Öç%QT­®–†Ò š¸­Ç“‚PM×wkJc'‚XŠK^¿€¾G!Y¾ßúZMÇ ©7Ú¿*KÒ"lWЍ‚±‹‡—Që÷XuÙuõCõôü¼qˆ²Š8 xŸ SŽdÃð6³lä)Ä!·ý±±½Í²|+n áŸE‘ Í«^×ÃqÑ›d)ÒІ/É_âäÈÙÕÓUž[·[ÈŽ‹k›ýl–ÖãxCò]a£ ÖõŽJÚ;]HrJòÝjîÊ~³åýyÉ= t¹‹íëìtO˜¤7ˆãÂÇ*"Ìp}zÚáÏíжìÛúÈâý…›kÀ¯Ž-. »/œ0ÁødÂô±f“¼5Ëxìu”ûNm²Ÿýè#>§p.¾ ÿ¾húÔ1•é¾F{oúTûå„)3GÎãUÿ j•ßAÂ’þé´É¿(ò0n’ý!Êù‹ï½ç^’[r3ú/1îá§æ{}’ðt…ßvYŠäùï{ï½×L6ê…ùú¹(ïçEÓ§XHoB†o³M[¶ g‚°ükðS³Ee…“U*á–Oß™²V)¯úþÉY³üÍ%¦µXþ9_?Ý ï5TOSýÜ`2¸£ÿÔ>?€ö¨ŒO˜·naÂ+ª§£ó†Ô{ü»ïúÊÅFòî°Û8V‘÷BÂûKJŒ%§è_R õé‡Ï¦³~P²ð}#„é2”àåIÄŒ¼a„&&f6R\Kg£ÛÈòÀ}è…IqnÆtÂÅc‚Ñ•³snéJÖTþ÷E…W¹ l4š‹ç½}?7ünKR7½ÞïçÒâžØG,ΠÖä÷ñé:î–»6XÕp€Ó©‹‚ ò<׿œí™“íÙ¾0_(Oʼn»“¿¨![kŽb÷Àã¯&ôEEÂÍJßã?§wknTcëNýotgð¿ü”d6¯ýjÞ4šD4“}þ°½·ý™ócîØ¸&ã² ­7B?>ÁÝ <«R~Á«è{7c©ä©/´fƒc÷½p7¨´R‰ÚS.×xˆ¢ZõöøÉoŸ5™ä/>5ÅŠ/‘vZȸjÐlbkì­P"¹‹…‚ô îw¡°æíÔl-°qA&ÐIe’l=±ï=XßëõÆ3v˜¿qã2ê{3n ·qéŽ/Žðfì#Úï “)÷vÙdFSyp̼¬t´¿Ž HÉ sP¦òBSû¡º|~^Â䩌€£¦l¦^\WºNVú«ù÷3/÷ñöùDÅàð çäY§ ò|¶àf4ÒuW Œ²—k4‡BÃä=]û¨:Gm}¬¿±_Î~‡^âé!ÜšƒøØkoÞ *ás4 éXtJŽÌ= aE'IïÊ}O}ʈ§^­ƒSÞápU»SÞa³Ú÷êô܃‘¯MøöýÙhni!b­õ¨è ö¿,†jRåŽê½ÐN}ç1—»rÿ³¾7ÈnpÎtdšú´Ë¨é]Å<Ü+ò©?þ/ñ£ô£î[N£ròL F€L?P#öòÇgLÓŸFCîËÌ1$A`î Qƒü‘JçXoøÇþ8"Ú/×>I˜”Ššé“˜‡Tø—G‹²°Œå…ø$IñHr†bºô “ª5&§ †KƒJfqø9Ug}Ép­mÔ¦ûâÖ‰m²„.Æ^=»QŸ_ƒÕSPû.ìÀ×OÉ|µ¤—×+U¿z‹nTÉͬ·nË\Ò–¦öCuùüüÊ Ý?pKj9rì"–ŒxãÞíÁ—^ÑË×ï×6%EA÷ïØ÷$þ#ôÌÎ$ߎ݊fªá@x.„Iq;•ZóûØ7Þ~±GFÔ›©:¶*†ÝÔj‹¿>·Ýèã¿Â-iË….yGÉ·­Êiõr „°!ŒüÊó‚Ü<¼–=òê”—°qÜh,µ¶{à’þ÷Ï·ìñ t_,tÔì$ß­¾ßlÑÂð \oñBýWÒ&ʧçrkíÿ&ÁÃ5àM‚¯á™½3y5j¢@­ôL³^^_]¾=§¢®ƒÄíŸ&LÞSýz=ÏÿDù×eg\ƒéÛjù7ʧ4˲aÖ˜‹§CP¥n)_Úf™@*ÂðZ¥·‹C=†˜f‰«ã‰·ä¡ö\j*nƒI“ó‹ döñï:²]v9!AžžúîXƒlÜc4åÏÄ“r4i±¤kH½ñ…#q¡\ Å]EÅ B{¤á–(õƒE?¸2½½“ÒŠòа ¼ñÅ­ó‰k¢;‡ïH9yå\-z•nBR`ЃC÷ÀóŸws÷|»û™Óòˆý»E§'òP+ó.ÀCÛÿV­¾º¯|8¸Ã¼Ç'%_ÌJ S3Ò„W ÄZ%8Äv`Ý_qnd~ª|ý©?E5ÎLçáÊ—dÃÇ—¨6…Ý$§ùE½ûÈÄÉÂ×sg|€¹¼Ör\Òÿ4åh·¥¢Zàý¥Þo«Î€ët{Xg›¬DXçO{ý (ï"§šÚe¹€ ©·Z–ð%C0ƒQºÓZFÅ—éë8h@?\’Ÿ4 aãFb¬Û”Ì’Y¤—DG ùvóè„‘^>où¾-e‡ÀÉwýºp"¼7ô 4ûáWÞ¸s¶¦g’Hu¦ºù¾ñäj“ïúõ=¥"¬3ÂN£u›qßÓ/ŽÀhz¡m-÷€¥ÿ‰|÷uÿYàä›z¶î@8^„ýwÒ(æR4á­¥ÿënh)¸¼€ìqùÿÞxãR®§×¢#è¶zõ÷䥉´ ŽD¬«§N«[ä_~i¾¹²Ž\7üj äêüeÁÇï“Þ½öÙ·o}¤¯ Ž|Tô¿N·È[uŽi¾cG­~1 Í2¿ÿ¥;O:v±²ï½ÿm©Ë¼iØ- ÁÅh¾E"¹¶&‘ŸMŸ<ðy‰âf£¼Ól”ö#yý {œ ‹£2µJû¦YŽ6Ö¿ÈEÆücéX’G.ûÒ'ã_X€ ª™ù‰RŽJP-§xåz~Ѭ„žØ0šï¨÷€¥ÿƒ´éA4±Ð©:¢³­ì!ù\y狟¿û6Í[£/ ŽÜÿ´²÷F‚©©¼—› Š*Ý÷å^©Š•ļFr~¥|Wº†‹ýЧ Vê[oòoŽ;ùi=[XŸ~¨§(ž¬ —«(—#š ÐƒV!àn×Þr[¼ V·%Wƒ<4ÄQHoÛ®Ý=ãží·dÑÿá—)‡œŒÇîª#z¤jG®y° ˆ¥€î ƒFüë¡øÕ?/Þ†Ré߀ÌPÈÁQÂ%ýO®yh:ˆ££ŠjKÿ©ÿ¬Y±%ÒdLGìÿ¦7ÖJ7A±ƒr8-‡@øðÛ3±tš ÖVÐîbÿþ>-W›KV¸¯º†v‚2úù®11l„#áéáí= sÒ‹áíhÝTGª+úùv´úµÚú–„©_Û¶7a#hŒ9â=pIÿ£ŸïV‹·#Uœp¤¾§ÿT¬™¡8jÿÛ6NÀm 'Æà4!!µ]Bš’¿PÒG(Dz§ÿK‹\£Õ…â —2÷óm›Þ!iÅPœÑ©gG|>UÜXGZá’ûù¶Mß“Â1ESpuž*vàŽvXúŸV¸ä~¾mÓÿ„#â‰Þpt¡(‘¼¡8jÿÛ¦Á•Ríæ¶iã¸0ŽG •! @Ž¥Æ2øYŽã€þ/Él:•F„˪“FŒ!€x¢Ã%!ÅÎŽø|b÷ÕÑÃXìˆõ³QO´ŒÄý¨Úaé4Æñ°ô¿›PÀû߆· â)Ð*ŠÔ9pÿÛ°ÅŽùgÓraŽ@ëA@eË\ô0áí@5'¢Í>?ãž‘p•¨òЙMœ€Û°“Oôùï…"óGÂ×rPu’Ñ‘êfÃ^h9Q„)¾QÿÓs´{à’þ×Þÿ6¼UOúOE‘ÊË—£õ¿ [[!Š¿ÁÙR.#Àh4‚PEÀ%É‘85‰þ/é¡@«%Y1‚!€+Œ¡$ÂØŸO÷@EmÔ`.æRظRÆ™£Ý¼ÿ/í,Ÿ±¾W^¾õ?Àfmv´›Ûf ã‚8Vˆ€lEÀÙ‘¸¢ýRÈ¡ 'Šr ˜n1\ŒÉkGÂØR¯Ê:Ú¡õ\d%¶D¾,x;*–úðþ·OTþ§*/_¼íSZËKå¼åû€×€#À¨Dk²hÀEYp$N5´~ ðÿNûÞµ Öö-¥áÒµ^ o‰cçPp¦½#¥^ŽT'g« ý·*8;ZÿÛkþ±)œ\G€#ШҀ£‰‡£¹!¤¦¹Äƒ¡)}h£¼ŽüàuäºÙþ£Œ³¯H àý_(6ŒRúÞéq&[8Ž€C €ÿ¸%ʪè“&ä8ph6઎! ‰Á%MdЯÛri™cÊ«fKT^>à3tíB!÷&(;yÌ–âk”%º¹ƒTÎï±ÁiæHOÿHê|#®d$Áéƒ?ÉPlר4 ¡;IÙjEµ†Ø:þSÖ¦ÚSs xíØð+ŽG@AÀ&Ú¿/?÷GîSd^y¯Qƒ¦ç&ÁÃ|ç¿ š«»ƒ*8är¶ÎQ$Ö‘UXûVýêžWÕ‘¸^—m‚{½Jª;Q“êâÙb—$GD·ºKºàŽàÖ¥^ikKÔùé·!øžñ òòAe?}›Êà :Ž}¢¿ÛÑ‹ÿ®ï|ž]{×V­†Æ7 ÷†v…ôMªG×A“àú‡·\Aü¥—Útj-9„i\ ¼ƒï_þ¡×€»O˜Œ¥T\Á]nƒëú†=qn¿bF."ã6 MÂÝFu°‹NÀí+Êà4k ˆ (Ã'Êö¹èA`½Ù¾+‰šØ>àñè¿­b~¨‰¹ýµ¨¡xöÇP²àÔ‚ãw;Á˼ßy t×öob —LÂl¢,›g¯ìëûÔæe0DœýúßÐhá‚Z>}úÃÙ¥_Àé¯çAiÚ¡F˪+£èîÚÀH›õ<|þÍ&¹ï‰º²Õ|½brs³³š+Qkl³ô¿¨Ò2«ól_kEêºÐ6ìZD ì]û Þô&ZôÙoìËóàÄžð÷±°{ù mØuÖóºªXÛuë¾§c§ ö{%vZÈxÃ8{! èz®’v‹‚rd¯ÒZV®¦¸Ý: »ö€Û]·¢iH9~_ å«þM«ÀóÙÇAôñ¿¯>CÒ>(ýø Pu Åø± ŠêRÖ9(™ÿ%˜ŽG‡}"ø}ñ>Íx<ÄàvPòÞ§àñX÷]0ôn†²_–ƒç‹@Ý‹‘qÃÎ$(ûâG‹YŠî¦ëÀíÞÛA ðsÚI&O.,Šwÿ÷0íÙ% rÑ¥Ÿ³/”S ÅÓþ >ŸÌiYp[AéǿƜlpë¾Ñ¡øØ~8û¿EPrüß5Ý æ²Rh3ôVÈüb$ým†Ü íñšÚÇ ÷î€S g‚¹¸|㯇¶Co‡ þ È®éÚuA£ƒÐŸ‡À›ï#“bû Q÷ƒÆ¯-Ü gûJ°\ n"˜Û3¢;ÎgCÖó¡`÷fp ‡° “Á#¼èÏœ‚SŸ¿‹yö]‚°ñb’ïç,qù;þ„¿d9çÕD¦ ?òÏ;Ù÷Ypó ³©À±í³Ñ|Ã}o[Ì´ßýÆüˆ–cFØôõ@ÕnÐýÚh9Ìf=dìýIï§LpÏ¡³ ðüp÷ Câ{?ß1:Ee×?ð'dZéIó¡×Mó m‡ÁÈÅÍsòO8ºu†Å,%´ÛÝÑ÷Ðy1Yɫƃ±<(>²ßs uo26ÃÁ“X¼u‹.dþYc(½úÒs vóµNÂk@€ð@q„¨'§ÎŠ3›M·€ „¡gˆ| t4ŽSu@.W$ÈB6&Ì@'ÿkL›”Tg&ž Å%P+zÜ[¬"ÍP°è¦M\o âZöù÷h§ O< úÛÀ|î<˜u×H(~o ¦àñüX$¾¥PôÆ,FÞ=Ÿ~ žÄ®‰íÁ;áU0: e«ÿ)û,è·&‚û]£ ø¿ @º˜‡ˆšÀ|üèWý ¤!õœü˜¤ƒ~ýFPwï ž/=úMÛÀðÕP‡¶gåŠþ,¾ìËÁx<<Ÿzt#n€ò_V°r•éìy(žõ#ÿJß׎€Ità°»4/…ŒO§Cð½ã¹N{÷ÈOÚ #þ{¶ÀÅ +¡<ëh¡š”d÷”¤„Žã&AàMwÀÙe_ƒJëÞ½ûƒ{øU»e ç@íé >ÑàüªŸ pÿNVÃÅsõýG`¸pBx‚ï©3Ÿe÷BÔ‚„„ÿÔ¢Y ööòÓ'Xž°'§"É/„ãÓžb> MM¿zí Ã+^=û±Š+&rñ‹îÞ ×ÐÙ¶û#0£yH!3 ÿÜ8sl%nËÁ?¤?Ý> Ê ³Ra½îG{î`ߟ/€Î#zÝ8Î¥­†Ò‚ FŽ»ô‰o¤îú.fï7ïPûÖ½ú’³ ™ Ptn?dîÿ*4ìŸAÁÙd8}øðž À¡@IDATkW{•{vûïàáÎH¶Î«‹?¶uäç¤@Ï!3¡#üô¤O.ë=2sñ ê m;Ý€_‚¬ÃK.KÃ#.E€ðKñhÉ3áÅßs+t+~V­V½`’LÁ¸$˜ìíá&ùùxn 7jDï”R~a)r–r11aêŒ3’$¿ª ý8!áÑòFˆäY쉀 ¸£ ¨¢Yv‰™\e‹—€1y?Ódë†]ê«{€5ÓÒÅ\õ'@Ô†kºu¢iïƒTPˆ¤yxÏš bû@r.2ÌH^<óà üð—H8áId^ å¿­bs±]H'2ÙKpí ¨+)’9óYåí‡ìÐÍ9¨ÕÚº‹‰0îNí ~—pE>ß×sI!œúlË  †?…-°É’²É†œ3P|x»ÞÉ6ç¼±ó‚ä-àwÍŒ€S„¨sƒìÿ¹[×°ëd—M¡ìtšEËMm Dæ‹ì†¡™~=ñºª3!Iñ ¦lgièGåí ^]®†´y¯€© .n\ ]Þ\Ú P¬[1´$®<<ü⯃coVh`«_ççUœM_'S¾`¡=îCÓë!ûÈRÔ@dqçðkS^*;n‹“)ÏøÏÓ+·TÔ†„t4ý  Öz¦oﳡ„—¶Ã(ûyÙ/_y"eÞ_"x Y.ºpÚvÂxH·;Á¤/DsúŠQùÿ‹GAH¦Ë‹³álþg`ÈÉøÚQ™5ðP”ùñþ=¶õ]|8ÏòðŸÚà¼vlšë Ù8‰Lœrs™[Ù"QBzD„Èñ=#àê¨PÁÃMGNéyh<ìÅ¥'ŸíO͂ăéí¤žž{ÖœýÂø)Ó[8}êºÆ‹æ9m€’»"SÑ%¸ µÊˆTSÜÝØ¾ú¦wÏ ²ôr•m­\\ªNanLL©ží’s2-ñ|æq¦í–rsAlÓäâ íºªm0§ždmëLš˜^ Â|¾N¯ŠV^’ªbøQ#(M¯z12æ Å¾G‚„FÔ—Ióî} hüÛB·ÙßV]«ÖDʯBPëí?à&,-½L&¼ŸTx¬mÛv,[I5;qŸ^}Ù=×ùé‹XzipG³™š¸W·hèôÔTȘÿ63¥±dâ5"P˜³×o(»*uÍQ mp2¥û¾Òu´%G@¤å8÷L¢…|["­È´„4ØþÁñPŽq7¯`‹) ™ÀTþ*òMYÛt¼ŽiÛþëw‹$\„Èrl}@š÷´ÝóÁ'°Äþ<üÂáà†×­“ðãjp^ f>%r¨zìõ7_P©4ïúÊÿÞº„µwê‰ÍŒ1+_d ¯Ú„ã§ÎÂOkw¶ÏÊÉ_…$|"’ð÷[¢N¼ÌË@?àîr¥÷.AÀe½þr jˆ‘Ë*à(š<LhFrI@BEA6(zëK®ZNtcF‚Ñ òÙ}ûÌyËrM**MÚ†W d›.]È…üÇ^¨v…Ÿ6†¸ë3—ƒñâY8ðôíµ+ µ^óˆìíÇ<ÂLIŠöí„ ÷A‡Ç&².SQËçÜ JŠªì»É™Ÿ”¦fǵý¸wYh"“»eumÉx¼fcýþâdÉ„îý q`1j—gYI¨:”ŒåU'5uF›pŸ ^°iñ f÷Ýÿ®ÿYRõyàˆ/øÕ‚ÉXÄ4à›¾\íJͧ²d„‚s{á©M¨Í¯_žš%¹FlÅ¿¶k´Õ‘ZI›4ÛšG^ò’ï9}»‡ ¯>|« É·#ÕÓ)ëB¿öÈ­ª¾Ý;‹ø2ÿ’ð±¡ü¥Çzu+ßͱ.x\ñ-ÕêÕU0gfÙu‹m˜ù‰éðqQKî~ßÌìDÀ—Jmß> ªÉ’óòÙLMlotØÕ’ߘ|5âàvç(ƒÚ2“A§ÃŽÝX^¸‚›ˆþ~¶r1h)›\Ž@Ú}{t颻'nP¸ M?:@Э÷ùÚVûµÒ8×7(Zn#Ú“ I›¡·UdE“âãû™ïn"èºNàƒÚv÷Î]¡øè>0¡f¾ý³I…o.†þ¯|ªvþ'ÛÑ~øýç8†ï|qØ!“TTäòRÈ~}.„vƒA[‚:ß› +ª¬Ô¾æZc,:½D˜}_úÈ9E ª&y¢,ž_R9²ëè?þy˜xoºªˆ²ã_ èô.>ý ˜ü`6ÛÙéâ™Uqtç'ØY³ \ZݽG>9é^•r|Gv~ }F? )3·@¿±ÿ‡M¬ƒÓs~hË| ž¤ñ=ñîmо×$qšsd~Äõ‹aÒ¬=0pÂ_±3çØ»þ9ç~^¨Æß¹ë>Ð{¸–vágüšsº(ªç{Р­ÆL¿*#6¦u{ò|3ù¾(n9ÀbµÃ« —ÙO-Ì“ó¬½?üpžã›«GJ ¬L=ñÛÏJú ~•˜HHIzݤ¸méŽg-„Î ¶Òï–âÓ£pj‹SÇÛ™ûN_KEÿ[×áªç’#ñ²tÑð–±¢êöfMôÔ;"¦^ºÈpŒIÇ÷ŸZñžºè(ÉTSñDZ4âRÈŽˆÚþÑk/RœÅÓ7ú ¢=QøÅót^wÏyöûn–³ÉWe~sñ³Zr¾\éãP…Ä""²2´jöÒŒã®?äD[{NÞtR;©/¢P‚ÃkÍÐ(Ñ©Ó^æè3Pßù-Ù¾,þzÈѵÞùÉ[/ãÛPÏb-]5Ú¿cèÉä±áÿiIu/z®ÞH}ÒqK[ux i{M‘`)ÏÇóñ;a ÐS߈˜’¤ch4XÊÎ_wHX[ì¨Y¬ª©ÔW$yÀm–æùM6–ÞÇ Ûîûìù`þ§pÒZû;«í†ç‰š{Àzeð¦¸{Ó‰—>ˆ#CwĘo&ß^¾îBèÅç–É#ôvY޵·‘Æ£ø7Q7T^ÙŠüÞZ•$¨É:Äö ›«zÛ.䛪OÄ»¹ä[=¿¡áèív@é  È7§n÷ù¦ü9ÕBÉWmòMG"IsÈ7ÛùVó.*¸€|Óv"Þž$ßT§šPŒ¸+ù¦½gm)¿ ×<³þµúÈ·š7vþµ”ï½ØÓv’2l(5—|7”g ïc²á½Ö¥¯ ô‚Shxdäƒâ»(óí½¨¯$jj ƒQÿG<†Ú¨Å_†ê+‹·7ŒßZa´ëÈÅÉ`F€8˜€{¯IÞïI×Ý<‹mRƒLô¼‡ƒ%Q[ >xÇÛg?IA‹ü»h-îT$'—BB´FÀÉ-ä:yˆ`ÎZÒ2ÆU¶©6s#y®îŽpµz pû{®åEήmOË›˜hx§i]½ßæ61.“$IAoï”Î¥\j j£)„:c²ü¢ˆ¹ÿeÞ<¼)­(güÁ(]&O.p)ÍÎñ‚Ín+¯Ô.ØÞìøD¨Ôq=¹Ô -áë´Å®ØË*uF纋½¼Ø,ºÅn·×P× ÎN;¨ý­Jˆs½UæS«°*&”¦·Õ¥Ë°83÷Îå/¸‹ Õ Ý"CM iSsÒÔ4ê(ZÓ'&à>h–ã?þwZú­`’ ¥yó¨=4›¬•ù%æ°€}8øøs¨b·Ù4?„žlµŸ.3FpÛ»ù")5†+V‹…œ5¨ý+”VÜþnl¥ % ûý¢ y%&àÞilÂYÄ›õFCLt«ð*¢á°dmœ+¬[¶Òbƒ@—mà·Ýٷ܆jüw5×#l\Ñ8ü¹h+Ñn;‘j1’^qzÀ|­p×}αïÚ¶%Ón©,ÜÛ¹íãÔBGÙf=·vÙ7ôFïŠw svÛéN›ÈFÙn=—Õ¦/·½›à%,q¸õüíëÙ‡YÒỏ·›Šhi6N{Dû³ åöo)ªx>áh·VÒ=WÕû+Îx»¡MfA„“ç "àU$\%yn)umê~XýÛ^(,)‡¾Ý;ÂÔ1C gçöðΫ ¶}+¸ñr\8y¦Þÿêg¸ûÚqнS[øzÕo~à z˜8|\1rzÜ+¶¨dù\A lH?Þ|Äu‰Q÷‰“G ‹0°WgønMj½猃G!?-O;>ûa3̘2ôzÇ{ß±Sçaåæ°7;1€·wíØ¸aàÀ8ðÙòÍp,ïÄ´‰ÂóFA/¬…³×ý†I— ç}ä•BR¿pí„DÐëtðÞ—?«¶¼õÅ`Àõ—ºÁik£/G‚Ò(°Ü{êJP ¸#) ÅX@ò̈‡Dc;–“ó£¾o¿›Žµn]óéë)§æ @øe·ë Ë=±Ïk±€ó8—wz‘.¶ÛC'Ãc¡S)EÍpj.„áÑÈnÒÉœ¬ï1úmiõ¨Õþñ5v‡v†#Í­zПwÖÖN[ã¥cÙû¬j{-·¿[Û‹=àn…³ÎÌ„'•æ* w§¼MT8\7!¿} Ng‡6Ѐ>HÆ7ïÌ»Ýñ KÝ*­V•œ¯O;»2OÀÌ«ÇÃÕã‡Á7¿l‡3ù4à;VZKקÞìãp帡б­*J¡îsý—sâ ¬O;_¬Ü (ácœÎK×ÃT¯÷ÐÞ]aP\@Õ,÷¸sÿ÷kSUâýòÃ7€^ŽÏø÷]©ºÿÓ6A¨ÙÜ::·o ‹VnqžGÞöÏWl‚ƒâàÚKÕ…©|øÀžêq×MH‚™×ŒužÓØ—u{*×Räj8¶€V=àD èA ,›–/Y%WV¬˜([õìÏhÎ%@¸~ØûîÜúï¿!.ÀÂÖœl=qްG½ÈVôØžÛØ%E¶á脜š‡aGÊÖÊü K¿ûs¡ß–¯ Ûßn9·³âjÙ¦pû7§õ 7½ßt/­j{­¶sªØà9LÀ„Çm;] [1‚ävX¿nÐ*"T%Ý{srU/ñ¨Áñè ¶­SJß’û÷TIêîÌc0¤wèÐ6útëÛµ‚4Ü/RE¥ æÜ9 .»d„‡šÄæóÏÑSNÞòâÒJ9¸W}b¥Éü¾œЯ;m=’ì.ðÛž,±ÉSm6›ŒÐí(«°¨ö•–WB6ü± }Q™Ä#‡ÄyËÏTq›Ð·»j…ÀtCþ¾ìªg½+†ºPêÑ©æÙÑYVD{5ñ4>¼EHà|ÛÃ'Ö8UÞfù¦qº+ËÊŠKÒ~ÝðÉÙÈ(X18™МšˆáFøصcþùó§éG.ÀïAóóðáÎk€l=¸sÇüüж°®Û$nûfO؆»¶mýGq1~zÅßNZ½j¶ÿŽó‹äȨ¸žÛ¿íO¸~é›7|B÷Òª¶×rû7£–õŸÂ.›ú±q÷Aêhî¶Dá©ûŽj窔:IÒDqÚý{v†Ô½9Ð ö‰ÓùpÛ´Ñ€C®ÃÁ£§ ëøiؾ÷°Ó޼s8mˆï¦ê7ú9o‰D»B=öÕGnB²ÏÞ{ ”!QÞ†ùSØÇ#·^åÅ:ó£…íûrT[–­Ï€•›vªá"Eejˆ‘î½:ÁšíûH[õ¦Ѧtàð)õ%âã¥ÕuúG2ÇO@›Vê¶îH°EBù@ôî/r[rk¹ÍªÏã𤋮š$Eª~Û[0ǧ#åשYЩôð%ï'‘úAfmyúÆué­ÛÇ|†\0R™ºk»dį=œF€<ßD¾÷uê"ÈÉü÷†KèO¸¾„³V ¸ó ›#[·þôŒ¿zLU.=ú³dÀ¡Â9]ò|ùÎŠŽ—ŽÜ·è·5?mųè7¥åk Æ= ºýÞ p³’`þF2HÜþk}ò|ù>n(eíÝù-ÝC«Ú^ëí祥¢Š5œ¸X=/¶ßp±x¿'pO ø‘“g1†z—“ü®Ù¶¾ÂØnÑÅsÔÐxørå¯Ð½Âí[G©1ÔTƒ^)IýຉIuV޼ծé&:CY"B«e—ÂÐ;>nX_ø†°ìGO{mN.{Ķsn§øí·ìBoûa=´7L=þ&IårŒAßU-Ölr\–DROÞm×DyP¢˜qN‡€ãí «…/eä q[BBOŸ‚ê¾à_ ]|D¾è K 0†~¦Õß.^‰]†t0pÈ5g#"•Ë÷¤ë8&‘©'QÌ7…çûXÖÁW~ù)…žž„+áK8;~츠¡tÁ5°ü‹M¹åÄõ¹3ß­Œ9¶NÇ1á ·Å|SØ y¾söïùúçÿ}I¡'ô›×ú5Ð@û¼³Hn§ 1-ÓqLxýíO1ßvBžï¬=;¿_ýÞ;íîí/*FΜíb¥¹sf1ÍE®ñç¹Í›J^l»ÝñL"’|¾Ð¡ôÑãÀ)©¼|ùã¯ðÆ£3Ôú]ä4—Ý5:aºlçEO#€ïMU8+»7E¯Óÿh“m/Éeò•XÏZPJ¬Š¼u«EoŽjß| ¤ÌH;4jò´ ¶ø¾ã³‡vˆÂ¾VY©DT”I&»îA•*õ…F ¥Av½ ñª,:v`ïO›Wü°´´´è,‚QˆÅÀž„+Åïh1üÍRS×¾HlÜûÛÖý£§^yµ½WÜåG»wkEmo¶–)aÖÉ$[ƒ®í ­JQ¡QCij›¥²øè¾Ý«·¬üaUYY õõ ö÷§k QíÚ¯¶ˆTª˜q`_£d ºö·*! Jƒìˆ¶?rhçú-?._S^^*H·_µ?=Cð9¥3à3…®ï–¤_÷?ûÂZ2àßK¡9§  ‡s(Nä^n‹SÇÛ™ûÎнû?vÇ\myª´XkÄl»æøé²Mp%Ÿº›xGÍD )4še8†• ùiR"ân±Ú±Üæ½Ãí@yBêÈùÔÌ+Õ¸nªÃ‚ÿ­U;“ÞuõX§-b´ÍP9ŸÈ+Oï<µÃiÊáÍOWÂÞÌ#Û?zíÅkñ8"ô9œ<žœê@ÀÝ¿ý̤„oñp:¥—¤ëz¦fÐgi·¥ûž}á˜Òî_|vj 3%²MÄ›ÂO(l†~ÛÑ8Q'RŠcljöû Ö£{Ÿ~ýB#"Ú˜BC[ôÚ:öR«ÊòîøtT_ ŠBBrí:½û\v’¤ØlÖJKyEayYÉÙ£‡ìÞŸ‘z±!²M`zø £ß›ð€Óç-p4OÅ«Ák _BRŸn½û ‹hjne0Mô™‡NvWа—uÖÉvµ§|…Þ|Ö¢3ŽšJ¨~UQY^^T^ZrîèÁ{ìLËFéóçkà¢÷€†Ú_/ÙÌf}YG|?SwøáO©”CÏØ”ú L¶¯Ä¶/,/)9äàþýw¥ÆÊÑïÛoÛÿþg_\Í5èŸ/>×µ¥ Õ<öÔÒRù|·"àÚaRdL£g<š[ve£·M›kÌI›¼ÎÍIæÑ\ò-Ê£Ž£ù8`‘ùcyçá0†§ô¡kj*ñçrŒ¸@ÂæÈLBÐK¢&YR;â¹×xEzoœ¯ÍzæÅñ ^zv} 2wõ€ BE¦‹ðòâÒK· 8{q:€Ëô uKÖ Øöÿ,}ýœÅí¡[je›ù/¯f·ÿØÁº¤A½”»ðª^ ø,ÿuôÏY2½˜R ¸ö§g‡¬(Sð>Ç ÅÜ(j0 ;É;_w^5¶%r|«ÙPŒO?…Ê+?lØ£hCt$ê™c§Ðq(+È)HPÔXjGå%‰”0Üš:;¿Ÿk;þ0нwÿ¼£>œ7«yÃ¥:¬¢°ì¦X'‚IÞ"àªçtŸa*š"ßhäTX~îk6«<ܬOˆÔë?,¶ÛEÝèæ& ‘oú$%<_¢+‘pjš7ÚO8úKmNöŠºR¼r LŒ1M‹4:.§s•J’ïcNàˆ×@“Ûÿºñºiñ•;°Ô†Ã¡;òÞ.½º+[“mçŽË)`ÚŸa²õÌ{øât4ÖÐù}w€ÃÜ(j0;®£A«jš4yÔ  ‰#P…€3ΟNn'àóæÝ]a3÷aYËÁzf!~y¹¿äТ9I¹ívi³® †_tÎ/;e%N8j)‰öâ5 êFsº†©Ž5îÛ„„ͽÍúF¨¦ÐAô«(·Bꢟô³÷±QøU &ª®˜¿lò|ùÆðÅ%ôè‡/<·Ê] ÆÜ]Hr>Œ#Ð"ðÓ†×9²°)ž‹“þð…gßF(èóºd;Ýãúf»!&œ,§0½8¹t%Þ‚|»-þówk:PQúñ Ð¶`%¤6ý%Ƕ7¼—{&¯…Tµ¤Ó«-Àâ!,ÖýÍë]$¢^¹.ig˜bÖ9.§"ü· Ôzº>Ã|¸=˜®:ÛÿÖ‰æ6ÓFWþ ïm£¨ðe ƒ¥ó¿5Í>|}àü¾ýÕ˜o ;Áfè ßϾíÎ&cîN49/F€hH‰«’N®bbƒ›ç* ö…½˜í?ePÖaÏö•Žò¹.L÷C3uÂéaC¤R0ò‚C¢Ix½«<÷re5+½~|–V=‰:Ò\LšoÒùvH *·Q‡KŠùFû§¹Óó-ž ¸@‚çŒ#àS0ÌoÎŽ{¶¤¯’çò EŽ2oÞG}OXO<„ŸÁ°”O•RY¾ÿ™óIÉE¶ìI0Ì×Y ,Gpç6­,üv8«ý„}ª9–°È'î}úù”–؆’Ί£÷¨%Yùý¹î¼zœÚÞÉtj+ö/ ÈܺxSÒ­¯Þ{©6! Ök µþxxdègƒ±¬‰Þ“Ä)+/ÍÞk™ØoÜï”eô– ÉŸÚ›)ÇŸˆ¥Av°Ítøó:Ž-7‡:\º+æ»v›3¯¯3Œ€O@ŠfŒ ½{Ιž4¦êÆú:–ñúž{9É.ÛI³³;N±8‘¦w‹“+ùt} µ8c7g°§[¯3)‡v÷6ØíúèŠò°øsg"³ÛwТgÕÍ5÷|vî¼úì(Þg²Ûö9¥ÓéÄÏÆóá.Š@¬at¢yñ@½dUù•"éä]Wîϱ?#á÷0-ß.Z¹À>à$V/•F¸¤AvþñŸR=]]&àžF˜ógF!€¾á’*8Þun!¿*¸ê ª®ÇoºM±ÉÛÇþ1)á#ds3©Ü¶oLOÍ ÕNA õºNýe‹B§(ŽÉšt*uô¬.¥ÑD9i¯éoÁñ->FSTn…mThéº?ýyÉ€%°MПȉ`ß# (NEIQh„IN^F@§“Š"1€ä†œ””æÔ%2á¹[PlÊïE†ø¢´4y)Ž^ÆI,M÷GE‘I=ÈñõN‚ŠÑ8nÊ“6$ßœ `~!&¼…`|€v‚tp” ðºÜUÖ\‘=~K[ñª9dÆ«GËE¿Óœ‘Ajrãz”î™!ª¯“ª_–Ä6ž{Òˆ^>_÷6I›bì0:½Õ¯{ zó¨+³ìò¾E\¢¿ ÀÜ_ZŠídŒdx;#Àx|9 8ªv1÷*úÕ…õü5ã°"Iëh ²p­Ü•øUÇKÞC }zh”H»J”ˆÂ®_)Äfž{ ßíZn)܈ÅEâ¼÷§Î±ßsöŠïâ¢b~X‰Ž«V•b[ W“Mvhƒ‹už{ÒþFïw•ˆ±ùVý"ï•Î% HãÛö•èùnEÛ°- ñ«Ý ;ùRÃsF )0o Z|,#Àx‰tv –yî}ji‚ßÈšàÞo*‘µ¿}ƒ»k©¬ñíŠ/» &àîBÒGù`Ç5À‘·œ“ÝŽïèA2¹Ö›pàäÿ §oŸ¨*¡¨1–bçÞEàMðâ‚ë½k—ÆÚß¾½ežŽ5¾}Û\º:\j W0ÐêFD“¦#8Úžìp¾° JÊÀb± Ý0Uº Ðê}A}ðûŸ)Ä­ÂàMT8ôï Ý:µôÚá¤#\uºà<Þ i$¤#ñV;ýaÏ&&à>l-êP–•˜€jʼ*3fâœFúãä%êÔþ^–ç¥Òƒ»‡Æ÷_>Åç­Sf!ïkYf0¸¯ wÕž ¸»ôp>Dº‰d¯Þ¶Ö¦íƒÂârê ‘¡fˆŒ0ƒÉdM‰ïÂ’r8~*ŠË+à‡M;¡UD(ŒMè )‰ýÀd2‚^¯s’q7gï&ô d¸ˆsŠ›pmn6¤ .[àÏøsû ¨šàÝ6¥ç67?>¯ià}ÿ®ê3Xû» Ï.‘ÆwYyÖ÷XJµÌ j|ƒ¹×XfгàQîAÆÚü³e)ÔbwæqX´òWÈ/.ƒ¾Ý;ÂÔQƒ _j ñÏJ¹ÑêòJ ì?œéŽÁw¦ŒCð»Ë’``¯Î`4Vq7ÉYy)42Ê#Ò#ùè¯Lj’V¬`]]á}±lI<3)a~vKÁöÐÛÊÕΘ½Øy¼¿åö·ÝZ~µø¤ÉÚß-Ç´19Ti|Ó(£Î/pø5è=ìlù}jL| #Ð8¼1(ùèòzS<÷O[wÃ?þ»ÌèÕ}àúñpÏ5£aXß®L¾«Ú…^BÂ…ð1ãú–l‚ŸÛ`µÚT ONÚF ûÆùøeg••¦œ¼¼K´mqPX·PÔ’5ÁžŸË¶ŠÛXûÛó8»–P¯Æ÷\y6“oW¤xÙ0wŠȃÈ"y¾Þº¾]ƒã:Ãÿ»!zvnçÒ'KÂçÁST¼–oÚí á••* '<™„k¿­EZï´R';?;·ñ‚W`Mp¯Âí,¬ÆÐó¬ýíÄÅS ¬ñí)d9ßú`^2>ÞNdqסcðÝÚtßn›z „õ>¶Ê?Š'œ/ÂmÕÖý°ãÀ¨p!áþQ‹àµR§W6ˆÚã{(p†æ¬ î}à·_ÛCO’©d ¾gío7ÁŠùú¬ñía9û `~$¾ß@仢 _­ú :¶‚'©c0øÞ0?³€pëÐ& –mÜ…E%Pi±€ÍfW¿,øYU‚Ê\E68=àøÅb´’’Â}U||°&¸—@¶ý^”ˆsK“—¢ì' °b¾îQüÞü9fîèP%Á Åh7åIÛÈ™2U0ר¥@!v» «·ïU;\^;~({¾›ÙFä Ÿ~éP(*«„釠ÕR,H _Ei&¨^8-.5õ(ŠH®**"§¨h”Šå"@€5ÁÇÍ»Tío nÙê8üD@áÖ¹ÐøÆPŸ7ñ±K*?ôoAouåc–]n-Œ3cê@€ x øry¿m6lÈ8¨ªpÌwËZƒð#Õ˜´C' ´´¬Ê nc/xË`õüÙ šÉ>],óÜ7¨Ðéc—Ògº,ó¢H­Ü0 a,e‰¸ŸNLîïü-¸±˜ ÎªJãûKtÄ<âi|üxù1—m¼Èx &àƒ¶é ïwΉ3PTR¡*{4=>£6¤RZaµ¢¢,V+{Ákƒ¤±u¢# ^5¡sêZ±Ìsß!@šàè!RBª&¸ï¬ à’eyfuíXû» ÷,‘Æ·Ržµ Ÿ·ÎvðEg±w°ãŒ9—Æ!À¼q8yå(·Ã¾œ\uÒùæÔrG´( 8…¡XUnç0”–Cë±zÄÅ­ÁF+R P”¸ÌääA+Œ3n¤ ®HÒ::ïUzk…ë 1Ê‚º¤ý*@W‰ÃXû[ áž¹ªñm-Ü„¹9;w#ù&ï›§ñ;î™si4ܹ©ÑPyþ@ ?!Ýïs…%ê—ždçÄéx÷«Õj… ´‰ ‡ñÃzÃð=Z\IÆWg;ƒulq^îÊ€pŒÀCiäLRC¡EÍ&GgL¶ž“ö/¶d&¥Oï7“u:°‘|7-sò) ±ô‡Ê8Ù±ÌÿÝiãë™òB§AZÒ’Ó;Ý‘/ç j|Û*Và7œÎUøâ»ŽôÄ´9ò›0=4œ/#ÀìÃË€7Tœ,+`C^XZ®/ßбîØ7còp¸ëÊQй}4|¿~Ј’-MæÁòÍÚë¿a†R¬_%NDÀ g›“†Ðé¾Öứ³SšØÆsï#p&ø%øƒ¬›µ¿Ý¦KVõi|_Iä›#à#˜€ûøÚÅRø‰A±Xl`2zþãDû֑Ч[¸zü°¢<ßÞ쓪Y´üÝÚ xñßËá…-‡%HÎɳM)ãÀQ˜ÿÉ*u™þiåã•p½Þ‡OžÃAƒÒál~©ºíÛ5éêq§ÏÃ?þ·þïƒ%ðΗ¿À‘SçÕíT_:÷Ä™øç·à•…+ÕížøGxZ­²ª‚B\éKƒÀÜåqž-G ¢Mû¥˜K‰#'e@vòP³å°¶(‡ 4ÁmÊÌeÈ';`ío'n]¨Kã[§ÓOư“/ÝZgÆ4&àMÌ“‡‹"†ÞJTÔÞœSjqPsœÒ[ö@ÆÁãpiboHIê ©ûÂO[÷ªû*¬v”G,U—éŸ_T¦øÖQaУs[ˆŠ …›'%Ãè¡qêqÿÑ<Í&#ü~úXÔ5o¥.$2 s.Ù ¡æ˜:ÚÃa¾h,é€ù¦‰ðæ¤]T²ðµ°P‘u÷ˆežûšà7)S§š|gM•ÌÚßnoÌú4¾§Î±­u{aœ!#ÐD˜€70O.¼±*÷Vÿyª¤ê|?]þ+<ÿ¯e@™Èo—˜ÖêN"Ü }ºÀ8Œ §‰F”ܾïHõ‰õ,µ …Va‚qå$ÿGƒà”¡úy¼G ì‰qØ!Ô¿;äž)„üÂjßµc¸}êŠez2Ñk¬’oÒW;’©^pO–Éy· E'ýÇ™ƒ¤ÜrlÔ¨Pç:/øÚšàÙgN]ïC¨PÖþvocÖ§ñ-™Í#YãÛ½XsnÍG€ xó±sû™NîöœëÎ0¡OWHFB¬×K08>V=ˆBMˆ4wl×ÊyRçö­ ³a(θ`¿ˆ9ëø•ä~ýS*¼÷ÕZølùV N‘'Ï;.(¯~=½Óa“ð%¯7û¨6yóKƒ+f¼Üxâ·glÂÎhé l¯V–Êr&{‡Ï#G^  ®pJKfíï–"X}¾ªñ=ÿù¯ð~ñˆØŠ÷u¤ñ=mvùq±çŒ€¯ð| ±¯kègå{“Žï¬vÀ<œ{Nó¾ûê1`ÐëЋ g0n[¤3ù%`1¨ûôx'#m6HAåäÙBqXsžGé> ?é]ãQW£^_c»'W¨L1y²ÎÛH ‘~ÿ•rT$å!œÑ°Ñœ|ˆi‚Ëø3~H"ùˆËŽŽÛmSz®Mòï¢í®’ެýÝÜÆ$ï²ò,C`|uÒ×`îuç–¬†„—4{À5Ñ ¾5âªqƒaÿá<ØSÕ “FŽÜZää½ÎƉ–ûõtxÈ{vn`Û¾Ãp¾¨¶ìÌ®a|Lë5&¼ ¸Lõ¤wëØÂCMð˶}j؉cÈ÷É«q¯0 ! ˜Âþï}ê1 ŒÌLNÓÐñ¼Ïó°&¸û0Vµ¿®9²ö·@¢ióº4¾ñýðÝisí·°ÆwӰ䣽ƒpïà|ÑR„7ø¢zà€Úª1ßKQí„òµã‡B×ö­á?K7Á‡ßm€˜èH¸zœ£ƒd»èp9¨¬Ü¼Þøì'h„›b¿EЫÆ~·‚—QÑäËUÛUÏù߇õW>ùžûà{ rˆÄk!ùw-Ôßlˆß¼ù4ކù™°URàq±ÌsŸ"°P”Ž¿ç;Ä2Ï›†€Ý^q+~Ûaíï¦ÁVã诅 ±Ù+¶àu8€vЇZÐI_ù¤üˆ2Uãh^a´‡ h£T+¼A¡ ¯>\3”vÆäš o·O¡Æ|“MFCÍ‘ë&$¨Ò…ôíY!+ÓÆ v¢HÞ–ð6ì÷6â~TŃ×&ßÂ|ÚG仾D²‚èy¨±›ˆw5ù®±‹WèñkÚ>¼ –ÓAHÂu‹ühƒ'ðN#Àšà-‡XÕþV”á”Þ-ÑC¡_Ôò\ƒ'ÒøVdû ¼'¨ä1,$o ;aò<—ßÖ´~å·UbÃF ЃòFu½”{ŽáYÝÊêÂx©X¼`¹YQl÷ˆC‘D.M^š{V¬ó¼a~xM÷êZQ‡ìõH NèuƱ¬ñÝ0n¼W;0×N[°%Œ#Ð=Rw¬A7á¯t†™+¬•Ï5p8ïò¬ Þ|Iû=¶·‰t~" hp.4¾AVÞÀû€ú™ÿí!ïÉs,»<™w2B€ ¸†ƒMa‹  èŸq!Á=G“’í:7ò‚7`Mðæ£½½|Ãd$ª¼âx:1¹ÿŠæçg²Æwp´s°Ô’ x°´4דâÓÒVcׂÕjUÅ`ûóP-¿®i‚£#(Ô¤j‚ûu…¼d¼äÚùU~¤ykm^*Ú/‹Ùð÷V­•ЬŸðB»¡º¤ñ7y£ÕÛx‰ð˜€ûG;±•Œ#P…€ÎÕ ®(3Ž :Áñj‚þ³Æ?Jfí例i|—mÄ×¼qÕg²Æw5¼ä0÷ÇV«²yjq?ùÞ7°.íPZ|òïðÍêôÛx…z¦¥ýŠ×¥Tô†é¬2¸tÎ ”Zú]= ‹QV ¸£ž9k×L›Yã»PxS@ ÀÜÏ›‘¤ùm—9 ôóê°ùŒ@ã Ï`ܬ]=XÉ9É ×6îD>Ê&8æ[âÈ[韓˜8ÒåJž¬ýݸ–T5¾ë|Óî\ui|ϸrŽüfãrà£í"Àñh·meYŸnàÔù"X±yÜ4)©Îs¶ìʆ™P\Z½º´‡‰ÃûB·m`oÎIu(ù>Ý;ÀúÔƒ 7êaÊÈPQi5¸f6Â¥‰}p”Ì®j¾V›G±Ü »³O€A¯‡1CãqoußþœS°6ý œ<[±í£abr_ˆÇ²^ÅÑ/¯ÄzÖl?çpèú!qazJ‚ª!n³ËðߟSáб3 ÃWÁ8ÜýÔ1ƒÔÑ3iDÎ6î„=Ù'iT3Š6\5v04dC•ç‰@\jꮬä„ðÁü UPV”·rRR~ì¹v-¿‰ú ÅI<+)á¿‚XÊ .£*ÖøÀMIÚߊlcíï‹´i|˲}!¦Ê âc PÒé§³ÌàE€ãÝ~ƒ{Àý¦©ê1»Ñ_Ä4uß8šw¾Îƒ¢#BaêèA0ëúñ`CýËÖ}êq« ̓=Y¹p-’b:îËUÛ`ÛÞ#*Ùm†„{—3Ï­{Ãþçè‡ËG €å›vÁ¹Âe¾]—]cZý׎E"Ý ZG†«çå•Áz~äàž0 ûöýG`ïá“ê>̇Få¼GݼéòdØqðìÎ<®îûqËÈÀõÑCãàJ¬_ÏØvêöúlPwò¿ BÀ`ŽxßÎÎQ¥ñgÐÓ^\8'¨ÐXe]5Áñ•è&eêT“ÆLÔ„9¬ý}ñf¨­ñäû8k|_7>¿`®¡ö¢‘#ijJ"/òÀ^±ª·yɺužÚ ñ ¸Xˆ 7Cz¥;­Ç.¾bDuÿÈA½Ôí‡÷ƒÁñaü°ÞPR^§Ï«‡îGyÿž¡}ëpèÕ¹-Ä´‰„]™¹è½vØ|üt>„ 0.!ÚE;88$¾ \2°'’öþÐ wz¼E‡etïÔÚD…A‡vQp_(¥< ƒ{wQ=é䨫“º½>ÔøG𠜛Šu#²çC¼ˆ@÷óÑþ´(R婬¤¤nbçÞE ¦&8´Î:sê:ïZ ýÒ”yóènÉÚßõ4i|ÿðªîWoTúÞ ¡æQ¬ñ]h¼Ùoà4+¤áß+Ñ;ݘ„Ÿ|ÕîŸoù³ê½ÖÓð.i%z“w:÷4ôëAkšD¢JᡎÅbÛ©£úbŠcÂ>ì² Ù8HÛ‘Sçaæ%Ò™GØçíSGÀ7kÒà­E?ABï®pÝÄaHÆõêaD¬EêØ¦ËËWW‹0ný[ì,zäÔ9ˆ …Â’r3… —^†Òr t©²Kœ{1ÄquÍ+-6¬{M\è8WÜë:·i¸k¦ÿ+ké·³ˆ'âo! ÀþZÌñà>h6ü-)Y‰ ã÷ˆyjñŠ<ç_ªËüOE`ûö¦àµZSû{™ÃéìUi|†88eÑa².ÔØjú„Ù,3ì×G ÖŸ ¸[5‰ïÑÓ…M²¬CÛH 󈃕[v«ñÝUƒóy¥)þúÞéc¡w×ؼ#¾Ç8nÇøaŽ" UDY(<ÚbæDÜ 8ÒK gqÝGË];´†Ù7_†äü˜ªÀÒj[(L5P=ìL•VÎbÈJ‡¶B¾1ý3>÷Î)jÜ÷?þ·N=Þ`ÐAÆ£Ÿ­"÷êFüw1ÄquÍ‹J* 2„?øÔ…?o“æÍ“_2ì!»MÞ„¯”ø†¥\“•Xû[àKßE%Yßãº«Ì j|÷ºsÂìÌJqÏ@B€‰ÆZ“<²Qa&5ô£;C6%]1¢ŸêÙÞ‡qÚ"—«‹­0¾;¿°¶a¬¸šªŸâЋÎbKúcBrZõˆŸ:W¤Æ€S‡ÉŒƒÇÑk]‰!* :2 L!Fg~{srUÏùžì\ÕûM/”Èãf‚YE?rR çU÷Åw‰Ý›~Ë¢r¨Ã(¥úlPwÖóp,©¨3{ÂWLõΛý ¿¥oÁ‹èïÂlô„¿—9z´ã"yîX¼~˜«´¿¯GH:¿púéÐnµ5¾ñýδ¹ö[¦1ùæ $€`®¡Æİ{Ç6Ô© ;<6íÓd(†oLF¯³K¡ªõëÞQU>yóóŸáݯ×ÀŒónn"’ÎòÏï7ÁÓûÈcMŸB[VoÛ/}´^ú÷r '1èA=ÅtëØþóýF }rª‘tJ£ÐcO¤ýÿ,U¿îÃØq‡¢ í»füP5Ÿ}·ÞúâgŒ5w„½ÔgS_" ϶&ŒWGNUD¼¾sx»!ý¶êaÕjEi+U–½ï_5(kŠÚ཈5Á«À°[+fà3­âí'-iÉiü܉4¾­•[ðþ<€@\|™~|Ú\ùxn†›(¸ñäÚû‚¢‘ö„o@Ð ÃIH¼ÍÃú:$ë2“”Mj§‘H|i‰Â9f]7N)7Í-öSçF!1HÇ“gúÕ‡¯§BG q]5Á]W;vü¬°XÑFÝ0ÕôØm“T‰@òf“69%ñ"з[ *§$«6˜Cª/¹±máÉ»&c^65E=©ê_kì”ùÿnLQÏQ0^ÝlrxÔ²Áõ|×åôG1]‚èp#èP>‘ì#»æ®Çò²"³vmIÖða÷]ù‰j€×ÞYïۖöÖÈ­&Mð’³yô-¡j‚ÓàIþ[#7Y.¡4£ ”’´ÐM¹úm6Ußß"ù„HãûÎ+çØ¿òÛJ±áŒ@`xÀòô¡äÕ«Qý»´…GNAΉ³n)VowdF–ˆ° ß"Oê<*È·ØFsEõ8Ã$[ãJÊÅ61'»ùÛh^Ÿ ®ÇÐ2áwàHtÓƒZ(Þ]`Lxs â¶¥ÿŒ応5’í?9nX{ç:/xÒÇ—[˜GMUšàb5ƒXï‡@IDAT(çªö·¢\B•Glhꃺi|+²}… ßè)Ôéô“1ì„ÉwPþB‚³ÒÌ@4ÒîÂKäˆâ”Ý 3à;ÔצkM­1œ:Tú"n߮ͪÂtim†ŒK Qñ%œæ¾°Ëô øýå ¤8j¼>Ü;”•)y¦$ε!X¼&:’b»[lÁ¯3Ë’—¢¤T¦å¯êG®Ï±úªÌ’oÖøÒk!Ø«Í\CW©Ð“Dð#fr\ ä¡‚Èb-ÒܧfNQuÊ}aÿ⟷#~EÐ%RBYv ÆÉ¾„s]j/¾°“Ët8Bf¡NRîÁ‡ºú±ÉΕ™IÃf»¯Ω1&8~øÊ¦c±!‚Zœ´¿ƒ ×þß (¯ãË1þD1±Æ· ÿ N˜€{§Ý‰ 8'›ÝV^aµÒz$BPˆ šM&èÔô‚8:äç+¶úµ'¼FE=¼BžoÂk'vÜì¦@LT(„†šÁLâJø’¼v Ƶ+¨^‚扶ò°¥œ½'蕺c•"Io8óVäùÙ— ê\ç#€/ß þ}â,È¡ î\ ¦…ôÔ&#áìLuF\N'&÷_Lõ§ºVi|cx‰â|Fÿ̺0c«qÓf—6<¸¾Œ!ÀÜó×ÁDÛRQYPPXzÁvò14*a ƒÁ=b O‡p•Lþmñj·Å„{¾Ú¾)b¾ßCµ"ßÌvèm‚pä'±$<‰ˆ¾"ÅÕÊ‚¢RÅn³UÓé ⮇𲟠׫÷3Hwħ#“lSå&%á@=œ¼…i‚‹/¨P3épR’CþÈ[h¤|©ŸYmJði“Æ·R‘õ>ðnpÁ5¾ã&Ox”ةƄ—‚ &à>hñвÒ3Åå•RYEÍñœ¡A8†LD„‡CxD8´;eùé3'áƒoÖÿ—lRRšªz¥HÂcÂç8b´9»ëU ###T O•ðu(¡8¾‚’‘ÔÔ&ÖÊJ4Ä+­æÙB¤Å‹-’Pö è‹&¥™b˱Ìÿ½@mMp;Ø‚N’0ص¿YãÛ¿4.Ã_¨Ö„ó×øÝ"¬A9zpÿÎÎ=ãn&mëÚºÜA0)^™<¶Å¥g`ב#a,„â3oBTôµpøxDet˜CÊ(dÇä"ïç?´ÌR^¾ó)AòŒŸxÁˆ¯“Å¥•°î° dƒ Öæ†ÀØáÐ=*¢ˆ„#ž„+á[;ü„Úã…¥SÇŽlB«.ø:Ñ2Kùl_ ·5ãPfbÂÃØœ9ÊWîÏLJØŸšñ±/ì Ò2b½S¨îøóº g¯Òr°¤`Öþ®Òø&¥“Xjo|^)ø%äñ©så·`nµó#X®®'#P&àµñÌ::1É»·ÿš5bÒ”Âm{²£€×¸9½àHe¼smÝÿHH¾Cñãy˜9Æ V‹ù8´z~¹ *måPƒáÈž1\˹Ò8 (ï mQ"<¨@„ %Û…A&F÷œ¨0b ‡†ÀßEyÁ)ü„TPj{¿©ŽØÈ¿•óë~øv®Š¶ÒrõÙ¶F Ÿ–±0+)á l[ò†cŸ/åƒÌÄÄÝñii"<¥¹ð!ÍEàMð¤¤=SS·67?¿;/Hµ¿kk|#ùÆÏ½º»¦²Ì ß]Âl°ç`î9lE΂ÌÑœôÕéhæŸpP˜=½»uǪs‡\û.†“ç7)7#Ñ?ä1ˆ2u†’Ò2$“ж²¬V oG.«ÃÐSH6Ô|ù½¨P"%ÂK¯£Øyür€,ï¯n°êÃà¤5>;`„§' ï÷…/© vg—ÎäžÀNBjû¸¶™Zÿó_ÂÛÆÜWz.oþ,ãdÉþ-êƒ'uÚîïï¿Ô°å¤ Ž/@ÿÅ{ÒL2S̓‚€ï¸&¶¯Ån :íï^Óߊßa; ™ÁBIÒOŸ:Ƕ·qb*˜€{çR ÿ´ ß6\¶mZ¾ôçnq½/_´ò×ȧî¾ZW[+;¿8Öïx èÝ%òÔçvÔ;Éw)˜0줢¼*ª¸Í†$G§$ ä[4‘p•|ãÀ@VBœTNº §û~ Éù`'Žxi4Ã÷Ùz¸b &Ä]zBŠ)Ø2ÊŸä¯ûî‹]Ú)¿)dkN$ðhRÒuV°mÃ7«Öø}£ki™òµ’’r¹´v-ý9yÒ·Û«:"*p³2uê¥+jv€ñ`ù¾ÊÚ&Ûîeã}yÙðe¯ýMߠȯáïLõà?Òøž:yŽe·À‚çŒ#à@€ ¸ç¯¼©! DÀéa£ ¥¬¬¤´X,`³¡Üîð‚ ÈNð*ç·:p^žF¸¤ðÒù&~KW3¨´Ãúø$ÐaÞ $v …6aN˜iÂ>÷LìMýí­‚‚³ÔaÚˆÚJ´.ròwº¥¦få O¼'?`ÃêðM5%»¸àu¬×ý½nZ·Ÿ4Á³’²ñÕ ±o}ølÞt´™¾6l"íïÔííoÒø^>ÿ/o¡Æ÷lõÎI-KßfóÔÉ,3°×9W¬e0o~=›¼©‚|“çG26¯ßÕº}Ì2\¿¾åΫÇJä _—þ ³GídiÀ΄Ó/}ÂB#Àj°ªzD2)ôÄbAŸžêý¶«p¼ù©pAÂkœ?GÞou»<Åt“¬ yÁIã›<áDÆÿr…nú¦Ζœ«xnmümZ„Z]ò|ùÞ¾/G:–uà“Í«~øwP»Ð µ{À„@J=·¥­ÄpˆgñGòWªþNÉ>t{¯m;> ¤zj­.ø;U²>Á»Ó<²Ín·ÏÄY@ð:µ¿—åQõ.­ù¨‡yÅüç?ÅŠÝ *‡·çu¡ÆVÓ'Ìf™A ÏÚ0¯ˆg։̑W•ÈRA@J¨N¡k¾ÿï/ø`"·ì•'Ïå+—ÑmÝ»WiBâÓÓ¦¯J¬Õ‘2‰d¢ŒžJ¼1äfêDo A¡,œêJpòˆW§*ZŽ@bþòå¸oi) «s¬°x¯"Ϋa'äù>rpߢU‹¿øwS›PÛPQ[1G-Å¥f¼œ•84 .¿£º)²ô!êSï푚šhuÕR}H\¶ÀŸwäãÒå¤ Ž˜ŸÔ’î´Å®(wUç¸Úߤñ]tæÈ÷X×q¢¾Ø¾_¹×]fg’Cƒ#ÀÔƒðz€qóf|Ίs"zÄÍ8™p2®ýþkÎ:yæ’‰)7ü²ýƒ(#*zPêÞq<$÷»[]Æ›šJ0‰pR¸yyq€gÜ7‘nA¼Å\=1@ÿ”h.&5¥ªS¦Ø6¶›îjƒw8ž/¬+V’ ÖIFKQqúæõ¥mX»³¡Ðjjj#‚‚‹œ ȨÖ3‹Š úáf þVBñö¤'965õl ÕU+õ!Mp”€\‡˜§ æz»¤j‚¿ªûÜiiÛ¬å׊<%ü±X¤y•Æ÷ üð:@Ô ïÆoOc ᅫ‡˜ØÁsF€¸&à@â‘ ‚€ 8>"ßÔKœÚ@·këæÝÃGýÖÇh´Åu$ÖfØœ:RIßý¥Ý*\2ÆI;]Rm²]{ÝåЀ[$’íšj¯Ó¾ «U9_X®„GK¥R¸d•uRš4(/û½^À8üóxH1NÔ4¹zÀù‚€bŠY»¶äبÄé–J;e*Ñ8u/ûw9))“z®]K×'Ï °³M¡¬YÜn+»«Hú:—–´äôNZ¤Ô Æ÷“5ïËTo® #àN˜€»͆ó¢Š-&W,y[Iß»Xb‡0L#.ÕuëoMË”voúåìù9,â\´),4Ò`0a×TŠ®(ëÑÝV)0‡ƹæ’͆c[–—…Î)¥ýo!I0ÉÛ!êš'û—}ùÜr\/Ä©'jjŽÿF=uÝ’–‰o“dXŠoZ:|q#|çnæÍãð#\A£ ®H3ñ–è@P’zJŸfÉß>…Ÿ  ˜€{¯1]½àDº‰|«®‚ÈH½îŠëäÛq]%ãùg`ßWÿÎûàkZ§I“.…¹;‰OÈÒ'N~€vj-æD¦Ô—Ÿnõ+5wO˜HFF%\yGÙŽÕË‹ö­)ÀUò‚Sø‰ˆÿ®zzâN‹@ÜöŒå™IÃÅ@ðw¨’H¯Ë^öÝ»¸øPÀVÚ‡ Mð*íï3~‘Ãû‰~‘!w{ѵ5¾ñÉ„÷OýôismëÜ^gÈ8*á ð:j©zµ½àäyÍ¿çqeº9º’¡Ø²bÉ"韸Hûˆº†H—V3SÆ£‹IKv¡-"¤DàGXžüìO+Ör‡Þ3ã/8L9{¿ƒ Mñ©éïbçæ×Ь<˜•4ì)±Îs÷"@šàΚàšû²ç´¯ V»ýnq¾Ð-K^8ÚßËçëžù3¬Ÿ`ç¸A2Žcò-ZœçŒ@Ó`Þ4¼Üq4‘pêèG±¦%w>^é;(ë%Ü@ñà`³Â¶ÏÞ7ü #!È+$:hŠ8q†"æÞ·¼V‰mPÑ%ieXo>"p&EN^R™÷ÕsG£]ö…dޏ•l×Eux¹ïÜKÌKñßâ\ÚÅ)H@ò­(7Þ83;û`$ñ"@GŸôŸ¬áÃNÅmKÿ9H`ðJ5 ë@ÕOK[p^? H¬çéÄäþ+Àϵ¿Uïמ'¯÷ïÄ‚K×[EO¿|V>õŸáÄ0ÍD ƒjf|ZÓ ¢g¿ç‰œ™¨>R=]KÎÃlÙ’¼µ®ä[ÄŠñÖ ù&›Û£¹K:å²ìëEÂWLDÀ]I¸µô·ïÿqéí—aÐoœºBL«ç𘹾6šË÷ÒâÅ–s#F\W`­Ü€ª(Cðº0b8Ê79ÃÆ÷Ü–‘á;˯ä@ÕÇqfV·–ÿk;5¾• 5¾/ŸÅßÕmÍKŒ@ó`Þ<ÜZ|Ö›ÿŠjSn/~Ed¤Ø¥7¾gÝëÔ&DºÅD¤[„ iŠ€·F=r—D/ZID¾)QH‰ú²ƒs'Ï]újaŸKgÌAX?¡ƒð?xsÏÇ{¸Ç±Îÿƒ¶[·5tZ¥¶àUÓ§H쓱œ”ÔɱÎÿ[‚i‚cGE"áj‘1gV-úÝŒ´¿ñKÚ2ë„/úþ©ý]¥ñ½ŸMQj#84¾'O™cÿÚï… fü&à^n¤—ÿõ}nM¬*¶L2šñ² \œ &%æ/øÔt„(SR\ú´Ën^ bâ·§¯ÀWÌúz‚ý2¡l«³GŒèݸ­ê55Á•[”©SýR<´¿YãÛm—5gÄ4&à†ªå.X†£î½(rÂÐü?Ý]qX¬óÜûìœÓ±¿E`‡LGÂô{k_±ÎóàF .-m™^'Ý„/ij¨’ð~²µrõÉqÃÚ72-¯}mMðœ¼Ük[ž«wsðwíoú"‹äû]ÖøöîuÃ¥1„p/^çu§æà\ÄÖå¶‘;:Gàó¢\T->>ø+$Ὺ›Å$ÛmÔA–# "Ðs{Æ÷’Nw ’pêC€IPV&ÿ’›”¤v¢vlãÿME€4Áñïq¾üÎËþ2¯SûÛOŒ¯ÒøþÉ÷ÃÂdŒ*\Ò*zì´ÙåÇÅ6ž3Œ€g`î\/ÈõÕ‡ÅâÎéi•@÷̬Y¹eÈ|‚€N9 V”[ú¿±w¤s‚¸mißèå6$Ô×ÂQ—+öŸÕ&èÁi¤ Ža¨¿ i<]áo1ö¨ýM!…UÉ´¿Iã»üô‘Ÿð:v`Gúª{“y€ÑžSÔ Ã–õÿöÄ9±®µùŠWus$Eþ í !ÛðatÜ ÇM›k[§5[ÙF ˜`îÁÖ~ùŸÆ$$ß×T!ëÀ8σÅqÖnB TI0 );|¸ö[ôÆ®nÊš³ 0hÄL½.ERsšª†×K˜]–—d%ãk¦ míOšàªö7(wˆêa õB±¬¥¹Ðø–A™ïtI°BÍ£&ϱìÖ’­l #Œ0÷`«Ë²íy‘=~¦üú©û-{Ä:ϵ‹@Æ£= @'¹ Qÿwã׊^»³e¾D ç¶ŒŒ“~ †.VíP#ÈòçÙ‰ úÒ.*›:ú‹&xÚ¶—#¡íLø¢Ý§“û¯ÐÖ¬ñ­µa{ `~!&nÙ213º²*3tBœdÜ-p&E 2"ìmôjæS!ø°í³ë(Ç‚{p?ϼ떴Lc¨4cÂUÏ"†£Hø£?;iØ èÇK‰ÓÅðMpY’gV×E{Ú߬ñ]Ý:¼Äh&àjÔ³}Bd^’¯žžeÙ/Öy®}RgÅ"mz[X*ƒìDIlã9#àŠ@·M鹆ÐÈñȽIæMM¨¤ólvò°¯pÀžP±çu#p&¸b¿½î#}·UëÚ߬ñí»kƒKfšŠð¦"Öˆã_YÚ ½^7ŠC%ž‡œ`øÑ\g6þ ]—ŽÑJÖ÷µ=ýÈ|6Õt߸1?LÒO—îå¢xºX*Ë×3,Vlãy½,tÙ3ÓeY‹ZÖþ®Kã[ÒI²Æ·&.6‚¸&à@Òò ŠTùGÌÅ@9!ûåO÷ZÓ[ž+çàmö?Ôÿ†|T]®½àÕ`ðR=Ħ¦–õêÕûïŠCp ®dK¹ò[fbb’ØÆó ¨­ ž5ÆÉÏØÿÄ€5‚octô¸Ë+?Á×#ÀøLÀÝÙN6ÙéýÆÇȳo?¯QíÎ"8/ï#@„% ˆ’qÙ¥ÅVž3 #@ )½¢ZOqý2†$ü[zVr´†Ï®½54Á&NJêäKïª.ßwÚ߬ñ]Ý ¼Äø;LÀÝÔ‚o~Ý%$åz‘^¯ÿP,óÜÿЙŒãH‡µ& ŒèÿÎÞÞþ_+®·Ö®µÅ¥fü?FW¯•Êǯfm@V–e&'¼¤Üx#¸Š˜hIœ´¿1lè:çµ¢W:—½¸P—Æ·¤7§Î±ÏxÓǽɋöpQŒ#Ð2˜€· ?çÙå'¯Æi$mÀÏÌûŸü½u»s'/ø=$Iˆ ëÔv¶[m3ü¾R\Ÿ!ÐkûŽ÷õŠn<^SÇÈ5.\VžÎÊ>ôSöˆ|f˜¶ ^èbÎL—e¯.Je¥r¦¨±úŠ––ü}Þ.¯€…Õ§ñ=õIùqúBçm{¸‹/ñó°Qt¸Þ [iuVÒÐgzmϘ¬NÒG¥˜ÿ"3Õ¦rh‚oóf³e\×¹ÕbEeb;X †/¼Y>i|+’ý#ôuÕr%(À÷þéSæØÖyÓ.‹`Ü€¸ Ýú””éaÝ“=h5?$ËJGzðD†™åè¨0Él4jÖ[/C*تÈ7½/ôè4DS£·¹ÿ²©Ê±{'gÖ}Æ9Wxády>dZÛª5Ôµí™ØÇ¤Êïj²ÆV«\PT¦—Uètýó³ž{ñ”Õb{çì¾£[²ä?eh´ ãš´?XŒª"Ø/d ¶Y²Ë_ AÒI_W^ÉNN{lÔ¨»ºnÙr>Xðp­'i‚ÛíU”[Pü1iÅŠJ×c<¹l³ÚfŠü±M–õÿöÄ9±îéùòùº¹ø}É7=#ñŽé Æ©,3èiä9FÀ;øš€©¦qÆC_Ýúï8Šd§½:+Ãö‚Áñ¥0³‰ök:ý°iìÊv˜8fÈõ0vèMÛËÆ5~™xüG‡ÓÛÚº;<ö»ÁÍÏÌógª/­e•°+ólÛ“Ýawæñ—; é5ûޏ'g}úÖ«+ÑåÓñéÆóöp ·-ý—£c† ³VÈ_!éK‡âüªÊÊò]9‰‰÷`ÈJu¨JùÊ®û,0Î?Vpêúm«s+õ†ØŠ6‹:vûèþgGŸBN‘Ïz¤¦ô|Àë\}aÁ¹TŽßvŠqK J• §·Çéûñv†¾'æÍ»©QIû{û¶¿ß!ðÔy)ü„4¾W¼ö—·‘ð?,ÊF¾Ëd0Oe™AÏÿGÀW\x½©ü;{j¶94ü/±1ÑÊÍWŒ€ÞÝ:ªoüþoöɵNSã:Ot.óBà!0¶«#¢±¢›r×);ä—+Ð:TÛ—+¾ÄˆA½h’=_þ¸5æ„¢|7sî³O/œÿâÛØJ6œØ®ËµÛ¦ô\%%eBVqÁËȾŸp˜¤ÄâPã+2‡ýÍd2ÏEox¹Lu« Ï{µK¥Ý69©¤ƒʱÓ]m ¾NVßEDy3Œƒ=4Ä èPJI1…†.ªs«Õå›dµÚ$»"c8¦¨8]ä'¬•ž{é,î;ˆgeè@Þa— õwÍ›ww…(„æ©©NÂYZÆ2N'&÷_ËòhÕc‰4¾—Ïþs,À©¨…Õ[kl=ý²Yù…+˜3f¯#à Nlo—jL›é¶Gæ<‚äûùäþ=•;¯« 1jÞá]£‘òÎí†ÒrG,txh;èÔnhý¼XD™t0¬£ROÚÔ êMÇ­pUo톡ÔF_náÉ™Wé?YºQÙ¾/ç•™sž‘¾ö‘p’Äc^0¬“T!;µÁ× ÿ7’ÈŽÈ#éµï!‹¥lRfbâí8º&Åóûmš7ï#ó){î5øÅsš¤ÓM¨°ZºQeŒF½½C›VСM”>¦MÐÔ¶UD„™ <Ô1Pãµ1·ÙíPi±AQI9ä—B~NÅeR~aiûÜ3ùmOœ.e±É:Pl«·Ízî¥-Ø_b9vñX±à…§v€lŸY]Žçµ¿Iã»øô‘%X¦ó_/¾ìÞ£ÿ],3XÝ¼Ä ¾ à‚|›§ß=kjXDÔŸ‰|ß{ý¥Úv#ÖÓâÇÎT÷ êÞaL=Gñæ@B`zÁ‰€SJǹ?p²™^rÕßÛ7@$ü¥[|ôÀ—{kî"N!)œ4€@ÜöŒå¹IIƒË%û‡Ž êP#ï’¼%3iè_âzõyYZ¼Ø¯ÚkÖs¥ÈòÌ“öܲ,G"©¶õíÞÑ×µÄu‰nÛê1îÛ-è#QC¨^%îÚG×ΓžCp&¿Žç@æ±S†ý‡sÇ )§ÈÖ—g=÷©í'·µpv'„Z±»„‡µ¿«4¾)$¬¿0”4¾§Ìµ?QÕG@læ9#ÀÞ&àtÓ£2MݺõiÛå­ÎZ«žoÅ3÷LšÓôÎ퓜˼¸$vªþÙ¤Uq¬-~q’rÏæ+§$øû!£‡îܹ™>qÓG{ò„sÒ±©©ôyíú¬áCïVdx['½â¤ˆñBVÖ¡iG“’îè–šš¥S4áÁç^hû[²l¿Üd2Úûvרú÷Œ5P‰¯RûÖQ@Ó°~ª^W\Z{²sá×]YRåáRF‡$èVtøÜÑqçáûg=bf•Æ71K â.=6uŽü6<é;lÒ÷úБӦÍÂ'}Šùö·°W„Nœ©þÜ¥}²ë.^P†t¨Ž?xcNÑoê ó«ÓïnÆ”‘º7>]Ùqð„q"+@ä›H8Mœ4‚@ܶ¾ä’µv{å'è¯ QPFY[FvbÂc½Ò2þ©Sk˜1oÞ×!¹¶CoZëBM&˜2f0LHî§Ç8îÇie]ò0rpMÒ¹‚bXŸv~Ùfh­TÂ>ŒŸýÁ Ï,lŠ­_ýµþçC‡b¤ } Hö L‘% —•|‘ÒK:Ù¶§lýCz°Dêñ”^'[‹åöo©ÿãÃޛ=ÛkŠ/M©Ë0-GÀ›wA§÷ÍoÕ¶Í=ƒâ»(þÖáÒòòÊ(,=®n2èͯ8Àu7/(áF z·ÑÃ$ßÔsß9$b\¸?&Š §ßឬã ýïáD±5DÂý*´í øÔã·ßrP™ãÒì¥ß=‰—ÝóUžð”³n2賺oÛ–­ žxíµðSÅ™ß+Š|Ùå#•ãÀlòŸ7Õ¶Ñ‘pÝÄ$ŸÔW·ø§mŽ|tß3/ŽøçKÏþ¡>Œïö…8EÒ GM÷K°“è¨_v¦€bR\?*á”A§“©©Õ®“öW¤¸º¹  ',OÚN[ä?<÷Rþ÷ãëðAŒÌùUÖëW8ï©£õ•ÏÛFÀðkpõ~›ÇM¹u²¥v$5èÏé|Qõ—ß¶­âð«!½cp â«8Õ5'ßî·œìÇß¡„ò„1So¾søŠ¯>ÙŒ›ˆ| O8ÂI#HóæQ»¼|89y¥M±}†*Ô·~$y“l²uwfbŸãâz¿éëØpò|Ÿ,üÿì]|TUö>3“Þ’H Ð{ïHDEµ·]uWwݵQt”âºëuÕUŠºkCET”&HïÒ{I#!½@zöÿμ™!™„”™d&yç——yóÊ}÷ž;ï½ïž{Îwbwâ‘8äñ·Qa½«ÁA ógMP|ýËaÚsüÒ|€ìS«–.^Éq0iº6ýv°ÔܯT('ï=L.Lªï¬Œj,Iý¼=ˆ­ë¾^X¼ÝÉÓÝÍô²€?i´zÒê´ø³RA‰ðMÏÊ-Tf_/h—[Ø6=;olIyÅ<Òëé©W—%iõ†mxáìôðTìxÁ‚ìz7J>AÖ€¬f×@SpÝ{†„GŒçÀæùnv 4¢× ÌÆ¦ _çL4B ­òÔ¨@Óû“’òœÛXŒûïr…! 8x2:“£Š™'™-á² ”àˆuìØÉ+ãÇÖæ-ÁCô/q*,ž¨ë?âbîO<øQÇ›TêÑ”£xjÙ²öºreü¢á’ìj@Ìû ~þç…ß.ü£Ág¶BOé0çf¸©(]I>﫟-€ÛÉ+°|ûý]ãhh¯Nõ¸ºã w-ʸ^`ˆMJ ü ŒHdêÒfé zo€k]ÿnT ¸;µ¡ö!`HdõÜ\ø8v“\1ý}¼ˆË±~Ø( ¢t9)#òRRÆeeO–+tO.Z²«·SŽÞ¤VOàûVY²œ@MÀù"ù{¸º¹‡ûzºÀO\·'”ƒ*2·ÏàJa ¸,­GÌLh‰ùl”t^ažp¾‹ \:¢£Á|È|Ï:wÃЀ–,vïæ~z`ûk­Bû1†KE{ñ©SèŽÄ ðŽ©^C 'g=µ*zï=÷²¬ü±“®{¸¨\zÍ_¼<ô€î’’q»T¸à݆#³N®mÞ®×Q…øµä2®–¡,—n‘í ßNýŒ—š,}饸|%­¾oõtwÑê¥ Žýî‘íTuÄÛRQõþd&^& íõ’*9ãÁ-FuàtÜ´ü¢’;3ô¯Í]¼ôW•ò“ÿ¨ÄÔûò ²d 4©š€óµøåÐo§0—ù¿¹Ç|¼ø™,KkÑ@˜ÙžSl‚&NÛ|¾³®]o‹ð=Ê÷ª¹NÛªÖQq¶t#yϰø¢¼ç‘RÍ–p¶ˆ£õ+UhïE:ûyúUÒÆSê7;ë´ÚiÝ·WdL²öP)ú°CH ¯*4П‚}) ¾ÐØ_LjOMéÙ­(-¯ |ðl—Á¹ Þmâ² š:²tÉó9°{Gʼ–G]:pìD¸’©›Cìwl×F,ÓÇ T‹O¥§bÛœMþ[…F÷âÜ…K¿sqU-øPýJ\sÔO¾¦¬Y7×@Sp~XóÂO*ÂUJ•—‡+Ïh:·”Uä™àéVgÖ´W4Z-Ü÷ äîæ˜AH\?~™ÔuÚTj\y…†®¤åP¨ú @x:õÄå$º}t¿z]S I• ¹ö67I ¬áÓßbò¦ Âù8ßðaõAsùå{Uºo¿q5ôaKÚ\™¼ç %ü^CºìÎ탥´Àø¶Ø!¿xâô_ËÝÜŸÓj´‚/54ÈWß»s¸²WtuëÖ $hðeff kãß’ÔYc[|¼¨G$õù«|ó÷MûNRÒGß €ó±,æÙlã÷Úþ9—@mƒÙiøãm•1ÏÚ59õŠu;éåÇî¤0+ƒ+‹;§LÛ28ßA|ZÞ³-£qÖ:¹oûrúÉ/ò‹uиº-.W(ݺE†ÑƒÃûP߮ܿ²´p 0¿oêpš4¬§bý®^Ç/&¾ž®‹™ó´zÙCªžjáÍ—›'kÀ)4`#ìZ_Ëz‹±º¸(§T):]uÃÂ/ÏR€Ÿ=v×øO›ÍÓ—qÉÙ´åÀYÀzhѾ§{' ¥_ž¡l$Ü#ФW v Y»í„õ×~"p¦Œ0úV®Ùrˆ‚@‘•–•Kç`i×nDЇHÕŸï~µ•:aߌñƒÄ÷Œœ|zÿ›_é XºÙ}dT¿.&ÀÌ/j–U?ì¢6~>tïä¡°ÜèÕ?QÛ ¦ãû?Àù‘::<4õûÞxj&m=t–Ž^¸"ÜlŽÃ `¿ð)Ö¦ðJJ¿ÆV7º{‚±xÆ‹süR"UØ3]Ö€nœÏѶÃç„ëˆtƒíÓ±WÅ×Ä´kÂ:~6.YX˘²ë ƒï †¸þÌ‘ÀÓÚÞžî4°{¤tt &NOÌ•”ÓƒÓFИݨ]°Ñ]‡AðS1äw`þ@žË¶&'/%‘\\zun/€7[Ã%ùi÷q¼ßüÓl±Ÿ·?9sœØýŦ÷®ôÜS)<$CÒi¢Þ_m9Žâhš1ní;C§c’Eqšl–{& m1T¹bd|0~13úV=Ê)¿K÷©SV¾µW $óõZý9 DG>v×Xúã}“•¬'KëÖ϶¾6o† Ï€à±ûá“‹–®R«w5•®u+_n½¬4Дܢ bªÛâ»s®º¹0í®QÊ5…Ҫ鳠¸ì(f.íðöô@“­Û’€ÞJ€Söß‹Ä 3þÇ,çzûÁŠÜ¶/¥ÚRÀë €QI˜¡àÁÛGÑ€Þ‘ýºÒ¥+Â×\ÚϟÑZ9¿°„âSŒq8'.& n^vá˜}ë0š:ªò¯~ô£ |NNí…?7è­èâ•Tê…ïA°ˆÇ\Í„ßXN÷Jk9ÏÂî!ìÏäïMœa‘–âpMè³&¡aº‡ö³‚Ÿñü¿ À—ýá3XÇ­iŸå nðì ·“p{9sU ø˜ËI™Ä–{β×:*[±Ù¥&!5›Æ èŽî4–~¶–sŠiIXo“†õºg r–uò쀶°°8·¥ªZø}{¹1f•EÖ@ói`îÊ•®àƒþÞõîÒÞãõ¹w«8¥º,²$ ð»â©Ù“w"#)^O¦i÷Ë¿i¿ü)k@Ö@Ój )FÀ-øy·7õV~qªi]Z @ cAq¹ôÕôYTR*\.8h‘­À,Ìï*IxHp Ç.o†ÎÇ.$J»AƒU`ZïfмÁ>ƒz.Ó’%$>ÙœØá$,ßœ‰-óß1Z”ÁÇñ™Ý^¾ÙzX¸w¼6÷nŠŽA•ñp—¹t%˜ë–®¨P^]ÒJ³kK$€¬5áÁ‹²Ã±”â^Uxðp9)ÀÞ—þùÙfa½g¿uÊdPß«s;Úu좨ßÞ—Ðæ2.'ò`Ä@ŸmØo*’_B)YyÂu‡7ZÖˆr”[I/4Sd·k¹±l-öÞ­K;Ë1D%ç¬Å$Ôݳà26yxo¹ßœ¥óš¡žläñóñ¤5[ߣHÎúF­6ÌV«‘NIY²šTMÀ¹AüB°\š´‘öº˜ŸwSÑùEFw Ó¬D€ äbbš°ì²UUælíXia•¶eÂWY’¬ëœIMXq٢̬÷L,í¾á“ÝOê"C{G#ò ðõ5ÝÈ-ÏãÀHýÒ{kE &ƒö.BéJj–°"?|ç(áæ±ëèEÐŽ¹ROXÄë"ì’Â’‰6±ïuUaŸí› û•óqÃ*ÝBøxNÙÌVqà·êG!`’™\n…|ß.Æ~ñp7–ýç¦Tó•>u¹¾µú¥˜ÀÛû¶¼c Žnq÷©µþk)Û|+SrÖé †ét7~HÏ–Ò4¹vÔ@e¬qD3ÓtËÞÄ¥^²ãåä¢e Ȱ¢3*´²SÞT»‚ý»šȸ~Ö´.­00DÐ ­þq]EÖ2¦üãÀIfG¹}Lé0ñÉþÖì.ÁV]Á«] p9ñÃoçãaÍM'¶ˆ³õ:;×l¿¡Z¾ ë…óŠèÊbÐÊÂþÖì3ÎuË[û@³Hœ¾=à«~ î*lÉg·‘Qa¢ |<ï³&|nbZ¶(›9Âtó a+LÙrtn}äÈù¸átA¤HÊ £^@YE%e´ƒ‚.ðs;°»ð/g¶–Îá¡Â fóÓÂí *ˆ?u€°ÅŸg JàÊ"véÜóÙfxóA„tžü)kÀVÀoRAÉYkuzÃô¦PÈàÛVšmå0Çl ô†翺ljëhµÜJYŽ£94¢/Ú×®R2¯_ ­¶ L%Ò&áVòço,!o~º Ž?k"u¯’¸†Áâ{_oÀ•“ÚôG@# O)s á»k¶ Èl!|¾¥µé‚µ¬øz{ÂU#œÎ#~îÌñâHàK:f‘­`ka‹2»•°?9D²°•ûÇÇMÌ$þ>^‚Å„}º¹¾Ö„™L˜ñäÏoEÜ9šFõï*H×ür^[ñƒ°êK’Öίº-ãZ>]{ÊU,H-\fØ:Δ‡ŒÊ”¥’y³eP’^©S®D¯P•bS yèrÚkFe«ÕÌ/˜æo†\;i Ñsç 4ÚÍu­Ñxþ´"ŒN8Z‘#ïÙ…=ôÜ‹ÿîß«kÏç¾ _[>Ý8…²r/‰FÜ3nuïh½MœÁ–*a ¶l1[VŸZþÜ?Æ‚/<šØjlÍ·š™Š88ÛRx`À>Õuug¹Ùµ¹¼Ò²ròªt?‘Žg&XÙNÑVÂÁ˜_aVáåÇî~ݬ¿•ßï&ø8>:}Œé2¬;ÎÚWa½°÷”›«¹Îñ×utçׯY/$t=ürÅãÔ™å__üBg/%œùü7B;®aáâ¥@fS?¾XJî}ËÃåuj`¾úÍ(älÙµ‡ðum¾¹~\^UðÍÛ­1ÂðöÆŠVï\jò`&î-‰à6g>tK©/ð–εæ#þý#˜çc&urszð-µUþt °ëÉS¯¾ù_ü6Ýœ6JßÎÑm[KÎÙNý头çñÛz®w0¡Èr3 üíŸÿô.È/{ŒÊ{+´cñúq1)Ãø£hÛFèë¥à< ˜9æ™k6>yÀ¤‚±†ÝyáwWŒsìÉÆ#vY…¡Jö±€kùÅþ™×ò{]/(Q¡\…湋—%Ágh­BáúÕÊ%/Wç¾Y¥åý¥Ú0¡CUÔQ+Ó»ÓÝ´ïôÿüé)>u’Μ‚²Ù5¥.õòó&w k]Α1j ·Ž”ßrÎèy-¿þêÞ"`u,øÆí!ÙÅzúæ‚9ꌮö¸Œ\¦¬50÷ÕåãÍ=~Öäá"ÑWÊ;d ÔQ#ûwQÂ%bþëËã”cu<­UÖešvù_ ÊtûF„èúF‡«ºÁu”ik¶m…«xpíÂà<yC86ìRbZÇ‹WÒŸ×h4/ÀíG•žžûpÙ¢dËŽxêÍ7µEºžJ…¡'FR`OÔ+(…â»>+`Y×`-Iù°°§( †pü¦€¾÷âGêWÌI6, –×m®[ýPl^1g)Ðß§1?—ðƒ¨òƃÏÓã·o¾Á¼¶¶ð(x9ÈÈÒp LÙ‡x±·°YhÑ®b*­dJìÛÖ…Fw¸½õ.—oÖ¿üÓuË!Ó«~ô€nr½Y5òZ#4г2&É £Q(¦Õpfr½šÝÞàªÔ‘ÁÅ S2o™ ,Ô ¥«"OïJ×ÚkºÂ5o¶6]»ì'ÌÜ9¸g'ÃÔ‘}‘u9ÈìŸØˆ>¨éTväLÖ¼ †¢¼B«:Œ Ò?ï99½¬B;džzéLƒN1FÀ[U Õxm‘VhÁã•óf<=Üõ<#íë馫šBÊk4:äëÒé‘+CQPTªÂ±pEד˼EËòqÈ1ó£Jƒak˜Ë˜ý²OzM½Ó¸í2oœþÄÙcûÿ•b’·b©VØ8Z»óºwüÇpë0'œ±Áeä"šQ^Ý]B{“Œ±1lšxq‡6È"k é4®_6Év:ƒ…,²l¥ÒwuuÑiµÚ([•éˆå¬]»VµûBl_­V?øt„’”}àoÝQŸœs°RØ…EųUÙÛŠatI…;¶B±¬ àÔãi#9ð™_M.ì.Êd=¢Ú»¨WþaÐŽ"³ùûzé0R…ƒâ¸]p€`kãïÃu¬:@¶‰í³U€„{pu¡ÔìëLzàŸ”ž3>53w‚Ö`x9]w ñ>›JÅ7“útÛ8gÎI5MÞö–vA€Û GÙ >eørÚ¸ÿ9QÚÕÌôbýÝ÷/4¨ûÃ…6LÍz½– KÒ±d «c—ÞOS@:°­0ãŠVoLòÃ@Ÿ7d«ìLm{#hÐÛ-s¬"_ÊÑQ|®žòÊ ÄÙ( * "¸ÕqåLûíæ¢ _ÄYy))ØSIm‘$'Ì›÷5üY¹9¶‚þu¨”R-’ïÌìACÚ7¬_K«rmœIðt»ßÝÕE?´wgÙúíLçuÅd,pœøïµ­{Õê].éÚCSð¦xpÇÙËwÃíÚ‹ÏÆ CÙ.HÅž^Ý®H8‹2þL~ÚœY™³*a)))ó ðqÖ!Îçñà´‘ N¾× g ® ´ë¤$îqn?/‘0ÏèA«b¦ >›âw&æê¬Â’²ûvM›»péÈÕcÕ*õßrêtù 5 #ˆUS¿}:݃è9´óØqbiym?ö¾ðu j¿‘äç.@2ƒc×m ˜Mr©¤ü:nî,Üä™Èœ™&–||c[C%ȯE†¦ÞgRDȆ§‚@IDATÓìçÅ\Óц˴惘âÀ> Þ ôTg¬ä%ÌWE¡å¡éAØà®$o€vvÃg÷’‚ =eˆ©àºIù7&‰û]owzn„lýnPGÈ'5XHèöOÒ«“Ò–AÓ ®|b‹Ñ³DUTèw2R‹§ÕÿñÑhrŸ‡÷9¸]ZV7¸g” –cŠÐDìUƒ«#)gŒb¸L@À1V¼nér&6Éü.·»¶T©+{@|‰{[¿¿ÿì³ÕÓ};’’¸.2·aç ëù$ù{EÐÖ#¯Œ“ÍÁz}6aXlx©›u½à ¦”®Ðɘ/…ݩ柠!=ŨÞ9ž9›`u^}¢Œ.Ãâm É-5Pn©Ž.ˆ:•NÜõ,˜­èð~´¿1»g=O——5Ð( ¤ë~ëÍÖ»ÎÈP+‹¬[j î*{ØI¥üÍ–å6GY'‘ª]þ'.ïU\?°?’¸ØL/á0n7|&´9Úâ(×äXµJ0®àÜ?í>á}òRÒ[šì‚§ç«—ÎY¡^tÄQêêLõ¸{«{ä4¤™AÇ/ÿ—NÄ|nâ ¹ xYAÝJ¾^íÈÇ#àˆ°v $’ý¸¨<`±õì+ì–RQQ(\T²r/"«%hC F_e¾n^ÑUÚqLM§ãÖДaKEýRŸ¦8'ï%{KéhZuÌΨ%õ v¡¶Þ°Zûº)É cŠ24·æñr­òášr­TO9°bgé)Ì%ÚØõj s}ßÓÓàNá¾ò̽”'lC è·f[$“r4ጺ`)ã¤X¦cÙ€«$+«O¹¶¬O}®këc¹ƒºGФn¶.Û²¼ý§.3=^n˜²ý>Ëíζ>Wýv0‚”×€ªïÖ^Ñú»Æ TH‰åœ­-ŽZ_Îv=ïÞ Š˜¤ úlãˆÜ¼¢ý°†?¹j٢ϵΎZ/€Û¡g<=iLÿçit¿?ƒ"ï %f L¤ªg«8»œTÀõD¥t%Wðƒº—{ÚÞžF°íçÕ^¸«øzµmÅZ¯×PjÎI¤j_O“6"»fžheN^ ­Ù6‡út¾—nùø¹9ƒÇ{¿•Òªãe7¸™xà:\ÛÓ»»ÓPø\{6 ÊìµÂô…J ç%§D/¶]ƒeœýÈ‹±TÀ»Á—<DMð襤®Ä™.]9w™,²šQ–jË—oÎÙh—#›/ ç4 𥩣úÒˆ¾Ñ‚Þ“Œm¶àçÒè“õ{èÝ¿=€gdý_Q’£šê#µƒ}]_zìNÑþwô|­Aßwþú€i[M+—ÓÝ\M9j:¯¾Û¥v„úڀǧd!‹p ¼¿ ªÕ›9Vë[áf>þõòn:}ù.¥Bö ²0îßU¶–رOºE†ÑÂßß©údý>%2lÿoÞ¢åÅ+—.XgÇK¶¸¢ëÿtkq*°_ƒØ‚ÍœàõåolXw&–[‡¾küç´\ååšBQô¹„ï…ùL0µ¸º4¿/3ä×@ï÷E‚Î,Éç¹C<ÈèÆŸ ‹y(âRûU oLÉò¹²šNd¸Y‘ËÚ‹©|›GþpÏ8ÌĹÓS±ôõ/‡h`÷ŽÂWÔ–µÔ#’ú7|×¥t–„Å¡3q4²_—ºœb:F«ÓÑŠu;‘y÷N s÷7mwÖ•²r ýoÃ~¬ßJUàßµÏ,_ÞFWjØ î¶ypªÒAgÕESÖÛËÞž=Qñîš­ ä>{Jýæ ™G¼î= ðºëÊ)d–¡=Ÿ ^QwÑ®KM|åWÒ÷Ñ7Û 9?kVºDv yi{11Óˆ$##\èÕqÞÂÝDÚ&Êhí Xš¼ÄÈ’'N5‡>BáþÂSú¡AtìÂ:q)IXÁ¹.×ò‹èÍO7"ö¤˜ìFw„€ò2ú÷š­ôàí#©˜X¶: ‹óXϦ3—1ÊHÄ,–¢#Bè¡ÛGQlr­Ýö½ñÔL‘8='`ÿ°Í U£»' ¢~];Ðîã—hç‘ ”_TJÝa›6ºŸàJ©åÓ®Ò“~ÜuˆH«Ù‡3ròéËÍ‘Y÷q›ï¿m$ÚHï³uÕÐ;h“ B_~üN¤qßHÀâÚ.3—aÿ|ãš6¦ЮåÒ¿¾ÜJófM´pßo?F'.#™!ê0´WÍœ4î„*X “éàé;°}‹¶÷ëA³&½¡çâRé›­‡éñc¡«ÆÇð`â£u; 9¹…zä>p…ú™¢.èD_¾¿‡wÇ?þn’ ¾›¸ß8K7æŠï¯óå\\þå&®‚Ó^Nž¢qÚ®«_ÅÙüÎÑïÒ„Á M'¦fgù£ÂÜ´±‰Wí¼|ÏîåFŸÌð•Áw÷ƒ|9Ç׀ OáZæ 'u3KR†‘,Ãà ôA•²óÈy="Í7ö¸L§Ášä‹Ì€®pKÇ>/\8Øõá—ƒghö­Ãé±»À…,‚œ0¤B£`Þncƒ|ЋôÜ \`ºnÈrYœAøž Cè¯ÝF)²áž’.qÓÏÛ¸‹Ó€×tΛˆz?÷ÀT  Ö~¦§Ú»“(ûž ƒE}½A[÷4vKa9—BÛ³h;ËeøÉ•– ×®Ñ <¦Œ0&;=°« ·™)ßÖl9ˆÁK¤Ð£ØQùï*ô½ê‡]4ah/›€o¦Õ{÷«mú˜¤t¸ž(žX½dÁËë9Ó:\n×éõã~7e¸2ª}ˆ3U½ÅÔÕßÇ A®í”-¦QMЀ7’éÃ{Í£©à,—„Aø³ÿ–¾6éçæ¸ ú ƒ’<—“7&x#%—,²d TÕ€‹ÂU ºœ¼‚ª»šìû†½'韟m¦Öî`nC}»­Ú\Ý"iòð^$ó¾ ñb¼7nt*æªÓY× (=;†õí,ø–Ùw5ƒ¢ÚÓmð)¯*qélYŸ5y ëÍ™)$ЄÊVð=:’¿'±?êXö›®‹¸‚stÆøÁ´óèEʼ}ZÐ_38MH͆»;àÝiÜT’3®S^a1u@’–( ºv ë`× Ø«™bëÀIRb’2E]x{׎mE[‰~òðÞÐSo1Kpn0’pÊñ{&†…°Ð‡´ù§?øv;¬ã]iÒ°žÒæÆ%gÒÒO6èâS2µ¥bÎÊ% ¿lpaŽp¢Â°($ÈO7z@WG¨M«­ƒN¤Ó4È^õøÈʪ‡²ZÊ¡»=+M©‰³üÀ™÷¨s» Ô>ÄÈÀßíÌ,2ÐëÈ,) [¾_-ò#H›äOY²,40®oçK;ÏÆeÀõ#Œ-ÍÍ! p{ÀÕbΔa Ê"^Ý’øzy æÄÈÄ4¼OgúaÇQÓ¾a¹îV ^9£ »‚ì=CÓoé/,¼Rü™ wéZâKå¿õ8ïøÅ$aÉæipdK]©æØEdÜXØåƒ×%¹œ˜!ÚôÙ†ýÒ&b_×”¬<Ç{˜¶I+ìzÂ>ñìz“–K†Õü×Ãçà2“Cqp§a@Îî ì-}“Ìï?#öIeõé!­š>7ì=E%8Wü¦õ\))+§(k×±KlõNRº(îwvú8µz­[º6fèð{3Už,Í£TÇ^ÍÐëu´¹yjàœW•¸sö[£kÍœå );C‹Á€d7ž¥'îÜÚdA™/ï(¢Ð²Dú+é•1-/sg£;I.@Ö€…8ô“‹—~|&6ua~a‰‚3×5µÜ5nð·v]w7ëE>°"÷ë!Ü2’3¯Ó°J7.é¼i|¡7ì= |î‚èÅT<|²dÂrÞ9Üì^Ààö—ƒg鹦`ê»=í:zAœ_Ÿé3Æk÷MNo¾…‚üÍÏN© ù3ʶTð¶+°ŒW•QaÈÆ«£ýp»‰¨f·öI?¿îìÜžš~Þ~^°¶ç›NçÙfRapI\Á0SUØOÝ{öIïðt}eÿ©‚ÿ¹®´\’Ň¾~n/½ý ÆÑM} s ãÓ)® ’츴 p Zµ®ªä ÎaŻ؞ëåêýZëj}ãZ+» 4NN}ö£Þ1`æ&‚·ü‹&iÏžD N1ZÇ8/Â?&û4ˆ^°I*+_DÖ€i@¥R}Âç}°œ:“Œ‚ ûG'¦åÐpP²¤d抠Eθ7¬Ò¢_Ä3èæ¬Ÿ[á+ή"ì'=ß8{ÆÖtt4¹rÔÍŤº.ÚbÐIX°¥ÃCA ëA›œe³kȹøT±›2ÙÒ·¶Js_°u¼cX0±o{/ Xº#Û"Gºs“Õ»Ot¼|Uø…3‡2¯÷C’˜› ü™‡ˆ²þ÷³Ù*³ó,÷9—`@¶K {6~fäW¼5oáÒ[8iåqζŽ1"âYšVüÛg&¡%ÿ¬ËÉ-È…×]ïªÿbäÿðmø*Ksj@¯×ÒG?ަÂcÿm#Þ¢]ï·[•ާk題|äœôfç#~ÔÆËüò³Û…å‚kÕÀ¿¾ø…Î^J8óù;o>„™â‚#ý˜nC‡ÅªÔáÞ·zž¼±qø“ú=?­¡ø¬¿·GÄ¢?Ü¥÷qã l‚³5Z--øà{ºuDoÁbyIXf‘ìæm`+§¬á F%ᬙU­æÒ>[|rÝXªÖ¯Bø·+Àn®K]¯Çþàl=´Ïy]êÁýq&6®;õÊdöŠLŒ/ž_µdᚺœïHÇÌ[´ì%½AÿwÆRl=ë÷ãNŽ=H¤¥ÏÜ[§Ë4Uâ&k•ɺ^† ­iÆÚ1õÝÆVo)>“Ay˜ZE*Å·«Ô OÔ·¬Ö~¼Œ|Zù/€y‡E’ß΄ÕzÎãJ'×áÓÒú=³§› ¾ë 3ùY–x_ýléi&,¬ºëvé9¸ÎQ…i/$¤»NxÀG|üàê,UÁmMma_rKðÍÇÙ|sù\7kõc·™†€o.“}¾›|søúÌÕþ·G¦)_zìŠ kŽù¯æ-ZºaîßÿîT†T.ÿAî¹×îÔ±‹“#‰”¸)¯ÐL8ДõÛ´ï$\’lzIv›^{ž±êÙοç¿4úãO½ºüØ“‹—ÝgÓ‹µðœ6“S ŸÇƒ)ªøÇ]K‹ý £ãýøå€€ â)^kLu©5[¼ž}—J‘²ž}ÁãSwStø„ºœZ¯c’ t´7ÉØOìûýÄ@÷zoy0a‡_æõübÊCb¶„µ‘¦­Ü¸ÿˆÇÉIz#ÉIÕ€±Ö¢ÖØÎ–¼r|þâeæîóeŸnt{zÖD…%Æ£è„-Åk=ú¾6pë˜Ð`Ðê(íi‰õàÀΗ¿C¹ûØEôÕÑ;źÜ'.‘3ßd溨£Á ™S §îß$¿5žÉ€+h2‡Ñ–ƒ§kÏàQ4É¢˜7¾jâ¦eœ%,ÒìÂÄÀ˜”ÁíÎÜð,k¶¼×òŠDœÇ3¿›$8ðÙ僓3m;t»nH$ÕC0ó9Ö’FqìÄæý§é(’e1N8t:Ç#@Ze‡Ë¨0ãÐóQ”WhUœLkßÉËR²¿ž·xÙlO•×ïeð›kÕ©¸É˜·u'"Þts»¯§ùúx JÜ©šróž©Ã¹åt#þB$zàŒrÌŠ0þ‰Ì•ë }Ô•–‰ÓÑ÷ïr?ÖoB&º­và;Œ—Ü´ñQ®ÔÁ¯îÓ·ü ÓàeÎý¿ #°@´úþ/¶èNòÁ`œæÜÿU-…uø9ɇ8™V,Y¸v®zYî…MoýocèÌIC•cA§Ç/HGvaßcY_œÉœ¹´B‹4Hðõu&aßdLpˆaH¯Î‚a‡9æ­Íbتmlüj ²Ÿô3ᬭQÀCÜˉ›@χ„Qƒa(12íì=Kg‘Õ”cò‹Kè dNؽ£à·ç̵̱ÏÜöwŒí/â.ä§ŠY¤’² ôÏÆ% êL¶Ds¼'òòt#ÐTÚ‹wã׿B–Ù»D¶Ø]TõíÒ\üÑH`e¦’ ®|,ª§c9>bF™®¤£Z½k¤Z=ÁüÒ·•Â[P9NƒZÏ#©ÃW›‰À¦xºmdo¾´µëå,ýÌ~Š—31¢N¦õbb?lβڥC¨Pž?k¢)iÔ»_mì=L‰ÉîNÞF×Ír4ž5'jƒAGsH_$“bV Ø«Y¥*ðZ:Á¡8[?|­Û~Œú¡SgOÜ$~]µèËáwñàä™Ùãé»íÇa ?)¬Ç ÂÙ%¡vk¸n'“èlüw¢q)Ûm À÷^Õ®Ò<Á´ƒA^5»JVov9âþÿ€RîÿºýôDÿÏ2öÿºíGq’A ÂêêŽT·«ÈG9¢V.[´õš4_½tXiYÅ3Ç.$Mýí\‚Èlƒä-ºi£ûªØÍ@Y7ÓûQï8rAWZQñû»›ïHûç.[ÖÎPªÏ ÓÏòöòÐMå{ìÀn*æk·—Ôu¶‰ýÂÙ*Í–ø{&¶Zš‚{­]£¶¤QV o¢mÛø+àzÓ¥‰.ç´—©Ý,ÚŒÍbÆn'ßï8.À׃ӆÉ໎ýÁ70ë‹Aë†}g@7uUøO³E™õZ“t‰¸Õ´+6åWÓº-Vv]1INˆªÙú-oö÷æzsý¹rÿ×½,ûŸï¾jë÷º—,é àôâ«—.~Ô5Ä'RA ƒ–”*0™ä0ÂÌ-Ð&ŠqbYK ö†õé ¢XÅk×®µ›åØÖ­ž÷êÒéŠRºŒŒ¢÷Ì?ˆÞüãl»vØ|×Ök‰›Øßû·óñ" [ÄS³rEÆÔÚÊ©i_mI£øæšç|eåš&%*À=­GʧúÖ×ÔкÝa8³š|…ˆ`v;aË·,õ×ë­-2·­+*.5ðšJêÜþDnÁqæõóTXœQÓ¡õÚΖo¶€K2˜5 |s}¹Þrÿפ©›o—úŸï#‰%èægÉG´ Ì]¸tŒ&§àBéæöïÖÑðêÜ»•“†ÙÏúͬTó—ý®f0…üÍ…i ™"­ä Bl%Ì{̀Ɣrg‘ͳµH—Žmyàî½ýl‚ѹÙÁ ¼'ô:ú)²}/þª¦î×ìF;N$U5qÓ¬ÉC™ö‘Þ]³žyószû‹-ˆmkMamI£¸»ÆÁÒžœ‘K~û+Á]Þ]È÷_|J¶SÞ[›âzÎ| ‡tAaÆl¹øQ²ÏwMS2άø¦¨;ëíîqýiÅ{E`Þ­˜VdW^¬ù„»ºx!¥òHº’¶GT/1c?õžÕ語ÏÔû€³´÷UR·6Ö *ÜïìvR^Q!ê›_TJ÷O*÷{À²ÿù~š:ª¯Õ~o`ñòi¨Îh¨Ðf-!ijAþ>ú‡¦BZôv5û{5C78RÊ?8m¤`O°eÔ‡Àè`JÆóñipë;V‰ÖÜV%…BjË>²GYs/óU}»†æß;QeO& v‘ÜGø]ºbác74éé9“nøþ»©Ã+7'nzjö$ßÄôÞ`rcF7æ×®*CÁv‹$ìÂbyM°|ã©™ ®ž4 YO p*ÅŒ“öf¢ùróA=\mr”*ÿwí}=g/ßá8O•óz7˜<˜íB¸lÜOŒõÇz<Œ€1º²|K@¼jé!CL<'ÏA^õ˜ú~Í53 ³þ““ú½à»V1®¯ÜÿõÕtõã¥þçû‰2kê÷êgÊ[œM\´¤“FwmÈØzMÞKÖUs%|a>c ( Ö°s`„ˆsÓªÁ7”Vÿ°G ´9°ž›^~üN«üÇÉpMÙyäñ@\0Á¢ÉœÕ,L»ÉÜÃÌB!Å:ÿÆ«òëñ>9x:>À=u³Op0ÛÄ•,yz¦(‹Ýa–}¼ºc”`™¨Ðèwt„NÅ\÷ ÓÉ ì)êXTRF ?X'¦˜ZnéÇ? Æ!)ý'ë÷‚BÎCÐÅŽI®víÙ· ›÷ŸA°{R×·‘¦G¢±õø§R %¶Ö-&õ(Ëþ‡Þ ðó2üáîñvß m‡5ÿm$øxy4´ÈjçÕDµÈྩ~_ŸmØÇD ¥Bù̇êgŠªURÞpƒÎ…­ ‰©Ùâ¡Ë¬²4^¬GN㜜)¬ËZ¤¦e=[“`ÿn¦Í9ù—MëY‰¿f¾Vt­Öo­¨דë+÷c´n>—õÈ †ï«šúÝ|´¼æŒ˜÷ê²I…ò¤—‡k¿<4UÁÓÜ;YÌg¼~×q°9¸Ð#¶ÈÚ)ü½á›+¸êùæ«ÿÝ”á¼*Žgþãó )&þã ?oð'¡¿>t’—èà²rJ‹ÔéôËÁ34ûÖáôØ]c˜»ZŒ~];ÀšèŽçF¤ØÎƒÏ 0E\„µ}Í–ƒÈü),‰e ¨»–_(ÊâÃ\Î ¼Y~‚•ûÈù$;éI³À$Ĭ ÌáÌì¨_sêÎFËóøÜ$+DÛY¬]›ópï1»ÒüÙ…»ÌÞ“¶1tˆ‹Úð_>ÚÂââ¢d–‡µÚ gù-#útváß›,M¯v#ûbÓA×#Ï_W.]°®éká|Wt¨_«då‡0Ú˜çÛ™~O˜ú )—«Ö;æj¦x á!Qu—Ý¿³YŸÌÊÙ]]]AMh%8 «©>9y±¦õƬÄå_l\F—Àêc>©ßù…Uk×Óû¿¡::›*^æý»Ùgj[꾯"‘”¢v6œ†¶B>¯¹40oÑòÛ1²Zß!¬Sت@€DG|{–i–œ¼baµf«jt£»]p É¢ÍÇTå?f@Í •© ó³‚™$4ÇÎݪzvj/Ž«‰÷˜õ=w 6MãßÌOý·sñë@Åw# +ë5-'ÏÄ©\RZ.®[Û¿ª×Þ°Í‹A°¨³ô;ËIp@߆v8š$ß‹µî~ž¶±ÄØ­»•¨§¡¼re·ËÈ[Õ@Œ;+¿ß¥µ"^îŠ×V-]øŽÕåÕ4à€\ù"‘Ღ)•j­¸É†-ÏazÒheà2#’§ßÒO(ÞäÔ:ïf·™O7 Ù°˜ °¤b’2‘a2‹š€s›}àgÆIlؽÃ/F7€p¦&d—K ò넇® é ZdéJå©–4OËCê½{Ý À£ƒªÿäŒ\‡àK¨דëk«þ·¬0iýßWÖ^~?c4uCàQSË)LU³ìÀYœ1–ï+ɬõ{S·Y¾žm40ÿÕeSá"°>ºC[՟´6Õm›+Õ¿”ŽaA¦“|¼ÜE nþ×$UùÙ‚~üb’˜µáézö/å…ÝP€ÿøØ¿÷D žåýa‘®=NnMÂÏnI8…gß,ë.í«Ë§õ«*–×fk<ÏF½ùé&óa7>‚ÍÛ›yíbbšTÇßyþùÒf®J­—çl‹H¾ëðÙø‰ÓÇöW5•»E­•j%;b¦èÛmG´pãªP*TÊ–ïúu|u4T¿ómz4O3@ÈÃÔ§—·¥pj×'î…3´ýÈEúõ·‹ôÐ4ãô§-®Ã/ˆ×þp'€­C©ÔÔ4?è³>‡eåå‚ÃÃ]'^lUƒ1 pt„çä­ß×ðÙ˜„<Åe_p®0Stô¯n—ú™:¸~\O®¯=„­Oœ3AÍ¿гÑÌ ƒÄ÷ÀæI\`vV-“ï'¾¯øþb}Wí÷ªÇËß_O©ßì¬×é¿CÚk‡߬½úºXÛ'¥çÀÍä,ÒkOî]G/ð‹6c¿Ü2¨»H3ξܼ=:¢­˜Ý«©×ØõE›K1`°ÍƒÑ”,3 "çzdçH‡×ø)ÝC–ç5f.³Œp¦¤gkº¶ Äd xc$΂¥k Ù %•iÙï\/®_Mé{}–Á‡0·°ØT<Ï–sŸò€€…-ó[/ßÙ“Q\e¶½d:žýƒªA=:ÒԽȕHÒÅ~J[‹ì¤ÃzGÑÃwŒˆÏGÀÊÓ¹M±byYë÷¦¨ƒ| Ûh`ç™ËOèôºþß5Zå‡Ù½–&}»„ ¿ï×W®§åŸn‹IwSúó>zö_ÒŠu;i*èUÛ"_K]xCƒ|‰-èìâ¢^±^¸"øš $s ¾ƒ§yÉꟄ6—Íà¹}H½òþwôéOûx|ÐûQŒ(/½·ø$1 ;jøÇ~íœ æç='é¹~)Î;kñ>©á´&Ý̳{_ R|¼Bý‚C`Z*ɧ¶ªÑù…Eqï}ý+­þq·!1-Çòy½àûmË3¤^ù£öíÏ· P:õ:ÈŒzºxE­Zºx¥ ¾ ÔÊSìcfl`}$W{€ƒbX¿Ùz”’1ÝÈA“‘”‚#7X8PæÞ‰½á»m úY¿û¢éÝàÏmtMðÂúÇë÷S¬Ù®ŸM »àw˜ŸZvi¹gü€j-F@ ÀÃvd¿h±Ô<Àñ—Xàä? 惲9:õû(7ßþªÖ§Zá ÝP9íÊà[rE°VT€_”isaIã’ñd›­ìÖÜOøBR¿sxZX˜§L5p¬ö§æÙNž°€œº$½;‡Ó˜þ]Äף瓈ƒo_JL‡õÜ ÂÆ€±î˜i9 ^áñCÌÀB*Ã^Ÿg}Ëâ¼`Ö‡ Ý›ot 5ÀJkeN©éÛÆÁÝ–ÜÄUùŒGô&^Xø²<–·U=ž]Ažû [ˆ%ë6ƒfæ6æ…g¨ªÆ‰Xã=¶äPàûÏ™2 - ¤+i&bw$iúÄ»CÌñ½Â3e,¾°†¿üøbKÚÆ…¼XÖS*§*³´ÄðÂl)žˆÅQÇ‘äû_tC…Òƒ–9R½êR—ÕK^9ªV¯í—ª‹yùÔ¥¤¿¿è‡™=pJ¦ ”]Sj×"ÇC0³»€ÅâÝ“”¡…±8Qa^:„ì/<½¾tô¸€Ú[é8{€380.PŽ%¢±¡®”xÐŽØ >…a䇩¥ô ’$A2aÁÒWj_¹ÎÁq3èº ŽÚøäl =U˜%, lÁM•}pÏ(SÌŽr)Ñf™-…_ûëqÓ~~‘¤_7ûZÖÇtP#W'êøæ”ô¬f£Î«bzºú›®T®1×É´±+E• xø_êXAªׇo~®Ÿž­Ç•l¨µ }á!fÝxãÅÌÖqnƒ4ÝÑÖü»ñFà™4#{5[P­½ÿínsE««Ã¼Ïk¢¿kîw{\R.ÓöÈÐ.ì²a“†Õxhû+7}‰ø¶våªà[:†ïźâÝ øÖäÃn­ìÚê)իꧯ·}â\ª^§>ßyF÷ø¥DhP±xÅ¢E©õ9×QŽU«çT .oüíŸÿü¿‚ü²ÇŠýùøä¬ÎpV6t‹jGý»F(ºÁe¨}H»ÙؼÚü>`ðÊì6à’×ÂÝPõÄŒ[l¡zÖœ‚ï}½Mïêâj€1H‰™3Q'1#Â|ofuU¨¿ØÎ.&Xß‹8€"~÷á’…Éõ¼¤|øM4à0œëÉ?\±Ü¤Ò ÙÍþzsjIioù0¨fj8I²1ÑüpÃÚî ÀVZÍ¥cù“-ëÁ>ÂWPÚÎî ’Hö'á~ÂÀÜR˜ƒÅ²>–û³Îºe‹´lÒµµòÜÜ|L›Ë+Ì|¹¦õX)¬0[[}ݬ{<™û½²~¨gsˆ c®‹V«gî[JÏɯV›Ù²[“5qwwA‚OzéÑ©Öv7É6Öª¤ë&¹ |»h@¯PLE ¡¾GT;ë7”]®*ÚÒ5À–Ï/6ШžœÔ¯Û;«¼Áo¿ðO)ÿϼ翾l°Ak¸;6)}Ö¥+ib*ÅÍÍEÕ.XÑ©}ˆ2.L!¾Œ@ü@¸#Õ—óÌæÒ²óòý]͸nHHÍÒ#Î agŠ2¼NN9—0*ÀÇ‹f ØŸcÑšCx`…äX˜`6¤ThÊ·—”µWf+`uÖV*t:C Ú ¡"õŽÇ¤Ì¹PZpFv/±ooYG ö½f­¥3@hnéÙ>Üq´ÿt<‚2Û"ð&V¸¤t k#ªÆn(‡Î$ˆà™Ù·¶ZÝèˆP:—Fý@ÅÁž±ë¾ð,\8Ès:‚ö¼‘ +¾j=p]{‹ÀjÓ³»«ÑŸ’ëÒh 8Û"*ÅÇ8“+}½á³.õºá;|az3þù½ÈAUmEÛê2½;!H >áûOÇaÊ:JXÆ™gOšRjë÷¦¬‡|­†k}Ø9ÐÏGÁ`ó¼Í^uùLÕ»=®\·KW^®-r%È™;ÖAë\×jÁÊÍ âX岈3ÆV(”#5ºáqW3GÀZ=÷”éí¬÷twÕ{x¸¼=Ü•ˆÕÂxWaœ¡ÅZpM•”êó‹ËÖä?„c´À³ç z: ¶_ Ê-+_›[:ñÒw¶>÷Ü™¸Ýäa½Tìš$áêÚ†úÇﱫ9t.>•3¯êrr 9CèQr3Ì^Uç™…õ½¬||=5àp¼žõ·ËáÌÍܲ»])‹ý¼èÛ†ƒÍ¨®~úyÏiášZuݨ¾á?•Iï}»ÖpN¼ÐQLKq…¹œ‡A¸˜s [Ì»?ddßšñŸ‡›™Ž¯¬Ñpó€Ê×­ÙgájÕ*÷ó´ÿÞøûÎÐèþÑäŸu£«R­Ô²³'8än;t¿©3bêsÜ ®MÀk©¢¼Ëy4àÅ34²È°…8îæ£u; °Þ€ãý¼±øŠ-ÊuÔ2>X*ÚÇm\ÃuŒBâ;`ê³³^¡ìŒ9âNE¥AÅeå~× Eþ…-R¸áÀ C¤Å²Vã|ƒB‘ ¾4…ÁF®ëäwöýgŸ½!;Ó*õ<¾ÄŸ‘,k[vnþ’/7ôõÖÃz°Þ(àš¢`‡ðÐ@<ø4؆yë™63+·¬j¹Â %5[ Lê*x µ Ýï¬xcÁf®Œ,Ž£‡àö´ÌMÕ‡x±&x)_ZîðÅKP˜­ÄR¼àû»ü™»-7‰›ç­?Í4mãH{¦³»ñ|s¶3N“üÂÃØë8‹‡›ñ5ÕÇT°W¬éÝÍÕÂEc;÷êÜÚõmÜÄ‹»Š9ðJ:èP2 %×”ƒ³nmî3šµTñ"‰eÿó¶Çî)íŸpÉ ?0ù7å‚òYª–+6Úù_sêÝÎMkùÅY®~3µü–Ë-´±8îfÅ÷;éÒ•TÒ²=ºâ…[l| ‡/®ÒÚŸˆŠò²‹Í jün~J½tÜ=fÄ^͘“”Þ–jñ7øy¹ë0³¥éƒÊÝMLàfÌ×PéÀ1Ie ’e·9è ŠJô`æRÂ"o1Wè”JÃAy<õû½Tž›ßQ?‘•,ލ‡à¬Ž ª‚ïúvâÍΗ€w}Ëmèñ|/Kz®I×î®f x£}À-ì¾®Ö1ƒe}*Ÿ5 mžMΓ€±M ³RÇ4—Hºn®ëË×µ”tœùqJuÉ-Î¥ÊE´2 0YÀÇë÷ÎÁUHïÉK~ÕÊTÐäÍýH½è0.ÊË+õÎÏ+ïŸë>ƒ¾c~Ii¸µ´C™?`9¬îJ¼¬ÙŠ-¦‘ñO‹(¥"¼#óÑwØœrÒÞÓa¿‚Ö1í¨K\ej“·M¾`ý5àP¼þ՗ϰ‡pC#ý¼>8pÜáN¼ùYò²d ØZ2·µF[Hy:½1£'7G¥ºÑ¾>Mt5ň!Ð^Y²l LE¿êÞ²Ï6ìÓkÁ/‹¬ºhà8žÞüt£ L9˜é·bébgg¬K³åcd 8¤dîÝÒü•ÒjËL•pQ6ÜgÙ½2Û(VŸCYd Èh¼:¾¡O]IËQ~¹é |c5^¥-ºðRÓ׿¦Õ?ì&AF•+—,8Ô¢-7NÖ€ƒk@àõì Îb)1—T=•ÓŽ>—Pu³S~×éÍÞ.. àæ6IÒ÷9³”k´‡þ¯I˜ã;1íZM»åí²lª•K~®P*þqøl¼â?ïC’-羿lª¹0“ðû Åý¨Ûsü22oëÃǨ~IWd‘5 k 95Ъƒ0™ƒ;7Ÿ“eqZãvÁ~4cÜ CV,k”MŸn8@³' uaDµC˜÷;69KpIWÛéD N¯®5V(A[g/©K“Ü\Ìç–›½Zêrª]Ù®ïÝÇcÄ5Üà'ÙÊnØ•†öŽªñº<ÀúzëQz}ît©ZŒ,*Ïà„MÃût··1aSÉ;d ØH«–,ziî¢% €¬ ŠËôHy­lN–5K.Æ`_oX½õ1W3ÀH§<¦TºÌ[¹äåÓ6(Z.BÖ€¬h UpÖßÐ^‘H¸Ò…r•pËÁó´õÐyz´ ³¤gæ…~íw‚¤e«M«³°~«Ü¤æ7èÓQ>’8šœSÃ?q×h**-G–Ê+ô3’ïô‰nOžày·&}£Ã©û“m­‚okÇËÛd 4…àŽòâ¼ÅËâ.^Iû`ÉÇ?韸k¬K÷¨vMqiù¨$a¡í¿7púq)òáø’°|*ÑÙ9`•å*Éh•hÙH²]êƒ4ðí‚ýÅ’œyŽ]¼*κp%]¤ŽÌˆ?ï=”ôatçØ¾ô¯5ÛiúØþÔ»s;*.­ µ¿¥ÄôkŽŒVÁ~fª¾ ¸+l;|Î%¤Q)’ù µ- É zúÞqb³!žKHE2•pVDGÎLÞí¢j¸û ·ÇÍÂÉ©ÜhTw”fR›f;m˜NŤPúµêŒ$I?î:EíBü(· „~;—HMÉÉÄoád,åÁ»#ý¸û¤øð@Î3‰[=-;Ÿv!“*Ò À®BæÂöÁ"éNÖõBú~× d-˧6Ⱦy÷øä0z‘+â|€;ʪ¹‹—Ÿ†üËw¾ÚÚed¿.t/fêøù&KëÐÀ阫xçœ3Ä'g)àjR Ù¼\¼þʼnXV.]Ü:” ·RÖ€i ÕpCç’³s‹èäådÕ¯³è>Ðq)Y”š¯Ý;p·Ûñ>–}§b)é{9Åx @6®6þF~&6…ÎÆ§Ñ3sÆS2Žù|ÓazjÖ8òóö ý§ãéRbÍ™<”ÉŠÖí8Ëk;‘ŽVÜÌÿ´\ÕXnñ C/‡’ вpß—` uâòUŠ@J` s¿œû-•|`&ïA¡¾Ä.(Üÿ’§í÷;O"[™ Mß›¸¿9°$[#oœûòãÓhO€UêÁiÃÄîïwž WúýÝcèðÙZÿÜ}“¤SåOY ÒÀª% ~S«ÿÛ7Ýòêá3q/‡tÊðÞª[Gô&÷ÊL» *X>É!5À>ÿp/¡Óxv¸œ¤Í/,A1åUļmP…|Š4èœÈAY²Tð¨ùkˆ)2‘Òýk9à­)„}¶  !ÖÈî‘mM—e€vïÄÞ¦Tõ’uS: }@×4²_´Øt%5Gjþõvð%÷ëÒ!Tì—€û%X×{v £@o±pÚú³ÈF6~p7qœ=þ•ýª0  Kº¶v­0Á€Âe»ÕÁEª‹ñ““²Q“ôqY9­‡¥›kHçK3Æ÷ǵÍ.3¬³',ÛUõ”WXB×ò‹èÉ{ÆR—ˆêÚ1®TÓaÜÿ÷Œ zgø„_À`Œ-îlEOÂïÓÏûxºÑàž‘´úÇ}"!°rðf*Ć+Ü7ÜW’®mX´\”i@­~œé‹Ì[ôÿVhÊ—nÜwzöŽ#ô£úwQÝ2¨µ­!¾ÅšÐjªÂï¾ë*­–Òàך•K1It&&Y_R^G–²Ô@ú-¤R¬™Ø§ëúÊÔêu-V>NÖ€¬fÒ€ÃpË‘7|p¯få7‰JF÷laá²ò‡}ôÚ“wš®Ý®'Ö„­)YyPQ¦Ýᡲͺul 7†“ÂÂÉVtoOê ®Óë)!-G€°Ó±fÀ– t{JAQùXI„c©wéúåÈ|)‰«‹§´Ú OOxÞHR¬‘lÇÒã`Àü͸æ÷Ž‚B3 bÕý¶úèëE/=:U¸\¸’YŠCä…`ܾ]ÂÅ%¢Úµ± ¾yçÕŒ\qL8Ü—X8ˆ×@s_8/^°‡Ï]¡Ú,ì¶Â/Þµ¿ßùŸ›~½à†óM;m´×êj¬+i­ßmt)¹ÐÀÊ¥/Æ¢¿›«^öVYEÅ_w½È@ܵGT;ÃØAݺwB¨ªÕ*páîã—ðüŒ×ß6ºŸrÚ¨¾VsÖ—ÓéÝ5Û nzÌ®)1sª ð#Ìjµ:d!Ö‰Ï |—PJf®>;·Pg‡@ìÌç‚~=)T뽂<~}çùçE¶4™ÔÛYr½[£€[*ßÏËqeð®àÄrŸ½ÖÙ·¬ÒG.$RÆ53øgmk–Òà¸$˜Á*[E%á`¾G/ V”¨öÁôôîô¨`ñp%t$\]¦ê#n×OÖcQY…xÕ L—fšêãílZoÈJ°§Ù <»¸:·V¦'ü±3 K›¬ÿŒöÀ̳¡\…uZàÖ˜N¤úò¬Ëu¸¤„cÀÈ 9E¹’LÚ>ßxXÌ„Ü2°I9w¸¬°< ÷°5…ˆþÇýäçeœ‰iŠkÊ×p ¬R/<š<ø§åËÿ\Qbx3~O_JLïßpÝžQª^,(wˆÀr6jœ‚óîc……–ÝrXv·ì?ÕuTßh¥?Ì-E6í;¥ÇcçzY™æÛ”Ìë]Ò²òºjuI›WF€+@H •Ë” EŽV¯;EÅY•JqV©T ¥—Ôj…ƒEÕ´”Þ‘Û!k i4àpœÁPdX ºpÖäLÿk{JaI9¦õò¤Šaq¹,,ž!¾p/0ëš®*\Gúu‰Üà±ÉÙð6-²¿0¦E€%ƒ{KfÞçìoÞ VÒ(ü±ÿ¹+‚ôÚøûÔt©Fmg=âÁNAÞ®bÀ:®ÍZTjæºöñlh ö6𜒚3öIuâO®gµR»÷?ÏFpß3¯ûé˜døöëŠë¤k¦ªdº·}'cé¶Q½E'[­$9p*žØ‚>¼w'ì%0ß1¬ y{ºÓŽ£iú˜~ÂOœgDx`/‘úŸï«ÚúÝ^×—Ëm~ ¼¿`ßÔoÁ‚úù‹—N-.)ÿÃÞ“—§ÁÊì…€b}·ŽaŠžÛ+ú·©Û=`‹V±b‹ðŸTÊÇ@ÑW‹ÒÅ}—‹ç§¥Tî‡û*æ›m‡•sgNpúß/Ô¿Ü|ãIð`T<¿zÙÂ/$=²uû…·ßöYvs7’W¤ÓäOY²œTÀ% ˲XC¤Ú€ƒÅ›SÝ#Át2¦/y0cIdTßΰÔdÒ{ßîþãƒzt¬|*ƒiö»]½~Ÿp9+Ô¦Žè%®±nÇ ZýÓ±¯÷Èí#ìÀO"H‡ù¸ýàÂõü6qkÍ,*Í2mööl0 ñ2û8f—X·€KýÎõâúq=¹¾\o{ö~Q)ýû›`¢Q‚ù$Døl[ãw7)£ÊÊTßþ¼ç4ŠMVóNLIÂþÿ[@iÉl: ôt?rûpp„ÓÃÓ†Ó:b2= Ϥt³ Æv¾ø~âûJÒµ].$êð@ÿóMø /sW®tU¤æŒ+Ãm—“2ïaïv#77]xp ƒQe{ HyPÐ(F6äƒîµ‡Bbª¼Ë‰p«(#to ÓŽYiÊ[Ó®R—œ¬ÿD;ñv¥2¯Ï]¼ôù“—®¾¿bÝNÄ\Œ'+ü¯xT0 Kßz0#áA§øãê%‹Là›ë_Ù77·ü8Ccå:Ê5pS 8 —€ƒ¦xëH'2ð Î!K`sÓÕ〗™ZãѺu0_JqßúÓLé+±‹L¡20 ˜A{_á¿·bÝš5iõG&ûür†Ì_À3>ex/Ƙkœ­!l}õòp·øbýáåJá¾*¡W%\jX¿¬gIç¦U®Üà‚âRuw½¾‡x™]x¬¹ Huú]ª_\W¸Þöêvÿ©Íˆƒ$«JÕßİ^Q4¤G$itº|Åt3ÓÍ+ÞF~^"ÈóËÍ¿Á½)Ipþ=¿ð0~7è{;2Tû?ƒv½i¿Wm¯ü½ek`Õ¼yüÜU¹¼ôGõ[í+4š[µíÀÄŒký®f\€Á£Éƃ‡›ÂI«¼==D1.<ÝÝ ^<Ïtzø.#ã­÷û2ó —A÷u,ë,2uÂ…¯ß]ðäÛþð¡ÝsÂrsÆm+ñÞmBYjɆ>˜·x©ötLʇoýo“áwS‡+»thœaÀ²|{¯óìêæý§éÀ©8Ú› ךùH¿ÎÞו˗5 kÀ±5à0œÕÄÖO•ˆ*êB—S®Óú=§è™ÙÄ4¾£ªÒ ¾5ä—¨ ?t~Q‰ð¾.¸ÆhJÂ@˜-£öv©øq÷)ÁDÒÞ×–#a=’tÌú¶&–ðÆº Üh·î²hÙïlÝâzr}³Kô¢þœã¸ýϱD«…ó@IDATîÊê·‘™DóŠKÅà+ 1É™¹‚ÎÐRßöÞ|î¾¼àwÎ÷ÓÍúݲnòzëÓÀê—ÒÐêÏ*¡€ç,k;8c÷ßµ¤|¤Hå£,sõ ro]œ_‡‹:•ÊÏ ×XÃw>Ë …Ï7ö[Öð§ŠÊܽÙþ^ŠkíšKåßÿ^Õ þ÷û•0zù*û0Ç8YwøÆ£ÕcaÛ¶U³¯\²hÅüEËSÒrò?|ûó-b¶ñŽ1)¢­i| êë(ÿ˜±ä$h A3jÀÌæöþi†»ªüßx_ý,8ºe‘5 k µk :rh&H–P ÀƒÑç{~¸½Ó¬'sVJ¼BIC‡vÖè*þ)£Åÿu>v|¿ô½ê犥 6‚ç|{š>í%p_¿ ·2ë‡÷í¢Ö«³˜mªzNS~gz@Ûðg7\JÊÐk4ZÜnÊd˜cV¸¸¸þ÷Cõ‹MYùZ²d 8¶€³šØšÈ¬# ÀÜAÍÖ!ÄŸròKèL\ ÑÍž<Ä¡-á–]Í4„¼4‡°å“Aë5…¸k)ÓÄî˜&v¸e½²~YϬokbK x/%ÜaÚbP°ÕjÁozƒTíwc=ÝQïR*®(í -¿9Uÿ3§»=yÝoP`•/–ýß5Ô[ÜGué÷*ÅÈ_[±ŽM½»¢\û T`NѪPìÁ@þ¡þ?¤â\³üò–êa=éßggsÜþ¿Fvêu¿bÎw¦(eƒZ­LØðãÿ°»2ò\q¡SH»ÅD§ø”¥’çüõ?©ß{§BWx_FNÁ\ø­fßõà@_]×mUÑnïÔ>D¸ºbÐikáØv«áÌÉ`0m®!)=G_ZV!üí@F—MH°á£×ì‚‘AR…­«"—'k@Ö€kÀöO§F(CrE`€èÀè?ÃníH£Ñ€m$Ud¼{Ü»ù„7¢ês*ûü²Û [>Û¸VP¨— téAžÐ%ë“õÊúeKhM.(…%fCMc]Pâ3Ï.6Z¿3ñî{#·Öï\ßÒRÔ_Cz}…áœiRîÿÚjRÿgñÌG ›¸êÚïµ—,ïm ¸òX”ǵì’ÿƒÕÓp™06YA:@È×yj™B­®>e¡˜_þ©š¡×ë?e/¼ÊÍUÊÐ{zÏ9_aq]ÙðÓ_pÌX± ®+dP>¢Ø²¥Üò˜ÚÖ+Ý8Vá˜UO/z«§†´Srr‹Æåæ?t&®Ò/Eað÷ñе òW†ù)™Þ¼Û"æÆ ŸžLæ8ŽÓ‘>+4z0X•µX8yV.(f¯çérò‹ %eå¦÷&ί€ëÍYîãÈÿ~ÔUoøõ?K&‰Âðo…œ^R…ü)k@Ö@ ˜$U¶7ËW~20t…Ë0oo°![a—0?rSæQ"¬+~Ø+²UD"‰Qm›Œ'¼YRÇ‹2Ï3SÍ1k.º߆¹–Q€§ªRžHð€À),¬WÖ¯äŠPõ…%é 2òY{yaÆ¡Ò@UõÀz|ïègà‰y ú«Àkê÷r¼5Z …èŠÉ•ÊèrßÈý_]ñUûŸ³vmã‚Ä;œØ£ný^½TyKkÓÀ©éa½srJ¿A»ûHm3Qž ü)í mRK›­~nyËe’Þ ûPVz¯œöró¿cÂ_2oðéN1¨§N£[*p¿4úÄ sf*iG??\úÒEÊË¿™ÊﯾÕK«×rºæ—v-Àþó.€ÙØ_7Š+Óµ7gIËF¿‚%A”‰@ì‰JWÅŶ4ê‚Z=¡iÒ6›ê$¯È5Ð4 =(¢-F ¦èƒ>à>ÞÞTp‰È|jˆzo—BJÉ+§„äL4Ùxáƒãü|<á^áPMi}òThXŠ0Ha»v(Ë)ÐUWòBâÖ¡¯¯yûx‹uÖ+ë×È„"©ÌÕ½–oúäÛÙ´Þ˜•Î*:žn|G%æêht‡ßµõ;3)À¢†Ë—’gy)åj”/÷¿èŽªýϯwE¸“¿Ÿo½ú½1ý+Ÿëü8:=tžÆ`xS¶.€ïïT®žs®OÌ»Y ·þÓu¸N¯]çˆ*Ç“%F¥ôž2á/y7œk?Þ%¡ ïs<®<Œe*Žwö Xv³òëºÏ6eŸ¯\ª6W½ÒKé’¨*w Ð’ÞÏñÄ,œA©ÂðA§Äùʽ6ÏÇÏ5ï/½TX­yƒ¬Y²l ‡C­ìŽÀÁbìÌ[v?ÑÒÊÂŒ…¡HÖPX¦£"ÄÛW€ðzY±4Y*€¨ ôRç"*²™8À(n!í¥ÕZ?KÊr@]g´2+*$ï ÃñÕÁp­…ðx׸ åþøôTéȽɖm77PƒÁÒÍàÛßß—üÆü„CŸ¬WÖoMî'× ,¸ôͪP§ý‘@†•r%ŸßÕåfýÎûUª2ru_®„JçKõ*ÒbzøZYú½þúãZä”ꩼÒ~Å6Ô"qPõZÞ¸¥´0Å´ÁÓ7´nï•ÊYsñ‹Q¡ï1¾!Œi|=xÆÃ8èªo¿Û»ÎrùŽ©3wt ,§²Á?~Uã½ [‚»éÏ´\]—Zoú—[_­F³ÇŠé2ü>“]Ý=n½õ¹"sBÊ‚ óÈ0¤òk¹«Êð¨b÷îÊ;°.WkÜ1«Ôó8]1/©+I>[Ö€¬YÓ€Ãp“5@ÑÈ-«3j#Hƒ‹Š ³y µ¶F ®YØ1`!åEòãkœJêwvêÞ¯M'„?ô¬i½¶•rPí¹ð\=Œ..¡C¨kÄ­µRã>ÖëE©4Ò º‚…}¾½½=…”Áw€¿¿XgPÎ,#5Y¿ù"¹ ¦kùÙÆÞ)ÀÌ~pkR×~/+/àLKnèw_Dv6¦ßOehiû¦B6ìëNa௫\>ù©éЈQM›™ÎÜï\Æ€ª±ýnjˆ¼Òj4püŽvcËå_wwßÕ…«ò¾Á?¦³KÇÿ³wðQTÛ?3»›dS©I ”ŠŠHBˆ "U4Ïöì]`ÃgžÏ§>ü?{yö.*J bI!…–ÐÒ7Ù2ó?g6w³ -ew3›œûùl¦ßò½3Ùßœ=÷Ü“¦• û9ìÖÕx¢æwÿŽJ$5Å·ewã‹ RR’ìª}ŽØ¾Ósúfl$k5'&À˜@‡# ;N= „v jˆ°ÊÔYwÑ}‚"zXÐ ®ù££Éœ"Üy®/…¸gl‰,ÌMI! g ˜ä}¡^R– }{Îa±M¹\;‡%Š ¢Y‡eòÇ_Óú¾¿èvB–oÍÅeý>þàKÊï›ïî x™hëù€/ù²ßKkTX¿ßrë¹Å$D¡¹ Çq¹RSûÞuA W¼Õï-¬_æ‡Ô´4C¦åÇÇpÌAñíz8Q¿6³ÿ ;›4ò»æ˜Z[Í7ˆ€~£_dÊp¦q=X»½15550¯¤ð=,O{Èðß×ú¸IS@Ö‰£ž4··™`í…€.8Á%1F‚’’°öÑàA §ˆ~Ì5(Àkh^œf•l5T+¨JMõ¼s§ˆ&çйÓ(¨¬ýŠlÕ®É+þÆÄ< ´d75 .dÑ®·‚:£Ç $ò§¹žOâz¢tØÝÜCðÞ8ÇN…äòŽ¢n/.îõñU¿–eEƒÁÂ#Ce˜2$}èëûÒ½NÇ[Ç¡®Ôœ¾w]ÔÂoô{ «Â—ùM“¢â²ª×¾Õ>ÛUuI:„ÿnpÆö.ví>ÑÊφu«¶W~‹çÄÖW ’aÂE³mÇTÔyÅEOà㯠îħ¬ ¯»^:ID••Ïǘ`þN@·\ˆ Ú:šLœ^ #…~®$¾µšV«6å±Rç'.¬ß4(Ñ©4 ^ÐvïÖµYEŽ=óNXþËlŒôA.‰¡°t pÝIó¨3~»^LhúvšA’ÜKè…B ’œ\’ðvN¾ã´|Ëã%ºÄ”UíÓK’Œù¾Ç;µYûiph/á»ÊœÌ p9°«ËðÖ /_ôûVȯEWœz¹¸ãÜ`èáæ&Ó B'؈ÀëEjnߋ뚳]'yªß›S>× dMŠ¾Þª*Ïc ÂD+p å²ð÷¡‹÷Ôf³\þ|—pKÍ‘UhÍ>¥î+Þ‹ÓRgÚ:Ö%ÃÏr8Ô™®c’<«OVVý`×^aL€ tºàÔ$4D¸<²ŒÒº°‚“åÛŠòÈœi’÷h~àB„û¢Ö ð¨èîÍ,²;\d˜ëræiו­Õ4¢»>i>ÄFûà¿d'6d§ßô¢B¢›&¡p†$ñúz/óÃåùxÈùæÒ)´^ÓtküñòûÉ Eð|tC9ž§ó½Ùï%‡ü«½U`ÀÙA)]vZ œyЏAÛnΟÎ.Ðü¾oNIõçz£ßësçµöHàÏKbºVÙìoà/„—ˆöá{§ ÿƒ¤vÇ3ͱDg¾Ö3¸¸¬h)þ›ªå…1Âñ÷Ê«P|¯y»/ ““ƒ«ŽwqŸöïßo²r^q?‡×™`‘€®¸è$<´Id¢+Š&¼qà¥S|;j’ %_ ð Q—ºv©›ÿAìh²k—48Pñ9GÙ;Ÿƒ«â>ƒñÄ¢xPr²qúÇ“ÇYØ4!®­£0ìšPt‡©Õ­Ó€¦\Òäsúw‘aMóô?8`|ÂÉ/u÷d¿?™Q Vôû6 ÿv,Fg™~^(Î êdyò5<#ÜÍå¿%}ß0·¦my£ß›V2Ÿå²&÷_iµ½u×ü´µ6H°]R W'/+Ê:Ylo÷6/¾_`IiîW¸ï\Úÿ‚0ø|Ë„Yç`÷“ëÖ-’cžÕÏy¾Th ¼ñ§ñ.&À˜@‡#àœz…„‰JeänAV^œ}Ìå÷M¢[o±ôEo†¸ù2Ӏǖ¤)£ž‚·WLD‹~Ô*{acþ«0fè£'ÌJ1Zбƒ2žfÒèàþC›]{¢»œÜ ï:¹ +§EÒ­æÛµ¥äø1gEíðT¿/Üj…‡ƒ@Æwr÷~r|ti뉨#zù¸RKûÞ•AWˆ%Ñ¿´lm¿7±h>ÍìIëeÞo±ÎG×¼»Ü«wÏËQ怙½ï?Ùzækɦ’²ìÏñw£ ]çJÒý(¾IÜ3åK‹#äïñ^Ñë×_÷Šm^2&À:2¿࢓„ð ÑAÉ]x‹mí€þ¹YOÉïº%ÉlKz@»ü÷ü`pÿÉÐ;rØ ³#" .b»%Ëý‡~w]Ýõ ׺'VNï^ïó½å@óÃþŠöµ´ß÷áàϲkA69ûèæä@Héã6вĹŽ\©¥}ïÊ +žî÷fͧú /˜\l±}€n"ÂG›j½ý´oH^\´²¹MP?K3¬(øüc´qL×â=øèų”ÿÛ—‡FŒ?b¯!q.Þ¿ŠÏÌy¯ñy¼Í˜è¨Ü$„"¬­jï8ƒ,µ-MgôKƒí»—Aî¾ïµ,Vþ2 nžô Zû[&ê›_µ Ч-à½Â (Ay­ ¢¡ôl4%}sêÜÜ~|-NÞã@-€–ÐÅ3F„ e½þ¦9e‹sëÞµÍÖô½È—L 5´ð‚5ëRÇã(–]8ðYY„JnM^\x°¹ù«jº¼bþïb~—º®•¤'/ž­<åÚ>ÆJ©½ö¿øÐ[;$I!0ø¶cœÆ»˜`–@½s‡E Ÿ†§ž9£–„k*­ÜkržöYå–íÄh,Î_¥CqfÎs7—=ÈÝ Þ 7”ÖVäã?já—½N«;½0=}~0˜Z)¾[['¾ž x’Àæ Qñ™Õk×áìeO¢›ˆS|KP!Kò)K‹§¥,i‰øV¥•óŸx e¼ZÔGã,˜0[yLlk™—’4¯¹^ÀÛûmØP"¶yɘ`u#Ó„>„GÁ…ÃþéªLæ¶·aOÉo®mo®¸»ŸôhB––ÔeP÷ú\Zâ†Ò’2ÉõäÙ õî®7 ‚ÁQõõhIž| Ь‰Ñ7ÔJ*Åß>KÔ ß3 ¤!ÉK÷×G[œ{¼åŠy†çq6â›ÅqÒ¯¤>¨8ýäÄÎF˽ÇwUUåu±¯ù0!3縃4Åy¼dL€ t4l×YŸ)$ÄœïªÕ2ô ·Ûk\ÛÞZ)>\?3ÊÃ0EO‹¬wÑÙ‚‘P|‘æ|_Õ6´ b"דéÃ|åÒã‹Öq™@椞Ý2'F}©¨Ê[èî¡ÅDámC+õ£ÉÁ£F YRœßR>ËæËópXûtq=õ~;uVÃâ˜û²Öa}ÝU¢œû¤B£9ôn÷ã¼Î˜`N,Àux'´…+JÑÁú˜=ºxv¦@|zÞü˜"Ÿ¦.Ùõ¤©¤ø<#5)j¢ªØGWúØÞ’´ cL^Vü”´pa‹ßp—Í•ÓÑ•e–‹‰$}œ:û7£5Ûù&ë:Ðp]O®Áú¤‰½8?ÁM}ׯ?"¶yɘ`õX€×³ÐÍš¯]QÅû× ð¦LÔX½ëbÒµG,*ì)o±F8iñìzrRD|‚ÈL‹Èœù†`]‚ÕwíýR”Ù4T‹íÝŠv-Ÿ+?ˆ±¥Y` ¥EÁÃλV’Ò“,ˆ–Çǡ»Ê‹õ»¥×ã2²›q¥þz^cL€ ´o,ÀuÚ¿¾tE)B÷»ÃéæÒ)´/Àìî5*I=êý¯3öyÏ Î®'^ëBθФ:`©Ü¢*p¨ºœaxÁÔaKK¦77¶·ÈC,WÌ—g ÛÉ3bó^Ñ7ö´+ÆŒYs•¢¯8Ö÷1&¬6‚ÝUv„v‹¼_äÃK&À˜8š ð£™èf¯\QöÿêjsŸ¨®uo¬¤ô¬à™…'ü^oqñìzÒbt|¡ l›<0 }½ßÀIuV uŒ¨"º„|AƒZÛ[ä!–ËçnA÷W\o´|oŽì;mÐß¶XÅ9Ç[ææí|¯=[;.I60JWE¯^]u¼óy?`L€ p]ß¾rEÙSü‹‹Co/ ða=\á‰!à œ]O\]É+í€@öĨ±•Jé(pÝ£‘”`8Íi^ðê3–ínµõʹ†kTEyÅ=½µ™sÖ¤ÈÉcn(8éèïüäd|cWÿ!Pc'ü–“)¶yɘ`Ç&ÀðcsÑÍÞÆ®(Ë7ÌôpTö–Ô_z[€˜AuFð½å Wžp\W³û]OšŒ/Ð!M×D… ¯÷ËèWý Šï>¢ŠèÞñNª3(yiÉ"±¯5K 5x¥CRÞÆ<´ï´|g€¹ó„q³ŠOjÁ.=:Ô¡:pÆMU{¢ñÚã'MÛšúðµL€ 0ŽB€¸ô´»+Ê‘ŠNÐS|h ÔÚ*4 4O§P×w½WÈñŽKŠ®wCÉ(²y¬v=ñJΨ dOŠe=¿£¯÷øzê´Jãl’²,_ž²¬øò–Lªs¬æ,Ÿg¸\}·AÄÝ>îâ‡Ëu~ã}å¥Ï£õ»íGw˜2)P¾FJ?ñ`ÍÆyð6`L £`î=ïMW”Ý%nþß‘Þõÿ¨Sbêx¦‡båz’ÄîÞ¼ô×;}½ÿ«(ÊhU޵Fq»Èd‚AÉKö&öµv‰–ï4”…øF•ÿ{°!ô‚sï,k’KKnJÒ¥8`ó·zÜ¿!g—Û6¯2&À˜À Ô+¡œÄ‡Úž¹¢üY°r÷}¯Uæ‹ïžþ½æÀ‘Š(­¨«ÕiIn®CÇÁÒJ°ÔÞ¨åY[; Vx½±µ*$V8CîÊøÏž–݆&´ØC—ˆøbOgœpG›‹Ä9áÎpžpÇëÉxŒ@Æäžgƒbý°û×g*–%uFòÒâë÷µ~mù³†ËT‡òæ¤Y¾Q|osÈØ13*6%÷=#‡ÄX­f»ÌÚHùqbL€ 0&h™òibæ|šg \˜ò4lúë(-KF?ðPØž¿ÂÌA¦–ueç°èìÔ­ZeïÅæ4‚±ªIÑøµ_—ZZæ‘òZØ]x*,5`Â7³ A°ÇOœ;ÈÕ…Ð;üëcƒªyÅ÷}(¾]w-Z½—B@ЭÉ_î*òdV×0£|"Ä7æûg@@ðùÜ[YÒÔ2òS†Þ×¥óñ¹Â·uÃß²²Êšz=ŸÇ˜`N,ÀýàNøö×-ðÅw™Ý5nŸvÄÅtóƒZû®Šô’4°·öÉßw¾Z» ^ùü˜2*Æ8%¶&Ä}W#.‰ ŸE8±§P§£øv³zÃ*S€éæ!_îÛ{ü«[vdÅ\ÃdUR>C+»3¨Û‚!øü1÷V75ǼáIC»ò”8­ßOÇge­Û¼dL€ 0¦`ÞtV>?¿œá»ß¶ÀçßfÂýzAÚÉ` |^¿(^NîJ ¿Í‚¯ÖähÓD¸Ñh`k¸_ô`û®$´¼ÈvØñ:`ìëj©ø;Íèëý†kŸWp’Iø,Ä—šøF+û_²É|þ˜ûªö7µ˜=#Gškk-äç­ýä†â;#.,âŸM½žÏcL€ 0†X€7ä¡›-ßävòÅwYšø¾:u¸nê¦÷ŠÐKŠÆ Ç“.ùq3tG'÷Áýzƒ ýäÙ%Eï½×>ë·%­WKuªØ¯so!ºq,4nüuá÷ýžZ_9ß0Á¡(Ÿc~N_5 vUó˜‹î«n–o¹Õf™!O£z¡€¯B)µ´æÄSÔ{ª œ`L =`®Ó^¥¨&®øYs;!Ë7§æ n%G*às|‰‰ëÑ BCÌš7øW„æÓä+ZJ sBtZµÅöZ £D(¼J Þëé'"Zb´“T´|«uVkØh 3öþêB÷óN¶ž›’x±ª¨®–è£~Oü¯9;NvgL€ 0ãpùÿ>âkä6ñ}ÆŸp¤¼¦œ7„ÝNZØd Ÿ:j”UZ`MæŸPkµbô‡æ–ÒÂ,ù2&ÐdÙ“{÷ÌœõÆÛþ ãz׋o>–ÍÒi8›¥Gà ºWlå<ãx¼Ñái´Ç!炙ķeŸûy'[ßyÖY‘X÷·Äyøâ°(>3çM±ÍK&À˜h¶€·Œ›×®"ׇC5YÛ0ÚI4¸l%iò 'Ž¿l-€sûŒJD¸¡ ˜heî|986ÌIQ7+ŽÚg1bH„Û{eYº#yIñR·}_]>Ïx‘¢:aÙšøÆò † 1ãfXö6·0©¶êMü—T÷ò nin|>`L€ M€-àG3iÓ=dý.Øw@³ÚRdöœ©„’Ã^o"q¬¬®…¼=ÅuVp;[Á½N½c9µwZ½¿G—7„øÆ×Z[°›ã#{>R¯1&À˜@k °xk zðzr?¡iÙK+«µéå[’õÖü"øysœ k³w@`€Fž‘gžE‡`Ñ÷9PUc…gÞ] ûDÁ%c’47/~Ȇ¢eеSL}£µ™¦!}§ÛRE‡Ê $]JÑ’<6å „ Ü?âôxX¼nœ ©gŸŽ1˳`Çžèg X‡žÚ>¯ykÉÍúýÆW?jã)ªðÐ ¨¨®šZ´€c„™ @ç`LòçÄZB€¦‘?|À2Gu¨³ÑˤþF” Ç$oJ\\˜Ó’|›{ †ü›âP>Ä×Iíÿ9>n;ÝNƶÀç›ÊÎÏßñ$FlI¢uü卯(«WI+VÔÒ6'&À˜ð Vžáè‘\EEú)Û!cV·$Ymv +wÖ¶=0áœÁÙYÈäúÑ9<bcºBx˜.¿ Î’ ñÅ÷Ù(HMpÓÔs0ìa„6 À¸Åðåš0*y̺v~' BQ=lP,ºqØaçÞíÜ3úÇ@"úYqÆÉ˜ÈNpÍÅ#àohÝÞô×øcç^0 ÈMàôgO=ët­lÊŸq¥Åù½¾kÿaøduôÅp—ž?,µ6xkñèN+5E„ù_†žÒÆyšöR°5¯é¡Œ‰§ # Ôâ´õ$À‰³È›ê‰ 4‡M¨sè@õ8›å£x‡Ö‰o©F’¥‡SRNî+ñÓË_íP•\â[‚íF0º¸…â» yÈô_@°À!˳ûflò¾Ÿ˜(—L€ 0B€¸N:šÜ!„ Š'\#Æ< 'Ÿ‰ÑÄ*EUÉÃ;"4Œ²]%ªK8T£È&ñ;bPºi@ò©}¡-áGʪ4‹9]Û¿w$„At—0¨µ×ûp[mÍ =~ä ÍbM(ÏMê"º tA±Õ-¶ãËMß³»3D/|! È$Sζ݂姡5|È€^šåü ºâìB«½Hƒâcàœ!ý`8Ö5¦{'íECkÊ’·U Eh×~iÌ›r-ŸÃˆÀ–´ØèŒ‰Qã„:«Pô:ß`q?ZŠ4‡¤,)~FJ÷Í5+æ®Ç‡ïá›±Ø-ß[‚G]ô`óâ|‹ž-LNîfW¥÷±]â{ae|VÎ â8/™`LÀsZffõ\ùœ“á‚â Þ -Ñ”BÌÎHdd±>VÊÅ‘TÞgßd¹“ÏtÑárèÕÌèâ±lýÐt~á!¸qòÙ®óhåt=©-Úäâ²kÿ!G±Oñ·ƒ1¯¦¤R<7º›³Ît~ϺuòÛŽ…®Z½¢ê“ ÌñÚtÜò°œÜ|èC¼91¦PÓÓ嬌Wo³TW?ç»…”K2<˜¼xÿ›Öµ«o¸¼EU•×°4ÔÝÚ Àf ¹à‚•ZRü å¥$½ƒ®41Î륃醖äÅ×0&À˜ÀÉ °?9#Ÿœ!¬±Ú¸@íOëŠ5›Öµä#Nét?!÷‘ƉÜMòöÔ&ºqÊÙÛÃ)†ÅyF·Y%×ãäxEË`öµãÑ7Û¯|±VœvÒe·ˆؼcŸë¼¥Úz( m‘šØ&q~ã%©#Eßè8€‚9ÇoLŠ·Ý dOŒ’•ñÊkøÕ÷ý(}ß7˜¥†.Ü@ ³Óà ÷6VÌ•1< ò"ÞÃNñ záÓ+ê.jfñù)CïÇça]†™ªª,]—‘Ñ6á’šYw> 0&àÄOþX÷vWg!½iF‹ìªÍ°YŠ–er?éÝU³’ÓàGr;!·’m»Š5¶TŸ ›s!åÔ>š[Hç°à2'‹w(ºªÐd7ä‡îî>Ò S’ÐÍ+< ù–SÞîi@ß((«²ÀúM¹p¨¬ÖáRrI¡úy*Q™dõF!åߞʛói6]’91ú?°e5ßá„:c‡--¹Vß>lú²yò=x¿äßd˜"ÎoøÎK2ó$˾–ÐýÙ~™9+Ä6/™`LÀóšf&õ|¹œãq4¦Ç9­Å»O‹ïwÁÓï`œ!òÆÉgÁ5©#àsˆù F¡Ô­S( üûE覡ùlQNH<“5YÉïJ­×øÏÈÁ ðé7™ð×k×›ØÈ›RpPŒKÖm†¯×n‚ÇoÖæ÷pe1#²L:oN¿ÏÙ¤ ½jüÍ’îI&”—ø¸ ç&Ј@Ƥ¨)Ö#ð†ös͆…áøjÑËäéð¸ðgú¿°ÓçQAÐò=KQÕy¢ªøHþ AÇ™þb-L¹Éɪjÿ m )æùkœjx´…ÙñeL€ 0&ÐD,À›JϧQdúP¢h#"âmSd’¹wO£U-Q<ìé­Y¡)ò %9ëš‹0L MÛ¡ý¢('«7¹žP‘ÃåUðìßÀŽÝ%G•CÆöì ^7N 7H.(Ó”QC°žƒp·¤¹´\;á̧œƒQYèSƒPDÝèÿîm }×OI NLÀ£~ŸÒ³w­ÝñF™Ò cIúΦ;’–îÝPÒà/6–Ï“AñýoWY8è3\í:᜾Z®Í]q¼ï¤qt¾`”Ê&¸Bú%Ëù ¹YñùL€ 0&ÐdG«¤&_Ê'z’€'­¼M©—-Ò“Þîûkqð&Ååv(ø#·Å0@wtc9Q:–øç4!¼¢»ø×ykékîÞjçÛ:4“e–eí=µû?Ñ9ÊuƒãË_‰êýÉK‹?l] -¿zÙ\9ïÓÇ]9HðƒQŠœtÎìâ*×¾¬ä&'ÝŽ¾äi®Ke鿏_r \ۼ˜`^#ÀÜkh›Ÿ±Þ\#R1”á:XIñ¹i&˨®apÛ´ó´Éqšß:}\Ö>— ‹o}ôI[×"sJÔˆÌ굯b=E]´ˆ ½ >tƲÝGÄ~_/Ñòý,Þ§ˆr±^ßt2ÆL9ëþ½õSÊŠƒÍXæz†jw<'.Á—2s¾Û¼dL€ 0ï`î]¾~{'t?™Œ~Ùœ˜@{$ðç%1]«­öãL–·`û\ÒÑãwƒÑp{Ò×…ڪݪš.¯˜÷Ä«(¾©nZBñ½ÂÙwÚY7Ôˆ}-Yî¿è¢ªCÅŸâËh]íÝÙã~€-ÉŽ¯aL€ 0p}é´àZ¾„ 0&àw(¦wƤÈÛ*möíýã6¢ÚÿA´Wƒ$?˜<ìÔ¡m)¾øa´qùü'>ÀáÂõâ[‚/úÆ6uL+Å7uVõ¡Š¢rJ]ÇUJ²ñržjÞïnc®0`~N€-à~Þ\}&ÀšN kRÔ™Y™/¿„‘(‡¢3’ëBßKåyúÐ/‹vÁÒ¶ ½üù~–Œµ ±j“DåÐBýnjì¥7I[èûZºÄÉv®ÁPœ×‰ëeY¾3>CqbL€ 0Ÿ`îSÜ\`mA`Ó%Q‘äçŒnr=ÊnôæIÊ“%¸Y.{Új¹j~Tˆ£&÷k´Nu@ñýbêlÇ |A¨[›¹Ì>|€b¯}Y\†ñúßÏÌy_ló’ 0&À|G€]P|ÇÚïK¢ˆ(;qêúã¥õ›vBNWω è…E7Éœ5ÃfUÿÂÑ·7¸Ä·$YP…ÿ£[wó =ˆïžëÔÉ¡–|ã.¾ež¾øAånOˆo555ìÖϰ_´/Íh[p×È»ôÒO\&À˜@G#ÀðŽÖãuíÝWR Ïú½¶…³úAhP ôÇÙ(/;(Ðö±Ínùñª øç­“Àd4u Í^9âô8-øQyð1ìÉ=Î˪^÷"úRv/í"t7¹Os7q?ÐFë«æ‡FVÛÊV¡Û‰+ Z§N­<ã©*å•-@Úˆjß5’ÑpyôêÕ­ c詺q>L€ 0ŽH€xGìu·6_5n8t‰†]ûh3Pöï I]“ÿ¹ 08!ÞuLñÝàDÞ`mH {rbïp8®jP ¶K’qFÊ’ÂÕ ö·áÆòçͽ5UߢøHÕ@q¬Jª4Å·ËU¤µÕË6tšêpÜYŸ|_ü¯Ù›ë·y 0&À|M€¸¯‰ë¬¼n8©NL÷NÐ;ª ü¸qì*:¤ ð­ùE±¥-Úñ°§†?56zw×Ögá4õœa3ÝQ­É*‹†ÖW›-SÄÖ¶¢»Êê_¶Ây…`Á™-Í4ë&ª‹;/¥­Ó”ôäíÙ: pö~0jh‘áêøÌ[“MRá¾{¥öèÊá6™Tb “I=cžKy]?³<.Ÿ˜€SÒ~‹n1±k p¥|cꃎ÷<Å>ÿÌÄXŪ¼)òCëÿç Y9óœ`L€ ´!àm_E“ˆ>x¤ ö†Z«’Né£U‹ôν%Pt° Ñ">(¾'*«„#åÕh¬s¦/¾Ïšõò¢Ñƒ`ó޽Pr¤~VlÚþgμ §½ßƒy¿·ì¸ã²Që7å¶‚ýð· †AEu |þ]œžÐï'øÑC_vä:dL޼ ÷>Óµ‹ðzN’ô‰L3—îÝÐvÑM÷Íʃ›õ|–zÔ³bD’+Sg:¾l|nK·ÕädS®Íþ1^߉òÀ÷ß|ÃÍ-ͯcL€ 0Ï`î9–­Î ­Sø%)i.4àÑiM–3Y±Éõ¤[„ËpV›.=$pº¤©´¢Zä·\r.ôëÕú÷鎂5N]ÊÝwzt ‡ðà è‡n-”HÔSÚ†ÖõSã¢qJûíÙ% ~ßY£“hǽñ‡^. èÛŽx5Æ‚µ7Êâ<}G {Z¾ŽZÇ´OCçú‚%éƒ$MºdÿÚúúX[6×”¢Øl+±¶]ëjT- ÓRgÚWy²†ù’ãß À™Zž’dÃ@*WÄgf•y² ΋ 0&ÀZF€x˸yü*„"…Àîß|OÞ4åÍ…õ«_®ƒ¯Öl„«S‡‹ªÀ)èzr¬´}Æ)Åt‹Ð–æÀ袭ӟ}¢`Ñ9ðËïyš="Ä ä_îPÈ+<ˆ>ç‡aÓŽzÁ~ ´^Ü»2ñàJye „šê‹¬Ý¹‹}¼Ô?-iƒBkª>ä¨Uhšö Qcìá2|Ãz<9åÔ—¤ô5¾y‹…7a¹|¾ñ|Uµ….2at:>öå’l˜€â{}.oò)ùÆŽWŽ™âŒ¨òp|æÆßÄ6/™`L m °o[þÇ,=<8*-5è;mEé€cžãé4í|ßè.è~¸AÖä£}¬DVkJ‡Ñ%%_*«êgÈ>=¡'|Ÿ± vì)Á¨(ÝàΔšå­’`”eyF<¤žuú±²öø>âXYS݃ÍÏ›3ô-šÅ2;óÕë,Õþíæ¾A½i>Ë÷%xpðââbXZìÛŠ5¡´åÏ.ÅñÖ4NÇ:2ÈÆqãfÚ²špy“OÙ}vRO«E!?rí_2—Çef/ µÏ‰ 0&ÀôA€ã€ë£\µ ‹lßèβ]5 ¼/" ßöÞ’#š¥zkþ~è‰2›’¢Ñ½$_~ÌÙd=ÿqãN°Ùq Y]¢…Õ(|i€e< p3Št‘¡8ÏÙ¾rQœ“E|ÿ¡rÍE÷ô’8Ï.!&í%€³åÛÓ”½Ÿ…ÌÌ|9SQ•·ÜÅ7vfz²¤äúÁ‹K¼ÿд ©ËçÉw¨åS¼…øÞk 0ëiñM/(ÖõC|é´Ï †ëð~Gdœ˜`L@/Ø®—žÀzaÙ)‚1j‰Ôã…ôTµ)®7%ò×>{Hœ“˜Ðä¬Ç‹×n‚;öÀà~1ÓÍumWô%'¿ë7¾úÅ/'Ñê=jèwæi0ñœÁ8ð2Þøú'íX¶õÚ‹ÏôÚ Ìœí»!À(A¸Ù¤Õå·‹µ«Â¼¢[›'DÅ×Jê< +x©{%ñ=jŸ,NZ\øž&ŠoŒÊ¢þÓUwœÇ`ºèÂû,{\û<´’·ø«ÇP|¦ì‰C’¥«zfdôPöœ `L€ xˆ plm6B|Ó¯Äâo`LgÈÉÛù8˜Ñ]ض¶q}Ld'˜{÷4±yÔ’^ŠÁ—â`ã}ÃO‹…”Sú‚ÍáÐBŠóÈþêçká²±CaHÿÞšÈþåHò¼¡K «½vó´² ªš./ö‰TE­Á-Á¯Á¡.˜^q¨•Ùuyî°¤±ø“Ò?êHOÄg䬫߿5&À˜Ð vAÑKO`=ÈJlТNëÛ‚1ÄßWk7jÑHtTÍU¡Y3M ßã0j#I‹^VY [|k^ôÀ›îB›^4HŒ»ïky+7(ŠË"T` g˜ ŒF#~H€;?Ä›“þÐôñY¢n-+«Ø¡¨0£›· t£>0[º?'Ô©Ö_í5ÚòÙ €åóžøŠï•F)r¬7Ä÷Þ#z©õc伩%釸ISžÔ+®`L £h¨œ::6l¿°Æ’8$¡hF«ð¾á—`á·Y "“´a5›T4 ò+Ç ƒì?wY¾M&ˆéª¹ 4)´ðÛL(>\±a*àËL@€ ?_â,˜{¨8ÎÆ²'FÅéãŸÓ¦oèµ¼A•äûPxë>’Çú¹ÝÂvl]„8Æ $x¯}‘t}ÊmžŸH‹÷m¯Yˆei~ßXV‘,›®’ÒÓQ>/™`L@_|%Àé«Ôý£/ :© Y“)ê Å@Œ~Ò»{,«†Í;÷¬P!í‚0;*‰Nšàª…!¤O[$²|“øÞ¼st´Cg,ˆßÄ•øgâÍÉNƒŽ[î·ƒÞ]ÝxIÌ›Íþ¬CU'¹—„Þú»Ð¤ûÐÐeÅŸ¸ï×ëúòçC»—ÕZ“EQÿ7u–ã>\[§\UY€™ŠxßvI•/ËÈÐϬCo1gȘð¾àG}éØvKÍFûY¹ÝCÂ…b Æ s èÙ l6NT³O›irê¨D¯ø„»UïWÉç›ÜNÈòÝÕd…È`#² 3²$žÄ•ø’œ]Pê»}ñU䨨¥ã¨g·þ ϬåLídwX·[mwaŽ&‘+º%Uª’ôL×®æÿĽSPßRœ ÃåŠç‚b•šªÕ(¾û»ª'K\OÈÜøœØæ%`L€ è—€/8}ÇjKuÕÁ K­Tâ’"`prp pÝLÐŽ\BCB Å¥-à=1²Hˆcv—ÖBÞžbMhÒàÅP‰ƒ®¯"•Êľ«Æ-ž ›×ÿÃ府=žªzÌD쬘ÿÄÿ¡¾ÛùÄái8»¥d LM½¯¦à˜yhgnrò9ªê˜ç|G¢Lå2s>öPöœ `L€ øˆ€/8}Gч¾Øé ž,]ÖŸ–/^Ý'¡ßE¯ü%ü¡&Éþ^ëî“D¢’%%aå¥ÁƒF/ý˜kP€×Ô p²‚;Šf ïâ[t€àBmr+qZ¿ÑcÈÝ„\PÈœ>äzBlj+'' ׈ÏzP8Ê~\òÕW¸—žM!ÂÅsÛl\^ÓµÚjÅ÷]x?¸2À‰ JoâËôdm€¥ ojdž½Ì+æÿó#ü±iª[»~  ŸrîeG\û¼°’7bD”j¯ý ß°ëþ1À/ ßï…¢8K&À˜ð2_pjYÓ„ø®ÅõÚêêŠÊìŸÖ¼'º`ú{KÖ«7OÅ®(Dª. qI¢Q[G8­“6›5ñ­ дZÑ -àu~âB€·goú5€’`DÓËÓ —ä^B|(Ô YÁiÀ% oçä;NË7]ÃÉI€ž»Â¥°ùן^©¨(­Ä½ôl Þl øž´^æb‹íÞ*«íATïÚkwläþH†‡S–n«Ûåw Šñ}¤fßl×Qyl×§ݹwî$v^K4;hnîŽOіጲÒ c`šôë¯8„`L€ ø_ p²€Ó—;ÅôµÐ'gýÚœÎÝ#Éò6 ¾õÚIçHl Gu‰Ä¢—§¹Y ÐVpr=±Zmš8ù‰“£Jhî'B„‹|Úã’ØhüíŸ,àĉ¬àã›^THt›4«¸3â [¾ëï²|“øÎü3_ÚõןÿöÃ7¿âQz&éÙ¤g”žÕ& p‡™5ë®/¶Xÿ‰/~1x­+áëÎO£qvÒ×…\;ýpeõ¼Àþöš*š`'ÁU}YšŸ:Óñ Þ‡ôkWS~þާQ|¢B©éª^¿þº×«…ræL€ 0&à5¾àôE_êd­¡/ú*üPÔ€Àï-\‰_jä0¹èÐõŠqgÊìŽ4܉G›ÚL™$2ÑEs;Á—Nñí¨)"¥tNˆœlœþñ$ Ȋ„¸¶ŽÂ\°sÃÙ¡WÉç›ÜNÈò¿mËgß~ñ ½“õ›žIz6é¥gµI¢²ÿ¡í]qêxt¨žæ~¾ý ²úHÊ’Ê߯Ӫÿϲۭf°«ÖŠñ­J3Rg)/Ã,ïÿ¢²sèKp|Ç,Ù>Ÿ‘ó­Øæ%`L€ ø_ pa§/| Nþ¡Æï¿Z¸òPñþý)ç¹ò?﯌8½_/uØ xip¿Ž^wO‘'QI‚’Ü-ÈÊKƒ.EôÝBx‹eÝ¥írA<(ÑR|ˆ”)öµËÆ7³Qç›B R´p©Øm9?­{'{ý²|Sè:zéãnw×Óx¨aÊžÜã¼Û>Iìb9á~"vG!öHúPóyoI ’÷ë´lžáR‡Ýñ¶Q 3ˆ©–0ÆwêlÇ_4,wDbÕïàí‡|—Æeæ<7½/Šç2˜`LÀK|%À©úœ|%ÉÚFÓNð£ŠÛôóÙmÌÞ1rÜÅcûy(ÂQD©aæ@µSDˆd2éòg46@¤ï¯«>[6Û·}V‘6(ˆD¶{j¼í~Ìë§¹eÚ}ïVü1Wkl6•fœ¥Ivð¾ð¹ªØõ×¶ï^¹luuue)^TV÷ÁH$Ú3IÏ&Õ8®ûIÆÄèá8Yå¿ð—N^´Y@IDAT—‹P|ã©®T²„Ñ9 Ï%/)Ä)íºøëÊòùò½žñ?(}µÿQx»•H²qbêLÅK÷z*LN¶Øí_àoáZaäÉaÒ5ôÑë…sL€ 0&àU¾àô¥A1²‚Ó‰o¡ 4qn±TÙо ÷¯08)¶ï€SN1‡†v 4›#Œ#YÌu—FÄ‹Jeoþ3O¬w„eç‹«íG‚̪íÔ¿§õÑwßcø¿šZ‹¥ÜRUyh÷_Û·nßœM}DVnz&Ë7 pâ´Nî'Âÿû(—59ò E‘þª2Ùý *ËŠ¬ª/àË'S–Ä<ü>Õ…\€aïAñ½Ý ¦Ž›Y“/öy{Y­Ú_Å2S9X~A2]»&“ú‹`L€ ø9!€[ÜŒ[çük ]üú“¦e o²~ÓOº!ø ÃYxBë>f\’Øv·k(Ü×êúbM"_[V2]¬w„ezïWÛÓ÷ìëPm§þ]6WrµƒªžÚ/42½ØÒGŒ¿ 7 mr7¡Y¾I|“ 'a.ü¿qÕ™2'õ<ãN§£ ýo˜iýó‡~Ðñ2zžU0oþ‚‘â|_®šâPK>@©¢-Øèõ²)lÊøûË‹}Þ^æ&'ÝŽ“ü¼"Ê‘dù&Œ÷ý–Øæ%`L€ ø7_ZÀ)ôEO‰„‚Ø&q@"€8‰sàT?á¦Rÿå;uš¼XomGŸt·Ô¡ÚîÖn±ª·ö‹g‹Ä7¹•Ð3GÏ pÜ$ÀÑUDûгGÇéYÔÒ¦IQq6PWUûßñ)5E–X ·#}Š£ÿ&nÂâüö°\þ¼¹—ÃRBƒ-“D{ÐÝc¡¹{ŸkÇÜP@ì|’r‡%SÊEaÈüMß‚/™`íƒ@[p! è ß}D -: .µš¸l,Àõ,Ä8ÅbÝÛuê‚ÑFÜR‡j»[»Åª^Ú/´²x¶„'÷ñ’K"œ%mÓ~á÷­nœØ+Æö96E¹ 3 —`WB1ú•I…dž,+þƒvÞšè:ä÷++˜†+–š¯°Í=Dc°½Ï¦ÎrÌö¥ÏõÞáûÖ:¬ŸcœI-‡uÖÓ¯+/™`L (¨VäÓÜKI0p $ÈIЗ¾»ø&N¢[|pU·i¿nkæ…ŠuÇpˆn©CµÝ­ÝbUOí§çJ|è9sáôœ‘è¦%}蘲钨H«¶)¶ÛñÒ ÜçžVb¤õ9ÉK‹²Üw¶—õåó —;ìö·±=ôëù[Û0ÒÉ(¾ß„Ù¾{ßWÓÓåÜ%‹>Äžë£ÕàˆÁˆ~ßk|f}§r91&À˜€÷ ´•-£Ÿ¼…'+ ª‰nñ¡o@Ýú€cÝÜÓ!÷ö¾ÞÃ!º¥Õv·v‹U=µŸž)Jâù"\q±­lžÐ§“Uªe³Â |iL†+¡åw*æ [\ø“kg;[Y1_F7åqü/¤)müƒýh¸4u–}­¯›š¿dQ:Öc•‹õÀI×Äþö[¾¯ëÁå1&À˜€÷ ´µ§’XЬp¸¾ª$¸éC_Šâƒ«Î/IZÑi¢Am&…6ôïPm?F'ë©ýB€ÓR|HŒ‹ºmòÀÐJõÈ}V¥æ~páÞŒãý+>|s†.-þÖ}{ZÿáíØ ËÝï`,ýË]í’`þø6ñâÙµ¹®}>ZÉONœª¨êQZàÿŸ•C¡81&À˜@;$ .°º Úç.¼Å¶8W¯Kò­í0)«wKªíní«zk¿áT?ñlAfZ|TWΨp”Þ‹û»ˆÊ×-7âDF%/)^Úh»Úüa^HtuÉ®¯±QÃEÃðV^mŠèô· o;B¡}švH:ÍfWÞÃBµ yX7iÊã•ãÓzpaL€ 0&à;zà[í èx›,ù& ðÕöct²®Û®&­`½,•3ðÁêäÔäÎV ÅûOÔ~£÷ç¾px †^ßµzž)Ñ¢VÓ,–½DaØþSc/½Wú›ïgîÌØÉV®ÒË@˜VIÊ5šC¯ÒÓé× NL€ 0&ÐN èY€·SäÜ,&à;^ÓµÒn¿¿V©¡Háš-¼®x|Ú)K†'’’oûP| _¨|WI•´|®a*Fx¡iå5_w47ÛQ|ß“ú òr[ÌÜ© º\¼ècl~¿:• §ö]¿^o!-}ÔC\ `L ã`Þqúš[ÚPT»M} Êf»Ewhƒ¦k³:þtÎGÒB´ú.Iop¸=n¬˜'?¤‚òú»;ý¦$(Å—´ñ³ìmæçž·äë§õxâ•Âhë†ë22µí±¸ML€ 0&PO€x= ^c~O`KZl´ÅR3ËfSq&EvoZ{·ª2<™’|ǧN‡…î‡Ûåú†½Ì¥öÂ7p€ãÕ¢(vwJjÀÄñ³k·‹}¾^æz¹ÃáxP”« ºÌÈþRló’ 0&ÀÚ7àí»¹u„€sÛìêjË­hLmÇýº7ãì•Oº|¼;€Å›ºý»gƒú–Ú÷}‰/"CÅm€^6kdcØ¥¾œV^”-–yÓ†(å-±uZ—™ý @Ή 0&À:ࣟ¹•í”ÀïSzö®µÛ²«6œ¹R to&ê¹l”tO]²ÿ+mpexËçϯQj?EÇŽn‚ 2x=2"izÊmY4Q›¤Âään»gÜTµ_'°K¶u2]ÝÞ¿¶ l.” 0& c,ÀuÜ9\5&p<9S£cÑÇûáZ‡ãzTˆ3ÇRРº$I²O¸Â v áMÐßû>EuÌ'¯ê:"VY’ïNíx «n—ïêèÑÆÜŠÒϰ¯b©tÝeºìúë¯zŠ!ï{0\"`L `Þ;›ì¿².éqªju̶۔¿c+Œè^QŸ$égY–ŸH^\´²~gÇY;Ž¿w‘$k3[þÜÖ$ò*JŸUCõÀ_&(ÌàÕñ™™mæ‡ÞÖ<¸|&À˜@G&À¼#÷>·ÝodMŠ:Åöƒ(¾§ ænà,ŒîeUz¢=Ï\y²ŽÒü½mû!›$q.rùY6™/_u‘Ø×VËɉשªz(_’¥Ägnä™.^2&À:à¬Ã¹¹þE krñŠ¢<„S¦j\staø£êý+eéþ5u¤m—¿74ô÷î{ê݃þ¶ÅÚÖ,r‡%E}UüXýö9Šï·u½¸|&À˜h;,ÀÛŽ=—ÌŽI@MK3äT¯Ks€ú âp$ºŸ„¦o2€/–dx}¼q?Ö×åï-Éòô‹g9ÞØÒæHòFŒˆRlµ‹°"Zd´ÊÿÒ5òú6¯W€ 0&ÀÚ” ð6ÅÏ…3zù×Ç:X}}fõºY¨³ãëh>'6á#Éh˜›¼¨§ŽïØ)óµžÁ%eû)¾÷U‚ŠÛBI2\–:ËÞæþÞT'5550·¸Å·CÛX¿ÃF9`jôêÕU´Í‰ 0&À:.à·ï¹å:!™–ª;´Üƒöí(ÍÈ]W7œ<§ 'Ïy#P6,üuáT¹M«±bnàÀⲢϑÕénÙ€þÞ—éÁß[Ô)·¸­ð0’¶ÑíIJtyߌŒ¥ºòv¬JxƒêHÒ!Ô‚MÆO]´ïPƒcxcù<ÃåªjýŠïPIz-6öÔzð÷uÊMN|]^#¶qù@BFN›M{ïV^eL€ 0`®ƒNà*t,Y“{õS¶Y–jËuèÐÝhòi7ÒX’áä%…Õ‹Ìñ[»å³A» þ\ ¨Ê]®³$°€$ß5a–ãm=ø{‹zå''NE×·A–Òë Yÿ+Žó’ 0&À˜ p¾˜€äLéy–Ýa¿_U¬—`‘²ˆŠAÅ£«ÉVü;zÆ|”òzÛÍÔè#Í*fÅsA±» ¶~†a‡¹.”`‡,™ÐßÛºÙµO+ùÇú”Õª#I?$€aºªÆU`L€ 0`®£Î઴?Ñ$«úÇiè×ý€ÝnA-tÞèü³A…g’–î_‚~ÂxhûƒÐŠ-›g˜¨XkßÃ,:‹lÓÂpµËMçÌ>X!öéa™?lX´C±.ƺ„8ë#í 0_&ýü³Mõã:0&À˜€~°×O_pMÚW÷ /-¯¸)«zÝ=*¨}jš+ ²á™¡‹‹ÖiÇ0D§zêgi†»¾ø7¨Êl—5ÀŠâ{æÅ³•ÖŸ¬ƒµüÑ£ƒ”Š#_áÛUoªþ¢Q*K†I½þù°ªÇU`L€ 0`®³áêø7ìi=ú*VuFiiùÍØ’pwk7вZÜ÷Q–žK\²‹·Ô{µ_ù\påùŸ‚%œçVÊnƒÉ˜6þ~Ûonût³ªT”¾‰.2Ú/ñDõò¸¬¬mº© W„ 0&ÀtE€¸®ºƒ+ã¯2§D;Üï°:.E+¨Á½(¼¨’ô²É¤¾ò¥Ît<ãô׎ܔ¤KAQžÝŽõ|§™Q5å1&À˜€ž°×Sop]ü‚À–´A¡ËÁ›ŠáxÆlé[ƒ,-HZ\´R¯ÂQO ¿}.(¾ú·µc†»ê%Á~ô¡¾gµ\³ôé_œ<Ô¡:Þ>êXËïâÃ:ÍpµW˜`L€ ‡ ðã€áÝL 1Í¿»Ö1ÝR}à]îÇQh[AU?£´ åëâßµc<°ÒÑ1×—Ï5\]k«}†‰Ûꀀàk/¸·ªXìÓÛÅw»êÀˆ'j0Õ ëü—Ñ–&­Yc×[]¹>L€ 0& ?,Àõ×'\#Èž5VQánôUkàß-ªj6›ûZXÀ1“,×ÏíV&~IU”kÜâ2ZÑmç‘ÔYŽzþå`ÏÈ‘fk­åkß1ÔL´|Cाë×9I³ù0`L€ 0 p¾˜À1hn&5‡®EîPÕSµS„£/mH°ÿÔöãß‚ÄÉ—+ž5 +W} $ˆ³QÄþ%KÆ+Ç϶eÃút9¡ºâ`K)/% ÝNTç¤@’dYJKøí·¿D[xɘ`LàdœƒNvg„ÀÆKbdNŒúouõ}h} …–S|cûQªh¡ýF2ÈS–»öw4­n&‰×åóäÙªbÿ Cö¹Ä7úo¼c#‡jâ»Õ¥x7t9Ûq™(2r¾Û¼dL€ 0&Ðlo %>§]PÓÓåì¬W.Fa5Ýnµ]$Õ¹5º…Ö»&ƒá¥!‹ Éò­9ý.›«_K­[Ýu±ªÅöž/¿‡î&ˆ !½2I–oG—Œù­[woQ]ÈMNºCU•™b‡$Kÿ—™óªØæ%`L€ 0¦`ÞTR|^»#°yBŸÎV¨¹13ã•;Ѹ߸(ºÿD_ä—Bäˆ÷NY¼]WÓž7®«ž·—?k¸Ìa³¼‚â»›¨'ZüY2^•z_Mاçenrâ¼GpNgÂûbQüÄ©@æF±‹—L€ 0&ÀšL€x“Qñ‰í…Nš3#Lßm…Ú«Ñ£XÔ;w£0Äñ–ÒRƒ / ]Vü­³Í<¶²%}ÿãË+*+^PÊÕn×+èrò”yØyÿ3Æ?"†8à Ú?Å»D€‹âû·€óÕRzºâÖ.^eL€ 0&Ðd,À›ŒŠOôgjúhcfæ¶© *wã¤9îSœ×5K:,Kêÿd£üJÒWû ü¹­z¨ûŠgãÊ+ËßÄw-RH]vK’áÚ‹gÛ׬ÑC5OZ‡Üää>p,EñB'ã Z¾<©÷† –“^Ì'0&À˜8àÇûÛœ©Ñ±ŠM½93cëMØ¢èc´j£¤º›>î½p/‹ªcjήUó£BêùŠÃqGƒëp ¥Ôéž‹g.o°_Ç(¾1Ö»}þJÒƒª‰>ëGd0^·aÃñf`Òqk¸jL€ 0& ',ÀõÔ\PÓÒ ™5ë&b̸ÛveZ/DûA!eS%éK / [\ø“G åL`ù<ãÙµäÝNpt¥ ò­©³iÒšÃ~CIMN6å©ö/ðÞ9½®ÒVŒ~35.#k›ß4‚+ʘ`º%À\·]Ãk.ß§ôì]kWn⬽I¸> €r%tا‚ü?|mØ—»Š\x¥U¶|6(`WÁŸÿRUEq½ì ï/ÌrèícfVlUmpq8^Ç{g,/l8,@º!>#g]T…‹dL€ 0vH€x;ìÔŽÔ$²vg׬KE«ëmµûÅšµÛMu;UÂ*P¯%·TZ¸ÐÑ‘øx»­Ý {BvåoÍDäƒ]eIP*KòÝ^ðÿ ƒáÿá¯íÁh8sâ³6~$¶yɘ`L µX€·– _ß&6Nìc—¬7eZÖÝŒ®&½Q‰ý8Cá[ƒôFý Ê…Ç8wµ„Àòçûæ—/5eôAñFbgžÕtSê Ë^±ÏŸ–8Ëå5Š¢üSÔÛó&Šï§Ä6/™`L€ x‚ pOPä<|B€&ÌÉÉzuœ¢¨·ÙÀ:§2ǰpõænÍU¤oeY~-)yà×Rº„¹ó <‚NÎQkrÿkÊí+²E¡Z%©ÒìÔÙÊËþ9–5?%q´CQþWß&iu|X§ÛÅ6/™`L€ xŠ pO‘ä|¼F`Ó´˜^6«ýú¬ŒWn©ácµ‚êu7z—H%¨ÃßxýŒeÅyÚñ%ìâíéY?·[X¹tx®ª8nG—w«÷ZÙpóøûkwzºL_å·kDÒiv›ºËÃÛˆLúÒæNÆÀ4i ¿Äùª¸&À˜@G"À¼#õ¶µuÇÝýË *§ ;À 6šGOºW_³vKÒ(¾_S{Ä,Jy=Ëæ~œ×=K`å|ÄråЫØ½DÎÕdÿË:&ïÇVŽÁ~p{%gøÇrß°a½-vë*¬m'g¥Â@Sà„®¿þê7!ýƒ4×’ 0&Àà‚/uA gjL¢Ãf¿±4¯gOT»P¥Pô¹ ½ƒ¸ãƒðzÒÒ½;œx¦J ¯,>´»ZSý_t͸Ò=kt9YüCÕÝÝ«”NVß{FŽìb©µøv¾XHPaaB¯_ÝëÞ^^gL€ 0&àI,À=I“ój-i½ºT[ìW(7Úm¶¤Æ™ ØS@•¾Cø[aqa‹ú¿°³¶ñ9¼íyËç®VkªþÝ{º‰Ü±/J(ÂÉøYŽÏnÓiØïËÂääàêZËR|Å;µ®þVP ë;{£?¶‡ë̘`þC€¸ÿôU»ª) ¨ÌÎxíBERn°TÛ¦¢ <ª’”ž ïßIYZ¸Ûy¼ø¨Óx‡g ,ûOàpX_ÂP|¸q%_û÷dcè}ãï/÷ŸuŽƒF=Ú˜WQúÞw#étiR°}ÏÊþþ8—ðn&À˜`#ÀÜc(9£¦Øgܬâ*×~?_Á‰vžqŸh]jžŒÏÊyÉÏ›ÅÕgL€ 0?"ÀÜ:ËŸªªÅìÎxå|ER¯Õ>Í¡Bhãú£è.AûêF ÞJ\²‹ó8îâä3ß>_k­}Yº¬Áu…o0ŒwŽÀ¶  ý¸ýäM¼Å÷ƒ.À’ô?ß¹¶y… 0&À˜€°÷äŽTDÖäÈ3þž•ùòÕè;Ò³¡‰FÂŽn&+Ð"þ¶Ú³×RØ6wÍd 5yZ­µc ‚D-ð7‡ª,?xñLû;íÍý'7%éJP”¢­x~ßÿvÈâ1—.&¼Â˜`>!ÀÜ'˜Ûw!Ь ›IôL xâPÏ Ö6vÜFÛ­ª$½lzoЂº¸lín‹;cÅ<ÃŪ%÷¿ØGýÜʧAˆ¯‡…†=rîeG`VûrÿÉKI¼PQÕw±½ZÃлi½Öé iáB‡^eL€ 0&à~+Àïxìéd‡â‡”ú¢ïpOüV ó ±F…,©ØçÚs뜘5® ¬ €ª@ÝB¾»Ñ]`å+ÿz8ËÅjElIZ[h«Ùö@Œ ¨Àü£Ê–¤b”;ËŠüAò²"ÖMë‡}Zqõ?À.ƒlXu²þ_5?àt‡jûúâ_ä^E£ªj¼óâÙ¶L§û½ûq_Ïž”¢Ø•/±&j ¶÷9Lš·fM¿·ëϘ`þIÀ¯xzúÛAûlû¦cXèû튽Šn5ÈV­„Ø«¤‡µÁL‰mÑ=*öòe¹VC€Re QkLÁ2òxòÖGŸ(r(ðá5¡/<÷Üý$.P£y.©ii†ìÚõªŠòw‹åà%j§£³G+j5–úµ,Kï'»ÚƒFé¾û•Uޥܫõ¿j€T©˜årɵmÚÿ]åŸö=õª‹®ZÕêÿSÿã„¡ bL1/¦§ßà—«æ‡FÚÕê'ìªífìƒÛqåè#©³þñº$¥+nûÛÍjîˆÄþªM]Ž ÒÆ àý¹; @ß{ÍÆÒvÓHn`L€ øíçØÖÔúÖ9ÿZC׿þäc£ié­„å\$©ê[ H1}*v« ‡·K}*ò!ÐÎs²ÔawXäv¨îëƒû”B«ÍvË;óŸ\…ýAÂêh¥ÜŒŽÂ(&C%Åñwœ çJ¼,ú˜—JÒwhßÜõ‹A ·Tóœ–í¤{T¾~Öœq& '÷Œ4íT{7IQÆí ¹tfËroWYÕ (¶„=ö!j‰­Ÿ$Kê>¤§F¾»,y÷‚¤>‚‘gÂ]M¥è& ½a–C3³â k3W|õì7³Z®Ó÷Œc­•Ö« Æj;%é çÄeemsÄ+L€ 0&ÀÚ€€_XÀñ‹þ>U•æw®9gïY =ª [ýâЬ½V$½„ô?²>RQHOXßktÔs×%7>ô‡Þzæ‰ÿÂÉϵYNݧ`´ˆ+@U®Tû€c)xƒÍ5f0U›a@æï8èÑiáɪmÀvÜk0˜ž “‹Õ3—B7ã.î'lí/½„ô6m¢tÐÔ6×LŠ®P"Wl/íY: 0·‹ûë‚[%MŒ¿ßŠQg*Üri_«Eç&u¯®V¾â›~™‘Á0Åwûêgn `LÀ_ è]€K(¾1l,H(Ý¡ŽÚý­dTlþÊÚ'õÆ—¸dÇBÃÚ>¨¹úÍ»~öùyO’·ãç„Öðìi=ú*µê*(W èNÓ¾xþèÑAG¾Æ¾Iý‰ÿ40ò¥tc|fÎí±¹ML€ 0&à¿ô*ÀeGénœ"º'ù|³ÛIËn0âvÎÞ5ò’þ—F;oäÌEîÜÝ=À0¹¼Ö6Z–@ë{wтņÖÂoû'ÁRøW§,Þîk'aßT¯À3Îuìˆ&Ÿov;iaÿ£%üŒ %†õÕ7õTº(wc.*²YcZV²ï¯R““MyGâËäù®Ò%i:Šï÷\ۼ˜`L@'ô(ÀÉÚm0àÞÞí„\¶îN!ŸpŠSÒ峌¿Ç£B1 m kŒéˆkë$>0~qê¢}‡œ%µ®àæ_­õ;^€spHðíí„\6¤ûäN@ü½¸Ÿf¤w.÷÷.÷Óýr]MO—s/z+?Q4Ç*<”µñe±ÍK&À˜`z"@G½%ùï3†^ÑjPo•óÇúÇÚ€P©Ø V‡X─Š*ý‚?Ïß æ^)K‹Ç$/+~½^|·I+]Öï .¹|„$ºS¨Á6©I;+”8ÒóDÏ6MÏ|‹‰«8H$oñ¢70ƒËë3‘þµiný6¯1&À˜Ð½YÀ5+¨)0`®¨ç›˜îŠ—N<ó#ú)‡ö¿^R#}ö`ö\ÌšeêÁªõ;Ö…¬ßA]"£ÆR}1Î7÷?im¢xéÄ“ž+Ì g»l8·µù·åõù)‰ÿ‡7ð¢èZõ||ÖÆ9b›—L€ 0&ÀôH@—AÅÒ —ßÚ}Ö>=òó‹:Qœpä©n‹è—‘¾añGXiœQ›‘¢bèI€ÓTáfÙhì Uá,—5,ÀHkÅ Gž8"À‹yÑ3¥—~oUÓvM|Ì™!2Á›å­¸Ì8ñß6‚ /™`L@Ÿôös4ÕÇ€ÑOzÛ*õV7}ö`k…<%ƒÉ‰§á‡,Í$ÄôÂØÙïupªgTÆ* ;ÈS yÊô\a~zê÷7ogJâl|w|Ô-ƒOã'_r ºUéá…Ò­Z¼Ê˜`Làhz`¢fT£ RX bc&¨x`I< ’ŠYâ‡8ýú¡—þ×ú½®^TO“dåþG žJÄ“ž+ÌOOýÞ¢æå%'Ý ŠêòñFƒ÷ÒÉxˆOߢLù"&À˜`>$ FM&ÁENÍ⋈x8aâ*Ä7YBs—Ô¬ìD¨>$êêÙ¬Løä“¨{®ôÒï'¯ð1ÎÈK2]Q•çÅ!ßßËaÓ¤¬,ÏÀ‰ 0&À˜€_Г'`Tz%³÷Êí£½ØÈ%F½ô¿³ßuÂúñ ˜wºßù‚«£~oV35ñ­À â"t7ù)¤kÔä¸5kœ“D‰¼dL€ 0& sz`„IXB% -¦sn~Y½:˲ß.ÞmÜ÷zhuc ¸wz¤î¹rç킼kÞÐÄ»”Fâ;<($5zõê*/ÇY2&À˜ð*= pj¨^mtÏœú\p¦¥’{}ôvOê'ë X{2O¯æ¥‰oP_¬/DÚ@â»ûO?ùz¦Öú*ð`L€ 0V w½%½ˆÂ“r1„†CDÒ9¥™kÁRð×I¯ií r”Kk²LoœõZ¯c²–$t‰{‡ÊÃ;`ÿÎeÇ<Ï“;a`·¶Zsê­ßOˆèXâ;Â2žÅ÷ ±ñA&À˜Ð9vcm N8†.Ì‚àøSš„dìÿòMØûγP»µe5á*Ù Ý{BîÓ÷À–{.Õa‡žWÜÞ„+›tŠÏ¸§6>/¿[ßQ0ðìGŽS¦íî;íý¶®»ÿ iµà,Uu@mEd/» ¾ÿ_"ìùã#è?r˜Ãzµ ·—øœ{ƒÒO²Aâ}Ö_ª?Mú™Åw= ^cL€ 0ÿ&à=³m˸(ÀOó- ‹ë}ë#`+)„ ¾ý "ñ,¨üëwØÿù`Ù ý}^³~÷ÿÇ+(fmðÇ“´Ë»ŽžÑ—Þ ÆðÎP¾éØýúSà¨,Ê«&;˜ÐÕ¤û—@þsCäÄ¿ƒd „˜kîîãÒ`ÛÃ×jËÈ W©S7¨Ø’ û½UX.¥ ^ñš;$þT°(„}¿e™ë (&úÜö(ǵE»a÷ÿžÁk6k׈?¶C%(¾]þAé/ßB¯ë»¬cìòoîõÞ8ߣuŠŒ¿ú ¾ä qÉw€½¶Eò»šPîÜ#~­Ö]`Ôõà@ÁغæéÜN?ÿï~:T•æã¾G¡t6Þ²fuÎY~+ <ëQuÿaÞ.ƒÀ¦_'Š}ÛYB¹ýI7ŠfÇ!ödü( ¾£›u2—Y©\MDÓ6DÚÜ¡¢¨`¿Ø±þqÊ5ŠÈØ¢ §¸Hl“¥ÛÞ¢,á-<à˃ŽoY9Ó²˜GÅv„@þC´8~°øëY¢}ëÐIó‘žòªÜZ·€ËnXéIÀ/›-Ñîoñ¶Õ>¦åÉ¢ {›ØôÛKöázµ&·²|û‹ï¦®ðKšóh'õªùb˜`L xæÁzË!?c½ïÚ¼Ù„ ‹°’ï ßFLŸÓ…«i qâ„wÅ€ÔvÈY¿Ò'¾íc‡¯ÉâÞôŒ ൠ ¥ëßl;,ë;ÅÍ;ÌO<¶×@K ŸÛ—½4DÂm¦,}b?Ñid‚Ø:å‘·i­/ o”Mà ,ߊ þ¶ÖNgÙn›w¦u_ô¹à°ÕaíŽiq¢O€ïÛú³•Gyÿ…7jëô8Ñ´í)¢0o—ˆˆn ~·@¸p¡“föžbW$;½Ôœ¢yûÓ!üŠvÝ‹] è\T³®v”Rë%]"èE¡í WŠW½cYê÷o_\*N]ÞɈë÷¨©Tñ§$º)–6uB|/[–]—¯‹ëΘ`L ,õÚ^•áúŒ‚\áÝ¿K¬½ïê²8 åñ”yÜ>ÕõdÑæš;-W’œÕËD«KoÇÝõÜÙ•Ðs²¬hm;‰¼œCþÝF‰5ÜOÈ_Qˆ<þÑõÉ—ÅŽ÷&‹¿[QT>WBÀÔ *ÅB/ʳâý÷“Ã…2ù€S0ôâ/ÖNÿßïÛª—XøÎ Ëïû´ë?µb™ðí6‘6ªqçR©”©ãx‘غö±qÉøRçÊÛ!AO0{ ŠÍ;œ-ê‹Ïˆëû,Äw‚ïº-ñq1‹oÞ`L€ 0zF Þú€WÔN…èì(0­^Ìɬ!ÉM$kùB¸~‡Î•· kÛÙ¤¹ ‹seù˜Sðœ\HšŸ{UqR)Eî¦5–/: ôðvD,¬í‘Çw¹V –ù6×Ým¥ÑÂ#DlXc ám:ˆÆN±ÜZò6ÿn AHÃ’ ‡ªÈÿ{³ 7²R“Uùà®TËJÞuà–Û ušlÙip•2Žˆi'ß—!ÈjÓ¶$UñŠÆïÔgˆøÇ¿‹sï\f_¾ñ—ñ‚:bÖå Ün-c`¿™ÊTÙ×!¥œëˆiri« ríc¼fL€ 0&P T^a”sõÃâЩI ƒi]ƒà@ÚH,±C‹ÿ²cѾWl>$bko¹Iµp*N ³¤C¥ÑÙ¸™0òráÇ]dªôšÆì¶;j–•ÈÛDByK»´Ðd;hD”`„oº]'ÒEìï½òüƒÈ–ýXÈ¿–ü4 ,U›ôõ9ÉwK[UÝ{É×î¨@s,mþùàã¯th¼ÿ¤³½Yå:U%Y¿½ ÀЀv Ù&)TwÆIi…†,+8\Q–¥[§2ý‚Ô\Â+üõ!õ‹M¸Â8`™7¼Å€KŸ<úÞ⼻Į‚¶)oML"«µ»]ZuŸ}çJW:u¶¸ÙÎ /³º Ç?ejª×wŒ7˜`L€ ÔS Òn·¥é)8B|Ó9‘¤:â›ÒV$¾­¼³!¾é8 ï`‰oʟÑȧÚ_|S ÞÕßÅéËßtŽÄóá⛎+Ó[ÒQ´ñ]©Úâ›’‡JØ2xpD†0¾@}|âonov½òš[Y|‡J+q=˜`L Øêu'Ì`Ããü™¨<=ƒGgçü “ϵSÁíä•.)+ƺ‚·;6¯™`L€ Ô¡f§ae(3Ï£¹ø9€÷X‘†‘­aA,ÆÄ¹d `)ÕÎÊ®‹µÖ ½À«Âh›C€xU¸2MEæy›u€r®\6[Ï:«iNÎyþâ[“ZR×Ô•±ø®CŽÅ˜`õ‡@( pŸà2½Æž|W´o¿þà>vW’çj¤Š<ø\Ž5ç#Ê/*ô,T8~DÍù@¥ ªXL«ïõKPk|3N;­µžŸ»#ržv¨|ùx—Ô‡F?9t‚·˜`L€ Ô{¡$À}°½Of¡+Jzœá¾c¼Q}Ä‘xæçù °êgä”TÏ"ÕH)LœÄ¡Æˆ#ñ¤çªÆ™U1ƒ?O=µ³Ò ÿ§„êCIáï­¤ÔFtK[yhÒ*æÉÑ™`L€ Ôu¡(ÀÕ®í[—À<'·Åt®ë|C¢þÄÑâ¹qýjTˆ,Ÿöõ+©„]'µ õ¤úîÖ{„Rýêl]ˆ#ñ¤ç Aœk%ü7À0<¿ÀòÝÝ*PJL+ï蚺bZ­T€ aL€ 0&¢BM€["láœ/Ö™†wz³µ&B´}R-âhx=YkS–¦#CsÏâ\²H5ÌÄ¿>&Õ“ê»]?4Ìa óoÐɉ#=Oô\„Í:¨L¶ p±®ô…ßm¨ m_è↮i+ßjÁœ9`L€ 0:@ ”¸- hmìÛµûãm1åÎFíêÆÐ­"ñ#ŽÛ3Ò$®~‹?ïcyþõðÕê»ÇÛMîÓ;˺Õù²‰ŸÅÏSIÛûóÊõmŽë÷oCšß skp{ˆï¿…p^Ø9eeÝž=((´8S&À˜hˆBI€²ÎZ"láìÏ>1õ¢ý‹l꘨„CÕ 7âgf-™ûõ<äk±…î¡hªž} SøÚÝ®#Õ×ðf­.¼ÒÔ·u`7â‡`?=OÈ#èíŽÙ-Çb¤“·±XCœJ!·jÂ9¨kjêâê\§aL€ 0&P „’·-s$ôƒ÷åþ±"mÒÈæbaÇ è‡* nÄ/mÉ¢÷óósiÆKšÚ“âÄÙfŽÍcì:XíŽZXu¤ú¦-^ô~¶ÙJ¬,¼ŽÛ¿ÍC܈ßii“èyBAkwuãŽôý§a¨Ã$»ª^p¥C:Î蜚ºÞ>Æk&À˜`L@ˆPàÔd %‘@"ÌóËs–nOßðnz“nrþñ—*¶„ƒJ%q"^ÄmóÚU߬üåkÌã·Ø"<”,à¾v·ëIõ¦úÿåí)S nVl ¯Dã# q"^ÄžzŽJ˜¥Ý3ãâ¢2Ò7~¡„9Ü®!Ä÷±Î9>5u§}Œ×L€ 0&À˜@1P› ÓvE ¡Pˆ¥à»Þûü¢os‰î'Ýr ¢‰´}¡Ö6¯ÖGS«3÷ ù|“Û Y¾7­]ùÍ‚/?›o³$ž%ÛÄ—,Ρ$À©>¾vÇ6Õ5òç/?!ì¤èÕïòl³…êþÖ¹§8”E€|¾Éí„,ß[7þñáŸ|ð9â­Ý£ =Ρÿ„O§ùÕçÝ.Âq·\²ÄëwŒ7™`L€ 0¡&ÀÉÕ€„ýp“hÈÃ1{ÀÙƒ÷ô;}ЯO¸¾qÇœmªëßdÇœ-"\'ÃnÃ4Î7 5H£P‡KÝS˜›2ïÛO×,ûe-ÈØÉxÒ>ñ%ΡâÚQf»£~4¼ /?ïßµsïÀsνa±qwl+×fÕÁ¹J¶vna’ÞÓv q¾i¨Aí„:\Âw>gå¯óßJ[´à Z»·ÈÉŽ¸iùâÞé$Ênt¸|®kêªÑö>¯™`L€ 0# „ª·-¡$H„…ALü¶>5eóé_v‘Þ­ûàm:ÆÒ¤Þ|å͕ᦻ +x4—¢Ci’(X©ænY³jñÒæ.),ÌÏ ZˆaNÉš¶I±ÚðPàG´;êJ÷¨F/›V­Ì8ý¢Ëé=zœµ'¬[ µ˜ÌS2KºdQƒk¯ S4c(M²SÜþžœ­›×,XúýÜàC‘G‚×îfðôü¥?÷÷zÃPM°cb¬ÁQ˜ZþuÚçÀ˜`L€ ”O Ô8ÕÔö'Ó6Yli  ¢BüôŬo°=ÿ„Þý:w:¡ÇI‘Ñ1MÃ##cÎ(ê *è:æ6Ìß›“Ÿ›}pëÆ 7¯[µlW²t“àÎ*Y²±&žÄ•ü­CÅýU±B¹ínŸÇK…±à«OZ ÄÂn=ûvèÔ½G÷¨èØ&áQ‘1NgdÃlÿ‚}Ù¹[lÝ´áMkVn« ·{z\¿ÛLa¼î5ŠÅ·”èLr[çÔ•³KÚŠWL€ 0&À˜@BQ€“UÖ‘ÔI”Ä·mÝ´Ešbc– œ³,¤XS\;6L ^ÄÅÇÛdå&±M–oàK¶Iœ…šõU²B¥Ú1i~z^6Ò±àäv/~¡"·¢ ¶»RJnØïS©”e3¬¨‘ÐÏ?þ·¿ÚÇx͘`L€ TL 8ÕØ“d­µEµ¿@£ã‘XÈêé³c›Ä;Mñ^ýüŸxP°Å7½´#6¶”,àdù&!ÊÖoTÏ Üî6‰ò×ǤݷŸqFdÆÀþÿïíªŒŠÎû<ã^™ÀâÛ†Âk&À˜`• ªœªNbŒ%ö> L²ô‘·¬¡XÓuXn*X7ñË´‚Í…Ä7¹•ØVPà$¸I€Û¾àÄÎÇPv;Síë³_,¸Ý‹[ÎæR+íþg\\Û"OÁ—æ¿ç»wÏ·ý¾í´8’Á¾&n÷#›Çn;›QÐÛ}K\Üiº20”¡jgWG“brç.Ýøžoã5`L€ 0&Py¡,Àé*HhÈð¶5”\OüÅ·í+N»>‹o\žˆ‰½#1FŒHtÓš:ê–oTѸÝ}(ŽØ°ÛÜf´vOØÿ.ÃÔiTzÑÅS%uL-?ªKêŠi"u•ßÇ:Ìÿ1&À˜`U#êܾ$8hmϘh[½IxÛ⻡ú€û‹1[ÑÚÞt¾.n÷#[ÍnK›ÝÎkw5x°3#÷àËÊ4ïó/å~¡´›º¦¥ýä;ÆL€ 0&À˜@µÔNg‹L[„“e—·= †¿å»¡XÀm.Ćbc/ö1ŠS—·{éÖ#ìö¥µÝæ´¶Sœ*‡g÷o™‘}ðSø{Ÿc'†Õ{•æ×t^šö§}Œ×L€ 0&À˜@õ Ô%n_¥-0HlPðÞö¾u¢üG,ì`s±÷ëÛÚ¾>n÷b‘m·¯ÍÅÞ¯öšü½óòõOA¿L>ޔ޻Ú-M¥~˜`L€ 0¨‹üð˘9ƒGËÉvÅáïïÙßÛ&Âk&À˜`'`øœ9G&ÀB–@ú€~·Ãådyiñ­MïÒºíY—®ü3d+ÎcL€ 0&P°¼4"_¨,ËådßžW1ÊÉ;”2˽]RV¼kã5`L€ 0&<,ÀƒÇ–sf!E ã´}òöïþ½WO´+ïFÿ†.)©kìc¼fL€ 0&À‚K€]P‚Ë—sg!A =®ßH¥Ë”:$¾1³å;11M㺦²ø‰FâJ0&À˜@ƒ!ÀðÓÔ|¡ ‘@Æi§µ6½žJ©«ü®?WÓ´‘ìrâG„7™`L€ Ô"൛‹bµI c`ÿLÝ3-[øÊ•b…t„ßÒå·ßàz 0&À˜8X€ ê\&"­gÕT/Èb𿡱½iöL)'wiÕö ùí·ž ÏY3&À˜`G!Àü(€ø4¨K6ì©7?÷¿ÐÛíìzc„“mJiCº¥¦ý$ÄJû0¯™`L€ 0cD€ø1ÏÅ2@Ø;hPLVaÞ‹Â4‡ú狉uÞjâ ¨ù²eÙþÇy› 0&À˜8vX€;ö5*yDÂø8Ã4.F&”Tí¤15ʰ‰?ؿחjØe7,ðíÔÒ†ÓË‘Jf ¥¶9Îï¦&ŽN­¥¢Cª˜-§ôÿGvAÞÛ°zoW à îÊ1´kZÚ7ö1^3&À˜`¡A€xh´C¥jáv¿±Ã»ã~©ÌGtSo Ñ­¢<3ÆS Ãtï1R²Ãþ½ÿ¨ÔE0R‘Óeæ„Gªüðp <’†yv§¡Ä˱…ѯNšôH!Š‚F¯¿aûgDy Æ™†ù .·CIrV¸#läq¿ý¶ß>Äk&À˜`L t°¶¨°&Ãâ/ÚU´ýMt¤kßyÿuòŽm¢ëÞLáõ:*LX¿OZ/….—HoÙNüÞ¾c›Œ­'Däˆï‡1}|[x£ÐÝòG¥´ÑÝRÓÈ=†`L€ 0&PÏ °&Ám¹ @ ±øB»”øXÛâÛž“XÍ e[\\W¯0ñEwbHÁ(ÿËBËåJhOvMMûÉÿ8o3&À˜`õ› ðÐj_²È:È!´ªU_jc½ØÐ=Oâ›– YÀ·ÄÅf*ý1¯Ò¯…º/UŽ”b½ZÀ˜`L€ Ô˜ ð# h¶( h¦œY)d¶9äEgï A1YžÜÛ2ö{"¿‡i(â^~W“àã]ìj’²Â? o3&À˜` Œ ðÐkð€ˆÂÚ¸,G‡vÂuJŒ#¢„營…Ê/¨bkZF@Ä7MŸ1çËs…¡îÌ.Ì»2;ê03z|¼ßw:Ô‹–¯\WÓJsz&À˜`L þ(å›Z.«á]Iä]·Š&o¾\é wõí)d ‰]½@iOyN¸zŸ$m[ Uä¹bà8yç-¢ÙÜ„³g)#s. æ®=ÛÏÐ-#®bÆ×³·Ð¬•è0ûOÿΕÝQÕçœÒy|×´•wuZ¾ŠÅwZˆ£2&À˜hØ:­\{–o—SD'<"²~JùÕ›ëÅÕ¿7òsŠÜ ¯A|ÓÚÁ 2º‘ˆ~b”p´kŒBŽÊ}ÿi§ÅfE7·ûNÇ8Ò·µ‚ð^…á߈ŽiüV« rƒQQΓ 0&À˜¨X€‡V;–¸GÔÐR«i¢É“DþÌ÷Dä-×Ù¢¹ð.^&òÿïc! CÄ<û„eýŽ?“²ëâà… QÃïagœ*fŽô|õ(øô‹NÔ¨»„¹y›°tG\rž(x÷SqÍ%Ö¹ÆS'ϼÿ‰‚Y_ŠF®~½à’bŠ¢e©¢à}n)áœ#"nºZhÍš#ýO‘“ø\x×ü.dËæ"òúËEî‹S…¹ÿ€%äM[„gîèÕ£Kß°ˆ8LyqFöÁ‹áÏݤÜ"¤X7“ÿ‹Ž÷Û¥¦¿µ”™O0&À˜`L |,ÀËgSçÏÀ¶ƒÊÊ2"ÂÞ-½†/7u¦$Kuø9§ûÎiÚú¶õß7øÄ·ï ß¹–4ºÿn+óï¿…Ö¼¹P¹Å®$¸À›ÿ´Ä·_áêßK8®ñ+I‡CÔ+¸ o£„íoëÕ(ªK{WX¯X‡ÖÌIåÁâ»T±8Tˆ I†Ê&æt^ºòÏRx‡ 0&À˜`Õ$À¼šàêB2³²#“è:ü¾uQ8çGAî e†¢¢2Ûï¹Lh]:‰ƒw?dù}Ç>ÿ´}J˜99ÂÕÍÏ££äŒÊ/澿ÅÁ»òÅ æFãBOÇ®ááwFKXéÜT.:QnÃj-aa‘?uøõ×:Î 0&À˜`$ÀÃ’fÉË ÷t”tõ:IȘhËmÄûkŠ<({Z£›8ŽïŸë6•¾" >áêÀÁâ|ôÁPÝ}i½+ÖÂ"ÞLD\w¹ÐZµ°\R¨ÓgÑÒ¡µi…—ZÖy­i“ 1è+®Ò°yÿ)Ὶ|Bh®Þ]SWvÂ2Ëß•ÆÈ™`L€ 0*` xÕ‡è4úˆÖî¨{ÿ%¢Fþ[¸å^‘‡S¢*b’F[‚œ|¶s’^F'LˆõJÏœDô##DÓOß::T’?¸£]±€§NŸž»‰¨;oQ÷Ü.Èæà/)»,Í™%òŽEÔ°;,ñ^ðù¡¯ÛP‰«E״º÷Ç}†±4Ëð.LÜ–¹ ¹•›§…¯:NNÁ˜`L€ T“@¹Ã°U6¿añ‰ („Á´æPm¤¤¡îb‡<ÿe—Üì·.[XíÌ*“PF û!B©Ità.aØ¿ìœ#|¶+•'&è)wFL‡Ch1„™udÞZ“Xaæå[£©T¦œªÆùð´ˆ a¼÷Êó"-½UìÇ’…8 T'ð³_jœ† 0&À˜œ BÃ%PJxÛÈêê†rÅ7eˆ1È̓eç]ÞñêÖƒÓ1&À˜`L T °x¨¶ ׋ 0&À˜`L ^`ZÍJcá)C™y‡“¶9ˆ€ÇáRº®“»‰ÅØo 8&À˜`L€ TŽ ðÊqªX>Ámz=¹Q¾ýÚ(¼¾—‘©Š< ÓrD`ÎG áL€ 0&À˜@0 °&Ýjæíõx2óÃÃe¡ËUÍ8™?âhñÌÏÛ뜷™`L€ 0&p,°?Ô+.SíÚ¾u ̲2½e»ŠcòÙJ ŽÄsÛÆõ«‘€,ÞöR©ô‰ 0&À˜`$À<4kž—% Îùb©{÷ÿÞ¾#ís¨!âhy²Ö¦,MGV&[€3ß²åäL€ 0&À˜@Õ °¯:³`¥ð…ƾݻ?ÎhÑZnoÚ"Xå5ˆ|‰ŸÅqKú¸`špÇ^üy7|‘L€ 0&À˜@h`í`ׂ¬³–@\8û³O”·hÿ=˜^×nªÊš¸?ÓS˜µdî×óV/YlN¼90&À˜`L V °¯UÜf[dIêîËý}eÚ¤}1±âÛÞÙU¢BteŸ$nÄ/õ—EïçççÒ„E% qâl3Ç&&À˜`L€ ÔàµÃ¹²¥E–Ä! EÏ/?ÌYº=}û´=N~ÕïtÅ–ðÊa$NÄ‹¸m^»ê›•¿üo ñô[ˆ/qf 8 p`L€ 0&Àj—@8Lˆ9X¢k·Úõ¶4Û…b!–‚ï>zïó­ÿøbR½{ƹ&û„WÜöÄç3Î3‰×¦µ+¿þùËOçÛ,‰gÉ6ñ% 8 p@¨vP2F(™]íôœ 0&À˜@%Pcçb©d&ØÅ5P~¾lr‰ aèÅBb1KÄŸ|0{ÀÙƒ÷ô?mÐNܸ˾ÝêäÛd×½™"ÂKQv q¾i¨Aí„:\……¹Ëç}ûéše¿¬›c.¶‰'í4Û›ªI -Ò¥T3-'cL€ 0&Ð` ÔX€ƒÜV!UëãÇ7:zôK20n pÛN¢1KXÚ¢¿­OMÙ|úÅ—]¤wí>8£oëX æ(GEæËpCÇnà ‡SÑŒ¡4ÉÀIÝS˜»eͪÅK˜»¤°0?4h!†9%kÚ¦/ ¶œxs¨zÞ<½5,à[«‘œ“0&À˜hÐj,Àšã{ÝÔ“Í|ór|¯AÓ ÌÅÛ~àä³L[šÓECGBñÓ³¾Áöüz÷ëÜé„'EFÇ4 ŒŒu:]$ÔTн˜34kWN~nöÁ­7lܼnÕv Ë6 l²t“àÎ*YÈU‚xWöÿ„šzÞ•šÏMòá´L€ 0&À"€XM‡Æ'n—B®‘iC„„k&ß|ÞXÈ¿>K,±Ä`i„…ÎQz‰¢ø´¤=‘O] dŦ—ûÅ…ÜKÈÊMb›,ß$Àb!n[À)ûBuðø¤o•P½f&%t¨nœŽ 0&À˜@C%Pc ¸NÉWðcu!ØÖoßäVb[¿ý;^Ú¾à$ÌmË·-ÞëÂ5†T‡¹§G)ïÞ_¥TMÚ9ëáv¡¯ ˜`L€ 0*ˆœ~„‡Å'E¹s…wïÛJ©›%~¡«PŽz$[\’hôß&!I¢’Ä·ÕAkjÇÃx}âö½es±8ù~kÇ’5íÓqÛïÛN‹CªBϵŸü6ÒôÄr‹oPàÀ˜`L *Ò†%$=¤L5 þàŸWË;Ù^); YÂI`“5œ–0¿µ-¾é<µ§½`³^ÒöBÜ_„ÓK ‰nZÓBçØçªÈòm½\ u£ÔäÃ3ã_®n^œŽ 0&À˜@C'PN0‹E¸xðµ#üö Ø-Fme qÛþÂÛß Õœ„¸-Âm!nï“ðf«7 T7X>ßB¼Šô=¥&eñ]]’œŽ 0&À˜@1€ pÊî(a5Ôu„+ÊwXÞ×¢´96%åÝ)W´~ÍÕTLêûîî<;nUÖöóDi¤’XmפúzZRÂOUɧ:q‡Å'}„‹Ó“ÆN(/½Wjý…0^Àyàe@2 u¦Têœb^:tâĵWšÒ‹ç4¥ÌC±ü~ªL¡þm†è>qõ"MjÉëíõý¡ˆ¼Å˜8’€vä¡Ú9’×;#ç@þ M…Pèz´R!ø{w½ÒÍ´Íqý&ª¸8×ÑÒ”y^'âøéø3údYç‡=5þ¡ä¥øc{™–[½2ÊʸŸè:6éÌÚ¸\°k£œºRÄôƒJëñ;Ì_|—[¥Zã¹J,ú[­O»¼5½èV=ày‚5½D½Ì¦À*ØËTrîÐøÄ窞YÕRÀ’ÿ%,ú‹«–*´bw' Ÿ8.´jŵ!½^^ «çšÂüŠÄw%©œm*ó»î×|Þó¥uÍ*™†£1&Ð ž>`À°J-ƒ1®wU™“‡¸x4Có¶ îפªé)>¬f_C4œtoÂxXÇJ©Ì»qä“ÒGy¯ ®sHQ…øÕŠ:"~BØrT+q=K”2,Îñýž—+%¼¼þãL©¾O¹²õ¨#OUæˆÜ1=qì;3’㟛‘¡2/±ß7n\óʤ®n|ÁúpZâ˜%ÕMé¼â¼°4 ‰ºp%|Nšø{¯"]¥â7êRßÁ*m¨kuÓH9é•ßO¨R2ŽÌ˜@ƒ!Pë.(q}/‚Eás…Y—!¾Î1²åœígœqA‡_¥Oß•ø£Z ¥˜mšÆ]HäÞ{®±ÊÑI@^e(1ÌÎÐíVZ¦‘œ,LqŽµÇ’"¥úpzRÂçá—^ŠÌÝŸ¿Rº´«…nN²¬%R¬ƒÔŸ}AŸ^¾é¦›à†*¬Ã°ôCœq&ê†<~TÒ9yfâèåtÞ*G÷ æÕØ…ÀÄ'}% ¼,JvÝô¤Ç7=>aBÌï$X9a¥—.äñ½+ÊñД1cöSCã“þ« í;ð9ù\‹8…p¹ù¸½³ÇS;ôM/Hä/ yš”Ÿ8M'¾î¾/—ÒQ€ëͽøÂ: ¾±»FJǦ'ŽùÕ:?î:Xv.„¥s>ª4ytÂñyð{|sÚ³c¿åž[¨gÍ¥—*SÈw`/Ð4ñÁôÄø§)½*ËŠâ—WŸáO%ŸoÞÿC6A9›(®tÈaÂ4§:Ú-¯»Ç®¤c#Æ­›æ›šCŽœþìXËZ~¯;¹—áUŸ5u |þ‰'rFº“ûé^E. À nòûFÎF¾ì~ø å1œ®[¨Á°´¾/”ù¸t‰0þÛè¼ ~åðøäiØw^зû0»ÍíóÁZS¹©W¶yëÛjRÚMÃ399åò6úÀ9»¦Ö$/Üw ý³F‘I_¶öÓ=)4ù\zà¢û+kFRÂëžñfOÀñ¥°f7QR¬Ðœâ±éîø”‘îñݼ^ãÛ—ëÜWÝOüeׇÚÔ0Í×…«åiJß;÷Àê‰ñ“éü¬Y³óVox÷ühËX<7ÿ“Âø ÷j©PÞ}E‘ÊkïדžøÃ?“ÊÞÇ=¯Ã’’ñ¼ýϰ÷ñXPšœ Mñ^dζË# 9Dúå²cË3†÷ÒñañÉ¡½NÄK Ľñ‰C %ïÇfW\÷¥‰©3ã_§sÊkƒâ³‡þ¿7)©½Y¨ÐVrRY.z‡bÖß-X®»yM}>þö¶ªÉUâ™êlxõŸ{½¸æŒµÿé½½&yqZ&ÀêZµ€gœÙ¿ôÂÇ5߇šAYäÉíÐ~å¶ð£û&~¤o5y2¶‹ƒÌÓoŹ%ÊéØe£µÛŸD!v@8<ìˆvtÆötü¨O5n\K:ï0 pTÝ•n|Žõl%{BÏL„(zfÞšMC(§3œÞr)WÊ(ÙB!_šÆøâ³BìÔ“.Bš‘Âéú—èЪ#,õ$„7ju*‰oŠñ=e´•NíJ—K"¾líÍ7?²ó sÈôb°Ó¥äéÂ!ïÇé™Þ ¾‹œNçùP«#ðÃ?ÔðxÈNg‰%Æ “ÑÓaΰœ?š¦ùã(÷„ã(Ä š+aÞõG„ÕYv ÄÐì‘îçÛL~zTN#gôà—¯¤6²‘«Ñ)QM£ŽpC¨,«ŠêÖ"†Ü¿,*‡–vÚ™‹À"ËÐÅÅö5¡þ· ®‘¦©nöÃy\K‰ï{Ýã‡øþ·Þ)]ƒÐˆC¥PÝòõÜïÐHj_·º ®SÀä]‡ÓyR+ç©V[ØyR\K|KÕ»I¬ë¡ÚßT~Ú•mEù5ßöuÐí;yÅÕíjêBtåeŠˆ ZÓ=) åFæ—Â?ün-B^IG=zö×x¦zKMÞ'\Úéá«M]-¢Ž¯»GoF;åêú×}¡’jí ÷ð|Êä³²Ï[½)ñþ­4ÇM.gxO!´_𜖺+º¯¨Œâû¼âö¦x•½+z^ަãq-Ÿ¡ÔÏèw6¾@jÎåhϳF¸Çw¡rè¥âû.ºNmǾóèXq0¯ÇµY/ò÷C|'¢òoâ…åD¼JMÇߨ±–H·£—Ó¾ÓØx09¹5Ä÷|ÜÎsªøüÖ–X®a ª™øöqU¢½W‰Ïâ¦×ÌàäË7˜¨7jÕ®<êuX›’~„îJ?¥ÿ‡]—¯˜WÙ|a™í]ñó2õä¼¢ÝÙ× ù¯ÂÀ)îÖ49y`eóúŸÀšà)Pd©þ¯n"¬âo•ì…8ߣ¬Óí8¯»'aÿ¢n/éÊ»nÄøñM§Ž}åÞ«ñìéî'·Qœ‡Ü“fæysŸÏ?PÔ»«È·q°ætvæý'Å>6)ÉTj!Y×aUL§c¦ g$M´¶…Ø ÜŸqì\Ãc‘ty›‘×—ØX‡®=?à“¦'ù”ŽA¸ñ>¼D¿$Hår4rÞFu¥8Nœ˜˜•Uø izû"_êttù*‡4rm 2Å++UEõ™ùÀãa©Ì3¡êü˸úbû|”7¡økBÒuBj p+ëèðba¬`i”ßRL¯ñ0Ø>3i,Y)üq|âpO¢OÏsé Xt•í XÑçÐ>\»õâæÐjX¸W ˆh×$ì‹cÿÿ”k:tUÞ"»U S7ô7áÖÒ{àŒTËÚz´ŒñLÇŒp'÷Á·šÆº0{CyǃÇË3Üî󥕪{x«Æm^}à+þ:aœãti}§ºÇ®.‰÷ îÕAE^s4ö‡`y/£°¶î?0o”uÐs,æôÌ”zíýŠýE &âžh…öû§¹¢û q¬rÊjo;ýáëŠîã£=¯øú”Ž8EÈÓãwg£cëz¥ëtgì0Æ“%< BýKÓ×aû{ü]h’çÍèpÉ{°_üìjbæÌı¯Ð>Âkx!l„—©xÜ÷/ù^kƒâ¨Åÿ“»PAž9l?™ÿ¬ÿ¹†´½sÎXüÍîÈkÆýtJnöšGç„@æ[^^ôëõš}ñu¤”+åð„çúšB„¯…Kg¸Ç¦•—ž‡|»F»ÝÓG¯­šqmjJ ÖxÆ€èœb\VÓ —•^Ь\>1YVœÃ‘U¢õm%å]8÷1 ¸&tvµŒù²èïÜ®‡Ç‘0>ÎP:¹ot‚E¹),$í§”ï¦tÊu‡¥Û¹KîV6}ºKnß5zÔŸUwœ!¼xAçRhŒ$j·˜¦8ŸÄ#Õ¯@/8?ˆ…‘Žð­”´soX§ L]u§CÐ:ʉºMÓºa·X€K…Žx‡êûöòJÄ·ui;6,ÿćÝ/5Ëõ浇¸¿ùžC \P~öñIÛJPü_¦-¾i÷…ÇËCGȽʔ1~q*µY«*Ô§TY`÷=®ë1úª±{ïøSñÒ¡àôÜ’Z½ál·{Ö/™ú\ŸÃ=ÁäGÿL^KJØQ´ƒi8LK€£ þ†…½ÌQ å%ñ>Bs9ºM«íáõ¢g ~­ÿk¨ñ¶=dæ_w"Ÿ™•É ÷×CWäV–…{ôO¸üÜ7õ™±³!ü’ËÏlñM1êQ/Ü_{^zÌp_<ÚpÏ]L¢œÑïçé¹/Œp'>Õ¿4âí»nMóg&%øÒÐ}*Ì3šB€/(uBÈ5xƬC•¾¯*hïÒyã™¬à™¯ôózX¦H÷½!Ä8¯n¼‡ŸßRñ†.&‡~8ñ[Z¼ñ–¹}ãÏLRSŽpf‹táºWXŸë)|_ÊÕóÎÙáMÞ…?L›à+?”Ž›}Ö1)Ã!’ ¤¦ù¬ñ%5xÙÔÂ}'ò.8T ßV¹–YÐÃ(Üf¾€þw_ XÒ\Ê‘iïãRJ]Ÿ}¼:ëŠXU¶>‡—{aßîË~\½ÁãÙ—s¦0Õ5`óYÿàÿú¥©Äµ;ŒÍÔ…ôîöÛtl`G'·œÃóñ@Tú?»Ýîsõ#âÑiª³Èð/a,”µÒnlÓÒÈ7oVa`C/•à`°>ËôÅÀ¦=[ZÇi.\~B[‡ ,:ô„ŸÄ=ˆx©D ûm÷ž‘cw)œ‚þ…wÂ7OCqU‘ÃY/4‡YêÅ=PHN¡ ÷Uùí]œ•ïÿŠîc¼ÁVêyõevhã;ü™x‡^ÄwèÖWœÛÎëÝcñ¼U»Œñgàj/ÀýùE=7,x–º­×zürC¸ûîãÃÛÀ./õ}ðÜ¿Ëï°a É·ÍHû}®!­ñØß{‚7(¡ñ¯çä<¥¢Üék¨'¯hÚwܵÞÅ‹”‚aa‘NAºJ ðÃó§¾M"×{­æŒh?ÍýXHÍ¥ßÇ/á‚¶íð:‡Ò>T$t…>X cB©^\—ºOÀ÷Ç9˜—¢.½4VÞÏÁÚé‰ }*i)>Mü +§»Çø¿ˆ——´~—òz|Å Ú5áïÒõȼBnæ)ú j¥¹ÂãüÄòÛ5©”–kž›l_~5É. ii$£€fŒÌh¤"LdŒ¬9φM h?àþX·îÛ‡£`Y¬¢ðcs¶™•ÞVâMˆà7!|Wúù¢–JŽs+`}ëB³î¿µ:k8„B¥C´+j=¬Û…è$؉6MŠTúþþP6ÅË@t=MÉöÚ ó3m`(¼É!5rW*NÇ{ÂkŽŸ<:Ú9=O¶*Ò½n|öÝ$Ú·üÊŽv´õ+cÇîš0îV¸ |ßæeSǃ४w¿W¡VJy©Œ ðó{wY ‹îÏàÿü°„çÆ†;¢¶zôœ»!€NÕ\â,»šÓŸ³Âú >I¼†g|ÞëÉñÛísþk²Šãåë ê$<ì©ä4ѾÅb¹cïe¸†—ЍûÜ?Ór¶+ó¼BL/Âß‘²RÚ3ªNz䑼Ì-ÂÃó$²þÂ~Ž0ºÑçèôü"îã.2Ê5ÏW¬”“‘ÏÐ/b­êÐük±cßExá|'Û;|q+Ø Ù0ñüŸ­¼æg#ÝSNñ!©‚dõâTŸ‰»Š=‚y1Ðög þY9œ+õòÊÁûØßøY)<ïäNûéoWY¡¢‘ugâqŒ^5ÏNKÜSô,_QŽ 4º”i¨ûð(¯"c""µGgŒ»“b[˜+% ý“ðúF‹ÂY×À浪ŒdDåØ#ú€Ã ø-<€¯;ñvߪŠê@iýƒ5¢Q#wÙq+âŠ#ÑuÁEr´p9¯ŸQÒ_̾^×-¸·ƒ áÇ‚ªU†tµü UË—šx£¼*B$Ã\ßL}ñþ"½hÜþÆàäòâ—u|’ûüaÕž2„1ÖæBé³!2Ÿ„Øó}~Ã,ò¹!ÏâàDXþ>SÛöë>ÊÓú!Ž’WÁ žƒ‘~ÀçéSm0”QV™U9†¡ó&âƒõk(w’Wÿ{?üJ÷BPN34G³ªäƒ>dA¿ SOÚ?þäéQ­p´úXÁw÷ àð9Êù ¢ër*Èiªïñ"—Ír?¡cn÷MEx¢!{˜ç‹C‰åõ*X¦®ËÓó¶Ã)‚ü…ÛÀ5é’ªÊmf★p9zécM¢¤ˆà­”¬Öý^• áÇ/heÐWˆpWì5x޼ðM]âñfï‚x~_noóov=-wŒî{þ2£#ÐŒÁö*ˆÏÄö=áëšäÐ0ZŽ_8Ú}嵯›•y^1ªÒW¸/3à"° |/øÃüÝÇð§ÿÔ®HgÛyEíà÷´ÔëtŽ:ˆãÞ›¯B“ͽY¸~zŸ‹N¯ vÚÊ®U‡V¡M²týÀ+›¦>Ä3´}Ýp9‚{-*bßšµ**ƒŒ*øê‘ƒ—ÓñÕ§sYq+YçðøNWÓ×1h2Žï ‘v"\±‡öÝî·"ðåm0~|‹WÇŒÙëŸÞêD–olÓœòlÙ>74~ÜU°ŠLoç<«ƒ¿2uè;´mäØO!íøX“† 3<öØâÕÉ“òh!NÝã_çêäCi*ªå[ïÚÔܶÒT· YBÆ*Y«›Gm¦K¹¢Õ&·nÁ,V³7ÎÙ}O0Ë ¼‹ïåœÆ¥FM©A¡$&²Ä(zé­(›Šî«ŠÒUçÜÑžWòýÑ4½¦£èÐË_S¶Õ¼:umˆizL\w^`æýÚ¥vêÆG{-¯¨kÝœ‚çû\ˆÈ9ðBz‰^ð) ¬ØgâØ¢ÃFÂ:ñ˜ÓFº7!ùN ×ù8:*ŸLiȯ}báBÙ•öË 4Ül¡×» (NóEÈ•ŒèÓ0iFÒØé ýŽa”¬8ö F‘o•išoÑhQ3üF‹*1B½N¤t%#íÆKÞ5¨ß<¼tÎÁœ)ö|ˆOy¾—ÊÑß?­þqi›êëÿLŒÜÕÊ<À*?«pYçiþŠÊðDœ·]!¾ø ]¨Ëð} __gâëç?ðÒ;íò%^Zƒóð’‘ŠöáE¦ß4÷صÈc:d`¤¢xßK1õ-ƒÑaÌ}z4#MA,PΙ~£EÑuÐq–"î¯0Ú}„®®óÑ…qžii¶°üôÐÃoòIЊ!0£†¹[=Œäpx6E…&†ÙªmËUþçðP·¦„ûÎàRî%ZŸõÜ?MM·_s?‘ yØu¨¨>dÝF¼vÜê®÷·¯n>µ˜.øzüÖÆõ”ÜËûU–Û=¤yÑRa¨è¾ª0a5NíyµB5².•„Ü¡„[êï¾j¢ŸvðúTµ˜’>BÒ¤a¦.GâKäk Ó“ÆNÀïAåFªàRhò1¯W]eG w¹þK“]APÿG™Æ|¾…ø3eZbü÷ô{U“уª2’QI9m0ÕõvÝìu¥ë`'8´®pä®êòDºZ©â_h$¾NÉgX|jܺ¾U+>àp«ØìŸr¼ýÿ-ÝîR"µ.5ŽÒ"–`ÐÚ¾çó¡ IßâM7ƒ Äç¦ËðÆû8[´êRk¿®ø§—¸‚\Ò/ŠA.³gÇ„†ùà `¹®Ù«Æs®ô‹&YNQðÈá É¿àwàEôAz1ÓXRÝ‘u|×ñ…¶öåõÂÛ®LIc'áKÌÛFž9‚ï]”»_ÏÝ#þ £óG% QŽ=¨*#å¹ Â…¹8e.•çª0‚‘2’®##A|_ Ý[Ðc¶Ú¦ÅÇï(u¡¼S' ÔŠ‡/éÁ§#k¡Œà]Yׇ¹§Ÿ¨™ûhŒðsð‰.ÊTÚoaJ™’4vkðJæœë"¸‡üáúÎ fÝ5©Õég*˜l8ïúE u‹È͘„Ç‹[KŒãê`$ʻ塷¹KõJ8zI¦Ð6`¶¥V»œKZ+½æ#aÁ•â”JË¡äKL2:&N>S´~§¾ù*šœ Vñj’UÙ‘ŒÈ.(¹°ú_ˆŠùúP%ƒ5‚QÝ©HNÁ R"FBjmªáÒvnUû)ÑØ|à˜¨Þ¥Q“é9³!,cƒuÅèdgùÈ+ÿÚÈ·xzmA~ˆÁ÷E¬ â2‚F_E¦qwÐ  Œ]âAÍŸ3g!B`ÁÎ…=&®èSƒ‚V%Œxã– E£:ar§BC´\ÝNt/Ê4~ÊHÂWädõÄèR;ƒ1ֽ”áí:íÙÑKÉíä`NQ+X\£aA¶> à‹ì°©Ö(Y•Ɉ¸£ÌWáÏ>3}qC„…MMzbCMêP^›Öµ‘ŠšÄ¸†Èö¦ “9ÚC”'aVÙï•wO^¦wI>\Ó1Zî ³|¶-WÄ Œ„%…­”1 Ãîí£ƒÞ_¢_™ž8šÖ¢&£Yýž*9’QX«ÆÏ`ò¨Ù(ÿ­úâ<fì4DÑy5­¥/+T†g(TD´¥æ¤9O†c8BZs¨ÃðÂY;áϸ¸˜M258¥É…ÝÒVNÞœ+M©—·žkÑð`ÔŸoøõî‚‘7çÉB‘¦Š=à)؆º5Býö6w4íôë#Ž:ÊÒ¬Y³‹6onæ-rÉŠ&Ï9ÚÈ:Õ¹šëboد+«6F‚¥_ËtkMà – \Pêr‚Q‡£ñ䑊J5ï€@­ pªkú€¾âÍö–ÔÛ—.@iÒyFçÔÔe¾ƒ¼Á´«:´3MÏø­Fòr᫚7pä)u¹Ss yp^ ‡À‰/®yã¡L ôCDŽØðhïiΗócL î¨OT#íü!ªñqv~´Æ/R|Ó¸Òè å_FyÛ4Ögyçü:åM³wù—qøö¨É“ÃÉ’røqÞ¯?|µ=÷ÿC½"¸µ8äUßd9£±[Î Ô.6z½Œß¨2;(Ö &ónýO¯šŽ`[ƒâ9)`¡H Ö03ö=U™â'XÂÕþPÎíÓäjò1¯J^ ÿ&ÃTÿu¹ä9¯»Ç®´Ó¢§7MSN¾vùXÖÉ­î)«§1MÙ‹Y*§Âw/F¢À°PâfšÁÎÇ^2üò6Ñ”¾½èr¼uÜ‘e²°va–³I3Ç~d—Ik SÔ=¥×jJŽœžÿ¡ÿ9Þ®R®hý Äï5½2X¾áÑ¢Ý2pή£úª^F/X‰1jÿK³2~.ÔöiLaÌ|Ú´¬g6ÔêÊõ©}'Mú½­ièúO_ÓÒñÛ°AFº­¿ÿ¤ý5Í‹Ó3&P¿ÔªœÐuIYõ›Ã!/¢q»k‚âû -¦ÉõUßèd’ŒÙ±nEù˜vüP@¯ësÐñììöγú`†®×^ù×¾!‡bo‘UŸ(ß"üòIñƒ0£f½G|Z d¼î¤ÓÑ1}M–€ZlÁôÂg Žg9"ÅuR™,ß5¿Šb þð/ð;Ä›õ˜@Ü×»’šfÍPWÝËĽïÁ4ð·VG|Ox®/^÷a”£V·üÚLñ}3þ~ÐPg˜ÀþxøäN—ãø»^Ãa8åJÌXy.‹ï#ó&À@ V†!<œôñËWþ’×_Jý-X¬·Ç)o?œù OuNI{ Û0¤W-¸„óƒ×Ÿø“ ,ðO ‹ò5°à}hO®)ñ¬Û4ml©O‡ó×l>Ž/Û¦'=¾‰ÒÓt¶˜–ö-²ªùO}Èx†WÜ®Iñ>•«Ý´¦@ÃR O2;³›bwš|¨¬À ÍhŸCý'Pò<šzEëåx ^ƒ5¼E•®ZʵNÍñ¯~_e®¨R:;²òQšö_ °vÏ} Ïõœ’øä::E/¡óVo:a¼ryîI/Ùßq_M_¡L¥0Œ¢êñŽñÇ21Ç(äÑTꩉH\ÇH!CÛ9»ÍÍÔ7½gTjšxlÚ³ñÖ¨Jåå3ýé1+ðŒ¿j\€rsðlDãè*éJeˆdüÝÉÇW¥N—>Õ¿/à7cŠ$¼¸6Bgß§Çhzhûyݰ¬{°ç¶žSÖª˜“qÿÞ‰{·Feƒ4aà™ÖLkò诽ÓeesåxL€ Ô/µn·ñuMMÝÖ5uÕù)¯…€Xb/w-e6Ff˜¡¹Nì’ºâÅêˆoʛĥ…¸>ìªl§”¶Í.o&[!:ØûöÚ”f;ˆ _<ë¸TŠ‚ãì8´T<Ë·/²‘óSÿüi{˜{܉$.ìžò£ÜŽÃ• ZŒƒà`ÿïÃÕóý¸ov¦Â»K¡=‹öÇ”äG R®Ã35tàÀ“úWW|Ó×SÈ«¤£ÅWx¬>ô*ï¿íRç¯Þ@VæpK9Ûá ?\s§³ÙíÔÏ_‘ÆcLÛÚ9ãO†¨^¯IùÊ ÷Ø4?®Þp®}¼n˜ÖÝ×3'ïri¾@ÊÙ¸+tu„a¨Âû§¦ Dºû*3âIÃ$ËW͘8&pôSWÎÆþì-§÷;^éò|ÌøÕG Ã" ð²…&Ò1#ßRÑ(vAç ‚6î1„l¬æP¾ik½®ˆ<å-<¢“¥fªXü@ûâѵ ¾yÂ¥†® T¼æ/‚CJÉ e>tÔÉÒ³'{ºC*ß¼j…zÑt!Ì>Ü;,>Ù—7>s¶ÀÕ>­n¼ñÙ´üÅg*ižë]w¼46ÅK§7“=›ë0Ãꂾßì^k‘ùúèZ½\‚Û÷_.…ú•&‘Â0]ŸëyÆ ˆãÑ%Öc_-¥äeyüÄ1 w^ô.#»-fzÝKcÚ ñ„€[ØJ8Ÿ·²ËÀרp¸‚=é~ò\ž'úf\›i¨¼‰ž#*W¤Ï_µqÔ°l—ŸÏôgÇΧ<8±‘WË‹¼Þ³±{DŸ•¯_€gì+3±4þ}‰¯HW#îî8ìwÄ…g)/t[…r¤„Eó׌ìCÏ&À˜ÀQ sn×°óÒ•bû {¿¶×°‡ÿ ³µod‡ðÆBùÇT“£©/^q=UŒr¨ÒqÏT·Ká°ÜOü™xödýFü9Ó’~¢ãÃ’ïÀZ?ãÙ'S°È{ã“%Æg„Aãd`埖·ë?ùÉ'¸}Å¢’%h¬”y'ÄýéÉ¿ëy–°ík7]„¿…[ʘHä•¡O%ÿŽîx P[hJiXÍ÷c&·¶˜õo(\Lvš¦yM¸e¥.©¦’‹lW0:q|ò9AäÀy¤x_˜šl'köYyùŒtO‰Ö½ÆC ÷ÎÎòà%¹,âßSúÃÎGÝ.9:\e¬òèûتÃãñ~Ã&°ñÑx—, _=`5&2¼ÆWRà ðéV ÕÕÎF×õ.ø)†Eì° œJáŹâ`M 'u’áÚû˜µ@<Qé{Îmël{ÞÃ’&â% pFrüó¾ã¦º:ûlœV|L†CÝKøÙØá‹ÇL @Fײ0߈ƒ@Êßà¯ÄAwbóÛvÚ˜…;Ìäý°[_®4õs;­=Y•,«÷‹¦7i~ætÿê˜'öÒ9+Håµ7iM_˜R5-iÌÿã´]^>ß#ñê©f&Ŧxð÷žMJ¾¬€hø¢¥¾œž”à!Ë*1&À˜À÷a†PL@ŽO!Ào#¿Òâ#ò.Ð!÷ABV¼Ki{zâ“dk2,á¹Þ´¿ËL¾a-¹ˆ:žô¸Üî!>×tø|VíãڹƖšbü2tn‹±øÏÀ0„wÁÏ•Å75‡€ð˜·ÃÓúSÜsé¯%%l¡%66#‰óhìûÝb\/¸§„;Mž¼°W÷Ùþ÷1žµ‘©=(Žk9å5÷™U¢ç`䢡öøüdÝö=§åå#U_t\N¡|‡%'“ËËÅv¦Ð Ü–!¶IDAT¶à“Pg{Ÿ‰æ`ÿÚûÆ&u¢côRM~áöy^3&À˜4¶€—–8zÄíg™ú†ÕðU-ÂáßÃ[ƾA§½y&†MTã°iý@ éø·0õYˆ—ƒÎdÒé¸)ñ n‡px™ò¦@#­äzóÆC”ïÍÔ“v üâB›†ž)Ùá¨JÝ©iÎ!þ…½ðØcy°6S`äÝâ0ÅXOÐõ+\}À‰çk/ü»Gÿ{Áï’õÒkþ$¶ïÍŸ„÷EñþôÄø§ýó²·µhG¼žk¼…Òm¸ç3½úßÍ39g ƒrfyù ³æë†i¾Ž¼‡ r@Qr†mÇóú…н³QòåuOKŠ_€/E¯y…Z…ü32½É¥®žÀ9ëù·ëÁk&À˜`L HhTÛÒfA1t0£¡þJ…ídŽWª0Þauˆõ=€˜Ýx|¢ÏÒ<ÎVh4;£­!À_ D>G+‡Ï3&À˜4à&Êù1&`‘0îl¾ÛèEÙîû°,mízã`#\èc`pîß/DtfÇýxš{lñ°ˆ•äGÖð@äSÉâ8`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ š SͺÑñãŸ_uQšÑV)&2Û‘~Áý9ûëzs O¼D“νSG§Öõk Výiúò]úâ!f´kÖŒ'ŸÌ V9 )ßÉï5‹-ÌËéæu¨¦š†rh{¢ÛtLà²Íž†Ä¯• 0&À˜@(pëÊüüó`gÁòE× aþóÛ-ŸVBD £¤V¦†7WÌ 7K!¿r†9ÿ{áÃET§ÎÃãŸ?A¨¢‡”½„P•kšølÚ³ñ_T'¿ª¦Á¥ŒÂÕ¤  ðràå6J 7вÐ\„(,ÀËát´Ã/ý_d{O‘ç¥Ôu¹ùzã™’ôLáĆÈÝžî7Cþ*”üÈÙò½Çþµ;ïhyòy&À˜`L p´ÀeUõœ¾è¸6ùÂJ™Áâ}…%¾ËÈÇ»™B=âõz×Íy^ûà‡ QíʈVî¡¡O%]¯„g¥’êÿÛ;𨪳ŸsçNaAÔVm¬[?‹TÄ­U¿ÔÖZ š„E( !’LðB&‰ ²'°~Õª(b]P·b©kµ¸¢¸TdQDÖ@’¹÷žï&¹a&$“!d"Âÿ>O¸wÎòžs~3üç=ïyO¡ÄbiÈ\)Ôg®«îKÏ –Ô[ñÌHŸ;‡}X ¹hÑQm JŒ{+ÊõgÛÂwªþö[åÂ÷)é˜ìªâÐÞoþ[¸Àe}¿r‡‚H€H€Aß‹þþ’S“–‹]×} ñãåV&”ú­-ö®yzšyi<õnŸ:µ¬saí[° ˜{õ‚üÀ½%“Oày¢O𠩆›˜—¬xÚû!—(:JÙ™?ä1©}Ï/MêcÛ[߯û7ß©¤x9@ïä*5·°ÔX1}aÛŽñÖc9   Æhv”å³z&ñÅO¢ËÛm­4Çyò™©¾e:KbÉÙµ³âF˜ö’Ú·õO¬]Nûc§åïs\uòVÀ¾ÜjYq0·Ô+«ý’7د¼ê>«8˜ýäø¢¢6Ûv…fÀå2(õ~)Õ ŠoÌÜìì°ŸzZ ¸Pr©Têd¬ù‚‚¿Êþ~Jmz ïDx䣾˜X$AÎsJš³Jó&¼‘‘¦\q& WxýÐwÔ±`ÐìQ Ü™>vúô–»·îyGú«…í΀ö?°{¾ù ìsÒÝ©©©žS¨–­ûud¼+¥ï¶’¼ìeLÌà8¡{¥íÁä-_údºpÝù¦i\?ÏÊyG§ Ï-¸ÀvÝņOŽ(™œ¶–³òOsBji‡¶þ³§deíaåÿÌ©i(~&VàÞ W´2[Ý~·5v»–‘(¸+ý¤”÷ åNƒÒx¶_$iF_ê|ïÒVÙŒ@~1^›Oï•9¯ ïB•úϵÝÐ °hÛXø ^\îìZU°¸u¿ì¡»·4Vë‘ 4L ù-àåŸÝ ±ÑÊ·7$(á¦ãº}fŠy—V×:Üi(ûºV ëÊ7 ù¢T¢·ÎSR®r”Yn£»úR(ð'¹f§t:”ïÇP²›4+ý~y äÚã>¸¯Žê†­nÆx”ù›òÊ}yûžL3Y+¦oHi^)Säh|tB]"Éç}”‘{×é^ =Àóp(­á~xéúîs¼ª—²GqL¤ÈS•0¦B´òÝO†xeÃÊ·ÙRw&™I'CÖsX…xn”UÔ#é¨6¯ ÜXôcG+«sôß1FßUßÇ5+(=x´„‹Ão<¹.ò1Ø¡³ å{µò#SúÏCÇÒàîÓs½ûÏÍeÛ"m°RÎ\ôá¯>ÓüIóça¥ß“Y£|KÕ“§1T¾=2Ñ÷ÂÅ-N°•ýR­|{ñ™û©°ËžZ‚*/w   ¦' •ºf»àó}3¸Å­ Nr„óÀòY:xôw;ë’§„ûc(ƒQ–ÕÈrð|] l§QÖ¬¶þ]Q{DÞp+¿Ï|+gM¸œ£n‚¢üà+c,Ã}¡8ö3LóÄbkÂ:?#'ÄþËÚ¢ K÷§á:RõJîÒ®ëìÑ£ë41Ï¿eÿ.àþ1ÝV¡÷‡v˜=!ë+È{ j²nÕe6Ûÿ)©»ï(Ùu_Pv§–sï©Î}2VÀ²y.^/Ôi°ªÀbFI0û‘ðk¥¬ô@~F¹mßX:zt!,Óe.fž¥Z—Òþ ”íx,²,el´ƒ×iäJå.Y²$£J1V¡H?­Ë»!ç6ôs}i0'Üo$}xk om…Ÿ Ë-¼ ¯—ërP°O”>ã Xѵò¾à.”¬|¶O¥çÌ„¢¦híXß䩪֑û/Þ>£ tVDǦ¢€ÏË9붨Wf2›J&å Dh6Jr[µw[QtóMðJ‰î¢|{$eÕ-Mnƒº×®î<¨ÖBµ…^yÁO»–¥¦ŽÞ©ÝPì°Ò-nÓʰ³Û¹Êð‹ u}Xn{Ã"¼×µí™PnÃ"áJaB†aôDB•.äÒXÊ·®˜^Râ—ë·^íJ÷ÐF{8"Ôýô© ¡ûŠ>‹ù˜<<÷’ÌãÆíu•ûX®°¬!åº~]—4åûµÒ?‡uÿ86ÖšÞqw¨¬;Æ{%ú”kŒ Î(BÀ¦îËuÅ (™£fÍJþzKáÏa!Wpk¹gåšµù/¬Y{e-Y½Ñ^ y¾ÉÕNÕ–õHis‚¹Ÿcò²›mû =¬€üw°°k·‰ý.G…´ò>Üðûz3á~|¼„Â…y7á³§'XMzar4¦hQò¬›+Ö6©` #  €Ñ´™®òíéPô:%¤5¥F¼8£=Øý/(œë 4ž²NUŠrÄÉRÉÏ<é‹à»}ƒvùp÷8ÚZÿI±x=\ZÊd(á{¥aÌñþ xßmøŒK\#ù-¯ CŠ Þs]w(­Ibý–—–yœ¡Œb3è]>SÜY¶$/ðtã­ðí¾nŒ5Cí*Ÿ4E–©ýìs̲È4¸Ô@w®º*„v+€kÊ2¯ïÕ÷ß'Is†W®öýâÓ{½»¢âÛ]}áòs.Ó¬à‹þwWÉ_opÖýïkù€Þ=ÃŒðlÃ^Y[Ôü Ôœð}mYýQ¶ŽKŠS  ¯rBÎô:r™ÚEG¹î„Á0m'4>A²)–H€H€Žx‘ Qba(õÇD5¥¯u¹½ ±Ä¡<׺L‘ô-+sô&C¸;<™­­Ðjýø|Ë/½›qÞSÝWÔfwõ¸–üîa÷ ¥çX2 ±ÅÛ”èÕ;û†ÐÇgCZß$¿ïä¹VöǺnZnþõH«ÝYÁ=ýR7îµw'Aá]{0‡øh——´@Þvˆí‚(03kŠxÀ„À®¥$ ­lcUàyl*í‡Þ]…úCÂU ¹ 4gHåì@úÊš ŒT¯cÂsu„Xq«UtLE¨òT×'>ŒL¯÷Ù4F¥øÕ¶½{ÔÛ°œß^š˜VoÙ#4£p‘¿/†~R†¯Têô%=n—úÕÞ„µÑ ‚Gæw„8¿8?p34÷ƒkb¤UЫÒu{—N,ýÁuž& 0fQÀ—ÏjÙCí-?5‘œ` ùû)àó‚YÂåb|Ï—¤çæò¥O;;î6¾úî4gý73aýÞ•|t›K«¶ÊBQ½×uÔŸ`…=+)eŸ2ÙÝ ¼°1”ÿfÈVÈËëjþ±Éžz¢”¡3Šór–Ä;¾Öþ”vÛeåØÜØ u>NË-õ»J’?E.ÆωP¾[Icÿ±ÅÛžWc… úNå\ç3;>ŒpŽ-”[qN’£>˜›ø/ü­_»ì–[?D‚yÉ«‡¨.Ú<ÊwÊ/ûœ¼ªÝ“žßè|Ü–íTXæïª)kúî!wDZ Bk³eI™(ïRi‡,Øâ?Ý;?^S®‡™99_§åüVºî D^ym~^öªªYÙUŸ÷D޹uÅöÍç¡•±Áäì&ØâSu|¾´²¾›ŸÀçç…Xõš"1ëÄ*ÑÛ%Áœz]ÛBÒ8`é ð: ;Žê‹Éõ8dQ¯ƒ“H€H QšÅŨ¨<+Qðä»Þ6°9r<”ƒqØ…h9eö·bý7{\e?c(ù®ðwþŸÚþÚI¦o16fê ƒ{áu;–%]D¹ †ê]p¿xvƒ„ovåZG©á^?â¹Ï°Æ}'…1HK ìoÊ~ ý»Jê—‘õ«Û~,¦¯¥qÐ BùM…·úx/ÌÙßmU¡½[ à;†/¼‰Oÿ® Q„±= Åê«ô‰ù—ëþ˜®ZÅ“#ì~¢Ó,+µÌ—cÓÞÉn üªk•ýž®Âú5evÙza;ï¢nWD:´ ##ä•‹ç^š—ý2Üh&Áõå¡?åçO#¥ >õ~Þ›ŽÛp†8«&ÇÒxí¾‰Iëiøü,Ççzߤ¬é:% ›þŽ•¢W¢`/2¬àÙ˜üÀºÍî’ $üv&þZ>Å÷Y‰l ±g)Cmè˜ÙvyyëÙÙÙëX»¯ˆMÛºµmåÛ:-33Ê÷º¡>xùÚwtaáQ±ú7”blÆìPÌýW¯)îÚ-D%9‘ O®öQßèÿ¤Ó‚œœM^ZcîÚw]¶Uzicê³Nýp”ü{˜ü$tU ;tç夻#ëï6ç 1!ýÙ‚`@OXÃ>³¹ølOJjet®ëóå•kŽ{z àW°€ÏÇ$¼[s´w m ŠRÀÅ‚ܬh íÔW~Xnþ±Á{øè Ò¼H€H€š‰@³¸ `,-=¸o˜o–œå?;ã­˜VÖjeð Âjkn”Åú@Ǩý¼Q§Þ‰€Ž©°~×ã€_¨ì†Êϱ²6ÖWF[·‘wPÊ·–ΰ¾¶˜ÞH*ñß)ô,¥q½sÿ†z“JWG×ÙZßáT:ôgyhg¬æ—á›Ð+*o¦È,±oް {†BÎÓ-üþþ³­¬¯¼~胠°"2O¯\){Ë,¬­YOîÓ‡èû»tŒú*Uÿ:ʽuÞÓqùáÚÔ ßûN^þÊ5Ÿð|“2|©~3«Æj(ßQ®0±£ÒrÐfƒ‡Dérñ~ëà.Óסcïµ\ª¾J6Û ”†ùVÎnž Ûѱ÷¡|Õã46|1w¯ÅØÂ“(ò·BùÎCçcÂrŠ0D ”󜰒®…„¯ºß/Wßµ›”ïç±0·œÊw$>“ @Óh& ¸ü/~,›¾÷‘¥@‡Ç…YQ’#UG&»™çÇÜwxŒ˜£8Pø6éÏûiZï@Êcão\ß)ø£·Ñ‡W G¶³…Ûšw@bduû·5íÕ:œJ[²m×¹Ðô§×z¾ãçU†Âᇠî")ÜQ¸j98¨©ÕŽí×aB·’:.¥7N3Kó&¼Q; w(´¿÷JÇ:Œ eÂí ü~‡Dyõkßñ]­÷ð«†îšgüeôjSEÄj‘>‹à#eÛþÙ§P[Âw@Qÿ»ëˆkð¼B»v•…ÊÎöùå-x]uÀ–!JKór¼èFs2ù­àºÀªÀt/BdÔ{@ØÈ‚‚N{ËÜ•`ûÜa&k¹¼H€H€G YpÃL~Ó©|£]ØÊô&œOð^º5í(l¦>6ù¨‰:íz‘ŸîzñƒO‡œê•ˆè÷ ¢UÑ–ƒÌ2ø‚G¾iEøH$@$Дð{—økÐØ=›`Yy'±-É剕Oé$pè0}¾„~Þ¡èîhsìVÇ5b)^†ÏrO(ÜgA»¶xr`Yõþ†šêµ§Â<6íÊÚåpPTÒÆ*«°\j‡ÄMZ|Äÿ Ý,j× çUú° Ÿå6ƒq”{8€Ã¨ê?$ÊV}uøú×Á]µDê—Ï`°ýµû |Ù¯œ¥:ü'Æbnv 1éyFL>Ó¸| mI¯¹Âgêâ÷#Kí÷À+ —“>˜ ½Š×?A¨Ößy鼓 $Ž@ÍΉk¢J²!å=8ØÆ["mÒæ´²ÐVuX&ľï&m€ÂHà#uKèuDBy?q‘PäßF^W‘¨a+é]¸¡éVÁ)Õ¡+ÃMÁmâbLÖu(ËðÿðEˆñÿÈ­¼)•B]h*ñÇꬨÛì;Ço‚ÛÅ^eË‹Ë}Õ…#[õ¦ÄðÏaTÕE›äW–†îÂ.Q4½)¥c«—wWÖf“¼ Џ¯»9aUjªt÷üqG¸ƒà+?@ùDØR­')8ç`¬ÝCο¼ŽÃ—þbÄH¯uëô¹—Vß÷÷Jòr3Ó&_Žº7Ã*x§ÄÊþ ¾òL' 8xÍb×Ý4DçEøaÝ|ð]®K‚œu~Ö·»êÊa ®”2 26 Ë´?iZBdW Ðû„÷  ¾ˆC›¦¤çÞÕ[GDÁ¦Á±°ìþÜgÊ?{m—LÊ~ iÛ1˜KïÊyùõ^^ä½Ú*¾ üÍáMÌš11ˆYUFT9}•RCYdÈknkí‘61ÿ2}bfd¹¦xÖwaŒÞÁ]ý´E;#0å$„þKõäC™Ö–í~éwÝÕÎK GjRr6_Þ´eáóð`ãQx¥ÂàÝS¦˜û\N¤œCtUÇï‡ïüp”›åEMñdǺëÓ0qˆÒBr—j6±Ê2H€Hàà4›~iæ×eøwpÝ­£¶Ÿwiß5*ÒA¥b&éýã³2ã-×/ÿ@äé°g:ÌšW·®{ºUÒȰquIcÚ¡N ;Ý~®/5y?¡¤NZ^í7ÜäÒÃõÆÀdÛ_AI )×þgEhçf(Ïãà»|=|›_÷Z +Öp;÷`^µÈK¯ëÞ¾­?Šè„ë>‹Ã¶¶+G}†Y¶¡Ã¨"Ëìs<wùZûÇ*ÆgbwèKl@­™\à?#¸˜¨^ð§ÄëGW³ÛJX«ÁDäU}h–—Ž0Œsà'¾@ g†ørËŒ_ŸÊ»<¹K»\¯L¼wul—L¼';l{ÛÂxë° ÀðVg¼f#kàPžEXšÚÈêÑÕ¤ØkæE—Ýò¢DçW¿ªŽ)ü0¢:,ÇUÍŽ/½q«bó0еÀ_{üøKòrî«-$Þrº^}mEÊŒGž>IÊÇfŸð­Â†µéø‘N‚Œøa^a##åég} â#ßÓݾ߲úÃ/”ב@ ¨´eÇ-C ѵ‰Æû¾½.пÿKÍöª:ÔjW»¨¨)1˺§Å±-EŸ8KL¬Ã¨bÕkL^Cw /,ìÐÆ0ì)YYµ’§C veoñ¬æé+ë $ž@³+à/¾ØÏÜóÆËÀ¢…åáÆ_°üa³–qÝåã'cIIË-¸H*g6|)_ÆîÎHá¾fcƒÓ— ‚9SÃKÀ»í·±¥o±•ùM¤ÌxËÅjë@äa‚"±êS¿™Ô[Õ:8¦#µOfuú >¡&s_òd޲Šz”‡B áâsJwó¼žTÀ=2GÆ}ÊÿÏBÊ~£=ê`FŒïÔ[-ý펲}ûÁÈa]   Øt»ˆ]ýÀsµemð®»ÖÝFoÈ„¢°Q¾ )ߺw>ŸÿCÃl1Ê÷ÚÚ½E„_µl)þO§‡—t•xZ9•ûÅŽ·\¬¶"ÛnHÞ°@þ˜ ¬Ó›Æô |Þ†(½±¿ŽÀp§FÊ«°+çb% K •™Îç#ƒÀøôÐ;R&‡Ùô{±Ë|¾Ný©|7š +’ @Üš]×=“©;ƒÇ»c|†OG<ˆ[i€‚aÃb]’”ÒºÏe·Û¯Ä3JmÍÖVåÈòÚšŒ„Ž3sr¾®I‡5ÜêØš×xˆ·œ®S_[*î7 o÷GÖÓϺ/øçRS$½àå¥åæÅHÖ”äÝñdÇô÷êð~øÈN¯ø¸[Òñç`’6ì¨P|±F‹²_ ø1']]“u372ÇbÅ<  h*ß‹îu~P¦½rp¦ÛGúŒÁPþ†ô}ʰW'@Iø·aHËߢʼn—w‡ ¼u×Ö}Ù{9i^+¸¥D…YƒeºLº¢}¤ÄxËEÖ‰õÜ<ËZ¢}½/Ç5„UŒ¾f-±^ÑVq3"'x¬pUZ‹ÎmƒÑ%ùêH$0dÈåÙéî-Œ6Çaš«7<¿‚ nT|hÍ“;˜#7 ãwÔ™'ä¤9ûí{8ùqÌ$@$@$Ð\Ìæj¨¾v´[òž®þ+ïnu´[YÙÍ…™WºrgR§c¾èÅö_iºÓ4q ôn­•Ýš §^´QÒÝV“€‡xËEÖ‰õܼ öºAPV϶F3lbð׎«®’Çvîï¥c—\ žo›5jT%þ$¸‰Ý>Fu^G2q·ìÔ›gè¿’’³üßùÞ;‘1:ø&Cæ7ãoÙ³¡ê{§CP¿u$£âØI€H€Hà{!ð½+àµG=pL™¶‚GX¿¨]¤É^C÷ߨ­È^la¸ŸœŽý"ªÄ[.ÞŽÅ–çÜ }2Êý$#'x¡ã+%ŸdfFFH·3²  Shs´í¹ž\¶Bøµïp0ǵ8‘ðÙxûÃr‡/ŒŒ·ôçeݾ†DVçhûxð‰H€H€šŸÀ!§€7'¨!ØRE›“ô"T>Øð'OÔ}@ä“ÿmÕ)åI}(F¬ríà¹Âöo›ÌÚo“gäX"ËÕ'o|QQ›í;Cçu3zÝèÕ–[x†«ì9Òô_13玚‰ÉÜìlí†ÓÆ+§ï°èsŒyþ1Œ‚I…Ï$@$@$@$phø^}À¿oIf;à£ÎEÌí7¡|¿&àg®#èCoàðò@Ù¶ò>ºõ•ÓyŽ­&8¢2M?Ǻ"ËÕ'Ê÷5ñ”e¥Öøíº®3i?vè5X¶7é¿pc5Æ<    8” hËsí“0µ‹Gí>×U®*-¼q²vñ¨×u•«K^T%¾                                                                                                                                             8,ü?!‚aViÌÙIEND®B`‚neutron-12.1.1/doc/source/admin/figures/demo_multiple_dhcp_agents.png0000664000175000017500000014637313553660046026043 0ustar zuulzuul00000000000000‰PNG  IHDRç.঴sRGB®Îé cHRMz&€„ú€èu0ê`:˜pœºQ< pHYs  šœ qiTXtXML:com.adobe.xmp 2 1 743 65535 558 image/png xmp.did:fe6944f6-628e-49d8-9906-268ba47de779 Adobe Photoshop CC (Macintosh) 2015-10-08T15:04:06+08:00 xmp.iid:fe6944f6-628e-49d8-9906-268ba47de779 created xmp.iid:fe6944f6-628e-49d8-9906-268ba47de779 xmp.did:fe6944f6-628e-49d8-9906-268ba47de779 2015-10-08T15:17:03+08:00 2015-10-08T15:04:06+08:00 2015-10-08T15:17:03+08:00 Adobe Photoshop CC (Macintosh) 3 Neutron Server Neutron Server r…J>@IDATxì`TUöÿ¿3™ô„tJ!$‚€t"EÞíb¯[ì?×uwÿ뺫ëê®Ê]ÅÞ]é"½Kï½w@HH˜ò?ç½¹™7“Â$@˜IÎÕÉ{ï–sïý¼áÍ÷ÝwÞ½&HB@! „€Bàª0_õH„€B@! „€Ðˆ8—/‚B@! „€ð"Î}äDH3„€B@! „€ˆsù! „€B@! âÜGN„4C! „€B@ˆ8—ï€B@! „€ð"Î}äDH3„€B@! „€E! „€uƒÀ4Lïé?ý'A\N¥(EsúoýKÿI¸rL²Ñ•ƒ+–…€B@Ô&&h‚côŸ!p¥¼ŠWñ ý'áÊ‘ó+ÇV, ! „€¨UvصúÅ¿k|=¨ŒŸÈ¼Cÿí§ÿ$\Yâs~eùŠu! „€B@!à5ç^£’ŒB@! „€BàÊq~eùŠu! „€B@!à5ç^£’ŒB@! „€BàÊq~eùŠu! „€B@!à5ç^£’ŒB@! „€BàÊq~eùŠu! „€B@!à5ç^£’ŒB@! „€BàÊq~eùŠu! „€B@!à5ç^£’ŒB@! „€BàÊq~eùŠu! „€B@!à5ç^£’ŒB@! „€BàÊq~eùŠu! „€B@!à5ç^£’ŒB@! „€Bàʰ\Yób]! „@ý$pôè|ýõØíÖZp/òqj{Mk­ÎÐÐ8Œ;k­N©HÔe"ÎëòÙ•¾ ! „ÀU#pèÐräçï«ÕúC©6þœÁ¡Z«×f+Áºu¡gÏßÖZR‘¨ËÄ­¥.Ÿ]é›B@+L !!ë × æ…@ý" â¼~oé­B@! „€qîÃ'Gš&„€B@!P¿ˆ8¯_ç[z+„€B@!àÃDœûðÉ‘¦ ! „€B@Ô/"Îë×ù–Þ ! „€B@ø0ç>|r¤iB@! „€õ‹€ˆóúu¾¥·B@!P' ˜Q°­ ~ü±;Ö ¨“=”N úB@!ª/gZú)„€uƒ€#yã\ƒ#H޶ÂTÓ^ÙBð󾿨,gŠà€ ! N#1uš%œ‡Hüš‚•rBàÒÈÈù¥ñ“ÒB@! j—À…Ø·3‡òIP×0ØCqp]lØBK>’šB³ä|ÅcÏúöX0öš–bB@\9¿4~RZ! „@­°E£Ty`k5áüñ4ìñ Z¤œF¨öœÛ‚㫺bc¾ÞÜÒ]1wÒ|-º·,.냉RÎm†{áTÛ²!4ö8ÒZíCãpwãÄÁh/Fj‹Seœ ˜‚O¡E³"ß…ƒ?£IÓó5w›aƒ„€¨6çÕF&„€B@\>Ž Qعª-œ³¢Aã#Ȉ-†ÅŒ3?7Á¡]Y8qzº´;F݆èô-h}$ [#¨Énd6,!oÐ\8–‰Õy@|“ƒÈ4¡8¯1žJĦ5@h÷=ˆr„ád!µ?(±!ž#ã„Ä a(<+‰óšÐ_>FbIÔ'—,ÎÏ–ÚñúË©O̤¯B ÆþøÇçaªñÛ[5®V Z#ð /ÔZ]¾_ÑJ/šhÆÙ}š0Oh»9JËFª“Ž!vSGl8žŠy'‘oEpÔiĽ@vƒžø„bè/©WÈê<Úu݉„ §ðnz!?uÄÎÂx?·‘–P”°Cyp‚U1CKÍAE¢ã’’PÐO<+ÈcÈŽ}ûöÑgæÌ)1F˾žþy/rI–úFà’Åù©"«ÆlÄÃÏÔ7vÒ_!P-“ßù;òK¬ˆ ¹ävÕªW2 Ú& ¿:ñ½›ÇcÃâéUã·EàðÑ`Àr M"Ò–Å®Ñ(–ãñÈ;[|W3¨„§DœælÊ\Š‘ô[]hAÑ0[ÈÑ…B€­ÂÁS€Uü¶@¯^ OlІM¯Cú5O³U ^àß B "¢*¢"qB@! j€ÃŠÂR®(–ÆU^cQ¬ç}òFn)¥ÎÑteÎs¢›àp\Ô€VÈ»\ʾl…€¸œDœ_NšbK! „@uØôQìÀSÈh}a•¹ž¹{ކ šÌV}Þf]×ìÆd8hÄ\Y¿àÕH½[a9Bà’ ˆ8¿d„b@! „@ͰPÖ~ˆvDÆå!¦Vþ1‘OyÝ–„i>å¡uÚKÂqžºc%ßóÊnjÖ])%„€äŸ$‹B@+BÀR„!dÙ‰ÓÅå’y»TƾŒµ¡a²W“åê4£èTø•Ó eäü2bSBÀ[å¯Þ–”|B@! „À¥ ¡Ü¸Ïr‚{âpÞ9ÁŠfÔaÁé×`á¼k±-ß5¼­f|²Ûjøn*A|Ê)Þ!8¸+ņ¥@íÅ Ø}€î,'Ñ,Á5sÌ¥uRJ !PâÖRZ’W! „Àe%@î,Íw å$Ísþs+ü´: ÉÎÒD‰A8{‚æ9Ï ‚9j’#Õð¹!çIXG øP*v™óœ‡¦‰ìˆâ}J؃̄(l>‘å?Å£q\,Všÿüh,ÎÙ/ QÛ=ˆ— ν*9…Àe$ âü2ÂSB@! ªKÀX€ôNkÑ`/¯š€=Ûk&,¡gФÅn¤6;…0×À9,1û‘Ñ8Ûi5Ñ»£Ò¸ÉÕç Ñó&m×"è@3ì=‡Ãûci2+¢Ž#£Å~$Çʨyuϣ䗋€ˆóËERì! „€¨!“å§o¥ÌÅHÊ^M÷¼Q™+Ð/Ó=N?²£Â4s âhÔ>®yEe$N«E †kW«¹R¯B@! „€¨»Dœ×Ýs+=B@! „€ð3"Îýì„Is…€B@! ê.çu÷ÜJÏ„€B@! üŒ€ˆs?;aÒ\! „€B@ºK@ÄyÝ=·Ò3! „€B@?# S)úÙ “æV@ÀAËÛ©UõL”n’{Î (I”B@!àüVœ;ð‡ÃÔw_+‡Ú¤Ö7.—rñe÷â9Ijõ"dQHÁÄ;Ú>íi"Qo£Ùl&ýèÐÒ¹Œž¦Ðmpœ^P+¯ÊrµüTž:¬ÕAú¾sËí5qºn®ŒKù~èÌ´x*ã°s‹ô8¶m·Û eYð:ë#»åmiÕ]µ?Ì“ot†ÎÖp?ŒŽñð3ÎTÙ! j‡@dŒ>éxl£ÎµSáUªåä‘Ehš1ö*Õ.Õ ºGÀoŹ.pYÛ5q6hРjMв¸¥`РNa]¶q³©ªÚº%jvt1Ëéê£ò°Xä Êª­1žã¸_Æ8cyNS6<÷ùXÙä<꘷*¯qË¢V¥ñÖ(vUy•ÎÇ*(êØç·L›<Éç›) B îˆO¼=GÌFIѱº×9CZšѤù0CŒì !p)üVœë&ÁKÿ+aYF‘éÔ¼-®Ê¨íE H†«NÀX£ïÇUo¸4@:A ®q÷:Ñ鄵GÀŹ.Ì»5H°‹8¯ŒÄ :KÀqöÖ’:Û?éXÍ ˜‚Ã` ‹®¹))j€ÿŠsÝK„ÙX ¨¤ _'`tëá¶jO8jødÅ×û*íB rS¾ø òDI©÷†ßó+°H— |•€Šs§ÿ¶æÓBnÞ5qkñÕâíb'|o}}|¢Áå¡„:{éëß¾›“Y\Ê“’!P7 :´nvLzuI¦M›F/•î]’-),®ÿW+›×â\ ¶+³ÆvÝÞH­±•ËZ°"VçÙTõòê娼¢:=íê÷eú ·vÏÆPf-W:ÒŠkâÜ5ñŒ§I9B@! „€Ïð?q^vÇKw¾v›6ÕŠ7n-¬ãXªy @Ïc¯ÏŒ&TË+¾Š¬±nGùRåÛÅí¨ÈÖåÃè[mÔ\—µ4´Ìåg¢ñÈåå¡S`ÓtŽZpòäþyqõÆ)̹Æüå_ÔeÛvݽ¥Â­Ë¾B@! ®>?ç$¶œZ®lÇf-ïsÎBÏ“-‹3UõŒ÷⸢ÚªmU-`˵¡²vÕ‚vÐÍNEb˜ùÙ´yÐõ‡8Û Â¹¼ö¤!‹]å÷Yí+¾ê˜ÓTà8ÅFmIAëçSeòØr4›N¼aª]eöTf’óZ\Ù—²ÙB@! „€ÏðPµ>×>CƒHœ•½ÃAû,ÖÈßœUm9q®Ji¢R¸Är™Ø¦ô;viñ­22œyÔUßeQ·aýz$%'#..ÎeȰÇV $þ쉮‘_%(Õ–ó±Í²cÚW7 œæ²E7 d+ @œ¦ÊðVíWd˘Æå8pœ&Pi_m51¬9p›pèÐ!í“wêÒÓÓ‘””„Ðвz4#Î~ºlé-WöÔVËËuò޳­œfl—1¯ÕZ9Z›ávRšÔÉá­ŠS=¶Z9ʦÕmržTÓ¸ÒbxÆôé˜?o^YßæÍ‹Pæcﺋx¨]àÛ¯ëbj§É8Ǽê7ŸýFÀX¦¬NÖ¸n4<ÓØ¾ê;?ç`¾üÄ£Œ¨鯂”Oåàsσñܽ ª¤[9B@! „€OpJ@ŸjS%14U׺¤þ(+‰´r#ç$žmvJ$…ËÂŒýª9³É@‚M-[OEI‰+ÉÆ"š˜çüº°¬°!dËÆùhåI^üH}Ì4ÊÍ#ò\Ÿ¤,0yßLõš4µ­×Æq,ˆwíØ¡µËLKÌs»ÔÇÄ£ïœâ¥--Ÿ³Í®þ¹ÚÁýà>±Ø5~ø©·#==cnº =ñ„v#†Ïè¦äÔÉ“eùu <qàcãG‹¬òªŸËi%m*+Ä ÕRô] Ó¹gZšÏ}æPÆHK£8ÚrJ@€³ÿT”™ð¹• „€B@_'à‡#ç$ÞX¿Ñˆ9 1ž¯T 5 6ÅÙ´8 ë9-<&§ï³ØSź ìÚ±aÃzä´kW–W¥ñ–‹±=Þê+’²—àSvu1銧F3åöÝÚîLõ´å²Yµ­rÆ)BÙâ4.­Ž¹Þ 68GÉïD rNK&žØØX|öé§Ø¹s'º;V†ò—¹±1 ìªh~`Ö8Ï/®Î…Jç­™oXHDƒÖFg„vCCmÒâØ :æ¶s}ÜÞò±zò ¥q6úumGç¼ëÌËÑ„€B@!à‹üHœ³ˆcÉE"7H„“ÞÒGFõúKé4j®¼-ÊD#åÓDœAЩ"éä¾Án&Óiô¼mNŽ–O¥¹¶<êÌšÌNõ.**BÎ5× ir’&ü¨ &ׯ[‡¸øxMèªò,þy$šÅ?‹ÞST_QQ1–/[ªÅÇáÇÀmY¾lI$+?øóçÏc=rscI,·#!!!ʴ柘”Lu;4Á]D¶Ú]Óέ~Îì)†‹‹‹5_zÍ¿œÒ•Îiw Ò—/G¥s‡™î÷›Ëq?XÈ«Ày4ÿ|òW?HùNçåQ_ÒqèàaÍyð‰S¶–“}öoW¾üÊ63mGLuÛ|Ò õ›]ÊØP­È¥³UgÙ¶¶ïŒc7˜€²©é{¡|…´RòG!p% Xqhò‹øÇ¼ôö9 iRÕÏ­§·ÌÁ”™K±ñ@¬tá oÔ ×^?ƒ¯MD°6ÒPu[m'æáµ—&ã¢Ðû©?`dЬœ]51I¾K ª«…¶šÔ¿ÜÇ*™ƒsJ>ý@Ö Ó­Á™À¢M u}T—„½*D[ö+gÿrvópó/wæQÂoÁ‚ùøžD<ûesàü\¶WïÞeãè<êÌqFñÊ•E7 Ï$LY s˜ðíÜñšàçøXNÓÜ]€?¿ø¢vðî;ïhbžëda<ŸüÂÙGž…-÷i·ߢk·nš}Nç0Þ\滩-z>Jg¼Tà¡GÑŠ)n æ»÷›Ë)_|ž¡†G§¿ýæM´s?8t=Õ,ÂU>-’þpÿ&P^nÒ3ʘòyàú5vd'Žø)n̆oP´s稺:¿|®¸|³TvR´C-–S$! |„€g7~‰×Þ_¢„ôž8sv­˜‡_ŒÃqÇsx¨KŒþ$°Ò[qü§Å$ÌCn*ÀÊ%0(% Á•毽„Ò}ßॷÏà¶îG¦/4¨öº.5 ð?qξÃ,ε©©ß´¯DS §;§iÿX˜‚׬éÙY ?d5Ì¢“…®ßÚ.‹È®]»j>Ú\ï÷߯ KqW£Àª O…ÀþÎŒm{ç˜@B›GƒòI펂÷y4Ãô2&Zs>ýøcM ?öøãhš’¢^ûmM³¸UÛÍb¶OŸ>8E£Öœgû“»‰s÷vtéÒEôãIüó3È¡~ðè¼ Ì-ì©~&ßt¦÷=-Ìq\&&&Fe×n<¸l/‰Fó¹-,®{S»TØAâœûÖ’FζÙïoØÿ_ÙVLù©×Ëìø¤ìÉŸP§ç¶rpÅèI6¹@y|´ŒòG!pµ XcÉ”Õ( ¿?y;Z‡é¿Ý;¥µóÖâÔµ}‘àòÂ,ßâÒƒX²ü,™·bDàD|±nvh6aú5±|ÚŠ±¡`ÏnØj«B©GÔ þ%Îù:ÃÊKÉ/\ür&— 2ÞÑ›âX‰6.éÒhz9ŽSÅá?ßx,t ¬¢Ë¶,v9(¡ÉvS¾4¢Ë£¿£ÇŒÑFrU¾AÐD¹«R-‰Ë©K&7ÕØ¾Ñ7Þ¨¹{pFë+ùœœvš »²0[ÎÏŸtƒÁå\¶‹´'z9“6z¯–;ûÌñqq±ïè¹Ýþ’_¹ƒæ¿w½¼jÖ†rË#B@_!`‰A›×£g{.œÆá|ŠŒn‚¨ª~©íç°}Á·AÏ– èœðµXµp3δïŠhíçÍú/ÞŸs:½ípáÈÌùàÒë8³@˜'›M_áõ÷VÁžÞ £ÈF£€ì\2 3?x'îz÷tˆ¢ß•X¸ÀñùødR2zŒz£™qrÝD¼7q>ˆNÅï†7Á5w=Û—ÿÆ„ý©óË‘hÑ "Ì=Ï´ rªú'_.óÕp rMÞÒ‡/:$Þ4·gãhŠâ«Sò:Å›(Ö´VX/Ey”ˆå‘pvÃ`wv/Qs³; F]÷ ÊĨÍ9Q¸je8ÕÇíÑôò×  ^m‡þ°ˆåÀ"˜í([1N!ÎmQA¯_·Å}áQjåÎy\s„ëõ([ª|"‰ôdB#âÜöŸO~ë|# úý¯q•÷[Ùái!44DìüB+‹s¶Ímc»ÜN¶Íýü׸qÆbÚ¾bªâÈ7¿¢àÖêžÛ-[­V*¦÷»¢ò'„€ð-ptñD,=Škn¾ÑUZÇ™M˜·Ýа=‘¦¹±¤ Û5‘øiÉB¬;Ù ½yȽdæ/þh2ÜÒK…oÝéQãñÒgÇpgÖcX¡ øi€9 ÉM‘$>ç¾õ5“Öø,?çNIËÖZ$îxÇMœq ]5ìä“Îñni,Šu%L¹t!K;naò)ç—yTWUŽG±Ùƒkv l}ŸM›ØÞô=wQ¨nT}Ë9ÝËq}Ê „g0ѧ:Ôsi;žBXÙRmÕÉ2ÙâÛ}®uºN’¼Ñ×›]b8NÍ%†FºYPsàpög/ôyÜU<·ÙØG®[=à<ìjö8§q¯SeCßR ýÏ/›ªàê—ÓgÆÑú¦}T.Ú‘Ó¾.Î é²+„€ðUŽR[ö þ3åâsÁMY×”rm¶áäš…ØgD·îÍ¡ÏᄦÝ;!fÉ\,Yõ3zjœÞƒý4W@l÷VˆÑ‡ÈÉ’ QY¹ÈÚŠMN»ö‚íØxˆé…X/¶§*ŒFëìÌœ·{Î BR´3>2Y †Ya"AiÅgPâúISFd+„€—üLœ“Ò¢¹³y¶DÚ¡7;éòrœ[ø…?gÐ…!ÍqN./¤ýÊ‚Q0–EjÒÐýˆÅûLk£çôÒ£+˜Ê\G”[‰‹,âÃÂÂÉUD¸JTsY]7ê/Uê¶X9²´t¹Ý0¸rpA1çÀBšÅ¹²uú4?ë¤õX׋›|Ì}¬P¬r"Õ¨FÏ9·Qb󋙚f´”¦ÙÑÊèÂ>–ÜI8°¨6î·ÞWhcÚg[ü’)û²ó‹ ìÞÂþì*°;—f_rΫúpèàAÍ6« íSû\ôTJ[ÃK¿Uä’$! „ÀÕ'à8‹íÓÞÅ»s"¡×#øåÈLš}¥ŠfÑˤ?-9DvANL) KõÌYh3óW,ÑëG£qñið<^ѱaN÷§ÍÀX$G¡LœÛ O¡’ÎÌÏÍwæqÛ8p’à•8Œ@k\GË©5·òŸ7kr „@ÅüLœs'øJàãRçf››8WÝä;Yº>œ¢ OÞã Äµ~¤ç$u¨ ažq„¥ , Óiníï§O£õÃ䪑¤ Iå’Á£ÃÊ †Eµ6êNB’íq½jzDÞWA¹¨×—Ü4Y¨üÂi½ ©çsÐ ¨KµxN÷ œ§\¿´8º£áªé<Û û‡³@ç¶ë/Z2³æjÂñ,ƹõ¼‚(³à>)wî7»÷¨~ëtÛ§òì†ÃL´UXé˜Å:n+×Á¶9¢Ùæ²ì®ó¯þS³Í³³¸*Ãw]: ·”²Ï4ýXë|YÙB@økV}þO|¶¶™#žÄ=½RÂà*Bé¡% IZ(¬À;/¬¨ çj,Þ?7èO)ùú_ep&Çô¸c¯Ö~mÝò›,ˆjT6ôî–$B@\>~(ÎéêÁÎn6[,Îi—\>Û²»†\‡&¤´7m݃3š8ó:pfË"luzÂpQsƒ–È¢÷îØ´ûŠ›##ÔyÝtœÇ¾'ae@ ì¡ xcU•ïsyþpw3­<¿¤!ÀüSœkãäNnÀ… äx® ÷u=!ñÇ¢­"áÆ‚ãyq~’5±hÌËÓ)òÈ.›SùyzEª,¾yT]¹y¨êyË‚WØä‘r¾0…„ku±=xeÌgŸ{Žl¨ù¿4R}=åãÎ83ó *,ÈOÊ£5c4±¬ÚÃY^øË_œ9©œS ÷îÓW+ãLÐÚ¯‹T£ [ÀÑ ·ƒûÄÓrûU†ÝRT¿¹_z¿ùÅ[›–—óóªTn 7‡Ù èÕüCK×ÿ(ÿr»6mbyÛ.;<:϶Ãõ¨¶±>wÆcCªC{Ê¥J„Bà (ľ 0?ÚÓ$Q™Ñ.z?fNÙ†Ò°6H.Ù‚å‹=šcn€´ÙhbTß$¬7ÏÇvkÒ¯k[ñl.æH´¾.»¶bá®QLsžoZ>MnÈŽEéᵘ³¨‰áÀ>U¥¥1rGtIJ÷WàÝÿZ0¬o6ãÈFZ!zéÄôëŽ`n†×#çôûE¯©Z÷b႟p>©ÒÛ4“éoÙ Jø¡8פ2+Kê ts•³q(¡¨œ:Ö0)GŽga®™óÅz L],s>œW½¬ÉÇʦªCűÈåzxÞuÆtŽçÀS j"–Œ³ÛIU¶X³æànK‹¢?.1̳ƸÚËu¹Ä0çö,¯Oy¨þ«4Õίö¹ßƾ»‹c®GÐ\†ÕJO7ô¾ê1ú_Å›çËUÁÓ¶j‡JW[Õ>6îWœßÅT•—­B ö”`çü©ÐŸKº×š|S[´m}‡ÎqüüðÝ÷ ÚQc OÏ"qnH²Äšû`ÊF¯Ö•ÍæBO€Ó¯CvȬ¥…‡¢ýn¶…K¾Á{‹èÆ µÞ{ Î}ñö9ÛøŠÙö6<õ`CLžµS>ZB‹!qiè0ú1 ÍM©ÜÕÅÐ<×n’{ Dö¦o±iÆØ™<OeŠ8wñ‘=!P1?çÎŽhꎮ(ôRèŦÊsmJ(êW#wU‰b…óq•î¶ÜÅ¡ŠœÇ=Ÿñ˜ë×mé¶õöp~UžãÕ¾§­Ë)†õv(zkŒÝûàJQmãã¾vJ\Ùœ{eWýr)\ÿ¥U¿‘Ÿg»UžK­KÊ ! ¼'`Aòˆ?a܈‹•è…ߌëu±Lîé èûì8ôu-wd ÍÄ݇») o Ýo7d³Ævk¡¤À²q b²àúṮÈ}fr=3ToiؼÐÃ3§ !Pÿçš¾å?º°tsk1tÖ]¤i…4­gÑË*Q§ò*!ÇÇ*Nåw?vÆV¬HÝD+çtÙÕËUô·"ûƲžûW]•à­*­¢]<ÎÕ/÷› cÉÊú¥úÃ6*ËSQ¼1N½Üj¬Oö…€BÀ@€VÝ9{ækŠá·÷AçÔä¶Ó;°·Žèš„ý'ÑPHv…€¸ZüSœk¢œ„&«Sš÷ܸPMå +¦F±gÜW³r›Þ§íz_ªêœ•µïbuU$†•-µåšU¾ªìñH~U¡ª²\ŽÓ/–§*û’&„€!`Ath¶¯]ƒw.c`§æˆ¸p«gMÇa$aHï¦ÕtW¹H}’,„À%ðSq®úÌ·ú¦‹ºµ¨ÜÕÝE£Q´²ã±1Ÿª£¢8•æYþb¶ŒåŒvûÆ<Û¯¨œŠSÛ‹Ùð:Ä·Ãc•O%ú/&ìUŠoùFLµÑs«ò«xu,[! „@ý&€„žáQÇDLž;_mâI‚C~ç#o‰^ý\ Ôï“+½¯ƒü÷_$š“X#‡m®k£€óµ5Æ©~UdŸã*K7Öc´[™‰B@! „€ðÛB]§Ž…߀»~M+•’ ¦oZð‡ã4A­ lZ6ØF¢—öm´„°(Ÿ.iq çè:•›ÙmC‹`‘O¦øØùÑ2R\ùNS¬Qf>âzµm¿,Ú£Û¥UŒ³Ñ>ûP³)Ê¢ ]ÚWyµ›­eÓ™‡‹r¸[PÆ9\y4íLÖí; gÔ¬¸JsQ×Qe\+DÊ·CoŸJ¿êÛ€:pzÕ!J„€B@Ú à÷ª¥L8C½*¨3W]ˆ{ Y–®zpéPŠq”ç¯ ”•,Ÿ¥>Æ”a©—> ! „€Bà2ð{q^) ƒÈf/²RR’ „€¨SÉÕqÚ´iuªOÒ™ËHÀ .£U1%.:!Î¥EpœË¯ŠQ–Ó¾ñKhêjT]%Ò±ÚUVUu¬,;*_ÀTn·œ±r9$â2`7¤Èxòr{®r™Œ‹! |‘À ;ΟóŦI›®6rs4‡_íVHýB JuBœOùðͲN*7µ-Kà»e·<7­ åU‹çTäS­ì²ªÒU>µUy«[N•—mÍŒxø™š”RB@øS`À B@?$P'Ä9s:th9üJs‚ÚW‚\«­*Ì3­T'({Õ)#yk€)(S¿û¦ö*”š„€B@!p êŒ8¯ˆQ8÷9¯çqEå%N! ü€ÍJ#24—!àI€]ÅÍÑ“Šû:-Î}Œµ4G! jÀä÷Þ¨…Z¤ %0ü¾Ç`¢Þ$_% âÜWÏŒ´K! jL "WÇ“‚u†€6‹í½“ â¼ÎœÔ:Ø‘ê9X×AÒ%! „€B@!à+DœûÊ™v! „€B@Ô{"ÎëýW@! „€B@ø ç¾r&¤B@! „€õž€ˆózÿB@! „€¾B ÎÖbÅ¡É/âóòv~ÿˈõ\Ùýüfü÷¹wq¬çSøÃ˜úÊÙ¢vX ¶cñÌyX±eŽ”ê- j€Ä´¶èÖz´ˆ@­ÝqYcö«/cºy8~ÿL_$xrô!nÒ! „€B@øz(Î]§Å¶{ ¾ÛÐ÷·o“+Úg÷ì«ñÉ+ŸbCq²¯€Ü¦±(ÅéÃÛ±jÑLø×VÿÕ3“Z;ý CJ§\äšš"´Öî|öôHÄ€B@!pÉê¯8JAVôAlš0 Û[ÝŠÌ0_W—V_ò=6E¢ë¯žÆ-é!.Þ¾ zvIÁ¿^ž„%Ó×£ßã]]ã» ìVL³Ë~e_3S$ZõƒV•¥K¼B@! „@µÔ_qŽht½)?¿ù#¾žÕ ÏŽLEpU‚ÖQŠëfaò쟰íÈYXÉy$¼Q::ôŽ!’â8‰¯¿ˆ‰ùñèoGšq}ƒ’ÝøòÏÿÆŠè‘øÃ“P¸l ¦.X='ÏÃÁv·F÷Ác0 '•Ÿ Ož£“¦ñÁ儳%¾'þcGX¢" ý°âô–9˜2s6ȧ6›¨®6è6x$´M@ ÷×ö3~|õ¯˜z+ﺟ³'“¡Ÿu~,¸H_oµÿ0¸µ˜­8µq&ÍZŽ­‡‰‘)‰m{bȨ~ÈŠQ=ó¢MÔ,ë©õ˜5q–o;‚³´7,‘HÌ슣 ]œ²Eñ„€5"àß.Ž _Ò›±xÞ¬ÞÌnŽ%°‡àè$¤µíоýº¡e”\+kôÕBBà*ðõáâ+‡Çn…¥éõ¸¹[$ò|ÇXVìÈ_õþññì‹êŠ›~þâNôIõ[ŒPV=ûxáÖm%?ÉÈnèݪ²©s-Hè4ÊÊzÓî°›#ÿ_€/dá†û~‡Ûaɧïbö×ï `E8¢:݈§FG£hËŒÿz*>“‰ß ÔË™Ofe¢ÿmOâÖ8àçÕñÑôÏð¦#¿“¯ð͸æ®Çaûòߘ°?c~9-ÄÑï§Ó-ò½U°§÷"WÍl4 (ÀÎ%Ó0“\5OÜõ,îéP~p­ ì?"PÙȺpšji„Ü[úbéësñõ=ñÛ¤³ÐaêQŒj<À㉠UöÅ£©&'0‡ÝQ¹£×m2#*{žx±ŽîØ„ ›6cóÆÅ˜²af·¦†û{"A¾¹g@…€¨9ÿqqt”äáðêij:¼]„ÃK×ÌTº®’§#PbB§›‡ ½¶xE z÷o‰?܉Cq·à¾iúIçþè<{=æmÛ‡³3HØS9dϹy ºk¸~·cÔ–ðÕOó°{HÚxÕÞ„%4AÏ fCBrSÐò}Ô+·È$ùm Xü€z¨äïý¸äö5í‡[ºDâô‚¯0ï¨]IÒi5 "á´_xâh¬Ùh•S<>Žø]èšd¢{ª‰^ÖY‹ãçá'ri jÝéa&X¯Ä²ÃÄô¹cûw@«æM‘ظ1’O‹»]CÎ]GÉ l[1 6çWœ×BÓ¢(wòÏÛÑPs•)µE£ij*R=?TwlpÕ§¿ª¾x¶EPd9Fô(õlAÎÖ M&ž¡¥M7 ¼ù<ýÂ_ñø Q¼u:fí¯êÅYϖɱBà┋£y/»8:_˜÷(Væâ˜U±‹#Îê.Žº[ è¥|rqT—«2Çžš‹c&¹8>þÈà²w¸*åâX\•‹£µ(o@Hȃѫàm»ËŒE¦¡E”¼1ÁÖ@õn”•ŒU' gíÁqI!Êæ'MGÛÆ®' »RZÆ’{çQìË/ó­,«¦:;e®šYÊU“Ý5éCÙUyìªix]ã’Wø¹ÇT'Ćô¡cÐvýG˜õÍ*4W×%Jˆi…6t}™»u5—4Gªz6G×o¦!ŒèHq Gr­º·DÀ§›°~K¶† «G 6Çk),Ь*¡.×G .^ˆÃh¯üÂbrœÆªÉ“± ûÑà7w¢}´¡TÖQrë7å“_6šQš9¨%²â6-ǾâæÈP¾ Žóä¯8 +:``ï MÀSñŠC}ñ,`ŽJGkf´e–4£©$ù îÀù_ãå·V£ámÏãñŽÞ´‰Ø˜) Ï¢Ýèah;@Ì¡Hj•‚°Ö Øè ¤§Ê_! „À%ðÇÀ0°û¶­¨è'#¤ê1‰×®™Š ×a´KÃé&zþjñxÂÊè±±öä˜vÃãÜÝ#¹LƒJÈG»~^BðÖ-Òc±K¨QŠ «G@Ĺ½)2£†§aë×ë±—âyZ –DôÖË?^„÷? ÁðÜ Ä »—NÅܼdí×£4š·<£;ZZ>Æ‚iy( k‡îÍCu3ñ­ÐYŒ÷Æ›1°ks„Ð(Ò²V¡ˆ¦ŸÔ–VaµDyÑ&3Ìá&Y;«ŽÃ€Þm‘ÜÀÛ™CX={%ŠBrÐ9Ù02S®!!„€¨!?pq4ÇÓ °ûø6.íŒLÖ¾VÌü[@Ák×L=ûeý«ù~’¸w6¥Æ¶å½wÕ¬qMRP\u"ÎÝNœt¾ ÿ ÓÈÿºýx* “f,ÃWãgÓ»D&Ñ‹/÷À m£Ü|ºÍéè‘nÁö­Ñ•¦$t^§ QíïÀ“¦8|7s9&}¾~ÂШM<0fZ9W`õªM ½ñðcfL™¶s¾ü çù7†\\šdôÆ BN¤qH§|K$F!PSº‹ãJ¼Å.Ží†TáâH/P–UR¹‹ãgìâØ/ËÝÅñˆÑű)”+¶=ogÅn‹eõÐŽ¥!ÚeGaî¢ ˜½&ÝcÝ~ô¬6ä-ÿ,LjûÇ ÛÍ5³êv«ªöþ¹S(rð±ã\O§Š˜P¾n+‘N+Q«Ñv®Ä^‚Âó´eßÈJ‚rÕÌwºjÊM% $ºN¨‡â܂丕œ?KcôÿÍ8ô/—lA|ÎP<@Ÿ‹ò³ËyøW.# æÖCðȇ”KAß_᥾å£ÝcLKéŒá÷ÒÇ=¡’# b²àúṮÈ}fr+ËPi_¨2õÿ‘M­Õ~8¦OåÁ‹6Ñ<<µn{”>•’! „Àå'àã.Žä»ˆ¦}!cÙWØñíû˜ÿ†dDºù›'㯷#¯I*âi`$ ¼®™—B´xÖ.Ašòý´ÆNž‡8¤=Ry•hšm,’«ÎæAórqÞÝXOnÆö³_&Îy˜œ>ôI¨hoÜ"ÉUSÆn.å JY!PŹ—f! „€OðiG"fŽíBOá­wæcÎ[ÏcmËÈNM é‹ðóî X³ë4ì±×â®û9 óÒ5ÓmÔ»§&È‚MŸ}„йȈ¶áØšé˜vHèÛ‡\:Ù^´È¤—“æ¬ÄŒÅäFÚ¡!@ÂüÇIèWN79Ý×M‰¢ë^,\@OO“!½MŠn‘lC‚ð"ÎýÿJ„€Bà²ðeGî(=…ͧÿ_+,š=+6­ÃÂ]ìæhFh\3´8ýûäÐ{Fj¹z®™5FÓw ±ÒÂxŸáÇãEpÐé½îÅmƒ•¥‰ýïØÓ_bæÔ7ñ×É4¨Þ(}Fß…´¯âËâRè @!¹×@doú›f|ÉñTf3$^WÍ÷M Z$ â¼aKUB@!à+üÙő҇´`[Ÿ[øã S/\3+qs N‹—Çu¯Ä˜—VÝ©3´‚s*pÝt–6…$#÷®gèãn­Æ¡‹!ÊÒ°x¡‡!†w½q‹ô("‡BÀ ¨Ûj?lº4Y! „€B@Ô-"ÎëÖù”Þ! Òé¯@IDAT„€B@ø1ç~|ò¤éB@! „€u‹€øœ×­ó)½B@!P»h ¾¿‡¾µ[«Ô&ê,9¯³§V:&„€B@!àoDœûÛ“ö ! „€B@ÔYu­%00Ó¦M«³'I:&„€B@!P?Ô q>øÞÇà(*¨gLzYm¦ÐÈj—‘B@! „€¸ê„8‡ÉSxôÕà'u ! „€B@ËF nˆóˆC ! „@] ` ¦n˜êBW¤B@Ô3"ÎëÙ —î ! ê:À  L8¡®wSúWC¦ Ð–”bB vÔ q>ù¿×-©Åo Œxø¿m»4\ê|ïãÕ+ ¹…€>D Nˆsæ9tèPÂ*MñæL›2Ùgš# B@! „@UdžóªèHšß0Y‚ü¾Ò! „€B þq^εôT! „€BÀÇ ˆ8÷ñ$ÍB@! „€¨?Dœ×Ÿs-=B@! „€ðq"Î}üIó„€B@! ê:3[Ký9eÒS! „€¸GÉ9Àn½X6I¯‡L–@[¤ªv^ºìDœûÅi’F ! „@uLùè­êd—¼õŒÀˆžëY¯¥»þB@Ĺ¿œ)i§B@T‹€¬Q-\õ&ó´iÓà°Ûa ¨7]öŽZqø÷ãÖÿìñh‹!1Ih™Õ=ŽÂÈ܇kˆ8÷øšTÿЊC“_Ä?æ ÿ³ÏaH#R+ò~úÿøb:ܧïhh¹T±”B@! êÄa¿Â­éÁÔ;.àø¾ÍX±`Þ^< Ÿ¶»¯üõ´kP}…n=ðî¹{&|ü!îLñï§"F%Y'Nºït³›¾ÅH˜ÛÛÜŒ'o¿:¼tß7xéí3¸í…û‘Éÿ$! „€Bàª0¡q÷aÕ3Ü­ö_?s ßzœð!žúC,>y}$’ª¥Pí8³m9Ô‘×LªÕu7’rP;Š÷|ÿ¾¿gZ Ç“wwCÂU!mCÁžÝ(°%TÑVIB@! „ÀÕ#` j‚^½†ß¸]ùÞ]ÓÏwŽ€‰›ä8ýs?ÄÛŸÏÆª'pž¢¢š¡cÿ;ð«¢EH¾d$^Þª·ü}1>°þ>õUt »HÙ0­½ ý½*’чúšâ@éÑ…xï¿?âXbüúÁÞH ö<ùVœÞ2Sf.ÃÆù°Ò×/¼qt<Ú& Ð~ó_{ “ :ã×¼-#Þà»—^Ç¡xæÞ¬›: Ë·ÁY¾[´D"1³+Œ€v±ç°ôõçñõAîâ1¼ý,½ü’rþüD40•âĺY˜<û'l;r–ê7#¼Q::ôŽ!’ÂO“l'0çÕ—05øVüfØ9Ìún6-‚Ãޤkâ¶›{"9Dõë"ýQÙ¸)„€B@!àIÀœ€>÷Ü€·VNòéÛPÜùZ„‘ëË©y/àá?-†-ûFüâ…ÎHŽ(Åá¥_âÍoÿŠ_ž×|ϽýxyA8F½øg MŽAr¨7e{!¦ú4ž-¿ìÇ"Î/3RkÞJ|öŸIØÕ?2-B=•)»»|…×ß[{z/Œ~  °sÉ4Ìüà œ¸ëYÜÓ!íz6Çä¯7`ÉÞÑh™IÓ>9ƒõØj¬Ï’†&`åø1ÿ|kô¿qÒbPüó,›9¾yOýv®¹ëqؾü7&ìOŘ_ŽD‹qˆ0Û‘¿ò#üãó-lÝ7ËDœù ö.ŸŠi_¾#¥Ïâ—¹  7e,ì|>>™”Œ£ǨFfœ\7ïMœ€¢Sñ»áÉôÒ‚7ý‰Òï~U'd+„€B@Á)×"Ó°aÿ^äÛHœ”àØ^ EÎŒùÓ£¸>Á©¤;¶‚cÃMxcñdl,ì…ë’ÓÐ,&ˆ¬… !5šÏy1özS¶G#|àPÄùe< Ž3›ñíÿ¾À†âæóäh´ŽôæT™õO^…³ 7à™‡#ÉùÎBZzðÊ+˜9m1Žä A“¶¹HŸð)6/Ù¢VYП¼Xq|í 9ú7ÏÃÒi@£!£0 kCýÍÞVm•Ös7›a²™–Ð aôE6‡!!¹)àSý4ò>wú”DõÂ/î‚æü]¦Få.z³~˜‡]nF*}3ÌÜü’ h{û-Èu64ºû@t»ó·îBþdÄ;¼ëOõ|Ç´&É! „€B 0†!„K‹pÁNÛ€PdÝÿ2ÞºßB@$7¡ËÈ+¦Œ loËúÞйˆsó]óCË~ˆ¢BêØ‡…‹÷áÚáiõ8çö‚íØxˆé…X“Ö²—¢Ñ:;3çmÁž3ƒ“‰ëZaÇÖÅØu® r"H)[cíºÓ0§ AÛ&¡ØNÂzãÒYX‘6 šG#ˆê J슉•÷–¿Ûiä=²û5Ht s-·%Ym¢1kÁnì:mEªrSÌ@V‚á­ç€ÄFP‰â3(qлÖÞö'ÖDåM”! „€B °çá¬:ƒPéÅû1÷³0aþì=v …¥$>Ê‚q¿,²lçRÊ–¹ ;"Î/tlIñë{º¡hâßñÁüwñyÒ³¸·S œß/­&[!}±hïÌü7ðÜüŠ*wàd!}3cÃÑ2·-B7­Ãâ…Èî û‰õX—gAÆðVˆ Çwõ™Ïà›­Æ7A±hž™…œŽÝÑ-§ ÊyÓ8«²;‰"ÚpkÝž""6ŒRÎèw¡ªišèW‡¼Õž8ÿ=xßçF†²/„€B@ 8pnç2죨ØÖiÐfS´ÁäßÞ‡×Ö {Ì#xîºVhFºä<6û^Ym,ï±)e=LÕö¡ˆóËF<×ÝØ-#É ûæûÑïÈ?ñãïâ‡Fc`J°ËçZS¶4rÞãnŒ½6š^Åô& ¢ér>$µ'ÚG®Æ²%[q¶}¯_‹“A™™AåLˆÊ…'^쇣;6aÃ¦ÍØ¼q1¦lX„Ù­oÄS÷÷¬x†‡³Õò¡CÜ&˜ª¾u/Yþ¸”#! „€?PkYÐ#Gc0Óµ:-Zw@Ï^eü9Ue*ZÿB7b=6 ¯ümJsŸÂƤ@>I œØŒÅó–`õæ=8ZPB¯ÃÁÑIHkÛ}ûuC˲zTíbó&reLk‹†"7=Òc F¯ßø×vb^{i2Ž ½ŸúFúùÑÆ¾É¾¸pÓß_€b$`Øà4ò'‡£óðíšRvù ^~¼7¢•„±ça;¹ÝV.¥lUvk#Íx5©úêE¦T ¼ÿ&øû7˜õîÿôÌÈi £""’(äÛ¢Ñ45FÏ’rp‚š¢;¼/¿ÛO'#oõ „dAKÃÔ?&ž¡¥M7í3ðFzùaæ?ñϦcÖþΛVÎ""âÁ³‹ž8ýpêÛPxŠÇôÃáŠ-oÁ=¦Zýq/*GB@?"‚Vý£m ©8¬(=—#{6cÝÜÿaíü…ȽïŒÊŽ*?àâuéeýõ_ãÍ—ƒ®ôHʾ×÷ŒC(=k=¾kV-š€-ËWcð£ JˆkÀ‡ò–µ‹ë¢¶܇õËV`Ò›Ûpô¡gpKЩ,лL?Ñ»NTS¸©+—À ”4' «¬dmÄËZµAùê×á(9Œ¹¯ÿÞÜNn·}Ç­ô‰0È÷œšƒ² âh"ŠâßáÓMÜn;¬v㈢ 6¾£¥Pý²z9_ø+âü KBwÜuÏü}ü |ü~žþÕõš·¹AKdÅ?lZŽ}ôâh†ò?¡y<÷ý8 +:``ï DjWR w¹3±dñBœ=Žv·¦Ò¥ØŽ¢}ó1eáY´= ­#œ—]s(’Z¥ ì‡5(.åo'ßÐÇasŽŠ“óJL+´‰æn]Ã%Í‘ª®ÀŽaýæ3@t¤ÓÌ/Þ†êõÇ[«’O!àk‘ҡ;zº­=7Z‚Oßš€E¼‡èß<Žë×ìgÕvb1>þ˜„yH&F?z/½„oxâÚ(†î˜†·þ;ß0-ŸƒêÚM*åÛ\ݼõò7X1yúfôG£ÊšUzK–Ÿ‚%óVŒœˆ/Ö-Àî-ÐÆ0tõ΄¬ÕqõØ_©š8¶t*&žÐ¿ÀkNÜŠås`[Inâµß^õšZ`ãNhý ¦Ïyïwz½šØplÃørrºKÄþ©0îjäôm…ð†<ô¹³¿Ž¦¢YVG/Ëf£e“°*n`¯‹ÊíVöϵò’â%"ZÁáõ™S1~b"þï¦Öˆ°4FXöþ ¼û_ †õÍFãàbÙ8ß/=€˜~Ýlâ°$tDϤ˜8w9½œ‹nMõ/tP¸ GÖÎêcç0 w[$7°ÀvæVÏ^‰¢tNæ1y+B¢èîÓº ü„óIÞ&½‡µÃòáýB0<714ÿËnúÇ27/Ùcûè«r9ïެÁœ> EóÈ^˜Åé"éåº^­£Ñ¤’µ: í—]?"`AÒ-cÑ-Õm2Ýfߊ?|k‡â¥ï†âÓñÌǽðŒ!IÞ–5ºúû"Î/ùX<âO7¢C±èüàßÐÙ-Ù‚˜¬¸‡>Þ“™_ÿlŒî]éó™k…hUÑÔ^¸íQúTaÄÒ°x¡‡G âs†âúṮÈ}fr=3T_½þxš”c! „€_ˆB3^Hâø ?g7ˆs+ŠOŸÂ© ò?µ¶ü"zïGŽ’<&¯BÐâ)Æ™k/‰IÉl>J£6±©härÖu3é8³ ó¶[Ö¡'Ò47–t»&?-Yˆu';¡w‚Óűdæ/þh2ÜÒ ZtëÖH—>;N¯*9‡:½\Ç#‰²{µŽFEku¸õ@„@Ý$PþŠQ7ûé·½rœßƒ¹3wÃÔòvtj(§ËoO¤4\:LÀ„À`žoÅJë¶} °xüËX\EÏ£(Ía-Ö^z  ¥‚î*ŠS’Ök9‡ÂB§¦B‹OÀº¾Á2rWI»©'*vƒ·áäš…ØgD·îÍé]&ô´·{'Ä,™‹%«~FÏAM´!ëé=Ø_L:¿{+¸^I¢òr‘´Ú{yÜo×½ˆÖ*£'U¯£áÌ%!PïˆÚóÉSî€5vî=„-ó§bÑéFèÿ5†GŒ>Ùhi”B ž°£øìyê{ ƒY$«1ñp´>cÂÙ@Çž¿ßMÚ ÅðªˆA$ÊmE…Úªˆ!FŸ[C™ŠwOcÁ¿ŸÇ‚r‰Ñh3丣{¬aV.C&ZÔî§%GH wANL)‰ûR=1" íbæbþŠe8rýh¤ÐëKöâÓ4½½¬Gka¸õ$0Étw¡Ä¹×ë^(q~‘u4 ­•]!P¯ˆ8÷ÑÓ}nÛd¼óÕ>˜¤¡×ýwb r6÷ÑöJ³„€õ–À…Ÿ±í ½=Ù‰n³œ„ qëv¸Æm†’õØqÌÀ°$6Ç£ ÖÝÇ·ápigdêÃØàä—‹<‡Ö#Ðþ¦[ÑYÝ?wPh4Ll„È@ϼ.“¥4Ë MÒBaÞya…+¡lo5ï‚ÛÓÉ]‡|ÖµWPM•ÛÓŠ9“½YÇ£¬ÙB çåøB=.ìúÆuõ…¶H„€B rvœÝ:+h¸È´)s+Ï[IŠ¥!ÚÑésmÀì5yÈ Ñîòƒç6ä-ÿ,LjûÇ ûÃh!ñ-2Ѻ‚•£ÜÖQŒ= Vã,â‘;v4ÚÐì_n¡x'&2ëíÂÈ–YšØçû…óÅôL Úõî“5ŸJ¢çH¸×ë^=Ü*–! ˜@ùÿÂE! „€ð‚­9±w>út=ÎÓêÍÃú5sŸmË zòõî3+vû>¦ï8KžäÆ`EþæIxçëíÈ»‰ø°Kûé¶nǂ䆓Ò}:´Akz¹ÓísMoôkiFÉš{ºæóŠjŽ&äÞrrëœ)k˜g¶,ÂV§7 ·V­{qN[ÇCk×;ÁëxÌþ ßÌݳeåý«lŸoècX«£²œ/ê9¯KgSú"„€WˆÀ9X³‹y…Prò°ž?ƒã{håÎ-ÇqÁ’‚þ݉N15ÍæØ.4 î1¼õÎ|Ìyëy¬mÙÙ© ˆ0áçݰf×iØc¯Å]öC¿{Zã@¢zó|l· ýº¶4gz†Ì‘h}]&wmÅÂhßÜ+iÞóMË'⣉À Ù±(=¼s ‘–œÞ§Lx»î…צ©,·VG3b¢*”­ÿ°âðÿîÇ­ÿÙDÝ€W>ÿ=ºkÿ–Œ= †&<€›Æâö>Ç/Òy½? öSøþ‘‘xyO.^›òWt«~DœWŸ™”B@zGà<¶Ïþ´º¸3˜ˆŒÜ1èÛ¯Ò¢.õç”֌ȧÿ_+,š=+6­ÃÂ]¼ †¡qÍÐ~àôï“C‹Ö]¢2µÄšû`ÊF¯Ö ©®ÑhuXúuÈÙ‚µ‹Öâd—¾h9ê¸Ùþf,ùï- @Tj' ¼÷œûâ5ì+$¿Üë^T¼V‡ˆó²åŸ;?à•·á³ß\‹HªKéõÀg¸çî™ðñ‡¸3å’î`/¥—¥ì¥^M.K#ĈB@ß$p‘µ,*lôÅËXÀïÇy®ua‚%¶5úÜŸ "/^‡!³k— ëûì8ôuÅT¸g ͤUÇán•ˆî·?EA[ëaLa·–ÐP}Á#-É‹u/*\/ƒ W_ñZZEòÇ/ 4FÏEX<íoxàÇx¼]x¹WœkÖ-;Îl[Ž|?{5ƒÝæá’V³Æ\â-xÍ*•RB@! „€Ÿ •DwÎúï|<G/¸Úl;½Û󀈦Iˆ¸Œ# ®d¯îF›ûžFßÈã˜ð·O°g ½h á½e^j,†öÎEnn.z ‹§ÿ5»hÑ/°ÉC½0â/ëÉál?ÆßÑ—ò Âèë)ï}pLÍlJõ\Ø57SùÜa¯b“¡nGÁB<Ý7ýÿ°üÿ³wðMUíÀI“4Ý›J ´…Ê” e•!P¶"ˆƒáVDq ‚侯 A F=dSöÞ«Œî½“ÿIºÒ’Òš6ãwý”ÞÜÜ{Îs¾§&ONν©Úxrõ>}m8íRPgßxýóå8’PP ¨wÝ â¹!³q j Æ쌰®ã±#E_ƒÔȼ° ¯Š::?û5Ž”sò“s}†ÜF P€ @¾€T g›xœ=,.ñûÃߨ{ôN܈Ÿ¾Y…›¨.]ë<à‰°¶<òœ:`܄ް¹ñ >ùù<²î‹ FÚñxùÅ™Xu«.†¾7_}õ&«›+>Ás–ár®3:ýßLêb/JòÄàé ±hÉŒo/&{_Þ%Nj.œw•‡˜Ã{pKb Iâaì×ù¤™yi;NçÈѤG0ìÄØwÒ¾™xnü\¬On‚gÿo&æÎ‰ÿ‚˜µs0îåù8š&ʔȠ½ùoÊqü8s+œLÀû<£÷R¨9·Öã?ãçã˜ç`̘ý"š;Ü?ý6‹i-r…‘‘‘÷í^>I P€ ÀƒXÁ£Ó §þ «¶lů'4Ãç ¸ˆy烆Bý· }ŠxŒÙ he+¸w{¯GÀçK>ÅŠð…xªnsÄÅÔ©µ³—áš]gLŸ÷tq-Hj[´FS§»xbæ÷X°/Ÿ…ÀßEs©õ‚ÐÀOÏž¡o?]—2ѵ…æ¶8¹ã lZDÛ³?bϱŒªWKœÕ‘뻈K‹6DÆŽä\Ǫ9‘ˆ‘·Æ”/'¡·{AÍZ"Xqÿú ·ÇÜ~ h/ýŸyƒÅ—Õο̨Q×]Tñ»1ûõO°ÓúQ|ôÕx´«À‰ãf‘œ÷=êï'tU¸nñ™¸‰ P€xp©-êu‰7Ä <´€•}û¬}j6¾ýl%ºÎy¾zòsUÂl:ÈÛôBû\dë\ºÓé‘pÔÇIß~YaÁ¥B''7 éö~ÝwÙ-‚ ×ïßvV…€×»¢«JŒÚï8ƒ”µà”w‡ö݇£¹HþUñǰóª8¢y?´uÓá û¨÷•˜Î%îѯQAõÑ£½Wñõÿu"Q§À÷'auF¼ÿÍ»èêQâ»:{–\5‹ä\Ó$‰¼Ì[ª•l1Q€ (@ Ô¨€ÌwÞ{áo<ýõט±. ³xÜOnâ $ˆ­9û§``ø=Oço¸‡¬Â™+:»H]›¡[0gÿ1Ä?çË;q"£kâ‹ÆYu‘³x®dvF“ôãØy¨3¦54¹³¦ÎDQŽƒ;”¥Î¥9ùÀIl»wiª‚ä\âŒÚNz’îœkX9åMì:¯BÝçŸC=Ÿ>tâÕ]5‹ä|ÕÂ/tÛÄu Ü#0ðÅwîÙÆ  (@ Ô”€þCßèucðýœÿbcûOмԵ[$V21 °nõ>Óú¾—ÚÕAé›Üj[dU ­Âj?iæ÷‡ß¾Cˆwk‹V^J¸6mÏä-Øw3õïîÀ9u- jïMú\88¯Ö“ð‹3N‘¿]R©Ä ÒRI¼¶~ÕUìºÚ-|Îãð·âǶ 0º¡²ø8íNúÿ1‹ä\Ó´ˆˆý-äVËɹRÜ5ƒ (@ P€Æ%`„ïÀ?/,ÃìÙ;1«µî41;ÝÙn"ñ½™)¦U5n‚³LJ´DßÔf9jwh·%°óü „î¾›Ð7á'2|…o4µÿEœÜ|m.G–³¸ŠŠþMd¢NQöèdˆd\wô<7ñšvT]YË åÞ¨WÒÏAÖáÝg¿À÷“ç¡éâ7ÑʱdK4£àAù{è;ŠÛ(`"©Ù|þ4q†I P€¨¸€Mð³˜4¤ÒvÌÄ·ÇÒK(uj‚î Ŧ“aK´Îu<Ŧ¬Ká‹O¿Á?Wu¯÷’‡<;ÐZûwÆ#ö©8¾u3þ¹$EƒÎ§†ŠŒ;‚e¸¼e ¶N‚mËn(˜-ui†îb„üÔìÕ) 9¸¹s“¸X£¡Ý ÊÑ”UÖ¢pEmg9ä>ø`J8œî¬ÄÿÍØŠK;–u(“ó²d¸ (@ PÀ°;4;}]Sp`ãõ’u‰GûŒ‰ºêÓøêµ÷ñÍšØp/6¯˜… ¯|‰Õ;¯Af§™ŒbûZâ÷ lü}-6oÝ ©"±¶ D·¦Ö¸¹'rë"¬±“¸:‹X$öîT¹Ç~ê› 4 oqáÅüEV¯?Ÿœ(|6á3,Û¼‡£ö`ý’ix{Á%H<‹WÂ\óË)<澿¥p { õBÊö1måu‘æßá°âý}ø,(@ ˜¨€ÄºèíÖD[À°)`RÇVxùpìš´É%š,mè ˜ÿMm|÷ÝoX=s2~ÔŒ<Ûz!T\ÞsÆóÃÑ^{©C)|û<›¦cõª™øpSKLXÔööh éî£Py¶A‹¢«¥XÁ­ikxá¡Ý"âi6 “抟2ö€Ä ÝçíD÷ÒÏKÝÐ÷›è[z»2£ïÀ¨ÒÛõ¢(@ P€09Î97¹.cÀ (@ P€æ*`>#ç•ì!‰˜ã}æoÈ3“+y$w¯)$¯ÆHôi^SÕ³^ PÀŒl’nÂãÒHÔ*3n¥ù4Me¥À=mãb>bK(P `±É¹,+_ï<Ã?“ˆÆOz!ÓÁˤ¢f° €ñ xŸY‡YGŒ?PFX,°÷; |ñâÇ\£€™Xlr.ZÛ…S½£Í¤+Í¿Ónùêü~3ÿÖ²… @u HÔùà{Buª?x]Yöøì¼üÁ à‘0bÎ97âÎah (@ P€–%Àäܲú›­¥(@ P€0b&çFÜ9  (@ PÀ²˜œ[V³µ (@ P€F,Àä܈;‡¡Q€ (@ X–“sËêo¶– (@ PÀˆ˜œqç04 P€ (@Ë`rnYýÍÖR€ (@ ±“s#îœû…¦JOÄó?ŸÇ×ñjè®ßNØ —ÑíÏ;8Ÿ¿š‰P€ (@#`r®·3ÔHOL‚m—0ø—Sûñ:ÿz¯íŽCTfUÝ¡RÛ×ã±9¹ªÊÓÛÛ˜—‰ùŠvÿzÛ3J‘wC–]ÇÞœ’Ûùˆ ªH@û|OÌFn©"S®]Aç_ªâ5¸ì÷ ô;7!Þ÷zmO1¾E•"áC ˜“s=]šƒ—#obmŽÆt©‡%ýëcNxÆÞÅøµ·°§*tuvÅ?©*=ªjÊÙïW47³Ž¤!¥šbÑ Á (Põe½©su:ð°…Í­8ì­Š÷»ªž%RÀ¬dfݺi\^~ß‹›^>ø©»3¼ ?¾8+ÑÄÓŽ»q!MvJ ²’’0wÏ]ü“ƒ‰þµÝ0®;ÚÚH ÊLÄËÆá‘vΈ>“€ÃÉ9H³¶ÃèξxÒ%+Ö_Àœàæ³èéStÈÃÛÅ¡yK[ìJ„¤Eæùd`Aå—×´¬äd,Üwknç Sb…úuÜ0AÄÖD•ˆ—JÕ³(D{ÿ¤x¤…3bŽÜÂÒÀúxÕ£¢dÍ™÷1Ôˆ»yS÷Äã¨w«å‚QÞ%3ý²âlf]²>¢(@{îû¬ÎÑÑøïÉ\Õ|Û©°FXcoL ‘aÆ’ïA…;À^ì¢ÊHÅò[ô ÷„õ¿Wðûµ\„7”ÃêÞª¹…0€þŒË@•™B±¹i©Ø”$C÷&ŽÅ‰yAàRk{Œ÷ÅÓnRHrEâ¼){<±tD¶>î‡G³ã0yWîŠüS"þ“ª²°æ‚Ã{bÕAøÀ5ßHF¬Da=ýÑM.E‡ð†Ø(^$X‰ý7]‘âň Ì©—‹ïîSþ}-El‹¶DcŸ³'Ï-"7ÿMBœˆ«D=AúsQº:¹cbC5þ؇Kú戗c ÎJÅ—;ã‘è5#ƒñÃ#rì>—Yü5í}âŒ-™Ãß·¹|’ €E ”󜃎ç w†ØòT0VvsBòÉ›Xš »ç=H“˜kTn_‹Ç ¥3ú¹+Ñ'Póg“pCßë¿E‚³Ñ¨&祜s32Eò,G‡ûÓdÆ'`c†cš9ÀW&ÌÚCĺüN"ef–4näŠ`¹¨DŒ^7ªm uZ&âôÏdѼ.¢N°;:8Ê HN¬@ù¥‚/x¨‰m]†-^jêˆ:rMl6ÐÜ6·Å~–ØI§QU™‹Z|Ш‰7ºgÆá‹ó÷Î},Ï 3!‡òlðt°-œ¥8¹»àÙ:ÅÞ7N~•Zf¿ð PÀÔ¸uÝÄÜoÍyO…?}·§k^µKy¯ÁªÜW0såYüâa‡~Nèà€ ëâ÷-]ÑÌøD¬IµÆˆºrhÆ”`ïˆÁnwñíÙ Œn/æ ëîÌu PÀ`ÅÿϬ Ó*Xn£„‡:‡U`+¦¯” _%²v1!þ)õ„öaþè‚îS"ׯø"ö•hè=îÞòï-<ÿ@©Â ³‡ÖF‹R=¬ÎLÂ!Ýzî-àž-n~^xéÜE|u(Í‚ÅÓ…±þ.qÄýbTC]ô©çþq–(’*-~`2NØLq¤ÏÓKðÓ ùo¶úJJ;€Ÿ€RÄ“õÇá·ÅÃàà ¦ú¤jv[^<þ±ׂcPS'1Êż$°¶µF€‹nr¤¤è¼/•÷l¥Àà®A螘†×R°ãÂMŒ9f‹ }ý0øžL[…ãb K¬˜§>gåiÌÑÅMŒÃ‘GlÑ^¡»‘ë €¡øú^JÖÊκäaû‘\.5ÏN¥™C½êÞ¿’k;²³p-»¸€ìô,픘€{^ôŠ÷©Øš¶Q¾­ƒ8q5' t¦†¨rsq;K$Ç  ä^R9úµq‡ûµh,ŽU$åÅ( Ç]k݉ÏE!k•ÇY2j>*ˆ^ûÎh²t½‹‰W@\1‹‘ ¨Sãçù‹ðçñär¾Ù3ò†0¼*(ï5XŒ­‹Ñ¤xñ-¤£³="šzcF¿2ع¹ ‡M&–MÁ\5r2Ò±üp Ô>.hUÆW†%ªCêÖî&å 9GU”°î£|ˆò•..èc'b;˜ˆ‹â8O|ˆØ°çFnN@tÑÈuaMû-wvûÁR¬‹JÖžðª9ª¼•®Žh.MÇÒÓiˆÍU!în–Þ*ÎÔ gÅZc9{¹5ñâ7a…æoS_³U±Ø½b²ìî9ZßîÜVsY×÷ãbá'Ûš ƒ5‘Àý_ƒ›g®à±ÈhlNï1j5’“ÓqZ|P¯%Ω²*õtçFJì1²¾ ü¨Sðãçî„'ëHpøtñk¿0 ˜¥@©IfÙÆJ7ÊÆÝsú)°äH,–îH@¼Hh¥b~y«z^øVœXÙ@ûÕž8á2ÜY{oáée¹ÈóËý=ðEG¸Š¯õ&Bº‘XY£O}k¼uPÜèèš~ê\jìþåß7Ç–Ùà9qU™œ½wð⯷%æ¡ÔöqŇ]P[š¬E%Ö%'‡ö»|‘…ß”#”x»£+>Ø{ƒO®µÜðr¨­¸´—øÀ ©ù¾qV"4îZ¦€]pxŸ\Š]Ë÷"¡mO¸–ú8ž½+ŽªàÞ' žÿ\Äí{JR!õì:|ÿýŸâÛ¤ ¸“¦ù˓ùn t{ì9<ß?ùçNgâÈô!·AœŸðí|´?²ó–oÃéÍ‹j·|c&¼„žþÊ¢YQâ¢m,»0(®Àœy¿bëéX1òg ï½0ò•ÑÝý*F}¾XƒÏÛÙiP¥]Ħ¿ÇŠûqæ®ænZâÄ8ß&èØo$Æk¯¢¯èubÿfšîü sÿ܃ëiâ[_´ô*Þ{¾-Tû~Àì…a÷eÍȵ|Û Åø÷Æ [Éÿw+V¯Nåy©“°åµL=–ï°àIt[x=õ=~~1EÍÈšÿZ’À}_ƒ%pm苉)71?ò ¦iÿ×U ­Ø6Iœ˜/'|½]qCDNìüýÑüž?()š4t‚Ó†lJqÁSŽzçÒX’:ÛJƒ HÔby˜Z®$fã‡ÿ}Š/¾ó0ÅTû±ŠŒD¬Xú-¦zGW{ݬðÁ¦ÝòÁ#žB¦£÷ƒPÃG­Zø^Ÿ8.JÃ&.œs^kìWxæø[˜¾?o._€!Þº‰d.}÷4žý!#þ÷2ξñ!¢ê•œsž}é¼4ê;œWû ã°þhíï«´ˆú{9¶^Q¡á K0ÿéz"AÌĉÿÃË«2ÒÆ Ù­0ò‰ÎtÊFô¾e˜õÃA¤zÃ7?CHÁIÒ/;¿ã2Ï}‡Æþ€Ëð@›a£«˜?l~Ûœ…V¡±Øz0]g­ÄG­l¡J9Œy/¿ŽåWÒkz´¨ ûœœÞþVŠ…mÛ‰XòY¼µ]Q{ð#ÞÈvê‰Q›Ã5óþž3 ß!d@Òå Çs¡U­\\Ù²3ÿ8I«°|f/^ü¢âõ×Y¾W.âNìÅîuÿÌշáÚëM¼ÙµýZ ¹¿ÑÍ=Ÿ6mšÉ½hþÂö.Ä—G“ùžPï“­>ËÞŸ—›äßZa5ï S§N-|hô¿_ÃkX(þ{Qü7WüÇÅp†Ï ;K¦ÊPÃmD’l·3~ßp ýGÕ+>1T$Ÿ«Äpáÿúճƙ{>¦çàî©ë° Aø°ðAoÏ¢‘ èÝi}ˆýËWàÜãï"T$ÜíÙÏ8}+ ‹? ‚$<´q]d튧·b㕬¹ÔPåʆ:{þ"s9š¿;3ú{åÇÒ·z¿§g^eÚÂJŒB|P8÷ã'"1·C؇K1=ܽ(í;0­>‰)[ç`Þ.ø¨½ƒv$¿0ö3©ý°L|ÈðÕ¾2†"Ð* [ßÞŽÓ«/`Ô’%?¬ÚÀg·¾„•‡×áTj/tvÔtDeê•WÊË-´Ú^ø^Ôqõ[£SX+xhêæB P€æ"PêKnsiÛA ä Haßô1ôr®¯^-N.vI;þ6Æ!õ—-Þ^¼&‡oÄÌ]ô >,HÌÕ*q"Y¶¸æ½Âõ]Åž‰WqWsÙK%`P?Ô/H̵›¥Žð««É^“TxÎA%Ëμˆ­'Ä ²fx¬sñ‡Í›Ú>‡>.:d^Àêõ·E®þz5” !.q…? j„?"&¥¤ãЦ Ú«Ù艠ˆ.§”-ŠZõ!èßžè¤祸‹íy ¸›V0üë­˜WQH\¡(@3Ðy2ó–²y°TeC êï‹¿–lÀï'žÃ­ÄœlU"®Øy+ ë\KŒB_Я£JÃÅMK±hù&D]¼‹´ÂܺhïüÍŠŠñ\÷:bêKñíšÌZóR“qîsñRá²%bšÊ ÜL‡Ö „·M©9¯Êºh`…Õó‹V¥^Å…Íú|0bGþF}ÿÞ¸tMÑ… nžöE£ìÚC¬ n[îè§²‚BûX…œ¼ü¯*WoHAD•ðÒ×n£(@³`rnv]ÊQ ´€þ}†¢Á’ÿaûòײ+ãþÅŠ}Y°í< m]DvªI|ïYÄå8±ßž…ʽ †½ü"šù¹ÃÉNiÎ ¬œö þ‰/}DL-)½MßãÊ•­ÊN'6‹Eaû~,®žä¬ªÏO’ÕÙiО³ìÐ ã&ö‡¯ön*»êü’:¡Dž/'Ééì »*µÓPt7Ü»^ÙzÅØ¾X*êuo}ÜB P€æ)ÀäÜ<û•­¢@ +¯nÖr¦ïû »b; ù¶ßqTå‚þ5/• ê–vßÿ(sI#Løz{é gɰ¹¬DV§ˆ2W+Y¶Tf?W^Üù6çž¹ñâåÉšÔ=Ú‰Té”'C• ‚Út@‹‡¾ï@™­(ñDåêÕ™_T¢> (@Kx˜·WK·cû)`:RW´¢#lU'°zçl'8«‡”¹æ%_ÇMMéÝÍJ].0çöAŒ}ðæW¶l©ƒ7<4#àñW ½:£nÕY×qàRñ|‰B<Äi§u«ô­VÄŒž¬D$fêίÑ-ìÁ×kªÞ˜GR€ €1 097Æ^aL¨r [<ŽîâÄÉÓËÄ]&¯õG žÎ9Ž¥«”Ú¸æ_Ã<ù&â ¯m/vRg^Ÿ3Ã-í9ȸw(»tQ÷<®tÙ6è$ŠÉ>Œ5ãuîT(î|»é;¬Õý `]½{x‰obÅ¢íˆÑ½q*‡çŽEÿžÃ1ó¨æBæU¸¸^‰öJ4â:7éÙåßG¡ ›Å¢(@ P z8­¥z½YjNÀ&Cúz!òçSˆ—„ŠUÕ¾ïåø$ŽÍ1°­GölÂGS=1ºg}ÈãÏ`Ç_âª/MßÁ„v3ðÙÞsøãÇU°ïÚ*ÿª&l]åÊî„Î-=Ðel_|÷Öߨ5u>9m|Ô¸sb3þÚ%Açf6øçhaåÖzú} Û-®s¾cF¿…Ǻ7‚»$gw®Äʃ1‡¾‚ÁÁù7+*<êáW¦ÞÊNk‘ÂÖË[L×9‹è•s±Àº%\Ü;â±¾õ¡{aœ‡oK (@š`r^Ó=Àú)Pm Ô‹‚ú?ÏÇ6ÃÐÉ£œ/Τnè>y&bfÍÆ²m?ãË}bV·Gº ù Ÿh û‹wðÏ™oõ×,ÌÏü5—/¯èR©²ýÑN$玭'`Þœ0û»5ØþãllÕÜ¥³u_Œ›ó<~‰ÄxrÁà2¤-ðêÂïÑpéb,߸‹f¯‘‰ËJú4F¯±obôðNð­L¼l—!ëµkþÆ…_ÄìͱüÛÃðìÕƒErÎ… ÌK€wåBMæ/šw5™®ªÞ@Åíí·¿9Sy`ÄâŸñJÐ}æêTodYïj‘Ý^íæB«\Ü”w­.õr†Îª+ ÖC Pà~9¸ù9Þ|áE|´9Fgι¸zzÌn¬:.&–Û7E[&æ÷Säs (`ü;­%ÏJ¥TÍh,ÓP‹ëMs±D9<z"éL$~øeÎÖAÏ`Î"O,þn96oXŒ¨Õ¢µ[xwÅ3^ÀÓ]}ó¯…nÎl›Án…ôÅ{wHTº—÷1Xu,¸ Rjçßp¬ Êb0&‹MÎ5æZ_ûcLÂX(@²¤° êƒqŸ‹Ÿ²váv <„@|6q4¥(P5œs^5Ž,… (@ P€-Àäü¡ Y(@ P€ ªF€ÉyÕ8² P€ (@ <´“ó‡&d (@ P€¨&çUãÈR(@£ÈÄÑû",¬7>ŒÊ0Ê(@ P@W࡯Ö"/Hïí WtKçzXݽ›½»‘ѱ3òÜÜj$VZ³é&cà„È„-:Mý÷p7¯¿wIÛóú½»¾/ý‚%#ëà^òâqðÕ¸<ƒš:é­çÞš¹Åœ”2 ¬$sn¢IµMyè¬âb‘Ö«IÅÍ`)`©ô^¬‹åå ‡ÂZ‰_æ~®»™ë5(ÐiçNtÞ±z÷Æ¡V­j0V]ZÀÉú¡ÿ—+]d9ӱ뿳±£åÐÕU_z^ÎáxZz?Ï_„˜çÃ1€ÉyÄÌ{k¥¿Í›aÞ4±ÖŸ=))˜þÁ&9Ã¥€e ú9 ’æÃ0nâû˜4a,Â=obýÂwñìû‘¸•+"±C³—?Å»¼´­ríõ&>úäSLÔ=[ةǰïFa&/vɹƒûc ™”®>¿çt2guÊl» ö©Û¸K¡Šß‰OG¿‚™´åù1¼9ZL͹Œ¾yÏLþ·51h äJÍyÎ-ÿß^ôCĨ±ÛËúÎUHØ3¯²I~á‹YcÑÄž/…˜üE P€5,ðÐÓZj8~VO ÜG@-rq™ï@Lzù<=w>]Ð?¾ÓŽe梙8÷ã'X~Õa.ÅôðâIûŒ@«GbÊÖ9˜w  >jï·Ðh{á{Ám8ÔoNaš“JÕHê ù–ýØ4/yÃJì¡J<†W%h0¤3bÿ<†]W2Ñ>T©>óÒvœÊ¼ÃÚÂK–Žãß~ ±@ý± ðõ¨@äïôíßu_}söÎÁ‚¨.˜ÚÆšÓ%Vš“°ïD8æüðšÛžŒ˜©-¿ø5ÒN|·Þ[Û^ýðÉW¯¡S™Ňq (PM|Wª&hVCšÃoÈûx¡ñ‘Ÿà›#©"}.cɼ€Õëo¶ WC ââWø“ F@ø#°“Emº ®SÖ"Cãùñå]§‘¢­L”S[q¾ GCE¢Ü‚ÈÇÅ’{Ž nèæ ¹ˆaÝNÍD™P ï_¿(1×î*¯ƒžÃšŠÁ÷4ìÛpo >½¢qQb®=Bç5²®þ‰Éo.Áyçpüßœ·æ¦ùØÀ… (`<97ž¾`$0œ€¼.Ÿ<Ÿý«>þ=—¾fz’XUêU\ÐäÅØFì(;ž·‘.FåmÊøx/ui†nÀñ3;q)£;ZÚfàâö“ÈqéŽV¡pÏÍØ} ÏÖC-Õ]Dí»#Î(}]ëZC•r “DÕî Q÷žé&Øú‰4þb®^EŠª¹N x5ð€¼Œ¨sîlƧÓfãPN(^ÿê}ôôâË_TÜL P€5(Àw§ÄgÕ¨NEýá˜KNiî˜)-Îm5”Nù±ÈmÔ¦:tÐÿÓ.´÷KÎEÚëÓ®=¤(@ T›@eÞÚª-(VD H@ÙOOڈƯӗâl®u‰»‚Jìüâ!êN;…¨[ù3Âu#Qe%"1SOƬ»SÁºuÝÎh%.~~y×I\;ºWQa¡ùwµöïˆFòdÙ{çv@¦$=š8jOî”Úû£‹($î,.'—®K…Ô+g!Î…S€?*ü f‹îcm`ò‡}á¦>E﨔Òåëi7Q€ ªQ ÂomÕ«¢ & MÈ(L&.xã'|ò»˜ë­»X×Gïâ9ÜÄŠEÛ#n.T´¨’qxîXôï93¦m–Hó‡ÐsÒ³KžhjˆnÍ”PÛˆ ÿˆë$Öjƒ–µògÒIì S Áßµ›¢Ä$÷Àp´p)x9R¢OwwQþI,[s±ä‰§Y—ùë ñœ+ºô *y²hQDúWòï&/…S»ñøx¤¿¨üOL™± ±ÌÏõƒq+(@ Ôˆ“óag¥¨A‰-BÇLÁZÀ¨3ÐLn)^ÄÔ—§ßÇ0‘»¦í˜†Ñ¯ÏÀ’?"ùçRÌ|kÞXyòÐÁ¬™€¢Y¤°õòÖN…‰^9 –þˆŸþ¾TPÛ"8¼¬R÷`ÅÑØ5ë€:…—º ´mmq碕XsðëÚîENQ"dô{(>#\ýþ¼:}1þ\·k_„鯾„ï.‰<¿ï;ÛD;'?ŒÊü+ÚßxÌt¼*Cʶé˜ö×õ‚«ÆT¦îK P€0Œ“sø²T µ€Ä®)žŸ<šñéÒ‹Ô¡^]ø=>x2 ^·6bÑìÏñù¬EØí…^c?ÁÒYÃQߺø(»æ/`\x(’bù·‹°òP,òÜÅ(u“p4»ªÅä™Fòç³ksÒ[5ƒ“vÝÛ{—89SêÔ¾[ˆw†6CîþŸ0ë“ñÙÿ~Æœæúö×X<±\æÕK!®^3í=t´ÍÁ‘ÙSðÃ錒£þÅÍã(@ P Z$j±Tk¬Ìð¯½,XL™|ø¡áëc  (`¼â«01MÅ9\ÆÛIÆÙkx Å/ŠÿæŠÿ¸NàaÆž K¦(@ P€ € äŸe 7û&k¾‰×´¸|Ùì›ÊR€ À}òÄD3~I~ >Eã`rn\ýQ5Ñø‹³ù4__ΛÌŸ_5e² ˜¢€&!É¿L‹)FϘ)P5L̫Ƒ¥P š˜œWtµVóòËÀºuÀ™3âNE—À¨ÖXj\@3ZxëVþÿ^šËCr¡€… X‹3¸ß|ÓBÏfSÀô˜œ›^Ÿ•±½=°eKùûq ˜³Àõëâ~€¯/§w™s?³m ÌL€'„šY‡²9 (@ P€¦+ÀäÜtûŽ‘S€ (@ ˜™“s3ëP6‡ (@ PÀt˜œ›nß1r P€ (@3`rnfÊæP€ (@ ˜®“sÓí;FN P€ (`fLÎͬCÙ P€ (@Ó`rnº}ÇÈ)@ P€ ÌL€É¹™u(›C P€ (`ºLÎM·ï9(@ P€ €™ 097³es(@ P€ LW€É¹éö#§(@ P€03&çfÖ¡l(@ P€ €é 097ݾcä (@ P€f&ÀäÜÌ:”Í¡(@ P€0]&ç¦ÛwŒœ (@ PÀ̘œ›Y‡²9 (@ P€¦+ÀäÜtûŽ‘S€ (@ ˜™“s3ëP6‡(ÈÎÎ_ÉÍ% (@ PÀd˜œ›LW1P P R_|‘¿{PP¥ãΠ(@š`r^“ú¬›0œÀáÃùe?÷œáê`É (@*`r^Å ,Ž0©xyÓü(•Fà(@ ”/Àä¼|#îA P€ (@j`r^-̬„ (@ P€å 09/߈{P€ (@ P Z˜œW 3+¡(@ P€ @ùLÎË7â (@ P€¨&çÕÂÌJ(@ P€ (P¾“óò¸(@ P€ ªE€Éyµ0³ P€ (@ ”/Àä¼|#îA P€ (@j`r^-̬„ (@ P€å 09/߈{P€ (@ P Z˜œW 3+¡(@ P€ @ùLÎË7â (@ P€¨&çÕÂÌJ(@ P€ (P¾“óò¸(@ P€ ªE€Éyµ0³ P€ (@ ”/Àä¼|#îA P€ (@jUK-¬„f.‘«BJVž™·Ò´šç¬PB.•")OŠì´Ó ÞŒ£µ“Ka§°2ã²i N€ÉùÃùñh hf|ü%ŒL ØÛ!cÓÞH9uÔÈ¢³ìp&NþJ¿¸µì¿¶ž(K€ÉyY2ÜNJ |ñJÁÝ - t7t%,¿R«~™‚«9"$…5Â{cbˆ 6\Àœ±móYôô©ƒ¿ºj†á%YËQËVž?Zo¯ÀÈ&IømK&nç‚Éyµõ*+¢(`Ùü¦Ö²ûŸ­§€é äf`Á¦hìuòÄÒ!Øú¸ÍŽÃä]I¸«²âbðÑñôîÑ[ž ÆÊnNH>yKdÖÓÝäRtoˆá°×£ ÊÉÂÖóéy:¡‘BÏÜD P€0€GÎ €Ê")@‡PãjÔt‹ÒSŽ•ƒvcf|6f(ñj3øÊÄH¹ÌCÄúÏ›q(à ]só!fŠ;XK!—HàVË_ s‡Tì ÍHú=˽uJ1%Ü^šc¸P€ ªA€Éy5 ³ P ò^Áu0=P÷„P5RoEãµ#š²ÔHOÉBª˜ªâ«(Μ¶J¸#—³€>xÞç f®<‹_<ìÐÁÏ ýd]¼é¨|ù㋆ùÓZrrrpòâ]ÌŒ¼†ô~~äPöq¥Ëác P€ Àƒ 09P9G P@k[k¸è&ç@JŠTÌ /XŠV 7h~‹ù,bÑ>e¥Àà®A螘†×R°ãÂMŒ9f‹ }ý0ØF»[©$+åðsÍó_ðoჸèKøé\ú>¢g·”"ãC P€¨rÎ9¯rRH ^@[;²³p-»¸¶ìô,ÄJäÉ·Z¥B¼Awt¶GDSoÌè€çmÓñ»8;TﬖâbJ¬©Õjä䪡*±•(@ P€†`rnW–J X@éæ‚6™Xr47Dòœ“‘Žå‡S öqA+kàæ™+x,2›“TÈ vrr:Ngµ¤°sЭ%*ÜMÊArŽx^«¹Y9¸›žÿ”í'nãçd:ÖU@É… (`pNk181+  " N})ÜY{oáée¹È7 ô÷Àmá*’o׆¾˜˜ró#Ï`šfØ[®@[±mR¤â¬Ð>õ­ñÖÁK|Í ?‡ÛjC¼yò*ž8Y­ÎN¶ˆèäçkYO§)|š¿)@ P€`rnTI <„€•¯ Á+zŠpð«‹O?¡tvÆÄGÅOñ¦â51ç¼wÛzâ§xSñšZ¶ÀÖÖÅ[^ÒHoÅ{p (`xNk1¼1k (@ P€ @…˜œWˆ‰;Q€ (@ PÀðLÎ oÌ(@ P€ (P!&çbâN (@ P€0¼“só P€ (@ TH€Éy…˜¸(@ P€  /ÀäÜðƬ (@ P€`r^!&îD P€ (@à 097¼1k (@ P€ @…˜œWˆ‰;Q€ (@ PÀðLÎ oÌ(@ P€ (P!Y…öâN €Ù $Ön ¥ô(¦Ýò1Û6²a (@S`rn*=Å8)` LûZèýüD•Îb)@ P€¨Œ§µTF‹ûR€ (@ PÀ€LÎ ˆË¢)@ P€ (P&ç•Ñâ¾ (@ P€0 “sâ²h P€ (@ TF€Éye´¸/(@ P€  (ÀäÜ€¸,š (@ P€•à¥+£Å})`äêœL¬^<ÇÈ£dx5)0ðÅwj²zÖM P€å09/ˆOSÀ”Ô)Úp#""L)lÆZ)"׬®ŽšX(@ <„§µ<¥(`2R¾Ü›L_1P PÀ¢øjmÑÝÏÆS€ (@ ““scê ÆB P€ (`ÑLÎ-ºûÙx P€ (@c`rnL½ÁX(@ P€ ,Z€É¹Ew?O P€ (`LLΩ7 (@ P€ €E 09·èîgã)@ P€ ŒI€7!2¦Þ`,¨\ÜX5ÿÝš«€Ç1ù•Žpµ*UqæI|=é[Üî4S†úA^êéš}¨FvÌIìÚú/¼„[IYP‰€¬k# I;tïÑN|i«Ù>bí (ð 9P9G3È»¸K†ÚdÚ¢BâÑßðÅÇßaõîË@Vï7úuGsÏ,œÝùæNŸ‹®ešP›Jâg_Y©ï-™¬’Ûùˆ ,C€ÃK–ÑÏl%îPø¡±óuœø#gG°­ñVϋم~Ø‹e0†Œ°ÚÖ¶¬g"ÎEbþ×[ð÷ⵜ4õ­ Ÿ4•ßyHºtIy¦0ã¤(@*`r^Å ,ަ#àŒv7ÀÝy›°|C{LTÖE™®žV¨³sdVmÜ3Ñ)È…vžAhÙ}úµ® ¥:Û¿œŽ¿Û`Üÿ=‰ÝÄ8ë"–ýgö9”7[#uÏj¬Ù~—b5#Ü¢¯tè;½›º¢ì¥,\Ù´—UJ4ù:é&æÚp¥phÐcž”#Jg©æûÑ òâÖ|&É‹Áæcõ0Lè‡È?·ã\|.`ëƒVÏàñfiø÷·ß±ñØ-d@ÐÞxrd7Ô“ßÁºÏ>ÇÛÇ0¾G<þ^¹ çã³…+ÚöÅýA-…ˆ!7k?™ÖƒðÁ;]áVø9(÷6ÖþÖËÄö·Zàìÿ¦bùuMcncÁÄ7¿øÏmá(ÍE©ÍX½~Ž_KöaÖíûBï&߯ß4Åq¡(@“(û}ÐdšÀ@)@PåBV'ÃÚïüí˱½Ý[èå]ÖK‚˜Nrp þûó)ÈCzbXÿ`‘`&ãòÞ5ˆ\6 ÑÙñJ˜BÛÖÆ_žÂ›Ù¨¯( +ëÆ~œH¹fŸÆPïù_ýqnôÇÈA>°Í‰ÅÉ-«°qñ\äNxýŠ+*@³’sGN‹BÚ£kC{‘Òë[dðhݽ‹žªHܰ’XA¦™wg;–moŒ^cÞÇ“òÛø÷Ço±qùB$í³ƒSëÇ0aˆ3ÒO­Æ7Ë×àÇÍÁx¿<ÿ¸èXº!=G¼‰ánÀÝCaÉÚŸ0Oí‚÷‡@÷sJQh¥W¤öhþÌxä-›ƒ?®ÖÃÐW¡¾£ìŇŒ”¿âËïBÔCž …§UÎÿ‰õ‹g!晉ÕÒ©ø„Òåò1(@ ˜”€þ÷7“jƒ¥\À}CSå-¬_¾qye””s[ÖžB–S<7ºÚ„  a ôùT¬\îE P€F)À¡£ìEêPÔé'Ú: aû¯ØzK™Î«ƒ•½;ìDH©1i(9-=©qšq];¸Ûç'ˆÇ`tó­Æ´Ø/¦´(B:"ÈV‚Ü;°ç¦.ÝF㩞-Ѱnøxy¡–˜ÓR²\=í—ÕB³P' ó6FÅ—1ý%ñ»à?_®ÀÑ$*·ž+¾)-é%ÈUH‹×|×` da’®†Jû!¦ hURË™“ne_K;å';ÏuêÕC½Ò?ÂÐÕZ§³*5÷¤(@#à+ºv C¢@Hl1M”·±aÅA¤é ÆZ¹4D#1…:õô!ÜÔ½9ޏ‚ÊÑ“â,Mç`¹ ®:Ò°C ¬âOàè©8•ªDãŽõ!rs¨s³5çNBélS<=F…ë»và¦Ø®V•ÈpK1(P§[4‰P_„µçRJ%è¹H<¹ —ŸE|ŽÜʼn••Š»Tm•z˜q‡uaòp^3I_é‹z.â J©µø†@”˜ÝY.¹±'q6E·&Í0¹*ùßHˆCÑØ]zb/®dèdöêL\Ùø+Vl9‡”û±éÏu P€0zNk1ú.b€¨>‰CS €ÓËⲨVŒSç/2tíß {؉EK”Ö.HÂÅÝk°%^‰Ð§º¡vÑ«‰¸nyƒ”ý€í‘ñȲm†um´åÈÜ¢®õNÛ‰]PW–„ˇ¶`wjc4s؆£×ãøe4ªë©Mæ «/ü-um‹§Ÿ»ù ·aóü©8Ø¡õ<Ä\ötܽx Q rm…gžïÚÚ+ºT0î‡Mn2œøi lz‡¡snG­Eä-À£{7Ñ^Môލ,>Ýl>€u»„qËZ€HÌ7­<+íôIÁôu ”NbCîeìØ¾™µ=ÔÈaÁžEûðí×2ôï /ë D߆¿w_ƒKàÀyá_S€0}¢·SÓo [@ <¼€89´Íãè½ë3¬Ö-MÇOc‚•V®Ûƒ_¿Ù(F­ep¨-®í=f z5q*1Zj„ŽA2œ=ûvâòŠóÃ%vñø¨žÈ^±+¿9È]ÐæQŒy¦ðïM\X½?-ÎÆK“Ÿ-cN¹ÁñÖ ±sãVì;q;.hÆâ¥°qóG‹G¢g·¦"y-üR°rq붸Rë.m1²_.¶‰k›oº“µ¸ QP—ÑÑ×OܲH³ÈàÓs †&,Ãú5óðÉ*1¨îŠnCžAÀºX–‘<Í©¾]Eè‰ßqbÝ/8ïû(&ûçÉLx¾VmØÕKþÍÿöÁ--‡¼Žˆ°Â:´ñ P€0q‰Z,&Þ†O˜6m¾øNÇ¡JŽÁšeKQã±XDâ΢[¾ø«Uý0éÝžð4æá+"W­¬ñ¿ÓU ¿ÀÛ“>€]‰ËâXÄ_ I“x ¯a¡øïEñß\ñà /®–L P€ (@ TH€Éy…˜¸(@ P€  /ÀäÜðƬ (@ P€0æ’jw¢(PcâÙîïÍF÷ €S€ €¹ päÜÜz”í¡(@ P€0Y&ç&Ûu œ (@ PÀܘœ›[²= (@ P€&+ÀäÜd»ŽS€ (@ ˜›“ssëQ¶‡ (@ PÀd˜œ›l×1p P€ (@sà¥Í­GÙ‹XÛB.—#22Ò¢Øx P€ €© 097ÕžcÜÐ# ±¶CŸ§^Ô*=Ïr“¥ HdÖ–NÀöS€0z&çFßE •(l*w÷¦(@ PÀh8çÜhº‚P€ (@ Xº“sKÿ `û)@ P€ ŒF€É¹Ñt¡(@ P€°tÎ9·ô¿¶ß¬V-ü¬ÚÃÆT­ÀÀÞ$“©ZU–F P j˜œW­'K£@ DDDÔx Àø4—×T§'Abçb|Á1" P€(àJW(@ P€ (P³LÎkÖŸµS€ (@ P H€ÉyW(@ P€ (P³LÎkÖŸµS€ (@ P H€ÉyW(@ P€ (P³LÎkÖŸµS€ (@ P H€ÉyW(@ P€ (P³LÎkÖŸµS€ (@ P H€7!*¢à (P,‹«¦ã¿[‹7iÖ¤ÖpòôCý–èÔ¥5œt_B ±Bω“ÐÏ[÷¹übrooÀ短CvØLê¹v³Ù1'±kë¿8tòn%eA%¶[;×F@“vèÞ£=‹ê)¬£T\šr$¶ðh‚޽#ä+mÙeÿ“³3?^…h8¡ë„)ä—MÙGð P€ €áî}÷4|¬0%öì‹&NâK6u.²Ó}é$Žlù ‡·í@ؘ—08Ô þœ ‰G—cÞ÷{%j‡¶Bx'7Ø w.ÃÁàÔÞCè;î%ôôSBR䦗f›ˆ-=ö ŽîÙ‡•óÎàÖ ïà‰Fö÷‰+wö ì$I8ðï5ôñ €uQù5»’}e>^ŒÓÆ"ØX‚ªYÖN PÀb˜œ[LW³¡x;øµì€N%FÁà±ÿâÇù`çâïàüîx„{=ØKI^Ì.üðƒHÌ•Á2n4Âj['à=#q.ó¿Þ‚¿¯Eड¨_”¨ê‹ ïü/æºûVíA÷=áYVXÙ×ñïÞ8È‚‡c ü/ürd;.¬F¶ÅéÿƒhUÍ1yHºtIyUSK¡(@“(ë­Ë¤Á`)@êBéÛ O½Oçü‹þ>ƒö£CQù¼6 W6mÀe•ÍF>…Nº‰¹¶9R84è‹1OÊ¥ ‚³T]n#­\›¡“ÿï¸xIŒžg••œ«‘yi'¢RdêÐ¡Š³P?ŽgRÜÒ¡äh{nޝ[ŽÕ»N#&KÄãÛ=†ö„íêOñsrO¼;©|´¯¢¹H8µ«×ïÁñk‰È1켡}ßAèÝÄrMΟƒÍ3>Æëáx·6ü¹Çn¥C-µCíæbİNðµNÆî/§bùuMSocÁÄ7¿øÏmáøà_O”ëÆ(@ PÀx˜œO_0 ˜€ʺ]æù/"ÏÀõìP4,Õ®`3rnãÈé4À¡=º6,k Š ­û w‹„Ä rÍdsµ *µ&™×3®JÃÙíÇaÝíakÕMíãàŽ“HnÑÎE‡äàÆ†¯±hs,<Û ÆèȉŽÂæÅ‹!×LŠ·•ÌkW#åįøò»ƒPuÁçBái•„óÿFbýâYˆyf"Fµt‚DÄ&ÓÄvg–®ôEÇÁã1ØSŠØ#ỿþÀbçzx€7š?3yËæà«õ0ô•A¨ïè{&æŽ (@Ë`rnýÌVR ê¬œà/F»q'wÒT:Éy.2⧸÷å%/1y‘¨³âq3Y<¨ª:3+'o‰¤Üµ<•EYv‰¶«“O`ëÙ\ضì„íp¿Ú7wÀþwàHlktõ(8•4ë ¶íº xÀsOtvsH‚œ¾ÁÇ?Ýì 2æÜÛØµê R;ÿ@ûÆhæ²ÛöíAtøø)DÒ‘‘›ÃÙÕ¶dYrWøŠO…Éy^jœ¸¶ ¼m&mË/®ä¿jĦ ›Âä\nE©¶j?›01/ÉÆG ,T€É¹…v<›M‡ȹ‹3×Å0±Cø”8T ¯fh^â /ùµåÞ¾ƒu8MJ,±v‡¿HX/Þ9ƒ›Ùmœ?Œ­',MÖZzhÝ-Ž6…Ä|n…#jùxÂA{ö¥žbĦlq•q‘±ìÃÂiûôìt»®öÓAb.‰˜³®Í—%¥ë.uXÁÓ.ŸÅS­œKžPªÙU"׆¿÷ƒJ©Rø ( `rÎ? PàTH9½û’DnÞ±5|ÄHs¥Y-4×Hß²ó6FÅ£í.5 ,ŠÌCüîo0w¯;ŽŠfšù0ÚÅîõƒ¢ç@á÷üVgàÒöCH;ž‚Fv¥’îŒóXµt Žî¼€AµÉ¾æóBfR†ˆÂY;ÕE[fn¢¸Q’X+ ·²¯ñ01ÏuêÕ“dÊXtgþ”± 7S€ î}/¤ (@û ¨~y3–üx™Š`ôïá_vBzßrÄ\ïn}Ð@–‹‹¿/ÂÚs)Ú;ƒ’‹Ä“+±pùYÄç8À]s’äC,ªÔ³Ø~\LÃñë†n-!DœÜYâ§yWô”"ëÔvœIUAæTÞ"ÓŽ=} ÉE‰µɧvâtÁlM8RÇ@4vÒNìÅ• ¹)êL\Ùø+Vl9‡”¢ã+Ò͇ñ£ÎÓ Þs¡(@ àȹ…u8›KÊ ¤áZÔnìÒÜ!TLòøÿöî:ÊúÌãøonÉäFî$1 xA¨ŠB,U›#hk­­U»«¶õôbݶ֞íºîÙîîÙ®Õ=ÛS—õ²Öm­§)”ÖÒ¹ ¢˜ˆáN$wrÏLfÿ’0!’aÞy¿ïœœLæ}çÿ>ÏçÉIžüóŸw|mªÚoÞ¹sO•:Ý9ºñ/h^òè›fgÊUúÂ}Çõ“´î'©äâ9*ÌK7—lч ̄]uêJ™«»ï¿¡ï*(#‹¿÷hÓT¿»Ae>—òÎ6×Lï}¼ßgg‚f,,§b¯6ínÐ×LS‘¹îyé›+õÂJé¦Âu)ÑºÍ š'Uö>Õ©Ë®ÔÏmÓ3O»uË¢BeF·êèî úÃÖƒJ¾a¾¢ƒçvƒn.S™hæì}hÓÆíj›œ¡ü™¹\N±×›Ï €@„ МGxIóhSÙŸ_QYß E%MÒôŸÒ¢®Ñ´ÄóýâPBÁ2ýÝ÷/Ñæ?¯×¶Ò·´©"x¹§bRsuEñ2ÝøñKM³;P7ÝÔÐwüÕÚµ±R]Q…*šqÖ õ=Û¡Øü…*ôîQÉæU_µHßú}¦ëe½ºåWzv³K‰yóTüÅ;ÔüÒªì›Õ69̾Sß?Q«ÖnÕ궘ëט¸¦NӜ۾®% rFøŸ…(e«°ô×*}õ%•gëášó¾2qˆpGÀlž#é!0î?þ¸–}éÛã~ž¡N°jŵdÉ’¡cÿùøŽhõ¿üP¯Å|Zÿð­ëÌåÏg°Ð=wÍš5Zz×}rÄ%‡î¤œ)ø=ú­G¿¯¸³/Y3À±<„á#ðU}U+ÌíKæöcsc?󜎿À¸ æDË×¾¨?]¯cÁ ²÷lþº÷TV+ÅO™¬x‹4æ½±ó@ üÎ÷ÒáŸ!"€£pz•S«²’]ZÑÙªâySßyD;×þ^G4Y‹¯Ÿ2Âå*£ ‚ç €ØM€æÜn'_¦€Ké×= ¯VjÕkëõripú $A@@ \J1Êœ x<ߦ ÜQ>̃ €á#@s>µ Î[àæ{¿®€¿ã¼Ça€Èpx¼‘™Y!€$@sAÅ$ätÊaÞvž @¬)ÀšskÖ¨@@"P€™ó,*)ÙW ÐR¯@{‹}È|P‡YoîHHt?;@ÂC€æ<<ê@Œ‰Àêÿ{fLÆaÈXú…äˆMŒÌäÈ ˆšó)$i Ð+°dÉ’Þ»|F O û*>®¾¯¹ƒ ž¬9Ϻ € €€ hÎmXtRF@Ošóð¬ Q!€ €ØP€æÜ†E'e@@ð 9Ϻ € €€ hÎmXtRF@Ošóð¬ Q!€ €ØP€æÜ†E'e@@ðàMˆÂ³.D…€…|:¼êŸõë]ºñ‘Gµ8ë\?f|ªÛ³N«ÿ¸U»6È'‡â2.ÑÜO,ÕÍs')zÓþëõÄVé¨uýïå9 Û: €€Ý†ñ«ÏîDäã#ÐÉÝ¿ÐÿóªÞnÍÕÂ¥ŸÖíËoÐtg…6¾ô”þ÷¯uúý,}ªÚþºiÌcçhÐ_·Tûø;âQ;*¥Ç¾ûœö…K@#΀' €\šó ¡Î9@@òUiËêjŠ›«û¿y¯–.ºN×^¿Xwõot…·CûÖ—¨Æ?TÇ!my³Fî‚eZV­æ·6êý–ÀO Ån¿ö¿¯†¡âE(œ@ÀRçú³¥!X°š@´r®¿EKc.U~ìéyGì$å§K%õÕj2Ímºk°¼jÛ¿Y»Nº•?– £Êäݽ[›ö5©`N‚úFôÕi÷«¿Ôê×÷êD»S ÙstçnTìêÕÏoÔw]¬IÝ? {—ؼa–ØÔŸZb“9S×ܼ\Ÿœ.ÃÄá?¡uÿþý.ú³úÎ-ÍZûÊ:½s¬Egœ&_^¬;?s²£µõGé—‡‚q×?ò”s§þé¡«4¡/¨Árâq@» МÛý;€ü¸Pîdͼöšyöù;ët¤Þ<˜”¥Äsý„êjVÙÆwÔ=S×]¯X×µº4®D;6½«Æ+®VR°™V§¯}ZÏ­«VÆÇnÕ¯HWçÑ]Z÷üóò×Ìĺuª÷7KlJ_֞ݡ®ü"Ýv_¡2\ *ß²F|þI¸ûÝ;'Q‡Kîàª6èÅßfëÚ[¿¡[3œª~k¥ž]ù=Ÿ”§ï-ÍÒåwCþ_ü—~s OŸzp¹.šªxó`AØ@†à×Å@ìFP têØë+µõdŒ.¿ér%ã'T ±TëË|Š}¦ÅšN<:G×\ž ®ÊMz«ºg=I{¥6¼þ¡”µD÷ÝQ¤ËfÌ4/6ý¼¾¼,EÇ›M^ŽžøŽëõU;t2ý&=øÀ­š_˜¯i3æªøÞ/©8½Eo¯1ëÚ}§œÁ¦¿½S³?w‡LÏPRbº.ž_¬«¥Ú½ª÷»›ž¥ôàœ±JÏž¢Éé±§gòCÉɹ@,'pŽ_}–Ë…€@ÀÊßúSýxõA¥-¸G·Ïê·4å#yùU½k“*»tÙü©òvïÒ”ùó”l^ºeLJfYŠYÖ^·_Z¥”™—(¹oyŒC‰³hzÔéA»Ê´û„”ù|=JÒŒÂdÓuïÑþÆ~/OM˜®Yéý® ãŠWJ¼¯½Qíá°äýtjÜC°˜À¹þil±T,+8©²5Ïè™u‡”^ôe=¸¼À\}åÙ˜“nßrTJ¸J—&w¨©©ãÔÁñ³tYòkÚ°í ýÄmÊl­“éÍ•”Û³|¥gLOвÍLwiÏ—þ¦5™ûžÔ£z<ãS@ÕÁðI=zâuÖÔFw¸4æg¨ñ €ÀÈhÎGnÆ3@`,|µÚñóÿÔÏJ:T°ì›º·(GÞs5ææÜ‡·È\¤ÅlÛ´âñmD³S¯X¬O»êî—C س;ùÚ{t×ܤ.Aq¸•˜Ñ7õ>Àùx@± 9GFAјó=¯üØ4æ]y×·tçÜT ùC)Ъýwê¤Ò´à®Û4óì)öÖr­zñ5½½¹BKŠ't/yikh•ßL{÷í«×±pÏL¸+~¢Ì—õþ$MÉËS¿/gfÕoeË™;ø @±èû]56Ã1  0\.Õnÿ¹^ØÚ¬™w>bó”ÓÍó9†èj*ÓÆÝmæò„·èãsf*å¬å%ꚬæ­ô³=U±x¡²L§]ºw¿?™Õsl@{6koÏJ˜à©œ.Ö¬4éO¥oª²uª¦ÇôL¥ÚTù—ß꯮9*¾~zwŽÐúí >ß|ü °Ô¥Ÿ w@† 9Jˆý 0J&UnÛ¨ Ig/q)±àcº,é€þ¸zŸ:bg*»}Þ|ý¬Ó8'hÚœBeyûwߦ±~wƒÊ|.å/œ=ðÕ\œ š±°@žŠ½ÚTq«n6×#€ *@s>( ;@àüÚU¾áw*`ìÛgköŒc:¼œ¡öèO¯ìà¨L-ÍŸešó~»üÕÚµ±R]Q…*š1ØÕ\ŠÍ_¨B7JúÚWô™®—õê–_éÙÍæƒ¼y*þâj~é UöÍj;”0ûN=|ÿD­Z»U«_ØÒ}µoê4͹íëZ² gð¥.ýÂ;}7JÙEÅ*,ýµJ_}IåÙÅz¸€æü´÷@ 9L†Ç@`”ne/ûG=µl¨§é;O uЙû]éZôÈSZtæ£ùÊS {þí)ÝÓ³gâçÖüÏõ;ÌwD«ƒËZbbä雨w›K)~R÷šA7gŠ|û)-8û€wO¼V÷=~íÙGò5 €çèÿÿâsÈN@Àræ]DË×¾¨?]¯c§£÷×½§²Z)~ÊdÅ÷,/?½—{ €\8fÎ/œ=gFñpz•S«²’]ZÑÙªâySßyD;×þ^G4Y‹¯Ÿ2Âå*ã0ã#€Ø]€æÜîßä@D ¸”~ÝúZ`¥V½¶^/—§Ï£”lÖ/ÿìreò#0¢ËOr €€øÍdÁ¢2Œ@À«¼¢Ïë!óÁ† €@¸ °æ<Ü+D| € €¶ 9·M©I@@ ÜhÎýBć € `šsÛ”šD@@Â]€æ<Ü+D| € €¶àj-¶)5‰ÚAÀãñhÍš5vH•G#àà—FÃÆs@P М‡R›s!0Î7ßý ­'Çù, oIÓ˜;b“,:A#€v 9·SµÉ5òÜQr$¤F~ždˆ €@„ °æ±T“¢ƒïlʆ €ÀØ Ðœ­'£!€À |qªªÌÑÁãIjl .( È{Ri“颜:ÅtÿÿÏ­ªWkwý©“wT\©×*$ïÔÍ¿¸µ/"‡YÒ|,Wï}¡š–àX~ŤTiÚ%•ÊŒ3óáh8”dfÆ[•wQM_cÀ]£‹r[TU‘¨CF+kJÛè—ÍdC@`šóPxÂC Й¨ò³u°Ù§ ™G5=¥Uî®h5~˜¥Ã³t¢®BW]vÜ4è~%åïÑŒ£Ó´÷H´¢²ÞWÁÄvÓÄ÷o ê<^ µRZÖ!M÷8ÔZ›©C5“TºKŠ™¿_‰XU7™Ü£ê•â={f< orƒ¢«¦qò™æ|ô3ôááK €á'@s~5!"èpêdåôîÆ<}ö.]šÑÑ7S=iòq¥”^©wªòT^[­KÓ|ŠN¬SêÉNóÌhyâê•–ÞªS/ªé}iKõ5mºìêr¥Gõ4ÞSªåÝ~¥Ê›ÒTÕ\©wŒÚƒ Ê£[Ýû´~ÕpFµ(Ê|ÝÞ£sœg€cúÎ]@F,À¯–“ñ‰€?^GŽEKîe%HíQ¦)îùèp(>£^n³L¥¶*Î,NÞ—sH©½yð)ÎMHð™;nµtšÅå]îSc¹ürô¬5ï?²Ãå;Õðû=¼(´? ÷@ÆL€™ó1£d K€/FMÁSõÎÖÔÁ‡nñÊhk€fúÌ'™e)ñ=³é½{rº‚³èCÐý¤áÕ;>Ÿ@™Íùȼ8¢£½Zµâ‡îãÁzðÌǺ\§f±=5š>£J±ƒýŸÏÓ,³||[`ÀÙðþOt8}§®aîw«ëTÏÞ·fƼ{–ÞÕ9¬k×;¬šcú`ßý!£†þKªÿáÜGl%@sn«r“ìx |÷»Œ×Ð9ncã!=ùä“çÌ-Ø(wÿ€ t)!µVÉ!xç‡YSî54µÇv¯)9ëœ]íqj3Q;cÌÚóÁþXè—Unn®ÒÓgiñâÇú=Ê]@Ư—ÁŸÌ@`ÜÜ-šà5£ûT×úÑUÁYìŽá.6n®Mœ`îHRõGÎéTKM¢‚/90±iX3çÃ=-Ç!€ Ð+ðÑßx½{øŒ\HÓ(gf´›¼:¸?Um=Xé)àV]ùåÚ´~®öÕŸžÞî}g—”?ÚíJË©1·W‡*ÒÔÚï­@»ZÓõþAóׂ»Z¹é§¯s!‰87 €@ä °¬%òjJFDˆ€YÎ2õ=åT›ëœx‰¶ïLTvÆIs¡Ä(†¸³U“ wš3nQ·ÓIDATM,ئ Î|ìÔW]pŸ³]©fÖ>uê@Ïá1@ÆO`” 3Ç/ FF@@À®4çv­•i­~­yš²óFG'†ì\œ@Àú4çÖ¯! `I'VÁPmr™¹sÉ£y•ªÓr@F$šÿíŽ($F@@Àž4çö¬;Y#€ €„¡Íy…@@ì)@snϺ“5 € €@ М‡aQ @@Àž4çö¬;Y#€ €„¡Íy…@@ì)@snϺ“5 € €@ М‡aQ @@Àž4çö¬;Y#€ €„¡Íy…@@ì)@snϺ“5 € €@ М‡aQ @@Àžn{¦MÖ `7¯¼ ˜Ûbs‹576@á Ó1ùÍ-ÎÜØÆWÀ0Ûøž‚Ñ@ /ð¼ž×ßš €ÀèJT¢ËÍmühÎÇÏ–‘@@‘kÎGÄÅÁ € €ŒŸÍùøÙ22 € €#øYi¿»è°IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-flowns1.png0000664000175000017500000022023113553660046025735 0ustar zuulzuul00000000000000‰PNG  IHDRàDα÷ðsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì]|TÅÖŸ¹»›JïM@TžŠŠ]T°`ï>{WŠ"" $]IBÓgCú³<Û§è{ö‚"6QDAP¤‡.-={ïïnrÃf³›²» »É™ü6÷Þ©gþgfÎÌ™&F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€hTÈF•[ÎlT#ð€çÉVùÞ‚Þ.—¶cª'mUTËÄÕæi`ŠIO÷z&u*ñzs ±aZ¶{SLfâ  ì¶ñx¾tn?tu ­dŠgôæšä65#û¥Ì¦³²2¦×Ĭø±±ð¥W a&ˆ„½O{Øëkêû}žI]Šõ’ÁRÄÿgfÖëkO¡^tºêƒÝ| án©MØhõs‡ÐŠÎê}èö뮻ΈVºýé µNDO}ùatlµiVjª×?¯¾ß÷ßV/‘MÛ‹[<ž;‹|Ýû{±®ÿSõ´.57°Ènìx„’ÿ/€wˆºë^}µ!ÅŸèÈê@ºïÙgã‹·ï‚É•’ýî¬ôô­Õ…‰w zóEžäÎ,BæhRüWS®OÏMxÕÚ”ÞAJ‰t!½mxp­#h`a®#Ÿ/]eó­RÈ/5§œ<Ó¾÷>þ~ªûž9.o÷êü56÷@˜?ð䓉ù» Gaºc4.ϦdLü~Væ˜ß¢›†V'¤» ©i£ëæ PKŸìñ ÅD›â|óbÔ¯c…Ë_ç‰qQ€ÃdF0’TtaFÁg³OºRʈùGŠgfRu„Í™3ÇjþC çñ¼˜@éVG›¿;…ó·;˜ßOYˆŽP&„Á»B‰8aê—TGOMxr0ó*O)ß4?K¿ê0ä^\*„SòUàþ „ðñ9ú÷Up ñ£F¸KuyˆÑG$Xh ’R­1míñ̉ ‡;žX~†TØc9Ã5¡êÀá¯ÙÌL÷QPbpRj,{%—ÎÊvJIÏú'ÔÔ÷¤dd,”lŽù¼©äÇš+>c†ç¡vzŽð<Õ¢@Ïû¯Û0’èOvþfpÆø3Le<‘Ò§3³2F{ª;ó>$>Lm~ +©±¼R}€ô½‰IòâgÒÓÃV™ÏòŒÚ…| c‘”èÚÕdØ´–â¢--ƒ‡dLè«+ãyKW]Œ…S-ÇÀå?X¬sFtiÈÇTäcŠÖ~Þçy¶Y±ž;ñ(Ú¾¿'Ôq; òÿ 󠙺yh(Ñ!Û¿ýLñLì*t= i›ãÍé¸e©Ð¹³6ïÆö—älrªï‚²Ïø#…n`®Ož”ãÝ|èÌß4—cÔ ÏØõv¸ƒùDfçƒÏW"×ÇÚtØ8jNíÚsz÷ü8“¸FywöE¾×ÏÎrfû¥N`ê#Ùà įÇï˜-úæ¦Àu €üÉ¥ÄXߪ(§!½é¼yzæØŸí8|ŸC<ÙǺ9˜m˜•å¾€Ü‚Õ r •§ÖãQÚoö(”«sô–Æ4®/¢®L%?¾¦6¸ø†óGüqR:ï6…þ#¦KE9yg–'m¥¿¿ê¾kZ¾»3Ï1„ÅéõšŸSk˜ÂÚ —3îf]/þ ˜ÿ Ì-?¾iÃï@|?‹ªa´hæ:uòèѹ¾î6ÏPŽ~›•ÅQ¥¦6eà ŒeL8I(hӔ̗.ç?gyÆl¤øSÝ“{š¢d4êí%9ÞUí¡ à÷n¬…X‚òÿNgGú Gš6- ýÉ#àF!r„¿“e/Õ…hÄÓðþ&„AkTäÑx¼áëÀwŠé-^èß«óÆ:(>„éá§ý á›lùQ²“m×®ù,¼—@  Iu¿Ö¶§'U¬)^B˜c¤&gFBøR¼Ã<ã{!Î$4Þ›ýã¤ü#á¾CÝ“Ž2Lãs4ö ‹‡äBÓ¬ÆËT¢%åóÀm(>_3Ø3¡{‘wÿOÈO*âGßæ5ü¾†G(¯ø~Ïóõo¿[óeº÷7Ä{ *êzZ4Øò¤ßÄJKÉ6JhŸ’­…v8`v5„/Ò“4¥ðæ¼g¢’¯Ÿ®1½Æ¯hûÛ~æ´[aäm¿M‡£iŠnX°õhžÌZbÔöòýµí¦ RÜÙs•©ÐÙQG‚ß„k!xu3ôªKSÉ.YK¡ý@˜éʼݎÃÿ©êvò¬¨#jâ½…µmQö •§üáI“šæx³?FÞ&AµCþßíï .uBZÏAè¼CåÜ7Éšââ&Ð;Zø¤™cA9šŒôã…n>O@~ƒÙÕ¦|™RöF¾¶ {¬ø¤XÙ³Œ~š+§yÞ†wò{ú÷¤Êm’7Z<ª÷þ<ý"št¯Hî0¬µÝj[6ìpáb<Ø“}4„ï§`Ü¡NMe ßÒ¶¥d!Êï](O{P–_¿ßC¾ äû< u®oL—ðæ°]êjúT¢ îXM8.™•ö±Œ“=¹%‹PŽÚêÍ û'm·PŸ´ø…ù&ŒFHhÍ꙼Àª¨ˆ#žûÑÀö§<33u؆¤xuc*FžŽûEˆþ0]”|Š†ë³æÍãï~⡇òù dgêúØ÷‚ y;¡]³[¬Å=° †o«ž•…Š?6P8 þ…Æ¥—GHekûºóþ¢û¶Á­EœS>7Õ“¾ÊvKñ<ÑFé…/â[sºD¿é7i ,CtxÞ2”œ™2sæÑÕmE±ÃÕÝÓìW·ùK¥4L3vWÍ¡]8c\ú\w™¯»!@Φ‹&®›fcÁö3(#ûaª„¡^Á~ÜßòŒÜ-œŽÿHÝô œÞuïHÿ¹ORíc´}#"X­^²ã ö •§ß¾Üâ÷(Kÿ‡òp§]J5Fù€Æ+SÝÙ·ÁÏÈS .üú*‰¬ãÛ6‡]W#­Ó·Ù÷ÂêÙ@Þýíj[¾feºŸCÏ¡óþž'J7|fvÅmzJŸÁm°)½ð,ß…@jù-Þï.C]_^m*“:”ÐP0¨³ç¢JIqX¦ÖeÃh?CÀx¨gÂẮŽ(šàwÙôÌ´oíèJt|UkPù(Féãl{zÒÈØá4ƒÆejÕãk\ÐT‘[)§ù _òi©„¤ü/½›Rœ@ÏHÚž&=ŒŠ×Z÷–ü›â$µŒ h˜·bÎöžZ§£dw¨}ûü–£aØåÕÍEˆkæLèüý/`¼J´‚ývýk#|ËzõבpOp6¿Ûnl) êõvtºÝ¨˜k¥ IpÙKázÃ×âï¡R;Ðq8××Mè…i$´ê-_áK~··-%T/mãÎë*„«Çv˜Â:¯ÛÎx䥢ÛñšÔî$|I3`*5œ0u&;o¡9|ßг3Ó߀†²ÓœdF0¨ÄB9Õv›¾ðB_ÿô>ùêóà¿#FÐß@ü—¿»ïw8ýP¡QÞä§x†ï÷IBQ¾éoŽM 7Gþ ;:Ûnªì.JUnJ¶ôs»”¾R{ÞÏÞú„*ö{z1¤:&{¤íVP?ˆ¹ÙQºð|ñóe®‡êøä­À\gk7|Ó†ÛâYiúÚÙïº&Ï„„ЦoM;vmïût8Óè̺ضGZ/Ò»!Œ[m;ûi˜ªÔNIËmè*O).Ýðžƒò@S_N÷Œ-W›ÚétvŒùn%øîA«Åm{ûY.¶Ÿš>gg»¿GÃU%cÂ솋xù2“œóÁK}’V¹/#Ä:hèØ£½ÄhšËœ]ýËœ…ظó¢<þÊîÈ„Z6ÊãÄKM1Fº&x†x桲¢)yǬ¬´w}ã*{·:˜nš>Ä3á°îΊ\6µD .ÁÜ(ˆ©Çý-D1µvh#k°ÈêÎÂBµ 9¦@(iòIlù!UO(æ/¨€Žô Hj®íâ»u]¥£ þôçËV] !|^ Óšâ4geU©odÞ!]»Ce¹:€³e¡¾ OgôEcË•p¬0 Æ÷5£Äeô$S¦F펠¦¡Œ¡è´ -uñýoö /4h‡ûÚzOõŒïmšæQþnq ÚWSÓÒÀóšÕy{‚|b„c›½8øä™ ŸávÓâ°JœŠ5¦%Êh—AGªíŘõ[D¶J¸?;º:¿“£oÞ »ËižÐ9züñä}{‹¯¤u³æñoW"ÆÏ"TžZѨ²õØ þüŸ_Ôb‹žE¼NRÅî/$^-÷õS.¾þjúÞ¬E|Úþ½Å—ƒGçañãÝèôì¸Q|‘._6ćw¦±ÄéCOCÔw,°sg_ i¸ûüc{-úbÙšJ÷bZê‚UÿQcΡ8PWæÒ“L¨e£4téÿšbŒŽ{Iž^ðÚ½#P³fd¹_óÇ~—]Û>£6îìž^aèúŸàûûš¦ÍšþXÚgµíÐÛqÆúa6µE ¥7mgmÃTçå8¸¡Qð€FA¡¬ =)ANî»ö.4HªÚÎÎ~W!þŸh~yÞo«ï S#µ4/w¶‚H±=XP¤ÐMiòU y:Ž4t-„r¿AîìgDZpù¡ƒó´Oíx¿ý}]GTòR’²@¿ÃÑÃßõ¶a‡ ö4 óF4xoûÿ¼ù•…r°8Ѐnv`žŽ~šæ:.ÙÕ¤%:A-ÑÐ&|).Œ(‚byÚ.VzAp#7Ò, ; Â¥ÔG**ùÌ™ç½ÖŠÿr÷—\<“á6§&Ó añT¨neéöŸ à^D—žÂ¡•«|C-Vú¨PO¤b+GÊ*´h,­Jå;Í·¢ä~4HŨxñ…&„¶DØ †£9l#Pýðü·¡å_í¢x r X9É ’ªF47µômšËLMÏÂÞõ§{K,µ*¨ÑyS$Ê<éRp6sý-þö"U€­Q­ìxB}"á¹H©Âü*Å¥%Šu5LÍŸ‘•1¿¦þmU Pþ$´\Û¿ÿ³lÕšz/­Å¡;lw‡K¼hxÅ`„¾vÖh§ ÝRæþ¢í¯êgè<…ÐØY¦ìð€G–f ê´*ºV…KEŸ5ÿ":v/³»Š½^*cW‚F¨´*šH—/ߨ‘6j=˜’¦Qí3í¢SdM¥ü—ž¤bÆÚOÁök‡>2ñ¨çÏÛ**ÂßmTá” J‡LM1F“Õ“ZÿÄ|Î¥¨“wä{ó_@ð«­HüþQ‡V¯Ó¯t«’q?0ÆbAó5` ÊÖ4Ã#àz൫$Ñe%KãÚÒÑL´aÝPÞ×QP“”Ô°IR¼npFö¼GÀ #,ƒí2ÈßzŠJ“*h>‘'k”Œžw¥N6ˆÐ"[‰FjBÞ`&e왼Áÿ¼n:àêçMÀ« - 7 h`žð ÿ_U#×pÓ¬QxM,¶üIÙ=˜ÿÔñã±Ø |Tr£Çs]‰ío†Çý#éß1ˆ:‹F{÷ggÓš€ó WS~mU=Ãá)VX¯²â–ÕŸÏ^ ‘vKr%?\¶¢óv­"G9ª´Ê?ÒåË7ç÷éµéïCÄš ¬äe…¹®vM,u3ùu(ù.=±ð|ß3PoâðùÙ•›0ÊFy5|‘šx|ö8÷¥«í0*SÀî*¬sQ]ðÙ™cÂ~ö[0">åNG9h VuñIJ; àzà^éÁ2¥UŠ'û„ÀIªíæÃþ¤(N@—tö쬴÷㜮»iNË6§Ð²ÿ`áB·7PXL4ÿz~!MaÅ…†å*šCós-ýÄáìiejé>W1Ûžš™í~“F“U @¹â”@q6»x­éÏàІº–æoæ©@ÝnÙKõ½¿;xñ"KI·ÚšPEc£äKþþ‚~‡ÁSl‡Z„F£!uͰñã±5%:ŒUW¥VºfÀTÐ2i ) ±|Iaݨ¤I½C xIÍ ¾Ì·ÚŠGÇ_æö†þÈ^\Ea\É8LEzQAÏE§´¿&Êçé;ܲaÅYÃè'X»YžÔ—æ¤àì䔌ñXV½™•™¶&ìGiFÏêC4,€ë—XIÊZ4ä›ì G²®Ao7 Æ~_ìï“£ÐЮÁ¾Û(ÜsžÑ[°ºwð¼^ã5Z@å_¨ïC&Lh‰(O#Þ³Q‰öã&«R„Ÿo8k¥©ß"î®_,]5Ö×Þ»Ç_ŠÆæ"{úÆRœCJíåñ8/ù˜¡ž©MèX¼ªŽ=t8ih h®sÔ ÷øËÅK[5(®@n±`g­&—’éhDOú—:­ òùAà@ ä#þyJJÂ¥y`4zÀ«I B­þÁ¾ÃáéLOÚïHóy´¸-J Œ—¨ìù§CÛkèðûºþ¶VðJÚg«Ú¢ssO ôÂ(_ë(>,ÄÈ6°Ñ´²Ñ¬¡¬úŽ-=–úÙö]ÚI_ƒïXÐ$΢’¬%a»Ó3ܲáWmÞ§fŽY-Nðà eÌ¡ÕÑvxª»þe”܈ÏÐzY é¤tã1i¸c.%»cnsI0Z±Ð'›Ô(ÁÜöwHjÝÇX;+{`Üò#LM±€áD,“Áqޏ•H¨Á¾éÐÈÓ˨`&“¸ÕwaÌŒÌô9ÈÏÜ7m1¾{ áÒ}ÃVõÞóHØ~ЛvB0v2òŒž¨Ð‰ø.Äè÷Î@ÛCì0¡ã=¼ aü TÙyH°üW­‚KÏë÷ÒΑ8†ži#°b¸3°IÙ¢/8 ª¿ÏQÆvB˜«+Ü׊ӕ€óCÐüåŸ-ZØ\>†ýE(€_~VµVÁ?ì[ ‘§S\’6Ö[`œŒ²w©žgü#6iËŒêZ€œnØ^3 D*Ù]_9庵q8îévçRç&PJ!—/ k lõRrê`7äu>FÙí|˜/ËÔÍê|ª‹M›»ÊËõZ~¾±ÿW-,Õ²p¥·pÊFŘj÷…5/ ë|Üš§çÿÏËi>Y™úã9æwÇÁícоV¹h÷Ž›vÐÅž(³oDºÍ©åõï»ÑŒ€Q‘âÁäãƒý ƒ.d‰[H8Ðê?2Újs=ý «ghTÒ ,¯íìêüÜ ‹4NÿFAí„‚:Þÿ0 ò…s‡at³½ô1ƒ2ÆŸ} duo´ÒU`ÁDéô\ˆ4: C…Z$ÉàqTu@y"*ã¤752 ‡÷£iã~gW/,ÀÀ>H?³U_€†Fu>:òû~Ó´~‰†šóœFí#ÿ‘.˜®IçÉ”ü‚20 –è䨻°Ø­;ì_ÄÎÏ «ŒýȈÊOZÔÒÉÙåf!µ)èËUýÃÀêqðö¼«wÏ&µ}pâ/­\ðî/°K¨<¥Ø¬-\‡´; åÊžr|3xù(NDÃÂ*ÕJhzê®S8K–-çŽ#^«œÇ ¥|Í—þx•f­¾Ç:äN®ñþ…²½Ö²AÄ·ãmûŠwƽg¿C€WP?Ûöá— ;¦Ú?±mò°’Ú¬r~ˆbŸÿ‡G.Ú›;QÞ²ÁçgévÍQïE9¦÷FeÐcSßРÞBÙ¥Ymÿ¡êõMËÁJÔÇúîÜ#âEÒÆ@½w›.ŒÐR©‘Bež•ìJk§h;–=é€ ó'0‚›E¥[2üüмó׿¯éŠŸ¤ 玳zº=Ðg¿`1õI‹õ¶êë»I§7¾ƒè¹ÊwÑU}d¤¦< F‹¥†.‡™Ê±¿ySµ-–êFmËaeî,8ÜtŽ„f‰«iaW0\"a°Ë†oH³§—ˆöš!¥Ã‘3õч¶ÓÙ×OcygÜX8£ù„þ£¡\®îS<£7Êí;Ì÷æÑáî«1z¨÷9Ã@4±#À0Õ!ÐhTÐÕÁîчÔS4EL«@÷¾Du‘žw\)õ2èôåŽ)bÆŽ àÆ^¢8ÿ¤–‚Šæ¨ é¸Âqt€¿/¹¤V£›p«Ñ¨¨½˜³›íëÎïŒ#ÀD3¬‚Žfî0m"%;»£(4¿†¶öBͼ 6Öc.·5V’vÅ‚£¦°Û†E\÷Y·1fŒ#ÀÄ,€c„Q™LÚ;¸U_x%„.nÐQX.pè„Ú†2.1תCZpðïômÌâ¼3Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ@ Àû€ëÔƒ%®ú»Œ>+Û}ÒÁ¦¥ªôeL8IšÆc8…½'èÝ$4çý³2Çü6Ä“}¬®›Ù8hãH†•£¤ó¡8‡Ú‡«ËÌiž±kªŠ3n¸&nnzuf¦7Õ° «Jõpì‰Ãö=ž¶è›;ãÖœç΢PéŠT¸Hå/Úò)|(ž”Œñ§GÜ_³<£vE2Þ†Wƒ¾xpÆø3 ÓX@WØÍÊtãÞÌÊ7n MGã2„UßP˜êñ(®ý %l]…A>ÛáÀŠãkjz]‘øFua4é¸'NѲa™Ïm”·è \9÷»&µLœtÕÞéhºî>ϳ͊½ûçãhÉ ¸¾ W–ÉÖMñåy >Â:=3œ„qéçÀ†îQ­d4)†ÏÌʘ‚òÐŽ.8¯ä9Š,꣜W—Ý@õ ”rHé ñdª{ÕB§æ8kzfÚ·©îÉ=MQüdŽ7ç8Kaõöw”‡Ÿ[4uÝ_·&E26–Ñ/›–ºxÒ:[¼ßáîÂiˆd]¤ëq6h\Î%ÎHÍÈŠï©åvxIugÞ£gŸ†¨î‰@t5Š8—ë»"Ó{­MîGAw— M­²íš3"H½ð2Œ|› é4£^;þ”ô¬Ò1“šæ¸lFfÚBÛwýN•î ¶Ýkõ”â¡É ÿ0¸K÷W»˜ú®£r^‘®†.oC§yÝ´qc4mâhº'·„î»uA v8Å·Ê]Maž H§ú¾‘Îáùð¤I=_Õñ5\wg€>(#k¤&\ß…WC ß8°sp«Î„¡éYïOËvoŠ3!@.G\;#ßÁŒ§ì¶¡Í6 ©dïW†)œNõ%]ºnÛGê©„ÖU¢kÜÉ1z…cD+%Ù›qm›Tô³³ÒÞ?à)ì·]³Ç¹ÊEïaS^UuTΫJ’Ü"Yè‚ýÏë¡…˜B+\¶q¦PêPMÓnŸ™™þr-T甽×ù#’ù³‰Ý—kô|Ù´Ôåsv¦›F¿l‚ Ð(°ËéL÷zÐ_N—Á¢‚5æσ¶+JëãQw¢-øsOi4÷d]P fc„x1н¸©çd+°’™ðwüwý´WùFˆ‘Ý<ôêãшœE ‹í†tÞBÛ>;+}8Ù¥xf&)cg&”Ú¡žíŽyÐUð?§£3íq_U7â»î;9Ý·æÙ!ÆÑ¶o×¼]ñŽ\;úò'hìNÈë°øhv¶{\¹C/ˆëÐ5Js‰Ó+AfúãZ­‰3³ÝY¥j5ï}¸Gpèë »…”sÉÚØécÇî¹?;»}aJ¼ŽðÈñfÿu"Úr F¾G){Hç¢íû${‡“gd»_äÎú·¦D1Òf“\Ž•Rç#ÃqyáßRhÌÊJ'mGÄÍ ŒñgKÓÌ@~EÞ¼àýb§ˆ3-kô”FJO‡Ãûp ©œW“'›¦€UÕóPóWžR-ó5ØÙßD{„:~,âHÀïO)÷Ï,ÓQ]Þ“Pþ¢\¶~KRsûbLmêÈŲ‰+EäêO"ŽþàI »\*cFVÆ|›>R¹oñfߊxn?ŽB¸dÄû'êiöÌqîlUµCS†/¦r£¤|ÙW—×ÛjÚ8;†üDløFZ Î4ð—¤ddßT]Ži> ú§(ìÌCŽHŸ!Ì =ç# ]X ì_F܆Ï_1‡™E?æ%`wùž'[‘_2´¨nÐP‘úÈ„Ra {šïD:×hJ­'ÔûWÞ …)PðåKHã6©‰1gù(ÕÛä§ÜHÙaOÏÑÇ? a}!*Êsp{˜ }¹Ÿ²jHÐè}ŽÏ}]½&ú»‡ðýäçBßwÑ—X:GBeü‰*9¡LøÎUš¼ vbs›žoL!wSJþI%E£˜"*4ý i—8„\LP£mÛ+÷#…Cã04.‡Ò;™ûž}6 Î"¤u>ßS |gÀOÄ4VBeÿ§gÝ,•1 Ñ”‡ûñsƒªº(Yœš1ž¦!¨ß2ï}Ó õ½¶å¼&yò¡¥ÏQ6? Vìpà‡®ç_¡<¨#n”Ù¨N†iΖžÕÍög?•0б̬Œ¿È®£+}pÞŽ²æ¶xn{ ð¬i~ÂûÔ±¦&ZûGc*ѶLÈUYÏípµÍŸ®6ùJÍȾ‹LhMª|½ÕÉré[)¾”™3]h7¨NÝ,Ð^ N>Œ“tÓü2å‘ìäÇ2h3P¿.yÞ¯Á/x7ü˜€v© æv>º×Y^¿¨³¼ …[>*_6x7~˜úïP÷¤£ì(OuíÐ?v{Û­Ú8;P~6Šp‰^’ˆ ý:F›7cžïi,ú,ت<ªäÅ;ö?Žüá쬌+ËxÿF7‹!_€Jìâ©iÖ"¤9è݃`[;#3½\–’1qž2M-_/: aߥðXP‚xäbTŒxÔ¡Ëaµˆì‹ÍÜ3IÈ+ÍEÂQlÑWƒŸ£1ª:ÚUÁúÁîñ?Êø€Òö]…Ew¨kíä:â 纊Ãß ™0¡¥‘¯ÏEåÙÞ¼EüUž‡ûóWÝ7*fºÃ!/œ1ÎMóså¦lDá;ªø/po‡›}-ÍÔ´4RÙÏIÉȳêï‹ì— rg÷€ü½ÖϾ<~û¥xçþ!h þáξ32ÇþbÛ×ÅsÔã'ïß[ü8xýæì,wy‹L^Ù¢/X`šæÓÈË©©L ™÷‘ »6弦yòÕÖãy zPž%â ¨?ž•™>Ö¶KõdÍ3½ê']Èk`G#1ËÐÂ<á-ºÈ!U¹–ƒæÇ¡7õr5´"Ï&¸šÍšâ¾ßGÏPòã>Ð;êùß°XÏËý×"åaðRÓ|Y£EïÎÉ({ïA³s?þmǧ6ï„vä8§K;~š'Ý^Çð<´Fë…iN‚¿òN?x˜„ņÏÌJO³Ã—òCüä’´våü€àÛ=!x¿õª’ßu齟–Ö‡ìkÒ‘?2µmãJC5Üÿèœ4|M§•Oét €KzáSÁr]²+·ÓC4‡xÑ×JÔ0"Fa“æ¾öþï´#8ÜOkô·Ýæ•èE~‰À_btK¸Ô˜æòKaJ-ÔµPá#|-ëYièÕÊ¿”0o( yࡤ;ðEšŠ:F¾¨„Ö)é’'z(ÿ@ÀðÞ°Jù‹ãÒ+ß`1bKÏ—¨¤Í©‘ æ§¶öàѵèÍ’ð…æBc_¥ €&7·ätÐß- iÊ -2¿¦¢Tœ<ô±‰‡†ÇûòhC~©M9¯iž|‰© Ï}Ã9•ëeßï¦ÿŒr¿ ùî¾öX˜w#4 †ÙÄUÞ¡%wK}©äÙ¿ëÇ‹¼û7ƒw“xòÉD;|(ù±Ã†û¬iþüÓ©I¾”¾Ã*{hÀžö¾¢*~’¿ú_Ëü¢6«¯¿æ@95âG…DðQÚ.Éí¨wýÝÐÆl‡*ù!´q•#i06blsk–gÌFŒdÓ [yÏ×°åäS«P£fÛs|‡Ñ»iˆÉ¹M°íU!f`04ƒ×n¶]°'z«P©þäžâ™ØUy½Ç;¤eJåDø¤êy.+c í¨Û¬Ñ/ù…[Ðó?z`VÃÇáþöNWK»Ç[ÑIÉ|Œ¦CPN[‡žòŒÜ]ÑC˜_2ðŠaj vçß @huÇÈ·5h°›Í@˜©–G¼‡!!-Ì×£ORÞÓ·#mÒL ÜAQªùq¸âר~í§Ô´U …ÅÐuâÍÚPyoÇg?1 2ïVº–Ô¾€ö#XÙ°ƒYÏš”s¶ç©<ò WkÂñ4RIECû|x© „þPQ@—j,Ë}øÿž9²°ª 2^¯PV,¿ŽÒòƒ9h‹7añÞ'qtza4Ø÷ÀϤ°Æ¦ºrnGT“<•û ÀsÛ­ªçã£FTåNn©žñ½!Iú¢CórU~gxÒ—ChݹÎ{!`ÎJñŒ?Ò×mòã®ü])Wù{ _j’¿ê¢ –/”Õ$ '\AË::£Éhc~öm«¬wMŽÁ"–;õIÓ>å¦&ôbŠh¸2½Ë |E[øø2ZsJ,È’•:Gq°v¨’ïU•óÚä©*šªªU…³Ý°mï6ðq™ÿ*^rÇbþ}˜ë½ k£?DgôWCa?¬R£àò Ôýú®ì¯M~ö~TüèT=exõu¦©‹–`'Ö-ÜiÓe?ÃÍŸï³6ùÂa;£BÀŒ¤¶m·¨c©¦iXëUhÑš– OÄ`b²yÕTūО‹º@ ùjmhú  kiæSYG$7c1ç?Ñ}\ë}Ôiç“N¬¼¢ÃǦ*h!U¾žß²EÓ¸ÁÎ¥Nùû½­¦f»7TWmÜH¶Ó›}ÈÙ}zm¾îºë0)[†VŸ×û°œú n“ÙéÍiÿ\VúF¨ƒ+u¨"ܰñã[c±žI'{E:îƒ_$òTõÀƃö³ËbÑÚt…ÓÓÓ·TÇçšæ‡bjúî¶mœî $xìô=ë"µÍ×½žI¤HÌóßmÓKS>ó–ýÙ]$jçõì¹#ÜúG‹E‹ã‹d]”õXoãlÌùÉ0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0 z¹xHÆ„¾†i\d»áRxÜg)š6*”9³Œ#À0Q.Ï•Jn‘šcîô̱?×5Áu&€é’ôoνBªû…]¤¦Pr;ð$šW×ãøF€`š"ܸdV{¥„&¤Ø ™õLgWçç<ž;‹jOmüÕ‰Nqg³‘‰®RÊOñ{MKÒ>š>vìžÚÇ~F€`úD`È„ -Íó¥ÔÍø]ˆÁãF¤?hVVÆg‘¦#â8%#k„2ÅRªåèM Ÿ™íþ&ÒDs|Œ#À0Œ@]#šžu–)Ĥó©‰Q³2ÝOG2MG$#+¾ê))ä…«Ýe³2G®‰dü#À0Œ#P_üüíü 'ž{ýËÒ,8£á‘'ö?wßÏßÌÿ!RéGlLjg¥äLjð3³Ò¯‡Ú*u6Œ#À0Œ@l#á+SÝÙoB¨] Ñvq¤ÔÑZ$`¡Wˆg6©…«í,|#*ÇÁ0Œ# X2 ² ´¬Àov™Ì ›´ˆ`Zíl-¸Âœï,OjAØTqŒ#À0Œ@!@² ó>’uÖŸÐL[ÐCø”\E€##À0Œ@T"@2Ždµ½6†-€éÒ}¾òµÐÃQ0Œ#À0Q‹ðk$ó,Ù&•a `:áŠÙ }¾aÒÂÁF€`¨F€dɼ²Óâ5lŒÔ»Ñ W|ÈFX|àÀŒ#À01€€%ë ó@j·pÉu†íŒ8èüL6Œ#À0Œ@ƒG€ŽTF&Iö…eÂÀØ÷K+ðÙÎa±3Œ#ÀÄ {$ó¾T(*èXÁŒédF€`¢ÀQà &$RЩ5÷gg·–žöM¤hâxªF`PFö]8MïǪ}±+#а[ݰààÜÄ2xžl•¯ŽÃ™ä7c›@ ÊKJzÖß8>n±ÐäœÙ™é/Ärþüi÷x”æñHœ9“âÎú˜óíÂãÒçúÇL›‚s›æ—ÏçþÀß=äo%Ú#ìñ!‡ç€Œ@ "À#àd“\‡'Mjš¯ç*”º ó3S4é¸Äé’§))3±i>WSroåP±m“£gm¸Ï3©K]äÂ4ÔŒQ?ž\qsœŒ#PŠ€¹$Ä<t.ë–Üœ‘‘CœÒÕwZÖè?|2E7—<ãóÝ ^gL8Þ0õ:¾BÈ÷°Êó¢Ü}EÙkDƒŒ3ÁD!,€£)LRíÈ1· ÀÈ÷,MÓR§eV¾A#”1þlišPO‹²£æÅN7ÆOx ÌKþ5Ñ%µ®¦©†Áßðÿ«Ôä¿H›’‘}“Pf ê‹ßV!å4ß;C¡¿]IqAgÒ½¡OAzgaß¼ ~p¸#fxƮǻeRÜÙ a^‚›Vú—Y•?¥g-Âe'/Ámú`wæ SéãȱØëý4zç®YÙîsìƒÜ™çA¦ãN²ã‘æN„ý “³KšÇsg‘í'ØS“j½©d6„ð£)ãÿoVfÚ¢`~mûÏÌ$eìÌ„B| ðézVáÄ 9iû«ÉÙ=J™ƒá§èZ Lg‚Æ;.ß'Å+¼;'Á} üµR,uHÍ==3í[_üÎÄ"¬‚ŽE®1Í0ÔI¸ƒºðÜcz>_Ñ!ð×àô¬›¥2æCÀìA¸?7„U ]”,NÍš_¨Bð}à ‚êh|Ñ<©0¤câšc¹Jõ¬&µ,tÊ/ Ç%â×ÃýS!”CCzÈßgð;(GÏ©Ñiu¦I²kÛ zÓü·Ç3'ÎJ+È?rWÞ …)nEä%¤ypúsÉæx³ßö †ÉÃÊ4gï_5‡¸´ÍÆ$öƒ4uàëÞSfÎt!Þïðz30ûÀŸHÒMóË”G²øûçoF Öàp¬qŒé­„FF'C˜®½îºë +«64¯¹oñãJÈ7gg¹o²}{<_¾²E_°À4ͧ!8N…P…ü)5RÉÜ®–]§y†YûÝïugÎ-Vb­)Å—«ÇÏèÍäó¾gŸ}³xǾÝJ×àó¥¡!•h¡ôß™YîGm»Áž C¯ÒõâQ°£_ÍìÌ´¯±j¸£0ÕðxgÜvúhˆ/Þ±ÿqªgge\YéKè,,F¾^@çàâ™YiW•: ‰³RS½CV0jóÎA@ï8§K;~š'ý×2Çç¹³Ö£c0 ß'WÀŒ@Œ!€(F ÆâHŒ€·Ö$¹¹%§+¡:B@=çëßã cô9 þÉC›x¨¯TªŸØÂ—ìŸËÊX‡ôÖã÷ƒ¯ð›2|x1âø’û áñ¡)×ë¾v¤z†¿1R½È×>Ü÷’]¹ý hÁèòE߸T¢†1r'Í3|탼[íÂtû%´©Èzªg|À‘yixu-òý…ðµ¬gd¥aÔ*ÿR¼,dÒÆêt‡tÌ* WúÿiÏ{¡ZþÐ׎ÞѺ‘FÊ>Â×òÜ)/}©³aYð?F FàpŒ2ŽÉ>€êŒR;°©âM©äêpůñ÷%5m•2 aèúáp[ëãžãón½BxîÇH‘Ž£«`0ŠÛ‹f,ñ‘ÐʵÑßNÓÄjÓçùÛ‡óyíÃ(<²1óÀì¸T!Ë8@£ñn¶]Mž.g‹4]ß}…Ò ¨¢U¿=b|¥`ˆ³mùˆßÏÃjˆ}²×(MÛá‚]EÚ6ÁC¾a›ˆ|üîë€F+|kÆî|êèT⣯_~g¢ÀÑ̦­F ‘^‹áRß9sæ8j¢†¦He¼î­¹Cy1)°ÀªÂÈ Â¥°’ßR =ˆ}%k‡aTÒ6aÔ˜ !î„@ATÞ•“-ÜªÆ y3¥L&7¢£¹á|²ó5†ë|¿«{§‘ÿ G² Ãüx«ž5L‹w¾ŠT*SÀ.Qö6žZ…5Œb¯Êù :UÉÿ„EW¯Ut /)ô¹³‚=01† àc“[M¨ï %nÿü·Õ·Ãõ…Ê>ؘÂñ³º0 dWØî9à‚7Ý²ÃØJû«‚}> òõŽˆ¦âhM©³1èÛä#|1­,*-xJñ<ÑFy ]~ò) UšÒÖ` /„S[8Ó¾$ §ZZΗþ VZ¿ZÇÅêóJÁ¥ú}ˆn•ìÉBª®è<¬²^…Ü€8„,2;ã±ìl :ÐÙ×`^ „pጬô—|íùh(Tê•7”Œq>æ!í^B³¾ÛKuOîYUÎÚ&/Ãà©Òö–Jþ”y Á¦ŽâðŠ‚²’ÇÚ[è†q©o¨ÁžÇÛAÅÚêûr{‰UÔB5§=¾åvxQÞ¢óÄ¥sÞ%†Qq¾ÙéÂqŽ2_y0EÎÄ'9F@ Ý|Фj…˜•\„ÎÁ@ê(øÚ§dL<ƒñ>X=ŸìÊDçGÂkX%{:Ñ #ãs|ÃÒ;⤕êçó‘¢þÈðwCA€GÀ …“8´b7jRl z[‰â%Ø3û¸pÈŸœJÛašF/laÁ"-Ç»32ÇþB ¥°‚øNlz[bvÆIõ–!ââLé½á¯ÚöV纒H 9©¶NÍÈÞçpÎ÷f[S/~*W]8åó´R8¿„WaLD~&Ë8µÒ(–gB¥üèA§á€9ÿ˜Ã|¾ìÏÝØÒó ÔƒšËeЂ°ž‡v¤º³31Ù;ù+Ñ\ê[Y tó$,¨:EHÇÔY™c~;SÍÞ¦¤¥í®€–—KC7oÿ/¬,¿Lé…Ÿ"ÍNWÜÓôö1 }ò¾²£ãŒS˜©Ùî ˆãÕ±àÁ–x‡óÓCµÞ¢g?çÖ¥ñúüoâÊ–yÞ[±Iû ¬¤¾ü=*ŠS °ØíññͯŒ@Ì!À#à˜c™ãÒ¿HpÆ·Ïð»—æ,uS_lâ£K0²Äüc©Á™ÐØ·«†` wC±’éªd¥©Ä?!´¯™™Ž9ÎÈ ‹šã2ì%Sä-Ù„¬H5ÜêÕsgyÒVÚ©Í̳û‰‡€Ö“MÜgB=mª é”×BÜ-µýÑÓšë–ÚCf½K„Z9,5/¹ÍÌJŸ„=ËwÃíjë^ó7äïϾRi9ù¯©™í~Â|®¿ÿ'z(?1IÒÒ|Ïë-Þj˜æ»4ëJÖúÑ s;B]@IDATLgWç! ¯@[1µØ[’£L}!ẫÚÿÛýœ5fÌ>-AžÀ Üñª©ŠWaD=«ªGÚ~øÉÄ2º±!æsC_QÐ@§÷„%cÂFà¾ñãÛª‡ë9ÏèJ+•}#6~|k,\2§[q>Ø×Sï8ÝêA'fe¹5šë¥ #ô¸"(ƒEK*ÙÞìÎq®ä\k‹N0eöØSÜ]Ívùn•²ƒ”^P‘ß²EÓ¸“GεíëêI´ïôfrvŸ^›«Z‡}×Îmâ‡.çô*6´ÀnÞ²?»‹D­à¼ž=wÔ$Œ–ŸŒ@¤ˆ¹G„ØÄD:“#딞‰…Î8’ #À4"%÷XÝ Šg‚`F Ö`kczF€`¼ ºA°‘3­Ház_J}Ï^ßh%•ébzF€p=ÎÉ5.ff=¼9¦F€`* À*è pð#À0Œ#P?°®œ9F€`F ,€+ÀÁŒ#À0Œ@ý À¸~pæTF€` °®0Œ#À0õƒ àúÁ™SaF€`* À¸üÁ0Œ#ÀÔ,€ëgN…`F€¨€@ÌÄ1$cB_Ã4.@nºá ´N8é¾i…œ5‚\s—‹+í¶àš¹Ü­:=s,.ðÀ“ ûòîs:#,þãÒÛÆÌÿÃYÎH­ð«ûÙT÷ Ã|ªK\—)Ï!ßù\kB8@XÄŒÆõO·[^€LéÜû°Îê¤&Ž9¼³LJˆw„…@l¶:EÅâ·59â§k;,_³yRa“ÂûïxÈ=è¥Ç³èòtÜIÓ‚˜®ñÓŸ  gk¦ìÔû°NÌÿØ.·L}øªû“ÑFޏ{Œçîç'z¨î7¤xøˆEa a ¯¾gsåëço¾|‰žua |ÀuªÿéÜ®E“AW÷×.:ãXÙ¥]KárÆLÿ¡.`)“p ú0yD÷býÖ]IùEÞ›Ž;ãÌü_|ýc™ÇX¬ŒÔÈ8ïóÈNWÜ à2ó¿œíüÂXm ÝÏ+,¹©ïYýó–|ûå"@‹õ>ê9)¹íj[Y*|Å“'Õ]{øöK=»vˆzæL ŸÑw\ê8ñ¨C¥Ãáš|ÇÃî‘ Ç…u¶båRx¢“èuý”Êóˆ°a‚ p îwG».ÿuOÚcÀk,Õû 9k¸ÖÑ,€%T)`äû8_uÏÕgË8WØö†ËIŸœN„áærÅ¿ñÞ/‚3 aâw´ a¢ètÝD?óh°aj€€oÝ—R›|û¨´ ,ê} r×ð¼D­NIñ$º\®ç»´o!n»¬_´ ¨,„[§¶-D³-¦}ô)ÍA¤-„£’Þ2¢,áKôÝ]Ú·TÌÿhfÓPéÜ®¥JHHš}Þyÿl£¶­FüꋦheŠf´’÷¦Ùéú§8xäZq Ün¼ðTÍT¢Ã±çœ= ±ÄãGçQË÷2úâ‰^¢›ùDØ0µD€êþ œâ0•êØ¹O¯û<šë}-s×p¼GcClÍÿ9]ŽGÞEñœox…ð#“›6Œ˜ñ‹Ã/ç…,¾—Ñ—Hô2ÿF D캟˜x/¢ˆÖzbîF°hÀÚ-ÃGŸdšª¶±ê9å¬ Ç¶ç]uý)ˆ.ZGÁT©—_Fg[æ˜ÏQ4j¨)¥Ú_y{êÉ‚GÁQV¢M[£ W|ÜtÈöùF\±IáHx¶j×þ\ä ¿hë ûŽ~ˆNæl–5¦:º°ë~“ÍÏÂz]`j¢R‡îtÂÙ84¼$ Ǧ‰ñJs:»"w¤†Ž¶mI¶&º‰N¢—ùßðÊ"ç¨~°ê>ÚR©9º!åh«÷õ F¦m˜èqMvÄñ’ÑF[²¯æ$µhž,.g;„ðG Æ¥|/™'DoÍsÇ> íïÛví‹j0ŠK¼båú­AiüâÇßÅšMÛƒº³Cí ¶T:dG„´5_ÑRïkŸ™"ÚAô85!›âlçˆ5ÀéSÿ+Þý2¼{ vìÎ9;öÄ,û O‡Ôh;©¨"FÓ|Å÷2ºâ‰ÎHòñ6³qëßbpöKbã¶¿+åéƒo~ï}^9¯i6-DÏÐ ÿ£Ÿ™#þóÁõAC­X»ELyãsáÕõ€~>_´\ü¹a[@7¶ ªKš°ê~´ÕûÐ2Ô€BQ#-†®¥ŠTBELøF*s}û‹hÛª™ÀÞºHEYïñ(eákWB{%4a¼Å¬{*Ëùޤ¨<Æ•ÑY÷)7°î¼ẫÂÉ{®:[´iÑDüµy—xëóEâÈC;‰SŽ>, º'ÙMýàM|Œl@têÎ ±ìNn´ÔûºËl ÅM˜`£Bâ€8¨3¼tÕ&ñõÏ+ű=»ˆÏ. ñq¢ÿ‰GгN8ÂbÛo«7‹Ï~X.6ïØ-ißJàÜi±.g§øé÷u">Î%.]ƒýu§Šcþ+Ä3*³}y…âˆn,¿‡vn+ªK£Äkˆ·çý(~]µ‘G‰“z*®=ï$kT0ç³Å/n¸ñGœsRo1ðÔ£-º"óÏêØÏ©ÒðŽSÊ÷Rš@_ôuÀ¢¤êhxsî"kôyË%§W[_x÷Ñ$)^\7Æ ñãò¿Ä‡ß.ã†\-¾Yò§˜·h…xð– Eó¦Iâ«ÅXe¾›&Ó‚Ц:¦];´Ý;µ_ü¸B¬Û¼ÃÀT¾_ºJœyü‘âÍÏYu®W·ŽbÞ)‡C³F»¯ò½È+(gôé)0ZÃ횥ýÁ¯.Þÿz‰øeåFQP\"’PWQaÄ÷]$’ãO]Ö•ŠyŒý/«NE[½}XÃÌA´4À” {$DËæëL—x½â÷µ9â‡ßÖBè,ºulm5EÅ^«ñzýÓ…¢;ìî¿q Œ®¢uó¦x"’ãÅñGtw\ÞOÚ¹{«fÉâª'Z •W7ÄGßþjÙW•yx﫟Å+ÖBð%®=÷Dqø!í­pß,YmÝjtÇeg‰ËÎ:^üï‹Åbçžý–[$þ•,íJXŽw$â#_:,Úxšûó‹DnA¡¸º2¸/¿ÐzvJ…(ÿ»÷å[Ÿ§÷9Ë0¤xkÞObon¾xçË%â´c(|)©Œ£ƒú&bæxO>¦‡ÑðǺ-‚lߣº l‰d÷÷¾<ô±K…ì«}/â!toºè4±ko®Øö÷9lŠóç?6ˆ‡ï¸XÜ~éV¸»0ʧNA]×+ è_Y›JõË·¾5 ÆfV¢mlŽ:GóŠþÇ‹£ *; #Ö…ËÖXHŸ^‡XénÀ<ÛéèŸwJïr:œ‡hÕnö»P &n¾ø4Kõl9úý£…]ÔC7MÓR§Ñâß(ÒÐ1R¦†ªk‡Vbñ›bÕÆm˜GÛÑÄúr7ZÙa­•0ZéŠ0üõ] 2ˆ*þ†´2$iÅrÊÕ„¦¯–á-"ônÔAhÿ´H¸6²<:œhVþîû²S;dºbª‡ m™iÝ¢©õNÿŽ:¬³5z¦)#ZÙ¢i²Õi®ÇºRNKy±ëXÉNìg#p½ ç œõîÚˆ´».‡\+^ýx¡h¹ I\uΉ•hÚ°u—øôûßÄý7 ´…/úÝRÁùv¥As»tN«ïH€"whš ‘Gÿ¾G"½¾•Ò‹ŒEÝ©ö#CŸK¬ÐiÓ½Ï@e¨¥òfw:é{ó޽ô(7$ÿܰUtjÛRÌ]ø›8þÈ®Öz…r^hJ†4JÔ‰ô5.”ù@¦#4Ö¢ÁõxÝ×› ~2u‡@4©û‚w±ë.ÿb&u-¶Ê+(²æ©A‰§…0Z7ë·ì4WLj°Ýû ,û–ðó7殾‡Û25XOLªï%9Óüõì—b1™ãè*­øKü‰QõòÉÍ_P[#ûï`ã~°Ó,šõ•u9»¬½×ÖÞµ=öÛî[w_V¬Ý\…nâel': á7ž'¶ïÞ‡…‰+ÊÝý_hÞ–:¤4R]ºz“èÚ±t„¿?ÿoîM“¬_4zž‡¾Û“Vü•#ò‹ŠÅ,Fìz“}æ9HuÅ? ±üÍõ. ¸xxð£BßÁ¦!>ÆJÐßûÛ$èÍ·ýËVGŸ‘éÿ}úƒñÄkâ6,9 JhÞ÷±™ïBu‡ùÞ .[X#ähåéóï|-žyý3Ëÿ©X´Ò§WWk%ô+}'ž†=Í%'"ÞÁמ#ÚúÌ©Õ(ª=•a\>\µïúqFšê'ç!¦BûmmC‚ì‘”+ìÏ=OþÇ¡b%öäÒTK“¤Dqvß#ħßýf…ýè›_-èºîü“-y%ÊöÛó£ŒbuDý Õdh‹Þ9'%Î=ùÀÚ ¿þßToÞœû#:¾kÅ Gv¯°Æ‚VWëâé׿ZõAèÖC\~öñõUWüÉÝïÒ%¾õ,vóÒ€('†„eRÜ™_Q³²2úÓ3 Cz*ÚãÐì·Üïõ>¼Û‰#o½0ŒèBJ½p [×i›„¯¡Ý…è‘'A]f Óö¤PLq‰nÍ“ õ5¤Š+Ķ‹äÄÚuóä+ŸŠ¥¿¯þãÕg&ßé¤:É&˜Içgàw°L9ßAMÌw¸åþ‡ŸéÓ»çQ‹ÿ ˆƒ‘.•Áø¸ƒÛ§u´‹À—ªƒ?=GÜxÑ©ÖZ ê~½d¥u ÎÓ£n)¯uQWê2Mªû¿­\»ìå§&Ü‚t¢¥Þ×e–ë<îHɽƒ[óê¦Ðp9ÃBÂÐWøRì¡ ßÒ°Ó!Áß$‰NŒdÃÔ-¾B¯nS ;-ðò§ƒ„2ÙÓ‚Å=Ø E[”–a_q—v­Ê…/ÅÈu%8®ìý–ÑO7SÈ0 êØÞ}åYâ‡eaÏïJ‘gÍ<-øv¨ g­"À¸2–³ÅÄ:½± ‰~l†Š@ÅIΆšKÎ#À0Œ#e°Ž2†09Œ#À0ÀƒÏœKF $è*O{ŸzHp F€Š à Ð°#ÀÐÞxÚ~dž`"/Š<¦##РØ¹fÿï+Ü ÖÄ:dƒn#¢ÓÚ¶ïÞoXóÓŠubÈ?à„8ݺžs+.p8óø#Ùß~Y¿…g†ˆ$<Ž$š#Р­@'Õݺpê[_X9¤}¹ï}µÛ„ÖÀ­±SÌxûK± ÷hÓ–ïÂîfÃ0Á`va @÷÷…¾wWoÄUt8:­jèuçŠ~Çõ²ÎH_›³÷c÷µüÀ]×lF jXW»2„øÒ£VËž8Ž• ÕlŸG—&ÐéUMËNpkÞ„N•eÃ0U!À¸*tØ`Äo¸á¨Ä«‹1§K7„‘àõ74?L—†,þc½%ˆYýì3•àEX•1aF€ðA 3n[šð‡¸"Ó×↤`æšsûŠÿÍ_,>øúWqD÷öÁ¼±=#À”!À˜‹#ÀE`â}×Yn—á @º)Ô¾ë ÌûÒÏ×wD7\YØÍºáëÏ [±Rz»¯3¿3Œ€,€ýáOF€¨Œ€´%oe§ 6äÍÿÏ øƒ`Êà9àr(ø…`"…@›ÍÄy§ôŽTt#Ð `Ü ÙÊ™b.íZ5÷ësp‰àÔ(G€p”3ˆÉcF€h˜ð°_¾¥Ÿm|ßm»Ú>}çÏèÝ÷»¶q±ÿºG ÒeÀ—ßÌÿºç_¨)DšïDó+÷hõÞãû~À¶voh!èÝ÷»v1dß^Û•Î4Mkÿ"ôóǺ-b÷þ|±/¿À:ß6,ö–·ø8§hžœ$Z5O¶.ïÖ±µÐ4ͪ ¾•ô —‡F™¼]6lÙ%V¬Í»÷勽y¢¤Ä{€õ¡VñÂ#ÿb—]ûéïÍßvž¸îG”KTâéGÚUí¶cûºâãχM7¡ÉŽhñšD"5%E®0ÕVeª¦aÌ}ñ‰ÌŸ¯‰_L äF+€©òQÅ3 S{ÅWKVŠo~Y)róK •pj…BsîC¹)=õ'ÜB£D¼0õæB7ÅG –‰¦Éqb@ßÞ¢ߣD|¼³\‡›‡¯9THÈÎÿi¥ørñ ±?¿¸NxOÅÿíú±kO®øÒm»ö‰B`UTT" ðLp9E‹&I¢e³$ѦESqh—¶Ö!$ˆíŽeÍ9Q¿>í¼qÝ(î¶ÐuôïeR·ïºW ­T‚·ÀL.É—qFID¦=Kqf¾3Y¹’4Íéx,%ý±mº®žÙõÇÆ©ï¿ÿBrf ãˆf2Ò‘5Jl ^¯×kxÞš÷#ß‘˜¸F´m½Ï?шEk+>ÓL……Gà<Ý>âýoJÄ×Kþ7 ù~¹ˆsmÚ}(6Ô9£H°''/µ~EEÝÄî½—‰Yï‹ËÎ샫Þþ!œÎÒ†+Uzu^°ËÀ<˜w¿\Þïï?¨ÞùÑÊÂÅ0 ±£ÝÞÿFéVG´U«Õ"1~-ꮀè+åÀ1•DaÁ‘b×¾Þbμýâ+Ô«».?KtlÛÂêTÚe™®+¤©ªqÐÍ’3mš)ÛoÀİ´ùÎu? +µG½$Gân9fxBbò¸–E»Ô›¾ó·{vÑsÏJúÉ­ÉÄ‚.ýÛ©¤ÖïÞñ°;í¥ÉYO#q¿¨ 7lW@/δ-†ðÿÓïbîˆä¤¢uëÿazÂ[§%Pä$ð;´Ÿ)þþûjŒ†K'/Î=©7;VIÂ+\;» Ì[´B¼á{0yOy9Xü'ÈØOÒE ësvŠéoÏç.ѹã›(‡ÕŸd%¥ r¬_ ñ…((ñއÒåKg“&b¢R7L UÀ¢âb±lÕFñÙ¢RáÛ¶Í›‘*!ÅC‚ߢa×õâÃo…hÛ¢‰8¶gWK;‘ÖÚ„Dbƒ De€. »jIølÞ°õÅ[mÙµW¬Û´Clܶ[Ð F:F½¤&nÕ4ÉšëÕyÐ̶ÔÍ¡0>)q¥p´}IlÝ6XüëÕÏDæŒ5MGgcµhÙb34<{1ÊÅâ6åÂu†-1¯Ü 8ô‹–¯ÅeÉâ²³Ž}Pþé4-*ÿ‘Ä\÷CádµaHLÂ7áÊ;S/JjÒìQ¾ç®ÿ¤ÎG½UQF‚ߢ¡ûE$„³oöÀŸoL}ê„!!L*é¨2BS$Õ|÷íÏïû«¥v¦‘o´¢Å»½­x{ÞO¢Gçv¢ Ôs¾ª¹h¡3Vé 2PTäÿ7w¡¥vŽ&Þ¦uÅÊ7ýý¶ªá?Ħí{,:´¸ý(cˆAz›C(:D«V_‡,|í²·È¿E±î­[}…²ü+Ò!M``£”#çÞ¨›Ä ïå‹£ºw7_tšh†E^$ˆÃYÔÅu?0æaÚ’ð%Ùßµk¯Vm;t|ªÔÎ4ò 3Þˆ-rw|K%›µ˜vì±§÷Y¶ìû}ˆœT?Q5'Üà0õüiµ#Íù‰¿¬y…º5ç{0ÔÎÁJÑÒªÅbÛŽ»­9´§þ£¼á‰Ä( XºÁÞ.óÿ.öaÁÍùFæ¿=âݱ{¿xõãïÅ_›wBM¼S´n¹s»«1-Ä6ÿöu˜& ¼|Û*¬gÇö3 t½øUßÞ‘pNNZ&’—‹Ü¼SÄÊ ˆ'^ýD º¢¿èܾeÈS26ß¹î‡ÅJÿÀ4Â%Õ\~‰§^|q*Våµ§9߃¡vö'Îþ&ZúmþJû ç5Žpæ0à'àf«¡CÝThG±gÔôX"–#¿ˆJ{À¥ó¾ùØ×»êgZí\ ®üH©ö“h"Ú¾ÿmµ5Z×uݽT=T‰€]¾ÁV³hå=e Rü/<†Ø€=íO¼ò1ž9¢M«wDÇSDÓ¦?V¾”6m½‹”ð¥ø4¶tU/|ɯmȳ¦ ±.b6:É»Åôÿ}!¶íÜcm£b”¯Ú›ï¤ùâº_äªô[>ú…¯äf-šßÕ«iþ5Ú ÑD´%%%mñøÑ€3ªd^Ti–÷€±Ý¨ó]ë°È¤°ØM’–F:©ˆÅG´åc„N bJ@7ÞkÛðDŒ˜‘]ˆ÷¹%QÍ{‚;\þ—æ×ÛÿÞ'¦¼ñ™(Ñ÷@ MMšü3ܤE]íÛ¾ ‹òÄóï‹úPh­ß ZÓº`óê×ýˆ±Þwô›pæ…—Ÿ$Î6ØjtPç}«ÊE›æhwÑõ·4j§Ñ{ÔÐÛ°ÊëµÔÏ¡¦ž>íóVC´+×o³è¥ç­y;tÙ‰N7‹vÞ>áòŸ„T ¶û¼üÑwèÀBÍÆˆw÷Á€>¬4]ØÖºÍ±so¾øü‡–FˆQRþjbl¾sݯ Z5öc `Z|•ضs—þ°PØç[ãêÛ#ÑF4¶hÓæ<¤Mt³®/&Pe%F§Ñêç}y…¡@=Vy;D}ÑT]:DѸ'7¿\õVÓF§º¸£»]þÞ—õ¼'þ„ÃÊ+-6\¸l5ÔλE«–ïT7ÇJ9HÂÊ餤åb!VHïÏË/ëÖllóë~D¹M6{þ7Áß'\)Ú‹­†h#5‡³+h´GÀQ3ðŒBê‚´·‘¶YP%,.Æ<~šƒÃE·!÷Ytý”6¡!`—}ùt‚ÓþÐ"©çP¡ðßV¹ÒˆoþÏ¿ãxÓ­8ìeY=SùähN¸Äkbëà&«]Óu6ß¹îG”'$/h•Y¼Ãål—äÍ‹un°œ8®²}ÝQ5Ü`05H¶Š*-­„ô¢"k²$Ÿ¢Æžhô–ÍèÖÞÎKÔ#„ظ•jAtð>z{꾆ÂÊ+•—?×oï-Äž¾QÆì{|ÜfL"gÇ«M#üê:¤¾|çº1Ö“ ¥€-!ìÐIñ¦7ê0ѨI. ºmtTÐÝ`0€¶æ‹¨ñ¥U\Hd²Ž CˆM;« Cg™­Š$¢'þ°…½q ]uöëd1»„ÞMþ—ÎðhÐi(/¬àótkÁ)V+Vûwzúýº]Kc¯Ya£Rß®Wѽ×.¥ÿ»e=zÓZºnøLä?ž¬mðn3µN=!àôát°’²Á”‘ P»”8ìg6C›a.³ñó²ÂÊ_Â<ó/âàØè«¾¡;¯àÃv`—1ˆùið=ùñŒßÇçà6hH´ÕÙ—S¿×fóæwʼù>üá*÷5è?éd™SÓéäw—Q\§>’é³· çÇç”=£ÿ#´çÐ Ê;²ŽZ'÷¢Ã…[èÍ9ЬoÿH½:^LÜ@³û~í+tJß±×*Ð$=Ê®ïüoß: ‹±’!„yÛcpkùç úl”¿šœ0çz”~Zÿ½þÑ`úà›[©[æ94¨×(˜ŒÌ§5ÛÞ§áƒÆœ°°#_…8¹ Øu2’oZ°à»ë9Þù±5ð1Jˆ;Vÿƒ™OúEz¸‹!\q÷›$Ål¡ö·ÜGyŸ¼ÉÛUÈœ”Je[ÖÒ–¬»\ןÇèqÛ Ðáo?£xÐï´ô€®c‚nC_¦‹÷EIç§Ÿô ­ƒp-¯t™õk“v"„ñ „TÐຠGãtÚiÕoÿ¢ÓOz‡ûÜá¡£¿Ñ¿æü‰¬ 0Òå å•;й>}©Oît…Õsƒ»ˆg1§þ'02Q¯ »V¨²‚¨¨ä.²°ù?\lL2 é}½¿p”zãÎψ/—ûöù•:fœJ¿n}öBH.ÚJƒ{ßAËÖ¼äGjÇ‚),¥ÙKÖ÷18Èþ+)8¿÷®«Ï®–åRˆ!Û¢rZ»i§AaáyÔ¦ÍûÇ" à©_·«É¬Æ¢<ªÇ²ãÀRwle‡qpÂAþHÑß-ß0¸~9εî 3’þo…j“>‹R“ÑÑ‹hÓiËþ5”j¡ø„x¬òŽ¥>=:Òˆs¹éà‡u[÷Ò×Ë×cžÏþ†ñØ.â´’â"2Ç'“9®–wŸxçŸf¤÷§“º¤µÛ?¤®íÎvÇŒü‡¢î÷>‘ îûm™Å³.UàÀ¬Ø´Qpä$Uû7Óê¬KHµÄÒ‘%®ÑÀ•ûwSéæ5n,ÅÃÁÏÞ¡“¦}Aq{RåîmâuTÜÕè¨ÈNè2Ñ6µ/<ºÁ7¶Ë!€Uõø>L^Á:JOîŽ;À!]¤#žÒ|¶Ôâ¿ÈS\L µKëGûóWŠWÄüÏÀ»p9׹ъ®!šLèÜóé?8_·¤”íÏîÚ¤õ¡C…¿Aû=vžyjbgêÝå2ºüôt­˜;§ìJÊ@C.¤¶i}NØbɧ¶mÞ£”äeäTÌTåÀˆ¬Âñxkˆu¹P¨6Þùç:Åé/ÁâXVfWÕ"#˜ù¯q”ýˆëÒ‹Êwm…Æâ*S椊ïzuyðYj}ÑudJLvçØvä 9JŠ(¾KàSîH òp¼ô0aF"ÃlŠ£¤„vt´x‡OdèÆÉJKê¢UúHz2,­’ºaäÌW—Gãe@%åyÐ~i›…à—ŒÓóÛÔ驉t÷5çàˆ¾DÒ§ÏÖ‡K«Ô®M*ÝqÕ9tðÐZøËï”_0ÃÑ­(5eq@VÀZ%u§¯²ß¿ûutÆ€‡uÍ{ñê©úð³ ëIZrWñ3à{jÊ×8©ÅÄv¥»¯=‡’ptcœ.„=ÏÊ>©WGâ‹·dñÙßÅ%¥t¤à(Ì˧…kvQ¥oç8G¯wþÏèÿx¾_97í©ãü;ÿÇ%/â2»PÕÝ®_I±ºCny:Œz˜Ö޹”´j—ÝþJøÍìèö-RûÀÉä„LÝW±g^–”„’ãÛû-€ÛBë¸ñü”Þ*­Ñ؇,ÐKÿ™Ok6nýíÝWŸþópaüˆ0D ¥cª ~„ÙñR^ÁÄ]Þt\ín}äÉWöíÕçñÛ.ÅOÿgCüÏÏ¿§#%þÀ%'tÀþÚýÇrÞñÔ¡õ zgþÕ˜zÀXwc¿É íÅÏßYñâ$¦fÁ”`¥Ó{·£U[òh_ñ™TRr*¥§ÍñûHBÎËÞC?×ÊÇ2Ìuÿ°þï”i7œ÷oÜn4ÿ'×Üo1„c,ÇÖ°WQAA¦~Ìb\\,Y dyNØS+=ïx<óÏSO§õ¿>^t7ñȈ56 ‹Ãb¨F¼ Š·ëAÍ(êþºMÛ×¾óò”[A ¿õ¾Žzšþ*š>Þxùò;&½•múÅíõÀǘ ®q1©ÿß>£´Sϧ‚¥_èo«¡Ç´vµÃÂ_4Üå´\,)gJ”ßÎß|¦k[øS07ÆrNºHG€5\oÞŸÚ÷^Ðãf}’·pf¿&\Î%€U¬D6c.ó¤X ˜@'vJ¥„š¢™N,&ðÓq^ë(ûÜé8px m?ð¾@QDŠüǘéÑçר„öýhBA—¿wÏüwmw&nñj÷ÑW-¢“{ýA¾w8¶´"ù÷—v£†«.8DæVÜNïxÈÙ‰‰fO7&½-q˜hsRûÀQ›½œJ!„¹—ë‹ã!KvÅ;õ»üÙ°fÃ[Lb,lÏh`Ï›é¼!OcÅóËú»m†_Â¥éC¶.mH¼ õhñVÖãã­ MxåŽ*Ǻ­Ó?ÑçQý¥óÏk„;©ÇH]û‹I¢níÏÁ*ð éPÁFñS/Ý0]ìü»æ|«±¸ŠV§„Õ7ì&&žù_½ù?ôÊ'¹¯Ÿ7¾©r½öá`wJ¡É¿;ú¨x¨Ú¿‹xZ¸¶—ÝL±Ð|cÚ´×·$©X}Z²îØºŠØvˆÃD›“CÐ>rôàÑnëï“ïv­NÒ‡£ì×ü…O¤'Ã"p¤h›>ÄÜüß}ð'¬pþ#hUèÂa©EóÔÿtÖ·K@; †Ó±l6cºF ^²nWTcÓ»dû= Rañaß®#tƒ¼«cÛaØj4^ß‚WXº›6îøœ¾_÷ŠžFR|¦¾‹ëK0sÙìbT +œíúÉO¼ï9Î3ÿg59ÐÎá¬"'¶fUUól’…&ÿ"½h¹WìÚLig^Œ­ÙÆB¬”S†c[Ò¤`¸dÃJÚõ·g¨ô·Õzv-édNN£ŠX´eN `úýº—é– ?@Cój­'?bŒ/áxsþ°>£iÑê\ñJÞ#Êê"Z± [Ëú?  à·æ]VoŽ:gœ¦k‹.b!>ǘ/Ö„7ï>D¿ï/ V­¾ Xør6ì˜Mgœô0†ÜoÐ÷ù~ù#/Àg8<é7qÜ£ó¼ DZ#¤`3€iZ\WådK~N-¦N5ÿgMÃ>GìqôtuåÇó»|>OÌ,h¸#÷œ øÏ°Ýn'“êÖN”¡Hwšf¡C‡o &êÑ+»±ÅŠ/‹¥áC<ù.0u_ á÷ý¸áœÏ\Q¥ZŽ{ïw ! X­Æh蜖{Eoºk·ò^FÛÏ8n„í)†Ï–ÑL|¬œtÁC !.‹-Ï{α?ügËŒhÁIV „0ì_Wv xÍÓá‚«±â¹-uI‰öË8ÄÁt öâò©H ÒeÝHïn«¬:ZnI4„ k(We–ÍV]]Пæúõ˜{ÃâŠ3«äÐâ1Ÿ×\x7š.Óæpâh·X‹›îFI "ÀüOŽÕq52ï9þò_,À2AÕëž™N¥¥C!È“ÄÅÈ ‹ÎÃA ¨U¡Né °î…:‹÷9ÇÄXôahÏÃ)êÊ‹¨÷|—u¿.„üzÇW¿*ÊËWZâ•*³qG[˜6¦±ºB·Š$h÷+ã¡ÕØ]±r“+kzb,†÷؉í ,ƒ'ÓÆ4¶o•ªu‚y ŒD”.íZž÷Ìž`ðÿä°ýl¦Cù£çÈZgéİó‘#× àÔz´¶ê‚7>ÛÄæ½Î™¡|ç:$ë~Ð*¾`|wîÙºùW<(»k -• FÄ´1{·o[h=éb*þGµØ]1J¥Ÿ#Š!«Ô æV5Xµè?b!É´Å¢ÍLO៼ړWé»qˆ“ŽºènŒafzYcTCóžà/ÿ9¯,h¸Ìðpí°0ÝgƒíäCwAvYð2:ƒË+O pÐAÙ`jç¤ÚX)))A·…žœœ¨Û¸Öí@{Íè/O¾ËºïŽß¿=…ÛŽw¬[ñã6‡­ªè÷V½ù›!Óæ´W]¹ä›ß@ Óí™f§9j0#ëjLh”xå¤kõd;አœ+YÙ¥ÙÁ÷&€ibÚº¶ÁÙ¥ —éæ‹ó!x–¾Û–÷œ»@øÏ ÿØ%/Tê’‘J:¦Àlcµ¨¬|€2]UOp áÛí©¬Çi³eèsÕ…EÃiï¡C8+Ãæ]Ô [ŽXè¦$'SrJ2N@JÔMlÆ@ørÝh¬Nxò]Ö}¿YïMéÂw;_{¶ïøjwRgå@ñ&ª¾ßL“NÛž=|x7Ó+h µúóxµ-»è»„¯Y_¸ÁsGÓ0„¥:éÈÑXëßaÒøí‘i)M±fNÈLq-4чÚ\Û-8?Ò5 ï20 {&´`\o0Þs®å? ¾\Æ­°m…=讃;&SŒ)žò¤y÷b^u ÏÃÒ6[„»‰òަƒù·ÊÀË`ŽH<\pç>t òÝIEÅç‘É‘Ní­êÓÚB™­’(%% Ç(¦PJ*0ŽcÔ籋O|jLøzómɺߴºS‡o¡9²caÆö8«¿ÿâóŽêªÂe‡;í83Û(Žiašœ¶ª£KçÌž]C¯Â"/ÍNnÔ `F–WIòjI®|úTD+qtN6ë«+¹®Ù `Zª±â³O»dF¦—énêjOŸ¼»ð,ñXˆ5¬g¦Ž³‘xÏ”Ê:º¶ Œcž”5Ƥ¤$j×:™†tJÁJb•4{;Ê?r=íÙ7ußYT^Ñæ$;èn4Ýòòþt´èBhÍ÷éZi¾'(6}»¨èœ€ŠUŽF,¯ìE©¦jÊŒ­¦öqvêo§S‰N„àíØ*R““(--†DR)­U*„0´_àx䉩kZ¦ñŽ€'ße݈mžYs· ÏUåå%¥¿ü¸ô£Ötú®ó…,Ø á˜¦iÝŠåo””–‚(¦—; L¿a4`£­ÐÐ{&ÍYVi³ñsã5 žêsbŠYoa@%æÆ©m²•ʪK)¯¼QþM”Þú ßÙê‹&¤ï]‹N®Ãa?ꚆèÑ`2L/Ó¬!èÊj›†}¢ldUôþÄ=¤ùó1rA‹~çý…Áà?§í]ºf¶¢ÃEe´qxí.'5`ñŸ0 .3V^I£¹çÕуŒÄ :RZIG*ÌT^Õ“ŽÖ³‘-hÅ*Njk§dŒÈ˜ðû@¥‚ù²;“)-e!â¬m¯—óR—Ó4ÃÍ݈…7ß“ÌNjŸèšÒ‡†Mf}_/ W”|’/¸â¹_îD°æË—· ù2ô,hy–uS¨û( eÀƳž ¨šrgÁÅ0 2>i† AÅ/˾û%­MÛÙÔwÀuÔõ2íÜÝ ³ÇùØð6Çš/ ßßS{*»¶üö¿Ÿ}½‰3L/ÓÍôK ¼Ý9mŽC…Eeü; Ì 0¶bÁË•¹ª² ç‰VQG»4g¬èK¶ƒ­©Uê\lqØUCAxn<çÇCá¼×±SЉzBûM¬Ùn¡¯öÝL?ç#PWX\¦UWUÖ÷:¾…ãÕqéWWVƒÿL|]eमmu£[ö#›½yxÏ´›ÿ,tXHÅB˜i š[øò;‹Ù5 Ä‹˜ZWU#ß6ª²9¨W5ë²\ÊÌâV3×›Ú+»[5Ú[ì #%è¬t:ˆ;päávÆÝØw\ œmʬ)ÇÛ4\©hø;髺yk• =Óꤌ$ÐQ#lY8²àeÃü.BV¯§ºfAl­éˆb߯󾌩puñ½%×ý:öÁWïv Ü9 0ÖVX¨±Pç=±ß~úá|Eƒ½Ð¾'_y´W ¹w e–íǧð9žóåagÖ|wlÚ0káÇïóÐ3k¿L'ÓËt3ýþäÁ‚ë9´UUí/©¨RÊ!,ãÑ`øë\•PÅ™­Ð€R^…ƇϵÛÔ† bŠ+é@EkÌEÝ¡ßmð³Àf4&Üa ¾ãƈ·šðjW^pÅóÑ=ÓTêÍ—‡Út:õýŽºvÙ½ „Æ‘ñ¬,/Ë$žp…e:ƒÁ¦·¾2ЯKì UhSžæâ½¼-Pò_È2݉ßâˆÂXÔÊÊJ\®Nè±cý\š±ׇokÖð3;®/ ñÕTT^MѤ•Tu§£NnÈ©P4Ѥbn7Bâj4\²ñV+®kšEÌÑŠz‡9l} åŸ;ÏúJfÐÕWß[jݯ®Àæ;!€…ÌÂh.qæ®çgÆnVŠ_Fsz]OKvk= 6+KÐY³ópðïóå­F¼Ú™\9ª+K~ýî›·W/[Ìšo .¦‘/O X `RŸÓòöìú>9=ý‘uÛöÑ©ý»×çϧ÷.­ÀÕÃf Øá+Ž3ãø{RL%*·ÓQ ‘åãØ3‚S•r2™‹p粸c3ˆXárÂïó5+Ê€VÑ1•çëôE'ɘÿ NX#à9=¦/PÇ8b(RÙ½eÓZÄÅ…O\FÌð‚&éìЭÇMÁà?X_è†Xjœ…¶çS^iwÊG‡(¼gÂÅ!tYs¾Ýy‡°³ÆYqªP….€mÕ|°½Mَׄ4äÀay°…Ë0kJMù³£ÞTbä(>¾Ûù*1o\M˜'¢ ›ÃÒª8hBx6â‹+á…öÍJÖhõ½¼º†ËÓ,<¯ËB–‡¡]«›u˜Ó×/×jg¦ËçÎ;ÀhéuÏïµöÁú§Ã÷‰§Yî,µ»Ö *­^ôáçsg͵œpÒ ®]N8ñDkbb«X«5ÅlÂJÈ 8¬©¬*8\\QVzd÷–Í7¯]½Ѳ–Ë–5_À,ˆù™‡ŸÅü/Óog$ìY8ùû÷}°^Qîߺ;zun`¢1ˆ…Êscì¸SÍ•‡½¸!àÆ…ç†ã!„Ó±xEhì— 7ìx(é0©<'Í{5]ó]úœ/4`Ö|õ!hhé.í·ñ½Ž¾ÐÄø­ß¶WÙ½m3Ÿ%ÇG\žxûU¨üxÒ!hs0½ªÉ42ügÂ})Vk%¥£ ðÐ,/` Fh.þ»„Âb…´k*†GXðê µÐ!eíW”qÇeUCþ­ `„©Ât†°ãQW¸¾T×Ì%³f¬ØŸ+¼Ë  Vþæ¡fÀ¬ÇÇ»1 eþÆ~˜7\M\÷àñ…ïÑ^÷÷íÜ>U@Ô-ÏúæovkÁ NnsAZÌËvÍ¢bžßîÔ¶üogåß1Ë–Ø-ë~Ùˆ‹…? kÑ£ D©búÙ1 |q¾xn—5\´bÈ™5_¾†Õ~A›á4`¨ã»Ù8ò¾‡núßüŸÒÆÝ9B±v8WD|ìD#ÁÃ^ÜèschPxˆMÌ‹y6*z þ‰ô„fÁtˆUÙ<4ÆsÓ®ÕŸ08Í—¿3½ºj,°~8OÝVòýs">‚áKTFÆÛÎÍw£ÓÈôvîÑë"П þs&›« 4ÿEž9ß*F^XããrÍeLLÃð¨ NOÌô²FÊw¾ø‡«¶U“µ Z4±k-sqgEâÇŠ§Ç\×0QÇ\\žïu­tæßžs»œF(]sñóÄysááUwÝÇ1@EØ;¤»Þ;³ûõs^ÐöÐ (¾™<õ`RÔÂÿí*¤´ÚÁÂÏŠ‹hñ57¼Ü€s£ f³mç‹0k¿ba a>ýˆ/ÌüýXÅ£8#jÀ,ì……‡K7®úùeeØiϾ3g™vÏuçÄ8Q¸èϨü̘‡ÄXøê æ¶ôa¹šFE4LhƒšìD»"Ò†F§7J¢!b!ìš‹u­EÅWX¨cÜöçÒª¥‹ÿ‹y1 ÃC1¢2r®üÈY ”Õ /hÐùŽ/L_5ÓËt«ç]to0øÏ) >„« ŠtÃÍγp\¦Ø±`ä2-4V.×¢ŒówQîÄâ+þíò¯¹ê ʨÍj×ï±õ®ø8<;ÃM¡E3ÞBÖ‡›ñé4¹B…î¿À?\|waàÊH;ܼuÿ×—¾YZŠ}w®zÌz¯]Ù5?ס)ç©(D&Eqî¯pŽýjoÕz¤%„¯{Þy `8ÑVñÝSs›Á‚Va¾óoÑÖ±_ÆqFÀŒ Å…Dô‡ó~Jn•öüþ#}BÚí#ÎRÑ„] ƒ–s5B æž<Ï‹¹æ´xxCjü‡ÂåÙ@–&9NS¿Ðñ7Bbñ‰Xtâ©4):<³æËpåo;”më×Ìùõ‡%ëà ¤¸< f1„ýU-¾ :™nì/äÞûUÁà?ç*Üe 9øÏùlÈ òãýMUy?±k(ÛÂÓX§®ùòˆ¢ŽðÝ_”{Uq E³ v \—6.Á뙑g¾súLS´×ý›Ö´ò»oV‡ ×ûÕWd\ÎÿŠÔ[tÒœ”}÷÷\WYãåôø®¯ŽÆ…/_,tÅ…G¿ Rq‰‘—6À O\ÚÓ¨ã9SvfÍwëú_ç.þìão§è²ÌÏŒ¯‘ çq|}L«uÑg}ƒ.sE°ø¸jøÁqhË€ài¸øÏy …ã|¸4Z4¸¥ñÖ =ëûu]®ϱߗï@óæâCèùÎtr¾Ù…‹÷žuÿ÷k?ÅÞÜ/<×£ ÖûU4 b@IDATWuìétT¿ƒ&RÏ š²/žr.}•Í¿…0ÃΞÂWÌ«RXø²ã6ƒŸ¹—ÐðEÛ&üÂK¦ðYŸû5`¬hÊ~1$PBj @¹÷Â…†‡Nâ|øÞìÁg?ä<ãœ;ÿúŸù)ý{vÔ†õ뮜Գƒßû„¹rˆž¹k®ÊŒFÅshî˜æëÙ¸ÔÐéóMTBÏFˆ+$7ü¢1~|ŽÔË#ïóå-;¼Ú™\9ì¶Ò‹~´nù<$p,Å3ãÉ¿Eï0t‰4ÁÕÉw„ç!, :‹ŽäÈ6ü‘àr0øÏ´…£ Þ†’ÿœ—p:[8Ó fZ‚~®ÑV÷y½ÇÊ%‹þ·æÇ¥«×÷ Öû•#ÚÇkNÛǨ°)ÌtµvÆh±£²³³]ÚʱQLncXàŠy_ºBðŠ;^5Ù‰6‹ïââ´Å%Þ59â&`¥seÃç=`ŒwÁŠNÆ}S¦¤½ñôÓGK¡i/<î½ ˜ >°zéâŸ7­Z¹í´K.¿Øá°‡ IFEÒ’¬±ZjJ‚g±ÂT7•ÞÂÖû·Ûc¸Â{:ïßžßšòÌæÙb­ºàÝþÛÆe?-øâûÊÊr±1äQ ¾óå©3ÞFpõòÄqU¹3±uͯÛO»øò3ÑÁ8 üO ÿ ož{ÿf?MqÞüöþÝ”¸¤ßÐ!àÍgïßþ¤ìÍkïßþÄÉaêªû;6ý¶uÿ»ŠŠ2^’z¯iöi{5Gk)•¤š®0g·g»/ ÈÜøy6€žÏœœg»%Òó'¿Â°¬s”Ù300·Ë¯<,€±­æ+»Óžë,w^xßõˆÛßGf%ð<÷àxø™‡1T,Ì¡o?5Ïßô:éän]zõîcMLJÃþ²d³Ù”ýeˆ;bœÝ›aå%å¥Å…»¶lÞ²mÚ= ^t`XÓåJÒAg½|Ç7vNt*‹?ÿèÛÅŸÓw=û ìÔå„Þ'Ä'&§ÆÆ[“Z"ÿ]°Èÿ-Q÷+JKŽîÚºióÖõkvV\BVïWŽÈxXsj· ¼Ñ(?0xÎ~Ö´ra Œo,ë0E©š!û/½5i†"ÖÏÈɺ,P‚jÂó° ^^ÆÎ§‰'ãJÅÅÃ<öž€Ës‰»çP>µ(Ç\ ¿pÇ…‡~XËåÎ k¾bCºèó7ö#z¨x4Œ“|7 +$!G ¬õ~åÕ§¢k¿£¼° Òè¡sÝopŒBBÞ˜¬œ/1AÙfÎÄN&°¬ )¯‚ ÆNÈ9gznÖ’@‰BxOmHt¸À 펵c^ê®Ïâ®kȸsÎN„qýŠÎÿŒ;!|¬Œè³ÒA';Éwò¿D >Â^ïWßЮ£Üùr _RV¥tMzŒèP}4Fí{–qØ;)DÌŸ‚‘É ªìì·âöÛ÷nÆ4d¡bisúŒì±<ÿ¨cÚ„FÄ‚6¾æb˜5`±×Œ5eîH!”øC²´¹#Hš0ÓÉš0k¸¬íòÅC!â.„/g¡+.œç1cÝmÁ„%ëR‡f™ˆD@" 0&llÃQ¡ýÔutQ¨˜,ÊÈ^¯ocÒ…Þ¾#D@" ´P®LÜøÊÞÊÙçŲªÉtýàÏò>j¯…ÂÙ¤lK ¸IpIω€D z`cØKñ„È‘¦(Ï ùüÀ|ñ[ÞC‹€\„Z|e쉀DÀHcÍÏ©7?$‰€D ¬°± Íiû[otKWll#ÞskK³tVÐëHŒÍ9J'H$-¼Šêé˜÷Õ-]aæ·’TÓõý>Ü[Ђ 0DV¥l6H"$‰@xX9"ã!ßÛÜ©©ôàÐ9ûW»ˇ°! ç€ÃµLH" 4/ÒØFóâïºÔ€½‘¿%‰@"°rDûÖšƒ>tÛPhuŠ4¶Ñ¬œ–¸Yá—‰K$Ð#ÀÆ64Íñ>iZ'Wj0¶aV¯—Æ6B}C)HC ¡#¿I$(@@Û0&¥lL¾Hª$‰@PÆ6‚cH"‘‹°B«ŒT" 4?+¯éÔƒlU«Ä~_œqûåÐ9¯û}›Ÿ7LÔ€ÁI…D@" *llƒìÕŸá[—±ÉÓ-§>7ÓZsCP“—‘ù€€À>€$½H$HC€m`ÅsÃÆ6ûx‡Vùcî›æ "-Ñ@¯ÀÑÀE™‰€D@"à€¯Æ64•Ô1ŠÃ1cÊ õil9-éc¨¥5Â2~‰€D@"Ftc½(’TI™>lÎÁŠßžw KWšUËÆ8SÒ0'içN™©Îž:=M·íéO>‡ÙÛ ®2V‰€D@"vtcšcµØï‹ó}W§·Ž?³ÛÛ;+#FÓ²Õ)3Ÿý3‘v³¦Xn˜0ºzmcaä÷À80üdh‰€D@"`ØØÆÊo|z¡‹ ¥ÀlQ† š·“³€<óÙ°üšñç”ô—§GåßÂMžiº\s:g¨ŠéŽ§ÇØŠ÷ò|ätð1•1J$°#°zå?¦á Í×iR•[…ðeb&¿9éªFz¥ŽìeU“†bþ·ÂYZ¼.wºù„)€Ã³LD" W¶;E!m)„g ÇÊÆ6†Ì;x¯g o½Õ5n¿m×7Ц¼«)ÚS&Ít븱öeÂϳí= Ư |mËÅ{yR‡k™’D@" _mè¸z×WÆjçNa>ÃAŽ÷°Húå cœ¯L„Œ (He$‰€D ô4fl£! rg&d(Zù¤)y)ié÷\Âþ&ÿ+¦¯f³ýG±$\:þ®R,È’ÎÈHldîHÚ$ÀÊ+Ûþ[ÓèvWfë7¶‘;SÍ!'Ùc¬m^°U¾Ds:ƒ@ÆÙÀ´D#e®ªZæ»»òwoí¸E€™4‡ƒæû&Nâp:.AZ]p0t{–¤p¤+ÓH$MAše 4Ëý³ C½_½1ééUM ï_6¶¡iÎá‹0¶ÑÚÙnòåÀ[eá{d¶.U´²çH3ŸƒÑè$…œWjNÛh_}—?´È0áE dpvö[qûlû$E{Kâ;*(8‡ò ð~$*Ï¡ /Ÿej‰€@'B·G»•T…v¹íÖ«,þ–}§×6 "lÄKcÆ6ê >y¦i$iN6ÈúÔwÇv¼U—?ùÎø„DÉšt1²>¸3zcóqýWWç½ñôÓG‰¤P" héÜ7eJš³ÜyædGẠÄn`2zFÎÄÁÂÆcõ¥#ß cpdüXçõù‘ï@Ðð˜‰9jNzC"ëÑ“|xzn[k‘N" D$c'䜃3ÿØ’T?E¥'fLÊz%ÐŒ°±•+ÞÀJeíBW\JÙ¢ 4;o§/qób+²ÙæX¬müéöƒe¾„‘~Œ‡@P÷»„¯ö2¤ú'dn{†¾Æc¸¤H" hÜŽ)–6§s»¦9µ—¹kZ Çû^µâÉBø"^ͤ*·z _^ñœ;Cùâù·Úšhü]Õ1²ø™­"?«®ïò]d 4 ˜‡5Mù‚ éôœ 7¡p`:E:‰€D@"`(Z›•û¶ëм]îïp4Û@\Ÿ!½ýÅ¿g†Î;4É%Nkê SΌžޛǵ/óüÎÏý—µS¥­*kÂX}>Øû³üEæWÈëLv&K›;¤ðÎK%&! ·khßh®™5í^“â`c03ù޾<Ȱûs=#A[¦Ìˆ9åé±ÎI*™îr’ãÃÉ3ÕZZ÷ ï$¶­²W~k‹}Ö3¬|Ž,‚"€yµ³¾à s¾3²Ç–G’Z‰€D@"àܾ¡Ñ|ˆÛ;}—‡oÁt_llƒìU#l*¿`cññ1£„¥«ÚQÙgNŸ>ÄòôXû‚Sìiú5yºúþßgµIœ5ë“­²ìX=õñ?Vì«NþŠ$‚"€y«z‡óåœo$±^Ò*øƒ€>'ŒöNßbÙ„VTOƒðÈA |«RGzZºQ=='!)´çýòüî‰{*w%uêq´æ¢¢£‡—o-üxbØíGÿaä=2X³‘ ×>_å¿‘ ¤Z" 4 (ÿåvOoÿ|ºêÊv@øÖ2¶1dÞz|¨ZÜ}ð??=¶Gϧés½ªú¢BZïLKç}HVz18 `¶pÅF6xŸ¯Áó*É“H$AA€Û;n÷j,ü5'ÛÀ¼ïK„÷Ì¡s¾)~ó}ѢᵬŽ[±¦%síJõ4OltãéÑγï¼Óûl`O_ò9RX#£]ØÂ•4²),—tJ$" ·wh÷O—†âbcØeôV5ǰ?íÕé­­{†yþ­Ø^?nù.s¼³rg˜n|áŒþ>~ô3¯CËNš:ÓtLsÖã;L<ñ‹äç€0Ûvfó’‘ ‚¤]" 4n÷¸ý«/ÛМ˜ÏÕ´N.?Jɬ^ßííÚÚë“wVmU“{A<σR}›­âÐìþhꛓnPÍæ±Íùì‹Ó“ È¥‹6j {ø“9ìaヤmgÀ“a$ˆEm·{õ,ãmlCõ2¶á™ñ§G±™ÞóõÚ»­’ËÊ‹Fण›qHÃ)xWU­”ÁpÁ%]!°Ž",dV$‰@P¨1¶ñ¤Gd<'ïKßõ>>|kA1>ò¢Öÿò¶£â¢ؤVòçÙH]D½=!ç6ylú¤¬!úFú0ðïÑì—S+ý¦Mÿ}°“kŽü;¡ŒotÖ¤eŠ¢¾=cÒ„Z‹\B™¦ˆ{ôÄ)çã/Ø.Ò ÚÑRÍ̘4nø.ï-ÝØ†½êxcó²› À7æ³–ýA“Ê@Ä à1Y9_caÃ…Œ2¯Fä…`X¶¿“ßLS¯·³³o¬v}SÚÂß pr£ÂVö úªÏŽ™:5mƸqEÁLˆFósïÄÉgbuæ2ì%ü†ãÏ®«ãAþ„Ä)3r³†øC_v¶¦fg+†ë’#O§V§ý܆ò©—'ÒbgæL<§!õ}“ýbkÍVù ÊàFUQ'aËI†Ù”´£>ÿ¡xož†P`ëkœllã`EmcÖxK=Æ6|Uú‹V"Vë QhLµ= )`VU­NaºØ©iÓöi[†ãûš‹iªbù4[ùô ß&çG£3ÇN̹áþÞä°ØgÏÙõPös§¿žýÔÞF¼¶ˆÏнb4ß$R,£§7ƒÖ;6kÒÝûì¹§ì{š p#ÐÐ\yéújlCø—÷–@d `¢‚é9ã¿ð`á  ‘ícÂØ‰SŸCC¸Æã[ØßÈyj3ã«yB³0w4åþ 9Ÿÿ#7kO°ˆ¹wâ”A§½c°â‹†xpv{gH¨hoz v‚Ç…=KþW!Ñü°'ì‘ hð 'ìllé9ow'¬ÒƒCæÔolÃíO>´X"]Ï8³ú Ùœ§½?>ꘇ¨ïŸ:5uÌ„œç1Dx.Þ§a“ûz³¢>óƤñK9|ûHS¨`fNÖq+ qüØsØ7xFNÖEº_œüD¤^Çljaƒ½Þ?cüGœõ÷"®?¢1z|F΄“=‡ïÍš4ÜIÊ„€xø‹ÍŠbzdú¤ñ?òÐÝ~[îm6¾iõÁö†Ä»YQ•ÜéÏfÍát›ê,fó›Íqžè „½Ò—ðc²§Ç“-ÿ9Ð1ÒÏ=kLŠš%pBF;5û³W•Íö5:<6äý0²½aºà„˜k=Ó GìôIÎñÄsµ˜×RÎÌ™ ï‰ät5Gþ$ j_ uEœ[àV¦yü žCÝŒ-¾_ÜÞœuÛ>Gî_°#òàÙ&¶mJÛ×~¸Ê3mñå+S‚ú´ç¾zÄQoy;yr;*×f‚¶ËA[!üòªY@¢LÂô‡ ¥?&+÷O û ðl¸Æãò°<}߸ìèÎÍ#M»ñ÷D=sŠsÈJ“¦Á÷tãocåSÐî»/Æ6ÂM“LÏø -ˆ.§Úº¡s‡¢ñ&yݡѳÛK‹Ñ¸¡-U³ÐxüB®½Ãéüê 9]j¼ý æÎû³Ÿ¯5¨Ÿx¢i÷ÂÿzöLJsãö¥FN«ª(ãa}"f¡"î®n£(­k„lMÔ71÷V'Ñ7xá„¿?#ì¼*ë“r€=±÷£!-Ccœ :Ÿ&ÍAߟõ\wDMxPI-‡„+ÆLÌmtH~ÌôéÍ–Ï ÇF‘JsÑÿòow:y&÷ùÅK!ôþ†oOÖ'|q|pÔž4™”ì¦ßšôúgÏ“ƒ&ƒŸ³PYF#??àz°°Äþû3 —1û W%ÎuÍåËìÔ~åïpýàÿRßÙÀsü>®ª&×jX­zpðýJS•»àw.h½Ý^æx]‰•7‹¢T#Îw |±J–~e~ð…4W×ÄQúäÌÄI_‘–ç´r6C¼{èµ×bÑ1[zyˆ{6ÊÕßi(³{§Á÷tÀÿzùëKùô†ópÒ#ÓŠ¢Jž5k–iáš-£ñ)L0'®t³A£4è_`eìÓâÝØìœ…N›¶ÂNÊõx÷’5^ùwE9åØí¶QøýWáï€ýÀ• É‹òo~‡^ÿ…¸t0gÝé¡™ýKø¯ë®k¶üç¡u­øz· &ªµRwfnÖ©žá!x—Ú´êvÅv ÞÿæùÍ—çj{µÌ{Ð6Gá4•W°PhÁŒì'×VÛ›?9<ÙlQý#{‚ ÿ•³“œÎçî”™“Æ7zbn&9µ‡cÍ1sÄð˜‰SO¾EµÌ^ɔٜ†Ý¦]ƒNÆJ4æ±UÀ ¾™búÿ#ç)‘ÏOïÍš¼Â¡9æŒÍš|¹çT:]ÑYÐÞÒûL±ØŽãñvètÜãtjˆê¦=;á+ïï>þî“ výûøñ¬ù²ûZZ hàͽ¯Ïþ³0J2†áÓ&MpuNØgCù™€À¥ÐˆkÑ-øxáK¸Á«¶Š¦\)^4VÞjèšM÷Y”¯íMI_¤á˽*¿ø>Û~&Å`Ë}4;{‘OÍ~è 4*™ûá+’¨÷îFÊAh7êõÔÀŒˆêüUÌ–û àpZÅË xGöy8WùUäUøE£2ÏCt-H¼ôºóvà™§c¸ø„4¯A¤‹ÐkYí–°Ë9ç±ßc[t´‘Ðľñ¾º¿i9ã¡…+¿c´áfÔ}×”¬ú„¯†„Ç>“s5:Ó Þ_—PrÇÓèƒ2ÏCøê¾=©–ä©í7 ðûÆ×FU¡¬¤<ñ º9BÊ›7-MIß;¬øÎÅH”ÃÅõ_á-x÷:øHù aµc:s÷¢n( Ü)Ç` i(o£ÍÎÛYÛ—ü%¨ŸHÝAðVËD¯ÿh ¨œ»1lú?5Ž^›–5~Ÿ7uñ)ênÏw,±õ¤•&F¼ÇóLìgúÚñÐéÙY+±(…Jì—cXl‚ðMì#h¹“÷Û¾se3âÔ7j4!á­ÖC‚Ý!ÈÉd‰ÙRëƒ×Ö"²ß¯½!¸x‘S"êt:æuAêåÝçŸ3²ÇíÆ Õñã~ ÷ÿNÏ™8_ï ž w4ÖVhak½×4VVelRo›ç7ÏgðCìÚp~7&{jgÍfdRè §¢™‘Ô£fMêö·œ‰; ÏCk¥k¿ìßz€žOø¹·>zz¿7[Ò„†îý‰#ì á;Mâ*Ì=Ï8ÞƒïoÐQ8~ñš¢…ÚCUdw—cTÜÃѵ¼=öÒKÖò‚²›ÛyÀ¾+4ßt:L_lŒ&ðÝŸòÆáj¹zү姑 ­;êÁçx Úçºøhù q5õ:²9½Ï‘õܵH¥l_m›_d"PÃn€,¯ÀB“Ø»Ù÷˱çõ©iYYÇ _¦ó…'ž(oŒÞLsÖ4¸»5ý‘ý*¥¶ë Mf³å¿ža1¬›EñJ7þw!´­*wì¼wBÎ(O?žÏh8âùw Y*=ß{>cóaÍi[‹v½úŸ*ªú”jV° K Ê>bä [‘”Ÿ ̧ݟý÷D)›gúüŒF6‚q]=_ëR•qƽÓ§4¸Ê ìÍV°f¨ØmW#®‚væ³–´7÷^ŒØKªe)AB'C躰NGô¸Þ/òÂvm÷÷?ß_Vûͱ_hïÆ/ ít–î;öů§FËMc±¢sq­¼o¸ôHù¯˜c~ØlÅvºWÏX”§zÇ×Ôòæ¾®ô½ý÷[S,^ïâ±–±Þòëå×÷ŸÇ§£‡­‹¿–O߉jÜ'Û8sï¢QÖÀ2ÝÍ2äþœÆCJcD¸|,#Áxâ9]h´Ü>ˆùäG1¤ íD[ðì'yK-7cÂ^<5 Š&WÙŠßCCú*}X×°(„úî%WQygÜj s¤¼i¿mËóh(ÀJë‘üŽ7Ò¤T¤«ã®÷þüç¼=0qê=ÕšmµÍ^8B¾‚­VTèPlC#W1-gÂÛµ>øøCµÒBG…¦”Ú+ÏBÜ# hçfgó"l¬^Êšô%V,]\¦UìÄÂ.Õb‰9&€mw—:“q­npäÀ;òö „Ö㘖(ÂðõKØ’öCsmIó¦Mü†ÑŽûÑA8AUÕ‹§?;Áæ—±&KKm×”òV;dƒ¿œ(Çiñ.£"ÏÒŠv€G\~ýq>§ÓPä–φânÊ7alâ‘Þ~bÔh§4¶Ñ¥_@¤kÀ"A»£ÛÿðôoÖn» áùÐÿÝPä¯g?\ Aóuzžã÷~uùUbh%*i•Ãî¼µ®ïù¶-JÇB%æÔú1vW%¯õÞÏŸ4næ§B“ÁáàÊïhÐà~‹Fî"•áÞ^j~»VnW;<=è£ ý¦’ã|`r6æÃÜÃÊ&Mùø9ЦÏÿ®«Õ©Ñ”åHûb½Ã᡾°Kƒe+•¾õxÝè#:쩽¹ýãèlq:í³t­¿Ñaõ€‘*ÍTÏX$RåE„Àóðõ»úʆ±ÃZü¨?–š/вå.…÷u{úEçà2ÏßüŒ¸aâS»Ä›Gžþꥡ éxÆçýì{ùôÜßžÆ6° ud¿÷êe.¸)ÉØ¢©{q˜ V@[›9¾  –ZÚ$}æé…÷)b F¬fR–$PÜ¡JGe?»æ|ÓakZì&O¿â™†Bÿ¡Ç ÆŠ‹2ûyR`µëÌ/nc·f¿=çðÿ2“bÉ·k¶›ðûvî#h”ƒæ2M'äî³o¾ ¬¬VV׊8Ñ’‹a÷Ûl }ƒ•ǘC5¯Ñ¨ªf$±:Ûi‚V™Íþ/:©ç²¯×n.Àªçÿƒ°þ?¨³±~:q¤Kg|jüœ%a^¹½ÔмÜÎ[Ä{¾§¤Æþµ¸°j„f¯˜ü?j¶Äls:mû?ÿM™¦3ßôôïësvö•c²'ߤٜ+íö‚7î6_ÆÚÊËJt®îØoÿaæËßRö!XÁ'$F&Ž¥îkyƒ0ÿ ݦbåø•ÎDuiBeŒéåìÇ ™‘bÃBqÇÔ±Ïä>¯Äh›UÊÙ˜žø (¨5Ü žäÚíUw`!ßø}JušwkŠ} ÖtÀÊ~}q_}44%c9¯ãÉÇòYGÈ ½Z9¢Ýý(÷·‹W´?}ë3>]%~Ë»D )H ¸´°òt&4,HR?ðÞ_ áëÀa¹šÍñ[©­ìˆÝéX‚-1 ˜¯½âåǯ¨#:ý G< Á3mëã›sö Ès¬ÓéHàáahm7àÛ.xþÖ®U³ ¥*¦Ð Q_œþ¼×‡È5å_fÞÛ­V㔡ÎÇwZÕ‰¯0Œû¸gZ7Þx£ëÂþm¹o5i;aÃ=D “ _C€ð¢­ùžxðªthHKø›IQÜ‚™ã}ñO*Ã60Þz³q~f³U>³‘÷–õ,1ŒÍ~›êfdߤ*ÚCÈÇ­Ø>uWSÇÊ{óYÓ1Bð"ÊÀ]X¬¶Ëé ÿB(oW)æ"Ï4}-ofSÌûŒûÍÒJl…e¶rð¸aÇÃò°ŽíEÚ)N‡Ó´»Ñ'*f…W<¯ñ Í#Ø&v ÊM…Ó¡}Éei¡ž8Oþ꣡)鈸êºûZ>ë Œwll½³ÁñíHíu`MÛÁÇMO#-GË@õ,0Ín1Çíh8ߣÁ!O³àÁ„3ØJ•wžØjÕQËä6v›’¢Rbž>,èí©ßf?×^!ki]áx‘RUl¥âi ©¨BöIßS½vsW²ªåöêuHºu¤voö”®*%þGö¥u|nò+Æ6ß–ÛéÜ'ì­/Í&Gjð¼bü¾Ý÷՗ߦ”·'Ÿ{.©¬ØÖêï¹YÜ™óÉqü‡l¹b, %ÞÛ÷ꊀÓäÛöeü-gÂn¬[8®#W MM§®´Å;_˧ðèmhšc5°>Ìmõ[ï+µ)f-šÚ¾@q’ᛆ€Àuà…!èyÐH;ceõIu|–¯$„€–­®\ñïý¾³i‘£•†ü}àƒoño)€éüAÀìO h Ã=û¶}÷cn–íêÞ­ù”ù’H|G`ÕÊ7r _Òm œ“·cÌ@ßã>%u! p *Ø~„…P{{¢{k‡ðÍž£› ¬ 3ùN" h!¬º2cŒ£<åή4¶á†B>Ž€\„Uƒ¡I5ÝãWñ©:RÊ ZƒD@"ɰ± ,P{“ÜbªNÛˆd†v©×0%l6n X$I‰@m„± ,ÆLå/˜÷Ý%mÔÆHþ ©Ž¡ŒA" ˆ2l«¸„/½4´OWõÉ?^iêÕ¹]d#b꟧î¸Ò4´O7Åd²<Ç“Y#I .nlX›ŒÇt2½¦ŸóÁù‘üÖ5/ÇÊW)¼Äí(r—{ocøò‹4¶Ñ¼´LŒfJŠ+†Y%% ¶CÚOøûÇt¤°C³DɉñÔ¿GGºå’S v†. ›v ®™­).–:c8ÆÓ¤¨‰ †‡xÚH«¡u¾×ÐËt†šÿH«YÝk¶Ò7?o¤}‡ŽRjRÜ»3¯Øõ™¦9K~!FBc¯?_ãk™[±a;ihßn>§iíÅ…´ý¯O’f·é¤[»õ¦ßû]£¨;óøÀ#•ûHƒVÒdŒÔdË— có=äèO¼‚.>­?ý°f ­„v¨³;4í£o©°¤<Ш‚–W!|ÅJè°`Ý@f8}¾˜ncjèÄctºªjÍúúgØ«={ßõtÃEÃèD˜mŠ»óê³éžk†ëAŒ\æš’§ øEáÙùêª>œ§GgJH¦O¼@NÕ ƒèz»b”r”ìÊH"#jÀ&Ô”°…ä„8êÞ¡~­Ù²CsÛé´“zè]¾~;}±ìWÒ êÖ¾5ºütj“–L•Õ”óæçúo ¡ûýçì%”’GW;˜^!UVÙèå÷¾‚*¯Rîƒ#Éf·Ó¬?Ó/›wéöùÃúêBŸ¿÷åÔ1£´ñRZúËzঠ¨GG)…Ó æ97B|¥Ætš@_x:`H³YÜÃ…záEÚ¶JÒ/&¤¤¬’¦¾5—n¿òLêÝ5“6c$å¹ßÓeg  ³N>A±yéݯhìÈóhÊ v ПRg™ãø~\»æÿ°–ŠJ+¨sFºŽß+ŠJo¾”ÖnÛK=QÖ.?k uE·ÿýPñÚŸÜYéúpÅ´åzºŽ$=£”{7ò¡e"`¤‚(4!Eƒý¸p²£ ¨”öäP‡6­ôd·ï˧·>[A˜A·]q•Cè²`å´Ñø¯¶9Ü$—–ë§ wX¿núûkÏBw\u–þ¼dõVý´¢;FœC#ÎDŸ|³’òëߊÑèòpâ†í{銳R»t6XW£Y AçÆ;4©ù«':mÑ®wÉl£;Oÿdmø}¯¨$tM&…x8™N¶"FTÖmÝ£ÿÞ¼+J+*©ÞàrSR^Aõ•¹ß÷¢ÏYFÛµ¦;FœMý{u¤«kQã¯5ÁÛQ¶¹,±l¤ÿ+Zµ”ò>ù—;™7Ž¡”Ágº×´+žåÍýM>Hš£iÀ¢r„ Ö8yNŒ¡~˜¾äôþzºË×ýN‰ñ±º&Â/¸ázõ½ôûÞƒÒiõÒf2©Ô Ú,;žn×Ú%L×oÛCÐf¤'é¿_½iÒ;I÷[Ye§œû/§Ø˜°Ìs§KàÌw#8OzŒÔ) 6¼îàé;¯ ·! ¹c×§[{ºÂ0=%Ïhëîƒzºwì§s‡œH Ú wüø}¯ÎºÐ„ÕWæ~ÆŽ5.†îºú}ƒðÏw~ëåg诖Ñg‹W#~ÖŒ=}EÖsÕÁ½´óõ‰n¢“O>2GŽqÿöxeÍã•|”4FlìÂÖ t¼äŒ“ôÅR<œ_£!-.ƒ–á¤Ì–N¾cw¤ˆQïÐŒës0ƒG[vçÑOêSþ5O¿ŠK+‰W± ׳SÛp _NR4@aÃY䳑»Qéj„lÿ>§$ÅÓ#¸˜î¹ö\ÚŸ_H3?Y¬GÄCÏ;÷¦”ÁýùGéœA½yî’v8LÛöÂ\±kÚ£±T ŠK1ìܪN¡Ú¥«'·rã.wD \Û=xÎXÅ03»j›ÍýW²¦§$¸{>ð¡Ùd¢áÐd®=ˆç'÷søl]‡whßÁ&?D MÎØqx5r:d~½œ§¾‹W7/[½Y_ÀÃÒ½»´£µ[öèÓ}ºeG]/­qô[žk(Ûû{lLtU{6¶Q±s«žMÅCݱèÊ”º©o<åo‰€¿In¶žéXÅ ÖG Wê8²6\XR†m"¿éÞ×dM¡{‡¶º¶Œzˆ÷Sa¥óÂå©¢ªÚÛVÉÐ:]ë-¯¨Ò5‹AØb²|Ãïú¢ÖˆY`‹9`wÀæ{h6Ük²ÜÜé‡y^\Å#"‡ Šõµk·ìÒøñp2áy[þÞCÓìzwm¯ÿNJ°êó¿Þ×UæNDX.Ã_/ß@¼ÆãcM7Úœ·±N£Ÿ¢ø}|Éf‹+w¾€"ý„£u…¹Rà ¯Ä ßõ £éKg :XßpÑ©ôÕkuÍ„çæî¹v¸{_ï%g  ÏÿBO½6‹zvÊ S°šU8ž/fm—W=¿ÿÕOô×Çn¡‘£ÿÌûž^Á<2õñܽ#Ï×].Œ÷ŒÝCÑaLºÞ¤ŒHS½ÄúÁfwê«“)Ò§X«½î‚¡îhyKÒW?®×Ë!¿äï³,¯UÎÜžñPW™ã2¹‹ g/ZE/\d êÝÅ3XÄ?{ÛH?ÿjj}þ5 äKoWDYkÀŸü$FÀáÉ5RÉ}àúZi :±3M›p‡ûݧô!¾xÛ LO7 Æ|ñ~κN݃ ®áf…Äðò}7\ 1²¶œm[,x¹[J¤kYô‡mîþ=¯ÕWÒóªgž¦ðtמ?åç˜@ÎlZ«l²_ïrSW™cÃ7^t •a$†0—9ïp¼íNl½ó¤ÁèÏuÛè|ÏSF'[Ò'¨…@‹ÀµPhà‡·ðõôZ—ðßc,ÇCËš67„ÒIÑ9 u•9UUˆç‘£Êa©.cŠÅµÍ*ªò*3ÕÔîzGuVeæ$h@ ~cÑ;™‡–„€À-‰Û2¯Gà8c7Œ­el#³'ÉoaHÜÂ.³+ˆTªë2¶qÃèHÍŽ¤["`[À’‰€D ^ØØÆï¾Û¨7ùA"`$¤l$nHZ$:Æ6ê„E¾Œp¤ŽpJò%ÑŽ@þ‚èÈâ¹îlvºçI_m¸Ãȉ€؈\‘4I$:º±·^t£‘~ÞUÔú‚kÝ¿åƒD ’8’¹'i—D1uÛ=.Šs,³ÖÒ¸¥q\æW" Hc‘À%Ic€H €2¸D@"|¼ÿ¯ýÉqׇs(¦­oG1ºÉ‰€ÁØà ’äIZllãÀ'ÿtg;SÛpc!¢ )€£‹Ÿ27ˆF@ÛˆhöI⛈€ÀMLz—HBƒ@Æ6ÎÅ¥|‚ tèC@ àèã©Ì‘D "8ÞØÆódJJ‰È¼H¢%¾ °/(I?‰@H¨ÛØFߦ)#—47R77dúŽ€ËØÆ_Ý(Hcn(äC”# p”3XfO"`d%E´ý¯O’f¯ÖÉ´vëM¥± #³LÒDÌAŒ+*¢Ò`€/á<ŸÅ»¦ÞE$üìù»©qIÿ¡G ØeÀ“ßFæ°óÍœj0ï¨g;^OÕ‡ót¦š’©Ç/b‰ =“e  Ðâ°htœN'9í>p„~Û±Ÿ ŠË¨¨¬œªªíDÇäqÓYV³€36ÆL) ñÔ*%úvï@]2ÓIUU½òl¤šž€ (¢ ìÚ˜6lßGEeTXZNÕÕ¶c¬÷· ˜ÿ"ßÍUöóÞŸ&mZxeøˆF Å `n|¸áq8œTUe£Å«7Ñ’_6QIY5„¢Ffµ‚Ts)TkKN{ ÙVš·l-%%ÄÐyCúÒð!}(6ÖìÆAILFâ\XÈ~»b-Z¹ŠËªBÂ{&ÆHü¯«ì/Eù/.GÙ¥ñö*²ÚJ(Æáö Ì<™bh—%‰ÊͱzÙOŽ¡Ó2ã©Ý'oS ÂñQû‘c(eð™ Ä"?I¢)€…àµÙlºÆóáÂŸÑøV“ÕºÚ¤¯Á}3beH¸ítÆQEEo*-HŸ/©¦ïVÿF7_|õïÙ‰L&UÄ!IXFZ .ë·í¥÷¾üÚ.NxÏ47ÿ½ËþG_ÿLE¼JvÓ‚ÍÔ¹dÅB‡ÂUAïNêFÛZõ¦å)¡ßmtú®o¨ö”1òžP$)ã”%€EÏßnw@ó©¦E«6Ñ—?¬§ËAj×v.ÅÅí 9³X°'$¬Ñ¯ÊÊ.TP8‚f|ZE#ÎHœÒÌf“Ô†CÈQþ¼f/Z Þïç„…÷œ­æâ¿È·wÙoUy„Fì^L™eûCˆº+j콎nÒ¯ íéûŽçÒ7=¯¦¸ÓN¤n¨“܉i™##€Ed³Ù1¯[aÇôÕO¿QBüJOÿ„TÅvv°Ào—1Ž¹Ú°kªù‚a}Éb‘CÒ¡`†( —o O!|›“÷œ¿pñ_äÛ»ìw/ÜFÃw/$³3üeŸþ5[?¢Å/¤¹Ë±016ŽdÙE©—q³ ‰‡Þ¸ª¬ª¢5›wÑ‚å.áÛ¦õÍ"|E¡`ÁÏ4°0˜»t ­Ýº[§“é•.¸0¦ë¶î¡Ù‹]·¹yϹ ÿ+ûèx²ð½pç—Í"|WYð3 L‹,ûyoI´Ì ½±æ[T\JŸ/ýUvfÍ×(Ži±`8ô£…+¨¬¼R§W áàq‡±¬¬´Ñÿ¾úQv6ï9—¡â¿wÙŸ³d ñ°3k¾FqLKZE}üµ,ûFቤ#<D½æá7^éÌs¾•´ì—­TZa§´Ô¹Íªùz³—5¡V©sô•¨‹17Íô2ÝL¿t! ÊÀ·+7R\¥çæ˜rh(¡à¿È·gÙ/A'äŒ=‹›UóõÆ5á3÷,¢¢Šj’eßù;šˆzìÒ\ó¾eØ×»rËn}Åk8\5µà0M¼÷‡u[umÝn·ë[¥šô_Q–`« ãkDÞ3ÅÁæ¿È7üpÙ_µy·¾Ú9 ®js ñ_L¯Äþqí6Yö‡Kúˆ¢Z»5l7ª¬¬¢ûò©¢ÊA‰ñk Ë>¦­ úNÐZ º¥«D`Kû0š@IDATÞ—`»‘yÏ9 ÿE¾¹ ‰²_^í žØjdTÇ´•BC—eߨ’t €XÔdÓ‡ŸG#ÌF6xŸ¯QÓÆ4nÚ™§Óíp8ä0tÌr "‡nÝÌè¼çl‹ÿ"ßµÊ>Œlð>_£:¦ ȲoTIº‚@T `‚cÆÖŽxõsQi™Ôr}/f° V|¼O”i¤óQŽ•ž÷œÃ`ñ_äÛ³ì[í•!3²áwj‡â}ÂL£,ûµq‘¿¢(ÀÙkpUæÁp©¦"Ãs“i,ÆJhn<™~¶Q-0vŒaQL‹šŠý‹$Ì¡‚Á‘o.C¢ìÇÛJÜ“¦'Ç4ʲßtÜdˆÈD j0Á‰a8^ÌÄ+Am6'V¿Ǿm(ÙÍ4Ú0_Çt‹!h΋tMCÀ³ TãP U ‰Å¦QÕ¸ï@ùï™oϲ,ÛÎçÀL£,ûþã'CFQ+€™ bŽ…ï†DŽîˆÎƒ‚Œe¢ DZ&Pþ‹|˲Xù‘¡%¡D j°ÐXæòJb'„päˆ_6KéÚ¿Ìô‹¼„² DcÜ7½ßA/æE ü÷Ì·,ûÑX²ež¢¨ÀÌ wC„æŒ5þ1Ž/ÿ戢Û` ü"ˆó.äÿ±|˲o°")É‘¸ˆj̹t7D‘$|™nÚ9Òù@$v`‚ÁYöý/32¤D D½5ˆ&Sl¨“ñK €)1…°yÝÐ4Jâ$FD j# …Ösýðÿoï:࣪²þyofÒ -„*MúZ°aAÅÞ»~®m•f×] ÜÑ$`[»BºººöÞPÁ‚ HïH'¤'3óÞ÷?/y“IH™LIf&çæ÷òÚ-çþÏ™{Þ9·½H{‹ÖӼߦSÇäþtÚÑÙÔ½ãáTV¹–nx‡~Xò8ý¥ÿUtøÀè?Ÿž~ÎñŒú„¢@’¦@bÓ&^¸€^ÿú ºä¤)1¾ËA¤>úZ?ºì”—iûî_iþÒ§zïσ–Ô¥%q½¥)õ„³)ý²q´â΋yt#Y“S©ç¸É”4ìhRcãhkÞ4*üu }î#Z=é:ªØºÁÛ¬›Œú4[¨DZˆUÀŒÿpkÿÐìÞéHê~<}ñó=FFǸ»ªèéw£ŒŽ‡Ñc^à >ŸŠø¸áwÐÐC.¢åýÛm©–öÀ)rÿP¿Ô&†þR~ÜÐ;h+뮽ËèýïÇ’E±¹³·4x¦õ!y⫺]5‘v¾k(_L¨¦Ùÿ¡ª‚´y†Šþø…tž¯^YN{æ|Dÿw+mxä®:y´ôÆ“þ–¦•ø‚@¸ Ñ 8LàFv¬\³QI¥=…k°^s í>ÀK[êØæ.°“¯ú;ì6¿ðîÂUôŸOî¡Øx,ÒK„6«$ùW£AƒiPN®2Û¿\Z#õcijµ~Õ¿²x1'öš¯(':Pü7²Eù–_tT1ðzó›« vî©]KœyÞ£óÑôÍ"»ñn”ôžë๞æÿñ„o†XªÔãÏ ÕM{¿ÿ ,uî£ciýô;IwÔ[ÿÑ+4læçÓ³UlYb5rÐB@°—üèœ2˜ÖnýÊû§eÏÒ%£ÿmô}õI?Vþù1m/øÍx¿kß2:9i qÿ°ËU·rg aƒ@Zr_|üØ(¿pÅA4èw¹aù®Üô‘ûó¿K‡!î{_.öo¦Ï|•­g¡¨(4(Գ˱tüˆºÖåúmßÒ•¹îbx¬ª*ÊË\”5„º%Ÿé~çËEL¯þT¶y¬_Ì¥GH|•®YJϼœFûÎ¥} fÊØ±7Ÿ\Å(®×QÀ¾€-iÚ~ÚTí+«%}~]©°h“»ÂÛ ÑνKé°WSrBwZ²ö5¼«vî3¼W¨Cb/w|¹_RûÀó±Ûä׫„BGzý±á-,wYá~·üOMêã¾o Û+Ĥ÷¢Ê[ÜÙÛR»Pâð£(¶Ï*G_o÷kï2\Ôf„ ÄNïnÞÊYA@,àF€ñ|œŸnÜaÏR3ð€,ž§ûÜ»#þÞ+O{Þ˜}ú ¡âÒ|DÓ))®íÙ¿ÖLÒ¢sçƒÐ—8›ÒR;PRbvɉEmiQž‘ŸxõKúcåºUÿ{úÑ;ð|޽8xqd8h©Ú´ÁE®œì”„# G׿ÞqïÓ#÷t÷5þYn¼ Tyy—Ћ/ ½ÅK‘}ËCR|yòÞÌ¡1”’Г~_S×WÎq“â»™Ñ|:§àãíìã÷ŠÿýºŸJ|˜Á³Þ{÷Rþ®ó•O稴®TºúwwZ}!eWÑŸÏL5žéU•”zÒ¹´ý•êgU°‚£:Vÿf܉äBB@,àƒ 9øAq+T¢Ä¸®Æ™ûÙíÌnh¶Œ®ü7íØ³„ö:ÛxŸ×g…JÊYÏIwŠËv¹yïY—#ÝD¶Ï¥ý%µÖ!¿g9á4‘ªöí&k*Ëtu(ß¶‘,q æ-¹ÊKHªŽ•Ö™8A@hQÀMãc¼u8˨J85éã¾²ªˆ ö¯¦=Î$›5Ž:u8”:§J[ò4޳˒þ¢?³ü oöm¤øØNÆ ;³&Áó^è]¼úeó‘ûÜ!ñð~£û>Ü/*wl&vC›¡è·ù×çPêxÚÅÆ`«Ô“ӟ¾1_St×Äi$‚@Óˆ ºi|Üoó WR—Ô¡îû9‹shÔðÛéÎ+–R9¦›ð”£µ[ªwMf4Àžý‚î„rvì=°ÞhÕüß’ÿ³Aÿ‘‡Þˆ}kÿ¤;æTŸ®iÃiÓÎïz®Ê7¯¡£NÇ0|ô` Vñ²…†»¹ÛI‰£ý¿Ì¡ÝŸ¿aTÏ–Ö…¬I¨üO Ú’ M" ¸Ixj_.Xö$]5æ-Z°ìi*-/ M;¾3›5–NîF­5ŠFKsË1É9̨¨:@¿®ÆÔ²¡·¸ðç?Uϯ_µž]Ž¡4xJÞ™{]ýWa{¿ï‡/©ë%7QÇÑçažï‡F=ò?ýñ¡DÅ^U;­ëE7 ¹|‹(à°e¸Þjˆ ÚK¨w,ÁT£iD¿+ë¤ðT¾übP¯shOÑZÄ­–R'Ü„%?.{£Ý{Žk*6ðZúaé¿à)l*ZX½Ó]NÚöÊ“”vê-9é©|yu¬¤¿KÛÿ÷LXÕOˆÚ ±€[€ü?Ý×lìå›> >$D<)ï£ÑÍVê£ïon6N8F8ðëwÄGSÁy`­¸JZ‚ x…@D[À ˆ¯=¼Â#¤"ÕÒ®„]áDŒ‰a8ÑlÒjÒÎç–Ï´>$oiqïIÀ3— A bpC/å¨é>®GØŠ Óõh²Y겦¡ú´"IaY”'f6«%,xÏ@ûËÏz›ŒcÙ¯²„¾ì3"û&×äéÔmå#¼¶1Ü;“C¾–.WÅDñú…@|Lð†>ï¹¾Áà?Ë~Ö­õÀ4Šì‡:—„¾@!ñ ˜­󈱪äÒã0¥$&Pø<¦Í¥ÅQ|´ÍMwÀ ig2ÿ“â°.7p eÞ3[ÉSîù̲_n¡Jkí‚¡&LÓ(²jœz‚…@D+`w„U©TøàÒ¢áÞS°<áÀ`áéw¾LÓØ-5T×~<øq;ÍÀ”^]SCž÷Ì¢@ñ߬7Ë[öq½¥f‘˜P¦M"û¡È¡)D¬v7@¿bAªŠu”Sâmè_Ò±ªÕˆ``<™¶hŒMOKŽ3èæ4f]R@;ÊÄÄ1LOK¤Xì(ʼgÖ‚ÿžõö”ý(U§õ©¡ûñÉ´Å çEd¿ýHÛyU#V3_ùËŸ70àÊ>0«ÕJ]¡„Ë˱WiEíÒz¡"LÓÖ»S’A¯I;×C‚oxÊÀàžC–÷\»@òß³Þ¦ìwI°ÑÖÄž´ÓÏ"|ãDÓ©˜&¦­Wg‘ý¦‘’·‘„@Ķì¦P­|­ØO5 ‡ºwˆ!›ªÑÞÂó0*»Ü‡H`Zö¦h«NÒ“ zùƒé7ë"¤† &n¦ ?$V°bàJ¼g@Éÿúõö”ý(E£ù=F“û‡J`Z€&¶~EöC…+BGk ± ˜Áã Ì­P`¬x£££(J86&šz&YÉáèL{÷^Ü{UÓRšuM2hdz™n¦Ÿë!Á7{®0héÝ!Š2:b hdz™n¶ÞÄíûoÇtÅš2Ð;=•g$Wó¸·µ%,þׯw}ÙߘҾé}V›ZÂlù2 L‹È¾ï2.)È^Š’]q¬Àl6›¡xãã㨲¢’*++©»ÓAºVNùåƒÉ‘ß‘RS>¥˜˜Í­ÊIîócW8[ã=’-ÔÖohd:ùCé6]ЭJXÖ ëÝ™œN'­Í‚4Ú†÷ q0ùßP½ëË>+¾ý1)4jëw”^º£U¹Î}¾ì gk\d¿U¡—ÂBv €Uìã ®­„øx(ß*¸ h€]”¡iUTA;Ë;Ò®Ý7Âýµqþ€ò[«³v‡—@ò‹çyòTíÊ®¸?º_•ºÃòMLL ø„xƒN¦—éæQ¬Ü˜Jð jEt° éÕ scZ½K¯æ},x\Þs Z‹ÿÕ»¾ìï¢ôIÿK¨Gñê·o õ,ÞDÑÎJßÀn&Ïóå©F<Ú™\qt¿‘ýf`“׌@D+`æ»âx0÷ÿ²eé€òu¹\h5ƒ­ü>1ª‚v—9©°²TôƒÂÓIUÊÈb=€sU@ØÏK`º° —†…@xž¯UqQ—Xº§ÄP"”nrr"%%á`% :™^¦›é“àÉ@È@JŒ6Ñ®’C¨DÁà=Sßüo¬Þ ÉþÎÄ C)*˜‰먠8g1E¹þ_“ºÊb£2k"•Û°u!æùÚHÙ²’I¸#ñ Øm @¡iºn(_œŒPÝ@ÁEmåAZÔÕᤒJ…ª`©:]h0 ,¬Pê±…¢`ñ&G[(16 ý¼Xõ'>Ö°|Yù¦$'×ì~æ‘«býùêyÔŒ%cÚ DaÙÏ¥åTXRA…åNªtbg-i)Ÿsi þû,ûÖ(*ÓÓ¨4@²Ï56 $ŒwË>ÉÙœtINáŠ@Ä+`fŒ©h£a…êñÕÚ—½ºÜ¿ÊnÞh x*/¯0ú†ã „Ó´j YÓªãê¦ÆöËÜVÓP½"‘Eå>iXä(“­Ñç ˜-_Ãí¶~eð•p7šÄˆ­ 4Œp@\’¶æ¿7õÙoTlä… TÚ…f¹!bÅÇÅêûšéIèo­€®¨i|y€ŽË¥nj”¯Q˜Gyl…±[™é0Ge³»™û¦ãâbƒ]Ïüžé“XÚJLyk+þ·U½™{m]÷ÀJä&v£€Í†€•›q %Ì×<ç0.6ÖP¾Æ­ª*c€–VÓOl*`_Œàã×Ýñr˜¼*»B¹\VÂló€+V¼üœßsƒÉ4J,­-& ÍrÛŠÿfù"û•'ÉMðv£€(nˆÌi=¬äÌ)J¬ ÙíXUÅ££Ùúu°Ž!#¬€M%ì Ø\¦q`ð [@\&[Á$Ñ­™Y}D£±£ug]ÑSHW’a‘ÄB;ÑLn%]Ý¢Z•Õ3ì“6 ^-G ]*`Sñ:ÃâyïÛŰxÊiHßîtñ©‡Ð°~Ýr4½HQVQIËÖo§…Ë7ÒÇß/¡~_K—Ÿ6’†öë½iyŸ`é–÷F¿£° ,_¿Þøòg*„â5xÊ‘Aå=ÝÖüÙ÷[t">ƒq™Y}¡\oSõüJMëS]a¢¬-:*J·ªŠ^\V©:].4V.Ò ¡ÇMÍÙ¬“þj´Å6ã9û};"¤U°])`óËßétÁò©¢¹‹WÓ?® ŒN)tãE'Qÿž]kãÙ°b?zè!ƱnË.zó«_(ïƒï鼆өG !«µzƒxϾ³Æs“7-EÀ”o® çþNSèožØ*¼gZÛŠÿf½Eö[*1í+þ¸)Ù“¡|T¡}‡õï®ì•N‡öéF;$Á8PêX%e´÷@ ­þs'-[·­ç†m»§8œŽ¿šýYY™/ÔG2¨Ü:}zªZ•à8ap×ÒË/¿ÜU?N{»o7 Øl€'úu«àv\I_ý¼ŠŽÔ›®;ïвYZ÷¬ðï»þ\úï'?ÀþÃèj>uä`²ÙÄ% f˜2ðÍ/+胹¿µ)ï¹~­Å³Þ"ûÁªÈÉsÜÔì]Ó'|ˆ~阑JºÉš q1ÄG¯ôŽtƱÔ‚Â"zûë…1PÆÏ#/ṵ3¾^¾îtréç[,ʘñSszB£ˆÐ×éÈÞ€À_>Šî’ôñ³·ß^ÙTy‘ø®Ý(`v½qTQYIK×n¡Ù¿¬6à±nS¾²â7hx}úÃRê”’@Ãû÷4”°ÅÒúm F gX¶n+}8ï÷à=W·5ø/²dÁ £ì'f>2PSœ#u];dÇè¤FÁâÍGÿn¡®Ñ¤ãÿ2€þzÎqxÔòÐ Vò-—Qž{ó}å¦íÓç,_w¹´C¢mV V´Ú5-‰RãðˆÐW)ÛvöÛ¸uwoŒ‹¹ºª ¨hì”ì)¶NçÙÇ—µ¼ôðLÑ.07@ìzcË÷@Q }:™ávfË7TÓ²kïzoÎoÔ7£3%ÄÇS8¤O80b¨¨pÐ[³nçPâ=×0XüÙŒü„s.7ÛŸOpº oRå§«ª»Ú0ÞDCw—ŽgT^áP îaîã½`ôá>)_O|ŽÑ_Y¾a[BÏôÔ¸³GÀ¸Šî*oi$X×À…ýýok¯úóAÅU0qBfÖÕ3³§Îi$~D=ŽxÌî7éÌ}¾åå4ÿ÷u‰\Ic/9¹MÜÎI[BWžq4ýëÕ/iú¦O?fˆ{d´ô7†šwÏM˜³h¥1àŠû|ۢˡ)jƒÁ³Þ"ûM!ÙïÆffÑ\ûÿ×rÞ=:iÇ ïG‡tïL]ÓRX÷“=ûK ”51>Æo@;´eÞtuïÒ¡Q­ëYÈÀÞé„C³½€^ùlA§fþVNæãžñ"ñÚ+€Â¹âÕ@u¿o)æõþ¶n»1âµ5\µ7¦‰GãþŒÒl­;NcªTKó‘øu0eàL3c|C‘÷Lq ùoÖ›eId¿®L´‡;ŒfiC³;vHH¿÷úsèžkÏVGÁÅœÞ1ŘngbÀ?»vL&v!"°b‡òmqV}2:Ѥγ5¤ô’þظ̜{ZœI˜%ˆhì¶0ݨÓ6á «´¢ŠŽÂ(äP L†øÓŸ µ t³õÎõঠ0ï‹0Õ,”yÏ5 ÿÍz³ ‰ìû&;áœj|fÖ h6fŽЃ¦Ž½Àz”[8öÝpÁ‰Ê1ÃúB„µG1Pë¢p ÛWÛvað•Ãp?o@#Ì_g<Ï7TÓÆ4®þs—A·Ëå쳪‘ËXÝ,ÔyÏÕ ÿÍz‹ìû!õë± ˜+jºáX‰ñP{häúõÙ{óãA\Ðþ±È”pû€ñ—ÿf½Eöý“ŸpK©DgîÓÍú††7ئa÷8z•ÞÛøá'b°i°Îå‘Ä”pø¨_þªG@ówCµ5nÂÕÖôš¸ß]aôñŸùÃÏz‹ì·µ¶^ùãìw¿{÷ïÙ¥õ rI%˜b‡¾7ÈEµIö«€MwC„æŒ-¾›R¹6ë6t‡¡&~aÄùjýäm½EöCL$ƒFŽJ•Æn2©I A+£µ3^¾a;/H´gôˆ¹ÃR»X ËlŒZ[xü)F8ÒíOƒ•–q ·þ›ònõDÝÃß Ws)†¿6ÊvÕ–]{iéº-<äåHÝ9)28éõ1a+A`**øh,¶A Jwíæ’y!ŸÖÜrwG ÂìÑ=ëƒy.…”üXK|N ò µ|"Vã«Æ;ßÒs<ܾ»fa£;{2Ÿ­Üd<ŸÿûZÊšõQÀâ`Ô'Ô„1Ðôø‹ÙÂåèí@I=ößÏéާߪCê~ÌÙ~ø¥ÏèÎÇ_3,,o¼¤,´¤.-‰[§"õn<å_­Ü¸î~â cC‹zQ|÷ôë³±Ã×’úQ[|¨ú´¸à0KPÕ£ó6,ð\¾yÇ¿)ç¾W–ç-;«»^ùÃòù‹iBÎË´~k¾;ÿmùûèñW¾ ÛùýýÉ7èÅ¿Ç ~NcÕ¹Ûýí,ØïŽëíÅžýÅ4ýåÏ\{ K+0é’§ìwµ<o kãx«€WþáÖþ!ͦ±šÒN:혡FFŸ|ÿ;U`jËã. SD/2Ÿví9@ÇŽè‡ÝEªè4Þþ†ZÚŬòKÖ¦ç¯ù°máYØÍ…7ÃØˆUÔ¦½ø)•V”ÕûØÁª´¼’þqíYtÒáéõ/~26*÷WLÚùÜÒà™Ö‡äT_Þ¿úi9åჳ¡…{wÎ #èëŸWoÜÞÒàIKÓ¶×øyãÇ;Ðâ}õûÚ-N,!å _,XBýzt¦žéiT†e|Ÿ}ókZ¼jóAyîܳŸºuJ¡Ž¿n½b ý­^ÁZö<™×þJ»¥L*)­P X´A›4ÁþXç–æ.ñ#¾8PŒørÁRâDxêJ,n°|ývÂ2oÔ95 Ç`š»h5­Ü´ƒNé8ˆN>j0q|¬gêsñÛw¼(&6ŽlÑÑdµY¡üû^ŠMH4vJÖlŸ‰jÅ„k·ì2¾¶ý)kÉ’_㬂 ÷ëéuv Wl$ÜjG­æ!{<Æ`‡*V ßü²¢N>KÖüI|¬.ÆñÓ²õh¬þÄŽVC}–ÞÜü•OP||Íɺ †ìs!Ö¨è#!ÿÛ*ÐÇæ‡ òlù’e6—ŒiÚ_TJß-^C§ŒÔ\ôß³1éï¸êtã=ßÇbZÓÄËN¦‡Ð&y†‘C!>Œ–L}ºu¤õøížpØcG·IϾC; !Þ¯&Ù»5=|ûeêð&~>ÿs4WÕr,¯9*7ûÞužeGµ-z$ àe¶¢êÞ5ÕˆÍ{[b¾¾ «Œ{¶–*ÑÐïÂ× ‡ž]Ó(ßê¹ÇÆùn°ÂíÞ9J Ó"pƒrÚÑC°™xݶ–­ƒ*‡‹:¦Ôî$Ó1%‘ö(5Òù* Õ¥Yxõ¯¥ñ›ËÔSÞ9.ȧ^éQÿƒÇ<4õ®g׎´5?"g4a›½gÍ{ßüJë } ¼/9·i=jÚ»4ÈóØ‹G“ »&5ø·°.iÞîC‡¤xŠ&vS·4°¡sÖ¨á4éoçªQѶTEu|h·¿ÕÒ|B=¾XÀ^pÈ•´ö•Ö.GçÅ͇b[»çþFùPºk·äc¥-§a¡ðû.ØÖ‹¿D ‹)î á‡@±w£¹PŽW¢¢jJÑðV”Á%ÍÁWYèºü””–Ú’(66} 7€Ãúw'>ÌÀ‹oðÞ×EÅ%´w_!åï*0_yu®/ïõ)¦©]ÿîë¿ã-îÖlÞÙ@Ly, ÷=¾Èžzý+:ÿ¤#èä#Åþ×µòÙ\¹ùûŠˆ÷Žn™¾{õ³ÆR¿ž«pñoh÷¾âæŠlô=[Îמ=Ê’ûÞœÁ;]k/Bĺ0M/¼çJxÔ'(TBùrHÅ®=wý¸t=­Á®E'q(ÍY¸’’kÖ™æ3ï豯¨Ägœëëò+Oöª6ijêüÄ«_Ò+×­úßÓÞxüiÌfIŽr›5xØJµJ,Övì+íú×;î}zÄàþƒî¾æLÜú<Ñ‹/hQF…pãõëaL«l2]\Lu#Å`f`oH¾ü9BÌ|[ëܼûZ6ÿfË–„@Ë>—Íò¿rýæE/=–}n"÷Øowç—=u4ŸC)ðÒUe®™4zégóÿp]4ú0ËÉ#{Eâ>ð‹­×–„÷¿]„q{è¾ëÏÁ‡b­c•ùÏùùþ2°'¥$Å»0ØñRäQ ¸)Šð´É Õ8ìÇÐx3DcMé“D.=™õN§íèçèÛ½“ñº£eÙNIl™›y˹íàM;<ùÝEl%ÄB ïFŸ­Øóa6`á( É»Y·–ž ñ› ‡ PZZ¯PÿüäÉ{£â,UY 9,ŽLêà¥ì›Ìþy9-øcúŒO«c¤ð{þ ùË6fztîµ®TÈ0 Ž€³(`/˜ ÷"[2ùè1Ïùä~`ðï¾CaгÚbʯiŒ;§&šÑåftIM®Ãï¦È?rPoš¿d-íÅô‰y‹V£~ØËHŽ²Ð¼7Uÿ¦ÞÀ•ÏXJh]x]W¹ö“UQüíÂÞRÀnã"ŒDæ6®¹°rÏS3ÏňwîŽáÑó|˜ÝÙÞtå˜ñ;W÷?+¶ÆÞ‡ësqA{ɹ]:ÐÖ]µƒ x~#†eKxhß ºú¬cÝ9ñܹ.iI´ ðºA ³‹î]R1·ûOc+Hs VcU¸# sßKS0œ•×e§F§úã+\e¡¾¼7V÷æžoÞµ‡÷Éh.š¼ ãrsmúö=a Ǿw^}†Ê£Š[Ò;&nd^‰ŠGÝ7æ-^mxûÞš½°N´™S®7ºx Zü–ü û‹XÑ‹¿ù„ZzÑ^räœã£'_û’Î>~„1ǧœqì0c°‚g¼"Ì7 WÐ%§éùX®Ã £†‚)Ké§¥ëŒùŒ&ùg7Œøð <Ò“û«yzZ¦ ±ËŒC8ËB}y¯®Ñ”Ï7/:×·vó.Úµ·ˆn¹ü´ƒâʃ "°uÏí.];á¦KNA·XË”/SM§¢¿øË—ÕQÀ< Ž«g¨ÏsÏw_!ý_à Ê€wПÀsì1+A%]ùÅŸ|B1­¸ ½äJŸŒŽtæ»ÍG_hÀ#랬ޭc‡Ú¹qõ#È}X À N\:f$ýøÇzã ߢÙb*_ŽβP_Þ½©ý8ßÁ::ïÄÃ0w¾z@Zý÷rxþñØcñÁÉ#ôÔ;Ôûyïõ)9ëøáXL¦Sˆ ë¿òê¾ ֬ظƒ.9õ¯â7‰çÔkšƒÞúQSñÂñXÀ-àÚ_Ï9®ÙØGÃrâCBø#0b@âÃ×î²à¼7… Ï•к¨8SÕSO=Ú»ÏQǃ œpqc¯›}ÎÆIÖ;§7 €â¥/ZæÂ›‹s³îÿÃ|)父€yBzí~,«¥½Æ§~UhsŠM Ûœ0içsKƒgZ’·´¸€Ç÷¤?à™Gp†©‡;½wzõŒŒp¯*wçíÛ_‚©Šú?ý. ѱpC¼ŠX¿¹ù‘} ÕšÏxô¡Íc.—ÝP}Z“¦p,Ë3EYåÃ!øËÏz›õÙ7‘ˆð³®ózÐøbÃ<È0¼é6€À~ ôYnöÔ/ü: ’ÑpýÇ öfngýt­}¿óˆc¢^õ¨µi‰”òâ1W÷@Í‚*¡^§`ð_d?Ô¹úTEÝÈ9ñæp< jÖ{óxá×6kôØp®KS´G¼fkÀ»ý† l†õðïk¶(¼›P¸…½J°çög®MÛ 4xÑÿ:3{ê¼p«CKèXìn€ÐÂS†T,dŸo£hXÁ¼Ñz¨†…ØK“ÝÏiÉqÝ<€Æ¬K¨Òªt™¸1†éi‰˜ßhï ]¨’lÐç/ÿ=ë-²²¬a¹ÙSÔŸ½ùåÏZ¸(aÞ¿x.V‘{0ïC×νJ±©Çy9™ï ¤É8b0ãË_þ¼ƒ VôÿZ±2Uç8•VlØîóV]Áäo¶bÃ6ê•oÐkÒÎõàž20¨{š¯¯Û´ùF÷©ÉÏz‹ì{σHˆ‰0Ýjép%c}öÚç?Ò+Ÿ.0–Gõ¥nì5ò\ZÒ—<<ÓlÞ¹‡þÜQ€e+«ˆç sÞŸ~¿„&?û®ë­¯~Á®Ú7V†Ãòã™.R¯#z4[ÕÊ׊íâ¢pب{‡Ú]Vjl,~ÿ çbå¢ÐìÄ{Êòfç± g@z²A/00ý¦E©B¬z™¸™20ütZ½uOÈñžëHþׯ·È~°$,tó}Á~K‰Ý®_¸C϶ÿ¼tý¤ßVÿ©œvÌPËñ#ú»wmkŽz…üöì…t©Wy „]ßü ¼+Ö£/®ÁÚ­gQ(XeC™ƒ­·ŸÈÍš‘£Ã-b0W˜×ðå½{YñFcby4”p,–YëžPF±‰Â?ùÁØhº1pZó9Ó²sO!ÈH4hdz™n¦¿¹µˆ[“Îp+ËSâ0눾é‡U;CŠ÷Œi ùïYo‘ýp“ÚÀÐk·+rz`bæ#¯UVU=úÉwKÎcks`¯®ú€ÞéjŸn±{Q%âwáÂx㊪*Úµ§ˆØJýuÕ&WþžXNÍÇQŽe){ÜÇbn³ÙR «°E',q¬«¡;ß8XÖiº¢W¢‰ÞªÆZæÏ˜4É·%·ZJHˆÅ5Ì#÷t¬cZŠ9›|Þ;߃é†cEM1ØÔœ76ïœK¥U%X*ðO¢÷æÑuçŸÐf–0[>Üø2-½;DQFÇ$ƒN¦—éfëëáoÀügÝétòÞ¿Æg³Dú:49]Îò@🠫/½ÓSiÏÒjÞ¿ÞŸ×v¼gú‚ÅÿúõnϲÏ8³ü£õ/Á¥)kü¸]„Ù÷­AE/¸53«OÑk6ï:oÍæC¡lXà8„âV~‚r|91)êÍâÒÊA˜¾ùã#¯|N7ž¢¥g:oÛí]àã~_³™>œ÷›kïþ96/{ʫޥŽüX¡¤€ù‡aÍáÚ½ÿ@)ßû¥€M´ ä³â£JL󩬬¤îNéZ9Á5C»öí§+Ï8†ú×l'XCFÐOÜçÇng¶|{$[¨_×$JL'ÓËt›.h‰Ù_TªWUVîo 7î ¼kG•_UQ¹?ügâ’a½;£¯ÉYÍû½mÃ{¦-˜üo¨ÞíUök–—ÓYÀ×5á ¹3_Dêù¹ì©›P·L>î´?™Rî(Žë ]¥Nhh¡›lx®¬KL²-}üž{J=pX4!3ëô‚}EïLûϧizw¥C{§+8¨+v}‹²Y OÇ?€5 ö@ÑîÆ6„<ÖfÙºmZ¥ÃéÉÊZE§ Xõ½G¾íþ2”°›ŽÊÊÅå• Ï‰ä9| ÕjìPƒ|âã¡|«`u8л(#*(Ï~ú׫_Òl+xÔо4¬_†_å6E/׉§šðHlÐh‹B}STêË711ââ :™^ÞY‡G±r=ü ÆÜRàYQVêÙù“ePÓ2à?Ù˜ éÕ scZ›_Ôj¼gzZ‹ÿÕ»½É¾‰9Ë·+|/è)û]ü1îµ2äé@·ÙŸé[©ݺakþEë6ï<üãyµ4äMÇzͺgÿ.ü×{áÙ~SPÞêf™ôƒ=Ëp‰ ü„¢Öwmݼ )-íVTþnlÀ®8ÌÄý¿lY: |]Ø2PƒòåÀï±gåî2ÖnÚn(EÖw‰±±”Wu Ò"pÿÊþ¢r*./7æ£òد.±:uO‰¢D(ÝääDJJÂÁJt2½L7Óço¨ž[ª+[Ö®^мøËß<üÍ:éMšt¦3£Oß+Á&°1èHÁÔ¤E´îÏAã=ÓÐüo¬ÞíIö{Sþ¹]Á-Ë™xÖ~{’Mãã.û©eΊÑ:¹ºèºOÝxM×lh;·*ªº²·áäÁ}7_~ùå®ê¢&ûPbä' 5l4Âß}öÁŠþ#Ûû늩PÀ~™nK  å‹“ª( Ù¬óÝ”nOpËòå)oE‚ Ðf„’fØæ0áï>|ïK'ÞvÅ_þÜáþÎSý¯Ë +>Ü(Vß×LOBkpEMãËtxô»êüQ¾Faå±EËne¦Ã•Íîf‹‹5v=ó{¦ÏßP=·ôgMs:Š|þÉ7ÈÏYs˜Š.€6ž_ÔºùnÒÈôöìÛÿ4ð?1ügêÚJLykmþ›i«zsùmYwSþ1ü¹Ûjro²HÎíPRÀæ—)ÿHœû÷ï)Y¹xá“ÊÈczå“ùúMŸä—/ØlX¹×PÂ|Ís$ãÐßËÊ× …¾Z —mì‹\cüº!^“Ý~ì^ærY ³Ì®Xñòs~Ï &Óèo`Üvì§Å?Ì{­¬ Û‹#y´£¡ˆgs‹ò'½IƒÁwdÄôU1½L·zòiÁ&°µeÀd¡YnkóŸëÌÁ,¿=É>×Û”nG¸=Á#þ ¹g%´sBI3+Øâ 7•?Îþìç¤Ô/¸m5¶|×-_òé¼ÞûÅXâÌV0_3¾l „’fzÜ|Ç5Ó;÷£w¿­Þ`œÎ ÿ‘¯ï`ËËYmyÁç¿QXÿL9 v½™„¶¨»§üo^»êÙï¼Î…‚#÷º’ˆ¼Cwk5f‚„FÀo¬è Ï­;"@µdw$7Äü£áÉà1ø}xø £wkÇxæë&í×]9äÅŸùº¦EÀ ’1Ö(º2û}Y隊×<ƒ–³â³yp™æ ,óY‹3öH`Î-åÑž<àÊåt”ü:ï›w—ýòãrD3qdãÉ÷Œ/ãÌx‡Bhï Œ'Ûð1wï®#G¹üO ÿ¹ÒŒ=S¸ ³,“׿¿Q@ þµF½Í:›ç`Ö½¾üó˜‡ßüþ¥ß~˜÷#ʦܳ᱈ë(Að¿0 ÝLŠÞeâôé°ž§Ù›–ÿx¸ŽÂiáêÅ‹ÖsÆÙ§»\ÎÑP4IøQ뉱ÑzJr¼c³ùßiŠ‚ê+Ûú÷ˆÒâÀg¨ïù®%×¼\#¯Å‹ €NCñn\µrþϳ?_PQQ†Umˆƽ|æÃÓ5|ßA/˨ÊëþX²ñ˜ÓÏ…ŒãÁÿÄ`ðe\êó»þ=— ¡¾¬×¿÷…Æúu­ïKžfšƒäßå,þsͪy?õùlŒ!ài2A“{nï\¥Î.¤+›Mzä,´¿0¦Õ|åÔœ9Z™v ÿ_K h ¾ÙÌ}6l±±û™·,Rñ£¢9¼ý)®¿í?ì/}zõ8(6!±Ctll’Õjó}É,dŽÁéÄÚ>eåÅe%Eû7¯]³výŠ?¶¢¦+—-]V¸jžDÏx2®Üß*îgb„Fùn¾ÇG…kÞÇïΙ÷1}×oȈ½ —”›Øù_ƒK»=ò_^^T^R\¸yÝšUë–-Ù0ZEî¹½ƒ“Lµ¢ýk· Šû@]ÓÌÇìÆffm…ƒuy^væY>fQ?DbŃ#GŽÉ8¸ß+¯ï8Ža!áÌiRäN­XV^¦c÷2[¹¬lÙ`ÌËα6-`Žj $<¾3šC Må~\föè¤:+{jæ•÷‚@cømëÊÓÆÇÆOÉ>170‹m›Ê„­5S©òδîøy,£gÃBÆÙBl¦Á£ˆ ŒSù26¬XÓ `…ËŠ—q([¿ ÏÂw 97†@›Ë=·sXUïL4M÷4F¤<¼A 8ÖñÜç¶Û0†éÙqöÜcóìã¹ïÑßÀ1+æ×®©`ØÂc̲i›J¸=(_TÛ&.¬|Ù­Ìø06¬€Yá²6û‚Y1ó{Æ5”ƒð=”¹´µ™Ü£}‹ÓÏbXÇ–nÖŒçB¡"\ˆ¶Ûo¨—™5 |NŽ‚—1xã ¶0¿T}ÅÆü‘±Òð¼fEÂJ…•¯1@ g®G}ÉŠØÄÖÄÅTÀ<ˆ‰ñ1•0ŸùžŸ›ý¾fZ< É`ÖIø’ìiS¢LÙ5e¤Uåž:ŽÏÌy Áq6·{8K|F  JjÜÔì;uMýÁï­Óõ²„¹rìZfËÖ.¼E‘y6•/¿çú˜.#:p#dÜy6F¬¼Xéò™~ê–/H<(ß‚¤Ý?0ežÏ­&÷lùÆé—aoÛ»ò²2Ÿj÷œüF   ˜©©VÂô8 àå˜#|{€ú„9k¦ÕlYÙz*^Sù¶×>`ÏÆÈTÄfãÄŠ—߇k¾‡+ç‚C·)˦\›r4¹7ú|‰žEu†(*ýC”opÛs ¸fáŽ>§Y¦ß®è/q¼¦Æ©Ÿ`ž0gÏ4›2+\>Ìgf}Ì3^El0">›7Jæa>‹L›’/àêwWn±H®jƒuk¯|oŒvô0˜rT2Ëz¦Fú5ÓòÔÓP~Gƒ<0ÕÁ%E”“«¬SåS…¬/Þ?®j…ñÞ‡m)§·ÙŸIªtXc¥¨S^ȾoUc䋜6† DÁ975ç&Òµ“ò²§^Óx ò&RPÛ²"‹ÎïzÎâE/¬†ò}t\ÌÊ·!ztÒ£¼_תÖ,:¯óËËÎïÜ¥¡xÍ=Skét¶ª¨)*}®+†äÁŽí¿Ž³çÆ5—ÞŸ÷Ŷe¾êO¡ÊÁ>vJöq¡@KkÑ0}–uôôYê—®}9¼ åV+߃ è¯éú]9–MËSÞy87¶çÁQšÒ–ršF·•€ÂYÎ}Gó”†nŒp“S¶šÇeæ<§kú4 :&t‘ʉ@›XÀ‹ÆaS¶o{Vsiã[R(h ôºJE9 ÖòUG|’?§%é͸KÊë/Øoá†fæÍ™ rèU+çnþâÌ5ãú<#û>¶jù÷p±EÑ¿÷JxC¿®ÛÕi³š¦ë®{!{^w×ÔĽT£ŠÓ¦åZ®Ÿ<Þõ¡7åÕÓrj·+èx >-axVrºÃ±àM`œªZh¬®ÑÌ0Ä[HöVWÀ+.U¶cë‡ðÜå½FX!áDþbñùéWññN¶ž}ìfÃ×òïÈ ?g2>sÚÅp3ŽVå5¸‚‡õ}¤¢çx7Ûsþâtè#ÚẢ—‚†¯â­ñÿxÊ~×~¸˜?Wýƒ™ÙSgq>ø«v»sþϲØQß(´±yÙ™îzOx ç MÓ¦#j¼_£ªê$]Ó* ¦¹Œ2ÚS¸? ÖúnÐõBnÖä§pF¢±™Ùÿ&UyOÑõ¤Ñmp©€ûêp3½y®ŽG_(š> ºäb<×Ò?T­ÖgfÚ'ýiÆ?5{Χ'ž-SËßQÞO욬p€HÃ4R^\®ªô:¼Àaìì)\zûí·-ßü±v!Ò:î6ó>sЯÿBnöäw¼E@¥…Z¤ º¿«Vº'מ¹ˆãßõı%{Ë–(‰899V-IDATÖ£ôçóDú9è2x ¯ž6óã3»…Çgæpce3bÀ¸Ë/¿œ»XùNŸõÐÿPÈUÐ>äŠtÉ€ù½éy–±“ƹþãCî$­%§±©1ßû¥ªuvnö½ë˜oäôÞGI,,v< ®œùµA<¿²ÅYî|~ò作G å´±ßÅí>›Žrjµ)Ù©ÿÊ]Úº“Ñæ0dÚ­î‚.+Ûó"äË­„|Åo”îÒ^ÿý‚n~¹Coµ?Ò ÆPбiAÑÅt6¬žç¡ä^µX­ƒ:[Z7Á>½7”ڪØF¸±ˆ×¯ÌYò%+]Q~péÊÍžõÙ¡ýxTiÍšå£'á‡ÕË|?Áž3ÔåÒ?C>3(ÁÖ]%ËÃP¾9È*ÖŒsó”ìN§¶™£Æ(ƒÉB÷£¬û&<0m¬ ^:¹t;cŠ€ï¼ÚwžWz:”ïË ¿Rµªg+VåRÔv¤ËáÊ3cÊW§ÉP”ÿŒ²F Dý¿ÆÂ×·ÙéþÌ?o+Ž·&œ«(T¦+êÍñ¶ø‘qâVHA?¡v‰™Çœ¥kNÀu4!×ñ?çz î'[uíW¾¯t}=LQ•[Ȧ%¼Tsê?ÜbŸ6€ß[\.À¢ÐKˆGùè<Æ’`™ÁïÌàV¾È'%Évg •/—1=ï¡¡Œ«Ìòü8«øðÊ›6Ër¶yPkÉi öý¬ŠÅôz#§ÊV>d̪žg³)§CŽ»8Ê4¶êjBàä´©ßE¸Êé ö)KìöË«ÐÔ~}›ÐÉ9bhU xÑ9]¯Ccô×@¡‰F=Úår½«zÈwV°KÙ« SáÐ ölxÀ©G¥Óq+$~C´5ù531Þ¾ŠE=7÷¡)Ÿ™Ï`ÁýJe+,½[kž­º53kM¥N'L~ÔæËzeM´ç ŸaŸ²ÔˆãÒ¯ƒ’{3Ï>¾lÂÔ3+ã¬9t¶2W{XÌïŽ:}3éÎ…HcÄq݆ly^NfvMâ÷ÐOt²®kpïVœÈg@tçä®ÏÞ~{eM¼FNÊü¼¬ÌIæË±SsžWHË5ïaùfBA?ÉV*?v”7¾Âé¼ʘ-Üý°|u‹â*a«Ÿã þJsÒ£wÙŸH}Ò~÷>ÔKIÑ^FþºSûñ$DùÖ¡*§Á±¹úùœÌͧN;Á©¹N´ÚÔnœˆî›™5ªÊ¡1m7à0æYY™7oþñØcñ|mqZôqS§= ”ÇǢGﻯ،ˆóù–s\¤Ýˆ¼8x àµ×^yì•„Á÷\[²ÛÛ|ÛBNMŒM½‘S€ÑÑð¦ô5½)ã§dg£?ü;POˆñq(9mêw9åßE»S“Gr_ZÍ^}þÀD4EìF h€’èY^¾grK2u:á:uÐKP ã (fS¢õ˜gí·¹óPh_7õ¸¯Ü÷ÕCØ"ô|ö\öÔMH¿ qxÞ”);¡>wJ—hâôé÷|ÕF/z¦q_+ú@…Ôyî{\$tˆ^îyëa°œ; ûÈ[TõuÏ{óÚ¥;nEž7«6ËežŽ”›{„Í¥hÏšeꌷ4GE™ù!åU¶á"§‘aøP,לΧMÙÑý>®$ºUúÕVÖ9­ÉËËßEmÉ‘&§µ5“«pFÀÚZÄ—h…cÑu FyP·®»ºßÃÞζY; ¯„Õ9ùvûÉøÐ®  Ý k±ªöIÍ•¢WBG8*zQwQ.\¯÷í,[pb¬›iÏ„EÛPPlPvežoŽíÞ½ê›}kÐ]óT¡h\-Ã|h÷  ()˜(Å#¨ m÷¸mê²Nyž+Éi¸1Îíôí®ôx÷œM·ìð¸?øRòÔ”SwéÓwAÑ*],“ìÔ¦ÅhpyC)߶Ù}*]à ¡h ¾ 3f-üP.œ zy´è–mÆËúÿ:~€ûü œ«•|ý8>ÞªK®B_z“7Lׯ⿱Þ}]¹Wü 9U¢a—CNŸ3`9ExJS£y|…!§FF^þ.jŠ­=EœÖVJ®Â: ^P+¢+×à N:%(¹™¿œØzÔÂÒ»À3î—«tT Ñ,´ŠŸ§«£>Û¡Í×wi?ž ÷Û_IQÿí¿ÞõX 'y>ûféšÞ°HÑNU µ ×£á ¯cy{¦ Ôõ ö{wÁ ¼õìœûPfÁNžeà=<÷TGn0÷õK(Û5ÒÐ  ~À#iñ2w‡kAì.GÎU@/±›-ã{Îãs’æè>Î>íÐ<ûäÕfÞèš8 žw÷½ù¼Á³U½-Φb¾ôïpòÜêÇŒçÃCôy_ïC2¯’€Ÿ¶ÊªÊÿCä€{‚LÚDNu}%~Ú­*p_¦IK°ÎÞü."]Nƒ…­äÛºÔiHƒUô’s»g`ªÏ°`åÏùÂ5Ë»^ ZVËÿÈ¡Ý<63gR‚56·”*:W9v¸S×QF§¹\¶š¡Äþ«¹ô; LŽˆŠ««°=iƒý÷oX‹ßÂe7 ¼´Ó•?PלYˆƒö¥:ØÈö¤Sw\<†)œ«tNÞQYPò FTÏÈšüƒ/Pg¯ ø;°ÊÑz‹5õ'•ÇèZåÈ(—¾’ûort…Ë=–í75SV(ÖûM©^úž_¨’Ñ?m`1.3ûS˜óS¡uç™ œ:ìå%=X>:nêÃS¢-q›+Å7‚GÁ]¼·uá•ÂÆNv•¢i_¡_ù—@àñØ+]â«ÊwŸà- ¾Äæ¸xðž-Fº”S†üÎŒ‹¼Þ‡EÑÔÕý¿üœG+GÛ’.ÄLJu^Pé(Úë÷nŒ¼¾²qw}mŽžW³²&‡Ñçº4í­;rrºx¾óåÚU±wÒõ£4åÔø‹SÎǧc±¦+³·;³Ë5½j ät¢/|h.7¿‹H–Óæð‘÷áƒÚöà‡_ÏíŠA3ØRrçÈÏv£_5øáNû“)J’^ùäÝw—û[,wå–ëòü?ïɯß/ê™÷¸‡NŽªPâ;ÒQ»ë÷O{Æ Ô5»×õ(WecüÞ_ZÆåæÚhgqržý{E·?ùääY®„œ¾áOÞ¤‹Jîp× ûQäÞÄ÷5N›È©ÁÓÂô¤xËÞÇï¹§ÔWÚ½M×Üï"åÔ[l$^è#Ð* Óóp°áP2zD™·Øm»<É?²À‚ÓtíÅ`×ÊjÍ¸ïÆ²¦¶›É_ÚVqAc"KS”o›ËSX€ÁeŽdÅtgtküI²ä%A@ U°E×·özY*­PF½"å6¢°P«ÈPÑýã DpRA@ð VQÀd‰Zäu-H_zÐËh95 °Ä¤ý²ëÌÿt50j^ä4РJ~‚@˜"Ð* øð·îÀ£¥ÁÄë Ìü%ïÈGàžkóK¡ ¿fMñ;ø2˜ùKÞ‚€ >´Šf8Ðüra)JJJx?ˆùKÖíõå`U^‡ªÆð|i ‚€ ðÊ€­âb:ÎÂ×¿× Ñ·ˆ*UyÎÛe([”¯Dnw¤éyŠrc0*Žm _’ÑÏÁ@VòÂVSÀ¼[‘ª¨ÿ4LXô3”ûô–äË›˜[åµ$Ä|ÆÇ46U½-5Ý£Øâ2ƒïAYÚíoëzô"Àx_gcŽåË{ 7–Ïinèï?]§†âñ³Æòh,~Kž3 LKsi˜7ãì¹qÍÅã÷Œmsqo{æ™hoÊõ¦<‰Óú´Ê<`ÏjýznçW°bÎ5žÏ|½†ò­TT}Ä'ù?·$¬à4KEž‘—yRKÒµU\lxTKW‰j+Z#¥ÜéyêXÏû®@ÔýÊ.Eµœ;é&g³ý¿XÛú¬–‚-üîö,Ë‘ÎWUåU³­ÅnPb‹ÊAžïÍëñ™öÇ*T«¢m¶žÏÙïsÏ5ƶ’XT½“yn߈òf[UˤY“›y˜gnÜ+ò‹žÂý¬·Ýßý|Ú´N•e®wpƒ#«˜eçfMùŸùþfûô~‡ëü6?Çö—SÌç±ëxôQÝŠ]µðbáX ýÉYYSÞäøãÈ9«¿ÍÀzâXa›>м½!ï ìPô™¸OG´ ¤«PTë]¹Y÷ó@º:¡±<êDª¹Á¶¡—»4ýߨӸÄúëZ7ônÜ9çè.=<Æ6=^Ž•ÖQ×7´X/)‹bÎÇÁÓÝV(=:ß乊ÞÄ©ÓÀnaÏP‚õl½Äù/´“#9.òÞoµZn{Á>i=îÝaBvv†«B_®br¬¬ôdÜËEÀh5 ؤ8.¶ÓMpEnÞûzFU¤êW¶TùryXð2ü˜•‰ö釸Z~k¥›`¬3öÛÍi­ò¤œjîûÀ? cè¯Õ°)ÇXo”/—lUloò‰ÒL^Üf¤;®¥«êo‘iFqŸu¥êzÜ|SåpüÕýмPô²ÅUYæmSg¶n+wøDl­¯²T{ËŒ~Ex öc>«¹ÙYN9Öç>Éét¾tóë§kâ~“ÕÖáØYÙS·ÄÒÅØ;ù^%-;lòñ"6?:Ë£°=%”¹2“óélË( Åj ‡ã8JûßXÚtjý2šÊ£~ÜqS³s°ËUPxXf¶nhìMUÖYlÑü1?ŠiÁ®Pñ; .©›S²O„ÁpB†õøá¨ç‘¨‡CÙ¶çÏx.Ýy5¸þuèPf!õC|ôŒÀq,Z¬ON­Î§s•öó¦yžyÈux!Ôuo‚®è*Ý>ú‚Å‹W>ƒ à}[+VQòñƒ»êÈóç6TFSÏŒÍÃ}#¾Ú¿r9×"®ÝŒÏ_¸°zn„À÷‚~ކ•¼-âmc÷ÿ;MGÃx~8Ý¡Á°RVâw6ø›àNǸÏc}ãExv ~lW`Q‡lü8âïíS‡ü;¯ÌAcùŒŸšs-Ꞔ<Ø=5ßjþ»%Êéª(­üyºùËþ=ü âÐþ´ÑƒAãŠ(®wo–`VFÎ~! (vˆ‚~Í´Y–-hTïÇšÖ`g ,ìù|Ýä±®½M9#û¾5°÷ŽÏÌ…4†«t:/Cãý‘Ý~yÕÄÌGÍŠ]–Û9—F+ú™X;üCD|´NdEáßÜ]¦Nÿï̬IîmëÄ©¹áý±±YÆXÕjÉw9*ë( XªÆÅ*P"D¼34å+”õï]Í=xýñ_ÍtØH¤t%›÷æùÛeëj,š5Ìͨd#ëëÏeÝ·¿Åyî‡5½{Þ>ym½¸h›¢ƒ1Öjo˜–±ªÓ+0øƒ!Ó3ï°¾ú%Š%戫øS3_Ūü¢;µËÌ{>šó7lèò;Ú¢TÏçr^´ºÌð(öyÎ#?Ù}3¾hÏF³¶ÆkÈàʃ¿e£áG~¼«ÅÊ×(G¡ë²¼aUÞƒ»Š×bæçÜ7ƒ¯íé)‰¶K»Y3Y±­Æ6{OçÙ§üöíÒ5§!J¸ÓN°X£B‚4«5õj#?¸‰ |ï¶©VNw¯M Eý°Å¦\D¶˜áÈ~ø×KלÌq›ÊGµÐ«Ír^7ëñ}јìwºª®|vòä`t3Úþ…5_Ãq>°0žÕõ• ÛXFÊlÑd~.!°ÀÖ§ŒÓ&«ªe4äa‰·¹Ã‚ÂÊÛ1Ö˜á“Çy¯|Íü±#å[È€XMЮ€1\²æ“†Î؃ùT|À-y.{ê&аkìÔéìÂtÈ%DU½Ë¥¹fzÓw›—uÿ²h[•ñû03áß ¤ònTæ3ü&·`׫|?Ó~Ïn>°iItî¸Í\ð6•P*Åœ‡¦hÝ ß[ê$QômTÎã7‹„aìÂÆR·Fí2Õ\žy?Ÿuÿ æ9{Ç<ŸóuSïÆÙï8~ê´cÇOÍ~÷Ð-Ñ ÌÈPºéºê® ,ŸÍ ×ÀŒóïNFÚå ®‹î 3çŽÇ=" òêÑq°·T?•ÿáˆ@›(`¨Ã?ÙõÅ‘GÞ<_ŒâWý.~lüU['à—ý£,GœiѺ2`ä§ùñA¾O£©ïzâ‰XdwVº5ýSc“E_?îé'r»\† ñ*àþ1cw4¸hT ·^#ŽºˆœÜ0 Þ¢ÒN‡/ÛhÕ¢<Ë?PN§—9ÇàñÇZŒe+Å8h®>‚B½€ã"4šÏ û”¥Üdz#}->li,ƒ®êdµÿíö—¸ß­O¼%îý1c-Võ Ðp®ù!QS®…Àý79¿Ÿ2N?Ì¢X΄ ¼™lLþÖ¨¤üˢ؆L§]ñ÷¿•oõ…«U} 쥬$'ا÷F}ða6§¹¼ÐWj|\r<|<¾Wîuži@·uVöä!Æ»¶»r&x¾óöú–_ˆ‡ÐWzƇü•*¥x>óåšûœÉ©åZ”ê¾wUÓ“ðc¨³l'ƒRô¨æ.ëÚN¤;ž=9øÍlÂÈ3ÃRö,»¹<<ãú|íª<ûH?…ßìü៤֡ÙÈW§$UÕÝ϶˜R(U7fð~] ¯ÜA]í9ÃïÅJ¢õi“¾ gU®®Xîöì?6ßÉ9¼À‡XÛÅn×@ÁG|°Y~aF÷ —–aŒRgQ[GÑE°‚lOƒ7‡å(ž»Ùuí™Î¦+·W‘þãÍöGßw8ªX„¼/Øo)Aw˧;`P¢®h Ê«;’èsþ7~,ŸÍÌ™:Lj®*û ÔêŒÖu‹n”ÅîjÄÍqÑísUcKñ½;4“‡;žyYS^Gò×ùux¹¬°Œ‹ÿôÌmÀ>Òw],äHrÕüÆù£mÒÉIÑ·y¦º:œ•oâ#ûò™põó»qSsn‚…¾:ï¡ûáP&dæ(:¾¶ ÏNžéå:ôhsì [˜¸g«ÁÃrhÌØðLéÝ5¾2¯·¨ÊõV]7úŽ0à2ÎAŽy<…àññãK1Ðâ_šN—’¢}eµúìäû 8çnêäï¶k9{ñ•Ž®ês»©x–èÙÀ¡ øÊÕ?ÊÍžz§gœ¦òáQ£N§ëžøÔ¸a¼Åá„)ÙW»ýÊúéùn¬üÚŠs³§UƒWCÑäY¨™Ï»£¶¿õOmVÆ•ú–FÚÙhlG¢¿°Nc^/¢qëp¸ž¢·£IÐx€Ÿúgl×Öž‡û÷<Óðxô1?ãpV=y­cÍzÆkìivð(är2ß)ÄŠÆh‡rü y^tË”ì^‡ îæëÆòAŸÜ8ÚW³òåÑŸ0Iªû—9Q´ö'þ÷áK–®ËP iªiÆ3”b¼”ƒ>߆¡a¦^P.󛫆Z_¯Q®)Û|Ƽû£Á¾¾¡´zŽO@G@ÃXCï›zÌ»NEùÇáþOäs¶jþ¤©4,×ã3³nkh^+Va›Þ½›mÊxÏ‹Æ÷tΰzThÖÈk!Üs¬ü§!Þ•p5¾„ótôGÇ”:KCÔ:V9§•¾°u Wò>(·_ëË,xÞò°Ó¬®©˜_¬uÊÍšü“ùŒÏQ>Kôù;rrº”—{¾AC>~¼nÛ‰êýd»E!Êšüp¥³è-зˆÐál¯cu6›²­p0>,ïÍûç¸çòìµz–?K¥Ó!Û¼õªI­ÎÌËžò )–ëHs¾çÅp«Ç(V‹18Í¡UÂïô1а ä'@‰m²¨–‹+xN‡•8 ÁåÁŠ®doÙ¥…£÷—Ènö‘C¡âwxÊ۹ñ SÎ~±&ðt)¬;Zª¹éàQçh7ÞÛá\³uá.•Ñ’^dK·ÔYrd7ë±—™…•–_µ|ÜÖ÷)ó9ŒƒsxP¨û^.HA‚þ8\Ðn·1OÂføœœt¼[{kfV³®<â ú;ó¾¡3÷ÉðÜHó+ï›Ë‡Wâx åÇ}|f^æû‰Ó§w¸Ùþ¼1Ì|&gA µ`yõf45Óë÷F(ˇ}¥Íô"y¦ç²ÙÊäßjýçüÛð|Æ× åqË´iÜ_ëWà¾ß†òaúêÓÁ¿á†¼~ ‰Ãû°­„C9>“÷Ï܇¦<;>/ϪoÝ}ÀyÃYÕí«mçϘ"tRæ±RêÌD_qú¬ìÌ¿z[$»§ð…ü‡¿ùx[žÄB ñd_ «Ößóì÷o %º„A -\ƒ>÷e‘³|2 Сpñ9 Œ³X”·fÚ§,Ÿ8uÚ .]‹Îšnp×íÁñsJ‚íEÏ%ý¼ab òñ¦,‰#‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ Кü?“Yé…úçÑ"IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-overview.svg0000664000175000017500000012664713553660046026725 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-09-21 18:41:59 +0000Canvas 1Layer 1 Compute NodesLinux Bridge - Self-service NetworksOverviewInternetProvider network Controller NodeSQLDatabaseMessageBusNetworkingManagementML2 Plug-inAPIManagement network10.0.0.0/24Interface 1Linux Bridge AgentInterface 1InstanceInterface 2BridgeBridgeFirewall Network NodeLinux Bridge AgentLayer-3 AgentInterface 3BridgeBridgeRouterNamespaceInterface 1Interface 2Interface 3 Physical Network InfrastructureOverlay network10.0.1.0/24Provider networkAggregateSelf-service networkMetadata AgentDHCP AgentMetadataProcessDHCP Namespace neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-flowew2.png0000664000175000017500000034767513553660046026437 0ustar zuulzuul00000000000000‰PNG  IHDR)Åž¤sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì]|TÅÖŸ¹»›F•ÞÁ*Ï¢Ï ¢Øß³bï’P,ˆHI6º’ Õ§("$<Ëó©ßõ=» ` 6DQBïº÷Þùþçnîr³Ù$›d6É™ßo÷Þ;wæÌ™ÿ´3gÎÌ‚#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0 ÙÀùgö2Üã{¸M~  ¯Ç£m™íK_]æ%?T‰ÀŸÿd# ܹÙÞ%‘+¥äÈÌé}L-p„¦´"E~?gâđ²#Р:¯ Í57+}iCà·©ñènj®I~}¾Ü›Åg=ÜB+™å¿>i™Ù×+e¶ÈõgΉ&|C ccáäW a&‰¤]3}÷ìrú׿¾¦øêE§(¡Þ(ÑÍç‘þõµá!ãŽÊð÷4¤:²BGá’Û¤&6žÕ·÷OC‡5bÁ¯®«w@§5„MJ‰¢ÝïîðMë’æÍ~øHͤ¿|Q4vÆŒvÝw_þþçîNß´nÅzÉp)ÿ•ã÷K¼äìÎÉ“Ûë%²…î}s}÷m©Œ/»Í³07#ccea›â;ªóRÍ‘wOS̼癅”(Jh‹ø¬—Ð1¤øÁ¨*Ê=–X¼yÏSèá=©ÙÙ¯6¦ŽÁÆ"ƒ|±O óf•!ó4)^Ñ”çŸsüã ¯j»ÆŒ_µÁ(æó÷7u1­D©³„%:àÏ Ÿ‹¾]½=Õëýìcú ‹•°‰Ï½d6R$¤X!•œ1Æ%„Jj¬ aPb†)%2„ ´ÃãðH¸¿¢ó¡ÔuBê;îÎÎîûhFÆæŠøØXÚUêW‹æìŠÂ±?#°R¥2ë®»ŠS½Y4#mÛ%ŽÎ#£R9À$¥Ø%…öÄ-`àj…ÛŽx:Å4ÅXSF``˜ãó~Y]N›~ÕeTæÔ¿ôÀNšK)6A|¿¯…T~`>ä©Ked†¿{@©KþgsøIu™V5 ©û R~Œº½8ÓN‰6…æã`ìÊúd.Õ›©„Ùlž?sB}¦Ëi5=XH©£2Ç2Ï߃¤3ë(…KÆL›sý#ùH:µ•Ú«?óR3 ŸÄûcÃÃDóÜØñ‹;L@éc6ß\jrzΤŒ áË0>ßÓIÄævøº¸ê.y˜¥¹rY“P$ð&á¤W]` š·¡n\‘æ|YŽ?ý¿±  ,ùÝAù³hÂrF 6hµ‰Ìqcƒi"h‰#6Ô,•Ü vKORj)Iõå¤TÅÑüùó]5ÍMãÑÀNéVÅ[ø{ŠîW—ÏT70 °Òp%ÍPÈß绥(×7áªø¨ïJtÒ—;ªJ§ª÷>ßü„êòRÝð6µ©[6ê\aŸÓ,šðd'B¿hÂVFJí^z¯„1{Ä”)U6šwÑð•š9õhh–ކ^]„¡6oiok@¼.êøiÓZÔ”Ÿd¡ÉEÁ„˜]UŒôM9,›ÌZ~Æ ÿˆªÂÓ{,÷|ŽKËœ,ï‘ÎÅòWò[ìž–šá¿ªäÛ­AHÉVPßça­ÿmÍ“˜n 7Ú÷Hë}Í\6‡¸–sÃ3'Ÿj*ãIê‚æh æÍºv£„ÐÞ¤ù CÌ€To%É)ò‚ÊÖ¶‰^´X Ÿù˜á¥¤¤hœ4ƒ¸h_—á#2§ôÓ•qFá €ÁAX3º¸ü‹Ò©?zw§ï±–ÅúÞéèá±7~[€Ýš[>¨tÕé.DY½œ®£ð¶KõMí!tÝ´#¿“Ž…ªõR(ØÒìw)îæ';€S}“º‘™ã‰H³;øÌCèÏ4kì\ßÄß÷Ǭ›;`±×Ò¤¤h]ªkßT]ÞaW´#^ë\¿×2œ%[ t=êÐ¥à¡ê+–ùD©ýƒ\‹pçG“ë4ïôÞ¦(¬/´Œ~Q µ_áî]]s}>i:iU—÷Jë–ÿí‹ö²yÙÞ[œé8ï‡yý¹àñ %\ãæùÓ_‡÷0€OGžg£MÍr†¥{œ†gúïPBE½:аCÝÛ‡º²Ë¡ÙÐn¼mÇñù”¶!=uõ2àikí§QWgÛᢹ‚ÏçøuIÍ\ŠóÍ9Hûrô3ÿByÜŸÚx‰P¿ëÅh_ålR¢åËšpè[&¡è¨Ïj…|îAZ!C\M“MSõƒßBj“r³2^ç}ÞëÈÐx ¼LÂ_ºÒæe¥d¿á›rˆ¡ëhƒ‚èŠø{!¾¯Ô¤ö\NVF®Îy­´>8úªó(ƒæ(3~).9à÷tôËÏåd{ýôžüóŒÉ7 eŽD¼#©ŸC]Äò”,Ö·¢\wbp{”^ uðl©W… (5á½²º%•çuÊêÊõ04…íTy7jòä¶àåf¼é~P ×ÂTâ «~HÙŽžŽ&ØíôÂ<†¼õC~V#o¯"5h]'£N†úWšmç²ßF½™¦p| ümØŠÇ1 þÚ£“~4÷f¾™‚ÉÅ(ÐÛ‰ø7 ¿?ûÜhâÙaªÃW²g[ ä±G°½¹;XJ냩Àƒü9؞̋í4ì+µAäÿbz7Ûþö•´¯Âó¡I®æ_ÛþÃ3³‡B@ù´¯Bz{QÎØa&>vG™¦™3,Ãÿj$meõA8ú;ð+JþnGº?vöœ:Õ~ÿG…iÒR6úbù)ú‘'Á ú0q0Âß!¤Q®®ØqùZ}ÜÕÂ1j€Ý ”LÔ„ë"çL‹:Œ{K–£³9rc ì=®mZd„:Ü—}­¡›4°Ï雾ä ß8kÀI»?ûn iÀ¨h6RÝôGú² èF.Í&¥pÝ)>:¨CtQ²ûÝV­o«ÎîS×§€fŒ–/'uhy=åÒ ÙÍFÝïÇ€11Rš!þα%f›÷C‹“e‡êøî¢-»7á]ë·||¶/#4ð¦új§ô§Vs{Äis|^ÒdYŽ>…/Jæ¤æä•›–†$êÆ¡žL6„ñ pÐŒµnÕ"aÊôñã÷V–Z¬xÏÍòÎD:3iK8fÉÿF]Y›qGei‡¿+ÑÑqCGÜ`h9Éùž4,.7mSÚïjÃ{eu ³ôù(çÛ ÅÕHíÑý)ïJ M ʃÿ?UáK1 Ùô‚ñÁ¨‹ŸˆdyÕ<ÇöÞá¾:‰þ¡¥±Ý{K¨^ž‹:ÿ¨··Øõ6¨!Ímþ<7"Ì¿ˆv´NIOÊ£ã×aÒsâ<ƒA;ýÈQÑðOiT‡¯Ò ÇÐÔû³/T†ù&°ú$7»¬¶ýK§@ ÝŠž‡€&.žu@ h²p4 ¯Î³Œ}·¦i]2Ëwii ¹ÞI׋Ÿ=8í²\ú«6]*ù&Môþ.ô-ãàï³ßÙ×Êêƒ&Ò5-Ó?õËÖraWOŸ«}¾A:…£4Kò;HXoæNéóˆoL¨Œi©oï®â~°c¢É»!’ôcDÉD‹€”O8Šfu,R¾B÷¦'Ð5n®/ã{ô84æ¶z äŸD“Téè§ ±m„ ÉíÕNGÉ^P“~éø}Žr[@7—ƒÖÍth…†|Ø‘€0›!]Se„wi¦†’”änu›ÝÑï4ïìözÑ©¬¡çp‡Îê¯ä'…ç?ÎwD¼†×áj°óÐ Ó©ÓD¬—œ …AÞ^1¨òUí­CËÄ‹ñÃ\ú›È3TË2@BØÎ=<`cÙT”Vœðn±§Tºj«¬£³GžðM,[fµá½²º¥ÄSÁÔÍÈ)SXþšP¥áÊòê|²–•ĬY¥$kW†/ÃÑr­=°Ñ@ a»Tn·Ë묷´¬ê¢0¨¿Õs¦ÄEpÁeR¹õµÇî=©ÑP© ¾‚ hV„ê˜æ›Ü×Éú›ËP‡ !lXË<Â…ÃáýYômÖBÛ[×>ä FãâQ§€Bïg§§oOl¦]C4ñxãcÇ ]+«¡@eohçhZÅ'ÒÓþ2Ÿoh‰B•¨î¥÷Ÿ9ò£¾ KB, Ø`ÅèÊBJŒ€¬.—pÍÇío‘?ô¾Gz_S?ZOGc~‚Ê…P-óßP&J—¼)×7v[µéJåBR›Jv@'Û|“vîdC/¹¦RÃTM{S#D‰ÞÁ¦K¤B—¯Ø3-glT@òE§Ý£óƒY¶¢Î¬³»ýŸåß‹ß,?%à /"—Ôž Cϰ9ø”®8\íhºÖ¥Ãzù·ô ÌžF/žˆrLUf`%„•E©¾ìHmÜð\–6XŽ›C¶QàT;Þ+¨[°EAyÉÕ´4CËN>ÈÖ žŒiþÚ9YÞœï"Ýkýt”A"êûËáKšááu#pê--~0Ç71X׺º&¬À;½çᇓ¯ªu+=îTàM!FŒÈœ|zU‘ëŠ/MïRÚжœeó@KlÀêT4ø% ÉBµ}u‰ýž®RÁV ÎÔ\!!aÎ&?árçX×°¿Yéé[ÝË„o 83ìuð±‚úà f›e 7 çIÀñ L„.Êõ¥8Ãu§|~d7˜=75ÓW¥}œ3"ß×PØ’Ìõ‘’5õ„íB£mŠ„Hïkㇵë[ ÕJŒÙ0ÒÃÀ­É‡¡šÄ™5r¿Bu„3fðdË¥ýq‚cŒÊf¾·rõ%hÄgGÚ²š ¹CkÎN•ÝCé‰Aä/…à ¾BI™×$ a0ÿ´Î-Q£Î> çØGÆŒ!-CdWW|a<ŒU¦ !åqJ¼¨HüÍML¼M‚øüÞ'“ðBBíØÁ²ë©Àa{Ú°jKýÉ}IOàrÿ'òG¤"8êpjKЈ¾ÜûÊêƒ#p–¿ÏAcud·'E;VzpÉÉ< Òa‡v ÒzK™êQô÷ o™‡¾õɪ„U'¾ÌJÙ `c‡Ží«bG  ^¡§$IKíZqŒê½¡LË"]ݧ]J3}÷Ë-‘¨´A»˜Hï*òÃÜ««õNÚ»KʇDº›Ëû"Çš´pÈ“ÐAŽ Õ0:—Ó†y³ÃÛc0”~ÖÉý×vÜO~\ÛƒFÐÚ_ÉsY¤ßa˜UíÄRƬÊi˜×àœ‘—ÃXS/'¸TNIZJ€Và!ѽ¥Ÿ >±%Óœc«»cÍ{eü¼ôÒKZxžèÙ0Íìxd¯ÓÕ“q™Ô´ë ÀR\fæØ`ü˜š™}­Ž®±à½²º•àñ<‹²&#˜ë, [(q !E*Ì þòªäí¨§õZîßÝRQp”O0, 6‘ë»Q]ÂàW«É v>Aý`Ü;GþƒÄSÀãFv©ÕãÃÕ_µ.Ÿ ¥"Ày&ÙŠYi+u)]=¦ø_í5Z ‹ÄÅô\¼mï)3’o‘mD½]|Þ aÜ\¶V&8jÂÜdÑ”Ø}ÁUVìà&A–V.­…æuö»ðë\æ‡ÂíÁ®Mmòƒ{•õ úŸÁ ãÃÃósíp×.:Ç®)< ”ÏWÓØ‘ã¡‘µ¦74ËtFvh²éÀ ònôUÅè Ѱ0@‹«ì÷±º’°A€ìN„ r®ÿ §]³ü«mD3+Ÿá4éÙTêrtè=œïçeeü'-Ã=®ê¼|¢8Pò„õ½ :Àaøx/#hGþî–žíb{4TÖûÛ8iÕä /DJ»ÃãjÉbm¸_´Ï¥ÆºiØÝp Êóä’@àÄýW¬y¯ŒŸ+¯¼Ò|ïÛìûÂÃ@^þÃéW:𼿆eNA½0îôWc—ÄóOưو î•Õ­Ç}ã7 =ìâçÈôŸ‰$?¤Ìéu(ÚÅâÙÙÞuÄG•NÛ¬SUY7 8oEzä|ÈçCUÒ®EÌaz{‰nÐn˜1ØB>?ÉcüY(O´®ø¢s{P'¡ESçl2¦‹{u20^1ÛÄ×#]¯–(ÓášìRþ‰ú{qˆuÙÐRO[1nÃéß‡Ö £ëŠ!4ì¦1)nPX ZY}°ƒ¢|Rp¿½”2Ÿ¨§ËæeMüÂã¼–žK4FÊãwí Ü„>e4âÜ$ ÄÀÿ4§A°3ßWKÊ­~4ŽQßxJ’íÙ:5¦ˆú‘n_À“Ð2TàjŒJj0>• а†b{ßÍÅ©?&$–S‰µ£³?6ò÷;=iRU˜OäÉÒ¶Êþ˜Á;Ìéh»·ŽÐMÈ«±ßq°+YvÃÙW‡>’šýýŸÀ«9}d.œVuŸ18-!íGøo®×›W]ZááÁç2ò>]èkÞ‰fEŽÅð<ÑóܬŒùÅ¡Žžß{=4+§b Ô1ÝUj]_¼?M¼™B£]>_}¶fÍè -ò«ÊA ùÕ #e¯*Ã*µ:¶êï~UE+š÷4@jBó¡F¸pnÐSZ 9‚ˆBz£:åë]âÕPæP† Œ¡•Ô^±ùŸ5áp°íq vÇy0™Dïd-]P°•_¡M'‘‘¯í~E>zY~ªâeàð8埥ÑÕsê9Yé˰&5 i&HÓ˜_Õy´Ù;àïâîÓRÒËÀ¼=>nê+OŸ}jŠ )5E®žã“{ÑxÚT`( ެo¸Dälƒþ3bvÔó誷ç64ª¦2g#F«…§ìt„öM-ˆ”j ‹¤ŸKÉv¡ìËÒ§ÒÎ.ü¯ Ì,øÏÆäG`…ÿ"Ô¶ïW.$HkðÇWpSÃéÅÙóÁA~$:~ÛÅ?ï¹YéË¡]Ây!¨¹ÂèmsŽ¡ªNqOìØŠ l5/'áõéJ»“Û¦üw?•ßA€Ý9óÊHgtß•þ»]Ë!Œa%G]N[X˼«£‡ÁÇô~y\!ûè|=ÿ.IÊ%UC¾ X-”]…‚ƒÛ6 @œŽ çQx·p—Á¶8S;wò¶ „òwÊàûòíQYš C‡¶"‚£m¿Ð_aáëÑ–G•dmh¶ƒšTØÏ»gÑ_ö2òõg¢!@;€Ü®Œ`Xúž»X!ÀBJ¬¬:d8IÓËÐÓ™ä°ûý—CÝ8Äégßà l0T¿cÑQ­Á¹$÷?©½±keu€ñ|,Žè&º4óÀY3A÷Lt{4·f Bô®¶3tœd­·÷Xüíê‰áô†{'_„Žåüpz†B÷ ¿<ž¶îŽôÍnNƒKeÇê»Ü®t¤WÅØaÞÉ‹D—¨"Z‘ÞÅ´8´S£"¡ŒÎ/AÙþüNéNøÔN3x·y!¼#Õ/šACãu…“nù£¾®y·¶+IZÅv«Ça¸íˆ´_¬ÔÈÔf®ô:'kâ Ôodq–ÎÖÇ,m€# ÙbØõ"Ç—þ#ô'1ª·.)0ž‰4;§øtnˆƒD­nÉX]“žÛP/¨¿é‘ˆÕ”/,‰¬%z{N§s^"ÑžãËX |6AŸy¤£3ÐŽ~ÿ*:,÷_¥¸˜<Œ%M 4!-ŠMç5æ Ûa;6|rFujÏ®â™4q¾OçúÒWÙñj{mÕ*i$xþ åû7lI¾×¦GB¦µýÜöp\õRÃxðªËŽ×|[CØ&¥:À)Ù ¶_UÆ™Ùó&yC*ÍŠÂÕØß%}PÞ†ÕüDiŠNâs¨ø[ ÷—¦ÂÑòr.šüp'}ëð¡ãYt)&$»Áy. ©ä‘XÝ«k7KD¼Ò™€“Bä{ ŒÝpFÊö[4L7tcŸÑR2ž Ñ ÝiË¥§&W·[Ž3tµÐT* ëÞg`¹á#L‚è<õLa`}»<”NG×_—mPK ¯ça/ìÊ€¹ÃJ¾h3¶føw¢Ç]©4mÚ¼IïØ|ïÀg ÞáDQã5Ü„ÿkt¦û`„;gaŸàÑ÷õÅ}P€´#ÇèZl꽕i¾¿håÏÛß˜Åæaö6tÌíQÖý±cèhäÙW;·’Çï6ÊÔgä™KC}|¯Á¬§†ÊãÅŸ[hKyo fÿqÖ“úà]Ó\O¦~fáÁV"´§l~£½ºÝîûºÞK·©?·œ‚ÁìCä-gÙôÇé²ýP7­Gˆ^BŠ61P` @~/Ò÷?á`¹EÐh¹£5’ï)þØ:¨DZöWE›~Uár²&|;,Ó? †ZÞ`X¤æjÂ×LX‹¼~Áb@žÿ%ÚÏSè‡ò]ÂüŽ´“û“À׺Uð”d¤œµß?x×É5qYž™½Ùž\¡‡ìQì°tŽNZæä‹Ñ+Ý\Š:ô0„¤:ä–žƒ6p nwBi혵»R?‰C0‡šõ9´eSÁç´d–˜½qõRäù#ìTZ~g-Rê =Ù‰8[…&f0õY»Ô9¶Ö¤8Ѩâ»(Äñý (À€«î  ´C ¶Ù^#Р݅¡â]=]ïÁ;´›ýã?Ñ Óqà“Ã$£PøNÍ(ÌÖ£3™0,sò™ûcVu§šîEö¤Ñ‰{t3EЬø0·ªHWòžò€ÙU KÞ Þù‘aœk ŽÂ ·WátH`bÐsõ%g£Cë |täw)~oÓ¼~€A‚ Ï„øV¸Æ„Î'Ѥ{¥‡p'¡Œ¦N×[1Àõ‚ÿÓªYéî‚°4cñH³Vðø ä0Øö¨s­tM5xß~h€AóhÇ€Ïr³ÐÍ»ÔMRóï ò¬²1ˆ?F÷ðk…²z ‹»Ý—quÍûܬ‰_;ÚiDç—üDKOeˆâ¡kæn~4â?‹å ooްS0—ê bßÛd¬mæÝ;œ,&áö†ÝEâ`b|rAªUÐlÆ|‚ÓÕÕ'‹q›ðkMøBW8³çF”Ýç(ÇCQ'³¡™ Ÿcô‘¿÷ìg)=åòF6'^¬‰x,D=øØï¼’p€óœ.FXœTKBšÚd¬ÝñfvñtígŸ¢íŒWÛ{:YÅ6 ˜ù"Mø\ÂóÊë5Ð>Å*kSÍ Švÿ}ärËÁ¹¾Œ '²µå©)ÆG¹³kˆÜ9yrû@¡ìÖ²¹¶&Ú#°b>+ã™–jô{O)8?4t4sÑ¡å6ó¤L ?)’“JÖñåû0êNƒžiÉå£×P§œ¢ ÷–3ú¼9Ò0‘âÖÖÒ^úË/튲-ÓÞž—“ž¾‰ŒhhHÞmþ¨“×KDGÍÉÒåÊ›ýÀ}›£á?x·óPÙud†¿»áÑZtWÙÛh+ o-ùˆCLåÚÓª…Ú/m¸º|‘A+v vÃ8¾%š/qW„G´þÔïãÔ×dÈ;ç’PßcnÞÓI÷ˆön¡í ˆ¶ëÃ~‹6O®rXH©~Û€‚Ÿ'å€AÓ``X†ÿT¶»r³½'ÆsއeN9QšÆƒÖ†…øShî»s³&|7—}Œ®›øÚ«<‡Øæ)é¾/Á¥v+!Mún]çi˜7k‰&ås9YÞ¹uÓo8ø|O'mÐ×wÅ„ó|¾[ŠçUsšš9ù$áJø5×7v[Õ¡£A,Þ²»[cÄ,zNHwÃaµqp:X‰‹‰zwrø»Ú>WÅ¿M?Í;½·)ŠÎ ä]? AEG¿ñ#êãŠÖ-VRyˆ¦îº¼„µs&y_JÍÌžj 5¼”R¤2.€† ŸsRƒS}9)¹¾´›_X‹{Nš˜fûÅê ÿ”Ö¸iÓZìÜ[²·ÿã]nñ‰2dS˜Àw—†. P}¾Aú°LÿMx–ÒsmÝH_öqNßRÎfwºÜr³a ¥2Bù2& §Dš0Ö6]Ž_{XH©=†5£ Å| ¸SFfø_"Ûûg͈”…AöoðÝZþMÃóÁLõ]'×Ð8ÐŒjï¼IÞWœþ±ºWBë!1uëâÿƒö“•’üÍ„öÍË|!yž?ýõýj}·­®òUkÎjAÉÇs'y߯‰¨£VU÷éc’yúÏWA+5‹¤a^ÿ"|~rèüùó]C‡ ›ì/Ä`6K{W }û0ðªÍD±¹÷t|NÁ%¤¶Èö‹Õµ*þítvï5NÇàz°¦i7åde<[êÿ®óí0á:/ËKZ”˜¸'|ß ÷Íè6×wßÁÇ ]i‡ò̼ãÁ)ô]¯ÕŽw|'°r€ ÂãvgÆ ¬Ì åTʑ؂]ÂÙÐìfH%ŽG‡¶ýìXWM§µhë#zjÔ˜ Þ…Æ\Q2 áNEøžô/uÒ…†`f•‰èèÎpÎ"΋˜ïnžçϸ‹ÂÓlR[³¤)†`)¤:ïÕ?¿³;}†sY i~†wc5(1â~¤9ƒÔT¨ÓCš2é{³… öüÞš—íä|WÝûÊÒªÆwJ…Y³T=Àã B ]Í´‰s&NÜywvvÇ• þPÂ#/ý5ÔÀ‡ä,hPŽT þ`Š6ïùœü]BLŸ›í}ƒÜ?5%Š‘¿Q6¿!¬”:é†Ùøv)´7rý¤5‹© ¥UA¹ŒðN;\Wù(ÿûœß°ûý— CÜ¥«(ûçl¦†{³B}ð¨Gz®~Â?þ'Û¿.¯´¼±!}p¿ ¼š¡þþ ì³s&yß°Ó5yr[½@Ýn*5ï WßåÒ#þ¥<2OTP÷±\ó’Mc£¾3gÑiý›üPv‹¡I¶øÇÕ´ ù%ù˜2å }ŸqŠK“×"­ÞM/†wHH¦9Ë@…‰íZ|JáÉUÖ.ƒ!„ˆÿ6-ð´î¥K­ ùUrCåj¢ßÀ`| ‚%á÷³”®»sJµ‚TD`ë4´×!À¶#ðùÖ%5ôOl²è+nBÙ\ ›{RÅ^ýaøD`€—ßkäçú3C‚h´eJ4AcH·÷†<#ûAôk×€Çö‰Zu˜u×]ÅÔ¦•”Ï:…•ªê¼Ío¤k˜€bYB7º¡%Û|/0†°;hB+@G<ƒà…P;_[iþ«Ð)/@#vap‚#-ð<=ï-Š ½o üŸE'³ ßÀ¦ÂO?t_a|]¿¿Ýã{¸ …%G† x7BÍ©i÷O 4ð'u6Ò¹\Sêw G³OغL˜‰|iÜU÷›P>€Áüe ãpÍó  ¼Šq ÂŽÁÉ;Ž÷¡[ª š~»»zúL ½¨ùMÅi«’J”…Xz¸I¼‰™èz¾1‹’3¥4Á+©•i6Z„*›~F¾Â”y90]‚e ÓöW2ásЇNõtìÓ=92ÈCg¿iÝŽÇWQ¾©Pæ"LÌ4eVBø‹¦\:ºÇýÞ»ãwϺâ:”O_”áuN“ÂAˆxÌÁõ6£$!øŒD=ÌGÚÙ¨³ãQÿ\XD{e¤wÚ‘6|óE´•QŽßQR»þoãw¤4\FeuߎOW% аógþJÏ Í4 ¬(-]¢grzy.. 6?Ä«wð»ˆ]zg9%ß%4ˆÒsUí2IˆXðoÓêìÉÀÀ*7£y­:g¿ˆpMË̾庯PÍå¼ïEÜï¥Kn¤à©99´oªû×!×o¢^Ü  RtÓü õþì.hûíP.ûá>€ö2å4åÖ*¨·îðfLôÈE[¦D4NÉÓ'ÏÄè<”Íãˆ>ÎÆ÷ð‰&¹hê|0dôÿ˜$ƒ¶½eî¤ñ+£Å!ëw}&ÆiíG D/IFgùfa×Áî`&Œ6ß­ÈŠ=h¾g:7apxI)•g°†ý%š§Ò¼“/˜žNö|Ì>&aðÿmnVÆ|;5.R¦©åëEgÀÏšb}tä—è”Ñý þË)¼­ÎVš‡±Aÿƒ:Ê-ŽrÌ®ÿ7Ü;ù CoPÚ9~+m ޱ_d¸\ò<¨øiÍ<¢£Ùª‘¯/Dµ¹UëÄK}÷ -‰°šž¥ M4C‚~¶{¸w€ªßÒ`ÍJO§å±ù©™~Ô©NìàÿÕ0oö¡Q®ó·i…®Å[÷ŒÀö—t÷››5ñëЋ:¸‰¦\0X¼úðxh³pÏÃ'çïÈ?Ø?¿›Fúf7Â7Êš™ÃoÊú}çÒ‡¯¦WCÈ[ÁÃYÎøÞ–;µ$Тä|áä“€*ùQ—~¢Tü¹ 1:ÇïµKø‡/µE¬û6]2Š¢ó]R…´^Tîàm%Ú Æ3(,DèVЬ{¸Ïÿ¦P™)Ä߲ѾGZèûŽC½™Ha£i—Ô6bÁ?¥g;`¯1 mVñ–=¿@»÷X’§eî,ß]{ì0tµ´­ÓѼMÞåRP,÷ÏÒ«Pë·C«=ÎíÑŽ§%‘Rÿ'¡%üZ£ixM`оR`ÈývŽ?#ÝŽŸæó/‚°ôE@HÒÒ’†ÅrU•©‚g/,¥ÓÅsø©>_åý@4uÞÙÙiTtµÚÂö‚Ñ(uÀ‡°©(8û öÏM5Y¬*XØK·gÚG²Ð ©‹’m{OÃàÙ]s‰§aT²Í ºižêô¿§-´˜-l’Âh¿Cš— Gþ‘?€–„„” #u6ÂRœ ‡º³ÅÅòžëOǬKþª„yuiLëUøâ¹“2" (HSQÇnäÐdHhŽS.|è¾ûòñks_YÚát±ƒ·j5vÆŒfáïjúŒ2ºé‡5P0£Ä€¹»Ü׈.ºrAaç<ÆÖ¢íÛQtÈ%;åï ˜;I©S§¶Âó >br5ôD×OK—8Xj¢Jº`]“›eW ”›–@|ìô·ÑÒ]…+y£èk°\a˜Í=!á‚£Òn®Ói7IPc¢Î‡¦~1çŒ/h–:nµ|½ð ÔšKs‘fBDÛ.cÁ?¥çtÖˆ’g‚Ÿ‘‹0æ]º3^;œÒ·œ‚:ÞÍL”+à+ïà{ éoŠm‰ú–~ášåv=ë¤2÷ŒÀhÔ4½œþá÷áeZæ½’Þª”`øèê|Ú•<ìÛ^ðµää ÐYIP~u`MÊŸ’ÎõMø‘tè¼Ãõyl7]`u(è}l'Móº7 1€)¶¿*ÄJ3¦øÚÓö«èŠÙ:V5Þ§ú¦öPÀñ.)ÆšR¹4©k÷g®E¯=j×Ð@…w‡‚ŸÿV@÷Ð<¬Ì;YÉn%ó1ó›ƒÁüMº.|Ä7fG™¸µ}¨ mê¸ vä_ ¡Óî…™p[ð` þ4LµMÖŽº‡ @ÂgøöëJ¯(Çß1`>¨yK-¢m¹¸\®E¦©K[‹†êBòy¨wÔ–£öÐüŠ,ÐÏ@Ô\n-#FtXÚƒ‡†^Jm1f®Õ +˜&ÕmN{…P\Ç mùÅNî•)Ç Ñ ¯š£^µEí•t»®PºåÅâU°e e˜Çœv6ro±äq^¼ž;aÂngMi‹MeŒÙ,–öb)iÛa·ŒµDIíZ ,ŠìR&JeRØ>ÝÑ5Z²‰8!'úvY[þ<Û÷°·Á²8o¸/û(3 î0:6Gi}Τ0X:;Ĩ.OB…Ëwh‡@ IF¿ag¿CûhC$ŒùÝq]c¿iÙÌSfé’0‚Aûn” vºFS¦vx·ç [ƒc{E¼F[ç#FóDÝ÷CƒFKèCÍÈØöšãRâ 0:»½³aßq-:Ò¹P¿¥vœla–ÒŒzOô¡ã)7hA¥¾Ö>Ò=fLïµ4£.ÐóÿNm'÷iÃþVË ¬Þ["ÅÅPg?Kêltv3ËÐPª ?¡wJÀ_b¹h¿C§UŽ?û-:ÄV¸€ùktžb+å'±Ü.)mRócÆ´‚W :ã\Ø½Š™ñtªØfŠ‘&¶i˜E5! l6ÌËÊ|¢Zq£(:hƒúï¤Eƒ¦àõ ºÿb ¶V:J¾Ž:u/ià?øü6Ç7ñ·Šx€Õ¢òq¡÷Òü9t_Ã,±Ý…3IE=z¼üOhÚz©©ÝXBxer¹¾ôU8þ@öEwÇ¢9,Ēćîf®ËÈø90ÂMšor_3`ô“.íð×.w«ÚÒ; æ-j[G‘ñ¹Þ`Pì— qóHßôNº^|*<ß§åzYvYþƒŒTü?×—ñ=ÞÞ†% X:žê›|„•žP) j³ ë$ëfÀù Ê>_& {ˆŽI’–BCnÆØ±Ð~†ž#ÝD[¦vÜÙŒÌÇ’£ýXù5Š:_9PþqPÆI©]!›=vqŒ )qP8ÔéÊœz{‰ |ÐwMFGY ?Äf{k ô”­-C‡ôUèE5n`»¾È(TrŸ^th_Œ™è›t‘À,êô¸CòUáïh¼šÇ“ð^ˆ´T+ ´ô =;o‚;e*œ¥9ƒÒ=i}RÜÍOƒ Ô_ïÚ[’‹+ÔÍuçp8ÛHÌ„û`»æœI¡|¡£ºþ1MÔÖ«1%Z±ê•Ë"ðvá“A®] pnéy ¶S6Šìð8‡އ;Ø5 ÷«Í3Bn¬žáòÓ\¿÷ ›–e?" !ÐîoôŽfí¸¼A?Ì„‡@PyÛÜgÜ‚çr(ø…œ©7"öæ.Ú) Cž¥7dMÀr‰ýPR È/´… rP3Ï»»ö`^ äLÐ8FÓÄS6ê¶Ëšòo§WÕµ¹+å?ûÌüÙ2`^€°«ÐÞÖ`Å¢€êä®Hñ±¶‚Já\Æ3‘ÞWׯºeZ-úÕ«óIC€åm?”tC«c¿‘{Ö !uj½¤Æ‰TˆÀì¬ ?ÀNb*ÆñQèŽ)ÐíÁÌNæ«€‚‘[åìFtí¤¢-ã¬q`˜&Œ³  œëüšÞ…5 b¤ÎFÇöݾq´C(èh›'¶&Z‡í‡«u*§ÇB!ÿ¾Ã»Ò[äKÍôݳ‹vWÀxr8™«±=²Ê³ù6~´á08a”¸yƒþilqžÖL½ß¢oWß‚I:´UÑR‰.°ÉÆ’ÀÍ0‚~7íþìñšéþCIýXä±+´DG%ºPÕ)Ó•ø>V¡p,ˆ¸ Ô±´²ßŸ×à?Ú5•âIŽZàÜO¡ò;šá™Ù–ýIcwÏkí=b™i Êá&Ô…%.éÙŠs]`' nDÝÚnOðû4ÆrÓ‹)IjUI@kfæÙ¦©H¨oÓT÷7š“ûA¸èêqkÏÚá¯.éZ¬›Æˆoz’e9m‹ÀÖ\Øî<‚¶±Îù¦hÛemøßßv÷smi¬óLä©\ë¥ ´5¤d(óVàö}ˉdô*hb‚ùqLÆÀžh³^†l…~d–ƒÞÍ=Ùr_à†€‹±Soôkßâ蟃aŽÛÓaÊ·?åªï¢-Óª)•Q:{x†ÿ:C‰Ù(ãù˜¤¥„ûAvkVÂQ‹gÖ¤ÄG9X\XîJÞÄîŸCÜaÛß42BD¶E.7æwüž‚­J?©´Ðš³Û•ð„y 6óÕÞÀ®ü@覩÷0AÜ‚GÆŒ)´ýI»YèÇôÎ%ËΦi÷MrеUw-Ñ Š7¦ù*­c{ši§U<;µ©Wrõ´¿4Ç2ó‹±ÜeãL±‹û´hB‡{+Œ…×Áøøy Ì¿i"ág¸XÜ“ [µ ü MC½£«’U(‡y°9.ô4ªS.´µ‚é×T¾Rs[K=6- Я•Ö‰/¨Øþ±ºB@ÌD}yÑùƒpñѧe øä€5óŸð‚÷u0ª¾õË¡Ñh†ªn…-±˜›ô€þ+ØûÊñ¡.îôؼFªûØz#Êceøî;]nmq>4•J~Ž­üÛïèÞír½IW´JÒ¸”qѵ˚ó_&±Òl’Û ÀØ«ô&& ß`ð]Œ~`,8|˺ç9wÌá ´q®2ñê3PŸ -¤™¦aíj#Cb-IöÇÄè`úœ©ŠW£Ìo2’®¶‹¾L«MZT§Î‡S‡&l0òèÔU¨Ï—û)k»ux4~ŽÐw±khñk¾žPë [*2<¥ï{äï ´™í]«ü‘ê|k »û™ÇöY˳4bÅ_UthWÓÙ}É«Þiö¼5×ñqƘí—:«âµ:ïz¹P^é ’âÄ"Y™, ²ùùzG·ÐJNï{ðƊʱ.ê~4åQU»Œ5ÿtÞ,mM·Q8'#cCUõìß´.R$ï ?OÅÎ-ƒ.Zùs/‘¬œÝ»÷–ŠðµÃWu¦L«¢QÑûÆPç+Êû3Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À4Xd}p>"sJ?Ã4ÎEZ=•T]h‹úH—Ó`F€`¢A@ ±W*¹a×¹4×Â9YWDÃÔ-u&¤ø|O'åòîRÝ-”è&¥0…’›!¤l@¢ûê6[L`F€ˆ)Í!¤tÁ˜ÕQ)¡ )ÖcÌz´«§ëã>ß-EÑSâ±D N„”ToÖ09ÝCJ¹¿çµí­9'îŒ%óL‹`F€ˆ%#¦L9È,0/TJ]‡ßy˜`ÿúÃrý™ïÆ2¦1RR3ý£•)’R}©ô®œlïÇѱ¡F€`øA -Æ)Ä,pô©‰±¹YÞ™ñÃ]ÓàÄËlõˆòáépqnÖ˜5±¤Ï´F€`úB`Å'ï¯ë?øªg¥Yp8´*cú¼{ÅÇïV_és:«n1r´Ä£”|ÿ›ãϸ KÈßâ©ã‚bòŒ#À0ñÆ=óøC¹õP±Xî©69 F€`F€hj°ÒÔJ¼ ä—N‡¼;;»ã¨ ?¯7ò–™}+N­þ¼°Ël2Œ@=!Pëåžzâ““aªDàßÃmòõÂIø†ÔuØ"Øš"¤fø·ã(ë/…&çÏËÊxªJ" (€Ï§4ŸOâûg±s©^ÿ{òÎv¹´óæNÊXN™„¿‚s“æ’Ë™ä}#ü}Ÿ•舸Ç×8>GdF‰kRe±6½L›6­E¾ž¿@(u+Ö‹giÒu¡Û#ÿª¤ÌÂÁK{5%w56Tòtÿº;}ÓºÕE¾LCÍ;cF³º Í4F€ˆÖ¤D‹‡‹[è;öæ½ »»¥§ßþñ?9˜¥/–>êxn·Ã3§o˜z(øîèkؽpþÞÝEÙkt£Œ3Á0 Rd±1ÓNòÌ ƒ A9CÓ´´'²Ê(Î`eî‡eN>Sšf&–‚ަ%íË—n‘0!LÀ°“ø êÆ Jj=LSB¸Ãþ©ÉÐrGjföµB™© Þ¿BÊ'r³¼3íİÜt“’âÜæî”; é™…ôÎÀ¹B¼ÿÌåqžë›ø{(¬7û>!Ì ñ…Õ¶Ÿ}–á_Žx>ƒws†{³†™JŸDïŠ÷Àc4·åf{Ï …÷f a#ß"?inEÜ7º¸»¥û|·Ùa*ºjRýn*™ AåÔÌÉÿ—›•¾¼¢°¶ª/'E[³°ø4øô?«q2çüÎîôáKRÀìv¥ÌáÓ|­¦9à±Ä¦å¼]Ø: ï‡ \G|·ý[—Ô¼s²Ò?q†ã{F€hœðrOã,צ•+C(…,|tï'£Éøð ÿuRïcÞ‰p7~^ è­uQòeZæä¿†Ñø‹!ÄC¦2ïÂà=WjbÞ·„À2?Í›5S™êA Æÿ‘R…Aw=žIóN¾,DCÊvR¨ ÷éù“ Û˜Ä0[£@IDATÑB“pbå fÀør¤oz§PXav†ÍoßýÏûï0˜…'²Û†t­­ùÖ[©Ó¤æ‡ŒÒ2üWáý!”KCzÈß»;,OÏ‹êThSÈÙ£ý? Óü§Ï7?ÁJ«‚?z¯[— SÜ!í¤y#pz¶-ä²_vFƒÐ6N™æ<àýæ7€·y0ª¹—–éœáè>5'ǺKq{0{ œHÑMóƒÔû³…‡çgF€h|°&¥ñ•i“ËfØ pü6tèPÈ•;²³Ø³«x†òÅy~ïµvhŸïƒoЗ,1Ms&ד!x`Œ:©ä!nÏA=žð²ÎºÃ›µ°X‰ßL)F$y<‡Îò_O!ï|챋·ìÞ¡„q9ÿŒ QA‰–¸_Éñ{°ý†û¦¼ !eµ®…ý¢vó²Ò?Ân˜ÎÂTw%ºÞ°Ó'à!±xËžÌßœçϼ¤”è3¨¾D¾ž‚uAŽ?ýíʃP•œ›–áóßnèji^`5 f“*гAÿe QÐDåÐDýo¸wò†2Þ°ÓL:µ•Ú˜:ÏûÛmz£}¼”ÈÿåXÆ©õ[‡½ãÜíø'|ß”¾|r˜×ÿ;„§ixP&?0Œ@£CvŒ@G@Š# IÙM.öî-9E ÕƒøãÎð>ß ZŒÙŒ|pêÁÎwX¾xÇPÈÿqæZ¤÷;~Ÿ9„YwÝU K!Ýt/šò¼àô£e„{óþµ½/Ù¶÷4#Ý¡¥xÚIK%kЬ wÒ<Õé_Á½Õ/Ìñy?SB›üg¤ù&GÔðã«+ïÅÅòžëO‡öCþª„y5yÈ}H»®\Ò•ŒüŸé»g–qÞtúÑ=ÄÄkHãâP¬ ÀòÒ2˃ÿF Ñ"Àš”F[´M'c:6@ÛÑ%ª+u(…syׄ‡—š¶Z™†0tý0¼ûÍñ>ÏqoÝBÀØ]ÆA°-Ëxâ!©çp?M¿˜¦8;Ü¿6ϰ³9„â#Ó‡y³¦Ø´T!qðQ4huzÚ~Ñ\=îÖ麾ãïJ7°ì£NÛ)&—‹š‡B iŽÂüшð„|bÓvyàWÖ·? ŒC¹$ùøÑùBXšv‘œð·ñÔ’(®™d‡U¦È‡ RÆAðl†Âý†²Ï—}AORèIrk~`F‡ )®H›^†4¡–JÜôÞw¿Ü„Ü?U¦p­Bfìp;Ë„Õ-?Ìѵ_ËøÇà¡ _ï 2egýJ åÁŸf.¢œ‘jªï¡v*Pè Ã#r¥)m tAB¸µes}_E TMÏy“2ÞÁ¢Àëd£X½W.ºT+ gõ,çORõ€€µÚºrhYdvÅe7ùÙt"]‹ÓÁÎh •¹þŒgœþ|Ï0Mr³»¦“uÎicAÀìÞá }k°øÁ4ïôÞ•å+©}³•˜„AP¹¾\8e^ÁòÏÎâ°²ÂD¹€Õ÷Ð ã"g¬á¾°œÑƒø§!‰ÝABµ¢3PB~¸Q¢v+AœÃ(kÿâöàhy™¯ F§±s‰)®Ñ6Š…n>B’GÊJ.‡5„„)§jæÔ£¡Ô9;}Þ'·2! JCÌý»ŸàO'çBÃr–3.݃&íÀ:‡?oŽ ?3MÖ¤4²n´9¥(iX’À¶à—•(þ gŠÌ.ù…[i[LÓèƒí«0¬u½:7kâ×dÜŠ1·`«ð³Ø»5Aª— ‘`ÊÀíˆ9–Hnðù†–Ä,,GÑ2Ò¸´Ìì .÷ûÃloêÅ3±¼¡ ·+d7"…ûÆTägºLP«Œby:–o?¬ö»sŽ>lÉ{+Þí¼÷b¿Wóx 2âë»oKš7; Æ'S¿Í#°T" ”nž#Ø“„tÍÎÍšðÝ~JÑÝÍJOß \ï/Ïcì×ë´jø옺Xé… æh·'aiŽ5 ý ä}Ugשÿ¤8³³½ë@ãß<&¢ 6$ºÜ J Õvƒž}/^· Òuü7÷dË}pˆÍbìš1ç[”ïÁPu¡Æ…3c|ŽÐ|Ë0Ö¤4ÂBmŠYÊ™”±8Ép4òþ.~w …nê_š8( ö…ÐPÀ"èð œk¢F@!pu±’¿êªd•©Ä•l®ÈÉÊ€ÍElÔE.Íu1ÎZ™P(ù'ÅÒ2LW,e Îõ¥¯²SËÉšð-Î[^˜†¹È(ÄR©2¥[^‘à[;]-۩݇¿o‰P¿ãP7kI…Þåø3¦áL—Ûðî2# –ó;äï)ý¤ÒÊ;>Z7/Ûûo< ÃÃ?tß}ùÉ)’4Ek‘æk@ñFÃ4_%{O3í4Ú9eÇéêé:BÆ¿¡õš](ÉS¦¾ 4÷a‰íf;Œ}Í0a·–$ûC—ô{ÎTÅ«¡™YˆÝBcì0|eÆÀþéP ó‰µê)j¤S2kH’£1µFàÎɓ۫—çqßør;pœÄGMžÜƦ有ËÚ§8Õâ>Õ›}/؇rý^lOè#ˆzB‘‹4‘¥å-ì® žf{­í¹,õÇ™+½4Ñr›s›´%øÑÅüƒZ·HØ2}üø½¶]]‰÷­ìîgÛg}eFÌ8—ƽI|Öí¬¾‡þYY8›O2Š^´òç^"Y+8»wï-Ñıãò•ˆ5<îÅÑ:¤G…eX&ä‰ )Ã2²°‡ã³cF{õWŒ¼ÜSXsJŒ#À0Œ#P XH©X”`F€`êÞÝSXsJM)<¯K©¯uœ…ÒQà,3Œ#P3XH©n‹ˆ ÿ¸_~ìF€`ª‰/÷T0Î0Œ#À0õƒ )õƒ3§Â0Œ#À0ÕD€…”jÆÁF€`F ~`!¥~pæTF€`F š°RMÀ88#À0Œ#ÀÔ,¤ÔΜ #À0Œ#ÀTRª gF€`úA€…”úÁ™SaF€`j"Ð`s‘9¥Ÿaç"¿=ñyø.øz[‹jæ½ÁWBì•JnJýár¹ÌÉš¸¢Ág*Ê Xåoèç ){4Õò*Öµ}!Ö¹4צÔöaqr–*A A )>ßÓIy¼;4MŽÑM½35Þ"%ÉlÝ2E&yß-ÔöÙ1#¤àÓØC6žÂ¸Ûµï!]Õ‰9D}XW™’”èj%Q³LX‚YAQ±ønMžøâ‡ß:}¿fý´Âæ…wß|ŸwØ33ü AÖį! +P’ ù9·0©pžfÊ.}éÂå_³ú±‘Úþtô‘£o›à»íÉ©>jû ¹Ý7ž’âœÔ ZðýÎ8ëfâ`Ålj_ñOù üpù7„¢cûÛ~/ôëò·§?xxiHíþ@AÇéÆ1ñ,¤H¨-Ï…e(uûegÊO­?q\±cp"¼7'qò5wÜ{>¨“ Båï‚ ñG|zˆoâŸËh°c¢@ÀÙö¥Ô¦ß46ýŠÜq¦ˆ@Ü )©©¾dÇód·Ž­ÅŸïk\Ö­KûÖ¢eëÖsŽ:ê¤V`ÒTâ’ßR¦,…ø%¾»uûÊæà1nûúxÄyŠâµâjFy§aš]®r’‹5(5«0„Û5笙Jt:æ¬3GJ"~dÈ·å^Ê_"ñK|sùvŒ@5 ¶õ¹'¹L¥:w=¶ÏˆÏí¾š¹ãàM x¬,{·Ç5ú¨Ãº)¶A©]u$üÇf-Z ¥düð‹Çuj«ÜKùK&~¹ü;F †Øm?19ùˆ×v_ÃÜq´¦‚@< )Úõw?Ñ4U'l3æežÔÄRÛŸ}éU'\¼jS¨.Òl/±”Ïö\þ1(|&Ѥ 6¤”êxÉMikSštmh˜™7!ÅšM{Î¥ƒÚpJÃD5θ& Ï6:kIøÅ۬ʩEI">¹üã¬1; »í7oÝêœ8l÷ Sfº~ˆK!ô¢“dqP[ý¢ÑHS#[$'*Ííî,Ò’O¼mI¶…â+™ø$~¹üi…älÕVÛG_*5WO$oí¾Þpà„.ñ&¤?.¡ÉÎ8ê>Þxk¸¥ Î[·j&]wÜ:5)ñ‚q°Üƒž$â“ømЀæ7oß#6mÛ}RŽ>Éâ’€XõûÆ #,þüG±æÏ;çÕG€úRé’ÓÖ ÆK»¯~f8F“C Þ*+ñãÖ„loñÄlʘýŠxõƒÚ}{oËŽ½"oËÎ[AO—Ôh+"©§¨³Š§õi«ÜKùJ$>cYþ ÛhÜ·‹áÙψ?6m/—§7>þZ¼öQíêy9¢•xؼ?#§üKŒt¾ø×KìÉ*ŒõÃoĬÿ¼'º1Ì{Ë¿?¯Ûñ{Ö jKš°Ú~¼µûšeˆc5)h ŠGB‰¥öWBÅL@‰UæÞúäkѾMK³bE²Þé(eákwTöºâQ¥î¹ •;’¢ú˜PÊgݧÜÈR¸åï§’¼ýÒ3E»ÖÍů뷉—Þ[.Ž8¸‹8é¨C"¢{Â=ÅQ÷^ËŸ´ˆˆNÝyÂxÖžÄK»¯»Ì2åF…@< ),5$†Ì:R¾]ý§øhÅ*qLïnâÝeß‹¤Ä1°ÿ⌷ ö»_Ö‹w?û^¬ß²CtïØFà;AbmÞVñÅkEb‚G,ûv Î8Yø‚ÎûPOïÞW(ïÙÉ {p×ö¢ª4J†xyÑçâ›ÕA«8±ïÁ⊳O´f—óßý\|ýó:/ýгNì+†œ|”ÅWlþ,áÊœ:*úÞñà‚åä üÅŸ UÅË —[ZŒë/<¥Ê:øÔ«‹æ)‰bèÚð%Äçßÿ*Þüä[1iÄeâã¯~‹–ÿ î½þ<ѪEŠøðËŸ¬ºNÏ-š‘ISY×Â{NmE¯.íÅâÏk×o±„jŸ~»Zœ~üâÅw—[m®OÏÎb>î)—K³´&/¼ó©ØWP,N=¶·À¬_`@µ( èâõ¾_¯úC—ˆ´U41îÆóEJrèÔe[)›Ç†ÿdµ©xk÷ VÎA#/ƒeÔžQÓ–¹:RJñãoyâ³ï~ƒ`0@ôìÜÖê@‹ŠVÿ‚e¢üî¾f:Õ¢m«¸vÍ’Åñ‡÷7ÿí4qp×vVÁ´iÙL\:¨¿Õ™tC¼õÉ7–eiP€×>\!>ÿá7GGŠ+÷‡uïhÅûø«_¬¯ß|ñââ3Žÿ]ü¥Øºsõ.¥ »£ á Úµ áäÃâ5)5CsO~‘Ø[PhE®ªîÎ/´;¥BÔÿ»ó­ÇSŽ= faR¼´è ±ko¾øß_‰¿sXD…"ÐòÌ—â_„ÐP›“GjÑ!~Z»AÒïÈžÛaùmß½ó  òÜ[ŸŠD&מÿW±m×^±iû~›¢¹â§ubÜ͈›.:ÕŠw+´E$8Õu[±2ЈþJûTj_ÎöÖˆrÈYi¬Ä›&Ån@uŽ÷ß/Ž„Zúh>–­\cu´Çöén¥»ëþ§`VwöI}C|¸].ѦU3á<\Ž„r»÷ˆ>ФM€= $ÿŠÒXŽYk¿#@Kãt߯ùÓšmvlÛBЯS»Vâ«UëĹ=Ú¬¶÷$˜Ú8Ó5œ“Ÿxœã›ZñPQ¬Œ(ÕuÒÆ<ôì;v‰mZˆsNªX£·ðÓ•9Ò†Ð2O‡ƒZ†È“ÖðÒ ûY y:…î{ò­çÑ×+ŽèÕí±³UßíÈ«!üÐ~[5OÁ»àqdxK®žÚŠÍJc¹:ÛYcÉ磑#oB Á]/'iPÈ5oÜæ\Œ¹´Ë‰ç1ó›4ïU,Ã"®»à¯Ö2õ2ìŒqi¦gš¦¥º&ƒA§Ñ`¤4th\¨3ïÑ©Mjø€XýÇ&¬ëoÁ¬ô÷Ð;Ú±c¯U¼òcøë—\¤:‰ªÃNGÚ=h'Nêeƒ„¦UÜ,ï‚Ö‘–{v@èø›ÿƒ6rØeCäprpèÞyó–QÉõÀ²*9Ú.Û¶u ëžþŽ<¤«¥…¡åY2ZoÝ¢™5±¨Ç¶â¥‘ÜØm¬‘d‡³ÑˆG!¥^pOðDÎz¯.íDú­ƒ ð›xîíeâ %)âÒ³ú—ãiÝÆmbÁ§ß‰»¯buœ|ñ£¥îvŠX‘Ò [ú®†sFIÄ]š&h;°ßH¯_¹ôbãQwËh±áϦÒPø´ùßk¤:HÜR}³sz^¿e]BŽˆŸ×m]Ú$.ûNDË~*  -’f’m§ó ÎGr¡)$·m×>ÑË©†aŠ=°ï²Ý Hó%ßBPÚ íÖ Ë>ÇX<¸°U÷mÅæ‚¯Œ#p ˆ'ÕzÅSµzBˆTÓd »¯ È²G¡N7‘Œõà:µm%~ß°Uí ©œwì.°üB˜íXKÿKF–‹bŸ -3} ­×Ó ñ[Ð’;þðbù¿ŠŸ1{¥Ù"½ f¬€±ý;иèôc‹f=Q£º±6o›õ£ûêºÃztÄy$[ÄÆ­»¬¥Î~["¡†x[‰ûC“x×5g‹Í;vØü‡Ðûð²#!¡4ßþò§èÑ9h³.ü™ Í’,#]ÒÂ,‚ºskò¿æ‰ü¢b1ä}Ào3ÇᎨ­„g¡!?s»kȥׄx¬N8pPÃÁïÀ̤ Óoc‡ÃÓ¯}Œ-’.Ì ;ˆ¥»~΄†ãÿ|&F?ô¼¸F|`Hv(æ¼ 5uìON€jzYTÈÑŽŠ'ÿ÷‘xô…w­ð'ÃÐðØ>=¬>ÿ~k©˜ ²mIÝáWœ%Ú;Öø£J ò@¥‡ìR*]?oã‘§úÉy S¡óHlGƒýý©·£ºøËÁbÎ,¡eÍæ)ÉâÌ~‡‹K¿³â¾õñ7‚Œp‡ž3À".AÝ~yÑ—¨£Ý-a=<Ú)D޶çŸuâ‘bð€ý¶\áaߩݼ¸ðsL~'Ñ«ŒÍí**ÖÅÌçZíAƒö‡ì³þvæñõÕVÂÙm¸ÏÁ“ÎvÖpóœ7)¨ÒÖÊ¥z³>$¹þÌt­…#0íolyË}Þ×úÖ³ÿ˜Ϋ¹šG¥Ùœ†ãh‹¤ÓÑÎÈBÌìR š¶iUhkrM\q‰nÙ³0ât¤ö.Ä–ËfÉI´ã2&îá/ßþøËOÏ=:ýn¤Ó²è402x!ýºßr¡rd(Ôéú»Ç=zlßÞG¨ò?P@ˆt©&&ع ÙqÑî8'ÔÇÍœ/®9ÿdË6Œ„ö¾ZeÊ8sìõ¡vQmå@”C]¦Imÿ»U¿­|ö‘)×#xi÷u™å:§Ãq¯Îymè ØÞ)NÑó¸#ÃBƒS@!ök* ãFN‡„£æ)tz=;F np u›RÅÔÉ(7œ\ÈŸŒÌwb4mO^‰sWºuhPˆ"·•Šqå7Œ@c@ ò(ÙrÆy`‹ ÿ·]r†ølå¯8e•HJH°ìR†üµâ­Ð 6³Ì8#ÀTˆ )BÃ/Fà@"Ð[éÇŽ`š.e.š.œsF€`F€ˆ3XH‰³avF€`F ˆ )\F B2f¿:ǧÂ@ü‚`:B€…”:–É2:;ˆ¶Þ³cFà@ À†³uN“h@lÁ—¸çý÷C|¼¹uP}…˜NEÞ¼cuèá?¬#®„“˜uñ>N݈ž~üá‚üoºø´”Sf•`â Ö¤Ä[‰0?Œ@œ!@Û€O8²—ؾ{Ÿ˜ýÒb‹;:·äµ¿Âá5x׊[Ì}ùñç–Öqú¯âÝ·8ׄ#À0µA€…”Ú Çq&€À)Ç&úAH¹lpñÇÆíÖk”m:väÐÁâ´ãúXß´ú-o‹¸tP?+ì þG6d8‹Œ#P×°R×3}F #”üìCr鵟† GßÖ±O\¦Ò)±-JOJnÕœ¾pÁŽ`Ú!ÀBJíðãØŒ@£Gà;|Ù¸$ ‹ÏacB_'á$Ü‘½ }óËŸ~·„^ê GˆŸF &°álMPã8Œ@B +¾²<å©7íô¹_F®È]>¸Ÿøïû_Š7>úFÞ«cEÁØŸ`¨`!%j¨8 #Ðô˜zçP+ÓŸy<®2ôq¿Sa‡B?§;îðžâØ>=­/{ÿ¼n#vmv¾æ{F€`ª )Õ†Œ#0MIŸÂQ0ú21;F€`b÷&±@‘i0Œ@Úµn)Î>©o?~`F º°R]Ä8<#ÀT‰@‡6-ħ[e8À0Œ@e°R:üŽ`F€`l“½Â‡Jèg;ç½íWÝ«s=ŸîÏÕ¥ÅáëX×gysù×}ùÕ4…X—;ñÁe_ÓÒàxŒ@&/¤Ø“išÖùt¢æOk7ˆ{òÅîüë{$b¿ÌRýzSjo˜˜à­š¥ˆ6­š‰¾‡t=;·š¦Y˜³#«~£¶Øu`݆mâ‡ßòÄŽÝùb×¾QRØ_ô5­¤ümŒÂ±¶ë®} ÏÏvž¸íÇs)1oM&+¤PE“a˜¢¸8 >üj•øèë_ľüBÚi)t™$Š´$aWLêˆKŠ$s§p«"ñÖ’•¢y³dqV¿ÃÅÀ~GŠÄDwH`‰IbL$*¨ òþ«Äû_þ$öÖQÙ3 ­üíö±mç^ñ#„öMÛv‹B`UTT" pMò¸Eëæ)â –)¢]ëâàní­ƒÞHX±…ï¨ á²óÆmÿ€ÏI2ÕD I )¶p¬™óüE+¬j§§£ØœÒWlÇU—Á£À«‰g•ÁÝ* Ú6‹Ž%ëÅëáhµ¸fȉâ¨Ãº[[7©ƒgW÷Pø~ÍzñÜ;Ÿ‰=КÔGÙS®â½ü —ü‚bñÞç?ˆ?­Ûñ!Ar¦æ™ Â- é¶„í$µU¸Ìë=ýunßZœŽïüüõ˜ÞÂãqYÂJ¼Õgnû¡ââF A Ф„{¥ëfÐ%⃫ÄÛŸþ \-Äêæ'ˆÝîvu^h$ülNèfýZéÛDŸÂïDîÿ>Ÿ~Œ<à/Âívî Q}^çàÅ », üê_‹|«ìO­—²'öãµü Ã0Ä*hMž|}‰(„Æ„„õ)‹]®öh#Í#¢/…)š»E»À&Q¸c“ظèK´«Õâ¶¿f -tfŠ]—‹Ktk•Ú_„˜–Ð&Òwlí %`‡˜X-<írç¶_ 9*#ph2BŠÝIð ’b(ïñ£XøÙOb›§«ø)åxaÊØ,ëT§ I(Zñÿì}x\ÅÕöÙÞÔe[î½×àN3¦ƒóC!@$¤ áË—äK¯¤’F:-ôjcÀ¸`p·qï–»­bYm¥í{ÿóÎj¤Uß*Ý]Í蹺wo™9óž)gΜ9“³€&5~ÈZ•mÂþáÒ9“yª¦âÁ1ÖweX¶~½¼bK¯ò4÷ÿ‚Œú§‘‘êM…â8lŸÄZÂ2šT»•þðÜrzð3—QùÙzÚyè$í(=Mµõ Q_F.‹xÏŸqÃМ)£hüðÍÚÄT +’ïªî·ƒ_ÝPè>#¤ !F#åõùhûþcôÖú½¢“ÚåšÝ«L‚p¦pûýúêíÔ¿ ‡¦.“©ç§^#͉£ `³¼WV~¨ Þ#»=ÅÙQŸ:SC‡Wб²³„‹ƒ¬=Á”LQ®“v>M>¶Åú0ç|1µ“;ª,i›q>ͬ_E¿zò‘†ÆÓCU,×ÛGÇädÝ k ÙÚËn¤3ÕTµû­ßYJý séÚ3h—h`PþS!¬¨ºŸ'Õ7 } Ð'„4RPóBƒR[ç¦WWï Fc®Ð èƒ $h™å®§ç—m¡1CÃÚh5¸^èÌT:P¼Þ=ýÖ1Å홞èIÿ‘oëw”ÒŠMûèDE•ÈvØheÄÆÚ;™ <ý:AÖ°qNOX@‘xÖ› ÈkrQÀ¦ÃŽ<µ9œ…±Nl­l$–~þÓ4ª~ýãÕ÷iâÈAtËÕó) s!¬$cˆ«ê¾äŠ:+2¬R0‚„?lP</½Ï+x<>¶A™Ý+S<Œ¨÷;¦‘Óý­d[™+æOinœS1šì,ݾp_–å›v #Ùý¬)èé½®°N5ÿ¥æ¤âl=±d-•ž¨ ¯9Npƒ¶Ãkt¶&‡g€,äc…¥†„M9 …ÆD‹aÏhV*ØN«’§^‡øKI;º‹~ùÄ[t÷u hHIaÂÓŸ’ïªî§€¡* …@/!ÐÉ𦗨IC²‘‘TÄ¥ýžlÜR¬äè #Ùx³š°ÊäõBë Å(8ÞxÔû­e+©€¯yŠSÅÿH碣ìóç—ÿYJ‡NUÓ>ÖԬϹ˜NÚF·P8/»O•€‚è°(ïÊ€÷OØÆðtÓªj Ñ_ZIe•Õb™8Œz‘¯x‚ä;ê’ªûñ §ÞUè¬RšGR¼ÔØËóï‡Ù0ÐëóS¹e¨~8ІÐÖÈš1ú™nhâmœÛDÙ§Ê2Þ74zuÍ{0*YþGò¢òªZúÝ—QmÀHs.¢2žrÉ”€é¢­®ó©Î¦¿½ök>=ž BG¬uAòuHÕýLá¼¢S!Ð> ¤„¸ ˆ©žCÜQaĈ¥•z ‚6¦qï‘2Aw"#H½æ­7è’6¼ë÷À'Yþ£#÷óRßÿ,^ÃÎ×4Ú⺀žæià®îõ¼FÕñÔèÆQø°P!1€0¬e—÷™À{ä2þËé hÞåU< æ|á001ÔôóÕI¶Qeûþãb «–仪ûúᥢD!Y+¤ Ñ–*_4l°ðX³Â}z!v=ð‡„ᬜîA^Tˆè2€)TíÃñ¿ÿ‘W”ó}GxsLÖ³Ž?a~Qg*b\#¬¨ hŠºÚ£ù®ê¾™ªHRÄ@Ö )À@ª|ÑÑ£qc©%hz÷U4´ [M÷$ÇY€g&…xù™ê ÒÞ£ål{c¤³¼Ì8+‚Øì“5‹¬ ƒ64ŒLvÇOÉwU÷³¢¨Lôa²VHA#9x9$¯ s‡ŸIÝS/èFß*óÒ‡ËiBY—¸ ù$Ó„”8ø|¢ŒX“O²nS>Ûßè_c S ŒƒY󓕺‰éO¡IéÚ.%šïªîÇ‚²zG! _²VHäÍ7tYáwÆ'øcš3Šn,ñË ÎGŒƒÿÈcDc¤š/5:ãBâä*Ønˆ ]V¶-â);lÄR—[ø®ê~âè«/½@V )€·¹±Ê$tGÑŽ|¨8(™âá?ò™îá•lldâbÙ°Ãò(ï^2³¥$ßÑ\—cÍ›ªû±"¥ÞSè¬Rô ½¢,Ó°˜ d1²ÑD/‡ˆѦØym OôDpZ°ïOzRÂ4ÏøÆm”ª¡‰%N!¨`{½mì§ ƒô «bUdY+¤¤côüØ¢zàÜ–=Oú»Œôô'riõùÍ\ÿäd+½rsü†¥4¤#?)%P‡‘¥³B‡6}¾€¦ 0ÓÄ~&zòú\ÚzO­½3Ÿ~u…‹MÊ‹¿_›C_š“úé–®òE¦¡M)pZ);öT‡kÆÛhé-ù™lÑ8+½}km¾»€ÖÝU@¿¿ÚEXú9´åîB[”üîÝ“6Ò@ÿ1*qh`¾Ìf3 *f±3rwv…U¢¸Dç_Æ!õOçÑ‹7å‰[©Ì¿Lçtä':~u­Ð3Y+¤tTî–#96Ìh¦s‡ZèŸ[="¢”˜éÅOærÃÜZyy¯Ÿòlºv‚5¹ÛПtd}4Éÿd³ÿ…YÚ|:H»*‚4¦ÐDªBtÍ3utÏnºd¤•Љ!üq£—î8ÇFj’ ’vœc ƒûå’5ìa D]¬Ÿtû´E÷Í·ÓŸ7y.{j¼ým‹.øg-Ýõz=?ÌJ7M¶Ñ™Æ0½¸ÇKÌO^H“»&W4¨ñ“‰…”ȎȱiS¢±‹¾±h›ùÒ—çØ©8ŠÏ©Ì4ý2=uVôE²c @pîîYvzy¯ª=‘c<¦ÿ¹Õ'4&è”d²×î'¶ù/qµû¾3Aúw‚&ÖÖmA2ZØ 1™rÑÐy“Æÿò–·%­z=o`Â6pûøcÕɑȚ…pÀKa_Íä•/Fkb¶¹,t~jš•îzÍ-èY|ÀO8dØÎ‚ˬÁfzn—O2‡ªÂô™i6úï|%¡óɺ0ýlS‘•§U¬A^YÜ¢¡À†‡/w Í6æ®›N2y¯qR1_ðî£]®9 ¥Ûö£Ž·“~½©<¯9h~¥Š…ˆJÞ 0×Êþþ¡VÜ–OãŠ#‚\ó‹q^ìqΦ£ö 4Ú³›´Óe´©ü5ºÈÊ´f®nçÓ—ç·h5ýÊ#®“Ìo^éòóŽÎžò¹ÉSã¡Ñ&®· ¶vmó´&÷7Ñu­ôcr^dHUþe|ê¬èë$Óëõ)ì&ö3ÓÞ3ìk¥) CBƒhné7ä#Ú]¤Q<Ú¶ªÉêfL2ùbt¡QØžì®lá¿ÌOžÍH“Y`ÝÊZv±€9‰ËKOØiYÛa2qa4[©Fc _à öN È÷±æ(‘ÏEœCóLtÅ ýø'ß7ÐË{"B[™;,4-‹“Ï£1—vºæÑqÛXÊ ×’ÝË{o±àÉêQ1½ÕQæ’×_µµmþÍÜjþôR=ü‡ØG`«Êü·ŠXýPôQ’oIúp6³Jr t¤&¶ÍÍŽÔ†„†ex¾‘žmß±õȲ*‹#óMt¶Q#7oØ×6üøb'•7„é…Ý-š•cÌÿyC’¯ZCòŒÂÞ%/—m^öˆÒ–þRŒ4ªÈB?¹œÝáW ¡wX£AõÛÉn #¬‘HfÅÏ(ÎÛ²ÿ1žÎüÂl;§Mô›5ªò´Ô£\O†¤N\(µO¡¢P%0–ÓW¯˜H¹9v²Ûmb*7ÚváH áÀ2eÇLuõUå]ÏË´l›êZ@ Øù­¶ùÇÔ_Y}XhÓî/n?µ•êüwN™z¢È~’oI³#Ä Âi%Æ*Üé—¸2GâÿwIåS^nN—T,4ý扥´m÷=O>úð}ü~U|ÀpF6½)IA…–Ö‡Å| ¼å¾‡1yܤn½Š&"•—;+3ýý53íNО|ìˆ÷_?ÏA3šèæê)µ¿ÞÈßôT@G -ŠÅbáÎÛN.—ƒÎ›PB›÷3›ë±FåpN§2ëð„HBþ·DiŠÉc=ô×Í^šÈÓùh(0Ñ÷VòÔäp*óÏÕï”u¹Xè*¯b (D`£-¤ˆÄÓð/:ÿãyëΙvúÒ7fmiÝ(4¦£ Œt¸i“òü§!O*J…@¦ Ðs-i¦ Ò ‘4VİêoVð[…ÌGšhÒ¢ÃçØé“ll8ên'À”0ÿË›ÊLô7麎)<%ÅÆ¥6›•œå¸\4qX9­lÓÄ2¨%Ìš•ò˜¶‚ÙŽò ­f•󆵌w€Uªó©„ʳuM{øÅj¦¶4¥ãwtþÏj&;g«¸óÊž§X Ê«¼¢O†tä_Æ­Î ¾†€Rbàxc@ìLb #X=Žp¤V )±à¥÷w0Õå¥.6`E¸W²<ÈZ”Çx%O/½=‡W~áü?RݳÊ),ËÅ2]hœNÙX£²ñp-5ð,Ô^ç9tÜ>N’÷‚‘¬)‘£Ð"ä0ç³ðê& Ûkµg`zìhMjó/'Úü¼Ë36 •»ƒÇ³òIÒï9:ÿOïôÓ¼¿Õ6ÿbãyLéžÏ+dHGþeÜê¬èk´´¬}-çqæF³“Ø€0–0•UàhØ|AÙ´Æò•zG¯ªæ}qXk0…ùºád>=Õ&4eß¾°µ=¤¦ÕHSû›éý-6*=‘/hSÌlÅ-µ)«v£:Ÿv¸æSµe@R$@Y4Ö!œ–Áxvæ 3}ý\³àv‚W a¥–^#`š ˯÷D™'•xÓÇŽ0/#â`5j¼›yPìø ¿0=¢ó`pÈàçkü¬÷Eî¥+ÿ2=uVô5”#Çcÿº6þ´É+üAÈÏ0/C8xºí#vúåšH£*ï«sæ"Pç ÓÛ}ôy¶EØÀë|¯®s$sÙ`v¯ú’ħWA B hTö« ÒSUÂ%Y´¼±ßO_d#ÙóôÖ »}ôÝô]¾MJ[cb`´¬ÔOûy5P*Cïáca!Ìn1żOªÒo›ÿèxYç!2¤+ÿ2~uVô5ÔtOŒß^¢7øè†I];i»š—e¬¶ò£cê5#ð'Ö áe·ºÑ¦ÝÌZ–ß³Q©ô§Ó“Y’ÀVl9@¦\:e•’äáûç¼Üöz.ûÑ–)m”"vlxá3 è-B{*0k*–QI®µÙ9c*â5ŽÎòßöûtå¿m:ê·B /! 4)qp#ÈîÂk<êÄ¡Bv!€yÑS-våî·"+\:{ž®ûPÂ<%UËÎ˰æ´c*<¼¥,,? ],Ó¾â‰ÎµL]}ÛÕ³!¾CdäG;Å*&隣ž ½™ÿžÊ£JG! G²Z“"Uà‘³áhú»~S=í ‰agÏõ|_ÒŽswöXr}ª"⥷ÁÔ²Ú¤»oõüÜrÓpßAv?o¦ö»cÉuW¸Dc|º‚ ’WSÕ±2JæAWÄ)b=Œ@Ö )5`ð*oÒZV!ô0Ö1'gf-ì":t”Ÿèçêº=јÁžÁDúç=r‘ÿ¡I ²ëvä å\ÚÅ?ðkrãæ[˜Æ àK¼¼‡…wz†"5*m£ˆæ»|–)u|Ã~EØ ÆÈ˜6ì(?2_ê¬ÈvZ÷„Yž[;wT¶pjçËÓh´[c[I”Žô³1N—ÝJö$|…ô$&‰ð›è…X›’ËÛ1£ó.œéI’Ó’ÖøÆmä Õ–ôä@H±‘Ýf'«•÷ïBJ÷&IX¦Ô}+×}_ØH>¶;þïN/}ô™Z¶ññÇ2/ê¬èKd½‚Qˆ<ì¼é†…|É )Àxz*»$N'Ag*øìtV¦ŒèϾ3 ´§Ìá=ó¼Ü’^Þƒžtò_vÌܧ‹ cÉ®˜òàºäõzùˆê˜‚—Zìù?+tÄTI“=®P_\N?Õ6ú©Ü ‹¯‚м]/åÇòß\sˆ l¬Ma'svkEXð€ ât8X(‰LiBKÓtmjÄt'ÿÆC¬Ðaºâ ñ=[ë~WÂJ©q„Á0|èÅ£¿}რßøuùÛ¿KýKñ0G½«ˆ= )ZÙñ£äß·ãàIš7utÙiÿjdt©A“`{œ`¯<ϵzÙu˜Œ,46rÃËí2+ÄÅ\}ȈàîžDáLJW´ýýyt:´À*æßóósy5MF‚YÆâ°ª}ŽÛßŽÜ Žíß»Ÿ¢7’Gû—{ICF¹)üGv:+£¸ Ø-TZYG&w…ÓÁ{ÐÐSü—‚ •ˆÀ1¨E§ï°;Èãõ!%àŸhTP ¨à[ÈÃ(wfÖ¢@{ah‚\o¼¬t:½¼”Ÿ½¢úýÄs²ä „)(¾g·ÌA^áOp–Ìf&lŽi4ø€Ð Eø:š; -°3 ‚)ŸÈª¡YAúâèÜõ=0í.tÆ÷l®û +šÁd6 ¼½àò»nÌ»ä¶Çm!ëÏv~{Z×RfwÇðü‹ÿ÷³Y¡Pð*.XÃ5ƒ6˜‹Gn Ÿuû µzƒf8Å/5MoýéGÿ»¹ÛÔ ‰@jzàÔe]tT«¿¼kÜŒsª6î*-b!…Ëuâ¡yDÅ­v˜G‹PšM—T…{i ,ÝìÚ0ŸUÖA^©…“J¾™p«Ó-‡N¤€ IDH 7 )‘©)¨ hY Ìx<¬…á£Ñã×NÖJB …Å;¬a‚NSº˜fbáCH\¦‘>Ví@‹‚2;(H žr1µ±ÁŠ|Ú“ X“Úë¾Vxb“¶ºº_ÈmÊm=W4§Ád¹/`Ò>?þW;þd×oÿÆØ6Ë­’A ÷ßÿ{Ýýž1üZ0„"d5¸Ãcù|É1µ‰4 ù<á<ͯ¹ŒœÆOîþöO‡5Ã#C­Cÿýïß¡ÿ½O’‚¸o}¬'!%º£ Už:ùìNƒáÞÇÊhÜðIq%2¢â¹z-2Wȸâˆ+ ÐX¢†­Š“•b68”£K¼+UḎ7 ¡D€Út°Ô/f¤‰†ZØ `% *bº‡µ=-J÷¾ b¡øímœ¼7gê%Ÿ1˜mS€-„f؃>jübŠ„4tÆÛ¿ñ+lõ5ãàþ–ƒÚ0ó6*1ï3X ÞTûzÂŽ_³SypÎXûð©à±¯ÝùÍïßù÷Ÿÿ­H6‘[2= )À£|ÑQ­zåÅçoøâWnzféºÂoÞqÑjI®Œ£±Â¨ A6¤r”gÃ\=7ºPgËyúè†W|”Ä?™4#¢áf:äj#LAÁ%²ª7QãQè½É?E2~áp0PÿÁ’×—q|0‘Ä!…ÈœW² %ÿ}3ß›è ‚ÞácÆ]Îô禂ÿ ±·Ê@oñ_æù6Ã\öLBë2&§<¡]„p!qЊš yg¨þ/ö±6†…•ˆmàB ‹š>•iBÈ»<ˆ `wYÁƒßѶ&H'¡·øŽ÷Íï~Íd²ü<×X®M·½AýÌGÓËXN”…fÙ†ÃpÆ2‚¶{¯)©3•¼ñùoÿð¡ÇòÝßò+ziã˜A@OBŠQ£ ÖÔœqïÞ¼áÜù?üÏëïkw]QR^6Ä57¸Æˆêg(¢áå¹v¡ojxeãÍítÜA¶½2m¸æGÃ-k*ab¾îã95|“ln§*khóê•O56º±üØßtHAEbžlRÉ|/i|—4‚^Ðm¼øò/¤‚ÿ Pò¡§Ê€d¡L·§ùÍ”)(ÓRór-Ë8žËr' fñ;ò>OW¢¾p 8‚B8i±ïŠÄ‡ïð :e©ÁwR«‚3žISä«ôý—ø÷ß#Dò#ÓîiÞ˺¿uí꿹ݼæ>R÷ƒ¥?_´˜¯ŸŸðë]WQ8ô=fÿLPÊçD4+h¤P°Ì·?ô!  µŒhc\@IDATìÒ>bÉh†o,Ñ×_L[½×k'S~}×·~`øÛO¿'´3*d zR¤^t èL}kÞ^¼.¯¨ð ¾¾^"í³×\`HF£i<¥ <ÒˆJm F„˜§Ì±C Îêküq+݈3-q¤)ž«— 7ji0Ájïè‘e\ tð24(h¤6í9l8¸sÛë[×¼·ƒ_c‹Ýæøg½Œ2Zñ]Ò º ûxƒ_› þs3²-•œ6²@€·……%Ô4Í#ëIs~¹ÜÙY›,ó¡$¢Uè)á$:2Ï8#}PÙ^÷ïÝù¦Uï TÖýæz¿ïÁ)¯ñý×XX¹6AaE (–O}ùÁ«,ÛO! Ìv<›üè*šqq^C8ŠÐp•‡oûú·öþûW?]ÊÑ Q‚Jœxêáu= )Q£2ÁøÉ³ô¿O¾tÅ'?õÀ7Ÿ®ªÖn¾r¾16*‘ÆJzé´D„npå<=F˜m"$+¤ 4ŒÜ>ŠÆQŒ0ùwdT cÁȨ4%`Ã)hPìÜúÆÊW_|—ãXòÚ\_à¬'!¥ß™6ÐêXñê ﲘ`>š*þs\Mü@‡•Þ2 yÚSüGÞÒˆf«Ø'J³p©ÑuïFŽˆPØò;ùòlÞ"|H?ßA'òÐS¼®û‡voyùËÏ/áäQ:­÷I+РX¦N—Ÿ›Ÿû§\S¹Ð ð=]ÖæêBýÃõ¶~_vÙ''-[ö<üÄ Q!ÃЛI ºBT,¨)ío?ÿô+3/\X>oÁ¿~biþÔ±Cµ9SF¦’°4 htÑ€DæÎÍÜðF«Á[4(Ñ 0ÓW UtC4Ñ9ÊS¾WÄQ/à –ëbŒdCÁ€{ãÊe/ìX¿f'¿&qD%žø |³^Fò郓 Z+ªÊNWÎYxÙ Ìÿ¼TðŸãmÖª¤³ HÞ¦“ÿÈKOäu'Sƒ¤?|6=Áû¶uög›Þ[ñ̶µ«·0 ¨ï1Õû8…ìΊ¾Ã6ý’‹¾Ä>‰Â¥7¦x˜†h™nÝô~テÌÿZF¿äÑÎèe`Ö!Ýêf{ô*¤HM *:*ë–Õ+7ìݼéàü+]ÁëîrgœÇ€–ë°iù.ƒÝ”¤ ´HÚþN$ ÙXÉoÛþ–÷ã=Ãu<<³ÂñÓ)„“Ò=»ß_÷ö’¼ÞÆFŽ0¬o:ã:Z“¢7!¥ß™^”Q#®Û¶–οbÑù,„]ÀüÏMÿ9­vš³dË@[~·ý4Uè}Úò¹íïD(lËë¶¿‰ßtT÷ïÝó×ýUOœµ%Tï»V´†/ŽÿÙæ¿x7½ý‡c/þo£ÓåüÂ^ÅÓF²ñbÐV¡þ2û(rÊG/í^¼Yê“ïëMHP`/yTŒ0Õƒa›‘)iùËϽÁ×ö‘Q#ÆM˜äÈÉ-´9yì4*q×´a&†`}ó6zêÝu5G÷ïÛp×¶ãœ9m ªÚ¦ ð®z²GarDè”ïò9 ^¡•¯½°|åk´jì”ÃFŒŸ0Þ™“W`s:rû"ÿ›pQ§>ˆ€¬ûw}õÑ{÷عíÃ!?%õ¾SaEÓœš¾ß2í¼/Ìö·· ÆÓý±ÌX¯i3ðÒä’ën»gî+ÿþ˦팚öÑ+Ã: KBŠTý£ÂaÞŠÔ’ÈŽ,p`ÇÖ}|”ò31ÒnzW¾Ç?ûL*Ìfl8çЖ@ B öìÁ50ધ©&G„˜øÎoÚù°°@vˆÃ|2ÒùÎÙV¡#Ð#õ¾µ°ü^X ÍÔB¼ïYÐïȳ¯ãЧÁJ"|<áã4vîýôÞ“ iܼhÌœ¯6G£ñPo=6†¬®þtÑ­ïÓÚç®!÷ÙýÍÏc½`ÚÐ8h9ù—ó7›ø@Û‡¶ø©èQHl²ÃŨ_V€èN ÷|›> M ŸÑa!Èo"¿²ó¿¬dÀ *lM€MA“ =kQ˜<ß%ê¬è^©÷MÂÊc¿³âzͨý_8à›nc¯ÌVCµuLiw ¼)å¸s¿A×ý†Å…0YìETS¶™ö½ÿñ•œjóóvc'vÿ—ÆŸ÷MÚòÆçºˆ±ãG i {Œ¦ü´ò¤A“,qäKôŒ€^…`†K.¶G’¿Ñ £V@H£j>#RPé  gW‰ T<àl ¤@("mS€žG=ÉgÐ(ó'…/Åw=sNÑÖSÈzÑÓõÞxðÇ/åL®)¹þ»ŸpŒvüÔn¬ÍI$ӃnjÚèÔ¾—ÅçV{!5Ô¦êÓPv´‡·ü™Þ±žrŠ'»j_ë‡1ü²ju¦ÂAü*ïh%ÚG fõÞƳ¾ñŠž…YÑAE_£³EÇ EÕò¹#!%›…à q‘F ÀG *8ã·= bÊoùR—AæIñ]—ìQDõ"²îÊ:ÒÓõ;ÚZkùK?\›{ÿ7[È?-°˜‚>ÌTscæ>Mo åõ›œb1øÙc„Ð/5î|©‚ÞгìPQŠ£+¥UCu' ´(ÒvÂI6 (œ=€‰<€QtƒŒ ˜àŒÏ2iä øÎ SA!вÎË:ÒSõ^¶«hg… Â¾¤ng]ù£…æDæïì‰5ä*C&³ÆŸûMšpÞ·hù?fS8ˆq«…YËâ,)®ùÇÓGRÀŠî'€¡ :G@ïBŠ„OjpÆ´:`Ð.…Y𤄜påá83%È &±‘‚Šl´äoù˶³ÌŸâ{¶qVå'^z«Þ˶礴 5¥”S4¶9ß#fÜA•G–óBŸ ›v+™­9,”¬i~îÌEÇv<Ñü;Á‹húŒB}ÖÓd¢Ò#Ùyµ½¯~g7ŠïÙÍ_•;}"’޾¾r w o€]¹C4`ô•4~þCdd›”ª“kiÇ;÷Sõ©õ{Î ²:Ѝ¾rw²ˆHÚ“G}߃dƒÒƒp©¤ …€B ‚@â6)§ö¿Bcæ~•†Lú¤ðƒ²ñå›9JÞÊb§PZÂèY_¢òCK©¾joËMuÕgHJe×gPRU(}h!R´pö¾ÿ#:å&ŽSF­µP¬Îbê7â"Ú÷ÁOS–vSD2ÑTÇ«âK1J“’b@Ut …€B KÓ%òœT6+Jß!]c½÷Ÿ »z%¶g‘åÒ’n% Ä†š.ÞRš]°A¡P( …@[”Òõ[! P( ]  „]°A¡P( …@[”Òõ[! P( ]  „]°A¡P(!`±ðmeëÚ6}ážRú—U …@/#€Ž/»g7å hÙ8¹ßðtéç·Ó¤?hE–Ÿ³èqºüž=âù°©ŸÏg_÷ûµVïªÙ€Z‚œÝüU¹S(º@`ÌìûØ‹ìFª«Ø!è=ë‹4zöWšw:–D íü^&OÝIÚñîƒtæØjöJ‹}e‰­ÿ-;ö?ì"ÿ?ä÷œ•Ÿ¨s# 4)YÌ\•5…€B@! ̶<1ýV:¼åOÍääO¤¯ÜLžúÍ÷p1hüµd2;ióë·SÙÁ%¼Ù`}³“·êÓÉ}ö Ÿ~{«oÔìE@iR²—·*g …€B@äŽ!ƒÑBu¼g Ûß¾O\â~t(<ªOof¡æ6ÂuyéR:½ÿ5 ‡|âµÚÊ”×oJô'ê:‹Pš”,f®ÊšB@! H!rSOq†‚ž€fÅu·ÁY0Šà=Z‘î6ì7üÊí?…êÏü;÷ëÍŸ5Ö&Wá¨æßÝ]4› ‡ø½hú»ûL=× J“¢F(2 …€Žh'Œø½¾¯–ûÝ.½qä !¯ûTLÙ3TËv+RÓziÈÄhßû?ß{ëO‘=gpLqá%¯–§üÞ¶,íòs„êÅE@iRzn•˜B@! È¼ •~Íeðkön3äm(#»k`·ïáwÕ²Xs›ß…öÅhnIÞ3ž²æç]]€6A£§±¼«÷Ô3ý" „ýòFQ¦P(ô†@ó”ɱý{·óCypB·46T—’ÕÕŸÌÖœnß­<ú./SžJXvœS<µ(ŸdÚÅÍß9óGSCMióï®.@h<~èàF~OÒÞÕ'ê™ÎPBŠÎ¢ÈQ(:E@vò8‡wnZw(ðÕÎÀï.ƒ»ú iáåõŸÚå{xXuüÚ·úÇ4nÞ7è¼_çÕ<ûéèÖ¿7—?`:Õi1Àm~ÐÁh ýÕ›W/ßÇ£éïàmuK(›=rEѤP(ô…@tbÒÄq¼ôÐ;¦ “o8cAýÌG;¥8è«¥#ÛþÁ~Q¾DgO®kõÞšÿ^Ýê7~Þú¸80Í›ІÌg£ÙÑ´éÛä­NÏg‚#¨"0ÖpêØÁ×ù%Ist>:ýV=ÐJ“¢^(J …€ž3q²³‡wµàK^_ xk·{¯ µÖK‰ÛfäІߑ#wåö›ÔöQ§¿£¼4|úgéÀú_SÀSÝé7xZ@S8è«^ýú+oà’väC… A@ )Â(E¦B@! èE¤=:|?ŽÆF·gëšµÏÕ…>ô\ß%y0€]ýäBª?³§Ë÷ºz¸õÍ{éè¶võŠx¶Õ{½V@ÛÖ­ý›Û]‹åÇ W *2/ÝÆ£^è}”Òû¦~óê·ŸÙ¶võNÓ<î¦3„• ??þ©‚žÈD!EtNw>ôýÙ£áJOFÑ0È Q‹÷=#žBÚxÜ⦰vš«ÜÑ€Ï÷Î~û3¨61JÀ‘mB‹J0Eiüì×þw–Åf»\òßH†î0ð‡*(²Í@õ¨ûZX;…Þúç¯~$ë~:ë½R®¾ú­‡ÎÿW0èIÞZÞS'Håå{žØ·çí[çm[Kç_±èüà„ TXÇærÅÕ¬†Ín¨5X ~Ôã¤C€¬š7œ¯ÁQeú½î#û·¿·îí%«<ž†:N |ðãŒ#Z“‚|¨!d’b¸ûîï;BE†¯˜-¦¯…ÃÚ@ƒÁ å:íá‚<§Án±ô9ûo ®©kÔê½F£Ùôƒ{þïÇe^÷G7í|låÊW¹ JÕf&WJ4jà­iáÂëœ#fOý’Ýaÿ²â†´0ŠÌ´ ÐQÝøƒžÙsì±×^û꾨¤4ý‡zßéö_ †üó¹ý”ª3¥~¸éé·9¡|¤ëõ6†V¾öÂò•D«ÆN™1lÄø ã9y6§#×lvØ&HÓŒ.sý0–yŒZ¨ÞPÛа¯¶Öwìè½ûìÜvŒãÅt4&Jj›,ШÀ.ö4jª‡AȤ)BŠñsÿóÝ+VËß´pxðäÑC´9SFÓ´±C N»Í”I€§˜V!˜5z}´ãàIÚ¸«´dçÁ?žpÁì/ š:æÞgþðk4RÅ™‰•S'œ˧¾üày… kÚ Åÿ—"]&"ÐQÝÿ٠飿zë˜ÿ¹ç‰G~±”3%))Éß÷¿¿+§ª¶f1ï­s‰x¹1Û ¸ÝÞï/ççÏq|  €6~ì-wm;ÄÇa¾½IkQ®>×pÝÀB:…êɳlµ†|Ê4¡-@ „”š¦k.Ò%“lœ¾ô.¤ˆQô]ßúÁ× ãÃúçk7]1Æ ˜taÏ&V³ Fó¦ŽÆa8p¬ŒþûÖúÜ™¿xëßüî¿ùùï8¯¨ r‘ •TðiFù´r>¾jw¸~8x@â?¢‚B@"ÐQÝ?©i¯ÜþÐw¾õ¯‡ü[~/%õ¾I@Y¢‘v!·ÅÄ{rĆo<÷ôã4\| ]P„¶Z КÀy ’R„`Åç„ÛïC' «F•h粇†Úijqž1TU†@‚4¥šhP ¬(- ƒÉ!i-Ĭ—Ü6¿·â_8§0ˆŽêÎÿýþýF£éW³'¢{o¼Ì8 (/…Id_TÅù9tîôqÆÊ³õTQÓp餙³½;Ö¯Á|5‘LPиÙ?sß7¾æpåþHñ?ûʪÊQjˆ®ûågë/Ÿ>ÿ¼†­kVË=kN,Z@‘‘ðTÏþý%òo´)РH- ©Ù€FE.ÿ>Cã‘ÐQU«UÌl˜‘ã "»•Lý e;J5´oJpÈi*R@)Õ$§±ßcRUˆF@J¶Ñ÷ôrm¼íëߺŠ”_p¥ÝuýE«%i™J/yK+À x7ž þÞuwܳˆêW>ë™çÀô tƒ~ÅÀ¢‚B {¢ë¾ÅjÿÉÍ_º>çQŸª÷ (ÆûÿðÈpœèø!@àv U|]ÉGåMg\·=ðN¼‡ˆÃÝhx¥€ÍäqL¥]•âCúT@è’J& И\Ú"PÁmI~¯¾ú3.»ÝùøÐ’Bí³×\­Š q"Ü÷/ ƒ‡>2|øø"þêWL¡è–ïMôÙ@/èVügDTPĉ€¬ûù…Eœ>ý<¨Ÿã®÷ (¿ÿÍy˜FB@Ç͉T`°‹)Ø‚@X8ÇV °”5Ñ×ò^,gñÝêm¦gYaÜ!…›·‘ÜlÃñ"=iƒ:¤€ú”€Â djÐcgÄ4`ʨ¯ÀH’mPLJƒ’XñnŸºj¾‘khÉüE‹îáX|X/züß›ès€^ЭøÏˆ¨ ˆY÷à œvñ…_âÏãª÷1(ÑÉ©žh»)¬`“"Î6qðQz:pÒl1<ÏS>”ËÇì±á8>¤í ìR@‡´ÅáK2= ) Él³Û¿2uìPd3ß^§øÇüâ¢Ï11.>ôªM|o¢Ïzÿ{½ø(2Y÷]¹9_hªW1iSâP$BÑZ)¬@£!WÛ@ˆv#IŸÃ!ã_Ü’ÙXôÊqÒÿûÓ×!²áZ¥=a²%èMH£éO~þ+s5M+áeÆzíg$ß#8ú]xÕµs8v>âUõ@¦£µ(ö†~Šÿ=€¼J"«@Ò4põMŸEÝï¶Þ'( DcaEjVäJ-˜~–CžqÈ!¾ÿ¯‚ë-fZ3E“‰ìÃKoáø.ÒW!‹Ð¥âÌͽŽÚØJAÝ{YŽÀ³ÿ¡ ™ Œ8ä²@½RH]ЩøÏH¨ HY÷ úõ»Œ£ê²Þ§@@éˆZ)´@€À-G²‡ˆËh4þK‘Ekw5]©S–! 7!ô˜xþx’…’G8æ:lšÅjƒÔ­IÑ ÿ#|Œôì ô*þ'Ï{CßF@Ö}£É<œ‘š”võ>MJZÁ7ú=Ë–u˜FB˜´äaóE‘Kõ?›hWX{9s ÇÌ{ò bW÷z£­—¡I.ù‚|—Ád1àX ù¡±Ši~:¹TcþZð½‰.è½1­^T(:Eu‰·Í(á:¬÷™(  ³W~£¼UÄO´d<|w˵ºÊô$ Sj£ÁÃ{ñ¨N*…¥ xšŒ&'G)*¹Â§·qnæ;ÓÁÉ :ÿãg~yU•‹ýŸ?@{œî”Àw7즃ÇË;}®Äê’Ñ`仢~µª÷™* H F<ÞF‚¦]¿ô7ypµ B! '!°‚›>¥´ãüöc/Ò++6'Ŷ öàz²+é29ðÞ©- +½ð?Â÷ÈÒhT™ tÚh?vºŠ¾ð“ѱ2¬àl^ïCzuUrå¼uŒ]ÿ’´€ž{öoúŸGŸ£¿þ>o:×¹íâ®ÒSôûÿ¾C Vˆ¶ï¬ßIûŽÂe† ©E@Ô)ÔùæzŸé ðYô`àCî-ÖãšK=rߎk²Œ\õ䈚­Ñõ×I-^ý!õg—üC꯸é`+`,ªf¼ù^ç½JÜ©ÄýA4‚¶&:㎨¯pÇÇ.ìNÞõñ‹¨{Ö:tâ =ÿÎzš8j°ØKª#~Ìœ8‚¦>øi²˜õÔôtDivÝkjSQ¿D}ãÝŒÅfØ‹Gæ”÷ä¹?ÊQ›¼­û³ŒÕ(<„rçq'Ÿ~£{¢1# ·–BvX1g Þ·í?N«6ï¥éã†ÒÛkw’Ú/œ=‘Ìœ ¢Úqཽn'¨8KÃJŠèêó§Óá“•´q÷a^“o¡µÛÒÍWΧiüýJŽg9«§kÝš0b xwÔþÔ]þ@ˆ^X¶¶î?†U74gò(ºá²9btùÜÛèÃ}GÉl6Ñ%s&Óó§Æ›ÅîÞ‡ÖB⌳B4=zÑîè—¸hxö­õB‹qËGÏë¶ þã•÷Ø¿„n¼B´í´aç!zcõ6úᯧ÷¶ì£eëwу·\Eù¹NZ¹i(ëøëÂâ°Öûi XL#÷§w7ì¢Ã'*„‚z°fÛ~ºðœ‰ôìÛëE?b=Ç×HÇd2 ­ÉÓo®!w£ÎŸ1Žxj‚¸C øAzmÕúpï1jôùÉ §\_úìÕätX9ž´×•ÖÍü_¢ž]qÅ×s<à ٠ €%0  RÅoy¨•ËEgò[¿2/¸òëÁ÷2Ÿ]*@@BZ;N @»KOÒº¥,Ì¥ƒŠEêõDÿôÒµ4’ïÝ÷©+¸QNÅù¹|F.‡Î™0‚n¿ö5¤Ÿ(=Ey.úøÅ³Ec†hñê­â~Wià…WWn¦ »JY8šD7\:›ÆƒMqçp€v;ĽªZ7÷'AäÉÅkÈÆ‚ɧ¯>—ÎÔÔSYU‹M âܼç(=tû"ºíÿOøîs¬-‚àÔCuEä#‹þ¦N½Ú5~òÇ^ʼik@ ‡ïÉ"žõù¬èQHé¦|lá94“Î]4“B¡°hhƈ|t”çýmì)è²y“i@Q. eŠ™=å»^sœXÅKBx9gâpÊgg‡ãY“²›d9 ÄóŽÒÀýõí¨ ‚:”7)˜ã÷‰ l Û @ì;zš÷/¤·Öî h a?ÕUÀôçh¶É‚ ,\æ; ƒXSˆp¦ÆMÃy:Í:¶ï’a&§ùæûÛXP:Ec†äiŸé‚ÓmOÔIJFŸµ°‘œÚüQF‹='*#d¢‘lýí.Yèú‹¦…çâ2 mOÆÞè³Ó=q ªiȺ½Â® ÆzçÓ‘S•Û¨œÏÖbï,¢B~§ŠçÒ×l?(~7MµG®;ùÑàÖÀ`¾#Äml@‹pOû¬ßuˆö± Œñ¬­0ÓI”êvCeãðÉ3âÀu¼a,O'<^A§+kÄTç®ÒÍQC!ú/%žÍšÄ¯~ê2*?[ËÆä»šŸ·½€ „vh<¶8NõžÖiû¾ü (×eFºÐÂ,c#ôè¥É»¤¯.frLº¢ò;Ñ^áðÏWßã%’& …M«~.b Ç3K×Ñ×~õ}–øæ²- ìP~ð—WXMmeû“™¬š^RXQñ÷—WÑ£O¿-ޟφ†3Æ+|žXüý–ïöÅÁñ~á†K¨ÔL ¨—²ø#‘ýwïþ˜üÓyî”Q´—}–`Z3Çé ‹fM ¥ìß.~o+Á÷ÆËç !â:.Û/,ÛÄet˜ÖÛ&€•BXžÉœItéÜÉm_éô7êͳomàÁA)Íœ8RØ|É—±jÈë ÒoŸzKÔÞ«…®ó½>~Ý»ø,c]ü¥J÷â_ŸàÂr/2Ñd@)œ›+Ex×úÛð¹û;?Z‰×þúãÿ[ˆs:aÔ¦¼;¾ñW'1û[¯J"ºÄ?ÅhŽ=4 ;“èX°2ÒÃ#;'«¦e€VK“ >Pس@‰P{{xÉ¥ËaÇŠË”„ß<±”¶í>°çÉG¾#„·,xƒÁ ôëØð«·B3ß™ ¼å¾‡1yܤÞâoÑé¢ Ú¬½;VVÇEÓ:øÐoŸ£O]=_؆Ah_µe¯pÊøÛ¯ßÒ\/ÒQWzƒ©NS (‡ŽbÓàH¨:Súð3O|öQþ¥‡z/ÉJéyɯ-çhÁàDÊM§777oð…÷ÖÆ¯j솪ö{ݤ¤«éžÊMÁ‡CÛ!Z@ÁóD”È·f¡-i›ÒÆ ¢T (mãW¿hÁ@Þëé3ŒrÛÒÁ÷ad^ÍË ž(§íìwe耢VõBÕ•öÜêH@ñöŸbåÉöog×á–ˆ#hÝîúÛ³+‡}/7½;„ê{x«+1 áÿÎëÐºí‡Ø'Ê^²[­Â.åŠs;_ C´YÿJGJá€r:ZqàLÖg¾)ƒÑ´ìæ.¾ýH_É{6æS )ÙÈU•'…@ 0™— ãP!6:P>¾¨ˆ¶”î!j½à*¶3ô-Ð66œQh3”mÉn?§Ñö õ[! P(t@gÊÂó[üÎè:)$´24Om5Ц0UO" „”žD[¥¥È0°ƒ¸\"Ÿa¤÷r•€Ò«M¦Ç›ïjÚõÊm3w¡„”Œc™"X!Ðs`Y>Vµ© O”€Ò1_”mǸdâ]e“’‰\S4+z Þäòñ—Vòf›9 6øƒÃÁò³uÂŸÐÆ]‡é‹Ÿ¼˜Ů৫jxçã „û·]sARÚ·’RJ×üV´]ã“)O•&%S8¥èTôXa3“7Ä.Ä=ÿ® ;¿ºr ¯¾9(6êIJý?¿°‚ŽWœžj_ágÛxɰ éA@ (Ýãª<ÐvQ&¼¡„”Là’¢Q!Ћœ7c¬Ø¥»ãÂá»×î½ñRºà#ãÅv¥'+èã¼Û1v4¾xö¤^¤8»“VJlüíÀ€öîØ¾Toé %¤è‰Š…€°Û"•MçFöºŒ·õÒ™!öض\vBˆŸÓâŠ]ÜPÿR‚€Pℱµí'”mœøéàu%¤è€ Š…€žØÁ›úAÚÀ6&ØtÂIÛ{ì1µiÏ!¬¨©ž¶%ÿ[ (ñc¨ hãÇLo_(ÃY½qDÑ£ÐCxßýã Þ°-D7𦃅O\:‹^Z¾‰^_µ•&Œ,éì5u?”€’hMŸðÆ” ‡Ã¢à*´‰ãØ[_*!¥·Wé*2ŸåFAå5¼ó0¶l“ûIÏv(8¢ÃG&Œà’GˆM3÷=Í+€Ê£«ëPJ‚À5}fw=Ëh!žäåô“ßú•yÁ•_¾—\¬êëžB@M÷ôÒ*…@#``éD (]eït´9gWߨg# ”α‰õ‰2 )}¾§„}òEQ¥ÈhúäÑeó>z›x% ¤Ê€6…`ölTJHéY¼Uj >À€¢\ZtÁŒ>‘×tdR (©EUЦÏžŒM )=‰¶JK! Ptƒ€Pº(ÁÇ0 •Ÿ6ÐÊŸê¬c”¢cæ(Ò ¾…€PÒÇoвíw=R´éKMÅœ*”’*$U< …€B ”€’x1|ª hcI‡¯¨%Èm˜rôôÚuè$­m w#ošhóFü?yÁY­*Èqò&m.š2f( TDê‹´"—ï»JOŠ=jjêÉßÄûd7Îþ{|~òøäñúÉn5SA®+«VòôfÝWJZ«vKä0  ¿(nh<Ð~åÂ{k«[^PWzC Ï )ëýìMsùÆ=|쥺†F±ÔÒ ÙX'h% ›RÃ3c#»™¨$Íà£WW}Hy.']:o]Â{œX,}ž ©Á8X‚Áàý² »©Ž…R,¡M9ïAW†ñûòì?Z&öOPeµÐ’·C8Çá â>€¦NF l÷Ž^oè¥î+¥çJ hÿ°SœËƒ»Û];_?Òs¨”âE ÏöŽh Ø ¡h„ŸzsÕre “%8’ ¡"î¨"û•Ä hwïk†i¦³ä—ÓËË7ÓŠ {è–ž+´+ðE¡BÏ!°ëÐ z⵬1kèÞ#g™ÀÿåvÑâÕÛ ûñ &2 ÈÅuÂÌSúh2 ¸‡Ø7–%¤ÈI(¯½·L¡dòMâf¸ç…EÏ, Ùöˆ3¬…é²¹S„ ¢×Qh Ë^¯D…2Þ¿°lS¯ò™ïMþc:§‘mK‚,˜˜Œž~tЛk¶ ÅìC¦àðøøÃS£¦à Ò‚Iãò¼øýmä´[X8)£Ý¥§xߟ°ˆÏh°² fb- kbX°avpúFš2z-˜5>mEÝÖ}ë YK7\>.:/'>ÜÕÛq#Ú%ŸäeÈÂ6…ËæÝÉ{qG¤>èúŒ")ØŸì8xœ^gu6³oJÝY"Ž <|eŇTÂ;ÉN7L *–Ä}”Lñ¼øîf]ðYéIþ:QAk¶ Ç*ØÆ¤VtŒN³‰ÆÇ*Ž_@‘‘ˆüÈâŸH~G%=Ï‚ ‘,¬5ÊÓ¨Å,”a÷ä¨Á€!DaC=…ÍU´óP9mçz9nø@ºñŠ94tO¹¦hºH×ußiB6Þaºÿpž‚ÎUu?ª<¥íRЦ ÚTGÜg„¨y! ¸<ôÜ;›ÈÀS<Рè%€+ŸYºžÆ-!‡ƒG¦Üq¨:°Zç‰ÅkÅôžžx¦“ÿ¥'+é©Åkède5Db6')` `cbS9­†‡%&? #’\3’)€xL,ð fCœNÊ1ß7jL‹Ÿ§Yý£)d>E‡Ž¡_üs ÝtÅ\:oÆØ”tغ¯û<­¦™TÝO¾àŃ2 «Þ~3jXÓÛ¤¤/}4R˜âñùý´rÓªoôr§0®W¦x:Ë%FÔ ©–…¨å›v zA· ©AXb—0Öï‘Ãtð„×Vm¡_þ{1•UzXÃÁ+É<ç³ýÕ42FŠé#OϘ‚Cø÷X2ûÙ&Š…÷Tâ ë\@i—ˆAÐañÌ!-OO½¹–w·±ík[’¨ªî·ZÝhB@y ÍŒ¢õB júY@ñx¼´vçanˆ¡zN¿‘l¼E4¶÷¶ô‚nЯBrÈ2€%æzå=r˜Jþ£s~ö­õ´äýíd "Sã\Ö’ðò`Örè:°m‹Ù;CPK>ØN+6îõ7AEò]Õ}]s¼×ˆSh{ ú¸Öy‹W^:|92’ -Jéñr^Véç°¤Ãwõp´¹Y˜:Ì*ú 7&Ò8ë!z¢a<ë=ºæ=0Kÿ‘ß÷¶ì£•›÷ m†™mDXO¡'–tK‹Ù?Œ¡~ôÊÊ­t¼ìLBšEU÷»…¹O¿ <Ðfû³ZHiIäeŸûØ9•pÖÅ~Pô„^ñ¹çð)ò3ÝJ›’§dØUzB÷¼GN“å?:æ²35bµ˜‰µr˜ÆÉÌÀYX¸ÒØ™â‹ìOÚxê‚ä;êªû™Yz‚jždÿ[s:´…ͿՅ.èBJˆ fÙ•7k'ªÙÕ¹!Œ%éqÔ– Ž ?a›pͺ1'W…ĈtV!ªªqG EuÌ{ä0þËŽyéÚ\nx¥ŽoBb éä+`adÜC'*édE•¨Ç¨±ÉwU÷cA«ï¾sÕC-œ{x åeñÍhû. :ÌyV )U¢Qê¯Ï' f5¸»×}° ZЬá î³šfeÀ>Lb›ƒ4§—šèã?òZ][O›v&C`°ÊRCOïÅ"§fwì?ѬYD>» ’ïªîw‡”z® hõ]²\HÑØYUDHññæhð| GRº¬âöûƒB¸ýá°Ò¤$Ê3` }ŒgÊöaJ”˜X¿K€ÿR‹²íÀqQ^ÄÒßXÓÓó{lHk +•­Ó6ÐŒÄ&¤¨º¯g¶ê‰¶¶´K6_¨'úú:-Y+¤ Ñ–*_ bN;“:{ +Ó-§{âC º d~ñò7:pl h4Ø… øÐÒñÛ\ôQ|¬ …§\Ôã®øÍwU÷uÌWÖÖ€6Lá{tBš"ƒÈZ!Ü•*_tôð“Â-[Æ0 -èVÓ=ɱL–®:µäRHÏ×ñð?ò.ûâòRYíæ=5¾NÒ“³8c…ë|£Ÿì# )ìÆ?Jpï*&ÉwU÷»BI=“(Z‰„þÎY+¤ áŽì`“ý„¹ÏF\ÿ.«d^ôW|ôM‘ÄMȦ⟾é¦.þ#ŸR“RïñsÉ»«èÜv~2Fe ÁN¡EÁ`yEž; Ñ|Wu¿3”Ôýh`@Ë+?7â—,»Û]{ôsuÝ{d­H›+.vÝ5l½Ç‚NR†p‚?nŒ»j;ùZÝnB@â×y—¦S¨âà?ò(5f˜\ñž8Ù4Co¾w„Š]fr°&Ejc©-|Wu?ÊBOäw¡þ‹L‡[Ý»äµ:÷.Y-¤ÚæÆª‹‘W<,0›Ø-Ü·X:~ÖÑû]ÝC§*iïê=õ¬{béÔº¥û7P6Râá?ò!S!N›•'qyž^fsêò6ÖSÀþ!ï¬Ñ¸þ΄r#ëOOñ?!";ø(Þw𹺕 Ê€6AàÒüYŸÙ`0].#}÷Á!ô‡¿—s'@ô‰k iôp;¯ Ó®}úïËUl« 'a״Ο‹L ´uW#=ùüÞ$¿`ýúO§©¬" ê›^D`öŒºêÒ|úÉ#'Y`$ÊË5Ñç>ÝŸúšé;??ÑLÙ Vºòâ|š6ÉAµu!zã­Ú²£Î›“CÎÏ£‡ÿpJ|ßüAŠ/@[¸IP\”C§«N“fŸ+É$õù[úóž?Azý­jÍıºí¦bÚ´­^|#ræÍÊ¡K/Ì£þÅ:yÚGo¼SK{xèÞ;J¨ô¨–.¯‰›Œ m‡ÈC¾•×ö˜Ídf6Üäå¢)Û¹+¢¢ëþñ“<…Æ¡£üÿà¡¡”Ÿ×~Åàƒß;F÷|v@ÂùïŠ6õ,=À€vÉÃÆ'Y‹òE¤Ðd@»:=©©XcE k5)©=]¹°€JøÔÀþ*+ÐÏ=EùwM›è¤Ù3"ŠSùú‚yyôò’júÛS•4a´ÌÏ¥úú­Û\O×\™¼ÃTä'ÖB‘-ï%ƒ´b½"ŸÞ^Q#Œ‘Ãlô/ ¢ÜœöUf 2vîLþ»S´òƒzúÌ ÅTÂåeÝæ!¨Êr’ ®±äïŒXÀ8OÿXŽ'“ ã6?ÆA+Þ¯ñ\¶ îøt?bó®VÁn7Òõ‹ éýõnzè‡Çéð1?}œ#@8¹ä‚\Êaa?Þ`öa³Ã"ªòj´éX¹ý!Öª°y£ÉØ­  VÝÑ]÷ñngùÿûÓ•ô»¿–5»÷{XPƒw\-©üGÓ—ŠüDǧ®;G@ÐvŽMo=ÉjM *wËÄn|/˜—Cüg¹øpóöÂ!ÃÑ>=ÒNk6ºiú'9î£Õëê›ÞµÓ9Óœ´üý:zwua´5¨ÄJ§Ë##2GWçSe~úù£'Énw“Õæâ‘¤;Ÿøû–4&QqÒ¤/ßíÛ-÷ô{ut/Ñ}ß>šš†)ù} dÈóñTœ#æøfMw’…h ´p‡]'®/¹ OœñÏf3Òä ú'wV•g‚|Ô uâ8;•WÖÓ{kêèò…ù´qkKÙiþ¸›‹ª³zãíJ2[˜v»èœå'ù¬Õ¹õÆ~B‹'ï­ÑòÒB ÛŽ±±xï“ØFš—1Íë·¸ÉÝ‘JrùýÃß+蓬MŒ° ‚Ž9SeU€fLŽà -JYEµI¹ôæ»a'úÛ®®Åf‹Þ^ÙSMÚnZ²áo¶RŽ3DV«‡&Ë¡«/kMË®½¼ƒ8 UX¢xÙ¯J56ÔR]k}òc·Ói[÷Aggù?Æí€ v. cGÚè¥Å-S2ùoi·0ù£BO!Ú%6r×1‡‘·×7Ô߯iÿ¶§ÒWé´G ™^¯}lYt§d€YØžœ8Ý~šÓ8C[éȱHuì,¬)TñuQADþ«© Qcc˜†ð”€ ™ƒÀàAV:ÉB%¦ù ŒBèä‡V!d{îÿб!@³’þ~?å§ý,,d¦Î^£üûQù¦BÄ”ˆÇEa.¶ë6Ö¨`Zªú£müFy=Uö1}yü¤Oä/úu1«ÖÔÓ7÷£]]H‹.+ WyºK†ã§|4”±L4Â…dñM穟05hÝFcI†Žê~gùNjÞ,—РlÞÎÞ›B²ù—ñ¨sÏ!m@Ë£ÜÏ÷\Ê*¥ŽÈjMJGŽõ^ÿ" ¹Ý!µoà?õñ~Âö`í&öIÁÁi7Йª–÷`³ât¶È<ÂîׯMï+!ê½^A`ÛW@3Ò]²Z7Û']sE• °Ò˜Q6éÅhßB³€~³_‘9n»¤"¶}ùìM…l “ÃÓFv!€tD:f(%ìôéO”Pe¥‰,#m;ZMµ¾2—òîÊ£yGä¡}Þîž…WÒä›ííò Mk- œ;;‡ž—K§Ê´ï`‹á mï(š˜ïùbw耭‚nùdòXƒÕ^ð™2ÑA8° {uÕÕ»©ê¬™ÊË´l[ÕEªk·évU÷ñqÛüãx¼àÜ\Z·¥Ah”p!ùĤþ÷0 ml8ó/®ÌeÊdx ½ê¡àêžJ_¥Ó%¤´Æ£ùW!kBªY Ò6\{Uf¡ßü¹LŒšð¼Á“|ׯ´'2ÔÔ²f%?>¨óHöÆO¦â¢î;)™NWçß<±”¶í>°çÉG¾ß+㣊Ì_ GiŸQ¾ÙCÒæ0‡RÌÇÀ[î{èÑ“ÇMzàÖ«øgâ!º³úûkGyº-ö¸ ˜_¥G½1}ð4PÏ›é¢q£xúo}=]Äum]DÀ!-7t„ò”.ãi)РX,fžz±‘š‚\Mä§-ÇÝäåN;mJA~DÀ®®é^Hƒ0sÿ=%ôΪZZÃÂʧ>^LÞ;ˆ~ôëSBP«æ²_gÙït#ïè±:ïðœ›ãdÍ”¹[Û”Žâ‰å^gu¿«o§°]Zq¡…Þ_×ZÛ“ªüw•¶z–Zš hŸbc/ fe@›Z|ã­e¸ï—Yþ>:—‚6Vû—^˜OçÎÊ¥?þ«²•[Œ”eèWÜZÀÉ熼†ãS!s€ÑѪŽràó…é½µõ#ʽ‡¼Âþ¤¹¹f1Ê–BKGß'{O )V‹EhœN9NÚSÉk† dŒg-Êð˜“AÙG(Èk)Ó}Þ£¦}bõó[@5?TéF@ ) \q&ÀŒ‰` ‡uö5WЛËkùžFñêòrÓCm€rÈ 3ÕEÛy² XšYÉñ©9`ж$±”Ø¢À8úö›úñ”‡ŸŽha°ÀŠPY•>!52ÝÃÂk Iq:´ód5U7°±°o (CbÉFó;XV_ǂŀþÝ )0u:M¢ìÃOÌ9Säá)ÒlH‹€²,“ ¼“•ˆÂÈ:øˆ{üôm¼Ù¶îwG;Œª1¥% ç£ßOUþ£ãT×éG ­Ú&Úô'¬Rh‡@÷­P»OúÆ2…b„8t°…;¯ôÉ#âOü¿Ö5V ìçÑó²Õµt'ûÐ@gµcO#­\Y u8–`žà¹z2øû˜É+|ØÔ£Ùx¶3êáKåâóó¸ó Óî^z޵ 2 cktzXù’Λ)ЦTÖ6ÒÎÃlÇ1œW÷”$”, ƇÄ`ðZÎõäM^j|ýG‹xºÃ,Vù¼øúYá? b£=Q6* Ã…nQÿx&•ãð>EM VÁ@HKeh[÷»‹û¢óòØö$@{X“Ò6¤*ÿmãU¿Ó hy…à‘RÄ€V­òI?ìíRPBJ;H"7qšf·8œK$>|óß—[¦­‰ã_ÏT&òY»oÂÖR2³¤8´Ð.¦v¢§w¢¯Û}˜Ädë>’NUþ“Ȇú4I`@¢ XåcдØ€ö«Þ[›~Õh’tgÓ牵24`-;¼âÜj™°C,Óha÷ß-´§vÎ=X—2%†¸F5¶L[¤,tD”ÿ!¤`Å‹××äµ/“tÙq½ˆ½ÎfÛ. ÛÜH·ø˜Öo; ’ªûá¤îw€2 íŸžxšµBJG ˜;~2´¸±î €Jƒi´[á>¤%t”Ÿ–§êª#¢1sÙ±„5xŒ$ÀLÀ/L®3²âL3Å# {óžf¬¥ å 9y¶uHœäYø°²? +o6ÍwI¿ªû uŽŒmþFy m†¢ô{Ã@IDAT§.²VHé@‡ÅÄš¿Ø]µ£çz¸‡ÝkÃL£ËªfâRÉ<î¼5£¾yü&ʘf„XP±™Ô?ŸÝâ›3|š‘ë@À¶“ ªb-ŠpRg³ ?0pZgBJçš”¶eGÕý¶ˆ¨ß±"àtý—-¶ÅÆl\Ï„ÚX¿Uï%@Ö )UÉ£È1€ÕLÉ͵'{ç1HÚºšéîümõ$Àÿ …çW‰o,ßõÆ;’¾xù鞈6%LÓF `A·‘÷í9ÖYH:MÍà!¿m k”ü4"—}“;lb[xÒµ³ Šô8ÛUb²Þã¬ê~WH©g]!  h»B'ýϲZHin¤X Ævžƒ7£÷2‰Ã?zúyÐ*Ðfeû‰â<SÝ"`µzIýˆYäðšY×¼G¦’áÄxV£òhh‘“§JQؤ߲Þæ Ø7q}õÐ0'»Ôϱ’Ëå`_CN>;…°‚)L÷€·ÉwÔ!U÷;CIÝh´0 µ•ª;Vð’|/k…”æFŠÛ1alÇÎ%ìX-l¬â£e§Ö$1LÙç  ´(²“±‰^´Ã2/)K¨D$q†àý¤¡Eºå=X’,ÿÑËcƈ"Ê·›xÊd7kTJ9öô,ÕMUQ ³v3`ûÿì]|TUÖ?ïMIfÒè½K ¤I,¨Ø{[]ݰы…ðí¬Pº`¡¸®«+*6ÖŠ¢ H• Ešôž>™òÞ÷?oò†IH›d’L&÷þ~3¯ÝrîÿÞwïyçœ{îvüɵ\c«ƒjD†Q„ÕJ‘‘Á?œ³ÚG7 åöÍ/ämwý]ï~~h‰{ÅA ¯m‹°%ó’XœÂDœ\„,“µä›¿¸øg„t‚EÄ ¢LdÂØæ2ïð]¶^@s!]ÄÓÂ4™1™¶À—0Ó«ÓÎõ¡døövMkS8ì’‚­í¹f¥mž˜e‰½Î4uˆj‘΢©–E†Då9,› UÉßéXaȲê…mCÜæ…E+ö3·ñ¤F çé4'!ïʶ®Áq'I†‹TÓÕM1ášô$::ÛSDaSÁHM’Âê6œ-êðmwñî»yDÄBð5 mdÜY¿¨âQÙÙOÿšò0(p±guœ³EbK9oR¡,]Vî°$¦VµÌøZô¬b`¦Šé×ëRºª^j7½XðÞµY OÛï` ¥mjLAቜí7"¡&iUÛ¢ÙuåL!pX6B¥ô§†Aaõg©ŽÓ¼—œss–\Æcé]éö½°<®x&¹4©Ž Çl2Ô:’ñú÷9ŠíTÇh§¦;Õ¶Jœ„ƒ)‰ ˜˜hŠŽŽÆL vu¶ NUOñ–ëí.Þý+ZBÜ(¾´VébD=Ãþ˜d#’ø‰@°éÕX&ŸÙ´6pc’gæ$,ÌLa`TÂ0€W·fQ†#›Î’g[uƒ£-k®ý„.0Ñù š'(7íFQPGU‹È¡ÓÃTù»Š¡Tå`¬Éÿ‚s1Ê,*J™Ñ”·4®Cç°·Íþ3h{¬Ö5`¾Šj{%Pí¯KxBæÉ<2ÒJNììt:©.–&G2é|¦‹R\ Ù%TDR±C³jáø±^Q=Ù``ÒµsV¦Xy¥~ç”H³îƆ…‘†—sû8O§9Q[]WÛ`§pƒ¢1ßL7ÿ˜!g ¿§¬Öa¦„”jÕÀ¨Dy¤(üþ2#Æñ‹ y۽ʿû¼Ÿ€6¶zEA(žçA€ h¿›!ˆO\͹[SóïBš’£²¸ &&Å;aºUw¦Ý³NþJçb"qyà6i«øë’ð¬¬pªcÅR_ÅAçÁ¨°O Cvk’•jÅÌ90ÑøkUS=@ªSϪRãê>ßU ü5XœA¹(Šì§ g_ù} {q/*2z~EùØ<.+íÏôæ×Z7¨¦MÞ‡/¢í-ÓöL[ ÛŸ¥Flwà rrF¹ïìQi2&“ªÃÙ[ð+”åÆF}ªÜ*/ÍÇ«†BF0*f8“ãŸUVY"ÀüÔEg†%1gSl^XÌŠ…´åÔ”(†³šª‰³ª "MÌ”x|x¤쨥‡ÚÙ…ÑÑQ^Å£æ1û]ȯݫò»:y]'_ñÞØâ_´u jÃmuáÖ O™œ“â­ âtŸ¹”’Á/Q©˜ÏÀ0gàæÕÙölìV›MN—“j»3ÈDvÂ&µXQ°Ik’»ÆJ·_—èbžðÀÍËLÝXuÁ6FÔ°¾Å ¯^ȽŠAwZÅõ(m¸”š¡:³³+…;g§=ûb ÚŸ1+¨´¬Mf0‰‡.d”[Û3=eÙþ<ᣫk ¹Â*M0\ž°YRaÎ0Q–Ò ì"FÅíVÀÄ0#sy‡nŽïÁÌÃð9‡p0;¤9­"²`ˆËƸ,Q‘”1Œ€ÑQ >Ó‘Ô7¬RrÔCFFcd'Õ'ºúÅh`é‰çÇ÷4_(á,“’³šÇjµx—sýtz4¢ ùóÔA¼û ‘öî;Áëw¡v ¶Gl@ûÝ i3^­²ä–Ó2Òþ ÅîÈeØPÁȤðdz"-+[ÊCa…AIƒg ò|]òÅMØðéýÊÌ" –;^tÊðÖsŽœ9Æ…šH›a€TèãÙï;“ãÀÃy #%6FhF‚qC¬­ ÐÚ€Í_Åüu\ܹ 2GÆ3+3ãâ0ó§ÿ JR÷ušT¦3íÏ•(¬4@ˆ0¦Ñ±KÙtÁq¾ÌÚžé(ö×ëÊ*M=xLÖ9L@ffÙÑ`T˜Qg/µ¼™¢ÎÐp¬*a†À ³z´ KdŠ wQ&Þ¡ttã ·‹ðŽ Å·C1KcÄ$1ÌœDÜdÁ+ÄÌHX˜USez˜c6³É£Ö4i*YøC“ÂŒ K=8¿œÖ…ë®cÁéÅ»Ÿ-9²²NýcˆD(!Z¥‡–ÜãV0)%IJ8É3§¤âÅÑ^¢ÓÇŽ®®YsԮǩW‡ÅKY@,ý+’uÒ,Ia½g`¾,7@GnÄ ]"í,ƒ+X¢:ÈŃoö@ñ|ŒB”YœiTÉ‚%F^ñ6˜'6ôo{ôïÅoP}í6ãˆIHúsÿÞ¸Á×+>Cð¥Ga:6¿êÁ@´?W®¨>ÀŒ`,;¥ÙÝ”A€’·š©Äéy’.M(Ïö÷Ô•© óNÔšA-˜žø™IѤ‰°ÉbF…÷ûa‰ 3)8=ãÁ’ÝþƒŸ1SÏ ¾ÝnG¿ÅÇ39ØqÙí† žëé9f<ÌçÅL†ÇÖDg@´¥Ä:æ„]¢Â Û¥0sáKçço(ªÝ=u­ïþ±ƒ6?ß÷Ì_8EüØ€6-=åm,ð7¢ëkhožèúMT6“â}~ùæ‹]-;u9¿9ñ` 0)þN>Xy¿¨À¤(èQÌ à Ï †¯LèÃíÙvmÐ5cЎ—%9ú¼>Y–èT´õ“åð°pMÅÃfPªÅÄhÒžLXü)  UÅíNݵyÃ\ò.{üóâóŠ ¾th´1=¯¿)tG—¶ý¹bÅíf³¢1ñòÄÍíÈ>Pžíïéc\ï0/ƒ¦ÔF@J‘ ƒZM¢Fí1)üNx^ NË~E˜Aa&¯ù3÷ÌÜdf‘C§Ic4‰dn&GÇ›Óqÿõµ5ñHH,ÚÇ‚Õ5$%æ„Ï’{öÂårZäSÒPÜvõw_U”K[ÖüÌËÙ‚é½/i³Vx:6 ]2µÓ™Æ] ˜È"‡á ˜”2j™`bR¸Šü•¯MTçNžøh·$=¿ÿè)jÕ¤^©ªÏƒ‚aPߨžÁ˜?ÌX„Ì_lüõƯf«Â_‡šžž')O\}/ ˆögò+ªTTûs}¹l½ŸóäÏ «5Ü£êÉaÆ4µ§êaÈ<ñ¡êÑ ÓÀù0³ÆL ï°lauÞ•,HT˜ÑñHS.èêXëx3sÄå†ç¨q˜9ÑÕ9ƒ¢1$ãpN£¿'|ˆ ÓQ•ßýGýXò»lï} š¸Bò8˜Ýó¤Î¤°ZÐŽ´eÓÁĤè_Ôü"¹Öüï‹îþÜíØXýÅ'o—ÍpÂUšÀƒŠx0ö\{ÄÐaø¢ã—atux ¾¤õòt1:Ó¡û²`ÛÊðÀÍ?fPøy i‡ÓMÀOQ\®Ôuß}õ#ªÎ1üÓ,sܪ° Ó µ;¨Ðhdz›\Õr0èDûsí*ªTTûs¹iVéä¨pÜè_®0·Æˆ³TÑc<ë‘r|­ž÷ƒñâkfÒù`õ«j˜AaI 3).Mš&ŒŽt§r0 Ü—5&RC–œ0cî«ÎÑ%&\ý¨çˆcEµ»^._Ç¿¼ß}ÕíNùíëåËAK°½÷hÚ Ëã¼Ò4=]©)ŸÆ. hË®)‚‰IáZòHǨãÂ…3i{wl!]Ýcúû_¯UŸºçÚR«}ôÁJˆ1(ê(Ý1ƒâcàe£ZÍ ð²ÊGW1‘Å 9ã®—)ÒÅèÞ¯Ëi ÞÌœð}Ý ƒ5ãvâì%Ú¶î—÷33ÓàüN0Rø‡õ¡¾Œs01)ÞvÏ¡3‹é®^»ÞˆöG>Z[”g¨ˆöçzæôþÏÏt†"ï‘ãpÐÚþ˜¹1™TÏän†´‘™ÍžåJÛNëy¿<ý]ëó9ý^—œ¢Ÿë´ç¨×ý2m¡ÿîÙ—ôÑï«Wn>ÁúÞ§é‚6ÎQg3íÃh€i" <º0 -£– 6&…Eÿk~þq9xPæS°µ®=‚ž/NÏóÒU² «xX‚òGbÂÿV-ÿtŠÏÀ¿¦øÈ¸2¾Œ3ã áŠvQpƒJa«¾ýXó‡ó;Õþ\ao¶Ð˜Æ2êåÝþ\·â.ýXTî³²¬jŒ63×,‰ñ¡_idÎyò1¾ü»|¯¨²Êòyyµ;×AÇÖSfù¾û‡’—ýôùÇÌàó{_–M]æy;T³‚^íõ@+ hËò`eR4IJÎ ¶âã>é1èÆ“JÏ>ÏÌþ`Eµ-©=Ú·:¶lXb?*<€x^Q­>ðúŠ¿uæD?–¤ ôŠúÏ3p{qý^IòÖÓ°^®Ë«xØHVq9Ó¶¬YýÑÎ ¿ñ’c¤ÒrŽ|î+I 6&%W»ƒVö¿ndFëüéS§ºô0Ú?&í|µö(ë>PíÏu) ÷Uî¿üNð;ÃÇüÞ¼õ.úŠ[ÓVÖíδäÅ€¯ËãÝß¾nÍ{ÛÖþ”`ï‹ÛdA²Â%)jnò…mÙ4S°1)\KÝ>í'øËŸ]¿6¯^¹vÏï›’ûÜrëí°¹“q ^z5ʦV‹‰ÂM¼·qéCÞ7ïuIJÐ+=mÞký¾¿GvÏžYÙñè”ÜnWÚáä=k6üðÝꬬ }€JA¾üKÅñd\Ùî'XT= E ù¶;žhú/0\ÛöíØ¶¿÷MC¡žÐþÑeÑþLIÞ6Ï{­QëÇ_ÞöÎ{íGV"j"·ó^—¤è¼m÷º$yršüÞý#û’WmXñ홙闥²¼÷%… (Òùz ÅŸ0 -ƒV F&EýóW5OP¼¬Gc@22R••Ÿ}´ ×߯véÖºI«6,ÖÈZfKx ö ÃìFùWŠ€=n²³³²R²ÒÓ/`€JÞ·kûaΪ––0CÂŒ VX™†ˆ',– pàS¿æI˜¿x²bãJfR¸,qá «*0(¨¦t\˜AaŽ.EÑŒŽqÍL ï~Ê?ÆŸ3ŽÁôvfõú1ݢ݃¹Õm剀þ^„Ò{_žø´,ö@ûÝ Ùk@ »Æá(@0)B9˜™ýEä Ê÷œ'+žtµÕ8jÆ•8æeRB™Ya<8è¸èƒ«rôÉœfæøšïëv(zZÜ Ê ×I´{P6 ªÐß]ý ¥÷¾a-}Ñ€¶ô”C03)L3¿Œü"ú¾”úW5KP|Ýv…™“PfPP=-0&ú1ò°#fLøÈ?~ìè ¢Ý½Pˆ@.ôw^GBé½ÏUÑÊt‘c@»‹Ýº£a„m¯ÔL „íê¦ü²â –_J>jiqdÚ™1Ñ̘TU›ßK´ø¨3'ü¼2ÑÕÍe‰€þ.ëï†þž‡Ò{_–ø&oUŠBFÇ}3ƒí"LQݵ{ŠúŽó|Ÿ‹ó’!Pj&ER¥(º[ÉŠ÷+•>ëŒ Kt£I]z¢KPô£_T²Èú`ÅGýÇØè?ý^%«Öär=tfK·»©Êí~@âF•B€ßúûÍGýç£~ŸãˆPvð‡ùßìsе´¾è”ü¼ÔL Š>B’Z÷™éÓ«¿ýÒKKNJ±Sê/!¿tÅsU5T=z] =è¸èסvÔë'Ú=ÔZVÔÇ_ªÒ{ï/6eŸç:w†«.©Òß„­/;/5“b ?¸×T%S¹dý7p¤;'}ò*v1$íÍ(*!¨\ð\ÛÙˆ¹//å€6/"¥¿Öm8JœÓÛS^Ú YÆ1xg|´Ä™ˆ„€@@ ¨hsæ¢üØnvbׂâT•û‚I©*--ê)‚€ —U>ŠzÿooÅT/ˆ³w5¶»ÓÖÂmÇï‰cÛí((^U¹/˜”ªÒÒ¢ž€@@ P!×€¶Ã¼]œ*­ƒo•…Iã;N„#ThˆªvLJÕnQ{€@@ (Š0 …jçz—“~I™<¡S»,—©ÁT†`R‚©5-€@@ ’f@ÛfV£pxúž$îNßiyHPÂJ gn%N$€?äç6vÖ®qDêóPí¼Wü(x"UµJ’lÅÞAV\[‰ 'Mh·ÛŸ²B%®¤„JKŠz@P!`³å^Fìk@ wù÷±-ÌNÀ„Hû±_УŠBA¹A"©«ª*MÀ´D’¬fKµ@CÛ ªp#$)eªÈR ª6£Ç¯ëír«sߘÛï_$¾›!mCÒ]»'I£o¨¼îû<¿sHT´¹º*ÒóDÜ€@ dá:¶`6ÁRª’,½$[åoß~饋U `Q_€@@ PÙxfúôêJ¦r+TB}1ãùŒëCÇOþ±²Õ¥¬é­_ŸœçSUÊ1(*=ð,à–ë’,¬ÉɺÌ_Ö©ô„ZˆsÆKO!´fžŠp(g>0H†a:ƒÒuþþÚvGö“ª¬lJÛéWo>!~pIʰÉñ£U…faí÷npä#M[âŠê E`ø¤øøü_€ ¶—d¿xJܼ­lTlô„ W»\ÊpH¥n’šÕi³`d«l.fõ›µ#³ÒÏdé_ËCÿ[Æ»Öòy쬄E88“ÆuÑaÖžA. éIí ÑÖRðBo&ow„ãU…P&Åà¨s!=ù”LµŸXlžY@u¡ŒÀ0Û"+9Ͼ©ÊýŒŒJá­ •Hب< ÆÌ…”F$“mDÍè>×ùª|òó@ÛfvâXô’7 2ú¤1Ç¡>Z,7–¿L| =KlȦª²Mº¬:*œšÊý4`L «x`Üü2übQü¤«¢Ó™ÊÝõ€@ `xõÊ𸩟`½’ò!Bõs%V>R“Û`Ëó•Ѩ.r)’]R”U²9üš×_ë~Ô7Õ³LÝ\n×ížn@›¹ö?Àú0&ï‘ɰ$iT»ýü¼Û"Õ”ž¶çNIu³TfoòøNÏûæªçY‚ÌF²h «xX‚"”Pí.¢^@UE@×1¾£þ‰ø-É÷«*WÔû¥—6Õ„Zç 8]Û.G¶~cnŸgÚ¶ K$EýP&y|^…3¸i¼s+lVlTFÀ@ôלqVUÇD¯ÿôé½Îã¼~ÍÇÄdû+$Kû^ŸÝ¶$„<´ #[½ÎR“6³ïc©‰Ó¡vÂÜú2$çHU È)$oD’ÂËŒÁe¯F²!ÙGD¥€çy¼×ÜKx¼Œ˜°¶¿"I£¬ÏèÏFŒ]ßß^·Ù±öA y=Ð~7ÃØ7=uÜé{¤&Æ&†ÆºTEOS•Ž¥–¤°£6—âjÄËŒ«p¢®€@ ª"&åCUQ?àñ_8|»²Œ°mŒ¦T×dRŸ¬Ff“¡ð;3 †´˜E,öMÁha@û!üáÃÈ1°’ÎÛÇé¿}ãUÕóR3)ìI–µ±”ª ¢¨·@@ ¨Jðx¯f(JŽ'qá•6OãK©î7±l{‡JÒÝçS.~H’ô­A¦g_ŸÝwsž¨Ú¥Q6,­Æ¤€Y¹ï··bFõ6Eø:P÷4eO²ÂQ[~]OÜ¡‡€6ÞcÜGÍš†^íJW£‰×FÁfçA¸âh&KêŠ1Æ.˜Ów¨/ƒÂ+¥ø§—Ä´X±ãaör hõgùÙÙ[ìÌ]ó{j÷J-Iá½x ïõ ‚@@ Tx‹T•Ç|˜1£_Ú˜7ÇÎ}µÇAŸÛÚéøñëêÀýì#Ç­&‘:7Wëq°¡à"øEÑTAà`øÙëú³¼G§ñB4¹Õ1Ø)ùY“$Û=®ãŸyã„Êu©%)`£ð{ñ„JõÅ@€Ç}ÿ‹µÊEñePXb2rìú럻n™]Q“%•ZÉ=<N?/ƒÂåg@[pVkµHj>ÃóîN•¦/î—Z’ ˆ:Àи)kñ5ôÞâ)“Þ D~å‘ÇÐIñ› ’^¼(~ò¿Ê£»‰+Q9DB:KŠZÏߢ†ÅůÄ×Ö ƒ|óÂW&ý7ý¨©Sëff*§dƒtÇ¢Wâx·ÕÀIí‚Íný¦9pˆœ¡‰€]¡·0&\d©‰¯=JaµÍÏ€v´!);#ýâ¤(ÃTÅU«ƒ–P¸©}òómÙ/KÈÁ¤„|WPUê;|rü³(ýÍ@SpÜd„íµÞ l/ tÞ™ŸâVŽŸ9³Ã¬ 2*’Q¶@@ PzÞ˜Ó÷>saÚogH[I¥nøYÒ3ÒK—/ì‚[ý%ÉøbrˆKMòëÔ6)ùe*î  IYUìôg'Å7$OOžÞ/o£@æyIÿc#ô´{Hë—ƒkA…@ x`Z:ø¢¶w\‡ÕÉ:=êj½ÎyB’’qLFã$§Ó=ÈEô62¼­8™Â¦ãì: †e]U¢³k~ÝÀØèe›íI;§:nÊPEu½ÂçÙNçJljéÄò÷sˆ—€øM±áÙÝüLÃ&Åÿf)lÑ”I|ÕN(笾?½$~ÒHŽË;¼ªî³S°§è`xÑl†<÷!þ²úÆ—gúª•ß_ñ|pcÜcÇÝSÿ :y—ÓÚaubê,92[/×÷8trüRèƒÉððBÛK‡}ŸùžC_}XQ¥©`Tþ1lò´Oyy“ïóü΋K7§6uj}ÊTyµ@?üP”ºÎd åre;ø¹oÈÙñö5`:âêºÀp§A’ãÞžòòo¾ñĹ@@ PrFOX×ErIgçÎísÜ76 ÍJ?7ï_$~íÙ퉮\¶)?vnR3r»nGœN¯ìØpáž±íy_¥ B’RÍ<•Á†Z™ðB<[»ß:lòÔGŠ¢ û<æ`{d”‘óG¤zÜuÜë$Ð-ö!Î2-/I/Kr¼*Éó`Sq÷îc›SC/çÛÔN`(ÁËcßáÿ7½§~„m~4ò¸WVÕÃ|Ïf[fVg7€‘x —ÞCÙK2}‘wNeëùËA’j!mŸã®ióÀÐÜŒÉû <œXƒ‚c_€qÛDƒA²Æ p IV©Iíép’ {~‡éº\ð•gþÐ=Ú6·šš¥l½× Ml$ÉÏß“.§c¤]àC.‡a‹™€ˆÂ‹Ò7`ÇAreu)Êêaÿ7uÐå˜âL ( ªb¨¯¬oàö›-Ñû¾³-Æ¢¥zÞìV?çcÇY ±ð‘ò¹\0¾öǾ>{0Îv+Ê/æï9)³¤ø¶¾8—ÃÉÆRH-…ÈrÞ0Û¬ÛÆŸË¯€óç‡eŸI‰Éð›%ñ“ïʉóÞð¸)[À,¼;i—sz—SE>ÒL²a`îÀ-M2‘­¤õgFH•M+9Þ ×þ秃‘ÌÞŠ!‰ï!|ùtÜ´ÍnÕýµ^¶ç6R’Ú þ :50µék³=p…BÆì)EQ§‚ãz$?cX=ž~Dž–ÅÇ;Ÿ±Å?åv©ëŽ;÷½ˆgšÔHã{ô‡î g$FR]I2·]4e¢¶í;òZ)O°ŒÃ3oÖê±³CQË.F“Üõ-Û¤9þ54.þ0˜§×píeø¼‰Ä‰@@ à?Fã^É¥|ß(¶ é—’‘ÁÿôL ²a1<ÐÓ®}<ÐÆÎI¸Öé–Þ‡ädŠÜDz$ñöÞ1¨í¬ÝÙû½HS =ÿÊt’”ÊÔZ•ˆVhY´¾%MÏ€ °+knAä;Î¥õ3ÒX6äÞ«BµÈ¬`Ê””¾¥åû‹§¼¸ ò€S¹êñPæ]¤¬FâÕS0“â Š2ˆãrÏ õ>H~öaP´Û ã_†AúC%塜”—ªWƒÁ„:üÿâïc¶R™gN™ä‘ü\N]Й†×Û¶¸ ½ Ú' ·MkWPd RlºAÒm¨óêEñ^EËÖl?È›¿G…%íðaP´(ðÉÀmÑʼiĵ@@ P<–-S #ǯ»>S¾u;íkñËa’;V_/ƒÂ9åõ@Ë´|_U¤×Fº9i|‡w|í©µpÌWíÌÏ+k’”ÊÚr•„îŶB"ò2ÜDÏÇñCøãXÉ (6r,×[ð©â¦¼L×ïCE©'¾õÕ¢]ocŒŸñ ä´Ãl¯6Qή‰Æ+’jDúÑÏÇMiþFüäC`ZAp IQ8.ž]z¾àó|¤j˼÷¦êº„!ï#ΰ”!Ø [˜ÅWF(úŽÉXíe—ëÂªË µÚï"M»"‘Ÿt7=ŸçÍÄþ§ã|î…Dh˜`H,h‡=¾ñÁD²*Mv_ÈhŒãßgâ\ (Q/lÁ˜djÍÆuO`ôÛ w‹jDöý6o®‚Rúz eZÄ›Q®y÷˜ŽºÄWKÚþÍÄHW¦:#è-TÝ|mAùUÖû‚I©¬-W‰è®oŒ{ö(D Ÿµ½ÙÁå¼èô%Û™G0·Þ…íLrÏšˆè&éoüüÎñâ¯D°]J¦+ãNìŠq¡ž±ßØßÊP¤9$º¶ïgºÒ»@r2/Wªš‹ï3•p_ºBrðæ?žÍxËöœ7šï X¯¿Cyò¸•G`Øû T^l8ìW@ÞéCÿoêÓäV¾;éŠN3þЕyŸîpà{ÅÖœ1cìÃ⦺}3–T´…D›a(û¡ï}FH"W¸t6×}q!‰€âÊÊ_\LúÏžÓ÷H‘ !?Ú1m‰µëe’_«ngwŒ5½ÝYPgKôu¸!êºožVœ¼+SÁ¤T¦Öª¤´ò ™ç&¿ú”Cunsº.M3b‡DÃ[Y•¸ÁŠQÞ°Ð6i›÷'²…~rg©RºËÞyßuÉ76/.‚õmÜ”ïI‘g¨Y‡ñ%"›Læ•Þ¬%u+˜Hò ’ÚyíËçI·P·y`LÆbòOªhÎðɯ®_4åÅ&(àÁ’W&}&g)˜žiîlõ2½z|ÿèæA±¡žT?>óÏYµÁLrÙ¤Hê0*Y ã'½§ÇG€@ t,˜ÝwrÞØ)å…Ô ×(¤ÁhX¿ƒSد½Ö=…ã²íw3䥸Àðئ2ÌbˆiW2¦`UÏ»`Là_þѤ{'LˆÕ>äbçìÑaõÖá>°B#hzðÐ¨Š¨E0#ðæ”±DîUhožSÐ)­FÓï˜(3T§¶©V®GW^H'ùžÃífµƒ7,Œ‹;Ž—6I&÷u`PúÃÄ«Â1¨ÒW˜ìHªf²ë-ÛÄSÞ„ª´ ƒÃ`ö²>×4c\deZå½YŒLð8Zcƒ±Ìà(Šk¤G‘ÅHzE”0«a4òÈ&—{ÈŠ|ƒ?t#.’†£¸ß,·ãßk>«°<ùÆç&Å7ÍûL\ A`ÔØu÷žKY¿ ÊB¼ã5¡ÚYYé5™;¤°—ÐêWPÅÞ¿ ¬‹”<®Ãˆä {ìßéئÌÐ-ž›úd¤íÚÐvÖ®õt•ý($)•½+ýõ ­§wí½_ï7a:ôJLÚ&œÁrÝ)0>™_$ÙDP5H™ªKéÁG/’ oꆮ7vl¹veÂÞ XÍ3é8ˆEÜú*˜Lü¤ò&ZÅZÍú£M¸)â[¨y,˜å‡Èõ=ý>cª…ÍN½”}»êÊZ²GMæŠâ쬸]pi-%×7ô}Ç7~qÏÙ·Ë0Û´U§²ö%¬òy¬¸iõx ^~ù,¶ƒ/©÷=÷P³œàÝF“á5—Óý×Ô”ì/žµM}A&ã§ËÝ« ^A³ô<µc¤iª”î|Ì)ÑÏXÙÛãN•²›Û^€Õ)‘-W|q!ø…ÀÈ1k§»Uu CߘÙ{žv_ïcŸŸµ'®m€”Oðýü<Ðâö|=MÞ£lVåjhÏ?ê4óTÓ„ õ®PŸçMì×B’ì-Bôi+bTé©+¤¨ã¢øI¯Á¡ÙßñÛ©nr;•]0¶}t7I•í: <ðLTä ˆ×ÎAêa8uóªcdj•ØÀsÅܱc½“ï<Û˜K°WYÃÏ ’äe^8OvAo±JìlîòüŸÓ™}þ–C¤°Ù!÷ÓUF×ß°Øör2ê4Æ_°túoþ¦çøK¦ÆaŽôCÞ´þÐý–í¥X }=èhát*Û³Žã*¹ácÆð0·\ª¨Å/¾˜"‡KÝÁH&‚‰ù¯¢fïCº º›—q-ø‡À˜~o‰É Z1Õø2(œË§ŸÂ3Qt¶ÑÝ÷åŸS—¥)ÚËqûFRM,?ÓfÖ®$EqÏÀøøEDtÍ&¡À pE/šåªvñ/ 7ÿ…cã k E”6~ÍpeT¯e>3ã… 4{Ú6½™LÑçØÐ´´erzÖŸuNm|mçÖÇ4f(™–CþÐÍj­’•¹¶±šZª0ò–-[fø)ao3²È™7´ju¦2aRX½Ä³À Æ~ÿq9fýÍH5xþÜ>¹˜þ‘6¶Âj¾…°­Ý2Nß|s^ýfíHx =‰ Mm,I†~ì¶í¬=ð±Áö*PÛªŸÁÚnQòض9®|s¨Üç‚I©Üí'¨‚€`Rü‡}´m{5wjÆ:¼ŽT peïV{Cb9þ•xCÁ©oÌíûÖøñ;#fÍêœKMÚEPi´p™ðþä÷Si»¿„Ô䳈Èê˶oé?5•#…P÷TŽvT @%G`ž­ë%8cû+Ž1’[™¥¶ªø4\ŽlÍ WÏ®¦ 1fÝû,!Õ«›×€vVfõˆäñoK×þ½PfP¸þÂpVïâ(2F`îŒ~[PÛÁåÞ˜ÝoîócÖÍ;Ÿº~8"¼Í‘òÐÚ]©ãv´œ&T‚—S • ‰z€@ ˜3qmwl,¸tÄø 7@ªr…Ù…d¤X40зEÐúÆ ¥sÁ¤„RkŠº@Ð#`P¤£ ò6Iq?4bìú-#Ƭþ…¶Äx WäÎXג˯QxD¥àf´E0¢mÿÝ c_oü>LJ7®¨š@@ ³fõ=Fc˜Ž¯¢Âƒ±ª'<Óiÿ¶(kž³vµª¸gIíK9{ …Ÿ§¥—ï)š!íåëÐ<LJh¶«¨•@@ 1’,/VTiØôé½ÎÏŸÓgÖ‚9ýú›ezÌ`¢§M$w˜;·Ïyæ5 ]=·Zµ ®b@HLJ@`™€@ øÌŸyÍÏp ÙjìØß½[|ÌžÝ÷Èë3úíƒâuFé›#лÙVížJ–Zß(!w.˜”kRQ!€@@ vàï³òT·ìnà­UÍ€V0)þôW B`Áœ>ï¿>«7oZìPÕ h“Rì®!" €@ b¨j´‚I©Øþ&J€_T%Ú`ö8Ën|ÜøžûÕ U42V¸yŸû^{TâÑ?*qã•’tß¾Š}»”ðˆä¡Ž@Uò@LLŠ>é°tG~|ôKÝLaa7‚MiJ²T_&IÛ2Ô;_ ëˬ4RÔ“`QލŠúÿfØØ%seØEÿtç¨äù…Pß®ä-!ȯØ€V!e—±}¦„¤›ü``RxòaÆÄ0pà]Ö¦Ý;<n ^QÔzlýe WªE[¥p“I¨¦’¿Áît*—R3Õ´L» ÷˯ ŸÊåtÏ3\P,^lãen¾_¥þf_ñEÿ(”+a!з+!ê‚ä`A€ h³ÒÏÍÆéPÃÚÛl÷¼|ÂÕ>›H‰!UŠÁýhL¡1½þ©ÊêQldxè†v­v>ðÀî`©Cqè¨h&EcN@¨éáçÇ Ž®Vý-EUë·kÑPíѾulÙP²†‡ŠS§@4æ.ÓžM»§Í‰ëî>pìU¹®<òo/üßSï¾öÊH©˜ºbˆþQ±ø{镹o;¶‚¾ GàÛ̉jMûšÝ)J½k.¸a«y*åÎLR–b”œªC —\ªÙ@`KT·J?%$§‹‹ÿIò›‹§¼ìת¢Š‚ƒ¿RK†ÅMù…3X?y ‹ô¯cf’Ì}qd¸%â•Fu««îehÕ¤^1³ÑJ‚Àþ£§èã6¹Ÿ¹(a«ð‰ïLûÇ<äÃŒJ°HUDÿ(IÊ4T úvÈ´R Çþ©EVdØÿM„}>†ÇÚ:5 G©ŽñÕ6¤jÆd W.Ò\d¦,w ]t7¤SîX:ãjåv©&4DÔ°Ÿ;vìŽãFØæG»Â2 ƈˆÌ#GB:Sq¡"˜}2¡ÚaŽš0ÊýÏîm›«ßÞO2›„à¤<ºƒÃé¦÷¿^«nI:$)Š{¿¦Ûæ¢Ü``TDÿ(Âeqß)Ô“R1ÍùÌäiý]Š{u”|ŽºZ¾0Ô0ó‹HVh¿£íÍÈÛ/¯\÷g“áÞ¡qSÿ»–Wy‹©,Žåɤ°¤„¿’Á„‘åš!C†£fuaƒBBÅSM[¼<û‡nêe˜ýÁŠúuÚ7AßÓ ¤äNW¦/êDÿÈq«äQß.y%DÊ*Às“â›Â’d‰¢ºo´Ê©®¦æÍRmãAŠ1œ”`cÂó&Ìa%ÊR£É¡FP5ÉRãb”4(rd‘R 0²-2¿ù<µ_IW…m0ì¶ßb8æì8mØäø>ãgÎ|hÖ„ EfPÂå)Áð~%ƒÖˆ˜š5þÖ¡e#UÉ–°å˜ŒÛ€Û",<|²eƵ<û…^Ñ?t$Ä1`IßX}DF¡‡À³¶©]œ’²M¢ìë:‡E7FÌ6¶ û•jþÌeË «œEG‘íZŠÃ èñù.¥Sw˧Ô9ükLêÔKöå#æÏóÈóòšŒ|¿’Ãûß|Gˆ¬ja™1ß!à¶€•º÷Ñä0ç^žm#úGôP%¡‚ûv¨Â*ê†Ù¦ÅºœÊ/)-úºÈ7 ÍÍ[`#RÞBì’U¤¹y3]þ¹ j¯Ï>úVÉr):Uy3)l0k©Ý°Ñ@ÖcÁJÑŠå‚··‰5*êFXQLŠèåÒÚU« îÛU lQÛb#0lÑ"9ݙ嬈~Ö%Æ~×Ê(È3ÍÞ;+¥‘)Ú„ý¶Jý ©,cŸŒÊ‹Iárt{”p“9¬a”%L…£6RÄiE"ÀmÁÞ}I–›æ´Uyõ ®¶èÙø!^v÷íGWT¯¤¨GÏŽ‚¢K×ð/Œ¬Æ DhÐænðØÈÁ=ÃwF½©ïÃ?ÒÏ쥆ï!cX ™#jÓOï¥È­Q$3)i¸à‚8|J@2Ì“IyMD\Û:°Ñl˜Ád¬S-&¢<Õ yª-.óCÛÈp\ÏÊÛ.EôüDÜ Ø·V‘Qè `³-3K¤Œ¯m8 Ö3î HÅ$ÙD­zO ?6c ¬Y®V¯+u¿ã}ºpb#müôNúiqGre§’#ã,Ûó1µîób@Ê•±ô*Óz£¢RÏá¶ií’©O&åÁ¤03Â?–¤hŒ ¶™¶b/Á¤ø4D0œr›È’æÄGW÷”G…Lÿ8}>•N ÌQYõ‡l‡“’Ÿ,0û=Óšm4 ,¤TPß®€šŠ"+'œûîTIªÛ*lmÀÆØ­ï$ƒF'ö~©AТÛst<é3Júõÿ(åt›<ö.‡¶-¤:Ío ÈšmWã~-É­²Mc@3 å˜â‰/‡QaGw&½ù9¿”ÆŽl(:ÒJ®jDßÔ‹ŒF&±t'fõkQx›XT²€73åÁÀêà]ÿÐ ËïÈ}§G»ft× n¹½f;¹7 ¿÷º\÷ËêâèÉó4íݯµìeY‚ºÎBíZ4 Çní ­]þ¯XâÁô¯å¿Ò¼ñÉxåëÏLÊžƒ'iÀռʪî%Ê·búv‰H‰BÁFÉî®m8Tú 'ªÈÚm)õ|–*{üqÖhØ‹öoœMíNƒ=Jý¹ë¿tñäf-¶=ý$9í—(ºV;J?_úKŽº »17 tË•ÇDÄ£¥þã1À»]þ#h kWD~}:·¢ ½•_ÓÖïÜG[’‘¢èÇ.·›~¶Š.¥e9c`…ÞVú±¬©ÔËácPõ+þäýé©»ú›¬ÔñŸºûZšø×!4¸wGÚp€6ï9T`žWÇ6¥¹ãògP L"* o‡r¢F@’”þÌ r%ODL ʸäy÷ &‹fÒ¼Û³äÈ„W{„^÷}N1u;{«Âq­Õšy¯KsâP­ZrURÏ—&ŸüÒ^ù)•_¬ÒßóˆÊƒ1*ÅÑáÔ¢amí·sßQÚ²ç ]Óñ*-í¦Ý黵;ÀldQóµèÑ!½©võhʲ;(þ¯´kìÖ¬Åý×ò5Nw\{5-øø'²g;iîÒÈç€SŸ¿œ.-ûñwÚ¾÷ˆ&©¹®G;1âÄK¿ß@êÖ€T'~Û¾ž{ðzºªQ-ß üÓÛ«¼HÐËãcÐô+ÿÉ›Hbö/·ö¡ûþ¤_·&S§VèÇ »!U3ÓÀî±^ Å»è3‘Ö0zÎ 9ü¾ûúæ·ôÊ3÷hê–Ÿ6%Ò¸¿ÜL1QVúeKý‚¼ø:*ÂrYujDS“z5©YƒÚôóï‰tèØêÕ¡…F3ßý»ÆÒ'?nÒhiÝ´>úâ&­ƒ~¶3³é½¯ÖФi é_êQÞüN}õë6Úž|”2³dEXô8ññ[Èj1ا½ç‰Þׂ“:AUÕ@@•z5%ª]:ù»†ŸpþØü:ý¹ûCí<ªV[ªÓâ&¨~vj×ö´˜¶gœ­´<án»vÀ¿òœôÁA.¤¤ÓŸ§.PÃÚ54º?Kÿþß0 u!:ïC™`L˜ùà _ctñy3=¤¦gRZ†ú@™z´o®Ý¾ª€'î觯ٶŸv U§8@IDAT8NOÜ>€nЕ¾øy ½˜ª=KE:V$[lƒèÓŸ6ƒIΠ/Wo£ÞZæË pú½GNÁ>„ü';lNzæ0ÚLCÒ¡`„×S·¶MÑ7[ ß:µþ‹ž¬ÍLÍèïw ¼šZ7­§Ñ«ÓÄynM:BŸB½­¯–îo1ãTXŸÖÓé±"úvB!Ȫ( ¯Ž`¯ öôSQOËÒåH§l\›ÌÑÞ"øžÁxù#'<ªžÇ¡„'n˜šîs pñÛþ·mqK˜MÉÊK’’‡€Š·Ia‚Xr±9ñ6ø¶‡MÊM½;htnÚõ‡ö•û8f–0z}éôDZÓ`dªk÷òûã/ÓÆŠp`›”zµ< Çîj_±ukFÿøþ¶ä#(¯£×ží¢øg‡P˜¹Ú°h5ô_pô@ÔêÎ]©móš´ŽU1ÌPtnݸЬƒ&™õþ÷têü%ªS#Šnìåé›ù%üa}‚v›¥",A©‰Ÿ˜™¾ûÖnƒÂ÷tæXž|øìkšCÊÓV»uàèiJI÷0Zû@k£:Õ!%´¢ž/.6¼åPTŸÖ"‰?€@ _à93Å¡Z žLòMUøÍŒK±¬¸¥7ÒéC+©Q‡‡±ºçw2[kh«}ö®ê}niNGw}à½.É [nlϺ‹Ò•Z²ARŸ.IE¥)&%h¿\X¼Ý¥MúbÕ6„Ûã·åbj5¬ãa6ÀÆukj8žÇ—n~LŠ–‚‚[QhßÑS`pÎàk÷°7¯ÑCËÆu‚•A)¶+2t¨ËýÈ‘ž¾• IF~AA?ñ -×¥Øfõµ•8ÃîT !,§ùð`MÝsýv6›Vl ¡÷ ôf‡-¼ç¾',<ãÛÞ<¢Z~Æj£]ŽiÑÚâ`) «­ŽŸ¹HÕ¢"4†«8}Ú·œ =é~¤˜ ²¼HûS”Ýq0mFÚÙDªßêvhd šñlòo¯P—›ß¤ž÷|BެsÚÒäK§¶j„GÖ'³¥¥Ýã¥ÈßfP6f=ªžv¶æõ'//ŒŸ¼Êß<Š¿<˜¦ƒß_qh+ó8MêÕÐlXDþõšmÔ½]Sí«‘¿\·ì9â-ÿô…íœmXdÞ„Åæzà¼fL„~™ëÈ* þ2Ø-–î¾.÷j=bpm°¨I1ô¶ÒI,ë£^ž~,ëòÊ-³)ÿWŒû…/ÃrìÌ¥\4±Êqï‘“Ô’»6좮±MØ#p®8y/jDGhfˆ}ƒ©€k¼ˆíYØJ¬úÔÃÕ(óûµ;Á(€ê³Ô>4`íWdŸÖó®c…ôíà‚@P 'þpÑݨ»]Ôö Q'ö-§«zޤ†mï×ü (îlÚöíS$ÂHq³jéòÇ4/O>ýÇ J;Ÿ\⢨yÒݵøë 0î¿¿fÉÔ¸õ%ΰ€„ãâ È¿Rܾ†¬‘–púì§-½,Ua[€ŸOÒÄã+7&jêŸ ëhËŠÕ­]ýaJÁ žŸ6í¡,ê}žLXz’™•­Ù°t…´fSâ´K“ù+”™š¼bw=½8VxõÖ¡ãç¼?—ë²RqjвI]:ðç:yö’¦b›$=ð ±÷¿^ ¦¹¤$73É?¢NODäœ&ñعÿOj aqCl³šê‘—3³**ÉLJJâÇ)ÞMƒð~´½>¢EŸ..Â"ž@ _þ£`)̾ìkó}X’›ªâ¢äµS¨Qû‘üò 3+¾ ŠÙZ“j5½–ö®›V’b¼i ä¤ë#çzX–aãÃÔæ(c •Ǽt’ÿg^€2¯,Ù°-ɽ×÷ EŸ¯¢~][kªŸûoì…/Øútå&HI"é©»zýžÜÔ§}õËvzaþ2b±|O$êíWXj«y>þa#Íó0 '{Ðß®£y°kaÃ[K¸™ž¾ï:mµžN+a·Ä?=Øž¾[?-Ö±' ¬“á³ä•%ËÁ[èÚnmhź]ZÚo×ì 6Â}àÆž0– ׌Z™f[–ü «y¥‡†°¹®G[º¾gñ>r_ef„ý­°‘W·í]˜áf{©yþ õ[–"²Õ×v}ZCHü J†ÀâøÉ ‹›ºð ãšgkK L„øS™ƒ+‰…GæyZó~ÿ¢û{›mhÚMuMû ³þ¢žs5}çÛÔoÛ&y åŠSÁ/³[Ç)ô 6ú…#ô|Ì'ðZ(6)fk>VÐ×û˨‰¯wnתíØÇnÆep^rÌLE~ 2våå›ÌÍúªrÜnE“ºD@jS„ä>¿âÊüÞœVОG¶ü{fü(ŒfØ‚Ò?ÿTVêþáus§Èv¸Ð‡*þ[!o?çeóç-£‡o¹†µ-4&å×mÉ´|õV8‚û‹·ÿ{ŸÖÑ® ¾­’ÇbŒý!Yï@UjÄüùaÙg.­†kªkb±I_˰µP \–ʪœòʇ}¥¬LëvJa/‰ûK Êêž",ˆAád1(üŒm|¾Ç›Hkp2(LŸå@00(\ë¼ýœjÙf…W ]„êóV¶%À÷K#”û2Ø¢O—Ÿ%† FŽÌ6k†yÀûÉÙéû´Ü½*måÌR&52'`‘zoŽãÄ€ÔE0)Qd"-˜ÿû]è0 xçô#|ûlÕ–×?sÿu¡UQQ@"ð–í¹tŠ4Ž‚J#Á­š øU 5¥/:Æp’ÍsÇO›æqØRú,µ ÿÈB 5Ø€\÷ªjuõãgÎŒH½”õ›Djû®áË©‰yG0Ubd5gÕ«Ó0nKHRJÜ"¡@@ J†«DRSìÿEê½­È%eP®¾õ_ÔºÏK^"j5@×M ¶þé½U+–nq4ׯN‹ÁÚóîw}H-{ŽöÆ-ÍI¦ZÕÁJêå±¾/Mf9i+Þb/•YBÿ`)2¯ãb"¡ƒÀÓqS‚jä®.–o¨¶ñòJAjX½~wªÙ¸í^5AKÖ¢Û3Ô¢ûR\ö\Ù˜Â=Îm7ùPŽÏ¢ô‹´8lšGÝïxÞg߇ӷ ¹Òù{qÖu•}Ï›m¯ H’”€À(2” ì‰VUs{£-›’D®@y! –JîT3œRš™6—¸ØÝŸ§ãIŸz™‹Èš±´yùC”•v,Wžf0)̸œ?¶ž.žÜ¢ýœvÉ‹'7Sú…ýÔ¤Ó¹Òø{‘¥À1¤» )Ò—þ¦-,~¥fRØÕÓSߣ×Þû&W7'¤1³—æº'.•ÞçãµM/ž:¯U# ò®Ëì™vág«àTî,VâØá¿g›æï‡77d)Œ@ð!ðÜ?_½JQå6ÍÍK5GÕjGiç.ûXIøqv9N IÎmÂ.ðùc§Ãõ3©Uï Q½e.PRÎî¢èZísÝó÷böv!ç”,ôoÓ¿T–qy=ãå¼ÿoÞ&‚@ à=Ÿa ‡:Ø!{ú»ßû4á­ÖlÛ«1*­šÔ£ZÕ"Á¬¬¦?Ï\Ð<Õ.³²K†E‚·âÖ8‚jòÉ'Ã)<²e\:Tdiç÷Ñù?ù£E¥zWÝBýÿ²Šê´¸Ñ›.3å—æÞkORÜõè°«»)Ê; ãâŽû›¾°øÆÂV†gpàªíàú%œLumÓÔëÖ—ö_°AÚªß÷h»»¶Ávô·ôíDÍÖÖTŽù9Ýs}wú{”\„«ón±Máý³-½ÿíZ¸½Ï€ßL{Î{­p`f軵 ÚWk» èÑ[zkîÚ ®ËwÓ1L¼2—Á»ß.ý~6f³b7ÛTÚ—åõjEÓ“w\«y÷ô¸?_§¹"gŸ[5¦{®ëî­—µ;Üò®´M°Éáðûiö Ñà[gq:´‚Kú~]ZkZ·c?öÒÁîÄu«i[,ð~P¼¡=Û ©ÊŠ{êm¿v&øéÊ’‹‘C=Q@ð!€ÉF#**‡µt¼I—Õ¤^-<ëOZ5Ò”‚hЉ¿D <Ìì­—%ÌDéY—âšb×b¼Ç÷Ã(8 äƒþ&‚@@ œeS†MKL =ã´––¥)þ† ÍZ«]–œpöôSþf£Åß–u/e¸«É$†-´MȽ»i‰rÌ($˜“É@wìF«6'ÑiH,r¹ÄD};ABÁ»ÈòÀÝ’”=‡NhR –žôƒr[ÿ.Ä“vÍj{  ¾¦ƒ¶w o¼Æ·°g†çjįm¥-Ñöä#šgN~Îj§0xš½Ì Þ➥5½:´ÀfmmµÍõg7ôjfëhâzÞ{…÷QáðûпÝ9€º`ƒB¦…CA4hÅ_H"p›U2sÊLñ lHØ®¹ç Ê·²¼¿TíêÑ´%é°Æ¬U/:â\ \¼?ùLÖÈîƒÍRxgÿƒÛ™IÙé§)¢Z‹"×ky ÅÔíLl›R÷ª›©n‹›`D»Î›ÎÓj£ƒÞëâžìɾNºba#M[ÿòÅMçO¼’Ëšü)¥â²8üW¨u>ùq“W4®ËûlM:¢I[Ø7qên¿9NC¨g8±¥½û¨4Ä.Çzà_Ú>ü~½¶aïuòèÞ^·ùõkÅxã7#’é KpŒ eé÷é¸g•ÐÅÔLMZ‘/¤¦CÅ“Û9ß/Œ~.Bè!Ðêœw¿ú LʺµgªY-*ß]´ï½¾}±j }ýëjÓ¬nè!j$!˜÷/:‘ݪÿެ»èjËç%ªYê¹=]Ûó[XÖ˜fÛÿŸš ¯ä9œðo:´õ-o’˜:è쟿z¯‹:QT#í´ßFGœWÃXVzgñ”¸IE¥)éóaRØ€ö¡›zѬ÷¿§1^Í…i×C$6!L:u¥QUa4èiÅ1tx’4=¸¡ÒÔm£Xj²pÒú#íØ6Y[7Õ˜ñ½GNÒÞÃqp®HâB ¨PV.ªãHM™$©ŒÞKý¥=Ù7b:rQçðoH–üÛÏõÀæ¹Ôó®OèÀ–×É‘qÖ[¯õßâ=瓃[ßÖ~Fs$¹鹞Õhx Œf[Жoþšë~Aç\Íh»ýNw†RÓ I²mqü¤7÷CBÝ£Ѳq]º6'6.ÔÃ…”Lí”™‹ó—Òh½¾ ·þ†Î­Ó9äÁv*¼{m*¤*þOκ›S¶ANÕ¢å}ÐÙ“Öd SÖfþ2ÕÇÁYÝXÖ ”[„à%±òžÜ Û„ýG½èز¡f‡òÏEËÉn†íÊÕÚŠo?Nض…Ó³!ì§+× ¶a[’ï~Û©ù±0AeÔ¢axuoÎÌ<­Þ’D|»v1VH|®Ñž]Û=–Þûj-šù_jZ¿Ýг=¹˜¦=ëÙ¾…æû‚UUŸÿ´YÛ=™W/D—!‚@@G VµhÍ6J¿G€@ âøþ5ãõßúl.¾;æ¢B¢ßÚ…­½Ï9¨iŠ»á’ÕÏÖhbÞ.µ0m¢j†¢Wíp^[šJ,9êAõŒ”k¬ïûõ•²ãûgs‘Ä›2u_Rê_¡R€Çûƒ÷̨ñï¹cÇf]‘° nTj&…WÆäy³ÄdþÄǼP±ÉX¬êɆ_ ÞÙ•Ã"oú¹ãñ¦åÛÓwçºæ¥Åüc»K¸ v%žvüÇð»4I‡,ÉW|ÉFXÌ4þñ[4ÿ0Jz`Æ‚UG¼|4«6|ƒ®¾zàÆž”‘•­1)|CA4xžŠi†ÛCúuPˆÀ3ÂZ¹È1CQÝwù’¡ü0¬M_2Ñý üŸáQܶa¯¾ºJJwN>æìúìQGWK´|Æ]ÇxÀPÓx„¬ÒE²È©X´LÂ)C©N)J}:íŠUκZÀ,NÔ$ÚzÊÕòšs®æTËxÈ·8¿Î÷Á)3(l ‹„'%ì1$©ÒÉdØð¶í%ÿ­ký*ýÊÈ•šI¹²:ßÑ”‚cø÷$*³ÔÓ7•ÉX8œ¾ Šoº¼ Šï3öŸ’_Y§ û¾éŹ@@ @Û¹{®VŒò©½#b‹'Â($K¶;q¦¤ÄAÅj=*˜“t¨ú_ ¯ÝdÖ 'Ûõû|\üâ‹l¼8~„mþ+wê#©jÝûÓœuúpô¹r²ÉIˆéá˜ÿàkùßDÕÏIÎÓÛ7d>~UgËW†&¦í9±Šw¸ènHÉöëÔÓîV`z¤½2)´2_T¡¡ðYµBI Â#­aXîuµ¡ˆÀÀÕªñÔ–Ý_’Ûý+ê÷xIëÈv'+ñwGÊ¥©{Ôòɇ½±~`‘,/š˜Ÿ‡}å>]` ?´6Û2óIÚ×IR¨±[¥úšTƒ(S’åaa´ãõI“.2"Á³¶׺]Ù_l˺»÷ÞìkÝõŒ{ µ©ºá:;È(±Ô²8`ËTjh™óî¦tÂÕ^Š ž$ ”É`PƒÂ´ &…Q(Ãð—!}Ê0w‘µ@@ ”SÛï$IÅ:`é–ös{&Žm% þ…37Àîd¾I¯°;1qôÍÛàvѯLm¶H°%çWdÚ·lOaó¾¼Ãr–Zã‘CŽk®ÿÃÑÛâ›p(X®¡ßƒtdžj8”£b¿š5aB–ë+ü(˜” oA@° ÀV&b•û°¹çiØ1q(­ ƒüï˜àuØ OÄÔþªFš§a-ó úãåúìLðBJ]J‡£¨œú—†ÄÊR÷ÒÔQ¤­\ÄÎÙ5þèoÄzþíIcÚ}ÄIQZŒÒ·[]ãv+óP£b]êv'ا»“òÁ)GòJûˆ%1'”ý}¡´i‰~$1PY%’ÏJ²zPU#ª›”—lILJIPiB¶I`OÅ+±¬< 3ûÏqIád—Ã1Ž]aÜ^âz(›Â•sdT±-V‡Ea¹¡g[ºÛ1˜òøé)q!~&Ä—æ¨ëÿóædƒ—Mýƒ±î~B%¢‡±³vSõz¨Kþ…ͯn;k÷}Põ<ðË éµœ*&!ÎÈØY»Nß'úCav'Xóª¥V“ÙyíN ̬ŒäHbV#{þUÊ ˜”JÙl‚è@ øÇ1l$¹.ç3袩.¶¶¥ó8º$¯[ ŠÉ•‡QuRMçiª›}Lsæ·ê÷$zìÖÞšt_@¹â–Õ3',ðe©Éáñ8ÌYyÔ?ê^V˜Š|+ŠŽ2Z íŸkŸŽ÷à 0)ŸžÚš8ÔÿC¯<”q«ÊòÞsþ\¾alã+–ÙËîdBáv'zYâX4^½TÑQE ø“Ïšm{ýI"â–#?ÁIÞŸüL§² ´#²/%D\C§ÍÊ”Aáê1Äåpy\îÉ,#½¹l1=Ì8”uà2Xz´;v¿ýÙj:e7–[ý+ºîe­È¿2  :Ín³f£Áj‘Éü4Xö§;Ìßu•N}â¸;¡ã]yI½8^¿§Ùîä»ÃŸíPTeQ.ÃXø;1HÆC^PžðÆê)ı´TY&%=»ÿ²}¾JóöÊ;sHËÈ¢O°+òÁãgiág«4‡j¬›_ ±‹?_ÿþ —Û㺘7ûûòÐ3&àÑ–½Ðò$°f[2-þbµ6)ð ÞômÉ¿Àcmeb×Z즟70ä|–~¿AÏJË–"ü´i7}yg hKÄJ1úá—QÁùdËån ÑñùÏ[à]x7¸sm~™O²ÝâzsÞ¼ÕÊ»hù/Ûµr·VPý˳î%L$ªôð’â6 ’øV[õþ’™mX¿·u|›sXn;ßé ±ú=>šÉ÷ Ï m7O¶;ùv†´v'+Áœx7Ëìó0\Ã?tëDu€Ç0Ö7qª,“²_‘bÃ¶îØ p9}×X;–€0£ÒªI=mwâÃ+쎽G¨;¼À®Ù¶>[¹YÃþ öáÙ{ø¤·Ÿ8KÇN_Ð6 dGo­šÔÇnÊí4çn¬÷_±~' „—Ù#ˆ·|µ‡¹9|âœÆ¸°‹}ÞíX„²C€'jVñ|þóV:gjH‰ÝI‘gwRʹ|¦ƒéY¾z;íÚÿ§&QaZ8/– 0ƒÂÞ˜¿þ-!(ê_u†"ŸÊ…/)V]î/)Ûùª/å2ɳa ?¡ý›‰‘ú}Y6¼+‘:D¿æc„–g°]ÉÓª Éún†<þNÁœÜ©Çs’Ž¥ºq–:MÛz²éOÄ1ÐTI&…=¼<~†îÔºµmFƒ`¼èx#·»¯ëF×÷lGf“‰vcb»÷ºtulSºm@Úš|¤Ð¯ÝZØ¥Ö‚Õ|d¯²lk°LN͘(:zê"œ°…aWæÃÞ<êÕŒ¡ÇnëKíZ4ô%Cœ^­ólP2 Q”díàÜK—Ó“!GÑÒ›(+ËPÕΠ¤CJ¸låVʲú—eÝK×*"ueEàò’bêÁKŠõz$Žo¿F³Ë]™Ê ý^Ý.mÏ‚¿¼+-°ÝÉlwÇ~æ³GöƒÉ纡û;ùE²¶2A™Zц±zBùX%™” ¨ZE¥(k¸Ö¶1‘¹–‘k÷šÖ«©S á`>ïûÃ!Ò¦¹·çM9ø~ﲪ¨ pKZy#A5b¢iH¿.^'oMë×((™¸ x¢fiV ŒD÷†w¬p JÞj±TaŸ¥#¥¢­Ú²GësLsiçÁý7Ûá _°TZfí ²ú—UÝK‹H_yà%ÅFƒ´D&õ¥œ%ÅÞÊT—b&Â¥kìÌ]3ÛÎÜÓáԶݳñ¹Làµ;QÔ…ÂîDG¥âŽU’I©I¼ÕýH3˜YÑU=ù5CQ|Xhv!Fg‡MÊAH<` #Õ…ää¹K”•íÐvX>5Ž"ÁiK:snto×L›(úwm M[¨™š‚i)ŸÕ:MUõÈên»Ÿ·$k«X*Ê¥(ü™.^eóëöýðÑâÐh.ÚG¯7ç••e§õ»mý]÷¢°Ï+?¬Òi;;!—šF¯UÒ„N¯íÓ1)i|§åp+`ç%Åú3^±ÓÜl?!©ªäž†ááàCã:Ž,Ìî;?(ìNtË÷X%™†øÞë»iF«ãç~Œ¯Wø‡($cn6Öæú²îo‘c° ÐvÖž¦6U•Óö4+ª´°íì=žmå —c[¾iXRì™?²UvÒ¸NS’Çw¼ã¨ïõœix­ »²\{ó·WÒR@1âv!Peý¤tiÓ”:·nªM{œ„ìi b–°äݹEÃÚ4åÙ{4±9窱ƒA¦Q &Vý˜M—ïós6‚íØª‘ö5lÎqÖÅ.òyeKoô{OÞ9@ÏNË]šxð˜æ¨ý sÐ胀-éÐ jÚ Œ® ššÐ_*z½N'ÙÁœð 2vTÌõT݃¹}m¥G@!÷kÏIü9y\‡%mf%¼¤ª®yèï½yIq~¹ó’âØ™ ú’â)zÍßɑϟÂ>;ñ¹Ô:àh`Hø¾…,/ ÒüГˆc Pe%)Œ5ûÎbF£¸Á—AñM“—AÑŸADèeFô{FL::ƒ¢ßDzCÀ3Y»¡ŽK×<É–¥£¶@Ô‚éc·ìšß £¤K’õzs¬êa›(Î7˜ë¨º¢DÁ‹€Ù ¾Œþ=¹×üýÑÉã:.eJÛÏÙýhaç]R\”ÝÉ­•'óú;1f}Ïc×1~Ý­…•%žâÏÐ-7¨r«U-šnèÕ.¨hÄVyðDÏ{Ѱ«ûÊìr¥@5Å«‘˜v®ƒ¿A¯7çaÏΆÁ¬ìR˜¿Ù”{ü@Խ܉–±s®m7{×_î_¦zEÕ»Æt:ˆÍ—¦:ìq,=‘$ãhì<­Û¢Ö‚Ñ—ÏRzEäï¤0»“QcÖ?‚e/ãÃ6SUhúȱëŸ+¨,q?°&x²qì~‹¬È-(`իز®€îÅS–•scsrèeCWú[ž^oÎ#†ÝN¬ðqKÁ¯Ý DÝýÅJÄbÜÒÃ`@ÞL8º{wÛÙ»„ųڠšy* ^dO±IãÚmÄÍ5©ç'T“Õs«U›åêø¤äNß ÕÎþN ³;9nc7URoY0·ï]óg÷Yl4Ëw¨¤þ½ ²ÄýÀ" ˜”Àâ)r "Xå¡«=øX™6AÓ guu?ôûÖ›oyu3-•%”¦î•¥Ž‚ÎÂh33ay§™§",aᓱsïIY%öU2ŒÊζ³îÚû÷Ø4U¦º²i&çd6‡¿öå¹³w5öÍ™íN¾Ÿ)Ït¦°¿“qÈÃëïúþ÷t'CFð¸÷Mœsn–,ÉaÆÑú£y¯ö> ©J5ýZËàÿ´*Ûú‹ÜC]íáÏ$ 0½Ì ”VÝÃéÙOŠ×)O0T®J[÷"²+RS·t®]ÂÈ›±+ñ|Vâ\×föîAèËñ±³â$…þƤ.ßKÙj5üž¼é„Úqã*æØÌ…äÄëÆžïÀü7G{ÜØgh·|ÿFLX7Xu«7Tyû¼9½?‚J‰#y#¾ôÒ¦šiÙ®}¾iþŸ½ïŒ£¸Þ{ýtª–eÉÝ–{ÇÛ`0½“?¡…òl„$NB±‰I ž„ÆlcÜ;îUîE¶du]¿ý¿oOs:õ“îtº;ÍÀxoÛÌ›oV;ß¾÷æüÝzHMJëa+Knc0Øù3 ¿ã)±äLP|𨢡Ê/®G“Q†‰J<µ>œ¶‡Š‘¼.¶`óÍV¢ƒ”úºù™ž2dÎŽsö̺”ÉÊ$½Nÿ?Ó³øx?þ#™Ã?t¶ÔÌgx"å“áÄ;™:}åLò¨éIYãUÔ¦Î\ùá¬Yjù ·ç<ªnŽmGºà'N³ZÞ’Ã' ´%ìÏ–ThΖX\0Ü#ª‰Ãä§''qh| éÓztöG´ ·lyãìÆ/‹½³üÆ`-äo®€â>”möã&…Ùö¸i§”`Òq)§¿Ð)úßïœ9äë$ m%ŸŸ¤ì¸iˆkðì¿`Â=‡ÏOÁ5;f YÈ›…|üz>ÞÿÓ’þ¹ÊÁ&»»ô'R§ñc/Ì:˜y_Φ§È’ûü˜uP.ûž<Ø15cð¬YCÊùo棩3W½_X¶ú×|ê·8ÄN+å3ü~à‘Õ“Øqì§/ΞtWCS qL-G Ý“¼¼±ðB¦½~·?J,?Õ˜®‰Ù ^ 8•·e¾SON²ø È :è“o6S –K8òìE¼n±*ŽJXÈ›D µh æÿ‰û"žP$äGvq8e4Ö0´ÝÀ ¸#ì7&·<;l}$§bÈœí¿d¢ñþ€ç·=±gæ°W!?[[Ù”s¥tçŒ!ŸpL”ilú¹5)ñN*Ù Ää$xIóêx'Vp  šñN{lkF¹·bð_Ÿ°R”Ç÷» N}+é˜9sÃ}NÕ¹ƒM@o¾øì¤¸Ž#œhôÿ:uÆÊwXUÙ—ýܧK‚@0â?Ú-IÁ‹_˜;§ÿ,XÃQa+µáùIƒ´€W­O º)ÓOÙÎcôñ’´dÝ.úñÕ4í ?èï`Y`dȰ*´ø¶4ºã“r:pÖC÷³ÒõýMd5*ôù>ÍZæZüÊuɴ餗þ¶¾áuœ"#QtK¹¶¿™Ûl¡«þ[Âĉè%Ñ”^&âÕ!hÝ1ýjiqdOZÄÝôA)í?뮀²¶¸E`Çô¡+y–Îù§ú9“ž­3ô‰aÙ¾Uõ(5|IŒeºÇM yoÇgЮ Á úðl½b˜Þß î­p•r`7åûSg­ôâ¬sKqŒßÁËÈiGý¹Øþù1Óf¬œËáÝfðîýS]Ó5'ýÝä^È“ŸÿÐ!mÒ­³f±wŒL­†@»ôI93áâ5Ûéå–Ò)‡¶$O¢­¶s)ßÄÏ Ôj­„²QêB'íúÛ{K諵;4ÒÔZõ¶Çr#©A¸o´•6žôÐŽÓº¤·‰~0ÐL/® »ç—Ó¥¼ÏH –—Ö;讑f©‰djN[šsm(2Y[òà¹úû;kà‰.ëk¤+ûšéÖJé²ÿ”Pï =ý¿af*¨ôч»4ãÜ@ôñPŠoòšH·§É åQG`û´aŒ6šÀOøüö·»ë,Å̆½#çîËÂlhØVÅ ÿ üN¾xFùÄëó.fíG€Ìð_Ý!-ÞÉ/Ô üEÜ]s;kÖŽd¾ï|¾þ-*ó=8«èŸçãhç«ê,æW96жFÙg-âóÏd¦f Ÿ;gÒk’ kµ튤àe‡Ù0ï,^³æ-ÛLg ]h£m2µÅ¢s¨scòdM†¿Þ@‹×n×ä“/åÈ=ïÀ2\ýv+»ÒÛ˜ÖE¸‰Ò±L=dÂÜæ-ÝLÙ¼vа~Ý[´^KUÉÓ-D 7CGÐ&ì<ã7aØ=*¥šª5%é…úu¨”wxhPÇÄùÓêßQO{ y:tÙY¸ßE· 5Ók×§ÐÖžt²)ôø×þ0§Ê}TìPi`¦öñ=2IšƒÀÆŸ)˜©p›}~É‹ßzß×¼¿~'_üð§ïä|¼ŽßIÖÙ©Çï„CÙ_d2mùóŸÇ”Ë1ç™ó6ð>2±oÉŸxÊ1â­Ü€}ƒª{”ë]òÀŒÏòJõo–®ú)“ ÷pN¦è#Ðn4)‚ `%Ü÷o¤ } íJò¯f}ØëÖY*t)ô¿…ky­ß’fκ(…~¤È®ÒÖS^ZxÀMo~ç¤ãüÕßÒÔ+M¯i Ê]þQúk.óüžFúÔ$zêb]žk"žéHGJ¼Ô+=qþ´zsûW7$d>kUFu6Ð}MôõA7kUª Éa¾¶Gz5‰ #HBD€cŸä¥5¯4èto‰uvX›ñr-‚¿“±õ­³¨FñÝRévþ&°_ÏóÏøO…”~Óf¬˜‚ÓsæL´S¯ì YÓRÊäåIÖÐäe¦NšVÏ­òPHœÏ½FÀ>(N޼¹lÃ.^ÇÄN{“G´±’ Ë^ë0J*_IK6ì¤Ë' ƒ—¦Q‰cI7Ö+Séh‰ŽòyÛ2ÎLŽ2!©ä¨NÌôÈçvÏé QéL-kAçdd H ¸¨âs•¾7ÈD'¸Î··;é¢*s®Áµ9|O¢$´›±Dºq0ûàŒ²Ðm•i‡f_n#?«O¯ð;£ý]¨ý¢Ýr]樣Žz<®×øO½Ú¬:¨#Ý/®|Ôû>QÝ3g®ì™’2ñ¨æ/bNú59*wL{dÍßç>{î¾úZpÓMŠwêë§«>ïމ2 ÷½8­Tƒ¨ïzy,º$(Mu-d*2fÓ7›÷Ñ”Ñ&•ö˜àïÂq´Œ‰“ø@R’Ïa•õ¡Õ¡É¯ðQvrÍ>ø–}R‘^e³Ç¦SÕƒx¶MGùÑ’®Õ›Ïmñ›tDM—³ödqž›6Wµùµ-ú)“–§Wø¯VkŽ%NûE»å6:`ŽwòûLã×wõ,øò4â\ÙH¼Vxþ™M40ýëŧFám怆Ïñ~`ÍžÚ-yñ¹ _M¾âHAÉê»øÜ+µÏËý¶C áIŠ_‹âáæ\”w4Ÿ*NÊOêÖvˆ7Qs¾±eTæÓÁãg¨_ÏÎLTÒëcGãÓ„øÍ:}åÜ}æýöÒ\_EÙ@Ÿ×5ˆ£8 ½¡ÏV«¹÷æ²dú×kÅÍ*/øâdöé–¢£Î#uÔ›©ì€ŽNùÝ&‚/ é7L“tdã2+ø hÔsŸ(*ÁYµãºSQ`O˜GŠªÍ!UÃäö÷á<"m:á¥k˜¨ ã ÿœIÝ´†ˆE‚yì]Ö.É$hù09yÃJÖ_ÖçwR»ƒÉôK·Ë¹Œ§¿‹©Å™ié/”ßÇÚ’K@Fj_/ö ¤û9ÿN¬Ø¢qq¼Mh’Т¸y¹z&'{s<þ .dmE¬&M6–q×ÁÔ³KG èt:Íô«27&×ðg÷wrë\¹É1—ŽËe»r.«@xŸr¸ÝQѧt¨2O›CÓć8s7‚¢{çT=uKÕQ7üæ³;’pfÅL.hÏJÙ,ôʉ–“”EìpÍso‡déµ=ƒ³tôÎR5GR8‡ÞöaÁE¤¡YZqÌ?F‹çínv¾ª¯•‰™?pÝÛ;ÔŸgï||s*s#ZËqR^æ©×H0saúõ®‚j<â¹íRöè ˆwB<¸æßþrÅ`x誙nCX>' cùÝÒ}ò¹߆Ù'æ<=6aÿW5µøQž‰ãšöðʇùƒuö{ï©#ÅuBª~°/ÊñÚÇä~Û#ÐH ¦»µÁª¨¬R‹$ÛšqPÂíRȆh·…%åšÜ&£‘ ^I"FM>Іäù¼½T7—§îåê| ~"„0סV&s ¤º©æ‹¨îy>mˆŸ„T±ß%…—‹’ÛG©ÓGomuj&uÇËé;vÈøj1•3oLp‚V¥7Ϻï‹ÄÑ$|¶×E?c¡ï2Ó<ÝNÉ}YÎ~(þˆ³® `öù*ÏE{åÌžàÇBþnÏŸ7÷W¼®g9ÞI³üN¦Í\q7°S”o U»åÛ5«~ųqf½øü¤÷ø]©Z êŸìnu;O-þ"ÅÎ}nÒ'lö™¶bíÊŸ²(o@y8Hh’S¾¦]¼þŽÃéd‡Y9s vCM‘:3•ð,$Èm1{µ™>Ц´UjPBjÖ†tå@$<\qâñZã#bÜÛ†geßx”WÀ;ìsÚ{KÏœ±ŸÚ[>6G¹u\ŸÌÞß~ICwFý84ïݘJ0—µ¨ëK?⩹/r´Ù†Î×wO¬cŸcúóJ»æ,û!“Ñrv &(’žõd {æ×ýâõ6Jù¢‹üN®Ò_«^×Ô–øªËâ·ÍÒcí’ŽV|ÊL^kçWÌ\õ›gž™8oêŒU¿S½¾ÀÔb^?Ýëó|ùè£+þûÌ3çù½¾£ÛlY[ Hp’¢rc?Iq:]äæ(³^%²Ñ0[€y“·xy1—Ë©‘ÈïÃhЊ)0SÆ“NGm¶¬~¿[ñ5¹ ;Äôâj¡ ±Õ« Lü†i"•°F%¿vò°åkóå t ã°¡‡—ñwC§¤rÎ䜓úà£7˜•4þ; Ó¾©4ãËÄ —ð4cäÆ/{K‹.ÞØeMžó¶òóÞ¤ò‚VC †ß q¼“êׇ¯9~'ªNå•©UޱÿR½Ê53ñ"L#æWÒ§ÎXñéÔß2qÉÆ±¹³Ï[ú—çÎÝúÐ#«¯~æ™ ’ ´ZG¾à„%)ðGñû¤x9>û‰³ãlkö‘ìößÐä†&H´%“O}3eàGé»ùåâM1€ÈF=Y»ñ3íˆ8ÜXÄ6„É;ü0¡<“¶N`G´<^­ôlc·ó¹jÌ&.lÉi`†ld“™žª;[RV´î1¨vÎõû! ù›[·¸Ï¿åˆ¬ˆÓs¹±ž ãÞ"•ÞÙî¢;'¨”£fÎXÇ1åûü9Ã¥_ú`6ËÖ,¿“iÓW<…öüeö¤_ò󬽕Œ>ãVþìÔÂáÃ1¶°¤˜ ÈÊs^xvÒR¾tÒ´é«®ðyÕ?pôÚž¬ç L-~áÙ ›P–Lñƒ@Â’t0÷` ÇZ=!|õÇLÏ ‚ÙÑŽ¦R@7äm /oí7U|myô&ʫ҆Ääè‡Z$›ÅÄ«P7­ ×·åÖìsÅTÓ,Ü–¦d«ïZ3“”ëÉÄ2ytZì™ÏŽ•ÑcÍt÷X¥Ç¾4Ö¡m3ù„߉êmžß‰8ÉœúL…«ìc^èïY³Þ1kVoÇìÙãŽò”aV2fÇØ"vŒý…Ç£Îá{¦à¾¹s&.äÍB>~=dëß¹óF| 5ý"ÅÍ2Å KR„öÖÄIáyòAšÅ˜êƒz…a=&7äm)âÉqÁqCDಚÚz‹kô ˜)£Ú‹É]Qr&ßeý7é»tË£1iÚFË…“©IfwíàìnÕ$Ãm+ä3“§;kK…„[\à~«QÏ'ì1Ý~´ÝÄm·û’5ýZ%›Øþ±ÑNÿÛ­Òç$Ñí#L”jn;߬˜òGH4æwÂêͧsîìÆâˆJž~zx/øwYAIÑ? JN,™úø¦ëaæQe«Ý]†µ}– ÇØ©3Wßðâó>÷â¸ø-·ñ‰@Â’t‡Ü1àCýxIp¼ü÷¦J*æÙ>§Ý^:Á+5Ûù ³¥©öL1K[Ì”ÁôÒÙo­¦'^ùìÿÊõÀ¹1â~.)4 =s2hõÎ#”éÎ×V¡n)Ž­}ä›Îɰifªú´"¡Ê€{Eî`3R^¡=¦Û/Ú>¹o*ùŠôtÜíׄ!6Íߨùõïtç‹$+¡>mt]H~'Tp<ˆý!KˆiÄ|ñ¬Mù•ê´¯~ðÑW{9ž JŠFRPcùcô&4ŸU]rùòÂØE ¡I `•(,B§×©äˆ€ñQ>7ìv‘>ÉE:6UèŒÜUÕÖ‹:O”ІøÉÇ Ñâ‡`ê®?§ñʵí1‰AºSz2™MFÊv‹i’ùà?“™jåî®&Íí;Ñn”™a©V#?›JL·_´ýÂ>Étc§ÚRb£ÿì1С*7GIVšûDÿú–ú„*éÜÙ“þ4múÊ^^ÈžðßàoOÍ/÷W9ÆþH”Pь랤„Ó Jµø¶4ºã“rÚqÚ£q„‡&XéÞªuK6V­gîðËó­t3O?Õó×ë—¼¶Ëc_U²jZ¡E|ÿM”Òþ³á+$‚µ!=ÀL‘ mH8íM´{5wž}2wË g^>¥y (—E€\¬IéÕ‰×Âá(Ãn5¢¡ö¸÷¢ QV‡ë÷–Åfûk·ÝÀr_ÚÇDß‘LKŽ*ô÷N:Põ7$ÉJ¨OBô®«ò;yŽýN®­Q«Òø:;5® qgîœIïL›±êˆOUßWÈw0ø6éŒFbüNX’ ÓÎ}£­"‚[øó—%QÏzV·Åâr· ³ÐSßVj‹Ë=yQÝ6ÌL¯òš&îrÐŒs­ô_”7ë‰Ig‚s{?3õëšDý;'SŸ,+Ae/Só€Ë î™E;ž¥~öm´)yrL-0©S½Ô¯r›6«'·GrÅl¤*¹[#'¸Ý(ËÀs¾»¤µuxPϦ”Øicm×sÿ]ÝÏHW÷7Óû\ôò‡$+ÍûhÕ«-:»~ÁŸuÏ×ï„ÙuÈ~'Írî쉫¦?¶þ|ÕËqdJhÚlêi®µ'…IÖeye³FD*O¶@„Ñû¿¨ã’\máÅÖþ»Í©Å’ølŸ›—°÷ŠW6;i “˜~F¼9)›Ã‹ÿ€WÙÒËHƒ8€X{5×4³Ú× { ÒV‹™FõÊ ›·”Ur”íJƒ*7‘ÍWJý;ò,$^oÈd2i2CvÑŽPÅ׋v£,›ºL&u³ù´zP_¬¤PÚmåÕýLôé-©ôÜe6êÓ¡úïIhV.z³”þºŽ—AàÁ2µ.ð;`ZÞù"Ûìñìí7ƒß¯â ñN^K¢¤þW?â{êªF WB„¿—š“pQŒýûš¤„.‡6‡ÉÎ3~3 VàΡÀõLå¡Ày‘Žñ”߮쌊tŠWë-v¨403a•V¢ÙáláÑœÃ)«Æ½X ÑÀ=i³ÙDݳҨ›S:ºÓŠõï)|3\ ›¹ƒú!GG÷ mAÄìtûΘ4Y!3dGš›j·Û_¦™2’ ”eöhõµuû[Òö`²ò|#dåŵõ‘-ƆxΚ ©¼¾ *¿“-Í_õ7’SœÅ:;c®ú…ïnÿB€2‰@øÈ‘³ ±’+ !ÒhS)ÅDt¸¸úºJ&2©¼ÀH‡yÙéÕûâ¸ÜjTWˆÇë±;Ün4aöÀ€o1›ÉbµPÿ.éÚºHTt‚F•—Ñ>ëð6ñQL/Рä$©Ô=ƒ×yeù #d…ÌІ„cî n7ʶÛ-Ô)ÉͳݰðPÛµ?ܶãÁ¸Š5+Wr^Àf —j™^Ú`§7¶:èŽáºã9\n•gø«Aë|¢L¥¾ªIHwv® &8(gͱà÷JÃeµã3HËöÊŠ‚2»S©d2‘Ä~$á$ÿ`­#¬&má²’m6Â:N.^» G!¶ÊèX±“t®|ʨä%Üån2“Sgáuž"÷çpïˆøÊêq­•°à¤\”eÕ1)±hr¥¤$“-Ù¦ý†¬Ù?Ã'´ç0§ÆÚèËþ(Æv²:y1D7ÏbÂÒÔíFÛPcš•}ú\EÉéueßY—<ìÈûfö±7Ÿ@  ÖH/Ýí=>Úh6_ÊGzò²Ñy¾Gª ?q¨÷2ò©'yùŒ#äËמûÃF._Uâï(ìJàw²àЇ÷VºK~Ï…Õ\gGQ^_Z>m`…/Íýà¿Nœ%¿ÃFMi"÷Ž´dm\Þ!6ÑtLâĤóK ö9é_×¥ÐäžþÙWðÔÉ78ð”H0½»]þ <êÙŠ+¶¾£ûöléÚ»ÏÍÛö§ñCsë¹¼y‡`.ã,ü2 ¥p3A ^nD “ÝAe/•³¶Úåãól½Þ\§ëÚÒa*0Æü±%ýš7˜b 1qJKK¡ÔTÎ *,#d…Ì-1õˆú›j7Îëõ…—WÛîà­ä`½ÏÇ+©.òøÊ9Æ&¸(®EÛh·“ÌÓÿUÒ™u¶´Iï¿gÀ³ßû‹É曳íÿ†µ¨auoÄDá…ßKê9fèýf³ñ®3‡O¨&¥ÂgÕ•2íuVÍÔ-#ä# ¼v%Uuél:Aÿ»{õ»Sþ/»ŽümþüW+¹ AXB.3øÂ@¼µ:IÕyøró"C²ô´îxãÑÙVóptX½x¥ ü‰¼ä ‹Þd{8R›yoeWAÛ:h6ÐÌX8LP’wÛúÕûÇ]|yÉúy©LRÂ)Zø9¶‚FPñðäl2`ö‹ƒRÝmaGh"¥2 .AŒœÏb†ùÉJР€ ¤§¥i¿A\0#§¥ZÑ©¡¶Ûát0qó‰âLág>’í†,mÒv®W•‡Þܤ®(ÉòVê’ýï;UMå(Ô¿vV*xv{$È ˆ<å·<0ó²”´Ô—˜œtÎ2îW»¾£lÃŤ8ª§#ñ…HÙq©Ê÷  £žÙ§©ïS9ÃzNûqŸ_üì­9ÆÚ5ø{ Íf]%ô;‰@ÏÈ""Š€$) À‰iŒomuÒO9pÛºãÕ1N°NΠ¿Õýøza§?òÚ(üêŽ8‹û¿ÊsÑÞBIR€‡ñ"Õ oÁ=Göïû’ƒÝ´ïÈ)ê×#‡…—1«&F׈Y@IDATRm~ ¾òá󳊙Uí¬MÑ|6xÀöúü&±rvKãî`€FÂlÈ ×Á?†µ:\Ÿ_‹Â>(lâE3÷´(-s˜­R[µr´uÛ!Ã~~~œG·*½NüûÛäý¦nï#E7çXUTƒ¬ôÊ Í\§ ²€÷¨éÇ3›f±Ú~Ÿ¢ÏW‡›?£Ž†Ãal.·ÑÄ䇺¿CV Œ=i«ãÚNjR§yw>úÄ/_æ/ðÍø{jR«¿“Jwéo¯ë éÄ0\•‰x'Òï¤Ñ®'[ IRöåõzïÆTÀqJö„  ñð«Y¤I ßÓ@÷ÌžT ÎÊmB“’‚*Öèp­übþ¢}ú^ööÂ5©Ýu­ÎÄ ä…›0`ƒ ‰¯{8§bj²™}@LRp,­Ò¦h SF`Í'Q4#0áøµ(þ™F0íÀÜ“”dÕ2Ì<8Y#•ÚªÝÁ8·EÛ]¼æ??>žÖS´üãÞ>{ö¥i=z¼™óó¯RÆÇÙ7DÃ¸Š¬¸}~ÍJˆdELÁ5ÿ¿y Êïºw¨çX>Ò”èÏraRD“mÿÐoqÜ £!Oßùȯ”ןýˆ „©—¨Ôð;Q©£0qâz~n_7™¬¿¼ä¡ vVÚχd’DIRÁÓ¯úoI#W4~ S˜/{ ëôÉÔx ‚çgeeYù¦•ËÞÔ]pÉo~ºB½ç† Âþ*d$@ûÍZüII²Z5‚¢9ÕºØ'Î¥¼rv°éC˜ˆšhKàt•%@ˆžÑ_aÊA˜f m œdANüÜüÈ©ívCn!¾¨»-ÚŽçæÄ™bÚ³eã3gÏžÆ @®’#GÜ%~w–ª¾ý¿ÙÛodmʯë%+ÏmkÐéç4¢Y‹A±|ﮟ]™”œú[”1Öw#×q\xsÈ‘_†›ATþô£û§ïyçosp9 *5Ô¹ð;Ypðƒ9LLüd­º²€ß ‘üÀª†Eþj š¤àY¡rgõ>ϲˆõd`üÕ]-{›¾÷¢H ^ Ð¢À™3/ì›W|³9#«Ó<þ}}Dêíמ§„«Q¦bZ/4 bz2H4(.v"õ°ok¡IÁ Ú"r1ËÑìèCÖ›C›€ú MÓ,ˆ‰QÓ®øgòDRƒ,l´ÛºÛªíР€ lØuP9~pÿ+ß.˜¿‚ÅÁs…ç Ï™o–ÂÌD +ï×OVè ·Ï7m@ýdE˜xÌ=zôïÕ¹óœT6ñ@ƒÂeÆDbY”Ro–ª¤g¾4|øÄ[·®Â×%¾`¿“ Í L;e¼“˜è>)D KRð‚¬ÌÛB½%®$EhRà© –†À°dÞû óO3yÊ-Ï¿µ0mhßnêØ!¹Ê°¾]Ê£‚Aƒ&Q˜a Ù€£¬0ñkPÂ%)¨KdÔ'iűhvb4Úö ±íl¶#ž¦«ól0uûþcŠÏã)ݺnÕËë—.^ÎõãYBÖ¤H äCd…ûù½³·ßĬ4`b]Z å'4²òçÍs+÷,zéè«¿À³jKMO»»Ï≆“¬&h3þ dËOêýó—Ö•çdTü‚ýÁ3ùñIú$ä6fHx’"^ŽØbá¼B»ñ3ßÔ-&;ÑH¡”Íɰµ˜4òB ¿ø¤àËö~|±j¯ÔïV»iï–Mû&\~Õ¯×3™£TîS5ÅjVÓÓlŠÅò—o 'Õ&$µ÷›[6ž½àT{?ø\[þ®ÝÎÚû-‘­v[kï·¤Lq–N@dbþcYö!*=²ÏâU >ÿ´¢¢´€¯ƒ‰Naxžð\ÁÖëWñúËó.—W—¬¨¾UõýÊÒë¼{M÷Õœ¼O·)zCGL3ŽÕIJ)§Ý}³TSçg<ž¤ç¿*´‰Ê7¤7LG¼?<±Ú)W{G ¡I ^ˆZfc+¾àR­l÷ç© ÙîcZôXì|Èfä¯úÌT+K]ýõ‹²FX& Â/ÄEŒî±Û+ܬUùœ/ê?ld¯žý´&'w0[­i½¡¦}Œ/’)`2áñ¸.»£Ä^Y^pdßží»·lÜË-!“, Âàã7L=ÂÏY“©~²¢a5ÿïM&“mZRßq.…JTÄAi²À6º€eãw JÞ¾°ïy tHuvõ¾ï·~µ‘`²Z‰@ˆ$,I ~}Àa³ °Í±éÈ[–OXà¬ÄÐ1D˜¢sdÊ`MJ/^¥WÈ‹pÑ–èHѦµkSÄ‹?˜¼àkØÊÙ¼wÛæœ÷ðo¡À÷ð!™A2ð¬ _&< #¼ JÈZ¾¶N &+ýŸ\w kRžP½îAªÛɶH7»=Wb•´$uð}ê;n:-ÿÏ…tákÈ’œ]§˜¥¯M É·-¥Õï]KågÁÁš— ›™e¬T;¸ Š•çæ­±=ÿõº28™$q@Â’ í‰ß1Ñ?õŽŠ]Rü¡ë±ú즔É üø‘èi,[™0«'·Sªæ#!dG;ÚQÂÀG$ HbƒF8Õ„çW˜„$Aa0ÚIÏ L8x^ðl€¤€”€¨ 4<2žœÇsÔâTEV>HKK[`ûá“ÿOo¶M7'¹s-ú–÷ŠÎHý& È ÖIqrLm9›²¼üYî 3<üS¾’Š ÙÚ¬ŸBcv«ÅQAL3Îv£ O>éù•Ñ%ÉÃ^hŽWÄMf!'ä…ÜÂÜÓÏF¬T‰) :Âÿ¤9ø…Ë»-4p³Lq€X±φÈâXk4&øyĸEõXSºPñÉuuîí”{ %¥õ #[ߪqÎQv‚,)]kkÁN°ü-¸]Þ"ˆ>퀤è´h¢X ¹ik³¸úÜ¿. |í¬N;¹y“ unö‰wê,äU"Âñ#’¬ ~‚“npQ–UǤĢɦ­‚Ë+âBNÈ‹(¨˜‘²%S pˆ®€F‚$Hü-ˆˆH­IJDÁ[ñÜ…õ¼9ÊO‘Ù–\®ö»×ˆ{èÌ¡¥d/=Rãœ%%‡ ®¬q¬;BöÜ*o‘´ ‘ÛFöj…©޳ðG†ÂÍaÏE¸sÿ xÍ–ÞJ²ó·ºÝ§'Ê‹ÌùÊÙfÖ»H“ÑÏ1TÍNaà%C’*Y9öÌ9И€”¤¥¥Pj*ç”dMNÈ ¹Û±©§©þöàÔ”<ò|»C å/‡Šâí^ñÊýLRÞåFú?ˆj·Ö””I{^@>ùqíSr_"Ð.h7$¥š˜ü$…U)  )IV«FP4§Zû¢À©¶ÊoEhQ„‰¨9O…ߥš!Ô=VÛ…)õ‚¨@›'Y7¿Eh`šSŸ¼V" ˆNç-&䯒«²–¿y~c—Ès„F Ýô"~1¥š 1=„ÁÍf;Ïúµ(^ö]a- þcv"ˆJKžÔ©eÖC“‚:¡MÓ,Hˆ‰QÓ®øgòH JKP–÷H$‰@""ЮHŠè@-"-›}4rÂæ8Ö çZ1(\’‚zýuúý`@Tx%R¬h¿™¼™„Œr+H$öŽ@»$)èt¡U9€ Z 8Ê ?”` J$HJ@£¢‘#ÿlq¼½?ˆ²ý‰€D@" ¨@»%)A@V‚ɉØ×N„ñêIÔ'öåV" 4†€š2êÖï\ÛØ½òœD ÞSHjõ H‹È~s ›g`¢iaeaLXjU-w%G`ÔÕ¯Pÿ‰ZÙ±Çdºø§[iÐäߎÿHË>‡.¿ÿõ;U;Ü}È-4é–Eü»úÃ'øzù["hH’’h=*Û#Ä$ÇPf÷óèÐæhòåŽþ9såß Ó‘ëKŠÎHÃ.~–<®òÀéc»Þ'£%•º ü~à˜ü!Hd$IIäÞ•m“HbÜ1Ðq&.ûYM¦äÌ´~ÞÈ^v¬^ûŽ{J v’£üdà<Íá-¯RŸ1~ÍJà„ü!HP$IIÐŽ•Í’Hb ”Žƒ©¬`G@¨­‹¤’ü­I픚5„º¹•¾ýžtzsÓ%g¶‘-#·Îñɉ@‚ IJ‚t¤l†D@"ÐêÀs58‡\¡ÎÀA“s¨¢ø`“÷ÀQvØÅÏÓž•O‚¹iî'AÑ$+µ2^t°g“e.PjÈ6È$ˆ $I‰‹n’BJ$mŒ@ÝãõØÝª©Îñúä´ò"Hö²õ®q,wÌýˆ‘@Åù›YcÒ‡5&ðÑšIf['í:Gy>oU²¤t©q_C;nÕ¬rì§ÊZçC’»Ö=rW"uÚýä¨#.+”H·ÃYäPÓ0Ø79ÕÆQbsÈ^zDûÝÐ?Y½¦PJÇAtþmK—ô:çn^ 9ƒ`"2'ƒ¬(ä¬88ߨ‡šªº]¿#LcÊsD@’”ì)’D@"³€”hÙ^YQàRs—j!“âhT`¯»’œ¬±¥çRщu^»þ“ÛX‘Rýjžô£ÏéÄÞOèÀúµûli½µmEñ¡FËÁIÈæRmŠË^F#doò>yD VæžXé )‡D@"ëˆA[ßÑ}{¶ð%ß3 $¹1S'5kh“×z]äq–²ªòŠìnù<~"”–5Œ*ŠòûÙ ã±¼ýëùº`ù»Mž“Ä ’¤ÄLWHA$F x€÷²œÞmëWï÷º%G=#p®É´ýê:èF2Ù²j\»ê+i×òßÖ8¼³üÍÉt`ƒ_‹ÿ”ž#Jû×Í ¾¤ÁßÍçqmXþõ.¾r·£Áûä ‰@¬ IJ¬ô„”C" ˆu›^#(¼E6ÏѼƒ_žv÷U <=›”½äÔ6ÛÌ£îƒ~Ôäµ ]Ó÷j*?»—N²ù§©™ ÛÉ£Gq1ä²£2IâIR⢛¤‰@# 4è1à»W~1kSŠ·:®õyÔºñNj˼cÉ/Z‘ÚçBÙ?±çcÚÀ>+M%È™|gÑ·ŸÎ›Ç×C^ATD[š*Fž—´9ÕÞYm.Jlpødí8pœÎ–TPq9;»¹Üa דÉHéÉI”™f£!}ºQΙa—+ H¢Š4‚ 8ù·³²²¬|ûº Ž˜8éîÍöhlÒ»Q¨¡Ê¶8nPK}hÛºå/—•#®>äDEjRN9Ú=IÁªÇn·‡–¬ßÅy7•VT"D)*GyTMì§L§é8Lr†TÅIŸ|³™RmItñøAtјAd4¶ûnˆ Ʋ‰@ë" Ì=ìáÅjÏÊêgMÒ ¹æô¡ …z!Å~³zŽå#Å „ÿqÓ’¦@ƒ‚rÌ=D9¼w×Ûë–.^ 9«ä…ÜÐI’ È´ÛÑäÄçóiZ“ÿ.XC%¬5Ñù2ÉèéEŠ·“”¦U·-éb•_^ªþ,•ûòéã%iéº]tÛÕ4íŠ\!¹%ˆÊ{$QCf ò` öþýÏK0ä{t»ìÙ¥§ËøP¹Oí5D)õuô 7¦ëh8ÌÇ¢—àƒ4(wïxï«ß™Z” Î *ò£2IâvIR@N¼^}½nk5¶09±‘Ñ9’·é­Þi ?Š'›tœ}ºb*U÷ÑßÞ[B7\4š.7˜t:é&Ôê +´ AR\^x_z×^“ÿår–wU8°šÏçõn[÷õ?S’2úüÉ·¬ðþ$­“q¿ÚÝð’mØÓd•–‰ÃöŽƒ‚iƘÅ'Y¯ÛQ¶eÕׯoZ± 9ñDš¡I‘$…Á)>hW$EhO<¯FPæ/ÿŽôÞN¤wâ×LôÉH‘bM^ó.úðë äãx—Œ¢©Uiüèç¿~j´×빂ms=TEíº”Æïg iËUAœùÃzþË—ÿðøÆ(´Ñwï½ïe‘1užÇíêE~¬çð‘ÕOåíÿví§ä½[6í›pùUS<ýL>mê›ÊϦjR*T‹R¢ŽÑn¨qP<.gÙ‘Û—¬^øù¢ÊÊòb®¡¤*—òšø¤ÀŸFšz™âvCRAÿɶýGéÓo·jÅàÒ¦½r¤ÉÀ.0ó–n¦ìŒTÖ¯»$*u{E™>}¶¥ÔR>Õ`Ð?äñy:+:EMI²øÒS“‹Ñ}–YWFy$Š8Ün_qi¥ZVéÐñóð§ŸýúO'}>uvWc׿Κu—?òY„åyôÑ+]êW^Ÿ§Œ&ªjóœ>µã¡ï6~°‰«Jãì±Û+ÜKæ½ÿ9ÿ^ÔØÈ^=ûhMNî`¶ZÓ zkÍ%[(¯äpÚ Kí‡ ìݳsÏÖMy\Ú B H È ~ÃÔ#µ( ‚Lñ‡@»!)0ñ€ ”WØé½ÅHa4(±’ ‹Êεo/\K}»e“Õj"½>BN»±ÒÈ–É/OÝ‚ oy`æF£ùI”{n¸ %/õ˜íg)Xë!"«=/•CÏþä±Y»^yzÖ—\c³ê† Ê$—^Léš AT A &(`×x†EæŸ-JhƒÈ¨;˜¨ nl‘qNjP™â„&)-ŠÛM¶[ï9|ʨã ÄjÒb´ðkl×ÁÔ³KG ÀäÓŽ´)ø25:>-5=ýå.24 J¬ö—”+v`Í›r²°˜N”¼rï½³úý󟳠á9 Tzh)sÍÄS­A Qˆˆ¶B³!L< '‚ àÙF ‡p£$Q§ *‚¬ˆ}qÞµüW"§ˆ?š8¿q±ý$Å˳nÍÔSTÆÑd}¦V ÔÖ¸4¡Õâ¨øÌTXR®Éíõzyx/…VF_…ç/wóð‹.¸ß§Rû è¥‰'Ž{´ EÇsó£ËÇë½>_oe*‹òû.@PTêïo‚0ñÔ!(¢…ø#A𸇀‰Ù6pb…3k¤2Êޱ¨õ ÿÈÑn^ÜV™„Ö¤øƒ¶yÙ…5)N§æ0«"Ü}Ì'•ð,$Èm1{µÈ¸íÀ_—øâ„ŠÜjKI¹ohßnªt’ù‡5¦„ ž£yÇbAgsÆàÝèÞ‚Œ(š ¤Úæp´(þkÊ/êçäV"P„üe­æ˜ äaM{§ÓEnžá£¨c<ñzA.—G“ò£í ´(—|ÿæñÜÞ,žf‰z;€N6±1ðñßPÎmÓ~1–¯kô&A©O üñ‚°ˆ,Ì1álEYض‹—C}ÀÊcíFÿ`ã˜H„¹ÇãÁ€ïŠ«Á^Áb¹…¹'ÁM>ÁZK‡NÙ³ŽÊqPâù”²ÇxŽð<ͦËY$áRGºV (uê$æ!°$0szÄIAëxI‚`Av´#Á“ )˜aÕ =R¬fñ/d’„‹ž#D&ærzq®—¤H‚.Êò~‰@ë °$EhRÀK'…×ÿŠ+½(ë4¹!¿hKë<1Q*žCábÑ ÒÓlqgêÉ/,ÅL’˜´!!œlúÜ}èdC§‰}78NÏžÏÇë ^:AG:¥3Ëç¬Æ{O”xíU)w{@ ¡gÅàŽÚìÇM9Á,s\ÉÝ2€1hàY„Ó¬Y¯è’y-ž˜%)¿úÛ‡4vp/úÞ”Ñ5ZûéòÍäõyég?¸¨ÆñÖÚ9r²ž|õS­xÄÓII²/@?¾z’_§¾zwä Wæ}C/<|+ uÿüARvæ¤É£Ôw{ÜÃóÄùºIPâ¶K¥àíºo©k¸äãm ²'X—ÔnÈ2¾p5¢Â¼,f Jmáƒ÷ïºþ|0˨§{¾uLO¦Ç èýÅki`ï.Z˜øú5°' Y?A©ïúD:Æt_—’¢§ßÌèJÏ¿|’NFø™@_¶ )UDEL¸4v¿ûåZÍ9û¶«'Òw{Ò7wÓð~ÝhÑêí<•ÜDŽÐP¼:o9%'™é&^‹iÝöôÙ·ßÑï~ƒfnùjíšyÛ”–’¤­7µŒËÂ~Š KÂÔL:¤RœLêÕ%‹¾^·ƒ;­‘Ȱ껽tþÈôš,ý9Šñ{üõèõ:*¯tÒëó—Ó~¾§gçŽÔ)Êrñ‚œó¿ÙD›w¡Jž—ÄmÀÁ£·_II¼æ{‹ÖÑæ=‡‰W¥¦‹Æ¦ËÎ*nÍ­ŸüjæIPb³‹¤TÚÔžµOÄû~$4'—_˜Ny‡œA±ZutßhäкƒDCçÊʼ´fc]{yFØpF¢=a Ñ:h_µ\´øÊÕÇ«&¥´ÂÁ±xS‹c“sA˜NÖlË£^2Ž @¦FN?YE‘ì|ül â~MÑ—Ý'zÿ«õT\VA/ÝD†÷­— àzDRÞ°ó —¿Žìs2nXÖd@äâÿ-XE£õ$¬ ¹(¦D$š¼Ã÷.Eý{æhòj'ø”¹q×azôΫèŽk&i÷ÝÍÚ"'¬/…•‡ï¼v2];y$}ôõ:S„8e±›øoH{Ön½õ%©A‰Ýn’’Ij °$­æÿ¶F»›Ü±ZttÞødúú[¿#dï;*ýû?uîmìÜ×ß–ÒÐVêœ w‹æ¥`ù›wgÜ]LT꙼þ‘4Š ÂõŒÒ¡A(šJ½ž Iøë»_Q§)téø†µ_®ÚÊ+ÿ®¡¥ëwÒ°>]Y’¨ÂåöÒ÷/­ùÏôâej§Ý‡N°MoÖò ¢«ÏA}»w \²—eíÖ)ƒÒ’“hPoÿtp8Þ"mßTÓÌdg¦PÿÙ”Ó16í>¸7VL™25§Cö°…ÌÑúûel2’l¬6EÊ%hHsOÝœÝÉÀêp…Žô¿” ‹<ôÚ;g¨cÌ’­™;W\â¥ÊJuÍ1ÑÉ|¬ý%S•.‰¿ÃР %ÛüÓ©¬É¨/ÕžfÞ·{6 ìÕY›‰sï St„EYÓn¹L3÷œ-­ çß\@o/\M?½áÂ@5q5ð;ø‚fçÛ Ã«–¦á“0mÛL»lPnWM ³ÕñÓE”žbc²Ò…ƒ}´÷È)ö9ÍDêP HÌnŠå¤'‹qðˆ?d¥_ÕX͸ÁP÷±Ü)›D ] IJÝœÅd¤¼ÜËÚ“ðc”œ.ðPÇŽ0…Ëñé“R_ÛLÆúÿÄô¼hd0a9vº¸Æíyl‚Ùsø$uÉÊ /Wo£‘{4¹Èd‡TåvÍÒÈCpaFö©/aFüY ‹Ë§Ï²)H¤Q\ç‚ß1Q:A}ºå°Ùg¸&ƒžMQÐö\8z ¦¥×ÇòÖë1É;n ¢è,~9¥%–ûKÊ&$”j]4*ÛŒt±$©¸ÄCÒê¬"Q~œ— JÜ¥b^¬òàñ‚@Ö‚6£}ÙD²ÿèi:y¦Xó)Ù‘ç×^ ,…ðæ§+hÌà\Ö’\BùgKhÑš –~ª°„5"š£îwûŽRv€ 5 ìÕE3Ó`:3LQ»‚b¨ì8pœ*xõð)ì “Ž-(¸ÞÈ=h펴‡¯‡fš–XõI)-õЩ#=I§ZE%÷ÍsŸ“”PŸyD ­#gÈ—”z)=µþ/ÐniðpZšžö¨_ÍßàMíëD°©'.HËšmØÉô@ —fÝ÷ýÀïP~ŒÒ›vsÌ’ßÿkÏò±Ò£Е۴[?_¾…à„{Ó¥ãØYÖ¢9µ~ðÕÑ¿;åd¦Õ)3…º²ÿÈEcÑÅã×¹¦¡І€Œ Þ |_Îe§Û,´,§‡^øï—š¹¼|Â0ºî‚‘ì <–Þú|%½ð¿EÚ9«ÅD÷ýð"Ê ò‡i¨ÎhA™Ë³ó<.ᦸγ?þ÷K×Φ².‰€D eH’Òn§ Ü„)Ä3;Ì:Ã3ùdeiåÚ²j’‡ã ?ÝÿƒzE¾ï‡SÇ1“Y$˜Gþþ«;Å.Ç5I¡?¾‚œ¼¤Ùäÿ3¼æüs´ó×s8d‘àÔŠ\;õ`—à2kŸ¯-Î×>‡×?üß dw¸DC$7¯õì èÇ×LdÇÚ\ˆ|³i7Í[ºQ›Í“œd¡Ÿßx±æ lçéÉ6VR`~,%APΰ¹µ*ùθ÷¯ßþ¹8 ·‰@l# Í= ôÏ©3n~«Ô­K]GÙn©÷p:kQ’9ÞʱSR“R/@íü  (m C0A,pª…Ï ¦Iñ4èýÇòi+Ç]éÖ©C 2‚X+ ,1OP˜@9õ[ó˜ ,jk¬eý‰@èHMJXÙí>úfu]zAí?x:pUÁY7=ø«Ãýàõ»drmÝQI'O5ofO «©qOf‡àäo‰@t0›Œô“ïM¦5[pL”Ýd1™4¿”Ë&4<::’5]Km ÈVf—#tèÔ‰šÞÉM%¯HÚIRé€/—–ÐÌŸçP×ÎF:^5¹‘ËëœJNÖÓ þzùõ3uÎ5u ‚§-¿ù^uïJtÞ•&Ž5RFzd|dšª[ž—Á<9žR}åî[;Òâ ;ã©RV‰€D  IRy0ýøOsüN„\Öà)LaþÃó-¿Ÿ)tÓç‹‹iÑ2;…³ÑøQÉ4„ƒÃ!†‹L‰@5 ”aƒ’˜¤T_'I$ñƒ@B“… åÕ™—>eU‰Ì´âVíb–Ñf5jb͉fìWØq‘£|î¶k9‰W7„%…ºä„ç3Óªí…K¢„@c%J"Èj$V@ aI ÈIídf'?Rª×K©}>föYÆn9ºìÚl:qÆB»ö>V½¼."Ø.[Y¦å®Mtîèd3ÂF /2%op¼›Õ¬MùM¬–E®5’ DKY’D ÖHX’RÐV£ž5)vÎnRÔØÔ@@6ŸÂS:MÉd0*¼ a]0!™*Ú°¹’Ön® ¢âÀ”Jö•qчŸ¥y Šhø +gÂ2°Ÿ5æf[Ô×òXÓ \¾ª†7¾éZâ÷ IPâ·ï¤äPHx’RmîQ¨ƒÍHy…LRôgIñd‡‚OÔ¯lH9¶€© û™ºò’t-ïÙï µËië.;¹Ýþ Ó¥7o¯Ôr*Çw?ÊFcG&SvVl’1´I¦ÐÀ"ï,\CˆGrñø!Úú:e¼Šò+¶ÒØ¡¹´ˆÃæ#ÈZVF -Y·“NÓù#ÐúéŽkÏ ­’8¼J”8ì4)²D ™$4I RØEG©V#x:¢×Oº%)ÍÄk­d¦²6ÿUùÕ÷뀾BF¹ßUÐÚMtøhµ«´ÌK‹¿)Õrn3cíÊÈaIZ`ºàräïø@‹øýà’1TRn§§^ýŒfϸ…¼ñòM{küŒc¢Ò1=™þþÁRÈf䨱}iÞ²MTPT½O|´4t)%A +y¥D žHX’"w¸¦ à”Ž#~b›ÍÕŽ—’OWL:_zLõdòé ©g[@^È/ÚR[XDÃ4.EËù|n͆rÚÀ¤$E¤¼#NB†I¦£ñ£S¨ooÿмâ¹múñº9çÓ_rå–}¼àß)ê–®­™óý‹Fk«%;œn&,§é‰{®Ó%t¹=ôþâõ±Ý°J' J “·Iâ„%)è hOô9Ñ“µƒº¤é4O ö˜ö’Ñ1:–˜è6•|šL&&R¹R5y…ìhGS fë¯Ì ë®È {ì¬])§<f $·[¥uìÏ‚ÜO?ÚFãx:3~ËÛXÌÕáê­f^ÛîÜ3'Sû…%6…£¿"¥%[µm¢ýSA¹ë–Ž„iÆ2I$‰‡@ÂŽPBû€äÄÄ3MEÓÄë¤tMVé‡úöšv‘Á5$&zÕkÞEª®‚úu´…UörC~Ñ–P…æe(ÇQAÆ, ’µ›ÊèDPXþ³ìx»àë-÷ïcÑˈÁ¶PŠ—×´ûœ¢^u¹ÜîäÙ^Å4¸wgòaNzPÊLKÖ÷Û°ë]0j }Ç!ì-5DP†–%ÑúZ¶G" HX’‚"6v91óר™~³ÙLIvªp9é ùÃÝë]ƒÚL£ ŠWšº¥°9*ÝV%§ŸTA~´£% S’/œ”¢åc'\švæ ‘öpð Ízß\DFkéÕrÉV81²Ы3½:ÿ[:~ú,]}þÊäŠϕ֑l O¿ÙBzŦcx¡C< Jˆ@ÉË$ †@‚“¿¹$ÅÂäÄÂ+µZ9Ûíê”ÄS}}.*d¢¢ê+Hïìuø Àì JN’JÝ3¬š|òBnhRB1÷4õ\vëbâÅ;Ð÷Ø$„YA˜´{Ïtªú ‡®Ã™A;èþ‡–ãõ8_'£îõ¿Ï¹øxSeËó­‡À]×OîåéÈú*Ó_VFjÏГFôïÉϵö>I{åîç’ ÄsïIÙ%á!Ð$f òF£Qüm¶$r²íÞét’Ûã¦,oÉA¼@±Û²™tÞLÒ{³IñòJ¯­GqP0ÍØ«Ï'Ÿ¾ ¬$élõ°%‰l6+¯˜Œm’&/äæžðº¹ún„Ó‡-2l13hû¯œUIQt} Fëy÷wÌX¹€÷_ËLIýlÖ¬!Í[%Q(·A@”Æ Žâ]Oç$A‰§Þ’²J"@; ):2ñ`o±˜™Ø˜ ¸Èåv“ÇãÕ¾8‰ìdÕÙ©È­£2* 7$EegEΊˆT…°q6•3‡•£&JV“‹&[JJ2Ù’mÚoÈ ¹1# d«5â©\zAª–1è•wwSyq O bfçO,¦zªz¯)().|`úÊÿ(Šáµgÿ®5ä‘eFŽé©tÉøÁ‘+° J’¥ @—UJb ÈŒÀM7 F…àÜôº¦8 Â 7¯W1ÈÁ´nê൓¹„ݧ'ê"{ª¨jøÁÏ1TØÅGÉ•¬M星@žÒÒR(5•3ˆ Ë y!w$L=¡@‰x*™9'étÅÆ-ž‚Áo¤¤fÿPQôçcµ#ÿý*¦‘<¨ªî™¬laX_µ™ ÿ{ê©ñ~VJ%òš¨!ЩC ]uÞˆ¨ÕéŠ$A‰4¢²<‰@|" ’Rsãäñzì̉eÝBkÃæ7ù°6…}̈A~~Ãæ }œ&02y<”ÂS9ýáÈëˆÞbq!ê¤Éh4°ß‰… ‰• AAIOKÓ~ƒ¸@ÞÖÔ¢Ôצz½®²7_½ñ>ÿêÓ‰Í^w2p·«¤öª¾G=‡Ýæ–;=ÏM¾r¾¢¨¯wî¤/oº)Vo¬n…ü›H‚›ý"¥’´Ñ )uÚåv8‹ŠK*¢BRP¹ #f6ߨ6?ñðÛîyæ›T0ãÇα'4_&*^Ÿ_Ó‚¸Hª`5Ú^óþ¦ÌÐz|dX³Ãuúµ(ìƒÂ&hP4sO@‹‡ÙæH[\Z¡z=ž3âž¿>wÁaþý[nÿ¬g®¾ˆ‰Ê]¼ïkA8x‹?dx~øíê•'§M_ñ†O§{ã¯ÏOÜ-ʈå-Ë_£o±nýrð;x?ܲ[ûþXÀ~Rs_ɧ3¬Òä„¿ÄA‘ÓŒ[»÷eùØD š$#€–í•ev§RÉN¬Iì{‚r€„ÿ_55™ep0IqÀ¡–IЇµ)^¯/bÚQ4#0áøµ(þÙF0íÀÜ“”dÕ2Ì<8ù¢™Ðè·Óy‚ë­1Z³üØÿyê¬5©J©ï^ô„e1ñtrøøe›vóº7û¨¬ÒÎÏ“V•‰2”Bì‹‘¤ãˆ°ÊYöæqÒç¼` Ð)cÐ…£q¿û h´û7¸]±ˆGY9Û%A î&ù[" `¢AR9Á‹Êx·­_½ÜÅ——¬ß‘—Ê$%jŸ–Ä”^ÍôR5=„;Ïúµ(^m@cc€fÀK½¥ uj™Ýo IAýЦÀi$ ÄĨiWü3y¢­Aíâ¾àfªg—}öÑ6>Ügâ’:Û*³Î£ï½§>¾rÍê+}Ю( ÏÒÌ@Àf¡3|?ž6cÕ!žÎüºÁ¤{ã…§'ªSX+äÓ;òŽÓû_m¤R^AXçË$£§wT¦›Wøòiþò-ôÍÆ=ô£ËÇÑоݵg¡-ú:ñè‘Ý…^zý4úƒ JO+ý1Èb%qˆ@4H `ÁÛG#(¼…±Ùsdÿ¾/Y³pÓ>ùݯGŠ^Âà‗¡FØ/E#'Ú×6Šß¹/t$l[,êAò׉ɽXè3Ë€ºýû~‡Zqm‹+káèƒíû)'O¼ÍEˆ~ò7>„2«f?ãK?{üñµ™.ïm01lé%L÷z1õ™åqú~ËÚ•¥(-íeyŸD ñˆI_åü@P(ĵò‹ù‹zôé{ÙÛ ×¤>v×µ:“1Bªöû„ƒL0ÐjÀO^è"£8üniÄ[‘Q§p¤ÇZZ~¸÷¹Ü^â>`%ˆ¯hù'¤ ÐW¢ßšUEÕ”ä¿ðMyè‘•ç0G¸›Kº•‹ó¯„ÇF.ø">vQa‰óoSg¬x¯¼ôÄÛ¯ýû¦ˆ9ÛŠÚ1'”%ëwÒ—kvq ¾NY¸m–@ÀŠÛŠ}´¶Âüåßià^xè—:{œ®;U½ƒ áÀªKXˤëÛ¡CïéW^÷g¯‡ ™ÃÚÙì¦ò`Õ<îŠ&hPJJËÙd)¾$Mƒ"êjë-´9ª®RóéÓµÏî²V™#?£+.ðà>Ǻ=Ûé¢s»·*mÝ÷²~‰€D yD‹¤Àt€ Ä€ Tp¶p6/ùøý…ü5·óu' ‹Ô]~®.Ú>*\w»JðA‰”£ö¾±ð·`æAŸ oÐGÂÜÃ?#›^xrÌI.ñidv¦Èv¦»Ùv£¦UñW¥7PfÊ^Öð‰§ÑØsl4~T2uíŒ,'hÁ0ƒ>(ˆ{³bó>*ç©ÕFçà6[åº>‰¡QÑ;ûQ™²™–±¯ÌeçÑÌŽ‘&Èq…‡‹ñ¨l]<êë yL" ˆm¢MR„&¥œaa¥7aä1,™÷þÂÂüS§ÆLžrËóo-LÚ·›:vH®2¬oרÅQ‰ín _:ÄAÁ4cÌ⓬Ïã)ݺnÕËë—.^Î¥£?ƒ5)-wÄ Qܹ³'®âK׎}ËcCF\‹5©ÃmF£m,Ý4õIe¥¾YU¦å®LVRhÌGé­_ãà×øýP*8îɆ½Ç´Y<ð‰µ™0ÃhÕÖ<º`T?͉~J‘4sH)øj7r†!^q¾[ýí¦½[6í›pùUS¼^ÏdHSùËRM±šÕô4›b1òb725,?€è¾ÔÆ_Ö Ç)=²ÏâU >ÿ´¢¢´€ ,ጀkèôMDüQ¸œÓÆo;8È7,0øÒ“/»m¶ÍÔ#ËãÆ#âOÇOºé£ÏÏÒ' ‹h(;VB»2¸?Ì$þó­O5v0!;xü Ù9&Š‘gÖÄjÒ³lŽB:IJöëÕ%àÈ J¸Iâ.‚ò~‰€D ˆ&I&hS@L@PÄÛX#0v{…›µ*ŸóñEý‡ìÕ³ÿÀÖääf«5Í 7@ó"Ss`’çñ¸.»£Ä^Y^pdßží»·lÜËE€ÀI¥¸ê7L=Â¥Õµ(\W½iÏÎÅùc/Yп+ëšIÒºå´eG%;ÂúE‚ŸÊwÛ+µœÂ«8?'™Æ¶QV¦^›:Žx(0õàO—âíPo=±p²ì>tŠzuÍÒ–hÀL³È‘/ã&ñˆ…¾–2H$-C š$kSA &/ø’ÇüCóÞm›wrÞÿAh„~_ÜÇdjA2€7²ð¾ #¼ ÈJ›iQ¸îSÿ\ !ÿð:mÚZIk™°:Š&øS¯óòÕ·%ZîÑMOç 1Qn¯6«§¤ÛLM¶Z#î‹•­G…e,*«Ð ZÌþ5£ÄL³pä„©ñ~ ³œÚ;á`)ï•HÚh“´ƒ&œ3‘0˜Š}Œ>ð‰IS-FÈ'LB’ 0ÍH[˜p€¹˜YR¢RY•=Σ/b.YÌ:š86Y˧ Ü´vC­û®‚°Z®H;hß^Π‚ºwvP¹ÞɳãAùf¦ÒJ‡F&<ÚR ‚_Š–µl‹˜?($Ë=TpnÏx´ Ey—D@"ÐÖ´Iƒ'Åàß(1hj³~x«9Õò¶6I‘d…Ai$‰QN`+H L9‚B›‚}~(â^>›©SG#]{E:]sy:íÜk§µ›Êiû®JÖ ÆPÞ¶«”’;»xM¤8 Æë¹ƒ–c€/ r8&Q4)ˆ¤ŒÙNn·µJøSŠñÔ xÄx‹¥x‰@#´I8 1x¤ -4¡A &(ÂwäD!„\EÎÁD8ƒ˜`‹Œs1©Aa¹Lðå2ÀªåŠ /­\w–V¬uÑ¡#ˆŒe ÐüøH„fšH$aîñîâvŽG$0•eH$ÑG ­HŠh)ÞÈ‚¨àkƒ§0ñ€œ‚"}RŒf$1B |QdEì‹óÍ(:ö.µZ:o|2¢Ð®=DËWUÒaxÛÄIbÝ +p‰ÈiRPÊÅŠÞâˆH"G<´YÊ(Ô@[“H…÷'M ˜ÂwB8Ë í‰Ð ˆ-_*S#ˆ1 [‘¯ÈâX#EÄ×)1¸gu4ÐyãlT¹Ù@p6>÷fцH-ÊB¹Ðª`?nR+à7m—‚J$5ˆ’"'R$APü{ÒÔ#pu<* lC½7.¯ Ìñ4 3ÒZçTiP" ¼Ä#’hʲ$¶@ –HJíö·‹µv£å~ü `2)ì”ÌãGöHKŠõ9 ‰G¤q•åIÚ;±LRÚ{ßÈö‡ˆ@¤L?½-‹NñЧ_i5ìk¥;nΤ <ÝùÃÏüÇ,]siºñAóŽ8hþ‚b-~ËÿÝ•My‡´p âãµ<…Ûžpï’‡‚f[Ýx]êÛÛ¢¦;|ÔEŸ.*¢ý+xˆöÈ­D@"‡Ôø“\J,B Ø´ÑkOn3õïc¥¥+„—è’É©t×­‰ýNk$3ÇlÉÌ0Ð?Þ̧?Í9ÎŽ©DW1iA9¹è¼^Å·ùVÁòר°…;Áåµ&eå^:rÜEOýåýþ¹ãää©Î×^–¡IKx´Fy›D@"ÐÆ4ÿmÚÆËê%­À%¦i1WÊ+ü¬$'ÛD}å4-ª‡zKJ=ýAã E9uÚC矛Ò"FµÌPñ°;|šæ AöÎ{4ÂbKò¿V ¨‚/+“HHsO ù£=#Ð5ÇDÛv"Æ?ýç}¬½ÈsàõO(ëÇaûAx:zÂIÝ:#ÌO|§æàߜܞêÙÍDcy•êÏØÜ#R¢à!Ú#·‰@tš”èâ-k‹AŒF¥§éY3‚Øvu/]õA>D@IDAT÷ Íò0^‘ùÓEÕ>(¬]Éb?xNÍÅ#³ƒ‘îùéªKÒéð1d¿‘ѹ•H¢€$)ÑÇ\Öc¤§ùÿ ŠØ\jÊeÏ­7dÒ?,¤ÃA‹±9(#-¾”ÍÅãä)=<ë¨æ“ü¦ÿ,‡0Û)ðð·Dþ+´’¤´ê²Î˜B ¤±‰ÒSC#];éÞÛ;Ñ|ž´‘gþ§´T=-||.^~7Ñ®BößY·©LsîZeòJ’¤DsYcŒ!€X'XQ¹SVÓ$¥c¦‘~~g6í=à¤#lÚèͳ‚“ªœE³øüé‚Ð521…&Nsð€ONŸ^^ÌQG oçOHÑbÇÀ©)ðÐ"ÿ‘HÚ¦ßÊm"–¬T"]Žtó Û´ÃëÈ!VJIÖó:AV- )_û mÞ^I=ºši×þj\q>Þ¶¡â‘ÊX\qqšFFœNíÍsпÿ{š<ë)Qðˆ·þ“òJIR¥'e;ÂB`áÒbzàîúrY ••ùÍ?(ðÙ¿¬Qîâ奄\_êÛÛ¬icþñVýçë»'V…ŠÇÆ­„Œ>Ö ªnQ"áQÝ*ùK" ˆ&ÒÜM´e]1‹"¥nÜZNF'·XÆóÎM¥_SEU¬•76˜ˆ‚ šHxÄ@—H$í©Ii—Ý.]ï||¶¾Ã!ƒÉ'‘’Ä#‘zS¶E"ŸHMJ|ö›”ºŠ¢ðÚ1"×:»Õ²×“¥¹M.a‰»,Ü /–H"†€$)ƒRÔV`@«§CUª}KjŸ™}–Ѩ¯ùgX_{š#o}÷·g<šƒ¼V" ˆ-j¾cK6)D ÅX z"¥:\}‹ jíYF‹‰emå$ñhe€eñ‰@« IJ«À* m ‚MƒŽ5).Îõ‡ºo ùj× Ù|,£Íl ˜ªj_ξÄ#ô佉@, IJ,ô‚”!l2)’]G™Éþ•‰U}xΰa ÖHB¶.’YjáOS×tÕH ž’x4]!çêUŠ#RY L­w–M“WÈŽvD"I<"¢,C" hk"óFlëVÈúÛ5Bsà'(Ž~jâl¤nM›â1í%53AÈdf­OÿÎiš¼ U_´%aEpP”÷J$±€€$)±Ð R†°бiÇÀƒ<ȉÙl"3•ÿßÞyÀGQ¦ü}gfS!•Ž],ˆzžPáÔ,œçyxêywÜýI J²ÁÑlBD’ ×-pvÏ‚'V@±œEì@hÒBúÎÌûÿ=›LØ,›dS6xæóÙ™·<ïû~§¼Ïöœr%C”‘вò}ûö“{Äfjâ JÞãñ”“ÄÄQ^V.ÊËËEË/”S*v”íJ/zy/¡9í[”?õA 4;Á‚Ò³.Nï’$Ú ”OR¦(ßnsOsdŒyÔ¤¸¯°XÙ–¼nÁ!÷]Í|Ƙ@k ÀJJk¸ œ‡ÊJŠw(-—%P2ÐdÉVY)k"•}â´IL„‚R!*ü~¬Îk‹îXý.¦°Ll+-þ¸Ï„f't»³vG!•'’$†æA¡aÆ4Ї:Éz rÔ^=`AiÛ¶Hl“È'å—òM#q¨ͱ1ƒé>¢ûÉ_^^pЕ˜8°’r$\¥c#®^ý°ñËÕÝO9í¶5_m—ž{jÄ¥§¦ê€JýQÈBᇂbÛ6Vç­ì4KþmcÊÄÎGìõï~}w@¶T1hˆ²Ò<ƒ’l@4‘\E@¾½£sœ=ÚÇŠ¶PLÚµk+’’ð#Eù¤üR¾›«©ÇÆ<*IÐ}¤”’Û7¿.lAqoÞ3#€@󼕀‚r[5j¹tÖ~òá×—^3hÿÇë¾I‚’±i¡Úz€JßQ臻ÀVYa£9È Žµe¢‹ßEå¶(òKQ¦ K•"lÄIÕ SJU£)Ñ.Vmãuô“‰ƒB° ‚Ò¾]»À15õÐh¤æ´¢¸™c•$pAGQ{Þ}åùupqï5ï™hÅXIiÅçÉš[iОV ü~øjÃ0)Û­›~Ø.Î8±KÄ(\e$–•X©¡TNj†‘?hR‰E'ÕÒÒ²@_•(*Ç9•–tª ¤Ê,â´BºM54²†ò¡kÔG–¤IÊH  ,)dA 4÷T[Qš¯ÃlhžŽutÿ¬ýj‹ÜU°u1ØÐ½EØý…ââs&ÀZVRZÙ9F³Cí1®‚‚¶a­xõå7O<íŒkŸzýö“†Ý¤Åx"_„*fRh#Å¡ò¼jh2ú”AI)C‡Z?”˲`qqMBMQP‰¥G–j¡|¸£¨i‡úÊ$$Ä~ÔÌCþ”¿hnÇ* ¿-pÿ8Ö³÷Ýžý»÷Xë™4'šže3£€+)GÁE<‹à~ÕRB uä¨())*ýôýwžÐ^›ú÷——«ÿ»åªˆÛb*&F¥’E¥Ò¢#âã J SmEE S­SÕoÅURcLqû»ºiÓÔü4ó-5åÐ<(¤¨5…:É’rBîäO „k‰ÖutóD ÇǺo ví_|úÑœ}û~¤áÇt[S¢…œå2&ÐLXIi&,¦IèËÖUPÊqø}¾ò½5NèôoœÏ õ»›úËH-*T»CzIp‡'“Â@”Š õCV;`IQh %ÅUTSJ3ðÃÈi²¤PšdM¡¹JHA Åİ®TŽä‰¶%¸ Dz ‚òÉúo忝7ü}å¯|XuO‘L÷[R‚o>f­˜@“•|@ùº¶â2rÖZ?·¹‡*‘2üJ«~ño¿øÌ2¨dE¹aÛî½ê7×]¦5´ UÐXIa@¿”€r‚?• JeçZwPS•ä³Ê:B‹ûU®%¤CI"e¥ò ¶€õ„ònsÓ>ZyPjâ! Ê÷×?õÆ¿ž|è~¢ûŠî/²¤°’MÞ”l [›,‡0:4YI‘JÒÜ?­# öbõ &ª<üøQ…RŒ_~4IŠçŸ}{÷öm».pÍ­ýãõ¤sOï¡.î}ªìsz÷ˆæQq­TAS Y5¨£,)&®õÄUNÜ=ÒmðFéÐF{÷Giºi]· næ”R˜Ž4 3¦Q<ÔIÖ±ü>[ùÞ_þûþ;+Žšyè~¢ûŠî/·¹‡¼5‘}œ~ÒD ÔI ÉJ ¤ &:˜:µÃÂÉ“÷Ö™{2ð\%ŵ¤PÅB &0tjkV­\»éŸsÙϯïg[þþ¨ŒÚ¢²UmãcUûv‰2ÎCS¥5l UHBÏ&­24)Á[èy°_k;-èycòZþÐóÆÈtãÐÒ 431MÔ†¼JÛ¶|·aý;.}õ ôiÚƒpdå¥{‰~Á–ºßxkzßÛÅVg9¿o‚ŽÊê%Ðd%Ã,—ZŽ•í”87 µÖ›"`á ¸ýR¨? }ùÒ4°4¤Çú┕•Øï¼ôÌ[ï¼$Þ=½÷ù=Oêuf¯„6IícâÛ†'²©i!·£ƒ€eaÙÒÒÂÒ¢{¿ß´aý¦5Ÿ‹’‘Å„”’ýU¿Bìé~¢ûŠû£Bslô¾G.ÍÀû¿9ä± &PšŸ}µ…ªÇ}¸7k3 Ükó}ÞÁõeo&PRHH9¡¦ž6ø%áG‹ì´ÃÚ¿ñ#? °°`Oqšå>†ÞŽd !ÅÖUn©)‡¬%¤…””}ø‘’âZR( ÷G„¦nÉ^ßkè+vî"_fϦÊâøL .M¶¤„+97ìÌ” ß•yÙÞ÷êJý˜@Ü ‡¾z]Å#¸)ˆÜãñ ôUÁÞµ´¸Ö7¼x;J ¸M5®‚âöe¢{õ¢RBÊ )+lE„æÜè=áMkN¹,‹ „#Ð,/uÓüK\µeš…÷IÏ —ç›)%ác7&º']‹ )# U?²¬%…””`kŠ«¨4˽ Ù¼µ~®…jÂq­(n§kRRèD?R^\ Š«àÀ‰·ÆH6ó”×èÖ¾›ÑãLÓFÖ+Þ˜@Ô4‹%…nÔdoÖpäòUáßõWtb» äø…µËvT v+ ªX‚©²¡J‡”àNµ¡J ++t”nî;Ž/\%…:\ÓýA&)*´§srwû¡¸qáÄ[cPçäoö_·7~׳‚ ¼E@³¾Ð“3}÷*GÍAÿ” Ï `‹JÔ¯ßÑžYTH ¡>(ô£Ñ>îžlò£ÝÇÅHÙp¤¤+*¤Ø’bB{ú‘÷A„¦ndA |€ õk©É±ùYÞ‡›*“ã3H4«’B V**b )k1‡Êî£Éeà0u {ÔUVH! VN\…û¤Ôð(ór-"¤|б«¨¸ÊŠ{îúeÅoùâú 1)÷–šÏ JË_ƒc9ÅfWR&š~~ŽÝ" Q;Í>¯ã÷„– ½Âó¨Ë·Z“ËîZJH!q®›{»û&'ÆZ-WIq­)´w;ÑºŠ‰¦Õ¢µgŒæA©f¬îD3Ï Lÿóò<<ß—ùFkÏ;çïè"µ—:u¦Ýêßz7&z»ß;=p“czO¹CIU€D©coL IдX¿jêüês>8z  9¹ú£éáè-èa(À¶¼MÐIó  u ÞÛs»{º?Ê}PÃá$«‡yFň̩?µû:$r””nx­Ðœ¼1&À˜@+"%å@ÕR'ßÓD ³&ÚŠ²ÇYaL€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ ´(Ù¢©EØÒ™•¾¯«ã¨£=±@JÓ‰ *aL Þó×u‘NË1ì8-nççcOÙ&;1&ÀZV¡¤¼>øÂÎR¨k”§SBË”I!_Òµ„\—V´3ØÿHLómc»µ|˜L4žY8yò^7ŸÉæ¬ã5«ô—B“?.| ã)%^¼µvîõtÚx–äOš´¿µç—ó×ú ‡3‹¯Í4.w”=ÇVö¥”po!¸ÅÁëJ%Ô•¶Sì{u†6?Iu| ÿÄ46ï)ÞgUq¯’â\¤Ú/òµº&žÍ}Ðû|ce6$j„ÑBØŸ +)µ€³mÕW*5Þ¬¤ÔÂ(Ôù§yÊST¸ö^«Äž ¿•ÏÓ¡O\ºÁÿ%œ;žšµvS¯Y«'lÞ ¡ò"9ýÈ#±;‹np”s“ªPrÔp_'ÿˆ¤’*Jü$ÖÞ'òcJäJ¤PRR2³ëøËfÛR¾$ml‘ç²¾òŽ6I*·öo0DÌÏø&®¯/ü±èï^OYæ¼òG¬¤$›y Âúq–Îÿò}™yÇ";.sxZxçèº*¥ä«Ó5ŸãØË¡™”HR$…qïÛ/wÿoé,ÏO#‰fø߯”(ÿ\IÕiÿYj2œoмôÏä ?AÀ’½¾ÿòa+&pö¬/N*:°v”ùxN:DšU„‡Â.ž?sÖš¿_>{s|¤ñÜpe; saÏRnÔ„žEe‡rÄneËÝ0 ÝCṆ“ù¼ÿ—ŸíMihüh…?NŒ.‚ìEVE–¨V»iÏí¨ ßIÊ¿kÞí·BÉ=§Õ‚åŒ-nIQÊÔ^›©ÿ/Ç;]b%N±ëÝצ¿<ÑZ©œñ3g&îßW>_™—ߨ x/Èœú¢-¬S§d?—û`ÆÒ ¿còp„wú™–¨¸ð˜,üVèsç¬9ÛoÛËð’ïÚØ¬#î]{ì}§¢©hкQ½©2®w#Ó~ÅíøÒëËXTá¹z#ÖÀ4•V`ùh O+ÛL3Ð,6¥•e«FvŽ´çöžììÎ¥¥ÎGRÉ9PtODÓdòð hq%åµÎm’‚RuÍp/'¢Éä…×fÆôœV±:’Ky °ü.´ÇÇ´Oòò¢¡þ!Ã3|ÿ´5 ²–ªòª&Õó¹¾L÷å+襼ÕZþ¡.t3×—þï Ó§·Ý{À?MFƒñàÁ×ßRO‚~ïüôôÝ”Ÿá^ßchSÍg MÏ7!ð\¤fNý ,Â3‘$=×îñ¥‹|™·E’ ãnGËs‹òüà–‰ö¸>2Å›‹Cãšó{%¿ÈnFÆŽäÌœ!ù¾ôUtMð~ö“;oLÀ%ТÍ=° ü/»ÝÄ›ºÇ®Rþ%oÿådê·Rï†çå\Äùˆ*Ïp5M¾×fòSR¾o+928\³ò:òðÝ0Îîd\¨ÈŸ6úÚ‚‚K|•”J&î¿ßCMÐÓ=oê×座ZKŠz½YpK[- Å6\„‘¾ž–嬀rù&îÿs„.&!‰©Sr†‡†éœ´Jñ·»’Ú]tß]}N¯ÏBÃѹß_q?Ê­{<1Wá™9€š¶ü"KóÐg³¾g¾êÙ:ÝN ¥C÷·­,Üßb ”©A1Rѳ;7ò7/òe¸Šéú»½YÊ•ø_ ƒäU ‡—ÙiR×n„5&ðLn©™•—sTNÎq¥ÅΛx}=³(Ûû ùñVIଇÖþ ŠbµÒØ\\`Ùòöž»îŸëîé]ë³â¦ÕÕè1©ÀÚ‚n$ê%X¿C§Ùy]õ^6Í¡ö°q¨«ÖÂ諊ó,î³J9©8ϯr ìènoËa¹SèÞ#óK-Û°Z¼[õìPêg!â4CžÍäìì:Ÿyj.΋m9ã0êpk¾Ïû‡ ÷oé8â<E¤Ã#ý¹£X*‡né ’¹Ð:.m<×ÔöaHaycuh1%eŸUðTX=êÊL£ý0 䓼ns.J)(©[†Ü‹*±]ma t$AQ©¸âœ.ÅC‡Ž)¤&+ ˜ˆûHa°‹ì!øbº’âÃÐ/ßRDzæBˆÄËÝ€ÕDhšv:*•!Ÿ­KA¡ˆÉyy¹y÷/é\Ž»‡-üí‘O]• Ê+ò,BÁZ “pÚœqãJQ#ü§LsXÅ·IC® qÿ_*'’ÛXsvÇ"qw”÷&ä=P( (}ÌÔ,ÃqÄR¼tÒhDÇŽ]S/ÁW£BÒ_Þ\½!û­Õ®0Í%+ ¬ §»ÊDo²ÐËxÔ—ù-¼¯PI÷€’d,5KƒÃU;°L½€\Céó†ñ?¦ gF€ñ°~âO}ò«îÁqx>²ìbç¸73¶ú7ƒ{yUŽ>xV:à>{Ñ•¥ôDÜk§ºçuíqOÅ@ÞAËŽT+Ñœò¦¡éwÛŽ½ÏèM¨Ù¶oãù[PEaš5ŸÍüŒŒmu=ó‡æSõ†â_ãþ a‚bàð(ynñyã§“šG?=—‡"×¼È|Ö PÜ[jSŒVJxQtÜQ¸ãæúä£Rþ ëYµ…S¶8féo\³¤ÔÅãèKr'5¯8%65ilÊ5½âK‹—o©Ô´GÝ”“‡5]û¹£Å~ꦡI±Õ=·§—°Ø¼ë%ìqšÒVèÒ˜¦âþà°yYÞÿ@Øöð[ï5ç@Ct©=&ôX· 4!Ü`Ê®6¥– +`®F3ÐónÞ«ö¿‘Æœƒ±j]{~¯UÐKÊË<Ð×vœ[!ðyb+틎’¿Üju9”œ²«ûœ`„c|P:/ê’ qƒä¦9aknPÏC®?„ëÙÉ™Ùïh]SìQqvöœ/.„B{~Ô ã¨»hHs¤òiŽ“|_ÆCº÷S\Û3·99q¥ /k KzNt9/ž_F"{wÇí'¨¿Mà‡DIúuódœ¡t1JÏö¨øfxæÔ‹ÉJoDi†{6ë|æƒÿ½jÍþ–bæ|žg¦.ì±ææ8ÝïQÛp;”X9x¯!‰,¼ü.(”{ @Qß®e³÷Ñ·cuCdQØ*+äˆpñª,5ÏÂïYÜ—PÚ­ñ8¾­)iÖõ̇æx^Õ5¡îtÞ”<„“GnGÊsÈ¿¡N𨽥%ê3XNÇ£©vVmåbw&PQR”(­ü©+'MôC_’zÓ  ˜`vžƒvü%x‘ŽÖ´×쎋´-{ε7ïœ +ÊØÎm«5úºGeþ7ÇV÷à«à§1 +Üî†÷­ö'~K=”êÍÊêbxßÛfͦ@AID»V”k9AY§ãUzf¿ýJ7:þË¥qÊ)¿8ÆV_ÌÏö~väU′ À§wªÅh’ú¥¤AHøÙyg¾Ÿ îÚË ì]Ð,3žiÕa ýŸÂïŒîÍžÜÆˆÏ+e*0"Ö‘M¢û /U‡«ç€fE>¯P~çÙ‘æü‹˜£""[Ø#Û[IÜï¡wIó Š*=Sµ*)Ôɺ²/QFÌ{ùæøÑßè4þ÷V7t&ƒrãž9è_õ?47ÞQ¼§ø<3[ ÷•o‚U££‡&P¼¦¦YÛ3š'²fjRû ¬F)åÅÎÖb«h‡e©ŒæÈChZîù‘öÜ.ÊJMÍàz-¦Qzn9xÏ"%€wGô7Le?Í#ørîÖ¹ý…1¥|qåGsXeem楧ïjJΨ©HlÛÛ5)Qß=+-­¸1²ÀGŽ™:õøºò‚/¶\t í°¨j†Æ¤.5Á¨»“⯘.X}Ri4œí/l7ß—±…š&k ŸHµÅ«Ë=՜ىš˜jK7i†ËÏ蜜D›6…¡96Zyàç6Ü•`·£@‹()¯ÍÔÆ ‰ l߇f*…}ÃŒ®9J7š“Cz?ù³|3ã¿Gi1¹Xè5kÍ:|ÁŸaðFƒubᆴ>è§Å`Làðh¡J]nŽvû9ðm9<£›*šYb„Q_ô’ÿÌèÅ웬 Dù!Mh?Àde%Eà¹å 0&pø´HŸ”é‰zÅ “PÔÓ8— (Ã~;BA¹§«ÑŸF/ðÆ„ÒägQÇ åQùLE'À˜@³h‘æÊí«3ä—Ô±²Ùr"SÚ§Ns0L—7&pô8köê«[¼­’âÅP+:uZÖ¥Q}¬¢•/–˘À±E E,)„=¼ÿ5´R”J½Í¿¢&Ÿ3VFà7cû¼`ßF-[R>Ë JÔè²`&À"$ÐbJJRbÛ\Ì5²/Â|5(:øåW¸§A‘808‚ `²Ã}gD§ÒÑ”%ÙÑÉ1KeLàè$ÐbJÊ#÷ïÅKÕŒ;Ú¶iû@ä²H&Ъ œw⹋ ø7ßM.XŸvÎÚV]xÎ`ÇSRˆæõiö˜¨#ži´Þ+€aÇB×ïªT€ê QšóVø­/p¤áê“ãú7DÍï‚U‡1igí[²™—P»/û þ5TÚÒˆ¹+CfZmŽ2Á*¹ú8Ù.0)ZsÈ‹¦Œ†<3MÍ=sádл¢¹ž5Zø3°–WPBôœÓ‚žANM>¬Zÿ«^9‘”òVß»¨Þ„8¨ƒ@‹uœuó€ÕŠvîßö:Ñösݹw0ÛãŸO°ÿÚø#Í©§ûýö¿0µü«ùYÞÀìŸ&c*/±©_K~í±Ê°//+ãŸä¼EŽâÔ–VCåє☬j».ô÷meÏ»Ȉƒ&µ2/Û;*X§ffÿ3<þ¥»§¿§¹&Í MƒÏ[³f®€™ˆ_Áä&*¦òkç¹rÃè³ ")î˵šÔ‡çe¥à†OõùºÛejm\§v]ÊwîÿË8|´Èçý?ן&6SEÖgp;•ܰLÅjÍÐîÈ53±ÜPX¶£ða»k¿œáʨïŒä¹se¹ûÚâ¤NñýÒvÔXtªÃb‚¢Ì0bn£5tªÊñ&Ô£¥Jà·Ï0ôÑ ÌÉ_¹2ƒ÷x&‡BÎc¼2x­/R¶ÙÙ÷+GÜˆë· /ä÷ò³½9“7.ÀäçAq´‘Î~ÃÓáŽpKBÔÆ)8}:¦5Ë0ÛïBX´wá~‰EZ·aî¯Ýp#2§þï—GDãz\£zËæ^kÌÖ=ï¡§\9¼gÍI ^‹As&F².J)(iot¿•n£;ºâ…Pˆ‡om¨‚‚u$®²,ë9<œËCË…i­§`ª÷WðÐ^†uk.Å °&ÍfÙØpu¥,³¾ti&Z¼T†zÌÓRi%Xe^Ø—cÅÙ )Ρµu‚åÑz*xþ_×ß»óñÑKàË´>ï@ýJØè¹‚P®ˆñˆ~‘*(D•ÓÓBV¯†ë”‹_CÖ‹Õ3¯*q¦Ìï_ ²’@Éy7ÿ!sµÔõÌDúÜg£¶8d!À$”Óãô_á™»#süþŠ¿âvèP¢ í¸ŸOÏ#†üÛ²œqÁrÝãäL_¶£œÛñÞ²5·+' J—nž~—BÎ WA <×XN³K÷Ï÷y¯Â4_XöÞjeÏ•R'7 í«Êò8g¾òú隆4IKoUo¶²îÄÛæ aÙìR1×çj|À¢@ Å•*Cßq[J¯Ÿà ÅCþ;ÜäÛR.<è/ë2ö‚Áiöó ‰Gauݳ^3⮚Ø/›ããEàDëÖàËå5eWÜÜØpu¥,³¾tS½ÙW /_Ñ×­I⮉BÓ€ƒÅGè=Ù;X^¹U1_ÓŒ‰_JÁî||tø"­÷*-Þsî ¬mDj#Û P€Õpb׋ΰöÞ>;"‹UJÆ,†}k¥"]é† ÷6Ü{O»r4]ÐÜ> KG¸Žìç™c …ô ×<±(OÍ­®g&Òç.Xbmq–­Ý4áÖ¸KUä>˜±ÝÉÔ$CkOåúÒÿíÊ‘†\%„sŽ{¼÷ãɼ,ï-p³‚ÝI,Ã<‰C-ž¸€d-­Þð¢vhˆdµCÕA]œ‚Ã.[óÖJR?äù&l"w* Þ+½©‰‰Î«š¸%õ¸%‘” +µÿ÷ÚgÈÓŸ7&-‡EIq 3h¢ýøN'*¤6ß;pÇea6)¶ãÈ7tã¢ë'¨!×¥•5jèe®™¶“~˜­³ÆÃN/Y8t¤U;«S—ò¼zVŸã Òp§¶´*&à;‘·C T J]gˆ˜·\™ôâ@IVçeMúŠTgŸ7ï_Þ}öî ãû ‰½ |ÌB§Úo—NÂ'?e -Þhsâ†ñçÎxg ¬Q†WÓ5PáIµs䔩ýÉ'Ùœv"*ñÓ1éà›nȘ8íC* Ô(Ë6뻓°ÒÑîb§äòd¯ïÏhúJ¡q{vz jú‡{}O¢Ùç~Ⱦ@´Ñÿ,×=ÇÉõs÷ŽtºAy­©PHµ¥L”ö 0Ûœœx׬¥Õ¬Ý8Õû²‘µ¥&zŸ‹-¿gªAñA4ÑÚ™‡}W†ðÑoåìñûÔŽ³4Çé†_r¬4¾¾z|Ù÷PÄiˆèˆÃŽz`A"ÞåÁðµVŒïуÝ" §®ãúä÷óo¼!Ö“”*'Å›Uµåd]!¿‘¾ž~G ëœ4 4,Ÿ[ÖŽéCý èžIë=]§Tö˜âÒVŽ®ËmŒ˜/W9£Y:ÛbïÅXÑø6¤õ¾ò[C¡ü<l°+ =!Þ_I©óßQ¾Å~Ñ´iê{fšóJÏ7Ó7¢©JBIx_(ï¾¹zÃÍøšÑ•e×xWŒ0³Ï³-çÙÆ3°!éKQÞݲ§pœ«”4Jeý¢pÙ+1€¬HûŸHï!ôKL}äÞ†$VsTâ '¼QqnËvä¦u'Ò8äc(\ÙÊ¬Š X–¡ù®<öÁqù˜ 4V¥¤4W¡#/زF¸qÑÔsàÒi/Òp®œúöu˳ï„1µÆ×MJ†ïJÛfB¼ö+úÚ"ù£rrŽÃËî ÈZš’é+¤œ›wî ùçõåý™@c äš“¿CÜï œœÁPô»wÓÒß '«»žÉU—½EÖ áüâV÷3ÓIõ‡5Í¡‹²2žFGõ, 2nž3n\)ÓP^Ë_ñ2:-O7:‰ÂÔµ¡©vs gYP È/Ô„•äçV4-xØ»úÄh™¥àÖ   ;TÆwè+tª) T q’ŒÕ¶n³¶ÝuóíYiiÕ––ZËæ¨[ÐÔ“ì¾gßáJ9CZàÊæ=hN¬¤TÑÄgÀ3–”¤ÓdsÖñxA\¯±/Î3|¿vçIˆ4\•ذ»¦ï²Þég’gmò&LŸÞ/°~]µ^¯¹BR3§þß-JqSpÿ™ùéé»ñÖ6ø‡8?v3úw€Ûn|Þ3èÐ+[åà~}Æ4Ã[JÈ]—ÆX¦áÙBÝÖø­¶g¦>‰Ã½9C¨ù¢¾páü©¯ˆ­üsÐÄ:‹üé}Pb¿ˆ¼Ì î@ëÆESMõ;Ãu Ý/ôMÜ@nîè§Qfú £r³úÏú¥ne9—Âï=:§!ØÃ§dvýjÛ‡£~j×>9sZ ¿ÝÉþš{Ö.œº¾Æ{}auþ#F&pãóž 4'ÃÞ'¥9 ÓY1F»iåVábÌýð‰ð—uš4iD =°E»Kž*Þ[ÖòWE®®¼Ø–šŒ>›f|mòðâú=ü_¡/9W–ãØ3¡Õœ",ÿ*XHÎxÛo…¹ø"7 ï™@KÐâÔ GEß QgEµ0kò§¸¯_FÃ@“¬)µ=3õ–[93[ûC½á‚,JõB_‘ŽBÓU…¼Kö–Þ»J_”¥žEšË¥r3´“â׿3\¯p{Ý#‡Ù~õ¸”ùýΩkÔ—HFÇ',kÏ"È^ ®~»c¤G~þbçç°&åàð$:¯m;$œÔ/k dÀÐê8ièCÉbRl]Ô͸ü×®œºÊ–ofðÊØ.(Þ3ÃA€,UÃñª“4§TŸUD.$Zà´2î’˜`¿pò‚ýù˜ 0šòÌPeŒÊy£Û_«¦¤ÚÏh¢´S§v¨=DxŸpïŒð!+]GšóÛ„ó§wõ ö#·HòT[8šˆ.X3&À˜`‡‘@ª9õä@gÔØNš 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`Ç.,¬Ù:¶ñãWt*sÔ@¬¤zÑ놅Rc„Ò 5)¾–Rû°CÛK?ÄJªÖáÊmŠ7k&]´Hš›Ó|Û(°>¸Q(§]|‚|=xeb7 ï[ºnÛ­åÜ6ž%ù“&ío}9ä1&À˜8ì« ·òjG¨´2[]ƒüèUܱäg忇ޕ#vï_±kô¸ŽÕÄìY³úítCDºO5}—8¶øDQ:Û¤”Ÿ ©¿Ÿ5iM$2¬.„ý ””‘æŒ.[­åtü_!å~Ë/?ÀñŽHdE3Ìhó‘¤rkÿCÄüloâúh¦u¤Ê.Jü$ÖÞ'òe™ó>Ê‘’B÷V¯…𽥫±Jö³Ý´¾KMsàaSœTþœo&À˜@¤›’rOú‡Rk‘£œ›"É,t•„RË™:zÜʉóf÷Í‹$…Iž’}ƒc9/)¡ÍפœŽ4OÂé+ÍGðŽHI M˱ýƒ`†Ú”ïËŒ(ÿ¡ñ£u~œ]T |‹¬6ª Zi4‡\¬Hk*%ßX”í]Ùò¢)# àZê?°òeëšö˜-œŸ*[=^à, éz£™6ËfL€ Ë‹’roÚZeÖ+ߥÁð•jkKî˜q+ú÷>3ö))ùë•á8¿E…¸lQvƘ °³‚Ž|è(õ3()Ÿ78b”# I F1%ÊÉ4‡ø[t©ÞkAÑ–‘kz?J5gž‘k¦¹¼÷S2}”#‡´YI‰ö`ùL€ ³Z\I=nÕù–m½%”h×êP~»fCy¼iª¡Us­âð¼ý\Jk ‘fö–å<ŒÃŸ@¡Ù‰æ yYéc_ÝøDñ“§Mk'Ь<¥ÄBÈŠá¾æïù¾Œ,òÝ’3|6¬-ê<øQó˰¾”ˆ Ó§·Ý{À?~ƒ…’$µÔ“ ß;?=}7Éîõ=FÍ R©3…ƒæ&©öC1ú‹£ä@X n¡0î–œé› õ$)ḄñE»KVk2æú<ß„Mä_™g{:,Q×)©H1üÚ0Œ! ÍÉßÔ—W¾»¯Ì“xM:ªŸ’òàH¡^Ð ã‘\sòwn¸ÚxŽy`^Û2kÿ«à×ÇòïàWªiâI4ëuϯù2¦’Œ%K–èoþoãG(ó»à5Ε žo¡Ò‚<_ú3Ô´Uæ/œ¾ƒq•Ú+)>Ó ‘–gz©YNŒ=;,>—mKT‘5œoÀ½0^s]y´W¸à)Þì\לß+yèСv°?)(/z5Òm5}ºBóËçL€ 0£€Ö’…˜4iu¡ü/¡Vh’‚Rg¥~µ§p…Y}^ËÔ´'QÝ•’á7~æÌÄÐ`#3|=¡ ¬@ö¦'Ϻ˜„Škbꔜá¡aó&N,L0SQ;½¿¿%z/NìÖ*“êÍúª±dih)íÛÆôPRŸˆ8Ë]™PP^@¾ºÂÿ&Gþueg‰ó´ëO~ÂV&ºÁšTBÞnÒ ã5øßœâq†nô#ÄB‰IR[¦Û6®©:ÝNŒë¯Šü/+á\¤49ÙcÄž‚&‹û:‹Iß‘ýyp¥¸{ä×QE?œrÍЮ—†¼ÊÅŶßÎwCÔÅó‘ûGH4ÚÜ(¥(QRIü:$LC}Îù•+ã­Õ®Àqghˆ¿§Ž®äNraEh(çc:/· _†ÓGjr”ðh—AQYíXêýQfN/ò¯bÑ‹Êë°ýt.ÓÛè ÉÏݪÈiŸä¹7œ‚↠ÞKG^‹ëBׂ7&À˜ˆµ¤•Âj Nlβàk>}TžG•Ïj“››•¾ Êoñõ;uÿþ²Lô‡xÜPrî‚lïfŠƒžã  ¬ÍÏöúªd<›ìͨ”“ŠóêÊ—üª,+û Ó²‡Í±ûªâ²ƒÌ P‰ň6[fLsÞuÁ‚Ј°@œæZ G,DïBöi°|+U¯ØNíºÌ3¦Ü ÿ÷YAM ™äV¾£pÒñ«žÇ½, «•ò‘™s…åØWèšqanÖd—Ñv!2DÄy A56¹/Û»MÏ,[»ñF娱¨H× IbÀ3ƒú•ôASKTþ/ºq!÷D|í£ƒmd[rfö{z 4Fû Ùb®îéï÷ï½²Ü_ø†0ÿ]ˆØùn Òë%¦Ô±¬¹H7 £œ £iÉ©TR„|6XA¡€ˆ÷8ÊìC§T)M¿§&§¼”?*àJ ÆGõF9~\øà¤Ï¡¤Òqÿ"σ£rykƒ]`ùÍ7IAn æ™kf¬ESζ"§d šðž/ðgߢkúm¶RaM¢f¥e(ø5PÆÖ [Ùç";ÜŸ¾JJuÒ0#½„×U;à–£'ƒÏÝc[ùïÆñ(§çF8y„™}žm9ÏK¡ÿ)ÏLÿ•Å{&À˜h~-¦¤ØÒ¹0>Z›C¥tÍØ‰õž3ý’uuI¯2忈Êý%t||β$YJR‘«Xì× Yˆú%6Ê(š5ìªÓv¨L…èJaU( ÌQE8½99sÿ¡*Ö¡"ž‹Í4Xab¡h”"ÍG)mUpv´X×â.)bk¥ïÁÿ6Ç%Žˆ§’Äõ&pyӯǿY¬ŠŸ€ûÍšô4YZ²×÷o4…eB3yÇ4‡•QZW÷9u-Fÿ¼-üÎ Xª2bõ„ïË­B“Ð%šGô§0‘l4Kðð̜ۥã,E?—Uá®7]GÛ²–B‘‚ŽÂŸ¤šSOve{Dâžyæ˜B÷œ÷L€ 0&Ð|`5þæ8N﨧¢D­i@!¹ éo,°–—‹Ö6|©ßƒö‚)Ý<Ô—EÐ̬R—ƒQ Ý\®ä×èˆz@9Î2ô‡¸¤)ùV~Ñ»Ìï_†¯ôPHv¢’½ éÜ–¾#Š9A5å¬+olµ|¥ŽªØ€4GD’&MÏünFÿ”sÚ%Å,®-Uò†G¿ Ö…¶Âr>V›wÙŽýL…(;¾©y¨-ÍHx‚ ¾ŠÛ4å‘,ê0‹Ý§(SûŸwæ»äV¹iÏ¡ÏË™°¾T¦¡¦»XOÒÍÃŽµý~¶ÃŠ2ÓCóš¸1#Ù/ÊJWIù€í8‹Ñ|Ö94®ãp\§ŽÈã¶ßú6øWnþ14<Ÿ3&À˜@ó@=ý ¯Ã¨•‡¢ÒñíúéÔ³ -IDATîWyhZ4L×_Xx¼c°7ßL©µOÍ)S&—ìl®)ÏIfbLL5Ó„æ‹Î“óòaL€ 0#š@ ))+ÇÚ13Ú¤ ¤x ¤`àoL€ 0&À˜À‘N eš{”ØmPè {€”hSfùL€ 0&ÀZŽ@‹()˜§bC´‹„þQO#Úe`ùL€ 0&À˜ÀA-¢¤thÛùtÜ ŒÊ8˜tó¡Såòæ•ÈÒ˜`L€ 0ÃI E”Ó<…”ê‘Q)°.ž‹Š\ʘ`L€ -¢¤J¦ÉZ';kjÉÑûwõ¼™ý=ŸISÓçøL€ 0&À˜@óh1%åÑYýÞBçÖeÍ_HÔezTä²P&À˜`Là°h1%…Jˆ•‡SiN³–VŠ'çÍê÷J³ÊlFa¦¹$†æ' 'rììÙñXôÛ6ÏFòj“4aúô¶µù5ÄV)¦2…Æ©­ŒÁáhÖÝHÂQœ{Í9ÏP\_Ùˆ1Í“œ>fL€ 0ÖO EæI ÆpϸåClè?Ò •³üïñíÚ_eš½±ÖKø ³½b¥`Õ>ß—9.8¦À_®irºrä˜J=¥}’çì'V+Pˆ÷†¡é“ifW¬““ F^–÷þ`îqjföPÛQy<òÊàuxhš}„‚M·Nöìô4ÛìˆÌ©?Å*¾³1"‰*û8hŠ+±Jó(×¹4§žî÷Ûÿ’šxkU/ýKî–ecUdq Ä`ÖÕò}é/0¬Q”‚™nïVRí§o°báœEYO‡K¨6ù¤\l³³ïWޏËìÂMóIÌ!)¾Û©Fc]r¬¨ãÁE’ëË|'X>M覊¬‡0këÅpÇb‡bŸa裘“¿ GÇ)S²¯Æl¿ !o Äâˆâ6\»¯ƒÃáÚlŠóx–[7DR6\‡W®kóÔ»ˆ`p:|̘`‡—@‹ZR¨¨sg÷IÓõ;`Q©hJÑÿãX-öºº’oHÏbT¬¿ÆÚ>Õ ÙhszxÝUëµ4©Jö¨È 7ð/9Ó—‰ênGÅ»&8**ï+±ðßÝþç-òe^„J×/·ü8ŒÂH¥•HCJóò|_Æ…Žç`ýžÁñC±¾ÌUXDñ9âQL–åŒ×”xò.mãI¸ë=02Ã׳JÆ·†§ÃåÈC=^Ü"•3=0CkHuÉ/°r2¡ téæéw)Òä*($T³¥ó"Ÿ÷j”é^,P^jˆïСDÚ ˆ{>•ÊÌ¿‘çJ#…'‹|‹(ßïóöÃÐu(b27XÖÓwÆ­´Üë-u$æµ*±Á²ù˜ 0&ÀZWR¨øÌº| ¾l¯Rò%]?‰eVDšŽ®zè¡‹ê}¡oâTì»S¼Ùý\Ùå–õk(/šæÐJEIÊG ÃÜD+ݺa"Ý{„ñ$,,ô…^c¦[X.°®ŒxÊZJÄß¡(ýšäÒÚ6yfút e‹Êó*÷Z×¢pºîY¯q×`Í¡óÁ”/¥†Ä—ÝDSÞÃÒòº-D_Š+Ä ÌQJ:×ëݪ„´µm…È/x«M>5íÀ¢1,Á“8Ñ-‹¯Û¶d$£…†ìcÝb¥ ³‘õ(×—þo× ‹ô­Â9Ç=w÷ËÖ|…µ’Ôy¾ ›È-÷ÁŒ¥°¾ô¦&&7Œíw⦥Eë-)£ÈÐ復ÕPt\Y¼gL€ 0ÖMà°()„dÞœ¾É“;«I- 5uAý˜$êW¹TJ½ï¼ÙýSçÌévœpr”ÐCzÐTÕM¨È°ÆŸ6 ïåRÓÆÁpõÍÏš´Ž TäÐ…‚7ÙM)í×ËM4]ë†ëŒ–&ÄSê:CļUíæ ×LÛI?¤T#J¦™¥…å«£)Ùå=«ú¼ê ÙÌ9‹4$9¡~µÉßf}wRØ]ì”\ŽÕˆÿLÍGãgÎL¤øu’¤–Yd¿GîŽcçë†<ÄBš–ð‹A(Æ!åu¤Ó j[5³@<©¶”‰R²|!½·¡›ª›e¢ñL¨Ìpe+³* C‡´JTh>gL€ 0ÖM ArseÞ˜3Ê™ÝwÖ•—õ=Qêúµ¨p§£~ –…ÿ¡²Zý‡p{B mt¬&Nytv¿Aóf_öaCóaÚbTî·’’jN=ñOéfô¯®$‘¦±È—þêûí[íìÔ†Ê^‰$MSÕ‹ú=qŨ,é OlËɺVNŽàó”mYOÎ̾ƒšŸ ô „âU£9-ÐqÔròt©ÆF ²:ˆåÝ!«§pœ«”Ô£gáþ²WÜš®½ ý̃ó±°í±ãŒ:Ë1ÂÌ>ŠÆ-²­1וáî5G%A!ªfFîÐÀŠ…-ÛÑñVg嵸VŸ,œ<¹Æ2 áʆfž?áÞY›ofü—âòƘ`G|àþmèP‰fñfÕ¯Ù3´Ðœü ¾ô·l·|W:B»JÁ³¡M”(:XŒ©jåHsÆs~E“¾¾aïØ#Y=¢Fþ$²Fåš:Å÷Kt¸"{ž0°)…ÎóeLž9õ-¡¬›¤ÐW@ h§I±5XfùÎýA™x%7;³Z9 ö¯íy6`©ù}Z&U…ù,7¦š3;9"©Èñïz[óˆ_wÞÿnµrF‹Ö{w~8yàÚÅo•?­ÚÐÜI“öF“{Ð÷¥šY¥¿j«tUÉÍQw¢|¦žà¸¡e£þ8–P)B?aYªRîÏÑPè©°/‘å‰7&À˜8"VKJËÒCë¸Í2×£¶ªnê ÎÃülï÷šÐñ[³Q—a´Jã7X¾‡à4W:½žŠÚq³{Nkm[˜ ñÚ¯¨Ï†ëÞØý¢¬É£ÿÉ”<_ú«h½ê«ëÚJWVJ¦o&”2txáºEºG3Ôæ@¯™ (G¡&¬$iíçOóLï'Ôô³È—1Ê€'Å;㌠àCRlù+^Ö…>!×ÌXê8WÆwhû:Õõ#Ë,''ÉXmkåÐe5°«Ñµºo … W6K“×ÁëlaíÜÿBa«W ŸÜ„QZÛ\Ù¼gL€ 0ÖOà˜QR ¥– r½—䤮žŒCFȸ—Jõ<~6¾µa ?qݳׄþ š]î88§ˆü#:½†S]|×?*=ÆMs32vËîÍBM"Án 9FsÏݨ¿s‡÷&gø&Á‚У›'#%Tü~]×Ü*ž:ÓaûÓ~”™Ó ŠƒAòãâÕ§`Ú‡úŠß蜜¨éçêóNþ†Î]ù”F‰Uü"âÍ î@KahsÃåeMúNÛ'gNëCîÛì_ ¹g-5ïHÿ®_@ö¦9Œ–Xlˆ¶lùYAakëþ”¦ F„—0²¨KUTÞ1&À˜À@ U4÷´§ÙÞ͘/c>kI”¬°rŒp”z},½åfMþ •è³Ö†Õh¡þ!_Äžô8 DÓ™}аü«àH•ýÖÀPeåÌtlíÇÿªæiù?XÚ£ÇÒsÂo)*Š)òOE徫ÀòmuÓBsWnbÇøE»Kž*Þ[F£ž0Ú¦öM÷Èa¶_=ò”ùýΩ£³36R°`È/°–¿G~å%6t39ièС6)&®|XG ô©¾hë…<<\’¡Ý˜¿Þ ÷UBê¿޵á`8rœ4ô@‡gð¹Všê¸u• ú¨Nƒ˜`L€ 0ðh.‘†Ì²ŠÊyc`ÄOxqa]©Y„¬ 7*'縰kqiÎoS‹—pGüûG*?\8š.X3&À˜`‡‘ÿñÑoÇ?í˜Gÿzúý“ÿzñîùñï/½«^Ôϼƒßž½Þî=ÚÙÝÝ»ºê…»»/Ž_x¯÷Ž=¸ÇîîË·¼GYvõÃîîõõµ¸R~'¹tû»ir¦Ù××p³¨àw³î#xLq÷™æÀÙnÔÉž~ÿÝ“á×§{,ú¾¾†é~Ü ¿<Ùugábgáy˜>%OvG?GUàÙQ'Èà–ï‹;+i¸ß=ég)¼òSh¡Ÿ\ÆÑyš ®üwðëç48;ë…êÉî°ÈTiª¬Ï4Ÿ)M™™*òdwtë¢ ƒ,ÙëþgÐÏÆÎÒA¸;ºþ,è|tŒ»ð¸«‹¨3.6|÷a1W¢?º6nÆ_‘Çùû±÷—”Ð -¥ÒD -Œ=m˜oŒÕpŠhC™üûï™wq7{Þ úÕû%½hÒ R•ý“ò£gÓ}_;ʾöÂIÉÑëá–ÉÇÉÕ©ËÅõip=iÖ¤aoßM7:{ÜK£_“E÷öÃ?’ärŽÁò<ˆ?ýwitM†É¨ŸónžþÊy¤7¸Œ÷zÑy\¹?­Þ?/}ttàãòiôžrcùQQøÃ ÚA–›Â;”îPâQýg?éýƒÀ¿JëÜM’´\ÿMeÞ¯ðуë Ê•^Dý«^ðõ¨ô*O†9 ÿ¼Î¥÷£7þ]¾ÁOQ/<þzU©À<(—ÍØ‹¤3¸ ã¬>§‚Êà½5lª_j?ô¸¦PLI+ –ÑRQ"jj¦µ5)°Ž0FpÅ#ZÍÖœùôåaпºÉõM=ñ,ŒÄ.ê†Úï/neeŽYaG À¢Èø¿£‰¦€7Ö¬ïhΠ¥ükìèÑË¥q˜ÝhПyǼ–™®==gJÓç8ü’5äÙ«“9r’fgô¦r”ÿ×¹:éƒt’N¸K‚ˆâþàþš|ÿ×É BvÚ;9#'gqÇÎEöÃŒx`øçßP¾ã¾TøçIv™”'çiÆîÇioÂqx /Q\`ªŸ\i÷$û"ÿ)eùAw`LäCÜó¼·y!ò"’äE ¶;(šQЏûFidƒ4è|ê¸Yt~‘…ñYÐB&ßï^ñôä¬O•wÒ9cÞËÿùû6¹f~^ƒÖûÚÆ%”¾ ¯¼¶)^S‹òwÀ½2^CÎCÎCÎCÎ[ç‰Ur^.Ç­òaÈrK¹Ñ–[!Q—ÛB]N2¶•ºÕ–ÂìÒD)˜xµìT·\R®©4Š3 D!Øüp+´eÜI`"PKòòòÚb¼Fí:•9˪Ħ *s[¨Ì­œ2çHo Qùb¤Ç&5$=Ñ„âHzHzHz·’#‹“^í¼Žâð¤ÓÛ¯ÂÞç0‹:Á¼p=#ü–Õ«0è67aÍoÐS†…gÞ¦ayNư”Ô0ÆA§n# m>¥†£…åÆÚÇž Ô'Œ+j•\J51 !…J-´á ˆS+yµ¶àwãœÛ±¶ì3Ðä4ðM¶ÝjÜd4ï¥éLïN¤¥:¹êuxt¾Î? zýÜ­eR&ò´+ÀMÃ{ÇAÔ[¬%¢n‰^óWeó²õ*E/ùœ2€¨UHeºÛV °Ü:6JKËÜ»Õòr1çÅyZòóÔÚü¼±ÅyKŠÓ†·AZžÈÊïßî{”Г¶ Z‡-aM‚û`>ª¸?;¾– = ò¦%RRb ¼+  Ð7¾sQЂ0%Œål¶pÁƵ 6 ì#Ìî$ü&sÈMò½ÿ¤¢B‹Z¬Ðbµà2ÍJ}îµ¾Ìã/p;Ñ‘”Õ¾âZ[.±Rð¡ç+·xc@/ÞÒR—è êƒt¶):[X#3µÙêèÌ}ÞêWÞKÝê×Fò»ùµÒ„ˆÌ‡Ì·Ræ¸Vs«mL­}­¦l~›­F€ ÁaZki•Æ0g¦Ô7´yêEÓNÓlß*éÁA/è„n?ƒ°Mα¢5¢²?'Å Éíôê"¸cfoF¢¾¨W7X)DÔ‡²Œµ˜7ó#¢®QkdTLìñ@Õ ŒŠ2*"êzeTg6GD} ˆªQFEuD}p~dÝ~ƪUúeÜ4•´€H˜ú‚3\Æ$«ºepÛ˜u Ö%¤´ìLf,R»E[ôïX&é€5ëõïx`°Z1®V—ŽÒ,gÞwkóÃ*§Ú†pÊ)% Zi±®–ž­â*fýß^\Õ›ò›+çvR¦vš6øÍiåË\vRLª C·¹èåÝæÐmõrt›kÛÜ‹ÈõÛé »Þa‡Ô{\†ý« Ó"ºOiÞ´M»Ð•¸”CV—,«\*ño@å5 Ø:3º ¯‚Kr)r)r)riK\Ð÷¥ùNTmôC¬Ü0‰V²‹Ò»lJ+Ò÷iEZË­]‘Ƨ‘­‘­ïÙÆÓlµ©‰†K'0Lz’MÄ6ÚÆgœš¯Êkœ#¯ÝŸ]|´À]|‘‘ï# ÒU² Tn«g`6PñµßU”èp¼ úŒæ¨Þ!±!±!±µƒØ8æã»Í1I¯} £—ÎÇWɨG«ùø”jÌÇ'¥O©¡Ô3˵·W~H†­e´¶yY¦ñš|Wíi¨zp¯ Åú’®jŸmÈ%àò¾#¤D„\)BÊõ#$Y!µ³Î(*(§–99°㤜w'22$"äöF©hÞ–ìÎÆ6yÖVò@KÌî¼geѵ]kÑš‹®µ-0æç¾´Î¡öÓé¦CR¨ð ‰Œ¨T²&Þ,¯ËrUåÍ–­¶ÞWÞ4țțțț-àÍý¸Ÿq'ôXËÂ>œ)¥²™§D¿ m û [¤IHXÂ,õJKÊ›@ö&xVBÃàŸTTÈƒÈƒè ´à ö¨¡³²Ã‰B:ÛN:#HgHgHgHg›¤3tn½u=ó$5Vje¾[䂲G–˜Û3ÁÕVÖw^® …©/ÔÃÜlzHdD1¥-J-Æi‚Á0j£ rš–PM3%&Ð\­kBÅ„°Êm†„³EQîE¦(ð ×Ì+»²ûý Q#«Ì@r/äz "Š4M‹(7¡¡$V±ùÅz!ÈôsJa(Ö£Xbýíb=YmJ’Ò ¯Yt1Èt[Ét™™™™®¥LGÑ€Õ]Ú€5Þê\A¥1Ìùì—2d£ýj;bÐhµ”ÑÊb¤áJá®ùª6)€h”iße„”‚ÃL³ÂÈEb±o[ x xØB±áp)8”m /TÎà_’@,mÚ à ×àE£­Ä0 “@s†I´dçŽãA‡½6nÚ‘ âMï|e„Ï8å  F;zt)Ë-ñ©i¨¥tû2sZä6ìñUne¥ Ѐ>EéÞ4šŠ†ýœ³¾Ý"D±7ö«Å–Ø ¬Bä;aÛ»±üÚ¢ S—;À ¶ ´6AáAÒÜ;•¦BÇpÆ“ZP„:»“m^÷0ÉòíyJ2öø‘ìf½qT}êMŠNòç+¬YŒs3Ók+Iî œmTñ[æÞ­æÀ¨¨š‚ú¬-(Ñç-)NÞFœðáû·û%t³,-áÖܽøØ£ û³ãk™àz뎺ŸKô î”A‡î 5‚£œ›ažË:hOn ímŠ7@{ªá ÄÍ5áæ^/:ë(°vÙ©½(Û `tó,8°¢˜ûotª) ð(ìíôÃôsÔ ½8Ì®“ô£ÇN Ù\pÉùÓ0Ñg>Ùeb#8Å…O( •6œçž=¦†kiÂRI%¹?+Õ>™¶y¹ÍàËçä7õòù&‹ XSgêoÚk'J;½°e›íj¢Pì6ñ<û•ðöŒi#Ä‚Ù÷@”D2M-e«43­wåan3Ì*wUé•}21št£IÁMSpÓ´ƒ£[^+Üòì*ýÏAõƒ$D%«¼¦-òÚ6òšÜÊô±¹aÊÙN.à¦Ç”4˸éLýHfHfHf‹™Z)™Õ(iå”?š#™m%™™­UÒ¶ÉŠ„¼†¼öxͬSIS yí¡ð'¨¤¡’†d†d¶92Ó|›¤ú‰ìØ*ßÊ¡¼Í‰ì‘ MÎ ˆ0^¯¾ ÓT»6讲ißÃÝ ¯µÊFÐ-AW6C`@ñÝpUÚõãêÒ›;sRå”W—‹à*§Úº9˜R3Zê…pµôlÉkpU?L\ÍC;@7æŽÔ¶˜ra¡O©ÑRÑZD¾A¹0F¸0TF´ˆ«+ÅUN6™\N˜Ò0M"“µòe.<)æ<Ñ ÃÀä»ÛDî߆ÉhÁÀä6XG^D®ßNYØõ8¤ÞÛà2ì_6탚æMÛtrM‚·…SF I)Ç0ˆR¨®¥÷7 bKö¬yØÁÂúD1ë´Zî¦ ¡=i©/¤RÆ‚hËsQ£1Âr‘ù–`„%FX¶"ÂòÝç0í_GÁ•ãÐJ ÀîÓÍ…SRc}ëtm¤’ 9@b.?œT8û›aþWJLP%¥ö†}ÓRØ7„}o§´ô]z N„¦ 6¹ʨðÜ6äÇ}o…gÓCÐwÜRª^buT³ìêhu³åštËË›uq‰7WÆÕM\ݼŸ«›œˆ¶¬n2þØ3ÒwATÊJÃ)³Æðêê¦dDÀŒ$Â70œŒ¥DK…i—ᄎ =‹«›¸º‰«›¸ºÙ’´ËûŒÒ<¥js/VnxY³š0£º[±Áø¬mŒÏ‚϶µñY­U2­‘­1T«6T‹­vsbækgqQj°“¬á5[ì)à+ãRoPn´åVTis¤µMÑÚâNR¬•Ölù½dÐm™)IIIð~‘ _% Jå+ë]GYçÂÛñ^Aáàsõ‚Ó¨e_=¸rá½xx²Èúl¨¼&Ú9Nƒ³³¨ã9\Ǽ úÙŸyG0Ò(¹ ™×”j§Îù²¬#0VÄ*¨Yè|fÜø. ž ÙV\µ!ÕÎúAIqLë…î=•pï¿`;A×çÉåÕ ½·Iwó¢m”V·:™J타R£4B)B)Bi« ”­J'FÌü.?§Qw•Æs|õè*è@ÆO®}ûfg'CghcýO’ÖV/Y—XÄV&×}WkR¼Þ>P”Žƒ«þq2GùÉÛço5ˆºaÿuÒùŽ+ÖÈTé÷Q?:ÀaÃcrG‡ƒà<O®úúŠ/¡às`²,Lç(þk^'G`ªcˆÂ"ø¦“'Õ%òt/~›S”t{AÔ8O¹Çx´Züþœ×´sÔ‹:a¾f¼Âë[oÛ4À¡µÉ kâ{qtdae•”:wµ¥äM»]3JÓšÒøÏË…0ãóÜ3¥âÄ'¥ò½¼™/ãó(®¶¥›d•Æ8ƒyâ¼_^‡ñù”é|ô6²ð[ò àXéyýôU4WùÙÉMüØ4{ª“m¸4³@ù>Ìž£‹pj=§øãYñ&éF ôç­~1õ¥G7e„ªJvˆò¨þAŠˆôþáz®ò¬üFSsuxùMeáµ÷+H…Á5ôO¹âÛ$ ç¥wi€>ÓÉC\ 4 ™¹oî°é|§ý9ʺéܨ´Ù­}г½ìÏî«_z¿ $üßg½?ö_%{âßÏþ½ôòbïâÝoDÒÓ¿ô:çÏáïî»ß觯Þ÷öàúó—òø˜ýòéoÉÞþ‡—{GŸ ÌO_z×Ͼ¹ù¯Ã³ì.ÝãPå¦û€Â‘΀tÍí‹Cíý£~²ˆ9ž1£gÔ=£b }ÒJŸi)•&Rha¤xìA}c,È ’hBàß·4Á ÄÃð3[X3;îÃH8tbß:‡ÂqrµŽÛÏbyþ!ÞÅ¡C‘ä0 ºïâÞ×Ûáå0¹žIƒèEKbdÁ:|"uÅGàã(«"áó þôg„–¢Æ%ˆäy3œ]ˆz{,ú<¤—Wv€[Ô[°îoqôiN´ñÞþ·~ø2΢ô¦n?¯Ðù!ŠA·nFõçƒ4…žÈ{²*³×ˆì/¿\1L¶ÿ+ºxJ¦›ððм‘Î ÕÄß”+·-¥°Åž”<ö´.%Ük3”'Y:wg®z†®€OtL]žøO_Ÿu÷½éåaÖõ‚ÓW¡›Þe I–E¨!K†ç3è5îçqâ+a¹$JIÁ¥”VËÜaˆh_[çhÍ5QD;ßwn¬¯(áÖ2E9—ÆÖtÙIrYU}Y¤xWÔHN˜æ¥fº:ïPûµXRã+S3»¾›çqSeÄŒÍj‚n$š¯z0õž~ÿÿIü’Ç.¢neutron-12.1.1/doc/source/admin/figures/bgp-dynamic-routing-overview.png0000664000175000017500000021670513553660046026365 0ustar zuulzuul00000000000000‰PNG  IHDR씚¿sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì]`ÕÖ>3»›Þ(Bè½7)¢bïØ}úÿ@ÅölDWIÀòž½þÏ^žØ°ƒ" ½Kï½==Ù2ÿw&™°Y’Mv“Mr.LvæÎ­ß{ï¹çžs.‘8A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@AÀW(¾JXÒ­ÝŒ›4)Ú^ „»ÖÂDj^ALÔÑi£GÛ\ý+zŸ`}1žlöx|tÍ(*rX”½Ó•–†{Lv»¦˜#¿e}8£´ðñsO“ã8H±ÇÒùiVë0{EÒ¨ÏaF'§Þ©iÎði)Éïy«u®ù-wMWµ9Ñ–æG¬Ö{ó\ýk˽/ñª-H9!4ä(û“R>#M»£´— )'ῚTåŠküuy„Çc¯¾œ}"÷MÓî&Òz”–‘²NU”/¦¦$¾äú¾¬2( 1¡±×ÔXsÜÇžLBe¦IŠMS´¤)[LDßNIMúܵ,rO4îÍ7óddj¤Y”5¶,±²X=`ÜÎf³o/->Úü˜¦)«T“òÊÔÿ,-Œ¿ùù/«¯”G( µ¬â/0Š¢ÌVõ=¾ððøü@Šv “ÍÅšÓù9íOÿDD©ëƒÉ/vÍ>ž³+à!@g¤µDÊÿ!­'p=‹ôÞÇó&@œàr”…xqH™† g²Û¥iÔ×”4û5ã¬oF”·,ÿâ4õzÑ'¤Ð ”1e¹ÞAÚg ‰)³G'½Ü¾¬øõÑÿ­‡ÎGÛÿ¬ÅÚ&ñø&Nëß¾7´ÓǸfj¤d£m.s:œh—‰“|•·§é&$¥&ߟ4ñÅÒâU^¥å-~‚€?!`ö§ÂHYü ƒýûÿë^²1Ϧ^æt:¿ÃdëØçR§ãý÷®aÆY_ŠË³̇_CLà~˜ï™6ñ™õ®aŒûÑÖI]4‹Æ\’R]ieübOÍiÿ/ˆœNyöŒ)ˆx{©‘Ëð,-M:&yÒ…NÍñÒ½˜(õƒ‰)ÝÞIMÚ[F2õÎ[&×V:ÙwuW´Ãï§$ßçš³c’RoAú©S£g@þ:-5i¡k˜š¸Á}/ˆï¥eå]-x••¹ø ~‚€p4ü¤!j[1¦¼8‹HýËÿ<×òó¤"ã Ò¨!V¿¿ÆZ.<¿,"ƒãMµNØä)~êÄgÖ*ªùVޝv­Õªyå[ž2q¢Xó >Ht:ˆ¨0£¼ÃyøÂY­yš.Ë10¾žÆãðVëô€òâU¦<å¥÷Ä+¯„–÷Þ“wàjhSS“¾D»|ÎbtɹâóÖEe±:WÚü>!ùÅîØ^l]‘° Ãeå2W$¬†ã$X§†Ïò+ø#ÂÑðÇV©-eÒ´}\TÍIQ®E~àÙɃ@d Ƥ‡Qó!_ Y2±¶õ  Òäv(Ã6×rTöžËû€õåGìöü‘ȯLHštÝ´” 3Æ$MŽõ»`ã¯~?%©L Xû¯kD#5Eµ27wcûh‚¢Xn 1ì϶åLFÙaÛ§Óý‰)¹ 6«&š4å…¤\!.ÂÔ©íàÑk'd\4¥ÇAÛ¢£“Ù’&îQ@èŘcßp—QqÍO ´sæi/¢ÌƒÓì[[€° Û‹ÍáO±PíCÖ—b l¶ç¸<m;ÏÓŠ¦,Q,êLr\ò]Žçˆ©“x+ Õ<ãxÒ“œòò…•~·Ó§ò¢> ¤à:”5ujÊ„_Ï„®ÜˆÊUÈô~§¦u)-…±ÖÉmv{*ÞõÍO?ÝvtRj&ê õ³©§¹ÇyÔúZTŽ=‹9‡Á}êþžŸ‹¸\ÿA;Íœš’ü¨>±ÛÓ_ §]纠LøF&b °Ð©ª2Þh˲ðÒý5e-¸2÷£MnF_¹/!9å<´q$Ê{mð«j Lžb}2ÝH×øÕ‰‹¤Ô;ñüøè䔸þCÆqÞÒ4Âð/Òø ÄYŠ«ŸÜ 5€WV5QpɳæÀºº—BU´•®¥q8wëþ`V¼’¼Ûõ·ï1˜BvÄ™+­‰RZ™Þµ>uòÛ…ï´‘üÛÌÜq!~ ³[JšXêJ–WñNM¹F› `EZǧ&ƒŽšf»9Ûž½3À Hg/&ä!s‚‰Sëçphß@Hà|\rðØPrhßÀo®…+zÚÈé95ç‹í¦º†ç{#?Òl·8òhÊ2$Árä7“tW=£óíß² ˆŒð»^O›èGü† <—kvDz„ÔÔNÏÕ!l{ÎÛÕïyÂÆ¤þò~øôEžÛŠäiv u`’öÒX£êò8h÷Sîe“œ: DÆZÐ;· ­Òg¸–"l7lóMQ7Ã}õo È5émCÔÖ==ã[i¡…í§Ä²_°åX8Ò‡ðð†Â0)Ñ”uÅ—óÌ6`YxéþŠ6ÂÜÌ£Ÿñm|Íß4žœ¶ü%¥q™@̽L?Aø€õS•”â»ØŒz3ѶQö‚ç󵪪u_A &0×dæ’wíD€'ÓŒÓ XYÝŠUí|­Eô'®5Á<“ Æ;µâÆ÷̺֜¶HLh‡uÂÀ‰º¤¡8iWƒzgþkµŽ*Àªó#°ËŸ* º^ϲ¿«ËÌ(À¤­…bàŸñÖ„ G]ßaH(ß+áæ{§=óÌiã]Brê}¬}„Ãdl|àÊšòü„ÙcžôSø WµÞ±ÖÔv›¶e¹«ã°ßi¤gübÒÉèÿ(>úC3hŒur+§ÝþÚ„KÁF”sn¨9ô–×­é÷ƒ“&5*ÈqbÛH®ä:ŸDZé•÷ ®@°ºé- `å–÷]T–ÇX_iÒŒú(/~EÞé+ù䔫‹Â2ÑWìÀjÔ0Ù©70ÊxÉu²e;y2¿–ìéOÁßj¼«Ì,;*áÙÔ+5‡óg´÷‚i©Éwxœ–Fq ,Æ«dºÊ•ÛóÔK/…ŸÌ,X†úv>d;øÒ}ÕH»¨ÝÆs&Y¢ã§YGç½{DËShhniéïOLö¡Qù*†€—VËLBÕB4mX¹+ùÂ@¶ ê¡{2NåÇ xIQ¯™–’4ĘÄ\j§k`]¶ÍÅÏ«·ºM§ý‹¢DÏZÕ{#3(qn/LGëd¤g6™Þg¦4®{J“ q:5fiƒÎ2}hÄ9ó«d+– W"ƒßED°ìA"5L§¥­Î„G.,›01ñW"ƒß¿gM\‡Ÿ|‰J'„øÞÕ±öFT„åq×ö™b¿+fLŽ\Fæ2hDû½3aÂqäù%ßc…ÞCÿ=ǽ-4å!®CH°z³»¼ ³ÿ]‰§s$Wêkå¶,$ò°%GÊÈý”P?¶ÛmVraÝxÕÈàĸN¡êmÀ#OòvQ©™Ô„§¢¼ëJdp^~úéL4ü·|ïT¨ÿN³;Ïç†Ágñ“ ‘¡¿7¤(Ü¥FxùüáhøC+øu´HÏRTDÞ•oˆ•a û'9ocMÝ3ÅšXÄBæU¿¦¦ÙS"0‰!’%­´ªA%p VâÿãþNSM¾?q¼>yº¾S4'öÀS™x B¼f˜Ð:Ý6‚'JL¿Äš_ òÅ΄=oHeh ¶>ô¥²ö®uüŽ„¤”?ñ<õW€~7ÊÊ«j›­`8,鱿gË#(ÚÜiÖ'fI÷¯'ŸÌ[lní2l;µÆ[l7TÀ!=P çÁäØY[zl¼×'-·¤°Ù4Vʽ´­4NˆjRg£hb-Þ-j©ªÍ>*Áàž|V´Ú/5\E=Ñ®­AØnâðHSþÍ ß!^ÛM¡¦{¬ãÝ«1‡Îd.•èdîÚí#wÙl¶!YHLé‘jî‰LÓKËÝl¢_ìNJBýù{(vØ–k€ïÏêYD¼Ò$2M9’‘‹×‘Ü­VðäÄ ~€€~Ð~]E}Ð]½5Áú¯ÆäÈ»spŠÓ¦Ý òÓ^Hü…ëÁƒXùpo'[~ùÞÝ„G±ª+†Mì+k¡&“ãêÙ“¸NS ¿Õ&œUªbzqÊÄñßóòîìXU÷Qí¶6ŽÂdv–ÈC£iðfBçñ[LhØЂÑL I>+u¯)¥a¡ç€*€K¤O!…YžùËD Ø:4G7Ì´ñxŽW"¡{pÅ!tZ*nS*¡ç$–o€Ä)ûÏäræ.ˆ‚NeS‡8«,gB¹FEò gO~gBUü•aâõ+r¨Háܪ¤L›˜tEÉš8iö…-Q¡‚©Ï=½šõ#¦Û/s¨4&Û»½¨±Ç€ g©ß„ÓpêÕ\Üm XhŽfãâ:oßç] n;–qÁÀa•®ÈÈ}M#`®éHþµ¢UùÛ£ŸMÙïth3°nb»-ÏLÆ V[Z¼Ó¡´CíX¢¿„›–’8|é„ÉbL²g÷_©àñÏÁj®@µ˜4£Œ‰lg÷à^{ÆÞUj0™o-‘h|ô ez:&Ãk³¾Úð5ëãºü0Љ!…Ì•_ôbâhiþåùÝÿlÊÐ,˜ |Úƒ0°¡,i˜ˆÓ0™db}þ‹©Eƒ½YLVå9ÍØß//Ð9ß!ó–Lð L‡Î¸bvB£x»j¬5e€ÝF‹ñ…¼Åë¼âšÌqZÞ ø˜ÏÁ3ß kˆÂ{•œ‡uÂQ¡ qjŒ T©8¾a4°±O¢‘Õ9c(qu¥n·Ö[™S!ˆý¥3Xݪæ:¯r8µG8÷ùs&$jDã·8A rĨØJd½cŸ±å™T e0–Ø_>óÞ³;Lø¦¤$ÏaM,c`ž¥âyh”ˆKÑŠUù™e0±~È[HÙŽœÛÙolÒK1ç÷«Ê²RÞ$Èi¸;¨ÉŽ„Z+Œ’)Í0‰Þá¿(¨Õ¶Âvǹ ˜® áG}žwïi~îñ+ü¬RávSkXá8|Ïš´Û8ŸoTìµ=•,²@X5rçúì µ1?ƒ=rØÕÿ\÷šC‹â0H¿T‚î\ñË{ÿÜsLÇVÜ1§Âf„úþ<ÍÕrœiø†T€(ºkê I?U9X!b«h/¿3ñ™îå.¢œ B` kD`‡eçCUôK÷°UxîÊqÍ&MŒtM‡ yáy€«_MÝ«¤®*ÌÛy³» o•‰5Y`ô+…ÛÄa‡LîY–`µœ—ÃnûGiy²J6¨â›@¬8É¢.3ÂjÜ(™ R&XSËàÀi—á]ATæñ3dYÊ$n\Ã{ë¾Àn›6H×PsØ5lìý’¾}ך¸F?[Å[™H:‚€BË`Ö§¤`%s(9qÁ¢ÿÈ]øÌl1=Î/†áë!¨·r´5¥Ÿ?ãƒíeLJJsÈ‹¤8lŽM ’:¡üp:,&·Ò¬M2»šìŠö‡Ç톼Fé1<öÝÍ10±Þä“'YíÀÑÁÊhíê_S÷ïM¿ 8»£Æšýè›EDPqq¸¼Xß +ö¨äMŒ¹= U)8a—út¤ŽqMF¡À©h¯ãõ|Â``B*Ù¯31¢àÃiÖ [\ãâ~³þìÐntó'–‘Á·Q*¡m ½}À×ÌËÜãúêDF Ð[lÛdw|M|ùª’® àµj¥éYÕ$´7€ŒÀ“P5¼ÃH ƒ9¨-Ë`?ó›„ÿÓxoü²(>;Ö)?ÆÔ“ìÚ2¨îÆ`σ:yÒÕ0|ö'lM,BÛ~ lÖ¡ M`¶ý´O/|»›Ì怤³J`R¬°¾ú+¸RüM´á°Û%á Zúáû‡&‡2ñK6œÆ»Ï=³õ„ÅUí¼{6Û™ùñ²Mä\Ï2Egåã%Ș¼„2BÿK›“?Ït=e‰Ú`çæt¾5«/º Íz){IFðáhx YýŠ€A´h€«\®þÀq>‡òÔKodƒ]e±lù,jѤ?&mÜW ^¤w%®Gq±eÑ‘ñh‚cÇÉ–€Âbõ ÕP¬T‹V«¼BÜ„‰û ”Õ Í–.¬SV½Œò±P(¤þ?À$dBÒ.îÑn–ñο̇|ƃH‹eÐN¬Êµ§@Þe™ÎçU¼7òñFLX‚•ß8|‚6…ÜÈX/{åîâh iÛZ©J~…‚ŽÊ,¤•—í|Ù5­©',QLÊÕ ×£]ïB»¼‚oŽ­›6Ç÷úz¬¥yßÒ,ȾÿBâoŠªÞö?€x·@ÏèßP}ñÚBƒã¦æ–æáÓ5/¾ga[³b¹w˯-¾éTn#'©=ÝÃz뙵œð­^L-üÍáú•/”e&üV‚‰fÜ ²?»s–¼UIGðôIq‚@õ!À¡âoªj¦Sæû‰!íÚ5j”!W}ñRN8ßãi>s¦,^†‡§½”l‰d¦OŸnš»igKÕ¡ÛâlsµôY" =<˜ÒÂaQÃchü÷mµê(&¸ÑùZ‹` ôĈdzå*qaêŽÒŒ•UvÞÂpPAdwÓaÇc_YáªâÏßÁë¶îÁ ¥š•‹ß{.q;®i²,ÊéSù;Ad55«¦‹Þ›8aë{¹j!4juɳN €U¬‚-—­X5·W,¦Î¥ìý׉zJ%üÑI“®Ào¿€;3 ZH£Ë*¶p^c®!si¦MLü¢¬pâ/T²uR]HK>uèDÌ  ‘Qçš×ï*„í¨nz¡Ô3VuK+$t¥zéþ&¨üŠüõƒF"Ô.ƽùf`~zÖåø_Bɪ…^¨]5ÒÖJøØwHÂBZ䱄äI+(®Ñj×m´¬“ÛA 7rÌV’ŠÀ¶ÈSøìØ$½ !Ð< àkñ=b7…š‚ÀhÉß$ü~6[Üþ®õÁ¬âˆr#Ô BhÔ ø’uíB`¬5µlZLÀ ßg7•ø`ÚÄ ÅÆŸjWm¤´µ1ÖÉ­œvç?ÀÚh ”Hƒ2Aƒf´GÅõvJòîÚZ7)· ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ Ô.²¾ˇŽÕ¦RÃåù Ö5®Me–² ‚€ »ü£¤uSZÚíMXŒ¯èÕUè:à|2[ÆùêÄOoÀjµÎ5§ÙeÁèä»ÓR’÷Fš’† ÔäPµúÓÖRÓB !i⥠2Öi¤ôÄd3Ñ*ªi€¢¨OÂ|t'Ín[;úÙÔ‹k¨xçÌÖjf×Tz\Q,Ÿ3°AÀ áh¸"‚€7Ð`;’±ES´ã¡æ°¯[;åšþÖwÂìö“¿‘F±±–­ÖQ®ïå^ÚŽ€œÞZÛ[PÊïפŸNÀY­TUMp'2¸à|ðÊí”iŽm ðzp4Ö©©IºWaßÑÒîý”ä+ù]‚ujÙŽ¾„<.Ū¡)×ZkRÔ¤÷&NXài.ŶÍ8i¶Ài£g~¨¢h߀£ÒÛdQî˜bMÜP"¼uR'Íæü i%NI™ð3ÇÇÙ]Ÿ¼?1é]#ܹò4ñuäÓÛ-×qøu˜rN˜x‘‚BïîOšø‘zäý”ć ?ùÚ€lÔþ6”ø1˜EÏÃVÉÑ©/$þQV1§¥&-Äñ›qXÛEaÖ"Þÿ>j}-Ê5ÎS/½„cé~tÚµìŸ0uªE³]„Û;p–çϘ³ÿ ÎHˆÝ霛ðlê0ãâºbÛf$ˆŒ·CU•ÇMŠé?¸oç°;ït Wxkw܉ðœaªA°tE¾MpÉŒîEøk³¾ÚЈÇÓ!Ýa¨ë…£Ÿ l Ý8ë› „nT5má'¿‚€ P7ŽFÝhG©…ÿ"МžpËw ÂhÔ¶(ÐÇàp$çÚ³nÄ󌈧2l7À?ØD³ŸvàèýøÛËlQ{¿kM\Sî?÷'¥ì!§ó%<OäzxM&eä”’f…¥„¤”oÒnÓ4m¼+wò$·ÃÆ´gž9m„uý­PÞŠi¶ætªÙö¼‹wÇ·Û´ë@x­ˆÓG¯—~úm¾3s0êbÒTK™Ç'µáhÔ¾6“×&4¥)¶:ÒÏ]d혢P ‡ÃVÃNLú ±­qG‰x ÝŽ0ËÞKyz+ûcÓá6ü]ãBdèÁÁ™‰›¾,¢{ýÿŸS^H,&2Ø[Q•Á]ˆ“”Љ¾ÐNž44­5$ 1ü]+’÷´‰Ï¬Gy+äjÄq >Ü‹¹ä$&4 Ó9ŒÃrÃK~A n ºÑŽR E@¡˜°uâEĶ„v 8Œ¢`’wN“’Ò|JRÒÁGRS›æähcËã!# ¸m@<C¶a“áÇ¿àNðV…ê8‘Ͷ:v°ŸîÍàz>£N˜›æLÝÏ xÎçˆd(Ò.îÑñ©Å!KÞT4o¤û'Rʱ¬/Æk6[o“BO8Í çч’&¶~;%y7aÈS¸%a–'A N „FhF©„¿"€Ét&Ðk0yƒIqFðѽ¼xÝ +úß ÿ@Søô|[Æ›Î<åVøý;7O»‹] 1A`²ÐA`4i¯€Àæç†Ÿþ‹€àU=H9êêü³]ŸùÞjUœôü„ÃX«u:„0£m‹FÓñá¨Q£îáçŠæÿÁD Ëi䨳¯Õ:ÑÌ<ÍQõ m[fBWCå“{V/`𺑾ü ‚@ÝA@ºÓ–R?D@U´…NîIHž|5Š÷ciE„ Kœ'¶XÔyÆû·¬g€ør:¡¡9é6Ð?¾7~üI# ¶dv`ÂÏ’’ø‘áW™_‹ÙôIÍ‘˜æÜ~‰JÛíà@D+&8*e»Šæ­ÓlG®¦dÙókq5¶M~f»œ281¿‘S¹4[Ë݃zªK€p4ʆ\Þµ‘ѨµM'¯ ĘaÂV¶AN!¥4Þ¬Y¢9´IØÙrI÷öî“ûÇà4ô˜aJ |}äZgs0á_ÂVG]ý=½Ç:a¸)KPŽ›œmîWLµN(±ãžfEóæm0W6«ä"c0¶~¾3Ò2iÊЮ¹HÑtùŒõïZŸ:l¼“_A@¨;G£î´¥ÔÄàÕ;TMǃ¾%[Þp)&c‹ào³z!ê•mÏ~ ;*áXí_ï¾UkNšfK=bâmLì‡/îÑnV ™‰0Kª’e»Ë¦ÐŸ£“&=Cd†Zl~kˆgœù„J­@ò1ÊñBQxã·ìèå­Îv’ó.ÔÁR¼=d ý[&Á 6î&Eù¨ìÌä Ôf„£Q›[OÊ^+˜öBâ\l!tGa—âz܉EPó\¹ç!ð’‚ÕlKý2,?ûŸB΃ã~îNˆ°ê©¤ôCz5Íù™SË߆°³4rz|Iˆ9ì+Lø¸¢BÍ¡_º—ÅýÙ“¼UUû[#, :óµÇÏ5ÒbfߘÏïLŠRL€ïåWêXdˆêD`ܤIÑ0L¥¾‘˜xÄ[ùNŸ>Ý4{ÝÖV ZrF´oŸîN”x+ŸÒҩɼK+ø ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ à{ä˜xßc,9Õ÷k×¾íz_Ýe©¯ùi.ç{×g—Wr+Ô}jË$gÕ¾E×AN½ªaé±þ¡¢pÆeø}ÜøõÇò×µ2ýÍèküët¹ ÿºVo© P*þ:øƒ¤zïÉ}U“é2EUâIUbÂK­‰x–‹€¦P&9µCšSÛçt8f}ø¯‰«?ôÊEί_r?aÂÂTt™o¾\ŸˆˆªªÆ+&µ™BŠô—jB´LÍá<ìt:÷åddÌþúý·V£(v\Ž¢Ëè5TBÉV¨üÐÐÎk®ùŸÆã´˜q:µfŠ¢há!AΨˆ%ÈbáUœ‡äÙlÎS9ZfNžªiš¢ªÊa[ýc›÷½óãä 9ô<Ä´†ƒ†¥G Âz ¿hlHXØX´mSé/5Ü2EÙ»÷9´Ë‘œ¬¬÷ÖÍ™ÿÞºu‹³̆‹‰î{â:‹€?úÀy×cO ŸêÔ´˜níâ´þ]Û(ÝÛ5§ À:ÛÕY±œ¼|Z¿ã ­Ø¸KÛ°ãEÊ¡œìÌÑŸ¾öÒL”C½êlŒÊåep1̈póèq—5Œnò–S£fÒ_*¨¯cÝçèð‰£é㾞úÖ,ä]€‹¹Bèûº!$ýCÀâóž§’µX'Å5m Ýréù¦öñÍj ˜úñö}‡é¿³–9¦ŸTl¶ü ½œò:ê-ƒžÿ6¾ÑW,(bà<ùHHXÄsÒ_ü·ÁÜKÆ}îË™KiGOQNVÆóŸ¿ñÊ“‹¹Bl¸&ÏuÞÛ­IWw,#wH|»ë·¬Yµå58BløkãI¹*…@MúÀ A¶Èv½züܼiƒæd‘Q©v¬r¤níZ(k·íÓìš2Ôv*ÿÃ#Gö3+Wˆ*#땘óÇ2­[wnÐã‚ ¿Áv‰ô¯@[s‰}N3 >qàðg§Ncy ƒØ¨¹‚I΂€—¨)ÖœLät6øAdc™ŒKMÒ=^F¶–%ÇØß6r€ÊmÁmÂmƒ‹„ÛJ\Í"ÀýT'4λ|äPM¥¿Ôlƒx#w£Ïq{r»"M–xçv®©qÙÕ’4³¨©ºxà ÃÒò"øyVÛT»··· 2—A¯Ú[ Ô ‹‰r¼ ŠhÐ𥿔ŠS­ô4ú·+·/.!ðkeKJ¡ËC &âóò[îî¯iÔ„UXË+¤¼«>¸-¸M¸m« zÕ}Y9ýÅrñu£ú!P´ô—² ªþEí]Ô¾,‡#œÄÚÙ”Rê2¨IBÃÕ¸ñ6.Äv2ÄùÜÜ&Ü6(‘ z5ß,ÜGyâ hÔ,f˜ô—šoo—ÀèsܾÜθ¸½kblövÕ$=A@G &>æâS5™ãÃ51Æå?_#·· · J%ƒ^Í7 ÷Þ·0[,qÒ_j¾A¼]£Ïqûr;ã9 oƒ,éÕ(5Ehè§j65Š ­Ñm“üÁÌy•Ááp’ÃÉãµßq›pÛ &2èÕlsrß0¶N̪Ù\ãýÅ8róY‰âÜfÓÉng£´õ×ö93÷9­“ëokHͽÔÕéJœŠ†³K¼Þ™ßù–ŽŸÊÔëÅ«…¸& è–ËΧæøeW`sÐo ×Ðâu;étVYÌfŠkEw_9ˆb¢£hß¡ã4郟ô°f³‰¢£Âé² ºÓ€îmu?÷?6»žxí+ 0ÓKâ­÷ ^}¶;´y÷!êÞŽ@ÞwÜ&*Ú)»zU§È¼_ܺž¢Á„æ·ê‹þR>2᯦øfŠ“`¢ýë?–Ó’õ;(/ßFÑw.ìÝ.Gÿ)Ë}ðÃ6‰Oß9’:´ô­5`_÷²êx.nWn_„c"C¶NΘ¼¯UT7¡Áàœ0Óà³ùžíiXÿ.”~"ƒ¾Ÿ»Š~ük5½ùb½q¦}7‡vìO§†÷£Î­cèTfí:xŒš4*iô¾ë‡Php -Z³fƒ—PïŽñ &Xl¡¤[·ý€ŽWp[öBš±%xùiãÎ4š1o•ÏÂâêm#ƒž—ÛÎÃä œM|ž‡ñ«=8ë#~¨÷µæ Ú¢ïü0o5×µ5±ULw—_`§µÛö‚ÐoHË7ìò9¡Q=}ǽ–{.j_îs®í.Ä}Åà“P~Œ@u®ȧgxh¯úµ'í(¸;ôf`B`»nX_º¨OGÝ/ºA•¦^ËfÐyµפ!­Ü´›VoÙ “ÝíÎjÎåvR·¶Í)+8,] Åk·Ó¿·Ñô¤s;®Ôƒ.ìÕ– L¿.\GY9yÔ¥M,Ýqù@ a³vÛ~úkÕêÑ>Ž~_²‚hh¿NzywH§/~[¢ÇI|ûêŠ|oGs7Jsk¶í£‹…®Ô“>ûe1Ûr!ÃhßþÃ'hæâu´iWÂ˜É +¶-ÐÇÜ4Œ`É–>ûu1í?rœ¸ŸÞ6r µií}§´zzàWëÚÛƒºIÐzŠ@MÈhTKG:|<ƒÖlÝK³–¬§e`ËòDÍnwÚ1ý·wÇ–úoEþì=|\À" %]Nn¾N¸ôìЂzãÁÄo¥°ãwŸƒ(¸t`7šøÀº™MÃDÆipQ>þi! êÝžÆÜ<œp°ÍA®ÀfÃàz–®ßE78ZÆ4¢¯~_¦Ö"C©]‹&X5†Ò=× צ³ÇGª¥­|Töº”¬Ñµ¦N»¥ù«·Ò3Ô7‹·-Ý+À„yÏö-@¨Ç‘ÝaÇÉŠƒü®“Çݬâüâþ†èï?ýeYè‘Û/£æÑ tŽ#¿ð£¾£—³j][W¢Ž¥ž!PÝ ^Ÿw&ž¨y°ËÈÎÅŠ'\ç8pæ§2²)*<”šm“|ôãÚˆ»«÷¤!} ~þiþß  hˆ“xLöÝÛŸ­†»ró €lF'l—Øí…¡¼•Ò·s+Úî ‰vÁªŽLf%çUÌŽÔ† út*$x`„‰þ‘2Òe/ûÚ¡½uî¯Ô˜û±uïab‚¦â¥;U*FOÜ{|ÞNÞ+ª¤äO0}àÈ ]FãªÁmJ-ZfvdêÛ,l%³K›8Z¾q§N°s„­{€[7= ¾õ¦X8ì#È1P6ø]èÛcnNá!4\Æ×?ŸU,—Åqý ïp1Ä ‚¨)BÃçàÇJŸ·GX«äë?Vп?E¯þóVŠ ¦œ¼¼âüyêØ*Ç¥/¥|[!`¼diøNx7êRæ*46¼KüòŠŒà^ýt¦î-j}û„ ֱьmof¯Ð÷ ·ï?Bãn½D·ywQsiò¿œI§uÇœ va¡l¤gIƒÓ!®^!àöEÔžº'ÝwÎÑ[-Ç·þ;œ‡KKl)rMVnÞ­÷Ÿç¯¡™‹ÖÑI,2°ÈÛ,L\tiCsWnÖ·™;Â}•ÝÖ=‡‰û&s ÇBßÒO:jyß©µí_Ür#ê&4ª½ã°ðf7¡Z¸f›¾=ÑBg¬u²qçÈ7Äé„ L ¸»k†ô)!QïþþÆ X…¹ ì"‚ˆ9¼mÂr"ƒzµ§mûŽP 8ßv)µk¢‡c¹ Ž“òàúsi˜ âG®ÚÛÎê^“EaÜ /­v;ÃòFƒ{w¤oÿ\I[@X»Ê.1 L¤·Šm\ìÏÄou®Þ²‡.€0÷È zÐ{ßÌ¡màä]2 „Ÿ[èíXØ/ñœFWÇÚ+ìü¬ï¸±ìûÂö=ÓÞe‡”7‚@­B &f²3ɇgز,LvìT–>x1ÁÑ´a$¶h”5¤^D8˜J ËÉ+¨” ŒwëZ(w^q±,;f¯Ùú­Â6sWnœE?jíÈÐ⃷@øùsù&„ë s^ÒOf@þ‚UéËwÍEbÿ{‹¾ä•5c¼îdÐó:¤u1A–-r84½j¼ýÍ«4KÈBI‹ ]N!k•¸º£øÖyk“;{¹ÈK± ÷+&4æ¬ØLí@˜3±`‚zakÓ¼ …!Ÿ_­¥›GôAbþpñö¨k>î÷ÕÒwÜ3•gA ž#P„Fµ@ÎÚ|1WÍn€T(Ìùð­—ê[%ÌzåAs¢£Jª·ž« ¬m‚l‘Áá9?ÞnáÕÚ@p: AN¶­Á[,¼{úž+¡QÒûÈ} vû·nw€ß_ŠU[E &RXmpü[_ë\™q·Ž8WQå½ à\·/b!”ùðm#ôíŽOYáN¶ÓõP#gPW·¼ˆHç¾éêúC ösh“°}¶{Ã+¬…ÅÆð˜¸`„ûÈè‡Ò§ÐRa›9ìXó¤ë˜\“*õ^úN©°ˆ§ àS˜»PŽ—$Á¸xFot÷cã?ëÞ©MÇïYe(‘W&„E™Ûá V+"Âf2ÝŽó:#bÏMù^bs5¶Å\– 7› Wl% XÎoÏ0ñäma,o²~Ë®uŸ¼6ùNdÏ*7¸rqÕoó šßõ—ŠÖŸ·Ax‹’¿ûʸµP{eÒg@”7„ý –µšúí<Š„ŒÕ?®Tœ¤a}”e¡Uºü¤išòàó/¶u:mÉéŒw(J<9©1)©hAŠF¤˜E1ia@³á7˜`òÁEÊ1UÓÒIUÒ1íUTuû°.m÷Ž5ÊQ—qó¤nµjVÆ¡s/>ÿm)ÌȦŽ-›ÑÈ]©S«¦àI½ëdØÜüÚ²çý½u?}?gÍ[¹…î¸|€Îå@'©“u–JU éSåã'}ª||jãÛéÓ§›f¯Ý6PQ´+HQ/<©—¦9C‹ë¢‘à²PH`  bøÄªÿ-⤻ݙ_àÐòò ´ÌÜ|œt9‹xé'Í^·Í6:)e=¦¬…Š¢.ÐÌ~f ¢¤~ºZChpãþ¹|}‡ ´Y£sÃEÔºyãúÙjeÔš‰­Þ[è×îƒÇhÆ_kéésè†á}éâ󺪊HNÐÕKoéSçnvéSçÆ¨¶„xÌújÃl{îØ9ë·?Z"dƒÖ*¦‘Ö2¦±Û¤Å4ޤ†¡¢˜Tõ\sc‰ÁÔnwÐilÝ=•EÇNfÒ¡c'-ûŸè½÷ðñžö‡Uû±Ü„¤”oÌfÓ ïZÇï(3æ°<öüë‘v{Vƒ³Ú@q(*)drÚø ¶h'_Ÿ0Üð[j‰;˜5^ ^qñ€8{ùF¬ÒWSvqtóˆ¾`1ÕxÙü¹L„=xóPúzö*úöÏ• ¶4â¼®:±!Ü n9ß—MúTå0–>U9Üü!ÖýIGä:r¿ÀÜܨ{ûÊ€îí¨së%0ÀâV/j®_Ô*ƨ²âp:M»¥5[÷/^»ã6p@nKHšøð´”ä÷8ÐØ¤—:‚?2È©(]@LtÓ¤Ûèä”L{…„ŒÍ©Ë:9°þû56¢1É©v\‡œš¶6)ª²J%uUSSÛõVë¨îOί c@\¿}?͘û·NdÜqùyþ„Ÿ_—…‰1¯ßHǯiƒB'bï[Í·…“>U5|¥OU ¿ªÆ~ 1¥…]Ñ:kЇÕSd$â°®ÂÒÞ "ÂÄ¿œ¨‡S˜«ã÷8±e¡¥4oÒÀ|ï5ƒÕ˜è¨ª£ÂñÁ¡v-šê×5Cú˜¿œ¹„–¬ÛùŸáxw¡ÝQ S%fÕÙ¬Q”Ò¬q”Ò¤A8Rhp…Ù¤’á‘Ãé ;¶e²róétF¶ùdfN‹ÙÍ÷9107¯ Á J$MÛ–´ÿD½6kÊÏï¦&í¯pa}Я ædäæÐ_®o—0'Cœç0né`ç1Žíâ Ï‚ØdŽçHÖþÒ§¼Ó†Ò§¼ƒã¹RÁê¿-‘ziÚ@ÕDƒm­‰¾Ä‡–·ÂBaP0™ÕyMÞÆ*_ËÊ-Ðrò 0|4¥ad˜ãÑÛ/UC‚Ï•¥ÏÞ3‘zÛÈ´vûHs8¯ïÒ*ÖÔ¹u,ulÕŒ¢D”ØŠñ z¼ã§³h/40w8¸vÛþKžÌ¼ú—ïŽNN]FÈÛJ\ão§ ^HÍ8¿%4x@ä}¯9+7éš%·^r‘l—Tòáüº!=iÊwóuZÒM#Î3§ŸÈ€RÀ^Z¸fÛ€£'2/T=–˜òïXKó×­Ö{ó¼–qªyäK)(°ˆ Ôv]»D?KÊ/ƵtÏa};oŸˆ¼† Öâ Ò§¼ßxÒ§¼‹©ÕúaÐAÛÁ‡@d$jN-ªE³ÎþÝÚRÿέ•¨ˆJ±`y|×û­bjæjà&7iA¼ ÄeÚ´ë k 6Z·}ÿäÃŽ´Ap<>-5éë*Vãè~Ih®¼ìÄB4ÙytÅ…-<ª”.ÖHÙº÷°Žkû–1 6ÙB)ª:ç+}Ê7M*}Ê;¸ŽMž4ø#íKp/šwoÛB»ú¢Þà\4¬ìv‚w UGRéÒ¦9áR¶ï;L_ÿ±<Ú0ÓïOJùOss‡ªKpÔï²xåe³ÑæÝi:{‰ídÔF—Ãb;õ›¢3ŽÌ®c\ €/soquºÔ§ü­¥¤OU½EFc…íМs7köÔ=WÒ£.V@dT=aI¡íã›Ñøÿ¹F½jp/Ðsô?‡ÛÿJ°N )ÈG~ÇÑ(dÃDÈ.,èãmc\¿-Þ@óVmÓ!å´ãšDÑÕõ ¦`7yÓmÛw„¾œµ‚žO¸š,PªiÇue<WÆ7Àb!V˒퓚nßæ_}Ê·5ðßÔ¥OU­m ìi…ðæsý:·¦»®¼²~7%U­‚~›šW]Ô‹š7i¨Lûnîù¤{E¼Ã×Åô;޳x‡~fÉéì\Š€Yq_¸È°`zìö‹éº¡=õ3RþX¶ÙëÙtoÛœ¬÷_åD†Q9Æ“qå3agÆ[\ÝF ºúTÝF±ìÚIŸ*›òÞ$$§Þ†êsöê@÷]?ÄkDsj7ì<»K+iáßÛèèÉŒòŠA‰ï|Kk·íÓÃpØW>þ•~ùSZµyO¹ñ¼ñ’¹Ëÿ´ÐIy”FïNñtýð¾ŠætÞ>æÙÔË<Š\‰À~G>:¡øc/"4`Q ª˜¾!4X¿¹Y£Hý‚ ý´`=åaò Âe«6ï¥?Wl¥è+·oJ7 ë½æBçe½Û´û­Ø¸‡ÎïÖ†~œ¿–:Ce©m‹hýþÉ;/Õå!^úd]5¨4?¶ÀŠ\.1!2ò‚®zžØfù}é&Ú°+rómàâX  ®Ð7¡ˆPïaÀgÁäâL&4gÆ[\ÝF ºúTÝF±ìÚIŸ*›²ÞŒ›4)º Gû¿­bœ·_>À« Þ™KÖÓ²õ;©_—Ö´q×VweõÑ²Š¢Û§0v—mØEAàü¾öİÅáEµ2roŠ9¨g‡ø2ÞúÖ›­EÏZ¼Þ™WpršåËÜüŠÐ`¯ÁæµÛíÉc_Ö½8íýé§tÙ…¨?±ðétXÓ¼ü‚nÔ¢iš1o -Û°›†öëXî;&vH‡éÙÓÔ B—]ÛÄê['3rtµoΌᅴ»š†÷ïDز˜1o-Åc/²¨ËuÛÐúiôਡ´ÿð úä—¥4ö&ïF…WÆ—9æ²}b S·~öå¶®Î>U·PéÑ¡õïÚã'aìÞOË7ì¤6°Q4°GÙ ÄŸ›(¶ICÚŽE6¬§7TO;Y ýaÞjÌ/t£\;÷Áœ¯ÅvÔËÈ Þ˱(m k£|šò_«·R8´g.ìÕ¾\¢©DÏñÀ¸·hÚPE½{œ#h•_{·…«\[)Ú:1&A/$YjÜøÿ…üÄ+ŸÎÒ9Cu•O‡’¢8ý´{»Xœ‚¬Sa€]yïø},Ñ3‡‚zc"¥4×Àƒz¶£óº¶¦æ°PÇrìvâl’œá+rMt?&^|á bN¶N|®ÿ¥Y]}Êÿj^}%’>åÖ˜ŸïÇA)ÓàYŠgB3ñ#N±^€m˜/~ñáóõI{H¿NôÛ¢u´jËžâw|Ãã~4¬rÆ5¢!˜\Ý©Ìlúà‡ù4ÄK?ŒÝ¡E†¿æàñ™‹×b!Ú‰ö¦…æÕz´=8MûýïæQ8×LD˜Í*Í'›]&³3¯§f#èØ©LÚ¼§p~á´¦ÿ±DL<ÅÇ4aDŽœ¤7¾ü]'ŠØØØÛ_ÍÖÓðÆÞb:t,á›1÷F‚å¤á— ¦uˆb^@95¨Â+¦]™êíܺE†ë)mßW¨RûÖWóΤ\ÄA+︶LÊsü.ªALtÀÍ÷0³¾5sE¸<í‹#¼·~Á7*Ä8+^áhx ]ÿJÇhßêêSþUûê+ô©Šc`}1^³Ù¢ÚaÌó…»bPOâ-‰¯g/ç­sÓpb»±`¼äü.àŸÔ‘«6í¦þØ^1=Ì5Áv}Û¸&°´y \élýu÷vÍuââÏeÁiè©¿çkÁ=i ®Ò ÙÛØm@IDAT ÔÇo¶þÉŽ·æïºêBý>2,„&ð3¶Å håæÝÔ„DltÝÔ€p –Îúöû?¤FG`€KUTÈ›dêÄG\ Y#^E~ük5¾†òJEÂW%Œ_\‘⑉ ü÷•cŠtT)&ÍÍÄ‚¢Oÿãlù˜òÞå<—1Þž)ÍukKsVl¡íûÓ‰­ß=€­ŸMþ:´…ÛTŒ·¸º@uõ©ºâ9j'}êy­½ :<ÎúÊõÅÖHOœë4 \…sWÒW\H6l'XwnwNEƒ‹·‚ÃÍŽ-n>?æzú„Ë[ÿýƒA€õ¦ý çPN|e×02‚®ÔKßNáç–1gTtqÎ 4=èÄÄʻ邞í9H ‡sKtbÅÕ“Ó7ÁÞ;žn¼ø<È B~¯ Ž-nõû2ã"㽩©Ió«\…¢–>ëU(ªï}—GY)wm£ u.\»Ôn+ãpGÿòä_Þ»²Ò«¨?o¡ä€Ú½Û*,\fŸV4¾'ḓ{OÂÖ^Œö²Ò7m(}ªâ¸¨{ó°Í|ôdVÅ#y’-av€dñÃ鬦1¢s!`û2rõÔ²rÊ·ÄÍÂ’|±cNsŸ¯Ö—ƒ;Â2ìúaŽH;zœñö0`ÖŠgCˆ¥¹ÛW€È8~‚ê2⬠=@1—… ¤Ü<ñ–ÓÿäçƒÔ¯sK>¾^OŸ¹#•q¼K°²$3¯sà({ðG”禤$¦‚ШLrÅñKBãx90ŽÖe,~_²‰~š¿N§"‡ôi¯彫j1ØN= +½?cN0å=¤Oºl@á‡^Õô%¾ àmÜíÑÄFGÒÕƒ{PLãÈ*guìT¶®Õ rKÞrL̹¬ÞØ3¿vsÄÕ oM˜ptô³)[–mØÑaÄù8ÝËŽå3BÀŸO=mß„n^x's X6ãé7¿ÒOA½nhº¨OIYŒ²Šrßã[_ýA&p˜ ¸vhašCûv‚ÆâJ~ï;};š­pº ‘i²\Þ7¬Ð¹#¥-$‡aÛä‡y«P¾ézùÆß{u‚ðè=ÚÑ+Ÿü¦û1w#åÁ›tå#ÝŠü´oç9°U‚*('@ Ý cfu\>¿"4xÕUŽ5Jø*Ë Ã¶_Y9ùPu‚Q+|°†+ë]/H!óåêÜý^wƒëkºçªú³ ¬¬)ßüE7]ÜG§f‡¥P³š¹x#]Š=E|Û>uÕ…»O+!‰—Š€¯Û–Ùß÷\uN΀Jøšmã».µ@ôœ½|$îÃôS›+¥Ü`l …ñ̦êò|{¹•­/qâù$h×}òת-¼ìäÕ?ƒ šUº¹ ˜ƒa¸ÖÍ£Ù꨾ ã1Ýx÷â¸QFº÷Ú‹Šï]oX&⥇Géq]Š1—áÎ+.ЉbΓ‰v¥¥ÃÚ(o?Ú¤gÛá‹]“†átÿ CuÎ k„å»zHoâ‹Í0”F œI­ì;æÆXp¸&;àIeŒ¥}‹ƒmßù' ËŽé7^ëu É)©¨À«ð¤h¬R4&õ#=І£íÄ–ŸOy¹9A ÷¿e'€ì¦]‡ô•¡¯ˆ î„Æåiy%|I*ûí—L¥bO®ý¥"1|ݧx@dN_lÿ凿ÖÀ¶aÌ >2uõp–œg‚dÄya3 N/6k|…Ð5õÔŸYîå[èÉ».Õe•p”¶>h¯„M›ë`ɰ¶=±aÖ~]Ýáã§éþëë*å®þÞº?vø SgPPpYÉŒ GsÚÒãþĉë<‰S{ÃbÌÿÿÎZª·ó@¬Ú½é ·/J_¡¹ žæYV\]6ÏKÆŸ bŽl•%28>Ž>åMlj7´c6ï>Ń[²rro“nÞGW×ûbÖr¥ÍMœ/„µá˜89Á7v¬yÝ`Ðî–ýp¸Vƒb6çumSÕˆ ¶oÃŽóf63æýM=Ú7×íØè/\þÜzi5Ð@7Îäâ-·5ˆkê°c˘S¾™£ÛªÁâÔ‹¬YE¶kÛ8]õåGF™nFa¡½ìçZlg]ë+¼ÆÑð”“á« ÕÖtY½•/qµ¹xµ°†e™-Üþû³?è8$æCAÜyE!q¼'í88sÙ”.BÛ¸h“Áê…+À¡h޳…Ês1£°Åa‚]ƒ`â£ØÙ-‡a߆Ÿ;BœíÛ°!=vlÃæÆá]ÏÚ¾Ô_ºýQ|©Îæ–—<–«º[·m¿nœí w£}‹¦ºðcù±åmU`®O™vikzïë9ÊöýG>cÜmŠuüžª¤[Z\ï.©¿Ÿš\:ÏŠˆ™K¬ÓÄ^î~lügÝ;µéñø]#õØl@ŠÍcgdfÑñ'iúœµº¿üj å|û•©B¹ý¥" úºOA2þÂ^m1IÀVTúŠNÝd »ìb]Cy{ÅàZ¸—ý\gîxÆ{žÞ|Æ!U4êÖaÔ¨aŠÓN0Xª¨{õÓ™´~Ë®uŸ¼6ùNÄ9Ž+W..®:ïØ$y^ŽvXú✒æ\á¨È0ÇÈÝL,p)Îw°"ΚQŸ}ï» ÜÛä”àíÜ|Bhx»’ž ø'8xÎ÷iG¯~>[—£è‹URÃÈBÙªt¨†ÄÇ1¨3vlYȵcéý|ly.í8Ï«e;oذ);uySÓ°& Ê09áÙÔuݘŠm•æÙP?e9²Êºã°º¹gžô‚¥Í¸¦ õdròòi„‚Ù¨WM¹Åk·ÓüÕÛè™{¯›¥Zœ¾ ¸zÉÑà†þnÎß´i÷aÊÎË£f0Àr1$âÙ̬·GùóÜ|q?PÔ…’öÞJ[ÒüÖ*YµeÍ‚ šë†ö‚= `p¥~žPTë.Æa‚]@0°ã>±v-^ûòÈvá0ªÖP?ÝZ\­=Ú¢ãßôÜÔчú膋ø ¡ÒìÛG’›Z‰@BRê?vçK¡ÁAÚuÃú€`me¦²vß=«"[ãdäÙËa*­¹»SÐð›ÿ÷V}KïÔjÙù8ÀlíÖýºÑ¬øf(gR±q¯çwÕœÿ~î*ØZê¡ 8/\³M·oÁÛ;|øÙ"p+Xuö¢>tNç÷ÅoK`.¡3NçÞ Ûcr5—Àùûç ºbpOhž‘]Z¼nµŠ‰†UÐêá°ÕÔûÒíN§²Ä#o<×KBc„ÈVoÛGÿ¸r ¬ÆCÝç˜n—Þ€i°ôs÷]U¬WmøË¯ PWp·GÃ{½ÏÝUqõxà|ôö:W‚5«\UµÂlóè.Ò¹üŽ*†ë Î%yàŽj}žØ°1Òqÿ}ä֋ݽä¹4ñu§æ|ä¼®m´Û. ãˆvo:Þy¹gðgý:·*‘4/8'þßtÔ§[ÇFë–=?úzýDmÖšê[|ÂêñÓ™0¤µI'4Ö@hu×Á£Ø"Ü£óWm%>¤U¸_ý|&ì>T»Hœî$ÖŠ€Z÷ºûYT·4Ý'¼î=Ä»D¤/¯}1S7GîJdð;Ã"é;^ÈÆ³Ø"蘱eÉw6 ΖAÙÍ\²‘ ¨DùùvýPœ‡÷ÖÁÒüyÐ}õ‹Ù°”ØGÆÇÀξƒ~Y¸ñÓt¡¹žšÓvשßMø—¬Û…µŽÞ³¹f¬ðÚbï»uUÚVâ ~«»–å "£´÷L˜D†ë{oÚ°qMWî«ûSî‚ ¡G˜ÓpÃÅý¼ÃÂp«‚ ÝÛÅA¥3ÛËhÔ¥ç‡`ŽÙâ^Ùæ /:Ùn ŸäÍ.¾YcÈv4ÐOteûFGa”nÃŽ ˆ»áð´ítÙÀnØÞ;NÝ )ÃǸC{C?ã²Íê-{ŠŸ™h¹F·Ø1¡Á6DÞþêOĉC:Ýuÿšúöyf-]”§YWû¢Õ×ñEÉ«f/áÓð¾„^ÿÉ¢ÓùŒä–m܃ãàãÀµþúI¿.Z¯ëwó~ð¡CÓÔæˆG}•·ß|BqùªÒ­bÑ}×  ì½òù0µ¾Xºy ¸ l0(ºA(>®FºYØõ;Òˆ”g6-@åúûeù»—÷®y_šß᫨l¦ž]Ýe»èr"|¾ Ëx0‹ÎWÎÀ˜ÅÕm¤­«§}çŠãü€õ0Íél×»s«j™ƒøPµ«/ênõšâBòáe¬ À§¬²ŒËqPSæs¦x '#»Ð€Gè ãq ¡ÂÚUœÀݹU,dŠ6è\¡ü]›Môí–¹`ÎlRP/œøZ–k5ðÞ}9ˆ …¾½²¬`>õ_ƒ#îÿýÙ,Nsݤ]ò–õáòU¿ªPšji䊖Ï}Ò3›JªÀU4Š„kã ˜;Ü«¾]1ÿïº5Ã] $VCè­¯æéWfv>…ôr<Ô£®ÖêR»èÅfÑbH¿³+Ëßµ °ºF9"næbS 9l ðY*üÎpÌ%aÇGس3¸!úƒ—þðÁmM®Îw×wr_»po[_ö©ÚTåK/}Êsìì1æ|Ìçy,ÛP]މ‰† 8 ÇœŠ;¯¼€¾™½B?ÈlÂÛßègöðÚkä.¦~;^üð=8ÓOê{t)zæ_vC üÉ Y§}Oo|ñ¶Wº`ë¥$Cèò‡=a¿sÊZí¶èt æõ[–?ù­S¾™§ÙmŽU“yЉ‰%Õ¾¼œkÙ›§^Ψ2Éco÷hF¡áŸÊÄ?WÞf6¶bÈœ ÞÖ0£ñBM¯´C×Xf¢'Lÿ¾l ý9Ž–1u+‡¥ù³q"ñ”1ŸõpôD¦á¥‘Ì{×®ÈL)ûÚedåQt„w®|]fIß{øºOy¯¤µ'%éSž·Õ´Ñ£m I)_­Ú¼û®¡ý:©mpà™·]GLü/Œ½±8Y&º“î»¶ø™o· ¾X” ƒ0¿B#/ì ­BŽ/ Sï)ŽËg³¸žÏÂÄÉ] Zn‡à)sDøÙp®‡¶±ŸëAj£Âéõ'î0‚žõûà-#Îò«ŠÇV¨Ê~ñÛbû‘ãfì¼cn?Îjuæ,€ª$^NÜ’KÛrVç+nl¾BÑðYP?e½|oºÅëv‚µ•®ï³mþþ¡c§‹U‘Xhèop4vâ=S~‡aHèøé,= û11лè”V>ü†ã–æï^ÞŽ-›Ñ†ÝiºÍ€]°À÷ +RŽqd<WãêÌ_òª9ŒööUŸª¹šÕlÎÒ§*¿)Ôôbo{ãóYÎÍkÒ±Ð1÷WÇ*ÙÆ ª®þåÝsx·dÊ ^mïx!=囹ÚkŸÍäEî>ÐO—ãÔÖÑÕAdp%}¿„öJc@d©Ü؆a´~ßqg¡Þ[x˜RÙÁYHs”Ýè°`ÈöÞYuŽÝUÆüæÏÕôþ‹t¹ ¦rïÆù |èÚ”ƒ/aú  èa´§¬–æïnÑîZœRùßßWè¶5Xþ‚mù_=øl½î²K]õ7Œ#Ë™0®Œ¯uÕS–ü£}Ù§ü¹þ¾,›ô©Ê£ûÞøñ'RS‡ÛòœsÞøâ÷NÌ!¸ñâþ$ZE•ÇÔ5&d/ Z{€þZ¹Ù N† áÛÓ ƒþcŠý—Õz¯ï¶ \ QtïW„Fñ€Â’)ÃF‘!ª’9 Þ$4X³ƒ¯¼޳¶”€…å#þqÕ@]“ß³$±A¡²šª{ö+ÍŸëòÒ¸ŠÓ6›U8u¾.“ÁDˆÅ|æ„^àðe8ÞNqkøWõ—qd<Wƒò60¯jÚß?0Ú—¿a_ö)ÿ¬½ïK%}ªjOKL<4îÍ7{å§ŸN\¶~çø•›÷(ôh§[u•i«Z.õ'v&ÎbAÔõÛ¡^»u¯#?ßQõ¢¨ÿŽˆ˜ö¯'ŸÌ® 4üŠÐ`x‹#âˌɸeãP¨%¦ÝÐ1NrôPîD†kº<(B™®þeÅ)Ëß5.ß»Êd¸¿óå3ãÇ8vŒ‰Ðq50f¼ÅÕmª³OÕm$KÖNúTI<*ûôÖÃç#î³c“^úÜf·ýsÁêmwÿµjK`Û¸&ΞãÕžíã‹·¶+›GEã±P¶•­Øèæ³p¬°àïíЂ1SÔhÃC‚qö0L˜ƒ+ζ™XE—µ\X½†'GŽŸÖW°PÛÍ€‰¯¡OñE3Ó„yV«rFëÀg¥-;a¿"4ŒÕW!‘a¦€€êI{eÑŒ¿ÖЃ7ƒ¥Í3œ€²«%o\`.Æ/È¢êx2®fÈš0Îæ®áå¾î `´¯ô)ï¶©ô)ïâÉ©½—ò4Û OxÌúê3Ù¶œû@Èݶó@z¯ïþ\ID8:µlfjÛf¼£u!|þ¶«êØ k“ðy;ë¶íÓ¶BfOÓœJDXˆãÙû¯1…἞šrlLì³_—Ðò ;Ë-`p*ª) çÄü ÎÅ êócLWY­Ã 8Qr¹ñ«ã¥_\a6 Â9°¥]æ`l]tjJkfÐ×Ð7¾ãòó«—:•ãÆÊz6×ñd\_ÆÙÕ`Mª´T¦éSÅPxíFú”× <+¡×¬Ÿ€çË|³¾—ç°]südæ‹OgÄÙ"ú1¬f“Ù-`­I£sã¨l‡êè`ÈÔ± –­cÇ“5 õ³>PíTf.eÀÊç©Ìl§‡ŽRð^gëÂ&ÒVEѦ+dZ›•“óyê~¢Û/hbË¢ÕéX¶‚­ŠÎZ¼Þ‘†òxx¢ißÙU5–gÅ©¢6ŽM1å˜èP4µÛS]B•Å¡& ˆ#‚÷ƒ 'ØåäÙl|_Lšl^žƒ)(8ˆš7Ž S°g±nÇA¢ß–ÑÍ#ú g£-Ϋ.·V tOÆ•ñåUni['Cá¶á½¼â¶ª@vÄO>彆©lŸò^ êWJoYŸ>€¿[tÑXëä6 Îs8í}ÒOf´…éðv ¥µÆy)áE37lx(é B6bˆ[e2)«T­|÷…ÄýFc¬©ýOgç|‹³F:€˜q èÞÎÔ&ÆÙ,¹)F6ú/ûaOdOZºnôkËÞCŽ‚;vïMk@=5%%qNQ„½%"Ö¢‡ê&4xâ*v¶‚‚ã§Ng— 4˜Æ ÅÊ“bhhåçåS»f LO„ìAÝ.ýuCpŽšW:¼üý¼5”NF‹H“Ž_°d<WÆ×Ø:qOáTF¶†¶áU…«+Ñv®/äÞ¿>åö©JŸòN $•÷¬ãw¾þëŠÆ£Ö×¢ìö¬6Å¡’3¡hªS`‚£«)94›jQNSpZÇÄ5úY÷S¬‰¦OŸÞåϵÛn:v2kÜO­øã¼Õ*hޏè(§ÂªØÎÑå&¢`#‰ÕcYNm3.Þ ‡K‡CÓX‰€U¡3 ¬É禜G%v•Žœ8mÇqö*ˆƒ«²S¿ÅÚû›)',>«`µÔ£º 0äæÉÌÍWr@Hvâ E p4°mŠCÌ `%ÓFm›8(P=MûNeÒ”ïæSÇ–M¡‹lMuvY‰ÄëáȬn÷÷Ö}üåYκ¶ ð®õÁ,”usÑU[Š]#å¬nBÃuâb¶”ãàž]?)ªz÷ö}‡©}|³b ‚"P -´Ê`ÎË0Û?¹¹yœO ¡k áEãÁRÆìÜ-s'\‡nxò`ÇZŒ„‡ {N°a‚B—ÉGƒ9úÖI17£t!Pnƒ ;(Ü&HVoüº¶g'®–" }êÜ çí>uî%„ P÷¨nBƒåýc³/øiÆÏ·>ôØÕ_Î\ù̽W«®v2x`䉓…Ï…j¯Ø*É¡‘—Ÿ­;Ùí 6 ôÃÛ+õÈÐAqÁ…9¼ÂxÚ:¼MÂ2.!0òÂo™ð{ÆÑݱ4=ÚÀ©9§¹Mðžu°v*ܳr$ϵéSçn2c¬©jŸ:wNB¨T7¡a¬Žy㉬ +ëtöš% þO4ôŸŸü´P»ï†!Å{F‡çÉQ¿±Á÷l"$8X'2tAÑ‚àhÉq„†±ÝR›²ˆ™QL€©àô°%UÞa|˜Ø`® ~2Qh¤«“ÁXº;Æ>íè)â¶à6á¶ÁeF»¹G“çZ†€ô©²ÌèFUíSeç$oú…@uŒ.¯Žu"¿lz6å_®ŠjÜ*=t3}GÚÝWR ÎwzC “Wc†ê+O¦ÌÉ(€ÚP!7ás4Ø<+±4ë¬clô â˼úbl˜«Á62˜ câ‚O›-TeUËäd0‘±róne÷– ßp[0½]ðkÂѨC_‘ô©²Ó}ªìÔå P?¨)Bƒ9<‰ñ r¹|Íþö«_‡_ïdûâ×:~R»õ²ª»Ìº•CžL!§¡Ø*a¾˜ƒah¦ÔBx靯˜ÐÐ/dLp>Œ»c™ Þ.aNÆÎM뾟óý׿"ŒÞøå¶á6â¶B Ô5Ç„»ô©’­jô“Bl<ïS%S“'A@`j‚Ð`6¦Q]n~©› àSj‚Ðà r, À«g>‡·MtuL|ι?|ýûÜh^ûn=ã[¶ïÔ18,¼A`Hp¸Ùla¢Dœ‡Øí¶üüœÜÌܬ̓{·oÙº}ÃÚ}H‚‰=æ\0aÁdøb¢ƒÛ„Û†gÙ6uݱ¼Óœ•›iîŠÍ8p*DÁK ¾éf•ëzýÝëw2#Ÿö¥‡ž<úñ¯¿uSÓÃúw¦áý:ëœT÷ðò\»H°¾m‚¶0UO¿Èêhˆ¾Eb;>5 c18ü©xg‡é"7ÑQp‘v F¹ÒõóSÚ¨š·¿ùÜSA¸×'ê¼Ü ¦ nCNƒ‰ nDƒSa!̶„¸×nÜs8¾Œp¸çŒ9cëŠ/s-˜¨`Nl—ï¹òÒYF]uÌýÛîÅç¿-¥“Ù0ëߌFì*fý‹üŒ òýôýœU4oåœ =€º‚ËQ÷°®~+u¥^ÌÑû|j§]»‚õ"}œ6[±9qðpn‰'ÁjAP:À/LL{ëMŽÝz-ßfsæãðɼ|Ÿ «â 7©Z8Tæáx•1É©¹£“S×€Y„cL47ÇünµÞËãm½t5Eh0ØÆ„Ç+gƒxà–2öÆÅ WŽìŒ8…Oò·, BÁ 2 ùÆ×àf0Gƒ9Ld7 ÔDzM.ßDßamÖ(‚ÆÜp‘TèÖð|äxïŽ-ô‹U›ñ×Zzgúºax_ºø¼.úV“[yôS^|1’² cŸ›ü(;‹eŽC\“(­ULc5¶IŠùö®<ª2kŸ;“Þ !=´PB ôŽtPqA×¶î®uWW]K‚ŽtwÝ_w-+àþºkET+JG¤=tRI#½NûßóMnHbRf’IòžËÌÜ{¿rß;™ïÜSÞÓ͇ü¼<„õ YüðÛðÚSë8”Q‚>u¸rò )#§À59#gLJÖ•1&ƒñ‰ cZñ¸øOµÚøºg.6Ô±º‘n¥›Æ!Û¶_Ũq6’RIZ“^˯Δ÷ö3Ïä©çv„×öT4^üØrÁ ¢ú™AÖþXÑà@QV4x®Â|…W©d„fˆŠ-+ìaÌ_V4X±`E&@±1ö|œï…”Nˆ[1XÉØ´ï8žÒRLdÍŸ1™\µ~;;á•·î’¸ZôÃó§Ðg›è‹ÍPÁD3FG eCZ7Z‡­­[/\úÒdM‰i5è£z…и˜HŠê¬ ˜§UÖgP úóÆž–Põr`é0ÒÙ”,JLJöØ}ôìý•†ûÇ-{peüÒÿã“þ·¬W…Y™¨hh ¾NQÈqˆïd˜YŸåh+£úÓ“ñ‹Ì?Þüë¼xé²r3iRñ·œ ‹Ì ¸mL¤M˜1¸÷‰ªbp|¦ÝH{+*Š iÍ÷¼ØñÂ'²Qð*EñZWѰʗývVaLYTlUEƒÝ"ª2ÇÊ+ü™÷«qj[ì’ÒYP•Œ£gRhÝÖCBɸ{ÖèÎry6¿VÆ^ß“À/¥Â÷ —ʆ͑· ðèòå¥åÊ ™ÃÍ&3û¯ÂàÉðÁBà€WÔ«V3)<ñ›¯`1ÎEËRÅlü[pwíýs&(a~m4[L†‘=ƒÅ6wêíGßïÖî?~nÕ¢¸eÓP2bB…ÑØƒž5ŠÆàÅVM€¯')‘i bJ.²q®Qfƒ_᪡üÂÄRE^),é•’•7|RXñðpät)úþ8|£upþö_º?_n³‹md öV4xj¼ ñXs1dŃ>¶dÔT2XÑ`åBÝðVÊ5`\Õq®©l0ά\ð+o|LZ2Bg¶d”•UÒê÷ w [2¤4Æ-+¯Hà^.T(-BÍG²ñ¤©ô·šH§Õh®+-5†q õG ñFd$â‰^ƒã\6•›±c«ÔT ÂÕîãånüÓ]×k=ÜøÙµ}„+‹ß=kƒ’Ö§;ôÖªBý¡ˆù{sÅ×–LL|é²®Ò¥Œ:—ší–x:ùfÄ[Í5é+Í‹–.ߦ˜•7ƒÆ}¥ÓM‘– ÒÚ6ö h¨×ÀßUÙ`@xTÝ% ¦ªdÈ €Ñ aLYT|UeCU8ÔÏêqËÙòÿN‡+LÕ¿åÀ ‘Yrçõ“¤»¤…w™-s'¡kwU³²ÁOÙü—Ë›jÅPïŽúŠCRA@U4øUÝX©P7u_#]ÈCv™pÑÁJÔÂÈ.á˜)-G€ñã,ÆsêˆþÕî“>™¶|"¤¥N·Æ)Ýxz‰É˜÷ˆýC»ûG‚°qôÀžà3ñlT¡hv]x{º5t¸]ö·E,TH€/ñ6kÂ-¸¨X:y>ý3Œgþ°ø¹øÇV¾·¾-/ÞÞ õÚÕÅCUɰ|²|VßË×k#Àxª¢b«~–¯]‹5Ã@Ò²©°¤œfOïWmûK䌔¤K—®}{‹§TéBi>î‹—¾4‹àjÄ]Dôëb¾eÒp΀j‘rÑüÑ;o öÆ îÆ›ròB:}¾é@DZÖ•u‹â–¿MáÝ]µx1?ÌÛ\:ŠGõ œ_Us¿|m5±«©tØü &hª­z=ñ ÿø èØþë3`OƵø²Õˆñ–Òt…ñG2›vÀ=úø½3éÑ_ß Hk[ÓñkꙜm÷ÀíÌñƒù;ºD“š³Y§{¯M‚V:Š¢ÑT,åy‰@,ІÕŽõ”[PL`üd~)­G€qd<WÆ×RÜQ*MEqqà¹z}è€mìïçhûE5µ©<¯°[Ù/ôûÛ&)&³yb†1}e ºiv©h42Ù@"б`· /€HCí’2ò­¸ë!Àx2®Œ/ãÌxK¹6`Ï\¤^;¸-š7U±–òËV¥cçÒÀs€v:MÙ ÐjLbßú‚©!Nás_ùïwôÈß> „“kf•cl ûï×;­ÒWs:5°Ý2e¸‚ïê} ã–ÍhNÛ–œ+– &ÛH:\ᘣïy!¬¬4tÉÚ%¶¼]Ψ"͸2¾Œ3ã-¥qéþÞ ¼ïõ‹4ß{Ó„ÆOnæÑ »Òš÷ŠtããçSáÖÊh´£ ɦU·lï±óÈPq¢×ž¸l°=mgƒþÞ4¤_„5ºjv7Œ‰&w7#Ê)Þ×ìÆÍl`¯Á Í¼ yºD@"Pì6Q]'ƒ®p¹Ö‡Sk÷1®Œ¯ê:aÌeöIèš eO ™Ðõž›&(Z¤zZS']¢ëÇ¢ë†õ«Õm”Á=GÏÒiïê)‰š)¦§“/ÓÞ£çE›ÿ~ý3ýîÖëjµg ÇAlÌÕÓ/\¤’²‚r¼ûŽ£ÞàSÓ[lmÝ‚BºûÑ)X,˜`‹Û ÐCpfp§ë·¤ð@rÿʹ”LÚ?Bü;›&æˆZ+4kb uóñÕ”·L"O7gš0´/€$θGúi“.e µFõaÝ;ÜØHò˜D@"Ð.¨®ul—ItòAUeNºNšv£Á"±hdT¥»ŸgÓ4ã,¶DpÅÝŸà61Öpc½÷Õ±hO9€¾ÿù%œºX«W_¤Á2+gX MFºrMÉ/*¡w×ï ‰P^FF÷‚‚`)"¾ÕŽ7ìJ¤)èóRz6ØbŠfÓsèµÛ¨°¸L(ÚŠ‚|,EÈúÚ°ë(uó¢ÔF9y1]ìç¾ÖlÜ%&‚"‚ý¡X¸Pjfýó“…RÄl¡o~ºIœkÿ›ŒÜ#¨Ï-~#ktÚ@RÑh¹["ÐP-üä%2"•Jg¸2ûºØ,øgsûš¡ýÌæ¡ØøpÔ‰ñí [Èì‰C˜´Š¾Ýy˜žûK±XWê¨RœNÞ®”r9d_®”pâB­áÙRÀVOêÖ]0mpòõ€"Á<5[PX¢hÌ ÞPtŒ±uÆßǃ2Áü òSÄ›‰ë©Ù®¥ï¿Ýq˜©Ìµ¤Uþ·¥}4µT4šŠ”^ ¥ža£›( µ¸©Ô…ÆF#uøn¨=ÆÁ‹¾­d\#ñÞŽÅÜV†T ÷…®-•¹5ªW@@dcÂÅÐö9+60Ò Knƒ»ÃÞX½|ûEÓ¼¢Òê>ý¼½höÄ¡Õñ=‚¯ÖTùñÆÊÄãhü¾¿uK„²Ró÷¯­beWÜíÓG“«KëN9ŽèÓöÒw°êÀ¿÷Ϊc·ÖÓïeŒ†-P•}Jì «Ê†M¬§S¡·M0,?ê·ñ%u¸áœœ4—ÊaaÈÉç¢ÑÖ—çÓ¨¬%ŽÄV/X|@YÎV ŽK˜6*J Z\ʵ$–é£o,l‰¨ÄwçV¤†vÕ7Ç`°Œk)Ó}_7¬/èü¨ ˜«Ô#ã†DÒ~(©YWè™èƒâ|le‚ײr=9;;ˆþßÿ&àf §Ü¿·G˘N9Hy?úÿîçDcfN!‡Ä‡8Äêˆâê™­uwIEúxÊÞ$]ïw£m §ÅõrjbH€7͹.(½[AN~‰Èàe·[×]9²A6ì:Ÿ|²xªe³õì ƒ©jfHi{ÞxöÙlÐ`ŸÞ{ô\$r«[Õ9>#-+•S5ÑæM³üÕŒQ"6ã©×?ÕPçNN“†×ŽÅh\|ßøt#¼ ŠP(nbés \_ÿt˜–¾½V¸ÎØ¥R7ˆTís4b;>߸Ÿ†#(Ô•YëÊT¸MÖoK §^_#æ÷ÌooU_Ç£¤ý+ï/ö±u#þá_ ’¸ºíûÌŒÀ«¾ÜnÌ+(Æ%(Wà†¹eü³ß-m¬™ÕŽIEÃjPÊŽ$ö‡€­Íølþ¾ÿæñ¨dZH›÷Ÿ¢öœÀçq­bÓ¾ˆ¸÷f[Û[1òÌ÷[ÌË Oƒk·¢˜çs'¶¶ëÛÛ÷î ÌFz)ùrî8`³nvHk/ái,ÐüôÎ÷ fFK¯ÐzhÁtâìVBÔcùã‚ê!{ë¤ê÷5ß„úÒ_Y Úra4U¸ŽÊ=³Ç ¥˜Çd«K}ýpÉ÷7Ÿ¾Wm*^' í‡LKv Æ.œ7EXN¸­:¿9“‡oeå•õ*(µ:là[dTŸp Póç cã?Õ:ÒÛ+tqûhfµÝõÛxZÐýÂØel=òÎò¥VëWíS¾Jìšß}{›£!å,´úŠ */+%//Ð'œÏ«ÿµ9óg‹Æ‘3iôÔonÍv#5pýöä[t ¹àǘ˨¯ÛvX¯±B2ct8ÂĹ«ØOnHí»eÒñ™}×÷¢'ï½¶@aÙ¸ï¤øÑFpš;i( è„@»KPf’„¿½oD Í›:TüðžOÂþãEÝW;)ªgÍ™Óà¥ìÁ<¿Þy„–?8·Ásšs`%*¸ž:—BÅ……äâêFŽÎÎä€GA𞔆€q¢ÐoæL$&í’b{8 æ2b8ívïñóFÄ® 4v·¢Qîy[÷Œ%·×Ó 6Uv)èj°ÿúLr|áîBÉàÔ¹÷¾ÚEåzº}Úp‘®÷ñû„I›±)Dp^iÙÕjÕåxÊÌGàK” 7˜–õ ¥;fŒ¤ð _QnͦÝ“î½i,]Î- ½Ç.ˆóy쳩YPjQLßP¤†‹ý ýw65nÿ†Ëým„@Urý竟hå[ˆƒ!¥Ø/(üCÀr²r^yôíïæN"wçÑ&£éÈ¢ç–ßd«Ñ¥¢a+de¿.€@žþ÷ä{çJɼB¿¾a”¸ê‹é¹bá¸iâ`aŘ?}¸¨¯²V‰kIp7˜¶µä‹D.®Åi…§.fà³ Ž ©‰ýaµ8ŠtEU8}qæøhš9.™µÓÕsøõðéb È,œ+¥ýP]L‡“R(öÍÏÀ±Q¤ˆrУÛ"À®™ÑѽI·äVmïÐW™V/Òý%£^u6Ù¢wÙ§D ‹!`e·!—Éæ@¦ô¿ï±g>< w W¸lª0TYY9Sî•-F¥å´ LêÕ^²+ñ í8xšžþmÞ XÝuä µJŸ,²OlIiÏ.•Ȉí‰ó—[É]°ÒuR(r—D@"Ð<8Cd<ê<|ûó1Á9À– /0(nGú+§ªþœxNT8ê,:f—ÈÅŒ\ÊÌ-¢sˆ™8})³Ö€è]1»Á I4~lÙ‡¿3ñ¬ø\„Eˆ-M.áþïu?C i’—Ç.E¿û@`‰nù Eqñ?™¦o|½Üï¸a´ˆ!à”Ï– ót|öË÷¢ºãº#ß õZ²jíVÊÈοÖi-:ÎÊrkLV~±•Π&‹£ƒ1,;‰•[ 3ÚJ¤EÃVÈÊ~%] Î*a®ŠvŸ ¹S†‚O`<}úãzåƒA¢¥é£Ð@( ,C‘}r&9“^ûd#‚Ñ\PŒªjA$U#6.¦Ñó+¿"Žï`b$Ž¿ø}½ãˆ(X6yx_êÒ­ºMCoŽ"3¦î£cˆéàM•»gŽÁ£êgùÚ>,Š[þ˜É`z±8æ¹à§k¦CC¤WÍ!³qr@ò¦}ÇéZ«+l1Øq(I¸ôƃP+2<Ö†$JDÌ“fE h8ì¢Lî5ÕN¹Œü—[ð]ŒÎ;ŸüìÞI†òú3N4|VqÀ|üýnb…éÇ=ÇÄ÷•«Âã±y?;nøg|ÔÝHÃ&â”Ô‡@ƒ®¦Áªû¬ùj0éLJ¦Ôð{¬Ù¯Ú—T4T$ä«D@"Ð,f„ Ê«?Þ.HE}~áÍÕ}ðçŸîš!¬\JÝ’Îh9ì‡ìN±­ÉÔÉŠŠ*Ѱ|°RRŽü•ÜhêÈþøÁîý ”òv?è|>»Bs‡LAޤ؋▽ŠìÇ@n¾gÖ8E½×Öš)g¶Ü9s¬(p6´ä5…- Ëþ½žnFút¯Q°ì…Å·A¹ðÅØF`‹+¬æHë„P48˜ø<ȯ¸’+>Û‘D\¤‹Ÿ½úÑ(ØÃ©‹6÷ûˆµØ%qälŠ(U? ÊrTx½”‘-¦ÁÊËkotä5• >XÓ €8Û–ÂEÞ XiI£}Çã\U­lÑ»ìS" èò0§FM%£& ¬€4$ܦ¾…Ç…¯j>6Ô^î·o–ÄÆßµþ1¶,¼m²Õ• ¾z®ë182Œ¢û„ÐjÔ÷¨çÀ5v©pWæ|ñrwEÁ³KB¹à¶AÝZí‡øˆA-ÎÖ .Œ6cô :~>Uð¹¤dæÒ °x{u·¬üÅ=pkõg~30¼q+ª‚Èñ3' !®–ÊÂJÊØûÅ{þo*ªò¦ [eî…ÒrOÙ"RÓJS³hŸ_³Z7OúÇw«ÝˆW¶˜Ô«ÖÁV~8 KË'ßï6¦eçƒtFùo°&ôAÝ‹¿µù‚T4Zyãds‰@G@€@ysÇé…òBÁuÁæa)­C€MÚÅååÔ3À]à«.T­ëµk´vtÓp'à­gŸÍ5;™¦é ú ¯~øƒùÃïv‰LŽNpivq \Øð‚I_ÿd£‰Ý3O\,Fyøx³¶[T[* †´hØÅWBNB"`Ô…M¹ü´åïíF.xR;B¢aרrj›u®^GÆ“qe|góÎu¥¶¹šqqi:Ý{ƒÓÍiÏÿ|øÌ“ûðÄ=qH¤vÊÈÔÝÏÓ6ƒvâ^™býlJ–P0Àõa„K¡ JX?_urð|û Ý#sýÛ©hØXÙ­DÀ^`Ÿ1øâÍ”Æ=º¹#Õï2]HËÕQíežmŒãØ?ØKàªbÌxKi::ˆxæá¥ù°²ÒðĶý§îÞ²ÿ”\)f”4×0›?bÚBx¡>~>]Ý ò÷n‹!ƒCwvß.OoOwAë:0‚Tb0.RÈ œ^{!-Û”žU× ]WS‚×µ1ù8X;a“N7õja¡FG´ÍA©hØWÙ«DÀ.PŸ®-J†999Q¿`oº”SL붦‡çOµJ$»]\lN‚Ó"?GÀ“qu@ã¬bÞ†ÓéC½µìéã¸ß¢’ëS}Å"pLüúô¥ŒŸmÜÇ4àÆ~AZ¦œgÏ@(l=j­pFFN>ø,²èpR2%]Ì0Áå Á¢n|ná-ZæÃh/a—ÜÇßï¡ÝGÎ^s Z6 ó>„ïÞOZE³Ã!Àcߦ{Õ¯ÔX®(Æ ³IA숑ãGÞÞâ=»M8ëDZ3êùâuâ]¯é¿‚Ëã-±¹—ùƳÏ6·I‡>_*úöÉÉKšŽ€Åª¡%g³™Ý-\l¹à¸6û;# ±¬¬œÜ\+ÈOo‘úlñà@˜Š›>X=“2Îa¼6ˆØ X‚€ +"& ¶d×Iµ5CvÐ[.§ÝHE£ @–CHì^aìõ°|'4 ©h4 .y²D s"À±[œ¤­ûOŠÂSxw„ È©\@«Üõ~&ò +(9=œ"åôÕöCäãéFSGEÑ´‘QÂêÓ9¿]÷ªÒý-Èd4ô1›LP©ÃQ¬,†=oüxAÇöà Ã©E¼²T†}\ȬTCæf+©’gä%Ò:ž©b*Åa)Œ@×û‘÷]" ¨F€Ý!Ça½øå¨óP¡ š9.ZRW!t•‚<…¾Ü’@ÛœBµÛ± +[¤tLÒ-jЛgCY¸·q¤^_Q«Èƒ¢5¹»8š\P ÖÕÅIÑâfóýVÀù‰:òæ ½ÁT åÅϨ¤\¯…‚‚/ˆðTŠ]·¼Iá h² Mv˜B»mYµx±¾c¢ÕúYKE£õÊ$ŽÃؼï­ÅäïEKæM’EÕêÜIWdâ ë.6.ª¶n{"½µf Í›6‚¦(‚Bë4‘í‡toyè ù¿‡wðq½Þ „94ÀÛÔMËUYƒºù¿—;ù Pî»j½¨ïjXìEœ¢– ÏAÍ ì¼bÊÈÉ÷LÎÈ™”ž“? \4ÏjR³ Å-ÿØÙÁá¥7tO¥Ö×iÍ}‹V®ttÎÍõ1U’¯Þ¨qÖš•Jr0éÁØRéì®Éûû“O–Ô<ßÞßKEÃÞDÀʰƒ•ŒMûŽã)ý ÅD†¡Lü°ƒ²uXJCpeÛ‡çOA™øúbóP¹›hÆèh™Ò`v´alüx“1 r´CúFÓø˜HØ+Dñtw±Ê—ÞÝÕ™x«SF^S©7ÒéäË”˜tÉ{ïñó‹`ùÝâ¥Ë­\û>ó$>>Ô\F¡¶ Ä×) ¦“Á˜c„)9Ë­¼?#*°A`á×ò|¢ÅKã ¡(¥âo9NÀÖ’ 5;$ükÙŸOÃòbwURѨ¾™òD ó# *GϤк­‡„’q÷¬Ñÿ­t…¬Œ ¼¾'_ ¯ î.• +á{­n,…ÌÊû£{¸Ùd3+æ0,¸>h‡µÌ¬Å +Ö4äå)dºb&M.|¥0A¼ÖÝßÛá77OPz†Ôò’\kÈVçïË >¡b›7}¤ö£ïvkN^xŠÏT­V™`,3õ峯àçaîæ£ ðõyž³(=ïŽòóZ2ÀÃ,½œr^\V8ª2/¸:^É/îëÉ z£QZ²ô¥¢…q˾'³ò“»æ»·ž}×ßþ"ö¿r6C€-ee•´úÇ}Â]– )ÍG€qËÊ+8F†‚1”‹ªYåá¸ù“éÄ-¸<;•™nÁR<Q×ëKz×¼\'G#ª ›5ZDB ‚Ó™Y®´¼’°iO!\¨Bl|üî´ž(Š×^Ân¸{fWŽŸM5M¦ûúõÒ èŠx¨ ‚‚¡€ Ž]2Í~x€²A—-v.5ËóÈ™”y¨<¼@_j4Áz²Q1+o9<óN§XJ57w+œ/ +€(»tXÉ`Zñ-NˆÌ’;¯Ÿ$Ý%-¼qü¤:wòZ±v‡ÀóÆqƒ«ÈÏj¹î[Ø{×n¶H·ÒMcÌYëÄ=æ2ÓT¬£/cïÐîÚ^¡èK~^"Ášjw`¹e €›³“Aí,—¾ôÈ|Øe™yÖ VH€¯ØÆÁ%qH¾œ wM²fçá33 ŠKo¼l|)eÑÒø¿…h'¬Ðé¦ OŒ5ÆnjRÑh*Rò<‰@F€ŸzØôZ‰&øñÙ%s ¥å0~œ¥ÃxNÑ¿Ú}Â?üRš@*©Ù¿WŒÙ/ ±£{ ¿·qTt/ͨ½)Ðß«Q…¢¡ÑœAÏ›= [6l-AþÄÛM× Õ&žNAÚú‰PÄŠ¼q™v=²ð¹å¾ób,œm'RÑh;¬åHvCÀbÍ0Ð…´l*,)§ÙÂÛm.i`ÎHIºtYàÚ·G°0ÝKJóïðÂ¥/RRsVƒ•¶7\ ¦[&£ÈðÀ)ͽó¶`WÒ°¼iŽKC ó¾Þ™9ß-Ž[öÏ`‡‰O´•uÃ:¶›Î{Ÿä•I:<ÕÖ ½žN^H‡‰ŸOF‡¿0;¸€=žŒk%ðe«ã-¥é,Š[¶X1w‚­Ç£wÝ@ß3S%£éÈ3›„¥>¿p®vƘhdLÑ£Æ]þøúëÎMjÜÊ“¤¢ÑJes‰€½#`Q4Œ¨Ìª§Ü‚bòãg[˜oíkÌqd<WÆ×RˆN*MÅÄVOA/[î÷ÀmT¯¦6•çµ¶püjÆ(ºÿ–븦ѴŠÌÂ7[ÐM³›HE£ÙÉŽ…»Mxäòï%e„ÀºŽuv>[Æ“qe|gÆ[ʵX÷Ò<“ÙüòèèÞôà‚iŠ›‹u®ÙªÄn‚/· ‡Nƒ@ ”Hì[_PâédqŸûÊ¿£Gþö%œ¼ØH+ëbKØ¿ÞiΚÑËØÁ}èæIC@¸a~`IܲiÍhÚ¢S¥¢Ñ"Ød#‰@ÇA€«±ªÊJC—¬]b˻ŵ`WV4gÆ[Jã0¹÷#û›ï›3¡ñ“›ytÃæÇ½"ÝøøùT¸ 3íÁŽ ÕÛµ÷Øyr•êµ'îlFÛYã ^iH?”¶ƒÌ?˜¹: +÷Ùzx jk„eÿvD€Ý&ªëÄ`0HŽÚq6whÆ•ñU]'Œ¹Ì>iø~—Jž‹·Û½7MP¬Ì?r8é]?v]7¬_­ T@Üsô,Fðîð¨žB‘¼Ug1ƒçÞ£çŧÿ~ý3ýîÖëjµg ÇAl`ì ˜~á4 –VPÀ[AûŽ£ÞàSÓ‹·3gyPHw?:‹Ó“s›azPÿžÁ¢ÏõÛ"M×l¢Nt.%“†ö§ÇΦ‰9º SfÖÄêæã)ª)o?˜DžnÎ4ah_ Iœ5„qïÜM“t)c¸5úk¬iÑh yL"Ð P]'ê"Ø .Éî.AUæ¤ë¤‰·FQ¡ mµ‰ š~["¸âîOp›€«ºá{_í‹öä‘èûŸP©‹ÕÇø/*ô2+gX MFºrMÉ/*¡w×ï ‰P^FF÷tã|| ªoØ•HSÐç¥ôl°ÅÍ.¦çÐ;k·ˆ³„áà ¡­(ÈÇR„¬¯ »Ž¢¶Šqm”“ÓÅ~îkÍÆ}Pb"("ØŠ… ¥fæÑ??ùQ(EÌúæ§›Ä¹Öø-o—s Œ >OµFõ!ÆÐ‘Ç$Õ¢ÁO^"#&¥XØ,ø^sëÒ9z|(6>‹œÒXmrA³'¡;oCßîaÝÁ´™CO]›ZËdóÞãä``ïPÑ5LÈßVôééî,â:øþ³Á-rïÍĹãbúÒ±³©ÄÕ€œ¼K‚¿ Ø'Výw8) KÔŠÎÁÿÁÖʼR«ˆñ&Eâzj¶kéûï~NäŠÍZÔWy­¥}4µT4šŠ”^ ¥ža£›( µ¸©Ô…ÆF#uønŽ$rW}<Ýmv-#à‰ðv,æ>°2 R¸/ôpmÆ\ŒÕ+ŒF ìÕèøgS²hÏ‘³bs“ç Knƒ»ÃÞX½‘>ß´_´Í+*­îÓÏÛ‹fO*Ü)|°G°_uÿ\l7V&¿@㇈'ÕÇù͕¡¬ÔÜÉý«Ìä슻}úh”­o[yxþßì8Œ¡”ÿ¬|1vcÍ1mñ^ÆhØUÙ§DÀθªlØÙÄÚp:jP,êcX}T~†U1¶zç¬C…œ“Qƒnƒb›\Ù‰óiÄÖGbVP/X¸ô;[)˜ö{Ú¨(1nqéÕú¨õMdúèÄ ["Py•n:‚ºùy‰ Þ?r`OJÏÎCªžj(2¢;Í›f)Èü›ñÔëŸâ»e¢¹S†Ó¤áµc1ºž\|ßøt#iaU`…âÖ)–>§ÀÅñõO‡iéÛk…ëŒ]*uƒHÕ>G#¶ãóûi8‚Bë«»2n“õÛ0¿5b~ÏüöfëYJÚ¿òþ÷b[7âþ• ‰SûmÊ+ÇŒü{ÝvcN^.A¹BŠöwâŸýŠhiSš·ú©h´BÙDÀ~°µŸ}Þ÷ß<•L ióþSôÞø<®Õ€lÚw÷¢Âlk;c¿{ qÍ2L,>_l9ˆç=¼`Jk»n°½­qopàŽrÀ¬,G Ä{;Ÿ¦‰Ckg‡´öžÆÍÖ+¾5 —qA¶‡L'Î>a%D=ö—?.¨ò··Nª~_ó þúÈÑÖÙéê²ÉVTdJ1©ZËê뇳QÞ|úÞšÝ"‹¤ŸØxgw?OZ8oаœháâQç7t켕¡"m} J­øÀ…åøºY€K€F1¸(6þGåíébÙ‡bS¹Š˜M‡‘Kº c—á™ÙvÂ)xK–ÿ§É˜Rn€ÉW_QAåe¥äáåE¬èçD¶dðVV¡§õÛS9~Ð\ðcÌeÔ×m;,‚×X!™1: œabî«ØOnHí»eÒñ™}×÷¢'qü§(ñLªøÑ>pòÍ4”ô B Ý%(3IÂßÞ7"æM*~xO€'aÿñ‹"ˆî«‰…²Ûs&ÅTcÄ3gTý™£H#´¦¤ÁþâÊuäâêFŽÎÎ䀧^Á{Íg7·|Ž4§MG=—ã8æà£ïv““ƒ–FêcÕK±¤­Ö_Ü®¦¢ÐÜAj+Rt­T™EUVêέ¥J÷Ãôã¯üé-Ç\LË·Hšçþ“_^^¹hñÒ—¶™µšß¬Ò= —–m¤y ¶™ƒìU" èà°ÿúLr|áîBÉà…佯vQ9”Û§ ézÿ°O˜´ùR ñ„UZVY}Õ¬œä#ð% Jˆ„Äã3FRx¯(·fSŽîI÷Þ4–Óòhï± â|ûlj–pÄô E:á/ ÆUàœÓÉ™B‰9””"”ÑXþ×n¨VŸw×ÿ$RA‘Ñnsé*ûáïs8â=î†æïÞ¡ýÍœ‰\zb4[òÜòm…ƒ´hØ YÙ¯D  Pž€ÿýp#åb‘p‡rpÏì±âª/¦çŠ(úE·]‡ ¼aÉàôÂý°J„v÷i™àn>0mkÁkàJj)û}¢cžƒÁ‘–Zýaµ8Šþ¦Œ´øØ9}ñöiÑ4DJõI^a)½ÿíd¡À„ „¶o}§É}mŒ€ªl¨i¤ƒ"CiìàHTníÞâ Ç6¾„;œ,Iãb"ñ7®ýךMî—2r×,‰¸".κæ> $û5‘·GÞY¾´~{mË&ËÆXWlÌjäßcÏ|8x@ï˜ÇïÙäÞ˜@ª¬¬œ ‹Š)÷J­Ù’Øä¶M9Ñ‘ñ†öA­ˆThÙ#È’ÒÇ¥èYBj†²{EµZÔíûZõAÎ$[ÊÛ¿ñé¶«Më =ÊGCÂA¥ñÞ*”Ÿow¥·>ÛNq¿›Ý`†@Cý4´?, îœJþ~¾äWZkN¹øW?Ø@GO?òþk/߃1r±b+ÃfÄÖéåÑåËËKÍ ¡Œ>tôLª Ïôñö0Î7HË—Rl‡€b˜ß>UóÜŠ/Ý+Ê œ‚ò µG“®k#*û“t!œi,†Ø=’|ùŠˆ£àË÷ó¶p$dÕHaÌAö‡'Ø Y8zŸÝª¤çòºÚ°pªÇyèÝ|u[xs­M¡²fÓ1§ 2-tFNA­öòCû!ðÏØØÌ•Ëãâɬ< "ž¨K~ªZe¨¾6r_Û"ððÒ¿D£RÌÛ&“ñ:¶dÜ8f ‰ÔrÚtK…y:˜”êØ=c‹°^±‚Éʳ‡6&«ÖnEªö0 á—µ…•'.äÖ˜üߺí"ëÊÃÝ•>üvÍÄß­-;"ËE1Y)¤µöÕÙ¥¢¡Ó½ç’¦Oû"‡7˜ ÁˆN6ƒ÷ÝÒÅÅÑQZajßÃ&}*×ëMù…¥æ¢Òr 0]¾xéò ¤c½êú¦N÷ÛÆ™kš4‚<©«#ÀY% §’é‡Ý'J:|ãéÓÐ+ü€…]KÓG  PX†"ûä ‚3_ûd#b;\à+î…ZIÕŽ‹éFÇCôüʯhþôႸhæ¸hú}½ãˆÈX˜<¼o“ |çiÓ¾“‚ ¿(~ÕîC@)®’Òþ,Œ[þ¨Álø»‹‹³rÛÔá4aH_þ~71GÆ{މï+W…Çÿbó~š}ÝðÏ\Uj8 W®—rʲ- ®:Œ`nôŸ=ê¸Ö|µ;EcQܲ.ÓßE¶o(ÈOÌ\!opd¨‚d›hZÖÓÎûßn6rjßþãçƒÀ½ÿ °þ0ÿݪø¥?Úùüåôì N­™6Êi¤Ï×pgðçŸîš!¬\J-ª° cñ¼IÂ}ÂÇXXQQ%–VJÊÁȨ¦õMEà'oÅ¥(åí(~Ðù|m(´æq¶‚°{ÅZ‹˜:WùÚrðÛów¤`ÿϰ¨žfðQ(ê½nyµ[2AÛ3ÇŠ¬–‘ %¯)lYXöïõt3Ò§{…ˆ‚e/,¾ Ê…Ÿ(Æ6 [\a5· DZ'„¢qøt OËn .|¶#!‰¸H?{õ£ ‚¬ÇÜïK Öò‚»ïÈÙQªž-q}PáõRF¶˜+/¯}¼AБ×T2Ô9r€lÊå\º€ñîše ²VYûõÇÝǨ´¼R«h4ÿ¶vßÜßUÕʽ7³ÏEKãÿd6+ßÁÄô?xûÃ3.0ÃD'R¬ƒcɘ2¶ŒqP7ï Æœ±·Î²‰@m˜S£¦’Qó¨ªdÔܧ¾ç6õ-<¼Vó©P=ÿZ¯ÌO •Œk¡ÔvÇÇÆÿkýÿ0Í7ˆª¬®dð•ð“úàÈ0ŠF¦Ñêö K˜z…lQc’-`Î/¸)˜·…• –ˆ nH­ö;gˆ þÎ)F›1z?Ÿ*ø\R2siØ@™Ú{`ïQ-'=ƒ»‰}êX¬´Ü&RæpaA ›ÍhF7ެžVëõôÅ JÂÆß[樱•\¯âV@lB¯Zöì^[Œc7ІP2Læ× ujþü››µ}#Ž ·]±OÆø©ûoÖ2æf`/•®ø-×,h'4ôª˜š~5c´ '“äŽëÇ"N(ÖÜÔê±,Ñ®šÙ&ˆªUM=‘­g½C»‹ ÌdX&è'\-lq ð5H@í]똫½²ËCvÁÔCçR3…2Tó˜úž-1¾ÿ&(I¡P’lâѯM½¾z#œ?tÖÃÁíulk¿Ú…¢Áîþ}dT/óó&+ìÏ•Ò60ÖŒ9cÏ÷€ïEÛŒ,Gi+˜QÝÚjÌ®8ŽŠ1¿Ji‡toyÀšÐT5mU›3i8â{WOŒ‹—1ÿ WYEÍQ- iÊ\ᕃP K,rÜ€û‡NS$ ³q%×(X9¶8 N–0Ñ_oð~œC-Ç\°åãLJ& EM“†„-%ÿsß,¤üŦµNã .ÔÆÂg/ç䃊ÿjüF­“[ñ‹Ïýýƒï••úËN‡é¯é¿šžÓŠ~ëkÚîŠ~jÍ»a>tßœ‰ò/´¾»Ôû{&Râ{Á÷¤ †”C´u=mí´Ò6˜B§‚Skøñ׎ӃÐÌ 4;T£òtSw±ò‹mÕi¨¬hðÓ?¿² ¬ú̯,“üÉÐt«¾¤~¼î•"Tlà?¦ïà¶É´ûèY:—*F£¬º?ѯ­FµÏDVÖÄ‘XK8n…‹Ë½±z“qK'Q‡~⺧®šz¬5P~Ú=”³K8ðóŽƈÈôs“oÛ¶lÜyãíÿ~°!4ÍÄ÷„þÞ†ÃË¡ÚWÄKdÊ$#kÂ]X\N^-Oôæ\:J_«/Ö/Œ‹ÿâÀ‰ ¿ž:*JÓ1 Ö–þXø_|ðöênYù‹{àÖêÏü†ãÕxã‚e¬x¨ "ÇSÌœ0k°åÙ—]+bï¯nËŒš¼©ÂÊɽPZî‚»ƒ-"5­45‹¶ñù5 ©uóñ¤§×šw9GŠFÉÔ(ÚGWÆ?û¹µú¿V?Ö»’kTÏq&ãõp0RX¥’Q>í±‹ïß¾7í1¾Óú¨JGº‡øyªðSkdYÔ®Ñ#ãȦhÆ•ñU±îWߺ«\¥{"Gc¤©ˆH†%Õ¼zá·®WÙZE€Ów9ãí5[Ì/¬ZoÞ{ìl)æ¿9h}ú·¥’ÁóiW‹3~âÓÌ<*8òµ}À½àKs«dmßÛÑêÑÕ…M¹ü´åïíF.0ÍrÓaõT9mõ€]¬Æ‘ñd\_ÆYż‹AÑ¢Ëý×ò¸”Eº•ƒcö Û’Ûsü<]®)ˆ‘ð‡[AJó¨¨Ô#“%‹ŽœIWÒcIY9“Ÿåá;ù›öÍ•Ï<ÓvA15¦Þ®ŠæÑƒ?%W;ÒÎo™gƒïIQIyÃ!ÓíCÿݳÆtèëkÉ3n™W iH¨§À“qe|%ÅyëïÆªØØ ôòéÊ#à!Îèç¥tóõÔr>?¸Y˜$ËÁμ¹@ñCÈ X?MÄé¢\Ĭ¸¬’ ŠJÀòY&·Ò³óM—.çšõzƒ nÒhµ©&³i ,R‰ù…%ÿ~齯‘I2AÛ¯É#™­ô(hË7ì:jý8²C4ÏièKð+FswèF`ñÔTàò*MætrÑž[ùôÓv]Џ] þ´µ `]kª¬E1NQ‡¿5sb¥åÕÇ-hc[Ól+°ªë„Bggrqu¡Ðn^”_R: ¨Èýý^š?c¤´l4p¶d°’Á¸õôu82žŒ+ãË é:iM<å-ݳü8ÿ okÖ¬Ñn:z~ ‘q ªú‡’Ð'%+7ºD8Üͨ§˜9n}ž$³é4’GÅñÀ¿^|ê¤:­…K_>™[Púù«lˆˆô7‰‰Ô0y¿·zŠÕ^9hórN]HϤ_x5 Ê,tVå¬F«<¹òÅØ¯«;fµAÛ¸£.§hxÂÇÅ¥¥y;‚9{ŽœŠWØ ô¥§ÜÃwLîfm;˜tIøÅ¸(μé#iÛS´ïØyzæws„u„ïÙkn@UÈîtËäaôÂÊu4ÿúÑ4´„(õŸ¯vÐYèp®xwßÚ†‚Ýÿ»Gp^¹àÊ¿{Ö8rƒfžˆ¹íJ< ¶º¨€¹WX_¸O)æ"À¦|^Q¹Ô‹¢»»U ¸^džôòD‘=®õ0w2ŠK¡|»”úà˜Œ/·¦,X2½µ?`Éx2®Œ¯ê:©¿¹·5,X°€k«­Úª»ÒéÌšË˃ɠø’Qñ6*f/Ñl °P4zÒ˜ôd2ë5J¾‘Ò)Ø7c%x<ª;¨çÍ;ËžÙ¯Ó­é›aJº7%+ï‘ä{c>ÛHäáæb§†6$ÀGéKЧ;6WA?Îu{œ¡lZMØ láfB·rÄ~p*4[S K`Q),õUà¶1]Î-¬¶ª€·#˜µø“ýüíb7âo×ÒQ=sìH»ºœ¢¡Þ¦aš×¨^–P„Â’rÐÓ"oTÛ» %{Ys]¿í í;~fOŒ;ݙúY-ÜG°É1O=ç~_Î-Aa³'ç勪”üa3ÊŸCõ½¹ €))̆ŸÂ¿hQ6 ŠJé¿_ï¤Û¦€âÀD*(Y|šfŽ j\=´ÛtTï»B£A(3´Ñ·üO"Ð\,І X4à6ñpw§ üèñw¬Ow#9k (.ÅkwPÿÈF‰Ëa`½1FÍ»£ŸÏ‹§°‚ÿ>éR&9Yµ¯†Â`òôô w¸`OÆ•ñµdžÀ¨-¥ÍÐé”ê ˜æÄfµq«(ÿþß"Ý_"£a6, £ñ»<ÛÀæYQjO Š„ ±é&£ù$žVO!1:A«8îù׋>­*¨¨]»QþÔå NûÉwü™”,2€Gþú±ÑÕ·¯¼Â@ñÍZ)ïdr®º7cŒåV(vÁqÛ´‘ÚÝ—¡¨+O^šm¿ÁhU[=u1]ÄjLi)}Y«eá?\.{x'>ȲDŸ¬h°°™ö¶›F`½ÅgùŸD ¥ðS+:ÃwÍOàz(F(ÛìÃfñv)¡Ôü :å™Tü’O.sí 6Ñ®&üÊE±Šaùá€Ad°R «™Â|œÈÊ…··'yyacex2®Œ¯t›tÎoÊ*ÝÓɸ²U¾fåá^ ÔáŤÉˤ˜=Pipܲ(Š^C¦b2+Å&­Rè`Öd™—ƒ 2uº©†ºh½ÿTÝ]âs—û1Cð…¹ë77O„«"XÈQïd$ ã°é‹…-Ð^…B¡‡k… ëð1¶jlÝR¸H¸Šßh($üã\õõM8¨çRF.¨jûª]þ{µ‚ [,XéxùÝo«sSMaåCŠD µT[5° šð%e%Cý®Z”‹:–QHE9•© ÂH•†2Ê»RŠóê|)[;™О-ÖÝÍÉLÞÎZòtÕ"ƒÝN®Â’ÁJ†··xÏnÎ:‘ÖŒpc­4Å*«ÃetÇ›”k Ðå ®Ø7wjý¤—5)g9zœs·3aÅP…}³Ì‡ÏÇFìM_n9@Ga!IͼB¿½å:õ´êWŽöïîç%â>ÔWàVQ…s£}aшøvu×/^ÙU#E"` T…ÂÙìDfwõ‹‰¼XÉàŒg*++GŒPùé "RŸ-¬0³¨iÖ˜‹½öÁ  ÿí2^Z ǶÀlX¡1°h°%C¸Nª­2Ô^臭Wû#Ðåæ@Î%€Ù7Ó/BÄ{ǰ°ÂiO_ÿtHX=Ø•RŸ @9a¶xpfK‚N^ÌéŠÈÐM8ýŒiÔÐÄ¡ý4¤ÕÛ“ó¿¾kû:¼xòÂÉ‹ªå³%íÕ®’r(åp­„k‘ƒÙXÙè J†z—U\ØBÁîÆKÍÖa7 Çd¸¹!»Lø8ã(E" ¨©hÔ‹Ø{'86Þ]¿Cðb°©9ªW¨HT›Œ†ûäƒo~Áœê¾º¯SF  ãçÒè¥w¿†uÓƢ HaÄi1°®ÜŠ Ñ¯¶¢Ï6î?ü7Œ$â>êö#?K¬€ºˆòâ(ÞCÙà÷Ìáæê*” (ZY E1øÞ×T4Tw‹5æbo}T3ª0ð*@Ñ`þ'+lÕàÀOV0,$]Kc)E" ¨.¥h,oÄE±äWSÿÈ,º}ªˆÉà':G<ÝÔæäà­¦ðNÍrÂAݼiÙCóDâúXæfMˆ!Þ@ù â‹[†ûãPZYùÞZðwTMÃî,¨œšÉÊ[2PäªÊšaéy×Äßÿ®`Õ`lÄ&[4'¶j0G+d¬\ðï€%•U#-ÖúRÊ~:5µWÎN}©-¿8ŽÉh­Ô§dÔì“ù=¤HÚV2xQÌ¡¼˜BÙàL,#\%lÁSFÕÌ”®¢hð=°`c‰_aeƒKv³Â!ÞCQ±kËû%Ç’tT¤¢ÑQ·DÀ ¨Ö ^8Ù‚ÇOíü©ºKjZ2º’¢QmÙŠ+–xu¿ —]Hº RÑè2·Z^¨D aÔ”–š †úYèÿ1ª¨¸¨Ÿå«D@"Ð|¤¢Ñ|Ìd ‰@§G@.°þË ”´2'«Í –I$‰€D ë! ®wÏåK$‰€D ÍŠF›A-’H$‰@×C KÇh$£ i±pÄ}7oQdmX:[+\åä… ,k•´JÙ^" H:0Ò¢›÷û¹“è¡ùÓDÁ³Õ?ì£RTll­?—NkQ EŠD@" Hº2]Ú¢¡Þø@o¡dDùÓ¾ãç)ñt *®F’äE_l:@“.!Ýl{Ò¼é#EQµ}ÇÎÑ×;ë'÷ÃÊÉòM÷Ü4ÄGôñ÷»©¸´œbßüœ¢û„Ò]³ÆÑåœúð»]”’™+Š­ýzæ8ê R ãÞú‚ƒtíæÄÅÛ^úã|uzòU" H$iѨºu¬H$žIŸB|Äëúm¡x\ ®?rã¸A´û(” Ô%a)¯4PnA‘xÏÿqû\Tf­ÔÉßÛõJº£ðš;ÝËDš:*Jœ÷Á·? šñGﺑB|é“ »«ÛsÛ·>ÝŒzNB™©> ßH$‰€D # -¸y+?ß**«–•W¢@ÚHêÜMÜÒÝGΡÞH/š1&Z|¾Œ’ñ»Žœç4vÏ}<ÝEù÷ôœ|ê‹ ¯,%et>-›–üjyº9ÓXXLþñÑ(!_D~ˆ aéëÆ¢y¿¬¹"Êÿ$‰€D@"ЊnÚèA½á&1Ò¶„S4|€¥ <s²rP³ü{x í¦¯ÅâÑ7"XÄeìL8Ê**ÅgWgË~Bûºó¨5 ùA" H$.­h4õž9hµ žÚØ1näæêü‹¶WŒ_’;$‰€D@"Щ®“Nu;åÅH$‰€DÀ¾èð ®Wrü\*ȲJ(¿¸”*+õÔ•Â|ŸœœÉÇÃM…E#€µnV‹}}åäl$‰€D +!Ð! ½Þ@[œ¤­ûOR~Q))Xm=]]ÈÓÃ…œAÿÝÕ¤¤°‚’Ós¨¨¬œÖƒ¹ÔÇÓM°‘NEŽ]®vÿåõJ${F C­ÊÉÖ‹¾ß#ÒFû÷¢™ã¢i@Ï@’q$‚LO]ÌDn }¹%¶8EwÏ‹Z+aPÆØö!E" H$m‹@‡Q4L&mÞwQfêEKæMe·…*¼m!³ßÑXÙÖ?\lÒrhÝöDzkÍš7mM=4’c¿wOÎL" tNì^Ñ`++›öÇSúAЉ £ù3F BjÙ óV5ïªX {xþúlS}а&³‰fŒŽʆ´n4Ky¶D@" ´»~ÄU•Œ£¨ªºnë!¡dÜ=k´T2šx¿Yc¼X9cüGVÚW)‰€D@" h ìZÑàE±¬¬’Vÿ¸O¸KØ’!¥ù0nÌ\Ê82žŒ«‰€D@" ´v«hðbÈ˶8!2Kn4DZ2Zø`ËÆÜÉCŽŒ'ã*•‚)›Y®vœ[P\o_\ˆKÔ“É,ê ÕÝ/?Kl…€N·Æi‰îåžõõÿPl|ø"ÝJ·ºÇtº­ê^î]wWþl—Š›öù‡¨²²’v>Cœ]"?[÷5eüGÆ“qe|¥ ¥u˜ÊÖ-C |7OþóSŠ}ósúà›Ÿkuò͎â>ГÿXM)—¯VDfÚþ§^ÿ”âP'hÕÚ­µÚÈ[ °hùòà4CR†Qo¸°(6þÿjޱxiü z¢Kf}öå%K_¦[ô—¿x§v¦ô†s ã–}¦îïê¯v©hX¬º–M…%å"‹¢«ß(k\?g¤0žŒ«Á`V k€*ûh6I3D nxàäÅZíNY>W€xïRÙU9“œIEøî²pueV”¥Hl‰€¦Ô< ì~<¢ÚæãÁ¬š#Àd6ߎ½ølö4™Œ³Ôy(¥†IxNîΟqp.[DÔc]ùÕîjk†^O'/¤ 2.æÉèˆRb±³©Ùv3uÆ‘é4×Jà+­vskºÔD"Q\‹²Dõ ®uí«>sfTÿžWõí^í:åâ„jûZ剀09:ümATÀTóf|'«£èñíÝdJ1*Mµ‰Í¬qÙ¯n)3“²C§[`© iÅyuÄ®ì.½Õ¢hI…}¸`ü´6×÷»ŽÑ¶„Óâ~qßaÝ}hΤ0iÍ›xOaŸü°Ÿ^X4•_Û?—¯•ñd\_'GGrÀ¼dº«5ïºìëZøy¹Ó+ÞA¹Ô'L<üU7™ý݇|½ÜÈ´úªx¸9Ó_Ñ&-+ïmÔsä«DÀš¬Ò=ü˜îÕàb}逕Ëbw¯Š_ZÝýÊø¥Z¸ôåSêÊØg3Ô«tOäÀ}¢ψé·ûõ@µ;EƒÝ&F£QÔ,)()#/ЊÛB¼=\éw·Œ§Ë¹…àè8I÷ž¤{f±êPƒû„Rÿ…v¡d¨Æx2®\ÆÅÙ*‰¼Ttäk[!ÀUë*êØ=Cê'âcE92¼cZ7Õk“¯ ×ts Ð®w–Çýbâï,{fÿ/vbǪ§Ÿ.ÀËÎUõì¢ûìPÑ0“¡JѨ¬4+žÀm!Z°dù{‹­¬¼’¾þé(•óâ‹e '/ÑæýIÂÜ7"æMJ®.W[CÇN\È ýÇ/Ò˜A½é«‰Õ3ˆú„ˆ÷OÞsˆ²úëû?ÐÍcIsŠ ŠËˆ‘™ã£Å˜•p³ü¸ç;Ÿ*q=¬8Žpò)ôÐí“ÉËÝzp-˜2ÔDaEƒqæH~)‰€D@" °v£ÁnÕuÂÁŠæ6ZS²òEì‚“ƒƒ–\6ÍÑÑ=éÞ›ÆÂâQ@{]øs eCÇXQ8›šEë¶X¬o( Eà%ïË+,å@"!ü~íÖƒ4|@Ý8v íƒbrâ¼ÅêväL*=—NÍsx/øÜ;¯iU%£jWÆ—-G*æê1ù*H$k"`‡ ‹ëD]­y±5û*)« ÕˆŸHɺ‚ÜüÐsG «Ã)DÄû¢úéàÈqzX&X˜2²?5vŒOæÜÿÛ§EÓÐ~á¢m}<ѽCiâHq|ÿñKÄq¬xœCm’`ÔpñrsyØâ·fEÅ¢*sŒ±äÓ°²O‰€D@" P°+EC}ºf†l‘Qm P§kÝW "߯Ö‘ïAäíî*:?“lI©}ãÓmW«Jjjì˜zò(&IX Oõaw¸©ÊD?¸h¾Møž£ç)#§@̧o•ÂQÝÀJo`7ªÊ8Adt•I„Z \ÙD@" ÔBÀ® ž™ºðñbhK=ÃÁh좨+ÎΈvw¥§~scÝCÔØ1õdmãÙ%ìž©Oõ ¡-ûOÑ™”,â`¸‡`A±Ùâ/ µ¸©o)‰€D@" °v£¡^äUeCÝÓv¯ÑÈã¿ ä‰g‰y0ŠJËébzŽ˜@cÇZ;Cv¡”‚ýpÜ*½¡h¨Á§­í·¾ö¬Z¨×w\î“H$‰€µ¨ÿñÚZ½wÀ~˜@hæ¸húq÷ úzÇaU˜<¼¯°24v¬µ—êïíAȲygÝOB à”ÓÉÃû‰ ÑÖö-ÛK$‰€D ½°+E£­Ìø³Æ"Þ’©p[ðV\Z® ZU±òù ãP5Tí·î¾¿þqžzH¼Þó8ñªG‘³Ÿo§_MNCú† Ecϱó´a×qºaÌ@‘S«¡•?´îVž¶ìN" H:6Q4Æ.k’ãŸÃN'_¦%Ëÿ# 2›Q±î }E•—•’‡— ðto7™°!iìXCmÚÞ|RÀ³Q‚î\pê R[9í5¸›·Í” Õu"•Œ†îJ×ÝŸœ‘KÇQg$· „òñ}dΕ&ýAwÈ8öÛ |:>`&õ÷v§è>aìßI®N^†D í°ž¢Áœðf’dZp™Dë×7Ž¢ƒ'“‰-Πïê/\'-èN6‘4=ü-NÒÖý')¿¨T(¸ž Ëó“,?»š”VP2b³Š@n·~û!òAÊûÔQQ4md9vA<ºÊýÿ£îu/=EÁÔͬQ¼Q7Íu®= |r<£º³¹ ÇJIK¥ŠY“c6;d™=LYU¬ ]®&_§Õ~A!ñwä1ú~HâJ¨ £Äø¬]c¨-¯ËZcqyúS3éPR }¹%¶Õ÷îYc…•ÃfYaÖš¼ì§QÒ-j0š'’‰†„¥Þ•Èr}gu#£›ëZô,Ÿñ¿‘óØŠˆ/]VLfM”“Ó )‡4вË1ÀãÀ¤{ËÃ@afƒ1LCZpÒ}ÄUmÙdVò±#פ%,ÊšR½±hî>3'Ä(ÓG TlÉS¤bàâÇ!ý"ÄvÇ cŽžMAÙ‹½Ã ŠKv,Ž{éARLc`!™Y®/Œ†ÝÓ7#ÃQkòõq7£…ÆÕÅQqsq!Gd=ñ7k0Z¶Òò ó•‚b#b©4“Y˜ÔJôÅpÛ,¿€„нèjƒ³»æ»7ž}6[K{¿vIEƒl×n9D'.\¦’òr òó‚4 õMB­v?˜Býݯ¦ùÓG"í5ÌjýÊŽ$­A€•Œ2d7­þqŸp—°%CJó`ܲòŠŽ‘aˆgqu"í5X›?ŠlÁ<nÒ(ãPiz<ãI¡(½þJu,ªc]ªF!NbaâC8®Ñ˜þt÷šÈðö‰ƒc·K Rd:<÷ö:³Ùø™ƒFkBup¥_ %´»¯È2ìæã¯ÎÕËiàŸ!Öî"úLE½®äËØ2rzMÎ /()»³¢ÔhZ¿êËGÎZ¯÷ßÐ=RØ@_m²»K*ÇP$íàédúÍMãPÕæ­Q.Þšˆk¡…>ÿÀͰ–tIˆ­ ¥ìËJ°’a€_yË"³äÎë'ÙÌšg¥)Ûm7lÙ˜;y­X»Càyã¸ÁÂ/c6¬sËþ ûkˆ^¯¿ Þ«7›†ÂOE(ß`êÚM ôU¼A{ÀÅ/9È…(µU1ê*]Z®§bÄû¬8Äv@IDATqñÌbl~^í¦dÔD$°çOU˜› J†Æ±’5Û4öÞÓݵºBÄVužC ”Ž#gS4‰IÉ£’/玭¤Â¿.ŒßÉQóÚ[ºgO7ÖŸ­ŽuÉU0%ó ù{yTgyp œ*àÃŒ ÇΧ±KP‚33(ˆÝÇ騹4ª¨0PäÕß>m˜  ¯o?}½úñ&šsÝŠî,‡¾ÝyíÓá#'˜ÓBiö„Á‚ ìÄ… Ú}ä< Dq·íϳ“‹éCcõR§%_%­B€­xle«¬¬¤‡ÏˆìøÙ*HEà,gé0žSGô¯vŸÈl”–ãº8îo}а¬ÒP9?“š^Áþ¦‘ѽE,EH€ôˆk?îóèöÌz– [JxñvÓÄ!Z(´ýÀ)·}ÇÎ/ÒL ÅÆ¿åîèöÂkºÇ¯Øruûî’ŠÆ@h€¼ òÃ>š96š|AÊ£ÊÞã‘Îv×F‰:'ŸoN A}‚‰MTÛ’èþ9 =+”–™'” ®ƒRß~þaÏñ–Zõ()‡O§ÒôQýÅP›P@ÍÑA+Jù®uR†æ›&¦“ÿ«‰‚iÔJ‡‰@k°X3 Èœ°T'ž=Á¶?v­oGiÏÙ(I—. \ûö†²¡HJ nžÖÔ¾%ã÷ް!ŽB3qh_ò÷ñ”i=-ÀSmäO÷Þ$•¯D®¬F²þåÍýÔŸÇÜakÞ8!Oèc“Òž¨ð&/~PD£ô¢³afPõEéв±züÞ›Ô5½Ú›Ò%?î2-{÷€H¬Epžiï/6ìÜBu"3GµjR_¼¾·Z¾qúû[_©ï‘Ý•¥¼óž}¦µp^A‘jŠâVIloæRáwViËuÊ’†Xß{㕉ÛhÅìYJãîù¼¯ý¸‰†KAç°â9*?%—wæ•8OâJ| HˆF屟”rƒáÒWÅFE´ùã½ÃlwÂxÞ›é*ß“ºq%× 'ïnëÛ­ *ÏŽON»ß#h¹|$Ô'² |†C+3¹Ú¾Ã§LIm1B`T4 GÛ2“®Ñf¢g‡DõõŠmêSØq´nÖÐt_-ë|s «Ðð'®N'N#tܹrâLÜ^C.HØV]à «îŠ^³r T£8‰_FÁöÕ&\™³é*aÅ¥xâI\‰oD¸Ët£Ð‹ã;Ö1µ³á2fÃ#ô±_Ýb÷Á Tië¾#jçþ#LÇ©Nm(¡>o‡WºgS^ýHº±¯é†zâL–zkÎ2uðØ)õ›[¯QWtiSúr¯~¦$ öhkWë­¨2®3¿½m7¿Ææ=Ór<½ì5ÇSîsE7Vã» ·¶Õ¨È›·ZbýhTæÀý”a€½Y¾ß°[í½ýÚžð¹_eÆÖ ãîî[¯)?U½·ÆêYs6ðЏ_ kÏkä}p!ÀçÐR8NÄ5’EÐ3L\‰¯¥:!æüû’R6Ú¡“¿uéFÛûoh†ü.ûªª]·}¿º±wØÂu¼ ªظKí€ñnH*H$h¼kf_Õ8Ë¿éw·_c}e¾®Ùº‰/÷)ºÐöÀf³/¼aø{ºÁïVnÚ­Ú"ž ¥áQX­Ú¢š7®›¿Ã¦‹-ïéݹ5¤+Í̺>]¼¶~  &Ãö˜êÕ©•ùwºiW†ÙGy6¨‡j;fSþvív ;‹0­H:sA‡/ò!¡ÚµllÃúf\zIéC.RõO¾(¢a-|üûdŠñQ*ÈLhäM¢AÏu–ŠÚIûˆßŒ`uñ{>4ÖïÝTKßÃseçXžyôÎÀCBlê×Ãû™ÑÝø#D«ôÂCËÃ*TµxÞk¯î+q$žÄ•ør\æÕ­[î\,Õ‰µnOkoÏ,2GŒ‰·”ŠÐ4ãIÏÒ»¶MôºTb2î²\ݳ½cƒïÿ5g‰ŠÆïùuWv†Äc%Bsªo×Ëø•Y—£Q½X„°«ëà®ìY2³sÕ›Ÿ.Q¾˜ž„Öï÷Bd;^½e7rß\©–®Ý¡ŽÊT÷ `ªâ¿X¶ªö–ꊮmÔ„KX„„|$ô`œ÷ýF•<æ6Ó[ië¾ÃfS¬‹IûF\Û 9TŠ@,"Ô!x7þß»_CÕq­:|òŒzåýoÔ_ÇŸ_W<ûX•÷TŸŸ'|óú$We°ž÷P·É{ë•$ÂzH­sþxeB(ºáOâja,ºd _smptˆI>Õ\g‚¸eâêö8A és˜ñp«5´Ç/Ôw¹Œ¶ ødý>¨§éañŲuê/¯b.ÖL5±1Œh+wðèúŠTk¶ì½`”Pj£Úµh¬öcÀ¶ýæÁµ€Ç‚›Ía Hæ½Èa‚¤¨36:\QêÁùgi µ]Kyí€Ô¦]‡L3€Õ[÷šq˜š7ªg^gý·nûx3vQýº·Ñé ª(i£ŽÎ‚$ŦN -ɇ7ÊFôçбÓnàú*ªÃ']Qƒ}gí®Ý$#D………©ŽÍâ± ·!kâ:0IWE·Ëwå @܈q$žÄ5¶&ÄÙ¼œ[åt `-|&Éžá›®Ä×ÂÚ7G­ù*¯=GÒ¤þyƒyoŒFœ©Þ¥ä 9iV«üf•y ¥•ÔY?>N Ô Ï€»¶ÖÍê—TËã™n„w@¿L{¤ê–]ðÌj`u#ãrñùý…f¬‹nhvËéÔÕóïÌW`=òú^ÕíªÜ‰4’qÄ™vª„×öéäÞ®_b]NûŒŒã™fè€ö­Ã~Â8ðçˆö<÷» êÉ—ß7måî@P0´_QU%ßÊÌE\¥ùÊ© ÅíƒÝu†Šã3DÜL~ýcSuF5Ii#R«’«º]¦>œ¿JõQhYÙc¯‡ÚäÓÅkпYfÿžúí3ôúÕ=Ú«çþ3׃@cx,^³Cý}¡ëº·JÄáKŸ l” …Ä0»^ Æ{—R£¢zàùÙp)÷ÔÖkiÏÂÝùë,Pî¾Iy;}»[}qáo›…•'Q°ÎUöµ¼{©†Vç +[]™×Yd¥ô—U%¬‡’—v‰‘©ü=]âÐìç™Y¹£nÚudÜ”Ôç›…Ny‰°t›Þú|žšy«ÆK¨Çn³åÔi”RÐÒÖ*çÓX1F‹Ü[¡}-·±øˆ\u(³¯Ž™ *×JƶˆƒEqy‚Uw0¾’¡gåä#W¡iˆVÕ$ÒP-ÂT,ÈE<¬¢ãâplOâJ|ËR›p.°ÐBl!_#V2&ß É¤:qu¿Ë/3ØTÕŒC‰GG„ûÿ®oÎù^ýñW7š;MƲA6ɒ΀ܚâ`îäèêM‰£öÒ奼d‡$.¼wÓŠ Óph¿Î&™(©øÜ›[ ½`΢×>\¬Z„0n”šE|Ð ]ýãíy¦ÝÄ­×z—üÕìè¯u+«ë9u‘}û¾#jé;š®Þ²÷Çô§ïŸš:zzRì¼_j”h¼žòÔšqÉiGVmÞÓD£„~–H5° Ò×Ùíÿï¼›„ÀX44_5/,PÙù.u¶QùêÌé<üˆ•Tã}´´Fø¤«Pì†*>Ü®b#íØ­QíiJ2H2âãÍ÷T›Ðë¤,i‡‡¹0€ñQÎM€Wº@¬Ø´Y‰šÒF¿ÜÇ-þIŒèCú˜½ÂûÎ^—ÁSpû+?4ŸK+|?m4(NfñLvÈ„‡V²CÎc!ÙþÓ}וõ”îçÔ×Óê¿ÒH ¬(ª a\¹*-z‹0-[füþÎëJv„Ö"ÊÅÑ|²Á÷Œi’ ÓP´¨Dsá­‡ÓÇ÷5ú|PºÃbadÃî‰?©!>$ÜUÑð“äË-Éà=¥ ±ç®j˜ßqNJ/ŸŠÔ³r4ìQ_,ݨ¯)ÝXºn—Z¶~·êÒ¦ Äâ;MUJ«¦ L—Udì4¥mÛ`ýNDLD¸«4„ÝŸÑ=0\£±)6­d‡´÷h“Øs@.˜¦ ƺ­Üו°¸?vú¬º)(Å ·ÉÈgD1½¥¦)÷fùÂ/Œw<ÝÆåtNƒÑƈؘ's• êÙÁnIÈªÒ æ4¡ñô¾]K$m!Ë@Ü‹þ—·«J•^¹çߟ-3í‰îre™õ1")£8ß ¬²`å3H¨”|U`¥íÕTK_Ô_ã jFjò×c“SGhÖÕÇʸïÖAš%ÙàŽå†iª° Ò5“‹)%fÆÄsÉŒ¬Ë$ÙðhR'±1Ä¥Dƒ8QªÁ$d$ŒhçveEž¾r$$ÀFòÚã3R’¿”ñI?j|þ˜?èí/0Ý]éMB‰ÁâÕÛá¿¿‰³¢Ô=·ô+‰‹1¹$¾þa‹J{óKu~<­ŒÈqTD˜p9¼D–lPŸ~»^ýå÷#ÊMvx1¢‘›_¤æ~¿I;›á§Yÿ~´|ã^õñ¢áB;C<+¥¦€×Ã݆ÓõFxhhó|\EgüŒ—lWµ_4vübÙzÓPžùGX=‰Ü$û*$\G¦¾ùr‘Ü^Õ¦+¼ÏTñì¼Ò´A‚Jÿp‘¢'%ÃÁnê¾Íjªù²ì=rC××ú¢€ ØŒ”¤—@6Ôê­ûž?z:S¾©Ÿ½´ÍUÚ#˜‹)Ȇ©.ª„zhË`ÔòL©+DƒØ‘@ðÇ’DÃ<(jÁpv bWºÐ&ƒêS’a’Œ¤—J_#Ÿò˜tßÍ|Õ¶žÉ)åàAwÖÒ®¤½:¶€wI UˆÍBY¶·_×ó\œ Hr/:å%;¼ïgý/è‡ç¤:ÆŒð©uºÿ¶Aæ&îƒù+UOx0173¯2‹kKØ ]Ü&Tfçæ«/áVÝ^•_#}ýÍ.¿  Û@†V¡¾Q7]És¨ù='5|€zãÓ¥ÝÐáÎW«_ÿìê’ï/¨ÀK8®“g²ñ‡¦ÍñR•T0Dƒ½2ÉFRÊ–#'ξ —§Äîí[HÅ«]Þ>ñ\U·tƒ +/wí4"³ì2<%u‰h”H6L"Fbá¶g±Î{Î8ãdÐ…•Þ%4ü¤MÕ%"ÉðDIÞ{Ò$óî²H†õ}Yñ(9ar+)µqN»Qwéÿè×½…q¤~ºªî(™(ÉËþ7÷;5åÛ.¨®¬Ì¨Žq#USäGatÛ rIñ9UC‡“h,[·Ã NÇ8EíŒW¯GHsff]²f›ú$2ôúÇÛsÕ_ÆÝafû¦7ÍžŒê*†Hfžûç'‹aˆyýOHÛº$éé}¡º#Úèåí[X·yý•y`°át™`sSûSÿS*ÉëmÑàè¨Fq8þÕ>CÏxd˞ñ6Ãh ıŽxõZD(2ƒ•*¥IEéÏ¥.ª¥ÿ8K¶‹PïFæÙ<-;¿ð°Ç³{}†Ÿb“a¡$¯‚€ à/à•ÿtãq®{G\ÈÞÞ'#vŽDrÄŠP´sð´ù(+3*¥»‰ ÜÌZAĺ¶m¡þûå÷¦àn„ƧýÈÖ=‡ÌÏêÅ€<ε[÷!ójg3í<ãÀüáùwLI ½¦Ø‡Îp'µ ãȼòÞ7êÞŸ T=¼-­ïùÚ9Uò íh{&_q¿üÞ7úÙìübd‡}ÀWA»ŽhÐs ßóxûüƒÉO_×Õ›³s Zgåå7Çãèv¬÷òU¨&!×Y·áïå[ë} ½BðÓØÑÐÓZ~EÿîíÌäŽ_]õo’±Ñ7÷So~ºD xÞÓ¨¼Ì¨H¢zAa‚6&#[¾q·j…(³= ]˜ I#¿v‡äå¢U[q?hÇ63‘Í’`´†´gÙŒÄoõãb`ôyDõ.#dÿÑSgM»ŒF^§Þþ|™i¸Ì´óÞ,XSUúG‹tHl`0bÜùzÊ”5ެ߳®€$ž<8Êgx¶UÕ÷ža§!‘\Õz|y¢¾%êø[4£%ŽW±úò¡)©^KK:èËv¥îšE ô¢‚°ô´‹â=¸+ …Jdz”ÆÝó;y¯Ts¦FŒÜü‚ê[}VО™ª¢ 7!\¾{ÑgÔÚ²2£23µZ¶E´êãfæ éo¦{χÝ󴌼Áí9ÒÞS”jôB;ěĞåôÙœ2{GÃNªfRþ9Ç”hô²gùïß›Þ2}‘Ê~ï¡ã ß©ÇïŽz=¯ªúû}ˆKóú‡ ]°!»—íWé©“çU½¶‹ßyá_Çů—+j) -ª… ƒƒJ&‡¿œÄb͘ûÇ‹ µtHÒí*  C³¬lñ^®tåÞÂ\.a~Y/ËíCmû‚Rkèo~¿a‹éüQFßÜßTwXm‘xX™Q'½üzö­/á­¨]°©6™4m–ú`þ óòn°—È8~vn FW$‚;†„•Ì·År÷-ý!õ(T¿ø®š‡4ìL´Æø/•úp;wø@$üN„DÄ*´é8 L† ìažºíº>ø>Ç´±®©ê+ã%}ºx-bÉ|¡gç"ÐMHŒ«Z_eïó?ªlsÁy§DÃË‘!½Øø¤”Á5ø˜F˜Y¹¦-N éx³/ÂÎz½óZ¡矫£XÅá¨RdPJ32hvN®”žR'†$"¬þ•„ù¹êÞ²tôÌÈ Ì/Ä6ÔóW¶ÔÅÈ ãS»*—ñÂÉGÿñ×·Øb‘2¡¦JY™Q-‰Æ¥ô‰ñœ¼áš{)mVæZH±éÑ©>Z¸Æ•y6†·ê-{´ý¯?õÔ™ÊÜ_Ýk*ÿ—PÝ–äþ€@`zjòb›¡ÝS ª/™Þsp†sÇ[45?ËA‡@‰íÄYÍëǘ»6fù•R}ˆ#wÁÄ–0b s ¦;&obÃŽÊ*úëŒO]+/¢¦Š§‘¨Õ‡Š¼¥¬kJ¿É(FŒ)JH’_ûØùÆ'ߪ¬¬Ü­´\3#5éwþ"ÄHˆFé'¥|NOKz‹Ï“%C5Œ»Ç%§>SòYÞ %$4’®¡ â£TtÎLá.¥úGâI\‰/uèæÕ¯=øk˜ž2ù;*èÉЪ7g/a&W}Ã΃&y þÑûf„ 8¶iw¥-Q|ñ=ã‚ÀVd­¦ÙGNO™Òƒ˜û¦åòk¢Q>6AýÍŒÔ)ÏÁjúUkØ•=Pð[Ÿå5x ß æÆWë†Ñfˆc¦b—RuˆCEOâja|)*“ª·o.æësÝöÆ6x“t…ñç#£‡Ö¸:yù†]йQXÂÃC]HXHMæ„Ô‘±œ¼"ØqYjoÍã‹õºf,¶iöF‹ógŒw>‰·A»„ú„h\XÁv)ÿ¸{á…{rNå-ÄÃÚO0bŠêïŒKž:$=eòò`o]¶Dƒ9p˜#(‰ö:7‰Vë3²Ôoü«aýê*4U7q£×AÏÄXOâJ|C€3ñ–R5FÅ­ûG<ιäßvòLÎH¸¥þjùF3F7¼5œm…4A@«†õbÍX šÅˆ±eE“µzš!Áß"3;ÉÕ2ÕAä>Ùwø”ªs-ÄüíÆek(rôàR:Ú#4¸U?^idÌcó–mÐmvÛ2Ãe|Q\TÜüdQq#´¨íZtt…PIÀKXÛmØl{bì[ž}òÉóî+þèh%Û¢QI ‚õ²'NÌëxþVåÌÿˆàö© לqIÏ^žú§Á:îº4.KuÂ…Y}#à‘Ø0Neæª G¯æ®P¿z¥H6*ñPP’A’AÜÚÔ 3q$žÄ•øRr$ª“JY‰Kè’Ë^çÁ_Zñ‰>X\û9›Ûomv^_ÃÐ[à7ë‚5 ;zƒ;rrãÏŒÞÓT¸p ö…÷x°@ ë³¶†ð+qíê0[ÈÜWÿ6i3»6&)mýòõ;S7ì÷GîÇÞHSÞDÀM4lX@¢µILt´*„h¶¨¸XµkìRá¶³êMÿx‰€¨7²QvnÓDâl`(¦ ëÛÀðó˜ CdÕvõlª$B±±1*13ˆ'q%¾nÏM³7'R꺇ãz†¸¥O¬OübÏE¥ù¨ã™E®â§Ïfÿ쇹ýu}§GXÍˆŽ u1&MxX˜ £HU4þ‘wé¤EN±:´‚BÚTœ/¸RI:`èÆ*¼.×BŒ¥¯ÿå©5¨‚·M¢4SYýÌLyjÕø¤©£]J‡1¨1£ê«œÇ߇Öçt§ÕoDj¸Tøƒã>ÌŸ¯K½Ý}=Åù4Å¡¹/ÉpÁb½Xâ#rÕ¡ÌBµçÐ1sA¥›f ϸ˜HÜS÷~&èy@ÁH~ðw€ãJ5‰4T‹„0Å€\ññ±*.É$Ä•øVKmâžßóóíž:ù¿†˜æxòº0ýÜ¡q<Ó¼Xwvƒ6¦%è@˼g³¼‚¢XP˜‡+r“iâ;>:$C9[”œÚ°ŸÐŽÛ5ãˆaÓŽ†!{l¢vM›0¡×””tGRÉû`yS÷~A‚eæ|4Žé©“?§›+þBø‡Å%nÄ7vÒ v¼ùYþó'\t¼RJ¤XuL.IYÜ$Æ¢¡ùªy!¢ˆæ»ÔÙB—*rBÄ{:×Õ½:7”¡0êlf¨øp»Š´Ãƒj§HS’A’‘o¾§Ú„^'>fxmþÝ3-ÿ{WO“F˜R*‹€Ê"U‡®Cb¸t $_SS8lP›”vú´:CÀ ä ¿ ¸˜‹O•V~‹P„aʈv¯a”\Ð~ƒbÿp42\yTd¡ª¤RûšÓ±0Œq°2zŽ/èËa{I°!¡0m2 Ñ $ÃT”H3ªgZPTl`~i‡ E:„hÝ”zg@ Ic“R[aq¹—5‚l¤Ž™’z`fZÒÛÞiAj¹¸Â΢â“ÈSÀ÷U"l‹'N.ªîÏn·×p¨J @4 U1ˆŒì ùÐM²QH† Š.”PPB¼,oªIh“iT™ð{âX’™•Ëù=…:̹®N]r¯ hÑ´ ¤þ´lô€vðD3,2CÙ-¬noŒIJ9235ù›@êf÷ÅZxŒ¢‚ü£Ùù…Zì¢@ ªRΓ‹7ÑÙpïØÃ Ɉ4I†i(ZTdfº„{àD#˜…ç„%ÌI#~R-Â8$”jÐð“äË-ɰ$!U™Î'ç•ó‹ûKæ»*uÉ=‚@ " D#g%@úĨr:^¾« 8k)~ÿzÀ¶)dã£i׼!@ºÌÝð\t\{w¯hØ<ñ÷ág߯{Û*›‹"Õ%é°\_¹˜R’QTT|Nšá2%˜wSmR¤ÄÄ<@«)Ñ 6¡&!#¹5¥n“êJ28‰œO`«q~ñ‘Á<ç—Hj5B4jõôù¾óÓ²¡o¸^ !ƆA»8W±ñåCSR¼––tÐ÷=¨ó-Ð-„‹kåâù[z tfÕæ= UVŸXˆr‘t“ ͽ˜ÂNÃT—À…Æ¢–Á¨å™RWˆñqcã¶_!Ù@ÆK#ó=ˆ……eu^1ŸÐLꙜ_ÔcÎ5^Ýî@Õ©Xî!2Ü Fè{8ùïÊTñ2ìµ°¿MDô›¹p¼8è%Çc™Ü÷ZÞ7kgËŇnrEGîÿX³ÙØyà¨êЪiµ‡G¢ÁÅ“ 'ÕT£Ðø“ä‚ÄÂ:ØP]"%’ àCl,ãPë|µ?Wçá®5Î+Náà<{J5Î])/‚@íE@ˆFí;¿öüÕ”I›Ç'¥Œtiê+ ¸-¨n¹ÎÜOŽY7;£ø)Å7pgk’ ¼.3ûÃQOùî¼&ýöV[8/œÎÓ¡#'³8oœ?Î#Î'ç•G{Q›)Áƒ€ƒÏ\úu$‡¦?ö ÷äœÊ[cþj h³þθä©CÒS&/÷kg‚¿1O©7´5íbV-š¿dËÊ›¿õ6׺ ñPm±‘áFB|´ÊÐ'Rj†‹g$Wã¢æ'ëàîó¾ûò³9¹¹Y§Ñ'’ zm!Ù–©6±ì3Dš@¤B4‚g.ý>’'NÌëxþVåÌÿD£œ!#•áš3.éÙ«ÓSÿ´Óï î=¥y0 ­¢¯?øß;þ'Ý®èשeûŽ=#££†ED$Øí!‘Á KàŽÎårædæçæž<¸kÇúÍkVlGoi‡a©J(É Éi@¼ÑÞ¹õËÈf8?ù ãé[\N'z©Æ8Âp~ÞxÇs¦;ž8î—NÔFH6h,ÈB’a}¦ÈúýH,f986à=ÿ¶)ù Ä"&x+ÅOXóCÃNªC8oœ#Ó ¯$L¢ÆƒóÇï9ŸR C@ˆFÐM©ÿôºã©=c’Ÿ¡)çb(H6Úê΂ÏÇ:ÒÏpŒ“Œ”Þ›kñâ¢äùž qŽÀÁ$(a8Ê"B8Œ ç…Åš‹hP-bB’ ’~æyË.ú§¤Áƒ€à™ËÉÌ”§VOš:)¸` jØA8ú*çñ÷gÍšuǨQ£ÄÿÒ{³ÃÅÈrä˜ïI<¸h…âð$–- † €à§Â9²Î+èçŠä‚¯<øH2‚”àE@ˆFðέßG6=uòçtsɘn6n¨ßlØI7Øñ~ïLð7Èʼn‹_­EÌ’b`X$Ãò,¢PüT8/,ÖYdÃ"Ögë{÷Õò¿ ¤ÑÒ‰­©aÍHMNÙ`òµ)ì²E›”v±7ÒjªOAÜ.4kGlÙXXvž’ !þ{,¢ÁWë ©°ëœÿz$- 5ˆ€?X›ÙH›”Ú .}÷rŒ ©c¦¤˜™–ôv°Ž¹†Çe-\\ÈX< †õÙüBþóœ«Xóc}–WA N!`‰UëÔ e°~@ e£Ïá«%¬|oŒIJj}–WŸ"À…ÍÚ=óÕÕË«ÿ°ðÄß“tøtâ¥rA ¢ˆ³}š1n\qxHÜ]Ø\ÓÕòcƒ‘£>zБÖ#†'CA@¨$B4* ”\véLsLȲGªáläݰۈs_>4%µå¥×&w‚€ ÔF„hÔÆY«E}žž””ª… ƒÕC-S²‘X¬sÿàx1¡ Cº*‚€ PE„hT8¹­ò¼š2i3|-G‚l0~ÙF·\gî'Ç,Æ|"‚€ ÄÑâÉ ¤¡MOM^l3´ûa³á6Œ3ŒÁÎo1ÙT õSú"‚€ à]„hxO©­ÒÓ’Þ…½Æ“%—ÆÝã’SŸ)ù,oA@‚!A7¥= îzNÓlŒjˆ>Áh¢ÖgyA@.„h×|ÖŠÑ4™Ö‘Uk%A@(A@ˆF ò¦¦˜™òÔ*›²†Í“~Q²Ñ×pÖ¬Yˆó%EA 6# D£6Ï^õ}zêäÏ5Í8ïæj¨ßlØYâDC•¡‚€ P§¢Q§¦;° 5J:T(iV/ C76)mŠõY^A@jB4jßœuA6’=ômk ©c¦¤Þk}–WA@Ú…€Ú5_u£·-=²ñ5X$CycLRÊPë³¼ ‚€ Ô„hÔž¹ª3=1n\qxHÜ]0ÝÀAÃ%dã£i=ê 2PA@‚!A2‘Á6ŒiŽ YöH5’ƒ„hÔ¾9“WéII¡ZÈ0¥©L^ÉFb±fÌýƒãÅ„JÜ.—‚€ x !^Rª <^M™´Á4F‚l™½3T·\gî'Ǭ°Àë­ôHàD@ˆFpΫŒêÓS“Û í~Ølæ)ÜáÜñ–a0Ý"‚€ àk„høa©¿ÆHOKz¬bRIG ãîqɩϔ|–7‚€ >C@ˆ†Ï •Š iIÏjš­$Z( DŸ›”r>šh uVú"‚@! D#ˆ&S†R1ÍC&O@¶ÙW½<6iêŸå­ ‚€—=õE¥.ÿ῾mS9qFqq(rjØ`Yh³‡„®P=?ª8*?·8çŒUÍÌ´dÁÔ#_{á…ÈœSy á‡ÒŸÝÓ”–¯ÙlCÒS&//«»cÓÓCCOŠ+.²á0l˜\÷Qì*Æ™æ?¢2ýµqîÍé7¾Å›}x†v@U³#T¹Ö¼’š¼·2uÈ5‚€ #øi¬›åÁ¤g:¹TÑÝš²ÝªFo0‹¨ˆpgƒ„h[ý¸[½¸h¦"ÂCUDX¨ ±›d‚„ *v¢†b§y俍¬ÜB•›¯NœÉvžÉʵ¹tÝ$(¸¾•¯Á-ŸÚBíïMw<µ¯n¢£v8…v.£ ý˜ÊHÌ¿Ù1£A|´«ABŒ=!&R‹‰RáæÜó(ÃM&)ÒpºtUtnþ ‹Uv^Ž|•™•§?“­çæ†X£µÛl'tC_`³i³BÆ})¤ÓBF^A . PçˆÆXÇÔÎÊ¥?oèj¸Ý¦m[6Ö:µnªµkÑX%6ª§âb"½2ï$"'3³Õ£§Õ#'Õö}GôýGOCÈa`=Ó>3Œ'ËÚé:ÿŠ8â:Ö ‹`C¹€ÿÔƒ<¥^c±:5¥íkºÊ…Çf†²ÙÙõCáõC÷½8qb¾W:Ä•ÀÛd¼MiÉN]oéêrYs{§6MU«& T³†ñ*dÒ¥°¨XeœÈ4ç~oÆ µyÏaWN^8‡vZW*ùÆË;¦5ÊUº­‡Ï6uêÅ]àŒÛ’±z˜oÎ=^Uæsoã>'•®2 ö9f·œæxòPéºä³ €@Ð êÒÕš™iÏÖõ›aØl119ž»ÇqISïTšþ¿HlSo¾º»}ÀåíUlt¥$ã^™«ìܵ|ã.õõòÎü‚âlî ²;íä"C±ˆÜ`·Ù{»tWKp” T5¤èáa!ºK74HIÞhNèd,)Œ»sš ‹Øv™U·¬ ÑB2`•W:à•¼ýõ¯o…çDŸ´g¹\!Ñaö‚ðB£Iᤳ( ŸíЉٺn ïÚ6ÑrU­kÛX¯ý7¸Ý‡Ž«…«¶k¶ì×Ôþ>.'»h®ë7Úl¶˜÷nxëÙ#Hà Î=Šáœ»À].—†q\ðŒ`î³@LVsîaI²"Â7Ÿ¡Ø=ë’÷‚€ Ô~ü™­þðfÍše_¼eçåøÁíßÛÞX$`K¡µÆt"าZ°i6ûi™0@ßѾm‹Fáþâ[tdxY—ûåv¶Ê‘>Û•›WˆµÁe£êǺÚ4mho‚]uó†õT“q*6*BEE†©ûOwÙ.ˆî3sòT4*3;O=yV8vÊØwè„++¯ÀÛc·»ãžcWúÇ \å—Áù°‘Ç/ÔÏÑóûDöpì6Õ­@¼š€l˜8z6çCÇügƒMœ2t½*’N¿~µÔ»£çe~¿`åVõÁüTÅ8ѧ›Þ6±±Ö¼a‚æžÿõ©¶).“ åšóÎù?u6W:FÉÙ)×Áã§5<ø³Ðœ-b{ï5ÇŸŽú} Ò  @ à‰sSä;sîÂf~4~<A×mê6@œêÅÙb£´z±Q¦ÊÃŽ• ´ Ô¥+UXäTyù…*m(°èª{v5¤ÞQTç Z±iÚ¼ëê±}×Ëš«úñ1Õ©î‚{a' 6í>¤6í:dlÞ›¡»m§aèÓÂBâÿ]›vºci}”S¿„âv…ö(m)Õ‹vÕ‹‹ áÜã! ¿À†ÂÀ£Í¹‡­Ä¹ù/T/k¦nêßý¬jâIâ¿?[fö»KÛæª}ËÆ°ÿ)1é¨V— éP{ŸP›vTëvt9‘ !H2ØBó‚—-©Vr³ —ˆ@ÀñÉO÷Öu׬·b·Ö¨~œ«W‡–ö6‰ÔeÍzua¾DÌjÕåN,®ÞºO}»f›[p0[6vÑK íø²Ã1Ê$ÀFÄ\$‡];Æ‚.> UBG°$½{ÛZûÖM4Î}«¦ UXèO¥<6Œ€èΉ3YjÉšíjÙ†]®üüBðMÛr]ÙþßÌ”§VD¥‚€ ôÑxÈñt{,Ž©>ŒŠŒÕ¯íÝÉ~e×ËT˦õƒ~2|=ÀGO©/–®7Öï8­Šm¯®´ßÎL™ü­¯Û­lý´µxðÏS‘Šîrµhß²‰ëêžíí½;µV‘ðþ‘RuŠNõÃÆ=ê³o×¹²róißñïÐÐz¾æx8§êµÊ‚€ \€"c’Ó~i3ԛح† íßÍ>ôªn¦kéŇ!W\ ÛöQïÌ[î:8S0|,=5yÚ¥Üï‹kËDÏuýÒ–ám[4Öo¿®·­S›f¾hªN×IwìyßmPó–oÏÔv@5bFjòî: Š ^|Š@À1É©ÿPº1±C«¦úïG^kc )¾C€î—ÿþ|™Z»u?Ȇö$sø®µŠk~(é™.ð£ù v5‰¿¸ñ*ÛuWt®øù¶ÚìØTÍøx±+¿ 03ÔÚKÜc« ©T å Šî1IiOÁR3iÈU]Õw\«E„û^LÎZºšî8pÔ<ÎÀs£EãzåÀäßÓ4Üu𘂦Ϧ'Ë].SÇNg©Ã'Ï ¹bðÐ¥k¾]¸Ïg –SñÿKKkw¥ÑQáÍ'þú{ÏŽ­Ê¹Ò{§O!¾ ÜLÍyÏÉ+Tñ±‘^3ÆœñÑ"Ó~ˆÁÞ<ËÖ½‡Õ³ÿþ2 ŒQÙ/zµôèØÒ¶|ý®ˆbÝy݈ë“ÿµxñ¿MW`Ï~Ë{A@ª‹À¾øÕ­¬*÷Oš:$#­ÿåív³æúGÈ‚Zjù†Ýªž)<¨Ã®¨Ìøx‘‚E—xí;@ÿüæÿfÄ@,Œ±†ÍÐþìµT²"º+ä«ÏCmZóÿ÷Ëí­š6¨äÕ»ìdfŽúàfGÖYIDATrÙzU 5Âò ;Õ/½¯2Ž—¤«©Vå}º´Q ±ˆ—.°?Aü ÿÌié¶ËûÜ´A¼ºwÄ@Üů<¬sy×ÉyA@ªƒ@J4Ö£–|Ö¬aB½GF…¢H£DÔon»FÑŋ⨬V]àÉpãŸ.Z£N!NÁ¾Ã'Õ‚[ÔÑSgM²h’¦ëìwëv¨ù?l2ñç6IÒ;s—c¡‰U/\­Î"ÎUv2wÏ_}¿ÑŒ}ÑmÁS‡4á´±»ëxß‘IœI­Û¾^!ª[»Õ™ÛJÝË~ ¶íû·ê˯®Z°ÀoÑEëuìyâˆ<ô;H±:ûÑãˆÆê-ûÔÄ{oQWuo‹ù=¡xŽx—ž¿ÖÍÀM8C}¾äGuòlŽjR?Þ 9ÿÖgKUÇVM1_¡GîRo~ºÔœ¿Õ[öšD#!6áè Ô¼ï7Àãg+ž§PµóÀ1uóÕ—›óÂgmÎ’ujoÆqSºÀ¸.$¿[öV‡Ah?Y¸FõéÒZÁM¤RóXÕ‹ õÛ5Û] ÛεK~ZÕzä>A@ÊCÀ·¿båµzîüçÓw`7Õá¶ëzÙÃ߅ޝÞj{&ºÆ]øè›Õ¦í²õ;UÏ-ABê#j¹®úv»Lцå_s–˜Ääº+;«¹0®[³mŸy~îƒêµ*Æwh×¢‰†œÁ™ÎB53 g“Íj=b°ÌÀ³BÝ>¸©*Y´j‹yÞßÿ…‚Ô`ÃmS¹úO·â>ìŒÍ®9Ú4o¤_)@M•ä)9v*K%žS›•ž¿E«·©¬R=:µ2¯ûÇÛsU ©!ŸZ²Â²qg†I$×S¼?3ÛÍÕÒ¡Fa ­+»¶U+7Ÿ··[«.عï„4……z|î˜?mîŒw`Çëï fv@¼víØ+©Sø«ý±Ž¿·‚*¡ ÜWk䤪ìϯ¤&M›…' ¦dûçü­E ’Á “}A0yóUê8âRP…vuöjÕæ=æ-«@"úwogгê ÙiÅ×_[˜6j°‡+%V´‘ } Ì‘´ð9da¿5D êåŸè¥ÌÃr6'|C[nõ]^A@ð&Þ GXÅ!zc§& ã°ÔÕLiUů)Ô³ {«úùLv®BWϯJÞ3â$ªXº\Ö‘I#J¾/mkШ^lÉ"Dé¥#,ÿýò{sÁbk†§4ÅßåÓÅk(ÒǾV{‹ßæÁ·»³1K’àïqÓvì7˜‰ÔJKÓ<çï¤ ”:°0R¥ÅÐïý@,>Z°Æ´íØ€¯IÜvÁ‰–Q:ãÎåÒaôR«Ððá:Í”(Ü5ä*Ä qGP§T„ê”ÓP ¾ñéWˆÝv¨‰­Y»8ûcÌÒ† øÙMZÄ~þ8vSÖG¿¿b0¥”PPÏÎòl4øcOuÆ›P0²& E$“Xò^«\Ùµ©^¹6HîÕJkÕÀ‹!è­v*zå³—2sŽëôÙܳ.Cv8~[PÑõò UE F%ø±ÿ1+'ÿ>Zü×ÄÎvËž 5áÙ·Mìhàycÿnˆž¸[ýeìíPkĨ·íS³¡3ÿùоêzˆÏÓ?ZlŠÙ'ýögæ9Úf<ùòû )ººÄäÚ>.in¸²‹©¢‰€(m?˜ÝÓ×…!©i'òýúÌ[ ýüøé)SÞòu»¥ë‹¶m/ÊÕ];ö±woï;7ÞÒí^êç»o鯦¸H=þâ»0ö¬oªÓ,MÚܼòÞ|ó\YõÞ5ä ¯QŸ/ýÑT­X×Ð𕪗çþ3×|v(ÝH}øçÖ×>{¥šl-ži%ëˆkÃ8ÖÙucäkiSÜFC>kY*ºŒ@9{mÿ@ò§gž‰ÍÊqîoײIüc¿º ¹Ÿj´;4%\¤ùKc¨Êöà‘›Ü,4ñS‘fxΈ¼o#p鿜^ìÁwß|Stå57‚ÛàtéëÕ©%spx±ïVÅÅ¢´ .IFés—Ò*wÇþ ìuÿ0`Õ"ÃCU!Ä0¹yEÝáÑ~7 Üçš!g×,Y°íRú^Ýkû_sÃPXÝ÷ãöá½;·¶Q=¨¥ªÏ%綢烞Gþ*õã£Už¶™_WìrÊSÙú\{}þ7Ü´cõâþ ã¯K;‚€ øa}ñqŽMNû½2ôô qÆ/†^é—èïUð_‘›‚½ Zå‚×x”ö¥=Òvß«“'Ÿò×èt¤õ0\ÆÒаÐhä7±Ó;#Ð%[þÂÆ×íPU·®´«6ïu»Š@Šª 5š¯Ç)õ ‚@Í"DƒŒw¤^¥éÚLl´{4¬纪kûåZÁ`/Á´ö¯Y˜‚¿u.8³¾^é‚Ñãá{T—ùmw;>55Qj¯Á綸è(Wßnmìp˜AÔèå!Å·ää¨wçý ÖÀèJ•¿ÌLKú›o[”ÚA .!0Dƒ 3$õ‚ »F*͸¤nF&Ïìn†õbuäA1w»uirü=VÆrxæ­Ïux༃Œž÷ú»ý±N»^Ó1Xìî„WJ¸{îc0÷Ýdîý0$KÖn×m!jÀtGÒJ?4)M‚@@   "FåJOüazÊ”Ðl†ÅZ&rDh ¦Å\R|‹Ý=á® sWí—¾ü²;x„o›¼ ö›²(=5é]é˜í„9÷gó4™û `òÙ‡_ÜØW!΋¡«_ù¬©Xê'—~Èñt{Xø¿ª®›bc"]7õï®õì`Œ¨¾¡àçK×™1<3„.X¹ÅŒØY:Û¦¿ž¸™b¹CÑe6Jëf%IÎÓÙôÕu»(ø©c9žmêt½b1¹?\t7¾¶wG›0«:Ý`nfêÒ·k‰—ÇÁ£§UƉ3Š ýjª0So,¢ÏÞ9äÊšêBI» bÖªI}Û®CÇ»—œ”7‚€ T€"c’SÉx‰Åì·^ÛG]wEG;ü¼Q(ùI¬ÝÑ1îv„€Ž5«¿b“jÓ¼¡¼¼v¬dR,Û%ÇÉ(¯>ÏóÜ­»tw°0Ïó5õþDfšÖ W„}¿?ûÀ,¾.WÑÿì6{ô°A—«ûu³{Ó>ƒ ˾@ÆVÄmQ÷ `íÀÑ“frµŠˆ3®N}ó3•<ævŸÀA×W¨‰|RwU*=vú,;³·*÷Ê=‚€ ”…@À±SR§ºñHŽ-{6PcŽ_ff}÷«Ô#£oüIõŒ‰ñÃÆ]ffW¦ûfÒ+æ«`†Ö˜¨0µmßaÕ½m •WX¤†öë†îì«· èa&d[†l® ÞEω3ˆôù= ,#ãëÕÈ[Ñ Ñ$Y˜ôzêúu’àÐ=Ö*™Yyf¯á×ô4à/ÁÉ@fu…ŠŠ÷cmÝãÍW’±5[ö¹àq»%}Ò¤³Þ¬»¢ºèuCÐmš6Ôçµvf¿õEéÞ.Q} Ò€Ôe‰ Ò3ª~»v;$ áj`¯f^Î×Ñ“gÕŒ©0Nfž“Ž­›˜™^×ï8 VnÚ£î¿mI`?˜¿RõDòµH”ÇŒ¯+7íV-‘Ëæj´ƒ:Iv˜ß„î¥ðôPþâú ú° ߯B}£nºJA›ÌÌȹ|N† ìés7èíú 㬪³¥tL>‚€ P ίrÕ¨¤º·‚dü áȹ)´üÅŸ‘ öÓ)õ¸™µt¿ËÊÈÊÌCÑ¡U3Ø/@즨naY·ã ˆÈ‰sÖúJ-Y³Ý\D(¥H{cŽ*@0¦®H=þï9KK®)Ôê-ÿ_|gžj¤mLòõå² ŠÒ…Ÿ]Ó Ò–(Å4â¾.߮ݦ1ÒÛ˜)¾n˪Ü”ÔkéÚÜ£C õÄo†ùŒd°=†§êäs¿û‰Í aËʨڴQu1Ô|WdÚmlæ¸!I`!±Ü "º}ÿ1ÅÀ[ WmE~œhUVÆW^_QvV>Gÿüd±ê×£ï„ièëƒp‘ÐR­çëX+”ÜÀÔ…¸.;›‡üû+Eo PãDc¼ã¹ÆXØR‚Û`¦K_î,¯AÍÂî³°¨˜Í&ËËÈÊÝu$‚ñ•)Ý»´iŽ]fž)ÍØ„dZC¯ê®6ï9¤˜híà±Sx$bá9Š:‘, ¡Ëûuo«ú#Üôš­ç¥ÑžÙAÙ8ûðÊû TWHK˜û„…‹âö}´+8¦®F¨ëªD¥4+ªäÜm8¥®Ù´¹éKú¬’·Uû2´÷BƒøXý÷#kåEϬv#ç*`>›×öÂ\¹É¢Çê]VFÕŒã™*DƒÁº8÷|8GÛ0/ŒºûÐ ¨÷º¨­˜Îy£z1æ5åe|e7ÊÊÎz’¬WÞûFýjØ@Õ±•[òÕùvV"þYÄ:©Hµã l(ÉzóÓ%êØ©L8ýè8×;½Q¯Ô!‚¨q¢¡ƒ lø þqµÅê`¨.˜muηk¡ºpÛ€”•‘õ ä)]"U³mbcµiwwÿ´#A‚¬<3µt¬$¹ÛÐ~]‘]t0S¸«)¯~dî†y­/ÊNI¾úÁ|6&Ä„ŽöEeÕ9Öñ|C¨L®¸öŠN!a¡Þ±Å)«ë¦Þ̾:úæ~ê³%?RM`}¥*ʨZrÞ0¡¥OËA`8©z´o¢yXí:xLu‡ôŠ¥¼Œ¯ü®¬ì¬›‘s‡’³íûð³Œÿù ¦ºlö¢µp9þÒÌk}çÍWf˜}û‹ïL‰›¡i­ùkoÖ/u ‚€ PãDTÇL{î{Õ€5ÝŒ<ù+.^½]eç¹ @EYi/’}.s+ëè]ÿ²w(fö¤q)¥‹Vo-Yh˜ÙõÈ#/R¤¾™;€ª¼Òê’?Þ7 dCS}³Ú¼Œöí[5:a¸‚m¬™N¾¼û«s~1úýÒÿ¾fìŒÝÊzÓ³O>™]ú.å^[H9éV’²K¹·:×Ò눉Íh'c•ò2ªrµ °Ø¼”„¨æ…¸uÅsÀd€ùønëÞæšŒ•—ñÕj«ôë Øƒ<2zˆZ›Žµ[÷›_Ó–cPïŽ 9€Ôþ#'Už#o­ÿ{ç+ƒžO„Ÿ<3%é5o·!õ ‚€ PãDCS¶Uœ†•[öøu6Z7khz‘X g–ֽЕ3#ë‘­óÇíî}ª.–‚X0ƒ'½H4˜q–¯,\pø™¯,Í n}sõú ÔŸ§l~îy{ó»òþãbûû‘×a§¼Ë\læ}¿I=ñÒû* ÒŒæ`ˆØ²¼[«tžáÇßœ½Äxï« ö1_„ÙâúNw<áûô±½ž””°çµ_øá-ç‡ê«xfTôòêYHØ'JŸ¨6™4m–ú`þ órkþ/?'ÁèŠÌ¿$`$ÊÂŒ¯9y…æó2ïûd|5/(ã?&•»wø@Sº@[Žé,Tú¿÷IÕ0Ø.QåçͲj¿Ô7æ8w\S)â=» ¯£Éª¤Ñµùž›hTcIñ.GNfrîéŽ é‘Q¾ùçÄÐ/‰á§wq–ÚAà§Ñ`·è}b¸ ÿ‰÷­Uë·îmëpÎÿ§Ý–3•E€n“ŒÍ°róngn~ |;µ©õþeeëðÇuã’SÇ£„„„„ÿlPûµ}:©@NïLªÛF>â½ü¸m?æ~¾mïQ8ø¨ºéa!aÏ¿âxòpuë—ûA@¨ C4¬Îbwûͦžáh; $ÔêbëÙ±5b Ll1««ùJÑøN¸ÄîÂo'‡11Ý+>µi¶iÓS&G§ž’ںئ^Gà¶aá¡!ú ÞlìŒÞZ¬çæjñ1‘8¢Ìˆ¢ŒBÊï²ax/Ý*‹Šœ¦Ÿ(¶¯™0ðX¬i¶ìö„Ù¯9ιX;òýƒŽ´.§1ý¹bþPįÐûtnmcÔÍ60âõe¼ã§³áª¼OmÙ“a\Ó»£Ö·[Û‡}Q³®Ñ££"l˜37 ­g9ÿôˆk®qìÔYܬíÀ Óšr±>ólvõnº#ÉíÊTã#‘‚@]D ‰†5üÑ—”6¿›wb7þs,ü¦ëE½Ø(gÛC.Kldº6LˆQú^réÃaØïýÈyÁ[x5¶í=b;…Ûf; žQo`Ïöscørq³Æ[Ö+È…zgÞrcÍ–ý äa1"–'ÂF´ mD1\'BFB~ÚÐõP‰ìVÛl!¶ï^ÿËäÍ5eàYÖ8ªrέN+º„i$,U‡t d»Þ¢IÕ®E#[k„ænŒÀfêÅV)r*{™T¡ÇËdçÁã®#'΀¨i)¡N…Úmõ'Üs“^'5Uè6ûÏO–¸ò 2@ Nknz}¼ºÐÇb<ñP‡¨xVwâÜnÍf¬‹²E-ÉEM͘´+¥¸ô•¹t ~üü#­—Óe\‹E¶?HÅ@H;ZYÍãGØ€¡«~l”j§~Ÿ_™KÄË7zpàÇX1„‚á:››gs:uÓÅ?ÚdÛ±ö,ÀâýQ¢ý©¥GŠÓ&#ÃÔß'è· ìaÇ®Zù#æûº18êzŦ=®"g±ËP¶g¦LyÓo]{5 †õ¬!˜Âþˆ¤Š¬hz_ÀaáêjmÄDGbîCñ „)œ3¿&žæ3éƒmeæäc÷ŸkääTPß ›–e( ¾¾úg°aøÄ¥4§®/À3Ó…’ëûv1CÃ[íùò•.Ôwd^}Ï¡6ܵºÍ>r†cÒ_¶+u ‚€ à jÑ( À£S§6ræ«ÎXHÚbÚ E[¸k6ÆN>î²õð>¤$ $‰]ŸçñªçbÝ9¤)ã0Åaì–÷ÙC´#â¢Ö¿8q¢;z—GCcþœ6 «ÑóØývŠwumÛÜÞ‰ÙZ7mˆ…'¾Zî‹l†ª“grÔÑS™ØUŸæaì8pD§ d*‰=ßÄrùü«iIû=ºUçßΚ5˾`Ë®N`ító®T;Mé­±«OÀ|ש ³³A:¡!Ö¼W^­ÏÃq<ŠË0œ¸nk¨r­y%5yoiPÇ:Ò£ ׉0ч@8" IquÃü3Z êb’êÆà`JUöfw!ô¹©þÁØ‚þ>ÓÌ~õ;âR]¤å~A@¨)j5ÑðhTáŒON½YWÚÏ¡®¸‹Ž™ú“ª $ÑrÕ¶'ÄD™Éà"`´†´?÷š±0ŠaûAiJA¡ÓŒFŠäzfNžéÊ9½:G£™™SA„¾³k¶/BÇÌŸ6aB¡¿Æ)í”ÀÃS§6(Î×ïE¤‘Û1Ÿ×€´šDÀ-E‰aÒ;{|l4¼dBM)Jž<æ¼³Fªh˜W‡ñYrá3 ¹r0÷Î̬| Ï„Y¯ƒzð,ž–å ÈߪPÛìŽÉÛx^Š µ!—8{nÒñ\{l’{CWÞ ä£ 6Í- ºie3ŒzX„¢`¨÷¬;ÐÂØòñ £Lã$È$*ÆLÀAð˜íˆž´#Ä¿¹6m^"tAqùãÏ=“UŒy×{kºê‚ùn‰œx­@šARƒy´Ô1ž¦TsŸ C›,xÕ@š¢ã0¥*»™s»-DÛöê_&í&yõ¼OÞ ‚€ Pۢფ׌ë´Ý4ˆŠÎ3\v»^–ZÆMK•5Œ‰èÏ?•—¥qîÙ¬¸¸¢ãƹ“¥Ôpÿ¤yA@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@º‰Àÿ @Ä∮IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-compconn2.graffle0000664000175000017500000001216213553660046027066 0ustar zuulzuul00000000000000‹í]ëSÛȲÿ¼ù+tóõ€˜÷cO6§d7Ù%„HrÏU·„-@'Brd9„ÝÊÿ~{$ÛzÚØÆ€m†­mi^M÷¯»§{úÅ¿¾_…Î7?éqôËsì¢çŽuân]üòüãɯÛêù¿^>{ñ?ûï÷NþsôÚé…A?uŽ>¾:x»ç<ßÞÙÙíõBggÿdß9:x{|â@;;¯Ÿ;Ï/Ó´÷óÎÎõõµë™Rn'¾2û;GIÜó“ôæÛ† n7í>‡nòÖ+ëݠ“¾|öÓ‹/þÍËÝN|ó¼?yuýï/vÌU¸D©á'/Ñ‹Ñ×Qè;èx)4ù)oy\ÉKÏ|ùéE?Mà‘_ÂÝø* .’xÐsß÷ßïü<ôÅ‹a‘Ri,´K Ÿ‰‰*y±3j: w»ÿôÓq×i2ðwF÷_y/¦Ë¨ Ýõ.ƒÎ¸ØðÙ‡ÅL‰þèÞxÿ¶ôcËù›sä\HÄ™dг-G*â*¥%\BRaÂü¨<‹il/ôúÍvã0(T«òv¿(?špRžû¼ØqzúEÉÑãä7¡ÉøKq·t;¿¿Ÿx×ŰŠ¾/&{‚0ÛÆx#ËŸ)ù™qçþ£3ÄI½þ;/M?à¥{×^âÕ+íý^èÝw¼°Ñ3Ð$ü9+çgü½ÞÀ¯AèŸÜô•= ƒzÙlíÇÁ•¥uO$iÌäpaö i!èêJ«QN‰t(Å®à”3L%#Hp¾åP†\Ì%“ŠR±æÔ—¤®D@BBK‚׈‹(—2Á¸àLk«ÔÕF^}]z=¿À†“GéÛè<žL°˜ŠW[½Ÿ8+Ýw‹\FGŽ6ªp1¡’Xʶ I{Jµ 2-=` u¿ïùѱõ·ý«à,»µf2¶üå—«ft£ÆôSé¬Î®ŠUÅ1¯p¬¼è;ï"òÓòû¯ªŸ&ó‚ŸJ<4Hó~›¯}/ŒÝæë®rÊÆ»>ÂpÚ«®¯…J‰–Õ0œ3ä*Á@Å/Íbë²×Ô\jÆŠš¤¥fÒ^SiFK5Y¥fe¡ÔMÿÒëÆ×Óf¢Ž“`£¥í-q–s’+žÏ<Ë9íM¯Ù>Ër]üMœåÑÃ%‘Ÿ|ðºÁ : êªWÒNüïéäe\½[Ói’žãS ú û§Ó»I…œvâNì™[ ±üGp¿™DÏþ>=’ž…§çèô< à³sé%}?ENƒ‡üó”þyšø]ÂùéEâû‘ùr|øÌn(š_ÇÐ¥¹Ž1Ú§=/鞦߹@ð/\Ë>„2„°ìC!óAiV„ê¬cY޲"PÛ|œ$Y»Ayé ñÂÓ¯s! ..S?:÷:€ÝèÙ3ó„g§ç},œÓÎ9qÞÏ‚w•þ¸Mš±V.ËÀNhªã(à á@ªp!¡À78ˆ1ua5+"0FŠj%·.\¡‰Æq@R­ ±8¸>8HÌ$Xdˆk…¸fËž©Æ‚¤Œ„8'b %XŠQeqÐâ ÅÁ™qðèòæÈ C?½Ž“/Œç‰s9è@3þÜð8 ‚È¿BÜ{4ZÉŽzG©ù|êQyà"mÏ'¨U ûå÷€Úîź»Ks]ÿ<õÎ@þ væŸÑ¥IØyŸ ¡ü•Äß‚®Ÿ8Q.šF:Û½€Î.¼t¢6ä”@ÚM𬾻XrŸ±ý^n9 ¾ еŒD™ÈlÒ5 ›4Ú› ¨-§~³‡´e<ˆ°8±´Y&)ïAÒ ýÕÑÞ Qfü'fj†EBg–d$ˆñõÑÙgVk—Á\¡[ =P)¦#,ÝÎF·t}é–ºQÅ@C`@W3“-—H܉´"J3¥g×E` IÍ)=†À*aØRðÒ( —)…•D@¯RrÚT€.2 ­2° e€XeÀ*+© 8d¬|:Ø=t0"¢ J–@àû®F ¦E Íe<‰ ³ç¹)ƒŸtF1¯áƒzÆI|H§ @uºKcPÛ¸ý}®”XpBú 7¶©TæÇÝp§iºš"¶.6™wáX’ûÎ%¹ Å{؈yA…)¹D›ù¼oÕ"Ç(ç‚`J¥ÒsZä)ÕÐ Vsç¸TÌØYªcé+?ò½ÚÎÞÎúÔXŸš606ýæ7X®y‚VÒ¹îúY¢)QÔ­FJø*æšý!E3‹}k€}r~… =5ìÛ8«¶…> }› }lh„ç8}v<8Û. ;Eë´ž¤ô›{ð$EzaORŒ]…á5b…`Ùdþ x>')1‘ßÚ¨5(gñôIz’®6ÎYOÒ…&ù`üô-Óšª…+•qz§\ X°a—r³XSFõd(+áΓUÎx8¢3Ö=Dç{Uc•]R¬2GhÎXeã sƒŒC$E˜ã¹c•Ét/ÐQõÒ£äΫ …î*B€PP%Èë)G9o´çSá^ùXíoƒKÌ]Á%ü"³©–@T`PL%å° ¡ŠFf^`ŽQaa×Âî,ɲŽÑbNØå³”a—ÃÒçØ¨`RjeaÖ¬…Y ³·Àì }[VÀÌT¼ÁK }ãæð3× <Áe6)ôm‘À9úfCß¡oG^j2–ÝS°Ü²CàÐ='Sxj=@p0Zfš™S“8$ãÔåŒRÄüË5UF?¬žó%™å–CnDpp#NóN„»ædw&56éˆnž‡'q‹sÊBð×Ò¥¸2ŒÎl9Q!Ñg÷)À×{X=ï:¦õ&úä¯w‘dÊù{.jΞ"á“6É­Oõ)°y$oq)xç§^×K½ÓgGIÜñûKõ›ÃD!É9ÕHH©òT%æ|Ì"UI¾¥‚(¢˜iEüODælµªnXƒ–°WË…¼UZYðÙÈã&2wE´øäŽ‹EM2g㢦=pÕ‚Ÿ¹ÀgÿÍÞ‘sè]ùý^vÞÜ 8· ½È¿v/;½Û7ŽB÷•_fê£X‹Ør-ŒS1N.Õ"v·=ƒ[j[‹˜µˆ­ûž°{Kåâþ9¤X”C¶Yý1‘.çÀå•L÷Ô‰rþj9¤åëºgÀë@Ñæ)½B´kü?sM&K¥1¬J"¤²–—%X^¸µ¼XË‹µ¼XËË X^rßWIнX)»ËYòõA¬.ómÍ+î¡Uh®(&zBª:*¶A\*AÌÈc«Ûš_Wèd:-tZè´Ð¹Iâú©MË÷@HUßJ—Òæ•{´Ñ¥K³9U ü§ä$†:Õ>Æ0æ|:ζ3qÆ€¨XÑ¥²hiÑÒžY³”ÄrØ&–{ôÄrL©E÷q"Ô&–[&Ðq@,…L ›è˜&q¦@ÉÓs§”cÓ;¦v#h™Aôñ6‚û;Äf–{J¼êM´š­8´Š€U¬"ð`©åˆËq”6µÜ“€7)-¼Yx³ðfáms˵ªm6·Ü“6.,°Y`³Àfmcõ¶†ë‚M.÷tàÙär -Z0´Ùå&Ù0mv¹§]Ž»CgÁÍ‚›7›^î¾ÓË)áfQõ\hC¥H—ÓË!sˆ$˜^î±Å0%–•(FÑ󳙓 –„ØÄ1ì ù”s«€D3Œi¢*»Ámj•ef0SÜ•ØØ•ÂŒÊÔÓÈ`öxœ/‹³sµª)Àl¦MËÉ-'·I²îƳ,ßÿ©™--Œgžó.‰²Q<6Šgͼlì`Ú/ïÄ!ÉxI>x‘PÅéµ)g.¥H_ЂšŸ&‹””8RÎ1S©fè‡òê"A©†f°š™EJ†¥ \*fœOa6Ðq©¬±³l¹]cŠä„uÓ…åÁºÖ<[Ï™±­<¸©ÌŽ,óÄÞ²'F+›ðɤTxƒSRLæ°0éœ)©‚5 ‹Yq6_J@dv:ª†ný@¬ˆ=àðS⊊8HlJÀG¤¶§ëÚÓu-øXðYÁ”€Ø¦Ü|‹¿ÿR—i[|‡Ô¦»²±'°Aj3.—AÞÿ)_ø$Ø6«ÿì`¯–CZ¹®[õˆ¿<¶Rš(T3•6%àƒ…@­åÅZ^¬åÅZ^V-% ¶)'lÍ#­™´):‰²Ði¡ÓB§…ÎUJ ˆ;% &®4º¬oÅ­@É£š¹sÐê%hõBqfJkãˆncaï KXÛ¾‚ ½§ÐÐÝ0¸ˆÚ %¨Íª²º¤XZ×?O½3`BCîfþ]šýIÐðœÎ$þtýÄè:N¾þYΤ>ì:SR®Ýk|>–Ü•Æ1‰Â’m9 =#BS¦0Ëâó±tQÙHcpíÐ<¥²%0³ 6Î2átË é„þÊo¹AÉ:ïw‡`f9oŒv¡Óèµkl¿@…Z#Ê0.HÐÔP¯@„h–‹À်µ•+îJþTXÁž9±˜¨0í¬JZ¬}Û…ðp˜ ýÓyî“¡­n/¾êÅ(7}Ç‹ºð3ŠüN| қǑ=˜‹k»ËÍíj÷@q ƒHKŒŒP²åª\Œ »S”HAÅZ¤HÏ-sÄÔä9bj2;†¸›…ðþ8'ÇlùœsC zë w3þ™£–¹Â1óÿ„¤a¡…”r>ãŸÀ äb¥@õ¡OÏø·þ6¿*Ð Rß9Œ»þ½ØüŠ-‰¬•ߨËx1~•ã»Ç=¯c÷0tfqU5.´X8ÓÿÆIkõ±Jm€¹_÷M­¢x»Tž—޼^ÿ$ž¡|ñôÙS @"éÄ/þ¸b+û(•þôƒ³‚ÓOè&ó\:ò.ü1iµ»Üä(s÷@ÄHýd†âø~ï$>îx¥QŒœrç&ïÆOŠžÇ[O5©Æ<øm"Q^Ҹ˷xCšnÜ,~?gU¨ŽÃ ã÷gƧÀ¿¾µÙI FÒÉK|7 ®¼Ôo, šˆhîv‚¤¿ ¢ý ŸÖÈ¡|RHQöØz¯8¶dÔʇÙ0_GåãGcéÆic0fû+6îl~tQÚe2‚w­@Ò¬8ùõç Hï½`¦òUbƒ!~™D=Mbn´ÎQ¾Ôs|é—vgó…?¦Šwq78:Ù¨÷KozÔ(ÈÝFz› ÌfägJ˜™kô•5T¢Õáíw^š^ú×Î ¯{×0?õЇqêÏþHï“ze’Û•†%3sã†7½‚÷ô¥?CYCΓ çðøUœ¦ñÕ;/ë¡´ã=lï<Œ½´JÖÃï WI¸`Yyoâ$ø+޼ÆDµ…×쨠ÙmíèÕnúW÷Íïá>cäÿï«ðÏ·oâ]öïWÿ~{üúr÷òýGÄñÙçßÃÎÅüî¾ÿˆÿ¼<{ó)Ü…û{¯ùÉ ùý럟ÑîÛϯw¿B™_¿‡×{¿Mþ(¸w™ÃU¦µª`RaÒ-Íç­íýxÛc3ôQQ¡Úúh˜ÁÆNÜš»Dr.$âL2Е·År.!©@üqËÌBüàpó[¨cVÂ#ôÝçR8‰{÷Ñ|•—g/â}ä.2Aùà{Ý÷Qxs;{ù_W¶Ý¦ˆ>P´&FE/¤Î¡ø8p¤MN¸çEß¼~EhÉk\@ž ÃHѹ¨·k K³HzYe# ˜-ú9ë~Œ‚¯¿04Lyú}ÿu”É´iÿ4«Ðù9ˆ@³žÌÕ÷I3‘ÍdSfoÙ_ïyÛÿåS\’é Zn’ §*íã™ÌD’f;uØØù%­úÇÊAÅ´õ"MþNåþ‡AXt£¼¡3¯t»ˆ(߯ºïO{v ºãÐ;{ãê®+H¼.A AúƒQa^åóq±Kk™ó¯]—[•²v­eÆþŒã«º Z>êz\蓺p¿•u´8ºµÓO­íiÙ=©ºmWæe ¾S|öB ´—Ïþ˜§-ï^7neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-overview.graffle0000664000175000017500000001441413553660046027732 0ustar zuulzuul00000000000000‹í]kSÛHÖþ<ùzóuAôý2›ÍÌ$»$a3ywŠª·„-Œ6Âbd“ÊOK¾È’ll#À˜ÃÔÄ`uëÒê~žsës^ýý‹Ø»ÓA”ôÿö’úä¥ö;I7ê÷þöò—㟶ÍË¿¿~ñêö?íÿûð­wGƒÌ;üåÍÁû=ïåöÎÎîåeîììï{‡ïŽ=8ÇÎÎÛ/½—çYvùãÎÎÍ͸V~'¹p ;‡ir¦Ù·8Ù6tð»Y÷%\¦8ûÔíÀ·Ý¨“½~ñë¯á·×»,º‚oaú¾ß ÿxµã¾…ƒQ? {aúš¼Úý:ê׎:A§üµ8ó¸S¦ûå‡Wƒ,…G~ wè'ý¨—&W—þ'øíç48;‹CõjgؤԚ*ë3·Ï”¦Ì”š¼Úº¸…«,Ùíþçj/¥WáÎèø› óÕ]²ß…Ë]žGq³á³›¹ƒÑ±ñmüù'ÙòÈ÷-ïO)ᆴ”J)´0RlyÚ0ß«á+¢ eòû÷©gq'Û‹ƒAý¼GIMn¨Òåýþ¤ýhÀYyì‹fGÙ·8œ´=NqN™|-.Žï§ÁÍä¶&7öñSùv`°Ç£4úmòK1¼ƒð·$¹X`²ìýë`ð)zÑdšŒÆ9æò[Î{$ñÕE7ŽzýÚùiýüyë£Ë §·Oà ~ÍaŽå¿ŒšÂnÒîYX½F¨Ø¦t›ê9ûQHï/~jwçN’¤Õþ‚,óþ /=¸ Ò Úi?\ÆÁ·£N×® k~¼Î…÷7oü{õ?Eqxüí²Ö9€uPm›O°ý¤suö³ê$«äpb†4,èé™VY9¥¥#(<ãŠZ%©¶Bð-áøÕ(k­¤TçkKùÊ2K9‘RPkƒ†ÆçB ©$ƒõe­¢Ó‹«iuM–×yp6­¯á&ýì}ÿ,™½<`.MÞìôñ¢ÁMéàäN]ª´xJ×mXsŸ.ÃþQÐl…Ñiw+§É{ôß°Ü5ŸÍz<«§.V‘É»ROáHÑôCÐë‡YyÇïú‡Ù+ô‡7"RmPÏ¢8ž7¦ÕAŸjQ49-Žñµô¥dü#Kã2êÙkìi,!”Mzò†žicOK-_“NõœzõÕi08ºÉͼ‘xö[Î›æØ¿ßÝ2˪Ðßâ@ n$Q³zÞ@ ¢)¬ã¹=›š3Á•´“Ÿ™=z¸´¦Ÿƒnt5˜zƼ—)÷.¯™Êò9ÿÈfÏä飓5r’fgô–r”ÿÓ¹ìPÀN:I' Ü!ADñÇàêþš¼øóä P!;OÎÈÉY?‚ÏÎyÂŒx5`øëwhßqo:üõ$ »LÊ“^†}÷Ëi|Âg~Àðâ{ —tßSÊ ÷ÉevO²?¤"ð/|—(ã>ù‡!îƒó¼ ·yl÷!IÞz»Eó&JCwÞ(íÙUÄ'¿wÜYÔ;ÏÂþYК$/^¸'<=9PåtΘ÷!Ì‚n'/@h턃Á÷Ûd;pÝò(å>L`Ã`åíH>J>|ƒÉ‡S.¨„ù—$n`ªÁb9ò!ŠÃªäZS«‘|||–!Ÿýw{‡ÞÇà"€Â.Í<3°ÿ ê‡w@þý.Œ¯Ã,ê‹b³Óéf‚ó»0èξ…2tÏÁîaã©§™¡œ/L¦øê6vÜúÖ·V@õ0M8=£ÆÙŽ4•LÁtRÊ|¯ŒÞ¸·¡¹@X¨Z.@TÐÄ'ÚhH«˜´ZÏì]¹¶Ð›i_JK¤àZ(+ »wÝŽÙUsÎ,{΃¶6ŒSA å (Ò¶8`+¨+ —ÑRŠ, Ø0ôR¨Íƒ—"nl·vÓ4i©ÈôØ=Â^Ðù6Õü,ˆ¹ÅqÒp l¥™·:F°Dñrwò%êVh:T¶(ËÀ—ÇÝ ¬"ÑésXQL*Á,ãºq•‚@¯ŒÒ˜°†+øŸ)E·¼Ñw€@n ô-ô„l¢@oL5kˆ4ë€>‹[“¬MÓMFÎ2Ú,.ÐèPÉH¹–pƒ= ô(ЯbMòv{a?›%ÐOFü0¹ß9Fm×îBVN¹‡—æÌOFY %i8eÖ>“­˜ïädA&UdSd«ÌOlSÙª„¡dI¶“žlI¶šôÈVÈVÈVK›ŸÖ©`Ì| ::1ZX=•Üò¸U¾qjåF[n…tLUwŸlyuçÉC2UÛœb–ånš8%®úãîÅÉU·þ˜H@kl5CžAžig¼Ãóo0HAì} ³›$ýꤦ æUÎÞ§ËÝ…{Yí+®D¤ V iŸ¨Ëý´Ñ*[€h´ôšÛÐÙÔ!Ñ!GÌÖ­ÔÒº•‘›¨[ÁlF˜%ÜZR> Fç°&ÿ¤£B ™n˜Î]·8 ªcŒ¬Õ½üIáhèñY3 ,˜$Dåü»Öøð µRv¤¬æHölc)F1c3òjXk@;ÇWý~Ÿ¼x“FÝ^›ú<ˆïvÊâµt4‚ÌóÈÌíÁf aFæAæY…§—æYï~êA¬[PO­2B[*•0Ú"ý<¨™ ·o¢âƒôƒô³ÑŠOyè–'¹ñ]è„ñ…ޤÍt$)t$¡# y I›æHª„ƒµåqA|*µÐ†sgÕS˜‘àACÂh‘mJÇ|&çÞn<ŽêªcH[¨Ž­ÊZŸ“+ ­“«ç4˜ÚÇ|Y²ûY· m¬)1´"ù ù ù¬ù¦ÉuÔuôÓº5°¾m¸Gi_S%9ÓD3ʦ}Xî¡÷¦7‘ò{ƒ¨F!“!“!“mDPEM‘B×Öóqmmd¶$¢¹ä„Ïò8̓_ܘ\œÜ´ Z±É…$’’º¶Vtm±{VѤ¡>%ŒKc¨!’+ŒTP¶ÑvÙ† š§ \)1ƒ Ÿ¡–é™)1aa™a¸`º8@øú2 ms.¾pL¢AG²ZK`å2/M ÓÂR‘g)"(E´H!QV™83VmÑ:.£À¨éçp£zë)Žî-³‘$M¡æw„ý;äÛyÔ Ø«œ{9ð™ªq6;¿×ã@ÕÝ <Ýð, NR†Xåþ}5 TÎŒrÆgÛƒ0½ŽUúE›ùYÓ¾Àb®¯Âé·µ Qí»ýj”»Ýr’J ($¸ŸK¯pÐÏÜ I93µ öª~'ÅäJk·´À (QK$I‹ÒN®O–´<‡=Ÿet¸5‡½Y%†7ÏaoÛ”=ï7ÑÙ²YhºŠÑÕúÄŽ(çÜ­@*aÙ*Þ <XÐ\¢ìpWÙA¢ì€²ÃZÈŸ®Ã4¾Ä†wÜé*ÔÕvõ”AUxQBø¤ùí ISßyŒ©ÕÔ/E8JU`OW’Ø=@ÏA”xnŽ ¹ïâ^’ðU‹{içAVT€ÒeAX³Äå!˜.ºÅg—çjê-%óµÛ`ʘ%\¸è±çXž«ðÜ .¥b kc—ôÜ¢8·pj–tâJ Hô¥Öçj·>W5*¼íú\Ïé2ãÉW‚Fö8Ðh-¬cW5Íea=*X;] Ñ(ù<¡‘Á8BC”dL, RP*…e”H÷Y\lÂj-$ç’º«SŽÐˆÐøÌ¡‘® pËw¬q)|îŒ~ Ôh—Z„/#5 b|ê°ÑédN™›Õ™á;W¥±T2É”‘›å•••k[ðWDD啯“­„ÊdUT®ev·ƒ[¹÷°%'Êak¾la`¥RúpeXÆ€Þ‚@‡e0½rã–ÞN'Xå«|?UP¦­€‚r»ã9·&‚]”›€qa+Â*ŽÈˆÈøT‘Q£¡UdÔ÷íz9ûWFÆÛœGwÃÕg£Éç¾pJ©QÚ¡ÔrnqÁàƒ0j£KºÅµ„nàU1„Æv¡Q!4¶õÚ‹¡nÓ›´ÚÕ*Ó@oB}Í3 Â"‡#³zsZÄ>1êªÝë"«+ª½Ýôè¦ß¬$ˆ•k€•jeÉrŸ—µ@Ëpë´^i¥Ú—4š€¶GŒ¢Ka¥R~igô3(bå-Ûƒi^- ^ƒ`ËíVJ1+ˆÕv©-ÃÊ*¸(—zjÊV²ºòQ€R>Ž Í˜òµKôÇ£ )èLA®á „j\ay„IT¿)Qý^¤«"eÅ4nyu C´C´C´´“ˆv­úaäýûaVÞT´‘tËc”ùônÆë z¦¶½ °"4"4>Uh´¡±Mhli<çBãÊ[€˜²¾³ÀQ΄páâ‹Cã*ÀŠÁ;¼ó4‘‘W#AÚFÆÙéôP1Lki€jM]QÆå{PÆ'0ñ¬á þgJÁ’TÌwSDͨ±3¤¶Püob†Ô<Û:Y¥ìD‘w½T|cñ²yöIOyS1o*–X&mêAð-L·¹·Û ûÙüŒDnÀã ^„e‘öi•ÜDʇ©Í-è†Êùh¸#+;ü’r /nL‘œHrß%QS’ ë¢L\r¢êöGýŠT«y‰*Å"^¢JE¾vÕ³¢Y>o¸AVDVDVDV\Vtç]ÝDYç|-¨‘™a¶ßq­Š-qã;5è’3­°áCWºàXË"c!cmv!\xDQ£µ6Œiœ²UÝ}ËÊÜ#­:PŠÜs/6D®å¦jKÒâBÙ‰TtImÉ¥%æŒgï^R[¢†)dK¸AîAîAîY†{~ŠÒð&ˆã6ë.1é;_°$.Ї&±ÚWÜ…^KA¬ëÕnb½ZÆìæÖ«]Ûí”HnHnXºöáJ׎”*‚‘oÔ[^½œmÁq…JÕ½(ULkTªP©BÞA¥j-hgýN›†¸&¤fZÜ ÈKÀ9"2"òM 9&xb¢ª2mŠªõúr(ª"0>{`¬9.7@“ zcb¬žèž±‘±OEzäår"ë¥R!ë™{,ÑÙŒÎæGrâbx€éâáëäDÛ râÃÌô FÃta…†ì\ŸÖ¸¡#7ôsÜÐú‘à0šêoèn)+kº34©6 M‹ç.Dú çr&•`–q%@­e²P€]•ÃãXÐ Úl€^ÜsAôŸÎxnVÂDù8˜ÈŒò èf pr ¤Ë@cz#l8PT 1111qeL”+Õ¸Qú‘äDÊ|ˆÈ8,z-©E˜ç"7m¹¸ec1P£•r^’>Ôa9/„:„º{½¨ïôw–§éš£ C/4ô‚ÉÍ ½XOÔBoz«0£íp ° ´3m…D{ó3¤¡Œ½Jx3CsÂ: ÝÊ–SJ¹²a @Ëpkœ¼8> í¨5> µRYX]VóÛ½I%SFE#rå€j{îhÑ’ñì-L>–%CRÐ` `!犌²³rJ’`R©»ÇÕR—ò“JaR©5H*õ!è½5X$mÂââÅÃjE#$‚*‚*‚j¨G+7´ Êýïi]ƬºÆ ŽVØ'ºm÷±¯g& Ó«·›&q¦ÞǤ>rÐ È ¾v60µ è „G¥Œï¢Tµ ’[ 3ÞQ‹¥N¶Ãlw£%nf#Àlk‘à0M®£.àÐ0Á£ä ZúÚmU!BS—TH¥‹TDÔj0«†ùP0ñ@ FäÓM<@ügô,‘fžª¨Ôóåèf1JP†Ää’ó´•$‡B?‚YÅ”€… ï Þ3WÔ*I“/aÍ}íÖ«²PÂI×ôÄ÷µûkšhqrÕ]@Û¸((T¾QùÞð¨fÿøý·/Éîû/ow~‡6?ýßìý<ÿö³ì.ÃãPeÞùAûJ§@ºáôÅGãù£A²ˆ®1¥t5]£Å2’ùFÆ"¥‰Z)¶<#Š ð(£ ~¿åÜDü^¹… «ã)Ì„ÏNê»Ï©pœ\ÞÇé§±<Ÿú¡C‘2Èç0è~êÇßn‡—ÏÉÍÔ¾¸9¢4­ˆ‘ëð‰Ô9Ž£¬Ž„{Aÿ:L -E ÈóÛpRt!êív²èzI/ïì„ ß —ìûK?úý*œØ&æ<ý/ƒðm?‹ÒyÃþë¢Bç—¨ÊølTß»JS‰|$ë2{ƒÈþöË ‹íÿŠ!.ÉtÚzÒ)¤šÄðqÁÁŠä*Fõ–§¹™¶Bµƒ)sÒ«,½ w¦Ž¾Š' ]k¯è4(ž˜'Ëǧ­‡óVÝQœ¾ Ýò®jH²*B YúsØ›B¯ñ@lSåö¢¸ÌË–ùªüãj°5ým¢ß’ä¢*šú²r®Ñ¯¨¿ƒF°hð4,ŸÏ_ª9&§lr¼ªaËäó2†ÅôúÅÿ53 ­neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-flowew1.png0000664000175000017500000027721413553660046025745 0ustar zuulzuul00000000000000‰PNG  IHDRà°@²¬ÿsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì]|Å÷ŸÙ»K%„ÞTDE{ÅÞ{ïš"bRô$ Õ®{ûÿÀÞQ@±€ t ]júíîü¿o“ ›Ë%¹$—ä’¼ù|îvwÊ›7ß)oæM‚ #À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À4*d£J-'6¬xÐût‹l_NGÛ>É›¸*¬™eæ‚B€ó4(˜ê¥§û¼ã;ø|‡¸…ØðRzò¿õ2uÌ4°kØÆëýÖ½M,ììZÁ Þ›‚I퀔ô[•2ã2ÒR&㿾ø±±pò«„0£DÔžg½îqÚWõý~ïøNùzÁ@)"ߘšöèêÊÐÉÕóNSB}V ›ï Ü­• ®~aîZÞY=Þvýõ×áÊ·?_U­á–§Îü0Ú·ø7cÀŸZß÷ÓZ/qmE»Í^ï]yN·Æþž¯ë× ¡žÕ¥– ,Ò;UIƒÀÛÅ®ºO_mH±QH÷?ÿ|dþ¶}¯B0yÒÓ?ÎHJÚRQ˜úâncáÏo¶Èý“S „™šhÊóòä´„W¥Má믔HÒ× Vš@ siüzÉ*˜o‘B~«¹å„)Þ¤eášô†T'œù!ÿÝNBƒ„G™&/Ûœ!sçf‘y&<Í/Ó#;0U@@«B˜ä…¡Có…T_ )~ìàKÜÖ +Å)µÉôƒÐ"¤ü?<Ѹ¨ö¦©Ö•ï×Þ´ª”v)çÞ¡Ô7U ßP90—R¾…¹ŸïÔ¥Ôm†O-MHN®IoÀubÄ`oú±µ‰;¨û'¥-’”Ö¥6ãå¸Â?® ìP=_Q.¥*ÁÃ> ÿmiIƒýM7.^í×_ƒ ¾ÊôÉWàÞËßOEßSG'‘àíZ‘¿ÆæóŸ~::{WîØî Ëó )ã~ÊHùg8bÓÐê„”b'45­tÝ|j铼޾¤˜¨q“Ÿm^ŒúÕSx\‘5Göð¸šY„Œ$]5É„EðŒ‘#÷JOTB3G%x§ÆTÄØŒ3\UMUÃy½¯EQ¼ñæïNáüíêòû™áÃsÑJ…0øX(!Lý’Šø &Oê2UÍSJ7ÍÏÒ¯" ¹ƒK‰pJ¾ ܇>.Sÿé‘nUü w©.¯"ù ŠÇ2bª4Æeб­½ÞÕáǦSŸŸU*ìõ9ÁÁðuàÏð×tjjò‘PbpRh,{%—d¤'÷OHJ»jê{RÒNJÆc>/S*ù¥æ‰L™â}d»†žÃ¼Ï4Ëѳâu+F}ÈÎß Lsº©ŒW0Rš95-e¹HN½‘B›hÄJj,ŸTŸ!~_tŒ¼ø¹¤¤j«Ì3¼ïD:sбˆ‰öìŒ96¯…¸h¿—ƒRÆöÖ•ñàœ%«.ÆÂ©æÀãNàòëÜŽ]"Ò1 éxÁk?ï÷>ß4_ß?ôûæmÛwÔqÛ¡òÿó O˜ºyh(Ñ Û¿ýLðŽë,t= q›éËl¿y‰Ðû'§m‚_+Œí/ÆÝä炲ï˜#„n`®Ož˜éÛtøÌ„ß…šÇõðï¨õv¸º|"±s‘ÏW"Õ=m>l5·ví9=û8“¸FùvôFº×OKK>ÄöKÀ¥¿¿c6ë›â€ëù‹G‰QΪ(§_ ¾C]Ò}ËäÔQ¿Ú4œÏAÞôž†nÎf2Ò’/ ·²ê¹U5O)¬×«´Í¾ô‡Q®ÎÔç[ð¸<¾†º2‰ü8Mepq†óý)Ý÷˜BÿÓ%£œ|”áM\áﯢï`Ë×ÀäÔs !FCàŸN4}>ó+`j-SX{áqGÜ¢ëùßóÿ€¹åÇ7üžïçQ5ŒfM=§L1b¿ÓÝÎ3”£?§¥¥`qT¡©LٰÄãþ)cO Ú4%³¥Ç}]†wäF¢? yÂa¦(z{I¦oU[h$ò{ÖBü†òÿQGWÒ¯Wš6/ ýÉ#à9ŒB|*ÊáþN–½T¢OÄûtƒ–¨ÈŸ£ñxÂ×…ïÓ—¿À¿Wç‹Èu=„9ÔŸ¦ý ákùQ²ƒmÑ&>ïhƒ$¹Ö¶§'U¬)^G˜c¤&§†BøÝ!Þ1ÝA3÷&š”~DÜ{pòø# Óø=„†Ââ!¹PhšÕx™J4§t`¸ÑsšÞ±]ó|û~Az€~$ú6ïà÷-0<\ùÄ<øíçôo¿[óeºïOнu= -lù*âobÅ¥d+%´Ù€äe£…+×Ì®†ðE|’¦þÆœ÷TTòÕȧkLŸñÅ>¶ßº|‚w«#Œ´í³ù°q4MÑ ¶^ÏcYsŒÚ¾Aº¿·ýÑ´ABrú,e*tvÔÈ‘„k.òêèU—$<–^<²–B[H˜éʼæáÿÔ uùVÔµ å½…µmQô¬jžRðGÇËô¥‰´‡jƒô¿Þ?B]ꀸ^„ÐùˆÊ¹3Ê`qq† ôŽ>fJê¨ßQŽ& þH¡›¯Pg ß²ì*S¾L){ ]›‘ÆÝ=)VAö,¥Ÿ¦ÄŠ—¼nÅ»é=mPòøÒm’7Yy"T}YúEþ<é>u>¹cÀ°Îv«lÙ°ÃUãÞô£!|g"ãvkÚöð-l[  üÞò´eùmä÷'H·t÷ÃPç†Æ$| oÛ¥.ا pGiÂuéÔ´Ä/í`Ô˜ìÞ_°éÈ-¾ÌÁ°Úv«ê“¿ 0ߌÑ ­)ƒ½æ[1ây l*ÀSS“HPWÛЂŸnd bdIáz A4ð‡è¢`&®Ùññ‘÷<ùÈ#Ùü²3u},ì»C¼Õ¦é­ÖâXP÷EOKCÅ(† O¡qiŠÆå1RÙÚ~ î| oûÞ­pká–/Nò&­²Ý¼O¶RzîkøÖÜqÆdo2i ,CtxÞ3”œš0uêÑmE±ÃÕÜÓ<£¶ù{©8L3vgÍ¥]8etÒ,w™­'C€œL¿M<7OÃ4‚í§JúÂT¯ C½…ý¸Ýžñß%Ü®7¤nzQNo„ºw¸ÿÜ'©ö1Ú¾ Y­^·i•õ¬jž½½û (¿/@Yú?”‡»ìòP¨1Êþ <^9 9ývøyƒü—0àRÂo ©bÈ:²uÓÑØõp5â:m³‘~¬žäÝß®²å+#5ùEÐx÷_ð·¹Ý‘HKI·ã4©ÝHø’fÀTj(aêŽußJsøÎÐÓR“þ9úÊNsp’Bn4Á  åTë­ú‚ þé}î²Õýà¿=FÐó ^ëïîü®Nž¢3Ù­-:yR¹Ý®dgy )(”†S\ðƒrQÚ”‡Kiߥm0:‹"[ŠqÜ !×ÄÑ—öÀ¦Ê—Ëe `>ÎqƸUŸÕð<åöoŒ/AÇÑcû¡wàq&ÒÑ*n>ÙW¥lØôìge1¦3Pç |+tào@ù)î X4•êLO̓΢Ÿ¡3^òŽ Xÿý¼6¨OÀUÈN—p•è}Ú$Ü.ñ½£¢lÛ…âIs©4_¡^µÜ á3ßB%Œ”.yÍÙV:©\ƒù—¢Ÿ’mеߤ9ÅÐ n*w‘“¦=ž6‚o0ïs*4Ê›üàïÐ}þ!Iƒät{t4hJ8éÏmïnýoiwQ¨rS²¹ŸÛ¥ôí’Ú+~öÖ'T±?Ñ‹!Õ1ÜCm‡d´€ªø!ÌÍ> ¡;Ï×¾^ºr=TÇÏ!mùæz[»áŒn‹§¤%~î´³ßuMž Lß›µvhCk°Èê®Ü\µ 9¦@(iòilù!UOUÌZ¨€Žp$5×6ñã º®’Ðö륫®„îè´¦Í]ZUê$àÒµ+Tæp‘«8[Vê›Ðð”p&AÑùwÔùÈ46_ Ç£`|_C0J\JO2EjÔ®jÊŒNËàBç¿y(}¡Aëæ´ ô>À;¦‡išGú»EDißMJLDžcTk¤íIò‰Žmöàà“çd”˜8%9™‡•2Èé2±Æ´Dï²Ì‘j[1rýf‘îრÊÏöžŽeê›öÀîrš'´GÎOœ»wOþ•4¢nù~)fü,ªš§U´[?ÿçGZlÖÓ(o “T¹ûr)¯–9ý”‡‹Ó_°ïM›E&îÛ“9ò¨?ÞƒNoÀŽÑ uù²y¤|HHNÃ4–8mðc¨cˆúŽvÉéWBî:¯g÷Eß,]ÓL)ã>L+P]°ê?jÌ9Due=ÉTµl†.üctÜ ²ôœ÷Ð˜6%-ù'û]vnýœÚ¸ã ä醮¯D¾ªiZÆä'gW¶CoÓ¬ïOt†ÙTæ¾Ä• S‘D”ã² -ˆ‚4 eUè1QrBÙ¾+ïBs¤ªíè>ã*Ðÿ…æ—çü¹ú®@”Z ,©¤Áx¹£DŠmeE¼Ý”&ߦ0§£I@gÐB(ŸÑ?9ýyàÑ. Û¹OiÓýá¯Ú£’ªè”¼úuC7ÔÛ†®¬§i˜7¡Á{ßÿçË.-”Ë¢t“ ótôÓ4ϱ±ž&ÍÑ jކ~XY—haDQ&Ö˜§ídÅWnäFš¤qáA¸ÚáHE%ÿ‡3Jfù®µhàoÿ¾‚«€g,Üf3½P­<ªKQ¼‡–‘?À=òý•RÚòp±ÓS™'¥¥ ƒøž¤2Fï(“VÐ娟¡._ÏS8‡«ë¢/Ù xlìIÈ»ŽÀèSêaƒsUuÊÅC&XŒÑdµÄ¤Öu˜Ï¹uòÎl_ö«~µEÄï:„°z—~…[•Œ€1 šïû“Q°. ñ×B^{ ¢íQVLYÑa\[8š à6¬Ê÷. jŒ’2Iª€×LI¿3€÷Xac„e°=#Dé[O¤4©ÊL'Òd’Ñó.Õ ÁZ„¢c+ÑpMÈ­Ñd´ì„=“7úŸ×M\@ýü/ðjB Cª›4 ó1Oø¤ÿ¯¼‘kuã *¼&[þ¤ìZ–ÿcÆ`±òQÉ^ïõ¶¿)ÞäŸÑHÿ…AÔY4Ú{ =Öôƒ`\Méµý•÷¬Nžb…õ*‹¶¬ø|öòxµ[Œ'ö!ಷ+h9ÊQ©Uþ¡._Î4œ×«û"Ä¿k&€°’—Aî÷´ib©›É¯KÉ鉭€çAøžŽzÏÙdWlªQ6Šiù"51qÚè䤧õ*SÀî*¬sVQði©£~Á~ö[1">åNG9h VEtê³; àZȽƒ!ä~T” ÞôãG©Îl/0¶’ 8]ÒiÓÒ?p{î¡9!,Û|–ý—®êöf_ ‹‰æ?ªNÃ/¤),ZhX®¢94?×ÂOÎÈžV¦îs“° 险éÉÓi4Y¾” ˆ†$¢Ùì"µ¸_‘K(êZš¿ ˜¦u‡e/ÕOþîÈ‹×ÐXJºÕ&7ׄ*ã%_÷÷Wæw5òÛ¡¡ÑÅhH]3dÌlM cÕU©®0´LZT`ΪX¾¤°nTÒ¤Þ.]R3#_æZmÅãc.Dæö€þÂ^\Ea¢<±8LEúPAÏE§´EGÅó¿ô]ݲaÑ òý«c—áãÑÜ´Ó!9;!!e ‡Ul2RAÄ=à( Â8¬â Ç àÚËË¿­¨ e-rFÛÿ±´kÐÛ (€±ßûûäÃhh×`ßíƒîEïˆÍXÝ;ˆFx>Ÿñ- rÒ«êû ±c›cʳ {6*Ñ>œÂdUŠªÒs†³VšJñhwþfɪQN7z˜<æR46ùÛÓ7–âTh/ÃyÉÇ öNjBÇâ•wì¡ËíJDEs÷Osy º´Uƒhr«vÖjr)鎖X@ô´9 ÓÊ ŸÔ@>柦˜\ ‘F£W ¼š"Ôêoøû+ë»:y:Õ›øâ|-n³‚ãu*{þñÐö:¼Áß¾¦¿­¼’öÙªÖèÜÜ(¾j”¯ˆbdØhZÑhÖPV}Ç–Kýlû.ì$ˆï‘ïXÐ$΢«Å~c»Ó³ºeÃI«2ï“RG.‡–'øa †2fÐêh;<Õ]ÿ2Jn”ÏÐzY é¦tã1!i¸ë\JvÅÜæoeñŠ…>é¤F)˽Úö.éE­û+`Ga¥ã¡·ü S0œ€e28η 5Ð Ð8½‰ fâ0‰Ûœ c¦¤&Í@z®€à¾y³ñã—ä [Þ;z'Ì#}fûAoÚ ÁØÁÈ2C…ŽÆw.F¿wÚb‡©ÊÓí–ºš…½«©Ø†sTéߣñ§I¡ãMa\t–€âië:uÁfõãL¤õB¡|K}æ.+ú<,Ù¼Ñn™¥JÓÆOô•Íñ|†Ã Ç÷Ÿàý{ãß¡ÊÎB„àïØh1ÓüñX¸} Þ×Àj?Ú½ãÄ¿Ûi‹âa(³ÿ u›S9Îkßw££"E"“+ë!XæB–Pd Zý‡BF[mnÀ‚¢§°z†F%‡båµ=„dуÆéeÔ(¨cü“ _8÷xF7›ÐKÙ?eÌÙBVôF+]LþÀÏ…ˆ£ 2TøgEŒ,û ŽŠH—ãNiÀ ”' 2ÎG|}Q#ÓàÑx?š6îwôtÇ ìƒô3[ôùhhTGà£#½?â÷%ýÀë·h¨i1ÏÙhÔ¾ðé⠀ɚtŸDñÁßÉ(ð`‰NŽº‹ÝºÂþ5ìü,s•±aùI‹Z:¸;Ý"¤öú2Ý ªXMDÞÞ†—cõîÙ¤¶/›y×kÀGF+<ÊöØ¥ªyJÔ¬-\µ9å4Êžr| òòqœˆ†…98>TªÐôÔ\§8p’,[:ÏG¼–;Y•ò•1:é äU¢µúë8^t:Mh¸Ð]‹²½Î²AÄÙñ¶}Eº#>±ß!ÀK¨Ÿmûê— ›RåŸØÆ6iXAm V9?BÏâ±íÍ](oéÈççévñ¨÷£Ó{£2耱©mèNP_®ìÔ´‰¶ÆÿPõÚæ¥®â#õ±¾kÿá‘"fc Þ»ÍFh¨‘BeΈõÄŒ²ŽS´‹žtÀ„ù“ÁÍÅ¢ŽÂ-~~hÞùû¿ÖŠŠ£ ÷ö³z¼-Ðg¿`õê“ëmÑ×w‘n_d;qØ*碫ÚHH°yZ/–:Gb*×¾ø8µµ>ÕÊ–/ÂÊÜ‘ÓÍt®¨¦Ñ«iaWY¸„¾®Ë†3 ¤ÙÓ D[ÍÑÒåÊœôø#Ûh„ìôÓXÞY7–œ®§é„þ£¡ã£<ž®/xGl ” Úw˜íË¢ÃÝWcôPës†xb;F€`*B Ñ¨ +‚Ýè§hŠ˜Vî.Kø×yzÖ±…ÜË2çøÃ/uÌ#À4vX7öÆé'µT4󠂦ã GÓþNvI­F7áV£PQû0g7ÍéÎïŒ#À„3¬‚çÜaÞDBzz{‘k~!l턚y#l¬Ç\nK¬$íŒGq°ÛŠE\÷[·1fŒ#ÀÔXדŒjÌlÒÞÁ-ú‚+!tqƒŽÂªpC'ÔVŒq‰¹¶@Ôò³º¿Ó·1ç§`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`jÞ\ †I\ýw2zhFzò‰áÆ›ÍjµY_™ŒoÜ ,"°Ç÷³ŒÔäQÖ`ß#M©®Ä¹XÑ8ëkuP›GDæÎ3:h§ýàõöÕm5õĵqóqSÐÛSS“qs F ÒÓÛš>·§¼cRë#Vt¶zž‘{Ô”ÔÄë#ÿõ‰çÆs0r÷P~ƒú¹\Ú…SF'•ºÂ‹*TN޹UsÉ˧ŽN.¾/7Ø õz•FW€ë¿¶üA˜µAº«L|¸g÷o|qDyap Æ/¸üà¤òüë¶E_é¥;R…&žÃ¥ŒIMì¡°›}?>Š¡SpÓÑ‹HÇï8x#GËÜyŽa˜3·ˆŸè>Ô‘ù˜2ætÃ4æ #Å®ii)Ö]°è¼ô‚Ÿ*Ç~ ZÚå“.é¤+õúq ´þ¥]# ñ˜¦É‰SS“Þ¶ã>¸Ùç9ôÜøRÚˆ¿É¾ʘ³¥i¦ îž!úÿb·ˆi»ÛaçBÄù°æ¦O<ÿ} XÆMMO.±Ú~é ÿ‡bDK ÝÓÒ“-ÞîÁ¾CU}ø:¿ƒ;ù¶L#ý Œpn‚бud›ø6-wÝïÛìK¿ xÝŠ^ï‘À<ü¯Ä­Eé¶Šá¯Ëô¥÷G|ÐJ‰¡Pç!¼Ø¦¹Ä$ÓT PG6AØ{`)âÙµðƒRÆöFãýªË#o™âMZfóJø™B&!þž°‹Âo¥”®¦¦&.°ý„êIeB;R1áp>øê ìWA8ÌhïNœHjÕAÉã¯nÞ‡Š×²è Hžp˜)|÷ãÎǾHCg¤a:;³\±Ú¨É£Fk7PVÎGö\ \ûA}|ú~LMKš8(%íÞ@eë ÎqÆ‹:p;ׇ/y‡dÁ> ù¾Ï>ø=‰Ÿe )/A¹kï*Ãü<Á;®s†wäFr„:4^dùŽG™}¢Ðwá?æçûnÊÝq(óP«Ï:¸;%z½wåÙþBÁ¿M éÎBúw:„o±“ÿKqQê<„ë†òÿŸÚgiIƒm¿Aò¿Pb¤¡É–(oCQ'ŽÿÆÇÍšz›0bÄ~›^%ò´Ìö‚ê4°>E™Æk¡ ¶-²ýÛÏIéÉRÒïïwÌaÓç-]Ù9ßvà'fÜ™A#;·‹Rõ·0Í—i^¢<,µ˜oõpoZŸ£<>£›æ· ¥÷¥°†t­B+œû‘êyMjiJj϶u?ºå ü..‡!n˜—¥QA±1ÉÂ>=^E–“Òn‘ʘ‹Š»õ~X¤$›é¢`ñ€”1§,|9 a/„ðý´×@¸×4×W~~¬OK(ä×øØÛÑÓ}\ ?AÛIÙ i?-Só,‡ !l_DØG_:4ŸpŒ#¼¥—ðëR†ø`pòø#)tˆv@ ¿Nï;Ûá áûüü‡NÎ éø}Göø=EÞLÓ Ë dU }“’~+ð£yZhåãBjPYËeÒ%·XBøGeFùv,À\5:òu`};æ¬?G~>ŽÎÄûUuó>„ì–MJ_$|gA7<~.”º]Ï6^° HJ; ï_)aFcŽ3¸B­-w#Ý]©q.«ìÛáéI#L”c ¼ß,¶—ò”‰3g̘á²íÀË%š’ß ­Õ·( yB÷]Vì–£Ÿ¨bŽmÞn@yÂ\°r!†Á‘´6ý3õÌ/~ªÍ¿M‹žJï£>öEgï§½ÿûýÏ?‰Nû"¤é^¸}Œ2žá;õá_Ûo0üù= e{:ícÀÁ ¤³?°û ¿ûöì×'Úô¬gyjÓDøÀíÕéÂNl1éJ¶EÅá엌Ԥw©Ó‚yÊC6E¸=xº9g7í^CW?b^b$0]jÓŒÎÔ±nvÜKÞ${þì•þÉië! Æ#ÜIÓR¿ïŸ‚kóL54ÒñÙ9àDi~‹J×ǦÿàÓOGgïʾ Ç«°»c°wR“¢Q¢‘}Ѧͥ‚Š9êØ}{ò'*!§OKK¾ÙUÁomÖçÏÇÚ³hðO¡FÐvC•ärI,0KžeÛù?ÛÜÈÖg!®mñÍ"¯ò>r=êDõ Ö®ÀµgÏá§{½%éat}²“:ï>Uð—.}Àþï)i)ßQ'GlÜþÒú»ÿ"ŒÞ‚êêO{'Mk¤áÛ1á?A¯ý&/;ý…ê}³¾z`?šˆ£šˆ&ùÅPÆg´X/«š÷¡à˜uEüiþ´šÆGµU¸ÐŽL‡;ýlóðnƒÕæ—Ú6ýð¾ ¦î¢U‘=•]Ë”]ömV‡évÔM 1W{ZZÎAy2÷¯U½áóçAÞ±‡è>ýpá’exä`FB˜ÔГˆæ]ÑÙ•û;zNÿ™¾IÀåoß7áÏ¡¹’ì`^œºõâUÊ\Oùe(ø/$]ø­M/È1Òt}ŒùS(ã?9ýÐ{þŽ}ƒP1rIwï)©£~÷w–G¸##bµ®“iäKf:0ŠGÝ£¶a eƒ¿`òÔöL{A~«ÒÙqð³|coÄJódoòBt¨'¡˜4À;¦GY0A¼A%*ÿp_Ë+8ô¼Eo«§[V`ØC`åµìù ÷éä-kWÞyxD;q{|ænD…*6©Ž'ÿô½Ái¨\íÑÀЈ²ØÐ¶ÐC£¤Nüĸƒ‹ðž¾ ´º›ü@`(âÕÈ60Ê‘²‰'æ»!vÒ¨ò»’ÉþÂ7­B%·A+Ð1{U씾Ý û¬CøM éghL÷:ýSÒ¬Ñv`"êZäÁ7áky›’– ‰\‹ÑâdQÕ¼gemUK\áXògd»"Ë£„Q.:Œ*ž]ò‡…FKÑP·ÚâKF[ÂÊ ÈÍ #ÕÍÀâm‡1îèï Ù€öZB°BHëú%ˆ,Ïýâ#ÍÏÁGŸGÇ+¢Ûõå{{ÛYÁÎýg@ÐmÉkEîÖCEkT/QÄÍÓéY]þ‰†Óô¸[œ‚NÊã(g"¦1=² HSPì¼]‹2þ] áKž‚忘 _8„¯e:Ž¶Š³Û–~K¾ùç©íZ^{aû¡gUÚ"gx~/JW¨²IÕ?»Y¢®ïºBéTÑêŒÝ¤áñ3hAAÆÈà/§* TÍØ•}žkœnÎw—Ë5Ç4u™­çûÑæP£¸`æ¶Ðà/BD½ü$TlÐ×j.·k¶^©CééòD–¢U¤«”iC×»ÁË:Ë?ýÉrV¸*™Ãdð}š&]—<㾫8\^°¸ÆÖ” –2îh‘oÇšÃÁ_'86A£ÑS!ëüAµ}ˆ‰\žˆU%"öCÉ—Ñà¯tz×L±Ìùí|‡@:åC§ã}5ÒGù"ªœ÷bôJóɦò uZ·÷t|È9Ïét£w”±_12¤2W¦!mLήì¡9è‹rÞB¥%ÊÔ(ئiåF‘b¼ƒ-aÃ!l2¢bµÉ/$&î(“°Ãa«¾àB„mãž7Ö‚Òî¯XALx<Öe\‚ç÷4ú%¦øª™I{÷@kó ´6Ø ¦½And°.âz¢L@ÝKïdT.J v%ºÐwuù'þ¦Hc•æõ¾öä}Ó-¨·#щ› >.æsÈ?°<Œ|êÖþ–ÿbÿò€êÚ¶CÚ Õ€Èzñ4Z0yz |9íE±'¼T¥-r†ç÷2Y#Xf aì@IiÚ@TÖS·èiC 4 u¾¤AEŠE7úWÌGN(ñÓäHL<Ý¥GÉr¢—¼£Ö@€¯—ÂèC«}Qš/Ãt €½è…ôl;`TëØ¥QèAë·ÚvÅOeÞŠÆëßö¢Ûšb» ^Ð(Rûa4°#gõßHÛ§*Veç¾Uí12D[þY "FÞˆ;¤?!»|C7KcU"ò}(¹åâ|«3á Iêv¤­Frsmëªä½¶†ŸC¨eµ×NûÖŽ‡V$£ð—Û){Á;tªÈóÈÛ–[µG† \öÑÙ»î\GýÏŽÃùl¯uø‘ʸ)L,j«Icàt‡ðýuæhüΚü›nncÉlåS•*Õá¿D|å|nXbq Ú¶7”ùàûÿ2b»‹*ò_>ðK•ò40©¶¡n‹Pæ·6„õNšÏÂÜÖƒ˜ëx³0 ŽÑYOºÌò݆ ¸ß`eåHt¹—(‘0Ú¬î5]h^ sÞ1Ýæ½tå.ñó„õCšÇcØ«¡MWä\h£44n÷Àësäß6¨¤ŸÀ~*¯ŠñD7Ü´+«ï‚júMŒJv`Ôüž!""L黣†k f»­¼Ñ‡M?Г¶€æ¹˜»zBcaFêÈ?ù«Ž]kOÒ¿8\c;ðˆk>öDïÀ¾Xl¤’üi™™’œœ -Æ‹èd Çb˜}Ò¿!ãé„"àTæB˜ª2€ÕãOa…úeJω´ s{"Ö˜¦¯—iè/!m+Ú»NÙ¦]•¼·ÃVç ­Më)é×ûÓÐ\Úo–j\*¬wnÖJÂ~Û×4Sï=gɪ»¬NŸ#s¾;hªC¹ä¼Xµ=ÏÈ;JWæ(”ÛÑÍ#Wý²Ë¾¼¤¾œæ}x§?ôM*\ì&ø õîàõ‚Üûõê¾ujÊÌÙ˜&¸›ìl3ÅûÈöÉé©Èã±à±ûßß@ÎQºy"´Õ' éšDåºzüÛ±>1úoš¯ï}­ÃçXG±ÊÚlÍ?iÎÒ•t~ù!Ð&Üa‡@™H×õü;…ž;{Àcé#4Ó½QI½ê\G¬Ô&XþmzÁ<‘'Aåi0´œ~j²-rÆÓßy\”ëØJð*ð,ÿB@g¡jQò¨È–+e¾mªüUh¸fa¥ëp§_kc¾ÔíQ ÔzÈQ¬†Ì@„J‹sŒE ©¹-õ³Ö%]Ÿ=¾):áÉvÓR“þ‡J5#Žó•\««‚¨À×a«ÆµÎ<ŠTæÅÓú~ðºS™Óí¯• ^‘_Zñ !p¦ ð;—xÇó4\ס‘/©j¬ˆXî8äQè4éÀ’á†ÏüÓ0ÍÐAP¸g8•ðB«Ç£c¬­:ÿÃO|¾ü-ˆïc¤ëO¬v†½R—HV5ï+ÁN@¯¡ž¦—úéúE ƒûŒ©? ŒîV>ß,fzem&"h•~±ð5PîÓqzÑßY¾ìÿpʵˆòõãUôÞRiÐݪñïŒÇ~Ï‘:(¶@›B8(Ó·ñ½ŒïÃ5!oF,êÀC»å}t+¶©ÿ¹8úö«ÂºkNC»q¬M/þm¿Á<ƒÍÓ`hùû©Ñ¶È?²Fô ¹À&XHÕ„ÞnW­åô;ì°í… Déнc»j¢éÎâý½¥½TÚfȘ1-±pÃtžPTi"u€æó#ódmñ~Ÿw|)¢³,uc §™Ðíð¥tv¯î›Ê*5ÌBµÉÓ‰Sýz’Yÿ”ÆÝž1­uŸŒ×D“­åáZe?˜ÒVœl=»y³¸ˆíΓ¡(l¨ù§í|¾}ûZ¹t-6:Ö“YÑv>¬–ŽÚáËlûbZÒFLA^—6åñ_ÚwÅ6åiÅÊöQŸÛ¢²SÅ.Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#PŒ€,~«Á—A)c{¦q¢è¢¤ê€Hãj0:&Í0Œ#ÀT %Ä~©äfÚàÒ\³&§ŽúµRªà¹Æ°×ûZT¦/ó>!ÕB‰NR S(¹ x3"ͪ¯„`F€¨ €›@w€Ìj«”Є› ³žëèéø¢×{W^MDZ#8!9õ|0; ‰è,¥œ‰ß;ZŒöÅäQ£v×D"˜&#À0Œ# ÛÜÌ1/QJ݂߅ %mpЮ%¢C’ÒºdúVý€=™—×R” #RxR8™XM!€‘ïeÊ4߀°}¶£+éǨðgÄùaa¼É5}ÐEGƒËމ\Š8ÞvìऴO_JOþ·Fâ¨A¢$| ¤ú ¿,–n×]ÊgÐlz… àz•]—YÓÉhl7utwå¾e’࣌©PÞžRW¨¨WAÝ:£½;q¢3|BRÚXÑx±tGQzÞóR¨¾BÈl¿ï¤»õP!öÇ(_ÞD¨dûÀ_S\±¹Àíq {É;ªø¦/¨Àº¤ëÓ4;ÁÏl=p.ú!åëPó>o39Ø;©‰Ï·ûG¨ÆàDé¶==¦¤ßi˜êÁiéɽÒÓÛ‹E÷i_Œx÷€~ᜩ’©P ¿Gþ)}·c<ø<~ÚB¥¼Ä%µäÉ©‰?{EÆãv'ù|F_]ˆÉð{iEþɽʘ³¥i¦ ΞÀÔ‡x»EÄÈ—ÒFüíÚŠ{æ{{w`±~§"\¿?‹nrj?`ž„ë[ŽƒŸðÿYw§Äò.AŸ”ž¼qÜßï˜Ã¦Ï[º²s~ ÂlÇ„9¬‚ó bö„ y>4ཀA¿>`#îĉü+ߎ·‘Ô4y;îòü7š<žéKßéî­@ûR¡çÎBm”ÂEg½~ ap³Ò·O‡ðý‚;_Jí<_†€=U×õ÷inÖAç(CO™R%Áî5áwA üuùs¸¢3Ýö稞¦&ZÚvöÓT¢5¹Ñ·GÊðü&ø"•úP³§ÑBï7rO˜:Õƒôýˆ×[ÒõNæ‰ÑMóÛ„ÇÒû’ŸŠŒ&´©Éa˜;½„Tûù˜”v‹TÆ\¤7ñ~P7Èfº(X< eŒ5oÓ@§æQh+¦šKÜ¿Ó0‰ Õݶû‰ûVo€Æ¼­r!ÍÃàw6Üúgê™ì“‘šôîõ×_o @ F þ!À#àú—gŽã-bUOŠHMºV“øÍúê!JGctv´ctöÑÀä1¿@P~6 yÌÅSÓ¿´iAàÆ`t=£Ï1Evï÷ONP»-ûK¸^sˆm±ß*u‹>ñPØ­¶iàyx¬»I—g½î)²û ršŽNÀƒ½Ÿ›â}d»Ão¹¯“ÿƒ‡ùŽVB®›’š4Ã@mÚÑëX·G;î%oÒEn¯ ¾õÂ4Çã»ÂUÆzA4V#¿‹tÞ‚ŽÂ³ Þ'ggxÞéŒÇ~xâÄØ}{ò'‚—éÓÒ’‹…µ×ûí[›õùó1ò›ShAW¸qñ*Ë7 aß„ß{müϼ—íËF瀹ÿùç#ó·ï›œ?Ÿ––re‘Ëë˜û^ z¯úçÓüÆ4 ¸çØ0ò±a§ÂGPqüۖમ…ø‡ðµ‚MIKÄhQ®UÂ¼ÑŸŽ”–j×¶‡ªy.½+—,1bÆHβ—"ÿ ÛoÑóK‡ðµ¬wÚìÜíA%¿æ´WÑšµ’YIÐbÃ4`xÜ€3·¡$ {G­­Eiu&MÑ Aûa~1jUÝüÝ”;>Ói¡°Ï0„p™îÛš°òzŸáƒ8"Â齃%¾ñ±zïž<òÛÅß­:߈ýÛhŒ^ÿrÒ0koÍØ•Mƒâ9j§ÿ÷ ïÈq&Bþ<žïÐ6ÉB]|À«R4Ú.Od)šRÓV)LкN˜®SÂ(L«ËãÔX´Ðú—À° æ“¡wŸ€´ŒµíU®I-,„7›>?pA€p¸äóQ6n÷:áÓ1U(K Î2)å è¦ìe©âÔÇrqßg© †(ÐKY°€jµ”6ÉWPЄ¼Â­¤°ÏÈ:Fç± ú ]½SÂÝÒïJ¡GÉJ­œnïNž„¹ñ›1ºŸ‚…bGë¾Ý±“‘zi{pF@Ì'aªEA¤ 3Ê(µ.J™"‚µØ˜途…¼sÜÙÅE/†ÿøÛñ7#Ð`Ür³¦åœ‡þ;gé*ŒžÔ½X€ôdÆ€¥3íRý ÕhàÑ“T!ɃšKv’¬è#Èöþ~rsÔYdɼžÆÓô„-Õ€êPbÔIË0Xµ4r§¤%½^†—JYÓªð!)ãî-P¾ß|úž1ˆ¶”¦pýŠ­×ÂÌ‘Axw âºe‡DjkÉ#ó 4È•yfG<ö’m0²níƒý)4¥­ÁØY·¶`Š7ÉZ`VìÈ/Œ@#@ T¯½¤™“XÏ •®š”‘êUlÚ1ªÖÒ!PZ”\çcaQ+§sBʸc0àê…Ñsö¡xÇö¥óhõµ“xèCªTÍãZDö/y‡dA@A(™}œþ WT›ç;íècÃ- Aêäv´ù<Ú [¡“RG.ÆãÀîÀk­Æ¶ÉEµŽ] Éš!|«mWüTæ­¬ÿ¶Ý,õ´[™ÖÒ>óŸ¥Å7@IDATêb?x)Jã9N;áö`7¶|ù•±a<n|y^/SÜΕøf¦™~%V쎒~ö;c BÃl†•¿X¤¥Å`µ2­ñÍ"ŸÂªÝË”ž;[b†¹=kLÓ×Ë4ô— W´wþr €…S™f Lû„Û¥mÃ*ã˰Zú^Ëw{†ïŒ./Æö¤ašËõ±2d¾ÍÞFûsq”å[è ŒB˜Í‘.÷ÌCµÜ¬§?Jl¿¢•á’ÓS1Ù;t 4€J]æ(Ýñùò·¦ù1Í›zbµ3hõn¨!”B£Iµç,ÿï+È„¢õY“7p D‰Ñ-€Û|³g Ÿþiê?`ÉÓM¸îòçÉíŠøño*s†ÚïÛ“í˹—üdŒ¹W‹’'@P.Çao›*Fö³°º{¸?Ê|[BTIÄŽüÌ´Ô¤ÿA5=N7æ+¹VW+ v¿N¸äµSS“Þvzïèé8·„2'YX˜úÐÌ‚šúN§?z§Np»é¼‹Û>óOÐ}êøÞRiu³a.èxWÏ`¯âwD½î>ôdÃÔt…kË®ƒÚŠv›Ë;1‰TŸ;|éÝ«û&Re×o¨ûA÷)yR|[ŸÎíÄ)›Êôt*–¦ïjÝʼÁy2W þ?>.{Ÿ¯.ýÝg̘ᚳteW­åô;ì°í5•Fg¼CÆŒi‰T¦u1¹ÓÁïû„Ý[ÅÂN4‡ _zŸn‘­g7o±}ˆ„)F ,¹GŒØÌ„%RÌ#PÃÆÅOÔp4Lž`ÂPÉ=VA‡I†2Œ#À0 À+¿9µŒ#À0a‚¯‚“Œ`6ê/J¸nq+×êú›æœ`êÀu:ÇÙ ˜––øiƒJ'†`jVA× Ì #À0Œ#PÀ%ñà/F€`F V`\+0s$Œ#À0Œ@IX—ă¿F€`ZA€p­ÀÌ‘0Œ#À0%`\þbF€`jÀµ3GÂ0Œ#À”D€pI<ø‹`F€¨êíAƒRÆöÆÕo¸·TtÁUfp­S\­ F‘àθýRÉ͸ºm£ ÷®NN…ËЇ±òßÐ/Rvn¬ùß8ršSâº/Ä—æšÕ˜ê~ <ê«]½À^ïk¸ô<ó>M“ÃuSo/qAi\L”Ù¬iŒŒòxÝh>Ïç3÷ìËQûsò4à‘†‹ê·èºñlÓ¼&/<óÌpºKõ´ÁùàƒOGí‹Êºßív ³òÉ6æüo09Ë ©4~u?ê¾a˜ÏtŠèôBy×sV:"P£ÔŒëŸÎßjl~2¥cC:ª:DÓ­£Œ‰ŠtÕ(BáMÜêtääå‹?×dŠ_–¯k·lͦñ¹Mr¸ó‘äþ¯OL›öMüê³ ¦;«5¤ç‚ܨÜiš);ô8¤çx—Kæ®æT÷' vÏHï=¯ŒóRݯÏõ¾æ ƒª-¼zŸuΔŽ_ç}û:=kÂ@ø>¨”|£c›fMú_ÝG»èôž²S›æÂã®7ý‡š€¥˜&á@xœtô!òð®íÄú-;c²ó|7{ú™Ù¿Ïÿþç"õ±2R#ã¾{äcº=¯"ÿc9ÿ‹³_« ô¯ûY¹7÷>«OÖo?|»ÕÇzö9*¹îj[Y(|ÅÓ'ÙU{ôŽK]‡unö™S— >#î¼Ôu‘K—Ë3áÎG“‡ƒ~ÔÙ¢Ñd}0Ä'ñë!þ)”ÎÿúuÌc]!p îwE».Ÿº7ñ‰ÁK}ª÷u]ÅÎXB•rF¾Ñøª{¯>[Fxª=`¯3 k3b‰ð"Ü<žÈ17Ý÷ÐEˆŸ„0åw¸ aâøôßÄ?ç?Ð`À³îK©M¸ãáÄ ¬>Ôû R×𼄭NHðF{<žW:µm&n¿ìŒpaY2·­›‰¦ÍšM>úè“ãÁ¤-„Ã’ß"¦,áKüßÚ6Wœÿáœ]Ì[8"@u¦c›æ***fZ¿~×5aÛÖ‡#~µÅS¸fŠf´÷¦Ùá†óOvñÈ·jÅp»éÂS4S‰v=Ï9{¨DâGça›ïEüE¿Ä7ç?aÃTªû7^p²ËTª}Ç^ÝïGðp®÷•L]ÃñŽ ±5ÿçö¸†Ý­“â9ßê6ÂpŒ‹JÑøEàŽóBV¾ñMürþ 6Œ@°ë~dtô} ®õ¾Š©kÁÂQk·q¢iªvØjĪ申"[÷»ê†“A.\GÁT©—YÄgkÎÿd>“hÔPRJµ½òŽ'‡Yi7l‚<‘Ð!ØçfpÕOvG³E›¶ç"Qø…[oØ9ú">9ÿëgYc®Ã »î7i^Öû𫸠K ºÒ G8d£ ixQŽqÑ‘Js»;#u¤†·mI¶&¾¢‰Oâ—ó¿á•ENQí"`Õ}´¥RsuAÌáVïkŒ0Œ-Ü0ñãšlã%÷0̾àYj+]w„pŽ€ÃãÂ|/™GŸÄoð©cŸŒ#PÔ–J—lw[ó.õ¾,–}¸eñãÖ„ŒÃÙÎ܇°ž.©ÑvR+PE §ù +ß‹øŠ$>9ÿF%Ͷÿö‰­;÷V2TízÏ/ð‰ë·”é7?ÿ%Öü»­Lwv¨<T—4aÕýp«÷•OL N˜®¥ŠTBUYø&Mú@|ümõ.Ú¾k¿Èܾ;l²zoVŽ˜øÆ—âÑç¦W‹'¥,|íJh¯„®2ÖÕbæ@àâ|‡u "Šø<àƒßŠظå?10ýu±qëÅvöËgó~Ÿ|_½²oÓ æióBü û†ñÜ ñÆgóP–|ùºÍâ…ÿ}-|ºÐÏ׋–‰•¶tc˪#€…Xv'7\ê}ÕÓ€BRƒN† ‰ §—Ö©Pøâ‡ßEëM6²×96ë2wˆ©ï+"<¡È*«cC„¨Ò/\:`…ù^Èø«z¬Î3¬¸ëŠ3ëääß{¯:[´jÖD¬Ý´S¼÷õ"qÄÁÄÉG‰ãè"Ž~èf>Ç= :5iiÕ©p«÷5™àzA;­z¨j„hÙ|Hð’UÿŠï]!zÖIÌ^°LDEFˆ>'!Î:þp‹ç?Wo³.›¶ïµm!pɃøï—¿þ‘±`Élf?EƒðßÎ\¨ÇöfåŠÃ»´³üܱµ¨(ŽŸ!ÞŸó³øcÕFZ‰,Nìq°¸¶ß‰Ö`ÆìŸÅï+7\¯'Î9±‡8ÿ”£KaI#ñ~§…+•˜³hy)÷ÊX,íJXŒ7h”=d©LUóëäÃâGÀUrú¬EÖèóÖKN«°\¾úñ<Ñ$&R\>íLâçekÅç?,£]-æý¶Ò*kÝz¡ˆ‹ß-þÛ*ÿôKkøJš6è¬vn×RtíÐZ|óórñϦ햦ºñÓ’UâÌãŽÓg/²êa÷.íÅ ¼S<.—fvßýê'‘•“/Nïu˜€ºÔ*ëCOŸ~ÿ›ø}ÅF‘“_ bPQ‰Ä£·_$b¢#@§âúS’ÓÆûUÔ¦RýrÖ·º¬÷73)—Í’]8ìïj= |>ñ׺L±ðÏuz'‰.í[Z A^¾Ïj¨Þ¹@t…Ý7Æ¡³h‡çA"6:Rwxqçågˆƒ;¶²xhÑ4V\Õ÷AO7Ä?üaÙ—yøä»_ÅÏË×Að)®=÷Ñí ¶V¸y¿­¶®¼ó²³Äeg'>üf±Ø±{Ÿåæü;ó¸î⼓F9j=§ÿ Þ)ÏmœéÆÉO¸•ÉpÀ'(öeç‰ý9¹–ߊÊåÞì\KèÙ„sQ'víͶ>OëÕ ë ¥xoÎ/bÏþlñÑ·¿‰S{v (|)©Œ£Ó:1s¼'s¨E‡xøûŸÍ‚lï#»ìI…Põ‰ÿöf¡ÇWØö¿ýÅO"B÷æ‹N;÷ì[ÿ;0‡M4ý{ƒxô΋Å—žn…»£|ê[,FøÏFÀYÏl;~Ö!á4¶a¹P¸¢ÏqâH¨ÅÁˆuÁÒ5VƒÑ«ûAV|0§vzÞýNîaÇ/Ü.—h+œ§p‘`&³wŽèŽ0ͷѨÔ6eű#‹ÞG,.ÆèÚi–­ù×´m'è×®U¼ømÅqÁ©Ç8½ÕÄ{¸VÂpå«&ò Öh–U.Ëc€Ê?¢Ÿ|ó+Ä=¢M‹8tKkgl³~Zj½Ò(–TÏmš7µ p qÕ%½-áK–ÎNæî}ÙÖ÷°[.Gtm:ÚÞªvàUìtÕ^|“¸ž @‹¸ÈÔaý±Ù«O»ŽÕGÞ$Ïá(€C4|É4‰-ÜWœ^8™×ôï w>zÚÇP "n¹øTKõl9úýÑÂ.ꛦi©Îh¡‰s±I 8tŒ”©QêÜ®E j8ãZ¬Ú¸sfÛ1rX_ìF«XkÖ„Fµ_³<õúÂgÍ#QÝ•Ë@4©\; ijH(ÒŠå„«û M+»_<$RAï‚@} Bûÿ Y½ÍÅäp¤hñ»ó…Ö7éŒé2´gµe³8ëþŽ<¤£5z¦i$šŠiku¤ë®þ³Æ/Œ@H'\v ¯fRËZÀÔµC+‘x÷å‚ëÄÛ_.ÍçLj«Î9¡Tl¶ì3úS½@|½KÅWË5†{é¨ëøƒd³~z T.)%TíŽ(}oÚ¾‡ņ„ãÊ [D‡ÖÍŬŠãŽèl­a(öà…¦iHËDK§ñ 2í¡õ!³sO–èŒiÃ0Å>¬±°Íñˆó«ùKÐ Ø,íÔªèž.¨Çi”&õÇf·¾=¹Þ…AŽ…Û| üj~Dª1Zl••“gÍÿRãI‹<`ÚµŒë7ï4WL*¯]{s,ûæðóæ©~‚Û24Ð…ßþIõýFÎ4F½ø%XŒEæ¸Ã;‹EË׊•aPžÜüur¡°*¸x84«K#yªnšj4<•—2wZ?z¯¬éÖ¹-öÛn[v챦d–¯ÛTLB7 ñ&¶­ÐЛú‰m»öb±bÙ iÞ–:©4R]²ú_ѹ}ẉb‚e¼p‹²|Ñèy9:·'-_›)²óòE_,Pì~c'ãÕaý)#5an]¸³ÄYÏœáÆÁ^8€kqÃ4Ä—XõùÚ'ó°%Â…ž{ѧhuôÙ™þßÌ…bØ“ïˆÛ±øã$,¡yß'¦~ 5Y„¸¢ÏñP-Š_ZeúÊGß‹çÞmù? Tzuïl­„~ë‹ų°§¹ähÐxí9¢µcþ,¨ØS£D€öÛÚ†Ùc WØŸA=O:ê`±{riú¥IL´8»÷ábæZa¿˜÷‡ ]ןw’% ¯DyÎb”Ûƒ¬Î©´¢š mÛ;çÄ#Ź'XOáï×ÿ›êÒôY?£3¼ND×ë.huu^¾.ž}g–UG4ŒÚiÄågÇõÇHþ®—P¨Z&!9õ;"‘–Ò‡žÕ0¤§¢=Mïz$ù“ݺœ0ü¶ «A.¸ ÔãÖpN m‰pZ_•‹Þw Tc¶¡Ñ0mOªŠÉ/Эùc´NCj·\l±ˆŽ¢5fž~k¦Xò×ê¿ß~nˆ„N: “hÒ™t~~ueŠó Ðd}»[xô¹^=;²6ò¿®.ñR¹ŒŒ¨Û~8­¥ N>¨^>úì qÓE§Xë3¨“úýo+¬Cvž}øÖâºR[õ'\ò«*|PÝÿsź¥o>3öV„—z_•¤„M˜Pɽº­ya§Ç†NáK¬VUø†  þ&1t<3F öp ½Ú½0FZàåÏ e²§EŒ»±ж(-žâNmZ _ Íõ§®rã ¥B((3 F€`ªˆuvï¹ò,±péZìù]!¢""¬yàóO-{;T£â`Œ@!À¸Î çˆF <z`ýØ0 ’Ÿ 5•œ.F€`F Ì`fÂì0á„Ý.fo '¾˜F ! À¸!ä"§¨!h»žãÄÕŠ…É2žnœùΩf‚F`;. ™öáw¸¬¤‰µÇ—.C d¶íÚgí¡ÿeù?bÐu}qhnݶçGŸyÜá‚ìï¸ìŒ ãaŒ@cC€GÀ-Ç9½Œ@% •ÈÇÙÕºhÒ{ßX¡i[Ð'ßý†UÊkàÖźßw î­þW{Ò Zî#dÃ0e#À¸llØ…`€]OØøj\§¹·‡ÑÞ\2tXÆàëÏgÛÝ:¶u]æv\ÙÙÛòÛ×o²aò`\>>ìÊ4z¢" O‹.zæà„82tT¤}8 ÙL‡gÄ*ß„µcÃ0å!À¸lToOdäy°é"4Ù-^“PĦ¤Ø/LµE™j£i³^{2õWÐ5ñ«W¹Ñ `ª|Tñ Ãùù>ñÝo+ļßWˆýÙŠJ¸µ\¡¹÷¢Ü»WÝB£D¤0õx¡›Ñâ‹ùKE\l„èÛ»‡èÓûH‰ÆÌÆÕ‡Ã•²sY!¾]¼\ìËί‘¼'Žê[þçäæ‹ek7‰åk3Å_ë6‰ý9õ¸¥hÞ4FÚ©½8¦['Ñó°N؆ä*1ò °-¹î×ø¶ÐuõéseL—Žé¹O ­T”/ÇŒ-È–FAH¦= \f¶;Våyb4Ííz"!鉭º®žÛù÷ÆIŸ~újRh ãIl¨ˆ6Jl ^ŸÏgxÞ›ó3ß½F´n¹Ï•ˆy¡Â¸ÓŒ¹¹‡ã@û^âÓyâûßþ7žŠ8ºÛAÖþIÄlj*ËÖlï~õ#F»ùµ’÷”ªpÏÃ0ÄW?.³.Ãñ“†p»rÐA\+Z4ß„:N)ꅦѹϑÂЛ¢C²ÜN,ús¿X€›‘â›D‰+ûô'u0üiÖ¯æs3ø¸îU%|R£åÂÏsÓ}×´éKJjíÚ¿Qºk¥è¼ÿ©ç“{(ÕPæ»#ÅÆ¸ƒÅÚ‡·Ý×yl»£» ½íÐÞzfüLDfàG‚8lM£ÀvÏW× Œ| Ä·¿®_ý´LDx¶‰vm>QQj<£¨‹]býòòºˆ]{.å‹ËÎì…»Vn·Ëj´ê£J¯ÆÁ Av˜óórññ·¿!ï·#ï?«•¼'öÃ5ÿ-m:¤“ßûV¬À]¿±Ñ‰V-版ˆÍA¡®”*êÃľ½g‹7>Ï·þA|¼Õ©ÌÆhzùºÍbËŽ=bçÞ,¡ë&èº ¬£Eç¶-E·ƒÚˆ–Íâ¬xjªÜÛùÎu?¨ì Ö“=ê%9qÛð‘C£¢cG7ÏÛ©Nÿ÷{Ñ>{3¹×¨`‡í^A?¹%¶ƒ˜ß©OÓòã;MN|}BÚ³ˆ\Ç/lGÃFÛЇCåó!|çþò—˜µðo³\´lù!¦'|5ZP'ß®íTñßWc4\8yqî‰=„ÇÃ*é@xU×Î.s-AøÖeÞSZê*ÿ ‡ýÙy"+7O蘂Ñ4)Z4‰3Zb ß–->qMW n‰úCB;&êo±k÷ePé#}(Ç«6në0·Ž(…Ô ÜŒi u¶Š„ ŽÃùÒ…MСÂgâhK{äJAlç;×ýJeiEžmáKWeEÞòÀ#@ø>qèž5êìs4·Yûí)¾¸jõ{®ï;÷Sk›uwç#Iòõ‰é$„‰™°ÂF“ê‰*`^~¾Xºj£˜½¨Pø¶n5½¢‚V£î$ø-vÞ >ÿAˆÖÍš`­³%„i.Mè 2@7ûÐeñ$|ë:ï)eµ•ÿtUà²5ÿŠ…K׊5›¶XS.þÈÒ1’„Ke…¯“­ŸhÑü ‘}œøjÁ2Üý¢YÓŸ­iˆˆ­N¯x—Ps·¹9‡‹·/^ÿl»˜³h™¸î¼“1§Ü:dsÉ\÷ý`Í'©€IøF]y×€‹bš4}œ„ï¹ë¿ªñQoyì“à·xèz áô‡<¸ò“žù aH“J:¬L£ÀTIõD#ß½û²Ä§?üa©iä.†xñmk-ÞŸó‹8´cÑ$6ÚZÈÂs¡É!*yXÁû³XjçpÊ{JaMå?¥û/¬ìþ¿Y Å{²…‡æt£VAHn€Û‡2f`tê>½$6ñq?Up¢Ù¬ÙlÐö¡ÿnب²êaDü6?OdçôÛv] žÿ¿½â²³Žç@DçJWg"×ýÀÈWÓ–„/ÉŽÈλ·hÝ®ý3- v¦‘o5é†,8x‘»"›+Ù´ÙK={žÖkéÒŸö‚8­«9á°,dÈû"õ­t¦9ß\¨Üæÿ¾ª7]4oöy¨ýØ+þ¤‘P‹fŸ‰}Xmú榉_â›øgS=ì20wñ_b/\5Îu1åP^*Bÿ…i6ÄŒÙ?‹§•ó¿¢Mëÿ:LÀÜiÜk´½Ï?1Jý^´lþÖ üW›A»}I“0ÖÄÆ,íÛ=E_AKñ»øà›_,­ ѪÔ;ß¹î›Aù£.©æ"ð‹>åâ‹ÍÕs¾®ºP;—Å1ñrƦï4¥¹ÚÓ÷Ì!Eüßu:B÷ç·Á àÂpá¼o6öõ.†ú™V;ׯ‚+°+ú&žˆ·Ÿþ\mÖu]·¶JUŽÝËGÀ.ó°Õ,\óžRªü·…ï[ŸÿhuæH¶o÷‚ˆ‰þ1¬¥2ŽVXSG¡iÜBìXU¸[Åhuve…°ï¤ùâº_ êªZ~A ¶i³ø»;cµ3Í¿†›!žˆ·˜˜èà-?µ‡•Ì +fBÅ=`¬îÌÃAÿdîÀ!Xê¨BFxËÆ}=x-ß< ®´v ¼§½¬áœ÷”Òêæ¿-|i¡ÙÂeëD³ø¹P7U©‘hõMèÍ¿ÄN?ÅÌ¢Þn¯ôHØÎwªC\÷C“' âýFyáå'J—»¶…Õ¨Ò™Z‹7ÍÕæ¢n?ö4j«Qp#À*/Nïúy-aZ$Bû|ÃÕoÄãŠõ[-¾«Òó×´Õ_¶@¢ÓÍÂ=ï Ÿêæ?ú6mÛ%>›÷‡5âmÿ]]À’8i5¶Kf[+ÖiK‹()}Á;ß¹îƒVÐ~lL‹¯¢[wìÔ û|ƒ&PÛ‰7â±Y«Vý7ñ͸¶2*+ 0:íˆV?ïÍúö®>ŠbÿÏî•äR „&M)6Šïéß‚Ï.‚Š‚½K"vQŸ@‚ïž$€åÙìŠlH±+bBGz‘ !rmËÿûÛË\.•$wI.—™|6{»;;ó›ïÌÎw~¿if’ËŒ¹˜Í%CCã¡y¢$ãáâRCn’¿¾•NCãj þy(ÀüÓPÏ{Ê@òŸÒJƒ ¿þ}=F›¸1°k~«ÎbYr±Ø¸¥lw^!Û…©LD¦õµñ|ß~P‹)l¼ÿ7Òb芮tš‹ªŽd#e“¹;däpÈ(ž!#HSd M½PÊ Ø…õkKqÈ& ÚŽd©VCÀ($+ÉÍÓ²‡-S±ó}ˆ¨Ì¨EL×­ÆñÒdnUr‡À¿V”ó^ÿ”F¯¦§` › «G•†X.4^œ’š=ÂXçv6ŒçPŒtÖç[®Èwñí7ýjoú“Wkäù«%®%n´F„“ïClMä‹apÙ”`Ṅcks ÉJŸ×MÛJL×h¶Eëw.w,{&Kб@67ø{àßOkËÿ†ä} ärÈ‘X0à²7à•¦õöÜ´ð‰ÐÛ2&ÙŠiC-Ov^öjÁQ‘اWMl–l±˜£0·š¬zÁw´Uç¼[™{ — œ%Äå=‚[ !’LMƒA R5Ï»­©¸yih,aKÀMÑê½røkløàI>Œ££’Ø}Êî½*Çwïä¾×²Û.ý×ÔØ žkŠôOºÐ )˜˜Ù"Ø„kþbO`Ií°ë/üˆ=|ÝföÀØÕlÔ™/b×›ÂÕçÍagœø@Щ+-¤àSwiÁb±ŒigüŽ ª {]ÁR/_ ²‘Ù€ž£ØWüĺv#{àê5lôðÙH¶#ìˆ{›X‡vÇ÷Á‚±HC‹Cß/í‘m6›¡Ý›’޶9C]X5V0ÿôó0¨6nÔwìÖ´ÙÖe búytnŠôø‡_ÏßÁ­Ðiû3/a_øŒ&¿³Î׌gƒçåøŽSÞÿÝËÜ.‘üîÏ,ò˜cë)f½½==õŽÙÏcØ0¥‘ nÅá—êFüìÖq(ëÙù öç_YÆÛ]; f·]‚åòl*…¶fÛ<Çõ¾¢ÒýÆ\TÈÞúL¨IoS¼Ã1 4ìÓÝ!–±ýkY‡¸¾ì`áföê‚sÙÜïof}»]À¨‚&÷ËšçØ©R±/nû@£ô+»õÏÿÞŒx‹Žœpü<ÒòÏ:å¬QþdÒ°œëa,s9“½øÑ`öáw7°^Ïb§ô½ýÏùlõÖØðS&òW}Nê8‡EE­c‡œ ûcû!V„åY½;#ÕO æùî=7Z ãŪéç¡qÒƒ,:²âûfúýåçñ…À™›pù¹Ñ"If ëríx¶ÿ“Wiº 3Ƕc¥›×°Íé·yÿ¤a+…ìà÷óY×ëîit\Æ‹Þc\î _’‹æE WN;á¶äZæ .Âdžô»‘}ðíõÆÛíœÏèðºm,·`ë–ü¶jË{lHú`Ñ6¸ß-ìçÕÏ4"¶ŠW KØgK×"ï­ØÚ‚ü¯(ñ±Qì¶ËÎ,7ËR)”Ø¡¢2öÕ/«™†9ÏEŧ3[ä6lð°­"ÀFþØë2˜‚#Pž?5Bرï'_H¥ŽƒØ]ìv0Š7îý±~»ûÊ?°¯u?–_¸É篡?¬Ö\–Ôa.´ù®,?ÿZöÅò-ìW¬mͬÖÖÿØnläÙ§T –æ@ƒý„5Ìz U³\6a(eÅGŠ˜9*Ž™#+y¯÷EÕôӋɉƒØ ½¯bk¶Ïc=;é +éoŠo¿ßñìD|ûk|‚6ð‡ÿ·äÀ¿b¬i#Y°s–»q]0íϸɖV°Ôk= væîb%›VW“ìÀü·Ù ³³Èî}˜s×ÖjÏ[óŠ/º5§¢dOj7€8¼ÞU¶€€e¹zfÿ¡µ,1®7Zì´‡p­Äøc‘Ï–JùÏÓig²Üüåü£üOƽærÔ7J}¤††ˆýs%ƒé  ýù׳#GN‡ÖZ½Œ6D¶Ž ýY^á„S±µ`»˜î¬_‹Ù%§=ehÅÔ8%W\¶r!KJÐ(jõaÝË’“ÞFm¯²24¦t¬ Ç)ÕôRS¨6UÓOßüˆÓžaßçd`dvåÅ]‚þšÒ÷"{ôeeoÆâ-SæØxÕó8ÖãžÇY‡óG3SLœ/™ž‚L-.bQ=ïÚð"?û2C$M-†ÙÉb£;±ÃGvÔ+ªCÆâäKˆía˜*ëõ’ð²´íËG´±âj2^ *.Ûí÷ß³BääÓ|×ý‘Ø.†Ý~ùYØš/›4DýŸ5…å%`™uêØŽÝ2ê,–—_Àöåd+w°C…±Â¢aäoXlì5½~Ô{íc{³CUÊþ Þ£Ùé'ÞghÞKVÌ0ÌÏ< úNâzòË€ÏK4ßÕ¬¬l»ñÒÓ±oq<4ûH?ìŠh|pwBßnŒš’E›¯).a‡³ûóÙ·«ÿfÎúíãÀƒó«¦ÿôA÷"Ïs +ÈÙ úüñÁN?7œÎ‘{0×¾]¾$•¬[Î"ºö`²Õʺ^{ëzý}lMÊELw; ?NøèÜÍç?\~®GNÆEw6|©çž—Å¥à_gqQ]MÀIÐ:Æþëk–Ø>á¨p=’ÀžyçK¶ú¯-Þ}þÉûá?ÚyG†–U¨6¸hfGCHi5yiøn§îÿ÷ó' èÛÂá²ñο"~íó_XAqã,pqÑ]YMyÎɬk‡SØÛ_^†®¨gåŽüÆEwá—M~&2™L†™šˆ ûŸ²ø˜(6¨‹‹åì*bÅÊ4½‘öwHOiÙ“÷g¥tüŒ¾î_׽̒1 mÌ9op{±/÷öý9fÁt6ÛfV\2”í+(Â÷Í, YÒøý 8˜ñù‡åŸ~êzúç ñìãngd±E$`p˜•µ‡ÅëБíÆk¦¿)¾ýµ·¯yûÙé7@ÀÆ~÷5|§‰Ï£ éïU}[;±Ò+}Þ÷}Œ¾àrgMîÆ½4Ÿ%üã_ìÐO‹»nhÁÖÞz˜û ‡³0A×#‹ËˆP‹êTß´§küIè#ž®µ#@nÕ¼ÿÇ€;Ù‰Ç^c BªJÎä—Þi.ç%`#‘Í苵bB4æ(¶>ÏòE«¦ýWÑoÛP¹(-15”}jtì;¸šmß÷£1@‘‡Ûé7™JŒà‹ŠJËׄ®ÿÖ„\®ÆžýÓß³Óÿp#v7êvrßë ò½}dÅЊ¦HceÕ÷܇ò˜¹=Õ“Õ™œ5t4ûk¼ÖÄ$Fï„›\õ(e¬$L­Üú82Y’;td§qn‹ÿfÌ6]6íukp:[@ÒlhЉÕBë¹3vRŸkØ9C&aÄ󳯽®‡0:¸K0L¶^mˆßkê3 Т©:‘˜LðÚ]ìp‰‡uìð‹YPô”~ÓÀÝ Ç^ehÖXÖ«ËY~6Kø‹?F×K/t×7ýªêÅ^Æ^Ù´± ß%ŒÌÐMíüÓ¿bÓ;ì¹OðþõªaåzaÞ`ŸM‘~_àaòÕû7#34wI_Ã" ùZ;v1¦$É}Z¼¶b\ED§c½nN˜ ë™£ÿsÛ zùîÔþÃ¥¨Þþ‹z½ÔÊ=ÍxÍrRïØã6Œ»ž0Ãä©_©+êBü¬¨™[i ж&æNÈÿ]~Ç监‰7ì¿•R4ãîÆu§ÄÙh…ÍéH ¦ù²¤8\ÂÖíÌcí°—ntÔÚ€ÅÈÃàÃ=GÂÜk2buK†©F“)x…%»Ø_;>g¿¬}Έ'6ª³1‹¾—`:7æ6K ßB=ÅØù‰æ=7‡óO¿ª¹™ŠwªæBY×0>€zsÈJÖ4éçñ…ËÙñ÷&–ð`j2±âOŽiIw3 }ÀÅë—³¿_zŒ•lXa$×’˜ÌÌq ̱ƒ¶ÂÌ ®g†þ²öYvíy¢¢y¾Ò€“ßÐFw49Xÿq쇙üV›8kŠòìVÇ®1H,õ1…•sº‹Ø²˜Z6ènƒ€ßXtq­éëžüOC[œ÷‘tó9"`:HþeÍ6˜IKY|ü’ °~ÇgìôîƒÉ}Œ1Ï÷‹ßhàÑ£Œ4àªÓ£Í»¾dù‡7%nHYÙ@–`³¢Ùã]ñ«>ëAów=WM¿x?®|‚ÑÁ]S¥Ÿ‡.çC?}É:]yë0|$æù~ƶü÷N´iQ~­‘LsѰ” ×éŠ[Yáß3Ç®ð#`a‚®Èç:åæ¯Â¨ÇÏ óc]û÷ÁÙì7O´.ß¡÷lZ¶<ÇÃþ’MË’¾%sòܹ­Ó²ä—0ŸpŽ­Ó³MW?ø†À³™³dé´légþ®¤ë½é:3[Ú ?ÙOgÅvàÏZÛ™¡ˆ9Ókú×)ú)ýnb?­ùs8×é¯)’9Öéò°Í»óYLÌŸ¨ÏÐGsÛ¿Ï™ÊNìs5B“|!V%ߨÈDÖ»ëÙh|Nóù Ƨ³'s{’X§øcäss˜ýå®-ýþ~èwS¥¿j<áp­cc=o?ËÏ½Ì ^#M(¿UÉ×ßžÅ|Ûûî áìjip5Hj¿ámù×þœž¬Ûñ©qÔí+tŸÊºùUò| Ÿ&)§eYÇÄÒ“osÿ5-kãͨ;ÊÑq'0GYOMUæÏ;æó±cçÝ ¢)Çž9áŽ#‡|©“ô[a½!²]ò:çáÜ™nVJòLßóVôƒÈ&{þð£J<é]GõÓˆ”h£ú܃…FðÖˆ½AfËîou9šª5ëÓŠE)êòÛg…Eç1‹Ig]¢ŒÑÞ|)JÒø›Ëµdú›+ÍOѲu9¥è[H:L]XkÀÜ,ç={W’ d:Fs•]@…ãbÁÓÐ\qOLu¯EW›:#Ûê]IBRÆb+Í9Fü’~‰®Ë³¤Y“Ô˜í¨ÿ¶m?òéÿÕ*›.-ž”âY6aì‡,I u>Íë8~ô¶†¼'t“ÿd’¥iW4@‰œÄš§Ôˆ¬ ÿÁ°âêκÇEÀämñ­ MÓ®(oks<ß½gñí׆SïÃÐå]…ÎØÒá’-t/¤[¶êª¦–BHùCBæ°Õ€kú8#@jGñ!|]B¨j‹Ä"ôþ®¦ôø?îoiŽÆ”±ó?`ƒ1’d½’1™ÿþ%1µ¯Ê ½ó†QœUMïR[ܺ,åðgš$•à·w(+¿ÙÄgÌ¢#1V ý¼'H“ÿ¤+ŠÂbmÞÕ×<žd,E¹µ‰nÚàÎcYá‘1ìFȺ¶·S¬hš•ÅR÷¦ þùÎ%ß>G¢ÑçjDëvº Ë¢âè~í-¡FG¼K-Ѻ§ØQa™ó]-=Á‹±þ!…-×ÍbbªS]$«„æe’MÅN6ÑØÿ´¥œÕd}ß­¹=ñZÄ{Šâv¦¥¸6²H¬£ïM»Cù¥B¶:ÖðÀŒ‘ -û+. Ûô×PÎ{B¨±ùþEú4f5KX3Š*>Ó~…–õLƒ3_Ac)ÿàXf‚ü}#ŒéU8GDb-h+™™Œå7ë°øöë‹Týý9ËJóÉ$—Ö ÅUÿ›Ñ'Éæ´DIn&‡  k4áM­a~´¶@‹“°L]¿Ì ¯H$ÉØ)!Ú'ws ûðοç!EñLäró³!„´µü¸¬¬.QtùòÜŽ140‹~£šßé‘“Eéfˆ8ÊÿB>ï ®Ææ?iÀ¼ø„žÉÌ£$`3ûá!’ ÃWû÷C¡Šd½ãÍXUËÆ¢£Ñ(ÅA󜭆)ÚlŒø®+dþÝÓY|ûu!Õ gÔ¢3Ž]›7®ÁiWùº ¥™<“l$ãîm[i2<—½™b?z4aMÀ¾¸g³ oUâ'™òA²‘ò›gƒÔ‡æ1¿‡)·šÍ¶xÜrtlº.éí°Òð.ŒŒ^STxpÓöÃ۽˃éò˪¢¼…ÁX‹¸ÿP8ó2„u•mV9¤óžð FþwOŠg=:Dc èsXIIÅ!¡G“–›ÌÝŸ S@ 뫳ö1‘ñFE€qКØ4×ùhËPò|§oH|ûGC½ÞÏ9ÑY[·ü÷mªÇU´­}?ºIG²iŠûpÎOßo‚€þò‡„¼-gçlâäû>@ôNxw‰1ç¤( Û[Œm­œ=К&E/tÉäpôa}“¢aéõÊKãLxZšSÒI)Ú,ćƒÆ.xݤë‹ã×åo¼Ñ3²À]óи#ÍøøÒRÕ÷²²†ÌsEí°Ayf“S´›ø{tžllOèrwéÕüù–93üïÝw4óÞ0A¿r`ë¨hÌâ…Ùó²æêÁg4׺²XšãáfÇÆh¬3Ûˆ‹Å*^±¬]|<‹oÆvŒFÿ/aMû¥<­šïô-‰o?àÒÎ5G"1"3`é.++q¬øå§9‡m‰ìÇîçÕ¿e°8u@²L«ÿøíÕ’ìàá•—“0OKÝ4ÃÓPÓ€Ë ÎÐMÚ¤œFKÒÇGÓ"ð!ÒHÊ„(+u»X¾S]ó¯f‰>i1M˜4ª|K±Ô^·X™%·‹.—Ó[i4t´g=Ê /|ü\WšÜ —…ŸƒaÕ2pLÇxv°¨ŒmÉCÞDÞ'¶\ÞS"ƒ•ÿD:[¬F?)iŒ´f2Í ¦ÁY±¥,¿ØÃ¹¢aê=$<e^a&óa|#E -²˜á?ï%B2lqˆ{q²‡EãÈSm,/ïz–œô6⡞ˆ†9§«';H ¬êdq²HŒÖ¦FwHn¯ìÃÄC¦g¤ùùÆâˆBß/éÑú~¹TUó½Íû´†gàæWÒ9ùÒ°gãXõëÒµ “²A'b=/ÖÏÞõ­ÔRš0i¾D¾ÛÚõ‘vl\÷Ñò¿£©\Vj4ü!£‡S1œª«eN‡®Rû¸Š8’¦0à#¦AG$KŠò`ʇ›80Ϭ}»…ÍÞ'L}~d 'm¼S”ÎŽI mä" 9I^’;X&h§Û£cžhåEV½pûp÷^6ûÿjñÓÿ`ä?¥¤¦2p\—vÀÜÃvhhu-‘÷$[0óŸ˜H‡ÊŒ¦²x±ø€A¾‹ÙÁÚ»\ e•¸uVªJØU(ó‡Ûµ3ÕÑ|q1f…EÊ‹6H’$E9TT–ëL`¹ûî!þsð_h(æâYµì£ G#±ø¾JK o÷fF˜]#1Wa/éÂ4l‚éóz‰\­¡¹Ó€+êû¥Fi¾D¾Ôp®é™ÇÍÓ,¾}”3|û(e›òsíWÅ£ß%7A‘Q7Õ'tØ~˜ÿÑw‡/±A'8ÙNÿ¿Ý?ÊÍÝ'L}¾dv&ÍwÛ_k>ýþÓy‹ýd$yInÒà„ZæQó ‹J©€DÀT)‘ÙŠˆ>f—Ó…ýD]¨xÑׄÅQ,hôt`ûón7.ˆ‰^ ¿›P™Q~ßÑnh?Tùx+³¹Üì°GfÅ®^,¤H{e© æ¹"c Hcã÷–AT•x˜ù¢ •xÍ{íÍnÖÑ&ƒx#A¼4â-~˜Þè·1ßÑ `ï’”þa5ô7áHx:ÊJâ]*|ühhPMéŸË¤“œÁȶ®2Ðe Ú\Ìöº`šmº¼'9š#ÿ9é€Ë ǯiڎуoÉétâ(o„ú¶õó’5½D„m˜oËÇÐorô½Da™ËX››9°áC‘CaÅnPõdnlÇ ‘Fhá(Zf|?ÑxÇbq³8³n¬êfµÒR’VƒT‰d£l6.ï›-?—§‘èÃ&y黥Æ35H®†¸ºò½-~ûn‡c?ðãßXC ô÷Kïq ˜È*h*qÄ%òÚ?~]·eõªíÿ¼à’ÿSúõ;cWî±(Az¤§Lò”Hš'pmјe–Ù€PÖ *Ù¹nÍÒß¿^ü£ÃQz^H¶âò3ýö×€)!áB€r`Ï®Ÿãï_»u/ûÇ ÞåÕ¼-lÒ€Éôèßê§ç&““™a–k¯ºêæÐLLA߬Ó0‘e ŽXª˜ŒRŠ‘Ÿ1X>Õ[Ÿa.DeD„KƒNââp”8¡ÊŠÌt ­xj’•pD? ´{˦UxNæçòsM¯4÷=y4’³k¯c¯FþSBŽV¨‚O‚fXì„imd·†Æƒà_)ºPrÍ™ÿœt‰„)ݾ´ƒÐlÐ>N‡AÀ”w7²QC”6r wIVÃ< Ó0iR9ñ)ønœ°‘‰#*ÒÍñ¾÷[‚ñ ÑAï{ÃñZŸ( Ò¾©AI­1—×ÐpÑÕb¬jE$KfhïèfC#.ïæZ/…×çK;Àhëßþží•¢¤DSýAý¨Ô¯Jš% Å?ªâ¼Ns:ËÔ%ŸôýÆ~ì3ð¤cz×ï8,¦Ò¹ƒUê/™±!œ[²:ͶÈa‘ —«,¿ØQR|øï-7mY·z‹ƒ4]"Ü¢òƒÈ˜ä$yCªÿò­:‡‚óUÂK~²¶Ï‰',[¿½=¸q__yм´`|„Ø$Þ¨0x…êý@½„Óå9+ÌJëé¢2¢J‰*”`9’ÿB¤J‡* 2;“æKäK£>é7i$¯¿ 09€£®©ê‘µË~ÛŠp¨ËÍ0ó@‚ô]. ÙHÎSϽ°rÇšÿ$\}Ë€Õêdq>­H)xe 9óß›^o×…QÆË"’férÙ âåä˵AJ+½Ç±â$L×ôQù:q”9Æo,G´þ´ªzqà í Ã÷mXI›¥2MñÓÈlÒ~éšúué; ¸LFC˜O»«øV)\<-mýÛ×5­pùÒï6”_þß[càåß)‘‘.*¯£99“©—LÓ–­ëWo;¸eí«{Úî·Ê’—|5V¸pWÙ‹ecÉAñùÇIZ.‘-i¾DÀ´5ý&Ræý¿ô^ȸPÓ€ L£>¸/÷ýu’tÏ–]ûYßîŒW0Sß9jTSÿ*™¹¨"ðö¡o˜FŽbð × Èo DL•92å‘&™ú¤É$èíï2ú|iÄ'ˆ×0ACK÷j¿ ëó2"©á)ž§@IDATá·nëi×Ö-_á1µéà$Lx‡‚óå;„1d$y±€ÂØ`ä?%°¥Ê@Kå¿—|8 óÒ^M”¬@D¼¤½z5Ø 4½Ge•¾ >þÀ `¼ãŠ‚ù&ì(0}/n030o¬xß'«’wZ™¿ÉÔL„KxT”—ˆ‰”é·òÐgByDïÓ,×RùNòót´ô·Ÿû÷Žù'˜ß='=Ò*yfù3ݧ“)ò†Þ¶;cÌR¢áA×]ßísÏù6vmfN $¯7ˆð)N®ý±“æKä²Ú/d I ˜ÈAY:ÿ“÷G§Þ}íû_þž0ñÖ‘²Õk¹ ™åèC$â#Ç+ 2{ùúƨuOƒ³Ê5 ÿJ¥Qú½ÄãóUJ†öë•M¦12A“yŽ"_’“ä Ô¹=*~ØO\9òËâÏ¿FxÔ ¤ƒŒT˜yÆÏq\#ß!!#ÉÛýØ>@þ¸`ä?¥¬¥Ê@Kå?O3¥[Æd2éR¹¦2Æ»aÈ*DË™$+¤‘òßôŒÞs{Ü/-DLÚ/]ó©NDÀÜQ|DÞ†ùߘ·‘Ký¼Þ‘ÎUûv)ž¦t-•ï”&J›òI3ûþL Q|Q‚ýÝS†ñ‘£o˜_’&JyÿñÑ÷uˆŽ3ÊüýrPyù¯"÷:< ÄñøxArPœDÀD¸DÀ4ê›’‡žWP\„Š E ˜ÈÁ}èP^ñ¦U9OJƒ‡M{ÁÏú£Ïè+å‘›ñ†WÅÀ˜Äˆ|JM ³\ùèP^1¡j°ãõ ›–Ä£J‰WD4ÂÓkšóP¡ûö{ù I¸å沿,y»¬¬˜ %F:øÇ*…’ä0òËHò’ÜòÙçÝŒüG¸¾Ê°¹Ê@Kç?¥™;"rDŒT¦¹ÆJåš—qzNe•|E×^ÿ˜¢„o‡ÈÛcS â­Oá Ïxÿèjlr-šÞ#M—k¼ôŒäá2ñ÷šêÌ¿¿æÊwJGKç=ÿö×üñËÌââBþíó»ç$Häæÿ›ê"¾È'†´#ò½@ÅSm{‰úÚâÝŽ/ðŒ\cêsЇ0¥‹âå$LçªõBÇLÀH©ùƒ”$ªˆ TÔùÓŸÿ›ð:~ßÁ>aúM#ÏÑ„½7Ëy+®SKžúż}ZdšC¿ý¡àøWP¥AŽâ4”7^)QeD°¨B Ò¥Š©±£=k†4_ú—oØ!Ñ|¸•?ÿ¸þ¨uHN—p%öå;ä2d%¹1ÁŸZ‘ÿ§œ𝠴DþS:ërü;¨ËOÕg²Œ™0MDJÓÍТi DþÐÙ—^”{Yòš¢‰ˆ½„ëÕ›‹xýÓÀÓLgŠŸd ÷oÿïÍÞÿó‡oþMõÝ©ÑwëOˆDÈ®9g$>kew(¨a¨’)tkŸ?µ®ø9ü$âm ùâ5ÃQ\ü ¸éàwŠ›ê6:ÓAÏš¦ŽÓ¥X„½G@.`–t) HŠŠ—y†xThJ¿÷Öû]s#ÉyÓ¾‚Ãú5þSFŸ°÷C¬è3ˆ• ï#Í€›Õ%`J}ôÔ*æZ™ø¨2ó^{µ’)PG}¦dv&ÍwÛú5ó¿ÿlÞ—“›eèL¸òÂIx‡‚«–ïʘ_ˆÉô_âs#õmT°òŸìͪŒ›¶ ð^6›Û êÓM6°·tBŸ®ÍF¸¾Ö;‘-MŨ0ÍUh¾þ• ½ÓÇ?BÿJˆ>H>0ƒßoH˜UýÒ<_š²C£iÀ•¦xŠ–/ý&£¯‚_Œ@ùBñ pÐè@ú ¹ŒŸ!ájÌwH–€#î¤ÓέÞ/Öà1` £z¿R4%S+ÓF üwkˆ£¡·H2) ŸˆÂ˜g¶ì‡o~þëÏ?6ž~ñˆ‘˜Rs>ˆ&\µEèí⣥H -¢¸«J¶U¯ÿù»U¯ùý†ži¹FZ1Š­€œ’ª*Å;7þµô·¯ÿ€ÉèÔ5@­_"\:Bz>ä«1ßqß輤•m6¯Z±å´ /9é< ù×ùø 2¦3w–ªù]õšÇ#Î-‹@Õ|®zÝéªæuÕëÆ„IïÔôíÿ½yã÷¿}¹èklP/ÍþÝçŒHNÑ5ý9¾t‚Y–—0ÉtÙ›«R·wÁ¨§9 S˜MN¸\p~&®CÛB6ƒûø½Æžƒ—>u7ŒNë²3Ò/n¬ UÞ£J—ˆ—Ì18âp´ÃA1ÙÞ£?yÈqÝûöd‹Šé`µEƛ͖ÔœAIÂok&»\G‘£¤ä>¾›×®Ü ÁɼLž/DÂô!ÒIL„LÏÈ^¨¹£æ;6æwÂ)={wüñ¶˜˜ö6[<Önü’i¡†‚G pðí;ñíq”–ìÚ¼é¯MkVlÇ+-öÝ/‘ôâ lhÔÁø÷‹¥½táIï z(¬\Jzư™ š1å˜@—–ñ0"O¡—óì¬Ìô¥ Uþ>™$9 “9šˆ—ˆ˜™æn¬AŠ3ù¥Ê›rAI—7¨ýÏ[D¤tPß+Y¨Ÿ—È–ÿùpœ|Éo¨:‘3B®P@ ¤¾{²¼åŒêüû 6´ü?±²ßù}çl¥º'¬\jZÆYvø#(æ‘Ù™éOš¸ •ÝþFd®²gò¢P²t<-ÛžJÃÐu$׈ˆh£Ê"_"d"`C™L霄ƒ’&„×}Œœ|Élϵ_c®‰€kšÇ?b<9'ò=ä²Db„Äw¿ô¾ùçÃ5-Ä"ß›_zëC€ótn‘ï~ŘNÕ2}1ªŸ¡>Tº¯ v×½’ÝNü°r¤ù2Oþ›è÷#ÉÒƒÙSÓŸ VƒJÀ$”—„ÙÓh%¬Ãáû‚Ø'L²ò ™ÈÖŸx9ùÒsrAO—7ØúO 9®Õò‘ÎDÄüš?'¿­Ñ‰|o¹&dn*Zô»_qiד4]ù dÔ“'ècCåMå×átööù²‘¦XWæá`’/áÔ$DEæh„=æ‰î0E‰cŽ%/ Ò1Új¾7Â"¦PD ¹¿{jéþß®z_°®;ÇC‘ÍÊÒîçmؖЗø ‡Ê4VÛ.¨V“iž/{@'Ïwµt})®j©YjíñS¦ÁB€ à.ˆ”¦ '!Œ€MqšÎß¾¨rin"³Ô[öuï‘ëÚi gØ8pqùÒÊÓS3§NÊiêÄ5 7u"Dø€@@ \r®èÜ_÷¨ó¡ öå!£;q!¦]ŽÓŒx›ó,¸9Ñq V€Àò‘Éסÿsº k%ˆ‚¶ºÊ²`ÿcAšbÚ Phz7=Æ"€@@ Ð*X}cr´çþ̱·pÑç\Š dn¼`ßÇüž8AÀÁÁQ„"Z5«Fu9Å£* Çù%d³d–®:ÿÀZ¿{âg n @F`ù¥É÷+šú; ð‘/´³7­í¥Á‚|›®` ¸é°! F`ùÈ.˜¦¾‰é›´¬×I ,Iw]pà=~Kœ›ZMJ8€@@ hc€|/`ºòú{»ø’.IË$³õÚ¡ŸíÞæ»'~4Bn2hEÀ€@ ôØr}Ÿ¸¢¢â§1Êy—Dfÿc]™<4;‡vU®Ü ‹(@( àÕzU,¬ûVµ‚¹9OÒ囆,Ú÷U(ÈØ–dÜ–r[¤U h“Ô¤õ´s%Ý=xÞþü6 L 'Zp g€ˆ^ 4%¤õêºú*Óõcx< Þ|]’î¶pÿGüž87?‚€›s£@@ hr„ÖÛä à€!ÐB`Ù¥®ÂtÏaç®\2Òzat¾{è¢ýóø=qnY·,þ"v€@@ 4–_~̱ºÇõ¼È?PÑ×ëFèü:y!$B`ý˜VGYþ£Ø1a2fEò@PÁïÓ%ù>Ñ×Ë ­³ àÐÊ!@@ h+.M>WÕõWðRÅ2’°?c3ù—ãÛŦ‹­g³zܬp‹È@pX?¦g'‡Ãñ?Ìé½®Rˆ´š•dÂR’¹+*Ý!‡€ØŒ!ä²D$jG@·7cÕ=޲²þä mªH’廇ÿOA¾µãJO„J¹!du 3¢ó…:ÓžÁæ ü½a5«÷"d} Ÿçð¿/~‡6‚€C;„t€@€a1ã™®’¹ù’JpHl“Œ5†,8ð}¥ûâ¢U ¸Ud“R h‹¬Ó­½Ãáùˆ÷.¤ß÷º#L’3£l‰Ï œ·Þݱ ‡4 ‡\i êçÍÉÙp—®±ÿ`ZQ{ž8‰F73é5‹…¥Ÿôé<~_œ['‚€[g¾ ©0E¬.‘˜Nææãý“ˆ~Þï%YpÈçyküï‹ß­AÀ­7ï„ä@!°òò®'«Šç ,y² õnÅÆ [p`¾ÿ}ñ»õ#Ð,<~Êô!ª¦^¸zè’Þ‘ƶ~èD ÂìJ^,éR.Òö·I6}5sꤜpL§HSè °fDro·Ä¦b·¢kQþ|u2~AÊ©¶¨Ž/Š~ÞÐɯ`JâËì`JaÙíoDîõ콇IúýLgÝŒ¾ ]:ÎE¤%ÁŽO„'¨c@À]Pn“¡‰È¨÷`E¡ç»Zº¾d·ßê F" !°úŠä$ÅæÀÔœŠrgñ¡"1UbòlÙÆûôúP ËMBÀ)éSÉ„2Xwô[|‰cŽ%/š9iÒá°DQ$*ì?}z‚V¦@åx=Ž‹Ð€Ü…DŽËΘòuØ%V$¨YØ8ª_l±VôÆS=„:2Æ?rÔ•ŸIyòO÷mð¿/~‡'A'à”)`äÞÓ’¤¯ƒ&q_VfúÒð„N¤ª­ š–q†ž¾ˆô”döpöÔôçÚJÚE:ƒ‡€±a‚óàÇœŽ…4:V y©,Kb>ïïUî‹Ë0F ¨ì%_ýYÚúŠY:Þ’mO- cìDÒÚ)ö¬(æÉçI–$܆2?À¤êv»¼bÅ+×išô8úy{ùw v0š„ÝŠûß¿ÛA#`2;뺴~’•‘v5 º5„„0EK©é™¢`Fñ¾D˜£Ã'o›"%T^VŽê2ZÕ5;ˆwPRvʲüØ)CRçHv; ,µE‚² ¸x³ÉìLš¯ ß¶X”Â?ÍF¹FùFJ×ã˜]^îÃ?á"… F`ùȤËsF&¯ÄìüÉeè ŽlQú ^°ïA¾ †6¬^ ÓhgcÀú|…Ù9¬Ê‡HL¨|㣹—Ê»1Ê¿ÊsqÙ¶ȹ4yä²K“V`̧(#'ùÐ0½ÉÇÈíz]xày1­È‡L›þ¦©FhÕ})\µé²ÔfOåœÊ»1ŮͤZ$´.hõªe#’—iºþ9¦]žÂýbô| ÊÊŒ‹¥×ÐEûÿsü益ù3qLÀ´È†wž¯4GÀ)h+ RCåÞ(ÿm%Ñ"ÕÀ.E,‘ü;ÓµEX³y(÷€òQ†9äO1ÉÜ ï¤þŸî-àÏÄY Àðß]ƒßkЙV¸¢E6hžoƒ^ž­*ïz©¦•¯ð&VËjÅyÙÑÑ¿{©¦³Éº¦œVé}Ir`€ÕL‹Eâ¤OóÄf •ÀU˜€`¬t@,²QZqÎPyOIË ÍÏ{„s:EÚ*ÐÇŒ1­,[:F“Ø$MÓO¬xB¿$'‘l›Í6}à¼û+?Wš˜€imgMkç 'hSвªH0•áÂZ@Ãé8xsNÙb Ú±èzð9h».”ƒWM²uÚàÏw‹zЇŒøQ&`Ìû¥ÄÚÎõA[ø +Pö©ÜSù. X}cr´ç°žêpäÓ’‘•Z4¸ŠIr³DüoØ'ï Ãä‹$5p3ÈØà(Æ¥OýY–¤w³¦¦ÏjðËxáû³íœªc଩“iÄëG}¥¹ÓsTBÈø´ŒA„÷eg¦ !±„(­5#º'¸eç½îÃì>h»‰•“"’%ý…H›;í9Tù™¸4 VCÀ)éß`e™ó(y4è‹ú1 d§$Éov6õ}ÓnëæIG…Lóï¾ã×M}vxJïÁR6§Ì˜‘=q"m!TWŸôp|L&ù¢Y§}UU€û33“ËÊ´ý²I•õxú‚ªÏvm·ë²Ý.…ÜŠ=mš„rá›öq´tˆçÚX~ù1Ç2û~7s݆y¼ÑÕìóŠ:'—Ï`,Ìá?2âG < )Èü®ÄÖË’i„ÎLW`=^,ï&åb0Ĭ½Êæ7V_%Ë<ùpSoCÅÔT}ÖÃO=…Ê#¸n¯’ñ÷½ö'º7Tš@ åÈÑùÌå—&Ê×f¬õ}/ß÷ƒÆï6ìI™ß+®÷ÐEyÿäÛòùN´ ¸ôCY“ý-ÏÆÔèŸIK2㉬©W·DæÌÌxt⥣…4B...rfB‚%ÌS¦Ÿ¢jŠ ß`*ÂiqtûpsNΆ±ÐtÔ˜:ÔOÙ5dƒee.±C"Ïš+Í›§2FÞ…ÖFÀÕSo–?a-MÒZìÜGÀX]IMŸv•Æ´ñØâ˜«Âvýi\\ÄãO?òHiêc™çB[|Æ$›n™5uÒJÿ€ï´Oï©zÔùÌÄŸýxúÇÞýåÑ0I‡Ö± ­â?¡}ÿ†M'žÂ‡ªc:ÊÍ0VMÈÎH;™®yXw¦O®1) ïÑ”Z/{“$™îÏš:ù7ò“šþd_yî•týÈ×a‚Œ_™¢åI™Ö…¾©š.e‚„ÿ“2eÚûÙS'ÿAñÔåÊwùyB_€t%cñ€Õ&INŸ9uòOôÒ0NÓ•Çé·ËãùXx çA“d¯èž¹Hî#þ›Œ{,ãJ¦²Ç°µÚSYSÓÞ¥÷È*6¶·H–k^ÉxÔØëtÜ”igKš6qŸˆô{ÿr3³NäϽo2†8±Ðô°lanÍÃÿá°8ÌÀŠTÜÿþEþ¿‡{‹fg¦²û?¿Û.+/ïÙNQœ)Ë—ýu/P¨Ô¨DùÃÆDÒ([Ï ^x ¼ k^ÛK¤¼É@YkÝNV´v”UÒ+7Quýr©Ó$¦‚k<¼,âèŠ ]O’ÿÎr—_pÿ,¥p]û;Í£^‡gý##奴,ž}¡3͆]“1òq z¡ãCíé#[IêPN²¾`R§dÞ€Súˆ5øûÞ{ï­“LRňIÝ=¸œ|¿Òe‰äXoRJÕ}5àÈ>JêÞq:jÓ´Wíö¹Öº^OÉʲèž|8v=“ÙBéC¨‚¢Mû!å±Ìsè]U2mùÍ5ÂÁàY’3tI~.Ùüï-„ŽKŒgüŸÊ®G8Ð/{=¿Eg`q 52†ŸØk3]ß™–q½¤«ß£±p…ð~éÀ§ÂÜËS§L«¼¸öáÅ»|?CØ[AîdÙô…SÕ¥dfvF-ú îuµ7£êsqÝ6ÀŠUÇcæUcV­z(T/Ï@ÙËf²yV­QA¾m+‘êæC UkÀsçÎ5}»zóTÎ…Ñæ˜å•`“ôã,Q¦ž/Ož\P~ÿChªñ ‚q}·Ý~«×ï€nQ=ä?ˆ ÷®é,~qòä|hSÔR>ÔÕœ~«ß ¤×+ÅUå‚´JÛ“ ‚ùЊ¯ô5c¯ú{…÷!®éàîcŒxN’téR~£!g46lÙ©©žñöŒ;TEÿe¯góD¼ÿxmaè{òÇ¡áp²Ù"ŸòŠ=mU¹¿×Æ¥gìS%uêì©“7%³3Óôû"ÌÖ/ÚÝãõ7™4ÓH-=øÌ3¶ÒC¥;Âçæ»ì/Ǽb¿Û;`E—Îy~?vìX•ú¨ºž>ÎÎH¿Ž¿o·ÿðN®òóÏš¦=ÿ§nh—°4“I³ôjÌøûã§OOPK•¯ÿøvWØ©˜ÇýˆsÛA€ÌÌ+Vl…òtV¬:eµªÛ2ö2“L³†.Ì=Xõ¡¸45PçO"<ŽºÎвAjz|#Rù9Óžþ»ÎdT,zZª}Ú€Ú⃱üZ¤p•ù^±¸À—ø1äÞ^ˆ¨í]º´adºt"dzäó|ܶ2‹9q[<Úá ÉF‡Çãz0ù§ëâb·p~‰®¹³ÛÏQÞËÈ£SïúïŒ^ü>!Ów5î6žÁŒ@²ª¥*,’c‰AÝ þï‹ßmå£{tÆv€å,ÿk§¦ª£õv®êQ×`/Þ[£¢:ö€Æ›1t _|ÄïæC ^„Ò|â-&Ì‹wCCu€yoÕ7Ad»ªÞƒø0ÌOÌÅÃ4›=uâZhq ÏòVøý˜ü+Šó³TÐI>ÎXÛƒ¾>‚ŸLð´\Ï/0ï4;2ZžIÚqµðËoÀLÛ[C<&‹Õ0·Öæ4ƲC¥× Mç€t{BóM„æs6c²¦Ô8²˜ÛMV”C—éŠ S´~Æa6­šˆ³7ÒjƒÖý—ÿCÈ@ Y=Tz Î[ýŸùÿ6™Lßjš"•*γpÿ3ٟ̃ý6Û>q—«¦Âý¥2å,¬›+›Ì¦¯÷uýX:›,Õ–dy³®©LU”>ð²ÝðOÿ$kè¾[¾ºTêÊ;2rŸN£äŸµOó3}à´+Fv:³"îf.•CK¥”KèMaÒç˜NôÒ¾÷>+FVÂH\4;UòÍ.-úq±à±³3¦ Âù’ì©éÎJO¯F¾år•ÕG>ôaÎFå~á]ö';¡Ç¥t T»üMÒd”΢¤^ þwááW™º“ú0k ÄEÏ  :kó“bºCIAÙ*Í0K[dfz~Sþkµ½ÓûdúÕeùN4NÛ§dÜí–ewÕ÷AøÑP-s0èêÉJ‡,M4ACP"¥ZÖ+öI[Q©íDÝ6œæ Cs)ëÒgF<:*;ôûÒ}]ÓÏ¿í3í“*ž¤ÅcøõÿgÒ{˜fVIû†ÖR«FK§¢r]‰†Ïóÿ~â‰Xÿ ÅïðE`Ëõ}âr.ít7v$Z¯jÚ”ƒ1UÈw?uS­VKOh»£+È7|1)k=´2 8øÀšÌí?ôx?«*®1w>6}9ÌU=tÙôVÕ˜²ÓÒhðÔÄ{í/Lsy޼â|}Çóü‰š¿"ØJšvYwœ*™Æ¹Ýã¼ „uLad=žf˜féú¥¯Ä}î- óìÇÓ¾€&úB›¦ºt_; xxÍϸˆì½‘Ã: TÇG×n€¶»»3ëSÍeʌߩŸ¿.ÿâYh#@[®p.½ßÊíNUÇì­Z…†ìj¦ËY1rü»Ç¾©¸¾)šžeB£[›3ãUóyïP–Ö÷=áO ,Ú¼L@–k¼ëaúí*Eêïúƒ òUñ‘fêuC‰§´@ÑÔ¥˜î-›¥ÏN˜€ùƒ5».æôÃìJ‹LLP=ÚZôOý„Ê#óŽ£é.æ3² =>{·éÏßw4„µ]fÖók±ñw±Å;h\|U5Z:SŽ”†"Ýë‘Æw5Kñéì+Ìyžàï—¦aÛ#àÑn¦ïÄ‚›ùólûÃAé+a5o/ÉfÃüÌŸ™$Ó|ºëeUG©ÏžšöÒ;Jù5ØÏm›¢»7‚4Ç0“t•ÿ<¬-ï…¬;uÝóa#G“7(:á9øкÌËF&gæ8~Ü…FÙhºdIñ‘/Ê\ÖŸ…±C‡.Ì;yè¢ý3k#ßYÖ2³ä‡ í^1wîIûÄkbQΧ0Yú7¾Í[ƒŸ¢@àè æ é-¡ - §skuHiÀÅÙéÆÔÿtÐ@¢Ã–i/³˜ý†ÚßÃQ~ßc¢‹Äl%µ½ ¸ûyzï5ˆî(a5ÕccNõšM=™M.;¯oß¼Úd¡UÂdwÐ7¿7Ý=mZ"V+ѳúW¢otáRö @_\>¦wû¿ß ‹ã­‰ãÔò.Æ2³å·°á“RÔ×ë K<6o‚¾Ë>½–8|míõÍ“> 6À"<€@ :+FuŠˆžÏY¶v4[€Ac«¯‚.œ&&]6tØ€c†.:0ñhäûd¶åä'³"Ž­›Î¢%M:B÷$ÉŽø¦tLUKƒIÛ§|HL~æèÛ*½'.Í€€¯O¥â ©(08êZMÒ_òx”öh]¯Ì¦ëý–š )Y…0p@`íe]Žq©ê 0/ߨªjÿšÒ„o1áw,öþIŸÈ3ü,:P“×J÷¦½›¨8K¾Ë’ëÞŠ‡ÒzÌS†ë?èÞäÛ•ß2³$ÓSÙVÚCzÝ›4γtZ¶ÜiZvÄq“S\¾ñ ôL8@S"Ðf XŽ1}ÉÚ5Xpb-(Ñ” ‹°m WtM,s+WBß¼Ö¥(gר¯‹igPGçÈ&éÁó÷WZ•­Þ¸¹JŸ@ßðcLøß÷yø¾K¶ºè]ÌjXŒ>`Œž®Xö÷–©’z.îŒkü&Ó=·âÞ$ š6KÀå~|‹C4 Ú"@@`ã¨~±eú‘Ëa꽦Ôí9¤kÖ[ÙÑœ]‰a§2éíÁCÆ/‘ì0ÅM›mºD?iœú¾¿W2'OÕ´nò8uö´,é¢â=;.Ãó¹ä'¢]ç‡÷fL#²ç¤[;éâ]Èâé7w户\Šë~-Îæ@ Íps€+â´vÜÒ3òpk„¦«×«…Ð8kLe¬Ç̾b”é3l‚à].v½^0™5y¿")Ãs%& ÷ž£@0Úùu•id‚6xÂØ=Žé³Mën×ëo¼Ñó·;Q=¨­8±*-ùúÐmŽÝxç> C8@s!Ðæa5Ð"@¸!`Œ`Ùéâå—&½uð`Yžª©aùUþä •ÈñW̧¿/Rf]1g÷¢¡ ¼ç#ßZ@Á¼Ý'§eÉÓ³²†X¸—§zV 4sælë‰ü^Õsï„Ñ_"ÎO̶uãÏ 1gƒ¤¿Ùçù{m´btàåÇ)Ÿñçâ,h)„ÜRÈ‹x­ò•©°ë»zùò WbÎ.­¿]“[…ÑÅÈÒƒ?Ù÷wMŽr&bý¢ƒÒŠM=òÚI·9·“ì6ù†¤)·âçƒ5½?vì<ÓŠæhšëf<Ïä~&¥hÓøaøS眳D¡6Am{"œ@ …p g€ˆ^ êèv»Œ5˜‡cÝWrKiÚÐw ±”Èw3Bý+¢õÇZ̧`uª'ŽF¾ÓçÄ'LË2YVr؇ûuŒ†^“r–æqýœ™eºŽÒ×äÈü;-Û|mZªú ¨Šb‘Ï>˜Z¶¯ª_ ¶fXe³Æv¸‹–bÄ|, i«.ÙÞ‘ô²?±BúØKæÑ¼Þûg¼j ò%’Nz6+ªsMaVC\ B°7A§¤g|3.}êÒÆf@ŠýéLS¿C¿S{ì^4­ï/̦Ø ¯1ïÑfy/˜ï„‚ ÁLO[‹F//•tþ²ÉY9eK÷a»¯o±€öQ®b^éÊ’ô’Éd:{Ȱ»º YxàÞ!‹öýä#ß:@|ñ‹¾V˜ß(÷7Õä]VAÀpÞZXh¶Z¯„³§Ï6§{iãJÀ¼½jkÑ'Ò5¹‰w¨ L&Û0Èõ˜Ã"c@•pÖ‹€Ð€’w’â ] –I–qY-°·ljúÔÛ÷*™§AÌ;Ž"j“=š,qm$`"ÝœœÍÿbº óò†+ ubô2V`®’~ü¬Kcó¼ÁÃRñ-ñ¹½ŠÏŠKÒD’뿌ã½ßàÁÆÆ"P¥éÊ[x㉊·Ê™°! ¦wÇJT/(n÷ehà.ÖuuÖuñïÏ*Ì×}]Õušów½½ &p6]˜˜9"âÜZhqÍ*Ôәܫõ8º˜Å~ÁÍïPAŽjþX+Ç 2T–H\Õå)C,9£:_´ìÒä×–/Ûp@×”¯ åÞQuô2Hw/´Ú0êÌ! tÃÆ÷šn=V§"9’kcîïþ÷ºí.—±ñ6»¦ˆh@ ÕêŒlóéü9?c·m2AÓ7¶ [œœ¢Ý€=~oóèÊç˜~Ôû}Î\$ëò—Ü¿8  6©c¹ßÑò˜¨ÊR¢¤±û0ÆäTÈØÏÚÅY{òÑG‹ïÏÌLv”é“Q‡Œ…,b¯'s帴 l¦Âì³Oÿ4%-ãf˜¥/èbN¿q¯šù_¬Ås-4ŠŽIñI‰‡îõäz2oÄ{´ð|L‰ˆFø›°AfÖãé x¢}p•2ýÔRçâù©o3&:þ!YØ[ºEÚËÊôÙ–ß+„̧ïéÒÔìÌôyuÅïÊ+ÆŠ>ÚˆšöhFþÀÂAoâÙL.GŠ=+JWó§Jº~>âïy  -`66µNÒ3±t_ýâ©KÞï»ÏE20Oþ û Þd`·Ú$Éé3§Nþ‰Ë)ÎGG€HWÏÝ{¾„TlïžË4¦'ÔøÖ_Æ›eÓ¼“?Ûû›Ï¬\KŸna”ßœœ¢.ÄÜÛ^.ŹÝŸñèí‹G:s¿øÅ»¤» Ù¾5™oÃï_Ë_3NÞ>`¾`¾qobŠòú‚”<žþ˜:Dó_õGü„ mUˆµ`Ÿd*›Òœ Æü~ÅqOa±òe.m>ß_ðówNÈ“³¦¯¢ç¨P:€¬Nß«L{$~*³—p÷ßD&´«ˆä.g)ÂÌ„íQø5é*ûø®ô'úï㟧TûFÀ»Qù}¡K2õ‘‘™­¿¤šT‹$¹ÿÛ £ý¸G¦¸ :Þ \×?¤ïŒ10 Uþ|áV2¿}ï /D€øþù’‰û34R@¾³ ÿî£ÊЀxêÂ+%+Ë¢{ò ëë±–àBÈø.QЦýòXæ9\Vq®õcZ—ê4ótßd¹»ó0Šyòï”­Jä -óoI’ŸA¹:mè‚ý=†-8ðà)ósÅÌ%JKÑ^D¡üQU >À )£_Ïܦ’"à Mª y~êíähþQ¾1 ¹]ÅÑ@¬‰©ê¢*·Å¥@ ¬h“pyö·FË=_ž<™4_rBK‹G¥Eþï|qòdjŽÏM™’qÌvÃgMM3Ö–%ÜÁoOÔ'v±ôû?»}¬›ß§óìÌôø_ƒxòèî¿ÉCJ6é°Ýyç z +#•—á>/?óÓ\h¾ëLÚÞÐøyG;»òŒGí7Ð$™‡Ìš:ie þ딡ÿµÞª /}Oþ84„N6[äS^±§y8Œ½6.=c'Ó4ê7ôjÿµ†Üöé:\°XhcŽüËÐØkW ÝÀö#4ðæ ]¸ÿOŸŸFhº¾wkùѧݕn9üÑü­…? /°Â0—»ÔcÔ14 jZ¶ô“Çqð*¬H5ç·-?]ä݃WßëѦZ‚·a@[Õ€‘©Ò"?ò52š×W¨¬b´?Ó¾Þ¹®KéUÉ·¦w_Éxtâ<ÂíJϳSS= ¾ 8nOM²oMïÔë^=ã¯-,4.®‚LKj!ßÚ^küýäõšï¥U~äk„ò ¾¿!†–ÞøÃæM2/cEªKHÓéÐUm!Ǹ¹ùJÒ¨–O™eùTôçöº(ï,QA¾M„-Ù.¡Ã5hh òm(Û‡ºbhÀ% ¨Ò%í¿¿nùq+´á›dÙ”}l»+Â<ß'›H$¬@ ¤h³0€ÝÕrF’C£@‡•b­ö¬–fKר*ùH™2ã‰)7!¸~0ÓÂð1 ÷DÌ·ô5z$³é*]Q?‚¢°Ú76×_@ßìו:ÊEmñå5ßcI—zÃ<\Uóö=öšäE…ÝdkÃt±¿üãCã€B²z¨”÷lõÖV~¬Øt澎EŸîå(C Üœ´]–ô$ôéþ|ßò<4Ë­»Çæ—<ó–íR§Ë¹F›tÍGÀ4 ê·ÍK»°È蹓o.†å Al^³È%"„"m–€‘eÁÈ—ÿsWé+ö»+³õ}ºæyïÇЪ?e²¼G’õ"̓>^¨›ÜeÛ'oÉœpç2/Q?ŒŠõ+˜]—˜£M£Ë÷+æ^k=×­žéŽ!^P9ý\”Ä4§ßup~VÇ·&yш.Ë0èjN¥È ¬ÐŒ‰”¨; Í8ZrEÎÌá(×`Ê­H•H‰¯”kÒv@4¦ aÔrN¨4áfÇÞé¯ZÆ2My^‹´8¸\åk1Ïd¬˜ßg@›F -p“d¼Ý>ךëÙü$Výš‘Ž­Ù¼ÎXÐCr`‡?Æ£òѧ ðsú{/@¥»X+QoÅõ3Þ7õƒKY5-ždÐ=‹¿¨Ôw`ÀV÷FŦ¾ñÔ>F]o ;fe¤½Y—¿p~††˜´êò®§©ŠzÍòe¯ŒAZ;Õ˜^˜—e¦Ïc:– !Ò­*ë¤;<4žà,ÆŠª>×@9>s¨@$8ä{6w†I5ê ‘j…S×€\ëlðùóŸ ¤³ù‹€µ$ç›_Éï×y–¤=!þÎ)ÓOñ÷§{œû_Óo„eõ BÕ‡å×µÊЀxj Ú¸ ¾GšÏ¿;-£G]þÂñÙêɃ–_š<-çÒNÛEùùv/ÒY‰|ÑHÛŒž¦>Ýa ô²0ïÑP&ßpÌ'‘&@S P'!4E„áfGKÚî\%SAØÍè×ýÙ$YòÝs5®oB?g7#ÚíoDæ*¹Ï`!ú£"õn­©úúô¢Â}”ㄾêùè*›qgú´Kµù§h§Õô¬}Â!þ¼¦³ÄÌ?0Ƀ Ô©e>)YõªK:¦ÌÿÂ%s³ÙbÍT×-Lq| ¿Êšy—.)'aU×ìŒ4Í -½fOMrúîÅX2¥Ï‰}—š>m"cæÕ:sõB÷ï? d›Ð0±ûü†ÁÕ£»vs»Õë0õë7ÓO¨Á¶L £}~´ÝNY°ÿ÷rKI¤^$A à ˜#¤3͆9u ˆöoù½¢»7â|½,™Æ V¥y¾å.§ Â4›ÊÊ´ýŠGÙ†upÿmôé.æÉÿã¾Ì&ëÐçkº6W/ö–zÊîàÏj;cÉÌÕ˜ï‰éEú© õoUÛ™ÉS$³D#žWû¿÷ŠýßûÍÌŠi>’CSõ/H^Ä5²ÌýÕ&CCâáaÕtΞ8±HŽ”†¢—s=0xWÓ]›©?2L¨Ék¼·åú>q+F$ß¶|Dò÷·ço öâ# ßJNBÃJÊ–Lò¿híå¡ Ü?xa^Å"•üŠ €@ µ#€ú80‡~Ë%´”át®ì϶sE8¥ºT=üÔSÑ¥¥J²™Éî3ôÚ7vìXµ"„Š_ÿ~â‰ØÒ#žö/g¦±×ËÑ yžÌ®VKtñsö öiåùž½É/e¤íªIãªM††ÆS—sçÎ5}»fSOf“ËÎëÛ7¯6<ê £¹ž­ìë/¯Øx!¶Ú»2‚ÂVU6à\†ÆÚçh ¿§wéöåÐìOU?âZ O‡g¾ŠT5µ0õëzdé ¬ÂR¤zr5Q$¦âÃûV‘w#"Û:pÞz±qm5Ä @ø# ú€Ã?E ›õcºµ/s(Ô¯{ úu‡`‚ê±Jl%úu߉д½?pÞNZb®Ú¹ÞÛâ¿@@ öû, l*hŸcon¿ìÒ¤ÏHh»Öª´‹l{¡íÎÁç·O^°¿EvÔjªô‹pÀ~âí6ˆÀª‘b£ˆ[œk^=-BuÒ|ë0 Ê ɉwŸa½7MýÆ·§®ŸñS e@ PV^Þ³¦8¯ÇÖ‘·`‚Fl3ò­ô&TýŽëošM¶Oùl§wÐÛ"{%?âB ps$ÄY PËFuù?ISRÅ1¦¦QÌN³ÍeSœÏ1ÙôæÐ¹4åL8€@@ P/× &á©-!°fD÷·äº Vå]UxËÕLÌóèqQÿñÇž9í±‰m ‘V€@ 8Ž"”0@ gDç35¦¥¸˜ë*h»‘U“„U+po¶Élû€LÌ)é}–Tõ#®@}hµ<~Êô!ª¦Òæö=ÐïÖ#Mcë›há/<€NZŒUÇrA–»L&ó—3§NÊihÊhú³ÌsÂJјڿÚû¶îÑ¥÷e&gY:;U“SÜZ­Š€i¥¦½ž½÷Ȳ4AєδZSlT¤Ö..JŠ´XIJ𭮸&°ÓãÑ ”éÅeNå!#uJæ>EQŸ‹sƼøì³h„”¿Ý¸Rd¤íbsøTG™ç*Œ`ލôи–ËËŽ´uÀœ]±PFu|Ä€@ PZ ÓV}ûÕÜ×Q§vл«>l`ovBŸ®RTd„)PÄû­£ÑUæt±µ[÷²eë·wZ·uÏŽÇý·<’>îͧ2¾BÊ4/Ù%JÒÕëqq6ª8±=CÛÅÞºs°ëPöÉŸçÒvzp¼'ñ_ VAÀ ß±]êS“âÙÕüƒõíÞ)à%4ƒŒ£®@#ŒýcPo:¤-»ö³¾ú#yož¶à¶‰M|}ÆãÏ-¿8©;3Ë㙦ަ1=¡ª¨°¤` H–mmÇ>8éý¥UŸ‹k€@@ Є:K ß°3Î3Cû÷Ôoy†dµ…·) B¸„‰Æ{ô–KMo/øYßÿÇO~=ºß­Ì}¸Ÿ®i•º(°5d)- )3Ó¬Á ÷VÚ!*\°éBP&`éö‰ö Ió%ò½côÙBë í²ÒiŽRVøÃvÚ’%×¾]$SÿŽ`ŒdÞ‚-ÿ^f¶è7‡ÎÛ^B !6‰@ÈpJŠÝ&[,¯uîÏHóm“¹#]oœ{¶³ü/粂2Íé¨ô îaÒw.½ðrÌY‹çÍ›Wã–•^€@ ‰U–ÕöÒ½0v¡>_avnâRЊƒ?²úw–÷ù;ìÈšß«¥ÂËLCÎeoŒ—ö¹¤ßÞ{áÉ¥ŒÍ£2OJ1 ÎN ´¡HÀ¤íšÌÓ4ÚY ¸j±²²늇úé v`áæÜµµšœ¶î}YNjƲög]ÂäˆHÖýÃoõ#[÷Ü ¯à âå#£ý­ÓÕÂ7@S"Š,ßpߣÃ4MïDS„pÔ’"–÷õG,ÿ‹™RXÀo{ϲÌÚ:œ%]| ‹0¤Ò3”# Ó“:žwÅÕÿøöÓÁC‘°0EWBJ\͉@¨°¡ýZ"¬Ò"4Ï·9Áq…&®ý»Y´Ý‚% ˜æª¼‘l‹bνœ%]r-³vìRcPŽ0àYÒÛ'%Ÿ ´Z–‘/ׄñS8€@@ м„$‚ž´Â•Xd£y C¨ÅV²qË[ð.+\¶½¶•­Å–Ä$ƒt;œ7š™¢bêæ ÇÚ"ô#fswx´á wã M¸rÀ¸!œ@@ hB€i®¦‰ÉRg,/I¿…kkh;üÇwñ–nYW-õ¶^ýXòÈXÂé0ÉTÿâÛ.>ZÚŸoNB€´É‚‘0•1Ò‚…ÍŽ@ýk°æ*D³Ì¤X¬íÜfÌÏnÚ¨‘Þ›þÞϺwjÏlÄ'õw‹~^ÍN>®;ëšTmQ¨údŸºÇÅ~¿€ÀˆfwÞžj¡Ç>ƒ%º‘ÅZíY}nPy2I2©Ê´î3Fe_4ò‚p@Ë JL„kôcqü°'_&Õ9‹c«·ìf%eÖ¥cqÆÉlpÿõ* ªª±?ø†Ý<ò 6l@/¦¨*Û°cÖÇîVçûï‚W²íbC‚€ÕÒb–÷Õ\–¿ø¦ª$»d‰`‰g_Â’.½EvíYéYc.t*Wœ|iI5^愺1€Šw€%¦„FbB¯\ØðÊ»Øïë¶±»Æü‹ÅÇD±-»4ˆM&™=3,Þ,\¿-—}¶$ç¨Pi âËž‚¼ÿgï:à«(¶÷Ù½÷¦‡„–BiŠ$Á^ å)Oì]{o´÷Ï“æ³=»vP¤*(*bA%¡)M:¤RÒoÙýg“ 7Hro’{oÎü²Ù»³³³ç|³»ßœ™33´oñÇ´ÿÛÏ«Lœa‰Œ¢˜K¯ÅP¢‘dmæM+ݨØ1`L¾¼‰ìÅ2•¬A vø›Öˆ+-à xWÆ~j +c3›ƒgÌýZ6‹ ^Ô~G:==s>Ŷˆ¢»®h¤{ Vo—v1tÙ9§Ò¿§Ï£‘ŸN‘á!ôñ×+aIÓø×>§Þ'µ¥ë/;‹2äÐ'K~£Ý™©eT]10¡œ ææÓ´wÑ¡#”س]1 B‚mµ{zêš=š³ç}`ÌXÅãyÝCP«8Šv“áÕÌãw½Ê,`“|ÁQW@IDATËŸ7ÜG,`oƒ-ù ‚À ð%faÍâ ÷÷§voOßüö½óå1¶s`²MÝ´Ó à]© ˆ—ÚÛKv‡“06š6ìÈ ózi™DKß)*œº¶¡] Ú[‡Ca!ÄMÔ¯}ºŒ‚ƒltÃegR~‘â[E×ñ¿ïÿØ@—žÝ—B‚¬IwnÛÚXQ¨<—íÚJY_¾K‡W~[Å£9¤CWŠ»âjqÎ%°I™ë5°Õk>g¼— ‚@£ àkÌ 4‰b×ö±ôàõƒéý…?Ó¿Þú’.ìß‹®”H=:·¡¯YO¹ù…èÓM§^8Þž¶ŸþÞƒuiËì´“;ÆUxX¢#éy³pÊ€ÅË«q`-&è®»¸ÜÊæx¶ª9œÖ½#]tF/ã÷/ë¶ÒÆíiõBÀù[ÖQöÜw(w5ÏQ1Dô8b¯¼ØÁªƒo‚-·c#à‹|liìLOëÓw]I_Á+yéÊ¿(MɃú÷$›ÕJÛ÷î§Ípªêun1‰mÙ™AÜïÛ¹M+£©Ø$ÒcArMË:Æ·ª6I‡ø–åñsmXÒå^ø‘÷ç”õÅÛ”·ç½¨šõ;›â®¼"zö«x¢Þ¿k£Þ!”‚€×ð%n–oå’ã&â+%Ѷ½û@ºûhð™}ŒæäéûhGú~ºièÙ†£Õ«6a¸‘˜´kÂCy´ Qö¡#ÔÍ˕߷>BnêOñVËJDó3/¢¸·Sh§îõqëºæÙ$Ÿ»º‚%× ‚€÷ð%f­ÊšßRù!eŵŒ¢Xlûs(mß!˜ÔÓ(ÙãiyÊfŠŽ £ÖÍ›<ë«_a[èòsO­¶ô9¯«7[¾ìͤŒþÝ¥¿®§ƒúÃÙ*ŸØÒ5½ªÍăȜ߾§Ì/f÷õºž,£ÅCÐÇ{Ç·w?ÕX¿Ý› …|«侂€ `LF 04‡ròiÉ/}½ì€uÖ©ÝàÙÜׄ­Ü/¿O¥óN6Žy˜{1sŸn—¶<™SÕÐN]?¯ÝJc_/èvtÿµÁ!ë|úxɯôoÍ% +5âï0ú“Ù©*ó‹wª¬JÄcx[ N±WÜJìÝ,AA "¾fW”.€x˜oE%ö*3YuˆkIo¿µ‚öÿ7æŠ ÇÜ/ìž&2<”žºmÂk:4¤tf¬~=:où…%†·³4÷ëøøž«/ä]ͦ‹<ôëRÊúÄ›¾³Âujp(µ|†ÝH¶æÕ÷?W¸@A@h¢7rÁ×vɉVÖ÷ëž."¬´?Ø=®N¿añúùkÊœ3“J2÷TÈ‚W%йôcÖ*k³è çä@A *BÀU1‘˜Ê0ñ²Å â-NßUá,¯DÄKÆ ¹Ž,GÇWH$‚€ U®‰D”#À}¼¿~cX¼•›š-á‘éÆ\~=ño ‚€ µC@¸vx5™Ô9¿O³§Wq®*'Þ!7œpÞ&–(*‚@®h| ÏX•ùÙ›T¸cS5K›š¯G¯X¼€‘A@ꈀp ´Ëò7¦Rú¬×¨àïõTcçªX43Ç …ÅѬÂ99A@¨;BÀuÇ. ®,Úõ7ˆ÷U:²ö× úðjD­/¹hÜJ¼< A@ï" ì]<ý&·’ì4ÊüäM:ôË’ 2+Ö ŒãaÌÕl>:_t…Dr ‚€ à1BÀCè_8sRæç3éÀ²/Iw9 ™²Z^0”Ú\sÙZÆ—_‚€ õ‚€p½Àê{™º óißü){ñǤ•U0úôÔæú{)¤mç ñr ‚€ P×¶>‘³îtо%³) kòºòr+ÈÙ;‘ÚÜð…wëS!^A@ê!àúǸÑîóû”þÑKÄý½î—l{ÃýÔì´³Ý£å· ‚@" Ü€`7Ô­ ·o¤´^¤üMk*Ü28¶Å_w7µ8çÒ ñr ‚€ Ðð7<æõvGÇÁlc,¾ªpž£9þªQÔúÒ‘ÄëóJA@h|äkÜøeà±Zqeùe-šEº½¸ˆ@“'`“l4M#MÓiOæAÚ´3ƒ) Ü‚B*±cºÆ£|\û",ãÞà +E…‡Q‹¨pêÕ¥-uŒoI*¦¬ !nÛ@{ÞùñÞ=ð8Þö·?AÁñíÝ£kýÛÄ`wÆÚ°#åPN~!Ù펣ª×/è_k…äA@ü&KÀL:Lº.—F%%Z¾z3ý„íH¡ÐN˜³„Byä²{¥s-A´ÛI…Ö`ZüózjD’zрĞl-'c÷›±wsÆgoоşThnŠiKín}„¢ûpO^ëߌ“ì÷«6ÓòU(·°¤Twt·{Ow¬.ú×Z!¹@?B I°I¼‡Ã°ø>ÿöÚçí¡ÄC[¨CÞN ×G(ï‰ìLÛZœL VØiEê&ºæ’3©O×öd±¨ó}sWÿB{gN%û¬r1[°±<`Ü•·ÿö$0mK£O¾ú…”º'Ô³î,oMõ÷D7¹V@ I°iõ:.X~vú!u3}ýë_Ô¢ø Û³œâ 2ê½Ì˜Ø»Þll™ámè—öiÆ—?Ò°óúÒ…§÷&*8Bé˜Å*ç×¥divÚYÔaÔXbëד`b°ì 4ï‡Õ ª;Ë}"ý­VKµ­žè,× ‚€ à‹46‰Çáp¢_׎f×´ô·MÔ%g سŒ¬š£Áˇ ÿŠ¿gÓòÁ÷b ÉNßÍ$¥(}Äæ`"k³æFs³7¼›M –ý¾¾ù6¦î veý¹›ùÂþ½Èf«¾Iž¯‘ ‚@   Š"'Òƒ›\™|‹KJhÝ–ÝôMù^´ëëF!_S^&~–Éðû4;mW¢É‰¾YüQËC©÷K_7È—ïÇü¹u/Í[^J¾­;Ëä®ÿ¢ŸÖÑú­{ŒrbY%‚€ È4 ˜?æÜìÌ–oî‘|Z¸bÑôÊ–¯¯–%§[4ýÑùB굌:~œZô;»¼OØS9ƒâb}ºd¥Ïéκú‡´ /¾]E'µ¡ˆðPÃCœ=Å%‚€ ˆü×›]ÙÓ™û|‹ŠŠéç5[)DtöÞåjùV~˜Ø<'íG* ФÌ+§àî§r³üžƒïS6R®ÎFwc4¹OCÿ½?Pn‘–£ožË‹ËÍúï¾rNÆB à ¸Ôú-í÷-À¸ÞÔ-{ ߆p¸ªm¡²L쉽rÃnÃZw:F³qmó©œÞÄà'ç³Ìåú¯ßæUý+ã!Ç‚€ øMÀ¦ågÇp£ââÚ™¾Ÿ í.êŠá6¾X¶|Xè» +Ëí©hbÀºué˺s™x[_-g‘K&@À.8õ8Œæçí !ždƒÇùúj`ÙXÆÍ»² ¹].—GͰ¥ì2f÷òuݹL¼­¿¯–³È%‚@@07½2ñlOìýœ›_D¡Îâz›dÓeçr³ü¬G]ƒ‰ÁÁÜ|ÌîåÛº³ŽÞÖ¿®¸Éu‚€ Ô7NÀ:9˸¤ÄNØÂùõ©Çù³ŒG ‹ fùyŽêº¾–óÈ-@åÃtg=½©]q“ëA@¨o–€¹éÕl~eg&öªu84¯Íí\ŸÃóO;ÐWÍr›MЬKmƒ;v,*á­y­k+GmÓ{KÿÚÞWÒ ‚€ Ð,3ˆfó+“6f·hHt=¸—YyðVt]Üñ=¾Ô[ú{,ˆd ‚@=!°lZl8²'±®½ YO¨× [ØïeÐ\o¨¸Fq .7’˜×Æs,èšÞ§>ÒyCÿúKòAÀ[,3@å2ck˜ý&@T&!S‡ºÊm^ïGš—ªê%ý늛\'‚@}#ÐÌà•?‘/Ëí&;ëáIð«ŠG™¢ÞÔßìäZA@ê €'àúÎ=_ÅDjh¸{”üA@Ž‹@À.ÆPVßIO¼HE»(㯠FöN¢v·=F¡í»©ZwÛ@j~æ…ÔúÒkhÓã×±ù}\ðks².úÔåšãÉdê¯Zl3úU »^™HjHˆÏè_I<9AÀ§Xf”™€Žnžáqr_Š<õtÚýÖ$#£ðî§P×ñ¯Ðþeóh÷›OSIv¹ òèà )îŸwb ÁËèЊ¯<ºéQÙëNäf ‚‹ÝõWCÂ(ç·£+I…w;…ÚÞò0lû‹ìû3}JOõ–ëA@¨/š€½ ZìˆÛéàòEä· ðv¼`nIÁ­ã bŽiGA±íŒr)FönÞñ죥žÎN{y(ÏŠ3ôJÿèåê3òÓØÊúó°£Õ#«hãËú;U«>kgá.lYü 6·U„͵zxû6¤Ù{j õBKu/]×z)¤ô·Æùj‚Þ¥MÞ^ÂÖÞK¨@Ê1ip¾[­’¾}«áý{ß/³÷Us±D ‚€ @BÀ5|Ø©çÐÏK¨õ +(sî;åWUfÔü¬‹¨(mšf—”§ „ÇÒ¿²nþª‚½ì^ÍÛwî:¥ kÓJUô^šKï¾áÔ´°)}0ÁI{º²ßí0óK;òp¤ÃÔÄ}ËÛpîWEW~Ó×/}dn”>åj“(A  " \‹BßóÖä¦>ôÓWÄ[ †¦¨Ò xvÑŠ²Í(֔щ6[VfŸ_âÎø¤Uáþˆ“r¶rgÿ© å0#Û?4]wÅaWXÒ7kšN©ÃbsV Y Kùñ¯¶(ý¾e¸]"?A ‰ ÐŒ¾sæ†éš1zÈn©Îpñ­Òfƒ,ª›ìJ4õ·Y-”ﺳ¢ÞÔ¿ÎÀ礩œ^3zB7eÓ5“ iYWüS”i Š®£ßXA¿±^ê_–_Y_óe°”/)“NÝ)CbבB¿ÀÂþÁb [.^e`ÉNp–€™x*‡`Z®íèСÊç}å¸26 ª8éFuúœH^÷kÂC‚hŸèÎ:yKÿáãÍóÊœ9𙦠eÛGœ÷†‘½ƒŠ‹$èš~«ÏFoq|Î-XAĉàbÞpê…®”¡±©ºBËP~Ë¢:FüÚíÕm1š›ÒòS¨iõ‡Ú,”e ¡k0;}󛯲AÆpÌÿìÍÐ,¬4__Öõ­/ý½‰eMóê=gzé·²í¾nýØ.NU=G×´RRVôÞ°ŠKgvá:Y@ȧc:öãrvæÁBþY«ºò]Bÿ1k”ädt3KGÀ»_yDƒ­@skn£ÚÙ™ºÞìƒÒ’!&„ ¸æáår{*(ëß1®9­Ü¸Ç§ug=¹l¼­¿§øyóúSgï@~¼VrÊÈ.QjaÑ™º¢]€¸ ˬá£Íº ëùb4Y_̬›²êÍCpìúž-äÕúÕ)ó3ö"Z‚ ø!MÀ&ñ‚‚Ñÿ«R³P©:mkq²Ï0ËŒÏoËf¡úh塮ϖ‰AL4¦vľ¬;ëèmýëŠ[C]—4gÏb²´l£5WtŠÖœ…A¶"î"ÔFN®(‹ÞóUˆ¿ªXsòЧµxº¨ea¿ù©(oðµA@ð–€Mâá®` ú~U,dÀûÖáVÚ«u Ìð6_À£N|'°L{#;P·–áåò²ü¦.µ•Ô¼ÎÄ GûV´Æ¡ù¤î¬›·õ¯-^¾¾ÌëKÈ­Ѷ£ÄÅ–ñE˜0{Н$çi˜¤ó4LÜõ/xXg`‚E¨k.Œ ±}×~Nq– >ŠÀѾ'бØêå„x³Â ØjµR›HXÁŠF?·@NÕw–®cY~LÁ°Ð»Ä43ä5eg=êÜ1èÕ±5…bi¿_Úô)ÝY·úÒ¿®¸ùÊu}禧%-Îú ÿâì›’ïkc±ª½Uy•ªoPÁâ>æò˸ :‘Gc¸ÓÂì"ÇUCcç§ ‹½sÃÈN•¿Ê¯‘‚€ ÐxÔýËÞx2×èΦõWJ¾V ÂО /œ›ÚD(t8¤%-ïpQòjˆD,Ë!ÈÔµu(…„°œAF…å7u©­æu&¡!ÁtZçVн…OéÎzÕ‡þµÅËÒ'ÌÏÚ˜´0û•¤Eû. mÕÒ¢Z®RTúeÍã•˃1&YׇÃ{fQaa¹~[5,öáµCÛñBAÀð5æþ+lÞéÇRU…¬ 0&Þàà ©Só0+µvÒŽè®´¬Óej ²åÇ2°,í"UŠ/“³´ÂÀò³u •1hß:ŠºÆ„û„î¬S}ë_ neÏ?gÆVMÿˆ‚—u~ÂÂÌ/’î»51éîX²XÏ…×À°mt× ô…ÒÏ MÑ©Û÷‰kEʰ¸{Ö][ºÎ¦{bù- †€/õóÑ.ÝUXìpðqÝ™›Í¯LÀ! Þ,jÏ Û…PL˜+×Ù "Ê ‰¦söþØà}ÂÜçÉMálÇ…éÔ¾y¨!ËÉò²Ül½z£ Úƒîm¢ÉápÀ·+5–î\Ð ¡?߇C±Ý¡;ÎêúDËŸ»Ò”þù¿lhÒ/ž·§R®h’â°ƒ·ô0xPŸ%þìÑ•Lç‘®gwÐ+è3þôg¡!¶¹½ç¤òOíEjAÀ?ð%.GPs¸öåäð‡Ñ#æ&X&0›Íf[xx•—PII 9œjí*ÀW©³ò7§…ÝþIíóöP×C[¨CÞÎz'Ìã\y¨ {û²Ã• kÞµsÂò £ððPŠ€Œ,'WXn³ ºœZþ8]ãšÁ#<‡vjÑ`º³è ­¿ WΑLÛ]rØ<ô}Ò¼½Û¡#¯ùÒú!š;©äJxV_«+ú pé0'c¯_¤ktQQ¡ý Lò­ª¨ŸF6 ŸßmÖ6^¨B‚ Ô#¾HÀü¡ÌÈ+*Q A–aè·¬k(%•‚@d!È'"<äk';¬?§‹·Q¨j§Ã•2#Û¤È&B¨£˜ÂœyäâY=v‹ ­‘Td Aîhñ¶°Ú©u¨  1d‹ŒŒ ðˆpã7ËËr³ç6ëQ×p< Úƒpk¥å”P&Ö¨ç A}èβ7–þ|o~Žøy**,à~R®Ø™Ÿˆðìt[‚SqÝ6n´ve…N]¼‡+ïò¶zd\k­®_ƒ²>Ïœ€Ø`)_ŽÖ§ËssòJÐg<ÛÞÅÄßÊÄ••cAÀ;øÆì´=?7kÙòÁ?·¥Ó}ºx¤)7ß²÷3÷ÿ²eÉM¯.ŒÙ(%_³™º˜¬hká*¡"'Ö§Ó,äÄTùAÍñMª;ù™‚—ò§nL;ÇÕL>jSŒ&f¶t¹bIÍšac†œ,/ËíIó³yÿaÀ$STLyÅÊG}îZ© ¨å—e€´G¡±õççNIÊÞ­[ÖBžÏÂ$`5ó¯^ìT±n±®÷9Q¦ek¿‰toòŒè†‰ßן3Ík1ä‰k½Wëäº:%åͽè/~ìü^ß…Ù;Í4²Ïð%.ÿ(._4÷Ï®§žvpÕ†-@À1`¹BÓÀ$L¾&¡”š¨±pkqI1ÈÙIAN'EbÕ&h|”¦5ÆNNÆð$ô·ƒ€‹Ù9«Ìt¹4¯YÁæýØ¢åfåRë·Ô+›››¹ :,,ÔØ¸é™Ï³¼Þ…Acéow¸Ϧ9G~ùjÁ7À“'®à‰ØÝ Æ¡Ý XQ`•ò+D4eºšŒ%OnIñƒ%Y›2CyÃâ ;qjŸ?^à±Æ8ÿ(Ö=~JÍØ;­÷w ÏøRdm¼„°£ñûn¹î†U¼ÔüvT³ÈÄqëx¨Ê9A *¾DÀ,[$üq´:´/oËÚÔg•„þÓ>\ø³~çˆ 3Ýz®¦»ô§Æ8­Í3U¶î±1-fYñ- øÛð.t3óÁûq*Èø•œÜ#Sà¸õ¾5ÈúÚi_¦ÿmž—½  /}²¯¾èÕŸå{̶€ùžló¸\&B&]›a—z<ׇåë.YCcÐú³åË䛲i§²}ãú/×üüã`ÀäÄ÷]òóŰg¬+‘È#›/„´™äM›i¤i®©ª%èü±·çïgÁЖs'<ï¹9ú˜rN™©NVTuñ¸;œ+«KTÖ_< 禥‰?OW]w¦\…7$ÜH¯S$~ßï´;î[54f‰ªZ^I˜Ÿ±åì›Vw#‰š°¢+¼¢A¢—0ã—•?Œ<ö‡?–_}üÁ'—^{ËysæÁÃúµ—œ©z£O˜‰Á˜%ŠÉMÑñÂñª”|KµLOiO ²MÊl2*LĥǥÎY,SC&ú†ÀÀÔ©ô~õ¯?÷ùr³3[¾Û7¬Ÿÿý¼9K€i6¶€yÏÏ?_f4~Ö9pÅÓ'ú@A~l+šë=«jñä%ÛX«g¦7Ò(g¸ñhi]¹ª®h®~5ͫȩqe¢Zv¿*qqæO8þióð“ïÏÓ_Oºr?®ïÍi d» No—¡yúïUCã^ mù>ÏÚ垇ü2FÆxŠÃnÌ{÷´iÍß;ÖÓ‰L6,`Æ/mð’O?ú¬ÿÀ‹3µÓϾû…–D÷éÚNïß»‹rJ×¶u'ÌÄÀäÇÄÀÍÂl²Ó•éùˤk¯¹¯ P&ñÞÜøž¦S–W—¼=½†ï]ßð=8˜zòÞÛúó8_jÄÞÎìp¥9y)+~ødÝÊŸxØ?CÜJÃ{ÞÜ-`~Þêøyw8cA>»ë”—/‚_¼üoGUïš'G9~3³×•Ü€þÒq·ä4ãÜ÷S§«O6OÁÓ9½2°¦À’Í|òŽ’-îט¿{,ØÂ¸NçmõÐØ Q£yì‹>c2ºcÆ­W ÷O¿§¨¶×¤3Ù M-`xõ.ujÎ)Z¡6@þÏ `r³ ÷—`c‹…§Ð³¬úáÛŸ7þñûæ³/2 µë‹ñ¡ÂÇ\ Ö££Â•Öz!T&ÛÊÇu¹…IB浕Íx_ÙWÖ¹òqm嬬oåãÚæg¦çéJyÆ4žd2*.—3o׿+V.ýꇢ¢“ty½]Þxf'~žø¹âçË£æg~Þ™h¬xþ‘W£U ™¥¸ìëÑÏ[At¤Œ‚·2¬ßªêN›¡ÞÜ®°…Æ\è,ÚÿPe%Ðoü†îte!~Pås•e‡¸ïÖ ‹íì å>Üïvà]–®îó ¹ì€ˆ£Ò7 kת?ºòýäX¼BZ£&LÚ‹ô_3&O¸ÌK p š‰7[¶fØøeŽÂÆýná=NKìÞ¡ÛÉ}BÃ"Z…†DY­¶`˜¬^ÑùKðœ.gIIQQnQ~þ¡ÝoÞü÷ŸkvAln^f+—É–I˜É7°isšªŒ„Èš†Ñ&6’>3'Ol_Ók:ÝÞ±%¹œÎÏÆŽÖº¢ÒSÁÀ2Ãr-¬ÓÐï{ÝØ;_<ó¶å_h:ÎFÚ·XNvÜr)® ¸êtĵ4¯ÿï{ÑÑÅ®‚Îcïtp¿ú1ú›bÇ”›a]ßœzVI¨(ßYTõi cZQåœDM-`']y/ÙscÆO>ú” Þx¡øãhZÁ&©º7O—l^›ºÛf¤3,dì™´yã`^Sz$ÿ“PøYáût™XÙÂå~^³É™‰—‰ØkÖ/?çhî½ÙãÈ×gƒæÔ[@8;,Ý%SgZf¶ÔN›?fLª¿/×5m²¢¨°RµÛq~ª®Gàðž© ;n¡£d¦®híž}7¤;â·ð¹"ç‘[AÊðó¸Ü÷£lÆûMÞV ¹XÑ”@Æ——7Oëú…ð·¸pÕ˜EyºÌ‚Fr ‚@ÓAÀ+D•œü^H†3m ÐÅÖú¬Éc ½!ËÆ„ÊŒ-¬lc‹˜=/C±±…Ìç¹"Ác9½WtB>|&a“|¹ÂfZ¿†Ž™„ùYä‰Ù´|MòFTíÂèäéaºcÿJ”Ñm¬íNNN¾­mŸSgX/L£PI>WÕ,w¡±~ºÅ4ØìÛ}áÝÐö%ÎâwПýŸ1ÚtvÜrQΆ6A»f8ö¼‰üïÆrÝKSg(aDý5O¶o¨­Òk†¶ëæÒíãþ¸¶bå_QVÂ7àéÄ™Kj›¯¤ü¯‘Õè “ƒ€¿B†s§OÙdå!0& óË„‰˜I×$_>æøêØkº! ¾…€I ¼w'`vÞc¢eRdæ=s¼Ùïk^‹¨Úîg3aÊgÈ`øò“'~S»7õë³[G…é.GTå©)a¿ët 0~ß{`a‡0<é^ã~Ñ ýÐÔw¬g‘Óõ¸1úÙ•5ùÏ;­"5×á‹Ð2p9ÞýãF»>ªœÆ<6ú‰u‹ã[€)¿¿Gƒ¢¬B-zRâ¢ì…G#å— ˜T¬…z #ŒFOœü˜®éÿÅGŠ`)ÜêK˜?–Ü´È{óCk63²åëN¾l3éš~J`ø™07~Fxc’e²ågÄÜóo>ÇÏO[¾x®ß‡9RQ•‡gLò/òeÅï½z¿Ù,¿¯2 _´H—ö³ã–E·ÝÌ*š%ESœ×éÚ(¸°Ï,…³b.סd”†“¾šøMųÊuýç?ÚL*q:ŸÂ+{påÊ4JTï‚Z€¦éµ˜¸eÆé¥Ê|E!äHð¼n%‚„Âú¢ÏãEü /â^êf¨XVnbf¢åÍ´zÍcó_¢Wqqo,^ÿØŒI^ªSF>|Ñ33,—é `UQÀqë5X¹g²¸ï½×)$Ó¾;+4(ªS¡=wcPhL·Ço6úw«hóì{áqNGaj×è^¯¾zW€Êƒ®'#ëd.‹*¡l–­'Pˆ£AÀܺu4(ÜäM“’$WýÑÄòKð/¼NÀ¬>7Gc7µê¨½.Á6K S{aœ0gÏ2›d˄̛gêcîqJB€"`0ïÍ?ðæfÆÕI}ç[:ÔH¿ͤ—bøòd4Êßšk«ü´éÖÁ×>v´ëKóÚ©Ó•ÕÜ @#ÆÖî6ã+ïyL1^¼VcÇh™ç¦¼ÔGq9>ÄŠ™²hkˆ5dÌ£·í5Ï»ïÿ[â"\«Ü ‹¸t†­²ø†lVT}JBðŸ(sæpåJ‚ à÷ÔQ±cVº#ý>¼ÌâóØ0´l)Ù˜Ô=7å¦0¯4{—ç…·Þt*¿‰üð 0ô­¼àÑ|ꙘdÐrÓYÇ» ¥á‘z¹­­íkþàpå*e2u†:–éÍŠj=gì(Gj¥ÓÆ!÷O›©þMºmظ1öÍÉC– ¹kUÝ2zìç7˜£úJLusû¸Ñú°êò0ãR†µiEºëQ¼Ë÷âÛÁÃË1ZËí¿(ë«òHù!ø)Þùj@ù»'NKÄ— YGpÜ´ÂKu‚Ëå´ Ð`€€óʦWÝ͓̼9ilµ„Ó`ùÀžyÛz¾æÒÆÀAë†c‰ÃsPëº+äz¾™S\>Íï:®{˜ãfÏî´-gcÒÄðñóo‡tthö!Àü\ fÀµ%s¼6Œl×¢¨Øù¦§{ix€ò€ ýRÕ¢>R¶rSy¼üü !`DdÚ#+ù4ez?Ïž=Ò²íðçÙ5ä´'G¥qŽÿyÛv:&Où|ü½Ã‹³Û…ç¤ÏG+Æwh{":ºUÇ2'±*7OÙ%Š ó@3ö#h•0g×âtNt,¿f³&÷ü2ý`• %Bðq¸ÿT‚ uF`ê‘-Ñ ?0$:þs3“m¹_ž «õ€I¾‰7ÎD_òüû‘«ÓŠ` ³¯H.šøç‹|9mÒœ¹I‹÷M"ÅÚ ®ÚoˆÍ>`+†=ÝW`wnÅ—býb!Að„€ý¦¨DPAÀ7qÀ{ZWï`R5%Ä¢ =aÝþf{E¿}¸Ÿ¹Ç¡ïx´ÕbÁЦ‡¤…’eݤ+§ÁÚ^f^¾âæÈç%=cïŸ)ÃãxNz ‚€_ ìÅ$B ¾‹À#w9ôÔ×â ê˜úS1&Ì1¢á€õOkP•<Aà¸`E¦ÛM„I;n:nÂZž\7$¶XöEô _\áR…¶Xõá„…Y_Wˆ—A &èF_n-4U¬šºNQ¬Ïx[îNZœ=˜û‡‘÷ÑqÅèviÚWð–žµzd\koßWòê‚€XÀuAM®ŸG€‡%é™i÷)ºþ¯ ã‡å ªê%.Ø÷?ŸWB h„€ºxE9A@(ÚÒùHøæJh,±[îJ˜›¹»R¼  ‚€pƒÀ,7ÆFD<˜4×tôw2e÷t>&Ÿ˜x÷k²Ú’‰Šì !à†BZî#ŽÀº›à}}˜¦`æ®ûa—ûÀ`‚ßT‹r‡Ì-ÝèEÔ¤nRÅ-Ê ‚#òØ3t½Užú˜ˆ€„y ãI‰¡çO“%MTd_Ÿ×'º’· ø,ÆÜÑ™{Çb.êñ˜4$ÈMÐ_ƒI¹éÔÅÙ;Üâä§ àu„€½©d(þ„@ê•ñ=5»‹gä:Û”›û†RH\”õž'{AÀÛ{QÉOü=9YMMyóI4Iÿ3w¹¯ª47"È6Z–;ô»"õ …€ý¢˜DHA@hà)@ºkš¤{˜÷ÃG2kFÜ–¸8s©'{AÀ{EÉC½#Û…fٟÜÒ÷šJáC ÃXy-6Ìödû9G—]4ÏË^¨ BÀuAM®€G`õ°¸Ë0}å»P4ÎT‹;lBûôÕ<å¥'{A ®”ƒ«kr ˆ/Ü ¨ÖS0Ç|Ò-ËeS•©I ²&À%Áß3~òùÑ«»7*T͘4á%oêàU.%_ý¿˜-fÙZß:#yL¡7…•¼A@ðeÖ‰í‚Îß…îC•ð=œÖêÆÞs6H×›/Þ1d<=ŒûßG™ŽTTåao’°×˜›u]ù Î>yü5Rã;FiJ´ 4[oèÚ,7'ïc|°‡˜Š‚„ÿ´Ø”áýæeí2ãdï? )Z3aÊghÆƌ˽Õí•qÀìp(gr³3[¾B¾þó`‰¤‚€ à]ºÍÚv$±ÿÝÃI¡çÌœAƧ¸úXmé\3Nöþƒ€Áià6H¼ÛÌ2ÎóX¯0{;Wèó•fgËD2?G@INÖú/Ú÷„Bê­°~KXpk,uøÝêañ7ù¹zMR|æ6æýÌuÆ/ àæ¡F¨!,‡+/”ˆd!ƒ@Òâ¬TEHŠ’ÍJ¡)3HÓ\¤ ‰{2`”lBŠ0Ç1×Ãk½ ·ÇÌ“l”ŽóUfyAÉB€B aѾ•!K|¸×³bèG„K´öLÊØWôäd¿Á–(ƒrœÅœgpŸ‡òz\ø<ÃO²Áã|=”E.A 8e~ÆÞ¨¨ÈóðñþÞTMÒ÷§¦¼ñÙÖû»–Ogiž“½ï"À\ÇœW6»£G‚zLÀ¸{GžáJ&Ùð¨äbA@pØ9+4´ÕehŽþÔTý‰Wåì8òÍš+:E›q²÷m ®çAÊŽžJê1óÜÎ<½¤§‚Èõ‚€ : lOZ˜u½JÊÝt=ßé,úi݈¶íÜâä§#ÀœÇÜ穈0ÆýFb“æž–„\/M4C뉋³Áwó1l¥3déz»ÝùëêĹ¯5Ü$ððG%™ó°y¼¨Çìà‰ÌþÀÃ/¾ŠÉ_Nzì¹çÂý_›¦¡Á¨‰SnG™ýámmïKþO›{’Ÿóv¾õ_Òâ}/ªÜBÆäYºÞ^sê?cJK¬9,¡) P6qxSPUt Æükò?tM2ÿ`ÁY¬OnN‰ õðCXd³M|-ùÉ€êINÖÕädÓÑz'Ü5qê9pù“Dü‚)õØ)¨ÔsË~ÔøÉo¢vúŒ)Ý¢=ÿ©S,2éçyFD%ÿ7ºÀYø4|a®+qØ•ˆðd©Š:ö­Iãß÷Æ="^¬aõÐØl—B_ÂŽ„cVs0ñ2LØqyÒÂŒŸB¹Gã! pãa/w®%£'N¹ä;ŒáTºSûîÕº¢|‹¬¢ZÑYµÌÒ§“™0éŽtç”õ"¤N猙8ùžzÉ»2-p¼«èúÅ¼à ¬`ú˜1޲{mâ?ÿYuøˆã´÷U}ËàÍüûÍK_›zeüdw}‡r‡%îÒ•E«‡ÅHX˜õµ7ï%yùBÀ¾S"ÉqÐ\4BZ[k÷±nä{Ì+xݵocÚƒNh¢þÍ­³ã­ãžs¿~ôøÉ·À›ñrÅz¯î,~E!ÌZDJ>‚ËkëˆòÂtGñsh’€tÍ`q­´Ú,½‘?jâÔ M›ˆ{ž L¸oŠ•‚žzcò“›*_ÖŠ;u]» ¸w›‘v:®+í묔xÔ„Ióñh ï‡4û‘~ak»qÇ[ý¥ä‡s*eCÏ>ùd°âÉ.B+Ÿó‡ãÄ/37¥ow¾®9¾çþ`4E‡h:ÍKsMÒÂ}óüA‘±vHtíð’Ô€@ròì |ÀûâÖ˜ýêj?âîbqzݱ%it“ ª*7c-ÏE˜ðÿÒS>wO‹ó­÷Pr-ÃoBžëõ[Áõºsßg ßï@Ü%Š¢>ŽýÛ Ø³œNççÜ7ë–Oo—îzASôñˆ{,te-šË_ÆSÌtráýTM¥£ëŖć¶5ŸãCœr 󇋛Ô×¢™}2o ½Õ|~ôôé6è÷ ~Þ@Ð „õ(Ìå0§¦ý0ú_Srš•ÔB,­ö,­!Ü´¢ôwŸ|ƒ¢»¾‡þ‡¡ÄƒØ&@Çh'ÙSÆLœjôÇ›y RóZ+f2ª…nBÚ™èĆŒúífsõV¯I/îèüÒ~ƒs£Ò鵞ØÇÀ…¨'®GYúgH\¶ÍjUÎv;Xžºë«ÏY=$öZÿÔH¤>b9çdÒß§‚(‚UÅòwMÊpn½¤ÔÖY7ëìË»&L]¢\È}„Ó'+o¢á†Áºž ësjYþŸÃ* ©Ý BxËkÞkƃ0ò4Ò'e:Ÿ; q[Ýä99ÜÑÑÍ2ûbÔ„ÉNT¾+ù¹—ßJ~|Ÿ[Úãþ|}ܸƒH0ÖÜÓ:>Äp*ší~ž¶>ͧYmj¿7’ǯ-;÷î·‹4í?8>¡­ÝiÅ’jCÏPQxitòóßÌH~ì€û}Ìßìi~$§ä9ÈòÙÌÉÊÉ:9ù‡2œ?ÿ Ëÿ%Å™ìÐ5ú™g¢ô|ÇX\û!ÒÞiæ§©9è·Eçh¸ÿ•W‚Köy8/š9yâegÞGßw ò{·r9½²ú_ÊÞ}w¡õ!2HÑË˶ú”¾ËK®Úî|‡nÿ’vÇfEånææ¹¥}[z‘®6àû"Aðq\J–Î6™5“T¿ âïÜÈ׸ì­Éã`-*Û1okBQ‚Œ¦]345óÇv™RÁbÖˆŒx…JÐDX!|åF¾Æ ›Uy‡PÍQrA…”À:¿Ž­K7ò5rD%–$%2±è ï¾bµÝ Þ E €ûÄ.Ï˳Ÿ =âA”¯¹ŸHNèί¥Óïù÷3ùœ’¯ƒtÑh’ŸážÖÀF¡Eîqöyç‚hÛÃJ~Ï=^UYÆÈ«†á®ä)}Ð|=UUiÊk“'î¬áe>›ì´Eié!:ý_,$*‰(/í½UÃbïðY¡E°Z# p­!“ E׌¡E°´ÚÔäÞøX¢{Œ´°Zõ®•ÏéÖ¨t÷8—‹È¢Y+ k‚çõ¬ë Ž¡ ÷ôøPî©pŒƒ°ð ­¹9Åœ¶cåsžãî]@¶¡°^7ºç2kcÕu¨€+ÛÜÏë÷Œä§öÀâ‡&ðW°Ÿ5}òÄ%lÉò¿<è:[ûd±WÉSQÕ¿utлœNÆt‡N®R]-6÷Ö#+T ö2f@rþËŸ….ÓÌx½HãÊ–j„Û]ÉÓ:iV¨¡ežÒýééfF~¾?eÁ¾l G¨“óàÆýã(}FêИâÄEûfù¹z">–ÇÀ÷°Zwà ‹H©BœÇ^×ÕžÓ ñJ qúÿ.ÂzŸU.q‘ÝY%²š4­¥û)‡ÝÁÇ8W‘¬Ý™¿uÝfþ<ÑÖy82]§«ŠaÜˆïæ )Îr¢ŒÊÎÇ['¼Ž¾ñëaÝ¿G±>NÇáj±S‚Uã-À¢?¹ S5”JZˆ«¤òíÑ—Yb-š=À²`•WÐÇ]P~¢ì‡‹”•ã*ó$v§c™®Ð–˜¨k¯¾újT›'`,ðÌ=Èé(úÇ$À¥B×°¦pqÂÂÌ/GÓ¦©‰pÓ,w¿ÒzP¯“ö.[ÿ7¬'ýN8Ú¯p°J­½ é.ªú lÕ¨Šò/nz¥´ýOã#t”ªÓAW~G‚Áp,2fH2“ŒžøÌ)° úÂ#ú{3Î[{ _º˜½¯Ýóƒ ¸)UµY~çø7’ïÅü± HIàž®Ô£ZìÇ¿A,™Èƒ›“+ı7òÅ÷ŽŸ\}%£Bêš¼>é© Àøˆ{/à5¼±Í+CZ‡¯³ƒ„o4ãÊ÷ºÆS)î§®Fó´U×@ÖŠ‹·œ$@IDATÚˆò4øQ¦ã ÷8²Ú0†C¾ú¨ ñ58à²-,Ò0fY[AëEa .óÛ$½ç¤²Ùèb`½™•@5Ʀ»ôÏe²¿-RCp±€ý»üšŒôq–q¦kS®€ÇîSc&NI¸ÙY.RAZ4<ᤥ†Á[™=€)*:øxíÓEK0$æ!«-h›¦9új.ç ÀÍñ–sÞ®àBÒϾkâ´[-j6¼Œ‡Á[úNåÇîc†qße°./Çð¤‡T‹ežîR¢0ÛÕ“H‡)+÷|´æ>ïí¡Z„úSxqå¿É¢Û%ßqâ~Oá§`B®Ó©¤308änwsrÅœjvoé>%ݹe$>ï—€Ë-ÒWx ó8ßì?žûáe<ÇEAAšâ¸SÓô¢ùø&sxØëS&ìÆT–¡‚0×d[¬Kì.½e†sÊ£¢Âð+ö 3aÊ$töNC¾vÕFhRW u§Ö-­gby}Ƥ§þ¬,=Ï^p¨`ZÚ©Šþ²æ:p†RUHfÕôŸüq‚‘ JT:èûeö¾ÕÃÛ_¨¹JV€€Ñ/q¤,@?ñ%2me%°üäP,`?)¨¦.&7“b˜ÐpúK8þ¤9ÿÀxÓ%ø= ²3…§ç¼ 4LŠŸ;AóŽ’LLQ8ûMmáê¹ì½ëm<R§‚ važåŸ07q:>/L>À„¬;5”ĹŸAfÿu9œ;5Íù\žö«d¹­²LVKЧ,¿¦k³õiüÿÜ“·µµ½•H×^7°Ðœ+‘g>š©ouOÇ¿¹ÒÜ#àÜö»Ë¡ý‰|ßEs|¢¢«°º«†‚#öÖÜ’ç Ê¥Ó <³*oˉ‡bUÍÙ÷cìÍPƒ-ÂÞÃÒ¢5(Lל‹× ‹ëïûÒ‹„•@ÅÛ³€±ŠË9Ôºð^‚ Ð𤠖ÌCíc).ãx3&qÓç~Ç”öôížV_:xò ó lyòý²è™qtfÚñˆžgÅR‡Z·²NØí>3WuØaŠÅÈ‚#Žl]V>?{öl˲õ[:Q¨ZxQ·nûêKG÷ûÞ;ujK8PiÆÂäî'*ýÆ8akýÖŽûðk"×ÃÉ/¶(p4Ž ÚdzZUÊNÝÀŒY]u—-aÃ÷-;‡UÅ:0aQú:·dò³žðï ×SI¶M&`X$/NŸ4áÿšŽÖ¢ic#ÀkcùÂåh hͲàcži±©góD-[ ßß[,MÐþ¤ˆ~‚€ $ÌÏÚh±YÃW ‡dkØéÔ–lº²m…¾ö€T>@”‚5A é!À«()ºe8ìßÒþrNÎw8îÙ.´é¡á û_™‰Ä>††eÞ`ÑmŸú˜X"NA qqæOXÉÃÁxJ]?+«Èñ©>r¤¥‰@à·jÊ0$¿-:ÜW˜9yÜ_‘Eähšð¬X©Cã€'ükº><¥pÅø=¦i"âZ‹ìå$R ‚€ p\e½gÀiG飱–ð¿ŽË/_C@Ø×JDäA Ž$-ʇ¦èÍË1ÿö¿S†ÅãÇÍ8ÙûBÀ¾S"‰ ž#Цý á¥fF˜¬ã­ÕÃâ.3eï;ûNYˆ$‚€ xŒ@ÒŒTGhhë«09æåF€— f‚ûtí°¸Þg.x!`¯Â)™ ‚€ Ðøôž³!ßD—ƒ„ÍÙÓš9t}áê‘qƤ/¡HÀËs ‚@"À‹7؈†bЬÒi=u½³V¤ÍÝ0²w…U»Pu¿QIØoŠJA vô]œý0¹Î#ŒE,Î-*Þ?£v¹HêúB@¸¾•|A@ð’d-†›¢À3ú–”!qOšÇ²o<üv"Ž»'NKÄÒoX·”:b)³6˜ˆ<²ñ`lœ;cî×<¬‰š»ï¶¨–¥oN[êtÑ8â4è]¥ün¹™!PÛw?iѾS†Æö„GtÙ$mÚê!±›gË$2X¶~EÀÉÉïaÑóôûTUyÄ©9ã1è\ Ñ¢›…)!6[“³æ‹-çH¡žWX¬)X¨>ë侈õX_;Þ}ø¼ytk)à“‹:½ûmÚÝ£d¤u +.Ò?Z7¼Íé}dl hüJ¿YŽË? Võ]¸Ó·íÓµÞ¿wå”®m),$د¯a ‹KèÏmé´jÃý¯mi æ…MÇ"î·c}Úoêã~‘§”c .÷ôujûîoÙ®Ea‘#óEwfÝà%½)Â}F[dýåZv“ZŽpôÄÉéºòU|머Goº”î»æ"åŒ>]„|Ë®„0Œ ã×**ŽñÂCò°ñŽÕâÁòÁ¤ ë!åïƒ%#"5:Ç|÷ñͬN¸ÞsÒYÈz%Z ù<Öî™ç:ü~ui%®þðùf[ƒ|5ý¿I=;©OÜ2ÔÒ­C\ý£âÇw`|ž¼u¨…ñ‚Çã‹£Æ?Í$ìóå| ÈU–Ÿõò?B-”!PáÝÇ7³¬^Ÿ„Eéë`úŽr;1ýÃcÝŽåg!àÓf<@ƒá±÷|RÏÎú#.P‚l²ºVMž ƉñbÜÐÈôÜ-ãiè<»jr/¤a9-¥r+ÏIù{QÉ¢I àþî£Õè¹;žJ¾ŠWyï“fLªòÒQPôÉ©CâÙ©UB"à³Ì7ÜçÛ.6šnvn•¨1òÛ[1nmcšë!!a3/ºhdáòöu,Y>•åe¹ÛÅ6×¥üýöÁ Òw?šl6Û;£G'‡BŒ*ï}RbÏÇIQ~dÑʤj¤}¼æŠ¸N|,¡aðYN³§ÝÏW× >Ã"–oÝÆíÚKΰhºß¶o÷û‘ &Æñùæh~&m,/Ë-å_·²—«š6æ»ohgszhTùÖ+ÉËA6ºçÒJÑÒ[8úÇzò¿ãÏ%]¥P|DÕbQaogéóõ¬D?Æ184ô>äÄ.ãürùl¹—ÉÌòJù ‚@0ß}[õAdQí{ÏÓU*V 7ø®Ÿ•š²éé:ÞR.«%¾ø!Vn{lbŒgã¡FµÔG’Wƒãˆ±±Ãn¸ý œæy`}±?˜Ëšå b9Y^) !Að~‡ø[zíÝŸŽlª}ï“ægÿ®(êø£·ÑŸL;èè±üª/|‘€U˜¿—ð$<ÎW‚ç0ŽŒgDtóÁÈÍW­`~¹–ÌrJù{^î’ƒ `¾ûhQb«j­`F)aaæó˜/Ú˜7€ûƒuMÿŸ¬œTÿϯ°aalZgžáJ&ÙðÎÀ82ž¨ØtDŽ!Ø|Í .·~Y>–SÊß;e/¹4mÌw5ðN@â˜ï=WxƒUºé²1 Ÿˆ×Šô÷ÑÅ濾zBÀ' îññ<½d=éÜ$³ž*¦ÈЇòìÉÎXÕ6G58&³\¡,'ËÛH²Èm€BÀx—Tj¥ŽûÞŸ²`_¶ª«·àeÿ²g´~yÊð¸j'ô(€Q_ûȱ<E§fMqnçã=%öR‰ã¥9Þ9੨ŠÂC‘Ü-`_)£Ü!×Ð1üL‰`y§œ«Š@öÁ#”u ·ê Šáçxó®ÌcJôÝiÛ^Ã;f9Q;ŒwŸ^¬Æ´€ùÞ'.Î\ ›÷yóø?“2¬M‚y,{ï"À}¾øÁ°¢êUçïø×¿ þ½:Ñë¬×¾Cyäp: ch뜇·.üuÝVZ¸b->R@­¢#i@ROºèŒ^uË^×_îæñ˜ýAuËÜ£«J˽T®`”ÿ1?Ý%.Þ“y¦¾»ÆÝ1Œ:ĵ¬ ÑÂk+„јª__¦,œ?HA7G(õêÒ†nrŽq\Ý}7ìÈ wæýH/=v=Ù¬U??ßþþ×ïdêÚ>¶ºË%®Ž _—¿©5zï•6íÇëiðö‡¤óÓ #{'ôž³!¿Ž·—ËŽ@Õ7à  šÞ,è®3{CÎÅ?­¡Ö-šùoß».?·/õ>©-ý¶~}ùC æ}îL‘áÜ’\»Pé%4› k£É©v¹y-uy¹#G~ƒÊäôÚ šJF·ýã¼F)É;¯¼•ÃÚžv€æ|û;õèÜÆ˜›¼:Üzt¤>VO¾Õ¥—8ï "5+¹'|ï“f¤:R®hî,Yƒ/C$ÞÇnEEû_‡$·xGÉÅDÀ—˜eâ‡Ä‚B÷ ¯û{/ý˜º™NíÖŽ¾Yù…Á‚ìAç'œlèÿçÖ4úæ·¿(mß!jÛ‚.;çTÚ™¾ŸVmÜIÁ¡¾rÝ6Ldq&‚ë—#ŸïÑ<–›_D'wŒ3ÒvnÛšNt»ÃEŸ/ûƒÖþ½‡=‘aw¦«.êoXس¿ùƒÖlÙMV«…õïEƒÏìcÈåþ說甞ŸÐƒü¸†v¤ ¾ÝÛ—Ç×ü‡Q±á2ç—7_±4K˽T&È׸°šãé[)?[ú;aÈ Ý8äì>—ïÎ[AaÁtõ`™FôÇ_ÛiÑOëèé»GЊÕ[hÙïèÑ/¥¨È0Zž²Éxþù¸ºŠ_ *«lwjÓš¾ûcíLÛg0¿¿®ûmúì›ß÷°{Çxšß|‹E¥-»³è㯥üÂ:§o7Bs)÷=2ÙN<ï«iÍæ=TXb§0¼¿x‰è‰›/£°Ð äsâ÷ÇÈHþãªñ{Ÿ4oïö”a±w¡,f1|(’›S‡Ç|›¸`ßÿNï!à+`ÖÈ´„x̪WØîpÐÆéôÛŸ;@z§SÇø–Ƈ ¸Äa|¨>^²’:!îÁëããÐZFEbßžÂCƒ©ßÉéÖáçRç¶­ ´[4 §+&%‡ÓE‹ZkÄïœ`þòTúcÃ£éøª “Ê›ÖV¬Þj,!xë°óiØùýhîw)´ÿð#ÏcýÛ²;Óøh±Ìu e–¥ù–ã]—¼¼x»†lerzñM#«#Å”WXd({¢ç2· È =™"¼‡r ŒÃ³ûv…¤Bs–­¢œ¼´º¬¦³NíZ-ùòL¢)¨´~B,Fïé§œdäÃ2lÚ™albÏŽ„1©ÄqsóQÇ.%Ùÿ-þ•‚Aº×_vÈÉ£¬ƒGû°9ÏÔM»é‰[/§[Påën‡•Ï•‚º¼?†PMô_Ù7•ß/÷÷í¸hð|ÑHü¾™HÓèÔáíºšÇ²÷_"`ÖÆ|8<×Ì-‡ èG øüã‚r¹4ãƒÁ}Vv£O-Øf5úUcZDR;XÂV‹…ZD…Ï$Æ>Kds¿(*"”ºÃÞˆ‹YSçóÕ݃ã‡e‘س3]ëš?@§Ü£é¯m{ ‹ ¶e$uïËKÒêÍ»sÕýã>`®ñ³•Ì —¹‰s)dæ¥KÝåñµgÒK*6N6Çz.' ?ÿlE3¾öÙ2â÷ââ3ª¶Î˜y,ýu=}ºô7úaÕF:]%1Í›™§@¸.ºrP¢á“Ñ©MieÖ<ÉÏ4W:G b𨳓qOóïÿ±·ƒFTDõì\:'€éŒXÛ÷Ç=ß&üÛý=« ¶Ê}\Fb4Gk.Ç')£Ù›Z‚àæH_ ^'¶|9D„³ÿQ jáÆüs ÍBó×Ó3ç¡i¸ ÝpùYFÓ³q²Ò¿y?¤µq Õ@n:ã¦>ÞÌPÝ=œ°”¹i­C\ 3™±Çü¬ô÷ž,ô™íÃGnWù9öb­.°ÅΓڵ¦á¨Dxjýzx¿š^î«rÕT~ŸLWÝsY ü\»v‚êÑ)ÞðX=bà1ªøšЂÄMЇ@¨/|ø5}‚–¥Q#”g‡)EË»ÿØîPéåÀcV[ÂÑÐ =»´5¬gîFJßw˜¢#ÃAÄmàhV»÷ÇÌOöåïCÑ÷£ì‚5W´½Æåpþ†V |@õ$ÊÜËK>]ãL$á1ðE>¦°u= ·ºÀ5òq· î ÿ}µ’šÿ†ÚzR•¤»3Ð’_ÿ¤¯l|¸¦ÏÍmÆã\–ºº{pß.OŠ^¹iÙ¢ª†•= ±‡aT¹¡[“øë³¿£fpººýŠó¹ ̃à¦}¨á¥þ"g ÕiÄdÕ=—,?ƒfE”Óöåð®<09r—G›ÖÍiéÊ?‰[؇áx»iºÀ/‚+–îÁ†÷ ºVrò©º}¸uê|,Ì€{~ýó:T2PùŒCSô©† ÈÁVzMÞ3/Ù×~óÒ×®†õ‚5zÑÈEWƯ÷Åi ³6Ô=W¹’ð¥æ¾ã¿Ý^./ncg«üÂb£ÿ—?Áìä×2Šveì'¶<¹ÉëPn¡ßi¢ŸêWx#á¨\z\Í®±¯F?÷…q-~œ±8ôCSôï¶ÓŒ‰ä=Ÿ«LÔlaOŸûâóhðY}Œærþ¸ËR®æö5‰jPÜ«¨±ï_H¾ÅÏËN8ãñÆ¿kº¢Ûc<ì3÷ç]2v¤•gát¹èÃ…?SZ…¸î"Ê>” gÅck¹ß–+©l©®Ûº—:ÄWlj.ϸÒ&÷ÈðÃá‹­çepräáfذ= ŠKh ¹›&²jòþ˜ie_-µzï’ï~µŸ•œºÞ‚œºþ®žœìKüQ­’¾Y½iØxRóC­þ- /ù¼>ß›¿ã-¨¹ÇЀ2ïè `™~²ä7zèùYt3œ?NGß-÷ûþ{ú<4“¡¿7McƳxB¤ØËô/¤—?6¦Y¥3á Ò·{Ãú£Å¿ÐKˆç¾äPä{×Uƒ¨µ[ÿÙÁÜîÆŸx¹o¯n=¢üKi_¹/?—ÁA[ç–Yà.¿—O¼4›®»ìLÃ?ƒ+©?®ÞLì‹ñÒc7–¿+ õþøJyÕE~÷ÿܼcý‡ÿv#®÷è½O;N#}Š!‡¢)Ö SxÈR]äòçk¼Å{M¾ gãa§ªÊ»»ÜÉ—Ïו|K¯µV!_Žç{³§õ º×8©AÀ븓ž×3¯a†<"¡²LÊÏNŒ‡1j[Z6­Ç¸âv1-*¼+òþÔd/%KèßóYdU:S×CÉi•# 9/‰çwÙTe¿SA@C€+»wÀépœÁ^ù䌓O5&˹{ä @SÕ¯ôQ’—;mëíÚè¬ùH;Ú¯”ð!a·íɇ€QAÀ·è…aH¼Ið-N[±^ÑÏ’¦3$ÓèÙu#Ú.î;7½ÔaÅ·ÄõiiÄöéâáA@ð=¢;E>ai›Ë$kæ(q¾å{Rú¾DBÀ¾_F"¡ ÐhðêbæÐ¹FBnìst{u[‰Åb¹¾+Æ .ðŒ’:4æŸÔÇöññÆD€‡ëñˆ ‚@eúÍÏø£F_3ã5R^^welŒy,û# |bŒ$… Ф؇ùšgÎ]n,’›W:) O ë&­Ä¤4¯|ò­1Oj³`ùjšþÅ÷Æ"(îc•›4€¬¼­9SHÙe¨¨ë-vz5€ÕõºjBÀ^‡T2 ßÖoÇb&ŒÕˆ^Ÿó¡¯\4dËkTóB'<œï­Ï ½XÚ“gК‡s¼¡„ÀF€çЉŒ2µDSôÕ©Ããëó†~¾öóñúF€—'äÙ×xÕ¢=X=ŒÇæràÉ2î¹úB:÷´îÆ´­;Ò÷aÉÎD#íÀ¤žõ-–äï#$,Î^†ÁÙï™âhšë%Y1ÉDãø{!àãã#g&@Hpéìo¡eûBÌǧŠ4'§á9›yòŒÈ²å;yÙN MK¨ò$fã(]ÌY§“õÌ4^ÆP >@rZhêü‰ì'ý±a'ñ¢%L¼•C˨có”M» "–æçÊöqœ¬ýºªüÛÔRÑôÿ[=2®µy,ûꮉ2Úb±‡iï.¢ïÿØ@Wa†c…^˜H¼Tçcÿý‹9”:k+­ÄJ|»×̱ÁpœÒŠÊæŒ/uhì5¤žWUö*œ’™ 01-"éòsû M~‹Ò¶‚y_1UÇ\•Ï¥ kfËþ(BÀG±_‚€ ^@ *ºÙ$ôY”:èz{]מðB¶—…pÀ©($‚@ã"ÐmÖ¶#J—+4DÑŸ€Ü¡q¥ò½»‹t¥2ÁÓh=9:û¼ûïJIk|È,f(uf9zlÆËÞwðö3 åï;e{òž'ãOrX‚ <½£®Piz¦®é{4—ké{ÏOJEž¼4¢_r“%`~ùøÅs¹4*)qÐòÕ›iŚ͔W`)êdU‹HµæâI*vÏㆂIsF‘S ¥Å?¯§Èð ˜Ø‹$ö¤à`k9{z¹¾æð3À$ûýªÍôCÊLQR/eÏé~XþŒÏ®Ì´q[:eÊ¥"L7YXTLE%vLAD-£"©9fÆâJe×ö1Ô>¶¥_T(åݯù;R‹”é"½eÀ€+Â:&õ¹78ØvßFRãpBu*aÎ|½uñÁ„Zäy̤vKV` ׋maªjµü{ôøg9úË6íy}Á‚wy“Œ™‡/œh’l¯Ãá0,ž9ËþÀÇ×N¡¡Û¨uËuØo!×KùhZŒ íûÒ‚vúqõ&ºvð™Ô§k{cü$[Åþ¿½ë€ªÊúç½™Iï¡…ªR,Ø×†eE]qí+êº~V¨èª« :JXV¬H‚º®»6l Š A)–TŠô^!¤'SßûþçMî0é3“ÉdîÍoòÚ-çþo9å¶ÖG€ëÀšÍ»é/–BÛµ‡¥ì9W‘^þÌœŠaý™!ñ—õ[©Ê \TT1˜k%~v2áWRM{&!?Éb£ ?ɉ18˜áXºèôã)&:*"™±lûFQ…úwZ&ü,7ÝûÐ%‰IIÓuEÍèU¾Sï{hõ.ߦD» E†™t¨œÑQÚÍÑ´3ñhÚ’vl׉½§t;±Ï}·ô}4ó?Óžú ¹ñcF±îˆbÀBòu¹ÜÐ|´pÅzúâ‡5eÙOݺ|F11;Z½ ˜±Çǯ4~6[:Tråb§+†¡‹ÎHf³IjíX ¢ÌÇ¶Š³þ‚²?€²Ÿ–²çlErù3sZ½œßúl ˜ª‹âPO»tÚHÑ1ÛȤ6¾µ¤Ë•F}UW@Ÿ/¶ÑRX’îºæB:ª{'o]6;Î>X\N.·Û8¾0%1ûG'¥ÝÚÃ0¢ÜeÛiãZ/ó‘¨[w_Llü“©¶ƒú9»¾§ŒÊ½¡d¸ ÎŒ½ñzþ)ûâ»Ó’žtÑãÒgßöHö„7ŸÎyX‚ŒXmøˆaÀ¢:±©¼Ìwö¬ýê§u÷;¥§Œ gƒÜš/™áwëšGEE×Bö ^\tú d±H“tkà.êÀüŸ§OÀ|Û²ì9‘Rþ—õÛöÑsaHd7uíú>™MžÃmš+ ³¹„’~2~v{/:xèFzyÖ7ôè-—ÑŽýÅ´‡9¬ÃغÍÁ Im‹á—~½ºÒûÒà=!Y ͹¶¯–=‰üɶß2ë„Ì—ÊŠ¾ùþ‡ïó}¢oÉfýüóU³þþ Ÿ®ÙôéûÞÃô-)ý¦Þöp–òæ3¹Ì„™˜ˆdÂG féž Ín§UwÒ×?{˜oçNïשWá}dÆoÐpðúl1Qç”Ü¿·Á„M&¶êH*¸ðÉ>|X<3ß¶.{ÎW8ËŸц´m÷ÚY€s}«íÆ™¾³Ù8åhó®ý°À”@ë}𫱑QÀÐGGï2ÂïÙ7–¦ü{NQÒÈbª¤èØõÔ)a—¿‚:¯ërºSÉaïAëwG«1ÎÌó"þ<ô:{p¿Çȶp1ú€MÀÌ|c®¾=󲸄¤Ç™ù^´ý‹V×z›"Ž¿AÃQ—1νñž6¼÷Ê4Þ›š™p} °©ÈÂðíˆ`ÀÜÙôÄšoiY}ºø7ÃìÌšo¤8¦Å¹¿3}8õíÑ…â1kË—ä˜phJˆë€Íæ¤w¿úÑ0;GRÙs[³ü.N)ZO߯XKE¥Sr”¥u ®Â}’…¶ïé†GÒÍ|EIY,…˜¤U€GÌx!ÅÅ­Ÿj]cø)~˜ñ\L„<†JÊ.¢w¿tÐ+7ÒWoLîb!´%æiÙökAªf¾Ì;¢{÷Ö¹[Æ´4˜Yó U-´(‡¢Su%)eúàÁgYµê6çð éˆî𠘥~žéÌc¾Õ˜Á¹ä×Mü]Ƙo[˜«XLKZÊ\*8p'}‡±éKÎh0_î|ZÒ5–Þ‘ô^ÔË×R)&\ñ˜o$•=—Ek”?ç{WÁAzsîbÚ[XF±1ÛÁÿÓ÷&Ô­†f÷³òìz«Ú5*£Û«~ÇÅ«bb¶P7ü*«Ó®ýWÑSÿžK™×ü‘ŽîÙ%hmX”»lûµË¦…O\IØ4Ç3ïbÏ><“TSWóm ³scyaZÎÝý:·ÿuÝ]8ô0`^ƒ,ÌС©ä%Àûˆ‘X 9 ¯ Ø3î[‰u½Ëa~æÙÎá˜p¡ðÌ41m?¬Þdhë.h.L¿t-C@ÔEXj©eÏ9 eùsž7lßGÿüïTX\@];¿M]»¼mtM#Ì—)e¿\\ñq«(£ë«äpÑ«- û ëU0í@”;[¾dÛçò ‰ój¿ˆ->)%ùŽÞ˜íÌ㯑æ˜&¦-..v4h‹ÆΈâyEL¨ Ð+c¹‘ k·í)ÄF7%Ä­ uR!‹i«„†¾´:@7kïœé‚C@Ô.ûò*GD—=ç0åÏŒg&?Mÿ`>xêAêÚíe‚° BY, ¹óë0‰—Ðk³QgB „ ‹rç6$Û~È ÑWûú§+OWLæNXjÔ¦ã¾MåΠM5u¹ì†ÿ;þXkgí=bè=°“¯œ†ùy :a6wErgÄ´1ë·t»±dC2চXÓß<±ÛØÝ,ÒËžsÒÒòg&eÇæ"obZ½¦UR—.¯aS™Š¦AŠÀ¯<¬súûÆŽdŸ/Y‰1b»Á„ým ¢ÜeÛiá Ì“¯b;÷èy^èXçÒDBÓÆ4¦tê4 ñ2Ý’‡à¦ââΈïvijŸK+ªõŒ­µÉFS´øûiã5—Åå•ÝL ’¿¿é)þD(*­ˆø²ç2iIù{˜ŽF‹0‡`ÏÁRJKûã§íùŠº½ëå£ÿ­ÝFe• ¤¢ÜeÛh†äÊ ›ÿ±DE÷ˆqVé5›l„$PG´1ªÉÜq 8bψ!$ÔÀs|š¦‹þ¹Ú±}^%~ªŸk[ƒãd˪læM 8Ò‡€¨¥•ØZÔT\$alù{„MÞ`fv¢ÚEqXúÓÞ]RÂÏX*¥ÓovC2lŠöG å.Û~Hkó GeFm²˜»Ä9+"ÆœÛXN™FlWÙµ†îˆî° ˜©0Cñd&ž éÄšDU n}cc…Ûï™F'6-`º… ÚŸN§5hiÏqúÖÕP±…b{pÁ”¿GãÓh&^•”Û()qi{Èj³4FEí…UÀE{c¹þLLô-wÙö›…Ø_ÌhùǰÁ„Mª).ZsF<fUEM¨¡[˜ #‚îË€¶aºeÆ?–œÁ‘ùu»pBx`Ú¥ :ø"¦Èö&ÀZþœOf6ëq’—ªh»)xÐ",¤ £”WÙ +¯iögb¢(wÙöCZ˜Ì/˜Õ0aœIÔnœA« =bø^ÄêrR0ó\n°8²*¤‹,BMoÝø ¿×t4,7Ô>£¸®_ùÜ07CîjGÂç&òç|²É•™Ó®ý‡(*š™pø·l¸ZöVǪ6± ëä‰X<©Š™+ç¹1ç[î²í7†RÀï™ÙŠŸÁÈP톣N0­L·ÈCDÐÞa0W/oCDwÖ\£eÿåпp',òQ´µ#b~wך™ÊŸóÈš3§ÒÊJL6+ŽÐLNŸ¦ë&J6 ƒ¡6À9•Ãå.Û~à¨7—yµGÞáK£™ ç‡öb@øxbs@†É33 A{˜’ì°É0ŽíÍRþœ? LØÅó:ˆö«›©¸dEcñHguÈy?ÊS´ŸöVþ”½?8„ØOÄ1±ò'h Hëzíð ¸uá«»I„^j&D |0ƒa͵àhœ¤åÖc׸OJÑQI>O-»ÕÁ| ^­¾3×9íŠûNØ#hkVnïG¶ ý°bá «Ž×ap(¤ÞؘTzðƵÔ-}PMPèüSÆÑ¸[vRÏ.¼±ŠÇuI;n¾ôCúÇ_7ÒßG¬¤+‡¾„óNc)>¶3=tÓê”2@x úŠüx; ØRÌ} e^½¾§™ÄÇu¡[þô ýËŠzˆ4ôí¤þ7Ñþ~=Ì£^ ^4•Vðy¸‚µÃâápô æÆ½^wÁëtÁ)㽎î~ê÷*vúÞw|Ó¯ç0ºõþÖШ«¾§NÉýï7 {›Îü÷Z~ý}Ø_øTU}ç9JOî×âöîK€/ý¾ïÛøž™–ï/hr³…ºß4† >~—«PƈLÒ1‡aåÓŽéORÆuwQLï~ä*)¢ƒ æP¿ÞtZF@Ï„±ÐÞ2Bj‡æõ\Ò5€›ÓN=özoþÍÆW~¶;+装wÑW|S+ÄÚísˆ·…öýF=»þ~Ûôýüû ºçºŸqÎï±TXâÿ~¼Š×Ñs¦ØxlÒ‹"ê+ Š-rÇŸ@Ççæ)¬’E¸{†xµöÔÿ´ŒL´kra8ÔVMTZ~Y°<ú*láMk¶~bÁZÏÿÖæã^SU‹°Æ¾i`V+Ö½Ag ‹x?ú²¨¤‚f/Z²ÂùДÿá œGw\5Ô0É bŠJ*é·µ[HÑU*)½€ºt~G| øzÖ {i5ˆ*Û!#l'äÿon¤KÏÈ©WŸŒ³ÁŒé›e ôý/Sé¯—Ì¢äøž´ÂëÁÒMtʱ·Ñ’•¦\+pi©ó()y•£­…§ÐŽý«)9.Šbãâ(::šŽïÛ“®8ÿäZ1¬Þ´›¾ùy V<ðÙߨ|§º‡0TRyY)™ã’ÈlœX+ˆ_¾u!ÊãB/¤O¾M‡Ê¶?ÎßÑ݇â¨ÇÍA·w_BZ£í{ FÛ_å›N ÷¾m©º’€)Z‚¥ˆÈÜ[Ú¹—’ ssÑ">ªý[R UïÜB"¯Þµ™µ'2ÅÄßöÏyËЊ™!Ûvâ[r‡[tÊT(²’žÜž…öÿnDWZƒÜÁñ¶&£gM¹[ê@ÿ¶ÜðW^µ@àóÔš '?FS§%ëÐxÎð6„)0`U­/³6õ­àÐjJO:æÓÖÛ°(&^)3-˜´î´Á|{••Ÿ4¨]RNðÖ}Žä³%÷ôÊh¾‘&ÆvÃØ³Ý`¾ü¾¸b»ñ9)¾‡qåüwE{ÆñÖéé³)5årw›»yK«8¡v¾uÁíæeP.XÄ’Œd¸ˆ2Ç¡Œ=ÃL²½û‡~LŸþTµc´_Oû*øø êtá•ÔëÎGi€5ŸýðUnZmDæ,ÚOîòRŠëãÁؿڇ¯ú½Iû »Õ©LK<Ò6¶/(­ËÎz›@û}Ï®’rjÒQÞgyù¤%ch7-¥ô±Q½B©‰}ŒaŠ@âKOI ;¯>»Z%àåeŒõ›cY;îÖ9•n½ò<:Px¾ýu+(¾ æèTJI^€ïM ޾±²Y91¾q½mαÀépÁÄPãœNÏ}Lt²ñ¦ùïÓ5xA€#INZŒÃTŽ#³¹/ºîJˆg-8ª&Å×Aý{ÿx2Ÿý]V^AE‡ŠiA!Í_¹ƒlAžìé[Üšƒ¶ìYh +¥c¬»,]KiúaÓ¼lï‡Ë¤±»˜Œ>dß·Óû¹rÃJªÚº–:]|-´_væçZ°ð`ƒß茞â±Ã\%n¤(Y‚/ ðŒË O€1¸“é­/¯‚” Ûg+«ÚKB#ïš»v6â_SzZj³psqñ÷çþó%­\»iÝ_xú~<àûÁDÜczÄPÜ´c®ÂÓvY¥HǯÛßîä…!'ô?þÁ[þ„ÇàoGüú§K©¨Ü \R|w˜Pÿ|â5!Ë+÷ãN§¤¸î3`gæËÌÙ‚Ð̘˜Yÿa@Wúesí)=“**N£´Ô¹”ð«_Q&Ågþü©ÿÕŽh€\|g6ÈÎîઅ †6ÄX¶ÔÅ'üBEE½h_a1…<²IžÇI› niº¾n]˜÷ã?hPßë!Xœ=ªÿC§'U@è.˜ö.Âòµ5Úþêõ[W½5mÊß}°í¾všþJøx_Úý½JïF•ë×Çcù§Qž«3/£ô¡—Q¿‰ÓiÓc¨bÝ/F”hÁQ<õÒß4Úƒ?i‚n¤”X‹MŒëÖÈ×ú¯ÿpÂhÜ÷FzÿÛ¿ÕcÜÇ']ûA€Ë+!€òo,g ˜9ÍãÆÕ­WþÌ„ l6SLL4árh‰ñt\÷TŠ‹R ai’†É~ºò*ȯúÏC3l^Oˆëj„IK:ʸ 檺o14â-,.3öu÷gOh#@þÕ­ g%­Xÿ&}üý(ÚV°Ä˜ß±§p…7¥PåÙa¼q:@æ4nØž*>‘’Ÿ‰ Yo«ôíÿìmªÜü;¥œy‘7çQé]ˆÃt4'p#%z¨l«±Œ( ³›sCúÝHž:ž–®šFì¿GçSŸ— sv1â;’ÜÔ™¦«&¿Õn¾¹üyì¶¥Ž‡2Ø*Ûn\[ë/Óa­5à¸ØXL8‹¦e;QµÓEÒ?‚w‰ßI;]UÐèöSšùçÙÏ´øKƒávºà ¬óMe¿ýhüÔèXÒì‡'õq¼Ý®`÷óCK4H÷/MÐM”oœÐ˘Ñ„·F?ÅŤÓ1=ÎG<¹Q?‘öar¾ú ~ÿð¥krž2ŸÍɳf Œšœ§¾Œõ„»ñÛ<%ßt ûƒÿ—t¢óÉV±hr¾âµu*º~ ?çæ+Ûá'ÿÙ¼Äú»Xø&A÷¼ÔdÁŠI4¸ß  Êcþ ”<Þ´å`ÙFŸ5âÆ˜ÖÙ}÷Ëz … Ä„eEàã{oáo {6ñðŠ¿®îŠ“ý?Z¼êŸTm+ö7ŠýiZâPk?h‘ׄøeݺÀ–16»×uí±½×ÍC¸žu·‹v¿5Ò/º ÍëpûªË|ÍÉi”tÒY´ç¿/†‹´°¦#5à&àæ%ÎMøhú7ÒŸ mÚS„}Uuó7nÅù>Èz–I›œu)Î>îp¬œ·þVð¢Îj|Ò ª®:Js»æÌšuý§#F|0ŒöÚ5qèƒw•òfIÑoWÈü·˜”®klÅ{_uP%kȯz¿GøÍ¦]ßÿ‚uk¶}Bü ‡c†Äc¢%•“¬¤ôÔà™¯ ÷‹·A]ç,º;¨pu•–qR©WZ¬1뙵}þ…ÓùSÚc{'†uÓ*]ö=ñ¯)Ç“²~¿Lºƒº­‹†ê¹¶¿ô¥?\ÔËt¬V°·ÿÔü(Ïî ŠkŽÒ|ÛH_чëº:C©P5Ű}à–­eŸœÓ(mº2oü(ç²Gì®ÆF`ê:}Âë†áM54© ÚùÚœcÌã¢û”^-Ѐ;‚³;3°ž÷”kÆR®h¬63¾yéUS¸øbç|•/ýD›X|DZ3¤(µèö˜Šš ŽÏVn¨q¢Íb¼¿þþpHºÈkËF å'8ó«¼­‘kÂ<ŽY.×+JÔuXÙÉ^þ¨»¿›J<ë†QÝšÞè"O]U¼k34E©@|ÍO)ŒÐ&}ûbfAÇ­é‘_öœ¡`ÊŸ'$16×Ô›¶:©É ð£¦ÅPaáMdÂaǤÇPTvŠŠ67É„}Ë]$)Û¾@"èk=¦år»ªí ¿o^B :Ù–t¨Q:ÚFU˜êå§Î÷°ÒãÈæKÆ5’Ëžq ¶ü=cÀT…ôH§ŠÊS æieá=ò C‡.Ç~Ö¨KŒNÝ’c°¹0vøŠñhÂâh¦â퇯²í7…T@ߘá¿êªÊƒ6Kœb7·Þ>çQÖ€g¦itT;áÚðÙ6¯:4ö6@tJcX¦¥\6|´¡ªØÐ^¶¶n§³ LËÇ‚ÎBÄéÀ°@<Ùm½©ªj ¹´ŠVÜ”uÌ ”œœh0àÄ„xcëèšIX\®M9ßr—m¿)¤úÆ•Ç`¾¸òñO®][·}e:ö¸ûpàFF€‡×”rž™¦‰½•}Û¶Ì©¡WÐx#"}‚4]‹ý‰!Bý)ØÃ|¹SŠ2:¦ž© UŠŠ¯À¬XKÄPÏ´MÑfd$ôŠÙž"/Cl;!Dà&êÀàc2 c|1ÂÊžáliùs^9ŸÌ€y²—vï”DC2(‡%_N{öÝgœÌf^ î€)û¯†YX×[>1°èП©¸äb#΂·!Þë1^}:á 'êí ¾˜'—žkœÆÌ7)) L8É`Âí“Ì`Ñâü6æê–»lû!Ð{¡92cæëàßÒyŸ~ívØK–ô¼@saìHqL Ó¤9íÅ‹çΞ]C/Óí«·9¹\Æ6è”X#àMêYzŽÅRï$3m)é‚ãÍ®¥Î°<5Óâ€vpRO˜JA#ÓÌlÏÈJD‘Àš¡¨q˜ˆuz¿ Z´V¨²gÀZZþ¬ñ±À æËŒŠÍ¶N‡ÓضÑ1» ´Š *S |\FÅ0óFEíƒF»,æ"RÍå)uc¼ØBnWñÚ[,1.ÌWI£DÅNå`ÚÌ8ÓR¿ ²|#¼Í~¥šìkb%4ƒ§Ç*l3k|šSbb<˜o"¥¤$̘ßy´_“aÙjŽßr—m¿9´üþÎ…&˜¯÷öªªòŠ_\üÖiç»÷ûÞÃô‹¶Ѹdäw2-÷È´ǦÓê½Z^^ÂK ™^˜þˆÑ€#’»u­Òæt²ÄÕ¢Âf(fdB+àΩ $ìJGÀìE…7Pz§akË)k>ÜùV‚–£R£pž0– F¦—é• ÚæpêØC·˜F¼ãõ…¡(ÎhÝ:pTF,­¤µ{PöQöémWöL_¨Êߣà &Ìuœ,à=“yR;fH©±8¤¾ÊIÅvª\ݨ¬¬;Äê71fºÑ°¥™íov>§ÚóÙSNKù ÌÞ³á‡yÿ4¬i¯®:–JÊÎM](Éä¢Î±¬­c)hõX' ¹›=B2Óž€ÉV<æ›”ÈæçxÏä+´6%syúãê–»1ƒúnûnÍ}x2‡?6ì‡kÌÈxŠû“ê_—|ÿkjç.³é„Á×ÒQ—éç˜}ÎC‡Ÿ°9Ö|™ùnIé§ìظîÝÿ-üæg$Ît2½L7Ó/0@hÈfÍé>PRZÙb|Ø,g1:%žEi·Ùqœ™zâX3]«¦ýÕ's'JKù,ìcÂlâcS8wL½’MÔ¯[’Ñù0ÜññrÜA5enkĆޕ”Uê»{McŸkCÞÃý®M›½$åÏ™h¨ :ª‹Áœ6îˆcíڦ왶P—?3®31X¬%>)ÈóÞŒINÕÐ"m”ä°×0hؼéáÌ`lc‰UzDqâ‰góÏ‚¯Щ~*Ýtíe~Qf @7<IDAThб[¡qï„pvB¬Û & |SÑÎz‘ÍŽY¨0]Gal·{ŒB[¤<3™Ù\ÎôŠqë˜hÏR£„Ìx®™õ,4_öH[h¨Üä¶ït8x¬o;ã*¨ãðÌÀX[a¦ÆL×tF/øäƒ/»J_Y“¢Ÿ³ë{5ÜcÂ<æËfgÖ|·­ÿ}ÖüÞcÓ3k¿L'ÓËt 4nÛÞE’Ì…k8§Ý¾·¼Ú®TYÆÁ¬ó4BËKÔiÞårStTQe6ÚWÝ c]wÂô» p%˜ß£S 6ݦÂñ:O^j³]yÂG÷KU©'4_6Æc² ÓéYnÑüxWSi‰oŒ#ãi«ª,ï|®^Ü}Þ…ó¶^úLg(ÊŸ3ÑXا3Ö†*´¾@÷”},Ê>®uËžéiÍò÷æC-ìø™™/kšà ƒ PÕ`ü!”Û4#Ðäaֵ˜<39<;·[CtP'»‹Vº¨BcYYúÜsïuÿñÎfLøJ†©9Ñì¢Äž‡á±ì°pÉ?c˜mÓŒŸa&ư [~¼ß™Qƒnf¾LçÇ_çÅB¶}£í;ªqÀsmW¯ÝÕþÜà‡ñÕ€™¹qÍμ`ö_í/(8õÜónšÛÿºäÞå;õ¾‡6(½Ë·Q´‹-À¡w¼Î——ñlgžpåvØÊûþÛ7Yòk¾åø1üóÕ€ƒÉ;¢½‹$ìÍݾÛHJO¿õæ=ô‡ñ¾æFt@<~Äš¥ÌWœ%Êññ÷Dhª\0ÍM…¶~hèå”*2™Kqe«EËoƒèv%Ãäã’кÆêÔ3ãu`ºž '˜tÂLtú;ÛÓÊGh1ÊÎëWùã¿­ý0=Žî{C(ÊŸóÒX8Œ'%ÆB[ ˨ â*„@ÔeÏ4„«ü=y壼 ˜×§ª¸jÌ6®öXDkƈ™‹ÃíYóõ˜…Í^“/kÀlÎfá5Ì;!–¸GºÈæÂ¦¥n¬?6òÈ´¡½rÍqY,`¶<ÿL—ëv3`ŒéB&Þ³€ì« “¦˜1CpÄì̹öu•;ç—Ý‘ÔöwmÙ|øt{_¿gðx•9*k–<óŠgç’ÚÊÿ²ñ·_6uéð ]ý=ogŸÞI›ôg•ç¬P¢5§ÿR"mÌÙU‹^eIÐy“ Ô=Åå°—ï\·fÁ_~þuUU[úJk~e¸2LoDÿ‚ŠD¬/š7û÷'R´ì÷­i`À-*0¯$Œ†ŽCâ æ‹‹á< TŒ?Ù¨ÆÍ* áW Ž8 ©ºÜ1³ôønÙ3ªI,:¥(h¼ÉÑ&JŒeiŸ;$žp‚ñ.žp’œlÜsGÅPs³=ý¥8êšÛ]¶fùOÛ†s/~þF‚&éüð?•‚–?Þ\ˆÂ¶Ÿ½`ž-®°Qq5+fYœK‘¢p–¿`œoÁ„X“4 L»v0_f ,ŒZ0ÌШèž2FÓû-C—Å'öË&\Ö ««mÆÏ`à8VΗûâmÂaëÌ@yb3Û¸ØXŠC}÷0aLª2&ò¦ž%‚¿glس¦™ó¬k®Ü9]ÏØsÇnûº¦•¬X¼`#pm,XH9Ç!´`fºÌ|E!̹ººÒ møs¼ÿzÀ “ê3à¸ãbÒ¢cc“Í&Ì„ Ã<›ýÐÁ²êÊŠ¢7¬Ý°ê—­ˆ–µ\f¶¬ù2fFÌ÷l~ã¿5o"ÀED?°g×keô¦Ô¿w·A%m4´P=Þƒ?·kîhXúæ‚;ŽN¯1Ë ­@Ld †ÑxÆÔ°.’¡‘Ô˜Úx ›Yó5LÐ^í×ÿ 'MÑÅø­Ù¼[Ù±iÃ|øãÂ?/ÎM… ã7_z ™^lžr](ÊŸóáOˆÅ$¥tÔžÀ$L³-­mUþœ_N[h´<9KÌfÆéË|=&hOi‹vÁmC€\ÿ°‰…ÍmÅfãöâaâlžZ¥ÀšÓeFÊŒŸµ_žÅã¹æÍ‚ùéxê:Ó,Ú‰ÀÍCUðÿý)÷ŽÞöwoÛÂÌ0”ížãZ°`¾Ü†cfm3¿è«]‹ßÜ3³6´d\EÜì<øá¾ŒÓä±]N“­09³æËÌ7bµ_ÐQ°o'ìþ~öÇ]÷Øï~ùSʸۯP£,-[ƒÈ ‘;OÇä™°bŒ=A:·q§RÓù²¹Mt*-a¾Fb>éq‡fŒÇ1+›µó5:§š¥bÌK„öêpº øá¸‡bøÇuÜG6üsþqüÛ3nf <†l³  qx˜8æTLa‘.k˜b ®QßÑÎXfA€¡érñ E…ªG[•;Ó!ðøsÛoÛw•aì<"Ú–o{« U ÏÌ„ÅÒŽS<3#dM”0OÐb5w¼ÂLÝæ‹h 'Òã<± Àtpšbb3a>ýˆLgú"ÎEšÌ ¥´´¨jͲŸ^RþpÎÄ·æ.ÑïºöüœhÜŒ{4z¾glt*`¾,Ñ;`žc³œo§Â¥&:¨@JIN¤íÛ)qºb‰Ñ)ñX~Üi‰Ž/´ò˸í-,¡å‹¾‹q®ˆ\Yù'c¤TJo¹ ™^¦[ýã%£BQþˆ×Û†«´uùsžÙqýŒ˜ë—ÐxÓôÕ^Ù¯øq=dÇ ˜ý8£œeFÌíD,u2„UýpURq| 3NSœžÉTëãõÌî?ÌàDZñŸhá*wÎJ[—½hû¿ý°èÊÊ2fL¡n÷‚ 2só½ç~†Ÿ1;Wc‚®up0ý9§ÃN¤'0›—ãç¼23æg~ÏùæÊ)Ââ6r\$1`ˆ^Pšÿåÿ’ÒÒÞÅ»›écÒÿïŠs•–h¢# ’;î ˜rgbŒiÚ/k˜Êè|ø¬o ïÛ)‰ÙÌtëvJÁ¦%±æË pùºmʦ5¿}†‰kñ WHQ)g9nÛÌ ¼åJ˜F'ÓÚ¥ ›Ï®EùsÃ]Ú¢ü9Ÿ 9A‹/ceuë7ûc'®¢ íÚ.kÑn˜”Y+fÆl´Ÿ6Âá„é›Ãð™m—…Kϸ´ ÁH( ÿ˜&¦ÅC›G8èèmËÚUŸ¬X¼7½o­v/úk¾ !šûf~¬ùú2_1VÌ•+æ‹`†ý…HÛ·ß}_ùÇßK†FðÈú×b ØÎž¢l1X,±p…áB´=ë¹Ã®» òº}EÅú—ž©†bLXtÜ1p‡b˜k™åPrxfW·ƒ2^úùÓaçÑj=ãÎu;%fÌžï-©—‚xÌ”ÍάùnZýë¼ï>ýx¾ˆFa`ŠgÁ„#¥rÖ+wÐÈ´:¿›óÑ"«b—…ªü—oïXe+Õp—?çÍ_'hׯÂñwñãºÊš$sûàŸ`Ð"¼ðËW®×‚éŠ÷Â_[\E;kírç¼q~ÙµEÛß¼fåÜ…s>üÉ‹6Ï×Öj÷Ü~ý·0;3ãÌ7ÔcÀ¾LX0b¾ ÆËß[ÇéJ""ÞÓÒÈ[Ì€]Ù "Nm)!5á¹ <®(¦_=ÿ£÷çtö¾Bmè7ÿó?_&ŸØ¯§~úÀc”Aýz½N˜3BnÆlHŸNEt(‚ñŠk ]D#_CÝ)ñ:_^²Ã³y•Ûå¬XöÝüWÿüÃúºXÖàÊøŠJP~ZÉsƒåŽ´x) BÄb¬/,<ý‚a×¢ü“BQþœpÔp”?ç%NÔaN M§ãí¤nžÃA[ i„£Ü™žº8ðs8Úþòï¿ý`ÕOKyÉ¡·­¹oÍv/˜¡¦Yèg†Ë?–DÄ·-Ö€9NOü8MñïØOkº DÞâ¥]-fÀ b)z×1S¦¤¾:~|KÏ<…È…'Ô±Œeü¶á×Ûϼdø`0CÁhQ¡õÄØh=%9^‰±XZ®>"QщàÖpuŸÅû@®¢!Š0uŸÅû@¯¼]#ïÅ›V€Nƒñn]÷û?}ýÅ6[•xŽ<1gò3ãË ˜ñŽ×h¹ƒ8c!„‰ ›VþöÜ™—\vÊÿìÖ*£n™×}°ºå]÷9Ðø¤ÿÖA n9×}&Õºe]÷9˜89LÃmíÒŸ¾ž·m_´s¾†»Ý È ‘/ãÏÆ‡üóí·Dz-ˆ.° ÌëÜ•®®¤+; Yßw‹0–Õ|åÒ\¹Z•v9¢ÿoý$z#:b¡såñî´‚õeêÂ9Ì_8‡õ8¤WŸÇˆKHLŽ‹K4›°%Êæ°Îλ¡Wa·ñí×oƘ›D¸â +O†` y˜€¯üc©XH¾¯ÛÌ5Yî Ê0]¡cÑ¿ûô£Eø-é{ÂàG 8®_\bbÊ‘ZþmVZ2á6GÀ·íïØ´~ÓæßWíQ‘ØîÃÎ [»p˜×aʃjïkiZ!ÑGfOÚ…Q¢5ù9Ù—µ” „gšX0`Í7¿üRk~8¬Œð3L“¸úŽ/ˆ¼ˆ+>wx'¨¨äÜE#dM—./HgËÿxa:KÅÌ„y¬F„Çm›;Yîm^’€v‚€h·¡Ý·È“9*;ç LÍ=qfÎÄ^‡ßw×b ØHVW^AÏdf圗—›½(8R¼¡¸R &šÀg¬Ý1ã`ªï3öÇ¿#‰ù"»†c¼Äø‡À†,3ZÖ|ÛÅŽ0 S–;@N"à'¥Ýû™ÝÈðÆ<;*þ ¬æáPPÜÃÒãå½®Ýc11ò¥QÖ¼³ò­™lúl‰Œ–Ç1Sõí ù=kÁlv6ÆqL·Þ0|ßQãÁN0_ZX0al„öËp»Øt²“åîÁAþ—4†@Gl÷å5¢Þƒ·ÅiΗ0—ngws—CA\H°Õz»mTö¤‘ h9 ßÄä…0Ù@T”`éäΘ ;ŽK<3ƒa °¯Ìšò‘¦ \˜ùúj¿¾¯XâãÆx2Ž‘ìD93"B°åÉ%'i ¢]t¤v.ì‚JéŽwqîèçÔx²Ècí¼Ã?©ª) _øÚjEÛfÐÊro3èe‚À‘ØîÃ=¸F¬¶Ýyc)^狎t7xÖ <É8Tc¾uó–^zÌÄ)§â|ÕK‘x0àîH”÷Ñ ™ãŽY8°æ°äI¤×–WŒµ{3ÞnsØ©åÞ.ò{ÇFàHo÷­UºèLËk¶VÞÁL½:iüŠÖJKÆ+H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D Ü“•ÓgtVÎÍáH«=¦quò€‘å\×i—4K$º˜ë¾hËçWöìGä¬izw…´(]QJEÙš@ÉËûtCyKi••s«®è#8EWªqÙ¥*úÜ9´4îæÂÊÎyO!å×¼œ¬§óëTÔ“‘ÿgñýíÆüÉïÝnýlE×µ%ϾÓÇ¥;Or“ÞSÑ)µ©e»-Æ’°üÛKJÚ’¶p¥™=éΘXõ³²²ö‡+Íö’ŽÕºÐ\àZr»–`™•?n\i{¡[Ò~Úœ/¿¶O†î´Ý«ht³ævôè|£ëÄåTìZ~y×…`Èù§6úcÅjÕ„¿€®*‡³—¢¨ÏêºÞºHÓ•1#³'=?3g⸀â Ð3Ò£¨Ê΃E”÷LkÎiäÒ¯ÍË™8!¢ 1“ÿ˜®Ø*ïÖH¿Åî¶÷IõÔ¨¥DUŽR-7OY‚úõZº~Ò{™™+œÂ_ ×QÙ“ú"Þ9 E7.ûpóKwsÏ[­·Û‰+P¿c­/&Ù]¥ÌõÇé9®k,¼®+¯VWéð]2à: UÄ/v—P¾bÓãS@ xÔÄÜ»H×ÎÏÏ™xKhåcD@m«<éÖ Ì˯èò˜î°m!MŸFëe¾ ÐdÆ÷‹Q1?X±üÕ_W\ÑõÌüøùJÙ“7)ë­üÜì©ù9Ù“¢ä’®h1›~pº™š¦}3ÖúÔqÌèÁ“ ( ‡Æu6˜k¾j2ÝÒ…Îð "?¤EÚQ`xÔeµÛ¦…p0ûnëÓGO·>RÀ~Á|gƒùÚ³z*†ætÒÓÎ*;†‹Í¦Ô).ýÐQ0ŠÚã-ñºÉ¤Ù©*Ew;ÏcrÌ«Öñ[­V]Ýã̽i¸Ô=ÿˆp5Ì]»N'åC<ù½n²ay˜c6dw;¯sΓŽÏÏÉz†ý€†ËÀóÍóÿþÜÜ®ÕÕÚ·Ð ßcoæ[^Y¼©â›n ÷…þ84è´ £´û [×/ U¨c¿â½¡7%|ÝmÍ=ÉåÔy.Á)î*Qf_Å›ãÿñ¼õ̃˜‡zý æ=Ìi°öµÇµä'™¬±i1ßVU­R•¨áy9uôc¹—¢Þq½îadƒªªãu Å)T¸¦„7öã¯PåñG_(š~à»A5…ôÙªÙüâ ëøí;#.ð“Q@Áôùûžx)Ñæ*ìi¤¼a±ZUéM§ÀaËÌœ,£}ðÊÿCûüöƒF¤ø| ½MÏË™ðaSB7ûà¹çbÕoJ¢ù ½Âõ Êúrƒ\‡_àïÂAiP2³s™©š‡ 0jĈnñM\Í%§ õ_[ mº‚´x-¯°2àå£Nµ8÷îžL[Â|½EIñ‰î²åÔϼí}ÙÌ *wâkî`r+É.ÒÝe£y>ßúƒÞ Š> ºKr·—î»ÏÎïX“uiîóÌuȫ֬U5þîS=ÇáÔÆãùvü^Çı±¸ üÏ<_Zbÿ ºà«kü×¾èúý¤¨/Ìœ4~Y͇gÀp» ±þMxDþ˜QMã΀ßᛌ*Ó—Ñã]_Ťþ9ïɬÏE¸¯Šn1Å›ÿúêøñÅüôM*-µÝ¯iÎ!x,`s:º ÐÑõ]fVNަëßó˜$4±-ðã€_;wä\:¬õºËŦ֭{ÜSX.E§6Gswž_ýÝ:-¥ÒYyšÉ¢Ü…g(êÈ“J3gNÊÔËèœâ!d£S|ÎÛ9Õ)+TWjóí‡3s³[*€‰h½Wî0'ÏTYð8Åû²7ˆoln¾º%k”&òpl÷ZŸêîp:O¶ãlLømr”Ëéþõæ 3™ÇºÉÑu~\•«âKÐqÖ¨‰“»uºxð^í‡KÑúkæô&w™ŠRêgV´(&r´5÷D·SÿܤОT¤Vhëš;Åf¿M ožÌ6/T ÐÁßý/«&u8h!½àvºóñÝ–ïÎÊéåriKÁì¦(±t³ÛAgêný•ÑM®œñä„™<ñüŸ‘ß=:©wÇ›c)Iº½òPõݨc7!£}.XµuUé Vw+G¬Ö ]¯“ô -ºÆí¹¡{ÂF“ÛÍX Ð+œΕef=êLJÐpXá¸.ÌWÑ¥$F]ê­ßÂCÍuº5ë7¾…ÉÖ:¾äcGC ¬ Xß»' \¤ã·õáGš±òÚßùxÏîú_ë¿A£=ÅíÒ?˜a­oWMÊ=¯>‘5Z”gå#Á|ù¥[wŸóñéOX ìõ‡Ö÷5Ò¿”_ęޮtU<;Æšsæ«ÖìŸÊÀ|Ñ’ ó&e‹ ^Þ0|3vòäζJw*ðwµ>² Úxõ€õ¹´ ge0Å+ÀÏã—`Ðhçè‚aºõ†SèPwõìMÓû¶¡›½‚ùòÇg~¸3³ uMIäg`1}Eµær½€ôødÝÌä@ãé‡Ç-ÆË:ÿî+·gÌj¦¢iÁD¹¯TÒçáÝ{ЈÇìuM¹ï›aÍ^3Êúl'ÝYÝ•È4ß7h_CÚºpí–>x_c‰¨]>þ5hå³g%4ßBóñÒ²Û©ù¦{Ãe-‹¥nhýé§fF}ýèHG£›ê… âG[s4X zÙ]Î{QŸ¶D›“½Â&:÷z¢‡Pd» éÝ[ߺ{³'m°ë´uôÄ)—a½©WÑ$B½Â¤[¿5à=¶Ô°àèK‡æÔyf½ÆüáȉSvîúÂ^›ÞPNžºÓ„På›&â]‚¡l 7rbî+nóij‹èAÔÕ5h³95ï>‚`z!,A£¡ 3£.M0W¹+„°Aâ+ÍEOs»šf}ðF*„cíM¤õ·}Úç#Ì·NU¹Ìý+¹Ù;üºk’§mByH< M.“ç t %X†=ýè£-^É!Ò׎xHxÜ/Wöê¦÷h¨Sƒ©)Áépæú¯B09e÷Cã=Ãu3žÌþ}Öa§*´çðLªðà¨ë6ú52³_OCW>r9éV~†uîÿÀÕÞ¨Æøæ0Yøªš4˜;4T›x²“ËÐ@Æ'0{¿ìóû[”bž&üẟ¥wŸçoAg­´êyR”hÀPí“ÎË`¼ÏC ¹DS£WÔóøÅ—Èì…l~†¹óZäù£?>v1òb.pO9 H ƒÐó%{#èT0äK¬I{nÒíüÆm`iÜ×)áï`!?áùxÌý«xªëÔ¼ÔdMÑ=H¨"åxtŠrëN6 ûí\.˜Nô/0…QÀíkJ4Ÿù’õ¾2o _Qç¾ñúÁÍË9·!üf0¨ÁùYYûP6ó\Ó…ugÊ”Tx¹RµÐë¾a¼÷Š~¬BêwÞgÜ$¤F¯ñ}®#¼Í7m½Fx«ñÞ¨På ™µã'eÚy’§AH3•Ó?#çàûaÁÔÇ3ßΰf­A=ÝW¡Uu}ƈM_Až ›­5(#}ªç|ë+tó³p,t¿Á♯˜KñŽï³¸Gyß‹8ïV-¦ëår$мú"àíð|_¶Æ½¦9`ž…9©ç_ûsÏ '}¶»ã UR˜ô?Œˆöe|\¾uÂz/LZ˜EMÞg,3zccBãxÚAúy˜º}›ðë{}éñGöÁ,U­»”óñ~¹øÓ`_4nÃñ¸,LÜ%èhºä=™´éRÄÝÜØZ0‰Nf• …9¬^ÌÃ;CxßâÒ⿯8T™¸Ï•s+:8SóøÅ#F(´ëOݤý ¢ÍEº fg8c 2{Òfh»ãñGÆÒ/FGhwf¤ƒQ4í€Çš¼IƆ?¡—üw¦uòoyÖ k›åÿW7•ÞRüá¿OÔÓáSó£ŽåøÝŸPsêàšIXy¯'|AÐrA[¬%à1y‚ÑÞ½®»)¦×G÷U-½ß7ÁB¶!§ ¼µ*ß/gõìé˜h $5oYx#ð&üÕÔãç!¼ñ¸µáê ¶â}×ZéÕû®ðÚkLHTÕ⧇qc®Ÿ;ÌSS.*Ч€Ñ*]Mã—îÓ&Ç`ß7!@ŽÝëʹoáXèFd݇EökÒM»ùZÏ)tÞ-†ùü9\=L¾ž'ùâHF l mõæVÚì"w$\ÑCî.tÌšù+7.$§öô¨‰S³¢Mq;ì®ò;!ÝžÍá\‘`Þ¾c-AO÷2Êüé¹Ù»Ä7ß+kÅ0¾~ç¨Çr¡–({ ‡£#ȬåOWž‚Ðr?Æ…6›Ìi¸¨:F×ì§C•ZËf2_¿-½ïaÎ^°×™»ÜéÒÿ‰YÊ“º™³ís=ÓWQœ'Ϙ”5‹ã3] c⨩S“…D?íÁ«aî[Œ™cãà哚Y´èÀÔ1ÉëŸtŒg™ï¥OQ^D<aѽWú\ÚsðÒ´1ˆ÷E1kÚë·‰›™Of\†êNí£»­¯œÞ £j"¦:ŸŒÁ ÆRçS(Ýäâv0!q5ªÖÿ ]åûÇíNÇ@ÍDëø}†zÎç{µ%zöÖÁ#¿X·ìë¿ÎýXÎ÷}‡ÙûGA˜?õ8¿„7ßZxâY‡´/À¼‡Zš¾o´À–{uJ¼WåK´±'4Ò0³\5ê*„…{ÝKc œ¹7¡†'v·ôXÄþýºEÜ ^ÍêØ8‹^ŒõÒ¿¢üó²€4§|Ù¡€ ×únå•ݳêÕš)¡Qk­øyâD´%éj0§®¹–Úe`žbìòF_ÍÁ07ÃìŒ|8Ìv¯7EOJ’e:‘•`>_Ó®%ÝsL*=à3&1£R}à4§ëPÆO 1Î:íšÒ|ý…âÞ`œqÊ• ½ˈ¾ÞãÊ©ÖtÇ·®ñ›LŸ¢ãÝJÎÐνÂ*Ñ—è²`<ýCá·›9c>:ÁîèÔ~Ìš¿alïeÞPË¡¦ÑÎÂR䟙û;УµâñŽÊ˳оòäZ³¦ÅÇ ®Ü©•RqO i*8k1z”ÛÎkr›òŠož<g$Å›Šx²VÝ8yÜ0QU]-TÂK‰R …Ö\7p?çæ™/Äèß‚VMW!Û„‘Z\CsDº˜,u²[sýb±¤%6¦Ùƒ¹Ý†en`‚ß "œ¸ÂºÂV™i`6üç,5™ÍwðR1á‡÷Õv8Ý`y˜…8Øzd8Ïì}[E”j9ñ•Iã S93_|œŠŸL͆ü„Âñ<s)–pÀQ¹¹z•þ*:”ᘠ€ÉË0]c¾ÅÌœl`ÊË&} t9&%>ÎϹ†ü1sÅÌëŸfæNôöW™å^„ážW@Ǽs4ÇR6ƒ9âûŘý"0èZ>†u yù a޹xÈ€ 1#yTöä«uÝý Túû°ËÛK‚¶š]Áþÿ"!3òU¤’é~¬Jø˜ý4„•ï{Åb:^ Y±@XÆÆÆ)'7µ}'¬:‚pô/ÌOÉtÈkÇEÀ[¡[3‹Ø4ãï¼n·5Ó@FôS?ÛojªckÍôeÜíÜ™¦¿A#ÿOkçD‰Iè4áÖòV¤x /Ááa‚–æ ffåž'žéúÊãïoª5'¼µ”Žºáy8$ʦÄw¢344‘ׯ¾Õ«±çP Ý¥#ßy„…/ûs·G°ã™­ë”½¢NËnÿÝÖ¥LÆÞ˜’oºCÓµ&‡B‘8Šíþ@fÕ¾PÄ%ãHÚ/Þñ‘ÖÌfe–µfüž¸›d¾­rGNKÇÂPOa/M K:¹¬dÞ$°0`LƒØÒê`)aH£Õ3!hK0©®Õë)LNþ¿ýõÆÕÛ2ß2m‰€D m 6«qË0I «ZÕýܪ±ËÈ;<iîÁkPO[—9z6éðXÊ J$Í#|òìí%ØX}qóä´À‡BŸ¶ ´ * >¿3P±¤ª¢ÊzÚŠðʨ%í °0`šE~+³[Éè9¯ã—Q!¨º©Õê)ÌÏ¥&5µU—ã!Å$³)脟zêñ³°°Ý؉'ÔÈaÓ‹ÉLÀâã×xYE¨éñµÆgº¾ÆúÕZ#'¨ÿÿ|ô΃rCþÖWÆ)h‡„+Öï\ØØ§•„v,k:5nh@ZË^×’åX¨Ï'ÞD¼3v3Âq€OhG"P±Œtfªl!¾µÝ¢zcg³ÖwVë,Þ|£ÕoTá»ÓTKä3…‹ƒ×47ôÏõ§5ôÝ÷]cqøú öži`Zš Ïe3Êšל?þÎØ6ç—wó']Ò“~Â@ص@ìŠõ(vÅâ]uBá LѦ3Oùxß#ã­ô°$ïQœ€ÝfÎô7\[ùã]ˆxãì 4£­h8Ó<Ót'vZ É—ШK³åœ w8Ö6‡%ö ~ Å‚ºù ¯_ìµDU•§TͲ§ìðñ™Çû~÷¼$¶]m±ô~Ùúè^ñÛ(NÄv°™ØõxßÌÞ×fÕ4þÕIãW¿âÊ»mÙóx†}Œû{ßã8M{•û<óá*)˜=ž“7)Ë»åÝÖ)ýœN÷ŠJó°õh–רu<»UÝ‹¦J´Te΋~ý×ìxõ*± ±½} €Íãì^EŸgÞ1ʆp6E5?7iÜʺé4G]üÌÛgº5ý5‹E9¯î¡$ }Ã^î—cÇ­(8@BÅ\—µ=ÌçÜÖЦ ¼u&’¸?ø¥ß•^]îòÝÿ|ÌÄ)§â¦)Á<\¯@¥Óéìq—˜Í¦±Ó­ã7ãÙëFçäôpÛô58†ìî¼Üìw½äM»AÀnJOû¼à©åîš ðø¦] ˜•Ka¾FzºóvìÍúŽx»ëž‰SŠíöXŠÄ&óS0 çl4öž¨üNt„kÑØ¯â†‡MëïD냆u/™•±ˆ#ïtt4±ÈÏÝ8edws¿y{]›žCg‡½£É¦ªô0yÈi7OÞã~Íœ˜Ë@ 㽘1`þ¶Ô›”ùXÎØ#‡ŸS:Ê1f eòYÙY97`Jy:ŸxÐ8ë¢ÁÇ>$¶Õ3ò(ÿµ #Ý¯çæ©`.ú3ÀPéÐyšÈtù8?˜/§`V,ïã`‘ù¨O‰§ÆZŸêis9ŽÏP|µ_ÛvtS”èŠã6Ô¡ù§óoð÷t-¿Š^UZw¯õ¾cÆ¥8zOùm¢–³Wjá‡ÏQGŸá¨À,VŒ¶>óõ ëÃFNœ|¾Ëåz ᾯ¨é‡mfKêY¼í&3ͦý€Ý§>–šª}³jãë*E_œ—óȦÑå^ŠÃX½¸‹¥Gáw5Òøe5M Áz"nÿÂÏÂq»n,áG\GMÌÉÅF,'÷Õâ¸6öÍ¢*›ÜjÔ¥œwö aâ“}Î¥×áö}–¯h³çÇ¡ÝÍç fæ<2;ç5e÷ÁÛñÉk½së®›±OúÛJjj•©¢höŒÜ wqXÄyË¥±@†>æ°sWÓ+`Îß~#ïÚa3AûsÚgû'˜H¹%Á€÷Ϭùž6g½†ÒTd¼¥œFÊ•Š©Ó§èXßuêÎ[…ÿoWmà#òzAbj2GŸ7ÝlN»™MFhôSR-énÎ>]ñzìiûB¾5ë0ä8„¹e<Üî×Ý<þÓ½Î7AÃ ©ŠézM£¸h*îhq”áÛqæøÁ–xõ<ÿv wº8†p.>½Ÿ d3_£Ãþ¼&‹r Ybc({ð7«6\(ò!¯¡C +“OsRG .”+­VtËYãF¹üS~5çÑ W„SµÎiÚ]®ëQççX­#â]CW6Yêý%ZÑÇ ³¿¹žœD…úrï5]ï[ƹÊe¤j‰®g€¦zul,½ÅAŒƒ6túBw;®æg“ɲN5Ç ÃqXøÙ!÷k±çõŒìì=wÜê¾²ÔoWo>ml'3_ŽgÆ“Y_A¸ÈÃ2|hÂÌæËßЂlh›É|ï뚊Ã×ß[Èü,M×âÖåï·W¬6 æ[Ɔs¢êÑÁ˜¾w…f¬êôúëE:5¦üëSÌ,ÖŠgäLøL|ƒ¢%–Ú â™¯#'æÞ~ãW(;}ßËûö…@›0`†è”Ï÷¿SE²¶‡Šéö6H{{UE½÷Ô¸óÎ Xóåv]ŽÓh~Ì·fV©ñæÑÐGøŒŸ€ñ©Ë™ JÑâ û„÷Ш²ÙÎ84@¡ß°¯uA/Vtwó¹ã¸#âïxÎ町œ[¯lEG¸åÛ•Ïi.­ö-Ÿ—k±Ø÷2h0CE¾W½Ê5 ÏŸj1¦]ãrBbžƒÎç*_?ò>t@þ0–bG=}ŒÑégÌÁ0í—zÂi2ëý ãõ†“þÞG¹Žð¾ ‡%è†Iöð»úw8Ûö"ÔÕß^Ι¸ m¥`äÄ)lÂô:ÔEÔOõ·æžQÓá{¿5t“?iÜêh‹Ù>ìÀ45¥Õ:P@Qv¢Môb_Üvø‡ƒj…;CÓw|z[8MѺƒµÖf0оÛFÕ=9c&u´¡¥Ö¤cÏ„TËo@óO*÷Î-YS•îJÏ´‹çÙ&¸œÅSÐy*+µWC08ñW5)Ôºðâ í2*wÍ8~`ToÜ«V ùÐ"jöm¾sZ^\vÙnD9] &q"ͨ‰Ø…:ºñ?”ÛgÑɳ± üTºf³ú>q_&ù÷šÚ[s¹Ž†ç퀋cÖ·)d2Æa¥yæÔ[á×0ÑrÔs~΄OÁ°îÜãÎWÓù} îž'¦Ç#Ÿvß0:+1ÓÛ÷]0÷<æl?P–gRô8¼ªéI`<•¾q¡mT’Û£a˜7vÆ9H/žÑ¾Œ&24e_ÿÍÅáë7è{·ýXÉî¾Ün‹ORkÑlÄ«S’jҽJÝiKiêš~3ÆÒßÏâ:Æš;ØíÒ®U,ŠwŽÈ#ÅüàLhÊÛ¯åµ"Ц XàU£Í>…gþ‘~ýõ¦ÝôcT¯¸óuû|º‹‰#¶*÷©h$çD'r`‡¢\Án¿è®Nø~–[„ŽäròvW{Z%›ƒ0þóOMÇØ’¢}e¶\ôÒ„G 9¼ápF°¸å+w&E ÒRß÷|ßX<`¾wC¯ÐqtÛìã½³ ÎÊu¼¡ësplZ³ãxu‚ÊÇ"Pȧ!þѬYžzúÀõ»l¬9qé{Üîškp>6Lrw+ç<ÔÓÁŠ>òòŠÕ³u–]M𠘓Á¢ÁS %>X×tmÑ•û¤ÿp·õéN‡Ñг¡wl*FýD:ì0 (QW´âÃo‚»³(} Ìôó¹1¨Ê!˜ÔëÌŒÖu“n¤Åæjø»€ýbŒõ&)óp{g‘.g?|®*4¬KEÜ耷¡[?Z<£¥}ŽçkîÉÊéÃïØ”ÈãÂ⻼†#>p³¦ëa¾¡NW}œq8:Ûá(ófÍÏNWé Úfaü÷h‚—G)ú00³e{´WÔ¥ì•Üì*©/:]ŽçÐg×Òfëúmèaöò,dñ æç¾èHv‰ç`®0ß>zm˜Ýý´7¼nÞŽ6yŒx溎öÚG‰V™ñÖr–¬÷Ñ.z0Óªõ!€8j… âá¥ûî³ÃñذÿX78,$;  õï1QíÐk`¶ÏµïϰO,ô={›û$—Ó1ø™aÍZ#ÂaáZÔ‡QÀ«Œˆc$,{¯CØšáSÞ´É€[ ]¿MUMoùÆÍ&®ÏªÝ•7*NCªîïrÿ†Y“[p¨ù2hÃ<›v(ëÉ©- ]…ëFeçlæ±ßx|ïÕS6` 9 wB‹YåtÚ°—¶vk*LÖšå!Ľ”ªõ!(ä‹8³ę́¾ôÌÇïÜ9¿ƒFô2Ôÿ—½ÎÜJ…³ÖÌOV^Û/Ø)f:WtÌÔÌeIs9ÁäÂÛ`ÂÌÂ%_1_â5tØ·5VïÕé9èìCÐñ7;!«nx0Áas¿ƒßóø'⮚£çÖõçûÌ qØüX!˜Öú–•3\Ïî–¬Lß÷5KŠRFaœ—ßh¹WÁ´æÕñã‹™ÑrœÂÿ>מfëB`k†’.ãoMÅÁßѦ®à¹çbù>ç+ˆmœÜ7¡ý Ó—•L¢ÿÕ+ŒðSi6§©“ûfD¬EŽé©rUÎÎÏúNÆâP†C‰H?ŒçÃq&iŽñ„–ÿÛa‚`¨°'5”*îíl¾SÝ5õŒ—s²·±?žT3Œ§bÓŽ?a\ç;ÌŽæ³#–+­Ü¸šêŒ¼¬,îtju<Ü9àÝÕlVÚKE ù?TĘå³Å3#Ë0Wa3¢1û”ªqùÖq;q{Ê=“'§¿2aB¿æå ùÙ1O<ÛYËH*΃™¼Æ»¼t¦çfï‚xÌm™1ùÏ7_ŠÞõÉ;£kê—Uéœ7i¾ޢº$Ì…Y÷•ûss»V×ÌᡘmÇ`yÝ÷ wøÆå{eNžjw•½ú–“Ó–ŠY¿Vž/áë§î½²»øhôä?>êeL‚ô~æÍÎÊ)Ð~ ÷ºrö _5ßÔ¨çObº•4×,¼/‡Ô˜œæÔç@ ~4ì£JÛfRM×0V£³r.A“›Œˆú‘53ºŠ¢ªw+‹mçÀßÏ5 tq*ô8LòC‘Þ¾½Î¥‰˜5ÿ³9AÍâHœ•š—Ž“Æÿ fÿÑ^׆UÈ æ±ÐÚèÎI¯³¦[éª8­»ù¬ëEÂUÅÕ·€-Ÿ ³õø}^¼‡îrÏê ïyÓÎ@Ý•Ž%W4ž•f‹é*^ìÎ&]¥Ò•qß ˜À@ÊÜŽYÊ/eæç›õ]Îhï’¹KžMí/zhHÏ…"Ó“þ$­ï\WýHe=¡„¡ýÞ ÜfÎÄq |nö·IcÉ“O6IEMIÚ¯ß!ßuðü~ôÔäaØ¢¡8|[¯ÇoxìW«¨H²Þ†Ö¾,ô">yíxH\S¦c&NêÆØ-ìAÝ¡Äï§”Ëë%Õ¦hrUO€’|"Æf`¢¿˜LÊûµÆeü¨lª E<~$%½H"l*s•®š­±èD}’ ‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" h—ü?Ïøù†| öõIEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-overview.graffle0000664000175000017500000001147513553660046027044 0ustar zuulzuul00000000000000‹í]mSÛȲþ¼ü Ý|= æý%'›S¼d7Ù%À’ÜMQuK؃щ¼’ Énå¿ßÉøE’m §*6ÒÌHM?OwOOÏ«ÿ|»ˆ¼K“faÿüûè…gâVÒãÎÏ/>ÿ²¡^üçõÚ«ÿÙ=Ø9þóð×Â,÷?nï½Ûñ^llnnu»‘ÙÜÜ=Þõ÷Þ{ÐÆææ›ýÞ‹ó<ï¾Üܼººò[Êo%¶`¶y˜&]“æß÷ ± ¨à·óö ¸LÙúØíÀÑvØÊ_¯ýôê«ùþz«•‡—f/ønÒwqÛ|{µiÂÉ0ÎMǤ¯Ñ«ÍëŸ×UàÚa+È¡ÉOe˃JAšöÇO¯²<…G~ wè'qØI“^×?€_¿¦ÁÙYdÄ«Í~‘‘ÒXhŸ ¸}"$&j¤È«Íë¦Ë[èåÉVû¿½,\:O{fóúüvÐúj/·árÝó°5(Öö~1["»>7¸þAëú±îýÃ9Üä\HÄ™dгuO*â+¥%BRaÂü{ÛØNdõv’(ÞP¥Ê»Ýaùë'£}_;Ê¿GfXòúqÊ“Ðdòuxväty~7 ®†·5¼±ýƒÑÛÎôÒõ¯á²{3ó%I.f,;A|diØ ‡Ã亟‹n}ËE$ê]Ä[Q؉kíãzûEé£nЂ&åSD¯)Œ±âÇuQøÃÚÝ 7Õ[!³ Œ70ò°|IÉKƽ!øÔîÎ6’¤Õúïƒ<÷~‡—\iP­´fÝ(ø~Ô ¢Ú•A&áãµ.¼Ÿ½Áïj¿„‘9þÞ­U@ªe‹¶›´z&Ϋ:èHRëÉþÀÌ,†4ôøH«HΈè0 OA¨ÀZp,5czÝØúTqEÆHQ­X!\šhLç kMȺG”O™`\p¦µÀãÒÕ$^Cù:º¦IÀú˜Äù»ø,™,0˜†¯vü|Yàjääð Ž]jDzF®Û t]q¶qd.ÂÓ$jWš)¤=üÛŒV-†³ 뱋UQdø²§c@R}tb“vãàeÿ4YDâFHªuêYEÓú´Úéc%Ê"§£§û½€|©~1¢˜2Ìa¼Ð‘~¹®Ùi¬©4B˜PC Æ zCÍ´±¦Æv Ê¢Rb-Çj޽úê0È΃vr5­'¶M àrÞ4Æþ|stÃ(«bÿ;šì"—ÈF C“`e}Gs"`D ¡~TzoP[á‹€…^SfªD>’JR[A¸–rbíʵ‡ÚDúœkÄ•Lh¦ÈíèëfØ®ºs&ùsî³JrΑB M63hƒ2†9ÓC*†ž´ƒ—Õrl¯Žé  m¥a+M“Fµ w^YcÏt‚Ö÷±âgA”.Ça!‚Q7Í4ñ¸†ö Œæ»“Ïa»BÕÅ£’Y™.8?È¥Á"J=â>‘"ÖXr³î$Å}D5š+ЉVŠZ­^(8Š™VpBQ"^÷ñ© `»€Jòî´ú%hõä)jõJ°Q]s­^skmk’Ùµz¥©ÉœVï´z§ÕÏíNÚê˜8Ÿ¤Î{û0 ZÆN‹4 Ÿk»SqÁˆ&T6*”“ˆªŒƒÎÊÜs?-¨zªDŵB\rç$*;)(É`½¨9‰ +"½5¢Ê•#*GT‹¸ŸV‚¬°&¾´Þ ãŠ)ɺG5óæ`GK°£…âÅ,½v¢|‰læ•Ñ6AÔËÒÑ(t\ý`²‚ –æk»z³R‘š›Š$k ¢[2È-ü?ê[Z¤íùk,zh²à< ¾ÝÖÚæ,Nˆúgÿ»>4 ŠÎ2‚J :L“˰mRÔ «$ýzb X˜ÚêÀÅ:An¦ƒÓgíºLŽ¿»E Ir_Ú¸ Ä$VK¶î1D|޵½CS¦0’°ôѨZ-”UµÇxÝ£}/.ü ÀÒÛS¥H"qA%ã6f¤Ò¢ .}ƒKßð$Ò7ÔfQ—¾ÁAãò¡/ àæ ÑA£ƒÆh¬N÷9h¼4.«?§B#ZVʰYÒvÝX4:h|´I¿¨ƒÆeBã’´ðiÐ(ô­ jEÓHÛlгçC\ÄwÐè ñ±jÕ¥0oKêϩШ–åktÝÓp[HT€¹Š©ªI|;`uÐè ñ‘Bcm²÷þRÅ6Mý?™Ø¶•˜úŸ;æ „í)Nýc†‹”È3‚Œ>†äÌ¥°&rb27Éï&ù8¸Œ²þ "ˈ®np^fpåÅvJc Ã…Ñè?´ùm8 )a3–ÛøR›¢ 1_)¬ 6‡w'{ ÑÓ¥`Î>]ÊãáÓ…Š{¦²ù3øQ岞»¬çŽà\Û ð˜½oýåA÷˜Pö¹m€TK >›«I,ÓÕtSôÅÈôf%½`Ö /}ë|âRÀ%T?ÏØÒbÃ…[@IÄÈ|Æ›‚h†´ÔsYqB ¸(åjJçgZêì$ÓοL¨«©·³A¨#JøŠj z¤ÍUŽŠÍß(ñA!Ý’IêÔ9¨sPW l! A|(­NúÖ_°¦Ȧ†Ú˜YWR„ hƒP…Ô9¨sPç ®X¡‚:ŽH««…ŒÙ½gªQ·êÔ9¨»Å4V=)¾”ØFá€å$µÐ.Pâ>%º«‰ZnnÉÍ-¹à‰eO8{¦°8µÔÕ½NÁ~òy䜎½H02qî„U@»…=§MÛÄÕüÚ9w³w'þPîŽ};ëʤ…5!t}/iÌú»>ºÍ¤oŠŠ™ÛLÚm&½›I¿â SDÖ¶“Æ TÛ›„=ȆҴARQZl¨QßO·Ÿt[ñ~Òx]ÖsØ8Ú­j_úªv¶p.$бo·„!ØR¨zÚ/|¹Js¤Áìr«ÚtÆsèVGTÏçŒÌS –’”r>g¼ÀŠ ¥œ[Ô¾\㊺,q-ÃŒŒ.’CÆç‡Œ·ò$Õ×ROZ_ýtv§r뫯Gq/º] Œ[_½!0[‡ï–˜7¤‘×êÖ²ãµ'ÈkDjÇkŽ×¯9^[^{¿G¼Ã¨×Ùã¥òö•ÒRP$lr+Åë›.ò"@(8Š¥l-¬} ;Œ%Â*¬„ã·GÄo\¹ÝÝß¿­¿í—1(Ðs'kÃД{É5™Y?è„I GÁ4ÃέùlÜšÄm~ìèÑÑ££Ç•0ÿL–'ž¬m÷²%ÚMW_dîîiœpçΜ#¸ ¸£?öNÖvƒ<8 2ã¯ßÏš±©QtᵟœûÚú…‘F0Œ@·(óÞq„²:p†·ÈÊQ·öÓ­ý|”ËܹrÇ+u GK«o Kj—1À; uØ—”Ò À1¦Ò9¤sáÆDº… Ñ2aqöWkÛmqªT¨6€€x°-[k®ßu¯î".×èWÃeA AÂmâ%´Ö6~ˆ®Àý;ÏþÁÈdÿx¢ûÇÝ}j“yܪ«¾4Ïya_ö–‡v¾že„•®Wo7M¢È¤Þ~Ò6tC)ó™Õÿ$åAJnSOiàA"D%U–Z´M¡É\RªÛÑÕÂ%¥rI©V")Õaš\†mÀ¡~Jª‡I?…¥oWª€õŠå˜Ëzþ)&\þ©*ŒðÇ› ³ %'©4ÓÔ(0uÀ¤ž®G7«Q6Å„dÃK*—–j¾´TSDQßæŠUL•ªAY‚!,«\×$©/­¼‚¦V)׈¹¸¦G¾®Ým\ÓD‹’^{uíÉEA9ãÛßO<ªØ½@ÊE?Ý5Ä]Ï}Q¥ÞHoÜõ#)ÌjJŠË|ï’=Õ ¬)ÛR,²˜Êr ä^S°Æ™_ÌK°ßSšÂq¤§´óÞÒæ'Ô¹ïÑuø´œÓ¦&()U"¾Þ7o; Ûãm ݃ýÕÂÙÉ< &<.Mzš«;ô.>·p¤ÉÙ$§nW¼°–Eö–6’œ(Š@%lHõ*&jY $Š}VABŸ‰ßéS‹Ä“ã¥ÆøTvV“B|ª™“¹]Ê ( &мA!“BÈçâS.ÄÇ…ø8/£ ñyðŸ‹n/7E|Ov'>Cý¨håWÐÙG1zð2gºA îap…>=új|îm8túó8ÿMÒÆêþ®Ü`9i“\e¶Ö°x3”¥ã ›'3”>}ñT=0N²½¤õÕ *6ÈHéOažÙaÂe ]è0è˜p5«+%3]@Á õܤ3ÿݘîqrÔ Fîb 2•êRðݤÃ+ôàŠ&aü&ƒ©,¹\4L¡—ñp½ø!ü9«oå( [&›í6> xc³“8ÜmÒË'ñ­8¼rS@Òžm…i+yÆ»a–WÄatKâaÙ#äaWŠ#UÊGÅm¾‰; òÖî¥äµ›±”™XyÏÄòì™|­h…%£;ëÏqƒ`ÈwÃ™Ê Üâ×IÒS¶þôïå3ž£s32g\üT¼OÚáÝö®wGÞôu£a±ôÁV/z‰¸÷/Ûsµk Èjÿôû ÏÏÍ•÷;èÈÁôOµâ~’›Ùé ÐÇ:y¨·ÖPJÙ¹q‹MÛðž¾f3”µâøˆ¿œŸ¾ýmÁù7üø˜üö×—ÏûhëÝç7[GA™_¾EW;¿N¿ý=s–ߦ{,ªLkÿ̯t ¤š/¿Û³d#`3\cÌêjºFÍ#~­ôqÍ}"9q&™âlÝS¬Ìf ‡T þ¸áì@ü`.ÜLƒt<†‘ðÁª}w9Ž“î]4?ŽåÅ‹8ˆE‘ :È´âèûÍðò!¹‹±¢ú@ÑŠY²j}õxà8ÌëH¸Ä—A6¦´”5.@%/nÃjÑ¥ª·ÕÊÃËY4½¢²U‚¸eæ¬û1ÿꙡsbÊÓÌÌ›8ÓiÝþiV¥ósƒ5>Õwzi =Qôd]goPÙß|ë1Ûÿ•]<¢Ó y¸ïìIÇj8ÈZ÷°.—¡©µîÉêJ³u0æOz•§=³9vþC/2t­¼¢Ó`äôÐå8z~Ü#8íáAꎢàô­±â]µxU…ê³ôÓC¯AGlPiãÚloÊ|1ú¡ëè¡O¾$ÉEUõyå¶Ч Þ¬÷ òòScû#ð1/Ü j`2üîF =¯×þ\7!Õneutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-flowew1.svg0000664000175000017500000006100013553660046026420 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 18:01:04 +0000Canvas 1Layer 1Linux Bridge - Self-service NetworksNetwork Traffic Flow - East/West Scenario 1Compute Node 1InstanceLinux Bridgebrq(1)(3)(2)(4)(5)VNI 101Self-service network 1VNI 101, 192.168.1.0/24Overlay network10.0.1.0/24Compute Node 2InstanceLinux Bridgebrq(11)(9)(10)(8)(7)VNI 101(6) neutron-12.1.1/doc/source/admin/figures/scenario-classic-mt-flowew1.svg0000664000175000017500000007042013553660046026067 0ustar zuulzuul00000000000000
Compute Node 1
[Not supported by viewer]
Instance 1
[Not supported by viewer]
Network Traffic Flow - East/West
Instances on different networks

[Not supported by viewer]
Macvtap
[Not supported by viewer]
VLAN Sub
Interface
[Not supported by viewer]
Project Network 1
192.168.1.0/24
[Not supported by viewer]
Network Node
Black Box
[Not supported by viewer]
VLANs
[Not supported by viewer]
VLAN Network
[Not supported by viewer]
eth0
[Not supported by viewer]
Compute Node 2
[Not supported by viewer]
Instance 2
[Not supported by viewer]
Macvtap
[Not supported by viewer]
VLAN Sub
Interface
[Not supported by viewer]
VLANs
[Not supported by viewer]
eth0
[Not supported by viewer]
Project Network 2
192.168.2.0/24
[Not supported by viewer]
neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-dvr-flowns2.png0000664000175000017500000032760513553660046025514 0ustar zuulzuul00000000000000‰PNG  IHDRš_®äzsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì`ÅþÇgöîRiR°á³aÁ‚]{}ì]AAH.x’Kñ‰’`{–÷Wôùx>»ˆ Ć vD!€ ”@ê–ù›lØ\.É%wI.ä·pÙÝÙ©Ÿùý¦ Á`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&Ðdd“ùÌ3&ÀZ€À­à SÞüÿ¢>îƒd>;_Q,Yܘ֫\×÷>ïïù ¿ï ¥ñWMágãc.™Oü½Žˆ„€7Kl‡ Ä+@à=ïz±¤¿Ò­ƒ„G{Mµ|VŽM¼Æ7â•ÈKÑÌÍû›ÒÜ[³4%¤X/=¾_æÆmlÊø¥gå\£”Õ>?˜5»)Ã1 õü樂Ҥ”ª)êÏïQ¹¹ÝŒrÙ>5ÕûÇýãÆÕeß±+E‡³#w8vG¦íQf”—"ñŸyÁ»vÌ{Ž'>MCCÝÕ–÷bÉ¢Ì0.B=¨™ÆdÄı¶øÕæg¬óDCâÚvkK}aÕÆ§>wu=§:åñIŸP;I"qëŒÀØÍ¡æîû;3:•‰ÒÎÞµý‘ŒŒMîgá®ïž6­ýö«›é³Jò33ד§pÛ'ÿž8ñÏ–.ÏÜqâk& V>¢¡Çn[ŒÀ­þiû›¢<¯ÀXt¬P"ÁŽˆ©„Ž‹aþì­RÉgS»¤Ü=cìØ’‹dœ<êá‡Ëÿ(§ôMcM¡v£èY„¼D–Jóg¦iÚ=s&g¾ë¨SØe> |i99óŠ6ÖáÄ›¥ÅÖ ¡ÔÕÛ ­+·çëŠ_Y±5 Óõš¶õZØ{Ö±[nêÔ™Bê]a6Ü1çsdâ=ï5&~»RžhLú#{ó³µQ|ÒÏÐJþaˆa™ÁMBªQ¿,Ó’ÅÔ9~;”«ø(Ë´Þ6uù=Ìr? w½­PŸj 5Bbž%;N™á¶o bK÷ç”!ü5P@žMõ&?RŸ"ävÏ×L Þhñ!ލ@º?{”©ô¯ »¿ 0þ§”Ú]øMB+þK¨J”P·íØ\´tD ç°úük+ÏKÿ(œƒŠ. @0ûXJq¯Z†&ä,t| žÇ@ )k Œ]†Jû ¼ŸÅ½ôŒ?ÜaPK*Õe#3ƒ}Ýæ|]I@Ê…”«¡Ä¼ËLN ®¼×pßbï¢Qñ«#O¤ùs²Ð35ö1mëJ‹– RlE2»ò—‡²ëU”“›Q¿ ¢úÅ,?P]¨*9ê´ƒ÷[ˆružX_Ý“–—çSR µ©zµªÆ†*ÊR¾ïþñŠŸx á/Å8ù¾è=ìЋ¤Þ¯*»|ÁZîùhe/¬­Gw˜?÷K™ƒƒ)…ÌìåÍœHËÍeTàáezáchE¾Ì0¬·Ðµ½ï}ãÇowÛik×éY¹ÇZ–y*Æš×sÌœÀÄßBŒLÙ·»˜ð«Y¡bráVVxTÝÿ²"ët¿ >ObLÚÅ<É›œIJG¿],YÍšœÚò^³F¢ŽÀ¿ºòß…ŸÔ\Ü=ª-ý-Y6@Ðÿ#?˜9"Ö]Ó§§nÛVš…c-%^§çô‡QdoèС&zÿ—cLS]ó×dö(øó 4øtE=ö}~ óËv¤Ì››Y­·´rŽË³xǃKuý ¸9£†;6`­€@•ÆÞ âÊQlãh,®æ}„AÓÄm˜PœªxгG£ ñl(í—Q¸wߺCŸ@æõóæÍóPõÙ }NnãÊ‘¤!¡þ5ŽÖ1¶¿JÎ §xгY‰+ÃñlŠøTóSª ªÝGpCÜÊœÞo^·Z+À“IѤ1·µA£ÖÝÚžÅʼ9 ¼Fßk$q¶,˸óh ®;}¯~•–5õôíUÝ´ß5¢l Ô6´lh!š»57˜5Á£yŽ…â £GwÄð@ðè*?*{1”%®t÷ŠT=¯¼P–­œ…aT¡Ïj»40~†{]KÏ‘O æU 9®Í›38%Ð`A+NÓÁÑjÖ›ßebtC¯R{t{¼Þ$K_@(ãï˜Ø0ó uÏ3 ù (õ¿‚’’ŽÉŽ×A°öβ‡K±Â›–üaÌËËθÓ0J©æA• Z¼îBÅs1æJ60Ôá;TO¢of¨‹Êð–!¼aðÿ2Ô·¤e¡ÂR²#Ü +ÿuÍ—˜ɤoØ áìã‘Þ«ggO\ÝßÈ`Öüꮡñ Í´ˆ„06N–q ù fçÂÏp4MN4Mq->»Áü&0ÿØyæ>Sï§a¯â}mÌÏöŸ®ü‹Ç²¾‘¹¹]Œ"k2†2÷u0ÊÙB\–ªœ¹“3iŠ˜”—Q†Ï€’q7V×˃ç‡SÔ‹2úÔ)¬³rO†Ñ{dî>Ff¶3ŒÍ¢œSÒëyÎý¬¾kšg‚w»þï¾Éüu?Øÿ®>7üœ Äîùˆ·7Âñ©• Ûè!Z›fç§§ÛBj­–ñ ?{Â7²>‚`–¬•©An»¨\÷ÃïÔaYÁlŒ¡}â®vi\-ì‹`v*–WµªÒ %zÎëðw*îˆÏ‹ˆÕPô‚ ý(*†ÿ ®;vvxR… 1×/ÀmXxuÏ‹pçÁ}š¥—-¡Öë®Â_Aøù„”0CYׇ·!„aªëÉbñY…ëûLqÜO—ëŠG X.2Š>i®^§Êø4ú„¡»Kh ÕÞ›=GÆÐ¿Á³kÀð7æP†åHc;Û¾’]•ÐÞÆ»zÌìì ]x¡Iø˜†ù ÂÎÄ»I÷·ðMý OBœÎ1”ñqZ ÷oBj]?@¼zA¾æöœœáà0 ý<Ûs·ö¼ëŠ#Ò0ûtÍ{îgá®eÁŸ§S½„gCðûyކ¸|G\-eM-0Ö’°YÛ±Ÿa”†´+ö³ˆÀvä»ËpýEú¤œjJ5yÒÐø…æ‰dߟíÁ¼Âû¶"Rr}GU?KmAÞsú&EZm‡âé`GÉ¥ároeƒ|åEÖÇH?æPÈõx_Oá÷ À/LëuÌ͸±¶4Gcîõì–p è«ÃH)süBm+Nï†cîœMsëEàœBõSC—e¦ºÿÛ“_f¢QÕ àøÍg&ÐpÏGkxKG‡µ|£>‘?9õQ©ÿ ;'Y–Ü·¦]µ¯´ä(MŠÓæ³Þwž äö§Vxòçik6Þó¹Î3:oÛ^>§3Q™ÿ_R÷7Ú&a@Ë,Eÿƒ»‹°2 µ¶ÿ¿‡{@ž¨ ÏyyÁŒ×¤ÌlÙ^þ)ܰ^/ 1Æ8Ïž½žJà Àþ^06l¸íÑ0ËV\i·ª)õ=CoÃ;éYÁÏQá%õÒ†gåÞ2';c±Û]è5ؽ ³á–Ôã\Åœ†4¬ÓŸJþ[TC£¹èeÙy R= a‘$?ÂhY}…Bö9ÎGJ‘0:/§æR²•­ÈÁ7¯&Õ-yAU¯õ@mÓ»‡€°; §L”y~þdÿkÎó¬Åe› o…ùƒÎ³Öv†öýJ'OÂØôl'þPªn/ݸ -£¢S‚W>:3¹Âyæœ›Š½ðƒ×Òóþ/œ°H¡.0 Ãp «…aeÀüºü@z1zæ!7—@¡â!Ǿs./±.Ç;ôA±zÞݳiŽ?9Ϲ7cÁðI¹×'xÚϧaœŽÔ£Åúk¤å:42L׋o‚z^êØ1é÷òʰ+„Æ™ÊTùø†öýn0s~(3ó¸š6)ç\eZ¯‚ÛGù9YW»ýx`ù£h*˜_Šï`”›éN{ò»„Õ¼Oí4«y/eCUÌ”5 eÎ|ÑÎwCþ„ Û󴬜[”eÍE|ïógbɜ %±Q~ýŠwþ7ÃÒ€Ñ2×¼Ús¦®ðMªKð=ŽtêzFÊîŠw#å3&‘ÿE£Øi°ŠÂuÕìÌÌuè ‰Ü1ÛdqB€{>âäEp4"! lB“ÚêHl“.vU…Ûî¤zÔ­xÐó™Œãvº¶”ÌpÛ%AžIåõzüîJ…†µ Ã^.vÂ×RÎr+†-Hùo;<)Ž s]µ”AˆyUX· Æ’³Bí.üöç!¨ {¢åõCG8¢VL-Iþô§TQš–¹ˆ„~ªœkëmñxlåC`XÁ©î06‹0 Æî¹y€@ëê¹î"{ÈŠ'"¬â„®í¹Ý6æÚ0ôIH+*[vYUŠùEsTì–Ø0#ɳò'gV)dÅÍÀÀ0ΚÆHYÿ€ðùc]?¤ñï íŸÇ’})|Ï»ÝQžD¾ÿ/üóè†I‚JØ£)ø`¨â·âAÛÓ4YÑŒž²ªÈ(»7·ÖµUfî «bl»&õ:T £ÊUÃ.è{ÉËÎ|Ú­x³™Ëq"eY „Íš‡¥Ò›|«[ñ Køgã´ßÎÞÌ!ä7ïAà ‘/ÐK«R·î0 ØU?hÃEÅûà»ý*/{²êO«ßÅKÙàÄŠÊšÄOš[ñ g:$ü>R(\]׋ú:öc|þÙöϲì^=º¦yuˆÓb´”uÒÿ,<ÇÞð5–¨!xe)žê]‹è F¥ôÌàåè•zšêô ާ|‘c¶ÄâŒ+qöB8:á T*ö°™hüÞV8S˶‹:ìÐáÕ^ç ãy_C¥µ‚B¿zNoÇŽaê§¢"KÁ³÷f&þê˜;çÞž Kñ¬÷ûŒyàdÇÜ9{„§Zcîõ[H–Bìå˜ÕuƇû$=7…YCpÃP£ 3%m;Ž?4V¸—wÿ“ÐÓs âøÒv$µ ®3Ö®¡å9©GñKg+Å»•œ‰zn0„D­â°„v1]YB,Dø.üéhS©|.~ßt 8`þ¾[9s7ôŒ°O¯t3³!n¥WÖÉñ‹ˆuC¬Í.àuBx=êúA‰«‘_jóÏ~˜/¦¤§·ÛšP{È£ySÉÝBŸ9÷Íɧ·Öë½Êp{:áWÌ;j'ŽðO«&ÈÓÜä¡Avën¶ÿÇM]çpaÔe¿ÑϤªL‹Y•·_ø&ÞËܶŒÂÜ‹gÉ®Y9„Ôí®9®Ñ¨’g‡£¬›BÃ3 êõÀ!åSö¹Ž?ñR6TEÌÃmêg+€JÚùß^Ó|ïRT¾k­sU|ì e¿kËÕz ”Qz9¾O/¬¼êž‡UÝ-^ƒ¥îÇÜ‘ï釹j¿cNb–JéÀz+âjÌ„"Éhèàƒ Ä=jáF«ñZD´2Uú‘D+‰ôB 7U¨¿‡·ŸÖœZ”Pèÿ7½”¦õùBÀSbÛ´¤!>´¤bµc¤6)lw¨J KÈnåì k I¥¡Æa  Qy«rÃÄ6ªôôõþÆÓxß Ò¦Níè´øÙË@n-»­n;:tL|©º+j…¶'e?áõ‰[³‚ƒÑ³3^‚9.“1Ió,S|¦ÓÚK~¢ÒÃp0q܈{s_ËIèŲ‹ ¼l>}@ÿOß]¾;‰›·AšºÏß¡ð@ûT:ƒá[tŽæ°‡-ÿ©ü0zyý«² p’OV¼³8¬ñžê]iŒÒ›vOŽ­l¹½ñb´×ììŒÜfu]+©Ýºlf¨}°~ ð®5wOù=Wï#Ÿ[`®#öÕz?p ¹ƒ°I-õa†ðIähYV5_3VD€•Vô²Ú|T•„2 ú`e“¾`ñED<”êCöPU«·½Ä@àÃ(ø±Jý”ûÀ…MÇ>>ö¬¸¬ñ·-Ò¥èH}²›ž±I`>n´ eÁÆ|8Õp¹C¿þ=N~n/,ÇÐ QRò‰Ðaî0+º…0[HËDbµ–WànP™±-³±;íÒœ uœaˆÁ0[ž>iÊÑ`Ðþ?EkÚc<ó»e·í@‹ÜÖn#QAÚó=„GƒÛèŽ÷—­è…ZK,ËR@âÛñûí÷çC qÆî‹/¾¨UN<®öÔÖ»0RͰ™oµ¢[ê\ ÅŠðn Þ„ÿ–ëúÞxC1Vnrý'»{}³¶h5„vm¾aùCýÒ‹¬a¶ˆÌíýuŒm³K‹MÊ xg¬µÈB¨-³Å'Ü]çHðùž.ÓuZ­ëjäŸÌŠ†Ý ¯ðýÓ±KçÆ„ávéõ°IÁK0Gj â¼Ò¡#ba±éØ®¨7ceUK‹ã7ÆÃTSÆs:'íÏrôWÂN/·ys^ƒq>X?dJý&„;ŽÂÖÖüu&ZLºâÛ}9T™¬=n-[6T‹—æô>T3mž%ûS~ðøªÏE¤an(£±Ð…ºP™Ôð¾Ó}§ŽA>ÚÜË»oÕ¼¿p…r;jnpç>hlxïíl³È û÷†sÃfL µ`壵¼)Ž'ø¿Á莦Vø“AÇAd3Â*ë|wÃãõaý‘ª+êÔ+ÖFç9*çM¶…Nt{ßï˜Gz¾ç¡Hm×mÏãOBiŽ(^ ›“m,+{M¥«'+ÏõžæüŸa5˜°‚­ åe§òÊŽz3X¹–z3B/=Gœfó§aUhI}œ.1iêjö=v–§á2«ó?’ÝhÃ×í/¡¿T»5ÔŸË.»¬AÊJ8ÿÉw–åØšû9†Ïüî¾o‰kêIÁphdê)ðŸU¦—ϲã ó‘,Ç2«/FàŽgCø ·àë6·{ºÆž«èLÃ"×9XlAœ„—õ©yï¡Õæè=r¥;Hê®0©øKû@¨ÂŠ\âlôÄ Ó÷iî&uï¡ÿÝ™9þÕŽýƆá¸ôŒoá,¬Rô<:>J ]Ü |Ý^œ‹ òŽ{(%Óß»0ñ½ZZœçЯjÍ«–°:“=ø[U¦8îšëìIñÃ,.ÆDF)~ÆÞáÓ[Kd¼?.ÕiuÎê-²§ÚÿS¬œµ ­w'@Ø !Wç#ýÛ}ÝÛ‘Rb%ç£-÷RLb?ŠÇn¨[ ]EÝëAžÓªHHãz„Ý“&jF²JE¬bó·²%¾ÁJflB¯ß¥ ZŽÖÀÚµ7סãi“H’?Ñüžú]GnŠ6å§ZóÔ"÷`¼£“ õ¬êí=~hèªB%Ezo;´žÊÀ{6æ]óûP¨+áŠyM•vD”a8ÞÔ{ÆPÄ4H‡^ŒY»#?;óŸ¡ ¤Ûi©­çö«zJCÝbŽUŸ 3ù[è³æº§!;h0xßéu؃ä4”¥‹ ·•^€rcC]=e¡ñké²!4>-qUm…ßÝÿQY‡žÞž¯®Ó Hi?•öÀõ9Tx´z†\…z„ûÙÁñ?¡ç„†qÝiêæÃ0:/Œ56b­‚ ø`­ƒ@~0c>„A@ª*Ë­ÑîTÐfŽb›i"àÌcpÛ±¯ ë‚f0Àx§@¨ïƒ yãîbÐÚ*;^ϧ0C‹ºº„ö!¨2o¡ TdÜ”,3ŒËJJ, wQÜ>ÕÐ蔛Ŷ¢…ÙïÜn©Ua,D…×9ížÜ³ „ˆŠö5÷Dò$_*6=¤~ÅipŠí^‘Ï÷Àj4äF“Æî¶Ûšì!v–Q~}ÍGm×dx`J?´Ð?3±Ôÿ X¹ma¬H[º²{‘OV„*äÞ0LžöHìÑáäÍh¼¾„vlF~» ùi[r—”—Ý¢ ÃíO×vZ¼lPrT®ê6(ĸú­T'Ó’ÎÕ +î,{ êùPKÃ=Ö ßŸý-¡—ª¶o©"%ó*.äÛ·–‡²{NÈgý»Úâ'eCmÑkRsê…CCÌx”‡(óäÍ›4&\€•Qþö’±Œú…°sìÿ:{òÄÃÙ¯Ï,ÑÛq2ÕIðïÜôIAò&Ð* °òÑ*_[Û´æwR7*Ë{°OÀcáV”²…²ß7-¢–Xôß÷öŸ_1êE ]i‡” ´~þ£Â†õáw]É d|ŠýqYʋͧn2¥Æ P¨;¾¶0ciž’"ŸAed€í–{1)F óÏÐ0°±ÙéîM°ÜÏiÓA¥ôÙd†ù¯¹ŸÑ5–V©èÅ0•]ÁJ᱇\9öì[¤øâ iØ™ª¥¾ë<༊ì`bïùáìjšçzçNï¦ù)¡vh@¨Y[¸Ç²{V¤Sž–5õÚ5™vºn‰Í5Ÿ°ß!)Ÿ¡Jyºÿ¾ýð=M©íØK+ù/|Ó]×ë+îF.¤•é^˜1vl‰ÛM4a¸ý‰àº"?Šæ®T$pªµ›¦b²Ç^U†a.Ž[ õ Ùw?¶÷àÀ²Ô¶ðèín“t?ŵ¦iïK]Ó¾CµùI+¡lü¬Ï†BDC‚¨9þÉÚì×fÞÒeCmñj*sú¾Ð0uê:=çsÚl’Ê[¬;pG=²ÌÍÂBSPFúP”=WÙ£ÚàhR#æ‚L´ý³ÄCöÎö ö…0–'àmù(p ˜@äh¨ ÿ!&MDUêæ¢¿Š/Å‚åØQv9*Òvø °tó@œQ1¼"¼I7»•‡j!¡µ"írC”•†^€Ðð=ïï‰I´£’è÷_ oW[(w»KHÑ&êÅæÑ°ž±ÃüJдd®„àEË©öÅR³ƒË±»3Ü\îv××´¹ºâ_‡ßg“xŽa/o‡kùF ¹_7ËOĦ¯0´êWHEö°;¾zé™8'Cy¯—çø{Cã %®rˆ•:vJÚwô½j¼çC!sìï¡–Ôµ„d ·æ'˜XXÉ;ð.û‚ãB¨<ÝóædOü æ÷÷xK"½þwP¶úûѥƶn¸¯¡Ög3èá9vÉ:µøM¼Ÿ³„Ò—ë:p”þÕ‹2ƒØÑZ-ÇJmÓæNÎ ó¾b £‡8þ‹urÑWˆËáz±õ)æEüyª}„‡(U~5„-l2(ÎF>I 2Ì'L˸ ys"ì‘ÆûD¨½hÃõ¯Ö{65}£ïöGd€%ö‰ž 7=P.ÐæŽh  ­Ï à ôœAؽ†òO”'±¹àP|Ðïµ;óÂ Ñ ï[ÃLgÝ3a–Íþ á O¿@>xy„õ õй}C\ò1„ Ãwä%¸þœVÜÏ#¹né²!’8FagOÔ v.xb•3±Ò7õD݃" ¥öLòHïH,Ë^g/V/-ãƒ+¸ÖQZ}^­Œ46^ù“3žD¯ 6¬TG ccü©±DcýfwL ¹Tk™i®@9& ªDS½íÑ„œÖ`96DzF¢0¾ýÞ¨>÷H‘†M½.¬m½} 5ˆwÈ¡ýÏÄålT&huWÓ±¢ÏX! äõòîq\¸q¼öj0{v? “Ž'Ã.¦;ÐJ=âTä·C´¸f?:²)œ¦?‰á5‹ :Ç¡lºþ\ûŸ° él^•ðY{عWú®ÃÕgôð½çPü1ŸæÐPwÉÞvÏPcòÏž }É}<” ‘ij1v(/‚áôCs˜–ÁŸ·ÁìQ©iWçeû­Oñ pi•>¼—Ñ5òøç´‰-]7ö wŒðGÃ7DQŒk®^öÆÆ—Ý1pPòÁZ7æä[»¥¿îÛi÷ïHRƒÍ-¨P:B(ÂÚévŸ™ì«|ßÌ{&üâ˜Eâ—=ôªXìm)OaÇöjƒ½‹v$[ÀE/Þ^ÞÕ”ÞÎX¬ØðvªT5'J¶@Ô ²6ïkyLOR‡äŸC‡Ý8q¢¡v¥…%û%‰äµ´œ¥cÞ–ÎèýI‡ä1‚P~ª/eb8h¿Ó|¥\87è?­¹øÐpŸR³t/áñÔ9%Š5G´ÇÌ{ßÿÒW3e²¾Çn+òÓÓõ†F™òjñ–òþÛÉ_›»\ a–Ø?eìg·1\¹H ±W-¶‘äMõöŠç½#"-ú~Ø>`-C€•–áΡ¶0 åCtªPè,¨P>Z8J<ˆ˜”´lË#’|¾~Ư çô"}Ç´¶bõ³¬f™ƒ.lŸ†g¯FWã³ÈÏ#\Ÿ±äX1&°+àaW»â[å41&°ËÀ0š9@«Žm©Mñ Ä—;«€ ¿ÜeapÂE€'0¥È&ÇJzh”'ìˆ 0&ÐH¬|4;cL€ ´{Ì·bØUWô€L]ÅŒ–«M÷ç^jb#3 ËÒ±ÏÜ–ˆ'‡ŸgöÑÍcË^˜_óêÜ쉟ÇgL9VL€ 쪼»jÂ8]L€ 0]–@мY–¨ €dézYJÌu’¿a‚q—}E´g·Çpš Xùª¼Éïî²8a@ùÝ}Ðsfo’ˆü²+÷ݱl‘ 0&#Üó#ìMë"€áOЂ¼¨uÅšcË*ägf®ïå=á@Mz.ÃÊl³1 ë+<)Ç$¾¯°÷BÌ/{vï“Ìx‰™1 ö*tË¡”¾ Åcœôu;Ü^¹ñ0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&ÐXž&À˜@à óg/ÂþOågg>Ö0—Íg{XÖ”£¤eÞ«„ØÝ¡yoÏÏžðÍ­œ†aå`õ¿I© ”ôŽKð¨mJHkV`âʦŽá°ÌàµwL^¶ íVÞÔáíªþ7ÇÛsrzXº×÷H`üÚ–æ«ôO&­3ÖöîåÝ£ ¸±´¥ÓËðÓ²rž„_òwýKɯÛÓzYv™p÷†XûÍþ1¶N€w8oë9€Óßhór7-s‘bq~¶ÿÄp‚$ˆÙ|ÎÏñlL@€Òi5ÆmSºAš•–Ú=Ò0Ò3ƒ—[B=_Ÿ}lŽw.6Æ{½>{õ=O ÜßUé¥ï ©¾×¤–À{x=íW <Ü¡L/\ˆM&WkšÌ±”ìÒΓøË½ø5l¾fÀßëó»®çiþà;ØAzH8;š£ó‚Y Ÿt‡ÃÃÙ‰G³æÈçõ¥;ÜwÐXŽ·‚ƒ ]-ñjž“fgg|Da§eG+Kd[=°W£Àw» ùa©&å“s²3çÕ¿hŸÇ2}N\Òý÷íg‰² ô‚sa&¡€H×÷”®Ní}·ß7~üvÇnk<ïy×é‹?ªdâ?6i¸#0£S‘Q0ÄCýc²3œ1ÙÃýÙÃ,eL&¿Êtý´Î꘣ð'ì-‡ý¾ùÁ¬¿»ÃIË .@‹{"“\B•@8/ ƒó¹ÁÌÑd?-—¢ÌMÙÈu†$õƒŸ+`^OoÆt÷ð.øw=žŸÑËë¿¶À̹ñ¼-ÁÝ»wìþÈèÑeî°ëaYÁla‰!ŸçÊ9‰¿9æ =#­Ÿ ^wi>QnébÒ{ ºh§æåøƒCIôQhŒøõ=ôZÈ·<©ÚÄÙ'n¡1û%Å*Ck(ñ(Ðs¾ÂPtlÈGÐãq€R0‡fRúGágdîâ¾99þç†ùƒiJ”!Œ‘îøâ=œ‚qß™Hû˜'á÷“”žÛó²3–¸íÅâzXVîÉÒ²²ÞH›ù ¯H˜0+8þò-äFóî£c£òy=irâTÛ;G~ÿ#Üw€aŒ§’[´N[#¦Ní„üz˜ £Ý0wç[¯Ô&9Cªœ0è¼ÞXyÞgägªÌ•ÜÖºªG¡õ½#²?"0³®oYŒ<›‹ü„ïoç1<+çÓRcææøMËÉé)ŠÕ\ <áoƒ£m›Jf#}/ÒuCÓW’²vØî=êÇ*³:.êËçTn}Ó4ä¿3×à·Ì#5(czH÷S“]PÆŒFÚ‡ý¿ôüN|“Ü=.õ}ËNtkËTÐ3%åÓn%$Ò2Îñß}~00f«ûž®)Îg9.“CŸñ=`#€r‚&À¢!  ­ÂínÏMËʹª>¿hþ„æ7!Ðz0ïà|„oÃͰ£à5Ç­)=+`§¢•Rª‡1d"¨¤áS[ ;Œ <ÐÙ±K¨!x FE|ú¤) Òüøq‰¦Ôod—Z}•¾i „k!Õ<…°¯“šxóîþÙ©:¤ì ·Ç¹Bˆ8 ÂÆ£xvwmŠGº?gZ^aZó/®[,KAÈ×®š39“Æ™G}@‘Ê„"sÖœÉþjþU¶$»[“ÿ îÝ1FÛîqz$#ƒ†¯ÌÃâCÁó”ÉÂ_óçìÝãÒóñµ[OõM÷¡ý¿˜r‰«7é±–£4¸kúôÔ­eÓÖ sƒþ*åjŸYg,ZdYÖƒHË ôIÓ4öÝGEÛyCòy¤irq…þ‡ûªÒ£Dè×±êÚDÇ,=\`éêsCÈK`ö€cN‹½ôlTÕz·’µÊ‹Íš©‡q>†Pþ=;îèܘô¸Ý‡»ž™‘A=óæd¼û_ÃæÉ¤Ïõb¢'r2 >‚òægôò=œäëÿH`t¡Û^$ù\­Ý4 ¥Áa^Ÿvø¬@æ×•îGoáo²¦á¾ªÁ£òÙ ©Z¿Êô‘Ñ è™êˆò„òöðJ;¢¾oÙ±GçÚò†Û]7´Œ uî^®Ù8y¢}‚T<ä* 6c €F>˜ˆ†F÷Øß‘ôún…pŸ,Œ’µùWþçö Hî©yÄ“n;*YCO*Yiï6½¦¥bÑr¿A óç¼-‹ïÁñ{èÕ å£â°¬Ád—ÜT¨KQ‰¾ëR×ö-Gž7Qƹ#r=/Óu˜ãgØØ7ÔÜëÛÍié }Döƒâ1½.K1×$¿¦…(L¤ . NÅ›‹®ƒÁPúA`ìeÎê5ôEb5§þ²·…µW=¾„ÕDpƒ÷ø³ªwÇI»Z‘s]í¬Ô>tïñ%®¬fŽ¬ÊµB!³˜†Aïæ×ƾûP!X…™®ýLjï¢Ç­¶¼QÍy$ùy#â4Uy^Ë;¯z^ËEJGíw÷#úæ°äñ6 gJp›£gãzÜ¿’?aÂ6·9]ÓJh8±'ÆýëµWãžû ñž…ÊJOh ¼4}á¼ÇÜ‘E0?‹„hôruWÑæâc`v2Ù$Ÿã;ÛÂ2X@‰Ùy໣¡Ÿš¹¹hOœ«ò.òÿš¶*¯¤¶ë<ˆ2aT½“}ËæÆ”q5âZi0<0¥Ÿ¥›o¢ ]pÚ!ý'çÕf‘Í™h0oƒ]°&Àj%ÐÓ량ùWAp™ƒ‰§úÝm­±©¤@>¢y5„QSÈUnûá®Ñ‰å2åUÔŠXl]¨¤Ø¼»÷„1o]+ÐWl/—â|¬Wÿt±±ã0VóC©jñ©z¦Ìe±á3ïQÁ¬ÊšûB q3¼ÿƒrZToÅгÙîçÑ\C€¬Á††Íìø«x1„Cù˜ƒ0½5‡s—ªa7ф븅À•B× ÂWê˜Ez†Ûus³³fEjß±'šïƃ÷…Ì„9Eö»‰êÝ;á å¨?TàꌤõSÕuõåsÇ‹HÒTe7Ì;wžÕuž~×]Å÷W—‘È=‚ä@éÑî©Ëbå‚ß:eÊËf‘±äoýª±þ IOØp”ò…5¯Ã0’ôÕáÜ~4'ù-.nÆP¬¥h0˜™Èý[~ ãÇHò9üT|sŸc‚ùsÕÂÁ‡>ÂH’4ÔÑ}»oÂ]7ô[W„ó×6k`ÎÚ`°ÜРlý)©{Ç+"YŒ œ?lƘ@x¬|„ç¦L Qh¬õȬ©·”+ýKÝØš %£’^•_šÒV¢[¯¶Á—Up¡%‹f –è1JO€ßçcÈÕ«4D‡¼@ëä˜}F‘*ù Ãx4Ÿ/á*¯¥Z e¤oÕ½û¢b娵ò#mBá›æÏÁîàÖéYS?ÎËž°Ìím,¯±iàˆÍý±|èy“3«Ò…ñäÛõ#†ZoW‘e¢¸N[cèu ¯Ð¿²3w„U,)¬-Õ,¶Ú—µ_ȼÑレ§B`KÕØûGÝÖ—Ï’¦ˆŒÒ’e˜×APþ£—v†ÿÔÐÊi˜ô6:#¯'Û I)´ÔÑΓòü«h¦Ô­s‡#É窶i)™Ì|*Vñn²o9eœ½b^IùB4ð¬‘Þnç?2:½,Véf˜¨ ³a ” 0 3³'|‡±ËS!¸Œ„°? ¯ï3ˆ%EJW˜ÄYßQ±ªR¹iÒ°†ªcŽß_€Ç4až çDÌ·¨.ãQòˆ6'IeÏ÷øfVànZI§âPöjOgØo3œíIì ;–kb¡Ë¸ÞK$›ÉR/o¯±P‚VX–1–­×aã-ìaiGOí¸÷/æÍ›çAz¯tîcu– â ô®”™†½¶¬¼ ëOR·ÔåxŸèa1®©aAY× ÕwMO±¯=¬¥ÑニÇÑÔ•Ï’¦úcþ;¨ß]… Ú=yÕ¿%½>·”¯°ªÚ¹È×ö®†¤‡†p!ï`h—uŠ;ŠÌÎp›Ñ5„Üõhž¨ö‡Ú‰Õ}‰YrùeyÄj:G’Ï7Z…íôp«‡‘<šæ[޲Œ£²±¸Äz×~'ÞnçæÒëíÅidúÙhÓ¸ç£M¿~N|Sèé韃ÍÌ.ƒhq&ªø/pæÆmIJ´Ù˜Ü1-öåØËCd±2¬£ÐQqŒž™ÎñÓÙwÑ;ËÚŒŽîDÅ'º1LgÕ+´/À¦W×B0°R:¥¼íøŸäK} í’!_‡VÖ§s:wì”ø¬ªt¾2JÞDØwx} +-K?Ô2Y–~ìé9þ1·ýH¯i¨ †q\®të ÃØLC¯®ÔmCìAxÄŠ`â†uÆÇ™˜ëò¤f,[q£-¸#Á±ï¼¯û;Øi¯¶«õVîi^ÔÛçÕžµCËQ—Ûþ‰ïèUìn¾ÂÚVl±rô‚å?‡,µ7mÒGn‘Ì‘ç`Õµ;4g¾BÞÁ.æãÎn¡q€Bý_t­LŪsçYí´RK<3cmÅ>Ôn¤÷.ìb?¹H*ÏZ¤©‹)å`SY7á[ÿ¶CûDš,."Êçí|9r‡~-6žy+âaŽ—w¶ÄÙ eæŽXô~ȯ†Mõ-GSÆÑ”¢ÍE  ¨îmõ!Ëüó"¬äW-Y^K}Ëd«yÎ7L  àž6ô²9©ÍGÀÇ(ý#/˜9 •ÛÍhM¼ØÔÕ§¦n}ƒ ®O`8Æ@©´ª9ö8c©ƒ½Ë…ú › V ‹Ò4õšLiÂç›3ÆŽ-q‚ M²Ðj÷!=óHY¥”ÐsŒ‹/JN±—¤]?ÿ«ëeë±Â|ïŸûRµ"mvÂrŸiü8Ò4 ÊÁ5¤or?‹Õu/ï yèå¹ÏM˜d¿󰟃ó«&NUn°ÁâÝÞhÓ¿±ôŽÀê#„nYfÌVÕr››ù<Òr+²ËeJþb¨ò‘'.y)&ó?ëØ£scÞ½Û},¯ëÊç IS]qªë;¨Ëó Ê; ¹ZºZ=/‰žuÆ;žcXæ‡ÊÒ—ƒûc¸ß_ü*×îà¢!éÁð¸ÛáÇ"(”3LÝX…^ÁPlÒ„çF'^ÎÙëIxž¾G,p0Om×·éÅ(7¢;Ðk³ ¯ÃÚ^¯¢ákSa¿¥îBZß@Ü΢²À ¡¾|Nôµ$y$R¾Ã&ÏZªl¾ó·0Ô’-hÔÑTßr4e\Qay7¤ëP(ªÁ+ùæ¹ÐŸî©±¬p£ÒÏŽ˜@['€†>˜h)4i¼È(Ú­Sû„îÝCãC+¯h¢ÃŸ•«ò„>nð= Ù¤çìyò¡ý×¶ÆÉ”´Ê×÷.h®¸ÓT)’w„î“Ð`ð8™›Û…6K¤yXoVb•¦X‰è#¥|¿ç´íµÙ'n‰$Rl‡ 0&À˜`L€ 0æ!pë”)»YÅÖ¹J©«ñ; ¿#äaùÁ¬·cƒ˜+iYÁ;”%î—R} Mjt^ŽÿÃXGšýcL€ßýI@IDAT 0&À˜`L öÒ3ƒ'YB<Ÿ’š¸+?Ûÿ`,CñÄÒ³ ÅCÍBþ[øºŸŸŸ=ve,ýg¿˜`L€ 0&À˜h:K?Z¸úÈÓ.ZZÅû£dì‘§œ¶mé‡ ?‰Uˆ1ëù ¡VJÉ×ááËyÁÌË1Ô ÃÈø`L€ 0&À˜`L µ€â!Óý9/@ ¿bý9±‚¥ÅM.‡?si¨•ðu»XPe?˜`L€ 0&À˜@˰åyÈõý;üæVÊûQG&&Ê­jeO.Çü@zqÔ±b˜`L€ 0&À˜hQ$×CYEr¾½Šm b僖Ӆvô&O.Áa/˜`L€ 0&À˜@œ ùžä|{ûŒÄ)jåƒ6¬ØÇC>ƒø°L€ 0&À˜`L€ Ä(Ï‘¼oËýQÆ+jåƒv.§ i(ãÂΙ`L€ 0&À˜ˆ3$瓼Or´Q‹Zù@úÒÎå¼`´¯‚Ý3&À˜`L€ 0ø#`Ëù÷³¾ÑÆÎ­Jª^ðc]´þ°{&À˜`L€ 0&Àâ“d~’÷IîêˆZùÀ¾íƒQÅ‚3&À˜`L€ 0&· ó“¼OrTGÔÊGT¡³c&À˜À.F`Ì$m.êÕ¡cÒ†ûÇ+ÚÅ’·K&gXfðSMªü¼`Öã»d9QL€ 08"ÀÊG½ Ž `­—@ú¤à…ÊRãwüUt,¥bÛÖ23ÍŸý=æÄ}žàóe=¿K O ”H+VolxVîñ˜È¸HH±8?Û¢½¹UˆçPf£åíèüÿÀGÑÝJu˜ÚîÑy®™`L ±˜pI8l‡ 0&°ËHËʹ ŠÇËJC“â&O£Im¨’ò$ºcWq÷†])ñéþì› Œœü&I“ǧgG4‰ßì)`L€ ´8îùhñWÀ`L 5@Çùʲþ EãÁÞžÌq®Þ€Ï®—+ÒæoÍI¬w(YÀpS±0bžRrʈÌà+³rükbá%ûÁ˜`ñC€•øy&ÀZ!Ë~)äÚÞÞþ]ŠG­)I ä¥(sS6,ÝbûaXÖ 1š×Ó›1Ýí>-3x=V9Gz“G*£ôa)Ô`!dÿ÷¥·Ûh!¶§(½t:†!{¤’K¼>ϳW:cØ×'égYÖ°s;„ú¥T+…”OahÓÃŽ½™ít}Ëbt…çb'Ûs:ÏʹÁ´Ô˜¹9þCÓrrzŠb5W)qÂÝ ÿ¶í*™¡P/Ò5¥O蛦!žgÀN £Z摚vvÆG¶Ýzþø¼ÞL]7B̆Õóê±n?–•{²´¬,„9Lu„û…W$L˜ÿƒÛ}eü‚Ù øY`²ØçM¼Ý0ÊÊÝöè:Út„úÇ÷L€ 0&PA€‡]qN`L€ 4’@ 0/‚î¡p>7ZC€ õ–ì+}Óa‰kIÐ4yÔÄ«J©{ ôœ—ªÙ—²+ü>O% „P¿Ká¡ÿ©ÄUÊØøw¡´”I©Ãù1ÒdžñÍÅpùs©ÌXReÂìIá7BùCÄJË æ8ö<„5ÀÒDÇÌ9[Jt£gtq~ñ¢ad_chY~ø¿¤çiyy>¤o1.¯H;±#nŠaYï¥MÊLvê;4¡KMÞ¡„:—†³Õgxfðj©Ì…Hÿ$âvüüHc'C”‘ž•kÏ¿!?îÌè¤J¬/  ‚ÿ!µÛ`w½¡—/R†$íP2^®Å.ZãÕ¾¡Ï”·cÛLóˆBÓÂcy«-Ý‹¶ M¢¸ nûº¯v›”Ô„Ÿ·m-%»}CŸEsÐ÷†¢‘Œ^‹ïÝþ ÷¡3î5ss)F+ÝÏj»ÎLø«je`Ø×Ã8?‡=8Þ¤ ˆU‡RÔË#<¾Ä~JM[¡0!Ç4 bú+~H«ü7Ùw ’Ö”ÿU};–X¦Ã_3&À˜€¬|p.`L€ 4–€×û«Ð Ì—5”†Z½TJûL ˜Ë=y÷¤•äÒk81E¹QÃ0ŒÕèáÖËËÛ‘U<«®¨„qaßÎ8œzeRáéç˜`þ\µçöˆ&)Œ$Ù ²zzý31æ*ôêÌÁ¤øƒ }KXv2Ѩiîg8Äü‡iÒRV-^¸y`̘Ò4Ô¹G¬Ó±Óg¾bL€ 0V>80&ÀIàÔ÷Y³`ùŠ5joÁ$åûóÓÓk Án¿¥ZŠá@á{¤êC+_¹­Çâ==Cý))V'‘´’Õt6ÑBgÝŽ N·‡”½ªõ68æaΘô½~”Ì f>æqƒhõ¯‘YSo)Wú—º±5è®±5Û/Kx–bkaË>0ØR-Ã6C"µ_*Í)­½«ÙÁÍ­÷ÞOê=¶*Vù0Öé “ï™`m™@±¶ ƒÓΘh¡C‡šš”“h˜ŽX»i2†플Ãy¤ä§°p&Qwu?NËšzFŠ•¯ºÍcq%zO§U¶Ü~!§`þƒ¥ù<Ÿ’ù¬ÀÈ.µ «Ïžâ¶W±r–u†ÛŒ®1Á{=ü !T՘ѪS§Ì †W°ªÙŽìffö„ïÀx*¢;xíU·—IÝR—£§¥ È5ŽYÕYY×`˜Öšžbߊ!Y`ggÐ$õ*;¸°Ìò!î{ºnŠt„†Á÷L€ 0¶J€{>Úê›çt3&»{2ž.°r.ÂÊLÒ³rŽÄ¾Ï™BƒÀlu O˜®¥`U*ZéItì”ø¬Ît¾2JÞÄ>wx} +-K?Ô2Yþìé9þ±˜Dªº'IúŠyó¦Üëõh`5©ó±*Ö-°ÿåÞN Wá,Á{‡æñÌW¦ìˆ]ÌÇÃÞnÕ½ƒp.Å1¤i*Vé:Ïj§}”Zšà™»Y´óåÈúµØhã]¬Ü5#{—)Q¶1߃U¬¡~ErßÓÓ?§Àøé2¨=g"t{Y_r÷ÈèÑeòrnû§ÁsS‚T/š"!Á’ú-–¥.Á2À×:K c”i†n^_¸­ìåœñšðnÔ ó8¥¬É`_R-M”Žjað `L àž6úâ9ÙL€ Ć ÂR¸a‚ó0ô€ô„Л/,ã3ìzþ¦%äôíIÑÚýãÆ%§Èóp¹ =ÿÕõ²õ¦eͧy¾TíZ¥)6±Úé‹Z®&Õo¦e~T¦—`ºöƒàÿÙË»–ÅÝyhÉâv<[A~†©«,ËøÓ»7iÂsãN[W^OÂóKYóÔv}k‘^| =ÉŸ0a›–$„’ð„úg-U¶=:oa¯±¡~4äÞV ”DˆQÈ17;óy Ǻ®(SòC•ÿˆ¡f— ¼4/;óYÇ:)ZM; ñÙ[×­¯*X˜A%=WÂÓeŽ=:7U:Üað5`L ­@£VtÖ{Ÿ|@‹Ö)tæƒ 0&Ж Ðužõ›÷ì!v_܈!AáÒ´IÏÙóäCû¯¥á[ámEgŠòy;|øõ8PxÄÔ>»‹AkëRrh·sÍØÜ­«×¿Ú½ãz¸˜Ü=mZû¢B½óÌÿêÐçóæÍó,XþS?‘¬Ùo¿M•Fw¸#ss»XRZ³'N¬>ÿÃm ×4ì-Uh–Ý[ò,ô¶%Ò¾gL€ ĸ‘ù)"Ndâ Ç 0&À*ò‘ž¼—y0&À˜ˆ–@¬d~ví›`÷L€ 0&À˜`L€ DD€•ˆ0±%&À˜`L€ 0&À¢%À«]EKÝ3&À─ž«½ÊósœF£Å˜`m+mð¥s’™hæ3^i)åT2&À˜@k!ÀîZË›âx2&À˜`L€ 0VN€•Vþ9úL€ 0&À˜`L µ`壵¼)Ž'`L€ 0&À˜håXùhå/£Ï˜`L€ 0&ÀZ V>ZË›âx2&À˜`L€ 0VN€•Vþ9úL€ 0&À˜`L µ`壵¼)Ž'`L€ 0&À˜håXùhå/£Ï˜`L€ 0&ÀZ V»Éà­YSš–y&@÷URõ’B´o-ÐcO%Äv©ä:¡Ôï÷ÍÙÙ—ÆÊï¶èç©¶øÖ9ÍnUeŠ«=šç­]µL±¿uÓ8KHÙ§­Öî÷Þ¯[c^ç:ª-æÔêi®Ê·­\îƒÌÝ‘æÏ~Ÿ|ÈfBç¦<'“ ô‚Û4M޵,«§”RµOI²:uH‘I>_›ëÅ)Õukka±Ú^\ª)¥¤¦ië Ã|°Ci»GfÌ[Šw|ÊG]8OÕE‡Ÿµ5áÊÓ´fì‘°Ç#ÀT¦´ÖCŽó@RaÒŽQ^¯ç®?ZëkŒ]¼ÃåuËRôöõ~4žò:×Q±{绂OáòmsÊ}±’ù[òŸ¡Ií Ó²z¼ïꨃö–‡ìÛ[¤$%î ù)ª4—–‰oVˆÏ¿ûU}»r­ôhÚºÒÒ’aOM¾-üX C˜óT(lÄ@ \™¢ëúÍO P™ÒšÊªã´ÆùÏLJJž‹ú£×œÅÝÂäõKY7¡Aõm·½–¸æ:ª%¨·Ž0ÃäÛf‘ûÚ”òÄŽAÃþô=zt—ŸqŒg¿>»·ŽÜѱüù÷ âù·>5 6n‘¦©Oxbêä ?RBø¨$ÀyгˆŒÀÎ2e+XwÏÍ™DeJk(O¨7ÜsÓ„Iwx<¾©{ôØMqýÙ;o«¶Üy+ÆA™ÑR,¸Žj)ò­/Üù¶éå¾X)žh1<éÔÈ¥¾÷c|ÈŠP_bî•·Ýy6úð£÷õP»ˆ"Ÿ–8OÅç{áXÅ9w™"¥vßõweœ…(Çcyb³BÜ|TîQùÇõGœg®8‹ž;¯“üAr¢Ø\õ&×Qq–ZKtÜù¶5È}q«|¤¥’}>ßã4ÔêºóOh®¿µä³ˆâIÜzuë$:tê4ûàƒéGŽ‘û]Íç©]írzš›•)½»ï¦’’RærY;„ouÅÇGå•{4ÔŠëæÎ%»Fxy½“ 9„êŽæH×QÍAy×£µÈ}ñVq8¹B3;ËQ49Æè’FÇGà ·+ϤYJì>àÔ“GšOË+Çë{ox"#wÁy*rVl“ „%@eÊgã±”êÙûÐþ£`)žÊ*×(>‰TÞQ¹ÇõGØ×Ȇpò:É!$ÀIS×›\GEð^ØJÝZ‹Ü×ÔSÝ”Â?¥^×繃V%áÉåá!EjJüˆcjûöÃá†Zoð#m®-õ&qžÂ çƒ Ä‚€S¦$&'ßÿâ¥<±¿ñÊø$SyÇõG,ÞvÛöÃÉë$€DSÖ›\Gµí¬ÓÔ;ù6žå¾xT>´kF? ëmïNËéÆô´QÏ*9vò÷Ë‚¶ØûÁyªæ}NvÓ 2{ õ¸èúô£B<ô~TõzT–sݸþhšwßÖ|¥|DòÉ%H{SÉL\GµµŒÕÄéw¹¯©>¤Æbµµ_b™´ íãÁGôˆ#ñìܽÇið- ¿xi­Œ>qõûÀyª~Flƒ 4ˆ€S¦´ëÔñt8léòÄþÆ+ã‘Då× zl¹N^'¹Öš¢÷ƒë¨:øó£Æpòm¼Ê}q©|u?Ú¹œ7l\¦ uEÛ''*Íëíƒg4ôŠ&ž7E!t<Ü;‚ ç©xx‡]‚€]¦ Œ–š§/ÔÒå‰óS<’©œ£òŽë]"«µx"œ¼Žˆôï)êM'ÿrÕâo{׉@¼Ë}ñ¦|P|bϹŽj}Y¢UÄ8žå¾XDѾŠW²}’ÏÇ•G´4]Gj´4&Íù a!Æi»bØd—œ§¢@[V®‹[…Íãô§ÕDIYyØÀ¾ÿµ@|øåOaŸ±aã P™¢ »LiéòÄþÆ‘ŠG"•s-UÔ•JÚí×Ò~_|¿ª¡^ÄÄ>?h±³óºl MQoî’uÔúM[Åk‹– Ì ‹I>t{âþ6Üæ|]@<Ë}ô!ÅËAʆÝý¨„Š©â‘9óßâ¨û‰‹ltZ7bÇpÝ0Ö¸o´-íequ§û˜Ìb_:´tb+§´ÑÏë<É‹],jûF¾ûux|þâÁ»®>oó—¿¶n·G]Ç{à›»üÌcjýöLÓ<ÿޏþüðïU Oßÿº^œtÄþ5ž±At X8‚K•'Uß8RB4¡²œ‹.aa\ÿgáRñÖ’oì'ɉ ¢G—Žâäû‹cìk›Õ—/ Ó?¬Z/ÙwǨÆ9ԯϿ[!ÎG†Éß5‡1 „~=»Š¤D™Ö°£¶ïç÷õ‰Ü'þg{æõzD׎íÄéƒÇÖ¿a„± £0ΚԨ²‰u>¯Ê¿­µŽrÎ 9tˆ(-/ÿûà+qæ± ¯‡°5îÍ ¡ßFã|­îÊÉË7Ÿ/úìÞE¸ÓDuPßž]ÄÐ3Ž=»vªî°ÜÅ«Ü×<ÒDä/ˆ*2Daú ãêxí£¯D·Îj€â*²µFÆVêèSI@?⽫q›§Zø#þÖW|gó)“ãÝO >ê@±qs¡øÏ{KÅ+|)n½ì4çqµ³Ç£‰Æ^)|ñVœU‹æ.zc—)-]žT|ãe2Al¯Ü/ÃoĨËOE¥eâ(æÏ¾ö±øã¯mvÃV¤ùð»_Ö‰ùï/­SùˆÔ/wÜj»&ámÎK ńλ'Ò^³±=n¾è$‘šœ(>Yþ‹xþ­ÏÄá(3H`‹æˆ„Q4þ7Êm…\Òõf«¯£:wH#†î,Ÿ»vj/¾Y¹¦Q˜C…æ…X~¡a¹ïo½`ÓñÚG_‹ÿ}ø•H»x°ÛJ+¹ŽO¹/žjk§€–pl2åcÙŠ5⃥?Šûí!Þ^ò-Z‚Ä)Gþ­ªUô›Ÿ×Š·?ùV¬Ý¸YìÙ£³8ûøbUÁ&ñ9º¼|bɲ•Ødk8î߇? ?û^lÛQ"öï»»mw¯ÞÝD}a”ë¦xiÁgâë¿Ó*Tvkí¥C޲{Væ½ý™øê§Õ‚Z’N…ðuÆ ƒc–Á+5`GP¨âvõž&ÍS1{Aqèååyo*&ßz±Ð4)ü蕸tÈÑâ—‰-…Ebàßú‰ !¨%ø³oA½Ld¸ØNI1´œÇþ'®9÷8±iËv±àÓïÄל%:¶OïñƒýýÐ}ûÔš›·OMØÚþý¶n“øxùJÛOŠÏÇËVˆÿ›xñ¢ï˜¾{óæ‹ËN?Z¶±£¸L<õʇbåÚh±ê*ºïF£%*Žrݰ™¯~ü]c˜V âPÜ}ÝÙ"%9imºïωît®,«©Li©òÄ®]¶5UϽ7¯æ©j€ê=”HzcÑrqÊÀ¯“«åÃpu )Èÿzc òh©È|ô%qÐ>½i#Xû»J¿t°xùÝ/l…;ç¶K«ùEa#]â‰ùB¨[+vïÚAÜxÁÉ¢{çö¢¤´\{E\}αâÀ½+Vˆ|ö:¶Kœ|zˆÒ2]Ìø×[ˆ¿&Èoêů-¯×õýPqûUgŠÞÝvÿ‡ºÚ9(ý¡üœg1>;y-–Þ6…Ÿ±Œ_Ô~Ñ7ò(”àÈY×¢1jï=º‹Çþó ¥ƒŽh€: YãÐt(HЧƫÚò‚ó‘ÛBäóùèOLðŠëà÷f4ŠÑ\:HixÊÉÈÙ#.±k1j jèÐÀÕˆ'5'ú>lÑŽHËÿ‰;¹/žz>œ×Cb“žr¸8`¯^vËÉ´ªÒ¦Cûïi‡Kí8 ûrÌUñ 1‹QqÐΑÎAJ Û¶ÛBuËQE൅ñ)Zˆ°—8½*îã[tSRKn.ííßî];Š/\1“‡¸­E{ítÍÂ9ÚÈÆÈ}[JkŒÕîÍaPÂO;ºâÛXŒV¥ Ì:dŸÚà }?ÔrÿÓo@±Øj·Øž~Lí½zÔ@@=Ž…E%¶ÝƒÑBì¤ÌüýÜUЇû›#;?þ¶ÎîM$垎•¿ÿa÷NÒõ |ç4‡¤c»|ÿ~Ò¤z:šéû³ÃÚÅþ8eJK&ˉC³~ëíS*zí¶AØwÔKHG¸ºd7(ëþÜZU—8ù—zÍaŽ™ÛOR<¨—Žíø.œù'n;¡×Ô3C=øtМªSè¨+¯×õýØŽCþä½ôž(Á7D=0?õH»·‘¬,Á0,R|†sí‚Z¾©“ìÔutjŸ*BAˆüåÁðKOíSÅ ô¬<øÜ[öÜ0jõ¦Ãͯ.ÿ£|ÖTù«©ü2¹‘9§9±·M}¦ÊòÔÑC«®ébåšâOÌマn%Bs—¾þéw;?ôAc/ q:íè•ÅÿÏÞ{ÀÇuUùãgFÓG]Vqï½Æ±Ó{/„J ¥…°Yê. ì²,,»„²°ð²IHHHuzâØŽcÇqïr“{“¬Þ¦—ÿùÞ§;z¤ÑMÑ=ú<½7¯ž{îy÷sO[0m¼PR÷®§Å<ÁÍ ±Þ ¸ù}ò¦‹Ä3›ÚºY1ß"&¿`ýFŒ,‚°¨eÅŠo<ÐÉÖIXœndkd'}äÒEÂúϵYxøK.Y^6*ÃBX<…NÍ7ÕË3¼€/Þq%kÊÐÿ÷%`¦S6Ü­b´mÌ¢†X«Ç Ù.,b=#À˜µ'Ôô5Qbf`ÿ±zafÜT{DÞ‚ý‰SiùHŸ;[aµ‘÷€+“®Ç±8È}rA_°ðÍš4ZdÏ‚%4ý9rû*žF‚|Œ0 öó'Þ¢_|ãy˜æõ¬‹÷ß… ¦GÎ…;\U³ù#« \/O²™‚&!†çý‹ ¤6ÒJáçÚxâ PÑ#ë›5”o ®›7HQÝ1º`W¸‚À²`{ŒïSP÷Òã„íx}°÷'ú^ø}Þ¼)ìbn”‹g¡”âL 0è´@ ‚û ŽECôX}|ß‘z1±÷çWÖDAà†Uó‡ &Eÿüwå´†‡Ø¿t•­qÄœ©9zº‰Þü`'›€¯ÂËʵ쾵AÓ-{nëˆå°˜ ؾ¯Ró8f‡¯8w?ïÜX¨¥kß°Ò=]èç¾ùܶ~šœþݱøO-àÙ^ øP°Á爛Òf.÷=ÍnebÖöT`Ê øC¨X³m¿Pä¹f¾,€Bðæ6í£†säÛŽÞ`“f·áÂüÈÇ»„ÙlãK8ãØ`c…¼F®mVí{ýü½ÅL¹äLø`ôÓ_“‚íTñ]ªî“‚&%~ +Ë4Ò$Ö]*{âîê™o§h`k,ÚpA¼ÇìÉ£é[Ÿ¾I(ÕßþÕ3±nÓï>¸\ÅÄ^²h:Oì6°Ë”‰þžÝé§²ËW<«&2(æ)dœï†Õ?6ŽNAxÉŒ¦w–#ñØ­=ÁE5T‡ÀWĆ`6¶¥]›ñ‚IiAe@lkü€MÅL붘Àw‰íx8‡}×ï>Hû8-"f§p,ZIðÆñì¡qv™àâC=¡³2ÊS aœ¡‹0;uødSd1˜>a´ø¬aW,˜ØßÛÜ[[³ó¬å¶&þý½×ð‡§;ìî÷öðã=^ßB ‡’%¤º<¾L=³&ñûµ÷¨ð'†;å]’ÝOŠLEȤ5cB59uYy†éýë·Í9y@ˤS2Ñ ýó±6„‚‚/wqlÞ_8øù]N8r3»c`¶Uƒ}KðÍ@ÂXâø²¯e%õà*Œ€w(ïPBœµ@à¼|}mŸš7P&p,ëð‡°>¯ôþ „ë]×.ew®“ÂçÍ›:Ž“§.ÍpwÄ6¾«€Æ Ç÷VO£)c«˜Æ6zgDZôÝÅïòð‚K$¯¥òÑ=÷̌ܓʆôw/X9àêŠ$?paZ±qˆýCâX8:º]lYpp¿ø»°³Ïm¢y¡ÏÁA~€×WnÚË–ð©´„]+Š5½A.ËÇÃ’oå:ãmŒ­.f­Ì äËëïo§Ç^^Í5 XC¯¢+zj\Ή§ßüþág¡Oq êyèŠ`Vd]pØ,t+·"ëBÇ®Rp’¦ê×VowsF*ÌF!ü9΄ƒ8+|\¢J°à\1ðuÇûÀz%5€Þ¯ZN‹ € æñ„¿8Þ1#[SõÑËÏQï_<Äáç´u¸èÇ.ÚÀ·ŸÀ }KÀók¶ ïüÏß8ÛÕ8¶\}yÌßà㕜%î Nï AÙÝ$\ÑZ¶j+ý㯟 Lð}’€4¸x'Ýê¯o}H?ÿÚ½òú@ï¼g¬5f”QgÙñàcçþÈßRÔà òäb¬îºF‹÷h¬À½£iôÕ{®a—è+DÛQ€wy‹mõ/»)«úð±eïÓ¿üîya-¼ù’…Bîæ×ñü+&¯sæ¸ËÏÁ™ªz]{£y!Þ÷÷…;ŸL*n…°œýãgnÉpŽ‚ÌPZPRð…ïýû*Üà‘}ÿ ¬“øQ z¯øo}ïå9Ó&.ùúý7$q»Ä/…¿ ‘k÷"ŽC,«pP—3’ôÎrÁ Ò_LˆþÚXÛ0—#^$Z¨Â ƒjÍN‘.Ö•Cß÷‹'Þ¤íµö<ù«Ÿ>ÄW×ó‚4°ã»yÚ7_#5<•#ôJ š°rø/úýIÉ͇pÀêß-¼×ßþïgéÞ/ñ\P>ÞÛ²W(KÿýÍûxvX»y:Þ¿! 3§bLÙ¹÷ÐŽÇù“ûéL'‘wœq€?NÍ}}ûW çLŸ©ï‡¾ûû–àX!ÀŸƒ¹êï‡íh¾Öè{„Ye8ÀíWÂ@¼>Ðsäõñ¬1à]‹å5ØX‹Fø6†3å)x½¶îè¦ÇþÑ­üèT~7#ü›i¹GuþÁ{2“oå#aiÇ»Ðßw#/Èkû[ÿñå÷EÚE£Ã5 ùýý‹bÒ` "ŸýÝ/×ö§CîK•̯,1¸)Ö ‰Óð²èìKTñЮM~¼|01+PÈE À§;@¯xÌz!& Z9Ý/>D;8Mð¸ªò>BõþeCïåý}KкèoI¼-Žækýu}bÅj Äë=GÿÌÁ¶:†ûÆ¢Ñp*ƒµM:à& '8Äâ…ΗÇL.ÅPtQC NM…ò&‘ôÉÔ:¶ô›)lÔsÒDf¨ÆŒJÌ«Ùâa³XDÜrÀ+PPPPÈ/ |Œ à.ç¢Ñpÿƒò1š3Æ¡°­reÏ|?+å#ó} 0PP& À]V¦GªÇ( ( ( ( d€H„[ÙG¾A Ù‡ŸÂHQ@Q@Q@Q@Q@Q@Q@Q@Q@Q O( ”<éHÕ E\§b0¾ü“ÇE@m®·Eá¯( ( ( ( ( (›JùˆMµWQ@Q`˜)€Œ8¨o£@Q@Q@Q@Q@Q@Q ) ”üí[Õ2E¬¤ÿÞߺŸ~ûìrQÔõsôUÈw.ʆ:ßãjâw\½”ï·ƒ9&ëÜY“èÞ.…à0þì;ø~-TÍÕÆ/Z8®X2[ÜÏçÒsË7жýÇD]T>¿ó𥄢‹¨´Žz À÷ª¥sè:ÆQAnQ—Ñ×àÛsfNüÝ‚UÌ›+6ÔR;+Ö3'ÖÐ/ ÉÌop¯_Ý~õ®h¾k.¹˜¯&reóÙôøkk¨½ÓMKæLÇ ŒÆyfçÂrxâL ¯.Ϙ=yŒ°Ž—9¨¾¹ƒv8N5£Šé^Î|_$Rîö÷N¡ P¨ÀïÀ{Bu}ñÎ+¹à³UìGö.—‡ß¹1ôÉ/ŒY‡&šêwl (ËGlº¨½ŠŠi¢@G·›ŠzŠh–8í} üᑨ¿°³€…‚€&Ž.—›j­(€ v×5K„`sødãYmB¾ £Yºã ú +'wJNôqñ´ÚC'i ò÷\!­è_ZM°Åð®kΣ²"'ýí‘{>ñÚZV(ÌôÐ'®§±•eôô›ëıºã BغëÚóé3½„Ÿ7*"T5·wÑ3ﬧËϤ«YQø`ÇÚΠ˜ßÝsXùÖ§n¤…3'ˆg¹<ÚûúòªÍ´Ýp  ÞÉBæ´ñÕâšÕ[Ðκ“Ü–Ëè–ËΡÞÝD­(® ×(p×G qŒÞkïo‹‰:øñ¶+—ˆº~®^®?|õ×·Ö þ¸ìœ ÷¿ŸzSXï>½ËJË¡gÄ}ûã}Šyx¿Ý{-˜>*JŠÄ5ÝZöÞV*-´Ó=¬,·wyØew³86Ð;uŸùçWÖðä×(æÑKiÞôqBñhg û/9g:=x×UtòL+­f×a‰S@)‰ÓN]©( (03…YÛ`0D[XpÂ,š6ï9LþXmÙ{ŒfO­?¤¶òŠ~ñÌñ¬I£é™·×ÇlÛ5çÏekD*-¤±Ue´ûàÉ>ç}ôòsX ˜@—;SÌ*c†yñì‰tÍs©“}X<ºÙrˆ•›KÍdÅß*Ü×·P3[30»Œw°îX½°DÞpÑü>÷_ÂÊKX@üÈe‹h" zûÙ (-vÐÕçÍæ‚mE´`Úx‘,bïázqlý®ƒlÙœL71.KçN‰¸Jîª;ÎBâ8ª®(>kF•ð{~T\£þåÌæºõŠsiÅÆ=ÔÀ†èY¤l¥_–°0ƒ-µ‡O þ”­„µãRðÕ¥‹Ι<–-a³…%¬Èi1~8·?ž1Ù÷‹î_V³‰®a¥– ¥¬|úrþ¼)t%ßw?ó·„þÞ© » ‹ßßÝz™àYi•ÛYwB(÷‹çræûyÓÆÑVÅ·’œ ­•ÛUBdS) ( $J|`žeAëë¿xšæMÛç6…l»Õw~óW£-¡»y6V¢@¾R–ÀǹÚ¿ÿßËçt€1M¨Âþö.»|HO4ÜHZ;\b&Ç$ÀÝPÈ®!€ñ5}ûüÚw¤^~˜½•à°YéÄ™6Z8c<}‚]HàB³zË~¶H,damŽ þïK´tÎúäM’Õb÷Á÷CÂ8VÚ;Ù‹‹‰qéïjéèbW«òh=Šö°â7¬Ÿüñ5yKV¶z7ÕÖÐ) ”¡ÓL]¡( (0{ûµûn¾¼Ž\”1•eÅô³¯Ý#î| ÏæbFVÂ<¥@Q _)>ïئŒ­Š4óÝõµt¼¾™~ôÿî~õ?{üÈ1¹h=zß¹ßfÕ>õÿð‰ë„—Ü/×p«‚Ÿý+«·³f[Zª#JL[N$œaEc ã ñ°L~ëÓ7 ÁîÛ¿zFœ&„IÆ)Ú ï3\^®8wÝvÕ¹ò–jÃ@àù=ןOàËòg¤%GO7‰¸£‡˜ß`éF¶Bð•^`·°µBƾ|Œcƒñ̤1£èŸÿÌâx¦uT¶ÆÁ¼µDܶ¡¹—o¡ Û­a½@–¹þÞ©B»öÔk–==n6¾¶ŒÝÈ~ôe¨?ª¶¡ÀÙ£T"wQ×( ( ( ‘fSßþr½â¡ß¯¶ò•°8`fxGOLÚÙÚÑMÅ…ž-6ŠøX@(4°*¾Î– ¸Z! |WûÖ‰†V¶Œœ&ƒ28]ÎãYÛ.[ˆ-Õc “°ptt»„5–•·?ÜÙ-œ[xÖ3ÆðßÎç€s86dýîƒây˜ÕƱh%¥ÏÔ¬§ây–pBµÛDpmiw‰mìà·dV¬(ÛÈlôÇ3àᵇE8â=cbe%AB ¿;k·í§ÓœÐá¶(ΘX-’ ôN·Û:»éNt‚ôïî<(&É`D\Ä¢x}ÌûlA¬”‚Ä)Ðÿ×?ñ{ª+⢔ ø¨Ëô¸.R') ä!àuë‹ûÔ±AV¸?-[C=ü$Ç[Œ¢kΛKgZ;‡ÜzdÇBÐú¯}@ßåLC€*ÎP5÷ÁÛ…Põô[Šø"‡®ç@âêŠâˆ>—_?ý6gÂò‹ØdÜÂÄÁuΧ·ÖíâŒE;Øj3CÄ‹HÄîf7²G_|~õÔÛb×ó§²{בñ ïÿÍûár†ŒZÞyÇËKÕ:)pÇVì`%UÂüicEœÇ¿ýá%rpG󵡆~ø¥Û¹¦ŽOœ7À"¶F`Á~¹Oèù‡´¢ŸæÌ?ˆÅÒãp3§Ä¾ž0¼Ãðé×CEI!}û37‹YbdD‚’€Àø¥»®É&ð<'»¹ÀuGAîPÙ×ô|Ìaáøõ·ï4®w_g×ZðŒ´¢éù:úú_~ã‘k±ñƒo‹üˆgþõ‹– #»Fó Ón¡or&6·‡ùº‡ÿpÓÞ)ð"ÜÈî¾ö<‘¤Ï–ü‰I2,ˆAÖ8¸*HœJùHœvêJEEEEE´S@/ô'û°X îÙßþ0;êC‹…„ÌÀÊı‚";ùM©x¤¢•ýñÌ@î»x®^ñÐ㋟åqÅ#ãV,èo¬sÕ¾þ){dèÿ|uDQ@Q@Q@Q@Q@Q`P@øÑG´€f«&æ8P`³'‘\Ž·$ÑWÊGTßÂU¦?Ä!ývÔ©qÿ„YZ¶õ¿å~µÎo ¤š¯ô<¤x*{y'ÕýŽ–ª¾ÏÞþÎ'ÌÀgÿñÕ»ò©Iª-#„÷ÝtÑiiî6sÄ+R8€_*2ã‚5ÈÐL íœÍ™ ( –èÑ;`~.q:D:º9SÆŠbMÈ`¢Ç¥òjCòÕQγ›«·´wSçì÷±l„"Clzðèƒl%¨ˆ Ÿ\ûCG‡ïpi¡3’ ~ˆ”Éøé²ßÕx’ñ®P( ( ( ( d)F¬ò!ª,{9˜oÕ–½ôÞÖÔÅa‘‹:`°‘Çh£ ìÓo¿›l¡V2…=ôg)tÚé*®H{çv·rv©ˆÄ{?u^vR@Õ¬`¬Ø¸—Vp¦ TN?¡õ¹ÄS ‹? ýGë9eè)ÚÁi>›8÷z,0[M]NsYQGGGOµXçgÃ>5ždC/(r#RùJ‡ßï³ÒÏ.ß,„ÄVs558æP3¯†¾™ERÕ™¦°Ÿ*ü Tí;AËVoc¥g?Ý{ÝRš7m¼ÈÖ%DAnR|µ«î=ɉ;ØÊ1üJe3Oi€mÛwŒž[±…Ú¸‚l˜‹Iµ+¨Ý6–|¬à & òûf‡ÈÊʹ5ÄÙD¼]ÔZ×ÀõŽÓßÞÝÄ)Fçp¦‘ù"ŧÞí(8E'ÙÐ EE|¤¾ÏqÚÛ.·‡>yã…´uïQúüíWäcSGT›F”ò!g' »¿øhåæ½ôú»ÉUPDû S»iTÚ;JMƒeœXJM4ý“yq5Ýr麚s¸#{ˆ²‚¤½RúÉWË7ìæüà[©[ðÓÅÃÂOhH6ò”¤ ,‹o¬ÝAo|°“éRB‡çS«©šBº8¨þ:ÃÀjÅã;,î±­&_æ4¨Öìçwxß‘zB%ݦ¶.vßò*=;9¥âøêrš<®J¬Ó©¨È6ªñ¤¿Tûs‘xgÿã¯Ð÷?k.¢¯pÎ3 ¼ËßÕ‹Nu5ºÝÞ>1oyÖÔÕœ£|HAÁÏÕX½¬x¬ØXKo}¸‡šÌciã†Rã^5î²³¹ð2šíÚÊVí"àê¥sXˆRnXC¡c&Ï•|µœ+¢¾¸rKFù tÈOˆBuX+aŽŸ*rZél‚â…{¿gaŠß²‡Ÿí&¶ðÒlª!:±™ž}{=BAÚ¶ÿ»Lòsøœ@|+™(@æŒ!­Ò. —]´p*]¹d6+,½•oSÁ/²ßÕx’ jª{ •¨ÚŒ*ÌFVâCØÔÞ)„³•ü]SUN{9n‚Ú'x¦qŒk¹Â³‰ÓÛ^¶x–( ˆçýiÙûtÍùóh\uµ³¥ö~·>ý‘KÅ;VßÔN¼°’‹«M lëpÑê­ûøýv AµàRŠƒKçM¡·×íäâ„óiòØÊ¡6E¯(Ð/ÖlÝÏEÿš< œ5±F¸îž;{O>¦ã -âoÏÁãg_¢¥‚Ü ÀˆQ>àAÁãõÒŽýÇè­õ{… ¸Û¹$£=¥8Ìí&zåýTYZ(|( ªˆMFû&ž‡ƒ¯v8N/­Úšüœ‡ƒ§ |c9^ßLk8Vj[%šÛ:ÎJoá(À®Uûí‹…¢Mcs†•—ß1Z¿û…ÙMë´y5ÙGS{Á¨³¬(¶‹JÙªØÑuŒ¹_Þݰ—n»b]¸pº°*ƺÿP÷©ñd¨Sç§ŠHÔðãG_aKùlQ¥üé·ÖÑÔqÕâöG8Áb Q…üÜ9“èDC+ýâ/oÒǸ'&þýÿ^¦ÿøòT\h.ç±âÀiÄÄÁ–=G Y‚j*KÙo¤«x"¬´È!Þs\÷‘ËÑä1•ô›g–Ó¿}ñ6¾_€VoÙG‡x‚÷Åß.Š©¤ "YϼiãhóôiVŠw²kó½ü¦¶NzþÝtÃE háŒñôçW×Ò·iþôq©DAÝ+MÊhΰx´³ÏùËïï$—±HX<ÒD×!ßÖ—s»:éoË·ÐÔ±U" .#*dȤ¶ ÀWŸžzkƒpµBf¤ƒ§äŒCs;ý•Û½ÿX=Çp4TR—u ¹…¬ hÖ {ÐÅ1ûbª:KAH„NÇ­Ó¨ÃTN'­SÈoèß’á1:¨Þ2A,EÁVšîÙEO¼¾Žö²‚ô‰.ÅÒ’qÇRãI"½§®IðÎÁõð¶«´‰³:žõusÒ 5%tÿG.?—½·•-#cè ¶þv°ÿü–½G"¿ÅNÝ?¼cYùÀwÖ ’D $wt¦µ“Š9YÊ&VTLKAÞ‰Jí³&ÖÝEm* ¤†j*„"y=OÔùE|öÏåXÜœÒÚÆJ‡üýþ»Ÿ‘—¨µ¢@Z(ðŸ_½;rßų' P¾ÿîÖKEìQª‘&É’ëÜ” â$-'¤ãôqÅid¹jg_×YÓV@0N´< 5P]½)Koàv(È H¾jnï}•®‚”©jq¢<a[Ö°øpGH³¹×¾ˆã;âÖS…ªïsÈ>[Ô Y¿³NÌâÆû~É~WãIª{DÝ/ @ñˆ 8Í.‘XÅ#@Ɉ;ûÕKÅ#ú˜ú­() (Å#S”Oü¹y®|°ðÔ£|xÙݪ›§ýÌvð9(Ýy ädQ=@ _Ú9H3ø ”K„§ ø"îŠû{[ëÁÛ­¦ÜÏãßm,&—©˜}„ë…U4~åC'Ùó*L$fpíýl°Ü¯ÖŠùLñÕì8=Ÿ›˜×mËí)̺³¶ÒM²Óøýl ÉY[6‚“ß炟(¤ÖÓ5ã4@‡Ó!=_ù¸¨^bÏ:q?f¨<¥½;œ%Ž]r6²áœtÌŠûyÙ~b'+ ­Í¢}fvm,(àr…ýM scôý®Æ“lïÝ‘…Š *Pi@ , r“ynùÐÜ®´\®™@v›Lu«Tœ”ÛU¦z ÿçJ÷ôQ.ÁPx m„ ‹á>Îó@V¯|cOß¡ð(ÚõCö»Oò… T;2A¼U>äL%d dç± I.‰Šl·xÙ–L0ˆzf_ Ⱦ²kÛ÷Œìý5ž’®e°ŠØvôzëdo+ãì0ÔN%kÄí ý:èû]'QJSPPPP˜y«| ÙyÌZ&` Lªa> ¥,åÞÃL¦L:ÓB/ïó}Á{Ùæ’ß`¡Ñ¾£´|×I ™­TU9ª§òs68Àõ²ßµõ'öshq‰.g¦ï­Ô”¸Ï/¶Ñϵ‘'зýMî0í:¤ ²·Û¿_å¤oð¤Ç=Ïû)™öëñïEµ;t–·Âïd@ŸÛúßÉÜ7]׎ôö§‹®é¼oªû ¸êù4øVO_E=5Ò³×ÊG2$+²èÞùúÜ2Mi(æxÛ.–u¾üz7-ã™W=Ü4ÝJv“|µ“……¾šG·ziå§KhzEhŽßo~_S€n}ºƒ xfØh ÑÌÅy–6q8ŸhÜù³g<|ßÛ‰ß#w¯\˨¯í úßß¶&×¶„ü y»i1§h5rì@¼pó 3»0½Ò#4ÄS¥ìºw´-D[X‰†Dy ÷áÄoôo«\ÔɶƒK€ Æ^«œ•Gƒï_f§±…Ú‡VPØ@mP¸ÛH•ö“Tå«¡3–qÑ(Åõ;ºý3FÐcÛàÎEôÀ9g´Ï©, Í²Ð L¯‹Xhÿ'¶{é ,´Uù@ÐüAû<:n›N³º7Óò-uôâ'…KÇQÙO…ôðµN‚’$áDGˆ~øž‹Ü¬}^ò»»ÉÛÅ¿ÛÜ4Ž™³Ñ–—žµÎ/îõR++€i<&|æå.ú¦¹:½aúÅ:Wd×Nžü¸nŠEüN¦ý‘ª”R@ *¨‡³ûÐIjiï¦6æ‰| "C|t+Z-&*q:¨¼ÄI¨ë1‘ëàýÌ¡n¤·ˆ=š§Ë>ƒe™ n¦=‡OQKG7Ïu‘—kXõ2o(çßê[¥è¡§Æðl+å£:O)3 !¤¶QS „|í­._Ü+¬ÉK—Œ1ñ,u>9ßJØ^~ÈG¯ð E¤¾+Dmž0ͪ0 Iù÷Vëü¡„í}¬€Jýt ž*eËÿ¼ÆI[ØúöfŸ:¼,ì2¤›§¤@áÆd2±S@ ~Ù ºim&[ÈE'¬S)d8û]¨·¢Ûÿìn¯8By4˜XÏþ«ôS¶ü@q×Cmc€&—ÙÉÂ&Çhe_^ÛH¼»ð<:§s59ÛöQW1iKT8ìï!1öÏe¢wk–,þÇwºÅY&²#/s˜ ´˜];çWÐGgXè—ëz¯K¶ýòj °@ÁX±q/­Ø´‡:¹'4逭YF[J ›ß»V2…=ôÚšTè´ÓUçΤ+ÎMVž5ŠHr-ÚÕúö¯ÚXKí.˜Hpø ääoÏm¤ :YR9Êc¢Ëí/vØèŠ%³2Úþ”5no„>ƒÂtá^¯ŸVmÙKïoÞOîôô]¶÷[,z¬Þº—ße+öa2Ýd4µ3_kߪd»*̳U¡@ O¢Ù9-tå¹sF$+å£nšTR@-®0[;—Jj 5Aî#Z‚ôí‹<«é¥‡{\f0ƒ=¡´w6µŸGªÝyNÉÌSG˜â 'ý,`…€ý5vÛû&»Ý\ú§vòö¸è$ÊS,Ôÿë*.r’ÝnãÊÞ}{à†ûMmã׸¤˜ýÔØ¢Ý4Ù³‡&z÷Ó!Û:iOsÄ9Ci?\ë;C¬Äûèk}-GÚƒBÈ™Pb¤:~ßT|?jEs\éæ·Ó¢™“Èl6õqÀ}Çé‘[ EJ^·ÛDìZÙÜ⣆z;-ßÎÖ)v­Œ¬l­æq¢¿þ³…IxîoØ% ×½sÐOÛzÛ™Šö럥¶‡Np»êNГo|Hlå@ÎÇjæuÀ¿5t¨O6q¶¶ UûNвÕÛXxÜO÷^·”æMÏï,[É“²ŽÇlÿÓ¯¯£VVº&6šhRS˜lþÁ¿™ñ?If˜Ö&¬f©j2í©×Ê™ÒåvÓK+·RwAí/¼˜ÚM£†…$PlØíKI ‰f¸wÒ#/®¦[.]@WŸ77’DÖËt@tûGñ»xGm˜ÆaL/+û³N³' Ë'K‰Þ›ãÖö'ƒ{¦®•}ÙR磕›÷ÒkwÓ(Ž£®¾Ë¦~‹Iv‘ÅÜ@5U¯’Ív4í]¥ÆéÜ.g"µ´ÝÂ|ìå÷xá°¼Çio`šDÇ ó唆Œ§=u-!ΠÓ;ØwñÌ Qè=€φž€Y¹O­GÀSUÎ^¾ˆ— ¬¼v³No=K'OApA Z3»\Y­Ïdc—+‡•;é$ò„ Tç˜OÇ8vb(oû/g"O‹<úÑBz3]Ý=×"—{b­! T<ÃôL‚pc—²ž¹õsü\ðaJœéyÿ«‡Øÿ':‚ôâ>/!óÞ¬Jm®(UíOG;óùžRhY¾~7½¸b 5šÆÐfçeæxDÓ ÏæÂËËVo'(D~@¸Õ¤ƒ£Û?­>Lw~8|ŠGtû¡ðÜõaˆ€Çp´?úù¹ð[öøÂãõ y}í.šÖ¹¾Ëd¿õG‡}7UW?2,ŠG4ß@Ù©©þ‡‘ÄÇJùˆæ„žßp@ZS§N©èçTZ}ÔGsØ7éuáŸþ±™œNõ`o&¸pmËWãBTQû£)€YûI¥g»9EŸ‡ß÷-°‰ø¢±EFú*§Ý®?Ñk5I7OE[>œìc~´ÅCíî í·/¢“–øÝ­dûâmÿS»|tþÿµG–?qP:,?Ö.n5‘ß'À‘öä”a¹ YÑ1±ÐæO›Ð\] _x>ð>¯®Noû@/㗠Ϥâ±}ßQzûý‚V™î»LõÛYôX¿‡œŽÝT9êN¹§mzÏÀåÕ÷·§õ=Nú ÝR)ýí`+¿´œb.gÜ Ö±Pøð7=tžž½³XÄ}<±]s›©a×ÌZîiRÊÇ`tÌ÷ã{9ƒÙLVNuF±~›|õd3½xO‘¨ 2·ÒHÿ´¼›6Ò”áà)(pC’Ög`Ús²…N[&ñ2±_¼:oûý;…àz¹ ¨AúȘÇï$ÿ2Ð3:VÔ|EŠaõÀ‡)°—Ç€Ù¬L ˜ôøá•NÚÀ5†ž¼­ˆ0}å®HüYªÚ?êx/ÀŸžzkƒpµÚã8§÷`l—±ˆþ¶| usà7\lRÉϲý}c=ÁÕêš]é±&JJàSÁÖÅç9Ž!íO¯L^‡>xÙÕª½£‹^aå 4ʦ¾Î~‹¦Ç²÷· W«ŠŠ2ÙM}ž \Ìæ3ôÜòyÏÇ*æ£O×÷þ€àóÄ/!ÿ†“½5:޳ÄìéZce DõB®Gö+øp+Ùxu?רXb£Ûf[é¹ÚÞì±xê—‘ —ÈÆ–˜õÖÃpñ”æ~¥)!ÛžÙ{ÛçèQÒvíd3ûmóÒüòC7a èà§ÙèázSÑöwÝ`ûGùO‹ÝÑZ@9>NépW‘xüv“›þôÑbB<êµH¸ýY„^õ‚ì± º92^Bñ’ÊöË{ªõÀOÀ%oŦZ\Ž¡fzø É>ûíóÉѵ–V±Oÿu̼w8Ùø}ûÛ\n'i‹G4Å€Ïå{òüRoÊÛý¬\ø-û 1ˆQ\³õuz|tÓ(›ún¸ú-=ºÜã‘I‹G4/—òÒW¨þÌgóž•å#º÷u¿·ÑCc9à®ñ‚^ñ@ÕóK'šXPê_°Š÷¾ê¼Ü§j4üg@»}¶E(ƒµ2g´â1Ü<yà÷o¢3ìÚáO"“ÏPÛ‹>7N5S]k@籎ǻ¯€³ö£±åVh†gÜÁé¸_=à¥;¹ÿãô½^ñÀ5©j<ÏWçhÐfL"³²Z WpùPé¼€ßÚ‡Älw€ã˜€{² Ûÿþæ}"«Õp—oà…ÌMëvLiû‡ŠG6œ/û Vn®Û±yßñ¬í»áè·hzlÚŒcë2ã1 ¸}°ó@^óñð|u£v–GšÝ›þÒNût™BªÞëžè cœT¢(°â°Ÿ>ñ|gÂe%†‹§0S¥3¾üñòqLD—©$éNL¶ýËØz$ &ƒÌxogëòÓœqå}fˆ“% §Yé¢ß`åìúTµ°ç¨ãäŒéá“ ¢Á<.«Iü\n/a|ñÎ&›DAßþ¾ïLÎ2•Íüºx†?UíÏæ¶ö‡›ì3ô¿Çã%ð®‹M¨ÙÜwéì·Xôp{ƒTèØÞ 3¾¸u³e&Ÿù8¯•irÖÖZð‚poÐnÆ9¬LŒ#fcõø÷sªÚ È~1sŒÊ~~‰†ÊSÚ€ä,P~QŒJYçþ“²§ì‘HQ:Ñ{€F—X©‚+FôÄ· V#Aö»ORÖY#ù ú3Ì•¨ã‘Í ðc<÷©ImHôí‡(êxd3?à™ªögs[ûÃMö’hÀåê +ÙÞwéì·˜ôà‚¨ã‘­ÜPä0Ÿù8o•Ñ`eÞÊ~(àh³ôuõŠÕžèö©ßé§€¾œ6 WîÝHÿÓÂPyªwÀfåŠëy ð¼0¨e›J‹Ì_iáþšëÚÄAÿš?±œS [Èb±ôTr×þXXêû]W㉤Dþ®á®¾¹½KT.OgÁTPø¡Âzkg·¨¾ÜцDAß~‡ÏÀ½Óð\ü€gªÚ?¶|pzÝö.7ӄ㳸ïÒÙo±èQ`t±Å;{eAàó™óVùˆõ:ÛÍd&/Ïgï[ÜLŒ£“+K+Èn ;¬ÌKž¬æ'P0žÒ”¤iô‹e|…ƒªý'ÈÎ e+ç($*š£ óÇ8©Äé`Ÿ_++f2˜"îW±®µO'±¨’_ûàz`¾kÁxŒ¶œhœÇh¥Îxḣ ‰‚¾ýΞls‰Þk¸®ž©jÿpáœÊçÈ>Cÿ{½óÁ‹3†ítõ[,z ²" 8æ3ç½òK¹”;9} Ã~N ž¥ pckÊœ¼³ÕxjbMYÖó:)ž‚§êˆk³F\Ÿ¢ÒIFÞgvomε·»å‚®u\º™¦•[iì¨bQ8•Ûaý0›¡|œm-n§K°VãI4uòë7piô±Ï|,s‚<}å÷9Í* gjnWhÇP!ºý–Üð0%à™Šö•^Ùp¾¾ÏÐÿÈvå÷‡M²¿pHG¿õG£¡·Û@8eòpÌg>Îkå#"(°Ç#ü¹‹9we ˜ÁÍV³ËKPQlg¬{§lÅw$â%ùªª´¬©’¢B*ÄâtjnWfž”à¾DßöÑý®Æ“þ(•?û!i ·)‡8ô¬–Z¦+ ®µc¨=#¯Mí*½²á|}Ÿ¡ÿC°|ebqâ ¾Õ?JÑCOìÚÎëÀN˜ÑÄ"(šSd¦†îMwí¤-E—eM±(#õ'd¹šRU,𕸣 ²‡z¾š3±’j·Ðt7óSaö𨕠OAØF;Í,˜[9 Ûʱˆ˜XfåµÝDžF:¯ã:j›I§l“Ù%%þ¡ÄöÑdw-qûÖ¢KØ™+9w–"®T>¯k=ûäÛÉÇ Rèó>¬Y¥ r[ˆÆ—Z©„Ž’’"*..–‡ÃN6+Çíð¸Ï;¦ïw5ždÏû˜NL"ÂK:’Ž{³Ä ANâŸè#äõ`ûWͽ{¯KQûÓˆaÚnÝÛgšÛ,~ç ¤¡ß=²³÷ã—²ÿ~±’3•šâa®,µp ÷8§›4Ûµ…v;—ö{á<\œ¡šQmc!xjx€¿lËp⣞›²/$_ÙY _<©ŒÖhd~ÚÊü´$ö…Ø› O¡ÊñÎØl6rrp¶—sÆ#ˆ±& [›ê]Ašâ©¥‰ž}tÆ:žšL5Ôaªàl;g+pÓB¶¬jßqªáŠÛ§,“ÄvBÿøcEÆÂÿE¡n †ØÊÉ) mÆ9̪pÓî.VÅÅP>Š©¤X³~Àå ÊÕ`VàÝïx?Õx’PåÜE9%¼õP⦺’%øHo²ôËÄõ²ïs­ïRÉ·zº+zè©‘ÛgK ™ÅKðê±N@jbáB‚JÅ .Ït–9ÜÔíc—ï)šËn{‹3fÁì4„ÄQþS4®ÈHÕ¥Î<5áøÇ;bõÐXXcSBç!<;§¦”§Ð€h¾_YBMí.¢3'™ŸÂå'_ xJ›ågË+ Ò Yù@æ«`H `…0n7sG·Úüìšå;B£½G(ÌÓ£^ƒƒ­ ²bs°#[:œÁž9唟|ÜIl™(ðSKÈFS]»Ä¹-¦ª!ó†•é®Th¤*³J,a¡HhŠ¡D˧¢ÂB*‚ò«[@`õÀx F\­`ñ¨q„i<ûÔ?à ¡ xCŠW8ÏæìîçDèãX®ïŠ´-U<‚Äâ«cJ…pN­™ã'à–JžB;a°±Û‚³‘²3HP<`-è6¹ù¸—Š‘Q%À)Ypr ¬Z(ê2ª`‰°š`³5"D&¾>æI~ÿNy¸Þg¡:cK – ü–èÂef‹I¹¿^(ë£8> …˜ªíAªthÊÞ(LÂ]Œ'ð>9šåC™Kw+-ÃUü.±ú]Ž'œ»öt7Í}ŠÎéì¤:G~'à5Œ)ÔÊZwˆ¼s}öæàdgo™a¥/Ÿg£›þÒNÈv[Éñ†¿ºÁIã‹téc½©>« ôÞ?¿ÚDÝüÙ{x­›žÙí¥Q#½}_ Ýý\Õµ$'è%Ò–D®é¯› ¸ çô§î¢#_“¼GZ©ò3çPéuSÉh3Qû»‡éô/>—NüÙõäÚÙ@æ¬z)„T¶%…h¥üVéhçø_MÞcítæ›È:©”jºs+)Ðî¡¶7ë¨ñÑ-Tö‘T~Û:ø¹—àï—2H¶=É^«!w\ñ(5wÔÑãoÓý7¼pÖ)û½E‡N­¤Å3 ?¾z=OAÒÑž³0L;²Iùˆ49äžikç)ä$•MHba„( ÒuÄË…wP¹¹2ØÍu?<ÔÈ©ùu­¥VS5 @ãDÛt“Bͤ>­öàÛ.vF4Æ`‹‡ƒñ³‹fà |5—Íí*Bœ7Ú:ºÃ~¯·5ÁËsþ²TñÑ_M«)&‹±µv ?Ÿtñ„mX¬a …ÚÊzƒPˆ!à[YAv¹¸þ»cá}²û"5/²‹„Ùâ!AÚµ¸ iÅà ŠL,V³ŸšÜœ?Ê’ª|'ùušÊØzbcËI°Hù^¶‹ì¬ÐÀ½ c9Þ›Rkªœ\üݪ4+¸,òbfËaDù°““‡J½MXA¥Å}/ô×ï]./½|°“N³€ØÚÝNK&…iQ(¿ÇÐLŒ)>_K¼ôËÅóÀ§‰|ðÍ<«ÿÐ6úÍ·P<±bñëä‰1ËÿÍ mTj7Ð}/tÒ•“Íôƒ+´æ˜ŸNv†èù=úúvú¯w ™|÷Dð—“÷¿]º‘P*<ûš¨äê©TvÓ :ñ¯+(ä ЄŸ\KþÓÔôôN¡tLø¯k©åå½lK®œÄ=™ö'ÚÞL^§ow²áŽyUTxî:õðZÑ$(a·÷Ýö4ÙgW”ÅŽ‡©õ:õ©ETzíTj{û`RÍ×ãŸÔz.Öß/YzŒ«\B“F_Bo|ø-®¡ã¥'ß¼#‚¢Ñh¢;¯ü#nÞF;þ.ZðÍ›rí:t¶‚¹(Ž =þqœž3§d£ò!ùT§Ûkp±Pã`ŸúDAŒ,ˆ`öÖ*²Û èŽÝG ø@H"r“Ýë¦Vv1²2…ö?YÉËE¥à:’ @T™F‘CO°8—š|Ti7²Ò·§pq"è²ðăèzº]ÝHñ)R.ñ\ž礌§@Œøj ó•ÓÔI'Ú¼dô1?¹ÒÃOÀc8xJ* ,²‹vk¿µX¡£N†ÛãábV¬€°ò¡Õ€òÓh%ÜÔXYU¬(ÈAïåÅfá÷€×]l9éæP[°‘m'¨*bdFeK Ÿo6°û/\'”Š,DŹZÙº!Á‚y·Rˆjïá6ñæ8¸~gÖjy®–FÝ·0iå#!D‡é¢ ç…v²báòhs.'7EžM*ZÊVÓMÛÅ"vv³Ë5¤Ø1F)’(ºu6)m±àCXýò OßþÅ/ßûô›–ýÓ·-pöN 0@èhQO5žE…ﺧÇõCï·žªYjù<P½V-û” ¸]AÃwà |“Ÿ?HL?v? t¬}}ÙÛ|?L=a‘ƒ¤y²ÊÖëeûÒÂSht¦ø*S<…çâ#.pæg3»ZC¡Là} ÂbÁÊ…Tp¾ÀU(!šõÇp®ßàx+ö!vpì+ˆÿÀû‡¨þÝó°`A¸T±’ ÇÁï¯tµ’ ž—nøóíhä¶!°éðãëŠi¬ãVº¯<O@S9¦pš«ö÷_y‰SÛŒ¨ñ$n¶B!Û*çà|ˆ8§NV@&–ôŽ÷xûÝýóBÕ…úð†²Ü“‹L=‚¬lA׆„ÖµKsÝÁoó(šz•ìS0ü4k‰ì „ !bwœìjuôo KV󳻩è²IT|ù$¡|˜*XYa–÷7çgßu"~“¡ÈQCm]½®hNGÍžt ½þÁ·útR!ïAºDX\ŸCêS ›”tF\ƾ––3û¶mþ©añÒŸ<þÊšðçn¿|ð‘wè„Ô£€HaH³@h¾ëð9×ÜG8ÖÁèQP"Æ)ÉgYhƒ%]VNWÎÚBéÀ~‡ •  t;ÕØF[Ö®zÜåâ©.ÁгH¤÷ ×Ýò`wÚx ´‘} ^¾Ê4OI~@[5—&ž1ʆI¬aõHåÛ8xkk¤Ù…ò&3¿c2„Ÿ¶ñÞI·-(]ùÊh-”øK‡†‡¦ ÈkÒ¹>Ô¤_¯çW í2šéÁó´x‚Q(OÂÝÁòq<M嘲cýÚßuv¶É1e$'q±Öav•šZß„Ùu~ºsŽ•?]>ÑLeœùêíƒ ©“Ø…ë™]ºs|ÇÛÉTn'£Ó,• ˜Hd]«Ài¡2Dw.Í©v×EgWBÞã0.(È$B`ÅÑ:¾„•‹ uñ$Ñ¡*ºt"¹9ÎÃ<º˜l“˨ù™]M »×|':Å:ßþù9€¿‹òâ)tü̆HóÏøý"Þ#²“7Ê‹&‹Ÿ-Gô»Õv²QùÀ,5F^Ïûo,[STVöGÞþ½@áOÝr‰! ˆ˜z…m&f\$*]GàwŸsM°J|ÖI \,¦D„'NH ! ‡ ˆ…pÅ#YÀì$„„M{Öîxqëš÷6fb,È_ú‚νÿÈS@ÓÆS ÙpóU&xj Þøàœx=Œ\óC³ò;4Sßañ豊èß;Ü_X?DìG_Ë‹TÔqÎpt«ï¬è&|Ôù™s9…êƒç±‹+KP‚`™É·ñtÕ)G÷ïyzÃÊwÖóî‘8žÄÅf{›tÓ4»H ­ gˆyí/?t‰4¼ïÜÏBÇ~üd‹Ž¶kCs »[AÙÓ„!,7ÁÃ5"ÂÙ§Wˆ W¶å4ù·áQ9LΜtø«¯‘ñ=`Ÿ1Šº6iV¹O­3CO] Y¹ß$4ün§Ô]H3—}BdÀj{ç u®9*ÛgTͰwè.^òþÙ¾nh­¥êòy4L~Îôûh{Ý_Ùêß;a€jÊçSKÇ!ŽoL.etäay¶‘mʤ|Œ²œH|غ_êÏOßpÏýÀóS§›[Ã÷\11 š@ƒÌ=¬°À ” a¥Cξêgq!Š4! ÂÒòÂJž­Ÿµ•çéQ'îV°xܽãå/ýíM>vPÌRb ôAï|‡aá)Q Â2&"]|%ùd¸x* ‚6`A  ä›z\­¤%¥—5å¹Ð/z·µëÓÛ@÷ü?Nº£^Í\lä?¯vrú_Žc1]ù7ž€ú1åðÞÝÏ.þ¯p·©ãÉ@ì9öê~}i‰n›m¥çj{­lö= ›Õ§^ì"'Ç}@ùèå|¢Ï/¶ÑòC>ÚŸ£ivÑÎP§—šŸ¯¥ŠO.ʇ{w#íû§#åt­až(ÓÍ­ŠéØwjõ»Õv†(ÐøøVšô‹©éñmhqSׯ“b1pж0×h‘kVÅ]ó¨áH~—¿°vç/éÞkž¡µ;EÝîF–½ôë¿-:«ÁF Ç|žVnùñYÇÔÙª|Ë£ˆœõÍ¿>ñÌÒ+¯=:ï¢/ýü‰7KçM^:wŠaþ´± ×ÁÂæKá§7Ë•~æ5ʇ^hÂ3eºÜŸ C"ç>R_" ‚ËCç¦Õ+ŸÞ¾îým=4„´Ä¢·|è¿s|(/A*iç)Po8ø ÏÏ’ü“jž¦ú6 Ó#z ªLÿfC¯àø®^=­\s­Ž~ÒÃÕ÷±Æ”­kWÿiËšU°xŒäñ$.Þá‰~ú/®Tþ9Vžgå#žÕÍõPî0Ð¥Mô¹e˜3Êmhz|;MùýGD ]̦˺Ñ­‚֙ǶP°½Wa‹>Gý> ¸k›¨í݃¢(d#§Ù• W<°¯øŠIä9ÚÊÕêÉSòr}ªqÕy‰N»‡>Øù?ý¶q6×ühêØÏç"'‡‚XÈ6å8ÂÞ •£F]®`CW¾³¦vÃú½Ýxó-q- Ù%ü!Ù­áÒ§ÁfÖ¥ á …hE#úw"÷•ƒ¼6ú·Ü?ÔµÇï£< 2ž†`0Ðydoíêuo½¾Òíî–òäa]ô]A_Í®Ï#2ÊS o4EÿjDóPôï¡ÞO?0Bì6¹¬{.ùƒNqbeAuîÞ@¿ØÝW`Œ¾Kt?GÿŽ>?žßÑ}ý;ž{ôwN¬1åèþ½+Ö½ùÚÛ.WW_§Æ“þˆµÇp`IZ\aºî‰^w¤Dï“ ×!ÍnÝýƒWz>ñƒUÙ€®ÂAGÓs:ÝA Ý¯°ŒxcÝ?ÚÌ]‡_$, ú§@6*ú™j@`jQLõvww„Þyîégù÷k³;cÂô™óìŽÂQ»­Äd2[YÂÓ¦„ûoo^áZ ^¯ÛÝîîêjaaïþ[pñµÃô, (  4`.W2Þc`©‰OÌ#P<•G™‰¦œ,;·´±ÄYŽg³¦ª<±âäVoKâ’e&Ç3yLñð˜Òáîîj>¶_í¾[0•9bÇ(vXÌœ„KÏÆAÁÌŸbâp”¸'£œÊ{ ý®l”bÛÇCªÚãöY¿Kö™¶†;0ôs ïÒÕoÑôà9k®ùfÉú~ ‡¹nUŠÞãlll¶²¤~¦Z*zÒ»wÛæí¼ìe¢ ˯¡¨ÈhmyM6ÒÔÍEä«-š;¤DEß¹/×±Úie¶£/;deÓÓÑozzt¹êiýžG©¡¹˜ìÎYYI=RÁ gËc>ÎVå}aQÎ,bÄ“¿!\c&ÎÎ 3Bù@;0Ú@ùèýbð<I(˜–½@(P6 € Y7Ð ÇAÇ‘ ’‡Ð~I;ÐDñÔHåˆ8Ú]uÇ÷¿Àà &9(ØÝR×ð×ï<Ï›Ù/…ÅѶ¨Sä;¡Æ“(Â;¬d {xገ+D‘?Ÿ‰‡3§EsLVh¿ËÂÙ©ø-°É¯rªnžÂû?Oj;-Ù,Ú¤°ÁqÜÊÎÁàõL“lî»tö›ÛÛJk¶ÿšÖoŽÚÛ8ÉAø2N´æä_.k„¸”}Ü‚!.@Ç|œÍo¨üb¨ÓoCP„0 Åú<ìg±”|VB¤à#é"…¸TIA Þ,üÆ~ç!¯å]#$½O¸®O¬Ác>ñ_—šJG/W8võ_~ôùz+¢%vÛl»JŽ òýP㉮‡0{:±¦ŒÖÕ£ 5XÆéŽf×&ðÃײ¦Ìq»JC}ûŒ2ЬӒ]’½sê¯~À.•íO=–é¿#úL.å\_åP³›²¹ïÒÑoþ€›>Üý­Ýö{êìêfW+¦;K…oYÃ×p%ø™ätnOg$ðà†(‚|æãlV>Ðe`|±Æ¬5¶åL5f"õЇŒ Ò‘ÏŠ7Oh"ÐE/0€FP8°Æ‚c#ÙâÁÍ€â©)ÔÆ@°O^h/\xíçå9¶†gšVüïZù;ÏÖr,‘ï‡O¸ƒ¥WUZHV®ÍTí?‘ÕÊðC|FE±?‚½h¢¼ªo¿ƒëRí`å#Ñ»¥ÿº}£yF’ÝŒRÕþôcœú'È>Cÿ# b±ÝL–ˆ@›lí»Tö[(àºÏК?碀M\F¡G dT–Í ñå¡Ú3!>¶0k•à£G>óq¶+òÍ„àŒ"Ö˜Á‡`-­P:¤â1Rc>ôƒ°–JŽ+èKÅS}é¡~EQ`ìg~û=2šjÄîpøLÇÊG~ÈÛˆŸÊGc„|/äø1bÇ“ˆÇB bÎWÆž¨$ÐDí¦QYÇÀ«Œ-“ªœdõ¤tÞ«@ áèöÏ_N[ýgèd)ÑX¤0É2^G+ˆ¦Wp5õ´?Ëš:Ñ}&éPYX@GÃÁ¬ì»TöÛÞ£¯Óª-?¡¶®£½ôâw ¢d ]2ï3ä(˜MMÔÔÕB';§‘Ç3‘l6ݹ½Wel 8¹ÝÓhzŠÞãŒ5dçŠòfH[* ˜Ñ‡²!ã<˜Å"lç;Hak¹€6r‘ûòÉ´4’ ”Ú‘ÎSÉÐ2¯®òí—g8Š?„<ßiZû·“Ú¯¼ü/›*Ç ¬åX‚µÜŸ—ï¯Q˜9–…açL¬¤Úã-4ݽ“¶^F!æ¼²Œ,XNwíÙq¦T‹ÚUo´!QˆnÿÞcÍ´jvî^&3¸"KÀÏM\5Û@V.ö™ÊögI󆄆¾ÏPà ŌÇ™©±3 h”M}—ª~;Zÿ­Øü#jhÙÕ‡VEŽÑtþì¯Ð”Ñ7SkkaÅCÒã ¹ln½…F×üŒ|ú3¡°™Z' ÞÍw>Î%åCr†üÊ¡O¯tàœ‘ xèi¡ßm Ч†N³¼¾ÂT5ùçá°°®ÂõfUÝ¿\úç¼n°Ö8ýø!߉ÐìØM”³Èâ!°ØmVZ<‰c?4Òl×VÚí\û ìíÚBÎPͨ¶ñL.»ÙX,gà.Û1T´äuúö/š\Nrû—Ï3Ð;ôì2Ô»§ö|àÓ\H4·Òš²ö§Ãá¹[tŸ,ì.haž1EDGlê»dû­¾yWÿ‚ò¡›µ”.˜û%®2þY  är»Åû §ÇX§ŽtVRsÓíTYùŒþòŒm77ßN>ÍMá{œ±Æ òà\T>¢›4â?’ÑQ¿“¦€â©¤I˜»7˜ñ³÷‹”(Üv[ñ›ÈøeÞ„…LAvR@¾¯r2,<“nbœÕj¡ñ•%ÔÔÎùNΜ¤¹ÝaÚãXœQ ,P2_Ñ}¦ñƒ•Ênêöy™V,öe¸ï’í·ÖÎ#ôþ¶Ÿqñe}(d*°Ñ’YÐ…ó¾Âïk[ ac Ï;MF÷\¢ÆSŨ2fÅŠG·knºøXÏ»ØÎ8äƒò‘q"*òƒs»»Ðï=¬kÍ/wsn­î·ÚÌ. œõ!åB‰nTjg4—º{Ú(]X ÈÛ¬<«n·ÑŒ1¥ä÷³›Fë)ZÜÕIì 2‚¸ZÁâQãÓø2;Ù?à\3¬©p»ŠÕþzÙÊ}/ß“™Ä ÀÕ tµ_²ºÇç‡B!¤¯—pßÉC\Gî ]©àÛX< ¾p»mTåðsŠY+ –Œõ]2ýÖí:Ckwýš¶îÿ ÏõÎØrÁÔ»é²…ß §£ªOÄCf÷ò7Œ¢òÒW‡=1pÿòû+SÎÇàÛ@€Ó~ ¾;ûÐðìQÊÇðÐY=EQ@Q (t…¾Ése<Ê`0·†+˜h+uð{¼­míl–Hò7ðf³YöN§ƒ¼/M«)&‹±µvÑ¢®µÔjªY°šÍÕi­‚:H§[í;Ae*`õjŒ#À® à´S!ã!lgév¥#Ï6kÿ“{~)§"n&šÉY°&5…ÓZõ –Ù‘\næP¤ñŽ`ÚÚ/‰ÕÖÑòw:Ö!ðL*ø¶¿>óz½äø©2ØÍtó°Òf¶¾K¶ß¼¾NÚPû­¯ý‚}ksÌœp]±øŸ©¬hRÌn‰^jò¢ú3Ÿe÷Ê:~¶ó;´/mu@PÇét»º’Û3L†`Zø|ë÷r¡“,¥|da§(”†Ÿ ®«òÜß¡ÕÚã¿»ã[5úÙÎáGJ=1^ @Ù‹ÛÕÝÔéö\¬$88N#Ð#YX·ñ½ Nòz}ì—í§1 9Mt¢ÍKF_•¹¸Æ+~.?å5r‘0Cê>¯á¡r¹e›¸•ð¤*5ù¨ÒndeÃ&ð***$g¡SlWàŒ,]hC¢oûOVY@rW®þë3P¡7LöµOøLaêâ ën.tˆN6ñÿr Òin?ðŸXˆ;Å?ñøt„ÄS©àÛú,À<ËÆßMv.°×ÊþO'ËÍÜwÌ'¼7Õ}—l¿ƒ^Ú²ÿIZ»óWäñöM±6¡úºêÜïQM…V†©¿N‰›ü^ƒÞÉÔÈ ½àŒ˜Úy«ÉC(lá”®9âu< Xé(OÓ{,ùã!c.ÆÆžuò IÁR7:¦u EEELQÀcô|Ÿ¿ ìÄÒ°ãÞoÌûËØ¢ ë) ÿ°†ŽØ·mìä©ßYw’Ο7%iäá¶€søŠÃª—«`P qZÞ*·‡:=Aêbo,_ˆ³‡Ž”R9qAp±GX«GÛé0¨Ì6°®éZö“!D…æ0Ùù:¸BÁÂ…¨¤¤ˆŠ‹yÂ8WàœŒË•D:þö´ösF'+ ²ÙÑí—÷w­o ÚÏŠÈp¶|æŠo 'Ž­aœÑ,Ù´x›0Øy➸qEÅC©àÛÁú Ç caªù7î~П¨”=5Ô¶¢€¢Àˆ¤À¼_ïœê÷…¾(Ge£1üXÒQí‚#‰5œÀƒ;7®«;ïêëÛ7î>TÌÊGÒRTdæ”úKÒP<¤@­ 5ì–eBF!á)ß6@IDATû\Ô, f—1ÃÌBëYôû°%@kŽi©=Ïk¢ùeìK4À_è8ÉsCmJ¬óå½ÿâ9·ñË÷òsËRÁ·™ê344Þ~sùŽÓûëK‡N/çkpF&‹¹Î›óy:΃lY´Ç¢Ý÷å=ð.'óK¾ ™HbLäu:øwÈ} /Pʇ¤„Z+ ( ŒH °âñŸ§^ƒá…ÚoÎ]?" ‘»ÆÌ±T<8"›¼.Wg×–µ«7^~ÍWeMøs·_Þ#Ò$ÞH)LA¸Û,%aõ?v»P—-9¬| æˆVÇétaý@p9„ìÇqX¸&Õ ñH¶ýñâ%› Ÿ;Üíßœjl£}Û6ÿ´¥åL'ã !|–ˇà_<Ï3,^ú“Tð­¤Ýpõú6Þ~ó‡Zèƒ]¿¤=Ç_dž ‹ëpmÑBçÌø$]<ÿ8óTn™2Èfz¤ê=–|‹ñã OŒ‰¼Nÿ&Ô?JùHˆlê"EE| Àì_î¸&_‹¶ðw«Q™ÿ9Ú5ÂÚ ÝVðqENäµwo]óÞֲʪ—xûvzŸºåC²/2}-„|™† ,>àEÌâB`ù@n&(Ra\¨Ól£‚"ÍkÖ·÷¯(àybaî„åσõÁæ(¡p˜…5DËlœÒ ©hÿPðËDû1s nӞÆ“‡ë}ÿekgðø Št(¸¯à_<¯¨¬ìüûs©àÛáî3Æ[ãYðn ¾ rªßmu¿£‡ž¢0» 2K÷(,Žçø]~η©Ø9·I d=Rõëùö`íŽ1þ1ÅXÈëtòoBý¤”„Ȧ.RPÈu °@øÿ³wpRTI¿zòæÌ²„]rÎAPDA1"ê™ÏSLçyžzg@ÔÓ3 Þ¡ž¾ œáÌYQD‚ä¸ÀÂ’–´ ›Ãä™þªzöÍôfg{fzfÞÛßlç×UÿúÕ«ªW€vÌc|à/oì½o@;æÛ¨A€Fò4x#/núØÒòÈ&ü—}öÑÿÓˆüòUâ ×(åBƒ)š4 è÷! èdî<V¤Ð¢G!Ý<Ȭg×4$­åDï äÑfx|LHÑ¢ABˆ´BI¨´-QÆÞ×^þ[ʯ¥óáæŸlåÉÔŠ4Göï}sÉûo“¹Õ'ªWT¿¨žy$GÜQ(V¿y÷Í÷.¾áfŸMWªÞ†«Ì“æÊÍ-ÚaûþwaãžW1VG=Ø*iÉhªâ½ºN‚É£…œôþ AÚv6‘ÆCÉv,¯·û ·±ìsì÷<õ–4¡®¿mƒÝÌ\øh~Š#Àˆ}ú¿¸ó:FŒ&NñûgFÛ•¿Ä>×1É!¼1Í}pø“Vµ¤qEYié˜s&ÿú…·—¤ éÓM;¸—0´O×Å¡ hCæN4ƒIælÅ ¹Æƒi>ªjÜ 1’… @^ªVr—ZøÇq´e?zs@gçZx<¤§éÝí忽Ñ;(1>i«4ÿ–%¥Õȹœlå·¯_óŸ Ë—®ÄWS]¢Ÿ|æ8TÂG“ú‹‚Ïc'_pÂ}ÆYwa½MW¢Þ†£Ì+¯ðAï£Hä´lîÚ—Ál+—„ \7AÚvÍBÇèÖi,=ö <èôëh=>½Þ:ê6®\þÞ¶µ?ÓÒºTgÉT0õ7¨rãÂGP°ñ‡8hF`ôQ_W»óYƃ¨^Þ{Ï "ÆSt"@¦04ª'ûfšé£%¤È™B²CÂòæ½[7ï;ó¢K'»\Îsp™Š1%Á(¦§% &=ÎP(1Aƒe'?>îJ~ tÉQ›_ù¤éÊ>ìù–*òä,¿¦†}9¿Dÿq{iôç×ÿ¸½ù±û­‡HÅ)°Ò(в¤%ÅEK×|»ø«††Z ÌVƒ¿ZüQ}¢z ÌVJÍÖ_€VíZ¿nÏY—L†ô]€õ6 ùW¼Þú—‘ÿ1#²=[*'7lBUÑg8ÇsÊ#tH­‘&{ò°q^ §N„¿«Àli¢^=ÉŸÿã`(õ¯·þÇæé_o±?«;´g×ʵß}³Übi`ÕÝpÖß@É÷ÞÇ…/|‡#Àˆê oÄaQoâç¡*’“ŸÞc”O¹öƒ†8$x°Q»4°Ã³µ ‹ñü÷ý†ŽìQÐoÀ€„ääLcBBšN«#MIÈSYÚàÈÐäЋl •u[ ùKù NGðN§Ãf·Xk,æúò’}E;÷lÝ´o$Aƒp4p£Ú´O&W¤•…Éf+¥ë/ B÷!ÞµxÀˆÑýòûö’˜œmH0¥étz#Jw¬ž7fùMrêASçΫ²L¦r©]1Ž$gùÉ3ª*+GÔy`Øyb£Œ§Ëi³Y,5–úúÊÃ{÷ìÙ»cË!dLI;©ú‹¯n_âÂGûðâws8QŽÎb ý_ØùcCÄù›fõ¦ÁOÑ€|ö˜wäƒ:š½¦5;øÁÞ…¿"Ü'A¥q>Ö+¬à©Ð¤ü»Þ¸ ð9r?zèðòu‹^ü>4oâ¹¶€3™¢ºB?æ+Duƒ„ f¦BÐ×ÃFýߣ'NÿiÐÇ™>Ìò4›=&‡\ð`¾!$t„TðÐçtÑ c®gòWp[‹×·9 py T'Øêˆ|Gu„ʃ¶ô£káÒxૼI•õ×Kîô K¾òf÷-YîéØjH›(%ô1Íõðã/+4ÿáKW :2ŠmYÝeuDõ7 f¹ðLü&ŽG @¯Ö-ýß.â+\ÅB±¶Ä é#M[šÁ¦%3µ"¡ƒ a±™ï|éû 5Òì0º;5ŸØià)¼P} Äê@Ø Ž³ëž»#óŸÑ ŠúKôïo2^q»}zZºë^<̱5×”kŸ~ý%çv.tÈ‘Qt?šêo«Œsá£UxøEŽG VòÒŽvL“Æ£¸Ú ¦ù±Âç£EØ !ÀÑŒ6 ôcÚ¦ñ`Û3ëÈCÏÑ]Øó¸ÒOî“S3OáE€ ÞhË~T7Ø /U-¿è!(¢õ·OŸLÍ¿¯¹Þ˜`û3ÒÒMN.Öå­uuš??ÿ k¹GÆ—_åû #@õ«§´eu—¶ì<Ý£êÄ…U'Ž#ÀP »K|»fi€‰ÿ¾(z`­ÃS| À>Êô¦Dõ@.lÈ÷¥”þ§MÌèÁò4šÝ¸Oþ<…ª ,±zÁŽÕºet†½þ>ýÚ©ZmÕsH€g‘ŽF„ÐĪØíÒ<¹mÙUïôÑGjÅ-éŠÆú{Z9páã4Hø ŽG ÖþbQW«ÛöÖkk-ëk…Ü>~Ø`®}OuànA£í‡Ú6)QÐ’æƒf´yâƒ@ÈëïÜWu1:ù<Ýg±~“E¡£ çpžÍG¾2ë®M¨Iä‚G0ïÏpá#ÞkçŸ#XÜöûQëAÎÆ4ß½r÷ŸýlsÕ…@/9‚@ÂOÕ!0oa¨[p<#º]—ûW«Ñ/êŒ9óœ^†Koò»Ì9#À…À±âwr8QˆÀˆ—¦[œõ3ÙìV®õˆÂrŒ’0Lz>|« æ¿f*°»ìO¸Àq NÔ°EÈ6ц36 ôôwÖ•”©‚^NDt#À…è.?N=G€#ÐVWýï0¶G ݆&;wýqè·ÂŸÚxˆ_æ(ˆÀà— óv—'À ÕÛÿÐ÷¨‚Ùó¬8A#0AJ¶fÛ]¶ßãQ–‘[»z­á±fX{¾Ë®ò]Ž@àÂGÀãr8êF`ôQ__·ó÷ŒJ´Uþ«€+]±c¾å„‡Ã5нãËlaû|ˈ{+7Éa;u¯Í]O˧6¡C€oA£è‘öž˜‹M®òŽ@‡àÂG‡!äp8jEÀ\[x5j=òˆ>48ž7zÈ{{ÕJ,§+fDÍ(QZY¹±|Ìt0¶`Áh}¥°å‡õ䓸þAn’X£µÏžé\Åã_6A†(Œ>”gÇਗྠ{ž¤WVL(ØO°" â(VQóF‘Íyâ„\iM˜·PwM9l~ëboùËqbf— h{d¦ë3«CŽ ß\ø²<_ŽG ¢ ùÇŽÞv;L¤©fü¸:u&í«%ˆ¿«Ñ¾áÝç;p"àô9›£²åIAà3ÌáÄ?ß5w¡±ˆŽ§œNçõ~ì›qÉ¿jÅ´çfϪª(ö»Ì9áA€ áÁ™¿…#À#ï¿Px¾®›ôJNæ´˜VcðWypkÐߣQÜàþ^XøNxiAbž¬‹¢ýNÌ^>¾sbÝ[” šž¸¦ù@UÞγ䎀¼rþ¿“#Àà¨\áêvyÂÛÜÑ܇ß 3nÍÞ¨áÎæ ¾Uy 2ÒÜBÍfÑòGÌ6‘eqpA+øT¯7<òÐm64÷3³K|ˈ(\øˆ(üüåŽ€Ò ~±0Óáv_ÁòÕƒ†›\10ø6숂€+]5º›»5ÜÙ<ì%»/\´¨‡é¸½ä.—Pý®«‘)çZ­îÁ‡ïpl”‚”Ë/ò}Ž@„àÂG„ €¿ž#ÀP—Ëu#æè‰Ô+ÀºÂïRö <7Ž@` ±¨«ÅeëNwãÊæ!wïìQ~G E>üðZmqÕ§7·~oêΖqn|`3h´³çÜé\ àh1~#I¸ðIôù»9Åp p;ûkX¤ø x†°ó\ï­¬ýè:Áå=æ; ˜÷švZqÕÇóP—6Hþ8j:Šq'æÌt¾‡þª6ù|Ÿ# øR»ê) N G€#ÐA¿T8‘R6X’’“Þï`–üqŽ@иÝ.¹ðñSÐñ㹯ê&>»PXír¹¿” ¨Q+Õ€pO–8jУ³\ïrÁ#î«JTÀ5QQLœHŽG .·×Ñ\…O6ÍêËIòĈ 8ÿ|.›‚Öˆ:.|D¦¢ú­ó†ºÇ3¢Ûu¹#µ(h¼ 7å¼ðàô²€M~—ù!G@½páC½eÃ)ãpÚÀà ÎÃîÙ`@³¨ó[9Š"0øß…fWO¦‚µ‡A¿Žû{( qLg6ÿ5SÍmÒ%:¦£6×k¥‚šz-0@ÒÓ̬+(‹i8s±‰>b³\9W¸CÀyÄu YÄ8Ú?Üó§AË…ãΰJpšÝç0RpÀ¸îÛ?ôÅA#OÖ˜¿ %Û.4V°}¾å4‡ÀßÞÊMrØNÝks×?ŒšŽTù=8™ò¨Ñ?üÈ ûN«üßçD%\øˆÊbãDs8rúÍ/ÊÁvg•+Á­í›òë|Ÿ#nDÎõÔGQ#üî÷ó÷E ŒÖW [îpXO>‰á`r›P-À­¨}xöLç*{“Kü€#Ípá#šKÓÎàH¢L®´8ÄÕ»„f pÊ/ñ}Ž@L À…˜(FÎG ¾ÀYæ_±Yf4øÁæ‰#9,Û9ÕǘX¿öÝ-‘£†¿Ym<·P7eîBÍóH×(?ÚŽÍ_™ñø"AxÒíwrb.|ÄLQrF8ñ‰@ÿ×÷¤ˆUŽ)Œ{VË…ßFÑ-žï}±ÜäÊ F|ï<ÿº~ŒËå|Þ-ºÎk‚„•A˜—Ôµ×?þpi±mÎO6¹Ì8±†@,4»ä™aò”Ž|?ÖÊ‹óÓ´´ñ&Ú—{/ð€ˆÊ¶$VÚ¦bàYF€-{îx9n4Á ˜w~#G@I°Nz’àtþ€{j¯ò¾“÷¥¬ðÚÎ]hì¢ã)§Óy½_–f4Ëû‡FL›7{fÆ$*ö»Ì9±‰@4 Ò鎇ž#h„‹Pì(‡ÁœRb³ˆ8W-!à±Üâ 9;l¶¥oýý9вDªjúñhKÀùÎ3aƒÖ×L¿ï‘Ñz£ñÖ¦pYÇdß­êÝÛé89 BïñÓìfÝŸq–«—ZNY¬#P§ÍLÜ b>ñ©Î ß>5qÎSªgÄê¨?E­M‰ÛåúnÑü§YÊûÒ K雷y°>.Šö;1 ùxˉ«ñ-JMOÜ?Ë| *È7ðÇ8щ€¼1¨aæÌ'\™Â=:½ö>·[ìLKi¦$šÜ驉‚I¯§Oq„€ÕápWךÅ:³U£Ñiÿ2ëñgJ­ë¿oÜùï+>7#.ü1A$Ži“U:¨½h'Mº2±`Ì»M ¦ßGc›rãˆim]¶—áñY =2ròzxOðŽ@˜Øjïâ] µ‡¡F׿{§‰a&!¨×5ן:ìΗËw—üûË/ß þ”÷¥";oAFšKSó Ùm¹Idá’¹¸ |ª×yè6Û>‚•'Ž@ü!-‡æö‡ÿ|‘Æ Mt»» êÕU;¸ íÓUH4ծΎ¿Z>Ž%ÓlµÁŽâc°¡ð@îÎâ£Ïô?{ÌÝyCzÿî½½ð=’âÀBÂG™zß$ Hžþ׿ÿÓ…©éÿçżhmS?rÀë‹ë%´ Ò4ðôMçªyNY\ pÓ§üí„g…¢™“òáò~}¢…ïæúÓçò†õúÃͽžõöKÏ/AFx_ÚJi.ZÔÃtÂQò;T?Š¢Z¦üVœñY®ÕêzøÇF)H¹ü"ßçÄj>¤Úsþr.;÷×¼œ4ñú ÇAßüÎtž'Ž€„  0nH/ú ûJJáýïÖuÂõ'7ÿqöŸß~qÞ?ð&Z FñHjoþh@õ=W&x&„°‰ö6åFû…e‡|·¦ôôøœ#Þ.‚û¿xÑ+x ‰U±F£¹qÎL÷˜G%Áã´Gù Ž@Ü" fáCsËs.Öh´Ïã IœqÕ¹‚AÏ-¬â¶¦¶ƒqª'T_¨Þ$&§>qåm³.ÅÇåH;r‹‰[©K‚aA˜ÄB›ÚxÜ UÏH¯S²†uæýCLÔÖ(fâ»bŸ&nr/jrÑŸäý©Þ`zö†»ï¿¹bHô3$ pâ³ …Õ.—ûKì…±lPÜ,ÅÅ:îÉG zäN×{ä›Ê®ñ-G€#àA@­fWšK.ùM’É”ø*™…LŸv¶š…$^—TŠÖáxyEúz)?¿ßº’’½H*}èG&Xñ¨íP;7"™ºt{©knFL´©ú´ôŒ^Z®ñh®èø¹¶ zóë‹ÇkPÒÈ饳ðþüðGSäÑm ¤¶„÷Ï „a+mjé~ß,ó”^Ä"OÈ!ð#.~àpѼNƒçh¡{jliâXŠfe‡Nžx7²O})ÌÍTðìBÍ"<¶"ï^Á;Y|ÙɽÑÄê)àAµ€'ŽG %Ô¨ùfj&Ó=ƒ{w¹syKEÇÏ‚Ù,éÓMÜ âíxÿBüÑÔ$s˜¤•[b9yµÈdRZVæí„E,´©'PZïQ^¥™40¶«»²X®Zœ7–û4q÷‰Ma˜õ§…ûþùÿ'þb¾/¿ %Û.4ƒUtºäúéÔŸÆl_ú··r“ž[ yÔõûq<òGÔiyt&ÿ´úá¨é˜î<:VøÓxC@mÓ…Ò€)1%årÒ¢8ñV œ_åÀzTŸrºv›„¹“!®4MI£îÄXbž'ï±Ô¦Öñ™\M꛳Ì1VcšZòÙѸÌÕN:è–ª¶y=åàgýizvöÌuþbª/]°`´¾¶ÌpXO>AfªM`VÔ><{¦s•‡í&WùG€# j>¨ÇÖ‚FS@‘ËyÁK‘ßÖ*´n}J‚Q¬1»âLóAÕ·Xu<÷´%Ï̤I¼ˆEÔ ô v Ù%bf\ר²­—ãÿ¢ OvÙ¼ô^Ú7¶ͽŒ5î°þ´¡V—§Hò‰¾µ®`um¹¸y.òÔ[>-…šŽB5Í™éúÜ3gÕßp8A! ¶é¢G'h„¼ôÔÄÐFѰÝl!ö ³Ù}ªõ W䑲ŠZ(-§…FNOÕµfX¼j4X|ÃÓï Ý›Ý{hñ?®ßÅGÊZ¼ª éiI‚V¯ë„ù“êœ>˜$x‡¤Ža¾jHR[BBˆW#ñN¨°ŽÒ°á˜ýza :ö’ÏG(R,ôJãÒVûVú}­å§–~°¤Æ›#š“ùßåý¨ÉÅv¢¾D£Ó’V &úÒçê¦Ì]¨Ù¢ûä©·¬ôŽhÍÜùİ9³Hðà‰#ÀP5i>h`D?-®‘lÒë(‘°ñíší°|Ãn¨7[A«Õ@Ÿî¹ð›K΄N™©ðõÊ­°bÓnøÛ}7yŽ×g^ûºuÊ„[/Ÿk¶í8;Yé)I0¢>ÜpÑ8ï½´Sr¢æ¾ñ•tN§ÓBNz \tÖP?TÞ—5y$¨ƒ¯Vn—Û³®>ï´ç+ë૟¶Àè= )Ák¢zÚ}œó£Ñ’˜ƒzu›§N@å”'y^…ŽÃëŸÿàFÐëN¯^K×턉#ûKøËŸ õ>Õ'­F›ˆïaKš.'&èk¦WŒ/â‘ Á@¼+Ù¦0ψ¥_ŽúL®Îì¦ì,s¸ûŠ9wLƒüÎYmby²²N'tí”Ñæ½JÞàÿÞ¶Úw°ïþlÙ&øníéqZU)35.?&Œè×b–áê[$ ñÂ'»}Žæ“{ +4ófm‘ÖëÔ—à <_JýKÔö¥Ï¿®ãr9Ÿw‹®¦S*q2/©k¯üáÒbÛ#3Ÿ +¾üeXGàôÑad9¦)L-›ÙIÛ×?o•>lל?†õˇšz |øýz˜ÿö·ðÌï®–êtϾ’2èWà‰ ~ªªŽ–UÁ玚íûpéz8oÌ@iÀ¤¬‚$¤©™ñ«s¥ÿê­ûà½%ka$ *Fƒrƒ¤Û®˜Öá2ñ“ž û–ÃGK×Á€ž]`Ü^Íò?j@ ùSó‚G³„õ$…üð~,éƒIõ-V“§-yØÎ%Þc‚×µG}ÚÇ3»+Û……»¯´@ÿ¼rp¢$܇ÿ{CÙ¾QÛ ÷\ÔáÑê-{áì£Gö/€Ä&QÂÝ6WV¤Dÿ|Oø¸j@ìk=|8H} õ£Q×—Î]hD©Öñ´Óé¼ÎÇ´g@ø‡FL›7{fšû]懎€(ûåîEl¶WÒPn D³…?¬+„sF €Éc=AH³Ò’aƯÎÇÿïX…ÂycBçì4ØRTâ>h?ÁhfúIذÚ´ÊjJR¤_k¬’6…f3Ik²q×Aؼ簤ýxìߟÀ¬k&ç?n„“•µ0÷žk¡´¢Þûv-B­IFJ"\vÎ3¨'üýßAÏ®9pŤQÒ«ÈÌêŸï/…Û¯<ÖíØ/™ŽÝ4õ,éšGs³G¢w⨦3…Ä? Z[ŠicÎC .?Dzî]|o·ÜL¨¨®‡Ÿñc÷õçCïnd™Ô41~ztÉAíO!š{¾ÞlÃ÷­ƒ§îºJÒ0.…w¿]ƒ'LÞpÆ °|¥)­„%¨‘Ú…Úƒ^:ÔHuGÜ~‹¿ï|³{zÿ¯/>z!IøZªcìcé­ox.V5^~yï|ªx¶Ò,ÂÞ Ï Ézœ§œP‰¾‚Jm)û¥ã¨YÝyàôƺ>uâp 6÷ šQnÀ~„&0Ön+FëxÚ·¬Ý^Œ×¶KÚ\ÒH’&—êv‡¾üi3lÙSf›±C•.<4ýÉ$Ó¿Í)«„e¨Õ¥I™þ8ùrÉ„aRßÓÜ{1öR“öÝRÿE|µÆã[¾Õi´^áÊdÐI<=U}QKÝ\ßùÁwëîIs½ û¸£'+!9Á$i¸/={˜¤Yi G9m-í¯*qÀÉÛX6j<Îé¡\}léj9ßø¦þ4júÒ—$æ™ëŸEÑ>é–pFCx#LOÞ?ËŒvÃUj™ÓÁˆIÔ6óË:1ÅÀ.Ç5}ŒöÌk’g6šDåd¤H{º0f`O òÞ³†áýºK肼ÉÔjÁ§Ë×7÷ÞÓÖÎáÒ é“Á3VQSÿþàG à*Ô¸Ünø×û?€››Qè…ÿ×>ûI2ß"!` .XÚ´çØQ艃‘Ú+ÎZ¤KGqà@³µdF‚˦äiåæ}°£øÜ:í˜vÎHIð!­%ʇL •:³Òäz÷I€ !Šf"­¨:£ÑŒ¬¹çíŸbãxþÅkÀˆÇ80*¯®“„-–ñ+6IBÇs(„Ñà‰ÒW+mß^¼Lz¸÷Æ‹ kN†¤A’.tüÕyVÏh«IΣÚÚyИ¯=æ3¹ÑY&ùð!è\=F²¯ ¶ôùòM(`è`:ö•µ ’ @” ëÛ]Ò¤’àÖËÏFÁ jêÌðæW«àì‘}á·×ž'™ƒ®ÄÁ5%j«›v†‡n½n¹l‚ÔoGmiNn4×f3S“àW“ÇÀŸnºM»\°ûJͽWÞ¾[ë¿èùÖx¢ëþɆýôVœ$Y½u/öS›  /…0ÏdˆßIÏRþôƒfôû wž9žFM7Í4PI&]­áèO_sÇïú|ë®ìoˆÇ%ŸåýLs©âܼiϾªyÆ –bœý¢%RÏ2¹ˆ¿õàGg¹gyỦàÄ4 ~ºÃIÑa-ÎæQJN¤EŽš¦$œ«i0K'G ,ñ‡ŽŸB DÍ8’Æc+j\Hè m 9ŽïG- "›w‚±xÝßׂœ»i¶ó¦K=Z´þ‡–vÁAD7ÈÍJ‘~¤á!MÌEg•n±Úœhzvi«faß¡v‚i/ÈܪSFªtLÿZ{¾ P$èÜ÷›‹`@Õ?‘?Þ{KÖᇜN7Üpñø&ý¼ïô¶µ~°ûP—Ë]¥ÉŽ®9éøéÞŽâ£-âèÿÿcr4_qЇë5ƒ;æc矔³>F•ä.ZÔÃtÂQò;T? nÈ”‰„/×ju=|‡c#)—_âûŽ@ˆP£ð¡(ËäN©gÉü ¦é#M© ήÓÌÿ6ü§áG™\±ghÖðÞ/”f?Zº^ýt̾í2v¹É–>Ü4ؾîÂ3¤Ù;ºÈL†`Ì –ªñcO©»Ì‰´{nT¢æ€LztÉÆÅ!ÉÑ›ÝÉÑÛ?‘Ô ,åwöíÓÌäÞ’RI€Ù¸ë»h•–útïÔªàA÷ýá×Jfd4ûÂ[ßJZˆ;¯š$eÑÚó$@PÊo¤"ráaP¯\mïž.m G–wKÛ··Ûõ»“Ðܪ -fŒ-±5ç?üðZíþêOo>n?üí‘®}Ôovöœ;KÑïÃw–ïq8aC@MÂGH†YéIÒÌ:™K‘KäsA³òSÆyü@è<­µm_ j4R¼&Wì~¶%íDM½Ur¼¦Ù4úXû§ËÏÕâ 6ò Èì‹R)ÒÂüÊpU›!½=Ù±ƒ{ãbš|%£ÖÂ#Œø¿+/;Ö¢K¤m`‰´4s9iôøÕy£Ùé&[ZY&ÐD3­D'icXjíù<Ô²P"s–|ÔÎ^lv™Î_|Ö0ø0h¦öôCÚÇS>&£§Zއ›A¥ûC”BRïBDk ÙÆ"Op´ÖÇê<öõI¸Þ°\e»¯Hôò%“«@“ ÓØi°îŸFá`ý[Ôšî9tµ¦ÑüjXFÞfŸ(G¿«ÒÄ (Ë7ì’Ì+%Ý Æ~Çmõ_t{{xbÙ“°559DçÁc'½Â‡¼ïd÷²mký`J’ ÎÑ'bÊÐT'M¦0ß¶Öpdy7·mÀˆæŸíöÍ–O—ZhTÑïÌ]¨½¼¸êãçPièû¸#¥X­ŠAóçÙ3œïSÀUâù1G€#>N9‡ïÝͽ‰:/ü)7KKƒoØ®B;âïÙ‰ŽÞu’6`!úoÐL'9A³4 …Ò0ЪW£Ñ„¥"4múø$°“ôö½‡Ñ_$µYÁƒ=È–´dñýZ]Ëp)àj\*—œI)1¸ Hõ°¾p?œ1¤w³YöëÑÆë¤¥€I˜"s y¢•¶ÖáóÄiBˆ?æó!¿¯µ}r*¥A ͤnÛw‚ìÖn÷^#m}øÉ៴&? S+Íæ²DüöA“2Zz—=:ÄSê…vÞd&÷Íêmov‡ vî?Æëè¶±ŽÅ¼éUÌñ¹ö¨Ë[öcºê·¯Ws_AZY2 ¥…/È„Š&RÈd“–ÿ¦ØC$Ô³ø9…ØV0ž-°ÑÍI{ÐRª¬ñh_I‘úægÖ84ó¯<Ÿ¶ú/ù½ì;qùpê_I[û.ZAí>µ¿¤ÖúAÒ¤,߸ÎÖÆ î Y¸Œ/K­áÈîinûÑ.;P°KJ}³´ ´®¹wªîœgy?Q1@àÄg «EÑý…\ð@KA#ü>K5è‘;]ïqÁ#¢ÅÄ_Î|ª-Š»WoÑ `é/…’Ã5 2ÈÇ€| h(–hK2wªEÍs€¦k´?¦U™ÊpN+Îó:9Œw4ÑLÞ=7LE_þ þÏ'ÒLßÔ³‡ãÀÂcf’Dq5ºJNî3ÍœüßI3xä¯òÖ׫=ÏOä¾ú5SÆ9oÿýÝï%s†“A⛄§@ÓŸ¯”n%|Èåü3šL(µš ­ÖõÁwëqåšhŸÞÃk³NQ~dòA&$‘ÀA+]Q –YWOBº×À£¸B%*—Á¿½Šf¯xŠS~‘™\ïª jí+ÎEíå{K~ûæÿ¦£ùY8iBmëKŒéó.NÚZÅŽÚµòë óhàM}ùx]~îÈÓ0ЦLäçñ—Ÿ£†AÊ“V©bÉÿ½l‚€®·Õ±<ÝR€Ôg_ÿRÒÖM¿FŸòs#ÚJ­õƒ¤q-@h'2)#ê÷N•œê[±¥wR€Ëw¶ûÌxoÖ²p×Rü¼rÌ[`êdz¢Û5Í/×Z óu 9/>8½ M6ù]懎@¤èðPnæcO¯ â>óø$Úv ‘€¿ÔÛ|ì‹A} Æüñæ‹;]óZ¬vÉ ‹>BíM4GvÐdΤt¢Ml‚ K"…f?i`àïÎ褰—Ý$'û`ßÁòjï–>ö´ŠŽÜcÛÞišÙ8 %Fi6wÁ'+P”·L;Ûû ¢™Ùlw$½øöضkßîw^þ뽘O)þ*ðGÎ/´"oJb yÛòBž¿oº÷¡—‡ê;0m*\xõF5TY<Ñ/nH…~8ãʤ¶¾‚ÆàÔhøÇ½ 6ZŽ&UH³øÐß?„_ãBcõ’î?mÞ# ùà¦Û>µ¿–bµô^9öí¿äyud¿¹~ð/~&§;É™Ÿú Ò=ñÊgÒ$ ™w±$Ç‘knûñ.<¾¼Qc” ÀŠ[Ò0Šgû¿'Íå-ç¨?ݱçÀö·^zî&¤9"}éü×Lv—ý/¸²âÍHƒ÷£Œ%aÃïû+z1é™fÕ•G ¦œNŽ@4  Ô˜?.4ò¥™ÿ`“ÜV:ØGQsô‘ƒk:ÝÓÌ&Å ÕÁ(¶šŽÜ&÷ °F+jž=˹ Àc¢Ûä:?àpT‰ÿŠ«²X8QŽ€íe>·œa¹¡õõ¿—ïsŸàҺǗz&Ó¿ßp­ƒ&$[4Ç潦»®Ü½ùY|AooP<@«¹B5Í™éúÀ71Bx¦Ž€âpáCqHy†Ž€’PL…UáƒüzesáCI|y^m#`Ç%®^Ùè3ç™1Ê&þõl¸ ïxöUÝsjæáã£ü²8¢4Oξóñÿ “û7¿ø!G€# ~¼+D¨ŸÔè <ï~î-\•F=*ຠ¼€«“ÐJ0þéË›¥híþçù1G@-l?éÓzôGÁèC „§6X³mÌ}ã«6ïkŸ,ǧš»—ç^ßbƒÒzÏX7;Q7–û9Ç%$!aúù×õcæ.~·ë{|Oð ÉLéÞ»ï#3]opÁ#$ðóL9aC€ÏÝ( 5­ÜD1+Ô”hÕž3†ôÂ8 §·[¢×³|©šhæ´p;Ê|B³ÒQÍÙ;bqK}­(L¢€«Ùé¾`|-åAÁZ§M y9é-ÝõçIèX¸Éçëñû3L\V¸Tç.4âJ#ާNçu~Y›ÑÆêe­˜öüìYU5Å~—ù!G€#œ>F.THóÎýGaCáA) á„ý¥€Ë7ì‚.¸ä垃ǡÁbƒ/9³ åt= £®ï=\*­çþ¸Áß9 Hsñͪí0ˆï×î†uÁý†]aF3QÌi°pg*7â;Y¾GqùZŠž~5DÜ}ðŒk@ŽŸª’.R¼½L Á± lÇæëwî‡^Ýr1"p/ †vâ&Wí™ ~‡}Æ£§00aoÓ¶ú"êƒHø @§tp4ö/«¶ì•‚²ž3ª?ô욃«iÁ¶¢#ØW9¤~ŠV«‹Åô×Õ°6Ê¿s´p×z(VÌ/-HÌ3 Ö?‹¢}f* â‰`zòþ™æUн“gÄàDnv¢2Ø´ûŒÖVo+†¯~Ú,½…âX¼úé Œ n‘ÿWÓõw0ªw·Ü ÈÁ(ÅϽñµd¾eÅ`ô¡ÿà»uR„p,úrl-: c÷Âk{á㥠Kv¾o;éé¨éÝ´®½mÞ}(Ð Jœûú׊ƒŠžÝrP˜8à%cÙ†ÝÉ}L3?…ÉM2wHD³ª×?_ ×_4†õí& ]rFÁFÔŒÜrÙÙoölhF“q±YÊ}xlmôÙ†ÂLVZ ””Vጧ~Ùq~}ñxéAŠœü»ëÎo1 r“—ðŽ€B”Õ‹p²Ác:”¤ w&×|´Z ÒyŤÑR @jû»Pë:ûJmõEò÷ŒÖ#¦÷„‚ÎÙ’¤Þl…î¹YÒ-ùxŽ&Lb-a¼FxögO$sâíò~Å—yîP1/ZÔÃtÜ^r·K¨žƒ«WeÊ3C¡c™V«{øá;1,¤üßçpb .|„¨@)¸¥Œ\NцY*Èóõ·ÅGNJ&Mt r¿ òÕ¨·øVX)@,J5¨9q:]håyGr‚QšÝ$­ÆYÃûÁûK~þ(¤P¤ãÁ½»HQŽ¥ñ_Ù)‰&vˆÁý¥`t‚¢§ãšø”2ÓRáÒ³GऔtPÓZdÏ]ü?G@YäZ!hrÅgÛ‡/µYÔI‰ú¢:Xj«/b÷Ñ6¯1¸M/)©Í¯M"Jáon·ÂÞ Ïb‰(ø>pV‚ÂoˆŸì>üðZíþêOo>n?ürÝ]¾l.ovöœ;KÑï#~@áœrâ.|„¨ð7í> ÏÆæ=%’ßGs¯9ÿŒA@?y*.)…êP`!ߌA=ó€œÂå©SfŠd‹½vÇ~éëwÀ b]$ÆÞ]ÑÔÊ ?®/„3‡öÆGÓáiNÞüj)­”lºw8Ž‚†Ç±tÌ Ò;'Žì+9§×Ô›%Fþn¾Ï';Nú#<¾Gû‘¯®k€â#e¨åHG¿¯pÁøÁÍfÒ\_ÔôƦý]#Ò¬Ô6v ¶4´´óß×úµ»Æš '‰[)7­Í]¨½¼¸êãçð+Öäc‡Ÿ¦b4£Ðñ~§š~äËšßÅàD)\øAÁ%£fÌ­ù×Ç—×]0.à·tÎN‡7¾üý6*aêÄ•ž§ªjO{þ®kσE_ü ߮ކ¦X]`Ú9#¥{HØB9™6÷Þ ÔlL;w$üýÝïÐÙ\ ñY‹Í3À›4z|õóVxü?ŸJþ!%ùö+&žön~‚#.äÁ‡vâá¤Û‹{ßüÎ’OØôÛÞ·{³¾fíÍ“ÝOó“Ñ?lÁ'+°ŸK‡Ù·Me—¢z‹!=`öf Ø”¡“ù­Ã}Úâ¨f.ŒÄÏ}U7Q]óDÑ}–üµXmJEðL–{äÂY³69yº`+¿Ÿïs8±‡>.ÓœŒT˜ÿ R®4ȧ¥XºíŠsØn‹Û¼ìT¸…2k`ÏRž¯TT¶Ãúå{µ *"‹“ˆòø<¸`DŠ ®^jsŠðð àlŒË8:O·äæVmU‚É2&_î|D«³Ý‰SòÐïnt:|Ç 1üùÖøÜI[YñëŽ@œ À…4òâ‰#À𠰯ܳÒ ÌñN¤rx8!Aà‰fØ×¸ºUº=7…Çôh hÆ.Üÿ”ffv®ã¼/Å££ô>±´úÙΰïði’¼WùG€#×pá#®‹Ÿ3ÏP/{Ñ슥~Üô…AÁ·!@ฬîE¾`‚sÎN„cN¦$dn·ܰz÷ùt€F#m’·k´¢öáÙ³œ«|x6¹‡p8q>â¾ p8êD`ŸLøè›Å»*u–RôSµé„æ­öDs'n®d„kðÇÓéì>üü´ù¯à„øÔ2x—ŸC­Çnpk}ì.×g€Wyâp8­!À¿è~èPÔñÂýÇ #Wcœ Z2·£‰Ö1`°¯ôäDŒ ž„ÿºA~ž'``GóVòù’PxàF]¯‡jŒ5boäÝOÞîWF ÿífŒ?Ð&ÁÖ)ÞV³”@IDAT®¶Ö¥Ô]ð ´¤ëÇ_6íª¢¥NÅsB•CíüŸjpÃ}K|~Csuðø9Ê ¶î·Õ¨"Q÷žøVl~Ê*ÑŠJ–\.(­©þúõ[™¯•ûÂÁËîỎGÀ¦_tÿ«qp,b?‡Ã Ë6ì†å ¡¦Á†Q”EHtÙ Á^—2ªc»Ö )`Öዟ¶@Z’Î7Î3ãmD®(R:ñþ%¬ÅÈé4ÐÓi0’4.1‰Ë"*V܈©ˆëæ;Ý.‰ÿÔä)Àb¤ùWŒAž‘V§–®ßu(ÀS5r &°jLà‚v˜²4V?~7?}6U‹‹è˜Üå ÑdÛT ÷SÎÑ6%ïO–mܲ6¥Öö$çÿÇ { Ž[þÞuúŽeoÅ*uÏ’z(7{<Ì3øçÅÉ@Ëë›XÝÿqý¨©Ço ÆÎÓi, ÑÕ ¸è“BID¿n·3 ûÓ„ÆþÔˆý©òß“åÛ`Å–çàpé?Ê¡¢bäñ·þµnZùqG)@yGç¨üò燎@,#¹Qo„Q¥¤ci–ã½oÖ@Uƒº×•À¨Ê"ȯ;F§r 9«6JRzBqføl™ V¬+„ß\6AÒ†øG#—?ŠýÂýGáíÅk$-‡ #g¥'C‚É’@B”(Z»Åj‡‹ùßË×¦žþCÄb\gKuê­Åk¡#kWés¡,q TàÖ)x-4ÇG': ËQ¹¶£RZ†uêæ0×)yòî·kQkjp´)µ´'9ÿï|û ]æ°”°eO+ZÝ»¤¶¡vÉ/]˜ ¹ÉÁ÷}žþtö§VHH(†œ¬m¸-Bó¤Ð9\»Ý&°XúC½y¸ô=YŽ“H7Míø÷¤²ö ¬ÜúWØsxq“öªÓš`ìÀ;`ãÎ~P~êDyùñU7 hò~ÀàÄq)|Ðár¹áGì¨iÆ4ÓZÓJ0JoÃñ—9 5}«öH¿I]`u÷Éðï„«Î#i4² „¡$æz>ùq#.í«…Ü¬T0¢YX8 6I FéG&mUµfäò?:¬ü‡ƒ×x{«Sfm %O€]vX Á¦ÌÐMú¥9Ë¡ŸeGXëTÓþdkXÛ”Ú“?ÿ Xþ{ÃTþÁ–ýì`åaŸ6mÎÄD8£kðŸCOÝ߀æµ'¡s§¯Àd:–ºO‚MRÒ6égµ@Uõ4¬ûÖ ¿' æ“°jÇK°ußû€Ñɽ<‚Fô½Îz?$%v‚M;—x¯ñŽG€#Ð^‚ïmÛû&ÜÏfçH5N‚Ç—+·A¯êb˜TòèÜáŸÀ!açʽŠü)(¸E7š Æ™24x ‘ö0 Þ?þa#$šŒ‰ÚŽH'&§SVTV×K‚P8øWA5Œ9äuª\ßv'Ž7V"‘HàÙ”| 4o yj®?‰d› w{jŽÿH– eÿÌJ3,Þç3§½û Ü848syÝOJ,„¬¬O±? ÿ·„Ú <¹¹ ÐêªvOlhb¼®ð?°~÷kàt5ÕÔ (˜ çŽ|2RzD¢Iówr81ˆ@ÜìCIþ;ŠÀW?{)‡¾h±’ÐC4üÐãø|9@nF* íÛ=$a@¦Ÿü¸I<²3’#Ê;½œ¢£¼ ÿ-!å?âÌÆ ò:EϤ1ç’¢cpCèêÔéýÉvU´©pµ§æøWCù·Uöÿ\g…ÿíð™ÔÞ4Ì¿œƒ¹¯îoDm@!ädñºO‚DGùõ}O\èÛ¸±èMX»ó_`µU7¡¿Gç 0iÔ#Ð9kX“óü€#Ààt¸‰ÜE¦$xÔ7Xàãï×K¦V¤ñPK"Z2,•ð>Ú‹[ЂèU:ÑêUï ™Z‘ÆCM‰èÑë´ðÞwëBÆ¿šøZ¨N½>djE5%¢§A“ï.Q¾NÉû“–n”ꮚÚT¨Û“œÿ—n2µRSù7Wö¯o±Âÿmô-©;­ŸœàAõÜÓŸ®Âþô¤¤ñPSÝ' Œézï»æ¿'"jÙ·ïÿ^ùüX¾é™&‚Gnæ¸þüwà† Þょš •Óˆ!âBø %™ZÙìvX+ÐÔààþ,ôñˆ„©UKu‡h™pd9T›m°lã.‰^%Ê‹Vµ"GØô”Ĉ™ZµÄ?ÍØf¤&â 1–ðßÒ{ùùà`uª‹‹LC#fjÕ4 ¾7a(Ô℃’mÊ¿?©5[¥º)óÅæøe{òç¿Îl½*+ÿ²Ÿ¿ªæ¯FUXc:·‡^Š`ÎŽÛ»eu¿W´ÊHÿ*b¦V-ÑML¤‹VÜò¯ûûŽ|¯u|³æ¨3ŸðfAfU—OüÜ6õèÙåïy¾Ãàp”F æ…R“s¹‹Å ¿ìØ/­jçòöÑD+ný¼¹H¢—è&ú;š´ô'­À.çòöÒMt}+·ìU”ÿöÒÁïoV§~ܸGZÕ(\ÎåmSÖô¢‹VÝúiË>Eêã›õ'kwTm› E{òçÍÎCª-Vö‹×í…76ÕHÚdìOɱüå‹’¤®šÖ–ÀŽË6J«Z…˹<0ê|w]´êÖÊ-{¤º¸t¼õí¯à“3 ¼fŸ÷Ƥ„¸ðŒgàÎË—Á —{ÏóŽG€#*b^øðÌÒ9%­Ç#ePosB\NW­‰h«CÍÌÁc§PûáTÄüŠ08„ùÕárÂI µ².ÑEôÕ£öGIþUÍp”Çê™1–黩𠢝µJÔ)ÿþ¤—Vs›Rº=ùóo¶ÚT]þTö®0˜â@§2Œ1t~O¼:-Œºà—Ôeu¿®ÁɉÛT]÷‰¾z³Þ\ü¼³äz8zr“—^ƒ>&Žx~{å*Õ:úÆ ¨¾ÃàD˜>Ø •Ýá+~$‹—b 'QŠã¸Û~+Å!w<DwGµ ƒÂG¥‚ÇC͉装¾”â_ͼF+mò:Eä(އš“DŸuŠñݤ?Á|Õܦ”lOÍñ¯öògeŸá< Wö˜?%ôøê1 <ý©(ÅñPsݧ8#€÷©®,Œ®h"fbuÜuåj˜0ô诼ߋšyç´q8êE ¦§:< :š;$“«ª:3$8­! ¨D1S¢±¢¦^¢Û ×ƒ±ƒ]z—aPËÙj1ry(*Á?ÑGt*Å¿4ñ<š" ¯S¹<”›¾9¸#¢èìhb|Ëû­ Qu›R²=5Ç¿ÚËŸÊÞ&’nƒÆpðíBË+¥úSŒ\‚ÁÕö¦O}Z¡—ÏMGÅû\ç}ýþº7½‘q80"Ó©Ç].Ú»¢æÃfCç:+$:êÃop¯"kМ…è6]’éU°ÁÕè¬Q“Gl+ÐJñßÊkø¥ ×)«Æd.á}̪1v¸N1¾åý‰FÛiô0A T{jŽ«\|Œ0±.½ÆŽeßÉdW¾?ÕÖ„“ ß¥ÕÕ¢¶§;\{Þ{гëpŒ¢þ2 šYþ G€#¨ÿËÙÝng|<‡Íf®xepù‚Ku ë>J4ÚíNécIôÁ&† óS»ÖƒñHt*Å?Ë“o•C@^§\™`‚íåÆº×)Æ7 ¬?‰†6¥T{jŽ— þù+¢Q©þ„aàéO}ñBÚ[Ãy¿F°CjboH1åKßCâ'ŽG€#IbVø f&@ŽÛ´:M4uº" NH7in/í­(ì9–G{ŸäýJðIúcõÝñZ§ä|ÇcïüS{–c@ûÑ”xM¥ÅiåÄ>1+|PÑ13|SœüzDM‰ÒÇè¦ñlbDÝÇR!þƒÅ?×2ñZ§ßñÚŸÄ;ÿÔ"¼?m¹àW8Ž@[ĬðAÏpïFg;Ô ´…†Š®#õÝ$/1^ÚK{Žòˆ&Á‹øT‚ÿöâÅïox­Sr¾ã±?‰wþ©eÈ1àýiÛ}¿ƒ#Àà´„@Ì İ÷cCYš±¢ã¨I$tÐ_£,Ýìù(âÜêBü‹®eâµNùøŽÏþ$Þù§áàåö¡Ê+¼?Ue±p¢8ñŠ@L T¨ÞEˆmbrHê Œö޾€ò e vàÖhR’ÿÖÞï‡@¨ë£*I4JJÔ)Ö&ÃÅ¿B¬ã4†2ýI¼óOåê²7è0úºF¯TÑKù(UþŠÅ3ãpâ˜>”,ÙÞ½]núFiÒB·Û‚o­„áoþCþýtþÕmÒ«²§ü Î;)7hR’‡Žäuí”±0yì &YtÉÉ€Ù·N…³Gô“ÎìŸ3®œÔä~À`Lëg„%7¥áÊkž39Ix÷êøù¶4v‹´½j –ß’g¦ÃÒ›ÓàÖžåA¯d€ÏoH¥ØvQŸ†ôîw]sžçSf ̹}Z“_¿‚Δh„§_ Ù)QÏo:®Êüã Zå+½qy|þ4ÖwŽ¥£»F ðËÍZÞÉwíS4pÇ°Øøl]2îŸ0~Ðýå*Àèþ¿…›/ü.=óéü ‚kàºÉŸ6"Òx+ßp8(G 6zñf AéÙ©äþÃ!eØpòË·ÑëØ+Ê`ÿó„íwLò¥ŸA—~†œ.P±ü+Ð&¥@æÄKš¡*øSÁðÌ3-QØ­S&ôè’ëv{o¡Ø#Sφ‘Ø1tncÚ¶ï(Æ&ÑÁÐ>ÝØ)E¶Jò¢AqšIGÊAǽãMðÊF ÐjŸ#ruðɵ)è\2XGåiá?­pÞ›5ðénüñÌÈJÔÀg{pÙP£—÷7°[ƒÞ¶‡—öÜAÔvÎÝVoÝ'ͤ'=ü¼ûíZxëëÕÒ¯¤´Ì6غ·&H¶ßÓ^~Ú{s„Ü6TÛNа§‚æá¦ÑÀÜs5àôzI1ðÒù˜Òãôzñúvn, 2IÁðÌ3-ј—9ºf‡mûÿ+Ýræà? ü«aÇ·á­ï΃ï×{„’=G>ƒ>úu¿¬¥¬‚:¯$/AÀâpâ˜>¨T©ƒõý:VιWÝ+¾gm•”QéoB]áFé¸fÛZÔ†h@›˜¢Ë §¾y:_uGÇ^èG°™1þƒ}ž=wÖð¾°}ß0[}qRÎÑÊ*ë "²D¾5 ÝßÑÄh§-OêA€•K{)šÚOF òU‘§õËÖ¢­6x‡¯N±<[f† mp¢Þ ïã–—á¹ZpâÂooo³ÁÌÑÁ>íÁÔ)ù³­’CzujPwì?*±œ`2H+ò:QGOVJ?+Æ&¢Dßü\ÈÉH•Žƒý'§?˜<äÏ·—ÿd”­®Båè;»|m¹W:Àï—º¡Ô/îkŠQ„ÀC+N_å„—CØ_ÛÿtÁ¤-žäô·uoK×Y-]ôüÈ~wÂÞ#_€ÅVF}* îq,ßòl-þ/4XO‚ÍQ+eåv;açÿÁ¨~3ͺÅûí´å‰#ÀàD˜>”6± X5É2©ßPȹàjÈ¿sT®^–Ãû¤ëæ»ÁÔ¥}ìD’ÍÍL…² Ï‘˜ÌÍJƒ‘ý à‡õ… Ó6 4WZQ YiÉ õ;ß<~w°QTËG7Ž}H¸X´ÕŠõ§u(ÆwÕƒU%;OârÙ˜vrBÏ -´í€¶þ¦ð]ÍÉÂöTUëõHDÍ §ž=5" +ÝçKVÛ`ÅAª¨ FkꤓYÔ¨õ >ž\å†Ýx¬c6x̯xt¥¬ŽæËwO•ý2›¿-ød¥ö‡S5»%rsÒb0€€Âè…c_„qÿÉ ¹^VNÕ삌äØŸz´cÞ |‡#ÀàD)\ø àƒ ôYÀz¢¤ÉÝ$ttŸ9ôiéP¹b±÷šµïCŸcgeM¼/ó )I&¨¬õLQ’ƒùeg€åvƒÙâ‰ò+ŸM«¬m(ÌHM 3¥üujF gšUŸ>›ÝÍ“5ðÈÙ ðÆœlð<{¨Æ%ùä§Eo÷••šämOÄÿ©ªz8xü”ä>  f]5µ½ÐTÖÔC4·§î©T¡‚”4Í%Q \˜8Z+B÷(vÑi’pQÓpX‚")1O7Œp7 $» ?÷¸òì·½0ÕÔÓ}¤%u÷žã;ŽG šˆÞ¯wQ7 àAÉŽ~ò´ûÁ_öÛ΃2ôéóè?Ñ'dœtÙQyŠl¾À国’?mû©(xP¢XJФŠüéªjÔph Ñ„ÔDÏ}u÷¥%%H÷óB ‰24£ 4%¡íÿ+—%ÖR¼ü Ž\ÓÉzZ„Ú“;mÛTluõžöD´)«€Ü߬Ú >]'Ê«ax_ß`“Ú^jrô ó¹Hz™Y™R¢|:%)“W$rINð•õæRéõAƒV»:X²î÷°eïk°tã’ØûÖÒõzËIÜŠlÊ‹¹üŽG@q¸ð¤Ž*&02sN»ÛU_§ÐáÜm³x…}F¶¤ù`ÏöP”¨³xI)ÂE¯n š€Ìºz²ô£ÔØÁ=á¼Æ•°¼BˆÙ7`Œ2–9¹!@  5’›á&“ªW¦&C¥E„–6HÂ#‰VÈ¢\˜&„¦- èÉB}st—W×Cfšo„MšÇ:™oUsϨùÜ)ì r’”ñ5ÈÁ9SæÀê‘1i°x¾'I žI­ªÚýH¦:G¸´;=f½Æc¶›dÂï ^'_ž8Ž@,  ‹&B̓ÛjÒf˜ò  ~÷Ð¥gAêÐqP·s½$ddO¾4ưÜ+‘bìœ/m­'Ž„š´°äïp¸ ÞlÅ™¸$i†öýï~Á™:ŸÜzûå¡ðÀ1X½Íãó’‰&%”*kšê —ü%¡Fà š\õF_¶¹r¼|qä§¢É:žÉñDÝ|h/$ôB5ø2”7Ð"ç’ë¡à·‚ 3@CñN8þ¿HNç”]R¯`;~D»Ï¬"Àרö6r6''sJò¥ué˜ü=œN\~”òð>²Qwº<ÇÒIþ/îØSî„Kû$ Ìé¼9Pº¤haROµ×/÷9^/)vÀýßÕK !6ÿ5Z›ËL¥çNb{Ô³‹ãƒÚOFJ\0nˆä[U^]v„µÛ=ËZ“Ùc"®†U*[ðA¥lµHÖÁ è1>6—vLp€ÎæëJ£Wð$Êkö@NÚ Ø Ÿb¿i…ïÖß“G=7NùFHVnÊ‹eNÚ`¨®?„ý©Ç¿Î{ïp8(E€ \éÇ ¡ï¯À‰_GÅI(šs‹´¼®Æ`ÒŒ°DÂH§Ë~ÇÞ~™Š‰íÏÛŠà7Ÿ«¶í•bÈ™úÏÇ(‘5&-jDÎÒ–mØÅNñ-G@Bàë½v¸kŒ ~…?ÞåH-Üdú±t¤ÖÿíYÒšc[=J.·Œ0ÁßÖD·Vm'j 'àRÕä×Aq<Öârºô3èu§ ÷gë E‡O Sºoµ9†G´lëì"|¸G€é£Ã_ø˜¾¸ùIŠcèÛ3þí¦×FuPóð§`"Û¦½ÿÓÎ|6½f[9œª.„—ý M¯L’0Âè£HçÃzO‡_ _`§ø–#ÀàD=>Û™¨g%´ 4ìÛ •«–@ÎyWú^„1-ä‚]È8s XŽðjA|7G÷Þñ“ÕhZuFôÍo•‘=óðCZ'™aµz#¿wPŒŽçW[સ¬hÜ_Ò[ÅUNX¼ïôØ Af‘Ç(ÎëwÁ°~MÛ“¿V11Á½ºå 0¿;"t*ùÒ7¶»qÑúd[ú+¤¯ 1ŸjŸ¬ª$‰aË«¬rÿ\Õ䤑§Þ].‚ªúý°ïØ7òÓ|Ÿ#ÀàD5\óÑŽâ+yå™6ï®üù _,¦oVmo“­ûýxâ4‡À²ƒ _°éKÔžÐ/Ò¾’R _k‰–²þÏG>Íbk÷ªýùz\ÿESMF{i~ìg”`c$­ØòD›œì=ú5Ð'ŽG€#KÄ´æƒâQø~ÑWl>ÚƒŸ)dyD÷äËï+¿h¤?Vifåü1ÚiÛÞ$6ˆÇÛû:Åï—ÓLæòçã‘ÂŒa ~‘~†ÑN[ž8Ž@$ˆY᣹–h²GA”X¢Q¯mZ4ÍñÓVÅ‘?£Ç0Òntl†Dt*Á4ðm4ú×)-8£‚èìP’óͦþ$Ú”í©%þµˆ«ÚSGËžñ'ÇÀÓŸFGÄqQ4v¨î3þù–#Àà(…@Ó®R¹ª4ÀÍzßê9*%S¢Ñdh{IÒöП„«å¸]Ña²@t*Í{°â÷†Õ)“Ûç8ØS‘¹Ëè¶*^§¨?qEA› U{"þ Wµ§P”½§?õ¬þ§vþ]®TÅë¾Úyæôq8êF æ…š­b?“N\MĦóoRcÑmDc’Q糧£tÿ©‰F\ÞäU®ý úˆN%ùï(~üùÓ`uJ'ZA'ïÃqzÎÊŸ!út`S¤N±¾¤}1NUV©¾M)Ýžÿ´¥þT¸ª¹ü•,{V3YÝw¹Áí6±ÓªÜ}D'ïOUY<œ(Ž@Ü"Ó‡÷C‰këPP¼¬d#FJ $¥§j œh#»d&ãŸà,Á ƒ \Fr‡Åªng]¢èTŠÿ`qãϵŒ€¼NQèñ,GYË7«àŠDŸuŠø¶Ùð .‰»xõv¨ªÂà*oSJ¶'VîÔ/±þTíå¯TÙ³jÌ0ðô§X,ýÙ%Un‰>QTî{¢J&9QŽ@Ô!³ÂûHàx´è?¡ÁÀ€é^× ¡8S½ ¢Í„WYi‰ÝD?㥽µ‹=Gyäe¥@‚A u DŸgT•࿽xñûÛFÀ¿N zÈumûÁÞAô‘~Gê”h®Ù¾}µ ÷Gn4etªºM)Õžäå.ïOuwEÍå¯DÙ³ª+ÇÀÓŸj Þ<œ]Vå–è3âš–©ûªdŒÅàD51+|P©Ð윅úépð¡Óé 7YGRòáDRÕÑD´tJ•èe´Á&9ƒò³Áj³K³·ÁæÊçhV™èë‘“¬ÿ¡¤7^ó–ש!ùYš4g¹*á ºˆ¾^9IAÕ)ÆãX_xþýÑ(|ìÇ(Ón©_! :e¥An’VµmJéö$/wÖŸæ!ÿj-ÿŽ–}sZŽÁ üN¨ùèVkAs·FüÑEôõÈQî{q¦8Ž@L ü¨Våì³Y*ࡃÁ€?=tË0Apêî“À‰ÑcÕ’ˆ–ÕHi=úå¥Iô’°Dô3^ÚK+{Ža0¬Wj?´PYcFßöæÚû‰žÊš0àLªRü‡–âøÌ½¹:eBZ?ËЈ‹á 4¢DO_¤Ë€š´öÖ)mÿ¶í;"ÅØøa]! l‡ž ìŒT¸è¬apá™C¡On è±¥º«¦6ÅÚ“Q§L{ò/wyªÓÐ׬®òïHÙ·Tý1ðô§TVMòWÏ·„è'zˆ.£NlwÝo‰~ž#Àà(…@Ì ?Š:¼“Ða4ÀˆH‚ÉÝÓtPeÊ‚ùS”±Ãù-•HÓ€<4B‰^¢›è'>‚Mr Ñé|lŸ\p8PY]l–!y®£¢“i‹¥ºê­Åø ±qž©:£O'HtÕÂ@óU!3м’®A“nS$tPÌŸ,‡Å?o…Ú‹—§Œä$˜:q$LŸ6úäyû“üTTw©«%±ö4°srÀ¼·E»¼ÜåýiAªIn*ÿÍme¶ëÁ”} ÄÉ1ðô§y`wt‚ŠŠ¦‘ÊÉ+”÷=D×ÀÎ©Š•(éåys8ñ…Zƒª*Ñ|¼ˆ«5XÚ~Ô39 âMF#˜L€¿N© Ð`¯‡Ð~èq L*ùtîȬØC<¤£z<Ã]³qYD¤‘è%ºIkA|›ü1è‘— å8K»ëX ”Wd¦'Cd›`Éò>'ÍÐâ ÍŒŽæ U§@pÙaÅæ½àÂX'äæt˜ö"«Ý!:NIJuL¶e·ÄÚ¶ ŸN—Ó¢D›"Z®SÇ`pÀîÄ‘à”]*º=…C³Þ4øÌv¸M‘бcÿQX½uTÕ"²””`„ Ãûˆ~ÝÁá@³¹Ùþ¤ÔlÇ6U‡m*%bmJÞž”îOüËÝ¿?óq,ÿ Xþ£"VþÁ”½¬¨ÛÜõÇÀן(¿²²>Ų̷„ˆ'  æÁ×ý6™næêO]n5y?ÓÌüG€#À85 Ô‰IÉíp¬®i ã ¤&§Á»^¯—„ޤ¤D°Ym`³Ù :‹Šn‹4è¯6¥Ã„#?A^9’†/‘™‘¦{šúà,U2ÒHt’Dt3³«`©jƒ¡=:ÄaoY8+œ‘š„³¸á7 ›t2W!Gºý=\¸l§ùFZ¶ƒj‹®ž2:¡&¨£©º¶A´ÛlÕÍäã­wÍ\‹ÖS§ñd·Úª•hSHku ÊŽÁèúZØ›0 jtÙaÇìüɈfâiS$tlGóª5ÛŠ¡ª®©ÐAmbüÐÞpÆà^’Ã:ÝëÆÑ}kýI ÑΊꈴ)y{ „÷öNsåîߟ‚õ8Œª¯ƒ}(ÿö–}{ù§û›ÃÀןƾ,…ϯÁd:Löz†|<*ÐÔÊP”¿œ8êOv{¥üîŸÖïø]燎G@B@M‡·H6Ûñ:‹M0£ Ø§çC¡âM˜OrR vTG;pðí‚®èLj¨µB)dÀW}¯†îu%Ч²òë‚Ñšàiǃ–Ó¥U­È¹œüOú¤k j˜#µ)ƒ·Nù·)j.6áªU÷3 ”§:†ô–„ƒ¾y:[âûôþÄUv1dm*öäÏ»ýIàüãª`(fÐø4åLÙ+Á u¿´¾œBa@À~N#˜A««Á­rËœ»E¸œiØŸ&Jq²bCáL>ðÓ|òÎÖá`^Šö‹‚ ~)y>¤h–¥#‡t+tF{îzþØu0‹YЀšZK¶²£ÞËÆÜnÞ}ÿúéqt”„3SiF-¤$ F2OJ44PHOK“öÉäŠV“é¨ÖƒÑÐ\«;:ÖVÕ[¡Êâšd‘Ç[ÁÅ‹HØH@†â¬xø×µÈÿ„¼\Ö¿',^³*ã”UÂkŸ¯„3Ñ fˆ¾’#>㯭-Ö#ÑírÕîÜøË~¼—¤NöÁl¬ må•×å<º‰÷qS.®A,R;Ú¦ö×) Ö) ýÊa)µ)ÔÞùê”¶Å:eG¡c˶ý°ãÀ1Ië)§‚´}ãHèØMªZ÷Ui‹oZ‚¶Ùþ“y§€oJ¤ö´'%û“öóïòô§n38E3šº*Ã{Ê^Iþ©ìÚÂàôþ4îE뾯üÙ÷ÄÐbÝWšêOE·»zÓÏËŠy?£DÕæyp8q€€š„y'æ:uüØ;áwûJJ¡o~ç2Œ8[$&yÆ›4ÃOþdÚdDçn‹Å*ù‚$¢’…šÉ$ûnJdëÝR:òÙûÞKÝïxØ»ÏvèCE‰VI!:´òAAM ¾“> ’j>Hã!™]yµs4gïgÛ@0HH°BúÃS-:Œ{GsÛ`ù'\f\q¬Bàµ;ö7Òâ†ÕÛöÁ®Çq©Ó¡Ð«kNs¯lrŽêÏÎâ£BIqÑR¼@âûÉë[“gbà@Îã×E`°Ík”hS„Q4Ô)+r¿jÇ!Øìt‹»Tüɼj$šôQÂ@S |ÿ?{çE™þñ÷ÙÝôÐ{SºR¬ØPÔì§xžú?Ë‘–; $ÑUåì”O=½;{Å*z* R¤ H =”ôì̼ÿçÙdâfSØ$»É²ù½ŸÏì̼ó–çý¾3³ïó>ïûN]ß'G“¡®Ï+E,w0\S/?3 „A8½OƒUÿöûtço›çûÝâû¾ Æ-†4@"œ@8)ŒšûE½/´E¼ûöucî¾áõ¹ÿkñÐmWh®£ôH­žøÏ‚ýìø¼ô¼l ^š_QDÊGQYÛ'c›4܇šNëó÷ž¼4l¨*gçÇ– 6ý³öê[<ÄŠçxÄÆÆx7nÅ×Y¾`»P18šœu-?Ç|Z_ѯG'ñÙÒUbûžÞ¬xbðóþ'úuï$†ÙæªT=!½Äc º,Ëðä.ýtΊl”möŸfûáF¡Á¯—?Kv¹™A×½.&& Áx¦¸DázO.4ÅÊ_vˆ½´ú”¿kOO”+Ð0M½üÌ©±4VýÛïSeš‡ÏùàcBÀïÓ¦ð.åê†"pR>ìÞ~™‡íÏûeù÷OËAg=öêœ%ê¯×þ½•_ö ›öÞcjØò1¯WãU<¼“ÑKhµšŒNÃt*(Õ?¹~WÚ´nUA²2£GyczIùàïŽÐ7G(_V@ØúÁ“ËYé`»‡Še ¶ ƒêä´‹`ç[×òó<[Fœ#~Ú°M|ñÃZïDuÎs ¡ùuÇ^qþɽÅi'GV¥ß낯ó}“½ïX¾ø«ÿäñ2»<èš7ûOÓ¾çÈ+¢œ].ï³D%ó–›0 mèÅ£ƒñL11»nƒý\UW5ÝS|í>X(–ÿ¶SäTžäÛ£s[R:zˆã:Ô®†.w)ëR*vÞu}žªc[[††ª÷p+©›­€Dºå£üY²ËÎ,Z´i˽–W㙢t¼÷w°Ÿ+N·:çOåä‹ »sÄö½‡i¸`Å*åçiYÎxNGëµ_¬ :Ø¿¡Ÿ%;OoùÃà}ÒÔËo×GcÞû ñÂV<ø}ºeÝêw–-Z¸œÊn¿O›Â»”«@ ˆÂQùàÞZ~¡q×eáÜ7þýÞ%×ÿ™×éüÓ®ÕŸ.=K Æþãô~­–š÷áU:¨áÂ+añÆ–{›š”f¼Vl™kÕ²…}X¾ç|Øq#ˆùÏ»Ñ9+!¥çZÙõà[;Êñ;(•'8 ü’®pìòÇÒäà+.8E èÕEÌ]ºRä”}Ž¿DÍ_¤þzÅ:q(·ÀÚ¹ç Ø¸ú§¿úðÝ…$÷^¢=[?ø˜ï/¾Ï*¶TÉ#‚—­Â³Dç\þ˜/?|g!©Ô|³Ö3Ei•ßÃÁx®8½êßS%dÜ”}@¬Ûº›ê›‹UÑEG9Ä)}Žó®\]ñbÏêYb±ƒý<ES/?3l( ]ÿ<ǃ‡®²yÓ/+ßÿâý·?¥âò×”Þ¥\Åp A$nÊá¯oË/76íFÏû¿œzþ½Ö9Üö׿6ëß³³Ô¯»гS¿Â/qnøóŸub+O0·çyøZâhµÛñ¤qgÿYðÞÞ8O{ºíç¯!Î9ï`0¨IV΃]NÞ£üpX¬¦6lÛ#xfסu3¯ÒÁó„j3‰Ü?Úž‡ºÜ¶<œ;ÞÛ[0ž';ýºî›zùí:9Vß§¾õÎßñàåtyU+^¬ƒçÌ-ûúË×þvñ ÇïЦö.õŃc Wåö|ðKŽK®‹¿ú~Ýòe¿žuéðKLÓB/ÅDúÃS 1Qªy³8íô™ù]0þІÿ¹ÒChùXÛÍzw‘}XaÏ̾ÎÿÜ÷Z8û—Ùÿ¼¶2ú—×ÿ¼¶éÙá‹<Å_íÎ+*‘ ±Q21>Vq›Œ¯ÇÇ'´½ì{îE®üù‡e¯Íÿðý­äÍ÷“¯å£)(•ž%bÀϽ¶ê»oVoüù§Íg]2ü\ÓðœGÏTÕMП)Ê«ÒÐÅZÝSTK´ôƒ0˾ÃéUpTãþ6 )õˆ/¾ÿÅ»UÓH'þåô?¯‹XþÏÿy]Ò UÿòúŸ×%_ÿòúŸ×%ÍPÆñ/³ÿymóö/¯ÿymÓ³ÃÛïSþ)É(é·eÝÚ¯ÿ7ÿÓE……ùG(¿?yÞ7¥w)@ ˜ÂMùà²qw&Uç1¥ÜËÂC®xL&ÌŠ/Þëc:^ØkÀÉÇwëÕ焘ø„Q11‰‡³ê%(p(Ý…Ro§ÿãêõ[ìcìCOÀ0<ÅÅ…¹yGmݰ~á}»ö_>ò†ÁÝûô=—þ½ŸÔNlÞbàù_üä9C†®9|øÐŒï¾ýúªU«øþªÜmz‘:‡jŸ¥2A¬¢¢ó«Þùâ«Ä¢žýNêÒ­wŸÞ±ñ‰Í£bcë™bÙ¤I4KLˆMLŒ£qSU| ƒˆ(Î9t$÷ð¡Ühcß`¸ÑÉl»¡íéÞ»ẅënÑ®SÇ!ÔƒWᤔì$¿éqÑ®ÌqýkNƒIÙx3ÏRlB‚ãÜ¡COè3`à)mÛµï#5­Rç­BW’½mÛOß/^üÝßÿoGãaEÎ q*½K©„l)fEƒ-¬|¢Í¶‚ð5ÂÚ:r¨˜p ÁjóWús´vC’{Vl…‚_ŒöZö¡Í;~ö^Ëí¹¡ÅÎŽSzÖp¿ü’†k|?°³{®Pñæ ²ŸŸXé`%V‚ Pwáª|p‰¸ÑĽ*ìì>gŃܘôµ~Ø Hc)$Ž8È?p FÀ¾/Xñ`ó?ß|oðfþâ ¶ÒöÍÕ·Þ:áä“Oý?ÝáH&ÿŽ´±Kë Œ‘stú 3¿¤›æ?ºS{÷¡ädîÝ‹4VÏR»âHáØ­G÷SZ´nsMÒ­¼RÕ@A^Þæí[ûæ›… ¾%…’{\á@BC Æw)eÉÊGAÙÆÿÁü®å÷ €Ôš@8+öË_r¾Çüâã— +ÞÉè´çrø+¡„ì#9àBO€ïvö}a+<>ÙVNYaE¤øƒW^Éù@¼’‘™™9eŸÇú-2{/)§Ð5¶„ð}r!%t¡eXÓ'MŸù±Tòß½Û¶üläÈ‘œ^$8›S£=Kç¹°Û §žrf›vÎŒ‹;Ñ8œ ÙSR²mïîÝ_®XºøËï—.Ͷý± Z½KI~'Úó<ì¸! ‰‚D.pV>˜:¿Ü¸ai7žÊ‡ÖÏ÷pÑf+¬|pCÒÞè°ÁÝîϱéfÈ÷„½ñ}á«€p#›ÿ$yÏ_³’““yÿo“gd ¦IÊãhu¬å a¥X¡½Ž¾qÝú½r2¦Ï|›úÏ„1c–Ð\ÎëXv,?—Ÿ÷ÜcÉÇ̆•µ ?K:tp]tÕUgtìÜíü¸ÄøÁš¦w¤|¼ŽxÛ‡Þ½e™»:hÏÒ©§ž{Ö%ƒOkÖ¼ÅQÑ1gÒ4O¥ òγùiù‘EŠßO…yù vïØ¶ð_3f¬.¿‚†&P¯wiC ‹ü@Ž}ÇŠòÁ¤ùÉ J»ÑĽ0¬lðf[;xÏÎÞ—ž5Ü/Oȃkö&ïíï {³ýj”&%9yxš·É™™}Mú™¹ÿ3E>þ÷ˆª3)'wrrG~Q‰EsD– MÎׄ5ÿìNøvèС܈?–³©×³tû˜{Û¶ïÖñg´ël]wœMÅ–[(qÎÀבåè°a â’¹¿nÚøù³fí÷½ŽcF#`?®¼·7û=Ê{Û¯ÑDÆ ‘EàXR>lòö‹_ŠìlÅ£ô¬ñΟçÀ5¾lgßöy­÷““yÈO*5¢Ó¦Ìšu)7)K¤„[ù$¦‘r†°ÔÔzO]ú˺Üôé3¿ ›p‘”ú MW?C“Ömf5>KîiÓZHgÌéý¨ð§Ñ§µÏ#6=}˜ðÜßÓÒc)ÖÒ…Oé〟Šý–¸ÝnVxà@€ïl¿ÂOJH ŽEåÃ|8½(Ѹò¯cð¼l~ÇR}é[o½u÷¯99g(S]Bð.¥9"gTèáWŠ¿;sÝ„W eN"YF6 ©V¼‚>¼ý£ŒŠZ1ñöÛÃ~1š³O©¥©úSZߟÊ0€Ž;x«‘ É[%³†÷"[œä*ⳘöK„¦–¤Ž³³ô~A@@@ ”@$(¨KZñŠÊo˶GÏÌlfê"²~\BòK¨1î3<‹Ú奫gõ z:»^qì‚"AÖjˆ«5BÉm4dk; íÚFÇÛ±ÍѲåŽq#G†Üj6eÆŒ–”,Kv&©:iÂ;œ¬3)H¶^{=fwò'ãF™«AÓ ëGY{¾§ ^e£Y³øoî¹ùf ;´Ùa   P%(Ub'TM lHÕ{t•7ñĬY½J,qiƒ¨!~ )ý¨!Ï«°Up¤”PßÛȧY)¤ºx¯*QL³EŠ÷îé/Ì ËˆÜNRR$­©¯ h}­%e!5ò (ýKš% iQéš±a)MÅ(KÆJMÆËŠ%¥†öt.É_ŠX #…j¥D©²aX*–Ò¥œKs·ÇZ•V×÷„ò/¤ô~¡ ÷«)•š&¾íÙ²åòZŠØ·¸8!(!„‹¤#ŸÀ£Go¤Rò6‹KKô\›ègXòTjøŸJ À)dY8‰”jøWïHhCJARhÅ-[5¡ð”@é“¶`ñ™=¸Ï«=ÐuRf¼Î»§s>-ó*Ý•”†ªé×BþJ Ð*!µÕ¼wI¹úþääM4­\W©)\š@ù¨‰®@- ”Y~¤h¼ý“£“ÕC{bÆ‹} iu·,«+ùt¡Æ<íEºØ…†0ÑjZÊÉaCé(Ï\RJv’2´“ö;h¸Ê;tMíTÂù[ÂqÖß3|8÷£‚#«Â9N@@@@ ® |Ô•â@€ʬk)8o•+'“³²ÚIèJ–ö¤Ä’­"–÷47$¦ôXÆjùó*彦Ѱ¬BšÐ^@–EóhhåUH&ŠM”×Rº8ì0õ1ÍbvbNF%ôðh`P>8²eÊ o„78ˆXöÁ#¶€(€€€€€@x€òõ)@@@@@ â @ùˆø*FA@@@@ <@ùz€     ñ |D|£€      |„G=@ ˆxP>"¾ŠQ@P>£ €€€€D<(_Å( €€€€„(áQ@@@@"ž”ˆ¯b@@@@ƒ”ð¨H    OÊGÄW1     áAÊGxÔ¤ˆ'å#â«ð å#<êR€€€€€@Ä€òñUŒ‚€€€€@xp„‡á#Ř´)§™¦ñ!eW%UG)DBMÒÍÉÝY~9)µÓWå'~Jˆ\©d6yoÕ5}ÞÌI–ûÁ)€€€€D4(BÈ{ï}*úHtÞݺ&þnXF)…Šöä[qF¾t™%[‡:äî\ÝÝR¢»¬|Gœ*rÆj”GFRÊc»,%Ÿîìêü¼Û}[Quñà    ‘B )+dÔÚ­÷§^šïÌ­I­c—ÜmªGÎzÑ5w‹Œ2Šõ W²W‰)vD‰m Ç‹M-û´ß–Ðõ‰ì’m¿ã!÷ÿ|Ü=ò# €€€€D&€{õ#¬ø\nÇí=<Î銚Ӣ8§Ýߨô¡ìup Å#dÅå´9΋ól^t°¦;>•òؽ”iS­ñF    áC ©5vÙÚÁ ç­¤ŽÓuç=ý*¯ÞðŽÞ!Ÿ§c4¬ã<¯Ùø¶ÞãÐFªù¿N|”–儈"Д”ï0+ª=çwÝw™Ó5™uÑoŸI‡åi´Jå¼Y–EJ퉿ŒŸø†ë H£Õ 2¦¤|pYýûŸÙ,!!afË¢ýjð¶aÓÀgYZPQ®¨ÙÆ]O²6¥º Ž4A@@@ÂŒ@Siàr9yr}ÔÀ ß©4½ý¹Ûéiñð¿X–óv|¥+©uètRï»Ëäm*õãç    H )4nÙºÁó(\´ÅÄÆÅŽîJ«Z5Æ£Ý?,ËæŠrÝU&/æ ®ƒ€€€3š‚òQnõvÍ gJMoCËé†Íp+ÿ;Å+›ÔÚ]ý—ä3è[kšBùcÀ9€€€€@ˆô†­¯Õ#ºeÛv‘‡¢ïx„mU²l,c|óf“l­õ#lk ‚€€€Ô†@SQ>œ%Fs8ºF{ T(¿ãQøU…eÙHF‹,4Ýè:Ë å£*Pð8æDºòÁå³ç{DëNGÛXO^ع²ï’Q“ºì@ç¶å#ÒëÉ.:ö    L Òµ\>ž7Áø(]jñQ–'ì•–Q/·ËrcÞA€8ö D²òÁJolùð* J;îSJÙŠ“=ì*앦cÿq@ @@@@BI ’•æÆåãÆ{™¢Ž¡¼WV[öH¯'®+8'ÉZÛòa[?ôcÌòaËí[Ž¿Q<H&ÉÊ×›oÃýX,«¯ü‘|¢l    M€À±Ø ¯mµ„´¯ÅÄÖVžÚ„·e¯M„°$Дðõ›óÑã§DÇ›ïñ¦×jè•bÀ¬OÅɯ.ý§(Ú]~“׿õ°kÄ ÓÞ › ë p     6HV>‚Úúïs’™Æy¡@IDATHx†ØûÑk^vñ}O»Þ}Q¬3BøbŽèxÓÝÂѼ•8ðå¡Ç%ˆ–ç_f3Ö>¨å –PH@@@@%ÉÊ3°‡-Ùû@¹T ×îÚÛů>Æ‘ƒÞk[g>&öþž(Ù¿[ìûü!N׫¿P¦!ö}ú†hí•Ò¨•Gé²À¶ÜPx *ß­béÃùLV›Ë    Ç4HV>*5Ø Ó(,Öœ•üíK4—2M³ÀO®°—ÛO^œ‚€€€€@‘¬|T((ŸxŠŠ8ãþŸïŒSž’’œJ€€€€€Ãš‚òÁʆw+,Èß_䌕Ŏ¨°­2–e,),Üm˶ÂB0¨HW>Êbbm߸þ'òÛޝ¢† ʲ±Œ;6ÿúåì+à ‚Ü@@@@@ È"Yùðm¸›ÄÍ\õ÷¿šžâÛZöákaéX6Ë(9¸ìë…kI@–Û·a)3„@D²òÁå·hó*´7xÛ¾y˼m ]宸Žt^Žeòʶ}û‡$ËkËÎå€cš@$+¶Å€ðÜ/ámé§Í7KŠ-é<Ä24gØTËÂ2Yžâƒ‹ç|ðA™¼¶b—%lä…     P[‘¬|0 ¶ØŠG1äæýøíâWÆ´‹º ›áW, Ë´ê‡ïfææÊcYic…‰å‡åƒ À€€€Ûê­|Pë=—¶ø0Å`»âF|m…¼ý¸dÑ›~YùÁ¦æ=åÂã.Siá¼Y–e놵o|ÿåçß•ÉÉò²Ül¹òAà@@@@‰€’ BÉ#õÍÝQߤ’Ù”ÆiõM'DñÙ²Áwm¬xäÓM[Ôï¿=—¦rk¢ßÀ+F7Wçn_¤uÈç¢4œã9<ÔŠ-[Ö­ykÁ»oðp+¶z°œ,/ËÍò‡…†dhz:P‘—Õ·Øz}8í‚  ©®8ã²Kž_¶p!÷Ö‡«c+o¬pq¹õ-ë~ÙnÆÎfÝzõÞØv`ô¾¸J )ã\á°¸Í|ÇßñØÒ¬—ø¡Ó¹ê‡gË|¥!KLÖÒy/¤Üûl¬€0O »"p    C`Ì”)-”ÇzŒ¾ñþòÅ_,®õ¶|èš>ϰŒ «ÀA‚ü»>„(®=ïƒçPpƒžg™³òáröó·‹WløiÅÆ³/>ÔèÕç‚mݺ&J²4D{ T¬'OFY:­¿+ÖœŠ¿®Î$3†4JŠs·­]ýÅ·s?™_Pwˆr°6g±œ,/‚    Ðx¸O=ôšƒÚýõ•"( ëQ©“¶K!Wg¥§^V_BŸ V:xÈÏOI¤­9mÍhK -Ž6¾æì=à”ãºõîÛ7&>¾eTLL3‡^óçÐ=¹Ý)ž×q&l¶ý÷†i)ÌÏ;°mÃú_Ö¯\ÁaÙ²ÁŠF.m¬|°Âʽâk<ì ó=€€€€@ãHJMÿL ÕvzZ—úJPoˇW%Ÿ%žLNI¿ 3#õëú ‚ø¾Ö[á²çƒð¤n¶2ÄеaտжžŽYaá§ôÌç÷þ /ا™«sŸ²}öö| –7ÏÅJçÉó:XÑà•VB`õ p    O€Û÷–R æðýÁÆn\×+­NÎN/H)¶QËúù$wfl½ ]dnøs£Ÿ- ÜØgKòm?íy³Ïsèø`ÙÆÖû¸Ò>Á)…½UÎ7>§kçáŸ'ËÃrÁâAà@@@@—·ë¹}Ïí|nïCš (n÷mÜ`E[?áÙ÷ŠRªZKA0„®cl¨NÙG×öÒ¶§lÏÇþ‡©´µpiÂÞª¸îŸ†}nçÃé±2R•âa[Lè2€€€€4o{žÚõ”c?ÚF•µ÷ë-@P•„¤´ô¿+K=Mó?ÞÎ6·f¹“ ê-ah`¥‹'ó<Þ\>ûòÕ°ÈùØVv¯_Ðr¥í{ã×9ícŸ=+öÆC®x³?|È–öÅ{Þø+Hp     Ð(¼#™Ø  ÔõR“÷fMJ}&X‚Uù`¡J1MJµš¾rO˜ÎaQ¹ì¶Šˆ¯ÒÁçöu:ôó¾’ûô¢Ö;mÏá ÷w²}ö¶ƒ• >¶[ ±Ïíë>Qq     GÀ;ǃ†ZQŽý¤&ÆSñàR]ùàD“R']B»Ù´$WW)å\Úþ£ÅjŸÌœ0çL„›c¶¢ÁÊo¶ŸÍÇÞW’}Ñ¥mx’¸× ž·/Á>öÙÛʇmýà=+öfûûDÁ!€€€€4 þŽGérºê&nõžãA9ÊJO›l ªmT×7#·ûåèžwÑÿFýý©}’}’*›2å‰ÕaëhèX¹ldnª‘ѨŸ¦_`ž}ò_ÛǾ{†Vž ™®|/á@@@@…5Pãi¤RGjª¶ãïxP÷ûjú>˓˃5Çÿ`ÁYj×?U:/xN“6å4Ó2/¥ãn´u¤­* y‡‡óU|‡ª¤óÕ%4M+W2ª ?#»H–å¤plåΜ4ay¨eC7|= ÿ0¢m¹Â1蓽àYOžˆ    ¹x~€€€€€„œ”#F     LÊî!å£A0#(¸@@@@@„”ÁŒL@@@@@ |àhP>32€ò{@@@@@ A@ùhÌõËä­·ú¹ê—B`±ŸûwËD¥Ü¸'Ã…P    µ$ kÁýü0¢­²½}²·ÏÉYÚÃäÙ|B’5ÎÇûÉYr‰¦iS•©NVR8&&Yø^·Ÿx9ª—á)Y«ë1]¼£ ÛöŸ’©¥YB%;­NxðŽý¹¶?¥;_JÇ„ £<Ëm?{?åŨž–Uò¶TòÓ‰ÉVŠí?ù¥ø6ÂÈ[(M…i.4-=e”ùoûzF–>R ëE‡p\ð@’ç'Û{¨ ôr׆VÂêºóM%ÔõJ‘ŠQæ¦ÎŽéLüº'ögûU·7J<·RئQtsa ,3gRþ•¼&g9+³ä=)ä’J‚‡¥”ŸLLVgé¢ù™Â²ÜO¾ß–ÃMÎÔ2¤²n$VUЍ(µ€U— ÞQ¼ž, ¦f9ϵãVñõd}øpäÈ5%¶_U{ï(©®sɨ1Bª›üÃhB>GJÍS^tžâÍÿܽÖ7L*±ÞÿšêjKļÊþ%|Ó`‹ŒR¢eʨü=¶¿”bùwáó‡’JÖüŠü*ijÃb    €ò(©ú„s¸Þ¤Æüu¬ Ly9ú8Jêø³zŸ÷ÅÑ’”–u«Pòu'¥öºRæ_|ãÒ@sEÌH+ØýøìÇFû^ ôxÆÛmã(~q…ðJäÓyó ~8z€òQO€DŸp{Ñf²ìxüÅô „Qr=©ïúUØxå)uÖ“Yò¥¬4²QÜXÕÊW.-êšW’òÄËqíin†ˆLv˜;GîË#›†ÿjZ ¤Ø´Ã`    Á å#HƒF7½iYÖp²€ ×ô£¹Ê+8|))o9eÔ%4çc„S‹FÙü°éк+ü³ÿ×¢­4*ê9ÃSøT%+†à*ÎIÑÈþÇK1ÞaV|™˜4Ôj{Aá    u&å£Îèj1ÚõM¿œöݼ#µòŠS~É)AC®Z&Ï÷°7zõ¢×ß/,Ÿ¶T§<%”:‰‰£N>¯]ÊwJÌâÛÙZfBkVÎè9•ÂÁ@@@@êAÊG=àÕ&ê}·n'«D¯r%¥»ÂÐ(j웜)w•o/ê·ù¡ÍÄ;Œo}óˆïtüò?#cv\;_>NN^îÑt} –[0üÃTwžÛüqe©³è!ËJDÞwšÐÜÜ–¿»ºððhü‘A{k„샚åÔ¶¦¹øÂyP¡"1rÔWGûÂy}ÒF\ˆ$vIµ‰²€€€€€@€òÆ•Ñ@@@@@ ’@ùˆ¤ÚDY@@@@@ Œ @ùãÊh     I |DRm¢,     Æ |„qå@4ˆ$P>"©6QcP>¸r €€€€D(‘T›( €€€€„1(a\9 @@@@"‰”HªM”@@@@˜€#Œe +ÑF»Ÿl+EI{aÊf–´š SÄ*]î=²êeçɺ2ÃJ^    áF@†›@á$ÏèÔIC,!†k–y…)õ¾5ÉïÉùÎÄ—4]¾5óщ󥔪¦ð¸    M”*jüδÇû•˜ž—„g8”aµÏÛ¥uÊÝ&šN³DD™ÅB·LQèˆù®ÚâÅ®¸NbgBË” eSk¥Ð’²2R—T‘<¼@@@@@ I€òáWíÉi“ÏV–± Ê(Š:c×·zïœuB pH•¡9Å–f=ÄÎ4 \‰JIýú¬ô‰ÜýÜsQžý¹C”RÃ,KôÐ…ÕÕTÒ$ëH®rƒ’bY”î˜û‚ûÁl?qp     C É*w?Ý<ß“¢*–ŒQBÉý.]ßîñ”|Wr¤ýUßÖc=uªèÍ%>éyu ¦íA¡Ë—•©Æ‚GóB¬ÄâÃ"¡$WS’fh.+'º¥òè.Ni„—\ 59-ó±”Ïë”1"€€€€@hRÊÇ]©“Ž/‘2IXÖ JhÇÑð¨*Ë?|ó‡¢ó‘mõª¶m‰Ç‹¹Ý/§,„êuh왳^tÌÛYåÄôCÑ-Ħæ½ÄºÖý|Gœƒ,"sJŒžž‘ºµ^B 2€€€€„*ßa$_PDëžo‡Â#•]ÿ¦µÏÏ­ŠöÓwXÏßÈu5óæ×oÿÊzçkh±¦ÍIâ¸C›D³âC¥gIâ ËÚeššóˆth7Îz,e^@‘@@@@œ@Ä+I©“z¾± ÚµÿÞŸäIû–‹º§j¨º<ÕLÌ?n¸™Óš¦„è×ñ¼‘†Êù€€€€€@¨D´ò1Ö=¥§éñ,¦Õ©Z_ºyŽ£mþîPq zºÿ@Áß"Oä    ¡"‘ÊO0—Ê×ýЯ¢ë‘-¡b× é¶Ëß%:åm£5³¬Ñôˆ&× @‘ €€€€@£ˆHåÃ0rþb ™xÒÞå6˜÷ÌÙÀßé6öÑÇfºH @@@@’@D*ôùŽÍ‹šm ö6$ËåÕ¬¨tؘ²¬^!Ë ƒ€€€€@ˆ DœòQ:4ÉÜ)o»bv –¼©;½yÑ—Ò ,Sd    A&qÊÇ]S¦´TBÆ6/>dT—ÜÞØv”¹T2Flj<)3€€€€Ô€£~ÑÃ/¶*‘-Xª(£8ü„«ƒDü¥ôµ­û›šPßÎJMÛY‡$@@@@‚@ÄY>¤KyMź+,×Wˆo;/òœ‰šÒµ‡ë›âƒ€€€€@cˆ8åã… r„ù£[kžo¾§<]g‹Ö¢OÆËbàìùå~­‡]#N˜ö’ªßª¸´º•øºËEbm«þBjbJÖc)_–g‚8 Dœò!%ÍrîÖæÝ nÀËÅ÷9I$ ¸:¤.þ•ïˆu¬kÕ/hµÓîÚÛů>Ƒ҉ì1Ýzнsþ-öÏ{§BÊ4ľOßí¯½£‚mOÚæïÃ7(ºþÍI_œ²«dñ’$÷´à›sj+ƒ€€€€@ D¤ò‘ùXêM©Åßv¼ÀÜÛ¶Žh*F‹íÖ[nY_î¹ÿó÷Äž9¯ é¨l(ؼVDwì&¤3ª<|]:Ù&.Ý7ë±Ô÷ë\ˈ    a@ "•§Ç+Tºöü¦æ½ÕÖfÝÃsíEXÞáLñ]ÇsYñx·£3åÞÚ§€     ^"Rù`IJs›Ç4e}¿°Û%fv|çð¢^ƒ4y®ñiÏk¬åíÎ`ÅãÅŽÎsÿävK«†(¸    ÇyLHYG!“22:È|k>­Q{â™ÙK´þ{¦}xNB?@+Z­m5@¬ou¢i ­Hjâþ¬ô´™u,:¢€€€€@؈håƒiuO7Œœ×”W·,Üož•½D=àŠ°¤.V¶9YüÔ~uÞö/µž×·ª€ŠÔŸ·ûÞdæ¹âe‹¢út‡&ޏ­G”N•QL6›×hýãÇfd¤.dUÁ@@@@ÂŒ@Ä+6ïQ§ÿQÖ3ô±ŒÎ‰%GŒ^9ëò¶‹¶ù{„¦L;˜wÏ GNLK±¥YO±¾å f3N'¥ ¿YqNôuëÞÐueT_›“54 ~iç!E¾Oßíˆ!CŒGI±›ö‹¥3ú³,÷øýµIaA@@@@àX!Ðd”®·û-×NsãµRY£éô|²†hÔèW±F¡cä‘B Cw©|W‚f ø$„Ió.æ*©žšæ¦õi·Ã[ÔЭó¥Ë*á$v%z”XÞ~XÕæTÊF}’5)õJZ8<Ç€\*À 4)åÃËßÝO7/´òÏ'åãtúa'RÚ“&`þQ@‰-Ri?FǨÅϦ¤ì±ãJK+,ñ\”Q(úïÿYïtd›h[°—4˜ªçƒ9¢ÅŽ„®â7Zqk[âq¦©;‰·6³££×8·{dí´[ìA@@@@à%Ðd•ºÖט´)§™ÊL§ïˆ #•Ãá°­xç[Y=t8|%…d  EÀÑXW—ï²k»uÅ%¥4]šÔŽBÛrúœì‚êÂÃ@ zýÞZã2²õã5Ója*ËÔ…¶÷†ûNØî–Òª>VàWî~’½y#,e]!¤ ”\%5±\Ä9^ ¤ñ™·,Ê<$²\òÊÕ«|$§eÜlyŠž2¥üHš‚vòýÀ% ]È»ÝÏ%‡×;„ëÂé® ]NÇnÊv}Ê"k1•ÊDZ[•@BF Ñ•¥”\qe{j¼ˆ›É s‘*.lííâ¤j, j#YË.o»JHí#]/òÁîßBF# NNtGtŒöñ³)){Â@œ°½ªUGïië[k²äVz|®5¶™§+e:Ͳ¨¦°ÄëÓVçöž¶êk)ÅŽ.Ú[kFö+ ,åÊ¡Šö™EJÇ0z>ghB›cIó eÉ+;µ{±rèÀ|H‘¹‘d{4+=mz`1&T+qw^¶HŸmÄ«ì†É±n¹$¥¦ž•žzqÝb#€€„–@£*Ë®èxÉòËÛ?£„:‹YÕ¸ ¥„F—NÊ:ɰĄe—·{EH}YCö×MRꤔÇsO*êSb¬èèèü/·û¶¢Ú¦W›ðI)é×SÞãgg¤žYS<ÒÅf¨õʇ(ôªúñ;=û©í19æ¡¡Šï%e>Ö{¹Š‡Šž·zØFг5ÂØjNí;mõÄuãûÿË/¹£ž²2˜íYz#= wÏJO™]á½£F¬!€Û­´l#}ˆ&õÉ5k”Kn·×Zôp£d`¦cR§ö1DÉ©G0hp¢|(÷ÇŠekŸ¶,ã®Z–ØA–’¿ e\¾âÊ7œúÑ®¯k_“ŽDÓ2†kš–LJ‡F ´óh˜È£Ùžw%¹3ÏÌr'‡lx—.+Méy­6ò†cXRàܤ Í'%ЇÉÀ… ÞÓVö=`z—Tøk#é&•²^é3måµÍ]1·|wO¯#ÇB¦É%MMÛQSœ±îŒ“ Ãz†ÂœB÷Î^F5#sÒÄgüçq$§¤_°ÓHO'Å(ÖÖë£RÒ‹5§¸1ÓºÌ?ý±î'Ú†‡îE5”®µ KÉršòBÖc)ŸpØ£å9*5ýE ÿ®Tªƒî¦÷ÁazìÖ¤zVzš­H V°vKþ§ ÝÓ2zaÞ‚•št ÏL`#ç3:mÊ)–e>I‡Üà×”Tóf§§ÝÀ׎&‡±Ý½O=Ciÿ$ÚU°ž&g )ÖP7ÉÃözfäÈ‘¶ñJ$§¥&«Ëܕ⯒R¿x~›üpÆE¦éù±hNì¼òI]& ËšéphšáNù‰ó“6ù|ò^Òt96ó±”ÏÙo´;£¿éQï¶Htžþăæze÷¨itéT*S>É1/Î7þ÷½‡8|rêäk-¡†Pþ‡:†¦‘B{ºS¸˜Á6¾n;¶l'§fÌ¢sǰ“z'ù–Ã=€€@Ó"ÀV…uË’Ns.[¶ö=K©Ú*¾r¶·LkþŠí®ôõ ôX×›ÿ7sRê¬Ùé©7;¥s(ýqöÆÞ[_—p3Ó\Ÿ5)õ…ºÄ ³8×êRE‡™LMZœžþåT²ä-ªvЇ/4²‚\y°¤ð«3V¶ðõ¯é˜-4TòMiªÜð­*ìØ”ô.¤x,%ùhÑòD¡‹‡¨Aúàè‡'òïj—øtÄ\ËþJj·Ä9ã]tbïýÃñ¹ÇSò•Ww:]ƒe¬¿Û=µ³«u w/Ép˜ÙñÖQ;g1µÿ›†¸ÔNƒÂÿ‰ÊcYÊ«$±¿E×iNÿaV<¸\†GQ¹ä:‡tžK…%©ÜFÞ\V&8<…M$¿á4¤o:ÉðšîpœÐÖq†WááëìÊš Ô<Ñùw(¥\ð  M@Ã[>vnÏ"èWÔ<) Q¦o.»ªÝÓ?Üó]]Óã‰£Ô›Ï œ^œFu=z®¦Þ@VõiM½¦ÔÃìRÂEc±/³eýpÆ¥Ô˜ÂyÓõõd‘™ ,«˜v£öžVÕƒKcå¹²‚+ '>“–:W É \¾¥NÆñš~)¤×“³&)-ý®¦NMðI/À<å»¶âÁq³RRvQcùSëpÐð¤)SØ t%Y5þé“¶Ï¡êGŠÁç>¾‡Êà…žH‡¤¡VÜz>Ùç^÷S-é9"eߺÂfIÏ,-C̽ªZž–%æQ€óxe²=æ”sé}§† èõ2%iñóÊŒ)þ¤úÌ-˹[TÊŽ½»ÒÓ¶t¿’Ò8°Ü_в¬Ì+?÷90•ç.ÒNÆjNýú@V=ó‰ŠC'Ð`–5×÷‹/(Üÿú“ *Rú3>ÎsP>D‰¦š°ûv§ÓÈ-Ñ¥ØðÜEÌ›¢ÍþcǧÆQ¥=jäÔØH*C½¦£Ó2ìä½{Ë£xÌö:ŸñåïŒJ›²•æ³|OòÞ0÷žúõàVȨ‰\BC¿&Ø^£Ò2¦KaeÚç5õªRc„-‡¨¨tiæÙ=«¤DÍ£!OpÃèi÷¸Kh× i½Be¸y—õÍ`Š³Ð£É‹iªîºé©[KÇ››8œÚIå½ËBümTê¤sK<Ëvm¶Û2{Rê}ö‰ÝóŒ^ÕR"}ÿ±êÏô8Q£1èîþþÏ­zyõ=¼ y ©—}£ã£ÝO¾fyŠ7ì²&¥x éVæžpZ~Wãqÿ^Çw7)ÂfÙi»-w“ú):ØHI÷6ÄgNš°œœ^;­Œk„¥&Ê-y„žŸá³'Mø!Ð<5)vÚéÚ{©‹’d™¤8?¸«`é ä¿q–;•žÉªœŠ"eœæCTáêXnÝtTH:5H·(M¿XÞá^4ë}²:þâ“ë N¥gûœW8¼ø¤Þß}¾r}qñþÜsˆÕÕÄÿ}¶v5ôC²\^³Óü•s(6 ç÷ü2 ƒÞ ^ËU…„¤*&=Ç÷?cÛ=”^SU8)ú’ïbêÜxŠöli…/ß?’")*ØÿWú[kŠLHY¸gÝ•}žèûÑú€Lû†!þKè…ÔpØACæ‹}ÆóÝs¤\¶ª{ôªì ¤Æ¸·70+%õSRPì^Óû¸×ÔÌ3¹×´êÆ¡T}hœúWåyÒA|‹¨Õy*ü——÷žÚᨬ]Iîîöyé¾bnÅk¿ŸQ¼Õ¿Ÿ±Š#·PÏmy¯jž'¿ Åâ^U¯ÌÔ«JÙ¤"ÕÐK=˲šÊ½+Ï*JÁ÷³=×êš~ƒ©TKýÁŽ…Ô:ŒRùŒó6•ÙŸÒÜ;㑉«Hù(‡ºÉçS>—–{Юiÿõ=·½½ªÔ1M½ª=g5ᙑáƒr»1¸Žz—§XÞO©òШZ¹™Œß—”–‘Cu>€".$éÖRcwˆ=±¹V‰Qà2ËĘªâñœò—7ºÉ:aŒ§ãê“gíÜO²­%j·õÍEdŹ™†'y‡;U•?=ˆÿ°ª®ÕG†ªÒc?bFJú!ÒFÚf>–Za¢G “Qá½ÎŠñYH“ë‡\WRüÛ¼á5ú~Še=-•y˜üØs2èú÷d¹ÊN“÷l!.ö”ô³t±Ö׿Úc‡vw¬S¤•û~¤w$¯ò7­Ú°¸  ФTø“ eÉÉÒkÚI¶È‰ùê0ýyÅö¨iït´8Ã}g^ a*õèÑŸóQ{k×k*4ê¡ÂêZgwî\² g=-"S&Y€½§UõàVS¶ ùù†©k¯ª7 )æÓÔã‹v«)»IÉíô K©ç;šÆ™¿B ÉÝ´t*7¿…Ã’’aP—wÉïý¹Þ¨È )WîG]é;J¯úý¢WUœøäš3 ažàG&h§4çÆËžÛø·ÏîéU\]¢<ɹĠïz8\_g¹Çï§yÇ“Âúº:J‡6Ÿã9…óiCyn¥FóãdÉ”m›eïË;Y§9P3'M\\]ÚGóçùR1Íc¾~zܸB¶ˆ>\Ô–ÝÞ„úäÉ=ù$ë¿,Sý´óÓ\±á¾réRŸnXæw4ç"-*VÎÒKb<Å"¿ïLwêÿê#ƒoþÇÄv*)<£ïýª;Z¾mˆÂher™ê¶,ÒÎïD®3:uÒ²ª~U_“<ïã~R,b/Øg1›¡:i½f›ÚÓ3;R—Úãåaú¿…Ç;*5cB¼#&3_µ-¡•ŨÓ`£èÔæ£òpG9àoJ›|#}ªiY<¿«O}%+\8†Øã²C*òŠ+»Ðržô­Ž:šðzY“' €·7ðbßV8-ª³|ÐÜŒ[éËêÐZý¾—àm,{¬oèôI»7ÖãwÓŸò©¢KÛ~öŸ2÷ðR£{ Yz†ºbõÎvãÅ?]þ¦…YHòÞC ó—w™{ú(˘D ÇåT– hu %cS§ž@½Æ?S¸§ªë5¦ü>¡Æÿ2Z:ø‘šÊ^U8ZæLe™ÿ›‘潨±ò)£“‰UöªRæcÚméèLù[Ù°ïDXj¤ìaÒœFÛÊ}iùUâsi›©<ÃY¾²Õ°>§Ã<¡9R¢ôØ­ÅFîÔ€›JCÔÎãñõÞžìCEy.ÍÙßWñ±ý¥S?HÔ«:˜{UixÖÅM­W•¾Pþ11ó6,™k(5èXÿjìÖìxõ2Ós¤Ùôô”þ-ߘI?ÞÌU$ãZ‹3öV;WÀ7BÇ4Ǥ-õª.ßPäY•XwOžÜFÄÇñÀn‡ • Üù¡\f±ýޱóã=O"ÏvnlÅ“è}ýk{üw÷ÓÍe¢*f Smã"<€€TE ¼—½ª‹Áò£¥Y»+­êÒ¡F{Hó´70Ð^ÓÌŒT¶ÜCÛã´TéR<¾$«ÂûÔð/·2„ª÷´:†GëUåxÔ‹Íc͇ÑPª=¤ˆðüTÁÍi·œzc›ÓŽEìWê´÷¸W•Êõ™íý®QÎÄ«IÉñP™—{Žì&«Ç8Z†÷OÕOìµcWÜ7å^Uš›ÒûIS½”Çóî{ŽÌÈHÝ^`×÷Ê¿à~0;XЧ;Ë}?´ÿª](ò¬*§ç'NÜW•âÁaC%³¬Jñà<ÙªQ_ŃÓá…% x0 8`ðöx+±êÒY6¢ýƒÔÀü}Lquëé/;uqžµ¼VÃê’e0{i¹¼óÑ'ÛMäþ=55¢BÕ{Z]ùkêUå8|½¾=ØI™™N±+·Ï¨NøWM€¾h¾‰†¯u¯új|¥xeÃø·)5$   4¥²ܲ+ÚÝMs2ž qVÆ OöÒ$n8ˆ|}¦­\EV%þ>MÈ)Ã3ÖpgÈ2@   MŽ@ƒ »Ò,±5ôdå¶Ðç@ <Á,ô÷»R¡Ï#©9”ù mRSsC))7¹ÍU‹¥¡Ìiƒ€€4= ¢|0VZ3þ•Pá¥_åY±qü¥c8hÖŽëÿ-Ý÷BVX)Þüv\,¯2ÀH@@š&S>š5OœMXC³ª‘’ÓO{óá¦Y…(uS$À+£I©O EÙI©ñHé8ê÷=B‘wmÒäÝôI½p÷>õTLUÙpþIîÌØª®ÕÖï^÷S-ùû¾ñø»8w?÷}æ'xŽWë $µ@ÊÆ²±Œ¤‡0   Àdµ+õòËÛßf)ë%û<({)·'hÍûõýh}nPÒC" pŒàešûN[½”¾Æ}v0E&åã‰õã>x´4éÕ«5©Êœ4ñ[;ìèôôNf‘ZݶYû⽇PB~?;=õ¯öu^2Zå?’_wöKJ´RshžåN©4,“¶E{ŽÖ=¥§Çc¾-5ñiÖ¤TïÇíkÕí«‹3úáôkLKÝKÊŒ".E‡ë†îv—•ã´Üñ J³€®r8ô»g¸'üZUô‘Ñ‘”΋N§¼`†;å'; 7ðw™(K\N+ÙG/ä¯éƒœ“ùzRjÆ Z¢| 3)ŸÃg‹?WõqÔê8ÙyØûä‡3.¢¯ËϤ×þ>ºg¢(¯²ÒÓ6Ù×ǤM9ÍTæs"Þ1œêè¨e³ëš†¼Ž¥ï½n§ƒ=€€ÔD Az mNûx÷ËôG´?)êý-š~cmþÃw»¿tØraÇ"¯õÃáø3=S‚&¿ß9ºêi¤GÎ7„2oð k‹ëIžË?¸§Äùô1Êó|ÃrÌV R^Qy»øâ|ëa!´O¨á|–ˆwžIJ˜›¿tÎáø«÷†a¼Gñ–øÇ«î¼º8Ü£oYjjt¬þGR~ÎÔ49Ùã)yÕ›N‹ºÐ> ÿ“h#åO}lÖ¸ªòHJKÏ N—IAYå=Û˜œFŠGûŽÎsϤtþ`+£S' ¡/Ø7;=í¼¬ôÔÁJª_ ó`¹g§S'; ïËÊòO)¢FPzçêšFJ™œåÆTÆMJŠÿˆËfŠéÄù+ß4p   G#РÊ Ó¬{Âmô'<ÿh‚í:ýéy¨Wõ¦Ae×zRl¶'ã¡lcé£å.×G»ÓÏY GxXwï ¿9tm8Ù0ÕW2z.Éúuùš‘ýJJKºÞ$ãËul±ÃSCúúúúö¹¦‹ñt<ÓûAIÛ3€=5]Hç(Íõ¢pjˆ_#¼J=œ¬Ÿ)³äj§ëεš#z˜ÐÄzÿxÕWgáêC(Î*þz9ÇõXÊ<ívÊJNöÌJŸø1û³“ùÖ‰¥gÂñßÌI©×’¯á{…Ó! Äm±Î¸ý¿ún Qaø½¨-²¾”s¶Ó©‰“†÷ WýJïµ-3ý|Îe!nýx¨Ÿs‡ íþ(õè·)Û¨´ŒÛIùý‘dÚÆñá@@@ Pü‡Ó ®×ó¿‹Ž].§?®JŠ@¡¸û•.F úx÷;Æñ Gî§?}9Æ=¥»¯8s®eˆŒp” 2…5ãú}¯iÎ ÈâàmXÖE*z¦æ¹bÅÆ÷ x^–·!+ÕÞ±OñZ6’Üw¥ÆyÏŽóØ2¸¢µÿ‘jò³Ø¶ï^Û/Ð}Ö¤‡VE9K*4¸YÑ!–Ϧ¤ì)OGÊmÔ2ïÂç³Ü÷ï帨¯YÄëUY­Š“¿–´:’ŠTQQjG‘(ìÌawY“‡Ò{qu–{|åú÷+ÛÝî©©4·‰.­'Ó*†˜ïáç  5h”¡G§g-÷T£VŒh7‡ OÑŸ^¥,»H­‹þ„ÿs¿·ÛHÿ0£RÒÏ¡vÀf'>Ï4Œÿ£ën; ˦ î ÂnÔNˆ¢Þäláw;±×Ï V®ŸBržCà©Ç&äª_hØÇUÔhø\jŠE“Þ©±°ŒünINI¿z.Ó©õGáÞºh`ŸûFŽir㥺t’Ó2þ¾?ÒàÉ«Kލût—aåNit'¹&ÿwihÆc¬4‘ì/‹IÆ5Ô}zëôŒÔ­v9°ozÖ;aU¿ékNõZn*ý]4d'ÐIÊû4M¤ÑêYYÜ@®-9)´7M¥xèÕbå1F’ôžo/¾YâÐcc<÷Z+îLI“üzYhî|tF=›Å¾r’%$_Z¢«¯_0ާ»'n !c’ÿ/ÏEôì^Mï ]f…üǸ3š†u­Œw­M¾Rw²„ì",k°’Ž™RW9\ô ¥1„­”÷¿)¿мËx^‹×ÊS› |Âj–J¤÷E¾½ÆèÜ”ÍØÞ=7Qÿñ½ÎÇU•­È(É$SϸÙ,c*úEü™á@@ f nùðçÔOö|tZìà>BjÞqâtíˆïõòcIC(¤xÒátž0è“=·ÔUñð¦'Å­Ô~xÝ#ߥ?ãí!#<‚ÆwOižà¼®£#•õë4)ŸÍr§¬X¸rýÅ· M^=_wDAJE+‡£åMÞôhÂ))㜚ƒãýÅ;UˆÇu§¼F8£R?_¹ÞÛ(©)žò“é_ÑÑq^ZÇèa–ü‰‡{ÐØì±ÞI»¥cËãO³Ã«Ç/B §<áš4¤ŠC’”L ¥ý` Ñê|©£³÷¹d¯XB=ü}uáâçÏŠk—m'8ÖýD{šëñMšÿ¿Ú*¦’{Íëxhö¤ ?PÇE*¥Û‘­£S'_NõwW¬#n Í9éMÊÏ`jè§ØùÖz¯É*GBÅx*A!Þ‚-IDATéê Ûýr4Ye.MLt}è{½ª²%¥eü•X¬Ëzì¡e^+½à¼5O{߸8ê4ªòÁBÉ·ß6yøÔéŸì¹úôAc[¹¡‡ÔçK]»PúéQ"ºå ÷ö¥í“ßß¹¡º‚â_º\¦º¬ƒ£ÃÇÓ'N<@ü¿&=<去ÛÜÔÎ÷=ñàƒ¹n75¤ø‰¬2ÞI¬t™Âh˸ÆC4( MÈçxô×¥éòyZÁñT1Œ¼?²¢õí"ÚðÐ ‘I™¸ŠÃ’«6™î”•¼RNv‡ Üýü÷ÊUí÷_n(ÐÙñqzì{ÙÑ›ct‡ö:Ép¹­DýGM•ÀÚ{Oܵ~|ÿô ãžêŒÕš95ídMÈ¡ºt –NWß„Äþ‰´šÕ° ãû¿XßoyÌrOø8oͶ&_F÷m§ŽÚÄEUq蠟̢ç¦ýÁ]±­jÒ—Eòê¹€{æPP= .UÁ ³3¾ct1¸u>iÛ[çBö;àjã³%EªÑ:/Xá\†¿3E¸ì¡ï{”“æm±@æN¸˜¦I9XÏpTØågwõœw£+§¡gÓn¨n9žI€H€H€  Ý49æu!%#%¡ÀÌ׋½uþSþ4Û*:9>,¨^t©•€áÖ¬L½h¸ôYé ?¦JMÞØO">ÿDm¸ØRaÄOnǤn#ßÀ5* EjãçôhŠ-ƒ¢ùðqÿ/¦WtøÑqhw^"9Ý<½ÞZo¯y å({ú0îT—AîÕZ.Úø=ÒN¬õ&d:b’Å®üÁâ_:ò–.˃H`OC­âïaþ w}.R{–f ´2ŒŠ>·«ˆ«”žá€Á²6;è¢GLïô~ßíBNA‘ŽËÿ¦{_ßY°ê¨5î,оO&Gä&™Õ;»²Üöôèg¢S· Ï$@$@$@$@$°¿8¤Ö|$Ž€9z%sÊÊ<{f>:ØÛ»”{¼ƒŽ× »[…]*ìë1òUCòêÖclvËÜwßb3 ÊÑ‘¶ðq«ÄœÃ;Ǥ`Ûh,Kâ- ì·à±ÃCGͱw!œ¥ì W¦ |®þešr¾¡9¬ è‚r²àÕ.YßâónÇLï¨@ðaH€H€H€öKƾêÕƒ¤õ(*“‹•ã<}¨_#ú‘ kdhM¨zUQ©1†I“gm é逶ÒxIâUHh1÷m®_VPщ½-bJïGhó{[©ÎÊÊ÷÷ß_ú“¬#òý=Tð›¥x_nJžš¬üœ¿ò¼û¡Bk¡òµá‘ôY”8eKVîzûÃUçÞVÒò,@$@$@$@Í$°OŒI¥žóaD|€‰‹šÚŒØ¦c,wrÑ\ã…© º§7µ¾.ošŸ*-ô•ÌõûnñJï%×[Ø›oÝY­3Ç?nuY¡ofcËïÇå®7¥JÛûéÚ]ÀÑA©Þƒ•:.|ÏF2² mˆ¯8÷þiNH<‰wùˆ¦>~‡§†”xgE_ÿÍM­Ëò$@$@$@$Ðmîv5y®·ŸíØo “Méèe•¸¶jÛúçKKϺ&'çýàùL˜í· £ù øIºJޝèzŒ_ eõI¡œ¡Ìí)gêríÀì ‚ûŽ8ëR*à¼õF;O»{²îþnU¯R½Pâ/˜«åèC¯QXo/yצ£)E '«Ìﻲ6Wˆ¡—;ŽSŒû“¿Ú0Œ\ÌUc„Þ-"ÂmÚÎCHø!tÌÍè×ìÒ¼‡pF!²|þG„!Ÿƒ/¸­„Ùv¸PwQ[N¼&5@ y=’)Ô‹†Ç3½ÄÊýŸ[>§À?TÕÊ9iKiÞƒöþ1ÒšÞ¡ÊÞþ*ÕÓ!ÇìÇ.ÃO9Jt‡µsýùú9Ä‚ Ì…®yýxýíÊŸ¿KiÌ.õç=–Ü1YHq%ž¢#÷Ã#Æ”Z¾êòwOš^¾¥òß²½§¯*·gaÿ*¸ÉMAÖî<}ÖJwŽ/P‚KÏÀ3zf<ùq?¿sú»¾ù͑ź$@$@$@$ˆ@›Î|Ly<³sȱ_@gšgxÔ= ¦+¶È&%z¸Æ¤ßiMÆâvÙe×êòÐé;@ ¤Thü?˜Ï)=}?j†Ç;(ð©Gz\ÊXi—¿®•`%åÛ!%‡G·¹Áyçr(¼'9ž# x«0dz¸ùC­@ïPH½9sD¦·»!ÌI0<™Ížï?Ö¶¥¾ÐHƒË)Æ£­qC'e¹r ‚w!eÁìJ?w ÜÕ»ó¢¯T¡ÿÕ†Ç$=ò<í9¡`¨Ì-6<”ȃ‘01Å“Ò ÏÿWGiMî>}âÈí<™?‘RT*i oçmwNF§ŒIXм vÌÏ\ÿhõ¸>ßÍmÚøÒéú9ðì—x”³\ßWÛ;^†qrº4äá5úÁùȱÕÛ#¬¢ž:ß …€‹¤Ëƒ('¾öˆ”~f¦9Gç¹GÄð€œŽ¼¿‰7<Üre…yËô5ÚØmѹ™ÁyE¿Â,°h–áábÀw„×ÔylEßûÏpÓx&   –$Ц3ÁªÊPJiÉ€’zwÑ<ϳywØÿh¬\%¶õjù#Ž­¶ƒwB+]›ê9ìI·>”¹¤iü¤ôþüWÜ4ŒÜ߃¾…þ;ëÒVÝé+\]­Ä‡_ “á1U) ‡Y>s¬üÂeBê6¨½,³r*‡\Qá³Tz–âÓ¨™’g³ Š¿Ê~uÂe°x4Œ‘eŸ¿®òsÙ¾À%X ?÷£rz¦v>옣FUוKp’Kàú•ëfffIᔺ÷˜ñðÁ8™¦g't8Xh/§Ê¶o…!¢g6¾ÃŒ‡2e¨\Ïöè20¢ÞplñÀÝÖÔçY£·:¸AHç1<Ã-w.B‘¿ ùcéˆOg|_ +(ºÀvBz¼ÆNBÜ•å+Ptt߆àãŸÏ-ôÝãÞÜ;eJ;}mڦŒÆÃ t& ·Œ·Ó-s(WŸçïVRÓZò™1ƒ’ƒòQe©³¥…o ´ 6›ù(žë= Êìàì{X”%)B¡ÉM‘kÛp ŠG¡Z!"ü‹hïé7õ#"CŠ­]þoDîk/NÓ3Ñi3ýŸ£þ` ô)ËÏßeøU;lp1¬¸¸Ê^cxżè:‘k©zIa,ŽÜã"³SêŠè{\ŸŽ¡èNp {Éý€á¤Ÿ[N>—Üð@O¥ˆ‘%ósðë eiã#ßÝ0‹qµÛfB^Dûx,×ÞîÖK¬ü0ßXîT^bYʀł5!æý*fd´{—¶bÂ@zM_†T¨7ú±yöļõ½{àEü êãÞë³iOEß»×!¼2‡^óÆC9änÐQ7l¹lZâŒ÷à‡Ÿ¼¸¹%dQFã è™? rÜØø^I=:ÔW˜•=iÒa^ïÙc  – Ðf3Ž j‰×' ô”y06;øïúòãÓ¼žN}f[#Êãӣ¬ØÐ£J³5Qej/¥ª†~æ(M1;)”â?Øq+—Þ„Ÿ•X>ÌdÔwH/ýÊèœóºw¯Y¸u5–œÔ¥J‘Š«¥aèu á :&ô~ »CŠõ»ï¼Ši/ºdµ°Sô½Æ XËñITÞL¯27DÝïy)a88òÒMªxŒ y´™»t£S”æÀÍ ÉÈ ¶ÿRTºUW„‘a£ó50äܧ ËC»˜DR1ÌuáÌø¤8IoÃel*εN|™ƒüþó‹§u¬¨,ÿ¿ÖzL¸ÞÙ‘ÙÀDí@Y¾ N‡áA|õø)НÕmUzšœïîU“ã{à$GT?$=é·•Y÷~/ 2æÁÕï5=ã^›¤] krCÊ/±¤ç‰ÒÂñ ÷$Éöùÿc÷ƒR~“!âû±¯ïá†ÙÇ¢Ïìë¾´Vû{.»µúC¹$@$@mO Mf>-ºØƒõ?mÍÇ ŠPXj­6 +¿‡þG˯]/"NsLuBt1¼‚rj“óÎ¥ŽR·Àƒþ‘èòq×káRuQtÚÂVÚ[ä;"§åž÷¯¿Fàªõ÷èz-q=Û ÃA|‡þwŽnK_Ï*¿ÒmùðV1FÄ×1q l¢ka^¼`Á]§‹Ñ”ÑôMAŒ +Ù{©¼¥e(éGÑ=Û*ÒDäÀbük7´HBCcdFºü9 ±~p»·¡¢k^EåÎk`¬iã´U¼{ç|Òßß#©pCœŒïáX„¯ž¯ ù<Ömûp öªY•“_=«‹gÌZ¼ g{×£ú# &†çøÌÎð¬d] Câk!çp½¿‡~Žcå+º&Q_`ƾcvI¢üúÒs,ÿÙØÐ³¨¾¼ƒ= 3›T¸ìƒýûàó‘ À¡F F‰l­‡oíÒ3¡D´o-ùµrUŒ"ßâmyÌ'DОå äfzÒK+DUç;hÁ…è3Ñí¨?éöôl Ö.üÞ ©» ÈŸ•’!¡Œ×@azÊöß  ƒbþèÆÐ×½”c¢4ô¾ÚÃ+¼Ól¼2'AË,•ÛPýMùLD&šS˜÷¶[®¥ÎP1r¬î‚RöÓsø3¶Ø•¦œêsRBê½^#ÜŽ’ºÝ«1£±P:-ÝL_X¡*ô(ùu†¯G ³ÀˆôŸ1S‹c±»‰ã¥§¿ѰåÙ“òSÍŒ/ªíw@aí µóµ¼ÆzT=« èfé8o`ɲÖàј~ì«2X¬¡B¨±Ö<ÛÑ¿©Ç“·!×—æGÊáÝðm &b ѣíâåxOþƒ÷.tò5'Z^PØ¿Àoåý2+/bxÂh_2·0ÿwn9¼Go*Ò3áß™›îžK¾§ÝëFŸƒâ DmÓ®‘‡â¡Ãe‡Ňç3“ ì[mb|„BÎ)­ý˜Pæ[µ ­Á=äLlL«°+|xí¦´Ѱ®˜““tŸ/Åcþ®&Zû³òò¶¸éñg(LoA!Ó®h“6Øë„‚^Y…{pþ¾[V‡÷Í™¸R8jVµ’cå×;l¨›5!)'¢L‹W;eáGŸ™XW2-ho-”ãêËá¹í…iʇUHM‡+Õ×àñ<Ãçè…æx–÷1“süÎèùfÄGLÏ#jØ­ Ÿå>“ŽJ…P»×UÛÛ…±µ´ÚÙá«ÉCš?/±ò¸¨¹µcÏs óÞ„1x_ÈqæcOºn>±¥Î;(ö­ú¾‡©É½kC¥x‡t°‚+lÛÖïê­¦ÇxÑâîͶ&Wfÿ2ò­8â0¤ŽÜ×{!ÿŒß·²Pï£,ÅG¦0=¦Ùk\+lgÞásaÜ`æN¾8°ÏIé÷/»À¿{ð{«ÆÌÙ@œ·!,t_-àVqdk£RÞiÁ‚ÍÁƒŸ‰YÝnIa7G⣤0ÿ1äêÏmíÚqÎo¬ieU=môè]ñ…fYyk¶GõÉ…Â3JZɈû¦=k☯ëÖA<-nOÃýÉzfJ•lw¤è»9z=Ê\ÁUÑå]×W®. m¤¯uájµrÐ.e*%To@i7,䟢óu_Üöð,ýôõ\7gì¯ñ"NùnVÝâþŸe—–zÅÆ‡Í[ðà˜1õÕ«/½n‘bWv¢3¾»;åéRµ{ެ•ž@{ý»Õï2ŒÓð»ü?Ý;Ù ÷+¥Ò Ù1ÆXÕùþþÈ?6#M6CkeÐV.û ý70Â$Ž•®ëøÛW»O—Ý>åòDá²Ýò<“ ÚÄøÀèyx%êÁL7ÌlKùÕ–k震¿‡ÝC"ØÆ—©ªÅaÛ(ãý /Ì×ýк÷9þ´vÐ×My]Þ=t¸lÌJ¹á²_Ø \oæM!¥¯ —ý7ôw H¼¦ëD‡ËÆŒ–+F|«Ë# ¸HÄ*.öÂeŸX2~|› ªD÷×$@$@û†þ¿hƒÃ‘Ť­ØZ[´ÑŠÝ§hhÕú¿)ÍûM)q6žHGl ³¾¯ -¿i {ðz{ÉÅ0,Ò3;¦j×¼˜ Îç•ù}·Ë㎺ k 6|WÌ‹)Ј3äÑ®{‘21>‘øˆ5 W¢™QŸ[R¤gZtÍD! jÓ²¤ˆ¯¾Y ›j´¡Œ¥¦ôL2=bb´ÜF^W&*×”gØC†.;T|ŒŒp¸l„L~Á‘ê:½Œ²KQçu]ÿi„מÕÍÚFDՆˎ„—ˆÞƒ“ñqÃeGdð‚H€Hàà'Ð&3'¶ïõþgÛ>Ñÿif´R©£0µîìJ«õ‚I ‰ð¦ã}·5±ZÓŠÝÆ^Ã,?;(nAÞ˜¹PXŸÄ¦œÃ¡ßv„òùBÝ:žzÛÐny¸0 ^ɱŠþPjå}RoÁ½IÄÖ¤¨æu«j—(D•« 5í{ØMo©óúàbªŠ×ìU·.Ld~Þ’³û 0úê —}yîs„Sî†ËÆ~E‹6„–ֆ˗ííö–æ—íÃá²c¢”…Ãe‹Æ¬:\¶WmCHæt¸ì¹߃-ÅšrH€H€öomb| ¼²¦¨Ôxn7µe>‹MûZM<“ÀþD %#ý¥šÊ]s  Fè–쌄OûGî#†Pʈ¢¢#T…“‘ò Õ#Ê•º Ãå3±qà«Ñ22ÌŒg+ìòP€»!ýÖè¼ú®u€¸A½¬lÏ*.ª¯ÌÞ¤Á—êm?:CÝz*=àµ(IBMïMc¨“éÍø´Ü®¨ Ù¢'n×dŸ#•= ìZôhÔ30\v‹2§0  ¦h·+Ý'ӘѴ®5¾4*–çå—5¾K’ÀM ×â{¿…rÿÇV{ ©f6Z¶R?ª©}k ±æ?B4K¸Ô˜W`Á÷oâeèÞP_Aº3°O/É-éáUr4&5ÏÁº„›é13Í?a­ÄEyðKÌx|¥;¡CMcÔ&–·ëPÓ[Tp×7ØG¦$d˜‡'íd’:µÆ„•¢=lÄh¿ãñ·ëË$U›”ݘgÐá²!t`]¸ìRÝ€îN:\vÇõéõ¦N«=t¸lÑ ÆâknŠŽJ•êíp¬µ`8\vpÇ&DË]ê.Û×äpÙp‰sÃeí¶Á3 ÀÁK A?è–~ì¢2ù2þ#ûIK˦90ÿ×v£™o›I`Xs~ññ55ö'˜QLmÉ.`ÖcÕigt>C–íÞ¿¦%åïO²t¤©ö†aÇ/hOjº¹}×áeG9#/ï›æÊJV?Ù3¸á²£Cw'“Ÿï†Ë.‹ —_Ž÷$@$@$àhSãã·¿K?¶:Xõ!\ :¹hþY>’Ÿãd5_Nr zѨe Ö!:[õÀ&|Ž#ËõfmÍmH/Ý ÊÒʬœzªê=K¢Ãºí-X°À|ï‹/ÒòÉwË&’áæ7ç|ï”)íúöèQ•lýÝlÛ<‰ž3ºɘè²#§OO½à˜cìdíFËÝ×+ûùGc/ŠHøÖf÷A ÛÆ§.ó½ÛlY@$@$@$@qÚÔøÐm—y:"ô Ü(ôáÍ:ರ´‹·ÇÀ!CþW•L5N€ë@Gl†7:º,\ –†œl8Þ5ÿø"6£;%:ß½Îñ=p’£jV¥z½ÇEïq]œ †2§cï)Ñ£§hï/ÃÌS˜û¾+Ã=·ŠO CÏ šÌ«Ø•9«rdQÑQÕ•¡gP. ŸŽpcð—æ?‘¬ž›}Ö¾ìªÜþ­vWAz%X}çñ˜#g[µ~üXÈ{©r´½üFœãE¸ lÖ†7“ª÷]P¯ .4UÒðÜ]Z8þÃhùú:‘Œørú~hA`0b§>âõÊ g[ùÿŽ.S_^ö„ÀUXhìG¿a4©t%ä'Ý<n¯o”ßá$ÈÓ ›µµRÛù×u{ˆ„›VP|ƒN™žA 1 Æ?Cýþn¡*µÛ\ ÇNôO»éûãY¤¯èçïà--Ñ?¸r圶lBYKÈ¢    ˆ'Ðvk>êZÎͶB©þ)në‰ï`¢{(¥‹33: jŒá¡ex¤w>f\nÔÊš+s¤5¹;®Oébô|ÃMKtV²ævä-¬ ÷Tò¤ªÜ¾³¦0QÝèô¬‚¢‹lÛÖ»//‰N××ÕÎøœ¿Þ-<Ó{.új µ¦tÖy ÕÓù{:UbûED‘9Ÿó ÀÿÙ¶°á¥g5Ùfž©W!¤éÄâ‡$K´ŒÎÞnßé±Ð‡3ñéÝ‹_wA¼ü†dÄ—Í.ðåÜŒïìãÆæy ù™éM½\÷O÷¡>Ûm .ýY|ýœ|ÿ…Øëà‚nžóû`wD’A¹îÛ!ÑåBÊþ%¾õ'ELbÊï³ðý,ŽNÛ_¯u¸ÓÞéçÁùñæôÏëàw9‚†Gs(². @2mn|èåe…^5¥·/ž=FÓ“u lŠÖG¨3/uËÖÉÊ»ùsüãV£½-9¾À7­Ú¶o„âúR2W*í¦ƒÝoH•j ˜_ºõ#g)§Ã¦¹zhAñ#i .LÓ»Êð¤ D°üÕñEbê§§‹°ŽÀ£Äk*Ts.×P½x9ú^ü—øóþìæa4,ÈwNÕ÷ûø?}aŒ|Yêû™¾/¹?ÿ Ìœ¦7)³¬!Us s—ët}`Ql¢éMÐbކdÄÄWxž*-ô]K¬IŽ=åép¤%Ö˜ÍQ¥«`íÑÍ ý{Ú1”xFÛn=ýÝáúgÒL[з<Ÿþ ïטñùÒMÛßÏrñ%vïe·Â‰o,f‹Æô¿‹/±@|`ïe¾Ù)Ï2$@$@$@$°·ö‰ñ¡;;>»få‰n8 £­Ù0(Ö4âl(… ”áýA^¶3.'çý`#êÄÁ&XóYeðîDç&(áI#mtŠ.…þï™þ‚ÏÑ×M:Læn­^Iãî*©Sv£³c®µB­?2jFÐ32P8?ÿëH)¿t„:Vß'ª)›ì"(®@×ŰÃrW(©±ÊµTëªÄ.=$ôú‰ì‚I§#Äi¢ØÜƒÞ! Pì‘LFtéY…ãWâ»C¬Ý³Nn~CyÙÖƒG懦ïãc•™ú¼[o÷YvUʈ< bGþ†™é2øî.AÝõ.ˆb¢Ëê™0ôrˆ8öÈ"lmmê´é8í=ßÌ4™r²!$Üé’Ï,ÂðÞhH™wøÑGŸÒ{Yþ¢éYÙW   “@›ìó‘ÍàÁÏ„7WŠË¼PèC¡(ö"ÞŠ8"l ÌlȵP’ÞMQ¯Ý›½ó[!ö~½·ÇcÌCK` üf“˜tœcÛßïê9?¬'ê£NÇÚˆÛ¥0þÿPÖž† ÑmHÞ=;¯®2ÞŸ ¬ß±>м& ¸ov;èæÕº=÷À¨~–œçÞïíy˜è²ëe¦÷-ÃpT"1#äXSQ9¥ðÌÂ&Ïš£°‡ñ ô§7¾ƒ×J)3Xÿy @›اÆGôæfµ2QèwçÁ ;w'íåÕ+÷¿0Öm²ý:Â8jøs®»N"‘:òTupÇO1Ð ›‹  §Â(èˆÙÑñîZ°–FÕõÎpëçƒÁ(z?f[#ʱH=f><·WÒÙÖx){–D_Ž ÚÕ4=Æà’ñã·‡Kr+ÜÈÚÇ–Ví•©Âm•ø|ë‘w±ÎÇšŠ›k„|—± ñ“ÈÐu›{”æ?OéÈSÕ›·?V¹­òÜOŒ– ãh+¦r"ÏbŠ`hÓáç°¬GÓÖÛë.?¬C*Ü‘võ1É.ü33Ÿ–Ý?þŸøÈ¡¾€T°4Ã3R8í®}`\ó—1Ú {®îS§áˆÇƒH€H€H€Ú’À>s»jˇŒm˘«`ÍAÐ,“º\íí7aÄxÖ{\†€«R¤ˆåë5WÇÊbVÀ÷üî§íš©ÐWcf1âËÖw:t´)7.W'à o€æ¦5å¬CàÚÁš—MaŽ-±òWDê*Ïÿ°€ûx÷^»Šav¡‡L5´Ñstñæë…úݴ“Ñ1õöâfƨQÕ˜yúü¶~_¾j_`4ÿ7‹ùGÃÌ6Ú±§Œ\.8!G]÷!.^;ô2²”ræa¤É³Xn_x&   ˆ%pÈ¥@±Ôö€b½$ÇžwŽ·Ãm§T¯÷p?†4Á*Û÷,­W9ãäg@éMºø<¾> €g±°åW:]¯w€œA†'õåørÑ÷z“¯_áÈl«4#:ýî©SÓ+튗 óÁè…çºL]ØÜŽz]‡¾ßä®…ÛÕŠ9¹¹Û´‘Þ8LgàØhëïUŘÍ:pÖ„À•:½!ÑåtÙ½9¢0ËZäQ"t3Œ°-ßæ³X0ó ½N¥¶ð3Ä‹úu~ cPÏž„†˜ Äò DÕjï~°æ£ 3O¿B¤°an}žI€H€H€H€šG`¿q»jÞc4¾öì€ï+¸Om…b¿|Mü¤:nY]iÊ1^Çòì£J óþá¦ésJçÌ—á4ë®@àè]»¢sj£LÁUi˜£Ô›˜1iÒ‘â9lRµ½c>ú÷O¬ê„èNÖlk즆„ÈuÛNÅLÎØ²‰Ù3±Á^¤hå¶]·Býî·¤žx¦‡"ãª2+ÿ_ˆnt›pìÈÛ W²4é1à ñƒNÍñÕ7SЇuè~&øÏMÃü©f54ßfgŠ «GX^"Nl¹Hã¿J1nh ½‚KÛ i,ódêÀB£ä—æ~ïnƒ½ú#<‹^ôIêQæéŽ »ü쮞ónt[MÊÄ-È3 *ÆNžÜ>YÔ,—f=0ɽoêYoF_G·­gô^Ñy:}Xqq§è4}/#Q¹øzÉîõZEEzGÌQŸ|=[?ûS‰7$@$@$@$@$@Í#3Ám¶5鏿Iam                                                                                                                                                   6 ðÿþXþƒRBiIEND®B`‚neutron-12.1.1/doc/source/admin/figures/scenario-classic-mt-networks.png0000664000175000017500000007651713553660046026361 0ustar zuulzuul00000000000000‰PNG  IHDR’7±¡¦5zTXtmxGraphModelíYmoÛ8 þ5v6ÄVë¶Û¬½; +¸—J¬ØºÊRN‘›ä~ýH›ò{ÒuÉÒîÖHl’ÖËóP)Ø$[ÿjù"ýdb¡Fá8^ØÇQÁÅ ü¡dSJ¢è´$VÆdT îå‚„c’æ2Ë–¡3F9¹h gFk1s-·Ö¬Úfs£Ú½.xâ{¬÷3®úÒ?eìÒRzFµü7!“Ô÷D¥fÊg‰5¹¦þF!›ŸRqßV1Qv Zc ¼ÊÖ¡HQ‰ÆÍm5H+4 d÷ Â¹Êiž£5J ò; qF Úºš‚ Jðª|fé6›bv› @½J¥÷ >Cí ¼d©Ë©Ÿ õÙ,¥“FƒlC…ÎØÕ£°Nà·g°®d2h~IŠ©qÎd X(.õû LÔ¬úxDø´ðÎYÞ+L&œÝ€ i/ÈÉoï«Ú F²´ág$ãäxIÕrÍ\%Ãô°=¿ãìçˆm8†qw© Æð€ê&„wXêJd-˜ïk0¦õáqõ6p½ Ñ„•‘lX)5`½neìÛ˗QáäiŸgÞæÐ>Oõù>]¼nŸO;¸†çGóù³°‚ª[)™Ã†ÐÂ/ú7Çí©P¼_õ%œ/ÖµÎ7ò.×:ϦðûÅ7#-Ûl÷sXÂ+„>e,¢ü”ùðß@BĔЭ±.5‰Ñ\]×Ò««q±–î¯Æõßhòáï`Çß  QäïI]é? +a‡Š–ÿÎm(Mã¹3 ªtk0€•]éøÓ/¸ÕFãAr#qÒE;[9ZšÜcšE ·‰ +Ú=‘¯%ûä4,kXD$^DŠ«–-V•õƒ*]}y{€*=@•^W•ž›ã•Ó@R„÷HB!ÿi2’"".ª4;É<üN•6#C™¨–Å©ÒÛHÖAÅ&ղꪼ7D$Øðÿ)†¼ ŸÏ‹t¾$"ÿÒîJúY4%àÊl½ê@rœüu ©¾„¯Í$¯1rH–‘7 ¾lH{UD~åÔÌ.«×‘:«[\”ã—±r{¼HB!$™’%EäŒ!íª¤¿ïœ!Ï@Uš1ªÖÕF²*œTË:‹2–zÉu´¤]â}C•/õ>Á:ª´ØLöñ5Õru@¥$Êä{ê@Ríyyþ6jrû°Í7"â'é–Q«$Õ—·ˆR‡Uy=Tys{¼HB!$™’"J’š®¾Ôyΰ|œdÜdf ÃwÚHÖAE=Õ²™¢<ù QÄIý¼-ÊSÆ©ùR_pÝB•–É>–T-¿®JÏøÔvF2’÷UÿžIþ¬ÈM 99úo»TiZY©ZÖVÒ‚qˆ2KœJnIB!„¤#«@²˜¤Z2’}Ui#Dyˆ"+Si#YÅDäŠaYŒ¤Íˆ~'Ê=‹©ß[®ú꽌ê´øLö±¶jù.UzNɵ¢¼HØUÉä;™‘Ó@ò=I{`鲈ü,i¨ÌQ••1T¯gºˆDþJÒžxÉýñR’4Z ZÆ@’B)"dHФŸ¹ÊH~®J[˜á{åD™-ì"iO~‹¤*6Ê¿I Ÿ¨ò½bXv>Ã2õ«lЉr¯_êwšd(w¼j™Ÿ*='dCCÚVUšo&ßÉŒœ’]Œ¬C=;œ1T_ÞN”´‡¡¦fÈ—Ûã¥W¥«6*&Êû7ŸHfVç„B)Ä $EÒ.1g $_‘³†´G¢ÜÏWLD*Jú'…'¨ÊRŸJúY2‘^ÖuJµl}†eð™ªZvBDš‰òôs/IÿÊœOTßÉI ™úIupý·dþâɇ¢£YY]”{DSóÇÖ]NÒ_ªÎ,Iy;Õ2É—›ãå§Jß#Ê».ŠÈ¼ ëS’Ï«sB!„bžHÖ’ôÅ9Õ2I@\•´×Ö¤^6­¬Ê_3CþK’~ÆòÍ ËWª–Ê°ì› ÛYA”We ¢Ô.ËðÜ’"Ê}…©éÁ™|/#ê@òyÖ%˜ü+‹åêôß²±®Ô'Û3’›ãõ‹‘¼÷TÿW’53äËXç„B)Äcç\Æóù;ÃzŠø©ïÌÉöó<'„SS”›Ó?Îþ£¤ýwåW|*ÕEéÌ_—ô£ˆñ@Rp+3«@ÒA”™TJˆHQî»ÊX¾õ#¢\V»+ÊåøÔÆØöø‹ˆ—jYKÕ±1¶M„&²sžkïêó½„(÷N{‹rÏbu¹ "±¢\V1~Îe<Ÿ¿‘=¢\J^c°x.¶Ÿç9!„˜˜b"â#Êåé΢–Eé\Çò¤ÞÏØNDªˆr (L”NôyäyKþ=0+3«@²†(7ò·7|gŠ(Ä‹™”¯&ã#"2R”†C¢ ƶçsÃñøÒp|vò–Î6R˜ÈÎyb¬½«Ïw‘´Y¼î¢œ—gD䑤]6vÎeH¦>lSK”{›çbûyžBˆxQD\E™1€(—¤Ü$írw1é&ÊMîå’‘ú©í¬É’¢ÜhŸ"Ê=SêÁX™ÆžÚþID’ ßÙ-Ê%)‘œ’e ùKÚÓœYmúiλ¢¼§î”¤=Í™Õ6R˜ÈÎy"’u{WŸï¯‰˜ADê–¯‘ã’|;猒""SEdŸ¤äxžB!„B!„B!„B!„B!„BHQ'Ig[*n³MŸ‘½Ã#{&'„Û<‹³-\&„wy’Ù39qK¯]ñáû%él+±]QJ©åM×?‡Z¾&Ä$$élK%FvšÙ-åâîQïž^†§ÉøßÝÀ½}´ùÏíXMÇÅ]#S#º'%Fv¶b»¢”Ò‚¡%úgBLFbdg«ÄÈ®wo%Ìîí¡ÿqoÆy?KŒèqÝÜÛ¥”æÌüꟉqì´U´ÛÚœ©ën¯ÕÙ;jt®©ÚkuöŽZ}ÇžÛZÔêëZz[-N’ζTBD÷S·â}Ÿáî.ТáÍ8¯g'·ô:f®Ë(lW”Rš;ÍÝ?“3P«¯ë¤žgGÊ¢°“Ï6ﻊ#ç#áÊßÙöÈùÇØrøü·&=›¾úÏGƒ¼õOÏŠIvòŠÖ8úè›Xz?ÍJ|DǪ‰‘]ÿuu3p'–1ÿºº‰‘ÝRLý´ Û¥”æMsõÏD ¼¢5N^ÑO|‚âRbâî þò_&óèÅ'Øvä&foHøÇuVìãÁ³b’µú¾ƒ´ºÿ^]Ƈvîwyÿè¸Z4½¸{ÄÃøÐÎýØ®(¥´`iŽþ¹¨ã ÝÖÖÅ'æ†OPܣ݉)8~é/³úç…'ˆÍwcâïbVpÜ?ÎZýS'¯hÍb†21²GòÓËAÀm-¢þ•¼‰Ýn±]QJiÁÒýsQeWLÀè…{í?óG.<µ¨Ñqw1mõŸ)ÎÞ1÷4ºLgœfè­ì=õí4Ñ..³b—»ÎŠÝ;döŽ#®³bÏ ž“ì:+öœë¬ØCgïŒ2kG £Fçê¨ÕwÌ÷§ÈÂlžýïVp{-¢þs# á]ž°]QJiÁÒýsQÄÅ[?ìâ}÷~ˆ?Ï?-0Fºasvp¦®»Ùg=ãìÛ[i7>ÌlW”RZð4uÿ\Ô°×èº ½ãÑž“p(éIsÿ™GX´ùÔ3G/ݳÉ+Ý[¹õôÇïå©Lýñ{ð‹<ólüÒ´ºg®>±‹Í6Sf ÜŠ¢E\³’`¿(¥´°Ë@2÷Øi#ª:yE?Þrøž}R$>vK"Î Ð_ŽćÔLzÆ=ä«_š«ŠRü ´ˆkž@2{ë¾h t}ºáÈ$wÜ Ý„Çû÷RûpÏ.ÜŠáq£¡ÿ½'îZbÒö“|~9fF8aÅ/Ğ݆cÉG¨‰ýóÊAìJÒaÙ®™ÐFº ùüò\××Å“!1'ó6Æ!6þ.®üMMì±KO±3ñ>f…Û|=.ž )0ýsaÀ^£ë2ÈKÿ`QØÄÄ? fvÛÑ{pö‰yìè£o’ãÊŠ³®™Ô¿Ïà”ß8ìpìkñ™˜ÿ¢ÿ?»4× ON­4I™%Ìf[Ñõé†s~ ñxÏ.š^Zí]Ÿ®øû\€IÚÎã䵘á„Ðã8råÍ·$lÀÌ'1í´UsTqÊ€¿Æ$rvɳKy¨;ó’Ï_oD§öx¸-ŠZÀˆNíMrÞ{„ØaßÅÔz„Øå¸¾4:‹ÿe¢ªƒFW ú炈6¢ê ŸØ‡›ÞAÔ‘ûÔB:xéžåø5@Ê€¿Ú$rvÉÒ³K¹¯;ó’Ï_oD§öxnRÝß*…Öãç ‡¶å`Åú–îe_×@…Òõàãjòmÿ—+0¯é{>“ÉËVɼŸ÷!vØsQoRû¿S Í–„Ï?Wú“å°ü¤@Lìl…råbøîí&ßö4·bý¬hjUÅåTjð-†GE`· ס’9«/»O>2©X•E³¹Wç;¸Wü“öç°ü„S˜Ð¡.Ê•máºû&ßö´õœÁL»¶¨VF ò"^®ßÃCob— ס’–ïŸ "ŽZ}_w¿#ÿ¼jƒwßÌÃ=’WW™DÎ.Yxv)ug–@2›mæAx¨IûV)´v_c<_àX%\›Ãòƒ&¡ÃËï`ºß“ow:Â?c(¦´{¥å ›lòuäµÍ¤êb‡]¶›T»wJáÃÅ!ÆóÅMÂÇ•aÒÑ–Ÿ0m«¼‡A;¢L¾Ýjcw:¡~™×ÐÎkB®„ç€wPº†5–œ4Ý:ªø)&îËaù‡7£í«á¼õ®Y_ʹ¨#*¾f ·Mç¹s&v­—ú 8ÞtëpÐè Dÿ\qÐêÝ}‚O üð=j!ç…žÁ`ß9®2Ëu© Ÿ÷Ê6pFÐYCÚw4-ûœc#Mvœ”@2gõå Ñ!&áIýͪ,šÎ¾‚m‡¡VƒQãð-Þ*_ Å+7‡ÍÂ$D=ˆM*(Çýõá±ã66úÂ'5+ ˜¼„*9`âöÛˆIx€­Á.°jä†QýZ¢JõŸðc“—•ï½úFl»ˆ…nÖx·ò‹)Žòµm0$ô†²ñ7°rZ?Ô{­$äÅ×Q§ã\øNALÂÝ,וf ¢¢Â0}õYèîaë¾xh« Ÿ-ÃÆ8Ó'%´|ÿ\qÐêݽƒN ìÐ=j!‡ÍÙ™’«¿»­ øþ&‘³K–ž]Ê}Ý™'Ìf›ÙlRǾY ­ÝVâ–o'Ôz¡ZuŸŒ³k#¼«ÊÖ°ÅÑà`<¶åëcÅÊ`Ü[:_•}†ùâü _,ÿö ”}¿Žã¦WX½Pï·±Ã&ïŸ0*×ÁüeA¸3ß ˾1æáür¬ü¶ ^ýÌ×6ãÖ¬®¨W¾6FOZ€³sÝ0ô2¨ßknYW¦û³n úTyÃ|ךü8åµÍ¤êb‡Øó‘&õ÷Z¥ÐtQ¶GvÅ[%*âWOlŒ_ŸAï t;¬>‰Øãø¨RCL8 ý¾aøøåªøiöB„^ˆñݪ¢tãþ8‰máÖ¨V¢jt°‡&b¶ÆMů×èÐéíðn…:ømÕR„\ˆ‰Ý^C¥Ÿ¦!ê|$¶oé…Z•ê¢ßlÔM@ÏePkøbl5².õ>èãW#ðÀ&èÏG"ö|"ֵǕ?ÃÌc¦;NJ ™³úrÐèLþþ¹~VeÑtÖl †7K¼‚· xß)x l„ÒïOÆÊ#½gšUl…ñ»`»n>ªd…=b㎃pïl…Ò §cÕ‘ˆZï‚j%*¢úOÌ :…Ƚ¡hóZsŒÐßÇÖðÉx§üGè·ø6Æ€Gçê¨ôýf„Ç=À–£aU±9ú,M@ph zÔ+Z.‡id]™íKˆ÷g(.)݃Ãn™ô8)¤åû炈£VßqÌ¢ý)¡ï‚æ¿ Ó0È;&1W•f $/7‰œ]²ðìRêÎ,d6ÛLJp IûfI´¹7½~Å;å?Æú•JúMmG¼_ígìXˆ”•#Ѷ\=øû¯EBÿwQ©‘+Î)ùî/sE›rïcÎâu¸¡išå>ÄÿuJù«ÜÑ¡rmÌó[‡ûk— ná2Ü ÄÝÕs±©c5”m4—ƒWcGû7P­­®"%x’½‡`ÞØ98fd]™îOÀ$ØV©†¡^«M~œòÚfRõ±ƒþ\˜Iý­V)4]¸[#ºà­—[`ú1%}k¸5ª[ýŠE' ?6Í*5Àø?C±nòû(ßj$6%)ù¢‡•ê`øÞÍØÖ Õ*~ŒÉnVÊ›Œ6¯×ÅÈý›}b5Öî^‹­ç ‹[ Í€7Qº•;ÂÎmÀ"»ªxµÓ4Dœ ƒþÜf„GÆÈå~Xmd]™îÏÙõðŸõ j—­€–Sc« “Gˆ]ŽëËA£3ùkCúÖ,ƒ&>—4oVúSv+鮨^Ãó¤`Ûîuø°b+¸ï¸‹€±¢|óå:ªäÛã¦?Âm÷¾nªVøvÜWÊ߂֯5ÇpÝ=l=„U[."òX ¶ìÃŒ~ï¢tóµØtìæÛZáÕ_Âz,ÛŽÝGH𠟇FÖ•ÕþlÙ{“z¾Rï»Ãï鎓ƒFW úç‚\ó÷Ÿ IDATˆ­Ÿ®”³·þÁº]×ràn¾Û³F4Ò\0ž/:+´Äˆíù´]ù´¾à=·àìóØ^Ý"W•§ øKM"g—,<»”‡º3O ™½6“¸Æ¤Ž­V­‡ûᦦ#jWû±«•ô›šŽ¨Sí'ìX½)ˇ£m¹ºð_º»:¼‘’¨X¶,*•-‹JeK£dÉZ˜è³7fþŒw«þˆC)þcСòû˜·()«gauûføðͪ¨ÿ~cüÒ¨"Ê4†ËK±é“Ѝ×à ·Óm›ñueº?+'ÀöÕjªYaòã”×6“ªGˆtI¡&µ_­Rh²p ¢"lPê$*éQ6¨Q«£òùèX4«ÔãaÑ€×!Rå*–CùŠåP¾bi¼Xæ]ü±m"7ÿŠ·j¶Ç|Cºã“Ðæõº±/ºK0¡sÔyçMÔjò!>oU %[EhÒ:Ìü¾¬†ÏÇ–tÛf|]÷cëÁ©øí‹WQîýÖ¸v¶šø8)dÎêËA£3ù“ž¶5Ë ±÷%lŠê51gŸ’¾9p(jÔtR>ï\‹¦[aLÌuÌë["¥Q®B%ƒåðb鯸}ãM„®„·ª;`¶¡Œ¨]›ðy•æºí.¢öÇa\ßv¨mõ.¬}…Ï>©‚’Í× øÈLýª j:BXºm3¾®´|w¼¾×iH‹ˆô»¥?RÖkª'b5ºÑ?Tœ¼¢5ã–|¸qßä·=j”A£™ççÛ®v÷æÓvåÓúÜí¿?È[??×f \Yb9»dáÙ¥<ÔYÉl¶™ûkV™Ô1ÕJ¢õÐE¸1£=êTû±+”ôtŸý†â‹ru°|±?Ž÷±ÂŸŒÀåÔ2V-Àiž¸¸z®Oû ïUM+ãþÒQèðòû˜7ßñvï¡bÍÎØ²Ð÷׬Â׺x¹Á\Z³ÑßUÁ›mÝqÕPfò´>ñûì7²®L÷gù8ؾZCg,3ùqÊk›IÕ#ÄÛ“6šÔ¾µJ¡ÉÂÕˆŒèŒµ:`~¢’žîóQ7|X©>ÜaÕøwðJ»qM-ãôJ, ƒM§7"bsG¼m•VÆöãhýz ß„€)uP®~7ø Âö¤Ø8·Ê·rCHÒ:ÌíýªØLA˜¡ÌÍ¡ ×d/øY—z¶Ÿ®u* öonX{Ê´Ç'U»×—ƒF‡ˆÃ÷LjïšeÐØë"BÖAõšN˜½WIO÷9v šVl…ÑÑ·°lxcTþ:A©e8‡…« ðà=l „·j¤•±c>«Ò®[oÂÌÇ(Wg44Ûo"âð=Îüå?ÀúÃWáÓ¥:^í †2ƒW{¢»ÛN,0²®´}¸ßž5ñjû´ï‡nœ€še?ÅhÝ]“'®@ôÏeV2æ¢çÚøgÁ{ï ?í^£ Î<µ«\Q£îp¸üþ5ª–+†â/„_¼Obý޽èÝÈpŸo•vq ~³FàÃêʽ·¯4ˆá¯!xï¬õwFõú£àÔ³^©1S–dQæÞ;Þ{#órþµ¾[fÙï‰Ëÿükþ`ž*Nð›DÎ.Yxv)ugž@2{mæþ*“:¦jI´vÓ~Aªí»LIO÷yÑ`|Q¦|g-ÃmŸ¾ø´Bu < gçMÃÚŸ­ð²UWò÷Çõ)?à=U÷—Œ@‡—ßü9~8Ö»*Öî~ËqsÎ ·^¨=gWúãÚ¤P«ìû;Ö—æŽÅØwË¡Q×é¸fd]™îßX%œægòã”×6“ªGˆ¶ž 2©}j•B“+Þ 5jý‚y JzºÏGFáà ïbð®@DÆöG£Ê5ñë<_¬?à‹‰ýßAù½±ôdÂB;àm«´2¶‡Ö¯×Á°½k±ÂÝ åšÙÁ/n="öŒCÆ%P¼™ ÖŸ ÂæMðfźè»f6¾MÊáa¾Øld]iû°>Q¾¶ ´ÛçÂ?Ú`Ì"l:eºã¤’9«/›Þ5©½j”Á𠨰fª×p„Ïn%=Ýg}š”o §°[Øê…†/7À/žû°jë>¸Û6Aù:°`ï]¯rÆ[ª26ÇnħUšcpÔu,ÚeÏÄü·±1r#º6x Å/ªw¼ÂÕ*|‚^‹°vK$z5|µ÷#ÈȺÔû¸°'*—k…>‹â‹Aß¾òŸ.‚ÿ~Ó'%´|ÿ\1üu›‹“Vy¸û‚öÜλÕ(ƒ†3ÎaÍJWT-ñ êÙ…aÉöxxüÖ¥Þ™€Ù;n#hëj|P¡%†FÝF`è<4®h…¯'íÁÒˆ=pmo…Rõ&cÎŽÛX³Ü ¯—¨ˆ7¿›wÿxø¯ÈºLcå¨×gŽ}öÙpìµ:ûï܇~¿†be¾ÀÐÈ«X¢qF£ªe”w66ì‹ÁAW±iÿ]®tÆ›ÕÓÊØ¤ßˆVUšcPÄmlÜ‚NM^à ÅËá•ÚÑwü0Ô+_¸ÇcþËðݵ^.)V 5¾×Â7æ6í¿žåºÒ¹7 S;à½×KAŠWÄ[ŸÆ„È›&=NJ iùþ¹ 3H««ä2+vÓàY±)ó6'aýîÛf·[õ2h8ýV¸¢jÅ0z‹’°ÜoVwÀLým¬ß¢vC"o`þð&(×Ì~»”|áKѰB3؇ÜDÀr'¼^þ[Œˆ¼¥”‘e™ÆËI[Ÿùö{QÄy ›»ë¡³WÌq‡z«\UX|˜5pyIäì’…g—òPwf $³Ùfî-[L-`^ÛLª!vˆ8³†Z@»×—ƒF‡ ûîP è Ñˆþ¹°`ï¹½³wL¢Ë¬Ø¯ “X¶í Öíºe»V/ƒÓ’°Ê0Þ¬niÑJú*ÿÁx«º¦Gߺ¨UhT¡%\#®`FOåÞÛ²å+,‡K€žÉXµÌ Uߘ®ŒÌË4^NÚú̳ϩ®Ùy^Á'ÿvÒêŸ:jc帢”¾Iäì’…g—òPwæ $³×fî-Y@-`^ÛLª!v;½ŠZ@%ÌY}9htX¿ç6µ€J iùþ¹°áè©oââ½#ÐÉ+æž‹Oìå‡yo8…eÛ“±fç-“hc$W,W‚¼i:%=ÝçH%°s ¿†Yƒ?ÀËm±4µŒ˜ÓÐøíÅ’Ø[X±Ô ÕÞ΢ŒtŸ—“¶>ÓìãóôÛz#ì}8ÈK¿ÍÖOW*Û¤ øsM"g—,=»”ûº3O ™½6swá\jóÚfRõ±ÃæS+¨Tɜ՗ƒF‡µ»nQ è Ñer»Í®u?þv“C1dVìSßg°ã¦É´©^õ§ž…ÿ²Áx«º=¦nWÒÓ}ŽX…†åšÀ.èVjP¯R}´›² BwcXÆ(÷þxh£oÂßÏÕÞ΢Œ Ÿ•£^Ÿ)÷Õ˜«boÀmñþGÎ^úÙ&ãìKsL"g—,<»”‡º3K ™Í6swÞljóÚfRõ±CÈ©eÔz„Øå¸¾4º|”hz4ºÑ?6fè­œ½ôñCçìz8kÓY¬Š½ir­«—Aý)g±l™rzò6%=Ýçí»Ñ£Y+ÝŽ›.Ãwª#ê¿¡Ü{[©¾-®¾¬|g‰#ª¾Eÿú|%ËrÒ¯ïºYö;3WÄÜÀ¸¥‡;{éwd«‚”¶Iäì’…g—òPwæ $³Ùff{S ˜×6“ªGˆ6ž\B- H款4:¬Œ¹I- HZ¾.L ôÜÖÒÉ[Ÿ2s]"VÆÜ ù¤¿þ:FÌßûÈÙ+zús+Ið}M"g—,=»”ûº3O ™½6sÇG›7½ÆaVí·1dÄ Uºg]~¯—BñâeP¯i'ÄÎÐdò}Sç3¶M3p¨÷§ø´J—âxõ­ 1·ÿ¹'Žtù¿9N|N>ÅäߣA™Ú˜–»ã–×6“ªGˆ‚O,¢P $sV_ü£oP ¨’–ïŸ Ž>ú&NÞ1)sÃÎÃ_ƒæ³‹·^“·þá@­¾®ÑŠŠ³.z›DÎ.Yxv)ug–@2›mæŽ×Ì\{Ô±;&¶¬ŽRò \‡OýÿôÛ“ïåÊàëNN8>vܬJáíÏp%Ã÷MïŽvJ–ÛtcL4{©ºöpʼnÉ#±îËj(ójèf¾«qƒoãúœ|^3qgÆŒyS ÅßGÀÔÜ»¼¶™T=Bìtb>µ€!v9®/K·_§ÐA£+ýsaÁQ«¿ Y Ë£oP 9}Mâ3g/}˜ÑŠR|/“X´f—ž·Ì³Ky¨;ó’Ùk3·=§åÚàï?A÷fµQ£Ä+p2éÿÓÏÿÑ•_i ý4åóÅâµJ-°ejúï›:ßíc²Ü¦3½k¡ü[pt†!ï„ø¬dUL5·=§áÆXthÞ‡ž“ï¶ç$ìþ±¬Þ«‹·K¾‡Õ“rwìòÚfRõ±C`âj•@2gõå ÑañÖkÔ*¤åûçÂÀÀ™ºîÃçï}`é࿨»xK2œ¼ôiu•²¬,eÀךĢ4»dt™Åf—r_wæ $³×fnOŸœ7'õG¯J¯Âu°‡!m"ýôʽÛg ynŒjÆ/ÕÄb÷Iªïš:Ÿ±mšŒFâè˜ñ¸9}2nOŸ„sZ¢zÙX7^ùœØítï3׿›Œä!íÐüÕ&ðwú ”z«=rwÜòÚfRõ±ÃÚ„YÔ*dÎêËA£Ã¨«Ô*¤åû瀳wìöékNÀoÛujaG-ØÏQ«ï˜eeŇY‹L¯aüò£O5:×,+KðgšÄ¢4»d|™…f—òPwæ $³×fnM—7Ýû¡W¥W1ØÑÍ6Ú½Žrµºà¬!Ïõ¡?⃗ªcÁHwÕwMÏØ6)^i‹1ïW@Å×bâoCqÍ~sTØ4¶Æ‰ ÆòÁkª~g$xŒÃõ!? Q©w°Ê-wÇ-¯m&U;¬Ž×šÍU;~÷}`qü Lwn€_ÖMÇê?‡Ã¦m8¹ÖE£Ÿ[ mûFhÒµ7ÆPî…Qò®ú?¶Ä_ÖBÝZàËNMÑø³¯0~ÿ4Œëòê~û1Ú|_º÷ƒ÷¦®øö§føü—¦øô·Þps­†ß|€Ön#°x³zYL[ò š}Þ­;4A½wcä;|gÛ‹âÌw\VÇk dÎêËA£Ãœðd³9;`ÚüºžáçáÖû;|ë“„9›¢ñs‹þè×·-ê~Ù­¾j‡?·ëêÏsáØ3cþÖxÿ‹^hÕâc¼×¦>ý®ê7s 3pý¡!Þû¬ Z~Þõ~öÄyZ´iÛ Ÿ|Ùw^ç¾­QçÓñÉÀx.T/[ŒÑS¡ÑG¿à“o~ÁûÕ†ÃÒø¢cf†™ï¸Ì O6’–ïŸ ÎÞú$ï IX´åµ°þGŸÙ{F̲²âìóÓMb‘œ]Êl™¥f—òPwf $³Ùfn“7Ýú WÅW0xà¨ÿO;׳*Wþ1c”Ïû6Æ›!Ê-ýwMÏØ6ÝÕƒ^/ƒÆ-»àøØôùOwû¿vrÁ £ù†ae­â)Ž_(ŽÅ /ê-Q¿ÿ>"ÇÇ-¯m&U;¬Œ›i6.ø-½ÇcEÜTLsªŸ×NÁÊÃC`ݶ5½‡O|=°âÈôü²%8«?·@?» ù]êàó…±bG4þ¼;柷Îõ`9î]j¡Ýê)Xy|†üXM¿Ž†6_à§~­ñÅWÍѵ·šiÇcEÜ$Œú¥jÚ²/ë£Yýz°™•‡‡ S³&±ÇöíZÁm¿ùŽËÊ8%ˆÏi}9htðÝ|ÅlNŸÐÍF‡ÏæsÕë[|ã}¾tøé“þèÓû34Ÿ»ðk [ôì¥þÜ ]l2ä·ýÍ'ž€Ïêy¨ÿÑlL=§ï¾Fç%‰pùá´ÕœohþøâCÔ¯óê´ˆ¯;õGË–ÝоCs|0ò8|6Ÿ‚ã—uÓ–µøÞý¶+/Áwƒ?4hûµG`ÛºœÍw\|7_QÉÐ?œ¼c–O^€QW©…6oÏ£3uݳ¬,eÀŸf‹ÚìRVË,6»”‡º3O ™½6sÓ}TÞÕ =+¾‚Á†ÿÚa6èP¦,Úw€sÃôwJãí{ã‚û(ÜãŒÐÎâ:Âäù²Þ¦‘Hìd…—_û ¡ŽqÐÉ ³ÎŒ€&õ0wðÈçä…ËCqÄÅG\p°_kÔyé-xtÂi·œ·¼¶™T=Bìà|šÙôžÐmý&Àÿø$Lqª‹&Âÿ@|ÓüSØ»ÔÃ×Ë'ÀÿÈpôþªúRþ}Ï"ÿÎhú•-柀Ñ6õÐ;r<ÆØX¡Ýê‰ð?æŽÁ?ÖD“Æ5Ñ!hü¸aØh¸:×Ãw+'Âÿ¸†ÿT+mÙÈñK‹z¨Ÿ ÿCƒñk³Æ¶×ÃÛ7‚cìT³%ÌY}9htð ¹l6Ç9ÿ€–“OÂ+$ Ã{~/5gàµn#>oô;z÷þŸM;¯ »Ð©¥-º§ûÜ ÖÖYä_µ [.ÅÔSpl÷-:-J„s»Oð…çixmŠÃï_|„úu?Âw¾gáµá0ú÷÷†]¯oÑzÆix…œÀ€/Z¤-³‡¯‹^þá´ß7hþkO`À—?¡ÏÊKf=6J iùþ¹0`ï¹½ëì]÷Í}»5®ïæ‹pÔD?}þÃ6秘Ģ4»dl™åf—r_wæ $³×fnº Ï›Ã{ gÅÊl7D•> gl[ã›Ê%Q¢xiÔ«ÿ b‡– ³F»Kà›®ƒÌ/«mrÅ–Êeòg¿^Ç »ÎèU·ŽŽ~N>Çaéöûšã7hT²&V ÍÝqËk›IÕ#ÄËŽO1›óæ7C ­–ŸŒ9‹?Gã/š õ¯MѸ٧èR_-eG†¢çW-`7Hý¹ÏÎ"ÿÎîhúU/Ì;>£lê¡g¤;ܺ¼ú_ÔG³Ï­ÐÀ¶<×ÿ„Oš×ÁÇ_ÖÆÇC`üzøv¥–Ÿ‚…ÁêeöÐ,i‹&Ÿ5À'_×CÃM0tÏ( h×#÷›ï¸,;>ÅH款4:h6^2›“ÇuAÓáGá¹ñ"&M€zwÄ'_wF½¿£W¯oñ锓ÐíBǶè–îs?üæ–Eþ•óÑ …&o< ûï¿EÇ pl×ïü=}ص;¬À¯ hÒèK|ðÉhl» ƒû|‹Ï§‚fã%LóQ/ÛŒ1QÿÃФåw¨ó^Gô_}=>ïû5æ;.š— ¤åû瀭Ÿ®”“—>~r@æF$S 9zÑ¡¿œ´ÑóVV|˜5pn²I,:³Kƶׂ³Ky¨;³’ÙXo¤uÜኛ#‡Ð|6¯m&Õ‰¡vXrtüŽM4‹Kbzâ«Þ}0û¨yÊ7Ë6ÇöÂ×½l;Í!v9®/'¯í˜|3‚/™ÅéË–¢Õ/ð2OùfÙæåËñéϫ̾Í]èŸ =·µtòŠyäµñf‡'Ó|vª„¿¼õgÎFФøMbÑ™]2²½Cm,7»”‡º3O ùüõÆþÑœíqc˜ ÍGÏ:€þ7Óœû¾Qö˜Ð KŽŽ7“c0qlk ˆt7ã:LéXLô9ú„Œ5ëzæî¯È9®/÷Q˜pS×_4“§1¸¿3ºÍO2ã:Li†::¢“ïY³®güÊp›U úç„“÷v»AÞ±´ÎcÖæ+4ô ½Œ1K?uÔê/8ÌÐ[=·’”‚Iäì’¥g—r_wæ $Ÿ¿ÞSsà`¿ž¸áêLóу¿÷Ä©yMrÞoß;Úh,:êNóQ¯hèö Íq}mÚì‘ `òº 4µhBÂü Dÿ\ØpòŽãìó`êÚSð ½BͤWÈex¬ˆ‡³wLгwìêçÎD¦f $7‰±vœ]²èìRêÎ,d6ÖûwüXèûYãÔýp}=Í~·E´mgü?Ö$çýã3ã Ÿ=C±àˆÍ½vgÄ<>3.Çõõèä$Œž·nË0aÍyšŽò;†‘s¶âÑÉI¢.ŒØ{noçìsmÔ¢Cÿh7]‚wÈejµ›.aúú³p[z䙳OlŠ£VÂÞs{»UŽ2à3‰§æöçì’Åf—úç©îÌHfoÝwtC¡ï×뎤þýpÍi®S“zÍi. ül»"ºOgÜß1Üdç=’ÆáBÜ(h#ÀKçŸ}C0÷ðÌûs$5¡sÀìÃ0s»#<# 9at®ëëÌ FÏÛ‚Q ÷`ôòD¸$a\À9jBÝ’à¶â†Íß‹‘s¶àâŸ3 Lÿ\Xq˜¡·ròŽwö‰¹?~E<<7\„vÓå\«Ùx)Oß/ zn¸Ï 1}ý9L L¤է0nyFûýùÌuΞG/ý#'ï˜s^Û5ÙºŒÊ€ïnÿŽwƒ¾_gÎ.YdvÉ-Ougž@2ûëÿçÔXœYرv6ˆ´î€ˆNí© ´î}ßÎHôþ=Ïm%ËóÿìXÄìs…Oä@L µƒG5¥Cí €¨Ý.x|flžëë¯3ãî÷ù‘pòÚŽšP'¯í5' ë7ùãÑÉ ª.ì8zê›8yG‡;h·ÿ3jÑ¡§“Nbfð…l=9?3øÜ—Ç ßØ×Ù»yn¸hÖ'õ-åÔugà¨þËQýÄQýÄÉ+æ–£—>ÙÉ;f¯“WÌ\{môHG­¾c¶/_#>Ì8;ÆdÞѹf—ºqv)_f—:á~ìÐ<×›YI¶+J)-ª2̇z+mÌgï˜#ZÝ3×9»ïŽXxðñ˜¥Ç0Î?ãüã0vùqŒYz Ãæï{8ÈwÇ=­î™³wL ƒFßÍÑK¿mø‚fn¸„ÿ’“×(AdŽ/QçeÀmRÿ99 gÚ!ÖΚ³Kf]ꇿãF˜¤ÎÌHš¶]QJiQ”äó¤ÕUrÐnkk¯ÕÙÛ{éf:{Ç:{Ç:zÇ,·÷ÒÍtÐèú9ú蛨¿£¼«2fÏðRÌùJ¬ütbÀi8zE?vÐè»åÛÁWüQ´ˆkž@ÒòûE)¥…]’æÃÖOWÊQ«ß5xöÖ$aZÐÅB«ÛңϽ¢Û{êóg&2•ø0kàÌHZÄ5K Yö‹RJ » $Í‹­Ÿ®”ƒvûdg¯˜ãV$€w›æÌ‰k’à:{ïG­îX®˜É Ê€?œqÍHZ~¿(¥´°Ë@2°÷ÜÞÎÉKuè¼&œÅäÀ ÚIç1lÁ5ú§Ú˜1;pÊ€?ŒqÍHZ~¿(¥´°Ë@2ÿPf'cÆ8j£Ÿ [pð©Ç곘´îBÒ#à,F.9úÌQýØÑ[¿Â"³j6[?ûß)WàôZDýß)W$l¶~ÆvE)¥KsôÏäù Ôêë:j£;zéSÏÙûÐmY¢Å_lï¾òÏÙ“â Õ=sÔ€2•Äp›ä§ñÎÀiWZD}ïŒÄ›d¶+J)-Xš£&ÙÇÖOWÊÞ3ÚÉÉ+:ÉÉKÿ`è¼CG/MÀ¸UIpÞ¬Ž 8‡ÑKà2gïCG¯˜GŽÞúdͶ>¶~ºR–>.éHŒèºýÎ;à” -¢Þ9`‡Ä›¶+J)-Xš£&¹Ãa†ÞÊÁ;fŒ“—òÞJ—Y»îwèɈÅÇàæ î«“0nõ¹\é¾ê,ÜüObø¢£2oÿcgïØ{öÚíÏœ¼c¢4Ñ.fö13âC;÷;ÝóN -š^Ô÷zÖyÛ¥”,ÍÑ?“¼£ÜK¹­­!° tôŠN¶×næà¥ìä{Ïeöž{®sö?p·ÿ±ë¼ƒ ]ð'\zæ:ïà_®óö?v³ÿÁ Yg§› IDAT{î:yÇÞsÔD?uÔF?qôŽ9çà³ÄÞSg?Ðs[KKïc¶IÒÙVJŒ°Iygœr¦EÌ¿aó8Ig›÷?“ÄvE)¥&Ó\ý31vÚˆªŽ>ú&ŽÚí4º~öZ½£Fçê Õ»Ûk£G:jt®öZ½£Vß×Þs{;G}“ü™BK“n=ù‚¾×Cœt-Zžîñ(1ª‹Y^ÀvE)¥¹×œý3!&%Ig[*1ÜæôõݶÏpÒ´hxk_Ÿg á6'’t¶f¹q—íŠRJs§¹ûgBLNbdg«Äë+×w÷~†“AÿÛÞÚgû,1Üæ^|DǪlW”RZp̯þ™““ÙÙêD„Íñ úÿгN  ÿ1Ÿ³Ã9]·G‰‘6Çò«“b»¢”Òçk‰þ™“£\Ž´vJ ·N9ÝýÞ¶xzì7ü/ñà-lþ“ðžû wö÷ÁÅèî)ñáÖLJY{ä÷å¶+J)MoAéŸ 1 I:ÛJñaœˆ°OŒ°IN³~f Z¸L·~’a“œa³=!¢K7KwPlW”RªXÐúgB!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B)<¼ "E䔈@D®‰ÈyÑ’e ¢ˆÜ‘Ê9øŽ•(û1,Cúw"rPDJäpÞ‘+"R>‡ßQŽíi˜‹ïB!„hЉÈt¹*"D¤ºˆü$"É"2ĂەJ^ÉÇ"RK•žŸd1ù\Dæ¶…$),ð‡¥qrûò«ˆœ4lË!i”ÃïBHämy "fHo'"a¢ ÅDÄFD.‰Èÿ éoò5¥Sœ'"ODd¿ˆ´‘ã¢t˜sE˜ºˆÈzY+"ÏDä°ˆ40”ÑÂðùÅ ŸËŠÈNC9—Dä5ùRDNÒ¢E |3b%"wDd™ˆ„KÚ@¡8ŒíSql(㶈L%°N8²³ /ˆÈhÃ6<’¤pÀ–Ï'7¤•(ýlQ¶}šˆœ‘Ò9\7!„8~‘xyÉHžÚ"rO”å5Y)"¡¢\ Eé¤ûˆHQÀ["òžˆ|&"E¤†($D Ð^­ˆ$ŠH)É:|QÒÕD䦈X‹È«"2_DöÊ¿gJ¬ ß±‘Ë"ò«!]=pÛ§6"r_D¾6”±KDRD8²» ©”‘óÂ@’øÃÒE‹H¿ å¥Î@dwRa I üaùï}2åËâ¢)"ò[y!¤PQS”{‡>Îþ£¤u–n¢t²©T¥“}]rHž’ôÇUQ.¡µ‘#’6ptåRXÆÃA”Ka©”‘&’v¹(õÀ!"òŽˆÜåÔÃØ>ù‹ˆ—jYKÕ±Èî6¤Â@’&øÃòßû-¦ùaYED6‹Ò·}–ÉrB)”Q.Ït%°ì,JP5ƧŽ(—}Ú‰Ò®å¢$gDÄY”_ùQž`,m(㞈4åòÏA9&iÇyK”Y‚ë"ÒÞ°S y³º”UA•6RÒž–,ñœ}úܰÿ_ŽÇCÞò9؆TH’ÂDMáËŒûdŠ–å ûä)ãéwB1)/Šˆ«ˆ\%غ*JÇš:+QLDº‰r#:DùE¯¾¹>»ÇQî;‚(jjpUB”{‹ Ê宾"²Ï°]%EycŠ(èO"’dÈ»[”ƒŒdH–å†øÃ’vs}Vû¤¾¹þ®(¯ 9%i7×ggÔëe I üaiž–6"rT”{Ek¬)9bœBŠ,]D¹—ˆR°áKÓÿ°ô1,ÏhfOxBÉ’„TØBÉ8!©°? „B!¹‚$!ä¿A’ζTÜf›>'"{‡'FöLN·yf JiîMïò$1²grâ–^»âC;÷KÒÙòÅË„Bþ;$élK%FvšÙ-åâîQïž^†§ÉøßÝÀ½}”Ò<øÏíXe€eYÃûËÑyO³â¬&$paɲF£1– £“lÕ¸³WÒX–5Ÿ\Ý€ì¤ i†ˆ\X²¬qiÈÂ’a$‘­wûÏ[{€‡©,ËÝÞ‡l嘊\X²¬iÈÂ’a$q)Á¦à?’€‡X–5ÿï^".%Žú·!â–,k\²°dId%XSX–5"³¬aˆxÀ…%Ë—†,,FY ÖÀƒ$–eHC%’\X²¬ñi¨xÀ0’‰¤ŠeY#Ò ‰¤$Ò,Ëɉ$cÔˆ#‘eY#Ò°‰¤ái–e‹äD’1j²¬û ,Ë‘†M$ ŸH³,[$'’ŒQ#É8–eHƒ&’FH³,[$'’ŒQ“•` ÜS°,kD6‘4|"Ͳl‘œH2FH$÷Köï»py£/9M†RfÁêY•ͤM‡ìÕ^ø÷•åê©>ÿ}ÒÎ,Áªø)䬞 Œ·Ç²¤H:õ=ònÅV¨ šHA"Ͳl‘œH2F8~ä“Ó:i Îzã~Ü~ß./ nØ [w–sù{aÙ¤-¾ßøƒÞ×»˜ {µØAC;àUj ׈½zoC)³ÔGºôSÈqäæA½*ok‚¾ëÚ终þ¦=x®œË¿´ƒ›·ÇÌCIz_oM3;£k½×14tâÎEc©C[¼ÚÆ~Ö_~ y¹úʰ‰$–\XraÉ…%ÃHD$’[uª”Y ?n¿^õzÓƒ<·ãÁ ktm?ëÇôCÛú5P«q'8ûEáV" s=ê¾ö.¶n݃k£ñÅõPƒj£u·áضqòãöã~øtî0kFvEëVïÁ¶sC^iÒ¢‘i?Ý×Q ˜¾ó1Â#v‰õPìÂOs¾Ä¿šÖÕn‚>Cœqj÷>äÇí-³­bî‚büW˜0¸/Þy¥\WìÖûv‰¤î>Ò¥ŸBŽÃ7RôªÜÜ}×íCšj,Úö˜ïYïâ­†5Póµ.½e2¯l€cßú "Ôi9§•Pl›€æõQƒj£ù{#±ð¨‡o¤ 5Ñf½láaßÍÛ¼a}EÖiÞîGöbƒß`´{­6ˆj açOáz ^¬Gn<¶‡~ƒ.-ê€ê4E'빈ÎNÆáª2ÛÒ4quOÔï6{®©§õFŸúm1#S¥·í$Ié}eÐD’ K.,¹°äÂ’a¤"Ž(*eÈß¿W¯z½a‚AÑx°\óZðþØ…¸¶c=G›¡~[œÛ»ùÛ`pîØ½lš‰! [b’ërÜØº[¾h‰ú&ãüÞ½¸j ³ZõÑác9ö‡­Ç½í¾°lÚ ‘›÷àQ¤-º×ïOÿÕ¸±%Ñ_4G³ýpgÿ^nЫÖÇâÞ2 ¼Ó /vDÅŠåoó†eÓŽX½1OvnÀŵ›ñ`ï.<މÀþ­Q¿Ç<ÜÚƒC-Ñz°~ß» y{cq;l.V{­Ây-m•ú}¶¶yk¸„Æè};)e’úH—~ 9ÒsôêsôY‹å(¼Ùd¾?/¦§$Zãm³‘X—“€ôó>ègÚ ¾?Å!va4|>ö_ó¥vG_ÓNpû1É 2´nÜ ŠË¿¸·èŒù'â‘–ƒGw"%7©7a™Ãxõ}o$äþ€uòVh& †27é¹ñHLZ€ù[6"FK[¥~Ÿk»µb8:Öo„‹Ö#EÛÉO!/W_6‘äÂ’ K.,¹°d‰ˆDr“N•2 äíÚ¡W½Z×Å ·¸¿l:¶þ™1búýe#Щõ0ŠÙ¼-nÜ 3¢6mÅË×ATëׇiýú0­ÿ*êÖ5G@x4î-ùíZ}ƒ õ2ò¢Ò¥ŸBŽÔëqzu²¹ z¯Ý$¥ Ú˜YbM¶˜ž¤´Aóâõ9/ô3íŸ3{°Î¡ˆê¢Aãhظ6~u굃Ý Š‰7ß±@¤z©ñq‹Î˜w\Ôœ ð·Ú¾óÞ}ñÑû¦¨û¾â®ÇbÉW¦0s‹Dr±uÓÞVÉï‘rj¦|Ò : ´[‘¢çí$Ié}eÐD’ K.,¹°äÂ’a¤’•` ü¶A§J™žìئW=[×Å —u¸·ØZƒÌ­bz±×]ðIƒNز> &™¡å{óp«pÛÖàdðRü³ wƒ‡¡}«¢e<ÙäË&°:2 YòöhüŽ’×FáÉŽm¸9§3št›‹_wlBÚ—ÍñÆ`oü®^æíàI˜75'´´Uê÷ÙâÛf­à²x³Þ·“Rf!©té§ãàõ}zõ;sô^•Ò mÌ-™-¦{}Î}M»ÂûÌlóm‹×†ú ®pW¢±)nö_Ùeü¼eV´Œƒü0¨E'¸߃íAРë,?µ¯ïþˆnhø¾×c1ñu4· B‚z™ñqv˜°0µ´¥ù\XŒÑ¡ã켬ßíS¨ŸB^®¾2l"É…%–\XraÉ0‰äz*ex²-J¯z¶ª‹As"q/x8:µŠÌÍbz±×ëfã“zæX¾b3†‡½i³ƒqmu0v~k†&f£q:* wƒ¾F{e<Ù0–MÚcõª8?± wœˆ“·àþªypkWµ:Nõè(Ü üæõ;ÀË+¿FxÁ«]ôý=îhi«Ôï³ÑK$’Áõ¾D"©»té§#åÚ½:Éܽ×lEb¢ ṁcõ%1½Øë³îèÛ¨fÙU¦=z4}#W/Çî“Ë`ß »MĦŸ÷ !Îo™-#å¼µè×wb«·ô“cãÅÝPóÁ¸^µQ³ß,쾺ñû-ñFãÎønÇ:ì;¾ßõn€¶®Ë¯¥­¢ï°»Â{¡aG„Œ@TšÚŒuØYÛI$’ÒûÊ ‰$–\XraÉ…%ÃHE ëtª”YàIÔ&½êÙª.ÍŠÀ½…ÃЩÕWÈÜ ¦{½1A]¡¦IWl\¹Y³‡bP³º ª…í>ÁÚ%kñ$jî Eû–EËx²Ö–MÚaõŠxá†9¡NM´nóí¿Å€z¯âcù2<Ú²G¿{=ÖÕ¨®ïOıuñ$j]™m•êzؾÖ. ×é};‰CwéÒO!GÒÕ]zÕÖ¼.zEnA|ÂH´1ÿ«²Äôb¯/…Áþ}SÔ¨× ŽÇ zz½)¶kÓÞŸÃí` ’®îBœÂoš-#éœ7>jÑ.Çb¡:åƒQýMQ»Ö«hÖåCÈ—ÈЭQ=ô^ å•­ˆ „vMEšYØaÍùX$]Ý^f[EnÃò MAD%4ƒSæN½m'1pHï+Ã&’\XraÉ…%– #‘¬kàÖ*eøcózÖ*e’úH—~ 9”Ww°ÐO!/W_6‘äÂ’ K.,‰ K†‘†H$#uª”Yà kX(IÝ}¤K?…‰WbX(Ié}eÐD’ K£– Ëêou),FbàˆÐ©RfÇk#X(Ý}¤K?…ñ—·²P Òûʰ‰$–Æ,–ÕßêRX2Œ$²¬_WéT)³ÀãÕ+Y¨”YHê#]ú)äP\ÞÌ@?…¼\}eØD’ Kc– Ëêou),F"‘\©S¥ÌW†³P$’ºûH—~ 9öý¼‘5€"‘”ÞWM$¹°4j¹°¬þV—Â’a$!Žå:UÊ,ð(<¤r†ú`EÇ·0wÞbéËpmÖ0|ÝÂ5kÖC—>2d.^VÊçõ<_¨v é€6u DµÐü­aÙü`<Ô¹¾!x¾gGõħœžø>h^5©&š½ù/,›·¨ø2ÂCp{ÞWèV¯#¶Wl»‰CwéÒO!ÇÞœu¬‡ô¾2l"É…¥1Ë…eõ·º– #‰¬kà—0*exº¤Âžs‹€oÄ^÷EÿþpáŒlPŸÉœqÁËf&xë#güVâóúž/×®'^kÜ«\}pÃo6½ßu߉ Ë–àQHP™ëû(t -óÀò^°ËÅý^1Åèqs³p>b?mzÍ>FêbyÏ…çªÙÛUlÛ)e’úH—~ 9öäD²ÐO!/W_6‘ä²xa¹XGÁÈ…%ûr– # ‘H†êT)³ÀÃ¥ÁvïWïal¿ŽhSû5Ì™øßé7캣ékƒ,^ÿ2­/^7€äEÅ?¯ßùá–»;œ¬Û ݃Ôóâè7m`Ö¾3ÞªÛ1Ûv"‘ÔÝGºôSȱ+;‚5€"‘”ÞWM$¹°,VXÞóÔQ0raɾ¤…%ÃHB !:UÊ,ððû…•3ÐL›aÎl?õ´œÖ ÚÃ5õ<÷Ü-Ðë•w°Þ;Pã³úž¯ÈkÌQ“ôJ;,uõÓ±¾ ñðû@d鉱“Îyúâ¾zz®Ã@¼]¿b}ż·çÅ»Íz#Êyzš´CŒ_Ŷ›8t÷‘.ýrì¼´‚5€bàÞW†M$¹°Ô,,¯ê(¹°d_ÖÂ’a$‘•` Ü\ªS¥Ìƒü+§ßT‘˜ÍðVOóÁÑ!ÍñZW[Ü,œÇÓ^iy¾ŸÕ÷|Žç;ÛßoŽz-‡àX€¶õõÇÃ…ó°±W/DyøiLóÄ£ ×+õðňYø=Èf ð¦9~~w†ž&mã]±í¦”YHê#]ú)äØžö]‚%>_bÆØÆbøŽë‰ï’*ÙÆÅ8}Ö“|»„e/x»ˆ£?åé+Ã&’\Xj–÷´Œ\X²/oaÉ0’‰ä*exàS9½'c‚i3ÌvòPOóÂÉ¡-ÐÀ|®©ç¹ëò z¾ò6ÖÌ÷Öø¬þç»æ<)sà¾Æ|=^y«æykY_Üw›^ÖÈñ¯ïÌ·…g‡FhüzwLqÁ<ðÄÉoÞYW+\òóÁݹ_£‡I[ló¨Øv‰¤î>Ò¥ŸBŽ˜¬æ¶CSðÅ$¬ÏZŒïgtÃðØïó“l ç9ÑãÛlѽGO„Ï<Í×à>½Äü³;¢ë7ñɧæèüõ|*ëƒ^ï‰`øŒz¿è¿êŒc'#lÿh|1¬>ÞL™9Ñýóžä1ëã5ß›„à Ÿ£ßG½1Ȳ7º´í…ùÉr|iku_Üv‰É Q'’ÒûÊ ‰$–¥–¥Œ\Xraù– #‰¬kàÆ÷:UÊ,ðÀ׳rzL„Ưaö4÷ÿNËß M›~€ Oñú—ïz¡eã~Hò(þYýÎ76Aë>¶øE=íwç!è\· 6¸i_ß+czb¤lîùzâ¾ûw˜Ù¢z … ^šËwE´yMÕDZ5Q»†úg¶j7ÅÔ©óʽݔ2 I}¤K?…Ñ—¼0×®éa¾Øzq‚»âÛAˆ>3ÖƒÁif{¼·Ü[ÏÎÃøOÂa†æë˜,/1ÿ¬Nøhm¶‡^EÄ…@xXu­*Þ£Ì14&Ñü1÷›6èÝ«ºÛ|‚a“á“!ïbôD3ô ñÅÖ‹pÞªè½O»¢_×.pÌXŒè3s!ë×óŽyÃqèûð8ñâ¶KôE‘Ä—§¯ ›HraY²°,½`äÂ’ Ë—»°dIˆD2X§J™î{»WN÷ ßø5Ìvpûï´{®6°¬W#ë6Ám_Å[ý'⦷;î{Î@œ•%sæé}¾\ÛžhQ÷mxMšŽËs&#¼[4i?½´­ïllïݳçã¾÷|dËÌÐäõç4 §œÕÎpF®§;n¹8áì¬é8;k:NM„N¯¼‰°iθâQþí&IÝ}¤K?…Q‚_˜aþÝ1x£?¢."ȹ3¾Þ€¨Óðù»ÀqV|¶ÅQgÝ0qÈØÏÔ|ý¾›ZÆü‡Ç¡Ï[D^ðÇ›.˜¨ò…§†Æ ê¼7fóz÷z–{uÖ® l0gF|€¨ ~pf^ôÞüo0|@LK_„¨Ó³1²_/¸þè7‹pÊ\ôB·H$¥÷•AI.,‹–eŒ\Xraùr– # 1péT)³À}·Êé6ã7Ålù\鮸j;Ÿ7­‹Ú5_E—®Ÿ#ÓMýž«5†Ö©ÏGÏÔÿ| f îÓNèݨ6¨F]´oÿ!öÌrѾ¾.6˜Ðy(Î-pÃ}9HþW1 ³;¹[ΧÏÑ£î;ØæR±í&Ý}¤K?…›/½0WGöÀlº°«Ö„^ŸôÆ ‘}Ыߘ6« †lñÅæ³.?dä3´?dü±IDAT5_¿Ù+˘ÿðXô2«/øÂݦ Æ«¼á1ê-tý¤+ú}d†n¶“°t÷0¼÷n'ôÿ´#úÏu€ïÜ.ø"Ú›/aí^Í÷±lÃ`ôþ°Þû¬ ºwë —cîp:óO¼¸í²ùBzàÞW†M$¹°,*,µŒ\Xraùr– #‰¬k w¡NUÖ–¸7oîÏŸËV±J™…¤>Òe@œÎùcãù€↌ñ2qVž{1Ë!ëœ9ŸM°}áëì§—«¯ ›HraYTXj)åV\XraùR– # ‘Hè4ÓnnÎpÄ=×YlzÍÉéS¤õ‘.—'9"ò”6œó}Az"ÀkTÞ/° }ê…Ea’Âë…¶qÜ¡ªiåê+ƒ&’\Xµ\XraÉ0F…8üuz9§&ǽ93Ø*ôÔÔñ¸¼zš¤>ÒåÁç"$mÖóf«ÐдYH=îR®¾2l"É…¥±Ê…%– ctd%X×}uúw–Ò'[ã²ýdÜ5­/É'!ÍÖ gyIê#]>¿êƒ•¹`ÍY¶ =<K•x~Õ§\}eØD’ Kc• Ëêou*,F"‘ô‘ä£T¤O¶Â©)cqÝ~2î8;à.«Wï8;àæ´©8i;i“¬ðä›äþ‘âÍ‹îQ9 4ÕáÇç"âÌ<¬þi>«G#ÎÌÃÊ“®XrÐ K•¸}iA¹ûÉ ‰$–F)–ÕßêVX2Œ$ÄÀá-Ùÿ»ì…«kí‘)·ÊÚJ™«GUÖ–HÿÎ ÙaSñw–G¹úFª_óBÆñ9WMC@œ~ VŸÄÉ¢r@ÒÑYx~Õ«B}dØD’ Kc‘ Ëêou.,FY ÖÀ5O–eHÃ&’\X‹\XV«saÉ0’‰¤˲F¤AI#H¤Y–-’Iƨ‡;˲F¤aIÃ'Ò,Ëɉ$cÔd%XWç³,kD6‘4|"Ͳl‘œH2FH$ÝX–5" šHA"Ͳl‘œH2F8\Y–5" ›H>‘fY¶HN$£æR¼uÁ.Ï®ÌeYÖüÏå9¸o]`ˆxÀ…%ËŸœH2FMv¢Íí?³fWæ°,kþ™5ÙJ›Û†ˆ\X²¬qiÈÂ’a$‘­}ðÑI9py˲F࣓rd+m2 ¸°dY£Ò…%ÃH"+ÎjrnÚø?py&X–5¼¿¤Oxž•`å`ˆxÀ…%Ë—†,,F×SmM³•6yÿ¾è\žÁ²¬ýëÒtd+mž_Oµ55D<àÂ’eKC– #™K‰Ö o¦OxŠŸÀ²¬á¼‘6îYvÒ(OCÅ.,YÖx4taÉ0’¹žjk’hsåîQÛüì–e«ÞÇ'\J´É¹žjkbÈxÀ…%ˇ†.,¦\d«¬Ì²•Ö¿Ý=:±?O˲Uçƒã¶Ù‰6d)G´2t,àÂ’e ¯±– S.²UVf9J› 7ÓÇ=ýë¢Èq`Yöúçy9rSÇ<ËVÙœ7†$².,YÖpSaÉ0åF°vÎN´ÎËMûÇ£“¶øóüü'ÛÈaY¶2þß%;üy~ ˜„_ÒÆæe%Zÿ•`ígŒG¸°dÙªÕX K†©×SmM³¬r”Ö‰ÙJ›Û—¬ ²¬Á²lŽ”hýïl¥Ííl¥ÍÁKÊQcŒ1Ô„ K–}qV§Â’a†a* –,««[aÉ0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 S)’Fšd#[r`ܨœ¤QVy*+Ë‚äQ²¼”q£®¥Œ±SYY–¸(3"í"¢%ÞÛ¢~¯£W½:ИˆQS-ón7×Ó¿$¢SDT[mT”/‰(þÙŸU†¿Ân¨¿Â~É¢DçœÀ„iyþqvqÓò%:][˜è濊ԿOÆÈq\š>Ô)4}Éœ™93ÂÓ󦇦ÌKÏ›³"óÚ¬éaÓC”§¯“دJÚEËg^ä¾T}‰èÕÕ2džaŒ“¤‘Ç&¶ºypâØ¼ŸÃ–áΞXü‘’Œ§‡3ñGJ2îì‰ÅÏ¡K Ò¦LÌKmu+Yf9BÇ"͈è?DtŸˆL5¦7 ¢ßÕïq"ùO ŠçDd®1ýz ðWØ L˜v3X93o×é 8x9Çsãô­“8ž{/'"öÔú‚¥ª9y oùÇÙéúûdŒÇ¥éCg†§ßt8”•r ~ºãWòpöæ¿qüJüt[’¯¸GÉ›žqË)$]J_ß%¢!DÔDCS"ª©å3ÆœHr|`ÆxHc™:a̳ÜõkðìP†VóÓS‘»~ N›—l#[©e±f$Ö)¦ˆè‰ÀÞ‘D Ÿ®~ ":KDÕóv'¢DäMâˆÂ]"úBýž¶ÏÕ "9ÝS¿?‹ˆ~&¢zê÷?%¢õçÒˆèmöNÑj"ú·ºíÁDtA=oÕ’°ŒÒÖù":¬žÿW"z]Ëv{DD›‰(‘І’Eiík¶qWýý¿RÏ?ˆþ&¢fêïpŠˆ&¨·•zþCD DÔRý™^Dô#Q6YRÑ@Ñ“ˆîÑ'e|½çœ8ó™âB Nß:®Õ“¿…âB ‚•3óãíµý}2FȬ°ôH—U‡Ÿí>t goü©ÕSןc÷¡[p]u(oFhº®¾¾KDýËxo '±ÿ¼JDç‰hýs-kŸ/¹Ÿô§ŠÅ-©‰$džaŒ‡äÑV»Ž89<}¢ŒÇÓÔÉ>QÆãðtûg)clÊX´‰`5ƒˆvhL_MDnDô‰D²‰Àø)µ ¢H"Ú®ž·;ýEDžDÔˆ|H$¡ut|®‰$ö"z‡Drø‰D²5‰£¤Ö$‚f$‰`XG݈h5'¢3êå´'¢‰è)µ‘°Œ²ÖYêÉêoÑHõtÍB[ûšm,'¢=YýÝ> q$æ>µU÷ÁD4ŒÄ`MDq$¼^êïEb`øŠÄ@aFb`±Öò=ôB`¼ã®å<Ÿ¹™Ž¿‘ì‘›é;èþ,(azYŸŒ‘1kyÆ.¿M'ŸÉÉÇÉkÏ%{$'¾›N<›µWr?éA‹[RIŽ ÃIc¬Ã2哟>QìÃÓ¤Är›—‡ÃS§Œµù¾”Å&’‰(—D«G¢JîNE‰¤ ½¥þL" "¢õëî$ZcõëžêÏ›èøÜJµ…|C"°Õ#";õ|…§_𑬭ÕíÝ ¢#—!$ŽB‰#WH\S¥ke­syÉFD4B½šRñB[ûšm|N"‰n¬^‡$Ž<ô×X§D´GcYmˆè ‰A£=ÔXß/‰(‹ˆ®Ñ^zÁ§°&:†…&»=ͼ‘‚c¿¤—Û#7"äÀ¼ÇñŽ¥ý}2FĬðô0u?>͸˜‡£??+·‡²óáµáÄãYáeõõ#úçõ‘·IÄ"¢Þ$ö›ßIìƒDÅ÷%mû\Éý¤¢q«<‰äÿ||`ÆÀ¨¬†™%²zúÇÞ]Èß_aÆî@ÊhÙ³dKËÎ%š(L$›Q‰ ÕŸDelJE‰d]"ZD"€%Qk&’…­ðu¶úµ¶Ï%8}TH_Él=$`¡ùêuÑ\>ÑõüD"‘¼L"‘ÔµŒ²Ö¹¼‰dm"RÑ "JE…¶ö5ÛxÄÀ8€Ä€1’ˆ8Õ¿QÝ^„Æw$GPî‘8rÒ‹Ä%…ßåKu»kI &š×héÿýSÌ&L{šž›ˆÃ7S*lêÕ8Æ;>ó›Ròï“1¦/N7›¹<óéÁóOyéi…Múéf†e<›’^Z_ß%‘x™kØ†Š’Z$NG+5¦iîKÚö¹’ûIEãVyÉÿÙøÀ0Œ<Új_v€oAÞ»QYs‚ü RF[ï)ÑDa"ÙÄ5H1$ŽðÍ$Q‰&’SI±æêϠ≤fb§ùZÛçÖ’®…|MEG$§“¨¼ ©MâhD­RÚ++‘,Ï2*“H‰ÓKIœ&+(´µ¯ÙFmƒc‰kªÞ&¢›D”IâT‘‰#…¼MbhAb Ð¿$q °‰Ëvö›*L`œÃ¾¨ãa¹JTÖèãË ‚§—üûdŒ„áéûV).¼‡Êw¥`öòÌÒúZÛ©m"q#Îuõ|ÿROÓÜ—´ís%÷“ŠÆ­ò&’Dÿ£ña#@ieYð8z òb·WÚ[6AieYja¡yw¶f"ù‰ ¯o’¨d5I'«µ"ˆ2Iе}î=Uø@ï$]#ÙF½^$‚y‰\§”öÊJ$˳Œ’‰ä#"zSK×”(ˆˆæ“¨ôO“þÚÚ/ÙFáщ±êu¸JDÏHœæ""êD⨡êem#qD·´Ró®Ls"Ê#¢wµ|— ã¯$_Ý‹ÔëŠJ«ú9þqòŸÔ™šŸž$ŽŽ×*ÑôN"ZOↂ§TtCEi˜¸â1Æ$ªü#Z¤òRÜ;=$µ þÔ#$}Ri÷Ÿ¸é!©3CRMK4SÚ]ÛMHì3 IYLD“IÄ‹ºT|_Ò¶Ï•'‘Ô·*’HýƆa ŒÊÊrð¡©“?Ù}™1iB~ÒÈáC5šÑL$k‘´Ï“fš‰äk$‚O‰@8žD ›HÚ²¶ÏÕ$"gð§j.“H‰Dµ}D=J"¹-¹|¢²Éò,£äi­dVÛ]Û%ŠÂkKÏPQRVû%ÛøˆŠ?/o‰»Ðë¨_× ¢1$ŽØ‚Ä©2Í»2Ë(ˆÄ)ºãËÒ þû§ ^–2÷qʵ=ЗK’fçû+ì4ÿ>;RÑÍS…4"ñ7û!IK$ÿE¢`¹GÅŸLPÙG´H¥Ú'’ÓC v_ûããøS¡/çEÍw\š>´DSe=GrÑnqª.‰x2•þ¹/•µÏ•'‘Ô·*šHþOņaŒåÈᎧf;?ÿcóèËS³Ÿ+Gw4ôwSÓ‰Š?vâ3"ú‰8 U |rÇÕ™>ÏUWwB_®Îôyî«kþ}Ö!q'­ƒÆ´OHÜ [Ÿ¤%’…7u-%‘ˆ RÑRˆ¶Ç\½Ärq\šê}îùÇA_FŸ{î¸4ÕXbÃ0ÌˇJfá}qž þX©7ϹÌ.H”YÌ7ôwS3Äiô7HýL&"¾s·šà¯{¯;º W¢õæšÃþþŠ©%ÿ>I$p…‰Ý:J!ÒHÖ'qZ»‰ëÏî“8UJ$í-šh{dÔKý(—é!éÞKb/a×чz3xÇÅÇ¥iÆ‹†a^>T#-¿;9]žÿ8b9ôåÉéò|#:"Y‹ÄÑ?H\¾™ÄÍTüâ¦~·<Ý=ÿÏ› /—§»ç—8"I$ž1úDýo}ÅÇ@õ{ºÉH<꤮ÚËDô­ú=)hÑDÛãb^êG¹8…¤ç»éL~Læ}èKßMgòùˆ$Ã0Ì $iäð¡Æ9}LD£I¡)|¬FR–ÕƒÄ@Þ¸”÷˜Jgqس`Ç¥pTÖ•‡&Ú—õ÷يľ'$UHa"ÙƒŠ?.æIÖM*z ‘8’y‹ˆ¬HÚ#Z4Ñ–H¾ôr™–ç}± Rõ;*«ß–s3ÃÒKëkmûsY1¡6‰;óÔŸ;EDÝÊøRãQÙ1±¬÷ô¹ß†ÑÉ6–UV–þæ9ü½+ìï^ d5â©Êj˜Y)Í|Dâ·µAÅcƒ£#‰ëÓ“8’R’ªž¯‰£?=I$ãHí &ñ󈚉ƒ ‰ÇmZ½ã7¥³¿ÂîÏ-gƒ±-kY…:÷=âíŸúïŸRÚß'‘8Ê–LEÏ-¤%•þ¸˜É$ŽêEÐ?Y§^V[’öˆ–B´%’D/ù£\¦…¤wvIÿ3ô‡›X™p»Â†î¿ §°Œ§Ó§—ìk3*{Ö¾$Qô ±V©·AÉgJ?5¨ì˜¨í=}­Ç7†aôKŠõÏŒq£òïyÎÇ}/÷r{Ïs>ÒÆX?K±é[F Hœf{NÅãg$Ž–ZƒèŸGx 1ß$® I¢ê/ ÔMéŸ×‰¹’¸ â q }!&Ø{'9åo9¿Q‚Ëí–ó‹°H5ýÙ‡²þ>#Á9,ÃsîÊ£ùËöÿŠPÅ­r»lÿ¯˜½â𳡇Jëkmû³¶˜`NâšS"q÷û"s*yŠ_jü©EeÇDmïék=8¾1 £_RmmM’¬FÄš0úù-—™¸ïî"Ù;nspdüè|¥µeY§ ©Gâ&ÍÀ8“Ä…¼Eâ¨J³Ÿ­êùFÑ7$NµÔ˜ïC7%þZF'§…úÒ?Õ„Ñ>©¶&qö±ÁÉNÏ×ñŦó’Ýp.ÁÉÎùþqvºþ>#@\+™;wÕÑç‹b¯cÉ¿Jöû=71wÕùN!ieõµ¶ýYJìAâ(a>µ/eùRãO!¥ÅD)ïUv=8¾1 £RmmM’¬G,L±™Ÿ3m î¹ÌÔi¶ýd¤Xxª²©»…R£‰8ò‰€Wò§«r¾šD´•Šÿš$žŸ—Gâ÷‰Ä5r$®Y{›8оP|RmM|v âòWsÃús>: ?⊀xû§~qr)ŸŒ‘`»1ÕdzÈÁ…Ρùž›³´ë¦N=7_€sXÆSç4)}]Úþ,5v4 ñœÎ³$b€&R—QHEÉʬÇ7†a^,*Ùð*+Ë+lF柱‡«SpËÉwf8â–“®O›ŠŸ&/8`32?Éjĵ?‡¨Òã,*½rn^â³U9_3c.¼&¬9Å“¸ÆìCÏ8Q,‰ÓBh«Ÿ¸©ýãì®&Øå/MŸ?º`õéùˆüÉ«OÏÇŠã®X–>³ 0Á.ß?ÎîZ‰ŸCdªÓ–èšvÅ9,#ßuíixlɆ÷¶«ðÝ‘ ïmWá•×µg œÃ2òBÓ®•òsˆ¥QÖþ¬-&t¡âwí¿Eâh æÑ<]Ë(ò&’úXŽo üxRmmMTV–ƒ“d–kS¬,s’¬,óT2‹‚$+˼+Ëœ™eX9ÈBJ Œ_x€sáoÊ~H¥_ST•ó #ñ»»DâÔÏY?}Wòæ‚§—þ"¢ÿ£¢ßT~˜ŠOª­‰ÿþ)ƒâí×ÄÛåøÇÙåù+äþqvy v9ñöaœ@¾ˆ£“;…¤­u ÉÈ™’–ç’Z0=$-Ï)$#Ç)4-LbI¤}Ö–’¸ªö$®,yÊZjü)¤¼‰¤>ÖƒãÃ0Õ–ÒcSwŽ%q]ŽŠÄO¼‰;Ze$ËRUóÕ ¨ “bCâ'êÚ“¸ÝœÄ]µITòo©@¢âïBÿ<ÍÄ0Œq mÖ;>"qDîCGÿbH 5 2 1 °ã2Ý@IDATxì|EûÇgöîBU‘" v±÷†ìúZ°¾¯ú¾’ 6Ä$=ÍA|프×úêûE_{ÅŽŠ]š"„*HKHr{»ÿ߳ɆÍåÒÈ.Éoø„ÝùÎìÞóÌ3ó¬R $@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$к×òR  íHàšPøˆhDùósƒ³¶c1xkÊ=ÀŒØm»z| ]UL(͇€å˜Ö¿þàUjE?lÔçÚÆH붇­ü›jëï¡Ðþ•êÓ^Ñ€µ9?+kyc”§)ç)Ï™VѶ¨C )׃e'¨*IõãÅÔ$°M „B¶±,’{ ¤ß‘vdsרswE”*\[h Í ï7Œë&çdÎܦKò›]ÿÐC­JWn¸ÕެUöR\Kž¨–e§s>7 ãŽ)we½èªÈ½KVnx · ¤çæ¾ÔR„Îâ"ë~eÛ—¹ËÌÜle‡‘ ,oiCßchuƒÖÆD¥ôǨÏ~Q;úazv8WFƒ›Rý³¬Å+7L±”E¥=¸}¬µºSi#ÓPzX~Aøp(K%Q†‡o¸¡DiXx´šÝ=’YI0•Ñ}(¶s¯Í ÷nŒ{'Cžà=ýs²V:Ü_²m½¼Äß”eæÒo®=Ô>Ê· ZÏPZ/†²'ÊBó¶Ú±h³õȶ®Lz07{h0gܶ¾/ïG$@‰"@¡*Q$™ $¦OéŒ`îÿ  eè[Ÿ ™õKì-ÒoÏ=’'‘>s™5{=Îß›¦¥gd=Ò²¢WB@_eø}‡O ù-–ÁðÐÝ»ï¤F/R*;öTBŽ1ÍŒ*ç_RhsÖþ*àk•%a&¶ÖNÍÉú¯·hÙãØ–ù_ôÓ=‹Í SpîRïùdÙÏ»+K”£>ÉRžD”ÏÁPP/ÈŽ=//œùB"ò¬Kxw]…w×§uIË4$@$ŒhIJÆVa™Z<ŒÂ^. Fãëèqx<I åß•õº6ü'A‘‰d™²n©.ð¦M›æÛËS(ôxj]òM#JŸLC‹oŒc[Y‡;ùÚzZ<IÎM Y i«1î_cžÚ>»ÆóqN ·ú¶•´oœ¬¶[T^Îè¹è§K´²Ï‘i¤5FÊ_[‘óÒ¯jʧ±ÎÉ}C¡i)[›ÿÖ>Guá[&Xõn–8[E'^s÷ÝÎÔÓØ4õ9–¾X[LÏ·¬q»Ö'ßD¦mHߨƵ•ý¶ñãÛm¯¾Z[ÙxžH zÛå¦úâð €üHOÿvÞ¯øQÝEÆÐüœ,Y^cHÏ ?¥ê|LÅ{0/œ=›XÖßÀÙÃ×ð€–‡C¾²îâ@<übIþÓ‹¦åådÞ[ÝÂÿôÐØ=•Í…x{¨S&¥ pݧFÀwK<%¤ü~sq¿¡(ׅНFÙC:@—+ж~ôʮm1¹ÔÓu^G9ûú´ÿ²É9cæH\l¸&”»Ô´¦¡|‹óÃÁÁP0o†Ó„{eº—Ô96}ì1ÊFܘŽwXÿ§Êù¬ð+(w”ÿ1äWÅR74·àœ­}Ss2?’ë˜ê——ÜK¸ æœUê.HªGËy”mòÄÊ2^uÁÔpŠî–€ëûâ( Y–DÕYƒ.€&üNÿá¡ÐRI >âD{X®|òs‡`¹Õß}òî <ùëmiwÞ¹VÒ7f‡§5I†qI¬%ɽ/Ê»eÚ1%àÛcb(sžÄ—±2¾Êg »&ûîƒM;zTþÓÑgv§+ÁüI÷úkBwï5MôEu0þú¢M6ÚÚþÖÐÆÓy9Yùnº¡·çž¦-ë~ðûdjnð*7>v[ÑvÊwÛÔpæ+eχ• ¦ñ=›>ãöðY–¥F¢|â\þ¾ƒº÷FßÑw.3gÍÀsu¤¡[õçôa«ž#U7.±å”c·=RÛøv‚s²¼ÐŸÄ3relz™þYªìßÀû=ð>9ö¼»6ýê<ôÁåç§ÇÑ÷0õ·,ˆ£e®B?×òÌw€KÖ£U8‚0 =}TÚ.¡Ï›Ü½®}CÒº¡®}¯ü9k‹ºVrÜPÎ%\ŽÅ#út^nPÞ%Jâ ¢c¯T¶5ŒöB¿HÃCÄúw¿¥ ~~ÎèïÜrpK$œjÍKÎ"³T$м |ðãÂÞåÊÈúî¾£ž¨Sm}ÚT,[ŒM¡¦þNšÎòð8Är„—çñ£=ñø·îÁš¦WÒóò* ’LÑ‚ô„M™>öF¥ó ®ÏËŠD¿ð‚¤óç~Ú> R&öŸEÚN ^ƒ ñ ŽÓ­HÉ'uM×ÊøÆ¦m]὇wߌÚWH”ëó²x«Lø2ÔÁ²†ºý"×C 9Ë›¯ì‹ðˆòžU–¿}eìyQh¡ ]„ø¾©¾¶_»ç’gy°´Þu_!ôO'J«y˜¾•?øÜûÙM'Û¡Ù¹bqù÷¼mÔ ÜÞDºÐn­ñw€« y®ÙÅ0+jÙÿ…°³¤Çý_€2’†²ßPh~Z›UÆ“W£îJûË àqM„g'+(WŽß+jEßÅÉsQï¡ä}ª £‚(P梽.¸îiü}Š<÷µ,+k½^r„täªÎ!]w(——ÃiA×ò[UÚ\;vl'(;W"r—Úù>“–­vpú‚ÖU,²ÂØŠª—qÍÑèkÒÖc»öÈ› "³ßë« [ûÕ…Ku÷tã­B+­uš¾VúÞ+W »=w°{®.[±‚Drß@_…~'äó¿îÝÁê(/"_<âJµüÑmÓ«¬ý$F¯—>^ñgÙ(C⟷úô )•êÚ÷Üô±[8ÕåójÔéÇn£+Ö_!þA¬y|é1ص£˜‚ ^èÓjW¤¿Néh•þ›7I€¶?If H"øÄŠ€ßV{Q(4ÐqV[ñZþùÅQǸPvm• ìݵ¥¯‡¥é¤)áìÝÓ׆ÆöD¬7!äœi,Yu%⧺çDÁ°ÍÍãØðÔ1“CÁŠõú.°ìèsQ[çA¹Ú7?#Ã±Š¸×BÐí‰ò1”ïL¬ƒ¨ Eàúscég¸ß^Ë#Ñþ¾Škâíø}OjÓ !ýŘâ32–‡cu›;ïoËì'$ XoÞÍÈC±S‡êHñÌaÙc¯®éû&@Ê»‘H äѪ fÄP[<È8°Øj?áåZ@ä^Ó¿[pâÅ9Ĭš\]ççAòG 8~í!Z¥Ü—[ÕÅôðÐ=;G"¥°j¿¡í«óÂA´œ £Ó«Ô„Ücw+÷‡Ú1FûôYùw_wã·°V{•¬Þp âpÏm­LÁ²­ˆXVL ݶÂ[4Þn¦*}  ßéСÕ?î½õÖBïyábš%¢ŒóòÙ/¹çEÙ‰Z¢ŒŸ+Æmˆå‡2Š`I™6ÿ(Ú eS©Ýôî¶t³åÖ@èÿï=£Fmtããm3Bc÷¶Íè]h—Mh—³+=CÙãö)µÍWq¯]ã]Ûç¨6.ñîgë@ÚƒY££ïÝ„sOàý’¾±omuvóY¿±t ö£mþ/u§öW9ŽI1"tÇ"³õ¶ÏÅúI±`>YîAoˆ¬•´£Ökà;3?7û27/Ù–õñÄ=oõíÞ²ÈþÖ2Æ;f‚8ÊDŸx»G ?ÞOeïj饅ÑëЯִñ§õ÷Zqo™0¡ÍÆu%cíÛŒØrð˜H ùÐ’”|mµp¶eí^Ž`q]QtR·-ƒð …Ên·ÓÝâ^§íG¼Â¤S•n”}X¡2E—}'˜›3ËpýœWA’sP|ÄåÇîoü¾zHÙ1ÿk=É« ÉYG0Óú²oi%SÉj ù¡Ñ¿C0Æbz»Ë ó“Sc¿ÿýü“!wƒÅ`¦Â,”ó¢m#Uÿ<>ù=a˜%ÊIzvîÕñ¬We;F»•ÝU„aï= žag3ó{$>bB÷œ?Qa]xÛ½Õ»¦¹]Ú‚]v…‚$ʪX彪Òe[—èçm«ú†· [ÁVÅl(H·ˆ5^ºœçµîÚ¥ö.åÙêU$Nú6¦äQAòòç> $1-Q’E#–EÀ(³ØúºÖ»Ü ³îÄQq§)¿ñB¼ü°ÖçuüØ/ƒBÑgU$·‡'Í™²ïÓF%aÝ=©pË~TÛû¹qÞ­Où¦yÝ}¿O9Â<æçÄywÓ¹[¼¤Äš'~Ñ*Â.¦˜•ÅÙÚIã^3%,èîßã8XήFÝ~BÝò9.¨—ˆ€»ðÜ0Ô;r-F¿¥GöešÊx4ÀY)­5” ¨BÊ>WιÖ«“dß2| Q’ tRž÷D÷uÙj¿®‘5 ^'Öu¹Wmi´mÆ7Ëô4(§§Ão+3ò+Ú`_(2¯w÷‡aÜ) nÜsÊ.['ãóWU°pÁÙ™«ÑÎÏCH‹D"ÇKX‹„þ©1­Q9Sù¼ùŠwC´åШœüÈ{.Þ> uœÄ~\…*µkûiPÊ­x×"®AϦÖÀ¥š;V­þt”s zÍ5Ùc­&YE´œ(LÁöƒÉ¡1‹*N”ïôðžƒsbÂî{Ó}÷a:hÝBbŸ·ú÷*¥¬cô§}1½ðZLQ¾ ¿Hõw8S¬–ÞüvRG}~¾¯‹Á˜Æ|ƒX»½ç¹O$Ðtpº]Ói+–´…€¬õ{¹¤Ø­®UvÖ-YåÌso¥ZÿÿºVqãE0ÅÂä…¸¦»m}°]R>­NYøÓpëæY6-Ý\ËW¥$)©ÖÒJå–™²F©~N‰w>6®[ Ç‹æÒuˆ?;}ܸù£G¯—42ueýº’sEèkß¡Õó±×•îbŠ–ýØ5Ùá°” ÃMÏkHNÇ÷zWL‘Ã4/œ»Å¶”(I25N«s ÂЦÞ! ¾Dô¢<É´"YçS¼jýѨÿšî",ª1rÙV‡ù·¿ôA&‰_ëãž<5 —Ä»±Ëelï¼7œtú¹çyãdßY~õùX1˜]yåÁÖQ´ÑCûÆMÉóbuŠPŠá—u>U‚(´pŠÐ[„ñ¼;F-u±Jš²=_:$Ù~n(¹ãYº;ªJE™ÎtãMÓ,+Ÿ­ž¨®è›I€¶?cû% ¨DÀÖ¢°Hè]¶©ýÿ”%«z:=gÇNñp¯î®E#n€ ³JN@Øq³™?þÚ ûeŽl=÷ãýíŽÓ?1§?/Ó"ÝO@…®*Æýÿ‹Â¥êM‘ Ü,7n(ý JÜç¦Å›¢å¦!S¤ÞÇt£!†_ aw%®;¢Ä\rÓt3ºÏ„^ ‰ïxwÊ!”ä¯TÀR/–¥3^Æ›‹ÕYr\òÇÆ£¤L¸nz¹%¯,ÙVþÿáÜy²Þú¬¬o~G÷ë÷ÇVÞ¶â²çž{ÎPQûùØ¿¨eÝQ‘¨;PˆráÍâ$ôc}ÿ®=G§b*äa˜zùBM IgUy’{«5êó… úèêš®7”µÂ¹F«^îµ)ÀShoñê2QÝxÄAIÒ6´ô'·ÄU³·üÏnÒî8»ª¦ûã<úUåˆç¨:.•ïT÷#¬Ù›„>;uêW¸¶ðN¹2ðž 4šlÝ€w€ûO‚ñÞâ¢XÞP ê4è!y'êykHßpë(Ûº0˜;j?‹äðµbwˆl¶*­µòæçLÉôàÙÒ˜ŒnÖésñA_(¨á'ªíÍ€û$@IA€–¤¤h‚¶ЭÕB%c»XÌ.Ö’š÷*Sùö‘ i¸f»]ø¸ â–ÇÆ;Çø!‡À)Ér”%ûÀµ&‚»Ê…¬©w¸ãe‡Bõ¾,î¾€z<QÃP ±<*‰°&ÀµV<÷¢8‘SBÁÏ3‚9W‹™xŒ%ÈYS!Š,3QßSVDÇ €Åj‘Úhž‘qÎÄpp±dо—Jm n¹-Y—ô/ÝbuBûmÙ64˜.kTøµ½C}óºð !£6,HïÎͽ56¸mþ=6®¦c[ßOÉÉz¿¦4ñÎU×_d½Ý2ÞA³S¼ëܸ¨2`Iµ¤=Ê”%œx$4jFñßBp¬‰Ç#êÃkBá#̈Ý‚ý{sËÚÖÍ#Þ¶}ßšõë"òxtŒwÞƒ.Þ÷Q>¿ède!Ùž#)•(zp@ru©O#3Bái©è’¢Hy¡=(¼«¥N!¼îõœjÐn¢ž·†ô oªë{Þ4à†ãÙ†aÜaÛÑéxþ' ;û“©9cÄK• V;D‡“ŒQë6E®@k®PEêtð?Æë¦ÊÅŒ H 2:Æ@$D&ge-ƒ(#. Ó6¬/^—¢E•u³¤Ã€w¹Õ#ÎUåX‰âœ‘¨]x¿ÿ7ÙÊÈRKP†¶×‡Æ÷tÎmÇÿD¹Pû#„ºãda~¹[ç“!Ä͇ð6«>EKñwøXÒÆ)T•¦ÜAÕ´­ã  ‚PKšñ?7ÅDY ¯ôŒ(ŸàLÁ²írN§ÊT½†YÛ€:.—vªêÅ®áw¨9ž±ŽçÞØ?(<Ój¾²qÏ–YÕôWèÜ©âH º»a}Xçœ-Óî*…ÇåÈRÆÅ²…²íXðãçÄK\M¡lBËTº6âÑ­†´=ä\Ô„­<$Ûsä–ËqØ¢Œêä³Mû1#Ò:ŽŠ„¾mÛóœk´ÚÓ½6Û?o èõ¨ŽŽÂ*z:¾)÷ æà^‹þ˜¢­è´Ú>Ð+TÄ»ewÿ½¡ÁcZ°Ý¥;ëqc&%ØN¨$m'ð¼- TG@„Uço‘óy¼µ6Ùùî‰mŸ…á÷þî÷W—¯2­³ã“oA0ï…ëWí¬Žð¬#ÒŸHú³4=ÞuÛ:Újm]bšnÞlaÚíÃáõ-Gi´ÈQ!ÉBéÙü2e‚᱈=UÎø•¿’³ ,8IG]ðÇ ˜þu”šïÅIÄ–\jÙ“)}†6«öeÝÁ•^![—€íŒÖGMŒÈÇ bq…Ò{ú°¥ÆgÞ$­º¶ÇÇ€œšXç‡BÓRÐ.D»­oÝ)­RÛz¯‰³ÿÄ››.ŒsN …å6Þ9¨Iõ¹e)°rWÂÂ7ÈIc×s=’¡Ÿ…†ôWp‚½q¬Ý1vÊg9÷‚÷·¯Úgï¨ï›¸ÏTû`­ÍaÅæú.8®÷š%)kSyáÛægd= nÜß³Mk6úðs`¥ÖÞ ž Oâs€ž~J0^=¡|>µÌëÐÆ@àmô±x骋Çh—ièCàÎü ÅÏá¹X…½Ïº Y£¶ f&ò+d¥lÏ‘·py9£çÍÇGÒʹ¡GÆ„”4cL¤(*–²3ÍMÑŸð‘Þéè‹2í´£óLý¾z`©vžƒ‹ÜK'Ý1úW0úï–ÃðÁÙ/ñ,>†A‰BŸ²¾–n:¶+Ê'„S8*p¾ùóS~Nf¥)yuáˆu%£ ‹ œw\ eü~Xaå9øRµóƒgñ£êòI¾çhKI{øú爂¹%¦òž|¬Wí²Ó¡pàqÞè·6<ª;Êž)}â~Æ·Ô*=+2eدCŸýmÕÏb.tÀº°ÞÜ‘OBž·†ô oyê»?%”õ=ªz=êáGŸxV¬H>`¿Œ¼Žr>8kÙË”P[¼~äóë“òCY_Õ÷^LO$°í à÷†H ©¸~ìØ.¥›î¶o‡ùñ”šxõÀøŸøqîk…O„I#Óäì€/0ñŽÑ ݸx×zãä›/ý¸@ž4CùW·÷®+eÊŽ7M2íË4¨¢¥£Ú¿#Vy™þŽ‹ëʬ±ë!ßX²Vínù¢¾Ôö­çW7õK>ÐY¼as¿TÕziu®Ý»¬É˜¿<%¥ö.­ª`{|{fX8ÜÃ2vÝÕ˜yõu×ÞÔž£Øöw¦Ü©Ý,Û·¡C;{…8&ˆMã=–©¯øNUOè«Ê=¾yO'|{÷ ·BÎ3¾rÃÎf@uñ+cCDuZš,﷌ܒ @‹& JÒЬ èbµ  @­8Ý®VDL@$@$@$@$@$Ð’PIjI­Íº’ ÔJ€JR­ˆ˜€H€H€H€H€H %  ð–ÔÚ¬k‹$ÏUŸÂÛ6ù–Q‹ÌJ“ ÔDÀWÓIž# íMÀ¶m½.%¥ë!GžØù‹™ï¯ßÞåáý“‹ÀÐìÜ¿rÜÀ¼93>˜ZŸ’Ýtß}­÷?ô˜ÞÏ:½ôãwßÔçÚæ’6={ìáŸxZÉœß)ªkF„îïxÀ±ÇüåŒ÷–Ôõ¦# ¦HÀhŠ…f™I€š?›B÷í˜Ì}$=;¼¶¨ÈZQªìßÒ³Â Í ¿%‚qó'°mk ÙMó÷ÀV]AêÀºÒʸ=|Nz0çãMk ‹l[-X¿®d=Ž¿EßzôºÐøîuͧ©¥‹mßP迲¬”¹9³>uÙ)¼.jY3ÓÇëà½.6ï9î“ @S$Ð4›"i–™H În?¾]¡Yø–²í¿k¥6´ï @ik£´½Ñ€q©Î™1a˜áÅׇÆ÷¬Sâ&š(=;÷RÛ²_°•2 ­þî èà m A¿zUêÐYݶ¢‰V­ÆbgsþQ`ææ{…BM¨Å#µ<é¯mßÐç 8Ü’?zt%«nKè?µ±áy æEÀß¼ªÃÚ 4u¡Ðã©Ë6¼†zìâ׃'…Gýä©Ó§ØÐsÌÝ–}÷QËlÖ ,HgÙ–õ$¢zø²n …´UŽîsl_(Û&€fòe¥ðl”julɦæ'ÅÆÕv<9<ꤑ¿ŠÐúOEe¹C$ÐbPIj1MÍŠ’@Ó P`- Òq†adLÊ©¤ Å­@z(/ÍŽ®ÎÈ;V¦>ÊÖó´ÖÓºù3'xa…éTWØÚ>]û[_k›ÅieTJB€üPû»Ü ÔÆ4;R<–«®½¶õ'þ€oĤИîËòPƒÛúÓ®ƒ¥ëa\{îÀùO}H;%4æ77­lë\¶`Χ5t'ÔãL;åXƒ,^êØ>pû=£FmŒÍWEVÇý!]W¥Õ\Ÿ6‚“s2gºé*êÛ6®6š÷!þðéŒ:o(;{J8û}I;,˜3Բͻd¿$ySÏ"¨Óù¹Á%ǃ”2ÎSÊ>ÙVvkÜïsœÿ$/œ5œQ„ª!#xO?KE®×6k»Ò¯UZ¿íkcŒ™i¥\Cƒ9'£½²@ù@”c5êüjwÏÌPèªbo¹Ñ†'XH‡6Úñ©øûEkßvªý›*²§¢?Žò¯Ãýs®³uÚ÷9)”ƧDYÂþËèóKórƒ×zó–}ô¥ñLì>5œ}†Ó¯”™Î:@Ú¿ºþƒËÖÚZ­¦WÉ/;<^Ùê üpð”Øs<& d!€ße H"QûP³›Oگߣµ•*š–bGV¢,õWáO†þ›6ÔkpöpGA$÷ùJ×kÝ‚â™Xƒ1ÂäïZù®‡ðø.ÐKmsÕ³PÞƒÐW¢µq+¶ÿ‚Px¤išÏWZkáäaŸ±É,œAýeèÊPwi­²"Ñ/‡‡îÙÙ½g½Ê¦Ô>0kÜ£¢j,Ê6 /æ¡Ì?Æßuë6šÜ´Ý”á\?´À,xÝ{ÿŒìÜËÁá=ÄYh“;ÐF7£¬ßkŸ^к×>…ºË4Âo¤måýê«ò<öA¾²¦KÂ\(bÿÇ e‡eÿËÔWì Eº¹N úU¹2æÖÐ>Ç@ÄUÞgB.K1Ú Š®úÞÉ€ÿ‘ @’ %)I†Å"–JÝaN 2²|Ía™9ÿZ( ûb„_Ïÿ‹Ã‚c¿ˆÚÑW3‚cOÏ g¾áæ! Ø´©¹A(#Nx£õiØþ!p¬#î(úóR7ZÊÎYnNè‹”óËÓC¾Sí!xþ//¼Ãºû (IóL³äÄÉŸªoÙpÉ^)mŒ>33Å‚$áYŒÚw€õæRìsbðŸ½tõPü€?`8)”õMyü£Cƒáß°<ŽË¬’õÅÚ›7P¯Ìòt*#žnEÔ¥ÿ‚¸û¦æd~GÝ”eßÐÊŸòêáQKÝ´hX2ÔÚþàUëËcîùê¶°F<‹sòç†ÿóN°TœéFx¶µÖ[œØ›"cpÍS°L\í^ þ¹ÂH!ÞšÃr5opl…µmójN‰Æ›0¡Í†u%l¥ŸÅ½„½àèàßËÌY³,ËzJø1Jb­upóÁ¶Ö´×?ôP«’U& _¾ë͹å×>µE_âÞ¹ýÚ±TFV߃²¾ ËÎùž2ýËs¿i°Ý…4‹¦äd•)Þ“žÝ'Á({³¹é|Ä=êÆ¯Û9O,ˆ>•ò¤çÝV×nÌÍýls‘ ›fä2¤ÿ§{Írsù™ò :n~n:nI€H`{€\À@$@ID@«=¡È,¯[‰ì à!ü=‚ä\6%œ ‹^ Èűùhòœ7‚ûûrlût%ËFçx­Jvñ¦—}ÃüÇ'Óì0 ÿFþOÛ_ß²é×= ’“ À·Q²vâéÏÍ–¯KÄšâQÊÒ*±:¨ƒEÀvÓÊÖöûžòO¹#k,_ÀìÐÇoÖ¶o!Ðv^ÉáxC‹—¨Žq°D}a»ƒ(!•/©½Þz“u4†Ž>íË÷^û@è¦u˜jøš7.î~Tï)ñ¨w­ýjãÆÒ£PÎn¸ß#Þ¼G¶ž¢‡ ¿sÜ®ÞsȹNmWvMíiKÿØx ”¡]`1{Ü{»µ!mŒn²5W9eÅùINÕ;䇳"YxžD©Ù´ºÜ>+_‹´%¾–½³²VÂbõ*4õ+½I-Û‚â©¿í¿Þ4Ü' d @KR2´Ë@$PAê2(.Ý+"jØß‚Ý Õ$õÇÞ=öœíïPàƒ º! ›•Ïò/óÆÃóÙ†h¥Q*Å/û©;~3 5ß²”X^œPß²A­úÝmü©lK•(³¢ (ÑnP"[Ã2ó£{/ÙB¨Eʈ®-¥nÄIhß&P)_¦Óƒáõ:N½Ê®Øò?¬pÏà ‘‹ºŒ]™=î×óSÛ“ÎÌ\½%UÕ=ùQÑÚ‹a½ˆòöA{vBù:KJò* ÎÕ¥Þ¶Šövîâ  M+çz4RMAÛ–Ó¶°ÕÞ¯l[,‡ÊhUÁÐÍ[Æ<‹›¢¦)ýjQE|ÛNÒ×¥¾X µ›¤Å­îA;ß-ûìÍXy¤aó³•ÃSçvƒÒ²¦Ì+KÑÀÿµXw¬¼aáp)Á`¬A]‹Šì“`9½nkrFÔHûMX/É ¿t܆o4OG%²¶&?^C$@Û’@¥«mycÞ‹H€â€°#Ð}¦M›æ‹w¾JœmGªÄI„­ÄA%«ŠDçÝ‘¾Y¶±!ªJÍØ¸êŽ}Ñh•w'FàÛ@(ôCÀ¦<Ô¯lEîe5m¡p´õd5ÜSéÏУ±xå*3UWR`&ÜrKò­îž°01IqWL½{QRýMÖìT—>=toçMkо%î.¨ó å“õM¸¶b W̵u(Ÿ!Δ•-‰¹VtÈÂØ¸*Ç~¿£ÐÀIA¥¹JÚòÝʬگ|e}MUúUêPq§ZÓÂÊçXÛ Ï>T©ÑæPŒþuGS%7( i²MQbÙ64´òµ›†g¦Ä*ÖŽvs±}ÚÍ4Ò|Þ©“u¾M7ð(u¿ƒÚr‘Þ9϶ÏïG} íˆ 'Õ¢¾„Y5­ªeõ\†iËÑojl[OrqàpH™gCqPa<á9WÍnõý' Ôã°ˆuzïÛçà‰°Œ=YM&Œ& ¤"@KRR5 C$ £ü·ç³,ûy[•|…‘è ʧ¿ðÛÆ*ËŠö‡Ûk8vð½4%gÌ×:¶ú'<‘e››ß‚¥b„?²À²"¬¨9 ÂãÏÝ|Gÿ+ÑD‘o.Çoƒëåµ)>ÿû‘¨ÕÅ2K°p濯býH£•­m Ó–þŠ÷¼/g£an€ëæ’]aÞ8Ò|˜ªoOÙo÷Yï~ûËZ¸Ø¾Ê×ÍF /wòM™Z§3Ú¨ÔUÅÑâ}LÛ¡÷Ö;´ú9Þ}pØÔ•Ë̳ÒCã7,óàésç]å(böØŠ017¸ýàßPÇÀß²V>ÿ[¥Q»Ó23î®U§ºd¹³/ó©+÷\Û²G£í–ûLT?Úꯀpì`¤Á àø‡o¸¡÷¸ éžBýW§hû¹¨JI±täjôÉó1Ýí¯ñ,hu)C]ÓL ݺ*#˜›ƒÅGw£ ¥F@azš.²MëP[ÚYû&æçŒþNÖ Áòó¬©#¡ÔlÐý|ºwÀu‡¡œÊ3"÷Dß|}v¼>žiµ5f¶)Nñݹ6^yºûƒÓ—Er Àú(V+NÚ÷·óâ%ôÄU×$ɤÜଫz æLDÝèÒîeÏ¥Ü% ¤%@KRÒ6 F-—¦i½—êOÙÞÁßu*j½aZæ—˜æ•ð Œt;kTî½õÖÂÖiŽ[é_!Ô½‰”,ZÖKÈ¿´1Žd‰¦9¿ØgøÎ‚ùÑÅ‘Ò%QË”éP=0î¤üPf…âÐXeË=z½‘ªÁ¨üø°êÓ–]2JÉÛðä7rkëê¸[Ç÷¡ÀpïReÿ†Ê:Ó¡ Eq\;ýiS¤piEgàÛ7m ¿>ãþ‘#ã®íêî?& ýï…Âöw;Y çÏ@qZd¨”S¶¶|r]@k ÈüÂöÄ’Him™Ÿ@üß„©oWÖ%_™ú×ïçÂùÂPôŸnPxò•e~Åð-¸C¸‡ ’×Ôœ¬ÿ¢Ì×Àjvq‰­švéÏ–­.„²~A^NÖÓu¹_CÓˆÂfhûh“óà@ä³hÄúex SÖ¶KWY€Rs´ ùèíHIƒþ?ì30 à¬k’T~_Êåù@ŸfoŒ¬+Œ]]~y•ãêÝPÿFŸ’ç¸â¯®ÿ¸™cPcªXñ´2ž%Ôç–H€’™ŠH€H ¹ \?vl»Ôx$4ÊñR¯´2õju$w—ãô_ZÁ.^µÅ¥so†Òpo~8ˆÙ[Ú×ÜfJ±¯6ooU6™Æ6ýÛ_ú¨ÖFÑÉýú­JD½ñͧ>†jÿǤе›„‡”ýÏÀØ.fDw0TÛΔ»Ú@•Ÿ‡%©×É{ïVˆr¹·Wä+Ô§=eQCò•òú–¯Ý¥«ÚyY(tU…ÒáÞÇÝ^;vl'8R°¶v]Ž›OC¶ÒÏ ÍÂ:¶KYuϨQ«ËëºÐøîZµÞT]ɇa 7DvË\uy44>¶ÿH~°v EÞ0|GåådB¹e    h6DIš•ƒ%-vͦv¬ 4L·{Ó¿kœÜ™+ 4®Ij®Ì•H€H€Z4PèñÔå‘‚á˜"{:¦­\Ü¢a°ò$@MŽ•¤&×d,0 $78Ú˜»Ì\º;ÖšPróÂÁ­úÖRr×’¥#hΨ$5çÖeÝH€J@«À+Z›¿Êz¤„fÌÌH ™€s“+mmí˜Ò¹Ý,:khfËê @ãЛ}Yî×dß}pÔŠÆQo[ÛÝqÓvÛâ¾¼ @Ó'`+µQÛzj²ØgøÞžœ3fNc֪є¤PèñÔ‚HÁuJÛ7*[õÔZYÊÖ+¡$-ÃM75f¥˜7 @ó!%©-”¤îÐ-ºÚ¶2”VK¡[<Ø#Ðã‘PèªâD×´Q”¤ô`Î t**ÐKkýþž1ÒŒ×'óg¢+ÀüH€H€H€H€H€Zkî¾{«È:öíËðw* 1¿£æCóÃÙï$’@•¤ôìðÛR÷jmmÜàŒD˜y‘ @FVø8K©‡Abm¨[òs‚$ŠŠ/QI>e ’}¿Vú*°ÓYù9#$2æE$@$@$@$@$@B`ÎÌ÷rÒEOi«hX•FrÂIëçÌxÿÓDÐI˜%I¦ØÙ¶~¾κSì0uH€H€H€H€H€$Ì}ÊÇyPANOÄÔ;#Å' ÈgªL±S.WRAJUæA$@$@$@$@$PG÷€‚t?àoj¹nRÛe5žOˆ’$^ì' Xƒ”Ê(ªñŽêiln<¦Õ½/û¶OW²<ÁÍœ¯UÉ.nÚòíɉ øõ$(w­­HÉñ1it+×% ñGAròƒ¢÷v¾þ¡‡ZÕv­Z9¿uÚ¸ºMkXÒî¯îšKB=ºAi|Ä›&h‚óDP:løãv•sÈëL´Óyá ɹ$ÅgüÛ{­“6õˆÍ“Ç$@$@G€–¤ÆcËœI€H Þ´m9n½a±è^—‹aê eè…jÒºaï{Îöw(ðÆ>µ!UÊgù+¹‡G½ ÑT¥R¼é¡ü^éimRæ¯_W,i{ÇžkÈ1î¾¢Ö°ýèÍÖœqlD׊·À{®ºýüÐèßáE/ÓýÂö|Ãè-±)@¬¶-V3å ´ª’§6ŒõM?ö@IDATy6ŒEMS˜.Âêªÿ'é½!¥}ê’Ò5•?g•ÈzxïÅ}  Æ!@%©q¸2W Ø:~ÿ"1á{AWQnªÍж#qÏÙ ñºŠ¥%ïŽôÍù¡Œ*—DU©Y%2N&‘U™…)-m+Iq®²Bçz(%xÑñâ`åjƒL¿€£†g*wf²ie¦êzyÄëæNÄZ­Ka%›çûš‘?ã²Ó­Ìªñ>pÆ…Xä2ME]J*• ÷ÝtSqz0jç–èzlÉ™{$@$@A€JRcPež$@$°•NÜ»ï’éßÎ[áûj,ö¿7?#£ª°îÍ[Ûs0 ,¾õFÛ½ÄÓ7y"öa‰é›Ïæ"û8‰ƒö´X¶QØ¥d åÀU(ä°,hݽ’õƳ…ó„Ècó”pÖqN×;J¼ý]›=îêR;òUÄ\7†$˜¿ËÉËR¾9ø4•²Št/DüY馇J Ë㥮=*¥ÁÁ5wÞ+Ž)|ŽÊX~2Ñõˆ½'I€H€K Êh`b³gn$@$@õ!0dȨ¡õí2=K-]}¦•m‘àãedëÏ`œtöžNÏ·f‘ €§»÷½ñ‰Ø‡ëðSÄ«ž7/”á¬Ï±Œ€ï3‰Ÿºv¦É­‡Wì¼éÊ<åYƒ¼q²G Ë‘‡L«'^æN¹6+_¬”ºnsFÿÆãPÜk×ñ²ç^™Ú¥Í·°\CQºÜ«ØÚÖ嘞·¤›Ú½l*ØãÜ qöP‘;V´ôdï±ì7F=bïÁc  Ä %)q,™ $„Àξ̧ ¬Üsá‰mtFvî!ø®Ð3Qe@°·:£;iðB'žÝT‡Ž­þ olgÙææ·ð¤þ@ÊËŠ °¢æ$()?wóý¯„ªr&©‘yÓ†eß}§ßg¬„÷¸³àïj(ÿñ~S —L‡•æt¸aø|/ÙQÝ¡ÀÌ…t;TÎJ„V/c*Û8xå;ÓjkÌlSœâ»?4r­jÈÕ›"ŇŠÞƒ§¾ÑJùçÚªdW08êˆ^ëB±yÕ帛¯nùË…PÏãq¹îán(š{Ø?ž«S´ý\T¥¤X:rµeÙçÃ=ù_]×ìø†Ôx3½bÃú’†‡rGÊ¿*bF²më.°ß\©TJ÷à @ÂÐ’”0”̈H€C@¦„ÁE÷¹p0¥nÎó•e~n[Ö[–Ò7C!qœ ÈÝî½õÖÂÖiúLìþ ‹ËË‘HÉò¨e½$ëxmŒcÄ+[bJµ%­Œ±†¶‹ZÑ™%‘Ò¸=xŠÆ“Ýý=á®{K0Z«qnŽû£óWË2gÂMÂjCù®Ú’ªlÏïKù¯”ß²­iöÆÈºÂHÑÕr&ôèõFª>ÊÌP>ž¶ì’y°½ ¯}#có¨Ï±£èØ÷@‰bÂÔœ¬ÿbÞ58uq‰­švéϘbx¡òé òr²žv“‹Bè3Œ“PžÝ"ëë2Ѱ­}— Ó¹n:Ù6V=¼÷à> @â`@¯aß¶øPrÀhÞ ²e  H,ù©oùÚ]ºª—…BWa*Xü SÙVGrw9~@ÿ¥2m/~ª†Åâ¿9üS,8r¿j\¯ÕKkRÆà ¡­a®íÒÙ\, `M%¸müøv…";NÌ .ŽM7mÚ4ßôoé£ZE'÷ë·ª±êè½ïµcÇv²´¶&Sy}’7öeºceXŽõ+æ\ìáö¨GlxL$@Í™@Rè'R· Í6ëF$@$eJRFvøN²   d%ý„Óí’µuY.     íB€JÒvÁΛ’ $+z·KÖ–a¹H€H ØÊw™ßöÍO¢±H$@$@$0T’†’‘ @ó'05œùJó¯%kH$@$ÐÒ pº]Kï¬? @%T’*áà @K'@%©¥÷ÖŸH€H€H€H€H *I•pð€H€H€H€H€H ¥ ’ÔÒ{ëO$@$@$@$@$P‰•¤J8x@$@$@$@$@$ÐÒ PIjé=€õ'    ¨D€JR%<     héšìÇd¯É¾û਌ìmk»»Vª]KkL[©ÚÖË”mÿîóùßšœ3fNKaà´Ô”òÃÙ'ȶ1C(ôxjA¤à:ÃÐ#-Ëꦵ¶Û¥¥ZÛ§éÔ@ ÅYÅŠ#k݆"{cQ±aÛ¶6 c¹iFh_ÜöáûïYŒ¶@?m6AßtÓ}©R7]ï÷ûF°ý›M»²"IB Þû$µîï™ÒóáPè*yŸ4§À÷IsjMÖ%) Ä{§X–}_@GšÛ;…òiå.¯í·µ|šý¤É(I¨ì CE-«Ç¾»÷´Ýg7½ßî=TZj«Ê-ÓŠŠKÔw Ô?,²¿_°Tû cYqñæ¡OL¿ þš²²$}Ô¸òÖààÔÔÖSÑþÝÙþ-°“³ÊÛŒ@¼÷I$ùÇ£ãBò>iÊïaÈ÷É6ëI¼ ”ˆóN)°lëï\§90¢|Z}+Æiûm&Ÿ¶% ½ †’ =»vT :Üׯ×ÎÕ·H ?3ÿ÷ê¿o-Xõ§ŽF#£w×@ÅŸ(KM-ˆuÐ÷÷Ñ·ðùãzvÝÁfû7µ&dy›2-ï“u¨†uÛÔÜÛå}Òß%Ò |ŸØŽ¼ïLºŠÒýÛ±8 ¾5åÓº#ÜÒöÛF>M„’ä«{õâ§<ø¸¯”3sf|ð„ltYT÷²×®zø“vlŸà[4¯ì:uh«ŽÜ¿Ÿ±zíFµb͆Aûytá7³g|ÑÄj)£½Ò7WÞ¼ÉïOöWlÿ&ÖŠ,n“'°å}²A/ûcÝàƒŽ=aãW3?ü¼‰UŒï“&Ö`,nó%Pé²zÝ©‡âÈŸ5ÁS>­g£miûm#Ÿ&B?Iæu<ú£CƒÅ‚Ù¾ú¼ãuJ Á:]=›´i&NÂK¸­Æ^rÝͧ¡&üI{‹ÀÌAÊ'å H¹¥ülÿdn.–­¹ð¾O´6î¹â–ÌSQç¦ð.‘¦áû¤¹wPÖ¯Éð¾SDÆY¯üYm*u¡|º•-åmû¦ Ÿ&­’”žj•)v;ë˜dì·²»4îe­{—Žª}ÇŽ“÷Ý÷𸛫(5î–»£ Iy¥Ü2ÅŽíß0 ¼šA@žÃ;í`§¦¦M=ùä Û"ϤýýðÔ—ï î’@2({§tT"ë‰Ì—Le«©,”Ok¢S·sME>MÖ9#º£¾^éËÑ<êO@¸]rê†e«÷?ñøk‘ƒx¹·ïIÛîååk%å•r³ýA„’€€¼O.|¸Ï²ín=ô¿EJæw‰“÷œ”‘ï¡Á@IFÀ}§ˆ¬'2Š—¬²‰—åS/­Üo*òi2vH±ùüßñbF' [ÙË/~±M»vÃ%#5)ø­3Ù¬sN»——¯µ”—í $DÜ÷I«Ö­¯C±’õ]"Äø>I¢~â@uÜwŠÈ|H“Œ²‰·è”O½4¸ï¶}2˧ɨ$—ß0êPøÒßYÜ|7° x9”sìrò_.:‡ÉjMªõ-/g¶?»/ $y.ñ]¶®ç^‘qJ—¬Ö$¾O’¯ë°D$—€¼SDæÙ ’Q.uËMùÔ%‘ m²Ë§ÉÖ-=Ð*e°|(V¾ƒÄÐpÂQxî¸Sד[*þ’mØ;ê›*ådû7¼Ý™ 4÷}Ò¶c‡S²½K¤Ê|Ÿ4FÃ3Oh$î;Ed?Ü"Y­I”O¡ýݶOVù4)•$´CŸvi©?›˜)Ûµne~/ä(SîÄC2½ˆ\¡FÊÕZÊ)åeûƒ $ç}‚÷³6|½Q´d{—-¾O’¬Ï°8$P÷‚4}ð—L²‰·Øî{…ò©—J÷“]>M6%IÊãS†îÖ±}Z²•­]aû^Þ±Cí øwB)¼–¤da\Öîe£Ò©RN)ïö%Ö|ï^RQ?ÿ¶<é+øËâjsIiÜrþ¸¨@Íøê—¸çÙøäý¬}ºîäZ’’å]"•çû¤ñ»@¥;Ôô¬VJ¸V®Ù Vü±>n Öm(R¯Ïš« 7—Ä=ÏÈÆ'àÈ|ýp'Q’’é}âV¾ì½BùÔåQ±™¢!!™åS™OžLA:¡ßPº]j Po!ù÷åkÔØÇ^U™ÿ8KõÚ¹ÓV×k>Ä1Mw·[G"/ð_/ÍP‡ïÛW]4H–Ô?OŸ6Äm¯¬IÁ&™Ö8í^^®VRέi\Ïà!ðâûsÔœŸ~SákÏ÷Ä*õâeêÑ—>RÜr© ø·Í+ kâÿÔšurÈÈQO<[ >¼Úg,µÔÃÿ}W]qÖ1êнw­T~9gâÇEËÕqíQå#Ÿ€<Ÿ†rÞ'Éö.‘Êó}Ò] ºß×ÚžÕF(Š’wÛÛŸ|çd-^²vlßVrÄ>êèúW{»Wg|­¢VTeœb•4k7ªW?úZ¼WÕ¦µüD2lkeïÝ÷M&ÙÄ‹¡Aò©d$¿ƒ‡îÝG;ð`o¾õÚ7£QõÓ¯ËÕ~»÷¬×u‘øã¹óÕ«3¾Qn(T;¶S'²—:ùð½ë}«d–O·„T7d¢ÉŸÏVv½¤ºÝ¢n©^Ÿùµê²cûj¸ºå’˜Toò½zsöÜ„³¶íðu…פ-¬íÄ”v«r©hw\-ý1¥¼œ[•/ªÀA{öVûÞ¼í$·DG觺·Zµvƒzñƒ9ꕾR×\x’{ºÒÖç3Ô}#/Q)dzEU*b‹?€óWI–w‰´ ß'Û¸gn¯g–uýE§¨EÅjö×óÔ³ï|®Ü£·J«Fɹêœc·ï/Ý6n—¦x»rÙ/™Þ'.ÆŠ÷Êö–OX¸L½ôᜤP’.Y¥N?f€Ú§oõé· ð»þ%ówUíÚÔÿ“WI*Ÿ:B©Û ’a+?º>ˆìÒ!þóæ'jÇmÕ²Uªï1òÜ·GuƱTŸî]œ¼_þð+õÕÏ¿©âSõíÙE]~úQêÃ9?«/~üUµJ ¨Oæ.ÀwAŽP{íÖM=õêlõ¦(†Vúí¢Î;ñ•Ú* æÎ[¢>Â5û÷ë©ÞB“Ú*Úôž#Ü¥‘¨z~úçê›y¿‹ógTü‚“uÌþO¿ñ±Z²rÚ Ù%§©vCùbòUkÕM— Vÿ÷Ög±§¶âØQ>Eê”—ü ïdeí^V&”oû*Éɤ1Ë }vÚ;Ÿ©»®9ÏéÏAŒn]pòaêÍç:#BïÙG3ð Õ}ùóïb¤h®Ê~žS¤¢â•û¯WÕåg¥ºìÐN=øŸwgêˆýúªÕnPýßtuöñˆGÅ*Uh×&UáãÀÎßoËV«ñR•a[I2.¨^xïKG‰Ê½îugÞKêÂSSìÑKm**QO¼2C-XºJõîÖYí„{{ƒŒhÍ‚ ´ÏKÛ֩γvú1û;#ËŸà>oÌúy«½wë®.;íÈj)ožÜ¯€óœ&Û»D Í÷ImM—ÀóòüzŸÕš~w7—ªð¿^Q—~$žÅ2ÇLb–D‡¶©Î;hÒ´÷œ‘«ÿr¼S‰ÏNWi©)êªsŽ«Rb¿á«ÈLMñ;¿ÛKWÿ©úã³R†ž]w„õz“š‰÷µ¤>ûn¡‚5çÝ%™½†pù½—÷ܱU¶@-Y±V½õñ·°V/sÊãÇ Í.˜2 ï(™²W—ßî*fDíÊd¿d’M¼e.{¯$H>­MvünþRõΧß;¿i» /ŸvôþN_”¾-¿eY<ï('—â÷LúñûŸÿ¨ÖoÚ¬öè½³“vWÈ“îoku¿ïÕɧ2“j¾þe±òû}êD n:b_/ gÿ¯g]wÜA{bàókµ¨à5 ÿ.ñußINùT=Y‚«©‹{Ù„(I ‹ÕK±n…èß Ô­…IPæKX°d¥ó¼ð”ÃÕ•g£zAð’¨ý¡‰¹]F¤$~וßçÃùN0ÓŸ ®ÄôŸ/~øÕQ®$ŸÒHÄ™úóéw‹œ—|o¤{hqIÙÍ—¡ñþÃ"Ç yÁI‡¨Ýwé*—©¿>[µN ¨/¬ztÙJÐ'N|ìòã B¡Lhh(×Ô]¡¦‚wCómàõÞr8e+/g³ååÕ>»fý&ŒE”eÿ™7g;Ó9Ï9þ G¨¸„âRi˦ÉÉ1ÚÆ¹V^®b^«” a.¿(òòì²wUI®]5ßà¥+Ód>sž‰=%Ú R†‰Ï¾‡g0E‡çD‚Ä•DLgÿ½ÏP V«sO8HõÇ€¿uJ¼ 5J¹ª“¥^‹±Î^äLï:Ÿð{¶ìu•äSÜ— ƒ‚2ˆ(ÞßÐê~ßãɧ’Ï÷ –8³£ºvj§äoçΜßùÁGÆÿÝ”5IbykS‡vi’ÅÖ†¤“O“q¿tÄ„…^;ïX‘W[($bF”Î#™˜)emÄŒ¯æA[ଗ¨HìÙY‘®ÿ¼ù©Z¸t¥êˆð'<áÄ.î ’„¶mÊ}–`´Þ„F/Ó„¼e4¿ü¶Â)ƒŒp»A~T–®Z§jR’Ü´ ܺzB97°Lry²–+UKþ,ÄZé¸ß¯°à¸qîV”oDz`Z¦È¢çšœœˆQ/Y°*žpž{÷ õÏ¿­î»ùbgPBòÜ·šï¢É¨šüX¹ÿ–A qÌ" „]!XÉ”™ç§áL±™+ñõŸâœûé×eŽ2u÷c¯;ÇÎÉÖ󷔬)î¹Ïm²•Ý-[{;´Œ÷7Ïû»¯(Q<ßÞ ÓŠÞÿâ'¬ÃõAèÚÇ{ªÒ~'L¥§4ò{.–ï)Ï¿¯Úàwô ½z;édÄ]¦ÍÇ 2X#S˜Üà-¯Äí)ö|ù“3mW¼hÊ@¦„íüÛ픡ü—ÌÏlÂËOv”6Î8 ,@«»¦¾„ÂÝœ)ªÕõg™15ç§Å˜Nj9ÖùÍ”?Y"!Þï{uòiỹñ`!¿üñ7çzùO¼CÆ 2kêL‹•%+gc&JƒØýk`V‰¹<•¤ÄÔ¬<™jW]ÏX‡ÀÓˆ¬¹EŸ}{vu¦ÕŦï³Õ’k0*u3²tïSoÆ&qF°c#e.§xÞ‰5S¦¶*+ÓˆKŽ_l>‰;NÌ4ÆÄ•§ºœšJ9«+Ó‹¯ÎA‚/YBä…*ýYÖüxƒÄOÿìG9’uwbMêKNMA^ô¢ÍÂ4™þæ¾À«ó´'/zY·'ë ܰSÜ kŽ9 ^ì+U+X’nÀô„¾=˦ÝÈAy‹õðç^ËmK À÷ɶnåê~w Ìà SÜ ï™F䆷?þk”ÒT)4§ã·W”¦š‚¬õO_ò[ûkÁª %IŽ« Ý:wTŸ`’d$ÜN=j5J×<Ì9£ãûí^6R¿ý~»½¥ã~s"PÝooŸîUæßφ¢²kà>Q;ÌJSÁZøØ°xùX:ò–n rf3}ðÅŽ<ë¨å‰ãÝ£:ùTfYÉlªÞ÷«Ù ŸüþOÄ:ÂöpÔð÷sàBléêsœœïédšŠÐ ¼õi I»tåŸZî(7‡•/2wµt1ïËÂrÑeÔ[^ íñÒ–¼L‹R]ƒLãû ¾ŒhËÁ\’»aÞt[ŒÖ¿¯uâYægé{Û)lSîq긽ï§HÍ'Ê„åçW,¤tÿÖÁÕm}B¿^Ý $)5 SðdªéGs*›HÜÊ·Œd.ôž}º«'_›í(Uñî!ëdA´¬7—º»ªNpì™ò*å~‹ZK°¾j¶ÊzD†F! Ïr2”¡Qà&K¦ò;æ¾Od¿>Aœ‰óù4L %Èû-4Éw:Ö^<ø0u>Ö&¾>s®ZŽ©Eñ‚‰uºò>‘QoY,¿£½ XÖ%ôï³³óÛ+_dSœ¾xƒX²vÇ@˱aª‘Xµ$$Ùo··ÈÍq?™žåmZéËâ@L¦”Êz$™2Þ ~D>¹QäR™þ¹v}‘/ƒï:DªËêŒxò©dv Öô~öÃBGFË’<籃ýb©Ê{áÄotÖËl±>Ugqr Yÿÿ¶)÷xÅ«ÞÌ/uãÇ ü5¾F)‚Óÿ½ý)Œnƺ Öj0}wíÔÞ©áñРÿï­OÕˆ{ŸQƒ÷Žãá­î‰Wf©'<íŒzŸ|Ø>j:F]ÂA‡«G_ü^ÀÞq’Ë:ý{9N þýúÇŽß|9!#åû ;¯šx]JTaÊ,g]§k;‘[wÛØ÷k1ù‹R4þ‰×*ê{ÖñVñ Wq2ÎŽ¬‹«˜óŸÇ9™2×±]Ù¨¯Œ`‰÷q­+óûeÿy/ªwaYŠç GùËϾ}{ÂÑÉ¡p^’R­Rå-ŽŒjÉ:@ùš”Iž#ñ<%A¦ºSdTY^ÞâheÔ•g8ŽXγñºóÜ»Ÿ;Óg¤l®ï=¸_Oe^žÜgV¶É¼eJ¦r% Ÿ•Ã;M¼;œe=»^ù †•æ•¿V£šæ<‡î ¥<ÇO½6Ëù}”5Ä#ÄÝvÅγ뽑|6÷ÑWœQoY‡q Ö$Åû¦š÷w_,Í2-ï) êˆÅéŒc€“‡-•2mXÞy2O„DÔÏvòÞNÛé·Û-~3Þ:²Ÿûü&[=ËËÕøò©8êz¿<ÃQÐE9?¡ü›€2ð7 ŽFÆ<ü¼ÛõT× _Jÿ“â R~ïdú{]Buò©ü6‹ƒ’ ·Ê ¤üNËú.;”ÉÈ’·8t|\ùVöeÚýÐóNÝ­ nÛ»Û­É#¡×HAÒƒ9JùáìdÛ€ C5­ñ×þª[ƒ/ï½{ïCFþõÔdW·KeKÖ3Ä=ß wÇÞo.ˆeIF¶&È(¶Ì•Îæ î(Z¼2xÓ5tÿ¾¿¥æþ8ÿ§§¼çFäµkð'“LÅÝÏWC8ØÆ¡¢Ýq_Yصóå7Þöà€½ûíµ-Ú×µÉÞN>`§ñO¼×lÏ .„cŸ¡Ç^žé¸Ý-2ÍG@î˜ò¢óR÷~pOùîçEß>uÿÝ—#§dy—H¥ø>iXÓn³«e–†;{c›Ý4æFò»,V"w톜–Ùâ-s4Yä"Rμÿ}ˆ)€­[»Yl«ßn÷~Í}+ï”,þòñ ásP×dM¼È+Þ+ÛR>•5ô>ÛïwW¼ºÊo¡ JhÈóT|*ÒÏÛ8ŸÕðâHì~cɧ‰ÐO’Í’ôÿì}`\Õ•öQiÔ{³Ü{7›Þ;$@€RaÙ$„l²›ü›¶%ÙÝÙô²Ù@BÂR šÁt\0Û¸[î½Èê]i4åÿ¾ûtGÏc4UiÞµŸÞ›Wî»÷œûÎ=çž]ÈQ[ á„ãÎ, ±ªp$>ÈF;ÐûùŒU,Ä âE°ð4|(¥æ;æ˜ÛGyŸfXßC –U,XˆŒ´€D(š—¹ Ù zÂUôc~¸ºA…7CΚ»ÍаŽc@~º|—?É÷è; pF-j"—„’ùVß-Œ| ‰oß‚Ùߟ`š@!‰NÙô‘2›Œ…~Z}° `A ö 9"«6îÓëëÿÆ]‡åÜ…Óå½­ûeÙªMêØ¶_Μ3É|в,$6(Ý÷Çe’“™A§Xžz}­<^ïʇ;ËøòB)-Ê“þô²2çíFÒ×Õ›öÈ___'3&–Kq~¶ï~ëÀ‚€ á@ÀZ¶ jÖ3,œªƒÕrÉ’ÙrÖœÉ2y\±P(2—ë/\¤V{™ü-S·ö]dFñ¯Üv’:ÚÌ·[Ç,$0ö­AòöTä'Z¢ °ÿXÝ)‹.3&–É…gy‹ÞÛ²Ov®‘ñeùÊ´—9fO®H`èY]· `A Z°4IÑ‚¤U†@[§«¾Ff …Ì)Åž–¦~g¤Û¤£«Çw­´0×|а,X ÚAO²3 zÂßùÙÜùŠ=Ý '<¡hŠ£ÛwmLï¬bAÀ‚€h@À’¢E«  9SÆÉÖ½ÇÄíöȦ=GàX}*@6î:$.—[6í>ªü’N½jý² `AÀ‚@?æL'5­r¢®YùíÐ}£ý¼5¦F;#o¿Ö|,5-ÑûÁjÑôDï»w4_³ÆÔhÆ^øm×x·x”ðaé“ -$qRzgÃnYña•´uö@hñJj²C’S[%Iz"…¯ïy¯¤‹Ç•'.O†¼²f›äd¥ÉegÍ•KÏš#éé©>É÷€u3ï$:L|ÚÓÓ++7í–Õ›w#Ë»3&ø+¸7l·Û+ÕõÍr²¡UêšZ¥«»Gœ½nl.éÁž¡ÀÝ<ÓÓR$Ýf“ìŒtÉÏÉ’Âül_R åŹ’‚ÈUdnƃc©˜}®£¦bó|òÎÆ]ÒÞáÀ2­ˆã¼;5IÜ&‹‚H;•fwy%ÅíVóIv–].?kö¨›O4MᾩµyZ¤¶¹UZÚŒE*Òµ!5i £ÝqÁ1#= ô$Smeý=¾¬P23Ò-Ñ‹‘Â8ž·ÆT<`aøÛ@¼ûó(«6Îî˜Ð”±BOb©„’8wì?.O¾ú´F=’‘±_Jжb¿ bŒŠÇc‡c–tt-’—V;eÕ¦]òɫϕùÓ'€iLVÂRŒ^mU hÂÓÛÛ«´†Ï¼µ±sXð?q¯™­ËÑ#w‘ÍØŸ¬óâñ©””ŒÝ^üî£Ò«6ªá¼ž,À<“Ú¦~È””$™ZY*ó¦—³çMQÌÎh˜¬1å { ç“'^]+mŽZ°VWž+ÍÙ6q™rƒE@©ø. :z¥´½óÉ,øì‘O]}vÜÏ'š Ü}¸F6T”]‡OJk{—<^ÀÌ“ Á› —˜ÜRÁ<Ú°@“Š®$,˜KqAŽÌ›:NΘ1A¦O,=JRóéh]„±Æ”»‰sì?Ÿ<ýÖ‡XÀíš2ZéI,GG Iš8¿µ¾ ê6!›w”—.»ýH,áì«›XVÖVµuwO’¦–ä÷Ï÷È È~ÅÙó$55eTv_Gãì@ãÝ…I§Ó)+6î–Wßßü×ÿ/ þGîI¬¹uw÷Ê[ª qÝ©#›­ðÚ+yyG¿ŒÙ&E0M¢PHt¹ PG©8{ÆË‘ši²ïh­¼´j£œ=wªÜrùRÉÊ4VƒuUZHcÒÈvhz;ºº!l Þ™¬’Hr5™+ËZÀN†ÈSK‰»×cÀ˜O6KWzŠìŸ/m™¶a °úÜtµåvõÊôúÌ'«1Ÿ,ŒËùDÃk'ÌÚŸóW×Ô&^,˜4â»o/Í–XU8 }îÅ‚áP%„ Íå‘L§[²º]Òmv=VÛWmÜ#ã ­¾õò3e†IX2×ÇvtbчÌg7¬I¨¡²¦Ø¡¡Ê…VNÓ>c>6׫c #kLÅ ÂñY¯Æ»™GYþ~•8†‘¦Œ6z2˜L(!I·ÖUÉó²2«¤¨è9IÆÊ÷H fåeIcã-X4Ü_®X:Wl6Ëü.šøÐxï¥)$2û¯°kDñ¸'\Xô^ÃÜ€—æˆniïrÈž_)GkZ¼Š‹Þ•´´j}kH{ ‰ii'Õ&X(`éí-†YÒY_åÁjò ùÇO]­ÌòÁ?pÿ±Z9?±#5õŽ£9`°J óàçW,̧2cB™dØ a+VB“5¦BBý˜¼Y=Ÿ4f§Ë^h„“‹¦,XÅÍÎL—¬t›b(ŽÕ6IiÉ_aó¿+rdøÕ`³5HaÁk’“½Yjjï–ÿ}vVvÊ?Œ·¦Ù!T“üü&hŽ:`’Ú‰³0ó󦊛Ë'îÞBô¥D¨„¼ ÷%É|˜ñ·h:Ìo*cbÆj)?D&àÏþùd³™Ù=.wÄ¡@í˜]ÝAd[ÌçÍܵCÃ{¬¦Qj!œP¡††>ŸôKô@@"=áF5ðÛð¦ökʾà6ÑÍóêríRŸ“.sƒ¿a1ôýíäxm³z…Ú¢Vh«:ó3Ä :ç‚ËE3wÈKÉ ©h¯š©Z‡CNèPæÅ|°¬(OÎ[0UΙ? ý°+š “5¦¢5FW=þóÉëëvÇMnzXK!‰ƒfCO½¾V™ØQƒO…íé­-‘gßÚ Óà«‘•¡ÔütBµJø Þ©¾¦©µ­C^zw‹2±‹'üÇ÷ì?2,UŽË–½Çdç¡ã>!„MMíÂ*©>ænü¢Ÿü(\Ù`pÀÔ äç­Ž‰€¤*ïûcƒÙcfÖ&ihY-ü‹·a¿í >xŠBSOÏ‘˜)U‡Î”­X)ÈÍ.\,gƒÁá·ï)ÑÇ”o‰z¬ç“'__§Lì¨AЧÂöœq¬Yžy{cÔç-ÕA Z·ã€òí<Ž…Uh>ç„0¯¡¾à I’ú“ Í4OôÀœniô$3ü½Ð2.Î’ü#Ͳ¯¥Mꊲ¤‚S7ÞJ¡)_~—S:H¢våfY Ýù §É5çÏ—¼ì¬¨ K‰<¦BÁÉX»×>y‹49'šKzïøL!I3‰ï|¸SZ¡Š§ÒH™ØlOaþ2©©û¼¬„¿ÌÕçÎS ],V«µa¬×x§’ÃÑ-k6Kù Åþc{^ƒøz#óÖúÐubRï?Ñ>).Ü'iéÕÊŸ(,¼ÞÔ‘‰gÚ‡ehæ/Ç7ð:†öo¨A|În?¤¶‚ü7ÅÑ5Îà—ÈcËð©Ú!Ÿ¾î|™2®Ä÷] TÇPçyL ›D¹®Ççi ÒH™Ø‚9Û³¿$[2Ž·Dm>a¿IS˜*áµµÛeÛ¾ã Í"ˆ*×!¤-æ„jhµKI ÊèŽèï„ÆèƒÅEì³Ú•–!ÕЉp¯¤'ÌYR—cën³}]X™w>Ñ4¥¥½S~üØrØ)5ð'Z?¹PŽ@{Š€×€ ¡q4…ÚU™'o7‚Ëüò/oI#ü:é÷JšB˜…RmL…›±|¯Æ»™GéFÞÆx¦)‘ғшÏ’ܲëPµZ©f¤x.lWÔ™?‚¹|a,T¢Ïý®¶ CšÚSC¸Æ3þ#Á½î/¤mûŽÊ£¯¼'É)Õ2®â×*ÜüpÁ=^ßÃÈz¥Å‘ÜÜ÷þ}¼s!úª…"(i'ʘŠW\Žd»ôà|ÂD±ÌƒÏEµí g>a_ù}P@úÕSoÊ {¡p°¿,Gå/Šç~GÛj`‚·¦–]òûVÂô²+,A)‘ÆÔpàe´¼CãÝ<ŸÄ;M‰„žŒ¼ø·s̛ۑÈSÐhD6ï”d:¨Ç.Q¬?pÃùÍö±Í˜˜ÈðÚÓ F.{çpÚ1ÚŸÑx' Õ®¾Á࿼hüÝõ¯¨î»Ý=ÒÜqT>Øñ[Ùq0ü@“Ë/êÆ­Ð v Öpq¯‰-ûz¢®I_¾¦š'¥¬ä‘¸ïƒ$ ó_;K–¿/ˆ€W)•eEž“Ô6ÔëÂS—žù]9wÞ—UõÝÎV©iÜ©ïKC뾡^9àõ”ä4™<îB9pü¯›O†;¦ÌuXÇýÐc€ó‰ fÐÁ$Š}íúˤ2+S1¨Ç‚ÍÊêZùÑ–Ò¢&S·bb6rƒ¥¤Ê¾Ö6}*àžís%§„5Ÿ°¯ÔŽüñÅw¥š’ˆš×‚pûVé‡@»=Uª*r%éx+Ì«äú )Zb³GSXS¨cjnAžüõÊ U#œ·ÃÜöÇÝûeÙ‘ý ñèœÒbÙÑÜ"À÷P%’15T݉t]ãÝÌ£°_ÝE"â>~±¥¯…¹\’Z;XYzR‰Òk#ª&9¥UÚR•Û(ŸCD/ãk¼†==ðIÂF¸[^\}¯<³â.ø²ì–kιñ²‚}ô”ûÈÐÞré$'³ü”ó~„ƒ{ÍÌP|¦d^o´&OXR ,#ÓçóJÓHøS"Sí5ò§—¯–7×ÿ’UŽ“ }3˜WxÏÔÊKä²ÅßðÚ@'ÃSÕc#CÛ?Ÿô¤¿úùCÇäÓo¿'ï=$Ÿ˜6I.W68¿¼¯j”Œ\©þUv"ÚNXw1X-€¾?ûä$§ô"ô¨Ëåò™Û±±JŒ6ÇSf¼†ŒÓÛëþA7³©ãÔ6îºæ]2ç7Ȭ‰×ÉöÏ"Éà4¹BSEáBiwÔÈš-?—]G–©z¯9çÜ¿¡a'Ê3>-/¬ú’\°è!`e˧®|JÜÞ^ùÝsçÚ†Pq¯™:~nCÈë#HúZ\øê°\´Cqx‘áÎ3ì;eßQøTÀÓ’<è÷é˜r{»ÕfOËCT¬ï#˜FBÓ·Ëü)7#¬ð×%;£ Ç-Htü]¬üF{’åK7¯‘çVÝ-—ù/R˜;IB¿*×bœeØ åË·¼/O¬”×× .0…:¦âeqÑ$óp"ÒY(A±H³£©Em—"ÅÓÇËëÇOB#”"ÿoá¹r|9-mäµc'ågÛv)-Óe¤nž2Až9xD¾sƾ±¡QŸr þiD_¤ûOH/˜ÿx q>d§‡ñ† hYˆ[šwgdôGºlÞŽdLÝ;w¦ä¦ÛdAažì„F‰šF–PÇÂs¨çªPÇALÁ–PÆT°u&Ê}f¼‡Ë£Ü3gÆiZå÷kä¿–.”sácæZq²N~Í4}ͨ™þÜÌ©òêÑò¥¹3`Ùâ•ßìØ–/["á~L Iüà|ƒ1įÏíq ¨p{œRÛ°!–ŸÀ*QšÜ|ÉCr²a³Z ¦&añÌÏȺÉ›ëþU.^ü-µ½¸ú+!¾ çüÃ@æf•Ð  áFrÅ&TÒÔéHÍ{p°ÿ mêå9†@-Lðt©mÞ‰ÕþJýŒëŸ€ä;êA¸×+¾´i®ojC?EliÆDê+é~—+y³æËÌñ…†I+GÆp¨ɘ"£äMY¿ó÷ja¥£«F½.7³B™têw×À\“%/»Ò'$¨^¡/‡¿rL…ÿ‚Äx² „Ö_ _j¡µü· ÛäýÚéÁ µJy`r÷¶ökƒv·´ÊǧNP×ôVÃ$*â$þÙ?®l÷ºz¥ÁÚÓƒP…DܸÑ_ÁøfBî¨|D;ÔfòzÞLHbÏÃSÉTë0–þA@ª`ÊÉcJ5ÌúsúñnæQ‚÷s\u²ö4­2ƒÁìjn“§•4ì¿8ïLÙÑØ"Ï>¦4ÓKK _ù£-Uróä‰òý³(߃ÈIONëô(<1æ…$℃1Ôâñô"¢ÏÀZ€õ»þ ü¨U ÏÊ”ÊK•ÄwùY±ñ>õº¬Œ9§/ªU¨ïg‹õGê³Öý4üÂÁÿê­?Q>IfX6·Q?‹r¦Ê‰žM}ÇS ÁYá»Íå Þ\Á÷ßA°¸'cÏU(ú\µ!Y. ým¬nO¦Ô5|þi)²xj¾±ÐèC¸cª³»A^yœij;,s'Ôwºc‹…÷ëâÆ1¥ßiíC z²«ü¿‚9“¹1¡à49»?(Ì$sÕ×Ì´P Š´OS Í*M”Ј8Ó‚Eˆ¼:%¿Ó)Óh6U-†„¾(ΘúMÕå“dn|<Ž)sû¬ãS!î|ÂZv3HÈZej¥)@GDM.À\PQ¢„$ýæßlß#[›e;„§7?z…,**ëƒ×J³ž`é‰~çhÞ[0Dìee–ʵgß/ãazÕ(+ÁÝ=Æ*«ª7…õ¥• íý ZáK`•Ñ j:áotμ/ÁÄò~™6þ2$§-—ýÇßرæ¶C`Ä=2±ìÅøÒŸ‰¿£QH`Ýð_ C“†UL·;7UÉ:º»'ICÓÍâõȹ3Ëá`mÇbÒÝàÏP«½±È¡“+åì¹_¥sþãè9gî=ÊüŽ&vJü#Ïœù9E{\nîï§?ž±ÎÇ'Þ…–ˆAœZo¯À ñ`åL¢(g{ ù â­Bš¢5ÔÐr¥¹£Ww´Ú/õ¤º½2µ¾CJ°¡4+E¦”dA›”‚…ªÁƒÀĺý¡Ž)€ iÖ’âBiìvJ;|\ùÛ*£ňPøï‹çËb˜õÖ"ðJ9~·"A­¹Ð¥é:€ë……ù! IæúÆúqüÇŒá¬Î õº³ç|AÊŠæËïž¿!}¯A®“½§<Âègº UOo;¯öÛ“ëk¡îcÑ—PÛ0Zî¬\înùë;wHAîd¹çc«åÒÅß•÷¶ÿJö{# hÈÀnÚó˜\¹J5@IDATô?äëŸØ¬|ÜÞàB þð<725©ø’3àcãpÌ PKè§o¾ø!W²8ôx‚Z×o}ö "º.Ä}äüŸÉåKþm€§¢wª©ù:©©û¼xÝ…²ù^Ædñ:UE¶KQLÍàï „ƒÁŸúê¡êÕòö‡?@¥{Õ˜"m¡yî`9µöSê)ïÞ[×É þzè— pG¬ú3À«ÆÌ©XÀìÍUˆx×*¿»x©üñÒó”OÈO·ö›ó¼¿8"s‘ìƒ]#׌¯è–!Ï ÖòÇ4¹+ÎÉü.D…¿üÅÙÓäS:­ÂüS—UžJýæÉ|y„¢õ.ÿzòºzå¬ÃJ@*²{ez±]ÒÒlý4%Hai0<ø¿3Øß¡Ž)2ÑSßYŒŠ7^)Ya†E_‚íóh»/V°ºcÆd™]+×¾ºB>þæj90@`—)9†æº =M²´h_[ä©qbÕŸxÀë˜×$yá"p -@Næ8éìªÃJ‹K¦Œ»X9óï;ö¦—¹¸¾hú'‘|“,œv»­ù lÍn{¸í÷5*̰ã¤la¢Ï=>1àíuM;åË®VÂŽœ¡¿r†j¨¼¹áßUt;®kŽgnÿ@uêsæ±1¾0Kö×N…`^!é¶“ú–°÷»¼Œˆ|Ç}>9Ù¦’ï2РÚšä¤ÉM¢&Il}<j-Ò›";N¶‹×f—’ÒÒV~Í8 eL­Üô€p T6ìú“p£IM£.Ô84; ˆ›öôüSî×ÏÚ›ÛèëüàÐ0ü®S¯^»|Å©'L¿˜ ôk7*_šÈ˜Më¬Á°A?ö¢Ü­À–›–†àN}zȽn;÷ƒÒ)–•E²÷x£”¶9¤&/c°G‚ºöa}“ô`Ag¨ò³óÎ’ÿ­Ú+Úú}µz&~£ÐŸv‰Úá\‹eI1Y4v'ÉÞF§œ‰ €ÆÂ ç­Öx ¶½ Ò°à™WÞΘ¢`õK˜ƒrht‡`Þ©Û>Ô˜ ØØ¾`†ÝŸd@(ùk•Ë33¤AœÜ0Å?¿¬¥BYyâTÍôg HývÇ>áÞ…q¼ße8ÅÜþpž-Ï ÌµŒ–ÖǸf-ÀÕgÿP~ùô"Ù ­ÀG.ø¹|ó“»•Cÿ:ø'fOöµ„©Îš}—\WðcešÅä‘ášêýiÙ?K„~;æ§TýM©©ÿ™9seÎ}%V{ôß:ÂG?Ný?z<¼fp†ß±tÃ=ÇÑ)Ò¾3Éö÷’•^}=5ØJÿ@÷÷ºB÷:ó«}'ŸÅ e2p*30eÜ¥rÖÌ/*ÓšwpR¶'ïÇ$—%õõ·É¸òßãZèï4·}bùù9} 㸠u=š!0I[ç 0õã|=röÜ/ÅùÓåc?s±7eÏÑWåäü)+œ/Mmeëþ§„‚¥.ÓÆ]&s§Ü$‡O¾+[pít¡1IfL¸RæM¹ENà[Ú~àoÒíl•ŠâEX|ør •"PÊhð~ƒ*göô;¹/.z^ ^iìéèX"[‰ì8zQ(×I~^'¾/q€1£~伟#QïqšÓ¾´æŸ¤£³M]néhwIks/Ì.—ôÈyGß{Ì’ïä f3ßAn³.˜}‚i.G~(ëlƒÆà綈˖.)XEN2M(9ЪÞö"±ÓôÿHSòò2Å™ê•ÉuÒš‘&Ž´È2æÂ¬§ þVdò?3cŠÔ9²´¤Hr y Ñþ˜óç¶©årä‘ÊE{v"ˆÅ/ÙaÐoÅy{rŠ|PW/ÞwØ÷åO„ßÅOáœ~²Ë¡ê¨‡ ‘™['·#o|¬ž;xLŽuv Í’>&qf^.B`·|Ö¿.ý»)+]ÖMK—,„H/îSŠÀ ¯o;.¦}\Ø23%B,ƒ9|Å.÷KþËïktÁ‹ÉÊMß/´?»fPèc´J¨cÊÁT!”½ð‰¹õÕ’’-Év»$8!Ôàw+SqÍ¿dð»W?ס5ëÄ- "¨Ù`à݃€Kžînqwtˆ °\’Ÿ¶ÜИjÿ @j•ÿAaþ²ÿˆÜ‡ïxÝÍ×£Ò?iRV¿Ï##ÚQ~ùºKTt»ßU탹nhx²ocìKH¡þZ€cÈQòàóöiN—éèn‘'VÞzÚªð¯±.[ô£!3“šš¢V0í`bRº×ˆÛ~…T×Ü#e%Oâ|øÑî¦W^!ô™¡NŠÇ•%gÉûÛ)³'Ý(¿ôOÐfÜ€°æû`šã”»ÿ¤|ôz]]¢ŽËî#¯@йY¸¸ðÄk·øú6A ¶|F.˜ÿuÉA·þÐwKçÜ%s&L?9cÖg@u’¥~W>zþ/dÇÁ¿!âä ˆü6w/ é$';%3cÚzœã¤¾áS0•¼@æØ>¼\h—B¯RWmí-Œ 4z],º¤Â§-Ó+ùmYx¬YvUäE|>BU·ôùGðøâòO¼[J3Òå/W\ ¾ø¦ì³H Ñrä…ÚÕÚª`Úƒà4/!Ijg¯Ñb•×ç,7!)ïƒ;÷ÉÕ0;üŸ –Èío­QçõŸY0KüýEçÈw7l–é¹¹ò¿-•^[)_š3] ÒÓåw;÷*ÿ.¿ ýüPûN¶àv"?þIí0¿ëü”\ìÊ‚™’†ªÂºnA l ¤U¾šk&fÿ’žá¾MUò£Í;•ª í__"ü¶„¤ °<`(MB¨«ÂA4ú%Á 0müå²xÎȹaWL‹÷é7 fÆfÆžn—“.’¢œùr¼®Yvקȉ“÷JvöV#ë ´G¾8·íÀS~–ËI˜$~ùæ÷T©º–Ý*´5tÙ ­‰H Ku<óý$!’œ.ôáªmÜ!i©ÙXðEY¡/ôígL¸ANŽIyÑ<ér4Éü©·ÈëÿZ©C2Ç4ÿ£°iIO«–Š2„ﯹWÚ;n’Ïßxò) sè_hÖvë¥Ãß«[Ú°ÚÛØÔ,µp¸kë$þÜï»õÛ‚@\B€9uî[z†ä"S šBÿF~4+…ïÂ7±*}ìd½¼··Vo‘†œt©.ÈDhp°§*·CîóDÔúÛ¡£ê¹›§LsÊŠ|¹~v¶´ÈžÃÜn¢pQ¨šMT+Vß— Œ±’^€ðôúñ“²!°_ÿÈå’ ™¹Pƒs¢«K5Ð Ó¢ Ù™BÁé(‚\;~œìÄsOa5ž&j‘4F{Ës±tÓ.e-¹÷Ši2y\‰¤Cã"–¹B7ÒpMWþØðžìBþ+«Xi•Ìu†bRi~.-!)ŠXïBRÈhEÉ*-˜#·]þ†!ŠÑ`“Z°Íÿùã¯ÉÖûv=ñ«Ï0IK#6:@*G6C ‚ g-Ñ£¿[ùg¿þ­_-š;cÎ7>w-~†^ÌÏXfh9ÖÈÐÐa˜LO6'L7Jó{ÈÁ#Çšz¤¹s!ÌËΔä.˜•½#99ýÂL¨íí EÍ`$,i0Â÷+ó¦|L.?ë_‘èa•X™—Íß„Óih_YGfz¡ßÓYÐHþTmG•æŠÜØs«¾s»Ûå’Å߆¶é òØkˆR‡dΑ”””˜½|X7Ê‘“ å_ªÚJíœU,$"4MQ /v˜“Áœ­ŽàLsËÞjÌoí=RŒÍ…oähq¦œÌßÞÔaò}iǪwÌÓü å°ç®¹S› ɾ[ºtô­–wôEøË£É—©”gd(ÿ žbĶŸnÝ)íHüJó>ÖyïüYr笩òé·ß“ã}iL‡|x¸$[J`~÷Áö0ÎSô™f¼-¾„\yœ=03?WþõòEQåQvî?òá#?ùáMèj<ð&fˆûø”»þù__œ;}Ò’pùVˆG1– ̯Þ1¿7š¥Z%4XÜ@hðôî×>ø¶¼ºö[ƒÞc]´ -¡á*e4IJH‚íqNn¶äbˇøäÂt)êã;<^˜Dhú1½òJ±¥fÀŒîc*b#µ;]=Mê\š-[ukÖÄëeLíÖí|›qÎÜß9“> †!]fM¼¦|ïš/©ã‡—)-Ó–}O©`;áEa¨‘"yî©7?-ÓBh±N’wZeAœ°Û«»NÔ6ª$\I·ŠD…€!$%ã›N;…$øÖdCóT—#3Êód|xEÈ(© \¡yêYp*/Áâ5;3P?“ìºðý1,q1|\Xx~ ÞÿÃÍ;„¡­©2—KÆ•JhàõÇ)ðH;œPMå5¿(É´CÛT#OÀ—é5ì«Á(N­ÜXß,w¼ó¾òÑ ÃÒ RGZ*´åð_„æÙ‰¾0—Ø еêˆ+fàêWÞ9%[$õ%ʳ–&)Q0=Ìý|à÷ÉßýîÝžÀ!½†¹=cñudh¨õ0V}=`ò3±¹¥««]ˆp³÷h‹´÷$Ãg¯¼Ƨ?Ai8ðp8›åÎë_†VÊ&ooøª‚¦sÇê6ÈWo] O«¤Êן÷™ !¨êÈK°éºtuC ‚àô•›×JcÛ~ykÃ÷õ%ß~ÓžGåÂ…ß{nZ¾Ù H­’ek¾.·ÀÜÍnË•^äZ»ã·âènö=ÑAŸ#R/V¢¹–dhÛx) „±÷Øë*ìw¼´ÉjÇð@`¹¯Bî$:úWîMSÒ±òLM’ ßEr®lßU- mniÉL“C¥YÒa ’rùZ8û ™Aèwˆfw¢O“ó$œÏŽ` pž÷këååk.•ÆžYuòTË}H–ùÔªè€?–È¿¬«k”“Ç.;OÒ@+]ø¶¯£ø…9Óä „§kk´Tµþ†ÿ¤Ã a¯4˜‚&±¸˜O4%üÎþ'çüù‘É^×¢ªÏ~ñÙÑß«±†@dT.Ö­‹ûú“ä‹7¾-/»ê4Ó®vO©¸þ!ÁD2ÊØ.ÿý°íìo¡×gË…µ³GGµ”’œ»ò …QæâµèÀ iC›D©yWÖì­CT8˜Þ•< !iWXÍÿísgŸò£×QSj¬Šö/#?õæ'O dò›g—À$&Œ‚Ó˜¡¥ýˆüú™3T}ïnù©òcÒ•¿ !H—Ž®Z¡F–°OF¤'íø‡/£aÃÖ/tég"Ù;{Ç©Çó°ÚLˆxÓ$%Á‰ý¢Eß”Öö£– ¢Gé³ î7Sþ¸ûÀ° I>m˜ûôt$«ì–o<ÓÝnÙ_ž#u¹†–'T~wÝ–S9­Ïo©RŠX—ßìØ#€PÈH\,÷¬^¯´EþÑ×®|åmuý·°H“4EzB7 –¼Î-ýa¢T–ï¸Mþk#“¾JTWÖ“ÐF¼Ë)HAx~§Z°"]‰¥&éœÒbÙÑÜ2 £¾êl‚ÿ™÷ôÃ…IwÁŽOÞs€ Hv÷ÌÆ°¸‡c^HbÉ$|Ô¢š‹ 7#è‰ æú/FÛ>³Uó=Ö±ËÜ.Ä‘P’?KEìÒåM?m…ˆç>qÙ#ŠÑ»áÂ_ÊIJsõícrÿ“DzKÝ×}c­sS+/‘Ë/5J´{'SðʺÝpDn‘ŠŠß…- ê4s÷ dgcdBó8 HŠÇC¶kðÂçµ€¤ïŒ¶€ÄzÛÛ—J¶=U ² !I1[€a¼•økQ¼AÈjO4! iJ2„ôgÞÙ&]Ð$Ï[@¬mfIßGÇrsä-IßÇ=i^0߇ô³|¯ùú|$û’önIFŽšrøjQ§¤X IdvuþYR³E« OûvÓ{ÕÀWÇÞÙÎ9CÊûÆC>|ô ¡Íô/ÿ±d¡\TQª´¸?V K“6’i/‚£ø·à?Wª6Ë $„üÈù?GâñRQ´H%t$Ãxýù?C4¯³‘ûe,_ûÏ05º^öŸx[Ölû%˜Å^•ëh탼)~.ÝÿPÒ[°9úÚ÷þÞ©lþûé³âùÁïÝí½ì?Ù§z]=Bkçb«JKNÿ;grjWow盘§¦Þÿû¤­0XÿÛ÷îöü'-î(åNo’çÛ\¬óz“þï_îñü"~z:xK•íZäʰÊ—oy_žX‰PÔß“3gÝ!Kf^åë9Z»VÞßñ©®ß A9Y¾tó„®þ¡œ7ÿ«ð š(»¾‚gþÚ cõrð7F~•Z7Öï–N˜¥T”>±y¹E÷<"M­ͧFý±£{:ò&M–³¦3^H0lVd]æbÉ5ç>€€E*Tvf9r­mÁØZ%·_ù„¼ôî? 7ÔFõ’sæÞ£òIýßòÔï‚ÜÉr×G^Ÿš0ìã*²^[Ok|yÞ ùÔ´ÉJ›ñ+$îü梹ò…•ëäûKæË6DYûõö=êÖ©ð)üÝEgËw>ج•[ýMEC¡¥¦eÙ‘¾k±8ÐLýê-û0F[eoež´ÛmQ{\š7D­â¬(¸™ØØ%YHÅP˜îÓátL }±~‡æ ÷üðÅçp’•k`JȼPß>c®Ì†Ÿ}´žGÀ§öVmø·³H]W·LÎÍ’KÁ$jë”ï¬ßŒh]1ic4+]øøgõz{ÜuÇ—.ÓõÎ{ôÁoxaN±óswÿtΣÞáþ‡wV²×ûôÜŒÂoîìnºcù>€¦kΣ}Ù–’|‘ ½˜°ñûв\ƒÉ»Ê›â}`×g¾´Z×;Zö¤)•!w üß~­ìä»vB…Êÿõ—>œÏÍÏ“®¹DÇ"ü÷¦†FuÏu¯®ZŒ…·^+e°¤à±UN‡€¥I:&¾3 §Ý&í'U>˜Ü¬J‡„–ÛüI3ßR猓d?B?øÂE2­ò2)Ê›†ÕûeÃë43ÊͬðÕ÷IIk½½®Ût;=®ÞÛࡱ–¿! ý^0MÏŸ; ßèq;¿÷½¿ï¨G¼¯Àla=©EÔ³^-ƒ÷ ›-óò”$ûGpüujœt½ñ¾oA®Ÿ£kMü¿ŒD¢w?¢šÜÖY-«6? O¾yÂoÛåÂÿäëJ^Öx¹rÉÈæ½Ëä âøabÔX23Ž”ßW{œÜì4¡&ª¯ý êwÒ€œHc¥x<™ÒÐx+´H)2«²Hùvqõ\o±èçuçýæ7]*ïSAΙ÷y®"TÌ}ò÷ót[=IrÖ'ß:ëé‡òpîG©’rsfª,ϲp‡£å²ª;¾´ &´ÿp×÷,ÚöÙ/«Õ^ïÇ’’½ïO*™8I•ÿ˜äJú®s´ì $þYÅEòéwÞ“­MróÔ òòÑ*zãW×lgU])Ι?Œ¿?9}¢äõ…É×BÑñ‡¥‰é±¥´ƒ¼x¸.EÂôL,?‰3?*Ÿ¹æh• %?gÒ€Í>X½JùX´ ‡KNF¹ÒÒrÔ½éip6w…vQ·ûa+ÞÔ?C ñ I0d¸ iÿüÈ#“íX¡™b·åü°¾Iz`¾z9þJAgZ‘&„æ¬vŒM–\h"»ð)Ôbn¨ÏަûÇ´¹ùõA5íñ†¶vQ»l©™ÐüwN“ øÈOý–̼x#Mf.>óÛ Ž©RYºD˜p3”âõ¦Ã¡îTùÕÜ—Pê õÞïÝãÜ}ßCIŽûN›Ÿ¤|v{Ýß¹ÛYußCÇ©â®ìr¶¾©ê4\NªöûÜ¢@‘¥§æÏúVyêvLËíð <5~k¨ òþ`Å…W;4üôºKÎüŽÌ™Å¸{ÕžÇ:gOCë^ßcõ-» }4‚øNq*îõНI“ 0±x<Ãæ!zŸ—½^›Ô5|NÕ•²`\®”! ç3×óbh"S•%g*€Ô6ïTûng«JŒ«¡Tuè¹øŒ–q%‹e2k&Í%ý¡'K£i\‘ž„2®BSºMÖþT˜ÇçšW[bÕww+S¿egsÿ1M¢v@kDᨠZ€™ÖÀÀæÂHo,d€"›õ}ˆd¬Á”hB™O ! &I0KJ•îÂÊ‹UB Éc§Ô#禗©ÈEz’ -!É2)/%óØ1Wd>ê˜2×c>fXô/Ξ:[%Íåµ4h¶tÑc‰¿÷`Lb‘ˆZM†F¶„:¦‚­w¨û’ÒåI¯3iîû¾Ç•+ïŸÕ3^ï·×uu'oÔÁ¥^Cµ6@¥àGÀ›åIIž¹=Ôë°uÒy'x è ÂØ›ñ®'’➦ljh’K¡ñïVÊ1½ôAˆ¦˜ºôø%Jæ˜|yQQ>rƒ9`®i )GØHá^÷g¸÷cZH23 „Àã¦F6ø²íÀ3ŗî¼n]š¼úÁ·äPÍj9á×äï ù‰7>1`e'›¶‰ÛÕ#ÿðñÐFÔÈÊM÷x_ “nw®ØaסÉ|}"EoorJ>Æi6˜š}mýB–ù¾Ž)ä„2Ÿh!‰éÆ劣¾EŽe sY¥œ)QHʱyeVI¦ ™ž‰ÜR’þ›ù¦R°à9SÜ_“qê˜òž¿‰!ú#­­i;`nWˆñ²â†S 8&çô/¢MÂ1…îh—B)¡Ž©Pêì^F¨ƒ@ÓF“;O¯÷V[RòÇx?Æl'dŒ«î¸ç{¾ÿš7nxÝ&Î'éݽúçûÍͲÂÎ3W^„ÐöyñÈqù Âç¿„ýo.X*=p8`Oì;$?=÷Luý×;vKWš¤‘Â}ÀÎÄøBBI$P¹™é0EÊÄ*»ŒPp&Wϯ¾Gùž0ƒÖ<øü…¾pÄ?z|¢E¿v³ïøéwîPÏùk™|78`ûØÎ¬t›"¬Á×U…}¡–Ÿr»/aýÐmOµœ‹+å¶ÊíÕÎ#žN½«Ð™?8·ð_hk²Ûl‡=î~..ì7G÷AÂNoöÔdH|¡á¿ Œí™3?'4•r!?O^Ö8ÕÀö®“Þ1¾!Ú*±Ÿy˜9þjÙ{x9„”RµÚ¿ ¦wÁ–pqïd%nó'ʦCÓ¥­ý|ÉÍy?ØWùûÚ;Ï’æ¦ëÀDØd^™]&•çK6VZ³a® V}Ó´dhnD'îCSj: *èÇÊÍ?’y“oRôÁüšÜ-žyLífÈ+k¿a¾$z\efKeñbÙ¾ÿ¯§\ô#Ü1¨>ë<Ѿù$•šúr xˆ²ùz¾3c²üvÇ>R“”ÞF˜ÐSØ>n¡Ì'’¸Ñ×ñ¼9äHm³Ì¨i“Ýãò> V2{Ü2£¶M²»]R”î‘)EvÉÉÉÍÍÆ>K%áå ó®Q;=T gL±Î#*ºß$æmìv"ª©í±KòZÑœê®Yý‹vº àdkc Â@O K¡äà gLéwGeïõþÙëò~bǶ;¾xˆu"]Ä+˜ó燱ë3_8ò¯7yÙ3¿ÏÙxÛ=­žääCäwü Ûí?ŸhØCSøü¶ìTæ”XÄî “¹ÿiîû(“:Þ£ËZ$kæÆòîÉz¹dÙ[’‚çB‰ð¨Û =Ñï­û¡¿ÞÑÚ³¾vëA8©¼„? Y°Tz ÚK :Z@Ò7Ž8T‰u³}lç¸Âl0týL¾~ïpì¿ýù®j(GZ0v}óïÇøÎ»î:Ü’”òIØ`܇(vû°ëö´?Âk߸ÓY=é]œ[ÿ’¿Çs#]4Þ CNRE*ÚPhøß‡¼@õÍ»åÞ[×É þ W£~ᆷ¡I|¾G†¦ßÜ×õ›T¤²[.ù½‡Ùå^Ôl‰÷ìó¬ø–dÛ¤©ùZij¹¯Nl®¦§g’œ¬¹Go‚ BšœQ‘)Jó%I:sE,BÍcÈÌ µâé˜Z½åÇðq¼‰w×IÌ0)l›Ënø³çOGž«*˜×©”¾Ë4Çc¼[‘T—cŒc3˜é˜ æ‰tœO¨@.ènåw £7àSt?’§¾pÍÅR 3si„ÿ“¦22ÙË~‘ëè[B𗝻Ž×“äÁû‚^ùUíC;ÃOòsìrÖ”)ètÊüã-’F§Ç.iÐÊM©ëÅG›$ù&d#¬kq„£ÉËË•\n8΄¦0]-¼6µÓ` wLñyú¹ýåÀùÎây²êÆ+Á0'É#{ÈßÏž*knºŠ Ê”S¿‹{ši}ZKŽÃRh»˜ƒ+”é˜ å]Ý›š”ùWÌëw‚y…•‹Qv|î‹+!×ÿV ·Î}ìÁM}ü!ñKÆ/(¯÷yˆýWÍ}ô¡·æ=öÐ…}ŒøNãÝÌ£„BSt¨EÒ’>Gz1T¡`Š€ÄúF÷Cõ)×Ç´&É7Á'VåHFZ²tt-‚J< ©j,`Rl_:°R”‡9JMß¿ÚREÞü½{¼WúWñ»]TMÌzàÏy¹¶´Þ{oƒ­A_ù—»=wþú‰ÂÜôÎ)˜ý7Ê¿Üí=Åq fz_Ó÷ÆzoÆ;a˜Œµü,ìò½!áŸ+ÿ¾z£ØÓóáÃÒáÕ#O¾qr³(¡SucóÞ'Ô^ûŽª^%/¿÷O}÷øÀT—ÃÅ=s™ÐîYÝ™Ý}þ¸Ùy¢EjÛ.nÇ )(x&e¡M„A58NoòÀ§ÏÑ5Ú´s•ïQj’G&å&ËøB¬ôB8Ê#“‡ð¸3ß ˜™hŒ©­ûÿ‚è˜Ï@ƒ”Ž`.]rï-¾ÜË ’˜áýø‰©§@•cNk«×lýEÈã*Ü1uJ#¬ æ1Àù$„ºyqêsÓ‡„™o®Ý(™øF»!h02sùò»Ì?ÕñrpàÆò£Í;•)ƆíKƒn(ó ûÉ…%ESRReze¡¸z{dëñV9óP“Åwt² ƒa”U»Æü™¹0ƒ*mëÁæ€ï§H¾Íƒ ɰ2ÈTš£¼\ƒ¦pñ…Úišðj-á¨D2¦tl®’_"ºx^%LSˆþÓîƒà·f‚[á¿ÊÈwæäºº®`öጩ`ê öžmwÜQ‡{Oûè½î'ЀþtÑã—Øæ:š7.ù¢ZÁ€ ÞQÜæÙ>Z´þŽ;µ#ýš|eç÷”û~ Ãïf%&­ÁÒ”ahæi¯iÜŸÖ a81¦…$ÂO{ü¹Keã~twO‚½ð‘aoh¯`»Žé2«"W1¼l3·`Ôõ¡½)²»¿û™ÖæjøÚg›`(œÈ@ÏGóœïèoRAéXrㄈÿn0¯æ¢$ó9}lL‡^Q¡ Háâž>4$² <`‡£°½ÏixZqd¤tJuG‘ÔÖ݉k‡¡5YÎ]Pñ½Ê¤û3Ôžº ¤Ûƒùoêöˆ¯÷ö#·Ñ¼«URRÙ ZW/¹]d{ˤ§g¼ôtOÁ¹d„IöH%Ìï+m*¾GÙðëÈÁ*o~Ÿ€D³ÍÌð;Œ™aã1¦¨‘¦€¤ ™àKhã*Ü1|{ïNó˜?¡Xzö×HnWoÐàB±ý÷‡n0«ÃægØ®|h€¦„8Ÿð;à÷À 4£¦uRY¾Ø°Ø°§¦CR:e<üpªñ]ÕåÙÅ Sæh–\ƒÐ@K’i‹fµÖÅ÷”µuÃd2IºmîD‘£,§[² -" ml銛—â‘’¬$D±\ Q ¢‰5Hrpœ™‘zLóÝàæîHÇ;æŸl×_³0Pçý“ëtÿ¹pÇ”=±ú±K‚J!ê´²þNŸ€tÚµ‘8aÆ»æQ*²RÄÕæ ‰¦ WÛã÷±‚Ø’´´N‚O&yáÔ Ùy´¦H7HyùCPEg*+à›ëõ€Ùd»ÒS½2³"¯ÈöGÆŠ3וèÇþxç„E!b|]j:Ú¤p®ˆþÛ`¥è„ïR¨%ܳ¯Ûì#™NÚ®^#Ó;Û‘›îÚö^DÍš õ“%¡j³2« Lí—Œôƒ`üƒóÏ3÷ æÞÇIgçéè8öìÎÒKyÙŸÌ·EýØëM‘ú†Ûñî²ëæü˜F®kŒùð‹/Ȳ+Á‘>G„‹bhÈÈà8«/`ƒff†ú¾b1¦:5èKlËHÆÔ€ÀµNúüÌóIÕÑz™ó«­ Ä‚¬@ß‘“ˆ&Æ\7±(XPíb —PçŽuö‘ZiES² 9‡ö¡ &<ôëiîm.±5vʤ¦N•G©y”˜O©;Ì€CÌ/TÐ唊ænÉtº”•ð¦É…â€àËRÙÜ%“ ô*É )™Œra‘—?Dvøü.YY¤+”ÙnŸ#$#Lp’?]!î˜ Ôóùf˜ç…›Æü¨D2¦N©Èúq-1ó(';:¢)±k"ã~L I4\igh_2əްtz…¬Þé…Â-RRœós,Ÿ®›íqö–Êãa;aÚþ°ÄÕõúyk*Ìx',iRE¸NÌM•-¥QÇ?Í¢þ÷oçžÚˆ E‚{c5 š$04éŽ×,š™KOë–Rø<´8\ÒìL“N6íKpBÛ0Îj±¯ÃwÒŒ$ˆ˜'v©„ &^O:W ¹dô:ËÄÑ3çX D’ ΟNiùƦ¥°àœž¦JuœÎq¨ÿ£Ð”Jyz¯sA‹Ä ŸŒF:Ì2ÀPÑÄÅ–Š%Ԭ߄ £M)! ‚…#ž£Ö-?$ý~î£=¦hÊ«ɘŠU›ÆB½æ1Àùäìée˜Oªe¦ ltw!¯ÍÕ¯¼ôý¡ÞÈöPؘÆ|Âï‰ýä÷Aa€a†…ô†‹2™]ˆê”:ød¥"H}–X¯A‡ºÒaV˜†„5øÝ‹gÜØ³p?ô)Íå;"jÙ¡­ÉƒæˆB µ¿¹)XÔA.³¹0ÞY™A‰è㻤’‹F)n)Ju‰ 'AL@ÏÐOlvpGiÐ1²-µjŒTg‡öH H¤+J;Å‘˜HSô¢ io°%Zc*˜÷ýÇÆmÁÜ6à=‘Œ©+Lð“f¼›y”I¹]àQ\!Ó”X‚3‘qoB—Õ¼n¯§³»WÅW5(kØ×*MÅ4ˆM®(DbÐNølÌÁªtQÑsŠ!Œà=Ê_24]ódrA"W!ü7&&ƒàÒ™<¸©¡Ñíìõ"ƒ:Õ ƦýPÇõSÚär»‘âßï„)'·R, v:;¤ð–zà¿xäð-ÜsŒ€Ÿ;&~F¥bá¹¥a²AãÓ3În‰N)B¸WŒiCT¦NLL\…âtÁdnžüs£a[˜˜|¬Ng¦Âao‡2Y*êk‚àåèž)y9«Á4ìCÕoVÎ rÁ„ÎÑ=!ÍÂŒn’¤âݳ¡!9Á°Úé•&‡ ­$24††+¼J8Ây ”Cå/¨½‰4¦ê¿ùé‰Ûãæ¼ùÛ5ß2’Çæ6y£AOtgüÇ@ÿ|Ò*³«ÛdoynH%]o´ö\ñ%CSÔÑÑ|Â~Òˆ‹KÞ,ƒ¦Ð÷‘çøý©ËMÉËìÁâžSºðý·öx¤ÆYÝ0)u€Z AOS†ÂH´¿YˆkhDd#$R$ÔÑ®$Y|¸IjA³k n…ù]hÊWÔÞ#%ØàÐ/6·”gB8³ÙUߨoöÓþšú¾>ƒ¦h“æ,S¨o‰’ò/A¡”DSCÁ„4)-h§®¿ß¡îëª]ÑâOýñîÏ£¾á‘¦)Ñ¢'C!*žùÓȨÎP=íºA‰ñŒ§×]×ÒÚÉ߃smAÔObg0OƪW’L.UŒâÞÚyX™.–Âü—GÄG‰>4ýê…iB^ŠLÇKß e FÐXéÚW"0HK[§×Ùãç\c<èƒ{0õÄàžÓÞï„P¤øï=Цô Yãx¹{`†æ˜+½µ#ƒÿhâÞè+ý’ÒÔäÎßš™ÑÂBww·”zmÛétBhÀÊ©›fy†æÉIÇ Æ?™ +Ç?°ìËèH„°Š•U®ª& d$Hä)”YÆ÷ãR HMWÆôGYïz0S5`¦ŽbCK•Ú ´T‚c˜¹&+mW¨ÓІlä0ËA=Yð-ÊUætÎÞJ|£9j¸¥ƒRWÀ·¨‘¶2°Jm^­å·­¦T´‹!Ìv¯úÝgÃc¥™sgfd§PJ"©¡àBzÒëtú; žö-UO ®ŸÖ†hÐÝÎÆ@ÿ|Ò)‹Ž5É’œ }”t½ÑØÓg€¦Ô E:Ÿè~‚¤øhŠ^„Pßæ'Û:@WH[²`Ž—3ß^ÐVÒæXêžô„&…Œ¤…œ{Ê߇_ êj ¡"UÑ«$<Õßd64W9™½r¼ ¾@–PÞê^˜úµÀ¤¯jälô…rƒÁúWðš¾@ÐŽƒ€ÙàS”Ž÷sOmUÌÌ2±'ûÍ÷çB8*É0|‹ÒÓ¨ê_81h ]Œ…µ¨@†t%]-¸ñ8ÍÖgñ¡hcûCÁ§†µÖÜÊ£Œ15LHSÜ.W½é¾Ó¾eÓµá>ôµ%Züé@x÷çQ((M‰&= YqÌŸÂã9KoOOu»£'© Lm&Q$ň†9‰UãdçM*Ó—$»k¼RS÷y½ý’¹û=`ºB÷Ó¶Ì[°¼Œ:Å 4/˜^€È[Ð Ñé3‹¾h£A€‡KÌ{ G³»«ÓL€‚ytDîa;#Å ¼;•&Å-• Òà°{ÒQlàßü#êa,ñKÜS`âÈÇÐpõ““®Ò¦@‹Bf†Ú$ʈøFA©W% %3CÍMô˜˜–BÖFù„#ëö "¨W;™òïe"[ÖgOï‘üL'ÆšSZzšª2iwV(æ(ØDÁ,Od`E9Ó­raÚ¢Ì%Á˜QH-k*úÆvèÕ824ì³”ÈôP R{u¯Ñn£/¡­ôê¶'Ú˜ÒýößkzâttÕú_‹ÇßÑ 'º_Æ€žOvÕtÊ„ÊnN„úf„ã6ç‰~G({š1,/£N1Àc(LŽÒ|bôÕ0SÇb|Á QqdišâD0h”úhJoŸO¤2Ó£€„6úÓÖGšbÐ-Ò*cñ… Ÿe}9™ÝÒ‰ºÚ¡ýF{;rÂùØÕà ŵ;´U©ÐVAèË…€•BáˆôDÓÒ jßµpd´Íðg¦ ä£'¤-ŽØfÒÞÏþp §(Ø¢¾c e,©@ðÑ4…¼_ {âå|´øÓ@x÷çQªÞa£)±¤'ð§q¯üi< IÞšcGÞË-*úúöý'äœùSÁ6èóš“Ñâ* ™:á) Þùv›¬oƒCÿT©‡Ðb¬žw©ÈYÉðµˆVQ+æ.$ãCBSš!¤b%½,Ã+ãaF@Gò¼<æmÁFA md[I€ÙöH áˆI*éèÞÝ4H棷H«ŽæóºM^¶³rÊ´Û#Å ¼kŸ^Ï¿N]|u1­¾;6ø.ܳ?$¼> ‡^ý¤‹Z@âoÅÌ@@êÅf04ý‚’F,ëô13ÐÔ(f†ÂHŸ€BX’™q¢î.‡C bJXŠ20ÖÛƒ=r/ªÕdj¨¢ñ‡{šÛ¤B0²I@¤bµ×Œ‡¿oQ„=ÅШ\ÃU¯úúúÜ×6C`221F?"ÿ–Xëö§%cuLéq`ÞkzrìÀþ 8¯¿[ó-ñp¬Û5z¢;h ˜ç“”§Z¨¶ ÔƒÁ퓉Öï5ïS°8‘5 ™b 9٠͈`>Iê|b0r††Äè7ý{ JfO†‰¦ /¤®¾…(jªù]ø/¾hÅú”vŠß¬iуý$íà‚w2±¸“mÇê%Í¢ÆÛ¾SCEá˃ ·Ô$éBŸ"à‘ â¸)ê=†0ÃEÒ0 #¤%4ÃÕfNšH×üi ë…!ú7+Ú‚6kºÈþDZiL +MSÈûá:¿Ýx-QåOáýôùÄ-É]ޘєá¢'!Uã>^ùÓx’ÔĶê•ç«f,Zܸ¡ê`!„¤ˆ)Aè s$ªýɸa§ iV–& ¹_sG·4ù½Ç•3 ßPÞ92‚ ÞôéȃS+Cˆ2J>©A2ò·ä©com  8z=nwÛŽ?8€Ö+Ëì¬ÃëMÔŸÒmáÞÃvžsåµ­hwn$ø ïœøÈˆ§§wK9&߬Tvô2sµ¦hv%ÌF£§Ã‰{£ÏSCáÀææª§±BÉÅ2zO ’ ß÷ÚìŽÄÙÕ×`fú55š‘à»x?£îøE »ËsZbÛ¸©çQ‡fpŒ½®7ráH¾‡peÛ¢%¡Œ)†ïì62 §¦ 2aF©~ÍûáSþ!=ó@ËÆwßÙƒkæo×ÿÖ‘úmnSÔè‰îÌPcàôù„  l!Ì'|“Xx`±c¼cÜ› ólÒ Õ˜OÀøÇh>1ú«µ¶PA ð>š×™i iK?M9mŸ&I3}ìƒúVû¾[~Kæ ~ˤ¤\ÐéB@MSºa¡„0Ò-JšVùÓõPƒ6{-èð»¥ÆˆtD G”HSôœË{ÙFÕN˜óéEMS4½áõhÖ5]‰Æ˜ª­Ã9¦üÛ¢hŠ×ÛDÞ×ô÷ëÛHÿVíŠ&:ÞÏ'˜·áÿ7¤ß_iÜÇ3OB’þ0¸w×WŸøëޤ¤¯ì;Z#3&–‰êÀ·‘°qÀ¥ÃB;¢’Ƒ𑉤ú=#£[Šô ;ü,H„IäY4cø _Ñ„T¯:Ñîš+YÆûÀÑ š$j”¹O‹€ „ߎýÇ“Žîßó&Zˆõ|ßf†÷Àž³ævøÚÇö" ìÇ#Å0x§™úLp2EQÂ;A7’¸7 Xm¦0aÉ  db¸76˜¯àXuÍÌp¼³ýzÓ ¿Í4ðï#“DøeÁ‰ÛðQ0´V½\UV~O|—!|ñ~ý-麹7×OLPB˜1®ürãouíÐ}Ô°æ÷ÌsºÞX ßh©ƒÕåxÍÛª™“ÊÏ“…×Ùdöe¤éɉÙŒQ¯æoXµoÿ˜Û¢ÛçŽ=Ñý f D2Ÿ4 ²GÔëòÀÔuþ,u<’øç» AÁlÜ’Fº€¦tÅøÞõwßß~~§}fúh ëæuÞËg)18‚6¦?8Ÿæ[/ìèº5nX‡¦¤U¬Wi€0Ïë¹× +ý4…ïÑm`ÆÖo¬Ïé÷D{ë1¨½ìËHÓò|h¿Yó7̦ÅC1·)ªüi0x‹< ‘: øÓ¸óIâZ›šØV½ð·g>þå¸ý©×>(øÎ]7$§E!_#‰$ ƒñÛˆZ“Œ+V´­&Óg^]×Lz0Œ?ú]$Ø\5Sê~e\É¢i}t.šñð:Ûiq"¬*àçñ¸zÛß[¾ì-Ô‡õLµiæ!„µÍH[3èó>¼ë6²½§Í¸ íωÿ#…wöx¤p¯¡­Ç÷–ŒÕW®j÷ GßʱºO€1w¶_õAE|2„Ö¥ëå½dh ó'¸3• ŽÒVaÅ—{ÍÌï>]HÒõùšß€a¡O¿½úKáÈ0u1$Ý6Ý×áܳ͑Ғ¤ÚÉ@d-–ŠÒIRŸgüâïH)MO¼nwë»Ë^xÍ$=‰7ZBèÅ”žð,ÑFM§ÿÍÁwe;Z­¾K†ûʆ)¶­oN)üëVòý&ð5?©j1‘ß·yCÐþþÅóóúÛe`ýýës¼O ^éþÑ+=HX›E¿G˜ôQSeÒ$é÷ñÒ"]÷¬×h§”Œ¹WÓµ7ùi!ÏŽT‰å˜¬Oýðšéfò|hc<Ò :]‰6:RxgÇF÷ñΟƣ&‰‰«¥¥¡cçÆõ¿HZzî>¶l÷ ·\1åÒƒÄQƒò˜«JT¹S@ÒÑ¿”:¿o…]3$ú¡Mkõ{¡QŒ*™>½’¥},(©Õ,0²ü`øL¤…p«®o‘ï®üsWWÃÓ~ƒ›fnôêH¤¯ŠäyÝ…÷¾ö9Ù^¶;ù²«¾)þ5ü‡ ï†FŸ~÷pãÞ!l‡n ¯qL“¹1ö<î×–êñÎûYŒgõÞ¨G]À]6ësõ16ډ̎’ô»ô³Ü›Û¥™ÍØf8}¾Ô.!ã·Ám¤‹†e$cÊ{ ^²Œ~2yÂ,)Î+´[#=¦4=Ù²ö݇;:G!þh ász¢‘1 ëh_YV"'aÎÒƒù©IKYô{Gš¦°-ú[¤Àdþ¾õq¸tÅãá"‰¡©¢¥‡6á3ÓmÊÇw±p¯é‰Þ“¦ž°Üs¡HíqlüŽ®o‘jH4n#¡+¡¼>^h y=ò|h{<ñ&fPžBW¢ÍŸ7ÞÙ±xÁ}œó§q©IâGBF¾çý7^ù ·°àqß)ω÷Ž.LŠT£ÄÁHâ¨%¹bM¡…$mó¬™;°§´%¤Â÷¨ 6Òæ•rÚE“R0â 9Û ™À^0ÀÍ\ñ%Cóá®CIûwl]¶åýÕÛqâù6-(Å“&ɇwÝN¶» ¤”+Ö7FŠÿáÆ;ÚÜ?a#îùÞ`Š—ÁÜ;Ø=¬‡Å`B¨]C9Aòû1Vy©¥ê_MÖ ®S·CïÍ+Ë¬ÓØúÍgôsñ°g›Ã§%˜sSk$§/ÿ茩‹`ök0ÁƒõMà TeDèÉ¡Ý;žýpÕÛÑFMOâ–|¤k1¥'| KdcÀ¨#ÐßéãÇI}­Ô³³RM#ÿ@íó?¯Ûæ>œßü®Œ‚s¢¡74Ö¤)Êtó2µ0Æwh!‰Çº-¤üVôÌ@4…÷ÆSa{§+¡÷DÃj¤hʱ{#¯‡–“¦Ä#=Ñ@=…®D›?n¼³S#ûÑÀŸÆ“&‰8ÓêL~(ŒÃíxí/O4sòÉ“ÍÞO^snr´|”8@H<¹b­V« Æw÷™!‘Éã 1‹?s§Nñ‡ï`Qć$~jñæ{ß3¨ï ¢Ú€·ÐÆ“&vÔ íÛ±åå•/þímܬ`‰=—%yLøRsct#\NÃ;Úöf¬xñÙ·!¦ˆ‰þ <Äï„§Æçpážï©Â¾cY¯(Ÿ®¥ÒßÞkøpßl"ܦó#Õ¯`ÞΘjí8&iü ‘|3£D*Ê&ó*œ†kL™éÉÛžçùg–£¡ü6ã•–ŽÃFOø2–pÆ€ñdà¿sÆUÈú>MR抢‚aÇàÖÅþЦ „-i…%îMÓC‹ÄÖð|?1æ]£ŽÑGSØŸXŒ)Öë_4ÌF‚¦Ù»ë©7žyò9´)žé‰Ùit%üépáIÜþ4Þ„$R<2ïˆÛ¡>štØñ½pæE—Öyοø®Ÿ=þZÞüéã½KçMMZ0½2¢Ô¢!÷zãûôÊ–>j½æûgža!†Aܮގ +ßzvûº÷wà>‘ªlîù›ð%œûgüÁ2 ÞÑ&ɲAÐ[ÑXs²~é¥W~øÏÿ„y¬ñNXò=z¯ñmÜ«ÄÙÝךÅoÊ\4ŒÌçFã1ûʘªk­†O¢ÑÓñå3Tà–`ú­á¥aÌ}´Ç”?=¡Íø‡«W<µuí»›ÐÆx§%ã°Ò·PÇ€~.Ð~ö¸I>f¤©Fà÷üjÏHž×ã} 6ŒUšÂ¾F{L ¿áSÑ”Íï¯~dÓ»+ßG›â™71ƒl@º þt8ðÎŽîG¯B’Ö$ñã!£œ†iýîî?÷šë¯v»]—BÈ‚½9éÞü¼¬$»Ífp¤Ä|„ÅŸèúÿµz=õsþ¿õùP÷ݽ½Þ–ÖN/¯¢J8:¸k~‚Bµ@IDATçšÞXþ^wwWêãF¶÷íylÖ$ʱââM|NÃ;ÚÃ1šLoßÖ-Ͻúú ð‘]üçDÿþxöÿlüqíÿ;œ:­gFüÇþí–7‘ÆèGMc†<ø·UAwÊ ùÿº"¿¢'‡vïZ z²ÊáèlÃí£–°WqAO4x5ÎýÖçý÷­07CÄ"uz[c‹oŒøãÛÿ·=Öï±pÇT Hø!ÿßžêüi4Åíj?¼g×Ê^_þüŽ›ð|¼ó&æ.¤+ÃÅŸFïìœ?®ý›Êñi¸ÇâýhäOãMH"¨Ò¤=9íS¹jÉYn¡È¥ÕÕ!ï<ÿôË8~{Æ‚3¦Lš1kNFvNAzFFnjªÂTB— ¹Ÿ»í]m-GöîÙ»¿jë1€" Ô‘©iíÛÈäž„+áK8ÇS ˆ÷¾Fz ø¹W¾ôì;+_’UÓç-š0i欙™Ù¹ùé™9‰ˆÿxBžÕ–Ð 0ybUIN¾('¤Õî†Æ†=dF´hzâèho>²o÷ž};¶EƒF#-!G==ñBK˜´tÆdpfIXܸeï‘d·Ÿ*vDGŒõr ƒC@ч£Í ){víÛ¾åžm¼‰¹“éŠÅŸšÁ&sŒð§ñ($™¥u†±¢€¤µDz€öâcÛƒí ®)Mö¼W߇Ä)„áâƒ Ž©-¢@DÆ‹BRKß1‰™žx2µCsT ︓Ym`#ÁMT¼+ YF'¾û“¤¯£åJHZÿöª§7¬Yq$z2Vh A9&èÉìÿùé7’s²Ë`ïôƇk^jy݉8'V,±DOtƒ¢+ªÁ¥èð¨æOãQH"t5ÃO­‡|̃“çiѯ|V°Wš&ìÉ0³ègŒ_có/áÁ¢ öå"lôJ 5IÔ QXŠg-š§Š…w k?f!`³%'eåx‹uì—Ã8¦iìH•±HKËQOO\mmÕi’P2¦O+€´—ÇV± Ç«ôDƒ|ÔÓÝ‘îÇ̈W!‰¸ã@d€\ÿ¦@M …$¥UÀžýЂR"Hè®*.hBGx6’(QHÒ¾I„¯Žñ\4žÙFÝ?¶ÛÂ{jéŠî@ ÷cf Ä»¤qȉMOpÔˆP0 GZ@JTŸ$ý±rò× Ž>Ö°Ã¥QWtÛ¹·ð>êÐg5x äåy'èóÈU½Çú÷íI?Xô÷¦iÇX¢%æþÅ=™øÍ{çeÍó_Ë߸»îo/5(Lôýiú`S[ÁuW¹Ó.5)%¥"oé‚ÎÖ Ûi!` ñ D¡'þšnÆ]Ñ¡ý˜£EH"žµ  "W)éèff R¢h’4\n„Þô9Þ3š‹…÷ÑŒ=«í§A ÕþÿÙ»ð6ªl}$¹÷Þ[zb§w „Ð{g—²„mºìÂ6Ëml_ K_z ¤'¤W;Í©¶÷ÞmIï?Wy,K¶duëžïÍhæÎsÿ¹3sÏ=F(;5FíN<²\¥=þítZãEЉÇâÀaC·æªý%Aïÿã™Î’^…ÿÃÏ“òÎàµòáµ²ŸËø;q[Xøãvñċ׾#Ã~xgbØÔ©¿¤ ÝBCKëãÆµ›Ž‚Ÿ^Ô~ì£_Œc±hS¯½6BúŒ$‰€Ï"ÀÏ“òÞàõP}Ÿˆ†ži«O¼W†¼¼2}ÀŸ„$åž+?tLjáHù/ÀÒ¹© .CµÙJûä}ªw8ÚõØcó4ZZ3Jyp#BáIjbÿA3=þ· ™º }vW{üøæŽ`}LdíXm°!_g4æ§eÙĸWyó‰Îm(,q-ʳæ\¾}¶ÒF¿O {Lg–ö}|¸4jŒÏE´•lÿÁcì;j‹öá IœœÌöFÞ–$ðaí}¢Ü ¯½W|h=$ú€? I–}@é”–ûåÿ¡€¼ïCûþÉÖEänÕÕ)"s"Ó9z졦^æUÜè `ÃdQóÙcÿS«h˜Ø/åŒoŠ2¦’ðx³Q{Ÿ‡§¯†ùC™&H;cßMß«¨ÑH)[h$ã5¢œN;kž±–$ø>{¯ø>þÉáP’üyɵD@"pº»óU.T¶Ÿ|1$ߨ­¯#]!´ [ŒFýC«VÍû×üù«Ù$LÒB¤åu/Ótéwå¿ü¯#F-íÖ’f7i5[ o¾{WŸ¦j5Edà±– eC®%‰€DÀ½H!ɽøÊÚ%‰€̓\hŠL–mp6é2$4Æ+ Æ®Gaì–gÔPø¦Ãkæ<ùœö£F³›…§xã„CK—ngI~Œ@ámKŸûO_ûöÛºƒíãôFÃt£Ñ8Íh4\Šý¼ô"Ž á&åÌý§W!ùG" H\Ž€’\©¬P" Ø@ÀHfM’fTJ©Ÿ-í^m^èí·¯Õ×<‚ŒÝùªŒÆË Ôõhfç×8ü —‘äÿ¼sÝul6ÇþF¼ü‹UêŠ;¬é®îP 笷ßßtÝuîðK³z}¹S" *RH Ô;/Û-xø–ôh،ʂ~û|djqÝ{¿|t©áG8tˇ\Äh|L«Ñ<&’Œ!Bã_[v¥^o¼ww4´ŠõˆèñÏÂ[—.³l^áu×u¼òÜa”c$mS{-÷¡m–å䉀D@" p-J^!×Ö*k“H$^AÇHc”aD²=ôä AçhŒí ^gMÏ^ÓÖÓËäéeAZî—ÿý‚WŸ¿Í 7ü‚ÑûˆàQ« Ò-57濺ì:«-2ÍZG£FgÖFZ-+wJ$‰€Kš$—À(+‘H$ý#ðô¿Ÿ÷ûp.Å‘í¸«±¶×zãH#~‹}ñO.Ó\Ìæx#¢l#|Qn2hB_0¥öéu–üã‡ÀÿèGæ&hŽÖ{å¹»öÝtçÖI¯¿ðH·^?šóvŸ&i¨HfçFC6²OA¹C" H\…€’\…¤¬G" ôƒ€ÆØ­Üö1µ{t©þå'Ÿ×Á¬N»K‡]F4Æ6KÒêt>zWË€á¢û¹¼<äC@X ¢Ì¢Ý˜òßÿäê †iØÑ`Mô‰BVÊ!u?RöɵD@" ¸)$¹PYD@" °†€Ed;˜O™Ã:›‹G…Ǿ×ÜÒpe7ÇBÓPôè݆Ÿ›šÈæ²rÃ@@ŽímZŒ¼†~PØÑÕ¹Bc4V…Üf­Uêw8.Íí¬$÷I$# }’\ ¨¬N" XEÀFd;uÙæÖúgHcœõB2Ï#ùØoŸÓ> ÓUêrrÛ¿Ðh‚ŸEÂà8n…F§ý@£ÕÜWô{æè ]7Lþà%±_ÝBáN£Idaz™ËîÔÇå¶D@" ¸)$¹SY£D@" èƒÀ@‘ížy.a¿Ã =ú1”L‡”á0ÇÒaÿð>Ê~‹@á­wî†?Òß&¿ùR¬è Hoœ“ÿÊ¿>Äö}M‡Ç½úÜãêÆq„;üçh‡LJ„;Ó?ù+H$nA@ InUV*Hz(²—ìÖx gÎÊ'­Nñ[š¤!í®žÚäÖP@ à•e¿éìê\_£ ØIkÔ¾@ZÍoav÷øþ[—þªOe„»>ȉ€DÀHŸ$w¢+ë–H$@@ÙO÷‰l‡ÞÝqèÉeÚƒO.£å¬9Ò iø¿cÌ#ww¯”@-`6weJNȘÕóooWZVðÚ‹ù¤é¾Qùßk-#Üõ‚Cþ‘H$îF@j’ܰ¬_" xÔ‘íÒÛœóƘÚÝ Ò“Ð(1’þYŸ¢ åIJ’†Žn—×­nVá-wå‡Å=6éåe£arw’ÈÎQŽs„;e‚³ ÞІܒH$nA@j’Ü«¬T" ô ­yP«Aþ# r{žÙúí2í¿qìƒGïêþ».1íærfEÙ’r5(‹¿þÐÉÃ# å£;@{X:_{í0D6,Ó@V§{Ki+ÌñŠ ¤D9ÔÈ0à 0r-HÜ„€’ܬ¬V" (@ÔéÔ"´·²_½†¦à?dÔÿé·Ë4?Ò…ÜÿÓ;;ªËí¡…@Q{ýFñ~! Á”‚ш@µÔ±½ðÖ»ïÂÿ^’´>,榽N[§D¸ÛtÝumC Ù‰€D@"à;H!Éwî…äD" ¢¨#Û5¬IêK~¯{¿3þï… [»õŸ?õœöýÈȸÇÿç–ÚÆ¾¥åG ð¶»ŸGxéEù¯<÷zþ+Ï/ÄÎ^~háæwánmsWk'·c‘$H$n@@ú$¹TY¥D@" P°'²R–µ?ûžþ•ð¸Ì£–j›Zëvýîù°aÊq¹4û´cŠ–šµ.I" H܆€Ô$¹ ZY±D@" °/²ÝŸ^Š‹kïjžn$ÃdYMj¯+›ìbᣲ¯;"´^ú% ížÄÉaÚǵ†óà›tV«ø¤Y¶~JDW‹½Fƒ’,á‘ÿ%‰€ B’ Á”UI$KŒ†îž  6"Ûµv6>%Ò4£Q³]«Õ¼«¥ÐG~ú½¶RS] –UÊÿC˜Ï=a4Ò õíµÉÐ"Â͸5ˆtóöÜò½cÖšÈîWéÌ¡ž` ÖÊÊ}‰€D@"àRHr?y¶D@" è  ÌøFÚ`þg>ïçK g’‡*Ǥ?¾œ¡¼¡ ~#D§yaç ß=a¨ÁZ³e„;k¨È}‰€DÀ=HŸ$÷à*k•H$ˆ=f³(ÖzÐ U`"Ày‘vÝxûq{$FHD¸Óhô¼­D¸ãmI‰€D@"àz¤äzLe‰€D UŽ$[‘íz Ë-‰€m8‘‡Ï”P"ÜÙ>A‘H$A#àËævHA¼(¤ÞVöɵm».ÁÛêÿ¶Ï’Gü ùŒøøÝzì±yˆÃ°fŒÂfg}8üNštʹ–8Œûµi,Ÿgè6ŒÇj—ÃuÈ<…€ú»+¿ÃžBÝýבß^ç0ö›ç—„$¥Ó±vK{Û}L  =bR.i5éZÒD9wOól£†šÈ`<ê £Á¸âßÏ<¶ HÈ—µvñŒÜùðcÓa¶u¡òl`íŸÍú\WQUX­‰à–ê áeµ?yÿ{ýv˺cu-9§âD—¢”†¶ßœýèãwºïj²fg0±Yùþvut|õʳOq^+޼Á‹ü;®gÏ•ãS7àíãS_’¸ó±`¤›7Üéãö#ƒÁ˜ÆvÚÑa†¸˜MXp°4 D'mïê2Ô7¶›ZÛµ°a|é/Ÿ(ïîÒ?««5þuÙ²ÇØ;\-Ñâ ò  ¹ûîÇÂõ šëî“φwÑ% TIÝgê Öæ…ŒÎM?×EUËj`­‘N‰16QHtxöèÐÈì…Âç›­þþjƒtÿËßßö¶ö¿Ø¶ïï«WØŠ°™"0ù|{A9>uãMW?¾:>õ¶$„#܃àôà1qñÿ0éùÃ33 †Ó„‘™šˆ°Pišâ\'Âek{í-.£­…GS÷—>­MÕþÏ?ýÕ]/þßã+P=¿¤%ù&Zܧ µ!Á/ † ùløæM²ÅÕ¦}GhÍNÓÑ©cgÓ¢‹m•û%v!PÜÐD«¿\+Ê¢Ãé%óí:Oò Ö¾¿OŒ™3ý‡éãGüàÍ¿ýáKpÕ…E–¼Â¤¼¨UäøÔ*,.Ýiíùð©ñ©·„$E:çë‡ÜúÀÏþ',<òñŒ”8ãõÌ¢Q9i|\’ €°I³ÆçEsød9ýwÅæÔ²ÊºOïzô~áÉ_?‹KÉÙ,â킪Ä3‚ûsr£<“ž+Ÿ €êé*jê™/™gvM2ï“GÈŽ„ºV\Ð(µ´R‡^O¡:9—è(Žž,oåû›‚û÷Æ>¿zõOÿ¼tba¥³ü{òÆX¿–ŸZÇÅm{­<>3>RœÛZn½b¥ãpØÍ÷þä~HOL7Lóðw.ÑA@²~–Üë2ãŸ~÷cŽøïï|ä±ûQ9÷)œº e§*Ïß¾?òÙp K¯ž\Õ ’bGy•yñ¡@°VK¹Q‘¢1l+}¤±yh4,@Z¡þþò؇Ç@hzÉï°wûŸzV’øÔøÔB_SHWܾtIDT̯14ÞuÕ\MH°œ óTÿd¬sÆ^«Õýßwz”퀼Ñ<ÕdºŽ–ïßùløÓm³äÕH5 GÌ;“¤dÆBn8‡ÀÈØž8FRHrKoœ­þþòˆÇBàC-(yƒ-yMÓHŽO½ÜÔχ·Ç§žóõØÄ.4'gtBJFÖŸ²Rã·]:Gj0¼Ô)ûÌ”xcXXÄó]t3OOzºOx©å>{Y-ß¾òÙðÙ{dcõM'©[ß.ÊF†§PXhœ]çÉBÓÐò|”$ù'üýÍHŽ# ñ˜­ÅÂc$ùöü-•ãSÏcÞï}a|êÉ‘!V…` Ÿ½dÉR˜ ¤ÂI'5Hýö·dìo¸p–Žf¤ û1.Æ÷H ­nEÝfåâáûÀ÷C>6qò‹U*¤d©Eò‹{æ/LŽˆéÑ$7I!É_î›%Ÿüý½qñlÄ+¤Táx8#Éï°%Xîý/ǧîÅwPµûÂøÔ“B’YJZ‘±‰ wŒ™e”>Hƒê;.=‰ïߋа0’ä,–KÑu¨2ñŒð}φC¸ùdájµ?RÜhŸäQ2埌ì¥I’>IþyM\+ß_a[sHm’ço¨Ÿzs»®¨<ÞŸrÇð©¥ô°s_6ÊŠ$„ù– O oÇ5ø^ N}êµßûñL—³Xv`æâ"âaüù>ÈgÃÅèz¡:ÙÎ  È%•wÜܲ3î¤éC²™¦÷½&É46¾IR›ä¹;-ǧžÃzPWòæøÔÓB;Ä…'gfÍãD±Èƒ4(ÀäI®G€ïß“ˆèèóQ»’\ñ@5Š5ã/Ÿ òã2²Ü'äRF¸óÇ»f›gåûËc#”b“;+Éï0@ð)B’ŸzìÁ\By>¼1>õ”Ä×ážgG‚CB3£ÃC]’o À÷":"Ì@Zm.8â{婾áxŸ Ó3üù>ÈgÃû7ÄYdd;g”ç÷‡€Œp×:þuL|1&â±8ç(wŠ&I~‡Ý+åøÔý;uoŽO=õòuDâX¬CuÁA)q±‘,½Kò!âb"´­&,I¿$ÏßñŒ0þ|<yyEW"PßtBÙ.YF¶s%¸².€Œp7´:‰xl„Vñì1 Iò;ì™[,ǧžÁÙ©«xk|ê‰Á CŠ:SJ:­.",8xH I]NuoœÌ÷™Ü9l’¢æÒ÷ÈÛ¸¦ùaü‡ú³aƒ!µ»wd»Ñƒn[Ww79ó.éèìôµ}ùă'Ê©­£Óë,VÔ4RyuƒM>¾ÞRDÅ%6;s@áîH“ Þà –¾p.¿÷yl^I~‡ÝcÌß^\*`Ƨ]ÝdäLÔ~DÞŸr§ð±0ÆþùÕ?ߣ‰£rèšEˆCâÍ[+¶PÑÑ2ªml¡$ü›5~8]>o*}ðÍvZ±i¯(ǪÃÜôDºî‚™”žäc¹SŒfÖ´ÀCþLψ  ‰Ÿ#PÝpØÜ‚¤AD¶ãwÈ;+·b^/>fIqÑtå‚i4m\ƾÿ}îCº÷¦ hܰ óu>Û°‡¾Ú´~ÿ ´yßâzYeÅEGÒä19êß\–7l½×zRý©¬mï8äUSíuÿf·^Oû¦ #³ÄÅôzýõ¿_Ñw.C3ò‡¹Œõ;šCÏ& Ìöù³ èœÉ¶…ÜOÖî$½AOK¯^`•¯6ï£s§Œ¡‘Ù©V;³S-$Ë\IÎ@éC犱Ë”ñ’ü»ÿ½>:>U¿—ÂCC(51–æNCgMiF†Ç°3òóèŠùÓÌû>^½ƒ6í=BOýøZó¾µ;Òú‡¨¤¢–BC‚hXF2Ý}õ<ªÂ»Ç¸LAA:JÆ÷æÂ³'Ðì #ÌçúĆƧž’I×âÁ‡ëSBßüs&¢3ó©¶¡…Vo?@¿ý÷'ôÓï.¡p#…»þó¥¼£›ZÛio}¹…¦ŒÉ¥ˆpëþ³·_~®×ÞyÑQÍ3!§š9Â]¨Ž?±’ü3c#E@2ÐùvÏMUcì³ãSå½ÔÒÞAûž¢×–o¤Šš†^BÑ@ðlÚSLo~ñ-]rÞdºëÊyÔMü±SUÄ‚—Bw]9—"ñ®Û°ë0Ên»/Âdzð òÆøÔõ_ëXª;¢OÎŒDG†‰?£†žxá#zïëíôà­‹­·Èbï‘ÒJÚ}¨„nYr¶˜µåÉÐ$©)H«3_£µ­ƒÞùj«0QwRuy/n+÷Ë‹,Ü¥%æCè–«#Û%;¨Iúbã^ââw/›áÆôº¼fÑL˜mUÑçö !‰5Jë0ˆç ¦š†f:Y^C—ž7EhšÚ;º„•’M¼ØCo|¾Ih¿OAû´š¬™Étñ¹“ÄDÑgëwÓÖ¢c⃹iw1´R³i¨,âïgë÷P3„ŠüátóEg ‚Í9>^³ƒv8I­øGð‡ú‡o»ˆ–£.ÖÒ×Ô7‹6üðú…ŒÁ= v<Ù AììI#i„<~¯2_\ÿÏÿö.Úž)ÚÌš´kÏŸ)Þµ¬Áoå6Úqð„кñŒêU §SêäwòLzM¯_BË>æMKçMcåÍÃ0ÓÊm.ŤÖhä’c>,ùÞŒ™ZƒÁH·\|¶¨Íßø|#øío0O¦ñmIy-}±q, N /Â^6,î¹f¾0Ù{í³h îW Úx þý‘áîLíø G›i\|l§Ècþ?ôÊ÷€×’Ü‹€kŸŸªßKü.≢ÏñÞ7mœøVØÏ«¶ÓؼtºxÎ$sñX4©‰ß=l-••’@ÛðîÛ‰7µÆJ]Ö‹ÛÊýò žìJÃ|þ¡Ç·œ¦ŒÍ¥§«zÝ„}GÊhKáQóÒ®²÷?‰ ì¡0áC¨g ÅGÔÉçï‘bæ –$î®@Ñêp&²›ÈÍM7 HJsÆ K§ÒJÓ{†…¤†æV:ZfzG±0“-üþÉMO&vϽ¿Š ”*§¸nli§ñ!e3ŒÛ0èg“ah˜&ŽÊ3Œ¬UaámXf54µÒËŸ¬§9SFÑ=×.¦}k!¸1ñvûþô0´ñß¹ä!ÄÝ­KltñuX‹^x´Tai0‰‰ Ç;7~!jf/y©³¦‰±‘0UKÁ@ R\wþŒq¢~ ; ˆ1}³’-…Çè‚ÙãéÂ³Æ “OÖìÇ:»º„ùó·{Â,z¦0s~ëË͘Eµî3Êuî‚°µa×!z“d¹éIÙ—ž¬òÍmijmÇù‡gxù~„Åj˜p—c¶W¡  c훿(߉ï]5W~uù  † å…”™/fq•óú[PMÄCH’4$à ‰ùA#¼ýâÌï½Á@%g¾Œï©ªzóؔǩeø¯+›Ûhœ½ãSL¸1……ôh™”º|`íÑ{ä)M’®Â¤ËbŸoýEfêìÒ÷r^^ef—¹mëè03]‰alT„˜‰5ï´Ø`Ž?\KÇ!|±}ÿ%çNFÄmÞo Žä_‰€DÀÕ8Ù®±¥¢"úšwE†‡‰Á=kNÒ“ã(-)–ØÄŽ5<°gS;žadzäö‹é?`þúß•Â$5–šmkífÉ›¡ gª®o+BXƒÂš™-œ‰mÛ ¼LÅ„kàw‚ŸÅ°e?J´òüN7Œ£S¯íÝôÄ–ô˜r 3Ì™ãD™‰#³‰…œÇÊiê¸\ŠÇ5NÁK¹®¢™•âgÓž#К £E³ Ä.L6BÃuå‚éJø…N80V¬ýbãeI¬±zó QÔÝm  ©S¿£ûð­ª BeU]#Ýwó…bÆ–…Zž‰Uèà‰ O³¡Í F[RqÏNŠïI ¬ XؽçšHÃJ³ákðìë+„Ÿl"|ú£‘1Ñ´’ÊE‘#Ò/©?¨üä˜ïüH'Øôý{&ˆ……ŠŽ‚µA¥ò—:ñàq,˜a‘ešð¬üðäUk['ÜFª‰µLF™ÞÝVŠÌ.OI~)ð,ixXH/{M6½³ Ü ô”(˜ëµ¶w*m¬b³hÖxaþÁÕ>N~yï|SkìIœ­¡â§ûÔA’ôGâ&Ç mKÏŒC34üN 2½¶§† GEp#¥ТLPŠ v`keØŸég·_b>nk#¾” ± Æ %¬õ¶¤ýø(7`vò©—÷:SlÜðLavƦnJðu Ö©mÝ÷.…)ánª¨m4›vëŽÌÇþW,d¨ý¤8ð p|L!”Ãi>; a²F,H>ñëE›ÙTï_ï~C‘YXc²ä[]‡¢ÕË9ð‡Nµ“?6¼lî”>lðüã7GÊ›;m4"êõ&,Œ­GHÚGþúŽˆì÷#DÃSÓ Ì¢?Z+r'é!$°Ôµ‹×ÞúÆV¤øXh¡F禉HzŽäbb<8WÞÖ¢£ðÕÊ3ûQ1¿lÈ1ØŒ®Y0âÈvŒ×Ò«çÑ«úÀùN˜¿‚{®¦â¯ÍŽp—¿¯ãM-2ÂM”䉀# ¼—¢a6Ç“Fü-àÜwŽÐ%üîÅ …óu~ðÍ6¡‰™&rï9RO •à<0wÿâ7«¹Ô²'~9×Vˆ§ËØ 2 ëÓn¹÷á?OÊ5î;Ãkã¿"p6x¶+÷'úã«_PQñ‰m/ýî‰ËÁ7{ú±­Ha¿?5Æ¿x5?#·ÿäåÌ>TŸ ÿº-ƒãö÷oŒÆ$I»8ùÞëöÀ¯qð £»»õP…©„£Ü°FB§Óô 6ãh–åY`kƒ†È2o›Èqt66qcíÉÃϾM7"HÁŒüáBHZƒt’^‡6ðgß½X÷aMÜs˜L‹êm‚Íß &GÒBÜ¿i;­,5oxrædº4W:[{â>»úüýÝ]txÿk~æ^ÔÍ7”CŒÉï°«î]ŸùÛ‹Ý1>åæ›Þ¥¡6ߎ!òƒŸÚ!Ÿ Ø8in7 DŽ฿ HŽ·Rž!X"`ÙΉëæìçêÁ¶åõúûÏþ3êhœý•µ÷Ë'–ŸËZsň…Žǹ‚ê á(FP‰=Оpî ò¸<·Õ–€Äøº¶$>Î×÷¶€Ä|pÛ­Ý3´Gup¸f“:zÁæÝ5PM,9" ñ¹áN¡£Mʦ\K$«˜Þ¥VÉHs; @ä_‰€D@"0Xœl7ØëúÒylâÇæÊß"4÷Zh8×û%]€FJ“Fç gR#-_¿#„êæÈ¦W.˜FçNí4$Ãcz’–Ë\INÃ)+H$f¤d†BnH$çp6²sW÷³óœI=p¢[^\M#ÕB’Ì•äjxe}‰@# ÍíøæË¦K$®E ¦á¹ÂÄAäH2Ÿ,7$v" #ÜÙ ”,&HD@ I&‹K$[ôÖ$²U, ösrÔwWnu¸­þüpbç°â’F@‰pÇ%WƒŽ66|’,!H$" …¤!’$‰€}Ô4› &¸&‰3önp»™ã‡#DmÿÖà'NWÓKý- ÁºB¡#MRHR°k‰€DÀ7à補`Ûߨÿ¯µæƒo¶‹øîœïƒsq(ô¯wWQtD(ݼäleW@®š[E~¦*DNzæÞëÙh‰€7¨o>iýžìTèoo¶Ã•׿0äÿýâ[‘aᬑÍ5Eœi¡/‘³ƒó' ËL6_¶­½ÑßÊhŽ+«¢²ªzV¼ødBî"NŠÈ‘óÞþr U#i÷²÷WÑâ³&RòÛ*ˆŽ¦•"j4Ñé—¨Ý@¶ÛKœ<]CO"§Ý°Ì$úéw/1s±µð(½÷ߟ¼É¼/Ð68 úWl¦#¥•"êç„‘ÙtëÅg8 æ+8ù•&‰ÃÃrt Îì.©£H<ùïO‘µÞ”›¥çˆÜ’H<…@u}?RRl`›Ú)˜o+:NYi ”‚ˆO½ø)r,uR;r­ÝqIW7‹d«Iq=ÑÙø<>¾cÿ 1ëXIŸw¾ÚL }’×~õm!í>\"!òyÉqÑ´`F>YÖ¡\?PÖ#b{0”îå®ËvúœÞà„¥M{z¬ |‰?oñrººž2 ý×K¯ ]¿É´OÒæ}G½ÅŽÃ×õM·¬Ñ’xVñ$%üÎ¥s¬6võöôÍ–"j€ 5³Ž3QÌRr–ó58Æ—¾úvŸÈ?rÅü©"©Ö÷"ûy( ÕÌ‚á¢^NˆÈ3•;žeùC¬h°ö.¥/QGie-e#Ï_cl^:ýÙÒ¯^8Cd»gÎ4d\¿qñl‘ûƒU¯|²ö?-òhL•MW-˜Na¡Áij­ï®ÜB»Ðy8gx¿fÑ 1{j‹uãË*ëhÑì‘HqåæBõ!¹-xµ?RrÜ]Õ·/3 ¡¿çL6…¹Þ°ë08^ŽÜ@qHk!°ù½Éï¿Nˆ†¤'Å"ßQï¹;Î}tÍ¢™â½¹ åŸ(Yâb"©£«›Ff§ú6àNáî´k’$Ï"À‰¶çAËÍãÓ)crÅØÎ’[ãSN‚ÍãG¶”úlýn‘OmÚØ\š;m½²|=&‰Úhz~ž8®ä¾caŒ5òÍ­í×fÐÍ%rÉÕ#7Ý;_m¥ƒ'NSLdM—K—œ;™Þ@2ë¸è‘Š`/&šÒ’bèöËæRJB4, lO¹ |­/6îãêœÔDZzÍ|ŠDÞ:[<¨Û=cj^a²lXFŸ,wIúõuܵÝûk䮫¸¨^îH×.š.n ›aX#þp^9:=xËb‘õ|ùº]¢XgW-£]zn¸p6q¹?\Kw£Î™¼ܱZ»ã0í-.£ï^z]zÞzÿëmTU×Hœ(ñ/6QL;î½ñš8*‡Ï؃×àãôf=Ï›:†B¨Ú¸ç°˜õä:9Ñ!›ƒ,½zêœC[ a`p\\î£ÕÛi Ô²ü€]ƒ‡DùèÛâAœ¤úá\çÔEÞ$I$ÞA@F¶ë‹{’£*Ž ¡æ¶mwnšÉ<®£³9•ŠÅRÓ KJŽï†Ÿ X’z# ŽpWÚÒBôH’H<‹Àb„øg_LeÜiyu[ãS.ÇãG6KãqàyÓ­Ûyˆž}ã š=a$-œ•O_còÿ(LÖ˜X³ÎqæLE÷\»€x¢|-Ê3}¹iñ$ýo8ŸŸ=ž2“M «ÙÒèã5;).*œnÀä}Cs;ñØ“©¿ñ)›ÉñµrÒ’0v=—ÆÊR<ˆJ­ü´¶wÐI¼ã‡g¥X9ê›»üJHê†Ó×dHè<ûøÖ—›­":š)cs(a44IEÈlΕB—Í"ŽŸ7mŒØÏZ –´YöòåÕ ¢è¾â@Y”š-!¦a†“g;9›:«UCñÁ^„ÎË’¸BÓÇå¡ãަKΛL¹ŠAs¤Ð"2#Ð9Ø4$3%ž ”‰C›÷ÁÌè0Z^Xâž<&Gì·ÅƒRŸ\K$¾ƒ€Z“”'ÍíøÎðŒ!Lù#~ VùÃÒûܰèÈpúÁu Å¢hòÕ…ØŒÅEF„IÓë3ÀÈwÖzˆÜ'ð,ÁÁ:º|Þ4úfë~ª@òh˜õb` ñ)kxÒ›5?¬1Ê– sâqŠ):2 Ú¡rQßÞâR1Ñ?åb"hüÈ,ÚyFÏcÔZ\5õ-€3BŒw&8‰5k«Øßs>ê=„÷³B¶Æ§[`ÇÁtî¸ü<165[TõÃR§åúÕå„6K±.°<î‹ÿýÊÜNv®¿`ýæ…hÃîÃèH½;á‡Pun‡=;KóìÃÄš^bó8¦(¨ ™²a/Ϥüï„ùÏTrça šmê–^=Ÿ^ÿ|#=þü‡0Ž g™ÐÒa{©KðÇNU‹¿XáÏ¿E¢“Ô5¶ i¼»[/œsÎð¡œ;J9¹–H|Ù®ï}ƒ ­?^!©–.>w%‡ˆ5ò® )˜LZ³ý =ð‡7éîáÉ®@&Žpw¦6Lán\|l Ã!Û.ð ,°kOä[ ¶Æ§Êä{æ™ñiPŽBB‚(35Þ܆ȰPêÂø”i?&ÿ٥䩗›Ó™¡ð’s&!PN·h“…óoYråÁĉ͙ÊÂD}´K¬ÁbgkãS.[‹”9àËBÞë—åê5[c-«F`‹‹ÅØ\}Ì—·ýJHR€dA„m5¹Ã ÏìQÛqHXö/º÷¦ hܰ Zµµu ¤yåL6×ÐõüÁ–NÓW™Æ<«çM+ìæ{€?Üá½ã2PGéµÏ6Qüúºbþ4Q¬âŒ&ŠÿTÖ6 ‡5Þþzs•”×Ð?¸Fتþþ•Ïy·ðw O–‡x'ˉ€DÀ'èÙ.IF¶Ã]¹3 ñ¤bKŸCÿúùw•C}Öêãç`ÀÁ‹BI!¸~óƒ«D0žé t’î½Èöû,LÜpá,â1^Bl¤™%{Ƨ–itÚÞãU¥26cއËÈ?¼ZÙe^G@ÀïIÖ@±ææ…Ö˜ËUÔ˜,¥¸pƧᨇM9z´µñ)—‹ £ýå=Q¼©?L%z~Ù‡Ÿ•ÞºX\¯çˆïoõ•|ŸgÁá¥çM¢=p@S¨¶¡Ulrç©MæF%ÊH"I):àšg)7¡ƒ0—ã<›‹° ÃNÆ[‹Ž g9öG⪲»ß}ø$¤å*áûÄZ¤±Ö˜ê[(&*æzZáÅ%…X ãhN<;À×áèL¶xPΓk‰€DÀ7èÙ®gPïÜyŸ E@r'R@2¡ªŽpwD&”uGW“uJìB€ýʧ#«QÈ•ãÓI£³‰#²Ÿ'ÝæˆÏÅ%¦1%; ÓfŽ(7&7ÂL.¤ãÐ »‰ã!´ŒÎMÁÂúŸò–ƒA|… `lÆ÷íÞ#"¨X<(mæ5_ïƒo¶Á„5\]ÂB‹­´ü…zÐóŽÏðÕãå󦊈 ëFf ?¤ÿ}îCŠÀÌ¢åq¥œ=kŽ.ÇRø³o|)|—øC|Ï5 /Œ>CrNb •(k²æ!PƒBüÿ/o~Iíè ì;ÅQì˜æNKÿùx=Ýû»×à«”D‹fPe]“8vÌÿ iÿϸÓì #hÒèáÎ<Ó*I" ðªz>†2²ïÜ—@âdDLol13’$ï!p5|ö`Ò\!WŽOÙ·‰Ç·ˆá¯¶A‡}…X8Û zösïî6 ‚],¬¡¦+,ˆã«¶íÇØv#üö#D3>Øßø”ýD9P[n½·r«sô¾þx0_Ñë.Õþ¬ Tż¾é7BÒ• LæljÄ8Š/ ±çˆj×<¡!Áb·r¼WBaíú&e$Ç÷úÏÂÐ÷¯](ru´utÂ(Ìl“ÉñÞ9D¸¦zì÷ĤøKq(Æï óÀá½âÎ˪QžÔûùx"r\< ;Mž`_*ef´?”zÕë‹Îž@¼H’H<‹@uýAóeŽ$3rÃs ƒS¶¶>ŒH”w¡0—$¸Ž\¬OòÕØ¢é/ßj¾pãS.dy¾eÚÇî¹Ò\opÐ1^د(<,X¸ˆðþ› œkcÍÈðzè¶‹ú˜(÷7>U̯;&µ´u!IñO²Åó¡ÐÏï¼LÙô˵ߚÛõ‡¶" õWÆÞc,±°¢t å<ÎÝ¡HÊ>^áÅe-!¥Œ­ý|<ŽzŠ€¤”çµ-Ôeä¶D@"à=zG¶“ævÞ»{eËwÇ`Z#I" ð-\9>å–qÔ;ö¡·¤þÆšÖÆ™|~çpp ¾–åX˜Ï³Åów’B’·nŠðO² á-^äu%Ï! #Ûyky%Ûô2¹k2™sÛ.-H$‚@TD(&üÃ¥¹.k§ß˜Û¹¬ÅnªHñúÉ_ë¦Úeµ‰€¯" #Ûùê <¾FÂ/éë2Sî“# RH ¼ [,°Ž§Iä8RHr3y†D`H"À!J9ÁqmC Õ#¯ûÕ9K}?þqpMD8Ô‚YÄöÛ¾F'‘ºðh™ÈQä§gÚnO`̶ŽzDºC4),$†þðª)¼?ï„öûÚ½,?lÃý”ÃöŒÈv[—»Î¨ŸÖ¶wÒäHb:X±‡þ°ë˜]¬z?õ—öÛu3e!‰€DÀeH!ÉePÊŠ$þ…áätœü›m…#Ú{c#iÛHÔ€~‡Kd¤P2tÇR·!œ>B4ž˜¨PZ8s<-˜>y˼÷ âDÎÜö¯·ìEb>×´½½³‘J*6÷ÂÍ×Û¿r ßû6ak„¼†´B¼ëÕŒ!ÿÇR£Þˆ~ªQ£b¢ÂEå|#ÜOÙZÀ¤ôÓ¯pŸšpŸøÖèáƒÐ¤!}?<ÕCÐß^Uk7Ë:¼º¤ÓëÅsö/š™ï3Ï©£í·»ág újûm‡,/¸ïP\×ÕÄyŒ^ühýî¾ë‰cÌshÃGï¸Ôc×—’¸Ž8Š"kÞø|´F^Lɉ»±>ˆ\^í®¸LŸ: †0jkƒc“7¡ƒVaÐwËÅçí’§ …GJu=ò? 2ÚÎ`ø^û7¢ý­pÖ ¡Ä¸(4†££:qt¸6hdZÚ:éÃÕ;h5Bær´(Ö.qž;OöU \oîS}dU¦ÅP]T0uƒwQÞ ñÍ]”‚gƒ“LòD­K¸ýYm;·/ÐÛï®{,ëztcrãÞg^§Gï¼Tûõ?? ß߃Õ``C¯õîk‘’À–—z½Aœ¡ 48]•xޏ …µ:!Á•”–ò ……p;o,|EFîK{{.ÕÕ_J»®B‡…˜±æ¨'h%’â½÷õV˜z®íÜ.ßjÿ6Ü{¥&ƘÓ%x{¸ Š‘ÈZÏ ›œÖ5¶Ò?ß]E—ÏŒ~Z ¢z¢¯šúé6j ÕQqV5Fô¤”p'Ž,€UÅ„Š%¦µ‹FV5ã9ýÏé4/<§Û~wÞcY÷ÐD@ï;“«žÙš-õ\«¤tkž±Z·ó0e¥ÆSxh¨<ŒÇÌ!Ç ÿfk•×ÔÓˆ¬´^w†Ã.râCÇOÓh$Ž?m,q,|î ûŠË™¸˜Â`ç~Ñœ‰´÷p Å"Å¡嘥젅³ ('Í÷|3z5Pþ2(B=›î°€ôñZ+…”˜ø>r«8ï{ä(P,”¥¦>G55WA`–Åh –Ý9SÏpÛß]¹Í«mg¬¼Ý~NÆí¢ºJêöSJIŒ¥Úúfh•v , JüžwW_U÷Ó˜¦‚öÈà™ùƒ>H°`¶+;žF—7â9Ýæñç4Ûßç&ÈÀéªz1Öä œüá™T ,œ<–µGw‹²3ò‡õÑôÁúióÞ#”ޤ²ó`Þι›˜ØÿxÎklm£s§Œ¡¬”xñ~›4:›Öï<„<¢étÎä1b"-ÐÁ÷Ò«×·`?!çùÖs æì¯6Sem£`ò¹÷VQiE-MÏN[ ôbœ;Z2›Ÿ5i}¹ií† ÄÄæ o# òÄÑ9ÂI=y–ŽŸª¦×`6ÁBXrB =õâ§Â¤£W…òDÀ (ûqðOÖ™¤ä¤·¼" )MdáŒy`aíÃU;ÄDkº˜_W×É!<Ðãëy»íÜ>ï´;±€”/${û ’Œãöɺ=ââgÉ}µ§Ÿn'dxO@Rðaù`~>\µÓCÏi`¶_Á\®% ]zúí¿?¡h„ð–™Do®ØDGK«ÄáÏÖï¡o¶Ñì #i÷¡’>ßÎMp 9gò(*.©¤÷Šsx<û‡W¿À;-š&É¥Xø¶Cc¾a×!Ú´ç0Íž8’6à¼OÖìPXèµ’pû‹Žž¢üa¨GÓ%çNFt£(Ñ)8cñѲJºbþ4š6.æAS¤¦`Ì&^³h&M›Kã I: -Ó®ƒ'…Ô>küpš‹s”äa£sÓhÎdÓ5â£#èÀqSyur["àjx0ǃºæ–6LlÁìP…Ð ¹ú:ƒ­µYÁ0ûã—ü@˜_WG«{ >Hl^È×ó%ò\û7Š™AÖ IrÆßùï|µMUãÓüá´ÖL3 †SFr¼ù´KÎ"Ƴ fäk¡$a2S‚@ÔˆÁc´= ÅA²fjikÇ€ÍH1È2ÌÁFMÉñ10¹0Ù«pô#Å´™Î£#CÕEÅvˆ*’gp——Ÿø*«¬cÉ¢c§Íÿ$—²oÒöý'Ìû• •TFfÓ~XOñd>Më°Vh[Ñq1ñ³u±_’$©I}`âÈ,ž™B¿øÇ»Ä>Hl®ÐÕ §Ñšíûé¡?ý—tvFßš9êðÓ¿¼M¿øç{"9'×—–G/~¼Žž}ã ºjPÂ$IÜ…ÏLwww -ÒÑ’ jiï†Æt·».çt½Ì[3¢i+«|»b†žë8ŽúšZ:}ºí ž{ÛÈ‚á!Nß#Y ["œŸ)“6ÉäŸä 6J?mF  ÊèžYcgêt×¹Ì_ f™ÝñœjûÝu¯d½þ@´×—ÎB|í z|ÙGBË£´Šs¸±¦é?¼I…Å¥ð/êmí¤”S¯9•Áä1ÙÂ/þÁ?¾Iû D!N}ðÈßÞ…UG+À’ˆdt;ô6}ûÎ¥s c6l΃èÿþ³ÜÜ7رmÒè\aR¡Óiéú g‰clÊ‹B·^|¶²I) Ñô½«æQ'ü@X°âó˜Ò“bèÚóg ­”½—¹R¹!pef¶³«‹ÚM‘í‘9Q,çAòUbÞ˜GÎG–›‘„çÆ¹b …GKínû¼©Ðì‚ï ˆÚ;0ƒ·—Vný5¢ lñÑyˆ„FUu<ß½í7½çdBþ^°?S9é‰0e vª¯ªû)Oür¤èþ‰céŽ1#D±Fø1Õ5ÐÓ» éHcó@§Z=žƒÁU¨.ˆ7˜Y-tf§à|ºã9µ·ýýñçîc®n¿»ù•õû?Ï™Dž5^L¾¸j;¾K¦ç4Qéî¿e1ua2”Ç®jú×Ï¿kþËþñ¼0±IC¯Z0CŒEÙ ¤ªÎTßEçL¤ËçOµ[!`¾ÀÞævª›kÙÉ”CüATeŸ=kî|ÖΓ’=èÉ2Î `xé°¡K˜ÕÁÌS§m¹zœ©×çr!æ±#™o=L¸ƒ%ƒ„oÖiÛìn{SK9½øéôÕ–_Â1ƒæLzp°,Ð9“î§1ÙÙu¾{ÛÓ–}°]ÜÉB Œ£N£%~¦ØŒÕÙ¾ªî§Ý˜°7Ql|£®þr-=¹³Ò"Âéc^¿`4ŸÙ;Å…­J˜?æÓÏ©#í·ÅŸ»÷»ºýîæWÖ?4`ë$[dkìj«<ï籩ÚO^)+ǧ ¦uoѳ÷±€ýÇ‘@²R\Ú~n­Cºô"²2‰ÀØ|‡… Ž–ÕÞÑAMPiu >óØóæ;,Ôä?ØÄ õ0p¤ízc'UBóÃKXH,-œñkD¨Œ†iUv%=ñ^8Ò¦Ò©š]´âÛG0X>Í‚–î¹r=½¿ænš?õç”3 !Yß ‚¼Ë Qn¡ #¯BWq)’BõCnkÿ‹ÖÀvú,óy+“ ~¦øÙr¶¯ªûiGe'u!·Ø!äKá%¾ROGQÐj5c‚á’œLZš?ŠRwom==¾}ln‚òçKæÓ}·ÓƒÐFåDEÒ{GKè¢ì jÅlôåò tí£5§*ûå‚ùtÇsêHûûeÐÍ]Ù~7³*«bp”ä h\I‘HoÀZ$ŽÞ)©7RHê‡øwÞÔÁÏÈY©Nìââ’$žB€£2²3'à:àœÙ…(\Z)€ˆ§xÌu˜ÇÎÎnÁ7óÏí,)t >­fp‘zÒ'B›…à]­”™4•.™ógÚsämøc¬¡Ùã¿O×.x™žÿxX4Rld]7ÿ?TR¹Vmû-Õ4¡ico§â²¯‘ìïªiØdÏ}í·ð=X¼•óæÃNþ,äí`â4 §átüÕ·ûˆƒâ Dy0³<…ĉlªÜåÈ~¯ IÐø™âg‹Ÿ1gúªºŸê©íËE?%jƒ 3)1žžš5™>8^BË«a–7œþ1g]¾b z)Q´Nÿ8g:í@‚ô?î9@Ç›šé¦‘y´¦¼‚>:VFG›¾O̧;žÓÁ¶¿¿¾âŽc®l¿;ø“u]8™¬«)“)—Ï›êêj‡D}RH·Q6B"Ѓ›ï(&<¸£P9#lôÔì™-# xà[1·ã¶h<ª1àmG("4‘.=÷/”‘8™Ø§hýž?O=’M_h@uôÙÆ‡Duíõtý¢×)+y:•VmûʪvЇkï1_NoèD˜ÖRN›ÍûÚðvûâÏžãM@ðÖWßRr\,òÏ¢óÕýýUÛú=•Í<®Y8ƒ^úd±‰¤¯?Sül9ÓWé§ ˆ²÷4„¡ ñÐEÐ?Š‘}ýÒÜLª‡ðö«­{„ àqÙy³hrbí¬1EnÝ íÒƒ›¶›!îÄDÀihp·Wט÷ ´1úé@mìï¸+Úß_ýò˜D@"à}¤äý{ 9¸Å„‡ Îå"¦™]~÷TÈGæ›nÇ`IÁÀQ!‰2ƒ¡›¶-£Ã¥+q¯\°‘¼ýfvÊëö‰íبL³täÔ*óñÁnx»ýƒå[}猫¬mKxh0拤ÚÜ/9oå¥' AþÐÉ ä°*Û×_0K˜$ß¼ø,áPü÷·¿FÐ -šY@crÒ 1R!‚z|½¹P\ÊÀÁyShTv • ­Âú݇è4´P#\ŸÛãl_l?%D9膠öò¡£´úTUžÉ»—Šœ*a‚§Ðþz“Svòýí<#­;]¥ôz(ôÓA7'ºªýÎð Ï•HÜ‹€’Ü‹¯¬]"àqøãmZ8 l8¦Kñ8˽.ȃaSîÓ@„Û2XMNuX@li¯¦åèÅÿ©mG×C­Ÿz£ýŽ^S–—H<‹€’<‹·¼šDÀ#˜_žòL5ÿ÷«<SÚ0X¾•ó]Õòc§WÓÌü»hƸ;¨¸ôš•¿T˜ß±‰-ªn(Fèÿ)ÐD¡̳Äa@òÑöÈ·ªgl¿|ÞÊHЧø˜HZ·ó ¹n.<*„Þ¸èhæixV í>\Bµ&Ç©êz³¹Ý„Y"8ÆÝ}ý¹:à ôù“N,Py’¸‰gËɾêê~º¡¼Šn=Œn•Gk¡1ú.|’ØünwmMxŽ!t8›íE""+¿*8ˆÃ€4úé€m쯀‹Úßß%ä1‰€DÀ»ÈàÞÅ_^]"à6̃/€ÆN H ¨Œ«èØ©µôõ¶ÇG釴ôе”š8ž>Zû>¶ýgvz…Ò èŠhlÎÅv±â«í·‹ù3…8Ó»¦`›÷¡¿¾µBÒ!q$*"LøÝwãtõ‚é”c3'çÉâœDü`NW÷ìç$ˆ"×Q£µzÙçªgÌ•ýtcEýr&Ý9v$-¿hCP‡‡¾ÝI-ýÃøï‘4ÉÍ¿½âBº0«G ÕC¡Ÿö׾޹²ý]K—H¼ƒ€Ô$ywyU‰€O"àêdª:måeÌ¡#мø­Þññb‹¶î‘x ‰AHp“æƒËr¼§_ÍésÚ¡“+èÐÉ/Î<‘Þ<è/Ó‡ÏîhAãO×îêsÑ™ÈÍïþþÎ×ÂüN„Û²°âOm2³<Î •þ„¨t¼Ø¢×'^¢¼ !Áb?® ïô$KWöSVN«°Ä„„zðuúaÌ3##Ä„J|±Ø'ëé]EÔ5HFG’éú:6’?‰€DÀuHM’ë°”5I|gg¥]™Luxæ\š?åQ§pL{sŽ#Lª¤Ï3:% ¦-ƒ9gàv8_"&*0Ú‰ëÃ3“);5Ñ\i-’3ß¹ðK Cä6Ö ;UEc‡¥Ó°ŒdJŽ!ÎâËä(w´íji sY3⌀4˜¶ æ¥+¡›¾Þ@¯:F׎ȥ©Ê!‡×Ž$ÓµU¹3m±U§Ü/x©IRáòt Ây˜3‰³“0ç¿`â‡3ÄJBì/aZc#J8‹rÒ{ÎÔ-Ï•XC€?Ø=‹µ¶÷¹*™êÇkD‹g=“©úþUéhÙjZ±Ù>©‡÷Á?}J¶[ê»GÞy=XRêìùî8o{Ñ D¤›L?¹õ": ß#6ÇS4EmÈ©´}ÿq:öxºð¬ ôÇ×WЗßÒó§Ò‹g vö—Ò¡¦hƒîàÏÑ:ùö(8ö^)ç;zm_(¯ð>ضs”:mO ü¬ö!”9/ó2Sè²¼,ZQzšBavùÐÄq´(+ FŸD_”œ¦?ìÙ/´Ló!H]9,›Þ9z‚~6¹@$ÍmÀwÞÑdº ¯ ïδ_©K®%!pât5"ÈMmC q’töËt–äø´^Hâ®ßlÝO_oÙ‹ˆJ˜½4R¶´A xÁ.¥5ÈJ†îX„l §Ö줘¨PZ8s<-˜>Ž‚á,+I"àŒ=qôµn=¢…ñÛÐAr&™j}K)¬Ü‚|C“èSD‹kë0åhqYÜÏX…÷)/Ö¨¤¢†þS»¼ó¬%ŒýIg9º‹†ünæAÀËŸ¬Ç;R‡ä±Zó à½¯{ç\bá‰I‡@0òkŒŽ¡M•U¢ñÿ3~ -ÉÉ eû‹Åÿ¥ù#©ïÁgažê¬Ô$ _¸å'N&†Ý0Ñs4™.W܉í裒$îB€…ï.øŠ1*Þ‹Mõßq=úq{ü>Ìh‹O®Öm$ú4O£¡ñ_43_ŽOX@Î ”Ò«Ë×CkÔNááÅ”œ¸냤Õ:Æ×Vg4¨­m LN&ÑßtЪ-…tËÅçí’§Žmñ(÷ûœË§¹­’[OQSëijlÁKCó)D;EU5åT]]MM¡|EFÛ×.W&Se¾ºbG;”LÕ>.e)FÀš€¤´§ËÊÀ³«‹£r@ª`$×DóÒShDLMEÅP„“ÿÌî˜.ÏÍ¢ÏKN‰üQü?/:Š®ÌÍBÿÓ¦ÏPF¡Á$Ó=ÞÔB7³¢‘09=!Ž2±NƒŸT*‚“¤‡‡‹u*ÖéøÐC-f¹¶Ž8r&k^û|’·Q}dU¦ÅP]T0ucbÀ„kÆ7wQ ÆÄ|³]g·.9+ Ç§ûä®DBÂ÷¾Þ 3¸JJKù„ÂÂN¸£Ïõ©“°ÈÈÝbioÏ¥ºúKéïo·#_Èth–ò! ¹§ó÷aDîði¬ @Íg¡F$7e᣹µmèkŽÅ¾ËÝзcÒi0ĺ7“©†gyŽD@"Xð$züÛ~¹um¬¨&ÎýÄZ¥X$/>¤J¦{ ¾®ž-Ž)­E˜tWQ+„úc-t‚óPÙø~shõ4Li˜xjþ¯R®âGÖãߘ’K`ÝT­Î.j ÕQqV5F»½a,|UÅ„Š%¦µ‹FV5c|úÈg¨ãÓ€’XBçÎ÷îÊmQH‰‰ïÃŒÃy»ÎÁô^ÌRSŸ£šš« °!1!"dqvy”¤Vi0ˆúÇ9Ï=7-¸ŽŠÒ º®lјe0sôÝ”‡už‘–í¤NzæuŽœÖWr¼… £nM(NµÏ|ÔÛÉTo£\E”ŠNFeKAÉëwfp °Ô¤+Ìè¤î,€Ðç²1á)ÖF eAîÉ®¦©¨]ËÖCŠ¤Ã“¨—td†SCQ)‘N1‘X°Žâ%, ~u‰¤Ñ#Lu›Ž**jiåî°Ë7Ùè®eDƒI¦Z‹dªSGß*xd¿¨¶ ƒ=Xœåy‰ÀàX-mà°àœ”š·W®è·ÂÁ$Óe3¾‡L¢¨(j ¦zä«êì rimmb]ŽuE[‡ÐrõËŠ©Ú°àP]XðtüåEøFè;auB”ÿÊ¿ɨ)ït)–5š­Q[JZC‰‘‚K¥ u4?[)ûí-.¡OÖíÒŒ¯¶„…3æaì©FŒOwäø4 „$ŽV÷|B‚+…É«½Ïââ¬ÑêªH¦7Wl¢‘Y©ð !N¦(Éw°[ÒC)²6ï°«IÖ  ! ¡("‚G*4Ž}ûç—iCîÆ¦fêh¯³ëjöR'S]8ý1j@`%™ªNõÖèpéW4iäôë7ÓÑS«éí¯o³VLîó!Æä¥¡kiÿñŸ {Ù»lîTÚ±ÿ•Vº®ßÙ{mÖ¾_‹^ÿ|cŸB±Qaì%'už=t²¼—€Ä †ÁG% ¾ ’†‹?[e³1ˆd÷À¦íÂI‹¾ª6­ã` ê€ J%îN¦Ë~R¼Œ‰³=ç©W¿ íÇKŽn©<ñRXNfHPbb¬."4I£ N‡F, ME…+<Û\Û)HáßÔ#5R6Átÿ›š"yvÒjŽ`×Ò.|¼abg«µÌKq2&Jën|B<õzIÛ E;Oi°Õálíg¾8ÊÞÚÁ4Ú³ÉôO²…˜}û€¸VÖþ NÔ[‚wY)¬0J´ZM)„¡2—Î=ëÔüù«»{¸‡õ9‰_Ìí?v§†¼" õð3ð–cÉTDÂÏÌìrRgÓ…³Ÿ"Žô·»øM ²Ó¨¼fô+AT0üJziùÅÉÔCn¼à-*«ÚŽçö?ìJ:{⽞J§P~Å·P]Óñõ¡cr_#¨õÅô½+çRU}3e¥Ä#±k5-_·‹.@£ìÔxºýÒsé‹M{E¾#5û£rSiþŒqTUׄÃÃB½´…ôx™ò‡eДqy„‰Z¿ë,¯és­´×_hFrýrÚJ ¥wŸ¤Th38oP4»# öõ+×›ßcÿž7›v×ÔÑ_ö¤aÐ|übÚx ÍF;ý£ð0ò 9®9ôeœÔ>HöðÉ=Ä™dºö\£¿2ì5‰¤ìß/oÆ&'«ÁÂΩ‚GèŠf¾ürb‹¦Ü}6i Y£&¦wY8ζí¤ÐÞ|œÇ ^eÖ5Rý RQRºù–[˜?IƒD@›vvv ë…ÇD;Oip”eæ‰#ì­Ùy( Æ§!$±¤~¼¬ z§óíhçðdù¨ˆÝÝ<’ŽßQ¹é”46UøžäËW¯eM¨ oý¬^>@g4@Üþš†Lê >2 õÒÙ)YTʲÑj‹}ò¯¯!pÑYÏ@óÕD+výƒóËiLÎbªo¤¿öûÄ1£1”‚5ª‡÷~ì¾àX©#8H‡È<ö·}¾â£óèͯn åéøéõBHâËUդʺø¿DIcs.F(ör”ÛDÌz)êè³ ÎÚ;ëéúE¯SVòt»óD¹¯ýŽ= jh–V ¶zäꈆ֧¹µ'Jbb\ÝpÁlQü…׈u4@üÞ-«¬¥ô¤8IåTQÓ`.:g ÏL¦=pPf:]]/ê?‰D³ãGfŠ}ü³ûp 44,#—J«êˆó';UE—œ;–zñŽÍPjB ê:i>72S 8&ø`mQøHKŠ¡ðºîü™¢¬ýL!åZÊÿÖ´O‡ ,jm˜Mt_¢XWtlæO§m¥š†f8òu!ÈD0aÇÒ¡Dý @xQdãÅ›%¢ÀYh€ÌØ?–àS„õQ ´K¥C]d­¯p²ÅƶX3|¾º¡GD¾°]/ö¬µ§W+ÔçDÂäÊ ·¿í™ÉSEuEbÝÞÙ€½¦=ï(<ú>Mw;}½í…°TxüCìÅ´+„«Êºñòº}âüبLIbsÀ÷µßž@ÅÖÙcÍšØ~>èLP~¯þýí•êÔ’ƒþi¬cÅŒ#ìRÕñù†žIð󦎡T”cbRyMõ_‡©&v# ¹bb¡K Á‚ÑÜ©c¡iŠ¢•[L÷JÂOEmƒÐ0<^NIgü@*k0dëî¦wVnÅÌmï´ʵ”óZОIiAê~gqÈê_uyî§¡]ö¿Ø&%Æ‹:Ô›¢B7¢MeО)ôé‰2ºiT=³»HOËO– z „+¦ƒê\B Hz:°»‹Rwh·Á-Ï©#íWxñÆÚUí÷ï|MŸ¤˜Dí TAÊ$$éÅ8-±)1ç)rW¢X†ÛYüáÝ?Ôǧjœú¾åÕG‡À6«3yÖ±¾¹•´:ÿ0%`>å„£ñ……êÅ,«?pï=8á0kÒÃÂÂ`óÙ‰æMoˆ È×¾–b¸‘ëµ·í5[ΕMåð¥â‚‘áÉæ›ZtâCØd?°…”’OŸn¼O«má×£®3xßuÅ\$E<°Pû©Iá·A#Ø/JÍ—Ûyð¤XB‚ƒ¨!w™Ô×;øiŽÜ4~¦”ç‹×Ο¯ôÓøæ.‘ñ~ úؤ®f:·ŒFÏ"©êø$…©ÒFpRÕ­0§ûAþh: M›ß1¨o¤jÌ\ß>f8ýqÏ~:7=…X»´æ´}BóÇR§»žS{Û?>î:îŽö»‹Ww×ëMA Âúá™÷ RÊØ”Çy0+fsb½“ïwßo®ŸyôÕñ©;Ú?¤…$þ¸**MåƒíÝQ'G_ê†ikÁ”v8û!˜O=>µ!KÒÍHÊ ¸ˆHc >ýYxe¹ÛN @ßGJ˜o¸q¬…Œ †3ñ“|VHbÞX‰”®{õåv;w—}¹eàM‘ÖEßó£Ȱò çvb¶•vðÕõ€ ü@èQ @-mU¢Vkç;¸O @æâ<¦‘NþB‚ÏŒè`ªl颚ºK)=í9ŸI¨l0S-x ÑixJŒàWáÝ™I=5ù¹É´¿¤J\'ÍζsØn³’ÊŸÖŽHžk Õ6´RjR,îý ðÐáV˜®ù ±ÕsmC ’”’x–ø™â :ÎöUË~ZTRC#*›iwN<Ù“PR H 92Ôj9cr¨œÛßZ KGæ+X§qësêhûûãÙ•ÇÜÕ~Wò8”ëò¤ Õ…÷ãø,òb‹"ðü§ ÁÉqAJÓñøŽÇyL†;òÜÚâÉSûŸzŠ'w]gH I š¹3º AwÕ‹'ÆÔ1£ØRFÕµÔÜ^ÁÇîáCë>ÒÑID;K’E\2uجÓ‰P- @<þW™æÁäBö8Úƒ̘eFjèxS2ÕT_EÉÉ}5!g¬©¹ þ!)T ó"øfþ•¶8Ê—rž‚A8Ú§ä%ѦÃFq½ä$ÇÚÞÜVM>Žò2Pyϵ?퇠ˆˆtIñQ±%[ P‹(rì7mÏ’é™r®¯Zë§SóL÷i4ò‘Ȉ±à¢ÿ¿•íí"oRÿ¥w”ù‰€¯ìh7?§Î´p-³ï,w´ß¾+ËRö"à° ¥7äè»»ó`®“‡Áb®Q«Í Î0h‘yj…™Ü`©äЊÇLUÞ'<Îc­U…fk€ëúÄá3ãSe|í<¹‰‰!/$1n¦Îç&ÝTmmÓ1z}ÅS¤ ª£Ð0ä¤@šŠ$;9ÐPTD*§!×I:Öøud†]ÌëçßÛIEGKö¼ô»'.G¥°2š3}›." ;ÁL1~CbÑ€ ˆ“s˜fÈ…â… Á#44”â#Ú¨¥³ƒªÚ ôçzJLzßk%Ö °€ÐÒZ@YÑZJ‹<çI¨cþ¹ƒ%K ²“c•­•W¢íÕh{¢ýmùóˈÍó¼×þª®3RBc;¯Ív µBƒ‰µ^É¡Ýx†Bųdz¦œï«¶ú)Ô¾4ö.¤ÅØ¥QbÜoúzƒËág ‰Í}NÓ~—7º»ýVx>ó-àïX¬‘»‹€… Å!*ñðlD"–´[î}øÏc F»ê’s¨¢­Ê[Û°n o—ó6‚¢°/ß@dS‚@d`?$ÔŒ‰h\gT'yvX tQmðDZµ£ð„ä((êòyiçЩšÝ˜E¶­vU—wÕvww;SÖR„]wh`(*<B¿ú#›æÀý$9€Y0Òõ­í]"YÊà¥ð£˜ð°á( 6ÔáXÚÚÂà›Ô…™«Nªi˧®Š$ –?õ¸ûà°Ù_WW2l¼”.øc>™_曵@®0·Sc0:#Nä¥8^W­€wÚÎÝÅûíï¤îšzŠaÁÔw|”œ”\[û ±‰kƒ;ñ졚ž%WõUkϪÒO©®ƒ&•ÔÒ‘äh»|”\ÛzŒáƒÄ&v¬AòÆsíWîY{g—Nü<1iIæïƒåùß=„ñ1˜DâÅqØýr¤$‚”œL”£‚T;Þ-;2a¾c×pOÅ'“E¾6Ž^)É=8zOÜÃ…›juVÊÕiCèªyÏÏ"+¹SÜÄj?Õ²”B ±i‰¼ é}4@ö @ý\Bò ]úÊú†þ(:%$± ÁHFÌÂQddu \pò1tuwQ²¾ÔAÕ]IT^y'…‡SBƒ‡‡„`➄˜ˆÃ\7·L¢¶ö‘¤ÑSv„$äFB®—(ðøÿì]x\ŵ>«U[õj˲,÷ÞmÜÀ¸`lÀ´PC%cCÞK£$”4I^Bc0ÍØ€m îãÞeËU®’¬ÞVÚöÎ?«Y¯Ö+Ye%í®Î軺wo™;óÏÜ{Ï?çÌ9('Ê‹rks»æ6N}ôá‘ùðb:V”ÚfuGü¯þVÊåà­‘¬iŒ6…sgÍR‹º]s›Ê¯®C$¸ù†;8ieånZX5%˜ŒµÏ’oûê¥úéÑ"+ =ULÅÑá”Iˆzߘ8JÍq€àæ^Üà¤ÁÈõOoÇç4Øë¯Û©¸´Âa©®.Ò¿eíßiju–橳åt†]òŸåA˜ ³^›^ß9ƒûÑIBÛÞ$©¥²vÓkÞvW5IŒh¼æ4`X¨‰nŸö&…‡ÅÐ]Óßæàšÿáåâtí 'r¾6Ê>õ­Úþ¼Ò4]3î8Ë„¦7šœÎÄê-Yÿ?Iùx“[4!¶;Ý|å(#½7ÅÇÅ«3„FIA>’gʪª •Lh¢ÜC6µÖNÁ+„û/k’8Ÿ˜èh‹¡ÆÂ±·xäÊé1±ŠL,YB¨¬º'å3q1°@b¨$ch ¯}3©Þî'›5žƒ™Fñ3ÃäÉQR(›.™B˜Eª²ÅÆÆP4´D9Q^”ùP榆0Hg ¢CËèTq5¶bÝQv¿¯?›_p?Ô0Å7òÄüF˜ä7·Yüö:;ˆµÙØ †×üÙPä !¤†Ãìl^Ê–0 GÑäë¾ÚØ~壅 ->IBàI_ÆU1r¥#,Î@±0ò áwAB(¨øÉs¬õÇ÷=ÞûU•ˆ"vU¬}*å€Ð'róè`ÎÚžu–vÜ Æì}i&ÇFƒIß=3è7;öÒÆsçé×c†ÑøN)*xø*Ž}†XhpÐ25½3ÝÓ¯-Í9MêË2­ƒþÊ!–œ8Ý,tµlÝùºY7l§‹‚ž$5W»ÌÞìcêÖi”|JÊÉÆfJ¹ç÷Ð΃ÿfa"œnžü =¿ƒÝ/¤èÈdÙïnÚ¼ÿZ±ùiš4òqµ|¼vn“‹EQ¦ÔF˜È59k¹À?PÅÜS9ëã’“ÿwÏáÓ4nH¯•f>–ââxQâr¢¼(wKLít¹/…rΫ2³…'¼Õؙб 4„å–¤À©?‡PõgoKV[[é¦ÖŸM†”›q\á?†½?Šv ¤ œkÍ‘Éh#10¡©Z³¯6¾ŸºµSuóÛɳO£ŸB.‡@j°ûñsœõÇûž…MÃÉìƒ;¹ 1Y}j×¼’¬Äð€`÷²ø›×”´æl.}«OZs.—>>všŽ–±cÖ(*¥…GrØ[lýiÂ(ÚËfÕ?Éï4#IMâŽå ßîÜG7÷ȤŸJË8¾®“䦵Š÷<‚r¯Ýn¡Ü¢}ªng v×1·ûúÀ«,„PBL&å ž]§(’„“Ë+ÏѪmϫ뢙äŒá¯{Ò΂"ÚÃäiÅõÓhxr"mÍ¿p½>OÖN„$5±'Dóü cCư;îsj޹ºØ•K~I¶k*еKñÑTRqʵ_6/`(G}ÏŸ=óö^ƒáÑìœsÔ—Í–$M†"ØäÍíÔ`ÔXó<ÞAí¯æ*1I°Ù¦x:úwKTéüའå0†`Žk¶øžN-ÏAb;h” “K‹Ô2‡ žxµ©þè';y4<Œ…\$8„è׳«gSøíoh+é§Ð|©­ßUíÝöxÏï=|Ês8{9? p!‹E%â÷Û·†ÿ,…Ÿ9„F¦$²C‰jô¶¤šMÜÒáÚØOðÒWÎÖ%Ã’„$¹áã¹)$É‘Kü;ðAêœ<„æt…š‡t÷5ï×¹"™ç#é”דª9¶J)Ç6’$4€€1ćѺöãß¾eö#w½½ì«Ä'ïŸÖ²¹h¾@N 8׺çù?f~YšáСV“¢‚Û±6¥%IÝÌí~%† sdÞém¦u0aÂ\,0³Ãq”Ïש½0Ðx{ý÷³¹‡BÈÄ.lÜ~Gdóã_7c«æçm%ý´}ÞUíÕö5ñ{žãÁ[K7|¶øsîà˜ŠE%ýmhÕ¾/™ßéÛƒ$ÆÑŒ¥«Ô<¤7¦N¸¨b=c£iO!{6e‡=0õË.Ïxä¶CH’ž›E¥l:ÂêÌÌÎã¨Â|žmíKYs”Îq]òØ|ÉJ=Ó'ñœ¥1”}r…ëRÄÞçN:}~; ë}åœûJåá:A6ï`Äƚ¼²ƒ;·ýÞ0jÌ o.Yïxð–É-6»Ó—˜(95:á<ïͤR5Oܯa—¦Ê©›å¹››i=ïÅö¾·V‰ä"e!lQb˜Ó nÜ|C›' GβN Êè뤅 Ô»-0ÐUÐ÷ æú•±§¦¢JÖ :ÔŽÔ“zd´Lêëöo(?j+Ý_¤ŸvŒçï÷3ùÅ´}Ãê7++y?±ËQ碉’h’zx;ø±c¬š”HÑü]Ãw:ϳ›Í_Þ9•5JI´útn”îf"5oo6ameùv[~aãò£.B’êâQçW›Ñm?ø&MóKºzìsôç…Ãiÿ¾îŠéÇwfÑ9ž«´™ç'%Åôp]w* p?ÍLü=»Í£_?ã:&‚@hs;|Íë–.^›˜øo?H’ã;³&Z¢Q‚ð¥]jƒ0an¶AX Aªá ÷˜ƒyKÐ$ar'4I-Ñ&ážjaw\ФàžÐ&!n„@£0¥]rz²C¹Z3µ5¡þÖ8®ßw‚bb‹‘¨KJ<]9fhk6c«äíOm%ý´mßUíÑöÐ m=pÌpdÿîv¬_³ƒ;6b$aA |`Y $‰AäwŽœ gF¦¯nº†žÝ²›Þ9|‚ž;œ6ß| í+*!ÌOêÎÖ:Á£‚à~2s²òn7_6{ÇC7“TB’êC¦vÿŠ-Ï*ïvàõdÞ״ࣉÊ5¸·³Uæbú÷êoòèxœÒ<]"{9,h`V· ˆñ¡¬øì¿o¼=ãÎ{ðŒ~çlA‘ãÎkƇøbŽ„ÌR„…‰’"G,ì: ’Ó¹4IH-%IÈä£õ Ijá߸·ó·Ó¡ÊÔVÉYžÖÇ@×)˜ë¿iW6Us¯âqhhÝ4mÅq€Ã@KþØVÒO!Zû]ÕÖm9H0±ƒéȾݯ\ôÞ2~^*x& k¼ÿñÀ÷ßI‚€WVžfGa¼Äñ€#‚Û"Íül•Ò,Áí·g2òwöùíûè·;ö+’$^í<ºø·¤‹1¹hÅKloÉýB˜æIš€€&IJ“Ä×ჱì·Þ3õgíc/ŸóÇ·–% é“á3¸—ahŸ®ÍŽ£¡BLà Õ£mbç®AòIÂýô‚{jGz_0òÙ©mî¤ë‰u0Õ?¯°”öÏ¥ˆÈHUÏicSF—Nj;Ðþùk[¡\­ý¬úkÝч‚¥þˆƒ7ßðb' v«¥lëÚUoïÚ´.¿ñ®/«]cÛ]“$$‰‘T?è š é³¼$} kh“$5!IéQgUr°lýÁiÔr’ P¨o0ü»tŒ(†ñbܲjÅúý_oκ|æu³ØMì7ø#ÏýÌkŠp$ÄG"Ã8ð’'!òüÝœ[x>ž¿›“gk^ãYgÏßM½·g}=75¿Ö>ß³¾ž¿/ÜßA5lžid b!mÞ{L-Î ¬-϶ñüíOµñlÏßM-«g]=75¿Ö>ß³¾ž¿›zÏúzþnj~ú|³Åâ(.©p P,—Ñ`³YËŽgí_»iùg«ªª*41*áó±`dï}¼ÿñS;A’o¨bbt¶JJIMA èI^vXÂxÄ‘ï[3-ûê ŸdïpDPÏáÐe÷Õ Û'…“LZwm&èÀ­"@¥ö�:`Äè~™}û1EŤ„›"ãCCÃ"Ø.Î'D©5+'yii)‰I‰ ‰lµ “Lûá£9§x^ÛÅöÁSe©‰ Ðd¬6kuuUUIUyyá‰CYY‡öì8ΙÀ”Ú""%$ÄÁ6¤X=I´H F°&wùŽET¶2ྭõš|Õ™\ÂÒÒdd«“Ž$Ÿ5Ir'Ñ‘PÓßÒþÑ&×Ûlq^×í³{]Ú¤r“öBÀ]›¤‰;yªÎÚ¹m/Y\@¥iâ5•öz ¯i¯òË}ƒ‘ãÆe ºçÞÇt5wmÙòÞoþkþ-kA@Ph‰ït,zÎ)4E C0­Ã ’h‘„޼És<0aöÿq¦«½CɧAM’ܶ¸¨v‹Åó."™±cÇ?ʇrF‡w˜¦ñφhßR჊ÑF$|hõo|\ÑyM¼`"H: 5H’$ARë!iœùÍ[¿ÅwP£8e%%ýç­Å­wGÉYhôû °Ö"$$UÖ.x¿ã8Þ÷’:&އj¯áÅNV¨•ü0¡lX:’|Ú!$q°öîi‰´iUUõ§èè]~ØýœEBù`9•–í2·óÛÂJÁZ ýaÅÓ}Q|PA©ö£ÞH’%F’ïxà?¼ß©rv8ªV,^ô{EÌ÷ý$GA  À{I¿¿5I‚)ìQ ~c¿ž‡¤¯å]’‚ȦzIŠ££U”Xn¡ü8ÿ IJ¡Gw$ù4èI’î€bÈBå•Ãýš$¡|P"%Ç™X-pá æ…ÔÍ+øPâÃêþ‘iÂ$w‚„Q}#½ð¦$AÀ·ÜpǺdt»MçzöôéwlÞ ï\’AàbðîÖ ÞåîD ïr#¬±à˜h„Ž’´l 9^OãLaÄSç©S™ÙoIÊÆ¡:’|Ô$ÉÕ YtD0Ë)´ã¨ÌæîyÂïžE”«ªªõíM!*¦Œ3¾Œ®‡ßX Ôàé‰FñaÕÚ##M´~DI’ àSÒÓÓCG_1ñœ©úfجÖMÿzéÏ/ûô&’™ \ཤßáš(i²¤ëãγåÐ# e:8l€lªå½ÎQF²–ÕP\¥…J£0ê? eJ¨¨¡L> j’„î†î Zi¤AÝSéÀÉ|*,šEii¯°ÛZ àøG²;ÂT¹ÂêÕ)NůÑåF$uhð±Õ#Ú® H‘^xSmc-Iðß{üÉgCBŒƒ!w¶Šs§r¾WYY)Áà|†°d„h’„µ^@ˆô¢÷aÕ¥J—BÀ]6E¼BXO £Ü õÎ+§]™‰d÷Ñ/„{,Êf4t8ù4¨I’fë 耦ÈÙ#…6e;¨ àJMy÷Rý¸ÍŽ£<5–N4¸s$k¹Â(œ#(£Ì(»®G›Fnä¯è*>²HîäHÿVäŸ à+~õ—¿Œ0C¨óãNøÄ‚?üá€þ-kA@¨¼³uÒïoý[Ö-ÓiÙò^x8ä¾PʈvÐñ2+õ;WJYéq~ÊUÃeê€òiP“$ô®äÊD0""œº¥ÆÓù’JÊÎLtþJNþ°]5JÐ UTr´úØêœMüÀ ¬(3ÊŽ:H¼ ]/ È.ß!ðÊ+¯„å[m¯9N3;¦åkžž;çågë.üù“ OÙÔ)÷EPbTUÔðÔcö{8àL)J‹k74H HÉåÕV>í$ÉinÂA‘¦Hê—ž@‹…Ž &‹5…’>i—9J˜ƒTÀ¦K*¥E9¨[¢‰L\>”eE™1Ò ævà)Uü|›ýL†¡h0³ck‹xT’¶•Imnç.›Bþ«ªŠ¤NQWÃÓ™( ?YHGRcÛ|Žæ ÁĤŽ,Ÿ=I‚ZD#,,Lèè(ª6WSfçá!Åt¬(•Îå}—MñS »7™2)i½8Jˆƒ7ßåéÊ܇B 6êec ÇFŠ6q°ŽReE™µ¹]à<úRRA@^˜?¤ÕæxÜUCÈã?ûðQ×oÙA@hõɦÕÕÕ5÷á| ÜÂ… Ão¿ývßÙaûº€’Ÿ í%›$‘O/ÝU: IèŒ (Hš9Ã9ÜmGð 3“$3&ÍÕj“l6»O»¾4C0¡sj‘œžö`Zs»¨(“Z`f‡ã(«$A@ÚƘÙ=?oþ3ñÝïžÑy(<ˆf‡E†äýezÿs›ÿ¹1ÄñӟλM ž´—lŠrˆ|êÙuw(’¤;HˆÚf-¶A’¢L&E”S‡š6¹cMRí¼%­Ejªå]­ÉÕ CXk…ÈÊ0¥Ã=áæÚ$8i9rujP>I‚€ ´žfvŽz‚üy³«0[î0`0Q›’$o˜…‘ý݇á ~Ÿÿ˜ß¯ÊDàw¯¾šQSmØ/5iyv~Áo×é}5vÇ}<µô ‹¹æÛ¼ï÷z?ÖœYe¹æ×¼ù÷ý²-‚À¥hkÙåÑ"¦¾·È§Þ[©C‘$gÇpº×C»i©†'ÈaNæ-A“äÀ³#M”¼ÃXÿ^ÜG-ì’š$ÜÚ$øÆA1 SÚ%§';Ñ Õ¥ÖAÀ›™ÝÓs8hìܹ Þðù¿-˜Ædj OöLã·äµüºÛï1<ÿÌœ9«Ÿ›·`‘ýy~…V>7ïå9ü œÝ/%e{v^ዃã&Þo&#=Æ÷ù7ásVðµ/°ãÏy||ë3̽ç×ó^^Í39_ã7ñÏ˜ŠØCôúSÎý?œÿ›W^`³Úp>}9ïl6žþÍ3ÌþǼ¥'æÎ=ÈZ£‚ç_þû||=αÖXoc ÒÇ0¡c·ËÔ>~ÿ‡ðñ[a3 5–E¼³Iâ!­—¸|?d¢ùÆOçÌÙá™Ñoþ¶àZ»ÁñŸÓ“†ì°G~6{v–çyò[:&uÈÈ­-›e‘O/Ý×:IÒ ¢ƒ¨¨Ç -lv§È;kp$§s§Fþ´5UT{#ÜÉy?ç(t~#ßdIm3yÒ婽LV‚€ ´1³óV‡¢vûÏB †ïöKMyäp~á v›fx«Ù]ø’_Ï›ÿ&¿³žž;g®îå—ïu ©¡ÇPö3ÛÃfw|Ìó|3I±ñ'{ŒÁîøQ(…ÞúÄ܇0Iâ+ ã˜0åD8"§8­¡–Ëúç_}õͧz(7ÒhÌ3ÛlÏôIMÙxðüù©‡í—|A½$ ÷ç÷ý»l"p;o*’Ääë2aš×`bB6˽ó™‡:ÆdîÜ óþ>æ§|oË…‹ vV˜ýÇÖð·b×þ{Uâ߆ç^žÿÖ¨}㩇çœùÝ+¯ ‹êÖí˜>.kA@4ZlmÙ÷ùT£^ÿºÃ’$ÝA@RÐ)aÍ5h¯v FšéuýPz?¢;!ÖzÁý´#½ÏûÕ²WÖE ¥fvü;ûÔ#sþ…Rò{òCÖ ½òâÂ…¦Ý~{•gÉ™/\Ëo¿#De9GåB³³–MÖ"ø½ø×'ç|oß“~ïÂ¥ã‚ǹÿv<7oþ*VùOãÍÿþèÁ y½ŽïiøíüùÛld|)Ç !lrDZEÖó5?øí‚™V;õœ8¨ßJäÝP²[÷ñÀÖÛ8‡ßäoÛö{yóIâ9Z?{dîbÖT}÷ù—_A0©—q.ããxîo/vºvõêÕ¯?ùðûœGä¿ #¹°µeSÜ÷ÑklcùTAâúסI’FÁ½s`Ü·õ¾¦¬uGÄ5ú^M¹^ÎA 5h®™{YøUérLÀï7;;,°**"øœ‹HﻊÝáôup¨:‡ÁèH×Û}““Wém½î—š°UoŽ2;¢ñû¹ùóDz™ßØ!C:ÞA˜¢***ôxƒ9VlÂwê¹ &…Øc˜”}0uêTŽ>RzéßÿŽ+-.½Ùî°åkÃ_‡þ:$0!cX×ÓÏrý‡£fãïç½þaÃìÒ&±±ÀL«ƒ^ذ/ë .·Ëİþ»ÊA@èèhyÄÉײ)òù(ÔŸ„$yÁFwL/‡d— Aƒ@sÍìê` š:¿øÁó*˜˜|ÿgsælðvš'éÀ9Þö©kyîÐýø™G^;oÞ˜BGA·<=÷±¡õ»vÖh1ÑCvÃÏ={þ.))»ƒIØBGx(Ìùx  ÕÖù‡ xÎ}€}:=ýèƒ'žùå—˜ ½ÈçUëýµ0î`íÕp«Õñί_~¹Œç{}¡ËZK! ²é¥òýñGÝ|;ÉQAÀ€™Ãîx\—ÅÞìt^zÍ„äöÔ¿™^|Ê!èb VöÜÀDîÂñÆm½ôÙgÐæô 3(ÓµbÃùo1}iT>ŠXÈ> ®çó»?õÈl57©¡»2±»/$Ôð æ#é…ç`ýÃa£û¼]—b4¾ÈLj8[ÙÄqý5 žØV¦vZip„¤â·$A@ÿE@4IþÛ6R2A@Z_˜Ù5¦`ÆPÃG«cÏ%ÍZŸ_ ¶§yîÐëù[Ïß9Sè8Ÿ\VdœÀyå4&?}Îÿ\{m5Ïóù“Åb_ÇŽ8^­cmÏv}¼¡õÓÇ¥ÿÃo&½kÏëÜ©ãΙ蹯¶^®º¹Ëm[mÖÞ¯síþ‹®yšMù˜sV´ó¤+àÌ"º¨È U{¬A@ü!I~Ü8R4A@|@K½Ù5§ÿ×?ÝÖ1Q“Z ‚€ Ð|D>% ù4 H’Õj£•[ð²_#$cS$ÅÆDRDX@U¥ùOŸÛ•E¥Õ”sæ<•U™iñšESG «Æ ¤0ƃ]ûºð›†þðÅÈÒÈò`µ[»B ŽØ¨H{B\”!2,L´¢ßÄR_"àp8¨ÚjueȨ̂G—Ôq®f‹Å^\Zé(«4‡ðóõüìgž?k³Ùÿ”žñ×_üBõ¸B~ ‚€ ‘Oëöƒ`‘O†Yì;rŠþóÙ&*,­ þÝÓhÆ„Á4 Gg2E„×m™ø«ªº†²ŽçÒŽƒ'iÑêí´f[Ý5c¼Ò.…„„:YÓ ¹ï±§¯©Š¬z5ÄnHÔ+Ý1fp/Ú§«!*2ÂØ›\ª,4ˆ€Ýn§×¯¥¼Â2u^fZ2}ûÚ˼†ª†Js5í9|š¶ì;š¶÷ð©ßŸ³ùÁwŸüÅwÿùÛ_,çs:’²þRxÉqA@HäÓú;A Ë§A’¾Ø¼>ør+¥%ÇÑ÷L¢ž];ŽYYý]ïÂÅ‘ý»©åØéó´hÍ.šÿþ*ºqòš6v0!²€ …6>ðä³?0Ã~Û%5ÞqÇÕã¨ofZP©È°]¤È~ŽÀº‡\),ÔH×_9¼Ñ%æ7¤CvÎ9zgùæÎ§óŠ?yè©_=þêóÏþ™3²7:39Q F@äÓ†7ÐåS¿–œa.òÅæ½ôþ[h(Ϲ™{ë!H ÷G…Ï#·MQx-Z½ƒV|µ‡,+Ùl6Ž#0ƒÀ AÐ…Ý÷øÓ?b‚ôûËö4<~ïõF&H—@@ ܂ڸû° „©l~›íúÝ” mzsBÁhò©ß’$t@¨0?ør ë“AwÏKáabYÕ˜n œ€p[²n7íÎÎQD &8@””y×3ì®G<3,,â7LÞ2Ù íߘ֗s:2xÆ—¬ÛázÎafÇÏO‹ Ás‡çÏ¡Áòû{ò³œ!¾B”Z„¬\,ˆ€È§Íoµ@“Oý–$Á[Ý–nR&v·MÝüéÀW·ÎIqô>ÍòŠ*QòsHÐ'Æ —0?£s¢ã;³&Š0æç&ÅóZbfw©à9ìÚ)Ñõêôé·Åðù~ûý¸T]ä¸ ÍE@äÓæ"wáº@‘Oýò#‡ÑPx±+*­¤'  Ò…~Õ¤-0ö›&§’ò*Z½õU×Ô(,À×Oú#æÉE »jò#v¥ñ$£hü´µ¤X~…ÀEfv—5ßÌÎ[ÅðÞyÍ8£ÝáèÒux¿ïó9xVýòâ­ü²O–" òiKt^(ò©ß}à Æd·³´š=´Á‹8ihY‡~Àñ«ýÇ©Š]…×0Q¾~hvmì)á®Ðûð>™ƒÔ²ö—«;^ÍìµÌÌÎrxñ\F˜Lòq<«2?ÉP²O‚‘O}Û¤ ŸúIÂÇþøé|¥ý€Ç¶@Nëw¦ãg Ú½ À±¼²šŽžÌ­Õ&YɵI.-Òô›ï@,—Tvó-fvíÞ{¤€@kšÙyÖÏ% oºwöX>&Ú$O€ä· %Á$ŸÊÉ¥¯öm÷vòwùÔ¯H’féûŽžâØ>¤â 5§—nÜKOüõCú`压.ÿÝËÕ±ƒ'Î]tÌs‡•5.YÇ/}žçuú÷ÚíÙt„ _cÒáSùd®¹ø±1×4öÄ“ž¨³™c ÔX,þ¦MÒZ¤È¤N§q \ÇAjlå&Ú$O€ä· ¾’O9œ‚’AõOx°º®Ça-»bycRKäÆC'riÓîÆ‘¤óÅt® ´1Ejò9þ.Ÿú!I²QAq9Åš"[(6*2œö=]ǬìdnUU[݈`ÚŸmÜÓèó›{"ÈØ[Ÿn¢Rž;Ô ~êcÏ¢²JergQ$ɯ\‚k’Æõ7…„†fÆš"ˆ×"IêG ­ÌìÜK€ç26*Òn1vçýxfÅäÎ Ù CÀI’|#Ÿò Ù˜ y¢ïÊ>E&–]“Z[nt/Ã_ï§½GN»ïòÙ¶¿Ë§~L|Äó).¯¤Ø˜È5B.É„ÀªGxé“‘ªòÚͬuÇÁ“uòÞvà}¹å UVUs ÒÎtËÔ”[TF±&ªÂ\C¿}cõçý7OI€[í3ùÅ”’Ccu§ Ãz«üް6è#ŽMTQU£öG„…ºH:ôû_l£ì“ùØ•hPÏtšyÅ azðÚ’J‹ôê¢u*ðë“÷Îhð>u ßÈqŒgY¥™ÌÕ¬IbÏ‘6erç'AfAÖ]š$cXh§„øh§—,V˜B:("²gÓ®51Òßô‹}tE5÷¯cgÎó³ÖÅkŽ_ò ·;?Ÿ}ºuöz¼µwB£š™–äuf?¬œçAšI£ú·v1|’¿§™ÝuMÛ’$ÄE…ä¢ñE7ó‚gØo=ÂpÙ$ ‚€ Ðl|)Ÿ‚p êÙ…ödŸ¦¾Ý:©2åœ+Rr`÷´Ä:eÌ+,£Vm§³ù%”œM7MI]’ã¼ÊõÉ™‘á¡J.]¸b ?[@];'QJ\ÝØy›ö¥õ;SY…™z±Ì|Õ˜þ”Éç­Ü’E oá,ÏneYù¦I#¨Of'¯ò,îÓœäÏòiójÔq „C+“¤j6;ÁhIb×4°—³ºHÒáÓ4ëÊauHR)wˆ…L^f^>„ºuN¤E«wÒæ½ÇhäÀLêÑ5™N2)ºcÚhŠ29Ù}LTO\îJ7NFûc´‡Fðœ°a˜÷¡“\=e0Ç&:EyL´t 5†P×N 4~h/²pÿóÙf¾_ìŸI#úuS„eHŒR—4tgSÖÀ³´ªŠª«kIΞªÞ¦äçãs!`¡Ár„ÑÖ~R¾+×˜ì œ¿ÇA“Ï/fbMLÀcéæ«FÓè=è,ïûå+‹è¿u5 dr­ÓgvÓŠM{é?¼“r Kø¥µ•10 Ô=-…¾{Ó$НíOúš§æ}ÀšÚ2e~MÃúf{Tä\ŸÓÒõ¾£g蟋ÖПò- ½ø9^Á¢¯Ù¿Å$)‡_ö¿ym‰*n6ÄF™hP¯tºçº+x Â{÷Ó’¿¾³‚î5‘2z^TU´Ãþ£g‚$y3³KlfÐØ‹€¸Ä<Ÿ!7àxfÑÈx†% ‚€ ”øR>@yqÑš<ø>B jîÎ>ICùûUÊáZÜÓ+·SddÏ'ÒWLdñ@ü£·Mõ*7Ö'g^6¨­Û™MÇÏÒŒ ƒ©’-ªVm=HÉñˆRBŒIÉÁAaö÷åætÿ W°Ì‘FvQƒž— ȤNI±<ë]žÅ}š“üY>õ›˜µVgbÝÒd³Ú ¶ó{kMîÀž-VõLO©“uÖñ³Š˜ í“ÎQéMÔ¿Gí9r†â£MEá¡!ÊÃâ !ÅsGš8¼7%ÅǨNncí×aÖ³)[AI9];q( ž]é›ÓFÖ¹~\9²/ 'QR\uN‰£ƒlÎgäΖž¯ÎÍè”èòæWß}.Ê´ ;ðû•µ ÐØiÌ›Ekœ i 4IŠ(qó{—p[ãî~ç©ÜBzéí”Ñ)‰ž~ðFúÕœ[ñ~õÃÕ* r—”²ó`NÒîÌ:AÙ`£}ºnÏ5³ÒO¸ž¹}: aòãIôÅW ïKOÜw=M7ˆÖí8H;ÕÍWŸ×Üõ¨ÝéO?öNš›gC×=xódzüÞkéê CÙÆú0mÙ¬ÞÓÕ‹?ºË+Aª÷"?<Ðfvž0ðûß<³xvõsìyšüA  в’–›|Q™^É*$ËQ¶@BÚ̓øÃúe(3<%[2`b3npOž2΃¦ÝÙŠ©DMÍð&7â:or&ö9•G#úvS–OÓÆ X[¹§¬ÙÒ;b£#©7k’ÌS2"dX§$²| otÑ&ç4ˆúîãžgS¶ýT>U¸¦Ô£UÏÕêL_$äÑÍy,e“»½<:<´wW2zŒ0gçä3s7Ó_ß]}¡n ˆèY¬=Z¹5‹òÙ$'…UŸHxp *Eêšâ$<Ð,%º±ôR6uƒùÞ‰sÇ ±‹¢øœúR}÷©ïüFígL¬LQ^,ÀÛO„-ZµDÉÑ@ øI‰}XŒe<ïÍ—è¾&rÿD·N«È÷Ò {hpï ¥Q‚iÕ]3Æ«ã ä9Ü—fMr’q˜·]>¬k+ªú~ìö½¾„—`,À²ûÐIúЉ4Vÿåàͬ^Çœ@Üë‘;¦)³´XCµýà ¥áÃ#E·L»ŒãneÑ×ìç§ÌRZ)ÜëOÿ^ÆjúNœo*-ü|³"{ %0oûïÒÊÃ"k!ÔËW—Äæ³õ»ù¸Yi‚îž95·^ËÓ›ó÷Lx#3-YÝ÷˯÷Ñ1þŒÒ‹vqÝ6î:Äô.—Z³[§QZ¹Û¾1–‰h¦*Ó¿¯¥Ã|M÷.)Ô)1Ö•=Hçâ5ÛiGV¼Õ8ŸW6c|ü;3•fyáç_³Vú…†Ù4a]=~ˆëÚÖÞp7³ÃýÛÊÌ®n½ÔsªŸ[gÇ­{‚üA (ð¥| @ð­‡ÉȾ“p‡ÕÓšm‡\xa dÙ…+.8r€ly¶°”`mä™ê“3A@N±U¾ó:Á²ÉÝ1Ù²MûØê4ÙvU6\ƒ–)ž©¾ûxž×¤ß~*Ÿ^l Ó¤ZùîdÍÔ'RÒ˜²e-T…høýltç5c.Ê5""Ti‡ž¸÷š‹Žyî@Ùó|¤¾ÝRiέ“©œç0=÷ÏÏÔiPA"rÜ®<ñf=åL¾tZ¿#›Í¦JXÀš¡Lòæ°FºhÝÐ}.:¹ ;­]$ÄIÌNí]{Î_á"á Ô‹¸¸X?•M¨g z:¯ˆtïâ"HºüÙYÅA€‘ðrûdÝN&üùÔ«kªÜñ²„yL6—³é]8›{N½l€W37u¢Û?˜\žÉ/r™ða°`ÉÚêy€Ð–O¯ÞN_ï;ÆÒaêÊO×ïâ¼4nho6ïÛ£S¹Êlî\A‰"C×N®Ì@â¸w©kþýéF6G £fŽ¢m¬åÁ¹:•°ö%ë•i!ÈÕÛLÔÖ2A›qùP5xáY}û$,?ø ˜y>ÔX.¼88v†NòHÜX&M#úwWûQ¶j&@H U˜<{Ó”Qjþá2&¥©µDi+—óŸ¼ÿ:6‡Í§ﯢÇXc Ý—_ =üq»oÖ$*©¨¤·>Ù LgSg•y+ýó4³»ŠƒÆ¶•™{•øýçϬ~~±v6ºû‰²-‚@# e%ÈM¾’OÇP¶:úpåNen‡A|OY,¢vŽÏCljRãžrØÅ3Õ'gÂüsè‹J+\—ÀúI§S,ƒÀüîA¾æHmäð5¯Ýí|³ë“ÜÖõÝÇí”&oZ?”OýËŽÜÕ› oý`þЖýÇÕ—Â¥gÌ‚h!wÄ4‚àçÇyT©S":U¥2¥ƒÚB%F»a WÃwÆŸÆéÀì×1Bç[Çà`Þ§4G1Q‘ì¨Á@ðšw‚ÍÿtJaÓ=<GÏäSß“ï뻾¦9kà‹Ñ¯ëæäÓ ×¸ YnD6ÈÞF…¢Ù#¡™m‡Ñº¤&Pk)ab‡´“5ÚÔ¿¡¹†ç´-Zµžÿ‘Ò¢`¿·„ùróßû’ÿËB•÷7Ævf®¶Òcß¹–¦¤Ôê›Øy «ú§¬µ¬ùIOMTZ«µåÙ~à8›«FS?&{î /åü¢R¥}µ߾îr÷ÃL4N±ùi4ÁDf¨¨”  ³Ž8ѳ[xpP¥Úʦ2Á:ɤ aj '7tgS×þ*Ÿú@  |™ús„x¨ ‡²©7‡_­ÇD¶Ï7í§g,¦ç_[Jjc# bÇ“âé…-£w>ߪFÐ'îG«Yú«W?Q‚ œ=èt ç³—5Vð†wšGèa¿©ÓÝ®`ÍÓ³¯,¦Ï¿:@WŽè«Ü•O`‡K˜¹ÿ꟟*gC÷q]ØŒ ׃îcœ›QÏK:¬°•Àš‰ÒŠjO<˜(W)w ÚùÁe{²yW{Ÿ©RœÑü['ìë¯AÏðœ&˜ŒýýÃUJ»¢»¯A’¡y¸÷ú‰ôGžŸ33@´g=  Ïj>œÓÍñàýÇ MÒÄë±L¦<Ÿ1h¾à% Ù)…NÐôà…ûÂkŸª#XùìXB'÷òè}žëÿ¹ëjv^qýæû·±‰b½½lSS@¼¼%˜`°&†:ᣤÓÀ^]y@ã†dÑ_nQ$Ž3`ý_í9â*wi¹™r[)Ž„.Öëyò-¼!µŸ™º½þ§Ÿ[ý[Ö‚€ %¾–Oa•ËÑ<ßÈs¾<„#°{fŽcÇLeôÛ7—Ó3 >f9q—RfyÊpxÖœy9˘H/½»’>doy£Ø ƒNØ<í^üÏôÒÂUÊ\]ÜÏä•ÐÏY~Ýì ÝÇýº¦nû£|ê7ævM³¡óá%N§ð0#ýúáõOv}F¿ûþ-®ßؘzYµ”WV«ãðÜ„ØBÞ>Eiwp&¼MÙO ƒ°#uOcy¾Æe<"ïužÞùz¤'LúÀĽ¹I¼qòpö,‚}»Z46x÷{ç¶‚W‡IÐV`L4Ýûâhr0Fñ¤M˜Ü­ÙvûÐS;w  qzàÆIôÄK ÙÕçyöDs±ù×pž¼ ÍŠ·„¾§&kb®Ì»t‚Yž;¨ϳ۪^šp>qÿ WêÓ\ë.µsôàV;“GÄPG÷x`‘l2˜Èš¤çù¦ë÷ ÷ò¸ï÷¶ ´Å˜ êžð!ò–`‚|0K§B6ÅÓ ‘¥l^mSïŒ4zü¾aJãkd&ˆúO=@i©ôù­½F;lØ•íºM{™Ù¹ ‚€ MBfsî2èÝ3ÆÕ¹&oî ƒíÝsµ2%ÇþH·ð žrcCr&¦„<þ·­S´<ËêV˜š2ûæ+•–HË®ã‡\„Ì ÌŸ2óÜ\Ç©!yÖ½ü¾]WÊoÇÚøš¡7§*0yÒÉýzt w[Qt(waÖý\^º“¹ï×ÛÞ’>?ôîBaC÷Ñ×´tí¸w(Bä­½f\>Li‡^ýhrÆ€yBp¢â9>:ÁÄ ó„Vo;PÇÔ/=Ì‚#‡ó¬…Á¤~$œÛÒ4„F@{…y?0?Ãö°¾Î(¨~¬©…ù´Mî'}_”Ž"¾Ø¼O™µ"(Ìu‚É ÊŒØI :|ÒiN Ï¹ÔsœN0!„Æg»QÍtÓŒ]êÚ=Ò•6 îÄQÇìíR§}LR+ÌÕ4•2À,"Ú-¸1\÷oÞw„òùÐ,a^Ì [+ù¡™·ªvøgÙ(²O?“9r'HQO¹û’3/$Ã…uC²+¬D4AÒW4t}NKÖþ€»_i’ˆ?€Ò’Fõ÷kae§qö#¬!\¹/þ£OËíÏîž¡<½ÀqÐFpðð­W±Kúºs|0¯„¨®©Ñ>žÛ³œ½äi7÷w_{¹š¯ÒÒ‚ÞÉ1”^ûx­Š-ˆ˜›sÛôË\ÙÂIœ`ÞO}éFvŠðîò¯ynàQVñ÷à€Í<ï c­Ž/^³ƒÞ[ñµŒ€—¸¦š}mÑZuk´«Æ Tó©ê+‹ç~hƒ@†o #mã¹>pò‚-æDýù?ËÕ3ƒÀË×°›ñ&T^òÞútýù¿Ÿ«cøx ½ZËqƒšÙÁEƒû3‹mI‚€ %Zn ÊÊùI¥üQ>õ+’ä'í$ÅÚþlüÌC7:]´ó\íÕÆ³ ³X@Çâž0‡èñû®Sä n«ë»×<_YŽ=|ëT¬ê$Ì{ùÞ7§ª9HøHèùQú$¸ôÆâžÆ îÅÎz¹vMÑÝ“÷UÎ[Ùf^1Œ°`®“‰ƒæÁ” É[y\™òF&ÇyXðÔ}î»êl{–¡v¿~=÷eRë>Jm×ÿ½±”î¹þrvìÐK¡5Û³”c ¸]‡–9·MSæƒUl‚'žó±ê¦?ÄÌ®àÉ¥‚€ ‚@3’ÔLàä2A 5)iîC !Ý ñE95qin^Ê µÖi}yÀ,¯½’;ABàÔeÆ<Å¢² eˆ˜RøëN†`v ÂÔZ)@ÌìZ«ú’¯ ‚€ Ðn4Wk·ËA@hm ûîM“8ÐîZË$8ÊÀ¼¤«'\p ÓÚe@þ~if×—{‚€ 팀¤vn¹½ ø'ƒØ8–öJbf×^ÈË}A@ò¯`²Ò ‚€ 0÷ó» ±Ò,‚€ ‚@‡B Ci’–nÜ«Á¢…MŸ%ƒýÕÏš4ŒÆ^K¦Cõ©¬ øbfçWÍ!…A U8W¬½â&ÿ’ñøúÕ úÚª̽"à7q’¼–®vÆÇ˜è‡ßšF7MNÅå•´bóV¸‹d)ø79()Ü^ûS*«¨¢?¾µLÅKò,×Þ\J«·vŒgUÌì<[_~ ‚€ Ð1øÖ5cé>öªš–G‹VïT^;FÍý³–J“„&0r¬ÙÄRe®á@˜{T$㣧ÏÓ–}ÇiÜ^´xí.Ø#Mi™v­´I¿{s9Ýsíxúlý*(© 'ï›AyEeŠHb È“Ãûe¨¼k,6Ž©¸›ö=«<²"pûõ‡ªPKÖGO«pW ïC“G9Ãx,Û´örÜÀjŽØCl|“å]|—ëÛ_o%‚èÀÅÒHUîRU9ÉêM¸ó  %Ä—9|*Ξ/¡ý»Ñà^étâ\!½óùºlPê×­­Ù~ˆ^[¼‘~|÷7TÜ”×—l$D(¾eêª0[T0JÜóƒ•Û)’ã½|÷¦‰ôÕž£Ü‰wÐÿÞ9Öl;D¥åôÀ WÐyîÜ¡|_¤úö«ƒòOh%uù¦=tôT>]Î/Êáý2Õþ»tM½l }þÕ^ꑞB“9àª{ÂñQÔvÝöƒjÀ`ÚØAEŸ% „±ó-ûŽq¡©Ê…öÆÝ‡)¯ „.çxIˆµz[9˜ôL3Heûõ¾£<°PÊ/êþtàØi5P¢„—õú‡Ø,6Ñ;  àÜîì“ôõÞ#Ô+£3MÖ‹¢"#hÕ–ý”Î.º³Ž¡ŠªjúÖÌ îňm1³ ˆf’B ‚€ ЪœÊ/VùÃÓ*RQi%ý‹åÏîéÉ4spOÂÀáëüÛÆdf:™Gÿå í) L²x*Ér&<;9lÅäÑý)‰ñuŒÃͬ È:~ŽnŸ>†Ê*Íôþ—ÛhHï.*Náêmé¾YW°2Á ÿAŽŸ9Ïßì‹÷«Bu€~en‡@X dU3iiáéå[èÿÞZNÛœ`›Ïþ* îæ=ãòÁJ#­ÑެN·M¥ØùÌ+†p¼”r:q¶€Žó‚N{3óý39 fo%0V²v äjwâ¾vôÀît†5JEÌüC¸nÅe•ê:\3¤wºªb}û}Yÿê«êø …g¬%u\r¸C›Š¾øæ'hOö)ÆîÃ'éå÷VR"ŸÞLB<Ž¿·âkîÛ=é|IÍ{ïKuÊùâ2úxõvv›}˜ITwElžÿçb2ó=õΠ7¯ãgî8ç­F¸‹iùÆ=üœE(îv~&m6;¿ÄÏÒ«®¦Á½Ù»wÓìœsê\ü[ÉšÝewÑ”ËЉ3ùàu»:†—9®)-¯Rur] žfv ªÀJ’ ‚@û" å¦Ö”OQÃÏ7ï§ùï¯áoò&Ev°U“NÝØ"éÛ3Ç)yôø™*,­P2(´GS1×~ C‘vÊ¡¡}3èªËúÓÖ" îÕEíÏ:v–-žÒ(51šzuMVƒû{Ÿ¡Žù‡H|G!ÿNáëêÛ¯úèŸ?˧~£IrØ£™½æä•øþ‹³A£_9²Ÿê(ñѦ:'¸wÈb¶ÒR\ÇÓk·‹˜èè„Qn÷t„GåaÊ´pÅ6×ntܳmÌ23ùû÷ÒÍÔ%%^±tÆúö»2ðÁÌcÂ.&Eî¸ûà6’E!€~yã”ÑJ›zèÄ9ÚϼT‘Æ0É¿aòHµ 4­1Q+Èù¾‚µB BÝ»$ÑSó>P#œl±ZiîíÓ8¨m˜":`8ßä+$€mŽÑƒ7MQD=ëøJ`"–[Pʃ ½X[­Îÿý|ÏAüRÇsŠ´qg¶Zãß®ƒ'x@"–rÎQlt„ÒÖÞ5c¼:3Ú{®¿Âun lx3³3¨g _Ê)‚@Ð"à.'µ¶| ¦Ò;#UM÷Ðò%dJ¤Lnt*­0«Ít–%uJOWñV«­)j(ƒÍöÜ´OG™a Wöiס|ü‡“ˆ›§Ž$88ûjÏ1ºzÜea’Ù9Éë~×Å>ØðgùÔoH’;Îq,Œ•W™©ªºF1c÷c-ÝÆˆõíÓG×›M¨Ñè:–Íf=î©Lƒ°¨GÁѹ0ßH§ˆp'¤±©TžžéVV‹Nd­Óû_n§ÿ.ûšž¸÷eóém¿çµÍý ËÍfJªK››Ÿ\€Èhe"TöP½ë”™–¬7ikp vÒš$…‡9Ÿ-¤ÊZ‚Ó‰=Ejó˜”F±Ù©NxöJʪ”ævÂÐÞÊ$Ú*h²¢ø˜;IÂýbÝú+Ìùt EBœSÃ’G×N¡FÀp¤-“˜Ùb«I™A £!Кò)°¼zü`õ­õ†k˜›|šÄò)Rž› z¾¨œMÚ;+ïxøFÃòÉ=aN~(/0QŸyùÅÑÇéIÃûvemV}Ìó–ºwIQrl}ûÝón˧~enÁØ»§%*¡'ëxnsq÷Éuý¸³ÁÙÂú]Gx¢\9­Ýž­Ìï @f21Â|¤Õ[R>wÌl¶={¾˜÷'+Ó!8{€‰Lø²N8ë‘uìš—)—ã)Єª¾ý>©g1‘ ¡ØiÒè>2â«ûH>…@qY>™«´@” Þ©Ž÷¬43ÐÝ8õÂàžíÒfžO”Ä„äÈ3õÎèÄÏFá¹~0ëÛu0‡Fè®N›Às öñÄQh©0Ê3 è™®4QåL–ò‹JYkTà:s«kjXËÔ—¦ñDÕËuw™ÌºN   1³  Æ’¢ ‚@‡EÀŸäShâØæ´Ÿ/®  ,§B^XkVׇ¿¿{œQ²é9¶ÖØÏßx¤Á<ÍÉŽ°Ì ÍŽA¾Å||ìÃÜ¥‘lž‡„¹ÁõíW'øàŸ¿Ë§~¥IÒ|'žxÅ#ÛhÈ‘ìD¡½R¿Ìάò®ˆÐöxvßš1Ž"kµE·ã2úˆ2üáߟ+¯ywÏËft tÛŒ¾ÏÎ~ËÞH0‘®ÿ·¯¦€5à³è˦skÙÉžc4¼o·&Íã‰6EÒ ¯}Â/YÝöq^±èš§;®OóyΞíQzÐø¡NB„ç$‰ã@”°þ=yÞ‘GÖ'C9`€)â˜õívanÔv$±dÝNzfþ‡jþÒ ^]é¯ôÈ!0~Š™]`´“”R:6þ&ŸbnÔ7^Nï~¾Uͱ‡æS7±‡e¤Xv}›9ücÑzõ{Ô€Lu î`ÉôêÇxðÜA‘,k‡½æ™yÞ:œŒ•ó¼ýv„ó»Tö´ïxÞö«L}ðÏßåS¿!IºÂüÇÈs†úweÇ GÏÑ1vÍݳkŠš‚”zÑ›Š™cbÏÓ8,æj‹êLîÇáx l?¡UB0$”÷±{®VÑñ;²Ö;ÉÍì :£&Z8^ß~ki~Y“Õ5Ö¨p au-ðÎó–ÞC®<0§ F“ †×é·ß¿]oÖ»Ƥêžë.çã ÜÎÓÜóÔ‚Ð`±Úlu<Ôáø“÷_§OSëÔÄ8ZðÔ}®}÷ΚÈêÆ»¼òè0½ûöµ—«‚½fucw†çK*i÷áSDKtÛôËxäø‚÷9¯™ÈN¥yCÜÍ‘šS#¬”ÈÞø""xa‚\A–€3ð¾8.ÙN_#{:×LÚ‘ªÛ*u3»VU2A ©¸Ë<ØöšD>õ K“wBƒHòi[¤‹:Õf­2[,Ø_GJ×êLñ‘,ÔGòäð~é d±XxrùiÊcOY7Má³9JMnݸ6ž0±Ëå¸LÉa5Ô) ªÖH21–À¸_ŒˆxšÛ±-ªƒG¸+ܪyQÛ¹“MA@hbf× ÐäA@|ÀE2ŽÈ§¾Yçˆòi[$km1W—T\D’ Ò„ð¦„úèè(ªæø+}Òâ(<¤˜Ž—Ñ‚×*?ð#9¶Ê€}GÉUÈÚ€Ÿy¸Q„—8ic#Ê´03%˜Œµ8š(†±ž KÀ8o÷T\Zá°Y­ùîû:Ò6<½è m¨·ûv°ãàÞ´i¿×9Û fv¯ÙÆ“fm ^„ Åï2ý»±˜b{5¶nrž 텀ȧ¾E>ÐåÓ¶$I Ej©ª¬8_VUm@Ê(ž{¤>ü˜LÎB|$¦j&5¬IJ·Ú(:´ŒNWÓQŽí2® ãbL®˜C:¯Ž°®f/y¥åU(¶ZÅA‚s½„jJ ³³Y](Eq0N`CÑ1Ñj¸_§‡» $ m6±TWŸaì.] F<µ Á^ÒrÎÐcg¨°´‚ã Tr,kÇ@¢¶`-cBL!PÝ`v­Ý==E‘iw¡¼½ú‚n¯11– 9Y1»¯ao<®ëÚh¯R6|_ıÀ3‹¤Þwü{ñ­e _äy” 1ÖâyÐ1ªrL©Ì.ÉÊ|ÚaäëíåYlù-‚€Ÿ" dS.›CäSß´P°È§mE’\á·ŸÌ>¸³kÏÞwÀ„nÜ^uZy8Àüh>`j‡QV²Hî;U™©Ìl£r Q »æ.4W¸„$Mí(ÉÀÓ‡B¹æñ¼6mdâÖ„†(<GáêO>ÜÃ?ÝÛ,¨ ÕääÚˆ÷W|M%LŽº•åÐ胔YvŒ"˜$uôTA9±=épR6«¦µ[÷Ñ]×^AC8È«»ßÚ8¡½ö²ã–ÿ.ÝÄZ£*žSNÉœÙN!`J<áYe®Q±Ú>]¿›Öí8¤Ü@ è4«Å»Áoœ˜vðÖ’ê ‚€Ÿ!à.ëˆ|êãÆ ù´-H`‡Hu@^³ Ysg/çÀ¦·g眣¾™i¼ëBÒd(ÂNŽh'ñQ#ÇÐ’°©<µU±6©ººš5MV5š a &SH,é}x\^UA6 Cj›ØAƒ¤Ìí\Z¤‹6  X5œ?{æm†O·“Su§ üèè#V6Û¬©©¡UÛ²h鯽”d. Y9«©K¬ %i@ûe©ålt:mè6•æ¿¿’nš:Ц¬úœîƒú_®u{}ñõ>Z´j‡òj ·ã¬Í’tEĺÀRÍÚ¶¢ÒJzmñœ=˜¦r_§Ëç3ßšíu¡D²%‚@@! ò©›Kk‚A>m ’¤™:„o$Ø0Õlølñ白û\ýö²¯âž¼Vˆ§koþú‘¸ó·Ó5xÏ«13I2×’$h“`:!¸#$Š.Ð Á„ΩErz„iÌí0/ Ììp8º'¸cä6°³[»¢µ’„6B[évs?= ·µÀ B]Íiå–ý´ü«Ô«ø0MÉù‚Bíl·)©^@ o:´VgN§VòiÜ3¦k=¢¤Ûë‹Íûè£UÛÕ¼Å$ÖyõX_o©;ÞÈNÉñTX\NŸmØ«´ðWDlÒϾþxu½š–íÐr{ Ë§mA’€4˜º&H°eª®¬,+ß¾aõ›!“§?úæ’õŽo™\ÇvFŒ»Úf¢„mÄù‰2™ARNXè…vÀ^;oI“¤`¶¼ƒV IcÄ9&I˜‹äŒƒ7ßÊå7“I£†F“ý™üb:¸sÛï ó0A$m4š$g$ê݇rèóZ‚4ýøR®¦¤Æ " ¼¾è1“­&êœGCûvSsàs}SÎA{íÉ>É÷Ù¡RJbLS.ïÐç‚H¯óEDŸo΢dvì0¬_¦ÂïOÌY”$‚€ àB@äS-ß6ù´-I˜:p3/pïTµcýš‰©ñö-ô!9¾3k¢Á]£ ]U+“2þÀÃÎD B/&s;µH6¥IbÎJ“¤‰ç´ ب…'­ƒ©'h“0¹ÂˆQ˜Ò.]˜Äí4H H[3œ>vøŸë–.^ÏÇÑ6h#´UP$mb RIi9-Y»K™ØAƒ$©é·âÈ$zgÙ&êÛ-ɸSËÛôœ¼_ö2›-ôöòÍÊÄ$IMG¸å”Ðâu»©{ZâkßüÞðÔ&7=w¹B A²ŽÈ§>lÎ`’OÛŠ$A¥‰N»&$,ä%båGï-cn°Î9î¼f|ˆ·9J]E<`¢¤È Tpì ;@ÀBê($ u…ÀÃÐ(’¢däß Kj›É“óx%aLì A:yäÐËÞy fvh´ Úm…6 è„~3LÌA¶õ;²Ù+¢…f\-&vÍlYh”®8¹Š–D~“VnÝO3.æ$ëè„-Lº½o ;iÀ$1±k¨À-1.Šr JU¿Ÿ6nrt¡ßxŸJA@P²ŽÈ§>ìúû òi[“$­I*çö@€¤p^BW.zoYAî¹s—Mšz×ßZÏ´c÷2 íÓÕG  Cðè0-ƒ¶Ž@Œ \éíŒí`Oºb­`£'Êé}ÄA‚›ox±ƒ“»ÕZºûëó·¬Z±–ÏA{`q×$<ˆN-’sRÇ=ÚÆÁváÅNœ4è^Ѽ5ðŽë¶…³¾Y"dzÙÒ¤ÛkíöCÊ‹8ih¢ÀÞ·:Eã‡ôPïM§ûß´WËJ'W ‚€ àèA|‘O}ÔÁ$Ÿ¶IôÚîs’ µ€›*HVÊ“À®Më¶Ú¹={Â5×NµÙ¬“Xc ±¦GB|´!2,ÌëЧ'!òüÍùmÒQWÐó7ö›-GqI…bÏÝ*Í9|pÅÆ¥Ÿ.©¨(=ϧ”ðRÊ Úmó‘Д‰]}›™ ;O•56Çn¾%µ>ŒãªØL…köN©5ÍÍY·Ú©¬Ò¬Ü|77/¹îѦp*`GÀu&§[pñvw!ÙA@äS_÷OyÔó7îòi[’$w¶b‚¤‰"PUUÖ*}Êû?ï7tdîý ¡Rc@IDAT 0ÅÄ$E˜Lñ¡FÜ"©i0É´Z-Õ5Uæ’ªÊòó9Ù÷fíÜvˆ3!‚“¤âÚm˜ÚéùH¯Er Ý6Œ¦vGXHD XÄA’Ôr€#ð Ø£¨ë=ÿKå{·RѦM.!4œz?öÊúé½d>}¬É×ûêãYRQ¥pÖœ››·n/ä‡9NÁ”öHWÚœý¬%kÂ\Ðÿ!Ø#(?-m¯ö¨‡ÜSVF@Ë£¸–¡ ‰|ê[à5¶~/Ÿ¶IÒà ã¹o£BhW^ïx­œ:ðÚ“$—Åôq¦H[Ý aJ§th“@˜ðûõ<$}-ï ÜÓ-m¾/ˆðng±Ø)Ò†ª¶_²”Q%k°Ä™DIS®W$ÉIßù%Ž»J®pã :ýÖ_XËTÃçM¦”©7Òù/> Œû£Ò›ÈÔ£…˜¢©ï³óÉa³ÐÞ¹³Ú¥RáŒgM 9Û\ŽSšcÂåÞ^ȯ-µHSÇ ¤ Cû(üÌÕ:Ën³W|µ—ÎCÁÚpꑞBðYÃáJƒz§«:µIbmú?ž2AcÞœöj¨®rLF@ËM"Ÿ¶N#jSãì÷òi{$@€Ž;Pš­CƒäNôÜ%#!H B#pÕ pvïˆÀlk,84$®‹JÚ| <‚ ÃÌÍ_4A¦ŒÞTºg³*Rú]s)iâ5tîýªßi·>HvK5ù÷Kd 7Qì°qdêÙŸ ×/£â­k)úT_ŠamÔéÿ¼D5ùçÚµZšŒï–$Ý^ȯ­SY…™Þ]ñ¥&ÄÓ•#ûÒ¤‘ýéÃU[,\íß:m ½¾drŒÐàÉþpqÅs€çKKÛ˪$eA ÀGrÖø°a[äSÁG ¸êØbÁH£–KõÚ/äÓö"IŒ‡J耺#jP&#½€éÉB’ŒF$`ФñÕQwFý[wž$ÿõ(9ämx¸c~ª“µwõFM$SמLpF!,‚r¿¥Š”2ùz&@ŸSî'ÿV¿#ºö ”)³IÂŽˆH:3ÿ¯T¸a™:np ÙXízä@»šÛ©B0ÈÀYcÞÍ„¾ù´™µ1ÁË+,S‹)"Œ¦sL!¸Ï©¸~ÒpêÑ%UÕïPN.­Ü²_mßqõ8v€JwϘ@¸~ÞÂ/•3„écSööǨÐ>6¯ûró>“+7kÒHÀÛ‰NæÑú]‡è,k¡Ú*Z<xZÚ^mUf¹ 툀–°ùÔw Ï’ÆWË£~)Ÿ¶7IP àèŽöR„Ek49Òk>$©t'ÄZ/ÀW/z_Yö!—àÍÕǨ9~·{b³§šÂ<:þò/©t×frÔ˜ Z%cl<™s²]Å«:žE)ÓoVÇôÎ’ëõ¦_­UGbl[Šï…öjßꥥī&t(Ó¹üÚ~ ‡By^Ï-WfbSD{Žœ¦ýGOSfZ2­Úz@9E@©§Œ@Czw¥M»SqY%Y˜”èÔ¿{ÚÁqº>]¿›&êG‡÷£÷¾øZnõ5ꢞ®ÆºÕo*7A °À'NäSß¶¡Æ”øÀYc­eS¬õ~ßÞµ™¹ùIÒE×À$$Mœ¿ÄÔNãÐØ5ðÔIc«wˆµ±ö‡T¼mùÏ_ëóŽ,yÑ¥»kDZwåµÇt²ó|M¾Â×Wù4§(v“}㔑”ž’H‰qÑ´Žjê²lÞwT¹ÊNˆ¢¼¢Rê•щveŸ¤ÜB8†$:s¾Øen7´waÞц]¯.K5{•[ºa—úkR„Jk«5ꤗ¶º§ÜG @@ËP"Ÿú¦1Ý…2­oröq.þD’<«æ×ÀyV~ ÍE dÇJ?•J¶®á,j»Xm{ÏÑ|6‡Ç]ì;hYJ ÉVÉNø·¤æ!`àñ{Cܼ÷eŸÌ£2ö°‡I3& ¥n“T€ÛØhU™ ¼ÞÄh4ªØN¹ìøÁ[:{þÂþ*s k¦œqŠ4óvìA@ðKD>õËfñ}¡ô\ßç,9 í„@  ž'_û?öxw€ú<õõýù+d>uœN½ùçzѳ••PÞò÷(ãŸÐð® #{ºó‡Ô\Ü›{¯ê\a®¦OÖî¤íY'\ yÜ“`~7ï½/é‹Ö°Ç;§öÈÛ}µƒÄXïmaa/‹þ–ÚwÃCÊ#‚€ ¸#àÏš$÷rʶ Ð$ ^Xšti«œ¼÷‘êÍ×ÁžìŽþñ1ç$öš†¹J:ÁYƒvØ ÷a}êµßךîñ\“ê 统ÓVÛç–ÜÏy´äþÞ®‹1Qy¥™àt¡W×TÖ(%Ó¡No‚…ôeîÎó’*ª8›Ó;“Ozv¡#§ò¨¼ªšM÷¢\ç{Ë¿­÷qqÝž „JA@úMR}ÈÈ~A À$w‚t©ÛÛ««Ú ]ªŒ||ÛþAÝ3“-ôWæxº>USiÛãôñCè‡ßºZyºûü«}Ê,ï®ãé¡›'œ5HA@ÀD@4IÙnRjA@ð«¶ ,ÞÒÉÜz™MíàêÛ[ÀØÏ9è,¼ÛA'wáÐ&½±d=……9xlˆú|?ø²nÌ¥=‡OI‚€ ‚€ à¿Iòß¶‘’ ‚€ à ébY¨Ø#Y,Øwñ~Óä§ ‚€ ø1bnçÇ#EA@A@¶G@HRÛc.wA@A@?F@Ìíü¸q¤h £Š}3`$™O¥¢M_´:!¦(\[Ùê÷‘‚€ ‚€ Hˆ&)ZKÊÚ¦ ™·˜Òïþ~£îi §øQun}'%M¼†úüôÏšD¡± õæ“ýÉSo ¡ >£o®£!ó>¦Î×ßí“|Û3“®éÆ)#›\„&¢ ¾¶½Ò•#úQrBLÛ'ÆE¼äIA@öA@4I탻Ü5Ȉ9ºÞõ•l_ßìšÅ¾’J÷n£“ÿøm³óhì…1FÐÙþA¥;6Ròäë,X¿Œ¬ÅÍÂïÎ 3)>Æ{0׆ Ia¡í÷*ÊuÜ«+×óˆÚçº@6A@A Åˆ¹]‹!” :ÆpŘ@I“®£Óo¾HGPÆý?¡S4UŸ?KåY;ÈRKÇÿúsÊ_ú®‚$sÎ3dc2•ýë¹Tuòe>ø¤ ªðÔtêýä_ÈZ^¦ˆùÔ1&Q{Tæ3'Áe«ŽdÑÑŸ ãó~NIW\C‰ã§©<Òï|„’¯œ©îuúÍ?SÅjê7n¦ø‘éļgéÌÂÔõ&a3\÷õ¶;x ßËB•Gö{;ì—û"#ÂT<¢×—¬#o÷Ëì¬Ê™C‹Vo§÷¾ØBW ïKUæÊ-(¥=º¨xG8/ûdÞEuy}ñ:Š ¿W&'Õ4vÁû«êœo 1P¯ŒTZ³í}²a'M3XG™`¶·àÃÕTTZIWs ÙWn¥x™1a(8WH™i)Íd§G—ÎÛJÅå•„ë 9ÊLK¢ŒÔ$.Çz*-»0Ol² d{”Þút#MÖ—µ^FU÷¡}2Éi’$‚€ ¾E@4I¾ÅSró0Rañ]¡Î¾3ŸJwEåLf’§Ì¢Ø!—)íK $SklW73ÆÆSLß¡tä?!kI!¬^B}ŸOáºR “*$h•޽ø¸ÚÆ?kE)ÙÊJ\y`_î'ÿVš§¾®êD6Åý?{ç_E•þýgæ¶ôBB ¡we»VIJ6PQAv…¨ìºw×U)¾Y)ê®ë®kY‰ĆØ]] Ý.]z‡ÐKHϽ3ç}ž nBzr“{“ßù8™vê÷ÌàüîsÎsœGG~Ìbm8e|ÿíÿe‰Vbx^”%¸öò‡3o{wXÂêÀG³Jâø¸â[[bïàǯ“7ã°ÿ­jÛœ« œˆµÉc/[^”R´‡­="h²sX– ìÜ|¾VH‘ëX¹yÕ· ™¦¢l¡)(,¢»®B·“þ÷[ö8ìe äµ÷Pq^¶¥é§uÛ9IWž:uIJ 5[Ó)}ÿQ:š•Kù, òe±ÕÊc 5Ég5[z$ÿö‰-)· ëT —­ã„‘´aÇ>êݹµnÃyí¶îÙ’ZP:—ï3 «ž¶ªc›x«®Rÿœ¼j[< o3[¯2³síäUîù•ð{'ø@@@ BI¢ÁP% ÜeÿPOEwÙË5>ÏÝ^lmñ˲Ò:<åæÃâI†ÕušZrß`Þ±[‰H’ù@•W‹V”<îaÎw†%`Üñ‰ly:FšËc íËgk–ÐNöŽ7Ðþ×òüËKny’:•ûˆ¬ÛÃOSî¦5´ïíçýoUëXxF³UÃåq·ïU¶÷O'V“ÅJuBëøXڼ뵎£í{ZI¼l*¶±åèŠsN£~lyY¶~‡uû¥•D;ïôn”C›víç},íØ{R,Š…IÂçß®.‰/V x*Rר¨@lñcÁ$ÁwbqY±Éœ"[¦$D…‡±E+›ÖÐnºèW½¨eL}ýSñ³dEà?3ŽñлnÖiB\´}™…S&×ÛJÛýêÕ³c[ò²˜ªNžbý*ü¹—½‡shî ’šûÐLÚÆ´Y®ÒÄjÓt³ Ø2PUZã„[mj—·­´˜ñ$Á,*¬4›Dö8'ó™ÖþîZkRG_²â+o!™…änÓ¡Tzeøx’—}ù.í›óïR÷ÊžˆÐOz¾ã´ãéIÄf”²Qª<Ïcž­Ãê.<ý ŠäüÌr„Žû81.†-BQ6[W¶¤¤DžTQËЯzu¤m{Nj'iÄT’×îeSr]Êü5{ÅKd‹ÐÒ›J®ÛbñY¼|»~ˆ5äoÉÊMd°¨ÙgYžÂxþQɼ'ÿ°‹-T§÷è`Õc;ϛ˖„oVn¦Á{Ò…<ìÎÍϱ̯ªIžg±X«I:ÄæL"©9÷~o»üRnoaNòaTèôÇW¹8© –½;É}ÙM$Ã×Ì¢ÊaëŒïx&µ¹á·´‡*øŽ¥¨>¿²¼ÉU77çåÍä ü¦A1§ŸCâ‘.kYñröš­atÙlò²G:Oë$Êúy‘µÅ_p%e¯úž‡í­´œGH} ¤Ÿ,–×cêò§'ȓ؞v=ÿWŠèÒ˺'VªÂ}»NÆ«äH8 ϘCÚ*‰Zí[ÒW’Ÿ¡LËš¤—côÏlÅÆ´sÿ‘1!Ç;?ýÆŠ"Cäf¼òIIt'‹‹5,”láQrƒd®)ßÿ¾z+‚ßJ7oþÏ¥ÒØùØÑ~d‡ 2\Ï?O¹—Æs–üƒº÷æ/+ÉsÁ KSúÁ£ôÆgßYó¼ÞbëÑ’r„™žö±X‘„§<ÿö» {Ê ÿ¤]yÜ#PòAHntŠòâãÝÑÒ–¬eK(÷Vkí¡Î÷M³,?ÛŸü …'wåuˆ>¦3æ|Ë^ç/ñj—/Þó\±qÖZFmo¾§x~Ò‰Ôé¯>ÉCðQ·)ÏQï¼M±g_lÝÙ3ë)Êãaxݧ^¼’òª!¤¼NA@@ d@$…LW5ŸŠäåÎÎ/ÔÄŠÁCäjŠ?ºuróG|çÉ©ˆŠx±U™xŸÄÎÜÇ èµ OºßHÉÙ»©[Æ&ê½# ë(Õ¦ ™FÖA7ßâÅNœ4xØ3`ÿ¶QÔ5©EFEZ<…«ð-ö˜Vk=k5³¢þêÛ1×øÑhã\:xô8[CÜìõN†Nº©ªu”“_C•-ë ‰›oñb'Nœš¢äƒb¬>ŠŽŽª÷þ’÷RÞÏ¢ü¼ƒ ÕN”   M"©¡‰£¼ŠȯÐÖ¶{óÆ5I»ÞüËÖ½tv¿.ůòº á’‰ê2C~I÷²@2 ƒÏ{Ÿ“ûÑî:”ç£ýÑI–àåD)Ü[@¾lrÞ*ËhjŠ.ÊsFS¾+ŒIhäbGí£yñÙ„jÑ"šbbx“oæ)\…o]‡ÚÙ +ê¯ÎÜ_qa.Ú~ø8È)¢£,ĵ®‰C^‹÷Í-˜¼@¬a(†hZ ,ŽZ¹ JŒÐy®X„%bcÓ_ò^òZNZú¶­?3wû½mn]€ö‚€4qIM¼ƒC¤yö‡–ì͵Ë~Øvö¥ÃŽý¼n{ ‹¤Z›(J¬ü1/¿¸‹@²— *þ çáx¼žÇS@m¼>Ê)äÍ«Q‘ÓMy*žrU­‹®5vYÃÈžÖííÃÛ‹'o77;šÿehî l5’![b‘kËP;ñ–VV$»qUõ—›=Þ%çæSfNeæû¨‡âÉÌ5³zžÀíbšÄ^,FaÒW¼E¹Eydh)Ͻ󄼿ø½TÊ4³–/]°‰aú¿»M‚-  B" ÏAcðÿÈ’Ï]kÛ½uÓW¼øåM[v îÚÔºŽ¶ò(7©H)JÒ”EfÙóóðdöüük®R ¥xþâK“yÂ÷4ÿb^ë²k“0ýƒ·K’%ÿöÁ’ã@ˆ8‘ æ„—C—n¶À1CÖœKbA²†o•X‘êî°¡l»ªÓ_ááÏs˼Ü_ì< Qû«lýâ¼1ûKÞǵ[÷h{wnÿ„Ûj¿¯þïpC @   pIGŒªA@Æ¿Ù\>>ö}ûÙ'_wèÚý²·þ÷CôCc¯ÑÝ.G5²)?Š|xËG¿ùÀ,>?áœçÕ°H*8ñÑ-Ž CD’ cjX$õËr´^µˆ‹•K l.b’!tÂËö (CëdNWDD¸µÉ0;¹/B©¿ÑþêäÙýUä5ˆßGSƱ¥Ÿ|ø)×SÞUû½-½‚ru8   Ä ’‚¸sšIÕì_¡åcK>ºŠdËËËÉ_¾tÑúÐËîžýÉ7ê®.:©jÆþ ”{똅’˺1áá–@²œ:YNÌó–l‘ÔZ)KÆO ­âíÀíO‘JÄ#[ïX$ÉúRÅëêˆP²\H³˜qT¼ i±IX"„R¢ý•åi#·5dÉ{¸ïp­ú~éK99ìF¯ø]µ…’ýWV}Ü!‘2]Õ¤+*¿BÛ©­mÕwK~i‘(¿X_Kï“}ÍZm-JòQi»ªK…í\„’ Û*b÷ÉÅV$ò$)žj!"ÉJ E?ÃsR$µn“Ð Å kcG bI6bM’5uDLŠ0rYÖ¥bOv² ù76Tú˿ΠuÜÐý%$HË6ìÐvl\;oÙâù˹­ö{*?jÈ» KRC=(@@ A@$5fR{¸|pð–b _øÑ¼ù,UÄdqÕþ£™ê–+ÎÑë:GI>2eþ%x^’%Žxx8v°;ØðZ$źNФø–-¸ÙÂC‚ˆ9‘dm|.ŒŠÏõ÷c=ª¨•Åu Þþª¨Þ¼Þý%sdˆX¶­_óÁ‚ÞýŒÛf¿Ÿò®Ê;+V`ˆ$†€  Ðt@$5¾ å–ÈPùПÛò&CyÂx“E’\‹>zoáÑûréMÿxý1ýºµWƒúvÑNë–T«u”ä#S>üå\†–‰µD5Øóü-H -’"yM ;ˆ£„†öG·ìíMØØŽìk Q—òÊòƒµ¿Ê«o ¯  v¿È¾>ûKÖA7ßâÅNœ4˜>oö²% ßZýýÒ\¬¼›9'öò®Ê;+ïnÃOàãB@@@ P ’EùÖ„€-’lK’|„‰@ró&ϨþË߭ݲzÕös.~¾áó^ÀoÑüq¨¢Ã=*.6R sùy<ൠeQÙóÚäYÓ4CxÝ;¼ðÞbû0à{ûÃÛ.¨ì¹}=˜öeû§ìy0Õµ¾ëR¶ʞצ¼¯WeËU²P,³Ôø=ËÙ±qÃ’¾ülq~~îqÎSÞËì{9ö·$A$1¦C"©éôe¨·Äž—$sä×joâÒÎfä‹>ž·`ÑÇ´¸[ßÓ“;öèÙ#"*&Îítò"1M \¬Qg»+×nÚac&àóy óò³ós²3wmÙ¸iËÚÕ»¹LùáB,F"ŠŽØD0É;*ï*æ#1¦G"©éõi¨¶Èßš$ÂH’mV±” í‘ax®­ëVoãMD„ĵãñah‡?]•ø„Ý‚·Ÿ{êû{hòÊ»æÿ¾‰µH‘XD$e8ádÏG’t   Ф@$5©î ùÆØgò µ-|üÅ“\çÍš«Ä{ÛÒd[›ì4|«Iù8E@°EŽ-ìùò¾ÙV$±$‰IĬH @@ i€HjÚýŠ­“5±I°Ù–sù`“_µE$YÖ$ÞËók ¥¦&¸i”)@ Øïš$B'o"’D‰HÊ;±É»(÷å]E&I"©IvkH7ÊþX“0ÿcù0“4ÛëíÔ¡¬HjJbépH÷$* ä“`¿k¶H’¡tö"”D0ɹ\·ç!Ùiù€€4-IM«?›JkäãËv+,¿Vûÿ‡þÉž»$â¨) $n?õ"#ݚ˥ô¬,^%4€!!Áå #gzºW>°‚—€¼oö&Ï„l"„DÉö^Žå,H @@ i€HjÚýê­“1ùx“½ýÑf±qd ¤¦:'é(·ñ”0õ?Ú‚Ûô^~ÒXnß¼æfW›s.ö-xñŸž3~{_ágÖ:&¼ù¼o“}ßß_}¿ WCþߺѾ~Ë]ή§ 2–Ø­zfº±Ø¾þÀ }”;Œ:Oÿ£9ݾæ¿ÿÃ_õkÛ¨'·¬qÜðÚs¾uö½‰ÿÐ'†Gø.çÞË÷úôM/=îùó¾}ùÒ‡Ô©W˜ûŽ»‹þê SN¾[]`§Á¾ÑÈ;&Á~ßl¡d‹%ûܾ_A@@  €HjÂÛDš&pö¯×ò‘-¿f‹(²½Úù[šš%I&ÊŸ Ÿ6·cg5Œo,´o¼À¸ÝôiŸîÜXpDÓ53¡fO´·£”ì#£ÕpMQöÝÝ-_˜Q´Sn$´µâïhÓÁ|ô’K¢Ï™??[†VQX8]Ö0«Sêò×gõGºê¥­mÕÚš³bÅyðqçù‘ÆÀ^Œ´|y¦1õyíÙ»,üõ£ Wo¸="zàùùs½>í3^õ|.â”|¥\„%`‹$ÙÛ›"{³¯5h¥P€€4&ˆ¤Æ¤²kBÀþP“7 þâÈ>·n4¡?2ä”àp¸ÞÐEóGŒqß»ï¾kÝwyÔõ¦©Oå“|]#³E+MDÎ)é§=ço_¯é^i*F隸.†+—…Tœ}Š=€€€;ˆ¤`ï!ÔÊ#àr¿Ã‚çF¥RùV,’ô·Ë‹æ-?sÿõl¿‰ßçݵbzš¶žEÑYŠÌ;ýãÈñ¤qÆÙÏHsÜÁCðÄÂP£À†¥ ÍPÑv"¯ËÃåbÍ'ö   AO")è»S <<¶`'_ÝõDÚÔ+yŸ4qüäŧÆ*}EÓÌ;•¦ßéÖ»udé˜r溗mT3ÈTåÜ;5v©+Šv)R]K®ù|]Xl¥—œã@@@‚œDRwªP¤½cj,d”6OÓR+µøüã•ðdžhÒcâ¸)óW°C¶‡Æ­ãa{ ŒÂ#%®Àí²&¦mä|g³Øù³}­º{Íá˜ÇÖ­Qsçö•õ¬Øˆdþ†­KV7= €@c€Hjì@ù PKáaž¹œô4]Óyè]™`xΘ©í—múLmk¡QxÏ[zó1¥i³LeŽ)“Ú: kÑn[€*çT^º‡ïò®dÇ ïmÉ\¿†Ë_Ã*Ilßååòâ €#vhU·0~òÔE’CÚ´)Cd P{?_•ÈŸâ0过êü~Úy5Æ~æÌ.¢ý®””}yQ>ÊhžêCŸ`¤æùì Õ p))Ëeá_Ù@@@@ ¤`¸]Hu*      hI&ŒüA@@@@BŠDRHu*      hI&ŒüA@@@@BŠDRHu*      hI&ŒüA Ȥ*¥Y¸0àž-ûÎëøÉ̈òšîܹá#æÎu”w×@@@@ ± üC©±ˆòA ©è3kæZ©q¿Œ¹û{»m§Ï•äÍ/XÛ1!¹ÍÎ#é?»È1jÍè»ÖÚ÷ý÷sç¤=D&]Á×.²¯÷ýåž^Ó»‘¦¶nôø/ìë}g§#RÝÖNyоæ¿ïózÚHeª—t§6xÝmãWÙ÷zÏJ{\d^›—Oy½g¿°.¢v×ò3S¼§½ñâ@Ãg>••Ÿá>FZXï×_ønÃwO°Óa   Á@–¤`èÔj@@iêm“èfÿ$¾‚‚J§>>¼Ðÿz¹Ç& Mi§½ñJÿûšFÛ•2Ÿ²ðÕ0ÿë³X›NJÝJýâ§÷/ æü/LLîÑØ”3ùoÁ+q”AyšÃuφ1wŸËbìW¤¨O¿×_âŸÇ    ÐØ ’»P>Ô”€Ã󋤛”RšÔTêfÔÛöyEû~¯§Ç©¶kš6ÇðyG—Ч´M|ýëÃéÞI¥®WtâÒÞ\wÇø4E¾RQ í:]£· j]74[)fÖŽ·aÝí¿Y/Ç\ë:í'f}å@@@‚…DR°ôêÕ$°á¶±[Øês¨ïœ—.$}gÏìÀŠ£[Bûž_W•…¡Ì;u]+Ò {ÅÒ­þB‹Ï#\Ñî‰,ZÆœöæÌ^Uåµ~Ôøu"tÄ*å—ÿQiÇh÷ÉkÎ]ŠTòÉóâ#)›¯_¡‘¾ ì=œƒ€€€@c€HjLú(jI€EÒ;,f¬!wJÓF²Jyß¶ÜT”¥8KÐH»2!ÉùéOcÆÕHmíÿú‹ƒíøœ‡cÕõc³ÈA /=o_¯éž­F1J3sítš‘ËåÆÙçöžç3=À ë±.Ù×°` ‘ ½€:€@ ¸œžw4¥nOuŸ¯ ¬K·Ïűƒa¨TÝå¸Q<ÞÙ×±`!‘,=z€@ ðð¸w “fðлy©ZåŸ~o½’ÌCÞzŒ¼}ܼ5£ÇíÍšS¤ÔC+¼±lÑ¿ŒJÙÈÖŸÙl¥úsÙ{UŸ;æ)¢Q²NRq\ó7ºÒ?”ã>³^@†ö,;v¸fí¨q«Î 1@@@@ á `¤†gŽA ^8ÃÂæüC×´{Êfè%caŸY/œð:§åš>ï+ì€ûͲbJiŽYì—ûO¤é?–Í#6¼Å´¬‚Ì[YŒÕ(¬s×Ê>¯Ï|Ï,ÈXÓ{öÌ"þ%f}‡Ví_^gåbü-TÙúõ#×ϺÂÖ¬½â*¼F… 2€€€@M¿N©ÊøÉSÉÅ´iS†ÈB˜€¼þï„ÿqƒ4ëÇá‰'„ ÑÙŸjÈ1ØøSäØÿ¼äFM.›é¢ýäZ~MJ^MÒ!.€€€Ô…@}蓆ü«K[‘AÀE2ìTýty<—±LêHºÖ–×ð‰ D¡•åùIξ’Ûã&¶[XrÒ@ìÌ;›LµŸ%Ò.eª/^þ[ê2.ºV¢éÄ|#Ì9j ¾C1   õG"©þX"§Ð! âH„‘cÈë":žÙo‚Çãú"½ ßPaÞ<3²(Ws2R¬ƒŸíª]î¾ ¸t*r¸Í\g¤*pEèÊAŽŸô׆Aÿrd¨gÒÒRó¹>u¶05t›P€€€Ô”DRM‰!~¨°Ä7ÂuëïþtytLÌóJÓÛ&gïV]36Q‡ìšÇWèõFÖ¡þ–0,tzhwtgÚÖ²gëÝѧVæ}¿yð‘»^yâÑ/8ïš»¯C…@@@š@ÃÿRÞÐ-Dy PL@l4"~\¼…ÝñLJîŠ{¯EaFâ5[Þ£aÛ>Òºgn$Hű›ù_á <„‹ð‰+Èlípº>½kâ_e]#áègójæ°Ð|&G"©Éu)Tù —gÝH·ýáûÃÂ#§uÍÚª]·yž£mîÉy@å¤mö—„Ïõ[Þu/^géÉß>œ*BIxB(5û§@@@ i€HjšýŠV•&P"®›2<"*æÿñ¿ºdççšÓ„_Ò¨Ê?NÂK¸éºã‰1ž8Œcâßòqá*€€€@ˆÀGNˆw ª_%yÆeî§C‡-Ú´ýgË‚#ê¢Ý_à R%ºS#·ùG•ÇíyñÊ+o‹äø7äTL¸   âðâˆêWJ@„ÌŸqó~Îðá)¤;ZŸŸ¾Ø R¥Ü*¼)Ü.سÈ!Î.ûvþ=GÄü¤ iက€@¨€H ÕžC½«C ÄŠÄ‘#câbÓ½ØaRuÐUGø G·Û%"I¬tøw¤b\¸   ‚ðq‚†*W‹€¿)ìÂa×ÒÎVìæÃ쪅¯òHGMo=bÜïÏ☰&UŽ wA@@BŒDRˆuª[m¶Hvá Ií‡ðÅë U;D¬˜€pžÑÑ—q,ˆ¤ŠQက€@€H ÁNC•«E@žm{>R˜ËíI óæ)¬ƒT-vUFŽÌÓ$]ïx‚3þ-©’"€€€„ |Ø„JO¡ž5% ϶̗§ ‡Ë™áÍÁP»šR¬$>óÔ5]kËQ0/©N¸   z ’B¯ÏP㪠ˆ’M,I–PrèŽé…Hªš]µcO]Ó¢Np¶™W;="‚€€€@°€H ÖžA½êJ@žmI'„’‚@ª+ÑòÒ«1ŠKÊãƒk   !I6!Ùm¨tl«†ì-¡¤Š?æ«H†Û5% ”%>ýy×4 Ä #‘t]‚ Õÿw<çõµ‚llÖÜÆe-øx ­þBmkFÀþx—=Bà€oàØ"gF “Ú@ 9Iºƒ¢ûüŠ¢z  ‚½Û)óû¯Þ/zx™ùy/€€€€@(€%)”z u­.z±lô{îcjwÛï«U¦ætSì¯.¨VÜŠ"µ¼à êöð¿È×’œÑqE«—ëñC¯¥Ó^øŒÎ˜½”ú=÷µ¾ú¶úÈ·^¸×GE€€€Ô…,Iu¡‡´ÁL@>Øý·€Ö5fÀ¹”të:¶â›Z—;ðB:¾v9¥¿ôx­ó¨n¨^gÐþ÷^¢ã+¿£ø‹®¶ÄàÑoþG¾¬£ÕÍâD¼RŽj˜ÑA@@@ 8 @$g¿ VAF vÐE”pù:¶| µþõ2órèðïÒá/çQTÏөø‰äŒ‰£~ÏBÇXx¤¿ø…%u¦)“(¢s/*Ü¿›v³øÉݼ†Hש߳Ñö' ¤Ûÿ@ž¶èèüID’Écû?¤ü][©ã„G(¦ßY¤L“²¸ì½¯?ÍÃãrI÷„QÒèû©ÅYC­{ß}A{gý“4w%ýÅu1)oúì-:øñìShîúÏ£%×5ÚÞrEvïGÇ~^\r    Ð\ @$5מG»kDÀ᧘3Î%Gd4íýÅüêBj?öÏttéçTxd?ål\I‘]ûÐÎgSÉ—eåÝáž)dä§-S籠+FP‡»¢ eÝs'´£®=ÍéVÓ>ûwQ왃É`ñµÿÝ©`ß.R¾"Êß¶‘Ž|ùi.7uùÓß(oËZ:º‡Þ2â/¼’~8‹ î%Ó[hå›pÙõ;àÚõÜ#PeÁÕ¢%{˜‡óa w|"ùrޱUÉc íËß¾¡TrÍádïx­á-Ï¿¼äž'©SɱÿÉŽ"ž¦ÜMk,ÑçÇ    М @$5çÞGÛkLÀ,(¨Vã„[mj—·­´˜ñ$Á,*"WQ†‰ìqNæ3­ýݵÖ<¤¾dEU<´N¬Vî6J%U†ç!yéЗïÒ¾9ÿ.u¯ì‰-ñ¤ç;žA;žžD¤TÙ(8fKÃíšm×£áõI pïNk( _“¡v9l‘¡smnø­u]-ØCåª[®›óòfa5ePÌéçðúIg”$Í^ó£5Œ.¦ÿ9Þ¡;Å bÝËúyÅ_p%DbY’{ž6É%é¬^©ËŸž Ob{žÓ4›"ºô¢ÈýÉÓ®céx8fJ"©™v<š]¿²–-¡üÝ[­µ‡:ß7Ͳülò/žÜ•×!ú˜Î˜ó-{{€’W)!ñžçгÖ2j{ó=Öü$»Öé¯>ÉCðQ·)ÏQï¼M±g_lÝÚ3ë)Êãaxݧ´ö9VÒÚ ¿&™;d;r°#ÞG›&%=,ܲ¹ÅeŠõjÛßþh9•r-O{e†Ò‰§»òÊ·óÆ@@@š;ˆ¤æþ ýõJÀ`Ç eC‰8*{£šçe’2³ ßÿ´äX\z‹`B¨9 ·«93¤h ’špç6󦉻6ÿ­™ãTó50Zä    Ðh ’ =  ùp/|†/¿Pwr½T$œÔˆ@‘îV¦i¯z[œ|kD‘A@@‚•DR°ö êU¯¼…™y®(|Ä×#Õ\W$/Íä;\Y"+ IAÑ ¨D€ˆ(²¶ü¼Ü#®­Ðé PQÍ+[á(<½……ûN0n^ÐZ&M"©Iwo³n\‰@b fú–M«ø‚¶;ºs³†R_ŽÂóàžÝßpž6ëúÊù€€€€@£€HjTü(<@ìvÙ²ýòó÷[ oá±m-{Ê5„:ަá=ºèÓ÷á¬üy×1g$Æ'‘Ôø}€†€ÉÙZ‰÷>ÙÒ·ïøbwtmd»À”ØLr~ÂñèÁCoq“mÆÂ@@@šˆ¤&ÑhD¶eC>àE Éöígif}Ó~ˆéÓ]e’à´:„›ð3}EG—|ô¾ˆ$á+œmæÕÉq@@@@ ¨ @$u÷ ru – [ òqa^^vÎÊï—ÎÎ §Å.Ű»ZÀnÂoóšÕËÈ8”ÍYˆΰ$Õ‚'’€€€':‹$þÒÌæ-*8›‡Z5cöp;ùˆ/à-_¶•ß,^¹mýš·ÅuÓæwºRÁ¢T½'D8 /á¶wÇÖW–~þ±8l®ÂW,II @@@ (-š”v¼.5qÖ%±¤Õ”&.€Ö5¤z& –"ùx÷ò&I= ãͳàƒwÿǃÃtêÛÿṴ́8u~úb½m®<Æå9H2ÄN,HéÛ6ÏúßÛ¯Ë0;á)\…¯=ÜŽ@@@@ Ñ ´å,«K-uI,i¾¸?+¥kκòòg–ÍŸ/¿,#€@°‹©lò£€<óŽ×§óB¨{c;vï±%±ØáȶJ‘¦Eù²ÉiÊ7ó²ÒŽØîôsÒùêç¶çj¹ÊqlÍßýkɧ|ÆdŽØD(É;áv @@@ ñ ÜóØc-”×|”M9,_º`imkTgK’Cw|á3}ÓÍ<ó*®ÄœÚVé@ ìyI2'I>èÅ[ƒˆ$k˜éêؼjÅ–s¯>Ô×½çàÝ;Ähì€ Ì›§"¼9šÇôòió …ºKå¹¢”,˦8ݦOß´þ«ï>ÿï'¹¹Ç0 Hb¾žÂ‰! €€€Ñ$üË·îdR—ÕËGà¸ÉSÓ5ÒÖ¦M›|e]*ƒ´ "ˆDÉP;™;Ã[o±¼EóÉ›Üsõ8m@§Ž=zõ Šjé u:ØœÒÀ!Æ›ÝÅ.ò¸+z»}Ü {MS>Ÿ·°(¿àX~^ΑÝ[6­Ý¸jùf.[‘8i”Å›ˆ¤ÞÄŠ$Ãí0‰! €€€4>ñ“§}®Hõ{qÚ”äºÔ¦Î–$«p¥=Í•ù{ʤiƒgNŸ¼¤.BZ¨gþÖ$ûG{¾’8kH8ožÍ¿¬\ÏÛ&>a%›;MñY€ÿþ¥_ô³v3×f?ex/<$+Ùì¹\ÂFæ‰ ’MÄ‘ˆ%X‘€€€@p-b*5Œ?ߨkÍêE$%¹’žÝçÛó{SÑ3ãSgž›–š’W׊!=Ô#ùð‹‡ö¹ˆ±†ˆH²¬I¼—wÂ’× ‰Ë¥hW©"3åZ›‹$B'¼„íôBD’¼×² 7X€€€@p` az?£i´»3©äGçÚÖ®^DRjêØ‚ñ“§ŽãJ|FÞï)¥nÖxèNm+…t PÏl öþÇò±/ý"dh›·òDR)åÂqZ¸m–UÄá€t2cû=µ¹Ø"ɶ²ÙBIöÂK®Ûóì´'sÀ€€40ÖZÊäé¯q±}y.Ú„÷u õúñ7~Ê´ÿS¦ú'ÏOz—\ w¢T§¾AâÀ"–"™§$›#{o $¹/ñaÄ··\c—të’Œþöq€÷"vìMD’¿Pa)ÂHö²É=±Ä!€€€€@£ ’e¤!5BÓµûÓ¦NþW}Tª^E’T¨X(Ñ“lHZËk(݇9JõÑMÈ£ž Èso‹%DþâÈH¶I§Þß‘ÊÚòÙ%­öÚ÷‡Ï?’dxo[„Düȱ-”l±dŸÛ÷\d   U°æ =Ã1ûj:ý¹¾’”@zw9çý"»ßëÀÃîþÇÛz„þßÿ<üpCñ ïµ@IDATαö!€@eäù·“ˆ"Ùìkö»aï+˧Þî-¾"A#Xá¢/‹÷½†¶H²­I²Adoöõ†¨ Ê È:HÅn¾Õm<Ìn˜ÌAâÈãÒ¦Mù²ÂDµ¸°ÀÔÔWÃöz÷þŽšýÿ6Ýž`’Ò*MíãBe8.ZR'öذ÷£¤2ãV=7ؾôâ–ØÇÞóÙ’†³©:ÐÅ!¨þP‰âQjíø“¥µ¬ƒÄ?kïáOµ§Å\}ÌA*[™zqÜP6S9?QÙ'ùðÉ{¦<6Ð0+ø¸#oíxk¨_ȹ(¨>à/ªŸCÝbúë]×K„KÝrEjyû¹ËYí’…bÿ3õáål~2$]ä 5$ðóU‰%ÂhÐáý¬!?Dú s0@@@@@@N€H£     ~ ’ü`à@@@@@ ’ð €€€€€€€ˆ$?8ˆ$<      àG"ÉA@@@@@" Ï€€€€€ø€HòƒC€HÂ3     ~ ’ü`à@@@@@ ’ð €€€€€€€ˆ$?8ˆ$<      àG"ÉA@@@@@" Ï€€€€€ø€HòƒCh”JÕ.â t[çÎíëž9³]D ËAþ    Pßþ¡TßF~ ÐÜ ÌHÓÖ’Ã1nâo}ßÛ,žšž”_X°6º}×69éÛ~VרIw­µïûï{éч”©®àkÙןxÙÓÓç+ÚèÐÃïû¾>#Í1ŽHu›8Þ|о濟žæ©‘ù’“œƒÿ2޻ʾ7=M|KÖúk5òøx]+5à®””å^¹ÿïϺy²÷lÿ—FêÒ‰ãUw; ö    ,`I –ž@=@ Ú´·5üÙ?zAQáMiÝ7|k¡ÿõòŽ•R#X¼h½ÖÅÿ¾F´ÝPÆ3¯¾Ú)ÌÿzEÇ3fêÓ5eÞªˆ~ñóøKÎÁ,¬.<¯ûEýYÉùz3ôUc%ο紌a·˜ËO÷Oƒc&IÁÔ¨ Tƒ€ÓåzÇTê&;¬?Š߬9ô·íóŠö§9ÏÓm×4}Žé-ísÛÄÚéëýÞÝ“ü¯Wt¬kÎ7oÞ@Š|þq”i^§+í­¡CY×u]Ÿ­”9BâÜw{Æq\ã\žˆ—üÓà@@@‚‰DR0õêÕ ð—±…[Xzì%×ýñ™áX¨t;§ë_W•Ü$óNÒõ·”;â=MS·ú -Î3"Ü3‘¯™1ÓÝ«ª¼_´NãLÄ*åW)jÇwÛ×x(Þ.¾–lŸ?”RôKQáIg_Ç@@@‚…DR°ôê5 Àâä2‹‡Ü™ZáH"í}ÛrSQ6OÍmÎÃà®lëLþtâ˜ì£oëc/ºxh\q`!ã¸lV‹¨‰ï®ÓÓ´õ,ŠÎRbY*&3æÈ%vÚpÛˆÌ2·«wV’7¿`mÇ„ä6;¤ÿì"Ǩ5£ïZkß÷ß÷y}æD2éŠõcR.²¯÷ýåž^Ó»‘¦¶nôø/ìë}g§#RÝÖNyоæ¿ïózÚHeª—t§6xÝmãWÙ÷zÏJ{\ÓÔµJQij]Dí®åg¦xO{ãņÏ|J‘rk¤…)]}·áŽ»'Øé°` KR0ôê5  4õ¶It³_AÁ¥ÓGŸ^è½Üc“F°pÑN{ã•.þ÷5¶+e>3dá«aþ×+:f±6”º•4úÅ?Nï7^Ìù_˜˜Ü£ÿ†1)gò?2Þ‚ 4Vâ(ƒò4‡ëž cî>—ÅØ¯HQŸ~¯¿8Ä?=ŽA@@@ ± @$5v |¨)‡çI7)¥J,Á¦R7ë¤Þ®*«~¯§Ç©¶kš6ÇðyG—Н´M|ýëÃéÞI¥®WtâÒÞ\wÇø4E¾RQ í:]£· j]74›-J#$ÎÚÑã6¬»ý7ëå˜Ëb]§ýĬ¯œ#€€€€@°€H –ž@=@ š6Ü6v [}õóÒ’¤ïì™XqtKhßó몲0”y§®ëoEšaï±XºÕ_hñy„+Ú=‘E˘Óޜ٫ª¼Ö¿N„ŽX¥üãò?*íXí>y͹‹‡×%Ÿ’²ùúé ÊÞÃ9€€€4&ˆ¤Æ¤²A –X$½ÃbÆr§4m$«”÷mËMEYгžteB’óÓŸÆŒ9ª‘ÚÚÿõÛñ9ǪëÇf‘ƒ&^zÞ¾^Ó=[b”fæÚé"4#—˳Ïí=Ïgz€Ö7b]²¯a   Á@")zup9=ïhJÝ(žêx^ÐHÍAUµ;^y=[n⥭è3{æz(gdÞY¶èõ·ß=G®õžv‡Iì«aÐT‰¢íTEÊÃs2ísÙ÷›“v=×ÿÚˆ>t¿ÿuƒ€€€@0€H †^@@ †VÝ:v'±Û5oNÚ•l¹I1jü⪲0IÝ©iúNÒ¯’MsjCX(]ÝöìȲi.º—-M342O¹W6î)çJÛÅãèºÚ×}>_X—nŸ‹cÃP©ºËq£x¼³¯c   ÁB")Xzõàáqï&Íà¡wóRµÊ->ýÞz%™‡¼õyû¸ykFÛ!›5§H©†VxcÙ¢•²‘­?³ÙJõç²÷ª>wÌSD£d¤â¸æot¥(Ç}f½4€ íYvìpÍÚQãVb€€€€@Ãø‚’ ß$”̓€3,lnQAÁ?tM»§l‹½d,ì3ë…^ç´\Óç}…p¿YVL)Í1‹ýrÿ‰4ýDzyĆ·˜–Uy+‹±…õcîZÉk1½gd¬é={fÿ³¾C«ö/¯³r1þΪÎlýú‘ëg]akÖ^q^£B@@@H ¦ß?¬ ²¨Îb²¡Biಙ.ÚO®åפä…JQO°$á9ó0ç( t‘)€€€@ `NR é"o#‘r]† ƒ€€€€’DR é"o#‘r]† ƒ€€€€’DR é"o#‘r]† ƒ€€€€’DR é"o#‘r]† ƒ€€€€’DR é"o#à ¹£Â Ä”RZÊOÄ8 T¼Ï x]ùâIi±J×ÊTH—ckoªB‡ƒr Se;ZŽn8³s×ΤoiJq+Q5hÚø[ @ ¦îyì±F®o¿@=Yõ zhšÖK)³ë›:ýø ‘¢H£€ò\‘ß›ÊLçºíЕ¶Ú¡¹V éßyóÈ‘#šÖñA@@@ªO"©ú¬³ÿøã±ZŽy™Ij°®i+eôf1d Wu»]F›1”ãHˆ‹¢ÈˆpŠ wSTx{(Âã&]×y#b!Eœž÷D>ä‚Bz½TX䵎³óò)+;2çRVN¾:šuÜ8’•ç`ñe½«œ¾€sù…MM u¥>7“¿MKIñ6ã®AÓA@@@ Þ @$Õ;RdØTÌ;×ñÕÚ-—k¦ÃmºŽ‡ÒyÜn§Ñ­}k½KR‚Ö59‘Ú'¶ èÈð€6ÙÇãööÉ¢½‡2­m[ú!sçþ£<"Ïdͤç™_häxù…©Î0N/ ½ÌA@@šˆ¤æÐËhc¤¦*}¿o #}:wkf êÝÉqfßÎÔµ}¢e ªQ†ˆœ_XD›w  Û÷ÑÏëw¹ù<½É¡ï4•zº#é…ÔÔ±lqB¨ ˆ¤ÚPCš&Kàž)3.TšzÞ0Ì~Ú%˜W]xºÞ·K• ÞW…ëJ«7§ÓÒU›‹&ÅÒ6MW·ÿ'uòµí(ŠGÝÅ{‹Tkò™‰†®{˜@2T¡Sw*‡Y¨|®Âp§ëð¿RïϪm9H   ÁH x¿ü‚‘êÔ¤ Œ›4íž+ôj«ÑtãÅgôìríݰc½ýÅ桌ãÄ^õN›>ùo•5âÿRÿ—ëË9SSÚYŸI^ƒ÷¼ËɧÃ,ÈgfÓÁŒãFæñ<ÖHÅN&¸ìƒ|üg¾@9]óÓRÚm×{fIÁÜ;¨[µ ÈúD÷ýõoI¦¡b}Ä.¸>;“s’¡» M9•i:šó¨îpìÒ§Óa7Úw§N;ËðÒwz%;Æ]?4¨‡ÖUˆ Áû×[_ªm»e8œq|¾c—²O‡ÛØ»ø5<ÏÊntIJttj׊dëØ6žÂÙ _]ƒ”+‚i랃´i'ϙڱßÈÉËwH¾Ñ¾Ðð ãEV‡ó°°ËyîK¤ÔÈírš‰-c´VqÑZ"Ï#’¹D1ƒ¡tþŒšË1;˜ ù?m .ãQ–”©étÛ Nú¢¹´íIÁÑͲ÷¦þ­ix_áu}®ìÕ¹}ÕyZËØ¨fÉ.M€­IôÒû‹½‡3øß(ý¡´i“þ^:Î@@@@ p ’Ç9WB€oÆ~ ßrꎘ/¨9³w%±q«9ðú|4ç¿ß«×nã)jú3iS'þ‡b²Ë,k‘ÇÀÜA 4ñL¿Š5ý¨}b Ïï¸ÂÑ·kûÒpL€8Ѐ^5ŸaÐÖôƒgºð›ÄåKüp@@@@ Ð ’Mù—"2yÆpM©;¶ié¸ÿ¶aŽØhÌ=*'§ࡘ¼ü¬¢Í» :ó¢KóX(}wJ$\z$‘T0‘UåD ‘2?ìЮ•ó·]®‡‡¹+O€» p‚@O^—¢U{½tàK—._¼`'à€€€€@ `NR È"ßRÄÅ·O§õZÇ{X 9Â=H¥á¤J2ìnjÚG¾ÃY9{”£Uß´Ô”¼*!B­ Ì;×±~=ñi¹ú>*ÒÛ‘ÛLM[Pë ‘@@Bˆ€3„ꊪ†0C×_q;µ°”›†êH!Ü‘Xu§ÃAw^;ØùÄkŸvÒÔ‘ \x¼«e¤¦.t¤ïú˜¡Hõä9‚ÉDªƒ®kI¤ô¥Œ˜¯Vo óÏ~/ŸŒ›4ý­P!o¼e¦ÚOJ;Dš¶G's‹¦ë›ºó—gSÜçŸÇ   j`I µ Áú²'»ßšŠ^ºmøytá€!ØT9˜<7÷ksݶ½GTû„öi))Þ`ª[0×e|êãÈðÝÀ®¯æ9^òf™sÅc`\T¸Áî÷õØèp=Âã¡p‹<¼9X˜ê¬†ø?žFäõú¨ˆ·BÞrò ('¿PeÏ5ŽÏÑ}>“.ºÃ±G™ÆD´ ûï?ÿøÇ|ûö     ’B¡—B¸ŽwO›–D…´±GǶ‘u9ž·îË`©ú/[÷Ðsï|MºC¿t棓æK½‚µRgôðj*‹¢YèèI‰qf¯Nm’()!ŽZÇÇZžëZÿ¬ì\:xô8í:p”vï?Jvì7ró š¦çrÞ‰u?ùäÈñ)AþPùÔUizKŒljÁ&«HVf^þG£ˆ4Þkz¶¡Ì=šÓ±'.ܱ÷o>˜}JF¸  õDÃíê $²)Ÿ€Y MbOηóB±åÇÀU¨ÞÛ’S×MC©K9%DR%øîž2}¤Ï§^óxœîKõqœwz7b‹Q@öÄEG’lâdãDpìØ{„¾]µ9òû5[ÉÉ.¼ñÿRÿ9¸ˆ¼a^oÑeüÂÝ¡õgñÖÛÈW‘VeðÈ? üWLWÅGÖ);})>÷”ÉÛÝS¦6MúIÓi9i´Ì­Ç,~&õ¾ãV$ü:€Hª#@$¯˜À„3â}ùÆØ3ztÒãc£*Žˆ; P27‰Ÿ'ó`Öñn5H²Qe˜œnç˜ÊìO¤µgq‘¬éZG­¸QN¶ºè<¯ÈÁ×u—£k”Éòâ°aš‡LR—÷èÔÖ5îºÁZTD©)F £sR+’í‚Ýõ'^û¬_ž™»ëÝR 3d)€6­â¨M|ŒeÑŠ £ˆp7E²çK·ËEOš2 Å›Aù…E”™W¼Ï¥ý‡³vî?2ìÀ‘cÕ©´"ó¸/eò´%&i³µéƒ´Ô‡v7H#Q€€@“$‘Ô$»58åÍ3FòÁa`RptHªÏ—á™4<« †ûŸz*<÷hÞÕÜ´º®_dx½‰Ÿ8º¡ZDG8âx}±˜¨&§Ys†Øáë#ö¦PèãyBq¹ù…yÎév;)åú‹´ˆpO£’êÔ..?·í9˜ÑB,M}xí«ö­[ViÑAì´b9Y.ºl;^Ÿvñð¾µ[÷8WmN¿èÀ‘¬‹Ég>•2eÚY=;sêä¯dÞUÙ„8Ê`Tetp¯NÆO™¾€?èÏøÝMU~ Õ© $nv&>;Ï—y<÷½´i“oi*OIÑÇôšðý¥ÌȘ¨p_¿®íÚ±5†EFRb 1„P9£YÙôíê­´dåf#'7ßÁBs£©Ñ_|tÒç•§Ä]8I–¤“,pTÆÏœé¢ôÃç è™ T\‘QvneËq²ídUSà!kˆ±w‚¿*Ÿq‡ÇíTg÷ëê8³ogêžÜÆ)^åjF@¬M×^4€®ºðtÇŠ ;é“¥«º:zü³”)Ó¿PÇx ëOÄæJ"©¹ö|€Ûíܤ¯W)ObËØ—„웟Öm/n²Kû2ÔÛž2iÚͼÈò+<¬ÌsÉ Þúåçô³†•…z»‚¡þlB¢A}»ÐÀÞ‹—o ¯º”Ý—¯7eÆ /N¸8êˆ:€€/’u-‚·Š¨Y(ðù´NRïÄ–1² аœU^¶~G½ÕEÜó¤ñZçÇÏé¿ß¬f'^¡=]B¸ÊÖ¡ˆ½š}ýÓzC×ôŸÒR'­hˆ2Q÷¹6~Ò´²c…·»$%†M½çzÇuCB ¶ Q:¨MúíÕŽÖ-câ4eÎ?yêå( Y‚€4!IM¨3ƒª)šåy«ÁEÒ¤çÞ£»§¿F÷Ìxú÷»ôæçß³g¬b×Á?¯ÛA?ÛVˆz€õÉ’•üëôòZç´çP}²x%{ð*®ŸFÒŽ ϦC'—‚1%mÛ°cŸÔr}ì LÖjˆP̵þÄgeuþ`árÊ<–Ëß½ÚŸ+‹ì÷Ø©À_Ù#Ýÿ]zv_úãíÃtqX -b衱Wë¼6OûÒçÊ0ÇÀ–ˆÜA@B™DR(÷^×,k½/ÿòßÐáüÓ»Óƒw^M—œÝ‡–®ÜD«6ïHÆþúBºëº!ÉÛÎtîW?؇5Ú¯Û¶Þ_°¬Fi‚=ò7+7ÓŸ×ó’8ô̦N\ìõ­¨~²vëÝ)âõñ¦KÁCE pç|ѽ7_ª;u-šW§} E K&Bs’šHG_3´½R§Ìì\jÓªaç%EG†‘x“mÍætúaÍVž—ÐÉBÄ¿ Ók/¥5leéÖ>‘†_pºïé7¿¤v ±4â²³­x2îÙw¾&B­â¢èݯ~¦M»öSLdýªwGºúÂ3è/~$ÓTÄ åZixÁLúßwkèXN>uhO)7 %ù(›ýÉ·´aç~ëcøôîÉtÃÅgR˜Çe¥©ìÏE¿êI ~Þ`Y„NëÖþ”¨âúxî—?ÑÊM»ÈÉ~’/æ!E2§eÛžC–]@Ó¤gçQß®I”“_@ñ1Qt#”‹EêÑ?¢Ö<_ìn®£„gßþŠºœàqàè1z‹-p;Ù­2{'¤«ŸAgöélÅË»n¦£Y9,@7Ó„›/±®Û¤LáÖ§K’5yÞ¾^×ý’›è­ÿý Ø‚ôÅ%ý{ü1­®6RúÔÔ¹îÆ–uIJ0G ;?R5B?´Œ‰¤½:êËÖïÅÅÿ®ª€"A@B€þ'’Ut‹$ dÈپÙ,TN ’U'Åh6GåÐg<'HBŽmè»5ÛJ†æ-߸“ ½^^3¾ü~-f·Â¿¿å2v^?JJ°Ö¤ãìe-;¯¸}"Lf}ò uhÓŠî¼æBê×½=Eòº.²ÎK‡¶,˜nÂ×/àá~;hç]Ü&ÞšxþîW?–ÔË?Ý’[X@íå|Ó5ƒÐûó—ÑáÌã¼Ðj$uKN¤8þ¼óÚ x>FoK­ÞRlQÛ¹ï(ñ::œ6x"»¬­Cë¶ïcÓ-­¡Ͼý5/Üé¥;˜‘§—>XL»Y0I6Ë0ÃuÛ÷ˆ÷0^ô¤ÑöÜܯÉãrÒU,>ë#ø|½ÃBPÄ‘úD9n9rdÛ'ë£1œÇ>cËh^Ùö×C²gj¶‰!4 YX×0UÜܹsá}³Qz…‚€@ð€%)øû($k؆ºîÛ¯mÎß¹÷pø9§umÐ6¬Ù²‡öÉ¢-é‡ÈÇî—Ó·¤üð07Ý>¼Øòs$+—>Z´‚-+DçžÖ>^¼‚Öó|±Ú¬Ü¸‹ÎìÝ™ëÔ- »œfëI.Õ¯ü¶ü´v;IÞ¿ùõ`^Ô³¤8ë@æˆõæ -Yëfݶ½t ¬*øx.Õluzä…÷ÄY[‰N¶CÒ®e‘ÓŸÅXëøhk‹Ý ®÷çžF-X ícÝ;´±ŠéÅ‹w~þ­X¹òxNÓ^k1Ïm{ÓæÝY{פ' Å­ÌLêyÿíÃHÎÅ‚´jÓn[-±'1 }4íÞá¥Ä§,júÚÇßX÷î»årYø´8Ó:üݾ÷0Íúôãà‘ãl@¢§Ú9'ÿ%5U;uWÊhð¤ÊÚ"6Ê`¶ø8opøÅŠõwãŽý&¿ÚBYp7>  ͆DR³éê†mhjêÈ¢q“§¾Ëâáö›.¤‹E¥¡Oˆçab‘4æê xØW[r9O>æÙ:c‡¨‰õCL\LõîœDËÙû][{eÒm'ÄÔðóOgËŠÒÞ_ÈCÍZ°È:ߢgç#ûŒã9<Ä®å)IDÉ›ŸÿÀCàR]Ë<žgY˜üÓVv,õv^¶x­¢súu)‰*Î6ï>` ­ãaC%×Åã^y¡kû‹Ã¶ôÃòHƒ8/ž½N›XŠ éÌCe`Vvž•<™Åœ’¹Í"í V*ëœ\—á„yl—Ë"뤬÷,Wì‰PãºÔÚè™NžO4¥.ÙEZ^Û©O󿯠{‚¢ÕÁU‰÷üL{gŠðþcpÕ µ&uÿ¹7˜Zƒºþ|5¯°H_É–ˆ† 2ïç–açÐé=’K $©ƒÌª(œ{z7kþÔW'ÃÏʆ¨ð0:èç‰Î¾?ÿÇõ”~à([^n¢ÉwýšXœ}«Ú{™g$ù‹#[ð‰…K„çÐ3{ÓSºµd}õùåæ+qEÜìØ{ˆÄBÓ«Së’XÛvñPºÞ|,!¡E´µ?qRlI»b"íëòÇí:õ?2ÌCãnÂ.Ö·SÉ:F%)jvðÖ?ˆ«vËÇbpkÞ¤ñ?~r\_Ͳ ®Øšv”燰ÝWÕšzmd¾U_ó;ÉéiÓ¦|ÙÔÛŒö€ÔžDRíÙ!eþ3uòbþ˜ßõå÷¿¶î*’4êí=:ÉV¥EË6ÒY};—Ôå¾ÇŽÚ%Äñ´¶lq9Uh‰àÈb'_ý¸Î²ºüðË6ËJ•y<—b¢"xÈžNë·ïµ,J%Wó@œ2ÜtéYô#éó¾’Tzv ×m£MlÅË’X¿dN’™+t”‡ÍIù2ÿHB¯Îmi¯g$-"”dÎÖnqrO‚Xb¹¾2KÜ‹ãi×i<¬¯² ÃÅ9† õ“ùC2ß«¶aôUX¢ôÜþݨETÄå¦i΢ß!±L¦L™qnmó †tJ™+öÊTbaDhYl½÷õO4ñ¹yÆÚ­{¼<2ô7,BÚ…|ÃC)  м @$5ïþhëy8—24º'ý@†Cæþ{1rqq¶ß<*qpðøkŸÒïÖnÛC× =ó”¦Hº!lÕù×ñ™Èåæ}ý3{¾#ºèÌ^$¿`ÿáïsxÎÓ*ºô¬ÒóŠNɨ‚ zu žÚZó§ì(â>Z†þ‹=óMxl6=ùúç<ì¯øã[¬híØÁÄÃϼK¯|´ÔJ"Ö"M}º[D ÅÇF±˜3Øj–hÅKÕïo¹Ô[üç=nÏ2Ë Ãé, «®¹h€ezåÃ%Ö0Æê¤)G¼žFÃŽ.»o¤“×¶¡ËÎîëŽó\ošÆw)L_zoêô3ʦ ‰s§ëYC™¾y_7Œ{vq¸!kk‰®Na,b»1B ÖöÚs0“&?ÿže=b'%^Mi;œ-ßmŒö¡LÐ"PfŠyhUµ )“§ÿ›-4¿»ïÖK5q ÌáõO¿åù Y¼èäU§TS¼ÀUåº[&…‹åF¼gñž’P´%‘kx VºüÂ"žëTºLÉFæ É!™TÓ u–¹GµHZÓ¢ª_æ}ÏYl"ÿÜÔ‡ÖÕ[aÈ@@ I¨ù—S“j>Ó~ÿï{¼G²W8Žžã®¿ÈQÞš? QÊÊIñô&“ºï¿íНp•¥Á½Æ!PX䥮àa‘,ñ'Ž7B5$³³—e¹ DÊŠ$ Ù’­‡<äÖòðÏ®<ïN\¹wj—`¹Ãÿté*KGxÜ–€“a–²þ×g߬±,¢b…¼íÊsIæé‰¨ùnõfºp@/vÓþ£åi±G]¼|£u,C6Ã8Ÿ!lMÌk~I¨lm¯´÷YeÈpPYÛk—SŸA~ÀصÿýÈCa\·Ý((,bÃ’6ã²Óz¤ÂË]}’F^  Ð4œ:»i´ ­"?}þ¹qæÐ+ç)Ó¸˜½Ýµ s»µ.ìm-˜ÂÇ‹WZžâd¸Xu‡–Sý›S]Ä E?vÓ..ÏWoÞuB(…&¶ô}·z‹åº],b¹“õµd[}Y§Lý½EŠ ¯üvõVK\ÊûÇC-¡³çP†5Ì4œ¸—çÿ^@8¹MKÊ/(²†q^Áëƒ]ÊD¤®‹ÒnÉ­-‡$óÙ5½Ì‰Äsø²ËüìÜ|k>[N~‘µv—¬5öåké^èX†³.Z¶É²Žfϓ٣¢,È|öi]Øòê¡GŽQ¯Û%k{ÉÐÒh¶ÆÖgkª¸Æ—g‡^Öy .mÇÞÃî8”qÞ5ÏcÑ¢¿†®Ú®OPÈ @@À"pê t€x!õC©©¯^x€öÿÿöξŠbmã3»ç¤Ò{WiŠÅ®Ø{W¼êgá*  èÅ«)ºš“âQìÞkÁÞ°adž€HTzo!ýìî|ÏœpÂIH%9¤=ó#l›ú?;;óμóÎKXD}ÍŸë6‰Ë í ÚSÜeX…« ycJ' ÷pòÀ|yú[_•î¡Ü…QŠ@.7nÛؤW ëº3ß¶eq&Ö¯ ‚J^M;¬í*2oºWXÌji´6#î¯5 V+ÇvP ZÀÐ{ˆR¿ÀußW\4 `ú]ß¹lÐÑ‹‰ÚB¤ž‰Z¶jcÀÚdUöö $¦ÿ´…Æ«°¦=Ëgßýöœunª6þH˜’c´$@$@õ…¤zø£Õ×,[Ö<¨F]‹5Jsæ/[sÿ¯K×ĜЯ»Ôê>mZšž®¯ec¾k‡ÀQ‡„Mvû‰O\ˆÍS¦¦$|_;9©~ªX«t ÖY=ƒ:ÒlÇî\Lب°Ìôwà QÐ…îVÚºµß±–ž÷ÜGÁ B”PÒÖ‚SIwPÇÂõOMb#ôŒRU÷ö*g8®õ¾^sÿ[-\±öNÄO!)' ÔS’êéW_³Ž˜Viyx”õè3Yvö}?/Zyþ"zvë 8¤“ìÃZ͇Ž*K@«†Íš¿ÜÉÉË»a.«l¸ºâ/.)¥6™}ÆMÎoíœsBqÊQ½L=ãWÞ^a%ÓÓkŠ´Ššïö«J>*ºöB®¤‹ðîÛ´÷öÒ³cWœ9 dZ»îÞ¥œ¿lu×û&LhúðèÑ»k-#L˜H€H N¨Å÷:U$f¦>˜dݽ}š/yLT”<ùõý¹fóoï~5O¥>û¾¸gÒëŽ^”_Wœ^ð­×]èEíu®+Ùb>öÐk]N=º—©”¼Hwt똡ɾ˜šYá=Gï…5M½yp¸¤ŠØèýµ`Nh«pÚ@†6%¿&ñõÚ£ü[dbViÅšMESæóªîíUfD5ø`öƒÅ»ì ÷Ý·ÿ›{Õ`~ @Ý @!©nü6'&nJOIz ¶©÷hñLÑ róò%¬Z‡ÍUuï˜Ù‹ÿO¾þ…ÈÊÎ+Ú˜µ&2î=i–B°ÓÝÆàôPB(sç5‰Fxñ‰¾)ؽx ¬=F¤ ¿Ò<ûøBãaL²Â¨µYò5wˆ=ò¿À€@ÿ^]ÅeƒŽÙõ_{}~bºÐ›+ﯫêÞ^û›NeÃióù¿ý±&äå{f¹+”þH€H€8Úå ¼´,^$¿ï(áe8®ê0àðƒ±Yë1X¸>ƒ%Í"Wå™w¾yâŽkÏ©Èk•ž‡sO½1ç=“^cn¹8`®J«‡žwbÝ1“§ L Ü:-%ñ¹º^HI®P)œÜ? „Ô¥üj‹ê¨˜ùÍ×n DGyÆBïïÏy¸ööªj^>þa¡x@{ ó´§S w]®j$ôO$@$Ð ì«8Þ ‹ÉBÕEqÖ#m¤“?ÅuÜÁX‡äèýWô~-Ú•·wÌG³~ –¯ d)ñÉ7EÜUƒÄ÷ó— m lÛά€yåÛ¯=Kxalúç³ÅšMÛE{Xì;éÈžØæð@8=ò®Í kSËz_Ý1†éañË’¿&Ÿ„Yf½©(Ö¤ì³ïÌŧ%|ϼkd'ŠàF¼°ÆsÎQB«gi§­‡}òÂÀûní[‹!—*ž{ï»À,Ò¤W>EЩw\-žC8½Pð¹ÇÂÍ^´*„¿‰‡†_)ô^CISÞñWŸ!ÞþbŽØ¼=S¤¼¦Ì=rÔ¡ÿ"" ×ÅÊ­CÙ*5+#}])8¡o:' é ë̓õ>H%]ÓØš3ÉmÂ*¡Þp¹4WZÚ¥ù«î½Å+׊¿ùÕEy§S@ª.M†' †G€BRÃûMëE‰â’ÓŽ‡€ôŽ!v—y4ölé‹uݵ3±™‰òoç-ú–Þ⦋N‚)æùB G·>[ôïÙU,À¦™Ñ‘ÞÀ¦›í[5Úÿßþ A%:pO¯ãðÃòч açxñ„ª7>ÿE ìÛ]`O(ñÊ'?Šc1CvÕYÇb=ÇfѺyÓÀ0_aÝU?Äbÿ¢c›B[Ók6b߄Ֆ۴ð²mW–Ц–ƒ.3+GQ­\»Y¼øÁ¬€ f= ·yG¦hØ·fùêâŠ3 ½B¹aö° u¹PÅÛ¾+»è–Ng Ô {tm+®D^wíÎ Ä­ÙkáõUlDú-LBÍ?¬'Y9\(il«Ù)7 ŽT—c™›GïÉEW;þ€Iò©o~åÂRß¼fÍ¢n«\0U  ºL€BR]þuhÞ†&§þS(wj›VMáWŸij¡¶]Y{ÇhK{1Q¢ FÖƒ{Çè¼æåÛÂ7âÂÀLP ïÑBœ5ððÀbw-X}€5KÿÚ(Ž9¼p™U¶av©—ÐëN‚®äž4ú~É}gr°Áhy›óB*Büó²Ó3A¿zÆJ»ƒ;¶©’ºÝ!Ø×&îÊ3a+Ú#'੎ü·qÛÎ=9‘+ëH–ÊΆ”m0 šÅFÕΨ@Ù9kO¾ž»T¼ñÙlìP¥–Åš1ç=rïÝ{G ’H€H 2($U†ýÔ¸dßÊuÇ,‰ºåÒSdd„·Æâ®NDUÙ;F§Ó³k»½®µJÝÇßÿ&6AM­]«Bk¶ƒõàpñW!þ÷ñâ¡iïŠãŽèP+¯Ü¥í;ˆhÏX»Ut¹=3Ktƒ@¤U¤ªê‚™††ëÛ³sÑeeöÈ)ò\Ë'zf ïóD—Öój9+'¯Œß”rä|ÌP‡ qé l 8¼þÙl¡ÕL )gDzšÝ0ɺ3óÀ¤ÎTH€H€ê Iõí«ÇùÅ~0Õ«?±OqÓŧ”¶we­•®*{ÇèLFx÷î £ºëõH‡ÒQÜ{ó…B/p¿ïñ׋Êrp§6"ៗŠ9Kþÿñ£h9+ûÄ[ô¼äIè¾3ÖiW€Í8ƒnÝæE*tM¢£Äï7•{ÔûÔèM=ƒníæàìKðŽ^ÏÞOBeöÈÙ²öÎ4ÿÙKþBÁÔ'ññ{ X{Y*7eÕµõæÚ-¿¿öéϽ±†Ìlß:|FJÊÍH#z¨gEßúrŽ“ŸïW†NMIL¥5»Fô°¨$@$°h|? 1HÕ ON;KÂ'Äbõ›/9e¿f>ªžê ¡gŒ2³sD‹¦1fliXX”°VŸÓ²ròDÿ^ÝD+l̉ :µ+¹'MQ “(¬…êÒ¾eÀ³^#4óç%"7¿pýövL_ïÄ>/Ÿÿ¼닲ÄO W ¿mc6«™žY`]’VÙÓk›zvkXµaËN± 3/‹ÿ\’Ò¾§5½Gξ)ÔÌmX#swŽWò…š‰1¼±hAÎtŹy;þóß%® o‚4v u?ü¶\<ðôÛögü •ö,Sxû§§&ù( 5Ò—‚Å& *Ø;l\…@ôJU!pÏĉ±Y™þéÚ¶7^To¶±©tõì˹'öŸþ¸H̘µ@œ> 7 ´ „w\GÌ€9mî[ÏuïÜN :æÐÀ3½'Í«Ÿüؓ榋OL¯K+'/SR“VÅ[i§ÃàÄkO¼úy?=»zñiGa†°I9¡ø¨2ô À/ØÛìûßVØ»²rùa¡½nóf¾ð˜r7mœïKM$@$@u……¤ºòK4¢|X–2ÖÛ¾ûQää¦MbÔuçž`Rý®½(j~-¾ûõñÅìEîŽÌÈFÆR|Œ˜š’8½q‘`iI€H€H€ê" IuñWi$yš<î8C8Ïb6¡ßá‡tr/?ãã ŽmIé_1׬Ú(æ/[-~^´ju~Ó0o¡i÷púƒ 3¤”8¥#   Ú'@!©öƒFËúʳÎþávSªd˜1k}TïnjÐq‡ËÃîØ¨¹4”ÂgfåŠk6‹ù¬ —¯vsóý˜4’9P·|Wzä¤t+iNC)+ËA$@$@$ÐpPHj8¿e½.ÉkJÇ¿ã_£0³Ôªm˦î)G÷6N>²—hUçËöû_ëÅó—‹Cé(N9ªwÏoU2˜“—¯Íq+/ µiÑD´nÞD´iÑL4o-G ¿c ¿¶+²sóÄú-;ÄÚÍ;źMÛݬÜüÀ^l¦aìp”zÛÆ»<gZÖ¼ªä~I€H€H€Hà@ t i3­ XÖôˆ þ?®2Ly»í¸'›¦á}èAÆÀ¾ÝÅ¡u‘uÇj=ÔÅ`ªz¹øjîRµy[&ê’t"¼†L~•ѼiL…e­/`TA¼ûÕ\ašæR¡ÔAP›‹./ï¦!saº{‰í:ó ¡ºJÎíìMüɲ$¬ÖÑ‘ @Ý'@!©îÿF6‡qVÚaÂvâ„”C”«Zh©W×öò°C:É>Ý;‹®Zp6Y9ùbÙª â÷?׋_ÿéæûm3,slWM’µ1¢‹3¸IDATÊû‹‹ïÛÝ«7ÍmnéßÄ”é3¿ß}º^©Ë42-­­¿À=D¹¢½²ÀQ2üy×Þù¤/q5×5„_Ÿe   ÆK€BRãýíëMÉõìÒF{ÙI®”çR^ ¯>ØGG6‰Ž²{ÔÁìÔ¦¹ìÔ®¥èÔ¶…hߪ96!­¹×zWVŽø{ý6Øáh£Z»i¸)‰Ù’,X¯~Cxä“V⼠̸¤ {þ<0èØÃŵçù.ø¤þ¿™»T¼þÙÏ Ìg;1æycÆìª¥`ŽI€H€H€H êêq®ê…eˆ†Aà®ÔÔö99ê\Cß<FzB01uéôlS‡VÍT»ÖÍ̦1Ñ"6*BÄ`MS“èH‹¿\kU¾ÀŸã8E绳sÅÖYøË[we«­;v+Ûvkj xåÒøÑqÜÏ”a~qN¿óì"+ñ_|RJ¨±Gv°úÇyeó&õKõN¯¯z竹îê Û´‘…”ÙöV|N‰bò’H€H€H€, I ö§m<Ó3M›Äòà ïô•Êí«”ì =¡ ÖkhZ¸JUz!¤‚Ýi¬ö+g)®Dø?M¬¯ioöüÙ²T–j\Rê(qiÓŒ8÷ľƹ'ôÞJg£²ÉÔ˜¿œÜ|1{É_âÇß–»« yLc•+Ô]é%½Wc‰0"   ¨'($Õ“ŠÙÜ÷M˜Ð43×m-ÑÚVvKBJ9*ßðŠåxòM%±µiAAŒ§É¶Ç¬Q;÷?¥â!ã¬ñݤíLr•{ed¤×9öðCÌãûu½»u(î±–®6oÏK0k´ô¯õjኵ ³dfâæaìéȶM_ž|çùµ”5&K$@$@$@µJ€BR­âgâ@\rÚñÂuoÃú¤ë1«ËwöáwôôèÚ^ôìÚNtlÓ"ì´Á‰5›¶áo»X»q»X¶f“³+3{Š¢ù·ë¸ï{¼òù§¬ÄùaÏ     :N€BRÿ˜½†Càž‰c3wæ_­¤ºÐ”Æ0¥ÝV—.*ÂëthÝL¶mÕÜhײ©hƒ¿æM¢ë§b¢"Et$ÖUa-fy ý§C¨Àÿ ¬™Ùy"z?£œ<¿ÐÆ&vìÂÚª]Ùb{f–»eÛn73'·Hצ¼×»®3ÍÄß'¾ä•:V:    B’ø&@-¸ÝJëíØÎé®0Ž„àÓ Vä‡j^gBcÕÉf­li˜ë•ã®RüõYË”pžèyÖ=[«7Ã’ @C'@!©¡ÿÂ,_½" Pl+.ð»m`M¯ Q´t ÑVÇ›£ †„)?] «˜NR¨Ày®!wJî”ÂÙézŒ¿Ú2èÈÞë˲¾W¯€0³$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@õ˜€¬Çy¯0ë¿]Ѿíg¸Rô—®ê$¤ŒBf )V aü4`@õµ]aDõÜC|RÊ­QÑÆ‡'&nªçE©ñì[ÖWžö¬!nïôŒ1cvÕx ,ÂqÏxv]ç|8z£h-•Î7K! ¯÷ë±Còþn`EÞ§8·'úr„8ejjÒÿöyÈâv+­wëö›öPÒ[ÄQ>>Ó§7‘ù;NWJÂí&”ˆA•-¤Z…vjNT ùíÜKâsÊ¥þ?eUþo>çKéÙ45eì¯åûäS š$à©ÉÈêJ\ó.n–+Ô½~u6úþÓÿ/\1÷—߷̹¨ýsÞñè‘ïlÚ¼?ùKJéŸÐa¥’è;‰ 8™×ÉÓåEË’·?qV6L\¢ï¤}Ï´Ô¤ãË ƒøéܵ ~($••;'ÒÙ)2džûQH*ÁG_N|©}¬¿e„rÕ×±Ö÷tM :}®ô‚|‘–.–†1iÌmöt)e¨· ÷ x¯oVR ÖQ§rqXcHõÁT_ò—®¦‡¸$ßkø~M÷%N(+*¿4ކløžSH*’㨓¤RwãQ•„¤aVj_ǯ|†×HH·–”uƒ¹Õÿ¥gúÚÊ-ò¶_å*½O*¬T"o‡ÈîûbútÃðLXpã­ú¾_nh’ï¤ÑQÆ ÆvV ÷¹ kÌêýа’FZO4Ë·w-óˆˆ3Ÿòþ½¬`l£Ê"Sxßb¤TîO¸ª”4Ìò tlu;ü÷Á÷l0ä[Œ“>µ¬3ü pù$ù”ªFÀ¨š÷ºí{á¥íÚÿrqû÷¥fB:ý6³¼£c×£ýê_.i_žß²žÒÓ é\hHã=iˆJŠX|ð\ï_÷Kœ•ŽQÁð9Sz ͗׉‚¦54ÑwÒI©T…@Z†yqAîæe®«F¿íàŠÂÂÏñ®ë¾–6Íø)-=â°Šü—ú܇¡#×uêu<Ÿƒ^]_WÉC“RÆ—ê¿oB¬{ÂݬŒò€GoùŽÅÈsÚO¸ Æ%¥]îÚîçøÝÏU~·U5¢ªÓA}õ|Ô/¥?îg>„–ÿC;©l§”ŠÅ€ßGùõy)}fž  Qu®ƒ mÀ Ãt„Þ‚6ê eû—‡û»ÛZŒÌBzÓì&j}Õs]wBÔ§6J H®­>Ço½Ø#ÍQø~.PŽzv½=˪;D™¨ÌLÒœK:“çØ¡áéPUôèØ5®˜úËEíO‘»üóØŒ¹þªÆaš-^yʺ]7SG$M8ܯ –H{ó¸N¯j\•õÿ´o´YÜïÑÅʦsü]iJõíH‡IT@jºa)å>P… {½*1PHÿYÿHˆs>Üû ²gr]zJâK{|KJMÆ`ă·§¥Mœ’°­²±TÕ_zjÒ«U SçüûÅù®-ë\¾ÊÈÐÐäÔå<.¤y)fè>ijˆ®Ïôç;l^“?ïñÑhsªä L¡­V÷‰Üí§ |ñÅ‹gß|sÕ뀔?dø_ &ŒŽÿœ?„¿³ƒ÷júhY“ âþšŽ·â«7mÔT+iö0kb¯©Ö½Aí˜ïâ“}-•+o·¤Z`Ç$I ÞhBÒ¼‹;éºþ/ñ+4¯Þ/‘½õk¢•e ––¥?îûå´Z =-ÞKGŸ”v%Fa„úB¹`öêX¯ˆ8Fûa¥eû•VÝ9*FÙíû4Ö{ÏcÖ¨P;š5£w f4MÇ£^C³Îžõ“)L m„îÐ _Ò…O…vêyɇë^x¾Ì0Œ±ÊuóC;4m÷1ø9#Š›‘¯§ÒS ªGT30=•™C!<Ž„ÎÓ® _ò1Á4‚ÇBâc¬÷:Y y%îcù—z×ðxž˜jý;èèaª0žn¸·PJóßHïG­Š‘gïš@?tê^¨f®aˆW ~ÒVNó%êrˆéÓ§›3ûc6òñ òqw0^ðùRJã©t_›¸ü™0jvfZ Ÿõ«á÷¦[Is´ÿQ>µ-g¾lꨲì)èp\„‘¶‰xôx0>}ÄÈ­ŒOJŠSÏÙGöŽÌw0¤àa©GzOÆ1þzæØYŸèκ’ò;tRF„æe½ûÃyøÒõr=­! ¨f¸ >èô;ê#Äó´hâíbs<¤TDU¤Ò1"Ñ×Õ¶ÝïùL#J…Ä1Hkô°ûÓ†ãAç«£p”…ýi·Âß%{Ÿ…ž©Ž^@þó q¡ôÈ«QÚã¿“ôè<*‘aæOÄ¡(ÿçâ>iMèòÄ#wÇzš\ŒNiŽ’ÆˆXoìq1-cÆã[Ýu÷ª`_.X¦;wíñ߬…D}_—e?ãÜ_ôu¾ùü~Ò· ¯q¥P9øN/"×ÏMÇÕ[eùáOl‚Žü fóiý,èŠ$ÄÓ¢™÷_ÁNiðyc9¦¦›×WW@ a…ßË}m³‘‡†ÜÛŸÓsu WDýY¸ôw´¼÷à)kì Ô™Ýy¶}chåÞŠwgQ†¥Èã¢uðùÌËõÈëÍÊ0{=‘}`ð弇ÅÔþÊ{Çu$èÒ»|zŒK¼^y.êw{Žûš~î1[ŽCY°H¾¥ëS¤§ùÙÒðü‚8Nnë®ýX–2бý§.§±në™ú^¡s¯BÙëFÐɽßždþ9•‡ 3âJ%© ÷J|'îJMmé äqFi’ŽJ wü|+q‰zÚ°œ¥”‘çÓÁ¯ºR ¾{ýóòª¿IPI—îEï^‰ïcEmÚ²‘©Ëã“ ê Ž|â‰HTÔx!/öÄÛÓ#Ýý¬2m”öWÞ;®Ÿë÷¿¦Ú¨òÚĺÖFé’ÇÝŸz¤ aw”& i?%tå9hÏõoEG$Põ~&©ÀÈ›„‘S=CQsN¹‰ó/íôÎQï¯×³A•rJìè ]`×qE×|Û: +ÑA)ZØNJi§?”•ÀB‡™£S²3&z´V»ßïHJY–¯ÄŸÃ’Ç]Ñæ•#R†[©ýŸ¶|8êft€^ÓºaÉ©[Áÿ\¿Ò³>KCfžÞšºÃp]$Ü žÞ‘íšw˜|çùÁøK?ÊYu/šq€êÌ)Üô _ü6IèhMÒ³=ú8XH/^wT!0院ñV¦t²ôì™öƒ†ôSײm5ɺ{»+Œ«…t_@þoƒûÃéðò…ßç`¤ré”Ô¤Uz&ÀvÓ<^ãÈ"NBÜ…5,'ø1Ú&DQça4üß8Ü='b¶B”mª¸ä´ÇAé˜g?fW>ÃpýŒ^C‚”A˜­íœ­Oôù\¥¾¿Pù] ?ð›¬O8ÏÄ·f©²í³pþç:gœtØ…:þžë=ð鿬I-²ýÙÇš^y®1ázkˆiÓRƒ3­Ob†53×I˜]{´h¡Äo ÃT#[çf»3ÁöMÌ4<¼ßØŽÓ_J¿e>£&Ë÷é¢#^ž:dÉÂïIÅÑ»ª‹ž1AÕƒgzÍîÉÊ0Î)°Ø÷ïÍCø”ÙFá»>ïÝ·®,¸ ñ$ë¸ò7e^Šw˯º¶þ@df„£`•i£*zÇ‹êrÞ½`z…ÇòÛ¨òÚD|¯t›X«mThYЦ áðEhˆ\65%áçÐgekž^ºÎðš'–å‡÷I€J'P¯Gíæ\Ö^wžôǹF:$†ßq®J¤¶ 51¿x÷8tæ?M='L¶îÌ,ŠCŠíÚºLÑuáI=³zïI_ò_¿BKÿŒÄÄ èΰ‚‘ÃÇÓë .5¼âÙÐ0EçR*…ñuÑ5Nš´Œ\zó~Ùn‰†í½àÜ“q_«1…8ùVÅr*E±ø1 ôø5Ói!ÝÎø@_L Ìw‘>Š¥J¤·7é©Vâ"ŒpnÈrsÎУެ n~Џg (TëP°\(U`dÌQN_äcóS$,ÜK`Hú3$Ô?ôži¯„^ϱ0ú¤3 É5Ù¸?/' ¢š™• ÂÕGtòÇ=ã¹*ôVyçxEŽu¦·¡÷Ô>Ï2Lq;,ÎÝ]-tˆLHxG¶@Ðÿ¢xÜOKÓ†]ZbF÷ëâÏdÑ{Véw¼ôú_<Ú=W˜‘…Š]1÷fFu u§9r]Û~uö¬=‘® ¡P~Šÿ °¸L×±;wêÓ]÷â¬GÚàkg†D ÆgºnµdåA{ïÿ öÞ.f·ÞEœÙèÔ&…ÜoT§ßBZa)´’©̘Y…¸ï€ÁtX©¼¿mé5Žš–’ðMhøR¾å¶Q:,ÞÇgQGoB;‚ªp7£]{)¨Æ¹ç^á¡mTåßñ2ß½âI–ÓFíñXÉ6qo´µÑFa€¢'ÚÅ·QÇA@ú~onÊ>ÓMàùŽæ­ ÝbdÙø„öŸ€gÿƒÖ¶¸ |ðÃ\ÃRgÏ¿¤CŸ£>ØX²ÃRj:^OËþ{ 7”ú77•4¿‰¼ÛÕÖ£¾ÅÖ “øm¤)žUŽH‡šÙè 9ß_ ËõÂÌâ‚WÒ *C{݉]ºÌܾ K¢öÜ“B7ª a¦Y¯» 8 jsèCíu†ëö^•{V,½PŸùÂŒ"Bæ|k–„<{Ò«Ìõ!×ûžJñTÎÚ¨ÆmDGK¶7Ç~¿ÁM‹‚•µЩ¹ÞöéQñu@töld¾ ä,ÒŤœ*öŽ›Ê\»ob¸#Åaøÿ;¨ >Šc¡ VªÇ†{s³mšÚö¶[ÃVBåþ qf+LC ¬?Û»ÖNûŸúPñ¾vÉw´2ïž]Á:…·°Ú͈ò'tën‚ðý\ÉwG§§ LÔ'G¦›­¯ƒõ%/XªðŽïSÿƒñ•<šŽ§XzÒÐÏÙãKÊHÌ$å¢þ> ·çø˜kD–7óý :X/ihí»³cןÙïÐYXëçÙèŒÃ³:Õìg æUõF=àYìÛ¤L•A)Ô·½uªäoÌúËýQçÿ¡*..9õúŒ”ÄR'‚þê{ ݈o«°”O©Žk¶®Œ¸_®Tü†ƒÁ€çÊó[òûˆ×®Â6ªIë˜7³·åLžì;ê•‹ssÝó”iÞSz:•i£*÷Ž—õî•’n™mTÀo%ÛÄ}â=Ðm”’ZÕþy´ñ÷aßkS“’Êm£ã“ÇéØþOP×Ga på¾»û’7H q(Ö¬O(Ô5טss¿¹ª¨ó†ÌۮРÐaˆ:%z>³Ñq¹,4~½ž)ß_ÐÇ5 ×t4Nþh½;Kmt8 *5ÿ‡Þ3¡þKœ¯DçäôÐ{3,;XÏŒíéHi\¿ã|ÔþŠÍ`…†©©ó§¬û6B=`'ÊÙ.ý¡¤ ÚÎ>Ñã94@´õ¦½ÎòDºÂÍB·ìm% ‚âWëï£7úS¯ƒTÓ´“·ó·:Ö®ÏÆ¸u—8+í° +ai0Œ¼á§è:x¿Ô£ÇãU;°ŸÔ¯POÐ{O=Rª¿|Óuw\ŒâÅ„±ˆ'M˜ÝeôÐÜÒÕj&\Ù÷ëÖž…ºÑ›Pm}¸@¨ÓÁ˜›÷ý‘h€Z„3Ïèd güXeý_áwG MJÛÄ«Eí l¿Õ±å¢sÛ÷uÚzö ‚Æ‹®£×Fˆ1Å…ªÐü¡£ó €¿€ÎplfûügӡʵSàí@¡ó ï$[ùoAœã1¥”.Û5_Ÿ¿%ë(–òžNIø.详Žž€ä¦§Õ¶ÈRnþqŽZ¢×ÒQR§{ F¹gî1+¢Íè™Ù*û¸¹!ë¥,Ð@|ˆ‘Y˜ƒ–_7ë=«_÷Eÿ ,ŽKŸiƬʷwß =PM<¥²ey<1qÓÐä´ë Žô)Ö·ü•ÍKmøƒP:(œé¢¾Bµàt¤¡×w•}ÒLøÂÏNL×>‰Ý̧R“Ö”–=ƒ5Ïâ;p+KÏÛÌ’ë¶\Nñ¡þ+õއ¨ÆygOÒ—ëý©sü¶ú¬Ï¥tð$}»ÁžØCJÿÑSS§ë¨ñøߊä¸ñã›UGuGÂÿwŒ/Aí׆Viüu—1Þ™ú^ÀIùâù7¬h- ¬-Y·õ\áºÃïeuÌ‚ACÓJz uÿTì}ôÖkÊq̶‡­÷çZnõÖ5a]‚:ušÃh4ðÓ†ÁU¢Ò©šÒxÖ6Ô›UáÑëxJu•i£*óŽ—ù~Þ¬T›XGÚ(]ÄM½Cwdúçˆ5›Ñ¶Š*Ȱ¦‰}ÒÛ†zº¸ª»s´ñ N;¯ˆÝ^l@ámþO$PhTÔO•X𠳓2¬iìm»úÜWfÛÙk 4†µª,Ú‰ð˜ÏaÄð”öýòöˆõ(=³r'þƯ·×nƒ€ôúyÐGÖf„ 6;.Myî]žËFXd»£ê_`ÞA?5y„í‰èŠ=‰®I~{û6åÏÝ!dªc˜E*(Èže:*t›Ð)M×ékƒ 8ÌE¸gö?ô}¯Ðo£c+y…ë‘ô=½ˆ<ÒÛìrtôü(ó÷ùþÌèØÞ5ÿ([5qOt%ZGêM¢ãøº¶ÌUâq¿TG„»€˜ [•}Êk°Ú†¡ƒ ±†ïÙòÊ +‡ ¨+¿A@ø ’PJõ™†¦2ïx¨ÿêœbä¥ÈûnX¨û ªs¹®*X†ú;</¬6¾:ò§Èò¯Æ`H‘ˆ=ÔéToÔEª7<g¢.u‚hõSP Òñ@-ëIÔƒ %œIbõ–](¿ÀfÀ˜Kr0ÊU×v÷¢nî²íåÍ‚W6ºzãoõö=ð¾TŽÃ—iÕô—3º„+þʶQO§Œ‹z²ßë#š7‹x½¬üT¦ªÌ;^Vüûs¿2mb]j£´Q!X¬¼eÇ †>sÊ/†âûР߀'¿ýWè_¾“ùÏbžyA$P.|×ê§›sq;Xiÿ wî=n„Y=“*›?m]J6SùzÄ·²aÊò§ÐÞþàÄöS¸wSÈj†}¼ë‘æˆ<ÛF Ü\r½Ô>žkà†V%TN~Y‚ž~^ݼ@Á+6ìn^Ì Z ä½1D‘–!—¡NõgYñ>NKˆsã™†Ž»¦ßËz>j—سGx/3û½ãeÜ…eÜѱY¬¹í‘{ï-¶ŽIG§ ½45 »º–õ`AK–à,ï~dµQéûò´A®ë|îÂC—ú¸E7 +R WzµÒF¾çe¿ã5]ÖŠÚD¶Q5Mœñ‘@Ý&P…¤K:܃ÌR„×{Ü^i}m‡7ÆNµOBÒrI=ÚÌà$Æa_":hàú¼4íLì÷E¸‹)=žãßpÛìp§ÃøI€H ±¨·êvP?ÛöKŠÝÂN™ ÔX¸¦UÃê0*ö4ÂZFN•%`ª󮻬S•ýIèH€ªB Þ I°Ú²¬*Ý/¿J†?ýÊ‘@Í0”ü£æc-#Œ°NG«†JÀÛbŠƒ {™åGî¬Ú{É3  š"Po…¤Ö­# ƒ-ój D©ñbV©÷y“ Ì$…ý}W { ð§a‘ê!ŃgÁàoaͺ³çïkŒœH€)z+$òÂßy0•ýq87Ã5ÞgüŒ›êODô{¨SNØò$Åï ñ•Û·*l™`Ä$pàB¾ÎÔ i„5þpæq“ @]'Po…$ º'ÃV¸ øhƒÞ¿‡Žû†doD­ _§ ›!6 ,$ ì! ¢"´‰ùüpAû—%Ø—Â7ã$ Ð{ Öc7àƒM_BR õ C&Ôc4Ì: ìåyÆj\}qþÙ´s÷FµOÎþý Õ,Ëšg¥Ç”¼¯¯ï›0¡ii÷yH€ÂI ì¾pf>÷¼‹Ú_êHõ6v™6ƒ÷ö÷ˆÆg^ttÛÓû¼±8«*qÄ%ú ¬—áK:½*ájËï0Ë7pª•Ľ5jë¨Ãé*eã2| †.¯™lÊ{ãÝÿT&®¡I)‹ iMOIø1è˜Ï×ÙÉS‹¢Ú5ï¿y×/†Ç¸~ª•¸(ø<ôXV=ŒKJY „œ=Í—t[пÞ8ReÙ¿â^÷à½Ðã°äÔÁŽ«žñzåiOY‰óƒÏâïO= {´= ÕÄ-ØŠ Ñk3|É+õó‘O<™·)ó1œž=-5©W0 ›@Ÿÿ>w„p fa²–Õ%6j³Œ'-úG|à+/¾I·…ÿÓô”ă0ø‡*]èâ’}w ¥Ž3E„ÏQþw3R“>+yDÝ™g2R’ŠÔÛ‡'M8ÔVKMÓ8êC‰Ÿà KJêJÙ~GïåÕ Ôûñðw)þrð·Xvmw[F|¼iÇ %ïPRíÂÑ+ 9iZJâkÁ8y$ p¨÷3IÎ1mz X¯G#PP-XRþ"¤ç¼ª H:M´>× Ã$‡[ãJípU+_5x˜5±k‹ÔŽ–Ñ5RZnLjƒ®ƒÐÿNu‹„Y©±•tZ0CþšPε¡éºùâÄóÞä;ï¬p&ªÜz¨Ä©¢N »¬st"S]å^‡éÂP?Ó§O7]W=+EäE9Ù4ŒDKSµŸ‘ÖÍ Ä}¡iMhž“ÀâÿûçÃ4ÏÁ{¼¥:4ðn­3…ç¬ÊH:§|£Gë´3.)õÄbé*q-Ô0*6â“ljfm+ªÿ+(ËŸ®ëN¶¬ç£J>+y]^݈Oô†¸Níì9¥ÿ4_ò±ˆÙ/×n²'Ž¿<Þ–'âþ)f´¸R*wB\zº·dü¼& ph0êa>Ø8ý—‹;üÁ²ÿa´®gU`¡á.ÌhíÕõµ¹U «ýMô„tÿ”ÒøÔ±í›pËÒ÷µÓ£Ñ®R·"‰ƒJ$Z–õÂ#Gž}D¯ßf.X6º“Ð(t”åGK0"}Y\’ïsC¨q®ÂBw©æàÞhH®Å†>ä5þ¦ŸÕÿÐ<ØÑ¶²â‰ON½I¹j,âÐ* ³<žÈ›¶“—ÿ9âèŽ|kó´oaÄû!-Ü!ïÏA«âäqq„·LIMZ¥Ë@×ø òwf”®ŸñÐXWªûñ~FT…„ tÛâœ÷«NȈו*ø ê=£‚#ßÊ×R=XQ<åÕCÖ0Å=ÊO£“uŒ¥./>¯ð¼òdÊè%¨C_‡úûbከ˫Ó}÷-×÷õ(:¯çGY¶šdݹ=.yüPÃcnrüùCBÃñœÞ0tnßWŸ  üÿÅ;tZU‰ úLDGܼ뜪h7ôÀÃ:܈D_W[‰CEç6Ÿ‰5;ËÔSþ!Ê0ž‘®¸íöäñ}¦¤ŒY’6T¿×9ë0P ’Cîïs:Ùº3³¬ºY¢Ë1Sõªea뀆/aÀQÇ—¶ï³`dS“’Ö Mò9æ†L=·9xŸG 1“„s܇g7?¤Y_i÷¢“¶>x¿¬#ŸbaíI>Ú4l¤@ÜRÜ‚Ím_õDË· d\ÔßÖ:Öu×¢©÷êNž$-|,5¤|<ÃJœ÷Å‚eç lW¨%œjz""/­=žV7âSâ8Hw{ ws@-Hˆñ¦W^!¼Qý‘FÿÏ,;Cû-/t ç{¼æ%<§ôÀ&ž;m§à“¶`ô{D@õ(5éH- éxlÛž¬¤ñRgoï.Ú>C’†+4˜FìôŒÒØx7UŠˆ~X^Š@'¦<$xw¡Ã3!ÚÛ¼w•$D>¤Ú<âþq§ètâ¬ñÝ Ð÷ìè9efyéž•Qƒá"¢ŒŸ0ò›X½eTð^YGÝÔBšžõãJ·êÇêÐ{„X›'rQoЫK³0Ò[P,L1¿¼hÔ]÷Ï5‹oŠ„wë´¥ªŒîHŠ_Ѧ]½øæøó´!ˆ}žWpmÀt"WëõzÚ«-å`Þ©h @ÏØ¸B^*Í6ï£*¼êWþ›‹%…Á·OL”øn޳Ò+ö¬”‹²ë†ì¤”QT§0r» Ms×’QÒPb÷Të^ H%áðšH ,ͰÄ\K‘öš¼"ÿØ6>2 úôn¦Z½a£! ý†óßÑ8ý„¿ÿ iŒ4"ÍCŽýpóù°’÷ÓþfwÔ£Fãƒ~AGOǧ$$lC‡iEÜý㣄•}p½åáÑ£w[Æâ¤˜ï ÕnOZðcÌѱÀG_ª­Bd7ÑÏРE¦œ¬;j:œÊ±ÏÆí÷Ý(sˆ²ýìÞƒÐsYEñ…$ÚbQ÷-ˆò·K«EbÍæŽz1«æèo_k¨0|.\÷ñ¡÷§.®è Ñ诧¬ûŠF ;½¾ fKºò,-ˆ‰Ý6Tó0Ž¿Àˆ¸>–O`Ñ«—cä<C­.Uª¹ØõîúgçöçÚø³HC®ð?­q ᎄ¸'~·~´€¤ÿJ8ˆá÷÷žãþ¤Çx]Ùöù¾s½íŒµ£+Š­¼zöñÄÄMC±ÞȯÄäÍ/µB„(ynÈíl(aiK5U¦ªrT%£æuã#°è¦asPjýV§¤ëf[åøng÷íõuz)*¥Û1yÚµ%v¶­}wü|ÑòsqüX_ŒBü*§ÿ„ºë˜yÕÍG•Ú²í•EuÊþfh¼‹Õ'¬õ{ÞGSS“¿¬RäôL$@Õ Ð`…¤j0©RP¬ù¹Å4ä-°‘ú‹<1~áÿúž‰c‰ÏÆðÿÀþñÕBºGx¼gMNX¸ÛÉHøf›º óK)C}ÕÉè|Yh–5¸ xGŒ²©÷Ò}Éÿ Þ ËŠg„5®§m;÷ƶŠé7éî»s‡%ún€ÀÃ…¡êÅ¢Ýé¾Äz”3ôÏIà@˜jýþªõnÚx;wF]B/{(Û•[ï½·h”ZÇÐÙLœº^ùnÙ‘e_TvŒeÕ*Lä’‘ƺà=I Î𘯻¶ýµp­Fú¦^ÏZ^G¦¥µÍËq`ðìdŒ®Ÿ)/Ù3XLH Äã5FÛùZË‹·´g˜I[Ã=‚Ï úú%ׯã“}!zåÁßÃÁ{<’ À ÐàÔí´`z,¦uzw0Þ|Ò—ü—þÓ*rèØ}¹;³àª€?¥Fq—èÒvÊ“ÖèõÁ°›DZ_¨2Dz<-Ɯӷ÷»–5$/ø¬äÌGˆóŠ Šî˜éuJÚ_Yñ8®Ó‹í—j©P·\®wÒ"Ý¿ñÿ!úT»Îf‡…(‡;,y\ažqO/D<ä$P+0›ä¨4ÌÒ¾PU-'•ª‡!áu|¦ô GÇl<†ª4(ž2F;iEèýt”ÝÔË †´èé±c‹|‡$ÇS¨uÖ˜Õho6BÜImU¡ª]~®{ƒ®{Ú´}°mkÖ,VÅ™¥í–a%,…ðõjÓ=U-¬!Ì7‘§ëõÞ°òŸØæþ]}Žª1PûîÒÉ›_ÕxéŸH€ªK€BR5BáFX¡{¥d'ýaÑîæÂMøäRèk)ÖlùVëV`TìA¤ôÔszÙöŽùŸ/øc%ƒ_ Ö ×íã¦ú’¿Æú¥'¡ ÷FØç­÷§n–Yþ«µÇ²âéhôþj ÑPø*=TE£ÚJf¦¾C\³‘n‚аBý0yœ ÿË‘5Ùvöóûd„7Hà0¢Ôt$ÕÌë%“tm÷+¼» ÿ|+*ª‡%Ãëë§SÆÎ…€ôêú}UtÒ¼Y¸öt]`˜åA¨¯b ôNœ€‚ÊÖȶ‚ÆÀ÷Ňa”½õ õ*Ñ7 BË-†a¾êïÌÈb­Ð‡¹Nv© ±-c|¨¯½¼ÐpOMû+ü¼µÞ^¶ùX€ iDµmö¬¨ÃÆ8¤yfk×íÍcêÅÉç$@$@uœ>ê@Ý®HEN/zE´2.5µ#žýqGRÊ!Á"Ä'§!ê›àuiGmHAïqÜ'B_WÞ©¼, zïŠ`\Áô†×r„5¥IðšG Ò gsKÊ»$@U! Û¢8+=¦*aè—H€ÂI€k’ÂI#bˆ“JJÆgdx`¶ûtè_7³wuÌòx7ø]¯°èleÛWb®H»´líY/TdþôÁ¿6±ªÜx´e=ü•Ð{W”|@µ¡’DxM¥È3fWéOx—H ªö˜$‡Â Ô UW5©ù®¹ˆ³i#ìÜÈH}¡æà×&^MS¾>ÕJ\4<9íTG¹C¡ÃÝ ?[ñ÷S‹&ÞgµPS•ÂÕT‡é JâŸSŸ<ö¸“t£øì§Çï~Þ6ÿõôÑ“ÿy¾¿wôŸƒ^¿ 2ïàý³×¯ö¼ÇÛ;;»ý~/ÜÙy~ôÜ;xýêðȃ6vv^¼}ì=>ϲþ;;WWW~àJùäÂì¤I?L³¯¯¡±m¨àw³îc¸MÑz¥;ðm7êdOýðäSøõén'‹>‡¯ƒ¯aú*î†_žì¸oábgáY˜>%OvF¿ŽªÀ½£NA“Š–Ç•‚4 Ü/?<d)<òS衟\ÄÑYš\öý}øí—48=í…êÉΰH©4UÖgºÏ”¦Ì”Š<Ù5]tá2Kv»ÿ½dã[gée¸3ºþ,è|r·Œ»p»þyÔ>û°˜+1]w㯿ȖG¾myI ÒR*M¤ÐÂH±åiÃ|c¬†¯ˆ6”Éoß*ÏâÛëƒf»‡I/št¨VåÕóIùÑ€³òØÅ³¯½pRrô8ÅEh2ù4¹Zº\\žW“nM:öv¿Üìñ(~›üR ï ü=I.fX,{Aü9ì§ÑY4Y&£q·¹<Ëy¤wyïö¢³¸Ñ>m¶Ÿ—>ìhb\> ƒÞSk,ÿeTþp‹öy…õ®0BÅ6¥Û”xTÿÈÙBzÿ ðÓèk$IëõßYæý“\iP¯ô<ô{Á×ÃNÐkÜd~¼Î…÷“7þ½ÞÀÏQ/<úÚoT@êeóö<é\^„qVÐñ@²ÆHæÀaH‹@WWZMrJ¢cAb@(¬6BY#Œâ[µÆLR+•åVYÍp1ãs¡„T’0Y«è–×ü®*]mâ5‘¯ó ¶ Øp“8{Ÿ&ÓåÓdj«×‹'¥‹ãÛß(A*€6*Ö^ÞJm…h«N©`¬àµ )-=_‹pï÷Ãø0ˆÛ‡áEt’ôºµfrT‰þ ËUs±Ñcñ©Ü¬ŽV“E¥-¯VQôMp‡YyºÆ‹ê‡éPðC ò[¡¯1y§Q¯wÝÜÕ'·R¢ez‡£t¤¹ä„Ûñ* LëL«2Ê™ä-ÅÓÖâZP­ØäF²R³2áõÉœÝäêºçÆ]çm ì?/oX[ufYâð n$QdüCg]A4”¸¶fû@s&¸’“¶Szôpi¦ï‚nt9¨h¨  F€©ìK0“5Lhú­6zãÚ”û+†)J‰áPçæÚóòÍ͈[ßAš¶…ô]á¶`3Á¥TÀN\;'›¢8·Ð 5s »0f“‰ðÖ­çÝ4­ ïDjSœ^‡gAçk¥øiÐ仜“B Êå¡ëø•ƒ¨7_O>FÝËæÊf% ¸á¼¢ÜP¼åE´ý†j¿åqiýœUµ%ÔR%ªûq‹ë‡«î¯-@¢æš?jþ­š?%¹ê/Qõ¿Iõ¯#÷òUÁôªÿÊ{©¶vîP”*å^­95Cj a¦¥„Á*SlZmf¸ßTRªß1µv£—  iáSÍ<’ZS!Ðì@³ãA˜ËÚAXÀì”ú„qE­’ú!(¾eسÃ<È· `ÃÆ´€vB(6;>JM ˆ±†Á«gÆG*”¶’ Í9³@Ž­´:Ðê˜Ïê`d™oϸ|XŸ ½r¼f™{}ÞPˆg%6"ØîŠØÔüÄ&–HlHzHzHzHz+"=¾TÒkη1jt×JzÒCkî^Ys­9$6$6$¶õ 6ºTbÆWŠ *™cÓ‰[_•í>Ñä5Α×îŠ×Ìü¼ÆVÊknz›³¾×K.»ÍÙFDDDœÙ2YPšƒ8kJÜ˹ÔhÞ=È=M‚æÛz›@çÀ›¼zÌê゜÷×bÎ\ß™É`Y+€`+œ.Á´/¥%Rp- êTç@°À}çŸ' QF)ÑZ›m¤ƒÞzSÚºùçY¦ñŠ<ú–í§Wß‹X¶ŸÞÆ!$[=BšE²æ)˜jÁ¸©¹¼"@"@Þw€KH²z€ÔwÂ*Ÿ€ð˸°`)ê–ÊòADx 0.åêQ-l[/`OUt¹,¥ñµ T*)k¥¶Sqî €l„¶PÚ!+Ô®‡òék÷†ó} *?TT¾³¸;fµ¯¸Ö–KA`j•ÜòŒôØ++ § ™<îŽ ê»°WÃ7RX %ó¹bŒ·ª…%µV¯òZ'®¿%}ˆ¯Øò¨“×7DÎŒ[Å«ÔIM63nïT'51 ¾b[§Wl†ßSR¼a£”­Û6àÜË/Þ³4êž…ÇwݵE¯þ8I§½ö›LþA/è„.| ÈV÷3ñQÚ×TIÎ4ÑŒ °^#NKi~Œ‚ò•e–r"¥ ®7‰sÍ|`î)qj‹Ä‰Ä‰Ä‰Ä¹Äù*dAÜ =vÇLŘòµ£*Æ;Þ¨fjC1ý!zkR¶qè ï0™>¨a\óÙ·Ç@c– PÎAJfOK+ a§ 2…l‰l‰žœszrê<×£Yj´^õýu óaœÂÃ<ºD<È8d7d7d·ûÇn*g7»Tvk{?ì¶ìÆd¦~ÿ= ¯¥7A©”×o’N£7«µ˜ÜÒ ½!½!½ÍGor™ÄVß°”ú6áå¸ay6,9Ù¸”™HzHzHz÷‘ô0ôüFÇVºúÀJÊvħR m¸Ëäf¬j:™×CJgÃ@m `ã2œJ FxÛ©6j3]Ô×z—]Ô:‘‡–‰ŒœœØµxÈyÛ¤œ+pÈ0_9ŽShÈp—$ó&\Ý”S³Ö[ÙGh\6ë÷‹Þ©çYg¼dgL[ôŽQ½³¢­y‚NÈ脌›脼{û½WŒÒ ‹’xíâxNÒmÀÊ;v®3¦¥Mµù‘÷Öú ÈJ« hÀL~ÛdQÊEoÏ¢CyE‘E‘E×Es=H“ÏQ7LבCûþÝ5‘¶¤ˆÀ¼ðá‘Ó†iør_ίIbøez¤Qm|GaZQ&¤fZ4‰Ír<öëáû%¤Àc¿‘‘ï# .õTçz"#ÆÐ¼Û¿lGHlHlHlëBl}¯¿“[Òµ†bQÃz ²æMC;«ëõ,î‰â`xpµYÈϰ±.Ø·Èeçu©èÂ)¥h&)±ðN´äôà”àò¾¤B€\*@ª•¤\8FE»ÍEåÔ2°§ › €WŒQÁ•{£"»«•†T™f¢|Ì|W_ŽÎµè\‹{¹è\‹̬ï3Ì—å¹hòf•‚Ì|/âd‰‰‰‰s˜¡w}ÀLÕ³kËkú åLE‰Ï€—(˜úFv¸HKC}AÁðƒ®¹àö»æk¼S–²÷ÖÊ%Š·½¸%Üb? wóQgFÔû¤§6Ôô÷’‹þezo“nxÇ@*©OÊðHÍ–kÍ—\ R¦4Œ†ÌÑ8¹\"PFYmMÑ—ŠÒ½2TŒŠ¾ 94~?çÌBúçðÎ,„~ïÌ\ÖÕíì’•Å&hŠr«¶çÃØÝ^t·í–“¶­õ{‡ÈÝð4 NC‡àìþ}u Š’EÇÑÑ` _%é§ãb7Ey»gp³³ »[?25!¥:w‹$È>)¥ø´U 7Q´ô½5÷ }RN÷¡ÜIQµïä:(¦Ë}+<VÚ2XM‹TŠÒN/\£P%Í%‡éÿ¨™5BFa‰ð™Õ@ F‹b“Éeî`¬VÙžY]´.!Є€4Ö’J &1ÐäÆ[jñ ßMJ& cZHÀ!6fw©ÐÄ0·‰oL ±zf€¢ÂÁ ÈKPÜbÅ Œ3™3Îd¥”9±U³Ð[ÁØ$±=äJ)%1ÄØ$æs–Q"…„Ø9%ç’º»SŽÌ†Ì†Ì6'³-7CNÕŸô\4Ù6$õ›M64ÙØØÖƒØÄʉ M¶Í 6‚&šlÈlÈlkÂl˜õæÆGëÆÈ NÕ Ÿ8Ú8Ô¹qiá;÷WËC‚µ,ÄÔ˜åÊ™$ŠPWÛ*¿œo–ñÍ+9Ïáù8YC¯(Å“úst°ªÍT¾*lÐÅOÓ¤§)lCç´9Ê@zÏÃ’G[±ðÇRñ&éF¯y¯Ÿ—fzÔ(#TmS²M”GÍ”ýH„÷7r{å •duxùMeçá•÷hœÁŒO½âÛ$ g¤ý4 ãjtO{TШ4,™™wØô æéÓ`†²Nœ§ ÎÛÃgI–%o‚z0Y%/Áa{§½$Ȫb=ü]ЪO «¸ÃË$þLâ ýŽâÚÂkÞ¨“tÃnëžífv_þÚûÏGJÂÿ}ÖûýÕËdWüûÙ¿_¾8ß=ßO$=ùøk¯s¶w÷ßÓßÏO^~èíÂõ½òèˆýúÇïß’ÝW_ìþe~þÒ»Úûåúî¿eà6ÃãPåºöÀ˜I+ ÝÒ|ñÑÚ~4H¶1Ã=*ŠzÛ=ÏH)”Vú ls¥ÁÄvÛ/`±A}cÀ~&’hJâ·ºàâ»ð3[Ø"÷a%¼sjà*—ÂQÒ_EóU,Ï'b?ŠLÑAÞ…Aw?î}½^Þ%W• ×kT(ZS#‡ÑŽ­s¨>EY ÷‚øs0¨(-E PÑón8¯˜BÕÛídÑçY4½¼²S‚¸ÎY÷}ýqNÌÙkžþý |gQzݰ˜UéüÅ`œNGõ½Ë4…‘ÈG²©³·¨ì/¾ôƒ„íÿŠ!.étšì÷p+ÀlîD©?43ÕM±uPÙAy’¥—áNåú»ËÞ„¡`ŠN‚ÒåIÜ~ùz5¬þº‡©;ì'/C'Þu IÖU¨!K¿ Ï*è5ˆm½åÑbsZºmknHË(üž$uíÓ—µ[¹B0­xÐâñß"!?´¶?ù’—= «Î…e€j€Éä³ßéyúèÿÒ IòRneutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-flowns1.png0000664000175000017500000027233313553660046026161 0ustar zuulzuul00000000000000‰PNG  IHDR¿É:¨sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì]|TEþŸy»›J¯ ذœbA± özzÔ³ŸšQ!ÙÈJ@ðìH;Ëy§÷½³wTD¤I1Az ©ûÞ›ÿ÷÷6oyÙl’Ýd7Ù„ßä³yïMùÍÌw~3¿™ß4!Ø0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À„E@†µeKF ‘¸Ó÷h×r¿ÿáqo˜á½¡‘’ÁÑV 0Ü—w¼îW-ºzº-ôùn+e`šv=s ±þ¹<ïÆæ‘«èr¼7®Éð=ÖAÓKŽJvSñkZ«´eOŒYÒ¸©JìØ3|ÓÓ4cdž4ÑLM )6I—gÍ4ß[â•ò»|X¦—•"ùŸÓs\¯xÊtý!Ô“š¡CcãO$t}¾/ܛŷÝ~]B+íôÁ› d8íù½>eç×Í o}¶ˆ?ÇsucæÓYF—vg â¯)=wßQ/—-;‹ XpWFÊ®gºÔ¼pÉ«ìº|5Šðóù”VàÏ¥„©ü%­¾ÀÊE;ŠÌÁY¹KÝšvçԜ̹ûG1D–Ë»ž~:¹|óž”ëHC¨¶Ê@Ð ÓTÞœï5M;m\ÖÇ‘QŒÜW¹á¬”ÈÒß¡†F²éúÜ"¾í©ûõJ‚^Gv>]¼ÒìÍÙ$…üBsËIÓ|YK9—Í¥ìœå!7n¡›îjMi‘9 •ãÖ‘&<Í«Ö#;ì—h 롾 = ô¼/•PJ©ZJ)?’šœ¤Iq·”Ú!ä7HÓ±†2æddçæQo¯¡Ó˜¨ñ•nÞ3Í*²®pûFJñˆZ¦&äsÀr)„Ó)‚eqI¿”_ )× ¥> ¥Ÿá͈0˜jßl¾¥ØÞœøÉa®`ò–¦”ºÉð«ŸÑé’Ðy­¡ì:Ý5'î!RÉÖì%¶®4’DÇ|ñˆ¬Ü±¥ÌÔ,h,äoÞ!øN„ûÙ%<ƒ¦æ>ôkhÆ3λšÂfùõn¸£·›!ÙãO3MãV¼-šÛuÊ4ߘu¡ˆ ÷M8¬“½VˆìP§zO—EB¯g8B(ÏÛPžß†skvv›gäf wæå¾ÇO-ÚQ2J)s:#OgdOüfFÎè%N?‰ò^SÙ%J£IêÀ6tô:èºù:Ç}}¾4 »)+2/ÆH²·ð¸’ãGwtä‡Â$ø .Z×ÍÓí”p‚r2\ûè×Ï^¼’×UÛõið;¤rˆÊ_vyXj~ÍõSéT' áÑŽŸáË\QÙ7x)+÷EšóÞgï*s~‘¶C½9gB€×E?ŠôÖ"ÏÖbðs¾Çtƒ®—ÍËö¹^Ë3-à…óñý4¥µM+Ï©“z¨Ðé>Ì—×ÛÐÍYÀaÉÌÜìk*»EÆÎ0‘ðž]Ï Ê÷‚G‰÷‚fpö„“…Ò_Dz‹¤Ç}½êzˆwÒá¦(íÒ%¨- ;ðoÞÞìæÊšÖÐï`¢ëðÒ`#¿/~YÓ£BÈìîê:ý¥ˆÒê’V2•èôƦ-*äRèW›þ²ù(ŒÅ24Rÿ¡P.7Dù÷CλÀÎ~â%ßàå°[˜ŽÂ[…†é*Óoüf`ûµŸ {8¤ !42ñþü¶G%•àu>¾3(-µõ†¤ÖáKÄÛst7Þ“—ªjFŒß Ñ­p9¨mK×f@¥Fiˆ`$Œ8~%Œ H. æ,ÞËÈy¢x*ꨀÁ¯…å¡)®?’£9ykžêÙÖ¸wGøŠÅr7¾þLµÓ&z¯ï‰6Pwj …†NÉ•(«·ÆÕ(«ScU>D<åEæ7 ?Ê&T²—ð[b5@†ù„Îm6ýÆ|*¡YShHø ñ FÄ}†{=Ê0O‘î+XA=¬iÁ†zhvÞ ¾ÅÀâZàRˆpÄÃß‚§Ž1Ms:ð·ì‘Q]y'´ì‚‰Ä :UY¦!Þ¿õC ²~Ï]ÐÜŸïÿÚê8ýÛï>úhË|Þà£G!:!ÿ¯£œÞDº»‚¯žEü&uxlÿô´ÓAÛ‹†žEØ (Ó¶à…πϗN¿5½Cµ‘6-gÌàÅI ‘,tóyÄ5… u‹¦ 0¥<ù*@<-ÅJ›Ï±ÖzÅs¾ÿÀ·ù=}˜÷Ñ#Bã ×[uM¨£÷ìÕ/ uÇV’óÉóø˜²Øg¢á}¡P£"ä=gû}¨/Sý#ÜÁXt8Ê|#|ã{)U޶Vü ôwR[‹ò~ù6ïsÁ?×6%ÁGùµ*®ñx>Q‘µèKµ6R}²æ^Uj”S°@ØbãÃÔã–îŽwÌð )&gªp˜3§Lå5 5-cúô^Î%Ñ$8”^ò"¼jn8cªÏœ«B…¸ÚTÆë†’Óîg8+j%„°£ ×¥öH…ì©!ØYXþâ>j“?8¬·ü‡ùGéĨj˜ýöâq¼<ꭼĄàQT¶ÿ³{‰Ós¼ŸÉÎ…À'KéÜ¡Ùã“ùuhXûÛãIúÔï/•;äî×Ä_°LС‡¯Ä±ÄØS|™+í°³—¬> ö´¨fÞ3¾»+5ê¶Ÿ§²²6ã}ÍÏ*Ã|é;#/ûÛÝù,Ö÷zQaÎAC7W¤ÊkgfeGˆC}“; NÚáôo½+ó.¢…çÖ£GÓ¼¯e2²óîP¦9ô&AÍö¯HyÉû§yF€¦iuœô!ìÑEùGú“Ö­“oìÐ1Ûg†û&€Ãó(#íʹ™oÙ®Ôò™Ôɺ\è[„½¯®¼cÓ }ñ?ZéÆ8pÂ^ *½i¹Ùsl?#²'þ©\éï‚¶íœÏÝ…åcð}Õ¿”N­n{æî»­…VÔÑ)Ö‹N]ÏÍðóOg8ëÝ4sÀÛÝ5—vaV&Ke©I“;¶W¶yÏ•ˆëô#ïNÐ~ºJ\a,¢mfäxŸ™gÑñ]€çIR$Ý==¯òvðã'pjJÿ@<ƒëH\àÿP¹eyŒ©ÌËá> ¿ ŸŸœ¥$–‰–7ìpö³6Þ³ý9Ÿ4²ÖuýSصÀï2çjûr]_Õ©‹Ñ)xfŸ¡¡Ëm MËDÕ[ªOÖÐ`V~}¤tÚ‹ À:@OÏÈËëR5œ,t§»FØ‚ÜÁ@jfŽ7•k!˜­'–DßX)œ^’I ;|¾î|äí 4Ðèíª^Ú†­ƒ*…³?¤|Î)øÈÚPRþ—ÞM)N¤gF‰îæMaý™Â²×„ªðÈ—–"ÿ<¾C¾ŽÄhbUFáF›Vo*Eä¥35rÎxÐX\ |JÐàN"{¿ŽÖaà~6}¢7÷±ÃºN¯¾‰ÝÑ;¼¸–¦¥j×Ìp>"Hjâp ù,NNse8ùoÕ*é?D eØ*¹d׆FÇX|7Òq%Úìv'¿]%J´ƒÝf¨.¯|äW×ý>„o!5ñ”Sð‘Û”ÌÌíÉéÚõTNø|€6%“=zÞQóŽ.Ì?¾,į¡Ê<é|äuJÎèeˆkl˜`‚f´t÷‡¸Ý.¯-øÈûvA2’ÞáÇKÏPƒ8O€J÷Ö: >“BXsô/⸼™"ÆÓJòиÂ~×· CÔ岄-ª;¶—?ôyýQ_IKô¾uát¬=¶;½3‰ß“:´œgÛ׉7ìÀô¬…÷œ^éö‚úuc6^;Háºjë  ¶ü*Õžš‡ÚÇʆÔá´Þ ²mâ5˜ðl,8”Ü),ÃhkTàö‹**B0Ôœ©cÆì GÛ'f’=zÕg„¸_Jß.©=bo}bþéz1¤:6œ»K¸*õÚl?n—xŸÞ‘¦ƒm»êž˜ëCªa©Åœþ¨÷…TŸ )þÛÔï—N·i^o~W÷ý1zU$Tª“hT ÿ¾‘¶„.8Ð´Š i˜Á IªV¤±yI© "N¨+œñH¥H,LÍUoá§ùõ3‘ÎdÄùFÅhÑUõïJ~ñLfæÖP–QÒ ?8Ô=ßà¡v4ç ä(¼ 4—ôéÏ¿®ƒvá)§2T¢AÎF˜Ø5íê…q•:ײw¹§‡s·ò/ÅhÓü~ÿYä§®¼Ž>ÕŸì5·;ØÉrúKéÜj §½ë†ÿlJxð‹©¾1kCÝ»¹Fÿ·rØJ«bCÝ©c:-7Ú‚ú %12Sé¦nXu>ªõjÂÑ7ÓÜŸ£.¨NÑy»Œ)4tŽP—„øªÓÏPZËüm®ø¿aë)”v”Åg'¢.¼¤i¿ÔÈ{Oˆ×¼Ï÷x;`1Si óÑ „M OK0c¦s*ÍQ;ì›ì«»¡RŽ:´@“éxÔþßê!mÜÒ|&‹Ô UBHYծ“T¦Õö´ÃY‹n¯ì‰¶Qc8æ%†Ûnûžõ,B¨ªIJ1¯j Ò“¶ QžÅÊ¿ jÇ‹Àcæ&i”—iÚáFë]‰—Â5š>ß jT *S/ ËΈùСˆô*Zr9š‹ïò=}APUi©QÔ¨ŠÞ(©nDi©¸ :Þâj\ÁBXŸJB‘„­(+ݲ»ò¿½+5bb «³1‚*kme4D€ÍÆêü£ü¶#XË£%WçǶ§Q/TîGÙßö3)E›C£+û»æ§ê¼#?ÀÒ6»h!–L“©Sb[†>“4÷¡vôm©Ãhä !1}ìC1·Îìä*b*D{¸í¡®¼c‡§gEÝê úþ©cG¯Ç"/§³õN1=: ªrÇSULC`ô þùOhÀ=2sAB%•ì)¡)‹¥N?¨“a1qú‰æ½U›äÌ=»ÊþŒ2:sÁ·cAVØŽ-ÑŒE.m¤¡ÈðæbêCœ>ü‘ñÔqþ™„ T¿W Œwœ×»×wŸý¼ºRÆÂ°:›¤^DÉFЍëÛtëÃ6 zVÇ{N?à¥ò½zñëHÈhr§åz_qºÛï²{ǧԆ­g L/Çõ¯(÷wp ÆŒ©d~®²Ã%òÖ2J®©ˆ©G¤1&mÜr ÕPc¢ù ßȪóBÂÜU--·¶%঺Ø~æþò[^@å äPA„û†žéNèÌÑnW5mýUG#U}Õn“äñ¼L=E0ÿ TIö…~RA‚þsŸ]Õ7b8¨ª>Ç*¶A8eä4„Áœ:µLßí³}wѺÎEÅ+¹³ìˆë/äî1Å›ÚÛ„qI©¸Œ¾Ë¶žŽŠ‚p³c1ŒÊG°â‹ð¿&¶Eè³Fo¦a^ÆæÐæÓªÄj Iñ;VC?MóŸîiÑómirM‚èuX ÆlßÜÝ[kj<4aÂKÑÝ&S_Þ±èlÚÙ…Êï[jŠîà«ÊuȮô25\º!J©AhWé b„“ʱDþEÚé’ñ=f«ˆ1jEõ‚–KéI&m@€R¸ÿ9;]ÉuÈÃú«nÀè:/©S‹Ï¦½&æWí:oKË‚D~bS¬oØ4èYï9ý [u/P²Ò‹ç 4gët·ßiýC7OÖ•èpÞP±@K\iæGî¿`ê寶¿¦ôl¸‘_ªX#hö‹hW¸9PàtáúÆÅ…ºƒ©:V±³-tÓ1‚ë+„ V÷´òlÛý(;U ¡Aó1Q›±c…òù¢V%À³¾‡ 0g÷ ˜è"Œà΂‡9Ã|¹§båסh4>›’ç]_%P5Ó|ÞïÑ㽫é>&$ܬ9:Ï=÷¹Èïy˘8q­(ÔiáÉSrô=ÒõV¹2s1r¼áþJy6Eƒv#Ø¥ï:bh‘0+Þh•)T½ V-~ŒÕˆÁ36A-Uüf¿×öQG£6áÜ«ãšÏ.¹{Ñ8bAõÆøØ¤ò ŒXðN«t×öÝ»ü$Â6vvŠÐÇJ ç‚4Ø[ÉƇzdˆmÿ‘áP!ça^è±Ð_m#¶úÄIØ@C"¹ShIua0ÿÚÓrS¤þ¬dêÅ;Χ$•fzu½ýŠØºÑÓÐ1î­0HÓJëUŠ#m»Dx¦yÒï¿Aà.œwêz¥Õµ”Æx¶ç×ë;Ä¿mÏ4²ƒ`6Ez:µ ¡h—’oÑ ¬Á×埄O«ŽZð/¼a“ªõ‰éÉ3Çyÿ+=G í¿»¿`^ûÞÚÎ̳û2oÄH°:C:Ú•»Ãmó¨Ncº7˜ð£Þžæ’£(³`Œh‰{M·öè)5ÀnèæîúDX¿PçÑ6ƒpn`À[È=æ*»Ëùô ÞŒÊö ÿ•ܹ6š ¨sÍ«èĤùª<©íÓþmjÊâƒ) Z¨eΰnO@ˆ¡Á:ö’›[¸+ÑÇÂì¹ÃªCLÄ£ÂöE–F*_)ÑÄè*l® ­ó{¿ùgC¨ôßÐý¯Ò»Ó†™«QLläþÎé#Þù‰h–ê{¯qҶ߇úrûRgÕþ>Ý®ï¬4a®™¶díù…Všb"8б6ÕÓè䥄ORÛšB€Ñ¤–×Iµ‰:ü9*C»Œ±ã/„@8ÍÞûÎ…,)žô÷Q¿ü¨¨ç î °Ò§…[U]wÞ°hFø2ºœ¼ÒŠyæ„r-·MÊÈ…8µ›9™X}N{<© 0‚óÒµ‡l| &ü(«Öƒ­ ½ôÇp›ÉÉ>`˜æ;ôfSÝu$¨˜­víñçÚóY–ü£ Ðp; ¹E¹;=oÛÓÓåvád0±’£{ÇÿÙéf¿Ó&ðá¾)-ìïx=©R@Ø¼Š´vØä_ù ؇¼îJ§!çWÝÁÚ·¤üS)èo¾ïLïT_ÖÏÀáÌ*žˆŠÙy_zò V¸Z½Q¬^E#Cl®ÔuÒ }Ǥ÷od‡õŒg†ALÍóâ'áŠý•[Ÿ¶F—"Tv µ#Ê„yÅõPÓÑnǼ˨ _މ΄Ѣ,âx’R4./:Õbä/ÞqÒu¾»¤šHßX¢8ÆÚ’âp¤N¥é–»ÃÚzîËüizikS^l¼4l„¶¡~¨œiÿh¨}¼¿­•Š’öÑ©Žèeß.¾z´¯c±Ftá½ÂsÌ÷‘lø¯Óg@@‹/Qϰx„VÛJ#]KÿÌé'®î¼J+ÒoÒ)©ÝEm€PÆ,Zj‡¥c CW““•3‡Ñ»tÓȱ阛ó³!iÝ:eÐîÝeO€1£Ò}Ž9©Uè-†{>8åHßKêºâ}&ªoš–“F®ÆHœ°¢Ä`œ4q*†êŸc%Þvø<ÝÄ¢4¸:ö±Ý?½bó»M–fc¥ÒH´Ö8nÈxï_Bbüˆ/懺Âßñå8Ô£ï=ïËípñzjšëÃÔïD ò‚n¥F:÷*†6íûò3¡[ÿ~×¢×’Ož¤‡ð—^€g*Ò/ºâHª*…Ä #ê&²Ö9¡î¸ÆÌÏ7ó6cD~¾åGE>ß÷ÜØÑ¿aÀ÷(·¾ØÜ¼Ðšw‘²È%Ì%ö<™Ûí~À¯ë'! ·«[N‡ÿ9¤’ÂåI(»>Ð ŒE¼áG÷¡‰mFß´? –_†í*Ÿ)Ýü«_G'Õ +wÏCé2ýÅíNò†Ëv¤¼.,ÙQùà6ŽY(—AB÷/@¹¼Žz±šT­èT^/{Á0sÁd¤5¨d’Ò´1þbƒF†—ê{å8¸–Ê“ú¼Å“¶,—][)`|¸Ü)wá´¥sÀ“aG¥un4ù„ÚMh/în=×ÏQa;ѯv¶*æýð©ÎCÙ•´líùÐvÛ÷Ttì¹øÆþ>5߈û­·úòF¹ˆ?gæd½>€|Ü´W/ú'ž&­2õÉùæ×ÇÃí¤}5¬p‘¬ºðN{Ú÷óå/«©ÑNÓ„{K¢ßÒMê¨âÂò†t·Ã,j±înƒ•b£ÝœùjŒwRËbßâXÁ¿Å>0\:†gådx´–]Ę+ÈÂyÛï쨔•«ƒR="?ªb„ÔÐÜÜn¦®µì*ƬŒ¶\,Õg±8ÄT®=­[ª?¢©ƒ1J~ÉDÛÐ^Xskña¦Ëp¥´J]nš¢Î‰©&`có†,šãÕËEgÍ©ÒåÊŸ2öÍÔ¶Ý›Ò3!„_´€ÙÂz8órl´áÙ?#À0ŒÀþ@ƒ«=÷o¸9÷Œ#À0‰€ ¿D(N#À0Œ@ƒ"À¯AáæÈF€`ßê‹LcámV¡Íà ³ ± Ç4F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`FB IžíÙHXÅ4Z\åtÀ¿7ŸSÂ1$Fìè¿Òu:—âäÚ$`û.nÀýŠ_¸7ù¿mJuniHʶŸªƒ:= ò·ÑU;}n¸›%b˜,‹®”™§IùoœíŠ“õÙÔ‹¥º8ÒÕRõ> Øç{1¥@ÿ½Nùϯîꦵ.ábÅ'‰–¯º`Q]ëâZWÒš¾QÛªóÓí}“ûÐìñý Ó˜‡ën¾FÊû­ªV@TЩ}!(úÔ¥èÂÔhO©¯K<Ñ„A>;áJ¢ ƒ»ò–ãö‰#k ƒë‘àZ•¾5ù‰Ôm“þ«î.šx ·;ÿ‚«‡vQØÿ×âZ—l4ð,òñ£ÀíZþ¶³ Ãüh“ø†î)û,Ò8Býù!Ô¾qëýÌÜlëŽ6ð]™Tçx‘§îAûå}®Ë¥]8m\V•klîÉËë\\lþAW}Mçµ®—ŠuzÂÕƒºð!¥k˜/÷Tݯæ»5Wÿ©9™s‡x'nвÇóýù—ÀYB꨷¿€hÓÒsOCÜò6õä“DÈW¬ùÀI:²¨Ï¸Ó´ä9Øtº5÷÷F~A€•è7$;w8¾§íbð‚[ÝoÏ×óN©;b@®qIHuÐdK;ÒTyJÉá¹¶fªö{}ŸÜ€{Ù>œ‘“=&„Ö ¸ïk:+÷Ûöt‹{±YìMÒZ.°íêóD£ùˆÒä' œÅWîünŠï¦¡¦š<ù˜p÷¼Å3?±®†.oFgä·çƙײ…«åÎÂrèÓ‡\n1W²»)̾¸ð´kC¾XçÊ‚®kì|Å“'ˆ6iigçŽÔ„çëxÇ•hôGøI1 ùÜõöÎsyÞ± øŸAkk¬è5&Œè>qÆÑõÔ gŽóV¹ Öé¯.ïÔ‹.ÐsÄÈoj•ðRuǨï-§}ÅmÔyN»ú¼CÐ}5mœ7e7'#߯ˆù¢ÂÝ¥„S—­Æ:×±¬¤Ï×½£ïg ìÔïø3qÔàÁ¸Pù\ ýrEÚ¿ÅsV¬óQ½XæÏŽcw¡Ñèù²ÓÏçÌ/úö;“0ÂÏãvgùýÆ@]jl/¤$ Ï?–,(JOóoE=|s ™4ב—×E«™¸mùbTÒ]Þœ€*PÉøëÿ= LþâŒjÅÙèÍ&£÷§Jm»!ž×p—ñ晹Yw“]†ozš2¶æHSœ‘QOˆ•ð?«‹;s²S½Š8¿…Û(Í#ÊM¿x QŸ8=Ï©ÙqXt½9‡¢ð*Þߟ™ççt‹æù¸é:¿«Û{S¾‘÷rr=îéî˜Ü©u§ö;îòøónB>oÄMÍG¡1N>¿âæë<[݆ð×äûó#N nwC}…ðb³æSp{0m°·ÃþRÄSUäò'ôÑMã—GÞ0Í—¼™=ˆ•Rç‹Ãßv)´wq 1òcj‚qUS.ü¡+ÿ,íÎŽÄà‡s¯†x÷CNFÙÿÛNÔPoÎCɧ<ÒsÝs¹-·íëúÔ¤Z‡[Òó€ÁX̳ügFNæw‘М=þ,išÙàŸÞÀÛ2YèI£iª®ÌÙÛU"¦„«˜FxâÇ(Û>qbИúgÁª-FlKÝR{˜Tš¡iܤ¯¾åß<ô/ËM™{é)]jE¨ßpß‘äg¸oJ ¿çר/ãQ_Pÿö™¡Ùy·¦ºu业ê¹?Ô)újŒD‡Q[ʰ3ð[ŒÛî½NŒ©ÁËž Q¨?Ž`Þ@w©&Tö´Üì`‡Ðê ÖR—­xA4¶ÏÜ}wµSJÊ—B°¶ºDt›ºo%†Ñ„VŒø^Ìi]’‘÷×ÚR5$+÷Z4¦Ñ\h´îEFhT48_ÏŸÂBÿRû—Ádàó'Mj¹ô,RB[»?ßç{¼ù%3Ì—G Ë@4ý†<æÀ @äKˆãfÌ…½‡ùœ±o‡ùh^Á÷ÜÕð;RÓ\:܃¯T‰Ñà| ‹ÝÝ<½&êò"e¤ùô|}ü“Т’= 2£“pFåŽ|!myÀä!øu)Cüw¸÷Ñ£(:4Ò[Q/Ñ;Â~â‚`„à{~¶£²þ¥Žß²ÇïïäÍ4ô@¥WiôM†n¼Feÿ‘ßÏ·@3‚oâÙÈÞŠÿ")—ÎîWÂïb;œõ4Ä (Ÿ£Q†78íMò‡Qî€Þ¯tÚ×õ cšìÞqò¿€ýƒÒ\­¡Y¹7He|Žq'xüü°I¶ÑEùÂ!ÙãO †¯¦ÌS4mouõÀ‹ôèú^cøÙ?xÁ‡pëj˜æÇ#²r{Øþì§SÌÑçf¯!».ž¬yHÓfð¹×*sÛc˜g¤ùq YU½MMXs¼NR`àŽ¦Æz £ÔHóx)ÔÿPöÃ`¿#Õ‘…»Ê&ý„y‰&_C²ón¿Ð´‰ŽðX!5L È¥Ò%7éŒéÓ=h7HµxæÐ߿݌ÓtÓü"ãá¼äÇ2(G¤ñR±×ÿ%Êòƒò˜€úÚÁ@§øNoÎÁ¶×Hê²M³ºö ‚ÖŸvç}t£jãì`MîéN”—ë婨L¯b”uƒ2Õ“¾Ç>©nõU°²-{&ƒyÞè㊊<¼½ÿB4`/ —wñ”ÌÌ`? ½šq*k§ådͲóš‘=q¶2M­H/í;K}‡É{Б Á”Éà_R•Z½ò2³ðL°Jó`úªðs zÞÇ8zÞoõŽ_`(ã]Š{z®7yGY.—Ä"/͉„5Ã&LhkéƒA7·n“ü߃ÊÃzŒÂˆž¨ì½»zŽèçóU¦‡ó)NRzsýªü]ú/€ýrô.çPe¶¼ì~tbGáPFÿœߒP{'Mz/ÛºgÖ?¹¤»Ï´œ1?†ºÇò;’rAcñøá ¤i€÷}?žZ´£è`ÿìn¡Çs¾ÖHvQÖŸ4íNý Ê#uÆ!~,¹ÃÐÕ×ùþ•£Au\u”17˜¾gWÙd”Ák3s½Á!)ü«@Ÿ7Ï4Í'Áï§¢±E–ÀkÕ—yØzŒWa%¯&>˜‘“5ƶâËmúÕ]È«`G#Ë ^vþÒ‹\R°í¨ƼÑ80û3¨—« x:ÅÓjÆ3¾»÷Ø~èm~œa«{G=ß·šó'U/Oš«g…_"õFX­!¬nÂ{0äà4‘æË%ù·NB9½ ÆUvy€Ö?lzê÷­Ð¤¨ãÝí„ç|Y?UØ??Ø›»¡Gñìp£ÍHƒÊåƒé¹Y™vø@yˆ~!I[,Úê²¾Þ°½Ÿ‘Ô%g ØÄ^ ðÃ@Ûh¥Eº=ÃP—S…^òDu)+ßVx*ýA¼èô£R5ŒÁbÒìç´}Ÿ‘3z F.Ha °ÝçèåÀ_`TGÂ/`L  øƒÂ,ÔÕP£|æ|–õ´ÜLôæä%Ìë*BZ‰Ï­î#GÄ©HEÆ{ˆšŽ´KjTÃ8 Gò®¤7Tð… ȋ܌g·pîuµC] šsê"øÐ0½ AµÛùCk2ç'²rAaÕ¥ìmú÷î(=ô’„LJò÷øÍÔ'¶Æ÷‰ä?||u²µx|ªÏû-´S@?kˆoüÑÕQ*,,?VH5¹ -R@°0LõþÈÄàHÀòa™‰U¼¸•çe§Ý´±Y?€ï·aÓÓi/õ’ë1 5Ìž`g’Ü-•™’g¡áþéš\êßó;Ênu.ìðuʸOô >p>‹ê:£ª¥ÍÕ‘$_Jßb• ÷Ið‹Õ ¥ÛëQãr¾@:i¯DŸÐ³r»"*Ðxj¬ËóFdu)4î¦ö0#?¸¾Ñ0‚Ë„jãi<_™ž›ý‘ÅP¨U¶ÁüÇ!ônbF!l{UÅæ/േmWݽ´ÏÀüÈ=Ã7±»òûOpI1 {×Ü/©žÍÍþzÿPñ@¸Šôü¯º«@ó°JnRÙ½¼JÖÖ‡’Eè)O…8]“®KžðÜQÕSÝmÜž¶aãÆÈ÷Xh.oV¦8 ðˆ¡ÒÝè¡þÆÎ`à!(wêDQÉ(Müê «™b©óÛùi¹¸\®Ù¦©K{ÔvJLÌŸ ¾Ccý¸‡:>ÿ•Åzð ær»>qÆc¿Óü¡©üwÛßôìâév¤{Û<î6™º¾ãr¥Pª3vŠñNRw¥¥—'yu¨£Ô´• ÀÐuâ·µ¶{uen»W÷Lk­mpºQªÝè¼TRÍ¢Sr ü½3côèÝNÿôŽ9¶yx\8Ô—w F÷A@*ÚQLZ†³È#èóc…«ç?ì­”7‹œÔv T€2¡WÊ_¸¨jËTŇ˜ åò$­ žìЉ9áT´Wèì3¨û4õ¢;ŠÂ3XέÒ=•¦ª+hêr¤¼i]Ú—‹¦ùÓÆ.Vtq{Ñ«•ߢ1žFj(T¿“6z£˜_²FNOcj’óF¼møL§ÿpïè‰GR÷_·ã÷_uu1” Ë¥¼Œ–ïC8f ?‹VHz‚ô•ðCXBmºÏ€i‹ö}U~C…h__ìQyž¢¥Õ•}ÔïkÊØáUâÎÈν[™þŸQWFûö&ч4·ÄâY¥1«_ìVè4—ÒºÐÁ¼ãÛÔëvþ°ðá«iEP.ÏùƬF#´ŽFý´`ÍÒešªX¹ªä;hì/&{¨ÞÂßÚ©¾1AÁâŒÛtû[bÔÇù+å×'R­*M Þ:m“ž;ÔåNúÎw™¬WâËͨ˜G­ÄoáÊÜI«º÷É£FWçfÛ[£T%ú€g^¶íÂ=iÁÆíBw¢qïŸá¤Ó_4ùq† ¾+å ¾GöRkÞ"!S]¾P‡Ó(<ÔÕò::‚éhc~p¶UÖ»&GcÑÂmzŠÜêLC$åm]ŽŠ7"¨KÎô6Å÷„ùˆ¤k‘=ñŽrå_ä×wG#]ŠÆ9ˆ¯¦´Õèó áÖæƒ!¢xÑRÅl£Dɽzé }„Ü{¤N"è}ˆiëó‹TÉ:tÙ4'iŸð“êôhÃ,i V~F𠥦¹[œQ¬ï¥Iýw–ÏÀê‘øZ`Qà_9 ù›¹Þ«íX¬yYÒBß¶ŠÉ#ªß€U÷˜«Htå2iëJ§‚l‡$tˆ¼[zÞÆÜç„M"ïD|À¨u_¹“‡™îó.Ä'…¯³™9.ëCŒ6_EZÆeªJ\Kü€µ(Â,¶0ÜY)"½WM[SÉ>ަnÜ ÙŒS| 2¬Ý´p¥ýß^³hŠô›Ã÷Šhòc™ˆ-¤3iÅ*eWšLo,š/Ôãß(5e¢˜x}W¸t¡“´y)™–›õR8÷híâZ—£«KÑ&=aüGÜSmèOɽ ꊉ#Ѐö®¿Ûó=ªE‘ò+L"×l ÞÜ„&T •Ì4¯7mýrMg£†‰UUÿ³=¸0 ÆQ*k¾oÉs¾iÅhÀ(ùèo ÛOR? >Åáçë_‘/EûãhÕzð4 ¸Ë¥kÍSDkpÜê_‰¥XУĻ•¼¥×!î˜w„€Ó”ÃL¬ˆ=¾’Ç|DS.´… (Î$í¤æ¶Tžv\ÒõvO,¨8µÆvŠÛËÕÿŒÂªgædýò0 ¯+SrMCqpÉ«òkK\Mõ ¶°äŽ­A7#?‡®V$7,ZÞ¹½›±ô=t2ö»)5 .bzáBç æhòƒ°÷€WçAø?aøõß°Pi.ÆÆ[5áºâušúæÏIË~&_8PâA:ˆ`$µEhæB¨ ìÅb ,ÒRäI@r™Ræ¿MU¶Ú–QFÚñEóŒg]ަ.E“æDó‹6 éZ®\¤µmÓ2iKuçÒb’¢=þvSò¼ÔðÇÄ f«?ï ³Žëõ{¬ö‚Å$a¡ÅG;½³fÍrÍþùמ"U+>÷ð÷Էíˆg]n®uÉ. ~2Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0ŒÀ~@ƒÜç7,{BÃ4p»èËZqוh¹ß#Ï0Œ#° €Ë; q‰v²ºÞ¥¹>žš3æ‡DÈvÜ„]^šïÏ¿×<߃›±”R˜BÉÍ~ˆto"džÓÀ0Œ#_ üZ@øu…,èŒÛë5!ÅïOuót{Öç»­4¾±WO=.Â/Ûs>¢œ‰ŒvÇmÎá÷Š–¦½_Ÿ¨«Ï»0Œ#À$:Ã&Lhk›—(¥nÀïB ˆ6 ̓gäfÒi¹ðËÈνW™â1)ÕRHû»§çy¿jŒŒqœŒ#À0‰‰À¬Üþ¦Ï u’š5#ÇûdC§ÔË‚O=!…ü¯ðtºlFÎÈÕ±¤Ï´F€`š>?Ìý|ýIç\û²4‹À(päIÎÙýÃWŸÛ9‹ÙÈTJÉ@ðÓs³®…ªª^6Œ#À0Œ@x øäoÞkWBd\Ü*P-|’¢³¥Å-1“TÂÓñV|ÑáǾF€Ø°ddò¾ ¿™²¤A ˆ‰ð£UÖâÌñÍð )n”s$Œ#À0M’Dw‘ ±v4PŽb"üh;$øG¼¸¥J£aF !@²ƒdˆµ5®òUoáGØûøä+ ”fކ`F ™!á÷ ÉK¦4@Þê-üèäÚÀNûø ½#À0Œ@3D€dÉ’ŠÓÀâžÃz ?¤°ÜÂØã^V#À0ÍK†@– ƒ="“îúFBgu‚ÛÆ†`F€¨3tü%“L‰»©·ðþ>:¤šÏêŒ{QqŒ#À4o OH–4ÈűP{6ïÒàÜ1Œ#À4;Xø5»"mZ¢îÉËë<"+·AôüM ÄLíà켿áD§ï3uœ*F 2ê­öŒ,öÅTFà>ßãíŠô’q8ö,onC®Y¹ÛqÌÑB¡ÉY3s²^¨¢iù|Jóù$Îòš=¾VÅÍÃõ0_ãPà3­“2BHÎÊ 5RßyÞ>!NõûT¢3œP?"šh\xä׸øï—±?øè£-‹ô¢„RCãüŒ&]—¸=ò4%e6¹jJîjnÀäë¹ëïò=z`Ìó¥D¿!Ù¹ÃcN— 2Íù5óN´ìÑÙ}…ùï!]¹¥§Ïs¹-w¤‘NuÊñÝ,^‡fO8Á0õØ >BGŠYÐOž•ûÎsyÞÍ0Î#аðk9Š}ä›1âë¯iÚçr* ¾}žo¾éiÊØš…áùöÄžÒ•PñÍêâΜìT#Bez –I_,Ý©#”^ú´j $CÔ¨s¤»ãÝB¦)édŒ4À_+Ü59ßíqÝûœoLðÚ­ qA wÚ™>ƒ°ýŸÉùÖ¿Ó|cÖÙIËðæ= „y N¡`ÛÙO¨¿Ã!ï/ÁmêPoÎ`SéãÈ­Ìïÿse~ÐÜUäÙAÿÞœs‘Ö,܃râÜŠ°ïvu˜É-×·;Ëï7êBL½Kmš5=g?Kšf6âê LýÀd¡[$éˆX$2²óîPÊŠ4÷BºVÀït„+Gp=òŽÛ1ñD€ÕžñD—iWEÀP'ã¾Ç’sŽ=üùªŽ•m|¾YIÊ¿u¾0ÅMBÊ—4MÞŒ‹/ßÃ"™±ùþ¼7*ù–²æK…^2[µA ”û)Ê_•¾å5¾Ï 4ˤÔÀó€§éºþÍÅéX4Ô%{õ¢¯H@aîñ^¡‰q8uâDÓo,î›t@Я0»`Äuô¾ï}oÇà‹æÅ„!]+Ak–å*ÕÓšÔr•Ô‚wâRÏkáþÒìBþîEbèVëÁùz~D'&iB+–§„º‚ê¯V<5üš•{ƒTÆçÈÿNÄu~^Þ6º(_8${üiΠè <¨Ls&ÜÒ\â&ø‰IËûI]íôGïõÍG(=þfâüâ0Ó¯„T_4¼k dTróQ ¯A‚£’c£’7‡zÇ/0”ñîïø‹§çf~`…°Kƒ`53Ï;¾Âî ŒFÒ° æf4ÜÏážÉ¶=ëBS¨œMúäCa·ÊA£„ЧçzÇÚvC}>€ð[©ëe£`G¿ˆÍÌœÌ/±:²‹0ÕÝÉî¤wŸñ=ô»ø®§ŸN.Û²g2Ò÷ÞÌÜì+*ì_âÍYÿBhþìpÎg¹^žŠæ«Èç ÊTOføûd†oÔ6§û}ÔäÉé{v•MVB¾63×”>ßÿ*ÐçÍ3MóIÄ{*-žÉ˜8±µÚ냰/Ãï6{}O¼^ä/BçbŸ‰E>öQã7F a@›À†h@¤8jSd1ª«1ºúÌ!ø¬`Ór3ßC½F óºP:R&½î´ƒzósúV.Yi¤ˆŒe/EÙANÿô®)Ï«N;RwBÕ÷Fh9íëû^¾­ð ›ƒ0ªzÑIK¥j "ÍÒìç´÷.E²U‡¥Û3 2+#ß'Âù#»ÂÂòÓ1Bìaû¬ÓÏ7PÎScßáL<˜Üä^ÄU¸.éšáôû¤ï¾]˜g¤9Û ‰E>‚Äø…h xä×@@s4Ðø@ uŒä…û_5~1ZS‡…º)wë|§ËcL—é®tŸË#÷~¤Fˆ$§zOiçÙj§ib•iŠsCíëóy·C(¼iˆI¹M°i©Z^ŒUägÎðÞ€c¦©ÄÓx¾2=7û#ÁAEi“\ŠF¹ÂåIÎsÚŽRÓV*$ÄÐuÂt­FËÍå ŽŠƒ~¥ØHÀÙ&–ù°iò“ˆ7,üâ0Ó¯„F}k±È¢Ï¬Y³\‘¨>Ñ`û+°?”€½L¶?íçô±%¸Óþ > QŽ5!‘—aTш`d”ŽEš@M@IDAT4nŒÔ S Tj2´H¦/Ü”2„ÈÑ\`Q(ICÈßBíjúîâöNÁ\è_1ª6Ü7åÝ¿3,v2Y¯jïΈùà Lµ¤P˜)FYhœÊEÎAë| ó #GXøÅ\&]M¨¯ %nùtɪ[àúBU©~€¬éá°Ù÷*UwèWÝ[q‘ÞÔ*Ž”: o£Cð™hÿ«Œ1çÖAùK<ÙPmÂ4¥­ÆXK·6š/kQµ#t Õ¯#²'ÞQ®ü‹üú®ñª¥À(Ú®„Ð…Y,»ÃrgÐ^tË:_m }¢“²ù²Ôì†Çn²³ 0ÀÂr ˜XçæËOF žTéáÆ32¦Í˜uz Mêj¡ÌG†x'^#"J~‡¦û|(NÙÅ€é8¬üüÜi«wÝ0.uÒê›Ü úã1šû&h/åï°kM{ø‚vxÁªÒ0ó‚9Îrè<¿èöàˆ0lÇð«ÁNõyŸ’3z™&åDhMG ãÐÛI+¥cúÏj¥t7:í­weÞ¡¶±‹8ÌúneBPJCøÍ+~«cͳv"ù¨DŸ?8 À#¿8€Ê$«G`Æ!þ!ç 5Mõ†e‹°'n²pÉn¥m1M£¶`AŒë­i9c~lÝ&ùïXx™ÒK>²û{Ýž¤Õ¦é?Î4ôç02YÑÅÕïÕÇT7Ð%õ߃C²óv$¹ÜŸû ³£©—= 5Ÿ.ܮ༜î/àÙ0&"?“d’Za”É3¡Æ|1CÀì3ç{ؼOþu¶ Ü3Lï×<ƒV}Nó=°eˆ7/“{¿rÍ#^À)Vºy²Ú)Bº¦ÌȽd¥ÈÞº¸zååë¿^Q|èG”ÏÜ}wVžÞ†U¡/#¾­IR½nˆ¤$Súï@F®‚êõ&ŸoP9Å2%Ï»eó/¬Ìƒ0É.÷Gå†j_ çÝçöΔÄ+Î8øˆ5<ò‹5¢L¯V¦Ëú,Åt,<~‚ßÂ0?ÐM}!¦œ¦C\‚曄xìŠRÓ$Â~C#ü¶ß_¶É0Í·0zYàI×ΠUŠä/–ʼR—æºÌTæèRùFœÌB£éœ3׹ŽkzÎèÅØ38 ií‹«§g%P‰š*[ºåÕ­.¶ýÑÓšÛÄþBäáèr¡Öa³{P]‹íjRÝ·+±ç;Ão.Á¢• DûH¥U¢Nš5½[LÉ; øöé&+àÌÔÿƒ:tœ®+Sr®ÊW ¾kйzzNÖ¿t»yº ƒô_¥O)ó—ç+SŸš{¡½ÕéÞã‘Ð8ø›ˆ%¨§õ38±bQwÒEý(sèý»Æï¨Ê]žg}UZ‘éÌ?©Û¶úó:ë¸^¿G´PÆ8ÂwœÚr?N3ylF®šC©èðm=©ÔõLfæÖêHPº¶øóº%yÒ ­mÕy¬°ÇžÁžšhµí9߈½¡^‡}µmÓ2iˤ‡* uõ÷ˆñãÛc±ŠiÝ ]qìtÿ!¾=ðì£Ý ö ’ÎNM &%O(±v‚›Μ\F $ügå`A§c•H%üÁ0ñD !å «=ãY’L›`F !`á—ʼnbF€ˆ'¼Ú3žè2í&…€žw¤ÔsìåkRéçÄ2Œ@ä°ð‹+öÙ̘žûà*d‘~lF ™#ÀjÏf^Àœ=F€`ª°ð«Š Û0Œ#À4sXø5óæì1Œ#ÀTE€…_UL؆`F ™#À¯™0g`F *,üªbÂ6Œ#À0Í~ͼ€9{Œ#À0U`áW¶aF€hæ4ÙMîò'ô1L÷•‰¸¦+®§hÙÌ˪Jöp_M!®Ú)Àõ7\¸omjÎ\@ʦ®0OÕ9×\¶)B¬ÇÕ^7ç6¥I ?ŸïÅ”|þš&Gâþ·.t UË´³M«4™âñìw£ØR¿ßܵ§X—jÀ#°nÒuãÉV¥-žy≑t\•ûÜšK%U>˜§b…$Ói„´)yÔ¦†ùÄI>ãóÝV§û%—&#üpÕÅù/ =ïvô!ÝÔÉ:D{X7™–’ìJTp ]–À/.-KVç‹ËÖ°tõï–´(¹çÖ¼ƒ_šœû1Ò€;bY†+ æ©p¨°Ý~Ž@¸6eÚÞ{oí»ýù‰>jSšE§ºÞ‚£Oÿ³o%fùá«/^¢g< ©ûpÅÚ?»ujÓbð•´‹úõ–vj+<î&#»ãK&á@xô=æyDÏĺMÛÒŠJý=¾ß™E?Îûòû Í‚aƒ™®ç óT=äàÍpmÊÞ’ò¿öé?`_|‡ÌÇ¥=iyb\¢« e ‘ŸtTOíÁ[.uÞý;íü ƒáóЭ—ºN:ê`éry&Ýú w$¼y𣎦F÷{Ã<µß³ ûÚ”žòïwd>rÂ7ùö$‘…ŸÄ0ûŒø&£!Ww\y–LòÔ{ M™7Y¿„áE¸y<É㯿óþ‹€TÞû³džj²\Í oLœmŠ”Ú¤[Fe^ˆô4éö$a…_F†/Õãñ<`ç6âæËÎØŸì:ó<áÖµcѪM›©ÇsJk²`i6å€ÌSM¹ô8퉀µ)Ý:µU))i3Ï=÷šHSÂÊÚðJÔ„kF;y—aš]¯=ÿøj+Æðî„Ûõžª™JÐûì³FÀW2~4Qš¨å>#±±ežŠ ŽLe?F€Ú”ë.8Åe*Õ¥Ûq½îM¶=IÄFFy.·Çuï1‡¨xޝ~5ð#Ó[¶ J©ø%¾øíO£iæ)8F ØmJrjê ×dÛ“D~Úw?t²iª°aj cÁ—aiTàØñÜ¿\{ <ì£?æ©°œÁ–Œ@Ý 6E)ÕùŠ[†ô…&9úK4ágõÐ=ÉIÐvìã«[Ép¨JŽ„g»NÏC ~M¶·V)c‘}0OE†ûb"FÀnSZ´i}5Éö$!…ÀìI'·`{Ä…Á«G€pl™š¬4·»;|‘êsÚú` ?ä™yªzaF *¬6m´Ô\=°I¶'‰&ü(=.¡É.8²,ÑÒs$šç6­Ó¥Ëãî„t9G~ûÆÌSõ`Ʋr¿X±nS=(4LÐ_×ÿ!JÊÊÃFöËÚ|ñÕ¢_ú±eÝ 6ZºdP°G~Mª=I´ÄRzÜš-qVgÌæû²¦üW¼õEýÎ|Þ²£PäoÙYwNiä„§Kj´4™†ÓĬMRO_ãÂSuHGB©®Ž,[[ žù¿O…_×,ý”–¡y/Y¿‘ÿxü_ÕX÷pö¤•Æ¥kòæ‘„ßœ…+º±eÝ 6EV›Ò$ÛjŰ³TTJ¨˜ ¾Xeîý¹?ŠŽíZ ìq‰É§£”…¯Í¨öŠOÂ:.G5x«F˜ÐAÁç¬s”ˆë ,m u.ɬްÙÒÎÐûJÔsº¥u‹4ÔÿMZÔC¦êŸW3ûg×±&Õž$šð#žhiÄG¦Ez`;Ez”d†\5=Ûoĸ™o¡"n¸ø4KÝi9†ü£E4Ô‹4MSмég›pqà²YK-Óý€v¶7뉣ÜÄÊ ˆ5PÕ,üe]Ðm3zÀ16M’Që‰AƒðT=ÓØd‚“*Ñ6Ø’qÙvö“¢8 i8ŽìÙÅZ=šqå@Kêtw¾Ÿ}òQâŠ} ¦×?] þþ¯Åã÷_ô‚S‹‚ïΪÔy=­÷áAëî´‡Fåwëû¨CºY£Fšú lmZ¦[à¬Át5£»MiRYJDá× V79Þ³k‘ù·?C­ÿþ`¾h;/Müå쓪¤iý¦mâ£o–ˆ{þz¾Uy¾Xð Ô§¸:ÏÑ̆‹ƒæòè|¼Pu¦KÓõŽô9ñõ©_l,â§NŽMú˜JS@ _Sº]˜¤QuðˆÏiÞÜiÖb$÷ëúM¢kǶ–:ó„#»[sÞN?¡ï4Ï~ i˜÷ÓÊJ+>= Îмd',LÛ¾koÐyT¢¶9q~8o±5:<ôÀ þìm¥Á•lüëŸ ~&‰6çר˜:…¶ì-.µæûhN/™&ÄahßZÐÄ;Í RotÇîb˾-ülßUœ¯˜±ÜªûGêÖE1ÒÜõ>cá ™0Yÿݲ5âWì«¢ž(¹… ÉêhÖÃÞ!®ëA%1ƒ6ç¼Åñ]Pßÿ–¿-ø#!9¼{?!æAº uâËöí­Ó C¼üîúVÜûØ+âfL”÷ÅD;M¦Ó*³´”$q9&×_ýp~Dl:èüSÄóo~)žzõË?Í—׫»µjí_ï-ž„=õ SAwèÕg‹Žm[ED7BOçý" Öd½5*O5%Ô¾]²‹CÖ“ìú—à{$/4G«¦i:à ¨*IuIjE2ïõæïJÅ ¬È¤•œ´åÙ ­yvêX†šo @éG~94°¢“êC$†´'$äÆ¿ð®5_HõëìW$C£ÂÒ2]<ùÊÇVÓ q¡9õ?ŸuBCÕ¿H²ÐtüVæ;Û”&“vJt½ nZŸCfäf g= é1hÍs«Ûð¾}ôa=NyÓ…õ W÷ ´¡WÃöMšÇsêÕ– ×˜†•Ÿ¶¡Q õJëbʰ:Žæ C+5Í•Ðié©)´ ;&†6 /þeÕò?5éü¿íøÑ„b ~Ñuñ ‰˜„á©&‚WL’I£<‰¿ÐúâQ))-¯T·¨^?øä,qýE§ZóùÔÁürÑ KX?9êÆ`]‹Gý‹"ÙMÆ+µ)KV¬ýùå'&܈DǤ=‰¡<©ÇDùÕšà†ðàq‡‡…‘SðQZê*øaÃÇCF ,$`Ã4Ehî,Lh§’ÃМ í܉í¤šýûìÔ.(ø(Ý\ÿ¡ô⟆ð­oüãåF€hP¨£zûýÅ·?¯ÁYŸ+DJR’5ïwþiÕo¹hÐrd Š ¿…›#cÆDàhlu F ò¤ãÁ0Œ#Àì°ðÛ ™³È0Œ#P~•ñà/F€h$hʈ /cEuø{ù)Ym3E€…_3-XÎ#ÐÔ ­t¸F !à/ 2ÇÁ0Ahà|¬¸üyÕÑ·CÐÁÓ8V\.]ó»X°ì7ØEô;þë8@:>°+¶$¬À©HE%e⯤Ç/Œ@]à‘_]Pã0Œ#Pg>œ÷³ø wûzìaâ'CF‡L—àØ@Ûü°|8×}»3ßÅ%¶dÖl3ÿ7GìÁÝ™tgF ¾ðȯ¾rxF€ˆ e8jì,\ Ý稞‚’'aç4Ÿqœ ›èô$º÷ª G:í&-Ȇˆ<ò‹ŠLƒ`"F€î裫ȴÆ-î¡GøÑæs2©Éë4ëÿzt©| ˜mÏOF .°ð« j†`êŒÝj²ÇŠÑš‹p[;™ë4?,ÿͺint y?6Œ@<`áT™&#ÀT‹Ýö°go±ùøÄâ_7TòGgÚ’ºs̳oˆ]8óÜn{¯?(à9¿(cïŒ#P?ºuj+î»ñBA·,Hè<í9?ººë±û·µ_†+†è‚gÛÜvyû•ŸŒ@LØÇ]1!ÇDF€ˆ ênO¡ÐNÁ5öÅD‡ ¿èðbߌ#CHÈ]Ô¯wpL I3)F FXíY#<ìÈ0ñD€Ôž—ãVw6Œ@C#À#¿†FœãcF€htxäRt¾ ýlã|·í¢}RïÖ6ôîü¶íùÙ¼ˆ5_9yˆy*qy'ÖåN9å²Myï÷ÂÏfNꚦ6mËq~àŽ=EbwQ1–]ëBì“…Ñ£^!÷’“ÜØÐ›&ÚµN·.ÓìÑ¥½Ð0ßÁ Wô6…6_­Ç±\ËÖæ‹»‹Ä.,ï/Ç2þ ;_¢ÌQ3à)‡Œýt"Дë„]îÜž8K41ß÷[áGLJ JmËp®àœE+ÄW?®…EåHJ¸µ¡¹w )ÊbRrJ$ So-t3U¼³ [¦'‰}Žú%’“ÝAA“ȘH£!@|Eîó+Ä —‰=Eeqá'Ê`Sâ)»¾Ñ³¨¸\¬XW ÖmÚféYRV.J+ÎölÛ2M´mÝB´o•.Âgvnku©£˜ÈÆÎ·'‰\J•Ó¶_ ?[èùý~«WþúìïÑH•‹ÔÔÕ¢cûÅxþ aTZ©}™fŠ()9Ç6'Þùª\|¹h¹¸îüSÅ1‡$\.Í‚1ŠŠÉ40ÄWKWÿ.^ýðkŒòÊ„Ÿ(‹‰ÌSP “ùõâU⻥«Å†?vZ§º¸4ü^ᆎ¦Va.Åš|tõÈU`xÛ*-YœvÜábÀIG‰Ö-è(´Ä›6àö¤+ZŒ¢Û¯„ŸÝ;Óu½órñN“ÿ𛥸2e³8 Ó{"%e}Œ`­ž ÕôôÅÖ¯´´‡Ø±ë21ãÍ2qٙljsúþI¸Ý.K:õúÕSc—D@Àæ«Ùß/o}±ü´üônƒðå?yÊÆ„FB[vì/¼=WlÛ-’“þ­[/©)k€S„Y¸ûû¤ðûÛáRÛ^¢¸äHñÉ·¥â+hfn¼øtÑûðîÈo…Þy'ú¤R.Æè1Éãé©Ñ Ó ¶Œg=²óÈíI"ÔÂèÓ°ß?›Qý~óxåPKý">þv¹HO[&Ú·ÿŸÐä¾+U¢‡±n!HØÐyºØ¾ýJŒS‹çœ|´ð Ûóu£Ì¡ ›¯èö7!ø“Ÿ(ÏÅS„z0QÁéШ”ˆ)¯†çnѱÛ>–Çÿ)ÔíøÍ­Z· ì$¶m¿J<ÿöWbè•!ÜRp&è±tÍF±iÛké$×"Í#îÚ ·F,ŽïÕ]$a¾=ÖjS»Ü¹=q"ß´Þ÷áGª bÔÒ²2ñóÊ â“c‡×µÄHèZiØv­xo®Û´°z·$].W£¦#¯â«%«6Š·æ_có¥¸!xjŸ3qg‰X³q³5‡· ÷í‘Ð#wº¹aÇî½p/ÆHøeÌmo¨Ð0><4’îü¼È/¸Oüó½yåÑ¢!%R’7ŠÖ­Ö ·k§Ð\żÌ©§‹ò²®bÅúÃÄ’ÕùðãžÖ[ ÄJv§2LQ[q{5d `¿~Ĩ¤š ßî={Å;s²T4âKCiñoî(Þ˜½@Ú­“ha]õ"cÞcM”ü6‡t_•–úÅ>žo©:‰Ÿßxð”=â!uãj¼Ï,·æÍià'5Cx\»sàÌN¿Þó‘ŒöVÔYðÙ|"e¹HK]&öˆÑà·ø}Îa‘í\ùIS†0¥¥=ÅžÂþè˜èbÞO+ÄW °ÑÔW«ÂíIߦþ¿Ù ?ª¬TQiޝ¤¤TÌûq•Ø[¢[s|¡ê¬Ža(-íÚ¼+þØr»˜ƒ¹ÈóOýSPõÏy‹êÒÃö5#`óÕç »±¸…æø‰Ÿ(õ±æ)jô©.£½5çG1ÉÌQ‹Ö-Š´ô¥ÂãÞReÏ0ÓÀDZY<Ö¶Ý¢M›O@¯¼æÂ©pMIY5ð:ÁCÅöW‹¿ÿûCqãEýDŸ£{ZZ•ºÔ+»Ü¹=‰¨ÚSb¯Žt^Z`ž¯ûöBåI«:bqK´É§4QÚ¾Y²Ê¥’úˆÒÏ&ñ°ùŠb$*?j±à©@ƒoXÓ;÷Šg^›-æcÕfëV_‹n]ƒ@š‘ïUÅïÒŠ±n36†ÀÃ!MÀŠ´c‡bÞïwñ&×7mÝiM…DS·ìr§zÉíI(ôZ’¸?ê±ú-•ç4V´öñ%ª¡´QW¬ûÃJ·aÔ­‡š¨ùké 4‚†uP¢óá]ž²-û`Þb‘¿u—è€Ebi \‡ªã15vìðŠ0U©xõão­…9T¿"5v¹s{)b‰í¯Y ?ª¸ÄÜtâUÞÝX‰Fj˜XÍAÄ£h)m”ƸŚÒMé§|°Il¾ÚŽ•Œ‰ÎO„Z]yŠ{{ÛFû7ïç5¢e Ìï¥.OœÂˆ2%.×^ѦõÇb]Áv±rý&K³iý²ËÛ“(AOPïÍ\ø¡òV¿2l‚-ÂO³V£%hiT$‹Ò¸§¸Ô~”~:s”Mâ @åAå²{Ø4מÄIX )© OQÇ‹æ©ãøå"ÒHèPu~ZC,MéEúOXðR.¾[ö›¥]‰\øq{Ò4J8²T6[áG½V[MA˜Vgùý˜0Ç’éD7”Fy á¡ÈÎK¢§{HŸ]Žç’±9û5ÞØEËSºƒUÒ˜2Ø-Ä/ë6áT¢EE–Ä;©q§OB<)iƒÈß¼=âyug¹s{÷"jš­ð#ôl5E  Ý~Vw5H)„‰$Ðø¬ö ƒMc[Ù|EeÔ”L4äóö5Ç?Fq ~¢ 'mÕ:¤LY3²é¼©¿¢^»…·MtÝù¯QߢIçDÈøÄ†`&/ äråðy†¦ùn·åÓ®ÙH¥…ãhôÀKé[_ûœyßL÷^½–fÍxŽc…ú+WŸó:mü½Q»¾~*/ô5ʱiBYQ`íL£X ·û×kïøÚ˜A_£;/_ÈÆ¾c1Œ2æþëvPAÎ dT,d¢(ox÷öð×XðÓC~{GÔN޶Höï£{w¿âi`ïÓèËψ¡`ºå¢÷X8‹:¼xíöWy•;øk®GrL$Ï«gbƒ€Ì—hß,S³ƒW*ïO¯~z#=3g:7~ÂÄ'%Sà©8ÇÊ ÔÔ2TœkõgÚØïÑÞÃ_QEå:;XMK×?Eym½òÉ7iPï3hâ°ëħ¯}’¦Ž¾“2mÑ}š#'aàN]‹—Vï®$=,î*2ßýûèH1-tÆÄû8ŽïŸ¹RÑÄ¢´p<rUàåÍGhMùË4câƒk‘Óé;Ré9Õ¶37O÷Zdž­©¥J¤‘an?ùM“ÉÊ­Ï_RNf6mËiqÁ\¾ç2øN§ÚÚÓÙmâߢïï²fäÐä×ÓËû Ü΃¼˜e[jl>ÊÓñ2H¹âÊ>6Gk·Ñ¤7Ñ¢5OÈÛz¼ÏÎúJ,¦[W7v>^x÷C1ðsŽXCœŽZH—žöÆ„•­-Õ6쥖ý˜ç6ó\a/57y¨¡ÞMµÕ.ªo=—,zƒÇ ºŒÌF+—oÊO‰ï^<í¢\ ö0-Ûð4Ý}Å2^ës©ÑohÆ#Ireqš$\j@fIÞh:T½!ð¦ÕÛþKËØøu曯¨ZG…9ƒ¹V‰³J Ε©à»ô:•ö]¸O™‚á“ÆÏl6±sÅ˽– ÿ¾¼æ™š¢9(Ì" û`ÊË@#Ê.¤‹Nùh ¡²)øï•?FžF¼7rè@¬BQRòryrhßá m£ª#~eDç¢Ã5›˜ÏöØ¢Ó¸Â\]·“vXÐáõM¹e\ãSGw¸®N¢C@µüÂÀÏl²Q¶£Tf·SUýN¾Í@ùÙet´fk8¨{Ò îdjÌ Ëixÿs饮 ¢•LŒ&:ÿäßðb°YðÚ&Ü~´À=w _F†Îœr?9RIË·¡#µ3xz€C„9‹&FnAö ö¢TòÒ]õÏ<‹¦¿‡5Ç@ V>Æ«› üVÃ:UÖë”Ày´™¶­§ô ^e~sâTZ”/Vz]ã®è«Îþ—˜õ@ëêxmÀj:Tq„>^³›Z¼å‘R=˜ªØÐÉTÂÆpÒð¸5z>·® ååÀF1?g`à\D€2~a`˜ãè-îªk<ÆÝDõ‡ø>»úDlü  Wõ!äw[P…EßôÄKóhÍÆm›þý§Ç¿Ç§¼Uò†À”ˆWÕ^ýä“J6ÈN-ʹù¾Ÿ¼5zhÙ‰?¸þ‚¨ØÃHY>ÿöbª¬_Ñûº’©~%SDËçÝ%?¤ƒGWÞ­…L^ÆúÁÌf3»3„‘Ì´ÛiLŸlÚt –5Láµì&R~Á;\)lo†ñÚÀ-9޾ªO‹¸ooÉú¿Q¯‚Ñtå™ÿäÂ~Í[êïë½9Ž>çµ8ÈæEx1òsÃö}T”—Íߨie@‹o…¾¼ì;ü¥¸Œ/pw®Ýþ Y-Y„0R^V/™uˆ\î&Žõ þûŠëê6(·g8B‘²3Køë§•ð}î·€QI!p,É*3GMâéS}SE§úäõºØè¯¡?Ìäë¡{xFËd6!r—ûØÀ¸¹u¿@à%«­<ÁˆÎ^céä1wÑí—ΧYÓŸl¢Å9°÷©â8ük‰e2¾KµüÂÈ5Ô¼Øp?ÞÞ¶ÚZwÁ¥ƒTU·KìÕ…@(¡2•Ÿ=®>û?´«b þk©oñdñHUÝvÑßo™ mù9vjjl¡-•‡¨Å㣢‚·(++²V«ªÛ!†ñgpK§ÕÕ@ã†|]ÅÆÃÔ§h" ëwí>¸8[¾pîœkyàc£‡5ú¬Ü‡¡ÚNSêŒVð_”;Tü„²åÉWÆn+ÎÉS]^¥¾?“UùÇäs™²jk”£µ_P@@¿0åàPõFQ; çöÒ‚qB¹ÝžÔ‰Šßêžž!,S#Ë.ÓfF ¸€°É4gá]´y÷»o™‚ñƒP¶þlV­ÝWEµÍ*)Æb¶Ñ­éWY[ÎÓ9\Ì×XŽº”àî1ñaAMÃÚ¸ómZ¼îI ÏO;¹5¨eòx1wÑ(æ1Šxʧt˜Ï8“ ­I zii­ |Ùéö÷ƒ:[ëÄ”—ìÌÞbšäE%íPÆ/L,¯û#]{Î+¬êÐÿ÷Q`“ÉdÌ )£n§ù+-/©½B S‚eê‹õg9ú{§÷%J¦äˆOÁCÕ T~ š‚µá“(ì¿ÚÌS‚ÆÞ-Œßû_<ÀWàÖWv‡A0¸w@¯“ÅèéWç·OÁõhSK³¿åURàðÇøŒcü_¸µ§»‡çô])æñór¸j#OÉñ÷ûá:0Úºg©Þ|›:ŽÕç&€ެæ5ÍæÐ„¡×tûĨ²‹éhÝV¾÷­nïS?*’A¦á+<|¹y7™M”›Ó>/ÚÄoŒ¦D_§LÁ£?嵉#n Ï×þš[ªå%Mö M'Ãf¦ü,Œ‰ŠoÂÜÍOWü’ƽš?ܵ›#?÷ΕéGãK`|MµüzÉþÚi÷¬ßù&aSI!z•)ô}ÁèÁˆþ°=‡jxÀËZvÓ¹Ãa+¬{`èž}kÆqï}ká·{OOohi)ã•ÊhÒà<žohý|h鯣¿OÒºmïG„­»„é O¿yzw·¨ß"D ¥[~R˜ýûJàcÁô' õéd¾„\NŠSI;öÝ%?Lëpq8›ÊÚzró ‹¥}Þ]wÏêý7T­ª¾„8d) ë[Ä»ýý›0‚Ý¥`ìŽ_w¯IØoÁô'Œ}¸ûÜÖ¡=%¥3å†l{5ÎÛSºÂ¹ßÇëY¸Ÿ%8uÆOðïê8>烅''ƒ<™žÊTÀøñJçÜàz=þ@ÓñA:v_©dÃ×êêEãûrÿ¥•'ògˆ9²ØÙ—ƒó]þ®Ê‰Drî;–°ÉÉCØTÛPX¹sþ?Q7z8ì’-ó³UÒ3[ÏuÓ¿<ÃžÊ”ßøù]žˆše3Q‹sˆž³#,Úê'QCÃ$ê•“AƒJóÈfƒñ³p¦9àþ ëE|S²”'nw6SÛ.[is_Ê?ÔØäf3ÉãË䊬M· Ú<ÞLrX-ºuKl™ÊÉ´Š¼Ò³2'®}šz””6~AåÑTpifYÙýcàðT#t›/  4ö)ÈbªÛ ·n NC¤\••è^ž=ÑÈxE*cãgçÅŽ½†ûÿ’£µ+oûSÏ!Ù¹ž –…&* ¼œ,ž¤ï¡Ûàö”K?z,óz™Lå ¯ÝÄ«yl¥¥þÆáâîç€ÞM¡¬¥åyÊ¿€ ²îbž’‘õæ9,Ü—æãh-t›Ù ÍÊñ…¼Ê4èFÙ#yÑ-ÑiD˜Ì äKïB6܊гTÒÁr5z@‰nå ˆE*S(ìÁ§… Â~Y¹o,?ÛN£Kldð:ØÞÆëûMç–od³¥Üî|^Õa˜&™ ê'³[vOË(á¾ÉþT_:r#í?øju¢²\MOù9”““Í+:8D¬RÇ.E¹ŽŽç;*ÉRž +-¡Ñƒ.öW¤¹(©mÚCÿb¸jë¿5Á?Y_™ä&·²¦æ7|f1¢ ÛýòmTÑPG•Õ3©wé3\»u邯ÏÂïgr«ÏGÃ{çF ~É‹.Ms"d^H¹?¸7mÜsHä]©Žä ÙLOîжv82ÉÙ⤢Væ5èvT·RuíÙlt¦Š¥²«¸’yüA^îÏ®­?…êêNcÃi¢¾½ÿÊöpTRUßpãþ1ï°½Ô‹¨öc/JnŽƒ^åææðqe·-éã~¼V^šïp•&Ky2¢OõêuÕ¶L£¥›1Y¾‘e£•é}˜örh¹ OyœÃØ¥ÆHÞc„ › )küÀ3:°Íl< ¤èÔF ÖÎ5Ø9fÚ^SB••³¨¸è•nà‰ßO ¥•k­'ôcWÓzA7è§#>~”ª/ËU&z™2´7-ÜèÓ•¬Pò-¸pZ}À38ß“±<Õû<9x Íûò^^—Ñ'tã®· _1ãîjIþѼȧp“ÞŒ1#ŸÇçmlq¹pܵ> ¥›BÔ^Y l¬ÄPä’;5¶6PE¯ }äj*,z#a-@ÔÎQH52-ó3¨oOs`¡´ -Œp•³;HZZ]>·ÛµûÆAûîK…ß4•)*W{ð²8´q?ËÓQ–§ÂÄÉèÓJ¦À'ZF6›W AÄ>´‚ÐZ²˜›)×îä•\TåôQ£‹Wzwöcë\m-FåY|”kv‘…]m«—³«òÐá›yÝÊ·ù[áO¢‡1E‹¯ž§-d˜¼4€Ýš™V¡/Æ-ܵ\„¾gff úáêÄ >è—„gø] ¡ùžŒå‰#s ÝpÑ­ö‹€Û‹ê¾4ïrš5ã9G2NB™Â•!4÷ƒË”pÕÅ=z2~P$¯Ës¸¦¶çkQÛ}ÇÛù•Ô$J ]7N§“úqä Ÿ·™5&ס"*È{‡•|÷ñ^©éïèûýýYy‡–æˆ6è½~—Œßíí‡kê}­NgM'ï àÞÉoÉ~)À›V2@:“«qKˆ+´õÐŽŠ’ymZÊ {1Ø…Cø~(Ùî ÖúkjjfCâ¤\Ö'OˆwóÔˆVol(Ñ–ƒiAKÌna=4eˆÖ脵¹=djò°@*¾Cn9Úwݺ“¿QÏ8»Ä†>=·'ŸÜ®|19½©ycÇ…/­d7òƒ¼æ Ó⟲`•Æ {NÆÏN6€™™¨TÚÄ}²Å‡¼ 7u–ïp'[y‚9çŸô(õïu!¼–×B ñ—?¾N,ª;fÐåaA‚2ÅÕÚZrs@çB®ëîTOÆ/ŽËéTë;CÜÓÙƒÁÇ.NõÎ"*â©KYlÐÐ’ ´æØ["ûã ÷0ÐÐ}馔}|à´õ$u•ïÉZžŒx¯–1”^—G‡XŽ\4wÑ=TÛ¸¦ýN·ÐÈ2¥µ™LÒ¤Gãç«Ø»{qNaá÷Ö•ï§“ÆŽ Z©¬ðï£E%ú-8f!&ï"á÷ìŒ:Üä¦jv¤e(+…Ý Md2×ò¾5ªïˇËÃÑe¼<ÉóøÌõ²û¨_žMŒ<ËÍÍ£Ðr`™NDx£Ðä7ºÚG®iölݼ–ïAÍLn]=’j×5•)€Ó•\ b¹Ê³YhÇ‘:X5˜Žp'òâ!Sþ¿}Ц?ˆÖ ·ª`øšš›¹µ‰ÖO+¹x0ŒÛí?6€Ü°­;>18ˆ[¢µÅPÐÎ:ØÂ˜æænµð:~N6†ÜzäJic«‡ÜxžU‚ ÿ•ärä„»Rôß·ysàÖ„ñƒ·÷`ÀöÒ *[{È»HSWùž¬åI¯‚1tã…oÓÿ>¹ŽÔl°,\õ8JÚKœü(Ë®?¿Bñ’eÊÞíå_ñoIYžèÍø ?{÷Í Ã&L¬üjÃŽ6~=«ž…äR ¶ÆÆÏˉ`½PL$¿ ³;†'¾ZYKÙmÓàäÍe`·ÙÆ÷Fõyÿ‡ø¯™ ªÝ?ÜÒËeMζg VÈF‹ïórs£Ðüo{^; |0è€qäUžºõË—nç˰úRXÛº9õ5—)@t<¹Êàý›©º¡…ª›ÝätÛÉåµs¥K;€ã%SÁ:cùDk FGxS„á‚GÅ-"ÂÀ­‰ ÷K=S FqFÏÁmÚÌFPPöÊäpD~ƒËÈÏø¿ 7,>þ.Zs0¾™\Q„K†פÑó«öOsóò˜ð-št¼|‡M¶ò‹å^Á›ôÆgwЮƒ‹é6…q”Iò*°•ȶ–žŸ¿”ÏÄz/iŠW¾ƒŸxåýiãïåî‹yüCF¿¼NÀ—ÞY%Ê”Õ_|þ†žß“Ä剞Œ€†Ä£`Fí\òá»Ks ò_âãé òÝ0ó4C4-@¬¬ÂÀàãhý¡Æ‰wéºA${ô9@Q¥d:zœ¤Â³úó÷üß„âbOvÈãwÐmBí †où¦†òõkæ®^²p¿Ó´IØ^ÂDûQý>S™Ûñ–«DÈTwÙ+éÁ=0†á$£Ñ'ôA´æ<ò°ˆÖc[«0Xïð~è…¿ï¯cËSVqO¼¾™ªåÉÄá×±\9öVÀ¿¼6Ó·c÷µ†›Ë_[þÙ'+øY¦$ey¢Gã‡Z:ÀÄ\ƒæy/ÿûó®üÇ“§kVVû®9ÿd£}€~…j áÄP=®mB±¡¥'kŸÑ?¦½­5çŸ#%jª¬Ì0‚â¸Íèi¡ÀðÇÃՉ߶õ«ßYðÖëŸðç–¼GëÇÀ8§‹ñ‹¹L1–»ìC«&r%åÄ_ðÇ^¦À›Ö <øuBT0ÍmžÙ’D£ÝŸä½°oíÇþçå=‰ÜKë|à)y°k1ÐVôâ›nÏNCQÁŸ«ß\Ü8ŸOQ–$uy¢7ã‰GA…€›ÍjÛ‡¯þwΤÓgöN;ãæ?¼4/wìÐ~¾)cÆ íñ<@ „Èß—åó÷EÈZ§4zrÏ´ô8Ia VZ|S€‘×{üâ 0çC1 ƒ[~mݲ%ëù6‰cOœ_àÜ^ÂðIЦ¸Éð‹‡\ÅC¦â) R Éšâ‘ïÀ&^yß^¦ûŽV]n(.œƒbЙÙû}ôÃÿ÷'Ó½û÷ˆè.I[žèÕøÉ– lÌrÏXùù‚/7¯X^~òùçñ¸gp!ŸÃ‚à˶[}y¹ƒÍbÑÄïjèBÏ!€=MR`ås¡çòzO÷‡H8˜ÀÎt £·cÓÆEK?|oqKKí ëÛö8nù¥“ñK˜L1æÇ¸Î£•«P =Ç7UJ<¡ùz …¡yzÉ;å3•);7›æe[š¦œáúº0€Y4ä¦{½?þË#ÖêêœI[žèÍø!d üÉh©À剞#Ú Oßüß;|üɰq' *6b”=+;ßj·ç˜Í–ÈCÁð “1¹Ý·¤©¹¾©¡®f÷Ö-[Ë7¬ÙË|Hjd0vµm[ï'pE¿j:¸<™M‘”LI$Ô^!Ð ²Lin¨¯Þ½mó–më×ìáÛQql¶Z5ã¦únÇã™:ùû¿p=üó{é>MÊòDÆOº©8ü 0|²U' 1×¶u«·ð¶ƒ¸›¼Ó&/àÀ†Qƒ¡C‹Æ1=q ƒ\ÓÅåɬФdJ"¡ö îè²¾tmõ1ë")™’H¨½BàXÂ*OžzÔüøC¿wåó"úµÉìûí£Ïš¶>|‡çíc_©ï+z5~@ …•\i#ÏQ¸£eãgã Æ|×(ïÓÁð1›"I\`øàz^ÀÆÆÆOöý7üÓ5Iÿ;`¢d*]%BñŒ€Ô‰.ËFÞôö«Yw|ý›%üà4Þ¸Ááýïoþa9ý¡Û\«‚_¦÷c=?™(œ‚QP¡@‡áƒaxß™ñKe#<$.RXáÒ”9 !Îq]úåå³|)í’ÄKÉTÚe½b¸d™ õ£ÛòdÕç ­—]šõ5KNã~çãðyÜsÿLö ?º³þh7ßÑÕOz6~ ™ŒÎYSG‹/ƒ7iødß Œ^*>fO$`"7`,°À{lø-[|Ì~ )™ @¡dY"õ£Ûòä‘û?ú¬õ"òµ.åòù-}††gy?+ðFèÝøIøPp#S°G {°Ñ“†/]ûü‚V -öÒèáw•:" dª#ê,½e„Ô Y~tYž<|‡sëcÿ0ÝÈѰüý}>úڣϙn}øvÏóÉe²?`) xiÑ¢±Ã&[{²Å'÷üSÊ&)¬ØË ØÈM^KY4` É ‚ì3Mg™ÒRõŠ$Eº€$Ë ìeY‚½¼Ž{éÁÛÿYz¸<%¯ÀB&‰‹ÃOÛIôýøÑ2F·ŸëïH?ý剢H! P$Ýñ³ß3Ñ‹Ú7“Ûõ[=3¡ŒŸžsGѦP(’ƒá¯…ÌßãsDß!Ïÿ»ä×ϘÏÔ+ùÊøé5g] …€B ɸÿN×JsøoI¶ÁàùVœ‘çzÚ+ã§§ÜP´( $GÀd°>ÌÖ¦°(øäÇž3C,)ã§Ç\Q4) $EàÛ›÷ùŒ¬ô ’Ïç}ôÅ"¥®’2~ºÊEŒB@! H~ÌÆ‚ßrßß!p€º÷^§7®”ñÓ[Ž(z …@’#ðÀ­GëÉסõw‡ÞXRÆOo9¢èQ(©€€%óEŽ¿…8ÌhþM}ìyË=±¥ŒŸžrCÑ¢P(R‡oi8b Ã’Ç£«ÖŸ2~2gÔ^! P(4EÀ`0>#_hðù®{æ™>™ò<Ñ{eüêû …€B Exèv÷fm Øã/¹•ÆCWãXI?=ä‚¢A! P¤(ƒáÙk^ïUã(ã—à PŸW()€Ïü^Óõ2çO¿ \Q‡ …€B@! -ßÙº™#¾Æ[Ùõi?äÚ7]Û/Dö6eü"ÃM=¥P(á"` …òVÁ{ºß‰ã(ã—@ðÕ§ …@: ` sñ£ÉzàY?=ä‚¢A! P¤0Üá\ϱ>ÛX,zô…¬âD³«Œ_¢s@}_! P¤8<Ý—õ£ ɦÉç,“ljÚ+ã—(äÕw …@:!` #’]¯Ç«ŒŸCí …€B …ðµ?n–$šSs¢ Ðàû<…„c‡·§àãö«ê(àiC„ãàóÀê l”.… UÊݬ;±Ñ%ƒá–vÉ@ŽD#˜ŒÆO(è­÷?r¢Áh8ŸÍ^ ½ >ÊN4˜êûñEÀK¾òú²ÉÛír:?úד¿YÁxÛ¶Ø(p|YŒõפ±C÷‡ñ†{šl±ZÏ•:e$CV¬ Pï×>ÕKòy}<ÿø#Ë™B­t)àöäw&<Àu2?Ãwï$6 ×UR¸D'ŒÍ°mO½üÁ².Ð_¿þþì¥'û3߄դݼ¥s+PècÏ`lî±Ù¿èS’§tŠQ©#èT¯ý‡«ßá²øþ<úOòÝ=×%ŸOWÆ/áMÏŽw8ÊzëC|Ÿ ßïO5Èpÿ—˜Øðu¸I(‚€|{Àô\>`L”Nõ=u+ëÊd”Í Kì÷ó¦7L†Ý‰†µGÄÇ‘Xã…^ç°Ù2Ÿë×+ßwÃÌÓÒ±¶G¸SóS›>ÅyTÒ§ß ^À\ZyƒÛO¯r‹Œ¯àÙ €…Ò©XÀœï„Nõ-É÷¡lFÍ\‡­K\ˆŒŸÉgQƯ‘¡3•Œô]tÄsŸIµø:AI]:.›k/8ÙÈ#Ìz|ÑEwòvÞ2xƒ !*TB—Úx¶`¡tŠQ)" Sל’ e3Êh~IXºôØ3ö,{ùø(b|ÞÛ""@ǶÚ~óx¯5U«ÍöݱCûùTßñàR¿w‡är”[Xp ߇šj:µþ„.µñìJ§º“õ[8HBÍ÷‡åIñZ§½{µÁðH¤#Fƒ^ݡތŸ¨©^yûw§ò“^z@Ð¥6^­àôàyu«B [PF£¬æ›ºÕ%Ÿ×yŽ| àW?¸­.ásü@ž S¸jŒCÇꌉ¢"Ç0”yÑã½³B›UÖQÅÑÚN‰àX—ôî¢5ÔØììô÷X_t¶ºhó®ƒ]~æ“/7RùÞC]þ« O&£ Át¥á“®š˜ÈY¬øó½]âûQ0e€÷žê”Ëí&äg¤Iº)íÝ=·ew5;4(±)ѺyBYÍ(t«K^Ÿ÷–v¤ sÚ{ÅÐS‚16qèTM $»÷—¬¥ù_m¢†¦2™Œ4´/ºîÂS¨¤ ‡ÞY¸š¬ØD¿»÷ô ðøÕ?Þ¢~%tÓ¥§Ó’5Û7‡ø¡¼l0b†üîÅÁžƒ•ôè sÅ5³ÙDÅyÙtþ´qtò¸!î‹ödîÂUäñzèÎ+Î:æUUõ4÷³U4yÔ@rØ£sóc4(;ÓN£÷¡ë/>•½í8±aÇz~Îgôä¾Aó±âõѲõtúÄÿàçâsÌ1ëýÆÊŠMO•?­!ðë’ŸOÎÁ{Xߨ¸c?½úñW\Áª+бí¬ÉB¦òµŸ?3‡¾÷óhÔ >÷½·x-}ôÅzúý÷¯¡eë·‡­+ß:“”ÞÓÕÁáªz‚1æ9f]Ý“ën‡6í<Èq„û‰÷{<^úËËÑ3O£)£iöÍ7?]A|±N¼Ó r²èÜ“ÇЩ' ïòºÐ5YÝ¥.=úLÆHŸÏ%»¸ÍöÿGÔØ%OñüáØÒ)ž_ïø-LbãQDØk–Þù|µ¬¯Ÿ}">€jšé~I¿é}úÕ·¯J{¶í9DÃËüáÓŽT×ѾCÕtÙôÉ¢öû¿¾¤³N% ÎÞC•dê¢ðÑ·}mº0<‹Wo£Ùó¾ ‰l(­w Mºù²Ó£ˆ­ÞsÀOQ^mßw”^ýhäB±4;K“F–ÑØvnø:»?ž×¸ßr% _@ÞøZ䮀x2þ·‚yü¶ñ~Ü7ì;TEžýñh>ºíòé\1Ñ•›é¹7íšshÌ~Ô»(VoÙÓÁø­Þ¼›& ïO0=Ñ•ãÔvûŸ¯¢b®¨ÆÛømØ~€æ,X0~¨8?ñƒk9â‰öE'»é»WŸK¼J -^µ•^á2jâˆ2Ê좫]k+«ƒå-D—ÜwÊ<æ›Þ¾ÿæÆ yžè½ö9GÄèÞô4j‹/Û@gLIgN-~)ÌÍbuýôï¯Ó"6PgME¥E¹´ŠZ?Û­¢¥c×ât‰¡¤ ›[‹Ý/ˆÖ$j³h5.߸“VrÁ€ÖßOþö:Ýùõ3éO–Óáª:zô»WREe-Í~ÿ ÚÅ­ÆüìLºäŒèD®Q>ùŸhPßbºlÆ$A3Üœ¨qÞrù´lÝváºýæÅÓÄoþ–ëfAïé“:ÖÁ? ýª-» ­Ñ³ƒóN+žû/·_¯ª¬i ÏYÙî¾úlÒ/„A܃?’Ÿ}йF¿vî;,Œ_gÏ749ù{ËèwÍ-l¸ˆþûþnq;éÔ Ãˆ]%ÜšðëÇÞŠ*šÇ-òÜZDabæ‚¥?ãö-Æüþû½%ìñýk/8…3$´ˆ¤œaŸª)˜Ç°[¸ó–¬#Â7]zWðü}ýœ©ìª>Bï/^'Œ¼  °«¬m =•4󌉄–aOtE‚Y*`½<Àž•õÜòÂy}ñé2÷»ñ¿b=Bò‹5åìq9™Æ ëG_¬-çßÖ o<ðäÀP´ºÜôög+iÕæ=ÔÄîÉLÖcvéÐý7\(ºBeÞb2 ƒ½— /–µi†Ò ®èng9]ðýø¯¯1ï}Ïhù^yîTáý~½þñrZÉú±ž2z ÍâJ¶™ß¹fë^úlÅfÏ´~È­bÓ1ãÄ‘\lwØ›¦€q·e˜ÏûŽTÓpž«ªs]“²ÖŸ_h;àÚ}ƒ¬Z ¦gýñå;Ü–°“°•"ŽjZ å‚Ê0júeÛ\9ÅùÙBÙp•c²Ø¸aU[M5½²ÞÅÂÕùÌóiÃö}{Žw°› $[º™ˆPHüí•OXA3„‚x¼^úëËsÿ‹]‰Óh0ž¼ù™pŸÂ-aå–iÅæ]ÔÊŠ6ˆ ƒºÆ®bi-âÖi¡Õ 7, ç6æÁiáÊmb¹Ÿ›fž! '^´j‘ð¸P7ìØ' šÒÂÜàGÇ0`0⨉¶pÐÔ67ngÏ·º\‚O_›ÄÿûÝ%deƒ÷ .˜ŽÖÔ c/_üרaô~Õ^H·Ïš.ö/½»˜ì6 »×Χ¾Åù¢-~ˆþTTMå,z²bò†ó ·þȲÞÃ'©‚þì;ì§ãWÛÐD;öû×&…‘‘ÅHu²4gþ 6pfºu¡ª®Q*|ü°þ“‚VŒò ¾ET[ßDÿœ»ˆN›8Œ¾uåY¢;b!d$ÈêŠM»éþ›.¢/9UÈã-ì-ÉåÊeg2›ã°ÓÄ‘é>6ŽX^ýè+ÂØ€Â\»çK¸2àß=“+ÉHÐc'—)Ho-XI_nØ)*”çŸ2–¾àŠ)º  p!/]·ƒP(ë]È:´LTÄ !ðÎÕlD¯ÞÊä|Wü•ÑÎèÖ‘®uªK]{¯äbaQÛÜõÀí?ÑÅ(O »Ÿ¤M“}»8‘²2mǼÏa·Qmc“¸>iTa°È®G„bídÅžÌ Ý€Ý|1·ÌÜúú˜þôß…ˆ;ùƒò»¾GOýïÀ?nXßÀ]hÍÝ1ëLѺ+ß{X„+Ι"Îo`EÅb½“ØÀ@ÁQûDZ¹i—ècíkÃàÔv¿yÑ4Ñ;û¤1oá`}ù^QóìU˜Í5È^¢…‹–¨L-N7+ýEtöÔÑ]ö~À­³—?XÊ}¦i×~Kòsäã¬È]?_Í -jÂ(0eKU>¼e·ßÍl³Zx™ª^\à´ŠBƒuP°žvÂîg´ÒÉã‡Z‰•l<£K>ÎÉtMáñ^×ØÌºrl_1t-:´tzs¼TxJàêDB —'*Š=Õ•àÜ€ì_Çr|¹SÆ£Í;+Dk -5´¤ Ø!ºty]ù>îs\ÜRåÈ5ÜÒóÓ³•+ký¸o07+“+½~Ý ¸*³héž=u”¨ ÚŸûÓ½âÛèÛÏço ß…»74}±v;{„Ñ9¬wØ çÁ•VÜÙŒ‰„òå²é“}†¨Lv–М=o™h…BïO›8¼Cßz(ÝÁïЛ®ù|°mñþHÒÈÛÿ¡‡¨.’ìõäöŒIÁFªçšeh‚+J‚Ô‡[hù¬awg. ¼¬ÉÊgPkD'?j•¨¢äÁ›/‘?wØÃ­7r`oºê<ÔöŠÄoÒÕ76hâ~ 7¤þAøý{åS×,áêاˆz—h‚9š„&éþ\8È4 ´ýJ¼•×·ƒ"-߸KÞB-*j¶Çë¼çÚó„µñ?üë}Ñ »}Ö ñŠîž—-ƒmô¡p+ä·L£÷¦ùË7‰AF WnF¿mÙ…BÏ'jöò^<»ïpM‡çåoQîc"wQÒíãó”'ZGÇŽn`OƒÝ–ÄOɲõ;Ä ŒíûÑù§Œ ÐÜ] <ÄÁ²  C 9„&ïß´ó€è»ÿÍ ïÊK~g6ŸÜW¸Úár”Ô‚ç„ÊìºmûØ¥»†qW„ìÒp{Ž?ªý›¨¨÷CBq ¿É„R–Ã_©pr‹°³„î˜_Ý}…à.Ó§_û”,÷0œH¡t¿C'º»ÇžûåMœuãÛhl0Û3Ÿ#j&9áÇz2~àñ^-5ô óìk·w%j§2¡Ï ­’sNò÷â:jmk¶íawGv &+ï—{ôÇÕ6´ˆ¨Å¡¶š.å^W#Ø‚G@ÂíŠTÁ´Èþ¬C<ªm,·®¦ŒÂ}ÙåšE½ ýÆPüôµQ¸ZdB P&ôÙ Æ÷nf>†ô+e÷çø4Xf¹€--¸uï»ñ"QI¾ÿO¯„¾²ÓsèZ‡¸ß^&”+(oð›L=cÑ¥ sçþÃãL·|·Ü'N×DY-eMó»õr´6þ•¤ýßwCƒß…%/ê`lÉ­¢´$Bˆ‚uûÑ?\ºžšÔ‹Öг܇V!aÈ4‰jŠõ)]žøm »—²`Ãý¶vënv‘ätjøä»ÂÙ£•× :ÃA×§<£†§* 3iê˜l ¸Oa;M;¤ÓWX*ܘŠcŽZgpÂHÓeü¿àûº;Æ œÝŠÎû5Ûö²Aò·f»{¿¡5í°‰Gh5~ÌSEP›— üå¾ML}€òbd!Ò`îç€kë=®ÃÕÙêò _•b*$õìú|}ÏÜg}€\`°ú–/:mB€é)Á!éòıÒxeÐ%×+\˜ø&ú1ýó Q©’óG7°¬4rŸ¸ÁÕÖSW -¼:îú@‹c>\º®Ã­ø.d•ÊÎæÎŽåѯW&*q8?l@‡w„{âæéK(_à­Aß ä~{ÂIzÒµÖæ#÷1ÍbÛñTú‡pxˆ÷=áWµâM™†ß»èÔñì;7ÒGK7ˆ‘–0ˆècúÖ×Ï# å§à¾€»±Ž[vÁ5Y—Û+F%¢†!j‰èÇŠ6¡ø]>þâÛŸÓÏžz]Ôô.æfÂp¿òdsGühvá`ÍmnÆÐob` jáÿzg±ÿùÓOà,í†âëÜŸˆÁ#Or?%\Hp]oïpÓ sŠ[FÆ¢0܄Ѫ¯|ð%\ÛÁý3E߉|ïà ¸x`˜að0Òs0ï¼bÓ½„~Ì#d‘/c¾5Kô)ÉçÕ^{Пwïuˆ»¿á9«hAV 3#Ø•œà)A@…àŠb¬te:{/fÏ[J÷þþ?„¾ñi\i…l½ÍƒK^åiHh-a3d²‚þ1Œ˜†ÌC÷á–½túÄ`òÅ1tð<þí®€bäèôÉÃEwƒ¼F#ÂúË«b¤ëwxDtpºæ¼“è…·Š‘Øvu¢ñÊs"+0æà×Ï¿-ZuŽÑ´=™K¨]C(3·»ÆO$zçüý;ò¢Nöh®F•îøÉ/àÏþê§3°"¡Úoç-çæû~òÖè¡e'þàú ¢x]ç6ó  ¸%:ëGèü‰ö«¨‰™L†cFµßùj´0¬0IBí-§Ð1ò]p9"*.Dú ù®žîdÀåöˆ‘|òÙ5[÷иEñàM‹!î¨Í?óún ÛÅbyŸŒ¤>ØhÒ/Í£5·mú÷Ÿÿ¿§‚·JÞÐù‰Qí4|’) KÌ üÆ¥ßüÞýš0zبžè”›óÌÃy×w¤Ä.ºCÜÌ-ºÐyoèÏÇÈ`TjáY¸ÿÉÿѵÂp Æï3ž§ˆJÖ“?úf—²^Q&»z%/Ø7qß*Ž]•èメ îÖ~>^ÇñÔ5èÔÆòÝË_üݯ.cþ„.ýúYã?ˆ›À/eëºãÿNèÉ@ í Hè6¥EË/p¤©;Ÿ{¤ï”ÏÁ G“ŽW@A©;ñÍ7Ã}¹3ú¼ÜÚ«æA?(40Çj×£bÊEð{£5zÁïRÇ=CóB#- b¡+¨´…>p׺L(ü!oÌVÍ]p®eÏæÜvWé¯Ý¥Î¾|p_ðõx'R×~ùwÓÅäóÞ$yæy}÷õÄðÉçâµT¶ãEŸúNŠ"×n:}÷sî׫­ý¯¡u:ïVI!)ðžÜÊ –òD¦Á[ôûÇsðÒ5ÅC×®ÿŽ­Ødr>Çs‘xjëÞáþ@Ϙ+ã§çÜIqÚ0)›J -@?96•Úˆµ®ã|Š ŸF>Ñ~›)ë[~Oh; z;JùÑžz\Ñ£P(R 3Þdà1Eà‰]Ë>î8½Q/Ëu‡³2~Ý¡Áoèc¸û7ÿâŽùÖžŽÍ#ºþîœîl‰™·9<&쫤H5¢ÑÅ÷9ÆéÀ R÷X­¶œßOÛï2üñÇ·¹?i?×ï‘2~ç o`ؾžùLåU:›lëôJO½ž¨V´(¢C ]D?!æ›/a¾ðÁ#5Ç»-Eo¡ÒÒ7†p[OŒ8Âèά~ƒNfUŸ_Œrj=ÏÍûŠÞbNà©££ß³:ÛÌ¡™0aÁžƒ~Ïå‰÷˜,‹!݈ӉH1h¹aÒ6`òš_˜³Ô‡çc!ÒýFžÐ‹ Ö˜ GËù›ò½˜˜Œ ôWðœÄM;÷‹ØŸ0€˜¸Œ9ˆc Zš…í ­åIì_òzlƒûõâøŠƒE¬QL\Fè'L¥Ow]yf‡t㦎Ý!‰.îá <ƒCÐKD“ÙÌa÷’ðÄÑE,^„ä[³e/ëªKè)l¥Kòù<Ä““¸ü¨Æô4Lkpºæë﹨ܙ,¨–_ŒrjŦ]"Våb^‚e./¯‚„¡üp1" VhÂïX¡G~ÁÚe¿yáá>ÅJ P´W>X&&‰cm½ß^$ ŸÈë®-\¹•^ãx£}Šòi1GzA$|ó°ÄJŽr¹~˜õèóï"ÙêWÌÆlG€ D\™·dXve7GÓ˜3ßO7ÜGˆ^¿”ncB}¢ç2V 0ˆT÷·­b½D Š^¼”ÙH®Ìb±fè0b‘Â˃ÀÖå–NéÃ/ ¾ –›wýì×úÀ…$8P-¿eBA¡Õ†ÉÛXOPF D¸¤ë9BB†UÖúcq":=";œÖ¶z3–'Bm³_¯<¡d¨Y"`6úîP›ýñ-3E„ýLvk>ÏQX®æ•å±v ZŸâI"÷—œ&Þ+ÿ U‰9w²–ºÏ[ø}Hk8:?âšî©¨æ¹SV±‹\³ “‡¿}ÕÙ¢6,ߥö d@ ]D0y¬©‰—ghž¹èû¼jý¶½‡è„¶HLJ‹D…5ô™T=_¶ñYZµõ?öë xôGžW’ä@µüb”Qr ?;O^Ç„[™Êz·¯º€%КÂ&Sp¿úêšÛW£(ccŠ„•è‘F)‹ƒFc€ ZuÓ& ®I,u„ ¯c†ô÷È?X0;hy'Ä4” Î © 7‡c9žÀÐý¿"dÜ@*)’ ptn}©‹Ð¯ÐTÊåeÊàhÐŽiëžy4ů¬76 ¯|ôGÞ'’è@µüb”Y+6í¤s¹Ïn%/ô‰~¿Îbd†ÆÉ,ç ¶Xǯž &úæF󳔜°ì â~b5|ãK^Z±HѢÊnvobÅõSxMÀÐpLh9bPÐ…û«¨cm$ôeà›§ó¡0ÂX°T%…@2#Ž.bÉ£àeBùí,: *Šˆ@„ÀØDù¡¤ÜùÁ£«éíE÷0_þòÈ@C©¢bÖ.¢ß$%¯ÊøÅ ÛF î·þúšX­àªsO û+¥¼DÑ Ü¿€þ†‹9H5Ö¿ël†»xõêßú\¬C6r`±J;>cw2½x`LgßEÇýLðûä?àÎjâg±’<–>šËËÔüô©7Dÿ & cl•ÉŠ@4ºx<žaÏ„Æ#Ä"¤Xƒd#Qº`Oå>Hl*)’ ht1XWƒÁ?ôN¦Kye‰ NÀºšº’ƒGװỎ+ôˆ]Í«XóéʳþIÿï­-|vT\KÆ?í%s2R¯sš¥ñŠ„ÌpŸ 5|á~+Ôð?‡ß¤á ¾®ŽÉŠ@¸ú ˜ÆÔÕj‘¼OOÏÀÕùòÇßè`ø¾qî+T“ü£[UËOG’6žG)£££ Q¤(Ò¿á㟫^ i+ kÏy™=I#Seüt”XT%…€B@!hYM¯ÀÕlø¸ÅWœ7"Ѥiö}eü4ƒR½H! P$?;,¤7ÞÉS§üsE‹/Å rI¿ä—UÅB@! Ð5å/Ó¼¥ñü^x_ª>0§ŒŸ&"£^¢P(’…«GKÖý%ÀDŽ£/]}öKõihàZ*(ã’›9¶ƒEWqرžäùzÑ& ‚Îàè(yY™,HŽºÒOÌö½Z?¿‡Ã;mرŸC®5ˆ¾­m¼G;‘'Yø×Oõ>¢t—©d(O¼^½»äG´a监--GWžùÿÈa/\Kµƒ´7~XöÄår‚:ÏÿjÕ6:9B¹2=N²·ÖS†G›uùZM´3#›šLVzë³U”˱3Ï:i,uâ¨++Ä[À& ¼Ìa¬wÍFL2"V»Fɢú<>r{=‚ÿœ,»ˆn“hþ5bO½&t–©àòäÓåÐ).O >Ö©f2škY£ÚCAÖãCYÉëÎe}²·é“•õ©çåIKk-½±àvÚshi€†¡ýΦËNÿ;° 6®§ÚAÚ?)âó¡•7û½%TÝØBýë÷Фª-4 ~'YÝÚi¨À8ÍVÚ“=ˆÊ FЛŸ:i½¾Ž]£5Š,ôY­Ï7ppì—x ,Óbã0M…î ñDÅrÒèãÕ†8¤Í­Ìÿ šÿå&úæÅ§$„XR¯ B ]e*¸<ùïû‹ÙkÄ•g{9®áýØ£7®¨½^57àøÁDy2Ÿ+±ß¼8¼ò¤²v;¾Û¨²n{€ŽI#n s§ü‚Ë¢ÔŸž–ÆFËû þ%Za-•4sÏêÝx ±:€QV½Yl}hqÿ3éoÿû„fu¢h ÉÀÒ±ú¾|/Všxý“å<¯ÐD½8ho¼‚Vð:876¸”«ëš˜ÿO™ÿÉqå_â öÚ!®2ZždXSiÉ\²Ùvkno‚Qu8Öˆ­¥¥Œªkf²>µ·<‹sÞÒÉånâ}æä‡é¤ÑßêâK©w9­ŒŸ¬Á-Ã÷öÂ54¸¦œfìù˜Ìì÷Žw‚±½|ëÿhÁ€sذöÒ9SLj•bÕ àýµ—‹…j ¸µ—¨ÈL0¸%¼ÄSUMƒ0Äñà?ÞyœßKW™ê¬½$Ð’ß\E/¿ÿûð[c²^Fob¥x¸:ÑâÓS=³‰fójõ±â_Oü¦ -é*SÁåÉ«}É:uH´øô’¯h}ZØý:ûy²qç\zñÝ épõ¦‰c^N7]ünJEm 0ÆAZ?*\ÎÖVZ°|Õ²q™Æ}|‰puv•' åÔ½ó©†×ñûtùFA/èÖ*á]ÕYÃ#:±€m¢\]ñzòs2ÅB½±à¿«ïªë‘#®2ZžÔ5µR~Þ; mñ…æ"Z€ysYŸœôÜœ¿Ð›Ÿ}‡Ë?Ä8¿àäÇhæéæ gûbÖ¡ïHõó”7~pO`pK+¾f^})/‹QñÜÒSáM íó•[½ ôG›$Ÿ²áǨÎx né)Ý  ô-\µUSþ{J‡ºÿø¤«LI¾eyòÅúíbTg<·?W:Þš0âtÛ^yÜ<¦€ëÒyYéÆ Þ¢†}£ãÍix–òÆÏ_Ks‹Vߎ½‡¨Á馡½&ІIö›vàÀ².Ñj‘&‰Á†ûÄvÌãÓs}˜f¨ÿzæ5YiKW™’|C/å O`Ç<>½&AÓØ+ÿf:oêïyÑ]»&Þ$½òÛºÒÀøyx ‹K¸<«y2·Ýݳ ì=¾«{14"ÄèöðªêÑ?UòtGn‰åö®xêÉuÐ:µâ¿'ßV÷†‡€ß¤ŸLI¾ƒË“±)fØÃËîïÂ<@3Óh1Ô¤<éþkÉõkJ?¸(`<0"­Åé¤ú¦Êt5è>‡@c-JÝ |Dš$ˆSjÔÛ(—.˜ZñßÅ'Ôå(HW™’|—'FSmHÆçQШôéX¬SÜøq,É6ãçt¶r4f±:…R»+ˆ'ÚÚêÆô{½‘»=ñ,Þáä÷é½Õ'Zñ/ß©öÚ!®2%ù†ñ“å‰Ñ Mì_írçØ7F¥Oàrì¥Ô¸…tS`àFgEcDâŠF‹é–nOðÒÓŒA$Ï÷ô{ZÞ¯ÿZÒ£ÞåG ]e*˜ït-ORMR¼åçw{€`ž[äÉ?(èÖÊí™tÆO#þ“&ÓˆPéþK7™’|§sy’Dbz\RSÖøÉšìæËyÙ$éc;Íÿüóü`³ý­ØãæfÈ ò9`L†lhÁêTÒU¦‚ùN×òDñÑÕ+RÖøå€ÀrQŠZΓ&1©0’‡Hé–Ï'ç~V5â?RÜÔs]#®2ÕÎwú–']KEòý’ÒÆÙØ>ƒÅ“\‡±’´Gû¼'Ù’–ü'ïÉ@o¬e ‘~´NZÈ”ÔÉXó¯GÞµ¦)ÑïK«%¢{ÈýOPó]tàß&[¿ÁÔÿ¶(kø8r×ÕÐÑséàËOQÑ9_£â ®¦M÷] ëí'uõü•çL¡£µ<Ï@SÇ >†¶·¬â¿Fš=ûÖ .=œn¹äÃ<½Î Í8ºŠ¾uù"^a&­I-þ;0›€“”5~Z×̲FL ìñSi÷Ó¿ÙÔûª;ÉÇ“Ð×Üz.e GCú7ª^òUΟK¥WÜF§_HU ßÓ,K#á'’gº"¸_IJÅôî¢5”a6Óæ·ö-Σ³OÊZ-SŸzÂp7´­+߸'Ú-y‰––t~^Ë|8mÂpÚËF †ëWÞÈ+×ràõw­æðvGó[q¬×«Ï;‰VlÞEM-ÚM-è /=¹7ù0-tÆÄûhÑš'¸Žì%»5_<6û£k¸¯ßÏãѺržŸ\CkÊ_¦¤×æßΫúGk~Âú¨ÎnJY㜑Áí[tÈ÷šu U.x‡[yÕâEæœúÎÝügK Õòj‘¹ýÄ|ò¨2š=o© gÌà¾bI«Wxy Œ¦ N{UÑÑêš`ßÓülwøÜ˜A—qä+­ßñ¦¸ãçö´ÐîŠ%îÃɲ OÓÝW,K©‰<”Z0ýÇ|$ /¤|ŸŸVyšY6œšw¶ ^Å/PÑ™—Rÿ[ á ÆmëÄçšvðê }Ê(VýZñÔ“÷ô*È¡C•uÇ<’ã°ÓÈ¥´bÓÎÀo•5T˜›E&“)pM(‚(âõF#UTùeªi!í;\M'ŽHp¯Ö¿ƒüd™*eL•Tœ?Š×lâʹßÐÛ­¢xÑ)¿£3N¸õgh€Õú¦ƒÔÌ-À’|:ŠÊø…¢!ÃF–Âj9¸'pwã–5Ô´c#;‹2JzSåGoúøZ*ø>vãXKûîOæ3±l‡ªêŽ 7yô@:©|ïá‹Um-@¬Ï§’B 3 rÔÔìäÈC.ñsN¦q__¯Â\^pµžÎž:†¦Ox´º¾‘ rôµs€¸ ²³>µWÑŸ·ëà"1Â{Ä€ éöK?¥aýÏ ¼¹šïÍÏ8WÑ#ÒnÏèáñ¿!ƒ Rkå!ÿþ;øþ?—êº;/¤BîßúÓ¿Ó¶ŸßE ›V’«êˆ0„…½¨eïöÀ3ÉzÆ©Ž—D Nf^y}âð2Z²–ݾA K'!år«ð(d‘¤’‚\ºê¬ TXO9ÙY9ßÖ¡%ÐÓw>ñÒ=÷ëÊää~Exd½©$OõM¬'¥’½cöGk˹¥7(p=›ïÅ3*i‡€2~a`émi­9[ï2q·§±ž»l£¼©g’Ñf'û€a”9`(5lX.~·–û–ƒ{Ãx»þoq¹<¼Àl ÷C8:‹.vàiGàÁ¥…TUÛ>(¦Ãƒê$í¨â%»™V1¥`”ï;D¥E¹4qDççp«¯?mÚu €\ž•<Í&URUÝ*Ì`gDÙ…Ô»heÚ hÄ€ hxÿói÷ÁÅßó³ñ€²su=Êí&†M»¶’}ðH¢OýìéObJÄ?åÖ_ Uò´†š¯ˆƒG‘óÀnòµvt†ù)]Þ†Á.è‘iH¿1¨å­~7•¼Ž}o¾…V“PI!И/Š‘£•Ý•´ëÀQúdÙš>i„0ˆ›v$Œ–©waí8ÐÞ¯,¯'ëþpõ=p& 0‰A/ùYéœÉ?ç¾õRžK»–oy‘–®ÿ»`/;³·0Їªý.àdåYot+ãfŽT¼ö, û¿§éàkÏ‘»¦’êV!6£ÕN^g»{Ï`Π’K®#ÇTJŸ¯ÙB×]0ç%m¥Æ&§˜ÚÐÙ°yà›:v}ú•RÔTÊ­y·àË;é” C…ñÃû—mØ!6¸;ƒ+Ne<´0ÏA¯|Ün µ¦'ÞïÛ°sMw÷ë])æñ-Ýða˰dQkÈš£§Œ½›¶î™GGª7Ǜ̔þžr{†™½ÛÖSÕ¢yT|Öåž6|ø!ÿ”s¨yߪZ<¯Ã}É~ràp »8÷Ñ Ãü.Ý®ø5¨7©©ç{÷wu‹º®,^Í]Y™T2…!ØðáÆI£ÒÂU[¨Yà î‰Î¯×MŸ®ø%z5“bjø2m…4¸ïtš¿òÑÀ=ê@T˯8îyúWǽ»êó÷[*¦÷­=.[ë·ï'l*)އ¦9<ýúüãÝFoÎ_qÜ{’ñŒä”£9»¢¿©¥’ž~óô®~V×£@ ¥[~™Ô¾Oªåé &íæF§‚F‹ÉD{{ͰÓº¹(ù·ð´o´a)ºùŽ–?N­ø×’.õ.?é*S’oÿÞ_žx}ú/O|>«Ò§N”7e44YÙ 4±O]ï 4Ú2Ú‡yƒÞÎø9ÁÏ88ˆ°—çN%CZ🠼&é*SÁ|Ëu’))kü:á•ì5›mä4G°³wj} ´FG†¶éVîáºzoý>Щ5ÿZç“zÏ Os™Byâñeò¨ÕŽó_õ$ ÍãÍTúÔI¦¤¼ñCMn ‡2О3£×Ú@ci¾#@w´´‚ÿ²Ò|RÍáLt>hôN-ù?õü±¤«Lɲ{Qžø ñ¦= Û±H%ö hó1JŸŽÍ‡”6~Aec‚ º9v e}T^ _amVöxæØ™êvÃ}lÖ…wEbP„mÜšllî8!=¼·Äï.ЗÁQc´â?~”§Ï—ÒU¦$ßÐKYžXL^1A·™ÚàDRútl¥¬ñ *wý™Ø7oä¹CØ;Ì´7{äØzzK  ´ (tèEץ䥧ôÊçðð>ª_ˆÆ"ƒ ÷ô}±¾taþWY]þcMo:¾?]e*”oYž”dZ¸å7”ZZÊt'  ´•hSžèŽÁ( JYã\P;ò:Ø„ÙÌ‹°öÉæÖŸÁK‹úÏ 7/(©—Z3MVn™.ÉôJÚÁG¤)ƒÑeÅ?Ñ$ÂŽyÙµ¨§zª8êG†É )ÿzâ1UhIW™ æ;¸<±½TY=“ûÓõSž€–*¦)ämy’*2 >"/UuŽ‚¬©ù Ÿ™ÝÌà ûúd¨š'.pŽn¸-ULÓÐb;Ùl 3CkÐ/yé)±ò9‰Ýf¥‰ ÈåvSUͱËõôýZÞ_Åã]n +²jÆ¿–ô©wùHW™ å;¸<éë0ËUL•GgéFL*+gq¤˜Ö'íÊÝ0§!z3~hðfФ]b4Ddx=«5ƒ¬lP¬V+ågš©Øê¦yCéã&´ˆh-ý²Ô‹Ã8ùéôk„z‘¦P úçÒ°^K­Å)–Jt ßDzGM<Ð%üw‚[›ŒAÎÄÖÉ-)qIò'÷š1•®2ÊwÇòÄÃýécèÈ‘«ÚD‹ïÈÑ«9á˜8é“(«5—1Í„µ›i;ž¾›…ñÉãó4µ¸\8¼Ô燥›ÆÏÆFÏÆkÂa]¸æf•dºxˆr«0:5¶<:uïgÔ»±=мŸ’ØþEܯh…–fú¨>¯ÁôNÐ ºÑj‘¦Î0Þ'kª.ÚUÝJn^!;ŸWa°ò·âÐÇW'Z|±â_òÔÒêò¹Ýîö ¬ò‡Ô4€]’lº=îf-t ïKW™êŒïÐò¤²y4¹QAÞ;ìÁØ-áË}|p¿¢k}CÐ)N¼ÔÆ1rÆ#üˆžŒ_€¯Ës¸¦¶1jãWŒ‡…× ƒ:™ääÓéä×EÅžF²P ¯jšOs‡]Aýë÷ÐЪ-4 ~'YÝÎ=Z`¦3`T'·XxÕþ™nnññ\^¬3‹i tK·g¤4t…ÁÐÒùZC;«Ý„lÜ2vØ3ÈΓá!£„y|˜Î€QÜbæŠcÿLOÌø—lÔÔ5ú\Ngµ}à] Xé*SÝñÝ¡½x”'RY¤N¡¬æk³¤Kz3~BYíÛ³(§°ð{ëÊ÷ÓIcG*\å ÿ‡7@¦ô‚nÐm:0°%¼Z{}‹›ØÖ·rÀãFÀŸ¸±U•߆(ˆfi›e‰/ÿ#gäÞm[V3 ¨ñ9kÛó.%S0^ðÞwЫµÐ) •®2u<¾ñ»ÉÔIyÂ}qnvG¯< G;ê—'qÖ'Ð(u e5ŸJY ‡|ÝÜ£'ã'ô-xçuCÇŸPùÕ†lüº·>Ç2P[cc"Bg±á“…¹_Ù-jfç§³… £›2x$d6€qäó8o'Ú¿pvàž¾ß¼'pz:ð=©<‹™]pÅÚ ->¾¼Ü\q ƒˆÑdѶú$ áb‘ÑB9Œ÷ þÃÅ@~§»}"ùg9òy=žºu_}QÎ4b…]lyëŽî$ý-˜7Á/xŸzöùµŒEN´:LÒU¦Âå;Òò$\yK¤>Fè§*”Õ|,oá²ðûôdüjåBY<0{½Áðm{*hØ€Ò¨€’FÎÊ®ŸÃoÐP{B\‹ÚÌ-á»çÂßãõ· ½mC!»3‚vs»mF‹-4AH‘0R t˜Œèƒä–(Óßêã>>vu¢Å'ÜžV_t]Béˆ%¡ß >O4ÿŸõåû {Ê·}Àtq»^lÒBÞR5t‰|žœ}•:ÐÒU¦Å70O´>©S(£ù4iuIOÆOÖ¦{á[oÌžuçÝ×Ξ·4ÿÁ›g3xrv4  £ƒ$kMP"¦@pÿZ ¿ „ikùxxepZ>5ì¾”)?¯óïò{hÉÁ•éoõùGŸÂ`Âí™™iÜøôjb…ÁñèLÿ­.±üðº¡îºÅï½ý!Ó‰l03)s|˜RIò%t‰9|ƒC†žÇ˜äh¡S@,ÝdJJI¢øÆ÷¥Oø¶Ô)æY2š/%­.éÉø[ÔVfkUÕáú-«WŸ¥…Nñ{„|k­WxoW)2Õ-ñÖ%Ð(ÞƒujÿÎòçQ639I­Kz3~p×@a1¿ÊÚøÞÿ9û‚k®7¬¬ö]sþÉF-ú!D"b »?…ÑãA.~Ãç#G„vgür-íÆª° ŸÉì˜ð$5¢ŸÃúÏýaä½ß›3?=Ú`Ð…’§xñþ¸:ÑâÛ¾aí[ŸÎyuÓ‡‰¸hùa¹‚|AÎ o©šŽÑ%f ÏY?}“1áÑ÷||©V:SU¦À[w)^|ƒ†x뾬S{·oýç¼—_‚»3éuI¯ÆO´ü`XVû•)gž{Ð;uÚ]xi^ÞØ¡ý|SÆ 6ŒÚ7ây€".\‘h…a€‹ìç nñugüA^0h%4IaÅ^nø¦#¯…>s|[ º£ß@’|b¯5ÿ˜s„¡×†Á-^·«~ùÂù³×|ñ9¦6@†àžÁ[pm5Œ_]bþÅŒT *UTœxƙײNåj¡Sün‘Ï© Sà¥')ºzâ¡OøÎ±:å®[ûå’§¾šÿÑBþ9%tIoƪ#×Å@IDAT¸Ë~?ôÍ v¸[&}ÑÆ/—mžváÅ3¹?î\.ärY|Ùv«//×a°Y‚Fžð‘¦PCzúÞ<\¦§_ÿLvØK•CÏåu½ìCy=ï)¡ü†ž÷ô}ò~„ëBÔLÞf »~׿ ¿øà½ùÍÍÒàÕòýØêxƒfS¤.u‰†‘+ .n¾Ëç7q`Ùð‘#íYYV»=×lâX|*¥\ñq»]ÎÖæ–Úæ¦†£{¶mY¿yõŠ­ @Jê’„-¸Æ* Z°";9SÖð¶™ï-CÞÃPŠ-ïå3|Ûôƒ‹Kþ*¿0ûoOæHãYÁ&ûŠÑ²ƒ‘“®´ø`Ó­ÕÇ,‹t\]â»ì¼Y¹ò°‘·-|œ]Ôª?‰@ -uI¯Æ¥E- ™#ÏQ¸¡f…E>Œø@ J7ÃÇß Mi89ˆ8K¹€áƒ+S¶úÄ`)>‡ljÛ 7ør”nIêø–˜ =ëRºåQ¢ù•r‘6º¤gã'3J| …E&F®ñ^tàó>Ôø%Â&m„sÆ/™äIÊ…TX¸4eˆJÎq]öóÉgùRÚ$‰S2éRÚdN‚•ú e$mtIÏÆ2 AfgŒ¬±¢Ålødß Œ^" –*ðG¥¸ ™d$Xi!#0xØcÃoéØâc¶)Ùt)@¸:ˆ9R¤Œ¤….éÝøÉ\GÁ…ŒÁ^F­®N=iøâÞçÇßN•Á'ê8¦@¤lH(WžËßýw«¿½ë’Ê©ø!–º”,Æb k%RiQ£—ó²µ'[|r?ññ ƒ+TŠRa±—dCnòZ|¨I®¯Tô¬KÉ…hrS y@’:ƒ½Ô#ìåuÜ“2)™ŒŸ]f2I>ÿYâ\žø>ú™TŠ™¤\Èsµ?>3=êÒñ©Wwh‰@ÚéR2¿Ð — z=ç¨M«¤HVô¤KÉŠ¡¢;I}dIB®"S! P(Ñ# Œ_ôª7( …@’! Œ_’e˜"W! P(¢G@¿è1ToP( $C@¿$Ë0E®B@! PD€2~Ñc¨Þ P(I†€2~I–aŠ\…€B@! ˆeü¢ÇP½A! P(’ eü’,ù …€B@!=ÊøE¡zƒB@! P$Êø%Y†)r …€B z”ñ‹Cõ…€B@! H2”ñK² Sä* …@ô(ã=†ê …€B@!d(ã—d¦ÈU( èPÆ/z Õ …€B ÉPÆ/É2L‘«P(Ñ# Œ_ôª7( …@’! Œ_’e˜"W! P(¢G@¿è1ToP( $C@¿$Ë0E®B@! PD€2~Ñc¨Þ P(I†€2~I–aŠ\…€B@! ˆeü¢ÇP½A! P(’ s’Ñsrïúéo&{<î È`à3øúˆ²»ûèÜúýŸïøIß“Q½Ág8À—w›Œ¦žúåC+BnQ§ …€B@!'”ñ#2|ÿûOØêl ß5é^·×ÝÛ` ŸÍÕèu¸ žÖ°[ǽë÷Oï*ßZMÞF³Ã×bÉ4ò7~}ÇqÐë3ü±_F¿¿<òÈÍ-]=§®+ …€ö¤³ñãFoºï'ç7ZêŸ3Œ}ú×ïñ ©ÚBêw¬n§Ic¸…uš­´'{m/Qº'{ÀãZ÷Ü{ëƒÜúüc|Àßã¢J …€B@!kÂnÕÄš8¿|›oyðg?°dXçæÿö®NŠ"{¿î ;›Ù%ì.a ‚HP$™æ¦égÌ=E‚®²³§§z§wžÿSôL€9 FdAA@r»„ÍyfºÿßëÝfsšÙí™}µ¿Þî®®ð꫚úúU½ª.ËN³ñtîæ•~9ëÄ4q8m΃óâ<;”æ$©6ûÂqS»™¶×úÞ’° µ!ÐÞ:[ÖöX£s\ÿÀÔ‰6›ã‰Ãr7)ox×–RÄÓq­ë8ÏK6¾c;,w#êAyúæÉ2²|,§8A@ !ОÈÏæŽŽ«î¸ï<‡#b&ˆO?cÛ'Š]s Þ†“å¼Y–EQÔ'®»ò¹ˆÅõ"Ø0|Bf!ОÈËê<øØøØØØ—Kè§îøÒ2ò$”Ô#œ¯œyæØÈÚžê¦YW" ‚€ Ð\ÚKËåd㞈£N?õv]µ%Ÿ¸ó[[[j|Õ+Œe9i×b›®¨)݆~g¥¼í¥~ªÃ!÷‚€ öй²vÇóhN‘QÑQ·¤Âª³-æøªI–‰esF8﨔WæÿMž ‚€ Ð Úùù´¾3/¹âXEµuÆrË wV¯3C6EMºøº Çàk«í¡ŽªÃ ÷‚€ pïXýµ>Wb—¤3à¡c_PAmIâ,ËÓ!þ,¤ÃÚªh-Tâ ‚€ P í…ü({¤j·§ºÜÅz0×ñÕ‚q“¼X6ȨAC퉈,·_“”À‚€ 4Œ@¸“—ÏœïsÙö.QîBËyšÕUŦ¤àÞÔü½žÌ¢ËYVA Ü;U.Ï›1‰DØ5&Bs[žüXF•T^îÀr˼@'‚@ gòc’ãƒ5?ƒu=tŽëºn·9ìiyÒdÔ´A@&áL~Œ—É£’õ"CVSöp¯'®+q‚€ ´áÜ©ššŸ©ýÙBLó3åö/G«5 ÉHpF œÉëÍŸ8B±¬þò‡s;”² ‚€ Ъ„"!4  ˆbg›” 9Sö e ‚€ ÐhäçW¯-›ó;ìg¨ë5wéE¦ö£ÞgÓÑo|Kƒ_üN8Ûðïtæ%4à©· s2o‰A@°"áL~eŸ˜þC(ö¨chßGoõ˜rŲEFÓÚ‰Wо…ÿ¥^w¤‘«[o:øÍ²EÇRâÉ纾Zž@ 'é ‚€ J„3ùq=˜Ã†æ¹Ùu“tétpñBòäçEñÃN¤ýŸ½Ce{wоOÞ¢òY7äXÒ½Úÿñ[”|éMÍÎˈX±,Ô[ˆ¯ehJlA@ª îäW¥°-¹‰êy8•l]o$¡»=¤{¼d‹âuè`X»ƒÔˆHŠèÞǸ/Þò¹ºö$ÅaÜË?A@k!À‹¿Å5€€ât‘£c*…–ÇN÷”SÞ¯?P·?ßI.^ÌÀa¤º"‰¼^ãyi&ÂaÎ/"¹;•îÜløÉ?A@ë š_#ê âcW~0ËzûËRÖ‡¯ÞÏÞ5†?ÝÙûŒçîìý`Hœ“|áåBAÀ:ù5¢.Ü9 38gbg_h­¤s}oÓ–§þJù¿/¥ÈÔèpý*ã¹#¡“¡ù™ñ|‘äBAÀù5¢´ÒbmΕÂ_ªp*,=y®Ï•Ú—úÜ;‹J¶o¢ÂµË‡ɩƹtïÎÊÐrA@°2ç×ÈÚ(Þ¶"ûAôuE„”ËÇSÒèkˆ5À¼ß~ ófùRŠî3€Êöl'½¼Ôç'‚€ ÖA@ȯ‘u‘ùî<ê÷ÈÚûî+äÉ=H{Þü;e}ð/òäeWIw|é2újÚýÆßªøË ‚€uaÏFÖEÑÆÕ”ýý§Ôùô‹ºÇ]ƒøøAÂñgRÉ®-”ýçLY‚ ‚€ ´6¢ù5ñsÒ ýÝÇć8A@ë"è}‡Çë))S|oiW®:u¯æ-‚>Ù--°'‚@ˆ!ÎäWƒäÊKËr‹15ü­VgEŽhÝ]^^u2±‚­&ªÈ#‚@H"ÎäW£BJ‹‹ö—:¢”2»u·cÙXÆò’âC+êk”D<A@Z‚@{ ?ßÐᎠëVáFÙÛ»%˜5.ËÆ2îܼi22ejž’¸ í p'?“<ø¬­Îøy³×]–·9±?ß[Ò±lš§|÷+t…}AŽ«ƒ~âÎoÕ”¢=µQéðu²Æ·yíª÷¿~ÿÞÆ”‘åe¹YsòâA@áN~<\ÈäáÆÁ¤ÂÉ.¼ÐϱøÃÿ}s0sïþ'ŸvÙ‚~ŠK-Ø¡–½^I-ØJV¾ïx/g`«N6nÁücÁò¯?ÿïÊŸ¾[ÜX¾ÂÊ3ËËr³ü–ž…,âA@yÚ ù™š Ÿ—]ý}é«7®ümËqgŸ¢§ÿ“vôLU@6.w±å.T"47n[îx[5Þ]†°ƒÉOyiá¶Õ«–üüùÇß–”å#–­ òÌ×þšŸ'‚@ wòcœÌy?VåX³bOs¾S+--ö.þèݯ}ÛwÐ=ïxTL\‡ˆ¨ÈX»=ªÁí`]Wc<…©`Iƒ(5RË‹ì‘Yº¢ò|£á<wYYñþ‚’‚œí׭߸zå<`Rf É.¯ò`"d9Y^Ž/CžAœ D =Ÿ9ôÉDÄÇÄgjs&1òð"‡:6­Y¹ÇV\sX3.ëwgu8éˆ8Çh„â8N»Nñßì-{}}¾{weL–ƒóóÏ“µ;&:Öø˜ür+¯™Íù>Ñú†8A@‰@{ ?ÆË$Ö¦LBó'EöÄaÌâlj†¦vhÆÁ£ÚÝ{Ê>ójÊaA$‹»°‡ë¶µùö÷í,ù±Lâ3ç 9OSëcÍ5>&AÑú‚8A@‚‰@{!?ÆÉ‡5snI¯Qćp†ûå ;sKIñ•Ž™ØÑ¥\m$ PÒ…Ý]OŸ™ñÞ[˦±»è ³†Ç„Çg>XÑø‚8A@‚‰€9§Ì<¬˜6 kX¬m™ón<߯'9•“P–ÏM>»3oÿ%ç\vcœSÍŽw¨ëP¨k¤zéCƒ£?ÿôÎg#]îäüYSãÃ¥8A@`"Ð^É15µ@Ö¸LäáOÓú’ÉçèZtÜòcîü¥9Þ¡.›2?Ú¦P¤]!—ªwNq)¯ý2:铌1]#SÅ¥8A@`#ОÉÏÄÖœÿcÍËœ‹c24 RÌ3khÍ9JÓ–çì:õ³ýW9ꇪìÂAàAÂúÀSI÷,[6:韗öL1’³ ‚@ph1ù9 pÄWÌVMÝ$C5çY3kÉa¤u사…q¶„Š¢>îc“të uý*/Ù°|t—G6^Ý7ŽýÅ ‚€ ÐîЕXl¹Ì£mAw-&?EWöàètIÃ$ƒ#>Z_0baæ}6Å9ˆå#³X Á,“HËË-ضlLÒ”uö5ŸÉYv‚€ím²¶˜ü ävŒß%Ý:kVBk.y ]¸kãÈ…YÙåLEQV™å—&HÓÓ ´Ü­c’&­¼6)Ú|&gA@ƒCÀ%(ßöÖ(c‹ÉϦÚ>ã¡;­X» 5·<†-Ìújøˆ[‡bð”mƒ¯|ºÞQ×ôYå9´=ã‚äGWŒMîì{&‚€ „Ì!Ì%Ì)­Q´“ßËÓZŽUp»t]¿º5Ç<”´4møÂ}oŽˆ:u Bêõ˜Üì+'“ i{Kôí£“_þutwcá¼ï¹\‚€ .18¥ÊÓbò3dÔ•¿Aðs'LI?¥dÛ,”wÞñŽX”ù¯á#Aªr3ìA·ø «ë‘º®Ýâ¥òu£“Þ[va×}ÏäBF€¹ƒ9Æ.k­b@Éh¹KK{͵dzk½®+¹Š£óñóÒ&ðz9q-D@;Ö¶¼ø»KuEÿ+,BGVONQh%Œ^²'Ò›CÞÈâõ‰âA@)ƧÍÒÝûR½CW{÷þii7ðò² »€h~•ÂŽƒ´ƒÈ½ÿu0x@H5襷x•šà;0Œ9Ʀª£RX^Ša8ŒÑHŸëÎÖwg\ôüòKR˜Ïä,‚€Õ0¸œ9á×ZÄǸ”¤ÆOK¿FÏ¢“~‡¯ !¬[qQò@¯G¿w5,CkX‚ûo°rðÕ$—ãýïìâEùâA@°¬ñÊécU¹wÞô©Ïµ¦%?¼‚é)¨°«1$w×ÜS—´fÚK^cûÄSiÑuÐoÅÛÓÕˊ;¥Ûéµf-­þ\îA@h+xŽ;¼€ü)*ÝßÚÄÇå8ùq¢ã§NçM›_Á°\*Ö°}ŠãM5J]ôòCñ¦ÑâŒÀò1I§kº~ˆð"$]ãK Á?P¯¹"]o zg[f€³—äA@h^ÇW±œA¿š[`³°‘ÆÍKŸöyƒ‘ƒ (äÇr²Ìn÷î;°þntÊÝQPLO)Y0ÞØƒLùkâŒ@\Y¾sàÕI}rÖ'G» ùcºUœŽÊ8”³µÃaû7tp ÄÉû™ŠA hðö—Æ.`XÀÎëø ríb«ÎnŽnoÍ9¾ê ùùgtë´Yýš÷øõùuE¦²u—?@A¸î‘¿3«“»çoël×<5´Al­¦eE¥äléÐwÿ¦Ä#”Ú#xïRq‚€ _ÈoÝÎ Ø[k_C…hòkHy<x{4w¶r”¾ë±\âT4ÄuŽaQþŠÅ—x´HP>öÞÞíÁ“HRA í¨Ñ¶½H"A°ømt÷n^Ý= Q®À˜ûquæ£(kð©‰EŠj[4tXÿ•´Å2úèq«x;¶ºÂ‹¿ ¡€€_(ÔReüõâä^Æç ÏÇDôi CW]Y2Âpf‰Bú·ŠnûvXÔI¿ñbüºÂ‹¿ VD@ÈÏŠµÒ†2íÛ=2«Ìs:éøJ‡¦œ…ô=ëK(Š1—ø«®*ËИ–)Šã—áíÚT_y&‚@[# ä×Ö5`ñüW^ص¹îp”Ra0Û¬×Á€†×sfàX¡Ø°Ùæø=¶wäº~/lbÃq‚€ ´9B~m^¡%Ào—t;Üëö°Õè©X¿yRCš¡¯t yAŠ1¤ºnµ¢ØVkª¶4Ûæ öÈFè> äBZ!¿Ö@9ŒóXyIR·WI^:FW´‘ ¸‘°$íÔØ"£b ‘öàß&\o„†¹IU•M¤«›»cûжÁàFœ E@È/°xJj@`嘤ÞnM½ŽÄ|à‘à·ÁðîcìîÐT„cìNh˜;0¿¸Ñw€ w’vØm¶ÝÑQ‘™ýÞÜ”ßÔd%¼ ´o„üÚwý·ZéÙf_±w ©ú‘2Œóâ«õÔz_/Ü;Z$ˆ¢”`)F4HÞ·‡’‰4³°£^&Ö*îC>xg7rÒþ¡tr¶X§¶m‰,„B~aQ¡[=m”}ùo›{*nO_T_hxý@ˆ}Q¢>8Rq_ã³M-)-<P%_ÉIfcˆ6e6òÉ1 ut=–«¹6RsañšKªš§h¶<=Ò“A󽳦¼%ùK\A@°B~Ö¨‘¢ÖŒížXV¢÷ÐÉ“ª+JÒôThx=Àa©øf2ï›h‚¬C”Jo¥ùçBó@žù| ò{E?ÒòIÅYÃò$Õ›çÐm¹ög^T4åö~}[«|©ºþrÈSA }# ä×¾ë?,J¿fì ˜Ò²¼d ¥&c[6ì¯'ƒ“Q¸$AL±¥Œp”NXœŸÐ¬¹Ç"M“µGlmSWŒ36ÏÅR’lœ¡‘Â_Q³ Z©Ýv@Õé Çðáã³ewV„$Õ®òk×Õßþ ¯§¥©Ë—ÏK´Û•D¯Ç“¨éJ¢JZ´ÊDh•HQ;€”ðÃètâAD0 RŠ‰Æ·x~²ŸÃP-äÙ÷ã~4ͽ5òíÅ}¦ª¨™ŽçÞƒ®ß'DÙ°%jØ# äöU, $l¸“OöxOYy|¹¦ÅÛt=C›ñÐ6ã˜1o ’T˜$ã|× ˆÏ ñD ⊤Lu¤åÁæ.ä…†*;`y‹³¾CUlÛuÕ¾q¸ã¸mbøSrâÝ.òkÕ,…´[¯ïå*.¢%¥åTÝ“ÀÚ&¾ôŒáX%ŸYL„AN†@A¢1§Ø¤ÕÏ:C¶`²@”‰`$•¶òƺJ‘ßzâïJ„kõˆw¶`ÞRœ Þä‡ÞIék  k[]öm'Í«tv۔ΪWOòª”¢sœ” ²ä­ç’A)MÙh zé ¡b-%ýŽt~Gš+lZ6ôƒÌmÕÃɽ Êù…rí‰ìm‚À¬W#úê^ω“'xÿå/ÀܹÃõŠ&,wûû·Å5k—¹<°’uc¹ˆ’ ÔT güౄÄXNÒš_£ÿ ÄB]¦èê2-U"£~ ±-jVò nüÊPÒBg^K,Ñ 6ŸÐïÔΧvèC¿3ç©Oc±½®;¢'oÉ©“Çyß­«¬¿ê8Æi‹Þpï ¹m²}[Ƙ®QŠâé‹ÏRõÓtípXÀÄÚGìÆ£ €Öè¬KnÓß0¾Ñ•_ahó-4ÏoGäÙŠÎDGΡ€€_(Ô’Èh9fÌS–ÚtÛ}“&x¾gáÿG§X¯çàV•\Ã4ÅÝ›Èûèäñú¨ÚŸù¯ØŽziáÌ·M›{n$æëw˜ïÓùÅ#dýwø¢¬‰#f«&$ØTõ|U¡' óR¤àñOdؘüêà[y¹ù2.Hú²?“-ݦ¸.xh‚6Ãnëx64ÊMƒ~>c®ú˜NåK5µìÒêé6æþˆÖ [ùÉð…û±(븨¨Î *Ùξ€Ã§µrZÍ2B|Ùë.ɾ½bLòyl½Ú˜¼$Œ  „ü‚ª¤ö°¡ ¬—”y NÖI»“H5´>.8:{hp‡ÈÈãÉ'ECë»óc…ÐÌ.dêš~‘ùœãéŠ_ P†5é“ìÿÚk½\ð¯u/ÐÉÊ×a^‘ãcªMñ‘íæ¼ ü匎+ÙÅi0qò}¶mÕ`¾ß”óÇ=¯$õ,eðÅãgÍuØð»pø¢½ŸïÂÑKµÑäíVÉÀÙW8`ŽË½šöñòâow.Ýåñå—¤ 0ŸËYh-„üZ iÉ'ìã|®yõ+ѵ'=4γÀW@ Ók!wéþ[Á?k:EB; $8Dp†©‘!ŽŽøŽ‘Ö@¬g½j?7GÍÂâöÚµH3? mV![4‰²Á|Îg¤QŽ<]| #›'pÌÆet>‘ŸÊ ÿhߪ‹öM¹(k¤j‹èBÇ ‚R¡Wf,R€ÅZ¹w-†D?]qAÒ™Ê_ÒBÀÞPy.u  :>×½å/ÂxåVž3CÐ:ÙªÍù±··|û.‡ë¤ûn,áònæ\åËle婸ù5ñò]%AU3^±? {½O—èê5 Éú ŠÉÖ !I$Òª²K „뙊+r5ÿƒ4U4?¯é”ó°vîAÂçã·1Ƀܺ†ùHº†ÉÏÌZð9^ÒÏ ®TTzJOîñöˆym¿^Ò”OÎᇀh~áW§R¢VBà¡›Ë6©ªíŒdGêëU²„ÖæU´©02ycæ\õÙ™¯¨“3Ëw܌ͳ¿ô'>Ž¿1lzÑ3ó»G‚ |ä4eœç <ÞKº‡µÅz‡&ñœ eü ²T#òíÊ9 ›ÞIj·õUäD8ü£½Ÿ¶µœ’x äõ(¥hÇÌšç©è1&MÈ©2Ï÷Ä\Ç0öô´Ù#~yð¦bžwó9¶A#¿´´×\»Ý»ï E¿¯^Ýñ¦áÓ.Y ¿=È´bjèã'% #X’í:wËGE—C Õîù¬ïE+÷Dw­µùÅ€üº¢ßJ‚Ñ VMÐ.ô[ëæèö÷´´J-\T­‰…üÆOÎ O_AãI…Ö§8ÞT£ÔE/?ôPNå“à‚€ ´ßFwïæ!÷X ˜Z™P¦SUN² kk] ß:kV‚V¬]€8Wã8/ï;vܼôiŸ×GüC €“ßøié÷`Ú§E_7¨»æÎ˜º$´ iA ÜX~IÊ­\û3€‰\6ÙFRì'`Ë´¿d?aJú)Ñ ˆ6HQéþyÓ§>nø´ÇòÔूøôgÁ¨ï‘½Ë B|í±II™ë!0üý½Øí¶1`=cókŒJõÓ5ÏÐêT¸Sç~M×ôg¹Ÿ³^ E¢¦"Ð`Å76AêD;ú˜ÈÜô)W`¨ÃçâA@°ØíBMÑß7æó –¢*wXÅZ]ƒŽ‰rÂÔo£c»ÝÛù2Ú d–Í[PÊWx¨“¯â³t‹p‚@»E`Ø¢¬`Àò¤ ¦hžømLò 󾾳ѯ¡C|G^©ì÷ê‹"Ï,Œ@@È­: ãÌñÍK›PláòŠh‚€ ÐÞèÖ}æüVTÀ »<ºöæÆ;ûË!‚†û7tšwrgX³7Až[€/gÀ[ѧ2ÇgÙzÁA ó–»ªýÏ賌uÙü-÷5 cý±Œ«±‘$œåh1ùñöŠu|Ê›–+$‚@- ùhÏz…ìZátÒøã’nÍû†Î Î7¹ß3ú¿†ËsK"Ðbòã[x;¯ã³d E(A@jA`XäÉs@€kùŒXâ‹ÜÞɵ«Õ‹û;î÷*w®ª5ŒxZ“Š×“wn‘ìÖ®h‘Nª" ¼óŽWWéáéúícºš á«®vgôwè÷àݳÚ#¹ ZL~¼W'oY"å1A@ð!0rAÖ‡¸ù‘=0ô¡kÞ)¾‡ \p¿Çý_Áä±Eh1ùa]_,ŽZ÷ɳh™E,A@|Øl¶‡|7Š~ÕÊk“¢}÷õ\p¿Çý_=Aä‘…°[X¶€ˆ6nJúµXxïÜéS‡ët’jý‰Ü“öl‡RoÉ 9Ó'ÿPȦ?m‹ò4]ʶ‹1nêôïE}}Þô)¯¶¶ã¦Í©hÞG1ÔâNRíwÏ›>é÷Ö–CòkÃ>Ú»$イµÐüBý‹-ÏQþ„þÝ´T$t¨!²ä7~júØqáLœ'žyÞ¦ÇÛ¸ãK±õ{=-íòòŠgJ„ÚšSâ.º{>6~öì„y“&å2oxƒå¹eÚÌ1ÿ=v¤ÿûž\éƒD_F}̼S‡7G¾´4]MKSPLk9”iˆ¢éÉM•ÊhOöz%}Ú)MËáǧ=ÕIw—~…6¸VUÔé0ŸO²Ûb·6'­æÆ±BXA†æá§ÿñžªˆ«ßˆ³_ó€ ™X!K~ ­QÉöz`»ªêÉØ­álM×çìÖ7ŒÂó?·U-¨ŠãÒÝÅsL|M.N'N˜–~â½Øä¸ DØíIß~gÚãÇ¿öஂ¶‹ÇЧd 4¾XRãæ¶¶7aêô›v{f°on+À­ CsËa£ÿ”zi6âÛ]?eÕI}ŽZ”µ¥¹éI<ë# Z_Äz%Ìž›>ùãWÒ'4wú”yÐb.ÃçLÒõ«&L›=¤Þ˜A|ørúƒë±ö™ fѸ¤šígÝ6%½Gã"4.Ô-Óf å5N Ý>BáÓo©0›/éj{·¾juâ½°Õ3­–¡d¨&R£oühFŽ”O8Ê¡”«tA£#KÀD ´5¿Ú ·«ï‘[›¢hžÁx¼’ƒð°èm³gw?%ý K ¯”ÕvE}øåé“¿ã0xö.öwÏ~%}êx¾÷wØÅýqü"†ÍKŸzûW|¯P½?“31O‰¡¶_0ìú6ô~’‡‘ÖuøMœ—>åhÿ!Ç[¦N¥‘k2ý($Ãû¡®WÛÝs§Oþ‰‡‹ö¸g\‹¡Êk×X‘E#ÝõØxwÆÜǦ.à|›êvû·Û{š‡èeÄݘøãÓæF‘{ÿãÿläŸyVÚuª‰Ê0NÓ=qZen÷À²@±W!NOlö{‰>ÀâK¤—“Sü±ÀÜÜÛDjÖ+éSîâðœ¯îÝ?©g£Žz!Í ??Å>ùIÿáUÆÏÏîjŸzínïŒG±}úUÀ³sD—ø./ÜuW™Þæõ¸iéÓ10~¦Ía»jNÚCÛLÿ†Î(ÛÏx;œäU•Žë. e&ïƒqއŸxðÁ‚»gÌH*)Öa.¯]ÎåÜížñ+†”Éf£´9M}ŸÓ@YîWT®¹éa`4 iÎÆËQú„©OôÓÈ}'4ÓP¦T„ËFcýÌ­>ä¿t¨¾ö6aæÌd*ÖùÛ™çC¶\„=Æ(—®LÇËà;õå?~ꌿBî Pg£ªc2,E¾ŽgÜv ç«#]? ²öE=¨º€"izý24>Ÿ†ê·¡öiÊÚœ3>Áö ~ÏcŒ¸„ó ÍIGâ„¡®ùÕ@YõhØÓ«è¼Çpèp<žBïbt,èÇÔ©øá> ‚éêÕ´ÏnŸ’Þ³2Ø/ é7Ü–öD•ù"cóZ]¿áWs8þ¶Nø‘h‘ª¢LƶðÓ@¯9H»—¯sW”N•W™4âM›qFô<4„{ñîC¼ÕŠMÙ˸ƒ‡œ·¡+BG8r>9mº—þwÛÔÇøjÂ…Jj1Èóü /?mFƒÃÀãçÎuèîýl¤s5¾a½ß}(w”GÓ¾ÿðŒÓ8k¯bÛ¹æb(úó3]WÔç ùl‡ß…÷¦=c|/Ÿßš6ã(”å4”éÄ Ϫè”ágÚóqHãOª®oãpiióÈ÷'È_y]U•¿à»i 1WûÈä]ãsŒ­®œ°Û3ó9Ò¹ œ¿ãÙuváŽú6›’Öâ«Ìoêì òÒLÔç|üXÆ¡çŠó'-#+ÃU9AV~yL2=ï|þù¼-…¼<¬úÚÕxß´Ù ËÐø|PÿuÖocÚ§)o³ÎvýûCñô“]ËU8"Všßüùóm_®Ü0?üÜh{L†¯Âtr¢3ý€>“æ ié_jn}™‡ Ë®g"£”•SºÇã¾ñž6ãîõìÎ;ÎáPþÅ~xÛ=§ìnö©7øi$ÿ4Ã×v6Þ˜ÝûŸ€¶ù!´Á?ùH’¨ŠEâ+3¦ë¤÷[/_ëQÜçÀÿÿg¹.÷”GâÍýÿ e]ï=£ŒÏç¥Ý ®¸ú®ýãP£íuèKiSÌÎû㦦o#M{ñŽyeúäoÇM›‘Bš~W„ݹÀœó?mö‘º¦©EžR~9ø€óð¸õ‹AðèH±i°ÆÃrKÙ¿L+@Ç¢ÛtÕñßïñl¼;[;9¿”þ YÎ÷o™:s™W÷.˜0uæù<¼ÍaÙÌ{áE娮Žþ'š†MOªþá߬i:ÈHýóœÇ¦|Või£ï8£Õ^/NžÌ»·¡ÄC~™¸å…É“÷ã<£C@Ö£æLŸRñbÀ!+ÚÏï¹Ð«ÈíïmáÃtÿC]u2Úôh¨½UÊ5Þch_[š’¿™GcÎeûóoÑ ²)öás¦?ôk-qê•¡–ðuzÕU¿iŸu&ÚˆÃ?È\½|LR.ê«Êš"ó~-„ƒ„¸æ§tÇ›ñDS1„óÒ—«6lE]€œl×=—vo®½ØuÇ¿ýïç<2e9†Càͽûÿmʶ]€7ïëùÞt𮡓SV™d m~öºgÜ“–öM£^tϾðƒNØÏùŸ™Eç "P²ðVß­Î@õ<À(œQ¿ŠÝq+È%’<%ÏÖÅç!Då7³¬fXÌe}ŠëáÆÛ¿éYíÌ&ýÀ3S!ï(óò¼‰~ƒ7†o Õ1ùU8M;ÃZ _ ä+?â3ÂÍIŸ íSÙ -ûJ3ªï¬+Së">Ox8ý"þhE·ÕF¾t¼PùŸɃÄôX-·¾d€ßW%_Œ&|ƒ¶ÿ“OkÍšÓÞªËÒ”ü«Ç5ïAì—¡.®ƒøÌ`;×R¿-iŸÌømê†owÛôO„&ê¼­[4=o»·ãí¸?Œªû¯ê¢ççL¼»ºÌQñê?nè0oÏÃÚiúãú¬™øZሹiS3°T!ž <çc(fŠÈ»xËžœ¹ÇýÃDÌÌsE«/Wjf°*g Cõ‰’ÍáÜPåAµÖžòüAûƒ4ºãq :ÙŽ˜'4H¬ZðFßÎK›´–x“1®ú<ÎoÎMŸöiÅï·~nt”‘Ð>ŒýÍGèøx(SõfõÀy“é_ýŒºÀ°®>ŠýǧÍNÕÝî¡6…îÇÇCíÀëž;¦Nïý÷ôi[A†§a¸ÒÐú8,žyÞãëZÜF„è[ÝßîH05Óê8Á^ ¾I òålU3@ã}@Ò;k„VÔB%•‘Ç×vj„ñ÷P|C þ¾tï3ÏDg] ÜNö½ ñuÖ2ØÐ¢ùÜœöÆñª¸:ò¯¦ÈÖ¿ƒ°ÇµÕoKÛgc„Ãvg+ñ¢f»èšÒ«1q$Lh"ТNÕE^†Iýð6k0ÎçcMÛƒs¦N­A|,ç“÷ß_ܼ)ö©Ÿ£³Û¡»é:«º/ ÙìvÇ›þq1”8•¢”Þ Ýÿ€0ï)+ön»eJúÕþaü¯ñ£â{'9Jýýý¯1lv—®¹W¡Oí ^~_QÕU»ã% ëQ6,wP~‘ι-íÅ„Û?¾F RZ—'ªª2 C‡7x\ ñÕéð‚µ—ÊQ¬)÷EH+;Ù~Ò’®öþ‹‘zA¹¢Œá ÑÑ <ù Ö"O…?±AMo­½øÈmEu J¿ ÏÐQëÇàEåÖºÂ5Ò¿ÁvÓP: ö²òºÀƒſaNñ1`³KvžC:ОþQ=½¦¶·êñkË¿z˜÷ºâ¨æ»±:Ûoµ°¿­™·¶úmiûlŒPª®ì9NévèZ®Â ×ü[<‡MŽ;Ÿ;0x†QñV®þRÚlLPÅÍ›2… U&Áxcf™;ÿÿЉý †ïÔ6BÝÊ:V§âTe8–eƒ=î O “ú¥—±;î a=α£ÿ¹l·O›}s¹î^áöäÎÁ–‚Tª¤2ß„¦dNú”׫Óm· ?~ûB~&0axuÍ/àU‚×Ý×ÐyvüjÕ¦‹Ð  è_õeòBÚ]ùèäŸÇ¥c¦wó ÚÂ*NÊÀpb™×£]SÛóýî ¾…QˆN ª<÷–^ )`/(/NŸ´sJ³ñ;khUò :»¯ÑÁœåg[=Hå}……j¹×ÛÃ?€¡u+ô‡JÞÓÉɘoó eÚtå#t §(º1ß÷{• ]YмÏ6ÈÞ/AÈÅ ï×~Þ ^‚À³9PW{׉ á šæ™oh» ÆlÕÐð©0E=á3W6ØF˜w­ÛÕÕÞ0tºV©ºS©|¢(»Ðîâu›~AÌçùÝ—HÛÆéçT¯#ÿpuÊЄ|üÓ«~ÝøöY=fãïu¬82Cc$£«y-çðC `k¸@óÒŒ©;¡¥|Š9'¡::Ç~è_6^‡3ïݦ,‰&×¾Roé ®=„Žá@dBÄ:ÿ°æ5“†ßþÂaãœ|Å¡|€5ñØæÌ.îl›´r'}Èï:¤ÿ½Mqì÷èî+pÿæAtˆs)¶Ãgìö¬ b‡©²¢JÂ1Žê½Ö­ÐW°°Äœ™}¥Ne½1+TÍm*ßudßï¿Xµ>Ö÷(ïƒç5­>öK´kÑQiQ¢>7Ów9¢{ #Q–¿ðrÓŸÏñ"žÎÏ-£{J>Eùï±;œ›4Í=Dóz^Bù×¥ØN|Õ?|c¯ÓÒn(Ÿ6ó Ý­ex<Ù/#Þµìph/x±¹~çÇ)˜}MÕ<Ãa©|Þ@ ‘ʽ±í Dú!æªfÃBv´£~]ê´=›6Ñx 8”ZÕ+…ìßâ†A¬wö„‡g<¡8õuÞ2åd ‰?ŠU†8Q'3<ž²ëa4õ9Â>¨jöºâ‚yän°`6 ©ê’¡)ùT•°Ú]#ÛgµXMºµ9•,oYe…:7)²)D󫥺`a÷ Þˆaü¡¾]}ýˆÏ«ëÚ Ýíý£Ð]tУy—Àì>ós<;qbI-É^X”ý:ýiè×&zÝÚïXcˆÅõÚMóFó$´•±x¶¿öèåL¢W«Šm,:CŸ‰]i7Åß–Õ•›A|~]lE ¼©êRF€× ŒÿÑô² è ?ÃÐáDÿ<.¿ür/lpþ -q`9éÛ°ØÝ7,‰mæ¾@çÍ2ŸúãÁÖ·Ð –ð3›¢øH‘Ó}ê¯-ÂR6ïߊ4?t»ËöŸPöeŽhõ$sè”Ã6ÕÍK›¼NUô;QŽk°DãÆ¦ÆVø®ö“æB3~ màFm×¼ô&q‹Jγüóll{³Ûœo1v0¬š¯¸s‹ÜŨãúc/\,aÐÁWY1dM;±ÚtšbWز³Ê01kêXŠr ÚM‰æÕ?á6мð;ÑŽ6s©K†¦äc¦UÛ¹±í³¶¸ÍòCášO"…hã-sÐhs Ð Fñ9Êt6wúXŒ|ï¾R½L¼KŽcfg[‰W)&ÓŠª¨žû;ÒïªPdamñØ ¤,¢Tñßᣞ¤‚öÈX3¹j}/ŠT‹Ïì×oŸAxµävKÚ¬^*Åx)íöÂZ7Ù‹±ÝïžÑãÔ!‡ïª+Ï&'jñl{æÀ>»ë*oSÚÛ?[”ïN|qÆT~‘j”ãô÷¹gts:¢ ª/ª-Þøa¿{wÒßÓ§ìÀµåmú5¶}šá{θ¸Çaº»Ì°h†&»qÄÂ}‡×7û¾ºÊŽþB~µÔ*†=AK…鑵</A@Sð%÷#tÍcl´€!÷?F,ʪu.¾_h7™óó«?~£ÝëÞ}æâxŸÄ+ýÉ¥ ´T¯Ão°Ó°TnÅneò«¬u,q€ÑÉ®¾˜gò€øfÌM7¶žjmBÊ,´[Š­Í¼FùÙà­ÝÑ .äWYÉ6Õv=Öø$:;Å~_ÝÈ¥´)¢ Xžõ6­m1›¹M@ _„ü*ë¶Õö, ß¶$%B•´Þ¦‰'¶:Ûò’Ô‰€,u¨y í ˜®ö1ËŒé!?Œ0< ù…a¥J‘A Ùø“ß–f§"-€Ÿå«HÖ@@;Ö†|-ÚwØ}›7´Fþ’Gë" ä׺xKn‚€ `Q~-û~(6·ÀgÄxŸ[eû÷vﲨ¨"Vò ˆ’„ „>^];Ù,æþ°ý ¸pF@È/œkWÊ&FKN1« -1¯åžù…g½J©A  èøä ‚ŸdFÑ›h~&az‡u~Ühù0ÿµé®gŒÎø_ûßû„éE{®÷0­ÒFË¿¤Ý¯Ýu¾nщ%À|ßþ öð—UÄ…1¡H~F§wÓi#ð¹—sÐR{’ª¤`È"6Œë©Ö¢i¤’¦ïåmw—•}ñïçf-G@^£ËG@:…Z3nO“ìx´BýË= wDDœeÖ¿JŠa¨Ð6¢I®mtµ³ýëšþÙ?žHË€Íj÷ØÒìÐ^¾ªÐψµ6’gÄù)ãǧEz•;íÛ=øg2N%6Ê¥uˆ‹R\G»Â-u»µÜüb½ ¸TUí¶G'LKÏ,-)ýûöŒÕ/.^üAbî{%@IDAT1ªŸ7)4‰°áÖ`ÍLz\·¶Q£.Žê9bðí®H×RÿÖ¬¬Ö”Ê¿ýcØò1nÿ·÷9[¶þ¼yiümM ±NÑxÈ3cL2>]\Ñlÿ­3°<B…üÔ|øÕéx_ï:°O7}ä >tdßnJ”+‚׿´Wg~qiý¾i7-[³%iõ¦]éýOq{ÊàÃnûïßŸæÆºq˜$j8¤¡WÝqßÙq^Â7R¤þC­ƒ&omí¶š¤Þ…þâæ>þØgÈ™_þêuòÄ\ŸÞƒá…úÀ°ý¿¢E{ë#C«“ŸñÖóäGïÁ§ŸHé¯_qö±Ô/5™ýÅU"€:vp>”;2é­Ï–vIüïÚ‰“~ã™ÙÏ#X9þ›h|Í«ÆíªsSñkN:îüó' •HNƇÕ^(Œz¯”/’åe¹¥þˆ¸f!ÀíÿÊs޵q?Âý ©Òî3.L> ö C9q^qd¤cn³2’H!‰€Ée²G¸\wîÛ]—9¾–µ+ÆqŒï˜x#Râ·_«jF½WÊÍòJý·¬î%6±qœÑþ¹?UG=4ݧõa™ÃëƒÞÙ•-˜µ¬F~ÆÛÿØqwƒµ7IXÎ`5í$$[FŽJ§“Ͻp$ ÀsVÓþüµ>W…œJ'©ÿln–šÛ÷'ܯ@8Cû[ya×þ ¼ XX4>ݦ8Ù*T\;BÀ’ä{½c_;ªŠà•qd<;wë> ¹ðÐ'”TÂ}[:“üX®H–Sê¿-«#¼ò6Û?÷+(™ÑîÝ^Ï$ «/׺¢,ºpׯð*µ”¦!¬F~,Tµ'ïÜÂëwĵÆ162Bw8#ømÂ_ó³JýWÔ{…Fêb9Y^©ÿ–×½¤@d´ô'ܯÛ’s» íýÅÄFQlOš×rn?X¥ó3gyìØ³3[–YM6SÆÆ&Ö߇SY¥,CÀJÃ1ü¥*J öê”ίquبPŒ§MµE!°I|æ°g[ãì«wÈÆ„ìd9ÛKý—•7o3‘¬ƒù”y ¯QuïhùÛ(cíV¯vqÍí‰û•ÿžÜéDLñUÌõ)Ø×ÅF“ÚRÈpgc%ÇdlƒùqÐ;ä)/þæ`.Œ(.&ŠÖ®:çX²Û™ZæÖmÛK½R:‘+‚§°¬äŒo–qs!ù°ÊËOE½WÈù 9­\ÀeùqåFúê—µ„m¸¨Cl4Ý?•ÍòÏ‚%¿’WóÒ„?nÄil›[¶f+ì<41°w£ó ›€èWzÇÚÒоÌ"ýgćY¿›7rn_X¥ócÔM €-³‚N~œá CúÑ_¯»€Î>n0ý¸reà­¸¥ÎãõÒœw¿¦Üþ¨‚µ†zW“ø|x·±”þr²UÊÙÆb/û²r7ÍÿâÒ¯=vëŸhìY#éˆ^M۶N¦›/eiå6<›žrÿÜõ‰6EÁ1ñ;2Õi{¸é©HŒpAÀjšŸÙ¶ ¾qÑ.êÓ­³q¬Ü°ÃA[è¸#3ò^ºz }üýo ±êݵ]}þñÔ9!ŽJJË)ýÕŒ{|]Àû–P|Œ‹.£ —Fߣ­à³NtùÙÇÔÚæ8½ŸVm¢O\Ey…%”šÔшÇþØó’^ÿè;ZµiõE[;ÿ¤!Ô m<œê-§‘Y¿_n0Ê©(/{oïöp.³”­~¬ØÑ´z‡œWH;3³©[gÞŒhËîýôÚ‡K@BItí'P1I;h¥táËÝü•  —_Xlt\6ÝÈA½ ÏKNN×_ˆ/¥À-Y±ÑøäÐõcN¡1§ ¥÷¾Ê ý9ùƳ|tx<„µfË.ºàä!”Ü1Þðâ?¢ b6MNÚªr5¹ Eè™ÒÙêœûÞ7´fó._ðX¼ŒÙl ñ&;|žŠÜIø}ãNã~ýöL*,)%lÙEÜn ŠK¨®6·y×>úׂï)5¹]?ædܯ;EGVXOÿVùö´mnË¿ÒH?œÿõ^÷E{Š `ärÀë°Í çòJÙFÀjš_Ã0kZ<ÂÀ Ìùsü`#õ¥¿o¦˜¨ã œ=¸ÓøÛÿ}N›we ê”ÀfS©´8v<ç—Ü©‚ÈVoÚIG¡óIêkì¿bÝväw¤¶´ÌCé·OÎ`϶Îp²Q¨ý 9›WHžg~è† èu¿T èÝ•®uŒÁu7Ú¸#ËHxíÖ=têð#èËŸ×/]ìß/5É <3çºÚÜ/¹ˆt9鯋N1æµÍð|fÿkÎ?Áð:[D.^ôY#ô>×%Û6P÷-?ú TইN[¸;Çç!í+i~­þÓë ‚:ç„# ÃÂŒª|3ÎÉ/ÂÛu‰q«è!#vóŠŒsõ^h„u9/vÎÝ€oìý BõÏEÆ‘_XJl­gº¾=º´ñ™¹Õ8·:îÕ$hëü«‰Ó:·ñ±Qt÷ŸÏ¦›/9•öìÏ¥WÞ[ldÌÃÛö l´Á=ûs蔡؈Û÷ M;÷an°b¨½!)³ó 1Ô™X+¡õL®hÏœ¿äñ°ìÊ5úþ´Ï²þ‹ËvÙî 䟀Õ4?n8ZçÍ?59‘NÖŸJa€°`É XÀõÄÜ]”1“±v»¯‰deW˜”ó¡Š¡Mvån·ï9[ìuŒçm3k:–²Ûl4 oð—œ>¼fø´òÆÝ•?~¾¶‚³¢L­‚ []æáeè/–’׫†/lÅùýŠõÆ\0…öï™L«6ì4†Êôæ¥j »˜Hý‘Y1|Z=t„Ój?ûêî~ß'oSñ¦5F‚º¢j_í-㥠Vi÷+¨¤Ôd¬¤ù5Yø@E8(ÜY¼ûe†‘$k¹E0EÿÃèp¾À°¿!÷éÖÅÐñ™âõRy°èüréZ*)ã¥W¸.‰qxÛV m¯¸¤Ìx£ 3ö¥k6 ¬ 2Yšs~f<9·Ø…GöeçsÍ«6l7Œ©x“w#áy:~>áìú÷êjÜÇFGó}Õ‘ª­Í¸Ü†¿Xº†xN›Óc ¯=¹ò™´ç­}E^™8xïÿålõyÈE»F@ÈÕÏΟΉù¿-ÄFL~cÏ:–>ûiM{é=tP1<5Ê·nÂT=øü|bãc`µg:žd-­;ïî-êï²3G5=‡yÃÛgý›žzã kYo)„Y9·G3¬0™ó=ýŸO0ÜîDû:Å—)/{àaOÓš˜µ=¾€!ÑÚ\mmŽÛä¨èƒo–Óä¿¿‹»e0ت-v˜úa¸sÛ “VRñ;+ŽéLK;Ì ÓÒJ±š@ûÿ¨ÎŒÛÿTÅgè©4gÊõ>¿3Ž@|ðÒ6ðw#1TůתÍHå ,V®âT|Cš·Ž=ÃÖb-1Z¦i\p ÌÖŵ/c£ñÁ}/1,†Ùº“‡ÆýÝ%§@û1–£Þ):Ti›ìY½ÝÔÖæxÑüågCEˆ‰ªhsÕãñÒsy¿ ¡~ùÁëT¸vyE1€ïº¡—’÷`¸Nl†zmµüUum#ƒ¥s­N|þÂÖF|æs§Ãî#>Ó5L³2ýäÜ~à¹ÞêÄ×4jksªªÏš/[-I?Tâo^K{ßžã7eì8ÊOHõÝË… ÀùI;°A@++¡­›Lº·b~3úð£(åқæ|RÀ! ä8,%%A@hcvýó)*Û[±)€M½ïNÇ+¾tsm\-–Ì^Z…%«E„¦"óÓtàë|ÑRožDÎ.[é‹$í!¿vSÕRPA |(ݹ™¶¿ø¨¯€ 'žC‰§œï»— A :B~Õ‘{A@)¼Å…´ùÉûˆçûØE¤¤RÏ SBª "lë# ä×ú˜KŽ‚€ @¶½0íÐ<Ÿ+’{àiâù>q‚@}ùÕ‡Ž<K#°÷¹”—±Ä'cÏÛ%W÷>¾{¹êB@ȯ.dÄ_,@ÞŠïiïüy>“/ºŽŽ?Ãw/‚@}ùÕ‡Ž<K"Pºk mûÛ¡y½Ø#¡®¾Ã’²ŠPÖD@ÈÏšõ"R ‚@xrÒ¦wº°svJ¦Þ÷Ì’õ|uà%Þµ# äW;.â+D@++¥M³ï¡òŸkR#£è°IÏ‘=®ƒ¥‘¬Œ€Ÿ•kGdC`_jÞºŒ÷î4œj£>Ÿ Èžý…‘+A ‘ù5( &m‹À®×Ÿ¢¼eßú„à\âŽ>Þw/‚@Sòk ZVÚ}‹þKû>~Ë—7[vv:ëRß½\ME@ȯ©ˆIxA@hU²—|L»þõ´/Ï„ãÏ¢®×Üå»— A 9ù55‰#­‚@ÎO_Ѷ!ªümLÿ!ÔëÎC{x¶Š’IX" ä–Õ*…BÞ¹e \HÓŒÂDöêgXv*ŽˆÐ/œ” Í°·¹@Ç&¦ó¿6ýšzVü>£Í×þ÷MMKÂ@·ÿú¶rýºÜ\SÍ-{þªŸiËÓú>JëêÖ›úM{™l1qÁo’C»@ Ý“Ÿùƒ×ðv©i:íØ{þغ‡²ó‹(¯¨˜ÊÊñEèC\ØôF¡TD‰pÚ)>:Šã£i`ŸnÔ3¥#¾±©ƒÑô $FK0ÛÀö=hÍ–Ý”WD¹…ÅT^î>TõÍm®³ÜVkû…¬ -OÜGº§Ü¨ÚˆäÔï‘9XË—ÐÒª–ø‚€vK~üÃç½×«QY™›¯XGK~]GEå $ìj ©ö³ý1é² Ü/ün%­Ú¸Ã“åXÓß7î¤W_[×=—®5ê¿FÛÇKß™Û>iâ3k•I—e`Y2:§mÑ©¤»¢é°ÉÏË~&Hrí‚üøÇÏÃ=¬ñååÒGßýf u²ÆgDz80÷î—˨¨¸ÔW0pµÃX––ºé¿Ÿýd uZ©î¹”ÁªÿêmÁ’•ÄC¬ñYű, é—ÞgRòݳəz¸aŒfùDŽðD ìɇ|Ø¢“çøJJJéû_7Ra‰‡:,lS¯zsb ±ÃÃân1æ"Y^–›å×2Ì6ðuÆZʃqKpn‹aîúJŒú7Ëíßö ðpÂÎÅmªñUÇ5Àw}KÅÎú%×&m¿:@ržü*Þ|+æùаn/cÃò¯5Œ[šZc,[þøûFCKõx<òÜTk o¶%0çg|­X÷,v ëß,7xpÛ_¾~‡aÕÙÆ-µTC½^,[œþ´j“´ýz‘’‡B ¬ÉÏ÷æ‹% ¥¥e´u÷~*)óRLÔÊ@áðtX¶"h¦Û k9äí¯e›m€ë¾&ýV®{.i êß,7·!³í—{©/–3XÕ±l…ÐL¥í[µ†ÂK®v@~^¸!ÏÍèy;¯ã³ªcÙXÆuÛ2 ¹½^¯ }¶ ²*HÀkìÚcõºçbªþÍrWiûXÀÎëø¬êX6^d/mߪ5^r…5ùñ°“ïâÁVžy…%dS‹ƒ¶€=Mƒ×±Œ9E†Ü,?—C\ó0ÛÀÁ¼BË×=—0Põo–Û¿íGzJƒ¶€½yµS5¯d¥íWÅE@˜“ŸNžJò++üÕ–$˜*˘‹Oî¸X~ÞsT\ó`ìü"lWgËo^"­+õo–›ÛÙö£Ü…­\’¦gÇ2JÛo:n£é„-ùñ°9ôÆ#lñævk°ò Ì~…M‡ºñ1XF7ægXnsØ“Ë"®iø·rlP®*ÁÙ¶«iR5º¥õï_nÿ¶¨½:.AóC°ŒÒö›ŸÄl<aK~ 9ôÃÂëüÀ†G¦CšÄ-Þ-«³ „ÚËCKëß,·´ý–µ‰¾„-ù™o¿Ìwl1©C‡úx«³Šõ‰,¿Y–ðm†Á)™‰›ñÎB/>ŒFKêß¿ÜÒöƒÓ¶$ÕÐG lÉ«Æ×  +á7a¾ǤÇ9¤ä¶À&~!Tó¶°þ•[ھڤˆcšüc_'JÄÇrûÉÎå×|Bñå!õ/m¿ùmFb†?aO~­Y…6Õ SuGkf)y –AÀkYDA !–üñ¶éJ ‰W®¥äŽGVâ¨Ð©Ã&ѤkwP÷.#}ØvIHWŸó.Ýÿç tÏå+é“_ ‡=’¢#;Ó}W­§N÷…mîE ÊÓܼC5^K1Ôûšpñl:Pñ3‰ŽêBמû>ÝyÙòÔöìè~WÑ£?GXþB]Ë\SÊÒ”°õIuØÏP×kîò‰r yíkê~ã_}~|?â:ê_Ñ×Ó çß'W÷>Æó¾Ó^¤”ËÆW Ûœ›@•§9yKœðE lÉ«Œ4‡Ž¦Wâ ƒï¦û–QæÁßÉ匧+Îø7 èyA„:Åõ£¹èÕgÐü¯¯£~ÝÏ&î8‹JöÓÊMoѨ¡“jÄiŒÇ!ÙCnƪ1Åk•0&†MÍŒ5øS†þû¬>6¤Q·NÃèÆó?Æ M§IÕõlÕæwÈGƒû\R#Nc+·`;}üãk§È(9*ê_¡Ô¤ãé¤!÷V)Ȧ]_A¶¹>?^à^^®QI±—bƒ¨kü¹¾g½HºôF:¸x!yòsŒ(®Ô¾´áÑ[¨ÇMVI"nð²EÅÐÎ>NžÜƒ´ûÍ¿ÓáÎ#gç®T¸î7*Ùµ…:Ÿw9í}û|UA hawÚFR·B¶ã3æï²rÖ¹åâ#¨Kn%··´ÞÜYCLND{ögá Š÷b3í\ê’0°ÞxòÐZtN@ûrÿ€Ö‡õ¡pÆ‹ ÈOUk¾/Ö÷,3ûwêׇl¶ˆ P ÀЪ¿€Q=§’­‡^Ö¶=?Š7¯%•ßÂüœ£cÒÝeñ±wYÖN㩳S²q.Þ²Ž¢zö7®åŸ `%jþ’­$]Ê’Û›ŠñͲò‚&IqÞñOâë™è,ßòÅËÉßJ q½|÷ra}cûP6ê­¥.ÛØHZ¡„ØžÆÐxcÓë€ðçŸðuLL ¸ØlxíÚjÞ·ûćéxa;»2¿ fçPVæ~óQ£Î<”ɤVºwGíáýFaíQ±¤•z!ÔÊ*®í1qFÜòÌ;xxí鈯 ІùÕ~\t7ÊÇ7ÆšâN>sCCéߟ^„u…o«tùÅ{ˆÓkŠëÍãòÓ?oTçטtŸyãSZ¹vãÿùÛw#|&Žƒ8x³K R…zƒ‹6pÜ£c`—¸·ìˆ#ùš»øÛýL¼¶éÃuˆïsþ$ð~ ƒ«|Ϻˆ‹îJ»öýÒP°Ÿe!ŒNqQ]›D~ &ÄN»òƒ,{ýÎS˜OªËå ¤FpUb¸¹¨â¥±ü`&9;Vh¾@r!Xö¬£X{‹jüöØ·ÐQ‡]IouM Òät8=q¡ƒ×WLê¿®’ÅÀB”ç KB§þÝ9š¢3±s]Åòù—íßCŠ#‚•a]É=Œg&q:»;{Ÿ/¼\VA@È¯ŽšÈÎßb,Up:bêqÈ{Hß+é´áÑ«ž%ß­ópã0C$`5é‰ ¸þy®®¥Ž‡ÏÙeço3ΡðOƒ…;{?¹Rz6(nÁê òäeSÒE×C¥]Æ\C%Û6PyÖ.#nÒ(ݳ½Át$€ ÐÚȰgˆÌÛd ]&'¦Y?תÂ{Xÿëp¡Ð™#­nö©ÐS(Ê•HY9k«<“k#°†N{Á?›Ïè¥9'' âÛ‚Ï*škN:­§Ù碯ÈYóÒÖg¢^wÏ .ç_ ƒ—]´å©Cë£ú  ü•?5ˆ<Z!¿:0/-Ï£eë`¦>øö*äÇ&èLjþîµEçùßV¹æøv|JûsÖUñ—k#°fët‘wa({¬±VÓ”ö§Õ/µ¹êÏxÇŸ‘ÆÑ7+fÔÜÒ~™ïΣ~Ì¡½ï¾â³ädÿxàÏ5ä.X“A¿?ÇXòà->ôÍÀØAÃÉÕµ'mžÍÓÌâk! ÞõÔ/pŽée ê U÷£(WGêÓíTt~3ë$O,‰¯Ñûzùt:ªï¯y;´ð†ò7ø­µdQkªhãjÊþþSê|úŵ>¯ÍÓŸøøy§sÆÒÞùs°V0·¶àâ'´)¢ùÕ?/s˜÷á¨zBÔÿˆ—JÌyÿäúÉSË"°qçÄGsÝê­ï¡êvÌIo‘è[Ÿ™Ô¢øY&a­ù) –þúŽ`œ´ÉÞ<Í#8R…Vª&†¡%u…´¦ì|nªóÛŒèMÍ.àáýåxâ’  –üjë0x{0MoÂWmÔDt¦ã¶ªUS[yÚH¼ÉÖ3‡ÝuÏà¶´þýËmV·ýr›õÛ>Ë(m߬59ª=l0s²@Ú.î=ñ¤~¼Þ8l¤]ûnõÇ”§u!ír’æµ~ݳüÁ¨nûÅX¶S~­åÏ2JÛo-´Ûw>aO~ül.»J^= K\–­u–Í«EQt„Ã'·e… Á¸þã¢" \­\÷ g ëßl÷|æ¶_bwQ™=¸{Œ¶¤I°l,£´ý– (q‹@X“ŸïÇk=ã>c"0¤¤`ßCën´Ë²±Œ]c õ!ânl…J¸ª˜m gr¢åëž%Tý›åæ6äkû¸ÞQ¹è¾*JÖ¸cÙtÈ(mßõîR„-ùù~ü°°aþLŦÀ¢˜OЩ°xˆeë•e‹€ nÇø(Cn6V0ËbY¡-*˜‰c˜Ò1–"ñY +×=Èú÷/·Ûwª:mJ´î‹ËæÂh¿´}‹þ ÂL¬°%?®'~ãåðù°cÎÃn·S2°¤¤/•–6¼uSk×5ËIJõêgÈkÊÎå×<üÛÀÀÔ.–­{.] ëß¿ÜfÛOŠqÐÎØTÚ‹M»­æX&–­giûV«›p•'l{Uóí·‚øìø¨‡ƒº'às-ªFsÆÀú¯ê·ÉÚ²’Y–lÈa×éð”xC^&k–ß,K[ÊŠy›¸™mà¨>)Ðþg+Õ=cÈú¯^nÿ¶ïT4ú¾Ç(òàKõVq,ˉµ>iûV©•ð—#lÉ«Ž¿|my0éED8ñuv'Eº"(5ÎNnw:xðRËÔ0ËR™$Ç2²¼,7ËÏå×<üÛ@Œ^FöM1p¶RÝsÉ]ÿþåöoû=â픃‡§žÙ<@ƒ‹eɆLG¤`h¿OiûAY’¬@˜“_Ű'“ˆ+"‚\ø (´K\$%GéTT<ˆöï¿¢M5@~ãßà C–^ N|Ë #ËËr³Ö"Þ5Úm£=Ìá?³ ôJI¤Ýâ+긷µ¬ú¯^îêmK‡¾ôe¯óÚTde`Y¤í7ºIKÀ!ÖÛ›ñð“‡Ãá0H/::ŠÊJ˨¬¬Œº{ܤk%”U2ÜY(±ÃBr¹¶ÖÆ%Ãs<<üÊZhxõ…ÖYN&i–Ûöl\Šª:µµ#{u!ÇC²‘ÛÓ6uÏr³þk+wõ¶Ï¤“ëê@'îü–Ršøáæê87õžçøxø•µPiûMEOÂv@~*¾±ÍÃ)1ÑÑ ¾r {¹Ñùy©›¦‘3¿”ö–t¢Ì}7aÈe¬ñ¬‡¶œOÐð:.6gg«>6náùǾ *u‡ÆCÑ1цœ,/ËÍÖzÜ‘‰k$P³ êÙkßZ—©WÔ}$ê>*¸uÏ%h­ú¯«ÜÕÛ~&%Ђ~¢;¨oözJ-ØJž²æÝ@,^ÇÇËت“[xþ±oiû À&ƒ„@X“cÆÃ?l8Âó}¬Q¹A|^¯f@ÊÏc¥´¯ØC9e½ii_NªRL6{Îåž·Uóbw ‹ìyŸ]ñRR¤NÝ;¸(„Kqq8˜!'ËËr³|âZ†@]m 7Ú@—ƒ¶ìϧÌÂ>´/#Á¨{–¾-ê¿®r×Öö÷Æv3IÁJ»Hw)Ey Èéu· øÊØå6Ûc©Äá2Öñ9H“¶d%‘– öäç{™hºnN†«è0,jgƒ˜RJv{¨° ‡[¡rhh/~¬ ª@8;5Ò¦š^|„b#˜×ÃnÑ‘†ÆÇÄ×!>Þ¸æ!O¶Ð­/ÈW¬“d,ÓÚÚ€[Éõ(*¡œÂRÊ)ñP™'’ÜZ$^“?§Òõÿÿí]`ÅúŸÝ»K'¡·€…¦RUŠ"¢Øì¢þÕ§>%ì>•’DO“ŠOTDH°¾÷lØAQ)šHïRŠHH½½ÝÿïÛË—˜r¹ÛKî’o`s»;í›ß|;ßÌ7ßÌøÌûÖ0Q 5ù&ñ>u(l0ÚŠvó>ÍÁ3ï›Ç]œ’/Ô{áG B.£/-Ú%ùH“Hói¤Z ‡qIaa‘>Ø §SSïXU]a5CZú€05@.\;mXdšƒÄHy’Óçø0ò£Ÿ®ötúØÐŸ+â DF‰f˜v€œ&ñ@]׿7åfÞ¯”mØ£#Ð „Õ5$tÈQƒäz.]ùµ"¿¢Ò†Œ!œN~*F~¥ÃD=¦oŒühôAªL¢Ã°>%'ÍEFEEê©;ÉŸècg.uÅu]ÿuUnª½º.»¹Ä©Õ'Œð3>B,ú= ÝÓš¢¨ÈH]ðéÆ0%%º1ŒZ:/h?_d`é ÏÝÐk´Û©ß(_€4ú#ãzôžü©±"Ù™‹@mó€Q…F¾uUÿFþÌûæò§Ú4áGÕD€±t€Œ± ‚©ºJJÈ ”F}N}ä§azž„Ÿ!}©jÊS¿°a/ü(OýѺ3jŒHàÙôѠ˲“èb8j›‚¥þk»ÜTƒÁRöÀq§Ê4(ágT”1²ÒwÁ A„y?]èAÍé|N<2KwY<ø+ü(_Wž®yF€9‚ú=„¢A“A#ÿï@ó rÁRÿµUn*s°•hbÇ4HáG…7zÂÔª‘FadàbÌóyŽøÌ~î^0CÊ“]£!2 £Rø·v¨ 0ê6˜ê¿6ÊM5Œe¯Îâ\B+üŒÊ1%Dä<…žñ¬{øñÇh( #??’ã¨&#`ÔI x Xë?Ðå¦j Ö²›ÌBœ\"Ðà…_ù:3„òïù¹á ÐPy ¡–»áp6—Ô¶®ðDƒïF€`,üD5s!F€`<`áç‰ß3Œ#À4Xø5ˆjæB2Œ#Àx"ÀÂÏ ¾gF€h°ðkÕÌ…dF€ðD€…Ÿ'|Ï0Œ#Ð `á× ª™ É0Œ#à‰ ?O4øž`F A À¯AT3’`FÀ~žhð=#À0Œ@ƒ@€÷ö,WÍ{²ÿwÇNä‹ã' D1Îøó×Ñ¡6t~_ã˜(Ñ,.ZtëØNœÖ¦™¿Ér|“Ø›}TlÜu@=qRÏ+ÐÏw¤,pº•_.TêŸy߯jn0‘22l™‰‰þ7ŒuŒXƒ~tŠd»håf±xõ½Ñ£#Øb£¢D\l¤ˆÀYf¸ÜüB±ïà1‘›_ ¾üéwѸQ”Ò÷qiŸsôã”ÌȃӨ9ŠâÔë~ÑÊM§ê>ußèTÝ»Nä«yÚž1‚±þ™÷=kˆï= 'ï;<@•¤+%M\*Yä¶8ò&ÖéÔ¢µ½‡-£SÒOH’¼ß©*;qtÇlо°|ÑvûÅ3`¾o°Â>|:»Fy,øUääè#².í#ztŠQá©·‚¢b±~DZbÃ.ñù¢Õâ§U[Äÿ]}¡ž7íªÏ®öظs¿xoþ/âXn~­Ô=•,êŸy¿öx,ÔrkŸ£(ÇÆKû<âÔDŒE–ÔŽí[J-7’"Âm"2< pË"÷da\N^Aܱãyçì;||ÚÒ§²µå’Ò¦‰ÓZL …‘aƒ~$ôœNUü°b#FakE|ËÆâŸ× Okp^%¡zA÷úµ}ïAñá‚ßÄŒ9‹Ä—ö—õëªpp"8±ð·âÓVéuïuk¥î öº®æ}fþÊH°O9Íé<¾§0žuþÙgˆ‹zuÚ·–Ãl–Ê¢Ð{9¿:ôû©CßrÓ®ÏËûßöÀ¤I—Ϙ8ñ(°ÛߎÈvè'ÉRÞ#E‡$iyª77‰±¬yaܸ< WÛ®A ?£ÇKª.|s—ü!zŸs†¸{Ä@QM¤^HØŽ»g¸xwÞR½!V5U í×Í}Â{@2mà‰PݲpUÖ=UCmÖ?ó~güjŠo·ÿhÍv.ÿ"Ìfë<ææ!ÒYg´©&Æ)ïèÈpqaŽtÉk·îo|¾¤§£@y+ñéô×T§ö@¶óÀ•ªÐ"„Z:{Žoÿ᜘jpª‰Éi‹!ßmcéü¡Ý>²äTʽk0ÂÏøøi~oýŽ}bÞÒuzã7êÆK‹p5©“ÐÕiøl±øâÇßE«&±¢Gçö,«ÁÍoâRu~úÃê ¨{*CmÔ?ó¾/ÜRÿâ$<>D8µke‹ÔV\ç@…㓰ɲüW–²<|rÞÃú‹š¾ò({ÖiâÊþÝ,_/ûãZÍ©^×(ʉQ¤¥[ÇxѦY,l¢…B°°¸X8œ#ví?"ÿ¶aç #9y—f‰íÏa.ñ©Y©Isʧˆç#üHÝC‚ï$ O>Aãߢ±>â ¨¾¤I£ÏƒGOˆ¾[!:µk%"#I·^¥ºÁ—ltœXî¾÷ͯºª“ð&ÈúgÞ¦š®}Z0w‹$‹tÕ©v ³:OoÝ\Fû'…Ù¬sz°j/ló×ñ¼Öaa*4a~/ëÝõ ±ùÏ,­¯ÎT§˜",›$5ka6•Áú½k‡x1|йV„Ÿ.\Õnÿác%$§^‘ðâ“Oæ­!üèã'UgqI‰X¼j³8q²PÜÃà:QuVV™4¸íÊ Ä¿ÿû­X´jzO=  x°2Àjøžx€,zs`ÜòOÌñÕ…š»*’UÿÌûU¡^¿ýJWÞňîÆö-š9‡^ØUœÎkÅj²¶3Åâ®m‹&â©{®©QZçœÙV$Ý­å»_7@¶úö¼ÜâÙ_ºdšýñcª¥²"9P¹Ôaº¤ò!ã–¾ÂÂ"ñë¦Ýºe_m·Ô´ØD­\¶v»N/ÑMô³óƒh) áŒuO%4»þr3ïûÇ?¡;aÊ”8§’ó½,É×ß<´¯˜pßpK¿îE%‚/(ŠHÆîWöï.¾ýrØÆXº(… ž˜:5:PÄÕ{áçêù*ú¨o×¾CâdA±èkË`uD[n~‘øóÀŒV}9F°Ò*tìž4âæº'<ͬæýPáÐЙ¯|Ug¿„›†ÈC/è¦k‘K@’¤Qà蛇XÐyë{¢dj@2A¢õZø¹{¾‡(Âúº­{‚ „¾Ž/P€ú›.­1$I^ºyôç¢lܵ?èëžJjVýå&bÞ÷‡B-vbJÚhMÕ®yùr¯.íC|Þîh¯èß]NH™tA Ñ„Ÿ†.]å‰E™Ø¹%2` ØÍ¨ ZF4Ò[D·ÓédÕ§Àº„€S=~2è랊iVýåfÞ÷ƒyB0*­©Cß9 £'upï³C°§H>¨—ˆŒ°95M}èÔ[óîêµð#µ ²ò+‚im^A¶­Š2½¥%NÀ*•è&ú©ì|CÀàÚ§•p gFýåfÞ…7ƃÊþ»°” Ù5{…|Ûn³ZÅ…Ý;Z!Ìoš3gŽé¦ï!PUlƒÝ„R*üŠ‹K„ŸØ`:Øí'ZR¢èÂè§r°ó ƒЧYû´úF‰÷±Ì¨£Ü$ü˜÷½Ç>ÔC:5é–6-;;µoêEÑ駠ňøqÓÎÓÍ.P½~¤ö1T?d8Bo¡$D ³× ^ µ'•…]ÍðäPÃÏŸú÷,7ó~Íx&”CÛísÂ`/0°[‡xÓGIu… ­E$‡˜MWÛÔ[áG€ª ´ÎÒ^‡„£Œèfµ§Õeð@È ??ëß(7ó¾üJ±‰íÀçíZ5 %²«¤õð±\Ý_ —TÐÏz+üŒÞ/É;²˜T!HBGôQOǵ>‘è7ÊâCý6è(n„a(u|¨Òü©Ïr3ï7œO-Fs*m£¨ÀœHSHb³lk›9aBŽÙù×ë^Üšê ÓsÈ8zô4‡ÝA°_Õ¼ A?ëÿT¹™÷ƒŒ%HŽT@­F±Z®zàh·—쿎cs4iZ ŠSoG~XîF U˜5¯HµA»Qþõ ±¶ee˜QÿÿÔfùÍÀÙŒ²›AG¨¥!¢ö͆ª°.è/ÀñFf|nËÖn£mÎTlõòiFzòG(K½~þ€F»Á<úâ{bo¶~,•^©¨1:ý±»Ånå¦?EÊ량G¦þOüë¥ĬOaG…|¿ð?‘}且CvЧg~êyŸÀr‰©ïÎO½Rñ·H–¹Ïf~.Òßœ«—rÙïÛDêì/Mi ê¶™ÿ€—W¹³ÆYmâqð7m¾îéžžù™þMÐwA×[_.Õ½_yÿ;ñÕÒµžAù>€¼l Ã$é÷õÛ÷ù½6ÊÛ¶Š“‡©¨Í{dê{:,ý}«_mßúíûÅÿ¾þ™¾»“8Â(Èê­ð3£·ûÍòµ8̱¥ s[:{ú‡ß‹Õ›÷ü­.bpžv#S½•ö¥›ve b€Ø˜H1àÜ.âsLåñ—†P‹ïf4WöåâßÅÕzé[CíÂöh“ÞüJßz®2¾^²¶ŒZXT"pdKeQ¼~_“²Ô$leìÜ» e‹Ë/ì®YðË‘‰c·ÊïÐOžt˜é°‹{‰'ï¦_ÃöÒã`­™øþרR°Höõåñ5ïP‹§iÒQwòÖÝÙ~‘îmÛGZŽÐ!,*vˆ»‡_,¦=ñúÙ~þ´}ÔæöíÖ6õѱjtrê(¿ SIä†1ç‡qxM‡âÔh-^½U’“+Þ»LDGG‹ðÈHaÃÚO4¢Ëé­é˜2Q©§üýohºGw4¿M;»Šqâ¬øfeÂ{ó@4SYb¢"ôàYàÇÇî¸R|ðíoe¢ÓwEÃñ-EÇv-ËøÑZ³¶-âp’Ê–¿Ñ\&`¹‡@ð>ea ïþ7ÍjÐhSf)òå RÄóýaô½DŒ»w¸h[ó}¡kÒö­Ú´ Û0*âÁ[‡ «µì _۾Ȉ0qßõƒÄÍy}äÿ|ýsÎàÌ„³ÒRf› ©­©™”YZt¶õþÛ·v™ 7kÜH?tÖV®‚ ²q–X³eøïWËÁ|Bôïéj4›€ùè¤ãý‡v2‡Aÿšˆ »v-›ê %;ð¼.ârlLq–wN›wç-7_ÖG”çÓZ7‡Žp-µ)±ŠgìjQ#WÓðÕ%¾®])ïSØ{¯$NoÓå/kLQÁGB€æhÞ›ÿ3´ô‚‘þi­›‹}‡\ÓÆ;þ ,4#Uä¤7払èÄÕtº&mßö½‡Ðéi!~ĉ)¤&ÿeÝ7¯ûÛöÑn\ªÎüƒH—^›”ÖÞLäêõÈÏ a}I£èŒö¼JæWôú¿Y¶N·Ð¼~Ho}jMf˦±˜„Îó*À¢zóÆ}ƒQõ°IUóe9w«&±ºpÀIÕ;ox“œ¦9â¼´—hÖ´‰ˆmƒÃ#*=ܸGçv‚.ÃÑÚ>:¾+7ï¤8z,G:xÄðòê×M!ŽçTZ~Éb"5œT*pªþ¡ÅŒ~}ºž¡ Kʬʱu*8¯ˆæ@ePÑ#¡ù›_,ïû«¸vðybHŸsÊ„©ì¡&m_N^¾Ø¹ïˆÞé#ÍÖ' Wê67¢#HÎß¶6ù¿cX)éµOmŠ¤Ý‡$í”®Ž…_%(á§ÔsñÖ x®¸ú¢žèå¯}´PACpǰ‹ôèÔ0Rz5qñuŒ¼mˆWŸ7龄CrÿØ´}óÿ^yá„?ˆ‹ºã´‚´WÙî<^Ô¢#]I$.’4¤Ÿk}ç#O½Ò«kçs¿ë*<úî<…À›s—×(!:ô¶SûÖÕÆ¡âw¿¬‡:ü2A=擘ÿ"×CGsE«f±ú^²ôË=Y#áWmÆ ÁGΕY8¶ uã%zxúCæéó~Z+n»Š¦ Âô4Ëš8³yŸò&þß´cϪ·§¦]‡GSø'Ž/¦´3ÓR.¡ß`sÙ_-v殆³SI –?Ô¤í£m‡É&‚4äHóñëúÂ~¾´}åql#ÎhÛ\Û“ý×€ò~þ<³Ú³ôš`È}§@ÔÄQ/•$ºuh« q)Æ!°¡¶A/ÿ ½¾¼©ÿ-0, †…,í³>‡¡Ó6]ð‘•'¹\Xˆ’Z°q#ï;Ru\ŒKkáMùËÓÚºYöÐuÉNRaÞ/RàŸét‡%ï{ô»:P'œ‰õÖÕ¤í£9ÝBì›l¸txJh7­RgVÛ×4.J–$é”zÃÈÀ_ùU ×i©Y1EÀÐ¥*Gzî3ã[ÀÐ%R?„v Î:£; ©ž–û™o‚VMã°ÀöDµ„î}–{~—Ï_ö‡Økß§þ1L{†+äZ6m¤ÿ†ÂŸð0«>b=„‘lu$ÓÈ—æ»;€ÿÿ:ž¯[w6Æé-1ê%wêcÂ’]í"íÌš 3¿¾£oºTªé™~5iûºwj/~X±IïôÑÜ߯h {Ÿ}†»°fµ}è`’ÕâqwÂ&ܰð«Ä6Íãôù ²â# »ªÜÎ}‡Åg‹Vc’¹P4‡aLßngŠk`úMŽT>dêݾí·WõÅöG\µi·n,@Ös•9l½$è2œ ÷è¢â2×\1­%õ'ÏJ®}«&bßÁê´hÔ;÷§ßaÍ|g†‰žÛ‹‘—÷s/‰Øsð/ÑõÌøP*zÈÓ:*%ý6XüÞyÓо¢¦‚ _“¶ïltòoº¬/x` Ö6;Äù|—™œ™m IúíÒ6éOh}‘&Ú›dèPÑËúÂLýçõe„MàÏJº§Lw^s‘¸ohø_Þ@fâŸ{Öé"¾¥÷ËÊ$Îu‚@¿î0Š[ëµíúZMƒˆ«/ê¹ÝÆãß~¯ƒ±]ähîoáŠâ¦K]“ÿ Ä/®¹ø<1í½oõõ{´fËpI÷]kÜê¿gÆ7)£®ÓU¿6+6¢¢ ÎR·mÏÃæŠF^n¼âß#€¥8ÒØg&?Û¦USÖÉ>MkÕ¤í£â …4]¤îöìä™Õö‘¥<Ô§8×O2u™–Oà¸þ‚&ù«/î‰Õó°L!Ç+šÊ >27&ØM—¹C¯á@A­Ñ»=çŸÿØ¡ÏÙùBmˆÐ¶yÝ Ô—øu‡„Z?X¯.ûc»Wd„Ù,eEú æï#‡µ‚õg£e¯À¨Ã@ ÉéýÑéêrÕ€ž~µí5mû¨Èž‚Ï̶vJB§ªÄ-e&´<ò«MfÏŽ¾±ŠU{ÑR‰Ô±¾Ç¯:uö 4¤2òEmdÐuFt…ª#†?ÎÓ ÔŸt8®÷H’vXu9­ê©šêR –¶T§K×nƒM©4{ÆÄ‰¦.õ«wP€uíO*˜S—ПA/쎌ll°=Eû)UR°Ólô’ vv= gFýåvý2ï‡B½›A#´žÍPçm¬êŽvSz{Þ2 ‹ôK­Ö)f—§Þ ?úèË»p_̷˧èçã0;eDAùUTž@Óêé{b cŒ¥ëׂ½\þÖ¿g¹²2ïHÔó_YÚOó~´ø<Ô"°mw6F0bÜtû¸ýf—§Þ ¿Š€ŠÄ¼D^A¡¾óAEþÁðŽve £anÎÎ<b1ï”äuO¥ Tý3ï›ÇKÁœ’¤©?¡«¬ý†§BÙý±m¯XðËF ¹ï2žKšˆ²Ô{áG½`ãjmÓÖï8,MI“h#}ë&ÑnºMI¸'Bõzë&A_÷TEfÖ¿Á÷ô˼ß0>ì6³ûß»ð·MJMwÖ „6  |ã‹%NðíúÆl7ã-¢ù®^ ?÷Çq3íˆia0˜¥sڂխذKD`„Ú,6TŸÜÁJo°Óeð@ËÆ1" ›¾Áì̪£ÜÄCÌûÁ\ãæÓ¦i•”f|ú£Ós÷ós27EÚ€›öÉ1g¡æTœà0“+^7.ÏÜ\N¥Vo…ŸûãÇÔm;&cñ1ý¶ˆ”ÅÆÄv™lŽhÂñ¢}“7½4ui”%Øè vz ÜCªû³â›èøcÝ–fÕùr3ï;§šKßki) M¾mOöQç”·¿vÒñXÁìHÓµvë^1é­yÎ/¯!RçhÖ_IJ:Hºë­ð#ШÇkÑ…vဵŸ»l´mdáI?—¬&›½²(m¢…ÎJ‹ÀBá-cuz Ú©ì|CÀ“ºžÞBDa.•p¦º§’™]ÿžåfÞ÷wB9VFÚÄù²$_ Áwâ¹Ùsó1¢¢MjêþÄ!ÎïÌ]Zãù+Ëçœ0AǾ-Àfð_c+@J{Ü«)tü#Ç÷K’å†Ì´äÛ2í‰5ÛX¹² «x_o[U£÷K„„^XX.¨=Ñøµ‰RqìF ]Z4µëõî¼¥ØK2Gtl&""ˆÎ0n¢ß(KíRú¹¸<‰]{zÞDÇ™ð&gfý—/7ó~0ÕtíÑ2+uâò0‹­—âp~6#ªñ¯ÎQèȭ쿪ß"sǾCâmˆûü;ó5:®7ö×­Û¾Oßøà—u;ó>Çvó°-ÞÊÉÍ/üLX¤›‡öèÜ13mâþæãmü`3)¤ ;t2cIDAT‰M\æLpÒžŒ´ï" ½p,X‡@ ljÞM¢ E~I±X½e·Ÿ.w_;PÐuá¨ÇO ßêÍ»E»F²hÕ'wëtº„5Ñ_ÕÞ’>Ð\б~(݃ Måy =v¡ÿëDŽ·ø u?¢îꞀTý—/7ó>¡­·+¯Ñ‹zïJ—ŒLLJt² äÑoYí7?¯·`¥C|Kk³¸h‰=lUQR¢è‚q7ԥاØ"Ir>Œg¦ Mj…ÓJîéÚ¡­D[5úâHðáty'F£›ÃZ6êS|,Ü2´k“â‘#Gº‡£³}IØ8Á$üÜ ±Ss`A2=c¶Æwg¨~HøE@èEà@P:´°0B´Œr ÂKtxðØqqÛ•ŠÎ~îŠPSJiއTp4 m¥a®/R§è$z‰nµ˜¡öÄâ~MQ:»¯¼sã^Þ£–žÿ–¿âT ͨ¢¿"èÒ¶±p`Áûš-»±÷dÝÔ=ÑÈú¯¨Ü •÷ kâ4ðž‹ßþÆw®¾ºŒôä%(Û’„ôô6¢H\}² ø’õ;öcßE­ Mp‡„e¢R;T¡®¾é;ÕÚüsR?>1ujtÞ‰’³>YÜ{Ðù]$ÚÇÓ›ƒžóq¶% =œô nÝs,)m³Ú†OøábÂ9³ŽÁ&áç†Bu8?‘OÌé—ð#õ ›Í¦ •hœ®^ŒutÅÅÅØ„Õ!Z8ó… œpj€ã°ËnãE¿îENñØ¡>0;$Ð:.2g'‹S2¼ ƒâ¹]”‚_”ˆF,4ÔP݆ÚÓ Ž7Çsó5Gq±w›”ú˜‡YÑEÅ9fÔ?ÑStj ì‹=8¶§¶êžè©­ú¯¬Ü ‘÷ wâ§¢¡û†ì2“’²Qþ·J/Š„Œ [FB‚ž©°Cðâ“Oæ?öÒKƒòLÁ>›c—¬ÙjÅAß*Nt[áüÆ0¦•J÷vÍ9‘¯quøX®úgÖQIÓT Æf»„$¿¦µkþúôÄÄ Ùf)…5ÒYy…Å5þ!W £b0òƒ0‹‰Ž†à+ªÉ!¸HC}:È<²¸Pä8d±õϺ@"ëÀF°³mŒåPAšá`zŒ°PäêëÍlzM­%ºõi4ö%Ú5ŠÑ1¤†ˆÖé%ºÉJ‘ÊáÓNÏ‚ü¿1¸qù“¬Ùq š4¢ÓŒú'«â¶àh+6.?^,¶°î‰ŽÚ®ÿªÊÝxŸ°7øŸÚšõÜø?ªL¼Ž<ƒMøé à¡ý{—Å6köüݘÔ?dðBs4¢"u—VO.Ág¨ÅŠ„{~6uˆBâPµ¥°D.Ì… òOðP½ºd—&ì8I16MDÚ$]­I#<vqqDl,.€ “è%º‰~ká´&íÛ¾u-Ò"‰ï4þ¦mR|OzT¢3þÌŽ·šQÿD_u<@Œ–…E"¯È)N¢_ZRäG óÜ­$™bûãêªþ«+7ù[,õ›÷©Þ þ§v¯ùS¥ 6néÈñm@—°Û5¹ ò…èãŠc-Ь-­²íö!hEƒß“ð3˜R[üÕgë;õ<÷èÊ»šBøù%}Ü=`­ >£1s5P‹Z¡ü,.‚`TDΤj„Å–$icU³ÑAù ’ ª‚ˆð]ÕI#>|ãâôÑ D²Ð3cÔGôGMu:sׯüei‚™.7Þ¸¯KçI‡NÑÙï²+O€îXëŸ æ-„…‰XðæFõú7“ê¢þ½-w}æ}ªâ¸cÔ®àÑ“ßÈ›ØíèÍ A ѶÝòªŒLÂ% õð¯ì¬6HÒƒdà¯!Š!äµ0¡E»õÆi>T‹dZˆž¿>‚ÆÏ©ºF†´ã9„ 5@äÈúŽè°È4‰‘(òtú0ÇU'øtµ§{ÔgŽ¡ á·aÇ~iïŽí @õÈè2 á Î]ï F§‘è…úd¤õO¬+¨ëú¯«ræu]v¢ÁàjOðl|O$²«#‚Iø=2bPeÉ—Ÿ}pcâ·cQd“ñ÷Žý]Š@ rôQºžK—@`>°¯ˆ aJ{þN'lžLýùÑHŽT™®QŸËú”Tœ¤öŒŠŠÔ/Rw’?Ñç¯s-œþUU%wùü¹t rIée@s³ò'¾Aƒ^ïDïi;]ú5£þ‰Àºâºª£RêªÜ”]–ÝàÌ?åP{r‚‰ïêáß:B ˜„A@#bÐ’cÇçm]»úéü¾“ÿƒ3î¿q°ßêO£Ð?H@×,LDÁ¸…Ÿn ÃÝ  t^Ðõù¢-ô¹ÚfŠvÛ •&­;¤å 4ú#cz®…í®Ñè¯#ܲŽk–/þOAAÞI¤G&Æt‘$œƒiä§×{)}ÅD/Ñ-ú õtÝõ@õ^<`T¡!j»þ©ÌäŒük«Ü®<õ¬Ýy×EÙ þ§v„ÚPl|ï‰ÿÖ ~ ?tÛ‰©Ú˜D½¡þ"&-ZúÍÜeš4y ÷÷‹Ï„öKþŒ©0– 4–A ¢_ Œ^h¾‡æiä§Ñ?H=CúRFÊS¿°jƒF~”§kÇ›.|IàÙôѠ˲“èò×Q—>üU›ÿ”vnZ÷ùï˰•™µº®"ü¾4Ò &áGôèõŽ_V¢»I‹–´ãÃfÔ?ÒÑë¢6y .êŸÊYÞ5Þ§r{òÿ?w¼Ií^›Ï÷šÔéï1»Jð[øai$™÷®4‡šy ŒAZ B `þü÷ßýàªÛî":ÿ‘}4GÃbtÙŒ9@j ô]0 xhÞOzPsºŸË(ưõWøv]åF#jxõ BŽ„ ëÙeC4ùëhŽªB•F|;7®ûrÑ‹4iq/üè—p%| gÂ;ÜßêDEà _ô9è×õ®5«þ©ÀÔɨ 0êÔ•_àëŸÊV•«­r uQvOþß·sÛ»ß~ø_RwŠï©Ó¿ŠÊÊ.ôð»µMHJ›ˆÍR-ÑÖæ3'L0c5í3F‹ë¢qÅáj†«iß!—êÙï¢1²ÕÚ¸{§vZßn$3£#;tdàbÌóï‘·ß#?Jƒã¢È0€1ÞQ_±pš¬ÚȸEU'V-ùñƒ?~YJKhKwª—c¸Žâ:‹cô‡Û pÖ;(£uD±½ú<·Ï !·ËV[œ™õO%7ê:<`£žé×ìú§2øâYn¢§¶ÊþwþWޝ[ñóÌ•?~¿d„ïÇLžÜÄ™¯ü…­¿R2Ó“'ù‚?Ç©[üùÁzq¢*éjz Šò?ŠCª8šÿ¡¹)j¤m¸,`äe›Vü¶å¢«¯³ýËÑÈÇáãÒE†k㢥Ιà¨AðtåŸ=ý¼½7#|ùgã}Mi 0Ú …„ƒNÉéTòvoÙ´ä—ó,,Ì'u4öHØÑEBð$\ƒi¾äè®Âz‡®† _³míšíý¯6å„ú Dý%åë¼ü³Nm þ”¯ïòÏ5H* AË—³ü³/™—/kùg_Ò4â”çZγwÇÖïþæëyùù¹´¡CÀøžÚ;4²íŸAÿ†¦ŒQÉ©û0®Ù€£(®6©øÔà‘Ð#ÕW .ì='㢑 éÙ£Ï>·w—Ó:ŸÕ=2*¦yXdDœÕj G«eJy~H8ìY\\Xx¢ðäÉc{¶mÙ²mýï»A8©4i~ƒ @jŽã"áGÂü(L°Ì÷·«¶Þ’xÂÖ¥ÇygœÞåì³#cbš†GFÆY-Öpw*|Sÿ@ÇWQÅ%…E' NþµwûÖ [Ö®Þ†‚× ß'$§}k€î³ÓRÚ×°ëg M£’Òž@y*fQ—n jZ¤3 ©@Iè‘$aHW$.jðô‘!~©áÔG ø5¥\H'˜1D%!F—1WJ#;š×#AG =‚Ô(‚«ãzÖš ºêœï鄘Ãý„fæÉÙéÉ/,LEMð[íIÆÛâ_ËRö?„)³é öŒþ&DH :PÈÃÏÔ¸S#NÂOà—ÊA& ¿† øPLݸ0#U¦1êÓ…ðLÂ…¤‹p ÖHs;£žé…Q>¢›ëÝ Qƒ¿1ø¢Öùí[”ê82S·{ÛZã_kð5˜"üìö{‹’SG‡ùÂqäÌÜ Ý>1¨?Î`pjø<賓ƜüÈ8¦"áWŸ… ­‹Ñ‹!$HR'žé½1ÏgÄÅ« tF™¸Þƒ²zê”(ƒw ©U¾G»&%&§¿ºáFí~Ù…(¦ ˆ„”´G5U›†ù¿…­Å=& ZÑê0Rs’À3~ ÁGþTãÂm½vÔ5ž  xôKùш*Ô×{¨ÕXàé5xž~kïiħwì…v‹$Ke¦&¿ø¢rDÀTáG„º x¿ Xø°‰s€D«Ñ’ óz†à#r¦—Ë•lPý¥Ÿ 5φÀ‚FÃ`øëCð×{VZI®u¾wÍñáDsŒøp²ù,øX»µ˜t@„T W  ³a |ÔŸßâzOŽ’¿6i Ñl4ˆ$ìè2Þå1~áUoÑЯq‘ 3.ã]}À¨c£Îj½×—úôµµÂ÷´ŽÏµœA»êΫhŽÊLKùÎWÂ9^p!0!a·¿qÀqàA,€Ms;0 ¤¤Cš¤e!S2Ä0ÍAÕêN æÇ+“;“ ¹zÙ]p¨b‚„ªÚ#£¡Ö{í!œ9’ïñAÅ@cÕŸV+ZLJnõ~4)¯QÏñ'?øJU­´˜cR&÷Æ1AW‚ÈÓ!üÚ"SZ¶ÀŽ` BÂ/¯tËÆ=´ÇÌÔ «ƒŠ@&†`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€¨ ©*ÏÚôûã†V-‡¢J¢§¤jm…$… !å IìBþµwï.¿JöÅJmÒTy%&§Þ)õJRÒ¡ºÈ?˜ó´Û´T–Ý«ÆØædŽ¢.h}騦ÅjþUÓzáãiÂ5!NÊ’Ø¥jòŠÎMÎZ>r䯒º ­6ód>­í`àÓÊ©c«qSW¿k†·ºLÚ“%m¨Ð„ýÇúk<¨bõÊÍGV]Óê-[˜x©×ç‡ûBoBrjG¤ø*Å•4ɉŸlܬikm÷®Ý~o‘/iz'!)íäýÄìôä ªŠ£iÒÌÂm+°ð+ÔÉèUáÎã"S*R—«V…ß”LëENá|ªÈ™7 õh#Òðëvªþà;r6åLÊÿk‘çŽU¸ß 7uɧÙ_-VNlµŠ°K_O·¹2²™O+C=!?ø4!%ý~¡©ƒ3ÓRîª<ö1ÙŒD|Icýµ-[­Þj®SÓBÎ]‰–ÄRU:šÐZàç(Ѷ­Ñ2±ª°•ùÉ’5ù “%ùKIó5IDã#~6Ëq`e‚=#ª²xf¼·H¶uÈó¿f¤U—i a¶JJ»¨.i¨Í¼§½Ý¸1„Ù{N͹¼s!ø*£þMÀ§+jÑÖɳå'50Xea+{_—|ÚLòYc“Ÿ?Ç¡•l’”ÃÔÓʨiZÞ†Ÿ™6ŽFst…º»Ñ"iKB½ÞÐ?)3¼KãÄ·{¦7áË…‰RUí…I™òÀŒŒ¶·%&f”ó¯ö±.øÔn—TötµÄ€âÓ,ÇòiSÙ"Fihß‚ÞЧ°Ö…ßšáñ½TÕ±ÐÅùŸv§–µ/R³ÛGJv;}°>9Rí —ø;"w¦“'Ý5ì%’$½õËèÅ÷±‰°ó)ÜX{ú¹ŠC{ÁÎ×$-ÊÓÑÖè'^¶?vjÍù²¤}>+-e6¥CŽzs”e¿Z„Å¡¦ uTfZòÕ._!F?~¥ªª“ñÜþ[eYž ©j±ð,èy*êËs‡A×ë©_Æ/¢1*9í !KŸJšv:A{*“óÉÏӹ‰o0Ÿ:@ÒðÃôªö…lµ¾:Ë>a·61%m4>>Jç4¼[/I–!¿_HV¤œ˜QzUHÿÁè¯P–ÅûP÷Ň³Ó’¨bΜ9–…l[ø?ŽÇtÏ"I’_ÏH›ø‰ž–#÷yÌç^R4Fq—­âÉ {ò* ÿØK/EžÚX…ašæœó_‹ÕzNKk¿í£í“Ï€àû¶X%Û7 á:('¿¥FX“¤¥NMëIK–úó•ÐyuV­ÍÐðk±`êÓ ÿÑöôîN§ö5Ò™)blída™Á—ޤ"0c“ÒÚ+Šº‰/”#¤®P G^ãF?=i”MáÔìø^®†ð½áFœòó¼ÓÚ@ð½ú‹e«­ê»U>}Ýž´ýø´5V“óoͨՑ_‰\4 £ Q˜ç45iíµm??wnÞ¼ršÈé>Úž¦:UѾXq<nÛn{ψŒ†µ£d‘‡g<—Õ¬Ëaäò/4bû0Ây°ôÕæ“S·kb×è”ÉWCd½£ˆÔ1öôž3íIëô0Nín˜3퉣SÒK£¹~T‡F£«-#ÅOF¥LÞ#4eâè`Úú8‘ ™éÉi¥‘?żÀMSGãÙ-´N—ð–q­§?üpqi¸J~¤e™©É ÏQ)é3$¡fϨ›dÇi4:£wÀÁŽü‹å.BÙLjO³HΓ4Ú¥0h¨Šxá1ûKM§Ù?¦ ùf!©ï  wf«?F²t9”i[f¤'ï“2i ¢:Ymr/7NB<2*9u@‰C%ÚîÅe¸?g§&ÿËxxbêÔhº·(-!eÒ+@é|t†¾0n\žÆŒßcâàs¨ë®f¤e¤á?&ý ë§I÷+?ïªû­ >506hó†OiÁK Eèhh“ÒÒ`ûï@ w,ÍâÓª¾ ð)} ‚O:â_ߨµ‘ߪëZõ€ZYy,ôÎe‡ÓùBå!þî£(P×9ÄÛhÐH'Y/œn8×RÇÚÊ-p?»nºÑHÈóÝki)"þ£ž™IIÙhŒç+ºÀbÌäÉMöZÙ&ÞôŒã¾—´³$!/v?ã&¦IøÏgÜ÷Àˆ± / iÞw,Nú´zÁJ%Q&}ŒÚþ~±” /4ÐñÅ0òÂHð äbiåò;•û,{ÒŒœ³OªCìvM†ÄÄ\‹eÒž)©WIŠEÃ÷ ÝÂp¤;è8üú3×Ó³áÀˆß!£žÆ3ýZdù}ÏgãÞ©9Dšce›å³—<üû­H¥>läeæ¯ät’êÖk*| éNZ¡ª(¯¼£JÚ8*(TùNØ>-MËËïâTÎõOO•Œï|E öF~Šx½i´opÚе#Zw;wÞÁÞ¤n³6éYjðRYðCvût0O9Ю`”TrêMé¤C>è8Jñ¦æP÷Ë.X~+BlŸeOÆH®"'Ù hÊAôo×®dá±­˜r, /‰pÜ­—dÙ=NB}éôLkÌx>Wq_&?ÏpÅBÑÕ\èK|޹¼M~¯Ù4K–Çóßo%.Uºì 6ù „œÔÊ2ay¶:)ï@ >”¥¤]†HwQD9Ä— #a”ROùb­•áG‹fÙ¯{–ÿ#‰³ñj)T¶/á×%`ˇññ¹ØQ<„¡nÌwH÷¼I™ÖÁ”Ÿ¼I=tøT Çȯ|úšQ®ÒýeUwkdÌàS=}/¿ ƒ÷o=âSw™øÆgÊ46>§RMDí–[,« ºÉݨWÞoE#ï_âzmõ Œp®ó Kó0ÅŽ’nªEl¦÷mä_g©Ë´ƒêÏ—Aås§ä7<׻߉Þò`Ïw ×m=ƒF²FåfÜ_õk™§g³î_·?uªÇã(gËŒç’Ë–xæh‹E¾‘%é[ºgU¡ž„xûœ,Ñø1˹<ò #ývˆëFmmñK(ØB¬ª£]‚}ÒÙ™ö‰[Œ´a t9ºFîgã}…¿Vù¡(›–ƒõ¿CåFk'_¬0œ/1{{k ùFTÔ)òJøù@>F÷uÀ§š¶ ˜5·ÊâÍ]ùBwMâxó]Ôw>­ ^¶bÊ4bñÿíê¢å½Ð¨7ö?¥ÊS@ãyIå¾&øX-ÿuì¨äô 1ÖÈŒ|QÔ²DqØ¡ÂÛ.â[Ì¥h´ò®êÔACÞ;,ª¬°ô¤ãž70Júj¢1Xdÿv¶óÐYšª¤" ¾m—³ Û4Es܃4§`˜!µŒË*>rò\ ,Gg¦N\j„3냶ç‘ý#ؽc‡ÅÚôcEFhjqß0§¶‰æëô|4‰òÝÂR³xi‰\˜¯å¿‡÷×ËBŸÔ±HHNû ÃØH¼ÅÆ&—õè°Ö ?ËR¦$…[¢ö+y÷AÙ*⋽- í€3*eÒí’ª.À<âofà1%#ò4§Vt¦·4øÂõ_ây§ø4Þš¼(Ë‘¾Ê¡hÿœšÚÚš¼$[™ÚQ’çÍJMšã5í^ô껨Ç|ê%L¬jeÎAݪ¡ÃoI h¥£”k±¤àÆ|%”w˜³ÒZÃôªÌÄD‡Q€0«å-¨ïh9Ã\ÃÌÛðóüÍHO¦‘Í-MÉRö…àû½ÕÏ1Òs«nɼ]²HWãÝõÅš´³øPn¬Á~ÀÆý<Ó2ëË0'%¿†é4‡rì¨æ(<á2Ë)[šy€…*óŒ€tc2tÁ»Õˆ×øÒžgýd„EZŸ¡Ó«Q×|½'«Ìp[ìõü”yy±#÷ :.ÃÂô¶ÊUħRô¼›:ñ'XÙ>ëTÕIOoåéçÓ½TPÒiÒDç¼$`κàS½%]‹n[žªIßPÒ U­d+øtŒOõPM$o¾‹zͧÕàÃÞÞ!€v5ðnÕð–°ZÿtN}ú޵ø³æÏ[úµOk,ÅjÅÓ¼ÐÛ8•…ƒ‹ôÀ³S[ÍxæÉCåçÁ<ã$L™V$E7ý—Ÿô gÖ=©tµ0gqeœüý¥%!#Ã&²óâ2íOüeÝþ¤3e¶å¤ïú“†7q%[tˉÿÕë4§Ml´åè‹O>™ïýÞĭ|ê .¦zjGøhýF,UÖõéÛÕÖ6¿,Š 7õÉ™–{TM};ÐØÂ¢ÚÇPM~Ti¿<䧇 Y\fÛ<¿’ãÈŒ#à3µ&üˆBY¶ÆÎ'¦îÀQßû}ætŸ¾à ´³qÜŽ7á9LÃBÀù(Fi‡M-µ$LHPþcjš•$f·ÏÑ÷i­ÄÛ´×t.£¾™¹I)Ò™€•%Ek+ò£ó#ËŸDQQ8zWY•…¯É{¢h©.ÕM‚=#ªºpäOØVö¡W_ ÷&_oòkhajeŸ'¨k®iu­SÒ>ÃnÕ2Šg¼Šî¡J]Ùbp·7bOIïv&™!|%–ì}¬º ‰ã—úÕt÷“º£¶~ä<å ë ÕéüËhsq¿øt[˜3à‰Ä¼ji°WéÓØ§±çAÀ”9¶¸[&ËÒó²jÛ†S-¾À1WçTDTbò ±»Êæp›í´×ìãÜ’ãh*l3§%âìÃs<€B~ßYeË„™©V—OÖ¢C¹/ãýPìŸÚÙðhÒ¤ÅÎñ«1vçIËHMúŸá?Ö>¹“ÃáüX’Å|¡•d¼¯ì[ü%b ¼±õÛ Úü»ÛN›šô!…O|:ý2¬ž‰‰“#X2… ´Å­tDéç´Yxnƒ`EˆW$ÉÖÇ2RÇÿQ>ŸÊÒ(ŽžqôØH§ª½3 •ß§´"¿„§Ó¯ÑœZê¸;>Eâ èMñÖ÷T´mSˆ,®Å…°b£Ô¾åýž»CI™Ü§ž¼*b¬Ãpˆó¿ÑFö¥°Hû¸Õjyèuû„xv»ÑiiñÎ"mƒŒsD±cÔn¾ñ ZùEç}h®,Éÿ‡ÛßOHðŠäÒ@’´RHÖ+k*ø(6´[ð!Icì“;Ô$˺;Ú>µ%ÎËK¯‹¼ržãïW–È’å:4<þíR"‰ÍáÖˆ¡Þ>Â%¤ó'vþ1ð§Ã„qN¹Ëã]e¿šTrü–8wþ-Œ¤œÈ+IýÛû ^Ш®øð‰Ÿ@ľòÞÅùêÓÐã| !t!ÎS¼€Î}$>¥pØou°¢(Ÿ!^M¬Zÿ´ÚšôŸ–r±%RÜ(iêó´ûh°aú›8ÄátTàˆ+RI?ᤥ-þ¾;h8×E˜o`»¼”ò´V•Fù° )iéØäàvÔy™ã¶(\e~6YÚn±…SGzÑ‚eœÑÙŽå7•Oç‚:}`¼õâž(g”Ã!íÿë^ÏpNM¹µþžhÒ¤À"ä/Ðáè…«?Z¬¯p¨õãžaéÞY(p§X\þ=?{‡€Õ»`æ†ê=ïàœ•Ã[ïF÷=l{Ö©&©£²ÁRfËHÛcí?Þ_ãíÅôƒ7%mz« œŠBç Úü©g‡ÓîC§#—pŒ³Ð=4´kç?pâÂd4JiÛAz:@û&0ûuؼù{ì]:{ÎÀ~•«ðî.0ú­8ù ´F#ÜœËzžõ/ÚÓ’>ÄÊÒILIÿ‡¦j©D–Y­áÿ²„)΢üâï‘FÐM=ÚOñ1†Ý$ênân¨pŸ[‡¸ p.¢päH*ïí›Oö×[þ[SêLø¡§ï(î3ïà‹½#ŸfÒåh`ž‡Ðù_YÜm#þŠë=œ‹÷n9³ÏW‡¯‚Õè¯5-¤þ±—^ŠÃ]ÝÆÚæ+}ÃfIÛ‘ðôäAäй³Ž#4¢ïR/‰µø uU¼F^E}”‡¹›üЇ]¸l‘¦ÓÇAñ´e(^ÏU#,ûD„â@Sñ%„Ùu®ÒtfÚ“Ö‘N?«Í6tèÄbôÐuº\ÑNýµÛߎÀәі¨Ï²"vEZ¬ò a¸!ÄO…ä;³ M²™Ô’l> 9ž¯À«kqmƵüúêÇmaa]’µÁI÷+?øš¿Õ*„ÆífP£í“Ï@:g¢S´¨ºô07vuì(:n@}x·g´èÖÙiç‚p¦öôóöþg_Ó{†ÿåKªÿG–Ñ£PÔ ‹¤=Fé˪‹¡ŒÚÁ|át¬Z·µšc:èIÇ7ógö¤Ë›4ʇ÷éÙY|…ªª/ã›}¼1':V.C³ž¦&beYs¿wØ"ò!ÐÜF<ÐúÜm VƱ§÷¾7J¬tšŠîŠ”’ M²<î9_høñ¯÷ R÷Núøc'¨XXzŒ üœÂ ˆšPö¯Áä3Ú,Ñ‚ dø“Ú®é±ïpœG6JK6˜ùúðhY@š,/Tõ•QO§oªè‘÷'þjÚZîü£qO'š“€yÊEôŽºèGê“ðU¥£Oú éz±÷H8˜߱ȡøå]–šÕiÆ8ò¿ò©€†7+ñÙ7ÃmµåÓãgï ÎB/-½ÊE$/reä‚ëU ÿδOØ~ØPI¤ £7ùSC]VYRú#÷Œ€ú·'A‹A|ÔÇI]êϦI—íç±ö>s8Jˆ…¼v¯Û8 <ñ§ÛÁ࢑&©ò«;7˜c|Ë׳ÒS\‚^–ŽA ”³Õi×^Á¤"E²—PÒ˜j¸½DHóq{=»]5i¸Ãùq“™šô>¢¿OÂex§ §€¾Åg<“DpL¨’»,áˆE£§cFZ´IWÆÅ†?äõÓÚ¡ˆîÈYP/“_BJúý™nÉ|nü*\ÒèätICOG‘»øÓ3 ¾¯:ùUAW@¼Ð»ºs÷À|ïºl’íðËp2SÖ{Q’ôoUH7CUÒ)Ìf»ŒÔŽDH[çÆ é(z·×YÛßÖoŒät:=¤—ëKXãõ5®ÙiÉ÷W•YÇA„=Ý4êFĹԢI°n«ØAur D^FZR?#}ý7HŽª˜j~[sä •†¡¡†–íÃêâ;”·¢4ó}Wo‡IÚPðÉÊê¶åãÒü°,äWJÉKàÕKkÄÉ"kK#]hH:¢!Ùg<ûò •áTÐ[^>5O©YwÀ¤ƒ‘„Ññ;] —Iè•qmlId(O£ŒG Ò(χ‡é?\Œ‘÷¡7½´|th ö ÜÑx©‹ WÇ,[ÉŽïÿGÏ# hY†â(™g–§fÙ“6ñ ¸†A–¼réB£4M}Ö¼¯»ÃðW4᧬’èÒÚ2ñš¡‹T•`žEy¹%.ë,M+¬ò#¢]‹žfâ‡Ä¤îøÃ­ÖÆã/ïÞå ãTòŠc~4ox )ítò§–æé¾²t0Ó ó[è|@²rC£çšO¤Háênü=“nÉÅ[Z¯G#§ÂhÁE3Þ‘€îÉê VM›>B£(NGþ¬º‚¡ÓvÔfoÓ/¬ªß@cyOEqµöÍ_Â8¶Ýó*ò¯êÐ'Š$ý“ÂÐ|Ò&[ÃçU‡ø:19õ¡ŠÖ­ÁˆeÍR¶®CYH ½)¼Eì›4ÂËWNöikí‹‘WANá]‰AUÚa_6Þ£c~ ๟ù†00Ù‹XËó¨‘-K³îLHOo¿m&§žiø‘e„ãOÆsE¿¤ƒ§µOF«K‡v¸ p¥Gs:FZ†ÿ˜É“›ŒµÏÐ oŒwüËÔį¤Ùð&?ŒúšâMØŠÂÚO?Ê›FWô­–O߆ç;º¯(&M¢ù9¿ÍõU”ÑWžú†+ýúEGö ZŸR ñHL/a¨·;㹤鉙™VmßáÁ祤m«-ûW,C¸Ž¬1é’ò•dUm0—w§·Å&•z†ø›Ž·ùq8F ˜H|:í:M¶þži¿7˜èbZ.,üJëžæ.„R8¯îP+9 ×X,ÒG4Ù<&eÒ@§¦Ž‚r¾-TDáúµqŒíMÏm¢¼a!³Òñ&/Ã0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#*ü?æÏòpŽq(©IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-flowew2.svg0000664000175000017500000010223613553660046025750 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 18:02:35 +0000Canvas 1Layer 1Compute Node 1Linux Bridge - Provider NetworksNetwork Traffic Flow - East/West Scenario 2Provider network 1VLAN 101, 203.0.113.0/24Provider network 2VLAN 102, 192.0.2.0/24Physical Network InfrastructureSwitchRouterInstance 1Linux Bridgebrqveth(1)(3)(2)(4)VLAN 101VLAN 102Instance 2Linux Bridgebrqveth(16)(14)(15)(6)(11)(7)(10)(8)(9)(5)(12)(13)VLAN 101VLAN 102Provider networkAggregate neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-flowew2.svg0000664000175000017500000011064113553660046026427 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 18:00:33 +0000Canvas 1Layer 1Linux Bridge - Self-service NetworksNetwork Traffic Flow - East/West Scenario 2Compute NodeInstanceLinux Bridgebrq(1)(3)(2)(4)Network NodeLinux BridgebrqLinux BridgebrqRouter Namespaceqrouter(9)(8)(10)(6)(15)VNI 101VNI 102VNI 101VNI 102Self-service network 1VNI 101, 192.168.1.0/24Overlay network10.0.1.0/24InstanceLinux Bridgebrq(20)(18)(19)(17)(5)(16)(7)(14)(12)(13)(11)Self-service network 2VNI 102, 192.168.2.0/24 neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-flowew2.png0000664000175000017500000031470013553660046025736 0ustar zuulzuul00000000000000‰PNG  IHDRàgžö(sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì]|TÅÖŸ¹»›J½KQDÅ‚ˆ¢>(öú콓Pž¨X€d£+I(â³!%ÁÞ¾'–§ÏЍ(‚ˆ…¦‚ˆ€„Þ!uoùþç&w¹Yv“Ýì&Ù$gòÛÜ{§žùO9sÎ4!Ø0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ@“B@6©ÜrfcažÜãU¯Ñ¬“«ób玒˜&–‰ û=O¶*ôõv¹”­S<«B ÄžC=9§šâ˜ž1¿AƒD:c¦¨’äñ|éÜ"vu ¥l²gÔ†P"OÏʽÙ0ô”üœ¬i¡øo(~î7®­Z&Sìô:„RRֱŶüôt¯Ý¾¦ï÷x&v)UˆHÿJ^ÎÄWÕŸ‡ÿ~[ÅæÃñ\NØXõë¹CU éLÝ1Ù3bo¬Òˆ®š¶‰bµä†0>,Sõ7ïÍâ®K;«< áÜ?ÝóÐÖªÒ¶úÍ¥çgfnªÊoStCUþT ­òîjŠùFž=Þ*vW½êš+Ø‘ÕvÏ³ÏÆ—nÙû¢J•–›û~cjx%EúSÂ0n²c ÒÇú­"-3gÞ~Š|QtióvMr™æl"SHoÄ7„¢oÊÆsoï‘æÎ&ü'~yœ]^‰e‰¿1µ _yHuç½¹¹½ŸÉÌܬ~nóÎï¬ ãOé•_ÀÏ `þØž¨)JM6Öp“GŒ(ÒøTH1¿“7#hãlÈù—RΑR™F?!å BÈçíTÎ1tH*o}Õ0ŒšMOH9q®£§N‹M>Ì…Ì—R¼x×` Ò¿éÕ ¿Üãy¶y¬‚Õ(Û„!ZëÏÕ5æiîܬÁîì u.§›4z ¸&°Cõ|yy¸¬šù0˜îŒìÌÿø:ä‘Üóu]Láú¡æÎ„ûýýT÷76“o÷êü55÷@˜§gMècèê0Ø9²DÝ;˜Ü«¸4¶6AÐvÔó«ÓÝã®ÌËÉx¯®pÇ ÷ xÖUzœNl#Àp„åC’"©è"Œ&&‚O›9[å%"F7DÿPˆJóä%…âÏßáæñÌŒó·åÛãy)aæÌ™ŽPüÚýP8ûw}¿çe^"çõD‡ÆåQm{ ïúÌg$m¡¦tS]7]h Ü ¡M:~|KzÔTGZÖ„c¡êi:‘„¯ŽÆªâ¥îUÞßÚ$ôøÇ׿YPj˜Ÿ[ëæyÙ:Ätp¹1í ¹$?×=s¦× ×¼;-+§¿0d*ÔJÒŸ(®ø,ÿÅé™9nC7Kápc´ýŽŸý‰¸ÿß}Nç%S=cÌHéîì{øp0Źù9™Ãìþé}xfN7¯4>DúÞÄ$yQUóYþaƒ~Æzr3tÑÂî‹pnÅ´ Å©\}vïÛ³dÕCp¿Êðnë7سvFŽûPòoùƒþzJ^NÖd{¦û#9—꺙îÎí‹ï$ภlç“ÎŽÓÛ¨~[F`kªÁ+›4Ï„®BUsHM^à-è¸q‰P‘î0®2»Ï$g³Sžöܿ۲KóŒ;R¨Z.XÜIÞ ‡ ½¸-T\ާ{ƬµüÕד˜0êÎNHc­¶‰ñ=A‡¹RØÂ±:¼‰n̤?’;Üä:üŽ…J;epfÎjÔÏ\†35×ý·•?¤õ1À=Ì!7MËó£eoõä§©:4 r]~Žû|r Ö&ÈÔç¥ê¾ÇAÇÀ’-{GÚ[1¥ñƒâ”éªf–Êé 25ã ³|ÊéP~B{24k|?ÕÐîG]¼õ¢%Êöv´ÍW(ÞêL\¢ü´´P¾ š¯Ò µ§àÿöêÂr¥~™ŒKÝ:Vèêݰ¸ùøÝŠOQä]7úáûj!•±ùÙ™oZnÖyû0í_D·ì­'ÚB>â=ÓŽôÙ_[öáÔ +L8uÏ cÒ@r£77ôž>ñõ¼\wŽåžî~üp]”" ¼«Ú£öJªÿRÈŸPCþÛÙ‘9Ý㑺忱?«q7våLàptˆGø»™öÒ¸!ïo¡Ñ·F¯ò*ÚÛ¨h|§éÞÒþ£:øm_Ÿ–ê§í»ùфᓠãÚ¥æÃ½ Œo(TeWÛüšn™/#̱R‘yQa¾HàxJG‘Æb{zˆ[}`žÝ>_²ê9äu¦æK$È‹^¨'aÐ:õi˜Ôm4ýÄkPëPéOtmOAíZN'E'÷à{©ï§ A®,¯Ëú¥þ RýG½´¼LŒÛýÝI†vìKp4£vešpë†.Ôºgù÷‚ùN½w#O¿vtæ›ïî×Ë0Ê ÌîD9îBßñ:°ø8h(ƒAè®kJÌ—psúƒÇßÕ `ˆ.`¸cá¸Ò¬i<ÍãöÍ»Ñ`F7´·5Cæ¥ååSÓß‘ ìPU”Í ì³ÔÔø»žxè¡Â@iTegHWÒ3™£Öa0}?ü½ŒµùhÃÇ<>jÔ¾ªÂYnáÐÿLz:-æ¼–B†¦„Ò7ù¹Y•v# mwðzKѤå@+ ëéUÄ?dBÔ„KŒÌ¾§zβÕ'V´oíÛÛ®V‚Ö³šºgy³?Ó³r&AšOµ³;»z¡Ž¨_eªñ/ä½5Ü‘“…¾à€!ÉØáÔÀ—›–QšVv£”[)§Ú™/Åj6\)ߥw]Šè 3Ý“¹…ô0:ÙÖª·ìyŠ“Ô^è@ǃn’®„»ÃNÇ0ÆAí³˜~è€~$òÞÝ¥;çí©\µãYÁ˜z_E*·‡Ã|‰>0ßL„U =mg¾ä6%{ô ŒŠ¥÷@î©d/…ë?vwêìÁˆÝáUµsìnB-ΠN ¡Þ¶3_òcNÃF/eý¶k+…«ÃS­®«–º1/PÒUáM’‘n#Ð1ïw&;oη1_ŠË\h'©¨;-q†¹¢0ß3z=:y,”3ÚnV\àŸæÜå ‚ÿŽ(§yXxõ§¿»ý{¨{"IÄ×Rú ÎÔ»,æK~H’éèt»ÿj{ß{$åu=âÙ‚ç 5a¾Dƒ.õz–«¬ålàÜuÏ^ï² ÉDB€Ê_Š…Ñž«v/h—W¢žCƒcªž½ªAŸûÙô k8ÊMMê†ÖzVU÷,?ö'­ðó…vJ|#]m¯ôx®-³»ctÑ•¾µ½Ê†Î °¦Þ*»4î/fÀ5(_‡pÌ Ìé“=Ôo%¹@aB±£¹T4ÀOÑ1ÒÜÑPáÕ_ƒÚ&^:ämùž·‡Ge?F*¾1ÿRþCÃm‡¸ãñÝQúÕº©ìÿÀèX<='£øð †¶gRÅé|1PÈ„öÍg¢#Ç8¿²AçíšL¥¨£³íß•]Ñ]bigÈ–~n—Ð7/øÙ›ŸPÅ~G/š4Ž äm;iè(»ÜH AÏä4wÎl¨ÕÿîÇ owrºÇJ³*¼UEž ¦,0È3fW ð§s*Ùÿ‹,w4ú—è]Ú-–õÔt£ÜΦË>Ðsy§D'ßµK^–_bÂ`äoYß~ÏÈÊGQ³¯Ïð‹;¬Oér¦Ñ •oèЬqg„82ú$¢(â3²†”|¶åŒ½ÊíÑŸœ†òûóÖ`°h­Â¸Âr§'4æàSW>\ÓºQ)ÞÚ:ˆ1Û,ú¥áÐÒŽ?`0vI¾'½ÈWÅû·ôÄÔÄ´¡žñ‡porV¬‚®A‘Ç%èÓÕ¸B”Ro‡N1ºóUwKÁ¦ ðù$¶ü|^£T¤2ܩԄV•¥ÈѽÆõP—]•?6ÓPØÓ@ʾ9&»}UïPóº°·¸º ï´GG¯Ã⧃¼“ô„Å;Û7ø u²`X_Ñà£@ÛHO%)ßW‘gHåÊM…µ;‚êš¡ Cç0Ìr;ðÔ£w(Z{°;øiæ^éïâ4”­Ó²3¾ñ·ö Fu3ú*ü* æ½ÐYý¨HÇ„éÙcþŒ™T…7:¼ ÚåŸV´þÏöbôÚ"Z|уp¹öÚkµŽ®Îÿ-P7ÐBµËhžÐ’œibÏîÒ+ˆ5OÇ?.ÿop×î4Šø»YßÈ×`h}šÏh”Oœâ »V"ÂöAZԑѨ{Ï¡¾¼pÿ“OöyjäHßZ›×¨Ñï§ù •:hx ‰?Gv%%âräH,Tœœ‘± t.†õ)ĘiÝ­þÆ4Ìi¨Ç;:9Fÿ(*4þ5­”¦eªª{–<‹0v.Ú3(ìq%)NÎ|Ê›ìÚöcý¶ÓÑ–/×Tu%òò?EQò§=–ñY°úoK§Q¾bôÊ&\Zz3À(¢kÐÏ£þ7ÔØàá4PÔU¡&%HSÑnƒ„37Ý]¦j` rdº'gf‚Kû»ˆt~&ôûEi~Òq¤ÐA«bœ»Y›ÐÚ‰5bŸz˜ëSrÜëÈ“K:Þ/3ôH¸—ãóy”/IËðbøÔÏåß5¯žLUu¯ÜÕ/‘„÷ùd5 mè™48kü‚Ùc~°üØŸƒZ÷ð&ü}û½ˆš7ý ªOF{½×¿³\%Œ¶mJY]“ƒ&'ri&€Ú°®Þ7©²£#Ç¢9 =ÚµC²roà="«I(,—Æ¥ªÐ¼pĦ\š’¤^N¾ÏóTÐÎ ™U }ÀØ+L[¾T,D©y=I“ŽDÙ Æ®÷?¯›Ô‡ÀüoàÕŒ.‡ˆ$Ô ÍÈu?áÿ3I¼Q «RGROÙLú¸qÀþâ!Õ®·/Š™îq/ÿÌæLÒ(Já¡äÿ@'øm ¸üíPMÖ’VµǸBJƒð ,£Y>þ4EòM+‹¡xPO†j¼¨x°_!j™~¨¡1?oèg‚ùž‡²ƒ&Gy×Ê—¹` Ûæsj{¯ÉM&úÚf"¨¶XBx•´9åeg,€žœö¢ÇI]›Êá&Ĥq~À͈OC½S‘× ûBH·qxa\åˆÎÏLF¼'2=kÜ©Ä,‚‘²Q]™ƒÊy:Ú3r2þçtÝ…!ïNÝÐ'ó˜‡7 ¾}Ávš£A£—…íáÝfTMb¿{‰ºÿš@þ†xrúÓ#°Ûøîåû\ÅlCz ûßÂJê¹ÓÝî‚@þËíäzâf¦´à~¶K¼’‚9?H–†q5ÍßÌM‘q›i/ïüÝ!’BÒ5d©ª^S\¬Cm8ðù²¿¿ ßº0Ëñü“æuú«`»ÅfùœsÜáOĉºxl¡Z8ÂÄ÷`âa>ý`š%´F K§«œ‘baÕø¼€ü9…ó=zZ‹µp–¸h& ¶À`¹?RËýÛB¤uÃJ«º'ƧЖkŸ0(}Ø½Š Ù]+T_®.¬åžŸñ=0y›¾q2Ùá–}Sx2®ƒRÖò·ŠdùºO!ÔI# Δò üjìy¼Ÿü=絫{‡R#ôzµ7èÚ´`áñ7¦Ðõ—) $¿—ísJáÄȯCæMÈ1æö›'ÚC­{…én³ö½b›þ!å²/í›æ™ÒŒNªêøA‡Ó‘΀æ:ìw™/2Û mÕ ¸lV êÕ\y,%ŠÒÛÈžô¯tRøóÀ¡ {Ä?sIIò5’<Ї^ŽðJ¼ëJ¢xÅß_°oh¾CÜ4oÚõ‹%«ZY7Ä=îÐva ð±Z>´HM‘®»ÀÔ0g"3ÑNv5¡jÚ¿(,†LgÓMód.E9l†¾à lξ+¦åŒZIá,ƒ˜ïÓ;¥_´C•¥_¸EZ7¬´Â}¦¦& Í¿¡N\F«þíá©íú×Qr'I’žô.òWz6•Ž»A€eÈîXÀóS0Z±Ð'wÆX·OÕÌ_Mìó˘…ʸå”RïÞ% ã ¬£Ø†Ž¯g¡ºÿ ŒŒw ÒÎÇðï4{üÃÇk]V¤½Šj©ã0‰[ì cHŠx°BÒ¸q£6ÿ1„Ë´‡­êËrÂà›,?èlH-Ü ÇјŒÌw^|»”JÇò[Ó'I¬Hs&¤µk±ýæàñ6z¢ÕÀ ûî½^šÏÚO90 ‘%ÓÞqê‚ÆüYÈëØÃ°Ô«ï4ÝK°¼óF»ÏRCQ&Λù©pšgÌà3nÏbžé¼õgtVû1™Ü þŽ/ƒVÁ¥îïwk€do0ÏÎÎŒû6ª9MŽò< G£~ŽU´ÛЉ§ê5ȳ åù4úgŠö—O`¡9C‹U¸þ’”ÿo§S>¬©ÆlìGÎÆüå™Hëk”) 'èBC¹Êé`CüÃÅrù˜ÇƒfåLÄܺ»œn´P?Sú§>:ú/ÔûEèú©…´ÿE¨ñ B_Fíã@'|•oCÊÙìËß:8Æ,(Ðs· ÌÏ#à]iþ×òIݰâ÷I}¶1^‹‹°ª`´{ß‘zšâÁÅ#“ ôùÇc Þ'hç«!=ïÃÀ¯/vHÐv¶ÃQgÿC¸†›fCößd$`Túxrß`?0¥  Y"-`tJF¼ËuÄÊ7ÐZº‚†LÔÆ§1ÙJG+Îo‘wÚWþéx‹´çQQ;!ø8ÿÃ$È/Î=¦µuôà¬qgù‡ö ,N@¼—Ø~'¦bÐò)¶Å\EqØUO¸öXe}=κ5¥ HýÃ1‚ ‹tî‹EŠótàóu 87©ßBó팪š:D?Ðû%¤„m ý,Lš}ì/éb•ñ4E:ûÿoáïdäû>êTñ¤4»Ãþ%#Yß(͆bGZ Ü'|p ¾×˜> ¬&¡l¯GV8å,RÛÏã%à#£ NIy)¸¿À.T/q剄1êé@”k˜/öƒÒþfÇu8 ‹j¤(t,—OgG¯lÔ¯*¥±pé§~À)]·E`ž‡ó\êt¡ô±ãƒúü¹õ-¥ë ¡€ÊýŇä4£üçYþíÏÈë†=¶Ðßéð dõäÉúð  I•¾y¿ƒòŽSÒž¥wØ¥"'"ôÞ¤ Ê‘M]"@ ª¶¨õPpìÚsŽùƒe]¦+i ÉÉ鬫JJ'1fUUªnHhéhÈÓÑÑä'»’ƘÇ)úe‚T]h蘻“s±¨ã?gó“æ(¿þu5uzIŠpn=³w-¤n ä·¡ÚQÝÚ¤®í&ÞøâpàêwQ-gŒ¦ÔûŽˆIëíb„’lC/Ÿpé§£'±«¡ xÔVÚ‡ F‘ø©ïºa§²Z&Ú+šL”GÁ”GÚÒTûAfÀöšÁï1‡0¤yB‚ËÕ}²gÔ†@Ò|Z¡w?îþ¤’^ü°#À0±†@“QAÇðLOõ@=EÓ‰½¡$ØŒùR,XY}|yl2èõ©±F€`êfÀu‹7§¤–‚ŠfTÐm %µ=8©Õèf#Üj4*j/®eœawçwF€`bVAÇré0m"-7·£(Ö¿6÷BÍŒù2¹s¹­±Â·+¥Àn3üÜcÞrĘ1Œ#Ð@`Ü@ ª)“I{7© ®Ó=ji¬ 8tÂØ y³0”Æ!­? v}bSÆóÎ0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0 ÞÜ ‹/4âq nªQ^ÎÏÎ|>´uï‹NµÚ¨®¤ëßp‡¬ˆÃßq{Òs°wþh]Wà\¬DŽõ¹qH»‡DÁöÓ;)ÿøÆº ¼¶(Æ{· Íûó²Ýýšêñµ…mC÷ÞÜÜöº×éªê˜Ô†˜G:[½D+>zzvÆühÓÿ/ÏÄN:®Wêy¸AßB-\šÎ}À@ ÷P~Žƒ9ÊÓÇft‡&5¨¢"}³â—åu›×}…´Çc(UÝìN\Ñô‹QV\·XéǪâOÏ̹NƪòCn¸ºðbœ>E÷ÉFl6©+=8Ùê>¡ˆg„.~•ŠØM‘nôÎ'BgᦣçÀÆÁEJÁö³5MŸµI|‡k Å5M|HÖ¸Ó4]û6`x)vÎÈÉj4Û¡Îô è'F-­zˆÌËRÔ¢±Rè7”zËÌk_QΛ©Œ^/GCߤ°UPºfLpÒ¤cìÜ[n‘< Ôœu÷x&žÚÐGÄq.×üÝ{µ…˜wtŠ`‚¸ËW1VYöÅµË õâN[ãÓüì¬1VüÏ›pÎóW†°ì©AéEî8%åË.’':ÏÇ E.³ÇCÒËìß î]Šß0BÊò§÷ÿâoïêê>)jƒpùæ]RQ ]×oÝ¢M ÍVúiyy.cý¶sñ­ªqž•0§°+KIMøÆ ­guô[é€qÐ=ÖÏ ÎüÇŸ¾çÝ0úãòùþº”…–¿†üT¤ëmax‹ò¢À| ‡BoዎB;6¾m«üpM×_ jk´ñ ó&È€å¨.Ü·§$v_´ mHÖø¾š®v‰V|õOÅ‚¤Ó¤?’»×Ðtát_Òì–}´ž$}lTsº@žvPœÒè é÷}»}Å]³T~Q1èæMëž•Èb'’í3ƺºÌ½6È ¥îƒùÞ„´Kœ®–ïêÎ]©¢XU׈¡ú°ò÷Ö3pA3íý&ì‰?‚ŸÏ@êÄÂhœC¡ßG„·á,òÕƒŽëu³í>éù6÷ÿ:-gÔJd‚~Q1É®ä;‹:&楧{+"üóá‰ص×{¾.ô›a-ZTˆ­ãHšV¤±V7d.˜ð£iYãþ/?;ãûê0Oóä% ﶉÒÎÃH®=ÔhKRqOËÎ0GâCÜÙƒuCKñ”z½Ÿc”ì3Ùªá‰9ćpOígV:ƒɹJhâE‘“ò²3_·ìÏÜìóŒKº®Ÿš3ê7²œ5î,©ëYHû8Œ½H±SĶÜÉOZfÎmp;¯“Ó}K–û¤Œ ækß.µÝä#JÉ¿œ•“ Uï ‡ËqÃtϘµþî¡~W•vë÷x7zso^7 CÌ“AÿJÜZ”k©øþšoî`¤Í¢uh[‡˜¢ëF$ïf{ì/A÷A-|úЬñýÐy¿èpÉ›¦{2—[´~˜_ÊDÞƒ]~+¥tÜ›—±Àò­guå’îÎ~eÖ åþO{šÈïàr?Ì^Ê æéß[fädްìjû™î~üp]xïÁmWÔÙìÌv$+c¦³ËJõù<Ðv%p„ó¸QN‹àwA^N椡Y9wªûù¹î³­ðôDVoÅÿ÷¦z†ïÇç~àð;žð{?ÓhR^Œ4 ¹+obÀ÷QšgBWë²z¨CSÅ~ï ¸žò1ËuíÒç/ ô[q!ßû‘ÿí6æësò!ú m[6ð=áz¢þïBù0?'s˜åå>¸d¢&ôEe€*Ûø°“³K†ÇsG‰åø/Ä q´¦ÈÖR#Ð&ú‚†p¿Es×#µÏòF™.M*.Q¦{Å#H{Ò˜—ëΡ6ï‘ óøJu4„¾È¢Ãþ¬0Û­ÑŒ|-…eb%‡&öÌ›–A$»¶ ö›ÐõçiñOU˜j1ï6áÞ„ùÉÐ@CHRuýË´GrRXM:V¡2—ÏýHãYÌmäRyº½óá?ÐPÁï¢Jihâ&ÄÓs‹$øŒNþÐ8®‡©æ’™s“4´¹h¸»PP÷â‡EJ²…*ʧg;ÕPÊ6Hÿ긧Ñ@/@ÿÜÆ|Óݹ£„a<ìpHO$Ì×L¿Š´i~8C‡Qˆ|å¤+†&Þæžx…Ç€hòËåq‰Ï`Î`¾“ág9¯@RñûŠìñû7ùÓu.c“5’Ìpø—ž•{3ð£ù`hå£P—Ce-—K‡Üdù‰Ö3”r1„²é]v¿çÉVVºC=¹4ˆÌûUŸ½”_‹3fΜé°ì@ËÅŠ!¿J›/Q‡K„ê½ÔçV¤ž‰8ÐIJ ¥]’¿hÐOñXÆPÄ;h1Ø;Û² ô¼çÙgã1hÿyºî§ùNG{øÛòOk-Pî˜Ë6Œß‡ÌÑ }pZð±å§ây4êö㴃ߙð7Ø}‡ß¿vïS'UòB™Zq"ü`¾ï£W#ý‘ŠâøÔt£6]>ˆõEJ÷yáÅ,?¨¥áÕ,Ï‚4J/ÎF™«*2%1‚§›s†zrîÖTc~wÕhx,ˆ±a¤3ãx§Ké;Õ“iÍŸ½0سœ`"ÂõŸ‘ñõà,\›§#âq˜ÎÀ¯ì/ÑèXñßÿ䓉…; ÏGÃ{v· óLiV! 9a.®1G¼wwé$CÈ·fä¸o´ÂcUðkÕo¿ÅÚÓ`à§P'Hnèܺ#oÇurqšÇsm™åßÿ™–•{7$K03åÆ@ Ñüý‡ò]UÚ3rÝ'ÛããýÆk”ýªJïù°ÿmzNÖWÔÅú­o ¯?û/b„ð掖ùÛÛã4% ï¶ÇþŒÚ¯²0Ÿçíþ¢ñj¹¤?2q&:•Bµ„˜©BW½Æ`^‹Qdñ'Šõ{¢©TßwJÐa(®Ïé;˜uGÝËñ«yjÂxK… iç-¸ÓÏ2ïïvXm~‰ef3ï;±`êÛ‚)ª»¦ ^÷-æ€éV0Ô ˆc®ÀR‚r#瀡Ÿûëª~ø^4Ô3þPÕ«!ò|Oz¤0b„Ñòy×Àn_g×i‹ÌïÚ¥é/ ôS<–‰KTÞ*+Ò†éB¦ë}HäÿFÿÎr·ž¥ÛöEÃ<Ú!ý¦gùÙ²·žÄ K·î„†û´:¨¦yš“Åh×/Ò‚3¿¹Ñ£â’•îS22Hò%ó0JEÛ£¾aˆiƒ¡”©åøgbŽ©îÙ–] g¨uÞÖîESÉNþ½u°K‰“F“U? L59cæ™æ21 ž‚Q`fºg\ï`(€½A%*±1_Ó+:8Œ\E?s¤,0ìQ)±òZgIBûw–œ ë8áræ"m—WßEŒH˜*6iœ@þé{ß¾² quD%iÖghÛ âC§dôöØ„>z1¤;ó5Ðë§?’s9:²éí«Š¡UŠ3Ô*Ò¶GQ®:—[¤íö‘¼êV+ìÓátVšèL?DgºÇþƒŠÞ”¶-?Ö3ÔrÉϽ ’;î)ÖXaQW Ì¿D9} õ?1—r£ëÉ/…±¬"­Áp…cåŸVèón åbÀh¤R§K¾°Ðh):ê6›¼¹÷Ñ–°à!»˜a¤q#ÊåuIÎįgh¯%+˜´ª^ŒÄJLéß4?0W˜Ró`÷µµí,Ôv)ýiûÄ]ÎV§`ò(Ú! œæczdA…¤íó&z5êøW˜/y*Û¾ïtø9Úž—|ðb$*Ô¯ Šè§ÙíQÛ˜¯é„úÆi¤X}Keÿ¾üËÔrAöE(ƒðPë¼ouÏ!žÜcP/Ç)ŠÈ}.'ë¯êü7f÷°TcÃål‘¡ª;/7T ªhãô]¤áñ3èEEM„dð«Ý ‡T‹Š¶³ð¥=¬¢‹åöoß{å‰ü äu…¥ùLÃëíëâAìiv‚©Ý÷/wv³‚æÌ'¨ô;Ô=ñÝðŽðÑ€—Ž®ÎØç ínôŽ:ö#$«3ýííߤ)ÚYx=èˆzÞL¥5ê5Ô¨ØÞé )ìÔÓ\Ð;[ÂF‚Ùä'$+Ó&gdl#÷êÌfuÁÛÎ%\¯ÚýÒ¼ âý+ˆ‰OĺŒ‹ñüš¤_ò§‹Ë ýîÙ§?tüø/´Bµ¨z…ÜÈ„Ú.#¥¿<µÊÿ+4V9ÏKOlR7Ü„v;ƒ¸¹è#.æsÈ7°<£ïÿUyà ë:¥/4ãÇn¼åb£eÐÀÄÝ,;zÂΧºöÙKeT¢T¨¾i´PÊô@øWćQç}qyâß]÷j´mÎ9Çö›Ä_S±ŽZ'Ø£†d( T!âÔMjÎð2EA›¯lÐ’QY~Ä|äã•~Š 5îj‚¬²#šê³ |-IB´Ú]Ç¥˜ç2U’æþ£ä‹ÈžTlð·fšgŒ¡%2^õV¦_ôüR%ifʣÂnƒ@‡|B¢C0ú£ãzPœZJ»|¿¤w)úˆ­ÿ‡QŠSbA–Œxo¡\tÆæ\0Ô %vûPß1ýÁŒl÷Tûª¼yU…¥\ ¥ù´Rõ^Žz´³ƒóôyœG|…’ÝW&奴¥ ½õñ¨ƒA°îô¦@ÜÏþÛ(Ê"j»iž'ÚìßQô ÇXÐò‡"O#¿é¨/øç ÉÜ"Iö@ú¯Ãý¾Ò"m-Í úû ôÑ­¨×‹í‹}þ0Œzqj…d}:†d>uä”\÷:„[Žðçè…Æ©ÀG‰““¹QøpÚe$ôûh ðB ìG~ÁÙÌÑù v~·Í[øhÐúˆ… 嬩ԯ ŸÁ€ò.ŒOgØâ¢Ws`âgWé3œ2¥€ÐJí/*E\ñJβ3áPµ9†+±@ôúP²Yaë³IKÀT¨3Æf~ †ô&Ô8­Ô8¨D§³½xzNæËT‚9ˆÿL¬ö=q´Á¼Ç).§t}€ùÐñ›Dî ø€áKòÜØ¨!ô"Ùn¾©Ë’Èò§ù á:ˆ§ÑLsçîÁ‚š'Ó³&|——=zIAkäÅ<ÙÊ»êq4òïòsÜW[‘P'!dq*š¿eñÒÁ_À’@á²;⫈ œrQÅ­ËfÕ’Ó1è¸\ã#K… ©çS,;¯Ð(^ qNq¹â|eïŸ|ÅTw¢f oÉ0 Æz)Šr^ÞØL_Ú˜W¼ ö¥“Ÿ™¹ –£±`l\©wÏ Œß6åA˜ûµ½…—a¢ëÁƒ"„ê´CÆèMê‚+f²Óáô1`Ó?ÔÐPÕãð }Êxó”ìÑ+¬xÂm—5¡ßJ«º'­ìÎý ÷6Ë/ü *êòvjYÚžŠ¡¬† €²+ù²9Õø5Ü2 5¡pê|°8é£âⲹжü-m/<"½4˜ß¦dÑ(º±Ÿä¸$49úSè*õ>`´ ùÜá™9ݪÎoùjÛ2M;Äßæ\MILüÅš÷¨˜]C`Óª„üÌ ›Ð6y)xFÐêÍ–ïiè7£óú»£è¹ÚgWÍ ;ÉK'g§‘Èë*¨ÅgÒ°j‚ÕØy›wUGH¦ñèË?¬‰Vr=¤™¨üdœX ìJ5ÕÜSX)¹h„S.ÓÝî”áoŠÐÎFµ:õà=‹´¨hgJÜÿ]VGóõSÛßQùÇ—M´"4Ý`}zNöŒØ‹¼<‹²m½Yûóèr?ë~‘ºÿ:¸C¸NxªZG¥Ó|ªãØ :´üAÚ¢JiB 6s ~g—«ó¸†Þ.„¡·pè¯2øá†ÕΣ]­·|>ÿjœo8-KûÓéÂb2Yhx Ú‚-S£2­.ñpê| ¸ƒ¢bý ”á&0ß‹­i†@~›š]T;† Íganë~Ì»¾Zž›tÖÌ•+÷{oÁÜ/°2q4†¬K QÚý V÷êH• sî±=¿ý|éÊXùú˜õi4k5´îˆŸ m”‚ÎÔÀÏË ‘~û¡h¼F’+q®eO[ˆ°²ú¨¦_…T² RóÛšˆ‹Ó¥—V1_5Õ-UIV<þOR›¥yÆ]gxõŘÿž÷[üýDã»­+óo®± ø6Ðÿ-öDoÞhl»¤’Üæ >vA>£kÛ¯ÀNŒðæÔo¦æºžã®ä«q~°\Q®ØJðÀlÿb¦³P•y"Td+ C]7JW¡ãš 5îH»_s>C*ö.ÆZȱÊrÏG„F‹sŒE+©8Mõ³åæŽÈß?øoXŸ‘ù4ª¡8®/5䟪Qö;ð5تqµý+®PŸùžŒßÑîA>n“¯Ü±…I5þhÅ+Ý5è˜ÖÁë\¢Ï›Ðq]ƒN¾²ª±š¸BqÆ!$ƒ‘Ñ%#5¯¾ ÇÜ}ž—^¾g8”B÷N¹àÔÅÏ+ÊwÖS#G[©PYC"˜Gny@óa¹Gú„„z$0xë Ÿª^Hqwržž‰ü `t'‡­Ãb 7P×Ö("ŽVéû ˜¯†zŸkxµßö{ wà”yØî–Œ¹ü‹­üªûÃ<ã{bA×?ñŠ/²€/Òœ×Åú€OýËç¥iéÏòj»¬)ýV:ög©ˆWÑG´B›N8ºw)Úãóø>BòF´Éм¤ÑÀ9ýá¿Gß~ZÞvõè7Ž·âÄA&ÑïBŸq¥æ5¾§z‹ø^Ä€¤Ÿ4h¿Â3¡–ix±–û§ÎÛã/Ü[ÖýLÔÇTÍùPÞðÿy85M¾À&THÕ„Ñnw‘¨ :üð­ÁÐJ?E4ßîÛßjUø>n\k,ÜÐí'Uá=¦œh.°4¾DÖí´ØCŠÄý¦º±–‘hÈåbAC+´õ>´ X}¦E‚»\ãÚª^™ªˆf›«Âµ6ê¾Eg°guí2ÚôÓÖCïÞ½mª’œ˜ì*°öU£«¥¶y Ú?—“¹SGà×ÚJT¨¶l‘·Õ~²ÕÁ>C³©®LC‹%°¯ÆPççŒmF€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`FÀD@ÖC³Æ÷Ótí|¤ÕÍF'$šRérŒ#À0Œ@(B쓆Ü¿ëŠcö´ì1?†.?µÆ€=ž— ¼ÿÒ¸W¢‹”B†Ü¼‰î„hË0Œ#ÀD0àf`ÀÀ³Ú†P„À³žéìêüœÇsGI4Ӳ⪜æÎ> Ì@&ºJ)gá÷†’¤|‹6­QgÀiY9÷ºxBJc9F#òrÝó¢M4ÇÇ0Œ#ÀÔ6é™9gêBLF:GKE<˜Ÿí~:ši:¢Y9ó5ž’B¾+\í.ÍϹ:šñs\Œ#À0Œ@]!ðã7s×xÎu¯J½èHÃ#OpΞçÍ]­ô£&“ÚÙ0ä'ˆð½¼œÌë v†J #À0Œ#аó•éîÜ·ÀÔ®k»(Zêh%°Ð‚+Ä3ƒÔÎÂÕövf¾Ñ@•ã`F€ˆLžÞZVà7£‚çELZT0­v6\aÎ7ß“^1U#À0Œ#Coü‡x¹Ã' ´E…ÓV#Œfñ‚«(”GÁ0Œ#“#^gn¯…3`:d£|Ÿ¯|# ôpŒ#À0Œ@Ì"üñ<“÷EHeÄ ˜N¸¢C6hŸo„´ppF€`F ¦ ^G<¯âtLjh˜#õnt²Q9p`F€`€€ÉëÀó@j·HÉuFíŒ8èüL6Œ#À0Œ@£G€ŽTF&‰÷Ed"fÀØ÷K+ðÙÎfF€h(€ïÏ‹øR¡ˆpCŒél\Üÿ䓉…; ;5OMØüÄC6®Ü5ÎÜ ÎʽSúbпqæsÅ„‡3àððbßõŒ@ú#9—º1jÿŽÂS‰”=»K5œÂö+Ö!üçre=çÕ¨¦C<Cñx$ޣޒ5î4,ù7½Ìǹ¶g˜ øE=83gFøýósÝýüœ"û4D{DÐ7²H*‡öxfÆmô®ú·!Œ)R\˜—“5«²þbbh,ŠÝÜ1e ´¬ÜÁ|ßÃqp*:Û;.y²"•k )?GFSÛˆ‡77¦ §»³ï*Psók%O†8-=+gX­Ä]G‘ÏÌéVà]õ öd^VGIr2Œ@T` 8ªprdµ…$ßK ]ÌöéΎ̇lRá"¤ù^yºîÚJ¾^âÅ@ƒ˶ZI\Š™8Þvü°ÌœÿMÍuÿ]+iÔb¤Ä|ˤñ.~Y,Ž; ¯FG²aÌ€Tq5]buM¸ÑÙnèìì5ÆÆ|ƒ’æÉK2´mÙPÞž ©;TÔ« nÙÑ™1É>-3ç6¬h¼H:‡jɳR……`~_IgÛBìK2¼%“ ’ÍqÅæ§ËqßTÏßM_P/tHÇCº®wŸ{ÁØzã\ôÕBÊ—¡æ}Ö"r˜gJ3¯w×|¨ÆáD·,{zÉʽ]Óûgäºû¤åævEݧ}ÒÝøËçL ™ µðÛäŸò'¼Û&‚Îóà§=TÊKRqOËÎø†Ü«3.§3ÓëÕªBLƒßKªóOÆ%u= iL½Hw±SÄžš3ê7ÿðÐVÜm`¾¸÷¿Ão•ùû3ãug晸¾¥/ülƒÿ;9»dTu ú”\÷:¤qÏ ckÞÒ•]KEÌvŒ@Œ#À*è/ &OšçCÞXàôkvâvœÈ¿áݶ@èâb‚Š"oÅ]žáF“G ¼¹ïØý½ â¾D¨Ås„0ÖKá ³^?3¸ÑP·¾æûw©”ÊCx>{ªªªïÐܬ-ž£5Cû·.Lؽ$â0”_ .WtæZþBCã8]­-;ë©¢-¹Ñ·KÊ2Ðü*è"•ú/P³çÐLï'rOËËs!óñz“@¾À°Àɲەmßw:í!’_²Û‰Š¹’Ùˆ‹ #Ј` ¸ncÉöŽš[‹ iu %Oh£}/ˆ_H­FO7ÙZ`·SØ«iB8tg¥mMXy½Wó‚ g÷ÑÁúJßøHJŽûcÏîòÛÍß-’o¤~(˜m"¤×_íñ€™µÂ·¢í,¤ÁoŽÚîÇÿ=ß3z=$Î ¨ÀŸÅó ÚÆC’,ÔżIûÂáŠ?(N©(« LÐkªJ˜®1„VžW‡Ë®0ãÂêoÃ2˜O>”Þüqäe¼eoë4ØÒABTq³âç'#+0Ž•’`:‚#àt®^S…ò Æ4axºöò 1ïÑ´bÜ÷yPM”©Y°€jõ m’·¬¬y…[ef <ž+u ;HçɈô,ºz£’»©ß•BMa­œîètOÁÜøî§c¡Ø1ªwW@ìd¼z°½8# æ“+0UÀR…ž ´.ÊÐE!«Ïèù—¿sÜ…>‡ŠMÈ¿üíø›hL0nL¥ÙHórvïÃþž³t¤'ãn,@z"?=ý`F`Ï»4~„j4°ô$ÂÉiC@IDAT®àä!Í%Û£¬îdG?ÅEÆ™dÎ¼Žžäiz‚4êTIê$A B­FÅÓs2_â%,kZ>…b(«!; áTL÷dš Ì|ŽüÂ4µ7?x©ÈãÙv;áta7¶|y ,*cÃ4=XnzeÞ sÜÁ‘ñjž{VìŽNÏÊ=ûfßЄ¦¡·ÀÊ_,ÒR’°Z™V‹ÔñÿƪÝK µx¶ÄÜçtÅ­Öuo]S§‚þÞÑqÚóµBNeš9$kücN‡²«Œ/Åjé»Á,ß´ïFºs ]^„íI÷)Çû†&SqÚÕ(økéO˜÷ÐæNÀêíKôfÊ7É%qާ<#wŠf®\¹ß{ 6â~•£!B.1Di`p2˜»«›=þq…òÝÑÑ+·@]y Xÿù`Œ>‰tòˆ¥8Çù`ÿ*ðÜ'·5§Kïݺn\õñ-Öö0ÚŸ‹£,_Ãa ÂlŒw8g•iFëjî ¡Òö+ZžîÎÍÆdïxÄ[¦¸Tê²ÈPõ“ Ê‹tLÉϽ,ÚÙ#Ð` ¸!–Z¤™Ô¤Ø&tý †$ܾÐÕEØo: Kv³3 4t9Cb’¼¯|àõ–nÒtý}š7u%+§ÓêÝhC(…2N‘ÆZœ³üM©·¬ŠÖ§ÁL^Á•¤;%QÜ ·oÁÌžÒ¼ê_º®~ƒ%OÛá¸ÃŸ&§#î?D¿nè3}ÞÝ…Þ¢»ÉOþèÑ{”y"å vñºn”®‚d?«»GúÇηÉD ‰4@‘Ÿ™‘ù¨¦‡ÂéúRCþ©e¿Cí~pÈ«ó²3_·{ïìê<ƒ×„¡O1±ÐÕˆs?ÔÔ·ÛýÑ; š€Û]Èç•XÜö½æÕ—!Þ¡Žï' R7F ñ"€wd{¿¢0ê@O6Œ@] @‡Q86í<¤½è°±ª“Hõ¹Í›{ÈY}zm UvmІ6°ñþ›$OJo³˜Ðµƒ8eCUŒžNÅRÔmÛ8Ýëì's¢ïá‰S ÷z[‘téï>sæLÇœ¥+»‹D¥hÐá‡o­­<ÚÓ>n\k, ÒÍ‹Éí~ïØ'ìÜ,v¡9üPèºßód«Bµ°e‹”¸­E˜²ab˜á{DˆELL"ÅD1µŒ1`\lðX-'ÃÑ3Œ@Œ -¾Ç*è)P&ƒ`F i!À ¸i•7ç–`F FàUÐ1RLFÃEÀŽ›œ†ã†›¦œ`êfÀõ:§Ù¨˜‘“ñ¿F•!Î #ÀÔ ¬‚®˜9F€`F 2Ì€+ãÁ_Œ#À0Œ@ À ¸N`æDF€`Ê0®Œ1Œ#À0u‚3à:™aF€`*#À ¸2üÅ0Œ#ÀÔ Ì€ëfN„`F€¨Œ3àÊxð#À0Œ#P'4؃8†fßpo©è†«Ì:áZ§”:A,†Áqû¤!7âê¶õÜ»:-{ .CoÆ,M½@HÙµ©–Ó(iÎe |m_ˆuÅ1»)µý@x4T»Å€=ž—péyÁ¿EŽTuµ£Ä¥)I z‹æI2ÁåjrÒ|‰×«ïÞ[dì+*Q€G.ªß¤ªÚÓÍKšM~ê©‘t—*Úi£1òþûŸLØ›°ÿ§ÓqŸYþ¸H¶)—£)YÎHØøµý\jûš¦?Õ%®Ë䪮ç ;!P«4ŒëŸÎÛ¬m|<¥sïC;'}¨8¶gg™”ï¨U„b;rsÐQTR*–­.?¬XÓaùê ‹›ß{ûCîÁ/OÊ™ òuü2#¦;«äçüâ„âŠ.;õ>´—l×K¦®öÔöGyß]£=w½0ÁCm¿!·ûÚG0Rˆ˜yõ;óìÛ)?ÎûòezÖ†ó½ß0ä+Ûµh6øÊÊ…§'»´k)\Î3~¨ X|q„Gÿc•Gtï ÖnÚžTXâ½ñøÓÎ(üùÛ¯Uxlˆ‘:磹ßéŠ{åŸÌåï+v~aÌ>пíï/.»±ß™öÿô͗ߢ†Øîc¾d£Å÷b]m+Ë™¯xòÄ£º+ßv‰ãð®b¾pê“@ÂgÔí—8N<ª‡t8\ßþ°{$èqáGƒ-’&‚!:‰^ÑOù üpù7„¢cë m¿;úuùï»3»´4¤v__ÐÕ[º±Ì€%T)çCò„Î׸ûʳdœ+b½Þ€®Ë„ '‹ps¹âÇÝð¯.DúÄ„©¼c }D§‹è&ú¹üF ìm_JåñÛ̸ÁB»!wÏKÌ2à´4O¢Ëåz¡KûâÖKOu¦“5ƒpëÔ¶…hޢŴcŽ99DZL8&é­ Êd¾D/ÑÝ¥}KƒË?–‹‹i‹E¨Ítn×ÒHHHš1hÐ5Í@cÌöõ±ˆ_]Ñ«…¢h­ä=š®wºî¼“,ùÖ¬:n7\pŠ¢¢ÃqgŸ5±ÄãGç1[îôŽD7—?aĉµýëÏ?Ù¡FÇÎ}z݃à±ÜîÃÌ]ãñ‹±9ÿçt9î;¦gƒç|#«l„ᘜ’21%â‡_,Î ™å^A_"ÑËå4Ø05DÀjûñ‰‰ÿB±Úîk˜»Æ,°róˆQ'éºÑ[Xõ…zVcÛAÿ¼îdD«R0ÕE¥ÇWÐÙ–Ë? …ÏQ4i¨ †ÑþŠÛÒû–‚c¬6Ä6¥ W|ÜùtÈöùÆ\ “‘ðlÕ®ý9ÈA~±6¶K¿ D'—ìkLul!`µýf-RÏÁv[`Õ51É€Cw:á‡lÔ$/IÂ1%1ÞPœÎ®È©¡cm[’Å€‰®D¢“èåòo|u‘sT·˜m}©TÝr¬µûº#S‹5Lô8„";âxÉX£-‹/t’Z¤&K‡ËÙ!ìp¬`\^îå’yÑIô†ž;öÉ0Á ¾T:dG¸[š¯Xi÷ÁHn2ö±VDS2g;sÅjHx:¤BÛH­@ 1–æƒÌr¯ +žèäòaš-;öŠÍÛ÷„ªn½—–yÅïk7Mô‹E¿ŠÕo êÎá#@mIfÛµv~fYˆXbÀÄpMU¤!Œ3ßÌ)ïŠ÷¿ŒìR ­;÷‰‚­»b¦¨÷ì/“^ùD<üÌ[Ñd&¾V#´VB×눈9ØWî°¢AA\|ð›õ›vˆ!¹/‹õ›wøì¬—çý,>ø:²ºoÅÊÓ¢…è6þ1ꙙ╿X@4øŠ5Åäÿ|.¼ªÐÏçß/+×mèÆ–5G ±¬An¬´ûšg¦…¤/– UN/­W¦ðñ7?‹¶­š ld¯wlÖlyï|)â\Ñ(*s`CQ#¤_¬ ÀÊ˽œ&ÐWóX½X=pÇågÔËÉ¿wÿó,ѦE3ñç†íâíÏ¿Göè$N>æÐ€Hœpd7qÌ7ò9îÑ©MK³MÅZ»¯Í 7ˆ¸£Ñ«G+£–$DËæ£Â€—¬ú[|ýãïâ¸Ã»ˆÏ, ñqbÀ‰GŠ3O8¤yÙÄg —‹ [wŠCÚ·¸äAü†÷ï‰ø8—X°d56³Ÿ"ŽEø¯Ï\¨Çöì/Gtë`úíѹ­¨.2¯&Þ™³Hü²j=­D'õî!®t’)Ìül‘øyå:ëõÄÙ'õçrÌAX’$>蔣qå¯!æ|¿â ÷p,*$K«úðFÁE–p¨™_;&m,× È·foJŸ7_üjëå‹ïÏÍ’âŵçÑÎ4!-ÿS|ôÍ1vè•bÞO+ͺöÀ͈Ԕ$ñÕâßÌúOß)É´†¯²i‡Áj×­E÷NmÅ‹Vˆ¿6l50µï–¬gô=R¼õÙ÷f;ìÕ­£˜‰wJÇáPLi÷ÍO¿û‹JÅi}P—šuR(óªâ_ÿ$~þ}½(*-Ih¿hDâá[/I‰qˆ§úöS™Ò¦ûUѧRû²··úl÷M·0l9 È"ɪÖwDÏ2¯Wüº¦@,\¶L¯¿èÖ±µÙ””zÍŽêÍY DwØÝ{Ãy躊֩)x"’ãEß#º‰Û/;]ôèÜÆ¤¡UódñÏ' ꄼª&>þæÓ¾ª4ÈÃ_ý(­XÆ”¸úœEÏCÚ›áæýô‡y…àí—ž).=³¯xï‹ÅbÛ®½¦›ýß}{‰sO>ZhU¨õìþCx§2·p¦g,;=±V'cŸhØ[X"ö›~««—{ ‹M¦gE\Œ6±sO¡ùù>=±RŠ·çü vï+ÿýò'qêq=2_ @*ãÅ´¾†X‚9ÞþÇfÆC4üö×FA ¶ßQÝö¤‚©zÅŽ=û1â+ïû_ÿø;¦{ã…§Ší»÷‰Í;ÌaSœ?þ¶N<|ûEâ¶KN3ÃÝ )Ÿ¡¶“þg!`og–?ëX’€-¢Î.ÐWµØ¡X,]mv}zb¦·sjÿÀÈ{Ðɽ­ô…Óá­R“…ý.bÌdöì+½ Ó|I¥– –Æ÷,úÕC\éÚn–¯þÛ”Ú·NôëÐ&Uüôû:qþ©ÇÚ½ÕÆ{¬6ÂX¥«6Ê Îâ V/«"€ê?IÑO¼ú)ânÑ®U kg¬8f·Ô|%)–TÏíZ6·œÀp5ñÏ‹û™Ì—,íƒÌ]{ Íïûn:_Ù½#ÚhG³ XW±ÓU{©Í’àV~&-â"SíÇ"¯!>­6Öio”4Ç"Ž:Ð$ù’i–\¾¯¸£p2éW o`t>vÆûP *nºèTSõl:úý£…]4×uÝTÑBûb“@i¨”©SêÚ¡U¥ØpƵXµ~3æÌ¶BrXës£U¬µk¢£Ú¯])ö†Bgí#i êe 8©^Û ijˆ)ÒŠå´+ E >. © w‚¡þLûÿ Y½;èp¤¨ïÝþBëÈtÅôÚ³ÚºEŠùNÿŽ:´³)=Ó4MÅ´HI6Òõ×~|¤ñ #b‰oáf5ئîÚˆŒ;/\#^ÿdhùm’øçÙ'”ÚºMÛŬ{o<Ïì¾üáWSÝf_*( šÛ¥CÑí£~ŠÜ¡(¦”= ß‘H¯ßAéÕ±E­áb>ê;ýÉl˜ÞÕKÊ ÕAk J߶Ïs\¹n“èÔ¶¥˜½`™è{dWs ƒÏC€š¦!- ,íÆ…vÈt„Ö‡ÌöÝûEWLûhš.öb…eN@šŸ~»ƒ€â°. Š>ΤÁõ8Ié1Ò~,rÚ“Û] ”X¬Í·Q¥À¯ö% RÑb«ýE%æü/uñ´È¦CëT±vã6AsŤòÚ¹§È´o ?;0OõÔØ¦9 .ÿðŸTß?Ar¦¹0Å/Áb,2}è*¾_ñ§X ƒFôäæÏ¨D « Œ}óÀш3Ò8b‘¦HóT«á©¾üU°ÝüÑ{¸¦g×öØo»UlÚ¶Ûœ’Y±fƒ/ UÓÄ«ØNt"´B#n$¶ì܃ŊÁÒ¼- RIR]òÇߢkÇòu¾ƒ¼sOIN0|‘ô<‹íÛ“VüY KJÅ@,Pìz“m'ãÕcû ’›·.ßYbog1NpÓ /–$à:E\Ó5ñ V}¾ôÁ½ºš+¡_ûx¾xö4—œˆx‡\}¶hk›? )öÔ$ ý¶–!FöHÚåÖgHÏþG÷¿cO.M¿4KJgõ;BÌš¿Ì ûñ¼_-èºöÜþ&ƒ¼õý9‹Qo1§þ Њj2´mï쓎çô?°žÂ߯ÿ7µ¥·f/Â`x8áÈî•Ö]Ðêê’RU<ýÆl³(ÚiÄegõåöã$7HhD‘IsgEäçd  g†ôT´Ç¡ù¹?èݳۉ#o¹ ‚èB J#nçÄЖ»¡õUÅ}'A5f’†i{RMLi™jΣµR»c‹Erbí°¨5óäk³Ä’_ÿøíõg¿‰ÐIt’M:“ÎOフ¯ÜAMÖw¸ùÞ‡ŸéÓûð£ê¢üë+Ó±’.ÕËø¸ú‡ÓZ ÚY`§ƒÚåÃOÏ7\xй>ƒ©_ÿô»yÈÎÓÞìk+uÕ~b¥¼jBµýe¿¯YúêSãoFøXi÷5ÉJÌ„‰߫ߖpºœ! fhg¾DjM™oyØÀéão–DÇ3³aê;Ó«ûÔËS¤^þtS&{Zĸ [¡h‹ÒRì+îÒ®•ùRhn?õUjœn4Ì¢3ÇÁ0Œ@  Áî]Wœ).ý{~ qqæ<ðy§ßUä8#Po0®7è9aF€¨ ÞØ†D?6Œ@cE òÄgcÍ%ç‹`F€ˆ1˜ÇX09Œ@,!@·‹Y[çb‰.¦…h 0n ¥Èy`j Ú®g;qµ–Ráh¦‰Ï7Írç\3!#°—„Ìxï+\VÒÌÜãK—!Ð2[vî5÷Ðÿ°â/1ôš8´F5o Û„ó£Ïè{„ ûÛ.==ätØ##ÐÔ` ¸©•8ç—Z‰|ÂQÝÍÛˆ¦¼ý…š¶}ðÕOX¥¼nÝÌû}§ãÞê¿qµ' õ>Üè:B6Œ#fÀÁ±aF€t=a?0à+qæzÜF{sÉÐaî=Gœ~|/óØÖ5[qeg?Óï@\¿É†`ªF€pÕø°+#ÐäHˆ/?ý-±âY„âÈÐQ‘Öá4tf3ž‘Rq¨Lj3:ÔŽ #ÀT…3àªÐa7F€ËpÁB™W‹0§K—–ãõ74?Lç˜/þm­ÉˆYýì3#À‹°Æ„mFÀ†@g\ö0þÅpk—&®Æ ÁÌUçôïÍ],>üúqD÷öÁ¼±=#ÀT À ˜«#ÀE`Â=ךn—â"º)Ôº0ä4ÌûÒÏnŽ?¢nLêf^:Bw ¯\»ÅîÌïŒ#à‡3`?@ø“`F@Zœ÷`§J6äÍÿf±Jøƒ`|ð° ~ah!ЦEs1èäÐïŽVº#Ð`ÜJ‹ieíZ¥ˆ‹NïÓ@¨e2úA€pýàΩ2Œ#À4q˜7ñ ÀÙgF€¨x–îNž§Ÿeìï–]¸Oûz·‡û¯}¢]ìåÍå_ûåWÓ¢]îD—}MK£i„kò Øjtº®›ÐQ{¿ýµQìÜ[(ö™Ì‹ü8üZU¡dâãœ"59I´JM6/ïÖ±µPÅl öFZî›ÿ×%VX·q»X±¦@ìÜS(vï/eeÞE_Ó:ÐHÊŸN¿**)Å%^Q\Z&â¢eJ²hVqòUC¬ÃV¹sÛ¯ËÖÆiÙh² ˜5Ú÷ÿ›W&¾þé7qýy§ˆczbîŸ$F̦ö :°|õñæ§ó!í–ÖIÙS®b½ü5MŸÎ_*>[¸ÇOjÂé(ÂñOѪå´ JÑ.‰sŸx¡©Í1 LE]î ¾_¶O,ÀÍH©ÍÄú‰“Žîa*c­>sÛ¯ý¶Å)„Ž@“bÀÖÈWU5H>eâËŸ~·\Ŷˆí> ëBG®†>©KN^bþJJº‰»/ùÿ-—žÑw­-œN‡Ùq5D•^ !©Ó`V˜³h…xÿËŸPö[QöÖIÙSFcµüMm¤ÓÞþRüŽ»~“mZÏqqC*ÃpAE}¸Ø»ç,ñÊG%býæ`Ä'˜ƒÊBHÓ+Öl›¶íÛ÷쪪#^˜u¢èÚ¾µèyH;ѺEŠ™NmÕ{«Ü¹í‡Tœì©Žh2 Øj€^*_ æ;÷‡_Åì…¿‰ä¤¢uë÷0ª÷Öä’!†ß¡}žØ±ãJHä¦✓z —‹UÒPŠÞ›Uæ|¿BüÌ·>ËžrU_åO8ì+,û‹K„Š)E‘¢U³d1ë»%&ómÝê"¥Ùâ°€—h?Ä´“~;w] •>ò‡z¼jýf±sëHRHEýÁ˜Ö‘PgC‚VÕœ/]Þ&|޶´$çh2b«Ü¹í‡U¤ì¹h2 ˜TOÔKJKÅÒUëÅgß—3ß¶mÞª˜ƒ'AŒß¤aûuâ£o„hÛ¢æÑºšL˜æÒØDªt³]OÌ·¾ËžrVWåOW._ý·X¸ôO±zÃ&sÊÅY:F’p —ùÚã¡õ­Z~, ûŠO,ÃÝ'Z4_dNëÄÅm¶{Å»„š»(.:Bü½åñò‡[ŜkÎ=sÊm£6—ÌmßvþŒš¦Hª'’|÷ìÝ/þ÷Í/¦Ú™$ßX1D‹wK[ñΜÄaÛ‰fɉæB–X›C‹¼Â¥ƒê@ Vðþßì¦Ú9–ÊžòR[åOùþ+»ÿoöB±cw¡pÑœnÂ*0ÉM`p{QÇ4H§.áU[aAbs‘šò]¸ÐäŸâlÑâ3ÄíE=þÙLã O¦…a¶Ã¸Ô-"5už(,:NlÙy¾xöÿöˆKÏ<^œ m+ÉEnû‘gÛØ@ Ñ3`R?ÑJgšó-†ÊíÛŸÿ€êM5ç|ëCí¬Ø‰–V->›·Þ%¾ÂÜôy§íëx¢©Ž –~c¶·êÀÜÅ¿Š=XpEs¾±Tö„}´ËßR»¾ýùf}ŠsííÚΉ +ÁõZ/îæ) ÂN#9i©IßvLɼÿ•!va+à?ö«ñ”ŒUîÜöÃ. PG4ú%·å#àòyßBìë] õ3­v®‹Wá–!ÑD´}·ìSZWUÕÜ*n<ì¿2V˜‡­f±ZöDq´Ê¿œñhâµæ›Ì—˜aÇ“ERâouÂ|+£Þ—¢”b ð¢yÊBìXU¾[Åhu6å+c•;i¾¸í‡ƒû­+5ö€±º³ üU° ‡h8<`I]áv:D[!$ôµ µ t“ônÇv¢8€U¨ìi/k,—=C¤å_ž_ s©+ÄÂåkD‹Ô¹P7Z…86 ¿UËO°S`™˜µ`ÚíVsý1ÔPÛ‚UîÔ†¸íÇf3UÐ|5f¬Îˆöû’úùOt´H„öùƪ!ÚˆÆß×nF§S³‘¬æ­>è²ênëeOøDZþĤ6lÙ)>œ÷‹)ñ¶Hýª>`Jš´Û! Íë$ÅÒ"JÊ_(Æ*wnû¡ Å~ê FÍ€©±’êŠN;¢ÕÏ{ö ‡RdîŬ/À«K—ö‰»öštý¡v:ÕÅÝÝ­:°ûOc½ì©|")Ê+-6ülá li+ît‘Ó)Í牿·î뱕©|@šl•;·ý]=ñœØçX΀Kq~m!~ŠcOÌ*Ѹ·¨ÄdÀD?m!aS3;ÂpO!Nqª߆`jRþ–Êuûî½â—UëD³f‹ÌGCÈoU46KþÅt¦3ºi꺫܉sÛ¯ av«O-¦ÉRCQ£¥•^/ CbÜÞ2Íìl¬Å'”6á!`¯ee*Ê>:çz‡GEø¾kRþ”WªçËþØ€›Íà ĬIQq›ÀˆœÝë8ÊËšD+·Ì õM?–®?—Ãå™Ò[$¼q÷\2iÔ€‹[Ü÷çG3îÑgBõ§¼¡øFÒ0дOu„XE‡Ê6Qzâ`*­ÜAoÎ?ƒæüp Î>‹ÐA~Úø?b&ï‹›h–^u×wþÈJùVUO 8™´üSÇ=À1ÊÿÎ899œk‡¹|•þññ±ôÑ÷WRÿ¬SiÜà+xþùmØõ!M÷°üÔïsF¯÷ÈlÞLå;­((§*ÏêÞÉ7-XòÝ}ö ñaëòËÔ&½‡âŒÍí?˜å÷Æ_æ§œc1¿R°XvâèÛi ×zK¹H²WÊ0ƯóµŠ;Ü›=Ù8vZ³õm:qô´¹ÀÿíK*¶ÒÛó SégåJË–N¶ÆÃGÐð'f© žG8üã8==;04±–W­¥¨ªæzÒéýKÏ O¤ñC¯¢^!ز÷3Âá†ÝT\¶ž²{O¤õ;ß§"Ò¥U;騡×Ò² /ø—aÓWÕµEôSþIot^mùš­*ñ¦Þtþ¤—…FŠ}|Y,qð–"ZµåiÒÒ.ïI¼³ÐnÞàaw@8àã‘ý/dS°ëó§"­=—zÒ¬k(åÝÅ“Á$î­Èn»dïk=”ŽTúòU¯/¦Œô9¬Í÷¥#G~C߬ɧíÅÿ ¤D;祦þ}OâAÁ½åQ^ƒ÷T”’+L•>}¤¼±HMèÏ–2ÖÆjŽÂq  šúC¬ý~èyVÉüÏí}¢ç·¿‰ñÙb~91!ž7i0Šù϶Òr `5¥§  óOù•)£ƒ%¥´nO•WžC•U§SJòw”°¢­Ï;½—š0€Ê[ÕýQ¦ÓIcîš÷¢µO ó³Lí$%1Oþ ø¬Ó•ðÎH¼KXý:ûø(-%‰5{£ÆÞ{eÊ>ƒp`I6_©®©¥²ò :|è-ܰ,N·õª«µ.ÿI£î`ž +Èi)•\°ËTʘ¡€"€}`eb\–x«º®Ø‡·Y ¨;Ìï¹(ÑÜÇoœÁZÇŒÓPZj ïÚqì R/Ìþ†6lÙ¹õ¿/?{¿ˆ„JBpd6ÐR³jÃ? p!…S"pßͼò®_;bðð{¯:‡úÞñ[ŸÿDe5ýJ,1®/µÅû)㡾éãèÝo.d‡!¶u7ÞMŒë#†ü !¤ÑhxãzLf³‰’âÍ4ªO#­)¬¢»žœ.?í¯Œ=ÊRT²²E9–ñ\÷Ï›_¡Þìvé”wXàö§o~qÏýV³pÍ‚ &Óª©=Ž–Uq{ˆ# YÌ { à`æç–wù1õt¨ßÑÿ~ä[FL†vÓS*[¼Ê« Äg¡(¿7>ÊuìP@À>𲦕(ÁœI•µ…~ÏÞÑЀk çˆv @Ãï½aâˆ[hÌÀßÐû .=J8ã]|.p `5{"ky~TÏÛšXc6S~I1 _դΧ„øU~£ƒ²Ä·*?àã`é*8¸X8(Ê Pþ½‡~’?ƒrÖhjE:UUu"&´žðކGt¨Á»üy™'³À5¼Ý½á†óÐßÞ$n…¢üÞy)×±CEûÀK›½žjYc”»¿•&ÐÖç0YÊ«÷ŠsOü÷ôš ÍÎG®·n‰öòC³Á½.ž¬¶Z;è74eüïiáªÇĽ¾½Æ‹"8â^Ž–"L¶nm(\e‡ ÂR#¯†¼©°Œ*jmÔ+ýcŠ3o  ”?=É-\Ð迦âÒu<À,¡>lœ=•öl¸)\ÿ×íЃ®ÆG¼¸£æ½²±±ŠÝaä€{•C¨µ`ïò¯Ý>›1?ö`wÒè»h@ŸSiö·Ó=÷BQ~OâÊELQ@À>²ópÅ6·òéíÌÔÑÂewX|z?^zú-ÝØ C¶Î˜‘f6yº.qÙ_ðeÔ à²ª]BÛËdþþ…=œ¯áb©hê„?£¨xzvޏÎLC{X+ '@A#„|¸¢–6ï-¡dÞK7Pá‹2”°óሼóEÀ 8begLà¥Fˆ%x°mÙó9ý´é%QÜs–X‚…öL°òÚf _OXØlv±óÖ=‡¼ËïpZÉÁ g#×u'û`6V²Ð”_æ§œc‹Šö‘Ÿ?mz‘.Ÿúw4/·p8YÎó`8$`qþ„á7ÑkŸ·zÄÙi·¿¸«¡ðR.lìlÃÓÄ9‹µŠVmã¥e£nøß_Nk—§9½Oðs„@ã€&üÓÆÝl&­£¤¤EAA Ï<:iôlr¿T¬óýz9"ƒA?Z:¦F; ¿¡#Û‚’·L¤¾~$¥˜ô<ìqGüò%´ü6Ðsëò{§·xÝ3„CB¨Ê/ÓWαEÐO Ä½Š¬g¯ÇyÂüØQ‘†çžG¥Õ;¼Ö‰vôvä={òuõ½|ÜïÙ“³T Ÿ|[?bΜ‘ú'g©ÿÉk‰‹øØõÔë}‚ßÿ‡‹è4²Ô.yòuÕ2ù­Êå€ßO¼®ÚËï¼þܬ„æ¨ò¥(9#ER|?^^3¼CŒÇ ½š–n|ž,¾ЇX¢ci´ÑŽýG(>~% dž`mûkþBc]Æ©5/ói-|ÍÆ4Ð÷4|>„\›“°XòØôŸA™I÷2$„þ #´WþÖ(„ªü­óQ~Ç ¸ ¼tü;þ`óžO G´‚Ú¥ýΡ²}Äø?‡2<9K?ŒT¶\Ìå>9kÛ5ÜÿöRÇ%Žæ(yN‡ý³9s.ý|ÆŒ¹w° nT'œrïÕÍk=T®ëØ0z¥1¹÷fKEñ«Vªƒ†ü*Ò6€°yý³É¢ýÙ’[;}'/@øbN´¸´R$¯7j6;÷G8:,ÕzíÓæ ½Û•g•USI§qQŸ³ðö–¡(C=÷ëc8Ë_Çsëîœ;ª™7ÊulQ ¦5`i–sŸÝ‘¤YŽ.Ö»\îpÜK,d•÷Ã3­›xªÍñôëzw$ •}oˆîvùT¹Îu¹Ô¯©jÕN•#¾€-ž» ª?=¹]Ü\ª¯~³mÕ½3ŠÔ* u×Yí¾¢’~:žÞƒ þð&Y,»‚ƒ@Eá™™…ð_UVs('ÑÀ&o·ç3œÍ°ì ¼m$ßÝçèiûˆ]×PLs8Äç’õϲ7}pRíÑK¹ß=ˆYÜVã4°PsÚ“º‡Ò]ÈÕáH$#¡÷†¶Êãý<¸×ª÷œdŸ!Òt¹.U©tïãšµ¬ÓUäxÞA• q°ÒïpºÚ]ðêR«<»T8UªZ– nWÖà"ÛnjÞ4‹3òZXGäó…ñ‡ÿЀív;%˜ÜÑ×l¶ÞíÒ%ZX,©²útâÝ©oªI,±Â2+®ãM¼ù.Ë=m?Z 9\6úöÍùgÓWËïáMeY”sìP G™ M: Wl3›êŒì¬™ÊÀÍÁ;ÙÄñþ§Ýzþ«³ñËgÞ2¼o·[-ÞܸCࢢ:RkîxôFûO͸uÃWŒ4¿×½W‰fÞ¦éɼ…üå?¦E¬ëµ*ƒi¦òšãyíïÏÂs¸{)ï_îv,)A6Ç J3ˆåUŸ FŽ‹­g'3¬n_nk´´}§+ŽÌpõnx#w>Ok É9‡Nye¥••s”S f5`ÉŒ†å‘§c-NÅaê†ÊÇwnÀ13%΃w¸‘¼ÿFË>γÜn·Ý«R7™Ÿª/¹—¿iÖ¬>b¯»WæôЇcqÿ¿×¦j ^‹°ànfJÄódó—ÿЀå<ð¨ÜÞd³§ðfö“#Œ¾¡cc‡«C‡nâJe¤IZŽªeâP”<(åëœõ­í4‡l÷8GSÛŸ4ê:ï¤ç¨/÷òx—¿óõùôo ‚åp D?bZ{ Û>ḑhÒñܪ“ƒjDî¸AùMK41Ö̓‡pW5Îù}^òqVkúPæ­ŽKøƒKåJæHÃ…ì½±ª²t{AE;D”KýŠÃn‡±¾”ïGÂYÖŒdçÉ; E2ïA¯`ð?·wå¦Çq è)T[ë ¼ð„›,>4“Mñ”—à¢Ôx£¼f3 `>k; C)ùŽ6mN.ž<‹®ä}§÷;³é°Õû fˆ¨ØJè¥@÷Ù9CL3Od+•ØG”MV8g˜ut fY,¹<š†¢9œÑàŒ8¶ôºñ…Ÿ‰,K81ýýÍÎ×8?>ê<ÙþþŠª þqÑ¿ÿg,³–ÅßwSuã&Ö„<:Óñþ¬Yãç6š÷˜Xy¦Gnv^íù/¹É¾”O-{ïBp-é‚÷òÓi]3"yâ”‚å„ƒÒØÜtÞžÐB¥åòžÞ¼Ã®GÁY–lV¹\®÷ÃXc?…×÷!“šƒ}°ðM‰ã $âD,ôÄÄxÄ&¡#æ5ÊÚ´æ»lKÑØö³ÒÇÐ%ÙoRYõ.Z¾éŸ¼¼ñs¶t¸§}Ь£¼Ž·~ÆÛŸÞFÃs,¥}º´G/å~÷Q f0Hêî È4"J¼'û$訤ÎFeçSVæ,R«šƒèw9JG匓ž—[ ÈHøou6Ýóu×íźåþ™,Ù¼`æÌ5 fd´ /ï:0"·må5² sfñ¨Ê 2ØÓ\„£tǃ6б9)´y9®9êë‡SjÊ·¦2Ÿsó}-œ¹Ê*¦±å¨œÒÒ>o¢ªÿ§êÚ¨ªêv¤*a|9ªÏË[yÏ_l™¨cÁ›¡çð™q¡íBè&%&RbR"ÏgÇ‹›¾Æ€öæ;"„E{ÛOO,ö>õ˜ûé—üÑÆ]s™vn‹#•Ûèó¥wÐ’uÏñF·r¨ÐKxkJ13ä?£”/ÃB˜.ÉQ°[øj…ãæŽôlßí§âpv½¨¬´9~kX¨ÝA&eeÓE°ÁéåOv4ñZnò(Ð5 ´®&ž?——.è zGÊÔÔqoÕgâxÐñ¼#ÒÐÌ”ÌZ1; —°SSQñýb{ÂFk6 ½¶›?„a½eˆxÿÀÁ[Y3À¦ññ<¯X§“Íâ•“y¯âxr6æ’Ý2ˆ‡k})k| Vï¤,¶‘˜ÀQ¼(9)‰’’Yón`bþ—°:Ó~ÁÓÖ|G[Š•¶`0gO|Šn¾œ&Ž˜É±Èã<Õ¸²vïHõ½Ê+Wny“ùÕày¦\D&"MÆÐœ·Y3P’ÁKÞ’h|Ð ÜáI™bn :k#i८G.£´ôOºM†æƒÎ·ŽCíe'¨©wr\žîN£«Þž>Ь‰ÆB ò]ò!á^ N­ë@¿^I¼¤£žv–0ïK™÷iÝÇ{Ð+Xü‡ÐX§ó¤Ð3kƒáœ•`¨£#56*oŒcSïi,„'s·“F[Ám¤Š…–ƒqÑòûIü]cÆ[ò½Dµâø(q˜¨¤ä êñ.烙ˆ®¥1J1èaǪL…Œì­A,ÉÀÛ;¶S4ðÀMÏ|@ó…ðMàÃÌs¿¤0±û2mÍ÷XkûØdÊøGÙô|;­æ0©k¶ý›ÝAX°qÌkç¹§ã†ÝÀ ½Ø²6Öµš ¼íMHÀaàp9ê-6~¤öI3°K¸É£¡ÁÈsÁ6^òa¥²†d;œN©É_„}Ns~0…CÏ4s3IÁ6rF'ðÞÁ2A[¬6¯mkHì¡»wÅãõQùÛö†`ðeh« é“Ì4·ÑÞŠ‘B«ëÞ·`òB BuÆ„¥l,xN·ð•4Ði(µ±‘¸.P­ÕEuï*”Âë‡SEïŒ>ZÇ-.^k'£ÚIqBHS®‡v[R¨øàí,asð(󳣨‡À»ÛW]ÝžÛíGZN³¯‘×*ó`Ø-tÙ4¬aÓ3¯ë…p¦sÖÜáp…¹_ " ùBøbàÜ•íe™c½íõI4iÌ=¬ ßBkyªU[ß`kÅaA䥞çNÔàÊÈ–~Â$mú/Ëì2V{ûŒkâŸr ="I{Jë´9J*«êPAÀè” À`¶‚`Ccn´4ò~¢Üñò\“£Ž»–F*µ¥Ó¡’Øt·‹âã6ð»Û¹3Ã4gðë<±Ô¤¶n,‡›DZÖ2ú™¬ùb™› ›–[oOÆøû2âï ÓÊê:—­±±ëªKg ‡à¹ÍÒX þµöêÀ ÌDÒ«+iOE¯°ñø„’ÿ:RÐö\qîeIòžNë¶54XÄ~ºñ\ÿí,T!¨½wr r·ß·¶iæ0—fžë4T]3I8L©Õ,8‹x¾ÑB*Ö’yb‡…¾‰,x!Ôq 0°¦Ý‹çvÓŒjär@&a áÁ Œ{z²¢ ! AÌÓ1b Ý5_ˆöø«m_§5±¾™& »Ž7̘C+ò_ã½Ë÷ ú»¨Q•’²¡ïùWÑ·g]¢þ ¢Dûì?ž²n•ÝJHÀÅ5 ªz–f6Iù îF¨æy’&ÓV\œè|¬¬ý óqw< dj´R…MM5ýé E8öªUõlž«Ž"þæïý :8 —“`/Ì{©Z+õ2©YðYðÂã“GülzõXï(°o&7ï¼Z_ƒŽ gC}])?ÃÀF­_íÎß'ð ÿQ˜Žê@®qÚ*ªldÓlèx<ÂÁ)tX– ¿±lGLÁp[²X,|4 B=Ûú¹…5>‚ÀæÛ&ÿ\Ð^Ìæ2ÁdeS§ªìTcÕ°o”ÀÛñ1¡¹Œ¬…sÕÒrûÁ̤Ng¥D­KDuÓëJR/„*„¬Ùdb+çf›ÎMíÔÈsØÀíƒg €WW #¾ÇrÛW«u4nÈtÌàË…ÇôÏì9]V厣ÃôÓÌ®k2ólWðæ(ðp홇o¶Â+/lð»ÿ{j¼Ãa?‡ëK/iìõ‹CÜyÔ¨\*˜döiÔšo_ýËï=QøO=t)Dšðá¢Âe‰iiwmÚu€&ŽPéÝ€{„ ¦GïQ?žk4Ò²Y.ÕÑHܯPƒSCvž›µ³iÂ2Pà¾ItLèµìù¯s‘‰m}Â\È.œNùhr8Ag3]W;ž¶pyPµçöõüA‚=®­÷»áž7>NàÙ·ÿÀË‚Á”¥³:€>ƒ5à ›fÙ‡ÛêäÁƒRžB ÂÉ)t!„QnOÙY ™Xûl°4lãúnå– D±‘¾®Â<̦ahª&ÁgçvcaË4h#f£•Òø{w[bã5 ¾w§ã¶>! hßPB£ky…†ËS-"ª„,ÌÐî8ÏB#nš–Z/Òóýж•þÐØ‡oT÷Üó‚±ÚX{7³»íN{³Óe´Õ9ãìu*½ÃÚµ‘U;Z5zg6Îeљ՜Ç7?úøA¾˜­ÏþÇc]sf;¸tåv$ `O'¼è‹O6 sLÙªü‚TÀþµ¾&*¸;$Ö‚¹ò&ñ¢Ãª»º;K£……³ôˆ§Ë:%t(ÁàáÝ!¢ÓA³34__x}âðõ6‚ÓÑåt8ª7­Z¾‹ÓÁ"Bz’v¾õÆCàú¼vh\¼ã¿6 aã©Ü-Låw+ÔõÝ`ÿ‚ñ÷W`Aj®øwk4º§S,¥®“÷/¦¬ºâà1–3h X°ÓàŠm8Tãúвìɽ+Lé_Üôèã¾ñÄ_âo"¥ÏèG’³q@8Ø—|öÉÓgÞvùßü’òðuç«õ¼‘B €†ˆÑ6 Ãý»iyæÆ0º‡sV“äÝ©’¯w~žNIh¿n¯l˜Æ`‚†y„/ð~‚Õæ ¦ï'n¯þé«ÏpzX¹C aIó@³ ä{‰ƒà{~Và›3pÐYŒb0ø»«ÈúnþË2£Üjö@†IõuLNÃÀ*+™À4RygøÎj³²¿kÑ,ˆ¡ýâ·\ê,ùAx ó3p݃\Ìóº=[Ïí"ŸPBwñeBÙÜôh„¹í»Žª¥óçÍcT<íþ‰ûì«ù÷ÅO¾­¡¶ÙÏö‹ßðoÑ9rcœÈ¼þü©×Õ›ŸzCóäÀ¤ésx¿o´M_ŒDÇ¥½öÁ?á;°r—ë´Â…j­3üñyXàÓÅ;çjçLuíNôüüYõæ“’BýN·C$ `­ÂÁZ^^R³}ýšgUÇNxêÝùË\7N?- V*„›¸æ†kŒÄaƒð ;š³\“w¨ì˜¸ê2È~Eæxè”dGO·iÎí ‚ûÎ{y# º©¤µ?-z·¾¾¦–Ÿ56²16÷šÞ†ÿÚÃw‰#ðÞêÓ¦Þ þ£H’áªÝÍo6B Q§¥ÆŠz-ë8žƒFé|…ßî÷y‰Ú ×Q›É.o³?…;=ñ!ÿÃ7lH-ßAÓ•/ž‰“ü.TçpóåènÞ˶¿qÅO¯ÖÔTʶߢÝ?r½u £zÕßÞ0þÑê²>È5áZVŒÀŸ«Å(—Óùþ®ŠÿÊ‚ø™¸¾ýß¹óÜ]è?Ú)|u—ß~ß9:áIß3ö~P¿Ý^f¾Þ‡à8äMƒ~öšûÙöÎsO~ÃߣÏñ£W÷5gßÞ‹DŒÑ*Šeéן/KHIy›¯o¤OÈuõù“ThÂîŽAšåÜœ? „1’Ǽ˜{N æ9â{(ïÊ7²6¿…<ÅÁöÙ)¡3‚:&]tLþz{6çÔ|Í põÖ=ªÝ[6~ºnÙâuük€qÀ!ô#I{øÎx \wJ¯ ŒÞ§ƒÿœN“p_èþ£œl½Óú™ZÍ+Ø4-)§§Ð¢±¢l#8{ÊËõžçýÚ0æôÓns©5™˜óí³3i €Ë¤¢Ešùƒ/Éê;vÈ´þÆï¡ßéV%$R°Ð€™8¨<†o>œýÑ„)gtÒýMò¨AÙ® #¨Fêë÷:a4t(hî¹*-w*Þ¦¹fÍ×»sa|º²zwBÈS:fÈû]J´ÕËXç‹%;ðv†Ã•Ón«Y½äÇ6,_ºž_ kšÎ¸öÖ€#M·à;ã*VnaQvøÐ¡ãNr9ó?)üç´Eê:þ£,á” t‹VøÇjÛ_÷Ó’ÿ¬]¶š¯_í¾I°>þ·w{?o³á½!]÷¹0§È;DrÏøgWmõOÌR¿ªµ_zèŽú#ümÕdŽ3ß’ÃÞÎáp¸âüº˜n…ñÙ·ó‡/ó!ÍÐÝÖFšAAÌc¾›Žͪ¿[¶eåŠm'M;ï|^Rs& š$nH®“Á•œ§2êD/ph-l[ÿö'Ù Ëo[ÿ–÷»zF¸FDŒBÐ ÆSÅ ÜkönÛ²dù·_ýØÐP'_§‹£šÐt}»uäÇù·†6ùÎ/ 3&ÖîX¿vç‰gŸ;…Ëy*ó?1üR­yÞúwkÄ;ûÝšß­wö½ò<<hÍçÖ¿ýÁ¢5¯[ÿö'M|ÓVÛß·cÛË¿ùrA}}m%¿p»o25¿0gÎÈî®Úv-[yž˜"†xn)Ø´ wþßËêÙ;Vi^-«›ÞO¥ÖôÂR£HÆMÅË“z_tÍÌãç½3 ^áèwºÍ‰£Ú:_ µ…p­««v~÷ñsø÷—ÃŽ?$gðÐQ&s|ºÞdLÒjuî9ƒ"„9ý¨^¿×ØØÐPÕP[[ÎoÛŽMëö2â0/CË…°…FCDƒÄ5ÌÏ k$™ŸíòŸ á̃ kÃ_òïCFËË2l˜)>>Õ`2%qLaÿC¦¹óWþ+ˆ pÛ·pÛ¯n¨«-+ܱ}Ëök ù´û3òÑg¼>gÎ¥oí¬øä2Žyö07ÖÑîi:B¿{cÞ(ûµ‰µ»¸vao¤’qƒ0qÅ''aorxƒ£/DÿÒ-Zp$ `¦…»Ãå3´5ÉLïºqÛú5øØÆÏ…†Ìgk¡-y}×1 ²Â òà@E‚ðÍ hanÆÍÂ7’µ_FOÊ!­mòŸ#À°[øÀP»§ñ‹¬@¦@·µû¦åHï³eàƒgÞÒþÊnw>³v'pØvbå jí0ŽpåÂZ\ õ”s)ëÒ›)ÿîédê7†?÷a‹d ž½jwn¢QÿüŒ¶ýþ²ìßÝâ¹/?€›ÑVïlPkrù}È .ÐçHºòeø R0(€ÎâÈßà.Fzèˆá¨"¢ДÑËŽ›/c$] |Q‰¤ö+Øø7p}Óºá9èÉ ù eùäÀBá{$sNÁ-\í¢[Ú=›Ñ‘ÿ|>¾¾ó1ÍYzóaŽÇrŠÓ•@/éT®´Ïå¿£ƒsfqÅkÖã“D2;ÿ| ¹x½9 áÀ^rÔVQéŸQßßÞN»Ÿ¹GÜïê?³­V]©IÈâï0o>r£[úÅHÀ²’¡óõ¾† Pð…ÙQ8êð¹µö«"p:Ñ @ÒE6DÔTЕ Bgü–£Þ %x÷¹B±Âêw¿«OˤºmØç†Ú­ë‡„aOϦ´)x°•µ`}:¦qýÖ€«TÜdY$mýKÔ¯¢IËâÉ †ÊÄsÿêægYVï #é"ŸÅÚY–Oá{¬qV)OW))í^ö½8¤EZËKH›šÑ.,ö’yÀ0Ïs}ZkÈoaì¿'íp^D£nMÙ1·¾¯üŽm (|mþ*¥‹ Eˆ5ï#cvO‰“O8¬¥‡ÉZRLñÃ¡ä “©t¯Ý`ÈìGßΕ?ý=KÜýý>àïbAL%…  (”þÏ7ìÛN)'ŸÅz4[…ÙËȶߵŽ5]KQ•|ý!š÷ .­7iS¨a/;mE9(8ʨ ¯P@¡€Bn ´Ç AùÒo(ó’)}òù¼Îw ÛwÄ¡6Å‘³1„š!óâë¨rÅÔPTÔò4cÛñU@vûŽ“Vž*P( P@¡@ S@špåÙv*z÷EJ;ãBöâi–…­…¯6)•9‘ü÷ï~ç%>t;ŒI¼›3 ,Õ.­hÀ]&™òB…  ›Xb$—µ—¶½ªœòog!# hÀ1ÂH¥  (P(]PptñKÁV¡€B… b„ŠŽF*ÅP( P@¡€B袀"€£‹_ ¶  (è±P± ^ì€"€c‡—JI (P(Uøà ÔçÊ;=8ëRÒièÿ¦1o,ðÜÓ˜ã©ßÓ1ÿ]FǼ»”†<þ&Å #ž§O½Ø½o°—ç´çÃ(¸Pp0IAQ¡€B…±Fø¡c)aÌñTòùlQ4ÕaϼGXjä j“™ô½úÐî§î¢ü».&,Yêó›[Ä+e?Î'M\¥ž2Íû“¨¹VpÔ°JAT¡€B…±CÞÓ¯§²E_½ºBÊ”;ˆJæÿ—J¿ý¸E!me%,|駱ü5¼cÒ~ªüe!Å;F¼a|ä«)“wKŠFPp4rMÁY¡€B…QNìçÛ°g»§¥ß}B‡çÏ&•¶ãðñ#§Ú›<ßÕl%cŸ\Ré ž{Ñr¡àhᔂ§B… "‹rCyö;•ÞèŽóÌûúvR'MãN¥â÷þáùÌrˆÓà9`CföVy¶mì2ƒp¡à QIB¡€B…=Œ\-Àî°74ªuGÝoñRÓl'À¾¾¾Ìι·þí{åÏT·s³ç3[ùÞÙEzÞ¤Á°ªõ.‡ÃQßê]ŸðnõMÀ?0 • (P( PÀfi¬¨×Åû$Èl,4ô©½|"œ)o0 |ø%º|Ù×-¾ç44`™f‹‡mü¨ÓŹlVkyÂ~KÀa'¹’¡B… b†¸âh¨¯+µè̪Fmçs±NKAs5fåvJìý;øÑW¨fójªÛµE,A‚Ç´&!I|kÈÌg ;huÀ 8Zñ»÷Î> ÙóŽg»C–­’°B…  D9¤ÃÙ¹çöõ}û¼¬0¡? ®ØÖiÑê÷î Ó€aD?tüjÊ g69’'N‡|{Ï QÅò…7`85ï#—Õ"µ{nŒ¬ª¨`×*~Éÿv¿ åE‡’ºJÚ  (ˆM x /ѱiÕò]¦œQµ;uh" àN·ø;ôñë4øO¯ÑÁß {e™‡J‡>ý7áphÞxoàÿÈŸ-Î*­ž2~u˜ýr‹ûíý`Ü\N»µrõ’ï·ò;ÀÛ»í}²ûŠ :d¤UV( P@¡@LSÀÉ¥—Ïvû ö|[˜£:קӂѪ|Ù7Ôëô‹:}·½RNœJ ETþÓ7í½â¹œnû÷Æ7¯ÄåèPp·]ÉT¡€B…QM©9BˆA˜YqüôÕç ìÕK³'»ìj]§,|í¯tð“·:}¯½Ê—~E»þz[{=÷˲ìÉN§­±béüyóøð•BX–Åó~¸..J+ù(P( P ¶(ÍQ ßF¾ntôJ6¬©,vTSU‹r¦FLiçLuU˜ÒhÓª¯ÖÔTÖW>¤V4àˆá”‚ˆB…  øBi‚† ³$Lj¯ó§ý©”)[*QAò ú>ošOš°/™ùó4_à°;yjߎ­®üñ»œNðåxCƒï6¬8a1õP( P@¡€B.S¦[0[æÕ¿Í6ìÿ/G}}:‚b쩯²k+jÖЀ!*ŒÉ®“÷/VgÕw9ƒ@>Àœ/ÌÎÐ|÷lËŸ³ðÂô í·ŽaÀåèˆ o;o;ïën!j3õ®8Ý6ÆòzgÕ“ùîMåºçQ RÚ=ðp æ¯ÃfãÇKCªËé —ÍÖX³zí‹KV¬Ù[uâ)ëÆO:õòùƒ/IÊ©)t ,߮ʩÙC;,ÀÁ¬óÅR#x;ÃáÊaµÔ¬_üýÖ.[Í·†`Þ°7=ùQø °èxoxð±ãTjÕÙ,zsI­ÊR¹(!|d‹Œœœäª%§ë ßöÙ¿{÷¥§Ö0f0§àˆ5,.üÔWßýûñ:ƒáLÉ5©âù¾ z\*&Üö]NW¡ÓáøößÏýE¶ý°¶ûQo¿2ÑAª¯T6k’ÚÅ8쎺ºUëî¯Z±¦‘´aùÒµ;Ö¯ÝyâÙçN±zjanN"7d—ÑVï2ÛjU§ í:`@LDáB &€Ênm¬)ܺù‡åß|¹ ¾¾¶’3¨j:ªù #Ì_w›ù™ó¦hÀª›o~ÌäHUÝ¡Õiîv:]™*•Ê•`6:“Í*£N×ãÊ,6›³²ºÞUSoQ«µš?Ïü¿¿²4Xþ¹oõæW-š‡X§r~£ÛFx¨d(x«™<ù"sîq£n3šŒ·+üªÊçQM¶Ú¾Íj¹tká+Ÿþ6Ú¾„‡¬œ#ß}ãt9?W9qÄ;iœTÁÁ5®,ùêÛ=œirö††:Ûóæ~É¿ =./wȰa¦øøTƒÉ”¤Õ˜ ›¥v9uñöº~@Ü¥RÛj´q†¸â8Ô–ÆòÒꆺڲÂÛ·l߸¶€?‡– a Í‚×0?Ëùßní£E«¯èg«õº7]NgŸúº&Œ@£õU™ ³§‚tÔ[iÓ®´*¿ ÷æ]E:é¸Û²F ¼õƒ>¿€ #ç9ºu¤ç'ƒ„àåou—ß~ßY‰É)ÿrº\Y ÿý¤¦òY,Q ­¶ÿTÖ˜w^5ð¡™³_| cå<èå5{ÖyN§“7îu‡Y­Ö¶W—þªhÖ›»8³8> Ø?´M†›Ömác;_Q>·Ð‚'¤éOÉ2ÞÅ÷©Ææ*ü`{틸n¤Eÿ†y¢ÏCž´Òä ÍÂ7b´_Æ%â5`¡ýÜøÈŸïV©ÔÏfõJr]vÖDœ“Ù‚a(HO„ÐÄQp¨v¢¿]‘Á‚êWÝûðg¿ðôß™6hÒÜ"+l$“Lj½ ê¹wMq÷ÉHVøÉ\Sp ;Újû\®y×>ø‡Gþóì__b„‚ÞîG¼;ëR—“Þc+úrcݯqºÎÌè1_ÜCa(!4Q`#xŽv Å ø¨¾¼Y«Mйo[TÊïTðÑÈü |Q^ä‹7qÝúÀ;âÈKÐhSôj¯¥ƒò~çÖiÈß2¤‡ü‡t¼’Â7"‘HÀêkîä6m<ïëÆé§©ôºžlmæ*ä#€N èfŽOüÓE×Í<—?õÂ>¦Ô-¯¡N á ¼¿ÂÿnგiRÀ»íëôÆ'~sÛ=ÓšÚSÀ}ýÈwgÝÆ‚÷m—Ë%:b¶40AkH3¨ G’A³×ë=ù¾<{'ÓDúÈùÉ9_à| |_D_Æ#bMÐêiÓ®ˆ3ÍoÀìxõù“®<(lO¦›ª¸´‚&éÅœœ!+ w R¢òáˆL+ž€Ï0OßÔŒ>Ù/öí¢ð¿‘”Ÿ :£€lû‡Tô¯1cN»qãÏÐýn÷lv¾ïsž|U´Î¨Uµæ·3!ìZú™L˜“fgoÒÒÅ—-ÍÐÙ&M’QƒÇ¬¶êU{ø„~«=@>™'¬<·¼–ÏñnÄ@$ 6P^“1²ÿp¸á9_¢ùúW_@·ËÏ9AÍ5´÷ çž;“S1ñ¡}ùp×p¾ˆ|g\€Ÿ øo…ÿ¨¢€lûNeŽžrÊmŒ¼ßížÍÎáàáË«O–›Œª)í_I'F?hÒ! š¨ôJ†f Ǩ£ŽT½:-N«"IÕÞ¶ÞiãÒ“NVÈó¿r¾xHAÍ—‘‘(€…d0ï5(Û¥ÌùVY@?Ð1)-õzN)ޏýc4i¼|oÂ/ø*ügj( PÀO ȶ— 'áW»g³ó‹.—ó6;ÿ !Ó™kfÌ„Fí @ðAû”š0„"˜„!$å×â0ëÔЀqäÔ{åývÎò{œeÚQãti°Ð‚.½éŽãÙÜÑ›—Eš–Æ<Ž>pÓQ•~Ê9L`ì|ø=Qé½µ_£OUºÂÿQ[I¶ÇPmˆ#CfL»ìj´}ŸÛýc.—z仯½ÎýðÝ’X¬ù~Ù«Ÿá¼W_ -Ö_B‡4{Îjre©¹GÀ‘ /jëV÷dZ8G¤¦Ëxµ )€Í g"ȯómiåf×(:‚ž½úfOæ/a††“S$™¡¥^&à©ðŸ)¡€B) Û~rzúTNʧv?ùǵsg¿>›÷MžìUª¹Ætñ¢)×AÓ ¬>¿™%h2P‘ªqäÜ¢òe Gš>^Õ‹WXã¦@àL—NoÀˆÆ[Žþ»ùî¡'ðUø8ï•z6dÛWk´9L ©·ÛîGΙ£/Ù¿c.k¾¿•”c³óFS._sÜLÌç†4š,¯ àíÓÐ.#º©ÔÀGË1ž³8¼d¤áÖM$qgÛh ¬î''Å©4:m§†Q b$Í ¾7áežÀ—+Ð .«¦C¥¾NÍu!á ¾Šz¼mïÁvSü~åÚµ+JЖ8TmoN¯Ãv?~þ,35TÌg‡«‹dÞl‰zeóU3¯Ÿ;cLÄ!•ÍÞGfÂË7Šåu¬žÑ G  Ã¦HµJϱýê€}å4aD]4e¼ßå*)¯!›ÝN}3RüN#Xþ¼a'Í_²ž*ªë(=9&7œ¦NÑåäAOZcæe#”&hй;çM<|g ºz¦×­Ð^:ÕÎl¹@’wU( mnÝŸúÑ- {q‚&¤aqà—ð ê_.]G½R#BïÞ_BçNK#ö¥_6î¢O\Í!'ûSB¦r» Xìi„hˆ‘bepóÝÝ9pxvµp=þýë.ä¾³†R7^|ãiwQ)Íåý·õï#B£¶Åc‡åÒ¨ûÚ¾m½¯Ü D›B›?ªÝœófje•ý®;pÔÀfçÿË¿ú–¿Êßá:«]ªlÞåMdçRÓpåÛ]ùD’–š{í§Þ°c?-^³Æ ΦË7“Ñ g rzìPAïM;‹hÁ/›©¨¤œúõN¥i'¡=ŽÐª-¼_¥^GË7ì¢ßœ}æïq:?°y¬ª¶†æfŠwû÷íEåaµ9èã…+iýŽB8B±vÞŸ~=u‚аç,XIë¶ïãD4tú„tÖ £ŽªWýêdϽSFŸ/^GJiì~žû¾^°Sh,¡‡Þ|¯ºmÖÞxÜšðô¼ \øF¾]A¼K]yÞIÖË·ç-¡xÞˆfÆYEâ+7ï¦/–n Ç7–¬ÝN WäÓ}WžCI fZ´z«¨ÿøÝÖÀ/ƒ«ÐÆóúô¢ïWæÓž¢!€Ñ6~Þ°ƒ5ÚaôÑ‚¢ÉÍ¢9||45mßwˆÞÿúgª­o¤“Ç&ÖÖØꮎV›ëûZZ·­ê­dæö‹Àÿ^=Ì&=§Óyûñr±ÿVSŸŠöåÝÞ\£Þ£·ËâøŽŸ–Tà~êÖ|_’¿ÃyæH[ðS rºŠäu¬ž#E’ô••CþèlµÙhKÁúeS ½ã)7+Mt–F›è¨Þÿf9åñ½».?‹;‡JKJàs?Š3hÜÐ\ºö‚IÔ¿oºÀ!51Ž.žrœè”lv}¹t½¸ßQxá³Ekhe~0ÿúŒã<¦µ%kwŠŒ®=ÿT:ÿÔqôÉ÷«éHÖ¤·Û÷pÀsIgœ#¼ñ‰´: ôñ ‡ê: ÕÔc)%‡ê îãyU]ƒz¸4p›(¯r¯.9iì ^¢¢¹ WQeM[]ÖÒ‰cµ)|ñ-„èj´~ÄÑÂs¼ÇˆÛ‡­{Š…€?<—xIŒ¸WVUË#>·ýï—?“…îo§H¥•5t¨¬yi®Ùº¼ö\º†¢øîzÖò1(ð§ý¤zö?ïvF£>x»ŸÓî\â%|wÿæî¾` ׋lÉ"ļŽ$ ØCwy¬ó…“ÇÑp6‹ `u9›qÑaH rÏ©Ä#oïyU­FC©IqØuɃ3 ª¦ž†°Œù69RÇýöòXÁšÅøáýé\Ö®½aó®ýB#è–@82Ó“hí¶}tö‰ž¨÷ëb#~hÉ耀0€t‚ýi¤âìr†5½öêeGH þC‹~îݯY VRFj9ñhëŒLãÛŸ7ŠKh±Ø•+#%Q>bë ‹Ï/„/nz2á×€ßw_q6 ËËâ6š%Ú€üx·ÓlöÃHŠ7ó3·b$»Ú~dš=ü,ÛœýʧͶ;±\ЄÇ[(CuMþÕ7¿ß4R± ZÎ\*b‚îNf+oh¾€ø88s¸Ö3/™Bï±ùëñ7æ±ix]qî‰Âô,¶ú7ïÇ5b4Î{` -¦>ÚÊÃΚ2:¥œÌTùš8;8¼màn6Õ­Þ²×ó ^¬m4ö~´f÷¢ N;¶­W|¼Ó¾™ðZ´à@Ãôi[õ²­¬Q¯½NPŠðX¾yú”vªðÍlA‚ ºœêó,´?`ËÒMÓ'{’ãˆfžkîäðôKfÒØÑPÂð}…öŒi¤%”œ'Ò]m?2=åì¦ÀÀ§æriáí,–ü°¦ieºË6]5s^·ÓHÅpS·ªÑ8 8Œ Áè,$ ×µ­èçõI§G®¿€…`ý÷«å”²ÌLŸ~ÜQ8ì;XJßü¼‰îúíY¢øqÕanóvk+Ìí"&«÷¨‰kÔj‚–1yü0ίcomñWæ|O‰ìtuýE§b¤l~Š]ð»óï¶Qöv[õE@”Qü.*©ÄÉŽ˜òèÓ+…¾]¾‰Æ Ë> žÚ¸À4 ¬LXzƒŽÛA[ÅV@ie-åð´Ãá¤jö±p,çùõ² <(æÁg&›¢Ç4Ü|m?2-åì¦@ÿûîeèÛû}¾bnƒ]Ô«Ôtñ¦+g.èn¹.½T³ºa±0;r§àÒëÒxRl/Ž´ù6tÆ }È;e˜ÆàlU[oó¿è< pò`ÈLK¢½ÅGš'L^åUˆíM”Âï”ñ<ÕÏlÆЬ»·ñ¦ïµ<…¹0Œâ7°3`ÜÐZ‘¿›¶³†=žµÔаg}ò#߯¡³NE0—£skOSn#û¶n5ÑØ3ÜÖ;ᾉ8…›]Êõe;ãáÀuWaPNo^o[BTŠ)™ü‚feÃîpлó—Ñqlºóò©t¸¼ŠóÛÍó¶¤BSݰs?åd¹ý&Úý é„{BœQ8|A{^ÈNŽXþ'!÷ª³4ÒvPÂøÆyæñ¥ýÈt”3S€0³oºf¬iÌð¥ðå®¶†bçlŽá mhXžÉxºGk*Õ‘‘sóÓ9¦¡mÕ0¦‹ì.ÖK~Å^Ÿÿþl ¯GÔðÈ=ƒ&7yGŸÆšéßüBw?÷]ÍÎdzóæ}ÿíáŸAêâµÛ¾/Ý¥§­„«ýD ¿üÁãÞ÷¿¦…¦²Ú½îŸ;ý²[ÏÊ¿áÎ þ¤ªoÖ\5ÝépüO¤¯R}>á‹Ã]U† ±6Ò –ÜëÞ–×FÁÂ}«­h<Às­ÞÂ÷ü¾îoÛ&5ÖBÆ›xE…a§€·Ð {æM"ŠVk< ”qNŒ¼ K”6òºâìŒTðÅçJûé˜kߤïu¯XL7r…Ö½ûµó±'7wüeøŸò¶‡o=Ƴ(ü„?Ƕ¥BøñPrT( P@¡€‡ìÞÀN‡¿lÜÍk~·‘Q¯óÀð…PÀ7 ÌÛ[DZ½‘#;69«Øû«—þ|Éþÿ¼×äÄâ[:á{KÅF4áÚÖƒ®Š_íRrR( P  ÁËp(Ðu |°k/=¹®ÙqÎhµ[ ÿûÁ5¥‹ŠØµµ¬çÉ’²î^yËçHó‚ŽeZ+eS( P@¡@È)ðÖöÝ-„o*+•CwÚÍ·4ä™’K•+?ת5ûäu,ŸËÜUʦP@¡@¢À¿òwÐK·yÊ<6-…¦YÕ¤³ñÚ²ö»É“(º\š½ò:–ÏŠŽeî*eS( °½§\»`RÊç!¦À³¶Ð«[vzr™˜‘Foœ:‘ô¡«àÉÓß‹Õç÷1³§»;8Gæwì X©ó ÌÇ<‹•*ðŸX/%y D.ÀžÇ×l¤ ö{<5+ƒ^<éXÒ«Ýq-<"ôB­væñŠÌ&pª{ÌóKÞų"€c‘«J™ ‘%¼aŸ,âÝÂâE l‚n‡Ë«E›Uù{èw—Ná¨qv±eçAÞÀá”qC ÷¯9R1Q’jMŽ]¹¾,lö­:«_=3ñÒr|ƒ¨'yæ9hW˜ÿo¢ˆCQS•D İèØáyb;ÀWæ~/ʆu¹Ÿ-ZËË„vñ³\ÄZzíãi?ï­–óøöV t°q`Ÿ{—¯m!|/Ê˦¿M]—IÄEÉ“”R¹z†4Ê«`Éuå¬P@¡@›ÀþÀ:÷³.äxäŽ@´ª[gœA“Ž"â¦(á=³Ç‹w§7¼Í´”›Á¡€…}ªn_¶š~8ÐÞx2»tÏæ"ùŒ»"€}&•ò¢BžK•”¼¯a‡"‚Gýuutât°;‡2}™ÈO?–Îˉ8ÙuE ¹,|ÅÐŽ‡xEª¹s#>jW°¸«à`QRIG¡€BÒ“iêÄžßÊ…Ø]]+æ|KYèÔjzî„qtzßLÿŒÀ¯l[óà4ÿ V(8+¤‚’Bh§@Fj;il´£[ñ߯PnZº‚*­£FC/Ÿ<žNêÝ«[ñ væj§*On™¨R¹ö;ýHNOÀ‘Ì7… z$6”UÐ-KWQ­Í&ʧÓÒ¿&M cÓSc./h§z_ì°ý)¸}Ú(O (P(v ¬()£;~ZE v÷Th’^O¯2F¥&‡—pd轘§·÷†#ÏHÉCÀ­8±ï`)åï>@åUuTY[ÏñmÝ#ÐV¯ué'¼ ôz%Ç›9žn˜M9Yi]JCy9ô@”§ü‚"äbeM=Y›xßäé7 ÿý&]X?Œ„¶¿ä` ÝóóZ²ò’/@šÑÀ;Oƒ“Ž^{Vâ„43¯(Xªž $íñ˜×Ÿ‘ƒ ü°j+}¿z+ÕÔ²§!÷˜žo±hUäðÑû³³ú©á|Œvi8„Üg‹×QG šzü:Cö騼¤@÷PÀÎZxÿÝÊüñ%‹Vþ×Ö[hKA1â ,Vjh´ña%ƒNÇA9Ì”ÂJáèß·™YXøê-Ý=Ün™k¤µýo‹Òÿ¬'»Ë½P¦ÉHožvå&ĵD<Æ~qט'‹¤s*°¤ELŸÑøœÚî¿^NÕ,x+ãôT’™Hñ:²³·a(@Ëy¦ÔÚ(£ÆBŸþ°FtþW{¢ÐŠ£©ó mÂfþî"z÷ËŸ©ŠµÝpðå‹þ[XÈ.âÁèšm{©èp¹{;B1(U‹váäk ›t ZÕ´iêî€ì^tï‚4~D©¹ýDj}ŽÄ¶ÿÙ¾ô§ÕÉÉý _\½5y"e™c;¤çê›Çë¨x›«ÓÚ'›wðè9ëÇ{¤êÁëàÍ'¿g­ç3ŽÚSoÐЮìdª6»cÞŠ¢ìG âH¬·Ñ #µôÊœhúéãÅVoè¸=®È§ÿ}¿:¬¼G©"™ÿh{xC…×>Y,¬5&•§ÅQ·‹Z£N†êmÁ·£¸F;¥ÖZÉz¨Œv¾”­ [èÆ &Q:‡¬„–_OëÀê g«Ùd M˜u$¶ýþcúÓìzŽnÕdm˜Oož:‘ÒYŽuÐ(êÇ‘ÅE§ÇÕ£xÂëkŸó‹"¢õ(,G¾è |?_²Êâ ´ƒµ^g7È=üõýRhÈ¡j! œlzšzüȈÖ¢¨n·‰*êxÿñÂÕÝÊ{ )üMp*§—ÞÿŽêÔ*ÚÞ¤,€;ݨ4ëÅQÀ/÷ª¶ëH½øáwôÐUçP9[6î(¢li*)¯_™&vôé•Lƒûõ¦‰£P¿Ì4°–ïëÉmßµ¡€²ÒMt0ÙLÃR“xÎ÷J6èƒUôˆNǪQå’íõ³ae_D#äzŒ– ó½›ví§ùK7Šx[Ÿîun€àÊ«iÞë¨7´=¸Ÿ"„CPÙQ`vþß÷k"‚÷(b¸ù_Q]GE%TÈÚ*¶´³O´Pl²°ž÷ïeñɃÂd‚`õŽ$©Q§¡Ñû+éÙÙßPEu½ð©¨ba^“ÄÏ´jr°€‡ ÛÀm±‚Mì_]!¦br2Sé¢Éã9ŽtfPë4´ýþ¥ ¢íÿó”ã)‘6{ ðFž[ür‰U='´äoÀ0=AøÖÖ5ÐÖ~`v†æ)\Ìû+èýoWг{“ɤç~šHA/&ð€Wólžó4Þƒ¸¡ä?І…ô#;›mß×´,›;í,dy ˆ°ÿiln¯ÛÂô8¿…¯¬$Мõ*e®ýœÞá$çŵ˜O¯æÁ@Yý5çñÃséò³O “QA m ·ýÌâ*Ò°£¦“=‘zJÛ窙'«×EE–Ĉ¥3 Ìμ•œKjê,bη;ÌÎíѸìêO¦¢Júaõ:ûÄÑÂ§Ì ·G±®ÝG€·s;Ûa¾?’x’„‚ÿ¼Ðxßc'C8ÚX(óœnE¼žêõZ!|%ᥭ³9…à”÷9od-Ú®RûDgÌ‹J6QI¢‰²+XcÞ¶ ÙùëwÓ'SFZ’Âþ¶ƒhiûÜöãzbÛW¹Iç“vRç¢ñ[ÿìLQTRtBp¸²²ðmàxª?óž¦ðx ‡ÃUWÉœ€Ûâu;¾Àø+dÀ2³Hå=JLþCðç{*-¯íåé*H¾+m¿«” ëûƒ<¹iÔ»<×=ä"æ°{lÚoÁþÃTß`¥’„Èõ.nu<7·çÀÖÚíb©T©‹!+&êÀ^¦g-[>"™÷ @0øò–UÖÒ ï}K•¼vw}NLáå,í[CF{†×õ¦¾ITÁÓE¯Ï[B‹EX°º*„•¶ïõÃü‹ËZýNyÝSÎ1-€=#`ާjáͬÅüwBXç© pc·î)&+ã­hÁqJÖü‚"!€"™÷(i üw ‡˜ë®il¤Ù‰ÂÜÃÿu-kéÛ{'P1kñ yY´Ø®´Éw´!¥í‡Ÿ¾ä¸õâ¾i쟂wÙS{Ü'¼‰tƒ €ì|ÅÑ{Øü\ÁK"0ß„#RAàÇÎWeUµo{©¢3QÀ? ¸;b‡ÐíL×Hæ=Jÿ¥ÐY±y7í(ÖÈV,›pLt·íöÊçÍw¥í·G¥î¿Ï|ò`Ž®àîgIp1f(1¬f‰Ü B˜:à­˜ #²¬Q'€»Àw]áµî\_öñ–ŠXf„hS±:.fŽ`Åò”vT>Éw¥íwD¥î}¦òö€V©ܽìnîè”Ü›ôx=­“qôˆ_+ðŸÛëㆎGüÁ¥\ì¤&éÆä‹ªÁ8Ðþ£œR†¦hÑÆNµ$Þ°;.%qt-h¿H£¬(s{àÍw¥í·G¥H¸ÏA8šÀHvÅ K#VΞ†ÈÝYg6âÊÌý :aY†ˆÃ/J’ôk¿»ŽÐ‚tÿ(£Ôô,n!&c Ãç–ÕqXFer8Ki By;ƒf¾+m¿3ZuÇóµ—föb.&‰¼UT3úó’ž³¡Ác£¥z¨õ¥§!úÐh[ëïï$½>à˜èb$îþâ¡|禀/v¤Ñª+üGù0À„yÖÀ!hÙÚÓ] a‡©`¿û—ÔRBƒ†¤™8e×S•í'ü7³Õet…÷Áȯ;ÒPY½= {¦èó8Ê…-Á~¹èl‘"jDwF›.=ŽMOm‘t*Žù¤ñ´ââ³iÙ…gÒ¥r(Ýh •ŸCZ¼«üˆ ü*§/}9m ©›:VìÏ:ûô“èÇó§¶(Àí£†ˆ:zcí%ÓÄó_èGÿ;ëÔ  ¤™zýÀعC'› ”Ðü÷“£»Ç óätRï^¢n?|ÌÏ=ï‹Ñ©É¢ì7wûÖ¼Î{Úþn„ÇÏÆûÕN¯ª¡¬Êêebí7ÙHZ8^Ç˜ÐØ_8Ôкí#¿¶Ê?­_úêÜÉÜîÏ´Aa ¤ü"þç°;=ÃÕC°ÀÞ˜ÀÁõÎäŽdmim©¨[„½Ê[…Ý/ó¨f‘ïì)'Qo2þ«6ЉóÐüÂTÊÑ·>ÙSHwzÔ7]½Œòt5Ïh?šé¸£¿ƒùöúÖB¸MK¡¹gœLi<Ðj )lñX_VAWÿð³8®_ô‹xeÞž”ÀuâW¹}[Òåß¾”ïôIO =ïldPãòž‘NïlÇ.¿D7 H/œx,ÙÛYz=~ܪc-\Âk[vÒµCPŠ{ÛêØoPÖè¢#µ6ÒhµbÛDì!Ü™ö…VÇöÎÞmï´WþJ^]ñïm4yþBš¹t%ȃ”KäŠd)¿7^Á(wz‘p­òZ‚Äéé€>h#¡Â·ùèZ. :]60‡f.Y!>Äï:ž_»ó§5ô)k5Þ€Q°™Gç·.]MVgKíã?Ü}÷«3hPRíªªñþ¬Ãë•Õ4ýÛŤ‰'µÑHjìH/Äd8vÀðï¼¶ ÃŒ#àáZÆa-9èßs¿ Ö¼Üɱ„Ç–[Hmð}iι9}HÏôþbßÃæß;;öˆëkX¨xC2 à}5u´Ž…°7Ø]Nzç^ºiø šß”Ž÷óήÔÖÓ3Û9O濆…˜Ê‹ÿl]yjâ1íià@3Ÿ)¡Tv/íW^GÛ³üßnóFÆù³½ETÁžÇ€AÉ t·…GÇ¿[ÿ›9bm«ªnq{mi9íæˆn—Ê£åïhñ¬³[8t‚ÅN¹¥µ´¹¸†–—o%KV¸=h™'dõ¦[Gi‘ÌââæÑn¶ðœ8ãm­ç¥K55d©¨¤ÜFîê|äë¶LÚ+ÿòÃ¥ÊxÀ}„ëZï4¤üÍý›7b¸TŠÌ|Y 8Ð:ÛŸ7(Ǩ~k¥[h¨«§û~YKv÷ÈÜ;ýc{¥rç[I¿”K0Û]”—Í·Û uˆC`Vq$ŸaIþw†Þy)×á¡À`¸;X 8`ße˜[PHÿÙQÀuâhóg ǡɉôÄñLj©ï Õa=ÉKˆ÷Ô‡P`‹,Ìä8T<\­åýuk©w•Åïì†r}ÝÆƒ@ ¿_±žòË+IÇ[ ¶†a)‰ôëþ9ô· [HÏQoØRYEØ6þ@Q+vF:Ľ¸(ƺfíº½ô‚1ݺí#¯ŽÊŸg¦3³3…‚ÏØò!!òË4bñìR5¯ÖSÑ€c‘É”)7>ŽÊy'™Z޼ÓdšL4.=™YûÝU]C÷A¹~ÃVñé¾Ú:ê—`î,åyQ 5-hµ¾Àª’2Êã›Í£wF÷ð¼é”ùßS#ÖåÖÕ‰9à~ñfÚÍu£+Ð'ÎDÏœ0ŽY€›xþ¹½MÚ1' ³l º?OG%%¥´`ýt¸†LlŽÞŸjîÒº` ÑÞf#¡Þ¶ ̓L¿ü帱ôâ¦m¢½4ùî{>+ä4&ôJóüîògU‘@Iå±2~'Ó#Û¦¡ ³öi}2¼¥û½º¦–ÊÊ+èð¡#´pÃ>ò•ú·ýæò£<çóÃÍl€ðiã66›7|.— 5 ’˜:UZEKb(g7²Ì&:ÔÐà9 åWT‹Q2>€–|kÁRbSÒë áÎô§¥´Ô”N;`_Ò}aö7´aËέÿ}ùÙ»øýC|”ñ…li7ça¨L Ô$ôÔ™WÞõàËcG ~ïUçðOÿÁ»#~ëóŸh+Ç×ö2™_ëØ„ê Ìâyb І¾>w Ù7“¾`?€6 C‡ÿ»*€eš!€!œu<ßldÓ´‰…çIƒ3hÍî¢òzʪh`!ïslèLþ€zÛÜ8Ì-x6²¨?`5JaS/ÖJY" Ð2pqû:˜h"Ó‘Z:R^%ʨå9áÎæ‚ýͳ+my¼ÊsÝonÛ-¬ ÿš4râèñ5›DöÁ(¿¿åˆÔï6]ÑÛâ á™Ê¬­:n~q³?R‘^GÛ“B”Q´%[ÂGw"¾Àîj^.ÁŸ„Z» ÞóuÜ¡!=¢‡àW/“ïsƲd‡ù;ø H‹ô¥s2®œ¥Ö³Ÿ4C3 ÂŽXéò¶èC‚Q~O‚1pÑèÒ*ó¿M|l–1ÀØ`a/›±ŒžÍèP;‚%Kèª!ýÅüq.ÌͦE=ŸÀ¤5g÷>Ïïžp1â7.Ti4;ó¯¼~K4–w/›/$Æû„úƒûÓvr°ÓÕ ö@YyØ­=ƒ÷€}ìP*p› U¼TÇ­›yJ¤ÖÔ@«÷WqÝ%Ú™@‡}LÇzŽ6!œÇš‰:‚™KV’Ök^ü£©“è«Âbzcë.ñY.›òAË@ÁÉZ>ÀÊñ ’Ò 7°ÆïbÍ?Дþ¾+mÿB¶tAû?ÂXcxÖä¬ ZÁS‚U~™^,œU.ç`·g…[Þl>Š…Âu± Šn‡`Üi`T;‚MÁ«ŽtÜ ­à97˜›o9”Ììó]Ñ!úïÎ="åLÖ° c[“3W;ÙEý푳ßK†„­ù3f·Y•Êy {r~Á‹J¼°¦õËÁ¤#V{LšÒ§7ÝÉkõ<¼ª¤œ]¹žÖ”º;ᑼ†:æƒC 0ÇBC †ú§-xKK+Á›¸2îè¥Sá‚ú:<ëß÷wø*ïéÚâ9Ö#£¬–¦ò¢üÐ #Ïe´ìáÝÍì· oa @‚ ]iûãÒSèŽ Æ4/bGͯö$,?’¬òËôbáÌkS<0‹àfbÅBáºXE·C°jnäïíÚK7ðת#+=oíg§šÑm,y‡=dqÀÅ»³Å÷ß8D;[-Ñð$#.§ýÅø††K¹8ÍÃÿ(.Û—ûŠéf^Š ç“=ÍBè žëÃá 7.þE˜™€¡›~4æC¯bíø…[å­¥ A¼ë@)í**¥=½âý¾@òÕ­;è­SO Ìoc=»„K.•—mžÏûz‘çþ^ç´[Zå¹çïEr½•j20å† ¡2A·×ö{ëò?¶s½›(ž>­6ƒY~é™ß5 `•¦çz@ƒ7Šî †ÎÚ²‹>àà ˜ãÚî£ë-|ëä¬tºeIàPhõÑÈw^»×Žø–«n~N&<âY Uݤßå´T¼À‹«/bO‹Z£z`ó•7ÊÏÿÁ£ÚÓjU KxqÅ–kn™„oU.×þ½ŒU”l~¾À¬U=²æ·3£ÂákxŸc«ÆõÃÒ§,€¥ÉLÒ¤õϽ…/žŸE»yêWûy‹À0´AÀ¢µ;ÉÂkQòföþÂ&6«~µÿMï߃‘¸ÍÉ]Më²Ayô ¯ÿ­lZKÜÕïåû^Û“Vk¥Þ‰ž÷m¹C(4`äÛÕ¶ßZø"`•iÅx4`—JÑ€cŒ·Á+Õùß,ò;A,c:÷+ÿ¿÷;ã>d¡úÃéúˆ“xôû³†±µ/s¹#ß™u G°é¥Ñ&ŒvØòœûg—ΙóùÜ3îþάé*£æ”ü7zZêü@IDATìõì"sZ£¾2QŸ¼¹²±âÕzAC~5ôÂúéŇ ‡¿/há"Èß*Þ é`idíW¨ådîÖîüOàþåkýÿØëK„¤T³î—jæfo“³÷µ×'_Úö@°Êpa"-—j–«Lz]6AÇ´4góiµ°s|šqîWG9oºræ&îÊ#Þ}$ÞsÚU3¸­¼‡k¾®J«zM¯«ušLŽ^²{óÿ³wðQ[îÝ’zïˆ"Ôg{`AŸbì*‰åYŸö™'°w *Š,(`A,ˆÒ¤÷Þ!”ž-÷~ÿs7³Ù„M²I6ÉÝÝ3ùmî½sçΜùO9sΜ™q;—Þ•á¾]w[ÒÒÅC‡æ#/ŸaúnPájÌ[bXc Ô`Ä’vºV䈓jö@†g7.:8\æ~[c)UýhUÔ‹‚Ñ•g+ÊŠ– ùb|¦ƒÊ—~ÓW ‚V\Õ¦%ú‘8Oʱn3÷zìÕˆ6d? Vê~­ ²¸=#/?Ÿ˜Æ‹Tn6t4¾Î_~|ßóÛݤ»´¡ˆóilæ8Ä.Ôë)~¨‹.„©ï©yn¥h¢K\ÜJ˲ÒVT}¹÷®ÀV‡XV{Î3mæ_¤¢­= ª–RUÊŸæCiݳ¬6G¨;LaˆÓd +® ýÚ±«Û~Ú0ßZÞÁ ¾å.1à¶/‘¨ã«Ûás’‡¾±Ž©©óäÖûC–Œd¢°£ŽÙ]6òˆ¶×ÀúŠ3®Y,Ÿ(.÷Üž¼ó±Sw¬º#ÉØÈrX®¢ZXÛÝ‹ŠJW ‹è€ÂÖp 8ÌÇG9Cƒ)U¥üiláŽG>iKÊÄ|‡8_yëç.†JEßÇÆc?èV‰~ã£E46ø uδáˆÆŽ•9¢Û~¥ ¯±Àš"ºIƒ ”Br…D0Á))f3f“ÄE£aù‹†¥ýÌê$}qQ6/ݵMë†[ïÞ ªc.Ýõ(*ˆ¡~&°ë\Mwì;;ÍØW³ÛŒñøyzyEìT \k›Ö@Ò£òOÄ1}Vð˜¹ì)/U-RA“lÁšÜSZ6-°´e® o†«¬íšãüf“—=áUò÷bé¢g‡¦Â†¸ºìÏÂ.Xf(…Ài@§,:`ËIú%àP‰Ž l"{bÇc‹Ï8üˆ“šTÐT¶e9YîÔ†¸í—…RøëºW­YôˆgÀa«‚ö6@´QÃp ¶~FÎE4Í.XÒ`FG´Ùm,Ô‹5è¦>Fæ¥6é]?ìž)H~^·æÖ[ÉÂçš ¦EgpÄ/»9é(h3ºøuÃ’?î»,m¦u#zK8<ó~ˆ›õ·¤¤—øúÕô½Ä0lÑ(êK«©Ëžð¨Nù£¡_ýxÒ¡¡X¶í¨èºï„q,¡õÞì.1ß)N9”#b.ÑÀæmëÛ ÆÝÄâèkl2b£³1ÙMåëÏù–;·}Õ¥ŸìÚ4{Ä« Ã–S£ÎˆFÊô³bþ—ÔV-â°?Ë!óœ"+–äó8¢©>v/ê€s\‰^I;åÃLnáÀ §@¹%¹YËû%“jÁTêß:нMcQ¸õ )Ëž€¬NùÓQqT ÕRѶoR_äæˆ‡rEï]8—·Y‚8V…±ˆ.bˆØoJäÁ.Áûb´yX§ìFµ¶Á(2ÏMs E<®*Æs-¢Ý¢q‚ûYÇŠDlš€ûxC&4aUÔ&|ËÛ>•bÝ;²€v» %:è=gᄎ§ªn)[,GÁæ‹-ú ¶¢Ž©uƒhq 'GœUmÍ$¼M…þ–h²[q'ziÀ@ô˼ÔmU ½Ô%n²ôìØB¬Ûaº²'d«[þd$™/1)š/íЬTњؘ‘/º@>kû±9Çqg¢™&c°6Gss…°ŸXÚ‡Uù:®qDb# 2K;+¨os‰†1Šˆí$íÖÙĉ‰‰¸‚c_êbõsÙÒ/Å[ºÜ¹í—F»Žž}, Qÿ"^ýL¥¶ ˜2gtJ``ÄxÉz2 L-{¶¶KÌÛ2]â´ƒYbcËD ZçŽh!I£këƒF¢×06ý•±ö¬óŒ˜Œß: C¬3;5¿®ßoª²'Ȫ[þRâ#fCŒ*Ò£ÓÝ£°™L&‚÷b3 ,£ëaKGÔÑÇÁŒ³blØ1 [§Bj¦#ÿèÔ$bº±°šï¤Ã¨Ž'8ã±1‡gÛFJ‡Þ5-(GÁ3аçh¢IvñÏÆ×Y,iR]"Áâ2@8äTDhhl`Ð{ÆB}xÓ¤¥D$I7DÚ1ˆ¯i”&ŒuÙ¨ÛíÎ2¶Ò_í4çK¿ÄÄ/óõ¨ž=gûÒYÖ}ér'@$·}·æÎV¾í¬,èjÒß×:âç h31`ª†ÓœîÙ'ré9ðV^ô­ïÅÓ)ÑA垆M£èBl,_ˆ#ÍZãÌ^]?ãëµsdMj}N˜æüHíL’o›zÑ _kO¹á壺.3+WÇQn™~âñâîç]mx”¾£ 03åOÄû«=Ú75vÚŒùѺ*{¢-ØåOÌ UÝljñ´5¥ç¤ bFÆII¹Ää D#4â„d\àÔpp„[ÌÃR!O%[­(«nœì¤ª4ýáé"Úb‰Óþ<ÍØ•Š$ÙLHÐ'`C‘…uÇ.H°>v£žÒœ.\…¸°æž–4ïK¶z aXÕ ØìöØ"íŽôz~$¹Ó)NÑÑ$½ƒY=ÓÙÆÄ|‰9Sþm þÊ=’Û>´!¥wœ:©ÝQ¬QÇÐ'Ák&ì%ÎYX¸?;¿PɳŒE£¬ªó4BT@;>.Î8KÔ·¤ƒVèTìYb¾.zìÍ4NŽ9œ92›pÕá­ó¤¥&díJW˜Zƒä«ŠÖ|HÕft>qFgDÒLyÖžâB8žy¹~S—áˆÎ`”?塬:Э]¬ UĆƒ8ݪ–Êžè©Éò—yÃ’ÎÃ|Á˜‹\^^¾(@} #ýb1¥Ý³4ìš%™5ÅAê[bv0`º§M>hqL´CdAš=FlÏ-Ë‘ ôÁtc±iZ¢†WQ*¾÷Ly­GÝl·y¦ZlÆ4QÂD bº$­R8jĤ+Ã|)m‰…<ž1ÒÛ¾#?¯ê›šËÊTÝ«Ï&؉UÐÀÓŒ X?¸g×¢ÄFZ³uŸ8«{Çj»ýÓüIÀ4ò÷t:4åQQ&@*8Œ“Ô<Ý`Š$w.ļêƒáhD’ Œ С©˜—k‚;­±Ó@˜.šøªÜ<ó]«ÜÊ£‘pD«ìÞ¼q5ÂÑÈWþÊû¬¶ßIšt¢³U‡Sn FùS&ʪÀTêGÛÄöŒ,aÁi;4 ª‰²'j«ü=y¥£¼LÈ0Σ#¦F Ø&ì9ÚO30}EßÓ $f9ßJÌ™¬…8Õ(jìz1žos!>çc¦ˆZ¬a"™¬˜mø‘Á´Ý¼!ÍíJæj,'’ Œ—˜°”„ið`Ø?€ñúÒ@ µ²®¬r§Á9z)m϶­K‘eÙÆŒü׿¿" èú”&JòÄßÔÒaµ™É*¤e6lT_æ~µîÔ^}Ž.]·½!på[žÞ‘00uÄ|q1œ§z柢¢ Ds§ iü0ßåÀ®I¶ZÉ{)!œ Ò„ê½zQ‘c…j ÖžX6K’/1ßúõê÷ÔQ’J.Ò/uÍíÎZ»ìO:È–zÙ‹ Puê|éшγ.¾ìèN¬nùS®*ªvp‹6¹ùâxN8žï…8ÔUR_ð0©Íò÷ÔkÊw”wð!³â ]Â8‹˜)IÁnäÕÓ&˜ÖøRºô­A ⩪«¨Ü)M¢/ÜÛ¾®i™ËûypômgU…µJßaøæÿ,ý¡h&ì[9Üû÷}¶VQîÛ²û 8µ­±ÙR• ž>¢†L-JÇò‹8OGCjRk‘ŠŠFÝÔ©ÐÜp,zÞFEgŽ’jŽu@UuräNª<£S‚Z:J“­1×E'`† º’ÖžÑEø­ÝºWÙ½uÓK‡(ÈŸ/ÞES“ï}é´¹‰^0‚‚QþD| u &ó£¨NÔ­j×êÖº*Ê/¥-ë916’,cc£ ÆKy$é×0ÔÂÙÇ$zÂCýl0OC¤xè1g lbH…¶’I˜˜¸•¡Æ.­Ð7obü”.Íí&Æ+UÌó5˜­ÇÐÐó§Ð}0œ¤#’Ûþ¾ÛgKÙ¶|Û[0 (ESºz, Œ13à"ÔÌÄ€‰$jÅFEùeÖ3o¸÷?ùþÏO¸RµÛ Óª†£†H žu4žç¢åI‰S§RPÔùJµu<Õa¾’\™žTíÒ*›Ôâ4?etNè HõLïƒÑ9` ü4ÍåÌ^ôíìŸ@ä:ã'cq¯)‰­›«·Ü%DoÛSN½ô'£ü)[uUêªü)]‹=¢‹ÔÊnÔ/W”Û`äKÒ«¬çÞCk1¤g Úe0nRó¥ydbÀ.C Æ`L\:¹!ˆÌ•ê²Á€¡í!‰—¾*f)éR:ääUÆŒk]•»Ì¥/ñ¯í¶¯»Ý'~›=kh¡¶_—íÞ+CÈÐEÛL XŽÌ¨’¸23ä¬_¾äe¥ÿÙÏLŸý»~÷uÿ¬–.ØÓ±x˜°q/;•óõ¨åЩÐò¢Q½dÀU‚‹ú/ת=¯TP$SÇDŒ—üIR§ŒŽˆpÛŸ‘)–ÿ¶ð£¼<œÔ.í€@?Ù%æðª3'i0ÊT4½D·:ð’{‚Qþ”»Ú®u]þ²D©>y˜Ø@ëâéJ¿bæ+ë9a$q’÷ôŽ˜51nª«±Žƒ; Ê¦¶BÚ #ã0Ò‚¦‡˜YòS;£ºm\gÊ9Xõ\泬«Ì¥oÜGPÛ_¹ø··sr°î®ŽÛ=ꆗóÐÅ5ÕL ˜¨¢a41ê„ ÿ˜7÷ÏĆ >ÀýâK¡»ò<¥:’05>iMééHì‘‚I•Fsbé×#ÊÄ·c•v²£Má©C¢ôI*¡eÞŽÉ<ÏDWuI¾Ä´–mØ¡l]»jöÊ?~]ƒ8iѧüI&\,¶T7Ñê}_¢Ü%Dwƒ&Miô~U0ÊŸH¬í:PåOùôçdÞéd–¥¯†œ¼øGŒÛfƒI´v¨®‰!óÇÅL\†¥o=íËSß:_Tï¥ÄŒz.Ó ä*ó^L[ø·ý×~¾ì—ùt&w·{T‰®Rˆa èâkFL’1Úo8ÿûO?ürÐ[hÓæ›=®ßtéÙj0æ„= Ñ£¢£y`£3DàQËy µHB ';)ã¡’ÿ(rÔáÐ-uFÆÏc¤ày_-!ßH‡æLIíL’ï–µ+ç,üú‹ùxa`‰+IÁtOøÎfbÀ%Ê´­1 ¾þ|>†AÌÁ*ÄåÕ2;&Ñ€¨†ê@m—?å-'é’׊¾¡:K–û©ÖjHÐÒxKª°e'ý¨NÿŠýd¸º¸ÊvVÓåNy“ØzҬݶ¿mýê¯~þjæ· ƒÚQ¶ûЊÈd hªg6LêHêˆiCª4¤:‰ž7óãYgœ?à°vÎ#^üàûzÝ;µÖûwë¨ôèÔªÊë„©qx:ì¥ZNJ½’ñÊ+h©´“®òçé”<”ô«tÄ>Ð:_Z²CÖÎdpåv9s–.üéó5ý±Á$ŽØúËÀ“ž _™ð6ƒó[î Œ´Ú0ˆXpôàŒþ.¾åŸŒò§Lö5]( ™–,ë`—¿‘@-üó¥ŸÚµÙVJ'_:ߥß×å3ÑVÓåNù+=»ìK·}²÷Xöë‚OV-þmH þ³ÎÛ} hžÿ¥ªáufeÀR¦ÊC°}Åo —l\¾lëÙ—^>Èív £ID…Öb¢ôúõâ”h›­úâ#*ÍlK?{‘«Älˆò“ÒÏÒ¿²WÚ®‘vŒ¢M+@§Áx·oXÿûŸó¾]TP—‡øèGf]éÞW6>©ÜA/ÕQ•[V­Ü~ö ËÏÅã<”BM”?Ò z(]Þ¥Ÿ)Mvu@é¶^ú¹*–.ëÒÏU‰“¾ñ×öwlÜð+Úþ/ùù¹YbšvÏÐe—²Ù0Q*çiÞ‚Fp¤~&h†9âç¯fÌÁýüS{ôîÐîÔÎ]bâDÅÄ$Z­XíaÎåžayùÙy9Y™»6oÚ¼uݪ=€@ªrIÒ¥Fx¢èG’ð$\ižÝ,êgb¸2Ë]¾Ç Â½ð›Ï^øø¥S·^mÚÖù´ØøÄúQ±1 ‘XþE¸ð%m??'ûø®-7mY»j7` ¬۽׋- KVV32`©Ž¤ÊDIÄ|¥t+;iç–5+7á·ï ©(¬ ‡Çˆq„áâÅ÷$å³%É—0íM÷Ô8 W3©ŸAŽá*w„ŒÆÏ†ÁÆ6üvàžêH$–;²Í.‚™vM‚—³tÉkFLJfBÒšì\};hòÇFŽž9B\ WiB,¿WØ:ƒœd¾r‘£`’€Iò%ækféäŽË]"ÁWFÀ?!×î1õÍÐþËÒ”{AKR©3&c!rr´GÏÄ`HÂ#lHC¸Ò@B2áH`¾È®á$.Ä|I­Lø6Ä€‰á–sÁ„½'\Íì¸ÜÍ\:L›™vÏÐåW³JÀDµ¬dÄ4|S!ækháê‡3#&<ÈI\$&õ² &fLÏä/ç}å·ð2¥“yâr7eñ0Quˆ€l»²˜¾Ý³tùµÅÌ ˜(§ŠF•Ì·ÂQÇLL…Œ³ìøIæ+犉ñ†3óEö G˜ÈaäÛ #bºt¥½3»ä ½ŽËÝ ß0%m^¶S·{œFÙ“õ8e­¼ã«³3`YNÄ<¨é*wÊòe¼’ùFê°oc” ’®’ñ·x†ãr¡ÂbRkÙ–eÛíÜ”í^JoO×MR‘²²V ¡DB…¤’ÉH&L’1\ië+ùFŠ,q!lèGØÈŸô£0¡ì¸ÜC¹ô˜ö`#@íœlßt•mž®ÒŸÂÔ½SDƒ"P¢*3àR%J X’.+U6r¾ŒW>/"àa!ÄE>‡ÛUæË=ÜJ–óSYB¢ÝïÞ>úHF^gÊ,¡5Keue3îáC‘—.Ù1—öççðF€Ë=¼Ë—sâ?VØY0x v.ÝÚëƒC´2ƒrÎÔÇ‹oF€`ê!€3¢1ÿëq<ÿ+‘(ye\~bF€ØËË€a©ów¢ »(˜‡]‘r†F€¨{°ï³—+ŠÊX~Š„°PØ‹`F ê@úUpŠwOCtT3` †Ï•°|Ë0Œ#P}V\Ýæ,?J0bR”CÝfî;`…‡Vm`Ë ¸6Pæ4F€c`¬äeÀšPú6Š‹pm Ìi0Œ#¦,KêkŠÒGfÏž{@ËüóÊ 8˜hr\Œ#ÀDúþ}}°ü(†² £¢mgÌ<˜aT9»Ì€« È0Œ#€¥¦çJ° ñòž¯#À ¸bŒ8#À0Œ@(º~Ž÷•"yïù¦B˜W`F€(]x%`l×Èp™@ü‚ðɘ°#À0Œ@¬º²Yl`Ñ‚‚bþ÷Dß>÷® à3R„3`® Œ#À0UBÀURýü§’’‚#sÙŠ3à@‘âpŒ#À0%€Ñ•WýŒ=ÿyþ·:?0®#Á0Œ#à]x °0ÿË ØFåø1.~Å0Œ@$"ðìT{]O)—?l¼ª3ªÓÃÀGîèèÆK"«êä™c¨zü-#À0a†¶•T&MUO [Ù8Žï7¡*?FYìóþsgþ߬.½ªé%Â-Œ3rqÜìŠ~s÷õ}Ï÷#`œÝXq0Á0Œ# (Š¢§¦«_áúL›£±ËÕ WÁ˜‰i Î÷UæaÛÉmш æ=6W8‰—U¡r0®hü #À0ጀ¢[ÞRt÷¿G%k·#Ÿ_R^'½ÕIhÎAº®Ýå*<š'ôF^,¥”VßiõùzWaµ8ÖÜ:ry8cU¼± º:èñ·Œ#À„)¯}Û)êÁË·Bê=ÙéC†X–åÿr–Ï4,ÕÚ®ßìý»é~À‚ÖÃ{6ÿ® = ¦®(+›µ>íî…ºèýY~˜hM(Ô]}W6=G²+w’=’á¼3Œ#‰LN·^šš¦>’³gûpÜŸ³`Á€“4¥+ôÌí•Ì—ð:¼{Ó½P]³aØ=W®vOoEÙ`Èã%–YzÞˆãY®Gås$_™GrésÞF€ðAs¿Ó4áÆ &ÚÍ-Ü//ÞüËøONKk+ƒºÝîâåG¥ç¥»EÓȰMÛžöæ‘/ìúqz7òStmÞÆ[2L¤^™GjÉs¾F€ðA`Ò´èöBè½ëÕo|!æ~SG'iŽIÒÏŠ²EŸ‰9Þ˜£ÊŤ–¦O`)}÷SµôÊN—bé*ß“êÙj±<$\ú Ýf̰ÃȺ÷é1 yÉb,k _F€ˆ`,Nw}0ÚÃ÷ÍÈñ…–NÖÂ’¤EÙ{v\o¼ÓõÊ0ªnýEÞÓ5^J·‰¨O}ýVß6òO¨¬hù™Ï€}ÿ5sèP·ïûH½g©%ÏùfFÀ'’œ+±µdü¤4õ…—§Õ¯ïóÊ{«ªÂ½üÚ]àÑÜã©ë3{ïjoÜ,¹ãŽ£«‡ Ëõõ£{[LôSXáôo¬fõs8'M®—ŸF€`"Æz‹KŽŠƒ©ùΛSÓ”%ª‚í%e'tÎgiB¿<º^Ëÿè.׉˜é/´nX>ÓµÛôôK!åv3ÏS…²¦qëS—zÕÐ;öuûèí -nûNßð‘|+qvŒ#À0Œ@1/Íhã8q`¦ë½À]›AUº[Ä%¤ºõÄñeW4›%FC(4ö~°ßìC¯Ë/»NŸ2 ¾ÝðÍרœ£™Ðµ³ð®­.Ô÷cêÏ,¿29O†å+bF€`@ m*—n~ ¸ …· ¥G¯¹‡ÖÒ}ïO¦µw8_)1 Î_7t¨w¹û'ï¶ÑœÎÇt] hߤ͙ß]~¹ßµÅG¤9žŽ´çü2Œ#P ÞŽn÷üôfq¥¼Oz\=¸9¤[ó…êùHÏ9×É@NÍY’ïa_æKïÖÞ|çžõÃ’R±h÷‘=#.ùQ„_™Gxàì3Œ#àp;þëÈ?¼bº²pâTuô³ïØúù; É¡(ŠÑÒúÎÿ®»5i%tªñ]§§½Ðû«i~¸ Òfëçbaù€Á·Œ#ÀD$Šò9L©r„EýH¸µË\n×ë“ÒÿÛqbººFXó,ÂþÓ#ó÷bNw E]à½/º‰m .É;.R ³›»LO[‚yàE`Ò;…æ> Òñåõcþ§ô7‘üÌp$—>ç` 0jä¸oa¹|^tBó•Ø€ãilÀñ{½Î`¾ŸÁº¿[+¼—æñüO ˜bUNbÀddµaXò#õc´Stu*ŒŒlØYë<ì8qÀb‰ï³xèÐ|ù=_F€`FÈ]®ÊcùUM{.½¢©nü7;è®ËGo·ë9}z…sȾßð½`4WF€`!Ê:ùHb£k ÔÏP${ÜByc\5÷"ïZ¨ÿ¦6T]ŸwÃíÉ+RÅÀ®Lx¸Lhø#À0Œ€]øÌÿ¶@ï-šò¹[9V]ÿVV—¹uñúŒÒ:v}ÊB]±Ì³©âlG¹×ßð0WF€`rÐSRˆWx`°+Ö…¾Ü0lä· æãc®Ä„Oo¸ãžØ¢:céÑg8ý¨¿Ë­ÝëžïF€`F V^Õ²œÿ]vEÓýþ>ù×·ß'%ù{Ç~þ`´\Ø—`°F`ÆŒ!–­™ŸÓæßÔÄOÝ=ó·)~çl]ºv‘ ådëgzÇ;\y øVâìF€`" ZVôÜÛö³4Í} ZfÐ ËŒ‚!ÿ¨*öyƺß"P–^ÑìG`]Lª¢ÞÙwÎÁi‘†WMä·Vð½ã&õuknœ!ÚéŠÞ‰&ÔDf8NF€`̆솳]!µí.‹jùáãG-7DÏk6LÌÉ;1ë~‚Ù^„U¿V«j~Ñ×MÖb#cØ‚ÒP1Û{ëÞsöî3cB¦SA§¤L‹ÞçÜ÷o¡è¹4Wk˜¦c¥@T½u‡`L/#À0•D Â÷E_Ø }aêȱ㱣”òj+[«7RRFT2® ŽâAg$Ò +Ž©ö¨º® C6˜/¤ã5Ì|ƒW5"'?$NEa¶Å6dßã÷‘«ÎýߨQǃG:ÇÄ0Œ@è pï¤I ´<í ¨~oÅï2%»AýÈô ãêì€ú‰ï'4R ó®Ö…v=îYPAÏGý¥ª6øöÉ»Ždº8~ð5H¿Ð=Æ ýæ~œî‡Ì˜aYWpì(¥·X”y×ßr÷ß¼î— Ü'›ð°®‰pFóZ¨]LKûkàäpHF€`Âä1.€µÓëÈi7E¥ûJmæzÒT[_]w=‡4{@ÿŒ÷˶6óFŒØy’DëçMwÑgQ”‹Ï˜sh>ÝÓr¯Þ>ËíÖ.ºz<:é4‡¬+?ZÁyÝ/¡T¾ *ö0_ýe¨)f [“áé)|ørùðó[F€ˆT’RÒb…3ã=H—Cp°ý#µÉ„'NµŒÀÁ “€ýr0à_ÀZéÔàšeC‡Î,qZѪ+›uphúv*#HÆyõ:$4<õõ­~Ïó=ëÃsô¼8ñh ‚_æbU,–áën½{ }Ïîd‚Æ€IíŒc‹/Ó&Œ¹…åݳìädÙ‡`F€¤È䱩Ÿ¡³¼]æåµ©Ž¦´ŸŸjïåÒ݃0ï{ TÐÝ!É.‚•óø!PY£ xÒTËBÓ;iŠh éV˜6l4[m=¾> *èý°ÎÞ•¯Ý¡ØwU(Ú³AÙÞgÎÞ-AìëÐJénà—B“½tj’ç”ïù´Ávµm²Öù*U+"þ˜`FÀèCÑ—}j á@ÛOjšö_MÑÏ·èÊ«ÕúL”-*dáŠnЪŘ$½ëè$}Ð#Éy`Lõ³Ç)ªî•~Égýí÷L[w{R Å¢Ž) 2a]þ±½]§§}Þ탴¤ÞŸLk_äÏ—r¨¶L;\Ñ&´Î·œtø#À0Œ@9PªçbcHÏ®5²[Y9¿9£I¿Ǧ¹í~»-f¸ÃQH›nlxtèÞ|_ò Õ^*ŸUíäùßÒsÈÿ˜1#&« óÌr8‹æe|õ‹@µ%`ÄÚ³ó‡x“ ¿ø²'#À0!`ô¡žÝÛôAÝ?4#gt²v«ª«_: rÿÔ5×h¨“Kl²æª¦Í`PÕ›’€ŠÚÛøçŠ’ËÂmÊfE׿‚õó¸ŠÂó{¬Óª.´·3â í%Ù1Œ#ÀTô§Ô—RŸZãnT²ûíÉiö¿4áœÄ-X0À:pàB%\ ©~1KLNQu›¹Î»}p—÷§Ü¿N°ûÁÜ±Þ ?cY+8éw?$ç½ÑJþÆ·ü¯\ªÍ€QB4Iï-œrSã—Œ#À0e"€þ”úÒ"ç2ƒíÅSÉŽ5ÏOoÖÏQñÖâ-¿,|ñݘ›ÿsgþH±´ž×ãôbõ3m?¹¶àØ¡ïTìä¥ë‡„Í~ÈípþY?¦AûÅC‡–PcË(øê`¨ ýÇ̾Œ#À0¦Gàña‡rÇ$iw(B}»ÀUxÍ–:ÁÚÇË®`¿g›9t¨[nØbñQM÷«"v·æÐcà°™¯D)ðkµ%àÀ“2È‘c&ܳúGÒÆí[d`PãD'›x–°Ø·¥§£¬…x0=ulÿºÈËÈq“ú+šû¿2NEçµG¨Ö‡ÒÇ?µ¦.há4B€d°Zb’³wæÐÚ_C ‡í–¾_Øà‹Ðº¡CIJ¿ÖÎw;õü?! /Ç&%æ}Ãó}Ù„=¾gÜÄsaUø{1J.˜ì^¬][lW­/¼9þ©uò˜nSlÏÖG>×ô5%eu¿sÑ/BÏ i=ìôб÷BœÆÆéeÅíÅÛÆa/Úóý <À¬þ‡¸Î³¢Ýq*íRRt5%EÁÞóæqU-k‰—EM™0®BÃ9NJy¡±î,˜õóë±íßxlõ×ÌjIØá/lMø™¥<ÌBGM`l¶8gÌèfß‘½¹çwŽY¡()'µEã,àÜãÝŸ¼+oÿ…W$<-éÇq–¼/}]w{òÛ=>LûËíVŒ9ä XhÌ!—ËÏþˆ´¢ŽVËÈðH0ß1²»È©¹–Þ—’jXúù‡§f}SRºtU<ª(¶÷k6¥b׏Éã&Ü@ÈJI;þ®}®ÔôJ}æWþ•7„b™6~Ìôô cž+åþZ³£Øçš°ë”gÉp¦Î׋څ~[ζÖn—kÚ¤ôÿž˜®~†ÃîšœÓVR‘“{âB¨•ÐþÐhQ?‹œjýZÞú»®¹-yU‰JZO8¼{ÓÂîŸ¼ÛÆ_8öó@Ä0`‹ÐþJ›0ú[ì8óÉÔÔ±ÏX­ö3IžË¥yG{þ!ªYß©ãǾ•6þ©U5›J±+bÚÞ¤ûÆLjÂúªR¨ ºPÛ‚4¿¥åI¯ö¥¶¸gܤ>´qNm¥WV:\/ÊB¦füÏîpöîÑÉzÓÓRsa<5PS –LLS6€!¿¦+ZNdúqù5ÍÏDÙ´ * %:ÜÅQ„Ãr7 »çEQßÖŽk* Ïï‹{tqVKÞ½•òÄÁ¤1þÓéîûÃ?í¾É“ëãÝsPþïèBYkUÔÿûßøÑ¿QX¼û[¶›:al’ï·Æ»qžEwFú„±—Ü?qb#Wž~7v”¡£¹ÎD|›±fú/Å&ÞOK»Ì?vüŸsN'F,ã¢cÊtwÆxX"â„glêGa 1’’!¡&}îT,xï"ζs ­åKœ:ªªë±mVë§ÓMú#:ùd°¤¥¼+æ˜/F3ƒõ‚}Ðh3 ÚŸÝÒÚz4m&Ÿ”šÚBäéS¡^½yÏÄiY4àÁ@Ypç"|;œür­oüÀõ',:Œ‚Tx¯*é|†C¿M0æA ïÅGäŠÞqnFø-¬£Ÿ÷Uu#Í?ñî1Õ&šSüÒ€çd Â&ø¦+ïþÒŽày. Ò¤EWÐ}Êèr%Þ–$²]/!üÐÕجU…>NªªJMm–Ÿ§ÆòŽ¡”Ï}ÎÔ¿¡Þ‹H™òÌØ¯Ê£7Ð2Gƒ€Õuúb,‰öK€ÁbœPöü½ã& .ºŒ|:?"¬ïŽ`jáBÊcYé[l ^q:/R…˜ìPÅîžq©ÃÝšþð¢é¯»gìøêèÀ–…"¿MŠbyHÖw–U/¬¶ßU&²è•å[^ýô!7lþuâÄ4Õ+º¤<2rùtd{ºïiHè÷ºè–˜Ÿ„Ûg*L³•”“ÕÕeA¦a9ÚnK.ë=ûŸŒÚTä:tšõÑQòE´Ë•ã^†æÆüÜXtP/‚¡¶tkÚ÷™Ð®(ì¨iFÜ—ò\sßoéT(Œ,ïAøµäïÌÕp̘~?˜Ãwº¢ƒ×·øuQÜ7½/rÝPÍäï½…“Qþs½~³0*MóºöÈ0BwœQÄ|ÐUåNøÏAºÃ\¹î×½a*yƒÍÔóÖà÷Фq©·Tô96¿ØàtÝ¢â;äŒ0Fîsí3vD³)ŠþÓÁÂ%°œ@?à½à.ø]õHÊK e:÷¦¤öÄ»`Øç&ÿß$³ÆËR^KD:׫::n¸””vÝ™±Xhâv”Ñ{Hc4ŸƒÎäi0´Ï)Œë†8/ó¦¿aUUËw>ï½·4`À@ëGxœhe;m²÷E 7ŠBV ƒEŽóÐäD:£@ó$ä¥1 zî¿ÇŽï@ÑhŠ¢áÝ"Üþ‰_EURégÕô•ô®lz(s:äq|cšH9£…¢ŽÃ à8hjO·b¡ Í×Á[ÊõòãÙóÏoúáFñê=5U4ò kÜâ&ELÖû*y\êmšÇöYVžÿA‚k‹r ¼zQÙt _z‰Šê§—ØH¹Qc^D™÷<ª¬X<1Í~:e›êÄIΕؔã9l?yÉè;s2Ðw]-!SöªŸ»Í˜aï>}J¿“f~Eˆo»ÿ}ó]´Ž™]€D¬|︉烩žJ9±Vº°£Cÿ6}ü˜QÒ?9eÂOšS_êÊõð{)&Vy??OLp¹œ·âùEî€ëÀ`tº‰6›ò~RZšMì9<óÃ8¤B2F¯9¿ü¦ôµ0#ë^0ÛnÅÚwÊøQ—~OÏERˆ¯$òFûM] Hrõ§ÃåˆDú1â¹U×ôW`(4¯,Ël$Îz nÎÔ ã¤Êé=Ìë-#|7yìÄËß=š3 ¥<ƶ}Êø1žŽžIã&ÿ¤kššë* †1‹èq9uÄ£,CŸVEjë¿È¿PË>Ÿ˜¼®Úˆ9Šý®-ÐèÝ­ÂÞý­ OJë̯î;q©[wϦ´iªÂ’CyŒ±X”Ë a–ØËÖóÖóÿÞI“¸s]?€9ªW?êڔLJ:|ßrtb±.ò[Hšp=ÎSoÄR§PHÒéõÑ£3p‘4nB/à4À“¢Oʤ72Ç€ q¬•uìMÀ»2î©ãGÿ2r4šþ`”Õ>ûõ”'÷Êwòê/c$Tp5´ÎŒçPæ_Ccs=uòEŸ¼íó©ßzQ™td\~é  ~úÖW8_ÇŒÌ%Aãʉ閑Bqþ:)]M•¤½å›ç¿·>Õ¥;ºÊ-¯aã£ÍѳêÌi íØ´Ï>HkÑõý)óuÅ2OÚë†%ï¦÷Yzî…˜VY@÷ìGÀïh&ðÏC'¤[W†@Ý÷Ìd¨ ç ³^5àbѦijé\XuÛt_¿)OYŽÑà çÛ“ÿ«cÆãžÞr8=K‡s4!9*«ßJ³2=9Ù‰žg~w‘úP†©èŠŽù0í…e1ß²¾‡Ä³Òk½Çž>®¬0åùC#jÔÅj»}fŒpå¿\VxÇ‘ìó@gÕ"¦ù†ÑcTHÄ`zŠv®¯é{Zn<*Â=@¾Cš×Óøx¤[bÀ§i)lñý¨‰çû0_#Ü” £ç ÓØéï&ù)]Ñ)ÌŸòÌ¿Ìiê4˜p纡AP A޽â…ÇÏõý¾2÷ºÕRn½ $®òè-ý}é2W„e5RãÎԇɾtø@ž+“¾¿øt×ásP[ 2½âÃ|ý ŠŸ?z«[?ƒB˜I#äžjÕíÿ@û½mbº27ujœWû¦ —Lõ?txogÌF£–vcž·G¬¢ôPTË\´ÝèÛ–t}?mC×éé¯aæU/Ößñµ|ªÔHËÒœoÁÔèĉq¶âi|æ¯ÞT[ôRæê>‡Æ!MÄò¶G1àLŽSÿW$yW˜´ ŒôûõOQ;b *,6ûæ@¿©V8?ô£~V‹&“üDrá6œŠtþ–ã_< ;ÔÅ“§Zyj¤ûk]×¼êg´;¯ú™²“±wóD0[§è"RÖõ9^Ær÷§öÒÜú ´û.ö(ËO&ϺéÈ‹¬*úmÒF–:,y[âúücåA*áWú¡…uì¼ý® »u§¸ï–)9Îë`˜euµ÷XÆô”Ñѹ÷¸çéÔÁè“CÇøÃȱZã,וc, æy–N—ÖæÍ[¦„pJ:ængAê;ŒÁå`‘^µyéï*󌼽‰ùÔ[07å¾”7»»œÇ¾ßCGºRŒKhñ$‰Ñ-”¾áýÝC1ù#b¸…æó\¹W“Q[sëy¿¢©«ûœ›³Џòá”—§ç¹rzCâ}¥D:P÷çt*ìb&x}ò-/õp&:¿Á4^}âÙg{îÉ'³åûÊ^©7ÅéÞÊ”9xc1Ÿý&Ca`öpažûÉ{ÆLHš’:Ö[/Ë£Á_úå…G=°ù¾¦±ôl¶2ë°oø€ïK¥#¿óGo0ê§Œ?Ô¯è”IS­W¢¿h»•ÆX7ÂÔQ£-™Ÿ72ú]º…öÞš››ž^˜-þw¤6rÇÙ,s|óŽc_Ôt÷Ûyë”Å=>N»mÍ-É=…^Yô{Î7<߆@Ä0àÀà<ͱAÂx_ü{ÆŒÿ´z3$R}YWûÆRTIgÃo6¤“Aà[ßj9îx~É7œ¼GÞ6ÓV>—¾b‡û 妪꠴gÆü(ßCÂÆü4µ‡ê;ÊÛýã&ßíÐ+œ®Ì‰hg{#Æ)*[Ý0—Vuñ””1+¼/*q£ÆˆŸÜùº’ã*8q£ƒÐçкhŠRõw°X”«çïD–T›ÍîÍ'Â->íü&å±Xò"i=Ö˜|#Ä÷wf¶#×›ýÆ]‡ž•-óô1c€Ü§0§:±Ð™õ1R¯ÂxmfJJåç¶)ÛnaAè—õ’ƒòÃ(¬% Ƥ¦;è©PäQΔþ\+“Nyñ£~–(½{6=¶ ¦„îDÙA!A+>‚>i#¿G,ŠzVGâ£ãä(CPŽEÚ3å÷._í;ê›Ïµ·Œ4æ»MO {_»½Ÿž²îޤsȾáù>0Š,0‡*‰†þÓÀœÍ_½ªýBTè÷K†(ùDêg„_c™–|Sü„ò;Â\JRO±o‰»èärZ¨ç,¾XÐé•qÐa˜cœ u?^O™–qµÚ– çÍÕúÈþ~ÐÐ OmJ¿š2vì>Œ´7¨Â}!zöóaü¥ ƒƒÂ¿A~.PtcþwM‰A -ãÂy£¥ñaWð^0 ûYÆSÑùÒ_Iy$å² à÷àû›°|¦Âe.TÐÞ9äÒaù¹bX®£2C¼•:v¤µï…®=1ÇÖ$Á;oBK’ö»ö¿ÛžÏb£õ§‡“¶/Ö4ýlŒ@Ÿ,+R«Ížêr‡Ô¼äÿK}RÕ¬»uÅÕ ª£V°*}Ì–Æbø~×c0w:MÕ\}Zµy˜¤Ô²b­š Ëi©û\›†`pq)®WÒ’òøá䱩ã1Ù; ’·ël?Âû<Ý¥õ‡Àz–P,oJ£) ¾†QÕdX)ÖâÕßâ ì–â¹Võ'¬¼ U‹­;ORm‹› ©4Ù ë=éOWX)¿˜•Yx¥îÊÿi? ¼¶jšóP®·Ðñola9÷mßðÞÃêýcÄwp~ÌüOI ß×d¸@ËôßD”nQ~ч ÜÝ\º6 ߉iµ‘h¼¤G§ß\½é¬ÐÿƒeuÿzÁíÏÚO~~‚¦ârXp?¬Z,³°¦©v8{e×À7, ¬ éyƒ›G±(K±)³¢®œ‰é ¯qa9õ" t|Ó,}_™úYúÛH|^}EÛQèHÁÐeVy8¬½)yNE:]Áñ‡1ÐZÜõý©¬¿c¤·ï+ï[~W–€KâQé'LûN…„Ñku?{ýÁ ‹#ˆC_®uš¶ /O;èrº¶ÁÈáEt†/´´Žö.]*ï¹#iKlÎCˇqÃwX°ó“HCëM!ZZÏKƒ´øxÖ0\ÚC¨çvUØ/)WuŸ •¥®Ü ZNbíXnó,æÕï‚yÛ©ÿåvjkáw¡Æê«èªwþÏj±Š0_#3ôlgf®3ñyœªê?bÐ@k¿ùÑGó¥?I¥~¥w–R›¼“•2–Ñr«¯ÓYxËÉfa²Ô§ž'ÕØ2®J]mM@œ;1ÅüYU­É+•^€-s0_7êXªîtoÈqæuiî_1ï§Z•+$¾Cqš Öå>Ž|vuàH9lÈÊS¡üŽ%j/é=@IDAT»®šæú µ"C–¥³ÑÒ:ö œÆ!ü£T/P>ØÀFKÖ4·×B¿¬zQ™tJ§ëûhýôý&Rï ÕÂë0Wl˜B8XÒkö¡ šCîöAúU]?˜2¢Ëûiwy?}2®S׿›‰ù%¨èó´÷R¼êkù%_AØê9ŒvR Pã  k¤¹¢yݰÁÃ9iãG/.êÈss]ͬBuœßµÃ£,¨Œg’¢3œûš½1aÌn4 ôg%Y_ܵã¾ÊÄY2†à<‘!U®+·Aýû᲌˜`à”›åløfêØ]ÁI•6åÐÕ gj›ö:mo]c¬ÏWj’N8÷݇¹­Ë1й©ŠÑðgŒ#ÀÔДõ³…À´ÏoÌ|k·(˜Wo,?Zµßµ·æ]`¾©Øjò³*Dß0Œ#P§ÀfÀ»zöŸÖ)1˜83à*ºEµ ÇÚº†öÆ ¿—4¼ªBdü #À0u€Àš«[¶)t¹Î)J[Ý[±k»ÚD€pЮì>ÍUH‚?aF F(Ô´aÙ)í€~ê7{ÿ‘M#? ^†t$ìÁ0Œ@  é^Û,£dõs93à:“dF . £±¼¨/Ñ@;—Õ«ÿU]Ò©i3ŽÔ’ç|3Œ@Ä"àÖ^éøÛS?ÚÊëxë 60®Ð9IF€`êÅË€±É«Ÿë¨0˜×ðœ,#À0uÀò«šö„ú¹+¥ õs.öo/qô`]Щi2ŽÔ’ç|3Œ@D" éÊoÆqX ¬Ÿó¼Ï|S«0®U¸91F€`êuCºÙ±—³÷¨H]Õß«;j8åpXLëØäZ6*Qßûp/aßèÞ÷9Üó^“ù‹ä:U“¸š=nßö–í)/ÿèUØzÒ8kêç]}ϸw¾ø&Åìå¶ô…"6:Ç»žHé‡ã×.»m‡xû)O&ðò³ß¡lOH óõ*º2ÿŒ/ìŠøÒ®cÌ΀I5¨Þ=ú¿+Šú\‹&õô%NmÛœüÙ!€Aˆ8«{Gú)[vŸþðWS“øÅí>õ¼4ù5sàçÂ/dGïEY Æ…ëT0P ó8üµ©}º>køcG¿÷Ü„WýjOº®+Ë7!‹ ð;òž¯u‡€™Õ¶FGyר”GÀ|_è×¥ƒòă-`¾u‡V¤Lø<9|°…ðŠŽ‰›pëC?²£ñ³áGåɃ®S¨ì*‡€o›²Ù¢&| µ©jO˯n13Q=9WŽ%vH˜U98tM `f¬ÞñØèËTÕò,˜‰~÷uÿTì¶HÖ6^ü„áE¸ÅÆ'>}͈äËñµ/<²ð Éu*¼Ê³ÖrãÛ¦löèÔ›îä_H\2áZ££Ê iz±ñ•úúÖÂ*ÇÅ ³2`õ_ÿº5.::vjëf ôaWžÉR[• ›pkÙ¤¾hÚ²õËmÛžÖEáGÓf-÷*ç5€¹N)Ù¦ê5høVÏžç$"´éÛÓ²!ëÁîê:™3‹Íú®¼çkÝ"`ÆŽ˜˜­¥i·`³æ|-,ùV­’n7_v¶ŠÆ×ììË/OF,1øÙ _ü"iPÃu ήúÈ6¥é¢yçßÍßžòònÁÚ_jûdý¼¢Ï¬}+«Ç ÌÈ€‰&kTtôÝ;µÖyηzÅLøŽõ5¼1Åá‰R0שêU#þÚÙ¦ââï öT¬~ŠÊÆW>eY×·fcÀ†¤2dägÂj¯–E’”VcuÁƒ£ÒøüË®êDh.Øü£öà¡Áu*xXrLEP›Â’ž¦ÿºqµ)Ó¶§¿¯lÞ_žû ù·Àb‰þ˜ Ñ<˜’Ç&$\B›l`¯y aJG³I«Ö RE‘ñH¤¨¡ Ìu %Î.hÈ6U¿qã‹©iÛ“[×’™V„þiŸY;3å3_ë³1`¢Ç"TµípEkñØUÂ1!&J·Ù£hDã+›­ü«ŸÙ“cà:u2&ìSMd›R-Ö¶ˆJJÀ¦jO놴o޹ߡ2«V‹•ö`g"LUa€ ÑcÅÏ-°½¤Ùh3Q±Už”úõâX?6Å—4ª¡ÃôÖ›•Ï¥ß/"®N9].ô»•ß}´Ðáwð byž22ÅÜßWU)Íòâ5û;jSضè4e{ÊÏ+¸µ€¤s2¾ú­÷7ûÿ6;¦‘FuÂfq¤*4Ô…ª¢Äcogº¯17æÍ/ÄÑÌlª˜"1>Vt?¥µ¸ùÒ³„ÕZýµÆÔ‰µoÑXDGu¿ÆòP™ˆ O‹j‰Å7²³*h¹ò½ue¯»°”7úYj£NÕ]6=)ïÏ8.>ÿi™Ø¼ë °XpJIóÆâ®k.õ¨Ø+vë¶ïïÌúE¼òØ-ÂfµŠÃDz1óVM”ûñÞÃÇÄì_þ—þ£»°Zªß~ÊMÌD/©M©Š’¨5U{Úò@§¨;²ï‘-;P²ôk¢º#I1&šHZ± ÒP§Yãîœ^§Šóúœ&vìË@ǵ[\6g÷8¥ZéºÜn1åóŸÅSËæQõªWð?ÆY/Åu‘ e¨Õ:ü2 <ƹ¿­§KŒºs°ÈÍ/; ˜ùR*gœÞNtÿ‡ùÒóÜßþM&VÈ€),9o繉¨ÿF›¢¶dªö”¹#çF¨$HãEö»ûÆœÿ•3#ªdB!³fbÀÔzí[Zà%ÆE‹Ž­š¿U›w‹eë·{ð_k·‹o_)2³óE‡–Å­—ÿC4i(ò bÂÛßÏ8 óY¿ŠzñÑ⪞!^ÿô'QPè/üƒ°ªªHý÷ †1cÞñ÷¦]†„}aÿ®bÐÙÝo?þn±hݬ!¤ññÛß›Åý7^$Nim´› C­$á*; /Þð w «µS§‚^h•ˆpÇþ#✞¼ ó´vžm[Ó¿\ %Æ‹ë/îo¨‰Ÿ™úµhÖ°ž¸ç†Fìo|ú£èˆ:תiC1cÞ_â™{¯?,^#–®ß!¢ì6±xÕVqÓ¥g‹§¶‹Woßÿ±ZœÈÉm›5ÉEq “‹×lßbmÿì{º¸àŒÎ• >4ƒÕ+jS¦jO8ìè!Ù¨ÑìßTfΤ$Ø™ ³I@²×*LÇNäˆ=‰VMh³(!¶C"žöõ¯`„ÍÄíWœ#òÀt‰±âD£;Šðgq}ÎÊÉÙ¹Ðsª¢·F×ì+†_užqÿëŠ-ÆiEï¼@\yAñåüe"ãx–ñ. ßÍþõo±nû^qÅù½DóF5.5S™KœéîNæ5Üó)zŸÖŒs­˜÷çZcÐ'3LÌvÕ–ÝÆãÎýG éxÍÖ=†´LƒER=óu8‚ê6j¹èyj%útngÔã­‹m{‹÷gÿ.ÚBµ=üÊóEw0d CŽæœ­Ül0ù¶Í‰ÏÀÈ)îq²Ž™¢=-½ªå¹(Ž3 ì%?6Æúv„”CÈeÓL°¯Ö*1IœK×í0:n˜¦9,r­Ù&âc£Ä°ÁçÏÔɼúñÑÏÛöfˆÍ»yu!-S–Ž´24ŸÛ°^>fxÏþu¥!ÝÞyõ†í„ +¯¤ꂸOiÕÔ”7a.šÒ'Û”)²ª¸Ý^éc©ºÍÜ{Ì„1'!`F|‘5åÑL²wç¶âËŸ— R'SçEîxV®!ÈtÛ@ÕFîè øûaÀnHÆe9·¦¡£;hHËÖïô;tÔ#“G§6Mk‰ù†¿Ö pÞxðù½EßÓÛãHÊ?©žŸNºšœ&†QÕ¶=b㎢?Ž®¤°›v쇱–jL±b0x,+jç†~™/ÁÝ®…§ÄÇyÚQ!$jvµ‹Àš«[¶)p¹®•©ÚTå5yÏWó!`&\ëRJÛæ yª,¿˜ýë ѯk;ÌåÆŠ¦ 0¼Ë[Z‡Ž0îiÎX…š™©ë¤Ûwø¸hIÁŸ#µ4I0'ví…}ýu¼×u­ãñ 缕‰X ÀARꓯÍ0 ±šÂŠy;ö6¦Wn|êœÕ’c`©ORk .>&Zl8x Ì ';cz§Î`(t»ïCâž‚P”ù½g\WgÄpÂ"`Ò9àÚ•Ô.‚Qu.´„ƒIÙٹbþ’ Æ\í®3TÒ¡Z#I'4‰åvŠÙyâ§¿Ö‹üB:ïÞ㨳#邤ÞmÔW0|ÿ¤éâ…¾Dzò¼ßð #P]0æë¶î/LÿΨ³ë·ïƒ¥þ90˜ò¨…IÊ¥©•®=Ò.izÕ‹uc€«{²dÞsð¸xø…Œ'ÕóýºsÌ£ßøÖ¥0L¬.åü}°ÈqŸ¸t†‘ êÖ>ýטּ9žšA buF©÷__Ñ>§·SÆ ÷ú]tfA?ZvDË*|]ÿ®ýH* e¥ÝØÐãnV¼êå{‡\$ÜnÍ–ã mS‡IN.ñ<ñF jP=|bøиÐôˆ õ²dÓ&Fì[¿)•§“¯)‘€ñ`’ì#zÖm ÐkqêöP{Ñ:ãøXO=.ýM¹”N«DBütö is(ßù($`Ã)B¤¤¤ðð(èH7B–€+À³4óõ îùÊ÷4Vzn—$mÙiÉp|e‚‰ ìJ3ßêÄOñIãDª*"ör)ýùZwÎs&cºË³ñ†Pvé-[}XwÔpÊ"À 8P¤8#À0&D€¶Ôý±bÒ”gû¥//¶-~Áw&C ¤žÊdÄ19Œ#À0å#pbGÖL=´¢PÐJì¯×!þÝò¿à·fA€%`³”ÓÁ0Œ@%ÐSX…®<)?Óå…S_ßZ(ŸùjnX6wù0uŒ#À”‰ÀŠåoƒås{ €Ã02°Õ{Z™ù…é` ØtEÂ1Œ#P1zJŠªiÚ(oHU¼Ôoö~^ßèÄü7Ì€Í_FL!#À0'!ð÷Òÿ …çiôÒïñx¥þ›'bS#À ØÔÅÃÄ1Œ#p2$ýº1F¾ÁÜïk§³)[>ó54`åÄT2Œ#àE`Åò)ðëŠçø6EdGéöW½/ù&d`2EÅ„2Œ# ÄŽáí£5]Fb¥G/ôœ»û¸|ækè ÀVСSVL)#À0âhFÁC8ÈY´|ÐV_y‘a MXÍrcªF X7¤5dÖžòf])½>8”ë}æ›B€pHË0‘Œ@Ažs,v½ªOàØÓýúv}'’ñõ¼3õdúF "øûšæíqªõýÞÌ*ú(%ea„ÈìE#$o˜‡d±1ÑŒ#i¸]z*N<2ÎFÅ!U‹úÍ><+Ò0·ü2·åü0Œ@Ø!°ìÊ–g`ÙÑÍ2c8òqyÏ×ÐE€­ C·ì˜rF€ˆtÝý² Á×p_ž1çðâ¢{¾„0,‡pá1éŒ#þ,½²ÙÕ~/*Ê©Ëf·ïÿþÙë2ëâåÌ1Œ@(#°êöfqŠ.^óæAi½¿Ú·ÙûÌ7!« KßîGźíûı9"3'OÇÈ0*Ê*êÇÇŠ†õâE·Ž­DÛJ¥Ìáˆ@MÕ'ŠëT8Ö˜’yrןºhK¾XvtØ®G+‚ŸBfÀ(=—Ë-~^¶AÌÿk8‘SˆŠ® «š/Të Lº¥|u%4W=áÒbÄ× WˆzñQ⢳zˆ ûuV«%(ip$æ@ 6êå”ë”9Ê»¦¨XuE³î]DƯ*êc=çð–“p¸F<^·m¯ø`îï"3»@ÄÄlM­Âu“PÕ‚)_M‹ùùEN^/ñåüB±`ÉZqûàóEWHÅìBõО|0ç7q¼–ê!Æu*ôëMé`¹‘²|p³)ð7úhH¿ Ϙ}àƒÒáø9´ˆXŒ .æ/Y'¾˜¿LØí‡Eó¦³Etô®/Mbìqq«Œ_AA;q<ó*ñú§?Š.î/.:³[§Ï Ô󗬟ÿ´¤Vëå†ëTÍ•i]Åü÷àæ#t!Î¥ôÁ|B±Ü[W´pº5‡@D2`MÓÀ|×Ì7.vhÔèK¡*ΚC¹Œ˜‰á7à ÷èÑëÄÌ¡RD‹»ø,fÂeÀejoªO3\"ê²>@\§L]M".ΕgÅY¿ÏaŽÁp˜{¾ïœýú˜…eMR¯Ûík¶ì_þ¼Ìè,›4þ¬N˜¯¬%Äø‰긿˜¿T¬ÝºŒ¸¨åÉ@|554A’/•a]×'Šë”©«K…ij痎èÄ|´Ãád&l–‚©€§ÓeØÐ4•¡™×)3•F`´´ÉÚ•Ø!sksÚ¢(÷·™¹7_>ó5¼ˆLÌ—:ËŸ—®7,ÔŸ]§’oéjDR Ñ” +lRg½ìÌÀÏK7| ꙫ>r\§Ì_|)Äv“±ìù¹3ô_ž¯ñų~ç†ïà ˆ`ÀÄÌhiH¡Ã!~]¹Ñ°v® ƒ«ÊV¢‰,±,_oÐËL¸²Önx*ŸùKך¶>\§j·NT/5× ±ŽœXŠø„Mت^|üµÙ{ì™÷Õ Öuˆí{aùSÄÇ®2m¹mÙ¹±cïaÌWk¬Š6iIQ½Ú¹ïˆÈ‚ÆÂÌõ‰àã:eÒJäCÖò+Z\ªkÂké¬+ꃽçìÝç„oðgÀé×eH¿ë·ï76Ù u¾fuDmB»q¹\.VE›´ ¨^‘ñ••™ëÁÇuʤ•¨ˆ¬uCZ7Ô÷»’Êýñ­3úÏ98]>ó5|kì•~NQPP(Žb{I‹šWc›l£šÐšN¢ñhf¶p€n–‚ƒjpãõêÊÈB;¦ÕЦ-Á¢šëT°¬™xòòoA¡Ò’b/´D;´»”÷z®¨Mk0`7Œ¯œ°~.Y¹è,-'LW¥ R-YÆ>ÔD7-›¢Ÿyð0`·QF–¨O„×)óÔ_JpÒÑÍhà7J¿?Ú\°)Ïœ èe¤|5-aÍ€IMH Œ–õâê‚e¨Ã´…! S•B¨Ì]ÝD?åƒyõª°Ð tpö ¯éÜqªi„+ÿÊÁ­[)šxS~‰¯¦nmÐù˜|ækø#æ X®"\Xè¬BÅQ'O¢_ÓX6S¹Qyåbš ®Sæ©EzÊ+†ØŸèBoà¡JÙÓøQóPÈ”ÔaË€IM(U…dÌDVСÄÈtêäA·TA³º6šCÅiøÖ«P+®S—om…X¾|ãdtQçé)Â-,–aÝf®Ë©­ô9s ¶ ˜à•ªBbb´Ù¨@…<° :°j9ˆ¬W!Ç€Qÿ©>qªå S*¹W¶¸^×´ÿHoEWÇôÿfÿ"ùÌ×ÈA l°”Tˆç’%±†Ž'tØ/õJ%­¦qƒGšœjiޜʲ r ¥!ÊuªîëÕª«Zvvëîi’E(_÷sà9ùÌ×ÈB l0£·³D×CR =ÛY,QÁŽÒ1^úcæ[3øV#VY&Á¯MÕ *O¹N‚R…Yu{³8‡æþÍ:Q±UÄÆÝã««J5R„EöÇz;Ë 0ßë¼#Žfm WLë*.9k‚hÝø ‘WxL¬Þ6Sü¶òÑûÔ›ÅGˆwç\ŠªTõvE_JÚ#¬N†Dv©l‚ád²¨vÑ¿Ë'E9û÷‡„ÍÃuê$dBÏÃy\¤£Q{ÎU”|EÕ¯ï;s»ù×E†Ô!CqØ3à`•Dë&ýDûç‰ïþ|܈ò¼^B­í¯Îì#Z5î#n¼ø#±qçlƒŸÓó!ѽãµbívsŽ,,8žà à[§lÖ8±i×\oÄ-Q§.ì7Nì?²RœÈÝ+¸Ny¡ É›åW4{XÓõ[ЉWîéûÍ¡ÕÅÏ|‰„-–„"+Å?zü[¬”›WàY¦ÝP9¾ »UåˆÃ'hkKÇ ÆCÕíË7¼+þÑãj1àŒãÄ;³‡†Jˆ¨h!¬6l¦PÍ ƒ.]E—Ô4ežÌSd]S­ŸüAõrMK²]NìXT DfÖÂf¯z|%ëÔ1œÒµÇY¿.w‰íûŠãÙ; ¿`Ô)oäE7Án#¥ãçgØlãj`ý¢Tˆ)Š:¥o5ÉÕT³K77†ÔÁÿªGkÓú]Å¡ã뼑,^óºèÙi¨¸äÌñâÖKfˆõ;¿û2Vï[#%vÄÊ‚ªÏ Nü ’¦ÓK7ß`”Ké:%)KŒk):·ý—X¾é=é%‚Q§(²âöº—@¾ñ‹ÀÒÁÍÏãýõEöµÄÄ4âSŽü¢yž²RD^Î+‘c«%Z$Ä5dzvx¿Ú›±L8ºZô9íVQ/¾µX¹ù#¼ótjDz)œ"$´ó†çFÀuJ¾?£óØ6uŸØ¶wô\§¼P„ÌÍê+šuT„>£ž"FW[Õz5Öûš;¾A9´ [t0‹%1®…]Vî~o´d|õ9}Äï(~Yøåã‡EÒaé0êüG<>öë®Úõ{ôö˪•QZCkì+ž#Þùf‘8š]µi<uгZ£aÈw‹X¼†v(ô èÈ?uŠâaW;Ð GyyŽïP„M)EX:Vû¿úÍÚs¤v(àTB–€(¥ìw''/Ç‚ !^û¶STöÞí¯ÀàåâÑIú©ò³_K×)Zv4ùƒ¶'‘juê¤ D€Ç) c,ã;Û¦aÎ×Ë|UE<×wΡW# ûœÅ*"Àp€ÀíÏX Õà,Ñ«ÓM%¾ðe¾ô¢K»+Ä‘¬ÍKS@æwÿ÷oÇF0ÚÌg§ÚþáK­®è7bŸÚ’‘¾Šî'¿cë…Û#0H»­ôkÄ»ûÞ¾>mZûèÒïü=ƒé§*ºv3LÖø¾Ÿü¶õ0íóÏ9õŸ=Á`û!^ç1uå óÚ‡ sölûŒ¹x­ïÇ&¾/«N•&9ÔêTiúÃý¹¾Í¦N;3áu«ZÌ|U}’¯ßAf¸ãÁù –€ÇJ|·¸âö´vÇW‚~!åTåSì•}#h6&±_|7¦M¡³ sÑ:õååfEw»GèŠú6çÝ“ÓíÝžJrxKC)½IÑ•œ»Ç ’qåF„—ªbýøÉ‘…ë'¦« aÃæu89æUW>8p¡‹qôDŽÈÌÎ3θ¦WÅöÝU+$ªv»MÔêʼnn§´m[4ªZdÕøJÅdïÌõ'ÆX„Çqa£—úÍ>øX5¢åO#fÀRÐåeSQl3tÅñ‹®§<¢()Z³p(º‘¯¤Š·¬oÓÒúÚŽˆW5Ö›>ªŠsk®;ö dlŒ=qtžãÄê‰iöF';6Êwþ®Ä¼ŸJRÄÄtȳ>}\Kôj ¥ÔÓ» uwùüT²cMêÿ³wðQUYÿ¼73™I/´ÐéöŠºÖ†ØvÅu]E>…€uÕu¥—puí –ÛŠ¢(Š…"ˆ((z‘"-Ò“™yïûŸ7¹“I˜L&™™d’Ü›ßË{óÞ-çþßy÷Üsî¹÷βt¨–H<”çFG€·þ\òã&Z¼j–Ñ,G_J§W9EWR–o U¨0EÑΨx*×øÇË~¡ÄX+]röIX³?Y,áoÚÍEÃrvKŠR‹:éŠòÜ™Ÿæz¶÷åY"à ðs©¯R›è/娹¢š¨ôÀ‹Õu+¦¢4ÞèÀ„;Ë·AèíŸ6+ãP s®~“®š&-¿D¦_‡AÃý>--§dÚÛ‰êſ̙sã„#ÞwqBNÓ£ŽÍœešHšãeܺÄo†µ<„9<jo±ç±ËR¬(åIžßMta†ÍQÓ#ŸŸžÆâ© Û÷Ò[Ÿ~KùEeÔµp7vd uÃÂ4VgyØÞR¹ÙJ»1ó`[J_úhI9-]µn¹zˆ¡ï!,床 é䕯SRñVh4cÈç¤ð â-3ÓÆkå›?þøª¬© ¡¦9›€’úér%Pt”É‹öð4"ÞT§÷°iãˆi¯Ûzà~·Þ ×/õ~îëZW´ÛIÑ/ÍÌV6jÅ+§ãŽ£^V3î¤Ñ®·ø°n…:ä_ª×LŒßx‹G—nláÆG”ª|Qå–à©8›ºÈç'¥1xjß‹ï-"õð>þÛ\ºrûÇÔ;sX…/×…;—Ãåq¹ÊáziÎbbzxÒP‡ŠC¹´eò”th§'ë§þÊÏz¬?žòB"à«s#Y3Ø ±¸ô|”6(T‘9Eˆisi1UýÕøªOÍúó[Ѭï‘R¶”ÃÕ•„[[žS_‹kGÎâÓÍzÔEUŒVΡU ×ýv¤ùüøt–» :’¦<é=¾{|<wtÚ'¯=OœÎžÈ£Iœ®¼ßCBŒÕxW‘ÌOŒ™/žÚ}àújõp*Agö»ƒbÅc<×ë‚×—^üÃú`Ñjêyt]´{™5ÃA½^ù„"rG¬VwÝÖ9´´ÛPš»u×5zÖ@ö0:´Á–QúûVÚ6í>xæY¡•Ñ·iÏÞ´ìÐSÁæ-Ó·>Z´\óu¶‰‹‚)NÁR‚}k>Š˜ßLÓØ!¹q§»ŽO+ÝmÁs!èÒTRëô~&WÉ-,¨ÿ™V¾ýáÑe;ù°D·{ñ%ϼžä1Ë `ñ_]™üë휢˜L û+Ïæü ©ÿ´ây"ï¦:wëñüÄØøâ©vÉ}©GêZµ1‹^þè<úbå#ž—êƒ' _6;Ï]ì¾Cÿ¼É„¯ ›…?ÓÁy_ÿLë~ÛchÂLk0¡`ÍJÚòèá«©fZÒáüݾ¯“¯LÛzhñX˜ ùg5‘ŤaU«“#ö3m¬ü¦ÄÙ<&èÆ"$”GÆ8Øœ\=¸_c>ð~>°¥á6¨·C0ÎöŽôðmŠAô§eŽ‚êsµ*#Ù’;e@s5<™½ÓÕu=áNÇ/вçþ–¿Î\ÊZH`5¶KÏWëJÎçÌOí“ãÈ¥F4?1¾x*:*‘†Ÿÿ<½n9Üû/XHæ#Êš÷úè›4¬;0tzûÓ”‚%YYó¤Àô$—¡ÿ}þ=:!A™£s?z¶M½—´Ò£Š¦ØZ{î(ZŸØ÷X$ÕYÒÒ¼¨nçl^´×I­Gø¢Õgí¢Í”SÔ‹Êʺ“Ͷ«Î<3ÓTZÚ‹zµ‹q›Ì@·¨CcÐ1iŒÆ‹<7žÈÞabšîñòô¾“rõŸø…¦8Lïb3CÏCf:7íÌAGÉáW .ÏÙÁ{'í{ãÒ]nwS|"õšøuúë=Ü«E2 ‰€=¬b5tÖxYðZ­Qd…P³Z­Â¥”UAºVNGJã@[JIú´ÑÇ„y|ŽMᬷ³:a·ô¹étw˜~®‡ ‘ƒ€/¾:¡]Þ£ƒöÄæMÃOŒP8xªmboºíÊO°ÝæJZ¹þeZöËtZþË‹ä¬G1Nsƒçùš’è¤?¡­§QÉöMdNL¡nc&R ³IŽ¡=ÙS)oá\ê5ù%*Þ´†öÝ &àyÂÑÎ2c9L~GQ‹aaâΔ,ls?~ƒrß…tgÕj]±}Sϧ“¥MUž%!C … `· š° ‚×m£h¥¥6cMÚ$lh¦R:âhK¹ï hÛ6Š‹]ƒ8[ ôÂ3O˜çdò´¢â“©´¬™¥F•S›˜(Ðè¦édz™nÖ´¤ :dü’Œ„ º&_uMŽ&“^HûŠŸ¸BÅSÝ:œC|ää­§oy‰–ÿšC‰Ž”cšzÃT¸éWCøÂéúf¼Fyûi× ;¬Y Ôeäûþ,CÍûê}r4lý•G–Å,5¾{›Õe˜¡ÅwUüÛzÚ=3ƒJwÿæ©‹b2Sêu·S꣉¯e„ÍYÜÃefA—oll •—•Syy9´6E¹(V¯ ««” \f**;ò ±#6(!“ùÎU½á`^/YèÂ*\áy¾&Þ$µœ’Ì.hÞ‚¾ht°¦—é&è`Ê–iC‹€?¾j¾²wò06z¬<|üÄ5j*žj›Ø—.;û Ú¶k19±OvC‚)6žÚ]þgÚ–q·‘7Gžâ)Hî¯{‹O÷‡|&h¶´Z,Þ˜hĸãà-xÅoãA-ÿþ4ôlã åÖDƒÉÏEyµÅ•÷[â=7”¯êBAòT]ÉçÈG Õ àš¯H4œ5ï×ö»W7¹H{mØÈûUÔ—¯ªRÊ+‰€D ¥" 纴Ô7+ë%H$€Àýz$q‰€D@"ÐR¸¥¾YY/‰€D@" ˆh¤òõ¼óù÷ôî+ƒÌE&—´R.¸Š>íÿ*-Ém©oæë4xÖWÇàëYÛ¡×Sÿ§þWm>ïq å ‰@3A@ à _Tî¡c´?/øù‰A’!“K"Þ~°ÓÍã(÷ÃW°™±F±}S¿'Þ&s"V¦ªj{vøëùÄ{ §\peò§D ù! pó{g’b‰@³D åüËIµXéð7ŸôGwïEç¿E‡¾üà¸úÔöLw9)oÁÿ(õ†;ŽK#oHšR7·7&é•41 ReëÞ›JvaÅ+ìJÄáÐÂéÀü7IÁŠp5ƒ¿g¼m!/M©@˜×7Úù,ƒD ©¸©ß€,_"ÐŒð\¬ WaŠ ˜zv4*ß¿;àøµE,ËE ÖÔ.µE9î>ÓiÁZìÞÁ».Þ÷åµD ±¨Î‘Uª,G" hÖì+.¥íÅÅTlñ½«¯ÊEµI%Çá\_êuÏq$—¬£¨6/‚S:mQ¦z•##KÂÀñ¶Ÿp—áùïÚˆ6lßGGŽÓÑ¢*ÇIþ¾ùÆãÿ¼é×ò—]¼ûQR\ µIŒ¥'v¡nß¾ÐWZy¯y#°{ÿaÚ°c>VDG KŒ·¸F¼7m0¡©x*ûf¿øëúhóvêVQNÝÍ6*7c;MgyÕ©8rÌ)íëŒWWöŽf Ø‘A@`úJAg¬Õ"—‚ /¥ñhõ˜×~æ­—ü¸‰¯ÞD…E¥ø¸‰\ب¡Ì¬«®±¢JBnÞ¿oÍ„rlNLØêðãe¿P|\4 =k]rFc#¿‰åÃf…ï'Íü´ð‡ õç§zÔ´1yªÐá × tßܺ“œØ3ûººÒ9½­ôéòõ´;þ꿹NÊËsv‘­Ë uÆ«+‚5µ›¥lÿžº¢Ï™>u§”8ü¯Ú% Ä2’D Œ´ZÌ‚—w>bm÷-Ìå-€à=ES(?ÛVÎS 5öf”™\ä ö…eôÑ’ŸŒ†úÖ«Î5´b9&j´?¿ Û÷ÒìϾ£cÐvƒŸ¸†ÁSãWýJË÷¤a];Ñè>=(ÉüüccQi[J߀pé®-”<ä2̆)¸Ò«!o(¶gba^sÛÂÚòbúl(²Mb vªâ­HYv âÚÒÈûÆ@ U޳àe-eáÊõ4ワé€ÓAëº$цΉ”—` ›ðåÊ‚ËಸÌ\h/ÍYB‹Vm0:ñÒeáA€ßá‹ï-¦\GE£ñפ1xêÁÁýiÞå ©gŸB±¶{kõïÞžöÄw£ý±êõÈò/H+/¥¶ ¯7÷£×iíhf¡æ3ÅEí¯¾ûgûˆ}ü-¦‹éëÞ>ÁØvTÐ-v©:>…¼#h<Z•f­×…ž;›œ®\Gó–þB‡ õþÚ%™ b,‡zeI\æ¯]“ æ.^M W­7èc:eh>ðûZ„w÷Á¢›”Ÿ±PðÔ†#GI«Áƒ'&ÄQOBsdAfÆô¡Á=;ZðŠ®£#àÿâ9¼{g?Cm.½ÖÃmÈN>w(•îÝAGV|Qgr¦gE׋ í·OÇDøaD43í¢uf"#Hˆ@«ÀÂäÌÂwío»iþòµt8ÎJ›;%Ö„(pÙLÓ2ïë_hÝo{ MX á0r}³æ÷Äf繋Š~âª5”§Vç#ËжŒ»JÂô±µ¡~ã)ÚG1k”A3ÓÎuA"ÐÔ4¡èiܪ³Ù™…o¦OÌY´šJ¬&ÚŠñÞH LK ¦I¼óå**-­æèHy1uÐQ/ù71æiüÄdÊS1–;æ›Utç²Ut¸¬œ2Ï:…†¤¶«µæl¾e-’°Íj¥S¨?†Tv$õ¢E=®¬S®5ã=`Í—é`zz$GQç¶ d‹¶´2ÍL»4A‡l™MP´ ',1æ[ŽñÖ¥ìé\\FÛ0þÚ”šoÍ·Æ´lkGÑ{Ò’Õéòsf2ÙPÔD*r~3_±·ó18ðE?1JuñT:ÿúi}¹w?%C;|ä”tÓ‰ÝÈR‡"›oYˆY,І`‹¡A=ÚïÂI[©µ%Ñ=˨cqN£¿,óýfç|h¾]MÔ Û8ÐÇ42­L³0A7:q²@‰@ Z¼f¡Ë¥aþe4Ë2únÃNÃ;µ)Æ|k`ÜO¦‰=g—ý²•.>½¯ÑK—cUÇÁ7_ñÔ5~g‘ÈO ”?žÚtô­Ì;Dw èC#ûž@1>–„ô¶[«afƒi7NYåå4°{;²aêÞ–ý:Íïý'êZ¸›zÙBÝ w4OØWYÜãy¾<Õˆ½Ùá*JѨW’J] ùÆÇÇQl\¬A#ÓÊ4»=¡¥ :leœð"Ðâ°[ûubA Ú±ç•À¼ËS"5Œ·QRníÜ—G½»w4ƪ¸Ç.Cd!À|õ;ÞQ¬)‘ÌOŒZmšÇ¤Ù$mh¾,|“k6?³´Ô~C‡¿Ì)xZæiGÃüœÅxÎd¸Ùþu¸çt:¡ñòÒ…L7›ÌÌXô^ àP š<ÜØE‡¿«Hæ'®1ÓçÀq0?´<åÖ‚MdÕ£HuOã~"[l˜o­pЊŽ.£6å冤 ‹o°å@ÓÜqdžñ-°'3Ó` ´q£<›{Ì0k¾† Ú£ýJ竆à-Ó„-€ùcçž9{ª–¡(,)CO<ò¿Ë1Žv ÚÓm³º-i6 ßGPßœ_k…ã]5‡P¾ï!jžb¾dáÇ£û7 ß(²b̵ ~üíñ vÒb Ư¡ÂW`-Êb–ÍÊL{d³·3››y\:&&Ú8ØôÌÏå7$ГçHA … `œ•˜DXýªÎµ#àÍ0N£±dú…ƤI€¿~/åxGÍŸø¥…‹§„ dg\ƒwùšpLt´!|ùÛc'H^}N3ø¹J×W f ›ƒ(W…¶Í"6/s™,„ÙÜÌW,xÝ‹o¸5_N#ƒD ’h±˜{ØÂTÈ=onš“ Ó¹‘74[s×E6 Mÿéˆwáól˜ µ©j.žb¾S{XËäkžîÑ5_Öº/³ nöðtC°àòŒ¬sy¬ó_þ,t-†Vìöx–šoCP–i+€`~ãq'²æÔ\²–`h ZÔE4<ÈG­†$ñ. ÙÛÜp#ðó( @~lf”-ObÜWàÇ Ã× â;à³8¸<á”%î5$o™F"И´XÌ Š…™hŒy#‰pCËbÁË•‰†f#Ó…ñN&>BOOÀ96"O !È‚‘ƒÀLÐÊ¿ƒ œ¿¢,ñ[ž%Í-€ù%ˆ?ؾ¶ÊËö9Â`"ææIÐ^[Ùò~Ó!.~ªY£Xh%†‰/2@IDATG N\¹smJž’B²æ›•¿%F‘ ŽÀóCΠ¿îg$è˜@O{­ºþrúüª‹éŠ®îýPÿܳ+ͽìB^ïC‰Àq\Ý­3}våŤVjpÌ'÷êGënF§µMñÄoOÞw.B+¯»œV‚ÇFôìftÄ8®íÞ‚º½¾e;ýÀ…W_J½°Öñ¶c…ÞÉê¼®úBaÔ®³8A" ðƒ@-ºŸ\[ࣾ09o†PäÀc¾xv²¦Ë5›hÃ=+Í‚¼¿j,ƒ…%òdzC`n=V@®J¤}Å%ôÐÊŸ©ÜY}škÆ6xï-.Iq]Bc¸E”‹Õ¥Ž•;¨xR‰€D ù"Т5àP½+Ãhüv¹DÀßî?3`?:B÷Œv) ìªtÆÚ…†“ÇöºÆÅÐö‚úi(¡¢Yæyô€5ƒ-&u…8XX8”:0w½2”ºœÄ‚YæÅ®ñ1â§~5â'ÆnV_tØL^«8ÕÆÏ­ßlxÜ9æìkðįhwe‡.ÈÌ“2H$Í9à»›±i+½zá9”…9œ<ó9Ly SBŽ”UT˵â[{Ÿ@O¯ÝTí~¤þýb?²š?ßp[Zw¬Vä™›2`vÖ}È>Ó¬š3œšsÞÆ‘iýk«ÃÁ=[VŸôÖÖWðüEgàY“±ïMZJ’¥ÿŠkïðHŠþ³g~e6™&¬»eôO"®8_¹`uס½Ïb °¡oKë-îó¹ÿÙÓõ²ük0ôY‚<6Ä PîüéŒ4Ç€7³Ò üïÉ?ê8†)VEQŸÙpÛ˜ÿy§”ëÏvåÐLO»¾îÜã!kœöÕ˜Å^Σ–®4¦¼•b¸ÃóbêŽ~½hñ¾\ú ]‰€D ù" 5àßÝ:Œï.سn€Y{B×¾|ÿò.i{a!âæðψÇܳYW”£ƒÞšu®7±hðoB_¢NA6ðÍWO&]9„™5óNÏ×P‰KŽuL©yß×ï³ßz+aס=ËHÑ«$SeÄþoÏÄÒbúí»ö¼idÚ`ZGÙ&ÅU…vª¶”s7Ž{¾%:úÌü~âôÕYUî¾ k¢{N]£§Öl¢ëÁCš Ø×À[ø¦Ø¢hHǶèàmn¢ZÈb%P! p=´¯^‡UŒ¶ùMñéî}”öÍ~ãDÚCXDÿ§éúM‚®“Þ} ½ ½¯µ?Õ½h‡æÍùH‰¢ïdyðYQ•ça˜>àWNõ¾ïëzÕßþV€äF›õhÖ¤«—rí»K/¾ØÉ4͆À¿‘¯×ÿ-í« #FîÁkFŒÜaårlŒNæg‘¾Î9@·.ù®šP­Üé»jÁR˜£ëžÎTŸ|e\‰€D ñhÑX,ï>7>¸Á–èM°yùK äæ@ ýÙ®ë?hÇhµ±‰×_:Ö45E¹Æ–¬¢+껊KY-¾¦à±ú‘s¦È»Úó?Öý-mf.?N9QXh[Ù]ݼ š®ÛQu“½“Õ’­pím·ôºòKñ^Bžq#d(hç³ ‰@Ó"Ðb°¯&U2U®BÔ´°û/Ý„U¶,P½ƒ¯úx?æzíw²Z¿.¨Ìç&Ť×i~.ÙDÃÐŒÿÓð´³)æC@;âÆ9s<“WuE7o¸uÌ'ž¹ïÏž5¶¡4"ß]Ñ<*_Œâ*ÆN°IÞùñø±ËAYª!𽟄öÚû=X°øJsà'F ±y*´¨ËÜ$-ê­|ˬ£§V64˜VGõeÿ<#芥 mQ9ÖX”½çT´§¼ûzØm`TÊÒº VtývĹ[]®¢¸î¸¡âèe5ÓéfÓ}pÈš4pÎë©ØõŠ~NÖ/ç Ý’M7ß»œ]y{^¹û³ ·^â}?œ×±µ:¼GhÃYZpy7OG´L-há´xÌ‹8lf•Ìpžâ#Rƒ /;ẃVŒ±¾³ó•Ž ÇU)¼?b„Ë_¹§Îy­4ÛÓUø™IÆâ?Œõ8o¯™nÓ-wîRUýy­´üi”Q^óy¿ueü³OñœNgOh×g-x?? šË6Ý–öo'Üg~7 1Vð’+¢ù‰qh*ž ÷;ùKš;-Z Œ„Tj‡UˆÐR'ùÚlÒwjÐ;¥ÄêªÎC¸‰Â4¤Ý(#—§)ê4?W”9naA½þ/iÛ×Þ6z'fŠ~Õ—œòÑëÕÌÃL»­¿ò4dÖÉxu:d_WÓ(ë¯<ØýLû?UWçñõÀÙYãQf—›n“v|ºðÜ|Õ=5%âù‰h*ž ú2W‰@ËA Å `ÑH²¯‰ ã©*ÖQNŠµÙ¤PûÂÈ]Âi‹² ³cÐÍô‹º„íå=”—²þÖ1lN® ŠÞ Zæ~qôŸõ œ nWtÓìªHDp~ÂØ¬þiEaù_¼ïó5;té&AzœóT͸5oyç/ k®Vvd-Ê^‹ÍìÕníº¼:pÎ+)ðÞž†NÕ¥sfgíôanðc5óÕoñ.ø½tlOV«9¢ù‰ëݤ<*àe>ˆ@‹^ˆƒµ^ÞÀ€3ÆÍØe¦c¬‰œ”Pâ ‚˜Èš.Ê4%WÐ  zí\Æ›nóÊyN¹­J™\{ë[p/` 6Þ6vTm´nºeì7xÑU{Xÿ×Ñð´wÍoM› ¯ë'i?YØékƒ;›³ýæW3ŸPüöæ«“º¶¥òm¹ÉO\צæ©Pà-ó´T§eoô„¦â¾fŠŠŠÂa¡.É6ÂP0x°ˆÔ fZ˜¦(³B}:&ôr‡éui#ªHÖ¢Yø6%Qâ]¾ܳ£á0iüÄIžjJN‘eKêF Å `®º Ï",{h^+ÖεBGÛ¬Ô=A¥˜ 'õÉœ¥ü˜¦i@jœA#Ó˦Ÿë!Cä àÍW1pÄ:«W‡ˆã'FKòTäðŒ¤D"à HÀ"$Ý¥kÅeŽàçwS! 2›ÕJ6,bÏûê¶Oˆ¦ÔÚ•S¿œ‚&Õ„YKa˜–ÉQÔ¹m‚A'ÓËt³¦ tY…C‡÷0ïh`ìuöÅ-é^HyŠ©ÉW=:¦Ð€Î‰ÁO}ÄS-‰IRþ¦\š‹ç§{S ÉJ¦i¥DÒ03±4‡ëàÑcÅü;(ÕÍ…,À,‹!xc±µ[96R(//§.NéäßÉ{ŽÐövñ>&ÌãslºdÍ·k¢‰z¥&Phd:¹£Àt t%4 >-(Ö+ÊËúÈÀƒ»gÍý–§n¡â)Ä_ êÑžÐÁ¡­Š›ŒŸ˜¶Æä).¯5þ¦Gj`àá¹÷åO‰ÀqD’öç(/Ï),-WJ ,c`2nhp7”*¼Š¡#Ÿ¸ØXß ªÀfçNìñÛó£ Ê(§T§A{ÒÑØ(:o£ü8 9ÃäøÄs2yZ{¦²ÃG÷HV© 4ßxì‹}^™N¦—éfn®G0qd<ËJŠó‚ɧ9§ O1µñÕÀîíȆ1üM¹ØÝ¨‘ø‰éi žâr[sßTEi ; Ê h‘(€õÜ=»V$´isÿºmûèì“z6¨b"› Ù™‰ÇY³t@øº°O¯V¹?*£ƒ%.RKtC(²Þ͸Ó\A ?A/YÈ«pqcÉ+,LAí¢‰º$Y)B7ûÄ&$à`! :™^¦;ægÆ‘÷ëݽuóZÐÃ=tqòZú9¤<Å`ÕÆW'àý&Ù,´#¯€LEaã'¦¡)yŠËoÍA|S{¶oãÀ[Û÷Ôš_}HëiØ`äeŸ}´¡÷ɧþqÃŽà Ô?¶†9£†ðÅÉîF&j³ó9Ë(Õá„E‡C¡ ¬p+5WPÅ{^oµk3U”Y§D«‰â£Í—¶AØFš/ ߤÄDãšÍÏìµ í— Žºær¬_½r;~¢ài0*‘àX-6„œ§©ºø* K‰v-.¥ü¢2Ê/uR9ÌÓ`/tüB‡sSòTèjÑðvBÂWMÍSþÎÃNžø¦öý¾c> kMßSرmmD’fìY?0zÙ¼¹ïÿyܽ7½ûÅÊäñ£†«Q–à6'àÆ’nÀÜ¿YøbzÆ[Ë €Ë*Jv¦q¹XcaÆ „¯Q˜Wy¬Ñ²Y™é^Ùlnæ1ߘ˜hã`Ó3?gú‚ ?Ms: W,˜¿ùA3Ñh„P –Ú°¥O1ÅMÅW‚‡›§Âö–šIÆâ›Ò]®cËçÏûdó7Õš¾§fò¦š™‘$€…¶ÂÌì¼5àÖ$€›Œ§€yÈùª&ÕüÍeÊP|}S;7oúßÔ²ÒÒb^<>lß·¡®bgÒ•]õ§\¦hŽ-€1­æK§æÌÔJ´aà­€ÀfQ³ãñîi²ù™] U8Ñ’æ|ŠëŽrB÷Þ}ûGÇÅ'[££ÌfK×ÌB†Í18Xß©¤´°¤¨àè®­[¶nÛ°fê!Ì­¬ércq¬òàÆƒñd\ßÖ`~F5 yJ !Ï~ßTiQaþ®ß6oùmýšÝHÀ·°O܆Âð¦šÑ¦ú%R>l1„Dk>e ¬ë³3Ò¯ 2<‡¯ GŽI8qÄãˆÅÁÏ8w"8>!©òiNµX0BȰy™µ\¶¬ù²æ5 EïŸqœÖ$€Q]ƒ?$O12øC É¾§1éŸcàë¤Y“»ú#P>k9­PèÊs`œ'Ó&e\˜•™þMà„µ5!T½ÍÓ| 9æi†Œß,„9ˆ4î_-ó¿0 á+ÆÎÑ[g ˜/ âÖªý¢êF<%g_4é÷Äm'Vê»M×þˆ“÷Z&!À-_Ìqî½>L/Œ±g›mɦéÜ`²¦ÆAôJù7 ÖâX{kÁ†™÷ZƒðE5 paáËfe¡ý²fËXŒ3n­QóEµ=Aò” yá&ùžÐfÆh޼à[·»“¹ó‹>è’·Z(!Àvû¨²1éSF£äÈû/‚n‚SˆèQ6:ñ1°Ðð¾fAÂB……¯á …3×£¦nÉ‚X`+p˜ÇªD……0wTø7ßã¾"-nµº ð’<Õê^½ß ‹oBðG£}Oì<™–žù_P7ÇUÜ–â,C+A ¤BjÌ䌿ëšþ ƃß'K»ÛC¤ ó«`Ó2 X67óåu—Ÿs}Ä˸±7Þ º|惟±ö'C’§ª°WUßSò=±æk(,¤ß¨¨ÊÙSÒŸ•/¢u!RÌй…0=x=æß¢1aΚi& [oÁ+„okön4„  ^~.ÃñHž:“ÖzG|#â{ßOX¾'cÌ—è€=PQéRø¶N¶ ¹fa޾ §Yp©ïSô8ÞVcÔÏB0O˜³gšEÃÉ—qOÔGœñ¨ÅÑ`ðYÜxˆCÜk±„°b‚?µVž !¤Í.«°O<Ï×=ÕH¿¦ç+xÌ(ÎΘüU³CKÂ&¨ìö×mûûîÁ"÷CåDèy.OÑNûqñs's—7ìöQeá¤c̤ŒQö?fe¦Ÿí¯]Wf”–è[瀿x­ñYQìj«ë(e+eÚrÔÿXkÄ@ÖY" h9¨-§*u×DUÌ ¤ÓUª¢~¬¨´@W(ïñǾÇØ³bêΡá1LŠe-Ê|³á9DFJtbì£'eœÔH*$‰@óE UiÀâ5™LIï¼l¿»¿gÞ•þD‡^±Qq¼¿³DœPŸgd<ÂZ-Í=Ü`Rôoš{%$ý‰€D ©ˆ¼ùš¾ñÅZþ5ºNCI¡Á8w‚‘8 ×lfÜ¡}oR”ONŸûc¨{9ã‘MÐê~A¾½9ï´ô©7h¤_¤(ÊÛ¤kO餟a¡¨Ó8Þ]öÌSœý)D;MWtŒÑ*_Æšcÿñ¬ý£01/Pý£™“gq>xÌrŸóÛ•&2Ùa~ŽÒI‘~¥û)ÑØG3/×4m~÷Æó-ªªNÐ5­œ š‹`”éÔžÅïS¡­]/gM™ø,ÎHB4:=ãR•¹Š®÷%î…IýXvÆäÓDzqvÇ£ÏM¢“rîk éóT³ùù™ö ¿‹xi“3Æêî|ºáÞ:E1=„ò¾¿×þ|B™óؼ›A)³¡—ª*½£éÔ8lŸ•1‰ëAsæÌ1-Z³õб t<(ò>KE}9+câF^Ž‚'ð~¯D-’PÝ_T3=œeO_Íñxúéè¢Ã%¿*ñæ³ô"çKDú0 <‰Gωüø¬´ôÌ™¸4=¹Ï˜#Fð°‚ @à‰W­}5—ãMׇàgpT ÎŒÏA¼¯ 8M–˜'þ_Q®[l¸{RFwTúü™™éo·ØJQ±»íSûThÚ YÿJŸD62ihRô/×õHZ=¬ý“EZ~ò· QnGÏÂ#Ík ZØp\І6Ý©i?ü8¬ý/?ëpMëê3Ù=ö':Aà„‡Û9äZÓUºîz BîM“ÙÜ¿½ù¬ßÆÚ§õ€ðý6›Ë7ñz•8‹¾`A +Êr—®Üå]HŽöÝåhøzkæ6>z„ywñ|¬=ó$—Kÿ ùÌ 8K•LÓ!|3‘U´ˆsפŒ®N§¶™/RmÊ2Ñx”õÈØG§Žq€SGrév˜Ö¯DàÄ^õÌûJïáû_Ð_®šÕ«³ògÔöL—Õ-bÂW§‰”E™£ú¢þ ÑAXx¯ý‰.Ï?voa¬9îjE¡]QĞ“3]!edùŸDKÖn¹×ð.Gr„ïs=P÷‹Íºft Êó! )ªr7YÔs „×jN}97ßär½^ä@<:`¦¨sLq¦üLðE>I –¿KáëFfê«æs§f+‹ÎŠÍ¾SqwîàµÁÑÇIx7á˜IŽâ}™Ùêëÿy-º«Àµ¾gt¬FŽNŸò¸þÇ3cÓ§\Rß|LzÆÿÐ{Ä_Z‡¢žª)F§Ù_´Vû mÐyh«/Ü~¡>/Í>u@}ÓÊøU4™üÓ5¯p9KßàFÂ#ÐpŠ‹ôW_Ýa^t´åŽïï=hBïx:åŸ4Öž¡¹4êZîtÜ!¹ÝjNôôѸŸ¨˜Ô«³þ5é3‘ ËC s4½{*ïmº'}Ê–rvŒ<íJˆÍÿê%4eœ=sð û¤µF—>BîÙö´’±“3EVÆYs謭nöÒ˜?=yÚ.Ò? ÇIô òúìÌôŒÊÄsǤg^¬ëÚXüöNäÓÇÚ>1õ…ûî+¯ŒWËIù6{JúñpôäÌ—Ò²Äoh¾éÐϰ–Ê÷€ƒ奕9·B³†{nR\E¬õs|ˆ_jNú÷ö§Sž±?xD#õϤhÿEþ¶_ûء*T4ÚüRfú®q“§^àÔ\š-êÉœˆîG>¤Â¡1m£pˆ°sÖ”ô‡Ä<ùd,_›œ&}Ìä©Ï¥ÓÐyúïG)qZëùù½¬E{wqŒkVIެR?t º¢Óöø¥J¾TÓ•qxŸÏÎʘ<þø¡»ƒráË¡ì]ŽŸSš=ã rê7deLžØø¥7¬Ä1éS¯Óœ®è|'êí©†å"S1Ð4?ütuêݺæú lû†”Žtו”T|¿zx'6“Ö;80:èu…1_Q¼ùœì÷x2RèH'õ¼/=¿ÝY#ô¾÷bÆäH¿ qpö¤IûÑê-pB—hÜ´iɈ{j¡W½Óx®½¯BêRÏo\Ä%[×{ÿÆõ hÎÉ0‘,Ô}îŸX=ž2·ná Jª–?> h¬8/ ÐŠ:C›.Ê‚F<å£ZzòªJŸiŸ´„ýEZÉÅv»®¢ADZéK佚ù FL]ÇЂþ9_»t×I ãàËM\W•‹Áˆ_¡ ÁÞ÷LªúŽ÷oqíÒ÷ œ»T‹éÆìñã[½7ôóo¥$îÙþxãÞ@…¯À²òΞ–­òPG‚²/kʤÙè(NÇËÁh™à‰Þ=uj›dp’¬ÌôwgN™¸"à‘ÑAW ÃÂmE³è´ÿ_q)¦ëðMcN†`ht ø§á©·Ã¬ùb0DW¦ímq„ðygÌÏ9TŸü,æäÁ•NXµ%;`·_ ´*@[pB[¬¨ºSy¥èåQŽŠ‰^Õ]”Óë#ûKVÜ„¿Í´§C£õ „]‰÷“s»t©Xtd † +ï*dÅÕ:EUyœÓ¬ÚÀdäªüiœT…öyÿös]­<ïxåäŒâß:©alw£×³-º)Çë÷ñ— }¥hÊ¥¹ú´\|”JÓ„ûµ©6 &oå{sœ—"Ñ­œ=>'ˆ¯@gFÔÒÈ嘠WãG“nÚk<¬ùO¡~¸µæó§qv ùšqZÉï9sFm;ºñcT÷Â`« “õýS³ÔÒ‰i†%"ˆì4î8ýËU¡qÇípm~ þ|î²Oëåp¸>·Y,¿`ÄÃlAqiÚËdiw¶îÌ{¹µ°ê<ÏÄþk·<á+…0”Dß(äú¤£!^m~œ‡?~.‚ÇOÁ¢^KNí|8gCûÇØº2oèàÞÏŠ!‘>ñD|~¡ã|YW‚. ØþKKŒéï/MœxxÌäŒLX½rX–†âœ¯«ÊP þŽŽÌ¢,¬ 0Bz˜Ò­ÝÙii¾ËÔÃè„öC§çþ ³ÿh ±uîDÔ{'ºÂ3`=z™Ÿq¨í¸ŸVý›‘ÑY+ÓáSKXfú{UOÜWQÑÊBG —=eâvtÔ6£fù;pUþùêÎ'ëÆžBÐÛïMºëeç7|?àú£w$÷ø1 ÔL´‰ïwT‡|†xz®öÌpúß0.üŠwü×Ûa^þƒ÷½Ek·ô@<ï ç;¦ð…ÞÌÖK¼Ó…âúeû?!<é(èoï]_¿4e<;íÏa¹§j‚RU”/Ð\Œ~Áµ±ÙíŠÖQ=ïk|ÄѹŽÌ›[<æZà ŽÔeŒ}* QO€ƒ4'}³ç†¿ ³zoL´ò44ç áú‡¿¨-ýÙö£›¦ƒg. U=Ñ ?5 ÚMpá2N®‘m‡;ß~ þ|^¶OØ^+äáoR\ºvød=뀛:‚}@¼óàkŸ‚Óõ!è˜G1Ê@|ºO ñø¢u¿ñ!|çtšÕá‹r¾ùŽíüÜlJž†ºÀùI™Ë>ª¨æ‘ÇùãìÓzr¶*Aøþ×SÝwè¾çÚŸ*Û‚ ¿Âw ˆ –~hA²ð=N2„´ˆ€¯Èý™™ |ƒÆ¾„/gÅ8Wn¯ÌVõd//„@µ†´A9˜L¥Àôœ…œµºä{ÕÃ;Ü|æüï†,S_™Mo‘C»ktzæ„8stV1•µ¯p:ìèÿFÛ}ÂIXkÆØ×šK¿Âìô¨Éw€þ÷ >¾ÅèEŽƒpz}¿ë@_]sNAl|Wî`!Ë3NÝq;òœв”ö‰9åyE§˜àQ=cÊÄå"^¨Î˜O øûÑãÞf2§¼ï¤R›®•ŸåÒ7òø­QŽ®p¹ÃÑ0,bAË÷¢MÑ‹Šõâ·qyJÆø´œd>…:?Rw©XèäÒA=×ÃKúk`ùï1“§O²šbv•; ï@ƒqÌõçs~„ç&M:0zòÔ›MûZѪpàMgÚ,Ëéšæ¼?ä4(ÚK/Íi·èîyEäo:ž}È¥$:IÉ›ñl¶ýU–©~ ú¼ «Ó½ aÓÁ>ÇŽ–ÃÇ€|w Á“¢>7kÊÃÙIž„Àm6âoœžƒ??<6ÊAüã|@ŒÄ>þA=‰ñÛ×+}‚ïùKtˆÎÁïWÐ9<Žša¦Á‰b¦AÚ¤Œ tΗ!Þ‰°ÂmG¶ª• Ÿ \Àßd³ît^Šëû\ÓX>†¶âcÍeX{¾ü»ý™¤bGñ&‹r'ž¹ë¤Ò¬YS&=Ç¿^„sZ,:µé° <-´qäQ«¯”k‹€íX,è_îläÿp#Ðh=˜Ÿ‡§^õìpTJçãºÝÖºdÛ'²vv ʺ¡ØY¼†TŒaê©ð’¾B˜…¸nQfÓk¨'O5ú„{‹|ÏW@óÜ¿ÇôçÞþ_£÷ý.¶ÉSŸ“r%î]W®+ÛËÂSz± ÂÊWžÁÞÃTž'¡5¼ˆ¾Ò3ç‘ú£4n¦K5¥ˆ¼AäCaV>€†Âpàbç+Üû é’.Üw™ˆ‹¼>Dcoj÷ø/ßçÆÀjIàñ#ê¼¢ÜQ‹†âA8ñü¥vs}UŽÞW³¦L\+Ãã0I¾Ç½wïg­áÂ÷qÔ3ä|wÖéXþájýþðDÇí4—SÿïáY|—ª&º;+cÒƒÕÓT÷SÄ Æ÷6z£ÝÇÙ3X Q„/¾…<˜œWÏ›èÞ©SÛá»K?,­þLñøìçàÛ¤z¶•¿0›Àó½VÞÚ‰Îu_ƒïá›(ÕœÎç p ?xd?ÂÏ0å°Ÿ}¤ûÒ…oŒŸáûC‡Cù/y°¸–5âgéEø~ö³ÿÅûSmÑ÷ÕEÞyá{‚O%|½q{÷ªûÕßAÕ}Ò •ÏCžÅÐnÙŠ C#!Ðh04¡q᪭÷êŸ_æc5'©šåÍœ2áÜÃ7\{˜9eÒñ”ãÆ[¾ÅÍ3¹ª$èåÏ<ø`iÍH/Ù'nŽãÊð•/˜}zÛ3ï~üÉ/=öðÊqQÑ›6²† ˜›~c¦OOŒ*SbÛÒY½Ç§ái:¬& ¾~ûЇqœUˆë¡µ²§œ‰{™l^×£\å5;l’Æóþüœie¡.F#9KÜÀ9;c"ÌoUù‹G•o“•e¡ý…‰³¼5%Dzêᇋ}¥óu¿rþ±¡¹ˆü[ÃyzVt7M)Ãt¹pÕVOCÎÿ(w…0ç»j~;§™ù¯êíxM?”:}X+ÄØå\§ƒF"Ë•p¼ Î}¯Õôàòô “…§9«&yÇÀÜe¢zø9çâɰƅÉe®V:Ÿ•‘Å ¸>Ÿ—ÊíYMµr[T[ÀŽ2›…í>gÆ ÐêÿzÉ ¾ßÂrdÎuM;µŠNÏœ8sŠ!9g5ÿݤ—ÃÞY•_EÍw G'a0ü>þ^3fræ_³§Lâ1|FÀkní[qDÿc8냕Ù,åW‡ª|/sQÐYV~®¹ƒõ›W¥·ï1¿‘BøðEû#9þ²«ë¹¿´âY¥å ÊL)Ès¸”òk1Xáé<Õ™ ž CzNÕròø;kê™4 è†/€æ0|*­KF:·/±µÉ˜fô*¬>`Êß¿+H¿Ð¬Ó핪^xìŸûav-Õ†_ÅjñÐ¥NIÆOösÀpN¥ŸCº0׊¨!?£s½鶴Y¥¼—í“~õY¼Äp‡ª“»¬èHqü~gÆHbSgó„å#F(†t>q‘vü¥º S¸-‚v½ ޭܾ~/rÁ¼ú?¢³RîèØf§¸WÛ=†õYS&?<úÑŒ•˜¦ðæöþšeŸ¸±¶øò~èh\‘¯œ‰^[XËBƒq^è`‘9I"HÞó„¢.J1ד¿©°à@}²Ÿ¸‚õ(¦¼²èåÌô=¾êËY ‰¼ ~ǘG3¦Îm¿Uöå]O|Öä=! ?Oìà.:›Ó—ä82W;œúà¥<%ÕœþÍ~ç“'*ŠãTXÄæpî¦ËÑšÌ.1¥Ž-k^v<¢NWÅÌüÿA¢žJŒ¥Êä¬(Ï#Ÿ‡àÁ½^ïÚf>í;tiÚ8äû¼÷ðçá/ðjXðÿ¸s{çÞeéÌ:fŠøËJ> ù*fÞ¾î‡ò>ΰ—Jze^àÿ7…¼Wp4Öž:P_ÃBï^h’WaÞü«µçH„Ñ&¢c²Âç+Úsð(&ëe˜TzÀ;M ~Þñƒ¹6œc”k@{!<™¿‚9¹TÓ+¶À‡Ã3‡Þ>éw9vC;÷t.Ð0ÃĬ÷QMÊ‚†TsÇEègt‚Ø^)„5?Øø‹0}gëäz†vçCýY¸/Àâ<“EÚ@Ïz×öCÀs:ó_ 4Œ×pÂfÂò& ÞÏkºØx’wÂz^Ÿqæ‹b_ê¬g2]"ÐìÈÌR¶èÃJ8ߤ1˜öæ |ªyMQ¦ÝþºíåÇT:ÖšSm~µ&⻎ùbM‡+}ªåÆ ÷Ä«ª3ØUÝØ1Ù11OÌP¨Vˆüq4ŠÆ4¡HÓŸgíQýôO`Ïcd´p €yœ´ox«©dOJÓª™pÃ[žÌ]"кh4 Í 7¬0ÁðNARø†h™D €ùçaÿ¦PÑQYI„D …"Ð(ѯoü£ŒF¨†,B"ؤ#€XÁEQ¡Œà(”©%ÍFÀ§ÏË]'©¼°"¥`Óϯ[å˜DF“Dª²$ÌiQ¶è¥a.Cf/hÕ4Š®4 럆mÿULQç©ãsœ+x‹f°ubXV¾j•—DúDÀ–Øñsðý1ŸCp&î%ßVt0YÉ,$ZhÌe+&Ëó˜ÖÀ“ÎCåã3æíÙhÆ(¾ËÖ)bÁó@Ó5E¼±ö'Ûc¿Ý̦([–¹<8b/VaSf„‹BE5ý'\yË|%7â-À^=¬Ãó|¼¸zÈ´ë ‹j|ò'9[ÉÔX ]ѱ˜ŠEÓµNXBÑ.Òœ9 ¥cÚ…ÞòÙŠC™•{‡è½;MƒàÆÂJÌësÀßk#Ò^‹Õibžó4Ìó{ óçVãÞ­Xpý&L¢Ï¸±ˆ7çÒÁ}ây•Û¥ùÌ'mræmØ)jòˆ=ßšÍÖ‡LQNWHG=±\ÜÜŸË ¥sÇÁåt¾†{@ã†(¬äÙ,ATFž[<Ó³’5:º|Ù>”•…ö»pâÝØÑ(”ùʼ$ê4šÌÅZRh¦g™¹ê¤4ìÖ‡¨ð5JPèv…L•¹`7c¹8£b·Ï‰Âª9Ó’â-îdNgÁ¶Ûì=—mŸôóâµ[x™·®˜ð~Él= Ú˜Í)·ùét&„ïƒÕÌéFòŠ6h§c§’ëÉbŒì/\»åbŽë/,`ÿ«ÙbÞÉ|þ‰X­ç¨ÓUñ—&NÌæôwaÅÁ xO»”8ÎtEÝÙÒ§ :_9ˆ&´È­ ñiùǰªÿ(L°\ˆ6"°šmaŸûËÔ6¦/¯ß"„ˆ÷æÎ´¯ü¸cÏâNôqÛ†ãnú¸á/Ñë}+P:x¿f~GàNÏxÝûüóÖ@òjMqºçÞbM1æØvD›ÊÔw¡ ¯àŽD%}ÞÑåu G`âhׂÌ,õ!Xm‚žg¾-UuÓµý_)x·î€ïg½ª˜FgM™ø½ˆÍº»Êôõ¶ö‰©åý¨šÕ¿ò®=â¹÷™}1Ðѽ÷þà}ß×Z£Ó™‘~§¸otl‹œ¿ÌÊHï)îyŸÙzåÒôW°çî…Þë.§=šy)Ö’žQâ,νØn‚•Ê®bPv €¿±¡èÜÖ¹’Þ¸ÉÓNÇ.NO.‰Zxx« –®ï°«ÙÝ‚ä?}ŸsË5ø]‚ë J×öwfã S±1Âuz‘ƒiˆ ³@C–Hç}ö•‡¯å$ýÑ~—}Z/‡Ãõ>fŸ,€Ò0‰óçö!P:xg©ò×ûåÎc¶ÊH‚u.#kʤ·jÒ‰ö&×D¦åþ0i „|«"š~cKEq»ÕŸêÝ„¥ÁŸØ¡š¢.@¯ý·`òÅ#ÿ9íÓ\Ï’näUœ_z=´Ë6`?ƒÑ7âC8 ˶ÝÎiµ.)ÜHtÄš­£Ç¦O½š?k´ú?ÓUu!bŒýhæ•Øø~h; ìù!BªÚûk÷žhÊ¥¸¾† ‹øÀölƒŽÍÏýåƒF' æìϱ”Ü—êØ×T·ˆ<½Ï9Zι »s‰£x!ç¯ñ‡¦+9iÿdz1¹w|yÝòÀbÏ`–Á]¨i0«À"“éãӜߊ˜ª+ÿ#Ýu“w|­œn-¿pß}ÜØú èÍÖî‹¡ÓXÛù|¿T>Dg9+íÝŒ6ųõ ?b­ V-ì)l†Ýš† £ a¤Ìäg¬Ý¡ƒ° ßR@ Nƒ=‡J³:ÂúÜìŒI§i @{q?ƒ0¾õ¾ ³ùüÁØyì Äv({âN±ÉDw² „ûšcÍ„ zÌ—Z[œ¿wðG;öÈþ,d¢^ÕÞc}è(/Ö…Âñ: çPœålo;û¢ÜVCe„ޝþ0ñ±¾ô(\‰ßò\…@£ `.úôOön‹S“OS¼Fa­2à€lzvן1?÷ÌT'DDŒ±Þ®ªÊí°ƒ ãâX.BWóFßF/SQþ£‘ògMÑzEY,—² ˜óï¤bßYRcûùa¤ê{;™;³Fë vûˆ ñùãø[ž)ôÚž|mùpìñ)17 Í%&½vç]W‹ðb¿Õ³DþƹƖ~‚ynLLÓf˜U3wla‰©_@šOÍæè“'ÞédËTàA‰zŠÕŸÝ ²;,77Á"^猄J_Œª¢¾ Îm5 ÅÌ?po†aªù°Æo ™ßÉš’~nWë€,^· ³ôÝYÿ4:û3ÿ5éK´6y_`cKLÅ2ZµX_©‘]­?yn±K·=Àíh '@§{\Ó»b«PU§ÙÀåF~6Þ¾RÜWÊl5£2óqlyp>"ø£Ýd²lRͶ¡Ø¹a‹ˆ/Îõ¡#:šfs:cÍi>×]׉|Ʀg^·±âޝý‰.h¤GBa2:>"yv#Ð$˜‹î÷É–Â3>=0Z1Ñ)`äÙè=û{)x¾‰TŒµ&+}Ϙpž¿¸¾žÝ5)£+>>©¦‰¼˜1y'/M¿̱¤° âOF]¿‹ÌêýÔ¥ÝKÞÛí ©'¡Q±šÍIãÿxRŸyvû¨2_eð=h¼Ÿ!Ïëïž”ÑóŠèíÖ–Ks Coæ]P¸ÁÁG} §5‚UûçÜ? úšRסÚØÉÓÜ4ã7(â¹<·^íXynï?œMo$:¶«ü!çp$¤OTÕt1®†?rG‰ßí'}åe6E?x×£Ó MuŒ}z7È¥^Íç/ò¿Ú½Z|1Dœ(›Êûÿ®E¨Úf â¹÷™¿cˆ<³Áû>:Ò¼qÁnï{ø@÷–Qi¾—=eü:«¥¢Zšjqýü0:º~¹™¢–¸£)Ð9ö”e&Ú±ÜUdÁŽ“if w:òÿ‹_õ½îµÿiÚpú'yìsäÎÛ{¤:Tv¾£Á0ÿv ŽÒ¥‹;o‡g婟î5z± ¥ÖIt+¼•ß©¹H¹J¦7`¾z s¼³Ï±b³âЖО¼˜ƒ œø6zÖ)=B¯7vùuáÚ|3Lcyè­N€I鸆ffÆä¥cÒ3_t¾fåçê¦8õG@÷«µåÓQíóù>më}Ð ¾Ç..mPî `rà ufzú>˜Ë—#¯о°ðŸŠxÁ¶k¯ã< Š­ØYü3ò¯¦•7'™®y#pñÅKÁê†ö2ûé7¢;—–W Aç±î%CB¹À[á;±!&&qÅ};RPCi¬wå±CÑ{ØÝ‡ÍÐËu‡s:Ê m¯¶Ìüùbˆ4® ³)&ÚñPI©ö3:³ï9ˆŽŠgžUMO@Ã_­cKV1PH 4Úâa‹Dì¤~Ë G§Õ¤{ÊrXlź£,I¤w9\à›¾ß«CUÕ,q¿Ú¹Ž<ªÅmàºè¸ûñ—cÑÎT>@[W ñÙ‹d±ÇÖaVKÂÃ5I¨‰ LÏw@œ¯gGV6׌/5¹/á„ÿþÎZåÒÊCÜÙfÚ©¾2Ëʘ¸÷@À=…Í¿—ÂiÁ0µS†ÖlÝ:&3s¦³T[…ñâ3Òwri“§ž {öt\.‚6Æwù¾z½Á§Æ=þT;­cB~VZšƒ{‡i“3|æ63ç¥ÿ|â‰ø'þùÏ¢JÓú "?”1Ò÷éh-å{ƒú§¾¼ƒ ›´ä¾)yöFàÁ‘¥ûð{Ž÷½ªë#U—A\aLô=Ýéü–žûrœ#à•ÍM¿¡†/ú¶ÔŽR·#Ñ2…Æø®C§’å–|ï‡\«Ê˜Äã«GÕãu:ÔA„±f\‡¯k”®í.Ù@ß«ó·nþÚO‡ƒ <º+V•;& ì(år‘=&Zý“÷÷ËÂ.˜œO™Âª§¯6ÂpÞĘ»îÔ.qÅ9Ðce{çºÆÕÌϾ0qª {·÷'çÁP< È…¡9]ëàþ`ènii¥oÔ=¾ú=À _ê{0Ƭ(ÀŠ‹_û÷…Sëtº…éy9³ðaíKŽ·ŒI9³Y.ùR–Œ#h\ »ô©Pp>¨9ÄS“Ž€|1¼q~&Å<ßætèMøôYSƯAì¤1“§âT¹Zæµ0A¯çŽ«¿\ÒìS`:¡ÇÇBÄ…ßÅ©Ðá^T,æáÕÆHCY€Æ¿²‰Ö_ù?ÜÄÑë6žÖ#ò` \…ŽÀ¾7Ξqθô'úòumyԌǿëê¢mÛ<4Àù‚¶œ èGcª-p¿J5[ç³…ïx™ñ3µa’=eÒ+ð¢Žf€\‰èŸÀz—j$”ÿ "ÆÝÔï£Ò)âA_t̘2q9îóTU>A!KBŒ€jÓç¸Jé?ŽÇuJ5§ö5†wx\A)vêôš?_ DšmDõú7cÊ„Ÿ æÃD[-X1$Í94bJ’M1›FxeíóRwºþ !Ä‹iÌõŽ i®'!N §cò3qgCDgÌœ2áÐ87ǹe-žñ¬ˆÖv ¯æískKñ¬ iËŠ%Põ)ð1œ·\N¬~GìßòÚòà‚¼ãñïú†¼¨ý±µÑaŒÉ.y·8¿lò]eNœ^î,x¾'«ÉQ– çW;k̨ÃH<ÿÌ{Ö‡?LêK£Œ/H$-1!ªa ô ^t#¸5ãð𕯕°Ø‡ã.ûK¼xOµÀZe•Öì~ä+_ñªeàÚè¸{êÔ65³p—ØJX5ÓÊ߉€D@" ¨7ðÞ½·Þ‰d‰€D@" H$‰€D@" XOA¿0;kåÀ·³Oñ¯î»ºAô9±ó ì®×Žß°Êyu§nxŒ6toRÏrÆé9 Ï¥éS¦Ù3΀:µé)©›¾šS_ˆw½Á¬˜@k-ö­}5Çù­½îÔ2†D@" /M¢zûíä÷ggÏÅþÚ×»zº~6¹hÕÀ7³îÝpkZv½ÓsEù.;cÒ"-öï\‹ëá*î…ú\¹Qù£¡Î·ÑósÐ)É^n œiOÿa¬ýÉÞ3í¬L¾‹Ò‡r]Œõxøù¨Ö߬±wó!‚¹œÉ ¤ŒÞ‘üoô㸃õ¡ŽÀ»eTÜPŽMÀüú$¨O¡–ÅOBò7QÛ~vª±èUfª/4â»/¯‰,~<ÓØ<ôóÖ‚ÛŠ&«|]ë]Œþ>kOuótûM¦Þs$´ óÙöU¾ o¬Ût6¢þø †ñ¦ó‚§ìªÑ]Ú©2­qTù óôQi€ÇÁå$ôg¬›ãÆX°v!Ä·œ 1=™øºåâcnóQ°©þVˆ ]J ­ðg‘]·BL”·×yÁ¶²ò¡÷6äM¬h·Q[¡<½¨Í’¶ ñeä£ê³<è¿ÅM›(:Û²£çèqò2Ã¯Ï 7Mö‡Õ›ö|x¾1”ýŽÍëÕðf@åF¶UŽS+ô}žUV:ÂëÔ¯‚ۋ΃àžèá——sUß+ vóás ìNPóÒ×s…®,5櫽¾Psæo£­ð—ÔÈ5ñ‡²;}/8[úÅ¥Gë¦_µlþü]ªÈìÅ‹ƒ»w×ÝdÛæÉ¸­Àª¤YøõðöC&žÊ÷‡‡Cä p_^bL«BŸêqYjÌüY•)0«m‹-šiÜÝg‰1k§ÍÄ¥XÁþˆô5[í÷‡"¯›‚Â(Àç÷‡ýßµÅå'â§!p98óàž‡Ü{ãÍVÀƒûHô_…• ?p«´7ÌóÛW vfkå("ió€Oxty _ú:û¡ >=Y]ÄŒöšŸOo¥´åmŒ‰îÜ#NY,|;¾2†f¿Ç°8gÛKn/ü4>_G©¼IxµüA ™ŽGŸä„ ðJW”{½fã},‚ˆëØ‹[­†y!¾ÝB¸=¦jm˺‚û?êÇæ/1赃ÜrðÔwª•½±û–/¶¸â«ŠÜ5gN5<Ø1'Ú£±Ê‰h³·Û–Šñ,Ã7°1¿YTjøÖcô`k•]3Ü0¤„K4®­†Ý!—8¤‰Çëlǽ𧒉/¼øAhói¸õÅß5{Ë·|5ÅŸ„g{†3mR™QcÜr Ê!D€¤–@Ú<`iÉé†ÌIÉãH>^°¿Ý^°àóʃþ?·ÖMjÿŒÏ‡ÐZQÆê•HƧK&"x.‡c÷¾9«ªwÔÜ;%~S8¼¡¶Ö-5mv|ùØ5æsáy×Äî/ Àµá†;Î3á×r!îsË5æ,µEfüg8ÛÚóY :^íÚIs–‰ôOÑ^©›¯Úã¶ú¬•Àñbó²¸BËûkó×lµ‹²l[þ¢–ö5©zF GDÌËaÿž”µÁ=2Û<"ß(úU¹Qø¹Û¼±Qð@û%³fÕbÈöQ[²«kjÙ:xÆ—uë¨Xùµ•Gç ŽŒŸ¿„°(os‹*ïs#DºŸ.Ø%†ï•–Êþ ퟋ¡ìW[l‡3%Æžø|ÁùËÛ;lfãS+ñŒZñùß7·D×dW˜á+!á=óþŽªƒ÷Ž69ÆÛNv— û±Š=˜ïõÖÉ'òËʆ'ePu ŧÚÒ|žï ßd4)®">呇ީ·"G§ò!!V£`?e<âÄ_®ÇêÞ7™i/Ê,ðej9ßE¬=“ ’C…‡å>›ÆÅƒ³0,-‡H.ZüLêP1xõ{Ó÷Ò²Š Â…™vm¤Úž‰Ró±ï*{‡ëþ7¶˜áµ¦%ÿ«À ¢ûßÙj-ȹyjiЧ¾cîÔàaž%–4¯Ãüê¸Âeüàž["?V¢1–±,Xèô þÿ»xáä/Xг|޼ݪÙZökÕ²ú\^,˜3_Ì”GüÊ ºoÆÄ:U¶½UÙ¶B¯žÉ»*͵ì‡í Q6¶âÚ­§¾YŽZ†ÃÙm\çk Œâ£Ý<ë¶ó^ãÆJ÷žb"@ˆ@º `D0õÁ”–•T‡”¶‘——Íôä^ì,ª²­5³²Þï,|Zs…úÞÔ}¸eÁùãþ˜»=¾gnÆnzblyõº..‚ ÊLëËHMô[ØÆnML‰‰œo‡sø8xÁ{ð‰Ó+›­P­-ë7EeÃ'Sn¹ÎŠÕ§K\ãcЇ‹#XŽEd{°Híu´7Ôm+ŸÃⱯY•ù=„ú•®_!úÏÜ뼓Ž}Û- oøi”Å\wÃü¯Jo/ǽ6Z¾Ztë­{¸ÐÕ*x/>GRq“ M6ìú ÷EMë›øŸH´òú&…é†"fø_›ú€¯°ÒÖù¶4•ÙÆ{õÄ9ÚT4ˆ!OÛº§g¹1[m¹ßaZQQ_ѽ{U²G®ñ†6wšÛMÛ¡O¹é©Š•‡›QÇ»õcC·'›_žR\Ü»‡–ÁŽö¡³9v´T"ÐÒ"ÀCV”ÍÅq̳KÕƒfáŸÞ|ÅqªÚ#»D€"@:J -CÐøÜ¦Ã^Ò>ìæ»qgÚS|ð÷«Ð˜ön{x ü x†87œÌävŠb"@ˆ@H›ì>çú ^ª¿ÿ{6s¶gßiøª¶Õ6‹Øgùg–Ðô,XÉ.ÇÉÍÄÅí¯«uðü'ˆu³C%à…¾î5Œ‡”µ”·Ù¾ûm0p¶qŒ/Œ³~Ï­³×Ï:iy(p:10ù?š¨Êp)j¸.¦@HÎÄA§Ùœ‘?7¾~âõä@Ñï,Ëzm½—˜‡ãg Éž‡½ßv÷äŒÀ™¿wLõ…4–ûF÷ô>}8KËf—pi/tv­J0‚½«ÕqŽ/à\â3XwÏo1:`‹N(¦n;Å^ÃèÏÓµŒÇÛÃÃë*vUIúCID€.'vVO¼aBÁÍ\ð¸ìм-Dê“ -óÌõã ÖîAɇ©S“œàö*f‡÷{ÅMk1–æD)øáªiCÊm‚½¶9º9æa&äÇn¸g²ðd6[”{M³ÇÜ­!+ :—©ÊêÂ=ÇVyðØÜäC›±Ä~ÄÚQšæùLèY#q ú·78B&å¸ì¾9O«Tµ—3<ä—qÌѨ{ê+%Æ4G¸ÔQ’ñ¨ØZÙ[åÅÕßìl¶B¥93Hö’ŒÖ__ÆÉë${þðÙØÛùËcnE[A>ïfcI³áÖr£ðsû x޳÷õ¹ÓV¡OW5·¢:¿ ¨žUv ŧ»{ô±Mæ]‘7Ù_4nŠ>I•ëHÀôtxøß6ѳ|_h<Æ#󸼉öw™ûí0¬Â<úõªL¾qW?Ì_ ôLgKÐørûkO}?Œ¿Á°CÅàØ OK<ʃ¾ð·êáþH!Æ {Ïaµ7‰oâ“î‰èR¤œj"ÃЌ۳˂þ5ððö6Çå x‚ÊSjX5 A.„>ÑMRñ]sæTc˜ùoµÑê+󼟧®»õÎ Uí¬¹µO¡48ÿï«§¶X›Ö¡j>vcæA¹*#¶]ŒÒ¿`–ùò»jÝì|®$íÅvT\ç$¶óWA œµå ðb{áea#÷ä|^5Ó¸»O•Y] ÏñÇ-Vh³ÛæœK»õÉ^Tµ£æ±ê]uÃÐÌzÏ«ò ͯef]o|/l¨ÊJ Ýr°÷ÅþÚ“ hïÃÈ‹ÍQ·Ê£ ¨ D€&Ôªë–vÂjR7Ê ‡Hþc_ç8ÕJp5¼¾¯õ¦õMìƒòPÝ•ån^²rn^|œ¬\2{ñuèš"@ˆ@—P‹É üEèòŽPˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"ðoMà_ˆ›’ @­–IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-flowns1.graffle0000664000175000017500000001151713553660046027244 0ustar zuulzuul00000000000000‹í]mSÛȲþœü Ý|º·ˆyÉÉæì†]L’»)ªn {É‘e»•ÿ~{$¿È’ 6˜Ä†q*øE3£Ñhúyº{zZ¯þõí"ò.MÚ “ø—ØG/<·“NŸýòâÃѯëêÅ¿^?õ_Ûû[G¼õºQØË¼ƒovw¶¼ë›Ýnd66¶¶½ƒÝÖ‘mll¼Ý{á½8ϲîË««+?°¥üvra ö6Ò¤kÒìz[‡ ~'ë¼€Ó­Ot~í„íìõóg¯¾˜ë×›í,¼4»ÁµIwâŽùöjÃþ Ã83g&}^m ?«À¹ÃvA“‹–G•‚4 ì‡g¯zY —üzè'qx–&ý®¿Ÿ~KƒÓÓȈWƒ"¥ÒXhŸ è>U*òjcØtÑ…~–lvþÓïe£Sgißl ¿ Ú_ì)㜮{¶GÅ×>(fKô†ÇFÝøûo´æ¡ïkÞßœC‡$çB"Î$Sœ­yR_)-á'$&üû÷‰k±mEA¯Þn+‰Âq‡*Uv¶Çå‡NÊc_keב—^NqšL¾Œ–Ç·ÓàjÜ­qÇööËÝÁÒðÓøC1¼=ó9I.f˜,[A|ôöÓð,O“á8çÃ\¾Ëy$ê_Ä›Qx×ÚÇõöóÒ­nІ&FåSD¯)̱üð(|±“v;ÈLµ+a¶Žñ:F–/)yɸ÷¯Zïl#IZ­ÿ>È2ï¸éÁUÕJÛa¯×­vÕÎ 2 /¯}áýâ>Wø5ŒÌÑu·V99¨–Í'ØvÒî_˜8« èh Im$³g1¤A 'gZErJ¢Ã´¯¸fˆ‚BÖ<*¨/¤Ä‚bÂbLZáÒŽT¤§I|ÆòstM“ );2ß²z½?ß¶JGÉU½è¡é…™†ÒIœíħÉtÙ‚‰8ž“Ç‹'¥ƒ£ó!ŸQÅÑ+œM©€$–²©BÚ\FEå c/]`2ìwMÜ â^¥vŽDù@k䢦F"7qŽ*Â'¢$täšP®6Ö§aÝ4ÔUœ›tõaèò¼XhÛt^LÛÕQ®ˆ@¥°Z¾hò.4µ5òã4;ÅÇ0-ÂüO»{4HŽÛI; ì!†Xñ¥×?ïL¢çŸÂ ËN¢ãSt|‡ðÞ>ÒžÉ7œdÿüÅÚVŽ Ü?SÓ!œŸ¥ÆÄöÃIÔ7ðžP´øÙìï¨}Ü1§Yp" z~Ü ÒNþgø“ý’…g癉Oƒ6`8zþ<ïL ï¸}J<Т.ÃŽI½ØdWIúåØèaámžÁÉ΀6¾OáÉb¸>2Õ!eòÞ•¸uVD¥ÌG˜K&¥„c-5à¦d>–”dåP±þJR… \ µæUãl€{+(NA•w&º4è³ÂŠ¥«9q£&\±T¿Î­0mG ¨OªRÂD‰R\,(À’rЍ½DéòéaT•`˜#´¡xÚX\2,ŸˆOÔœ€°å‚Ê­$MztÂþä)j4´l½ƒØrÐæK¦•”ƒ4R=ew ÅH.·ˆ?&Mèg©K§J8Ub¹U‰»›{F`úD}äc 7[ ÍB°5Ì6ß~NS¦0Nµ¨âŒXaÕ‚À?%ÇŒ¯gV-8Øs4zñ™µ Æ´½”Ó2¥e`ÉA‰Sˆ"&±B l8ãàV f¯²,35³Ø2!9¦7 |³Ø &™`Ó¬'¶÷[M|ɹFŒ*°¤Ì½¤ÜB¥  AqeåVIgÜß6`9ÛÀÙKaì_š4 ®k¦¶+ŸøçY˜4ÅaÁ(ݔÔ(-(µLtºÞ3éeØ6u§ÅÞNá³°(ðSQªjðH糘jVx9D*BË ŸÃö!‚«»Ø>˜Hδ3xeðPÐn„ÒL…Á˜rF.}”å0Œ ;*¨^óyêa!ùœgó„…Ø©®ç Îð% ! xµÆ7àO¹¡òB°l¢÷ƒ¤f¥È¨²ŸÓ.Šr*”di'ÜyåÃ$Ë#ÉÊ!YϦÆíÕ®xX½t)ù 8ö)CXƒ\hе¦e sºáCê†Ë«íÑ¡¶‡U¡íimµ½\Å˾qà/ gþØ oÈÞ¾)dß(Í‹Pa,/ÂQ^jÛ7ó"b (vÂ4²~DÇ_ÛÓ5GÐ sÍq Þ¬ Úñ=4Q`ƒ7aN7Q„õIá¬rhb€Ò)‰’>§6°Nc%¬äÙå9é#aE@4MáÈÉ‘S±HðÂÈIÌKN„©µ 0@„iò£¸ `Äk‹!͹DH#æ¸Éq“ã¦ÀMÄ·óL`:ƒ´æøúZ1ÂJ0ªlǘpÜ4 pPsS×09åá |Iɉ)½0rÒs’SJ")­¯†7 ¹hvróä8ds@2´ ¾d3è[eŽ¡”YÙÅJJã¦ó "Œ}…qnúÀ¨i. ûÇŠ#ª@£×K.s”O™`\p²°R?Ò§~WžP‚͵í @36Kh¥e÷c‰õ–¹O’¨ópŽ6¬èâ}p›¬|»FŸMß¾ù¬´M÷–†(éw–m¿£œ ‚)•JϹßô0J54ƒÕœ[/¸TÌ.G ÂVg±áŽÃ\luâÙ×qò=7×lå|ó#Ÿ¶T¾ú¹WRV…lG 2ëÚóX±ÈLZÞ=)1ïcþ?ßoÛ¸>‡¡Å³¡£–î8F“ÓHp‚ð¬ ü†˜#Á•!AncÙF‚󤣼QÞ› ;oš‡U‡…c=Çz«Ëzl~Ö›Â;Ö+´ìáLïLЙ×sUÃóAቫ™’Uff»©ÉÍvÀO·1å̧IdƒÕˆFÂÆÙ^õèNᘫ>Æ #%™¦J ^¯ÍùýXgEí‹%ç´ñ|ÞLÓ‰á+LMªÕ®9 Ú×ÅOƒ¨—g»zVvðVœªS'øƒ0š¯'ŸÂN…`óK%³òœp~'´Zˆ(/È+†óó²66c&¹óŠ=J¯v^±ÁFC l#$´BÌ·çxÓY3‚çÜs(9T“D;¯˜³œ}0‡} Ú)Fˆðm2?NÁ€ B¯¨S¬QñÁ?˜›ÄüÎ*öäœUcË»óP9z $É@\øB)âœa­ ±¼’J´ÆÖÞ¦Dq2+ 쬰Ÿe…Éù™Ž>¨æR09bsÄæˆmö¥üàÌF•oã @ߥD ZärÌöè˜÷DV˘VOÏ#él8ÇzŽõVõèBYaßfZVDÀ< ¼é9n…L8ÀbçœtÄæˆÍÛ’yHsNÐGøá̹›™íÉí†rŽKÇtŽéV…éÔCZpBß‡èœ ·R&u«pŽÌ™92ûyd¦®¯¨_¥(û„I˜³Ào#EµbÎj[~2óçý“Ot[À#Ú æHÏ‘ÞS =îRXܺï?| y×DK_P)5å6#8ã`4ƒ àÛ§ÇqF!5SÓ¥)ÿÖÊgÖßÌáG°·{š,–›Ñ\‹;d°¨)f‹Î`áqñÈ( éyj›¨™ƒFë[Ù4Þ3ï{xhäw…Æ*å.§™¡ñ.ÀúD ±xŒ %RÁ(“ùþšI›äŠ ŒØ|¥ÀD'``JáÒž-ViÔÜ!ãªe„d‹BFIoÏéè2BÎÍÊ9G Ùå|6_4+Ó#À9†ž3š•Sʱ=;¦ ÊA㡱ÈþÐHï Ò.ð Ðn(Öð ÙMòšù Ê#•MÏI§"#dÔˆsûä4ЙÔ2>bsš:d\¨ÒHÉ•Æ ¸å©ké§Ac#®râµ(ÒpLqáÌigN?‘þ´,â·©Á.)£\qÙàÒ—Ô†d å[¥F+*à?Eð–ö­Œcɰ 0¯¸Ë º€ì;ø1Æýæpèæý—͸U„Äk’™q«ˆ×tOôqÑPË ¥†O(GE0ÆdÙ‚¡“~fRo/¸0½nÐ6Ç?»{=ÌŠž}Mó®ÝþdÛƒú}aʰ>[4µwÙ S‰+’y¤r5ÎÙ¨Ö>™¦¥bv™lÍ5OßêTˆƒ¦ ÚÑÄè6ÎG Ž@:]£µÿÍ{“†³%"Ï“ôëg³åY%NÇÄ Rƒ€_5q–ç"ˆ“8âtÄéˆÓ§#Î¥%Φ½±XÚç‹ÚG|b!,UægSè |õ–”€¼I Ü+Wam,sl£-eŽm´9Žˆû1ôB–§ø„Q3;BŽó÷X»sjtä;3ù®>çžöÈ€ÛöLv•¤_¼½¤c~6ÉÕr¹TO$U}zO\î˜g¡:’t™"3E0—)âí/¿1·ºó~è†HÏœ)âi&\Œ¢‹Q\ÍEúЛ¡gÏ*Uƒ-@]Ôǘ•‹G›ÕéûNßwúþjéû‹}ˆ…T¾e0)0a\Éx-ÏíMµ/ò0}L¨›šÕiRGk«ó ΔÖìí­ßõ­(éwêwÛ‘ #AG‚Žg&Aú™§â> =θ[¥œà„8ãÎñšã5ÇkËñˆB·˜óƒœÀ7.æ,4OYm=fzž²ù—‚ÜjŽ[Í©­æYfÒøÖy‚»· ]íæ/*' ‡§¦+»¼:„t¹ê)B.!Ń#$W‹Ê爛”@2sBÇðÕ!¤CÈÍZFÈO‹ª •Zó÷ÕTh®(&ZAµØ!^Û;®´t{Ç©CÝÞq·wÜ9sÝÞq·w|i÷Ž3âƒ/“sÁ@Ç­§Î[›–e«¼¹d‹­+Ê›D9Þt¼éxÓñæðæNÜË‚¸m–0ÅI…‘”àyPPÃ,-|û /ž¦H5V?’§~ZŽAñâs„¸4O]ùÒhl%Ý~f–!•¾}^¯b àT n3@1?­” Á”&Eª(›˜ée˜U&Øm©(•¡bXôÐôrhty¦~‡*‡Ü§íG²sš'‹¼µ†?݈”Ô3Þº×2ÑézϤ—aÛ ÓõŽçi¼–¼è( NOög!ΰ{¾ÑJúÙ¹×j›8HÃÄÃ7Ãò'µ:MÞõQ±É×ý .­ŒM£[>:Úêmhdt‚ÁJ¯&UÞ±è<ÿIÒÆê£– [;’«ž­5.Þ ÃEé8èöŽ’ʯ>¿ª~Ø1½Ý¤ýÅŒ*6ŽZ©ôǰžŒQeÊiòe¹ƒàÌŒä°y=©@´ (¸\˜™t†âÓ=JZí Ô‹Ñê]±r\›t|æ‘k¥B¿öÂo›.EIû…†¥~{׋À×Y´õfëÆÇÐ\ÝÚì´ ½aš>Å7ãð"ÈLmUÄÇm‡i;yÆÛa/«ˆV•ùŸ—mÀ€ “¸Rù¨R>Ê»ù6> ãz_:IVëŒuï$v­v×Äg%GÏðdèÒ :ñDÂqý9:hÕ g*?)lÐÅ/Ó¤§.lGâå{ =­sSò>$ï“N›÷z»t§‡„Å:FëHxX¾äú%FÞ?ìÈÕΕ7T’ÕÁá÷A–›+ïÐ;ƒ+ŸjŽ$3³_Ò~‚v=1Èc}§† P¦ÌÌ[lz÷éKo†²Vœ§ Î^ëM’eÉÅû …ŒçCÉ£;hï4J‚lR¬Ÿžá1dgx—¤á_IDÐï0®L¼ú‰Ú`ÁtOôf3û«óî÷èÏO™ÿ}}Þy—l²¿ù÷NëíùæùþÄñɧߣöÙ|ïìÀŸÏOÞ}Œ6áøÖ[~tD~ÿúùÓÚÜùôv³õÊüú-ºÚúíæîï‚:pŸá±¨rSû`Ò¤ ÝÐ|ñÖØ~ØKÖ6Ã9&Ôõ¦sÔìž¡jÈ5÷‰ä\HÄ™dг5O1ì+¥AoàH*P¿ßÒ;Í%›iŽU˜ ‡V|È©p”t¢ùI,ÏoÄ~l,ŠLÑAMÐÙ£ëÛáå0¹ÚŒÂ³xÕŠVÔÈ‚uèXë¨ÀGaVG­ ¾ zJKQã”ô¼Ö¥Q¨z›í,¼œEÓË+[eÀº ç¬û!¿öÍØ¨½áê?ôÌÛ8 Ó›†ýã¬Jç§0u:ªoõÓF"ɺÎÞ ²¿ýÖ b¶ÿ+†¸¤Óyxà%H'ª”/IØÐl§ŽÀXé5O’Š|dLøQ^eißlL?ìGc†®€[t”£ßÊÇ'ƒÓnºxºVœ¼3V¼«¯ªP–>4gè5ˆu¹æ!_T^y\·É6¨B £ò9I.ªÚ¨Ï+§¶…>`÷ñ¡aµ¶Abž5¶?ü‘ú£òkÒ›3¬¸Œß»HÓëçÿA±A øneutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-flowew1.svg0000664000175000017500000011423013553660046026636 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.1.4 2017-03-02 04:52:59 +0000Canvas 1Layer 1Open vSwitch - Self-service NetworksNetwork Traffic Flow - East/West Scenario 1Compute Node 1Instance 1Linux Bridgeqbr(1)(3)(2) OVS Tunnel Bridgebr-tunOVS Integration Bridgebr-int(4)(5)(8)(9)(6)(7)Self-service network 1VNI 101, 192.168.1.0/24Overlay network10.0.1.0/24VNI 101Compute Node 2Instance 2Linux Bridgeqbr(19)(17)(18) OVS Tunnel Bridgebr-tunOVS Integration Bridgebr-int(16)(15)(12)(14)(13)(10)(11)VNI 101 neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-overview.svg0000664000175000017500000013665113553660046027133 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 18:08:15 +0000Canvas 1Layer 1 Network Node Compute NodesOpen vSwitch - Self-service NetworksOverviewInternetProvider network Controller NodeSQLDatabaseMessageBusNetworkingManagementML2 Plug-inAPIManagement network10.0.0.0/24Interface 1Open vSwitch AgentInterface 1Provider networkAggregateInstanceInterface 2FirewallProviderBridgeOpen vSwitch AgentLayer-3 AgentOverlay network10.0.1.0/24Self-service networkInterface 1Interface 2IntegrationBridgeProviderBridgeRouterNamespaceInterface 3TunnelBridgeIntegrationBridgeTunnelBridgeInterface 3 Physical Network InfrastructureDHCP AgentMetadata AgentDHCP NamespaceMetadataProcess neutron-12.1.1/doc/source/admin/figures/deploy-lb-ha-vrrp-overview.png0000664000175000017500000053764613553660046025764 0ustar zuulzuul00000000000000‰PNG  IHDRÊØÃÿßdsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì`Å÷ÇgöîRè ½w ØATìX±ÿÔŸ’`AÅBËEOrÛ_± $Ø»Ø*¢"6TPi"%¡Š´ÔÛòÿ¾½l²¹Ü%wÉ]r o4ÜîìÔÏîμ}ófFvL€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`UU†àL€ Ôc=µÊóå÷w¹”íÓ=“V×›‚׳‚Þæy¨c±Ï×S¸œ³=6Ö³âGµ¸Ñfq³Ç{¼æÎìL÷‚h4iF«lñ󉇻ÀeˆWÎx-—+~x<_9·‰Å]B)~Ê3~s8%KMϼÆ0ô¦ÙÞôᄯ/a,öòBèI"i÷4ÏØÝvÿšW—_Zx¢!ŒŠUý5äMMÊ/q-æá<VXéÓ|Ïdº7ÙëP]¦ö4¬ã"U½Lc𢩓áw¿åßÇxê\¤–"ñ¥,ï¸5V]£ÍBUO‘v Ã0)%^¯ð\¨òQìPiF󙯔± UYý«Ê5Ÿªâ»~›;£‡ætÊvâøÏP5X»_ŠçÁ®a8Û‰ö¹Ï …c¦Li£˦ö0Îcß“'î¬êyˆf\ Hgóžòܾ×^>>ð° |àÝóˆk¼],î®úÔ5š«ùªó䓉EÛö>Ε’™ùAvZÚ–ªâÔ—ë‹Àòæ‰ýb”;£X™£Hñ®b¸žáO¼"v ™_Ä0ÁbÎó·U,î¬ùÔuèP×!jo+?fj‘¨þo±æe"MH_k¤2ºú)Å&f¤åkhÏD¤õÍ]¢XÈW Ÿzb®X8y¼]Y>·xnïóÿ¥AÙ°£ñ–[X˜¯?. ãj{<Õ'Dª;³hTšwÞíW;“ŸzÜs×.{:Žf\SÂ÷í)î ”©=Éêèìü 󔻇€ràT•kZ[žºýö"!¡’baGߤmµ•o­æ#Ån)•ôáx¦ò ü.€–±ƒ®÷¨†oiªÇ;¨:e: øUL â0ÓÀ³¢Jù-žó b¾´¼âê7ÂòUöLfBÙ²[Ó¼Ý⪎•¦’ú§¸3Óñ!ÿ`eÑ£uÍ‚F³%å„Ý`é«ZñéÀµ¹Ó'Mú§\)¿ÆóöœÿO¼ƒþd)lE»a¤Ò³ß—ÿ'iÐË…·ŸÔ .ñyfÛ.d¶”âÃáñ(üÍÌU7ÿ:Æód3{V|Üð °F¹áßã:©!L.Fø3N¯“üc)ìmÙÞ´[óIyðÁæÆ>õ4þé>4òB&œó†Î/ÑÃLkF4kr ÈÝk–JìbW§|¡ž‰¢<ý¼Ã„Ë‘»G7åÊês¬ð!¿8º9O-!YyË—§O3¤qöÍS§¶œ1qâ¿ÁCÂW/¦_©FʬYioÚýKìá_E}†ú|ÏãÚ™öë¥Ç5ˆk@8Ì75ýÁ# ]}æ@‡ª{g"Ÿ«JóâƒO€5Ê þ× z™êïñµ×_Iã‚ðÊÿ¡RÒ<£=ªSÒê´%šá¹P)'èù:lùƒ»[=SúBà$…ÜÓÑÕé£à¡Êû>ퟫ$‹kÉ/ÄgvBù¡Ïj7+cÂ2©8¡ý¦|µú,†®_©% ¶Ã«%~œM°ëú—šee¸ûÙ;å$ÝОCs³¼éw’ª;c †oB™LLC«>i|$ éKn$Ïy"-­Æ¦"Ùž{v¢þùèÜ%»vÒ¤”|«Œ~.ÊÏ(Ëè›Ó§T mì¼e«ÏA‡Ñ2%Í{=fú¿DaCñ£k4ôW¤î{é-ܶ·¸m‡©ËOŠS> c,“ 1†òŽ&Í`V“y ËñåtÈ]&ÔQnïf„5ãX¡9›oŸ˜HB®/óäqqŽºÀÔc÷wdðîÅt+^¼ÿ†bJB†¦iâã]©éÞ¾bm„{ñê»Ó^'ˆn¯feº½v?]Ú­S¦¤æé“¡A;)Ç·ð0ÄÝ‹ã?„¢dΚœFÓ"r$L9;.ºñÜÆH³Kª{¡Šòÿ-…òBgÇ',;I܇Opz9¤óê—ËèfOæMÕgã©Øíu§0‘äa¥Yúî 1ï×S–e¿7{¦öÔTõ¿sêÒÓØ¸½9ê±ïä§tު̨“i::=ózÍ0®…}ê@Äq"îrC(oÍò¦=˜ouÊøLŒvgœ¦ 1ÏÀI”¾Ï§Š0°%S™ƒIg™ºÏàøçY^wH­"Úi(ïY†T<ÚIJ‹Ò¥géR!•ÉÙi¯›ž¶ðýÏ`_p{íÂöKæ!ÞÝl¼»ƒ éH•1é›Àú›|êöÉBWo¢{.òüÓJGQäD{; 3Új0 ó±ÿ Üñó–¯Âýòn@å?iìjô@0»`+=û/Ò~UÓóñn‘ùE¶ýšu¬jºešñ®õ<[×*ûévç [‘vûÚ_}ö÷ÊÂÛ¯Õ$. ËxßváÙhµCLítWÛÓæã†K ö¾Ð.C®Yè$ú A98ð’nˆ–äo¾ËòÔ¼ïIøB˜ èx^@C½”4 šf¼‡FßÒÊšIø f<iZç’ûÓ–-¿„¶Í©‘.†p~sª{Ê¥–?ý’€T,Å‹ˆs¸TdV4„dJ×Ô”@ØBÏ´90M₌Þâ~¨Ÿ¦k_ ºBÕoèÆC°*íÄBñí™Ú½Ð·÷'Ô'é'¢_{ _A°8Øð‰o‘ýéT†@gÚZª¾¨ë5Ðàü‚¯|ù7!fèŒ[CùHžÕZ9 ¬øãz¨iŽ/sîËC¸Gm“säû;"ÞÓè°Þ'ŽVøxþ É4Ýû8ž†—Q‰.ø zEòn°]‰j‘ÐŒgXn€zómEQ¾©P?)[çéßãbâ’Ü‚x/âo¸ž$4}¦*Ä©ÂCæì"4ã£{¹éÑóïTÝÐÌQ7gYI@p^Lþª¡_gùþªšq…Á]¢WÓE’‡ÇzwñÜÐd¾°œ¦j¯ ï4<_É`ôÞq“ÀçÕP¿OñL9$DBÅ£Ò½ÿ‡úâÃ×h  îw¸‡ý„¡O£ç.P{_ò>ûãÙÎÅsî7† ¹œþCüÙÞyð”·%Ê­î¬ì÷<òHcÝ7!NϤdIæ*Až¯Ut_PŸóÐûŠw‚&Ý7ãúÀë¤é—Ëáß+ÉÑäºXó#Ý]ýí …{¬º˜¿ºá¯#]ò»ýø`Lƒ€û&â´GÛü)ø¼‡º6BYnG{½8ܬä–ÉÐË}(ÿ)·¤yñ^UtøÈ¼ÊôuÈW+^ íCí þ'„ÐÕˆVªI\Êφ9 g'¯„A@ç<@êÉÕŒ3h@ÓÐì¼/›:o s«x)é™7º> äTt„χ³¼7Ø/MØíɼ 5.gb–õ‚g<ã¶RØÔû2ï€Ð:åø0+#ê»[<™GúT&ì—ÂqG°ÑÌ÷TEñ\Ÿ7ožxã£÷Þ›,\0?]UIß‚Ã;Im›]cNH‚i}·¨^/:ˉÁâA%ö`Þ £û Íΰ 㻣pûhgD‹§|zº'mµu~÷ì+¦ô†£¬o ¿¬üüþ¼Ðñ\miŸ^¢ðõÍ‘¶Uõ·£Üû„«M×lOª¥ý ½q‚½}VFzzкúÜëD×õÁžcÄÏñ+‘<Ç3˜4oô}S®Kp4ýÀ¾4UIYÅ3ûŠ“¡Í_'œŽ—¤ª{p®@>wæCF,®ÄGI/Zuˆ(+R5~1)êéÔ}Y÷+:™ýä¨9Ï¢W UŸz~Ê9fÍ¡ß%ÊY°½ýºhÚ¨ªÅãy½0GûžâÑGDÔ\v†ûi$ö4îýOø…Y@ÂíY™eKáQF)éÞQöqÅBÐGÐ}ägwûö_„gÆ4i‡ýšýØåJøÂç+BRr¨ÝŸŽ}Џö»xí}ÜNßöuÑç­X;¨ä}^`Fìé”|¤L¹/ó\CÓ?F™¾ËÎL·´¸ö æ1¥‡0¥CžŸ=Ùý‰€>–ÿÝWü®÷+Ú±÷føO³®…ú}ü®» 0Òõ.Ò»^“ÊU‡÷¨Ì¥¤O9ÎеÞh6uT&}#¨;Óÿ0_¹~FZZ.´ÄaÆ4ï] â>x¸¡ûšãžlµú°3æ€õšk”ëõí«Ï…—ydËk.¨6Íš%¼!°ZŒV´,X4j8Ó“öt=ÆAª¯øYJ“4Y.¦Bs´å¸)â| ÙÃpKl¿¡sÝéSõÖÅ™Ô+Ë;é½ é¢nðßáüÊH„ä›Ý‘†~$ áIÎæ7ZB+åáñH½ƒ“z ¹–Τ¤ÈO ×›ök”|ˆÍŸ:‘RGK7AЃ°/ §Óá¶çG¦P®ÜE&üžª4õÈOˆVö§û´¼`9ª~j@#ÎÙ„d3h‚4 =7Ý™%¿~p/ò9B>Ç.Zçª ºUˆX‰ÏÆËÐ OÚrD#N(ÂÑ~i³Z¨³Ñf«ºè,ò³»ù¿­9Ï}$ù­)X—\Œ${z‘geLZd’)¾9̮Ȓ•Œþ¡ÒD™³íB2…#;SÔ÷63Ža¸ëÂVÔépÌ¢w×Ëf ×Pù áxÁ,gˆü´ÕÂh—ê™RŽÚ¦‹ñ^àCÃ4¹ð©Æ{2¸~£ŸÙýkz æÏdON+’)½‡Çß¡B/FÃÌß0þQJ4źa \Ρü¦ÀŽgóuj»Ê] qB}©iÞËñÉ÷2ÕuOÏqˆàå¼k—2ÍÖtÕ2É*—8Ÿ4x,(7ø[§„¹Ùò–Î †Õt½Gàõêž“M%: %švz7 Ÿþ ä£DhO® VŽ*ó‘æ’FíÎÿgȶh±[IØQÂïxM-¾’çé(Êá6òVº(>‚ÞYùn Ea¨ÃA¿ñ–ÞúE§„QZÙœ:ÞÎ6›,ëiþe²¥åG¿ªæ; ×Ȅ䫞‰þ0¶–âk¢×ØÇK¶]ŠÉ!±ãvUüµ‰$s ‘—ÔYYO¶mNCðÄ.˜@d†7äWÁ´†öçåÚs ùà+9µVyñ@¼@ǚЮµü¬_ £ûý i†±ü+ý ’G¥á«q±“Ò±¤¢´ÉÀæ~v ÏÊtÃdK®‡ÐÖãñpÇ`abé÷ŒgâZ| }‰÷ªK®ê=Ýž}\âüt\ßÞÉyÂûµ`ÇŠ">'h|O³®ß‘™ÙÏúI>¸’%a ­ëô‹Ñó£VW¸='C0w:„)<£ a?Ë”I_¡}ÈEÙ`Tï0«”Ô.šÌF„é¼jùþÂFÿQ("þ ?ØIoübÙê"]À,߉x毆ݶõ!UÔ(®¡Ã–;ón2Còã)äý™P}ëÑw†öà“ŽN÷ä ²Gƒ&@:;&Pû ¹9T¦öþ€FšÊ„PaªãÉz7Ë!7NGê0ï”j¬"HwÝ,oz9ûJ²™Ü&†N°ÉÔ§}±|õ…èN9r¤˜n‚âü%ЯªsHÁÝ! ˜\*,„ïÍèÀË]& þ×ô‘£åR‡[N«ŒóK(‚"ÒX–9£ÄZQhqß(»à?‚@z é&ì-è_ØZw¤1ÓuÝÔ‚ÚC$$)_WX?Õ üñš@æå/c ØpcÑõþ¡Î¥K̇m7œ~%þyÀηs/M²LFg¿4”Ö ¤+|xXiXϱT”Dò£–”û3/¶®[¿NCÙ>#cÒwÖ9ýRØ[î›z²fh‡A(ìŠóŽ`cÝ]Lƒ,åw\Þ‡Ý2Ùj^@ËZ£4d+»gwÑ…Úö7kžøŽÞú$+Nu~iMbØqC³-º™uF L&M¢´P§ÒzTHÛ)r*ø•ylÀa"Ÿ¯;~C¶%eÁ£|d˜Ô¨N7"eSØ¥¬uQ­WÍ`‚–æW¸›÷º A™Ì>°ã†W¯£˜Cax÷–Àûx Éœ‚ì„a.uØýÓ‘>Vƒ[[QR»$— ú<ëjÂ?BÐòøÒ|–ÃI˜Þj7Àèn]35È)Þ—ËWúµÅ_F£}!Ó’F¢ÿ™G|à_âìu4vô«tÉ9J°q‘FtsT€’Â˦!ߥŠt<83câû‘*8Ì4øŸzM€åz}ûêoá!D„´Ý«n­0ÙJjZèQ<êd0¹ê9?Ñð©ÉIÒÖ¬n~ñJ:ÆÅ˜/Âpû÷D†Ì[±æ„{60lkAvÒã½+=‡^©õܶ… ˆz½†Í€_ÅlŸs¥¡OFçÕ2Á™ða±Ï×úHõèâöÎæÚÓ§n%ç½ÐYt±_³BËRê•~ÔèQÝpÛ♇Xoõ,ô¯­ó"íç\‘ùîU|Ld)ÒxCOVV)úyÐÆš6æx¬YET öí·ßVJ&é• ¢ ýKxœnyŽºÏ{ VߘŠ»ØúpϡنfNÂŽZH´Ù¸3†/¿#S؃âãÇ-÷û.…ïst¥ÔV«&šøDš‡?§Èþõ¯Î²gFa¾FeJ@]þ…²ÂÏNÔ¥È}p*Õöj'g¡œEܛޡBÄÔ¿k›ä¦íÛñ^Œëy¬•µž#SÀ’Âùb8ùwP:~—£o.Ľ;•F-H¸D°qƶ¦ºxߟ†ò!î÷1…â|œ?[´s߉x’ÀðÃPpþx‘ÿ{RŸ>;Ÿˆ1ž×ÂÓ”-[Yœ¿PÕÍÞFUÇ$ô媙˜ì)äûXƒöþìŒ +¬x~mwá~zY-¿Àß\W.™Ål ô÷ŸËVôœã#o{ðë±õÍNMõÁ$\ñyZ>?ñ4µ/ªQ<ÏÍR{]++IÉGÎwH猭ڃG`Dà/±OFiL÷º7P\—t|Plè^Ë#pú,žSÒ>#ˆU³ J“žQú–{Æ“ö+8ýŽvðÐÑd„áW,1y>Ú=Áé¬0JUY¾é«>hñõ Xvð•`æ`¡âGúo33Òæ‡J‹ý<,(x÷¼^ÖØUœŒ‚÷SÙ…ª´£Ñ ½L ÓçúV¿Ž«°¹UF ¾ÎH¬ú)ŃFª‘' 2ÔçaUV•ê÷7ÕZÏÎ!ÃBëL×È(90 ¶k8b‰ŠúO€^'×Ôê'ÉU´¶h`X3 ÃXmÒ”â`×#ñƒ ½áé/HI†`Ü Ð¾s(òq+.E®s´jºÒ>±¦/ù8¬ôƒ:)xfœ¸ÉwbmÝ—óĪ&æýµk”)ÌLûGhÜ ƒie‡KÕòó ²•]ƒµ“Ëq¯ne©ì|›˜rÞ¯Áø [ßÉyÒHK#gÅ)Èó™õ ±|˯¯ϴ_.(¢«Þéü»B¼Zò@ÁgáãoÞISPÖ¥ï*’ÝáÿB„EøáÏÐ }0TëÏ\Áw­4¦gLø#kñN1׿޴}(]“Id¶ÿ·øUÔiªîçl ©ýnçbMÌŒ¤ô3¼ãWAñ$ß­ù4RrœnüšÄ 7×p @!ÂŽ Ä?ÿrÉV)žÌ£ƒ—Ø83¸?Æ­ÕU^4ÖG£ž5Ë;é N×èÑvaº§nñLí*^õýug&”_«ŸF@L]˜i¡/¾(äDAlBË<%Û]h*ŸÂÉt,÷86Îxk¦7}~(!ÙŒätü@šn—ЦÁÒm~Ūo ˜ÚØÙäÚ€aÖd÷»¤ ‹¦§C)¬Ó K\É®vÇx—ž¢Ð Y¤ª—è0wÀ„SC¾X ì Úy”%Qù‘î3Ì<ð¾®’)¦ªj§TžÒ/ftúÔ£P7,-&¶Ó°x°05ö£•wà©¶•VÉ*"óðqr‚¹î±Ži0ZÕØÙ8"M©ÓåxññJL΢üœÂYnÅÌ^ûù4¡õ¯!x‹|~«ôݵ‚»Yõ!ëb ýC—óuˆõx<ŹøX?×ÌÀ¯V'£DgóÉtßÑ–Ÿ›zŸ—4ìa»šÄ ;Ø ° Ü okƒ­ÔJ³fšqI` Éæ¬ ‚2ÖK†¡ì{ÐX¯ÅºÅc).i3RVY4ñù´×h"^`šÕ9Ç0~K mOCº§¢Aß«8•·«“N°8þÙþØtÚ´/—­6'ÆØÃvO9ÈÙv?ëSîºøåQ´¥í-žéMhç®Ê6ÈòLúësvZçk/Rݬô¬_ÞhWë¼>þ¢ïÒ…!ô³hÕâB!Wºˆm%×Sòšj]o©£²›w<íeRÏ€ƒFä+xæT\#pß.ƱÓ–—‚Ñiµó’VP/ÅåÏBÚÀ¬T÷Ã} Õž4¢Í“æ3ÒÆ<6/A&˜àX¢•W°Fuys!{ØûÙ‡t˜q—M},Þ=yùŸe¯2RÀZö÷j+tëGãY„^üNP{0Ì<þ€Îñ±{òqAà [› ó!³.UO¡õÏíéÖÆ1-aˆú}‡,Zûùrç·hæ*÷!n9hµ˜F˜m6,yÂÜ}0ÌÈ5‰f¬ˆŠpÐ@Ùpµ ²;†¾~ô¶Î1a [öºK‡ -ÿ¨ý:¤“¡æ`†øD,ÙÓ ÿ0Gh ›µA˜ƒ äLt%£íùQ' !ïeɺÓ%®µOj‚ÉÅlÔ3Ì«rµ… ^š=neÇèÄ;Ãöî#+ 4åÔÀë¡Ï±ó„Gëå9 yж¯Ë!$Odè GB'Zåªv®Abò [U/*ŒáHZãÓqy'Wß; !Á̶¼Û¢.8T'ð²Xˆ¿9ô‡²~†&í Áà“Qî)ØcšÂa—¶Ç€ïdÔM§«ö~w@>¿?¡™ÝÇ‘½018¦ z¸ÀƒV–ðs‘;'K D·Á¤¸{°¼ÙÇ%¦1(EY’懦"o…&ºÑó¥Oóq¸§û’œ ÇáÞg•…växÁÔ8Bë¨Èàk'×<`ù–÷#M¯âtB«-éã¬1„6ôq–¥"çž~Dßá8‡0l@«kÐ$Æ{!DâV²::;Ÿ¸ALYÜš™›nHeæâ™#‘7>ÂuŒJ•w4©sžGùôü Ð;lÖžÒÿÂ:—ÒUá}¢I§¨»ùaNá¨ÿ·Vøª~q §týÏÐt/ð|dÒs¥ 刪âFëºlì||Š)=E„^;9œü¨>ènG}€MÜɈVMâ†S6Ó0 àÝcÇêZ›ÕW ;7k¢¬­‰v¢þÕ¼¬Äd6¡îÚwp¢h´ÑoÃ]vÍ~-c*uôè`³»M 64L ìCè~*±ù³¼îaöøöcÓü"_ôÔ ÇÞæM­õ™=Ùy±|Õßh[`s‹a3îOû‰:R{}KÖ"^A¨Sq \ïØ6šÇT¶¯þX×MÑd²¯sËÕ$E3}J«6ò |h¸¿P+ìÝ%rfzîõ«ëéBŽlì¡ÕqN¿ºÀ{A2¥wLß‘ß[whޤfÉkhkæÀD Ý¹bi뇱öøÀëñtN¦EXù§³!œÛÉ$"žÊÆeañJ€åx½3\.&%”¡I’G'¹\ÝŸòŒß,Yfò|ûÿ…Æ«$¤×k›ã`õ æ—êžrŽnhŸÐVåH †ü0T M¨q'f`% LLbÇü ¡•0+Y…Ñ>Òåè—í™ô'³aL a`Ó‹†u?¹6L êÈñ(Jÿ %$S„Buÿ‘þˆ2¤ z¹„À 4臙ÕPĊʪƒIV~6އMe<øZÉ·˜B2¶6f!¹Œ 1†D€å†t7¹.L €€i“'Ä·èÌiêÉ4ôjBëKC³z©fÈÙЬú0|<Ë~½A+Ê7fýt16%}Êq6È´l &r½á!d’’uÿÄU šW.ld’‘âžr!>B‚­¬†U>&‡™2&P¯°éE½º]\X&9”ÌÌ¢@ÿ†4_æ°M”Üà Ldë ûÛ¦ðÛŠÉ€c°Eë;‘çPcÀ¬bæÑvÑ &a;n± ¾D;ÈÝÀ‰vÄøØéjyÕ3ž[÷×ßšrÉ£A€&»J¡ÍÂ3’Lï =˜ÌwG–7ý©h¤Ïi0&XPŽ¿{Â%bQ'@ëDoQ]áø4˜c`ÑýVhœ· CY„e®>ŠÅ„±¨W$ ÒD1]կê½ñáгùH Þ‚ÆqC‚4ÞzÚ›¾>Ùr’õÀÍžÌXžq>¬ZáùXýÇŸÏΘôC=¬ ™ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`õ‰¯£\Ÿî—• 0:!àñ¼´MÝÞ-©•kããwÝUP'…ˆ0Ó;=·(Ô ™1ia„Q98`L€ ”`A™&À˜@©éÞÑ:¶¸Æå^Ø ÅZtìʶVQÄãYî™!¢Å…wjš×­ 1Y6u¶Ìž0aO\Š Á˜¨gœõ¬¼\\&À˜@Ì Ìž=Û1oÙêuÝ8{~?çPÄÝp­•Rí.4q&„çgF¥yÏ:㈾—Œ9R‹yª‘"]o ןÅBr5èq&À˜€Ÿk”ùI`L€ HMÏLÑu=KQ”k³2Ò^ ¸,RïóŽÐ5ã©(£²3Òž ¼ÎçL€ 0&Ð0° Ü0î#ׂ 0(ðx¾ræøn†™Åºl¯û¤Pɦ¤yç i*º´í*7m¦ò!‡K^=Ó“ö›=NŠgÊ!†OË!•´™ÞIÓµQîŒÓ…iÒGBìÒø¨£³ó$ç†B+.Ò¿éŸÙÑé¾6GË|a¯„ùG„ÿV1䯬L÷­VXëqž0¤Ñ{–7ý\аwe{ÓŽ”ÈÀ SYÞ7»:X5|³üÞloúç¥qîó^Mú}Š"±8Œvg Ñ ù„Kº®xÆ;~¥ž™` …€ÒP*Âõ`L€ Dƒ@®º Òv†¯Ušž"ß4 ÑÞµåŸÍ›&,„pÙ[Sõk*ÄQµk ðöÕ›(ßÑ5Ø_. 9—lž!xÞ‰F˜ÒQ9jÎ'åâJÙáNÌQ§L“º8KHñ4®ƒvãH½7Òd={øq=Ô”ÒAzËLŠ/Œö0UåÝÎ9n Òï‚¿sìñ $_úõ7 ãj»?l ÏAݺÐcµÝŸ™` … Ê åNr=˜ˆL܃¾WÊ •%³y½X7z=<~ü>Äxæ•&ñSæ !¯‚Ç4¡nÌ“O&âê#¸ú1´¾ƒgf¤½Hša\¿]Æi©î)åTCÝ1p@GWß“²3ÜÓ åÍBÜ—àŸX î¿¤,!vïõ] ÿdE$¼d÷·ŽÃÉÛãH.¯ ˆ±â}ì±dÉÃ!´?/ 1ôÏô&Ö5ø EÙçÇ«vi9ù€ 0&PM,(WGcL a€œÛŽjfÊöÊj( e§yÝèW*ò%h˜»ŽvgžbÅKMŸrêaÒ^‹wî;‚tÅ!^°ÂÐ/Ä[h˜ñ+õЦ†t{<#‹é:9Ëë Ä/@9Ëiw¡q¾ Âû3¼ãWùC–ÿ7ܼ‘öH{ÀXÏc­(…ý» ÏÀO‚p93!D»|ú¿ÃÉ?åÁ›ãüh Oçì˜` ‘¯zÑï*׉ 0j¹ÙРS5 S•a¨¦@- }3…é Lú*WÏÜDdœ~K~¦©‚¹Ãü©‚¥®÷$]ÃVx*“3  È5—žÝü>eÿ:]--;+9’‚·ž5Úëí4Óíι#3³]~¾1 ¦·UkE 3o‡Ã1O×U™§FÔP¬ AcÑ,Ï„)@ÞðW櫃uC(§ƒLGØ1&À$Ö(7ÈÛÊ•bL º\šñÅ•Rï^iRš×NçZ gš-ãeCè—y<³hR å‘и¾j™&èR6¦°0ex“û¶ÿ)R¹jéYtÝî¦ßKžýœŽMgCý\¤Ê+輠иZkUiäx‹Îƒ¹pó~Æ3q­òo)´!>Â8“?0Ó4äÿÏ9äoèÌ.ä_3<M^Áòd?&À˜@}'Àåú~¹üL€ D•@WÚ¦5sƒÐÅuHøÉP‰ÃÌâZ(‡öïµaFI —Óñr±OKËÕל¡ˆ5*„Ì6Òá€ö×ïCY«afœp*‹°:ÆÏ–¤¿Oynß íîû°&Aùÿ ]\ ö3&Nü7TZæ=ZäÁ9¾Ìã‘^ëi¼Oé:¥ëCŸQØì¢Rè|‘ 0úN€5Êõýrù™ˆ:N®´×rUï…´éHŠÛ{¢Êÿ§±;õõ04qŽ&ŒhSßÈš<é l8˜ÿK0'—xZ¿æéLϽÛSÝ™Ø{*Ö9.V\´Ì7Tý˜û'¤czvÆ„ ;ÇúÊór}™9X7ùih“·Ðû3²ƒå"É[w$Îz¡­òHï {š¨Û‡ð¿¦F#Wò|û5>fL€ 44¬Qnhw”ëØ@ V«K\¢Hq̆þ¦êÓ—AH~+=œMêõ³¼n¬2Q¶‘‡•i#g“· H6Ã_‹ÆÎÆoXþÖo–7í!E7BμXó?h>}&Åaé5c VÒ(´ÂUõkj¾ñ L@GØ×ìšíPqÃÍ;ÛsÏNß¿ î­¤â4Í.¬4Òñ!ùãü§iž±»-þeL€ 4Dh Ù1&À˜@eR²²\ ›¶w.îÒvsvjª¯²°‘\£%ØòÔ¼–-š&l÷¯ÅIìš…­Ë¼kVrŽÍ˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`ñJ@ÖFÁnNŸ:PÓµáÈ«›!ŽÈ´imäËy0&Cˆ}Ò¹ˆµÁ¡8>›‘1qid)ÔßÐÜNÕß{Ç%gLàÀ P}TÌeç…¤_ÎmBwCt–RèÂÛ (ç"ÓýÆ-åZ2úEPÊñÞ¶3 ¡)6ã½}¢“«ÓÓÏ …õ«6U—–Û©ªq&À˜@¼¨‹>*&‚rŠ;ãL@…޶«”r.þ^S)ŸÌ˜8ñßxÍå`L 4›§Nm©çëç†q5þ·îF„•íMÿJ)ŸMõÎhB bÎ"s ájs= ÉÕãȱ˜@<0ßk¼ß(ãïø›UòÞÇs‘˕۩r8ø„ 0&РÄªŠŠ L«[˜÷`“œíIÍoPä¹2L€ ” ÷ÆzßÍUmJ¯Äÿ·Sñ¸„L€ 0šˆEA™–€ƒ$?—'îÕäör\&P?Ð{Nﻹôcý(²¿”ÜNÕ§»ÅeeL€ T‹@´û¨ Ê´H¿dùZµjÄ‘˜¨w (¿Fï½ùþ׃Òs;Un‘ 0&%Ñì£j,(ÓŽ{´™­“¥úq2L€ Ä9zßé½/Ùq3ÎK+·Sq‹¸€L€ 0¨ˆfUcAµêF;îñf"Q»¿œˆ{æûŽ÷í÷…õÛ©zr£¸˜L€ 0šˆfå¬ia°%uG¤‘[Ót8>`õ‹mGÓû÷ŽÛ©¸¿E\@&À˜@T D«ª± Œu“›¢fû£Z;NŒ 0¸'€wŸÞ{zÿãÞq;÷·ˆ Șˆ*hõQÑ0½ˆjÅBbc{,ôºç‘G7„úPF¥y¯E~¦½­:rg,HM÷޶Îâï¨ôÌÿ¢Þ?6ĺq˜ÀH€Ú°;23ÛÝšæ­/fCâm*Wgn‡Ëáà“Z&Pcr-—7®³K½Ï;ÂÐñûÿÉ; ºgw‘!ëØpÿ”àr¥?íSÇP<©Çf¶Es”=mHÌGàüK»_,ŽG§O9 “±)fg¸OAY*ìúA~Êslv¦{`TË`ˆvH¯\½£š>'Ƙ@­ëy¬UžZ0[˜_[ZP¦)iÞИ,Šœ=+#íùZ)H-eíþ€ÛáZºqœMÜ`r”nIJzæU’ßC£«*Rü×á’Ç)RiHù²hÞZŒÛ¥¬B&“£z7Œñ<Ô9d€ú~Á'Aƒ}K}¯—Ÿ 0Ú%0šæ©ys…aüÔO)Òq®Ó%O@ûœõÀ÷)†Ü]»%Š}n1븎ýÍãâŠk”£p; I>ßÐõ—ÐèNëäH»×¦Õ¥!û÷üY¸£Sè$F§O=JÓÕ†+$SÕ¥˜QÓ©·¤yÿ÷L¦{Sh|… 0&à'@[—çîËùg]œÒ5ðïø•66‹qü„í¼AÆ´?àv¸A<#\‰ð ° >«!uM¸¥›;9ûN´ É!çx²ÚŽ Iœ mFw˜f¬†9ÁìÎIØãcXð:ÌÚ÷! «üË,lza‘¨æ¯Ç3;/ÙêÎòxFªìISx÷c‘ÐŵBÊEþG*âcØÿÞŸãË|Ç×[#íóÄ~ß78ö!ìD¼àS C´Ö„øä6wF ¯IÇjøÏ6ãJãI˜|x ©LóŸËÖ¸vbŽ:eó³ ¸> ÿq$$GT3±ÊÿIÉÊr¡n êj:¡á¾¶€T]ÿ*å¾Ì¡¥±Q¯Pe* ä@J¾TähìÎ%S— AÊyER?tXã0*0 ݯŠC\‹c–.Êoü·\¢8IMó^ŽòÏEÃëÀ=¹a?‡÷¨5‡7Ý „ÅçL ® hÆ1Pd ;¼Ïsáetš÷jihó!Üþ‹wûüa8P¶PEñ’Ôô)æü3«}V æ¡-Ø(…c Úæ/ðñ|•¡n Bò—ÄŠ¤TîÅï³HïUUß!ÛáÒr˜içîWó¾E›²¶Òw¢íœŒÍ|ŽÖ}Ú’[<·/ +ôQë_v^v„¶ö0œÑ|ŠÊû\¯iûi;6O”-Úí0ê:É~j=Y‘r’J:îå¿èO»³ 2ìÂ"Àå°0…´E¬Á-6o«C‡*»’«®¹•5h'³i'Þížò“fh¥º§œ“å4ÇŠ†·lžçàëw’å—êñÎÓ}â'ŸÁï±Y“¾Á¬àB7nOt&|ô”güf+,ý¢|Ý¡íÐÑuðIva>Ò²ØÓ vllÞ1 ¹ét)G=ãIûµ$Ìs£ÜÞ¿…®?„óc­x¡Êd]ö[¬'Ccò:´¹WÃ|ZŠçÑϳ=÷ì –ü­_ʃ67öû&"Ê˳¼î›¬ôîô<þvž/`™ó䓉EÛ÷>¨Ïò¦_XråÅTwÆ|ì<xÿÊbò`uAïï±Rÿ9r$ô •;Z©hïî¢G !ßB[Pú1îñ|õJ®º`®ëÓðžo YÔ>CŸ=+Ó=¥$åwÐ>5BûðHÃϠݾÕò‡Ð¶OFÆõ‘^ð[c•i4Ã÷»Y^÷ý–ßhÏÔ9”W«jÑ=𣿰]eýA4Ú¯HÚáHxÆ¢Æ}ÇÈŸØÕÉé¾Á6Zû|Ø09 ²/[ÆQ=š<„"B°%¼ŒK¡øÒ&$›Ñfz'A+×áË÷ŠÀt §ãe»ßÌûÓ–"¿:¾Šíþ•Òm’ýa#/KeyøÍ:ä¯6!Ù ŽŽÚW1érñƒ–©\ˆr'R$šÏ«tºnÆÇF²P / ÂIxõ“ûõ“бµpHG¶=‰iž±»¡ÿØîW¼sßÉè(»@ëü‚ÝßHV¨ŽP !-vL€ Ä)AVû¼o_ñ‰øˆï€ö€FÞJÇ3TE»=oø±·<ð`Ò 82áír熜Oç†C–!Ä•é/EQ{x:V ×ëv?2¹€Æ4(gÛýkzö+’v8ž±h‡¡å_Ž‘Ö[|™wâc‡ƒ5}€ÐøüàÔðÆKC7—|ƒ¦¡c8Iá¥í¸d‚_…Ð2½}›5v•›¸FÚŒ·w4% aC;]-- oiꔥ4rt0=Ñ!%C£ò‡ý2ËV8W´]yÔA”Úç+“=^¨ãlÏ„ÐàNÒ ñ$~_Ëò¦Ï55<¨Ý…[?ChÝÌxW©–ÇJ$›Ði–:Ø-ö¤Ø¥?ŒzNµ.:},éÈÓŸ–u™¨Sh—raVû S+Òö ‡+±´² /eµ_SUj£ÿ²ü góë˜~ñ½WƒîÚ¡;;Áº†•öj>”&H»Ô浄 gý*ŠX£ëâtë<¿Ñl¿Âj‡#à‹v£³ïÀF9íò”\ß»`ËÔX™ñÔ¤I;¢Á“Ó80˜º£ª1ª¥Ói6˜Xñ¢‚€2GÃð½fš„W^늀ÜsO~ÐðxN¿ÿ–¼ Á#,KÐ4J<Ñ5†v)&ï=\îO‘`Ì{ƒš$Ë5N!ËTY&%×:8ÝÐîÈÅOgÞâ™Þ\¦¡üí¬•$JVOÒŠ³6tQŽ´ø) ¾Už,WGÔ¶á7ÂnV`|Θ@ÝÀÇû_4izöìÙŽpK!ÕŠí‰Ãß–`žD¹6:ëþ”‚`éj¢X æÌÏ¡iúbh°Câ„¢?U8ÿÀ*¡‹rûn;ÏØ´Ã0×sÃ@¦Ì0^È;‹òµ¿ÉnºJX€ ”p2‰š8­¯M󖯆ÖѸ “ÙÍNM­ØÀÚ³ÆR´{ÁµŽÒè Ayµ=xL£\4Dk!,Ìô¦½Ór#q²7»5ýÁ›Š ßÏ>u÷®…`W>Û0뇎t)e¡Þ ?{ì‰@SÉ4tÕïCY º/eÑLOÚÏ–?ÿ2&Ÿ°BÐBÍ×}±bÍu(áó••RŽ¥X _èù²+Âý[.¬júAe¬¬+ç…“ü<µ’)¯Å6ŒSÑòl2GËüyèhá*Œ"b®FkÃWà hý‚–*ÚíWUíp$€0îË\¨þ VíW¨v8ž±h‡q¯#SL¬ü¶±HÚ^¨ªúDŒ@îLn™øg1>b¡ °F94›°¯Ð𖺓=FáKµZ²l¡«?Bh Ú»±$9A„|ôÞ{ó’IÒ¬‡`ö¡ÏW´EÓõ ÅøÉÕX9™fW‡±- ¹ôÖëDšý‹…ñ76©Ò„#ÚeÉž0a’$Ahýw¼ªE«¡%ÿ +yÜe+jTÍNË7AX-³(É!’úuruºý+Ð:M/ò纺iîÇ0ëõ¦E7‚õŘœóƒæÓW`báóè°JC)×aÆås&ÀjŸ@Öä´/“œ ‡#çÏñw›Ðô9ª®.Á|>zÏE»mÎS ’ÍÊH{‚ÔÍhR®(2ä:Õ(þï÷e®/ÍÊH{•ÂDÓ¡á*t(ŽóuCŸPè+Þ„V郿ÌØ†e{&• sY–aMæ›QÖcuMŸ§À,C7Ò¥S^ŠÑ¼eö2UÖÄ¢ýª¬Ž„g´ÛaÉú¢Lç­ÜïËûGÕµo±TjcÅ)Ï}ü®»‚Ú–Û9ò1 x¿jæ0£ôkJÚÂ!ôË‹¦cã Ç–]]Ú‰ö¹•íÔFCû;|™]N=¢ïæpÖø —-Öà쮈f;Ÿñܺ?Ü8Ñ. Mœ™·|Uw‘¬äŸÞ§ÏöhÖ/Ü:ÙÃ…[?ZBh«XÜ™lÏÃ)óXÏc­°£VËM¶?<~ü>{ž ý¸>½ûõ©¬ ý¹‰‡ú™2¥Qìp=í_neŠÀ²Ý:eÊA˜§Ï˜8±¼½r`Àjž§¸3ï† ÷h¶× +2iP{¢&:*[•Ú²í¾ÌN ®ÆûÌ%,«È»²þ ¶Û¯pyF³&^ÿº¦´Q}²¹"šl5Í/ª`Æ—¸i÷© VaZ®`á¨Oï~}*k8ì9Là @‚ò¨´ Lm œ‰Ü0êǵ`uI Zí>›^Ôå]ä¼™`L€ 0&Àâ– Êq{k¸`L€ 0&À˜`uI€W½¨Kúœ7`L€ °¤pýOJu½m­ä–Wœ Ä+”ãõÎp¹˜`L AÈòŽ[ƒ Ò;&Àâ”›^Äéáb1&À˜`L€ Ô-”ë–?çΘ`L€ 0&§XPŽÓÃÅbL€ 0&À˜¨[,(×-Î 0&À˜`L N ° §7†‹Å˜`L€ 0&P·XP®[þœ;`L€ 0&À˜@œ`A9No ‹ 0&À˜`L n ° \·ü9w&À˜`L€ 08%Po7¹9}ê@M׆ƒk7C¥Mã”qÌŠe±O2WÆF‡Ã9wFÆÄ¥1ˬæ{Ø õùˆÚIÅ|–5õ,!e×¾bƒCq|ÖPÚ©Úy‚8—x%ÀïvtîLi?ÇíC½”=ž’r|9·)мKÕÕ´ígÓFIz‹fd’ËuÀiÇ }>}÷Þ|c_~¡ÞÔôÌ-ªªMkVØä©Ç¿«1žÁúáøÞFÿ>5¤ç#útb–¢;ö±¤½IûÇ8Ž;ÍvJávÊÖNeR;¥ëÆc\žöxn vЍøÝŽÁ] è§â²}¨7‚rŠ;ãÌ­Zîóý:õïÙÉ8æÐžâðÞd£¤DG î]}IÒü8È/,+ÖæˆŸ~ÿ«ýok7?TФàŽëïuzñïg¨ˆŽ¿¸˜ùÞÆìqkÏGÌèD7a j ïÝð‚¤‚YŠ.;öïÙ‘Û)?ã`Ïá#hÏïÄ»ÿßloúçѽœˆ*~·£Š³Bbqß>ÔXÈ8ø´ë©ÚK¿ýêEú…Cc:Ö0äKÚ¶h2êâ!ÊÙ' Û¶.g½‘óc¥4Mâ@<Ž=¬§<¸{{ñ÷–ò }WyÒ)y¿,øæÇ’€q),ó½-½1;ˆÕóQï~´ Ä¸¬ÔÐ;ÿ;á¾±NWÂóh§s;UñÎyïÍ+ºzЩC÷¡ÿX\1û0:'Àïv-Ý‚X´Ñj÷MI¾–8T'é¤ÄcƒúuWÆ]wž£O×öÕI瀉C|Æ_žcP¿Òáp=|ý8÷]¨¼ ôQD_ÆñâøÞÖÁ¨GÏGЉ8KzŸè½rÑ{Fï½wÜNUͱì9쮆xŒÚyÄŠ§ö©êJpˆ†L€ßí:¼»ñÖ>ij ,oœàMò#è|Œ›.>U&¸j¬¯Ã[_{Y'âEÜ\®Ä)WÞv÷ÙÈ„eºßñÐñ½­½Ç¡BNõàù¨Pæ8ô ÷ˆÞ'½_ôžq;Ù]²?‡ÔÎS{â¡}Ь"º¡àw;îh<µq+(§¤x’].×sÛµÿ9ÿdn<«ñà·ŽmZˆf-ZÌ8ì°ãš# KX®FjÑ‹Â÷6z,k’R¼>5©S-Æ5…dz¯èýêÜ®¥ÁíTõè7˜«jï©m¨^*‹ D¿ÛQCYó„â¡}ˆWAYÑZÉ1š®w¼üÌãôeÁ.rÄíʳŽWtC´pÚ©·"…Dü‘aw]Þw¾·‘ßʘĈÓç#&ur¢ôþÐ{”Hï½_ÜNUŸ0=‡W ?ÎAí=µûH©.Û§êW„c6ünÇÙ]Œ‡ö!$Ò;œ.LJõîl­ »ê ~ıqÓ¦£‘ ikðWWöÊ|o«+c3Ξ˜Ô1ʉšÏ0Ò¤÷(™Þ+n§jNØz©ÝGjuÕ>Õ¼"œB}&ÀïvœÞ½ºnâQPV®¹}ü1Xg³=–€£—] ”plsúE—‡¤êR«Ì÷¶†÷2ÑãèùˆEõ¢f©Æ©ä}jÃíTtGj÷©ýGŠñØ7E§¢œJ¼àw;^ï ÊU—íC¼5Fæ+1a8m&‚u’ëü¶ùTß…^YÖ0Fã^ç嬬đx¶jÛnÂ%á¯.´Êqwo+cv ]‹“ç£> ·kœ’è}Š—vª>À«ªŒÖsHí?²V¹*`|=šøÝŽ&ͤU—íC¼-Dl=¬ÝiǽºÜL$wÇ¿âyKÄê […Ã!E·ö­ÅÍ›62ãO¿_.¾úi¥ØŸ_ˆëŠèÝ¥¸úìDÛVÍÌGäýùKÅÒ• ï­—Äà‘‰,IpM“½NgWÄ$ó Ú «*þjKÊ›{‹:³³ˆ“çÃV¢¸=´žaš›¬à}¢÷ üÈŸ] ˜Ï!Úý]EEÝ‘ ʵÙ>Õ°ô½žàw;Îo`]¶ñ&(“†Û!ÙÛRש¶û“ï–‰bŸ*&þ÷<‘WPD›x˜B2=K÷«ølÑ qé°Ab@ß®bÏþ1ûóÅ£¯|*¼·\"\ñ†UˆÍË­;œmQ|K£LÂ21¦ûjÃÅͽ­ÊÖ·<âàù¨ÈüϰD&Éár¶%nõ¡àõ¥ŒÔîïÚ³·ÊK‚rm¶Oõ—36øÝŽ ×¨¦ZWí=ñä¨úæqö‰È^¼\F}û‹ÐtM¤^rZ9ÿÚ>©Ã磶«ZüJŸaD¦v3¡„WuÒâ8•(i#j³}ª¤4|é “wÛꟈ_rb‚hwPsqêÀƒÅ z—" f¢¹yÛ¿Âûì‡Øa÷\ÑJ8r›·íï¡¿[·y»)(wnÛJ\8ôhÑ¿g'‘6ý]ñÏî}èW„hÞ¤±Ч³Àr•¦IhiF¶ƒç?üNüôû_â®kÎ2•€¶K19Œv[íC< Êt“è«Î±­¼$“ÛWy¢—q¬h”œ(>øj©i‹|ÅðãÅž÷ÂÌ‚\“FdÁPÞ5NNûòý×Ë_‰—3ó„î9uBÖÐfm.&÷–ìÃÉLæøÃz‰fM"Û«@Õ4ÕóÅ„ëÏíi?–ºq7Œ8%öŸ*aU­NŸ°JXÇüϰÿÝÁ{T÷ôuÌ#6ÙûÛÿÚnŸbSNµ¾ˆÉ» S1æò3D&ý¯ü+W¼úÉ÷bÛ?{ ä › ÁO¾ù… áø®k†CèNÄÜ©-‚6³ÜIGô§}°X³i›xïËŸÄÁ د»u¹ô·¨XËVoÛµ?þöWÌå˜ô±uÐ>Ä“ l}ÕI Ô¹ LZ¿óN9R <¤»xó³Å"û½¯Äý)ŠM›ݾ<2ñ-ïöç‰~x@ãÕ•hÀ,!¹”7Ê[[å¨ßÛCñE³†÷ñAsÝù'E¿hùZ1gÁrsâeÿžÍI—N§C<õæ]ÛõDKLðÐWT<9ë›2uÀWÛG š¦›újÑØ¦ÿ}Ýæreܾk¯Øñï^Ñ­£¨¤ÜÅø:¡{nq¦ßÚrVžQÍ^ÔËN$H^Ÿ³£BÚ{öå‹—>Z N>ª}Ùi"¦1ßBx¥—ø˜C{˜á/Â×ýõœ,ºbe²%Ι¹Ÿ±j‰Šû¾ æä~_»æ5Í`&¡‹§!d@ȾÂmÏÎmųï#6Bh&·QdNñû_›Má»=†Üìnl•øm¸òìãEb‚Ë oDû|‚sÄâ™O·‰·>ÿÁè)¿^j ÈSÇ\†a·Žf²£.>Õž|Mëêù¨i¹k#¾õ Óo¼µµQÿÚÌÃb]›yr^.ëy‹é»MÂ+õ›JúÂ]„6ÿG˜BX+d‹XH Ì-!¹²[D\Z±‹ú•`Ž´ÈGôé"ëÕ}›*V O³\e}Ë+Ÿ,ÉI.qÇUÃE§6-ÅsYÑÄ?{ö‹×>](ŽÃ¨îˆS6•C¤ð ÖÇ–Fªùu¿jžR)ÄccOêÔ‘–‘&^mÜú؉aÒ ’#Çépˆ30±oÁ¯«Åç‹Ûwí«7n55Î]¡Ù;ÿ,GBÖúœ¥»÷åY—êò×zÀê‚sÔó$ÆGÜÍœxIe £† U³ÆâèCºá·íRhj‰é˾ ´¾äÈF™vþéÜ®¥iR³zã6Ü×½b?V;9þð^âÏ¿·ški¯…}Ù¦¯Ý´Ý|..9ýS‹üŸóN´tÍ÷Ö-WX¤Š{ÿsŽvlѸD @6d?ÿù·©Ý}éi¢C벡3+žõ;bÈQâè~Ý̆‡>ÒVa™Br«6l3‡ËȦºO×v"¿°Øl¬xQø­Ëç# ÅyÌ'æˆÍ ¢ÞVÔN±9—zL æïvÓF~óÀ=ûóK1ѳoÎý¡ôï3ŒÚݶ]{D¯Î•+à–¯Ù,f¼ý¥÷ÄlÓ†ùŒãµ'aÓ(øÊõ9âHô…´-tÿž!œ¯+ ªo¡U¿þ‚êä#6çþkÒ>“Iˆå¨¦¾îä£úŠ®P²'Xk…Âo­¶ñdzvÑI‚Ò~öý |^7‡œv™ZÝ~Ý;ŠUø0"×+¢X® „ì]ø²¶\ï.mË= äOæ<¯ÀF-9)öfeq­8ö_Ò$“kÒØ?ÔFZrý{v0M@(­o^%¨ÑŠžkxÏGôØK)z¼h4bÊó‰I7ž_ÚÎ˱*¿hOœ©*?¾Î&è½Ûv>»1ÂIŽV˲ÜAÍ›–ÛoÁšÌg]oÚ8KÔÒ¶¡&¸Á°±¸î¼“Í>Âå¬(Ú-Y¹Þ4ñûøÛ_ÅÜ…ËM3‰½ÒI1HŠ—P}˪EÌZŽC›·ï­JêAoËa hÉk¶2‡•V¼üV¤Yw%«D|©ÝB‘°;v¨$ƒÑZʉXùÂîHH9ûÄÃÍ¿||m=ÿ¿oñ…õ/&ò– G6PÐ_=pµÁ=æy‰Ì©°û¢É—=;ÑrÑ~G6¾-¡Qwã—C 1þìûßÌȇôè(ú`#Z'û×U1k¹™iWF«›Û ­3 Óä¶adá°^e;IÒ{ £©!ûàë%˜®ø´À ¥ç¡Öâ> «fÌÀDÚ‡F6ïÝ¥4NŒb~ïbTîh'÷b2q&Ú#O/î¹G^%Žgjí[´|©ÍíÒί ‡Cg˜:¬\¿¦š9¢,™ST5AÌ.ÈŽ¹ú5r¤d¢U£h”óDL Õ·$%úåŸ;¯:ÓÔÛó·U¡ú+{Ø×ʽ+/ƨ&$K•Æ_l¾æ"(‡”4ËBr`d74ê¢!ÐÏÓ^û\Œ»îœˆW_L3Æç%ŒKí”cœ]iò1¿·ç>“íÖ‰åk6a˜¨™1­T²f2_þøüúŠ¢bŸØ[rÚI‘vQ¤2i†É¤é¥É˜43Ùû­á'nÞZ¢‡&GÍeÈ‘ö˜&÷Ñd»‹O$~ƒMŽeyªrGÜ_áÄÃ/Í1MzNÁPU$n>vƒì ›èSŽ:es`ÙŠy$é [WÏG¢ÄW­±¡ ¡¤­¡å&Ã0f/<ƒ4Ù´{Ɉ¿þÙìàÈć†eGb•žç°ì“}r*Mœ!Mõì/~Ä$œ]¢ž÷èÉ¡ýL°Uå±eçnØ".iÀh9ªèCMì‰ÞÝ2Û‹uô’唘@hÖófý†ÁË~’™)[hµ 2ã$Z,_®;ã„ÃÄO¬³ÞûÚœ„×2GîŽ=yjUº ZUiÑü)šÃC“¾ÉLÂr´_ÄO¿¯7åP} )žh•¯9 —a>Ð1¢iãFfŸiW Yéþëc©Ï­™«ýö!m”kưb“Ú}ÓâþÔA—«ƒ"YÒpЈ!G›_ÊêØÉïXÃøŽG^㟜-VÀž‹Ùxˆ¹«â=ÓÞ4…Œƒ°6­‰M ²“­92ÇØ…Ù¼¤m&GÃZc®8Ýœ¼yߌw¡Å^bšÔX‚´¨’HØ¡YÇoñ– Ú[IÈŠ—hœŸÿÜ }yŽ˜œý¡¸å^ Sv ‹M¥ÑúPÿ&ŒÒóGó&ÈÑý¦•O.;ã8ÿ$T {ÒðlàäT Û þGÒöòg‹#ð‘öö?Á®½ˆ.™“HCåAvñ4a•Vò¹“NÉö°Ckÿ¤ÔÊ&ö˜ ó?L€ ˜vïÍ™ÏýO¼ôñ¬”´×E$L$ŽL>oÃs[°òÄÔç?îgÞ5?~i~J¸îGÃ$§Ð$>»£6ãÏ¿É<1ß\#XßBf©— p¾Û\³ùö‡_AŸùƒ9ânO+Øq°>6X¸x÷‹7r¼ó Y>F­ŠÂ8CŠÁ…`¦-´Œšµ”š•åÙ°)§?šÌ@3wiB¦å.~\‰‰Œ4‡ÄÈ?ÐLƒ–~£?»£åßÒG0…kj€ìÉÁ–j;æÐždz–&AKÒ9{øÀpTÖ™i×›á–aiŸù?ý!¦@SHÚFÒŽg½û54ækL ¹ˆÿi0èÃÏÚ4gçî<¬xò³Ù9Ñlr2 [‹‘á0³†Rí“SÛ—µ´Õ°cû™ÏʘéЦ7®ßjN%P¡ò ‰«4›ýö+Ï€íb™I‘5±‡Ì†hS²‘ŸöÚgæÄúÈdǘ€Ÿ@8æ—ÁÂФr«Í·XÖ»&¢_lš‚*èlhySËeÞz‰uò—æF›E£¬ôWUßBs°&ß|1Vzò ç´ŠßÉ e½eä°råÖÇ– PNX²«7‰‹´Öd0WûªXïèX^K ý‹ !dFÃéc茆äÙ5<4´j9ª¥­k龓)ÐUgŸ`®þíÏ«™ =¦¿´Ü/ž|Š!Óm°§·Ö^¥e¡,*Z•œ}’W6±‡e"ÄŽ ÄŽ@MúªªJNßR& W•Zùë±,wùœbsÆ‚rl¸rªL êÈ´ƒ†ïh'Âöì37¿!D¤vÎQ/'•Í Q“Aý»c½îeXgûGØ)·«PÒ:“}2-ix/æNЈʸ'Þ*.TÖ’†$`[V)be{Ê%Ì'L€ ÔÜ·T~«XP®œOXWi“ŠŸWm0—«¹¡zXYr ”ÀpLì ?v.Z>*¯ Pô„ǜç+˜ãùií“Sixvo^>>¨™C¶Ÿ/^64ŽIˆ¦5]/>íØHE’¹¢Lu'ö„9dL Ö pßyƒ”ߟ¿Ô\ú„ªNÃí°YÈ©'„¹î,m$BC4yª*·»ìí=hvûתâñu&À˜@¤h¤7>[lnkKP»CK’³&§¾‰ëÿ7öJq&&}†ÕYh ÷Sö5—‡ '?Ò(_þ`ñúÜïÅý3ß37µ¡ É6Ÿ&öÐzàiغ–Íl?tôÅåìôÃɃÃ0&Àê+(|šÈ2³Eó0Ë{å_¹æ²_ÛþÙSåzƒ÷“ï~mЄ#(SxrÒ\qÍÌÿ2&ÀÂ%@+¬Ø'ðØ'xR´C$ý‘£%é&ÖÚ Nœ¡ <ñÞ6iši§,»«, wÔ!]Í?Zù" ÚekQè‰=öÔù˜ 0&Ð04hAÙ©8Jݾ˜µIŧЬ ÁÆ$D½ôO1ëëÒ‡´õ4­Œ@eæ`&Z·†3-[+®~¼è‡Ñ^þh¡Xù÷¬n!ÍýÒi ]ûd.ÚÖúÓ…+Ì]Ý.À’dVÇVÙZ¦4Ù†¶Â&­4ÍZ§2Ð,öدUÚ0`®8P  ɇÀ‰3–€k]ô7Ôš¯¡ò4}ϘˆgåU ñ\Ò(”m`¿îBê› ”’k…Û.:HÜ}ÍYæVÕŸ|÷«éOkïÒÐãQX˜ûú N†ðÜÚ4© ­ ;^þÉæ"Ý´£Ý-ýão1òÌcMáüµ9‹Ìtèz¨µLi§¶×ç.Ý‘îWž‰Åü»baÿK¼V©¬07Nù¢¸çñ7Ìm8íWߟ¿DеW>^h÷>àŽÉ\(T°cL€ 0&À¢C ÞeÌÓÆ¦åBÒoÔÙó‘£ÅµÉ‘@LË͛$‹¾Ð(ÿ±>×\~‰ìðÈÖ¸U󯂆iò ¹Ó;³ËÛbCŠ&¦0üûºÓßúç‚!G‰£é†úO4í›ÿÂN8䬵LiëcZË”„uZË”4Óähç«D—é÷7—p²Ö*=ùȃK×*¥Ý}þmb\ câlþE!ÉÚI‚†5|\¬Ú°¥\†KðB;$èŽÌ…~Á¤Rv1#`½3ÖoÌ2:°6Ûf|`?\{&7âÉô‚FÓi†–_èóÑyM÷:,IÑÿ³ëÏ’£íXÉÑ®TKWn€†R7Í2HÃKGÅlI¸~ýÓÅbÝæmæ,ò±ã޵„’™þéØº…yH‚1 KÒ–‘dòQÙZ¦©— ¯}ú½˜<ëqLÿžØ`à„˜¬UZXì3TU-°Êjû-ånó‹ËCZC–ö´ÿyåߥ›,¬ÏÙ) °ñFOhý-§jZH3™e«7‰o`r3ÛMÓÔI¸OCRºIIef2ô±B;¢ý{wÞv”§ vM"[ÏP¦2Tf7&=]]ÛÁNY ¹–i±O3' îÏ/4í“Éf:Ξښæs•Âý`æ˜weçÄ‘xäçíD8ª¡õWY´¸¼ÖL}ª&VoØj–o ´Ë±ñ™dØ]Uf2#ÈL¦_71âÔ£…¦é0çð§ÊL†Ò^µa›i¢C8éÞæ›ZàpLeH‹LƒÐ¶Õ¤9îߣ´ÅýLm1íhåÿÛÚM¦¶»ÝAM1ÑNÐVÄ?ÿYfNam9|$>NÐÇ4á¡%B™ Ù™ðqTXïŽAïS4Ú©¨”êÿÙû𨎫ís·¨w!QDïˆÞŒƦ¸á‚k\;q‹í$ŽËïäKûì8vÛqìÏNOÜm0Õt ¢›"$Šh$ª@¨×mÿyg5«•@åîj%Íyž»·ÏÌ}ïÝ™wΜ9§$"ë)Ôÿü8µÿÐíàùÔ#(m_2½‚¢:*{KXtô³Ð¬šÐld Ø<âÕ.¥Ðà@ñ}üÎé­ƱÓa—üÛ¿.¦ ?º•½T`h[ʵ£Ò竾£Ÿ¼ù)=xÓdº–5ÎÿYº…žýã'"¬ëŒqC(7¿Æfáa1ÿ« ’ B.£^5äËÔÆD~GYû÷’Mdæavä$ޏ"¦§¯RàÈ&ZNÆ‘4~>;/®Æ^>o[YÃs Ì/R˜ c$w`¯®ÂŒB>CcÌd I†„;m›+YÓ ¹’™Ì`ö|²a÷!ájkÓž#4¡Ú'÷•ÂúFU›ùÄñˆæ~ìj+®K¤ØÇOp‡'¶X…íúÑìslÞ“K°»–rž£ñIi(ä° t#òèÚýcÇÿ)®wŸ»[ZOy´Äm(qYO¡þçbK¬ÛШ¢*í_"ʲbt$³pßá#óv¥gE1QnÀâʯ¡}±4$ ,?coÐà aa¥ÀO)Â:–³6VN{å©;„†ÙÝ%®Ëѱ°@îºafÏ;„†ZÞÛØõ3ÆROö¤ñÎgkè©×>¢7?^I—Ø„¨1s¡œsùÂ\¦)JtE@þWDýÄ)‹ÿþWø©zªùXËz õ=ê}N©M×OÍGBÝ©Pø"¾dz| ±A%YuéRnñ‘´”7´Qc_ûhÙÇ£·_Û, $ÚQ¸¼íÙšüQYYq ã€Ù‘XÐÈç6£Q®â÷‡·'qñkäÙûnpíôïB1“Á 0upÜ™ÌÞ£Ù¬Q>H¿g)°;†ÉÎ_¿N¦­i„ü6•Ñj¥<ß~î>¬\ò›ÇosmÃg÷ó®¶×ðœÀ¦%ÎÓW 9\Ÿ¹+aµ¡®ú‰ÿ%ü¯ðÿ2\;ãǪžjIJžB}zŸSisõSóž\Ý¥P´|‘(CcƒŠ²bóÊ¥[B##ÿÅÛÒBrß»}sZ5†Î–XÜ56Š(3 •™‡%I”kÕOŒ†/&tJóΟ;7fê´{¹ž WõTíïäòzÊZ´oç¶vmX»‰¯TõSm¸Ô^;A#X¤¸oËcíuíî¶Ûîûmé™}(;i@hà»Íȕ閃;wž4ûÆ›ÙÕÙõLÃtGh ¿#"¹#üUùq•(tE Áú‰s$š;©Ö./çý5ý‡%öêÙàÀÀ(ÿÀÀp“Ñäõ!°`Sa‚D Ôž%·½²fe†Õj©¬*¯(,/+¹˜qäÀá´”£œ·ªŸ¼òT&ÞBädxý®Ã”¼ç—‹‰Ø¡<üƒ¦qߌ:+/ç֦ЃˡLh’A˜•6™AP¢Ð«ÖOœG /þG÷§äåo·ZýôÄ\í=ùÌŸ/v¼%·=¼–q`…EÎ=Qõ“‡WÉ{LôæÑ#;pÔßÙl¦7°W—ÅAðþ“x&Gxp:|â<¥É¡å[ö oQw\7š†$Ä‘Ù\C˜=“{ËSõU¢Œ'CåêŒ+ìœt&÷QÑBcІi@”ñÐ<£1j÷$™Ÿ‘¶•ìJ ^Ò/ à6#ÛþL ››k±m^SPΧÅDH^ƒ,à /à<£…€B eÈú©€Ê}Ÿ«ŸX‰ã.ùî;Þ–¸€$ÃÜKŽv©úÉÃÀ«ä½ƒ´È Éëv¦Óâ ©F_?•zÇuòNÚH.ð•8 ^,ÇO_¤Å÷Ò¿–n¥Ù“†Ð4öÿïççGð6å«Úe_&ʲ’Eå꾆ÄOÌ6絘HÃëºD¹=fàAÿ¾÷á۽↌ƒŒ™éóÓ̪ª»²ÊÊ`jÆ às i—,îå}% …@óp¯“Ü·}®~ ®õjíyfGÖ1I”QÉŽ„ªŸ<ƒ½JÕK¸HòŽtZ´a ç XófŒ&åÂöÊ/ˆ§æ%Ñüu)´bëŽ`£éc“¿ÃµË&Ÿ$˾L”6*ZéÆ ÷á;h’ÝI²´eAnÏ$™Oˆ£Ôjul.*ýé÷b¢ò‹ bÍr¯÷{utöÁ£¿ä+Ð(¡“¸)M2ƒ D! #m¢~Š ­õÄçjíynØÈõtÖe½$ת~bP”´= IÞŸ‘C‹“SI¾ö¸¶÷­Tbt&^+y"ÇŽÃÍ“ý†³ VȲ‘ù’ø:Q–Xä¡ÒÅZV¶(;Д Èq‡±QægôqnîÅ;£Ã_Ž3û½ÃöÊi2?²zèÐÿÎ  m²Õj¥ÍiG…w 5q¯e0?x Ù“qš& í%&õ™L°UÖ|ÆÃwtÛÍǰÔR`-‡ô:ÂÚõÜýwíÊâï%Œšƒ~“5)±§ÜWk…€B Uhõú #˜ra¼Y/ºê'Î8(Q´i¤6ùøé TTR!¼8´Õª´``Þ71J+ª¸Vp¤á*‹Ehí·/Hm]ƒ/”H•¡ÙôNè÷ö±¬£÷q“”ÈX £Rh”ïnv‚êF…€B@! P(N¢l£CÇψ`"ð“ì ùÃG«)Ÿ#üA´£kL8Íš0˜ºvŠÇNçл_®§gî™Nq1Îc8ñÇW‹ˆx7M&®³Xm´”]³eää ³‘žÀØ?žfMB+· d ˆ}ï²;·î#èæ©Ã©KT˜¸×“?ÀVǘ(÷ìMþlÎbÚe_0¿heO¾¿6•¶6¾Íd4<å*´ÃqW昑“]ûjC! P( f!³ ¸3Ë+,!DÜ¡ô–ŒÜ“~zßut{R"9x¬æÏ_l œó—=Hþg«vràsL€GÐKÍâ´FRïnÑ®4@‘Çܤ"ìöÚ‡\ç<¹CODò«¨d2G8ÎÀÛDe_x :–¡×ÎÔíÜû\&©Ùémþƒø†E¼,”Z+ …€B !`·;ÈÊ®°´\„¥öfñC‚ØÃF8 ìKß¿yÅF…rXè.ÂɳytðøYº~Â Žˆ×•"Y› Û`,RŒl£…t0ë,U0iõ†„q˜ïRŽàWÉ ˆ2pÞ¾ ÊôÂÞ‚îe0¾¤i¶¹Âü‚cõgñ‘îÙ¨ …€B@!ЀF 4UUVòg¿­%0SÚ7Ž6î9Z«GNž§ ìnMJe¦$8åô…B±Ñ?¾qæ"9lÎ|üxb7x–—ÚÛ*1Y8KÌ[ÛüÂ;xe•‡ >))ÙY£ÿÄë_â ÝaÅ1{ö—ÚÊ•ˆŠ¥D! P( &" M/@àZ[BýyÒ›­–Æ7y÷áZM–­‚I§è0ÖJÃ.¹!)-¯¤/VÜKt± ”fŒ$¼O4t½îÇW+ÛQƒ$û’é…"ʺ¿ißH0(:æ¥yçåï.–'÷Å¿pæ‡\²÷|£tª …€B@! h;Hí¦àÈ>@”aÏào¦?)vÊcwL½l2Ÿ<äOå0£0 tMbžKáÁòv¯¬Ñý° ’lgm2»Êá,­­QV6Ê^yýÞÏ$vÍšR^“9ÛíÚÿ;ž”T;ô€<©Ö …€B@! P\IÜZ[ŸŒ€'©§¨7{ˆh¬Àö^/2O]hð–`ÖRßÅÑò& ííu’ŒB_hí™»Hrƒ…õâ E”½¶·³êÓ9ö¯å´3_G7{QÁãÞ.ƒÊO! P(í¹Öâ²Jör‘OiGOÑ_o¤bÖ(Ï™ìtûÖ˜ò`_Ld-X—ÂîíÎRAQ‚Ýý»oËçl¯k÷1 l»ï‹gæ $U™cFüžÏ|è<@?u$%½§%'ûNüÊf¼9<#oõ…?“,‹§×î°í¾ïé¼Uúõ"Жê c½O *Z÷v«Þv¬õŠV;çºíNí³mgm†;Iö¥’ÃâBâì+íz[$Ê¢Qzä…ßŒÑ ÚL¦È=É uÕÔø1_ú*ZP6y/aßog™Ÿ´TV®ýè×R89„²ÁâªpŒ!‘ÿµåÿŽtæ¯GVIá<>ï JÂÛ>/òC8!Ïfçé#z©¨”¿—Q%ûµ¼¼›àóÕôVw±òãÙÎ!AÅÃpCâ¨g·N‚4+âÜtX›xG«ƒ~íz¼¾ü» ®µ¡ðÛŽù@QU:(m‰(k?úÑomQÚÓ&³ñ'L–b™8BƒììP 0›;œ½u…Åbgƒ|GqY…Á`2þö±ÿyå\EyÅ{'wx?9yqÓð6nïœ\‘9:ñ=Ž{ù;|ç‡ýç¼jDÙé»ÒÎ{,”¼ç0mIË¡.¹S,ˆ†rDŸÖtþL½&èþ°`UXRF§x2Fqy}³y/…‡RÒ¨A4}ì@A¢aPéùÓ6ë ‚ú÷ìzMÍžÚR´>mǸ¤Õµ_ë—Y• c!ÐVˆ²á/þj¦ÁÏü‡ÝÞmpBœcìÖ7N ðïÈC‰¢sPVQIû3OÓ®ô¬.2O½2`ʘ§ºíóäçïýi Έ?i 0šÿ¯ÒVõÏ ®nFeŽ5½ïž=ë}ùs—Ùb±PzÖiúúÛŽ?_.Bn–j`¯.>;|äM\Ë9äçáç)õH-Ù¸‡6qgâÞY8rS÷ZÎç½Y¦v˜W›­ƒVý¥æmüìY5;jK!à4ºãâb´T‰BÀ«ø:QƧáÑ—ûM3¼Ñ5&Üq÷ ã©_XWRwhüÐ,ZFö9ެ³£³ÝáøúŸ½ô«ßúû|YU÷; 2ÆŒü7›¨<…Û4Íþ¯|’(K-2"ô œå†”ôr[:Ç £{nK½ã:á”T#[³Äñb9~ú"-Þ¸—>X°æNKäÈJCYVÚåf.ªj6têF…@ãhL;Æ©an4-l|âêJ…@ ðesÑ@=òÿ~óS&ÉoŽÔ[{á¡›ŒL’[ð¸íÿVàóâÃ7W@`ð+÷?ûüOù©hÄ\jq€4;{äǬãÆõö5D$I¶X¬TQYIëv¦ÓŠ­hXŸ8zòÎ$E’¯òÂЉxj^’ÀkÑú=´nGºÓ»ù¤¼Ê#øÒiUùÒÛPeé0\©cÀ[ðßT¢ð ¾L” ýüåYƒñu&}ŽGo¿Vó3wd+‹ÆÀ x· °_Ïýþcsøî€‘{÷fÛH[”ØØË`³UÂUœO Ì-$IÞ{ä$­Ùq˜†³ Áý³Ç‘zÿ{UÀ x·ÅÉ©´?#GåÆÝ­®rC@ÕAn`¨M…€7h¨ã2 f³/so¤òò¾ú±fϾ?8 èïÝ»D:¼yŠê=6ãcnÝb"¨s·îo÷èÑ?Š“ð/$Ç?ÜŒ¼¾ï=º&P|3òÐód˜[T²¹EaQ }³e?›[„Ò<©©¤é·.ì\þË5;©¢Â¢ÈrÓ TuPÓðRW+<‚@}íg³Q_å/.Vn;@/þe!mÜ“á:†–G ×§Ö:¦v|_üÐ@Їô~šíl»²M²Qi›÷7žÔe`íq— sæ<Æ©þ(ëäz›Ãq Ó‡€ä¸Ãz[óR×÷.˜\ ~=l’ËÙ‹Ã–Ô *â·N©4ÉÍ„ïîµ#(¿¸ŒÖï>(ðÎJ®Š€ªƒ® ‘º@!àêkÇ8g?^0ÄìóJ4£Ñ@ßîÐܤ‘ÜNÈÊÊ«ëR(#ç·Dƒ{w£Ù“‡R€Ÿ‰ª,6Z¾e{¤:ËΈFpnš2Œ®TŸ}IW)_¡É™÷Ã§Ç±æ« »€óùÞ¢/×PYœ8j®™uËX¾&`KEÉ'L­B·èpLË=ºOC÷zã¸K›Ìnà*ØÕÝñÓ¨´¢JxqðFþí=xÄ(*©¸Bk¯´ÊW|㪺"<ê¤B u¨ÛŽq)Ú†V™Gñnf™rè$eŸ¿T/x!"|ôc·Oæ‡ßî8$®«âIíG³ÏSú±3t+“W\÷Åš]´ëàIAJÃ9àÔ²Mû]iîH?Á®BÏÑ]3ÆÒõãóDøý”WÈqÉ8H×¢iß9’½u Þ®,îË/*£¥›öÒ„a½i ë݇OÒÁgÅ9“e„Ç~`Îxºëú1´÷h± ZqnõötJãýI#úÐü|½9ड2ˆ“môÇ'‰rPhèõ&Â~’Û(¬¾UlàMF¦‹¿™'_ðЖ4½æ¾ÐS÷1X=VùØs6XœEëSØ–ò½òÔµ®üËké¡›§ÐØÁ½kóÔʲz»Óþž¢ÂBèzžÈ„z²!Y¶)••"6zìŽé—]r‰ëðeSiô ^ºe ×[?ûKòç N¯?s—ÇÿWfGžqloÚѤn;ÆÏ/M/ðß­QÁzÔ›=c#iûÁl¶>ߤ¹47ŒHiG²éÛÜK”² ¸\¬ÃÙþ8¿°”v±-³f<Í6ßH=’Cý™p÷bns!¿„ÍD Ê1yf¡Gø{­ñ {Íò†²)x´É0ÀˆÑÝ bN&l¥O2¯ÔËibÑ·{g:À¶ÓzÅrút‰'Âö¹¡2D‡‡ˆ4ëûŽÜ$QT°Yü€±/µO^ï‰Õ’Û1”ÇÈŸ;>z]eåTþ@A )‡²hÔ€žµþZ…ìFë¿Ë¶Ð¶Éy|Þt:Í=½MLh!k¸x¡ ˜ž¾çzš5i(ÅÅD‰á‘ÏVm§^]£éÙ{o áýzPtx¨¸Ã‰{ÑóΦz’]ÆÕÊÊ+éÓ•Û醉C韼Cü³GòyTþWÊ_$ÚâpEåbÌw8¾I2,w·–Oeizá$Ë<¹úɲ©µ>È ðVÒ «ƒ̱ œÀ ø·~v¯×H²„„Gé~x+=y× ¡ˆ€OpÔ¡ É÷o½†5sI öÈñ}¬,†Ã߇«'Ay$£êD—oN嶬šLy2#ŸM»v;ÆÅô‘®sDù›)m LÐ>Ïd-®ä!¸w {%‚§‹·>]Gï~µÆ³rs^'âØâïK¶ÒËï/&h€AÄaÒ±~×azõß+èÕ®`3Šš8ÔI†‘WØhú×’-¿sO5™†Ld x)ÿß~õ×¥¬<ĶÍN8wËÔ",ÞBo¶Žm¡æ •÷4$àf~&ÂÍÂ,i²‹07t7û’Ú$N,üa­«ÀE *²£<|8€{D»YkòÐMS–íçÙœQaÁ4j hv«F©<tÖ¤a¶ùIä”Ò¸¡}ä-b^Ö$¶sžÁ³L¥ ‚¿nÜ vwfaCùx¡É€{ Y@;38!އ.L⣮`MäJùËt[²fþ \Iæµv×¹ [7wÍÑÏÿÜNYÛõ||EKòh꽨0œ \ì˜lÖÔDÔõC@`~ˆs_ê±7î<~•Gë —Þƒà›ùí_Ó¼ëÇ:öŸqg?Š5DgX™WT}âbèÆkFP¯n1TÎk^ùÇRºÎDQÏ¡Xÿ\¼‰ÂÙü£jþl­¸ÃÐò‹èÝÏ×Ñ-׎$öjpÙ˜ F—æ.©v?xççóçÏcóKW²GÍ?ÐuÐó’¸1}ÍäŸ×ó9w¬P%J¬;Ö“7éi=V5©>x1&ƒÊ™þEìïuñ†anð “R ».gÁ7†kágUJQI™ðÛ)"T("¬sj†?_µC4¬c_N’q/òKc2¹•ý€/ü6…gßwbRŽ©$òxÿËoé¼ýº1âÊU\æÊ>ÅfoßlNÄþ&žHµ--C\ƒ½FöÎÖ¤Á‡,:{X±³Èn&õp öÂÃs˜¼Låýk¼ÃCƒ¸îϸ‡ožJ7OMdÈ•°ÎmˆH"†>|Ë” šH¨}ÿ¸Ú1~Lp†úàÃ$hø¡Ç2¸g 1©\Ìn×Ü¿õúîkÌ1I’síÕ®ßÁw"I²¼Þ:$I–ǰFß×Ö%Ãòš†Žã<Ê-I²¼ë†Êà~ p[”œF~<¹±[¨™;&Ñq”ûʤ󚮃{é[wÛc=ìŠ?]ù襌b³ˆºµîñG²F¹®}àÀ¤¿¸A˜>v}¼|+ýcÑFq]/öøònáJ1‹>Y±"·ÑÜic„}2†/žhŽh ^ø³“Âý˾€oD|`ϰÉF¶õ\)q>?’œY»ˆÿû?GҬȿÅ1ožQ›?¿¦…Ó'Ï«¦" œ"ÉW…ªE(|ŸÇê FåÞF.‚¶êþ9NÍíEi[’¼‡ë«þ&²¡|ëÓÕtöB½üÈÍ—5è2ØL‚LW°23íï™5¡–ç¢Þ¬ÉþÑíÓäåµÖ0ƒ@½ý½ê2BÃûé §Ò"óT®.#{™ôc²4'‰£|˜õS ØAcô²;ÛÃ×í Þp8ÄÄ¿Z‘r 3‡Mõºó¤©P±`ò!0™9q˜¸®!l»ó<’ˆ &áèÀR«óR» BÈßÿˆž‘ô]æšÏA9îŸ=ÎÅÐ=Hî¸a@kÈüu»é<õ uˆÿ‘”øùù |³Ä¼5Êæž§/e÷òéºÍ‘þDE…‰tÏÞÃeiC+ÍÅ·;2™í/®Íå^~_惆¸Û%ÁMË€ž]9Í)ыܛ‘Í6F]…}rTØ>òg² ÍqG—‹à•óšïœR!ˆÊ†Ý‡éNæ@%Î¥\)yMËÖŽËÀ¯úõÛñÚ1ø’qpMîˆ9q"s ç±±eù´¿»ÑÂ¥OÝÎUû{RõD Æ!Rà~šÔ†:bð:$ÅÌ$“ò`bBy¥Ii˜ÅÒE°ƒ¬§`&(‰{ˆä0©!Aà"L°–â^^÷‘=OŒìaþ Ä}dOÞÛΦð"óÖǫĥpF€cx®Al^÷ÙÊm´‘M?0ß%‚<ÀëÈôÑìstŒÉ:‘Ã¥U@H ˆb4kA!Vž+²nGº Èi´Zå®uü±Êûä :ênã§s]DumCÒµSmßÌu:ŸÍC¤è1²s˜ñÁÖ#‘¸µ„F&¨V²9ÊágxÔ0–M0†‹:ßÈÏ7u·M-‹Tkݶµ.R;î´ä¿{ábeWy…Å‚ãµÒ•¦ rLìØkUÿn"X&³åæÓ\¶·oÈfÙ½Àu6É0·€&9Ú\Eƒ`ºÁqKà \/4ʲ.‘X±Ý5S{ÍÙÙ±‘§=¶n¸–ñX–WL%/úõAB?üÅîLx{’kÏÞW[³ ÷nXs=㨣BƒÜÇnâ°Tð„ w{œ_?6WhR l—jL¾oœ2‚f2ÉFï~ü_K6sƒ"L.P^dè_¸ˆ2CCÐPþ¢úüTc,*Q 8솅<•Neö‡pgó¬>Y5.¶š¥q÷xóªÔÃ9„FZÌô­Û˽oæ8a'¹‹µC‹¹€{=íÍô|V‰³ži¶³´t¯ƒÚ>ˆ$†ÆLJdX 0ûW[£~ìÎ>[1£oø_ÀOª”e<Áû/=|ý“ëÃÿ~³•sD])¯‘k+»zËá ˜²;ý¸ÅëQ­t×4´îÏn«`£ŒI{ýx27F¥àÐÒ‘½]\Lªƒi‡ôs‹v#íÈ—”Âf¡¬%+eOGÓx¢»5šp™"Û3ïH?&\öå²c_µèÄDÖ~×Ö]ÇF‡ó™ ¢-âæ¥–›¯º×¶ãýËÚ1=ŸÕRQ™_PXzQv¶çF2óû± æáJ~Ç}cÃÈÏP@'¸=ÿpá&þî»°y]¶±~–õ,¿¯¥…ÿ;\ÀíaïGyâž™©R¬¹‚"Õ8Rc <+ð•¦îÏRPTê°Y­ÜycÛ׈²7ž¹Qy@ãPŸ¸“dy¾!­†¬<åur]Éš–vE‡ ˜õŒ?f¯ºKCù»_£×vŸ°°MYÅù—¸íˆâ§øcãÇôÙ™º[¯ôÛr:˜9|ˆÕ<À¤OÙV¶å¹v—N‘!Â{IWnÀö=%&÷Œf{x% ¶ˆ|Æ¿þŸo\E¿ùÚDžÌ<ܵߘ™“†ÓÒäTzñݯĈܸj'Ï^~çáÆ öÃ÷ÍžÀÞ4ÑZÖ0K¯îé#HÈ«ÿ\*ýÙÖ½l£ ?Î ºWÌýÀ¼”˜ˆƒ„ÞÈ~ïÓÙCD‘=x»Àq÷zõ6&‰ÃüâÞYã™ÌZé¶ÅFy¡ƒ ò-Œç3ÆŠ¹.ï|¶Fœ äkß9½QDùZÖD¾ê;úÉ›ŸÒƒ*±”—•^,.¯ÔàÒöâR¤âˉ\ A²0zÒGJ‚MÅtª ’²x¤žБ áëÂØGrG%@XêBžÈ[Êžpð—Õ¬%d(M¹VKN¶f¹Œõºá>‡Í£%E”Ìh7MÔŸÝçÀß%œ¹×%ÊÀ rЇ!îŽÜÅõ£h#À !s÷‘¹ÇÙ-›»Àü‹Y,˜¼V÷ÿðÁË¢š—btíÝ·ÕZ_©,h@ÝË#ot/¢„a¢kÌöÇHœ,§ÇÈü;×'¥„½ö ï|EÜ4‰qHdxãžÃÂS¼\„p€†'æ]';´mÁ¬E“œÀý~]lá]cDÿì†Iw6”肈—\ì9GÒâz÷¹æuý£ÃƒÉ|°§…ÔÂ$1¤z¼Îµ¯¸ÂF%l}TţЗ*J]Ì®nÏn«¤Ê2Œà {&“š‘üƒcxÝö陯æÄ»•´¼ƒT•»‹´Š3ü?$:_Mýǰ¥¬Ùq**,"¢Q\爉´¼^­º$¹5ž»!Mž'Gö0÷郎’òYC»}<±{ç(! U ÍMjE’›ŠZƒ×K‚Œ5<>ÙöïÚž9…»Ò³Â˜(ר5ù¤K«Ì„Îά×íÔ™¶“D³i† Þ*(Œ'ó#â,H4–º#!e…'èÔ¡ù<7ÈéRÐìFñÃî'³?ŠRcãïL½mþ/CèPÊ3\ ‚Ógø›ç¨g¶<Ê=ô!uKz‰""ºSDx¸Ð*Ãì^/êj“ñäü.>Ç¥äoÂ;‚û;ó805­½Ç³Rø2<²®Œ;½üõqP››”Ò99¹Ä—Ëìé²ÁŸé1ž9Á›l¤*®ðàÅöäÐ긄QëÑ•nž:\¸ƒrW …€O"àé‘=t™;•¾ÛwŒ}&fÿ´~<*ÕEDdõI@T¡xzŽ“$ó¬Õš™±šƒ_Ü•ÁJêºâ“„ØßáGŽ`p6ht|Ø#› ÀƒC9k•+++YãlÿdÙy-si~]<6ŸüM’$‡S¯ÄGÈ)Òkë?ÒlFà1l.•uïKçÎ6Ê<×ËXD§SOq±`rœ›6ùòI|x2OiÏžùœq^ãyEQö ̾ŸI×Í©2GHã~Z"ÓesIiÑ4.õ2ß/¹çJwT~<ÏR0Ñ Zfw¢|»ºBØP% …@Û@À#{ˆ¾ŠE‰Ï# µ“ ``­˜yZµuÅÒ5ûw›yôÕ?‡ÅÜ:C‹˜s]­ù3W, ­©Øwºóg;Û &ÊÕDZeØÅK­rqÞQºtì f×h³õ÷,ùF×Ê£­ïH\ !†9Elì4ŠïM'wÿ‘Ér˜J(këÏ):â]ŠŽš&ð®îWrl“ÏQ¸íù›–,QhxWò½¹_î‘mE”=kÛLTshkÙN9¥·ÛE8ëM”S™(#hŒ»{·ˆÐ@Z²q/•²û'% …€B@!Ð.€vR’dTî•·…øE?´y­)ˆíbóÓ÷SȈAdŠëæzXIA–Å6«”± ?ÀA‚$W²ýy•–Ã6ÌÕvÌ…¹(;ãoè4­98å—ää <æÊ  o@»‘!,5&½Â¬Â?>ŽzöèAG6ÿœ4K>æ :¸î)öõuëw“¸Çy·ó÷£e[gxþÏ‘´”7.]Ê-æ£èÈà])²¢ÖýýÅû_S^ukÌ|E”¥»gŽ¿¢ƒüÖ-q s7jkÈæx©ðw^Ûo^ “nk·Ã&ü$Ï7¨Vчõé.ˆò^ö•Ú3¶}õþk=¨ÚQ(izVñZÏîƒn‹ŽþµÉáÁ*Ùœâ“M÷ÝÙEs("(ݘAŠm¸6Y†ÉEOfuj“mB£\pve|“ƒ OüŠ¡a3þDþ!µ=)‰“mü؈…Ù4ÊÀZeøH6w‰aíòg´oÕ£TUvšŒš…ö¯þ1Ù«Š)~è}âÉ¡IIÞ}è¸vúxæ?7¯\º…OTðð QVD™Að ™Ì®wà3—c/ÚÂÁHöˆÙÊ>Q8 ‘Ð)vKVîÙr¶Ÿ dª§ÇŽÛµ+GçlÚDr#Céõ§{¥¶úÑkOÝæ:Xß5òäKΔ›j­P(¾‹†ñA¾,«¸®O@À[¬,2£¸v¤ìŸáSгÎLÎùÏ2Ç=3'ê³YvjO9˜ È “eAy&ûa¹tj'eïú%…ø3Ïóc’̾–çüB»!›v'À‚6A”ÅÂûÀÈÖŸ¦Üý5¥,{€íµ3øJ¥ox‰4>_’$Ì- IÎ9vô¿«¾ø&4RÎ TñÒô‚7=/Êôâ*Ã/&œçc“÷mû2Ååìð•,¥ûçLtÙ¡ýsñ&vÌÀ>2lj×kØ Ü©ÜK"Œ*‰ |©/‹¶re%Û)oâ2 †Wá°"¦ë¿|¹Ìªl …€B@! h!‚(ïOñhFoðŽ0”­rÐÅ_åä¼¾ uoñˆââô1S§Ýû§W…íÛÝ1vH‚6ŒC¨K?Ë † ‚ †03€¼ŸÀ.9/gelú Ûä²U³.ÿà®4öÖ™$·_vI”±– °‘“ûpÌϯM¸ókÚ½ìA*<—&^áõ/:v”Þ«®è[´oç¶vmX NÇXÜ5Êxg^E”¯ó¹¼"Ž´tR„[Ü‘žEIcŠ;0k5¯°DDŠ’I±cmžà)þŸ­ÚNcõ¢;®ÃaNs9_¨¼Ì§×ü¯å)‚(³ñ<Ì/Qöé7¦ §P(-E sÔÈß²Vó’}1IÎüUö©Ÿ/È» føÞí›÷MÛ“1qæœi6›u*{ac²ç ôwD„k}Ñ©B­S ªÃ_ö2±y{£*-Š2üŸ¢ô5Pº9ouni7» ÃîRwç*¼¸ðG¢á¼f8«±3mlàç¶cÛýÞßµ¡b_RÈK/Ð(Ã~Ü«öÉœú6>%øF6‡½´¾8ë­QÒƒÉé8»+*-§ÎQ¡4´ÏÕ{€è1ANžÍQ“fŒÜE¯•'b¤óP†-ÆnëZ×±÷Ï5.ÓM»Ž;üm³Ç8 þ<5 †jøßÀö]JôCAüXË!qÖ/e•’B@! h»8’’LÇ‹ þÁ>‘’OÁãú;^?{þ‘yy°ƒ…K#¬­å奖õ‹ç/çí5ý‡%öêÙàÀÀ(ÿÀÀp“ÑäÏÇkI\àñ€©1Ëbdcû¶°[׿Î=SdÍã.¯Öµr‡¹…Õj©¬*¯(]¹~÷÷Å"2L4ZÓù>b¤WUV:CÇÉB:×5…æý>))ûqŽæ±Ü³ëtrìØ‘|8µö-úí¸Õ6ü/*Æ‹½€OèÃ]ÉÕ‡»ë¤ÚP(s7Üœu1w>{{š]ó¸ÚÒÏ/<ôßsçp(˜´‘°‰IƒF3ÿ£ûSòr„·a¦!L5xíjÐî¼ÖÐÿÚÙŽÇù„8ÇŽ’.½úQÙŸS3ÿ©²S†Kt@Ð öág†¾yñ~úGœD,ž{¦Ûߊ 5ßòþ×–³|M«h“QH_Ó(£L„XÞõÅY'[é$w(Û#mI;ÊÞò)6LþçpøJ)8ŽÿQ¯nèåÜB»fÑ'+¶Sä–  ;F^êÕµŒ‘^QVz¡Q;(™¯½›Ã:‘·=F”ë+O ÿKΗB»òìa%-C8–°Oϰvä~¨eˆ¨» ŽŽÀ™Ñ£;•æå.g’<Î……¦ý½OB¿'~³g>a2H2ˆz5hp@”NçÁ¡8Öœ Ã.’ü½ë CI2›áò9V´ÑÅ?~®½’šio\Œ›Ú¿HlA’1„lÙžn?ýÕã³÷]gŸuh¡F:Ïký¤G× k_|¯ Ze¼ ¯‹/eǹœ“[⣟­/κ·‚&.çÜ%AZB«·ïZá.QáàoüR =»Ò®ƒ'±CùàÖdoF6 äãÃûõ ¨°}Â]Œ·Ë.ó“1Ò³ÞÇÇðqÊE^RkíÐ Û9¦¦Smî åÿ«uv¤IÖQÁfÊÊ+§Ã'ÎSâ€xäÖ±’Ž’ 3 àˈò·oœìxŽ$™=Äå¾·Ððó‡lBEÍ÷tt¦‰­$ÊBcÿÙZ[öÈ>Æïímÿ„/`JÐwXò¿³èMÌä½Î¼ôëkD 86._”ÞoDbÇöŽªgÝK¸¸²Ù¶7ƒ°ÀûÅPö¡{猱 ¾š9i8-MN¥ßýŠúÆw¡qCÄ}6»VlÞKÿ^²‰c¾)!®3%àJÓÛˆ‘ÎÎ΋ìþîç™À¹z}Yq4ƒc»Ÿ.‹CDÙ¹ã¡_Iæº ³bÃÍlO«q¼lE”uÀ‘ƒ¸c×9"DÙ(뀧JB! h»dKa·8V2I`BkgŸdOõIIýÐí©ÐF¢ÄÚeÀÛÐ*C“ìN’¡Q±Ö^þžqÂäaößñ¶°W¶ÚèìgëŒ?XŸj=ÍÇ”Ô àÎA€3h•A–-/|h[7ÿwÆïùÛ?ç}(zæ¬ü£ñ9Þ|ûÞ_"ʵ€»pæô—4íÉúâ¬{ ¤WŸºãŠYÜ›í{ ûåºöÇ¿~l.Y8l¥Aƒ£ma¢tÅ´D;w_ß”ÔE à"KXK"'µÈ È’$Þù±qJ¿öw™ZCãÌÞ¯èôúÓó“-9ØWR pˆÄ×›gÛ¼_Ùæ¯|Ý0ÖNŽŸ‰+ÉñûÕ4ožù¼e‡sß{¿­ÇàêFÙs³m\üõ|vO–‡ß0eðe©K’eYÍpªÝŠ$YÆH·[-…[W,[ÇåÂ(>B^Tà}™h))<̤¥ÈåÚ¹í‰5´ÈÒ9üOÂy·PÖ*³Ò¢ä´Z.ø<‘{MïñÆ4 ò3Ñàž1.Œ·…€B@!БÈ5â.«ƒV¹H2iì\ᆾ{ö6D’%< uh/¥ù¼/ÀîÊ`7[üñËÆQL’ÿË$öËœŒSæ™þÚ’ÎÛpm¦–ú1ø1>À˜[híµ-&"ñ%^ïäZe³ÍnýbÃÛÎ b8è%ñ¥Sj7ñAZ .–LÙù6"³ Œ¡—ðhWÙÈé)›“?-++‘®U0´!ɲÄü²çæ ßɃÜa²GDj7A”á-ýDˆK?&wÝ‚ìÂõüu»=’w{Otþº߈ž‘l.äÄ8KÌÛûó«çS(@ stâ3ÜØaßi7©Ñ)“Ñ1%aÏžÍM@í¥»f„®ò›×L“¢"ìóÝHrvU•ß´Ÿ½Ï”Äm¯ZjcPQ ÖG^Kn"µÌ4汋ÁÏÿn6l)àó°ƒéUVUøOl{S|‰(ã¹k}„ÛÖ,ÿ.çØ‘ëû 7:|]³ìÍw¥¼€ðn™ö~“¶mÓ~¾^~ŒX»õ&¥aB_µðÇ9An{b ¿Ó&&pˆïÏ^.üýxñ÷§È Åø[i_æivµ·Ci– >Þ?ðÚ—yŠúÆS|L¸Àøgé绑ɩË …@›E€‰¼Â“ÓÿÌíX5ßÑü&õܵÚÞ–ˆcåë¦išÑ¶Ô$› þI·ýOå1NJ?µ4Œøž\Ð ©Wfÿ´â›°þÀíäíË_7<î¶ïñM_$Êø°@äDcÕŸ,q-E’[_³ožóLfåŠ×Ï9È.&_ò<ª-ËïƒVÓRŠ/² °"L6ƒ--4 ðYˆaOH£õ‹¾ú†·¿í7ldïžý ä8ëa&“„Úç$ÄRc²ÙBP0‹ÁX\êçQ¯B"FzeYyqYIQÁÉ£GŽf¦ïÅp*t4Ðk1.¬^0ëxWàÛ ÙŸ¾½¿cvvø£œÈ«Å üÀü“ù`Ÿ ¨…#Úl’$óËž1*ÈÄÞÛ£leTÎOPn7’µ¢Šò*J˜(ÊfÙ…ó±$¥BOldÿë‘üïáIh€‘5Ç‚‡‡‡R“å0eƸ_åñ³/“;¬üíZiý®C´!å0—Á…²F?4$€üÍÞ¯rÃbǸÿ)oKiQ%eŸ¹HÅrIòAš§LÓÇâoU»…€>4"H‹2R$¹Eðµøæ9/Ú/C[Ç„d/FÍjy›Ñℯ’€÷kí«ˆOKó =ØP£&•,H’hKÆþ´#¼dñ9<®Ã"¯ãMßaAA=fG†?…Ò°Wó€Eçs?ºdµBÓ«—/à↷¡5)†&D3F± ò,퓯¢Mfà †ï¬B› `£ø^‰Óü‚µÊLèìL6РCK q’h6Í0±Fe… "~ì£:ÔÎ}K.ÈIGà\ÄDH&Àf&`°éšdäˆðp± ³ ઴ɞûJð â[„ö˜]ZR>ä=ciöÄ!ÊD¨ö¡Z¸~7%ï>D÷Ï™HC8“…@Kp±¯â¿b,Òb2P_ ‘fg£Hr³¡ÓõF£Éü»Å’Æ­¾‰ßõu¬ež ­k&uóE¢Œ"JÒí§$¿`CR[ŠãðW-²KãÌÛ Ëys¯÷—•šžb4ôàb˜oˆOüâbÞŠ$Ù¡$ÉÀ¶ÇÀFj“¡Q†&$¹IÚd¾ž‚‚Â÷僆²ÞRÀúüxè DÛ#" ±¿ÃÁÎǃ6vµ0€'ŒrÖJ ÛeÖÚ!"ȉ 3¤#fi6‚ ¼ŒØv³&ž±!6ÉlnM²0½pi“áíBþ=<òú:l¢øm6;}»3–°ßêØè0züú©*PN/"=Ú $=L:]¼i/½÷å·tÇuchÆø!u®V» Æ#ž8<Éns,d5[c‰4>q¾R‘ä&ÁåÑ‹gý¬*}ņ¸ÁZd¤Ùßܰ!é›iÓ’1RîñU¢Œ‡ñƒ€ É}AhLA”xQÆs@óì“ZåSUUËÇ…?Á壑ÁA“æ̯BËÚr‘¸€$ã#^ÀDÄDYÚ*7œot¾““K2GÌâ²/fžÐ7ˆï÷ˆ2§+dÄ"µ¦˜„&ÜÆ±må Lòc¢le­2`ìhZe‰ 4Ä0§pj“ÞB`fï  @±ÀäçIŸ”®?R‹ ;zä¥Lü†÷íNófŒæŽ2)¸؈¶ùÔI_ß ÖíâKY54nˆ²Ÿ¿hê\½l2èvn1þÎm”𑬉@"†[ú4ÍGr½iã "É BÓj'BCB]\Rt?kñ¢xéS¾sóÝ\˜O=U _&Ê’‚ܹoƒðü$‹‰~¼®(ûŒVyu~Á7×G„ÝËþQ&ú11ý?p'Üêq‡Çq$÷(ÑI’ÑaÛŸ™CË6ï$ùþÙãôˤ§„΄Àk%Ñ×ߦ°&>\˜a¨oµ¿xïëA‰2›~ÃI:‡ËHÄà˜Õsמt=²Q$YõOãš' ó—¿aøÓ™—œ©ÛŸãu‡$Êx~Ð@wBâ òM2z’$C…6 Þô É™sh_Äú™G‰fD„ÞÈDy~ KLäŒÜÉ209Æ Î5Z“Ì׺„m“÷q&sÃ\'<¸†Rº1É“®ã@ALªxBŸS›ležhÈÊçâÁbùDÒÀF,ü™C£ lœ Í¢CrŒÐéNWp°aVæžxqÅÀ·XRZN É šdOäÕÞÓ„>7¿˜ƒäl§ßüh®èôµ÷gVÏ×rþÑ·Ï3]MÆGÑ:D̳ôð‘ŒôI®†ÕGW¿+l•/pñ ü $®âà/³^´nðDq}Y£ìþ¼RŠ54£ îY’dÉ |N}¶¯¼øoƒƒ;?Ê~ÜL‘dõzBÎ/ferÈ&nËúAb#ɲ$Ìr_žobòÕ—k†}ÕHìð$;h”½&R*'¬ÁNYda ’ìœðÒ‘Ze¯°2’Ú6'6Nûmežx)³Øf-±k…"¶û,ñ½a4£’G50!­]ôÝ{ÃXenÑÌ7ÍòÜkGéð2sÒ0ÕÁk&–å¶7zõx¾«Ùt½ëy5Ã6$rSÜ–-ìm¿å¢HrË1ôt ×ý¼âäŠ7´å¬#»yÙ5û£¼êÐD8€Jͨ´Ç1Æ"µÈ’ Ë5Ÿò ùË™ EÇÄ.d_·w¡D3BÃâUKœeK¢Œµ\Àå"ñ¡ˆÁ¼ŸìPàdÍ«DYäY­]ñƒÌ00yOÚ%»k’;Qvi–`#'÷Éãâ…©ÝÀ7»ø*&ɘXúÝÁ»ln•4à/!ÉìRûÔwÜ|,ÛûáåñM&Ì—bµ;V¶Xî¾kÏÌÉi±(’Üb½–©þ«ÌQæ1åÛ¶¼Þ)tÊ‹á¼@Wi+e÷‡–ЩF¬!ÉòŸ#ɲ`~šñÏ~š]åƒáÎM‰‰ÏMMM½$Ï7c ,¤H\ä¾.ë„9s2³–-.c‚ÄK×SãÆEwß¹3O—Ä›ˆl8A ! ,X¤¸oËcíu ,¤H\ä¾Z{§6Ù*´ÉYø¦¤¬Rxqðl®#uxÃøbÍ9:qú%ÄwfEãÉÕS6su\ÜSì°úô©³O¥äçcNN‹E‘äCèÕf>oY½ò†cLØáòïäü[ïBHS½Óõfz`KR‹Šµ49ð¹õ°´´mFÍb„½vÄ*hI9ÝŸ»†5ꈾö›ß°£brMŒ°Ú*½®U®ïq@šT§™Óä€M`†ÐÎ÷çv'Íõᤎé‡:cB›ÌÁp*8â‘“çXóIÂO²~¹tÜ”’x¦grÎ=pëw\TÔ“7„ÀÙ*ëßg8ü“d´¡-E’[ ¡×àöyÆ“úœ¢9ì·Ém=×í(뉇ÇÓ2ï¸eò8û&†}µo‹æØ' hkó ™·Z+Z'Q¶ñ$>‹0»@P‘ö] ÿÀJZŽpDü‚’êùéû·¼ *…ÖFÀ±¿¼üÏ;ö½ ¢H²^Hz?žº.BZWç<}Å»}uÒ¬ˆ²—ßkïNÝæ³Ò$ÙrÃÛãøñLô€ô}, .âªÅ;ž/dnj­ð`v ¤ðº7…ÅeÆa©•è‡Â|”” œ·…@]ŠýýOÿéôÙåu7w_‘äæ"ç÷ÝðBesªL”†¡‚U'¦ê]2E”õFô*éi+WVò¬¸åe‡ý¹í«k#»ˆs•Íáý }®¼Õ†B À$Rk5Q†/o {¾ðçÉ¥JôCxVVq0!¸Ô¬Ž¸©_ê*¥ö€@¥É¬Ûd-E’ÛÃÁÏ iì½ZìvE”%mym0ü•_,ü£tMÖøQ>a÷Û¦¶Â]£<¸¡ëÔq…@{EfÒô. áõB9ϼm‰³ÄÜ3¹¨T;:Š$·Ÿ/€=rm¯yǘšm}¶”FY›”J­©gx¨`¼Éa±=+·}qÝ-%å"—Wø§d«Áà£GwõÅrª2)<‰€4½€ùü(£—«D$QV¦úc«Rt" Hrûú4“i—Û)¢ìF›Þ48 ïº@ÓîÍ™81ʵ–!‹ÅGúÊmµVt¤vÜž/:]Ñd½y¡½s8„ßC¹¨d;(Š$·¿?ó§ÇX™W(žÌAV½¤«2Oi”[é›é½gÏwlX“‚칬ªªøQ+¥±Ù cyQ^ƒ£_coR×)Ú .²ÌÚNì+Ñ *±Ö?u•bGF@‘äöùö…›8ŽÉ§³Û-=å¶kE”õ@±™ip4µ?×Üêx·]Å9\D™[1¥Q®yqj«! \kä ù%”{I·yLM~kßî:LçòŠÄ}û2NÓÞ£§šœFcoh |]ÛD@‘ä¶ùÞ[jÖ[œ×jv{/¹­ÇZMÙÖÅf¦Ñ;&ö«¬ógÞd Jgn¤«8—ír3“õÈmš25nÝQöÊ*Q…Ñ>ZMù…¥.(̺ý•'n¥µ; ³æLpóÖˆëšïRTX0ÅF‡QÚÑ¡õÑ¿»·Š òQ4E’› ]›¹‘'ôeó”kQ^‡Ý§gÁQÖÍ&¦WqÇFøßí¯p+¿Ü§yå›DÙfd²‹)+Ó‹&¾kuyÛFÀÛαCzÑäá 4}ñîëyŽŠ³hÛ`6¡ôÞÆ½ ES—¶a}Çu£èàñ³´+ýš@K7í¥A½béæ©Ã)åÐIúv×*+¯¤~=ºÐíÓFrHè3´)-ƒ~zïõü­}³e?8všžœ—DaA´mß1J=œCOÝ•DÛ÷gÑ–´L*.­ „î14}ìêÑÅûóŒ%ÖµP; &" 3I–Ä&«r‘Ç$I–ë&–´Ã].I±$ÈX#Â\äñ¦ã†czÔ”¯v­"ÊWCÈÃçá*îØè‘ ø«¸YU»Š{ÄÃÙ69y¸ˆ;6jdÓÓîµ…fß%aÇŽóMN¨™7Ȇ“¨à¿6ûl:~†.•Rai™R {“ÍÌ¢mÜV] cåçg¦ˆ Š ¦! qÔ³['EœÛÆ[lV) ÿH•ÅJ™§réìÅB9 žß7*brûÕºš=i(Åw‰¤ÅÉi´ãÀqÀ$ú|^1Ûò} ó´0áÈÊÉå{{Бç(*©€‰ ÷G†ÑÊmèÛ‡èû·LnVyÕM ÖD@G’ŒêÄØX½€7oûþã£B#"¯3ñšÑkÐ ºj19v/ ¬ƒ§E—ðhúY‡Ýž]QZºö‹ÞÞÍŽák, ÎÎJ7®"ånçݶ[¼©ˆr‹!lypg#› ÊÌtà*îùøíÛ/µá£ÐaµÂüÂãDÄÀé¿ÖN••JÞs˜5^TXR.´c¡„°·&BZu•U!‡ú=uî—WÐ7›÷Rxh %ÄZÀ‚D+M³îß¿×Ür”6¥:=2öæУs§\–w•ÅFwLB#ûÇ‹s;Óî°¾ÝÄ>òþcg(iÌ f uÖé<2›LTZQE“ج#ãÔEÁ÷žàNç-SGˆ{õvzUéîÃå5;ºÈùeP>Š€Ž$Yd3?ª¹oßá¡ãæÌúQHXècÜ4uæ:Ö` 2˜ÍÕj ÅG‹Ua±Ø ŠÊ¬0˜ýýÿçG¿|å|eyù{Çv¤½¿eËò.¶$ÌW{· ªÜ¶[¼©ˆr‹!lyp—9j$»ŠsŒfb(]Åý¡å)ë‚ð|á$Ê[_N}‹Þ9¸§' ²ÅbááãÓôõ·)TÀy@ÏX¡õØ« úû¹ßÒ!·Ë9œòáç)õH-Ù¸‡6qgâÞYhhßîd0 žWÒÖÖ'ŽF tà &¹ É@&ÃR2²/­ò_¾L–‡\”}ãcèÄ™<2pSÞƒµÍ½ãbhñ†T:Ãé Ý7¾³¸gÕöt‚G ;ëyŒüí`ô {è©ISm)|H2>xTžàHhdüo}øG7t‰‹‹•]†ôéî;$;¥qZP€?4ÍJš€h¤Ê**i?víJÏê| óÔÿIšødQƒžøìÝ7WqÒV^®¦]v7·p™a4¿X5w*¢\ƒE«nÁU7H9 !\ÅýQ›?=)Ÿ¶ǫùE™@”="R‹Œèg¼!å0§‹Ùö÷Ü0–ùNÉ·­&ŠÎB"¿c9~ú"-Þ¸—>X°æNK¤ã†²¬´ËmëívŠ&©Ý½RÉMÆš6ÚßßDál:ñâC3/»D8y÷Q&ÎêÇÛ½»E R}€5Î1‘!â¾S¹ù´a÷¡½Æ5ÛöfÒ’M<]BqäËðT|I2´Èè¥ÝóÔOŸ ˆz9®K¤ãîƳý¬úWèü p‡C̹àyZFö9úbõŽÎÜláÃÏÿâÿùã«ïpv^$Ë'ðI# •éY<¥nÒͤWqüÏËEL¥«¸¤è[5L/œ¢‘G<_H’laÌŠÊJZ·3Vl=@а=yg’"ÉÿÖèD<Å“´€×¢õ{hÝŽtaº\•´o†°Ùlö·0Á­äÿlšOœ¹(ºkó KèäÙKb’L•zÄFñ$¾l&Î]Ä5ÅN?m¸§ÛÅ…¨Oljƒúõit"ÉxFð"ä^Bîzâ™§™$ÿrÌ ÞÚ Ýdd’̇•x`üâÃ7¹Ù/à5&Ë?áüðN䬬p¹„cÅ®f¡ fêITÚ—#Wq¬¹ùPž©v'w}bÍF÷ÇdAø£ì-·õ\ÃÜB’ä½GN²äaÎ&÷ÏG~æ홞y¶·´€ðn‹“SiFŽ Ëíí9ÕóÔFèY‡ÐšíéW.¥Wÿµ’ñD=H$Oø„d+‡ÞŽëìô¦äüâ2&ÎN³‹lÒOo}ºŽÞýjƒÐîÔÎAí)|%¯š¦Û5Û2Ö(ÊI\Ù&ƒÒÌç+Ž7±ÄàDi‡&9xÖ=Ì Šy‘ ›ãÑÛ¯ÕTÔD4[p9°æÀžÉò«÷þø¹YœÞM¼ÕÑSf§9´ê^¾<Ò²µ2½h~ºÞ]í*îeV)›|ÑU\ fÎ.§jyÍá4 Ôd˜[T²¹EaQ‰pcJófŒÖ1—Ž“pËÍ/¦/×ìd­a,jÊf¹ ¼þ—¼ÜtÅ~ðÆ ®ÒcŸœÄç:ÈÓxâ–’²J ð7“ÉXÓ¦Ô5ɸ~ü Â"Åd2Ðc·]#´Ñrrì„¡5ýáן¾]^Z«,®ƒjC!Ð |ü²ñZ“Ù6_’¬qñ¡Mr`çÎÝ£ãú½†Žåƒ7OÁ9%­€°?s1ßqN£FN–’’\ÈÅÀ8W­±.Þé%‹gò3tš5Ë#-[×Ô¢-KGÝ­pÇÿÆù2©jWqr·Õ×Ý‚ƒÏò†ÓnÚA]£Gc(Di€Íf6ÉåìÅa Ïø/âÆþÖ©#•&¹™£G>÷ÚBk¸~·3ª›2Áh&˜mì¶ ÿZ$¹)Å—$¹)÷¨k­À[O§DEØõ É(~-mòä›ny„]+ÅÜ3s‚Ai’[ãí:óö<9ÝÀsŠcOø$½L«¼î½Ðh¦ÍÂô‚9T…qä„l=K¬ˆ²žhê\Ź’qºŠó¾·WjohÉÉVæ³b,—{oÚ ³ÍeTûʦï9µÉV¡M.e¿È{xæ=¼[¨‰{MÇÒýà7§em½U™¡èÍ™@IDAT`¸ƒ£¶ 6‹ÀËß3ŽÐÓþ4ÉÀ€ù•K›,l“£ccïgÏAe“ÜúŸÞÞE`pÐS\hü¡ùÇ;RUVîvfn²oÚ´dxÉÐMQÖ J}‚«8~ÿì*NLê“®âôI\T4GŽLÆQeÐÅüÂ¥Mf7pì"æøé Â×+¼8(i9À±¨¤Bà ­½Ò*7Sx©Yš—†º«qHœwµºª£!ðÄ-†‘“‡Ùÿ¢I|’(c”4püô™£øŒfp.2ÖÑ0öµçÅ»`E]ç¹=µµˆ2;ÃnkQf~o‚?éY~E”õDS§´à*®&)á*…OS…¢¬Ùua²N¢lãI|‚ÙÅ1&ʵ ?ÉJZŽpžˆdhãÉ\Š(7S·ºÕð.¡D?*Ù§³Ÿ©¦º«wýrS)µEºù ºq²ã¹ìÐüBrªªü¦7câžónç/¸>ýÎ]zÔWJôBxVñ°¶4½PæMGVd¦ÊÂÍ^tˆ?»$‡7=5uG]†xöä@(ëº×¨ýމHrÊ7ß'ƒÃ"`Jî'kŒ?øó×–šö¨eЀ |aa¯ŠÆ ³ÙcDyÑúzüÕÿÐ'+¶qvµå—ï-Î¥»:߃‚iæÕ¯«CÍÞÚèÈÉs5®°uøÄYñuâ…ËFlÿå‹uB›üög«ÉÄÿËW|çó©UxÏìˆ÷åWYt?wiœÑ5:>ç9ËwžÈÎ+„' ÞÒôEWqñ×ßzNš„ð;ˆqÌžÝâÙ.²ÌÚNì+Ñ *±Ö?õö¢Ôn:I²‰üüüx1S÷Èò7j´(9ª,<¢¤É·ÅÓ(ÈßDúŠp–˜79AuC»@ .IéF'C~NE¶HOÿ»“echçvnGØÜsè„+¯Ý¼=r@/×>6 Y‹ûße[hJb?z|Þt:›O›RR4‡¥ÙŽàÐôß2…¦u’á°à@JØ‹žÿÿì}|Tǵþ¹[$­z!@ôÞ;0Ø€ml\p‰[\§9ŽóbÇéÎKÞ?±¼$Nüâ4Ç5.¸`0`° ˜ÞDï Q$P/Ûïÿ|³šÕjQ×îj%ÍÑïêÞ½efî7sïýæÌ™s˜O£÷¦ÅŸí`-µM¤ùæòÍlÖh„úRI9]`²-ÅÄÏ\ïŒzìö™ôÕÓiÇÁ“´ëÈ)221ž8¼¯8íÖYãE^øÑX>2Í ®QWܥѕy°IÜÛr;ÐkE”h€Ó 7WqڳϲË]ôæ™xi'K/À°¾Í" œ"Ém†²Ñ¾ÂÓèAhYð1AŽdO"‘L–-<»gœF‹ÊhñçÁm´€ôàâϳ¿ršÐ?¢y"ðÎÀ[I×D >’<ùöÅd7¤†1¼Ô¼œN· ´»kÌ/r΃Mö®ë6ÈÉL†Ç Éâu4oÐî#§)1.†’x?"×! GFj¢(/Ox£Ù“†RZRЋ\¬|:rò—URaqÝ6{Ú‡î»qÚ÷7gòpêß3Rc)3=‰æœ¦f½ºyâžõÉHyá†ò¹"Ñ íxõG¦1¬[S“¼;Âñ¯ e%fx+m•n€«¸Ü‹çǤ4INïWq éÖ'¡k™,{Ì.ìn<Õ§[ŸXø\ Ÿ¸­D¸# ‚„ŽÔfk”:€äœ-äV"E1yPÒ¾HÓ ¹¨ÈHŠ‚ C^Òã-Ti¯ }'Ødrå6Z4g‚ »ÞŒª‚&$ya™ Ô'#Yà |¡QV¦ͱžÒI¶ÄÃÉÒÁ`Üq»ôÈð½Îfx޹ÈöÊg6úuáÿ.SóÊòÚ{o¤ÄûŸ¥•›öŠÎ{zrœ¸ÆérR.Ç'€ô®!½p·–’è9Žý¥Ul–±•rÎ^dÍĺŠ`GÝ4”OCçzr¼û«>i®œó¤µ®½ŠÏÁ¶n*å¶"äëá*.wüè¿ñ3õ3‘•ÇU\»e~F 0ŒÑÝÆtÏVëþãeÑZY¹ù­Ë>F“xXèökÇÖIæ¹×VQ÷ ¾yšá|® „^|w }÷îk)3ÍÓóÆVž>ýéæƒ”}ä ÷æ”ÅCO7\5R̼¯“ ÿøí뫨¸´RL4жÐоÝéækF‹÷žcy'ʈ¢÷ ÛŒ-b À˜ð¶àîKWù S83]‚ ÇÄD“‡4mìû»§ÓÁÏBµ ËÅå´pÆe³ÜHÀMò6W¹È®AÝbhdŸt'p¾Òô¢‘$Ô¡Nˆ@ã$9¨7 êé»53™8æ:Œb 1l“÷ò7äá…×ÈCÞu^AsüëoßîÝ×Ð>§°OÚ7ƒžzð*g¿äOÿé]qzFj‚X_*© ÞL€ñ})c.å‹m‡(ïÂeúõ·îà«™~÷úJyèŠucù\qrÀwèÚ½s"RMÇÝ’=ðäê?ó”Àç$T¦‰0^‡«8i–pi½î8‘<Ђ5H[íÒ‚ ùT'8˜ëÑæÊ+ó.³oÛæ™±A‹\\^IÝ4•~pß\ñþlë!™Ôë‰Ã²èÛ‹f±½ØÚÆ6\só¯8'P;à]áÞP’,qT»R:¢l`m1k”YC1±1+¶3#©W´“Š˜ü½üázzeé&öˆ‘§ü,×4øIÀø—UÐȱ4<+Mà<+ðEÛÞJºíH’Ûä±l±iÏq¶"Ði›Pø ì˜aO ŸÇˆ\ r+}wOI Ë| f•Õ6öNÁÇ+«„FØÎß¶Õ[÷{“ë‘–Dq1Qôù¶ƒB‰ô9§å¤-Frñ7še)é<)Ïã±3¨ŠóÁu å#¯ æúæéÎï1I†›8ÈÞyÿåXí٠ΥQ®M®ârÆYÌ CxÀÐ=®â¼Fìͬ9‰éT OcâÕ&²L§µë>¬†v*‡—=ÓD2ûxè a›ñQnJxâÃC ®òž6ªZƳ‹’Øè(êÕ-I,‡Næód‡Ó4j`fÓ1„¶zÛzúëÅ~„?½³†µÞãh O¾8_XJkw¥ã<Üff‚‘Wô`û²nœR'ÚÿýÏç´àêÑìovö¤!\ýX‹Õ  sx»€}24Ê»îíÇãxfzA•›rÙ‘?]‘$ßzíx/ø˜Œ÷âÓ÷{[x”ôåŸ|Õûó¯%˜RXØ×8&»Bâø9zæ¡…–×Anœ>šæ2‘–ï*±³æßô1ƒhÚ¨â[äßiñ†y‚ŠÀôÂ_îš;™<åÖÄŒÆòñ¿6¿GÅ|Í”çf5Ú0ÿi׊@¦__ZŠ(ׇJîë³}÷–ãÆd³UðxÖ4Zìvë×¹˜¿m¢jwîá|ÈþJ¦ÂB¹xæðÈá™ôÁš]´pæh:}¡H¼úúô¢›[ØCKüÍÛ¯´“iæã È'ÙW%HÒ5ì²§¥í÷­3Çð=õë‘B‡Øi<\ð4G,l¯v[=v“èU[Š>‹¥nz9GâH=‚ôO‡øÃ®f‘<ѯšC1Ãv9šÉrŠÛ%ÜÊÐá)¥ÚÿŠ7»3~åÝæ>Òx®^éŒmYÏØ€Dzöf+Ð$ƒ0×j“Õ$¾`ÖK¸¥ÝIr[ê¦õI´ß¤;Ø>7$x&ýI²ï¹õ‘dy<Â/bncùÈk¹ŽråQFäñdù u8? ¦m²,»"ʉ°æþ'þؾî)ªþM}Ñ¢´Å‹k)k¨îÁãõ¢&·¶Û(·¥Ø ƒ¸'ì`-n.Ðl3<’¯ûÏn*DDZüE6Ý9g|£¦ °#Kà!tœ3 ˜N4%pÏã+¸nÓ¾Ñãßzà$š%üå^ÚÌû!˜$8mT?±íû/3Í3!ûbXƒ-50ÄÄ÷\µ<@þ@ü ÀÞó»Æm›YX™([™(;˜(#4;&δ—pn®^IJ¬m?ÞAÚ¸@C s à%½…€Ã&9š'ÅbÉŽG%]E’»F=ò.Ó­B1$záüY]±ðÇÎÍL¿¡´Qn™0Ü6®â Z;K„ÚU£ŒB˜ØÆ(à¢íPîyº{îDY¶f­ó/•Ò¿?ÙB7LÉçz5zͰ¾=hÞÔážcdâò ?™(Ò÷•YÓ런&ö׌ÄöÔž Wq¥‘l# II’ó|¯äñ&¿}Ý£êW¨$äNlscþ•£-A’ml›n·ÛEXvDœô%ÊÜ[lú%‘CþêZå ÂØÄÃÅUœ¦›x:¬]ÜÛ\¶«²Dx‡ý|ûÓ F¿Ì4ž\ÿtI«Ëåa)° 33‘ýç’M¾þŠO±I$'Á£Fk¤/ç"´ƒ£, ÎêF[öåÖIfÓžö›"l=eðh¥ScØÑ{-« õ}BªM"R'ݘäI×q ƒÐ$Û¹ z´Éˆž5Ê@†º^¹){¥[wÏ„WïŽ m±°òe`­2|$£CrlZf‡ ¥IRE„a²Š$‡a¥„y‘œör:°æio)­víƒo>ï8äÝä E”ƒ p “‡«8»UÿmMüɽ&wò¸Qý¶íjØMC  ÀéõéÓ§07ç¸ õ61¶‹ ˆÏ½ f·:˜9ÝkË–¢@çÓXz¬I¾Ä$Y8h4h8ò 9Qn¬|ápÌĹ>qën*©¬šÅó—K RæLRß©j_C@@Bˆ/A–¿Åþ‹ôyËÃ68T,¤H\äoµîZ(’ÜöúÆ»&\¡ö4Ñö’·>§­”®ý¡7ÒˆItêÒÈ¢½ûB±áó Ev*@ p¥«¸ª¯qºÏ"íæ¦Á¶ÉÜ£cª q¹C3žëÉ­CÿÆ¡E1ï‹íG„¯eDBšÕpa¯Ü¡oL¾^ ÖpvQ>h½•(B‰€"ÉAû•7ÐŽƒ¹ôýûæÑ ,ÏðÀ¤\*GNåó\šÔz}*×Eà÷Z÷3²Uzâ›ED§Ò3BI\ |FM¤X¿Ê«‰‹ÔáöG®âjK¡} vµ¿ƒ¿Åº"îÕy„;¹m"Ê’LxÖð½J“„Î*39ZÒ#·\Å!³¯§¯/œ’Œèhl ±î¬ØªûR(ÂE’Sxï=všzrÖít*Øâä¹/¿¿†JÊ«‚Uƒé_8±‚Î[â=>bÖoÉ¥ÕNv÷Á†Ò(‡ä`dWq9óÏZÝ4’é{òÄBÎçƒ`äUošK…²fÐ[M”AÜü%’MÊÊ­þ»Õï6 PÆz§×úЭ÷6$¯.U(uP$¹mú±çØÌ莺÷æòÍtϼ)<'£çÌã [ŸnÞ'\£Â}(LþzqtÙoÜ1‹àåé͛ټï2¥'ÇóuS ^¡`ÆñÓ—> ;æL¢•›÷räØJ?¤Ý2kœ˜pûçw>úþðŸUdbÍÕÿ|çŽ6•¿¥W§ý_ü—÷²ƒo¥ô~×múÔ»/”J£J´˜\Å4ýe™¤îÖ—Û!Yƒ(KqZM”e¾k OÌ«°Z©šýÐ*i;Àž=â£#Ûž˜JA! P4€"ÉMÔÂÃÐ"Ø‹FôïIN—“öŸ8ëMáãuÙ¿þo_DÃúyüðí¶âøË7‰×O|e.e¦%± Õ-Þë.—VÐ[+7Ñäýé–ãhÃîc¬µÎDyâð¾â¼[g§¯Þ<Ý{M(6`—¼ë“GÈe¯ÙE'dѰEmèþQn™°®âx,½ÆFAŸ‘3~¼ÇµBÊ® eOFl©Üf¢,M°NŽ1s—èÈ)5?0U gV÷$azˆ4U …€B >I®•Öï+çÑÀÃ'Ïј!YLˆL†{Òöƒž®Hõèé‹ÂfžžöîFUV» »•Õ6ŽV[HÓÇ ¦8V’L5€ }¾\Rî-̘ÁY4{Ò0šÎófz³oÿù焦º›x@`£<]¯†NtÚóéw¨ªô”ÈÒhަq7ý‹L‘ñ¡+B=9)Ó‹z@é(»ü]ÅqÌó'¸ì†¢üÌ»j5ʺžØ–<½$™©Á[@¼ÅÌö´í>z†ÆnšGÑüMOŒU6Ê]¥ÒÕ}*ÚE’úÎÃ'…«ÉOÖï¡O7ífeUVar<¬_­ÝyX¼Û×ï:*1JqôÔabñÚ²ÞBEs4̳%”œàq™ÅDXJ\tT»Ï :ºé7téÌ—²H4êº?Rlò ïïöÚPD¹½P¾íæ*NãÉ|Ì–…h­×({I2›*Ãæ án±N³DOù$‡yî›Yû0¶.“ ð;zúí—.p…I¸Ä¼Ë€ nT! :Š$b˜]ôé‘JCûzÌ*`_¼jË~ÚuäM=æMEå‰wÇø=Ý”4r€G¹Uãò{_¹^h‹}K‡4 °iÉ?ö1Üåµ&¥þ¿KÝúÏ ‹â)Ó‹°¨†Ö®â˜úd#nü»]¸Šk}‚ͼRw»½e~äÚdz-²'RÂÜzBÝöˆc­2GÚûhݲ;\Í,•:Íà¶äË=a¢aYi^Œ¥_ßsÕ¶B@! h-Š$·¹Æ¯+,.£“l>1oÚHºeæ8±,d»áAYì*xÍŽÃ4 g:]=v0e¤&phx¬~™éËZâ›ö s |äœk<Ú£˜ø…ʱ3¨ŠM8$±nÖÅ­8©¬àOÞ{Ê{ezßÙ4pʼ¿Û{Cåö®äÏ®â^¬M&4®âŒøQöˆ¦·ž(Kí&ˆ²ÿAL’™ÜõˆvÓÅ¢2ZüùN™•Z·Å®øÎJâ ‘_à,1oARêT…€B@!P/Š$× K@vng2ÉßCLâóL¶;rê<•VTQfzk—OÓï^_Aÿý÷é©?¾C'ò. ÿÇÝ>“ΖÐOØÃÅwŸƒÞ[½MÌWñM«¾íK$Í?„ÏßN?àô¬6G}§d_UÉ)Ú¹ìr;­"½˜¤~4êú?$í@%>z÷@ÝQL‡]Žˮâ~JWq:ˆ2‡è¢5Ë=Næ…iµŸ ¬­‰ rdd¿x‰Œ¤¤èjª´Ûhß î¯ÜF‹æL“ü.W?ý€æ à60=†z¥%\/pÞHM= Ú+ꪑëÔ!…€B ‹! Hrp+üFv‡Å_¦Ä“ô±—Š3´fÇ!úì¾ vÇ6»ƒþöÁ:Ú´ç8 èÕMLÄûïoÞæõ eáï«G4zù'_­“ì·îœ]ç÷]s'ӭ׎ç}Zо»ÖŠ ´}É=d¯ò1E&и_!S„džºNÚñ‡Ò(·#øÊº=\Åé.ƒW£Ìlª©É|^ÂåÒ]UV‡ÃûHÓ ¹(&ÈQf¡v£"£(=Ú@)f» }/-^ËÃPž*PØu¶t€Ï_Þ[ËîƒÎQŸ¤Ô#Qà \/4Êõ™^XíÝétV׃Gºªç¸Ú¥PtAIJw»ÝåµJ˜W>yžN¿$“ø–¹–$ûi|6Ìð´ ±WÑ&ÉÖr9ˆÑl¡ 7¿FÐ(‡›(r¸ÕH+ËWqv«þ#Wá:õ¸ŠëŸ½¿•É5y™Éb)wTyṴ̈«¸¸&/¨9Áíp””V‚|yÕš03›Í‚ ÇÄD“Íj#›ÍF§ƒÒ\•d&+]fjþò‡ëipV7ö†Ñ›†ôéÖª‡¿¹eí(çÁO2\ÀÁK\Á¶{`ЉŒÄS,c <Ññ¾ÒôÂÿÞJÊ*u‡ÍæíüøW¿ …€D@‘d‰Dû®GêM.—Ñò l‡\ZN‰q1B |5»{ gqÚËiçÒû¨²ØãæN3˜iìÿ¤Äîã²؊(‡eµ´¼P¡v×»ººÂÓÄ™–kÔÜq±óåÕ6­Š‰0\Õ@Á‹ž©PKe™Îðpp9G‡üxÝ.Aš¯8Œ®8”Ûdp†uC}2?tìQ÷°9nﺗå äZ3ÇP|·ºš?¼(j[uêÿËÝ¢þgqÝ_;a(r) Ⱥ ÷´t·“v¯ø:Ÿßî-êˆÙχ8o¡ü6T+õ¤#ÿ„«¸ãư«8}¼«¸ç‚uOš®Wð R¸†;{ö,´ÊE äå%^ë>ùpÿ€Qc.ï8˜›ÌDÙËn½Ze&tn&xéÖ¸zäT³‰ 0lVAD"œNŠãÉ„ ÒÁv]ÓÀ=µënàÒ+&B2Æ6Ý11¡IINLHÛ0»ˆ`\ëÓ&ã&¸.t·ËU¶Ç–üL ‹·Îx[I  ¢-B{üö§[…Íàà¬î4êpe"Tƒ]­‰P}¸f'­ã ÷Þ0Uh›€·C>˜s–ÞZ±…ŠÊ*Ù<¬;ÍSu_§Þ|ëÿ£5Ù\ÿGèÞùSDýûrÕ¹Pýè¸]6&ÉQáéuÞûrõ/¨çÐ;½¿ÃuCåp­™V– ®âÜný5q¹®}“5½¿cMoPÔH¬ÄeU®‡(œÎγ!¢Œâ@S)HØ¥üóoдïg¾á1=Ze#Eê¤Ç€§Á,HØ#› ÀF5k¥„í2kí\nÆ™ïWœÛ³ü € /£¶Ý¬‰gl@ˆ…M2›[@“,L/¼Úäú'ñ¡œ8«9q|ƒˆpèX$YF)id—ËM_l?H³ßêî)ñôë®QrüpÃd"DÚÄ‚I§KÖ勵¼ûÝ>{Í™<ÜïìŽõóóméƒ/vzêþ6U÷õÕÞõÿå^zé½5t{V@åúF»êKGíëx¸ì•”ýÉCTtn«·ð&}úŒyÄû;œ7QçÚiEÙ긊#=+÷TÎ-œÌ‡­HªÉK˜ÃŠYy8Ñat5f§,µ“ `Îõøöm}ûÖ¼%=óЃï¬Z¼,åpœÔšbšpÇv¶V&ÊVLòc¢Ì^AéjZe‰ 4Ä0§ðh“=ÞB`fïèh‹X`rãõ}„àFŽëÀív:Ë6­Xºšë¦3X$Y–õÆ»”ø# µÈnI^ÊÄoÔ€žìÆp|ÐfŠû—¡£þF´Íoß1“ÝfÓûŸïàÛЙ, ¿Â~>Üïmuÿ>»cTußüÚõ¿ÈSÿè`¸u7ÍáúÇ{J*šŸš:3œpXKxâÞýTzq¯·˜&>A&ßû;Ü7Q÷jaùà*.wüè—YÉú3\ª»ÜßåUPˆ2Ïâ«ðŒÐ³ ÒånŒ(£(ÐN‚€Ù‹Š ÊîÉ~^7ñ7¯/Û¨?zÛŒ:&’, BÈ*eÆ4‚¢Ùþ$YLô³Û=ýj옥6Yšk ÃÎ&ЮC$QF¸o˜¤À¬ ¸€nõ¸C‚Œý8ÞÐÇØÃý®Më^¯ª*çº$[Í"ɲÒ(ðzD’dtØöŸÈ£eö ¢tïüIõœ­vÕ‡:È/ž_øÁÙ¬MèPÃðh0·@ÙA’UÝ×WË ïó­ÿ%kwS·¤x9°Wƒï«†SRGÂ[eíøø^ª¸Ì“?kdðôŸRß±_—?;ÄZåQM-+¤9‚þf³kAwÇêÆJY2ƒN0½hL¤éH˜uÃʥ㒒^áíG™Æë,˜®IÍ2ˆ tc’']Ç ‚˜`ö¸G›ìeµQøhI²ÜX!:ú1`#ö®2°ñD44‹ȱYh™=.€Ÿ¿@“ ’¼óðI-çоvoä6p+âY uí¿"Ê B}‚Q ´ÅŠÊjz_¼x¡I®ï\µ¯q /(.§·Vn¡g¿¾Ptú¿"<Žâ=„2ÃÔ÷ ¤uÈú‡£À èÉ.?-˜Oѹ&y¶™Ž}UuùYÚñÑ=TUzºæF4~ío©×ð{:Ü)¢Ü᪬é÷Ú²÷\θÑï3‘½góÔ8h•¿Öô•-;ƒ9[…ÔàºÝ†¦4ÊÆù‚,²Êÿyííywß6ø@þåbýî¹S þ6Ëí)Gîcò;eA…M(H²gÂH ¤«eÜ«GKì±ßÆGÅÈ„‰m&Ð i‘a“ s h’sîûxÍ’ÅŸrrèð@£Œ5êu„ºB)ñCí æ6ÕÀ„´RvÑwÏõ•¹…NÍý‰ò£…txŒ˜;m¤h¿Í½¾=ÎC@Y‹Ëªèl“,;ùíQ–Žžgúßyˆ]ôŽšuô{ëªå‡dL„#ïA4ƒ‰ÃRÿ‘2ÞÜ!!QD¹CV[Ó…öuÇ#ö÷æMúÃ^[¶5}eóϨ£Q66©Q–DYh”9³ÈOßyã݉³®ËwOšöÍß¿ñiâˆ=õ‰Ãûi#d ?ËR» â'<_°½-&ïáC%µÈ’ Ëuóï ã < B«\£]6rrŸÜï{gð“ pðn‰{n§£|çúµoïݲîàPˆƒ5_²"Ê ˆ¯ aâžI2&–n=tJx8€Í¥’Ö#üà)b»ÔÃľúÚqëS앲  ¬(³ªû¶ã+ë#‡^ž5~°·£/ßwmÏ¡ýR@{ñý6ùn·_©‚—sѹm´gå7¶Éø\Œ‘4zÞ_©[¿ÙÁË4È)+¢d€Û+yá*nüè]¬Ç¦Ån¯‚F9 ®âjÜÉ[ä<šÒ(ã<¨~a§ [Xh/Ùû/w¬ýlã¡íÛŽL›ãvSv“¹~Aê±ìÅ!)Þ¢E±9¿ø¿lüûŸß™~û<üË{E¨pDAD0ÆGs¹œå§ŽZ¿eÕŠµÕÕ•’—òùXÊxA nPGÊì‚Að6Ù)´É¹ø¦¢Ê&¼8øŸ§~·xÃxgõ:u®úõJÛáw´”# ó§hùª+êEõôôöˆRH³2Dç¿#š`à[„å4ûGÀl¦:^̓w£^(:ìN[Ua©ÅmâL'õÌKö¼djº(‚Í4¦Ô ×WD9\k&å2h†?ñlâ×DRApWÇô¢ieÃW« ãY¢ iee™û³÷ß~/2fü ~ýŽïo0̹mÑtS•GDžs qJš‰€Óå´Ùª«K«+*ŠN;räØþݧøR˜U@k R ² ’\R³ ³ iŸÜI_å|‡­|ü„6™ƒáXYK:4&¥®¤íGày0÷,eõHõjÛžràRmeTu8\‘’¬ÿÃ'Ï‹úInÈ„,°9&5´ á5;ŽÐº]l’U^-ÚH{ Ì5?•ˆèTÂ"ÅÆó8Nä[iwî>Z¾i%pÝécÒÌqCx.Bí<š†”=2ö^+¢ÜÞ5Äüƒî*NÓ<±“ù˜õB;ÜñÕ* ’ÌùhÛ‘=Ù{¯ÎÏÒ#*Òàâ—O©ÛuòÿÎæÿ®&qyMsòêÊçH¢ ¼±Hûpt6@ˆ¥©4É ÌJ›Ì 4&’äâI|avQÌ‘×0êÿ°JÚŽp©¸\R!æÀÔ*Ü> ²  Œ(«ªû¶×»LXâyBÈo{¼i÷¯˜˜ŽNQ¸Š"ÊáZ3(—ÇUÜØ¿q¤»Ÿ"¹@»Šc‹!z!ÌÊšK”q>ˆ4›:ù$Κl2ʼnž%ÙóÁòꥼ¿˜E’„ˆÄ$u%µÉ Ê Æ ËU5 °ÇqÔ…’zÀÇH¡5‚›Âò*+ÅsXj%Ca¾K*ªÎÀ;Ü>ž²  Œ(«’À"€ç©”=Éà‹Šô” ·6à{Çè8¡M|ξ´áâNòE§v :Á†8ØÌ+K7±éÒp¶IÒ¤+ÓÚ”ÚgKåöÁ=d¹š#Ü/³«¸gØhŠëZŸ‘3~üÈþÙÙûQt'˜„_-iK’À˜ùnƒ¬Uý¸WæµÝ"̉~ YÝzÁÒâÒOjΓDY®y·’z¨­–Zm2ˆ2Ì*Dg„× Ë0ÁÀoìÇqdy-o*ñE“H5D¾¼ìù">º%ÍÞ75µ]‘Ù¸Ÿ²º'“–PP‹gʆ“É"Ê­¯ãseŸ{6Ÿ‚«8þV`¸^ˆæ¦¶´%|u@ÜÜ=" ߆FÁ͔ޮÓkÿ9[x–÷ûj’Qˆ7ùO~ɱ– ˜‡\ä¾&R'€{4[àGð|Án ¨ › °®Å<\´‰¾m@u’‚Pï5Iò“&ž1ìÄ­­·•3iÒ@rÚnÐùáÄtÖQÿ‘Ó„ ­E’%Í[ƒ KQÄX"ÑʵühãC~+ <¢¡ ²~øÖ¶Àß·J±®vLC6ì9¦Î rd°™]ÇÏÑ”}„§D™…¿åp1ÁÃÙ¸]•D8#Wq¬˜-Dù”•{*ç–¶–—Yk L/j‹á²?ÁïHI†?º}ï1>(5 XKsµn¾Ø…먭ù³%?ÞáL’…ïÒ³âQ¯×{OЩó—ë=.;Ãßp.꯰¸‚ ŠàññJA”/ØÏo•SÂSð¢’ÏY¸•刂¢”UX;|°¡pyÀ#F%·Ià õvvœÃåYk³0ܲ*OýÃUœ®ûLæ£Oæ«SÐÓÓ§'9«Ê”;ýAn«µB «"p® „^|w¸}hXb£"9jY7ºãÚqBãR.ÇÎ\¤·Wí _~}™Ù±¿¬ßuœ&èK}z¤øR¿Ã ß¾¾ŠŠKáÉ‘„ÏæŒÔxºeÆaÛPQ?Û~HŒûo˜rÅ)%L”Wo=D#dRt”òÿ}@Mìðe!( |¶:ØÐÊÍ8\û1š4¼/Ý~íØ:¹>÷Ú***«¤‡ož&4Öuúýp2™<‘WÀåèîw¤y?[ò.8ÁïžéIxÚ(ƒÍä0QÎÊH¡(~Ï…“m¥Qn^{êgÁU?Ý5vÅWqm¹1¸‡“׳ µ“ùDÎêò¯s1øÁ–I{ûdï]+ÓVk…@{#ÐÞš¯ÌDߺc͘0˜²Ÿ¦½ÇÏ6ÉÈþ™ôì×nª—$7xQ˜hoÜ}ai¯²L–Eß»g¶ T¥¬Å\µå o±®Ø¾ëº „öÒY¤½p¯?˜]`’!‚¢´5à :*qí{y‹©Ú樭¯uö¡S¼bs@¼½ÖI×ÿùË·°^E/2Ø F<à£îÃi2gà»ÇP¥ í*ŽÇõ½O4w®[Ý–ô™3M9åÅß‘·©+m²„B­Ã|Ðj—Ð,5)–2Ó©W·d¶MꑚHÜ8E ßÀþaóùœ”ÄZ8s¬˜™Š;—8‡"¯–äÑ劎¢ŒÔ±ä],¢‡Ïˆ"£,ϱÆšãì5à2kžŸùêaCûìqâ¼Ï¶¦-ûsYh# ¾÷»óÐ)Ú~ðå_.¥ÖâÁðmö„!4qxÑ!ûbÇQªª¶ÑÀÞÝè¶YcÈ"-´ÄëpéGAQÚp¦kNOž»D9¼ è™&nsw€¡]Ý}4¯Îm£sì_‹Ëé£5ü\³ÉÂo_û”sÝ:k,ajÙ†}t¾°„RcÅ3?uT‘^cïá÷?Ϧãy…cìÞâvDèèé‹4gòJOŠcû>§ Î’b|ÀÓžs+Ý¢Ò**ଔU[™}š>óÐ|aÒý¿)¯?X³‹££™é‘…Óy¨?Aø‹•שu耽ñÁÜób¨jÚ¨~ÞÌÑAzuéfA`çO!ö£MT0¹…ä_*á¨q‡9TpºnòP1–RÍëÃu{hÆøAôÔsùL4œÉHrY¥•ÞcÒ4‰·ïçNÓ&ÒÛœ”—vɵ$ïÐtÚp±†öË`_Ìç¼xî;qŽFðˆ¯4TIñÑÔ'3…âã,tל 4m´‡ ÇFGÒ6­ùæ×Ððþ=˜4ïg-µÇ&½±w‰ÃHg¦'rÇk2ÝÉ£{剰ÜFfÍcõEBC^Æò'´ð:ð‡[ø¨÷÷£Ý¤~º"ʇ4¼„«8ÖìB)ùÁ·ØÕ¶¶Ä#l”=º¶Ö›^èú÷jÓ1üŸ¶xqøÎ4‘Uk…@X—}”IòÚÌñ†°rjB¬7wD‰û&›eL=à »Óv_…a⦤Q3éöÙuí!¡ÍêÏ‘²`wØm–áFLö:}¡ˆ&³ e¬%‚ÆÍb U©×^Ö›¹Ú:ÇÙu |Ê&‘\Oƒ¹þ}¥ûŸ½oþd=Hè|±ýj¡ ѰƒÅ„©écxŸâQ L˜Ø+#LFQ÷ä8²±7È‘Sù”ͶÌ=(‘‰Ø`¶ÝŸƒ¸O][¤éE 4Ý.§[ØŠ¨1¿@} Úgß©u@n¨.b,”Í`ÁsD·äxq]B¬…ßý)™ßCûd‹ÍEКzàâ«ÇdádJfÞíáòH“‘ t´‘6l”‘¤¡|ÄÁÖþãø¨GgD™^´Du]@ðuÇþ¿¥/Zô{&§ð$Ñ" „éÅ©‰c¦q0aPÇÚdkt4½Ü¢B¨“]Gn™.L/ð±{ùÃõ¬ÝÝãÕüb·¡pÊg. t2yè[À¤1@üIÚ´/Gø+ÝÊñLˆ!Ð`ƒ ¼÷Y¶ø¸6¿¨¬ÎõÞƒj#h\ŤgÞÔáo&ŸðˆÂß>Ü@¿`th!Cú6<‘ õ߃Mv¤À|G Ìx,lޱ|ã6ßI “ì åᛯ‡Ÿa¯¬Uþó»ëäéµþˆj÷t©-©QÖ 0 Azƒzuc-ª‹r¹Ãz 7Ÿ0¿À[ii]9y}<¡Â’ 6½ð<ë M½ Êx$¦§/\¦x&á°Žæg¾!i(Ÿ†ÎoÎ~ŒjÀG½ÇãE­÷“öö§ÜêáòæÜ´:'<€«¸œ‹ù¿ãf˜æã*îÖ–VÓÙrÍEìû¸Um‰OÊ|uÒÞÌØ°»a¿VòDµVtQYˇV§ó‹¼ÔçÙBLg-!¤ˆ‡è3Ù¾  &@RfML¯²UØI^3vwö<4—¯±Ù†c•´?è ogó ˜B@»1¯ôl"K‹úÏ>zFþäGþÀÐ9Ì,@Òøáá[®"tº ‘‘&¡1üáƒsåéjÍxÉr€Ð0±6x(×é>6¿8Äæ5wÏxEÊ-© ð÷¥lŸ<°Wše‚ ίÿµB¤ÙÔ»?ò/•ÒÓÌ#LýàË+Ê"w4–<§5kà+|Ô³YŠÄº5éúezhD;@zpÇÑoþ&‹ª»Üß•Û-YsSöš^p¸Å¦'§ŒéÃùÝ*ó4ô?ÊmµV(j(d»â³Å´•'eb‘¯–°ö¬+·^„h¡Þ°ç„Þ•gnÚ“#ÈL,ð!•¤»w÷бDò¢ÃÂÜÂÎZ¯#l­$ô”WÙ„ÙË6}ÀÄKæ´DO¨©ÒôçIbp/Ûd˜àl÷±3ÙÌ£ †öf0µ2¼o†pQ?»ÐdÃîùÔùKòp—^·@ ì‰wpý Õ~™žI}¾é7Vé<Évêx¶a.ådml×Ì"ìlÂð%» “ÒÔ»dLEdcÜ@Ñ.Bxëí’ÕàèhËÀ¢KÑFgŸä?¸ww¶ÖhäÀžõ>[ÕÅ0ž ¸ýàiúÍ«ìõ‚Û|/cr&|4#°Ìö—Þ«[’÷>{LÙŸÞýl'ýüoKE»¸zÌ@ºÄ渲›:²Ÿh ó»äÞÔh>Þ ;ÉF‹ÉM'¹ï.pwbÜ舻ǂVùkØn®Øô‚í”…0an‘F¹ðª«âJ­•"gˆÁ`øƒØPÿ / .Ï=~›÷·ÿܸù f¨ËYê86iXš0$‹¬mòµe9Þ¸7‡~Ä®žyò4‡o®ØÆCû§!¡~êþëÙ-”Ç $Ü‹) -ϰ7Іäµ¾¶áÛ&01ýP·0Ñ@К™L¤ o¯Þ)&ìÁäíA.~÷ægì*ÐÀbûìÆRÁmsiÝPy:úþw š”ñ`ŠÙ8W;˜m),) zh÷¶šÓ1ñžÜĆôP‚Ó`öò«oÜâ½Xû×kCuk‰¢ïÜ9ShyqdöÄ!4ƒÍ¨¸yˆ‰xÞ„y£¡wÎA"˜ÚÀ \}AEn™1šæOÎgj5®†óAz’@âÞÚ2)¢ÜZä:Áu&£ö"O¤D™Ÿ)¸Šûa¯-[j›¸G·ndÓ Ï@>Ò¢¶Tf­|˜U"ñž,´Ã}¶g¯ª·;ÝDÔa…€B i@" W>¢nÝM%ì£ól÷Š€p3ç+Š û¢Ñ1·};H¾wPÌ.·‹`Úr•ÆÃù¾Mvg•¼©£3­víÖ×<Âß°:Ã4I—˜(û ž“ö$n Õ…¿kØ>7$ ½ äùõ‘dy,‚;T¾ÒX>¾çµd–-çöÄÚ·ÌuïÚ÷ˆÚîôÀU܉ñ£wñKb7Hé*îùæÞ¸ÁÄå+e]oþd>ýÙg 9Ë>òµ‹þ#kG|¬8š[užB@!ÐZ†ñð:&ð}±ý—WR<»ššÕp᮵iªë:ó§£õl»þÎêlÛê¦n)qôØm×PŠëÁŽs7Í/©>~¼9WsßÌlì!»æñ¤ô+fC^ŽŽ±¸n^ÔÀæ'«ÎìÄ(¢Ü‰+·y·¦½Èý·WqnK]ű.Ù72_³Çes–~„q¦~È“U—##,oˆmõO!Æ`¨»v ã‚¶ h†—Cñ-¸,è§JœƒžQ 3×rµð6Äéð ró5£[siЯ‘8c(91aÂMw<”C®È­쎷@IDAT×uV,2ÑJ öUï\.Z_´èoîž14Py«t:6Š(wìúkséû§g¼Ã®â^`²ÜbWq°Qö:_Öš?1”ß}ObxEˆN/³¹Gu›oD% "õ}°æC×J‡‚§Dp(m)õá.…zí[xQuø@ý›küCËÔ}q—ûš»Î?>Aלwkºö°îvLò|väÇG¸†æÁPm]™Óùæ7Nå­Ý^Z »“î÷57u^—@@å.QÍ ß$\ÅåŽû7·®ÿgén÷ã¼ú°á+j¸t³©Tnžò‰qãÆëºëj‘Ц9Ld|©6Eµ¥è8D1Y*+¯õIÜqJ¾%E‹Þé á[Àš’Ű€3¥a_ÎŽV@ØK§Å7ä¢9÷Ãf„Úé cfº˜óšÛÉM¶,®s)“ï3l“üšfÖ^é»uO´ð‚93u ´ë\¥~tU¶øîªˆtÁû†«8Sö¨Æt}&÷ÂG6³V뎃…4kŒLÓÜOÊ´Ù*ùÝ>ÙÙùò·Z+Âh·äÅf*¬Vª¶©ˆë¨7àXQmåpÊ‘ã@¤Œ4Pÿ(#ʪê>p‹úçç)†½7Èg¬%©Ÿ›8±Ϲùiî„19 k “ãû0÷Æ' o¿ËóZçö[°°o¿ì½?g’|Êç¸ÚTÔ‹€"ÊõÂÒµvÂUOl€«8!5®âäÏ×NwíÔZîKOq ž檱=¸_§<Á¨ÿ ·ÕZ!îÈ7Se¸3¤”ØH~lˆŽœR8QwÀxZsþNï{…ªKO"Ù€¦!Û¢#ªº(´â9¦=’9¸þj:¥å2&11âËáCo;2~ô «Ë~ŠÇ¿â4úú^ÃéìáÇõqst\Æ€]{ïf‚¼Z{öÙ&¿W¾i¨í®€2½èÚõï½ûÖ¸Š3\FvÍZ#šwKîñ_;¬úwøë"&ýñkp=k“wùŸ£~+ÂùÑæo·ðMj`Ÿ´‰1f¶§Õh7‡;¸W8»C•iãh¦ 2–m§“;·ÓÅý/QrÆ0ÊxuïÅ$õk×ûñmì!":ÊÌuŸ§ê>@µ,£Ø§pJB´xÆð¬IÌý³¸))©ÿ¢Ô¤¯ö²Üo4$j,…m½§ñf‘®þc4êÿê»cÏï0ßX¹ù€‚bZ"#8ðG"-¸fuK†Uˆ’öB@i”Û ù0Ë®â¸/ˆ««¸FK©¹kg]ðkªÑ:ûhæ!0ý1™ )m²C­;Ð"™ c1±}²Éd¢nÑF:Ê¡OžSá}ÛR‰Àïã˜hÝÊÚzîŒðÞS^xˆŽoyž6¼9“6½3rvþ…*‹sÛ’U›®õmC{¦pÝ_Puß&D=£þeVjŒx¶äs¼ý%±ºªï)IM5™¿b =Qçæâfb½š¯½»_zví~¼#‘dy?ýäWfÓ™£©¤¢Š>ÛvXRëvB@i”Û øð̶e®â\F·<–ÍijŠ%Êv»õAK÷­i9},\JÙ¦£žÕ¥J2¤fËC’MÁ‹™z&EQAe%}´nGÈš%"V…¬P$#»ÃEK¾ÜCÑ‘&ºáÚï’Ñv-Y ×SÅEî»ëµ“%Aš%qŽKJÝ.©¦Ù¿ ŒâðÁ‡ó.‰²{‘ªûÖ6GYÿQf ÊHÏ:¡xÖ$æ¾i›Üî(ßßüá9åÖ ¯FͯdîØ‰yZM±{J‚Xª­vZ¶a¿ˆŽ™Ë‰OÑäýh)‡Ú§»Ð6CÿÅŽÃ"p ÂUßvíX¯/ì‹EeÜ>÷Ò9$”ÄÑ7çq8{„Ä.(*§Öî¢üÂRJIŒaR>–`NTÊÁ‡>Y¿ŸrÎP\t”>gÒÐ÷wh [Pø+»k-¸XÚ¹€«8Öãâ®XCœ•“sìæÆîÐà2øøqÒ4½À,dîì?Q›–ö¢²«ECmu ÑÊÄoäHd²l‰Š¤žqჴøóãF¬”‹?ÏfüÊiBÿtЧžƒo¤qó_¢Ùì¢ÑsÿLÝúÏ#ƒ©7¢òK‡k5ÍoÏ ™¦Ù· Dó„>”eÇ=(ixnðü é#ž'<[xÆð¬ïú„Í,l¥.÷òýÕö;çœÍ5tמ_w’ì¯y%bd%‚;vvEyâl-Y·›F ̤1lîuúB‘“•‘J·_;Ž'—:è•¥›Éͦ(."óïe›©ªÚF·ÍC“†÷¥ôä8‘Åkv‰°ä,œ.9Ò„|™}Œ.—UÐÃ7_E³ØÇ:{cûÅÁ.ðOi”»@%7÷ý]Åñú]¾ö£†®×|4ÊÌ…Ô(çL7™·ˆ‹Ë‚Ò¸¸„WJSíW´3<ˆt—î®´:„Wï—Z»ã#IQ–(²ð’o¡J{í;qŽhå6Z4g‚Ò,7£¡IÁÜwâ, ËL >ÉWà m¢™ SÆ [ÄârTSÁÉÕtáÄr*<½ŽÜNM3“fIœƒ­iöo(ó¥ÒJq´’¸îÇ«ºoFÝãOýïÏMŸ¤ÊLÏž-ÙüM/lv‡^a0äýòtÞwOÙí9œÌe^üö Ÿ@ŠÔpÓv%ÛwVí ¼‚"ºTRIÐèÊ0»ýÚá4fgNÄÉŠ±DТÙãÄmFóö?—l¤Óù—9¦ŠNÅeUôÈ-WÑ Þݼ0T±–û§P,Ÿ?~hý㣠TÌíØÀöN%åUâº1ƒ{{¯ih¿÷„lÀ‡¶‘;F0¹’8c¢ˆr8ÔB•®âlvížtgâE¸ŠëŸ½¿¾":ݺwD‚Ûsƒ/+ú$Ø„5ÕÿL_·®ÂóKýW„²™’Ûá*()­Äoï›/mAàÌfAcb¢Éfµ‘Íf£žN[éW‹~Aq9-œ1†úfÖü+¬n·ý ›T|ä/—Ñ Ö$Žì“NÀ3ã+‡Ýe fKXæúÚÊît:iÎY*àûQu/k­á5êæJ¬Iî•`¤Ýã)–뿱6€ÔŠË*õ"·~‚Ir¥_êÞg×oÀ~ú’¶`ûÑ6°‘þÕ^~hßî”ãëáŽh›\H)©¨¦î©^3mêQ³]ÌdWJÏô$¹)Ö9g ùÓ®Ó{ŸÕŽ‚`â`>×Åì‰CÈÊšë7¹ÃŸ‘š ´Ô0çhh„Ûø>´cÍÞ×­75_ܽ;C¼¡ˆrˆBvhY¾­Ëw»ÅÙ±«¸ GÆþÉ­pãÆQ`2á„ç›`5‘9‚¸Q®5ŨٹܸáN·ó:üäï*5¸ÿ¯¾ójNoéÊ›9_ˆmßß-MK¯ð"à°ÙΗWÛ´*&ÂÑl^ñ$k Y£Ìûbcb˜$ÛY3æ`¢ä¢L·›"ʬ‚ü½üázœÕ="ôæ[71ƒÝ›xÝ€Ÿ\¸€ƒ—L€Œdï’#{ÄRîPÄÄÆ<+ð52QhèÙž¤¹¡60<+¢ØÊÑ‹¤ê¾þî_ÿFú'¨'k’ãâb›lxñLZ«*…y`ý¹„f¯¯mÌ@JŒ%’î䑉†&)RRbhßqɪ‘Â’r±ËfA0¿€–TûcñƒÿEFxhßרì"“½jøËlÂ1}tzÿ‹]ôŸO·ÓœË&1Tß~ÿk[û[úÐN‹®Û)hmz¾Nå@#üô1~äég'hm.Sä,/Éààã£äÿ’ÝŸRQ&Rb%ÚW›³p¸Ûh”!ø¼9üÝfKL©ô<˜“iä×æÝ¾Ö{°fãÍŠ²»' ƒÍQt).þÕ¯]·Èÿ´Výv“^ÁãKùLO3±ùìõ?þ]dv,Š8· UuÚÎ…¼Ó›âSRžØÏæ˜<#CÁ˜hûdh¿L’]ì#ÑÍ$‚ãqUV*¨rSnÞEA1zË0žg³Ë”L¯+¬1¤ZÆš¯ &:èWƒ õŒ7R¿´xJJŠ£øx^@”Oà |ý‡Ü©õ¤.çnl•˹†Ú@_n‰ì2.·°ŒN²-):]½îQol.A¥\ÿ•<äúgpÔcåõLŒ 8î %$4¯ àYÄ|—3ÇŽìãdåûë :KYÝ“hË¡3¢ãמ®!qg|Þ´qoOîëFëw¦½»§ˆwR¤ÙDëv¥®)ÏÃÌßûÅOx’zßÔ¬6€gðÀ‰³Ú™G?ãf…÷¹\|ŸY4¹ ‰$ÉžgÝ@ƒ3“hw®Çv æ#ÌŸ6‚°Ô'˜À''ñù‡™+{¼ˆbòî+#ú÷ ,•Õv‚vÙTLå}êþë…Û9œÅh!·²w ,V’d»±ýâ¢6þóøÐ¾H™qðQo sòt–=ænò]ÒÆlÚt¹"Êm‚/èãkcxôÇ¿üž¦žO./Õ¯;¸›z_ò|…‚ž}ÇÈ€; 4üüi,Z^R*­>.ÝøÁýßæçoüïo_什í<>K-DǸ1UÊö@mD|„¿\òÁâ;¾ùø]oº5陇"0f\# J ~¼È=¿kÜÆ±™…•‰²µ†$a¢\5Á<£+䈼¸à£s à%½…ÀÌ6ÞÑl“ˆ&8’,ó—ë`‘fÕ$ ¯Å³‘œD=û §ÂKéò™ÏxÄ…ídÍ6º|ôïaÝEésž¥ØØn ¶xzàgÐív:Ê7­Xö9ç†w9I–C¦«G£CŒeÛ¥=[6~´ýI2ããxƨO$Aö?æK’}5´ß÷œ–l£n1©æX=âØ% ¿'ÌIbŒ÷AKÊ'ÏUDY"~kA’ùѳO2I~ahþY}þþ³ ï% !Àz`ËãÊ‘ôÃ=}ïOßúÓ âóm¼@»¬ÈrCà©ýR;…°³¤äRÅ¡ìíÐ&Nùï×—mÔ½m†·ƒZKŽMBÈdÙ£1 h‹Ed1Ñíó1чŒëåάT†Æ "1‚†?˜UÀG.È2´Ê˜¸‚Œý8Ž"® ¦’4ËûC½‹mÕ¼U'«Qb„6™ùM²—Ï£Ó»þ@®ª³<úÂŒbv·üNqÍO)kä=õ¶<{ç K({ú·ªªØØÙ£øÊ<«ò¹õæßŠ ™†\_‘î H|¨ÎJ¢­' …›C˜5(iÒ‡vŸ8ßäd®y4p†ß,²Ž°‰(¢˜[•‰áÁüxžÁ`|$ùæ=[ƒûiUÃó"t&-ÒIIì¥fYè87'àŒOÍ®;ŸkZ¼©ˆr‹! Ɇùóï‰2Gþ#…Í- II®,ÆM+Œ×µnèÝ{ж3gŽÁ9½ì¶õåÚÉÐR·SƒÚ´Tø³5%UúΛ^¿è+0â»;ÿr±~÷Ü)›eYæ(“?Ø) ‚̦˜ì''üIÏ]…(3^5Bý¶Ne­10Û°G åhJEš‘Rm@Àgâ©ãºm eöϨÿè…txíSTU’ËšZ¢KyhÃ[³ièÕ¿ êØ™ÂÜšäãö|²îã¾à¤ÄóÈkh•±gÏjÀßåN—³Ú?àç#îm>iF4¨G¢ð~¯ʇ:Pj\¤mDcL1Û)=&YžàMâÔXÀ+œá÷¨¿íÆ3 ÀQE”b€“ÀÛŘ>¼ïãnƒ!6ÉÊÜ¢u·ëî2ügÊÌnSn¸á±3/û§„—*I˜[—¸ºª³"€v/FðAÆK9jõâÿ,wõÌ÷´kúýŸ&ŒÐSŸ8¼Ÿ6r@¦ð³,5‹ 0#€Ö“÷¤]2ȱ$ÈrÍévZ‘$ k¹9¹Oî G® ͧ>£ Ç?i$"`]ïщ}UàŠmª Dõœ@©÷®âPä/ÐÉÝÿà+Ø‹ Ç9ðÅÓTèìÏ^CnªÜºfûâýÛ6àƒòYD°*<“ø-G¥U”ßÝaµûâü¼¦9‚ùlítS" %tŠý+?Ú@ª®øûÐ6³ê¯»ÙJ‰c ŽÏ_ƸÖt)–pÀ—Ó ?Úªóºmà—"Ê ÓŽ»¡=6±Vêñ~ìÝBMÜk[MÀf8æ$¦<Ì)ý9DjÛ–ºº³! ‰²Ô(ãÃŒˆ#»6¬Û~${ç‰)so¸ÞårÎäYøñLô8K¤ž˜£E™¯ +åOŠýw6ð|ïG%¹Ïÿ·ÜþkL„º 17Qœs/ÅÛwRŒó t4 „¶'Œö d5ö¤²ˆ TfOvc7oIž«Ú€D³¶:F³–ðÛúPí-FóLTO3åÐìø?E¸3µ¶·€«O<‡pÚ5_r H“—$sÚn5ñ8ÄûkˆrýA‡zð\„S9-±)ê‹Å߇:œn$l”dv³‰…ILäŤޖœqØmç8iY_"Ÿ`ÿSD9Ø·,}¡M^ôµÇ'±ê¥\À)i;Œ£ÆnãR¯žwóÄ Ÿ.ÝÈ)JM„Ô,·=•BgBí*ØAB{³ ¸¼0ðd"ZóÑ{ŸðöGŽé›5pðPKl\R¤Åo2™=!üø`WÍí6ÅÛ­=ù†w•GDs µL2D€pDQC¬Ãžft»¢e–Nƒ±ªÂQÀ,'„*–“É â*-+æX4/1Ý"ó,FÍÅÀ#Q®³U}–Òi •9SlgªTž,TUâLÅ{'¬õårŘݮ8ƒîF»÷ÝÁxÚ Æ ‡ÑXLNUU]¾¥zFÙŒîŸ'êU:š350©Šœ?Y¿kÒ`mØß–~¿iŸ«”÷#žI<›xFñ¬¶U$é£I®3'ŽíîÑ·ÿÝþ‡‘gÔ¨á C°ÉOg¯7åVøPgûvÛVd­ôª@;óD^àã+xäL|ç ¼¶]ÄnÔ…ÙUD„tÙò€3çÎnà<|ëÌ7Ë l+¢X[¨ ÊÑqq×ñ†?É­NI]èE€qÄ×[OËì9“w"zŸÔFàE€'§¢¤3! ?˜ z @ ÉòY”$Úq|ÿž£¼äò1¼GqyovY”š|cBdd/Üq™Ë}æ­ _ õݧF˜bîLN~8Öhô’äs6Ƕw‹Š–8¥x •k0/¼F6qˆ>Šƒ™ bžäõÅoº9"˶dŽ$|þà)ÚÿÙmÿŽ#îvÃ,¡ˆàã“bc‡±DNL6™²x¿—ôËsªÜî ¹VÛŽíå»/9 ªÊxAã# –…h‘½gÞ–$C^û:·¤šLÑC,–ÉèM@¶WT­àU0È”H¿¾ý¢¢RîLIþf”AK“Ç[m+ߺti¹ü¬uq…›þ½‚6cIŽ7DÜ1ƒFެÍH¡a¾¤™ƒŸõà@g¼èsË*µs‡ÏÐ.&Í{¶t_ VÙKwXtt&ÈSzF˜'qP4¨ÊëˆS§ê|»={weÕ–]••§ë ÜItñ¼aq±Ëä½Ûwÿò!÷í+ãfÞǾ&(¦ýWK£Í>_ñõo¼`Ë­9ŸWm”A>Ûxæñ|ÛórN|Ê}ˆ»ý!79¡9A‡ºvÀ`Õö C2àÌ¥ ÞbøQG²“$Ûª%h¢ˆrРmUÂøÈš4ƒ–[U)?¸­JH]TXk•f4›Òy¯°7åµÔÖ=QýRÔ"€·.Ç YþIFG D/e´'ažÁë.C’ù^iQjê¬$£Q˜T»Ýy**6òî|ÀÿÔ¸¸¬[S’žä HÄo7“»¿Uxiçgèþ•¹éïËè"-£ÏÓ‘÷ÌÑÇŽê§Oî–L£™/xMsâc(sòP,ú‚Šj8wNÛúÙNÚ¾n·;?˜¥M5›£oHŽŸ: 2zf¬AëçŸ7\½Ôí>t¸ºúËE%ÛË].ÙþýO äoùlü€9ŠË\Öïþ‰^xâNã׎q?Ëócû#CîxÌì•fÏ^ù‚ñÉùO¹þ…}<Û’$ãù¶mZ¾ty¯ï<9ƒÄûBž ËÐŽB¤ÖTº‡ŒTA‡êàÒ– C2à éî¢ ô'ŒŽ ê uñÔrH²R™4>®XŒM‹t9»ÔǶ lÚ|xM’…6í¤Fb²:穤ã ?à ¾Ûøb‚H²oÇËŸ(wúg¸[ddÔÄØèëd¯~Geå›ì¯€q ‰Ü—š:æÚÄøŸrþs M³o./ÿo^Ú’4’Ia‰‹^|ŸÎò)Ëz¤£œ§OÒ[¿&%ž&ðh4ÚŽ61è=f€Î Ýù[µÜ3Ú—_dk—oqåÉsÚ²fw|Ú݉‰£ÆÄÄÌK‰0Mg;n/a—ér/8g·¯^YR¼j[Ye¨4Üò½+Ÿ-I”åˆõOï¹Îl?j™ñûlO›úw¸¼~ãܺûŸËŸ×Œzäº'ªÏÈûhä ù#ot‚«++ËÊölÙðOÃÕ³¾ïpˆûcpxø ·o!²Ñ– C2àÌñý{ÿçÒ¥|ب£ŽPWŠ( ”»Þ?|oŒü"èôØö©Z¸J’,IMûEåÚQÀG/eß9ˆ3È24ɾ/ßÎW—x†޳ǃÝÌæxÆUïzÞ«—ŠÞåÍ|Àžë›5oDTÔÿp~¨® ½ì˲ÊÇÿ~¡`7~‡“œ¿ä¢ß¼I§¸Lï êi¶<²À5#+]¿>6šgzF&Dq-‘Ôop/ËCÞ¤+,ÑVm> }öêJ®m‘,HNθ)9ñæî&ÓB“¦eú_Ì Ú^ær­9d­^ò‡ü‚­•GHêͯx®ä‚ç ‹Ôìâ9³oÙ[íX¸—~°ìÓ;Ýõ Ÿ=„÷ãª9v›õÀòçß¿ñi×?žÖÿC7ò”®è¢²×¯Ùž”–¾˜÷Ýåpˆ÷ÕØ+½¤>–¥VYªq Ét¦5A‡|Î\È;õ×uË>\[S7¨#ùNF5]”F9è7;|XÅÂÄG¶ÙÈèDžm \Afü ^’J!v‚µüûvºd›’ÊÕNÿ ML4¶Dßg¨q&qÞîø3“­LRûpÈ G3̦g¹BÎ.ò×——Þû¹sG«Äp8v쬃~øWz‹ËòÖ´‘–ç;®O×o‎³øf`Î#„#úÊLÕ-š©?~Û5Ú¡òjmÙ\Ãòß¼éÌ‘çø¯Ç$&F<–27#2âž(M»šñ‘íÑ{*wh\v:ßy£¤âÃòóáI¢=E¾{åó$žsûÝhgwÔpˆ÷ 3 öTà‰ 6=A†Œ×hØŒjæ`“e:ïž6ã«p(Ñ?àŸãÕ.ˉ~*èçÂ,`#'÷É}ÀRÅ[àŽÞ-ØG½¦»]%‡²wüïæÕË×ða˜\ NP7è@¡®B&Š(‡ êfgÔé?°ÍF"8'_¹'•jgG@~¼åËÚ¿=uúgøÙáÃi&ówuÝÛ ýþõsçJ‚Yñ/`º>>ælc{¿7Ͱù¼f¼å‘£û‹½û:èÆ«+ªßXMorñßü×Ó©ÑÝS‹oâ Lwðïy¼xI37®úˆ¾Ο­úoÓ¹¸S)†¸=#¢¬®ò˜ÏsëÃnMû÷ Ýðñ‚ìì vbÚ;ž')òÙ’¿ë]ßð_Ž­AÐ.#o©U˜r”epsÀ¡ÍG²³N›Ó<·Ë5‡É\“½FáBÿ3þ¿qNga_ñÿcŽ(ˆð‚Ñt·ÛUzîdî²MË—-./çˆa› †[@N¶iQD¹iŒÔ `ìÕƒÌÇâuL¶ÕkI¯B;gQ&-á\;¸lÍú¸wàû»¢è÷G™ïâZ1¦ªi—RÒ_æ“@2‚"®¿>¦âÒÅ÷9ñy"Oä¢iãïžµn´ŒJyþˆÁÛXV½Ð-ÆM—°ÞEü»ivFº2‹–ÈÒ(Š?“H±ùñy%Ñÿ°L¯fîØ‘×©€ñ¹™ i—ÑóƒÆ Éò¼ºªªÜöùoÃfyéбô8x¤%&6%•h2š¢x¿’–! s”S«C†#âÙœãÙûwl9ÄIÀÌ 4ÉxÚM›Ìy‹‰MX+iäCÙª’X¾‡"§O¦’‡¿×¬ëÍ£‡“óxn«É­m¡„—~KŽì}ä.($‡M‚*Ü3µu±òÔ¤IÛ\.ÛP—IŸT™Q]Ö«”*»—‘n‡óˆ-ÁJ…#/`éůË;xÀ[_uWÄûs`?"ÏéŒë h—A–aVÀò7>rè” ·‡wï(ãf,Â<ƒ×ê[ ´@$¶x†Ñ9æÀWN¦Q†w!,ÀÇ=CX¼JQåP¢Ýt^xÐx ²æ“}?ÆþìûTöäÏÉUÕ:³>óØ‘ÜÍ2QÅsa’Œ¶<Ñbc(ö‡“±‡4‡lS^5+ó‹6¡¨.œ8nOŒå€ ³%æ/Áƒ bߪJû*öh1PæÁ~æÙçžgi×¹«S®ó¦NµØmÕ·1ß}Øå´ÍbV¡űÖ‹Ûè¦ò¾%9E/•Ùã­ƒ¯y×Ï(Æl”ÛåøÕŠçµ}Lš ¦NKš¬]–ÄÌwdM¹…lûÓL![I”1‘RvF@–A,ðû¥]²¼–w…NQ֡ˉ æÿõªúÇ›d¹{!i©)䨏ª^cÏM.Åý÷ á¸ßü˜ûhN*yè ÒxÊuôc÷SÄÔI¤s$;ÛÒO©úýOD™£˜Ü'Ζ‘FQó®¥ê7Þ§¨…$Jøësdû|=U¿÷1Å<ù™ÇŒà>Ÿ›ìÛ²©ú_o{5Ö‘s®¡¨;o!Cr"¹rNQùÿü9Uö[îYHOÎ]û¨ò/ìý§ÉZÑØ‡’^UE¿ú=ÅÿßóµÔ–B@!r؇íd¦<1ç¯Y7Å>øäÄ1cX‹º’¿ŒÝ‘Û7º˜~‹Iòßeþq3iìr¹±Û«ïa²› èDÕ ¹·ÿf$þ5è½¼ƒ8Ô˜yFW"ÍÔ.ƒÀù’9g7LòëÒn!ùþÛ*ÀU.ÀÙ—,gc¬±àX»h’9_!Š(K$:ÙÚÐ-b”ªß]"Ì"b¾õUr9Nöõ[ɾn™F ¡ªWÞf³ ØÊ³&À°7®üß¿’–”@1ß{ŒCí wþE2$$PÄýw^\LÕÿùû‘––B–Ûo¤Šßÿ•Ü—ù;É„Ûuü$ÙV|NšÉL1?ù¹Žä’í³ud:ˆb¾ÿ ²}¹™ìÿÞJ¦Ìî‚$’“Äþj.‡ƒÍ@b¾õEο–¬ï-­Sî …Tñ›Ér´Õ…€B ùœ7îZÒ]Sj®°išù›uóÏD>.·û#þ” ÍlJ`åÙU÷ôݵgIóSé8gž?>µZwÝÇÚãGt§›µ ð “À[Ÿ²Ë€Wú¹µeZv6„W`žÁ?ÞÁÒÕIs€µË h’,C« Þs ¹`”RΤĶ’¦[â‹ö-à,·åñ¦S âŠ(ÜöNÚ¾eY—¬ň¼~&EŒEö5É™{Zì±uåyL/̓Ʋx79Ïæñ‚ý‘WMb­ò2q®f‰¢ÒG~IzµÇÌÂ}þ¿>tr8â½MëG+øuÁî_º¥“ûd™9?åˆk§“^YI•Ï¿$®‘oxäéb¢nÛ´]¤áع‡"8O¢ìÍ@m(튀F®Ë/“·WúîØÁ/‚À ›vÜÅÑý^gz­l¤¸'nº¹ovöÆÀæÔ¾©é‹sOŸË“¡®"×Íür4ûpcQ8î c^eﳯöÉÎæ—sÓâOš]ú¥›Ø;É|%†»Œy†vùW¬™ÿÿì]xU×>3»›ÝôЄ’¢´„A Š Ø –Ï *E± b#øEH@…OÅþbo`W@°!½$$©)ô^Bú¶¹ÿ9“ÌfSÙ2»ÙMî}žÙ™¹së{fî¼{æÜs'#¶´˜W(*'õ»L·<7"mŠ--cÚˆ+ÊÇ´ç¡a”a„öÊFø*›×p)^ºÊ‰²—€nŒjˆ++Á`PN«ïÑÖX×ûBYó«ª(‹pèÔޖβc·$Û"íȤ"xÒrÒ™3 ¶j¬¸Â„Bƒ¦Öœ}2I¶Ëº„^ Á|ásÓª¢‘|óÀàø¹ýãû3+»Bn™ X4š€Ùj·271þqt½õ:–Käƒ~iE½)S61ãüü'w@|7¤[crs÷Þ‡ƒb”Ü»q;^‚v&‹†ÚãÌÍ«Üén%iF›;ø†4ÍÍ4Wj—§ ßåïTð»¬7"sì ²r._à?!`ÿ²W°u(£·q¢ìmĽXŸä¨' ‹í’-P¾øw 3ˆ:ƒ‰¾8Õô7Ž16 xB¶K{õ¿¶ÄRQèºV~M´Åâ+¢´¤SgöÔa—•r8^F]&OµUÉØW]6nÌ·»yPáI£ß,Ô|>k+Jþ5hõ#:nØpÈç§²{»Ó'nÅE Ç23Zw7„µhó½ $$ü›È+ªOÔ¨;ƒS±Í™4«¨]¶ÇܧÉ}Cù±{(v5î•ÂsûV2›À wº^‚"›K˜×e€>y0NÆë){³Ðté„6Áíî—ˆ6ËìlAE¹ýú€¶gw[^sÖvÔ0·ÃÍ×€ÙZ6ŠɃ¦õ ¶‹Ä‰#em·Ø"óÑämÏ„£±íè6-/òLé¼TŽ@ÓE`ÿ€„‹p54ÕhL§_V«·,9Y››”ð±=IF“ƒÕºÀKý$ï»8aPNbüŧESµÑ IÆ~Cr<[u=ºnΛ‘õ¡'HrMYiõŒõ›kže·hÅÈHAïÀ4ßãF^l&JŒÍ°XÍ;Ñ{Æ–¥¯Š)Ëæô°%ð£Ò.zFš"h´CP\e3È`¸ÉX¾}ñ«šý¨;¼©^D€k”½¶¯TEÞ&Œ¨=šp/=|œ½c” ‡Œ'‚Ð4œÐŽvÆdS\”öNæCRí@0.^!ON„ß~(ûg&{eMTѦɃÆ]!èþÛ!èÁ»€Ì@ Öf€yÃfÙGà=£!hÜ=2É.û~±š>’«µÜ`1·¼&<â»[µ>¬„o‹@üôŽ;·TKÄO8ZXÌìyTŸUšC?FoÈÚQ+‘ ¤iÍUQòãB"".$í§ ‰ä ÐV2ïÅEÛÆZ,RmR‰f+ØÕÅ"Ùx‡„/V¬ óF .iš_¶¡úBt9ç!ír£ÉWìy*>7ê—2ceŸŸ6-™ö<¸ŒÍ %Gæac¦¤ü[\˜tç†\.Ì‘Œ‚A/'«¶XÚ+ Á莭ñ³›s¤7 µ¯c±/×(XUõ‹Ž„¢ $}Ч]×®=Qýšï)6ͨÁ¯‚‘ØBÛD@ûÖ"‰ð[ÒŒ¶Ë«Ù.SÇZ N|òšg¬ÿgßO~ì¨5îs²ÿÉ^µW#ÈJ©d¯Œ_WC½$™ DÎRAÝe×ïj;êÊgÔjÏâÔeò‰ö&•Ab L¬`™“Û/~’ïÄ^wãR!5U™°¡¤ä{Ž@³D}?ƒóÒ+H2¨A’›ÊB"ûö»P²HcòŠÎÞ‹d²¶ÿJ™t ßh˜øaÌæÍëýéªKÓ\iÆ/ ½ÑƼ·¼¸‰Ÿiš¹vÙŸîÈÆk+'ʇ=¯ÙËé Ç&çïêÙí» ½2TšÁo fà`öúl$ŒÌûå‡ü¼Äøwu :­[wÆËÍäÕq|üþýÛI’é~¥AÍTŽ]ÝûûB"'-*/¹]‰y“uP]8àJx+q}ÕA Y•™YÍî·®ô¾×”IóùÏ!À‰²ç°u¥däkèΟI%F–ŽÝ6q¥M1Q£c‹¥¬Ðj5O=pèOì㢣áîÖ-®Ñ» rrY–-Nbðsðl\kzn¿¾_iDÝÛ]22²š".¼O†°Jæ§ðÙÿLâh´¾Kæ–¿J¾k ‰Xq$òg‹-$"À1™Ù?/oc_Ïë×ïR&Hc ËŠGãX\»=Âaü»ý‰^'~ÔiÝæœÚ×›FLS%Íõi—q’éüů ·è \ùxÙ¦!EÞ gàDÙ´<›–ˆ±$³õD±!ˆÎ9Q®ÄÄÝ]±!™ È-GU˜¹!n 0æÿòÆw‘L0µ@ }a+J…ŸÉf|¬E2Íé¿_êoÇÆvûVX´¨a_y”™Ž€Ÿ#°Èæ²¢ñJ7DQã–6¹Þ…D2|w!‘ƒ¢Ìåì>4«+µ+üšÁ„ƒô/L?Œ‹éºÇZ˜¢Ù{Òœ1/*èĹã×9bž¢°H#h]ý´©Êû„ ÆµË> lwçƒB1GJõz¡'ñà>„£ŒgiÉÉúJ‹YŸ½/nsö³šÐˆŽøÿd n™ÕÓ²AHœ¿ÈËÛsm™ÿK$¢úu~ÆhZXJ‹³Óün‹Ù˜ù««=¬\H„œ´Ë«í¡ àV'‰óÁÕöpżtWw ¶y1’äøÜãÖÕ¾ïhZ±·ÉA‚¶CÜæ-·vÍÈZÚÜH²=tœ4þH)¹œõ »µmDû6är• ßá¥j¦'ø_£7“ØtÙåÜ+ÂÖ%³Åi¾ærŽ´ËAm¢гǼW+þüàW&ÉÚåߟعfÿùyÓE€eß“-;vpÿL„Ü6‹6ù^ý«E„#áy`Ï®­ØrÒ )[­ŽÄ K*ôgú1nI¨ñˆ/ÃÏ1‘MƒLvÐ,#5mû‘0¿B® jÂ#8~޹mï+Úº!ˆ³4ÐsãT@’)ä&&¼Œû7è”3Ë ‰©åbΩ5871±7>Ó¯åæí=Œ„è[ló(ÜÈ‘p,(@ Þ´büSÝ·7Ðþø”rï«h ¤ù|~—Ñvù¡ª󣦌'ʾ%]™Àý³ø‡%‹ùôŽ~1ùVw|£5„£Õd<·=c}.¶ˆ¼YÈ8WîëmdLfæ|Þ†àN¸(@ ¾æÚ“vØ3ÌbÌÏIìûæáþý;Ù®ñŽ€Ÿ#PrúäxÅ ‰sccº.t¶Kþ°’ãp$ò od̲ŸéÉØïÖJ_‘Ù£Sø ò]bXDû¸Ìì‡ã6fe(×ùþüø;iæÚåó˸©§àDÙw$lOÞ¬§Žÿ&¯u[á` Û˜í;-õ£–~2Žù¹¿c³éš²ÙãÝ`ÈçilFvz\l÷\ŒåN|qnS2 Æ Gãe’977±ïÿÔ¯Ú'Z%ßsü2=Àûú)¥½"ƒWœ5+)<û ’Î{•rpÿ(†¶¸2zõê³¶¸F8À¾ 4©M+ðk‘õ(NÖz ²/gR} ûñ9Ÿ.±q™[®@‚ü%}mj„æ6©*ý•4+Úe­ Œ7G•]uåª~\»Ü¤nÓZáD¹$AÚN™Èýóãw‹˜ÙtzyÏ~’Y£mÔFùkå„á'ËÏ­YòËØKå¦eÂÛá@dm¿FÒÜW# 7âË´jÑÆth–ñ€É$íÂðûû÷íépÁ_]Ž\g#GêÉÛ>‹Ë€YòIÓñmLµ¼d‰ LÒ†Fáļ;ñðï|Á¡jyüÄŸH3×.{üvð¹ ¸ªÒ·DBN"qDæŒk—/^Ö²ÅgwÁ}ÙÈm‚ÎJ—yhÒ$IÞÙ¾£³}Ë/ÙkW’©½ì•M!ËNi”몓^ªÿ;j«†0°¤ õ¾šÒ!ð³óM¸Ðáõh’ñq@ü·Óº-‡ë*ƒÇq|¼¼½·ã}'·GN·j3ÏѶùÒB"û’’,’ulÞ‰#ÿAbܲfP{|ŸÑ/DøaìÆ¬-5¯óóÆC€H3Öþ märîTáñk%&݆r¬¶Œ6ʯ7H¬·ÌÓ—àŠ€Þv9GÚå¿?ê’PzêÀt|fžÄA_ƒ[(÷»Üx÷ާjv[£Œ7knUK{ª¥Í£\Åô‚ˆÙÕýöõçßïß³óK$}ì³AÃ$n³Üð@ø|:èr‰ðÚ»=û—¿úöOK³ò˜ð%²ÛD˹¹êš¹?5k’P ÷M¢ f2Éö"až•ŸQ‘£ ü2!§:Õ½&yè^sëÞ«tÛ>§ô¿”¼Ùnùò弡}nÿ„+¬[‰d¦¥#3\HäÔ$Ïo(Ÿš×Ô2/©ï$Ôg¡ïóÍøxO²'ÉØ9 ÿ¾.×Þí±‘QQø¼>ÆI²šP¿,"Í#¦X*.ç4¢x;Þ[>ãrŽ´Ë×L‘žá¶ËêËÞ—Jt[£,0áv(Ñ—:åÇm!rEÎŒ‘:zI–/úòÇ~—&ŸH0xÌ—“ÃcOg> Ä<3%mÞü$“ 8ònA÷¬ååÅ›þXúí¶ k·#2 ŽÅxLxÒ9¦˜^à¡z¡ëæÍäùæ¼ýú€Yz?A“„3-^òœTÄÆå%&ÌŒ‰l÷¶3Ÿ´©  í±MÜ€ Æ•&¡‰ÂuH,{Wæ-Ö¿åH99I w0«ô ¦•}$#!=‹®"®Có¤5Žäw' KMóùa8-'m4–Þˆeé+ÿ£V+@ž|d>é°iS¥ç®D®È?Ž*5Íä}e¡¯iš¹vÙ?î!W[‰cš{aÜÔ´@`34ÁÚÖï=ÿ|£Îfv¯'>“›üvÒ ‡–H Ç­nôé°EPPHËWº*:®{²V¯Cá± £‘…”— z«ÅmYb~h™oZÁÁ‚ÅX^”¿g÷êõË—¬)//¥Ïw´‘Æ“îË3¸ÆínD˜­2z.ä'Å'K¼jo³Iµ¡ÖùnÓb®½ás´‡œ8kV k‰åj”§ÍOOÁ|;ŒK™±‚Z8?mZ2í½&N›•hµZF À;3EáCê­º­§]ÁÙ­ÕFùJõ†ƒgBB‰`6ÂJK;„—•ÄÑóG %A4â¼€m&Φ‰ÆkE• •ýQ³ì½ÏÓŸI·ÙBKVãü$t?~»éT³0|¶Ê0þ;ÀDgd¯Àsl†wƒ,{ÉJ¦XѾ.{O"ã ùÛ··!ÒlŸoP¯˜g,›­`a– ,Q=NXºB©¥,ÌR$µÍ+gáGíÛÔÔm²gì€F£ýMgßÌÔ÷Ý&W4X$K†(Š÷Ì›1Ýíðà&$Òôp#²LŸêi8Úˆ8“™K`·Þñ1ÑÝ.¸00$´…>00L«Õ¡&¥y‹×0,-+*-.,Ø¿g÷žœ·¶ˆ4ÅD‚IsLZd"ÆD”i£%¬éNf-dìí•—'}ÒÎOJ¸µ^H(k¯ðÅDá9ZÙ Ûã7aü´ô»%IúL+j“¼=ø¹’Z¦u “'¿f(4?ªà Ô¬¶ÇšþÐJ¡Æ2!ÀbvÛÜÍ6øT“V'éé-zšÃ¿ Œe‚øZ]‡·SSÇгèP@ÓŠ@“±ìfTÌŒÅ'w•U3#âHJ´Ð¾jµaƒ×Í‚RS?26ž$€ô$ƒ ÙÌ¥R°¥D°ššìI>&M€T¢ f庠 ù;*1áõŽßrFþ5e]×¹£¤ïœíx¯,Ôiµß^9Ù´³®²\‰Sä/ Ö§$&¶C3¦‡bÁ âK^KÍ+X@/•IaÌÄ‚m²·JðFXyÈ[¯¿þ$=û}«5î×h\ãC)3¢=Ûöùi)ògfWÊàyª!@ª7"ËDŒIËC„™ˆ2i¤ˆ@Ó5JC¤šÒÓ¦Š<± ô ‘­1mD~ɬ‚@"ÄE¸Q&‚L/M"ÎtÒPz¯–˜¨ËgÖq¨]~i_9 îOM€æÑ.ë7«6hÛ—¯öñ¸”´¥Ø^¤M«¥ÍS».5ÊSkÀl -ôì‰÷OI¹Ú Õ}`Å(n"U­š&Rèæ°$ cQË¿¼zÊêg¹ýãû㤭±8¼Ý‰:i ¬„“ȾñO,ˆÞ´åß½vŠ÷ØUø'`Bwè\t€ÅÙ-t.ʽ…æó`Ôêá@h ä¶¼€íŒ"“Ž ]û¾œº ÑQ0ÙH³$ÆÂGaAuJA%Ò,ËPþLè©Ëa´[„¶ÚÝ 8ü_°Îæ5…H3ÀqËpÐÒ—0wÅÿ(Ò“ÑüÐdzÓHöô.V]þ„›Zã¾*Äê¡©iOc?g£‹Ë楧¬¤òà6d‚¡e"ÆD‰0q–µÊ¸'-2¥¡´ YÆÃfA˜•K!ɤI&Lo%E›LĘ2fE“¬Ø'cTã„ÉÉ!EÅO3I^ÔdYÚ67Ü<½Íš5ÔfŸ 㧦 •€ýƒ·Ù”ÒSæød#k4J­³F±Ê)={š±Ï½ø„F£{¹MÑ9vå¿YšNgO)×ù¾4é}œ[O…†“Šy ’å×í“/æÒ2én|}ŽE»é^ö×èß´ô¼ÿ† -ˆ•„_„ÌLzv-àý5?ÍnY~üGÓ¾äH£µÅ*>«;&[Ï¶ÆæJÏ|þâtà©¶{š4+òÓƒ>ú_5­µû=Õ¿/÷”%¶–_g-”"«ÕüÜ‚—§“ìéyV]þjûªeúÜpÄrh7‚®Í ù©ãÉ6”÷ ÙÐ ˜ˆ0bú7L+"Î49Ì^«¬eUdŠeûC ²L=döÚd"ÊDŒ‰(+¶ÊD éeJé’‡ò h‹K`“vyÎö³›X+üMMµ@IDATAß³OÇed}Õx­«»æq©ó‚˜ùä:$7QÚŽ¨ýé´îZÝUkÀ¬ÑåÕÞÿLÊ:þå ânk€Tß©½GüDýä«ÿ|«sqÁU¤=F“•ëñ™ ±¯ZÀt{pXüHâ']23V»Ø8'Þ[O ™-® ‡]vàA+5*go\¨Õ"êàŸÎÃYnDWTÄKOÿßÌÿ*dÙ£ã³Ê¤Ù&ÿŽºY¼á{A+ë;\¤e±à£]~3;dî)˜ÍÆç>~5dOïpUßÏjûª‘*lÐUH”—`ßÏK›Š.\¼?y¢ Þg¶1ö&øa&r¬d:§x"Y5‰²j²Å²}-()íí‰2!¦o]D–iOç¯úCˆeªh?³$¼Ä ¹Z‚°B'²Iù9Ù¾=dk=>%ýýf|¼G}ÞÆ>VkÀ´ë‡òlêîœôÔˆðˆï$ ×g¯oÊÏ]÷Õ;ü9~ ÛÙ®#Ü’±úLÜ©c­jˆçø§WX‚f¹aT¯f·Kx.õjQ£ý5®`¯xž¥5›îvÍ¡€?»Œ$²ÌÐþüúOæÌü û¬*YjC7I³MþuÛŤÀo¸ü»Žke·YfÅç nùêíÿ‘ìUUf©5î«*ØqÓÒžÀÏɯ£½ò"е¹Ÿk–ë¸3\‹’?íbVÒ°ÐFäXÙ+$™ˆ2ÉSÙð°I"ÉÊFeE«L¤˜6eOÇt_Ÿ¹I wâóƒ¦ ÊòÁØ\A°à_η"túÔÆ˜œ¤Fšd0Ÿüí’G£¶{òü)¤ð› Ö€i×azÞt½z |íÈìV¥ÅmîY÷·†/d‡ƒ‡¤YFñ`Ôêà~ƒ¦<ªÂZ\ùrAHHø7‘+VÐ×!Ÿ ãÆ¥Akao ã™¶7îùVÃ5É®‰‡4Ë?tm-0´8‘·qû…ü±ˆdMc¶WƒS¤9N¡¹Ý¯•ÿ%T<ÙvhÐ| ×$;/.Ò,¯,/I­N¬ýiiüöíhN‘òÎv¾À9Ô÷‰€©èåI/QÒ8åÄZ²eT­ðæ]½9H#joK6¬¸‘7ÚN㦸@£ãæ°)ýU0 <hI£Lx)šd<ôí@¦aa 9žƒ È™™ÉéäKùîÜ~ñw5Fè9&s Y“ì‡$٘ѸIPõ}.¿ìI£iG6Éœ$»†4á†øAa`lŒéV„“áf‹¢®.V286#ëC_$ÉØSÑÚRx½wD‘M2'ɮɞrvC­Ð –í;ôíþ(Eá¦*7¡zÎl‹›<ËF·hßF^Üà[ÌWÝ”mæ%‰½t؛ɘE6Éœ$Ÿݺ¯n} ¿ G M;K1}%où×ÝŠXjªÈ2²øXè4ágÇÿ†f_ˆAâbîgÙ-¨I{ªhF‰ü‘¢Á„6E‹¬|!Pöx©Éƒí•þP(›Giü&T’‚)ûö[`5[ßFÛÇË©ñ¸o‡¿Ÿãê~ãqy?Aoód§ÈO²T*]ƒæwIŒ@ÛÐXߨù3öPàÉ6ùHÙôl‘6™¾êM ï8q¯9Wý1¥qS?ü¦ÛM\%Óçé ¢ÕFqeÍvw?ölÿÏß|eöÁëŸßÄM¤6âãÑ]À9˜…';äNÝÆµ½ñ¾ñÿøÉ¼µ˜žÈR£ß5IóáSǯ?n‰WΆõÔý~¾nñë @®ôÐm\Sÿøá›5˜EùÜèò§æ{„(SÁ•³áÑÞæØ¯P„çQ¸…âÆƒJØ“D{ò¨Rñ¼˜FD «SÌÉí;Ÿ¶3;º×¡}qz¸H[üþœè˜“ÇZ/ëÙo÷¾6‘jº’;ŠÝÍÄÿ[ûµ*­¢Öˆð©]5ýS´É†–‘m¯À†KÉ7­ij£æ`yˆ#}c:}ÀÕ˜%7E«ä` ^I&ßÔFj+úIæ²W vò9M˜†D„_‰E’ü•/¨ª*Üi.’f2éû~Ì”;/Ô@2úIæòwÐʼäošdOc*FeâF_Ì}Fþ#Ê•ý—w•+wQçyàp\@àMÌ“Ÿ˜ØCë<üj0”Š/-¾}ÓÊxaÞJé´n â¥áý¢SA#ËŸž1zÖ| c›ü Â9_k›÷¦bˆ§@c*I.p}Jþ\Ð* šÅð4äŸ<.3û]´½,/³ÕÇXÎÄþ 'û½wrð`nÚdFÕ/é+ âz †è­N”U„˜ðDÃxº g_|?É÷µQ/ñ/ *Š^.Š0ÅJ!xBϘ/Þ6ùëöU¼OS±HšãSò÷ÅHEèyQ¦‰z½8Ð5sËü<GkrGߨÑ|&œ+/þ7')adÓìy£õŠ^Š´‘–K&ˈ5Qz@•’ gsÔâR‘J{4ÍiÒ´KH¹‘ =íÈd‹ðµ{€Ëß ¹:’µrLUH²ÏÈŸeG¤ÇÓp|ô5û±¨ ¸5Í?ؚȠHÒ’œÄøOÔÒÏÜE€ÆK¼iC²Ü|≮ œSù+þ€ƾø~ª¸øŸ$§Dê\bù¹Rž3_»¸ü¦“©eÙ“"§äïk7¡“ òäŽ@̦MÇÐãf|˜oG•Ç "ŒÝk2•îÈëßï:[?pE›D{yçeW¡l8-•Ž)ì·†3x節M•mô^Íͨ¦Jlé³áí#Ý·µ‡Ëß3©S’lÃÛ3µ9^*'ÊŽcÅSr|ØÍ[êµzÒ.©4ž¶’Õúsnbü»Gƒ”x¾w û›.Aèp&k‡3x)¡¯¶ËKÝ÷Z5 δ÷¥ ´Ë—ÚÔÔÚBc«‚³OÈŸöMíãýiÖtܸñ4j—ï@ƒZdá°j@&–%s_bb?%Žï]BÀ§pGz é†›¯ã@"ÏK~|âYR¾Ü¶jMÖ„„AËKGAû[‚À.Ý«]óÔ‰hPåSž3O5ÓrýFþÁ-â &atIx´4OγA£ A …°[A‘½Ïà̉²[òä™9¾‰@ÜæÍ¿ê‚Bz£vy‘ÒBÔ.÷°€u=zÆx–¥¦òg_Æå½ó6Ê æBàýw8V£N º‹K[O*"Æáï¼ ºÞ‚¦}`åÆzRªkSÿZ.ù´=/P©Ð¦QLPÜ…ÐoQ&Åöp¨Cúö °s7‡ÒÖ—¨Ë#/AûÑã@‚†L?=4A!ÐéÁç þóÕÿé*è>ýÿ ¸{ÏTæ§¥vü<\vßj‡[ߪÓ`·È-ã!ÿY-: „À°Î`1—:\·³ Ûw»†Þ» ®œ° ®·F}DšΩÎ×á|þ²t3žƒ#àD¯^}µË·¡fy ~ȪX½1æ—sþñ¯Ãýûwò‹ŽøF#½®ÝÐõë Acît«÷º„Þ8íP ů¼ %ï}‚kÝÑbwž BH0„Nô—P³¯ãÞ@ã½Ú"¸.o 9 _´:ë;Ž}ÿ!úx”æîh8ƒWÅÀ h¹³‡¿ ˜ÕQwLp£ÄjY½Š{µš«Ÿx­8A[&›úàvÕ[àÄYëΗ‚ ê`˲I°óŸqî±çž}sùYÈßüüõa?ÈøénhÝy(tîy—­m0©×p¯¯žû‹Y_<ž#Àð*äcÿþ+ÍVóç8Zª¨œ]Vn5oE7r»fd}íÕùoe4`ÛonõD7  ×^ ¦›Áp˵ÀJËÁôë2(_ò'è.º‚}݈øh.˜2·BéÛ‚¦SŒ4]£A:|JÞY–]{Ñ?„¾Eé¯CÐwؾ-”¼ö>­ Úáï½Æ?VBÙŸ xòxÐÅ÷’I³iC&”}øÖ]±¨£~øP0Üvˆ-#Àš»O.ÅÞy#@XX6o…’·+*®Ö!$Ë)…âÿƒ°w_­v͹“j“ùœËêùÔ•òw_óÕiÜ `>q Ñ]!<þ(Þ³ Ž}û”ìÝíoyZ¾¬e¥ÐjصpðÃWá\æ*h•|-´ÃkÚ°P¸e=˜?¬Åç ¼ÿeÐzØ pêï ã˜)ò5}ÛŽ èôÐážÇ¡ÍÕ£a×ó÷ÊûÈkþºˆÖPôoûa”`½ ce­ppì…`ÂåÄõœËX †1ÐyüTŠéÆ£àÀÿ½Œy¶VCÚ|ú’äÇlqëÿ€Ž÷=i;wê Â£ˆjÏ™SuŸ?±:òGoѤ]Þµj:Ä%= †(8–³ö¬{³@ÒõŸÊÚä‹oü $f†>¾D­.¼4ÚÅ«Õû·,@rú¾ÜâžÃfAáÉíÞ ê`ïú9ÿ |mÈ]Àá‹ /óè5|´î89³Nìûv¯I‹©â9îÐãVˆMšúàH¹¬¬%ãÀ\^wñcØ Ní_ ÿ®x^Ž·‡êÔÁU+bL¥§ÀXz´†pû$ÎÛËžŽ=pr£‹€7€#ày¢7mÊ‹‹ëv©B*šcX©FôƯrûÅvzÀ€0Ï·‚×`€hЃ.±è/eÿ÷X÷æBЄûd;bëñ“`Ù¾ ¤Sg¡øïñçerÖ ÇVR E/ÌëþƒüÈX[‘bÛ6šúŒL`Ë| Ò‘c`\³ Í*Êø ?ûšÑøfo>Ï|Jæ¼C/€Á`í…Ý!øÉ `ÉÅëX§ySI[¶ãKÿ†â´×AÓ¥3èGÖÖtJÇNBñ¬7A2šmmâõ# C²u×£ •—Áþ÷Ó  u[™SŽ‚Ì•`):ÿ†}oýW&Ϻ–m M)Nÿñä½úšet…6Ão’+ÐBhŸÐé¡àìú?á̪ßàäÒ¯äk'—| ?š-›N‡Ã_¼ {^¢^¶ËãäxÒ>w}a.èBÃáÀ³àÔŸ?@ù¡|ùZç‰ÓÀZZ {g< es¡3šXœ/„ô¼X&þçKל¯†v„^Ã^#»¿Gbû*têõˆŒ½$ÔÆÝó“ Íîu³`ëòÉòqgºÞårØúdzápÁà (²Žç.ÅkLÎOuHVßYK'`“¡}÷ë‘t’óG´K„ÞW¾†ylÃk'÷ý%“a}H[9þÐö¯`óâ!´uè„D¼®@æíºŽ„^WÌF".Áá6«¿º’ûU×(û•¸xc9®# ,ZDù¥ü~ý–Yés$PqTæ» ÌÆ!¹‰‰÷àB&Ȧxð&eŸ.sÖ6Y3¬¿ò2Ðö¾̨é•NŸfì(fj€Úe]nP4ãu΂ñ÷ tÖTÛµéÄi¹É¤].ž9×Ö|"ËD”‰t+¡ü‡%²Zl RþA™¬_HØYI ”¼úŽœG¡»d'm=Z¢5å"ÌÙH®/†ò…?+Eò½‹XK áÀ¼t9·¾M{ˆºóa´eûö³˜Átâ(ïÜ,_o¤Øtê˜L„)â\Öjˆxûñcùº¨7À‘÷Þ‚3k~“ÏÉn˜BÙ¡\›Ö˜4ĈtmÏ€¨ÛÑ<¿F„\ÐW6ÈIŸ…Ùëä4ô£AâÒ­7äÎy,çÎÀé¿@·ñVdl›m®°-=´2"ú…=/Vh4«]ä'Õ8–÷ìËþPŽëpÑh²pÙõ=Öå¸sÇñëÍÙù¸u—+àxþxžW¹åÈD7M(h‚áŸÏn«©D>/m‹tðÙ?{dƒ|N?ùÙàý%B’Ú¢S;¡u—d8´s!Dõ¸,ÆB4Ó ¯¸tUeˆŒ¾Ê‹À±\30œØÿ´Er—ùneŠª],#®?æÇûwÏš—Q«|²ê¢Ÿq¢ìçäÍç8‹@ÌæÍëO$'Ǽ‰$y åÇ}¬+rúÅ¿‘*¬Xaq¶\žÞ5,{óäŒD~)y_óG×§g©y ÉMe`Å% ‰îl#ÊæMÙÊ¥:÷dR<é í±tæ ˆ­Z+®øôªiÝ ¬9ûä—«}f]B/Ð`¾ð¹iUÑøæÁ}JóªþÀ˜ Ï‚€²G"ƒF¾µ í3t-ZCW>«ºVCDž ¤Án1h8V!"ÇG\Ôà±FÖfS¾’vÌa½’ä{®Ë#©¶b‰Ü¢¹H]D9¤G(ù}yω²/K‡·#à!"W¬ v46/)a cÒ<ûZâ¨Á¸©yEg‡œpGç5YGe’Îß§üƒÚqÇòÔ]’oÅVŒ®¾Õ&ÞŽGÀKÄfd}}pÆÄŸJ•Hš/5—IY¹ý†+q|ï}¬Ù‹mZÉf–{¡Ö9ðŽ›ds mœ’ú:Õ0*‹-'òéúõAnU¾uÍYÛQÃÜRö¹,F¶–M1}˜Ög`}‘è‡y$ˆ-"¸ë7§Pw-qÙá|êÖ ÄÀ`Ü‚àܦÐä¡#D^û _ÅÚˆV@\GÙ@S0£2™N´v}EVüT^¼w›l+MDZ a¨½&¿ËÅ»·‚5Ýín~@ÎCæa —Tä³ûÕ·ëݦ¾#›s”äì]Ñ{82ÝàÁyJ ð0z©hÙaè òW†ãyËP›|´êx ~ÐBh«h£ÜÅá ¡Q`,9ÓR,²WŠQýmyOXúv²ÏeChÙƒ&žÈ[.»–ëÿ€ìî- ¸¬‘¶e¬<  ä³Y*—35Ô;j&óÛsN”ýVt¼áuè´nËáØÌì+A|¾j¢D‚UZ–›˜ð"÷¹¬ÎΖb^Ÿ)OØ‹øä-™òˆ¬.JÄÎü1·øþ#ŸlèhÙÆÅËA ƒß~÷Ü ²½refÓŠ5`üe9Ý;D|ü&?t·ü‚6oØ ¥Ÿ|ƒéGc â³·A7 Ÿ£Uòt."pjÙ·èiâôK¼Z .{ 8‚ž(¢n›ñŸ­†>ó~ƒ°¤¡—~nóZôt‘ ½¶.|ù3ôñ½-¯5ÊûÞž†·7ôœû=ÄáÄ>òš!••@Þœg °Sôzçgô“¼:¡W ²Cµ-ÐVšˆ{Ä€apAúG¶-¬W³OÏF€¼Mìßö)\8ô%¸üÁ,ÙÆ®ÕÓ¡ðÄ6èã—põ#y@aChû† ²»z`ë§è¹¢%\…¾Ž» |ò³Ð^¹2Ùý#ìßú t¿äYH¾ôò"ŠX„h½wÝ«òDAò‘|ù˜MÐ&¶¶þ$I÷€›ÁðñÿBÏa3q2âϰcå4¥x¿ßW¿Ûý¾;¼Ž€;äõOÊ$ö5šaØF`$Ï¿ wµ_•å·³3Æ¥ÌXA¸ÌO›–L{™¥Ð’ca¸µÂ­ÝÝ?3÷Sù…wnøO=„ÐÙÓ…½d!¸Â–‘<`¸h!Å%\­ü ˆ¡Á8a°Hþ,o]Œ‰êDïž_ ¸ òBÂ2>šFöd´Mߦkìz¢òó—i»ÆLIù©³éTÒµ9UdóüÙ]H„T&»€³Ï­ o Ö’b´3®ßÔÂ>½ý1i§¥J“ ûxåX†®Ñ̇™«›)“Ɇ§Â¯]o†|±ÅÖO_Ÿ…ÿÒ€f¨úÒ=PMþí& ^à)(är5:|NñŸd©2« ßÈ:}(˜ÊÎbætý´‰â®ffZUO¦Ò3µÊj…þŠdï5ó)ç¤Q¶˜pÜp!¬. ϵÚùùÜWÇìÇps[þ*ŒûrO¸FÙò,¦Š@즬•‚VŸ€ïç¿”>â@}eI ËÊIЬÄñ½÷ý×X(„²«$™Z^/I¦‹V+HÈO꘴#Ç{ˆ$SÕ<Ô@e@~’kò@á I¦r"ÉtÝRXP‹$S<dO’dªƒ‡ê ³=I¦«dl*«Md«ç¬ÿ¬>’,—“HM¥ÄOkpŠ's WIrCeúÂ5N”}A ¼ B vÆã±×Ýt¥(ˆiøÉ©rÄdðhE^büÓ>ÔTÞŽG€#ÀàxN”= //œ#àŸ©©RlfÖ448ƒK8%÷‚1­ÄØlt!÷S~r<Î0ivþ4ØoÍïtXðeŒ+Û&·Ñ;p4·Z„jÏáíKËßóÒ°þ}Bþœ({^輎€ß"€>——Ðgשּׂê»^*b›súõK¬ŠkòGµl‹ÕRfÔhkÅ7y$<ØA£F‡‹zI%vUø¾¶¶X™µÔ(êlçvíå‡n `˜Õj­ixï+8ÛÚAò7³Û¹]æY+03=ºö¶Ôå/¯ÑqæD™ß¦Ž@ƒtܰá.BrÚ-¿¦$DÓÉ´Z\“›K‰5Ï`.7ž-65ú Þ”Ð/62«Åâó“F%³õD©.„Ë^囯DÌÌ&àút ù—³p.¥TÎÂpþ(:töÁÀ‰² …7‰#àkÐJ}q™[žÒÂMèˆWzœè÷NN¿¾Ÿæ''×½œœ¯uÄýöÐËQÞÊJKN•êõB¹Nç~©¼  O|YÒB7¾LBè…~¤\$µz.9• , SSYy~â«¡…£Áü‹‚»B" e,ü Ë¢gËWïjò?aî*œ²D»ÛýfŸð#æå/«”½ÏÉŸåf}‹òÎsÜC .3ë=\;ìrœèw\.‰1Ù-ç&öý?6rdS2àT´ô§œ<ï›Ö,ùy¹d4,ïÙO2k´2üÇ97Âgq^ùÓ÷D”•¥‚¹sz.µÒù ¶¢ôôêŽÉ’WKãÁ5;ÂP2Ï®úåDZz¶|ñ¨-«éôÖòë$ ãòwEú„á‡ÚäK+eïsòçDÙéò<Ž€ ¸ÌÌÕz­! 'ù‘m¡Ð+ƹ'ެ808!J‰k{Ò&)$™Ö÷5––o^¿êÓS¡a°´w½HypÂðÛ½mË«gΜ õo•¥¯j”å{€Úºgë–W϶‚:ç²wRîJrÂŽ0ܶiÃ{EE´>6=[¾zØÆYþ[¶¼Z(EBvùÍ\þŠ@Øn„_ÖÚUŸÒXZ){Ÿ“?'ÊN•'åpêF€\ȉaCEAøÄ–‚Á@s9ËØwq [\#à[¬·7«W>»ÒK¼7òùY–µúŸ¬Ü[ÜÙ¾£ðsü@Æ5ËŽ¡L8^„Ûáüœ«–þL“øW—´¶¾J”©mò=@m¦¶çFtþì2’qÍ2"ã` ¬3Ânÿž_oüû÷ ˜•ž)_¾ªŠü™{ e·3®YvLø„áE¸ÑØIch¥ìÕ•?B …޵ªþTœ(× ¿Âà8@ÌŠå±™Ù÷ ‚ðÚ-“Ö Ð £½Å"­ÈKJxÀ‰¢TO*0ánQnLZ#"Id“M:-ŒAZ’¿~Xô[î¿2YfŸ &q›eD¥@ø|:èr I2;˜»çã%_~B&„'áJø«uPÛ©HøØÝo•¸Í2Jî<0ú¡Ûh‰0Ëßõï7Ë}I&ò³„{_¾ê•?’>¶²tœÄm–>á³²d¼DxјIc'æ°¥*Ë¿=–w´áÿªæüIx ŽG€#à8o=¶áɨ¶«%A¸sá¦AÂ|ýcQíB玟øÇK+Vx%½¼캋G^õVÆŸ’ÆÊÝ@JÚÈ0™ÆQMþ®q±ŒÃá1qÝ·wéj8Ú¢öSÂÊKA+ù¢r[íÅ@~’w·ëÿôèÃVvï% +:·eãÚ7VþúÃlƹÊ^˜$ú£åë Ùîœí[ó4Zí¡±}öFö6œ nÖG‚b)BÙççü$ç‡wƒM³Mí %L,DMâü5Ë~ýÑQäO{¹jÉ?¬m×>‡¤þ†3R'ã„@ñh*tÍú ?ÉG,=á_ÓÕl§q¸PjÒfüó×'ëÿXJ_‘ÈÜJuùOœ5«3KÓQ ?d®úk•;ÜÉìhÞ‰Óf%Z%ëÕ˜>š , + u4/OÇàxÝ‹HûŠ5î׈šeïÍx>ÓÕÚó.IˆfFö#’äx¥ |ö kñŸÈ+H{äµ@cE²dˆ¢xϼS?w£b3‰ÓJÁ¸EàÖ¢r Ç}H``pè «G ‹îzÁP­^†XÑÈBÊK½ÕBù›U e¾iCZL„þ9K½ƒù¹¿¯]ºø—’’BZ`€V㢭7{’DdÃC½÷@ppXëKF^s]§Ø¸+5:}8ÉÞ`.eAæbA/™›ìIx´Ô7­bH‹‰ü-&cáܽ­ûmñòÒÒb’9‘$ºœ’þ%0ç`jvò§e¾iCZLD‘ýþ½»W®[¶äï²²"ÈôðˆüÇOK¿[’¤Ï´¢6É÷¶— ðPHMýÈpØ|xjqÇhqV¼„¶"Ç‘(ÁJ½ú’ôPy±&‡f!²‰‚ÀÚ¢JLÄâ>·s;è:¼š:ÆiMì‘ÄÄ 2°~Ždù&,œô·Íu8 ð€çýC)3bÝÛç§¥Œt³>Ò$Ñ4w"Ëd÷†f"ʤ M×tÝ{'t‰îÞ£G`HHK}``¸Vã[K¹…šÊ£´RÅòb¥:ÝI$µôòR/³XÌFSYù¹²ÒâSöîÞ¾+;sV@„XÑ$Y";Bz/Ð=F¦þ MnðèŸØ½s· z…´4„kµ:=Ú"©þÎ µG ŒÉ_‡K´A‡­‚†&CùL°X-寲²Â²’âÓöìÞ±{ëæ¶ÌåÂ*2*˜Ö|Išð2búìJd™ötNñÊä=%/FùEh´{ X«ÕÌìòF¿dÀâÃÆG—*Ûí#¨)rlê÷€Óò×jµâ㣭ϴ†áЬNŸƒ_ßúAó–ÅbQpS.ùë^é‡WåO_/ǧ¤ƒ•ÞŒCû(W¾€Ö¸jƒµÊ/ ºÚÊã8FD@­Ahÿþ±f«ùWTÊ䌺„Ñ×bX‹1äbÎÓ]ôÀzÒ+ŸaIÃLäXÙ+$™®Óx«lxèáݸ˜{£ž¦ÖKÖ5·ïΙ¨BËè©ldvA‘a"Ť9VöŠ©i’ý9xýx¦Wøà„–š÷4ÔA»¡x„Éd"Ì}%(ò§}S¿’pp€fÚ½æôP\§é\©°0õ#mºÉNiž;{¯Ê_-eN]V…(+Ÿ4±‚³ ¼„k’낚Çqüµ>kå&&†3°|ƒTêj4ÅØÀôÁ×w]»ö„穽LÄh,U^–DŠí ²B’é:UÆÝŠ¢Üÿܱ]ÇÑ-Zn ’ú¿žsòLŸŽ¥™èî…°)¦ QR³r®\w§._ÉëÕ{`Qrëÿkàê|‘™½ûÊÓ3|ˆÊv4·{ Aù·n¤™5¶ìMCܪ•…ÏžxGÿ|YY™‚•‰Ð­æ(}Ržqå™W} PË<°¾Þª2`«}Új¦Ž>ùØ$#Æb>œôvËɧNRpò1ñ¹Ý¥_´W6åùWȳ’ÆéÊÔœp~¾ÊU°Ut»t¾öò뎀  ¦ë¼~ñ Ó¬¹hÚAšWšäw²7 û¸Õžîª7ÜXâ[7ÐÆ[•1×V J—îÝÑa`ÎÎ8*îp‹Vg¾˜¼ÝÝ¢q®Š­ãh‡înq~_í{ kAn‹aû~ëM ”kƒÊ?ë5f£/Äïü§Œ·~’þËí´»Ú’ŒÐ.¾ÑûȦ²[r|Qfj¶ÉòGøTuaêHéó [¡Ò‘GŸw« ž™#ÀðÈå#¾ü?£çß]gî±›³ßÉMŒß‡ïš„Œ/’–hð{^bü½¸$ö"O‚R9#šÜͱ_ Ï£p#—on{’hÿâp»` ØÙ1úäàÜ]2QîtîlDˆÙ¢)ÕM1*  ö=w.§µòßãPD—“èjÌö§D…æò"TB€dÔßPE’©Ø£ÖÞG2£÷ŠôW8Š™2Qç°_ëæ¢XŽVî6Q¦÷P$‘ŸdG+åé8ÿF€žwV"I•+nºýÉ7.3{qN¿~— ‚u1eZì½G°oò:ÇffýÏhU~·ûâ¶z¢Žû®Ã¤qñ ñÑ?·iËGž¨‡—é,5Y›q.÷˜RJ÷3»n½ëÓ>©QVÚØ÷Œ¥ŠKæLÿ$v™ÒT0¼ÿà [~È=o;Jq|ï%ÔøOÌþ8_LÄKãÕp|ùyÇç›­VsºnÞœ)ê„øÇ{•‰*2AbÒœœÄ¾o²ÔT5Æ*µšÚ$Ë@´iï™nk’lÚ¼y÷pü~ߊº‚Äë@Ò/G75n5©.Ø‘ä{”ŽI9Åú0îqhãÁŸpûåƒKRGѲÔþÔiÞVŽGÀ}è¹§çßý’ªJˆYŸ½Oz š(¬´Å2x4÷—¿;8hP -ލހ ‡ïl… ÂùÉñ´,7>†®Ú6ÚÖ$ÆrâeCÃ'8Iö 1¨Ú·‰2šà„âV¬j«xaŽ€Ï#@Ï==ÿj74zõê³±mÛ_…å~c+›±M¦Ò¿^šÐÆÇTE vmÖ~ôϱ^.”1TÂnRµ^˜ÛÙrƒR®liû  Äñ}ã!PIFµÿ<®In<™¨Q³ÛDYFð2üôz²zÜ´ôý©èÒpÃø”øS››S[…¥Kh·|'j–_UúvËJJÙºÜñÝ”8¾Wn~¡.žj—ÆÍ.ÔFT½òê#É£¦X'r­¿z87FIœ(«ˆ:ºÌúý¡©3Ø„Óm‹(Øÿxzz[º>þÅ´ëìã=NMe>)/Ô*öEcÒvŽöCI7aÚÌÁ„mZõM‰·ß#¡}oÜÔ4õ'X ,žèt›íÛÆ=‹½\â6g?‹ûGp#'õä[)ŽY`mnÿxÙç¯g[ÐüJçæ¾-snvá›òá$Ù7å¢V«|’x©Õ¹Æ*G²²÷Ÿž=;Xíú[Òö?šúJGµËmôò ?-íáFoo€O"€šåwQ¼ Ér©Ü@ÆZ3+û+/±/™gð "ÜüBE0U.Š›]¨ ¨JÅq’¬>\ 'ʪ Gø‰&8+OW³è Óf% €¦G’ $¢ByÖÃSÓ:©‰/«é »ió/¨`†ö~§*{"ðknR‚¼ÒUÓéiã÷ÄÞü¬BÕıÆoZ³n7»ð=ñs’ì{2ñD‹Üö£ì‰Fùs™¢ÀöILHG²üßqÓf~5Æ Îןq©ó‚À|òôsÚ´Eâ¸E#ˆ)ïÍxaå2ã!‰Y¦Ó±Ñlþ—Þ5£K¾SA7ÑÂÌ8ë™M™Ÿ6m9]§ðЋi·€^Eaö¼S?¯ˆ•ËI¶2a®NÐÝñnÚ³;å´Óf^&HÒ4¬».¤eÆú3´ðœr]É‹u®Ç:Ÿu`’Ìð"¦OÆY/ÏKOISÒØïš–6«®Ñiî|?õù}ö×jë´Ú©f³u®nð^»¶æõºÎr´Ýééí¡”ÍÅ2†à†¢aktZýã‹ÑT³ÜóÉAI_±ô±x3Ú Ç•Ö³ˆÍºyiSg“¹€’ŽïÕE 6cËÆ¼¤¤!ŒY–!îè–’épû"'1¡M×̬7Õ­­ù–Fæ¬þG AÔpò~³"» ù"â=çf¾!¥œ$+H4ý=×(«,cÔr ÛÌB¶´$éÿRS4TŸyótÌ|r ¦¹ DøyÖS¨9²HÒßã^LFy­‚f¾±ÊåìMQÓp¶ómµÏìE’Ö ·Qò5åÇ wa9¡Ýï]JíqqõQH†;'÷‰ÙC禦Ý%0ë_HÏâð8n)¨Þ°€)cü´™ƒ(]è‰yG IþËÎAþ¤(j–Ú]·ŽOI Ì3z>’L™DKqåª'ü\ƒ“ÿc+¨žGÛýDêë¬LÊÀ?±üTÄIØÇ£³ij°¶ªàˆ(õø©iCq·”(  Xæ4Äì,j:»p’\…§§Žb32vèÙ`”ž¼Ä2>g0inNR¼ª_p<Õ~(—›_øž”¸Ù…oÉ„“dß’‡§[Ã5Ê*#ŒoíÀùãÇ›'¦¦=hµ°5‡Í{žÃ*¦×W ;tò!ÔÛÄkub»©S³+Ó}øPJÚ>$Ú¯àùÅÌxះ¦¡fTb鵿¼•ú졊t/j7ÿ&íne>˜üÚk%gJ®Fb½ãî{8õwS©pßÇ„aHrÿºí¶Û¬dC]X`œÍ@øæƒ´9MMýû³#–Õ«Q{ñm\ü¡JCŠ^¦"ùñþô”eJ}5÷äýB’XºFÿóþô©õ¦³Ïg²˜Q#þ%Nê» —E~c\êœåóSŸV>±Û'gÚ]b.y yT[A¸pÞŒgöVô#j»‹KúS`+Û9PbüS1wg:hSƤ¦ øßC„5^B Óº-‡÷2ÔRVü Þ£ƒåj%öBnbß¶±±ÝÇ ‹ULüóR{šb5d~Ê}«0¿ø¨)öÓ_úTÓì"‘©þ_ß_ºâ÷íä$ÙïEètPÁƃÊȘ¾—š²½*¼ƒZØ©ãSg^T_HCÑÆRȶ#ÉrRt‹õ$>úæ›úúòR<ÙßQ;Úgrêk-é¼øLù•¸ 6ëÖ™¥³²Žq/¿Žçý(=¥+*2]‚Üö¨½~›Î•š:̂彃”ðâ‡_z9F‰§=¶éÏúÈ/*hÉ›Ç HtßGmóÃïϘZ¡·/ žcô2f‚V‡ntX XÊ^¯'©SíÆ²®ÅwÉßóÒl$Y.6@#~V³|Gå €f+þah}Ôœþþ©à4ké¥sòµx%þÕùE©åò@nÞÞïò““qùkÜAÀÞû…b~áNy<¯{T3»„Eö ÷Jæ¹A€“dgÐj:i9Qö ,uÚˆðE~ŒY¬h‚Q·k7$«±˜&µ©;ì7Ԕ݈/~Ñz¦¤Á næÒs–XÊÉ$ɬt#j˜×ÍO}îžn@Íéõr|©e(•'j4ËéœÜlÑN£ÓçÐÞ> ‡Ù4Ãj±tµG¢­h¼«EË'ŒuA’ü9g¢]ôüÚ ÎCmF,^ÀvÞ>ŽGPŽZ/çÚ%(šd[ m'•ŽÊa^Ú ß"ùNGŒg1¯Ù‡îë^|tæL¾FM@½pÞiݺ²Ø¸î7á¸*m'c7HEg—óUåÜ7¿p?5s×2»`à°BÍv4÷²8In¾w'Ê”=™<0Qœ€ÄoÐQKÚ#&Q4Õ¬N`B0²ÛLœ¼÷jµMžCó…1ƒp²fûówSŸÏA¢°Okrg׉LøQNÄŸ‘xŽ¢x$±Ã0]Þ{©ÏçÙçô³ý¹|¬arÚõVÓf#i-©•¶2‰ãxø3i¢Ñdb}éÎß^›‚Úla=Úz¿Of#Hèk· q°ÝÌo¬Yçk“'—c Õ>Ï;#4I !Í0>G|Ÿ0–Z÷‘ÝtÍzø¹ç 3 ôµo—JElåÁ QJß;÷~áSíf¹æ»ªæâ/’Õ4ܾL:v\Õs¾•úX!ŠôM4ÝhuÌšÛ³úU~æMP³üJòüCh–ëek±˜VïPïo¶Ïßê"ó 43¢gˆLµtR »Éßúàïíåf+AN’_©k”½ ‰·^xá$Ú²NƗͧÕUi2!D—.›ïAÆŽO™ùê·00Æ :w:tÓ –6•ò\Ù»ëêß·î>Ã$é)$ÕO‰:Uñ~!iôT.¢&ÌÈg°- yþ ã'"yeAºÀ¿” o=ö˜=iŒA“ŒOq‰è“¨…FW’`&¯·  º{\ÑŽ¦¦Ž)—:óvf–2,–3ä¹êS•R¹ûöšîé‡-»Gã'# ›•,δ[«Ó¼b1[ï+òHE¼ÐjÙEFÆÎcÊý‹Š’ý=¶±Z"~¢:œ$«©ßȉ²ß‹w€#Ð8ø:Q&TÈ>Ùl¦I´¬ÂÚ/ãg´»b3³5jþWknb“øE^ÒM––¢yË(ÿë…ÿµ8óúö#$«UþS‡ò$þr¬ îk}aó¿žùn‹9Iö]Ù4f˸éEc¢Ïëæp<Š@ô†¬:îRœ”–/W„“ÒÐvù뼤²ççÁ¸ù… y 7»ð¨ ÉIrà4óKœ(7ó€wŸ#ÐÔˆÞ´)Og‡ ñÅê+ªäЯ¸ôAnRßÉM½ïjô{¿PEçÊàÞ.œÃËÝÔœ$»‹`ÓÎωrÓ–/ïG€#€t^“uÄ  ÀÉ|‚ìùɲÀ$x =d¤r€A€¼_T† ïÊß{Í›v_¡Ø&“Ù_dÄ WÉI²ç°m*%s¢ÜT$ÉûÁà4ˆ@ÇO·Ðé/G;Û•JBtøßœÄ¾s”s¾¯n~Q7.žŠ•é6[Ù‚°ˆÛ&ÛÐPõ€“dUál²…q¢ÜdEË;ÆàÔD Õ† …úÀ4)ÍvÁS9ýÞBÛe·'7ÛÊlbÜüÂ{åfÞÁš“dïàÜjáD¹)H‘÷#ÀpNëÖ•Å šW•Iš”›”0“å*Djqó‹Ú˜¨ÃÍ.ÔÇ´f‰œ$×D„Ÿ7„'Ê ¡Ã¯q8M!3ÓÖâü¤ý•­ƒŒ=”Ÿ”ðKMå㢠”ªn~Q……'¸Ù…'ÑÅɼ,U\:{úGø§ø^[M‚0oÔëDnâbC„Ø!À_v`øØ!}&ù(›ùæ8 n´çŸÔª# ¬Xa‰½îÆ»EAøD¹"1v_ÞÏ?|Á’“µJßW ÀÍ/<'Ô4»@ÿ,Ü߷аs’¬"˜Í¨(þ2ða™³‘ã{Ÿx>Q§×_‰1Ñ íEB|§©þÓ´:-‰EŸ`ûqâÖ²_MÍÀÖ“Ó~Úxhæ©©ÈÙ\TÈ·Ä8‚oŒ;ò ϰÄÄ;HóÜÌ!ªÑ}2¿äUº°‚@IDATú ÂûÅG5ðS7Í.À¶’äÄq%>t΃ûp’ì>†ÍµN”_ò 9Ö$'ßÔëC a’$±vô(4È E„ Ž4£<8‰@¹Ù,–²¢Òr ÑôñÓÒŽYÌÖ74gØ[óç§–aqœ0;‰iSKNφ è*ΈwãÔ?¼)nÎË÷läÈ[…¥K‘Dó@ù+y•>ü:"fEvGGd³ eDâÞ.ÔKá$Y5(›eAœ(7®Ø‰ü’9…îÎIO]Ñâ]Toµ¿(¶ëß3zwí ôt×ÿ`”–a[ÎaØôo^Ûí9‡^ÛŠ}öż2}-¹^<ÏÙ¨´M|,'‘È2{šúÄ\›wâèÏ º‘&6…~ºÛ2¿ÈMì»±€é¤¸ ËäZewÅüdv‘‘±ó[QÜìÂ…;œ$»ƒÏKp-eãܤE– 2î ÷<ùÜäðˆï¢"#"ŸºgLº}¸0 W, InœÖ5ÁZ K”°%ŒÛµo«Ñê~}ð…—hu6’ÿ¾Ùåîl—ºffOÁ[!]É‡šæ«Œ¦²ÅÇ®º*X‰kî{Ä*»Y¾øˆj·CMo²Ù…j¥7Ï‚8InžrW»×œ(«èùËSL-t˜Ôp×ãS&ƒÓ’.Œž¹ïZM·ÎíÎ_Oá„ñ³÷_«!ÌAœóÀó©D–éYàdÙ-d›Fæ®›³SðN˜fë cÃJNÿíäàÁ¡¶¸æ| ‡o•î+æÊ9ß»Ž÷vá:vuåä$¹.Txœ+p¢ì jîå!Ìe’|ã˜ñ£‚BÂþ‹„=xóeB€Ž[Y¸­ã¹ kœ°EÍ+÷=ýÂÌÍŸÇ!lÒ)»fnIÃÿMÏ(DSƒ!çÊJ8YF@¸÷ å®PoϽ]¨‡%•ÄI²ºx6÷Ò81ðî@x“]¸¾sçî-#£:¾Þ±m vïuC¸&Ó»r°ÕFØwˆlÁ † F޼‹>¯óg†Nó>@ÍòlÄÇ«P`—p²\7¿¨º+Ô8²7»@/¹Ù…ë¨r’ì:vvsö;5Èò Ô,7k²ÌÍ/Ô{¸Ù…:Xr’¬޼”ÚpBPOÄØk“ —ޏ¾?Ú?¶FpÜäÂh»P&É=´ýУcv®Uv朅“åÚÒåæµ1q%†›]¸‚Zõ<œ$WǃŸ©‹'ÊêâY_i Q¦I|m:tL¦Okè'¹¾ô<ÞË,H&A¡¡WbÕœ({¨Ž“åRâæ5qí”4ZÉÉÍ.$ßs’ì8V<¥kp¢ìnÎæ"œûdƒ.@ß!4PϸŸdgaô\z’­‚¢])+þlxn¿-™“å*ÑÕ4¿°ÂUWù‘#ٺثÂ/2âl¶4u‘dü³1ÔëDR|ØòŽ€p2àxNd%œÉö•&òé5:mdDx°ß›]”MA€& `±XJÛ˜‰p©pQ…öØn§Ü˜‚ðñº9Y®½ùh·U]áGŽ ÀÍ.A©î4õ‘ä‘S¬8I®3ëœ(»†›3¹ˆ+¦2YÖˆš ƒNç0Q>pô4LHÿ;íL½I+I ¾Y¶ž˜óLžó%¤¼ó,]»­Áºü´ &½òìÙ¬Átj]ܵï(”ÍNG2!3*¦ËÈéÊx¿F€È2¾Œ©êkžü¸ùEÕ-àÂ7»p4ÌÂI²k¸ñ\®!À‰²k¸9›‹p&òE’eüØæ§A@Í+L}Ì™| Iè?¯Ø §Ï×Ù#£É[ö쇎m[ÂÆíyu¦Q3ÒbµÂûßþE¥®Ëljø³á‚Í&W\fö»Í,só ×ownvávœ$»†Ïå:¤áäÁ³)V6™,£%‚jDyEæ.økã8W\D·ƒ‘ƒû@L‡60÷ËåÕ&F_9@îÝÑ“ðö7À˜.…èö­`áòµ{?hµ¸¼ÿEpÕÀ^rº/—®“Iíé‚bX•µ¹ý ˆëY ¡«õ¶÷Œí?þY¯7{ÏÐé`Ô¾ðùâµp爠ÑTpЃÇÎÀok·Â޼#˜F ZŒïÔ®L¸u;u>_²? ‘-Ã0ß ˆÅ~‘i±o~1j²·ÀÙÂHìÑnÖ4¢o}ý‡Ü–׿\Z4d~ñqsè·»}”Í.€µ’ËQT{5¸Û<ŸÌÏI²OŠ¥É7Škͼ#b…tÑ^UÌ[†ÃMÃ’à©»G€í€¯Ê–{ÔIóÚ­¹`µJòyæ®}`4›e½róÞÿgï:૨²÷™÷^zOH/¤Ñ[ ¡„Þ¤)M»â_iv×µ¬€•bwm« kwWÅ‚4i*EH¡†’¤'Þ_™ÿ9÷e^^B)¯çž_&ÓoùæÍÌ7ç~÷\8‘– fŽ…™cÃO»’ ¨¤œW^U ›þ8 §2²áÆ1ƒ Àǃmoþ/3§þ8rþ»í`h5ÀÑíšÂÖÉ‹<¨G(ô •Z…ùfëŽûeo2#Èk}#ƒØö…7có¯¶ì'G;xüΩìëÿÛvPwy¯¿ùu? ï³Ç a„þع,F”‡ö‹`ÇÍ fÖÓÎézµó4~xWD ¹g™†»®¼R¸)+>Þ©KàÁåºÌ\vÑ>Ø8In^ühÃ!`PÒf¸bÙdJù¢¹Ál ’ÐÁ½ÃÀÃÕ ˆŸÎÌe^×øÑèY­gë”ÙÑ3!®O#“'Ó²``ð÷qƒžaþÐÍŽà~ÉjëTðô½3`Ò°¾àâä mn2ÿ=Ïä}®¨ªƒ"›ì“V*t§fæ@LïîHˆåH†Càð©ti7œ½XÀÊìè`=°Õµõ¬|U5uD|tL/ŒDá#FyŸ¯”VèÎéÕ•¤aè!OÍÈažêP”x…vÃ4tÇ·sÁ ×¨yóí"ËM†»Å õõ5ÄéÓ[¾¬°Ž­™Ë/ZC¦õí\vÑ:6-íá$¹%Tø6S!À¥¦BºI>†Ó(“ì!9õ"h4F©³M¤#î ɧ3!‰pNa Ü5c$¨ñ¸s—ò!=»’N_ЕªàŠÖ£L¢CýÀÁžB>kíé}‹„¸–­¼úø|$åΰüÁYP„6Ó'¹ÃãwNÁü´^a鼤ÔLV–ͤÀ¶ýÇ™L¢¼º–I#ˆ÷ „=I©¿˜y§‰“½ÏÈþ›öII…oË.,oêk(é¦Û‡aÝÐ[®Ò­óŽ€9 á®Ó†Ä 1_£üQŽ1%£0ïGqÞ¼›…õëÛ"Æ7@ž\~Ñ>¹ì¢íxq’Üv¬ø‘ÆA€eãટªÑ¼“ó.£Æ÷„ޤîI< ß¡öXR@ÇІo·ýÁèeõõrg_*˜B.‡ñ±½aîÄXýrê–Éû«oKn¨“p¸:9êv9£·yÌà^ð#J7Π'»9Q&ÙExP7ÝvÒo?x½×`ä 0mä@ø;ÞQ4ŒP#= :”¥íè ýY>䛼ÅúFi‘¦Ùf´kg‚²ó,Ì€@ô‘”×3âbìñcu%e¿×Ó3Î'Ž?OØ»×v¿æH~Q o°: 09s|ŒgÄÞ”R3\«È’Ë.Úv™8InNü(ã"À¥ÆÅWJ—þ$moל¼Â™9—ÙDËÅeÚÈ^¨S&YÂãiÚô´\÷  ˽Ig`Xƒv—Ü+ ¡â,†Q#3¥%i”[*uæ#YM$‹8|2;VCF– ÎxõèÍ¥¨úF鑎yÚÈ0{ü6ÍAÝpÏîx*“º;1¢1m"Ûäõ¶ÃŽ…d‘Á~àŠ^â­û±zÕ+Õp2=‡í»Þ?êøGjòš“Ç["Ö×;¯q“Î|›ùG D&¥¬Âà0/ëÅ9é¥ß g¹é¨îë_àò‹¶_C.»hVœ$· '~”ñ0‰[ÎøÕè9èK‚°ƒÛóÿw#¯/®Ý€Ò{FFI7,E´†í~ÇÈÃDI›1bÄP ÎrÿÂÈD$ð\ò“×ùz¦5L.ñÕ–ƒØ9O è1ž;1ŽuèÓ?÷0’a’oP'>}£Îvß`4 "ÚÔ¤#Ô;‘cŠxê‹oe<Ë0Âà~KnÖOªÅeÒT“·œ¢z|»ý/xóÉ;XýZ<˜oä Èäc/¤£ Cñ–…(ÎÏÈ8¯î´=l”·¹’åò‹¶!Ïe×lj‘ä×^úïŸ{¥£Ñ²Ž&"¡Áç¦D€eS¢ÝÁ¼H~ðѲ-žý7ŒvQW¯ÔiŠÇéÕä8/<È—MiRŠƒL1ht=”SHQ‰ˆ¨^Ë<Ý\àÙ71‚Mž^û–B7b88ššÛ蘞ØI¯'’ãK°¥"«1|鎩kÜ ûSÎ3¢Lñ^Zz3+¥áä@ƒ’ WañÐüIÚ] ÿo›:¼AV"°N„MvòŽ€‰ˆ:’òl:Ê0Døe‰¥welÜPóð¥ßÐîc¢Â˜".¿hÊ\vqm˜ôHò}Ò‘œ$KHð¹9àÒ s nà<õ;ÞIIÓh~;†ƒ'Òà–I-k‘)ž1‘f‰$Kç¶eNò†ÖHr[Χc¨b Ê7H6’Šç ¹—¯’pAn$ÉmMY«an®µnûÙüHŽ€aˆJJyï•¥ÔÐCvFìݺ´Ýæ\~qý«Èeׯˆ“äkãÃ÷š–Ýæ) ÏՀܢ ¸î½itg¤°DM“„úé|Œ´±åOÔ!—Uyª©sá ÷Æ#`KD&}˜:ø¡ÊéªzgĪCyÆã¶TOª —_\ûŠrÙEëøp’Ü:6|yàDÙ¼ø-÷{ne´´ •ðÔøþ@7Ž€-#@2 Ô&/Jßø3é‡î¡ºbÇÇÒc×D%}ΦêÎå×¼œ\vÑ2<œ$·Œ ßjpé…e\^ ŽGÀ† |QQ=ïÇ*~'US5Ïf ‰y^Z·…9—_´~¹ì¢el8In¾ÕràDÙr®/ G€#`Ãà #ê(w¯»±SÀ©šW!Y~XZ·…9É/¤z`W†yÒrWŸ3Ù…(jà BVì†| zßµ“ä®}ý­¥öœ([Ë•âåäp¬t$Ê/ðv„Re°ƒß{qƒua°¤íV;'ùEƒa„6øˆ´Þ•ç\vÑôês’ܾf¹p¢l¹×†—Œ#À°A„_­séæ7»¾± ç'EÌâ§iC͵…ê6‘_`u9`]»¶qÙEÓëÏIrS<øše#À‰²e_^:ŽGÀر£Jî3°£_ U‰2Ú÷-FǸÁªËåM¯"—]4âÁIr#|É:àDÙ:®/%G€#`cDìM)œ§¢Ž÷lCÕ(„܆´¸ËYs½kÁåMj"»a=EBir@Yá$¹‹\h«&'Ê6vAyu8ëA úÀBG™ý ¨Y¾H¥Fϲ3R¨-ââ[O-®.)—_4br•ìB.~߸·ë,q’Üu®µ­Õ”e[»¢¼>Ž€U!œ˜˜eï ›Œžå|*8’e•Fµ=36¶·UU¤Ya¹üB —]Ðo:Aöëk/}ŠWù°ÔÍî¾jùp¢lù׈—#À°qBIPLA²\¬­ªè«Õo™#b­¶ê\~Á.]W—]p’lµw0/xœ(óŸG€#À°¢’“O QžŽE©dÅ!D­„bc- xí.‚V~!H±‚»dô‹®.»à$¹Ý· ?ÁàDÙ/ /G€#Ð5ˆL:vX!À,$̵ QŒBÏòެøxokDµ×:=.Ö©Ë >Ò•eœ$[ãËËÜœ(·„ ߯àpÌ„@xò±= Êçá~J*FÂè__W³­hÔ(73©ãÙvqùEW•]p’Üñ[†Ÿiyp¢lyׄ—ˆ#ÀèâD9²‰ò½€† ÀNPCËjª6eŽïhMÐteùEW•]p’lMw(/k[àD¹-(ñc8Ž€‰ˆN:ú-J–4f+ŽÓT”þ(ÆÆÚ5n³ü¥®*¿èв N’-ÿ~ä%l?œ(·3~G€#À0 ‘GR>– ²¿K™aè¸é¢ê+1!ÁzžÝ]T~ÑÕdœ$Kw)ŸÛÖó°µ5äy}8Ž@ˆL>ú¦L€—õ½-}Ó†µzë½Øå]MvÁI²Eß‚¼pD@ÑÉóùéfBàRÞ8•‘Åe•PZY uu*3•Ä|Ù:8(ÀÓÕ¼=\¡_d0„ú˜¯0öBzlŒ;z”gÙˆâƒi±1¥ÑÉ)O1[ƒ%Mò ÔY£¢_|n°Ä-0¡ääs±¦ö$Y±òSÅmÑ8I¶Å«Êë¤'ÊúhXø²J¥†ÝI©°ûði(­¨fÏ]7'Gpsu»®w)«ÊjábÎe¨¨©…_öO7g˜8¬/LŒë …ܯ&/G }D&}2#v°;ÎûÙ™¢ø÷ŒØ˜‚Èä”7Ú—’Ž&ùE-°r"ÙŸœ9>Æ3boJ©Jbš,5êùRFø‘°^p`r4N’mð¢ò*]…@×cWWA`N£÷øë- ¸¼ zu€iñý w¸?89Ø[GŒXÊšºz8s¡ŽžÍ‚Ÿv%ÁÞÄT¸kF<ô‹ 1b® EkÞ‚ñÜ9B@øõ×:AÏFâyŒ%)Š ô5ÿ9dÈeaœdºJô —]p’lœÛƒ§j¹p¢l¹×N¥g#ÙK„Ñ!p×ôa`oÇu·m¹\„áE¸ý´;NœÏµZͽËmcD%'—ÉävÓ°X&[NœÕ‚fó…CúXjºBô‹æÑ.P-³ÞR¯GGÊÕ"IáãéO«—ت»#8ñsl N”-ôz*•*øfëAðq‡y“c-´”–],ÂÍßÛ¾Ý~jjê™g™K1,ûšñÒµˆÄÄ|{{ùŒ¥PÈÎÂ( *¥z{öðá+ηuùEsÙÅù‡Ú~E-ûÈVIò3êÅœ$[öµã¥ëœ(w?£½;¤QǽÙcqOrQ&Ïòœqƒ #„ìN< ôñÁe“Ÿf‘„<’&‚|PÁ (Bh­²nûÅÑ£½,²À¶.¿°QÙ'Éy7ñB™N”Mt{²!2Gaà(º×$·¹«%üÇ?‡ºúz {œ,_ßb½D9’,Èd£^[ ±¯²ºrsV|¼“¥ÕÊ–å¶*»à$ÙÒî"^S#À‰²©¿N~$ ¸€±ËÐ J‘¸u±¢ª2²  ɲZ­ázåÎÃÊS° ¢î”Ëå÷¢ £¡çª8²¾¾ú{qüx‹ ŠeÔév± $;^[”]p’Üñß?ÓvàDÙ®%y;© âDq’¹u‘ð<‘ÛàUæŒÎ£ÊS°4"|'È@;r¿¹oʨ(ýØÒÊ)Øáà# †Ž6øˆ´nÕs“]p’lÕ¿F^x"À‰²ÁìlRäM&oçåÒ  ÷Œ5˜HNa)<ûÞOlZöáxóëß ñԅΟ¯ÂòŸ¹o´ •áèŠx^Áá¾kkë ^©ä^eCËÓ±("“޽¡•WI…ÂgÊ‚ô!1¯Jë–0ø+åvþ:ÜPiðK(Z‡Ë`k² N’;üSà'Ú œ([ÐEÕe5”VV³a©]´;¦…ûnŒ‡`_Oøåc@#ÜuÖÎ]*€­Nt6ƒŸïŽÃ|—WÕ`ô‹ZìÔGD™‡‹38Èç œ([ÐÏ@"nDèLi7€!Ý àTCg>Éîdf.78'Zî¡}§Fû2’™˜z ±}ðxF“¢úy¹2Ýq)˜Bžé°pqr€]8$7É-êQã|æbA“sLµBø’é1'ɦçcD>|NP3°0•T ¼”µâ–¢Q£ÜÌYÀ&ò €ùæ,KGò¶fÙ'ɹâüœ®ˆ'ÊvՉ̙ÚÂ}˜&yF· ";{ì õõ‚O7í‡uþ?O7˜9FÛÑ®›§ Œè Ûœdñ—}‘“6Y²¾‘àïík0‚Å·;’˜'úÔ ‡ú•/·ÃŠ~ÊÇ ÕdEÔ}Œ˜«P|Î01Q‡&ɸ£a()k¼bÊk«~0çè}V/¿°RÙ'É&¾ùxvV€ñcY5<¶Yø`?OxõÑ››TMû÷Ü=c8Ó$±´S4•FÌÃBÊ¡7õÍ2˜1j€.-Øã‘ù㡽Éé‚,"¸<}ϨŰldŽöÚíØ|U9ØüG€#`"“íH‹Yˆ‰Nàý=%£¼t.þ­›ÚH~‘sËA {µV~ñ…©ËÑ‘üHv‘””ju² N’;rµù9]îQ¶«O„ÔÒŒôÊÍI²TFÚG$¹5£poØ©¥Én"È$¹É.³­X"îfƒgÜ%ˆNNùÙ ReQˆtz\L‚´nê¹µÊ/¬QvÁI²©ݪ?öš†ÒÞ²ï8œÊÈÃKan=®U†+Ï7r8íB *ùè+鱃º£Gy ;Q#>1–/FI¡üŒj$¿@w6&—çcf_5Ãv&Þ¢ì¢ÙÀJíLÒ`‡s’l0(yB&péE8¬pßf3‘@&§^„KÅ-VÀÓÕ ¦ì‹o *$»»¥²ãê•*8w©N¥çÂl$¯tÜ·;!ñôEFJ=\‘ŸÐ¥yèÔ8s! ùP¸ax_غÿ\)«F„ŸOP?/xpöh$¼àåæÂÎ+)¯†ƒ"`4ë¤3áô…<¶F÷£á´ïÁá²çßÇÎeÁÉ´l¶oûÁS‚ë#EÁX¿ˆ nl{ke`;ù?ŽGÀ DFö|ùßf)1\þ7ŽÞ7CZ7ÖÜâå*»à$ÙX¿Hž.G€K/,ò7 PÈ Il[Œ¼²ý"ƒ˜÷vãïÇZ<¥×þQAàæâQèå=—UØdÀ)Ãû°ý#úG²í‡ö†ÑÁ0vp¨¬©…Ââ –îô@÷‰_/ˆ ö?o78‘–‹Þ`ôý e–€½Bcb¢¡›§–(ÓöÑ!0¬_’ë>‚Ä8=È’Á<ºzƒ·»3øws‡³HÜÉŽž»z„0Ï4y´ûE²í­•í¼Æ¿ºzÈÊyÃø.ŽGÖ¯W»øøßŽ‘0’ÅRñû´!CZn¶2j–,¿°TÙ'Éúññd8­ À¥­cŽÍØ‘e¸::À¥Â²6A†yÖØø×·;™7X.×W)mè=~>ßs$‹2æ&/°d$• sqÒvpê¦ åì¨]'¹ƒZ£ŒÜËp1¿ŽaZ’•V²Å»§‡Ÿö·ÿ÷Äô…¹#i–³}D€% ðö€¬‚¶ZŽºêŸ±ÓáÅü+àîâe•5àì`^o TÕÔCHC¹¤s¯W鸖æå•µàjGø" hÀ™æÜ8–ر£*cøð›@U{®"ð‰á"êÍ™#bâ‰Ð¶|Vç·â]©/¿˜‡)~ÑùT;Ÿ‚%Ê.8Iîüuå)p®‡'Ê×CÈDûõI›»³óäÖ VØ ‰c[ÌßÇ å Q°íàI¦?††ÓÈËKúà猆¡~pàXü‚:cºVgŠB+m<ÄÒ:͉`+pŠÉdúûh9Ôß »m’è,qÃ#Ñ ¦Å÷c‡5x¤iå2J5ü}´ÄyßÑóLÓü̽Ә.ùÃgÇ“GÝõÒ—H8Ûˆÿ®W鏿s±²¶º996ßÅHóUùŽG€!yèPAF\Ütå$ËÞ8h”â¯GÙ}ß>í¯±bò‹zx’EGÀ ™ãc<#ö¦4Äx6pfíIÎÂdœ$·çâñc9G€k”;ŽÑÎ ó÷Ä X+ChkFS†÷fÞâTÔKVZQÃ=P\RV‰¨efÖèP–½î¼Ê7ŽžÍBéD!ó0ç_)geêx—r.½Àu(ÍO7gp°·Ó¥w:3—y¢Oeä2o2v2ò »:;‚ =»¤•¾˜wEwNtˆœDíôyÌ‹ò¡Ž‡d­•Awb „#áéãÚ¶Ž’à›8]È$üÒÅl¼MYh¼—z+«+6VŒeK”_Xšì‚“ä.{;òŠ›N”Íúµ²$ϲŸ—+JŒ”^ëØæûÈû<½¸’ƒö÷îÀ"]¼õÍNx÷û=0uÈ5Š:ŒrˆÙϰÈLDœ$»ÏÀªÏ¶ÂªÿlE…#Ä÷ÐeàŸþ²(¾s÷oF¦igðì}S1-“^°“þya羇æg爨§vtÐz¨¯Uýó¥eÂïìÅr•a§Cí„/|a–ŽãsŽGàÚD&§¬Ç0qáxg¿FGâý½ cHÌùÈ#)«¯}fû÷ZœüBOv!á|Vw M®ý84?ƒ“äæˆðuŽ€ñàeãcܦt$Ý'D4{‡ú€“½6`Ø5’6tÖ$’ÜÙtè|*VäÄM̵ÎIÖß!2b W‘aé}ò,m“æTn‰$KÛhÞZô!Ü~Þ›öع1ÀU[6Ʀs©ìæúçðeŽG u0–òëxïèFëÃIVfÄ ¾µõ3:¶Ç’äÍe ƒï;V«ÎÅIrçðãgs:Š'ÊEÎç‘ÇSΈœìÑóÚ?Ø p0‘õ;Y„&#ähü$½P¯LóÌa„áçï d¤(0tu\”0&¼¹q8íC K‘,靈Э* gùËŒ¸Alö¥tí£ñ[Ö"ä– »à$ùÚ¿¾—#`L8S0&ºiS3þÔ¸§aIòn‰#BgooA>®îåÇQŸûͯ‡ âY¾*c#n :=·`‹ólÄl®Jš<É„áæcWŽr°CÏ´=v0$\ _ÂY¼i¬IUºVMwñ5ŽG„äd¥ÂÉíV¼½±““`8a¿é±±a†„§Ùà#,ú…!ÓosZf–]p’Üæ+ÅäN”k“D‰t51•ZUS«T^µÂ²ÑÐÐDè°cž’º_Wq“1Ò÷Áú=L³Ü$1¾ÒÒ$¿‰${ËëÀÛ^d$ÙÁÁáI¸¾„só0x¨»5M•^‚W]#½}|‘#Ðe Ðpx݈_›,T eÕæ¢Q£Ü  “_€Øž½ºf*í¶¦cnÙ'Ém½Rü8Ž€ñàDÙxØ¶š²²¶®¤´¬ê*&I/ˆÈ9"±sĸ¿N8…z9AwW —”ÃG?ýŸnÜÏ"bP|`n„…­#\ŸâÒrð“Wƒ§š‘bG-Ž„'áJø’G¹¹ô¢´¼JT«TESŽGàú„%'§c×Ý9x${!YP^Sõ†3˜Ö ½Ö:=0J1Lýœ²‹–H2v–þdú3êÅØvÕûãúWŒÁàtÆ9›ŸÓèÁƦšêªË5uBum‹!%BR"pvvvŒ »¸8CSWW¾*%8 ÕPT­Á8Æ,’.8zž;ÆHvDò×Õ¬#oP,æªÚz'YÝÑ=„:p—©Ðc, !¶oÀÑ \K“><_Iz!aFׂ®‰²®.·ñ— Ÿs®…ÃÎ|bKÌ—tvžžö/\|ô§µy—Ù£_˜IvÑIžö´z'Émþùð9A€eƒÀxÝDt$Ôd?›uÅÖk¬%Ê4*z”1ôš«‹ ’äzÔ&+qXg5&¢Á–Î𬯆 …§àJM”ÛyB eîHº}0¾D×h$PÖ–€²¶Äú ëJÁ}X¡Ã@†™H0y‰ »€››+¸¸º°e•ðÕF¾À/£k’„‚ìKûp“t½¤Ý|Îà´‚@dÒѯ2bõÀ(Ž+´‡hɈ|.2ùè{­œÒæÍ$¿H“ˆ|(ž$É/¾hs8dII©suI˜(Ú'É:ÄùGÀ"àDÙø—A"]4§8oê‰Ó†MšZ–x*Ér#[Ã$ Îf¤O&¨I²Z­%ÉTTÚ&s\ÌÝμ¨ÈÁË-z†ÏÅîj‰ìÑa¶oÎöPTxÊ ŽáÇ@%Ö¸Ê!¨ÇŒ$“ç˜H²‡‡¸»ãDdñ$\ ßæ² ¼X¼wóO'0)ýkfûXòr:‰@DRÊ?Ócczb2·QRHlßN‹IJJÙÚɤéþþkD”ÑÀä&!Ê-Ê.èacDã$Ùˆàò¤9D€å×ÎÓÎjI2ÎU4]J;¿cúÎ?)z„è’Óy•‘Ðiðí@$Yâ¾Dî.îƒKEÛe·¸ÀÓ%DÞŽç˺In@+¤× 6â^Ya ~$Ô^IJ”¡„÷¿I±ó$Iöôð`ËDžIŽÑÜ›L×àdZ¶p9/÷˜4û˜Á9]3nŽ@ 9@æøñ Ô%Ý‘%ÀNÒ)‹‘0F¡<ƒ>>;lf“_˜XvÁIr‡"üDŽ€QàDÙ¨ð²Ä%ï$0"ÉÔñ¥~ÿÖ;¢¢§üoÛ_îÏÝ?S¦kXëU–ƒƒh¢‹ÖCLŽŒŒÜ–÷8¹`BJw§HÒóLNÆdlû$ë ^eú  s<òÎP^xh@Àº+¡&Ï Â†ÝÏääIfÒ 7¹i'> %‡×GáÖ”üñËOD”éѵ’®.rãp®‡@ÄÞ½µi#GÎj«¡G9ï 7Ô›3‡‘˜˜½ó[Ûoù…©eœ$·võùvŽ€ùàDÙ4×€¼“I®ÃåºêêŠÊ#û÷~)7ù‘/7í¼y\“6="Ëû—ŒHáù¬_áXæàä  À~{Î= ¾ßS jä¨_V±a¥IÃÜH2¥©ۨEuâ3¨(8 YMá¨Ìö°‘12i•IrAxÒñúFØç•ÂÙ”ä׊‹ +p}Èеâe} ø2G  D8Pxqè ›”á>ŒÜñy¦Q+7fÅÇ =x°¦ I´x>M*¿H:rz}VAȲ1ÿÓxµXºÎmä$¹søñ³9ÆF€ec#¬MŸHy)‰„ÕâD/Œš£û~?êåë·—o†Ÿ@¼wæhAò,9–ÈrvÑaøóÄË.‡nÆ+æåß&Yƒ^dÔ××k;ú5è˜%¢,É50m›³g2û€ ŒhXêÀž… ÉoAyÞ~”c”]øTEõ¿þó˜Ü‚Fä£c%O4y’‰$'¥f 9™iÿùó×û(º6tèZq¢Œ pã´î‰ÇNaç¾yAØ‚dYAñêëk¾ÄgÓ|¼ÿ´Í^mH4!á3Ç|ÈÆ;1ø³²’úþ—2 ÆÎjí¦-™2÷k\Ž÷(“x îL ©Þà">[eøÁ«©Ä¾¹•˜öö‹e‚˜%WȳíݳÞþÛß®KØ0ËÍJ‰Át~hO¹ÛP5Ý!œ$ë à ‹E€eÓ\zâùBÁ#É4¨ªŒÁa÷Ïë·áó˜Üœ³ò®”ˆ·O!“4ËDê*ª³aó‡A¦Púœý»õ‚9£Ö"ásB’LÑ0È›¬fez°Q–È2¦i³Fذ »÷æ˜"]Þ¸Îí[¥9bx8€ ‡VB@p/p ÑÄ“Lšd’['9+ýÜÛ¾ýŠ$tMèJ×H’^à"7ŽG ½D&Û‘;øQ|}Hçâ3éÖŒ¸˜Õ¸ø–ÒzdùËõ¢0÷Åáò—L.ë“«ÊÂÇkÊwõ€ü¾ƒÙ©Nv ¹»“ÃmSïkm‹›LÐ~+ñyX[§„:•ZP*UréY¨ÆDÕøq\¥ /_Y*‚p uT”Á1$â)vv^Çÿð0õ SÉ.8If—“ÿãX<œ(›æIDYò(ÓÙ'{œ»7¬ßv¥ ??nì„;Þüj›Gÿèqh¿H¡gwøaÏýP[_Ê^®Î~pûäO1 œ¯– £ÔBK’µþHzA&½ØŠþ#’L¦õ£§‰2MÃozŽlºª‹Ï`?%ß¾Fݶ D{ pÝ‚:îiTªòã‡|˜¸ç·?0º4é{”éšqãp:ˆ@TòÑ0òE/Q#>AI é}.}è sQ‰Ç>[”°ÖY¦,š«Ä‚ ›P§ÑâºQÔÍ]ðóöùy»ƒŸ·øxº+vÄuqrÀÉ^jºî»‹ZÕ(ÖzyU ””WAIEµv^^å™w¹4.« dHm]=y¢1ºP‰ 4†¡¶~œsàÊ`Q4ªì‚“dBGÀ:¸îÃÆ:ªa¥$KÚWÒ(“÷’FÑ6ⱃ9—rä|üÔÔjÕØ“iYîþþ_‰NiŒ Øõ[U÷ |¾é$­%ÃØ‘bÆèô<É]áI‚nF˜É»Œ˜03º˜ìÅ¡X 2±Toù×/n÷T.¥ÆAШÕå—ÒÎþvà×-›ªªÊ/ãie8•ãDׄ® ×'#Ü8†@ ò¦9Oelü9 ŸI3)½K¾ëþþÔó·T¨Š&àç½³›³£ªw÷@ET¨D‡úC°Ÿ·¼á¸ÓÙS:Ž4Ê©n÷æéÑ#C^Œ:» Ò.ÈÏ\Èv)¿xx¢ßá”O ô.:=JÓÅçŠA©œ$7¿|#`Ùp¢lºëC[I§L͉D’%¾ÇHtMM•½Ë[pûŽ¥ÿð¸ßÉ¡œµ5â_ÌΚTXR\ƒdî înjæò {ÕÖDJ%)qtÊ–Í1—<ÌRÞ§;Löý!P&¨eÎP(Ä ¾µµû§§Ž&ŸÅcˆSÇ="É¥ Ë$»ôÉ}1bºÜ8]!!AS8~üçd²Ä½}ô¾èÙMáä`?cdŸpahÿHè 01îÀÞî.@ÓÀ¡tº¬ª¦NgäÀÁãépTá$ þÀ¢å¢;ö ~v]Âs—:’‡þ9D’}í¥OÐÕ±@ÚNÃRó÷$4øœ#`yHD­Ã%[´üå½tòº•+ÆÓœÛu ‚LÞdÒ(c 7 ¡„QÎpÂxlrº}¡lÚ€¸Æ鹓°å‹÷D Üßéë…iÌBƒß—KÈÊyDZ¶€9#º‹gɆÌ%> ••[W\‹ë’Ô‚<ÉD˜‰8“ìBÒ'ã"·ë!Àïýë!Ä÷?úî»uåïb'»]…é£ ãb{bz Z¶‘\ãÏ#gaç¡S•F¬DÍ“­\ñq{J½híZ;È+ @å*™_¸ý¡Ûœ øÞ:Ñ…uM©×c¹ª¿ˆ¨Ã‡{­(ˆ8—çÉbš‹‹}úO?MÏ&nŽ€àeÓƒOÞc"ddDè¤ujö¯4C6`@¬x7í$»R¾þ·ìtF[I¦²¹7}Ù•Ð6 2qíFÍoQÁ2÷~áâ<*Wt0ÌX±@¶ÿåÏ5[p•ÈruÃDØÓ5¡kÁ#À0ϼúª[ù•ŠÍø3-~€0}Ô Óh=¯/ô4Ï?Æ é%ûaW¢sòé ë-[9líÊe‹Z’c`Ëž°øÅ5½µ8 uÙC±CâHMVÑÜnǾܱA1£.Ž!k¯`5Jµfö-ˆ¯­¾Äa¦@ƒO£²ÒZXºbu‘ZÔœÅç0rÇ_ø€ÚN©t\žG€#ÐF¬ç©ÕÆ YÁaúäX¹.2Ò^3n†òYô³ëRW g>Oþj–UX/‰(Ks³WÕ‡‚Ö³"½es.jßI !Ï,{ÿ³çÔ>Ø'h"jxoñ™ÿ›a·ãÓ­J’]A&¹áK$Y:¹q8E€<ÉåE{0ÅàçŽâúFt4)³ŸG„yáÜñ‚‡Ë!ؘúàâ+S°PPÁ¨S¢ ,š‰;–¾°jdWzˆ .ZÓ=ÀGÔ ü|<Àc¹»¹8àäî.Žï=;I‚ÇLýMT* (Uj(­¨‚Ââ (,)‡¢’ ߢ’òn9E¥Ã«ªkÿÆ|rUjq.ë–-Ëc‰ðŽ€QhÂtŒ’O´%èY*… #‚FËÊ{žP½,·éRypü‘âB=‰K.Z†ùÚ‘‚DgyK—‚á_6aø<õ»?Û=µânÕFŒ"ŠzH¹cTolK š‘››K^dž®7ŽGøþûïå»O§‡â÷y4>t¢Pºëн†):%ŽØä¥÷†·á&YÞSy8´e¾½`—NšüžþJ]aÙ2ì_»pÎxˆín¸ÎŸ2ò®”‹ç.ä._ôª ø„¹S¦¾<;%:y¸9«÷ “wGbä þ>½®Í&ÇNÈr{ó¸»:;@ˆ¿·þ¹”’]aq9`‡C8{!/èÌż{ªªë@¨Y´|åN ”÷q "zcBÂ|úðçÆàN” f’’¼˜šß—OUØiHiT” Ïþ¶Au×é!)yÚñè•R2ÞÜ‹‚7Ú•ÆE³/I&cø&žR^ι¬Xæ¯Æp¤kŒûä±üGf<¯ã1Ò±ìþ#Еxò­·œ*KkÆ Í$ŒÓG®õÙyü\ÉíÍ¡ýޤeÄåry° jck«ñ»µHÜpA”õŽ‘$[Ô3K[ÒŽÿíÝ]HÍÈ ÀúmvqtÐÄö — íû¾Éðã9´~¦6\ž;ŒÒ‹’ç–ÀѳeûSÎOB=õ”|õùì7ð¹`÷Ÿ^~îTë)ñ=Ž@{àD¹=hçXqÕÇ.ݱz-Åý$ÃŽ?¾ò¬æS\¤—Œ4Ñ.‹zé¸6Õ(SÇ8K1‰üÒœMKßTíÙüªl5YA…Ä÷ýË[ß´ûmÆSÊ£–Rh^Ž€)XºüÕ^8Çtüú¾±ª¤z,hD{…\äã)øz»Ëü¼Ü NDÌ|1†±3Æ.¦xåô…‰&=ƒØœF¸,«¬Â©†Må•ÕP^YcWU[7ŒègŠê˜4Á½»“¢Ãü¡odÊ%†I‹Á2 öóÂpz^pãèyjf aöJ9wéñzµòI$̰Çä?Ö%<u˜$Ó•çȰj8Q¶€Ë'hª?G6çKEAgM¶ÌÝ}!vå ý¬ôRb»èŸ%™CÓ¶Åë kâ²Kd™²edÙeØØ—ªŸ‚kÃq›½¨R}sà­Ø‘˶´²›*ž­#°G‘§>p;6²<£ÒÔ ;Âß×KÓÉ^ߨ`ôˆúãÏíwŠÚÛÉÁ× 5N]ÁhГ¹c-ªªôîÌ& o'û#ul?xb&ŽÜ: ;~ÎB×1[Ô%ã…±28Q6ó[³Vö2­¡Q”ßûü]eR }²gæ’^}³‘HëkÑ6aÂ^ÕÖ×îÂþ{),…ãëS¦Ê%ù…%…¶³h yᬠŠs @’¼JJC½±l±j´Îç†G`Æ3ué¢Lö˜”2~™,Ýòª6f“´‘Ï9VŽ@B‚([´|ÕÓeÊQԼѫ{`ÀcwÜÿ\L?kÆ"u‰¬o|Zý6Wnn¨¬ ÕûµKTžWÒæX¼üµùšÕû‘ ¿†žc— g’dšç¹u-|PcþsÆÂwMÜ\£Õ¢êТ+uŽ‚®…¯-G cp¢Ü1Ü:}Öêÿ(â±cÙ=RB¢\þØüù§xh #ÏííG²L:pR0ßöºâ>#gÉ“ç6àÅò—¡þ¸ƒ½ÝÐ掃%ó&ɨ÷®@ïè„$}@IDATð@HX4[>¸WwÆz~#“|ÊF ìÚ°ðÚsÚ„'Êm‚ɰ‰b‚LT©Ñ‹©í¬‡„í‡eªv6žÚµ˜üdmÂOúdfö•ßÖzyHë|ΰ&¾·Ç0~ÒˆðîÀè0‡Ï‘µâA>¬ {k)«“ƒ=,ºe‚@£ â»gdþ@¿k)?/'GÀ\p¢lä׬{ £ZÀ†¬«e¢ãSf(F—ÏÒß#` ‚p©eiYB—…`uÙÉSûÕC³o›2 ½ÈŽG %¨³ß=7bƒ&3sUgדž½¥ãø6ŽG@‹€%ß ¤¥òIÅ.²ú)á ·nØÖ¯ëÀ‡«Wž[\“cuS£[Bš¬±üq‹sëÔ¢ìi,{ƒ‰lxÅ~®Xýï ë Ý74çúë†+lk3F’Õç~FOòw͈& íkkUäõ1£bzý^ð>+G½Jײf„¬x’«GÀ’ÂÃÑ˼99–¶I/zinµÀËœ«Vah2Ÿ† dîþÞåßVéþ©Ã§¬žYef=§Þºið;Ö‡µQÈEå{X§›ôêe‹Ò…¡¹4ÑðjÒ$m³Æºñ27  Õ$¯ú/ætòùáÆh+£ñ÷RT\A1—ÿ†Crï^÷Ò²-m=—ÇèJXQ–ȱäÅ£2ÉçÞ¿dˆ›§×$™\*Èe2AFqo­Ú—äò–*QX4·.|èÀM •¶X×|}iã¨ÕL»e‡u•¾±´¿Wæ:Mpÿù†(a~á…ÛåÔ÷)k<Â:—D+Dµ&ãç^ª./ß¹þã÷Ž`MT8QÌkšˆ8iæf…,~aÕ£xgÞ1mp’l…Њ<õʧ3sTÙ…¥ëþþúë=ßxúé* (–Íá¡„\eªb_Bæ¡T ‚ ºŠAޤGFÈ4øªEµXvB5RŸËAà_Èã][ÖOÀÜD™š…‰ ÛÑ=ÐmØŒi‹\ÝÝ£³Òã ‹nÎŽOwg™£Õ{“U° ™‰–—È „øNï­ϲ~m/Mc¸)Œqßöó,íÈ`(®9Þu{YÁxéëæ:ÙÒ ÙîòÔ*•šÒòj±¢ºVfïè¸{ºTWV~x|÷?~ Tâ$æv§ÏO0KÖDjÔêWûãÀ!ãb{›¯ ÝT^V¿+ó¦UWÈL…_ºf—ºF3RP‹ƒñ #È 'ú]”Êbmq%ù$½Ä §¯ä«PÒ ä@6` tü`/át7žA–‚ã¥ø(áp›‰0Q–¼È”?õºu˜½`Ñÿàзð§âß/*DÚ/D ÎŽD¤­ÞÊ«rࣟÿÒÝwL] ¡~4’27K@ ¶rüþÅh5JpV¥Á}ÜÁ'd¤%­3e Q¨®­ƒi9x*ÃïdZvBü¬éKzÅÇ>º~í{Ûq7…$$/3÷.#Ö`$¹XúÏ5Ÿ;Ø+ìïžnõ¿Qk€Ü¦ËØ=°ô‹ ÖœÎÌ}[o‘ƒÊ¦+l Ê=šð®{ºüÿnUUªGàË]Ž‘”Dou°¯§ÂÛÃöqf““ƒ8bÔG{;c³%bŒžÁ:¥ êqª«WAeu-”ÓTYãr¹´¼Oaqy+¥U2µFC,Z±*4â÷‚|³6ay’ªÂ“¹æ ÊI&/²Nη?üäCnžÞÏû{‰·M=¬Þ{Ü÷ƒ'ßÇß7ñ€0ÿœ$7ÈÌ뎮Úï6¸tâkV’ôÃÿ²¢Ìê‚›0¼$MÂùKùð¿mùaç¯ïîzüé¿yçõwð Š'MÞeN–b–ýoñ «5š1ógă¾ˆ¹q:‹ÀˆQ²S9Á‹^x…F)Mìlz¶|>E ÉU®z®^Uþ<]—°@oÍ€èPYÏðèÐMÀXCò*…J­†ÜÂRÈÌ-‚Ô̼ ü y´¾^ùÄâ«¶(4âìZ~Qï% ¯û©ÕÊ>2ÐôÁ/žäâH]ôDÁ {9á¶z$XõèÈVâ0[¥¸/‰{6ˆ²l9ÈSÿýò3çøÇ’>¢Øy©éªIÖÈËE$Ù'—ùK{Iò?p˜MñÞ™£eöv6á@ndEuKû^·mÔÀ'uË|ÁrˆŒ{²N}‹^eçüÅ&ï`tØáG(l"™U½R-?|*6ìIžZS§Ú¿xÅêy¢¨ŽÁ†¦ä2¯VÖ"1Öz= ™%¢‹“½àâè(³SÈA­Ö’oJ¥ÑTakcYeµL¥Ö`ÔØ´¨L¯ ezÉH ±ÓÎö@YßæSËc—5SeúAQžäIv™vû=Ó<¼}Ÿ%’üàÍãl΋,ýªþ:õ!h°IŸ,Ôo~uZ±œWª” Î]ƒ ¤Ï<$ËÿcµKO|l(SÅèc”Ýo?‘åæÜ¿øÜ†ÏÖnÅ]ÔäJ“VP‡ Ü, Ý'ÏÍÒhÄð1Cx„ ˺2Ö]7G°·W¨•õêpë®ÉµKO£ŠÙÅ1¨ãÑÓ:‰åС9ÊlÝ`SÒÃOMOB$•jzâ_Y©*„U*(*î¹iŒÔãÚ™i/=¿)bIϰ@Å??ú)½Ú(+oOWuŸð@9ÄØÍƒMžn.­‘xÚ®ÛG’âò*È),KùÅ.—ò.º˜W¡‘=Öûyz’m–$×++áxoò–ø;àej@ rè£}ú{ÔŽ©áJÖ~¨,>®Þæy û¢Ð}—{¹[ßà툈>23S‹1OzOH„ÙØEàé·µF¼›v5q}#u/¹v&Áçt)X¬q͹éøH¿ ²ŠfâÃÝ‘pžî.êî>row&a"=±›³ÈPC,# 1vt$bR]«„ªšZ¨¬©ƒêš:¦=6IÖ¿p~ÞnpïM£¥J }"Á×˽ÃÍñ®ÎŽ@SX€Äd¹ÈU˜î™ ùpüü%—cç.Í.«¬¹%O} ;~èä½³lY~ylyÙ”D™ì:oò¨›f=€jvßÛ§Ž`.[ùxúzPª0ê š¯g/m«Uµ‰z9¹…€ÔTÈO#+Ûwâ è;n¥MÔ­y%È3¡Ådo~µÍØôiK2ÿJ=ÞÉ[@d™õÇn~_7 {¹ªý7a“¯ ɲù Âs¶9jj롾^üP¸l+•[ôÊ+P©|*Ouþ1ô{¸:;©‡ö —÷FRÔ pC‡‰¥¥`„ò+£E2þÑÁlºcZ¼âTzü~$;„çü³¶Fx óª EЛ]!”©ž¶úÞdÒ&»úÜÕ?:D´ÅŽ{ú¿Ü£g¿Ô­éµ@·Ì,° tD9'õèÿ(ì­>Œw‹€“f™îÓiððN’NYò,·xßhz á@lfuŽ ñ5}æì:‘¶Iÿë‰Â}ÅÂYƒsÇ ½ÃYH6£fÞEG‰,¼y<<}ß ðñpD½ÃÖ=hËÕ7Q¦|èåKúdÇÀ°ðQ~ã$Û2¶|ös]ýFÏ;…UŽò¬«CWZpŸ®º—Ž¡[¶Åºé~ô œ€õ£{”îUS=lRãÔIü(a3åKyW`ɪÏÙôÈ«_Á‹k7À_'´NŠ»é÷£ a§2rá½oCÉ©€Úo×*TW?ßÜ$aŒ1O¾ùß&ÛZ[!_[§íœÝÚ1†ØÞXê:`”°&ºã©Yî™Æy5­/5áÒD^*ÊÏÞÎÞ®›§‡ m³YK½¸;×’Ì <`x»G´Zׂ+ål„ž éb©F/ÄÌÜË@MX-ٮç¡;ÆzŒõoiw‡·‘W…â>RÇSšÜÎ ‚{ß ¼Éyç~±ÉPq¦t?\VÐÅ£{”À–î[ò4q³P?š‹žãžâh_.f)É>¨g|ˆŸ7$΄#g.‚DJ®”U…ÌÂLÅö ‡9ã‡@yU-¼óßípŽÒ7RÛ‚¸ýà %2–=06¢'úÈ™ è¡UAj¯q8e8ŸUßï8/-½ä¿+ïr) ’ÃÈ­Žv6gÂØ#ö&ŸÝøÜÁÞø¤?…é£BDðõõÛøHñq}àç=É0¸Ww1ÔFM-ÿr|½õd\aRìÌ„uöBO÷NæM~ë¤ÀèÏݼòÙfŠ>Hà,z›¿Ü¼¦ÈBx])­€·¾Þ‹oÀÂuý¸3 Žœ½ˆÏ{ìX7OŠŠ“{ì\8vÆ î ßaÝâÐä·NÚ¤P؉ ¾ÝþÜ?{ bÅšìoï E5ø`ý.˨Æ87w¾ñôÓUíMÃRŽWÖˆ?âˆw!Þ6YØÍÓRŠÕ%ÊA# >8g¬ðÜ{ëðCr!VúY[«¸©¼F”½|Y–ËäÎŽvv6N”7ê~+}"fƒÔäw)ÿŠn»´°é£ðËïÉÒªÑçRY¨)õ¡5_À³ï|_lÚ‡±ž[çD×kýíÐI8{1ßàeÿô—?šzÏ!íæ…%oщ´lÝæ€ž³uËùé[ñåf» è~ı/é>5Õ³A‡1_¸6­*‹Ž()¯¹ö&Ø{±á9æhO ZÛ}ø Þf 9 ÇÎg!Ét'G;FŒ¥ãH²ŽÃ&§g¶ÇaÞ ÃaÁ¬Ñ8 B7pvrÀ”@¤u`Éï#9­¬®ƒ»¦`²(F,…õš;!žº{Ê4Ô°åÏ)‹ëΧÅ÷Çç¦Õs¾Ú²Ÿ•ûñ;§B°¯Ö{N„`h?­Ãcî„XVfŠ?,— :M5JzŽ`ÝÉè™X‰¡ÅÈÿËÞ#p?¦ŒèS1ÿƒˆIVȨΩ™¹ðß_@lŸî G¶£á½7Öý´& ík’Lqsßüz›&=«Ç©î[·jù>ýü¬iyÉòÕ7áðÎcn›6\Fsp3=n.N¢ï¡ñ¥iúb-GòÛ$ÏÍÙK¿¦mš$—Wå@n‘ö(Ð+l:^i]oG|'˜Ü¨µ›§+¾°.ÃúßAïˆ 6ÔqK¡æÐþFlm)ϺzzZ.Bˆ¿7>™=ÑkdL;•ž ö&ÀhR xÆ m][™ÊšŒ«¼º…3fÌš6zï¥{Tÿž5Ã/Ó¬0XlærQžMƒ\.-GÏi7³”“>ê«kêá¶.)У±ŸILÏî0yx_V®ýÇÎÃéôlö<‰Ø6ÿyîÆ–!"ÀÔ9 ÃOò¬¦á°êSGÀmÃèpz5KC2Mç‹ÈˆèK-€}"‚á<‹€Ã ž¶ó¯S¬,´½G˜?«ëÁãéŒhOÞ¥‘¥  ücîDêCKdY soŒÕ‘d©.¿wýw;ÑÛÜ& ëÃŽíÌ?"ïŸoüS…¸©±•â¶u/-û¥3é™ý\™¸ÌÏÓ]=r`4ñ nfBGúÜE›t°˜‚(ÓeÓñÚ$ú¿ÍÓ½ÉAcÁÑž< W{’¥s¾Û~ˆysï¾q$k‚û›©émÇÁ“Ø,hÍ„½aìm†O7üÁ`þ”áìôÃ'Óñ%tŒ5Sþ^œ‡N1‹Oß›”Êš'ÉãB_|ÍMjF ò…]è ÊÄ—Òðþ‘-6öìؤ9”¶äý O_G¤î%••_Ìr¬zøŽÏz‘,Lß\PŠAVPÜôA$tÛøü«¹& 3YÛ8²öP<öö©ÃÙóötfŽö|üïè õ=qçxë©;tÓ žZ¶îÀ†…Þá8Ò±ö9ËZ¿HŽAšéãøaNÏ65tÈžîÎP ÷œ,Ä:‘>šöIf×B_ ÒQOÄçi¦IŸÞ#'Êò~TIÆj¿ãìåcí$™p¨/®Œ¢!¥}¹.¹#¿ CœC¿ãµ?íA;Vãj÷¢!Ò´´4LI”õêΚxõÖmg±¤â>ÔO² ÉåÐ3tJ‡*7{ü`‚ZµÙøb¢ÎlDj¯gôÀ%¯4‘ß÷±©Žô7 oÙ[KimG} uÙ“x Õã#JÆš'ÆbÇ™X6Š‘´æäå¡uD¡N;”§¾½XÀšBé%@ÍŽÕØ³ššYÉN¦e1o¹¿z•üYó%ujnøñŠ/¯”|Ðr}#CPߗΣæX„:áèJ¨!ôD¢¯õ^‘Ƙ´‹$ñÆyhŽ6K¿%l©syº(/òÜHB<ü‚“{ËWUWEo^T¾Î0v¢ð8Q$¬5ÙH”_~—$DŠÉ² JXç7ú¨Öà!oN¶#±sBHÏ*êôL:Þì‚bth #ݳÔaŽ$ ÌÚ¦¼Ð‹ÿ©óq\ßæ–6Fû±á|·î?ÆÒ¦gáI•ŒŒZáhЊs(¡vò<“·9,  —×·A>Ò+<ˆ­SKžäE¦þ£g/±g9õ¹ å=´Ï)ï–æDÄoFy¥õùÆF/wKǶ¶ ‡këë‘‘£"Y€»«®Tÿkñ «&Ñà­c ÛQ"_D嬨²Ú¨vÖs‹eD]8kYõÉ/¼¯€\3sÝsÏ5zÌZ<Ë:7š‚(ãl×1}ÙETð$^®ªrÐ5›!»c ¼ñä°=¿ÔDHážô­¥f@ÚOú=²°½½(|<ÝØ6ú×72ö ìƒ:í'ƒ4~dtcÑ †¶¯ùt ›Ê+kÙ  ÷/)5“5£nÆfÇ׿؊ž¦Ë8æ|6ëqR KózÎ)}lòAϹöåK/Rê/¥àXá‹TßÚŠ­tNÊ/$Ë?ߨZ m³áy—ºw­á:~°j9}UþwûšÌœËÖPdVF’2©ðþÂÚ ìGF-QŸnü{íkr:¸ùû4~¬Ó1äQ¦Ö'zæü󣟀:öÑyû›tÉÏy5FÚC:ãÚ-øÁo‡Áw%£üÅ·ŒÇ~&¥°ìƒ±|_1ÙÉŸ©<ãQJAò±¿ÿë[]¶I¦Õ#g>IîH=‡ÊƒÚç'ïž&ÃÑÕÈQâáø>_úÂê_—®Y£uùw6ïîéðL&?ñïõ»Ô–ÖÊBïdsP³£ÍPÇyCÚðþQ8èÐdÖÉ#ÕxØ+ä‹õ_½°úøÂe+›~q2c3¦Õ²ÛÒŒ²æ¬/äï×?Ôo(>Ô¯î!­ß“šç ‹Úv:œÁh/}¼utNØ£ºlÛ‚å»e Ô1pþ Ñ¥€ÿ?`€{"D›EÐ £¯Mê,2i˜6´SóãZZ'òúÝöÃx:õÀáMzdSzÔ#¢L¦‘Ä(âÉB(ˆ>Å(ý×w0ÂëähÏÂ0‘§C2Š5J/@ÒôéÅ0ý›z¨ó¢ÔÉ‘¼.ÔÓ»g®ÚŽmÙÿ78‘ñ«Ð¸ÁÏA|ÿ‡ŒV9ò–R'sT ô¯_ŽcÒ"R<‡¤ÕGÑ¢¦Ñµ?îÅNwNpßÌѺ⒧‰š5qÈQ$³ºÍmZ AH¨÷.Å‹ò&ò??ú™n)2%Di°òž´ÇÈCGžH¸déÛáèÖ…lÕ# âç5†”ޱæù[_mƒg2Žùöš»±z€Â žFS‹Ö†{¿ÅóøFÃ!°dÅšÁ‚ Ù‚//ìð%=¸g»ï†+ OÉÚ¸[„1£w`„ vµ*Fè‰$ÅNšÔâH²zžÊô¥D”—®þ‚Ev™>j“Ò(’÷àH”?Å'ò»`Ö ÈJ$éÙu8•…¤Q(˪ªá+É‘ôãä,Â"”@°÷eüÀlÔKj‘ù:µ(Œ+9}N`‡øÃ§2àMì_DrêÃãìd#S‚?°u8C.{`ëôúÎÿv0é#õá!99­ŒiÔÿc“«•*Íñyü°„„ *cægª´¹ô€Hë{”iØjcš>95f>×J›¾,[*u0,Á0m$c ÎuÔÓ¾’õâžÒM«ÇEõw_w™tÚ¥˜I7èæ$RЬ#ÉCÚK’)cLŸ$Ó6Ÿxü¯%ÎeÇAU_I›¹qÌŠÀG/ÿã¨ÂQ¤R‹»ÐËÞÿQóJ¢ˆèpã´"câà.}.·7?cO¿Чÿ¶\>ùæ™×—†<7–‘Œ…ZdéC•<ô©Hné§?@ûz‘Ÿh8÷§ïÁÒ“Â#R¹[ŠÖ$ Ž3:¦—npÇ€Þ½ôÎ¥÷ŸwA76I¦2Òpö‹n/׈š˜\åþgh›-˜y]’¶€`C(,\Eu[s°sCÒ6À†j×öª ê†1•ËqhX ­TVv:¾ŒÁˆ¡l6kíÄ€þŸþòëC_Ê4¨Š¾|ÃPyIé(ÜÁÃIÆ`ÊPœóøE4J3¤ãøœ#`j¨ç?æ9;c®ª­{ñlj1´™z`Tˆ¼vn¢NƼ7L]_žŸqˆÂþ%3ÆÄ°áµå2ùè^~¾QShܬ šú¢VMÔð j¯qüŒA.¶¯i±¥Ñ+®?ØNó$¤ÈOÔq>éôÝn }(ÅóoI{ßR´¦k Ž£‰JJÛsŠüBƒðœ¿”?ó[mŠ<'ÊBøB^ã³$,`~‘w]g=…y¢ÉXFQ:¤‘ •GKéú„ŒÒeÜYœ½å–@âÛ̆ÀºUË÷aæ“®X3´¦¶þ‘¤Ô‹SÌ`a0Þ¸zú¨òñqWw¦3[yÆ‹Åßßuè´ªº®î ,dãËÍbKÜX°G^ ÂÁ¥ÞÕš[\œÕSFô#GܘÑaÚª‡nKä§Ötü-å¡?8y²-Åü½Ý…óó¢,¥<-G×esE®Ùùó饵îþZ\iŸ[?ÞH”%»’u@ZäsŽ€E!ðñËÿHüxåŠû캹† ¬£ÂáÈŸv °«®­$ñ°;Ñ"Ü, "kÃúG*P]7#!aÕ8Ô._= ;—QØÉçÌÆHIk™'Ÿ?€ c„[ ¦-‘ŸÚZÖk ŽCiP'~ÒÓȾÔ_ÈT†÷4>m„“¦ÊÏØùp¢l „/æ7§îMGª3P<3#à4 ™+EÅ•3P_M}Þ¸q,¥+V©/*OÅfçE(‡ÿ¹h®lÒ0ãy“ièé%«>‡Kùm»'(\…®ª®©e‘q … ÅíHØÍ¶äO¡¾häÏ®b$Àî&Îp íáÌ΢«DY܆îAÝœ_\˶æ5U1[ †"?uða‘Ÿ^ó%¼ñÕ¯fµcC“_kpªã8³ +¿žxãûÙõ&4ç€ý·™"?Säa5_Ц££y\.=‡´1‘}À׳ã£Du´ ü<ã# ÃpžC0Žò!–YqîaˆžnüŒy6"°(a­3(‹Öà(xúz»jîž>’ô‚á[Ú˜Ž1c±•±“ï]Óã1z´A³"òí‹ÃLr€©€§ÒsaÃÞdŒ""m²é9 ÄB†dÙ×Ò+ºxÅêxìDþÑ@ [†aåÔ‘ÎXF}n¤³¨Ówóeš¶ÓÒ5KçMbu5üDaT›[óÁh¨ƒž~ž×‡ô¡~C5Ø‚CÔm¤Áþæ×™ +tõ°{ÇØù™*}N” €tAIã Á¾C "OÂRð l$ÊE'9Q¶Ô Õ˵4aM¤¨¹¼I#@ŸÉÃú ØK^n®-($…‡ÌŨ4'1@Æu½qÌ ¢Ú>þéwPaXÉw%±ÁŽž»ÿ&B«yüØ,”dìÆƒhHzêøÿì} `UÒÿ{Ý3I&w€$$\!œÊ’ Š(°"ž(®Ç®ë º^«Ÿx¡ûå/ëñézr誫¸ˆ ‡ " €9¹¯$!áÊÌÕÝÿªžôd2$dÒ3S&}½~¯Þ¯ºg~]]¯jÒåCYohËw²Ô,! O÷h†¡¶0ª{ÜX qöÛöà£:}ÙÄHᤨ¿nWCra[èòâ‡+ØSF©™@­“÷›µÛX„ºD"„a¾†ì¥ÊXUc†È"ß@œõnìxµÿ‡Ë!LåeÎÔÕÆ+"4H«‹qäÝûž1î1Ã÷›v@|\3œ¯>0x‚ÄàX[R íšZ]‘åv›×yÎYW‘_Š Vî¹~ oO’|NÎq°1ÿb-òÓ9NkÑ¡Æã`¨FO\_MðÝZPttÀþöúìÙÕ-€Ž+·ßc—ŽÝÖ¢8³ÇÙdlÔ ç:­øa]êßBVž®×»ï”FäMÜûìÜñ²$gÁå€Gï˜ÈñõnG‘dÄ aÒ! yPÌà‰þȘ¥ó#÷&9ºuÂHf¬¿â×l5Ó(jô­ÄI»7ŒKU#Ú`¼öUsÔº˜isÍo;Øô«GB|ÚÑjFQ$˜l_u‡P]¸¿w·.&Ϧ†¨üïêßXÊE½ÔÔØfˆAy´‚?ð§Ë« ®#døw`5Æ8µ8ññæ«RÕdE]"A&l{ÈŽ1t]ÏÃö*ªjÔøí¸ÞXßåÒ“NÞÝ7ýª›È¯XB¥Æ‚…sc‰åÓdJKSéÅHð©n,\©V–퇾%úϊꃪÀ•Gæ?ÿìÒöëÍó-“E¹ 0wµ(ÇDÕ©6hššÐáÑõB'ëß$èLLÇ€0XS¸¬,íÙµ“pßM#Ãu1zŒ2€I°œ*«V­ÀnŒõéá° Çu‰rZˆ±Z‡_øÛdgh,$¾X\ö‹2i$§9Émî‘b6üP!®ZÏ=n¬ºþ ¾aJŠJ’qßùü¨·îÊRÝ›M ¶kAW„¢SeÎ,¤ÍIîÞ÷& Åø j,ƒÁ³š]ãÐ[9¾¦`Q·Gè“É×vñÅ‹yÑ&C.ý[¾õ¦ä6§¬²š-øf½|,É0‰g΂æ¼ÓÍêª "Êm Ž¥õ–ÅØNõDª š¦&t†@Hd"C_eÙnfæªbf3—1cP¤Î¤$qüYÏνF’ )ÚÅgŒ{ÅÛQXqwv¨¦îE¢ÛTq‹é̽‡ÁGVV“7`&Pü ûÅÁ¿y)ÿ5ë›zå0°ðžÛ@„´©‚Ö0­ KHU…¹Ê®kÎRùÜ‹kߘ€ ]I^þhU}5]y׋µ'¿Ã$¤¿ùØc˜¡S·å–[n‘f>ûÂú-»ò®šrE’ˆ×Ï €É¾ÿ´Í^c¶Y $î _xf™gzöl/D”[‰wUM ø¸9ÂCÁ?Íaie³tº^€øØa@<åíª„§ö@Ö>‡ÕL¯"“\¾‰¤ïœ¯{ÇwÖIFÄ[úÜ5BÁáã§À½b'{äT‹ñúô=àc¼MKŽÉ®LÀR/NP}qŸî±jF´¦4.Z!æv$ÅØ_x¢><n£'Kë@hç¹/°lcA ­`Ä tÓp-®}0´L¿ðÀM®Ut·Ž 0 ¬˜‘ |±î„kD ÑÀg[­¶-ÿúïš Çn¿Fð„On#bøÍ.|+óõ[åÜ£'À•o™ñ/ó^xr¿¯@>Ê­Ôl‰«5™Ü.Z‰¦wœîâ§\qŠÜ/¼Ck¾%å×_ ·à“ $·ÒgÊþ±H*1ïo0N-`°-,)eû Ž¡5°K%ª»µLf͉Û¯gœêº±iûAp ©„ … ßÑ•# ,ÙhýEâ»&õaÁ¶Q–Rð·ÆÔÁš«{l”v ÝCÖnÝѬjݦþ ëßCíóg˜¤®àÓ\ËÐçZoe}Æ^Õí"Ð$|®7Ù“g^Úœ\Q¦;Q&Íý÷ iWî±ÆªÑ¾V"€÷ÝÛ_ü$¿ôﮌ Â= æÎíË$!#¢ÜÊ ÇÕ?™Ü.Z ¦—œÖ¥Þ½¦’ü”½Dk¾%æÏ;Þ.Cïž6Z 5ùÖà`4CúvSý’ÿß‚eì¥VBÔŠú›Hn?Z¾‘=üê"6ÿ›ujÐØÎá*͉Ó)LµH£kGÚüe ²ˆ±È°z+0fýŒ foý÷G6÷ƒïTblIn|t'öô;‹ÙG0»ËÄQCYÑÉröäÛ_³œý‡Ä]=ØÈô»¾nl2[þK6{äµEêy;6R³ãv‰³÷ÉLa êÒ£wœ0-èyþ Ï­c~iyeí¾w¿ú‰-X²^Áx¾TZ‡À‰3l%L¤}îý%ö7?ÿí;tü$¸Y<jLø`îœÀ¢|¶¿QëºÔÝÙ­öŽ‚×pT _xn,.)øÎ ¿Éñ› s,v½ã‘'Þvq¿‹»óØôîòݯc{¯T1é²WÙ°¾3¼{@$ýy(+Îd¿/Æ4ö Ü0²ËÿøãyÏÑ{…7>[ÃvîËßñé›/ß²bÖ|÷Œ¾‰ŽP°â^šqﻟBÛm€Z“(ë׫kÌ£œÐêïð6©ÝšÀlb𵨽´Þ6 Ý ›7ˆÀì#ÕÿÙ½mÜF‹/úG›‚ÆI|¸CÇiå\rjuÜ—•éÃddWO>”Žšï³pΜãz’­9²Ì\°ÀÈŽœœ ^r³Á§=2!®‹|EÊaX¿žŒü—Ï d5d§J«Øap­8x¤„8\d?YZe€K¢Nò pÍfŒû⇶œ»%ß:J>Ê­Ôç©òƒÎbÉõ‰…/¯„uè^UižsVO ðó®ƒcÀÅŽ?Ï6OÈÒÞ}4E’±ßÆH2îonÜØóÔ¦|¬ó=—œ(Sc%,$¨±Ýº/soXÆæx#IFðΚ…Nã/=úÆoVŸ©¾ëȉÓ}¶rsÿE|‹Ò¯g †äE¥{L'ð¨Øi+àqÂéþÃÇÕ7ÙûHzvþz㘶洞„(1oÛøÌ+‘a&, )'³ÂC£ !åŠ*3<µ)ª¬€MøòoˆÖ)&á›^øÐÔBˆš¬ND¹Ihšw ¼ºÞ*"´góN¢Z^€ÂL˜µö dLµ1KU õê1‘ðÞƒüxM0ˆ‚ ÷ÈuÎ{Ô¦{Iqãg«6K@’3â Ï@Vµ9º—ù\ÖEëXuü-íÅ$»]¾>¯°ä¦‡‹ãyÑ õŠëÄ!Þ¶Ó)‚EG…±.‘¡03¬Ùß.”‚:† < iÜ!IŽr¨è”l¶Xa¯·{]¹Ö¤Æï¨d(˜ˆçË5¿Ûá»ãˆÙlû¾Øb/9]©ýhYeE®å /†œ–ã<ƒ`ØõîóOîñ·Šs]CÚ1"ʰ´X+`Æs•z¦Ñ` … »½ò” °n*QFák«Šˆ({¥½Shø±KŒ •!:eïT¡î¤¶ÚìèÓ+Y­R0È[ÒÒx}Ì<ÝIÛrÞO›“gá'í¡´Wº[l¶Ë$Ù>Hí¥‡ŽL† ŽNG$‡AF &k*!A‚)(P5VgÕ,¯ÂûÍQÂCºi«´ôLáõFH”©žB~O‚?a‡û:&û`°Z÷¬2[{T™-qà¾l“RÀ#G>­†þ¶ÃZ%ä§k6«„7K'ÀÎ] Dÿ8Ì`=Äeã8ÈCYµá|*­@€ˆr+Às%ÊáäzÑ $½ïTt½Ð Y”5$hé  +,v뻳÷ ·ÇQVHO`îk}Ô˜-ìÃ¥¿Ê¦š‹\™5úÚõ>ô_Óë>zׯå#G·V¨¿¼ºþ•{xH|+Z¢S½ S˜‹ëEE}äoÉë}8|,ùç¿mÏ•Ñ'‘ !Ð0³á‹ÿ^)AæA;¤óþ $êø %çS]BÀß ¢Ü »Z”#领 $½ïT²({ŸÎ|Iâ¦ü/ÌÔ¯ýdÅFÙno2'Œ/ ™ÆÒlÛÏ^ýd¥ i¸O(\=îœOÚ Yj‚ðiˆ(·B½U5'œg‡…Ä9×iÅ÷0š¢˜ :2vÙ­UL¶ã[4*„€gx÷…çA¶€Y‡Ÿ>]õFƒ¢B4‰Æû]´ê7öѲ_ÓÝÄÅ€¤æ>¯ý©„Ày ¢|€ÎuØl+w6D:×iÅ?0ÕëÜf¦Wàþ¡uýŒrþ‹Ï~á©^Ú¶+²|£‚Á¨îlÞ~=7ÿ[ióöDEx9NuÕü´ÙõV÷h›  @“ùÀÑü E‘ LKµzd2bÆÐæŸL5}c`³T;~ol–rH:ÒÕ'ÆEƒðÎ}vάgçò­»òž.¯ª‘ï¹~Œ ÇÔÈÞƒ¨ïHоÈ_¬Ù"çž Žî#7ÞI$vûÎi$„€g ¢|8×1ÒJPÆ#§âo]21"Q¦Bt ^xî™{Ÿ{1wÿáâys?üNøóÔц‹룲t„LÔgÇ!{´„ý´e·²ýàQ&rV*ÂcÇ÷ÓŽ“ˆz&¼¯'Êèœçê çºÞžª©®-e†R-†H&AàvOˆŸèì×]·|t¥£ôî §HÚ5`®9ã±kÀUÏþ¦wWüi½æÎùhVÚ ;ªjÌ‹Þþâ§#‡ôUn¾*…‡…83óÖW¦5ŸC}Ô³÷f?þ¾K.(: ÜX(…ƒ Áoý+íQò ó9Ó€<‰€×e Æ<æùÇØ™òjVVUìV›•[Ï®zèvq¢ì/jw¥gBØŸ­ñ¤Þ 9cÌ:…‡°‹zÇ«É0)=|Iª¤Ù•PyV¸¶ïMÓ» Ì}11Ù„7b¡Œ•C O‹y@ï®#«.KfÖÚ^ê®ìŸJX€É×@ݳ. sSd(è?"„  b¯ø.>§wW¼iýÜ,H{6ã¡·ßf9Q>'\1²öð«/¹X¼ú²ÁÌè˜xzîè¨7!€™Úö±í޲ìý‡í•Õf|÷›ÉkqbüêâôzÓHVB@—x QF¢„dx]ú>¶!k/ƒô@  3±°Ð hôìPŒÆ`Ö-:Õ©T°k;×=²Ý•WÕ²ÂâRVYkf«6ï`¡&6:©›<æŽ×H³Gdj‡NPïHŽñGÁb±î÷±M9Õ±w¤þq¨Á‘½Õ6l\u—.À'ôFÕÿÊÛYD˜ tûÈ*‰ö¥% cZžw~Ø5þqÚËŸØíö¹ßoÞyÛÏ{åˆôÇ$`qÑõPÏÝmo0}´A¬Ë`ÜŒÎ,ðûw âf;QÊ.f;‘ÍV;|Åóøš\Á¸øß8ñé•ii¼î]g3¥*„!p^<Ë.Ï+Nã(íÊ-„‰ ¿³ÒÊ6 WW6 ¬$bÉRÕZ¬`Y(«ÂQ¶jÓ¶ˆäMW¥¨VF£±ž07Ž®~÷jÙùíñíÁ’Ÿ3áíA­Cÿ£Hÿšæ\õÿÝ/YìWx˜¸íšKÙà¾ÝÕ%­-ýyiOçÃhoì¹ç^(;õÎÆ ë¨ {yß±ìÊ”,y`¯‘4O#WXr†mÈØÇ`’¢|ÍåC…)£‡yZ„ví߆½õßÑÀ#Gw ç#BytT( 61$Ð6›]]Zm«¨6³Â¥òé²*0 )ê;%`ǧYù–‰Â²8¡ßÏii·X?Ó®rSã„€?" k¢¬Y×nÛÍ–­Ïf];‡³û®¾’õîÖÅuÕä˜ñµêð=ÔÏ¡c§Ø²_¶³–of“F bãRкÀ Ñk¬ËšÞ1‘‚Õjeë3÷±Õ¿íVõ?cÂҿەИþç}³ž]?n8É ¯Ñ»Û°h³|¨o`ù¡Š»YÖ§àMG/‹ÄtºˆíµcÁd¯¯LeqPŸn`pˆgA𪣋‘,ð³Ý¾—A¤&p¡¾ ­Ù¼càåCû ‘áÁ-b›õ¿jcŽ Ñ’Nþ¼èdYŸ’3ý¥\©¸•Á ¹”làEä—›Ÿ´ËJ¼BÝ ßâ;EEÞùÞÜ9‡ÛLjˆ Ή€n‰²F–ÖnÝÍ–®ÏbCÁ:6}| „akþ«ªsŽÜGâCÄÓDzÅk3Ù÷›w©Ìþ0âb¨0#¸§èÝCÓ;ZT,@’×¥ïa?ü¾—ôßÌëÕUÿK×e©~ÛãGYn&|^_-cj|0—¥™@’gÃëøxÍq?P2³¡'sJ‡žÊyãƒä‡·Ô˜­÷nÎɼ1û@¼ºWÐÒ|Qb7>|Ý»ÇvRÝÚ<Æ©²Jxvœí/(f{ò‹¤êZ³h0Œ ÿRBÅO˜™E€ ÉÁ/~ø=`ÖMãàûK5¨zB´vé­ÅŸ®üå…mŒ?²àÅg¿pí(-íë€zë°ëZ'ŽB@·D_»ï„ð6Ë6d«$éöI—tF^×/>L¨x­fìÇ­ûXg˜ì7´OuH–ÅøÅyzð¨w$Éf‹…í8pD•’HÿÍׄ«þñþ‰íΆôë¡k½7tT³1öMV¥”=Àdé1™)Ñ®S& ÎI&°7Byä{—ï¯d+ç`ðº~½!T|ù!YÿqòXŸHô{ýn}&3Š¢Œ¾ÌÝb¢„ø˜(˜‹ëÑQàá]´Ÿ)¯b§ÊªØÉÒJvèØI ÇÇe˜Œ«&½±DVäa^Åçï§=ó#ø×k“>Êg=÷Â#0aíý÷¯å3oüƒ×KŽƒñ¢ï“óµ\î_0wN’ŒJ!’Œ(P!ô…€.‰2’%³Ùƾúq›úº-ÉTZŽâv~”VnÚÉzu»³¦@IDAT+Q„#„NòBË²Þ êÝ-Ð’\^Q¥Êݵs˜ú&Ao²zƒ<šþñ>ê×£+3™ô©woÀR¯2îžÞ½SM­ýá*©ìap±ˆr•nó"x‰ÿš"ˆ SWÕ0Vâ<üýÛ¡ÑÌ|Õ?á Ž#t±(!ì¨-é÷Ý–‰›–œÇ’¤¹1ÚIÑ …(aÁ&148‡˜Y(|LAŽ ·pÿJ’¢úÖÚaò-~ÊaN Xe˜[¡/§9X„ðe’"ÿ ⟠¢°î½´ghýÌ^%òÚ&[0÷ÙùTŶ;ïØÂ~¼’Ý:q¤€óT¼¥ Õ|L¶ý}gž_»§g-˜ûÌ2o‘Ÿä$üÝe|õŽÖeìQ'î¡O2¹[\ØeЏ]?f›ÿí¯lSöAvÕÈ‹ÁïOŸáã4½£Or-Dñ@y+j,ì׌$ý_˜úUÜ4ýãýtͨ¡>î±ÔÛiÛoˆ±YÙc5µÖ¿õ8L3¿¢œð8 ® Ê+½Ã?î÷N.FÁhP¾U¼•ÕV¿çDkàkáLPûÔ#Omú€±MÚnöhÚjl5ƒ® ‘d{¯ò*© D[é Ú1Àz£áëºX‚àW Àíàaƒ“áíоîëã*Ëì—^Q[Í"kª˜I²ÿç’õ¿Üíb5vö×Ô $Uù÷}ÿxæø•ÏsÑš„¡ð†d2LðK€ˆz,®Âó±-@Ž€[ àkż!Q/Îþ*=ÊL2„@ãèŽ(;¬Šv¶1ç€Ý€&î5®¸æîEüÐú’uð»tp‚:©Ï`@_e®«WñšÞÑš\ q‘Q^”›ôß\M7^OÓ?ÞOã/%é½q©ioSdÝ×K²J³!RX‚“«‹œsìËÉ)ñ´ XÜ‘^]kkõ›A ŠÕ2_Qä‰Ú>\áýFä!Lœ]Õð8öfÚcg`ñkÝ-+y))£žÎR­É@7&ÆÄÏj IÖzKó'z \Rñ?;s=³ãàÑ®#ä‘Cú— êÍ:G_ï  ìh1Ävë}‡Š”ƒG‹»]À•äˆ4/0XøÏ;ÏZ6@Ö´¸T ]Ï(Véz Æ Ÿn8O‡9o/¥,/þNµÐ®J;«¡ïÿÏ8üû‚o>^í:ÑC¬y~râì’ê³Njƒù#GÆÊ6Ëj¹6÷s±`ä“zmÚTÚͳ÷ÓT÷…¡­ÿ–ör_›$M(­¬“¹·`ܶÝùN—ð¡¶Çv‰àÑ‘ab8¤óê`… KS` ×HAH5YPÝÒ “‰!*ˆú©R\ ‰Ž mqŒí§Ê«¹FˆqÂB¹m÷ìLpyI‡ïÕŸÞM{²¨-ÆHm„€>ÐQÆ×ï„Ï9 ³£1ã¥]m›‹q <1“F“À ‡A’:G“ú4½£\(ʉò’þÛNÿx?á}…÷â­½·Íè|³•Œi]Ç)’ò èk¼û¯öròÊtiPY¨{ßÞèn*·=ïÿgÃ1×ïùí‚h¸wÒã¶t×É}îç·f»x„ªS'VýL¨k§Jd†) ¿g´¦Ý¦Î}?íé\8†Ÿ÷±Îi/õ·ÛÙ`è¿_•ÙÚ¯ºðd¿üc§úr&w‚K?ë4·ÖVøT0E)÷ŠC çÖ­vÈĽ1†'öS&¼æ¢IõïDÀõ ´ÃGÁÖÕÓåÕµjZêȇ‡4ßÕ3¤¶ªDãy"Þz(šÞ‘(£|ÕðAy©´˜æï+=é½íFç-Á›ž}mשøE’GºŽÊa÷äËò¾œú]ÉV×cîëk^3Œ/µ[ûµcp¾ß‹A#®üç¸qè¿Ü>E™>]ÌÏ?ø5T‡&ø9ù–„ŒLêí™RAÃEõי …ãQ"³EÚ™.K¢j¥DI$Q‘DüRä5f(‹aÑeiiw›]Ï?{ýɳwÑB€ð)tC”ñõ»ö Þ 3†!µ§OÝуAúФ ]# +6¢½‚Çr*í€àŠqŠ‘,áñÖCÑôî ËpÒ{èï+=é½=ÆèMmÞÝT\k¿'3}ïÿÀŸ¾‹øºXP>62þÚ°%‡\4ºŠ!߬ÕUg…|§&Ï– äÛÏŸk´¿Ævæ§&Í·C÷jÇÀýwnbf·Ú6- B€ðFtC”5+—Ê‘ˆ(µËµ„?òJ’!wlh˜w¤eQ“åÁøÙ(ÊI¥õ¢÷v×4‰IBjÍÖKjmBº4¸Þ9«+ì¼ “éÍA‹!ïyÊ…„|;O“t8?uøðÀû‚ódÎ?’üç6­„!à¥è†(#~NÒä¥`ê]lÄ­·8Ã[ÃZ2k² \ª|Èæ¨´9ˆª†u›7N žŒ©ñ=™"=V[cû+è"ÄÕ‚¬¦™æìmÑ`zwø²‚²ó5†!ß¾/Xò „|{ ê;ësv˜CzäÉKàîÐö!ßœý¸¬ä~X’ÿ­í‚±¬Mdâ_µmZ„!àÍèŠ(#øCN¥ýЈ’ÞpÖ«\í§‰ŽiYozï<Ûëö)±ƒ­\yB‘í·AÏ ¿s9?Qþ/Ædø¨ÇâÂÚæHÖTÈ7 ¨ï‹<ú©‰·OÈ·Ædƒ„"CYú¾¸Õ€Å ÃŽHcàM|ëVô¦B„€×#ÐðKÛë‡ÓþÈ+<ɺÅD² FÙ8RÂÎTTC<çdóöˆzðˆ¯z ôíî ÓÚ ïMÛsY÷è(H«Û¹Á~ÚðO2§Ä]ïHž´1˜Ü¦4Œ ì¶C¨±WRƒ®üš/^ Žùç/ZÈ7Y²?í©É-êÎj÷oIW8rdw³Ýü=È®ç¬0 @™ÜyËÖŠÆêÓ>B€ ¼Ý厰týóÓXiyµª7ŒÙ×%œ]7&‰uíìøÞwW(úÐ~´b3›~U*KêßÝý0;p¸„pa¬bíá£èd9[Ÿ±Ÿ+4ZŸEƒÀâ»D²»¦\ÊNœ©dKÖg±ãP§sd»~ìpÖ««šé×C#§nÚ SzFAšœûËU> Ê­3öªÍƒõØŸŒ†×“–k4ƹäXýš8ÃRSõX§»hõà+äŒÂ„''?!AØ5‹¶Û£Ë<ÅŽ÷®ÂNÁª­p‘Ý“˜žµÎ£BPg„!@x¿'ÊHZv™=YZŲ÷e£†:ü‹‘èæž`ÇO•³¤=€Ç«*Aâ„ǰlÌ9È ŠÏ°k.Äj,6•uŽpå Ùμ"öÀ-cÙQ¨óéªßÙý7aá!AlÓö<¶"?Ý2~«¬1³o~ÎKf몶K<ƒ€Õ&©º¯ž¬ýGX÷˜('aE½ìÚzŒ…ÁƒÔø‘YLTC× Ô¿F†–¬Ë_u›0vC}Ÿ(­t þÖ],Î}êîIêƒVþ±“ìöI—¨Ç—¬ËbAAFvÏõ£Ùï;óÙ2 ÛÌPy‡ó|ZÑ7Û§Æö¶)ìïVÅr¼ q^ 6Çrø;Ÿ½•úíáã-‰ò "ÀLt=Ü‹<øÁ‰³«0aF‡”ÜÔ¤Á>~§³sÎç$¦ç,rnÓ !@>†€ßeô)F"[ ¯ß»€uo@¯X§Š‘HÝô‡AàÜCݧY µ y@¤“úõ`— í£î:tì”J|q#ÖãÀ×9ÈRß1êq`ïkõE½»²è¨õÓ)ŒíÌ-bcSú«õèg¨6[Ø2°ã$=;øŸ_7v K‚V,ÉÆ4š%²¬²†.¯b÷Þp…:¹¯_Ïhx0ªwÏDýß06I%Ò‰0¹o<4¡»Æle‡ázCËr¨)€¥\Ô‹}°t£ê+U÷¥õOKý!5-.U’åÇÁ×àf Ç Õ9?Êå­P1jáÀåû럚š9 =…|kLäüÔaB¸g´càk½ OföËÚ6- B€ðEüž(_>¬jF²´Ü!|»‘ýï½×:u=\.+ðƒÁ O”ÑIpÆhh)ÆÒ¿g,¼¾ÏV-†h•Ž1±~@˜áG–åRÉÒöƒõÄê$øHSñ,QaÁìÉ?MTÝ%ö*«ÿ “:‡ôí¦ ’×¹Q’Œ—ªuºÛœ êJtQ×›wä· g¿ï:Ć!Æ‚îøÀõõO™ê6þÁsŸ©hp¾ó ­t8 /žy]Üd°¤>™ Ǻ *ÞÎözròE_ò´ ðº©å_=…|sn禟¡ÈòÛÚ1óÊÄľ°¬lm- B€ðIüž(kZEÿÒ`åݶ§€Ÿ†7§u}ˆ+hyì ¯âQ3°Zµ‚“ÂÖ¥ïS£`$ÄwaK ’&~a `Y¼ \<&¬U§e"€dv ¼IÀèGÀÚ«åÆ"[hbâ[,gÀ£LþÈ(UÕfí07bûtåïê›…+‡÷gÚW ¸j`¹Ü.ðÁŠŠ~Ø=}P€¥æÔ™×v…ÓÊÅî’‚òOЍ¼–ºüÄOê±å-'Èz ùæ>F܆ÔÔWƒÈÀå¨îu ÿ- ÀtKsÃÚ5Ö&í#BÀ[ð{¢\Yca8ñ ãoÈܯZ£#Ãàµz=nJ™}ºÇ¨.Cûvgf« HñIði T«£?kŪNÔCîIahô‡îVǘ8†þÑF˜ìE>ÊM!Ý>ûѺºGÝm?p|Ï% ¯QÍê C†‚®7fd׌¤N´ÙëÃánÎÉch‘9¨·JÀ5Òݳkgb d?§ïeSGUý˜ñ u*ú@2èu z÷ÕÔœ|$jðJ ˜"¸&ó¯ È)ËKv´FâU¯®.µ›m$:}œ9«…·M©W¾2nZ§;¶äH!KÊ· …# 绦Ðk{lÚÔ¬ä(+=õN„@ëð{¢œdü éÐ "[Œ‚0BE3ʨ!‰jìä·¿Z§ú7'ì©F9ÀS‘ô¢ëË6ª¯Ú°"IîÏ&^z±ÚÇ7?g±¾Û¬Ãþîš|)åf`Þ–UÊ«jÙ[_þ ‘Gˆt­ú7»©>'Â$Îå¿lg9ªVèÞðУôO_ ¡1z r$ÇwM 1–»°;'dßÀ„>ŒãßL €H`ئÒd]×õb …‡Ls0YM r¥RC`4Šÿöí±B·c-ÚTC¾UW¿¦HÒÝ®'‚þ× †€Y×í¾¢å¡µM†¯ßŸ¸k3CÄ‹zr=„¡eqþ7¿°›¯JfÃ`²ú¤þ¾+Ÿ­8ÍF^¬’&ŒÕŒ¯ëÑšH$ɉªgVÐíå\®/8Ùν¸_—\œÀRöb6IjàËŒä#›<ý§kXdx°:YpÑ÷[Á­ç°J”‘PϾ®Ð=–Ʋ<º÷MÛíƒúg]?Èñß%{Ã(u= \x;<"tA¿Ïs+Z+…3ä<imÁ×J‡‡|ÓdÑ–G/ÖÍj±Ã“œâ“óSœ&PB !Z„€¿ à×D¹­”\O’-Ê@Œ9ø0cœåòªÕuOþq5V3ü(: ÆâEK#ïE}Õ…³o#Y‘YYu­úT>ï˜úÃ̹"È®hxvýèôî¦5¶;Áÿøïà|ÑY½sž÷ê¿RR.úÚ1Aï¬-Ú¡çoî9VZYÍÂ!=ö¤Ë©þÊú’Ôÿ¤ÉšÖ#^‘¬+®µÝÖÒÎ à Ì—1ÁðæˆåE›Õc+JTié†ÞC¾¹çèe—™¬µU+àYß1Ó˜sgÊ+9ݽ.m„!àèŠ(£k~pâ†kóæ‚áá𣗂þÒ"X?Ñ¢­áŒK=WyP$pçözýcLl=ÅÅFýÀ}¥a­½{R˜ — îÊ’åVˆÞ`tNžsQ!0þoÁÀß¾¬¸ ­äj2äçï‰<ú鉗œÆp[ ÓŒv”±c ù•¥_I¾«Ã­¯ÆøŸ3r~lÆéT… ŸD@7DÙ•´…@¸­#'êC´ù$òTE•™…Ï&Æ®¸{X$µ»Æú—”ŠÊúPk!—¯õY¡ëzÆ8b>ãØÃÝׯ¬¤¥ ™™ïO/˜GÙ~%ŽH²Káùð`öv¨ùÑ…$qi¨Á*†|+µýC‘쳡C×ø’ÛÑpï¤Çm`m¥ºA‡m°¾Ú‡R‡$ÙDžsá‘ÄŒì/Ú yj‚ ¯E@7DÙÁpˆ@QUkfµ^ “1Piˆc•ÙÌ¢ƒM­kÈCg› žqqe-é¿ðVõ÷Sx°#Cd5«ÛföMV%—ý%#}Þà ¤#'½‹´@Ž7Â#ã›É©÷ÇÓÒä¶$­ B¾i}ê,ä›&–ë2/uø«àPÿ§ú}üÅÄÌìwê·i ÿD@wD-]½ºF±-{Ž@–»6|€#}´ª§mF8‚¥ˆu 1:_½ëÍ¢ˆòh”3ÿt-é¿mÔ¯âˆúÇûJozo£!ªÍd_ß5A²³‡*¥Ò¿ÂŽp×¶;â3á_)+;Ò"®Ls­Òªuo ùÖØ óS’‡ È;qþAßÌœgÛ´B„€# +¢¬¥ˆ+ ±…1)åÖ_Ùû€*gá&#øÿ‚7&þ«#¦­o½õ-h² \(ʉò¢Ü¤ÿÖã‹÷ÞOx_iX·¾Uý´>-þr.Ûÿn·Ë7¸¹: Éi¸¬<à½äåG‹ÚCjo ùÖØØóS‡ß©©_ÕŽÁõ±41±ßý,3GÛEKB€ üÝeíøðiºE±ìübvèØ)æšÈÁ¯µuƒGüö.aÝÂDWRr#¾ˆ³†ù4Ûf§h2hz×ä‹6 ªÜ¤ÿÖAíÐ1ž£+½·nTàkœ6Ö‘¹w:ãG™dÑÐ÷'¢ñ½Š ¼Õ5ÈøiÅ…í’E®©oÐû×"~hâ쪖ç´n-0-8?/%i X’ÿ §À·þKbLÜm”šÚý%BÐ QFaК(ªDNd÷Šfû ϰe¿ä°¦SÓc*ÍGS2/ÝÃDÎâÃŒÌ`0À‰²ãƒx롸êåC9QÞ“5²*ÿƒ·þ/DO¨¼‚ êý¤7½_ȘvLéeeÖ™é{„ó»»·\?rA|3ù»¢à!Ì?»W¿ m ù¶úð’‡d«åEh ØÙg‡!{ßý“—V3VåÜ­Ç•‚I£ ßÑbðÉR«œHCà4¾zµEò’L„!@tžbJøƒåú9k¼šeÌ‘(™ [ݰ^Q¬äL[¼ÖáRxÖI´ãœ,^›¡â,³ KàÒ â‹8k˜Ÿ³‘v>¨É éåsÈ dävè?£¥ðÍæñ¾Aüð>Âû ï«sêæ®ݳ}JìàÌ)±ó­ÜR¨0ùŸ ˜ Iæf¸Ž>4 ÂàÔ•'&¦,?¾¶qm^êB¾mUdåMh\#É2ô÷ŽÇ rä6ï¶M<°owk¤ïÈÿf°©ó—ƒï¶6r¼MwykÈ7w RRâì6i-d¦œW€÷Õ5=Ó3óÜëÒ6!@„€Oe$ÆêÇf¶”–•WŸE”Á:¤8#¤|F¢Ì,f ëÛ5œe¬ ¬’ÍÿöW6 W,DCèÉ&ÄRœeãäb8Œ÷ŒàPÓÕhf‘&±G ,OÄñÕ^ÁëáFhJï‹…Ùì6-UC*53;]ÊHÿ(Ì]ÿè“Þ¯³Œ„7[ïeÕŠÝj;­Ý£tÓ®¹LfüA‹Ìn·€Ôá݇ ž«—B\”wSVßè¦} ÞòÍ]Y…—\ÒÙ,YŸä>x ¾jÍðBéºÞé9ÞÂ,Ú&BÀOe'I†¾esMõÉÊZ ¯ ¾“Zq&Ü+À¢ ûCCB˜H ,Êñv‰…*Ya™…å-Q !|Ù³P¨j·O E“¶ã—˜–¸¢ª’‰XÔ8É È‘‚…Eep±0°`H0‚†……²ÐuqE|‘/ôázq.½ÛAï² 9!X-3YjY©M`y¤õâs×?> EJ¬{d ‹k¶Þñ>ÄûÑj®Eæéz¯¶ûE~èÏ AgN™o¿ã%…%;ºoÐm1X°€À©ß>ÞàH;n¬~U¼ÍRSõ6tÑEë¾oÎ@¶º'&Í– R„÷Ìy;=rdx©Í‚îƒÔ±pncŠ0½wfÖml´$B€hO°K×^ ÄŽäÈŽïÝgºSŒœØ@2tÀIGè§ŠPt»€WÂudÉ:.²ŒUš%V6&«ÅÆÎ˜«Õ_wl+x²”W:»‹q™_äÜÛ~+8_É#€¥I”˜ ´‰– @ÕrŒ$9""Œ…i G² x"®ˆ¯^Ü.4tΧw<.Šff°ÚX'ð¨µu–Ef7[ÙisèÝ󤿶²^÷¦0Ïé±àBÝGA’äP#caAøÁñpÔ½ã}ˆ)ŒÊÛ Mâ=êzÏÂfÛL"ÛåûOª½:ïìÞŒm31|WîÖ}IêÂL¸Ó=S~x-¨·$[æÉŠ<±aÞò­¡ÌŒ¥¤—ÙÍ+a \e¸—îìž…û¨„!@œOeM‚*I†¥}ç¶ß\rÕ„²ôÝù@”ë~ö±¾D÷ °*¡ƒŸ*IÖȯƒL‰ÌhÀè ’×fgv»]%Ñhu„{G#ü{pïGÎÞ%?í\÷Ô â…¸À¬u•Ÿä“jQD’¡®£Ûâª'k²†Ssõn¶˜ááÉÎ@ïa2Ø!;Hï(÷þìzÝwåYÝ×ë&ÀƒOkô÷¡I'ʶmøi K»OñžmÓ‚d^RíÜë%+@’E É_Âwá§Èœ?š˜‘ýoç6­„!@œOe­SIFç>ËæUËWõxðÑk¾Xó{øSwOŒðþØ¥ iBâ‡E³žá$4 s~¶f ÊfœìUgU– z~GXM.Ü8*2Âeí¿ªá‚âz«¢#Z7t½@?eü Ë≸ê¹x‹ÞCpwOê¾­ôŽaäàþ/©lãòeßÀ`ðÞÔÈr«-Ê9S»‚÷=”—WÜ $4Ô ¬ uÈò\„ÉyÉ÷­æii­îϵýæ¬g,ˆ.)/~N‘ì³¹»~mDý“·¥3æ‹vsämn´ÜCÖ½¡þ Îs8{®OfÎ[ÎmZ!B€hž$Êh¥Âa3|j««+*r¶lüP¸bÜcŸ®Ø¤üõÆ1iØ@r§®ƒÕa1 `Áà‡‰$Yègµ‚ûX”ëü˜5k2þ {¢„„Õ÷Ýå,WËúƒm¸VgLv>@`ÚgÌh‡nø !àЪŒ÷ ã~<Ž$±Ôsñ½#†!õHzB÷šê4ŒZ«w¼ïŠN–±Ý[ߪ¨€xrŽ{ïQ¼W/ˆ¸¢%3˲é:E’´Éò¸z„k0†2…óO87¾—º¼0WÝ»<ͽZ»ocÈ·’²ãó¡£úIœÕBêëL#®|uܸ ø`ï•%?uø{ øšððÖéµÄŒœ´mZ„!@4Oe¤­øã‹“rjჯ2ƒ2]·-*:f1¬ßʾeÊ]SGsWË2-Œ’<\׬ÊhI¶ÂÄ.ôQÆÉ~hQVð0d,C»í^ÂÂ뻈í]¿ÑÎkˆúÛZ”´*c,b| @rlT}W.ônIv…ËôŽòF¹<$yJ÷m¡w´$#IÎØ{ˆÍ;ðŸ-?­ÞÃÁ{ïM¼Gñ^mÑ£æöbc ˽5¿Üçv‡OƒWëNEPÞ3FòEÃ>ë8W ùf©©~¾0þì* ÜNë ˜9ù Kc\yÕz^rÒ+ðýw¿&4Œk>ä'´mZ„!@´ OeÍ¢\b¢woÀÚ%_~7þ¦hæ¼ùøéReÆÄK…Æ|–‘ ¨Ü ‚ †Ja"—ƒ$Kê{\G1|§Û¢ßø–!æVÛäbUìÜIMvåV£}6,+±ÃɲH˜Õu ÐÞ`En !Mv=êewq»ñ”î[«wôIFw ´$ÉÝÿÙ_-‰]x?âßöhåfÝDÝ*†¥O‰ý ‚ÓÜ7ž‹×6´îV@— à^‘¼üø¯êžüã ù¦xÈ·Æ`ÌMö,|÷9I1`¿|’ÿOÔU§}„!@Í@ÀSDEÁW¹ø:ý Ñz­T¿@Èò²ä+ÆÉ£Æüùÿ>[9¸oweÄ D>¤o7gœe$Hþ<¡ZMqòžæ—ŒäX#ÈÚÚo÷äâ§Šç£y×lþ~År˜#pê!I.ƒO%|ÐíâœÖäìë»%I’m¦µT¹3€$Ã).…ó-pI¾k êò'RK»ôÜèª/†|kl y©Ãÿƒ7µcp=ü˜˜Øo_¼Øký¬µ±Ð’ ŽFÀå§Þ#¢¸Z•5–ç$Ð55•–µK¾@Ÿåå Ñ·g¿CL!¡LA‘Ñàb¿óˆ¬ÍêdÐ`ÖO«¸}×ÁƒÚº?,;ÕV;Ç~ÆâWcGýîî¢û=úÓ½$Ùk­fsÌœ=u4÷ÀöÝ™[÷ƒØHˆ5W ´$#InÒšŒÖcé »Uæl–Ýf»ê6(’ J¢"ÿ‡sñ½ÔEYŽƒ)¢ÑoÔþžG?ã!ß_·‘Ÿ<ìˆý|‰ªß§ð ²ÉÄÄ€$ãCB€ V"ài¢Œâ"YÆ CX$kÛøÚ}$Ñ™!hovz|vÀ:ZžÑ­kXÕOyqÿ\“æ¿ï¼ž¦­ûÃò„žÎ±¿[pįƎú½ý]ë^»·ð-ZñžS#ÎÀ‰1’嚺Þ{xÜér‘um·a²Öã3`=wl÷©}àð³7«ëãžè¡%ÿúçK÷@] ùv¢¢øòíq¿;´²]¼7ä›6×%¸[L…-Ÿ3ü~„Â3# S:oÝŠz¥B„!ÐtQÖ~Àñ‡Ùu¬ñ -ÇêD?X¢|îDY—„äÄrÒ±ð¿`Ò Kñ«±»Œ[[ÕËøñžÂ¢Ý[QF £ö0ŠVe$͸ûU¿äŒ©ñ&A–fùš))¶‘°ß­p3X,—€OôBœœ7óÙ‘Ü*tè¦/‡|s¶ eØ80Z6ªÇ8ß$L’Œo ¨„!@´ ˜NµÙœfðGÀ]Ì‘8ã7~ñãìy$#QFr¬}`U·¥X·’µƒ`Ñ}Ä¥øÕØ]Æ­­êiüx_i¼Ï\É2ÞgHŽq‰ ¬ÇC$f»W‘íwBE—€‡p ø¼îS8[düÏ Å…èÛ¬«rŽo?ÃWÉ,oùæö¡ääK튴ö£Q”qn®î¾mÛiu›þ„!@´E”µà«^,£U À] ²F’ë^-êÓýB ,ýê‡* ¢¸¿»Ë¸µU=ï),Úý¥‘e0K_è*Þ±bÐz|©£zý_p­°ÀݶD€èÉ+Š©?¢¯5_ùæŽvAJJ²¤H«a¿#Äg…¢‘Oø=ó¸{]Ú&B€h=M”qø£Ž?àø£®ùR"1Öü’]-ɸ®çâW¯=C!\ŸKñ«±»Œ[[ÕÓø5¢ŒKíƒ÷—¼õÚ˜ApcÝ AÃî€ÜÕgå\Gë1œòAˆÑ🋖Óù×pV—jÈ7Å2Æ0ÁõÈÿ•ȃž8»ê„ë~_X?4")I’í?B#q<ðex2Žïý{F/ŒÆ@„€ÐQÖpqýAÇ}®YÛÖêêu‰¾Ÿ~S†§ó«±7¢d½ï'µ€ïqgò YQfÂįËp§ó ¬«Öcƾ…Ü ôl=Våþzº¸¦`ÉÃvÙòl×§|áì0„û'?ŽÖVœ§è[%dòPÉ.¯ÅuRGÆùin®NÜš±ß·FJ£!B@_è‰(»#£g÷ýzÞF˸ß±!Qö«±7¢dÝ?ãºØ!\b3Ñ÷ÌÉgY!ïËò!†Oôl=Ö°ö—oÚxµe^JÊÙ.ý 9qÜvg ·ÐøÞ[³vhuhI„!Ð>è™(·Ïˆ©UBÀ‡8:½»©¤Æ~‹Â•YŠ]¹ÌÕrŒÃFë1LÌ[ k F¬,Þà Pœ+äS ò¤-ƒ±ŽÝÜX1lM’Ì”.Ø>è¯ Õ_ž™ÓýQ›„!@  ¢ÜÚ"¼íSbC‹™%µÖ;G6ð­pŒè¸&|®ÿŸŒXQtÊ[éO!ßÜuRpiòEv›J’£ñø_—3‘_°-³.±‹û´M„!ÐÖQnkD©=BÀC õ¸¸Ö>)ò,+SF©Ýº˜XYas)ßãÔåÅë=$V›t³þõ°.µRõëŠ$ýɵAp;ëªï…|s#®ç§¦l¶uŠÂbÕcœWp®LHÜ–Ös*„!@žB€ˆ²§¦~6B}!NÌ_Áz|XŽÕ®M™<È™ðÂ…½Éz¬aÕkâk䪷`[u7Àýè— ÖðÙ“Ÿ>r„[×jûÞ2odR?Ån_$¹«::Î* "¿&a[Î6ß-ˆ }#@DYßú!éìë"%›ù6ð=¾|G¸Ã¢YH ’ ÖcØv±-»×Öç¶òM‘ý'ä›»&Ž^–Ü×b‘Áú¯ÄÕ«‚§„I Û²·¸×¥mB€ öG€ˆrûcL=„€’–&dd½—øÝv[í @ž‚Ü}ÁÒš Öã…ÜÄ>I^\ìH£ ;½©(~òÍ]G‡GŒH´X­è"Ó «9''fdmv¯KÛ„!@žA€ˆ²gp¦^f#°cJl¢•³»32æý ˆqÈžçv.7‹Zé^>¾¢x7ZµùkÈ7müÚòÐ¥I v›m=¨º;îÖ@\ë)½Ó³6juhI„!àyˆ({sê‘8 íwƆXKùMœ)w[e ø§že†Ð`™@ >6*ÿºòH©Úˆ—Yµ«!ßÊ‹ÿW‘ìÿäÐ5Åãv_ù¦a -óG ï%›• ð@Ô÷ŽkE¦\›žó‹V‡–„!@ƒåŽÁz%T²¯‹%IÒÝÖRåVH(v–í˜ñ“ŠÀ>çû(õ»’¾[S!߀!Î N½òµqã6`*{¿(ÇFŒèa¶X׃Þ{á€á¹ÇÌ> Hòz¿€I„€Î ¢¬s‘x¾‡@Ö´ñ²b½K‘•»ív{ÿFFh«â°.¤Äw_9ba&„HöþrÞo³-yŒmðþ6s…#Gv¯µ[ÐÝ¢7ž‚$^#\—˜ž½¶™MP5B€ vF€ˆr;L͈ÀîéƒjjOOC× I¶Lts7PA׊½èZd úlÐâ‚br' /ÿëï!ßÜÕwäòáñæZ ÉLéSwÌ"â à“ü£{]Ú&B€è8ˆ(wöÔ³ }}·$Én¿»¦öÔíàZÑ^±7(`A,wä¯}œ²¢ä÷}`ã\!ßXPðC“®rDêð±6w))q6³]+úÖc…¤›€$¯inT BÀ3Qö ÎÔ‹!°÷†n«íö?‚kÅ_ ’A’ûÐ7æë¹ |°¤ÇâÂZ÷:Þ¾!ß¾?¼ä»l™ c vއ³ÃJûþÉK««rîö—$ÉS“‰8\n8· ‚psbzÖ*Á€ÆI„€7!@DÙ›´E²êeút1Û¼q‚ ~ÇÕ6ûuŠ¢¸ ® çÿ ì“áËŠ Üûʶò ’câL‚ñ¿+òè9/©vî÷£ôI6ÛÐݢΒ $Â}Ü–ä~ • ¯B€ˆ²W©‹„ÕäXÄ2k9!ÝâUù\ý+ ÔX¿…‰ZykƼæb~¾o“Ÿ´e0VÒÜæ|ª†€³XÌH’{«’ –õ@’—ùÔ@i0„!@øD”}L¡4œöG`ß´a•JÙ-¶+3VÕ;Hr]÷`=Ý ù£ðˆ°/û}ž[¡î¶ì«E ùV~|ÁÁ1^V .&~òÍ]ÇGRRúØ,Ò:xRã$Ãq«Àùtp·Xî^—¶ B€ ô…e}郤Ñ)˜N:+sÞXH qW•Tv3¸‡h$ÙEäbHò™`?NYz|¯c¿o[PM¼Úðý+Â'Š$ýÉ uö3c³&ûYÈ7W p=ï’Kú[íÖup½8ÒRC8… 7&fdƒ6B€ ½#@DYï"ù:íSbÛ¹rgFúûAÔôÂ8ÏYÀHléle¦ÓÓ’S/ZÍÓü'YF¢qkÌÀ µ}ááár  Èg€>>y¶ô1 £íöËeÁ¥ÉI6 dÖ€È5L亾'Ù/¯4!@x'D”½So$u;" &‘,·uøNH'=Ì•kÝ‚kÅΊ>•C,1f&6úIe[éÛÖcmìÎoʪ‹´}¸L¾„oOñÃo®8àz^JÊÉj_ T1¸ ÏSÕg×öNÏÞ€ÛTB€ ¼"ÊÞ¡'’²€„ ¡äF®(wȲå* 8‚;A‹ fÿø’+§)«Žg®z…»˜–ÛY@4O!ßš§„‚ÔÔávÅþÔÁY%D;™Ô;#gsóZ Z„!@zA€ˆ²^4Arx é–eÙtµ"ËwÖÖžº‚«ÌוþBÔ ì;° ~–’:ðGr­pUHc!߀ü)ÇlÃŽ% ò×o®ázÞˆ¤’dûV£p®Ê&N쑹·©„!@xD”½K_$m 9%.Eä;2j½ Èq¬{“àg+CFõ™`QX2pùþJµÎʺ¬Òî'øðö¹B¾m©¹Û~ÒžP5kÎs~Ù]í— ¿L’”ÕðœÇTmE˜Ð;+3Ó½.m„!@xD”½CO$e+Ⱥ1®—b‘os‡Ì¤‹˜|vƒèw ÀÏDføoÒÊÂcŽþGŽ5dš ù&0þ|Ј+_?ùsÂZ­®¿/¥&µÛeLªbÁù).òñ}¶em÷wlhü„!@x3D”½Y{$û9Ⱦ>!R’jo†TÒwÊé Ég2«_ø"ÿ—‹ì³”å%;ÎÙ Ÿ\ÿzX—Z¹úÿ äÛ]®C¬Ôo“ž°ä1¶Í|ö*×Ã~»ž—š4YV”%@‚8•åª^Ûrvû-(4pB€ |"Ê>¢H†Œ™)Fåxádœ”'Ùj§Bè²@<âêv t¹ÈÌ·‚Ì O½OKkľ쟈~ÿŠx{\õ/¬‹†`E!ß40Ü–ùÉÃn?E€—#ó ãÇÀ'y|¯ôÌ}nUi“ BÀ  ¢ì…J#‘ÏF ëÚ˜Ëdç¦Þ~Çĸ=¶Aþ Ì}Ÿq&~—º¢¨FmeUÚÙùážúoò×§ ùÖôÅ—:ü/0ô¸Þµgù1`|¶m‡š>‹Ž„!@xD”½I[$k2§uï+Ëö;¸"ß!)¬ƒà5 ÇPŸg€Et‘ÑȾ¶´ûQqAà\!ßD&ÞwÍö5ŒU¹œA«ˆ@nÊð‡™,ÿ ®¶:w¾' ˆ_Ýsó¶"Bˆ BÀw ¢ì;ºô‹‘dLïÄøVp©¸C–¬—â Ï¢ÆŒ{ù\ÄE`9¦WàM\k^5&¯.ø,¢,ÙY…3‰+ü‘G?;qv E³pS¿’›2ìY¦Ès×gYÁ.~sæ©úZ´F„!à Qö-úø€CĶkaÂÔL¶_Åuþ õ«q÷C2Ï’Wm‚èNS_‹Ö ùVRQœ&)öÇ€$‹NT8ËaŠáÞÉOÚ2ó,ƒÎ±7s%?5éUYVfkÕáºÛišÒyëÖ m- B€ |"ʾ£KŸ dÊ °ÔœBR<ƒÉÒ4™)!8@Wö dØ »¾¸°(,!de¿wr-*À^¨4ŽÀùB¾·ÁÞø™þ½WQ$ù} É÷iHÀõ÷£‰‰7Ivø»khI„!à3QöUzÿ@”´±†ÌÌPiFmíÉ€G61ªß€ f2¿´¸ðLuh· Í ùær ­Ö! ŒkÈOþ1\wh I^š˜Øo_¼Ö¨„!@ø(D”}T±Þ2,%-MÈÎZ0Zrœ™¾÷fð=ŽFÙ]-Ǹ ÑöÀÞ/ÿ|誒|ÜG¥yPÈ·æáÔX-eÒ¤À¼Ç¿„kïz—ãŸI¾H²ä²V B€ |"Ê>¨ToRúµ]/eFfÆû·( ëæ¹!=Š9¯:·i… BÀ/ ¢ìjöì ³¯ïš ÙÄ:–gX%{’£÷†¦c°Î•*œ-e‚òeJà•ëœþžd­» eQÈ· ‚í¬“òRR†H’õx ‹Ãƒð'Á5zoŸôœÏªL;B€ |"Ê>¯bÏ 0ãÆ^qÌj¾z›a·Éj"÷žCŠ7¾^\dêòàŻë",v¯JÛ-@àûW JÊÏ7ÞÎÓ8«>hÄ•¯SÈ7'*ç\’<š)Ò ˜PªF[ëÕ DùÖÄôœåç<‘„!@ø,^K”ïîåI–&‚fz)\‰òÖZ*.)pv;óÙ„ Î ¬€¶²¨ÛÂQ4¬™7÷éLtëìBdf 23›©–1«µ&å ΃Înf\Y‰@¾Š5VöX\•±´.¡…ªÉ~ ˜üzv”þ7U¨#Á?žÖ=öÉ™Ý+æ'ì7écÈe‘¢#{2¼Ÿ ˜5é Kc°*•ó ?"yª"K_I6aU ÈåpÍNMÌÌÚxžSé0!@„€#àUD9-íã c¶cB–¶ÇÀï5ȱd«‘CìÕ<@²6BÒ<¡9×$qÇÆx¢G­« WB³1X<^˜9çùã’Ìþn}çÍ73C½†þÚ‰­XfLOŒ`55×£[E¹d” ’ »lpàG.*_†°Èï.ß_ÙŠ.µSù£¾TTõ(0˜¨úçL àU²I¨àfé ý;Äë,xT÷Ø«(¥R¬¨&†¦’C‹! ëÍò: èz$¬ç+Õ¦ÊGþ<ûÙ{?yíðµd\¢U„yû±!Ò6UæìVVS= ¬nª<IÆ Î ¦,ß$ãKSñÛúD 'Ôª­øƒ½0ž‰Õ•ŠmÌUz¶³XÃ~ÀÍþ¬UV%ˆ•ذ£öaü¸mà­?:î—¿,|á9œFåä§$=Y÷^…Äq5sžgŒzmM§XÝçÀ„!à/xQ†ýG!ƒìkQæSìò£¿°¸ê"WŠæ/ºjrœð°Àú•îÃ?Ï6uê¼â/Oýã©þùü¿àDLŒ Fck²·HŽíe|Š¢ÈÓm¥l2ãàÆè¶ÁjdÆZ8Ô©¼Ûå%­ó§p“6ñA@„qü]ÿ J”¡+YÃaÒ¿ Vð°Àz·ã‡Ÿ2öb;ÌS»VȱßÃ}3Èò›.UiµLI}hÄðW %õl p·Èá†ÀkzmÝÚÖ×±Ö- B€ ¼ }]Ý ¬¸ƒ$³7ú”®?ÎM¦@IDATð$¹§ùoÄ熃‹Å>e¹Èå«~âÙÇ ôAËë9 &’ãÌkcoM¿6æk); ¯£¿¿ã›TÔÇâUá™\fGœˆaa§»° š`6dù‰¶$('ÊkDùqÝ»ù•Á E ɰ›JS >W†,»wA>öÞ?P÷œzoª-_ݯLŸŸ:ü3W’ NÉ" c‰$ûªÚi\„!@\z&Êüž§Ò&¢%HŸrUÁjnÁõ•Êy@œ/ÄÍh |é¶ÿgœ„dõÝ€4íž>(4kJìŒô)1KTr¬(_‚åø&ð×45èˆó]`q{Nú¥®*IM]Qüº µ‹×ʇrQn”H²’júŠ8é¿NšØ@œ/Ä ï¼ j½7qªÏï†ÈùùV0·kƒ…ëziŸ˜¸k:oÝZ¡í£%!@„!€èÖõbæÌ4ø¿£Ì'Ù˜#kéGþ®WÄíL`”ÂÃ"ç <2i×®­åÐŒmß´Á•JÙµ„wzMÍ) ÑàäŠL®bCÚhe1 ‹SWís=ÖŽë*Iy#Â"Âæ…‰%JRзz~ kG(Z×4àÆ+¤hÒåßp?õ[¸0­¦u-z÷Ù…#Gv·ØÍßÃe>D äy‰‰ýrÆñÖÐ’ B€ôJ”©HàB<ú$“%ù®UÄmtáaE¿›º¦Žõ?/tÉËë,N­°—ç\ lH‹}qÀ™þ‹‚¸xØò¢ýÖóŸ…„¯ÉÀ¡ó€ÂÄ®è“L–ä Ãq´BÜTsO¼ÜI~Zy >-òU¿°žõw&1I†·%ÝQ:xò†d‘Â3}2³ÿÉ2sô'0ID„!  ôH”Ñz,Š"û{Ê# MÜkÝu‚>Ë=Ç’àN— ~œ$B²hhV3ÓC¤Ší ç‹Ã⤥Ç´®Ç >[Õ;œSpHð}1Ý‚&î]0žê‰è³Œ8žd‰‡oÀŸ{FRëûâŸÜää?À|ÖoaÔuãƒD7ü/@’?÷ÅñÒ˜B€ Ú=¾ÒîxøÉ–·+„€Ó¸\ÛØ[B-¡¼Äì²Â$øÀÿÈÜ6ÇÀú§®<‘4bEÉ‹H’Q+NkòønÉ1BÀ‘þÛàzEñ~Âûªç6hÕ;šÈKN_d }’U’ oLÀýHœÔ'+‡H²w¨¤$B CЛEYµ*&Š‚q’;épT_5Dô9ÁKK>šÆvce¿°Ú‚öÿ}‚LL6מW¶sTЈ’ÞpÖ«\BɹÈ:uÉ¢â/aUg²âÜUÖkˆ€0f·V¶¶I½é½Åã9xYl~åikÛÕ´…èH®™ó?i“ÁºY¢˜Or^mn!÷9Gì$±·u“ØûÍ,+ÝÞy‰moO¶¶‹2w ÎsÃ3QB mþegës·qS…«Qc±uæD±ïÇÏEÁötë\‡‘qÂÈËãG‰üm©¢¹Xœ(„A~±%üDqây‡3D4ïp¡ˆ<õf±náx‘µgµ8í‚g¨ŸW˜ÈËÞ.ì\* rvŠÕß?,¶®~ÛBÔó’IÂC‚zÙœ;ENæzqúD/:¶Þ¸ö:Ÿ%Ö/J´D0û%³æ–ëĦÄûÅ¡•K¬8üÇAÂ9¬{/‘:å1¡gí™ ¾låhÙVíÙáç»Ñô¼«DÄ  Ƨþã{Ûeظd²Ø·m¡%hÛžv‹hÚn°Ø“öƒÈ'‘Ö´‡8ñ‡•Ê!š´î/–Ï.Šò3ÅŽu‹A7ÌÁáíE~vq;°(f˱zŸ-\Mš‹SŸ{ÿðQr†´Cöº•^‘l;rÍì&ƒ/¥¯õšPºN"ÙAÛËšÍqsðcï9ÐÒîs{³bqLî"e å°SûŠŽ£âĖמ¹×xÓ`£lÉ’Ì¡(¿µv:Ëî¼Ù¬Ý9Öï¢÷¥‡¨NÖãFÍOõ å}[~¶ò8ÖŸÀЖdí š´$ rw‰ °6$¤ÒO P¸¨³ß¡=Å.8vz©9E³¶g“@("{xg\!M»ÚQJ­t¥`AߦûubàÐ÷,Ëwæ¶E¥âÔ§Íô× sçÐ-ØÞ¾.ÉÿÕÂ#†·[° ø-Õ>5€€Ôz-”+2Œš‘Ÿ#<™»Äšû®/µ*,,ó¸}0¤ëé¢õ w[.Ù«ÿ-¯º]´»çqên¤„žeE jÓQäfö?6J¬ÓìvÁÖíã…àNÝE×1/‰ÿ}Yì_ôÍñ¢â\ SÏ/ ½(׊·”Ü.Ž´ì£ÌÁЯÍ:õýoÙSüòÞ¹–_òY7b¥3É÷ؤ´!;[ûöeêt¼HlYóžØ°x¢}ø¸kÞÜ™ï”scE³öç‹ú*”Sô½ÓÆ4ü[0!dƒ«ÇtIY9帀p@@ª™@½õQ>§ê4'LS4:½¿5´»Gd-û…\ÚQ'½;UìŒh&Ø‚[ÞÀ>Ð<ä§Ì®Í.Zœ”¦ÐÍÙø§å+ÍB:0²£'ëup§"gýj¡“¥»õM÷Zi´À Þ¬›G„ÀÖíE÷˜×,wŽÜMk­¡áxx8vÝ@¨8¼ý›»G°Õ—­´w¥XVç®°Ü-¸ó]‹ŽC*”qP£HQ˜»‡~V:ùG_@ã+ò¦ß·u¡hÝýjÑœÄm£f§Š–].·ÎíNûެÉ7 ¶h³…™Ï…4îäMÇ¡-È¿ùFÁëÀ°V¢ËÀû„ÃL®57ÊJ© ÔâŽr»µÔþ}Ÿ£ID> ¥X$ yPSÚ5ɵØ( @@ÀK A eƒ,¼{¾û˜,¾‰>oþ 4ž2ûgÐH‘·Ž}ß_$zÏøV„¼À êDYË£‘.RÄé/|,N{ö}±oþgÞ$\ÞæWãHàögLýLt¥Ž}­Ú‘+G®H›ò„nßUô|m.“¼X´FVh×¾¡ ùJ³p8ë"qJâÛÞ%¼ça1æÛÇ'°;ý‘¹N ö‡èsùË–xżhÖ¬p±X\>rƒ8•:ÿQC?#Ÿ³[W¿g¤qùˆu¢ûÙ‰ôä¯\þþõiê<¸K ¼áqîß‹V4ʇu‹Æ[ìA7|(®¸/M°:¨Q›’TÅ+G¹cïaâÂ/Ýý‡5þó†ß& îÐWŸBú¾©s?ÿZ õÄáë’jƒ:/_^Üûöð l€€@­(¿8Fu¢bã𩤄¸!¼®BpPZ¶"…{<ö‹Eû^»é°Ø¬B¾ÇLªrqJ˜%óìˆÎÆM…‘›C~Æš•Ç<¶;üÙùù®áÂÈÏÊSÚ•ƒ'áÀ#`ÔDøªÛM"U„ÿýß©“¤üwÑ’IË!ZØ?Á ¥ÂaSÿ>^'înËWUö·ämwª@3ZZÿëÁ'¦¶oœyÚy¡oU¸NIÀÖdO!! !ÛìÀ³ßq¨ì x<² WVp¸B,˱Îeú©¹„+°YµÐQ/RŸ´I. ²tžâΤ¥OžxoQî=bW~›ä·''°oQ•ÚÝ.­ºîý-gõ;]×ÍÏíñ‘‹ó—s‡þ«ÅâÅÙvyXƒ€€@m¨×>Ê'‚i–íÃÊ#PT6O$sžú¡ƒef]S¹ÌÂpÐ"À>¿G†Ê d;Ÿc‰d>,‘«L·Ã¡ÏQkóÇJTÜ:t u`¿;t9“^¼=-5©%tN^þù&ã­¡] ª   P§ 4h¡\§[•:L€ÇGNúóÊ4GÛ—AŸ$riĘ»»$¯øäH÷#;Ö   P›üM(³‰X’jðGPÂØúÆï/;¬S 6ÁÉÊÚº¯lÖ'«bÛà>mS ©ƒíJ[þz§&nî¸lÅ_ö1¬A@@àdð§Î|^Ñf(#¯Psy÷O6¤úP~‘@C;—9^ÚÉæ|Tùº¡ç{TÀQÇëC;œ¬kð¨@2Þš¹>埾›ú÷¿¸°P® ÷a‘,?  ÔqÙ*ˆdŸÂ&€€ÀÉ'àoe‹ˆé1öä†ñƒ¼²ÄN>Y?«A®+Ty²i,º:<… Tc´5¶U §ÙÛõ½Õ˜e…²¢áÞdÚÀ~ch\ÆxÚæœtwK]’ÆG^ñ|…2Cd¨%þdQ¶/™„(Ì(p…ÈBg } ë*`ŽÌ3?/weÃÔ^ªkµ'µë¤¸žE*T)šI¡Ê˜#óäûŠ2cßô£YöÔ[$SG½4>òÅɵÚ( @@ ‚üM([bi÷ö­‹hCnmÔ¹‚—ƒèe`ŽÌsÛÆõ+é<…fq.Y—•¤¶ùÖÇäzr}wë§Ôv=êeyÌÑâI÷] ÍºV®5}Pß¾F¶H¦b‡ÚÒTÔ¿jš«—åËÚǰ$àOBÙ~€«_}ö§ix2S›žÂǪH€9žÂ¬?—-ÙDYñ˜É¼xyW1ûª&÷­‡U7®'×w›~xœæªÒÓ3G¾Ÿø¾"¾¼k ME=Ê4Õ𯽫· )žïqIçeËx,oðkþ$”[;-±”¹{Ϭ­:È¡‘~ Ðß+Çü˜ã¶´tžÝL/Yl±|x¦“{!Þv·ëÈõÝãé&÷éOnÍêxéÌ9òýD—R+í¾å¼óšHþŒÜ,^£IDŠýg¤<$Ž›»¥¬zL.XÀ¿Cð{þ$”mK?Ìõ_¿øl–©e.j7ÄÔiæ2„Š`nÌÏ(*<¸xÞÜï)žj*ÌÙfN›'-Øu°ÚjaÕ‘ëKV僫 ®3u…ö¯Lë07æG?€L¾Ÿ(o÷̓úž£çç¬$‘|£·ÎR¬N1°ë²å5;Õ¦·@l€€€@õð'¡ÌWÄ–E~˜íß¿'{ÃêU“7¿t¸”ÅB 07æ·bÉÂ÷òò²y^ež7›[,û“EÙjw»Ž\ß¿-|ïÙR¬,¸ íO`*˜óÛ°jÕ$¾Ÿ(}µ»r»µ´þ}Ç馸…Dro]¥x¥kËÈÁ]ÿX¹Ñ{    PGø£PfË"?Ð ~3wÑŽôMo¥Ft“?vºJÁ²\¾_sb^Ì-uíê9+ýBãÖ ž¯›—Z˜/sö'¡ìmwª—UW®7×»ç ™œ›‚e™È”#0'æÅÜøþáûˆ’ÕX»§Ô:mîçß›B%’?²5ä$M ²_Ó´ëÉÕâùÍ7ür†  uŽ€¿£Ì–CLZX,åÎûðÝYWÞ~—StíñïAêÜm¿hmry”+„²°O2»[°%9õ¯Õ_ü4ção)O4Áe^3WæËœýÅR{T»SÝØ·5ð§Ï©þJhâŒÞC™ÍUïÀ¯´æÎ-t ¡,ì“ÌîlIÞ–ºáÝo?zŸ].j¬ÝOÛ¹£ ¹v¬¢liׇDòÂ@gÐíþøc»} k¨‹üU([eÊâ.öÿtÑe;{ ¸–IJý{´Oc   už€¿ eË¢‰­£ø!lï³ `‹) &îðÅB™¯ƒ-Ï,š‚H¦Ë´‚Í…E2»SØÖd«#$í³XÎ+Y˜ŸgŽþìvæ:Ú×ÇõF»n5›K­´; û6Üc½@ŇÙU a¼ô¿g_$v5ŽÈ¿ "ÙÆ‚5€€@=#àÏBÙ,’|·Y0±ø³FE µÕá‹ÖG åú,˜™›‹-˜Ø­Â”,–ù…‚÷ù¸í—l§¥C~ìkB»Ýçï–dª¢7 Ý½(ŽÚ°ÛÜfT#íž6 ï-†é™F~ÿÍ×@®u Ç]RR–‹”"*¶÷áSØzHÀß…²œE ^³e”E ínÁÙÉ ÕGÙW4Ù‰׶@æóu1 Ýn5»-m6v;WK»§éaf‹×ÈŠ|§]4½uR™ò%-V×ðuØ‚ßöÃF»nsæc·9¯+Ýî©û^mI”E[ûG#…Ü¢iâîÎÉ+ØÇ°†B . e»Ml!À¢€ƒ-’‹÷†Û…}­ÌÂ6{¿¾­íëC»‹a»}m.ö~…×igÕJy ^R¦ºÝ71‰ä·#\5ûã9@@º(”l¤* …#3Ä~ €v¯†fJØïÉSf;;zóÜ#5mx—äsícXƒ€€@C$P„rCl7\3T‰@êY}»+]$)Ó⛑&廎€GÛ-]šé{Û   Ð @(7ÄVÇ57XjÀWº2WºŠ#ofb±8H™*5ÝeÙŠíCXƒ€€@C'¡ÜиþC ½ÿ³Ó„>“Ü,zZ]þøÊ¥ÔÉùù€€ gÚ/YÂco#€€€”€PÆOê9½çžÛ(+?o‚)ŒQJy§y§^°r™tÈá]–®XUÏàò@@@ Rìq‡+•‰@ü›uÖ»9« g- w?Y’íû=‡¦ ~¨ËÐΆHöïöCí@@N.X”O.”5B uÀ€^JS©³ÞE¾@þšæçÕ5%e«HYé{ Û   G€P>vA .Ø6xpÓ¢¢üxš£%š¦Ÿæ+­ ¥Ø­iŽ;/[þ?ûÖ   Ç'¡||>8 u‚€ºåGzú†‘EEyÏrSo¥­Îzâ5­‘pw^°ü ÷86@@@NHBù„ˆü›@ê ~—¤¥mx‰rOßš’›Å÷—öP§ß—ÿí{Û   å#¡\>Nˆ~G`ó™gv6Œ¢ç•aÞXºrr“æÐé²lù—¥c@@@ " ”+B qAÀXýæŽÑõÂG©:Þ*I‘Mã"'tíÜý%ùñÇEÞãØ¨åJa;ù‰FÆM`˜ÆT“ŽJªH)D£Ú®Õ‡™{½EF]ýÞZÚ áβ¥’Ôim«ÃáüvZüØ”Z*ú¤“>dHÊκ/« w,]s3»ÔöŠò»šæÛyÙ²]"£YØl°ª€P® ½ZNëv¿´Ã³ã~©ÌGtSoÃ)¤°ÐlT˜/t=Fn-ת¸¸ö™{/¬í‚‹œ.3;0XåjÄ#!jÜø†/…„½òâ‹P}HK×ý † q¦æ¸×8t0Ž.©m©+’âw¡Éº.[¹¬Ôq쀀€T™„r•ÖNQ±ñ—ï*ÚöYÛvÎÜ£Nß±UtÝ›!ƒ<ï`µS¿*Åz9(p¹Dj‹H±¶m‡ÖiÍ[=—”ýàÝÇgrÂwT[“–:)˜•Û­¥}9çöÔìƒãé º–º )ÒÈšþT—äR§½:y}~õKBe@@@  'Õ YF}p¨ $’VJÎkšs¨õ¿/·,[(ÏÈØ"H$—»áb̃¹0Ÿ&¹9­\_Þ3æ©Gˆ¿ Ö¹ßyÚÀ~CÓ¾ü|¥Rêr³ ‘l™!¥6ª«pžÚuùÊ ’m.Xƒ€€@õ¨s¢úøu޲X$‹NÛ¹M»kÉÏŽööùu…Ovå˜Ï¿—üä8mçvép¸&ÝýD,‹e-ly'oÿ›ô¹hÓ€>KLÓü‚†{ëe×–& ÙOÕ"00¸[×”ÓdJ Þ’l8Xƒ€€@ €P®!°Õ­¼wŒû ²$O&ѧ†®ü]º ½²­ÿY0'æÅÜ\®À wÜÿèUtÕ,–ù÷î—b9½ÿ+6 èû³®ÄOäfq¶O+åhRÄG8ƒ:w[¾rrû%Kò}Îa@@@  ÀG¹áV%ë¨(w°&å›M³Š«þLöKqW•ë«´ÌmoX¸ÒÂMëÙó¬¾kÖü‘Eå²%Ö¨òOTϦ—š¾é©Ô“†2úúzR“¹@(9-$TNl³pÅááEN”)΃€€TX”« eµf¤MåhCÓ"/ûk…–äʱen—ÿµ\3ŽÖ½/¾ð>Ê…Ç>é>Ë<Ì[ê€~#SÓ6l¦9‹üûz¯¦œ¦›3AÝÉùˆd/l€€€@­€E¹Ö‘Ÿ°@¶;R<ÔyßnE>·°&ŸÙ±#°Ïrâ˜Ú¤ùŠõ:-< †=F­Ž‘>¤o„:$F™‡ÜVc+c¹ƒTõl©©º$¯\Z±²@@@ ®€PöŸ¬=K²Ë)Ââ‡~Jy;*EÀÕ¯W"ç¹WI$óÄ5dX¨{r´pD¶ªR!Íòs»_Ö¢ù»-œ.k0eNþàäˆQ‚^J“O-‘J~«IÇ·““W`Ìã*!Gb¨Ó ”ý«ùX±ÑRÅÎ|š&"Þ|QäÍü¯¾ý!›7žEˆ¼wÉsÀ0D£ñO ¶7š8Ž&sÖÅÁa  B¢ïƒÏŠf²+œû­Èÿä+‹NÈè{„¹i«mZˆ +/ùï"‚n¸Ò:×xÚs¢pþ¯"ö"ôáháêKà ›¦(ú#Eä¿9Ëk±¼ôtëõBk!ŒÔÍ";ñE¡å>|Ç B„‡ }ùj‘ûê[Beç”jBù䉜øçEøë“J«ÈŽ4•“u±m;æ5uÆÛNBù[Äß ¥Íïš’’åÍÓÐÞØhH ”ëikk­ZˆÐÑÿùÿ›c¹E„Žº[xÖmE¿þ.Š,Ξ§Š¼·f‘ÛÄ>‹@ `ö7Î}ašM‹Ð‡¢EáâeÂܹ[h‹€»þ!Ô"ÿÃÏ„çϵB¶h&‚o¾FäÕÃCn ¡£†‰À«.³ç–js×^‘3ñe¡µ©šE™3%7‹ìe®0uñËNSÿòæ¿6,§ÃF©±   DB¹ÿ Š–,s¾±®0ðò!"`@oQôÓ"¡§m±Ž±°5¶»^¸ÎìGVàBß¾SZøxà¹g’UùK+® Y÷>#T~±›…™±‹ý„¾f—`Áçó¨+œ&´V-…™¾M¸¨<ÊŸ'Tn®Èôš•ÆS’‚Ë4H¨..vÿõ$¯Tæ‘BÙ[@7‡¤FmÞ:J×uºH‘IË¡*f‰ä   õ˜„r=n\ÂvPY‡„ ²wK¯ÉרÕë4ËòxÁÙÞsZû6Þm}íz¯HöôÙ`—ŠÐûïµò0÷ïZ³fBå»P8ÈõÃØ´ÙÉ>I„«_Oá t§&>̾5 M+"‘\sÔP½‘-€€€ÀÉ!¡|r¸×J©fyG¢ÐuòKÖEÁ×?vƒ(3•yØ>xÃÕBëÒQ¼÷!Ë/9|ÒÓö)afg W7ò]>"¨¼aîÛ_î‘:ŽHŽ]¨Qe[£E"ó“MÀ`· êpçêyšÂ,w Ï’d8ä\êŒw†5š…£S{ò n]îªj䳬,ηoá<£‡7­gŲ07A7]#´–Í-W îySSÇ?Ë_¹$5w,üò{r÷m"â—EèðÑpšðü±Ü‰#ø®[D“ÏÞï¿*\gõ/w™ˆ   5I dÙÊ¿€S'%Ä á5B¥ 8(e0-áÃý¢KΡwüñK¥3+OBhE+5Yù+ËPŽíPöQ>ÅåÊ“†;æ }‡Ð… 3ëè¼µˆpaææY£g”§œŠÆ™uÖ…b}@Ðßÿ:éAJK&uog>žN£^T(Åǽ_ hH  P§ÀG¹N5WõV¶”@¶³feêøWÙpL‘ÌÒÎæÁ²ó>ÖñÊÖé@@@@ ªàzQU‚H    P/ @(ûW³òÐe´H^#Ô ÆÖ}à\3Œ‘+€€Ô ÊþÓŒ^Ñf(#¯ÐáôîûOënM .Ec(³?ò‘œ$‚}‹„²þL±''(®Û&'(Xy i®m(åòqªÍX,è2òeËU›åÖÛ²˜#óÌÏËÝGÉ/ öRo¯   PuÊUgX9Xn÷ö­‹hC¦¶ˆ¬Î¼l^Ì‘ynÛ¸~%A0i±…2¯@@@@ LÊeb9)½âmÁWŸýiêžÌµm;@ÈUCS0G£¨0ëÏeK6Qv­¶iR}9=!î§êÈÿxyDÅ&|D×µbFBÌsÇŠç‘Z?!Œ)tþƒcÅiÈÇ C#•z„|Ú9àÚA@àXNºPÞ6xpp¡'ïßR‰ædî,”r*ŸÚš¢H¤öï³GHñ¥”"©Kòª¥>§+¼i?Ø5%ß›‘û¿#3PJÉ踄ÿR"‚œïxÅýÀ¡#ã`rB“ƒ"I˜ ‰†_ å•7¶íaé#è7|“Gu½r–4m éÉSË®m¹Rí£`çg|¼}¥[T§Ò½Û^Jm Ý;‘tŸ^b*9rxlüK3âÆT:ßr$¤r¿šÜZލ~%Ú0Pèê¦ qãü¶’¨€4`ÚɼöMýûÞ]T”—.L1])q>‹ä²êCù–tþ^Ó¤èûõÖº–¯\ÇèÁNñÎ6…(ó!õÔÄ „’W‘²¸Ú Èq•+ODòˆŠwI8Ç{ 7È¢øC f_ç²N¾.²ù²k[½¥{<›B=¬„êXÖEÐý$é÷ÝO(ó¹üü¢-É×¶«ÜCʼ÷ÊJô1¹cF|Ì{I‰±Ï&%Ä^&¤L¤{è‰û&LhvtÜê;B/º³¦Ç[\}9ž„œ<âJz±hrJF‘  å P…‡c9r?F”ŒBò•þ>=Èo"\¡@V««‹¤~~êÀ~ú&¯¨ÔçBú\û%zûˆ¸‰ý¦Ç]á[©Ì{iÿcZ¢|c»ÜnrHõk¹cW2âÈØçNÑEQÿJ&¯wÉ–_Ûr°©ôOHGVä–¢û/ŒìÍR’ÿú×-n<ããÍ»ªÇüòo™üB›9<6á ¡ÉOéÿ)ôR<š|4²’âúv¿^à9ôÉö«È:¡¤X¡9Åã3ܱɣÜ»y<Æ7A.×E¯¸ŸÜn×idÜ„ó Ó|]¸Zœ¥ô½/ÓW¦ÕIñ±/óùÙ³g;æ¯^ÿ4‰ô»È$\ ñ«ÆÜ#yУª¸(ÙŸR:?n ç;á&zÉ"¥ü€^$¦Ðÿ¨.Ðÿõ„'ÿæóvxø…‚s2óVJ—v½Ð͉ûYtÑ;ÈœK{wéÖ[oeã½xâ¹çÈö¼HŒéå[º¤Tß¹B½6n\fT\B"ÕãQ:VH/——Òú€Òäsäzö½pœo—?Äô5²C‹IÑÑ>›ø8N¥—þ%FÄÆ7”¼Ÿ6»Òu§+ML›û:Ÿãp¬6(>{øïˆ„„¶f¢¶’/–õÅípLl€@à PëB™ýsöíaKààJ#V¢=Äf§ ìÕ%yÅ›͇ÄA>=`瘦q¥m§zöÙÆ*[g¡7ÔP‡…²Û­´ #1‘òC)n[Z’é¡6‹>—Ze—÷¡IÖVdmâªCåñƒ’ΗgÆ]Æu°ÊÑ'<(„y=힢ù|*aЃ¯@Êú<ûÄÆã=x9~ jBû–ðS>7Òù€DÃÿÚ:Oyj‡¾qФ¼I8äjR~ìp4™üºû¾NÇá„B™—iRû‘ª4ŽòèHIæ;4í­éãc¾³Dž5®«—)ä{ôàÏ×4ñáŒøØ§­ÌKþ”—G?V}¢ŸJ¼Ä0<ïRFP99®tÈ(ašÓœNíö×Ý1äV ‹*Ý4ßÒrÔŒñ1ü›#܉= ú´I¸kà¤'ŸÌåNì«{û°ö'N¹”Ów¡ÎÐÇ^r?lùðK8Qü­œŸ,—ØÄé´ï¼´O([(Ùçkrr]«‹é÷ú §àÊ–Cõ?;/?ÿ·U7µ½ Ïg;¼Â´’ù]ÎéLTÒ·@µ!÷7 Ƀä¿|¯’V›ꇾ$Ñìšv éTé1GšºZHÌú¼æ»î—ì]¿‹²šh×ÃàY©Ö$¹£óȽ£ ½ôî°ÏÍ_½1–¶ÿOiޏŽmºî¹K óY:FŠƒõ›Rb¹Š<âr:~óžhÓ4í~îTätO†“ ¿Z)óú­'iÇ]-Å™Gñp†Fâ·‡ÒÏèþœ*B´{T¾Dÿ—>œÿçFvÅyƒK$‘<‡âH§vý³5=1É“g~D§.s:šLÔÕþNô›+ u…>b‡Y(ò"”á9o¤{b—iî±iü?a‡'ñ*C×vì£{Z|G ófúÿð o‘à¾ß0E,Yò'9Ÿžè·Cb:4)!f2Ç¡:”ÙÅçŠÿ>˜˜Ø*?ßü‘¼Ï>$~”[šo\lƒ€@C!@ÿ»k/°˜HØlL9<òIDAT—•É%Õ%¡¦ÑÃ`FzÿþÛ;/_^òð(ßµÀ ¤Ç[dÁš5úå—{å 9¥ÌÕï ‡ùbåtìÛ™Û-Mzí ôÃŽPç2#Ǹ”Êÿpô„ s_7noyšNg`.=¼—‘k¢6öˆ<á–Ê`p)¶SO¸œ’.×%¢M“rÛ¾‡Hð>Ú,¤÷‹<Â¥Žûàåóü@$!K^›âRòlCö”†ø8óá6Ÿ:ÎK<†j«Ls–aðP‚NU>A"D˜è¡=N ’j4Y÷æŒrOêüòÓ£w?üÌK׿é9;”ÐF…:ƒ•áÊâÊùÛ¡¼¬ŽWÑ4xQáîCÓKÀ ƒ8ïÑ7g‡±8ËÐÅ´k e@·S]ƒMSÝFÇ,¡lÒyâÅ"y„{b'ÝcüFã-§pŽ6DQGâ=†®á[ú­&«¢:–pÊÉô*^‘,U¯ˆFWÔªH¾±Íi¦Ç˜C/T•ÉöuÐýÔÙS¨Ï#޳~™‘ç=~‚ bÖh¤;±7½Ò5Ö…Ù‹di,q)ÉýØ>oR©z¶lÜھ׊_bŒ œ.­Ï4wÌê’x’ø=·ÈcŽ¥ýa´¼I/vü"Ë÷ˆxlòäЬƒ…ÿ {ôÞ?*(õ ÚTûÅ“ÎO¦ß¹m©Ùqɂ˂òÅ ã,‘IçÜ$(£}9ë*ÚµôrõµîXkRË“é…ùí’ósIÜG/‹gÓþì‚D>.C4§³ët÷ØÍ':&!ÁTê~i¦—ÔTŠSD‡ í3Ú>Dý(Ö)]¿„¶ÓvϧuýïùÂ4ÄM´ýÝCî#r=¹.ùÚ'mNפ‰™3ãc¦ò>…W£I$Ó½KVö¼¿Ç#Ú 8jñ_v“ÉÏ5çÛOf&ÆŽ÷=‡mhÈjU(§ì?=D®­.àô@s˜Âx:žÖ~É’rwH¢:ȶ®ØùzbnÑîCüе¬'ôÀ¹WÓädÁGú¼ûªÏÁéAW˜¯ØòkYŽøÜñš|þu÷üYûyÞæ@î/èÊó×ȉ›L;ö•{YaçÌpÙÊçé83ד3)ï@QÚ]u¢/}ÎNåt$ IJŒ‰·¶…ØBâãg:6ˆ®áqt|åõm ,‰Sü°=€ ‡µ‹^îäºr:.ñYYš¦§åË/+)_29>~»ˆRë±:ž ™ùÀÉÒ›Km¯|Ë!Qô-‰bÏ[çn"áGî4I0D EŸ¸å7\˜ƒm3bî/©Üß÷ÇÆ¯/T"Ür®¢cóøxY‰®ÝÊÝ¡¢â&L%aØ_„¹.e^’W¯Ô-·8’ó~a7‡FÕU‰Þ^ÒÔÙ y_yó¤×‰þ†®>#PYôóÚLüû¦=3‡¬’>YÈOm‘Ì eô¤Ö=¯?=îOÊÞxô»øžÚ„_vDˆ3ìƒ\=gÊHwÂÙÓܱ¿"‘Lœ÷ÒWŠ©£ 7 oÐKk‹‚\£ µ÷‚R'„ü“ZÐ:ô°û…¦9žÜ¶$^¯£û÷>H¿ÒÍôË&—o:)öGjçðïù„A:%¹[” é”Y>B,zÑ‹o¾©ëS©<+¹u8¹:š¦u£Å÷«uæðJ÷QüòqÕxçtwìš(÷”æÊ“ßJÇüÃ9pÙÚ÷ô¢øìÏkS;Òñ´âs¥ÛÀ'¾IVî9”g.ýñm4Ÿ(؆I Ö„2‰Ù¦EEù¶x«6ÚôÌié)Ê{š2|°"™ZVâØøwè!t¥û[Äè“|gW‹F_íÏ9üÐ,ÉtdÜĆÒÙm¡# ƒ&ô°ã!ìJuÂ9ÞC“³‰š1Ã%·e^oJ“G÷hgO=ÄdwmL§Y|¦S‡ÅKXäqýòõüsÈÂSìÜÂéËýà•jÇ·Õw;mç–ˆdë0 Žm´ÑwÊ- „ȰE2§›òøã¹Ô¡n¯2e……ÚñXU >\ o vßÑu=N_ wïx&½(ò}›üVZ½þ|·{öoúzHŽñ%‰Î &–¥ÙÎäÕ„¸tû›èÓ{o:f ejƒc 'CyXdÔ\ŽnÓky˜¸”¼…QTv_»îÕ¶–bDòõ­¦üb7‰Ìr)~!ŸZ~±ð†éãKë-M ¯‹G"A¬“ð+òýMòqúAï)Åzù%ˆ\‰>Õ=âÿèÔïôèߤiß:2•®Èá"ù-4‡Iî3‡ý èžµB¡ÐxƒÊøœ^H×µþ¾êRŽ ŸýÝn÷EºÏþ17†³Tyôÿ„nÓ’èR’(Ï'×ïK6Õ‡ÃK¦¸¢x³Ì¿ßÒo÷=þ?°C·^ö×)‹æ¯ÚàÜeLLWp)ÝÓßrÊáÐsIáO¶L{ƒõ_Åd·;HÙvdó½‰Çód@ˆŠŠK¼3)>†_¾@@ˆ@­ e'$YæXV{ ñðígž9¾ÝÒ¥™ÉÜát¾CŸÞÇE¹Ÿí`xô{)ílõŠvO(• }Æ|RWúzȣط.—¶Ï£ÿ%éZ*Þñš$Ò2¶mø™D€Ô”c =NÓ¥Ã$p¹ ùƾ£ç^@>‰»H€n4…Y@¢î6¯Õ´œ^ÊÛrÓ°ó-YÓÒY^AÏÿR¢àˆü+´{g+2‰­/L%nÜalb Spi¯nKgÐíèôi¿”À°ò¤ÎU$D|ïc 'i¢²Ðð/PZþ4^+A¹ÝZʲiOÔDa$˜4¡ Îû®šÈŸó¤~mKɤß.Ê=áÔ$÷8 Ш»Oÿ½InBŸ¥R‘PÐØ‘w—U§Wž~b'ݧÔGX^Hç“í8ä»ÍÛ¬À_tèëÊAR²-gŒµÝì¨Õ¾¦ÿwkéGÖÜ©‰½¶ßüQ…PoC:F"ÿpiúKÎþÜFäŠõÊÑÖ9vá­·’ûWlÂ\C˜W’¿D9ÈÝ‚ÿo kõ&²_F»Kì\È×û2z©(ô´i–n;Öšx¬™÷8M<ò;™úߥÿ+g¸Çù¾H+)Žƒ€@½'à+jôbé¡ñïš*€ò¦,·PþÓ+Rw–!µPz4ÿEÁ.*+= ÖH8M¦OÉÓøöSâr¾ò˜ŸŽr¿6È·£ïñÒá€ÔgôÅ®æCúÙ};‘•ªGM–D–Z˯±Âe(ñ=œŸ hšO§¢RÙйôÙ· ÏÆÆBê@¶ÎãÄ–²•JPÆN˜+d=¸ ¨³™ÅaxÜÄAä;û€oT‡(èFûMH´GP‡²3£ã& fw ;?xI,ØÞ!üi6:vR÷q‰·Úq*»¶„R÷Ѓr=$ÃØ÷qøS‰WñÀÊSÉ…Äæ:®[…Òù„õ sýAå“R*©&ÉOYÜNb"äâÞ§,äsmµî?RÜÖ´y+ ±ð7¾ÓA_DÛ᱉cÙ݃­›EºçòYÝ(Û¶˜ëw‚©11»•¦ÝAÂ=;© zµœ¦Oú•û½—·tzùÔ³´»~Y t…ß@íäQ¦¾¸Ðsh‰ÜGè%ñvò½õN*d92PgK²Ì^-…öæñªîGm°ŠDâ÷bÛžƒÊ M<웆F$!ÿkíUr7xÑ£ïÏ$ÿÞ½ä<ÝÐM}ãUǶ%pCäPª{6 ïö=¹Q䛪h½¡ÔH;G˜c.ýoL9ž­díö¾ÐÍó- úäïý‰·µ³Í|z —/ù»-ªù÷Ÿ —$zy|QlÝ›E×Ï"|užŒ³Ó–w­Ú·|œÚ$K×¼QÞ4ˆ õ™@­X”"Õ¯¦!’E¥ReHW‹O…¾çU©‰c>„É 9òŸš¡/b×'=Œ'³µæH׋ã]ã‹îGöSïú§ aÌ bˆPz‰AeC½o§#‹Ñ~Cš9$ÜÇÓƒ<—zصP[÷†Ñ§Õ±ÔÉæ5~ðF%&yj ÁÆ^öä`!ý -–…ÊΫ¢k4¼–ƒ¬Ö, ¦ÓƯ[ ÍÉ·”7?¦mª2ÔËzÂn²ð}FVøR½òæs¢ú°P—ðœiÊϨœ<áÑI4JÓTßy„šIbh†m9v»o-¢/óèšî0ƒíáµH`Ð'J;”,Ð/æê¹ü);€–Åä’så´’ñjË[ß™ñã~!Áý YüþGÃlõcñ\Þ´•‹Wó÷¹<ð=E‚íØDÚØcŸ->Cï®)+Nɬ—7[/ƒ;³Ïô%Ã' q–H»¼” Gæ[Ò‘ò·ûíaYâ@ßs% >·–ü&¬üø¥W…<¦±}ž,½ïÐ6/Ç ìŸOè_Aé@c?ê{$)&f'íß`õOØy Mx¨#³$­­ÄçÿrîÐÛHÓt;-aÌ®<¼xƒÛ=¬€vÊì@R'Ò¹‰<Ä[OÌ^Û m'>’•}üÈë-±@ŸmŸÇ@:£þÑ×Mú=@¢ojMämçI–'£kÊÊþle½ôôlf—]‘5 QùÀĉÍyX9ßtV¯ýjM(k¿Cvï€ú>u¨ò¦¦Ä ‘))ì¶€ †Ý¼ñ5r±4z Ãñ|EòæÉxxèʤA\ðwµ&”;/XPàòêB–¯u[µ©ÐCýxuàqy£Ü3BŽÇ>ÇcïÚÛÇ[Ww<.‹§Ñ>^™£_~9pöìÙŽãÅÁ¹ºM`ÀW{>§—ÏÃ3 VÓåЈ+Ïôú"£B/µ4OrôS‰÷USj4kÆØø®5Z2¨jÝ”: ïKdyz°ZèI‘-…óÜ®))V$?žòÙ0Õ.—¼àuwÌJ;-͘÷,ms)¶Nÿ%Û·üï˜Æv<—ЬeÓhb®½4éC A¼fΣqmK‡êŒ7<&acËuQ¡^t ÍÆw?MseÍʧÉgÆÇ|ä[òˆ„„¶FZ£)9Šføšå{Ûõ‹Àª[µôx¨cŸRªåÊHx8êZév—{DÇè¸gûÐ4ÔÏÓ,“atøý¬n4ÓåHž{F|ìôja†L@@ê-Zõ—^—ënx$mîœf$0ÿå{¼Û9Tùë;UP$GÅ%$šÊ<”¥ÄutLÂ4ŠÀù‘Îs{ólr4åñrû¾aT¯RC³±•ö‡ÕÞÔDàe3žØ8â©Ä+LSñ÷2ßk¨Îx#Ý gºÚñŠûÉíôOwºš ~Ý}_ b³ÀüféûÔWÐùâ5²´/ð­¶ë'>ŸïÞ³jhäåSÿ‘ÆUn[•«¤ßÌBWyKED²Užò Sšö†4Åî‹{öŒ×âÇüÅÇù˜¿zýDº¯Î¡—Êv4…¸‡ôéZÓ×ó˪©Ô½4‚xGzá $ËxM?šòhBS ¢YƒIüÓ¬€Žá‘Înó2ô/ÐÔî7P¼MOkMK}¬|f<=nEt\"OK)•›-¤üˆ¦Âާ‰}®S†H$Vyôò9ÒéÑÓܱ¿Óý½$ÐKo(Õqö%½Oy´*3pZ\ð@@ Î¨5× ›?„»¤¬ø7=”ÇÓC©ÜV+;½µ–"Í©¹.蔲êçRÇ˱ãÎÉ’tEÕ}£“…–³ì)—Éïù=zPß⇷üsÓ™ôpßÊ"™÷§ùŽÄgðç\Þ·CuÆ3<âŸÔPpÞ$2¾g‘ÌÛÓccw(! mç¡&¼Ïax\â=ô°‚ÄÇÖâ#ø[ß ô™›±^Hç94²Ì•½VºßiÖ<äò>ïïέH<•º)äPéh>—æ,òüŸþÇÕëùå±}R|ìùgà™TF3§³é?Ù½‰^.'F4rý#Ò{:Ýwë4)§&¹c–Ó½ÅnOн*Â\Ý"cçfx6Ü¡„ÙÂæè¥IÇ-¦)¦²?^>l1–šü ÄÚÛª]@ÿjîí~®ÝŒñ±_Ò©÷Hˆ'ÌLŒíÃ"9êÙg“˜Öá’7 WPorµîýÃêõÙ×5€€@Ã%PëB™QóC¬kʪ§éa5˜ö~+/~J—Gb`Rx£&}:%'WªÇ?[»¸|²hÓsÛ7ÈH¥4¯¸$kõzh·÷ÁÛ¦4#©ÎÞxÖy©¶ˆüv¾q«+žÕIŠD¼ u~â›?oG¹'œÊÖ²éîÇ÷ð> º²a¢}ó þÉ ¥„_fl0è´óèôQjûÌò^6Ý ,R‡òTÕßÙ\PÞtÞxÛ2¯‘B-á1ÀµPçgôâv+‹Ø’ó$Pµd¾ß¬ß¨Tû„È Ûe¤žAn{'=ùd¶5ã¤+M¡ZÚyÒKk`¤ó¼1IcÆdñyÚ¿ZjÚtCJÓ •i$jS\µáÜå3c|Ì/¹>ìrñu-+òxηËð]«<ýRÚŸk9¶‰ Ý£¤ü‚döõ¾q°   Ð0 Ôºë…/æ.É«–Òþ¹›õ=Ç0Å?I˜ògÒîô ôŠXÇ´ÿ‡Tbn°pü7ry =lk (®9”ךæqå*OÁQõ4S…“ÅÉkBâ W²±o­ª+Þó·ËFò´±cøæÏõ ÷šáêaûx^4ƒ,‹ÌŒŽöDÅ&Ú‡±n ¤{%y!ùºÈéR˜·ÑË }9QçÑ[aéß±”Ûèø± 8âÛ »Zøð¤)Öï¦Ûõlòï_«çZiÚü°fãåårÇøA˜æÔáO%®¥oG=¨Ìô×ÝOì"+t¦Ø¶§ÍˆØøáäZ±Ó4Í-«oIÆJ.´¿ìðúý_LùtÙä4Q¼/LMFÊvM?=V>£Ü¯…éžI÷:”U˜O÷è)daþŽÓ¤)/¡º]%²ur)þçC¯Ñ«ŽŒ‡}†Gठew§e+ÙªlY–· lFkzxhJꘜ¼‹-RvÜšZÓƒq?™‹½#I8„'œžÊ¥Ä©U¶F1˜Â¯¸>ª‘r¨Òq«+ž©þI3¤Yn¾×^¸'ë 2Š==1î'>—øFë’ÆI¦EŽˆM”ŠL…ô ›ðÕ!Aï½F:ŸÓ½~Õg>-çMOˆ[@n(¯’Z_Eù/Ïð$î‘9žøÖÛ   “€ßY”OV3L»‚šŸfèëWÓòˆê±6°Eø›\O®y9Yk'ÐfGÞÒñÂÔgS¼lzу÷V>\ÝñÈßäŸäcùçÍGÖÈñäN$ñ¼—¬Ø;¨üâB›NC_Sh”D ª•½”õ-+Cz9Æ.?$~×Ó[癯&ÄZ_ˆ¢ã& Ê|–&'¹’ü…Ðh7pzk¹U6åwúŒ˜˜/é/ÞP⟃ÛývP†È KzúÑLv'¢ßÿ”cå3=ÆrÓè3Úýrø+îy3£$÷˜­´êß„ Í^7ÎêøH÷Ïdªó”‘ÏLia¶ ?0ãVnßü°   P Èú{i•»2îJì.þÄkçÀŸbw>ÛøÈu<¬”ïçÙêŽg—5Ô5líÍð,^åt9®Ý=vß+2W%¿ä6äö°‡¾xl¦Q)^‰NJrªm{.¤D³„³eGßûîD×LBù…êÈçDåà<€€@Ã%¡ÜpÛW5J`dÜ„ó e§Þx‘Ô‘n-¿G„¹Þ<˜ïzþ82:÷¤/&»Ëù¿éî˜5©P”{Jóêȧ"e".€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€@& ý¡öJ)ùÝd×y¦0/BõVJDRÅ\BŠCBÈT!åïš3ð«+ÎÛéõ­Ž:ÜçžÐÃã1û$%Æ~\ùÕÇ<ÜôEÃÌ0×ì¤1c²êã5ÖÔ5½øvDD'ûº·Î¥{ª‡¢‰” {k”ò/MÈMTßùÑÑ)žšªòºNÀy2/ yÆ×ž¬#æMÖJtô­ =Øéùný¹P(uQ”oΛ$¿Ò„ó™+Ÿð,÷[™m·ûí  ÏŽG”Tƒ¥’](tZ~‹tµ}ÁíVP™<+’Æ0Ô9Tö”BùàrB“ƒ"I˜ ) „ò18ùž<3¨³Ç,ŠË/ʺ“nŸ@ßs$’­@âùC¨'2ÅòÌ IÚkGÓ)OÞ»/Û7.¶A@@„ÐN„ï'¹úî>¸|=´_>R$£N=è‡J_öõsÚÔy/w+%Ž‘¦ÌãÝϵۡo_NºaY×~Tù˜”ê;®w’x^6"!¡m™ àÁ¨Øx÷ð˜„sà¥×©Kæ¯2$z+R…k•PÃŽÉe] ÅiFéžÒõÌõßp^YV†Là¤åyÏ9nЕ¾„ÀŸV øTgõ€ÊOýiáë›T"½(Ô=ãÉŠl8Ãg&ÅǾ4s|Ì73â^iÒ(`0‰åB3_Œ¯L¾õ4ÍM©‚êéµÕ‹Ëš=ûŒ€‰IŽY$z'ÓKgeÚª2y$´©@p   PMjÝGyÞÇUÂ0ç’5«:Ü>–¶ŠhsÑÀ茼òòážÜÒðf8â–éãc??2]tì„«MeÎu¹Úzá-¦’ÍLŒ½É7^T\ÂDiŠð‰±÷ññ踄Ê£…Th÷O)ΈÇ/‚ò»ÉjY®?ÊœBÖ¾.Ðß”úY†2Ÿ˜™w:Çs»•–a$& S ¥]¶h'“•{ ø7ï‹IèX$Äü@©.5!.ãsˆŠ›p–RÆÛAÎÆg¿â~€ü¹‡á± oÐ÷‚o¤©ÎUBrýM)ÔÍé|yº{ìf;æ(wb_]7_¢ý~d”dÿÕשî/=ðÌ+ ô¬y$¼Î•Bî öÊ×4ñ¡©¸n2ufBÌDÎcöìÙŽù«6,¥kÿ%)!Î+´¢b~’R{}F¸OF»_/ðzŽ|ί’JD()VhNñø wl2çñð /çdæ­”œgªý5zº†^d&‡GNÍ:X#]ŽÓ’ÜãÖ±Õ4úÿÛ;øªŠ{Ïœs/„EPQPl• VëÚºT°¶<—tQ‹¢Iˆ *äF.$7¬Ê"B¤¶¾çREí£‚  ÚZÔ§q—Z«l¢ì!ËY¦¿¹É¹ž„Üä†,ùÍ瓜sfùÏÌ÷nÿó?ÿùO(Rˆ&AgôJOMMutûÖœ4“) Í'pLmRŽÊIwhYB$@$@‡8µ(/›ÑöD(É7‘’¬Ñ÷ÿzç–ù y Ç‚[™S‚+jk—Ü¥í+ºÜµËO5¿¢ÎÐŒÐô“½º#çÎM‚2›!¤±RçE•d%²¡NlhÓŠæK®ë¾¤Ý;t9”ÝŽPP‡@¡}eÿ ™?îèÿ©.ó§pª·¡pÞm¶7OÄy8Ž,(8úÁHè ,ÄÚR!å­þ6ðݾY(ùIM%¹²Žê%ù@¹0†È€¼£éçXN±'ã¶œüP’_‡RºÂh+O¦…+kĽis'ŽÜ“hÿ ô»OIã¶”`J¿äÎÉS¡4ûOÆËë>¾çÇ`¬Ãôâ;¯åâ†`@@¹oêër{÷ ˜WiÈÛEÐ8Êò:×Vkô‚F]n:Ž~JÐKíµPOl ˆ6çÁ]楘’ 9Gt ÞE%¹’ ,ÉYM¦$C$Þ«³ ôkÊD$@$@­ž@SXu†è¸å  PuJ¸A¡ ûëÌÀãƒÇØË¨]Pè ¹kzVV­‹—f] kè·{âüð„—á£ûª++~Ù¹Z~ùÖÝ¿„Òg©G¾ ¯aIA¥-§Ñk¥Âé¡HF™m߈ë¨ÕŠLOi¿(šœó¿ºŽN#r#•'¾ÿp™ç»| }ç–—ª_!ï!ô±@)w”щáð[+ìe_ïúµaJ=¶8I¾™¼Â´ÜȃR¸EÞµ-Äh$×#òF~UÞbŒ}ú¥^+Ô;០ngïìðÝ;uáÈ2×ÓïßßeVxôvW× éþ*Ö ›Ý¿ýUVZ†¼jÿGZÁÏÌ-¸Ðv‹AãŒáœuZÒi¡¼ó+,WíæhNå¿Ïæ…îñ®Ç̘‘¢ÏMÛT°žÏÁã³Dûà x¯×®µ§?œt²eUL‚Õ¿ÉÖà#â,‚;Çé©©ïãAÆ¡ôÓGˆ #¡Gí™4ÏèõÍj…ëöY89´¸yz T 8´ ´˜¢¼tzàç°ª^Þ¸”ãh…4!EÙQr»”nŠv¨Í*Ye¹lõl‡+ÉE°²å#ÿ^(Z%†ã#E–V÷Z%Ç¡ü*(µéú°0C§…*-DO}MRlïnü¬ÞñeæN9‹…¶?€•·3 îhõÃînžüôFë“Ùí¿AÞóZaG?eÝŒóµÕ»Ö„òõþ ës(B}y}0¥Îûs^æyÚä]×<†sÖãFbó^w߸‹<»ÉŠ\mæ¯w¬ ‚i•€6¢ãr”s:ä}=bö{P”câ`B^ŽyV{?˜†ñX¬‚ïÄQÖ¸Ì4‚æ &.Fƶ,­$·‰e4ÝÉÉv~8â´›K ï…aðëº}à=[ŠÊ_R½P˜Ÿûr › 0=”ÿÞÓïåçL‹'Î’Æ™B83QNE¹H:úŽTJ»LQQ®…³H€HúJ %åhe§Y®3—ÝøY"Âáû”ÅÀê>ûN‘õ5™›ÿCX““ànuhdòÓpoè”™›?àÎHä(€—+ÓX¤›” ;ª¤(a<+ cžïï†620Ë'v«¶û®÷;…ïm8jwÍè¯(4Æ ¯zÃáÔ \ÿA*÷Öª¼1ï?Õ#·nßm öžoÜó`¡‡7ÅyýÖz”b¹tåÀ-ΔŸbLòsÂëÒϺR Õ¾Ö¸qˆv/ê¶ecœð‰ÞdÄÄY9ò«Ý¨™Êü*VÁ"Å)ð X·‘ûýÙ­ù|Æ#í»â¦æºæbÙQÿûzåâXú{Òø3ê¾…×étøõ/Ń©õ¶md¼£žÃûêµFŠ9¨Í3Âùçd„ò ê Ø9 @\Õ•¸µY C¹©² W6å#âšCrí¨ßìßjæ×¼î&&|´Iæ¿cÙQ+ʈšåÜðcÿvÑä¬õÅyãEÔ#ыخßW*ÖÁ‚µV/,Óíæ‡ÇmB°:`עɡ95e5ä›­Œ‚Î9nQßÜqÓ¦uعÇ9 2žòä$Áo¹\ª2CÓzÃÊz|yÇzer„¢ú!”œ‹áòR¼ö˜›ãÖPh )_t]5 cÞ UøYí_ —W69¯·ÛbE~ 7ŒˆGýª–©dp­p­ãÓçxÜ¢ù½œ£ãõË#“ƒjGé>õ\AÆ`q¥¶¶êd••j—œfûüâ½qzAqR¯ìôòOê-7åå§._z¸‹ÏÐ)¸±¾E_å¥Á­L?µë‰y® ±þþ±…Îñ^ÝÖŸt y·LáµÂº‹HH?%`" VMOÆ["á²ùSÂ}àÇå53`ôÃö™®å|€Î aÙáGüÜ  öƒÅ²šezAÞ„·ñãó%ü•OíÔ±Mµ„)›«ê<”Ͳìíß*«t”¹BÇ0»4dÊø1/À£ä36Ù¯}[aW|ŠEwÛ1¾¨à—#ME}ê"¼pQ÷YCÏõ¿4å`Ìmh¹’à÷¼G¹îJø÷÷d¡\[Êm²ó·BI/ÒùzocÎG\Ò·÷jW™ŒgàÖ‚È•þÉ:O+ IÁŽC1K¹öëåÖî-J¸£%ä7…áÐÚª† æe¯VRN‚¢ôç¨LB­ÏJpJøý~ ”{À}\¦ûtEÛ*×!Õ þëa(“ƒá¿| "¬\¥Ë늆‚…´Ÿá}´§jQ¬®M©x ÞKë‹Ãp+Bdlšâ•­X÷içÔa¦Ixcü Ÿ«j. M¥&©ÅvžÁ8–ˆdy܉fà³9iÅ{ŸÞì Jò’è8ÆUÁ ¼ Ÿ™c¬}îº<`vž‚¹À7X.ÖQe’I#ð&d\žr’®£]™ $×ó46~s‰Î«Lî5˜[Ty‡Â}”ä<,Žøn,N¿S”蜨2íU«Ú^ƒX1Nôg JòJŒq)•d?ž“ ´f-cQXèÕÌI/RSO^gÊÔ§œDºÒ‹ÒPïbýCô˜~ìQbÜÏbZ[{XÀ~R[~•åH‡°ˆÜžÖ]µqÊý› órþˆ2ýW-Õ̯RÎû¥‡g5èÔvx©jpo%°¢ííЩí“5Ëü׈Ï|¥ÿZŸçeÿüæ~—àv±W§¤OÚ©M™L9 ÷ü~ÏUn?ÖsÓe^Kð8OŸ/ô2p,ÎφRP]¾.® _wMzQQPlÞÓiaxÌ7:ßK3ÇŽ-Áyµqé²Úò«â7OñÚ¶Ö#`u©i)mjxŸ%ô¹…ÒÞ!3é+ÙÉnhÈ!Œov±ÿu–ªWR×NÇ>0jT¹g‚ÑP°Ö‰êÑ×[GAÙµ³QVÄÐZçªܦŒ9 ó&¼YU>бöå¾Á« Qj¼ö5¸aXçWå?…±Ëð]¤?é-ñêbÄ.ïéÅ.ÏÈÉÏw•Zz=ç‡o߀:¨[îE•ÁùnÜ~¤l{ Îÿ¹Ñ™¢-Ë» P?ç:âjœ/»+<눫ä3X.2:'C,\˜—ã¹ÍÚ‡܆`e¿?ö]Rã5€¬X‚›Ì‘¥%î °}ßE“cqøÁ—鹑üøÎ©R"}¥;-®Œ&±+ž”&™[åcãjJr¼þ˜_/¨L½µQ _BŸ[<¦?˱Õ3°bî‚ø_Xzû‚I9KðäÆ×»\ì)É:3‘h(Éö–Ø{gf†óÏ[½±J2ðmEy¡•¸ôÉBÇ/+q:CQ^U­@È÷`Ñf5u”-ñÉánQ-}Ž'C'è°èƒl©kÛs G+Á5) ‡cƱ!šYãÚ-sðÙ ñtêZs“=åb(ΛñDf½¾±ÆS¬c¥g…_ žØ,G<÷©¯|°áȯ²ìW |õ]X¹—@f n€ý/š¯ OI€H uhEYÊmømV°~!ì[¸Ù•‡fDáÚškYÖo`%ºóüA÷ÀñûY‹ã4eöáK`¦Ö«Y§'…î£þ$ve öWÄ®—þKP(ý°ÄÚPüâDC2‰¤­¬ð­]l[b.߀ÿﱘöþõžLUa¡~ ÃtõÓ‰X‚b]æ}óø£Ô`—Éb•„˜T¦ÿ&·Þ(5^[Ó TëJ-ôܪR)“pS[ª£Êxõ1f»FÒ;•§µþþÌè§]íü«¡üÿî’>½_ƒ¯ iF¨A¸9yQ·LF”ñh¸à©-Ó±¤LUË>¼0*Yꂚ¯WÊ|_ð¸_Ñé¸ÿ]q^Îc^$@$ÐÚ ´ˆ¢¬”¥fÖaUµ¾Ãêuµ–T¶:?˜KRÌvÏx å«Ir2 "åþ©êü5jhe)ýÊdC[×Y?Ñh(ØÍqü柾#”7½B¨‹JÜT›à&ŽÛŒÉReËŸ£ü-¯Ž£¢‹Û¢—M¥Æ“_×O€>€õø¨€!¶Íç¼[k]¬6D>”üïRr—”Õ{·—tØlçƒÂl˜°&5‹CùÏ;½Šø@eb£#$}3kõg°_ŠË¿{R°ó奸©(·ºù¹—ïÍ~}Q^îØ´{ó߀©ÿOá‚w‹ÂÙÍöÚÇóI€HàûH Eåäþç¿[ºvõ.Z:5XrVyX›«ƒ%·0Òָѫöûý#€}«àëzksެ­Ùfµ¥ÍÒE¢ÑPŠ&e¯‚¼açAñ[1?ú²¶i;.|{Á/÷–ô{#ÿ'Ž;ê5¹qÛ„1Ìðׇ%v¾'¥Æ/3ÞùqÐËØç-„¢¼Q)òŽ „^ÝlÏè)¥u&Ö(D×@é]¿à\½F Êý)’¾Ëk`ZÙÑð‹ºCÏ`!ë}0 Ÿ$“ƒß¹ZH9rîɬîºñ›Ë„ëfBî\/JF¼1úóõî|ˆ°q¡²ÜÅ·…ìj„d" ÖMOìš? °Ê†ÇV‡7c ê\ÜÖŒ=S4 ´8”äN/à1~óh±˜ dÿýžá¥µ*¥M1ÙD£¡DÍ–Ù!RTnô¯ÿ#:³1îÿ‡’¸\|ùõNåˆ|Ówûë7U”¿ÌxçÑõÉò—ûD®X7ŠRWU|Œ¨2™^³½ù<\þ)öZÿF˜Æo|1õBõ‚¿÷Ó^ÝcÝVà~ ;^7<¥Z—!<Þ< Š•pf‰oÛ…ùëïÂ¥X<™ëµMô¨zt E|—mïx(Ñ6¬G$@‡3ü®´LZ>=øKÙuùåø@¤xåÊqê’À–$pè((2æc½]LéjÊ`±Ùo&¤9nJ™ñdyÑPªEɈW9üpøá¶»ÄŽäª0†q[Ô¥&nåFTÎqG·Ž)æ·µ-ÄÍœ2¥sð§geíiLW:Ä[g+{[]|#ŸmI€H µh1EYƒ]:Ýøøí]ß”±¸ {€Î<ÆòBB5¥xÊ"ï-é§k[û>E²CSŸ©7'¤¹çú–¥5¥xÊ"  C†@‹¸^x4Ú)wá™®ö·m²¤„œÖÔJr8üd›ôpQr}ƒL´^}r¼ò†ÈÓ+âëãȹs“ônež|/ãn.Ù"”¡ã 7YÂs©o¢’ÜdH)ˆH€Hà&ТeÍiÙÌàÙŽc¯Â¾öåË×óƒxíÕ‰n2âõ§Ȳ­»gãz‚ëŸìåë£Þ·_âoþÞ—=ºÞZÛ‚˜DëÕÕäÇR}ò2s§œØ³sEûÀµ×¾~ýÐxì Ì‘z'³˜0œè­h2µÞPò6ì²õ¸¿Œç‡‚b¡½T£{â½äÀøº éγ‡!ΆH€H€Œ@‹Z”õ/c½m¡¡'£5î¼ä“¢mÏÔ+Éá¹˿޵ýÇÎx]`׬‹°²þÂãôņç`ÑŒ%¿úæf¯Ü;&Zod}y²ô1y޲¯GÙGEçÎûLa,‚þ~Š?±mw?%É)bŽ«üýðüð$îÞƒ÷mµ­š8À++³+Š„ Œ^Ësz(âeóØJŒºaûnL5_©pÁô‡"ýá×~®±ÕµêŒÏ“ƒ'&_ã–ô}3\•uKùÇzÛçìŒp+¡Ãi’ @ÃtEÙîQ¥_áüïºòè™õö×ïW/j†+øo®Œ…Ú2…ÕûËî¨ÙU¢õj¶‹w]—<v£ýÕå:&U‹np[xú±–]þ„0R Çߥe§çFnE\ÝŠ' rD("¶pƒûŒŒØ»‹©Õ2¬?4oTýUÍÛÿh°×R«alj’ €Gà{£({:˜G¸|!”êéÁ¶í“àf±ß¢¿Dëyrê;Ö%o³½ùÃ+þM î Ï:bŸUò‚)Íq…áìõ1ù®ºúð…¹ùé•y2I)w8,ËâºY6¦ˆõÍ   8̇Ù|5C˜OCQþŽg\)H‡ëÇ}žÎ?/34­·>O´ž®/,(8:íÞÈ`]^— 5 2 1 °ã2Ý@IDATxì]`ÅúŸÙ½K£#E@–‡X°cÅ^ŸíÙ; ÅŠ Rô$@ñ)J‚íYÞ_Ñ÷ž½bG± Š"R¤¤HO½ÝÿïÛd/›Ë]r—Ü…”oಳS¾™ùMûæ›of…`Ã0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À´d (¡ 0Âç?Æ OA^öìPœ¸ñ©‚4žXÜì{°gy °·ðzVøÆ¬ªJ¥þ¶DЬnš^LƧiÕ‰gWdÇçûÔ³NÌé§Öß„.‹=¦Z85/{õ®ÈKsI3Ý—Ÿ¦™›÷7¥¹·fiJH±NêÞeÓ}woHd2rò®RÊjWàÏ™–Èt C½ú•Rš”R%2­ºhß2~|W£\¶kÓÆ³þá»ï.ª-¼VŠö¦úFítÂÞâ{p2£|¸ÉÿÊ÷ßó»ã^ßgS§¾eˆ5^¤¶O,Ê ã!ÔdÍ4Æ!÷Ç’ÇHù‹D3Þm"–¼&"l¤òוV$|êŠW›?Í)ëÅ7½Cäˆä­úFouw¿ßî{´c™(íìIR;žÈÌÜèö g¿çÁÛí(±ºš^«¤ +k…qÆwx¢÷øØ±›võxæÎS8{£2#²Üßåù…Æìc…Iv†L%° ËÎÝ*•|±Íni÷<:ztI¸Ì¶F·[<¹|ýö»U`ãhS¨N„%LŒ[d±Tzvîwš¦Ý?}\ÖñƇÒ.[¿ý$åMÏË{ÝiðñN§©Ñ+-¶J]¹m»u9òörmù++¶&q¹VÓ¶^p/:aËÍÀ0¥D–.pî¸ó3:šzÛ«OþZR›¨Où£«ùú…Ú ¾ékŒÌöNaˆaYþBªÅ˜_h©bâôììBw*%Vñ‘–i}hä/pÿ›Û/œ}ÛöÀDK¨‘ÒÂ4…qÆ wx[Fv^Ò_ FàÅ6žÔ'êbHÜñË®5VBÙ¹·˜*ðÆ“æ2€ò/)µ»ð»«Ú×PA%J¨›wn.š7Ò—whcå«©§Sº~ût4¸\LÄíÙ×RŠ„Ô25!§bÁü3ð<Ì@Y"ÊñÄ­·–¡ó¼‡úùªg s½; â|ѸŒÊò÷q»³½)¿R®3ñ1c;µµ½Ø©Å?F½òWK›HÏÎËÁBhbüsšе•—Ž RlÅœ2­ò—±ëmŒ“›1¿Có‹Y"~¥¹ÈçSÁ¹ï”þû}‚qõOøT×Ü“žŸïUR]j£êÑ‚Le)?C¿ºâ'^Cúó°çÞÒTßÎ@ñb’Ã6K£H†e?ÏRæã(³)…ÌêéÉšèóIËÁ-¾ÇÛ—¶?…UÕ%†a}‘˾Ý{ïw˜ÖfÏȬe™×¡nÐ<úÑÓ}cÿÅ`¤o¾ÝĘåBä„zÅåÛçWªN¿¬È: ∫'Ç%¡F$\Mþ}[X±µ8‘Ú^£f¢–ÄbÍ_mmÐõBÈojI®ÉyE*ÿ®0á®/ðg ë®I“ÚlÛVšƒÆÑ–¯ äõC˜[(Ü¥—^jB¢ú°ÞašêJ<$÷°¦pÓiXxuÁ<öK/k~0RæÏÈͪ&=¬Ôxu<¤4xqN«o:¹¡Dåöj¤0"úš&n†â×øP€üžðݺ~—bÂû/@î¶ug` ¹×efΜ©Su… õ÷ùžM u‹æLŠ$QX4aF ëh›†’3Ã1ä7Õ7vi8<švñ¥:¯Î0!·XëŠê7„L‹z¥vØ26$n$ iµÉ/^5ê¯ÑäÙ²h­Ê$×*êÕmé9†Äh¯ê®Íø­c•6Ö±!„H·g†?gŒ®éÇb@Â9r¸ÏTFåª^Yâr·” è_iQ–Í$ñ~¨_¤÷'}÷®Å6ÄÕäÆ8Øç›Y±)B#»Ç[û¬•³ez¶¿i|ØËÓo¤Ïwi¹  e§‘“w)Ô®û1³þÖá!Vï´ñ¦=Ð÷Ôœü»ŸíÓÊDOÉ÷ç<áöK÷Mì- ÕÁ)…ÂkcX¶*ˆ…Oó´=f²ïŽ­NÜDá3Â7aoÓ0n@:g¡mì­Vmæ¹PGs=ÒsŸÓ^‡Ý—w¦´¬GQ—sfäe_ïä+ô‰² ,'*¡ß3ßù&ùG›†›VhÛsû…³Û¢ÚÂçIj;J( |µgFöWèü!…ölOÏÇ|¾ëKÃÅE=•Rÿ Ê'ÀNí®ʱ#É\Lc±ê]/Öü…¶ RöƆqÂ2n"ÚÀìlÐ\줣ir¬iŠ«Ñíæ7ó¯?÷“¤†a¼úÚP›}R¸ñtýˆs1¶ÇäfýÛŸìßÞD9û·g0ÞÙ‹6w˜`J=cFnæçv06ã5jüøÝŒ"kDìƒP_ý‘í°ÿ‚cÞŒqY¤,WCmcø£˜ìïÁi¤|?Œ U=Æè_1§¸Öœ>%w·é›ÒÖ06ŸqNIþ’Û¯.;é! þýÝ7šË÷CøEuÅi,ÿ„KPèã©0ྦddØ“Em…+Èó»/1@¦jeêwX4òýð;yXŽ?{,ÏbÚÑ–ö]^·‡PÁo†[edd¿ À÷èL$Þþ{FùX#üŽt.²æ˜Ü»Ó"»žTg afÂþ Âî†âm´WÑ t¼§[²9ÑH0}CÌ¡¬kCÓqÞ S]Ka/ê\0ÖOöƒòà{˜Ìj5S}÷ü‰NìæÇ‘fh`”ýr›>ö¾¶ï4Î õ7ŠD]ûcÁöB…±1 &®Ò`–>e_‹úÜb;Cš é‡3 ÁŒü†åä]†Ç yê(¸½‡pŸÙTüu36-!vbo4Ë´ÔËv§ðHÿ¿¨ˆ4`}k‘QôMcIa*óSïDŽK0¦ÕêÍÖ¡0?Áï*`ø:à]ó ÊØÖ¯d%´QWO™õPÙ„àcæ H; u“ Ü?@Ÿ¢I"y:ËPÆ×6ó ©uùùê‰IéªÛòòº‡‡uLœ×ÁoÏNítÔu…‰6 '<=CÛžÛ/œ]n,Lõü†â· mŽD¯‹WKY 54èG2ûFùw(ÛÀ`)Ò~Øvw ìs3îË«ÆÜ‘XóÚ&R½›ÚóÞHïçŠLÉmÔ‚?KmAÛæÔ'Ez¤ŒƒÀ „Qr^8ÀΫ’¿Q¡¬sCéP›D9Ï%äåºP’Š€AùÜ÷IÑÛþàø‡–?–±Á¦¾Q^d}4±Ç.ס¾žÃï';H˜Ö»Ø»¿ÞI+žOÞ)é€áV‡ãçÐÆmOìÎjßqwž¦¹õ`”FóS¬ÇI1&KüoG´Ìd#ÈØ;´wå3á’ŽV‚¨Wù[´EãúaO´,¹oÍ8j_iÉ[4)N™îÏùÌñåßV¥ûmõ†ëà>Ãñ£ T%Ïâ]óxÅñÓ|ÙÁ½70C_áUSÉ|0ýk0*JìÉh¬&ôsòý™ï:4é˜È–åß"½× GÂýÇ/ìÓ£ÿK–á/ƒØk´Ï7Äp‡£Ž6kÁ’Ëm.S©çÈ«ï2rü`\Ä‘2Púåðœñ7MÏÍüÊ/Ôì>„ÛpK†àÄœDmk_a?£Ã÷Ç HÌÐLw|4îSÆI4¬4ž„Ç“`Œ¾Çó)’nÍÏ«y®rUõ¨y4©nÊ÷g¥@$nÛ &u Mi·GK+uynÁ¸ìwÿ*¬Åe·€ûdǯ¹=Áÿ“Ê ŒïÃÞe®“07·•n؆•‚è˜ä‘ONñe-qüœg¢ðC|—ôX|_ö\'-bl § ¦¾RV&ܯ)ðecE8ù¸±¸Œ9ágy‰…ÉByÁà¼ì–4E›†C§>ÏédÎ~ßøk“ôv¯Óö¢Cƒ$\`pDY®Ájl\¸U=úÄMÿZ‡)×¹…"ü ÞS”© Їöí·Nõy>–•µñ.M¿/ïleZo·/ òr®tÓºÃ÷ÈÂFÑD`~1úÁ-nL«ÂÉ«ìVóÓýîØ=º°'+)Ä^Ž[mO€ý,ù›Â¬1€B^ᦤÆ¡C{I==ûŸÉÇM4y£lG—¼ÖX³šŽÑ ß KO+Íó ›‰ö6Y«0–Ð.$þOÐ?¶ø´Ò_¬Úx4 4Àü³PØ ËiŸZ~J,ñ¤GÖŠ5òÖ±¤),Àëˆôº×ö3•)~¨»]Ð'¡·‡§ëêšþb¹í¦d§P?ç½1ñé¥õü´2ÝNúûÒ[@bàÈì«M¨´76tŒ½ÚÉÍþ܉SÛ3\µ…¯·ŸT•e1ƒeqÓBŸø´ÀwWØ1 {ó/RX³rkÓ¯1ìXÜäÛé(ë†ÐôLƒ¤0R>g?kùÅl[©ÃÉN0ÚÖA;„6>;)Ub’§ J]àøÓrl›)µ4þq4À<Üå<6#¦¤Ý~™½â˜b))*ëZë\åH6e×µeŠje”þL‰ÞvëéT ¬,õ0t ~¡t™VAg­ G¼_F9¿¸ú`èš–¡B%ÌЊ«¨5H ·2u¾ÂhƒæeO°§Ô°W…ŸÖ8,€¿ qz*Më‹çêJ1{_xY¦2G"?#kÒ´ö!7 ûÖô")Å¢2Ô0–‘ô—eh7•ÕQÝ¡‡·×ÿ°/IûAç¥OœØÁá€íã+[Ë.º³}‡äתǢU™­<÷4&‘gFäø‡@Ò1‰^ˆqP¦9 Ç+OwV?DÛ⸑Œ'¦f!M>¸´â0›OÐïÛ.ÅÍ|æÍÕ¨³Dém{`† îè6æ ë ²FOOöŠXŽ/¦xeÉ‘òç`:ªód•7ýþ<›é¡¸Žñ`bZnæ—Î{]O%µCû„ÆÖϼkCÝýSû„$ç30`gšk ûjÒ¼_Dñ0ècÅÞÄ‚­ø,˪6QÕ¤í³)™™h»U†ÎvcÒ(Ñý®7òØJ‹)˜cž¨2xyá&àâ/b\i«À6´7m[”x.Üj'–4*IÆü ºyß„ãÑ×ûÛåPª'òFM @žÁ³o"ŽMV¥dÓcßðQëJ[€×H·áXb¼¦úïý•R´%y«7üýºÜ›Z±Ÿ]kNìm>uW¥”¶õDi©8tÑäÄ»4!c|œ çcˆ9 í ÒÁÁ6Õ `øWO}̸® K{úê8ÃCà¶0ã¾ Gƒ^ ÿ‰EÇþ¸löä< š7]4T[@èš½Z¨¢»í³Kz¢õãh¨,$F0 ƒöÛoÓc±DöÕW_Õ*ĪùšÂúC«96ò .E~ûUgƒ'‡·S’'é º»p)öp %~³»çØ÷#e+|p ÚåH+;”V È:n³ÉݾŸÃØ6­´Ø¤ö˜„:Ã@e­A‹Ø„NQfcx¡°ŽIòzŸ/ ètÕh?YU¾½*UIBüË KÏú¤áŽ­}Ø}þ‹ C3yÞå  BU¬E9vWŒuÈ±Âæ]ƒ¾_)rIÚ¦rÈ獵۽1íÀ¸X?fÊÀ H÷nJ[[ý×é&YDZêP¦.\Þzh=¿,´Ö”‡“HJK}41öàJPKü¯"Žö0:²¤Tœ‹÷§Ê6í8°¥Ã7bíËôjù«9«ñZÂ$ÊKÉ~Ôtou]5Ú~Á …dl‰™Ä”?ƒ~ºÚÔÑÀ`sOϾØ:ŽlÀdÞ2Ã_uO˜þwñ™f‘u+b=9æ®óI<@ZºB As;ÏÿDYÔ¿Q8숇eÖz¿î ïuaiIÕu‹úµ6¿§½÷/ñª¢˜ÎaãÔáxÿýBù|uŠÒ[÷Šg1yG†®F”§)ŽÃ]UýÙÊgé¾ìï ={4ŽIãž:r @££Õ½'îhuÿVývGÇvˆ?‰û±²x8]<ò¾‰‡ßñÈ#¿à¦Fã®,ðe.®3ñ:Þ®‰à—××¼š÷%—\ÓP-rå ÑøhAž=Pºý!Ö]å~ßv’,`œ‘zøO- ”OµóQý+"UÞá˪®4êÎg,ø ‘@u›;>Ùqfy=i"Xkä½…¤ODeýOjžûétù‘©P•î¤Ù¯Â¥â/{Æà† âLH¦N‚ëg´7 å»}0ù~<%/{¥¾¾i8ñ£}¢/œ­î—!(Á,ðv}uø`p½³ J«•ÅñŸÓɱ‡>-aÙãèÚcJ¨c¼ëiú æNs"SWCÒ6†˜yœ°³W§Ðsx6š–h[HB@ÝeIh yÊâçcκ~Ï MaüD³Á}-aN4…F¯ö®Kï¸)6k ŽÉ¾0Í7vyµMà%ñL€ÔÞʼ‰¤tT&Õ¶Ò%<†åŒ?IXæ@TÐÆ$OÛÙa1 Ø[ á™!ö²ãx<Г¾CNn5€Þteã¾{רþ»èMÞ@¡—n’Ò½†Y\ …WÄÙþðå×$O‡¯Ktª¨ú åÔCú}‹“ÛÀÍNL[ç¢ ïðvkKÌmt%_ÇÚæb(ž  Úxzzƒ¥Dœ´ÈQÆuH»)ÔDsBE®âó·reúp|¨ÅŸ .,¥ctdc 1\ AÌF‘" ½Ó¼¡)ƒé¥ö±M­ãû£ŽN÷±¢—gÐ¥¡ZØ%E^vB$•ù¢‰çLèš\†çg`l+'¤ ½—Ê0¢i8dê|b‹,£´‚íÛqþ_¡À,Ûe‰$ @x[rÞ¡ƒÓ»Â]þQñlü¿$Jãþ*úé5¸ÃàŒ¥_mßVzÆ?k“…É)õñSMe(w¨}1FxÁgSrÇ,ÂJx)&ýÁ¶²ðê Cˆ†L‰|b(LMÚ §ÈlÆýîÿh¬ ÍlO·× ‰y>™°a? ã'„¤ul„Âû4ÿ½¿aþ¡í…;Í€}kî9a‚íR'0c‰5þÌ×1(c R]Ñhk¬ÎÜ©S£#1©í¦ Ÿ³ÏícÛ ë¼np ³þ4Ù£clØ]³¦*ŒœCv|Õ Å®7hP@•¤¯i•”XÃ*¯ÏÅš³r³ØfxdÈÅ´J@Ÿ áuN¿üèè¡Á¿ãVøKñ¶ÁåEts–8ñÛik1hÿB{—âhÒØÝŽ[ó½õcå×Öôj½.Ã}úbÅú˜‚#‚æçe¿‚“.ŸÄ›ˆa+ l‰ÚÉ’P€â†IÛaMr÷ö¸\FlÆbî"º íí´§m©»¥ýס!i¸éDa·ËâÑqÑLˆ±÷αÏâ\ýUª“è(juÇŠ7«ò®xLŒóÂù7Ô ýÏîKÚDêKIàs…E^¶ckÙ9ëpf]¾®î"åÉã­˜ÌÁ|RÝžAá<ÂS­Î Áö:h·%ÅaðG¡^Ž©}Ö=6DÊ^BÝI*…ѽ1æÉ-š'åŽp V^(õ„KÅño:)q<Â/Ÿ6nì×áÂ×å–ìé0Žæ$Ð;;ã>?ÑkR&áL•Vóˆ;Iô‚†u?Î?ñs j{p\µq6­Lø/½ôA‘"©B¨f2]R‚ÕÀ?+âh8_Z%RÕ=z&¬RL´wÑw ÂÑ¥ 3èF¨p~ñvKK“/ Q@„nŸºv "Ú…¦ƒ JNu_fáö·ï>Piä†ýäwÜ~dw4±7n7t)ô ·Oþ¶†«Ÿ£aœÄÁf­ÍÇä¥YAá €un¸ðš¦ßOuŽIâžj×sV¦}âpñZº›4Õže”‡Ñu±Ôæèæ8RÀjì²k^a×!1ÔÜégd?´úÓ·›ÛniTòßèÓ]Ö–܃VH'y^ ýhCÒp§…½¢=Št‚†~µfãD(ìt cA9ÚoÝðSx··}†ÇiíAÜÓíi·_¼ìø hE=àˆ.}Ö6]:™A2°>Œ ‰ªiyúl¤ðáÜé¸$Êò'´<WÔï¥XD«UwXœ–zÞÁ¬Þ…ñÁ Db•Úå‰46¸Ój ;õ/ºéß øž.¢ñú¡·×*¡Ô¡» ’ „†²—*%Œ1g™³ÐkÓ³ÄcöM‘1SI\OâHWQ&8*a¨I CJÝXôWñÅm-Dc[ˆ¦†‰W À­}ᙌ zSxRntOâU”`ÃêSËBC”Ï…fö+輸bRô€²Ó…¨¬¾ˆÿƒðt±'G'íÃ@$3 ±ŒùìŸcæü{'öM{"Ü¡åBî5v"8CŸ`CZ·ÈûHæLš&!Žý0§N˜0ËO€hýˆü—ct²5˜¥”žŽg*…O{êƒÍ2˜©JÑ¿:aJÚuð¾x¿ŽUàP¸ã~5§¶£/5âjØ¿6q¬QÉÛQ—}0‘|Ö£›sÎôܱ?Àý!ÔϽV@|òRú‹ÀôôÅŠê¨RcéutªA·…;t׳V}õ>êç ¡ Öf»Ä¥ëí«[qCœZˆ“-&âÊÔPh»‹Asq3¾ì© [ßbßühS…Ø©=X©ò+1èá² q&ÚIZh\z£÷Œi7£mŽE8rx&4\CÓ¥ñ]Çå`¦ >q ÚÝþÈÌ,`‰sæþ³§;ƺ¤ ‹‘ðe}eVÈ;»@Š&7a<ˆK‚ð=,;¤vg~Ñqxj±¹N½Ì ÷ýéUlÍE é¸1”¤DnjÈK¶6Ç qìßçû2kH>ÜáÃÛåG‡¯&?ôÛÜÐ0»ëcçZyëÑ.N³Ã¨õêBÓ‹óûž˜> šÀ“›ûà3ì=0÷ ¨}pç ®nå\…)ížZæç…–ÃPPÅñ1õ(òq6&¡{ÁÐÝŒ~õM¤4‰ÑC9 Ïi(﹙߆†mh¡ô"½Ï—ýìM‚?Å9XÆNF[ƒ„BîHñ$[«ó#ÅE•œ¦Ý)¼b_áÁº»16= :—áý7Ü>—5hˆœ6RG'÷Hï5°}Gõ€þžGù‡¾Å!¡ñR=m_ ¦mH‡ß³¡þѼ£¾*”¶·š„âÓ)”û-²SZS¿ {´&š±!ZZ±†£¶ qý9%C Ê@ãC”ãI7¼·°[@iÚHùo²£ï¾A‘Üb5TÇHÿVPCÅݤœ+D…G]ïCûtÞ5[út±#Ú{˜ÁáoAÅvÀà„³—öZØ÷^{uï”ûÇ,sÜê*cÿü—¥ÔÙÒ4áÙpâA{­§}ôºâí*Ú«,ÞQÞÅ”žÎ81Qlx:B‹¿¦Bˮȉ±­ÅûZº©§´Oý=Tì䉶€J·—ì—"R×4—9y×Ò ŒÓ1 àcHcÃá€áö$å'¡cŠW>ÂÑ!1t©Yº—Ð=…µŠIÃEŽÒ­1Ò ¾ýé/Ëúh¦L ìÑiIkÀ£È+µÕâ-åý:´•ËÃ_Ó‘z¡í?Ü¿°î¥Ùn\$E5Ü5²äSt8#°+€•ž<<Åëíé” M”E[°úÀi‘œ&³ZØxqš5žå¿¢·Ñ>^Fû¸¼fvabC Ñ¶bˇfZOÒÎ2ÒØ‰ —;­(¹œß²àÒ4R"5eÅ>¾eûHCéq|F€`&€Û#ÐØ{‚B|í€.Œ =õAÇìè‹–øšåLl ½<£²ÅI4Ðfö ›Ÿ¶Õ¤x{FîØï›IÖ9›MOÏgh9¤Ée‰úŒ@>뚃}…þ€nÊn…%½¡ìÕbÞ?qœóŠüq™·œ‚sIê‹ÚÈ×Ѓê IR¯ rIRšv]}éq-ůø}ŽCÛy*KýèØ›å3Aí<, ÿÊ[|ºiÈkÀ¸¯˜:nìl0×éŽBÝ<†Éñe\³ü*žë-¥ŽÂ:Ê’²(šü5$L"ú9}2|ËŽò//˜¥{uøR™²·…2AâÕ³¹3„·Ï7Ä–ã­ ïW Á?4®Í*s ëCýø½vZ/ ÅL\Û:ad–ÿÍ©yÙ«k‡)z_Lbç!ôÆèc4Ý•ƒ÷'‡÷åmW¦%<õé4_ö7Ž{¼žJh½%–=õ{ 1¦Š¬”än%um[Ù˜áÏ|³*Pƒm›fŒËþOƒ©45 jçusx΄ÃLËØ£®pÑøÓÇ• ßþIIdÐÅls-ÊK‡èwÕ¥—^ê0‚qXjË["úù¶æ ô MÓ®ÍÏÍz¾2}êg —jÔVÖxûÍÈÍ&)@Ü ˜ŠB"”à* fñ¸o„Z-àõx²sdÉÓPÏçDSרSŠ' ÃÐa zToaÏ.“öìÒóòzˆb5+å³0XmÅJ«%snÂ÷ˆêïît°Â…›Œ¢kpHç|àqý Ö­>Ý—Ÿ¦Ì¹Ø`8 ¢ò¾ ¹ágöðdNro;€Þµð?­§'ûêB3ïäßW]“»uèöÄ­·–¹Óvìè@¹ÂCu¯~ùtߨ?÷XŸ(ï7È×]šW”[qÊ;¦˜˜Ÿ—í¯qnÁ·t‡ ½n³ò½6vÚØ±[hO·¤Xebw)áQÈû"Pä¨ÜÁ!”®ßþ¹ëB<4=/û¥aÙþ§4%ÊÆ('¿A¬”:éì‹øþ%…öV?‹¤>q7ÃrÆŸ$-+倲P÷s="iÌTÿ½¿RbX1NnHÝ74Ãõjçu”ÉÉS¤:G{ǪÜGáÊ.€ºØ„íµ“É «vkäĉÑ^f'Á©Vº?{¤vŸ#ê§pŽYg,=õßmãÇ ôvëM. èj©«Ž(<}¦7ØòÚìx´'ô¿*3<'ï:ÓRwÌÈË>¤¶~ŽòA{ù‚))k§_W‹ƒnµX†gç¶0¡@°ü~“R¿-¿RZF}A6>ˆöw°êüèRËvcLcúÈY²­7]ì0Á¨“.(ÅÏšP9Óý9Ÿ¸³PW_vÂFj4Ÿ’òy73ì·uŒq}÷Ó£ËaPø×4_ÖBŒç ¬lb@m¾uMhŘdnÇ$svzNÞu¡@ûãxÞG‡Ó±/};€ûq†…ïP\ÈïÊáþ<àŸxý{Ú~ú¡CÍÇüµnçÝá{¤3…%CŠnð¦aPÆ}*¸Óþ7Ò¹HSê G« Ø8õÕ=ŸC×HM¼ýéû1Y¾Fa‚FÊ.ˆ{\¡1~2†3Ð韄ß=‘€Œì¼{±ò¸G×¥¯! @eúCyÎð:&¥Èçhˆïß³ýTùá• ÀJ“7Àím¤{Qd>AþßZO«8Zõ”êØ÷§:ö|]Èo)…å¸+™ôÅ“³7¸½ÈNæ–ÇOÆ ÷-Òº ¯¯£~ÓÁLG˜¸Izì„*ÿ Ïò_)•ù Ñ-h·á—\u4DùÜŒœñ´%D¼K½ëÞV}í±¶óhÊäÊKØ:7¥¾í°bõ*ÕãÔ”Ô&;ñP†±Óü íÙ„_6Âþ@õ4-ëƒQYþ>N8ç©„ æ:<þœeA7M¼܇`"<Ùq ÷Œ¶<º0Q}j€¥‰ÝBéXJt­œhkíçN¼XËçÄëáÍšö³}(ÛnËŽG˜gFNÞUP:ú^è>ò~pÌw"îÏR—ë(xz~¾ãõ©+ñÁø·Ñ'ïÆi†e}š~_Þ cŒè_çˆÏQôñ¨ —º@¼òÎÍÙ¹Áþe‡¯£/WR¥GضQéÿ7€ =Ž ÓçDr=iò§9¡, Í&Z­$ Ü(OÅ òopŽWboq2”Ò>Œ¤­J²lÃöIèDoÏðç\P ðsXåÍÅdü ¾¾ìõ.Üg‚ˇUÍr·bRzÎÄYʲ´"£ôD„yâCÉ tä\tÎdôãóàô-¹—Y;N FCiÞè}­ñû(„éÕegu çÿ Ïÿ½©Ì·(m·R˜š¾èéÝÏwi9ÑgÀøÜdY “­vÅôqY´Ù`ƒ# ÅÓÇeW£W¹²r¯®þÜ»I%m Ì™™´}2ûzÐ9PƒÝØÁ}þ°ì¼}À\â^#¿e·Àü7]zNÏK_èK˜¹kÒ¤6Û·–MB]¿2ßd"¡øôÂZcöl˲&£,ÇdÜ÷`½ë>™¥G[&·Ô*RËdÌR·&{’Þª¡ DÙwqJe¬SÆ Ÿ–PßB^7Z‘Ú†”EE ôL]ª ´‡<’RµWʋ͑˜IÒð:¶öþ‰•ú×±*þÖ§<îøáì•_ð Ûσác(_0,$Õƒdnä'0Þü©×ã)ÞöOønÝîg¯šBÛ{®‹\õñ”N­Ù8 ãÈ¡¯vØT_Ö•îOCzö‡°¬ñ\x Ó ûn¾?+Ó‰_Qâû€$½ ÖG]}Ù‰OÏHmÆ챎q¡ñù½a´Z® Rg»ìÒãI6U%F‚²|ÓŽã1 ï©éâYw•ªA2€Æ.­An÷P;qÃJ߉7;~Hó,e?EäO±Ê'& ÂXÖ Kq*ÔÅà>v1¶ót&¸{¹L ë²Ê˜U%³#1éŒûüçƒñ™®d]kѺmØ“û8Z†ÜŸ‚aé@uÝ”£ :º+ÏêÅ@‚‚Éd[MBaÌŽåÇ!ÿ=À’´%hHñ õ5­â¨‘LÜ«au$[oK,í<Ú2¹3K»ãy”÷y÷ûôû³æ¡Ýo²¶¯ÛÊ¢—cÅoZm½ÕöÅi2öz:Fò~àMŒóW˜4ç@bGŒ¶mêS'nCŸÑ–/4[D®äI˜@A™&•¶¯A›|èŽGIuÂ*cƒÝö0€Mv1Ž·ý¬Ø ”?º€ wAÒL10TÒ Ž[¤'¸vˆîÔ`òO÷Mì­Ãt)ò þí$v{ÒŸ³LÁÈ´l)……ß>ÈÏÉÆüŽû†º{¼Î?Ô‹ö0RˆyÐE(¨ .2¼&= `Å›‹.C0qöÅÀ½&l{rÕ !i@ŠÕ¢‚îÞ¨z) ¢ÿƒ\ñ8„۶׊{µ§RûлîM^ZÍ/RÓ–(4Ó0¨n–×·îCéb‚ 7;]ÛOjC ©mT‹M;GÛˆºLAâê<èÁ’ÖA[åö¢>‡£šÛÀÄ%¹Ý±Ò¿ïoŒ³ÍíNö©¾Q;ñðãLýÃëŒ5W¢AøOÐGÏ€ÄnV½ÊšH=ߣ-_8òÐ-˜ ÷3†ûòúC:r˜œ»Š6 ·“(<¶Pö¶  §{“–Ð{8ƒ~¶7&áT`f¢Ê ßuÆ›fn.ÚÏ`Ûm߯»º*Úp„úˆ©/GÙ6ê3ƹóÊö†!àiXô–»‡'{ öׯÀ2 BýÀ–€»dX´¡Ùý‚ö7kL ¦+ÜáÃÙÁ™ W^@±Qt¾’bóîžã¿€~¡VX²£\Šso÷=ú|±±óPtúÉÕh(U-?A?%à.±PÝL¹dÈꎕoJˆ1Ðþ¸+°Â-‘iaÖÃG lHœ»ó¯â¯ÀؤaP*Àõë^l@ÎB^‚âàz$. Ò°JÃyÔå†AsíŒÜœ©u… õ—ÉFͺÑQ_hLÐI°ë¦AuïJLJ?°¢‡¤õ[Ð…¥®vLÁ°aêÜñ«í9鮻оûîÚ‚ˆ ßøƒ¬€9PêÚýµ¬¼Lçé&ü×,2æù›~–'–ò8qª=•òV{â%šòÕEfº/ëg„¹[óÀ¸OI÷? À—¹m5â& o͆¸ úÜ÷P|©Z:èxÀG)’¶à‚&šüÆÚ—ÃÁC-1Žq¡Ñù½þ0ìh/nTÎÄ›ÊU`~ÀØ:“})VäAT5¥-źN6s~Ð#‹–*f™%Pi6Jís±ð6‰Ž‰¸õ÷ ÞsZ‘*ùâeÍëMú(HZªy` úßÝ– Mûˆ«wPÇŽ²MÆÄ?:=;·íYdäLügò8þñ~âòŸ‘`8úáØÓiù㲂å‚V2ö~ÁÄÑ€Ú `Õ;Ž$#’Â:l4;„Ul§·¥Z@£2š¶ŒÜë]÷Õˆ ýßá!N1½ÖÕÎc)SL ×3°e˜×`ÂZßS;®šŽI$rtÒú#‚¼–ÂÄRSè`ÛšªÉTƒûïI‹€]eÚêi/ï´Š¦È€uò°R«”›2QLm}k¸|a e)ÊR2ÝŸõ\8ÿú¸%¬/ÇyŒ«OÙZsœ¸‰b›;ˆSrÇ,ÂÞÖD £0‘ ¨V÷; E*  lS»ÁjصU3Ó³³ Á€ÿª ódŒ4'`?>(ÆÕ•|úD©l}€Ÿ¦úî¡FÙÚñ§Ù RŽžP6<ãÒ!P°úÄå\§Ãf ÔÓÓs4˜‘%–e̤ãQuF¬€½0híì¡÷©CbæÌ™:Ê{¹ó¯'pÇ)uz(Vñ¢ï¦“ÒµÍBÔ'VbÆUnwÛ®¬«° ZÝCìk‹[ë]÷57Ü¡¶vK™êÎI…–z¹iÖè uÇ%ÆçH”Žêß³\WàvšÛì‘úyh¸x¼—˜%çK+é)“Ä\äµÌ4¬šmÀ /Щ•Sö¨Q¯¿‰éËqãêU²V‰%®Êï¡÷ËÃ¥$— ‹ŸŽn4ßñšî»{ŽÓåbóV°å8 ›,V†u$îG ©Oqù0á½%ÈDhïŸcµÕ¾lSš¤?êmO¼XÎÂí_W£ƒZiÓ>tè§xÛ¼ƒm€TLŒ×`ÕñœãNÏ“ÿ -ôs•Qò>Ò¾ÝãMZjYC,Ó˜Š`q}ÐSîðÑÚí» |ãÿ¡Ö\ÃØL[WG7–pÄq‚B\·Öø: ºÏj–1pÖ‚%×Û( Olò £ì:(y~ˆ‹îÕ,Ï*%C°WÜ «èˆŠŸõÉ»„üõÓ>zÙ˜$Õ«¦HJ²d€N]\„m£««+gÆ^÷õÉW4q"µóØË9µSÞwöG ÛŒS1wb"ºâ-³Æ)ÈÑÅ:kü){÷òz´çCƒÙ×HÛþ…~ô6®¬^b m+®h8jÖÂßÎA“Ú›.Û¡8õ(ôäY8¥r»¦ë¯ã¶¾¸ýó^¤Ó)4µ÷óÐÐѽSº‰óür¶Tú”i7SÊ!¦²n@_ÿ¹}»ä÷‰1•ØÊ{‹•ÑÐÙ.½òu¼Ò‰hwбm½yrgàj\\ñ1Ns®ÔØ ct ,Ò@ÑŠÅ$ª/'jŒ‹¥l­9,K\µoÚŠö±‹bp|æA,Mnw}¡Pßšë'L.Ï@L8P*-¸7çÑ“^F˜7 ¸3Síl- ƒ^…Ñ4õ–¤˜óþ££G—8î“}wlÅÊâ òÓ¥ 2ä}Ó¢Ô4û(Ý ¢”­Ãyê×1‰~ïm£í*ÉIËý¤ýE”éLÒWaB»Áí/{OÏñùz<Œç(C®„¾ÜKL–k"éÔx¥áÐ! ŽRâè“,±Lõž¡Ê£f`Û£jÝ ‡çŒÜ¬—Q–h.—•)¹¬"=q‰ÐåÅPº|ÑD}êÞ?žöÚÚy,eª-Oö%>R»mö r¡þÀ¥A1m[y¸õ¸0T»Ò,Éü:£“N7,ó e¢/>…÷ý5èݸnÛ±”Û6·Æl0všc¤d_b$ب ýúвÖÖÏCÃFû)Æ6(î]ƒ³oƒÑÿÑT¸@©»PÖ÷·3h,pháB°{À,Ð%U£i,˜ð%úX†e™öiR¤ÔRäXÐ,Âe[/Zªl úùè ¤\Z/“¨¾œÈ1®^me‘À䲉Rî+2Š:ul—´!Ò}ÞtxÑö@ç)yÙ+c¥)<‰&7òö<é~k¢¹)-]åN§"†´wacä¾Â¶1PØýIÖ*ˆæ1V&ÖŒ?~7ºôˆö¥›RãQW™†û&ôÕDûM•Úüq+s lßÞE7´6©m¼…îI2\"Ñ–‡¶Ç4cs×.žì•¤GŽ–ã–ˆ~ÇN²LìfyÌ’iYYkëj¿7ûì)EêÎÐûœ<Ò6 ¤$}EªVŒ#À0­ Œ,ÿ‰–O Ü“š¸« 7{rkÃ`W–WOTâ €zT ùáívnAî襉J‹é2Œ#À4Oæ}ùÉÊ#NùÇóÒ*ÞRÑG >eÛ¼/>ù¦y–¦ùå:!’ÚPJ¾ âÿÍ÷gý[Ø bÃ0Œ#À„G €ÌÈÎ{“Å…˜2Îâ­ð8ÅÛU‹7ARÍ´ ¼]¯c Þ3=F€`Zö\9%[„ߌʹ¤å´‰•(îL°•¡PàË(nbååì0Œ#À4QhÎÀ¤t Í!ö‰²&šÏ–”­¸3t Ýû¬Ø’š —…`ÆA€æšCì#å“d«N%®L]Tq€|©U£Ê…gF€¨7`^¢¹ÄžSêM…#Fƒ@\™º .¢{¢IœÃ0Œ#À0¡ÐBsIåí²¡ÞüGâÊ _}è&@¾(Ž5ĤF€heØsæ»O++z£×Ïé[ G÷B³aF€`ê]+È4§°I qep/} ˆ¿À cÒŒ#À´0ŸÐ\˜KpeÇ{; ÁÙeòŒ#À0Œ#/˜ ˆ’L§ÁÐa·ååu•åç}À£Ù8†åäÝ0ÏIŠ@IDAT€B¿kœÔ8F€ˆ7q݈wæ˜^ë@àß#‹Œ’qøÞÄ•8Ô‘Jžåÿ ׇΚœ9#7ë™–„„ϧ4ŸOâ›)ñ1ÃsÆ‚õl|–õ+||åûæµÒòüÓ ^=ª /{`ˆWÃ^•è‡5ŒÇf]…Kvòœ®À=>Ø®È(z_(u&©'4©ŸíñÊc•”¹¸,d‡¦äÖ–U¡á_y‹ïÁ=â^.%eäøGÆ.d‹KZlÕ6ý‚ÑÝàkw¾œîé‘ÞSý÷þêÊ5}Eì1×{‹°Ï™p˜iñg)fbGeÂÈ,ÿ›Só²W·À¸Œ#P˜ H(¼L¼6 ­µC 8QÓ´Œ©¹Õ€°ÑÒ}ùiÊܘ AúiôÅK úžÙÓ9É-^ÇVµ8^t–ô¤ŽRFéãR¨!˜!‹°½ð™ôt½Uˆi*P: ’‡Á×^*9ÇãÕoŸêüÜu qz[OÚÍT<¸'"=/2öްÓ}cÿp2™žw·ÖÙøêÙ`ÇÍyB ÿ->¦õü¦ ÏÎf)cù•a/=š› ¢?9>;w(òš…ïn†47"î[=={dú|×—:a"=½OV `1„˜†0çD çv–3þ$iY9Hk0 “¹‘4&„!³£¤çäݤ”5yî‡|-FØ|Ä+wÓsìÃP‡?F ñðv@â1æ"!`ª#¥%§¼ßÓ‘‚8î>ßÌ$Ø8GXâj!åsš&¯‘šxÊ„÷ò^sÂÙO)»`‚:G%³„P«¤Ðéƒ$ab½B^ð1˜‡2)µ»ñ| ŒÀ±†a¼F{õA:6 uöN£è š¨¡›p»ÐÄ8Übv¸0çŽô=´{0¬°z`~PÕ{• “e¼Ñ¾¹0¥¾´fÚ¾R=®Iͯ¤6Ù~ÇŸŒ,ÿ?àÿ>ò¬£|·#3ÂyX¡QÕ œšÐŠ%â)¡ÎÆ„}…C7Òsx–ÿJ©ÌOPþ-Hë6ü²Á€t4DùÜŒœñǺã)ºGYÖ øÿ¨éâj„¥†;iÇŽì -G(=~gÄ!À’€ÄaË”ë@õQ˜€–_zé¥fAÅZã÷Q4¡b•ÚßµJýßðìñß›Ê|+#{üYùþÌw:˜ôÓÀ`Ìœ‘—=¾Òí5¬NÓ xx &°©ùþ¬QŽ;&­–P¹ëŒIûÀíwö˜Œÿ“ïϾßqî›ð.˜€%†QvÜèµ™‘›ù9´é{KÝšìIzë ß½kœÈ·<þxrن퓿·gøs.¨t.#;w.gBËçÄs?ËòTHþr^©,59Ý÷ð‡¾»6¹Ã8ö»&Mj³}kÙ$%ä+3üÙA†Áçûô…µÆìÙ–eMFºÇ’aúĉÔÎÀXÄ}aorhÜî{ôÕ¢@˜¬*rTQc#À$Œ‡l]„€`¢^]êêb¬¶?v1v´éþÌ·1Q-Sº,”Ž”I¯ºÝ öÿ„Þ•.«I°¢µÝ¥(ÛÓžìšòþÛíFÛ¿‹û™n÷†ÚË7í8“îžXe?릥R5Hgi r»‡³K‘l÷géñŽÀÜ IÈ£á‘ێåÇAbÐLÇ“î0>ß8OAŠG|`â^ä'w"mœÚÐ¥^à;ÙwÇVè!NGÐÄ£Abla„#À’€„CÌ DB“ÐZLÌ=#ù»Ý±²ß“ýÝn.;Vïj_×»mUž…n7L°ÛMÈtËSíjkÝ+·›äFˆ$wx²§tö® uÓ4ñ»e‰¡¡î yǾüÞß2ÅCXÉOph© »¨èïP/ðY B¦¥Äãx¾”ïÏyŸVôÝ;d—"©‡Ð½ÉA=ÇSjÚ…Œ˜†A˜.WÂìcûéÞ ”$VŠÕœcâY‡&?F q08l™r@ °ÊhgΜ©G³%€‰+–¤p—É¡~ù÷§—ø2B…)Ê¡;ÑM³†´ +å6P6ô`厹“km†” ëBÑ-)ÛÐ$ r¤+PJÒrE¨[mï=<ÙS +q¤ÓGú¦ô7[Âb'“šî:pFDèTbª¥ ‡ÂJ1ËBÓT–(“4ñ.G0[F !0X™h4hB}e*qíG?ý~-Â?Sk©æaÎí6ŒT½!/_Ö¯ŽÅEF¨¾ZVê$Ìü«] €…y°†{ò]T Äëš##æFSÚR¬½…ðhs¦û²æG ¥–•3ñ¦r˜0¶ŽsQ Œ‚±-¡ÏÂV±ì Ç-A²¶öB´eô fm%Ê+d©Õ mäæ`Iò­0ñ.‡C—ŸŒ#j¬r“ Sej"`íÙí9L-K…²ÈÈ~h¿š!\.J~‹)ì4šX]®"=gâÁX@‚“Ÿ¸Ýãe7Ló7­á¾IÝ W?«û¯ƒîR®[º è N!„ѨÐ(7Íêú/®ÞÅ1Æ€æ¦Ñû”Ü1‹4)'b7a¨nZ)]Û,Äì^Šÿ*·»mWÖU˜ÜW÷ûÚÌGY`¤)Ö…î°§)¬“Ýn"å¨FŸ_F ®°$ ®p2±X(ÈÈdÜ—7ܲÔkJ”ÍÇ™úIB—ß{”¶Á²Ì~8’ÅAýõé¹cèÐ1ùŸÐf?W%ïã¸ÚíoÒRË b™ÆT¬T÷Ð=KÚÑ„]‹ß“‘“·9I÷|0­®–Q6âoCxô྽žOÅ0'¢<É$µØ,“'@¼ÿÒÁD[eN=xßÙ-üm3ŽÛ݉o$Ü©y½&˜î»{CFv^.6ÿ' |åšW¼„‰·XÖ‘JhG ©O)ÈóS¥èl=ô~y…Æo—€%9ô‚†'n½µ '®Ç)‚ç‘ÞÆ$©^5ER’%7¡ aKâjŸïÒrJeJ^öJÔÍ 8É1qÖ&ëž÷ËMµÛZ#ïNxïæÎI¢ÊáNƒíŒ#?X?,™R=È—õqŠ'é`Dý¿›…i½kXÆ\lIçcB<+lìG ñðÝw¥¦IZ•¯ÀdôF P¶Î´¬×±šýÞÛF;ž´Ú)\< „Ü¥º¦Ÿk)kLi |5nú£I´”O)ðe.vÒÊϳwŒ@^²Lk–Y‚­KåH¼Ò‹N8zÚº¸Ÿe8¨\¨?piPpÇÔ¤º~BQñ[3`ýå¾gÀL ”J«ÆL¸iÖf·'r%oP%³¯Œ€o2¼Œm‚ðº¬LÉe†*_Œô.#vq~nÖ‹nº½¼½F`àHm¦”Ê •eÌÍØ2¸ÎŽì‰(GhüÎ0ñAcTü n@ûŒ¨…»9-~©0¥–ŒÀ-ãÇwUåº÷Iß½Õ4øÝe&1ôÆ@Þž'ÒoMT …îÈQÚq à¸ïá6$êRÑGŽŒ¤Rý‰ÌÌ‘HP¾6òz%yÛì°ÏE XéŽ;új¢ý¦©¾Q;CƒV|T©¨SÇvIº÷Þ¡þñ~5~ünP곦[]? $!Ü#àùS|³ÇÉí³:ì»!Ùå×fŒÏ'Ͱò¨ÒœŠk†Ùç,3Aˆ –•‹.mº /[F Ñð|’h„+èóv@ãàÌ©0Œ#À0Mfš\•p†F€`ÆA€O4ΜJ3C@ ï›R+\w4³pvF€¨fêÆˆC´Bòý÷üŽbÓ #À0-Þh±UËcF€`jG€™€Úña_F€`F Å"ÀL@‹­Z.#À0Œ#P;ÌÔŽû2Œ#À0-fZlÕrÁF€`Ú`& v|Ø—`F€h±0Ðb«– Æ0Œ#ÀÔŽ3µãþŒ#À0Œ@‹E E\4"gÂ@Ó2ñ½tÑŸaí‰O#¶k±5¡`øNì|âv->;»JÇ÷Þ§åŽ!(;G·©(@â -à˜"ÄJ|RûSZfu7[&Àç{6¥0Px³¦ÉÑøþ|ºÞµ]ZŠÕ±}šLñz[„£4°¶n/V;ŠK5àáÏÈÉ[gæäö¥mŸxôÑÑô-úß“o™Mºþ¥â6Uì8fËC dLÉ£1Å4­G÷HÚã ŸïzSØ´š%€OLžö§¹öÌk½Ú»—:òo{‹ƒ÷í%ÓR’õP'õ-‚Íø—–‰Ÿ–Šï-ßýç¥k,i[rÛuwg{n’ÿ¶ðcf Âܦ€ÂN­pcÊC{o¿qŒïƧ'úhLáñ¤™·’¸NšO<ù:ÂcÞŸ>GÏD ÖwàïÿêÕ­cÛaÖÎ4@îÑ­“ðzš%?wˆÂã¨þ{Ëýûî.þX·)­¨4pÅ¡ƒN(úaöçßU&È×…<·)leB7¦ì,)¿bà‰ƒwÎÿòÓopâóx ˆM,lsa$ÄO§C0 šºé“d’7®BŒ&V-ñËáDxn^oòøËo¾óLP'F€ê¾53ܦâ×̘R+BÀ=¦H©=tí]™g ø­}Ъ_÷xõÛûﻇb€†5#ÂplÓ®ÝpPJÅ/‰ðů5IW¸M¡ÂÙ0ñ@ÀS’SSo½Ö8žÄÆ]J£©3ÚU·Þ{¤e©Ýq °5MT k•8vú÷DZ£4€ÛTÂZnИ¢”ê~ÁµG¡ü, hf )3öŠÍ›œt:]„{š´M3»„#áÙ¹[÷SÃüZ÷Îmªi6KÎU3FÀSÚvìpj+Ošq­Ue½É3Èj_º UåšmõF€pl—š¬4§7ˆÐ–@k:2h3(3·©z· ŽÈTGÀS0FKMïŸÖ4žT¢™¾5e&€ò¦ MöÀUÀM9ŸÍ®ê;vh#u¯§2î–´Œ¹M5 µ–•Äâ?Ö5€BãDým埢¤¬âqâáûÛ˜üôûñá7?‹56‹=»wøNXQ¸Q|ÿË ‘œäs,Å9ÙcÄÁˆÿè|òÝ/bÛαŸÝí°{õê*êJ£<`Š×f}'~\²ŠöÄ‘í%.z¤½Úšùáwâ‡ßV G'c<í˜þq¬+›¹¢‘’:,ýï–nÞ¦Z2€Ô–g~ø­7âB¯wŠl¬Ò/z”xïëbËö"1ð€¾âü!‡‹Tô£ï~^&ÞúbÈy¡ }Ø*ï©·ÄUg'6nÙ!f}»HÜyÕ¢C»4¬Ìµû½·kCj*ÕM»6)bîìßk7Н.µP~¾^°DœpØâä‹ú1õò_—œz”8tÿÞbgq™xîÍ/ÄÒ5DŸ]D·NU_/6CñÃâU¢ÛiÈ7:¡¸çš3EZjÊšÈþW½Œ-ãÍSZÓxÒ"ª­©üÎªŽž$Œ (‰¿ùi¹=˜õé±›=˜”–Ž%Š¿?Gô…Ûm—Ÿ†¦·Ø­C;<÷mR“Åaû÷×w¼Ø«W»!tnßFü}ÈöÀ0LñΗ?Úîµ¥AÞølžønÑr0Š‹O9Bì»gw;Þó·¿xݹ'ŠsO« R,:¤ßžvº+×ý…Ak?1ô胂ùðèºèÜ¡p_^DÌ™m;ŠíÁæ­/~èÁ8‘Òø+¦î%΂”Ám~^ºÚ^Ùtß­ ßî]:ˆù‹WŠÓ=ج¡vbœéÙÒSÖ–^ÎF+ß¡`†O9ª¢o|µàwñ+˜êcÞ§Öô©ÿDàáçßÿUtëÜNœztd)1ê$Û^Tb‡í¿OÕqab*þ~öÀ àîs”‰Å¬µ¥kÄd“Yºj½-­#ûôsúâf‡¶ièÿ4Iù‘L#õ?;­öÇéc­a{ìŽmvtlU$¾ÿ9¹à'#°khÊ:»3’àÎâR[€öü“Iqf÷Ý:RP"ÝZlÞVl»wB˜¿¶î*.UnÚ~‘þÐ6Ä|Hho“V#   Hæ0(5}»h™ø ç²ieB~¡ÌB$š poÉ«ä–\¶Tyø¨[±­µ¢pSðG“y,f¿Þ=À1[›Ð'>ŸWu6ß0Mñü[³Å®ÝzùP±~ó6(à.ŠH~{Q©XýçfA |Äl3н3}³ns@_ô/l£­‚D€¶ù~uÝs°Ç‹ °H'úõî.Ú¸.$ÛEý¯î5¯ÜçšA}5eI@¥Xi׬XMËï~¹@<ûÆ8¥C_ ›\yjà$¬ÐÿïýoÄí¿$®BÑQPH"¥#ÒJNKIçC éßï͉ªú/=íhñôÿ>ýûC;<í§Ò¯·­åüÂ;_‰Ép§U*è¿ødѵSû¨èF¨ã ^@”Ñšm°]Ú¦šjßü´ JtË‚Yö ÿ{Ð…öùé” m“½>‰ôIÜNæ/~Äþ~©¸ü¤ùOJ{¯Íškëáƒj¾#A? ÛŸŠÔ¢1$M£É~ü3oÙúÔ¿~Á}dHJPZfˆÉ/}`÷1 8Ò¹9ï¤Ã«ÿES„æ¦â$—{Li>yoÅ9¥ ‹›IÏÎýŒˆøsÓ³†ä{tV¨ýõwg¿qо}Ž}õ Wÿ¨t1Іk0hŸßmh•S‚UDN 8†¤´J©)ƒ65é„n´—J·ŸµIM¡ÓKq1táÊ‚_~ÿõÅǺ ÿÄï/üHá ¿Ø–|ˆÐLL“iSͯ¸d“VýÿBûO\ˆÇ@¤¤´¼Zߢ~}Ïä™âò3±õ}ˆÑþ|þb›i™|×UÁ¾–ˆþC¶›MPS~Z¼|áóN¸ ™ŽËxÇù¤Ùà¸+2Ú”%»iz=á!¢ ÙÍPÄú2qçCƒg[(\±aš#´·ÞL(sMJƒ¤3@й[pL‘¶,âÞ=ºu2”oîM¡ö8‰D ü̓ș6#À0»bØo¼àDñÍÂeø–Àb‘’”dëœvl䣊»8Ëœ<#˜ H¬L”`š:ሠýØ0­êÝ­ .;#À0Œ#ÐÊ`& •U8—`F€p`&ÀA‚ŸŒ#Ðd E½QžÇ œò&“'Î#Ð`& %Ö*—‰hæБ=º$‹ #À$V L,¾L`jA€î˜ ý…¿¯Gák„ôŸÓ]ú?/[#¾_´×q÷ƒÝß¾f›®å|‹qËfQI™¸âÌckI½F 6XP:ìÇ0 Eà½Ù ÅÇß.—÷?âz_ú˜O ®ãv̼_ÿÇà3Ã_-X*Þú|¾íüÇÚMbÆ?Ûw–ˆCq½6F€¨?, ¨?v“`ˆÀ"\á{ÒàsÚ}}°‹&}·9ëøCð•ÍÝìot̳pQ¥']/|5®ìfÃ0 C€% Ãc3Œ@Ø^T"èÄd:´I­v[¹Ñ%>dR“½öí~ö þôéQýóÛŽ;?F 6˜ ˆ /Í0qD€¾¢¹×õÒýó[iyÐM~Þ¯+ìÏÏÇI/€ #ÀÄfâ‹'Sc ¯ nßY,F?òbÁo«ªÅ¤ofÐG¹Æ>ùšØŠûý‡ÍWúVˆ_8 À:q‘I0Œ@ýèÕ­“¸ãª3}ÕOâ«\ŽN}2ûá;.³‰ž‹OûêøÌ¯c®?ÿDÇÊOF€h U=«„8:#À0õE Ò×:‰ž›¨/}ŽÇ0á`& <.ìÊ0ŒMögTlää99F U"ÀÛ­²Ú¹ÐŒ@ÓC€¶Î|xÓËçˆhÁ°$ W.`F€¨ –Ô‚Ý_N?ǸíŽ[¬OZí8†ìîwÇŸ-x·+wâ6ÕtÛN¼ëJÊußt뻹䌙WM9Ô‡K,K‰Uëþ¿â~òÍۋĶ¢bW2„¨â \1£´VÎÿÉI\Œ’&:wh#Ú».>ÙMhØå=* v ¬ÝË׈7¿øÌÇqùiGŠþûî)t]³™¸&ÈÄ jW?/]#^|ïû2œÆhOT¸¦Ü¦*VÄÌöW ~ßü´\¬Z¿™¸p!4]d’(^aJxo%’Õ6á5Km;•­]Zª8î}Äà#ÚÒÃMo;Ǫ)6Í VË8ܺa˜X­•‹Oñõ²w¿^$ŠõvbIÛÃÅ6O—„×#1ë“ö°ŒM¢_ÉO¢à_ˆsO N9êoÂãÑY*ðZˆoN»šõÝ"ñú§?ˆ"»= j”öD%iŠmÊÁ„VÆ6oO¿1[¬Û´U{:ŠõɈ­Þ.b‡ÖI(—¾ŒS+Ä ¤˜Å¢³±^t ü)>øægñùüßÅÕg-ì×ûÿÙûÀ¸Žjí³½©ËE¶e˽—¸ÄiN!‰!! …þþ/Ç£†àQR€4Ò‹SœîÄ%î]®r¯rQ×mûÏ7W³»’UV[´wwgì«{÷Ös¾9sæÌ™33\>¢16x?ºZÜìM°ZÌärX¨ˆ»Ý¤±€}º’äQé“t!¬Þ›.òÒÖïp?»k·°rÙJ',Ãh«s&… ©qû÷%Ó`t¬)8&¹×±W`ƒè+¾èôÉdae&ãúò>uoÿ# å «Ý½ôÁڌʸϔL$ì5LÂ<ÿ€°XПý€êZ¹ Ày:·÷õô'Ì~ÉE‡L£ém4¹‚Í4ɳ–~e1}sþ\ÉÛyÍý´i×a:ʆ…ü¶|§Ëa§ÑÃÊy•ÂQtÚødåxœTw'h<†H鉺ÚgyiÀe‡ëõùhãŽýôÖŠmBaW»æd4ï`|€†)­D o¤%¢µCÀdêÃ$£`dáÇ!W›v —­Ó…<Âþ©heâ9þ=´ë@­èãohñˆÊ×±R`]c D¶ÒF×¹l $¶ <+ë]çÐÜæ÷é¯-åV¿Ÿ­ ¢&~_½mÇî¸Èo´’)⮨aÏ ÚTsˆì6+]vÖTú/],ëTˆ™Ò'©@Q½#Sä€ —<M-ôÊâMä6 @¦2¡ówá˜ÝÒLϽ»–Æ $áÊLu ¦ówÕïÄ€\y½~zê­•¢ y¨§”™’-`¸ák¸âÕVŽ«9¬y Fj3:)@Ûbàþýà12†cS‘° ñ pÌÀqs™|ûÙ;0–ØÆpYw·a+ §ÉMëè¾'Þ¢.?‹fO)¼l‰”+™ïJŸô\>ÔU}# ï±6)ÆN³Úµ8€V÷¿jÇ!BÔvö•ÐÚ–r5¼èSý*é)WÝ¡Wyj©)­â Šî´ú¦fúÃ3ï±°›ÚÇÒ²ÂKh·c2µ˜ŠO1ð}Dÿ‡Ù3Š„÷ÄkÄ~¯ÞIrõ˜ÎÈ #­龃"C4¯ßÏžE*§3Oƒd&›¦€¡ˆ%/éü¦zw|ȼÐòQQ‘8yaé¾ÓZ4Ü÷—@4pâ0'÷¤¦„ƒ¢;#Þ–Jr_TOÇ‹€”+äQ6¥¾ÈxDeÚŽ}µ¢,es7@ç|jã@ÁfÀÅbà3ž2&ó]é“ÎhªßÙ†@^Òr‡žF„}-ê,Ê)öcºA¿ä%‹ÈÏYRe^ˆú?ÛŒ€>È”ìò€míI1ü/Ñÿô( VžYÐÁÓ #È•;òµ§›ïJŸô„”º– ä…€Œˆ\V~ñt]e*ücåÔ›‚ÒÝy@ŒÌ“ž« §L?­µ«yšZ¹ÅÌ3¦j¼¦‘1ñJž¥<›`E©Kó´‰2Ö;UÑ|Wú¤w´ÔzF oŒdB¤àöbéë-ÃPÁHÚõF›¢GË›lÃ!^™‚ÜÁhÆÈmPÍ’ùâÉ“‘ÞídiBe9—±¾å¥,“ØgSŠ7ﳉ'Ekâä•8LêI…@fpZÒ·üm<I#@x‚\ýóÒ½f6ú#wSÙ¯hÛO•¾]4zP! (vòÔܱìpðÖ—o˜•–î \êÞ>"â•KýOWÐ=g9#ptñ¤#Ÿ-¤Å7GÎ];ÙJ/_W„9dRšÒÁOJ Ì“—¥:¤LX ô“óœ´ö¥´ú%ôÄüB:m°6ç~&d |bƒGÀÉ}ç– ‡WèK­!Pê0Ðê[JhÊ 3]1ÎJoßPDk˜÷å7—ÐC—»HC|º€î8Ý‘´„ óí¦ ¼lw¡ÝL3ªÙlësy¡.þßcJu¾ãcW·ÑÂ/óbK? ³é›¥4²ÄHœF!cËâ[/¡ã›ºÿ•~ºÿšº¢7òÂèR‘iûä²aV…™Îª´Ðcë=âEPÐ/\Ë­ gÇüÒ¶6*²èÓÚ—2Kâ³±ô'ñõhŠù’ìkceÊÉF@e‘‘n{½™.¢‘ü<˜å;gj_¦d |" ,²±QæUùŽ%Ër‡ço›í 5GT},@ Þ0=¼ÖGók¤›4Ó9íô…ÉÚ*^奛fÚFC2) .¸¶oöhý>-ØËuÇ»ª ÌwmŸ %D®ùï<ÓNYí¡Øi@¦²AôÙ‰QÝq¢¶zéžvYH櫱ô'óõlö#š%½²‡>qðÙvzi›ê=šb?ÀÄO´ø¡ d ðä~oðîe{›<ÝçýöúÌÓMdboƒÑÆ‹³Zx’#c2öÛD•gLÿÛ/¿Ýgbrà¥ÌÃÒ&¢¿ÿ©>9n¸eò{)äk¥Y<´Ìhµ$ü¾Ž2¦[_k‰¼ëí]mô£yš*™ÂËy  ý÷"75³äš¬2£-Lk†Ÿœç a<»vû2Ö¡°^ª1Q!Oµ;»“N˜‡Š©·#„&xPȆòõÓ¬tó«ÏËø#o:é&:îr‹]«ôa(ì:¢/M³ÑWz#÷õõà°m·¥áÞ]>VC-ØH-§“‰ “ÅOå&úí%.QAËwl ÑÏ>t“‡çí¶q€¤§•|-ü»ÁC<åñ¤ ¥+Ç[ÈÆÐ/ˆÑ0 ~q¡“þ±+ý³¢žGÖù胋i\¹‰vžŒšã„SåÉÔ$yPWLN`¦m'¢ðÙj^bâ)iËñ*5‘5›§|UÈ6:ËT,ýg ³Ðº£QyëO™‚ ³Ù, …> h¤o[,™ .5ŠÊvËñ(•E&ºtŒ…~ÎaÆÇÖ¨]ÍFñ$.ƒÉ&,$„U7œA–°‡œ ;à2ä-Ù7÷íy4"¶s…Œùî­sl´¯1HíDxëÑ–ð”L,Ožÿ¾Q©îÎU”$õ1gmf .0ÐÞ†øæðßËm˜ÅFª©‹*¹>~VÝžÃô$SŸâþñ G›éK/F=©’)7þë'ºÈá°ÜáÜÆ8>s;ÇÜ5¯ˆŽ÷Óê]¼Wëv²„xEN®H“Y3ad±‰êÜaji‹Ö‚Ÿá.´ÛæØEÿüýËÆ.¢T~S¾ ü¯ån$þâB½ÀÝ…Gi8﵆Éí ¬†¦˜ñõ'/HmIÊQ‘»â³Ä1jwkÏh/Wò ®d ­ÁÿãÊï¾=ôÚΨ+Àô§Li]²#ÀF»N9y«*uƒë(C8DÖpGû’µ\6à]ëœü%ãg1Çœ=<Ú^Ìeª¶½v~&Ñßnƒ~ ¼¤Ÿã;0 ¼ ý‘Àÿ v}‚“Ùð¹y¦^ÿb‘à8³RÃX¥šÿþàS}CŸDK–>éÓU°ÄQ¡£ŸAJ½¥*võ!ímTF@oXåëõÎ25‚eæVúËhsmfòh¤= AÑÜß2ÕÙàr9ÈÝê¥m'“'h Î¼¬pUÂÙW8*?·|[¹KàjŽˆßÈ|£œÍà‘7Ž´0Q_;øß[Ÿz‡ªü0Wþ˜>Øfµ # s—@ÂLöðàæ ë$ð|ÆÃìaiOãËôø5…ôùç›i+Ç!¡ûä™Í¾ö;ÔN!ÊH?Nâ–Z‡‘!èÁðÈ­1ÉýE§úNn" Œ€òõO<ž÷Ÿ.¢ÿ[í%ŒÝ• }˜Ød²ðˆ€O³Óo—ñ8'•= +S[ËrÄ[W)S2%GÀ¨­o¡šÃõ´×>!i<¢Ò{|£n™eg# …~ú›~ÊçÑ',ˆ{çr@à(MpÛ©m —ùkñzTÖ¾†ýÕðÚŽ6ú&A^3ÉFÏoéÈ—0c†²£wwójŽjx È/õ'yTL@näáZ¯íôÑç&E'òèê5—s+®¦> Z2]]Wçl)̈WnÛÏ‹Ùè€m¼$?éýÿqK¿!B¦ÎÎ_7ÕFqРœ£CÞ›ì~°ÿ ¹ìV*-ˆŽÉOöñ>þ¯—zh>ë“S##¢o)ãÉÈέ2s£¢k1z§:RÄ€òÄU‡;ÑZé-½Ê>6•ñ  W™B‹•?Üäè/ßWÛHµ–‘2¤® ÿŠ'£}áÝáuÏ[Úî®'r¾˜W,œ¤ #Df „ç£?â$½ïïñ¶ž†Q^ú8ñ¨¤H©+Å©£)-o’…ZÛ§åi}i,ýiýzyŸùÒ§‡tr³¤ûžŒ ‡ôüt²±Y»MŸÉÈ#Æz6q¼ƒ‰Æ À $iñˆƒè)Åb× |=½&c×béÏêú@ gI׉ÉÑ•’C7¥x”ä)=õ X¨ÅÂý°±©+~b¯«ãþA 6P‰˜úiu½d¹ë«LEŒ?G§·ÌY’˜,ý©|~¬g#›hÆðr:ldåQ˜F@lþÆ~³«óJŸÄ"¤Ž³ ޵K6Qž$­vVÚ¶þûÖ@£Ýí'M’mõxš@²gψÔW™ÒŒ­+  rß¹…ÊZv(L8¸t«¤È# R`9þ®¾ò^ò‹•[yûÐ =Š‚ iZVÀTG =Òš¯4I¹ªª(Ó½DtÎ÷lÔ'óªlôâкçm7­ç%›‘°.ÁW^Ñ_®t‰œÄÉ8þ@§°QˆécuJOª[2€^€ˆîùƒÇÅâáIš²b¥ÀŠ Ê-Ÿ×G>Ÿ*y&´0¢œ½‡9Щ™v:¦S#·bú3¡¿ÝèÇÎ}–c+ŠD‹ t‚^ÍU©u$KWCSk¸Íçkèâ=Ü»¸–í§"¼¥J¦HWr5mä b#‹¨öÍni¢'ЖJ™B¥'‚üĮKƒ vYÑÚØPu»=ÜÚöñwýì)ðªw!âºÚØ`À2[¨bÑ2wðÈL€e4jêÆ„=$“;HFOÞü>5Ù“`(¼ m¼NAˆx:&Ä †Àj…rÿQñÊ¡œW׫bãÙÁ´hCý,Âx¶ZØ“1äbCÀé´3­vqŸô /ãM]å{6ê“ ;/U<¿îãEÎþ¹AëÆÙ|,@×½ÐL»ªF•ÄgA§øÛÚê:á)sΫŸ:B@¯F@"¿Ïw¸Ùã3¸¹Âv²û>ѤZ#YEkÆ&ú4}¾6VR~VØAÆ ËÚä¥ÃžftZJõæÁTk­¤“–Á•™èw{zc¶1d ÛD|ÂH^!­’=pSºÐZéCÔrOß’×€#ðôº[³¶ÉT÷©’)|¶;¹šR5ÇŽó’¯G[4yb9ªµ¤Wž@O:eJ\u ¾µßZ¬‚<ÎÞãõ ÃZla^TòHÀJ¸Ñ9¾Æ<Hð*´µµ±—¡J=mbeN›¿žŠ|õ4»CÜÓ害]ÜòÇÂ: ¬TÀ;Zö‘Ö={Ïd=Ê= L$Ý÷ša c¤çÑ]}»»|ÏF}ÂbJ?šç¤1efºwQ+qvˆ%œ¯gCàÏì˜UÑs5!uJ›ÇšèήWçÒ†@Ϲ›¶ÏÆýâðÑû–•—ß¹©æ1utÜvu£TZèÿC ‹¡`NtL‚‚„ë…V/ãå\)—B¦¹€øYéùŒv R¦+Ƭm˜¬Î3ÌK2ЦʫpSЍå"L§Ö_©ÍbÖ_}9Y)öïØ¶‘Ÿƒv–[_^“Í÷¦T¦Dwr5Šåª„û¢wo"SË1aè¥Cž@CÈ”VñEƒS1lPx¸• ÀíñWx×ÚÈ߯Æu½lÀ€!•?fä­o>F qô²GÎãáÙ1m^rq·B?ïg㼕» xž‹(øÙø1LÎc·šÙCfÖâ{Ú½{p÷À÷ ?*z܃ÀFì-bÏñ5íýÿÈ»DSwùž­úäÚÉVì2Ð]oµ÷êP£7D7½ÜB¿½ÄE—òЍÝ%©SìªYÅ÷ä›>é–¬9ŸšZ-=ì aúðõ—ªÇ͘yrUõî26X$ž"Ö;!VJ0Ú)íŠ}ˆh1x©‚Ý™-¾ µø ìÎt³"r³"Jêó  eaóÛj S1k´B‡I¸&].‡ð`ØRIqq$jY›À¤ï­•ÈcGžù5Ø´yõò]|Ö,´BÇÆÜš‹‡)—)€Ô›\Y90mx+/Ûâå%päãp Ø#§áO Îý%S±†*ATêO´®Qù Àáa‹z4#@ku£Cñx˜ÎÃXÀsèNð°!c¿a¤±'•*f,D2ò3ÚwÑ=Á­{!N6˜áꇀs²ò×’Ãl£-ð’Lê-ßÁg¶é“óª,ôøÕ…tÛë­Â#ÓÆƒw-l¡²§àÆ]{b¡S¡PÚÅïog<óIŸ$#>ºyV¯F@¬ >ôÌfƒáöûÒ¸I§Yï<áI}›Òe¨)!î~`C œ# ¡„à²D‚ÒJ4I¥ƒ‘PdŒ”=l³éªF@{¹Ã3èFÆZùl€ ìˆ‡/F ¦Gâ;Úl›’fÐ’ª„w¢²Ï%}2e™þý¹BúÆ‚Ú]Ï %ëWKÜÜ( Ósí “:åÐÞÝ øB>é“8dó½À­T!T¾üÂsŸûæ·¿ðôÂå¥?ºé*£•‹’I(¸¨|‘4ŤE>‹¾BnAxY Á=Û¯«ˆ’ù¶ü™tQ"úÑÅpý#ʺ@'èM6µùƒÄø…BóÒ7¼ËïC806Ypµ>‘d?¤ïçÓ&S`;Sr•)™Âwe….FXxT@Èéb ¢Ï•·4œq¿ Ušk([~G€>;ùœmlpÀ.Çc©½»N¾c­lÂÐÞÏÏ^Ù |/Ý)Sù¾žŒGªõɰB#=5¿@Ì(¸æÔÑWyxñ¡0Ý>Ç!~K/xù5>™oúDàíôjÈV*¨@CÉ–-kVþÞpú™?ûׂ%á›çŸŸTÉ–¬TJZ‹\ëÛ„ ¹5ÛDÐ ú,c€öFIŸò^ê"ùmL] E&]©0d+•?Îã: žI6·ÃÇhÍâEOºÝ-ØÖ¾É‚+1OöSz}^ò—™Ó2oûK®¤XÈïö·LÉŒÆ÷a ðÿöJß,ö]yÏp/èÖö†È}.c¨ÀáÆ÷ûy^ŽÝ ({2I^e%¬…1ÝÞò×èÐ ùLº÷’¦þÊwðÓyZCæŸ@IDAT_Ì#ýL}‹§^¼_[hí¡^1…ôm³í$uÊú?ÜÒÂãb5’/ú$ÝbÕoï׫Pò!P¨¬|ËÞ~}yQYéã||#½Há¯\5ÏŒG C`#ˆÞ´@˜$]š"½’1­¦£Ï ßG7I%†9 @PñËÀ%\‡l‚µŽÂºzëCÍæ Ö/ûh¿cä|sTÓòMi•)`Ößr• ™êI6$=¸FA<ÉÈ­K­%Ëe0h¡ —°Ûî%ˆ-wx?Ê…ÐÑ! fÜÓß ßÌE}bå!ºÂE·³!°¤Ýxp¹‡ÖTï ›ì1ìÙ¶ùùÕ¾·†ñÎG}Òßb––ïéÝ@« •–¸ð,ü÷/^zí¦zÝ‘“õáë>y¦11šbiŸ• QùsëŠHÊÖˆtK2 }NR9iÊJc-Z.¬Ô` ˆãöÊ_ÞÛçÄ<€þ:tÀ°sóú×½òÂ{|Y`É{xp |s¾i—)Æ2âÁ‘}æhå¦C®¤œô—L·T'ð€ <˜xÈ_˜gÔÓ•NqÔ¼½äø¸/Ž5—‡ûϽ¡òkÚzðÑ;žãŸù¦O$TY½×³`e.ú›`i¢+=Fn£÷_zö5>~oÜ´ÓFU›0ÉQPXjs8ŠÌfK×Zùæ\Mσçö4»[šöíØ¾£¦zÃæUº¾a¡£Òolßšxlò>>Ì›¼€K>†uŽ X3Ç0 d<@>x˜]‘”LI$Ô^!Ð3]êËÉÖÃÿ¸û§•·=r¿Ábί°»ÆÎ}lü^;gǯ>µ—ç“>éÁ,¸ªw#Ê ­VY±Ç*rœÇÀU´þ#ž>†!€$ŸÑ~åæ_Yè¤ c)€ôÀ €|õ0ë")™’H¨½BàTzÕ'žý[—>qKé_šo.å­‚T½8åOÕóªï˜]£R– F „ÒÖÂR5+SþF%‡–.ŒLe#<‰.ÞçƒÀlŠ„‚ \`À%¼€ ŒTú(˜26¸á:îÏ×$eüK쀉’©|•Åw,²Lt«O޽þà‚éÙR^õ2æ´p×ÀŒ€'ôÔ½áðÕ÷x}h•²l1¤@BIÇCa£bƒ ‚yß•ËÆð@’¸ÈB W¿¬Ð`À Àoœ—ývòY>•wIâ¥d*ï²^1ÜR'ÈòÑ£>Ùý˫ޛðÛ·òûÅ;9Øñª§ïÛü3>üOüVIÿd‹$!”ÈXá”-7x¬¼I@Æ òÏe€Ù ˜È Å\`„Š{l¸¦¬t“’) õW!‹€Ô%²|ô¨O¶úcã·i¯Êöc¼„úñøûªîøÞ”%±/UÇúD ›Œ‰ *0'öhÑ¢‚‹­ü¥¯1±W^ìeåë*uD@ÉTG<Ô¯üF@êY.¤þèVŸl¿gêÿ›ø»Íg²'ଞy‰´à¿&<²mÆö¯OD ’J:F À)+:i …‹J›lýK€Üó¥œM²Ðb/7`#7y.gHcÀHJ2¦"Ÿe*ªWd)( HRo`/u öò<î‰ç'Oýݦ¹E¶‘¯–pŒÀ(C½ÿA¾øµö[ÔN§d« á”ÂÁD’€ö+?º$¯ÀB&‰‹ü­öñ# ±S2?fêÎÜC Ïúdów§˜xßæÛyáõ§{n߯Ww|oú˹Oîp”íF@çœ ¼óyõ[!(J¦EN=—wlûÞÔ§9PðS\h¾æyU‡¿O}`ÓÇ›ïšV›w`d Ãpwª¤P( ” à0ÞÁ•ÿ~¼Œ½Úô@J^¬^’”XÕK …@~"°þîQ a£16à ãïÛ|z~¢¡®• ÿ–T‡ÛüªK@‚‘¡½22¼ú¬B@! ÈGÂdXá;LÓ#Çê #(# #°«* üD€+£’sÇjŸ”ÜÕW …@^"`ŽÇ0®Œ€02q¨Œ€L ®¾©P(ò 1Ƈæ) ºaÛ¬JRCÖ©Ž]«:ö85_PoÑ+áÂpû;æ’:ŒU–â*o‹-;)/KSèx8¤¡Æ/wå ~YÅR¶BQ}ý÷Î1 Ÿä꿊Œ†!ß;ÿzà—kø¥P5ØR®È’&X/•>¼ƒÆ¯ÜõãÙ›íY¦Œd(ÐÉŠ¢t"6P³,SáPø­G~sïjþ^ÒeÉ6R@ÎÓ;ÓɃzwïd«`øÆ7îuË ß6[Lw…Bá ¦p¡Ó*)rì‹êæè=ïsê¯ßjhr‡›Ý^£Ñlúï[òó£^÷ûVoþÓ¢E/c\r7iäïI2ƒÊåÅtÁW;«æL½Ãî°K•©$QÍÇcËÏî÷3”©€?ø€©.üÐßþv¯‡Y„AÐç šüÔn„)¬tuŸLíÙh¿öß~Òhµ<…†N=,|ú”Ñ4mì0ƒÓn3¥õ¶,B@(·×G›jѪê݃7×üù„ysî2uÌíOÿñwo3/¬|"Æ@±–6REåÏo·\ÿ­ï^ZTRúçP8~ÿ¯þÀ7µñ”Ï^ÙúGù·26ß±;\?:¨D•)D¥ŽtQ¦:Vÿëâ<ü‹ÿz€ïîSYbOjV@Ø`PF@G¸ûýW¶¸b„Òúúï½› €ûæLeøÁŸ2±Ðf~õS&È Wr?ÿÒß¿›©·ófá ²Ÿ¤4€˜U¦ •zE ¶LAC'óC}*KaC(vD@]¯U7¤l1Œ7~ï?.3M¿fe¾yþù«EyþÓ*9òrÈ ärã,(ú¯«oºõ f-ÖÈNãfe^À˜¨27vêFF ¶LA'C7óé¸ëŽ/ˆ¼ªà~jfˆ;ã2H¦ñòË¿ä²Û¯\þÊUóò±õ–AøsãÓ›¡KhÐÐÊß1¾Œ¹²ñwx6”TexÏ6`,T™J´ù÷”©aƒJÃÐÍÐÑŒ@|eÉ5x á}ù‡œ¾8Ž/Ó2G3*|Ó )£¾€%Ž0)@æ2#›¿ ¹¹þ²3Î<øÌ+®¸•yqðfå .¥|0,EYjçÙ €…*SŒˆJ !€2uÝ'Ï0A7CGóKâ*Káafôƒ†mÑcu” ônˆ–‹ÍnÿöÔ±•a ÉoB~ GÅåe_c®ÐrÉ'o€(Kí<»€*S¹#Û™âD–)èh¦¡WÏwðhnŠY90´$S´«ïjèÙ-—koùö\œÁ< 0ZkJ.ÓŒ€&G†ç^öéÓùSˆ Èo@¬À®ñn ÊTš…-O^9‚ކ®f–{ôLýÝ–I|ï€vhŽïøÞtå ȰœèÞp^‚‰€x€ C¥>Ÿ @Ž O‡U^Àü KAr=*.¾žíIàÕÞU™Êö,Õý²LAW3U=–¥…΋RnP^€(;Ò³ÚLd4Va&@ŒUUI!,£B‡-l±Ú`UÆzô\’e[+Kš×ÃÞ*Sɪž¢L±Ž†®æŸ0º-Ky ôš` ˜x‰Š´+fTúo.ÛH¬ÚJ-n/™LF;|0}éò³hPY½öÑzZ´f+ýö®ëПÁëç¿B•ƒÊèÆ«æÑ“o|Lvàç=4t`)]9ï4š5 Þ1-½¶x½xü-÷v›…øÞ—äÏ~ݯªÞCa^ÓsÎäQ¾»ÿÈIúÅ£ Ä9£Ñ@…NM=”n¸òöøEù}( ÑCÿ~G`qz§÷á>[v¡ófMˆ},ƒÇ¼FšV)BiaÓ«Aœ Œ´²¤ñÉe^ð×{‘oϽ»ŠŽžh`Y!PRH×\8›fOIGøÜÿõeºó‹—Ò¤QC#ï{céFzçãÍtßÝ×ÑŠÍ»O3K%….:m +‹Ü‹)oÿñõ«hDEy‡k]ý8V×L0JxŒzW—Óv. ÒÖ=Gx’JñÞd>QB^z ½õñ&ñ8†á•Ð%gN¡sNßí+«w¦G^þuÉÉb>U­¿³b3;s‚Ðkݾ$Ù š®î¶,… áoÈ%‡8,çÏ}Þ=-¦d±Ññó§J‹>ˆ…‚†”¤›$TÐ(tŸ»hM?‚[<ôìÛ+é¾ÇߤŸßþY¡ðpÏÎýµ4¾J3d×7ÑÁÚzúÌù³iݶý´œ•Ýí×^HÅNÚÉwgu.à©c4åñ›¾NÏÂFÂÈn+ÕtóÏûo¾æ|Vú´ëà zî4‘=æåï*Ápºÿžëy61½ŠTGª¹Bƒ\I "o|.q7PÇOèåW,o‚ßvÞ{¥ï`mýáéwˆ£¿éæ«ÏçÊÅÄÞœmô÷‘ýº‹i Ëó%´~ûþFÀúmûhÆøá„JóÙwVÒ…s&Ñ­Ÿ½Ôž$S7Fd¯ÄÄÜðúâu4óÎe,æ–´Vï:L//Z1Ò)óì^§oáâU1iéºô 룙ªÈéè:6jÖÄ*šúÝ® €´€ÑÅKÛuu¬¼EÊÒŒû·ó„|WŠÇØà~Þ‡»x…:•ô¬±¥0¥´(Þ]QÍ-Ô‰ô‰Ó'‹o•ÐÍלG?ùó ´dýNºðôIT1 ˜Ö±²“FŽ6«h!¿²h- 䯾&žïJ9: ’ÿ•»häPm¤ÌJ6 |´þçöùâ:º$þ÷áôå+ÏÊõ©7?¦2¦é0·¦6sËl̰tå¹3øù´aÇúpÍ6š>®’ÞæÖ—iº`ÎÄH‹üÁ Y·}™Y‰_È<^zæTñÞþÀ ‚–¾óÞÊjÚsð˜0ðÍevpËb"+§âÛŸ»øtÑ*¼ö’¹¢µ×âöÑ?^ýˆjø™ª!hPiaäsmþ½úáZa<¹¹ûÀÉ4³‹…~ð•ËYÉY¦7òøÐB–r†}®¦Xãöx,\¶‰P}õÓó¸òÖûÜÅsÙ­|œÞ\ºIð,æJŠ'bØll¡ýGOÒUçÍž¯Ï/ŒˆAe…ìU‹Ê@O@÷$ïo,Ù@«¸ ËfµÐÇjØ«p&McÙÿxc ½±d£ðäÁk/*Ìždíu~Wåà2:ÙÐ"x¸ã ‘Åd†Ë6€³üŸ=c,—§Il#ÐOáÿûãóÌû0Á3Z»S,­ûիΊùÅ÷V¼ñ$(Ä  âå>Ç¹ÓÆˆÇðÍ­{ %3›»<ÐRDÍ>Æ FîCÇéê f à ôÉ„w®Ùº~ðÕ+èÆO#žûÚgÎFR2ôÊ÷÷a/VÚå¬4¥ëÖ>ó þĪ!@†²rð˜¶î Œ€Æ7íæ¼F‚WLÇUCŠ.€¿¾øUï:(ïuß“¼O7œ\\¹£U ãdÔ°ÔØì¦.XBófŽ£ÛØº?bé'YÃw|´ŽªwFuEy1¹4sâHú>¤3¸ëâ¹wVŒòr6ÚÇÄF‘K|÷Ü0@Š•y4VrŒìOž5•>ÞÄÆý‡ëÄ}½•SqSÌ”£õlL,]¿ƒ^|o0¤Ç C,+QWtãý …ûÜÅ=O¼¾Œl\ñ‘¡¬“Žžlçñçöf òÿå·¯œ»eþùØÑã¯/%‡ÝÂ]<Ÿ¤aÜ­ùôÂÅù>ü9¥,Ý«~]¾ƒA—ÇjŸyôn¤¡&vý#8í§|Ëå°Sc«¶Ò%ú÷šÜ´÷ðq¡tö°Ò›Í†âÐ/ºƒ»~ú——¸Ð®ŽXݧ¼4Á†ó¥+ÎæVv5}mÛsT´6äë>sÁLƒð™ógú*Q#m®9 ZêƒË ¹1Xx4Ö²Oz‹[ ÿ~k9ÇJl¡iÜòTZy  ú†¯þÄìˆG#r‘¶í=Ì-¡Q¢uå¼BÊë;˜¶JîÏE×ɤQš÷D%C¯||ûôw3ÅGG&îŠ÷¦V—‹SÝÏ(02ÑòÂë1ÀK†.$T\è €qŒð™ßt%•r,ÀCÿ~—|êmQQÅÃqwòŽ0ZÖe\)c¶:”ÛM5¹ßÜEp‰—±ç‚gBF:¾Ó“¬áº×à ÿ ºhîda\ÀóqÑÜI¢0}ìp †B¢¬!ž¡”¿>z|Ý ÓÇw±A<Š.>cŠØ` -cElꮜÆÞƒcxž^¸Bx%à…˜7s|‡®ÃÎtÇ>_ÏúðB€xcÓö}Z·&â‘Æ±Np{Û„¡×êñ cnÞi8ÈFgNKð$Ò°‰ýÞ¿Wý6NFàÇTÕ²Ú^Œ½®Ž3‹€^»X}ôOBáFjæVAçW*+$û¡¥°»ŠYÈÖŽ|Q?»ívInàø‚ÍTà²Çív—ï{TâÓˆ ¬y£%(f(`éÞÃYx \šÒöqË lÇþ£Â•¹zË^qjOÆç øÎõ—Šîx~÷¯7E«à–ùDÞeÛUB å>.„±"º ¬‘&q· \•èÆcÀ/Yzå·’Ø÷›Ü%Ac_M˜§î¾jjõò=¿:ìÖH¼d+6ïÁk»Ör+xZätÁ@F‹­jÄüè¦OE®wwмÇæÊgá•BÏ/}]žÒ:yøWw²&oDë] 2mÚy»:6Pm]S¤û"Ô¼[òž®öˆ@%Û8œ –%ì‘À5™º*§òZì]’?¿ã³¢Œ£+á/Ï¿O.nÈ`ãÎtÇ>+½2#øûH0¨ÊÙ³)ÓäÑCèƒÕ[E3Fì ²GÚ¾ ‹°ðªÈ{ñìÁc ž—×zÙ ¹»ü;m»Û<¿ˆÞþó›ßwªPEoPGýŒ€^À!â-¾VK¢¸•—¸¸Ý"Ü•hÁÈtŒ•¬é‹ÏÐâpVõ†ûÙ5XiíÈû±‡2¹æÂ9ÜgzŒvñFZ7iì-]#X …/Š~{éj½]=¥®òЋVÓ³'ŠV{OÏ÷t ­,ô ¢E›(ÖUÂô'¢¯U¦:vUÊ4kâz“%x ÆTVp·Àt¡LÜtL½ò;qìÛeLÊZOdç- ñ x+çŒR´ìeÚ¼ëÉ çP1!pöÃ5ÛÉnÕâdä½r(-^`Úù}òžØ}oò{/â`ÐJG¥Ù9u'kò>´ìeB7ÑÝñý¯ ƒ<øŒ¼Üãr /BmŒÛ:º×dꪜÊk]íaô`4èÜsèXĈ¥»ósCØ3ƒ„®ÎÜu¼¥Çç/;{:ýð’\Â]ÓØãd·i:æ.6ÚF´7*Ä…¸ÿ]-eM<µÇïý6ã:²ýÇK¬ŽßÆý:uc¿ -Ùýò9ý}a ÷½½½|3aøZÏã~Lx T#¢ùÑrÅ(Ù€k°ªÑA‹ •¸,ˆòÙžöãF a#€hɆ¢ÿÊ4Ui&÷k®¨ÞÅVþÑÒýñÆ qß‘¢ÅŽá#8À/Þ4qäPB·†¡kbkÌæjƨ•ûYˆ‰. ´pdJ†^ùµO ¨,š¹Kàïë‚`¿Ã˜† 4Œ¸‚»xd’^2 £•]¸™[Îý⨠áVÞ¸c»Ù‹:ò}ÙÃ#‡n9tI  ßD¿7†"b>TxrL|O²Öù›hñ7q÷< ˆz{ù¦·à»pÃÝŽVç„Ñ?†¼£‚Åñôq ÞùÖ^BAt‚oÑý6¢=¸·‡‘…ì‰DÀ3tÒ»Œ <‡2½ÏC¡ÇVC¡§¤1?šcнò{BÀ'¾ }–hšru¯0øÿäó\ÙülÅwÆÅ熔©}Úè¹y™öÏëãWœ3ûÛŒôÎòjÑŸÃ}e·}îBÑ2—TÂÕ‡n·h,S[Ü 9ZRp»Á ~ù9Q%)ïën¨iŒŸGðßóì2ÅhÙMÑÝ3ñžGÔ>‚}àþXxàÆ_Pƽ¥G_þHܾAúMãMð>@c¾ðw&" ¢Oó'ß4{¸?}þLnLœÞxiS÷ŇúûïúÒe"êü—œ0T!7Ÿ #‡tx ¼dV5ŽýˆBGë^2´°ÑOl:Ÿeëé…Ëé®ûž¤¯p`éÙl¨†P_å ¼ç¸%Ö3‚ó«Ó“¬u¦ãë/e9DwFœ?{|‡x-ô㇞##¾Å£ bÓu—žA¾ò‘˜/#È]ˆw¹öâÄøEüÑÿ>òªð"`DF_t5ÿFì÷cÇ3o­äQ»9Vb¤ˆc×Qž¡kÐÍ€.8Tü¼nýì¬/–ÑÿûÓ âvà7å¶ùŒ©|:þ}0üOŽSÔ‚' †WáÔ¿Æÿ´º³¿H k»'íÿù?‹põo?ÿÉØ'‘à?sðVtÓ÷ÿó•Éc«æÜsÃeI¼.þG=$^W}½½³å!V Ñ„¾C Œu½&ú®ÎÏÁ%úÔ•Hîü¾xO2¡Eòƒž¥ë/‡R-Œ€yì9”Òßûr„¶tÒ{ÿã iÖ[Ÿxð7w2]Gy;ÉZ(ˆvàòH‘²Ä¼ p¤âËwþàÁ“ÇMêK™BWUc=ú⦗ءEi2±\·3”ç“Ùà ñ°7©ó¸yÄö º†|¼²Ö™ðŠòß]9t³2ÝŽ@9†ÁÝÕ¤=¿•Î߈Íñ3/±y¶aÇ~z’½9?úê•bØ1<)}aé:ÄD_’9óg¼ú ejK;ÕýöçŸáw4MüÅŠ¡a‹sŠÂo0˜®Ýþ½)ÏË÷dzOa}Ïçòöå è”õ±V§K½þŒ·Àt÷¢Ø¾ÃîîIô<ZW# }_¼ÏuÆŠ 1º¬onnÜÜ"ÁÌ‹±ÆI¦è—¯|»sL$ª,zê¿NGÈJgï‚\¦xeMÞ/÷൧ÔÕwcïOg9ŽýNoÇ(g±€¼?Ä­ÿzV Có!ì=|B ‘”×±OF—•=ÃÈÀ£ÒàvÍò¾±´¨ãô"h¹N/Uêí9‹ÜÂ_¿úò^eè,Ç1ýéó¼j›J …@êÀÂ;~ù/&lëóKßä5ñ0`•zF`wá¬J2šÆâ.6Z¬¦ð j¥Àž1ÓÃUeè!bh@Ж÷M$anƒ#ÇyT=£Èi‡áp‰$Ĭ` }o)ŸËßî@·WE@ ‡Bwoº{úîÞ0S×3€Š È|œB†Tý›ÇAc˜ÎE<¹\cµ|‚W4äFXã¥eÂôŸ¶à–Ž_Ü`Ÿ¼º˜g<œÊ+Ž•Š9 0éÂÌc‡ØXÀªžøhCÆr¯½EEËo©½B [ØÌ ­âE~0gÁ9F.`mŒ¡<:eOø…I€°èNlÚÏf!ȳèá^ÌN¸§Ømàû9“Gò6Šƒ\O-±ïÈåãMµZäe1|mÇg>=¡ŽôŒ€òè0w0Ï%¯0ˆg(û壯E\˜y1 ??÷>/Èâäévw s­£¥ƒL ”0f@CÂLjkyå>žÀC Ë{îb!¤³x¢L’„URä:k¶îså/åeˆðrÖH"‡5 0Óài<Ãfç„ë‡ÚWMÄñcl\æ °&²!ñÈËŠçº*ß“‹¿´„èö7Z)n¯JBÁ÷ê×oËE^s•'å ÐaÎb¶Ây§iCv–ò ehuÌäùö‘PÉcf=$Lé‰éM± \ftDÅÑò7ôð™`éaŒ%Æ p;yšSÌú¦’B —ÀtÇð¬a’L«+WÀ”À7ðìƒHXçb/Ow„®€Î 8¦¼FzséFÚÉ«‡žÆCïâ-âæ,ÿÓêÓm¯µÐ wHpbùë_ÿÊ¡ç~Ú˜å¬åùÊÐavcA™<{a‹'ºÂ¡ìÀõuܪiæ%G‡ñô®ÒÏuÞcYØØ4°´0²4©…×O´¿4öêX! w0/’(WÍ+‡]Æ«š¸Ÿ ‚ذ "ÿeÔó9o€M¦®<[ª½B 'Àê‡÷Ý}àí*Ž©‰-W±e®+æc¯ÇãÞÿ¹}~ä‘Îå/r!‡~ÿ1/-½6ÚrùX+Ýu¦ƒîß™CLæ+ÊÐq†Ç*ªxÉìj±5ô/^ôÔ}ù€@"å*^\º*ñ>«÷ûXî¡¿­z.m¥ß\âÒ;ÙŠ¾^Pݽ¤.+ |GàA6þº&j\4ÊB¿ÿ¤‹ÌªÉzÑPY˜õY¨P(éCà¡^úKŒp!\V  €ôAÞ¯oVÝý ·ú˜B@! È~½ÔMÿX¸`$ åÈžìReôŽ‘ºC! PämÁ0ýàÝVz«&: ð¼* ýárYÚó fV9œ¹Š5…€B@!ÐW½!ºƒ§^s$yô“c-ôÛ‹•$‡”ÐCfîã…CªyjÞºÆVjhq‹1Æ=Ü×% Þ³òb$%N*/vÑ”1•ba’¸îÇ›öó´©Õ»ÑIžB ¥´ñøj$ž“(©”-ü'Ťz¸Kò]¦²AŸl Ñ74Óž†è,ˆ_=ÍF?<ÇÙežª“Ù€2bòËúy.þ÷WmåÕª©±ÕÇëb‡ÉnðÍÐHfŠöÅ<ÖçÃÙhW¸˜¼a½òá:*.°Ñ…s§Š•ü0…o¦æïﮬ‹¢`³ÑD“Œ¤Í= Úx±d ³»1À‹ÿ¢]4wrÆùOoêÈg™ŠÕ'ï­ÚFÍXÌ‹‹QÀ`'¯ÑNA2u+Á_&ÖKöÐ 2‡½¢<rã⹓ú\ž6 Ðm¯·ÒÉöµPâ4ÏI_™aK2õX6 ¹GGè °bþo´úŸzs)·ú}4Ø\Ccx¿¬†èИT’ݶSm`ôΠ—Þ÷Ñ"®|¿tå9Â;`èjnÒT~¼Ó»ªy‰ÕÇ__&ZýX» ¼¤€v+ÓHGˆq÷xÛxùÖ6æ }°r+}ùʳ2Â'8ÔÏ ¯2«Ožxs9Ônª· ¦Zç$:Éû€Á’tO}…9ì§r- öåé}.O7ÄYžÞÞå§½×Bžö+þ÷ñ—ŒI­§R¯Îd ¼7Pùƒ!z+`Ñ*5£yÎ4À¼/íyãb¸eƒØNªh“ç*úÓ³^šáÑ2Æ|äý‘°šÚ ï­k«æÅQ°vz$.‡Ml˜Îµ¾ÉÍü¿ÏüÏîWþûƒ×|ûF¾ÊTG}²žZM…´£àj4H»À¸¨µVŠ­8p‚Æ{6õZžx6qúO; Än ?_Y@3+ò¾zH{žéáy›ËÒZ‡»À«m aæjšéx‘̆hDleŒŽsM¥užù\!…Â!véMá•þØŸ¦Ö80ïÏ¿»šœv•që?S¿0<ñ‚Gu - éþû+oóé;ù*S]é“–a´Õ9“B†Ô¸ýû"G0:ÖœG“Üëº-OG[Bt÷ÂVZ_ Ql¢¿~ª€F–ôO¤/<©{Óƒ@^æ´,°èÿ߸s?-X¬§;ŸÉˆ ³Æh€1òòkiÓ΢›ô¦:áp×¾ðÞa (Íœ yƒ:`¼üÁº´ò/¿©ö©C _eêT}²‘`T»ædÄ9 ã4€–ÎåiÉ~?Ͷ©ƒ€i€_ø|¡2$€y²ÏK#.;-­zþ•Td¬½ä9¼EÆcôï…“‡ûËAoª¢ýŸàÌu€žè±ð‚GO¿µ"müë‰ß\¡%_e*VŸ<ûÎÑ€^hi5ÒS W›õÉ–»y@ Õ{´ÆwÿÓæ9è!ž ÀŠp@•ò ¼3P`Ñàkk£E«·R£»¦Ù^˨ ³ÀÁ#0Ͷ€xtÂû«·zSià]ÐÐâ¡’Bgƺ:ó-Ã#PZä¤F¦/üËï¨}êÈW™ê¬OšÝÚaŸ–Q@ç\…G`‡c5q£ç;¯£ÿ[ÙBaînDT`¤ÇçÒM3ìS¿ó¼2à¶C`—–oÞ%FôG`_å 4a„ÂâuÛ½ ô'›$ﳄQýØWºAèûhÝŽ”òßW:Ôý½#¯2%ù–údÙæ½b@öž+ïMuæÁ®ÛÇÃsýæ†ÀÙ•fzùóE*°#Ty÷+¯ŒÍj/ÀîµÔâ P%Gçë5¶f·Ÿö:ÎÞ€@Jº€Á^~_s«—£ò­ze]ÐúZܾ”ò¯k†³”¸|•©ÎúÄíõQ­¥R·¹xŒGØyNB=Ý:ËÌ€N*u(÷¿n3¬ŸË# bµûýäåº}ßQ1æÐkm˜¬hëžÃÔÆt'ë Tï>Èï%1€^y]˜§t¦Š=óš­´å«LI¾Q.¥>ÁD@˜@¯IÐÆ4~qd}mº™B)ò.ê•_EW|ä™ä€@¿è ¨ç©pmäNÛD@ñÁßó]˜GÀfp‹©{Aw0LªK@S\A:ÉÃðL<`:'꙳ø®‚>Љ©‹SÁ|_Uwõ|•)Éw¬>ÁL€éš¨/yÒݽ  4ÚÃnUžº)Ïç×*QD0{}>v³{ÉnlÔ}–ÛyºâFèÝ |$š$XÁ˜© úH<èLÿ}ü´º=òU¦$ß±úÄkÐÿôº^£M•§8ä:ŸnÉ##€çªo7|¾6òó3µé>¯Ac[[@ ?J<8Ïâ>~ŸÞ½2c@gªø—ïTûÔ!¯2%ù† õIРÿ¹×„ÖK‘>I©7e¼0ຓî;Ø!š7™Ê´¿3,ŒÊ›é–Ý९)ƒDžïë÷Ry*øO%=ê]ù*S±|ç«>Qe wÈ #Ù%Ýw¨H1O[Y“‹P: ;UÝYg¤ˆÿ¬Éð,"T–«|“)Éw>ë“,SEjä… -wÔûˆ°q…š=&Û+üOÛEójô§]^’σl2€ÀL*øïu2)òU¦bùÎW}’”ਇu…@^@ýI~WöU¾=píŧӉÆVÇo ¹SFŸrë+‹ÖñâDFš=i4=üò¢S®« X01Ô×^DO¼ù1=Ñ@N‡®8{::€åÈL —m¤µÛöÑõ—IÖÑâõ;bÏúã«ÆÛ莹vºâÉFWf¢—¯+êÀÓ·Þh¡ µAzûËÅôù盨¦ŽãšTR¤¼0Rm©—™CåÃçÑæ÷¿/²`ìÜ{¸‹¡Þx&• žIs®~’Žì\@Ù3÷N:ñ:¼íÅd—öŠDøIä™î®TÆÊy ½¾dYÍfÚ¶çHäÖaKè¢3¦Ðáõb sNOÓÆVÒ¦šƒ‘{’=H%/ÉÒ’Ïϧ2æÍO¸r‡`àa¡7^yŽŸ×–¬çi£Oˆ.<`½„×’øÂ¥gКm{ÉíMÝß¾ðÒ—{ã‘ Òwži§?®ôF—ØyZ?N7½ÒL<šO¤] !jô†è…­^ºçLÝÎFAªRªùI]ê=ýƒ@^€‚Ý’wôœo‰Ö>ZýHVG5×m§@[‹Ø£Õo¶ð"Ú·þQ3çÛIQÚ÷(Èw$Ç=ÑÙ3ÆÑÆ„vó<˜|H¦ÓÙ+°ëà1ªoj§VUï÷'c«k¤¿¿¸ˆœ.ÙìvÆÖ“%ד5aÜÈIÿóÇ?¿-éη½—g üÅ£ ’b±5~Ïë«qZ­‰u‡a±¨Ù“ªèé…Ë=SFKI?ÃË|#ú>6¨­£õ-4{ò(Z¼6¹)¿eyÀ¾¯)öÙïð¹+Ç[Èf"Z°]3jJx>^Ö„–l·bî~d>¸±˜Æ•›hçÉŽØÄÜÖëa,ý½Þ¬nÈi’Ó¤9 M÷̘LÍ'ª#7ìZýUNú”ÐǬ1šyîí‚ jmˆVtõGVSã±4bê—ÈQTI6?ÉoÕJ´[Üg gqU¿¤ÏÛÍ\™ºìT×tª;röä‘bJÒšÇ"Ä×µ{J‹œ‘sê@!‹@Y‘‹ÜÏdé§‹œvűƒË‹éX}3]4w ?{Bä‘úæV*+*ˆüÎöƒQÅ&ÚËî~™ÐÂÿ˜½ð0\:ÚB¯±ˆ.e‘—iß;¢Dë2ˆœT Pæds OxšGžœõ©GÐß@;Ç}Èäã¸x d½¹$Oµ­!äê¾e¿«>D#‹£ªzpj[5O£ÄDí‰"•¬DßgÏýnòqëÞU2Zpð5QóÉm4xôed²8©°|¢Øê-×]ţľµa¯Øgû¿?H-¬´Ë‹]XA@`õîüBcLjm¸z‘ê£ÁƒT?ò:^*Úå´‰¡€£æ`-U (¦™ªh`i{†ÓÖ½QϺNòðÔ\I{ؽ?²$jä\:ÆBÓ›©”/á㋹K`ùAÍ žGr÷Á¾†\sXåJnfª; èí6zéƒ5½Þ“7 ò_FÿwG;L—>~êüÝݯÎ+âA o<˜Š4ºONB Ô/Lè}¹'¶‘ÅdŒ¡=¶­Ð—7ñ…v ,<œ“‘dC©â?øÍ6óU¦$ßÚ^Ó'¦pt˜Ÿ^óÑÌ4ªò¤×ÜÉ ]ya  vN6®X½¡hp[çëzùí ‘Ý]]ñÓ½±Ï¸x±–½Î†:SÁ6ðšm4æ«LÅò-ó úÄÒ¿›4ªò$sMí@^]eµÃb"9©-Üq¼{W÷fêh.kj{mŠx8V0Ò½7^Йjþ3•Ÿ¹üÝ|—)è —Vs8:”Ooù ÚÌL£*OzË™ÌÒ“WF,x¹•¹,<ÉOºˆNGšÙ¬8õë  4V”º"tŸzWß΀ÿªŠR1%©Þƒ«@z-RÉßÐRwǃ@¾Ê”Ô%ØCŸ`¦ðr¿6™X<¸õ÷=‚6UžúvÝ/oŒ€Håø[¬@Wä°Å¢ƒþºÍ$Ðfc'@y‘ƒ©Ž0‰,1Ä ¶ØÙ»Ðêé8±O¢ïM×s Ïʳ¦ŠÿtÑ™ÏïÍW™’|£\J}bâ%ûêV@âTyÒme„°¼0"–CLÜwgä±ÇØbë½60–Nª2~OM mD™+B/B$/==ÛÕ5ùÞÞ'U–‰Ùýä¢-]=“És  ãÇ«Ê)á?“¼äê·óU¦:ó-õI…‹'òaO@qà„î²4¶‘evUžt—;™%(/Œ@ kËÙbÃb7f³™†ZÈÊÞ€Þ«(fwžNhM6S˜F*ôJÚÁG¢)ƒÉUy~v“˜Î7Ä.B=%ÐSdzÈYy µTò¯'s…–|•©X¾cõ‰™½ãÜ›ÈÖÏŒ~ 4aT€*O¹RòRÇGâ5JêhHû›¤å®f²Z­¼±À.ñ¡¼pGSh ­óÌO;ñ~´4…ÑØ²ÛA§U- _òï»ä}ò9‰Ãn£™#ËÈP]CtÅ2y&÷u<Á?ÀŠk€-eüg’Ÿ\ýv¾ÊTg¾cõI¥‹YCM4É}êÚ™’Кưªò”©LÐñwõl }Ê›!%íT#[蘂•¿Íf%W¬6›Jfh Ò¡ÀZåþBF=ð€ÐRYh¤Á<=ªF§f´€~ð‘hêŒÁðÅ4n¯åîõ‰e~3íÀ÷±Ü°›ÓÁ¸µËäLl]Ü’§$rŸ2¦òU¦:óÝQŸh€ÿ0Mi]•Q< ´ôOyº:å2–2aU/êÔŽ=ëò „ ‰ Ý^¿¿¯ýøa龃`çÊßÎkÒc]zÇNƒœ~^À¤ù'ó¥hºí5`Þ§ÐO€.x%*œa^êôNÐ ºÑЉ¦®0?´„ü¼TëÞú6 œl R^õÏÆßêï„tÀ.þ%OÞ68DyrÓˆ”%Éf ð¤¢Lá}ù*S]ñÝYŸW¾³Zši§c:5šHøûetÀîò†P¦x¨Ø¥O‘»~a\}¤ÏèÕˆ0ò54ŠÅ³“2àÂC%jáuÊQX].'ù¸ìóù¸âñÓÀ`«ç{20€–¸¿NƒÍ5TiÙÀûíd5¤gÌ€a€€ @‹!HÃAöðÜ.0 ô‚nÙ§Ýa0¶¢ˆc#hO}€°B =%.‡•<©‘„iJ˜Ã1 A€fnH¤“ÉFCSkØïóÕËßù¶÷{}õ©(SÀ-_eª;¾;ê/çIOkYJõæÁTk­¤“–Á0¤ÇÈÆ<8¸í •jŪ„C´é“Ørƒ2 ŽÇžSÇÙ€Þ(ëÃÍŸ.k'÷c'š´Bkä¥IÙÀï)p¹Øh£6n¸õ‰¥L‰<äàsõ~# Œ³+&¹Énlä‰6R3¤Óc¶BL„yÌ\ù—™Ûh ÃÈ•¾]ÐVXX@®—8½ Qýà#ÑÔC—¹¤>È@IDAT™6ø¨®ÍG'|Êh@@%ƒâ}ªRˆ' òš©Øcc\ffC,Íüƒ~ÈäÉãnE7Z+rÃå\N’Ï0xOE™Xù*S=ñÝQŸx„>1r¥ŒŠþL?kŸÑNACjÔ/¦+ÆL€˜¬ÒŒÃ’~Ð'²°È2]Íç g*e©‘Âô0,”VíÁýKŠÊËïÜTsˆÎ˜::©/Á…‡Qè¿C nð ¯s¯Òµé%3»¦Ë‚>ò°ï ñR¦ÜWïe7=*ìž’§ù`ä²£°2r{ Õáa6(ˆì†6*°„Éa1w?Zü0NŠ‹ ©¨ˆ7L'èÝ ?ÙÔbè¤ÇKÍÞµðägml†HÉF¥L†¿ «6˜ ¡~çræŒ<°sûz&–Ÿ³ö=ïr2ÅòïÃFùB*ÊÐÊW™êo\7™¤>qÇèîz µôªOâ‘ÄX}’‰òe™‚®æŸRÖâ!_Ý£ôjHA /zíÅMc§ŸvrUõî26z®…{4b½s¥*¦¤e@VjZæî3Wø>/Ü^çÈùBŽVƒ‘ÀG/o'Ú¾îÑÈ=•gÿ8rÜùtà{R‰X,fvÁ£‹ÂAðÀ().Ç0 }œ¬@Ò/V«—Šî;üÇ‹üNOûLòÏrƒM›V}\Ã4b¶ˆ¼õDw–^‹åMð Þç^ôÉFÆ¢(Ù2LòU¦âå;Q}¯¼e²e  å«LeŠo`žéòd™‚ŽæŸùP–ÀvN%½Òš„P>zåŧçßzÇõO/\^ú£›®2Zy’›d .*_$iE#ðN äþw/^ ¶·„ƒ¼’]<-áG”ªÒ’®W(”ßCË.~Í  V€á€î§Ó!6tà:èMuJ½Ñ™)þÛüAbù …¦¥o¼ú6Ó‰l¨!gRæø0§’äK”%æLð FŒ{)cR”Š2ÄòM¦¤”dŠo|?Så ß–eЇÔCGó©\/K`;ç’^Ö „ª­®îXóöõk~c˜uú/ÿµ`Iøæùç'Ý- ®(DÜB×ZäVr:ÂAƒmÜw‡ Áö¸éè®g Ø²µ4p@¹<ûv'@¤ÐbªQÌ4W?Œ „7A€¨üµ ‚4hLu’Ê|§ƒîè”,Èïö7ÿ›ÃÇhíÒEÿr»›1;GQ‰M¹î eIò €…ñü‹¿•Š2…|—y›/2ž‘ú›oí›âÓ‘o÷wyÂ×e™‚n†ŽæSùP–4àsè¯Þ´^ XÞÅo¾º¤°´ôQ>¾™^¤ðW®šgHÆ#€‚+‡ÜÁ ÃQ!ÃÐÆÁèGà <aüãÚ_]É@iaôìàŠÑíGø¦Ø8Dž|ÞÌʼnŠß"¼ÚHЕΔ z¢7ü£µeµzëî-_Z·äÃuL#æÀ†±Ÿ/ÈY®‘²Ä¼ þEéÀA/óïù©(Sü!ß©.Wxow)2Õ-ý]–@C¦x-S‡öÔ<ÝÌääKYê*û³úœž¸1¡¸°@7”VëOýóéË®»4åÈÿgï<£¨ò?þ›™Ý즇 t^E°aEEõ,wêù?=õ°¡¢`¼õ*ì¼Ó»óšèYOÄX°ƒ!ôÞ!ÒË–™ÿ÷7›Ùl)»Énö÷`2íÍ›ßûÌì¼ßû½ß{/÷°ñÛ1§¨¡ðà“9Èh0 8ú¿ó Õƒ 6% %È  MëV³jàûpàÂ7ùci.ØgeÀ¿ïw´âVM!<{~yBà6 ­<5Uþ¹½’›Ø°9{Õû‹ß{k!äãMØÀk~¯øýâ÷Œß·–jü–Q'ÇâwÁÄ Ö6ªßCl©ïç­¶ÐTùfšú÷Ä÷ þMíܼáo ÿó7ÄÒo‰1´¨ J€i uþp;ðÒ½yâ¨Ñ{õ“FÞòÔ Óõêlœ8°§2¸W§#À?&.€ùÌ&z®•³# ål¨M €5?ع¯z°~´¼¶¾§å(h«~]Sìó½CÁ 6Yù¬|ò:Ôùç>ËÜe‰=–Ù P÷z —}µäß+¿ûš»ò;ÄfK^ó\{‰% Êo ù7{j²r”»ß¾gŽº¿©ÔPü¦¶ùœ[Â;Åy©OhŠßËÓ¿'¾OÍß”·`Õß¾üӒϾÂéXû-1’"Y `Ж_·ß²¶ÉCmixù–®ùñ‡u#Ç^x1ÚëGãcŸŠ„‘ï0ÒR§ïCªøÕ÷«ßb@ÍWþûeõÓæ¾õõNVß·ŽGʺzž«ï×WÎêù­¾_ßô¬ø< .‚ǃà@FÅçón[·æ«ï>Y°¤´´Ø*øóŸ—,ü>ñ{Åmå-¹)Ù3ÃK8c¶9AIúyÊŸ7ž:fÜ(°;¿©<›ÿ¦X’êïPõ}SÚzü©þU߯GRaZ=ŸÕ÷róêy­¾ß4­kªÿ¦¸kíŽMë?ûöã>,..àÁ¶bõ·d!jë b+"óc™1¹Ã+î`ðx õÏÞþ÷|ìÔïøá}ºöî;(>!©m\¼3Õf³;𥠉"€ôëu¥žVä«×o±¶e~¿¼¼´4¿´¨èÐö ëÖmÈúeîʦ~®ísÏŠ´ò*¶¹)ÀòhÉVdÓ Gý-ᬩ @YòÀ*ðö?í3xX÷n}úõ‹OJjíˆOµi¶†×é¿¿ü&P½^O¹»´,¿´¤èàŽëW¯[±|² ¿¥hzŽu5Ò•ÎBp Æ*؃?håx9WbY‡¸¦¥kVÌÖÖ5Ø o¸f¶ò‚u‡¿8g޵-ë°° q~Wx±|I¸¦Ï…½e²d +±f@–ÍpÌßbqGW”¨5XÖc»Y~K¦´ò§9Èo©9¨7ã=£A `<üñâZ~I­}þÈsM?\Ü"ÏJç‰-üñj2÷ªbv‚šê šhßz/X`¿e0J±ÏŠ@IÅÂï Ÿç÷(Ö‚õÛá|[̘E$ÿ–bí5w~­÷B~KÍý$šàþÑ¢X/%¬‚·ùÃÅvÓÓkÓÑ ëêJ@s(2£D~8Xï…õábS¿U°±"ÀÊ"ïóqËÀº‡b&Xœ¢é·3§™3jý¬wD~KÍü@šâöÑ¢0 ~1ù¥ ~A­ [‚Ëw€ ÿæPp[ÚÇ$4 ~'¬…ß‘à¿#\ðóš>‹d;¢í·\6ÂNÀúYïˆü–ÂŽ¼yoMJ€EŠ?àü‚òÚÍjàÂßRšÜ'÷¹Á;²Vü>p°Þ K°>`Ö¾uÞ[þZ<"ý·$OªéÈo©éXGÄ¢Q `p––j}¼¸†g90YµË`­›8;¡IhÖ‡‹×ÖÂXÇšFšèº ³a%)’KÑE4º¥å÷ƒõ›áµõ;âµuœãHh¢U °Ð[/$¿œ,À¿×|M|n‡–Ðtø]°‚õ^Xû²>6‹Y$þ–Ž-½Ä%ù-…’f„§íJ@u¼Ö‡¬úñæØçÚ•!­"é·­ En!ñ¬vóˆTB@! BK@”€Ðò”Ô„€B@D Q¢æQ‰ B@! BK@”€Ðò”Ô„€B@D Q¢æQ‰ B@! BK@”€Ðò”Ô„€B@D Q¢æQ‰ B@! BK@”€Ðò”Ô„€B@D Q¢æQ‰ B@! BK@”€Ðò”Ô„€B@D Q¢æQ‰ B@! BK@”€Ðò”Ô„€B@D Q¢æQ‰ B@! BK@”€Ðò”Ô„€B@D Q¢æQ‰ B@! BK@”€Ðò”Ô„€B@D Q¢æQ‰ B@! BK@”€Ðò”Ô„€B@D Q¢æQ‰ B@! BK@”€Ðò”Ô„€B@D Q¢æQ‰ B@! BK@”€Ðò”Ô„€B@D Q¢æQ‰ B@! BK@”€Ðò”Ô„€B@D [ÔHÚ ‚Þ2ãÑá>Ÿ÷R”®†btTˆ’kãëâmâ(‘1az÷/Žç CÙƒóÛ5Uûäå™,?Z\9.„€B \D ¨JV™¦ØQøªj¨Ó훌.¶•ÔÞ¶^‰SÊ´?S™pNÚïíK;½C;ðôz|wÇ]7Þïºñ/¹>Áý`0 „€B |êT³ ßí#"ef`ûÃýÝms|˜¬æ´?=á/42þïJûJ‚6!9m¾ߋl¯j¶ÿŸö§É¸©<›°‘—„…€B€ ÄrAõ®áÛ¯¿wúÝšf¼³=[93ažÖÖ¶Ù4ià{ž™8Wël_g¢`¦—ܦ?ö%:oâ:뺥”Ñûbóx—WÓiWŠm.¿%! „€ˆ,±¢„´n•1‚Út9¶ý2×|š½N¾‡lö$úæ_£iÛŠ¿ÐàÑs(±U/Úµö-²;S¨c¿ËBýÔCšŸP 'é ! „@tˆ%€Ÿ†eN·Ö ~B=GÜN»QÀ»K‘—HéÝGÑŽ¬¿SqÞVÚ¾òu*-ÜCm»žA†î¥í+^£ãFðD€þܢ4¥\*„€•bI ¨Ìu#·’۠ƒÙf*†Ïcö6Gй¯¨v²Ù(©us??' Vž¤jŽFÞU.B@!Z…ø$ÆŸL“¿uiÖâ)´eùK””víÌzƒŠo%wÑ>ótYÑ~¬ r&w´¢ËZ! „@D% ž¡¬˜ u2­Ö¥>w1m_õWúeÁ:¸k)%·íK‡÷-7O;’Úa­Py±_)°®‘µB@æ& J@=Ÿ€ÏSBå¨Ý'¦õ \i‹K"öHjÓ—Žó"üÖÑ¡Ýß›çS{˜ëâ¼mø²!„€B ˆO@žBÁÁ5”’>(påq'O¦Ç'¶äìXBÙK œKMŒæ-¤{ËÇdC! „@$% OaÓOOÓI—¾I›–=KîâÚðÍc´>î’Ü*©ñ‚݆§õß̪r\v„€B@DihÀSÈß·‚ölxºôÿ­yµ¡{j(|¢C¯ Í{7¼ß€»È%B@! ÂK@, 䛽ø¾c^¹gý»Ä‹! „€‘H –,@`ñú¼¥#Ž÷#:x ‡áÓ}Å2 {D , ! „@Ôˆ% Faï.+Ï+3Rk´'Wf¤·ûP5¹"^îjòÊ®B@D XQj /+)Îq‰ŠÛpÖ8)X6SÆÒÿà‘"˜È!„€-‚@¬)“úŽ ëVaGÙïí±’ecwnÞô„´dXyE0! „€ˆ.±¤X…(¯õÕ˾ßìó”çïôåýˆ ,›îu^þõâõ0Xþˆ”W„B@è"+J@pêÃ#2—[6vÀÓK9èíqOebÙöìØñ¡%/ÖÁùˆ8™E ! „€ˆ.±¢ðSѱX OñçýfÁ‡ŸûQ1Iz Ý›únÉG‹Þ+--.ÀX6¶ªðš—`K€("A–K€Ë_±·= ´Mk’m¹Hš³*šª}âÕ½³ôýBÜñG½kó°ü¸-Ü%@ÃbùFèee%¾/>x{ñD_ö8´K·>}û$$¥¤9â“m¶xV¼^O¹âÞ—dìê®nR•²=j•N9@Ù/€OpƒüŠ…–“åei  B@´l\†©6”)‘SÃ0”[zìxÝðž‚â¢5>Å©PPR!›JвK!}>Î;5›sÕ+®©"AæºÊ’šmðÍÆOŸ¹S!eõ¼ÌécƒGÐ6ø\øsS@–,iXørT">ÇqL Ö|M(Yqm^ÐMívÚ`ã†8;%ÅáNNØ$ÊÊiÑü%Ú#»s½{Dz °%€›D  B@´l¦g~ ?­A¯fÎèÒœ90-ótC1Æ«†q¡NJ¿,ŠaÀóvrëü!/3¹"YQ> QœŒ•¤*±i­þú’ë6¶äFteÁfftü´Ì)° ?õ謹³¦¡¹ç‡f)\èsáÏÊ+¼Äc1}°¶,–µ 1Ì,S>æ¼øzf¨©¿:͸3%‘º:qÇx(qq´uÃ.í–gç{@K` B@M`â´Ì3u2¾D¹:õÕYÓŸlŽÌNpÍM0¼9óÈ0~g§R_FÜ:-]ÛBéÚVr(…¨ü[ŸrvÒR $Q±Þ†x{Ó^O½Po«BØ«hÚµsÿ4mQõ<¸\Kl{hyšâËÚÑ­%.—Òl¼ÆhÕóeî»\¯;÷xw­‡õ$O±§Ÿ:Ï5±äˆ›÷ çÛ²paŸP±°ÀJ+ÁÖK/Ó €ô¹Pg¿'=E3&\ªßšÑš.dk€ Ö0||ÍÖ®™®¿nãæ€Ê·;„€-€Yøzr¾C!›ÖÑÖ¹¯ËuW‚š4¸\†º×3ëS("çôs,QzÅ-%›Â†Øº‡\oWú¥ì_±žîƒ’0ªUŠ=« Àó+]¡‹Ñtpºn¨ðIçr…ƒK6)êw(•Þšûðƒ_(ÁZ†?NØþ†¢P«!Ü„é3ÏG¶‰¿37sÚUM™¡Âýç6ù£Ø5kþ\ð[ +|œÏWWÂÍ*Ä«+ì¨È…|ÙcÕ ºu0±k”dÃ5H§ª”eÓl7Œ™âá1$! Z$nwŸ8}Ö›ø@^Ž2pܼÌŸ†+£æÎµk» ñÞ¸_|Ðã°¨pCØ #m{ø#Læ|ºÅqG­†á ÅE“|¥F |Œ4(©N¥Ø×Ö¶YKRÁ¢P„š ŒÊÓ;¹Þ.ºÏˆÓ÷Š¢ÍÈÐüGSXR˜Õ‰È„™wºñ4üÞ"{úõj༰"À…<7𿵶>Ϭ¬› ¬X [,k+¬jºçÜ×õ¸Nž—4…NW¬§£Í+¯$%%Ï8ãÖüHëu±%! N€-äÉù+ü®PTeò¼™ÓŸixjG¿r’kvçrŸgéúM(”áàÇŸ®(^þ.“×pð·žR´ï9‰Ïó÷¿QauÙÚä>2l먷ãkj­AÇ8JÐ íõöãøúa_'4'ÐÏ(q®„2´ù(—„ä°UÌ„$±ê‰øzšÍj¸KÜÁ>ÌÁRø%.ü-€Ïsh 3óECÜþÃÛ–"`)æ¾ ö¨“þt¢< ”›'üA¡ƒóqSg¼¦(®fkC²Ä‘µB ±ü>ô<Ò¨¨4% €iexhÖmdè³Ñœḭ̈¯U:ÙW)iênJP¹3–?@  b£9ÑîïP¸cVã‚׈£r#™ÕÜz%´Û3ˆV”þÊç%g‰¡©W½ú§i×+zDnLV§ÛpÓ"¾ŠÂ¬+šbù§š ~ãp~˜‡¥p¡Ï‹uÌbe­qªÞÁRxm-\˜[‹uÌLø“'œ=|zùË88¦ÚV*”)¾°½Õî'»B@àqüÝß¡€¾VÏH||8šæÏŸ¯}¾jÃë¸Ïµííõ¡ŽPUü!ËTˆ*A/ÄJÿO/ðexÑåàBô”ø<Ä·0“kLVgyØYp·g÷íHèN}ñÀás¡ìG׋= ¢»P I#OsháÀ^Ã6Ð<Hf¯:%ÒÕöK›þŽÏz9Õ"öY„|_Ǽ5åço9àëÉ IB@D4|ü’`îȃÊñ8¨bíBYðl'{§ÂåˆnëC¹óûŽÏ©ã«ˆæS]8ö+XZr£¯À×Ám³Û†¼äz`Sõ8ݯ[)ÔØ»]ËŒG‡ût×l»A è¸{ž„c°‘Gàü¬KWû².yÍv+ë’\_—CÝ£¶ï÷öâq$! "’”€ÂŠáå·óàr/Ï| ¬Ï7Ϙu½O×_ïïXD}_F$“c ÅÝßéõéŽo_Éœ~v¨í›\ 8V†å|í>&±½Ç]:V‰ Фٱ2(´D5´Ycïó.ª<([B@Ø#0Ù5§u±·h{m{Âé ¯qÓnÔ†ížè—ÒKIÕÔóŽ4î@c2Õ`“ñh½ö¼»Š÷½W¿ú³ÌiìLèÒÉ÷ùG³•?©]—= ÷t „€ˆ9ÅÞÒÛ CMêø0êË9LfGµÔkèúBý £N¨DKzãî-ß<î^ýÍ×Êz ˜] -ñ‡ê>ý¯Û¶®Ù±àqõ¡Og't´NÈZ!ÐÒ poô˜„^”¬åD}vUÅGmµÍ6ÅÐG†:3¢„šh§wÁÝå›  ÜèP½átø”à~-p¾1öRéö+ï.xRk.yæMüŒävB@4-›ž5þélY-¦É;AɃ¥šj’R „šh3¥wíãîÓ'ÅÛSÑûB¹Ê@`T ülð¸Ôðé <ñð–f«®…s½šIT¹­B ¼¿®c¿'²Þî÷ÂÚZÿÆÜSºsÜTý(§ÛŒøÎP&Agý Iš *’oOºq-”Ž¿ûÜ¥{Ðl°zÁlõéOh/y:­Š"$Žl ! š”À¹ƒ{ý¨*ÆæMîÓ5ÛjÇ>—¦:hÏz¿>¹m?ÚöË<Ú™õùépÜXÚ¾ò5:¼÷'ZÿÍ#ä.É¥½/‚îà¥í+^£ãFLªqͱôŒûF'=£t³/³£B6ygý7æ´ËǺ°çE ¨¬Zl¨Z@ èÿäê› ²ISÓn§rϯàTz‘Zî,ûð¹FÕᆃNž_É(/Œ»Ï8?•Ú´eR”¿ÂBp (š¹ »Ô@X"îÂ(V”¸ósy¤BX žÃx¿ù䉤vÕã˾B )\yå•>R4Wž/C]W>ªÁ·LJïO¹kaõ̺sõ¿h+”U­êSos¤’jsRiÁŽÀ½J°íLö»UåçdQb«žö×8_× ž†øøøè¤ø7ù’áäÍy©®×Ö%ž(u¡ÁqP[–€AÏd ÑIŸEŠý7ßÝÝ¥µùK¡<‹öý*J@QáêùýŸ\cö£­-k§ßw°}.¼W¿aìT½ƒ¦Ø†c@¢û¡,ÂR^íZ~—†¢é`Æ#xË«ï‡?Á&(ÿXð„zûG³í#–Í~Tå£ZZ²+„€h0Ïõr\ÚzæÒ-ÿ#VV–]D£Ê$¬uJ;1µ'çm=f\{\²Çë©ì’èöݙj/1ÓP(!µÛ1Ó:Z„ŽölêåøCÐïoŸ>³ÇÑâÕ÷xUu¦¾WKüf'€‚¾X7Œöð–øæ«¤Þ¹vJÿ '?·1%Ï]6Àa$ü¾\)Éram—¢è§ÎÙXÏ;µ{œý·k!ý€§²®Ñ Å·nÊ ×–™ ‡”Ÿ‡—ÙßÎ韯ï;S7ôs¡&Ÿ‰F+¡«Êû„—õ8(!ÇÁí2^:÷s¬¸^Y¦jÊϺªýœ0läڊÝ^Î ! héì¶Éù¶Â Åçƒc³‘oO¾;`ñ4·ñÊ€ÿ]†Qº¹5ŽÓè„§i{4m,?v¸‡ÑPçÿ¨kÜ/u&ŸÜ‘òöþxÌøžò|3ŽÍ0Ê’†mÏaÿñ²¢ý8oÀ2БŠm8fzG‹ÐÃþ=çEq“z9âëv=ÎÓÖWd?KUU58É.ô):7 ü¤ë¾KU{£ ~iU+w1 ïÐ6ñ³u×U_±Sa‡ýò¿¥ðàCG c¦îç9 Ø™bÓNáªã ÝǽNA3¯{ò¹à€ß++ 'á¼ÙÍ‘ÛÞÜyy„¦ö8 þü[Y­ëêºTJ]ÏMÁ×˶‘EÀ˜…¶hÏ‚¶åº§êÑÛa¬Ûv:íøÅ *!íPŠcíß÷ê’¼Õ]‘¥*…|=³ˆûð÷jŸNÚž¯KÇ÷,ðµo3ÔùÒ#nY½R*ÎÛBI­{Õéš½ÿGÑpÿ–O©]÷sЃ 5íÛü±ymbjs]œ·­NiÕI#¯á5Žî×UÛµG:'–€#Q‰¢cøqà5Òn£¬ÚýÀùÙq¾úY©”òΊ¢ IWžD“@æžZ=Æ©$LæãÞå"nßÿâ†e¼ú?¹êRXn2J=§ïû9ë>œ›|þXÛ#&.÷ qÉËóÁsIéJY鉸yŸ€ºÂÉ…¨+Ÿ«p® ŽuÁ—`âbS'ÔX9؃è,ÊzȾÎP”†bÛܽK¯m<Bõtd_†X²äl›{Ù²ÖªÍÝÆçÑ󢎱 ºË·ÒÞ&ø¶°øµÁï³ Ý¶ø•¶Á~úÇÛÞnß/ñ¯¾«ŠMÿŠcºmp£û>üö÷"ݽ¨$ì…{UCÙ‹ÊÂ>Ò°Ö{ÏŸ2å ¢¸ô Óÿ8²œ4"þ-êlϪÛ‚bædSFï‹q Ÿ¢ÊYÛƒ¢67|÷ 7κîb€u_»¨¤¢ÐOMLŇ·î­ñ© \_— (5Tn$Ú Ð®ºÄ¯KóAÕ%¢Ä‰\ß\ßvù”¾- û=¹z ~˜7¯Ÿ2Øì˜zÅ|C[µcõ>t™¹]]'¯›:d4Çí÷dÖa†í=ƒþn]k­û=•}½fИÀú§wÖÊ»ûî¶Î {ncz¹§lŒb¨Û²§ \joÈšç-(/+=AÑõaø@ Æc^ÊþøáÖ×VÇgg'®Ý ËϲclnUHÝnØì;ÆÜUŒ>Q„@ŒXòzw§~x·Ï׿7âF PGiÝÖ@á¯3ð(Ü.ܱO”jèá†{”¡ ßB}?î»»÷›û\Ø«Ê~UÇBÚ>¯3qÿ¸;ÔUމ3ªëž½ßÐ@·\Ö?(èpÆ5‹i˲—h׿aõX©hq‰äs³®âÿܨZùû¥è68‹önxÿX—×zóз%¿‡@cæeÎø´ÖÈu<)–€:‚ŠähÁ Ë ­8‚g-™ßºRñÁ/`1fŸxã<>~öë[{ž‹I†Æ[ñ‚×ëîø×¾s²Fàãp(Xè7gíà’òÒ¤Ò?¡çOì÷äªÍë¦ ¹>øÚúlWÌShFàk¹öá]ñmoŸ×7> ƒ1 aüžú¡ïU‹r "N7üìºáÇw§ãÃ8[f]Äí…AuW41lG%e><;ðCÚ É^Ô=v²ïÝãÂýÊ•oÕ®îs„@3`‡ÜrïáÔ2Ûªªz~©ø}¤¢ÆÍë4¼ñ©*Q+üNÚàw€¥¢†ŽB½äÀöÄ*bóÏÀºâPÀoíWĨëŠòCøq·â˜ü怹­’­(9°äÐí@\RÂþšþ?Gº+[÷Ëëz3žax¦Ø”r_߸/´z]™ûø¯[:“z¿JwÑ;’lA`Ó‡6ÖàСׅ¦3`cNs—w£¨£­ßÁ÷h̶(¡¡×®™:ð‡¢h ºÒnwšªè¾CEç¡füsöÝÕˆ[q@õaúaÿ<(†ÏûMÑ&¬¹gàÇ}ÿ².Ù8äùaÀœìkî˜mEâæûî„„“{äYÇê³®è-°×ð2ߺ–Û?ÞöAwUñöƒóP?ï‹ÚL/ü.Ñ :cÁ÷ï¨!FއÅßÀ€}þ£“Mˆ ¶¾­c"%þPq“ÃÔH`nd“#å`;¿¾X*6[NÛ„þ+š<8 Bà˜²çŒÛ¾o{ªêõ¤¢&ž çØ4ÒP€fžÊÒáÝã>e©xCS¡¤š…:ìWæ1¼‡i‡=»íÖôŠÂÛ*–¬µU¦[ñxí׃ÔyÛ*Ðs‘F.d:ˆßB.dÉÅOç šãrUlùø ®ª¹šwðüncs«*Ó–dÕïYÿ‚½z µí+¤ëd[­A¨-Ú1ÏØòüLW§cÆ=RhÈlèHçëz¬ÃîrÅãUÿìr]²¦OQêú¢<ÞÕSÿmþœ5+VÝÑ{g?ËKñÇteñç~ÆñQ숋ÒùWVãâ£Àuþ[#Ì?óªÙ ²âZ*=>vXÎ+ç¦  D!8áfEˆRŒû¡À(‚é´b]±c0oC¬RU7ÛN¹ý‹R†ã¥ºO)³©J™¡)eºM-‹wÛÊ(ÞYVêKõŒt‡£*Dˆª•a¸ÔŸÿ‡]+/´©)n›^àqêsðúg¤±5ÁwDÁâÃ6¯yáczÅ>¶Á6œÁ- s#ÑÚç5â'âÑ qX„8 âV]¸¾jÞÇv`7°Q½Æµ•§´yËp›|¬ó‘×|sŠ&Þ|¼?lyã6óÃþ: ujêqÚÁdwRî‘{ÖA耚‚ôݸS†„Ò“Jóh©,'Q˜€È›Ü#i·{ô1uæ¼iÓöN„`#:Ÿp2.IÔNNƒ÷àëÖaÝÔÁS­˜¨õ¯)êÄì{­äc˜½°µW×·f Ô&¸ aß'WmQ4û9ë&÷ßf] köE(Íú¡ƒêõv„CA<¡Y1`«@:Ìb¤ƒI:>›mñÃaÇ©ˆW¢Q0@ß!(hñ0PtbšŸzaÛß•Š÷Ѽ •ŽÛFp òËÊ_[õöqÒ¯XÀiá?¡é_ãJ|¨à–-i™çÌcþ¦i33ž©Ò†óü6÷+ŽÙqÌéàX‹ ÈÛ¿¹ðæÂÚ_€›kXŒø8o£`‡i…:åkŠ‚}%_ÓÕ+|va“—Zø(Ÿ<ÚÊg”·ÅèeéèyÑ–]¶„p;nJÊ4”‚i(GÛ| @+0KÂ6Vá÷aÿìcÿ+þVàs–tG:fž³ÒâäÌHþ+¬í Cþ{òé ƒA›þ «žkò (D¸'ŠñŠ!³¹'VÌÇ •@vs›­48î?O [tŠÑT‚×£ÝÕ ìª’YàòÚ&¤å˜¸Ç2.TËÒ‘HT‹b:Åq ›„:Pè¥\o——r|=(]ÛZçË")b‘Þ†~,¹ó Ð~‡-î²P+œWQ"é‰G,¶íÊ+np {r¥P¨ n@íøjy‘òÓͦ^RU¯q)*…ïk‰Ûí¤ìÀˆúä‘Ûœ??üy’Z\–äöèIª¦'¡'C‰$ø$AyÀÚHB”ˆªtj‡I(ˆœèÂåÄ<ñø 8Ql8qÎYeÛ?ð·;³Yœgãšv´Ö‚tpà±&¼È;× ¹±¹ùâšp9Žù×Ö¾uL1½À*ã¨fÜÄG“ k(¨}(´yt°((´•L6SŒÚv±nkW|ìÚv] m¤,úQÊÿ@Ù5Ž¶Î¯ïñì¼{yÉUÝÏN|ÉæTëÜ»0Ô¢4(½ƒÞnôCÉïÐ’èÌSmê˜ç]÷³rÒ5Im¤!‚É5‘G€{è>ý]´ÅÂP†åéµS½,)š ~Ö4íÙ“®>.ÛÍC€-kÞdßµý =ÎVn'ŸÏV®ùìP&lšjØ=nÃ®Ø ÆAAKZÜ¡4 Wˆ‚QU%%jµˆ s?¼¡T¨¼­ÂT,à$Š)È€w¸‡2l"ÆTñyH·ñØ*æ1œãcðIÑÐŒ`xn’ð:ÐÅÃçS=†ªz Mõê6›7Í›à‰Klï>a™·¦3Zóð“»F7[¦Ïîë3Ü?Å+‡NIü§–¢ˆø ñDG<éÑf÷©üóÚ¨Úíc_v=°%\‚‹.²-4ݱÏmtìÔËúz4ûÁõ“úqWº@ølvWŸÇ÷%Æ è8(B@f$0Ñ•9Âðè@ïl7Èñ‰Öݾ Ú­ß­¥>by8*ÒÛRšVå³WŸ$ªÄ=àíIñJÅkyìçJù¾ ÚçíC;<Ã0« Üc å%›½Õ/¹ncçá°Q†6ö†3á$¼PÇ­2ø®ØË½äXH%pç¬YíËJŒ7`œ¬ôõv|­u´­†w*·8ÕØ}g·g0e•]à+7’Õ³“^RÒÔÆ9èïõö‡©Ÿ[V«|?ݰ”ýþXO½ìš¶ªêÙð쉮1™*B¸¯¸,qÝíý¹²! „@D˜øPæÅh»šZv›âöµ×6jmlÛ0Ìâ^Š‡Ï€M)C×yàzƒI‡Ì™wz†øÜF¢¦’þ³¡¨ýÚÙ¶8Oqþ =bêëâGQ¢§ÂÛ¼·TOÆ0çê4ôA$gƒ]ßÁÖù{—놲¦„&J@SÒ–{ ! „@³¸yÆ#§A¸ÍçÃÝ¥ëÑBÉΨ¤ý­“íÿíõ=r®›ÛξÁæ|_‰W viãÜÖ¿Ã}·÷û¼J\¡¦ØÏ{yæËkDlâ¢41p¹B@D[§evñiJ?ÃgtD­< Ž®¥pa-TíJ¶/£íÚy'Vi3?#óV¸À> X[{Ûz5ݶÅì‚èTóá]ë׬ßc¶ÔH¡½`6å½Þ~”ëë‰ÑÎÙ¯VùÄf×n §³_}èŠPZW! bžÀmÓ2»aÔ­;•C·é­«ŒJèŸË,Zå+&{Ú‚i€ÿ¥iÊ›¯¸¦­Ž$€!›J¨[f<:ܧûÆà~ÝÐ鈇kMnª{Ë}„€B nÐâ]ˆÑ Ù~»¦jŸD‚éºn’7], ÿ} XOM?äëFkÊÆÜ_†‘Ä žg‚GÕ4”­˜¥u ºLo~Áu_hº„!kM¢¸\¯;w{vߎADï¡Î„aÎhck÷‡00“$…€-ž”€$(ñÝnÏcD m|¾ÛÏv²wz¡©×"6få¨Ì‰¡Ú§e$}TÈHÍ'Â-Ú„é3ÏÇ=^ÅKÔ€,ÄòO5Aýèå8î{KúB@!Ð8·<úh+½D¿Oý˨Äí@ŠãçeÎø´q)G÷Õ<ò'&›âÑ,9èÞ‡¡²¢0„U ˜0#ó.Œö$ºR¬†FyÇÜYÓ¿ŠBF"²B@€ÀÄi™gb˜ç±9.nSæÍœþL¬‚ùln«Tw^^çŠQÁ¸{ žU4ê<Ãü {PÒ;dk7R€ðp–T…€ME€¿ãŠ=ýTþ®ðçï|SÝ;Òîã)/ ø³±ÿD¤ÉWWyÂb à& Y¾€_”¹™Ó®’qÀëú8$žB ò ðœ§Ïz…ßå°ôދŦOžŒëçõyÖòÓ‚%`=,ý"ÿÉÕ”0ä–vÄm^å&²§c0†«TSV9"„€@Àü®ãûQ²±¼ZñÝÉšNx·,¸kÔZB®p/Ó >ó\eþ̦{'åNB@&#Àßw “ø{oöþj²;GÆ0ÕfR$QÛË-äJw„–¸P|‚^ÙB@´@¦¾÷f÷ڲdˆ% &È?€òÏšgåˆB@´4¨ôý“¿ûæ÷¿¥e®–üè*,˜iP,ÌŠGä€x€ZØÉ)! „€h!ø{ÏßýŠ‘`[H®Ž 5È'xñ ¨@ÖG”€ŽýI ! „@K `~ïñÝG^ºµ„üÔ5ð…h޶ºf¸.ñx.Ä‹Ø1’ë’‰#„€õ#ÀCÀã þþÇN0(%(³AÛQµR%&ÖŒ¢¶m$ªžœ+„€ˆøöów?¸f!’…UŒ@~¥9 ¬œ%q!}ÆOŸ¹tÂŒY7E“äã§eþ0qúÌ£If‘U4%È (b h¶!7ŽX7Ïxä48 -Å4Uß`Œñ3Ž4p ž—¡EŸ4oÖôá ɈËe¨.—‚áÌ#+ OCÝèP_©&LÏü £±§iê¯üiÚ'Õ¯¿sÖ¬ö%%ú>US~5÷OÓ?¬~¾QûŠq<&‹«·Ìº§\,¢•€ašÄ­Qän6qFæ­á¸ÙnoæöI®ÙÑvs¦©ûŒW¦<ñÏK.AÈ$h€C|ÔZB?XPd>,‘ª9 (4C?zë´Ì.¡ãæãþÉ¡L32ÒRÞg'ÛÂü²Y‘!H!„@ AÍŠFQ«„Ô1°$9 @Àn³Móx|£0ñöËØ½¨.PЦ~¦å˜†™'†a’’LAñaG[ç]®Êøú›§Ï¯Þ?ñv¹Çó&­ò@?ˆx«¿&4¹ŒÏYa´ÌÏÑ,á˜;sÚ™Á͸ϛDêþW3§ÝÁq'¸æ&¾œ™h`8£ uGš~†íÁ'‚›Þu8~GÛôkwûf= 9¯&2ÒíRÛ=ÇåÖ}ƒ×ãgdÎ$ÎÓìÚÕ¯¸Ø|.x[UŒmº¡Ì‚"ðÇ 3ù÷¼™þ|þHÛu•›¯0kV•Ïbót,¸•ñÝæ¸Óë-wóùàÀé’'g6˜ž“g{0\©)êô—g>øup<Ù1G ¨w€WQeœ€˜{$Ãu& ’Z¢¨Ê]fóB8Ëýß±.ÄœåW¡ð]ˆBUSqÌUŸâšñ»½»ƒPùmâÌ7ÓRŒçTEÍ4õ´ioDZ_MvÍimÝç׬!(°G¡_ïizô$ëø$×s)Hãתalãc.×ü8Óó êk1-Ø_qïßcÎôÿ¡þ»=³Þ¶®3׊Ò׎Üí}ä(  p|Çï=š€×î#øWÓWm §­“’ tMïZÒõ?³\æ=ò§>rßåz:Í(Õ—AÞSðL\¤¨·ƒï^¯Çý¬5(ç+Ĺsíàñ ŽüŽÀ Ö=°¼$xu}É„‡fªŒ)[B öà÷hpêªXbïו€ÛëŽGÍü_¨uÿs?3Áõä§ó\SéúIÏ=ç(?Pð ›ÿ½šu] 4@IDAT9ãÒŠ8…×ú2ƯMœþȸ¹™.xuæƒ_ŽŸ­nÜá°Å}ø¼ë¾]wŒǺ®{ËÎÄî{|Ìë1޲ …˜Eì¯pȬY—ë…g°¢a¨öÏ8ÞïÆÛgâ½”yŸ9E(¿{óôG~ò¾­{s\Pjº+d éhï{šËueZ´?Ë4ë&]7fA£ù¿#9ûYñ¬5ÒŒŸ7q¢çWæM>¯ñÍnφûqδzXq‚×õ‘»ØS ‹‡Ò^QâúÏyïÆŠtÞƒ•¢,§ã\ icWÎxäòx›]ö’kÚŠŠ?=s”“ÙØ(T‹dCÄ n€&À!.Á%À4RúDÑ_T$ð€Þ|Ï›ý²ñä-}úhwt,<…}U£×ƒãñ*,(’ý´àãÕ·çͼ? õÙ} ùζÎភ°/A5›•ÐõQ—¯ñ0~ƒÚð¢ À<üJ惨+› Ò[qeåÊP¦M@ÅÚ˜øPæ%P|^UáÖWfNó[.*¯>Ú–Éëe×ôïaÙx²O›èzdÀÑ"ƒJå†H!ÏKæf3Ù8M}£zúþ&eE`FQˆ­44œ¶ê×Ⱦˆ!K€·ÿ°¨mŸ€zc›;«ó\÷ï@þAÝ ç°þçÜÌ Q¸¢B]¡NC@E×{²œº‡åàQKf˜°a¡G]Õ8öФ˜Ìc ƳùÚ ®ÇºÏ0M¡)ºbØpý]·OŸÙã…Ì[¡ŒBÅ×´p\œ;ò¼ÃÛG¨5½ª·Ù[Y5äê§8ÁîPîG®–Ãa^ÍÇ>b·¥=èõºÄðúÐ,`œ~˜©qQ=åîyþ[=‘¸çNwnq•Ãx0=QàÇã9¬ >%›ZTß¡bvôÜ|N¶…@,X6·cÂþ¼½š™W…JGú.OÑÄÏ-j¥Î°M…Ð÷(Ò_¹Õõb JOpftE1»ÅA7xh/h÷¿mدÇ?Ò6Ô ôµW†°_€âõ\‚‚þPÛé_u´õý÷.t+ÊÅÜ6Žöñ(@J€™V5yéÄŽ‡5j¾/þñÖª%gàèD7b÷lÇÅ[‚NÕyó%×mE†ªÞ 9OÝëͼͭªGnv¨»ÜN0¯á¸8gòä2°ñ ¦x -~涪ܦ¼N%'8¾l X!P˜ŸŸÈ«½=8b BYdS¿{Å5íçÀ‰zl¨ñô¹¯ÔPмe§#í‹QØÿÏåâÎ ð.œ>ócÒ•ó‹Òm(¥U»=®R PŒåPPS>BPŒ®HkÃÎõòö |!îž0}V>šæLœñØ·sgÞ¿ò¨åÄ«šö1”ˆA©xÄWnTÊkůŸÜÛqY'ëRk}ËÃO¦CYAͦòY ×À&(¥¯dNû«OÖB@ õßð¦Zð‹É·¶£q-–€h|jQ.ó‹3ïÏVå1X÷oã{•ìØì?¢ *6<œÒŽ”½Ãíó±Y:^™>}7ʲµ*ùÎpÚã&~ÍP>@az¦b˜þY/¹îݸÐP~Àú|8.¶ Æélh`@•?Ö6 ÐC§£­ãݬ@èºw¾iý8Ö…G8ïHÐîBåäÕáO[Gp¨Üˆ‹Kϯ>‘îsŸœ$oƒÅb(£o›–ydŨú²/b„€b×J~(Q­ˆ% F^ÚHËf†ÖgÖnïú+Pûƒâ&PãÅ5õºÓÍDãÿ£è‹ïVíôOœ/1¼ú‰¨¸ŸLŠö¢åÈ7zp¯¥Ÿ­Z½îAAuªõ>«—š¬?×I¿™ž–ð©•§=ñ£oQSo€›Œm4(`®WU˜Í b¶…Oß}w p{Æ59Žƒ_ñ9MQʧøäÔ©Åñ f´i¾ïñ”ïõéú{Ðô²'ª§[M ·¾ažëÁuÈÓ$´í_ƒ®¨ïõÿÕYÓáÁ¯|RýÚúÈý’ëMhÏ?rôôxô_Ê=îÝù0Æ‚v5£*Móî¿?_u*# ¨eCIø‡n”oÀuŸ iãîê2Ⱦˆ)zå¼øMDµ% ²:‚'ˆvË/8ÔÎæµ!ÐXìÜWì-n•–wàñûî;j7œ›]vW)å ;Ò5öž|=OL”ã™Õ嬡}v™ÊF(m‚4ê#77{$’ª?íºÛl¶¨M¼ùóçkŸ¯ZßâÕ’óz÷>MLjË—œ Xûö/x\ûã¿0=XÐ^wŸÞ Å>4ô—Š44ŽŸ\fÔ1 ©cÂW_1+†ÞŽ® Qê#÷Ñl:R†+ ýÍ|nÞ‘"È1!C0 W*,gVˆjK€4XQÖB@! ê@Í“•>ŠøÔ™DB@!ÐBÍ ˆÎb h!U²!„€Bà˜àL°¨¤ˆpLbA! „@‹!PÙEP,-æ¡JF„€B@›œ–Œ@Zpì+"7†8Fî³É„€B  ˜F¸"`¼i°`ÈZ! „@K'Ÿ€´@E   ! „€-Ÿ€R©Äéö¼hΰ4DóÓÙ…€B 9Tú¤§‹ÐO@î)„€B © ,y½»ó8ø¾ŠBå£nؘϤ©e ÅýÄ Š’†B@Äœœ€?”¨¶ð“¹ZÖkËBO ¼Ý²r*¹ &4Œ9æGÄ´Á'e[Ðp«ž4òùÓƒ%@”€Ð¡•”@À,ôo¼×5BQ•1(þ»‘ªd(%7 -¹$Š `À’BÒ½†nìÐ}¾O^rærdGÇ"JA?W=òø£Ò@¢DÞŠ ‰” \ñ¾ÖÊ$›]»K׊¢É N=-%AqÚíÒÌïA —ežWPb–”©ªM{xâŒÌ}·÷Ùƒkw¼øÁ¯• ¢¥®‘ ! êO@õi†6»Fõœ{i¨ÿ;ÐÜW¨¸ï¡1jœýφ®wг“qâÀž4¸W'%ÁéК[8¹³0¿’²rÊÚ´›~ÊÞÒ~õ¦]f éyǵÇÝ7ñ§g/„düíbe@‚ $`°% ¢ÁÍ04Dwë›( |šá26ý«7=øð]Š¢>ž‘žj\uþÉÔ»ki÷o†‡©·„"H'êÉ‹²qÇ>úÏ'?´Ûmï]ïôÿúxæ3Û‹E¬‘úE®ˆ'Ëk 9 %øˆÙ8â_9S@S¸ñ×d(OŽèßC¹÷º‹4(Ñ!½HÙ,øý¸ïú‹4~_ìvÇc×O6‚رðï^”Çfy*rÓh'  „¼ÀÝA”€èx~êuS¼@UµÙø 7]~–gËt<ºæ•’ß~_ø½±Ç9gýö¶Éc!‘¥4¯prw!…`F ‡\Q¢ðF›ÈêØ±¿Kt:^íܾ•ñû‹O—\´=Á—ß›Žéi”ÚªõKC†ŒäÉO¸)P*ðlD„è"€pes@ '@>‘ýþq¯µØc’nðÐÄÙ,R¥ã÷æê NQuƒ: uÆm3Žß-,¢TFêC¹"“€¤;Fw% ²Ÿ?›Ãéœ4¨WgC|"ûaEºtüþð{”˜œt3dåaOÅéMä‹<ŠÑÚÊP¥9Àb!ëÐ0­WŒŸt’aíÑ Pjl¡gs)ò{„¡NÛ½ê÷'"ób ˆ¹7@2%@UÕܤ׬Iˆ% Yñ×zsS HHNÍa€Z#ËI!Püñû”Ö¶íyˆÏ‚Ò$PpGT€PàVs(ÚÁˆ¹OŸFªÚGäþß„@c ð{”ï0TÍÖiY–ù4¬\;”`%À!J@ì<ù&Ï)˜m˜ CËGºÉñ·Ü¦¥&*Z¸=rÈJ€ø´ÜG-9 1Ãpa˜A dŒ™|·8†˜±$ç'ÀMfs€ª(I˜ €·›$”»y@¹†ŸO'Ÿ.#Ó6œ`ø¯ä÷IUÔ$܉«9 ÉÞ±ðçPî ÂC`Ñ‹OµBs€ù[ái„Åõ;þHˆLþ怊.Ü"~»r#-úq í>p˜Ò’éø¾]é·cN¦w/§O¾Ë¢ËÏAçŸ2( Æ+o/¡äýnÜÈÀ1×KSž~“q6š}Ç•ÜöLÏ{öæÔT–O?¾]sa嵜ȴÿKy…ÅôÇ —Q»Öþ‰áI·<ò7ºóÿΧþ=:î%%€aNü +bij,N¹>&è%î€?>oQßÀM”€È|uùm.(M­3œb–»=4ÿ³éœýiâ¯Ï¡ûsIc£WEÐ4•>úz%2è8JIŠ·×X¯Ú¸‹ãTZî¦uÛöš…öøËÎ"·ÇG0Žý;‹–ÑW&³]:ÑYãzëÀüϾ§Û¯míÊ: *Þ«`+?ðèž % œ$I!LÀPôÖA¿Q‚áÈvÈ ˜J@ÈS=B‚{æQY¹‡x6B®[µp+êÀžh7jóï.YN×]|ºu¸ÆúÇÕ›iÐq xèÇÕ[L% cz+3^~ÏfKÔ¥}J®E8넾´ø§µ˜ ofFìl^ügÇÞ\SaÙ¹ÿµoB#‡ö¢³¡¼pø×ÇßQëÔ$Ú±ï ­Ý²—:´M¥ë/> Ì Z½e7õA?ùKÎFmý~}·j-XºŠŠJÊh@ÏŽô»±§R”¶F¼õÙO´~û^JIL úw£‹Î8ÞL?-9öåPÖÆH?…nøÕY^_,_G‹aMÉ/*¥¾Ý:ÐØÓ†PNé¦lÌøß ¿§í¿ d¼tÔ 4¤w:š æEáÿc½c•_øï)wQK@7ô6–ðИ£¾{ çEÌ€ÖÌu“|œ»e¤›MsßYBÙ›wÕ Á&ù+ÎaX[wçÔ8ÏJJËi5¦°Ú§‹Ù”ðóºíÄÍõ ]:´1•‘·>ûØ¿ zHIŒ§aýºÓÔߥ¡h²àš§ÏåPP\FïAQi…‚úÿÆžBûQX?<ï]гÛèš±#iÛÞúrùz3n~a ýíÃ¥tú°Þtóç˜Í _ý²Á<÷éw«)'¯&ýv4]0ruJ÷[9ý¾ü…Ò` ùí§ °/£÷¿Xn^ÃZ§$Òe£FÐ=×\€¼ûLåƒs>^øÏçP6Ê¡hœ‚{ö"’JµÉÀ×5A°”€&¸•ÜB´A=RZ„%@”€ð^66 hÛ¢n¸…g"=ÂêÙ}J¹ùEd½(ÄŽïÛúuÏ 7?ý!pgjã%ô9jÕÕzJй'õ§ôVÉ4¤Wó>ë¶î DKJpÒ£O6‰¾ÝÛ“ Mÿ‡>׿ïÝ•6l÷ÇeKÚ'ôë†u¤G¿@qá ¢)äòŸ›WL'¡ dX?îMçì/Áþ<]ï¨û›ÍÖ9®ÙsÜT( }` X³u±µiדço HqzçàºôV)¦µãh2XiÊZH"P9Z šZ„ >‘ô~UÊÒ$€ÊÛ¡Ï jÏì|·lÍV³výê;_Ðý7\dFႌæ. ™~Ÿ¾a°ÏŸcó?æÙ¦9o,ä]b—>6¼ws¿>¸ ¿`ä˜êWÀ¡g•K³ X|üÍJÚ¨ `†÷ú*-]ÚüvàŸàD»•Y¨s"ì¯PîñÇ]‹šÍö¾öQeúÔÇ6M^šË&m¢kÆFÝ;¶5ãq Þ Ûµ¢BXXab?[!–¯Ý:±óàåpA±yI· ÖõµÉ`ÅiÂu“¿sM˜7¹• ü´+?0⦒ÈÑ ðGKøƒE1 ‡iæ>’9>³Ð5¼¿YØõìÔ.pÙ!r›vî§SG­P»æ’ä$¶p3·³×7pO„oVl¤w/#»Í¯«².ÂŒý{dÐÔëÆ™ð½Ï¾Y%éê,q­þHÁéˆ3eͼí×5N³¼×¢çר߸èúó»_’on~ >+"ñH‡kóÛ÷¤…ßfz1,ùi ¬&?šO•¿g…ï×&Ÿkð÷:©xÇü]žÂz?I\´°š¶®¨±`‹°Hs@ x1›…õðäÿ>k3@!µsß!Zµa»i®æÚlõpñ™CÍÚí*8ÆYá§ì­èhGy$ïN0—ÿ»`¤oy…‰ÝŠ[׵ͦÑoÎ;‰~€5Áªéóº ¸þ èqà¥O¿Ïªkr5â±ïÂA´ûs·H¡VVd8°µ»5òÔ»}»e °®4˜±ÂóÍŠ æyîVÙ§[{³+ä¡|¿ã#+A¹H÷[8š_ .ø¹Ûä'ß®2ýذ ޵Éà¿Xþ !a–Íh>•_·#-â4WG-– ¨|³0çš6·{)p÷>.èÙß Ü+`(Úùà¶÷`n8Žp ܾÞi°’Â-çŸ:ã¬6½úÏÞ'`¦¯oúÜ~Ïù`G¿·`]à1 ØúЫK{´Õï„ò±™¼àÂ= .ƒ‚øü’eka!ømÿ KáóÉÏ~Ï}] ãª0bKÀõŸIÿZø-ýñ•wД¢ÒøËÏ2ý,Ž&ƒu?Y !AŒ ŸµeXŽl+m ó Óg~Á—ÎËœq6¯%4˜—¦ñXRn˜:ýý½º¸ûÚ œX]/äþüš¦˜…T]¯iŽx^xÞs¡}$KECäávýx§N„•J §ÃÝ&žcÇx€$ÃÐé–+Î¥Ò27®áQw«s­"G ÜCÀ «@°ÂÄq&ÃÑÒiÌqöÛÈZ·eÕߟ~ô¤ÃÝœ °”bña‘ êM V¾ý=®|v€“ªj#ÇNõVÖ†êM-2.K@d<‡ˆ¢z{zDu!ª GˆR¯CG· X¨žà‘ŽS›Àç“0Êâ‘ÂÑd8R\9&„@óP jƒ>3誺Ò)¹y$jü]E hr¯ B@Äj•s¸Q´Fgw½àpK  J@KxŠ’! „€+¥/§±à/3 *P®|«EøÐˆÖ×FB@–@ °¨,Ð=£·´ˆ1ø¹ˆÐÞNÉC,úq-}õ³î€:] ‘„€3V†£ˆc`åC•­%°óüŒEECÜ òóòˆ|<0ϺmûÌyF èN<Ú!‡/Ð?³:ãâÌc<(ôÙ÷«á‡köÐ~ufnzš}Kˆ P9Fz ´%@,ñr‰G#ÀÓú¾öþWæÌ{#ö0Çÿç¸Ûö¤×?øÓ 'cÒ¢ úË{_š£þñ9žDh Ü»³9ä/÷÷çQy,€Þ]30ð€1À÷“ „@ Ð+• T"J@ <ÈV‹b h±åf‚‡ ~øæËˆg|þ?ŸÑÛŸÿÈl‡6)í8ÄãÙûxF¿)Ïü‡6ïØG­1³æü6§ó D” ! „@CA3¶ æñ hÈË ×4Òr·9YÐ%£†SÛÖ)¦/€uó#UæWn؉&‚âø¶ï#·rD/n&(ÄD„€ p Dí¹Å4ˆЀ7A.i:¹yÅôü›Ÿ‘†?ÎnÃÄ<Ãk½ùñpdÇÀûžëA+ê4uïÈ¡½Í©—bÀ‡Æ_Š&žžA‚BàØÐ-^Ê¢™Ä'Ðùì;®4§ûåéx­pÃ%gZ›æzæ­—öÿtËå¦õ€•†àp|ß®¦³ Ï§› Eð9ÙB@ÔJ È1#ŠP+,9)BL X¨KÒÕëžÆ— B@ú€1²u`î£åXäkXŸ·@â ! „@¬øØíj‹±ˆ«¯³ä[! êN ¨w€¢ØE ¨;9‰)„€B z ð ‚¾••ƒ´ø~¢X0d-„€B %ø|^ëƒÈïi¬PሉË=-%¿ÒÐRž¤äC! ÂB@/( øà-Æ À°ªö¡ >I4 ìØ›kNÂÃ#ðå–ÛíW|¡ 7*°M-Ã÷¦a˜ß6©‰4CüvÍŒÆÙ¨´Cyq¬ç?”,%-!õ_@ hI“ñs% êßÎÐeÀëõÑâŸÖÒ¢³(¿¨ãëdSKIµåcðÝòÝÈ éÞTòêñôþ—¿`Ð{Ò :gD²WëÛ²›Ö!!+ÿŸý¸† ‹0² 4¯â¤2ÕI>ÒêBÝ¢h`éÔ’Í(3óŸ …è¼“ú7{þë&½Ä±GÀKÁ“Uµô{rÕ—pÐð½ÈQ #ÎÌM’£zŽ¢*9Ñ|óê;oŽTj¢Dê“ib¹²7ïÂhzKQë/£øøM”Þf%ÖëIUËÂ&‰®;©´´/• ¥w—Ó’³éš O3­M=Áçÿï}Gù˜µð°½=íOèO¹X{{Øòo3<ÔÆ³ŸÚ—ïBþ—Óâ×ÒµžÚ,ù[&%a!Ð((¨Ò ÊcTîa쀟ñ½*ÃTçŸëd´ƒaºBzºb(ýH§t¯Ûà O‹T ¢Dê“iB¹>Ç }ÿ]ôÌô¨C»ÉéÜÞ$wg#1q¥¹”•u£ÃyÓ‹óËèòsFÀ20 HÓ¸¬øó¿ŒJ´dZŸtåÛÚ6IþYÁØ×Ù\R½©Oiò¿ùÞ¤ùo’ÌÊM„@T¨´ ø¯âà vÓËœoUÅxiÍ”Á‹¢-›Mó•6*1"/4Vúü‡ÕæÌ| ñÙÔ¾ÝÜ&Sª#fÅ£}û¹ÄrüwÑ2ú rù|¾°Îœÿ[GZ–xf“)ÕóÏŠÇò¤3‰åhªüW—Aö…€82´ —°]eáUS;ÛâÔ‹s÷ ÝG¾:²ŠÙÏ'lÒqÈ&p.p²)½í›˜£y{½ðýY–ç½%?SÖÆæôÀ,k¨Ceþ—ÓA{'ÊNA:šõš3ðýY–ç½%¿„5ÿÍ™O¹·ˆ6ºAé–ÌPZÛÖ:ûÎ;\Š¢[ûÑ´–æ€hzZ!”•½ýÿ€8ûjÓæ¦Üø¤XÏþtú÷'ßQ¯Îíá›GšÚšóÿ|¸ `m塮 ÂXž„¢Bú×Âè¡0å?„âJRB Å@ÁßΪŠÀ ð@p†û?½&µ•Ëq¾m{Uû6É ¿¸¡Gøœª‚…hĶX/Z/ÕuÝ쇭Ò>lv @uŽlh ¹¸‡Ââekˆ½öYæP+ÿùè°Þ9¸Ù-ÕóÅ ñƒ© ¸4,ù¯~?ÙBà#` `ï+v¿9k^ß—(ø;ºþ3º-Çyô«¢ö,Zß³­¸‘ºK@¤>™0ÉÅfpžJwñ²l³@S9Ö7;,÷Røê;Þ×täí5`åѲuf/€¦r¬oþY.î¥ðå/CšÿúÊ!ñ…€ƒÚYld Ð=·“¢NZwÏ O¬óÖzÀœìºÏ÷_ì÷³ŽEâZ,‘øTÂ(ׂ·íΡÂb7%%¬ ãŸ4ËWTâ¡­×ëõ†Ä`念ìýöÎ2Œ)°|Å%e!ÍÅ•¤…@‹%€™*-ÚP+šH1 ­ýàõš»fcìÏð¹Føúß°Û¢4\4^fÕ‚³·ìBÚ0Ljä|ð8,çÚ­{Èíñ˜ ÎCCCpþy  ’ƒ)ä Uþ#9¯"›ˆTønðg(Ðo¸SÇ@´Ü½AºñÚ€'²®öÜÆ€¢pöÃÖÿ©¬«ÈPr—Olfëc€%à€ZÒi!è£Ü¼"ÒTŒÆ€BÁåÓÔâ¡‹=¦и.ƒÁùç‘Ã9P(òÏò±œ¡Ê(d’4„@¬Xôb äo:W(oà•Ùn‹Áº» §ª?ÀcéÊ’òÒŸáPÐ÷‰Uö._½ߛߨìt•7R×â©O& r±)œûÞçÁ!NÕòÃp‡Ð'ÉræÃtÏÞüN‡ßA°¡ƒ矇ކP¦:B–ÿhȯÈ("€¯¬ƒ ?WQbäí¶j¦ Ú,øÑñØ,¶•úŽ`]«ù‘×\²)BNÓ`f@²XUñ Àñ¨žAs%J@È_™ÈM0PF®ˆG–Œ þ‡_/ Öõ O¡¡wnäu!Ê#¥Ë…@l¨:o@% Úgä*>1öZ7¦m.T\h[xcehÊüÛ5…ãÐÑ¿‘!”ùo¤(r¹ˆ9øý|`¬âí3òÃ% æ^é†ex`Ëhâ¥_aðë•Qè¬î§û¯ÝAÛh&Ú)}8ö·×X.?k%ƧÓ=W¯§¶i}&@3_uq-¼&½*ü‚¤'ªô¯_'Ó×7¤ÖìäN6zï·)ôËÄ4Z6>R*]1 Î<Öx• Æíä€a%Péh\qÏhžA³ Ía}y"'ñÆÔ€UÕNg›JKWÎA\'g\*]rÆ Ô*¥{• æä­£,üuà˜ªÚè7£^£½¹+¨¸4‡Vnú=ì~z{Éq²Ñ¼4äK6;Jþ;OqÒ ?–bp0¢ãÛÛè¹±‰Tæã:zÕ0´ƒFó.N¢·²ËiúâÚ‘¯Sa¹Nï®sÓ-'ÆÓ¯úÆÑûëcT½¸Ž{ÉKo!Ñ„€¨$'@#µJs@e”èݲªuÑ›‘¼Î¸ðhH2°Ç%dà5«·¼kÞË—Båž"úï’›ªÜÛí)¦]9ËK‚³ ¦Ž£›þmÆû!ûL |¥§õ­r]]v,Ù"¿•¾•†µ_×õ…}ìäÀLÆVÞ}ÚjôúŠrúOVÍÂ|ü ñfŸùu)­>à¥(¬*xÑ©á•å4axÃ)²doLþëš_‰'„@%àæÍn¯ÒP+z·D ˆÞg×d’§·êOòÖBð€•_´“ÞûêòøjŸ*{Dÿií¶©´ì°)kaÉ^*-Ï£v­4™ì¡¸úësÑE±¢â?µü×W”‘ ŠAõpbGm<¤ÓÏJ Ù£ixF¥±mMŽ—z´Ò(¾„€ˆ|†áR¡Å·fIÑKÀ8·ã¸À¼‘/}Ý$¬üBÕ-¾ÄŠA­“{Ò¡‚­õÊy‡6C¨súZ´ìá*×F:Õ›ªDˆÀ©mË;öøñ˜+,Å¡ÐøaNz{M9%À)ð—'Ó•oRÖ~/mËÿö¾>ŽâúÿíõÓ©ZV³eËÝ–{ÅØ0Û4ÓZè¡8¡- ü~NHBBh!?BÉHèÁ;T7lclãÞe˽IVoWwÿïíiöVÕw§“´{;£ÏjËÍ̾÷Ù™7oÞ¼ оEÐ;ÅûÊtáQTƒ¥ÁIât‹žž6 –%(®›«|¸ùÏo •«ù¸‹` Ù  P"HB î6T"âÙdŽÉ; v¹Q½‰ QÁf¬DÉ®p¬x]DLO@-ÀÉÓ[åC°ªî$»zªiþ:'Ñ›NúÏH§ [ ¯m¨‡£¶€Ât3Lëk•…€âZè@ùq!@†‡ÿãh¯²2¿ÝFö¨Ð›ã7K¹’hÎ0‰b ™’ öEa, Q¢ø·jœAn¨õÒ}Õu§ 1!;lR\ ™ßg|½ú±fi’0ŸC§V5{®åEµ"dºÎ¬Â¯õâî"5"$©–Öø$pX‚iiE]c~Lñ Œì­Ü¹3`*ªßÞëÍnås àWæŽnôŒÝL~ì9²JÊ Ù#]œ¿ÜƒküÇ;àê|»lðLj~s£èP/J ÷]#¯ðá5k2È{àm£ð—Õuêèüš#ÀÐ2*—ÁhØh: ðÑ‘o6%=ÿ/;‡ãN'Ïâw?§¾iú»ïùA-–ŠÆhE?,Ýø Œp=Rvæ¹ñ–È'ŸýzžË6ý±¥Ÿ5ýŒÖøÿUú×äÛÂæž„&s—ô·Â¾r?|µ·±vDÓŒsâ8GGûŠa ˆ§ÔÐ a{¯!Ïo[?vüÒ9ã†(|däwê8Z½æš­–ŒÆèÚ{t1Ðm¨s—ÂëóÎ6y—§[zÐtD>GmEÀf³ñÊÉT ‚ð«_ؾkÈóÛî!CA½âÅ…½–\t›L&ìL©C5CÿlZó^ ¥å—ãJmÍ‹=eå³ÐáN²e&ƒ…¬ùè&¢ jþ‡æe€ÝjõÛÁÔ°Er´ùÆ:Ñ3 nxEš IŒÿ±¦“çLjgRÈ[ 6–\>:|áîGGL4™Ìw¡-À¥§N×üüާ†½X o;¬'\¢oQõÄ%§UASgêóUÀò-ÿÞºÏð:JO_£)„JK¯/Ò5°»+Øl6ì-² ÀFô‘ÌÒÿ”—Óa‡±}ÒÀ¨‚üºÍ‘fסñ‡Ôm—X ›Nü}K¼’¥Ýüw(Á0õˆâtôÊ—võÑ $\ÐnIÑü¡y¨ö’Šû[ƒ?P_­yj=Çp“ìýà©Yµõàäôõ]® ÑQ[7 r“L•ê; v» +X°'¢ ”–ò ¼(Ï^)00ÓÝ}ÇaXíú.×€èÈð€‚"?œöÙàXþðƒW1  ÐNþá†^Pð^}4ú™ßp € ÷`¡‰&€=fgs/ñE0}ˆf¯¡¯Ð[„€ÿiö›ÖÏ|u€6Kˆg9¤@Û‡ûÑF믷!:I¸äìó•÷BUýa°à € ×ßËu68T> |þîÐ-õK}V¥êœK²(Å)NÃe'HÐ+Í N§tØírÇM£øXL@yRÞƒz¤â;Ñ`ù [S {#¡ÒÒ½s˜V½…lÖmG @•Ì¿=×G9@°Úá@µ~»Ò¯]Õ>þÙëÜ^Ÿšnb Ô9Ÿ9FF@ÀÄÙGÑÔ&@ÂeKÃ_,‰Sa§?Í$0 $¡5Ž‹-fáòŒÑÃÖîÒ x\ÐxA‰¾@qEemL„€/W? 'K7¡¡  À̳ Iö¡PZV6S,Ï€SÅw¢ª|$âÒA§sw‡ú ?´ °¦vÔ»€E@¯„jÐ7€Ë‰4Ð9A¬V«2m‘Ñ” ” ”·ÇíÙÉ2ÿÊk`tÍ*(·dÉKK­YêG€üÐ2À,ï1HóÉ»öHðËüOI‚nÙ‰ð¯Ýhˆ‚Àº“\å?\ÔØqP4XTTÕJ>¯·,š´< GÀ0è#€I¸]0ã[Þ'à…í°Ó/L°XM¯IÝ,×ï¾sH5‹£§3´]Z’Ïã9Q]ïê°³JÀyìhò ¿‡ÝG¾¬´`ÆRŸ6áqè›5C¼8îá€ËR Ç*Šÿ>°ƒÇ„N‹P«`FWÀä ÐŠŽ€¨‘¡ŽÔø¿¤w*@Š>ØFœf+Ì+ô£†Ä#ïh--T¨>yëëÑâ@™ˆ6;žŽ#·à_ñ¨6 ”¦r‡Ç2vë#ƒ5ï(œÂ‰]ËÎÛxœH T*:vderzú/¶ï;‡÷‹$½wcá?aí®7å{Z"7qØOaâÐÛpî½^V…£j½âVyh)^f½ªÝ>¨A-¹WÄô˜@Œ–°í ô~ô°´s‹D«NÔJŠžFçÔ1§à(89p´NvdÌמ©F4åAyQž”7M´Ì ü]¬)ÿÔ÷Ò‡gA}ã™øŸ}v  Y|± x¯¬­‡h/qåàèö~ zDªÌcö­Çå:ÖpÆŽ€‚€ÚA?EPpß°ŒCG\.h³•Æyù—Ÿm0rtéú‚ÝPÀ.4²°÷è"X¼þ•DCú\Mx;?ìø±G§uìÁÕåh0`³¹!Ù‡i~¿, €‡’O{.hDNïb²—ê9줞wÊRSRäk hu@ÐO@Äì7#SÑ`žzáÿ·ÓìPêwÃÇ2?O-­…,— ÎÎüóÅz$I¢X±aÅ·4eI*u­XüGÀÀ ³Ò,&ýãÚ$4¿ @T¥y+Õkx¢( ¡95ÒÓ'O|´Cîß{ä ìvVeUá‹U¿ÀøÁλg÷±pÅ9¯`çKËä쨖—\Áßh„Nóå¤v·£Ñ\=j< ~ 5¢Œ­0@0²Ò'ÀŒt@ï jЧH O(Z€ØÄÉ/—ßOÚ³nø·ZÍð·K“à¦ÿTÃîÒø±f<¼°æ]Ÿ Ù‰á/ð¡ú³cß1áÄáƒÿE¨E“랃j ¼à#ÀPP[«+­r¹ P"éý‚ Ú,A6:£Fڿ⿟}tÍìûnühÁš´'ï˜e²a§p¦àÇ‘ã¼ïfã|w­551~|Á;ØÑí ¨¦˜™“Á-³ã|¹…wƒ@Ú€@@”5Ñ ò‹T=©åƒZ€ ¥>©çi: !Á)¤²§ß‰ÖX½ñï2 ðæå‰p- Å5"T¸%x÷¯FgJaÀãEÍÖ—T~ÿÅüùˆ'y0A€Õ·XÃÌóãèÅo¤¥x+*œD<Ž]j/¸¯$nÔÿM „ MÑÎ=Ψ‘ö–•WïÞ²ñ9aì„gßýb¥t×5çŸQ/¾pí¯¡¤¢PæÆbvÀ5SßD«ÿà^ô0Ôñ[‚×XÓƒ£r$8² Íy½8%€š€»&D:3РPÞK®pi4Nª~Z·lñZLH‰ÝxPÝ¢:Æ…Ž€‚€)£ú*h%MÜ.h·hIEK 4ÚèËví×þ룋o¸…ÊìÖ“¥åÒ 3Ï65µ(.ß‹Ö=Q‚adÿë`ä€ëÙm‹gRô¨G2N È?’á 3d+Ú#Á÷mH0ãûé½òuCçOôtVÐÿwŒrÀ¦Xr€úp€ß,«…!É—Òx^€lh €4 >YòŸi€´4GD‚Õ-ªcT×xàp¤&@P¿Ÿ”B×ÜdMRG·}ÁÇïý{ÂÓOŠgMþù ï-H> Wš0¬Ÿ0b@OÜvW’íÒôŽþ‡ÀŒ‰¿—¯ÏôŠ©C$5=ÌÉ­  ŽŸuþì|¦<›þÎ:v:³ƒÞÇ Ù³¦é:ãžÞMBˆ^øvZìA#Á#•¨õ¢}À‚˜{m2jo<@ËiŠ~_õæU+þ¹iårÒ3ªGt¨5ñÛÂ!£â#Àh‚€b`²ÙPÀÆ`MbÅÁ-×è§©Ñf˹h'/{¶øþ /÷yaÝ»ÿg}Û{f;@?ôSÆÑPJõB:ùÞïä=þÕ9‚Å–K/©Sïù% 7âoÔñS FÕ®@xà´†À†7ÆY‹+7¥ËR²i¿x¬šÓZtÝ?çB€þŠqªŸâoÿf¾Äd'Ê,à=´Ïr?úûgÓlÑ8ôWÆ‘P,·UÞò"ÑslÇŽ>ãaõ0 Vû´As¾µgι+03ÖùËq#ÉœÇå bÏÎ,\-·›¸›z± ̉kÁ™ ú¬áÒ'Ÿ\ ûÊ?ýÒª‹Âÿü«o;²ÃFÿ¬ógg}rÊ©>¬ HG^½}õ ç¶|‚éf9QbÊ3xžŒ‹s¦¼øïÃ# xý9ª&®í¨°¹ Ó*¿¯ò³[±¢%òqé}µ \¿Cãoš÷UwúêkrÊɥͲڭ¿ñùÄkѳ“»þ³ó_Ü~Õ®GFÌ #…#À@p¯mÅ[Ó¸^@ÎTƼðu„À;ïôqàžð¿ ‘,<÷èìêÓxO©®ØAj`~Ä?¬¼Å‚_ ;b’„W±Ü倞Ÿÿxí'Ò™·d ø™#`pp 'A|¯ >¹*mÝ\ô¹‰íEãPÿ”Õ‘ñ’nˆç„v8f³ð| 9"WÏC¶)¸£Ã_Ê_ÀˆÐAzHç;R‘q!@g÷ ´\Å–ý—Œlt;ûÛÇn-¢µà&ExÄUJ} ?=É0ªéô°Á§ PäšgñÏo%dÜ,· rõjÍÍ Ô‰‰iKÐÐÉO„I öj›^Ò­œ Ž@—! š0ƒ‰OtYAð+HÏ(å`Ûu×Í ¨îù%G ,6ÎîQ‡KžvË‘Ñ!zÀVB‰#` Ð@™ð;ì\` ²×,«¢$…„IØ¢YB9ašG@€Pý‘B¨^ižrN G ãXýb.ùaIkx“÷’ªOwü[»þ |‰`×—AÛj¬akÛ‘ù¯6@]Bõª$ü'Ž€Q¨ð—(ZÜó$ùØ0ï\Ðx)£Q ÒX `âš——–É3 ª?’J¸Ô2Ñœ6Ž@'!`6T>ŒaHÐr! “*X4¯yç>4æDiq‰WÀžšµ#š|xŽ!`•œ!M€Ü&€W Ž€ Q 9 2Š·@bŸ ªJ µË2©(=¼É›YàùÔ#׫לý °í±Å(LÖÅè50‘¯ÐOÙqJ;ü&TÓÆð@¨r! ãëVÔop‹–ï vÍÏhÀIN¥™¶Ôhóáé8Ô@é\@IDATñ†®žQ¦Œâ#€Ê ®É怨n¤•Æ[Ã$sÒ´€R|Ÿº~iŸrN!G #Pù@_†ð@pr! #+U;óIÝH—·3;žœ#‚$(B@ qýâèp Ž€”« Ç”ë8¿àB€† ÕSŠ n¼5L2'Mã`R„“ÐHÓ¤qÊ9yŽE§\eϬô›Õr´cߦÜ-Ú!%zJ~þô³ãb€¶ÚÍí { egRô¹i'eEùw=RS¾• ª©qÁ=Oýx¹v¨ã”èí¾¢!¥–L™ôþµ[Ÿ;ï©gÕ#MiF[‡j”I…{Øl2/|í™_ml‡ßsZC@’显yî·9ô; ’ל‹Þ÷µ=®žëV˜3çÇqßñûM&á¿èÏ!ÇI 159ApX­q¡á Kæ#8)!¥gjB¶b¸Wµ3Ói©7C©/øºÞéÎü~–ìüN{y¾Èíó‰UuRuÛ„íÁf?ý‡“€øR®-÷osæÜáîÀWó¬ã¥/ý9IÖ–\úà>O° Œé°"k%Ò=O=3ãTàÄ۸ЩçÐ~=¥ ÃúÁˆ=…‡]^N§:ÛKGááÌ_ñžœÍÀ¼d¸ê¼‹Û›%Oop >¯GƒRÀO.“{YãYð¯s{`û¾ã°¾à@öŽ}ÇžÃvâ¡;Ÿœsç[š³5„¸x)ÐÎäÃ#ù•©¬$†™ Œu' ð0îò—œÌ¸~ÆDØ;µÿñì¶d…1·J¹æh¨ö†úÁ$[ü}:8€‰ÃûÑ!ì=r >^¸6ëxqÅ—wÿæwÿãÿó2â&F‹O¿±û2ð«0” 'µ¹àÅñù}Lßv¹€ø­•È™ÃÜ>.ÄuawsUîP˜ìÐÓç9@Ô>^‡j: ÞÃÝh­^êõUn¦ôH´ëƒèé¥v£gfšäp$ü㢋®MÄœâ[ =TM©ò`âš­US ›ð@@{  €ÙV Žt\¯Jí€Û[ µuÅì'~æDŒÀ¡Šd€ìDXq[A£j7n˜9Ñ,JRNÏQƒ@¾ÉŠ F©gàýg„¦ ä(ˆ`ÑúG@­”Ùb5?4|@®ï6-ÕÓÌÔÐ ®¢ò-EáÏ8a!°ë4[p ŸÿÓiMA¡öƒÚ»Óy?þfÃ@0Ž$Ô~¯ €•@l`GA€Ö…ÓÍ>1·xÌÆe€†üX³º U**(øE–„„€!éÆ2jGp·¸¬«n›}Þrm@õ(Þ’,[6Õ‚»´2+s1%i˜aö  ²Ô² k¬vÛLr„~â­î…ÅOfZH(.++ Äh ÂÒ08]w«ƒ[b)âgÔŽP{’˜š2sm@ÄÆ_qãê\ä¾½ž?{cƒ;­øãµ%Ž4/ Ñ}È ­ÿ5bP Eå\0bˆÏêé€!œ ©¡öD0™óð–<%ñ)ÆÀÁ#ú•ƒÐdæ¨Ñ Ð²@´™Á$ä +`-ÓÙ¡u&#m0Ž\‚ì—U„z²ÿK«ïõùýàñF/ÌRzT™¶š¿^(*­‚S§»~©%•Má¡“­Âøíº°ïhQ«¿GóÃñjJë‚>œØõõN1ì'ÔžfüÄ3M€qÁˆ¦2ÅYT )ö¨~>gì‘-ëéô˜@H½ i@¥g1; g÷±p¬dÞI°ÿø·0¼ßè§façã0wÉzìè*°èžšW_8Æå÷“øì·ȯ_üdä÷í¡¤ýzÕ6XüÃxþá ¨¬>]²ö>f³yÙÝáΫ΃”¤%>]üæÕÿÀ„¡}ઠÆ5zÞÒ?€]O¢[gEØn)ZÌŸµôÞ/VlÜh fÿ蘽ïÈÉRøãÛ_Èùá>8ÊtÂÐ~=à–Ëκo)8oÍÿ^~ô'`µ4ÿ¯ÝçŽ zeµ”<ªgK„„‰=­†¶†£öÄ&ZvCB¢ªUq’H  ÑòÀøµURÍ[ ¶bwÞoÔzÒa–°T:ïµÚ|Ó€^Ó„€½Çµ(+*ƒW>ZL†Op×Uçcçb†› áŸ-Ç Á°þ¹Ó=¶ì>ÒHØRxF ê…¾ ¾ú~+x}~øÕO/‡Úz:yº™)BûOÀüå;]hé½w\yn‡}ßw]}> ]‰°ÿØi˜»x- AA‹Ü×¶ÆɃá¿lYh)~,ž-;äU²¹ OÜì ðéjºäA¦cÓÔΫõ´8//l(y¬†›Ъ@Õ>R3~–†æÎ€å›ž•?ÁÇ¿ÃѬÌ&Ą‚ÕÛIÍ ·_1 l~|ÑY¨V.oVm—…Ò|¿yÜxñÙrÂÒÊ8rªf7F¾?xâ4L9ЩŠ|?(̆ÞÓôj랣ðÝÆB90¡FÁa·ÁÔñC༱ƒ±C,†¿ùjêÜð›ÿûiè ?¹d’¬’ÿëÕp´¨2»%#=“ _Ï 9ëÕ[÷ÂJ¤ñXq$:dÄ—N ‰8Â^½uއÀ¿­•ßGZŽw¿X»PµN£îQ{Á5އã%å-¾÷ß ×®4›/›,¿kíŽðõÊ-PQ]}{t‡›.iÉòTÈS¨í ü¾Y½Ê«jaÜ>påcÁ‰üµˆÞÙéЧG|»®"ï$>Mé”—Ÿ ¿ûù5²ðµ5/~³qòÀ9£¢»h«2CBÙçßm‚Í…G Îã…z?bòø­—@‚Ó†ù¬ƒÍ»ƒ…¾ ' …goF^µG‚uÇýÊs.òà‚:‚ =æÁ€„4&Y`,´Zù©ã—”Ø /¤§ô‡´¤>rÍôùëàðÉÕÍjéñâr‚ X„ü¾9r‡J÷$TÖÔÁã%òÏÔ±P§Fêk £Q#°;òEkvÙ„¼>Ð4ÄšíäN3/']î¤Ý¤§¸P¥‰Â‰KN.˜ôyðÞW«Àé°âÔÄL虑-øA~Uj>@¡aƤáð̽?’‡e£÷†sFB …§NÈå¸üÝå?8í0,Ó ƒ ê)°%lø3Ž@µXœÇPÀUGaNh¯möCœŸC½…¶å€ÊcìàÛ”§›v¿£\ÓÅÅ“GB5N ücÞw²±ß‰ã8Z5pé”QJÜ8ŸžË7îj4@S_agC†‚4/M„(n{¥'U?¼i* _ÏLTÃ;àëU[åç¤*'A…ù&X¶¡&ìã‡õ…ôdZÅÕz <“QMnBCH²KP«ò›¾·i.4åPQ]‹F|»duüâ5òôÑM yüøš‚Fì[÷E'¤Eh+?*$<–¬-Ò",Aj{ šÞ¨u{à4ú#•¿Kå4k ÚK¬-Ø»Ñ0’4¤)PÛP×ÿÑövè¦!§-š úos Xð"ˆŠ€zçШÀ@X„7|ë@è£Äƒ2øGö¿Vly;‰z(./„£Åk4hôþÐMËVçÏâºuò@–î?ûñ…0¸OËB>“ uøãpj€XBÁ¾c°WøükùɲЛʼnæL6+·ì…_ým®¼Bá\®H†|ï¡1ù @š†a?»F3ÃBZ@–ü¤-xâöËZ|õù¸ ៟¯„_üå} ÕýEg C\ªå¸-½W ×NŸˆ†Ûä%}é)‰p×ÕSe #‘†·ç¯“]Â…hI¶á†+§Ž…/\ëwÀ‘Ù~‚¥%lÜ?¼üÁBYH"‡¦p®8 bN@WÁË.’£i*o*w Ëú€œQHupÙ@> ƒAÿ‚«ŽÚ>í¨àb´ Q  ¦Ša´@AÌÂ=O=³œ2{ó÷OO¥s;é|x$ßñØSÿ: oü#·\ÜŽìâ'鵿‚Í{>’w\uÞk͘óc'ÀÔn‹\Æ£þ–¥E“¶!ªdùOuî,Ôã’7 lÙÝÛÿýžÔ¸To²ü~ÒHüïëó䎭-gC¤Å é€–BKïm¯Wm]Hà!áK;iù¸ñ’³Ñ9S?¹£ÿ}?áæËÞLs˜r )Â’Œ@Ù3úáÎÏk`õÑ =ÀÝcíðȤÆNŸ‚©÷ÿÅ÷ÀöÂÛÞ}éÙ›‘ûR<È‚æ`xð1lû£x{ôI¾~ÎôúM9à2ã9—<&þ6úÜô™R«ÓúD³¨;è6å-»,@cÀãÊ=» 5ãê΄=çLH´iÛÊ?ç»ÕťΟ ,-VT×ɪí5hÁOi²ÓÛ¶IhM <[z/{;wµ@tŸƒ¦¸“`@ÏÉ@9N]ìÕ Ûp™b.4ª;{²¡)õ³½¸YHV¸a8Ÿ `åÍÏ}”k‰4Æ ‘‡‘¦8ÎH‚†~gÑ¢582 àôÀópù9/iŠÆh‰¹ ñ,Aoãú}rûKK yóÅŠz;Ú|õšÎn³Ên›×lÛ/{$CN² ? g /¯ NëgƒI\Þ?füw#"š¸`Ä  OžÏý¼¿ðG2ñ;|‡ÎÆŽrˆ>™QQM–õ×ÍÚ8¨ú’lèˆ$l:凥h@´÷ŸÅµ2üG )ä±G›5­/fñâí̇:,ÑÜÌ 0 ÷¢Ê%X¾ùYrÁIî(ž_Ò\1ØÆ—vÐ<_]#°áqV4êÑÀ„˜š0ÌpÞ‰w.è´Ÿ?æ ¤œÆy€; .“§äþÏÐ|‹€Í¨  `E{‚&’}-Ž@S*ê È-˜ÜbKzbüì!¯ZM#Çñ=tZ¸©ƒaD¿à”±°|ÓuÊIËdÓòÁ­{Ž´ücOiµ7Â6¢ÆÝOhG/þÒüd„ zr[€¸+gÎPlð{ýÊTާp*À˜ :.÷sG?ªì&xâôؾ®Ž¹iLz@ ÐjÁˆC:'5¨÷Ó‘“ ›vŸ1žž"¼»÷e(®rsÙ˜=ŽkôT~œÖÎE@BF¨U5¬ÀWtn½‹éÛ’]=`Bþ°¦à59ßo7üú圮„è¼ÞÅ”¸dVŒëüã³å¸+`¢ì|‡vÎ#ïxE¸Ñ¹á¥]~íʾôÊÚ÷ Ý)îñ’ Ùß>tE<·F¾p|¾ì y1®BHL°ÉîxzÅyòV¼1 ¹Ë²8V€¿®­SÞÏ8¤9ƒÓEÊC~Áà„ ! 4Ð8úÁXW\ óòž2êae›a··®Jç…ȧ¥qcóûÈÛç¾:÷[ùr ôßå›`Ͷ}ò>VKc9–~§=(ÐõÜÅk¡ýL5È50¹ô¥Í†h+ã½sлßPPïµ 'Ôá¿§–Ö»Á°ypw3Ü9†¯Ða1r’;œ>ëÃ^'XÀ…V tz¶˜pɤçê÷ ¡]‡¿Tîõ|1yÔy{]Úf÷ÈÉRÙiñCžôî½nnG<Ê*k€¶Ï¥£¦ÎÝŒ]~|ÑYèŽ7òÑ}ò^Üš·;îÒçÄ5øtÐ+«™£f™hüÁ'X{<(˜qðÿÇ ]@g8¶PM˜Œ;À…€¶êˆN~#çAcݤP»xÝÓèF¶B¹×ëóèlp \‡éP úäH‡M V€ŽJÕî{òø/#-IöºG÷VÜ™6Ú‰§PT#Ás«CÆ€¤šÚi1žxå¼pbŠ€ÊG€ L‡cš·Ž2ãB€Ž «-R/ûÜ.[ŽRç.…Åëô?-°U÷´Á:œû'GB-miœß·‡¬ ÍmÞÓ4¨]éª#7»Õµ¡9tõozºþŸå5Pë NlöI5Á}Ü1žŠÓÚEHÒ¶ ´DPI¦L.00øYŸج‰pñYRˆßyèse£!å¡Î.zâ6»Ï¾ý%,]W?ž~VL©ŸŒ6´]ò£/}U-hbú²Êì›Ü°âpƒ!¾ã÷8 `ãó„6Ï6žøæoî«du" Å“9R§Å£aðÒØª*Œ<ŠvèŸ{!Œøز÷C™È%ëÿz¤‚¬ô3ûš×Wzà:™¤Y¸e.9Eb#úsЀŽÖ‚úwõ5Å¿å²ÉJ2Z=0b`®¼}± § ôÖŸðÁËkBíÖ]8 0.G|è wNo| x}}ØÂÅWÑqÁ§¢ÃM³©¦Oø-du&Ó½ðÙŠÙàñÒN©ú ´‹ bÍÙd= ¥u"<²°È9êüšÄ}Ñàÿ9gF@¥~,¶0صÏ\ˆ³R7›ípõy¯ƒÝš$sVYs¾\ýpœqi\v¨ãdQ-œFA€B·^œÉW·FpΣA@’BB*¹ ˆ®ÛHÖæO4ÇÍBp¾;tÏžÇêþ€æ.»n»äsHIT–È6JÏ08|â48Žùj¡¢¦¼^_ˆõh1èdþ1æùß_€¾©_ƒ%à t3¼8#1LŽÂ‹ÆøŽ—º×<–Xýb®³Â<›4“ù.î9ë(@ül¾i™r!£¿@@LJÛò \C^]ë–-ÓÍ&3˜pý5vÓ‘âÛb|kŸÐ{]¾Z¹ ü8à‚qC`ê¸|°Û-Š@Ðbâ:ipÝ´wáÝo®D/‚å@Ž„þýí-pëÅóÁaOUr# ¨£_º¾–m(€ªZò.ÅT&K%rôÖ§$hÇ…vý)à üÛÿ¡Â¸dªù_ºa–=Žº±¸ý‚Ü& v^øÌP±,’[æ?Ñå„ Ç n‘ÿ’Zîþ¢ª=AÉ+3Ño^ž´K`,B<×ýXàÃóˆ?*Äâ¾ØÜ±èˆpÝÜàÖ›ñÇjX^`¿Ïç“G¾s—lÀФ ÒSq£k©Ž"ÖÄz·jë½ðùŠ-ðnm{ÃŒ³`ø€^ò¦6¤ˆEHKê?¾ðøpÑõ(èxP³qþ³ü.¸aú‡òVÄ„ÁŽ}ÇàÃoVá¨ßNç>ÈHߊçÝ(„æ cA‹:Qt@}ý`ôù? ù÷"ÿ»ÿ³cοú-]3þßÿf :ªƒrk% …R<û… {â–Òµ÷™EòAº¯²¼Çäò_¾iÜ8c‚¿;€ÛU'k‚+\V€DÈJŒM}4BÝooñôñ‡€$£@ze•®a…6òû8ö²…ðÍê°áµYé!ßôù párÚåãðòª:xsÞ ˜uîHÜ:w˜¼½-›&h/=»…+Îyæ­øf%ÁÑâu0ÿ»ûàŠ)ÿK7ì†ùË6ášùbÈÎüŽÃí}]XéIÀp¹¶Ê‡Ûe³ò?*æü·D«KÐ#áüe›¡Öœ{ÏJK÷–¢Çü E¶\ùHñŸ†AõÛ•òŸ2v(übQ=ì*Fë 9|å Æ©€öÆ·Qê~{ñâéã “ õc>6$ye@´ó›ñ‹!…ÖúÐ/½€¥ëwÂÂ5» Áa‡n8ú7Åf Q ¡ q2ÓS°#¬ÁQáVš®‚i¸Í-mz+A`pÞ%¨v~–nüNì>²þúñïaß‘þàJ(€ôôÏw_DtÇ*2 ÙYo@ié5ÈÐô"Öü«ieu`ÉÚ˜‡ÐikOØ•0D´£èŠ@‚ÇÆÄó ¿n³\þswºáw_LAMÄïÐ%ðä^í×J0¾V÷»¢Lù;µ‰¶}Š&›zÃkb£oÖfY·J©A©t{<°u÷aX´¶Pº§uÀ%áƒh aäËï·Á¶½Gd:‰ÞX…³†Þ“†Ý+ uuýQè' ÝÿÝe㢃’/¿ßÚ!ü³w¦´AÑüå›e À5¾Ë…&@ˆŽHl§÷@šç$H¢:Û×äÛY´v\÷ÛOG¨™Œí#€ ÕpB5‚¤% @eŽº±³µZL²@+µœ´Vœ–øtÉF¨­sËôÆR8wôã0~ÐÏ¡¼ò*°ÙŠd €Vx':H#aÅ©‰O—¬ïþ K·Û.\'O@K¡é©5%Á_!Ü;Z€ÛGXeÃÕöÒÈë~{äéã•&À$š¸& . 5L&HJ+È ¾Þ +7ï…4ÊKÃmj»b  5²‰–´ä¨B`9Ú*½D7ÑßÞÀ0ðxgbž)(ü|Õ倦<‘F [êÈ¿·Ãø_ºa§l¸Ç1¢Ë5Mù§)‰} #p …zzŤüY¹¹î7Å™ßlEû2ÎíVF8GBA;€Z\÷¿aÏ1yÍÇk-M´Baõöý²ÖÂï÷Çp4臸 ’Vt–`¤ø]Dßêí{;„²Ä§Ue)ÿDÑ·jû˜ðÏë~¤%ÀãÇ#Ëžse#_ ¼•_ðpEE<ò O†™PFB¸ÐíöÀÁã%¸nÞ‡–ù¶HðêÔ¸D-<„´z‘îöjÄ{5޲¶v*?‘¾Œè«­÷wÿ4ÍRdÍ”¤NOôÕÕ{ÚÍ?+wªCF­ûZpüešEÀ-x-z 0¼€ Ê`B@ í|òTÀ~ìiù?ùÐj ÚˆÆÂC§dº@»¦‚A@ö‚HŽ€È€–ÑGtvÿä*„üh9ÈôÅ üY¹¹îk¹œ9m‡ßB¸9Ö†HJ(yÅ£U•5õ¸öÚ„óáØÊj4mDcyu­L7ÑO|D¥•5è$¨®CEK£:ù :;‚òØ‘Ž€Ô|D{Môí埕»‘ë~´eÀÓÅ&ÕÊ® ×@B€„¾óƒB€Çƒ^úð0™µÏ>ÑH‚Ô€ý´§A´ÒR•è×d®Š6›NMg2Wvÿä XÁm²·›VîT‡ŒZ÷õP֜ƎG ‘‰/$Äµß Æ ^:”©DÉÀŽ,¤}>QÓZÆ6i|Þ.ôËš Æ û=Ü3KÔ†ø‘÷Øí. ÑÄ3 XVÀ,÷ˆ†¯pÓЩg{øW—»Që~¸XóxF@@êϸÄÍÚ¸M‚a!€ ©D©$?²·V4~fL¬¦(?=Î?-m ÈG4ÓA¼îë©¶sZ;,ÉbÙÇ®|6„ÀFCÔ÷‘…½ˆ ªžºAÔcÈtýŒ—H+-K'÷ÿz8ÿQ—¿ºÜZ÷#ýVxüøD`Ù«‰Ø”ÐA2¸ö\üГGâ“Óȸ2„@(!Öѽnuþô‡4·‡n–^Gœ‹ˆóß®ò•»që¾n¾uNh‡!Pï®ip÷@A˜½•u‡QÙùF h•ÆPOÑ­¢øhOhÑž÷¶'-ç?Tw£Å‘×ýh‘ãéâ“(*BjöÆ _íåÃPB@{ÁŠ$= ÊI»Ë#á%š¸fsl6¼‰æÝ<60rý×F p*Ô þWpB€  àBˆÅè—÷_sáx´â³Âd2ÁeSF¢«k¿õÊÍ{`âˆþ¸Mväü«éW2äÀ@$0qM@~†"¬/¢Óf>ãòó`MC#è°ZpS—nu»¡Q<º!Á`ß±b8|ªN–TÀ¦Ý‡a@ï {ZjDâuFZr³tZ~0¬ï•`A§5;̓ɴےÁã«ÿ,»«ÙNG7(«:ˆ@ W’[b lÖD9ÞÚ‚×a@îE²PÐ,¡†\6È v3À»½2•ƒº›á-øx{ð^MzªÃ‡+DØ„‚$Pð£ Ò{[=pÏ8}8)Ró4yÔ@ض÷(Ô¹ƒüftK†¾ù½nÖ©£)×SF„¢²j¨B§T,-*ƒÓå50nh_öˆŸ9ŠŠìŠ`6Y¹Ѐ>¨†ÝSÑÅ® N•½ìU`ã7oÙ† ¿&é}?Ž”­ÊS'œ¨ªuË›ea#ª§‘–Å»P €þ0TÖ…ù+~¾€»?lÿŒpL?ë¸iú'°óÐçp¼d“¯ºî$ò_™i!µr³ 4ø€:ýÝ¥¸N¿A ñI…7àÌO³Šš€!ÿO¹àºavý‡>±%~è›f›¹¹¡YFz@õµ¨4äaòóï6ÁIÔˆ™ñ›h²ÒS`ÌàÛ KnI»%Øéª È6½SôóÙQGžär`ù§5ša*ZÙöò)£aÙú]ò÷BqÕÓ´B·ä V¨Y>üG ðVÔ(ZI€}h´ªª¹øbd­ŸÖ¨ ÁLv9QµÙ|ÔÛI»€¹KÖ£ Ü‚óàØXxªUjQÊ'9Q_B@²«òp¢%v›=ûÑÔ·°Ë—àÿ>ßmþÜ0ýCè•9Q‰WUw’]=•{=\ä$š ¨&ƒ=GÉÑ“rÿnPT¾³µäš|^xÚ—pÙó1ãÀÖÖ× árB;F&û€'—Ô†A!`x†H ðøõ5 !£@2ø;Sh* “=mÖ%oØ…‰sÒSáÀ‰ †ìLyñß9±D@¤ì«C7n|e€ \.¨ÀhíÒíñâhö L5 ‘@†N|û‹FÉŽ—ÃË.Ä%„¸õ/ ê0iä@Ø}ø$””‡,­Õ¿kõºàà|˜ŒëýGö¿V^ëÏ謨> z¯7»•ÏO|tX-Nðù«~' ¿‚Èa£4Z¿ùr~>ÞWçÛáÓ¡-˜ßÜè:ÔáŽÿVË*‡ ^eGjE¡à¶ÑøËê–—Õ©óÐÚõ÷[wÃMO†•[÷Èþ.}oýw»lñüÚ§K•çyÙéžê‚/9¨<ãND@ÑX$ !Hƒ&_ÒÝi’<íµjË^HEƒ>šóM€G:ôëy>,ÛôÇ3%ÕÜï´ÆÿÏ«êáš|[XÜÓˆC-C—ô·Â¾r?|µ·¹oÍ1Ü„ Å¨Ö?&;ÁjòSØ·cóûÀŠÍ»Q;¤?þÃf’GÔ$ßÿ=% ¿Éôâê¦?^{R“„vQ\&ð´<ðõÿ, 3vóhu¸R൹¡‘QóÚ~²÷èb #Ú@†‚¯Ï;7Úä]žnéAÐmøµ tè5|½2d× ó–mŒ&OÃh7µî:E €«WøòÀ&ˆFÀ6ô ž›  ƒ[5ýÑ’Ëòˆ6}W¦c´Ó9ÚÀòˆ6}W¦c´Gÿ:m;àë2öÕôwüźE@ô…–"8ÀƒC-5œdĬvâ£EK×D£×[«CKü¨oéZÆŠ®îD)rî-åÛÑÏ$ÉÞ!ü›u2'h‘üíâ_]ŒV÷ßülT¤ÁŒsAö°k~"иw1*ê8Ù«ñ@4:Èý\ ƒ W(ˆ3[{Çð•Qg$wÿ1dà5qÐ.ºcο‘ë~'…Ö@OÞŒ$Iôe•ÌïÀ³¡„±Ãa1ár/QÓÚÒ.»U¡»½uøOFŸ1—==¹µ7ÏŽJOôÁ¿EBßÿRôsüų:_¢·ªŠ ÿ¬ÞÓÙ¨u_-¿6’¤&‹‰ MŠÞ0B€Ò¢}794IO´ãšw´âÖ°µ2ÑF4öè–ˆT‡˜&eö-à /»æ+@}½¢% ;ÎŒHôÁ?ùïM÷u&;¿K¦/åÏÊêQë~Äàóq€$Í!7ƒ3¢%‰  Œ†³!„¥D›2ògnÂMQR]Vœkп¿v-¶‰6;j,ÒSdºÉ¨‹ñÒ¤ÏxËÒQ9éIà´™Ðܨ3¦ëÊDŸׯtÿ´=t–ïXW²wÆw}d¿ÑþÕånÔºF y„¸Eà›¿<ÛRvbÛ¾—>ذlÜr9c†™±ó§Ã‚ «Åbl—ÈP8»FmûRMD[ŸŒD™^F»Ú-k¤oPc0´w&j€Ûi6Ÿè"úúd$wÿÃ{§CjRü§;…ŸH_Bt}ý2\íæ_]îF­û‘âÏãÇ&Á¯L G\ ÐB±B`£¡ `›Í†‡rÓ`EÊÈ-0ª]µˆ¢‰¶ ”“"ÓKB ÑÏx‰”V–Ža0²_j(+Ÿ…¼£{; ¢‡è²[¤åßû; ªß&) !îQ`Ez"]6Ôµ·ü›–»뾦 —Ó© ]U>{!7 dH4>B –M¸©íN¿Ýnà ~l¸»™z'›Ð½mÊ*ª#Ó…wD Ñ”Ÿ(ÓHôÝD?ñmPc€Æäà†H™PZzM´YvH:¢‡èÊÏNîPþÏ *ȯÛÜ!|D›i~Ý&p!]C³]1á_]îF­ûÑ–O§op*@Ñ`Ë©?w­¿–…›Kh_ëöùÚ=Ng*QêLv;8œpâ‘™ì„ì ·¾õÂéòê.Õ€h Zú¤Ù gw\‡4½D7âc1À0è“Ó †öLAðàäôõ]® ÑAôt&ÿÝ}ÇaXí†.×`Xízèî;SþyÝoÜ’º½>) hW/¹i87ŽÄïâAP­ ¾2 ¥BÕªÛ`¥Ó}âŠÊZº~L‰Ñ*„:Q«Õ*wþ.WxÜðx<ë÷$ÖCv¾þÒ HÃí~Ép¬3ÙÐizá–³pœˆ4$¬Ýl: ZºZÂ`DŸLÜåÍ{Š†á»»C·Ô/Áá8í+¢NG6¥8àC @WðEÇa\MìqŽ„JK÷¨ùˆ6!Ù ¬Û.±*æü·TîF«ûêr©¨ª•|^o™ú^+mN“çüVϨ4¢ÝŠ6|㠦ũU!@¡Óçñœ¨®÷uØa' ú>ÚlM`ÃÎÔù$º\(xQíì“·:í‰;þÙªÜp²^Ú:Õ*x—“¦ l`"“ú䀖Ò*2$û„i&ÈE @RR"¸]2D/ÑMÖÝÄG´¡5 †åeàÚq OIpªøN:öAbÂV<ïF̓;Ú×1ù e€´ €Œ­&±KùßuªF×àÖÏÖ,(²æB)žýBÇ ƒä€–Ò*2´àTOŸ(ÿÖÊÝHuŸUFjG¨=ñÖ×i{}(#˜Ÿ£FàëW3¤úZyã l5k.}°^ÛË¢æ´} µ.H§Ž^•œžþ‹íûŽÃÄáýÚÅ-©EÉÀŽìh„íC  Óœ ç@ú=Éæ†âº”{%(ÅŽ™ô¨„v’f!6³'": àÆôtF9,‚YN€ÜT;$aÇŸ’’ÉÉx €t½Dw{¦p­aÐ1HÅýo”TÁ©š~P‚2ªÒPª³¥ϱ[JI.‹þä?Aö`È¿„ü;4Á¿¹¦Xì}¸ºÈcB§EBì>3º&O€VtDãO2óÈù·uÿ­•{[uŸäM¬ùqS÷é vDBçG÷ï[·4úç&ƒÉí¢˜üâ'YŒ K±kÙbBN£Läô»¯æ 5¦t}Án(`³}PFDØ©ÊÞøP N˜B°‘Äé º!Ûç‡v/:¬1ásÍÏÇ`š€:}'ú(°™$H±›!ÉiA̓;|§¬  5%E¾¦©²èn¯ È%›AH+ØÐEq¯Úz(¯qCy½<~'øD' J,‡öŸCü‹ üÛ4Ì=–}Lù'áÊŠBe¨üÍÎ4u¿¸Ú Å•urÝ'£Ô”D”TÛ7+' ¼]U÷©æb;"I¢X±ñû¥»ñ– \ pâ,H Zä.˜sKE¬U!@ýqJNÿ÷A¸wï‘S0°wvK|„ýŒuövJ®`¥ Í·“ÊÝŽFxõõnÙVÀ[^•e§iä ç?v쌽jˆ)µ6Ña6‘j&ðÔÙË6¨ €< hÚg(¿Tõ/ œN7¤£½„…!4¢’µ%bÃ:J†‡*˰.9ÿ][þá”;«û Xîûì…Z\©"a¹÷íÙvK «œ[Ф…²§öcǾcÂñC¾@iH‡º­i‰tþL§ ž5´2€kZ-E­ D0=åõ»ùÿ™ûãŸ?pýG Ö¤=yÇ,S{:bʘCê|)Pã¼oX:ˆóïnÜØî:RΗ¬¶ï•Ù ²2ºÉiÚó½Fö¤â':ØjRý“­BB‚S>h€~'úbÂÅ€„2 àFF¤:ŽV`ôsþÀ.*ÿp˽”V©à¢GBv“ŒÜœ¸‡E{BW–½×l?D)¨üþ‹ù_"d!Æê¹ÚƒOKÜ3 8|“Ûy¾<°pµ*0éœ>REÅéš×½$L8ûwï~±RºëšóÛ=-ÀC¹aBA 8"·A‚Ó) ••5PQïWRŽ„D˜8j0¤&aƒˆM!È7aþkP(B¹.&ïm¤–§µÛ$6€Œ©ó§çô;ÑI4Æ:°™øn ÙpÒ‹+&pÅ‚Ø`;Á„€H1`,°÷rþ»¦üþg*÷ÝGO+u?7+ òrƒ¸HËê­ÊžÚ%°å‡ïÿ_M .à #&°ö†Èå!NÀYÜ!l@3NPwÂCS´*$ÓGJ«gõ¢¯Ö$wK{¯oƒÏ@ºuÖ¡=j Ù’;êhÙòAêiô»ûH1$¸åÑoVz2 ÔW³Néˆ8Ð;åçUI@ï$m­Û§F™:~«¬® º:2œ /.[ j²&çØÚ…ç_å¦r¯«CãX\)“ˆ0¯N?²ÒSä²¶>vUÙ“€€ »  w|ºá»o7"´4Làš€h V£éV¿˜ë,÷Ï#òpåÏë=p@F©íZ²´.èFªú¿ÿÙŒkB:ÉN––K7Ì<Û j d¯jÔ!£]u|OUȪPÒL›ÝÒRñµ¤` &ù6¢ô ÁÑ}Ð3vö$ È×(tÔè¿5bÙûšb@ÞiõÄ6³$ο¶Ê¿µrßw¼Ì6ÔL¡1,ùÌ:°\u¢-wJÜeO64@€ý;·Í[:oî×HJ}ÃAm µ-ÔÆp!Aˆ§P%ÑÎlu`ØuTÖ<´€€–…êmé¥MßéÃ%žcÑÜç=wj±8ù¼;^xoAÊð¹Ò„aý„zFíG€(ꀩQ$ü‘¢2p£ œFæ4:fH?¥‹ECHïc½“ ²gÈg§‡¦f‚ ™ñÍxgçH‰¤wP`|Ò™óßµåßR¹bçIuŸ>À‰#ɆªTnÑ–;¥í¬²'?´ V è÷UoX±ì£­?|¿ É 6¤¦áLm µ-ÔÆD/Ùcb´‡`†1ªpZ€Û00Z8ëA`šúxÉ[mÓ÷Ë×nܰïì™—ÎüSñcOÆFFJrÚ¥Ô—à°Zƒ½M ‡óÈð£P` —ð°½ñÙ 9Y{Aö^Ö¶vÏžkåÜ”ç¦÷‘ÒÉùo\5›â)ž±ŽOåëAM˜5SÖî8ë µK`46åµé=‹Í™\‹“gQr„<¿¯æ`á®k}ý]}}mæIímBg:Ôš. ñL¢4œ©wpÈ…ó¼ˆ[+_- D3³  ù;’âi*€Öè™êêj`é¼O¾ÄëoŽÝ7oàà|gbRšÝéL¶X¬Q»´á¤üÀ}r1_G©Òž}y¼Ø*òÀ0¹=²ÓSRI¨†ªêšêÇNis¯å&eá÷£oѺúêúšêòÃ{ wïݱõF¡ø©Ó¯l8H  ¶„Úú®Y_—<Ä ’ gý> ;â…¯ŽàCëB‰oÌ.€æwH`C)& øönß²øñCñè`ñð2üpóÏî±{QŠÓEÅ{þõÒso‡ŸšÇäèäÔTÛ/÷ÌSŒƒ¥_|þÞÆÕ+³{Ÿ©­ 6AiðšFûÔá“€„€Š†k ˜=""ñ°P‡3žD‹…êFxh ­ D3û¨Irg»Z8 çäÊŒFÿЦ¯I ÀÒïÚøN{LýšÀ¢ìܲù;¼¦F„Ž@Ü#0íò+F›LfY‹VW[{€:`šuâL Öžæú©]`ZÒ€„®@â9,üK–+ ÷¡Š¿?/wàn¾2 õ׃@ÔÓN6*[vO:Iû$8ð !€x’§ ð¶€qaÆÕWO´ZmÉtí÷ùÊ–/øz^²F†óÀˆ[†Œ9‰1whÿþÅx]Îî5~fm ¤â§¶‚Ú¨Ó'! ®á 6ƒ~§6„‡8D@2— ý¹‚m¿{øÊ€¶ Y/BûÈéãU_ÓM7 ²Ñ ž[†w.¦—Cñ‰_z½ÞbvÏÏxF`Ú¥—Jp%ô‘y”$ÏÊE æãµÖµ`L@gmHÕÏ$@@÷ôœÙ°´øˆ‡xB@ô‹ÊTÚˆâT/ê¶ÊW/Bñ@%I¹úƒ'¡€>nÒØð`³ Î?,`Ú¬Yy®¤ÄÑdFQ kV¿‹i¹€ ðÿŒŸ2ågŒËšÚšoŽ:´ŸÝküLí;¨}P Ô>PÇOg:è7®@â9àbfEÀ_Qà¡-ô$0>è#f‚Iõô‘«;&Dd0ñÜóEôdzxý÷ßï¢k8ñŽÀYS¦$&&'_Ìø,>yâ-¼.e÷?S[@µ L`»g¿cóÿq‹®Î*_pæbÖ£@\QÓÇM6›¤NŸ6úgvÆŸZÓ¦M³;¯ f‹R…×ý:Æ$C"8qÀô+¯¾™dcìxç¯]®#¦Y{OgvP»ÀöLG,qRÛ‰€¢ 0KÔPWÁCkèU`ü°œ>x LÞ…9pþ•×\ƒíG79ظñK–?sâ‡3áv&ã\ À4®§@í ¬M`÷ül ¼˜Ü-à«Î‘Y ~úc¿ÚÏ1‘³ªw! )ÇQ5&³0›|ãS@)oÎ;—‹ŽA8øÿ8Gà÷¯½v–FÉl BÕbz¯yýórWö±NÑàˆp— ÌaÄxe¹Ý|±yóvg¤× þü÷¿F`r~Á+8oë•N7G bDé–ÍŸœ=›œêðÀÐ%b ´2‡tÜ(0ŒR4¼à“„ÛNØ~ñëŸþ´„Ýó3G žŽ6¿;@IDATxåë¯iYíõ!Mï„®ùG@ iwH ñ•á” ¡…\ @ü߀2™áŸìšŸ9ñŽ@ÍÁ#³P –F|â4ا2ÞyæüÅ7êå’™kÂ)mC |ýõé(ô$ p)IѤ!C„È’tãëÿ»ìšŸ9zEµ¹ÊÂfÁƧÂ(HC ’J#ˆÖÑ\pÁ´ÜŽ@Ü#ð—×^ËÄ:/û@çX’h³p! îK=¾\ô焨٠®ò £fþ²þh|sî +üé7RpAÀÕ F‹ÕüOvÍÏxGÀ+ÂMÈ#[ôýÓwß}0ÞyæüÅ7³wãÛövÍÏm#`X! \’D{ÐTÀ&´ŠÞÞ6TüWŽ@ü €s§·2nÐQ6×00øY·Hq4#µ[[Ù5?·€a…QngÐH&áŸìšŸ9ñŽÀþþ‘¨6•LœC­OIIœïùäÜ`K {€`Lÿb—üÌÐ+Þè‘€;G ’é Y|z7ÌÂ4œô (¾0ý3L¬x4Ž€îØW\6í²9ñÔ½÷,Ñ=SœÃ#P\S2Aû3œâÚ=ù‘czÛÿ¢ËÊÐpBÀïÿþæE8+#.Åç üM—¡Ï_Ìèd‚Ê7€ ¼TÜ·z'—]ìüe*5]|* ˆ 'àÞ(Š*÷~Ÿûˆ ¶ð¨ºFàÕW?IÄ:?‹1gñ©?ë!hèJLp! ‚Ò4” ûJ—à*†`5À®ù™#ï”CÙ,\?í ò)lýõÏ~¶3ÞyæüQ hæF‘”º¡„€ªGÈCZ2„¾Ò÷ýföìM‘€ÅãrôŒ€âuŒ~¤OØ5?sôŒ€$Í1¡Q â(H°9¸& ‚5”€z"eÇ4\õïpâQ9ºFàÏo½•„¢¯ì&˜A¿ê\Ðu‰râ‹^ø#­ Hï8~éƒ5|'XNgÃ/~ò‰­¢•ùP«ÅÄ…€0*øÜÞ+˜‡LTƒmþÕ}wí‹Î8FG@”TFÜ âê`!À]RvªŒe„ØÅÝG\Wx#€USÀµ:.KNzcpÙ÷ö]Àó©F˜gâ$ÝÀ01ñ©?WÞ?mf2V%‹‰» f`ðs 2 Äy..DX¢†hiú¸”a#ð©?ªŠê+qU€]fU€OÏž½ßlsƒÀhƪÉdáB#̳!„€ áô–FÍž]&><G@÷`ÝWM|*@÷%Ê`,x)!§º2å{ªg>ìæ.'̳!„Ü1PY`2ñUaÖ -øÓo¤ 3+’ͧü¬{Ä€GÑ “ ­èe^"AÀIdÄE·Ñ@ “Ï>+χ²šQïó|Š?â&*<Ĭˆ‰-ºVßÇ«ÍØiVïY ¯ß5KÙä{Ö?}÷ÝGðš×þÏêzn´z_¹4^)B\õb¬Ï^á¼]zäÆïÎÇçŒLÂLìþóÀ$ä ,ˆk¢ƒÁ–Lü2äùPŸO¬.*©|ëî_ÿŽýÌÏq‚€R ˆÒIìúû<žÅï¾üìFdüâÓo#ëôI“gºõ¡_³ÚíÓYýGÃ×à*üÑëñ³Û¬xPU]ŸŽu™|ÃÿÅ ªÁ©îK¢tD ¾óü3¬îÇ[½o^fj!„ Í#ð'gB@¯B€pÏ=sœn«ù!Q”²I ””àS“‡ÕªLsøˆÁ=Rì–¤Ay9çž þ»þp£„WQU'U×¹Ñ6ÈüÛÙOÿþ”»Þý‡7ìxuùòùuÈQ&èÁ ÅÔùSÝ6OzUBÞøá÷9œŽû[­ÿظý>…׌Ԥ~™iÉý”ü".h©îû¼þ¿žÞuäÕÏ?›ê¾Þë}ëå$Áö£Õj^üÌÙ~= ¦Ÿ>ñ?3M6ëÿ“D±ÇÐ~=¥ ÃúÁˆ=…‡½‘šÓÀ‹, ~ˆÆ]×L…´dW8¸ð8úC@üêÜؾï8¬/8µc߱ߞ2þ¾œáýïýèÿ^X„,QÈ„½q(wþH´õÆû9#95íï¸ì5§­ú_xè$|¶488ÊJO†;¯<_o…8–¶í>H^/dŸ¨ S¶T¢”Z'Y¤“‡õÃOÙT²ÞžõmWn,?¸ê®ûûßgþøWέ‡^þ™¥w$zÿ(«ÎÇm.wæï{tÍkSþ‘A{Š]¹MQÂÉþnUzí½ýúßO>ògζ™ä¾Í¯TWï Z¸,U”‡&ß6Ÿ”7èÐ4Hý ƒîkáÏxøNVž5äåÞk~`e Uv7–ì^ÔÆ½ÁåNÊ ù¸ïÚX!/Üp>ò“Û﹓3çâÉ!û-*|Í$ðëÿÈG$òÏQÔhóÎFùЧ›ò#yHzÙ·Û¼öž!ûf‘ûÈPRÔ  * Hг(–kîþõ$‹ÅúW€êõŸ©8ìM,ÿaó®ï ÉJ0,D)ryÜddåü¿ ¯» A£ôŠ€‘1@ùÔð þ#•ÿ]ʨ¦.àé¦Â‚<#çSò–ô²ow¸½â—wž× Of©÷Û@¥qf€U:¶UË—Í –óÎûI¦Ë•ñR¯nÔ«ÏQï •`u-,¿DY.êÑ¥SË(È+)ä¦G—<êڣ׳}ú Ìç cÆLìF-à ü9Á/ønüoÚ¡³ôÄRá´$¥!Bös;åÿcøðÓ°”º‘å¾=_(8=P!»´´9ݽF­‹hð­]‡s+œ ØÀ‰oÚ¡•¦PgÚn!7WN:ÅÂóÝN™<ù&ÂÍ?ÌŸ‡I)"Å’ïKirÏ/nð ¾Û%ÿM†Z6KV¦ä{:!û~• Æ/™ £Ê}ÄMÒÙŸG¼ Ì£ð–N¼§v[ÄË› `t%@ë 9]®[‡÷RÛòÐçlËîÁÃâÞr<4Fï@~ G¹óÆ0`šˆQ­šÜ7ð— ~Û#ÿG*k¨ôH¥ö¥mV+õíÞYÛ—ÿÒ!û™ÙY¿`Œ*÷ EñêüHZ"F®ùFV´ÞÐ¥7Ü:šœºñ4Àˆ{k•Õµt ¬BË­Åb¡¢îÍs.Ϥ%9R ΘôCT"ð 0Z¯HopøT Ú#ÿ[u p+VV$I Cˆ³ÞåWCö&÷íû@þF§@E•Ní¯é݆W2²³ÏA ŽДóV޶ìj´æ“Ý&+ÁVàJ«K#ÈS—ž½ÆqÆ1$Ç;# %|¹Ág{å뮃üh€úõ”þ‹tß ÙÏ+(˜ÀXMîÛõyüº™[F lzMo6²Þ¬d±! æÀFJú¡€~½d%)népä(ÛíTí'´J½%À(e! ÷žš |‚ßHåS·íiTú÷ê’ŸUæ1„ì[¬¶>|»°Eî#ÈAàU}ØÂ¡áO(§`D±5²€7¯ ÐCĢV î>„¢¿T‚XÈy¹™ŠÕnƒvÍ•¡‘¼¥5¹oàË >Á/GD˜ã©GL¢Ü¬ ÊÏÍŠè9ySz Yâ°Úp’2šÜGüf=ûØv” ¶B»'ÝY½7â‡å͈¸qmödbO ÒÓÌ¢EÉâµ"®w,§:O`~tÏ.È ®%ÔŒc„˜å¸ëÍÎGz¢Î¨l#½?÷í/=Jû ›4ÇÒ§O®¤ªšº°×}ß¡k[¢/¿[Û$ MK÷Åû<äÉj±fpº¢"ÃËY¼yjH/(÷| ÅÄ>Û#ÿÛš(À‘YØx“ŒÔ{½¼˜[ôå(AX¶˜,ÊÄî‡[¼Þž F•ñ–òY²(4 /£È}Kì†=¯z}ðiЈ Œ´0¢ÜBŒJPP¬¼þ[»*æ{­ýz6¯ÑèÏøjÍY¼Žà@hµZ³~rÞ©Ô5?‡>™¿‚æ.]GOÝqÆŽƒØ<òòÇÔ«k>]ûÃ3è«•› *’¼ìL1¨]1ñäà½Ø)Ù[J½:M;gcŸ^¼…&žvr\ÿ&÷Åz0mþr^ ÉG7]rV³¤Ê*ªhÚ¼å4rH_ÊtG>œÒ,!>¡ÏÅ¢Pv†›Ží׃®šr:Ø4â¤vÍÖ=ôÊÔyôç»Ì~ÍEíóoWÓ' Òð×?—œ}^{­±"Deh…8 ÷ šAÓøŒ’í{å¿o÷æò¯OJâ[³¾¡µüл]:mx1]8þDí{ýî…©ümºÒO&Ÿ|ì@ÙQúíóÒ//›@ó2éý/–ÐFžŽkµòž…ôó ÇRn6ô«ýîÅ©´—óP3b ýtJcº¡×u eeƒž]›ÆI=o EM¦ óF’û–˜mv^%utãI8šGmäÛ8{Íkfcð† Zûq¡ ß´Àç]%Ø'ÌÔ¨O¬ Y_O?:{ ؇0êÝϾ£§ÿ;ƒù¿K´÷lâËó«>J»ö¦ ΩYÞýü;:kÔ­áݹ¿”¬-4‚`ñú‹ÎÔàE+6Ñ[3¿¦XaK»¶…v¾î‚3’ZŸ‚¼,Ú²ë½÷ù·4ø˜Z¬þpLŸ8¸ˆ†ý*¼îþdžã¶r%* ¼ñ¹Ž¬Qô|h¼5ð4X'cÏ¡Æ3Z"4†yë3òúüô—EÝ:çjV™·f|Cµi𪜮•…yËÖÓÏ;â@JËÖï ·Ó¡)€¯~<_zxàg?Ð, ÛY×+xâ–O½6ò:~¹„n»òÒÆ¦3áŽaLJo ݆:ò¥—·Ž”û–X {ž•€SÅEµ|\!Nœ”Ûv!`T%™q†°dð.™¯oÈÔ@ôv¾øv =q0?éX-ÝÎ4ú¨$yº [²µ_k ºЧ°³fEX²v¡…5à¡ç> ›~4^«Ñ»zìÖKi_ézkÆ×´­¸7õƒ±#hÔ±ÇПy%ÄcتqÁ¸µWÁüÿ··?§Ÿq¯ëÛï·hC¢W°d¬×ø=ãÄMXCþ¡ð,ß°ƒ`8‹18÷”aÚ=oò{{u˧ÒòJZ°|#ýòò³)œ?…ÈOß]ز†¶ñL ,ØîùÊê:~ß·ôû›/Ö,.¸ÇøæŒ¯ØSG§?€Ø44#ïÜWF3ÙBƒ^©Ãn#z3n¿`Œß×§EÀï¿rÒ©ÎÊÓ$³‘ ×-䬡™‹ìÁÞ¥ç§]Ö‰ìàoX6»K§­gߟ齬0Ü}õyAKÌh–éòŠjúhöR:ì A…xË®ýÁ{V°ì?°·ö=·±Â ËèQ ¥YÿN©óHeµvºw·Î”Ý ¼:u>GótÒe¼è»Õ[è“+5y’‚2ò£ £Ùr·’­¢‘ƒûÒl¥@Y\¹q'Í[ºž†èEŸ}½š\|nܨÁ\¶iiµ&ëÚ ­üK1o)§z9kéßó\—¬êªCÇiŒ)äËVó¾#j´~Ža0Ô®J¦òӮпy, Êç%ƒ.XO‡¸ƒÓÔc«OŠk0 váU÷4(ŸÊíâ2-ç†[T|ElbÅÀ Ρ5[vïikgǾRí—ÃÐ⊃¹<÷Η”ávÐÅl•€ó÷·¿Ðâ½_ÅfRÌjxù£yšñW«6kÏáßÒõÛÉà ú1Ü­ª¥ŠêíÚ®ýe\‰®Ð†' @|ÅJžæ/Û¤-³{íùcµJ=3X9@HC k¶î¢)gO…Ü3 Ghȡ̼ÃÊD-ùnÞ÷¼§¾^Ë'kîZR¯ú9¹áÿ1½*¯Ð”ñŽç.ÕÿÇYÂ0膋ÏÔ¶ÿýt7hvºýÇ©'‡†E%NdÔŠ0*¾`ÎÔV€ =kŠ`h ­cÙ²ƒxÏ¡ÃÔÃ,k 1Ë? 1†Frù`e`7ÀŸ}³ZNÐN¶ãß‘ªM!`­ƒ²#UâP“7f,b%³?[áNÔ”S4þ ÈÖÚ­»é›ï·jЬï°Â Ôš¬k7´ò/Åd¼¥œ kéº!Ï×V>™«X1@ß¹ïP LàXþ£+íÊÒ}%Ø£y€ £lú…*8—évÑ‘ª@oåÄ!E„ñÒí<Õê÷Œ°$±¨ø`}àº)ÜSÏäÞøô—7? FgC:¡„†õ©ÿL§çßý’0Ú@jKÖõi„î§ŽŒ‡æÌüǪÅßèH¢(_™?GŸ£DUA—ìk4 …‹ˆ<¨¢¡‚ÓÃu¯G!˜0Ñ^É&Ó\¶(ˆ¡q?Æ=oÿñ¹Zø½ÏÓKÎ¥û¯û¸Üd‹^Õà¾ÝÙä9:¹ç@Ãt`†õÖ9+õîÖ‰{F•š ¼/+5ËÙ‡…uçZ{¾;[@’éÃÖ à%,38?é´áôüû³5OósØOá¸âÀ÷q9"z+]m5lH§Ô cBÖÚùtbnЧ½ìO"Qø¹¸YÎ[#ÌtÁЬGPR­æ² V\ …xÙºm´…­Uçüè,qkp‹aƒŸ]0–îûë»ìÏr(b%2‰F[»P‚Hkîzk².ðÖÒ×RDÆEv·§X½œ5^3ð;eúS hdµØÙþ50Û†g­y«ex–Ã3ˆµêÙ;”ÃŽGXC=”ТY¸b£6މgÐ;FV8« :‘•ô¸1K@ àÚ®4¿á†ŠzG«6î`‚œ° €H+’-zýî''ð5›§0–ó¿ãØñ 4zh_VT*é»5[h4‘†£} 539¦0B©YÈ~zÂÌ„oùyä½%äOøèïkm΋0;Ã)kå¦Ü07WFÂ=ÆapÌ„á žb ç-AÈo1û>`Ê êao1|3}ÑJ-oð4_½¥©IX¤‘Î[|KAÂQO‡ÛB Æ}¯}²ˆM绵¡¨E,73­â¡—š,Šç /à2ƒWøk`ìóíKØ×þp&µäK"ÒÒo‹yØÃ`˜B3>üQâE±Èº”ñx}…ø¦óÙÓΡ¬´S…öÉ•ãƒoëjv|Þ‘”T)MPOîå´D“OÎ= }þÍÍ3ŠÆ Á=xÌ B /á£ÜÓ®Õ{ýšû~n a:‡“!ƹc%Xn½býë x|õ­W=eÌñl…@„OÒ”šcûõÔ¬76˜ßCß BŒ¢bG¯| ›j×èÆPÄþp²û3û1 G„yáÈ7”˜H Ý àƒ™ðˆ”0»áYßÑâµ[éDöôÖ¯ ‰ô0¶ Ç/((hø13Žk7]2ŽùþŠdoq¾ËÐ_\œ¶éûSù¾Ýìè'XFB·óÐÎ[³¾f%x¶6þ$°ýÇÙõ… >fµk|cÖlÞE³¾ú^S¾aËâ ´dõÒ§'öG=†Öólß¿4•¿·›Î9ˆf.ú^\ŽiÛ’¬·ò›qØÆ@hѕϹ>>¶¸hÔ]WMj598à}Ï•h;ÄaºS[TSëцôAÚzF\GR`zŒ7¡—CÌÍnoúˆdˆžt¨ã H¦øš:æ í;DZíÝ"X,6NG£þ¹rc ½ÁSï¿vŠæQØ^ø`.÷FÝtÍùc‚¯Ï øhÄBÏüw&­\»iÝëyòvNgÿàVçxŽv¤}1(÷ÌÆ zû½9þØCÚ’ÿ¿½óyÐ×åú DZ¢ÔèoÁé´IÕì€ % ½÷̸ÑÏö¦yåùÖÞײno Èþ÷ë·®zíÙÇÊ÷ÄMîãX÷·Ä:MÂòožit nàEƒîž|ŸÿO-Þ,/DŒ@ü[°ˆ_ßõñzuiÙ +zÂÑ(H=íD(H~ ±4ΨP[R>zsèiÇò¤ ¯p>Ƴ³s$¼þ×mÛ£MECÜ=¡ñUЧ—*û<ÕN8»ÚY.»tÊjwÖ¢QðÈP¸ïÙb}¾µwu„¬Koí‹DMUÔ %@±X¤% z(›<ÙØkrÚ\è‰'8˜ú "7o›+§©É-†<öñ,…O9PLé‘ Í?΋gð´0Im#°ŸýSub+ÒÛø# e<6Lç<]Pí«Ôœ¶Xñ¬ëS4h)HÅ–¨|ZC %”€ o£žE@’¹˜xê0ÂORûÐ+˜9"ɘHþ»ÔúªƒVNeÉÐËÖÆ£OR>Ù€@J´–ðæÔ-?0MË­D Õ8PÖ(ÿúàM©žo™¿ôA@µ4¨2HP\?|J(uæÐ®-„»+j&†©S¿|ü5‚¢QãÇbç pË ÿoî2-ð‘Qx•|D†âIêÆ3'ÌJF,/fÅ2ÕøæÙLAK€ ߯›J€Q+ALÃÃt7#œ!GsHápAVü¿ ‘8ŒÄ´ä¥E°r ¢J‚àä*‚Nµø€/ÄR^»â_¼ª¡¤ÔC`Í»CŠJ'‰œ9œ®¸-"ÒLçmJø,otŒ2¢9Qد٦Å8càcfbï÷àxëÙññ±¨Žžp=—a­öšÚ:mÌÁFO~úÂUt7äŸñ’ÈO=N‹î¶˜õYËAtFp@ ÌëF˜Þ%üN‘.B ¢K8¦Áºm»µµ `!ÄLÀL».:¦~­â`@XÙ­_¯ntêð~Z¸^Â4¦ "O7_:^[{^Ï»ÜO¥ºwáÎÊq/ÌNí-/Xç«cbe¿&:œÖmßC1U«P""mbÅK@B0-ÄÁʃXÇC’1ع}ÃÉ\¹4îÚ4ᎪýÆäÔœ\™Þ€´žçìƒ0·>ƒ{ºF£¥ë¶k±ð­ÜLÓæ-ÓØÃjlXs¡sÑp‡®cÕ½^I° ›xõmX+÷!¶þ;³¾Õ‚íäeq€¡…Z\÷Qaþ²ô>¯gУ !œˆ$‡w#L,V\¶n‡¶±{åްè¦czuáF}k Dð›ÉK¸byÖ¼ÒÔ9¾a²ý˜‡ ¾á…˜Hú8ø°ÜIbV ^˜ŸX#i/OЋÚ[^ T£táµ(°<6ö·î:H{uQ¿ß´‹C|ÖçXÅ i½É+Qçr‡)„ÿùda‚r"“~Å?N¤£¨Ê\±/·ñAÀô–„ „ži2GþC/Ap6÷’&bõ*^-s¦èÕ8¸H;‡UõÐ{aYàõ¼˜N¯nyÚ¦Ð!æ;ÆöÑszðgçkK¿B z…£ú]>ñdmÅ@4ì=ºœ¨-vtÍï MX0G[¬%°‰k9=ÐJ^7ë&”ì;ÌÑ Ú’­WN:E»†p¿ÿwÙÙšÒ¥ÿ: 2--=ßT hÊ Ê> ]¹%@[‰³ˆW[„E‹)…[]´¥çåù$" ªãÄÛT‹TñÚšÞФ'dÐJÐåX'°¨ V+TÔ½1b¨£wŸ ý¸=Æò+kW}+jX”è[0.,, Yõ ŽˆèåŸvü@ÍdEbÀdhÿ"im[Q]ë¬l8‘Ç+Ë Bà1Õ2?7‡&Á¡†W¶·­ÕE:r›XÊuJ0–·Nж¼„æ½A\5¥¹:Ä9·{ÃTJGC¨p£ùî„æ%]áÀß18Vj÷;ç¦+‰Ê·é-z%À¨=¡¥¼Û9ÊxÙúÍ/ ÜÇD þÐ8ü›yq£#ÜW°â€±ûcy8ïé áa±XVÄ;¾[½U[ë=|¬@èåÁ_~·†Nå¸ï¡ÑaIÀ½X &ÔµÇ=ŸW :¶¯öÎ3N 9”ÚZB=Or?yè-aF•ÿö¢MyÉdeö([²Áʆ!5„ð]ÁK‚ë« Ü׈éâµrgàÀ‹i«ÁñÛxî}Õ{âüŠ´OÎô–€£ì('k¤`bÄ0À_[pÂÉ‘Ä)ä¥_å…þüæLm1 Î<æŽn¾ô,ZÄÃw=󦿴wþØÀ0hô±è V‰;M·B¢HÆùgžÀéÏ¢ßñ".ú…g° ”‹ß<ÿ!ÝýìÛôÁ— K’áÐ˿ޒc8F#d(Úò‚U¡,ßõ§·4ŸÈ}›øïxú mÅËö,l!«ò¶$  ÷à^ÌÜ$¼2í^azKÁn$Âê|Oßy…Æ[ýZ×ñúëmQw|é9'kæLñ,Òüçƒ×6y´{6ÿáÿ.Ö†ô+!â&Œ÷‹1‡>• É «)ê ÕOyU88¢7%†&Ngü$#Ë{Š¥¼@AFÀP†Î@¿¹ámX Žƒzúã­—ór2š•§àE¹Óáð´Ññ‚ ^4h®Ø—Ûø!`zK€~œÜhJ€þ3‰F\.ÒýHŸ U"M?TÐ?‡kBП—ûfÅ`EFPª}§He>ô+@œUÄy¹5>ÓÿZìäøAÅ.ýñÕLo ¨¬iŒÆ—i0K@,l8/ª#ßXLýg+kESIöSÿËÉF‚€Õ³ýdž«¤y.Ã`ÒÕ{#yNÞÓ>L¯`tA©T"˜‰$‰@kè=Þ33&ðÖî—×$fB€glŒ ò«ù4uŠ^“;1!`êáLSx¨÷»¸&·TDKh rØL¯Ï‹¬È­D@C@¥ÆøÒ qBaj% ž§¿ jm\[Ü#·TBÀ'å?•>§Ì‹é #Á»¦V¼¹NbÙK’¤õƒÅoùXžb¡t—ýPìôxÌY¼&Px¾šK©!§r„lÔèúl{޽ä¤-j.Õªî@YÈâ²0Z–ŽMr®ÀѪ(³Å¾ÜÆS+NÝÒ·X/ZB÷³U½~,3ZÎñø]NuæPº˜wl&B`7©âiŽÍ^Js¾[G?rªfH¶“ã^„èu^Mípe-õ®(¡Ë6PŸŠmäôƧ² _ÍI%ÙÇÐæüAœÿ:šË‹+ý„T‚u$ÙùÇŸÑÏ5•ÿè¬TRö›~å¦x,âº ŽºÙ6S±{%o7Ciœ–ÙôÉØŽ<ª‹ö{Ñ®ÚãeC}ÿdJz–…Ï_è”[_^>ºA­õÛÜ_Uİ|ºEÌ­°Y]VÓ‹†ÐøÃÁ ñõ?ž·‚çæ[©/.’Œr \`Z#~j8Ìë¢?÷îlº˜£üa±€O4ùjÏ3XÙðƒ/S~m)_2—ºWíiÏãQß cÀáõÚoofZÔ{<çÿKÎÿ¨¤æ?ê tðƒXBŒj/IÙoŠXS<ØBg=@c2¦QmGÓp墷}¥ö;ä-¢ïkÎç²P›–e¡¾üèxV4áæ*rÙ„[*J¹L²ÆZĤعÑFä4hð>v”²†„¿m)[B㇠ Àÿæ¯ä•øœ”Ͻ^p/饣+/zRV^É ò^(ÈOFÕDõŠòþþK¨_ùfWòÙüÑõ(c ŠÇ…ߥ¹}&pþ))ù•çŽ~^o À2º‘’”ý¦H…ã§m àþlJòË”Ž3¬/Ðòš‹Ó³,(<ÝR>£Æƒ¦NÅÆAŸ$—üDœöö[D¡ÇøÿªM%4mÁ*M(èÔ1 €@ Êx€22uÎrú~ÓNm˜üÆ›&† p@˜°}F‡)"oP@Àø™:gYBó/Þiæ­C'ûú˜­åIÊ~Stšã±’ œ”ñN‡(‚;(༤_Yhô ²° )‘˜^ pé†*uÑ[ f?(•¼á{Ÿ/!L/„À(^ÀÓ[³¾¥ö¿ñ&xû¿ñÉ"m#øéTSFo³F¢òo¤üFË‹K7 _C£µô¤ì7EGÇûŸG9–ýš é]wkDŽå½=3=ÊÂϺúqÿ¤?ç>Qe×Üã¿ê8ôÓãͦW t÷Ãm; Ðc Î㡹˜ÀËvâ•Ä:b %/àé;(Î^²Vã7žŠÒÂ,€ÃUµtûtÔ@Kù?§ïœCå¼4l"òßÒ{ÍvCH™•31$†i¬­‘”ý¦è„âq¤ÚCÇ9?éP @Sy`œ-Ç9§Q9ÏÖI‡²ÀõrpVksGÝ´4ùã1¡!ÅM¯tÍÏ ~¢eGƒûáv`úƒ ‡€ššZúzõ6m@2œÃñÓÚ9ð„ ó—oÔøßà?V`Úf$Ë °½|ƒ/ð·`Ù†¸æ¿½|ýþÂüÜ ‹[‘ñÝÓYöƒ@ñN(߬ޢÍH† žHöÁf(,X¾> Ê‚zŽÀ„}¡>ûr›8L¯vÎ ¢Ó–Ðü½š`ëÎýTÅÓó2ÝŽàóFÛo•ÜÞ¶û [¼qÛ9½£Üë)æi€F&ðWÁÃ!ñÌ¿‘ó oÝôJp+–0)ûMÑ Å£²ÖK½Ø;ߨÞ*ªëSº,Ì™3ÎÆFгÅ7°(6é ÀHàÖôJ@·üì <û î‡î5ÿúzª­­£ ;öñ\tÒâ„Þk”cÄ(ë¶í!ó«5@`°fë.-â™ÀÅ+ÿFÎk´¼aF‰ ýeáå_|wÈPºÊ¾ÀÛðx¨Zý}FÚGŒ…ƒ¥rY¨Y¼ðt¶uZ¡ïö¬7Ò7HU^L¯dgºµyöø@UÜkÞ}àpØo(ø>v¬×†s(\«bIX  °L´ó$âX9Z!B÷‚oLD>¢%A)OCÌðÖ&4P´<êŸC7ó¯üëÓN•ýB°u×AMFBó&¾{:˾“px8©:a€ôïŽvqœJuJ—UñO â£(Ÿ÷åNB0½tAZ»mwp_¿óQxÅ×ÖÕ±i­–,º°«ú{´oa/Á#<‹|ƒä#ZÀÌ]_m2I}.ƒùŒWþ“Êx’^–Ÿ“Iyk¦ nÞu Ù›ÅwOgÙ׃—%¼Eÿ\Gï»8\q*—EUƒJ€E•J@²ä-%”€aŽƒx­eÓy8âXÔämPêê¢%¢+Æs-€hù‰ä9ð¯üGò>3Þ3¬¸Wíµ[›Ë¿øî¡t•ý @¼Åom ý»â¹Sµ,ÌxÖÕ—œÇjx)T“kë>'žØÉ´ZF %”€^];Q§ìL-—ØÌNz‚ùO˜á`éXS}ÚÉØW¡À0ßb8yi/é1ˆæùö¾/ž÷Ç#ÿñäÇhi í×3ÈÒ&–ýjvx¤ÿîé*û l%z4 ´ïñLܰÔœÓîÚU#Žå6±¤„ˆ†7V„_~·¶™Ù\˜Ñ"N׉E6Ž©£âßñ0§üÇñ“*©\^…²Oa`9jÈöüeMý©¤ì7ý\¦xáHU‡9ÔO’2JÀÈ!Çð¼ú@a8’-Y»=¤ÐþÑîÃÃÞÏ ªyTÖWø/03 ±'Ì\„;z ̤!{ñÈ„0™ö¶ÓŽä}ù†:Pœ¥ÿîé*ûA`xGâ¡GÃû_=ÓËÍõñxÁÃêNŒ$lSF Èàétgž88Ù‚9â^cµ`áç&=›†˜U4„"Ñò-ž7QÎYSþ£ÅÍ ÏõëÙ…d»k¬â;ÏúzUÐÖøÝÓWöõßPâ¡G£ã÷úöžÅÕ›œðPÀš³ï®ÝÑñ\¥)£à“Ò— ÓL±4ï›3¾Ñ¦ ŠÏ,üqR¬žbÈ¿p„ó6ŽÿB£-x5=¤/RlÇÀ°¾Ä+ÍpéÄ3ÿáÒO•sNJ¶†U4wî/£ÿñÒØ‚„üÄóû‹´C·Xõk_ăõ퓉p°Ú3â·•ñ« âÂPaÿ¬ ‚ÉÈ©A(’µ¾KÖÛðžÉ§ V„‡+ªèÍYßð”ÀFG©h_‰À=w_u+yÔ­s]5å4º÷šÉt×O&ÒãNlRésÊ0º÷êóè¾ÿ¢ñ#µåãýž«'ó*Á¢å¥#žË?c2 ýëT^ÔËBî¾iàï_¦ÞXDÇ¿ú%õ½ãQ²85E^cÍÞ©€=ú/þRcÀ/[^gñúBrõÖÖéˆ,¤ü;ápæÈAÁ|bºìç߬ Dzsé„“hüIçm¤Ëd².(?7‹~<éTºÿÚ)ZY¹æc‚¾ WN:…Î1PÜjúí‰S^¡§=ÌGAŸ±tö «hÈØßÏÙÙtì™Ð9¿ØÀ¿utò%ïS^á‰ÚõÞC¯¤Ó¯Dùà¾o“ª>laçV«åc±/·ÉA å”4Ò—œ=*ØC?xø(½òñ|Ú±ïPLˆŽ9~ íÜWFû•SAn6<\I/~8—Þùì[ا†õLÓÈ1 F±Eâ‹o×Òû¼Lï1= è$>Ƭ…KhüÈ!1ñ‡£éÍEóŒ`½ýWÞLû>|ó«ÈݳÕìÜBkﺔ6?vå:“òÇž§Ýž9p8 ~â ²åæ‹Çµ­·¼”Íþ˜zþø–&ç£9ˆ%/ѼÏLÏœ<¬?Ö/Èòâµ[µå¢+9.F´Ô«k>õíÑ…¾ý~³–Ä©ÇÓÅg"_ÈtÕ*^ÅsoézËÅ?Þ›MvR?* ï y Œ“ëÏËdǦ;Öoëó S÷QÔ¹÷Ú¾ü ~#o¦çý“T¿W;ÿ¬ŽLrçô¦¥Ó®¡ùÿ=“‹Ž—œr·vy׺÷ÈîÊ¡ƒ/·GµG~¢zqœúì™GN᤺iÉ)´ïÜ»ú&NIËd"D å”仯®t!÷Îa–a5¾w?ûŽf·†ª¹¢j¯E‹ùŒRDß4T‚k¶î¦™_­Ò¢wÁìºç`9õnðΆ°‹£.Y·6•ì£Õ|ïczh| ЧuéÔ¸è‘v!‚(ìâÁíao‰öùü1ÉbwRéüZºe‹fÒΗ§ÚÝÛ©rý ªÚ¼†²†œ ]sÓi¯Ó¡Yï7ãaÿǯQäêSÜìZ['ïØJj £‡Òˆ}‚7mÝ}€^Ÿþmà¾(‚MÁépÕ¦Á©‡]x½‚7x™g½Ï ^†!¸9‹×R;æâÚ^V˜aA¡œbÅyä±ÇhÇíùïo¯O/qê7êÚ͸‡—»euL‹§^A5»šd«®rŸ¦”íþ†ªË·ÓþÍŸR§'i÷@aرâUê?êÖ&ÏDr ç?’û|;«µ …”ÿ)ÊÃÑGC3rF Ì[J*À{pßît ÷Vô=Õ[vÓ›¼.÷üå¼2GW‹”°\1Æø÷…Y¥ +ýòðÀî !;ÃMå’Xöa¦å¥{kêêI¿è‹¸ÏÈ[WѪޱ‰­<µ2„¬Y9”ÑwUmX©]9ôù‡´ÚÙWÀr'Q}é~òU¡Œ¢Ô1 7ˤANLs<ÂVAw¬H9uî2B½=yÝ_Ú¸Bçÿæ-ÓxøÄ„’Ýnå¡‚®4†Mÿ°Ž-ä²&hoi9ê<çͶÍ.8–*5³¬úìv:²)–Öýcò{Nå{—³{äà÷”Ù©G. ,¼N;ªza0»…Ç%%æ¥8Ù$ð}è•ßtñx:¶¡'ŽWAóGƒ]Á¡x#%„f…=PšrúñœV-aZÈå°iÑÅ}Xç]L]Ä9ô’:åÄÇIH¼#Ñ[W÷"ªÛÈ_車~ñ[ª/;@‡¾ü8ôRØãZNÇÙ½WØkòd|8kô±t5Ëwbùn  ³3]Tv´…0Ó!†™¼¬LúÑ„Q4öÄA´—-d»ù'>:ù9YâД[‹ÍE®¬Bª*ßÖ.þ{ ¼ºõ;‡6|õxð¹j- …2r‹‚çÒigæ3Ž¡,>³ BE}™Nù7J^SZ È0GÂqïâ³FÆ6£¡^¤½øP:ë¤!Ô³k½Í~˜vBO_ïí`/éZ>'éäd™K pt.ä^ü>‘…à¶çU·Sæ€aš_€êmÌcð†0;¶8 º‡¹"O%ž]òèÚóO§S†SV†«Ý¯ÈaNþÃ%œ'ÿ3]ó Àu8Ša¹£¬x£,™™ÜYÙ­©Øq6:u?‰†MxоÿüWl1X|®¶r?ï«äÊ /¤ÉŽêõꇦ½l'M²n¨l¦¼ І‰ò"V®˜x²f¦lijŸ¸_¿ÅbCÙ!è)ìuüÀ"zkÖ·\A6Zà ïyåq/L_¢WÕ+„žŽÚ÷pOß–ßµÉë»ýðj*mzôòj® 4¹YwàèÜ•ž¤ä!€•(Oâ±xÈþ8vÔsØ[7[ë9«¨ (¿¡ò¯¿'Ü~9÷úWm.цãº5¬tˆ4*ªËJ¸çŒ~®¶ 7[üØ e ¡‘çÿ‹6,zŒölljívf¡L)TWyù‰äf¹‡£µ\(xeE±)8â‚Ü&´Q’è Ç éjq­¥-Lø˜âç°ƹát+Æ;<е ð­ãiYè}õïÝ•ºòø'†"6ìØLC ‡VͰS·gaH@PÁYRÏŸÞF{ß{‘¬® ÂŒü"!gaoBz’:îó4ó~¤oÇpftÎmRhéÙ¢îµ)p¤…ì#Š'êòŽ¡€Ò#æ’ýмúê«©Ž{ð™yýB/5;ÎÈíK']ð•îúJóÈ+ÉSGò¬€<íÞÌÜ€“d; ¦ÍzÊu AF6äÛ£:s§§FÉosï-£pf >qÅswîÑìØWª%{ú9Ò8~ìÕi´}Ï!újÕfú;%ÂÚ°©d?}·f+.³)Ô¥õŒö霬´ ÿW³cu:ý\ŽÀ`Ø9°Ë¤Ë¸£P¯ŸÝÓ„óe—Š2ÝätðÀÞ¹Ùr:QÍvv2”dà(zó­1åvÑ' $Ä €ïÁ޽¥ôþ‹ƒ¾4P@¶î1¿è衵”ÓeXkPh× ‹'“#£€ºõŸ¤ýÄ+fðt[ž)Ûå8ª:¼•üÞæCâÞTÝúTÏ¥"o\•|6ù¶0^×â¹M(R ˆ^8R}·vz|±¦ î@k4oézZÀžØ‹X¬¨áæS‡Ь75•-˜I…—\OãÎç¹þSiݽ?n“ý}ý‹ðÓSáE×Qù·³©¦D*z\Œ¾¿`åúɤÓháÊM"p†–LÅ>1^^£C?³ˆ§ÐvÎˤw¾Øfôì¶ÉßæÅÏÒè ß¡ÍKþBžªƒÁû¿zû¼à>v¶.ý‡ökr²áÀbuPÑ 7ð0Á£á.§Á9õr‘I‹jy—Wu‡r›dÒn8 Z|­ØDyìÐ3g$˶ZaÃÍn'õëÕ…f/^ÉㆺGõyi×kÏRç³9º'«íÑ‚åŒ8•v¿þ×h—Ït {”s㾋F hŒ=Ð;Ð+¸÷Ä!}µ©¹5ºeŽ[KÃÈ׎ì[¡ï÷rEÔlO¡Ê²´wcd³j¢~‘œþ¤³?ÏÒ:¬quRçwç¦ú.ÒáÇ€yóŸ̉ðîæ·aŠáóEͬtdñ<Â/Zò)£5·4†6ù\Ç 0}᪘^üÑœ¥1=o´‡×̾/&–öløˆðKGR•úË´E8ó*ÑÌ)r( CÅ m,˜¦ÔøÓBà“?špaIþ\àÑnµèx®'¶Eþa®õ°9Ò >ã•3ä7<Šïئ§ìëq ‡‡—Œ_¼ª35Ê‚ª²SQ) Ô‘¤…€BJNnXýèÈCÓÇÜw¨¢£±M÷÷§•€^€øågÚµÂFvTo±(ì”ä;VEþ‹ ;±2®PIö1±&—ÐçÁøŒgþʰrmºÊ¾þó4ÇC¡ýÞAú[ µÞT5Ê‚ªþD«È¡E‡nÓF znT0?Çmçñ5âÅTŒ©¼9lêœãf®˜h%F`ЕDrÛ-´9߸•òþœüâ•ÿhq3ûsâ»C†ÒUöõß0,?íª?^›¡öÁ›“ ‚f. 3ž¶ŸÄš@¥ÃkäÙºÿÏP §)3i¡ =»XÙÀ‹¢`Û5qý=Á`&F’±ÐKQ¾;È/\D^ÚË«xi ïƒ{ÐÎì>´7Ó˜qËÁøëÓ93.ùo/^©rèwOGÙ×Ëñ`Ëà~o1òéo7Ä>xo}òÍ]T¿ï*¨¢*ïŸv×®q,·‡@Z(€= «Öø[ÉÆÞñ6^ê¶G6[2Dd³»Q¼€'‡U¡~]s4~ïÈG´¤ÇàØ¢.ä¶)´¨÷xò¶±j´ï‹ö9ð³¨÷8rZÔ¸æ?Z~Ìþœþ»§«ìë¿aKx8ذªö|òª‘¯­ O7ûà<9­æ. s挳ñxF0°wÅþ›¼dšíG ú¥ýïê°'„ö†¿ÃáàŸ6ꙩjñÍËÊãŸ^pe@“\.ðéÐøÿ"/íS<'0p»œ4â˜:ìʧ¹}&´7¹„Þ~Ê\©¸‹;nùO(ÃN<ô»§£ìë?OkxôÈT訿 -¯¹XÿH‡îƒ—£þ®T\`î²P·dÁDîÛt˜lŒÜ5ñÞ‡æu(°òåAŒ¬ oÎ?Û˜ !|±6:§ÓANnXN'uʰQ§—ªÙ ïÐኵÀÀK¯l uã0«>J øG>¢¥P zwɥ⮙´5¯˜¾è{^‡[`à'ùƒ[ƒŒAδ_˜[’~Jð"¶13úÝÓQöõ ¶Ž‡v{‡ÒâêË;Ô" x/q/ JPÞã&cz|ÃíóL§àPY”7åaãÏÏ—‘<ß¹gñ©‘OõU×Ö×ã8úÖ&@(.nü]¼Ø‰›55.ö ¨ç‚€ 0C¥ÞÜàü‚oôâ‘h){äQ}}=m¥b*çÕÍNß9ºWE¾Vz´¼„>€…½Ç±e¢sÂò/ÞYë©ç¥Ì½áÆ#ƒr'îMò¶Ùp w‰@IDATû½>oM¬òî»§›ìë¿c$xì®?–—=. áÎO¨À¶CÿxÂ÷á€!X%â]@ö}>_uH&šÉ]Èõ˜§ÿ5?G­=üC‘ˆÅjã¡ã:d >ÓekT% ˆ¿¿Þw üH„4&%f@4¢v^K`ffÕÕÖQ]]7¼õÔÅWEvªeÇ 'aÕ4[ 2Ýrs` ¼é@ˆ€i€˜E=zgøØÀó3Ý”Å<‚Oð ¾Åp@´¬´„Aqa9,å´½,Ÿ¦ ¸„zW”PqÙêS±œÞºh_׿sˆ€i€˜'@;/"Ò;Û°ü †ÊV©õuu‡Å±‘·õµu‡c•ÿ–¾{:ɾþG†G•z haõÏ©›m3õ²¯äír(‰Yñq0 ³àhWPâ_h²ïñ”éñHô¾R{ä ®êÜxפË'ÝåY“èwÊô#GÀèJ*ë=5uJ57Ø<Ž- ¾…ܘº8¬ÌLV<äá^0úÁ‚?D5䮫¥Ãõªð¨TÊ×Ñþ[8òô*ÞÆƒüÈçSyè+­±·>7þù6VDÜnô]oÙÙY”™•©íƒ_ð ¯~ä#Zj ƒŒA¦­‚v•×Ñ^ê©5ʰºëk)Ã[A_}´¯möœÇj§j[6ÕØ]ü¦áÆ?ßæIxþÁäòTS]uˆ¡\Š.…O*øŒUþ[ûîé"ûú9­.(õ£5Ì —S'U“Ër„e6>=Y„+F´BB7þ‰* Bö=55û!czh²Ï5ÝõÁ„å_Æy r•Ö;FV4!Ý¿«daNçη¿y7<¬_L f@8bL=l˜ÁÙ4Ö ˆ!ƒZ²±i>ßWC5^V ü¼,*N¯—›DÞÆJšRÁå‘ÊmŠŸ²ìÜÐÚÍÜ?”“ÜÜlÊÉáæü‚oð+µ…6u²¦–*jë©’Û}ÅFUŽ|ªlx1”–pä9Ø8„àèÒò´Ã€£r%J”«x(Ë–ÜüCŽx…;eç¦ +˜h~¢2l!gár›Ðsz~üà³ç1ý/UþÛúî¸nµ¦¶ìë¿Zûð¨ÓÕl/d3}[uAMÅ®àëÜÙ½‚ûú}Yp¡,$¸.²¿këæÅ̇^ÎôlÅuÆSŽá~ýIH”ó[g±e½At4®ï‰Å†€Q•€ €ÎýäÃ(]¼fk>+1µÂÁ7ªZH^VD£¨x¸ÀÆ…œ­õõ¬£{½”ÍÞz°„. ìàï‘Ýnãá Q¸ (y¹¹Ú>xtÇjüFŠÃQK9Œkùo ƒÝó߯ ž?½-¸n§#óÏrÄÓ•}G¿_üõfæ ‹˜ã”·pü&ñœž7ð9úì‰G˜ïœXä?ÒïžÊ²¯ÿމÆcÃòWƒ¯ëuÚÁýÐd–Ⱦê÷—/™ÿå:æ#)r¯ªÞëEžÙê÷᤻Ž&u(B¼[n[FÀ¨J8F/M«íÝóÖjE¹eSÉ>Ч°åÜDpE4öNÕA*OA#Çx;Lî˜1PÃ=am¼”AŸ?`)ð7ˆE@Á;|X-ðQ`Ë¿3``€@ZbsÔ^ªû— s@¬ᨣóùY½y—R²yÓ,æí<ÚO“1Þ‡¼‚rÏÌh<‚_òsY¬òŸˆï)`ýíÃñ™H<Ø•(HáÊC²ñ²¿gǶ™1ÈUÂå~ú_‹T»E&øeã³àçIû£*¢GAõÎÿøÃ·.¾é—W¾5ó›N÷_w¾Åx¿1 ?_ cà¸aê ¿×²P ‡Á†ž°Wòk«');â}èÙÃİf+ ²Àp@F†[ûa×Á_¼)Þ”ó† Ny-¯ÈÖQù÷ÔûˆåÇï÷z.šþ¿Ï˜W êâ'*D!s"±O 8²`ÚÔ© r•p¹Wj·]Ì}§üü·žwwýº§±žhí»ÈkÉCÀ¨J@‚ê)+;P±aÅÒ'•Ozüµi Õë/>3&I… ¬¶ÏŠö? ÃíÖÍiÐã 8 6ø +€B“‘Rƒ ¨t |+¢·ÁÔ÷b ¬pDã°€ÇxS¼1(w4**] :7cWdA¼7Ùù‡Üì9XNËÍ}­ºº.˜ö€ŸPŒd Ðä^ð~Á·åÌ ·Ä*ÿÿt–}Æ5H‰Â#73ø -É. BöW}»èùŠŠr!û —{½C ãü ÿ¦×Fhäž0º€„µvÁŒÿ-ÌîÔéUÞ¿ž>$õêóÇ(±XPøÅ”;ôÄôA4ȰxØ9ãáp„%@Å·þBˆæÛáÚ=âa À; 9|1+!høíšu 0|%’â‰A™³‘×n…Z`°f¬wDþÑ B%¸dÝ6eËÚU-_8o93†øa¾ä rf$% (÷Ì—Æ+øîÔ¥+zqÇ*ÿñüîÌODÔß>"Æø¦DàÑ)»ñíáÊC2ðÐËþŽëÞúnÎçß2WI‘ûYO;{}õgi((䳩®óÜœFPäža0²­•!æ¦Ap«¦¿ùŸ·&]qx¾zoéaõЉ§Xâá#€©Eã~ZãÏ΀ à<˜BÈdј˜aÞBãŽ](Ú¡ Žƒâ^íÿ ð¹X„¡:çw»M¶"OÉÊ?ÆA1 À–5«>ž=õ½™ÌPÿÐÂrù‚œ¥—ÒLî™7,tïœýó¯€þa<ä?ßy‰ˆ’ýí#b*ä¦xâ‘£s‹ W‡^ö·­_óî¼ 2irïóy„W¥Ï½¯ºqúPð‚Ü1fP4KƒvÎ|û¿ïœ4þœ½þѧÝü§ÿÎÌVÜK=ih?å¸âžQÇ@DŒJ&zôÊá(üô€x(xŸøáÂQPœëÁˆƒLc ÃÞùÄ6ÞùÇ\hL…‚'4œýÞúŠ%óç¼µòë˜Â"Øâ§·M h"÷Ì«6«ŠLéþ}ûF%Ën¬òëwg¾"¢d|ûˆiã¦xáÁ#{A WG8Ù_¾hþ¿—-œ @Òä~Îs]²jª]-ÔjÅb}.0²„Dî#+€Iø`ì=7Äòµ.žóùµß}»þ´ó¦œÏÓ½ÎáÊ>— •šívªy¹™ŠË®óR㢥Ð?ô8štEáφ‹óFÙ†æ9ôXð9Žƒ) úçóÄn³mh~C›=á „ÖEd=Öaî‰Tl_¿vþ׳¦Ï©©©àN?LT†Ç²üç0Žq•ÿÐïzÜÀg»6¡ß:ô¸]‰%ùæÐü‡‡²s¬®fm©<„æ?ô84Í–ŽÃÉþŽëg=óÓϪ«+Ëù¹¤Ê}meéÕ¬Qç€_Öó×N¾Ç;»%ÞåùŽG@'ªÏL„i½"T€˜ µ6UUGýŸ¿ÿÖ»|üéà#ö0h˜;#«ÀávåÚlv'Ûí[%¾IRb8‹W=oX±zÃV±Ÿ¬-ÇØ¯««©9RSYYÆàúß/ßÎ½}4øPP¢RÄ>†„?€Q¬Ì’F-Ê=_ÕVlêÙ*ð)6ð¸ú <Ø••ït»smVŽÇ,©CÖ'yåe¿–eÿhMUeiÉÆ k7¬Z†ò×ar¯*ê/ƒVUyÎ8#m*†}¹Ñ•§Uz¼E¯M4ìúJ²nýŠ¥+ù·ž¯k–ÞBaÐzMºgxWR¢¸kJ׿‹´ßzî™gÄ~’¶¢‡¬à'|I 3hì…ù(F¶0{µ)÷|&¢9YáYË¿ ¼/å^ƒ®ãÿýô %åÁpr?ó Ûx^ôíXí (T‘­æóbAˆÐ-ɨ˜A v¨Ñ«AðÅ1*yh¼¨ 1 %y‚Å¢PxWRèˆÅy„\@€‰_X4§R>†"÷dü 7¸922 9"à[ʽ‘¿ZsÞY„\Bî}Šïvà?cî;¥[’0‹  ~•!*uÍ{š·šóoC•© 0(I¤ƒI|ä$äBT†0õ‹ÆŠ”Eã¼ðÏò)C’È“”{C~žˆ™JDy²+d¤Ãå>׺ßÍ¥ð  Â¾ªÕbã¡7IFFÀ,J0„°CÐõB/zE°èá;€Æ_* B’i_’ß™?Ȉ¾B„Œ &Â?\3º€Y ’”û ¦ÝITy2/d¤Cå~ c^/þBböÓ‰w{0D+Éà˜I P¢‡Ðc‹*x1€Æ_(B¥À $™J“ü>ÈHȆPD¥(ŽÅõÀÝæú/x—ro®ïnU #÷.K…½Ðº®P|^å©@õ,ÎÈ­Q0£,…æ+*Dôð„S”èý‹Æ_lú R‘¯d¯**ClŲ!~âœÙ±F> ÐH¹7×—LTy€<€„|c+d[q÷$”9æ÷°(>­ãÅC‹'ÞíŸÐÊÄㆀY•€r–dU" ÀCu›½gÈå‚ óq"gD*‘c%ï”H$F Ô °ÝsJ‰×oÓÏT8™©˜äCI6%‰€a¨Ýz£ðà¸{6Ô±×0¼IFÚ…€TÚ—¼Y" ¤7sþÕ—×jiœÀ <î'«´˜T,¤`Ò'Ù–H$@õÁ’›T•zhïVh79û½Ô|ÈwÆ©ÄG™ŠD@" Hyæ<›—§¨êo3ª<>ù¶ÍXS’IJ€I?œd[" $ZÏÑß°Ý¿sÃ{·*®~2.@²?Bœß'•€8*““H$©ˆÀÌgœÅ~Ro æM±Ü+­A4L»#•Ó~:ɸD@" H~¯‡—&‡öF…L¹×ÇkH2;R 0û”üK$#0ã)Û8v¼¯á)ªÍb»3Á¯”É' ©$ hù‰€D@"`FTõa‹_õ=+xWIùÏĻ뗊c¹57R 0÷÷“ÜK$„"0ã©?\ËFà%l¨²«®úB™xRJ@Rá–/“H$æAà‹¿góLÿãAŽå‰sï«–k1ÿŽTÌÿ e$‰@B¨«©z–}º6$^’gíñtB^$í0¤ÐaÐËK$ã"0ãiÛDò«W -ŠåæÓîÚU#Žå65J@j|G™ ‰€D@"7æ<×%Ëï÷½ TåóîõMÇr›:H% u¾¥Ì‰D@" ˆ Õ•¥²3`Sˆº-™wÄ%a™ˆáJ€á>‰dH" tÓÿd?…W FTËíãï®8ÔqÉ7'©$]™¶D@" 0Ú2Á>ï+̲Ö6ð”ÀOxà-eA²ÚN¤ÐNÀä퉀D U¨9X‚ÙÇ"¬%—ëæTÍ«ÌW©HIH$šñ¤õRUU! PÉò«É·ÕìÇr›šØR3[2W±"póoéóû&r:Eª¢ö`ç ìÖÒœV±;xùƇzÎ „Ùá¥H+UAÀ‘V‹uÖóx@† ƒ“³µw{¬•-S­µgXø=Þøàï÷úUåÙ^Ž^{øáëj[{V^“$ åÎ;ŸquUÞjµÐZyà…sJ¥ßm9ªØ¨.âòÐÙ²½Åòà%§¿ÆŸ£zÔÌ+ kÞêØ±}í;¬ä[.÷›É•w#QY’ –¯éH¤БèèÝ7>ô‡s÷Öï|•«€ž½+vªýË6PŸŠmŠÓ[gM›ZZgsRIö1´%PaIvŸ'÷xJîøùýÿü•?><‹ßÉu’$‰@Òà6,×ÞóÐÄ*gÅKÕÒ£‹}³ÚÛ¶’ºÙ6(¥6ÞåA+ ÕEû½ƒh§÷øÂõÅOîñ&¯,ìØ¶î .l£€4û°vc»|ÒmeG“޼|a‡ ±6Û!ÜÉ—&VîTUez^miáù›> I[>V^O¬$ôýHïÁûðÞ¼ÚÃÝ,VÛ'7<ø{,S*e3¡èËÄà ™³ýìþßÞew8§e[v“ñ æ~Mém_I¬„y$>§6ÞwáÙÊ¡¤”Í€ÔÆŠr÷¤{ë—Å'W23 +Z3|¥Äñ¨z¦ù&Ë…ß·v¯ê˜µAðÞ‹6½gÜùÓõ¿þôºÐ3“$H$1ÈšýÚ{ºËjµ?Ù˾F›ñ¢µÀ¶#‘ï ›6Þ96ók/ûê„–ÄPUÿk‚ ¶|0ùÿßűܦR Hï.— ›Þ'²à©þå›Õ³·ÏPlþúp÷%íÞ>À(yòš»=‰_•Š@Ò¾BÚ½²³_y˯γ۱ Žr¿£Ø”Ž+x7x/‰( ÓŸtö'¯÷< àÂg`ƒ=7ïçØ—”^H% ½¾w0·7Þø°ÛjQ^ɯ=Hg–|a¨Fütª)UçK&\šÅLK9 ~9¹g4`ذ“s³s³ŸÏ±îWG¸>4Ly/9–ªÓ¿²0ó™œ|R=ÓYè,Y8à°;'ŸsÓá#qÆV&gdåj‚”-¾|åVU±ô8}ç%jGùè ·¾ÀŸÃé@ sxæŸazháx–çL…€V˜cÈ–;#3ã]y@Gø´…xo±–…9sÆÙ¨fËë<ïæŒ†wúÕòã‰÷ÔÛòzê" •€Ôý¶-åÌòÓÛî;‰{…< ÐЪƟbévá57æÌHk@K_Tž `ÂE—Ÿ¬X¬]x a˃Æ[ e±jÏ{-? ‚¥(wM¾Ï75x,wÒ©¤×g×z?v§c"ï¨Àйà3+/÷fTZ ýµLÅœVdʕߵÛÙ3Ž`ØL€·hËâ`@ñšŠ rDÀ?M¹×ÿq,·é‹€TÒëۋʯ/"&:@¬Ð‚?ðɽ´"NËÎ?9$+¨òy ÊdÊm±Ùú8”*Ž˜¸8±ÂÞ˜Çv—…%/ôȨ>¸c+“ŠEybò½þ»Å±Ü¦7R H¯ïïm%‹Ò=£¾Òß|*V¥;ó-,¦à;½ÄÊt¹ ”ƒ€L¹¬v[W—rŠ¡‰ylWYXðÜNûË÷Î`€ S~DZîo<–{éŽÆY%¥¨ülR²þzÃWzø,àÓbµ`š ”éP$ÅŠ€V8ȔӪX²ìŠÇðåÿâXn%@…ARz €J?žn¤¾ÂÓ^ÞTTÚb8ÀTüëó"÷;`9`N T:ØTnyФ,ÌxÊz…ß_ÿ5çMS8€j±(·K ÃeÏ HK€!?K˜Bcjeó i*½šÒ?©¸@‘ÿ£G PòÄu ™”â–Ë‚úî¥ÖÛ?xÂï÷ÿ*B¬\}Þ½r@¹Ó©4#¥DHáÞ„©”€~õVðϳ$IÚ@°ð“šbi2KøoVfþÉ~üômï¿È×06@ ­·YìM¼Û³^œ’[‰@(R E$µEhÆ\ Þ±•$ˆ½,™Ñ²äÿ݇»ddf–þÖïõbÁ­`}ÎC娯s÷¡ŠX€’Ϧ>A¡Iý¬Ê6 ·FT±Á¯Š= ¼ÍZ±¸3È_SOÐEÅÏ4eZé‹€§¸•‡$B©ñþïû­“2]‡ž&?õf1nüyýoå·çÝã{ŠcˆÓIdM¾Êl˜Q 6Æ)Áoþ“iè_9¸˜ÅBî¾iàï_¦ÞXDÇ¿ú%õ½ãQ²8ÝZ>;ÿ!÷Ïé4âµ4칩Û~¢·åu¦¯/$Wïþ)‡ÌD*!Ðþá±§¼BO{ BAŸ±tö «hÈØßωp×z½’N¿ò3¾¥ý:ÈÍ?´Œù×Ê´ìLÿûÜÊ÷ïa`¶Çq €'¥ P‘Û¶J@[¥Îõö×6 yWlvêqåÍ´ï׉ü~r÷ìG5;·ÐÚ».¥ÍÝFy£Î¤ü±çiwg A{?x™Vß<…JgO£?¹• xËKéÐì©ç±@Ìu^b~³LÀìÄ,;º¢Î½ÇÐöå/hXôy38ú½Í°iéÚ®uï‘Ý•C=_Ôì™ÖNdÛÊ×_}Þf¥‘¸ý Gâ冯™|¯zö¹÷ÖmjíyyM"Š€TBIícT€ük_Ï'ÌD²ØT:††NÙ¢™´ó¥Ç©v÷vª\¿‚ª6¯¡¬!'h×v<ÿ{:ôù‡ä9´~þ>AÈ0L»¶ÿã×(wÔXrõ)ÖŽ#þ˜ÍÐÀ{]§ˆ_$oLô²„ývQ¿Q·ÐnnÄ=5eÚsYÓâ©WPMÅ®fé´t ÃŽ¯RÿQX$3r²¨Þ ÍÆÏÿ4Ó¿ª<çÌÈ<ù^ßk‘§"ï”4" }±{- à*@Õ;¸ƒá÷5»Ãš•C}Ñž¯?ov-{èIš¿@õ–µÚµúÒýä«8BE©¶ds³ûå ‰€È.8–lduÕg·kûŠQˆ›Rk׎üžuz,V'-ʸý¯«¬Uþ»a›ýÉGÞ¬ã%€+šÌÓ’·I¤ e M\Ý‹¨noIØûŠ~ñ[ª/;@‡¾ü¸Éu{çnÔ뺻iÿÿþËׯÕr:ÎÇrG"`&,6¹² ©ª|[ÌlWki(”‘[D•e#J¯ÎŸµûÕO­W.ÛèݺÀш’7IZA@´޼@ÀѹêK÷5ƒ£çU·k¦~øègXÜ™TüÀ_¨jÃ*Úóö?š<çak€£ {“sò@"`ÜYÙ­©Ø3˵•û9 •\Ù="N«ÖŸQÊ @yÄÈ%m •€6’—‰<ÜÓ·åwmE·^Mã/ MÞ¢ÿ‹‹ û?ðgò-£my°ÁsI\å­»jé5ž‘{ó P[…†›4k@¬\;³P¦xN_Us;Ö´åóHJ@¤H¥ñ}u{v†œu!õüém´÷½ÉêÊ ÌõnR¿_=Aή½hÿÔ×(£ß`í¼³Gã³ÎÂÞ„ô$Ï€¯¾šä÷Sf^¿˜ÙÏÌ=FK£ª|{ÌiÉ$Ñ" }¢E.ž«Ù±:~.Çàh¥ìØeÒeškr¯ŸÝÓ…5·\@¹#ÏÐÎÿæ¹àµÃ_AÛž¹à'`ËéD5Ûå,¦ 8rÇt=´–rºf¼ÄÂ|n—ã¨êðVò{kcIF>+ˆ ©Ä_z<\¶`&^r=Œ;ŸçúO¥u÷þ¸ÅŒ/»T›¾özáE×Qù·³©¦D*a’'MÀæÅÏÒè ß¡ÍKþBžªF§×¯ÞÄÊ—‰Ðk«ƒŠN¸6,z4ÜíòœD iÈဤAmÞ©>/ízíYê|öš šœØró)gÄ©´ûõ¿Fó¸|F"`Žì[A{6N¥ÞC®ˆš§Ââ)ÚŒ€½›Îª‰:Aù D J¤% JàÒí±#‹ç~Ñ’÷Ha¸@’D X3û¾˜²±gÃG„Ÿ$‰@G# -ý’û~Ž3BªOõWÕYìØ7oM½ê0|y¨W²,Z¬ÌÉœTÌùÝ¢á:XÉùë}ªíYÁãhKÖ3UöLµÞã Ägm|©)xodWîf²ã©­+¯Us›7Ï+µjŽ, Fû()ÀTRà#¶7 õuu{jíJÍÙÞG“z?øŸžšêÀä줾]¾,]¨­®:èQ3ê2l–Á›Æ£, †ýFfeL*fýrÑó­îÛ¹cw{”’ìÀ<åè“Jì“à|îܲy1¿ =5Ã÷Ö‹ˆL=ŽyRK6®_9ÛïÇäã›x²,ÄW™Ïü– ¤ZÅ7ïÓÖø}õ¥[òáØ°þü^Ïᥠfo`&5Þ¶†åY2f ô²ä_½ä›-¾úº#;½Ç¶<€7YL![¦cR*¦ûdQ3¬¯ø|‡öí§$»²73ò¸åQ¿9ŠÁøÛSR2Ç*iøéóEªò‰@råÛ¹uËçê‹•CÞ"ÃAžÀ›, †û4)ÁTRâ3Fœ ?ß©U|ó¦~ð÷,Jöç÷†Y5âp#ø_þúºÃ ¦Mý„_á|óy$ˆ`9àD [ÞEÓ§}᫯=²ªö|¿Wm¾$p,/‹åYðžü^YbÁQ>Û2R h›T»"zÑP¼åå‡*×-_öìawgš×g®†ÀøZùí×/WVÁô@ÿ„" òa~%#¦B@ÈV˜sÈ–§ºº²fÙÂoõw¥µ¦<€ð´òYL%e&bV*&úXq`= 4¦¨øê¾úìÓovnÙðß-yÅÊ—}ÏS;Ú"€÷ƒð³mýê–Ìûr)ølø E@ZI1!ФpJšŒ­øjþ÷›W¯üdWýPeIÍåjGZðnð^dYˆé[ˇÛ@ ®Y}®à÷ÜnãÅòr‡ Ì hP±jIÍÌ·_ÿðÜKl§C®8ìÊSOß9ÏÒ½*öµÒÛ›;ø`€-kW}4û£÷¦ƒ¿†xÏè½I%€AÍʧYsÏùøý/UR6bÊQ:Üù‰¥À–ÜU/á€!XLST%›ñÛÓW‘wqUUAëÑò 2’EùR0s¢!­ç*=˜Ú]Ÿ½÷æÔÏw`Ä)§_7mÀ%¹}*JÔþe”>ÛÈéE')1„8˜ˆYpôyj+–Îþì­•_/XÆoo• [ð žÁ»aLµÌ‹$s"¶pV8Ã>÷ãæ”îÛ{pÔØñ?ZèûyNWûfµ·m¥ÒͶJbVüCLÄ,8²BÅÒ¦* èü-1§8¤7×qUʤ¨Ýn~üñNÏ?ðÀáô†Ö¹•Ÿ° ‘EÅçX¶`îwë—.Ù|ÊÄÉçz‹Ž+)ê“£pƒëª¯V3ê+§¿žãCYŒˆ…Ä )^OmåöÕ«æóÙôy55UGù-à V%lñÓ[¤À€HŠ Ë§Š:Ñòý·_­Þ´rÅÖSÎ|ºwР1ÅÙ(¥Ju)G»â‰Ky@¸bD+D  `YØh®²€úÞWåíÆ”1}ùp‡ W%Àj±Îòú½ú«ýS87¯wHŽäKÛB@Œ‡¢‹Þ6\¡­ü³°sÍþèÝOxÿËÇ8¦hÀ !î¬ìNN·;ÇfËh5¼`N}Å1üœFGíÙÛÄ~¸­×Ë1 «VÔTVÞ±iý†M«W–ð}PLÐãG£¤á…<‚Wø2È¡AR\h±4¤î¯­­öÍýßû³çÍ+z|fdåä93ÜÙ6›»Õò)‡²pˆËÂ6Ó–Ô÷ªJ×ÿ‘æ[Þgâ¢Íê³sÃCØ©²úÅGjyqmýr¿#€C(ÄIÍâ_ÿòø—Ë?Œíeò×pÖ3â-žiQ^nœõ_×èoë+ïû-lÑC%,*b˜úÑÛGƒ ”€rþ «®á©0’â†@ÜËAœ™¾,ÜøÐ#3ØbØKü¦wù—t0qµhyQ•¿°@o%™&y<ù¿ñ7Ü¡úÕgÙ?à=²w¹VZâ‰nÜÓB ŽF>ø9t[¡à:äEüx·‘Þ›¿J]9¿l¸ØÙ¢Ò?T|úÊ•0~lñÃ5ôÒ$I’…@Ìå ŒŠr€­©Ê,ZÔK‹rç‹xèÏíÈ·¼Õ`$L @>Š=͆€ÕCà6é#`°¯ß”È‚¨Ñà롈î~3¹™~vÁn‘Üä/õû![Tx 4îúÊO(¢2×µ›å?‰@ˆ©´ƒOS–Í€èoœÏ¡Š…î– @;¾¸AomV™Ç›O8—Ó|‰§ôáa™ü{Ã’aùTƈ7ÒqK2!*B4úø‰sB^Ä6øÒy»À‘O£3gÄ ƒp$*>lÅ ¾ø‰sáž•ç$ÉD@ȼ(•ƒv0hš²€8i€êOx`|8Ÿ7¼øÈo>kG~å­E Yež>~ø_®Ýõ»oá@B·sÕß‹…ÈÏ%ö«Šº‡€#˜$#ÀÃ:Aî´ªÁ£ÆV<7V½4â—óÅ~è–‡‡‚‰±)1ô²<–HÊA{˜7zYà‚šÅ܇8ÜØÅõö_àü-}Úó¥}oÒká›óøHŸß7‘a)b% 3ÐR¯ÑØÈIîš pÃò¿)N¼t­óľÜJ$æD€•€Š†Pð;îù?<°Ôœ9‘\·†@Ò•€Ö˜‘×Ì‹Àâ)]ƒ=ü“>= åʼŸRr.¤ç’$H$‰@" •€4üè2ˉ€D@" R r H$4E@*iúáe¶%‰€D@" •)‰€D@" HS¤¦^f[" H$R 2`TõaËœ9ãâ¾èU8žýW^^¸óòœD@" H%’R¡¦`2/Ñ#ðØ‹Êj²ZoøõϽ_‹Tžù»gM]íêì^ý «vn½WUÈöëýÿO\×où÷÷sÔ6š &ÂõÇ^PV‘¢|ÇÏ]/îÿã r}JùòoTû‰súíã/;‹ý~Ï{ eú¯oò?(®=ú²ílòùž¯®?rùuÚTÇå÷ÞT·%xýEëeðòeÙÆÞ{cý q^n%‰€–3~5Óò¬¼­øü—ëÙ¯õÔ]Ê ñÇ·MÞŒåƒ[%Ž[~)‡œVÕ¦aWÏøã ¶1­&Ðpñ±mgª>χœÔBýýï¾{©•ü¾WìÇVN·õA¯âù§¸ç±,*ªÿJŽŠô½8'·‰€DÀÌH%ÀÌ_Ïd¼Ûìöwüªú#¬E.XçýË«åmqÜÒö/ÚNãU¶*Šåu½çêÐû,ËÝ~ò=ÿ #±r«dw¹ÖÙÝ™8½ ú7W|4š+¹÷ººM8ÿÞY3}è3/çäãØ¢ØÞ|àFÿżþ…Ç’$‰€ÙJ€Ù¿ ‰øGãÊìÇ_¶k=ö?¾àîà jñ)ýÇ|ÑV6üä¿–,–·TGƼ4õ•zEÏú-îoxH`e)-¿³­´î¹ºò~~6èïU|j^0±¤É9^4¥F­í…s÷ßèYë`ª°Fèï‘û‰€DÀ¬H%À¬_Τ|s#úùC~¥î2nt??~n«=ëgÞíåæ•‡Ïënëýɯ¯©(å¬o~ü%{pÕB@aST«ª¸…•*Ÿ~ÙU aw§<3ó½™g:wUÚbäéfÝ0¿WJbÚëã‹7Ê¿²±ùO Ïîk+&YÊã\‰YÓ0ñ^C¿&ÞéýZNËŽê#}M[~%ŠU“nΣž€Ð1’K"@Ú' ýX%ªÍb¢{ŠQ¸"+ërÉ„¿±X÷ŸdztÏ{˜7ðÒaÊçÏšÂ|îž³ÜZ÷I‰Þ3s”wa÷`›‚ÛLæ]„= aÆ0æÀE‰%šîD€"@ˆ@E? m/¬¿Šv ñ]n6ÔãMq&D€„C€†¡En› ‰ÉûòšLb)¡D€4Y¤4ÙŸžNˆ M)Mý  ô"@ˆ@“%@J@“ýé)áD€"ÐÔ ÐÔ¿J? D€4Y¤4ÙŸžNˆ M)Mý  ô"@ˆ@“%@J@“ýé)áD€"ÐÔ ÐÔ¿J? D€4Y¤4ÙŸžNˆ M)Mý  ô"@ˆ@“%@J@“ýé)áD€"ÐÔ ÐÔ¿J? D€4Y¤4ÙŸžNˆ M)Mý  ô"@ˆ@“%À›lÊ)áaášÚCÆe¦`çq!Ú)¦Ù†qÑB0®u,ØÖ#Î{Å—ä³_;üã&îˆ^šå·3ì@È D€1¤1Ô 3 ¤ìlÛ²+™™Æ½‚)=e*bõB½¹'O2<<Ú(‰`…Ž8Vàˆ÷ýéŠÆ8gÐ؇W§ÍOOù²a¦žbMˆhÜH hÜ¿oRw·kzg¯×óŽÉøhé›Çíý]é’¿™%xò‚Ê5™Â¶Çwf'ÍþhÕË(rĪœñ9ÙîÔû8çÐ J¯nwæQzÁ:ExTÁ°X¾jþ¤Iû,7t'D€È % ²|¬t—kQÔ6ïïß)¦Þ÷ÂMŸ¨ÇìßvZdÀÏa+ÛŸÊ ¤2E{—™ú]è$¸ŠE·@1Ie£Pøk\UžÍv¥¬ ä†Ìˆ D vP;¤”dçŒãó\f2ÖO¬cf3$D5¹²‰ n&.¿ä¯÷Yj(v ý/ñWË£½Lp‡&¼¢ëMJ—ƒ›X Ï~ë-b¦¢0Ãv7kËvÄu›ZôPh¯ã˜ãN _±G€ž‰ D Rbi܆I“30C¤¡’ SÚÌ[h´.Þ­:Lo‚å;š‹¼˜–f«¢=ìªõ¯«5¥aöM× ÍÞ»~Uúìú…a.A¥"‹µflMÛ>ì—ö§†â0¸¢ŒÉNOÍ©ÔY"@ˆ@ØH YÃõpÿ£6Ëß[ø8"1Þ›¯÷Þ½Bë¹ïwWRP/…ùìónŦ„˜N dg§§Œ´Ï+¨—‘¦H"@RÐU“¨>8sfܼâ÷ÐÐïêŽåÊéÛ`ª-ÿúýÐéös‡~r^Áœœ ç½õ?ÆC"@ˆ@à Pã®Þ†‘LŠeŸ3χ3qÑE}¤œ¼ûWÌáÇL€ruÉßÂtÅÁ¶Çu:ëŒóþùÓ—Ÿ®h Q§h"@ê5Ú1°^ÿ<µ¹$gúhTù—»åsåØýëkGè–rVî×L.SÄÊ‚9I®Ympð D Q % Qþ¬‡5Ê57»ûeup“yâ,àÓù›>Å"¥óßÑ£OQ&D€Ô;¤Ô»Ÿ¤v#¤ûîÂr»„~Û¾mð¿uKÏ^Ö9‹€RskíR"iD€¦I ÁW Móg #Õ†¸µcá³maãØÆ¿CA®Ü?à—k™rJˆ Jc1ºÏ5»%fžÚ-oc£ùctœÒ¨íg¿Ä7–߉ÒAˆ¨+¦r¨+€õ9\+8{ó+m wÔçh†·|R„u­žÇ\÷ïË#9&D€Ãp’Æc`˜¼LM3£¨Q$ G³­zš˜ðY£H%‚"PÇH ¨ã ’Á£Å,ÏùeoÃç+ÚŸ†-ã¦)ó#Éd"@š Rñ/­˜Ú&™¼Ñ-"’J®E1®9ÊVckwÈ~CËãØÿ‰}ØkÙSœo ” ‰ D ,¤„…«a9ž—>~ºÎwüpt#ÞºÿÖ{Î[ŒáÄ¿f=z±^Sžb§½ø5;å饬Ç}L‰Æ„ŠÊºÞ1žúÜì”g?g'Ï}›u¼z¸/l­evê _±˜ndž—?[ödK{ –' ncŽf£Â@ˆ D Rbi†¾ÃvþŸ-zŠ}1¾éÕJ˜líw¾q$ÛþÆS8mØdͺÊ6o`kÆ^Ïþȼ‡µìwk}þ`ØLß³ƒm˜>–­¸sÛýÉ›¬ó £XT»ÎLß¿‡íþômÖ妻ÎCBIK(Ù/÷9î½…ß$§M;%l!ä"@#@JÀaH—Aœ—‰yÿ’—ÅZLµ×ú¼K™âˆf{¾øÀçï×²Í ¦²â­±üßa¬fñ'žæ³Ûþö³ìàêåL?°åýú­¯ç@óÙíxû9Ö¢ßù,樞aÅ£má.vÚçÔËþ|—ÅèEÇÓû݈´©¥†%‰"@ˆ€)vðy¶kì^¡)×în­¿Ñëã¯Çà AŒ¬‡qÅt?Žþ3ÐÒ¯x©ñ ,¶Çñ¬`í¯~«¸^}X»‹¯eG%¦0©0I¿¸¼è%0æ±Øî½ünÃy8êÀ_ìšß_V›y Âð¾A…CÜ"@'@JÀáLIΔÔe‚ó‹ ñë?>z({¾Ï]Æêv}CNgL§î̳mS@÷ÝGLfÞ½;ÙæêÉÊ¿[R s´hÉö~ö^9ÅÝ©k9³p^bõBÖË2Õä¼Ç6ýÛ+ÂñKn‰ D <RÊóh´o 2œßˆ„¨³9ã«1ª<š7Ô+ªMG´â·æ¼Ë­÷²¸ãNöÍ º×oÿÛ¸Ù¯Ã/b;ÞyžõL}‚5ï{–ß®½Qm;ùß«ópÔ,Êð¦0/®ŽòCˆ ¥H h"_‚ËõL ?PòŽâíÑó2vÊŽCNy ZúZëöåÜw¸âÿXÛW²õw³’݇+F~Û…‰¦§¨œÕ¦=“òjraÅkî=ˆQ qTMä_"@ˆ@S'Ð8v‘iê¿béߦoY€!3/Ùøïž÷g>9ñäþÍbºZfØö¢«X—[îa[ž™ÅÔ˜X׫thÁ³s+Kès;¸êŒqîSäÒÁâëü¢;vcž^ó¿W÷ÁÄ¡\ðƱbu!?"@ˆ@ PC€ Á{RZÆM¦iÞrvî×,\@¦¯èﵬչ—øö“Û]6ÌWÉw½c\¹ä¯1˜µüoÖ}D*6ŠÂªU,÷Å9¾ÉÒ¡£M¦%´bE•N,ç9Œ(3ì`TsŒlˆÀÂEN‰ M™)ü×w¹„²Í;eJÛÂ=fß?Ukøgï—²Ž×ÞÅÚ^x9Öú¿Å~SPjkSnó- T¢¢™Y\¾¡.7Úÿý§¬hSÍ”€mq]æ4¨ŠÂ—Y"@ˆ@•ªU)T)•ÔÛÌÌè:?ö´ÿ«öo- myn6k3ðJ_@•‰Ã†B­Ek–pê9lë sªô^•ƒµ­OÄ"GQÛ2vIUnÉž"@‚ ž€àl‡ÉúaWÑ%sx›TH}ÞŸ3ùWÝKÏÛËVß %¢†—<aCë㱃0Ÿ;{ìØò] 5”MÞ‰ M@µ[‡M TÃM¯ÙÉa”˜ãо†š9àËn !¸GÑbf5ÔtP¼‰ õ…)õå—ˆPËéÜšìÊ<‰•ß8Ìâøs¶~¥öÜ»K÷«žc 7û‘ûþÿÞæ$¶¡Õñ[ÿþáàüÒ'ÝiUJ  D á % áüV©ÿ;&ÌgQ0A¨ü‚›h0·z }åò¡×_¯Vî#˜-ßšžú\N†sZŽÛy1ã<ƒ >~tff›`>jÃ<;ÃùrVzÊ×µ!«ÎdxÙePšd‹’®zHàý™jb+\‹¼rŸ°+ÁãzìGDц5ïÏÐÎ î¬rô°-4 ö2´÷-P°FïB¶ÉE[S”üœä̼ªrßMÇV*Ñhg6SJë+:ï øih‡A^¯÷ TêÍÃ…„]“,úüìÕ×÷¾¶÷k«óÕQÞ½ùÞ§%汸ïItºŸÂðÃbôNnˆ17ÈËq§>Æ5'¡Ø{`:z£•ÓRpö³¢±qÙ.çòQ®©=½^デcÀ® [,ù#Ó2û¦99Ú%ô]sÐBZ‘“îœ#í-Z¤.Y±ö!( ·bˆ"3ögÆ;e 6KKNs¥ñ8 †+9WÈNOùV:Hvf^c2q! Ý¡LÍBáßÏÁ¢NŸçžð›_€åN˜+\Y*˜‘‚0ºÃx‰ª(OgMIýÈr;~úôæûzg£0Œx9ÐSü‘#V½onJÊž¤4wâñÌ<‰©îA¸ï ŸÎMv”©þ– YÈAQÊj×/'9Ù+Í“œãÐ%}¯;åûgz¢!øÝx<éÞ(6Aºsž´“W°ß ÔöÐÿ#Üî.f±ÀoÅgCÉzõMÓz‹®W?ظx¾0ÍÄj¦ü|Ÿ0SM<ÎXŽŒÄɃ™iÞ®rõÂùé)_Úü¾äLwcb~’+ûãWr¡Í®i>J%š‘Ý4üú•ê:U~º¼ã¦)þ  º&XÐÒ¹¤¨h÷»ëÇô¼ì¸'jÔ•y‰Œ‡Ébþ,èÄ æBe¿óîd1Ü×éѼ …@åŠ2JÂî5Gšºø“ÕN™ëš´ÞÁb]¿2¦–ÊAñ'Ì;ág•,1äÐ ã±[-»%+Ö;ñ|›PÔ몺Y×½· fNƒ™oDºó)‚¥ u5Ö¡©ßx o²išŸŒqM?A*¨ˆ ¬ Âü'*øEUomÏÎô+!R†¼JÝ!,fö@¥›¢Äð•F±å­Q®GÏsß.ÝAx @1×”Ëñ‘˜^/›á-4_ÕÅšÚjª.öö€4Oœ#n¬ÁTÓà [ Ã{ÞH×Ôcæ»&ýér e«7ã„¡+[w_e †y-t^Ç;ƒ2q7ºˆÔÔM[ì1¼WAAH…¢—ãN)Ý ƒR»ÒÿïÍÈèPTd.‚¿å¢É*’Æ-Îx‡OØÏ?¨0Ÿú`†j oü'TÿÜãWUPJ½kí3…wW×wÞEù“ªe™O*SzïôÑfù{ á͵3E¾>ßÉP(€ò›yÜ_ùæÈIWÀ¼ þ–#¼Œa,Ÿ‚ƒ¼ú¶àü#»âYöí~ÍT6kÁg¹9'>e»*Ñ2](;&1‡vmŽkbƒª´ÿþô\5:øå_]»˜B¼Ž«F €•DŒë_˜÷çG­÷Pî»ùHWF_ÙJOLsB‹7 •óc9®wûýsÑ+º}˜0øY–Ó¹UºEXçkš2*gJê{9®”ßQñÜ ÷«K¼ÈD¥×BÎÌÛ,ΜEå:´º·¬„€åéé“~•°¬Ñ¢Îò/ïhyCQàg»S^Ò5!­vŒóË” éõ§8ŠÉCâÈÉrMúËå û,*þÇ…CSoBAø¡LS‹1éÒ‰izO‘w´îÿÉÑ« jÚèWêOó\©¿àCqCþ šÇÎsÎG…[‚4ysÝ¿ÿ ×=dÁv¿ ](el5¦öÇ-ŠÏK˜ãq4»Ï5»%üõSüCùîK“Â,p§>.ôŒ<©0½#¦SöŽH7¾«Âo`Ë»º)*Kðøú‚ ç»]S{ƸþüFwÔVº¡(æ¼7ÝÑ/Tyø¾O†Ò'‹Ã._럋ïñû÷çšô*bKQö»µ+Ê6¥÷¡(-êxä‡Oʔޮ҃jø$E/‘ï…BÎvh,êl5^ïVöàr¡ Ÿ#¾Ãûa4ž³Ϭ1™™í¤_|M&ó¯ÿÚff€ù)%Z*ì̼nRÔÞ7–)Ñ-!eJt'©D;ü¤»C™ͤø@¹à‹¡Dÿ#Zk1ˆ+Úøí|J´”!ô¢I%ºM™]&Z*ÑÌ×»çS¢O‡ý4zO` Ë.S¢ÇYñ#(ÑÂ…†Ö`ÙÒù!»Ò§¤ÉC¡ä0E¹›€Štÿ;2RÝ\:ó>‰¾mm†ŽÌ1Jö.„*-‡Ó ]¼ üZѕƌ}9ãÜvñÅOÜs¢”!Œ“Ñ•¿sÞC)+mŽÿØÇȰ²@`±Zü‹ˆK÷‘.÷ÙòýÀ~Ïu°Û…Jn©|·_²0‚2Ò ­‘Ïìæ( üòïw=ÚnЊ1/—-ù‡Bò-H+ŽõûãlogåŸþ.}¿ùá¹ö™Ó³Æ+@¾K˜Ü7$¹}Ph™ºþ¸Æu'H1Š¢ô<\\© ü}d06H¾qÓ¼…ÓGàò>X\) µB½èB~Û²\©«’\³ðÛ‹X® EOÇÇP.–­ÙÐýP8åƒCæè¡(4ß‚Ì Ó8mæMîq鬘îøF‚®6©üÆõg–-»A•_RÑÅïÙȦ`.9S6#ïSf_©¢ŠÒ[&g#ZñÌwOXkÿ¦íqÊ%¾¥=ö×`÷›§H\)݈xÇsøn»&¦eúË (ÿ'+霉óìrüÏH‰F¯ä ( Ï"__™“žò½?ôÐdT™¹#AâÇË;œa€ˆLBï‚ìF?'¤xsö9ưÛÝfM)_—(­Û…J s©Y Z&¨Û]‚)Â>ž²uŒ±ìź—Ýß¡ªþ?T²OWô#}‹ó ¦¨fÁ!i¨D+¶ð0=Êç–)o* [cs÷¤C¨¹¶÷A[ÿ6G[.,›Ué#çÑè (B¯Â“–â#¯ÇL%úçÒÇ€ÿˆÞ”ç|Ý©ºûÆ•›.êsüWK~]§m7¦â7ƒ ·øzb™ª ¤Ï»$¡ QÊXJ»Š¿å^¶*ÁãTVIIi7大Ê9Mò*6K\hí5«õÄ vrñ_Þ¹ÿ©LöÌ,DË´˜™¼Epw¦´Û+í¥¢\ çÏ’Šò|—ó;KQΆ¢¯ÅµÎ÷tÁ7(•Þó¥{(½Ð)ñÁ`îˆ|·.Ìe©ò7™6õ CèWÂow|­À ËŽ}Ë$™¬è‘W_æÂ¸ fŸKe&o¿çZ´šÿe…à~˜e¿Á”hŸwÌ×Ñ®¥Do éó+Ñ ¤-‡)ÞGyó òÔÈ\}j™íô)ÑÂ[%Z=L‰†3­L‰þ³4œÀJ4z3z"¿b‡?Üà'*Jf!@|ä/Úc"*zÎþéŠN!wa†Ì‘û2s×$Wæ v¿ÈPX] ~·Ì°p!Þ‡ÝíL?…Ùùšÿ±ìì÷'¿ e[‘ÐùvsC*ì|ãô˜—€B¢}ö”ÔOìsÓ'®¶û«g0\ƒß¨­ª°]ö°ä³¨3¡©@9tŶŽûin¾Mw߆ÂEí¢NúrذaÒÝ;3/CWå@¡2Ÿ %¤çX’€¾]\ o§6íæžá*q˜”x‡0EN²+ó¤@î»Ù‡&´Û›"•NtßS•l©àâߟã)—/Êù¼Þ}ójäïU×R”ñ¹0´¾Ke»Ò+QÛß-Q\›m—© u‹ý½âs²3c‚.tYQnC Ù‡ò ¾Ñ²Ê±ÔµÆÕù¨Ÿ¯“ÃUP®Â»c~ºó󊲬w¸­Z‰æ¥J´w´´STå(Ñ?ZrÜ¥=@*Ñ€ %š/¾¨ïñ_">•hð ¬DCp(J4º²ŸA®/'Öˆ5G¼'³ø£Š w_I¶Â4®‡üå‘c`ŸcV¡e»ŒyÍIiÓR£ÕØ¿=úÁ;QŸ©8ØyV˜Ù§|†h¿‡‹'Q©-™—áÜlÙÙï²ð”˪ä#Ææ~b]Ú~ŷ’ärîGW¯¸3îÿPµÖ¯é¬(F˜žDbÍÜ çßv·5}î¢9?Íõf,÷êâÌÞOï¨9¿Ø¦Ï<–sïiYé©‹¤|4ɾDåž–4mZ «ÛtöرE˜Oð¥ÉØD8y³l<’"‡\§cx¬ãPË…ó9ó&%­Ýڼ˶î¾FBîk5A(i‘“·À¥¿ðš‹G¹æþCÎYÅ_cqc2?EE*=¨ôN[2;æ˜A÷—«<„÷"†F»\ÏÌr¹†ÛíG¤ežk ãT ˆßj™KE«^‡¢<£Ä§(³Û¥TzÑM]¦ô:ËMô³ü†z7™y”“™˜/ýÈU/û½ñ(‡|×üôI?¢Çae¡Qx ä0VÀ^»2çUÞ¤†B[ J´œOЃT¢}«›ÙJ%:oÁ!%Z“J47ÑËàS¢‘W¤í”>¤…8[Jô·–”ð”h>óÒ!¿&¿š”= œ|g…I÷†Màˆ÷ï>Ýæ?ŒÜ…ÙççGJºlÙF;®B…æ¦þµÇ{`;*ð±˾!ËåüŸ®¯ë… ƒ! ]h™º·Lp¤@#ÿàÇlóÎýÂ`n´Âï·»tJ¯™Ðï1‚Ïöê{÷ +pº ³ EmmwWϾÊ;–_¸Ä䤷êî"¬ó^k1Ò’IVï ·àO–ï݄ۯààƒBK_ôRÔÒÒ}G­Óè:è‚åßY ƒ4—cµèòÄlvc6Û´+é— ÆûÑí[¤Iûp.Ñ­=º5Ež®ï{*Â-&ªF:XÆ[e]E…{ WßòYbÚÔ¸\‹¢¤’%ï:(o¢r!'ÒZq•Š2ÜïÇ„ ;•W”a>ŠõPî†C±‹—sHäD¹¶å?”;äüŒ^†cäN‚w»¦wÞwP—{”ëÁ’rÿæ£CNÄ»0ZÓž Ev07R‰†âo)ÑÊ–}²sÆq#Ò2|;”úƒ-Ã’|,9R‰FܾDž;L‰ÆÙ0|ß=ËMV”J4cÃåÌ~Tà9Á’¡D‹°”è–͉·Ê44èjjð ÙëÇË;ÞŽ.`tAEîÂØáþ~ÿÝÙ*r!”J–m;ØÂßE^ÃÑ‚ŠÉcûbg»ÆúÆMƒ‰“…™ˆ2Ò.Ñ™1 ÿ_%.ÃÁê(Ñ“¬ÉÌPÐßS8_Ža³‡¤{ë’æˆïw²'@š¡W³Þï8So³Ò`¹¥{ã&pÄ•€å—w|]€2#Fôê{Æ_{͈h $œÔïÍàëPIÙ¨ð…C'˜w…Fé æ…5ݨ6”^—A'µÏ7G%@"dOÁVï×[°ÇÆÝµY ’6Õ+G^ Úþ¨«³"M”€H&ùõ…À‘PК|jðøjïBX_PÆ×G¢\Í»µ?ÆÅƒb"‹FHàˆO D7ÜnÌF(JŒÇí£^€ˆ"&áõˆ4ùݨÀ"Ú€qõˆ=ÕÒä´Œ$Ì8 üîÄüÁ¤Ôů@aÖ%#>1i‡–ÑE*á(°ü“"É%õ†€àÿÞ1Ñõ·z“ÞÚŒˆ`­°ÇÇ>®©ga.ÀWµ)šd†@àÈ÷tèúÏÝŒmgåäœÈ\XJóEd$“T"Pÿ ‚þËÒ†G2fŠp O5¾)6Ø!”fÄGòÃ!ÙõžÀï è—ó£³\ߎ$Ìz}-’òI6¨Oâããe~*‰Xœ8ûñÒqÅ#&Ÿ"Pgޏ SŠuü8$&b×7g¼·­²]¹"0 &uA ÿ¨¼}è x!RaC©Žd~T´I. !¨%àÿÝ.7Õy#„ø…íDaj•k™íBåÒ t¥ñUö8Ð3¨)GtôÃQXS9ýcmþŠÁ=®y±¢9½"Ð8Ô‰ Ññèfwc?6À¨½ òž@/À—áHÌÕ¿Zž<9ct8~êÊ­ÿ8Úš œfÉþ¼¹!P‚…ewŠ<ì*¸>ÆáàÑK†bç½»±Ã_žo‡?…Ï^žúŠ]žtÀ€ðŠò7Ù]£Ó¦õ¶¶-ݲtíT¬þ' £®˜ÄàEÝ»…Ñ•²àÁqÅwBq鎂"[æ2ŒVHÇ?¸ÂšaxaŽøLì¬õ|?W_ÿ(¶>ÅY¬ÇÞŽÃQÅoʰƒÉÉ~(åg¬]^'ƒîAì·þŠÜÚ3y²ûrœ)U…(´Fj–,`Å6£ÿÆ® n–qˆã¢} Ø®h¾4ÓšÀàñ“G|0sŠŽoÐÎC¸ Æ·¤ãô»».{0<G g˜XsEu¥=L|£ç#/õï¬ÛWu£{Ÿâ[v‡›»;™ï>Y±n¡Â¢/Îv_?brÆ¥8HK*¼ÛÝÉç`aÙÝ…"Oelèbë® [ doԭΑPÉÊÞ,6¿ÁŽ‹íÊŠQÄæBAúÌ=†L Î†,hýÞÙ¾LQ£Î@eç? ˲«êŽÂ¦Öä~ýFýë”çw¶¯}eþåvž&ãWpµí;¨<_ö ïm–û¥+ÖÊB§¸é¯jÑg"Œ6šÖúfÙ‰Bi*ܸ®³æ< ûïØIíñWêOP d7ç=(|ãX¼£ggmÒ;¹Þu7âp¡v8l§ÜÝ4Ùã²`ªLŽlécŸöcµ¸¾Ž8‡¶˜·ŽqMïš=Åù.¬žƒ’á^áX'”*8+]À3N#S¿‹c ŸœðÎÚZ‡“ËnGs6Æ*×è¾J§OV­¿á€!‚Op¬íã8ºt âÐ ao”gœ£÷`ŽÛì4™Ž1WuNC¼*Ú×Z‡/yáP«µ#_Q^9DZƒèÈ/}g¦Â;ó®­“#OaÓ½û¦BÙès ÏSåäx(GIÿ/nòt0;¨cØÂž,~­èŽÞ›&KÇåïDÊÇ­^Ô;uó_k/2˜y.*¶^P~[á;10‘VÚ¯V4õ³ËÆzÿ‡oI¾Ö>,ÁUø{꼎˜á->lâŸbŠôfùÝɈàû/@LýÇ톹ªäm5¿¹ytùüI“öÙåʉž²U.î·Ì‹õ’lƵ± ’“½8ùÐ2¦;hðê`'yúÛÛ×à]þ¹¶1ÍxLff»âBã çB»ðõ"”p~9&$Ý tVR>ßjfìÁ8ÿPÌŽZÖYéâk]˱AŒE>b vãæQšcà)­pà!ºd¦reL–;åëC¦¥OÁä@…½À‘£J—ÿKj.8C)ÞÎv§ÝȞ̈€$Ð{˜o®Ì‡x”¶KVúò ¿#­Ô_hÿCáØ‹f¾ƽʼ ЊËU¼>I ß+LæwW*]4ª8Üm(AW%Ï7ãè\ßP€]œggÞSЦßËÊHûTš'¥eÜ…Þßs¦L\Ž?>™ÁÆ1¤¶‹¯ñb÷NÏD A¨ÃGš˜§È¼Ýæ¯ËYÇOºÓ6Ê¿„„è…²å.—í`™'£µ­i-'^|r¯·\®á²[¾ô’“þ4å^ÖµÝÜ']r-ã@wŒ}¾‡‰R‰IeË¢d+_Îð¹ &‡‹SΖK7òLrô\jÉ6™²ÅöÑÖ;J¡÷ð~õèTwwiær EΰìéNêL¤ûÊê±V\t]?ßífëÝÚ_ÈwÇXïò{FžìΣ•­–YX÷J䕿I1 “Öé¿v™Éiî™PŠqŽÀ ¿¹)®A^K‚Ýù‡¸'¢'q!zæùÝÐh š¤€Ùú·cXá9ûo6kܸ(öÿ-2 nà^_Ëã8]ß÷ f+oHJuÿ€aƒA¥ á°¯ù)Û¼ë·$§û ÛåØŸ•xÕ‰¡Œ–Ì»kf¯ðê{׿²?;V&ça(äÈþš‰×¡¬øgPã“7÷‹Ÿ%ø;/Ëöæ.<‰î‡_!ÿ§\oÆNžï½Îz&uMx½Žïö&¿ÌøLarE“½rv,Ÿ³Ó'Ê¡¬–IiÓúÈ÷ífÆ•èM[%»ëíî¤]°Ëî®2yÜ»ëJTöÛ|䩉hÝwíìHM¶Ë‡B0 †æÖæä`‰à˜ [ívùôLê’@½ˆ4dæS…L=\vñ¡b_‹æú™Oº¥»ä´ÌspÜè4l,tÆç?ꫤ¹œH¸ä×uëÐbÏÊNM}FòÏ•5^år=“ËöÄç<ôÀÙ}ˆ {V09Y©¾¡ƒSƸæ$<áºç€_r\7ávúèÌÌ6sSRöH;,œ‰8Ïùð¬vf§„}Ù²ætúB +}ÒϨ`çêkWàÛ/A¼ÖD·KX(ãç-0/A–ÈÄ£¯7‹qõ6fê‹àî ”á(¾Ãº“†®På¡5IÈâ1K„\ï-˜ Å`W®îÞŠðˬ”,䱠оåŸîD ¡@o]v²•žëýúWÍ¡^9Ï5éٽΠt'ætBW%V ð¿0{ÿ‰äœMlÞy¾Ì´öÝå*»œÊžQÀ8‘o%WØ,N{>EÍúM£ ù7ˆÃ>ä´Œ[Loñ£çï —âÆß¬nüjÓŸ<åÓ£ç­ÕXÔEóÜ~«MÙE–õ{†›GKúJ:”úÑi9Ú¥™ ^eæ;8w*ÉC @Y$Ç__Ø;½Â³>3Gy*óéøvÕIƒÂµ4DáÊÛ(¨ÞœÅá` ‡s½[ðUÕ¢•;V ÌçCt^o%9ÝŸÔÛȈ˜ì]Þ]_ãû¹ÔIœ4£3²sc{ÍZ™Á˜g“`b¾õ³‘§SúÁ"ž11J÷s–o<á‘Õ·WBñŽYP.ÇÁZë¦f"·î&»¢sq‡j+P(nDÃÿánç]9ÎäêÆ­¶ýµacò!s/rk[vmÊkhy´6ÓN²B#pX¡š·ÚsµlÙ…Ú·ë¾xÌÆèp¤BPQxÝɽC3s´R’ôÏÃño¹UÕ–/Ís–:k”sú‰^Q²†ë;oÅ{ÄZ‰óÝÖB¾ük°×HçôãuVrzCIÀ½ŠŠÌÿ¡×g6*©£Pé5”¨‡Ï^³VœpðÀž7?N Ç3‚NBÏ?kå5-£bnùþžã„ê¿ìîѪ“åN]Pæq¨þ%ÎÕÝ* E=»\.ßÊäz­rÑihy´\äé刨S% ;û Ç·ë?Åñ¿ª›bøíˆnͧ-P‡ML4Þ®®éOvëa˜àg<'ß“™×˜L\ˆ.È™0g¡EÕÏÁ¢N—îF8Ó Ác1±b£PØüéÎy£\S{z½Æ1Ç€'\¶H9òòOlšó˜£ÝY\ßs™`fbŽÛ9XÚ-Z´H]²bíChÞŠnÛÈû‚3ãŠÕÔ(WÆ©ºn>/§¡×b'â5/;=å19Fzÿ£6ËßSø o®)òõ¹¨†¢Â›‰ÖS¹ÔïΡ\Éts6Zˆg¡+x5EÞÔ÷¸Ç† &û‰}Wrš{ZrcТ; +9W@xß&OÎhÞg9g-SÝë¥c®ò$fšó5M¹až+õi&Ó¬›æÓŠÊGeOIõõŒpeœlxÅâV Ž~3&L8èK“W {žŽ´c¾ÿ(N‹{ð1×ýû¥Œ`¿¬6I{ëBÆ“Yx×Ò+ÉžéæñÔÔIi™Wä¸S¾OtºŸc¯å·1ÝOœ½ætÓ0>Á·ÚººéËË÷yŠ?ïýèê«ÇöÞšœ Mƿڇ¹;þo>¿Ê¾a»ûäT÷ù[u·ßg¬ÉÌ—ñy»1Ûå\nw'ŸG¹ftÔu¯ ñ€×Vø.Äü's¦¤¾Wj<ßH{ù=Àýbt3qù½ç!/nW¸x3Ëf)4L*:[õ¯¾S™êjÖ:f)òÛ …G Év÷åiSO3Mc&DJåXÁ÷üÑwÚ¿e¡¦[ºmªyT¦®#O N‡ö°Ÿ AVmÀK°(C˜¯df;‚ޱûÝVòp·kzgdþ“ádƒt†º²‚ÖÑ\T´Ï«švb{íÌõIi¤ÃÁÓ¨ìO` z „HMrfŒ›çšôü,Öuٛ࿿;Q¸¬Êq%J¹(¤»[–KV¬wâù6¡¨ÃZto”ß@˜fÙËû¨Tw7(_£b_¢Ä +[eQèM193QÚcš~KÑKä{ßE¥¾c•g«ñê|ig¿üîtC¶ßb±¼·`ÊLÈzxÉÊõÃ-·>@°Ε‡¢´¨ã‘þOLÓüdŒkzר¶Í¿‚»ûÁ*/Î÷ù×Yùç—¨‡ó ]jÉ€ûPÙ6Ã_A(Í1})ÒŸ'€®©=t¯ø׸ã\$ ¼{êù">ðü7vÖ%ÝúŒC·LpÜWQ°Ü大|/Ÿ1­Ì'Û2o,÷“YÙÍ0ôÀ£Ú À!âT(poŸ‘-‡Ì‚?É–1 ¾Ê 1O*‰\Võ ÛýDuHøžkÍ®‘f‚+·ÊolàI½¤‚~Øåõ–@ªÃuå§p¦,ÐÅ×ÓZ˜¢3„ Ç`Tüw"].8ÿy|”=°\ó›K‘ÆãL­Í§eù¨§ÆÍ(éF~ˆÐñ-³-PV.‹ââ4DüqiZ¤ËÒ«)çQ‹Ý:ë ÈX Þ"Ló¶ZKª`1Œé¯Ì|®ÃÉᬠlßÉ#\nÓ0Y7î½™|C´ÖâE+^(Påªò/´d}­ iŽV‰ÿ‚驾L£'Q Å¡uïD«þQ´êrfŽùTéþÁ™3ãòö{®Cq•|?ìâ^ƕǤOú¡Ìn&*áöûË-ærE]· -{w™Ùb(„0Gà=Çr‡ûFôH<`{øˆ wf¶;í™2ËwÐò²³ñþ”4C€•óìlwÊë¾wÌÚDxÉR¹YpÏ=SÑB/Àø¯°ZìÒ âü!*|YL/ëʽéJ㜠.É¥•³…âéÞô@ÙÙ¼Àz·|ÇõÛÝÎôµÁþD«Jö’¼/ ý`-íT]há?Žßítï$• iÞÔ.0âèÆ—ßmûÚJ;dž—`ÕC'•Ô*¯NZ׉¹ú ã‹w0ùõ/Ì{y¢“Úëi—kX‰ôÆ7Ìž¸çZÞÆVöz™ÄAùÉ.° שhu>Ï5~{™½"ä0¹èݾEG®”‘”‘ñQÈÒGº2úÎw¥®ðÉ5Ê+þŠTäež. «ÔJ7ÇrÆ·¢wïv›ùFùrlå#åÑ @è5"êD ˜»¨]|޾݈ZNäõ(ñìš±!Z2x]g/¡eX„.Ä-h ~Ìš«óž˜xÏÔ8Û‹VîGÖ»\®$¼EPý,±Ìä-æÑò¶l͆î±Zü‹zþ¬‘.÷Ùó]Îï@@%µ+;ݹ݃volLff»â£*ËÏÊY0¹ ¢¡>hA·Beíò@!}â}ŒÝŸª(/Ù߃=sc Üµd·?»ßõhë|oA´Ê/GxçK3TðH£lD³cå{ Ë4ÙGHç89K|Ç®©g¢%$0Ä𠔢ŒOW¬íïr-ú&W_ yê”2ÿ½eƒ]Ö“î´P²þ€rÓæ>%ÊS¹ßÀîÞ^©@ŒTjϬ‰óìvMéù„GWÝŒôö@šÇéG(Çm53®Æw7iÿÁ’‡Ó¦ñõ¬…¦ÂÙVK®uç*[ˆÜ•‰ Û ¿–CZë³\ÎÿYöåï"Jr¹<å·1~÷e”G+¡÷H°´‘X¦Ã[ÔŠiBAaž¬ þS+Ò*‘‰bɼ çæ@ñ‘íoYha>ÁI“3~b]Ú~ŷõd»{sÌÖ…÷vTÎÓÐœÊæí[äzv埪25?=åK»ÛÚxFK|: µ{“¡ð¨Zë×tV#LÏ?0sÍÜ çßÿžÔ›a•Ä…˜Aý™?L…Ëyã¬Ø‹úÿ¥ì¶è¢·4×X×(ÃT®Ló»ÕԘוè̘¯5Ë.`ÅíK0Ë-ªõ¬K»wüîªx3ÿÓ2oä¦ùV$| UD¡N­1t"¿÷È]B$lÛ]øOði°@ä„ÑÝ8ShQ_ä¸Ü å÷h !݆ï¨3×”¥¿H}Ã#&g\Ú¬e³/f[䛓WÜ ª¯'°&aÊ@ä·gMCÎÙagDÅ–WXí,T®ÎÕMã{Ì›I‹ŽåYjI3¯‡œ ‡k{Ÿ)V$BïÕ!€^í#{ á9#Ò!b¬<¢aä¤;ŸD÷yZó٦]yP!MïcbQš•6_ç:V  ?³•Ñ2 ~aF{ ­_!çc¶yç~tAºU³ïm—\–ˆexƒáî*à<;DOÃRCˆ3mÎjíËì°ÔIyóÍf{õ½{0b*Ù,CQ[Ë@äp™n þ”˜-P`†Js͘?!zÁ¯o(@šÉ‰ahM¾1ÓãÍæŸ_‘ãJùÖW çàš½`3Ó 9¢#Va\f)SÒ(ׂô”Ïñ›wbò¾&»‡¼…îM‡€\‡³+¦F"Åȧ^Î5ìA ‘@(îÜ}èø6ó¨e¨›±»]f-Ë (N®hQˆrݵ<‘¯6ÄJ9I®ìØ@²dz‚ÙÉ]Øp 6'¬üªLFå>C³D<*cbÅ*˾>Ü{ ÷sP¾©õ¸pöè-që÷U›ù¤ª”Þÿè£Í¹ å[ ä/™<Ä«b#ó <œ+ûêšÉý9BñJÚdÜB)'B Ü9èE¬» {¼ˆ=nª•pVÂõ¢”»ô*Ç.±Çüd4žZæ¸ÓÆÚÃÆŽc_) Ÿ.L~*ì5œú÷ÝÞzNvÎ8Î%¿E;G=éšk™ãLêøß·ó.¿6öùúÏ0;Æ2³ßG¹¦öôz×pìêû؉зØ´—Žx ×ðƒ¿–hé¹³ÓS_vò‘–1Ì0ÅS?ž+õ—RÓÀÿË J‡G0ó¸(Dën¿¦©cæ¹&ý!}È­ˆq»BÚáo5ïÖþ.¹cŸÜP ÷0Ͷ5]´so´AQ0Wî’EñŽòTØAØçÿ8»e0»ÚŒ‡Œ'*Ëí,N{¦2&V¼ÀùvìH÷LÇyŽ@é¶ÜÕ‡û ³ë! ýì˜Ù¶VâÃÙwŽ£Ô Vë +¿Àu•ÂÕÄìô”o-—#Üî.F±Xƒ#z=;ó~¨*oÈü£hÊMY®ÔÃæ û6ªÊ'Áò—Ç@÷`~FLv_LS£^a•0±â,¿ ú7à å¿-³ú|ÿýþÿÒe(òÔþšÆß*âèËCQdX(ü_aÂø·=\ÓîǷõ¶ç¼0ò†]NeßFeù¤²üe—oæG¶pq&Æô˜XõZ䡳ÐXÈôzKžóù á[²Â¨,ïæê™iP:vvœ{¸ÌRäùØŽ¸òçyÈ‹à„î5º±ÏßаdWÆÉr#ïeiYˆÃ ‡Ê¼ãÇÑøàò˜ÿeýf”Ž/†’O¤'£ˆÍE¾þÌ/€ :U† []ÒVœ~9Zf ªK …¶ U‡LJ2Bž 8ß=a->Ø=8àç\+\®û ,¹Ï½eè.µudÔ뢹 EâæŠn•=³ùIÙÙŽŠvßUÕñ›¢Å ÂùÃk+Ú!£_Õ¬ó2rŸ~hÿ£ä*éÎÁ´—ÐKq õŠþ½ËV}–;å¿–Î)ÿž1ó$ù.ÃA¥ñ²ÕÊU{•åõÒn®+e]–kÜNù\v3…·°^¬{e2,7òþ„람;GôSvóªìj+#œýÁñÙz«Œ‰7^2WQ´ P~jµ Ö’‰ûê±½ÿ§ ­?*ßõÕ•¼ña3µyÿu¿;d<êUèÔ×Ùk䓃Ý+–Œpò†åGÞ+ûn*Ë'•å/»|ûs0?KW­¿îV>‘’â›Ë”5%õ#ôö]ö¡|KVÁò®”ƒùðXGÜ+/Z~pBy”õ,ï(´±ó:J¾ WeœìN—®üãLäüMÙîñ¾oD¦ù¢·‚îd‡Ûµ\YJÚÓ2î@þ3â´É=7 òÇ®ÓKîì—’d&©ª‚£gÙaDÆÄ‡÷¼æˆí;)I_†?ŸSÁ”W‘†òg¢s¨À:d^þ g£Äix¿<éNÛˆønǹ岋ÝEÅ(ßAƒþ •;ÈïÀö +Xù‡tÊehY µ<ÏïœóMÈùÝäûÜô‰«‘vl_ÞŸßmU^v‚(;ŽwBñg^±¿ÁÁŽ“äšÕ69-óœ÷0”žnB~ãpñ•˰»ÏIŸ¸2ÚQR.½–}evµ©´!à­ðÊÝË1Á 6ü +²Ó'þ VU·('«Ž_~wÒ*-VAÏ Ÿ…^ OÈÑál'ÎHþýÁ>C~¹ÿè°z| ;GMžzž /É5í(|¢=;içùóf8y£bœ}Uå“`ù«¢lû{0?šÂeï•?_ÈÖ4¾‹„mìžvÿ¾ç ß’Ý>XÞݦÿÕ'“î)0 ÏIrºŸÆÐH²œãóÛ­ý2”9ÞD§û% <„pOeñêÓv¹Ös N–u7¹Ù-ž÷™s±¥˜u•Ï(ã ¯¬’§AZ~ü÷ i“½e(‰†³nm3ñ­5¨|âOS¨s%Àâ?ñ.ãÝž-¯;'q]‡‚ú-üœáóßQÎpDEåáÿÆ/ØnÉç®iÊ«øx¯“Zï×Ôð{tgí¼²Š1¸$Œ£ÝΙêóR8™ ó6»k£DSc›ñÐB¹wtª»»Ý.ÔçÑÏ‹Cý^®ð†¼n²–¡Êæn¤+£/d]ÛkûÜ– (¢ÀrïuÄ 9Žá¹Ä4ÍÇЪ»ÜÅ%(~·–´"*—áwXƒ‡Æ£l’ÕÐhG›cQ‘ɨTw7fŠÄ˜v îŠnÊûêѽó׎ë3ÎÑLéÁîD¾ù ?•®Òm¼—@a¸«Òª ÂÊTGÕ¸0Ù÷Uœjé^}¾á7ì­ÚÚÈöhE2ŸØÃ‘ϲ7 éᨄŸÄpßõKV¬ÅDL¡ Ý(—O+~Kå{çÌÓÐñÝ™@çÄX=ëv ¯ø=é^¶Æö ø Ïü€ûPÙ.õõV…¹bŠüÂåò1æk0£´—O˜âfÌA:LY”¶b½$ñë‹cá’uý$Po”‰gØ°× Ù­Ÿ’,®ž”ôPë(%ú®ªç)Š:ÝhgÄFµh»QùO?ÜSíîNÖ|פ?¡]oÙ®»Ï7uózt²-¶XÒMÅK޹!3^óÛgb"ÔŒ¦!SÞXVÁøûZðœgxÑ“‰}‡¼~—ä„å»oŽøî ì#4ÓQ®1àL¾ú?«A_Â^L„lnIP™7…?œœôÔ—äht‡„—<§p_áDË­u¯J†å®&÷šÆc«þÇeÿ_—©-"˜èŒeÃÉsÆŒ)±º·s;­–†wAؾö>kÇõ=#†µKà~ ºå/ÔTõ|Eqß¼yŸëÆõ½ ÃÂoÇv+ªI 9kôT_+kTØŠy®šæ »¸Hå{öçÎŽ^çâ[ÿ •ð *‹š;3®u\®å&зdÙUu70U ù{Æý'.HŸôæà8á§³œ4™ù/ ÙÝ«ÅõÅœ„^ȇ@I­JfP{…ïE:üy¾Ôh•fŸ<É=Œ—&$D½m÷(mIiwÅï9S&.÷õÊ õàûåq·û¥çúM ^)vTœ»Ìq‰Ååd¿I‰úg㓽?Ý?|X]”vyŸ•WQCÁG?½‡Xýxõ¼#3.Â|€K0H<4Š‹AP ~Øj®»¼¢Û.jj2uÇ}ùúЊv¡¼£ÍõµHËc(àXüX‡M¨ E–t#—éÞ’wU¦Ž·Ï¾Æ¸Ãßèþ?Ö’ƒ‰ŠÇ@9,9¹ = Ïc â"Ë­uU†å¾&÷êÇø•åZ7˜ŒÎÌlƒß­?ø„!òñmË6ïÜ‹.ÚKj÷ºö»b\Ç‚µ÷õYñûؾŸ¯ÛûK¹ôïÇd]µv®,פ¿ éï\3s0¾¡.•”ÏI®iްˬí|b—]ñYÎZžúJŽ;5]ÚÉ|b}è[ªè¿²wEm–»=ØÝ@þ…é &3¯CË}Þc®û÷Ë9 Šƒ§ Ò½Îî6¬g¡ý…¹ÇX~|JcÝy´²u›¾í_è1Z6kÜ8OAд™èQ"ÉÊ'ˆo"H åJK6Ýë?z« tš‹ðáâ£gÝ;9R¿ª*L“ñÛÑM–-çX¾x ]Ç·WôërqSåÚHhíÓLxJƒ×uÎï¾äx8d Q´èw+“"'#&;ÓÇT\×.×5êoCæ,ûA)KaêëˆãM‡z3¦ÂÞ’vv%ÄåZ¦ fÜ%³ÆK—0&NÎ,Ÿ+“!—pYî¤Ûê\•Åc¤Ë}öHçôã¥Ü`ñ?}zs°çbåÇVøÁ˜ÌMIÙƒVXsûüìÆPQ+˜}lù§åq¥DÝIDAT{0˜kcˆLð~]æ@®jš7ì2«“O¤ÿDgæ²{Û.+Ôg9VoïlÆ•YÒO°oÉ’'‡‚í-`¹‘“•å³µ²h´+³Ò¦•.ãߢ}–åVèæY°ûB¾‡š¿ìîä<xm™”6­”±Ý̸ëæOš´yüf¬´zIšË«²´aõÂrù„ó¬¹=‡#K}Óÿ æ5Ýk^†s3ºõ÷¢‚ý¡bM{,Z~IE¨ãMf´³¯ƒ–vQíãßŲ·¹÷fdt(**_æÍOŸô#2õ»è »7 Jk1Í£xñ[μŭ0+ß%gµ[ñ tç[ö„ŒÏy(éÉW²ßIá¾¢[Ñnù'âÑ izÌo¡)CÑzû q\œ«¯];¹2bMt»„…Ò—³‡°§‚loËõ~áå{-^.'‚]y Ì3ñØ=+}ÒÏAeØÜIÕ¹*‹‡¡‹IŒ•¬‡ÜƒÅcÿŽ7Àþ=ûÊʘä¸R1~NWu(1b–‹=­·ÒŠ &yïêäŸ 陆r»]VUϾ®½0Vßš)Ê<Ù+ ýTö-Å%4û-OáËûŠÏ…Óï+ Cuðá†W¼ˆ¼Tìõš¹ªŒ“î±Ãꋺ¾wòç7àªãoz!}K|íù°2Ù‡¹ãêmÌÔAæA kÆpM&[üz~¿ÎÚ9-½*Kå‹݉@ ÈV¬ì® %ôÜ ¥aZ(n+º‘½{¤¹ùˆ¯‹¼‚§‘S§¶²’ÈÝO¨ÏÁâQʧüîâj8ä®a'ŸÈÊ•ß:k¾G¨)–ß`Åo>¿òOeþF¹æÆ²—yIŽÙÛíBÍ_ÁÜÉŽìòè™L y²ûJ¹4«'¢N"N@®òM¶‹xH D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ@Ã#ðÿ\°€Q|m‰íIEND®B`‚neutron-12.1.1/doc/source/admin/figures/bgp-dynamic-routing-example2.graffle0000664000175000017500000001153513553660046027050 0ustar zuulzuul00000000000000‹í]kSÛHÖþ<ü ½ùºAôý’Íd‹Kf lL’”«¶„Ý€6BòHÂLå¿¿§å‹,Y ÓTÍØ±N·Z­>ÏsúôéÓ¯ÿõý,ò.Lš…Iüó 죞‰;I7ŒO~~ñéð—uõâ_oÖ^ÿßÎþöáo½^f¹wðikowÛ{±¾±±ÙëEfccçpÇ;ØÛmzPÇÆÆÛ/¼§yÞ{µ±qyyéVÊï$gV0Û8H“žIó«=¨l øÝ¼ûnÓ¯½Òøµvò7k?½þf®ÞlvòðÂìW&Ý»æûë û+\ ãÜœ˜ô z½1ü:,÷;AU~î×<*¤i`¿üô:ËSxä7ÐB?9‹Ã“49ïùûðí×48>ŽŒx½1“ÆBûXBó‰ T 1‘×êûM8Ï“Íîÿγ|të<=7Ãë[A盽eÜ…ÛõNÃÎHlðì1+‘ ¯šñ÷ß襇~¼ôþæ\ûDr.$âL2ÅÙKO*â+¥%ü„¤Â„ÿøQy[Ùvd“õ¶’(,T+²»SÊ;œŒ÷}_¬•_E¦”>Nÿ"T™|+¯Ž]î_ßIƒË²YeÃ>ì7:{ÔKÃoå—~÷fæk’œÍ0X¶ƒø"ÈöÓð$,‡É°Ÿ‹nËE‰$:?‹7£ð$ž¨OÖ_H·zAªɧ&ˆÞPcÅ—¡(üÃÚ 7õ¦„Ù:ÆëyX¾¢äãÞ?üM´ÎV’¤õòïƒ<÷~‡—\iP/´f½(¸ju‚hâΠ“ðçuμŸ½Ñ÷z¿„‘9¼êM@ê²ÅÛI:çg&Îë :êH2Ñ“ƒ™Y iPèêH«iΘêpá M4¦ˆs†µ&ä¥G8÷1V)É4UZðB¹š$•O™`\p ¦µÀUíjR¯R¿NƒžiR°A'&q¾'ÓõSùj«×ûGcG·E¾ Um(Ò,¯¹ÔŒ5H§PšÑZRKÇž¯A¹÷{&nq¶Þ2gáQukÕ¨þeÆ‹j#GêS¹Y­ÊA%­V_ô}p›|üuÕOÓ¡à§1Èo„¾‰—wFÑuï®þr+ ¯wÐ ÈG„2ÅÔXo4¾Þ‘<’cªË¿†’icI#XYPTJV^xýåg§A7¹¼îù·L ÐuÚ4ÀþxÛºalÕ™eÝ˨âH Ñž½£‘Ä€×–lîhJ|Ú+úáÃ¥±I?Ýð<«}üV¯–šÑNócÜ‹ÿuz'`sv'é$½Äëÿ#;?‚3‰ÖþnäGQûµã>;§Aš™ypðÏ ß±o ü³š.Àuû$5&¶_Ž¢sŸƒ dp±þ‚ x»¤Ývþ ÿǘBÙBXñ¡ý°VýÐ…c…G…”¶"B‚ˆ­7Lã ?Oƒ¨ýgÇþ‡'§¹‰ƒÐ0Z[³xÔ>Î0óÚcâcûÍk™Ì2_{m³å1Jð›Lœ»Qœâ>¢š ÍÅD+E›(Ž GqO›â(YaŠÃ`¤ ©b>¶c>Áš<'ÛIÅ$\ÇvŽíÛ݆íÚkãt‡A›ï™î¨Ä>…¡¬˜º0e1£“ è-μ6Ц¨p_:‡„¡èG“È??UÞ@6³rŒš›c$oà˜;RCÝõ3Í÷s¬½CÝ7ím꞉*ž¦ñ÷€ªoáQpk¸R´ÿ;F}´d²hÕ5Çypø2.û¿áOÓæ8#¨/›Ýn Èâe¤g¦aJ¿g¾€Oª_õ5Ý}êæ2—}˜%%à“K EbÑg°4²Þd$•¤ A¸æ D§)Ê˯¼·¶6 «½4ŸÙÚèÛïeI2³µÑ7äË’ìkã©"ÓJ›€Až›4®<^£~òÞFÓ ö1ÌDeÅTZý•Ú§Òj0¨4WRQi5+Pk%@)¢—vN|‚˜$0 G”+-Ľk:ušî4ýMöá>BgRc.˜f¨]HffHS˜‚0¥$~¢‹J—MÃ/ÿb^EOØrã¨s„9GØÃ;¦Ì,—Â&úóÔÝ8˃¸c<Ú~ì†|sQùS°jéýqdH¤Ï¹FœÁÜXh&¥#Ã%CåÈБ¡#CG†ËD†d9ÉÜ3Öc1ˆpdø€dȈ#CG†Ž .âå$ÃE&RŠ}Á)ÐÌ œOºI™*"5¨_,« Íg0qd52„†)åŸN`"G÷JºöõN¾õí(9ïN¾íÇ¢hc\»úǤäTÏÎ)D˜  ÔF‰Ð™™CJIá~À<\*-5_$E»U-ÇÏ«ÂØ2ÑñzfÒ‹°cÚkL~™¤ßÌÃ)b þÏ5µwö™Ýロƒ ôYÿ¨õÞ°4ÊçÖjaJ B4Ó“¥¹¼Û¢â“ßeö‡–Ãy3M“ƵnÔdz홓 sU?¢¬ÈþU &gLº6L}ÑAÍ×’/a·¶~Z<*™•1à†skr=ìú–šìqŠƒè>/öíÞG˜œmÅ´äs!c­´ Ï—Û%æñVȨ2.'œ÷Œh‘ȨµO`ö¤A-…V0ý³"£õhÕJkòaUq#öé£ Â¼º|ý4éƒ¶× 2U‚´×*±£”L©¨²ßbŠ¹Ç„>ÏÍPO}|Æ2mÊ7²Ÿæ§ÉIÑVÚ´øgA^¦»¯Ú9“ S»¸ñùÄS 6³"B L‡©3k ëZíY˜eöþú¸rv“2Åê¤É_wg\_š(®1¥½ËØ3u†,yöÏÇ3úÉö†ÔÕ>œÑO)óa:Ó@ f^z b¾õ:RÀ-íd¯°ú±M%É]ÂÈ»ËÄË.aäR$Œ_ ›sÌÝž¹®‘ù]X´°„%ï$ÅP QL1F” ʸ_X@ZP…„ z³(VË1yßy'—ÄLžEMk]Ó´·}è‰nš3oÜÝ€yQv’¾Ö0e Zq¢‘¶ ¬±³Ba… g…8+d)¬ƒ4¹»&Z ·°?J7Ùr™ µß4v&H}'´~º&"ÈZœs°('döÀÎ0æ ¸ q¦B³;ư#㆓½;¦+•¾ŒGº=³ŠV+¶ƒ“`!µ“I&„šõô+¥]Æ¥ìr]É\¼+7s—Ü&UÞ¤30?&ç9ØÞK™Š—ÖØ©¿ÈÄKœXOÉËfåDÉ'.€Ù*râÊårpœè8ñùpâRfä%£¸³ûæÄZŽdx&NÄ€sÂqâÝó>¨•<½xå298Ntœø|8q9ó8û.¹Ä$—àê¶A!à…*Œ”dš*-HCr á’K¸än§àÄ:‚ÛB½ôÉ%¸\(2Ö“K—\Â%—pÈ8áMvȸôÉ%¸X$2ÖvšÁŒß%—pÉ%ž=0N¸nŸÁÊ'B_‚¹s8et«;]þâ•<솸üÅ.TÔù¼Ïû©%0îL%/^àŠ+Ç6°Y V«’0ä „Xìx %¦k”asÿ â[ q‰ X[lT·âz×MQT°U= •k…¸|{|γPí– ôa²9ÏBÅŠ€d¨rÔã¨Ç„:ójëhO\Ùõúm¶Ë¢ SäºjÀµêŠŸójMAûûðjñEyµ¤ ³b¬0ÜbŠsÊä é“$ÒÖÓ̈u÷.Ï LAXš¨çš>i©'bέu?'Îß¿ÐrÿÈÈ,?ÖÊg„cÍ…¦Úzô ó‘ÅRVÇXs¼ŽÃw‡ïÏ ß™ƒì%€lü¸]KK"ñ@öìD²d?È~ÄH"¬‰/m®F•bDJâ2Vßo®HF]®H—+ry3V“Ûf¬&—.KîK›1‰Â’¹ŒÕ³@yºé"W.ÌóÎX]h–ÒžQ3È™¡ÀÚGJåBšW0¤™ áBš9áˆÐ2liöý!\"EæØÂÀ¨çÓâ£\,™‹%saÌ׆1¿ýž(˜3—%ãz÷ÆÀ߃»•Ýú8íÛ„w]ã¨ÄPZüœÖ} LÕ³ôT”F$ãý‰žÒ@“5˜|šA§ÎÉnð")%0cØÅ†-ÔQ)Øc9*›vxÔ²ç)%nÚáÁ쨆”Ûá±€œzØíðx;<\N=g˜¯þ&­_¼Íç²½cε¦³×”tëi Øf¨ÜÙkn=mIÎ^3Æ>ߎ^³àÑ_I£hyVÒÜÁk7|Ò¯'‚aŒLžb¾ÃÁD°fÏy8˜äPLÁy.¯-k7?…BŠLãašÆ-@Õý¼¿P¨}jW á×Ä®úÚ q€2„¹]R”0ã"î ¶ø¨ZU?ÀØìÍé`×/æ]ç(KºÜúÎàüsùöèR¸šüÕ3ßF©ɸä é"°VÛ\EÌùîæ ;?€ó,…`"0tds†:óÁÒ•L*J‹ÝAî õ™p=Ý©¼‹"{ú‘°†ƒÛ‘s–ÕÎrp–ÃòîÈÁ·Ý‘ƒoGN£ñáväÜEê ïÈYµ³(Ÿ·‚9÷ ϘÔæ ‚[G»/*ÇÕâbú@ì¦rgíÎy8CdµL‹éÎÑ㌒2Djç*ÎÂN‘ ·=O]íÎz‘ñ.ÃüÔ;Ž’ÀVåíxA·›š,3‹ôx” BEÙ_Ó°;®À£×:ºÚê¨dtƒA\´¯Ê=‚Hë±å×ÁzÕÿ’´±ø(œ¹æîssr™ÙR¥x3Võ¥ã —&3È—O_<ÕyØ5Ù^ÒùfF{mLús˜…G%rL¹Mœ˜‘®5Go÷Që ·1r“Î þ»1½Ã¤Õ ÆZ1Š•ïÇÉW&-ï´¶’OS艢''mö“ýí÷^ƒ²ý·ßÅc6]ÉÃO@ZAª‘kÙüŠ…ï’(ø.iÍ÷1šT ¯óôÜlT®çt÷ë}?¼K“faÿüûè¥gâvÒ ãÓŸ_~:úe]½ü×›¯ÿogûèƒ]¯…Yî|ÚÚ{·í½\ߨØìv#³±±s´ãì½;<ò ŽÝ/½—gyÞ}µ±quuåVÊo'çV0Û8H“®Ióë=¨l ø¼ónÓ«½Òøµ¶ó7/~zýÍ\¿Ùlçá¥Ù ®Mú.î˜ï¯7ì¯p1ŒssjÒ7èõÆàë Ü;l9Tù¹Wó°P¦ýòÓë,Oá‘ß@ ýä<OÓä¢ëï÷_Óàä$2âõF_dD íc Í'‚RFD^o ªî5á"O6;ÿ»Èòá­óôÂl ®oíoö–qn×= ÛC±þ³÷ŬD6¸6lÆß£5ýXóþæ\ûDr.$âL2ÅÙš'ñ•Ò~BRaÂü¨<‹­l; ²ñz“(,T+òn§”t8íûžØa~™Rrð8½‹Peò­¼:r¹w}' ®Êf• û°?Úèìa/ ¾•_zÝ›™¯Ir>Å`ÙâË ÛOÃÓ°&ƒ~.ºyô-%’èâ<ÞŒÂÓx¬~<^!}Ø ÚPÅP>5Aô†Â+¾ DávÐ7… ÌÖ1^ÇÈÃò%¯÷þào¬u¶’$­—ä¹÷;¼ôà*Hƒz¡0ëFÁõa;ˆÆî : ^ûÜûÙ~¯WðK™£ëîXáô .[ °¤}qnâ¼® ÃŽ$c=Ù˜™Å…®Ž´šæŒ¨¾ÐDcŠ8gXkBÖ<¹±ÂHI¦©Ò‚ÊÕ$©|Êã‚P0­®jW“z•úutM“‚õ;1‰ówñI2Y?`0•¯¶z½'pyüV¯–šÑJóÜ‹ÿµ»§À9H«´“À^bˆõþ‘]ÿ™D/þnäÇQëµNâ>ÛgAš™ycpðÏ ß¶o ü³•šÀuë45&¶_Ž£ Ÿý ¤±Þ‚ x«¤Vþ ÿǘBÙBXñ¡ý°¬Î~èB„±B„£BJÛ !AÄÖ¦q_¤AÔú³mÈÃÓ³ÜÄ'AÌ0zñÂ>âqë$ÃÌkµOˆw`Œí7ïÐdÖòµ^lzŒüã6Šs7§¸¨¦BsE1ÑJÑ&G…3qOÛÄQ²Â&IR#&ÄlÖŽø@kFðŒÖNr(&‰à‚8k笳vw¶v”ùž­•€˜KÆ%åðìRXk'X7¶@kæµA4Aƒ{ÒÑ(" D?š¬þÙ-å-¶fZ£f61²ÉÄÜÑ2Ô=?“\?ó@íê¾gç©{6 ª8šFߪ¾…G­ÐJÑÞïõÀ €É‚UÇœäÁ1ÀK·ìÿ?M˜“Œ ¼lv:) ‹—µ“®™„)½žùZ<®~Õ×4úÔÙ2—}˜%%ГK°ŠÊ¢Ï¯4²Îd$•¤ A¸Òrqè4Ayñâ•wn²Qö’1 >5ÙèÑ÷²$™šlôx|Y’ÝB6ž*2­4<òܤqåñ}ð“wM'ØÇ0”SiõWj¨h0¨4WRRi5+Pk%@)¢—vþM|‚˜$0G]ˆûÖt¡¦;M¿UÓ¹`š !8¢z6M’R„…RÊÙ4]`EÍÀš.n®÷xÐÀˆ"“ÛþdL;À… à ŠüJ¤. ²5ÑU™ƒÌë­†,¦ö×õÜ83øë ï `Ýì“)!Ýdêá&SOÀ«3Ï<é_ŒèM–Z‘ý¯M¼ƒÔœ„ßaöti Tfl$>kM7T'±ñ0¨²o€óbèÛõ³¤ëDí¯˜ú¸õ¢ %È=NÚ&ê^›;(&@Æ[]š’`2èýõѹI7ØvÜ"¢­yZûÐBKÅ„£&èÚ¸©4P\c Ó ܈ÕKÏ‚y¢TtÉ<¥Vn¦i¥{KÆÑÄMöÌiо®ˆ(Ã@i4yª¢¦5_ëÀŽa4[K¾„Z´©xT2m°n83 Õc‘sÒ„œRæ3¥`"Ž@Á`nš*³¢(&á*+9¶áî‚wå±.(à‚Ë84ÑÉzfÒ˰m€sæWIúÍ£­©ëæ‚»Ò‚»>Jlb (†(¦#Êq[`ÜW€`B ªÀªˆ-ÔâM±ÍÙCæñ<MžE!û@ãϹ¦í¨iËOÊl¸ZX1³VKOï|ÇH! A”1‚”’<5 ²\î‡$ 0¥¥æ‹Ìs¹_¶9£oðnÀ¼(‚¤¯5L¨VœhTø5v,d,9âXÈR°ƒ4¹ ;&09øGé&[. RûMcGAj8ÄõÓ¥ ˆ Ë 8çÀ('„MÍA8Ø3°mˆ3Åš~e c`§0œìÝ1}:d…Ví<ZEˆÖŠíà$XH-´ÅèÓ þL»ÀAé¥ZàÐèÓÃK¿ð€éU\x°ró1·îÀ­;Xåu}êü1¹È{½~Ö¬áJˆ’ÌÓ~Tœú‹\ô×h‰ô9×ÀÆ(P€06­M”ÜÙÄØD¶Š6à8–$ Ú¦Ÿ€1¥`Õ” „H–qz¨fRªG0@aÆéÖâ9›èlâ\6‘,£M$ÃL±û¶‰µ$ÉðT6Î gïl©ZÉêRŠV˜‚aÃZ‘ü:ð–I$â7ZÓf¨vÇm 0¼‘ÌÙDgMœÇ&âe´‰ƒìi¼P›H)ö§`å`NHà|ÜwÊ ß©¤~±¶Mhx ÓH6fmú‰ÛæÉlÃѽÚÞ ñ­(¹è¸ ›'šaãÌ´3Ó¿ÌhJgëŇANç}›Åšû”3gWÑ,2ìÌâÊ9uYtfñyšErÏf±æA…¡åÌâ šE@ggWίëÌ¢3‹ÏÓ,ÎîD}nHptïHpÕ`S¦ÚzaüŒ»ó‰Ý%B2Š0fõý ÊÒT(ßîpE™ˆÍôxi®žåÖ Ëí}¼ðÞy¡žŒ½èž22|ÿÈ(ŠŒ ûvZEp+s²™±Vú™nJ³Ü>1‡Œs!#sȸ@dsÜ2ŠE"cm».˜^Ï‚ŒµÒB?Sd\j·ˆCÆ9q,1óávë"XùDH0˜Å¹ªBك멻ÎǼ’IÄù˜Ý‚{çdvNæ'w²U—¡ƒy‘GZa»=„ h«’0!ä ±ˆºEEÀÄ´¢@G)¯y‚ø–#!.1¶E4qëVî¼µ+¹–Óò®â ð øøŒçrØ„q æÃ’²ÏåÀŠ€d¨r¦Ç™ž%ß~™Ö¬ wë-^¹y_0ÛåQÐ.öøoÀµjÈϹµ& ý}¸µø¢ÜZÒ.Ve¬0ÜbŠsÊä-›ÐK¤­«™ëÕ"\ž˜‚ `š¨çº ýROÄœ[k‡?'Îá¿Ð$rÿÈÈ씬•ÏlšjëÒ4DÌGK°&Žq=Bpï§ŒÜ-Õeëàðýyã;q©.ßßé£â;–ÊçÅ ˜0.ɃŸ"u·„‡ïߟ¾3ÙKÙøq![HŸ0†´M¬˜“x {úL"Ù²Ÿd?b&ÖÄ—vËF•bDJâÎý»ßwu'î¸w–÷Ü?2ï¹äñÝÁ’ûÒž°ƒ˜Ä aÉܹÓ@yº‡î¸íWVêÜ¿zF³”ö¤ïþ¾ Ø>RЏMW1¥™ áRš9áˆÐ2miúõ!\"EfX€ÔóIùQ.—Ìå’¹4æÓ˜w¿çŠDÃ4æÌm“q³{c àïÁÝÊм¹aó¤wÝà¨ÄPZöýœÖ}˜>Ïm2 “F$º ê,ˆžÍ¤&k |šA§ÎhÝàERJ`Ç°Ë [¨£R°ÇrT6­ð¨A¢”¸m…³£J`Rn…ÇN&Án…ÇXááN&qÄ|õylýzàmžš8å3FÔðI œjʼnFÅJD%]¸6iyçaà¯f¤ìƒß6\z’‚ó¦TÜÆÃãâiãŽ:fæ0‚ñM׌ϡ¹ºµÚIZ º9yˆoÆáy›±TS{µ 1yÆ;a–×Ô«Úø/d (~PÙh˜¿P“ŠfîÆ§a<Þ–N’5Æ»2bÏħ#aÈÁ ÈÀi¥"´²±MY~†uéä«ÊMü6I{Æ•­æžA>í9<3#±ñÞÀjÅû¤žÀ<özgäM*%‹u¤Ö1òy…ù+ª½Øž»WQш®ö/¿òüÌ\y¿9 ® ê?$¹™þ‘öÓÐÄy¥“›wåHÙºr‹M[ðž¾eSÈZuž¬8·’&Wçå ÔDk4²guhÉ:ûôìÀQ˜#áv_Y…´ôJœ3/ša3zTo³‡—Ó0½¢°%AÜ63–ý‡^˜râzÃÓÊÌnœ‡éMÝþyZÒù%Œa:Õ·/Òz¢èÉqÎÞ@Ùw¿wƒ”í¿½.át¥î{Ò R ]#hÍë`ˆ‚¯’Ö\ÉAÅÏð:O/ÌFåúÇ‹¨4ÎcðvŽƒ‘Ëå2ÓÑëÕU 7=7(Üa¿5V³ë“#^gO}ýÑœV€kØ댬yȵ?Z¬Àd¬z®yTIŸŽþá†Nûš$çužêóZˬÐç¦ÅY#r4d6èÒOõ~¤×M‰\c(S~v#P«7/þKêba/êneutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-vrrp-compconn1.graffle0000664000175000017500000002065513553660046027032 0ustar zuulzuul00000000000000‹í]ksÛÈ•ý<þXÚ­Hp¿gRò#±[V$g3¥ª-ˆ„$ÄAƒ4NjþûÞ(‘ă"(P©ë©±d²Ïîsîû¾üóoï*LÆQ<üÓsê“ç^8ìÅýhxö§ç?þË®yþ矞½ü¯7Ÿ^þçÁ[o4ˆÆ©wðó«ï_{Ïw_¼Øá‹o>¿ñ>¼?úìÁ1^¼x»ÿÜ{~ž¦£_¼¸¾¾ö7ÊïÅnàøÅAÂ$ýþ¶ ü~Ú§É>w9ði?ê¥?=ûáå×ðûO{½4º ?ßÃäý°þöò…û¾Œ†ix&?‘—/n~½™çŽzA ‡ü’ùvR$û凗ã4[þ ®Ð/†ÑY_ŽüOðÛ_“àôtª—/&CfFSe}Fàò™Ò”™™!/_Ü:¿„Ë4ÞëÿërœÞž:M.Ã7ß¿ z_Ý)‡}8Ýè<êÝ›Üûd˜1¾ùîö2þó²ã‘ßw¼ÿH ¤¥TšH¡…‘bÇ£RI_k+%ð;“¿ÿ>w/î`¯Á¸|Ü£xM/¨0åý›éø›ÎfŸ}>ì(ý>§#on'ÿ~;óuþý›$¸ž^ÖôÂö?Í^<ìÛ§tóÛô—üñŽÃ_ãøb‰Åò:^ãOItM—ÉÍsÎóì[ÎfăˋáÞ :–ŽOËÇÏF‚âv|ƒŸ8¬±ì—›¡ð·hßiX¼F¨Ø¥t—ê9ûQHïþ”®Î$NŠó?iêý^zp$AqÒ›h<ßzÁ tfØ“ðÇë]xòn/à/Ñ üü}TšÀ>(ŽÍØ›¸wyÓâ½}¬ô$' sì0¤bCϯ´ÂΙÙ:TÃÖFsKl«éŽ'˜ô©UFh ;FXîö¥ÜçF¦(%†[£w<©|e™¥œHØ^Ö26¿¹ªv×t{£°jMžae¸OI¹ýC—~Ê‚hªõâ™ÕO™3Á•´Ó?µOùææ’a˜ýèrþù•v×Üö*ì´Ïáoiý2žÿvºŽ“ô”z£3Ê$;îŽ8p_ "òŒ/OàßB“gÿ9>IOǧäøtÁÏÞyŒÃ”x% ùãï0¾çÞLøãqö™”ÇgIÝ/'ƒË~f_žNá”îsJÌ>Iÿ8ýM*ÃgÙeÜÆDö6üà<Âm6Dˆlˆ$Ù˜í~(š Q†¸ãFÉ0H/“`pü­ç>H£³ó4ž=`Tò왻ÓãÓ1UÞqï”yçßáo?L¯ãä«ðð,/{p˜ð÷»„–åIKPr\(\±RÔp -i¨Ï˜¨éO®´c-ˆÈÛd¥(-,¥Éˆ¾=ª=˜…›¡‡á8#Š50ÞIÂ_Â’fg*v÷Ìb³U’ê àÚÜY×F¨F² Bý ÛìfŽÄÉü‘òÁñ8JgĶY´£€±Ô2Í$5TÎÕ}§™”;+.þP«S”nøfúÌäÂ4¥°Š f-0)U³O­f¿Ÿ=*W®rìN“I~˜'9‰ðNÈ”BÞÃR‡ ƒãg_>ìí×1FþœŒ.ƒëük\[ú|°ˆTÁ[´ð™"@‘C™¡·(ääÚ·˜†Ü"Œ’qááYm“ YL.HH¦ˆÏ—Ãa8ðÞ¿YÌîa†ƒÀÎ÷`ƒTþ.ÀÒĆ ª­Âô5Ñ ħÀ/Úp2¢•Žn”ò f5¸0f|.”J2м­Up¥åÏ:e€«¡ gÜQ #3鈌‘[rØÐ§›sY«¡ï‘Lw„ÁF×YxY•`µS)§V%¹´UI«µ˜ž²UÛÝ«péyÕZ+Šfh¾Có]¯ºóæ_ˆÜ~Çéœýî£U#,àŠZ%©³É›4(Ÿ€ôo,ãÂ2Ķ•Ä&ì6›6ŒS>E¾å}RB1%ÍbF¬†[Ê´É É É¬kd&¹ñ]<„„)á<ÆÈf[ÉfÕ4TÓÙÙ¶‘Ù* LÀÚ0—$W.˜AfÛJfÓ¨§¡ž†l†l¶-lÆ’† ¦Ì(%Ñ;ž2ÚW ²*Ÿà f‘Ü{=ùè +E*Ì ²IôÆ 2v3|C¨ÖÂ7lÃð û‰f%%Â"ôCÅ2CÅ­rANBà °x_]š‹ÌL°´œ¡å¬„²¼œ/ ÄíéDu>åX~2OMÊbm·BY¾ì¿÷(¡÷_q¸Ïä#„¯ÉÈ8'BÀßÒr|kA{4†a™1–›ÌËWÎ+kD öøXÚ£jβEíqCl¦ÝÃyT-QµDÕ²Nµ<~–ŋ˜Ðãmú+xöºOA¤·ÚÁ)×bCy¯yÓ+ð‘@>B>B>B>ê&=U†Y.CDè¾Û(÷ßF÷]žP8ÅN±|\Ê$³pé`2{‘ÓÓÓšéXAÚ;?~–^×Li |ú”Ãâ…Û)m+)!¥!¥!¥!¥=6¥ 5¦´Vqñ÷à”T~®Â4êË¢±+"Y Çï ß8æAT–¬˜¿›šj KBU}ƒ>º‹¸>ÈšÀ>U\ 9)F ãÒ¹'‰ŸÆèW*©`²d¾–ÒÆ,áµô~t³±ÁN˜.ç½$‰+MÙ¤Jnúž½ïsÃOƒÁ8+RüÃldP!§v}ß r š]É/Q¿À²yØÎ²$'l¼“%ie'#2Öø"ÖŒdUd€‚ÆX­ˆÕ”€evº&ÊòËßmt”ÄWQ?LÖœtQ]áyvëxV Ï"Ï"Ï"Ï"Ïæ—h¸&ž­Rg·¦r òìbžåhÆE3.’0’p×+Ô0Ì}¼+’J¯?Œ’­FY•¾xG(㙓SÀ +ï`o „\)ÒbTy›pÈ ª|õTpàåbJ…Q.{[ˆ •“1ᱫÒ=Âá*pÈÂa«p(ׇ­æßUÜb1Þ•¢ƒi2˜&³Qp(ZÌ|áVùÆíʶÜÂ~Jû.'3M4£‚e_ô}·A°ÐBYaØŽG‰ð]€¯EŽI¦,æ½Ü?ï…YÌ{Á¼´ºbÞKGò^œáõ,ɺµu1ùeA-¼‡J~a°F0IˆÊ;Òiö(•Ü5*vU6îÊ5RqC‘DÛ Qƒ$Š$Š$Š$ÚÍXô`­ÓE ½+’èxn×w{GHb¨VŠºdn}f•~#Th“õGÖØù¦Ø©i»”×8m»”•6•]mÌt[ ’™jØ ™Ã.t%¹1 癇j "$• à0ɵ1Örlüä» Ã®ž° ÏÛ ËŽ5þ°·ïm@/a,}Ùº'ˆ©VãK—¾ä”PR¨!° a$sßÙµ%¡’E­hTù²¢Ä±ð%ú䟮Š·é„*Є-{¡¸ªK; œû v,lWC`‡j´,h-hhAC ZÐ:`A{ô¾^ŽŽŸÆ—i˜xûÁE8½YѾ%Ù¥=¶'ª,ôK›²Ì€V”QÍŽüé»z«éXòMÈÞËq¦ ,‡—&° UÔØÀjx5<]•ôZú›jßB*ÞZó]GøtÌ&춦×qòÕÛûá#Sœ Êgîè ˜Š;]‘ZãKI…tÍó±ÚQœ5„?yQõÛÄE4 ömà""ÅtÈCÄ[òÉê”ÊE"*\4@‰µÔr.ÊCÄ©ô5œ– 83‘–ô¡‡ˆ²É9†³nùˆ²üî! ~æ¼E‹Yæ^Nžznìó áF) ÿ¸IÇ-Ô—†s'br%ŒÈÉE!¹ ¹°¶ÈE5 ?F9ט+­.­ +„Åä‚ÑaŠ˜ôbxÿæþ1»KÎa±®˜‚zºqŽ+xdÜŽrWи¨6ãƒÎ­ˆv¶¢…AºAºÉ醶E7¬©.Ã\Þq T ;oCºAºAºY:„ÍŸ5‹º?æqاXëQº\$#|÷»Ò‚J…ÎBíÊõ“±¦r—j=6îŠncpG¯—±ª)qÆ/v\ÕU;°ZÏtX6Ý.%,íØ¸CO›Ý(u-Öð˜¤À³,[еŒ+H)1Îr‚̶Ì&·²[@VÙQP"²6ÔÖ¬²£! ´_YõgAeGct@†V¶ˆÄ†ÄÖ=b“Ò·®~„3ËÀãÏÛÍ!±m±qTÙPeCfCfÛFf«4F êkÎèl®™ª5Hl[Il56ÔØ××¶‘×÷¹5ÚU^Ö„Sí2²\!#µÒp?ðÉ2›¹Å’F7°Ù$+CˬxÓ°¤“1R·RØ4ÆÃµ«§$(-„´Jé‹WÂZc d•†b¼ú}µj.Œ0 D-g-_ë] ’w]=v’ï¶%à…ßdyѼFµ¶cUšöß{”l@™¦Ez¤OfõH%\w0WxÆhbéê Rš]˜öIö©¥J1<ë_øL>hÓ)UI;ÂVé`Ù •ïóu”ôBÁã™»v#’¥õ6`zφõ‰+×D9çn Wp‹Q¶2€%Ö€¤œy6ŒÞñ¸Ü9ù±MaIûe0 ·îÉí ¢³áaÑš¨ÞOÓà„މ4ãþºùhAr9™ô¸ “AðÝæIæÇy]¢ˆêšO^0ñ(Ù€T^B¬aBe¦"U‡Ið8”ÚÐ~)‚‚R'@é(œîŽÃä*ê…%dZJQZ* ¨:wõr,55œe …l®†ÓÁ¾Î¨à4Up €¹«¸V¨Ö&u]‰Ó¢‡›ôp?–‡[5϶i³”ꆄue6J©QV«RÍÌ3‚ÁØV0ÚÐ<£AD·`O1t£ûÝßÝßÇϲÄWz¼Íèå* ,Öø·J¨òÝœšˆx¢Ô´éîä#ä£'ÀGÇDÕ ¢Ë0†oT郭LÍJ#iëZ"QøæË§Ñd5’8±¾°ê•iFC¤!Lãz´0 7F~C~kªoiïüøYz9\3½1¥| ,T¦ˆÐõÝ”Ý6šÝ8²²²²[‡Ø ©1»=±N¬¥ôþö;±JeWîÄ*…â„+Qk×BÒ¼ç†ÈRu•åVY]ÛFùÔ*#´¥ ~YáfëIÄÂ$Aë§Ù5/ ¥$†¸f\¢Yùa%RAˆmXþ@‚dHÝÙ)ÇÖ¨m¶F-W\q+#4Öø(ÖfUh†úÆX­ˆÕ”ºHÐB;[)k¡±j¶a¾r½ù8¾6BâáJx¨[ÅÃõ§*ŠVSï*Q±ïÊæÁœÌ©ÙTd”-¦Ép«|ã6åF[n…ÜñŠ 1Y%Ö²â[”ضv.•L2e1IæþI2Ba’ &É 1“d:’$ãì±gIÖ:¾‹™2 ªÛ=T¦LAsWj&Õô¶|Æ©¦ Q*n(’h$*‘D‘D‘D‘D;@¢‹L‚xºH¡w=2#|¦-±‚Z.§bǃ•äKîøR¸p%-³ $®ÃnK¿çUšòꥦAHSV´T6iÊj•ÊûÑýúúU‹ª^"ÎîÓØ¦ÂM•uô GNì-]* 5ìBÉ™rVKJؼÁÇ>ŒÓLd.˜ ¦µã[Xo¦ÏÜIvÎ9°“°ÂÆ,csLŽýº×Ù¯»»¸aSOH›ì Ù œà—{û½›]Ã,ú«pVtA8ÑÜg23G‚âT…ÿ÷êúå+[÷ qÓªËW¢=Aµ‘Ü÷ò•âOÊž U›@A.aÌIéÀ0 ö.Z±îoÅâè B+Z±ÐŠÕ+ÖÇ`œ†Éñ³Ãø~zûÁE8½Y²¾%Ù¥=¶‹iLã*¤IÉ…ÒÀŽÂdV,'{*b Ïê¦1´bݳ70bå1ì ŒXYèºíª‹Ù¶ŒX¬©K1îb» N”}(+ÓÍVÛŸò”mrü†Ñin’=©[F¹«0=ï¾InÏ,<¶®‰|¹B VÙè6»Âle!ñnC:*ƨcÂz}5ÒoW'-h)·wuœÅýga–í+ЉxrZºÝQ ©©©o™BܼM ¬È8[QÁÛ O2, ŠeA‘ååºTtA¦Ù*ô¦˜TžpÁ9c®êuIëCzÛJzHoHoHoHo¢·-[ÒÞªé Ýs[GoÝsèžCzCz늲Eb«Œ;Ab{Ćq'HlHlHlŠ;‰×wRj„¶1îDq'w‚Ô‡Ô·A=‚ÚŒ8̗ņ5ŒÂ‡./žì&´BÕnS ´FÕU;ä7ä·.ð[Œ°÷Ýâtçb·_½J¾jõ*]_» ¦„5’­Ó˜*Uë‚ÙY l}‡­ï¶¢ŒUÑ]Œ­žîŒæ€qå²~Uî3fµ¯¸Ö–KA¬Ò.]ÖÏ,5 }šeýºmËBh\K©€÷„Æõwe«C£ ¾-TŽZ^h¬š}W·eQhÜT¡‘!2¶Ù´˜q·d\¹?(µD<# ƒÆDd¤’ø ö¡ó\+£`¶”>HJ”-,7VÉúÙÅžów ¬Ø]»‹n*®ŠkJëq¹½SÑ××+ì2;\1ß±'…åBÕXTº…F‚E¥±¨4:º°¨4öÝŒþ¢¥ª–¶R~­`Q½YdQÊEïÏ¢Ô"‹"‹"‹"‹v¥ÁèçËá0t‘A$–?ƒjåK.gŠ g­p½Ä·>‡¤Õn—Ç šûî3£,Ü«–ˆM ìÌ!¦I_… YDvf€¢˜ª›7X ë­%æÞoùµ¿Ä—ýòëFbo‰Ø;iEVGVo9Ç@ A¿)]Ù©_RÞïîCŠ]L—ÅZ—¤#•R®Ol3¬Už UVi­›a­¢Æu]7’Z޾§V}OT®×§_¯ H2­­µ‚f™Ï@ô±TP)ˆ$Ì)–ºˆÃmiÚöh]ÏhU,¶®_S›¬½At6¬ÚC¤jÃu·©VÔÖOÓà䬉çþºù¨¾#“2 I|õÃÄYë:N¾çö åíÁÉ΂4\l½XS+.|2WuaÇ“d’TB„’"«~NµOfêÊ’-|&´ÈŠ5¸RÕ2¹NG’Þ ìŽ’ž7f\JÅ(çÚØ†Ù Qœ[8 5 {´Km„[-Šm–¾´"ÛÌ®R)ŒhŸ“Œ”÷~™Ñ"«Æ-v‚}ò`…i«¬`ºa'XÀAa k¥1„òÆ`ÉbµÛ»na6öwíp×/ûï=Jh -^}éÜ ßáµ²„4vx}åX¤Âr,XŽ=qXŽ¥;•6W)ÊÒ ÿ¬`7]Ui3Ë@*°çÈnÄnCR ‘ ‘ 7ˆ 'Ñ( NáxÍu7‹ThQÏÛÆšÓØQ¹ ¹ ¹­3e7;9Áò‹ ÓsÒ"ÉékxùÖÀµÃòeÄ… P5oaÍr ˜Fo$z#lË)MCo¤lì}dè}|j³ÐùØaçãW žGð}&׿zÄê‡í†mÖø*y€V?Äê‡kÍ”8R—vµ¦ÜŠ–3&Jެ‚ØõT2±zÄUŠ_c.–Î~’Édån†÷ åžu2PBhE QM+œõFÉMuQh ÄO@Œ…Úø(¨¢´óµž¶ÒG‘½^Â)TÂÂ\¾xkþž¹5 ‹ÙÈåÅÂì…hK8ì#«ÑG> ¬ ÑÄEñ1Lƒ~ÇÏ’¸ŽÛôÃ3櫹S¬\®×dYÐÊø€Â®à¦Tæ±·¾«=Hµ ŠƒÎ(±Ð` })ÄBƒH>H> Ÿ7ï^xûÁE8½üÖ?ï¤ÆàSs´d`\ÄpжØdó.›ÿýü è1@Á¦{ z ZÈõ{ äÊÅçª —0ÎÔ"dsxE€D€ÜPÁºÊ5ë’d¤O¸åÊJÃ)sFÖ%»$Y†f—62ô8š]Ðì‚f4»tÀìœ{ù[ç:;|;I¹­Cѯ.t‰8­2U)ô\•#uÌ+¿¡ÄYÙþ‰‰‰‰óá[(ŒÓ`Ø »Ÿ°<´QÙ*­ª.žñ”µX]üþ¹kLP¬.ŽÕÅ;Y]Ü£·õÅ¿|ØÛ÷¨÷ß—Ã48; ûÿó(uÆ©ÐNTjn38Ò /1dƒ 3øÏè:‰g‘”KJ¹X®–²„k«!¦§4Xh¼­Bã”±¢£Fø™AY ”0–é,l3—ôaQ¾¸ïîç  LÄ^MdX”?Í'µ?Ü¥xWG×QÚ;÷v½w0x7¸ ¢Ap ¢ô»ßœ{_Ž›œbÌ&¶»×ñÅ(‚º3ö‚aþ9†½4º‚C?’âÓYsÁ8eçuVœDYß}È5¡ ¸‹, ¤—R@¹Kh Ë`ƒÖ¶´®³W‹hD·ÔÂ÷Dë 5pC ðñ+‘Ì2Ýezûqÿ±Í€ÀN¾K¹cÔU© V¨Z’+äjƒ@Ï…ô@¯,0$QTrä¸ à8N‘ããããÖÈqûÓòÚ8nê‡ÏŽò×$êÏòÅí«¼ýöhôànÏ0‰ßôÍ|„附 ü+N*§ß†].07ºÆ×c7k:¼Ú•£ñçx‰ñÓ»Ïîê2ê‡ãqïkx;±>fF‰ÆÑÉékN“ëgáíÖªŽ2ÍYæ¾# “%†ÿ= GŸã£^0s·1½y÷«k£ÞŒ†%³ôÁ6½‚÷ôu¼ÄX·ë7ÎþÑ«8Mã‹AW0]3a^“ãâ ßÖ“ßßÂSÈÊÏð.N¢ÇÃ`× ¯|¢f¿òD¯öÒ÷ßýmðÏ_( ÿ÷Õà×÷ïâ=ñWÿxôö|ïüÓÏDÒ“_þ6轆÷?ýL=?y÷e°ß¿~+?fûöë/ûdïý/o÷޾Á˜¿ü6¸~ý×Å—ÿ!ÿQyühïb‰sÌ©PUç(y}nÓ–¬ô™–Ri"…FŠÏPž¹Am@üýŽKp ñ0¼r +vÇ&¬„C'ô­s)|ŽGë8ü<–g/âÓ0t(R#ƒ†AÿÓpðýnx9Œ¯ç¢Mˆ>0´ Fæ¬Ã§RçD|ø¥e$| ¯‚ñœÐ’ϸ<» 'Eç¢Þžóž,#ée“0àâÒÎýy}» §††wÿó8|;L£dÑcÿR-t–ùâ—hšu=ª¿¾LxÙ“,Ëì"ûÛßFÁ6ÛÿåxF¦›òðÄr“Ì!ÕÔaÄ-€SyZ•rÇÓ¼àØ¾Õæl[/Óä2|1÷ýáå`ÊÐ¥ðŠN‚™¯§9q³ßϧ¬-ºyØuGƒàä]è¶wQC’EjÂÒ‡áÙzÍÖìÑ þãÌ¥„ iù¤Y«äÚ'”ƒL©dD»/¸Ò¾å_+ú?SFT<±_ãø¢(©Â¬ÌN ¨‘œÌ »ó%Ýx\ %ÑÞ›ë‡eN73FÌYã¦àV¢éÏÑvÞOÏþ/›¦Û…neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-flowew2.graffle0000664000175000017500000001204013553660046026550 0ustar zuulzuul00000000000000‹í]kSÛHÖþœü ½ù´[ ¢ï—l&[2“Ì„’ìLQµ%lÚÉ‘åÌTþû{Z²­«mì`C“ 6Rw«Õêó<çœ>}ôì__¯B狟ôƒ8úé vÑÇ:q7ˆ.~zòþäçmõä_Ï?û¿ýw{'¿¾tzaÐOÃ÷/^ï9O¶wvv{½ÐßÙÙ?Ùw^Ÿ8ÐÆÎÎË·Oœ'—iÚ{º³s}}íz¦”Û‰¯LÁþÎa÷ü$ývmC·›vŸÀeòÖ+ݣݠ“>üèÙ'ÿÛóÝN|ñ¼o~ò:êú_Ÿí˜£p2ˆRÿÂOž£g;£¯£*pí ã¥Ð䇼åq%/I<óåѳ~šÀ-?‡ºñU\$ñ ç¾ƒo¿$Þùyè‹g;Ã"¥ÒXh— è>U*òlgÔtÞ…Aïvÿ7è§ãK§ÉÀßáu>™KF]¸\ï2茋 ï}XÌ”èλñ×_hËAß·œ¿8‡IÎ…DœI¦8Ûr¤"®RZÂ!$&üû÷ʽ˜ÆöB¯ßl÷8ƒ¢Cµ*¯÷‹ò£'å±Ï‹§ßB¿(9ºü$4*ΖNçç÷ïºèVѱ·ïÊÝÁÒè[ñ%Þ¾ÿG_Í0Yö¼è‹×—A1MF㜠sù)g5âppí†ÁEÔh7ÛÏJ÷¼41.Ÿø^øœÂ˾ŒŠÂfÒî{©_ï A˜mc¼‘ƒåSJž2îüÁO£w¦‘8©×㥩ó/pV:9¾rUUÐpTábB$±”m’ö ”0*jW(D¼tƒ-Èð®çGÇ^Ô¯ÕÎ(¨¢F&jj,r•kÔ®˜ˆWdºåc}„á´¡®ãÜ$ kCÿÒë–çÅRÛ®¡órÚ®rMj}¨Àjù9 êShk«òÓ$=ǧ0-‚ìW§w4HN;q'öÌ)†XþGp3‰ÿuz3,= OÏÑéyÀgçÒKú~ŠœÑ$ûçw(Ö1råþyšø]ÂùéEâû‘ùr|øÌN(šÇp%scµO»þyêI‚Ÿö¼¤›ý2¤ÁÅeêGç^0=~œu¦OsÚ9'hQ_‚®Ÿ8‘Ÿ^ÇɧSS …³{»Úø>'óáúÈÔ„”ê³+q묈J™‹0—L*J Çš 2î"ª©0: bBP¨Xºdb±„kÐ[?ÆÙ÷FPœ€*¯üð‹Ÿ‚9+¬ºšWP®˜jÞç^tÂÔ¿% Õ)¡R¢…†7 °¤œÂóÿˆÒí·Òø*Á0GhKñ¤µ¸dX R\ˆWjV l½ r/N"?9òºÁ z‰ -[[-]¡9d5GÑ\†9„20³…Ì¥Yª³"ŒeE@I@ymó!pVD µ¢nD^:H¼ðôsg²šŠP¦&}8Ø}ë`D¦+Cf€üÐ3î¥]Š6ˆ4ö¿›c!Ø‚¸ W¤S8+ºÃ+àWilQÆ•H ¢ŒÜ?^ÔJEþ)5‡™ÊÆœÏa¦2¦¥äëj¦šž-‰dÔœ$cæ$ÌSă)K°²$cIæ–$²9dª²|-9o2Ç€BèJ‰'„`!5Óp}­\–ñBS-´Ì w¢\ j$‡¢R" #èiýú¡†û¢<¡›Ë™©9Œ ›ƒ%´rf·c‰ícÿ*8‹Ãî¬t!æ¦ ¡[èâwùiùqÝë&/ŠL?W,ŒÝ±Ï‚c …‹)¢"zf——H­<\¥åÌÞ P‘%˜ØLRJ4g /Ó{ñÂ|/½l›†uUm1Æ‚/ ñžy ó•ƒé5Û:[Bà…›HOè¹½3›B®cИƒeÍuó,wS‚ÖgÏJLû7Lÿþý¦åß9HO —hM0ÒD+‰¸ñA3ìJJˆ"æ.ÓJXÒ»‡¤Ç”|h¤—;êå\L©TzNG=B‚R ÍT­º|ö|¼šÎ,ëYÖ³¬7ëñ¿Ÿ>þ&óSßò1® u_(}å{ݹÝU¢uM¤z7´fö­±Þ8ŒáÐM´øé2 bª±¦& mƒsW#ÎAA0­˜Â“ˆκ@ÄJA!f“ `âr­¡ÀÒŠ0¡nG=w½Jz/­¹b:ï&Iet ¥©M½:ð/¼Î·Jñs/ìgq£ÊNÝš#uâü²„óõäcЭ‘lv«dVŽ€ Î+É õlAI^À(àÂ`` bÉ0¨õ`P©]e\a’ILašÉY­QpWFÁü 'Œ.Ñ(°Þ.ëí²z¿Õû×ûõ2]­¼F¥k|]”€´J¦³¼v/yÜG^Ëã<$ç)dV.ÙìÄf>˜ñür˜õé™ñ6‹ü`œR°Ùàê˜Zb³Äf‰m>bSË$¶š´\k°m&±Éù‰ [ƒÍl–×,¯­GxZ9±Y‹ía²›µØ,³Yf[f“K%¶F`¹I®!],85  1Q·Äv‰Ê{Il6¶Î›%¶ $6‘ÅÖaSl]ÝÕ¶ŠØ:ºhl]=.Û"-±K-”±œAÞ™œ[‡¥AðQÞ°©­Y= †­³±uÓcë½4õ“hEÑxKޱk¨bËŽ±{p‰Vd’ãz ЇˆkîÓ²i!r ’.„zxQÔ“Æñ"°Éœ£A‹QJÝŒ[7Ô®GþÍzéË?½\ÿdü69ãÒãĤu3”7³+Jas3ÚøQ'0ѳmƒ£n bõ¹püh=S­-91{íðjÒ䦤].@ÊÕä²BM%çÈàÕ"¤EÈÍLïÝX ú!¦Dºœƒ¨1*™€Ya^;_ÞÜÂuîªmÄ¢‚Î"ˆK!ÈÄkaF­Õdë3Çëùy/sÝd ¿Å²â3ÃV¾\Ô$3ÃV¾\Ô´ìÒà:- *:ô£|ec²n+ƒ@¹ƒ¯Î‹$è^ø§wݵ>fy¯Î’Ï7;´C¯ã_ùe8ŸðVåx“.еPLjlöýÈÞÌ’¬Öƒ|¨hòæš…îl(ormyÓò¦åMË›kÀ›¯£~êEß!?„¨lö¥»¾¨^ê.ëòçØì;Öõõ ]_õvQà–ÈHVŒjáUÓòæÜ.{…ͺc³îl,4¢»Z \ºT˜û i!6½åÁ]¶ QBa®ƽÁÈàƒàÎmÇ9"¸s“qŽî<ÙËšFp3¥—Á͈œ3‚›i¦‘Ô&¥ —Ñ=ª^º•\ôÈãJc£bÊ+ÃfcÁd,8PéÐ5AUvbCBÄ?ì¾u0Âwë⇮ÐQWnð›Ü*Ô| ñáJ‰E0~ ˆcꚬD`ŒúõgÙ’‹˜Ý’»1[r)bK\?ؤ÷ÏLò?ßh5ðéËÓ¬†â’Ê..ØÅ»_w¾ýºÌ&è¿É’P+ŽejaGXÛ©z(žœè£pA‚xk¬©„‰ Fx=bVá‡ì[WJ³Ž°a ÍìŽ3°.lØ=”Û=~ç¤Õù­ÎouþÒùÉ*3°JÖ$6ÅlÖû—•qf3°Ú7/[´,¸‰,HWžÕšwa¹‡bÍ;kÞYb³Ä¶&)XWœcQ]^à÷/ëÝÀñeÀqeS˜Ú°µ»[³LmÓ³,7WÙ²2˜JÚ\aWxž¦7­ÏÛ­&v«É†§èkø†ín¼ÛíÆ£«B"rQˆ\$Œ¨‘B»ÆuF%ʶãr²åPÌ\ãf§2W æk7’ù™wIc—×☚´òa¬¤¸•Âó fxÕì.(O .”jh«™V2,GÁÌîLA˜ aZ*°Ö£-V®Š{¦aåâéLÛ’6B>k¾çY”Q«NZurãÕI›¹¹ò”ø ÿãí2>Ï‘ ¬i·1훪²F)ŸwÕTh®(&ZabS>ÿÐPsjSWÚÔ•6Á¦®´)Ÿ7)ås7…¶)Ÿ$oeyÓò¦åMË›ë”ò¯Q ºåP¤\-•s‚¨bª©²øwÄ\¥À²C0©Ió£:VO²™ÏÓ|¦yÞ)in¢p¶ ]hèˆïŸÎÓðØŸ?rãŸ$ÞùyÐq œ@ë/½~ºóÑï§ÎqǼ$ˆ;z}fËji-ÆTrœá°–® Rj0d(Mܬ–Ö²$è²XJ-ÙÅR»Xz׋¥{ñUoúÎÛ¸»ªþ"$.kå ‡2*æøìqÏë@ÆWnLwUf‹©3ŒØû_œ´Vï'¯u0·Ýâ뾩UoWeóÒ‘×ëŸÄ3”/î>»«ð`ÿ î|òÇ[¤TúCÐÎ 8œp™, Á¡wá…«}û|ÅWPpˆ,õ“Šÿæû½“ø¸ã•z1NV'*ð¾ùIqåqèc;ÍßD¼yÉ·ÞU‹%]ÆÁÍâ‡ðç¬VÈqtüþlÝøø×76;i‚CoãA:yŠïFÁ•—ú TSDÌÙNtâ7A´ôÓš8”Óœe}x¯’4ylþ×ʇY7_Få„Í£¾tã´Ñ~›Ô~tQ ÄçaÅ‹j…­¸Šúst´¾^0Sùª°A?M’ž¦° }ç(ßé9¾ôKÑÁùÄKÅ›¸€¢šõz¿ô¤G„Å6FÛH8X=Eä)åÎ?ÌÈ5®•5T’Õáé7^š^ú×Îo z×0>õŠoãÔŸý–Þ%zeÛÓfJÔ™¹qƒM/à9}êÏPÖˆódÁy{ü"NÓøê—@ŠùPЏ¶wÆ^Zëáw†«"\@V~…WqüG^ý¢ÚÄk^¨´Ùm½Ð‹ÝôÏî«_Ãß?bäÿçEøÇëWñ.û÷‹¿>~y¹{ùî=âøìã¯açbþî¾{ÿ¸<{õ!Ü…ó{/ùÉ ùõóߢÝ×_î†2? ¯÷~™Þý0«n3<U¦µGRé–æóÖöƒ~¼í±®Q±3Ú®Ñð”>®¹K$çÂSO6Ûrîq•Á!$(ßoè‚™ˆGþ 7¿E:6a&µo•Sá$î­¢ù*–gâ]ä™ ƒù^÷]~»^ŽâëÊÚÕÕŠÖÔÈœuh¡uÕGà“ m"áž}ñú¥%¯q*yÖ £EçªÞnÇdpŸAÓË*eÀl¹³îû(ø<ð k|ÊÝ¿ïû/£4H¦ û‡Y•ÎA¶õdTß$ ŒD6’M½EeùµçE lÿ͇¸¤Ó<_ÍÅ5íæAêŽCïì•oÄ»n!ñº 5dé#ÿ¢‚^ãØ–f“ÙmÖÿ¨B-cðG_ÕuÏòû7Æ…>x`àö[Ñ eïT‹|{!ÈÎóÇÿ<úBdõ.neutron-12.1.1/doc/source/admin/figures/config-macvtap-compute2.png0000664000175000017500000006337413553660046025276 0ustar zuulzuul00000000000000‰PNG  IHDR­¾ [fzTXtmxGraphModelíZYs£8þ5®Ú}˜Gâã1çìVe¦R•ª=e#ŒXYÄÉþúiA $„ã82IìCK´¤þôu·Ž‘¾zøÊI}cMFž<Œü‹‘ç¹îìþ¤ä±”ŒgãRò8ÀJµà6þŸ¢ÐAitmTŒ%"ÎLá‚¥)]CF8g³Ú’%f« U‹µàvA[úwˆ¨”N=…”ÿAã0R-»ãYY2'‹»³<ÅöFž¿,~eñŠ(]Å@ýK0"g ÔȧÕÃ9M¤!•Jk\m)­:ÉiŠÙñÁ´üâž$9ô¯ëÓï I©Ø0~7òÆ h:›sx åSùÁZ<*Ë€F^Î"±J@æÂcBæ4¹aëXÄ,/MsvO¹ˆÁª×òURáIâP ºÔ럢¸ª—%$N¿„œÒ‡5©šn¶ á+e+*ø#TÁÆ8Ëprûø¾©¡VJ# e´®Cpr…•âÚæð€fo‡`ì[ü™®IÔ2u1¨üPÚxÅ‚Þf*ú  Ai¡GhmZY€i(Á¤B…‡U]!2gB°U51:ÁpbÂ0µQp”é0÷ú' †s¶Êr(8ßÁÁŸ°Ʋ~-À R®²œÝÑs–0°ãE š@¸Œ“¤!ÚiòŠm3€AíeùEõ€ÐKÅ-vMö¤x/], Ò t0ˆòÛv¾Â©oì&èÅt§€÷ÆWÆEÄB–’ä²–žÕ\rL˜èC,þ‘â£|ûW•¤Ð1­H¾Ê2©€¦Á© 35Œ ¹p±üâmNrÁ$RU¿®™äš6ITˆ96Áräõ\NÓbÔr¨Oƒ–a9/&‰œé'> )V´Áå4!"¾7õw¢6®ÑLqIŽÓqü_.ã`Qðe]Xà*¸^sæ¬*W¤"‚ÉT*ƒ~”ú¶°s/Oº NÄ™ E*ÞØÄQn±÷ЃÉË!ô€srL=“)Ñdf¢1ŽÌÛÁäYÁC¯L&õJæeÁ¤ø @äWªBÆâ"ñª4ßHÁ ΙjéÓ¬¯ºÞ^ÊÔ“¦ÊóHm/­¾‘ŽuâÝÞÎÔZZíÎ+{årcmå*âj\VÎVç²Z‚uáòtøÄpì™l>’,+^o(aÒòoBrm­¼7ËÑ'ê,ŸÉÃÐ,÷&¨g“_:›­,ßQ¿;ËÑ2F /%}=§ŠãúBq¿È>…ÀnEõßò4ÍWsÊiðûÖè¾s‰úòœ®»W( `ÚB¼J ·€²Nnu¼¾[x7 ï'f·ÐÏtþÊJEéQð«‚û1QµdoeÞæs› K_ƒ'}mLº*¥UlÙWy5š`S=Ñä-rÚ§ØùB¢lÁ«#QÜ&QÎ;ˆb3NåM´¶RTÆR´w ÞÒÎÖt»1À¾Ómç?}º]þù6ÆôÏ´vÎržÁ8^æL@`­ž‹Í ¶bUãï—1ºé»mþ¹MØ939œÿÕ¾Kù 6a§ŠŸlQðò½½ ‰Þ©kúólZØç±Ÿ|OÜkœP yR4ÅXqìϤNËko‡ ÎÇ9cí/Â7—+C’]Ùa»©æžxwŠ­Ô!øa·~€"ß1ϳÝ)²/H‹Û[î ®#’Iá"a9hÖVà›§êŒJ·U‹7PÕºØJéÕluÃÙyg.š©[—  9ÛÜ™w§–GòÀ«üÒw3{K{ðˆå îfÎìM¥6¬ZNyVÞûÀª¿8{‚®àõÑ‚×ú¢t¹cX_=÷/zn  IDATxœìÜyXTuûðG-AÄ5wqß5—´ìW™íVV¦¸•Y)𠢏 i"¸""Œ;«  ˆÊÀ°¨ îû⮹¥•ù¾fZùzÿþÌ€gs®ë¾.9gÎú}Î÷D„ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆÈ\Z‰È29%"wEä?"rLD|D¤‘ÏˈrrUD¬óyÍ›¯¹hÂsq78Ž‹ #š#¢b/"ÊãÉ*oîˆÈ³yÙ‹~"w‘Á&:†aÓ‚ˆ|—ÏkÊZÓbÍ)½˜þY úW_Ê?'ŒŸEäržeÿ‘·ÌtŽæÔ[߃Í&:FÞ¦õ›ˆÔËóš²Ô´XsÊÔ[Lÿ,ª–ˆüWâYé*"årÖ·‘#묳½¥ä›DdMž×”•¦ÅšS®Þ¦Ef6Erw_7Ÿ×´–Ü“©½Áºr"òˆD‰H¶èßÒÙ+"ËE?ùäµÍ`?EÄMôŸaü."{Däíœ× ÎyíD䜈øŠHÕ<ûÚc°¯çEd¾è?º—³Î!ŸãnÓ&Ï:ÃÆñYβxùg3ˆ 0Ø®ªˆxä\ÿ}ùAD¢E??©üšÖCédðšÂšVQÇAD¤¼ˆL‘ó9ç}\ô÷Ì] nZƸVK«¹âœsŒÁqޑᢟû"r&çÜlòÙ®(ãSÔc<ɳÐ@DT"ò½èïïeÑßÓþÂo<ÈHtò¸øü y»è¿ó_#"í –/”ü ¢ßͳà $-Ÿmˆþƒùüö·>Ͼ '½lãYÈ6ÆhZÕEÿ ç÷šRð$–—᱓äñg=)òøa/¬iuž‘È^¿ßà߆MËX×ji5Wœs6l( Ø.Ir7‚¢ŽOQñoÏBSùµë\™Ï}!*²;ò¸¨Þ+⶯Hî¢LýÃù½Á²+"RÅ`›my¶Ù#"É"òWžåÿ‘XÑÿ`¸¼Á¾öä³/Yg°ì¡ˆ4/`›'iZEÿø£å©,"rÖ/0X—*"cDÿæÃœeˆHãî_AÇýw¹yÇ¥ ¦Uœq!¹ï‘VD¶È?ÇÁ°iëZ-­æŠsÎ1y¶¹(úŸ˜ŽåYþ‚Á6EŸ¢ãßž…Xƒu:ÑÿD;_Dn,ï-DO¡²ä.ÎEÜ~·Á¶s –WɳnŠÁ:à d<þ.îsƒåäñÛbåEä°Áº7 öe8+ƒuË Öù°Í“4-‘‚ßǯ-ú·@ úÏe*¬›]Àñ ’·iÕÇû÷¢ÿɨ ¦UÔq¨s¾–4Øf€Áræe¬kµÄš+Î96”TÑß7ýØe¬û2gyqƧ¨Ç)ü3­kë ÿˆá}v¢§`'¹'†Eض¢è³ "r[r—("ò¾ä_܆ț˻,?’g_ë>0Xn8|“g›VìϘMë]ƒå ó쫳ÁºïåßåmZ"" –9JþM«8ãÐÌ`ÙiÑOÒ”“Üö£¦e¬kµ´š+î96”Ay¶ñ0Xç–³¬8ãSÔcˆÞ´®¬;("Î’û³H¢§V^¿uéQ„mÛl—‘Ïú:’ÿƒb8t7Xþ¼Áò´<û2|률 äÿòlS^ô(Côoû”Ëg›§mZc ––»òïòkZ•Dÿ ‘Eäƒ×\ÌyMqÆáƒeëòÙf¥ÁúGMËX×ji5WÜsÎûK†Ü Ö=j(ÅŸ¢C¤ð¦eø–¶aŽ‹ˆ—<Ù[ÇDÿÊðýû¡…¼®¯èßF)ú÷ë_2ØN›Ïë+¬¿i°ÜTH·|ÎáчÂÿýÛky·yÚ¦å.ù?¤ùå)\~MK$÷Ûu†o%]ÌY_œqj°L•Ï6†ŸÛþÊò.ƒå¦š@>ͳMuƒuç Ø¦]žmV¬{’¦õ•Ár7Ñ&PPþMAM«œèï_Þ àbÎú⌃áDŸÏ6Që5-c^«%Õ\qϹ¨ ¥8ãcì¦õH9Ñÿ×wùço3¦° Ñ3üœ’ÿŸfq2XÿÑP[NôïÑ?ZÞ%Ï6s Ö,7Õ›ggƒu†ßá¦,7üK åDäÁº‚šÖƒå¯,_çø6¢¿—ƒåÉ~C® ¦%’»ÉämZŇšòø-ºû¢ŸX±ýçmZƼVKª¹âžsQJqÆçi›–á³ÐZôµâ"ÿüiêƒm. ÑS*/úß2œDV‹þo¾9ˆHäþ ÂÏ`ÛyËO‰~B°ýRüÃ`݋ۘjè?MôzçjŠþÿÊLý_›èøä—a4Ňš¢ÿÏ£®"òšè›íßûZ-¥æJêœM]‹ÿö,´ýO}®¢ÿ¦çcyòŸÀ‰Ê´Â~}ÈXsDTlœ@¨¤±æˆ¨Ø8PIcÍQ±q¡’Æš#""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*;²E¤_1·­&"¿ˆH ã™ ëÀòpÌI‘X¸$Â:°DsR¤G…ÛQDö‹Èl¹-"7Eäœ×T‘5"ò—ˆÜo±‘"¹""uEÄ9g;ˆÈQi›³}aû.'"Ÿ‰ÈUùCD‚D¤Jκ7DäTÎþÒD¤±ñ.›ò`XŽ9)’aáþ)"³D¤ªˆ¸‹È1yVD>‘3"ÒJD^‘["ÒNr·ÕBD~}±='"+E$*ç…í»}Î>z‹Hk9("n"R_D~©•³¿½9Ûñ±,Çœɰp}1Šˆtýw:•Ed€ˆ\‘^"R^ô…TQrnei”³­ˆøˆÈ¶œ¯ Û÷B 68Ÿö¢ÿNltÎöår–×Ê9Vý§¾bÊëÀòpÌI‘ ÷Q1IÎ×Ù9_?+"cD䬈üWDçå#ú·nä7ÑGFÆÇ:°<sR$ÃÂ}TL’çë6"Ò4gyS9""$w᎑C"R;çuý%wá´oYmp>]DÄQô7XþLκ Oq­T0Öåᘓ"=IáNýûÍ D¤¥è?hu}áþ*" EdœˆdˆþƒÔz"²'çërÿ²ïn¢ÿNêÑ¿¾[ôß}5ý‡¶‹þaðÉ9¾¯m¬ËÃ1'Ez’µ‘xÑÿÈþ§ˆ¬}Vý[wDÿÛBé"ò@ôßu}.ú¢þâ_ö]Nôߩݑ‡".ú·DD>‘ 9ÇÝ-ú|É4X–‡cNDDDDDDDDDDDDD–ãdâÀ—N%‘˜4ôN–ÖŒérR;èAvÒÐ;ÙÉÃS³úš{ì ±,¯8æ–7æŠw*ù³•§SFܽ}6ÞLþ³—1aþº ÞLÁ/'œÞ:âîé”/"/èFTþ÷‘b”¥”†:à˜[Þ˜+^vò°¨K»\ïüï—tàön¦„óðV&.ît¹›üÙÖåÆuÀ1·¼1W¼ïµûœÞ:â÷ÿý’ ÜÎdÌ”‡·Òq:eÄÝïµû°,7%YóÒs?ûŠs*åó°[ÙÀ¯;3ççã¾²SF¤±,;%UóÒs>ûŠ“4ô¿^ßÜÒ1fÎ_7â‘4ôëÀ²SRuÀ1/=1糯8YZàÖ6¦äá/É8™8èëÀ²SRuÀ1/=1糯8YZà—¦”$KëÖSuÀ1/]1׳¯8úÂMdJIÌÛ´ÌýLÉÕǼt…Më ei€Ÿ˜R³6­RpýLÉÕǼt…Më ei€Ÿâ˜R³6­RpýLÉÕǼt…Më é w3SJbÞ¦eþëgJ®8æ¥+lZO(KëÜÜÈ”’˜µi•‚ëgJ®8æ¥+lZO(KëÜXÏ”’˜µi•‚ëgJ®8æ¥+lZOH_¸QL)‰y›–ù¯Ÿ)¹:à˜—®°i=¡,­ð£š)%1kÓ*×Ï”\pÌKWØ´ž¾pÙRó6-ó_?SruÀ1/]aÓzBYZàzSJbÖ¦U ®Ÿ)¹:à˜—®°i=¡,­p-ˆ)%1kÓ*×Ï”\pÌKWØ´ž¾pW3¥$æmZæ¿~¦äê€c^ºÂ¦õ„²´ÀÕ•&ÏÝjÌ J…³¯NQ™à§ÃÜ íØ¿+²Dî“Y›ëÀ¢ê ¤ÆüLj"}¢¡rU+*+¦¨é…ÓÛƒÊ̘— YZàŠÊ¤9±? SUéÐî¿ï¯ü©¨ýá>â÷þˆY«2pb˜Éï•Y›ëÀ¢ê $ÆüRÆ*ÍŒÆÞ„£8wä²¢ræà%ìŠ;‚°91¸”±ªLŒy™ /Ü“f^ðVhöÞÀÑK(6š½70wí6“ß+ó6-Ö%ÕAIŒùúùjìÜ|Ùû.(6;7‚Ú;²LŒy™¥u./5iÆûé°÷ì]8O±Ù{ö.ÆûéL~¯ÌÚ´XU%1æ+¦¨q,ãNì<£ØË8…SÔebÌË}á.1iœ}uØ}úwÅÇÙWgò{eÞ¦Å:°¤:(‰1W¹ªq$-[ñQ¹ªËĘ— YZà‡E&³¯'ï*>ξ:“ß+³6-ÖEÕAIŒ¹ÊUý)'•«ºLŒy™¥u.Í7iœ}uH=qGñqöÕ™ü^™µi±,ªJbÌU®jìN8¢ø¨\ÕebÌË}áú˜4ξ:¤ý¯q³s=ºV{³vü 1SÐÄ~G“çúÂñýnx®’ \ÕöøtZ<’"vb³[oØÖ†å!ú}n™ùjÔüþÁE?•«ºLŒy™ /ÜÙ&³¯1»o9ë5+ð|µ¦xsî'îÁ¤›¢r;odÜBÌÖHt¶í×ä[ˆ\犺R-F&`Mj6*Wu™ó2A_¸3Lg_ÂÓ~*r¼†6†ˆªTµƒuU;XWµÁ3•;cpøU„'D ƒíK÷‚ƒ&¢~£±ðLÑo4 ì]à½õ:;µEå®aX™³ÏÐõ‹ÐÔªÆn¾YäóÑOV¦½WæmZ¬Kªƒ’s•«±ª”"Ç÷º©ë*6°©b›*Vx¶bK Ÿ­EìbOt¶îˆ) S=s(Ö€EKõÛEÏŠFõ†`‰"TšÂªµ;Âsöã5Í+µÇÄÉE>}ÓRþ˜— YZàÜ4“ÆÙW‡ í7Šœã:£ÚkÑ|´,å¼VîDÀÖŠ[‡öU_ÂØÍ7°j ê7 ÷DýëV­qAý&0'éç½›úm¿å~CaWûKÌÔý|œ}u&¿WfmZ¬‹ªƒ’s•«Ñ~Ú"'àÓ¨þü\„¸ŠÀ.¨×p,fjôÛ®pA½Æ0+þG¬ˆ‰@÷jµÑ}Ún,ݨÃnv¨Ù/þÅ8g_Éï•Y›ëÀ¢ê $Æ\åªFĸ"'l†ÚZ7Å»_¨°Ü]…ɯ·€Mƒ/±h^"úÉÊt÷éÏì ÈNô ë€uPuPc®rU#Ô;¶Y%Ã?A»ê•!RÕ½ƒÑ®ÑúuË0´…ÊUì ǯ?EƒZÁÃC¿ÝÚñРΧ˜ë‹PïX>æ3<_« *T°EÃN£áå¾±Xç£oZÊó2áTâàø[ûGg\Lg_ü4Wg_IïÓ­£p:yP ë t§¬ÔAIŒ¹ÊUµs6(>*Wu™ó2áxü€^§“ÿþ¿lgàô8“d¼_*nºŒE›¯(:ξ:“Ý£ÿe;ãtòàß¿×ìÃ:(Ý)+uPc¾|rV|©ø¨\ÕebÌËŒìÄAë.ê†Ý{xÊ 8mü¸¯Ôbvx6|b~Plf‡gc^PœIîÏÿ²Çâ|êÐßO%ZÉ:(Ý)ku`ê1wFà”u˜®ØNY‡hïÕefÌË„ º•O%Zy:eð_|‰?ŽN1Zޤ{aò²T|zs¢/Â#ú’b2'ú"¾ ÍÆ$ÿTœÈð0Ú=y˜=e9âÖþ8•2øÎ©äÁ!¬ƒÒ›²Z¦ósI³°jZ8ü'…Áob°ââ?1«¦­Ã¥­3Ê̘—)'¾t:yPLvâ ;YZ3‰ë'ãÛ œ}uŠÊ¸%©˜Äõ“z?N&8<ÈNtçT¢C|i{[€u`yu`Ê1Ï ‡ ™«¡rU+,úv 2ÃÆ•É1'3q^’>;׳$}¶¹Ï‰JëÀò,Ÿ9Û°É,ŸÉ1§Ò“‰°,›)'+aX"6-R$NV$Â:°DlZ¤Hœ¬H„u`‰Ø´H‘8Y‘ëÀ±i‘"q²"Ö%bÓ"EâdE"¬KĦEŠÄÉŠDX–ˆM‹‰“‰°,›)'+aX"6-R$NV$Â:°DlZ¤Hœ¬H„u`‰Ø´H‘8Y‘ëÀ±i‘"q²"Ö%bÓ"EâdE"¬KĦEŠÄÉŠDX–ˆM‹‰“‰°,›)'+aX"6-R$NV$Â:°DlZ¤Hœ¬H„u`‰Ø´H‘8Y‘ëÀ±i‘"q²"Ö%bÓ"EâdE"¬KĦEŠÄÉŠDX–ˆM‹‰“‰°,›)'+aX"6-R$NV$Â:°DlZ¤Hœ¬H„u`‰Ø´è‰Œ[’ÞÒ²ŒýÎ~º¹& &W&ø¥ÝŸ¢Ê<:v‘n˜¹ÇÌJ],ÖÁÙ7íq—‚s*cuèÕõ´õû—»F>0læJ kD¡_›++¦FÝ_3#æh k„âÇ\ñ&¤/ªÚùûæWqüò8yõ/¦€½x›w^ÅTUæ=Wÿ Ì=vÆÄ:°¼:Xé¶aiз1¿gn:„³‡~Àù£W˜rzÿ%dn:„µ37Ü[饨1W<§Åé}§­ØýÛÁó¿ãøå?™'ÌáK÷á¶|×qKÒû›{ u`yu°bRDß Y;¹çÎúyœ:pkgm¼è¥¸1/&fÕì½£—þ`Š˜˜Œ+p ÈSÄì>ýÆû¥Ý7÷ëÀòê@åª~p|ç|¿ûSÄÛq+&G)nÌËg_ö»Ç3ξ:˜{ u`yu rUãxÆi¦˜Q¹ª7æe‚³¯»OÿÎ3Jœ¬òÃ:°¼:P¹ªq$-›)fØ´ÌÄÙW‡Ìì»L1£ÄÉ*?¬Ë«•«·}Ï3lZfâì«CzÖoL1£ÄÉ*?¬Ë«•«û’3Å ›–™8ûêzâSÌ(q²ÊëÀòê@åªÆî„#L1æe&ξ:l=ö_¦˜Qâd•ÖåÕÊU̸CL1æe&ξ:$þSÌ(q²ÊëÀòê@åªFzì~¦˜aÓ2g_Þ~êÄÅÍCSX½±ë®»Ÿ÷í Ò _ÄÞ4ʱþNzºØöÂt÷YÄ(q²ÊëÀòê@媆nÃާζÀIh&«—@³Þp¾¯V…ˆ=F.Í4ʱþNèbt·é÷`#aÓ2g_âöÿúÔÙ´ÅMË ¤ÚÇpO5X—7k¤\k ¹a”cý]ž·í…iÛ¸Ï"F‰“U~X–W*W5¶Eîzê$/uAór±éŸµë‚ñn5”kН§åXgíBt³yß­6â>‹6-3qöÕaÓÞ[O˜MÞhjÛ¯´n„—ý.ü½>£ lÊ? ÛVƒ0V}±F¸n%NVùaX^¨\ÕH^—ùÔÑ.™€æ6mñº}]¼:5éïå[f}ˆz^G{Ûfør¡ÉáIøâ-4³}F?6߯„Ûõ¯ߎµß|„6ÕŸ…†ðõÞ°¯ö\~†zÃ<4«ñ.¦hFt’l{a¢ö—Böû3Ö¸÷ij &c‘.ç|“bЫzSô ¾öÔ×­ÄÉ*?¬Ë«•«šµº§ÎæùÎhVõÿ0{îT´¯ù:<tÐ,DßçÚÁqÎB¼V­)†{mEÜŠMX»0›Öê°EϾ Q¹­¢Ö&cé;õQóe?lX«ƒfm*Ö{ÎËÄhlœçŒf6=0}i*6χ¶½0Ó?šo>üÔ×ͦe&ξ:Dìøù©í…&v}á–p £:5ÄK‹Ï#x¥#êµõÆíôªÙƒÖ]CÄöØ8ì]4kÜÚ¿Ýk¡b·¬ÞqÓ_«…†£÷",ïþS÷aHóÚè±ð4–NîŽê¯FbUúψHT££íK˜ÿs!ûý +f÷„U§5X™ž³¿í»0о6^ô»ôÔ×­ÄÉ*?¬Ë«•«›Wn{êlô‹¦U_†û’H8·¬ƒW&lAôôOÐÀÞ +—,Æ«Õìñ™G6F`ú;½Ð²nC4iö^jk‡Jm<¡^‡9]íи6æÝ¿*_4¬Ž—ÆÇbõ°¶¨ÑÅ ëVlÃf¿¹xÞº¦ún+d¿[6²=ª´˜…ð9û ÂÐzÕñ5O}ÝlZfâì«CxÚOOHýd5Uû#ü¦ôDÍ>apÐÍœ÷#$q3zÕl ‡ðËð›üª´tÃw›¯!<í'¬ðè›®ë°2í2æôo„ïÇcMÎ>W¯žO\3°6í*æÝ5ÞZŽÏ;5@ùg–öÂ"ÐÁö%Œ‹û±ýÞ„ê»x¶ÁdølÍ9ß„ x©zc¼·êÊS_·'«ü°,¯T®jĪRž:<¿AS›^øÎ7 «†u@­îîpïÓ-„`Ã’…x¥š=†ÍÖ`Õ°v°nô¼h«JA˜c'Ø´öÀ:• z?‡š/ûB³OõôqpºQ*-–õ³GÍnøºEm¼ä‹ªÄ.öDg뎘²0©ý&#ä«öx¶ögðóÏ9ßÅóðŠm]ôs‹êëfÓ2g_‚So>uÖFÌEc»÷àk£¡¥]+4©ó<‡_EpÂ&¼X³ „^‚qQ¥ã|x'þˆÕ6¢_ÛŠ(ßqü·ßÄÊΨSµ'>]zª ø´]u4µ «SobuÐÔ·® 뚟ÀU“s\Í:´·é‚/£¯²ßX6³Dª¢£S"|cwcü€6¨ØpÜŸþº•8Yå‡u`yu rUcƒâS'jöhØÛ¼„™ =g<ÚTm„fÕ[â‹YlX8ÿgk¡37#p@sX7s‚ß"-"çú`@“gP¾Ù4¬]šˆˆ)Q·J{ ™°¡^‹1ľ*ìû­F¤"ÔÓ‡¡UMØØ¾†™ósŽ;߬ZaŒG\!ûÕ"hD;ˆTÁóý—`¥×L{Ý•j‚ÅOÝlZfâì«Ãšm7ž:«ÖÍE£jïabÜ ¬I9„ÏÛÄ~6¼’n`fzÖhƒþÁW°:6};ÕF…ò6¨Þ¢?¦MBK[´z«¶ž‡û$4®ö ¤œ¾¹sâÔ#1}ë ªôÙ€eŽ›˜‰A]k£œÕkp Š)p¿¾ßö„U« x¯§=*IØ4€á«Ïcµ®[‰“U~X–W*W5¢–$®)°îê…àGÇ]¸Ã[Ù¡\¥.çæUÀ~C°rx{Tiä€ÚÕC%)ªõ_Ǩ©1ˆ4Âu³i™‰³¯+“,ùŽÅÓ{¢J—0ø™`ÿJœ¬òÃ:°¼:P¹ª±0® g V kë–3±ÚûgÓ2g_T‰×Ëp®a[Ty>‹M°%NVùaX^¨\ÕóÙT†‹ÀÁmaÝ|V˜`ÿlZfâì«Ã²„ke8Wá3M?Y-0Áþ•8Yå‡u`yu rU#xîÆ2œ,sh ëæÓ¡2ÁþÙ´ÌÄÙW?ÍU¦˜Qâd•ÖåÕÊU5îë™b†MËLœ}uX¼å SÌ(q²ÊëÀòê@åªÆªYQL1æe&ÎKtæÅ\‚ØËL3?ö8/Ñ=0÷ëÀòê@5Iý`…›˧G0ňj’Zqc^&Œ÷K?<+ô{øÄüÀ1³Ã³1Þ/ý°¹ÇÐX–WªÉêÃSÖ!`j8SÄNYÕdµâƼL·$½ÿxÿŒ»s"ÏaîúKÌÆ3êÆûgÜsZœÞ×Üch ¬Ë«ƒ@רþ“"îúO ÁRæ‰ã?)*Wõ½“"7æe†³oªïxÿŒ»ÓƒNÀ]}s¢.2Ä]}ÓƒN`üÒ÷Æù¦-0÷ØëÀòê Ð5Â7pRÄ]?—P,™ ß ALY2!~.¡P¹FÜ ˜¸N±c^fŒ]¤6Î/ý ³¯îž³¯Lþ·$í¾ó’™JüÎúI°,¯]#†-ŸqP5Y}OåªS`î«&Efò',Ê—ó’ôÙ¹&Š%é³Í}NTòX–iù¤ÈÙ† cù¤HŽ;•nœ¬H„u`©Ø´Hq8Y‘ëÀR±i‘âp²"Ö¥bÓ"ÅádE"¬KŦEŠÃÉŠDX–ŠM‹‡“‰°,›)Î?&+_ÎyIúlKÓâ´`ç%iºqKÒ‚Í}.%_ŽMËò°i‘âäÓ´_œ}ÓJÁ9˜1lZM‹‡M« °i™»6ÉôØ´HqØ´ ›–¹k“LM‹Çy¡®·Ù?O)Mùû³4-ÙÏ­$³P×ÛܵI¦Ç¦E¤pòLãOTæ±i)›Y6-"…cÓ"K¦E¤plZdIØ´ˆŽM‹, ›‘±i‘%aÓ"R86-²$lZD ǦE–„M‹HáØ´È’°i)›Y6-"…cÓ"K¦E¤plZdIØ´ˆŽM‹, ›‘±i‘%aÓ"R86-²$lZD ǦE–„M‹HáØ´È’°iåá®s±›ÿ—vÜE#”’™Ñ31-tÙß™=Óìç”_<ãxk~öNtZêµÑ±ž¹ÇÛÒ)µÞ•Ö;™”‡vL¯„±·Cw/ù=ãüv¿~”1rŽ\;ˆ]tX›¹àž·Öùgí˜.æwKÅzg½“‚¹ë\ì¼¾¹" G¯bJ )Y›ÿš›0öWw‹¹ÇßÒ°ÞYï¤pÞ‰NKC÷øþqøÚ0%—Ý‹ÿðNtZjîñ·4¬wÖ;)œ—ÖéÝY-]ÝË”`tgµðÖŽ½fîñ·4¬wÖ;)œg¼ãƒ}—3pàÊ.¦³ïr<ã˜{ü- ëõN ç¡qľ˙Œâ¡q„¹ÇßÒ°ÞYï¤pG친n´DNnŒJÏOÂæ‹†ËµðûÀ5ùcû¡éhoÕS÷è ÞÏÙÕÖ@ Uû`iÖãå;wC ´ž† ƒ×oï…j]°ùBÞ}¥a{âd|ôB]X—È3UÑèÌIKÆîB¯# Ia÷ú|l7â½É>Ä%õÎz'…óÐ8bשFËÝ(4µj)»·=^žµoÕ¬‰cypÚ[µÄdÃõy¢Ó|ŠzÕš¡ujè÷÷òŒNh!©Ô“3Sþ^¾5ì%Të0±çsïgç÷‹ñ~m+´wö‚úp<’÷®„Ç—Í`eS…]Çvh:Âîul5â½É>Ä%õÎz'…óÐ8bç¥mÆË¹P8·©Œ¶s#°#gÙÖ¨·Pã¹¾X~r2LE;«˜¼;¥€}$`ÕðçPwøBŽª‹jïz!é¢~ݎ̱hiÛŸZ¶½§aÓ9ýò”ÐQ­Ã8l<—{_i©_¡I•Θ{Ä`yÖ2 ÷}xîIÆöMï£^»QˆÊÙîñ×[¡]Ö¶Ý¿Ä×ï4D•råQ­Í똤C†ïâ’Çzg½“Âyh‘q)ÙˆÑ"Ú½¬:9cùdd\Ú‚€A5Q{¨/¶^JFúÉhgÕ®»óßþ¤/úÕ¯! l×: ~õ^˜, —’‘–9-íz`~¦Þ{®^[…ôKÉH í‰jœrŽg,?84~Õ_ì—µˆÍNʵ~릾¨×îkDžËûuâ—µ‡ˆ5^r÷ÃÆkàýUTj4«³w¯ø—<Ö;ëÎCãˆô‹Z£&5s,Z[·ÄÄL Ò³£_½Zè·1éµÐퟄvVÍ1iW|¾Û&­uZ EÈi-ÒOà3ûªxyu4Ò.j‘š1-ì^À‚ñؼâeØÕy $@Ú¶ÆbýÙ|ÎåÈ2Ìqë‡Þ= ê³Vx®ko|µ:[/j‘ûêµû 9Û=þ:qËÚ¡RÓÏv&g_Çç¢O:·Ùh÷‰qÉc½³ÞIá<4ŽÐ]ˆ7nΆÀµK´š³ ›?DÝúï! K¿.ußD´µj—qùl»¾ŸT…HyT¬\•*WD9Têó-¶œÇ¶Žha×óŽëáöª ž>ÑA=`Ûa ¢ÎæÞ_ê™X$f?>NêÉ,û6šT®þ±1HÚø.êµûágôë“bÞAݶ_"üŒ›ýÛÁúň{´¿ÓËñUK;¼c´ûć¸ä±ÞYï¤pG¤^ØbälÂúí`ÓqfQÏ}6‰9ë¶ï›€¶VÍà²sÓ?·;î7jÕÁG᫱ao6ì ADø{¨SµÜmÆÖ#Ñ®æ׿>%ͪÖG¿IQµÃhDž5ÜßflZÖU:~ƒ(Ãåç‚1¥S%´˜³ñßAÝ6ÃzFÿú-ËŸ‡u«/vf36ù·E¥fÃ|:g»csЧV= Ñn0Ú}âC\òXï¬wR8#¶5z¶î™€ÎU*£†u-ôÛùxùÞqhkÕŽÚl:–“phNmÄæà^¨V¯/² öõý<ô{Î ÝB”þšÛu…÷±G룱fJS<#i? grŸCÊNgt©RíGMÄÒ­«°~×2øzõF]«&žÄÄh`Óޱ!ˆßë¡*¢\«Ïzz#6.m‹rbƒÝç!jÿ2x|ÙV-‡`M¶ñîâ’Çzg½“Âyh‘r.Æø9³3^®©ó.üO<^ž¼ÇmD yÒÚg <Þ¶FÍ¡ÞÐäÚW|ìPùå‰Pë¾Ds»®ð:j°þÄ n*v#~:ïy¬ÇƘ¯ðNφ¨QY RÕÛ¼€¡+—C{.)§WbÖ`{X‹Àªqg ×-;}†àS°aiÔ8¿ú*KØuxS¶ª‘lÄ{ć¸ä±ÞYï¤pG$‹fÌ>Ä%õÎz'…óÐ8"ñl$c†ð!.y¬wÖ;)œ‡Æ gÖ1fâ’Çzg½“Âyh¡9ʘ!|ˆKëõN ç¡qÄ–ÓÁŒ‡¸ä±ÞYï¤pGl:µÆäÖ´:._VøëŒGçj­0e_÷Ÿµ®ŸÔGë£[U"×cŒð!.y¬wÖ;)œ‡Æ±§VšïbÁ³Ïu†ëÎeXüºÚ[¡œ<ƒ/¼)ºeˆÉ^ŽÈMo£Q§1nd ÔhÜot±†ˆà™Zíà”º f½û RÖ­{`t‚¿þÄ%õÎz'…óÐ8býÉ“gpÓŠè¸[ÞAÝglÐvüD¬>à…ÙN Q¹Õ'ð?€õûÆ Sµ˜´'Qé_ ³]-¼á;kwÍÄD‡Z¨Üi–@Ħ7ñÜ3VhÐÏ37yaÝþqx©N38e,Cdò'hRÕƒ‚ݱ6s&\jÀö½ñ;€ÍûhT­)B=±Z;Ÿ´«ŒÆ“fa]!Ç2å=áC\òXï¬wR8#¢²–š<ƒšVDûÀyßü6êÚu„Û^ýòðMo¡¾}Ì;¼Q{ѱZsLܽîaýâ—Xý½þu‘;¿D‡jöæ‡ðØ7PǶ=¦ìöÓï¿^¬ÓßìðCäa/¤ú`]ÖR¨ÌÆ·£ê ò‹£”µ󾬅ŸŒCHÖRDeù!8î+|³j6ü 9–)ï â’Çzg½“Âyh™µÄäqhZí}¶ù-4°ï ïÃúåa›ßBæ¯ÃçðD՚Ãe÷BÌU"QŶ ¬m«ÀÚ¶žµj„¡ ‹Ûõš<ÞGäþorb_Dù®#; y³ÚhÔ¹ z¼dƒJ/ŽÄÚ¬y˜ñ¶ Mšu¹Î­ðc™òžð!.y¬wÖ;)œ‡Æß/2y6­ˆös²éM4hÚ^‡ôËs}½ç+t¬Ö v-€ß· a÷Ž#Ö<ÚÇ1,Œ™†ÕÇ!dãë¨gÿxûF£g{ŒIŸÿ9MP¥í;˜1ß/ª¥ÍaóâWXý½<‡Õ@NÊÙçÚ˜þèï> 9–)ï â’Çzg½“Âyh~b¾É3À¾"Ú-›ƒàM}ÐÀþUÌ=¨_žëë=#СjC|½Ý!)ýѦz=¼»t2T“1idCØ´{ ÌGPÌk¨g°ð½£Ð£N8¦yÃwz}Téú1昇`Ý(|Ü©ÊwÕñùX»þ5†×~o„ž˜[à±L>Ä%õÎz'…óÔŒztÌÁÇç2%>Ä%õÎz'…››0æâʃ3tlS‚Y¾o<ãÇ\5÷ø[Ö;ënŽf´÷¢Ô‰¬96LÉe‘Îåwø1^æKÃzg½“¹ë\ì<F_÷ß3«ŽÎdJ ~;'Ã#~ôOî:;s¿¥a½³Þ© pÕË3~ôõ…ÛÇÝñß7Ë»aő錳ü°LÅ‚Tç?=ãÇÜôÐŽébîq·T¬wÖ;•½´Ž³æ&Œ¹è©õÀCãÆxñÔŒzà™0úúœxG_~Çi~¬wÖ;Q‰òÔ8ÎÎý8Î6÷9™ ëHáø“%a½)b²$¬w"…ãCL–„õN¤p|ˆÉ’°Þ‰Ž1YÖ;‘Âñ!&KÂz'R8>ÄdIXïD LJ˜, ëHáø“%a½)b²$¬w"…ãCL–„õN¤p|ˆÉ’°Þ‰Ž1YÖ;‘Âñ!&KÂz'R8>ÄdIXïD LJ˜, ëHáø“%a½)b²$¬w"…ãCL–„õN¤pybÍ(§Æq6cþxÄ öÔŒÖyjFë<âG›û|ÊB<4£tlZD ¦ ›ÃXNØ´ˆ†M‹±ä°i) ›cÉaÓ"RwÍÈÞæþœù÷Ï^ôáçÆŽ»fdos?ƒDDЧo\ü©€ˆˆ€M‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆˆƒM‹ˆÈÂ]а;•4Ô+{ëð‹YZ”æ¤% AhüX®ù Ë5_!4~Ò†˜ý¼ ËIí ÙÉÃ~ÎN¶4+©=s7‘bÑî’<äöµs~¿Müg/cä<üuþ¸žˆ+{gÝËNþìç3ÚÁ]Ì=îDDŠsA7Â.;iÈíÛg‚€Û»˜Èóe'ýõ‚n„¹ÇŸˆHQ²“?_zí ûø5LÉåÚ÷?²“‡-5÷ø)JvòÐ_î]‰~McJ0÷¯Ä ;ù³kæ""E9©ôàá/)À­íL æá/)8©ôÀÜãOD¤(YZà—Æ ÉÒ:ÀÜãOD¤(ú¦•dÔÌmQ}"6þº sðVõŽØp¶ˆû¿±Qƒ› šM{¬8’`ôs ήú½›Ù ¼T@íNobÅŽMxhÄc°iQ–Öø9Þ¨™Û¢2ú¬‹.üuç¿Ã[Õ;`Ù"îÿ’Öi¿ƒ›~Þ†ùëàô¬R_,ÀgÖ@;®)¬íûãÀãƒM‹ˆ¨ˆ²´ÀOqFÍÜ•Ñ'< ÷w8 ãóC°njw´´-‡ 5ÛÀu}8\ Ä‚ª@DP©^Ol8‹7AßæUPNžAƒ^!öp,ðSîéú£}×a× ì_ÄȪBDP±NWŠÀŸ×йfˆ”Cõv½±rgŒþa¸Ÿ>-žµÅ«Ó¼ñãùµÐMi ›¶#pæê&àì·x«z{l8µ ޹àêu1jõ2ü”½ ÑÃë¦ë×8{uî¥~‚fÏZ£ÍG¤èÖâ÷ss0°n„œˆÅŸ{G sµVðܸ?}ï˜áµQûcܹ¹ ÷3† CõÖ˜³i~Ü53:UAÇ™ø½c^ÃóA¸t" ÿ»¹ ¸‹ÛqïþF/$ž1Þ}bÓ""*"}ÓŠ1j涨„>aa¸Ÿþ)ZVïíYýòûºþhÓ¼]ŽÎNÇ[ÕÛaéõøaa TÅ?Ýпîa¶+Þ¨Þ kmÀ½ÔÑÌ®¶œÚ ßÿùÙX·5BNlÀÃËA¸x8ÜŒÁƒ ˱u|}ؼâ†Û7#qhl]4ì;7c€›ðߌɉVá\!ÇÊ÷z~\‡“«ßG7ëªøÀ7ñ>±iQ–Ö¸mÔè›V0î§õGÛæààúå÷Óú£]‹qè‡hàÌ4¼e×N­Ã‘ñu R vvÖ¨ngêvV¨T¥ìŒÀ½íýЪÙ8³œ›…u[!äxp9›ÆuÇ -ë¡c·çñé«ÕPå•©¸}#[?¨†3ýðg®s+üXy¯ã¯¬Ùð~³&ìZ¿ ß-«ð—‘ï›Qei€ÕFÍÜæ•Ð't îë>F»æàÐ%ýò\_Ÿž‚·ìÚ`Cv8.x7E½ÝðŸGû¸² '·.ƯWÕ¸·íC´6ØÎÎÐ7­cáøaQKØuˆß‡?ªqkm[Ôxe2nÿ‚}_×FÃa³ñ[Î>ïlýß-ôAV!Ç2¼†‡g=àÖ®*º™‚+—{…M‹ˆ¨ˆôM+ܨÑ7­•¸¯ûíš÷Å¡Kúå¹¾>= oUk†Õ‡CñׯðZÍÆp ž›ßχf\SÔè8§.‡ãÞ¶÷ÑÚ`8ë†u["äX0Î{5]Ï/pòBþ8ê†Y]ŸA…žcqóz8~Oy-ªµ‚×Ü>þ¼ºÙàù™ p·c=¾†0ÜXÙ5ÚöÇŽ]‹qnoNöûã¿WŒwŸØ´ˆˆŠ(Kë\1jæ6¯„>!Ëq?õC´kþ]Ô/Ïõõ%ø¾j‹òÖíu|5~í‹>*A¤êv{á»V×Cpo[_´nöx83ë¶@ÈÑ`ü/k¦¿h‹g+TFƒ/bѲ~ø?[+¼±Ô®­À±E/£KÍ rÖèøé8~.¸¾¦Àc=Î*ìûÚ"’'M :d´ûĦEDTDYZàÚZÆ aÓ""*¢,­puc†°i‘¾i-gÌ6-"¢"ÊÒ:W3„M‹ˆ¨ˆ²´ÀeÆ aÓ""*"}ÓZ˜!lZDDE”¥u~XĘ!lZDDE”¥u.ÍgÌ6-"¢"ÊÒ:½3„M‹ˆ¨ˆôMËÓ„™ï½_Gô®9&<Æ,d~ÙOyŒ nP¿Û ª9¯a­ÎÝÄ÷Å“M‹ˆ¨¨²´Àw“å‡ÀyÔpܾ0Ç]Û`zÜ·À©±ðz«¢ÜZâíOºaÔ§mñþç‘1ËðëH™˜çõÓšãõ»cäÛñZ¿npÔïõ~;OÌ@ægõðZß.øêƒ–xç‹Á8ŸüÆ}Ü_ôïˆacb»[s¼ù^;Œ˜ã„ÛÛ × ÂÑu¯â£×;`ÄÀèݲ=’2†aüÈÏqë¼éî .¸³iU–Ö8?Ëd¹ò<†.wÅÃó3pܵ5¦ÇM²ÇÀë­5¥)VNÆÃÓNð{»;Â&~Ý +ÇæyýÔæ6öGßÞŸàÆ¹iÐ i…€ô©ÈÖ“bÝ€sS ù¨!ÞïZ oë…ic^ÄÈwº`Á×ñI +žŸ†íýŸ{¼îíÖø¸C+¨÷ͲÇÀ£G$õ‡Ý‘vÂt÷çg±i•¾iÍ0Y./hƒÑêÉÀùi8îÚ§Ø IDATS7ON Çø—z jj+8¯Ÿœ‹€wº!,××]±rl¯?Ô½3·ÏOAú°VHwEæ°ÆpÝ487š⃮ 1[;83Isú!~J+¸ÄLÎOFÊ'¯›ý&¾}¹Ôû¦Ù£àÑ£=’OFÊ€¶ØpÐt÷çg°iU–Ö87ÍdùOp' ‡ç¦áÖºžøàÍö9¨>èù¢¦¶„sô$àôh¼ÓaS ¿îŽøÕ¼þàÇøèOqûÜ$¤k‰€4d~Vo¾Ù Ÿön„·G ÄÅø71ø¥æøv38¸}ŽƒÓ[Â%fpn~O4\7—"z¡ßk­1øÝ–x§S{¤q†úîH;nºû‚sÓØ´ˆˆŠêd‚Çg&g§˜$÷÷Ǹ¯ðëÓìß$ç|àSŒÿj ÉÏ™M‹ˆ¨ˆ²S†\¼Â83ÑD‡ï={":}‚ aÌLÀÙ=±6e¼IóÛѱÈNtÕÜãOD¤('{_Û9✙¦är-ó‹ßOi{™{ü‰ˆå‚n„]vÒ ëÿ=< 8íÌ”@~=0Ù‰ƒ~º agîñ'"Rœãñze' º~-ã³;wŽÄÃìo€SŒ1ó0ûüqÜ×2>ÿ3;iÐÍ3ÚÁ]Ì=îDDŠuA7¢rvÊàYÙIƒ/žLpx¥uc¼œLpxäpýdÂ_þ„EDda<5޳=4ŽxOãlsŸQ¾Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆH1Ø´ˆˆÈdÜu#*{hÇtñÔŒî;'nô×G3j˜gÜÈ>žñ#ÛelZDDdTžñ#ÛzjÆ,òÑŽ»87~̃ùI.wuî·ƒw-úíQVíð¾³,õÛÛó“\îxjF?˜Ÿ4þ¨w¢ÓRϸ‘}Üu.vî›M‹ˆˆŒaŽfô—ÞZ§«ó“\îDï_õ õTŽ_?ú¯9tu?RO% æ`Ѓe©ßÞöŒýÀG;ngÜÈ>yÁ¦EDDOÅ3ndS­s–oÊÔ»IYqìÚá§Êž éˆ=ü`ÉV·ÛÞZçks4£†ý},6-""*.øQƒ½â¿ù#öp0Ž\;hô¤œÜŒÀ´Ùw}´Î×=ãG÷gÓ""¢bÑ¿è|/ãÜV¾zÀ¤I>¹ óµ~÷I{:¿¦å®s±óŒÙgŽÆÑÉSã8ÛS3f‘Oâ¸0O͘EžšÑÓçhÜÿåó2""*£<5£ûzkïí8—‚CW÷•H\ÙÈ}ª‡sãÇ4­1{½œnÎýÀwÛ´ÛÁ»Ý‹:°ë®Æ¦#!Xp5"ö<Þµèžÿöé·½œ~÷Ñ:]÷NtZê?ª—¹ï#™˜»ÎÅÎ+~ÌïÉÙ±8xuO‰'%;ó'üµ ÉåõWBwVûÄÛ[OÇA½_õ`A’ËoÞÚ±¹>3#"¢2Æ'q\˜z_ÀýWvAÉÙw9‰Y˜6ç7oíØ«ùý¶")˜×FÇzÞZ§»;Ðaß•Ì2“ÍÇÃ0?iÂmíøsßc""*w݈ʞq#ûxÆ9NôNtŠöÑŽ;å¥uúÅ+þ›;GïZðÇÞË;PÖ²û–¥Î¼ë•à”é®QÙÜã@DD…ðŒÝß'Á)qnü˜û‹’'ý'x÷¢?6^¤ìèÎ% ý|ö\N/ÓÙõC*Vìð¸ç•à”iîñ "¢|xj?öÖ:]ŸŸ4á·Ø£Áȸ˜‚Ý?è,6;/mƒßv·ß¼œ˜{lˆˆ(‡»ÎÅÎ+aìŽùIîn>Š?lgr²ýlæÆ½ëµÑ±ž¹Ç‰ˆÈâyÆlë¥{eí®yd^Ú æŸ Û³äw¢ÓRs‘Es×¹ØÍMøæöúëq)™) ÉÿßÞ¹ÇEyyügL ƨIjÌeÛ$𤉵&&Ýí¶¶É¶v³MÙm?Ý´é-I—$È W¹#*:ð΀7ä΀ ÷ûÅ sAA ^ð†A“¸Ú4Ûg€˜Î™2†K2 Ï÷óù}ÐwÞ9—÷}ÎsÎûœóž¹PIJ­÷ Wß/†a˜iM¬ÞçtqkJ_ãûûˆ5¼bk}ûFû›_ Ã0Ì!ie¯ÄׇÞ0¾¯'ÖÈÚº/ðÓ½ü9Wß7†a˜iI¬Þ·mWGÞÓ²œÐî´†a\‡¤ñ²¸´›½WÃrB’Vfá†a\€Tã¹(®ÖÏ|ðÒb¬]Ù[ëÛîêûÆ0 3-QU{=¤Òûô¸´‹X#+ÍÕ§Òy‡¹ú¾1 ÃL[”:ùÇ{ÎæQýÅê SvÐÃtç³^T|Áþx!ŹßM÷½*ÑÞ£þôôìEÐT9t:g·Ò«ƒ0÷´éTÿñ} oÑb€žŒH"½Ýù{sÿ‘æ-õ¤¢ W{ÕœÍ'¥Îûÿh$Ã0Œ Qjåª4ㆾý+i¢¤«™ý4–÷o¢ßw/½\VHûŽúÒÓ³‘SùiÔìv§æ?BOÜ;—^ÌÍûûq}Û´ ܹ„üÊþ~|wîwhÞÒ·©ðÂÄÕæÚîrÚZÔËOY Ã0.FaðpSé|þ\z*•ö],Ÿu%Ѫ§î¤oƤ’Îzlwñ‹tï+)¾³œjúÐÓ³#¿¦Ò!Ò( Ä×ï§…¯GÑÖ·Ò¼Ÿ®¡ªnñ™®á¿èñyOÓoÿó~šû¢/]Çwå>Oó–¾EÊißÙD ùÍbºg&³î£g½×Sù…±×'­1êóØZ«ïÃ0 @¡]µB¥÷1ßNµÝ¥ "ÊzŒf/{‹ò»J©¶;¶¾z/}íw ÚÝ]Jú#ÞôÔìÇȯ±Øñ÷;ô³‡¤_í- ½5?§ïùGRœ(¡ÚîRÒ6ü‘ß³œ ‘ôÒÂyôÃÔ Òu—RuÎó4oé›”ßUBå©ÏМGFŠúL*ªzƒ–Í˜ÞØW8¦ºTtfPŒÆ‹$—¯«ïÃ0 cEÒz.‘tÞ¥4¬3ï:»ƒôÝÅã’¦Áƒžœ³˜| Hß¹‘ܺÜ+rHß]Lº#rzjöcäÛXèð»Õ¥ÿB÷?þ ©Ï“þlýæÑ»é{™™¤ë.&MôøžåsªJÒ¾Kó¾@Òñ"ªÊyŽæ-õ ¼®"*M]Fw=øò«H¡= ¨ú˜š*Ϲ.é´eÿj³¤õ>,Õx.rõ½b†a` z‡Åê}>Ú~ Ô”lUŸÙAºw‹F¯óIä¿|6=±1*«ÿxø'´­C|¦m‘ÑS³#cƒïfSÜ/ï&à6šå6‹f¹ÝA3šõ£@*»PD5†7hñüåÝ&ò~a-|}=åe=Gs—þåž/"ݹ4ÚóSzþ‘Y4cÎÃô}ŸÊì([=¬Ò\( ¬¥E¥õ¹£‘}ßÕ÷Ša†±¢0x¸IZÙ+J­ìbŒÆ‹ ´·+—´ïŒBy”¿é›4çÛ¿£È?.¤…Ø@ÕÖÏ4-^ôÔìGÉǘ÷ÅïµEÐ, ÏÛN-ÉTÐ’L;ó~B æ>C‘­ù´×ð-žÿ,)ÚÄù{¾Aßšû ¹.¡¹KÿH9çó©|ÿÚnÈ"Í»ùTe¥WŸžM¯ß6Êò;VI[âß$ì&w\ Ã0“…!ðIçÝWë×Sxr;Õ\È“ö6{ѲÙwÒ½s¾Fî;ìŽ{Ò7gÞÔ¤Pɱ4«Ò©âL.•fÿÍ{è_i[‡]ZíQä¾ÐžOJ¡]‡þ@‹ç?COÚ>ϦÔGi&@øÖ”}n'©Ã¾N³—þ’b›ÔT\B¯>5›žÜ¸mÌõ¬¢¶DRê½M*d†q1âwµ¼¯¨›7|¶÷BKç)tÅLÂý+ik{ÿñ=ÍoÓ7 =©’hýKsè¾ß¯£Ši©)îÕyä¶BF9~O‹æ?CNÚ}Þ¾~ýK^§¬s9´çøZzýG É Ü>žøõ›”Ö>κ RöQ•%Vç{εw‹af£0x¸I:ïrZ7Ñž®¬ ØÄ« †a\„Rç“”l\óùî®,b¬’Ž$Rêå×\}߆a¦%’Ö«¯¬3™vÏd9©¸Zþ©†a˜¯š½ü¹¸ZÿO«Î«‰å¼RŒ‘}Ñ"d†ùj–ß§•çÒˆå¼R×~.i½B\}ÿ†a¦ªj¯‡”zy_ŹTb9¯íCLÑšU¯¹úþ1 ÃL;T:ù{¹'b©ü\2ËI)u²›ªj¯‡\}ï†a¦1Zo¯Mu½¥g‰5²R›ÖZ”:ïzWß7†a˜i‰uïÁ3)M‘–’³Û‰5´2ÞÙH’FÖËOY Ã0.DUíõR'û“úÈz*>³5HE§·Rrc¸EÒÉ®óRw†a˜I€ø™ùG ư›…§7SÑ™-Ó^¹§””rxE©÷6©ôÞ¥ Cà=®¾O Ã0Œ…!ð¥VV«Òû˜3[7PáéÍ£R~gܨ¿ãjåwÆQ~gåžRRÖ1¥5GRRc¸%¶Ö÷SI³Ê¢ÔÊ‹$­çW߆af$Ì]¥÷îRé}Ì©GÖRv[4城s¨¼ÎXJgm;°Ú¤ÔËoî<% yîdRÖÉhRêdŸK:ÙgVýE¥÷nWêä’FÉa@†a˜)†Tã¹R¥—WIZù§J½ySßõxCÙ&U­IÒ¬²HZùÕhͪ×TzyTl­¯9§CIyªI«¬“ Ñaidî®¾Æ Ã0Ì£0x¸I5ž‹¤Ï•Ñ5²·m’j‹ÑÈL’Nö¡èìä’fU¤¤‘¹óöJ Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 Ã0 ó% idî’F¾mSm@—Jïc’´2‹RëcÚTë)¶Ö7Iªñ\éê22S ßx£»¢q[HjS×êd£É/Ñ`Yd4…¤6] J5&ù%MýÙé€@·²XËœ0Öeœo-Ë}ƒŽ/²–+|Ðñ—œpûÓ^`0Ésg¨‡¸N“†ŒàbwuXɶìµ]å&uH©%3¢Ì”Yq)kMe’:´„miät'gmé÷.@Ôã$€g&¬’Fæ®Òû\Þ¼/ÐTu2‡½[K­ï7Óɫǩõýf:ôn-UžØi‰ßbŠÕû^•´²W&,sæ–Ä7Þè˜l¼®>l*|•Ú?¸9¬N¼×GÕ‡¯RxúaÓêDãH6õ!€ïÚýÿNïB8œåÞhçÜà·®ø?zZ¿7xtü]N¢À#Öã3 ÝU7ä@8©fë¹W žE> ýŽe°£q”ß,»tÿÀÇ~f= €¿XáOø£µ|CÕqðõøOô;šg|àÇÈLë¬åïÃ$è´2×”eî\WÕk¬:Fç[ßVg^$cÕ1Ê^_eÊ/c[r­-ýÊš¶­3¿À ì€GJç[•r0ª§å²‘Ž]iqZ-—”thmoœÞO?®0·A)U1yÇ{ZºÌtüRŸÓjé2StÞ±Þ ”¦álj°£yÀç f¹õß… éiŸø9„3( …ÿØ;š‡!BC¿hÈ™ ôK!F«?ðD#\ƒáGDZþ½ ÑpŽf¸üìÓMáê!Ð!Ý5[Ë4T_ŸA8šEŽé7Ã\ç»|wZ;"Ê«ŠbkzÚÎÓéæwV{Ãy*RííÍŒ(c[r-ÍE§/@tžcÿí¹ØZߤÄúˆž¦ÐÑÿ6ŽZ-—QÂÁ5×U:ß-c.sK”lLZŸýNOã¹Ð;j>o¦ 9Ç®%7eS×0p‚ äLˆ†õWô7þuv¡?„ñ(ÄHo!:€vç-€hìØ ×.ÿ¥~Š‘Í<¯ø“õ{G3\~öéþÄ<À|ˆ‘t„“û®õÿn#ÔqðõxÀY—ìÆð¡—wZ™kÊ“ò¢w÷œ¨?C§ÎZ'¥BiÏõŒð2¶%×ÚÒmš €ç0ç Tã¹(VïÓc|¿–š/³ µ¤ÒùöJZÏ%c. sKà·Õ¸(0¥©çPç j:×3fÕú„“{}ŒŽlêCˆ¼¢Qßm÷ÙrˆI_[˜F Îîó¹#½'0ÐÑÄA8¬¿ÚÉ 1ò¬á Î8šÛì ÀýŽf¸üìÓ]àϾáp~à € ô;¿áê8øz¼lÍ7 #‡i\Úi¥„,ÊŒ(ï9ªë cu§Ç¬MeF”õ¦–±-¹Æ–î pâIk쨴Þ{ [“,ïï£ñª¸5ÅWë·k\b¦<«“{Ó5ïZ6Ñx•©í¶§49²©Á!{–£Ôë!FŽ6h`` £ñƒyÚ¸Às#îdˆ†iã9^pÎÑ"ìr@úÍpùÙ§{;€c’ æ5p@Dg¤:¾/8 :*·j¨•r.í´Ôáå{w§°Ñ¢ñjoÆAËŽˆ ¶¥¯Þ–æho=o|H/KýÅÝdxO3ní¿PI’ÖË¢0ÚÇ*£ b«ƒWÔTØ ëìAÿ$¥#Ü Îuˆ`c¼KAÅÕ«o¦~ ‹îÄ'T×~cܪ9vü –ÀÃàø÷hmÂbÄW1¹<Í£“Õ¿´žaGw@¬$û+Ä(ñG FËó!&É¿>¨ ƒ D¢Éïí#ä78]ÛHú5kY/è…ÿŒTGGŽÆfs‹!Â5ÿ<ĵti§•Rb1VµRÓžãã–¡â(¥‡”Xrw³-}µ¶ô[ž´ž³Àc‹–jKúîâ ÓŽfÉ"iVEÊ*¢³°u"ÙK2‘;­9#½ç!b´× ^œ[ jÏ2ˆåšQqVD‡z&÷’Ñ)ƒ_‚qã¶ÊsTu䯦Íåg,¾ñ ƒmj¢x ÂÑÜù%¥ÏŒup鯒ÍZÒç'LÅ›j,é¡%lKS•íª·RŒkÍ5òh¢”b\kô¤ˆøå ëß9+¬ŸÔi½±¤òNô¿ô÷ ëgÎ,µgDÇb›{ýºSaÉè¤Ç?ÁøVt^›¹´éM”¢óÚÌ_Ò“Öw!^îü²œ3ÔÁ¥oåEW›k²ÒD)/ºÚü%=i±-}H™û–ý7vweÓDik}Ðuû΄ˆ±úBÜÜ b²ÀÈVÄÓL¯UñÔ2Î-µgÆc]¢:–ŒN |ãîaé-7 >¦‰RDÆÑë£Ü—¹È.vßY~cWzM”²ÖV\径ÌdBaðpSj½ÍågÓ¨ê¼zÜ*;L’VÖ§0x8Šé¾ à8Ĥ\ºÝñá:­yÄK²ê%ˆù¨pn)¨=ƒcÎÎ.Quõ’Ñ)ƒG®Á- ¡ÁœU÷'Ê9øÑ¸•¹ï ù'6ôyäxž`š‘«ÈuË+1—m×SyâøU¯%uXI_®"—mi*£ÔÊ´êæ(Kù¹d¯Ò¯³¨jååCdõÄ“Ë ß³;në´ž˜«²i„C¿Œ+Þ1õ*œ[ jÏpÖd^2:¥XdÔJÅg,™ûÿLãULA‡%0É8”M9Úܶæ2ÄÜa9ÚÇhv›j‡óo8dM£€í…ÕVC¸â ߯LˆèÀ³NÔËÍZ ¦P§•^¶DRr3WYEù±»Æ¬ƒ˜Wœ‹Ñí6í(}oˆ§ð|ˆ¨_œ_‡þhAÄHx4å·'b‘o@Œ¾Xÿÿ{ÿ‘ëõ4D¸ç;øâ®2“ž\E®›:¤´RVÒ§^[BëJ×Ú2J+5«C† Ú˜ª¶äl9n=[R<Ü¢5²X¥ÎÛœz4‚vv(FTrK8)uòž­WæÈ90Ó \ƒ›_¡؀ÄFsTþYŠ«º<¢¢òOS@RcO@Bƒ36åȬ€˜Oü!Ä—ÝÞ~¤èìnÓŽÒß`M/"<œ *™Ñ?x9ünˆ÷ùŽX?s6}{6@¼LoãkÖ2ÝZ‡"ô;¡ê5 @#Ä\í#pµ£#¹Š\·Ôà¢Ø´sZX¥†ލ´°"J-îI)¶4R9n][RhW­´²n•^fŽ7®¦ÔwÂ(ãd$ežZK'#)µ5œ¶-*½Ì,ie—mÙÄ0_À'þà ÿĆFsxÖIZ_pž6–\¤èò÷icÉEZ_ØEáYm–€¤F³bÃ%[6 …#GpÄöuˆØ|¼õïÖÏG³Û´£ô#0p¾ô~ˆ0ÉsUvç.€pz"}{‚àxt|¿5íBôÏ…U/•ážÉãhÆHZpéŠô’î´àbsJp!%çSRP.%åRRP.%åSJP%-¸ØœR|ÉÁ–MC1•mÉ™rÜÚ¶¤0x¸I5ž+•:y–R'ë’´2“¤ñ²HZ™I©—u)uò$î¬˜Ñ žº®ôOhÈòOhìòKh0ù&,~ &ÿ„Æ.ÿƤQtV69‚9¸áè ˆ‰d7Œ~·iGéÿ_§°9“Å«Jwۻ xp¸-IDAT"©Óú)Äœ‡m‡…Ð?ñs¿³®^ FóŸø_ô‡š¾‡)J®"×MZ²RR’•\Ü•RbJ.±ˆ¿Å]iÁ¥I£è¬lLU[r¶lK 3 pälKqWBŒJ¢—‚áv›¾À¯!^Í.ý…»ªxBŒHSпÝ×rÇòïÖóÔèÏÙôíÏ»bâÿ5ˆ9…ýÖüf@„’l¯X W¯û!FÕ߀x!ÞŠ*Ì4]™ª¶äl9Ø–fàÈÌ€˜#è_Ä¢$;ÜnÓs!B?ö¿"0Ô“Ð Þw!ôOžÏ€˜Ðþoëg 8F‘þàó¾ñŠÄçïÖÌÉT …—³»hO®ÎäbªÚÒhÊÁ¶Ä0 Ã0 Ã0 3-ø]G˹ïgoIEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-compconn2.svg0000664000175000017500000013566613553660046026510 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-09-27 19:22:48 +0000Canvas 1Layer 1Compute NodeOpen vSwitch - Provider NetworksComponents and ConnectivityProvider network 1VLAN 101Instance 1DHCP Namespace 1qdhcpMetadataProcessvethInstance 2DHCP Namespace 2qdhcpMetadataProcessvethVLAN 101VLAN 102Provider network 2VLAN 102Provider networkAggregatePhysical Network InfrastructureInternetLinux BridgeqbriptablesLinux BridgeqbriptablesOVS Provider Bridgebr-provider OVS Integration Bridgebr-intPortqvbPortqvbPortqvoPatchint-br-providerPatchphy-br-providerPortqvoInterface 2PortInterface 2tapeth0taptapeth0tapPorttapPorttapPortPortvethvethInternal VLANsVLAN 101VLAN 102 neutron-12.1.1/doc/source/admin/figures/port-chain-architecture-diagram.png0000664000175000017500000003716013553660046026764 0ustar zuulzuul00000000000000‰PNG  IHDR®=Üe sRGB®ÎégAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ< pHYsttÞfx=ÙIDATx^í-ìGÖõ÷e C"-4\``h³°µd¶YÉ ,( 4±=(ÀÀd¥ÈÈð!+®Œ¼È††yOrö9{÷VuUMOOOwÏi)ÎügêóTÕ¯ï­îÿ÷áÇ¿ýíoÿú׿þàË X+p{ |þùçß}÷Ý~øá‡Û«»kl¬€ø¿¡ð·ÿþð‡Gý¯/+`¬À)ðí·ß€ÿA!>ýêË X+pc üôÓOFáµ¹«k¬@¡€QèNa¬€øÕ(t'°VÀ …îVÀ X_ÂZ'øÇÿ]ïÞ½s'±Vàr>|øÅ_ß»w› ÆSpH+`ö¨À Ò¡„Pˆï—%´¾š`=ꂬQÔôÙ³g?&MÃõ›Ã9Z58 …%¡ƒB˜¢U‚ä㚭⼬€XYQ0‘…ä&O¡PÓm©>?~ÄOéË7oÞðKþJ$áþÄ—ø€üâ‚_ü’â¥èL¼Ì´ ?eÞ¢0(R ßÎ:¸Ze¤¦Z+eµœ£,ÕX¹£8;+plN@!†4=åè-–(|ùò%Jýûüùs‰ß?%0‰5üUýn’ó•ü^Î8g0yá{ü©\X*P[±X”mª9!Zäoû’ã\f]-0RF””f4B‘;µ•hñ–£êh³Kö^ ü»°ÿ¥À (D¼Ò[L(TBr„‹AmV­BÑq\LßÀ€â2KtcY*-zàO(»(¹É¢Æä ƒ±$eÖD!ÓQÉë˜nÛj͘D‚H6à±:H³¤ª;µ°g*p ‘×d³$Ò¾‹þǹæÚÚ(debx‘%²‰Ik5L4A±#šxDXÛž’¹'’¢¦±:d}‚³–ñKÆ3@ü†&-ëKI£íŒ/€YW«sfÃ;º°Q“Q(‹Œ;¢pjå6­¹y(L&UÄD¬ŒÊRE—y*ëjo•ÈDÙwâë’lFV\Ù•6  éÏ2 å&GbèâÏxn—ã\ì M„AN×O»m ñ§6WÉ"›”dä©ve'Ë aªiÔ—iâ_¤Æ]ÙÀÍÀh–j㢠ÌZt›B!ë^κˆ†ä²25 /= œ¾8 …/qÄ¡©H@”6á(‹¦¹³´›ºJÐ!®`r~gX…ì q3 ™lÀ¸Z5›·§P(¹’ˆïã¾t¦‰êÈ 7 =P­À¥BayDLÅâù³tB_b´ƒ‰°nªÓjø’«üµLi*.>OÍÍÁ"c:ÕçD¤RM€K«ð,ÿÔsº¸m{*ë©S®©FUuÊ0¥È—îNß Ü”C(¼)E\Y+`nP£ðÝU¶V +`ºOX+`ü@÷+`¬€ßmâ>`¬€€vÝ ¬€°FaÑ>|øðèÑ£û¾¬ÀÑxõê•(læÎðâÅ n®öeŽ­Po…“}€7‡`ó/+pHž>}ŠNnF Ø*¬o/‚ì¦8ª|–ŠQh¶z8oFáQ)àzA£°ì¶ m7§€Qhö;½­Â¾F±sŒB£°ß…÷ˆÂêËôàŸ~ çîàùµx¼¯ô²@úe(ÕÈ›¦§Jt~ 1eêVÍ %j¼bAQP>Áh! WJÆ(4 û]m(ä#«×ù/Mæ3º»Â||B%ר'9Ƈ3vŸžÛÍåü”Ÿ‹« 2> ³zÅçøòQ’é…ÝZ\=€Qhö;áNQÈç~ÇKƒ2|úZü"=X·‹ãÅ3wÓ;£ D=×ö|-hÒtzÎ.QŽ'Uù\a½ÀÇ(ìEÛæe“ÜFûEaÙÛôþ™s:b…|ßKÕ2Šï‰EÎGá9‰qùrX²¸ð¡|ô/QX}În|ƒQ¸T‹\=£ðÈ(DÝ8nUÉ4W¾[ ö†7þ @.¹ºø0åk“%M˜)bÉ, ‘¯¦ã»«~ŇXHþ¤‡„£œH•âk!Xȩǖ¦‹t¦^ÓØ@aüÉ(¼:–*€Qxd¦· bÜ’Œ §ö€ƒ89Œ ¦×ÎÐè›Ba¢m£k…ÊEœ³u AŒr²%L™‹f'ãåÈÌ],3‹‘j«p)Äì%£ðÈ(L¯ä˜N4ÂyùoÛA>É," cDgÙƒi#Ñ<¹Ø%Í«oRMmL^µ®¾Kv …Õ—ŽÀwSDð²IÙFáAPHK*^Ä„F){¿^Ö¬j'û¨tuGa*CLÎr¹È”(Œî?*Åj¶WŠhlF3°4luW(%M(÷ ò¦(?»0FáqP½E:ŒÑñœZ²ˆ ©Roq¦YÅjú| "ÊÌ×±Šw% “EÖ}Q*ÍLÄŠKÃ_,¿)%M›%O2ŠgÒÅ#Ú*´UØïTGZA޵½ 5a7¥lÜP]-CB!ÂО%‰øë‚(dª—¶ÈpZ`j9ÖÔ(쨄°Ux«0ù‰e÷» I©W?Óå¯]rk ¢ÀlQ‚mùT«`-O’ð{yÍFáN¶X1Â[A!× ºs…%RÛ2äcÊÕõå´*ÒEa9g—¬Î3d:†Õ¢’ÂÒÇ(\Œ1;IÈ(¼Š)q¹ÿ3P(w2!†Œ+Â3PÈÛ¥d®/W·@¦¥j£p'[¬˜Fá ¡ë³Ä È]{iõ³j*Xã838¢u-¹’ƒ1V…:ÀG–Îõ‚s…U«S=€5åŽn£p1Æì$!£ð(ÄŽSþ¾ƒ(ò%m:‰Êt0gÇ(Ý,xDðâ”_,L:+ŸR¦ñ±<:‚#Y&U=m³@iÓÒ¹~e:}6 @™Û˧&@•&³;ÿ™+ãÂ+È¥àFáP¸ò@rv{WÀ(4 û}x›iúµr+0 Âþ€0 û9ÄÎ0 Â~6 û9ÄÎ0 Â~6 û9ÄÎ0 Â~6 û9ÄÎ0 Â~6 û9ÄÎ0 Â~6 û9ÄÎ0 Â~& ïܹsß—8¨wïÞE'GåúãáfBx‹unêW¯^M=ÄÉß[#)ðÕW_Ý èú5 +½~ý„/+pl>|øÐ'ÄÍ„0 o¦©]Q+`¦0 Ý;¬€°¿…îVÀ X£Ð}À X+ð«QèN`¬€0 ݬ€°PÀs…îVÀ X£Ð}À X+`«Ð}À X+`Ù}À X+ð›ž+ÌýàÓ§Oß|óÍAá»ZVàß |ùå—Õ÷Aß,ÂÜô/^¼8Ò‘{×Å L)à'ÓÄÁofR‘û(¾kwË <}úÔéJ#ß(¬£ðÑ£G7ë)¸â‡WÀn-›Ø(4 ?ð]Á¬€QhöG…èß×È!v®€Qhö»°QØ×È!v®€Qhö»ð (|óæÍ?jW¿pK‡@I¾ÿ¿ëùóçeò(æÇÏÉ) —Ù)Tµ:'Á“JÂÜOв‹ÀF¡QØï¨+ ðÞ½{Õý _|ñÅ“'OúEì… ÜÚ¡0È>|Èb _\ü àËnRíŒ&êÛ+òäïSZáû—/_žš,¢<~üx<rAùÇÃï%¤Qhöûêj(õd‘ñyt& Gø…0Ä2¥…õîÝ;@ G’ê àžDŸ” Q˜´B‚©¨Ýb0À©\6 …=@0¯ çF\ …¥ç‘Pçt¬~‘#åa!’IꜢŽÄ% K­TTˆ6’ŽQU²Uh«°?j®ˆB®ùêÏž=£Ù˜¦óСñ%æòð=EHP`l1þTVa`ÊRÃ÷(MňB•¡h,!2Mð â²*0¢È"n³l …H )”‰³Ÿ Ä™µláXHþÄKEeeRá²v±^Õ@Ñ‘~YßF÷ûë¬F¡QØï8[@¡–è´Ò­£)çÝÄMù€šõÇê”.I¢Þ¨®4{˜JXúøÑ'eë˜>7öK›Œh•3ž’ Ÿ9¥¨ùÀŸ‚]E´b:T@iBä(K˜2U hʨ?ÇYN ÈŸR÷ûë¬F¡QØï8WD!;(ƃl  +t¢!É‚ ß[`]¯6ᣡ“Š–Wò éÑÇ–>~‰B˜ù¦•…G!˜’ŒD’È ƒ¼ˆÎh Ò¦Öt­0§› £ˆ¤ÝxsRt ¨$(e†KDÛ~¯=1„Qhö»Ìj(LK2èäó›äOqìÑŒªzˆ‹£Ph¦v„Ç6ŠQ®äÆS¢0ùæm4·Q—5 F„²Œ¾Xþ„B(\B'†aîi^"x$„3 ¯Œ­©®É//ºaÈ(4 7„B\º8£ë”Æ-‹IG¦º8 ¹˜iÕõÆèÅã(Q˜ Ü^¥GajZ@¾§~ªJª_œJ(Lw‚t3(3M)k¨t+E#Uñ}¼h)ËðìwÙÓC…Fa¿×¬fb ÆP–l…i€uQ8¾A¤šTB!ŒVM&bk«ÂTàsP˜,J‚Xeà‡†UÍÑ ,.–?úÔÕ²%Ú) mýaúøS–>³«^Fa¬.›i²œ«¡°jXÅÒ\…´SªgK„†Æ´cÖ&­. ·­Â¥PˆÜ#¶8WÑà¡sòÕI,K’F“w¦äÑwQØMA ŠôÅDƒ4dy’UÈ?Ï<äÓ¦„­B[…ýûȦPXzgð4ª¾j×*ä0˜š˜§{˹ȮUÈÀéÔé°‚U˜–,hÙ%‚4¬ÂDRö -ñÏ* 5‡;’tH³~qá%™¢ ÝÛd¿7C…Fa¿ m…åê$§æÔ硉aåÖBÙ8”©‹B’".ì`Óé»4 Yw-ÅÒÂJwŽ´"Ÿ,>Ö®:ªd©vôU ¹ðF2’BY*Þ̸lÍZ$_8M¶ö»ìé!ŒB£°ßk¶ƒBa…¾'—k1r´Õ£BÎÓO9YøžI1/m‘ÓE! €¡KŸN³f‹£e‹—æõ¢õD‚@e#†"ù'~¢ÙË?5i+6¡´bEuSÐŽwäSPG¤ìÈšÅæÝÈ›iúuéž+ÌŠ®€Â´×¬Ñ¦|hG>'¢ÁÑ^:SZ:hl]F¦q:Ÿ‰Ç}Z=)G21Ó´&@d €xÏØÕ³´S"¨.â>H?ZÑJ¥V\Žçz®tàînÝN j‹¼$8ËÃÜ™ˆ’ªvS@"Ê”"3Õ6ujâ3¡ ÂV¡­Â~?Y…ýBüwŒš 'E<œ«§`”x <£xçd׈K­Ú›òÒ¯T +W#Ù‘(Q#—554 ÂþÜ û…v+pŠF¡QØï/Fa_#‡Ø¹F¡QØïÂFa_#‡Ø¹F¡QØïÂFa_#‡Ø¹F¡QØïÂFa_#‡Ø¹F¡QØïÂFa_#‡Ø¹F¡QØïÂFa_#‡Ø¹F¡QØïÂDá;wîû²UàîÝ»èä¨\<ÜLŸ6ÉMýêÕ+ž+ðeŽ­ÀƒntýŠ…~ùåx¾¬À±xÿþ}Ÿ7Â(¼™¦vE­€˜VÀ(tï°VÀ üjºX+`ŒB÷+`¬À¯F¡;°VÀ(t°VÀ @ϺX+`ŒB÷+`¬€­B÷+`¬€d÷+`¬Ào x®0÷ƒOŸ>ýøãßù²GWàŸÿü§)(ŒÂÜ^¼xqìCø® ~CüFaF!ÁSŒŽn¸~·«À£Gü®4òÂ: ÑWì;X£*àG·–-k…Gï®×¤F¡QØ~ _#‡Ø¹F¡QØïÂFa_#‡Ø¹F¡QØïÂ;Bá›7oþQ\ø²_ÉßC êxàÁ4ÛÁTØ©` °r©©Ú¾1 Â~Ý ïÝ»WÝ‚ï_¾|Ù­ê_|Ý` @Ž(0þ}÷î]™,Èê¬\ª+¸—¤ŒB£°ßWw‡Â'Ož|®Ç“8ÏŸ?o×öáÇÜWd¹,®gÏž•©¢"Fárb·R2 Â~OÛ áQ¦Zá@gÊøêKp±´C§¬Q~o«ðbòÿ'a£Ð(ìw³ •„™¦ÈèC×Ç7?~„©ˆ4ñÖ-gèð+¾—dðjùRÐOH_òýpJ¶Ôš°£Ýš|dzÇ,vrñS4{cDÕ _*Lé}Ç’³œ©l1>³:ø7c^¼R¬²ÊPuÿû=oÅF¡QØïnÇ@!êIÃéò{Ygºø 9ª“¾=™ ¯,Ø¡$¯\àêÒsä8L>2¼cüÊt" ‘& Ls’Ÿ5Êz1® †v*9S`"ˆ¢Ê–èªGsâÄb ‘8ÿÀâiê¶êþ÷{ÞŠ!ŒB£°ßÝŒB#œ( ñ™˜ˆê $“)à püUô$ÂHC|žš¦TŽ1kfŠo7¢éDÛÁPò€(Œ`eÁSÊRã7ª¬þø߈B~#QH=W÷$‚/“9Ùïs«‡0 Â~§; #Ý’¿,ú„NDXŒN$¿’_ŽDX‚i©µ˜|dŽLü›PˆbGŠ1Á‘z©$H³\OFÑ™ØMp1™ÉÖíûÅj£Ð(¼i¦ŸL3ü)+3wò"9ÔÓFB„¶’ä¶¾Ê1ùÈ$£Ò™ÚLC{3ÚtD|ªWiáªT¨²Ž¤«f²D!« )RõcÄX¤~Û@£Ð(ìwÃÃX…%2ÒZsBa´Ôh7ɦX½" ãKUè˜cúLì–s…$ëBó[¯D7k¬ZLA¤«¢¤ \òÁËêËöLJö;ÙµC…Fa¿…4ëd^E3G¤-µÒ`DRåÉ|Ãy1"ì$r­œb¾ÑÑNn;‚!0g9«rB|¤g9q†(Br·BZ‚…0<«Õ/§ú=l!ŒB£°ß BNäi)s…†ä8I+¤²€¤ (Í@!aÊ;.­BR,­BœdÒNgo( gñ%³‘z À1¬âUë­B~Ž +=e¢ ÆS7ø>`9‘KS«vûãjó!ü¼ÂÜD»C!w´éÒŒXtQÈÕÒ„ Äí#Üï‚‹vSÚLs’ƒŒ4uÒNæUš+$mµdARŸd²âH‡Å†‰G8 …2 µE1кÉ\ºA€¸™ÆVáæY×) Q¸cj=A€ y‚ñœœJB¤\6IÛª¡Ó¬žÇàO\=ˆ¦½E|3‚˜£¶+ª ˜N\ȹh-ÂÕ˜TÂj½’U7~³Ì%ú¼²ø\Þ9˜©êÎi;–yD°ƒl¹ßQwdö+s4¯.ðd’°UãÄܼ¬O*6QX˜áSÑv´pJ+£Ð(ì#£°¯ÑBÐLë*‰Ç¨ºQhö{²QØ×è@!tðŽó€Ú€Ýuöw­Qhö;°QØ×èX!`ÆI‰ÛžÂ™-`…ý.dö5:bˆEf$÷"ŒQhöûªQØ×È!v®€Qhö»°QØ×È!v®€Qhö»°QØ×È!v®€Qhö»0Qx÷îÝï|Yƒ*ðèÑ#tòû÷ï÷ÇÃÍ„ði“ÜÔ¯^½â¡_VàØ !n&„Qx3MíŠZ+0­€QèÞa¬€øÕ(t'°VÀ …îVÀ X_Bw+`¬€Qè>`¬€€ž+t7°VÀ …îVÀ X[…Õ>ðúõkÒôeŽ­À‡Ì@)`9w¼;öi3×N |õÕWF¡Q8Ùxs¸sç«û²‡TOñãlf&ú!]¶¯€ÒU6±Qh~ໂY£Ð(ì […}bç …Fa¿ _…|sùÊï\ï‹âÇRÀ(4 û=ú*(ÄëÖ?~Œ×NjuŸñM¿¸‡ÊV¯‘—㶸#!/!ß(«šøT½>|øæÍEa"kVÁ(4 ûÃa}r0‚`_LŽ÷ðb´à¼Ÿ·_âý‡`õQÙxéËvý®‹BÂ…GÕå¬Ö uDxü$‚øÆ(¼nGö²É•—M`r8áFŠ2’×í"+ä>eXQÜV(ÃŒ,h[Ѧ«Þ´¦êõäÉ“X/£p†ø‹G1 ¯ŒBŽö)‹ :–`Mp>¥ÿøñ£& zaå7ˆË¤˜H™f5æ ÔSJ‡ù*L*gÕzšò1iCé>¡ãŒ*>£î©:1—²œSs²$*35ê8§ÁÉXH…ïúÎ i.ε …WFac´pl§!-÷ŠNì àˆ‚sÍ0¼hTÆo¢‘…ЕcštÒã71¤†4 VZm4‘¥‘B½! ƒ,T&ÎÙÞBPTÝK¨@òUi»)…—/_²`ú7†§ ´Úº©ŒAfQÚïFá $]+ŠQxM‚tBƒ‚r¥9t7yІ¾ 0>ÅM~ƒ‹ßhð ‘d ¾ãø ÁAÛ‡J1 ~Å7¸˜i¬BDŒÒD˜„õ¨{$ÒWâÈÀ¢í&R¢äPËvCŽdRC½ø'«Ã?ã=‰# Ü$¦F&­Õ*LÕ‹qU)[…×Â_Ì×(¼& Ó8owˆªFûˆ”ሊ HCa"8"×”5s)MúÑL0¹ó²F™i§>Á‹2Ö ™`BaL<ÕˆkJ\ÑiôE‡žuL_ ïN&¥`II0rÆùAö²økvÕ^!€Q¸VM ÍÜ …ÑI˜ #’½ÆaIðñŠ• )çþ­ŒÊb$ÑVa‰ÂvℵÌÞø'mÆÒ'ýy;! ,KNs>Þuª‰ó®P^ø>zåFá ¤ëfa^…Sƒ³ÚlÕa\ΔEŠ 0‘¥Âd ©œ … 7ç °t»‰ÇÂD#‘Iq!]¢ÿTS‹p2‘ébâÑbe.TnT±Ê›MwÄ.Àû KÂk¢&O‚Q,‡¿¹MÒ¬ÓŠD)BÉYMòN£¸ 8F6é3× QHs¯ŠÔh*¶·jw/Bºn"F¡QØí$ÿ~®÷£GúA—AךּAW ¤C:è ¯`vä®á–”k ƒ“¡2µFPHïöç(·ôjËAa¢sL„‘>FáCd¥4l^Ù*Ôºpé7¥ Æ ,w\K)}ÛÅäê¿Lû]¢¬³d¦œ–§G8+§5YÜ岉,Áq«0.U¥Þ“v‰…+al‰lŒÂ+£ÙsÀsR‰‹Œ0d8¨¢‰£†CKfNõÐÂE­B‚ƒ6…ãžD\D ­×Z•ÆTò[™R‹‹ƒ‰SR\i±‹¶d«4L’ºV¡n]ÕÑGÃSü5 —`ÔJi…×G!JÀ }i©1ÚÁ8Æ8ó¥ ¬`"/ Vbðgäï ­ SÝ Ô.9ド X¥/•Xli·FwQØ]í¥8D­Q¸Æ–ÈÆ(Ü Y…–ŒšÆ~p“ÁR˜xðNµBš²×”‹"–ÇËÊDøMJCe(N#pYø2…¨;mátU»÷xâ“sà#5,uîž·ëàqCÊÅuÇiUán¬sxÙ¤TÏ(Ü ÏéÜŽkÆ0 Â~oYÿ!]ý29„XT£Ð(ìw(£°¯‘Cì\£Ð(ìwa£°¯‘Cì\£Ð(ìwa£°¯‘Cì\£Ð(ìwa£°¯‘Cì\£Ð(ìwa¢ðÁƒè.¾¬À!xúô):ùýû÷ûãáfBx3Mnj*âË ^£0~£0£ðýû÷_}õz‰/+pl~þùç›±ùú5 û9„°‡WÀ(<|»‚VÀ ô0 û9„°‡WÀ(<|»‚VÀ ô0 û9„°‡WÀ(<|»‚VÀ ô0 û9„°‡W`& ±ùîË/¿<ö®«Ô:CíjGüúë¯7RHƒ ¼zõªÚRf–è* àØØ/¿ü2Âñ™(üᇿ;D#•m‰ÞN ]*0u~ãóÏ?·D×R`ðí•3QøÝwß¡bÈã'4·S)ÜÓ s…,žøQlŸê% _¼x±HõÙ:¸þþ÷¿/’àQùöÛo‰© Z…D!þÉÃaf+€Vl£pÙc¤`ßR¤ â-q³Ë?cïÌdù¶&½9„ï„Z¼œã ¶ŸõB¾}ûv<ÁjH½£Š ÄZ7^ƒ3;»ô¦Àé `zó_j¬©Í‹rÒS¦Î² Ây-4kMò5˜`ßAб—‹òåpSo¬¯H É7dž9ØÒëýÊ7áÍ+ÛìX+ w) o§B¡Qô†Ø‘—IW-Rl7îãå¢(du CµQ/Ô++:ª2ãO©ÁôK«°*×H7P™ScñOô:ÂQ]Hw\u!ä‚ÀhÇÁº§`Fá<ݶk²ç%«* •<5R Ù}Ñ¡æ:I  Á­‚/ÙAD6™Û@!GiZê¡¡ÄÁ¦¡¸`“o…t¥Ól†Ì:Ù}"ªMaÕÒ\a)×`7˜B!o{Q|ª§ûœh~f…g ¸¡è[C!]érA9öã4å{?Ù—P˜ðZµƒ8`±Âj\2‚öEDÃR½FâǪIŠŸÔNbVQM³ÁnPE¡fŠ“ò±Õª]hFK…3DÛh”uPHlÄ*LcFª%–Þ\…É) Y4#(¤ÿ›®)Œ.ÒöAáÔâoBaÕll[…i¦¯Úa¦ºAì6r·ËÖQ·Yd Íj.Ò·7‘È:(ì.›€•œVß 1¨PÔò¢Ç=U‹sšü¢(lOær(Íð½ “€åÅ&0 ÏéŠÇŒ» µFQ‘ çq‘®ƒ¼ˆUX&rŽƒ«¶;Æ&(ÛˆF=…)·]Â*ïQpÞwÛ+!GF¡v9xaƒTÓ¦¤Eö… fºr°uPˆJ¥õYU"ciƒ i¦ _F¶ÚOu«vP\KiÌF4¨"øehÛ¶ç4îE­B2®z"%uÛ h’ÿ­þ4QË*ÌÆ‘{j7ˆéó¾›–MÒ—ÇD!º§rÑB¼ð9j`€Æé®4eS‚Ðnjו»œ<Â7Ⱥ1F_ャn5j°QÞf´•,n7IÐDàÈÊ)ï¦D!ÒÑþÁrô¯¸Ô:\…(LqP$²@ÃowV!ª {uã¥wñ@wܺĺSI.£iVëÙuOêIpmÄ¡ƒèˆBݲ >st±ÚøW‹Œi7FÂDZr¢ŽÜƒ†ˆH7|Y.UÊh²ãE:wÏØ¾ÇF!†Jºiñ¾•L ÈK“Œ¢€]«)èNÆ!]¢a¸Ý,f­Ñ Ã8Åým($Å8vâ…oÒƒ-L „0êº#(¤>ºyLÉ5Þ ÊtzZJìfGC!W%ˆp&l%»/â,î&«Z×̼’Õac”£º4jíµu‚­i²FP â|vÃ^ÆmŒaÊ»Hõ°F`Ú^ƒÄW¦JõÌÈŒRtc±ËôÙ+7ÝÃRÜEšòÒ² pû¯Ö+VDu/j$µKA˜ ukÈ5Ø ª)¨"hâ4Çuæy;é°•dÞÁ¦†SšÐ©n‹e•Jߪ$‚µï$S(ä`‹žÇ•†(G`lËꨋ±Äš á &² oPÛÙUÞ Óƒ²2)À”¹—6s*Ö”1¥Z…¤-.Ù c:-üê.V¢\Ü—ÏN7­Ü? pëh•ì,ŒQ8{¬Ñ(\Yð‘ì6ÂîVµ üYELiëi†ôYñ dmÒ€¥ÆÉš¢ˆ7‘0@<· öˆ0e ¹Õ«<ÊŠkN;ªMkŽôø-„1 ·Ð © ›@a=,1$ÿ±D mÀò!z²¹Ä,¥½¥¦]žhô …ÑðLó¾ÑgGE8 ¸ÌÊϩ̌®iµî"ZÔ(Üà«É(Ü`Kí …q²,!†^g9×FÑA2±ºŽ™æT¶i2_Á7þY}Ž@:¾ÞX#JÅ6 78ÀŒÂ½4ÊnPXû§@NhÄÓI(,ä4—¬ÂhÒáÕâ5“Ò}ˆ ܃õBÊFá^F­Â ¶Ô&PØ]6©öb¬èi¦:ØB5µÈ0b÷MÅ%°hål¼¢wÏ•úõr~ù=÷:¤Ku1 78lÎ,’Qx¦€—ˆ¾  Sî-ß’MB WQ’@üµ:-8µS—)4@™ö$¢ëB¨]*gÜ9ÕºFá%úýuÓ4 ¯«5÷­ ½ÂrãK:8«AÄð´@¹`Â_Ëï‘Bu’N)O¡P‡~´e…´j9KËS=9σ:¾f®6lÚÛ6,†Q¸ ˜K%µ OùܹÎKgn¦º)I1µ=[çö”&@C[¬{ÚÁ¢Óª•èȲA¢vÚ˜öô°„2xy %³;B!mÞò‚b#OÁ@Ä´µT/I‡›CGBžæ*(D_­¶z`÷,)]%D_ínA‘åH±ä(êø–¸S›iC(DÑÓë~¸¤€á1å8G€NÕÂl…ÕÇm¦wתëWá  âØˆ 4èÊË¥š2H1Jõ Ê˱²÷l …ºWÉ)¡Ôñä{Ü-;r²žs‹lNÍGûŽjw_^\úC Ù ŽL£pA1¯œÔ–QH It¥qÅw×FÒ•=>nɼÄË‹o…iׄ(†ïyò=¢°{²žALâ›"pi–©Úºã‡· ­­…]ýw`ã(Œ¬Iëæ”8¢c)ΜÆ-™—xyñ£0-…Qÿt†*y¯#UlÜdéW;@{¤q¢×àÔù©ãÖ(ãªË%¤Qx U¯“æ–Q¨áAiFPˆ`Ü`}7~ß¶)ŒÂ“P¨e¨è̶­B„œ:Y¿àË‹ÉA”dÜ•ž=öŒÂÙÒm.âfQXž&D¡Nm|q‹{2aØ <ûåÅ·ì SϸiÄ*œ:Y¿ÔË‹õ\‚uFšQ¸ŽÎkä²&ÏH{ÓN}ç'$£=XÄË‹o…ibA¯åI6à Ñ@S'ëyy1×I€ér&äÃÉ(¼„ª×Is#($¼tqP•¡è.›È•®®.þòâAaj®oD{²¢°qv[RÑúI<Ø×U›Ë2Õk|½å¤qhž$צ_…:oW%+Ð!˜{Å(0óå04R˜//®éBí}•Ó&T8]Cñé§)I«­FÝßÂ<ãåÅÌnêºD»…—Põ:i^…שös½ w(ÕzE6 ×ÓúÒ9…—Vx©ôÂ¥”\0£pA1¯œ”QxåÎÞ(–j½€ë¡pjÔß/«Z´ì>Ièeswjã Ü¿¿:Ð?ÿüóñDrY`OŒÐ—Üüî»ï~ÿï÷O#Ñ^¿~ýÙgŸ-[b§VU:Cí²Q>}úä¶µ>35|hÝûºŠUK¢P3Q8‚Ë †AKl°T·\¤·oßè·¬À.êk÷BÏÜßNõÂí´Å-–Ä(ÜE«…»h¦ i«ð±V j®"ó¹™…ç*¸µøFáÖZÄ(ÜZ‹TËcN(¤Qx‚X«5 W‘ùÜLn…¨3VÁŽzq‰Ü×vøë_ÿúÇ?þq;åqIª `ië/ù˱ÅùòË/ÿ½™æÇ¼ÊR·3µVÀ lD~øáا†ÿü¶ ·Ö¾¶ ·Ö"7k²â>|¸‰ wž+7£ð\·ß(ÜZ‹…[k‘›ÝWhy]ñ°…4 wÑ´¶ wÑL'ÒVá b­Ô(\Eæs31 ÏUpkñ­µˆQ¸µ±ƒìä]ôÉ£Ò(ÜE‹Ú*ÜE3PH[…'ˆµJP£p™ÏÍÄ(#'(Â1»ëž‹DeË}ùe™÷‚BzLh\¨Šªñ.…QÇù¦´ò‹hA]ø•qÓ€©¢Pn²Q8 l®ï³¥Ðd¼™•§MÐ:±± W­Sìé&…¿µÏc‘}!],à­~ŠMLDÃiðåTGÑ4Ç”›0»‡Gäyjó]Lg/(D™¹™†]_©§Ñ*,/+»0…B¹É¶ Ç»\ ™œQ5PH̲Añ}ÚØ8k”ÖÍfxÁˆW° åË”c^8›²ûbÍ9ÀÈÍñ”SÚ±Ù^z$©F×IÑw„B–ÍÐÓŽ¨šùšÖЇ)ëßOÝ-8ßÒ=/1Ò‹„ÙÝ“i8”Ð@l)4Y9{HýÓU• Ѹ¯ã§h‘†Iä (,ÏKÅ‚òv¡ÑB8VOœvm,6Œˆ˜¯|íÆL?‡4;ã–-­Ùϲ™Õö “T¥Êv ÃÝ¡p¤//ÌQx¼V¬ÑÚ(ÔQÇ©ò¥Sæ}I!rüe5_vYúb2SHdAˆk«ôgå_h‚,Ñ–Û÷5m@}É,Úígöïë3 ¯«ÿI¹¯Bâ£úÜM•;Ùe\Í(Oÿ$^ ž ¬ªCS´ík“ƒ\ % /Yp,ª†_µ75Î73<‚8'Ío˜/¾±UxRßÝ~`£pûm¤®Â´]³ª¡#“ówqCmÀòy'#'(«9F_â,$ó i>>n»g§OEJ&Á§–Ó>¸ÏîhH,XT£pA1/ÔvQç˪h=M͹¶OP–‚’GµåGi™¦¸‘\iïCÒ»× x9 ™¦8ÂKw÷•Ó7 Wüœì¶ˆÂÑEm§ê_=AYÖv*|Е&ìªk/$&‘­iD^ÈG.‹mžÓw·×(Ü~]ÍAî.›”O-FY‹3Œì^éÃø ÊÔ6L9ñ‹Æ\FPˆ(qÓ©>«¨FáŽÆ"E5 ‘qDÖ¶ i@ Sî-ßr—¬`TõUùëÈ Ê$kÚ»£_i¯É «.ݰ¨´ ËéŲýŒÂuúôvr1 ·ÓÝ’\…z:E¹V§PÊrsFq”rÁdüeJ¹tÆ î¬.›ÄäêûŒ¸F¬åò] ð§Â»½pûº;=«U¸4 µÏyûn¿„W@!D¡-4è´#U_Nu;¢$-Â&ruOPÆ&™zÃ$¼2k˜«(ºx¹™F½<>ˆBjÒî:—ØWX=~‰ClœÃ=gl@NG0‘ª£pNú).oÞÝF)s¼ !`ùjéî dæ rfK!Ó¸rˆÔðç %ۅדÒiÙ”GGê~4´ªtQ²ÆY±rªb#'(c\zµ×8$›QÏ`Q£ƒŒdãlV ÿb)Ç. IÏ´bS­ìâ(”=Î÷Z ³¢×R‡‘žäå¦ÁE§æÂðÄîvP¨òÄóûåCæU6ÅZä›{|Äxù1î8NÑu!ŽÀÒ×Õį†Bõ³ÆiÇTbilŸ[ìž L#ª}ƒ*»Òä¤dµ] \eKT /ãDËßmÅeQ¨)‹r²•4l<h¼Ë6n §&² 9®¶ƒB-*–=„†Â²÷­Eî1—F!y2ïñtÃH»2 GЏ…0|pN¢ð"÷Ìyµ[…i‡©‡MÍRÅ›ÃDÑx3c7Õ=@a> ¢߉Ñ#(œŠKC·ŸöU»¬’ù?Þd‹;ÈÕ·hÆ{ ´MÅ›Ò!>+¡Úšø’(”JjÜÒD ˜e âûNÅmôöuÄ¡ŽÍ> }Óc)—½ å÷@ ¢°»½)Ýt^P¾|´ÙÓì‡6™ÓŽÖÅy(=È‹_rr®€ÿÆÃšm¦ÓâÕ¸ñ$xâ¯Z`Õf»u‹£0ÍØ”ÔKNOCCVJÇc£¨‡Sa]´°Ø¸¼1àR³JL6ïsUT•JröP ]›úF9LJO±-Tu …£üa{ëZÜ-ÇÒ(9®²É•¦ƒÆå£èAóO\q}‰Cá«V¡F¢è7u&‰ÐÑ“÷×@¡^ÀL)nù˜X9ŽÜÌ6‚ÂîM+v!¹ÒS²R Ð(£È\iªqùÜV¶)Ɉ¸ä„§†UÈ2 G¢Sqep”}cd˜L¹2í¸Fሶÿ£‰Ht‚ö”å ‰Î º UÈî8¸PH» Nn¦w¤et‚Œ–BÕL Uˆqò«zŠ1îÜl °úT·¸-”qS-Ú°²äƒS`] …¡X÷dL%7³lÜt´”I%5PŠqu*ûFw¸@¥Äân0 …ÚV°« pê)q*£%Ý'R/ÿÄ7‰Üçˆï8b«žQ|¢2õhôj3ï…U)j(9V¹d_lš²qÓ³ò”Tjá;*9õ˜äXòSý\r0º)ããÖ(×jC!Dau÷xµªÉS˜Ø¿«ÓÕ]&K§4W̽aÊ¥â(Ò%LբѺAáÔ³;«%ïjX­T…é¦5¥Ì å¡—-¥\ªµ˜j)íä·áÁ(ÜàÆ‹² »3P0ú8+´büü¾)_ôÁ÷‹B¾m%±ÂìE{A!§Ë«Q‹ê0ÑÆØ©E°îà2 »m1À‚(Dõx¯žàŽOÇ DÊu†ä 'á–m#qjÀGç®m–ˆM¸kN™8ÔŽƒrÇ<«°´ÂF¬ÂòáxåH´ ÉAdzÎ ¾Q¸EÒuË´, Õ“Ê|ÓL9X:<GÝ"rõP\h ° CâÉŸ]£Pw¦ÒöIë¿] qéR¤‰WÿФGü3­úûéËjWCwÔ´…g xèË¢uà@B÷åËpá9'òx'Ç74!ñ/‡œü²A¢‹# "µìî:J¥eežqŸÇT¦i ÒW™ÙT»F¡ªÃI¶TÒ™T™ö] ÇQÖÐÚª6nÚç¤Ã£j»)"AmÄQwb—“™9‚B„¡³2åeQ£pP¨m[…ì—ìUºðgÕNóÜqóó ÓŽÙjw¹Yªhz4¬BT„/âŽ~ˆ®ÓÞQˆ JœØXåùý¶†#(dÝ«‹ò°A¥6>D—¹B݃cÒmµz›LãÓ&Õ iž4h“äÚJàK uCßâݽ¹:{Hc¦ÂpseêHVšÄ´7Ÿ§¦xP¦ £¹W3UEJ_2ÆiÑöû|),~Ú$æ…ZPœö.×) «•*•Ä7zTRUg ?©Û$AbûNeÊîTv¹Fß`.Lpêi\…1 O’k+/‡Â­Ôðå¸( ¡Ð†*an¨1Æ‹bŽkuÅFáÅ?5k£ðTÅ6Þ(ÜD3ô aöÚÐïFá†c¼(Fá¸VW i^QüS³6 OUláÂM4C¯FaO¡ ý¾< §Ö¶ýýâ üôÓOeWjl/X¼NpDû÷ïOø‘賦oß¾­6ÖN‚öëׯïܹ³f¹o9/ÜÄÞ¿_6ЧOŸþô§?ݲ2›ªûgŸ}V½c±á¾þúëM•öÆ óàÁƒ)âý2Oi¹k“uIEND®B`‚neutron-12.1.1/doc/source/admin/figures/scenario-classic-mt-compute1.svg0000664000175000017500000003441613553660046026245 0ustar zuulzuul00000000000000
Compute Node
[Not supported by viewer]
Interface 2
(unnumbered)
[Not supported by viewer]
VLAN network
[Not supported by viewer]
Linux Network Utilities
[Not supported by viewer]
Macvtap
Agent
[Not supported by viewer]
VLAN
Sub
Interface
[Not supported by viewer]
VLANs
[Not supported by viewer]
Instance
[Not supported by viewer]
Compute Node Overview
[Not supported by viewer]
neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-flowns2.png0000664000175000017500000043744213553660046026646 0ustar zuulzuul00000000000000‰PNG  IHDRþÕ#ü–sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì]`TÅÖž¹»›@B¯ ˆ€ŠŠÅ‚ì]Ÿ½wŠ$]IÁ§X( ¶ç{ú?Ñ÷ž½bG±"ˆtH‡@êÞ{çÿÎMîr³Ù$›ìnØ$g`sçN93óM93gÎÌ‚ #À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ@ƒ! ,%Nˆh Fú²N0ü—íÓ@Iîódîô=Þ½Ìïï#<îµy¾±kCeH)%GeLêojþC4¥mIò·éãÆí–ÝxGÀnón!ÖLËö®‹÷üÆSþ€Yã6>ßîbnå7.Yä6ÔBnµ×éèô¬^†Ttn[¥&6ž> ß’«®ºÊ¨=výB¤fdß ”Ù:/+czý(„K×Õ‡ÙŒN“RªðbÅ6TŠ/7I3¶lH£fjJH±Qº<+føÜ”KuýJ!ÔÍÐǃޣÁ4iLõfÿK 5L †MúS(J˜<¹Ó>X¾)¼ßå{|ÿR½l„‰ÿÈÍzèÏx(W›Ä÷=)/F·ëòRSý5å뮜œÎz™lÝUì·Á绵¤¦°ÍÍÏnóºÔ¼({vs+$åm´Œ¤÷ñƒ Q–›¯Ï9Q(‘``(A½h¸7s§Tò_É“zj̘âHjjqS}YÇšºx¼L©Ó þ7â³,Û–âÍzçÌý‡G{p×3Ï$–n*x)yR²³ßÊKOßØÔ° U*wÙ¦‚•ËC¨öÆbº„½iªo暦=:c|úÇ¡âGË­L/›Š$‡aÂ1}ãYL‰\¨ûM•éne†¸R"]H'¼Žˆ–‘ÐÙ,¾ï­ûuk"×m&fEL«ZSRhNB=ݲA䟂@ÍF‚U- ì%ãOõfÞe(ÿãÈZb [‚UÝBÈE@$I s ¶!XÙܹg{á£|Ù×Mó¥ÿ´9‘Ñóëþ/Àë[I)þfŸâ÷‹ª‹Pª?˜³QÄ>ÑfúÛ³wß] &G+ñŽÝýi›ÆcÛЪÜlÌÞ.žší]c»7…gɦ‚4pgíô;Ø?UB+Õ”Ú_Iu Óñ˜”Ʋ¬£Ò³ð+u)ê|í™>>õËü×›¶”_c|Xƒ¶ýY½iÄ6âßÞlÈñ©)÷µØVUÓ¢ÞèÿpoÎŦ2žA5RÈôîîô‰>Ÿ4Õr—ï™6¥þ‚ç!ê½R×Ízüñ¾“~x·3Ls´û•þ$M+©ÉI¹ãÓÇ‹Á}¾—Zl›ºÄ ˆø/)§Q)‰ÒBó|0ÄÂãJ¬äÑÈ_R3rN4Mã0ÜÍšÛuü ߸ÕÁEå›Ð·‹»RˆÊ˜‡‹ä]wɾ–TGȹ͆é0´qbø½#Á.VqÑ&¶¢/vÂøô"Äÿƒ}¾az¬ÒrÒmª}ÍYF¶×Ž€V{ø AûcR} ¡iâN(oå3}ò{Öwwü®Bçú/:W—{ücɽ63kÖ,¥Q[¸`ŠSŸxD‡˜m0½X¼“b¤ ƒ-Ú®“ƒ™>¹ÓbuŠa±ÈS€¦TìaZH„^WÌ©~Ã$•`>oRrV(¦O~Ó|ã–‡jÃÎ DÒ¾ÊÓÝÊéÉíNºõ±cÝšÚR¸q#É;éE„›N¤áêÒž")S ŸØŠÄøô Ƨ£óõï ¸G` k,©G_‹ KU¢†•Ç*±Ê¢Ý|¾Y ‘ä§šl6 ç°;p<”Êa)¦iæ )WÉ:\›bLJÆÄ#”©/@!K Á|s_™öV…’¿`‚ º7a0£ýÀ£–ÿ"¤1+73í‰P ’°ðù”¶ÁŸýÄä—aËáÈ |#þKXÙN­x<ÊÓ0Ó¤ô\äJXWè/šÏ“ÿŒ£Åˆ·Ds‰œã½ÿ DªÆ2ü‘ìó¤i>…xsgf{o­&t²ò¤P§*ázhfVÚ;(ón”±•LÒº;±”oÒ~º^ú%DÔÛò²¼C‚Àm < vm<'KTFú²º9 Ø,š™•Å3!,Ì…h“›é=”páÍ<Ýb<¶,úà¬~ í·ü™YÞ3ÈnÄ?ö,”û< À&ó‘Æ'=ÜýGù|W•QXèxì€GÔƒkDFöUØJ¿ Î'€v[„‡èW¼ŸìIzì)ߘˆ!¥WIñfßeÆ'P®C½÷„Ø{'pÁ6 ¹ õz^¹½ú¿”|#çÐ…¸‡‚N蔡Þ×âý#¡¹óò2ÇÒ6[ÀÔ5ïvßÐÜÚ¤` ]b„—#߃Öj¡ÉÑõiï6]äsjnVƳ VXÂmO¼®e NË~' {üšÓ¤æzÞT:C†ðhGåùÒ–Úáìçðô¬—ÐnA:mhŽín?S|9‡Ý€®€<cØ—¿ï5ëçd³¶¾æq'\íþNy¤Ibê#Ù£Ñç®ÆïôALårÔëO%Æ…RĶ뭺ö€q¡Ѷۼ„r_^V:éKÌðŒ Ç ¥¿l ¥Ç}¥½ IõNêgв‡1.^€|tE™ØŽ?óaû_WúŒÚ&âD±¥Q­øÑˆN&¬QIÓkcúŽ$̪¿ÁJ·¥VªN 7Û`Pé‡ßéÃ3²218Sçr!ì› Žð4À™“R2²ÞIÉÍõØqì'­zòýÙ€.ô däç äêàCÝ10>‡åÔàíðô4•h¿ƒ•ò_Y¨~‡pœ× þKHwhkê¿hô)Îx¡ìRëôÈwã»ážìl4ÞªftNNG,ñoÏí[»¾¨Q>@©«Æ¨ì2Í÷Ð_è”ã$R¤¬ìKZ^k•G¨{ô* D÷«³É bìr\û‘›ýnJ9˜m~;,7)–!Í…ôƒÞ{¥ApxFö5ó+â_ƒ:J~"Ê¥Zâw”ÍômÚxîãM7LõoÈ:ö£ðHÿ¿| ¢îF|O«Â”þax0'Qçÿ·¡¿ÖáÈ3 <³œqÑ©Î@þ‘$!M^¦÷9x<—’žõžÇJ‘pwnvÕcW$}ðûËž5·&Õ¹Y^bD–¡Ár³˜\E/i·AùÇI—¼(o¼÷};< â;v—ýÿCK·Œ„ûÛ/ÚO¬¼?MÍÈÂÀ,Ž“þ’oFdäÜ1#3 ƒ_Í&’öå¤ |©lS0™¼R¿ò²Óït†©ÉN“DzBãN´¡mÉî¤þN Ž&ïÞY:ûè_;iD”wÓÌDZ=5—vnð)‡ú´wg¾œöº¶§ˆÊäLØi—ÊÚÆHìÜfœ…IÜ¢•纙ŽñΚx™êEèžüó>ß“}mË.&œÕ¶‡@˜ ITt]ÿέð»hzfÚ7v2U¡:¢< )äxÛž$ pá,¸Ó­)Ûë4ÃÜ÷@(‹ykR[n^0”‡Uåq«Ä“ê9'Ó'ÿ©¾´eHã²›J¦9gâ4h uÀO*·Ûåµ™>…â»o§’®1dG/=«=µHq2} Ó¦MÂÿ¡ƒ” b:òS5^‹/–»˜¡W聆Ü5ÎUafd¥½† ‘­ôcV=nG?L7—¶Dì0ΧËe1~HyÅéN÷¿ô9§¢Cw”B¼Ž=Àô)ˆ5Ñ‘ò?Vp¥…ˆ5'ÚÎÐZÈ¿‡€×!†iÌ¡IMòªÛ[Œ¼}E-ûB•©*¨}<0Ó1ÀÜlo%¦iÞÑfŽF¿»%˜é[y¨G{¯‰º´§HËT]Ðw,Ý?0VÜŽ6‚ù³Èá›Ð»º8•Üõâ4à…É­|ÃÉô)LnVI.?@Ÿé¯­ÝrU¥x5¼D³¿Ó=!¦Rw£\{ÜÉî‚Ç»™™éÿ¦þŒ~ÑÛž¡²Uc{îmðëÆlxu’Âu5¶ú*/:”êIÑ4aSÙÐ=¤oSÙµé¾5Æ_Á|-‘LÔ·†_%¦+¤H\¸µÿ†¢5=sÜûè<Ð0{oög÷°Ãè†ÿt4HÚçübºoÜJÛÝ~öp¿2¼tß“OB d¤ú"Ï÷@•ü[ç©•üŠB¦y`P¬*¯ØÛÿ¢q1h”÷ñChÖ‹N=m¹jz¦×¢iû£3LwKÏ‘`J/¡ã%¢|)Êô/Cšâ˦½ó€1“ÜŸ# á†aE‚±ªÜ˜B£m a ñ9ľŸF[™¿uh¹/þ®Ýr<1j`þ¥sbð¯£iŸUej]¢J·¬$…°ãº]š µâlÇ©ïs†×›ßÝ}ð©*Ýv±XK’ úúuƒfôÄI;âöå$¡½‹8é7Lw¡þÏÁ¶×ݵ)GFšw¤õ3MNCe»¾í=­º´§HË*ý`7«lJBú¥’Mݨ$] ëx¿ì.©UšÛþRhèè“ 9©·Ã9ŸÑìïº&OÁØ”`MLª¹ÒåvO£ôÑÏwæÃ¶×Ôì0ôD|Rƒ¸¬i6öîД¼%/+í-g˜ »µÑô‘¾ }Bø7§JƒN<—šVvØ;_<öTº¥¥œN~•”ݱŸ„ö'׆ŸÒVkP[8Ý•¦õÆs_Ñ^' Žâ ?ÿgÙ6èY'±g$TBqA1…ýÍáx’ÊÒ Ém”UüG‡©Ý€¿„°p‘ÑfÇ€¨ëË®ÄËTÛÝ~NËzx ì·ðMkøKoB»0ž!ýâ;ˆ…ïÈÍLÿ…¥Y:.ô!±øI£Ë¡d!M°q)˜Øö³öÿá³…ËqCžq'Dv—ŸDl”ÿÓ鉴?¦g$†˜Íì…ô ½»Û»ª.ÇÞZxdyeÀÔ¶ QJõTë?•7åÑlk¢ã$ãÆƒSŒèô ¶Wè¼Z/ŽÌÈ)Ò t9ôHÆçû¿=ÇOÏ¡“(V¼HÛWpâ5¼×V6ÚnÂ>ñ¥˜à½}Ú§¡tw&ˆ3[&ÉžNOßT…t„yÇÔò—*4õmï¢Îí)Â29Ó®ÉÞ¦]bZÁÎÒ‹Q'gbîö`É–3®U†Ëz£ßš†2Faåô/·›Ö8…6Þ·ª_h—höw´íŠt%¡!MW1võ‘í‡çT¦à£¦µµ›(ÚEÙ½è <C4kF–÷UÛÏù”=;?­Ön9có%†®ÿÜÞÁåYyÓKû$Ô8éŒÛÔìZ£**oD¤ôv¾+Ä;è$!Å8ÝÅ ÕÑB§ÙL~h(Ýì0°Ûi&~NÈN`¶ºfŽ|l±iEúLðx^¡9íõ4ˆï¥'Áø¥BâÿØëVÕF×Åbµñ„8 K_ä7eKD‡N×»î ]¾G¯ë¸õ &õ‘ ƒ®ÊýuÔ„.­>#qž‰cBvð~†ߥUµí%¶íËËHaÒô6Ñä/ìˆ8¤_¿*’•ºÄ§°o¼ñ††IÍ›Á?He­+-\°­ô95ÕÜòDÔ˜§:¡Tßå³iEÚ¾l:á<Ã)›µ æöà†6Mªò—]Tl®Å„ðeÜÀè”^¤yÇjÍRB¬.ï‘¶w¢[×öi™ª+K°;Iü ’BîhäOØ}} Ý+{× '™o~_Õ ùòX/ÕA`ø4A¡Î°Â…ý':ýzXû[IJûIÕ PFþ¶PY¨LÁ!jkvx›½(Yãž×“®•íç|’®COúeRÓ®¨ 0®\fæGXÈüŽí·ëœa›ºÝÝÈ H³Çah.´úüO˜y?ŒÂa·5$ãßàù®3¼7†¤…ÎêvÊ´&)¶ Áña²ÕáO4g–ÏùÞ€Ø'ÈÏyXIž†l|I¨FýAèôŸ…{ ^Å ‰Tæ}>öB¢ukÒ€wZÅû°I«ø§±ºÿž$b´ð'Q>$#§+F=2ñloüŽi›aM¨ãI·.F÷tÞ&ü€_–_w[—¸W^ye& ¡hOd?ì‡qxm°[]Þgø¼?bew t-èta:†âc ¨}Õ%á–­B+z#Æ7£®ïE»¸Y‰ó¡{2éÄD#ïµõh´÷º¶§†¬(J~Šþü"°½­Ôï'1ø¥èÛ¥ÁuênãÙ&¶ù1 ©"ŒA‚ý#yVÇøó:f1C«1H‹¤‡Ñ·ÍŽâÀÀk¯­=Â)ÙQ¹Ä•Ø×¸ÒÝ[ ý…/Â¯Š”ŽÂW,^ƒõµò#Æ=Àø(¾ ìž–n…mʦq­ø¥V¾7‹Œ‘Vqm3<#ç4´=(pÉ- îVsB†÷Û—›„ô=Ðru»WÛ¾R)kƒ¨ûÛm?_¢ô±ïNÚý_¥»žž¨XËìá,HçRXk{¤"ÒYGöÿxº0ކÁGÉ‹€çnO—V–XŸ‚¹”´öÓ ¼v˜þ`N’ŽˆWûD›N0 ýèœIØšèBn ihð!©HðoFfú¬Hó‘ànûÑÀª§»M«!ÛW]ËFŠ‘¤ÞÝÝÚÿ›Èyç2Ýô5pÞ#jïumO Y„c’'ù~jïÀöÒ|Gó+´ñµŸôý¸¯CŸhE m¶{4žQëïšøÙÊ”½«ËWjN¥q”wB„8Ž[]´*îøÀØä™ã½ÿ‘žÎ£ÝïÀîoÐI¹·JÀ ‡™™ã~Â7@0<y¹;ÔÑå hMâµQ1~RØÀ`®:ì*©² sÖi–c"9ÞrÓ„/°‡ê Dvݼ8؉Þé ,:VO4ˆÍû‰Ö¸]?À +Iu9w ¸ï#Kb×6ïÐ~;Xÿåtô•Ĩ[vL ©´XK6Ë':B.¶Ã‘84?3ïòhιè0è¼ïTÚkáI~iú‘3¤C­¸šøØ¦Që“N3ÀhR߯š°Ö bêe7Wãß(ËŒ" o`À[ÄYû , Ò nWz¹}ã¡Â4@Þ£ÔÞÃoO P&>zÒÉ !µQ–›©žA¯°´ÿaÊíÒš¤C¡-¥ª_ .µôµhõ÷D­õ<Óa~Eµ‹´"HÈHeM€kÈu^X”QšÔy47ÝØZ„÷°ä@ɸv“—™†S7t Fuaô«=FãѨ?Á­¹ÅýÔRÀ”ÅÙÞçCiÎÓ‘(qÌA SiØÃ5$¯ºªB£¬O ›O/£Mrž©Ïõ¥ýŽFòZH»²"ãå‘&´¦M“º Ø=ïÄ€±  xtÚè_öš.^x=ø«„´2™‘s )Ñ„Êõ—¯ÒP©#âzäO¬8†ºž8*c‰ùm:僕ø ½Š3âT„0’µäÏlÿ0ž«( • M¨j4Íõ(Õ9&7ðe Aßfv‹‡wÜXv •ë¶òO'?ÈQÞ·ÃÄSû¢~â›ØÓΛ󩛦u’„ú—íÞy·½Ûy õ¬K{jˆ2çÑÒH—t/†ê ÆyG°?½»Ü®4tDÿ•Ð÷KB…¡#u£|S[ùÕØ×(l4ú»µÐ’’.cê¥Å'ƒO¯ŒÌ˜€[Õý(1íG‚òXïש™c+©Ý…t=B³HÛß&FÇ–ƒóA~Ö"Q”÷ÆI @{¶ã5ŧ»±ŠöF±?+ÿ@§¸½p[ÑØc^ˆã+ ÑQÐÈÕ@ÜZ5ÏDÌüÞî·;w¥òÒUJ,ÔEÙÏPVzÌówÈÈ»ù M\ë 5 -cw'kpvÆKHÒÆù‹ŒÁ¡¾ÇX‚ #YŽ•[;p§^8Î6¬L*Zñ^íŒ+;² S¿"ãq˜PÏ}18­RSï‡cdŸCC~+öòç!¯ù(ëV„ï ¬ŽÅUÈGÃFg¼&ø¸!&Pb}uúâÖm=Ó·@ëL¸ãü¾škMª í¢É×ÁõoÄ v/ê²òö9 Ñ×pfRºá î“€÷æ_| m\J1&`½ûà}Ê ªLÀà¶O 4á½~£ììþ‚ºY‰Yv>eÈj#þ’sðl <¿èîò˜3£ñÒ¾Ì2³n†ûùÇíybž)Ä*ä³VSÇáB Ó¬••[Ni輇ÓÞy ¶×µ=í‹úp¹[ÜeúKÏ Æœz§£ÄècÐ_qm¶ñ6ì_¡ÿü‚»ÿö@¶;‚…{•ñè{0â® ÛÔÒ×(X´ú{wÚ½8åÔôR6èsŽƒøýSÜU°íf ®ô+‘_$¸¢Ÿ“îVÔ îxãùP¤{ã½ðx^LÛZ¸¾}r¾ùíQðûp9œvc 9Z¬ÛŒmLÑùúw¨#ÚQËXjt+~ÂŽ4£“Ý­Ž û®ñº+ÞP«£QÁ7£1õÁ,ò'—)8³~I0sbã>óÈþçÀm:š;V›j2”Q¨3u ë/»»÷?‰ÄGÎ8dŸš–¶ šðÇѽèk §@«^< E-(ŠàúL©–ÚÊoÁqcñN-i©ÒýKHtœŽ­¬“ùò—ˆr‚é¨ÛÀ˜f·‚Q£Móh«\zBp[¸+-š˜Ð„ú†{¢;áí½ijá‹ù‰>]²#µ40G`)®B9f`uÿÀ^z¸:1+c¬æ’´²!惸¸c²‚;ŠÊu œáãÂ.Å\èvÃcP.ܦ&î)o#PÚÂ$u’ÚÝÓÿÜà‰i¼´/—ð¬E{¡z= Œþô©øy©ýó¯\nyFž/}¾ë†È{8íÝ™§Pöº´§†(SpéÄ ¾¢Yã>5ÝÉ¡I÷`ÚþD{:íì^ª«oKZ¸È—p3@¥“áôµhõwR¤Ãz=ºè³hG}i¼±ÆXR¦ÃÄ'NÓ*¸ìÑxoÓ6q$Ƭ¥èwBðA¢‰ñú¿xì.óT6úâ3d‡[[Œ#"¯do¼¯ñÕxÖïèïw‰Ýö½Ìµ• +ȼڢááŽ~¬ûaH4¦<.ÏÔGÇ®°Ýj£Cþ–¸¿Hô1•« mkõWM×à†C/–aHÔÿíŸv*ñK¬$Ì$\ꛟ›–öW]Ê«üÑÝùæ–¢¾¦ËpµhÓòÏàí ;]ÚÞ))(î×B´\|£œ&žžtMpÑî²N†twÀé’"ÝÝ'ªN(«Ëó¾n_V½l*ØO÷ˆÎn¡øEÇõáæ_ç½:LîumOñX&ê×_ý¾ü 0ý$M¸7Ÿ:àÀMÁçâe·¯9ãDb'ý£úê^ÒíOÜOô[‰2_$ù ¸´…¥—‰®š![J—+ê£nЇñ/Òrqü0 Æ?<=>Õ$&?a™ƒ0Œ#À0ØåeÃ0Œ#À0Ífüͦª¹ Œ#À0ŒôF€`F€h>4ºã|Ѫh|~ øà3®Ñ"ÏtF€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`š'|O}¯w|“`¾4ør^fúóñZÔ᎓¦ñ¾”Ô rÐÜ÷äeŽ]4Ò—=P×Íl|Lé|G)_I÷ƒ .µ ŸÒ4§ùÆ-uyðùâ‘î}¹™ÞAüú£ ïÉÎîjúÝžg}¯¯΢3Zåóù^j±A_ß_‰Ë÷ùn-‰NîâƒJJFÎñ•°¢¦¯¥Ö'§ô±¡ÒÍ»öoŠ˜Õpã4‹ |Fdä 1Lc.ìù6/Ó‹ïÅ—Ï :ït0ÁyÙÞAN÷pí>ŸÒè3”á†o¨p(Ó‘ÒTû…›^jzÖÕ¦Pÿ®-¼&]äf¥}P[¸ÚüS|OtRþ’Ïð)ãß5©á«I¢«ÛÕzÕ]¾gÚ”ú >ÇEKkð9M|BSvlåJ\±Ç_ô>>!¬ƒî)µÑ®Éßäþh:3TMŠ»s³2žE;é‚0G‡ n ÑÎk+w¨~P_Gú²NÐýj®[s:=3íJßt¿ßtO+*2» Q&Ðoéûîó4|‚vFfú¬Úò©4Ëgç%Õ;©Ÿ)JŸÌ÷ç_7 毣\¿S¹ÚµöÜÏ_û´ËPÓÓçû½ÁÿíWBÓgÔÇÔ6\¿oæÙX(ä€é‰1à ì0{ËÕRÜ5ÃëÍ—Ns ×, r•’š‘5 ïSnQ°¤z3oÏ׳O©;¢@nŸ’Hðx¾-1ýWØ™À¤:V¾©&4µÌvwiîJßa·Ýëú”zñEèÀ­…ô ÏÅ*ߎŸ’žu%>1ÚQÓ\ÍÈL›k»÷æL@> û=¢§K„&3‚ià»á¿»5ª÷µóp0È׳ÖÜå{üÄh¬Ä ]Þ„ÉúªiãÇÍãìG£nžCü·®7ðÜd*5Ÿ—lJYNþ" ‹~NŸlÞ±»ìcä˃ ÒÃ.·øF²§‰2A²Õ½±3}ÂÛç¦ÏÈ£ Ï·‘àoÇåË> Â÷„”3€Ù].·ÜdÆ…B© £X¾‰ÉúIp·>µnÇágešã—b¾Â;aTzÖ;Ó²½ë*CQÿ7´°‹{Ký)ÄOÌŠ{½£ÔG² ”a ·[}1ÝçýÞvÖS ­§ÄR »ëáÅBŒÝKVJr7:·ª4Á˜™•öÎÞ@Û¶ÎïýOÄTâ@ŒÚymÅ‘1áhÃÔ÷¯-\8þôýö|ý«!±"É‹=ˆßŒñò3ö¿Áñ­ù¨0“pò‹~¾k·q Öš¦Ýœ›™þJE>¨ŸÅ\zN™£ff¦—VûQ1Ó|鿎ðMÞ†ïÁÍ‚Ï`bØI•qçcúÁ=°Hq„akÍŠñ{Üît¿ß9ñt”ÿÂpZöÈÏÄ`“Ž¡çht|ˆÕ»ØOJ£=¸”ììn¢HÍÄŠø| P;Ñð°ú€Q2ᆠ|¯¼¬Œ¿9ÓÁJv6V1‰èä§:4t^Ç7“6ÍÌJ¿›Â§ør“”±%›gC Þ4—!ü¬nî´ÉÎ-лþgww{oÌ7²C>¯E:'viÛåÙ»ï.u¦mÛ1ûΦ8Óåq];Ã7nµí^×'Êû=òõ€æe¦_<‚òÅWŸ&æf{³ÊÅ—þ»¤Rÿž·3ô]ÉÚ¸éãÆí =Úâ"•†ÕÚU„G¾?ûˆê XÏb¥¨RpǬ dSÁäîbÒŒlï«Ã½YÏkJ”"Ñv~X)uÒ髤Ú&…ön^V:Iw¢n†gäœ&M3刲ùQ÷?»EÂØiY/¡Ä°2œIÝGšázµóZÊd空:G{Çê[OáJýþOκ؊­³ÓÉ «ssÔĉíÐ^'³ÓàÔ+ÚßÜR{ÄãS8ÛlÔ—c': müÓv½=Àz«ƒé¼‚-µÕ…å›ÚÊïßñ-ÚlÚúß^3"#ûÃT÷ÍÌöYS?GùÞ Xu-_ %eî±â»ÔÒ€[ –ÞÌ¡&Æ#ôñÖ¿?¤tÝ“[!£¾ ü[Gû;Xu~ \Ró:1¦1}ä|ÙÊ“"vëO‚ÆPÔ ˜¦üM㜑•ñ¹3 µõe;lumƒÆòSR¾âœúm-cœMßù bú¶×²è†ÖÒvàghÐæ›Ñ„VÆr/Ë)Ù×ÕVrÚïÆ`ó:™ ûÌ÷¬Ogx¾žÿ>Å…l® ƒý…×_±GE?t¢ùàYkàvñ}¾';PX2¤¬¿a˜( I}dBù$î´Ÿt.×”ZMáhµ£ü[æ‚9߈åe¤q“ÔÄ{a= ù&… );!îIùzÎLÎEG~UÇôS½Ùc…ñË%}‘0ýŠôCyÎÓ ÿräs DóZ~ªì˜ ¦ÿ±Òämp{éޤϒ?D³&ÂÓjV7%.ìãÓ~¾KÈ€)”…i»+™ð#ÅÃĦµÉN†”{0Ðý€´îÀë[¨ß0ý5‰Ž•PÅŸéY×Ke|ŽsÚÃ=øy‘«vº(û95#‡¶{h¾Rïºw¦U_{]Ûy8erä%dÒµ í°|•*Õ3Ô”Ô¦ØñPº¾ÇøíÙ€Ÿaÿ º¦ùñèô¬^v8û©„ -tr²2VÜ4ñ&pæwºíêny\Â@õ©¦&:Ó1•è\Á\kìçv¼º–ÏŽ×Í“f%7¡y­¶l{„x¦fdß%¢Ïà…î#Å,ù~ÄýMºäF ž’›ëÁ¸A}êz|wõ=ôÉûq’nš_¤<’=ŒÂXcú×…bÿ+Ô‡ýpêcÆ¥NØC{ÿNof YákéËTé²mTø°¡—Qnê4ÆÙ‘jyBš;cÆæã^XKÐfïínN”ée-1¼†ÕõõØ+œŲOªÓ2-×-˜ŒŽóÞ̬ŒK+pz«¹ŸÁ€_Lõæœ?5ÍRn›…Ùìx¬^V:•‹R2&ÎV¦©ê%§"î[ûR #F‡LDß½N?{©¹ûš\(Íó)½oÐÿ0‡cy¸½Š„óÿFxs~2”ñ.¥íT¬ÃD¦7Äâ»{âó]UF4BLvî0M«]7c|:í+Fl0X¤cqîŒñÞJô*VPÎUÔ€{©¤%iy6-¶FfAY :j¨;¸ÏîÍ>|ÿŠ ÷*ù-ÝR0ƒîa.é4#sÜ/UDÑáÉ““ v–NF]¿>3˘8ByéŸô9sLÓœ‚²œúÈãõ®ûhd·.í<Ü29¥SÕÕùð HÀLuw¢;áÝ*{üJ$`òúN—Œ³Ë˜êËšmúÕOº—ÃVž–!…Oá/9Ï%U@ªC -µ×ËŠŒQ`~$Qx ýß±"ÿ®s0}ËyFVfñr…æ51÷>”ôVÇô!bS©d]‚ÉÎ ÌîGÕÆL÷­Ý­ûÏÂD@óú LRÚÒà\;åðB Ž®ÀêåËz1}HJÀ@vUùã avï.; ùï†É IU†”—P_SÑ*zlâ‘Õ}€l½-uiçá–É™™ºÔ¹3ž[y^q¾Ïx4}ÚýV,_{;Ý¡ðy-Vö†ÙÊS.A¨ð$ìqw8“ÇG7M–¿£œ ÉM®-SŸòØq#}†[¾àt,ñ·’§iþŽ2M.ñ¬G›œDÌÌ«ôÍVÛÃ6¥:ÆV¾Í'u0}+:ê‹Æ¬AÁåv…UvœÏêúrøm£cœ3Aö=ÛŠ¦alõ´l é›ZhV+~<ߨµX¹§Aœ÷ ž¯âèÖGVgBϳ öpûÝ4Ä$¬T'Øîª;lXŠ"h/Û­º'fçË©¡äŸâ›ØSùýG»¤xÀ”Êø÷’Hí¹¬ŒU˜ ƒXÓZíSXø„üü—ì!ÌŸÑ7ØÝíioÏðƒ½ˆ`o0ý±˜ σnA^Õ¸ÈÐð4hm/¼ ³ìÁº#˜´ÅP5HB"H±RTÐíƒ ©—Âêq5&E•M¸U­Ð¶Wz*u½»<‰Ë+¹ãEjÚ2…Æbè:ÕÍÊúÖ}0]0µ1p³Òµü¤ö¤=ÕµJÑÃiçha—)@¼š:øWcIj«­uzQŸÃ±Ê]˜¸%8ݱ¢¿ïïä»ËéNöi¾Ñ{ðÈ™÷'6êë¯G‹ðŸ£ž ÉÜìz•'8‘z¾‡[¾Pä¡+0îçŽðe)È}˜ØêÔ—ÃlõãœyuÚ1IÊÂdœ¶eÏ~:=}“Óí¡p‡vnú®ÝÜÞ©Ø/¿ƒÆ (ù®ûwø¥Æ*$™80úíWVa†«œáCÙ1ÿ®£}þ"½ð%ÅöýÜ' A-ß¿lw™Ýë{ê•"}ÏQèèS*ÑPªR~~JÀ]b« ²™úè¨B Š•+Þ”·cpý?Ì®C'‰íŽé!ÖÃElHT‹ø·˜Ì$a ÊÞó[RlFÎG^¢=#GfI=²Ž±Bm˜™™1­®qe¢^µn\¨/4&èXuQÝ;2„‰IL? 8Ió€= KmíÜ&N™aCÔ¹íWÓsò=ñàƒ5©¾œ¦ß$]Ú£5¬¸àæ…‘&ü×(Ôçù;~¶§.å±ãTz*å©ôÆK8å«Ì _úos;Äÿó0YŸšâË9$Ï—¶m5‰â&Oµm“àdô¹Ÿ Ì÷j¥tÐñ€Ð[HÚ^ ˜pò[×¾j<$l©ãÞ¡¨ø:òC¸¤ìRLˆiòÄ& š-ã§½µÑï(Sþù~}g| fŒÈ4¥-ÇúM·6q~À£è–Î6СЬ—œ ÚAõ‰…‰fåBEçìBU¼ Wóx> –j&½ïNK¹†|µ³~gPÛŽ²M³“âÍÆ­wæ“©¿Ã™ù¶´Ÿ¸g&ýqDéìÜñér¡“b/¬?ŠÔV«žQ$Y-)¬·æASC˜EVz;*Ô+ò i+ȽÞu_‰¨ØÏäT§×ÚÚy]ÊT§„ëØÔ›À¤6u×Nú8tBú Ÿ`òw3…¯Ky áÂT ©©ªiÌø»ÓÄ_™V®¤ï1 §J¿y>ò°Ò©U”›RQDm}g¨|a{d9ÊR<#+ýåPþõq‹Y_ŽÂý {QáP¦Ö®rê<Õ§œÍ-NÔD®¸©™cc¯j"Ñ`+•ÁíùCB¡ò+(ÌÔl°ªßˆ)‰Ñ*ë)\£ ãtŒ.§`= ¢u)ù:ò©RYûû‹¦ù¢“åFYZíg[JN¶žP<cÑ‘P’úÜá\«ƒÁv ÔÝÝ} & ËLSŸEG™jXÿb ÚÓM;é ›Ä¬Y³\(ïµö{´žÀÚÿêœ`¬¢EßI§Eçä…X8aÅ¥ßàt·ìʼ«uÝD_K”ZﺯB8r‡šÚy]ÊT{NʵËË £J_¨=.)ºáü‡’ÀQ½fOk‹Gí ':.@»¶¶êRÚ6€D Û æPg:”¸ít#{uý<8\4ދ⋉Žékè)ÄÏÈk©¡›UÛ€A_ Ó&g…:%Q¢^cÓ—#ãHr‰-ŒÇ‰éçe¥½U¯’5ãHÍvÅo×y7Wÿl\r%ºõ9è:ómw:'Š£o™ØÌŸ€•jΪC|&‹”n‡úñBº¦ÚÊx`rocæ9Z÷š­´o’K\OùÆXÌk¿Ù¸…ëFtJ3©]Ò'6ýžä÷!âo fxV/ÛîôlÛ.ñïпHéÅ!í{Ýž„å¦é?Ò4ôièüK»¹†<ï ®Ýº{À—sµò›?ëúv÷ßnܺ„ÃÀ“â– úwéÐmxI3õA³,»Õbš(p4 °ÉÖõÒ[ ¨ù .zX3Ýk•ÔÄÞo¬–«UÞ¬Oèˆ$4×o…öÔË–©Þ0DB‚)ýtZârl ÝXYÁ²îu_Ÿ|…§ºv^÷2UŸÚYGôóéÂ?¶ã4Ëý`>÷CŒeTÑî¯>ºØh朑v[{%8˜u…³¾ëèGïáºèe†Ðvâ …Á³þq!šTº‡âÔ£<Ð çÓêQs¹Þ­ymq çÃH§}pjîçÁ¡Ã{·V­Öy{9G*×z”©£!å0C™·¡¯ÿÖ¦uâGD‰&’`vÏa2zÒ#ßÈ+Ý\ˆvW®ÜÚÊ“-÷øoÄÅŸáäÏXˆ+àÊ‹1AWÀtAêç /W{CŪ/G2ÆÑqMC‰©¨YXP%ÍÆäq'KöÖa([³^ñ Ö@­h»²AG]ÇävÌ¢/3üêÃo.Cy"ÀARi½6·+áßó6”of©Ýþ…þ"Ð+7š¦>ÅR”k>zj̘bÛ}Šï¾XA|M~.)òÇ>haË$ëØÛ*¢ë÷—nÄyç·À8ò$k'‡»²Ór>i¿eº Œù0±Ûœ~ѲwwŸœ éÆlnƒBãè¼½Šd¥&ΊV6’”àØ#Ž)ÉbÓPêªl)êa&¶4öî‹Û£ðœ™™þo”e$šË5¥J®(OO\)\ò (NþË™D}êÞ?šöšÚy]ÊTSž¬‹u¤ö Úì€2¡Vã"Ÿ:mIaÂpêqa°V:¥Y*uøu@'¡›Æ×Êô/D_|ïkУqÜz'êRlÉÜs0™{Êðë« û#ÁM¸n .kMý<8l¸ïVìÂÊõ&œax“û_ÁÐ>ÃøòÊú!òv.6-\Òõ Èï‹0&|ƒ>–jš†uJ†”!µòX,b㬙ªtúùÇè ¤ Z/«¾ÉgJqÊèÔÕh3¯Vù)ëèb½ÊÛ\"abË&HA¯P/lß®uÂæêîϦ{· ü¦f{ׄC3œ0$vÜâÏ>à´#û¯çƲph6d:Ípæ€>ù ‘wúºÙ~×ç²Ò×BìŽñ1¶ftNNGºˆˆö™c›RÃQV™Fø&ôÖD›­ZøQ+IótréZrËdO¾“1†J$ÜòÐÖ—¦oïÜÉí]Cz¡hÙn±èçPRl/KEGÓmOOOßP[û½Ó÷xw)Zî >ïoç‘¶@ é-ZjEgöë·9ý/V}¹±q6æüdF€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`˜ cB5ˆèÈŒ ƒ Ó8ν”TøŽ´h„_F€`š JˆÝRÉ (Ú—æúxzæ¸yM°˜ªH1cü>ßK-òýùw ©îJì/¥0…’›Àø7 Ñ= %Î,#À0Œ@½ãoÆß¼ «RBR¬/xº‡§Çs>ß­%õ"Ê‘"B &Œ?Å›y6r5•ÜSJù~¯jIÚûÓÇÛQn92#À0Œ@£D`ä„ íÍ"ó¥Ôõø‹ÅàZdx^VÆ'²@8ÓQgü)Y÷*Sºœ‡ÎéW—»3Œ#À05!@<„xIÅ-¯5e¿ˆ˜ñ#ý^t#_ÎaMptF€`š1/½š1 Rtw¤©ÐÝû A÷0³aF€`ê]éŽÈÄSØÄˆ?ÎíÓwøîýV“fF 9 ~B¼„?âãÊŽ†¨?ÆYdòŒ#À0Œ#-˜ñG I¦S/èæ®{²³»ŽNÏâ}½z!Øð‘†gd߆›:lø”9EF€ˆ‹ú£‘ ¦Ñü¸Ï÷d‡B½x<¾ïp=Žð´#RÒ³¶áêΟ…&gÍÌL±)¡âó)Íç“ø.IẗŒœ!Ð~žƒOœ~‹œœbÝ€DzxzÖtˆNçe{yEöªDW8:2"›`ö¼âßWÈ7ãtzüñÖ…záGB©ÛÀ˜žÕ¤ë·Gž¨¤ÌÄ»5%w65xòõ¬5wùß?êåRbHjFÖ¨¨Óe‚Œ#Ðdà“­Úø,ÝŽawþ{ÈÝné4-ëá%ŽœÒ×¹žv¼7 눌 G¦}¦OèH1 »%F¥g½3-Û»®IÆ…`˜"ÀŒ?¦ð2ñ`òÍ Ã°Ò?UÓ´Ôi™•˜~pPë=Å—›¤Œ-™’Ÿ i@oܱ bíYÝÜi“¢slÜŒ£@çKwËÑJ/yF 5 \±[_Jw绅ؤü%“!aŠpm¤’sÝ×½Ó|㟎.§!ÎiåNº‰g÷T¤çAF¾w!ì ߸Õv&S¼Ù a^€¯‰ µÝì'Dì?àƒU/ÃoúoæpSéãɯÔïÿ{ã~ÐÜ ñûéðÞÌ3‘×t|Ïòh¤¹qßíîÞ?Í绵ÄSÝÓãv§ûýÆ0]ˆésauáœîÃ3rN“¦™´S?0ùÙ-ÆM¬()Ùw(eŽ@žû#_K6ñÊœôlûðÊaÓà'#ÀÄõÇcNÁ‰€¡Ž“BŸqD¿œÎ¡ì>߬åß2W˜âF!åËš&o’šx æû³ß¬GÊN`J ½x¶j­.úèǧ`¦×)}óë`úŸaÂP*¥ö žÏƒùŸ¨ëú›´÷ cÑPìÑ ¿&æ ]ƒ{…&Æã6±cL¿ñó(ߤýa…Ù +í{ß÷ÚÀ Çíƒ Cº–Ö,ËWªg4©e)©M±Þñ'5=ëjø„<»P¾{‘™Oà<<_Ïë&LMhEñ”P€I_gÓ­î9"=ëz©ŒÏQþHëü¼˜t´ÓEÙÏ©9':ãa"ô2Í™ðÿUs‰v&”î§-g8²GZŽ`züÎ0±C€Wü±Ã–)‡@Ìy0˜ÎÊ«®ºÊá]Éiƒþçhb¢XîXþo„7ç'Cï¦zsÎÏÍJûÀŽFŸ„IŬ™ÙÞœ ·7± M‚òàM`ZÓr³ÒGÛî`T»M¡27ê“‚ÛŸmÀ€ÿ“›å}Ôvá›ðÿ2]/}nô ÛÌÌLû ZðÝ„©îNt'¼û¬ïáõv仞y&±tsÁdäï½™Y—V¸¿œêÍü“›ƒËgÇs>Ëô²–,¼†r^¯L5%Å÷Ä'y¾¶:ÃØö&ON.ØY:Y ùúÌ,o`’àó}ñÏ úœ9¦iNAº'¢`ÊĉmÕÿ8Ä}aï°iÜë{êB!&V{M4ʱ—ÛF Ö`¿Ú€azÐ)‡Ñï(Sþù~}g&%À(Û®yBèÂ,’=á¸#àAÝrÃ>‡¶‚^1A[ƒò YböÀc¹Ù@É‘|ËM´ËaÓå'#ÀÄ*+›Ø$ÃTr̺¼ v²\(ó±Tï¤~5â¢ä`[g3u†KɘxÊGBÃÿs§{´ìºa\è¤5Â7¹ dæ°Šÿ.à.åz¸µ¥3ú7Xpz „@¹NC™aTÖ'p{pí-ŽúÕp'HìS3Ç.Ö¤œˆ‚ј4 tÒjÑ9y!8z ¸ü NwË®ÌÀÐ×u}­ [™˜$HCøÍËœaËOA˜§;ÝD ÊQ‰>¿0Œ@TàTádbµ!—šêO}${„iª7•(3ï“…KþäVÚfÓ4úãø”ÿ\oÍÈ÷KÛv‰‡úEJ/þGËîu{–›¦ÿHÓЧaEº´›kÈóµ¥WWÐ%‘÷C©ÙÛ\îÏý†ÙÙÔK§@´­ ·+°/…û E1Œ‰(Ï$™ –¥òˆîCš`®{ÍYGôóéÂ?¶ãhÜýø&ÁýšÇcvÿ ߃›S½Ù™ØÌŸ€ò•iñ*˜m‘ÒÍã”ÐŽÒ55/s좽”³usõÏÎ×ÿ¸Ós@/ Ixöî»KqÂàVhÿ¿‚ô¶$Hõ†!Lé¿¹Û 7ú|W•Q*S³½kP7ÿÄ Œqˆ³!Ñåþ¨ÌP7èÙ÷û£3'±*‡3 ¶3Œ@ôàô°dJa";>ý³î„#üüî†ùnê?c‹9L𬤱¿,Ä>XØ2IÒê{ÐÛ~éFÃ4ߪõ'O²v2i£S¸h°K\šë"S™cKüeëpã1ÎPH<#Ï—¶ÔN+7sìÜ 0ylæl£Û¦Êny¤ ìpô´tpÊ0 L¨Õ¸È'°E#†kRݿˠløƒá7AAïEL I¥Uš@8iÖd·˜·’w€éï•ÇWDÀ7þ-€‘ðº¦Tɺ*[Šô®ÄäëŠÜÌô9éöðô Ùÿ?!™Zê/ËW¦>4÷`;àg8²Ç¢Áið;#ÀDŒQ‘ÜDö%QuƒYd”9vsAஜœÎªÌåyÎ÷p%Í{gùIļş}ÀiGö_–R 3r˜vÜÆw?n©{"/Ë i¹Tô!!=¡ÄõlZÚ–êHP¾6û³{$x’w[Gݪ XáŽ;zk¢ÍÖi¾Ñ{‚ƒ–¸¨°}»Ö ›'=üðî`ÿh¿ÎÉéÅ›%çª#`)­Úµ=«™'¸Ööf=.?²×›näÃå<{sʶz#@8¶n™¨4·»'ˆ¸¿9ï³?ÊÌmªÞ-ˆ#2•°ÆŒÑRsõ‚OsO*ÑHßâñS~\B“Ýp o¼å­‘Vqy¶ÛµM–.» Þœ+þæ€1·©Zni™_,]½1 õ5‰âÒ²‰ý¾2_|=ÿ~ìXhŒ–.Ù l bsOêXÅŒ·Š¢ü¸5![ãîý¨íï§Oýxë‹È¾_³yûn‘¿yGU]ݲBxº¤FÇoHŒBµ¹ìËŤMÕ ýø]]Y¼rƒxöߟ ¿®7X!(/#²_¶~cþþâÉ~TcßÃ]òV[‘2Äø¿üyiH?v¬?4¦hÂSšÓxRÀâ(& þñbˆÑ[bY%TÔ˜~´ ÷þ7¿ˆÎÚœaɧ£”…¯ÝImÍ~Â:&×o6x«&×mªjvãÏå˜Cz‰Ãï¿®Á¯Çrd?1ì¸bóöñ?LÚßùj¾y%©¨T5.—&žs­HðÄÓpV5ŸMÑ ~ÖÄekãI“©Âxë)Ôˆ\`C1cü –­_Í[*öÛ_|2÷7Ñ"1A =öqê1[•ºèÏõâ“ïë7otí ð]±*‹øé÷U"1Á#æ.XŽs¬'ˆ#ÿKÐùüÇßÅ®=Åâà^ûYaìÑYÔ–F™ßoÎþQüºl-)݉ã(®8ó8kU5ë“Å/¬n·KœŽïìbc³&TTçÔIéGx7uó6Õ”¤¶<ë“Äø‘— |Sx±¿âÌÁâÃbÐ!½Å%ÃŽ-Ñ~üm…x÷ë"sÔe$ôñ¨ìçß7\p’زc·˜ýÃbqÿ 状­“°_bõzoLj'•Mëäbÿ®í­ßê [Äw —[(?ß-X&N9úñ:òEý˜úÎc¹o‰+Ï,Ž:¸§ØST*^~çk±|ýfÑ«['Ñ¥ýÞ/v—ùukñËÒµ¢[IÈ7:¡xè¦óDRË”5–ý¯r›Æ›5¦4§ñ¤IT[< üöꌎ‰ÄŒñ—ùý‚Dß/Zi `½ºu´’R¿ÀBñÚGsEo¸ÝsíÙTzŠŽm[ãy€Hn™(Ž>¸—¸åâ“Å=:Y•ß¡M²øÛ°c­Á̯âýo~µÜkJƒ¼ýå<ñã╘p*®8ãXÑ÷€®V¼¯çÿi}eï–‹Nz´øïg?cÀ,°ü¢ñ§bÅowÒÞÑ §4eŒe›ŠÓ²G%[Ô–·íÚƒ¹x¹Pˆì¯~ø­8þðƒÄ%§#¾ùe™5Ñ¥ÄJÊt„ÝHíÍŠKÝ“Žì Õ)Þ˜ý“ع»«øùâÄ}C2}"ð×¶ñ+&ÀÏ]$~°úÊ!]ÊÏ’UÄk~'ÚKÐW:ÉP¾JÁÔÉ|öãb±“õK‡#úcBN}Ý6?c?oÉñÐ-ç‹›/bÅ»í’S¬ÉH¬ûŸ‡¦ô¬èW4¦úZS*_S-K¼­øíÆs¼/z´8ôÀî¢Vès±š å #û`¥»fã6 TýÄ™ÇäÃír‰m“…óB!šÙµ»È`ÞýúŽˆS]?`e4èÐÅù&8ÍoË×Y+˜®[ úíש­˜¿t8çÄ#œÁ"µÓdÏÆ™žMÝØemêål°ò… ðƒËûÆ· þK0‘>ሃjLŸú­üŸxåC0õ¢K‡Öâ¬ã«—fÑäœ$m…ÅVØÃÚ{´—&»`P€é;ûebéê –&Öd–¯ÝdIåȾ ýœ¾dÙ¶Uú9MR`$Ó@ýÏJ«‰ý±ûXsOšDÕÅã'P¤ñÐJŸL«äò#ƒ¥XII½|V4߉ñ3ßÂàÑG\þ‰–ˆßò úC ƒ´z0MSÐ>#I èg›Piè (²ç~ì`Ö׋ekÿ+ žüù÷Õ¿MXùDÙ4ÇNÚ m*Êõ·äH|n» ¬´m7ûIJwNC’­Czw³N ¤\6ÌÚ:pú;í§w¨¸tØ ALùOÿçÇâÉû¯ Ám”»ÓBý&î'ìpî¹_GHÒÖ[ï‡öéaI h»”uÛµN¶ ØÿùjB{LiBEjÚE‰GÆß ˆW§Ô»{'‘vÛÅ`¾+Å¿>˜+ÚÏI;ýØ*yZ³q«øè»EâžëζŽ/~ú[?Vš¶„Jƒöîé¾ë`¾KӭІ:é ª’^tb·…ü1•Æ€@¨vMùvA€Vß4¹¥vNz2N³+ø?ÖlÝ;··DøGÒÓÒqq† ¶“^ÍḀjίË*iö{@?”!=„.PÂݶsOÀ{;¶ls ÒüpÎK*pÐþûAä?Ðʃ Û±ïv.øÉì[âmŸ¢A"DRâÛSTbíïÓ~")ÿÀì×±­ %#Ò UÈö]E–{{„Ù¶sw@ù¨b+Ôò«îm1̇¤€ö*iÕ±J~dކbÒ‹Wˆ?pnšV ä굨q…à>×ê+ÞVü"£}³25LC|ðÍñÒÛ_ãø’ ûÿ]ÄÐ mÿÓ°ÿ¿¾÷>ñª¸ JAƒ¡TDŠC¤MœÔ"A\E¢×>œV•_uöñâ…ÿ}%ž~í+<íÙ¿§¥üÏ÷¿SàN+§– ;âŠÓEçöm¢f  ŒûüaFk´Áöi›jL¨}¿háV²ìñ·€= íÛÓéÚ{âyד(Ìû_ÿŠýúq4ïIcŸïÞœý³¥WC“ê`ó&ô£°‡T®¹Oý!CR3bð9/¾kéPÿú÷!i@I©.¦¼ú±ÕÇ4HÚH‡æâÓŽn¨þNO˜òXÎ1¥ñä½ç”*,"“âÍü’äee ¥g†dwt®§Í­zßзױcn<7rõJ—•h¸š‚öí†V3ÅX-$AÃß6´ú§ÕH}L)´ I? x@£½Qº…,¹e :iC— ,øýÏ%ÿzzÒ= ø~Ûð#‚büê¶´C„Fbâ¦M5¼¢’MZÝKü î?Q!^"Å%e•úõ뇦Ìמw‚¥¿C“ë¯æ/µ&*S¸!Ð×bÑÿêíF”Æ”EKW.|å© 7 ÓQO¢ÈO Žû"£ñ¶âßTIÓã 1a'Ó§ˆõeúåqC§Cf+(M±a#´W&xBMФ@ʵ;p¤¶#â^€ý»t0}Ê7÷¿x¨=ÎC,Íyb™"ÓfF` @“ôÛ/=U|¿pîî_*Z$$XûügŸXý±Â}MN’ˆ9Ìøc1'À0ñ‚Àç£F 9#Py»9#ÁegF€`šÌø›A%sF€`fü6üd}Š)Ûžð NΔíÓ|pâŒ@SG€S¯a.#ÐH ãutqF€ˆ-¬Ü[|™:#À!@çüçB³~áŸkÅ`|å>¢sŽC³þ·ëÅO‹Wá*ìnbÈQ[W\Ó•ØÝqìn)n»,,.×wbU~epà¸Hq8F€ˆ ÎY(>ûa1¾è×WüŠ«uéƒ9Ÿ Û6ó–¬'à“½ß.X.Þýj¾å¼zÃV1ó¿_Š‚=Åâ(\m͆`ê¯øëÇdz °×çžvì!ø4uoAÅ"Fï4çŸ|$¾^ÙÑú&ÆlL.¯ð¤«}oÄuÙlF 2xÅ~›`êˆ@Aa± Ïù’i›Ü²Ò­yäFëi™è±nÙ³^ð§W·ÊŸ²¶ÝùÉ0uC€ÝðâÐŒ#!ôuʸ*—îÄŸÿÇë‹~N’󖬲>í;_æ£}~6Œ#]˜ñGO¦Æ0µ @_í+ØS$Æ<ùbÁk+…¦oTЇ¯Æ=÷¦Ø‰ûôÏ<ž¯Ó­¿0Q@€÷ø£"“`ðèÑ¥½¸ï†s}-OâËWö?}~ú‰û®±]„ÏäºðÉ\ÛÜzÉ©¶•ŸŒ#!{{V„„8:#À0uA º¯` 'Ó¯ MË0µ#ÀŒ¿vŒ8#ÀÄbðç Pö‹Q2L–`°¨ß[F a Qÿ%CiØD95F ™#À+þfÞ¸øŒ#À0Í ^ñÕ7ÝN?Û8í¶[]Ÿ´ª± Ùï¶;?›6ÑnWÎ6Äm*~ÛN´ëJÊu¿õÝXrÖì¿Ý1M|Ä4•X»q›X‚ûÀ·Š]…E8Z¤ ±wP÷z­àù‰ n\V’$:´MúôÀe$…†ýM´ëicˆa·«5¸jvñÊ|±}W¡Ø‰#le8ªhNKKÔÚáCÆ~:hÌ}®wOœ5ÊöxC Ù2~ê Ô9é‘RÜþåü¥â«_þ{p«˜ÀÀªË¢Dk! áŠJ¹D±haînU"ÞÇ]å­pcÙéƒC*ÝI@Tc"û jWÄÜ?ÿi©øüç%bwŒÚ°1µ)»¿Ñ³°¨L,]½A¬Þ¸Õº£¿¸´L”TÜÕß¾u’hß¶•èØ&Y€k{÷ïÚÞšÓ$9ž]>Oâ¹–8o6Í’ñÛ ßï÷[«±Y³çYôOW±)i€Ø†§.=6FQ}º•_tôo]ËÖ‹w¾þŽeâÚ³‡÷=@¸\š5ˆj‚L¬Á võÛòõâ_~o]PÓí‰ Ïmª|å[>ÁþvÁŸâûE+ÅÚMÛi©/„æ~™ Ê„GÒù¶‰j—ð%–ÊÖ:©¥8éȃÄÐcm[Ñõ¾ñ·UÆã ՛Ƅ@³büö¬\× ¬ÊÊÄø*Øß-E®ÖbY«cÄ.w§˜×M(6%ìoýÚê[EÿâE"ï_‹‹N(Î|˜p»]¼úy-D7»]Íþq±xë‹_D¡Õž†4H{¢’Äc›²1¡ðæí»Ä oÏ·îEîvbSâ!b§§“Ø­µÊ¡ÿb× MZE¢ƒ¾Itòÿ%>þþ7ñÕü?Åç/öë‰þ±Wg†èÓ6J¤ ·Hném°¥fOè+c—‘Ç“X!Ìtc…@³aüv'õûuìÛ—Aû;”%b«§‡X’t´0etDúu©(šhÌkuª8´è¬þX{¿g7@x0€Ùûÿu¡Ça»]ÑWäþ÷Åü}Úž¨ôûªMdèYމÂ}ûº ò<7ë ±½âý¤ãÄ–„îV¸šþ(¬÷‹]É"ßÕGä'öÉÆnqhñ|ñüÛ߈‘— co;þ׊E+6ˆ¿0™°Ó¶i&·l!úô舯ÿ(ŽêßS$@¿&Ú[åe4'6êülL4ÆOâ8ê¤%¥¥bá²µâã–Zƒôâäc÷i}Ñ„ƒòpX¡ï~³Ptn×ÊZÕów¹~2²OÁh„‰S»Zôç:ñÖ—¿ÄE{"¢Míeð&îÔ/+Öm²öìwî)¶>ùÓø¶ïÚ¥ÆB±0ùLJê÷u=’ üšå‰òö-4ŸI:A{¤”6ñ‡€Ý®èTF¼¶'B-mªœÙÖVÙŽ‚Ýâ™×?Ó_)Ö·è+¾k}–XÙr€Øãj[…éSú¤µ¯ ˆ†!:á2}gz;ÜÅ­O;d;ñ÷¿³>L@*W]]ïÔ?y<©+z>^ˆNŒ—Òå#0;DZ½’’R±* öøËÄ&ÏþA!ãç•òV‰Äjäµ ù¦V}¨ø)QÓˉݮ¨=•Äu{"ô#mSåÌΰVú¹ÿùZäoÙ%~k5X¬hq˜0£tÏE¬[ m ,H:QhmÅKÐظe‡µýW—¾e×;õKOb]cL?–4ÆO+¿%æ_š.ç¡súñj¬¼!KWÿeåÛ0ê·2‰×ò5…|•3úá1ÞÛáI›²™>)Å~0g˜þ±8éX± ûíÍ´`QÒñ¢ÔÔ —ñƒ%Q£þ®±ëÇ“pãpñŠ@“fü4hQǦ›ÔhàÚEÇ"1f—óD£’éL6ݸcw¡•oÊ?•ƒMü `·«mÐX§ºŠÕeOÑ*q}Û1:ûŒú:\e=gá ±1¡7N/t‹VÖœN™–hI*Vã*åek6Zµpû—]ï<ž4xµq‚QF ‰3~ \Œ¿"þBº×ðÆ»)ÁàT2 0”ú†›øA€êƒêeΨ7†öDÈÕ§MѤ“ôLhÒüÕü?,±þª–‡ÆOEÔ3't–©¹Å‹WYRµð?'õ„œ£ÅM–ñÓjÅÍÑàEZ¸~?$QÒ0Že=Â-üeåƒ. ¾vYb™&Ó».Ê%IzÔ¾å^êõU×6UÞwpÛd>-^½ Úû½,e½úç">b’^ÂÎÿgï;Ü,ŽýG]:]¿s?ûÜ{Á…njèHHH$$$ð H#ÿ÷’ÇËã¥B „t ŒÁtƒ{ï½wŸí³¯K:µÿüö»ÕédNõôIÚµ¿û>}ewfvvfgvv×TEê⎣ ¯w%OôQ Šä)·Š$‘®9Írá±¼$¢x“'mj_j‚×/†*âµHR+Q}/$_¡Žr)%ÂSÀ ž²m{ëD[Êed=µs°_3ÑbQ/àO“õ®äI$5Õï\£@Þ*~ÙC‡lFd|–sÕû+Ü€_â’Càç-¨².„ÎÏ5ÅŸOÉá xÊöÕÕ‹©zÉ®¼§Gf°ò ^ÊzPè¨×X)¼Þ•<‰E)õ,(·ŠÄ5Vxñ4n]U>þ±@êI(é îFÖIlU¡CBÄÉSÀO³j5‹¿©•-c^‘/]óñ³MïYÁ«úõ¯pj5ÑÆz†ª³Þ•<é™Zê =S ¯?j¬=ôèõVIP*v½Á¦àÑê&×è/OïÐQFD¿¶ ™æÈ|ýxêd¨{+‚~SSÅm,±º”mç\JñÖ}.á¤`Mžy¯ø“'Mô/-&Y¶ «Èb ~¬’¢@BˆÅSNæ©ÞNRñ «ßÏ*ŸùÝ̪?S©7qìß¾j<;ixߪ.+âe± b ßLáO¾Ý¥[÷ϺûFÝWè‰y«øÓÑ#¯phÅ7ÊiB_3­6Ñ3³x—°ÛËiñ×Ëè¡ËœÄ[‹tÕ(+½{s)­üf9-¹­œ»ÒIhÌÕEFZõÍ YßÚâ±*+øÄÊ_=‹©ÖÃ5£m4÷Ëe¼‰ Åä©YãlôÑWÊhóÔ{7—ÑWOÓ6¤é-žž8`ùñX¸ÅïâïRWþáøw‡£¬‰>N^hçs%4ÿÖ2y‹no¥×n,źI §Až]4†·À.±›iJm5™Í&±†‘7Ãâÿ1Sªõ.3üªbº÷ì"*¶è'ç ù€:†l9­_çž ³Ð¢¯•Ó2–'oßTF#:dH*øKpN>áyªëÜ¡@Þ*~T^Ú9ñJ¹cºƒVöÑÆ£>Qa¢íõ~ºæ¹&ºýͺx¨• ÄÜAúÛ*Í|ª‘n›ÝLç¶ÒÆÛèx[€^Þì¦{Ïr$^8R¨2BY/‰fOÑwϲӟV¸K3Äâ©iLôÇnºøôÊf+ UqG²7y x"õ)µ±¢ ònwGE¹Ëû‘øw‡#>‚|ù¶Ê‹ºjäW·´S)[Á×ŽéØ‚¯K ±køf·ÖìÕ±õu¼»õÉz×αˊötZ3]c¡§Ö¸¸3e šR#Ý1§™®|¦‘¼<éè;rÂf6Ѓ—8iÙA}î…&jðè‰,S¿£3'ë5Œê^aP ³‹YøÆ%\l_œd¥ÛÞhßÌÙÞN8dZÇéÍôÂF-ÚÏûƒw¤ú6¢cm~¶*4õÄj°ÜFUiù^Oç­Ç}tw2Llõm¼Ñ©…2¦ÒO;“¨æÌq£ýåw{*;Ÿ/d¤6ýõñ“©¡ÇpÀ릀§•¦ñ40£µÃíG®W¶?³·j|‹§þëCf¤Žô<óØwÎtД~&úpw€’å)dÇ\èæµQ3+A“ÕGc§7ÊÆÒà'ç;hP1¯lݱ%t h Ww˜¨„—¹âÞNÇÍŲ×¶DΑøÇÂq4{ØžZã–ý­S;·ßõ1üO¯õÐ7§Ûéõ:Æ Ã!Û0:fHƒÝ;)xt=6{µô?LìÙ3Y¼TUl¢__êì2”w )@?û¸\¼®†¿ƒ]­äiáß .âe€ˆ;æW·xè¤KŒ¸ B~üîÎvºo¦f œUcæ·‰ŽÞËÜôÏÏÓ #l$¿,KRÑ$yM½áF!6;u-ïR›‘Ƴ`ZÃÞ™jJMtÙ =À=s?„åfM¸i ÀØ*ÕÇ’´*Ô3”ÙVöù5Cº ¢ñ”|á¬Aò²‹`ÃQ3ÍSPúRñ›ÍfÑ98î±P‰¿†z¶H°>ÇÂ?Gt¨ŸZã&öÆŸ’6óÑ0öÀY“¨Áf=ØMpCñ™d ²åݰ ®5>N)&í7ÆV›iËñSå :“ëxõíY?îì·3“À»ƒ´¯I“3Xñ#¥‚¿È@ý)x (mÔ -3щ¶ µ´Ÿ*¸¨ˆêZôÒ&M¹#‹ëØõxÇ »+|x‘‹ê]Z£Å³½ RÞÕe‰û*†1Oía^ˆ–¢ñÞë_l¤³%ø${ŽŽ2ÏÉ”,O™Xwü÷…ETZÂ1*»Ø—^æ)ÏpÃŽ1p;ïß=³”ŽóÒŠ¼ÉUëV²x§KVž‰îQÐþÝá(á‰<ïiô OÀ2#í8]‘F~ùû„¹°Ž ×.úŸ ˜ÎÕEd±p''b°îø¿\S,¦ý¹\f^Å0Hõ'x‡Ï#z­‘öv:û"‹èòîû~ņ¨õÿiŽºx¸™nzEó.–ò0@[X¾.¯&;Ê:ýÒàÔ‚£€RüÝTù¸‡ÙZLß?‡]®ýMtãKÍ “Ï_¬tÓØ>&úóÕÅT[n¢ÿž×*#Ÿœ_"i [?¹¸”ª*ËXHw+¤ãÍóá§çÒÚMÛ7?óèƒßåoŽðQÏ;¿ÉÅGrÒ“?Ôy‚½ÿié­?ø¯×ǬqïÍW¤2"Ý].7+3=ñ†™65ÄŸxjU˜—H~ÙO9YüéÓÅÂ|t ª©3%ÃS_÷|…qoXû6›Uð^‘ÃA–ÐæC¬ü÷Ðï>Úæ˜BG¬CzάãhøÇ±»Œ¶`… "ä—¬âGÞ‡mCE„ÿƨº¼„;;ƨ¡îàHäþVúH‘2eú3ýß§Šèÿ½ßFëë4˾ÑäààÎÜåu“G»—.ü;KPW…FÄ´QQ=zèáéëSíô9޶¾mvË) ïÁ‹Æ;ŸÇüÏÜÙ§B>u­§zÂóV×ùOðT_g|<7öŸ¸y‚ǃ¿ÿ^ë)žèLò”ææçi«BñÛÈa·“£ÈAE|ÔV8ÈÁÝ)C0@Ö`§Ç+žÚ‹Ä¿'»ËÑþ b¸¤»wcÝo3ˆ`:λ,z9^+òe*ðíhGûïVÿcxèç\Ç-vÑ›añCˆ+mú2žHµìÙ@’†tá/2U ’JñwSípÉbê,¤ë9Jÿ{lí?¾ÜMÅÝußï˜n…Þ:¢µÿÀ$Œ_b=€ÍÝõˆ—ÔŸ‚ :ƒWtˆEžà×O]öt]ÈG¤'®e«´#ÍÝá¥{Þiéž‚â‡ë[Zýv›Ö8AM./mtžAÉlØŽÿÀSL%ÎÑÎè¡áñ¥æE³qpŸã°NXŸgld2!°o[ùHWp 0¦g^:G§_ÿž¹­4—#üï}·EÌ0¸yŠö³ÂÿÎÛÚ°!¾MþÈK¥Â¤€RüÝÔ{Ï}z‡¾1ÍΊ¿…fñ|ÚîÒO?j£ŸòCXü‘Á€øþý]¼ÃGs«TØxs[;ý€~–‡‹^Úä‰ÉSèPv—z‹§dd?:u'[hÇ¡“´Ç>&)¥\"ñ…£Äq38dÂ*‡_a ø×‹:§;Êg‰ž+½u⓾•kö³âÏ”«=Îë7üýÚR±>Ã_Ø“ƒ£»´”óÿÞxŠLI'þÝ•­îç?:}‡ùkÂþ‘­öAÜíåExÂæjÉqž{‹§`ùBáÃŽñï½uTgJCj¶Bªø¿Á^©¦2Þ¯ÌWOc†T‡V#r:_ªåD~ï`*)]ø§ƒú6÷)Z+Ö9þ²!kg¼pø£¥b%ì8ÇJPü˜ºèõy©¾±Y\·;ã b}«÷gFž‘0ÒµžãL4jP5oB¤Å3 ®!V §]ä‹•MÖž…ß5 TÁº @lN׈ÉM°¡]›Ò°ÑHrÅÿ6C±ð¸jxІOøsuÝ;¯(Sw­K'F‰òTHñ{yvJGЛ%Áé{é„?yt­£bM\NEY­V±fxý†—í¾’'áR×¹D®Ú%— OV; j[@ÿãí€Ñn/® 2¨OÒDŒÛy»\H‰ò”¦ø570àç±p Uúxå¾OØ–w@û^êWj¥aýËÉn‡â·ðÒÀæË?^sEžXXž„ïÉ/~ê½ü¥@Þ+~ôÔåa7óô$òðö¢É±fš›™atÚ,!¸3]¦Ê?q €§J‹lÌK¼ž¼Žù ˜%ÃSìéçEƒ´my1Î?¼“ÇÄó}Ç'–N¾¨ð¥Q®µ¼6¯‹0´Z[”ˆW$Äê„X®×ˆ½’{HR–àœ+òÄäÄi-n•òZñ‡)ÇPÃWUÌ[i±@«ê˜Æ£Çê°1Œ+‹êÎN‹a-T˜$_Õö¯Ô=?¡ŽRá)©(jû”Ýb¢ñ­ËÙkÖuùà\àƒžÝ4¹m199œyư */-¦âbm¿¸úåö¼±p‘õŽv™Sò„‘:è±ñâmôÔZöx*嫚 âYÞ*þP#å.æ!yÓ‘r'\z¼YOãÑkl;®*+p£J\ô s!Á%ëõ2 ª„lì&Ö3?¡n’á)àgä?PpجÇf±ÐäNÞ…ÖKÓZ>;õåB½;M4¹e[úë›ßœ^[FU¼.1ïQìtj®~Æ 2uÛ] ¯÷\’'}YžxxKå&¿‚¼óS«Ûé®·ZyÇÐî0U÷ y«øQyZèÉËÆ°éȧ‰*Øâ‡ÛRo 06¸U!l%ìÀC%ýP œ¯&©Ò-?bÉòð´°R´±Elã±ðŠÞ¨§¯•Šx(ê4Vþµî­dLr'{ c´tR­YDécÜ®|(úRß äÙE“[ÓŒ¦¨ÒŒ×»7Ñ´!åTYQJ¥¥%TÂÖ>ö°Ûx¸†åBsAž`‘¢1ýœ4±?¯©˜\§Kúé³¼ Ùê#ËŠ§ZêûÜ¢@ÞjÙC×”¾YDî"ˆ§¦ÂNfËÕ¶žŒAý¬¦XFñ#+Ç!ŒPŠ4ü—Üb­ü„VÖ…ä«ÉÈ­kGsÝ鉟@ýTx xB±¡ÍØy“§³HXÈÕeNšÀ Z•™ý4Ô½…ÎlzŸ{vðÞöñ9â=|wFÓ4©e‰PÔ©rÊÀöÝ4ºmMHÑOm™/¦ëõñ¥~EDÓúÛiò•——RY¥¥TÒ±ã%:6=Yû€/²Þ1<+òdÏ^¸ÿSåtóiÅìÅá a®[ltó+Íšë?Õ Pßçòzë˜Yq¢"€–‹ƒ-—ÚÒ6ÚÙÐDãÚVñºã§ë¢Ò‹“§¯)0^À øã :ÒD8_q€ß#ûÒ'›1?­f~š¡*¤ÂSšu«­ÕïpØY鉅|üáˆÿѦ:Ñâ¡#­mdum¤á®MÔd® s5µ˜J©Ý`'ŸÁ"<˜Þ.÷ÖS…¿Ž×Ç'*2úÈ4Óˆ¶ ´Þy6c¸Ùc‹ð soæƒTeñr>l‰ópøŠx&,ztZJŠ‹©„ÇõË`í³Ò‡µyË=k0„×{.Ê“oœQL猱ÓOø¨ûiØ/âÁ.ZuÈO^Âñ[Ä"¹z–ЛâgVäͲ‚V·—·³â¾i*4–®9aµ°°³ƒë[ê Öö¢¶C4ƒ•6Mãɲ3}Vtµ÷ ­°Ò êR'à†e¯`ŠE+w»7èóù•%hvŽõY>¦çYYé×–x³ÄÅ˜ÄÆBbˆ‚;ÏhïEEì­`×>Üû8K¿É¿Ó3²ÞsQžÌ¬µÑ+‹y 6ZÃ[ˆ#a/‘[^ ð6ÐN±qPT‚G¹ ™ÂAì.S¢¼©nézRü!yðú64Š ¬SRüš€bAÀ =ÛC‡jxE² ¢“݇8X©™¶;&S#[+½™0þŠ!ŒKæ1È‘ýK…e8¯æ†Ô\ý©ÂÕÐÔl÷x¢ä¢{”g¹~+„[ºx ‰ÆW“†ö%îXÕ¤é-M´- üØÒÉSPt"°/h¥ S#% s©\mÜ9mks±Uíár½ìð‘ûa}@íÜIÀ^wP«°À<#‹RšÈAÂîó“©ÍOF× :½ùCj2²ÇÀÒGx Úy_?Ol ðIˆ#@L€#Ð*<UÞ#â7„Cï|YËfâM˳ˆ³Õ³âw“•Q‘aµ‹÷¤¥ºŒ7E«÷\”'ýíFzzV =Äýc­6D³á¨n|¹™þrMI—íŸcÑ2ÅÛÞ~"âP›‹¸¯~êˆzRü!²x=žCÍ.¡•t»æ“MZC5’UX-61Féñ´³`ò²öÓ RÖ&7r5s°ÒB:iîGuÖª·ônÊdËõæTcz"­ȇxƒ¡FªaK.H'¬’¢c•%ŸŽ §»­5÷W`‘H%xNO¡ØîøjBmžÛÍ[0iÑø‰ù¨Î’Y~<™ä)©üY] ¼µßÚØ?þŠx¼Ëíi¡ø¹óƒyÿPìH •p‘s¼: ð Á{ÐÞÞÎÞ„vªpµÓñ¶{ NR©ç$ qoïDýÃzÚÉ>6+ª.¶R1+sXð!+ž½drüí,Ò#]óZgØQüÑÊî®ÞsQž0›Ò}3‹hD¥™îŸ×J\„-Å¿ÈÊÿlùOë[5H™ÒîjKO„f4‚«{£@ìÚÍX±13Ù¿waiUÕw×ï8HgNóåžJA…ñ^¦§@¤žøÊÊÁeƒ[]t²ÅM']>òpx…7è’ËÞ§…νÅSáÊŠŠü + Wxф҆'­Óâ׿f]CéŠis<¥ ÷ÑAÀw*pqçüFǼ”=P¤X9ÉÈßhåbè•>— +"î$Ã¥{RákïÈ)±>pI%õTïÀ3×äÉùµzú3%tÇœVáyi稿»ç¶ÐØ#ð•)Ñ=®)Á@ aåü·2= Iž¤Â>ºùVOŠ?œyüÇü÷ƒáÎíûŽÐ¨!ýS"˜ÖKçEH¢ŒUJw &xxh•G.Ë}ÈQ0U²I X>Bxñ"Âba$„¢¥áÞ‡¥/\ý!k?=A} ß† ûvl}qÀüEy„Ó;Yôôþ]8Žiå) _9nªbËnpDħƒ¯²ÅSÀeK×=,iŸwðshCgšÒŽZ h$ßGgßã@{BŠ´=ƒ†Š¿£Ýá ˆ¬yît íˆƒ§†”>Ã#:¢mÕK 3`IWBžPðù$O&ô5Óó×—Ð7g·Ð®“l1±~¹  ¡ Ýu†½ é¤L9¸g×l~PHò¤ rù‡ž?èkT0Òǯ½üâõÿñí/<7wIÅ}·^c´rpP*  IFZIJûcKÁÍ‚®ÇðqÊpá“JÙ²</é~DÔ>¢‚áÖǘ> ¸ø'àM5µ{ýÄô |Þæ…oÍ~ŸóC/ÙXµñŽT Ò÷÷ã) -¾ÊO¡\©ÄEd¿…£ùÖÐð™–:+lÙYÆûVÑaÐ:x†¶åuøÈ᱓§¨;tËcþ¢ƒÔ1'óqkŠ_XýãölýK÷¾ì ¼L§lÕ;ðôdz¤[ž *1Ò³³ŠÅÊ~+CLý~¹‹7ø Ò3â·”)A¿¿qþì×Þä›…&Orýž¿´Î ”| Ç[6­\ö[Ãégý쟳o›uAJ­Y6(U)ˆ4Ë[«„Ò×\–í"ðcáŠ¿ÃøH¨¾¥ü‘ecÙ`/é&…ò—Ö >îã9„ ¾I5n‡Ž5ÐÊùóþÕÖÖ‚©|í‡l¬’æ©¥×ï%~á) -ë¶·øJ²…,··yJV4ÊG€ÿw(z³8ÃÚG’J×xpkgCè= ·1(m¸è½^ž÷Ïãûr¨mO&‰«Tì µè@wXøZç@~“鳄©·êøôFÝ—qÄÿ“×Ó·xYßùû´ÍÌ[êË7ß1ÝNR¦¬Y<ÿo--<‡U“)…"O2ÍV½–¿ž?FkAAy½;gIieÅÓ|ýz…‚·\3ÓŠåÆ !!-®«–‚‹¤»RD&c”1Ìza8N(K¡$„æèCh@ÙËà#<‡ÒO5¡WŽºbónÃŽ kg¯YôÉzÎóväú‚ÎÒ•äiÊ(Of½ÍWÙà©X¼!áÁ;èÄ“ŒlEj+·A¿…üÜDÐm‡7 ¼Ý!´ m¬¿«ÇAv’ñNo'”™òÄÊÓ/¿ÊIw²ò_Сü]⢕wMûwvoÙðÒŠ?XÉô.DyÒÛl–‘òô¨øaA1a ×ÜçŸyå²¾„ðÒן ÞxùYÆtŒùk¤cYRVþB᳕á#¥Õn½0 %)4¥Í 2tÄu‡Â—ï&T@Ä˃{–þö kÞœ÷úËð+‚–|†ÕkÐt.ÅŸqžbZ†<5r Öl&øJòIoñpKw8€ƒ‰§çy?ÍÃ&=šçåÊw¡Û;¯µïÓ W2ùÉÎG¦ë]ÒçL×½E*ø[¸_³ü4÷7”¸úoXùòOÞb K Qž€ü9Ÿô¦øÑÚ!¤Ái`*¸’ìï¾øìkÓλðhàœóoýÍÓsË&ެ ž>a¸aÒÈAIÏó‡ÒEÒÆ!x´±GimH…/Ï KÂ å … ,”)ƒýäý„3ûsj1½‘¶äóû¼-Ëç½ÿÒú¥‹6ðk’޼T¡ '~ƒ¾ s§tåyšz§@¿Þà«Þà©ÞäÙÐ.r5õF½ƒ6½U÷R¦Lrí nòÕNšûˆªipæÄÁ·ýá¢ý»ó|ò¹Ð䉠C®ÿÑ«â—?”æ“XWÍŸ·lËÊ;κüªËü~ß…¬àJ¹K¶`y™Ó`·XÒâë‹Tò‘¿“©pÙXå·‘¿åýDÏXÖ+bq†S(ü]›7-Xòî[ Ýî¶6ÎhØÜqÆu¸Å_HŠ?k<Å4ï2æí7î%’"y(òw"y©w3GHùù;™’#ë:òw2yÊo¢ÉÇŽwõ¥‘æªÁ£ðžcìy7×|íñÍž¼ëEþYhòD’*§ÏzSü ¦“Åøz”pócäÐÈjôá«/¼É׌štÚ°ÚQcÆ9ŠK*lG©Ùl‰>á”_Î×äóñztm®æ¶–¦†½Û¶nÛ±qí~ÆUºµÑ‡¢oì8šø z‚®…2¾Ï¨Š¤xJRBbP@ÊWKóɽ۷lݾaí>~½ÝR<×7äÞ¾e*é3Ž{±†¢qçýwÍí[sàÏ·-äç…&ObP07éQñK×,,4øþ ô¥5/¸wûú5[ùØÅÏ€ÞÃ!ßãË‚I è¢ _£%KŠkôã9¾_Ö>£+’â)I“] @IDAT uVˆM¨òÄÛRßzèï÷ü´æŽ'6Xlƒ9 »säO¾ïÍs·ýòÓ{øw!É“Ø̧zTü ›Tb°N¥2Þ¸‰¥°òC¾†òG’ßh¿òó¯lhRéËØÐFZû°øaéC骵Ϩ‹¤xJRBN¥@òĵo]KãÂg¾Qqáןã—+øèOÕµ¯Lx|ãÌwM€¬Q)G( WÅòAPká¤ZoRþ†bƒE Å%¥ ø‡às!(}FS$4VÐJî6Ð ´â‡¢Gc”cý žãýBM’‡€¿¤h¢xªP9BáNÙ&º•'Gç<º¿xò%_¶TÕ¾Æ3/-ìöŸâsž½?üÌýÞkY¥œ €ž¿dBæðki(3(}øÇçhŠ?Ÿ; ’¤‹l¨pãK%åN~㾇“ßò­‚K’^Ч ®êÂ1( e‚l1åÉ®_\óÁ˜_¯»ó{yrÀâ5Ï=´ág|ù_ø­’þ) gÅêÁ„á )-4XúV>¤Ò—±Pøù¬ô=‘@y€Fá4‚²Çž©Þ8“â)ꯢ@8¤,‘í#¦<ÙúƒÉOþÍúQ¼óÙ‘ ôãÑmœ»íû„gª®õI½+~I5(-0$ΰ\¡Ô¾Tú…:ÆÞXeƒÅY*|¶ÿ…Ñm8=¿±Î]ì”âÏݺS+ ( ( è†Û¾7á¸z]Äëù(ø n€S€t¡€Rü]È¡~( ( ( ( $M‹õG¬ü1C.ÿ ÇýzýÕIç¥>Ì”âÏiUÆŠŠŠ…E­wÝJÃ_$Ö~CðW7¼Ä:+*éˆJñë¨2(ŠŠŠ¹N"«ý~ôÓñ Ò„u{7|5×qÊ7ø•âÏ·Uø( ( ( d‘«¿3êðÿR‚4Ð}ìö/„EÕ$ʺ?+ůû*R* ( ( ä¬Á>²Ë[‚c°䨇7^(®Õ]P@)~]TƒBQ@Q@Q (°îý[ Aú—Ä( |C^«sö) öë@A ( ( (w0›¡ ?^ÏoÖØßo®Ê;$s!¥øs´âØŠŠŠz¦ÀÆ{&¬awÿ2c0h zü7ëÞB‚M)þBªm…«¢€¢€¢@/R€Ýý!«Ÿü”»¿i«(¥øcQG=SPPPHš6êóoÞk˜7ìçCvò ÷‘ÂG{1]xágŠjgL¼Ëî°Kµ©©šŸ‡·)^eïghS>¯ÿÓ‰àcùËý.F€„“ßoòR‡âRPÉê„)˜ÞrEñ¿ö£Ÿ^n´Zþ Ž>(xú„á4iä C‘ÝfJ/ITn9D!@ÚÜZ¿ã -߸«ß†3sÆ]&ޏó¹ßÿæ]Æ…N¨C¨e T¡ð9wË¿õ½ËJË+þ¨6•1zçZÆÑÚÔ/ýŒßa|Û“¿úÙ;ŒP‡Ó>~Ô¬¦ö>íšÁOìÕó‰ÿsõfš) wÅ/,“ÛþßÿÜm0Ч,ø…ËΤQCúã¾JŠ‚Üù£3'Çaؾï=ÿÎÒ¾¬Ì^¾ùÞû~úôÿü¿ÔÎÄN![ÿÒÊG›·2m¾cw86°o¹jSL•ºR J›êwðèÉ7Yÿðo?ÿïGøí„Ú[üЧiþ Á Wr÷ú/=»\„ úúᅦ•þC3Æ 3üð+Ÿ6±Òïu"©s‡à}õÓ&ð +¶núîîaèí|Xø¿b§Q*}ÐÀš€6ªM15Tê‘ám ²2™?J¨- ðHþ=ª^È(ô¬ø_ùþÿ»Âh4ýŠTð¶Y¬åÕÏ(7äIæàð ø¦¨¸ô¿?sëíW1jáÊ?O0 ´s¡ôA ÐDµ©¸i§^d „·)ÈdÈf¾·þàxâçÝúö)¢f—qW\/ƒi¼òÊ›œv{Ñ_kúUo¹ff!Zi½Lòü+|3°O9õXóÛ!CFW2†6>àêÖ+ßg¢€+p¶ …jS™ saä‰65¨oE²2š±Ž¯-:?/ß»·0¨¥_,ã«´Þ…JÞÔw°o#èˆÇôMÊÒïÝ È—ÒÀ7_¼â,#‡!÷;몫ng¼|Xù€ë¨:“¢-uàì @ Õ¦˜"*%E´©/?ÓÙ Í™ÄÕ–‚ÃÔÎ [:¯ÕU6( GÅ/,›Ýþí‰#k‚jL?l‘?e‚ÀGeU•_c¬`¡’Õ/ÚRÎNÐ@µ©üáíla"Ûd4ÃУÝü<óšÂvä ,Èìª\zSüÂB¹áß>ƒ™¥OÙ+«Lñb†) ñ‘¡ú¼+®=‹ÂX!XýáÖ¾]ÃÝP­ÚT†™­@²AFCV3Ê1­þ‰¿Ù4Žß­î ͱmߟ¬,þ,ó‰.QIÉ¥Xœ‡çég™<ªø| øüÔgPÍ…ŒÜýt‹)¬øy®'©ø«¸«6•ëUªøe›‚¬f¨b¶%Îï„Ü ¬ýNbdíJoŠð˜Èh¬ÅŠ|˜Kª’¢@ª•8lA‹Õ†žd¸Å¯7þOÕðïµ¶¤y7ìÀ4Pm*œDê:Y ˆ6Å2²šó€âï¶-ñJ}!ÅÏˬ’l™ê»ôQ ÛÊJ_ åxÌÌx^½Á–"êe}Q ¼Ìi0YÌ}*ô&áêïqlR_$ hK¸Ú€;hp.êEn( YÍ»mK÷ƒFÞ’÷"™…!˜/¯Õ9{Гr…PîI£ÁPÌkï÷šÂ’¯¼NyÒµÐîÅ’ð¹‘VnÞC+6íN°[÷!— ãš6í:HŸ¬Úzêƒ,Ü?™Œ&l"•¾tOöŸõ"Ú¡¶ÄeB([{¢mÊëó‘§+'—×íW±x¾7FÝlÙs¸Û"?X¶‰vì¯ëöyªÀOÕœO·méßm¸’-~mÕ5ƒ¡nÒIkR-W}Ÿ: ô”Ð1ñ6ÆPôo/ZG-ßL-mn2™Œ4rp?ºéʳ©oe)½ùÉš·r3ýúî1>¢Ñ{júVÒW®™Iÿzk1­Ý¾Ÿ¿wÑÀ>tõÌÓhÚ8x¾´ôæü5"ù[ží6 =òý›äÏ^=/߸›‚¼?æŒñú”»ïp=ýüÉÙâžÑh ’">n¾ú\öæuâþ‘ß ÇžOÐâôˆüðÿ¦]‡éüicÂ?Ëâ5ï=¦)B*zêø¦›.Z[Òðäv.p« ÔÛ‹ï/§#ǘWˆªËKè³O§éã†Òa¾÷?~¾û¥Ëhܰ¡üÞZ¸ŽÞ[¼ºçFZºa'Aéð¯T^â¤ÓÆ Á°Ð»¸üöÿ¾~ é_ÕåY´GO4:"<‡<ÚãŒÝóùý´y÷aÞ¤F”ÑÏ' È«®¤w¯ŸcÊ\ei1]zÖ:÷´ÑÝf¹q×!zâµY–|‰,æSEù{K7ÐySǹÖm&©>Ðdu·m)`~SnëÃa6ñó†Ü±’R¥Ž¿?•[²,„²80ý#Ó`@)£¡]ÿ©4yôjlqÑ ï.£‡ž~›¸ósBÈáíûêht­Öa=v²‰Ô¤ë.˜N«·ì£%,àî¼áb*+.¢íܳŽJçq£8Bþc]rÆî íV‘fçxò¿í³° /¦ŽÓ‹ï-¥±,ܱ~´„ÎÒÃ÷~‘WõÒEƒT»ÇJ |%•~ˆßø^òîžî‹Ëæ“pܾ¸÷Óºô»çÞ#ŽÚ¦Û>s+{m¶Ð__™Gö/¡ ÌϪËiÍÖ}]ÿš-{iÊèÁEùÂ{ËèâãèöÏ]LûëêÉÔMDZG`Â^˜35õáyd {%#—w¢×æ­ )þLò<»ÎéÛ_¸”x·IZ¸zý›åÑÔ1µTäˆë4ml-Mü^t¥ŸbDÉ´CV‡ó[¨-Myxë WÀsµøŒµ>áþ-JêV( 7‰-(£¤€åðþÒl‰Ž¥‹N/ʪ*+¦Û>{>ýä/Ó‚5ÛéâÓÇQÿê2ZÍN*~\;lVa ¿>oõaKˆw5ßGHe%E„ÉÀÿ*Êœ4t 6«ewf²–þ÷ÎYâ9†þïo³éËWŸ#ê³o/¦J†é[MØ1¨]}Þþ¾­Ý¶Ÿ>^¹…&ª¡wÙʲ3Lβ¬:1«·î%3 î‹ÇËΚ(Êéé¼°ÀPÎË6ÒîG…âG™‹Önc b, ¤¥¢ìë/9]X7\z†°êZÚ<ô÷7>¡üMí€jê[Q*®Ýë£7>^%:Lm<4PÄ0³+…~xË•,جIÃ* þ XÂ’ÏpÎ׎cÜž¹‹ÖÐW¯É [ûìúKÎ`—ñ1z{áz¡øaùÏgÅÄ‹# ÚÕ7¶Ð¾#õtÍùS…GÀíñŠŽCßÊöžuò@,BÇâ÷·¬¥å<ÎÂaÜ0Ä¥t&¸4û°²‚²EâõÌYyî×ø³ºÃªA¯2[7p{>ñêÇ,DšCïÄ{áæñÏúÆÎïÐ>!@e¼@S«›^ûh% ;3ÝÂM­¡…Ôîõ 7ú’õ»B¹v@•PƸHŸ¬Ú.¶©ýê5ç aüÊ+ÞŠx„b`m¸y ñŒI#Äg(sóîCB°Lçá X„H€ÙôDBGaçÁcô™ §‰Îà“ y®Ü¼—~øÕ«è+Ÿ>W|÷µëΣTà•ù'p–B*ã|–L™z5a\áž[; ¤ô%`h+Žj{«@ñ7¶´Ñ.®k$x¿d‡¸v@áÞÿó+ÑÆäç=žcñûäQƒÉÉ Ö/:$ÃUScsýcöš9uÝÁ^7Àý wFbñÊ™ýÉjÚ¸ë€èH÷¯*£R§ƒ¦ŽJ?àNè–xñ½å„ŽxwÔGîË!§(÷"6ÂyÀ2>CÇúò³'ÒâõÜ¡ÿxµx¯§v*^ ûƒv´†; ×l£W>X):Ï#!•(ÜȰðºxç™9‹ÈÆÊþKÜ:Î2éH}£¸?¯³× ÿß¾A.¸÷YàDOÏYH»…‡o.§A߯°Õðø,†EÆ Ó¼$2p,xeþñ3?„Ùx+>Ü›Z]Ü.Nu-£] c wïoÜûHPVpó£CŒp˜ßz5UðØþcÏ¿O>û®PNñ`Ü¿ÃÒ…]ÉŠ«Æ¡Ý®ßq€ÇÁww%{(xEBÑ1G9±x ÏÝ+ù«èSgŒ x8>uÆ8Ñ韀>Z±Y*c¦ <ÒÖ=0&‚Â{"ßÅ·Ž6tù^>ëá,øîÊßm·íjwý¼óÝàÞþΨS™ªóuÕËГâê`>â³N’¥UU¹“ÇÅ- KE¦£ÜðÑk¾äLmÜ÷Ñ{^»}»ýJBV|gÏ^<ƒÇ@ÒN>Hö %ê5žÐà|>¿‡—nÔð—áæ•¢Õa\ÖÑ…ÓÇ ë<Ö÷±žÁšÂ8:á Á^Ñ"ÿ1>ˆ±S™N°R¦ic‡ÐÛÜA‚W`DMvùOBÈÄ&b:à•åÄqîà1Ékq|‘›¯$…':½›¹ŽÐ…/Ó†I*܃2BðëÇ+·’ݪŽÈwå3G[Ü"H42?ùNø¹'~q-°Æ¡(#Sw¼&߃/†ØŒˆ¡Œ|å*a üðÑËÇ1Ïà[x êÂ\ê!-x&S´v*ŸE;££ƒY€s÷Á£!Åwäw؃„aÌ!<,zKÏ&î_qÎdú#w$à ¹”‡%&±gÉnÓdÌÝÜQÒaHˆqÿ²Zòšøj·×ým¦ëÐŽ,Ž•[¿Ž;;õb¯P ³e÷Jqú(Ì¿€ÇÒÞ]²0UVò_x\ÞÆÈ„(|X¨ˆî—n~¯oÀ„qÐx¼ ºXøÅˆ"F 1FùÈ¿Þ0öp_{ÁTPLÞxaSïÅGŒßß}Ó"Zü\蜂oÀ?c†è’ ¼a8 ï{}=+Þ0XÒwN5]À¼õÜÜ%t÷Cÿ¢[88ôîœ_ÇA¤op Ý‹l±ÃJF€bobñZ$˜ÿ~ó!†ê0Cà‚飻į ƒY>?~ìE1£á[< <ÝxÙ™ôä럈õ,üìÞGüÊ —$‡/â‰þï‰7„·3‰0k"Úúáå‡_ƒÿ~gÏØÅ±CE\‚|Žö Yƒ! ¯AÙ#¢ôºýs²¼XDÿùøËâuÐo³˜¦òëøÏþ€ÿ¿8ÖP †0v8K&þ9þ¯Õ›½E$ª¶+hßü¯ÿ‡;yà'âœB‚oÌÁGé­?ø¯×ǬqïÍW¤]üŸº8Ðî¹hc‰=å‚Uë0öŸlÂX ¦ú…»U“Í+ò;¸û³’iÄ‘ùÅûôDgC&X?|äúâ•dÃ…âÿ˜ç†C=òý/‡`Ë$¼?=—ÖnÚ¾ù™Gü.Ãu„z>`‰ Ò³s@–äA µ%Æ ý¿üÝ>:eü¨q‰´) Cù9v#¼¤,G“‰ùºcJ ¼ŸÊ{"çµ#VQéè¼ÇËk‘pW´ÿîÚa[ûàéîdÚ1:ÙÑÒ‰,+“¿kãe\Âëlí¶}ô/öÚÜ÷Õ«ÅaxLþüò<´uˆÅ·$é§€Rüé§©Ê1 °Â¡\å0ÊcuKQ mP¼v*)±¸ŽL¤šÛÿ4Ù`µÿC1HÅë~Ë=ʱxÊE¼SÂÎ~˜Ï,–Þ\»u?[4^ñ>vþþÆ|^yp"ïäU!ÖÀB ج+€ä¦;íàňFñ†q ê©í/<|¾^_ç£y®(üonûÑÔ§:o¨+=S@Yü:©¬«_Ãkó÷å•Â~ñä›!÷ä:Þpç/~È›žñR·ýº@‹µÍaÑ`“"$,ЕȰ¢Ù*ÞÕSè^|o©Ølèl^ü ae>•ò+7ïkÓ/ä-}góÖÐH˜Î†=°âßi¼ÒedÂóƒ»âú)îP÷ãE©Ærçá‰×>ßEk‘ùäãïÃ-ºó­Vò;ÔGÀ¿£mÅœ;ò×|ÅIYü:©Y¬8ó4mzÍB^) ÖÅT^ß Š+Ü!a9M,-ŠM{ä–CúW _¼ãÁ6¾˜ë‹Õ¶ó£X}M%E|¦–† ×`I[¹º?–ã½™WD¾{x©i$¸ù#:ÝXnéí…ëh;ïÊyO“CŠ·ý‰—süO«7Hw¼ÙBÇÛsÀëk\3ç–ƒ/þ´1ÇQ+(ð•â×IucÓ™¼Š`‹«sç@éöÇóÕl½4óöƒxiU©øåw‘gl±žúT”„¶ù´ðÞÜÉŽ†ç©®ôN¬$Ú/"%Sím';üÆÎ|K:¶ÓíÇKÖF¦þU÷¬ÜF\N¡%^‡‹î}§•¶Õk ]šxmÞ1MKv¼úÜO÷-r_¥øuRƒ;xcŽF?láñÆC<?ž]ŠÑ’´Pð óf±Ú–æÚ¯¯÷c+f×c„ sVoÙ#îÉ?á«äÉ{ê¬(ïX¹y7]Êq3«¶ìãüÑðÅÑ8ºKÑÚö›ˆlÝ}Ÿ÷¾ÀEŸìõ†P9ϱ‹¼GwnÁz¢.ôN5Ư“Â(Oò8âoÿ5W,¥¾—vw B]4c¬Xwû—Oiû’cKal*ò½‡Ÿã@¾®[«v—º¯(¯À’Õpñÿø÷/q`^«|M®ÑÚ_ºòÖ[>/n£­ëôBÞ>ÝN£,Çõ¦‚'N (‹?NBeòµ[¯;?”=Üïá›üòÛŸ=‹vq-ïÈuï hâq{$ùáÀ¦#ˆØ—ÑÊçrü™n¾úy©ÎŠyIì*øÐ=7 Ü®á™ðvÞæ¢!þ<üïþï³BŸD¶¿Ðƒ<ºøíbÞ¦yUçÉ•#­t÷Yzx{!Y`¨(ů³ Nñ‚mC5M/^ê©÷ É´«xé­ýÅû­Þß{d‰‹þ²ªÓÒ¿d¸•¼Ô©w°|=P@¹ú{ z¬( ( (Pˆx”•þŸWv*ýO ³Ðo/w’YiœgU…9_… EEEôRà±¥núS˜Ò¿˜•þ#W+¥Ÿ^2g-7åêÏéUÁŠŠŠú£À¯¶Ñß×tŽé_8J_Yúú«©ä!RŠ?yÚ©/ò†íþ ýðýVzgG甽ók-ô»+déÎd ¥ø œúŠŠŠîÝÅËð®<ì ãò‘úõ%J釒GJñGTæ^Þœc#/‹{¢±•ZÚÄàˆWþ‰‰vVÞ𣼸ˆªÊœ4aDØü#áŒ2üÁ>^²t㮃TÏË—b3’vžÿŒÄ륔rÿ”TG¥@¡óT.È“MúæìfÚÝйáWO³ÑÎ-ŠZ§êfîS à?¶îôòÚ÷.ßÌ»pm¤ÆVï+$»ÁE6C#™©s¬+•êö‘vËÈtÐ믦²b]|ÆD±C–ÏÍVÂ|àþþ²bã,Jb6šÈ`2‘´µÒoëø§7•GW 2O…Ë“–o¡fl˜ÅÍÈg°“Ûh'?™º+É_&–KöÀq2Ý¢=•°QqÉãnOŽúèŽ9­Tß±ö>Zü}3‹è–)¶$!SŸå²§q²L4P¬· ëþÙ·²uï¡~æ4Ò±–Ï[ÉjèœÆ’NPÛƒvªó¡î)ôê‡šÇ ÷¦«Ï^C´uAÓYxD^y»Ò§ç,Ö=ö ¨*/&‡ÝJÆ Â`º»Üí¼j;ã¿’>Z¶™¾|õÙYÁ?‚êg(P¨<.Ožy{ w¢Û褥Õ£z>û –4P÷Ô,ÌA/Uy먟ç€hOr{º9ÎöôîN/Ý÷A ¹:†ô­ÜÙˆçè_:"3°ž ½º“- ¤â‡Â÷ûô+]a}šŽÒÌ¢ÙTmÞ›ñz@‡b°e­8Žûji½ëzü7ͺx†°€±þwo$ìRöò+ÄÞäýxì=Þ §Ã&,¥z²©ñÿñŸÞ«ø÷®…VF¡òTWy²†ZM%´­ø\j4WgœС¨³Öˆ£ÌwœF»Ö÷Øžx%oú /Á¹_n7Ю.¦©ý R%d¼žôV@AÕ²ì•à ¥ÿÆ'kiy#Mu¼BfCg$koU:ç™þL«]³X ‚v×MàôØÉž!«4î/½¿‚Šì6ªd+?[»èlôåM…N4´ˆNHoàß[u[Hå*OE“'Ç-ƒhsÑT ÒãÒO„ÐÑXY|>k[Ým{:Ò {æ¶ÒšºÎ ¾!e&úó§‹ihyï‰à¤ÞÍ  ¦¦e#Åxþºíûhö|MéŸ^ôï¬(}Yèpt@^ûh­ß¾_ AÞt'ä WìˬJ¿º"{J_â†Nà@'äµVgY¦:§…ÊS§Ê“u¥¿Ñ9#+J_Ö(:€°D¶§û¼4ë…¦.JKð¾üù¥ô% ä\0Šî8(ýÞ£þ¥÷–Q©±NXúz©gxJGéù¹‹ÉÅã߀7Ý QúÏð˜>Ö‡¥¯§x,¼©Ðsï,ÍþzÂ7_`)Tž —'/¼·R¸÷aéë%–Vc =;w)µ±<ùÝ’6ŽÜo¡“.Í àá|úáL=Æsô‹­éS©(PŠî}O{;Í[±™ÛÚi’íͬZú‘LË’m65ð¬‚Wlð¦Sù#/Dï7´¸¨¼¤(kîýH¼åoXþ¥EÔÈðeYŽ:§…ÊS‘ò¤¹ÍEÛ쓲jéGÖ*,ÿmŽIÔĆÎwž^M\ÖBAJDê[l¤§g•ЭS쑟©ßB¼WüpÉ!¯•¾Ëå¦%vŠèýÞäK”‡fÌ_½EÀ ¸ªIÒàCîô z¿·ù…p¾OVoK+þ‰Â¡Þï™…ÊSo)OmØ#¢÷{#¯çZéú`:aîGÁ{y*­—‚Üù?§ÆL¯}¾Tñu%UÁýÊ{ůõÎ}ÂÚßµ¿ŽZÜ>ªá¨z½&ÀÖÜæ¥Ý±ÕïK‹Ë4ØÃù5·º9šÞªWÔ\€¯¥Í“VüupŽW¨<)OÚܪ³Ôè¶rÄ¿çü—xOÒíÓÌÄWDåÚ×m…õ`y­øC½s¯—ÜÜ@·î="çÁ<}½&À†„6ï>Dí wªV¿¤ÁÆ]8_óôõŠ;àÂ:€3]øë×\…­PyJâv)å çÁ<}½&Ãø¥¡ ôµÉf ¤É‹¨W|\ñQ ¿Ÿƒú¼ÂÍ’—¡µQ[Æç‰ä±ßÂ<›¡M,› ¸ý~Jî~MXù©ž§Ì™xE¾L.γøž>À‰eƒÓ|¥ª·¡@¡ò”Ä;\ž`E¾L-ΓHt÷.`Œö`›jOÝ©ïçµâ‡[Š‘Çn‡]èn²u_Ív^*¸‘ƒr7àÉ&Iì;`ÌÖ„ýœéÂ?Á¢ÕëqP PyJâ.OÜý/më6ÚT{Šƒ¯ é•Àß"ç§wk_V àLþ2OuN •§$ÞPüRžø ú_MH½4É“ôq‘Ê)›È[Å·œtÍ!HQ¸©(ÐÞ®¤ 6Ã-]ýÀ%ÑNƒd¾O´¼t¾ŸüÓ ÊK£@¡òT8Þ…*OTÈ ä­âGI×”'æñsO gj‚p§ËÕŸsŠ?MøçL…ç ²]OI¼ Yžä›*PcP o¿ì¡C×#2>ÀJ4wÔ>÷QøŸÑþŠæ½ˆQQÉï@ƒ\êô™tà•(êfJ(Tž Ç»PåIJŒ£>ÖòVñƒÊ¡ÆÊj½uüΙĠBùI’…[~ŸC˜k¨¦ ÿd馾ëž…ÊSx®<éž+Ô“\¢@^+~TD¨±ö¢Ò·ØË¹dž<›B‚¢–°§øùäZJ'þ¹†{.ÀÛ[çG4|ú´ô¥ÏÑÉÃËCÐY‹ªhÂ…¿¤êÁ3ÉduÒÆ~Lu»ß¥ n^@‹_¸†ZNl ½›+GÔÐySGÓŸ^þ(ä-¹pÆ8:gòHzzÎBÚ_w‚júVÐ-Ÿžy J[÷¦¹‹×Ó׊žš=ŸŽŸl>åu£ð(ÉSÅEvšuñ ª(.¢GŸ7DêòºüìIÌ_åÔêöŠ5?Yµ•¦ŽBÓÇ §¿½6/ôn.]<~U1í< ‡· °Ïl¡ß\VD³·µÓÿÍw…P¹f´nžl¥ }Í´–·ÐýÞ;­t˜·Ô½a¼•nšl§Ï>ßÄþ@•’£@ÞZüé葘ñ]:yh¹Púf[͸îŸÔäÕ§PÚÀbœuý«d¶Óú¾GïÿyÚú µ·£›žçÎÂ}§|“èdðIæ —Ñh¤ ¦¡…k¶ ¥5ôo¼üL7l€|Eœ²Bÿç› CÇ3o-â}tøXµòÒ»k¶í£‹¦ëòM2?RÁ%™òÔ7Ñ)J=DòÔ >ôµkÏ‹ºŒôySÇ?à§ß>û.Í™¿†fž6šúT”ÒÚíx?3M™ú2¹‰à’È»Ñ)G4­¿™Î®±ÐSk4ÿivzô 'ù"–épZ ôÀÅz——.¦‘xG½ßñ.zH¯ni§R›®“ÚÒÛéÀ§;<Õ}ýS ¯-~0wç‘Xe˜m¥T;ùfZþÚMâC ÿöyZhÕœÛhæ—Þë’Ù€Ñ×’É\D+g•ƒ=]ží^õ'ºðÖ¥T\5†Zê[*¸öäûö2.@ÅñcâðAdæôÖï< Þ¶[̼€Ÿ^z}㳄r€’?pôDè÷¸¡Éd2ÒjVøHK×ï o}áR!´l ½×ÓÅÑô×WæQ‘ÓI6»Ì /@”Z?ų¡ãþ÷÷è4+{"Ïž»y%ÈŸ?9;%¬+ãã|±•ÆÎü [ø£ië‚ÿÙ¶6즢ò¡‘EèúwU©“N4µ$ã€êr“­¤÷–j%ùñ ^w¿¢´HþTç¥@"<µŸ½H‡YÁOS+Ʋß^¸.DµM­â<•+±#6³úhOC„_¿«`°Sù/ÜÏKuó^¾¦˜VöÓEC51í hïìiô /É2#í8ÑÙ‘H]( ô@¥ø»!£x¹[uó´ëm» 9øoÝ»ßà4öúâw7"{É ®éüW©ÓAûtºðã÷ô ÃÄØ>Æ÷ìþRÞJ$õ­,£Ï_<…ª*+¨´¤˜{JÞÃOÏ¥µ›¶o~æÑQIGø¨çcpÍ7é Sþc¸YªøèÿåïþðÑ)ãG»÷æ+øgò ‹×¸\njjn¡'ÞXHõMi=åšOÝpÉéìm"úݿߧ‰#ѯ8‹ž»„ö©ÛK£¬2æÑ\QüXé#!@¯§Ä;‡Óu¼wÓ$;õuèá%.úéøöh ar&Š•âêyt (Wtº»õÙý»yÚõvKýv²XKB7á%0ší¡ßö’þäi®ÉÔÌʺØÙ‰CO#:{ü°´bóîS^-á|šyÓ!• ›ñò”Íj¡aûÜým.-ã8€CÇhL­X ^CjnËž:ÚªÅôcEOÚÛ Ÿ/h£ûÞo¥j‡ŽòØþ¡f­б~ä‚{*) $C¥ø»¡ZëÉ]duö‘úݼº}lïTÚw" žø%Ä7hì tdÇœÐó¢²aÔÚ°+ô;.êÙÍ_U¦EÇï´±µb# ŒïG¦J68Ùáž|¦~âå)o‚ƒÀ¾ÑµýÉÂ}ˆæïËÇÞ#DZÀOH'µ)qâ‡Îÿ´yƒBQcl>žT‘û ÑÅÃ,tÏYEô5òk}ª-ÓòØÃ•’¡€rõwCµ–“;(ðRiŸ‰tâà’nÞÒn×ï_H[ç?@£Îüwœ¬ôߢ½kž1þouTRó±M1óÐÛãõM‚7ðNy2À¯;d5mìPZ³}?MvF¥lí#Bûç§RaS žú`Ùf:÷´QtïMW«üÛöj^³Ue¬ô[Ä®“¹DQö«ŽOñ¿ÁQüýÙ•”Ýû]åæ)€AÅû˜h7Ç x8P%Ed( 7TóyiÏÚ'iøŒ»º(þ¶Æ½4÷±!§|µ{Í_ \üá‘ÿçßEu;çRsý–S¾Ñó E Á;eÔ`1_Âz²¹õ”)a÷}äÙwä+]ÎgOEXÌ'‘©|]2P?ò†ÝñÔ¢u;GxÚuð(á°˜Mb;mùÌÄñ4gðÔ¿—çVGð?¾ÂE¿¶”þ¸ÂMÇÛ:;ȳ^8µSüég›x¶gxº*w¬ä÷•ÓìôëE¹ãíu§Îú¡€rõǨ‹Ë~GŽ’ÁTRÿ4áJ«ùU×^@[þê¹· AÓñ­/h# ¯~× {|‘ÀÑÊ–yÀeHf¹±h™føàLþµ ³/Tž’xkgMž˜‚<÷NçÉÌ0ªö¤óJêeðòVñ£qF&+Sw ,ò¶î~»ƒ¥d·v ІOO€‡ã什¿s\±§o³ùp¦ÿlâ¯e*O…ã-ëòÄè º“÷õvŒª=é­V² OÞ*þhduðÔ Q{P›ílßl€ÑiMï(Li‘§toõÃÚœéÆ?Ûõšå:OAžX¸µšƒ^ÝV/`ã]6T{Òm e°¼Wüè©Ë£Òiá©iªóɵã(°ÆþÎÜq|óà_Û¿B¬„&×ÐùA>ŒH¤ÿ,¢“·E*OIY‚3ä –Ð«òÖé¶žlª=é¶~²X^+þP#åu®°³[)¯ˆa1è€wJ¶èÝc¹€w¥ªRCÝÙiéñÃn^4è[^Ìî>3µºôø¬fcÚðï†,êv (Tž’x£]Jybâ vúy¤@ÍÌ~ Øß“.y’YhUî½E¼Uü¡FÊCýØ&ÖÈ‹ÌàÜ—{éu¾‘tÜWÛ[4Ž»À؆T:Cð"TAâwF/ÊïpWSInO;¯°§O×$à|µ•Ž´àŸ(½Ôû=S Py*o)Oúóò¹lñ—ù´U{¦`xÛÐJ»jO½Göœ()o?¨^9V•Ãaæ^¯Ùl¦%²²Õ¿Î} ù‚ìªÓI,€Éf Òð¾¥^ {*ûЇÓ`|m²ó¸$–:åÍ¿t•ωÆV±Õh:ñ×’yL¡òT8ÞáòÛêŽj[OÆ`×5²YÝ€0!š_µ§lÖ„>ËÎ[Å/{èšÒ7“Õj僕>»»òFM>´Ú5K7µXš}idµƒìvÀiÀ/qIXù¤Ãn£©C+y%4hHlËÝDËNôý¼( kUmKþ‰Â Þï™…ÊS‘x‡Ë“'£šh\Ûªž ØKoÀ4ºÚªÚS/Ñ<—ŠÑ›â‡ʇ!-ö¨‘{âfVœPø6›•l¬Lm6U™©ÍO}hyÛ²jùÃÒ €¥¦ÄHýÊpjÀ<’M‘4ܧŒFõuR›Û#¶4Ͷåò±µjõeÿ(tëà1±³iZø,Jz¸•1< •§"ñî*O|Tí=DZ—gÕò‡¥Kï´'!«%¯éï qP ½sÆâ(0Æ+!!ìúÛÜ^^³’{1Þïñ‘tÍAñÛYáÛyOwìëîrñ>×E^ Úé wCf:Ö˜§){ýÚP…¯ŽxXä˘< o7hS~ŸïXø=uУ⇀>ÔìòàŽ.âqéd“ÖPdejç|ŠNVúíÔÎÖ®­LlBä"ß;é5R½o˜PÆVF6j#»±‘¿HÏô7,ŒU±8æé›YáWšÛ©ÃÈŠÞ.`+))&g±S\^Àh|à‘lŠEƒL§¹™4xèD»‡ê™(Êh@P$OYâsºR€åñó†â8cž¾‰i\iæÎW†ñüà#ð“«­¡×°JäÇùœ$žAàžŽ6b*OÅ»«­~ùÆ]•¬øckÞˆꥳ"ËÁ²Ò—ŠLkÄ<`¶°ÛÙͶË9⽄#ÎÐ1`eÑCîD[W?z§æœ‡®#/Ê“‚Ãb1³{ÝÎJÞA°ô¡ôËËÊÄ5:ˆNÕÚ—0ÄK«ÕM¥L øÇKYN¬s6ñg> üþ¦õËï`1ç GˆßbÁ£ÏÂqø÷3>uy#Ó¢4Õ6š*OÅ‹w²ò$^~Ëf{ŒhSœN@VóÏp~‹õ^) 'Å2À‚êøáCÏm0¾…myý•çfÝ~ן›»¤â¾[¯1Zyá™T+.’ì-#xNLóãñt7+~7‚þ:,^?ïÅ[ì脪¢<úβ“<,¨È"@IDATÇ—y•$^¢-1foÐ`Ȉ‘—1MJÓѦ@±Bã)É%ÙÂåg«=¡lÙ¦8œÿ$d4ßÊ÷¶´ó.éIñƒ¸°RÀHí'NmÞºf僆i§ÿ⟳o›uAÊ.ÙXEÃaK\³¼­Täp¥/ÿÚy,qÒÚïÎë_æØZêS]%/Å¹ÃØ5T,ó‰¿àÆG‡Sö`õ# _[´G³ôcº“À;4èN‰‚,··ñß:Ö@«Îûg[[3V,âH(qHåŸï¿hKoд0^pÉ·ÒѦPï²n …§€3Roã­•)Š•ÝÛí ¥Ë6Ù Í· ¡-i„Ï£¿zTü°RÀLîùo¿± ¤¢âI¾¾^¡à-×Ì4¤bù£±ÊéqèÈ©~P°ôÛ9ÀãÛþƒÅÄ?ÖøRùG«÷Š’Î»ýú÷éüÑq…2ÅÁ¡½°øQ&¬~Ìч°„²·/€Á¸2™2AƒXðfX%P+6ï6ìÜ´îÕÕ >^Í0b?ÌÓÏò]ñ‡Úã*ð-*úô}ÏJG›â|§»]!ßîR6x*,½Ý–C¶poSwïx²™Á)”¶­úsúžÞ?\”VØE‚ªõ­gÿñÜ7Þ 8o9\2xãågÓ1æ$Vâ‚f—¿PøЧ)}-PFþÇRü¥aÃúU• fׄr Ðq )þ€ö[ ú“ïvÍ!3¿4xÒCƒXJœz Œ?½KçÆu¯øÚ‹s>,2‹gðø |~Ë×tJ[bDí|Ø>|•i$ô0¯MW›ó•§€[¬Ô[x†ÞnO(3¼Míß¹ísŸ.þBjK C^%½*~añ3¥!¬mÌhÿ>ý¢KÎ8ç?~óôÜò‰#k‚§On˜4rPÒóüÑ€ tÑhá~‡õ`>9®néÇRüì©%èE&ÙPq–Ê”Á~ò^äw½ñe§ƒ±`EHOœÓ?æcz"Èðy›W|òÑskÏÇô=ð\’8ã·R AñwiKŒ¿˜U‰Q}Ý‘#3οè‹Ü¦ÊÒѦ8oQÏùÀSÀ%‘Ôm ðôF{B9§¶)_Óºe‹þ¸ü£÷>áÇ…Ö–@’¼JzSü ®çÇx,z•XòÊÄ ·`Ó²¥[ιòêkxüýRðeÜ‚%[°¼Ìi°[xb|R¤’üYÄø0 þéå#‹ß²±Ê‡‘¿å}½œ#qŽü(œ‘øFþN4?ù>– ÅjtX˜†a4øý¾æ=[6}²ø·>r¹Z¥²oä÷q4ñ~_aì;ŸÝüŒžHQÛ?ãIÜ1Zµmͪíg_~ÕEL»ó¹M•rݤ½M’HŠü- MàO$EþN «Œ¾‰gäïd Ä5òw2yÊo"Û¦ÁîÛ±õ½EoÏ™ÝÚÚ„° µ-IåÅ9Lmé预¥…p~¡Ô™ñï½ôÜ ü{ÎØÓ¦2jÌDGQqµÕa/3›-6–.iQþœÜiâ._^³aë.y­Î™§¯?ïñ¸\®––{·mÙ²mýê=\*Üø°ê¡ä¡ü!¨:®áæ—ãûùlí3š"uÛ–ø©èpÉËÖÿþýîèIS‡ÖŽ;ÖQ\\is8ÊÌ&sòËfjå«¿¹Dîôù|^O»ËÝèjk9¾oûÖ [֬ܯ(¨¶”Kõ¬zTü;ÜR‘Ê<\ˆy˜!×ò±…ß>£“ ,>Ëoø2³éË¿2ü^–ðÜã?,¯Õ9㊼‚CƆÀ¢‡‚—îHXúèšµÏ(‹ôÿÙ;À(ªüÿff7›žPBèM”"ذ¢( g»³œg9+ØPQ°÷_`År<ËÝyÞö†  ì€RBïB =›-3ÿïo6³Ù„$$ÙM²»ù=˜ÌÌ›7ïýÞgfç÷Êï½wÄßBñ T N«±­Ãq³ü–LiåOsßRsPoÆ4#Uñ3þ`qí¿˜Ö9عFÇ+îagÅÏùà–þ`5™ÒGZU]‹]¦*ˆ&:·Þ VúÜ|oÕöMÃPœ³ò/)ßø½áëüµ4gýv8ß3fÉ¿¥–öŒš;¿Ö{!¿¥æ~M~$+~ëEäTð1¬øcnZ(co+a_Uñ7G@VªÂƒhÇï;ë½°>VÜŒo)3Vþ\@äsö·úõ­{áÕbœÅ)š~K-æá4sF­ßƒõŽÈo©™HS$ÉŠŸóÏ/#¿ˆÁ/¥USáš~°Ò·lXá7‡ÒG²´—ÿˆküNX¿#Á,~GXÙóž7¾ÖkúÈvÀEÛo) ¸4:ëwd½#ò[jtäÍ›@¤+~‹´ù¥ä½5™Õ¼Ï ßRúMÞÇ´ƒ]nð‰7*~ØYü­–un]÷‡–¿Hÿ-É“j:ò[j:Ö‘R´(~†e•F­×ä,#$«–oÕô­}SCfC2qMCÀúXñÞÚøÝ°6˯i¤‰®T˜ Œ"ù·]D£[Z~ØY¿Þ[¿#Þ[þF\ ˆ&Åoá¶^B~!ÙYJßÖ|Íüœ>÷+‹k:ü.XÎz/¬sÙ™€Å,KG–^B„“€ü–ÂI3ÂãŠFÅ_©õñªêßç\‹'¢•@$ý–¢•¡È-"ž€Õ'ñ‚Š€B@! „@èDñ‡ÎPbB@!5DñGÍ£A…€B@„N@è %! „€QC@Ô<*T! „@èDñ‡ÎPbB@!5DñGÍ£A…€B@„N@è %! „€QC@Ô<*T! „@èDñ‡ÎPbB@!5DñGÍ£A…€B@„N@è %! „€QC@Ô<*T! „@èDñ‡ÎPbB@!5DñGÍ£A…€B@„N@è %! „€QC@Ô<*T! „@èDñ‡ÎPbB@!5DñGÍ£A…€B@„N@è %! „€QC@Ô<*T! „@èDñ‡ÎPbB@!5DñGÍ£A…€B@„N@è %! „€QCÀ5’6‘ ·N}t¨ÏçMŠÒÕPŒŽ QJmI[¼Õ@Å 2ÆNéþUMaq½P1”ݸ¾MSµù/M{piMaÅ_! „@cÅ¥=aÂÓññEã5•îöêÞŠBFœR¤'¨ŠÊêÔ*ÂÊ¿ºõŒš”—z©žj¸$iL;ù¯{tCy¦s\ççÎë]5Ý'þB@! ÂI %+~èjR¯›4eT±£ðÕP;fØ7]lË)Ó¶N‰S\Z8AsZŸÛˆ§}ÞÞ´Ã;¸ý~O¯'v{·ß}ãÎ_}Ì9—Ñ0 N! „@ã¨Sm¶ñ’o¶˜9ß¶øË=ö8ÇÇ)jN橉¯Òð„*]ìË J¿Ñã¸9 N‹ÓLQdªší“›'ÿum©Ï£ÑxKÄB@!P™@KS4\Ëçš¼ýºû¦Ü£iö':Û³•ÓgkmmÛ*“i‚3Nóô¤YZgû*<å©›z˜•?ËÇrŠB@!v-Iñ›Mû h¿òŽ{ÇØíŽG ôa ÿSlŠ'ì`ë!§Í2°,Š¢>ñç‰ƽü\Dù×¢„B@:hIŠŸój0àÄ´”´”—Rµ}ƱñïEŒreYRÕý†ÃáxeäÈË’!kKz6u~a% B@„F ¥(Î'2:uÆíií9>Ñš³¦_õ±±,ƒâ?Ö R;t|Ìøry[Êó©ŠCÎ…€B ‘´ŵzî7Ö˜”xK;Xï7GŸþ‘ž!ËIJÅ9âî(—WúûM® ! „@½´Å¨í¼äŠUËÀ½ˆiâ¯ú´LÙ5óâ?;׸•¢%<£ªä\! ‰@¬+•àÚ~|ëv™gÃÃÀ8ýFÂz´,˘œžvbãV ©õ‡ŽUbB@r-EñÛ‘ßÕfë§cV¾Æ§ê›Å²AF-ÝË-Š?T¨r¿B@ĺâçüYýûñšÝÖ.^ÉØf~ë©@FUÑ”8·jü±þœ¬¬Ë^! ™@¬+Î÷“³uhŠšlWܯøYF•TÒÇrK?? ˆB@ðˆeÅÏ ž7®ñ›Êß0¢gRÃ0¬B‹ÕÔñ–ð¼’‹B@4&XVüÌóÇŠ³\ùó ºÑâLY-Ùcý9EËC9…€QO –ŠUã·jýZ”Õø-¹ƒóõ/œd@! š—@,+~&¬4£1¯Áò7ï›"© ! „@LˆFeX_ðѬ<-Ùë›g /„€B Z-Añe¼~}üö„V4rÜjJm7°<…Žþ¿Zu8>oJÛ>¦û[[»žçR\RsË:Jn}L ¬! „€hNlí«ŽkË!¹£†ÝE‡vÿBûW’Í‘FÇŽ~Óº§=¾•é÷Ëû$Ýç6‹m$+v®þ¯YXXöÉ ‡ÝWOóSÏô$¸B@Ä X¯ñ[MåÖ¾ÎÐæH¥nƒ®¡-Ë^2ï±ãÜ[VDË>½é°8â øu¯‹rw~O‡ö,17Vúì¶,{™ÚõIÉmzv_­þ¡‡–Ü¢ôk…%…€B ®b]ñוÃaá’[EŠj§‚œlóZiÁúmÞ­¦‚¯8.¡5†NÎ~’Ž>y%µêâ*ÚcÖüSÛö øÉB@æ" Š¿ò‰é=È]’K^wa !*¼ s×SîŽÅð0¨ýQcè´«¿¤v=y¿+ÎÛB‰éÝ­SÙ ! „€h6±ÜÇÔ„äNä*Ú]§8íþ¶?ž|ù'Ô¹ï´ó¦Ÿ«p7ŧt \—! „€ÍE@jü5wï¥ø¤ö5\­Ý»†}Üb`¹ø”öTV´×:•½B@f# Š¿ôŇ6›Ãñlq¼VNí®}¯1”–9˜¸¯?ó¨Ñ”Ùs ý¾ Ü”˜ÖƒŠó6Îå@! „@s¦þÈópo©ÇˆãóˆvÃaøt_1„ ÈÑ‹pB@!5bYñ¦àÝ®²<—‘v˜¤=-—‘jxÜîƒUäŠx¹«È+§B@!bYñ†ÛURœã6’·صHñ`ÙLKKöEŠL"‡B@Ä– øÍåÛׯ]eŸ·ž æ4áófÙXÆ›6þ‚d-Ù›PIJ! b™@¬+~Kqò^_µäÇM>OYþï`>HDzé^÷¡¥ß~¹Ë‘òŠPB@!]bYñ+M‹¹íؼé‹ýž^Êo·ˆ{R,˶{ûö-y±ÎGÄÉ, ! „@tˆeÅÏOBÇf)}/޽ßÍýxÏãÊ_áº@÷vŽea™toÙ¡o?þàÅòZ²s>Ä ! „€™@,+~«¦ÌÊ“•¨›·’’¢Òe‹¿ýwÞŽ~sýžÃD„cYX¦å?þð÷¢¢|ÊÇòZÊßÊKDÈ*B! „@ôˆeÅÏO…kÊ–Ò/ñ¹ýöý7+7®ZþÉNOeIéFsÖü9m–eÙ²vÕ»K¾^¸Ô’{KùK0Ä ! „@èBž¹UÑBˆÑ!tQ%«©Ÿ¨ [iù–°èÃwd(4àØó ô¶Æ Ç'j[Û¶F¢¦H¹OŸ›÷¹¦¿iõŠ÷¿|ÿí¹A2²¼,7·XˆâqB@Ä8CIAwÅx.›={!+~ÅPv#C›='Õ ÀMä¬8=ØXés:âw`³õịr÷îÉvúˆKûnLmgßht±-W2më(Na½~ÇãôyÈ[ï³!ì —~ûù–ÿðí2¤Æòñ*?¼gyYn–?bº$ ‹8! „@càJä’ÆŠ\âõYñ#šm¤™·>úh«—|ðP„µ¿Uãg¥ÊJ?ç]]ùÓ÷«6,ÿmóIçžwŠ·wïS÷ÇõJQ hã”b#^ÉW슧¡;ž*Ø¥§¢ Ÿ¤„(½…Æ®M¿ü°èÓ…”– –[OxÏ[p_?€ˆB v °ñ{3ÑÛ´M¯±‹´Æœ…¬ø5U›ïÕ½Óõý|¤òf)5ß«ŸŸû÷¹&ͦü6˾Aw¹J|_}ôΗ_}Ý«ÿà.ÝŽé}Lbrjº#1!ÅfKà‚BÈÎëõ”)îqÉÆÎîJáFUqíV»¨tÂð~ÊôêÏGùåXN–—í¤™Ä !ÛX‡©6è”HÈ©aÊ­yìXÝðžuÑŸâ4JÒ ›JвS!};>Î;4[üŠ—“öG‚Ìu•!,µÙ›§LÛ¡²jvÖ”1uM¸‰Ã±’g…ÏÍüÉØR±¥cã‡È}JIØø‡1[°ç{ÂÂñ°ãZ»Þ¯›Úí”ÆõqvJŽCJñh{p•ÑÂ9‹´Gvåz÷ ŒUûç?7õ‹âqB@Ä6±S²>ƒÝÕ€W²¦viÎœŽœuª¡7«†q¾NJ¿,ŠaÃdïvrëü!wI\y,×èð&c9©Ê«6­Õ3·s‹mD»°(¶›'gM„^{Å 3fMŸòM„昔¥üYѳÂçxKÀföýcoµX­¡p²šéYóæëÙAM»ðã®Ô$ê üãâhËúÚ­›ãý a,¥Ïýûâ„€1M`Üä¬Óu2¾†.ôÊô)3š#³c³ oÎl2Œ«ìTêë·VËÐ6S†¶…J!*ùÖ§œkq ”2ëmh¿÷hÚãé«êmUö(švͬ¿N^X5Nç"ÛnZšn£W;º­ÄéTš­RŠB äËé|=~·wç:´Œä)öŒ“g;Ç•.FÎçÕªù³‚O,ßXésA€p­ßRþá`dÖö?+rn¾÷d¤jÆØ‹õÛ:´¦ó¹ÖïÀ†½ ŸX½¥k–ó­ÜÔ_ñ¦áDœB Ö˜ דókzG[çÞNçõ\ñiRçtêÏôÏQø8«c‘Ò+n1Ùnp­»Ëõv¥_]ùŠõ  #Z¥ÚWx.Ժݧê†ÚŸtÖ+ìØŸ¯WUü ae)Ÿ Y±»§ŽîÖÞxÄ®Q² )jNUi¥M³]?j¢‡Çô‹B@Ä$îG7eúÿðü=ôÞy³³¦~ÞX;k–]ÛypÏðCz=ðAæ¬`c3a_0iHüÔ-ŽX5Ìy }Y4ÞWj¤ÂÀHG -^)öµµmÒ’Õƒh9(BíF"åé\oÝgÄiÈûvEѦvÐz³)Z¢Ìj$2vjÖ݆n<ƒþþ·Éžq]„ÖüY~Vþ¬Ø¹éŸ7VöÖÞRú|ùXäXé[×ø­Z?+.RºŸ¾#®ëQ<35…NU¬'¢]'/''§L=í¶üH-±Å ! N€kúäÉyýú—)ª2aö´)Ï6<¶šïï|¼s™Ï3žtý&(béñÇß§+Š—¿Ëä5ü­§T-Ç{VÒóüýÉ­r¢îS¨ƒm-íø–Zk(WÔàtÃF{¼}8¼~È× ]´ çr€6ÕpKX¼-5–È8¿ò§(Á¬‚ÉÃÜçÏy· üàƒ¾¥ôù:»P8™/âàþ>¶”¿U0Ïhk:!ñ¯·#È£(yr׃ß)tb>xÞ¤©¯)гÙú„,qd/„€•€¿OŸžG<ý•&6†Ò7[þ2ýv2ôÇÑ=ßÁ¾Féd_¡¤«»(QåAT~ÅOÅF+ŠG?¾CáU¡9¯GeF %©¹õŠh—gýVz¡ÏKñ%†¦^ñÊ_'V¯ê8…Vc2Ü싯@uE“ÿU…c;Å%7ú |íÝ6»mÐL烫† õ¼nZ(ÄTnúèPŸîãl7(þŽH”‡Ò‰;yÔ~ñ_téj_ÒE#¯ÙeÝ’ëërpƒ{Ķ}Þ^<î_œB " @ñ–Oí¾'|{iÚƒj´|ËÔé×ùtýõ¾Ž…ÔÛñuD29’P›¡ý9{N¡ N!Ðâ{Ko7 5i°ãã¨×mX0Žj©×ÐõÂý £N¸Dr|çÝW¶é¼ûô«5[\`åo´D¬ûô7¶nY½}îê_><±£uAöB@X'ÀVü°àë}JÑr¢>»ªâ£¶Ú&›bèÃÃQüá&Úñ¾§l# 7:TÇÑ0|€à1(0 1öRé¶¹O(ïÏ¡1 §<ç&x.’„ÍGà–‡§…=AF'ÛʘéÂNTò`©v7UQá&Ú„ñ=ѵí¼ûõñ ö4ŒšPîG 0S~6Ø\løô¹sŸ|xó§«ÎyO;z5¡x’”B éxètN,ö©éÒlä”Ê0¥‹byáNF¸‰6C|#&äå¡à‰„ãÏè©©ê°X„Bt¹3¨¦ø?ŸÇ½­ßö¸zÛ‚RÊW²É^!Å£«]u{J$.S®Üs±ßÛKǃ¿ÔÿîÚïyzÂÚ£—«MI`Ĉ¯x 9¼}þ„ãh¯â‹‘סÐÖ’­'ÃïdwIѳŸ>¡,Tõ{Bâ#ï(¬ß4SV„²B@DÔtÚ`U½Š OÈŠÛaÕïÒSTUÑ^%žêî•uT"ܯÿ‹Ù¼¢`­îÜûÊ6 `R÷îý:a8ÈÐðnàõLg 4h´®ëwíû¸òùgOªã>ÐÉ #{! „@´À\˜.·û÷aq¥Tšn¥Áèù:Ó ×ï|X¿eÁËÓ|¿ÁÕp£(þÀD²·¯Tÿ¸ÿÓÙ'ÔEÆþ—g»ÇLô½wÞ}Æ%š=¥lnC!K`Vt ˆlÃvŽ®/»<®èXŠ‘6Ã~¼i)[—„$ŒB  à¶«Þiƒ[ Íuì} ~Í7˜¼Ý¯"Iíè¤ËÞ§³n¨~î!U‹£Ó®^HÃÿèŸQ½Kÿ+é”+?‡õ/‡ì÷E‹‹oÄü±Ê6=A½ßld-¼Nxy6Ilhª/õ‘žœ·ô}jõIÁ~UGßSp­/¡0\Óâ»ñŠX(|\à{Ðpþ_tŸ÷gö|ú¤úÏyk×Ì{&1ìÖ¥Ue”s! „@C(ªú5¾^ f3mÈí{ÕNGŸ<‰6ýò¢Ó)½ýq4üй—è1 „µŽ:qB¥ë;×`ÚøTêØç+H÷Ez[LÔ‚Q|¤v¡R}’Ó9'ìs³ˆâ¯ó㈜€hÒ*Qt,èäÐ ð2¾›{?¿¶cŸ'W¾Óç…5µﺷtÇy“ôgQ8Õf$tF!`<ʦ_ Ê@w@yô™¤×  ñOŸ»t7ºVÍ}\}fî“Ú‹žI¯TøG…€MJàì½~VcÓF÷©!­bÚñ˜‹HS´{¿…=¥mÚúëlÚ±òÍjó“Únuî})í\ûvມ{iÛo¯ÑQÃÆüêzÐ3î':'ùY¥›}‰•° »=ë¾3—0®ku'Š¿"0H±¡jÅßwƪ›P4”®¦ßAež a ú;µÌ;&XŒÊSý]<÷þ’Ý(¼pÞýƹiÔ¦-Û ³ê ´ì f¢Í©?ZîÆlR•¸ósyÆ@´ <‡ù.ÿdr»ªáå\!Ð.¿ür)š3Ï×A][6¢ÁI&gô¥‚Ü5hùôOºcÕ[´Š_U·…Wà7ðì§iíwY¤{Ë*¥™Ÿ³’’ZõÄ”»ŽJþu9á%}MøˆNHøJÞœ™u¹¯®aDñוT…ƒò-¶jüž]9H'}:)öK¸§K)jí£ð7ô×WRüE…«æô±ÚçZ[VN½ÿ@!ÛœŸ~ý˜Iz{M± …ÉÊ(,ÄVùÍö/a<Ýã1_ÀÛ^½xì6¢ ðæÜ'Õ;>}Ü>lɬ¡58j“C® ! êC`îs½·ž¶8QËÿ”ÿr×ïÈcTZÜ´NÑ%¥õ¤â¼-u Ë5zWÑnÚ³þCtçãëäJÌ8JLëä[¿ÃŽölêåøCùéÚ;¦LëQ¿»k}x¦æ°r%Bàõ*Ö #‘ûõ½%¾9*©w­™Øwý‰ÏmHÍs»ú9ŒÄkË”’•NLZíTýä§w$ÒóNîgÿãä¡ßS+¯Ö Å·vâ€ÿÔ–¥r£’eÃÛãß?Ý9!_ß{ºnèg£8|::¡x¦¬Jï^УPð8 ¦ƒWyiÞ2Zp¿²DÕ”eºª-K2|MùÐÃÚ’—kB@ZüxÛ”|[aÅçƒq²Ñßžøî´G˦yŒïPØÐu0J7µ†?“ø ­vŸCÊN¡íî!48þê÷kI&¤t¤¼=?1|J›>Ôsè­´ì“Q³?Šâ­P»3kùŇ6£@°qøŠ®?b|5èaÿ‘ó¢¸Iý=Â\÷á̧SéÏ_sa` KQPÀ^]iOKYuθCùõ•I !}øÛ¡Ùò3=^o&¾ íÑB‰=µG¥™ÏÍc|Ì}>å&¢aC‚_…+?6wAþ¨ëPï¸/)×Û•úº ™òˆ£Ÿ+âÄ‘«h/9’ÚWò«î¤M—SHµÅÓ°‹ÿ]é2[ó>³9ÌžO4“ï­t½¾'‰j>Ù•2¯—¤Æ__x±Þ¬ñ“~öíz:âOYWž9ü(.VUÕ,À¸užOѹ¹ÿ]÷]¬¨ÚlàÌ­ÜÅ4´}Û¤/Ö–ßWudžíû÷Éÿj„ÂÕèFMÚÇk°A oÄÍú9…+Ž5t.8 ]¼ïÉׂ~£\88×Í!‰Ü—æÎË#tð”Ã+Ñd†É¶•Uº®®M£´uÜý|¿ !YŒ9—i wÏm[¦{Ú©½æ™m§“ÑŽ¿Q¼¡âÑš{ÿ¹Wߟì­jFŒ,URìõÌ"ÒáïÕ^´Ýß–ÞܳÀ—ÙfpüGJ¸%õŠ©8o3%·îuÄ{¶¯üí\óN \¯ãÝN§ßáŠ9QRZs_œ·Õ܇òG#¯á5j¶ÓªoÜRã¯/±T~DÃí6:ΪÅ÷Ÿ“çÛ®Ÿ‘F©7°ˆŠjÌ#]™æþ¬ÿ>µjT¼’8ý½%Ê︿þ«ë{¸ø<Øõ±âb´ Üd”zNÝ»låý¸6+øú‘އ[êAž^’·ç9üÜç’3WéñøI‡?† ÂP…¨+_«êp­ üºà×ÂâP'”ö¹@°'(§(ë ûZCQ6ŠmS÷.½¶ò<Uã‘s! N`Ñ¢3mî%KZ«6wŸGo‹nÅ6ÎÞõ6h^o‹–½6ø}¶¢m‹_iœg|¶õÖøý‹µpüó-ß•ú=Êý'u;(A°½øíïA¼{P1Ø9ö¨†²„½¤a¯;öœ;qâEqêc§üßÓå„a oSgûʺ¥ª0'›:}’Ñð)ªX=(ˆy¨ûÜÄ›å|zFÿéä-+0½Ò27ùëÞÃ>µÖ-uÚ£ CeF’ í¬Ó ud>¨:„“ F`èŒum—Nì}À«ÏŒU£ðc¼eÝÄæÀÑËæÚŠí«öbxËŸ1uÂÚIƒÎá°}f¬|M쮹wÀ?­{­}Ÿ§²¯Ó ú @¯Å±üžÞ»¬kCžÛQæqR uköÄþ‹-ÿ†ìy€2WéqŠ®ÁGa ¾ð"öŵ¾æ¯:>5;pï&´pðÊ›0¿å…Ôm†Í¾}ÔÝÅøXà³$N´P‹^ï¯ÚׯíóµÁï ŠÛ(rhè¶:,Ç Ô¡ÐVè8'J³”xcaC.(ï}Päûî^~ož³‚W•}ªŽ´½Þø¤}çÝyЯIë ̸© ÖuÏoG;¾£þ¿cKýÓ®þ’6/™I;WûPë ÷õŸ~íbZ÷Ýt¿á_}# ùúéû’k¡÷iÔ쬩Ÿ]jð¡ÔøŒ®yo Vú, J¿yh ø›%ÕÛ—+>ôó‰…2œÍþg¾¾%~ϳ±ÏÍV¸àýÚ{û¿Ñûé•ÃðA8¬ôû<½f`IYé\R Yú¸>3VlZ;qÐuÁ÷Öç¸|]€@ß˵ ïoßíóúÂa &ì‹B(ö^µT„é†U7Îâx|˜ïʬs¸½h)PÝåÝÛPÙŽÍvüxv£%dNên;ÙwŸÓãü}Êåo×\¬çHÅ f$ÀFµeÞCi.Û¦ªz:~iø}¤¡fÍût¼ñi*Q+üNÚàw€­¼&E^²[R%Ñùç‡Ê€ßa_îhf·ÎËCÔuÇ…ðƒøñàýX`f¿y¬’¯(9h±Û¯+Úþ¸äÄ}‡ÛóT—*·Ü—Õ5}3œax&Ú”2_︯ «j®fÄ8·Û˜ÜÊhK²ªiÖ_™W¡¶s…ôó:ÙViPþµ;âµý›¿ Þêxòk †ÆÁ÷•`‚ÖîÁx¼êßÎË+úB‰÷Šâ`$ß~åÄÿ˜óôêßVÜyôN–?Å‹ñÇ4ò«In(Õ‹4Ò®°®÷yzÅø8•­žØÿ3ö[wcŸBÔøÃÇŠûã³<³²¯ÇGwø¶é¿ó*…áw6‡ —+ÿ˜˜ÍøˆóÓàxù£º}ËÆ¤xÂG“‡ö„QQ7„é†ãnÈ/×~Žä¸Õ =¶GÀãLJ8Ÿÿö&Ð$·‡ö¹—f.ÌÇÔ^ð1ô¸u} yø˜ç¡u%m+y¾˜g7´<=%!oäØƒÒíRêÌæÞ…I ®²ÄRò&¡†…š²ž„òa"Þ,ŠNIøúÂÏz»AÂ…ó¦û<åP»uŠè gôÍW¶{4^ ÅC‰Êá@8iFX\N×BT1€Õ %µÖà<·ò(L>åÈžÔu ˆ®ü_¦7ç|`(Gヷ×<$¥µjsña)ÅeG¥p|RnØ·Éðv˜ã!CªíPW¯OïŠi‡»Á8©3>_ñQçqÀñ!숛2ø—uØÍ5xà>MŒˆ[àÌ?¦SsÈbù½>Tn|lhœWÆÝ: …àT„ÄŠ¤éAI¡Y´|_~?4cC¬RU7ûB¹?›â‚©îS\6UqšâÒmª+ÁmsQB¼«Ô—æ3þj8•‹U;ÃpªŸ=ÿ¦]++´©©n›^àqês°Üg ›qŒ=ÁDÁæÃ1ïyc?½üÇ`›ÎIà–ˆ ÜH²ÎyðIxtÅ–ŽƒØŠŒî¯ì‚Ïq8 T?ìÞŠK :‚¼.$“}>òšo£p‰÷#ï·°qø!MŠÜ‡yœv Åœ[ýˆ˜j„MPS¾)E¡K,=¡4{0iO …$ ³y£{8ír÷CL6{òä= a8ˆÎ'†ŒK‡€áß½ø¢µ_;ià$ë*j÷?jŠ:.ûÞËÙ«¶öêú–C´ î×{ÆŠÍŠf?k턾[­{£a϶¥+j¯z½a ÐÌ\àÚ6¬Êad€I>•mñca㧈/,C ŒC( 7ÀºÄ5<”FáØ?ì‰ÏÑ]Œb÷{àä— |oe?œã¢¿03jÄ…ÿ„Ê¢;ñq‚™ J~ˆË¼fúù»aTÄÍÌxH®óGÙXï+}ž6Øã¡ühSúœÏrÛ‚8ä­V>ÊügÒZùŒ²¶˜E,#&Ú¢Õ–[<¸_6Ú1š/º3pÌ~øè·³dc×ø®<¶oð¯êáð¿üO`8¾fIWŸyÍŠ‹£3ùï°Žƒ¼üiòå Ï Cÿ•/üšü… ¤Éþb<¤bÈl㉳ŠB%Ý<æÖøû¯“Â-7Åèê)ÁëQŒ¡evUÉÇêjymÓó‡Ûm5"TÉRu$ª1 Û¸÷L\ (43×Ûefޝeh[ê|[$,ÒÛÐÏ%WbÝÚç°Å]n¥ÏyÅIO¼™e±%j—_v[¿ýÙ*AEp=jÁWÂçEžüÇSL5›zQETi¼ÆÅ¨ü}ì‹ÇåýžÜ§ÏÛúúä‘ûZ¬»’Ý=YÕôdŒ@HFá!¶É(0`o$Cé$¡ÊœŒZ`2”O<†[Åc]†|â¡*âq-¾Ò±2$îGæ&o^Í‹kÔÑZÛÑÁç‚ð"ï\SæÎã2ä‹k¼$]¾·Î-?Ŵ䪣šaKÝ%T¬A9û ¨yx°)PÔJ t)F­ºX·µ+>r­º.б‹^ƒÎ„‘ƒpèhëüúnÏŽ{––\Ñý̤™¶xµÎ#Ã-Jƒâ;àíF?•\…^Âø<Õ¦ŽzÞyÿ+$ I¨Ij Lî‰ l½¯ûô÷Ñ· ƒ>e¶gÖL03X:t,Ó4í†ì ý ö—ãæ!À-«ß`ß¹í€=ÎVf'ŸÏV¦ùì(@Ø4Õ°{܆]±6ÌM‚^ô £ €Ñ fTVUhGÔ^Mù°PDABås”$Ì =±°êÖa†C„Â<'>é6žïÄôÃ5öƒ‰†.ãpwƒ×¡>Ÿê1TÕkhªW·Ù¼éÞDO\R¦wèØ%Þà ʚ‡Ÿ¤ÝnòxoŸáþ%A9”xRÒ¿µTuÄgˆâ……6¹OæŸ×Õnó’óÁÍ%¸(þÆ"CñŽynƒc‡îêíÑìÖïÃÃÞ®ÿß²»ú<¾¯1®¿GÀS„€ÍH`œ3k˜áÑ?BY³ÝÇ|­»} J´~3•úˆå5â¨HoKéZ¥Ï^}¢¨v¿·'%(E” å±­*åû:Ð^ï1´Ý3«˜ÀÜÅPfÚì­œé¼ €͉âo4´-#bŽÇKtÔš‰ïn9–\ ! îš>=ÓUbü P礨|G;¾Õ:ÚVÁ”{“jwl޳Ë3VºFûÊŒõÌä™Jºšaýo_4ãs¯ie‡ï§-bÿ…}ÕS/9'¯¨|µqÎDñ7×+ϸ·Ø•´ö޾æŠ~;<ƒ|n#ISI_f(jŸv¶Íñ'Åÿ#YêkëáGQ¢§ÁJÿfo©ž‚)ÆÕɹ‘C:¯²º®½­óNçõ®¦„&Š¿)iKZB@!Ð,n™úÈ)(\‡&ÿsa¾Òµ&! Ù ôSƒ´t²=øÉß#7á¾Yíìë!ñ* JaM·æÏ}÷ÛÝÇqÿ½Ï«ÄjŠ}äKÓ\zXÀ&öÅßÄÀ%9! „€h^·MÎêâÓ”>†ÏèˆÚw:ŒUKa†Z¨Ú•l_‡¶kfW©?àæ©Y·ÁŒõØ´Ú2mëÔ Ûfs¸`¼š Y,_ýV¯¥F*¦ÙmEX™x·åúzb¦q¶UæÛìÚmi°W¢¢øëCK ! „@‹$pûä¬n˜ ë®$åàEzëJ³úçÌ2Õi@§bA¥ÍXR÷-MSþ÷²sòªH‚²1…ºuê£C}ºoÒè†B·>úh+½D?“A]…m4*nÛûͳ³¦~¾T¢/&ž :ñ¬’ìôóïÇôUQè®øÇNͺ³wÍÀ°‡U(9Þ9kú”o¢‹ˆ,„€ 0nrÖé˜úæyö‡™ÚÄÙÓ¦<ÛRÁ|1«Uš;//óÂPÁy÷¼ZgÔ9X†Ïù•>[>Ò{dk7\”~øØJLB@æ ÀßqÅžq2× X¶ów¾9䈄4=e®€}ÛCD‚L ‘!l5~nÞÇásù嘕5ù ™w»!CîB@D&^bÜ”éÿƒÂû=ZtÏk‰ÍþógÄõñú‘ù´j—*,5~6äC2¯pó>Ù30AB§7ª]V¹*„€ÍDÀü®ãûŽä³±½RþÝo&iš'YX¨jü jküaQül½oò¡O¶sœ¬EÙ<襤*„€hTü}‡ÒÏß{sÔV£¦y‘c Ëä ©¢vtZX?ÙCipžôé½r(„€ˆAfŸ?¾÷æPíÌ_mY2¤ÆïÇÓóøÇé+ÿ® ˜\B@Ø €ŠÞ¿ù»o~ÿc#KuÊ…®R ÆüZnŸgäãÉyxœ~ÈI ! „€ˆjü½çï~ùŒ¬Q—ú¯ÕøaÈÞ¢ûø»ñŒ|29O}^ +„€ˆ^æ÷ß}ä [ôæ¢þ’ö!&ŒûlõÏzå;xî}øDìœÄ•¥•3! „€ž~ñð÷¿å8ƒRƒ2[tU‡!+~4wp (jû:¢êi‰°B@!€o?÷ƒkÀ"Y£ŠÈoKoêoTʹˆ7O™¶xìÔé7E‹¼,çÍ“³~7eÚÑ$³È*š€TãW”–[ão¶ G$[¦>r ~cù§ï0§÷iÕMæeóJË'Ìž>ehC2átªÓ©`úðÈrÈÓ`E7Ú×Wª±S²¾À¬h#5Mýò_'ϯzÿ]Ó§g–”è{UM¹pÖ_§|\õzHçŠq,a«·Ì!¥)7 h%`¦~©ñGëC¹€A§Œ›šu[c$°Ë›µm¼óñÎwsÆ©ûŒ—'>ù$¯ë-NÈ$hê‡Q{ÔÖøÃ3Od> ‘ª9 (4S{?zÛä¬.áã–©áñÃáŒ32âR>dCÙÂ|×ôÈG¤Bà0AMýŠFQ«øC6î; Œx°Ûl“=ß,\ýNW(è#‰¥/&c¥‡!X$K>|ÜÑÖù!§ózßË”i7ë†÷¯|\æñ|…¡<(u@¸ß ‹†\Â×,7vrÖt98fM›|zp—Òù‘ºï•¬ÉwrرÎY‰†/g:ÎÅldÝçz„ŸÓÁöГÁ] ˆïϸ~nGÛ”kvù¦? 9¯$22íÒÚ=çeVºÁû›§fM#FjvíÊ—n ¾|¬*ÆVÝP¦Cùÿߨ©ügö´‡~ ¾^Ýq]åæ{ÇNŸÞJŒ¿áðTlHÊøÎnsÜåõ–¹ùz°ãxÉ“ó8˜ž‹æÌL0\®)ê”—¦=ômp89-Ž@U¿WQ[ô8þ÷ì%ÃG& ’Z¢¨Êݘâò|¼ýéHw`Íï+ pçA‘j*îCSÔç¸çæ]Þ]‰¡|жaæ˜q)Æsª¢fŠú,ú¨·Áï Χ[[éÜêœ>JzÆÝž2î/ž`ùw>—Š8þ ÆVös:çÄžœ œ¯Ár[o ík±æø'èsÿ¿]žéïX÷™{Ei‹{‡ïò>ò, £¡_€ÿ}5)}¬dv?Æ}š¦8kSú·NJ¢Ò5ãQ(Û5¤ëg¹Ì4køS¹ïv>“n”êK ïIx&NRÔ;Àw×ãþ­2Ðínì¬Yvðø>W8 Pu/ZX½º¾hì_¦¨)GB åÀï3ÐÔ¯«Rãoy¯€ä¸6n¯;5ð·P»¾ kx?;Ö9ãóÙΉª»güsÏ9Êö< óÉ+YS/.ó¬Í—@¿6nÊ#çÍÊzhî+Óúú橨¹êÆ[ÜÇÏ;ïßÉaÇN}l ¡ëj±×u:N?`?¯Ç@<Ê(.Ôê…ð2kÐezái\¸0Tûn·wÃí3ÀFqffÝo.· ï÷o™òÈ/>Ã÷±•6‡e‡‚Lw…ŒAí½Oq:/?¬¶ìÅ2M¿I×é(Åü©:ƒ=+œµGœ ³ÇóÜê̺Éç5¾ÛåYÿ®™­V˜à}}ä.ö£eCÉT”¸¾³¦Ý·¡<žÐQ–Sp-µ±3çfäòX›]2Ó9ù·ò ¯Þ<%k+ $ã‚Ãò=~ãRÔz)}Óû嬇PÛU6¤ÿ±üΊ¡L©Ié£mŒûKÖE(켌փÛ^ž6ÙßBQqwMG&¯—œS~D Æ‹}ò8ç#ýj *u–"ýy^4++ ôÍhã4õ_Uã÷w_(¿)}3ˆBÜCC¹Võ9-ˆ@ Æïí;$j›ú¥¿½±Í‘ÕÙζ£æþnÐsØÿ{VÖÔyP¨¨8—›!”¢ë=Y6ÝGO …àQKN4O£õuRãÈÓ‚bÁŒ…P†gò½cu5<ž!šBuŰáþ»ï˜2­Ç YS·  0\³¶Ïaqí(ÈóWãP;6zUõ·Ù[Y5᪗8ÂîPú WKa[0ûðGö±ÛÒòz^dx}hò7N=DvS=åîyÞ­I\jüwnq%o<˜žPò x«ƒ/ `ÆÝ(ªï`1kn ¾&ÇB %X2«câ¾¼=š™W…JGŒø &LÑ餯Ï-ª¤î`›ò"ÏPã/ßæ|1ÊÑœ]QÌ!l(<#²'‚7ôã߈>éW‚ÃWwŒ¢ÆÂ+ƒ¸Ÿ_ñz.‚r?ØÞvê7m½¿BÚ…nE¹€ûºQ“>J3 øÍ¸ªÈˆß 6<¬†ûâÿÝVY[n€Þ'º§áèÞt©Î‡3·ªz ä<#Ô`*ž¬ý7Bù—¾œ5ù +œì…€@o¾áM³8à“oGã^jüÑøÔ¢Pæ§=­*Êch¹¿k敲`³ÿ åSlx –É){8„Ûçã&ç€{yÊ”]Ð_kTò¥ú×Í÷š¡|zºb˜ýû+g:ïÛ¸ÑP~Âø\¶ øáÀ440ŸJ_ûéJó ‡éhëxtÝ;Çlå8ÒÕ\w$jw#Ž2òê°@›F°«Ü‹[Ï­:9îs Ž’ÁâK(ÿsnŸœU}a¨ê r.ZÅ®?~(Q­ø¥ÆßB^ÚHÈfí˜é»¼ë.C-sTL fÿ²sÒ~ }›†ÎüG1VÞ­Úé߸^bxõãQA?‘íEË±bÝAXñß åt/ªï>˺]Ð tÒ¯òÒÓ?·òoOú´Ä[”Íy-Ù³üyŸ–îxª ¯ìÃ[:ißm³ÇmÔuÏ`Ý牾îµ´Sþ¾®Ç<÷ÀXç#W} úëy.ƒkêz¯îù‡ÊÁôÆÐLÿO¿rVîê#·Í®=îõøþ\_öÞmÎé÷«dÛïñú††þWä±ÔŠÓÜ'Û§+Ežk< -ĈØ*Ø–TÖlOV ­ÎJáåD´>ݨPüÝMýRão!/m$dÓ´„7”›«½B¸YY“Ç$67¢¶ù{ŸÇøÉçÑW ð5(½¡Š¡¢/Úï.¿ürúü'!\?7[1‘O )^UÑ$n¡Í{æž{ íYç„<ÿ}Ã×4E 8Æ“&'$*<ÁÐÄù¡ÇS¶Ç§ë Dÿ‹=I=Õê.à°õu³­EžÆ£¯þj C¼¡¾÷søW¦Oå½2¿ê½õ‘{¦óÁèŸ?rôôxô_Ë<î]ù0‚v% C•º!f?ð@¾¯ Cá,ƒ7u£l=î›n‹{ªÊ çB EÐ+æéÇo"ªküUˆ>AôC~Å·¢&p&ïÅ P °^±·¸UzJÜþ'î¿¿Æ!3·8í®Rê6† 5M¾ŸÿÉñLïrÆàcvšŒpDÚqÔGnîÒH"UÆyÙ%Q›xsæÌѬX×Ô’‘G½?š˜Ô–/¹-íÛ?÷ í†_ezh){ý¼ûõæÃC?´X¤©?4~rw#(WJGTLG𠝾¢•OÏ» C¢ÊÕGîš&Qª.ÃåŠ~_›]]ñ-ˆ&ÙJC ™å¢ºÆ/MýÖc”½B@ ë±¢_‘>þ0‰·B@!´2F#K?F«dC! „@µ`¨ñ«¤ˆâ¯–’x ! „€ˆÃù¤Æ3U2"„€B z0ì Ôø1hAõ¡¢ÃWŒû¢ã9‰”B@!М ,É[î0ˆ4õ[0d/„€B   ?=/Qür „€B 6 (Š?N·çEs&¥©?šŸžÈ.„€ME ¢?#CSQ—t„€B@45E¯wÇüüNWQ¨lÄõ[ë‡4µ,áHOjüá (q! „@ìÈÉ ôï£Õµ}~H2Wô¿ª¼ÐRðbKÁÇÑŸ;ÉAM‚¦ ǺƒXN¯¦€â/„@hܪ'|þ8PãÅN¹»LEã}ÎaŠªŒ‚ÊïFªÒA1(¥qÉ-QL“ˆ’nì1tc»îóÍ}Æ´¥ÈŽŽM Qü\EôÈ#àSŒŠ?‰â¼'»)cÇ:|­•ñ6»v·®íE1RãõôÔD%Þn—n›Ø}öÕæÌåñèy%Fa‰KUmÚÃã¦fíõ¸½;°fû‹}ôZ n² ÕÞ/žB@Ô€ê3ÒË+üܼÕcø9ÇÒÔ_·çÞÜ¡ÔîÿË(5ÎþwC×;öëÙÉ8¾OØ«“’ïК[8I¿Ù˜…½W­Ü¸‹~ÉÞœ¹jãÎG; êyç5GÝ?î_Ï<>’ñ÷Š â„€h ƒküåi†¡ ©?º{ÖDñ7ðEh¢Û¸Y_½é¡‡ïVõ‰iÆçžHGwm/ýøMô¢!þèÄ=yS6lßKÿÿS»]†ñÁu÷Myè'²žE¼Ø¤ö SdŒHha 4õÇB¿4Gäkf e*ýtN€ÒŸ1¬oå¾?ÿNƒÒ\‰E²f'ÀïÇý×ýNã÷ÅnwÀMý10Ž_>‘÷þ±’×Úõï1^7ŒèÓפ¦y)$â÷æÊÑ'©ºAíŽ8ívÈÇï6)HFÃ#‡€¤ø‰û¢Û‰â¼çÇÏÄæˆ? WgCúô#ïE“Düþð{””’| äæ)G¥ÖMPd ŠÑÚÄP¥©ßb!ûð0kû—Ý<þÃ021dOjfááÚ¢cá÷ÓŒ¶sŵÇ„Ôú[ôÛ ™o €âWU5·qDÌmRã˜Ga b*þÄ””sxrŒÓ,éDš¨$Àï¿OémÛŽDØÈOšû£òIŠÐÍEç€â‡™ÌÁæ’#\éŠâÉðÄÃÏC#UíÆ3òñølqB Tü¥$8 U³uE\V_~û¡‚•û[%Xñ;Dñ·œ'ß$9å± sðwÀ4¼òanä-#‘ô´$Óúf"·¬ø¥Ÿ¿e'ÂB€ß'UQ“+}«©_Þ±°Ð•Hb™ÀŸj…¦~ó·ÂKò*Š3ê§À怸È!àoê/ÉÂ)ÖäߥܼBôõ¥&'Ò€£:Ó•£N$›-ôIÖnÝCÝ;´¥xw‡æ¶ïÉ¥G^û˜ztjË3Ð"Ã<ôôÖ¼é™{ÿð“ƒúÀÔ#~¥Ï]Z”ê‹O·Hz‰;пïgÔ7óóC”ä¼ÊüQ67Xôó>ìnøà£iҟϧsO@ß/_OKÖl 9 ¯ÏG/¿ó%åòbpáq\8Ù†À+6†'B‰Å$Pþ^×öå=ÜB –ŠPüÈWL(~©ñGÖj*þÆ)5)žzvÊ0·åë·Ó’Õ›é¤G™Éý´j3Í]üx)õèØ–®:ïdÊh•J¥.7eýý#ó«ša_ýàJKާ Ï8Žžÿïr•yè™·æ“MUiú—’Çë¥9ŸÿL¿®Ûf¶(œu|?³°Á7¿õÙÔ9³5ZŠèÛ_×ÓíWœMGungÆkýAs9¬/½¿h) éÝ­Ú–„¯–®¥/^MùE¥Ô»[{sÊ ´d”MAëÆïφü,§C( íÓÎÚ—þùébÊGþ†õën^× om²®Ü°“>ÿqíܺ@fN£oŽfÒSion­Ü°ƒÚ·M¥ë/<ƒÚµN!.ýóãïh ZATô >º ýþ¬a„Þ[¸„r˜× pßÇßüJÙ›wÒù§ ¦ömS`Wz£O€ôuúôÛß*ù['­S“è’ÃèÞ«GCyû*…cy±BYx8}È1fãÙ·æ¡Ó‹Î>±-DaóÎýfT5ÉÊykÞèÂhCw]y. :º+µIK1ïá<|ôõ¯”žœ@}”¸‹>üj©yͦiÔ÷ŒûÙtݧb©Ü-´líVóÚ&¤ùS×ömqí4ptgSéç£pÂþ§9šn¹ì,Úµÿ}ƒBQ#9Kñ7Rô­ˆ1Aý )Rã±ÇóÙá6+"VŒýÑÇ? Ê•ÝO+7Qr¢ƒ®ýÝ)æ9×@ÿöÖç´iç>Z™~ÕýÑ4Õ¬ ó5îãoßÖ¯ÄWmÜEÙ™2Û¤˜û/[» é 4£q•y)ë¶óÈW³M€ÓÍ^tæPú/úõOÒ •ËCƒP“fÇJóÔø¹0Áµ}Ëq-ÿ4(}/ _ü˜Mýzt¢³Žïk^þµøuÛöòòÆt$Y¹Ë»HF¢ÀìÒS’ÌVö+(.¥ÏX¸<òÄþ¦,`SÑ©]+ÊÞ´ËŒãg´ª$ÄÇÑ ^);+7î$.È™Ùa¦=ú¼F÷ó D,B@4ŠYûȦþfx1deÍÖYíå|lï®ôÞ—KМÜ)Д|¨  ª¢« š ÙåæÃ¿ÅïCm¸&çC-}=Ö„çÚí’Õ[Áö¡YÜr½º´«Ué[áN=öúMúÿûü'âã`÷º–®Ùf¶ p„kè¼qó:»NhšgÇÆ‹qq6œW`’0®ÝãñÒ‘d÷‡ôïϾ§¿¾ò߯§ÙÝaV:”r8ÎPîÜÂ*dyë³ÍBw*(1kõî`Au…\UÊ0´fËn³éÿÑ×>å`~×èoƒ™LÓ¤båIöB à“VñqŒã>é㬑?ÄØǸ¯kûÖtúq½Éåö †¼ }ÝÝÐWŸhöM/Y½-@bßÁ|ó˜m0=¥yìöx×¹)ºMZRà<ø€›ÿ¹¹ûÌ¡}è’³†_ ×uÑ!VÄȃÿüŒZ¥·mÏô“¯¤»þt®Ùç¾è—Õ(üì7,O%Î^ùÕÖÔÃG/IÖî°uxè† M[ˆ7çþ@­'"OÃÌöåúñɾƒ”àˆ3kíï¹Ý(¹hѸÔì×gÙ-—œOköî±NûxÜÛ 5þ¬Ûÿðk”ÿh‘òwÌ?<©QÒ‘H…@ Àw¨µÕ˜ˆ*OL4õK ½ uÍÊÙ0¶c%ô΂%æ-\ûÏ+,Fß÷³/ž›Æ¹é¿g§v¦òÂÒÀ¨]o5›Õü´šJËܤڵNE V1kù%¥ef÷´*ü”½‰ÖÁÀkÕ\P°úø7Öñ W—LPzÐw¿mÜq0ß?‚€•%QüÞ²þ¯¹!"poÕƒšde›†_Vo1í¸Ÿ›âPЖ;ˆV’ï~[O{rò0Bbº2MÜzÂÃ%¹À´zó.³æoÝÓ†Ìù‹Ÿ²‰í,~D >¦ q·Û”¹½°¡(¥;öY·É^æ%¨ñk†ôñ7ï£ÔL€›Æÿpöñ4ëÝ/Ñ~ŒÙìÙ9'ÒüVÐÛ_ü„Ú|2ÝtÉ™KôQÃÑG_ýJ÷?7‡XŸÐ¿g m¶àÚ=[ñÿwþôÔ„+éÒ‘ÇÓ¿>ýŽž…÷»s¿ö-—žeŽÜXƒ?ÀBņí;xîyî×xÖ”ˆ¸/:ó8ÓÒ> 5ÉšœOs¿]n=ÚÑ]À… 3ÑZb9æ°hÉäó{³Õä£N2/1¬½ñÑbºëÉ7‰»VFžÐŸö*4¯1·-0¤änŠwü‚ÂU¼9jí8l0øö?›rÉiˆB ™ A}üjlÔø¹Ù/$7vÊ´¯8‚ÙYSÏ佸à¶èl©×Ošòa¿^݆ÝsÍèGÖÐyø+êê\º¬>îª×Ýè3ç¶öàf|ŸO7[’кPµ_»êý 9¯MžúÆW“¬\#ÇŒwÄ…%˽üÎ"htºõ²³ÍáŽÕñâ!Ž5MhĶÅhaÅ_• Û $ÄÛÍî+½pìŸþ×óèÕˆ/]”b«®qB ®ZÊ·ÿÓ'”aÔw"sQUmø˜IÞêÊ(RÃUîT)E®&#P³¯Iéóõª}êìÇÊ’•[c¹Úä©oš5Éj·Õþ©‰WMJŸåbÄØOTçjò¯.¬ø !ÐøƒÚX½ˆºª¡ÐÌ•œèvµÕ¢;o"½hlÿ`û4J©‘C€Çñ—kþD#Æ}e‘#[%©h¿l`r›hi®>o¸9ÑQKË·äW´4<Í5 ùéœotËgv}(ˆâ…§(yB@°XüR:+}¿ž4¨@¹ü혰‰ÅöWE"B@X PXä åÃì*11†ŸŸ‹(þXx;%‡˜ýÞ"sŒýaÄC!PGŠ{+óqÖŸ¯Ž/€ /žø†'²QÑqÆÈ/4WðãYø:bú൘Ɩ‡¼ý «Ôñ¤8. \xÆê¢?! Z0Øô·±†ò¡Ÿ'¾Š '}ü1ñ£+…XÆ–× °¯kìºuØÓÐLw›ˆ™ì,ÇSçò„s•/[“;„¥{­Uû8ÌCûb:ÝŠ4¸;@œBà0FÐÊ|ÒÔñu&з{GÚ‹š;/Þà þ¬ÞrøŠuVdGun‡~ Íe~y:áåë¶Óòuë3Ñ2À]\›ÿuíVës^-Ïꌹð¹{•=×òOr4–Ò­Püå@!P™@àCZ²4õWf#gB îÒSé4­?ýæ_¥ÅkØp7¯þXAGXîB¬v7ú”Á¤a^|v'èinÜjÀóès!‚Ýcã/7÷òG!p ã>Ø‹â? xz°i¼ aÝ]MáYéWç‚W ´®×´¨Žu]öB@‹ê­¡ðýοÅEöB dÇ`\~Çv­BŽG"B@„™@ ßnW¥Æf¸] &ÀøˆB@D «~E±Cñ»#NĆ$ÃùBMîB@˜&À+ó!ƒ¦Èô„>1SãÅÓ¯®dN! B`ÁìÖ©èÞ÷Or§Pá°qK+fkH„t(þz"ŠB@D½ 4п‰b¦¶ÏteÊÞÈxÇ"BŠí{r){ó.ÊÍ/2³q»ý\˨µ¡Br{Y\œÒ“©MZõÇœû];DÞly-=ÿ }¾rŸˆIŠ/ øci~V¢øcò­{¦x ü—¿¬¡…?¯Ä weßnM-%Õ–òËêÑBä Ý›F^=>üúWJMv`½tVƳۛï5´òÿ&*Ä È4y•xr©ñä£ê‡ !«Õ^ÖÀ2^?@6Ãeæ?… ‘˜E°¹ó_­°â)„y)xžÊ5þ>3V|  ß‹Å0rðáÌÁÜ 9º¡ç(ª’c³Ñ¦UwÜ©›ï‹©DZ\Ù›vÒ¿>]ŒÚ½‹6RF›åدÔ·®F£ ëñTZÚ›ŠJÓû_–Ñ¢Ÿ³1«Þ)f+€5©N£%^%bÎÿ??ýsÿÓ!{&íKìK¹Ø{•еªÜò©ÍðPÏ>Ê,Û‰ü/¥/^Cלr³ä?äÌHB † (˜¼§¢µ“ÇðWœalÿ2|¯\XV|NF;f(¤g(†Ò‡tÊðº_€æ±HÅ#Š?RŸL#˵à§lzwá/h‚ßOíÛ}LññÛ9Eô\¨HJZnn.W7:”w½8ÇE¿?kZú™óì7… þü/¡-…Ö%ŸBù¶¶M‘¬Y¨Ø×™xKó cJW"ÿ_"ÿC›4ÿM’YIDD5Š?T~¥>~µ›Rfä|¯*ÆÌÕ.Œ¶lŠq_´=±ååµéü´Êœ·>1!›2ÛÍj2¥_Ut.ldfÎ"–ãÝ…Kè Èåô¼,cc¹àüçØ:Ò’¤Ó›LéWÍ6–&ŸN,GS忪 r.„@õÐë0DÂq¥%yWLj_l‹S/¸ôž»ª¿;²}EñGöó «t¬ô¸y›•LRb6e´ýÖhÞ*œ>ËÁò|°h­Ü°«í¡ñ¬”Eþ—Ò{'ÊNF:ºéšÓqú,ËóÁ¢_5ÿÍ™OI[D,ú™aÉ ÅÀ:¶öÙwõßîTÝ:¦½4õGÓÓ QV¶Ò}úqöýÔ¦Í{!ÆÞÛYϾ úÏü¨WçLØÄaµ½ð*eÎÿ¿Ð§ÏÍûk‡„7!ÆÆò$Ò[ó~¢¿4RþCQn-Š”};«í†|ûƒ3ß÷™ÕPCù=®÷qŸÍ c¯Èl“2ï«ë{4ž‘T°!K?xÑt+×¢Ùz?–û­Ò?nöš~Uv\óo ¹xdÁ—KV›+î±ÌárVþóa¹¿.~`³×ô«æ‹kþëRAqi£ä¿jzr.„À(F ÆÏVûVè>O¯hx}_CÙw2t}†-ÅuŒ‡¢ë÷(Z×çÉ•gZa#u/5þH}2a”‹›¸y ú/—d›ÖûMeÈWß,°\<ºà›_í4bhoÓÐ-ýCµö·ò¿pÉZÓz¿© ùê›–‹G|ý놰濾rHx! @À vÕøuϤ¨ã×Þ;`¾uÝÚ÷{:»¿îó½‹ó>–_$î¥Æ‰O%Ì2qmwë®*,vSrâò0ÇÞèX¾¢m¼^¯×ìï5+ÿE¨Mï³w5ºF½Ÿå+.q…5ÿ*°D.b”fꯨñè-whöO$Å(´Îƒ÷«ï韱ýž¡³ŒÆœ`Eñ7\´ÜfÕv³7ïDÍÙ0ÇéG²ì<˹fËnr{–“§MGþÃ!“Ä!Z…/¦òä=þ®p…òú_žXwí=?‡‘Ô °@º¼¤¬túô z?¹bÿž¥«öà{s©ÍNWD:/éãô'¢|ÜÌÍcãó`Ô¦jù!ÆÖ4·³œùh–g+üx‡ÏlîWÕ†•QƒóÏÓðFƒs©Ž°å?ò+2 H#às•jò¨ùšù-9×Þ;ð{óFg¾¾%¾ìMéܹ³ûíËŸ&’÷¢ø#ùé„A6ƒQ½Püen/ ©á›{? ¢Õ…ª¸¡ô½¦âgÙ9 uÁùçÜû •§.÷ùPÑp»ËÂ’ÿº¤'a„€¨J@öñ|üU¯ŸGÃð½`yù¸aÕ¨ª±ÈyDàfn«©›÷Ñä .°À¸ÏšÉ¯!ò·ôüGÓóY…@$@c…â§Ãkü‘$kCd‘C¨EÑ=VSwCgsf“åe¥Ïç¡¡®¥ç¿¡Üä>!Т èAcø‰ÇðWTœ¢}e>~®¢øcøí¶j¼fe?Újüø¡ñÜ,¶•úŽç·îk©ùáW[²&•¦ÁŠ{þ$°«ÔÇÿ¨^™s%Š¿Q_Ÿæ< üš_”úIÀ ŸÿáWÆ[CuÃchhÊ!Þ¦ü‡(…Ü.Z&ÊóôWRüѾ2?Péão¯u(г¹ð°¢¶”v¨24Vþ1Üžì*ÿ ¿ gþÃ/Ä(b›~>~´4V2î‹ö•ùøÉI?¶ßß°æîg¾J¹1Î>ŽŽï{Ãaq¼ø.²Ûè¸Þ×ÓkŸŒÂuV_±áZ%(ôÅÕiôç‹hõ~/e•H#ºÇQ~A?ïôÒäEÅt¨Ô W/L¦e{|ôâ/¥±‘qÉ…h‘*ŒûŒàézËYðÊ|ÙwE/QüÑûìŽ(y8kº3†Q÷§Òg?N‚rO¢uÛ> ¤ß±í:kØTÚ}à7Œ?ßIÃÝEz^B«6‡oÀ†ä¥!÷2Uåà–¡ ´t—²¡ôGõ²Ó˜^úýœ|*pôïKRéªzágÍüÅE³.H¦·V¹Ì‚@•h|μ4X¹Q´qü©•šúc4õÇÂS¬%¬0¡4Nx­Üô6•¸b½´3gI`ë˜qmÞý*Ü |/-]ó»<—A­sÓ=''ð‡ÞøÍ…ÚÅLÛó}tb'ùiEëó¹[6ÏgÌàÅyü•bƒ*—¿øq÷±òU4¢öÅê|9Ü÷ߎb(9XÑ'GÇ^U•æ }ŽP¾Nþ€"A¼Ô¤¦Å»+‰c³ÅÓ±Gÿ‰~Xù"üýJ‘ï3ÏS;Æ„âï¬Òž¢ŠI„.ëG7OW¿ç_™“ký*¾-.áì›aÛãqB@D#wÀ¢_µJýûšBN¯ft6t-CÕõ ÚŠÞEוã ü3|ºÁá¯ô\‹âô'ò–°"'JIlÅ~¶$ØóR²¡9ù¦ÿüø 9‘7 •î­ä­'ûŠuÊL®¶7ªW}±ÙC¿îõšYz5þ›Qxl±?‡™I*í+®(Ek¾En!Ð PèV¾Qž¯¤øWÝ;p®ñÕNT?¾¦Þã-¡"(ÿÖè·ß±ÿç@¢ÃúÞH«·~D®²¼€´Néaž,Øjî£ýÏÖ<Ú&ª”§P1šû—íöÑïzÇQÇ•J½ÒÅN?îò8¯ÝÐ5°õP u0Ú³/ò E@W0”Ï*·•èéûäŠûuEq)Ü̯é9ª®æxãl9¶LßìËû–ît`¢ø#ý Eˆ|û­¦ÌÖÒôìx& øŽ¢n½ß¾õ@:X°«ú­à7EéÁ¦C:y°hPÿ ~†‚ÿO¶‹Ži£ÑûW¤’íÿÙ»ø:Š£?÷úS/V³eËÓlši -:˜Ð„$†ZH€@> ¡ƒ ˜jŒcܫܫ,Ù–¬^_½ûfîiïdÉzMÒݽ]ýž®íÝÍüwoggvv-ú+pÿK8¥‘YXZ¢›6€‘Í·Ž€Œ€j¿I@ŸõpO€j\¦··$Hƒ%?dᜩ,py3|ÅRÒ§6¥àØÿ«Û3ú·Z’ ~­—Fè[¶éY¸âŒ÷aÙ¦¿CcóyúÞ_Þìwuà>7Á¢µ:êš^OÔ¡ÿ›èµOæü•¥ òüü»4à¸>FÀ°a˜ŒN}pÀ-_½üõÊ7§›#—¨Âõb¿¾•©Û½£_i‹É°¿m‰+‹<­À@˜ßöº¹’KEƒ4<²Íúó`ÌÀËIݰ‚s¡¢næýä˜ùôv‘4ú>)fÒˬŽF€VBŸ.\>ÒÿÀ¨}Å'ŽG@ V¯8÷ØÚÔ¯æfäÓ›ú}jÓk"øPØKŸåM9jÛ=£¿WçÑê>×øµZ2¤kþtJÕæ½ýŒ–h*ß9o×vÊÖ= ;ÍÃ3p8ÚE§îg³n»d&Së4êÅé¾&áAtï!ïýç“Rzݾfvï¦m­³iúˆ ~MOôÄÑR¶ô³ZÌ J¶èØ O$;XÍ&™nF¤¯e÷ÿf:àEú¼î¸Ï"ùbÆwÐËßÁ0*S?ˆÁz¦¿¾×q¨¢áo#܃cùï‚Ã:bÛíÃ*õÈ;üz,µi&¡ÇR¢Ã¢?•jzë÷§€Ã4©±j^B%^}ñï;רC}vW泋.ä¿uÔC5/]ùnþlŽG hê·ZƒqúË*®D“þ“8SùSl\8¥çæ¡Oo®Àå¢$,æ#9£‡í]|ª y ƒ þ8©å) vð‹ Kß&SÐ]kì}Dg"-{ÃDü[$þ¼à¬1|rlEôYÀü'ÆöÁüiŽ@H ™?8ßjGS`†NBJ‡MMîu’_̘¾Y’ PŸB?ˆS =þ¬êu;.Á—”†ô¢ÌÛÖµá¯îÒ rÓáÇ-ÅÐÜ<7tœ¹‡¯}z×ä¦'*¦þhIRóŸé-ƒ2[~´ì²û‰>š=Kþ»ŒXþ`Ž€ÁXýòkYÍÚt™-ü§ßVW·,§kfG&õF`™{õ¡Á =úe§%WkhsŒÜ=‰è#e?3ãàÓ_ ý‘RÆî'þí6+äxK"}T·ÜGô‘?B¬øï¢ùK8A ªf«¢íc¼ÛæçCŸÞxϰ§6^8G’t/7uÏ€Aê[—°Á„ÊN\)ÏCó{¡Æ?\®‚.y_´%ºˆ¾‚ Œ}oFg<ÙÁ/0¾O¼„›Úò?Ñ‘å=Ee>¨ðÚ ¤Áúч³PøGÉ?ÃAÞbdܪ­.óŽ@Ü#`ÂØû,µÑøÙi¶5÷÷YÓ;èðÆìÿ¥à÷=®i}˽úµSBÔ ËÉ/ù›\^/G.ñZž%áô°O—Þ uÍûÁ‚Ö}ΟP0jšl¸‚ÜðúzAFÚç¨eïo¹£û64¦_‰æ}/«å&HÐ7Ý N§ô³ÛeaMÚz,Lý$øé™ôìÁ½Óð^€êƒ0¾¡v:GC­¥W÷1Þò&ÓÔ´ 5ý:™{~"ïs€`µÃžz+<ºÔ /]ÿŒ)—Ç+ùýþ&vܲUê\›óü#—"(QûÚŽñK8Ýhä3E£q8à ô§ûŠý'àª=EhY\h1 çe±b«NPã‚_ƒ%zýå5µò‚îQ þÏ—ß ‡*×¢³.(c`æñA²}8TVUƒÍT{«³àpù hßI8ÍÏéÜÞ¥óüiž>MÙkhÍ®`üÐ7Áš>ÎÝOt" ´M;V«U1õGZLdî§Î=‹:ôl·Ë sSdþ÷T7À؆ePmÉ‘§ùUZsºtž?ÍÓ§){9žH÷•K¸w‚Oædj2dä&Á·£s ÿ•‡þ¼Ì :£u0ŸH°¨©k”¼OU$÷ò{8qƒ€€sø•îpÐÔ/ÇåzÓjô•‚  ¢é%)ÃrÙö†Öë.øµWj’×í>XßìšP@%à¸t¤iÑêÇa{ñ—€ÌXÒ§OºäÌ…¾5ÞÞ>?$Zê¡¤Æ Uîp1Í^1 M˜¿·±[Z–Âû}©8´ ÏÓ7£Àϰx ˉëÜ':Pà'Brr$&%ÊûäÛ& þ@èÞH1~“ü,z&½Çíö ?AkþMž2HoÂ9ô(ˆ½`·  ¡©=VÉŒax)"ŸƒóPÃB£iíðv¿4€T'¼½1ÍVøx›-!nøÕÄȈ©Q}ò47£bêkü9à €š|p•©_r˜ªnËø ÷ Ñ|pžP #v-[(oãy:C€úšRYIñÒ”ÌÌ_oÚU SFvvO»××lû¬ØXA’fÂMq=L~ Ž¥7Ëfn4ûb?Q¾—¦Íe7» Þå…´€{DÒèhÇ:¾èÁU¢÷c¤+ •,Ø™H²JàDë™ßI 'aœŠÚnJ þHø£VNãü䙟MÏ gÑ3éÙdæoŸ ÿx]lŒ)ÿ$oéc³ -±3þgŸ 8ÌŸíÆ¼çW4Coô¸`Hdk-P="3eÉž]«ð‰rkÙâ†'ŽG@A@ †ëÅOF™ÎWtÛˆÌC?C$.øµSŒJƒ¼øó6 =¶rUÑž ü(6ÃK;| WýA¹ihÿ³áŒI ÀCaRœ„案¦pt°Ù\âE§2ŸOîPdžòœhvHó¦w1!lÅiu;™Þ²¦OB?-5UÞ§Îyõæñ‡ÍþQd*Z?>S/ü?zº*}.ø±$0íðáï!'ÑÇç‡ÿÉb=’$Q¬Y½ä[‚¤*uí(°ø Ž@#€ACsXç¡à×|ØýˆJ+üV$¢×ð›BD€Tpj˜ý‡¾»YnßY|õË ñv€ªº½ðÙ²_cþ€ÀîÓk<œâó(piJ€MîRbàiâ4þM&u;:¾5£Öïv»±ƒàC7`iÑyzZ„º”È»ž„¾é ¡Oï hû8¦æ}ÒôeS¿¢íÇÆ©M~¹ü~Òúͺáßj5Ã?ÎI†+ÿWÛ+ýàÚq÷‚øø²ÈM }2ÕŸÍ»J„ƒû÷~‚0P+&×/ÜÌ=¸ÃG€#   4¶ÖÄô²i5•_ɤ÷.øµS‚L £†Ù·ä“Þ½xömW¼ûÕO鿽n–Ɇ‚ ³äC ñãïgãøu£œ5-©~~êë(Ü~$xIèRb89½Ñ47;Ž»Pð»Z?iý~¿(kþ‘ }ùEªw‘O&÷€¶ð°'Ó;™úœòÌñthuÒÿ‰èù÷ÊyIp ÿòj\Ü…Âÿ­‹0ÀQðxЃõGDwþÚ>›7ñ$§ &üY}‹5Ìüy]"°ðåôTOM“ˆG}¥ñÔÛŽÆ´ß¶@¸ào‹HÏ“F ³§ªª¼~ûú5O ã'=ñÆgK¥/>¥S›÷‚Á‘šm2³.žþ zëÖ› “Aao ìcíhß6Hp:e¡/;¾y× 3¶v™4}ú«·îöïØúîÊE Wàt³ T·¨ŽqÁ ðÄP0ùóT_Í€1lâ‚_[EKæWj”Ñ·^n¨¿|ç¿ïžuù/©œ®>TY-]>óxSÛ1ÿòê­ðõÊG0K >îR=ð2vØî–ÌÞá‘íH£É_øèÐGÎÌyþG#ø‰€Àû>$üÍø~z¯¼ß"ð‰žîJzãÿº1X{Ðßì!¹ ð»E04+ R[ÛüiLŸÌû¤éïÝVôÁ7ÿ{Lü¤íÓø ª[TǨ®ñÄà(HA_Pã7î'¿RèšØa‚_Öø‘"j°í_½÷æû“N=ó8yꯞ~ó«´‘ó¥I# …Qûà¶’<®ï÷“PË 3¦<.ïwöi¿$ÉO89ó1o~öLà³mgÏl{ sÚ²½9û±smïëŽcz7u<ôÂÿ§'Àtô+®õC£Çû¿j€¹—¤ •Æ 4e¼÷É‘Oôyë×-[òŸµK“¦OF¨ÑO­ñ·UCFyâ„€$å©îá¿ ¾Ûõ –ÆùI’“–Fö\óªE —nY¹bÛԳυcïgbŸŠ‚KÊéµÃÐîkQ•P]}<ÿÎb¼%òÔVÈ·=÷É$`Õ©í±úšöÛòÛö8\ÛòÛö8œçMW@•ÝÛ*üðóV‹yõEÒ(øý¾úý;¶}÷ãW_|ÝÔÔPƒÏ­mùÕá–êÕ)ª[ÜÌ ðÄP#€=aEãÇi}|Œ_ ßïrÔZ?ÙqÉ_–œuâÂßý¿:vÂàq'¤N+è»t£è`é´òªÊJÔì*Ù)¾5 ¹é¾”ÒÔq½ˆµB!Ô–._R¶æÛ•Û7®Ýƒ§H«'!Oš> ~êÐ>™ùÙø>×ö ž8jPØç­û×øÕàðýnA@­õ3uYÝ!pع~ó%׈W#5òõªJ(zá±…/,$Ù=ÝB,I÷"`BŒ¿ýêsJV¡$¦Ò~çöÝ÷Ös¯#$ôIÀ3Ó>iú$ô¹¶ ðÄ8‚(å²±Y¢1~ã&>Ưݲ%áOŽX”¨>²c2׺®»[ºÄ‘}ä‹"4ö¶€B_ªÆc.ô 'CW~ù̋ٗÿù ,n«9)£0•üë¦÷mü$èIøÓj|ô£:Cu‰êOŽ@;¨5~t}æ;ñS]€ZØ«÷Ý]mîÛ»¯xëîß%ükG‘¸I¢¡&øÙ¶ë)åoèNäb¯YóEYê‰Wþ×Þwäôò„ÂÉWe͸é£#_ÿ»Iø“öOŸÌûl\ŸU<ÅG€#ÐeŒßd³¡ÆÏô®6¹ pÈ5~m"5ÔlêikòT¿ñÇ‹ãy·×+ßø§õelã™/ |.ôµ]®ÑRGõB:ôæÝÏÜÿʼn‚Å6 §L8Ò¦ßüüWà5öÔjÑê ×ôž8!°úå ÖòÚµ™rÏXÿ鿾ïÜ5§£ìº?Ï¿>Šnª“â£ÿ0Ÿm2‹Sd²ðìÛe¹ãë3?›ÔÍ¿>Ê5R*åöÉS]&ºK6ßáè?ák¬&Áj?}ðœÆì˜sÒ|0ørÞH_ÄïãÄåî-98{Yn7qeòrA˜cèÎ2üú©ÕÒ\»ª?|LiÉEáåÿüÝ» Y`Z>øl«î8¥á Àª€TüµË?¹þLWÉHJý#n§âå ç¹óy<;G nÀu«•ñ}lM íÑO…ÌLÃq[àzaüõ×û;pMõGƒô OÞ;»¾IYŠýÈÄËÆÇ€•·XôëÅ&IxË]NuùÏ—| u¾œ#»o9qŽš÷ó‚Û£Ÿøä‚?XÚšÞ;ä-¾ ìKD¢JØêÈzVÓs⺳Yø¾öP˜å¡Š‹®ëVøË8:Fƒ“5~ƒ¯ÌGÅÄ¿*ëËèqŠ­ùo©òõÑû®.£¹Úü-Ùç¿HáW•éX&Áò©FIådi³¨Ôt=Ïé×b!qš´€€$HŠÆ’À5~-J¼ÒÐxpß©È{R ÿ;¸É³5^±à|‡ÀåwZÞýGPIÊþÌÖÀŠŽá?ŠßÁ06RУ‡Æ¸ÆoìÒÖ6w’?hž5 hoÚ¦šS§æ‚ˆÁž>gôøÁÏÍý ¾å´F hê7q¿54ü¨[ÀØÑç±J&Ó'lŸo9¡#`R:ŒèݯԧÐïç99q€€ÊÔ0¸©?Š\“,þõÕêäÁåw¦^´\“„r¢4@RRú7è¬ä#"%FŽx¡ˆ išnNG [P™úÍ`â¦þnŸ¿LA@ò»Ç(/½t®_uÌw9!!°fvï&œž´]ÎŒÈýÿ¨nä™8q„Žë+¦~ŸÃÎ5þ8*{M±*JRPðKÂzMljÑëä‚õJW\pb9]ƒÀògò)NJzËÓ=gßQ_Ñ5oÒÖSùt>m•G€‚ ´ lÐ"‰œ& ºþë•NÈçdrºßEÛÇ•-Q Œ®|ŸVžÍ¿VJBE:ö) ´&®ñ«°á»á!` X$U‡2¼ÇðÜC"`6ùUsøãñ ’ ~Uç×_ïï@‡¬ÁDNÇòÛÓr6kŒDNŽŽ°JΠÆ/ã×QÙqR»Q ¨}„*ü]_·ÂzC•T–…‘Öä#p{øžKKšÃzÏÌP!°ñ¾åØl¤S½/‰{ö«Àá»q~*S|Ìá§Bç‚_cUß%úSIØ`×°}¾åDŠZ*õÈì·¥Eú~GÀhà¬ÅÔ/sø© ¹à×XM6ûEuì4Ø#““£/”zäu{ÕõK_\pj9±F@5‡c]ÄÅ~‚ þXW¤(Ÿ' ’ºa®ŽòqüvŽ’ ~ëúÅÑáÄ9R¾€ ”(ûßá‚_cŒ¦'Eð«l‘ÉÉÑX§ÁoZY”tÄ'•#{p8UŽJO¶Y-bÿm>Ñ¢M²:§êW<1Á/úiÙÚ\V±7zd&w~—ösÔTß;-õ[™Ð†¦Q§ÞüðÏkŸjN¡–Øä-ZiÉ–I<®qÓ'?üÇ{µLo¨´¡ïB=vŽÉ<»ßl2/xé® õ^ž# IsLóŸ|4À€ä1çcÔ¾]qŒ®ÿœ9¯;J½¥·›LÂ=>Ñ—GÁ’bZJ‚à°Z a½ðcôHŸ79!µOZB®â|5’3sŠ›ÍPé <¶_¦sX¡%wXÌ_Òty½bM]“Tßä2a{ð§Ùüéß/>›oËÿÇœ9×¹z€$þJ!ðݳÍÃÎ#“Gιs—[GäGE*c:ª‡tÇÍ7?üLJý_ÃII}†ö‘&(„Qû »<õ­;hèŽwlÛï‡yKÞ”_5¨ .<ù¬îx-‡(ú´öHþ_œ1¦öµ…[¹³ßäræ]¥°ªhOîæ]%Ob;q× ¿s뙳‹HlF)ÐîäÃ-ù3?V’¸1óƺü(ôïÆ5Fþ–— —͘ƒúå¢eߘÉnKQs{ê”}¾ÃˆzOPö%ÛŒ÷é`禌,¤Ÿ°³ø0¼·`ENiyÍç7ýî±ûÿý§ß?‡¸‰‘bÇï3.‚_ì˾ ü*âJðkÝ<.„><3qXÓýלgF¡oÜšˆœ9¬AÁïòrÁoèÂî&æê\A¹—âÐú'(Ô>5”ûUu{¡Ù­¬±Ò! ^ŸÜžÈ;­t?šC;|¾^/”UÖÁኞŸIe³mß¡aüvåØu ¬Ãë‘\(­¡²)0‡ß‰â®_jÜ~R@í‰`(.;ÓøãŒH*“ÁîA2¾f惱×);Z³ýÑÇh1Œ±÷ãr|ŸJÌbv@Ÿ^ã¡äÈj<’`wé·0²ðgt騴eO)Ìýf ·ܽҒá¢Ó&À„aýáž{ôåyðë_Ì€az+÷~¹l#,üq3xðúó ±Ù ûU%ôÃE«h÷A˜·xM· þöÞ{Ý'uÙ7}ãE§`G+ v—TÀÜ…+`(v®(tl{iüÐù›ö…~{ùcqnÑ>ò˜Sû&>¿ÂS¸;hÑ’ ¼™ú©‰¯?\Ð š_žÊ×RòX+âÎÔ¯%ÁOUŒ>L3~Šq/øåÏ€ÅkŸ?»=¥ß£Ö곉”•`újù&2aµçOÃkæçgLF“ñ˜¿l“,øIóÿaݸâ¬ãå+k øp%Ì:yœ|¼÷`L=0Љ|<¸€9ºßÓvoÃŽðýšm0zP>|–‡ÝÓ'…“ÇA!XïÌÿš\ð»~ˆ4ô_œ}‚lnëËåp ¬²3Rž °O–üèåvÂR¤±¤¼ ’œrÄ‚s¦†$Ô¤—oØšðPxÿëòûÈšñÆgË`+šÍI»3¨/\|ÚD(=RÝî{ß_°p†\uîTù]+6ï/—®‡šúfл\yÎ •ž"s<ŒV Âoþò P]׆ö‡ NN䯽D|ôËÍ„þ½³àÛ•E°y'ÁOø´¥{pA|€<<ö«‹å×v´°¼39âä†Ç ÂPÍVe¨…:bŸ~¿Öm+†&·èýˆÉýWŸ N>g%¬Û¾,ØÑ;mÒp˜qüȣȫwK°²Ô§œç‚Ÿ  úô |0tš§8D ¨ñ›d?¾ ÐRå'a/ÿ°g÷‚?3õ8HOî/×F¯¯ öZ~TÍ,-¯†¡(P˜Ðg† È“…(“à¯mh‚=¥GäË$LH‘išÒXÔü ðþú§Í@ãü¡$× 4ÄðÓ¦=² ,ÈË”³Ëí…ÌÔD4Wgc‡$Qîœ:)“àÍ/–ÓaÅa‡™Ð'+ÞýêGùUMhex; 3N ¼õg²ú5vH?8qì`´DxqÈà , ' +-³úáûfÿl:\;k®È¶ÖnÛ×á{ë]PßÔ,¿‹0xý“%p\~šå§B“Ëÿxï¹c@¨Sôöüe(¼ƒ N/w˜Hˆw”Hx¯Þ²y_‰Á–¼0yÔqrÖöè¦sô|´fÉyÞúb9ØQØS§¨¢¦Wýè™k¶î‡û¯=®9ïDù¾ëÑrAÃ/KÖî”W¢»vÖÉrçí£oWÑê£×tXRìU÷ŒÊ±@V¢–>õŽíÚó-í }¥­éÚ7ò§kü •1~Élêø#×*QÒ¥µÖ€}Q²eŒÛ£ÖÏÒÎZa´uªklF­øè™‰¨5“&Až—•¹½RÌû”Ö£¦ÈÌüt|É™“aæÔ‘0oÑøýKËÚ*%]0}ŒGLB×A„iɉŽBŸ¢¦Qðj !ºÓÆÁñp;†‡« Þ.Ô’é^\jR“°S†B4Øñxý²ÏùôG ÒSF ðΖÍìd©(Ú]Úî{Ûò°bÓn¯«Q˜N>@~nyUZ)‚Nuc‡À铇ôqƒåÆVìàt”,߈+Áý‹VmQhÙÈFËKíÑÍ®‘5„õŧO”;fÌÁ®“¿E>òEx @³Ž@qÜܼë€lùÈÉL†Áýrä²]ÛR¶ì~ÚªÇ÷¹¶¯F† ýVhÄé EðÛ…ø ÞCE®5S?ÑDŸ'D``¿3aÅÖWd,¶îûNŸð4ï:lÒP¬kt+Çl§5\§Ã¦8‘áb%@&î3! ¹™'ŒbYe³úy'•ÍÚ$Ä^ùhüáæ es¼’©ƒÒô)%%:nÔjÛKÛ÷–ÍØÿýl©r™¦X•”×Èæ~²@|ˆŠù9°=Ûï¸üL%í`ä5嘬ïÌÿIæƒø¯®k‚DçÑåÕ Ü>ÙÊ™¾9ú+kÑJ8]è\ÐN7šÝ;Jw^1C6õWásŸ~c¾lŸéâéJv5ÝÊIÜaÖ—~È/%Â"2Y†  á²êPgŠœ31„5ìÀegi8eõ–},;ÐÌuªÅÕø¾Ý,‹Óðñ}>\¹P»ËŸÉwV{K³‰wAóžv÷‡àž9q…\À±˜í›=E6÷W×ï“ãöoÞû1Œt…r i…[÷”5frÔci3jÀL¨Ð9ÒÊ?ÿa= ’íà°Íü,?mÉ2pý'ÃÏ ;øÑøugÉf ­ú8ì|wáì2Ó·MÓÆBV†¦o 0%m^Èi‘¥oWlAkA%<~ëÏÑ·À O¡À 5eg$£À X>èšÑ@)%1Ø™ •'ùÆ–há Èꤦ[}>-0”*j vZÈâQ׎ óã‡öƒùK7àô¿ƒˆE.šüGË43¶R4Ô1}ÂPÙZAyÛKnõ€ÛR‘m†!q±¯=lø9Ž@½X^ÀPÀ™PaNpÝjvÁàÛ ´èyF¹¦ßNŒrrvíöו}Ú9kêh¨Gsÿ¿?þ^vØ;ØâàFÞþçL£äícê¹™©°xÍÖVf~ø 9ûÑ839R¢¼Ñ$ºŸÌø¤a“™¿°O6šØðå² òy2ƒSç„ÅX´zœ0ú8˜8bd¦ÐŒ«Ž=3Mà&tf$?µ™¾í{Û>…†jêÑo«lj_øS‘lú'ú"I4.¿gAf¾açìÔ­Çz•G2v6¾YQd-øçð«ý+hè¢Ñå†SÑqÌù‰ª@VãÐÿaEÑnØŽÎd ‹€zŒŸÄý»›‚V +G;5Ç¢)N¯ñ6' ^Qüh_jq„Eh*[÷B"þ¸sƒ|ôq—Â’õO¢`h†òêmp |%€iéw]y–ì-þÎ+§yüä¡~ËÏOƒ!ýóØ#ä-9ù‘Ÿ€f–P„¢]%°gx}þ/÷©²ùšå‰dK>K×ï„ÿ1WžYpN-$g¼7Ñ¡âP"‹Âˆ[.–µ]æHÞüäOV®=·ÝWŸ‚³þóéRøõßÞ2ËŸ1yâR/çmï½ê‡à¿äÌ)è̸Qž~—™š7^4]¶P$ÜôÚ¼%ò-ägp:1’o@¨é‚éãáý+aÕ–=¨á÷—ý!ؽ„ËíƒçÞ^ wŒ¨“CÃ3çŸ2)'!ŽËà¹w¾–¯Ñ•7•;¥E{½@{(¥98w7óË`пÀl¡–6†)*¸ÄÛŽ(?û©bÄ[¢ ªtóÃ\LxåñG¦Ó6ŠDö\'þR®»ïáO†,˜xÏ/ÏŠâqƹuÁŠaÝŽ·e††œ žüÒQÌùPpûQhÚmá÷åHæÑ²Hî=ŠÕ òØ'ÁD¥fœžF‰M‘{í“HíÇiuSå÷“åáÿúXfÇ DÖ 2õ·—Ú{oÛ|ÍèÑO´õd¢Nu¸Ô¸“æÿsÀg“ eáþ=Æf çËçÆ$åDÄ%9r²stá†O`ùÀøþMãípÏ ­1ÿϼùlÚ¶gãÏ>qr_‰?rŒ ñ?þxжý¼=ò[¾|Òô'ìè?DOÀ)ÁsξO|4ò§éóN-™úõ‰`7P=~ð5Ê[¶…}¥Ê1Û¡9Ýj·²%Áé½Çz~Ž_«…>å%Ï„>»—œkê›d³õOèyO÷äfÛÇ #¡OÏlï½ì]lÛÓBŸè 8mq§Î§ùýÕ8,± glÄ)…ù蔨ðäÓAÃ'ês;qA&ô©pùHnægåÍ·ý•}‰4þøK᫇ñ‡Qsœ•>õŽ‡â²ŸPô£éÿ)8ïÄg{œ®Xp!Èùc¼†óë)ä.MÿûÍUg)¦ëX¼COϰ۬rÈäŸ6î–£0’3&óSœƒÎÒs+‚‚§Ú w2ï×w†¿MýüñXôÃóÉcüL&xóž`ÊðÙ(‡ê‡(%øKg|:Èw§É~ᤵ‡}ðŽïS"mÿöÉ\Û—Áàÿ8m €²Ø,¨ñwO1ó£…fþøL\ðë¬ÜO{¯²JßÁŠõ°i÷\qÐ1¹~ÑO3ûÂN90hÌà~ÞG{–¬ÝÞi>=exc®ƒP˜‘–h`ö®íë©ü8­Ý‹€$ûÐz·‚Ÿ{õwo½‹úm)‰½aÒ°à§¢—äg}»ú1(Ì;"‹>5A1~@9.^óïãj{Ir@Z‘Ž¢Ô•áb:—VãûÕ%§*ëÐëÐB;ãŸVõ£¼¥GjäHx»0 ð \fø´‰Ãä0Ä qö@R‚M…{ýù'ËËÚÆ˜ün}\Iþ¾¢IyçÍî )'ùG€#D€P¹Ø¼_{\ã×ayOs·²d¯ËS V=¬C.Ú'™¦±Ö_^Šö…¹ßÊ™(¨Ï'‹×ÂOwÉëX-­û«tÖ, Dûs®€ZŒ p®sOay)œ.-èCËê—'¯a¯^Û@¾Q‡ÿþ® \-ÉCz™á†qÜ“_‡ÅÈIîFph¬?{Ç?ü¬èhk1;àìžT(ÞA}¶îÿ\9ÖóÎÔ1å¥jiÉÚâC•r â‡"ÚÝzé鸴ï`¨Âuíi)Zú54¹Žb—:??c2†Â-€aºx'.sÛ W¿sâyÚì›sT`¡£¢ñ¹aEi@ê›QÉÿói‰@[ž8c! 2õ›â×ÔÏÿ±êˆ†¯Q@Ÿqƒ¯T(\¸ò áZ£ëu‡Eäs¶„ãmÂÅj(Qüz nC‰Ìþ¤ýÓ¯Vµª|ÿe¥'ËÑïèØŠ+þÑb6FJe <¹<èÐGšþð¬à †Fâ•óˆ)ª9ü&0íé³uô0.øuTXmI=uüïp•·\ùt“«®Ô¿ÉšåiÝ€•8–OÁ}Ú[˜Ö¦'íŸ~´@NÛ¤c«¾F!nëƒcâêkzÚÿýâhô*û§™à6¬GOÅÇií!$iŽ ÛšÎ'§dS6ü ¾Õ6kœ5ù/ Á[ö}ª,棜ÔÙN\²ö‰×>‡ïVÁÏÏœSê§â˜?-=|ï³ïB];–‚˜¾¬‹öïµ.X²¿e`ßñ8šømÜÆßEhóÇ ùÿøkoœ5$› ±P>õž’ ÙÌHŒ†ÀKk/©nàY´…Àqù§ÁØA¿€õ;ß‘ ûfÕ wæÈÉì<¶»¶8øË—Ê$ÍÂåg)PÓÜOÄq}úu”Ô×Õû”ÿ—çNUn#¯ÿQƒò奀m8 ·´ê žû)ØV݈&þ yúãCo¸sz€àñögýØØg ®"ã‚›ú#ÃMSw9éQÈÉ!Óä=ðÑ’ÙàöЪ£úL´:ú±æÀŒkÛëQèW6‰pÏ‚& €=”HàßuŸ³@ƒÿçtŽ€$J…,¶0{Ø~_~·8ã,$ìïùº*PøSÊHà™™Ü‹_ƒÿㄈ€$?¹à7žMä%À¹SŸQ(Üy`¡äG9Éwt‰Àßq¹Ý•-S÷hÆÞÓg&Av"ï³ë²09Ñ=‡€ ‚¿À5þž+þæ˜"0¸ßL˜2ìfå™ß¯û+ì*ùF9æ;úCà³nxeM0V-Às|>××_IrŠ{A¥ñK ìíizzòý¼iƒ>šƒ0^³g³pË4n?5zÔàùý"¸Ý^\âv,Á9Þõ.Ù£Ül2ƒ çG£hÓvó‹XãD¿„QäüðÅÒxǧN Ó' »Ý¢tÚ½9Ì“NG:\zúðÆü 0š_5PpŸ÷¿ý%\}Ö báÒ¹%-ÇjHÅ¥àgßëõÊîÜoV£Ðs¡P´AfZ.æb ñÔIÄÚ×ìò@c³>]²¾Çeb/Ÿ1Fì ´p Yb‘Ò“ûÃÏO{Þùú2ìܸт±þ·øF¸üÌwäe} ƒÍ»JàùËP»wƒÓ¹ ²27àv;ÒSŽ-êgˆ¢š›‡`Œý1È¿ùߊüsþÕïloŸñÿÖüŸ0˜OT[s ,a8TâÖ'B·w_´ç,’2½eã)‘ËñÚpÅŒI ÿ.?.­ûEjxð'ZúI“›úu?Ú2â÷Iô+Ž}¨ƒÅµG?•n\ ~¦éø|~Ôt=°hÍ6˜¿¼l¸ÜkNf0|WV{êP$:íòÏÚvu]¼òñ˜uÒh\†v„¼T,ˆ–Ž>½ÆÃù'>/¹%Áò•0ïûÛàüiÿ„ïVo‡y‹ÖâœörÈÍþ ŽýѾ.¤û©S‘˜¸Aþ¹\PU3 ùw#ÿcbÎ{±:ð Fœ·h4š“aGÒ‰PkéÕ^ö˜Ÿ£NE™-_þ¥ú*`pó&¥ü§¿þº¶–£ÐÇzBùž?;† ™?ÚÄøŽ—º-^ü~c!`¤BC’=ú#»4.q#øYÃçÅ8ðnúß­Ú ~Ú ;d –oŠBV­ Eg²3SQø5 ö·†ŸàôIÃå…eb%ü‡œ&åGà»5¡³Àöâð÷÷‡]ÅÇAbBdf~„¼{â;V™©³‘›ó2TV^Œü\)bÍ¿šVV¾YQc§§ÂÚ¶&Œý"z"QgcMÒÉ0¬i\þs·¸àG×L‹ÃcŽwjßè­Œïx«û=Q¦üÚDÛ>EãǦ>î5þØØ•µYÖ­¨"'5|.·6lß_¯Ø& ý^é=#ôqÔá ¨òùaãÎb™N¢7Viòðá„·Ê‚¿©é8ú…²ÐÏêõ~ }Æu:ˆê„|þÆ.៽‹0¥E€æ-^' ý¢Ä‰=&ôš°ÓAtÁNˆ­b¤»$zá®ãípñ0;ËÕ6žë~TÀñ› „€*x)¾çðS¡Æ…৆Lœ¤é×Ö¡vÖj1Éš¾Vj6Y¬8äðá7k ט'zc)üO{?Lü+¨®½l¶2YÓ× ïDY¬8ìðá7«º„ÂÒåòÂ; VÊæ}Òôµ”¶!=¦dêÝ·ŽàÚQÖ˜”?¯ûZ*eNK! ÒøM¢‰kü=VÝôb2s’ç>é77»`éºÐ€Žué¸äkO˜÷;b›hIOI€:ú‹Ñ÷€è%º‰þhÃÀ홉ÏLÅÏ=®é·å‰4ÿŒ´ÏO—ñÿÝê-²#ßǨ×ôÛòOà »FáÜ7ôñì‹Iù³rçºßg~Ÿ`+:€qn·&rÁÏÀ0ê6 ñÆõq^þê%²÷>¯k-M4³`ù¦Ý²uÂçóÅPëóÁœ²HÞûÝåÈ.¾DÑ·|ÓÎ.áŸ<èÉ{¿»ùÂåŸè"ú–mÚþyÝ·x~#"°èÉÄ\ä+¡…·êSﮩ1"ŸáðdhS¿¢ñà´=—Ë {Kà¼v/zÔÛÂÁ¨[óm4ÕoÒêAº£ÕúÄ{=jÓI º•Ÿp_Fô56ûº„B)³æ‡KR·æ'úššÝQóÏÊêP¼Öýn-8þ2Í"àÜŠ¶3ùã^Û§‚ŠÁïGg9¯læß¦çÓ<}­&¢hܶï°L·ßïÊÜ~9!ç¡yúZNDÑÙüSøš§¯å$ÓƒògåÏu_ËåÌië>ør¼GcmhÁO¦Nœ޼ùkšqn4É!ɪÑD´Õõ2ÝD?ñibTÖ6`àž¦. Î)êûhž?ÑÙüSD¾® Σæ#Ò}¢èŒ–Vîñ\÷#-~ŸÁ0©<ú¹Æ/®Á¿„±ê‚ßíÆhyø3at<­'¢‘œü¨Ñ&úi HÝKϨÅp´&s]¤éÖûLæÚ.áŸÂðê!¹Lö¨ùgåNu(^ë¾ÊšÓØõ´šÃ/ñ©|„¸ö¥`„õ‚LÌÜINräÙìõŠšÖö«¤õ{=~œÒç“-Œv=Ô-»/`õð!ﱋ½* ‘ä3 XV]À,cïGÂW¨÷ø1 f4ü«Ë=^ë~¨Xó|ñ€€tãDãcü†a?43w’à£yñrV4¾e–X™úéyzJœš†ê—‘ õ𺯧ÚÎiíb±çKË.¶Ï[à ~¦õ¼#ÏxQ=‰>´WÈtýŒ—p+*»O–ùzüœÿˆË_]îñZ÷ÃýVx~c"°è…¬$lJh:9M»Ïºë·ÅÆä4<® +ø ¥Ä’' ˆŽu“HàÓÒ Ýì~q("ÎTå,÷ø­ûºùÖ9¡]†@³«&¨íãª|‚0'rOé.£²ûlhÁOp*  ž„>Ñ­¢øˆ&EÓqˆæ½ÑÜËùÖÝHqäu?Räø}FAÀ$ŠŠàG§QøŠ–à þhâ÷wŽ€Íš„Sð´¡s"ËAa–ié\žh1&/›øFí¼ŠàGc?ü-ÕÁ°Ëòv…–{É“ ¢¶á’¾'ÓÆV¾*JøË¾€qCúÁ„a…ðó+×b± ?‘Üs,Z6ýU¨¬Û‹×>Ù8qô0và•Œ1÷^ï-¼ÆºÆ¹^û|&æ‰ÝC$¼DrOGü§;XxU*\óIì®òÁm“pÁ`8­|±Ós7É·¾z~¬=ä‡V5wô¨ˆÎ‡ÃK8yEŒº¾S¾Â>Ypáôñ°iw),üi³rë ~9pÞ´±r`¬ªºFy¡¥Ššz¸â¬ã¡äpü°~‡’7’XñÉ»ù=:G@Eðs?X–†Öø©Áþ‚LG²—Ÿý{gÁŠM§PZF·¤¼Þø|™ü{ëËåòc7줵,0j`ô¡aƒ´G.@Ù3"áY}O~ÖDèŸ7 VnyY>}ê„aÌÀËaõ¶ÿƒþo2ÌûþùüÆÝs‘ÿYx‘úöˆöí´4±gDz?»ï– NXsÈEå>8c€ ~6Ô÷.l„ë?m€3ñøÆq/®rÁuãì@…h£=þÕ÷F_Ûú~¨pñiÁß&¦„Ål†óOûUÂkŸ,ÁؘuÒX™õ¥ëvÀ”QÇá’Óá[ƒÔôG‹#¿?~@  ~ L\ão© †ü±¬îSÇ Âµâ@“Ë#?Ö‰‹éTá¿%åU-¿jùÀ*_£ÎÁîJ®e×5ÿmë{VF ¼=ÿGŒ~°l0æú÷Î2ñ/X± WÖ¢5l+äe¥AjR(«‚Šê˜0|ËηnE»ßJCl6Y¹àoAŸ þ«a6|e•ÁÈwN‡rÒSaj;ã†È«ê±G®¬ÌT÷FmÈ();m8”UÉìäfŒ‹×0™á“ÿ'½O6÷3^Wm‚Ì”Bä_ßÂñS˜n+èo9‚± 05û$H±5ú4‡ƒ2‚e]Táƒa½ô=ŠÖ¶¾úýZ8TQƒ¾­›Œä§k ±)ŠB SJMrÊÛCø-äâ·ÃG »Xørz*Ú ³è½øµºfÜÓTÒÝ4hõ}­¿b­RÙÃt‘939Ñ!køŒ”b4m©©Ch‚é‡Ám—œ†ûÆŸÆ9)¥§°• Ù]úÜZÌY°W×í•HNìC("Ls”Um„ãúœWÍüHa®ªžò ž\ œÓóNÿT3T5IÐàÁfÓ·»½pRþxjxcCp¤ØWðÐ#ïíÕ÷ŽøØw¨ššÝèÌ9P›2ò8Ù¦¦e¬ŸÌü•8–'Ž@w#€]ï ©Ÿ þVðsS+8:> ǾœÌT%à‚\YàÓÿþÕðÙ’uPŒ[Jy˜¯ª¶A^W¹Aç;eÕ[ ú(ù|.øhñÍpîÔ§`ö…Kà@ùJX°òA…ÃÜŒQè±ù(u¸³»Z/Ú Gd™eOþáY&xïg)àÇ–eg¥®ú_=и>K#³,°´$0ûƒÓÛ¶m}ïˆ~šv÷ñâ5pÁ)ãaÒˆ@cú}·ZÉž—™{–+Ç|‡#Ð]’4ÙÝ.ø[ÁÎ+8:>øaÃv¸ò¬©°tà æwpj%«Å ^Zù¯%‘×ódÔz¾Ã ?FJË6= Wœñ>,Ûôwhl>‚S·6«ŸÍ‹Å!w¯ÁoÒ°›`ÑÚ?±SºßÖ¹Exs£nï@Áßûaêk5èìà%é¯J¤ýÀY·|´¨.ëf·m}g„¿ŠsõÛ&šÃÿü{ åi}nO`lŸòäfBfZ"¼ÿÍÞ¶·ðcŽ@w  hüɆ¦þàp\w¼\Ëïà¦þKç`y í)ÁÈtýZÝ¡útaØ€<ôö¯Ç¼¥­òéýàà‘õ°eß<9hšÒþÕiXÁ¹PQ·ó~¢>­ûý—00OŸ3 阹QÝ,%ô‰ÉËGÚáµ®ë9uTßÅ“ZèS¾ñÃúÃ’uÛ¡¹%öűîå×8±Dà‡SÓñ ÌlyfÓ™÷7Šåóõþ,®ñ‡Q‚_.ÝØiîÍΔ~FLó| S¶6ïýèg´DSùÎy;¤èX¼ÝƒFI¡Ô÷cñúñ¢5Ǻ̯qº FW“¢íc¨^>•¯ Ò†ÖøqÞ&®ÁÌ~m8×ÁaöÖåáΞÎ=ZÉËh§m¤‰=#Òû{ò>F{$ü«ï¾c_MÁ_¬[DopU>d‚{ô·)Ià þöK :&F¸¼ h]}H4ZÑiPÚãG}½½}õ=ä‹ JáÇLoï¹]}N’ì]¿Y'c|ÉÿêrgeouŸñÍ·ñŠ€4„q.H‚þƒ‰0fb´m-]bôP­>ÆAÂObM£„[0l,ÈLÄ…RDpVB,žÙUÏðûSº„‡¨‡;»èŠ9ÿñ\÷»ªžòçj†2ê$AØÆöù6€€á?i?ìçÀc~ 5«e­Ÿh#1ä)£;ÚÊJÏII°cXÙÅöƒEûŽXÝOô]Á¿ErE zÇŠæX>‡è³€;&ü³úCÛx­û±,þ,! IŠà7YL\ð·):C ~¥áÃ(r&´uf&Ù1Æ<.²¢a/c¢hì‘„T;-mÊ-äC†AAn>W€æfÅò3º3#ÑGtvÿc>ÓˆBØ<…ó.™¾”?+wªCñZ÷ÃÁç5’4Ç„Õ~0ãH´$sÁÏÀhÙVð+ ú…™q¼Ü„aEÓ­8v*àœè"ú ³£æ_]îñZ÷#-~Ÿ¾0 >ÅÌœpm¿âŒ\¢´ó0-bZO@è[Àf³áÏ ùé\[0¤n#Žõk‡b¢…h²[ÐF•—*ÓK¢Ÿñ.µì>†ÁèÂ<Ôú¨ªž…¼[Ã}\—æ'zˆ.»EêRþ6 nÞ&)m±K ñáDÏ ¤‹V·‹¶üÛ–{<ÖýaçÙ ˆúI clqÇ>†Dë­a?±i2 @k‹“À·ÛmRÔN‡ú¥˜ä0»UaO+‰h¡(€Ãr“d‰^¢›è'>"Mj ÐÁoÒÀ<\I0*+/Žô‘]rÑCt ËMéRþ'̆ kZ×%|DúÐaMk!éž›þÕå¯u?Ò²à÷é4ó+?¶œ[õÍM×P¯5ÁO:¸„^í.¯7j}œ™;I€:ìvp8àÄ_vŠr$\KÞÕõ=ªù“¦O4-ýÓmЧNeC‰^¢›´õX˜úýó2`xŸT\o`©¸¬Ç5Òô‰¢§;ùïå-…«{\ó'MDã*èå=SþyÝoÝ`º<^É/ú)¬¢ÜÆ´l[gâG†@@TýÀ=úÛ+T-…ìU½èõ—×ÔÊ šG®ê"·dò$ÁiµZeŸ˜˜n—Ün7äû¼ ‰ÍP†×WY´Ž89ug¢1}2ß—rˆÚnÒHtR…èf¦þHéjƒQý³qaì(ïîiŸƒÃ±?ÒWD|éW¢yß‹š~Oðe¥0¡¡v8GC­¥WÄ|Dz#éjÚ‰b]Ìùo¯Üã­î«Ë¥¦®Qòz¢¶:ÂUhRš×4`¹ÂwA¢yú4e¼÷É‘ü âjnù¨é'''AbR¢L'ÑKt“W6ñiêƒY8·[€m‡%8\~v4vARÂÜnG ƒ+Ò×uzÍÓ§){ä½OŽ|V“Ø£üo=Üc–Aµ5ʬùP‰[ŸÐu@š§OSöÈ{Ÿù,8ŒÓ¿ Ê¿£r§ºÏ*#µ#Ôžxš›´=—“Ì·#ðåóIYRs£¼8¶š çÜÙ¬íi<sÝZüÒáû—¥dfþzÓ®R˜2²0*ÉäINr4¾Oš´…¾ßïÇ@6¢ü\ºžlsAy“ªq!–JÆ8ÐÀ&Œf!6£!"åñã®´EÙA‚'@~š’Qا¦&CJ þHø#D/Ñ™Ÿ×ƒ4‡ö©ƒÃ …p1šÉ°ÓÓfK-nc7í‘Âû}©È‚3†á¥ˆ|V ÎCz&¹mdÉüÛºŒÿŽÊýXuŸú˜Xó S÷é vDÂàvïZ…‡¤åsMŸ€1`2¹\C·]Ôk ÈbLXŠ]Ërå÷_|\4h̸ÊUE{2PðcSyR4¤rT<ú$x)F °óŸ r½>hpû`{i5x1ˆŒ Ï'Òx{ †HÐ;1†€Í$AªÝ ÉN Z(䲦OB?-5UÞ'3?ybG«í¸dChùè†îÛˆKÉ6¸p9Y¸}NðŠNì±'D¿ ò/¶ðoÓ0ÿÍXþÍ1åŸ:TVìHËßÜåüGR÷Ëë=P^Û$×}r,MMÂÞ)õ†¢HÁ²ïþºOdc;"I¢X³æ‡ï¶ã!ü\øGQ¦Z½U•G?P¨^^Ìí••–¿úƒô9XúþfA¸ugñaÔ/·=ÚC>ǼµN)1PH³¡ñs2§ÛÑ‘®¹Ù%ý{ªë ¶ª‚4ùùãÇÅ<‘ÇͧƗyYfù ßI^ÓGŸ4}ÙÔ¯hûÑ9õÉ/Uý §Ó™èÿàÅ:BÉV±eÎ#ÃCõÈv9ÿ=[þ¡”;«û XwB#Î0‘°Üôéƒ2’C*çö2i¡ì©ýؼ«D(Ý·ç3¤‘”Aú©ÛšöHççtŠÚSƒý\ãï°µ$ø‰HÒ1åóûyÿ›ûó_ÝqÙ»_ý”þÛëf™¢¾ô`jIàR¢)pÜ2ÍÇÓ](ø](ô¶gB¢l’ï›9Yò=Ñücï# žÌ÷D›e@f}ò=HHpÊ?2ñÓu¢/Ö)T Hð“óŸ "³p¤BŸÑÏùoéôõPù‡Zî•4»'Ó8P4J0jH!8q͈hRO–½Çël?DÉï¯ýá³yŸ#äåÅ„ íYÑ Äï%Æè¨lr;ϧòu®–?ë…Ӈ髩©hزfå³Â¤ã{ã³¥ÒŸ•½‘5@$Tå}þ´OsœœNYè×Ö6@M³““Qãaʘ!–Œ &6< „ø¯EÙW:6˜¢¨‘ÉÞKŸ´~rä#Oçé:5ÔDc¬S(ÈΜé€Îb‹/üábÀX`ïåü÷Lù3üU÷©Ü·¨Pê~~N:ä,má–;Õ[-”=µÔÀúø¿†œ>@N+Lø³ö†ÈåÉ àíPfÝ—ÀŒ¦~'<µE@K‚Ÿh£^8}˜ôº—ýÅO)éoâþ5ðHWÏš&D£ùSȦǑpeSýH“–»½¸“d-7'3† k»Lð!a'z§üÃqRÒøé¤õÓ¼zjˆIØ[e+@ÀƒŸèêÊÔœbÐöý²ÆcfQaÀù×FùwVîMMèàŠ3\’°ÓK¨Ó&Ž„œÌT¹ì#­=Uö¤é“Ð_½u¯°wÛæWÿíäÖd¦þ\ã´`5zßògòÕÞÒ"Õ&_A¿A»Š4JmÏ’¥EÁO]4ú8]økþê½·>šqÉ/ÈÞxù¡Êjéò™Ç›b1æO’ÝŒ„0Žó“°Û{¸F6s’¶?uü0ÈHOÃ×’¶ÏŒGòaXÿè=”Z|À¯€„¿Ïó§F‰„.5„d^/.«š·I§qÍqC •†+½ýèÌÙC>»=µÅ€,äÌÇÆõ‰oÆ;Û†K$½ƒã“¶œÿž-ÿöÊ} LªûôN=Xv6¥r‹´ÜéÞî*{š§OSöÈ{ŸùDŸ·~õ’Eïnøñ‡µHµ! -[jS¨m¡6&òÞ<ÞÌ“ö@¥e£ Mþ||ŸÑÎV«‚ŸiüôÁRÛگܶfõ®ãgž3Ãï÷MÇ<)Ùi—ÒR‡Õ0í0Ê)߇ H8Ýεæå–È·EÓð±÷²°£cv^+Û¶<·=—NÎëªÙpñŒu~*_7Z¼Ìh¢´bó^XY´/*¡ÏhlËkÛc–/’-…õ¦Ÿœyü>oÃÞm[—üôõ—ß777ÒR¬Ô~Ђ´¥ŸZãç‚1R2‰ÒHfÆA5 mü¼ˆ;*_­ ~¢“óÓxõÖÉÌOóéLMM ðÝÇ|Žûß5v@Á !ÜIÉév§3Åb±FâφƒìƒöÏÇç ¨J;ví-q{°%ä‰#ä÷ÎÍLMM¢Ž4ÔÕ7Ôo)9¬Íu‹Û”…χ1>›šë›ê«÷ïܶ}çæ Å˜…”ÒìIÐ×¶ü¨@m µ)ô]3ù€»<IF2Y‚³Qøê >´(ø©›ÆÆùi¼†„>S™X§À»sÓúíøÛƒ×ˆÊG?–wCOWÝrë ÌÝ—î¨(+ßñßgŸ|-ô»yNŽ€~HIK³ýæ±?>Ì8øî³Oß\³|é~v¬ñ-µÔ&(íî“VOBž4}ü5-ûÔ`ãû\D0Œ–°PG2žD‹?‰žÚC@‹‚Ÿèd2õЙ0Wwè<…#-_±à> JìžÀÑ1þc SáÁ“X–-ë×}ûÔpðÄ0<§ŸwþX“É,[Ëš¢Ðߢ¦™àfBŸZx»§viû¤ñ“¦O®í#FN þ–“è—ÊûSÅÀÆßW?h;÷èï¸Äµ*ø‰bú¨éc¦DåÉŽéã¦^= ~þHðòpnCú˜f\tѫՖBû>¯·jñW_.Á]Ö°Ðiž8†E`è¨Ñ'0æöíÞ½÷«Ù±Æ·¬M ¡Oæ{j+¨] ÁO‚žSËÚ ºNmOD@2WÀõ¯m¿;¸Gÿ± YË‚Ÿ}ØôÁª÷é#¦š„¾ìø‡ÛöH€‘c'œ„÷Ë©üàÁÏ=O9;æ[Ž€‘8ýœó'$&ô—y”$÷Ò¯¿š‡ûZ·v±N9k˜à'3>S HøS'€Žé<×g÷â)žŒ„€è3?úy¢™Ÿõ±ÊWË‚Ÿè¦Ò£[ý‘SG€>hÒômøcBŸùÀIèŸ>kVAbrÒXòjFÇ&ÿꟖ¿÷rÁ ðd|&N›v 㲡±aþ}ûv³co©=`?jÔŸÚö´¥]ãš>‚`ä„Á ª¢àçéXh]ð3ÚéÃeŸzïôa«>úañO9éäÛh½Äív/\õÃ[iŸ'Ž€Ñ˜Nû(;aèЯBŠçá¿$]ÃøÀúÿÛç[Ž€^@«­²¯Y°qSw‚_AiøÐ«ùíSO=•¦òÄ0<{é¥l¬óòÜ} X%‰6 ü†/uc3øõ_z£+0; ešù›æÆæ86ÜÅ•àÿËË/§¢#ÿE :‹Õü¶Ï·£#àáJä‘Íäùá‘›nÚktž9ÆFÀoöŒbbÛ^ÄöùöØÄ•à÷ûý—$QŒ2ó¯EoæMdž‡_å ½šqƒAª¹¶ÏÀà[Ý" ùűŒx´bm`û|{lâJð‹¢p-ƒC2 ÿaû|Ë0:zñߣÑ$*7’8&Úœšš4×è¿bæG‹7ó‡q\~\D1sâºÍoñ¹ûaÔžU×¼ðÂIXçg1&pq*næg`ð­¾ΪĄ üa”¦á¿›\‚ &‚Õü6Ûç[Ž€Ñ¨†ªY8¿ÙàSØðÐ-·l1:Ïœ¿xA èØ'š¹c_8¥nxÁ_·§˜"•¥(›|×ïfÏ^@øÀ‰ÞÌÊø¦Õbâ‚?„JÁ³¯Ës>‹T‰æ®uÞvã.cpƹˆwDIåØÇÇ÷î†ü®#Uç¢9(IFE€­ ~æÝ.Þ©eà„¸5¬àÇ•øo~“‰{ó‡Xx6 ð——_NE6f0V$›…›ù|«{D¿[Ñö1pÏŒD‰ý\žÂAÀNfæÅ0Í@?–„ß>ñ„<¾ÉjC³×ý!^Ä…Jx2¬ˆ‰-ÚWŒÕ£Ø9ªÞ³Ÿï"tx²ÉǬz䦛ŠqŸ×þ·êzoõ¿ri¢R„8[%¾>{…ó¨vô(øåï†ûçLLÂLù`ò°Ï‡s–É –lüäñM¯W¬/;RûêM=Æ.ó­AAjQ:„â~¿×í^øÆsO¬AÖ(=ýŒÖ 2AOV:ÓÕw=8Áj·ŸÉê?:¯f¯àEÛ7Ân³â@]}s&ÖýEòÿg$4qSÝ—D©Xôû¼þÔYÝ7Z½?ºÌÔ‚„ÕGgàg:C@O‚_¸ùæ9N†p‡Åj¾K¥\2ñ$'8Ä´”Áaµ*Ã^¿üb`’»%ypAÞIÁ¯ëöêjꚤú&ú÷˜ýÈã‡]Í®î_½ù…Å‹ç5!G~ü±N€þ PLŸê¶yúô  &޼ÍátÜÞaýÇfßåó*¼f¥%f§§*'øŽ!h¯î{=¾¿Wl-~áÓO_£º¯÷zßq9I0‰]´ZÍ«Ÿ9;÷¡  ÁoºþßÏ4Ù¬ÿ'‰bïá…}¤I# aÔÀ>B‚ÃÞÊ„éC¡ÿÌÛ Õ¸ñâéž’ <þ;{M.7lÚU «ŠöälÞUòøioËyÜ­ïþó鯑%’‚¬ 7eD[¯¸ý73RÒÒ_Ä)ªyǪÿÛö‚¾ (A9™)pçègNoh´W÷ŸÈ]xç/{`ö›Ïþõ+|Œ^ë}‡|ûŒ³ËëÊ“3 Õ㌻ÚwÏé0?¿Ð>Zü²¶sãCÞ%¦'ó²R¥ËfLAýré|»ioéðù¨¾ôJOæB¿]”Œu;0ed!ý„Ňá½+²Q@þï—÷üö÷o>ó—ç‘[þ|øÓ‹Ä´|ú>mÈÇgâc½³Ó:­ÿÄ?Kƒûæ²]¾5(íÕýRIšwíý?ôŸ'ÙÖS½ï´”Ü^¢íãG²FæL»ÞÉ3¨{êÚ—¿œs7 ý§& ÜÍyfúÇ$qGq™r}H'y•Œ|Ç0PýxàÚóÌT_PX>~å¯ï»™sཀྵ¾wØiÄkZHLè½¢Ÿø¥þc43Øu XÿõËÑ?œ†nB@]÷­Vû_®½ïwT÷õRïCCIÁ/ šùyŠ- ~Ó5÷>t–Édþ+6zÒŸ"ج­¬úíò«ÖxxÃ×.D†?Iõ„ê Õ›„¤”?\xÝl ä¤þZÆ€¾IYèÝD¨õ¿¤¼ šÝñýäD'äöJÓ2Ÿœ¶.@@]÷­6ÇŸ.¿íî³[ê“–Ûú0zô›¹c_¸µÎªÕÊ`:ûì+Ž„çç¤KWÏš’–F _“‹¬ºI è•Þš[~WP½é•Ù½óŸí×op2O3=È|®ÙzßBŸè%ºÃ©ÿ;÷«´ý¾ø&ø<žâV÷SÓ3^=z*-K®åzN )Sù°r?äTyµØ’7gp92ᘾ9MŸxÚ¹_5¾ÉÍœªbŽÏ]ª7Wœu¼ ÝsŽ?çœÙˆ‚4¿LG!u&1_w%¹Þãˈ>'ÑKt‡Uÿ[™ù=$Ö]Lñ÷ô ¬î‹äŽ:õ¤Û ­ÖûúòIûq8šExúx+gÞçÚòÍÁo¨òe|;ö5*þ~Ý;dB¶Ê4†€€}“ÙÒ“_¿Qä>„ø^QÕi&vË~‚ø@±ÙeW¿#‚£ÑüXØvÃ2oZÅ·÷ˆ?ùý¤â÷c!O|ä(f«-B´&Qi–³&÷ |ÙÁ'øåë«Yê]ðÓBT›MŹ!}'_Ê  KìÒ“žŒ&÷!gÀÏ<:„'»ú[¡½“n¯ÙòÇòÅf„¬\›}û¨è´.O“¢ä²ïý+¾½‡+¨®Þ·~9Ÿ×/·/ôï×ÓŒK¸we?çÍî‡z£®ÞWÁ†ú~<Þ;XzŒ94hö]OŸ/ZEÕµuAŸÇû&ònc[¢o~\ßÄÉLKïÅú>äÉl2gs¸¢ò]ý!ËY¬yjÏ/÷|ƈ |†#ÿ;š4zC³ö¹“ *q¹Ý¼IZäå(NX¶,ÊÄÞCå->çQe¼¥4@–LŠ Jòe¹o‰Ý ÷U·s4â#­}F„G‚‘ 3ï«Ve¼k£µß·[óŠŠ~Ö·«iî’ „I€f³‰0ëÿšóO¥ŽÅùôÙ‚•4oÙzâ÷Wa,ØÇÃ/}BÝ;Óµ?9ƒ¾]µ… ´PyæåÐÈA=骉'ûßÅIÉþRzô•Ú= Ï1à RhâiÃé”áýš¼íÅŒ+x"ÝtÙÙÍ‚*«¬¦óWÐICzSŽ#ô¡’fñ }zL&…ò²4´oWúÅ”Óy4¦'ý·ë¶ï£—?žO»óg<Ï¢¹x}õÃZ:ã„AþúïsÎ{š5V~¨Òðõɽ¯RfÐ4>C†dç¾FùïÝ¥¹üëBÃð­/¾§õœOPÞŽ,6¢?]<þD-¿þ<ýcΛŽtÍäÓüŸ*;FzþCúí¨]a½ÿõRÚÌKgÍfÞ³s{ºþâ3© m*ýù…i?7ÆiìÈôó)á>×5((0ºulêÛ#ýd¼%5™‚ÌIî[b¶Ù}•Ô171±/u|ç¬yÍœ<ÞP)k?.¨ÁµJ ¼íÒU|=ƒ,cúláJúâ»5ôÓsFш= KŸÞýòGzòµYôðÿ\¦)I¼³…Ýýìå[ÿ|¸üí9XNu’Ö›ðîW?ÒÙ£†hÊv÷ÁR2· øÀâ —œ¥)ÝÅ+·Ð[³¿£¸‘ ¶Im! aݾî¢3*÷HOûÂ\Ú¶ç½÷Õ4¸OWÍ7~0¦OÜ‹ŽûCp¥ìýDÞãúr%*?¿¼ñ½dÖ"z>4Þø ìK±ïH£’ÅŒþ– ðÙ·¾$·ÇKÿsÅÙÔ©]ÖûòÖ¬ïÉÉÿ~qÁéZY˜¿|#ýìüÓ¸ì iùÆ]ä°Û´Fß+Ÿ,ІîûõZÁNntë•>¾¸‘å¥Þå¡Í¼oÀ‡ß,¥[¯>—´±æL¯0&¥‹Œ·„nC ùÒË[2å¾%VƒÞgŪx ¨¦ü»°‰›òFRü`\eȉÀö»{t]x½–ñÁªùú‡ut扃iüè¡Z¸íx ô†KΤÿß´ˆ•óÙ£‡°{ÓZ±©Ä¯øq.*;(zTŒ¼4†{ò´_k ¢¡gçvZoÁÒõ;'¬þžû€núéx­2„õèï.§¥Gé­YßÑNî-(b«é‚3GÒ¨¡}èo¼Ã`h܉ZTèÚÿÇÛ_ѯٺúaÍ6m¸BXO¾‹¿gœ8° kH?9+6í"ôBœÍœwÊqÚ;or¼Ý;SiE-\±™~{å9l~„HO﮸×cíàØ'Ø÷U5ußô7_ªõ¬lbËðÍYßrOK~üânGñîe4›{b`}Ú¬²pOLÆí7ŒÒûúÌo Ø#þ«'JÁzsš$6´ X×BÎT[hÆñ-=?aõBìæñ}oÃÔŠò5 ¾%>!Óû¹‘pç/Ï÷÷¸Œa™®¨¬¡æ,£ Ï<ÁßÞ¶ç ÿ•,;Çì¡åçnd£‡@X΢¡¬SxÌ^À^5íto¶úqí6úlá*M^Ð0Aùé„1ÜC·ŠÊUÓIƒ{ÓEܲ¸jónš¿l#оün-eñ½q£sÙ¤…Õš¬k/´ò/Íd¼¥”ê嬥w wîsrkª ×SÈ“§þHÔØËe8†S€¡°*™¥'¬Ê]ïØŠTÌÛïÂU¯žŽ°RÃħ!}|;9ŠgèöëÀ»÷íkh4°?tVŽ;ÅcZÁÊZTv½¸ûÝûÓ?œKë¶íñ¿ÓÖÉ®¥Ú+Y6 +³«)v°òÜ;ßP¶ÃF—rï-ÿ|ûkÍ¿ú/¸ «^úh¾ÖÅüíê­Úwø·lãNªg%Þ‡•ï±j'UÖÔjÏö,ãŠs¥6ô€F÷ÜÑÓ‚å[´-k¯½ðL­b‡†Þ ÂÁ°Áºí{hÊÇSg¶ƒ”70ïpÂÉcøc†.‚}_ïriéäºÔëŸKvVö?ãa•#•ZCGÄñɼešÂŒ@BÝxéYÚñµÏ³³Òm?›HÝØõ2zNbDF­ü"â ]Õ‚ÚrÚ³ïp¹Öø tn5”{p t÷)§.ìâXk³üƒ |1”p—ÐHn|ÁJ÷Ëï×jCÚÍ0þ­®Õâì-Pv´Z\j²óƬÅܰìǽm'j R(|dkýö½ôýšíZã½ïp#rPk²®½ÐÊ¿4“ñ–R*d¬¥ç†¼ï¬*?™«ôV€ÖŒ½çˆÏa‹ïZþ#*þ°’±K_ñumî´çw냸—ãÈ¢£Õ>«äÄ!½ãŸ;yYÔQ¶€°½¯¨ìÐåyßuSØ"Ïa«ûkzöÍ/ý^ÒN A™>ñß™ôü»ß††À*2ÁŠŸzéxͪߺû¦ /›0Z»þ%wµ¢K ÿdV®àCø'X¾a'枀À±uL¤Ëæ±üŸó˜,¬ðsN&¢ÒŽk·îÖ,¤Níòh »1F¥ŽAÎ:7ÝõËÉtΘ¡-Î ø‚­ò·y\xî’õ4¼_7êÈ–¥ Ö¾‡Ò@#Ì9=âÛMì[c–ÝÊ[-wÒöY0³ó%LLÜÎø9ˆçØé¶0Ñ;PÊ ‡èHEÅ—:Ÿúù-=y¼½5BYÈe™$q 9¯\¢ë{¾pãòsÇð¼•ãèã¹ËxÜÿ#Í ×^Œá¿‘ƒziò8ö„ZùÙÀÊ^O;P^Ñ0ðð°”6¨-Yׇxž>2˜²Ô¿VMÞÆ‰!Šòmê§(ù)0RWD•rÉÆ.Ÿ`Þú`©ƒ**5=äè~îÞ0ÙÝ“°xWqwh÷ˆn~ñ>Æ1oûÙyšåûÞWKèÅçѽ×] 79ÂzÜ» wgŽñ{Ä=Ðq:§DèbõÐM8êÑ©ˆ- *­{»77dV°¥IuèÙÀ¤º@B¥w× êÉ΋¡Gã¬h<,]¿SÜ&¬ „‰\mÍ?¸õêó´¡‹2VäO½:K³¾o¼tœDkßCyƒz6ð‡FM;îi4´oš»tƒ6¡rÁòMš‚dzM;hèg,¯j ¢=‡*š|ïÝIDr]”M¾Ž8~÷tíÓ­î6¿Ed“[©²á^A.|‘¦TÑ‹„Æ–¾›Ï0ùõ‚3Fj]ðh ¾À½`ÿ;õbM^ñ<\Ãúïôå˜ýyP]ÃREñŽ˜Ç›ã›¼ZÇ=¡Èºø>Ø1ÍeÈn‡c«4zX?Ó\Ïà ¹<+ŸÐ¤.¼õêw<æ/V¶ XÏðÛ>î¤ÁtÉÙ'‰ÛMŽ¡n€„0”>E/îµö}î]a¸¥'÷J/уû“NAÏ¿?G›!~.Ï;Þß—?YvŸXþžZm)3„&5Șµ0¿ŽÏëñ´Ÿç‡ʼny+–óÖ+T0,†^"4L­å²ž$ìdàåvÐ6î•:÷§g‹WýG üú¢3éž¿¿ËóSŽ„¬ø!“PԂИ $Ì÷h‚=oMÖE£»µ0ų4‘q‘œÆ£ob«^ΟøŒóN™ù„ ]ý™MV¶ø}C»fÛð¬5×Z†g¹‘Aøæwñ¬fP>OÂäÅ¥²håfm\ßÀ †¥‚ÞL8t"+~XÖ˜Ý/ºùñlW”ß³rEcVÐêÍ»x~@~P¥/ åëW0Q |Íá冼o8O^Ö›'Uôãºm4†Ç<ƒÑÀÞµ.p,7DCfOÒÓVüÀß# °Š>1Ư¯µsL@D—2&V­Ú²›•qóH°ï¡@0© “+Ñ[ð5/‡Ä,AHožË€å}h$7Ëp9‹¡™™‹Wiià ñµÛšv÷Š02ùˆ¼$&Û‰ë`G4|ñÞ«Ÿ-ænñ½Ú0Ób–›Y‹Wó°Ê@MÅwh/ä2%+æ_`,ëáKxî æk`B(¨¥¹!",ý±?é`ˆ ËýÐEù%±¢hd]Êx¬r!¶á|ù¤}+}_‹T¡rG¾ØàÛzÓ:6qÄ-x,Ô­™–hòé#Ø¢1ÑW߯ÓfÔ£1€1åß°%ƒ™î‚P)bvï1¶èEe‡g.·W›}~ ºÅ1QãÖѬÿß]5þýéB/ý@³ž§Œ=ž{à]“´†Ìоݴފ© ]ëqb Æ;Q™ÃúžÂݰëtc¢?åù˜(÷7ž—Ëë¶‘n4\B%ÌĬ€À|€P «ÞùâGZ²~;È3´õ»-"<Œcò%Pö˜ÑÉg7]6Žùþ–îçYÞ ä˰ß\ê_bjüéüÞ^ž¬'X†B·ñ°Í[_|Ç ß9Ú’;ÌAÐOxÜ\OhCÉc5ŠèÕÂ\—u[÷Ðß®ÑÜ}¸ ëý[êÝÒ‡'ÎÇ ëCyÇÿ½ø1ç·ƒÎ:iÍ^¼F<ŽêØ’¬·æÐKD(e\ a¬£Wõ4Žï«$Ç÷c”=èú‰Š¦>ðÐ<ðÂÃŽÃ1 ‚†ÉžÝ]|2´¯QwübR«ÁaÝ®ˆ@xR–&µEµÎz­ë_﨧­oÄsXžpZ‚nÅX¬)4*ÄÚépÇGAXÌ“ÿD8èf¯­«×&4F‡+Ü#(¡gÆnklg®Ú\BoðrÂ{¯¢¹˜…7´éÌc«ÓA¿ºp¬? 𠜋hèé×fÓªõ[6¼þìã·q8ø‡éð˜ì€ÙŸÉì;ôË=óñ¡Î?¿íîg:`H[òÿw¾òÏ]¹áâqÜ8jœ?Áá´I5<‰Š?\Ât¬”Ñçg¸a@^£ù¾µø’!ëFñ–0ì¯Ù¸}õ«Ï<ös~'frú¿%Öiæ_MÿáB¿Â ¼1Ï“ïñ>ÕâËòAÈÄ^ƒ…uô/ê×ïwïвů o$JaÀ¢Ž‡ÒGؘ‡BF%Ú’ÒGø°Ú`QG‰„ÀW°JãÓå<Á³õ7ìØ§-ƒ_=AáG«ôõá¥Ëy%/‹V­,—ŠrÃNZ$J‘@†‚åg8 Dû}kq%CÖ¥Œ·–#‘?SÕoñ+&“´ø#‡²É—&X“ÛÆ¿€Å!&²¡¿sûл®ŸºôçÃxuÁçì¼¥ôh¥6ßÏà%\’ÚFà Ï7Ô¥]aÄY†<Æ)ãÑa:÷ɼö5ž*m76ëzö´Œ›¢ T~­!²ŠÿPƒ¤¢=Ï܇ò—”ZL<õ8ÂORøè?V|H2&RÆ#ϧ§Æoís(K‡]±Î7îyòËRV[b® NžecâZ%éŽÀ¡²Fù×;TJ÷tËôeª©±›_•Ž{bšñ)«øëº:;¶àj6¦H…–9ýö±W “B~Š'øÛRøÓyË5gDFáUòð÷ ¨¯xHU2byIU,Óo^…ä·ø¥ãžØænÊ*~£V|X2‡¥iF"LhÃî|ƒ9>ñjü6xÇ0Ó’—ÀŽ|ðîÂDUáªÅ ü šòßÿæÝ%¥ëÞfST-Rf³gÅl³f&SvŒÿpEãä&#vuÂÚ’u;´5ÿ§³Ïy¬€¯û®ìo`#Ï`‡?zl\£'þá¶Ê:ð»Ùug~¸ûö±W>Ó† °#|Ù¿óÅšœö…¹ìôg‘æG}û.X°|3½Ïûtm_DðÄ&<º!n¸hÅ®~Ë7ìÒ\æÂÁ£/ÆžÔ§{VäÛýlÀ“ÞlÞ[îâÍŠ>žëãݱŸðÀ÷¼yœéÝû?–' C@¬fA„ž½(yœ" ·¼ !rÐ÷~ÀVÓ8ß¾ç0í×y3\³e»×ö퇱š7«z“wxÁåËýþûÙ¢8¥D ¼ŠwœGQ•yâ\cƒ@JZüpÿ*i2{àƒµÇ4pY{Y“poú Þ…„®Ja½8¸—v»ÕÁJa‹Ý¼aM÷N…Úð–»ÁÇ:Æêa!Ýÿë µmTÑðy™½ë]9ñdm'>(ó®NÔ6úÕÎp&z°†ZøîßÂ×N´Š÷]Ç>%ÊÙk ]ÛþôêI§hÏàj÷®8Gkhi7俤!PÆnÁÂMФ¼ ìcŸÀm†[Âã”´.{ñ.†°ü±aQ°];[ú^ÞO ª:NĦš¤âXÄꘒ‹Ç _–Í× S°   ^]ÔÀg9¬hüéÇá16_UÛ¸›Z¯†ŽrÆyEOG.{_ÃdBXó§?PëŽÇF,p*2¬_W´v¬¬©åÝþ|=h¸QÈ;¶ ‚3±,²¸ Ÿ&Én~}Oá2·­]üD8ò_*t _liy L{ƒ¸j 嚀 ¶]–=ÚÜtm.N`Z2õãûœþqP«×>/S±ˆWºSÒâ×+~£Z<Ëxg³sÙðò%Ú8° „Ïû@¿÷[y¡£¬€+¹±€±ø¡¼/&àé ®Y±!våC?®Ý®í-K;û¹Ù=ï7?®£SÙÏz —Bô`»[l6„îÑõì7½˜wvÚ[‹óŒhÑMÚš7@=Oòâ‚Ò©òƒƒIÖÐÏTÏÉöuo·ö¾|&H%x¥Å8?¿Úø~Ó‰Íþgò$*R®«ËØ¡›;pÖºx&tDÛQ ²YR²Ý.Ø—G‰@3Tj\¿/Ç÷›Á³)§ø]¼TMPkãÔây”¤)ÿé”2-:äø¾Œ8Ÿ¦œâw³9Að/I"I¸xÅ… «%劯`]%ÍÀø>wìûÆ÷Ú$Ç÷›A³)×WèÖí|gáMfbMp£»Ž7¾)ãP*Ø ÖãGKpÈbâùf…Lµ=yÙ•êQÉíõÐ'óWP~®CóÚwö¨!MvÈ‹>¦ðBÀœ ¤ý›ÖÐÑê:N­JÙž:rÔW’ÍÓ¸z"¼P›¿]o¶Ñ[Õ˜íZú x/€³Ù±Q²ÓßœScßÁ3‚<,OÑP¦Ë~ vz<æ.Yç+¼¶,K©%»r”,Ô8©8ðÛp®Ýd§mj9U‡¯,ärY#Ëû9OàhV”9â\c@Ê)~»nYì:)¡{¹÷Ö=¶ì¬`ÿ÷Yvµc7¶Xl‚öÁý.ŽTó’Äæ,£¹?n ŸO9UëHôDÅu¼ÑÏë¼KYy•“zT–Љe›¨gå²»cSÁƒ¯Îb§’¼>´µx§¿ŽæñF×ð¦EèItúƒñgô{Må?²Þ()ûMs¹)‹¹.¨£N–­Ôß±Š›È¦4.¡lúetWõjt¢=Îã}eÝl_3%3ËÂWÓ‹ \cš²^‹#û¢Êè–_·ˆ@ê)~î2„]ê"!(|L’‚?ûOæ¯äµófêÄx$b4(°? #”ó¾âϽ;‡.eo{ðÛ/6ɉ$]á|ƒ?øf ;KéÂ’yÔ¥z_8ŸGü.Ê7j¿ý9]iqñœþo8ý£šþˆä±ƒ lÊ.IÙoŠXS<¸'Î|ˆÆfÏ ö–]M_ŒÃ=¬«´ßw/ZS{!—gF–Wűñ¬ô5áæ*rù„[*Kã¹ ²ÆZ$… ±²¢†3´Ô=<ÙÉàz¶¥¤ˆ–=º·¡ô?]°Šw¸³S1[ù¼‘]Â Ž¼±HYE+᥼—&Œ¦)ÿxY¿Àiÿë¥Ô·b++ùš,ÞÈ,ÇhCcãâÍïÒ¼ž8ý”ôGËs²¿×[üØ’6T’²ß©`xt³¬£’EI|y@Cã ótZQ{if–…»ùý#WÊ—ÔxÑ4ãäULh0ŒIp‰ Än ßêãù«·”ÐŒ…«5¥ß¾(9J_ …x@äã¹+hÍ–ÝÚø5!Lt¥?a笤)}‘64:Àøùxîò¸¦_Ä™ÊG›Nöõkú[K“”ý¦è4ÇcAéÎ~')J_p‡x/™WÇ÷‰L¬ø%Å”TüYºîþ*¿Ö€B—”~ïì÷ÞWK Kaé…À xz단–ÇÿÁo¬ ³ôßøl±Ö½KßH~ŠjËèmžo¯ô)½‘ò’¥ëê×ïYÑZxRö›¢£Çãý¯~¤|ÓAÍÒoúVò®Ðëo:DoÏÎŒ²ðõ3Y}Ù&éÄÙªêXpü·ÉC?3bNIÅ}ä*o{ :º÷ëêëifîóÖE¼CW2º÷ßGðžŽò$Ã9K×küÆRù#,ÌÞ/¯vÒi<¦Ÿ¬îýÀt‹kðsúî¹TÁÛ¬Æ#ý"žT?bx('Û·#%†»°ä´5’²ß@<ŽÖÔÓpûgIµô›rÈÝlù·Ï  ^e“ eëeÿl~ÖüóFÝ´,ñc-™æ×)©ø;çû³åPÙ1ÿy°tëa"_=+ýÚZ'}·v‡6{?ù‚ñÓÚ=ð„• VlÖøßà?Z`‰fï'j"_¸|ƒ/ð·pù¦˜¦?\>Œþ~çâ?‹‡[‘‘ï™,û~ ø$ï×nÓfï'b"ŸžPÎÁV,\±1Ê‚z®À„ç6}%Îå1~¤¤âïÜ®ÐH[Šß×ÂwkÖþöÝ©š—Òå8lþïvÞªØêݱ÷0[ýî˜tùƒÞ1¶núó’=#ø«ä¡ŽX¦ßÈé„·Nú†o+=^Rö›¢ˆG•ÓMÝyV½Q ¼UÖ¸Òº,Ì;ÎÂçˆ<0)9¾/Àˆã1%§âªg¾£µúë¶ïÑœó`¾‘ üÁ‰P¬Òoä´FÊV‚:X\þE¾C†2UöF8ÇCÕÖéëß3Ò9|(ì@(ËBí’E§sŸ¦O Ú5ñÎúFʃtå%%^ŽC[L©fëxï¡ò ùã+ìžÔçÒºùËÙ ­Y1ÅÍ9OP&¼‰uþfö·¹àË‘ŽHI`PÊK³Ýθ:牔GýwXçï`>c•~}ØérÞY§ø·ï9¬ÉH`ÚD¾g²ìë1 †‡jâæœGw¤çXçoWjÒº,¨ŠwŠEùÜ.OâŠ@J*~ 2¨Wg?0ëwìõŸëOеʼnÙìκ:î6s’IçòTÿ®‘ÎM<Óï(¯>ßà鈔˜æpUELB¿Ëf>c•þ„2ž ÈŠós¨}¡¯× Kú¶î9Ô,f‘ï™,ûzP‚á‘e Þ[¢ÿ.ÙçYì*8Ë‚¢ª~ÅoR¥âO”¼¥¬â?®?Fë¹[<±ïgr7(þººzrñÌþx¹â ¤÷Àc={%D¥ þ‘ŽHI`/‡±ô½)?¡|>c•þPâKÅwŽëßÝÏöúíÍå_ä;d(SeߟÃÃB±Û‹BW,ÏÁcº–…YÏdõæÎÌ¡^ ÕXºÌ%v2¬–HYÅß½cååh)CwÿVž¸§'tí‰î=L’ÃÌæh¨>ìDœ«h´0ߢ«i —ôDò}¸ñÅòýX¤?–ü-¬a}»ùYÚ²_ÓVéó=Se_`£ÄC†Îëë' nxZÓÜÓîØS+®å1¾¤¬â,Çõo¬ü¾ùq}³.qѽå‰uü\Ä͆ŽÊ |Ǫ«?åŒÒÃ,1TP¼»cÏξ­!Û –7%e¿ivI<šâa„+UiìæWd7B³$¥ÿICúðºwŸû^L[º~§<Ñʇ®ÇÌx/+ÑÔQûÜFá?ߌþF‹ÅŸ¸Oô¤R£É‹EúC„)e_;íø~ÞWl*¡Ce>gVú|ÏTÙ÷Ã'=Æ8ÿöéî®Ç nlf›œØ'ÀHÀ1¥6/};ëÄÁ~˜®ÜÌžï=™ù <«´øq2ĬBù‰4DÊ·ø>…RîKjŒÒ)n©ð]ßnx’kUäóß­ö÷z5æ{æÊ¾>%z4’~̳ÿl®Þà„»ù×s§sWò¹ÊRZñ#›NÒ›:·÷-Å6·oÎú^[â'²Ð_àã¬ôc½›µà]¤%Ò#‰ ñ²C“]+»1>–é9s pÂÉÃÈÒ°;åîƒeô)o3-HÈOÜò_Dãc¼ò>Ñx˜­Ù1A&^xÄ„¹áùVù?•ËøüP$ê$å?€š|ÚåW^YMo~ñ=/ßkœì+0/Ÿ0šÆöMBašM&ºéÒñtýEgi·NÔ“n¸xœxœVÇ~w?M]~+Ù»ö¢zžN|ë;ùêôÈ(wèIZZÛO¸„†<ù67ã¹/)î`¬ÿ¬“ùãÁÒÖ¯¾_翎æ$PÞÑÃpÇ5éÜSŽkl°gWO:…Î9°Ù»©zãÄ)/ÓÀÓîó³ß¾ç™tΫiÈ™öß³ØòhèYÓ¹¿ÙÄ¿ tòeïSaçµç=†]M§_ Çt™]6Tuš‰û2"@3›MŸˆsyL i¡ø;·/¤ËÎ¥ícØ—£—?Y@»‰ŠÝ;Sï®è‡5[›„yƉƒš¸^µeÏ;°ÐpÝr«&Dx‰ÕÉ7-±—;èxÊ1†}ú¹–Qõö ´îöËií-‘·®†º]ó;íÓÒ¹3Èœ“GÅgœßRPÝeZ"bÀÀ|\?s\_?‡KÖo×¶^®b¿‘R ¼Ÿ:¼?]zö(òYZÚÒ³E¼çÄÉÃûñ–ÓѹȎ6ï£ýuEízŒ¥+¦kö=éfyþ¿Hõº›@l¶å#¿-›ñ+ZðÚY<ô⦧ܩ½³gÃ{dÍʧ®ƒ/iòM¸±HO¸qÆòý/Ÿ~ø¯“¦Bλãïc¾ «mÒBñ#™ýºw¤‹ÇȆ¦¯5]îÞýòGšóã:ªá­{£ííÆDªÕ[v7Y6j#XÁ¯ÚºÇ4æ,Y·ƒô¯üÃâÕ±Î{}x‘ˆSßQ·Ð^VÜõ¼u4(·Ý`ZòñUT[ÙXöq¿®ê€¦ôËö~O5;éàÖÏ©¨ëh<Ò »V¾BýFùÉÚÍÿéùñþÆNý-…”OeZäÊ ›Jc3–6Š0îÝ….c«Doa¬Ý¶—Þä}­¬àߨËY¤„Q–6îhâ.þ Î8°ŒP[*¨ ø@iµ+È%sÃØ«îQÊžf÷Hµ;7ø1e9(ä©Ô岨[÷ûß{ÑŸ¶î ÈâáÅêÛ>Öÿ@žÄÉc§SØúyÇNÏ[NÕÜø ‡åýÓùËiÿ‘ v'ݼÊhíÙ~. u› …ÑÞÍk?”*4¡¬þò6:zp5)&ߪ¢–x-î~:Uì_î|ôðÊ)êËD3¸l¨êÅ~@LÊÇþsy’0š—â„EŸˆ²+_Œ¹íÓÕZø{•Q%»Á„0y*/'‹ÊŽ5º¼={Žqxë¶ïõ÷2ˆ°ËŽUk§Eù±™Ü#ÂMÖQ±e‘µ]Grî/ñ³`ïØúÝõ$u¹êfªÞºžª·¬ñ?sà÷¸çÅ޹ѻœÿ¡<‰+gJ¿¼`,±[_A˜ôΜ—`ò.ÂÒŽ­uÀ<Ü›âüÜ&Ÿ§Ú…É’EY¹©ºbGX¬wx1uê{.múö1ÿw5Z eôòßˤ“ÙOÛ†±ˆô×Ò¬Pe¯žƒ¿É¤ô%­i§ø,v¸»ˆ»ý/=û$ÂXe´”ÏJt¬Ú7fÚ¡(ŸNå±Ë%ë¶k–½ÃnãÉ… ³•ªlx¯€7J²±ÒÕ—ô'§¶d ­¸ætmŒ7=òob3F{î*;Œ% dkçÆó$O‚@·…tí…§Ó)#úSn¶OvÉ8PÞÃù6ð]4ŽóS¼8r»hɪ­lî90½âº¨Ëh:n´æ«?pÏ@ãJ gÊJYy†‰ø&ŽªÛ­ïæŸ9ìŠu±Ÿ… @F™Æ´Tü“¾Ý:Ò%¬ü¯šx2ׯ»òŸxê±²Ö§ðó*Ñ>]Û“Åb&ÌZ¾é²ñ„™üPú7^r–¤¨l+k"ëa•¯D½ç*gEÎd+îÐ,Êúƒ{xìÿS²äQvß nkQ{Íâß5ûHÞˆ;Øáq4­CöÇB6kë]Òz†å]ÿ,Üs”™T/Îj_ƒV(”×~tá¿iÓâGißæ¦=Ùö\4¢ª«>JPi÷{Sñwóó|¬¦à¤]j› ‹qY‹gPÄÃySŸƒÕ»ÈÉž{Ã%—ËC˜!Ý® ‡v,¥evÒjÝ„¾±ÜíåLÿý|±4vO•mt&¤ÝHÑ^g-ÁŠÏêÒ‹ª6¬ ¼a£x¢’—jwm&[Ç.ÔaÒ•<³¿–êöïÖRhïì›dæl¸NÑd§ Û]ÚòPÕÑe?PÞ£Ýü¥G}C_Ñ„“Ìo=®ž´wr ûRù¾[e%» 7¾è *Ýó­6 °³o™kuÅ6r9+(§À7ѱš'þe}ñDV··Îw ªö‚™\Kf †HoF(þX ‰}öAÇ,iÎO€›¯±Ê c© .ü^»ÆÎzéB5;7“£ï`¢9DÖÂöÔ助dïÒ“<µÕT¹v müNòÔøæ@äô¢ÍøWë}=%é‚A&¥C/ïѤŽíûE„!¾=vd=åwhî¿ ¹Îý'“-»=uê7Iû‰ç+gÝLx†A‡áT]¾¼îÌ+µþrOúrò­e³¥ÅyLRñ‡óÂU›èšI§Ñ¢U››xÄçó–nÐ~8Ǭç1<³zÎ’õ¸L:ðþ 4àÿEûß‘ÊÏÖ~ðÚçuñ·±£XlÔñ‚khïkϦMÚ31!-É;üc´DÏzñ&Bí sè¯w´ôIÊÜߺäsñ;´ué³T_íúóß¾}~“4l_öÿ¿`d2Û¨× 7òÀ#ÁgÀ=õJ‘H“jz—wQ—ò˜`ÒzŒ?–Xî;TÁ3ø÷ÐÈk¥ƒ…?¤O:\Q©Íöö¡È{k©ühî²Ö§Ü³usç}›>"ü2‘TÅu/fЈ³§Ènþ¤ŠAZ[üðâ×øãÕfœZo$n»œEàÑj6éxÜ··H¿•W!ÔsWc*øŒUúS!½ñàQä»ï˜™²¯Ç5n2~yp«öô( ªz…??tóKJ&i«øQÐÉÎÊÔë1¾wHð˜eó­‰i–ñ¬¥£þ›ömPcM G*à3éo —t¿¯Ïw‘ÖL“}‘n[ÂÃéõ¹™Ö¿k´s§šŸòeáó§ìÙک味À[4Ãh8g?i«øƒe¤Ãj&Šø }NÁ^Jò=ðsl±…É϶S-{ «³ÛU(øŸ±N’³5éÑg²ìxÔQ6¯) ßÁQ°ðâq¼ÇT/ Š×åŸÔÇ5ï̱÷©Œ^2ÌÐH{ÅÖ¾øçXµÍzŒ<Ù¼¡]Ò¹(ÇÏwèÙüM¤¿Wç"nt+T’þ†)ÁCÏ]ð>c™þøpjüP…Ü㘩²¯Ï¥æx(tÐ=HÿŠ¡ÎÁ›ª¦AYPÕk°ŠìæP$õ˜ÖŠß_ÐY‘`S|‡•Çˈ7,1®—Hðf³˜¨]¾ƒ¹nl´D*%ƒŽ…¹ä°šhk±q+:¤üÙ9b•þHqKõïD¾C†2Uöõy“—ö¸Ž×¿f¨sðÆ;|§tY˜õ¤u42¾J‡}óZº|j(3”™´Uüþ‚ÎCýfÛ7ñF;8vÌ6k–g;FÊw±™J¯b‡Ÿ_LUi —WñÂ@Ú÷hO»ózÒþcú _à¯g»œ˜¤?\¼ÒåýÀ|ÏDÙ×çe‹xpàAw:âî¥Ýçà ¼õ,Ní² z=¿€*ªòþiwì©×ò˜<ÒVñRX:Ø?øÖ·X,Ô5­~Nu»…·=£xO6Þì§oÇ|_Á;Ò)é1Ú«9, -î1žÜml'i|‘~~÷Gv“ÓôGÊOª§Ï÷L•}}¶„‡­þÕÎ É­†¾—>Üxœƒðd7§vY˜;wœ…Ç*üŽØüz-xÉ0ÃG r~\ ýB´ò}JßB6›VþY¨[ŽJ.·‡ÊØÑŽQ¼€§í픕>yÇ?n¨€‘–pyß YvÙ§=•gÓ¼ž .®ïƒŸ²¬vÔ¿ƒ#fé+Ã<0ß3QöõÙÓ]s:æí@+j/Õ’ÔsðrÌÛ‘ú·Oí²P·táD¶g´½¸ÓqÏÄ»˜ŸT`eä~Œ¦øaƒóOÁ1j2™Þ.׬)|;okgej·Û©(ÛBìnªá‰tGÊ+“jùÃÒà¥{ž‰:±‹SŸ¾† øG:"¥@ zt( þsh{aúº÷ùI·üaéƒðôÁ­AÆ gÚ/È+ ¿%xǨÌ÷L”}=ˆ­ã᡽îa´¤æÊ¤Zþ°ôÁx‰yYPüò3ÓãìœW(ù»ùɤ¼¡(ÓŒ¿–:XBÒð^l׌ER#ê©qº\¸Ž\ãñÇ¢{–~+ü,G9øW[›Åcý.òzë©”÷Êp—VPï¨gç÷IÓG÷>,ýÎÙ*õ(rhüOð ¾a­#‘R0 v-$—ËEÛ©?UdÒé»çS—êÐ÷”—Àï0¦¿¨Ç8îh·ô‹8õ.Þ Ül|Ñ/wâÝ›Åïö¸k£•ÿ`ùži²¯ÏÇPðØëÊ[·§öϨ½e—þó¸ŸcLÝûè}ˆu]Ù÷x<5‰h&wÏ£ºœù÷â|ÕYþˆÉlán~ãNª|fÊÑHŠß¹×å9Tq´‚•âG§•÷"G¥—““MuÎ:ª««ce뢞j²’“'÷Ø »‘eq¯@ŽÃFvvcÂŒ¸8ÖécÉfï;y‡? wnôÈö°¥Ïëus”Ë<‚Oð ¾EW¤¬´„AÿÎùd3UÐβbš1à2êQYBýË6QÏÊdw×E]›ßa>–ìaö>&òYy£ŽÙ_0Tq¬ZuÕÕ•‹k#]κòhå¿¥|Ï$Ù×çqhxÔQ©»=-ª¹ž:Y¶Rwë*>n"›Ÿô°NKö0{ù¬ ÊBìëMöëëºÿ­â—hPŠ…O*øŒVþ[Ë÷L‘}}ƆŽG½V”ºûhÊXárj§Ê2e™Å WÁðçÁ/øÿÑR[hËkTétQëúz“…ªmÅTÕ1 £úÃö-/ôµ[T®8‰ ”zʵ$6ý#•3r÷–M+™´öDØBÊ‚¥6®÷ôüxÁg·>ý®ŒVþÛÊw<7›Ó[öõ¹uºº€û¹ ¾­º ¶r?:G^wÿ¹þD_²Pâ\Ùß³}ëæC/gz¶bz>ë Û¯×5rzëL–Ü7ˆŽÅ4XtIñû…rÞg®é?bdé’uÛ‹YñG¥yý-}V¤š;\VúB‘ù* °pÁf«ßåⶸÛMy<㽬,¢CW÷5ø@|¢ò±Z-<´€áÁÒ‡Ò/,(ÐÎÑÀLìh­}}¨ØlNÊg x,\K[ì]𖈂ºýüVÿy°“d¦Ÿåˆ—{Ž­YòÝVæ ›€ãç—·`ü&ðžž7ð9有G™ïühä?Ô|OgÙ×çc¼ñØ´âtÝO»Ïx’ȲÙW½ÞŠ¥ ¾ÙÀ|$DîUÕ}ƒH3÷î}8éŽc fqËcËIñƒKXcZåwdÿ¾·Ö*Ê-[JЀž[NAO„‚·«6Ry)-oŒŸ£;3ýkÙâÕÆ?Yñy¼¾oÃBÿh(ä Ì*fæpÇé³öyLŸ»÷aék]ý~k?ºI}Z¤ºñÀ>¡—"%;ýŸµ[÷(%[·|ÁüqŽöÓdŒÏ!oF ¿Ü33à—ï\­üÇ#ßC,ÙyŒÏxâÁSƒü¬<$!ûûvíø„ƒ\Å]îgþ½¿œÛô.z_2N1ógOÆŸIñ ËÂé^ðɇo]zÓo¯~kö÷E÷^w¡É_»Q <.Ðw ¥ÏKüx<ÝɊ߉I ¯‡wÈkËâ •,xtßû¬}ß*TèêÏÎvh?tñã9ø‹5Ń ®TTØòNgÉJ½ËC,?^¯Û}lñÌO¿d^1H‹Ÿ¨…̉d$ã(xÐ便zðÛ³_ÿó˜ÿ#žÎ¨@IDATühå?ÖùHÉÊûÖxŒ¹ÚT6_Ì-•‡Dá!dŸ§ó]8ããä*îr¯8w\ÊöRqþÛÏ¿Ó5—îj¬'ZËù,qIñ#Õ°| œõee‡*7­\ö¸râèÇ^±H½áÒ³¢’Qà TµsVþ8‡âÏv84¥¯Mü«¯÷Mük˜ ¬ýHzýŒ}C®SáE ÝøˆKö`õc"¾ÏiÏÒ±¦XcPaklœthß®»" "ÞD§r³ïp-_<ïÕššJLYÀrü„ò7’ůɽàü‚oÓYn‰Vþþ™,ûŒ«Ÿâ…GAŽ? ,‰. BöWÿ°øùÊÊ !ûq—{ý¤>ÆùeþùºX¡‘g@ÀˆŠ–Ô¹pÖ§‹òŠŠ^áóèCRyáX%Ë^,C«_,õƒ†¥_Ïü0¾É°øUü±ÆÊ?’üBœÚg²ÃâGœ°ú±F1”½UëðÍà_ñ¤XbPfoäµSgÍAW3Ö“‘~X;¨ø–nØ¡l[¿ú£‹æ¯`ư†?¬Í‚|AÎŒ¤øýrÏ|i¼‚ï¢a­]­üÇ2ß™Ÿ(ycüR<ð(ÊkŒ=XyHzÙßµyÃ[?Îýêæ*!rÿÅ“¶Ánël …<5ë?¼¦¦yfŒ¦øÑ:DˆudÖê™oþ÷­IWý|þri¹zÕÄSL±óG!Ô¼yA ó8¿¦ðyBŸOéû&ú–ûñ,°HÌ}f„x@Pè8…â×~|€ïÚ7éO¼«}ç>~¢Ã ›4P»â"qÚä(Ò”¨ôc\Ýû°ô·­[ýÉœß›Í UóVŽ+ÈäÌ(ÖH3¹gÞ°Q¼}ÎGÌ¿Jú'±ÿXä;ó%:ïCb*à¥X⑯›æ¬<ĽìïØ¸îݯ?xƄɽÇãþ­^•>9ïžšÆe?þòÄUñk?¡µÏ~ûµwF?w¿wÌi7?õÚìÂãúwWGë« ïß-âuþ(„Pº(øè~‡õÉ|b\_oéÇBñ#>ñCœb²Ÿ¸— aˆƒÝä>LN Fˆ$Ò‰c¬ÓµÊX¶„̘Èçu»*—.˜ûÖªïbùd›2àˆŸÞâ7šâo"÷Ì«¶—ÒƒŒ:süÕ,ÿÑÊ´ùÎ|…D‰Èûiã¥XáÁ£v~ VâG0Ù_±xÁ–/šK?ar?÷¹¹µÕG~)šÒŠÉüœoÔÖ‰<1FSü€FŒóc,üèš—ÌýjÑúØxÚùS.ä¥Yçr_ÀIÍsØÕ‚%˪›iÆDJJ>ð:’pEß^‹ûF9¦9ðZð9Ž ú×óÅi³c`z¯›}â ¸µ…‡;8»a¶8*wn\¿à»/fέ­­•ÞQ?,$†ð-åÞȹ֜·x–!†{â¹Mgíÿwì=GÐЖd`Œ¬ø…p£ÒÓŸ£DE®Ízæ£6ŠŠ_6”ÒáÆy ¹ ºñ…‚„òG׸/ÆõÅ·|Ë$Ò$åÞÙ2Sñ(Bv…Œ$]î Ì\ Ï*<¶¯šMîæGq“ddŒ¬ø­taýÀÒ×+}1 _*}!Át ÁñA&Ä2¢¯!#¨}pÄÏŒné3‹~’rï‡"eOâU„Ì IªÜ´ÍïÎ9$†X?Ÿxg=†_%£+~*m:ްÜP©‹î}(|¡ô…JÅÏ $˜Jä$dC(QŠkñÜ÷vjý¼K¹O­|·ñ*†‘û,S¥µ³yCg‘5¼É¾êYÜ‘G£"*Šø‰®¨aɉ‰MÂÊ _Š{:ò•è}7Eˆ£øA6ÄOÜKu¬‘4b¤Ü§VNÆ«<@@B¾q2£¸wâJƒl ºšflq7ÿ’‰wºÄ5BxÌH%Å/-BJßw%»ù‰>bL=ÑY$äB\§ÛQ¤OÊ}jäl<ËCÒåþÛ§»;¹VvYaRLOúÚ§âŽ<TTüxŠ 1ð¾¼N,°J%%)÷‰Ã:’˜ÒºâÀ䇆F@*~CgdN" Ä™OYOáÝ÷üúÅtÛø;+Ä?fC2Š?¨Ë8%‰€AжÜõ¸_fv4}ÀË÷>ã.þ· žd#HÅPe‰€D U¨=\‚YüCÁ/+ýc”•usªð.ùŒ ©ø#ÃM~%HRY›/WUõ7"!*™þ0ùÖÚ=âZÓKz&K¦*n~ð±“<^ÏDþ¶—ª¨]y‚O^ką́Üë<õnóüANx[ÏJEUàd—Ùdþâù‡î“î?ƒà$o­JÅŸÆ™JÒ¦MûwÖ^×Þ[òÞáöº»°²W³\5ÞwµbóÔ‡Ü#Ô¥rïY­ÅWo¶y«-9ªÓšmâx™zÿÿí÷ªÊ3ÝmÝÿ1mÚuÎÖ¾•Ï$ B@¹ýö§³ŽeUýÎl¢ßkå7§±)U^‡é˜b¡ºËC;ÓÎ˃›ìÞZo¾Z¯æ$­,¬{w˜m×Îõïp#¤Ør¹ßJY…S‰ÊµŒ&™HÅŸLô“÷Ô:o¿k÷+\ì»õ¨Ü­ö+ÛD=+w(vw9¬i•fÅN%y}h[ñ Î%y=ßW_òûëïvýË™öÇÉõ$‰@Â`½G¦kïz`bµ½òE“jêÚÁºUíaYE,››âŒuyÐÊB½šE݃h·ûøÎ‡\ýßçN\YصcÃ_¹°Ò<®Ï-Ë•“n-;–päe„IA älR¸“‘Æ Vú·«ª2³ÐYÚùÂ-ФmŸ(Ê7+ý¸Å‰€>âA|ˆ·ÐYÞÉd¶|vãýÿ‡-?¥<Æ}x s–_ßû§;¬6ûŒ<ÓáNc³_¦Ó¯*=¬«ˆ•~Obs a#Ä…8ó”# ) Ú¸>©kôåÎIw»–Ç&U2”T@@V´©K±åQñ)}zº_ÅÓÅ›ß7w©NÎþˆ÷’-ï™ÁÛOÝðÇ?Cùú‚&I"O c5ëµw?p‡Ùl}¼»urfö æö–]ñŒ7h؈óÌœéæîÖµq- X¯¯ªÞWlí0ù.ï?ŵ‹T*Ü­>‘-ý'úUlUÏÙ9K±x]âYRŽˆ|€vòø¯îüã$fr)•Rr$#"…lAƬWßò‡ó­Vû£¬ôÕQŽw‹’¼ò€¸Áx‰GY˜ù¸½¹ÝŸrr™•þ&kAáõ8—”YHÅŸAù=uê4‡Ù¤¼\쨎ÌúÐ0å¼ä›©v{ìÊÂì§ó‹I­ŸÉJ¿°f¥ÈfµO>÷¦ò£ Æ^Fgdåj€LH &O±ò;U1u=}÷|s²-ýÀ4ƒŸ±{æ™™¿.ÝŽø;~މ§R>’×Ñ"™‚lÙGœ}ÖoU2waÿÌœLK?0AàeDÖ 3¯©IY˜ù÷þv»òcVúµ¸ª5+–ŸL¸Ý¹=0nyÈŠ53òÖŒÙl¦ß÷¬,Q“5¦ßÔà üÙì6ø ·gþÆk‹ùÜðh倹„l9²s²Ó‘gï'cL¿-¤Àx‹¶,Ì;ÎBµÛ^çõ2g4ÄéUTÓÏ&Þåú¡-äóôE@*þôÍ[}ÊL?¿õžÑlAtæ%{†V¤Š©ÓÅ¿ºi '@Zýú\”çÑ"à·ö'\råÉŠÉÜ—ì¶Qy¾¡S þÀgnaÁ¹Ì¨´ú [)ÅœVd*«¸c§s g¼Nß°‰o‘–l¼Ãz>bü‹²g¾§¦Üí}V\Ëcæ" ú罨ðzÃ#_¼×éG 'øŸlõâ°¬ü“ÝýÑ‚*¿¢@¦&‹¥§M©f¯|ñ[§-ìày »,,Þ5»æð®¬ô' “ò×Éw{ï×ò˜ÙHÅŸþù<6“Ié’íªJ‰üŸŠYéÂ| ‹?%øNQJéúÊO¦²ÌVKÇ,å(†&æ1¬²°ðÿ¬Ø?‹Çô'4&Lù3¯Õ¿·ñZže:C•”ޠ³˜Hɳ{]†¯èàÓd6aI¿ç(’¢E@+dÊnVL¹V¥Þðå<š(´²0ë Ûˆc•Ç>âôõ`)¤Ü;ùï_ŵøç•I’$a#à/ü¥Ö˜L1‹ü7+ ³Ÿ²?sÇû/ð3,}õ‘B-&ë%ï¬ß(nÉ£D ©øI¿kQé¥bÊï8J’Dƒ€^–R±ÉÏÿ»Ó:dçä”þÉëvcS+ÎÝûå«í~5öÎ#•Ñ%¿MüB“þIÍèÆMqš²äuÖÆ \QÙÅ+|nf! ä)nåAÀ©(¼Æb#+feCãý?÷š'ådy’¼Ô[t±Âç½´•?—ç ^«/n VäQ"Ð Tlù6K„¼‘úÝý4uýù­dÎÎ¥7ÜK#__D#_]Hÿï%Ê8Bc¢ý„KhÈ“os=÷º51‰–±¤)á}8åexÚ}~<Ú÷<“ιq5 9óÏþ{9E}iôÅoÑy¿ÝJçÞ´N¹ü*îvŠö¼Ç°«éô«¿äóðËÆÍ?1ý÷}ÊŒ¼ïû¬Ù{‹¹˜Í±m8¯Ñ\*}Š<¶…€Tüm!”ÚÏïaZHoî ã)oÄ:ôékdrd“­CWÚöØm´î¶KHõ¸©ëU¿Ñ¾,;ƒÌ9yT|Æù-„ñ혥%b䇩Š@Ô²SÔeµë1–v®˜®aÐ÷¤›iäùÿ"Õën‚I]Í:zh5-zãšÿß±lñWû {6¼GÖ¬|ê:ø’&ß´u‘g)0¼Ÿú¼ÅL'á]žŸ€¶ÃÞº÷W“ïVÏ9ïîº-m…!ŸKôHůG#=ÏQéñ/| GG§KM¥ó>#÷±rr•b¥+U®[FuûwSÅ÷_Sîà‘Úëhžù6u¾ôzýçáŸûV!4ð‰~Œò‹ôF@/K8‹úŽº…ö²â®¯-Ó¾Ëm7˜–||ÕVîiŽ»îmþö1ª.ß®=C#ÀšU¨½ƒF®•¯P¿QØ|2t2©îl­ÿžÿiÝúªòœ=;wðä»=¯†Š|S"Ј€TüXȳVÈî5jwl úFî°1TµyÿYÍö ”Õµ)V»ÿž<‘¤2yí‡Rå‘uþ$¬þò6:zp5)&xnJfk6µïyõ}+ut mûñoþŽ^C0™Ã+¬ó몜ÊKË×Û†_øGïmn©,õ*O$a" '÷… X&¾®Ø²ÈÚ®#9÷—4K~ñØó©pô™´ùO7øŸ9ð{lšØ;w'çîmþûòD"Š˜,Y”•Û™ª+v„Ä~v~O:qÊ‹<¹/‹n›MVø¿«ÑÂP(» U•mößoí¤Î›»÷•ÏÍW/ßìæÂÄ] ’$Q" -þ(Ì„Ïm¬ôAõ¥›$Ýû½þçAÚõÜŸ©zËZÿ3WÙam ÒÖ®“ÿž<‘¤*ŽÜ.뵕ûBJBeéFúòùÚ?>8åò¹ŒeøDÎ*”!•²òºjסüsz³KYéW„ò®|G" Rñ‡‚R†¿ã*gEÎd+îàGÂÑ{õ»÷o´÷õ¿SÙ¢Yþû8±µ×,~ñ]“‡òB"b8«} ^XýáPí±žð.ÙÅ”×a¨ö©=h^W} œ ä»˜" LáLÏÀ°NV|V—^Zí{ЀûŸ£ÊµK©zëzm)–ó™ó ž÷ÔŽNžø'I"êx\5<À~r û¶™”âî§RQדÉbË£¼öC¨çˆkµµü5;µos úhÇê†ë6”/H ã¨édÍÎÍäè;˜hQÑ)祰ž<^û‰ôîxú*ÿîkÊé;„êöí"µÞ)É£D ¥8vd=åw8®Í4س;Rÿ1·ó¾>䮯¢ÒÝßÒŠÏoàsŸ3½‚õÿ^·,m‚)_ˆRñÇ Úô ø»ð¿ÿ¢ýï¿H>þö –B…½•u¼àÚûÚ³ÁË{”D`ë’ghÌÅïÐÖ¥ÏR}µoè ùöí¦þ*öoþ„ð3[Ù£¥»ž×Ü{üé5™mÔë„iÓâGü÷ä‰D È®þd ž‚qbò^Ù¢ÙÔáì‹[å¾èÔ T»g;•-žÝê{ò¡D •8z`%íÛü1õrUHlÃU¯^éã£Îý§h3ùÑ0$H&ÒâO&ú)wÉ¿n“ã²…3 ?ItC`Ýœ{¢JÒ¾M~’$ÉF@ZüÉÎøÇϾ?Ø«®ê­®3Yqnxª7ÙT×SÍŒj¼7 Ï·dÐÐèeIu{ܵ.ÕføòàRí²,Z¬R“9©øS3ßBåÚ_±y]žC5Ö\ÿu¨$ã½jkŽêª¯÷ùFmd %xodWžf²SשׂpªÍîˆg§š/Ë‚Ñ2% ø‘Š? 21”$¸êêö9­ÙJ%þŒ£s ¾ùˆ4H’Dƒ€¿p -÷â™3¾ö¸œGW;/ôºÕæÛëFY4ß‚ðäu˲ ŽòÛ–Š¿elÒተt øÝGª6¬XþL¹£Íï9Ï Cà|­úá»—ªªŽb)_=ÿ„òé0 ¿’‘”B@ÈV˜sÈV}MMUíòE ß8æíH+—¦<€ð´ê{YRJÊRˆY©øS(³"d–(*»ºo¿üüûÝÛ6½¶­°¿òMïóÕd[þˆ|€Ÿ×~°tþ7ËÀgÃO(iñ3 ’¢B I9à4[ùí‚5[×®úlk˜²´öJ5™–?âàE–…¨òZ~ÜQ{îãf2vŸðmXÝFdòqR]œP¢Ø¤vöÛ¯xÞå?³ÒÀ!W•gª§ïžoêRÚ^ã±LÆôѽKÛúÕÍùè=¸ü«møWð +M*~ART4+dÍ1÷“÷¿QIU踑SŽyÛ«#쟙Ú[vEY¸cLÝû°ôS¦,¨J§so¸i•ï'¨¿¢*Ð'%?)’ƒ@&”§‹¨èОõå{o~|âã<åôëf ¸¬ ge‰Ú¯l“Ò³rÙÝ0†âCX§%{˜½‰|žzgå²9_¾µê»…Ë9FðVÕp¯à¼¦–y‘”š-œ8¶°Îû䃹¥öuæøŸ.ò\ŸßѺUíaY¥t²l"›‚6hì ëô±d³÷1‘çT.[˜ReßÒØ##CŒ7Q+~fp)j§›{¬èùûî+7Ã2ü°ž°ø¡XQÙÙ–/œ÷ãÆeK·ž2qòyîþÇ•ô꙯°’ÍrÕ¨Ù®*Åîuñelî‚á9Îy˜!Å]ï¬Ú¹võ‚ᅵ9¿¶¶úǾÐ{„#~z‹_*~DRT´X8TÔƒ¦5?|»v˪•ÛO9oòéîAƒÆ²õÏCy°)Õj–rT±*õ1)p ¯pÎã/ ›S«, ¾÷T»;qGÉ®¨rE~œ¢Vüf“ù ·×ýˆ·Æ;…SðzRR!#m 1¾ SV5¦0›ùgâ N4ç£w?ãóo Ù§×€AC¹yEv‡#ßbÉnÕÍ_¾«²§Ñ1kÞqìèv³ïÀšÃ•µU•å»¶lÜ´eíª~XöPôG~h€GðŠ¹ ²›ŸAZ, ¡{ÎϼOߟ3h~ÿaÇ÷è5pÐÀìÜüB{¶#Ïbq´ZBåÐWŽpYØ‘²eõ½ª’ÉÂõ¨é–`o|à¡Ý )k_xø¦›S'’Vò >|”æò/Ÿ…ü+àÆêrø‡gxG³€øˆoZ”‘ß Îý+?×è«îç-aq¡â•/ºñaÕCÉÃÒ‡â¯àŸ°þñ ïHÅÏ HŠ1/p–òeaêÏâyǽøðƒ="H¿ü$ÉDmñkü«Ê³,OÜtÿÃgNäIN“Œ>8BáÂ’ÊÆÐay㾃Ú˜'µ>¢¢‰o|Wüßair <!Púb¾âÖ>,~(|4¤µÏ HŠ1/apšeõ¼WU'q•pWi—¯˜(þnÖnÿÜçÞó;¯Jÿ˜:mú©/L»©Æ@i”¬4"€JV4HX¸††… ů·ú…òo¢áùò¬Mn·6¿CÄ¥î{aíCñCÑCñCfð/ÒÒg$Å ˜–ƒ0¹Lé²Àõ{¶×uøŠB%]-ÝþfÚåëA &ŠÚ´ëœSxèFNÓLrþªªW* æ­H2¢ÒbÕŸCÙBéBékÿøÙTüM4}‘Mtð›D‡µÿMÿ q Å/zÐØ€òÇ<à¾×ßò-I˜" ä1&å D΄<‹¸S®,p½®ÜôÀ#ÿáôãßdÔû|””‚4©È£åêƒÿ^õªÏðxÿ{díp­´ü£E4®ßCkC±cL?›î(”>žCFÄOé­3‹W‹««”çGTtâ‡ÊN_á¡â…²Ç?<ƒ5&I"(¢.a0*ÊŽ)U`ékF©—+&åözàoa¤[¾j0bªø‘6Ÿò§'Ùà_Ëküo•cþËñ¦ì ÿEÅ%¯WøBé ³¾™¬Ì<§ý^ÜäoŽtçGTr (t}…'¢ϵ—å?‰@ˆª„ÁgJ–mLŸèœÎaЉî”J?Œ7è«Í*óXðÉÝþçq8/òržÜå?›o˜²MŸËuþ±@7.a@DåEŸ¸'dDý ÌŸØ“ñ4:ë‹ÃXŒDe‡£øAÉ‹Ÿ¸ì[yO"H„Ì‹2R9ƒÁ”) X§ï[²§^Ã]ü“0¦Ïé¼ñ…‡ü2ŒôÊW Š@³Êw¦¸zqäoˆóÀ#ýøãnÂÀÇòZ"`XB)á0oô²À5—{j»Â)Öésó×ÛÏb·Ó'§ýnBjá›|ì$×3‘¡èÅŠ¿+GÚ’uhl´$wM¸qÅ?Î7^<áwóŹ»ýqêóÛJƒ¢x¯eå{­M±OÁϬXÇ‘ª^ðÄ«r¿½÷Æ?ý‹•wg·§|Jà³6¯M¦n_ñ'ÜÈèer8öŠ{ò(HÒ ©øÓ)7 œ•”w¼Š÷QR•÷eZ«–ùS¯8zð,ú¼ñÁ÷ïºÑ¹¿†±ö9žº#—&á)Šåfõÿ OÞãOC§{¯w­â ÿ2Ý6_ýåŇ.âFÄÚû®9*-þÐa”oJ$)„€\ÇŸB™•ʬ:²ìï:ëœO™t“¤ð¤½;x]þTq³Î]÷9Ïx³YAQþëU½à÷^ïŠã}7º–=ú‚i†ª¨a[ýìFàW^r½Ëkø+UÕ›Edåáˆz´´µh §ÖšmVHùûô‡ïÿ½¢(ÜðÑo~¸Õ™z«*uRMjYUŽQ¶²öùûî+ïÈ£D@" Ä©øã‹oJ…>mÚ»¶ý®ß›¼îãJ¾2÷­Ø6ÿ°ü—t>•ÖtI¬üï'“eyÝ7pgÀ%ܘè,@žh²C5)ï)fÓ§Oûãú`ïÈ{‰€D@"¤â Ž)ÊM<>€¨n’—h”I¥D^3oö*¦EU<*©ž·s&õŽ@éëAø²ÏêÎÂ>.R«Eu©Ý•˜ºU–PA]e»jÉk2Q9‹Ž8ÚÓÁœ®jIAo•&n-¼ÏByï ?~«CÏ€<—H$ HÅ–ô»9õOŒ'ú +öñHÃUã)v1[½n¾R©Êš§Í*ôÕ–ÒÅ[Þ7G‹wùÓ·ÝÇy‡^e~x%ñÜ€VƒtZ´¾ýpZÙñDÇdõ(&Óï¦?tÿ ­~$J$‰@ØHÅ6d©õÁíO?í¨*«y–TõÆ\W•{ؑՖþå)§¾Ú áù4¿Ç9jI~ož`š>ý¡?Þ¬Ÿ'`H¦%S‰€D …Šÿÿ·wðQûfw/ „ÞEQ,<ËSl`ì…ç³ü•„"¤&XÉ%TE‘’Ñ÷ôÙP|–gLJ½<±¢€‚RB/ôÛÝùç’=6á.¹$w!å7|ÂîÎüæ73ß›™ßo~ÓêÑUÕ¬ŽŸ3'îPNáÛÐ8}×*åÌß2UÈ~Ýwßv<ýо¿\'0?+Íý@ÝÏ1å ú@Mºõ£˜3—}ξðMÎÄ¥—þñ¾rÚÞŸ°ö3ûõÄuÎÝÆ ÅÅvÆu<ç¬ núþ³ÿ®®'Y§l„!P§ “ûêôÏSýÌ%¸SÇ@Ì_yþ¶O”n¬>£cóœì/˜ÜRˆóô¹mŽaV(iB€  $øÌOy¤ £õ…MqÊ^úñ‡·X§ì]s$ ¾]¸å¿Ø| 4gÞ»ëaö)Ë„!@Ô9Hð×¹Ÿ¤æ2Ì÷bk\|ÿ_Õûß·EÑ~Ö)w›€"sGÍ‘!„!@õ^0ÐOSÜÑ!—Õ&¿a›ß>/[îïï¥ë+µ¥%/B€ * @‚¿ `ÕÒõy-°Šïô.9›ÌocÉe‰ÚAöcÓúðP B€¨Ë4áP—A®Í¼±¼^8 _i“¿«6“hZ¹¸ûN‹×:Ñ„ˆ9!@ü ìG6-ÞZ©‰YÐ J†ëÙæ–=,ÌñÜ D… Bà#@‚ÿÿáN#cyg.3yØ_Ýî 'ÜTaš²8ÜX?B€ #$øد®XÚY¤CÑÍë}É~oqû¶Ó_±Ÿ½’9ÝýF½/€ :€ þ:ð#„3 ‹R'n€Y|ןñ'„“­×‰cn¿ßÏ7¾ß¹¬ß3ÿeÇÝ=Áï×fÐõ씹/1Æ!®kà6µèÁ>ê6XÞØ·ƒ¹šŒ®+ŠJ„!à@€¿Œ†ðê»ÐFáÿÚÔ¼‡8ã›îK±šžÜ5ë{6Ûýæs>~®»“ux6FÙ³ÿ÷­|‹©qÍX«ƒk”n|q‹/>(Ïî̽ù_&¦ÌìW#†™ BÀ‡ þXâ´¸tü°Wt»Ò,ÔbÂRÂö7ÜÍö}üf:àãs|¶á‘‘¬hÏŽ2ü…i°=ï¼Ä:ÜpOÿª~´ÉßÃnY÷¬z妷XŒQÐ]XÞ¯G¦Ì8£ª|ˆž B ,$øËâÑ ¾æéãö M¹ñ`t+㵞·˜4ïŽ újfzíÚ“l^ïÇçù),ÿ÷uLÑ\~?û%Ó/,¦SWÆ]ѶWµŸÇúƒÝðë‹jo¾K˜Þ×èŸjCI B€ð!@‚¿V„¬éÉ+ç—æ»šnüà„¡ì¹>÷š?·í[­Òò¨æjÝŽîØ8¾(ë]¸t˜ãîp\Ù€j~ÅùlÀ¶•ªÅy·ÆW×T“ E#B€$øp5X’æþRÄG‹;í7ª1ª¼æ¶:. B_ºâ}¡ äÝ¿‡1œ"Õº}u’ çøC›Y”YdZº, y„!@„„ þ`ªŸDºþL ?Tü>®µí6`ëJÖo×wÕ*ˆ÷9\T«¶!ÅwµÄ ºñÛñBŠT v*°fÞØ±ÇWBJÁ„!@ @‚¿pê{ÐcÛ˜ûϾló;Ê)ûÖV»8Va“£ø˜Ž]CâÝ¡D6îØ}¨D T\ð†q$a¨…&:B€ ÂŒ þ0ZWØ%¤¤Ýj vû9Ù_ð®9›jœ­ü?6°&Ý{…Ä'®û)¬(ûO&Š C¢… ;Õ ³bK(ôDC„!üq©×¾º.nÓÛæï±úîþ>,eÙùjk}ñULkQöl€_&Þʶ=3ǟעX»«nc;–eùýÂñ²#®3Õ+|e8øB€ +$øà/¿ÃJ¿fñÏØõ¿°ý¾yײýŸ¿ÇÚ^z]…ˆµÈûÇ㋲ pÃÝq‡«~ŒîÁè–ìÏÝYŸÝ?2E˜5O^ücûþL³¼¬yQåÑØ&­Ø–fݬ=±íE°Ž…|‹SÝO‘y¿FPSdB€ "@‚? , Ë3ajÚ%Ü43±þ¤ŽyÙVï=?)Ýr6W*ÄGųumú°5íN·p "/ú©É™ÿÕM-z°Ý—Øä‚}ÏTþ/Mmùô"}Lnyú&B€$øÃƒcç¢ëË¢²c¸eÞ/ïµ×„ivÈÝ®´Íßś粣€™ŠÊ¼j;ÝŠíŽë`íŽí Ý‚ þ\³{f´YÐçú ËÔfŇªUÞqÙ{'\mZÔ:«^¡*,U¨¬ùöÕ‚”"„!PeHðW²úAž„“˜â¹ ?üóKq@Î)íe6ücô‹üO¸[÷-¥ {.ÃíÞž¨§ŸÊŠÍ/]VaÓó¶®öØ¿[ë+_3 à‘çìÿÚúTö{Ë“ŽÝýÍÅù <)›ë7’”{B€ ê'$øëçï¶\Ë }ö²ÙŠMÑÚ¥ZE¦få-NNÎ4¿>Ê=ëdÓ*þ¤wÿ(ÓkvÌݦvÊÛÆšòÍÙ»,ƒ äìpTsv8:žŒnÁ¶6ëfiÑ* ‡™ª,„)?F÷aûùˆ!@UF€•!kܤÅ`Ô´ôË-S á¼ÚâÊ á&,;þ±¢°—;*=ß×õaÅiÉ— B ¶ Á_[H7ÐtÒÒ:*^ÖÚ2yVüÇaÍ¿âÒ´?ŒŽ­þÌJL¬ÿ—4ÐߊE„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@@`L²§ëÈdÏmF`Æèé=GLõÜ8”| B€ BA@ …¨6h¾»¦]_KðÜ}gãQ‚±CŠ`¿3…-bbßïÿʦœšä%!Ùs§àb˜äÁ/Àc«ÂÅ[ž”ÿÖ„o(qÜž—8ã?dz’g£÷rå ÆÌ¹>Mcö7MñW.Ä8`°¼1ãJÙW>Ó-&ÏÖAŒ‰ó™=ѦZ"ž‰:¸ÏŸUUùøòqÞ¯8çhfõÛéúJm§ñùp§½ºxÊ”õ»4‘Ë}¢;õJε]©S~ˆ\*Ĺ> pL¿ÐueÕªŒÛÑ™·LÑþÁùYòÛŒçç}{U»eQŠ–ÖïÍìõÒ»ÊNa½ÐÍuá\™+„è„Îp ”Q#Ü©/ñ¤L®2¿*D@ºop…o©B”:Gš¨{ú3CÜéIIªs™£ ùX9;®C(˜T°çϻюâý°”Š÷ÒÆ ÃdïÌV¶¼=Gy‚Gw_8äþߊü´Õx9Õs½i±‘foÎY.êûjèîgz&n¬»*EÉ[mdYQùüKD$Á=ô¥c¹°¾F0 þ 5oåXôÇë;÷\µjñ×LXÿDg¡_±M4:•;¼¦±fÕЈ›oV+Ž,”oÏLM~6+Í=3Ë㾌qžÆŸ8&=½u°áðÏLs¿˜‘šôE8x3^v%%9r$WxgŽ:¢€å¯G[yP8…~ð¼¥úQQðûºwfkç'«8–´¥–É^„ƾ Jõ#°"dZ\´±Dñ îôë*ŽÝxB¥âŒQwzã)1•´®"pLFüßm?Èëõ¾AÞ¬ªÀ`àâ‚ `êwŸœûóͽoìýÊϹUåQ–ÞzßÓÍbëD<÷p{žÂÔÂrX!N†¹a,ær²<)gŽÕçÇzÍ‚•`0F3-g?(›©»WÖgôðzÍwc\®KžÔ'm³ùJI`ZÖ"æj{Ž0öÌÇHhuVª{¾ _¶l™ºbõúiP:îÀôCœ™šô•$Ht§ß`1q1:Úç¡@ÍE‡ßßÅ¢Î\ä™ô‹ŸM'¬Ë®|$˜™„4ºÂ{…ª(OgLO~ߦ8kV³‡½óÐF¾\°¿ïŠU\˜”´/!Å“†|< ¿¢ÉžAx ŸÅ-ö ¨6Ù±A9ÊoÛ?+1Ñ+ýÜi`nîeëù=Ò:Âü>¼žˆro [¼$Õ½H†Iì7( =òÿH§³U(ð[ñyP¬^>Ò¸ÞIJ›Õw7/_,,kD5KÞõâ“w稉ƒ'˜K«ÂcÄÔ´Á̲îR¹zñâÔ¤Ïqÿ•àNõ`vaq‚žùA–ž˜ïkœ¯Rqf¤87οn•ºÖÿ÷Ww¸È²Ä ô£kF4—ì}kãØWžôdÌ”—Ë|X,fSI~DGf2þ æÿïa1Ügª,2½%@åŠ2ŠÁïÜk² ñœõ[¨OÙ€Nîp¡aÜ3Jø ËÖ=ˆ³Vvz˜NèˆùÕívØŠÕÝx¿S(êM.UÝjÞ;³fÂÏ7»!é|B_°$Œ¢Æ¹4õK¯éM´,ëñú¬^RÁ€ð‡‚2Dë¯êYŠªÞÑŽíW<$éJ賺AÐ&)1|Y(ÆB)y}´>û„EúÄ’BÿuýB®)W£bX^/›íÍ·^BÐešÚr†!öw·¢8WÜ8“©VËo!Lï£ôÝëS6éºP¶{ÓîF†²}賂WªTX7b²æU|3(÷Á$ìF¦fÄhÚò"Ó{”‚d(qYžä9’yø”„•üÿ@ZZû‚ë#!ø P(­Ð—h¼ûÇò,(}w;ñ©ò»À*¬§Þ­šƒ'šÿ5>7Å´•eå„~It­]ºðîIàÆî;¡X™r,ÛIEŠîC=Ö$w_þ¼™v¶È5¢ž …Ò'ëÌÎüÊz˜m¦¥¡%]ÿÎø[…6ð"¦¨|J Úê‚ó÷ÊfiÝý‚©lî’éî2kH| vTœe¹ÐwLa.íÆ,}r½ž†tþþô^9µjêÿñªã:[B¼ŠNªFBß.æé/ÎÙtè1û;”'Òn6JOë+Gã#R<£1²M@~1Šÿ+‡õ@Õ´1Yzò÷‹ôäQ9<à?剋ô1¹²Å(SÑãúCŸÔï?$; `÷«0Œ’ÇvsÆå7_[9öÓ:•c‡¢;-J‹:íáÃRE÷8A5MTIÑSäz¡„³]‹:Wmª.ö3+}ÑuØ¡PQBø xÏD>3Ʀ§·•$¾üZL¶_¿Ûa¥_ÿ~ÅY*é̺4Ij ›Kç6“RŹ£Tœ].~9ÊݾTqfRqF~ PðåPœÿ­5Äí[üv>ÅYòʬeRqn]ª8—²–Š3óYñ|гà©PœŸ†…±SXf©â<ÁÎçû¢ <†IDAT0‚â,t ®ËÁ ý«„•¼%LM ¡ŸÅå>úåÑiøßhHµç æ]€ŠÞ&œ)¢AŒ–V„Pyb„p¦iˆ×ÐhÇhy ¢²1Xi/WŠ;_þäý÷û;™Â< fúÝ‹¦%­q¡Í±ÐHe'Àbµ¦Ï#/]Géžså÷¡ƒE7!lÛGòÛéd¤%F;ýÑ)øù?¤?Ö 4­XWËŠüCÇø::!°'úãq¶¿“òW¿¹ÞïôK¶sÅóÜ ò ´÷‹û¦[À·:ªË0ž°ÓÃ<í$ÉFQ”G³+ñA¼÷MÆÉ/nY7¡Cz¸¼,®•Y¾Qp1:¼zòÚ}.~{ÑËt®°h|…"~åºß»I§ìopÄ–ˆ|ëuðÌÃŒÛáßè^?šÓu$è.‘ê‚ßÁŸñÌÊ•ÃèS±“Ê-~϶¨ [‚Qr¦lEÛé^^¡rŠ¢[Êg3Fë/öLZï¬ÓÎþ÷¤8Ãú8ŠÁ?Ñ®¯ÍJMúÆ_Fzi4TڸÅÄwW·?&þˆ,ôAšÈÏ )¯œ}‚9éÁNÚŒéeå‡Â1Zp82¬fÅ@žq‚)Eèx}ÊQ0榗^v'(¾†xþ?Ö§ËÇ‘±E±Šu &ST+ï7NÆ íŠ˜å£eÊ¿…­sÐ-p 5Ûñ½+è(ßA¾eÒr•¼r¬ ì0äGºÇ-%ú‡’×€ÿ¿«É³>S©á¹qåÖKûœüùŠŸ6h;ÍøMÄ è*¾Ñ~,S<ô®À³ØÉI¨¢HNp(¥Xʰò¿M/GÀãQ¨„„”´[³R“åFé ­b£º&a/¼`§~ûÙíàûŠxÏ?>#ÐBfñæÁé,¶_†Kå8ÏÈ+•ãźûk[9΄rÜT‹k•ëÍëŒ:(Ý %=]葨0X "¿m‡µ)•þæ£Rfœe ãZÄíŠú×8a‹°oK#“ÂmõE.Ì{á÷‰T`r݈ÑñUvžG)ÎØ¢LqöEÇú éÚŠóïx¢|~Åy‰TœåÚÁ;èo^B›•mÌ(UœÝ>ÅYx  8«G)ÎP\f–*ΛJÒ ¬8ÃjÑíÓ3ü‘z¿Ø8 äèƒkÇ¡£©”`E8÷ûk:†lž¬j>°ÎíhÀÇ%è齜qш°+@üjûa»ÞR|»Ïz:° 5!þa‡9ŸON›¸ýY0øENSéà|óîXg€Ž¡]æôä S'ÿ쌎w`¸¿QUa{œiÉwÿ4VB;ÒrÄŶŠûen¶Ãð܉Eí¬Nùlذa’îM“YW 9P¨Ì'ø¥r„òü†Nê²#`Ó0ÄeÀ£ÈÛ±õf§ wÄ_ A1 ï–ÈJÔÓO D×ÐýÞ{,¾°½5R儸þÊxK¥ÿ6aþ¦L»(Oðžøö­“‘¿?Ô[[9Fuae—(ÇNEW*ŸŽ¿Û£¸6ÏÉSê6çwù÷DwÚ$CR8î@"™.—2u´T –Pk\] ™|“œŠ‚пJî®Å©îOÊó²¿A[¹âÌKg;ïQ?®¨ÊåPœ¿³ùxJÅù©8L(Î|ù¥}Oþ ù ¨8¿ÀŠ3‡¢8#­+Àû´º‰rql€üW#@ VFüX}U¿÷ÚHâ),ófð_‰4öé¾#Ø•ÌkÍNH™™­ÆþYd¾ïÙŠ‹]`§™ùHÒÇètq±‚lÅ¢4÷V;Ìù”¦Ü%ç 1×ö=ëÜæs¾}ÏXDËÐ 3®x+åSµV¯¬ FXE‰2ź…iî?´5}יּÿ›íM[å5Ä£XuŸÚAsºÃ˜s"çÞ32R“—Iþz}ž’0sfsÛ$:oܸ¬øÌbl2Hþ]:¿ŠNH‘Ó)"Rwë:2Bá|>ø<Œ…EkE—Öo±í{/ÇBƒQà;ßÞJYä,à2@x­å£õ…‘kB‰×PhL3O¶§¨H•‚îŒóbºz¨°ŒÀ Þó˜£ëÏÌÕõá…Îð‘)éç[Â<ÜwØþR9ÆîƒW¡Ï.ö)Çì.&]˜ K]w™ÅzvÜPŸ³î‡B2&þÅ2ŽÜ­rð°Ù¯Òäïs‹S§|Ëš|3ÿ&xÈ)ª€Ö¹RòJRqÆà ÅY® A*ξ]IGB¥âœ»?ïˆâ¬IÅ™[°&øg´©8»e ©8!϶âü•Í¥jŠ3_ˆõD©àß |_NÈ̼¤*íÎN“žõZñî=&qÌFÎaÕø…‘â.G°Ñ®øë ļÂ2¾(òÚ ¡=sÓ·dèîÿÙéúÌúè@Ð ÁÜæRÛ?гE¼+ š÷Oz°­» “y0Ú~ÈI;¨_Ï9Ðã±.‚Ïóû÷ÁÌ·æÀ SQ[9éÂñîرüäý0}°Ýð`özSˆQ6,”zVM,×»´_©A%ˆ^ôTÔ’•û’¾ƒÖqô˜Wù×¶’ ýåÜ+Ì™X…nÎc[öä üR©x'º]ó^'º´ƒÉRäƧª¯AÐb±i¤Ë-·•¦ÑÉÕù1ÙCÙÆ¶G¤Ìø‹®/‹’Š!»› øýu¶\ kçU*Ç ?ˆ4˜R*«Ã”黡Р‡2×T® ‘ÛåQÖvüPžàó¬ Ýå‰~÷é³:8lȳ:ÊXª$´¿Å°VÈÅtGkÚ?CáŒF*ÎPömÅùb9‚OtÏ>idJšï¤P_zPœeZ›Tœ‘·ÏÐæŽRœ1ý5 õ»G™‡Rqfl¸\‘¡í’‹ô°¨г¨’âÜ¢™kÒm‹¾ƒ r Ô¡È»ï®îpÌ»0/EÎa.ð`ÿÿìn¹J8ËÆÆvnî7×0AŒ”brØØyú8ßNÝ'–ç;jÆŒ–ÍŘ=iÒáòaUù–[òZz“öØV‚ªÄmì´ïÌâ_aT~n$qÀè|Ö –H:)¤½ÞØ•"°=ŒÛaý®(<»MþU>2¶n&c*Á£põf¹cÅ/9Ûbãdÿ 0{7…v½EáÚuò˜Ù’¹øÂÜ(Åušsº«¼ÉlñÃÎð–VM(Ь)”è]G¶Œ2&·æíËߎ°¯a‚g@á}vLÄéž§: 0jÞœ]†„´´Ž"_,F§:ùÇXÊÖ-ñ¸/‘ñd»1rÍ—Ó9øí_韘ì‡õbZ`¦Ö>’~²_€2µêÉÎs2dØwÚû‘>Á-ÓÁ®(ÎSìÉPÊßV8_…)±i’ÞvÒùýZŽø¥¬—}˜ð~Í™z§]›–ž Zü«®î0æ=Ùø"êúÇ^¤ñW^1#š1'êoÏæ OŠlVøÒ¡“¬{«’FÉÎfù5=°'Š®ÌË S?à[s Ò"°ÝûÅ6œq_8)ÎÀ&¯:…@íþ¡í†Z:7Ò%'Ái„‰]A 6?FO žXíÓë TAóùòQè—Æð.íΠyî 0Q@D V÷ÁĶ«H# æ×Ðh?¢ó:„4ö½Zñcž<âÓJÇÒÄ”´ÌíŸ üîÁz€Á$ôů@iKjeqÓŽly‹TaÑIùE* âKÔx}ÇbÕ_êLyÙÁZâ Ž\SÏÁÜþçádM¼ú€@íŒøÛ÷=ÏÞŠ#_å›È8l{ù42œ‰+!P÷€Pþ[ȆG2gŠp¡M5¼%38©“V²G²âï:@­Œøûg}çÅêÔ7"‰V«¾IþÄ›¨K4mÚT¶§âˆå‰³ï®˜P¸9bü‰1!@3jEðËÒaŸ=.b‰˜ûò¬·wTt:VÄ&Æ„À±@`Àèœõÿ+RiC‘Žd{T¶‰/!@„€@­ þ¿üg§<èæµòTe…©•î5v2•Ûx`&­• Îté'®èèGÀ/ì÷ÜcïüêÁÝnx>œy%^„!Pw¨5Á/‹Ì£›Ü‡Õ÷8”"|üžÄhÿ³ªpÌ6>_•85mLUâ+ZyK¶x¬Ò§të.—=P°Ea¸p%¼®XaÚp>,<çaøNòÓ3cCÉ¢<7?ºPiªÂOë[ß±óçG—¹6º"b #ê8µ>ê]uu§ p®þ‡0þÇÔÎ?ŠmÒfHïW~y®31ef?»+ÏoŠk;Ï­q"Ì@î5–G㮌'Eìë)ïÌRp²QÓìc¤›b”;¯œd>W^òXZÓOáú çÔã¤8ykæ5ø“V‰Ÿ±_þÞ@[ç „Ä_8W×D3["ÙßÐ6”‡`i9iCá‡Óý6â>ûKŠŒâ¡8ï>œ´—ã;iOáó–¤&¿ää'/³Á¹ökq‹ÎèÌ4÷‹Î0z'ê#µ³ªßLÿ·²?_uM‡!¯Ã;ÞTµWÎÞjÉnîý\èBß—€ðŠò·Ø½cRfö¶þ,9.tý ìíý+: ã°(Á y»е²³ÁÕ¿÷@YéŠÎ!Çpf3–(Ç_¸Âš`ê`4®ËÑIëñN¶±ñ1;гýY!®€kÿ-ÓÆ'sZÒØ[¼$ƒîaœoþ’V‹ëëŠSp1ŠuÇX}Öq™ÓÝo!èY(ž%iî~RèË >СÎT]üzæŠé+ï§ÿpõúKìrгñ!À¹n ™hÆÅïò™œ*#Àqm®¦ <ÞügU㺘ö¬Q7 žáŒ‹´T|_”B_ú+‚=‹v"oÐ,ã>ZóÛÙhG[¤Ð—ӓ߇òÛ[Nq•!ÄG°´œt¡ð3½ì6t|¾5 Pì?°ovÌp»·c²©ì8ä¿ócDJÚÝhŸ?@áßâL‡Þ úŒÀ1ü°³ÞÜö[ÿ·vžw:ˆ*‘ó( ÏpÅÕ»ÿÛ»S¹®Wý(À­ûp“•øJž#®Äi¯¡‘“B¹4m\e•Âú„ݘ ÀiƒyMwš¿÷Æûy!ï2Î~Ä…íìü¢ƒ‹î¤]0YÞ@'Ãñ=#§ “ã½8¾ Bú÷~Úp~e|äåòÚÍ&.W!x[ìõ°Óp>E¾1ßoZ1êVcxqÓÝPä­ä9C'˜Ï¨J\OÔá¹hSC€c.Òy [·SOò°÷ëè"‘3Ùfä4ey'!¿°„iñOø.ei âr«,l~:_8Û YÁqåiƒ§u„²2~¾…½PJxœöê‘X%o zz/iqóµxIå%κ´IGßc÷å£Ñ7!Pï¨uS¿!Ùaàûù÷ýµNÅÈz Nöí‡N«#zÜ5.á·|©_DZø{½¹¾F·ÁáF°»ÐÉœ‹¹ÇuFžo Òñõ/GúïÂüÿ!®ˆ}×€®Cz"íÍòŽpX öáêÊŽ¸£s¨êÜ2x]´oTŽXÒáJM{T#?Ñû] >'±Ã0Ò—|3Káøq­–ã#o73¼f@Áès(§¨ ÉÉPˆÞ—ñË;nñHt0;l`J—žìr*OGß+&äîFÉ'ü¼¬wòÖ?Ö_j2ë|³žPx[¢ž˜X +ÃV4õã+Çyÿ‡º$?Ö`ñŠ*ü9¯+&Ox Z¼§X"V+?Ìêr꿺¶*™«ŒßvëËËÐFW-ž2倓¯\¼W´ûP¦ÊÅC¶¡QœÉ¸6nIb¢7 ÚÞô$ê=ÇTð;Ñ;óëð-ÿ‚¸AüCó›žÞ¶0ß< þ|h>kA1çWcQÑ]àðn'%é“íVÚ>ÌÛÅ §•”ξQ´œëÃÜ⣖`71n½¥¹>™4éÈÎ.p8Ñ';-•+c3Û›¶›çzoræÞ cÕzõöV¿ÒËøÝLar'“Ö7L© –“å4U‹„”™}ä÷N+íZXÍÖJS¼“N†sNºŠøqïžk!à?p*õhS“1Š?®“+9ÑÉJÀ šÙ˜ãÏÂv¾»±È¶Ú;'œüé8–ÔS¤A@>=PhÈÃ¥ùÂ|=†åg/ð¸7KºÄ”ôópuçLös%æÛ?Æjÿ뤿\ ¸â§ 02ÏÈLN~ ^òÏïJç¯Óõgb²Ù¾¦YÓÞ'MƒÒsƒñÉHöM ô«ÏR¿ÿŸ^²ôÉ[ð8sLzzë…IIûd¶úÍAžçŽzdn[«cüLLGHr„@]A #uʪ˳õ«Q÷‹‘¯uÑmã—Êüyó¬ËÑ$Òñê³Z1®ÞÉ,cèC޲;, ô àBå‡Q;‹Çmrç@®7o”=Ù†g;Ò/ R2ÐÆ‚*÷v|zõXÕÈÉÑx¶÷‹Ÿ4—zí"}ÊoÒtÎó 7æõ; ‰þü¬º21+K[w_Ð^dZ»®rw@¨è¡Sy,|BM躀€ÜBËv0—³­Hsþ®è™ÍË/°“íNóŒÎ·ŸÁèÊó³ééI4vHð—Ö€Q)éLÌÍcIq',¬Û‹¿¯[4u-=X F3£ FÓ0ZðBx¯ªüå =ymU*O‚>·M8øT%M¢%B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€¨ uîÈÞYKc;áNÂQ¦¢ŠŠj¹yÂÿíÂô ßÉ«vqgùiî£î oø¥¯¼„òN…Æçí¦®eÎ3Ý+Ùx)Ïßý{qÁ ŠÊZªB5M®ì¾åÁ^[tέº€Šý›ò8íUçÙýòˆkÅ(¸ž)|ïâG’_—]Õ…üR*FÀþ=©VŒÓ±=æ·ó ¡+³–¦Å­\·ãφQÐÚŠi2oán‘žÅ×0Æßtñ¨§'Œ(ôÝžç§©Â .Ê9½Ç|WßBÆâú.¾ï¤÷OçUU`2)n*»iÇm€ç‹äåÊŒ™sN‚?H¹q«¢Íƒ,‹ZŸ!Ø™KÒ2^¾{ؽÀÕú ·4f– l€=ç®oÃEñpÁÅ ›Š ú£ˆše ôLVy“½øèÚÜžsÖ|Šû(^:1*fÙ»÷ŸTT]ÆÎŸ]¼;w¨%¬«Ñ–ú঻5\aß±8í¹P”3û7Êç_"d>SÒn·¼…™œ¿‰VŠÿwuóÎxòöÌ"#g½Æ¢.]ä™ôK8y7^öïYÕ6ÚPÊ__ʡˌÎÌÒ®H_òÈZÓ´ÞÄ5³Ã ýÒŒán{Ž¿¾w{EÑÆô,å©ô§›¶­N¾®Åãž! WÞ@çôŽà,—ï<’íÝþ­O8T‡iˆqTîZ4Ÿ ‘¼Î’%¸=ÖÙÌȘ´¢ïž/Pn‚P:5Iƒñ:+3;¶çÜ5iŒmLÌF]?mê(åX4eL ¢ýì¦âÂͽýù®ê‚P¸ëPŠ'pyÕ…©éh­»„Å®éTؾÚÊ”ˆ¿c€ÿÈûÞ¬4wbuóîx­ÙØ\ð\b4ÙáæN~õ­†³ìÄ+4ŽêB‹V3ª•+/Ö¾Úðéã¦0ÇT…Ö=Ü›74=K»%)Áø¤*ñmZUmñÂ"}ŒlģݳNñŠâuÜØ}¾#6\ì™´üå_½u£Ü³N6Xñ™õ¥¤¥µ/(°þëÎ<¦ã!èêKÖ«œÏžsW÷:|hßkh§T%2”€ŽB˜Ïœ—˜â‰ÛXŒÜއÇÎÕ‡‘ÞW‰SÓš¦÷Ÿœ³#’=%1Wy³¬Åš¦Ü²HOþQúÉ2–õ´¢òљӓ}Ö‘zÚi¦W,oïê?{Ҥþ2yLïìL”ë7øûqZÜøÇõ‡JÁ~m‘ᶃÐâ‰î´ |kƒúõLp–CÒ<‘œ¼+!%ýš,OÒ7#Üž§€±×ŽÛž§Ì[w¦e𢮶ªn¹€åÕŠ ?éýØÏ×{h|.¶ÿüÖâøë| xÕa'}b²çÂí†Çƒúk1ëEÔ³"ÅÅþž©»W9éäûh}vÃðêÈ÷%øl‰zùÖ,ÈšžüvIxðv#Ãe}ýr˜Oƈ¬ï9h‹;.þáI±•&•›íÆç_«LÕ›´ŠùímµÂ£†dz&úÚÀÈ”gX–9,¥B¬ >¿¿Ä“ò7™F¨å–´µÊ²“«}jÝÔ¿ý°¯j }?D‚E™Âz)=ÓtÎÜO[ÁË}ú¬Nhð§äwIy !,†`´Âõ9UÓNi§½1!Ås„~*ž†€ïÅX„HNp§MX¤Où ñ†´øòw:”µYzb¾ä‹Ž¹«¸bõF7ÞïŠ:Ì¥E÷FŸñ%„þL;\>G'{º@èa¾B‰™Ze“ÑÑM95}„ ÇR-ü~¢§Èõ¾A¾ sçªMÕÅ2Ìéüt†)G„¯³XÞ[0ex=²bÍÆá6­Oè –Ä¹2-J‹:åÿв¬Ç곎‹jÓìsÐ=¬râ\q‘”¿~Ù›cì ›èo€m‚5¾ÎOúc~ù ”?G ý‘úŒn†W| Õ¸ë|`ðî‘o侇ü jðß@†ÙNÒú„>æ•[Ä»,/ômº¬Ô¤oä;–†ùxÛþ åyÚ£kº˜¦ñ.ð¨¶Ð?‚…8JÛge ׿àor P_æ¦X$Ã@”•Õagœ¨öñßp­É ÒOpåYÇžÚS*åG9¯·J³P]®¨‹x,ïÇ™²DSŸE-´4EGf JÆ`û{о®œ†6>Ú™X¶õå(ãI–Öú¿¥í¨‡Æ­(I#ë²) Ôe¶ Ê•Q\œŒ?!ÃB˃¤,q¹Úгö¨ÕÚõvaYw†­x‚Å0f¼4çÙö§Ueå¿`N©{,Ób]Š ï}hØ¿GkÍŸ·ó…NôD®*WaÄê=HŒ>ÜöK–¤&û6¼@ðÄaïÆèý1ŒÞ—rf…ÿ I?~Μ¸œƒE7¡S¸N~å„x€qå‰%©S¾- ›ÁÛißnÓb=Ö8È·µÁ{Jý–CѸDk$¾³l:<7Ãòð°ã;à+„ìœLOÊ3¥oÂÒñ>”°sñý”ôÃHß gëOò}òî—ûÜ©ë‹Û„Ñ“´†¼#=ýÀ4Z†©†*0’¿Û™¬©kT(¤csÀˆÃD/ëm»p•‡5I–õLšº‚¸Ó1ºþd‘>qgi¸Š!ä4¹èÝ®y™®ä‘–ö‘ÏRGéi}ëÉ«}|MþŠ¿$•wÙ¦KÓ* 2¬qœñí°âÝåðß,ßC΃#¢|¥6ZúŒµ&ø.kÛ4çÀÞGE˜‹~ÝŠ‹öLÛ:*™¼a°0,€yp‚°fê¢''ßÈŸ5Îöc4û¾ý-· oA{ˆœ¶Ÿ|bdüF¸3W®û½k¬Öôù<#wî(ÝsîbÝýõ!}¦=™©î`úsFccÓÓÛæ™-! ?.Àäî…2õÁH¹%´:óñÈwwgÆ-a–~úØŸ½Íùì]5µ2éÁ´éÏ]3|æKLü[QØ:.¡f;¾Ë¼^Ö¯ç7®^_T´÷ð_™%®Cþþ-GùÀâ K°ë·›¿É"êÓã™xÁ‡ËH} ¹(”†³>–ù Ê$ÊY/|fzÍÇðô™†Ë„7’Xh’"QTLIEy‹øð––¥\éüGGêsž³¼EvXé— âGPàBªÃÁÙ×j'&±XG;;âåûâÔ)ßA¹8i»•v=êÝ”ƒ‡‹§H™1ÄgA 1M…³í6_ûÉU¶­+–‡I;ò¿ÓU3t÷ÿìð²O ŸL›ò‡‡˜?}é µÑòˆÐw$pv´‘à„§Ã|„ý->/?G €„3J¡ŽQðoÝ_†Ï¯ì4,C\á^äíØz³ôã _Š©ŒWaºž]ÌÄ…š`wÙ´Îç“Ó&îÀ4A0øEð_e‡™Â·hÐþ”BòÒ‹íErþ€¼H“éwêAhí2§»íéŒ2) fBh—Ý&…c÷‡1ϽVtiýÛ¾÷r¶Qhýó³½2ÝÌG’>†@?ˆ¡ët +¥¹·ÊgËŽ ëîI˜šö=ëÜæs¾}ÏÌ•':é]Ì5ÏÞ» gbØ”ÉÛ5Ï.Ú“{ºÊXÔâԤϜ´áxLj{:²¡ä¨Z«W V#¬¢¿`庅iî?1Ÿþ ;l4Á±òùcš —óüP¬ØKûžü™4OtVNú(ÛÜІ’a*Wfúi5õ_ÌkáN›ÒTk’™Ç Ûcu6FNYç¶oúé*y‘+öG¤¤ÿ[ÖûØIðM$ð¨$ Ç4Ó"²¾GÎ ¿coþ_‘Àƒ%"}æÙB‹ú4K¿ ï ˜ºõ¨×”d¼HÕá‘SÓ®hҢɧóÆ+ð­§É)l¥Ôgñ«IšÒÒ‡ööOË”kpØYQ±e•T'*W–ù ÖÁ¤DÇò µ¸‰·ˆåõ’S}5Ƀ3òïÔFË#BßÕAÖëÈ;!ŠÎŠt*˜ûŽhY©î0gaÔ;mÙ“¡¿ ez‹ƒRì²ù çXõ¡ú¬2Æ$¸ÃJô$tT?Ïlëîƒ0/zT«æNn!Ä–¹Á »®Hðß‹v: ‹ÂG¦g;ÈÂöŠ-qØ–¤,Àš±y^cÿ>¬kØÁša*j+™ˆ<‰ ó³,Á_ƒâ² JËPé¯Yë!DOÄõ™ù¥Ÿ\Ü…Qã;˜=ÙŠaþõYzÒ¯¾‚òŒ¼­Ì0庆Ø=q¥­@Éø¡¸%©IŸà7yÄ´¬—åžýPâ4l9h}÷áÄ…<õ/¨ÃZ™8l»›+ë êCq1¦¸0îŽUòC2õ$ßtQ¤ê0~ó[óöçm‚ÞŽõ4¡hçaeþD™Ù𦥩O—$Þ\˜”´/rº‡ÝëFbQžµk|v†HG‚¥Im42ä_ S"ïfd*÷¡£z2Â)ɉ¡mCªi>¤iéMÚSjj¬;]&&‡ˆ§Û_£„™3›Gò¸6ììÝÎõÅ©I˜4¿ã¶„¢@Ÿ\p•íÚØZ.†ªIêóZðxQ$Gm5áÓãžx‘.ñ'£¨ÁäÏ£“¯:Â-áåHܺ‹ÖaÔB}|»­»SΆ‹@­þ˜è¨ï"!¯…4"_ J(Ó¯ïX8ñ4B)+Ñ„@x¨Á?î΂푯 oÖËrÃâž÷ÊúÐ!ÐpàŠˆh}‡bq¸S«Ø/.‚T2B ñ"P+‚_‹£qÿ)˜±ü0îÀY)þÄ—¨kü2î´¯ L¯X¾8{¹ª‡÷D,/Ę Š@­ ~Mk•½'¬¹·™ ¾prâF{f» =rû‰£z9×p¦9B€hˆÔšàŸtÏÞÃ8ÔÆwÀF˜ÜŠSæÒÃÌ3 ;¹=`@˜=å¾hyÓ]8ØJ> zfl ^²<ÁÂäih¸]‡Vì*âQqÌÐB#‘Š0±s ;¼.<y¸÷³P¾ {^8{ìׇOÁq´uÛ…³TVÒ‡{¬I šPêR xüäEYåûÙåXè«ë'ÏÏ%n(e“y ¥Ÿ%=¢©=`-¬]‡=ýÏcOÿ­aI•³b®¨—&ÝkT:‰3ݧbÔ"Ë“2Ι6Nþú\Qø,añÓ®á6½iÎpû=Ñ=û$Kÿír¿@Ÿ”mûã ÿÕ˜Æøßû^¿Û¹Æðënû9Ÿ£õ=¼^ó\aúNôô%Ãå! Eùæ+xÁ_ Œè<™©Éÿ’aÒLIfZâ)—‹_¸HOþ±Ä7ðÿòÐäáQ̱üùÅÔ4uì"}Êo2†<kdþ~æ]ÚÝ+OΓ§ñ Sx@Ñe[×Y;ÿ®@‡ã~eœì w’7¬¹ú'9ƒ……32Ÿ;YœöLE˜ØùÎwád¸g:».p*·MWž½æýÒM˜Æ·8¹²MXòÃÙ×®ãÕ‹~ÖñUì€ëZ…«#2S“¾²)Gz<ÍB±6×ÝíÎù¶²¶!Û¢)·fèÉG­ V7*k'ÁÚ—Ç@Ï`qFNõ\6÷î—òjZÔßä™ù•µ¯òik»R¸î0Ó¦áÂ¥«p¾ÿtÈŸânß@G/±Þ}±ÞÂD;ÎÑ\-o t¹X0œÊç!qjÚ@œü¹§+îA´þ†þðw›nTÊŒ³LaÎgMµ!!µ“ÒßZ|tfšûE›=ë>aUV¥˜=Zž2g`ûp­J\'-*­WaÊ­¡}O㮗ѰnFéWväYã:¥£Ò³Òü^|hW{½·ãYÖ 6G–^PÖ3ðΗ¿È0Œ×‰ÏËSàØÏ©8ööm4Æsq.þ9È«.O&“t )ž4\yúwt>8â6ײe¾Ê”×!hûáï<ñÿ¸?\ÆLLö\ˆÎd@g킾¸âDzr/ß¶w¸ s)|£êоwŒŸ|œ‰kzãvx¿¸Q†9]E<œtrTð Ê{Ô½……+%¿7¦©Q/± 0±ó,ë:ú[pñŸ¶_]~þúÐ)hŠ2mê`Mó‰ºá}u(B_¦…ÿ%&Ì¿9ÓµŠØÍ¨[oøO°«BÛpò©¨nTÔN*j_NþÎ÷`qäHwPÌŠ‰UoD:„t¯·øY_Üê’FEm7ÛHOÐïÐÉuþ9HãJ[èËû0pp7´Ï Ð/Âm×ë ó€paó®'›F>K˲—•mWycÀÁåµ~g ã6ôŽÏ‡ÒNd$³€-D»þØÏ€^ê µ.ø‡ û¹¸8ójŒÀ–T%tP8¢S2%Á yAßbϤõ¨¤ûp‰ÎùvºE†áë¤ä¹ò¶_ §ÔÊÑ8oŠæb”‡ÛÊÓ(*¿Å ™™®òaå¿UÕõ‹¢Å Â]¾ëˇ¡q_פ óu,ò\|hùï ³ø:IçbÚ °FÜ€W£|¼@ßrôžáIú†{¾¿aÌ:U~Ët (^´G³Š`ÏB@Þ,ÃêI2ô »å{©+d onØÏŠxØ4òù¤~ÿ!Æ]#WôSNÿÊ•‘î´Àñ79J«;oEFñBEÑ&Aá «yÕæ‰çÏãzÿOÚÜÕå¶ñ^µÙ€ ãOÞ2õ2ô蛜Ê4ÚÉ߀ÝK6ª´ ;Ž|VTo*j'µ/'ç{°8­Ýx1èÖ<™”ä[›”1=ù}X»Is|(uÉN#XÛ•|0ò늛d·E;nû޲ßå5N=GÏWÎU„““ô£5¿–¿%Ó3ÑWGdYÐ.zËéI'û8+uòšhWq™òÚá……#RQCÂÏÛé•y–ÁÓèÌð+¬ÎLü°ªt}C^Çøã— §®ÕbXhø\X+ŠBÎg»q&â¯ãû ùñ¡ªd5ð .vž:ã™^‚>óxTѵ üm³*m£|žÕÊÚI°öUž·ó;XMáÒJåorÔŒz¿ƒýÖÃß÷^®.9õÝÆ]qãç¾<+ÿ¼·çiL{$Ê5-¾¸]Ú­DŸãáö¼“ÿ4¤{:kª>íäk¿ÂɳŸ·:a„ïoó>.¶²‚ãä;ú¸KÐVÖÊ[í8þg¹²I«z¢á¬K›tÔµzÕNüejä/ÇDðÛ˜O¾×|«G‹›zᆫ›Ð9¿Ž¿€+óáÿ+„ÞlWTT/( ÿ7qxÞN›GUžš¦¼Œ {“ÔnGê3º!î ´ J…apN˜»‹3Õ7‡¥pþ"ÖNj³XSc›ð‡1y`L²§«3,Ô÷1,ŠƒL/Óaƒ_·X‹Py£¥§õ¯x3í `ñŠ"òlz¯+&Ë‘tÌ¢Ë-Ëz£·€û²¸xÅOkÇÁh¡b~¼Ô0¥ ¥†F»âÿ]>å1ìéÂ,1"¦m¼§e´èm; Ï…–aÝ Úrg'%iÊ;9‡†x=î?ŸƒÅLë0ç—‚†ø÷R¡â'÷Ô9OóÂJ‰UyGw¶~ÊÀ/rÑ:²æ=Á›!¿ÇÍw´>»æö_ªÿ³;Ø öc1c3›ƒÊ¼ñè`üéd¥&¿ ç4£ÛÇ·Ç<ãyùò'Û´ö³26]Mž5ÍÇvã·+‘þ—>s¨##01ËÉÃóÇŽ-¶M×Ù7–úç ì\ÿpŸ´õúžÃÚÆsï“ûÅšª^¨(®“›5ëÓ|ľ—AIXúÕ¸.5)!‡2 +ôR™†PÆTÌû—s5mNv‘j'Î4œï\=ÏG]ÿ‚·—Ê¢¦#ÌŠk—mÓªKvXeOKо·`ò’Ô)ßbMq:Éu=#ÝéWa:î¾X-®/ÖôD;¼ Hre<ƒ†+|?Êáoó%t¢Ô˜ò†PX¯ˆzÃ?PÙRÒî¿fMŸ¼Êg}ÁˆÁ÷ËãéŒKïuc.øðp®[Fn– ö¦Œ0>ž˜èýþ¡á«d~tò ü®¼ ©<}FÿGuRåãxœ¿¡.Ãüþå˜ôÅÅ (ßn·6\]ž¶³šœ†Üá@®1´|X(ßè8³}#ÏRb˜ùOÄtÔ¢¸PxI¹mÇð¿¥2u¢sÕ4æþ„iÿD›v‡ÒqT:r,Ïa~áR›Ö~†Êæ¯É³úù0oƒ!²Ì(&&cÒÓ[ãwüßÇôÆ!ù‡ü¶a[wï‡ùõòšäýXÇ]=¡CÞúû¬þu\ßOÖëý™Ü¦÷]"‡~—¡Oùœþ̶Ò£uî¤$}ˆsMÛ†“g¸Û‰“wùw¹þgIjòKYžäT&Û‰}t ºT>~Eß‹Ú*OcpÒ€ÿ!…ñ³nÂ}ÑãúCåÅÅ“ horÒVé]h`íEw;ŽOQc¬+V¶ï0v\ËÐʹ&ø-AËfÁr(D‚ÝNß-•;lÞô¬ûÔ)Á_piB,CeEEg];º’?¯,M‹ñ»`Ë”óûö¦&ž‚Yø®òqu[*×FA;Ÿ‰F‹dªæÐ¼jp~·Œ%ç·Ácˆ¢E¿U¹ 0Ñ:¶ü¾s¹ï8ßÈ{<ç:ùI^ S_Eo=bµ@š {]†9]_© fþ‚«½K¶Ž˜š6X¾WÄCn·²é$mu\Eù¥{Îåžu²ä,gÍj†Nõ|ìØx×N?& “’öa´ÕÌù‡8{1 Ô~Øñé ¬1E:ð~U¶@T5mNžÕi'2þwú5Òtíä껜{7…wãÊ\'X]²ùÉ©`{ÿm¹àX¾Û;‚Æèé=Q6­dË­ø céslZaXç ìSùjûrÒÉu+ˆÚ"!efÉc§•v-LýkO™rmü6ìzAúKWQÙ°ë`H™vÂyvwÜ á¨’Øô}@뺗[”æÞ “ý~ÕoËwRШÇa„—`#¢u¢Å̶Î}Ê2,ª]Ó·°Emáiií Êös‹S§|‡†ü _UõGiÍg‡^FþV1oaK¬¦×åjt;?ž|ÛS‘ƒ‰YÓdé‰~’üw`|òWä£'Êô¸?@S†b”ö=ò¸<ÛX¿arGúè¶ñK%—³i8ó@Ž~wd{¿ÀTƒòÖTnýAXžu9üÓñÚ5#uÊAy8èd¼ê¸Šòab cÅÁw|°|Üuè„¿íܱQ&Yz2æÃÉU%F,ÃÖ®G¡éVØùפm8óUvâ‹é:ËTîròªìÝ7’¢'æÞ[1EY$Gÿ2NEu).¾É/¹ûò_Ì;Px>H¿©( ÕŇ›^ñ<ÚR¡×kæª2AÒã¤Óç cÿ´Ï/«¿}°6ú¶ã:ÛaE¼¢ãêÌ2–çaLYÆpM&GöyFnÿNÚy˜ú,q•Ú‰= 0# G«Ò [Œöï¢03Úò4ÒZPÞR iä >ów¹2O£fÌhéôÄ#3N¨ïÁòQ‚OÙSå#Ôtˆ®~"P•v"Þ{ýF¨%–u°|%n öSQ¼Ñú¦Âe[’sðΰPÛW0:yø“½„@=C qªçZ¹ªže›²KÔ*roÁ\­¦J‰„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!@„!P×øà`ÐS–IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-flowew1.graffle0000664000175000017500000001075713553660046026564 0ustar zuulzuul00000000000000‹í]mSÛȲþ¼ùºùtoóþ’“Í)ì&»8@’»[Tݶɑåv+ÿýöH¶e½ØXŽ ©Šmif4M?ýôtOë忾^‡Î?éqôósì¢çŽuân]þüüýé/›êù¿^={ù_»‡;§í9½0è§ÎÑû×ûowœç›[[Û½^èomížî:GûoONhckkïà¹óü*M{/¶¶nnn\Ï”r;ñµ)Øß:J➟¤·ûÐØ&Tp»i÷9\&o½Ô8Ú :é«g?½üäß¾Úî¤Áß»õ“·Q×ÿúrË…“A”ú—~ò ½Ü}Uk/…&?ä-+yIâ™/?½ì§ Üò+è¡_GÁezî!|û5ñ..B_¼Ü™(…v ‚î!1QE^nšÎ»0Hãíîýt|é4ø[£ó¯½Î'sɨ —ë]q±á½‹™ýѹq7þþm8èÛ†ó7çÐ!ɹˆ3ÉgŽTÄUJK8„¤Â„ûVºÓØNèõëížÄaPt¨RåínQ~4àdrìób'émè%G·“Ÿ„&ãOÅÙ‰ÓùùÝÄ»)ºUtìàp²;0ØãQ}+¾äÃÛ÷ÿŒãë9&ËŽ}ñú‡IpÓd4ÎÙ0O>å¬F®£í0¸ŒjíãzûY铞×&Æåß _Q˜cÙ—QQøa&í®—úÕ®„Ù&Æ›9X¾ äãÎ?üÕzg‰“jýw^š:¿ÃC÷n¼Ä«VÚ ú½Ð»=éxaíÊ “ðçt®Ÿñ÷j¿¡zÛ«Uö@ªe³ ¶w×~”Vt<¤6’ÉÙ7Ò Ðå™V‘œ ÑáÈ•”EqÑ ñ ‡!é"ª©0"„˜ Ì—Æ­HO“øòsåõü& RzêMëõþØ;i(Æ7õ¢Ç~?øËo(GéÛè"ž.[0‹iQ>Ÿ8Ÿ89¾r *¡á¨Âå” Hb)›*$Í(aTT®PˆøÄ 6 ÃaÏN¼¨_©!Q6PELÔÔXäJר"\1%.Ét#ÊÕÆú"ÃYC]Źi@W†þ•לKm»‚ÎËi»:ʨô¡«“Ï•ŸBS[…Ÿ%é>ƒidÿuz— ÉY'îÄž9ÅËôçð›Iôìï³ ˜aéyxvÎ.¢>;W^Ò÷SäŒ&Ù?¿A±Ž‘#(÷ϳÄïÎÏ.ßÌ—ópàÃgvBÑü8†+™ã¨}Öõ/Rï\ôì¬ç%Ýì¿Ñ!ó# .¯R?ºð:€áèÙ³¬3}‚œ³Îq€E} º~âD~z'ŸÎL>Îö%\ìÔÆ·)z2®€LuH)?» Ý:/¢Ræ"Ì%“ŠR±@B®ÐDc¤¹¤T° Q1À,pI5‚p-Ô†S=ÆÙ÷NTœ+oüð‹Ÿ‰œWŒ¾j ,¨ XÌ Õïs'H:aì'UuB©DƒVÞ,0`I99þ·ß¨ÆU †IBŠ'Å%ÃRâB¼T³„aë…•;qùÉ±× åKÔôÐRÀu¹%ŠºL0.8cAkr‹1uAá;Â)xÀFl‰rédAĨz ýP±]”Ì(ÁZq/ÍX .£•f”}—Ù<ñ¯ƒó8ìÎ >¢-ö0%°çw ŠeòqÙõOÓm¢Ùç »x-‹Q°•¨Tº%b!$(ÕÐ V-Á‹KÅŒ~„-¼^û˜zWMó°jP,†_ Žtn0 ñž{ sËavÍæÎL^h =u [ƒóÂTö“Ó1j´`©æºù –ÓTB¡©7M¿rà ®Ù‡P惖}(d>€Ùe:+ÂXV,h”×6gEÄávƒ$òÒAâ…gŸ;Ó(ïyNk3Òûßü¾ÝµúÓÂúÒRŠ8gXkB@•1Àmè»B`ýsÊä¼:O`«óîKçÉÖ|›£%ê<«×¬^³zÍêµïÐkb™zMkgÂ5ˆ¤CN jõÚÑkL[½fõšÕkV¯­‡^“íõÚÕ²Dþº¯í¿ñ½îô.L"ö SdX¸t7S‚ æ¶všÜ–G1ºK)T—€Í²0L@c¿AÝP¥…š¶(b˜aŠ5ˆÜ]»­Ò¹ï…ýǩҊù¼$¥á-8Q{Ú÷/½Îm©ø…ö³`§¢ˆòd€È¬ >š¬^šúIԢƩ„óö}–š€fZ‹t•…-(ÒKpãˆ,l…¹”"‰@É *ÇÖóý8’ ëDZvµ ¬]°þv²vÁ"³4U‹ÚDK¸„Ô@.æŒkàB¹ÜDÎ2%!šéi*…áJ‰f‚…ÔBî!\¢5ÁH­$âS­ŠElkUX«âûl„aÔdЭhçìVɼÊe!kB´6ìʲ±’¹,¬TdÃÁDºœkÄdB3ŧbeÙLmiD½“•r*VV®c¶1æºÐòPSi×o,ÒZ¤mŒ„üž¥F\ÿ®˜Ô˜ ¦™= °ªá)&³¥˜&­ .ÀÜ™SâÇ×j)¦ñ9âõwªÇèÍÀ ñ¹¡(_ +j’¹¡(_+jÚU» ²N« Sö\­Ó"ÈÉMv®fïÒ2#}zßìm@´2ï]ÈaPw4ÐK³_«JY ÅŽDŒ+$”Âk°_«í6ÐöÛµ„¦v¨EÓ‡‰¦D/ú„ 7Á^ÝÂÀx¡so‚u0pÐFü{ÆV,©«‘l”@—(R`˜½JmÌIÉöÔ1³þIžzrDàŸR-<¹œaÌy O.c Ìøºf`MKLÆØn½V]Ø­Võƒt"ÁÆ.r&D¥œ™O^ÂŬòqœf I*†ÿ”ô/µ;UŸ¸•ÜžÇ.Ó†MH*Á"&¬dÔÙô +L¯°¾z¤z¨x¨ÊNðõP;#æþaûÀÁß­_ŽýÐ39«¶aN7©³ªàžñk ÓM*`E‰š|pÕH!f…e rBµÓxÿ,[#ç³ךmŒl°â’Ê.‘Y£Î µ Ò6P¨m|À …Œ\Ðù]ÙwhòAU…èTô"aFOŽÞ*Í:  õá÷µq3\J…)yÝ Ên~”›†é£Ü4l9¿åü–ó?@Îñ2³a` |Ôš˜0.AAMYè¢ÚÊ0KL6¨fu½F©Õk÷¥×ð§•êµ)ÙYÃxÐ]£ä¬V Z-hµàCÔ‚h¥¹µµïžŒ¿[ûÎj6«Ù¬f[ÍF–œíP™ÝÀ Æ7{ÕˆÑl²ä# "Ólò1…ÙÝ_˜ZZ˜šh¦¶ì°4\fƒËÖ,¸ì‹Ÿ^­`Ù“s²óÕgãÐ géÓÊeæmK\hª…–´•“}ýq²?æk“ô-èk'«õµ?9„$«GÈ…sp€¹ïjÄ9FÞ±&„#aZðÕ"¤EȇŽÔ"äR’®!Å¢Ù”ë§B.€¯6PÓj>Ø@M|ošÕ4 $€ô)`( bÆs»¿!¤³’VHiiÓ }%å6­M+dýk6­Ð¸×@ç¾:¯“ {éŸÝw×úx˜©ã<ù|ÏÙ8šòñ5+Îj¬ uŹf0Uq2«8­â´ŠÓ*Î5Pœo£~êEß!k˜“¯¾6‹hiØ«…kŽp‚5#˜j¬žDN>ImN>‹¨6'ß½çäÛ‰¯{ƒÔwâîýC)ÖØUˆL¤â’j¥7•®DPÄ ½'JÚ|6ß(¼^Rpcí…w7R®$ˆ¬T,s£Pþ£rðQª]0v§›¼¿¥ì6LÒ†IÚ|?._õíVÚæà{„{²(bO4ç)d¦þÍÍN›àÐÛxNŸâÛQpí¥~mUÄÇœíñßÑnÐO+â€UeþgeO|@€r–ù1i­”³nîE—“[G}éÆi­3Æ ›¶}?ºœp‡.@F^;­¡¥ÃEý¬ês•/ tñÓ4é© ÛÐÝÚ¢|¤çäÊŸðÑæ,ïânðšõzwâI%‹MŒ6‘p°zÈ ®˜‘«]+khBV‡§ßyizåß8¿ãôn`|ªâÔŸÿ–“ÀÊï&hÞ@:* SfîÆ 6½†çô©?GY#ÎÓçàäuœ¦ñõ;/óaÂï=lï"Œ½´,ÖÃï —E¸€¬ü oâ$ø+޼úD•‰W¿P'îúÝÆ ½ÞNÿê¾ù-üã#Fþÿ¾ÿ|û&Þfÿ~ýï·'{WÛW‡ïÇç ;—;ð»{øÿyuþæC¸ çwöøé)ùíóŸÐöÛ{Û'Ÿ¡Ì/_Û_gwÈÀ÷ V³Û?c&)tCóùGcûA?ÞôØ×(õ¦kÔ,ž)äš»Dr.$âL2ÅÙ†£vÍ’7BRIüvGÌD<ö¿€ró¤ã!Ì„cCW9NãÞ*š/cyö #ß Èrì{ÝÃ(¼½^Žã›ÒŠë êE+4rø®–‚ué#èÓ ­#áŽ}ñú%Ғ׸ŠžuÃ8}rª·Ý1{™ç`zYeCŒ£¾eÝ÷QðyàæìŒ»ß÷÷¢4Hf û‡yIçÇ ãt:ªï ’F"É:go ì{_{^ÂöùOpºB×’RK¶œe›|óýáXé G’JœÀØ:(­ ¼L“¿U:< ]+èÜ›8]l˜<_ŽáŸuó u'¡wþÆ7â]µx•B µô±YB¯ñ@lJ³O^Tþh(aˆ¨B £òg_WÙèdnŠq¡˜ÄýF|hˆik˜ŸÛ/ÒI¿yyv°jàR|öB¦WÏþÊ IÎŒneutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-dvr-overview.png0000664000175000017500000071507613553660046025773 0ustar zuulzuul00000000000000‰PNG  IHDRÊWé¾GçsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì]|TÅÖŸ¹»›FGj訨X°cA±÷н=…€yJI6ºl@ÁO|*%á‰ú,OñYžE Ø)JK¨¢”²»w¾ÿ¹›»Ülv7»Én²IÎü²¹÷NŸÿÌ=sæÌ™s…`Ç0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#P#²Æ` îp=”YáñôûúBׄõAÁÍþ‘ñ©:F»ÜÇù<Â^˜ï\\5$ö§xæe–žˆ<ͼ›Â•ñi ½Èm¨-öÚ&ätÍ ‘®‡;hÞÒC„’Ý”CüšÑ:ãÇécÇ–6/bkíHWA†æÛq€Oúújº¦„›¤Í±f¶ë¾­±å[ì¬Üüë”Ò[ºsgÅ–2úØå^ïB¨G5ŸwR=}ÊøÇt¹Ú·ˆ¥=íB«xÜ5~c¤̸ÒãóÌÌwn°Æ½ÓõP÷roÅ()RŸ)p[e ‹õ>™ð‰µîµ ?¯W½‹|Û*¥4)¥ªm”®.y…{7Âå.~]êßPi#õOMu ‡OMé"…ß9yrGo…leãÒ›)Ž-v¹NõZýƒïǸ¦·-eíí)j÷ãÙÙۂßÇ=ôP«Ý¥zGŸC/-ÌÉÙdÒk<ÍãóuttÛârÝ\fõç{F€eap¹”VìÉ¿³ÚXå)íì3bâÉ#DÉŽ}DŽû»¦Ý1+/ûÓ°™4À;{,µbË®û”gÛXŸPí]=bt]tæ}©iÚ³'å¼ox¨ìò-»æ¢(ÇÈüü×iRˆwÉ–ßV±´·×ã]å“âWÔíÀHõÛ,–v÷y¼kÀ¬­A¼~Ö¸>Ï¥DŽžðe ãûšHvüb}7b_3B #Ùú§l¯>](um0*Åb‰g„3o5„2¿Ø„xev¾óùà8¥úÞ£uŸþ¾Ï#BØÁÁáÁÏ;wyÔ…ºMzÅt„5iFp¼"ÏFú¼])ùµf“Ó &å|‡Ÿ›Zók2·8F¹¦ô.ö梄z V`,æKMNÕ¤¸KJm†ò3äs¨Où>™ëΧz4ù6‡8e[vÍQγڸ}&¥ø‡Z¶&äL`ù˜±cÁ0—'‹Çﺫ\HHï¤X’éÉÞb-ƒ$8XÜ,¿=ÇÝËêÏ÷•H¹HH¹“7O޵IŽ_¤w#Ts#Åo”ïR„þéÌÏsú`(í¹sÊ,úný¿¯ Ú’…K hxndŽ{A–sjk=†Òÿ#ÐÕ͘ŸÞæÊ?Ü|?² À¡¤nøÛµç¬áRÈx矤êñ ~ó±õW‚²Ï#Žró&[ãó}óD€™›æÙï[íQ™åÌDèH0Ä+lÂ1|–{”'ñÚÔ'–4$™‹%~mâFß þjŸ®3µŸTÅjSf­ÒÔâ]ªU9õhdb¥OmŠªí{MY³ó²—dÚO<æy`t[Bã;™W)Vº¸:Rß+ݯÞSEšlÉ©Ê-«‚|çPî\ ègT‰ÀÍ–(7».Ü`"| V¬ÌaB“P©ˆ|°¡ oÂr¬¸_c}û9‘jŒµÒÇ­ù+ò,Œ¹Úˆ–¯õlôùŒwîãÖJt=اÈÜçÊ'ßy³Y¯àk` Û¸9îì7(|´kJ_Ÿ×û7Üž‹rúªõ[Û Œ­ËìÒ~ð; ¿àòÌçXÚb¦¡+úÈ8p5*7ÿ&ŸR×cl0êeGýV(¡½4ÇóOk|óá_â>ðn˜þá®Áñkz—lBåc<ïÕ7x¯® —/¶ôE}ÏVRs…’¦R:”WÚÜ?tØXx·Nº÷V*cù<”ù Ý“Ó49qö$çkþ'„K¡ô$78ŽZÜÿã¬øc u%ñv GÆ?¦»Æî0ãÇãJ‡únsM½Ûë-?sê7ùâBwöë”7I‡A£†ÿAÅúäSàµ0¸ÌÛ\3Zz½;.B(i·=éx|>¡+50R<kúÔß ¼écÙ$Z¸ð§5½@xzîV¦í„§£j”M+}]ÉSƒãƒÐôÇï´¹î+è[‰‘ÂFäæ_…ËwHú(¸½‹x Ñoéøne’ýyÉ%úg~@¹ ižÆï{LœC„O ÁÍF¼FðÛ¬í ܤ¬²3bèt{=ß#ì:ÚeѼ\Ã\´±¥_É`ÎÞG_þË×ÞVZ¥©È+ÞøÈ¢íC…Oý圎ßà É—ø‘ê¢+ýÁ"ïÆ³Rëð ꕉõÒuwççw6ý­WZè@6{üz´keC_ûÏë{yæ ÿÓÑ¿ïá½¥…\Ú}®Wy?I‹X‹ ‡Ÿ%JµÛXÚ”¸4åÿÐÞ'ñ´¯£~MÁ»ô(¾×BIïƒß <«=ǯé]êb?`12iŒ®ºÃ™×§Z†ð¸wÚ´ •·×¾ié2¬>|¼iCpÿ¤;¶·Bz¢oð×úº•tÁ¸êÊO/ö5bvs|ºziºm>¯¢­ Sw•xK–&bçn¦kÜf¼oOø«¡ÎÞWbî5ƒù5¥ÆÖ0º÷ùþºc8ƒÆFìf,µÖ”hg`áKÏìšöæ×dnq$t]'i"Q‡ßj2Ñc擪ÙW•ù Áš?­¸ª~R—wâ à°ÙîÜMïÛ]“tŒùùÚ†­7ÁŽFL¦ò–>…gÍî'Îr9—ša` /‡îÚË>% À`R˜•eHGÍpîî¨ÿDMØÎ/pg¿cú“‰ ?wW|òÚä)º þ˜a!¯vÛ3Ò«»ÿ*Lºcƒñ0¤ïËW^mH+”zšò€÷ƒ¬\7˜{q´ô”} =»[i 1dþðt8R>ðxʱóY}‘áÑÄ%0—A’bÒ%¼f¸²Wšy-ø~õQ𧃋wݵËô¾æ9i’y‹‡¯p=JŠ”» ò«›?ƒä¦‹ÇSñ/”hפºµÀí|ÒÌ‹¶6·ŠiÌçÀUéw¢~¯‹–Ž› 'L =uÃÌÍ¿UéúL¤SݳÁØ™ñÃëÿ*q¾¿Ð“gÖLÁÝe[wnFXÛ»|b†+'Ð7f0mqÇgö?²Œºò)¶V¯[ûv7¼õÓ `'A¿vM¡+k/¤Å´u}ËÞR,~„¨&i­(Õ±ØR,^œ:~ün³îX˜Þ+íº§Àå\fúѳÈ[ô/”q­ðêÙð'Éb­],m±Ƭ ä›#¤M;º·˜a†ýjoÅ[hïÅE¾Ï¨n´ˆˆ›‹æ]ÂÂÿià3®BZ$Þ\øî]—oƒ¹dÚ,Ñ´áŸ99tØw85Q>ý-ÔéÓÂüÜkƒëk>Ó;€8¥M^P8Éù¶é¿®ŠƒÊ·í ÿGͰx]¡¨†rê kžš]{ÞçQx'Õexo7`Z"€vûÛ#å³ïo‘N¢Má-~Ø5cX¢ÜŒ;?TÓÁÜô«ô_*<”ß~b\1˜%ؽT-`’¬k¨8`\Ÿ°2ɇ˜>MjwÓ=$,ÙUô̼¥Ù~Â,_¶2ÉÌ/I¤Á«ÚúmþÓÌ`uRδ2Éd0R¾B÷º8¨Ù‘ôÕq³÷ó*’ JùÑ«NÇ„ÝÒºEÄ”mMjiòàñÂôé¾ÅÄ ãJŠmHKp`etÎrM®²Åb})˜—R0,S)oW]DWÓ!ü4º‡4ð=Ó¯.W¯×ƒI&pìrL2åI:ÕþºV-íÜ›šaie’)FëÖ)¤ãW†>ìPì]Ü«jªÄURƒñ¥{Ú&G‡€ù[œ’.ÁÓÁ%u1…™N§ÁÁôk¶¸0Ê`¼Ï¨ÌÛP¥1ˉx…ZF(©ØÃ÷ÝW‚íÛO(-*Öˉ"xÙQVç~£È*…$KhGZ°tµwܨ¼ÁBà7ãVÉvÁa•þõ‹T ýõðu5ëÝd¼+ê6bðm·ö1Ј@XÐCÇ )Ÿ•ç4úËLîÚMˬ,CÊ·Nþ!ÚbÍO³Ëï¸Ñ^´L[Ÿ?ÄÔLkšú¸§Ãº`Þ>ÄØéQìuŸn-“vmð|:·v³Å~d—,´Á¬¥ ƒ¹IWñî%è]·ùç#¼gÆ;iVȸªçè¢û„_z\¦¼eWâý´ãñ-ë¹Ê`ãBõ…žóOô3Ø9î¿*„Z‹wåôÑö–¶Óón˜µ­|_{h±cà¼ÆzrQO‚†~ñ†­(QªH_O×ê.5¤?I”@¤Ö ~¦Ò´Þ¸n¨Tiè všo»­z~~èõ«†“tiúÆPþº7å˜"î-%Tx°_WG·× óI:jŽ|ðÁ6¦ä”ô wþU~1ˆéžÖmR«I+*uyŸQŸ;:×}*$æ£PèeÐÉž„ÃWçÞéz쬀DPJ0Êê^œÞ&FÙÐÅ++ÈCà.Þ!F,CØqÄ@Ó–)ébÛÚÿG¦mÂ×BL ®zLÏþCœ¿öF"o¦Ýù{´¦ë0Vª1fÁè¿?МSÔj´8BÒt¨ýTaâ(Ÿ”4íãÙÙ賨Ü*˜zª"q NEöÁñÁ´/:Gã;c±s^‘¯˜*U¤Êx¾ŒrBG‘4·š‹Ÿhq fá¶û§œˆ÷ã0¹=ñœ‰±Žq4e‚·Ç2Íâððü§øD-î²Í ¯×kH7ñ>dúXP•qÚÂÞâå=Þ’Ç0†Ï·ÒhDò3ÎR{.\9ÀØH¡÷¦Ò¡ȇ^h7ÔlrMo¾6o´æÝ|n}5”ñÅ2òîU-,ŒGʆ­Ý ¦‡ÅÂz΃‰Ù é@›¶R˜ƒ9ÿô§ß»âÞaDV’,I„úõƒ”ïOl‹WÒϪY·óÔüYÓª)B?[ÍJ¾ˆÊ¥É=žËÍX=C˜3¤§f@Еˆ:TN>‚ÅŠá€öb Zz\¹w§ËŒÚUËü”ÔÀyœbnÕ‚y€#t˜uñš?žö?¸´L\@ÏåÛwŸ@uBº•}´ZþÿxùÊLäoÖø„k ¦ë4á—ôÔ²\3Œû_mP£Cj–Ÿ§¤:ól¦©¯+>ºlL´X·M‚e4éÁ‚y>q„3ÿ1ôå ôéÒ.öã燬OŒøDƒÃˆûÝ—AþW¯î[„þú'úíjLøý1Ú .vªdU˜Ø‡ãßóó@Œé¾ºJ0ÊRaÕøÌ>?!°k¶>_¶×·Ïyaã ³;,J°Ç~Æ‚˜Š:ºÚ´Å,²‹·[„”2Æ%,7dšñëõÚ³#.Ü ˆ.ºÇõH{³l`o,L¤°?múEº&m°ÖoHÿþqyß­yFs…ߊ‡ñûkp|cÎQ Uª*ñ WŒÝýñ^‹wbG¦½_$ÉýÐæ>æÏfÇ\CK@]Œ¦C®ÁeñsóDÀ ªÍ³éÜêP`m½Fй}©ú’Ô4h¦÷ >! ~UªÕ¦_ðµØñmyo ö7ž¥ê@Ÿ †ÙÞÚñ‡øÃµ,0É„LÆó„r¹ÂÆèmsˆ§|1 ºIŸ¤ä¦ž!nŸ¢çhÜl—óKX¸ §ÏÉ’1Âc)1ãøbÞ§hï›}©Èob·wˆü×3Ü~ûi{½Bé0Ù¦“žò¿0á’ôQÔ{t­«ó::þ!<€_ú?¹m~XiëÑÆÓ{8¹38Ž–.~ö«ïg2ß••ãFÕÓ§3Ë=3:`&ÆBè%‘.ïqåœê U¯Xñ© ŒŸ³qÂÿEðº¥àRoŽŽ/ÏÁ=³l0ÐÓP­{¡×ŽáºÏ‘î.˜{Xæç`‡ã„|<Úå>‡ÿöÇ‚óCóC”‚kø*盈{2Äk°•þ@aÞ„ïÍÜü»)e{0†«”a†G{­m[Ìü‹Ťîš¦ ºêUÒ3M}]é€1vÊžBÆ—øö^ƒrŸí|t*Ó{mÅ3R’6XëwÅWÄå}·æÍ=èY¢O¿„ŠçpÀû"˜L¼ásñ~€¾RÿËyÕ,õ„Ê ÒÎàßÈÚË {õ)ð!:5˜Qn&m3gåäCæÁÔá»vVÊôšÒú„þwŠg¥ô3D ¡ÊfR}ŒvûZºN;¶ V=ït=Ôýq×øFxý#LÆOÄ84ÑæðúöîÅ!˜ªƒíäűT+ÅÞæ³2YaRÁ’®÷‘Ï>¥Ÿ,w«~~‰ºöŠ™÷Œ¼ ?‚™^ Æx¨a{vÃÖS)L¦‘ÚFÝYG@7¡ß»Ò!˜hìL×½Ô}9`AD8Æ„å¾Ô‰¿Ã§,È„šz$ ŽÃ«jö~%}ôx–^PáÁÄ­ìØ»ÛÏ—E˜¡ç,Q®ŒÆMœ£ Ûÿâc,þŒ­i0óäp[ÄäC0NÆ*à÷nö!ÃUJKÉ«ë´kâqHû³¡ ëjïúV±§ˆÚÐ)~* ÷çjaÓ«]„ʇüdº6A”ê—‚Þß:ÊåþÑÿpqÙ¿y ÉŽ؇© h6y/ù`²¼Ïrx_xðݨû󡡆‚i\ßÍž9=8<ðìÕ/ Ü[nÈ21Ä´MÙEgaˆåç­Ü[‘+z]LnJ–{½W”–êPÁPø¶€|ÚÒ”¨n+|{ûPDý­ ì?à &ì$øŸMava¯r{^V-Éö,x”c ü!&FÔ;à4éíB׎ô ñýŠC„5[/Òk†÷q0æá¦ã«]/‘:MLØÇ=ìÞ`lØðA— Wi‹ü¸ ïÀcjçÖøxؾÌåš—‚1}ÆÐÎôý2ªŒ3Ý£Œ2ÀÔ­ f’)3¯×Gc4®Öm1 GSr§÷³_uš*WæQó»$*-á,À;{¼a]Ãw9tjÿKMM@‹ º¦>]ˆ¥ºqk|ôF× ‰öba6•¡ 0Tä„x†t˜þ¤]·‘à·Y“&~*~$¿ÂœœM8,î¦2}^5ÓT‡‹”†Ãš6Ì(7íþ­Uë`réCL¨ÐëR}Þ²oC}؃2†ŽæÝ>]Ã(Dj+‰UÈ2éìQð‰{ÒƒTéÿü 4ØÛÝ·}m³Û²Q‡2Ъ{G8'‡d²iò¡//…,0ΞòYL¼^0²ô•§Kq¯C-à™àb²îÏ?£òd{p0lC+Ï, Àaž·­È´òÜŒéàH00扃?âýº‘VÇÁ?èpCÊ«4ùwJ`†ž³µ|º×4Û`áQ¼>’”c‚ÃIo5د9®žþ>ôª€¾<¥¡É\mÜö ­ÆB,T>†9;%_ãÖa“gå8ŒB²PóíàXãk¿º ¨¡ÁzšYΩýñÎÒ–t<\­ÛB…‡¢)¤‚ñý°¿rUiJ<*lÉÃ_÷0ï’*`ò°¸ê¼³tÐôpç8Ì4Á×DÓMÓŒ¶àÍ?iŒkzÛàòëû9Êíî†]E·Ïãû‰pÓûC¦½;˜×Îæ?Kíü)DAÊž>ø!u• ®öþúÈÕX,.òåªÈÍ{³k178*Ú´I¾sgùtÌF#|B~„mÿU˜8—#q˜¸AˆAÿ/÷a{ëúÙy9óÂf )ˆð ¯¨X†Ï¾„‰Ÿ7]qPëRäÓ â·ÂÞÁ` Íä¯äèáõB=îd~DcvÞÄoá?ý3^÷ˆEhï»(çG, zCåã˜2ïNÒ3oGe7'×ÙvüçÅjÉ|à~¶PžBY¸2ËD?ÿ‰1º[ª—ÏuÛðQŸ qt'úêôß”[ÕîóP¥Îx—èã(†*”QÉ X ÍõéÞ;06'’VGsƒ¢À¾ãeølù·hïž½úÐ{~ Q‹ ~¨RׂùÀGÄ9`U3‚ÓÆô\‡¶ kP‡µVš‚ͰN;w–|º½:M‰©n5E®á]2“«ûýO¬ß¶*1w/ íi3,¶kâhÃÌ&üu»/A‹Ùë-Y†1= ù¾2J;'±Õ³±u5óÂÍFJ%21Þúb c 4ŠìÈߟکÕT×]7—GÊ=SËþ¤Hwo4‹»í¹Hñ#…‘^sÖýî±09÷òˇõ——C™ÁŒ”‡5X¢Ütú2®-¡C|sÜΑPø\ÛBLBmAH¯Ào þ™ `% úÏ;”‘IF­×~úaÎÂ-˜ai¦š†/ŒÜD°’‚HG6¸غœ¥Iû1˜c¢>Ö(Œ(®ÃD[>¥Z蛃Ó%îÙöÊvÐOCÙ!Ë‘âe`õâ‰y_wÓ„ÿbb6 ½ÉÊt 8Û*=7óAœÌ{)ýdÓ¬QË7éÌv)p[d†Es5> µl`çC݆£¼Ùj6fz˜W›`ô¹0¤Š">aL_·‚¡:% ‰¶·¹\7yŸŽ1Û Ìt”ñqÚmÁ<èÓmÀè,@Þ·óOœæLr¾ݙۑ'Ó 0…ú£èH‡åî4{ʱøêxA¤òh1„v,G3ІŸ 󲿎OcS³Û±k"iq°?®Ât¡šŽ¶žj<VwàÝ]œ.Ö纵EΦ)hÓ}x?ÐOáiJ¬u ?šw‰ÒÒ¡>|勨jÁ^ü°AýjuømKm@?+»tÜ€1ô%õ7hW>+è²®ýñõ‡IC,þé‡|{€Îü„1úúÑ¥9li!üŽPåôQÊ( 鿲~Å4Tüšü &9AkaÃ3)+ѧ֟Û. +ìè0lª–j™ÊÖnU(Æ6T.zý Â×ÄÎF™âúz›cƪÙÄ •ù‘ßO~ZMDëºwwEŸ´·‡Ö÷^¯½íºh1KtÝIe@ß¶·ŸnóÙÒZ§¯ Þz7Ë¿ç‘GÒËv•öOécÝ.6óhìWHÕ³À¤ÌS\ØÂ‘11Æý‡çÆìü—Ãê£Íô>,üiM/Í'Ó=ÝÛ­¬ö÷8U‚¶âË|e}„Í^”¨žñh é‘Cêc…¦Ä¢hÞ¥,gþx]é´õTØær•©ÁÖvwXŠÜJ_&MT9œ/#И`F¹1õV#¬«ŸQmaBX?£Ü›ÁUn¦€Q†”M™æpôg}…˜ÉÏž?!Å‚”ÜÍ*nv %•dó‹­þÒa;¨Ð•ýKˆhìÅ0IŒ«^$qçpÕF á &±¾û3“Lµ+óî9Ü_KùMÃÕ–KNFÀ$ßf0ÉB¾ÍLr2ö׉¨>ÌW3Fƒ`š!´‚ƒM‹ÀèœÉò$»=uæL׸€N<™XÛä]}!l_ÏÄÖœ[ësš!LÜäJFùÖ=ç@%ç!û`IdRˆhìÅ0f”A'qF È·ÈRõ ˜å\§< 3ô6åZèÊïWäYÙ‡´ZAåb³¶k &eØ@µäb“:Ð)…oNÙ–]é460Vðtu÷lWî—IRE®#ÀĈ3Ê1ÆÑcC‡ –â´w½Ø:Ž­f›¨úø€Ëµpà&ïç°Z¢Nƒ:F&ã  ; ø¹ís˜{³Ö jÎc4uìvµÖç• ÑÎö }¿“ù½‚–Eš:Ü>F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`âŒÌ²cF€°"àr=•¶Å»µWZ{ÇúécÇ–ZÃ’õ~ŒkzÛ2_éÁ³ó²—$k¹^Œ#À46˜Qnl=Æõe„!•ë¥ëâ°?¾¬fÃÇEt¥äjMÓ òœ³Vp2ÎÊq;u!&ÉVöv…&ìŒC–œ#À0Íþ2_³ #ÀÌ›7϶`ùÊÿéº:OHù¤MtåX-¥··ð‰3Á<Ï‘ã>ûŒÃ\6|øp_2"¦IÇËByö0“œŒÝÃubFŠK”iÇqµF ~dåæÔu½@Ó´ë òrž Î9ë~÷EºO½.5mDa^ο‚Ãù™`F i"ÀŒrÓìWn#ÀD‰€ËµÐ^äY²jk ÝÎ!á’Ìq/R,ztê)7l¦+ùÍ!¯íÊùÁšf¤kòÊ£¿d“ZÎlwö[6™wº2G*q„b›”êÍL{÷l—ëæ23íHgÞR¡ä½šCTèq?â Õ„xPIu´TrcA¾óv3®yEþ‰ð~sܹçáþF¤[èÎ9\¢3N¤²³œy"b¯Bwî%f|ºúÛ*R±h89(¯—„жÌqçÜeÏ÷Œ#À4U@‡Ù1Œ#Ð|(ö.î}äÎJŠç#¢ É•]›þèÓ¦UÊð¢ý|^ýºji¼¾ëÀPÐ[jŸRt‡¯<Ÿtž5MŽÑ}Þ#мEo¥=éΓü:ò†^´«i¶wg9˜Ù[è°ž5þ¸‡jEù ¿å†¿”PÆ kœšÊVB[‡øÞãz¤½™n´+êq*Ú:$ëþ)ǘþwºkv\¦)µÖôã+#À0MÖQnê=ÌícšÀÁ=È{¥$¦1¬ƒÄy‰i+tµÿŒñãWŽtº_‘B]­”šh•º*!¯ÿët îÎÇK-ߺkšPâ-H}/®ÌüiHr—!ÝÜ,çäs ÜÙ‚9ͱÙäÙ³'9ß3ý iþK •[êÝsüž4ýÿÚå¹þé6‘òŒég½FS¶’Ú¥ëZ‰·ìd¤}Ò{= õ”ËÀ¬§ ¡_¯/È¿\ß}1ûJs|@ÏìF€h°D¹9ô2·‘`Â"«)P)mkØHJÛn„+Ñ•®R“Ï€±í9Ê™Òï²r'Œú@§Í`^+¶ï> qÍ&ž2ãÐU¥k0ã*õ*ªRÈgOÊ 0ÉjkÀˆ/F=¯¥ç€“â0ï_Ìrÿ5àg¹‰¦ì¼ ß#ÍRø†šIÁ _ŒÆ.Då ]£ìwº~*Å¥4¦_F€hê°D¹©÷0·`"" mr£òAV¬”Á‡‹¬”×`¨¥Ò7Rœ®ZöÂb=Iñ¸ˆüÀ_+¤(6è€ ð,u½/ùë>1ºÂSèžœ*Õ!Á&Ós¢—ß§ò¿TßUy6¤ã­Œr»»Ív:‹îÎÏï¼w¯õŒ;Ì(Á×hËFý?D†Rú‘®{*ç›÷êRÙQ¿1w8óú<áÎýŒó©hK“ƒægF€hÒ0£Ü¤»—Ç05!àð©ß*IJ½wĸRö',lvûjŠçrIjÿ†úÃh—k·uÔq(p8$ÍO™&ät)[PHiÓ¤Vœ¿OÈß­~W‹Cá©¶VóÊ=»ÓËäUxü¿Ò2u%¤Ö^-ÆÃu¡]´eãØßÄ쓞ò^oÉEÐÕÞÑÅ~"ÿmZ‘gåî ).€~ô¿÷z÷©ö£¡Kc_F€`š&Ì(7Í~åV1Œ@”ttäl(ò毃šÁHòX¸dàw¯‡jÄÚSî¿nVe$‡Ýöï /§X_u†&Vy!™í(m6Cí‚¢hJ[íƒ!fa×>‡uŒoÂå]“ÿã®»v) ºÎ£¬tq5å7fMœøg¸´Ñ–­¥‹¾R%÷xËN„Ôø¨]¼årжÖ:Þº<³D•®EٚÑÂåp€³?#À4IXG¹Iv+7Š`¢E€$Ã`s!>’ì)‡J7*Ç}-Ô*NšÌ1¥Åo†+{%T(>‡êÆå°³<÷_¸² äaw| Yu‰ò¨¿Úß<ƒ:K8x§Ž“R{:bVQ–MªP©øY¾ÓÀ$ŸuŽWÍ|mJ¾¡„8ê&¤ŸüýL׸Íf_F€h°D¹9ô2·‘`""ÐÍ‘ó|±×}1}tÖ,NB{C³«ÕøR_åçú„ ý‰ÿLÊþ>8œ×3P­˜Téi^ÇÙ®û¶f9óó Œ<¶‰+`#&èä^åÕ†€öX!m3¢=—iw.(öäÁnò&o6¨ß{¤ÎÅV¶¶@:$æBÏh›Aæë —æhñ6T.ÒÁ,߀ö?]éÍF€`š ,Qn6]Í epTÖ%.Ó¤¸ÒåÁJé/z=úr0É/CÊz~ÏkÚÛ³&Nü3YÀæz0Œ@xFO™ÒNß«Ÿ§”º¿³±Ð]Ø# ݹï‡OÕ¸B˜N5®þâÚ2Œ#`"PŸsTÜ呹î1JK©~€dꮂ|ç"³a|eƇ@VŽûd]ˆÇQóƒ¥&î-Ìs>ÚøZQµÆL§ªâÁOŒ#À4V=GÙâ ŒòQÓ¥¯G§ óÆ®Žgþœ#ÀÔ?_úѺ£†]ùo©ï=Òå±G ¶óëE-­ÿšÄ§D¦SñÁ‘saF Hô7‰2mc*%ßA†¯¸s®„ºTIØ1Œ@SAL²Ìræ¿„ûR¼Þç6F5 ¦SMe4r;F€¨Š@¢æ(­j1µ{¢1H9‡Ô-„£ãMÌ$×GNÅ$3Æ{÷uü¿9•ï}2W¹JݘNUƒF€hR$jŽŠ £LÖ-Œƒ{ÐI.teímRÈscF €½ß wÒûnXµ „$ÿ Ó©äï#®!#À0uA sT\e2N~>Ü«K÷rZF q @ï9½ï†éÇÆQe-™N5¦Þâº2Œ#P+â=GÕ™Q&#ý~;ÉòùZµˆ1Œ@£CŒòóôÞï#¨=Ó©FÐI\EF€`â„@<ç¨:3ÊôÅ=ú˜ÙIŽSû8F€Hrè}§÷¾ò‹›I^[!˜N%}qF€ˆñœ£êÌ(£U½è‹{ü1‘¸õ/gÄ$=ÆûŽ÷í•ô•õWéT#é(®&#À0uE žs”½®•Á'©3‘Gq]óáôŒ#и ÏÑ£Æôþ'½c:•ô]ÄdF ®ÄkŽª3£ »É­Ð²=qmgÆ0IÞ}zïéýOzÇt*黈+È0Œ@\ˆ×Õ‹¸6Œ3cF€`F€`’f”Ð ÷<òH:¾¶ÿ½Ó¦µH@ö ’åˆ÷õhÓ7ôå³#œy‹³rÝ£Ìç¦x‘›ÿ7´û˦Ø6n#Ð vw~~çÛsÜE¿¾9vS•63®?Ô3uV½¨çú&uqY÷»/Rº¿ç’ã©¢;ÿ*÷Éú ‡¿Jq8rŸpO¨.·Ë¥4—Kê‰ ¦V:a‚9š78æÃðü¡Õ/÷£r'Õ‚ÅBŠ%…yΓP—jŸG#? õ9¦0ß98®uP¢3ò«Òî¸æÏ™1Œ@½ pë‘ö%ÞÒI#sÝ×´a[*tdŽû“eB“óæäåÌ­—ŠÔS!ñž˜×SÇq1I‡K”ãÔ%#só¯“ü*ˆ®W“âo6‡`:œøÎã’ –(Ç¡; I¾@éú3 ºv³åÜg‘êÒ–ý«þ"œq()|£r§áÓ½M—I¦¦K1»¦SnËq¿13ß¹!<Â0Œ€—ë©´âÝEoᩇ]:ÏtÿÙ‚ÍRÜÿÓòÜ$n:0nc„=Ì(GUؘºO8¥»ÙL´0Éaãtd(ß¶<(Iœ iFo¨f¬„:Á¼®öìiÖôؼæMΕ-#Ånï#Èp(âwÇøƒ&TîlwîGTÈ(gÞ]y'Ñ}¹ÇóÔ=<Ès;ÔN£<æÌL»óú"_þ? ´pµªcj§6¿ë®òhëByGã(?áÙö$ëgBrÓÌír›Ôœ³ò²?5Ó×T'3^ðÕa·çx<¾S½BÌBØùÁᡞcivnUJì@»ãÔ¿í¨•/ô³OG?äÏ#gâ¿™iïžírÝ\*>û1Œ@à P¤Ÿ IòÉš¦eÍÌ«Â$‡­ÐˆÜɧH]ÏÅ»=ôÓZ°Ì.R&X™ì}¶§ß®¼eI¡NM(Aš¥½ã]BìÎPž²iH;t¼µTòs»Ã6f¦kâj³`⬖öŒ; ñ~iOýq |© qg»&® Äuæß'„~^¡;w¨ég^¡zöhÐÓ›i>įýª ŽO³nñ¦Ã˜ÏB»óÞéJ¨tôÇ—ÀøówδPj|f=øÊ˜°ê…‰D-¯.×¼¼x¤«;Çå’©²fMñ•gÛçB× )ŸÖ4yƒÔÄ[Ðÿ} È“ÿ_k\„w@Þç‹=žOpïA܉xÁ§(%:ø„xûg^Šï“¶•ðŸg¤•ê1¨|¸•Ôõ?Ë;¡È;ùQ0ægƒq}þãˆIŽ©.Ff‘ÿ,(p mKëZ6pÿº€^]_8òþüS©Ñ®pu Ä q£ m¯Ôä»óHÕ%D”*^±´Ö8ì ÌÁD÷f×ãŘ£ ú«¿UÉY9î+Qÿù ¼6ôÉÄ}Þ#мEüuÊ`°ø™hh|êh2J‡ÚÿÉhª2*Ç}­T¾ÀÜþ‰wûnü°(ÛzEŲ¬ÜÉÆù#“>{K€¬—Âv'hóX<_£¼[_“ü!±r)µûpýò;Þëõþ—t‡õ0òPçíñ–,MÙ]é1 “ðÕË#uoÙm®©]q…Þ;j÷=ï»­=Otž"ò|€ðºÒ¯XépÔx¢nñ¦ÃhëÉÈö]%ôtMÊl!µ\ô埘O{3“ dØE…K”£‚)|¤Mbå 0n©Ðy[>Ö¾bïªÛ‰¨A:qˆE:ñÚ(çä¯|Ê÷f–sò¹îìwÌ ¼Ðy~«ßlÓ/Ëå^ {ÄW!/ß#sò²?Á©à®BWw¥ÚSÞ|Ü5~£—®¨_oH;e:beæc­‹5ÏP÷jã¶(íp»C;b¦+ç»Ê8OŽpº× ]ÏǘéÂÕÉ u­ðV¤Cbò¤¹×BüÑ‘®‡ß/tÝ»=T\ò‹¶}#|°Ú㙈$ÿžãvÞjæ7Æ5ýåO &À}îÎÇK-ߺk@}kŽ;÷âʧ³œy˰ؙÜûRò#À4x“úÛðáÃ!_ˆìÈRÑ®¿Ê§)!_-,Æ]®…Ï{/ÖuýQ¼çÇ™LÑg0áóæä;'Wæü_Ч ЇÀ ÏݾÝôÓ¶[*o“wÚþð[eÖy´Æ‚û•·óÓo”kÊ;`”Wz½å÷Â~Q»HóA<èW,t8<A‡ÑïØù;ºÙ7[vkçF &Gd€À¾•-ÃQ;|ò@J À¦è2P—C*ð¡…I6’ÍvgC+×`å{Up>Ênû·Õoö9_£¼í:VÅVÿˆ÷J:­L²?nìu‰T†_­C~ga’è˜H }ƒ‰HWI²NUbTy"Õ¯ÒîÅFºð–N¯¡ÚCtí“{ô!˜ØÚÚ¤­ÐšÅ£®{þ‚þ-«_ÅöÝ'b¢ì©óSV•®Q!B^ìF yâ@Р¨èóîÝ'`ßô€vÞÎå:Õ º=oø1·ýãÁ>ÜH™òr•g%?¢ge“Uv±CeøKQÞßî5åxÁêG*PÃxå«]ïãA¿b¡Ã±à™: )ÿ ,D:lòäÁb‡ƒu@Í4=œ:v¼Tºaò ’†Ìh²ÂK»?âÊ~ÕR@Ê úû¶ná¨rp¤#Ç ÷lw´3%¼(µ©K qˆL0}1!¥C¢ò“5Œe{XlÀ¤pÐ[ìKÐKŸŠvN1T©N‹%eúó2øÊ0 ŠèR1ôƒ£¢ÏPµ"i¯°9Rtʬ¼Ô´• /¾Ïë%ý›é¯ìmŠÌ{ºb½ËÙµM·sƒKH»|Ô&ÝNkïXoÆ3¯š&Véº8Ý|ŽÇ5žô+*:ž‰ ÃØý/t”óA—'{–Œ….waZ mÖãÙÙÛâ'çÑ<0$tÍ£© j¥ÝnLX¼¨Æà†-Q)OÈ0%è^U©+"N»÷Þ½!ãÇà9ãÛJBF±.!ó¨ôÄdÔدqxoj•Ÿ&'@™÷foš¬BœÂÖ)R!•a]íNHwäR°§³osÍh‰ . ¦áü­Xki”­žæ+.Zé¢ nâ· 8X«_µ¹ì†Ø!hÀ¥ç?XC$и8Ó¯héptx&†C]Ï ™>PÃx@Ž)ßë[KzÓ5‚ÅJìŒDÝ8màþ¬X ©£º‡Ù.ÌʪN`­EHõ5è^h©£T=Á(¯´FOè}œëB´ÌrélwÎÓ ­72'}³Ûs¼µBy¾ñxÿ𠯵 ØU-6Êöa"]GBcY¦wÃe§5Hªq˜†BýNSÚjÈ”0}iŸÏvå|cúó•`’XZâSâÆ¾_u#j87R-uaû¦ð…¾WöD¼?«Äõ~kkªøÇáao‰·+²©*ÅVêPž Æn™¿ ®Ú."ÎjtPžRGõ Y«xÓ¯šèp,x&’æäêÍ„;]M.÷ìzA‡I@ö~¹º:bHØØ³™#PmÛÌñˆ¹ùt@§iï'µ±qÛ¤WÿJ~‚v&7ka#s<ÛC‡ÁÆGVÿèïý:x>©7Dçâ\´‹NŠŸQ_Ÿ†‘7áG`ÿ 4nÇâcPµFGÙ>»Ò19JŸðè0!´ÏùO§ë§íóÁÝÛØ0åQ8¸ÈŽ`’½G§§ñήJÿG–sjÿHõMëØbvÅÊÀ,_W-žÒ¯Óº¡«èW•¡­1v¯Ïw¾5Õ(×´N˜SBþYÀ_ÊðkC6’~¸uzÌaæƒЯHt8<ëƒ?îºk0} 8î·Ù·æ`+Ž|Ï„C€%Êá‰Á¿‹-ûßEzþŰÄ0!+7ÿ¨Q¹ùÏû„&No k8ì§eàô3Y}mÚ¦þNU_ ¼¥óa gŒÝ‘²Z×=‡é>ïL¬¨éjò¯ŠD=ãÐ~‹?Xñë˜8û;Õ¿k‡/ØúE råMÜëÒÒ‘/÷x®‡ÑÑaýa¸ÊåJ”÷AûŦŸ [`®à:Ôõ¹«m@~‘÷×+0]œ…ɰЄ7ÚöÍÈw®ƒîÚ³`ò'ÂzHqªÍ>¿Â§ö+öæÿõÛÏZÇÙ®û¶f9óó Œ<ýW¡9Äó(w¯òêG+j§´Í(Ì›ð½5 ß3Œ@Ã!@»|Y÷çÒuõ_Ћƒ«P@IDAT£oð®O6ù•]i[uÝ7GÚqØÏöú켉ߒÙLЀ›AËÿ÷{[ŠT/ûDJŠ.=·"ýeP¯º>ÞRHÐ}Ì…ŽÃܱ#ÅfÿÈãÓ;êÞòGqæÁ+ì¶)&rRØ"*ªá{í™*SÔ/¾ryv3ÿ8`î÷¹póA¢èW8: ž‰ ÃèÃIU+µi[Ë|e{•>;ÛÓÛ¥þ²1¾cÂ#ÀåðØDBÛO0t1{ŒÀJµ+(Y¡Ð½_‚iÚ¿Ã$q@„2|ø¾ûJÒ3$I~cö?§|“O×_‡ã+G íD:]uÁ–ˆ†é#ØëDž+„Z‹Ô¨ÂïºN˜°SK“GiýîxNWå+!%–<ÆZª×[cÒRòV0«ûô#*Kˆ¥}ÝÝFƒ¡R§垊"¥{?Gž{°ÍzSp…iÑ£Iu °¾‡s¾ðyôïq°p.&¬ÁRiU&¬à´üÌ0õ@Á¤œÓì)‡¢ä÷ñ»Cøôw¼ºw¶à °è=tÛ8§@5›“—ó"©Ñ )W•+¹Æ«*~Áû}˜ëË òrž£8ñt \e6Ív®ô ežŠ øÂ*-ø»AmX¡+;ÀÌäMX›Ì£Q×ctŸ¾ÀW µ ]åJ»¼»yË­uŠ4$‚~E¢Ã±ào: &Ù‡¹(_y|?ïñ”üáÕ}‹`*µ…f—çM;6¤n¹G¾g¼_us8Qú1åiáPº²ƒÑt|xöiG΢Kq¤/µÑÖþ6O~S°1ŸÑb œ½5ÑzûL×í{¢MïºÐÁ™+~í-Òµ½§÷ï¿5ží‹¶MÖxѶLmK»“îy4u¾ÇõH{|Q«]ÛV)[§Ž¿ÛZfS¿oLï~cªkS7Éо;'Oî¨*lŽ'\ã«X¦®Ûí“'ï‡pú¬‰«ê+G¬åóHgþßÁÈ=\èvB‹L*¢'Þ”2[$« D˶zò»¥8Zì6LXÖPv¤ù ¾éW´xÆ“^:&wôzdM´Ül¨_Ô€7 ’†îSEÌÊ4 h¹Œ# éÝoLu{ŽÓ4 FyDN޶ŸDníãV0 ‰@¼è>«^4d/rÙŒ#À0Œ#À0I‹3ÊIÛ5\1F€`F€`†D€­^4$ú\6#À0Œ@³E@ ÇRz·ØJn¶XpÃdE€ådí®#À0Œ@“F À=nH?vŒ#¤°êE’v W‹`F€`F a`F¹añçÒF€`F€`’f”“´c¸ZŒ#À0Œ#À0 ‹3Ê ‹?—Î0Œ#À0Œ#¤0£œ¤ÃÕbF€`F€hX˜QnXü¹tF€`F€`$E€å$í®#À0Œ#À0Œ@Ã"Àv”.`F q" Qmú™Îzoúñ52ÊL÷ÖgKß2 ‡@£e”GçNìÓ}gº^JªLP¨V cÔ в[*Y,”Zo³ÙçÏÊ›øuÃÔ$¾¥r߯Ϧ:>âƒNýäbŒeŸ÷l!eÏfO§„XgÓlï5b:e2Æ´«Ý0fâ`Gjê`•{ MvÕ„lY?£ªi•¢¤Ø-tµIéj½îó½÷ÔÃy4éø%5ãÌïv|Æa`žJbúШe—ë©´"OÑš&ÇzuoWúìg«Œ4½më ™æp4;5’2Gÿk×^µ{o™<ÜY¹ù›¼^ߣ­ËZ>>}úزJBŸÑœà\¸oãpSñG'a9Ê{îy$mWÚž;ívÛƒNiL§,t*Ÿè”®«Gº9º=árÝLt*Ù1È4¿Ø†½8£×Q‡Üž–žvÚÐ¥¹ÏAñè8+Òì¶dåº7{*¼ÿÜþóúo¼1w/Ê0™æxW—<øÝ® zaÒZûô2)éC£a”G:óÎÜì+ž‹Ef·}»©£î+í×Mf¤¥ÚÂàß¼ÅÁÞ²rñýê"ñÕ¿uùaõƇJ[–Þ}Ó}ÎOOs¿’…È„íîÛ°ÐÔ5 IŒº‚POé f ïÝY¥i¥s4]fì›ÉtÊ~¨q8 ô| Þý¿ºs߯§>ªM1ƒŒ„Ž«ïøû™­Û¶›©+Õ•ç Ú@6MðøèŒylJ×A}ïº~ÿñYÏNh>Rú𣹬!¿Û‰E=¸ÿ‰I*úPg&sðɧÝD~½háÓtM„1½G)ùL·Nm[ޏt¨vÎA²{§vÂao4|~"` äI8ÇÒWл‹X»i{FI™çšÃ‡œTòíâO¾¬ŒHÛXIç¸oß%‰õñîÇ ו½ýoî¿ÇîH™ :Õ‚éTõž 1[ì*)¿ö¨SNÝùciõ êCÌÍüR¯;aLFËÖ³»unÇ}› n ë6ý‘QZ¡_=èø!{¿[²è«[S¶ünׄPœÂƒû|LéC¼è¾ÁÉÇ©‰ÈFú)ñÈQõÖÆÝx¾­Ï.‰(§ÉäIøŒ¿é|ÛQõ‘6›cêMãœcÑ8"öDô‰ø'‹ã¾m€žhDã£Љ¹È3Eï½oôÞ1ªÇ}ã°·¦”x„èúH‡#õÁ›îË¡ñAýAýRã„ßízêëPÅìëÿä ÉÌ(Ë[&¸Î‚$y”ºõÒSdŠ£ÎðP}Òäü'‹p‘™Œ-ÃsÐÈú$25aÊ}[B oã#­[ÖfŠÞ/zϘNņ­u'zêƒ ª©¢&ù⛳΅$ùîÛš ‹¸u|8RÒò¯ºýë<ÿ÷åÈïö>,ìÎÚÿ M’–Q9Ò•îp8žìÞ¹­¸á‚“x6Ø€©mÁ„[fǶ¢uÛ¶³9äØ6ÈÇd–k›e\Òq߯Æ:g’¬ã£Î «Ÿ fŠÞ+z¿ºwn§˜NÕx ê*‚è=цÚå·TÔ¯¤Ó—Ú³ç€ö2»Oç¾¶µÊȤSmÚµŸ9hÐ ­+û'‘¼ ¿Ûµê©Ä$JúÈÁVÔ4_{y§O×3¯<óX­,ØÅŽávõÙÇiº]vÊíÈ!?š²ß¹ocïÊ„¤HÒñ‘¶Æ9Sz fŠÞ+z¿˜NÕa‡Wu¬è=Ñ}äÔPô‰24Ù¤à—~ܹçfá`Ggî[ Ñ€ÎJ§=õ$šÇ¨¥JÈïvöu¨¢“>4A …‡ég+»Ã6æ~Ý骰«=„áØ¢U«QÈ…¤5‰$25U”û¶&„ê9<ÉÆG=·¾VÅUa¦è½b:U+«$2Ç!Ñ}$Š ªRfˆ‡“„°mökÿ7îÛ(5€—9>Z´jIóX¢>ün7@ßFS¤Ùÿ E’‘QÖ®»küÑd£&àhà6¨óx½øžGxƒdš umÐ:ÖTx%ŽO¿äÊc7QD¦¦jPxRõm4nq’h|4¸ÌTåûÔ1èTc®¦:ŽD÷‰þ#n}ÏMV&)í¤³/DdîÛšz­þ©/0w:çÊh|$BàÃïvýugÌ%5$}H6ûj±r¤¦œE†ÜÉNrÌhÆ)Añ¶?Å,+×m6>½Ô¥ƒ¸åâ“E›Vcüîg+į~{ö–!\ýztמs¼èÔžT¨„xí£¯Å×?¯îÛ/‹SjŸ pćÁ¤jß©ó0äB_=òàgÚ¥¬/.?iú¶öH6Í”I2>¸U˜)zŸšN5Т­£9‰þ#Í2üêÓ¼Ù·tŽ#½c·îC¹o£í¹ú‰g޶:œŽÉ\\~^üâ1‡™ýO x¿Û@!ÉœÙÿ A’’QFÿô¦/î5äÇDÞþt¹¨ðxÅÄ¿/JJËÉ6±Á$ÓØyëÓïÄ{Ÿ/.v”4 §Ø¹§TÌ{ÿKñð³ï ÷m—‰GrÁ E«ôTµËnï‰ê“ú} +žDÙÕèLBÔà}[cM›Y„$us Ì”†÷‰Þ+àGþìꈀ1A÷w”—÷FV¤~/&(šš‘4‘Ê4%GJj7îÛh`«¿8&*ÙeÌcÔO4Q¿ÅãC$ün×_WÖª¤†¤õ½½U@~b¥É®ø,uƒÖí÷âíâÀÞ]E7|Èc@¯.âÌã1êNª ¾øQœ|äâÔ£ŠýÚ´}»u·^r²Ø]R*·ª¦66HxÛ6-¤Íaï„ÂÓð3·­êã¤éÛ†è€MÛþo/^5žê¥oùc—ؼ}gõ€zôI‚ñQ­­uQþ1\ÉLÑûD¸Õ:7NX ƒîƒþ#€˜Öú¦O$á Ú˜ÚTú–ÔIàóz}Õ £É#Ñqè}Ãg®;£ê'ê¯x‘Fñn“(ñ uq”õuct E’KôéôvMÈViGƒN@‡è©ñ"%Å.N=êÀÀW·ÿµÇ <õ!Z¾ÏuhÛJtl×JoýsŸgÝž6©µD•HG9ÞD&š–!Škß’z Iö/…dß\ÈPEfÿw¡h•‘*®=÷„ëõËÚM¢w×"-•„„‰s·îo~ò­8ç„A¤S¥ 7}+|ºOd]vZÿú|H‚ñQŸÍ­mYÆFb?3…÷©¡éTm’¬éO¢ÿ¨_<™ ššK/$ýˆ97˜e›fˈµosf¼"ŽØ[\|êàšÊKx8-¾ÿ3ÿs±rýfcqÞ;³ƒ¸ä´Áb@˜ÃñßýºÎ ›C(®:û¸„×oëŽÝÃG‚¨Xœ1>üóõõ—Ùw!D±äl0ÜFß#U*Í•±öpiæüDþé©)¢ó~mÄ)ƒÇêˆJEsã–?…û_ÿÇÃÎ} „#·qËñ*æ»5·¸uïÔãìHO© wüµóŠmZ¶ƒúw°Ôb¨„ ²ÜÌýß§â«c¯;ÛZ‚rÏ9¶èCÜVcñØð6%Ý7¨»âŒcÄY'"^_øµ¸ÖkbùÊ F}vAÍ‚\Ë ÌVu-ÒÓÄî½þðª!Éñ„…$áj2ÉV"“è &¬oI?œÔdÌ~‰¥!^Ÿ“ÃGâ¯Ý{cI÷¸7_t’¸õâ¡qÏ7Ö p|ÄZÕ†ˆÃ(ܘP+ñjˆº4é2+é}Ò'ÂÓ”(R¹è߆ŸƒêÒÉ­[¤‰^]÷Œ¼Ä8'“–â0ÎÍ„ËóË~Ý;·Ë~þ½^§¿ýé·â[0çµsFßP?Ñú­®.aï6$ "wÄEbôðÓ מ{û3ƒ§ˆ¥ÂÄ?öâF’±×%\Y—bGûAßH0ÝÃúÓyŰcŠO¿ýU|·r½TåZ^á/³ÎèkêóD»Ḏ Aˆà'“ó+?C× õ"©ßù'.Ø[¼øÞRQøêB‹EÛV-Œzí.!õ¨ªnÏÞrqÔ5’׆ú<žD&Úæ&¤oÆŠºj ¯aAsã'†¬Ëç+V‹w¯0^ì›iº´Ûmâñˆ²r˜þÂ{uk⼓@­æþ«óÿ.øJ|ûË:1î¦s±RÏ —ý,ˆ¸ÐJó;ÅÞýºëˆv8àyþɇ‹£ö1Êþ4éü݇O¿])n¿rXµz½øÞÆî…½òá2crºî¼ŒÙ'_ÿbHÞÇŽF$C±£A„‘܆Í;Ä|$ýé·bCÞŽ…B.û‰Q—ŸZ­ŒØ=t|Ä^ÝúOáÃÉ£Wý×¢©—è§ÿñb‚¢AËd”èjÐÆx.‚ÖƒFÌûàK±RÁÎ8ì}ÂaýðN$>üò'±ôÁyë…éßÿ=;ª|$ý=ÊPÇzîÏîãøÕgo¨ù‘Ðæ³å+ÅIG(^zÿ ƒV`ÇêÒÓRŒ4!’Û…E©SüøÛFcrè‚-7«£‰â‹Öˆ«Ï9Θ,(¾¹Qáñ€ .K¿ÿÍ >4ÁÑdH =¹ÿ}üµÁ O¹ó l»e~#.=ŸÖõ_ŽºV½>ÒÆ0 ‹;3U h,eTÒ+ÞõQuky´ Š›kÝ"]Ë}`.; §xùƒ¯™%ftÓö¿ÄO¿e3² ªT [ž}{‰HOsˆ»¯9KtëØÎP¥ ˆD#~FšÞý ÌP/б¾!ëJÒC¢%~zóhÑá!ã‘0 Ån7è ÕÉ*i\†öëŸý‚Ï"þعóáIÆÁöE߬߯.7]p²¸àä#Ä«XðÓHŽhíÆ¦BuñÐÈ` èŒ¹Aý{ˆ`øŽ8 —Awû`aP gí¯Z$¯’ÄšWBßmb^iþØP9ÇP-ÊÑŸ_BÂü}Ä[¬‡!^P4™ä*5z .Yì¢EH(G}{ð?dÿî˜Û¼è¿}|L¤¹%ÜX¤2hL<ÿîqì!û‹‹N9ÒÑb.ÔªN±ú5}ˆ+AˆµÁ!â›6DPýySB/õú͈íØö © 9bxì6›8û·R¼¿ôAºV¤F+ñžì€ƒ¦#&ë÷¢íß_»KÌ †¼RŸ›8Óµ¾œYf\Ë#ŒÁ¥ƒ—ÄP;"í[·GØ × úøŠ!%¦•}H}É‘Ž24ǧj •š•ë· _w‰=°vrÜ¡û‹_Ö’žŸ«¡FÛg«7l5ÆÅe§mH‘oÀB'r?³nº²r/&ÆsŰc“ù“Ù7¿¬5¤Û£.?Mtí°oëÌLg^/z„8“ Z¤ý 3…ä~]·ÅÐ+#êþ=;c­0’™.׆q¨z³0Ç0]“v&¼ñõ\€‰u}k–I׸9’;æ ã Ë ~= Fé—ß7Râý±È¦9¹o ™mÝ24&Ó°´ôþ'~€qÞâ8赒ĶáÉUx|†Î1éC“1”£¹kÖË !þqö3$Õ¡âÌÎähš‡ƒN.‡ÔÑ<HæQ»C˜vÔêÓÍH^^á_´ÿ°zƒ!Íî¼_+HK;‹.ÚT‘ZM¤s"DŸÔ_P›AFÝ6šGÛ·iaÐÝPjŒ¡êägöUw­Íüèš°w»U°”µOÝL̾8ÿ‹Àï=ìZÝ–;ÅþÝ# àV¬Úˆ¾þPŒûç/ÛŸ; ©îA½3ůX‘ëa9ˆÒLö¬¬MׯG§*cüIçYè¨Ñ¶(M>‘I’ɵláßj#©¹}»* ”×¢o~4ÆÙ:æ/ÎY7‰ìLlèÊ.q40¾ñS«ùLÌ»K–‹-X|›[æ$Í#Gjÿ…$ö:,†¿ƒd÷ìrÑ»ýkåâœvÃLGŒçÆ­™Æ¢ß|x ª\¤Fn8háiGdlù?>þ:cQÿʇ_a.zGL½ûJƒ!6ÓÑŽÛ¯ë6im%¦=󎨀Eb’épß1Õ6’\“:íÆ‘ê!1ò$%.[öÓZ3;A‡M׳‹_AÏ-q¸Úüxµ/I]Âßmó< YË2Ý~ÀÞú½ó0ŸÞ ;%¥dÍ5¼ƒÞ.T[ˆÏ?ј#Ø!v¦þù[‹¾ó—¬0Ô$vI'Á ^ÂÍ-‘Æ"©×#‰·é`ÚRrÿø6ýp­×ATÍ´¨±eIÌî8è¡FDƒ¶¬Ž^ôsN8ÔøíÅjkî‹°ÚÿÛçeæˆNÓ/¹\üˆrµKR‘9z_´Ý×·YÁó;Òñm‰²•™a¡®BbüÞg?AbBèÉ£ýݯëqj¹µ¡WFÖMÈmÆÄGÌ4¹-ØY8d¿Ä…žiÅìÈT²×?^†y ôŠO Žxg‹ûlX͘…ˆ$é¡C!¡ŠŸkºã#~YsŠ^¤¶3yî›"û–  rkIÑÞÇó„y´e6x ›ti!ýdÚ‰ºïÆs¡kZ©ßKÈè\í„Ѷ;í`^Wi©'-Õ?猹æLCBH€²X@ν`Ó‘©Ò# &g2ãfYd"‰.é“õ’ø™Žt…‰Öƒn:‘”™å#ì ÚåØU+†T³ æE¿Õæ@’ “•Œpó\ð¼i柀kú/~ï¶µ½Ÿ¯XeÌ =:ïëkx¨ûîP»ùù÷MP•𘇊Cê5Y[¡>¥ÝZè#!Y¢]Îp0ÜÜi,š‚ªpóU¨º6F¿„m1ÔŒ öZÔÂ’„¾5½ì¤74â’¡Æöú£Ï¿_+ë –"â¶>p¯2 'w˜q(nŪ Ö"mAÒÁÒÙ#ë«!1&G_Q¤EIFhÁC/=Æ$=»5E[Å@ê2ÑC‡#Vn=’ô˜¶"é°©Þ|„/4ÒîA4R‡Ž"é¼SM•ž@e£¸¡²úa»ö¤#€êF›*“eÉk¥^ú®6«ç4IC"N˜×3Æ¡Š«/Ü©ë/T]jô#‰¡UÝ® àv•ì…$6ú¼¿ôû*yPæÈ{ã#VË Õ>“‰¥Å>©$¼I4©[ªÅkŠª¤µ>sLöþéGR_RÕ"}g:¨GÊ—ýb0ăƒ>¨OqÑÐ#?:”GzÓ¤ð#Ê,>51â¤^ÑRmÓZö¶îé I˜Iâlê(›qÂ]Iqmñ6Cšiªr„‹[Íߘ«Î}eÉ7acÌ ³Ÿ¤2ót¹ŸÇÎ$ÍC¤+NöhÝÇb¨jÌyõc±úÊ4h—‚v¢uÔ/t†çløÌ¾&Æz@¯®Xxýndnn‰u,Zëj޵†Çá>a}g­[UQ©5¤aî+bVs‰j;:¹L¶pa3QÅÄ+_+©—AVYñ„÷-mM צ£ƒ#äGzz/C²CŒ1̦OŽÓ’ˆÐWɲÉÿÝsµØÒ’À”UT$/¤ŽA_c$i39ÚÖºóªÓÅSo| Ó¯R©9 _iŒÆÑÉo:uüò_„µk.²;JRs:,A“M¦dñ‚Ú'×Pã#NÕOh6õ†M$«ÔÂÿ}ü! "]xÒ_«OÂ>ªÕŠ 0gqò¨© :höŸùK19ÿa|X‰ì¶Òûî<ågÐëødYO¹,ý~ âîÓûtºDœyü¡†M~²¼sÊàÕtЇ4Ò\:e:Ú Ïºl¨¡ªEvrÉÓqð¨KÍ(¯» K4‹ÎZ¤¥¤à€`1‡™Éž¯éÈr`u@b܉’´™ÎÜÐ{ôù÷ A©ž…ö\5ÂËqFƒvÆ}á}#ŒTÊh—Œ<×äNÝ¥±5æáçñ ‰f;s¼™×¸Tç¯]{Eþ“oRŸ ÅáCÂ’X©|Þq娙ø*„oa˜£Z‹ÛПm°‹Æ} f˜ø:Ägut xÒ™Ž4·Ôv,†šciœÔÍÕ?}H6F¹nø5`j:¡i g}vA¨-?2£fšR3ërtÊéGÛžtŠœ¶ MwåYÇVnJƒá%ÿ`5 2ýF?«#óod“"@D¼LÊTL·žN'Óƒô#gê:;ç&#²ù諟Äd0@¤F’˜‚W>Kð5È82ÊFYü¯a «¤ƒ~ÇÕ€7¡WHŒo~º±#B&oÇäIúøÄÈÒDL“IÉŠË~8(Eδ¸pÆùrìbÅ…cék>T %Ë.4¶¯…u–=Б¤ rÄ‘i0²Æ°º«ôa‹œ[.4šó¿üÛ/ Ù|bDÏÂâœétÀ)Øù°¥NîX‹ú=Ó¹—I£/5¤Âôl2¹Á4‚‚‡T‰†Ðüìˆq3i‹5Œ¤3'Þhè{tž¸þüð!•¾3üÉ7¿ u²rA‹ôÑW 3“äš¾#`ÒA+M£¼éP4ýLGÌ" J!­ŽÆšƒ™®1]£Q¿ ‡•÷Ë!ýºA'ýRcWBÈdŽÏtáÆNWƒÁ "ò?ñðƯ¦¹%ÜX¤M˜àºÞ6|ep¡æØ@`#¹©þÖ6’Šs5X [“V&ÙLOúU¡tŠÍðHW’ü˜“C¤xñ Ó!Eþ[n´ýF×âSëÄ´³kz„³@f—H÷u5˜bÒ9¤íÔp'ÌÃY\0Ñ WYx!³O—Ãô"éªÒá0’FsÞÌ›¯û Æ&˜I&“idÛø?°x@ª ´›Êƒl2É¡Â#ùm Å$GJc†Ñ™ JKßøªe«7n1lôÒWá¬4ÚEL³ÕÏÌ#Ò•â7U&9R»ëFýie’ë’WpÚhæ–ڎẕ́Áõlˆg6ê\&#P H³§ÊéK„ìÜmè"’D‚¤Žìšá¬Îü5ço|dgÑ7+ Ý|b´B¹H(~¸2èÃ䬧Ùé9Ò øpŒ¥cWÒ%Ǥë{>$´Éæh7á|äbéŠ5ØÝøÅPá =å3¡3Ë®i!ÀsKäþdF92>Ê$ga’¢»¦@¤ƒÄ¤^tÔÀÞPÉX« _BO¹ºŽzMÁpen!92ifZv¡çH'à)œ]ôÐÇ'è—Ìn ÌÃÑ]ÓG€ç–ð}̪᱉:„¾æFúƒ¦©”¨rDF€`bD€¬¥ÚÎ<¦ò«l$ý >aNvk#Y\ˆT,1ÇÄDÓÇÈ6.©ùi±ºœ€T‡1Œ#¬4Y‰òk}mØ$àI¯¦3.œ2øØ“ìU_Ù/šhè$hMn#>Gù&¬*Њ,”lMé9œ`h s‡ÿU‚Ý%¥øj[ºAwÈÆ7¹`+.5Y\W&I”éÓÄ/ÌÿL<0ûUãëô©tÒ‡w>VÕÿgï;À㨮¶Ïª÷j[î½÷Þ0¶\p£™b !@:$„ø(_ò'IHB |IHHHB66.l˽ʒ-Ër‘åÞmY½nùß÷®îz%«kWš•æ<ÏìÌN¹sç½3gÞ{æÜsj:·¹ÞDÀDÀDÀH´Z¢L9åŒ gÈŒ¬3ò²¢GHœºsóØåS¤=ÂñÔ‡(sŠE…át.›¿&&&õE€ñsÝG×5€81Ò@ÕA^UG˜×q¡¶s°Þ£l‚s…Àº¬Õ<¾¾Wkîg"`"`"à;´j¢€˜ÆšèöG¨ŽÎ]‰X–‰ÈàF„ÐFkœ™×/’!Ä8P†,C’Ÿ3·îÉ”{æLD*ÏNòֲ͒Ož ÌL8·Ï«R?êæÞ”z©JÓTúcÆÄÔápj‹eÊÁ6«·íCjÒlék ëÀÌ9ÞUªkmÎML|ª$Y_]2ÜE\÷u Y®)9BMçoHÙæ¾&&&FG Mù(sà“4œ)¥Ä!µñmÓÇÊcߘ 7 ¬È©j=ƒêóÓã¨=ä›·Lyn§\*hõágÇoÞ.SnSjK9\»Ð0\£)Càšç¤¬¤4ÇzÊc5öÁ‚¨§lV«ÓÂà¬ÿ5¸ûàe™U60ðaŒfQVP–—–žÉ/.µÐ=Á“²uïa•­[B<Ò¾^éICàþ‰òÞH;QN9xŠ/9#KFÃEFÙŸ**ʯ5¹É”•—ƒèž–miYÈ:6 â‡v»JEÍÃkr“á¶Ï’’AþÝ# [´3W #Pjs•a†³¾ØB>H¦"9ÈÆ”CòÊ{«àÃÞWfâ+ÂøÉg!eÃîÃ’–yZE`šØO×ì’‹÷S³sǰ]LkÌÌ^ KH©Î]Hm0¼†Ÿ'oè)¯UØàk=EýoðªšÕ30hC‘(;Î<¾–8 CSÄj·ÜfË>”óîŠ-ŠÜxýH¸RKv®Óý"~ÊL¼eo¦óTö‹Ž'wìÌEE Ja±d¦ª¨ˆ0 äóSD‹–媲vG†"5+7ïQþú!‹>ÏÖË´¬Ü¦ •(ÿdúLÃÂééX¥Ä‘xž8t`/êÌ+ÔSÕK0üf†¢?ù¡ãçT]wÁº<‰è’á.u¹ÉÜJ7™A=äÖi£Åf³ÃÃY^Mn2,ûàñóÊE‡i«Ù¶E%eÊ \Ÿ´¾´"3ƒÞM¸ÿh9Ü«‹J Lk1SkëóïË<©¬Ý ñ‘ø‘ ÛE«4·úÚjJ9\“»>Μ{ ýì8ø^ÖSÔÿ¸”Ê´_›Y}ßEÀH®DQ½„Ö/_œÞoĨË;Ó³â& í]ƒ·nÐsàñü¿–‚ˆ„ ‰ïCwÎPa•xä°¾]éùõß—HXHÜŠ(ü´­eÚ˜@·M~ú§wåþ›®“i°8ÿgé&ùÉßQi]g"®äëÝ•o1?Çÿêo‹I&!×Y¯jŠej‘_tÄÿþlƒâ3; r"2n‘ˆy2V)ptØm¶¼}»¶A…í˜\/{×øÈ#—Ðý"™Ë$¹{vRnúêã&CK2%"Ü™¬–fJmn2ƒùdÝ® ¡¯ô†ÝebELîÚÒúÆU¸ùtß;…îA°wIˆUÿù,ååV5ÐôЉspï¹ ô»Ö„ZjJ9Ì:™âuÜŸ;Ÿ§ ³ææâùŠjŠžòz­}äÔSlêTYcí#µ7«i"`"ÐZ0QÖŠ‘sÛÅ3§?Üg±üð0ˆÔ5Tn›1F8Õ$$,"Ú­Å GaZX-ŒSÊüçÅp[ÐÅž{øea&‘u—qȎʼnr×ì °Z‰ÓûÔËôé(¿T?‹_¥c<«”øíËn2UáÿÚÜd¸}íΠ鋨'×õhõ€ë=é*3 ´íéGTŠb†2d¸7í£ÌsÕ&UÝ…jÛ×ÜÖ(\ú G«g‰ÏŸ/oë©FÕÖGÒzŠúžzÕöiýä#°›Õ40¨'Õ3·zìáÝ´JÒš“s©`òŽ—Ï\ÌAƒMN³œ‡OØÚ‹#nÄ/ycÒ»EEȪ"RV1ñ%Oœ5æXôaÇbXß®p›‘~Ý®ýÚ@7ú}ÓMféúT¡›L}„åj7™Ç^ú@ ä`K-L^³ûÀqX¨WȳÿøLåÉJòiýÔx$Ì#MLŒˆ€‘\/ˆ-6T’| •nY½|[T\ìÛX~@>Çý7O±ÐbkJíÐB×ϮŒ£–Ì}{–¥nÙ†#BDOÄ—8ûŒE¹jŠßïÝžˆê_•Ÿ|}¶ëOßn R7@W÷ÔÁ5¹Éì9tåýò[DH¡ß1]vþþI’lN=,<_Í®2–Jåóœ/?öuÎ\òÌC·¹–³û g*ßkFN  k‰ss])‡«srl.xJú ªç‰ÏWlûŸãÿ-¦žª?Ìîzê䑃oQßW`êsú©þWmîi"`"àk‘(ÓbCEY‚©xÕï|:{á×é|ÏÙËWH'íןeß&„¾~ü L ×á}©Ÿ'}öÉ\¸ÂsZ•¹L|‰³ÏeÔµÁR—›LMÒM¦:a¼mÆA¦ßñÉóÙˆŠrIn¼~D¥]ë&S©ü¡›ŽNtSu[mÿ«sªms[ƒàóRI?á?Ÿ©ÐuŸ}¼Æ!vin4õP¨CÜõÔñCï¯^ôÞ§8¤Mé§: 27›˜£e~Þ䋈!¨4 1…@‰.}}âûä©ßzñíUÑCûvu`𜅑+.«­ ã2´GsàžÍZ^°3é«Ó¶oÙl4Žü¤I<ùŸøgÓ¥ ÔG8°ó¢O,G”’˹ùH:ƒôç,Êo¦´ªÕO¸z*¡@tJ×]>wöâ¸ÄYwBOE™zªò}QUOÁ·;?eˆïÞ˜´{¶ZýÄŽµÔKDÜ—+#Ôúþ¹Gãá²ûÿÖwµÕ_‘ÙþN\|¹ýJ”µE™Ê“/¡ (Ó’weNœ3¶ÍfM!ŒðŽÈÐ`GLt¸%$0°ÍÅÇ*)+C|çbGAq •¯"ÈYû7m[½bsII[9ÃüŠ9—Ý-Ê&Q õ•9“† 'SÚ,š(_£Ÿ€u©;§‡÷¤fMœ=ÿ:tX§@OE¶y=U^î`C&gQzÊfÍ?v0#iÛ+VÃÇ;¸µ*ý¤‰Ñq|qJGr£ìÜBÉAf×2¸k¹®k¡?KodFꉃËZ85uQIž˜À—‰Sm­f¶:hWÛ#GÄ ^U"0?Dóµö7Q&ÂÚþ´€Òí‚ßÂý Teíâ>Çòš~ÃFöêÑoÀ ÐˆÈØàÐШ€€À6cZ¶–G…ZËã| Z®”–¥§dIÊLßsØèOô“çVL ÆK<‰«Où'£¾¦˜õSEíè¤Ú’–~¼6i©¬ï;dD·ýô‹ˆŠ  l =UêÞKƒl+<ª—›knµ"×^qq^qAþ•ã‡fNKeZ~"A"^»ó€$íÎPééé ‰q‘!Œ8ûmJ*:¹G~ ƒŒóaÐY¾i¯DG„Ê4„bM3 ¶\¤É×±1Û¿J ¢ý+µýfgÛOÙù"¢í•‹¡&ÍUŽ6Ô_#>¹|¼4ÙcT’䊾©‹D—CÙÄ”…mÊ’ƒ9÷Õûa±uËýíÛÝÕ1(p /8R,ý’/g/Å"-Èt« Õ˜¤˜–圊e¾œ´r…ÃSLLê‹@½ô ÁˆÎëLG±Übú©Ý?{A_Ü¥å/¿®—›iN¼Ø¹ÐŒV©Ÿ8~_T’*ŽcУ£ÌÃ×§=Þ³™Ú§ÙNÃÁÉŽ—”ƒ'eé†TÙˆðžwÝ0N†"=Çd0ùª˜í_{˹·=;KÇÌ12_¯æÚKi¹­F$ÊDC+UZ?5ùuAq}(&刹²8c®Ÿ4} VµNùäröÛtîØ9Ìϯ.:èkñq_{ñÜùgKl6’em­¡E™–dfÓš L1ð>¥Ÿü‚¨*]B=ÐB}MÑ$Y=¡în5ú‰VD’¤¯v¤Ë’u)È%Ý0Uzui§.Þü¹Š:ÐMMGO_’%ë÷È?o›¯.3“ƒ¨}ÁºxõŠœþæfû»#RýrumÿæÒÍ2oò™Ž°¦AAA†n£e¢MKëE[%øŸŠ–SjeµÁœ×¡Ér«'ɸV)„r^ŸŸÿêÝqqÏX,ñÒó±Îç=òôß±™/"¾I”µ¯2q#~ÄÕ¦!à3úÉ?4ÊýJ¯¸ÿñò²ÖÛ$Ét÷ÒÖäV¡Ÿ\$y{º,^·[†#¶ûÂYcÄ aZ÷]ÅŽÄà eÑWɰ.ïQ/ø™ãúè;®fû×ÝÎÕíáÞö+6ïC(T›Ì@Û;‚ ÛþF&ÊZÉR¹º/“ð‘ü‘$«~˜WG”[3a&²57ÿü¬¨¨7‡†þÐW WŒ;ïÒåó?ž>½›ù2b‡‚xÑÝBû%«cñ߯#ஓܗ §ŸüÃcݯò¢û/-k£qÑD™zˆøP/ù¼~¢%1íðIY’”¢Hò½óÆ{ ÎÖY,; ³•"ŸoÜ+íc"dx¿îŠ,ù#¾½ÑÅlÿÆ·{Û¯Þ~@â1Øo8"KQØY2Zû™(3*Z*Yw…«­Êä„I“díËL‚ÜšI2.O 1q¼pþâ_õî5"Âßr¿Åâws\ô/7•”$n½|™Ödbʼnš–d€`Љ€ð ýäYÉ àœ¯¿¶¢”~Â#w²LDÒì³ú‰$©¤¤\>\½C¹[Ð’lJã v®äË'kwKŸ.q5TEÄ0²Ï²ÙþkëªGé¶ÿ|S2ÒÆ‰%ÚBQŒÔþF'ÊW’<*\Îi¥’u'Èš$·e\?ñÀ(ë2û–ü‚‡ïŒÝ«r¤¿Xúþµ[§‡G]¾ü 6k‚¬öåþ¦˜˜xCë'ÿð÷ ¾ìþÇ‹ËZçhl¨‹ôD®—õv/VųEó“»Íf—µ»ö«DôI6Ý-1±[0m„¼þéIJ> ³'qù*“0MÌö÷\‹¸·ý¦”Ã2sÂ`ñ« ÉF è+D™-CÅ«‰Ÿöw#1椭Èú©Òsljµ¢_DŽNŸÎ[Ø.ö¡~~¯*0,~eŽþ^ß]{÷µÚ«7/ÌDÀXV?ù‡»#ÅÁ½Í!.ý„“q™I±žô:¬ò-¡5ÑjµÊÆÔC*º…9p¯éíG )dÛ¾,„Žë‡]ôU¶î<¯Ôlÿ¦··{ ºíw>-‡öTƒúŒÖþ¾D”5¶ZÁRáR4IvþknúZ‰…’Ïo»ó/÷-[r7z»×aEØýþŽå)蕹ö©ØÕœ™˜xÃé'¿@çp }ƒ›KÜuÆ¥¹Îí•óhkâÑÓ%¯ DæOîæ•ó´ÅBãàñsr ØöëÙY‘d£EÁ0Ûß;w¦n{>WÅ2Î8QP´«‚w®¾yJ¥ÖV Îõ'½¶0w]÷3ÏÏo.Û, gŽ•‘ý»ÖX¤ûež¼‚Þ±Ú}~ÿÖr)³I8#ÃBdö[0u$>g7Ý&– "ßµC¬„ %µ7„XF ‹á•üB•á0$ئÜŒ4¨«±íßT¼t»²f6ìÔ>ZæN,Ú9Çœ¾#¯~¸V~|Ï éÒþꘄ?¾ý…J}Ó”aª åV›,Eüêø‡˜'&*LFõï&s' ‘J÷(Ú¢k‡¹yêpIˆ«^²©—Ríñºí™É¯¤´Ôpíßô§§ÚË6W¶!ð{PäÓJo ^ 9^»ó ,˽%*zÍÎ å_Ý-!VnŸ1Jâ£#`Å+—W>X#·£¼þ(—òþ;a•FŠÁ°vo[ˆUÞX²Q¥›&É¢urÙ†½²/ë4®Å_®ÑW Ä㱋ץÂê%WòŠdû¾còÍ›'IÏNW ÷©Nˆi:!,I˽ÆÜÑ/t]X¯Æ´u×ÛuøBÐ1>ZMüRðç÷¿’åHØñÐíSëUÌñ³—eÿѳrÚœ÷%ewqÞ£Îsã>[¶1M¹…à+ˆ·…m_\h¶e†kÓ¢ìíÖoò{íÜý¢îä©ñp#ƒ¡ VfSLLLLš‚€þôNÒÔTÖ·‹ØàÊA2L!©,ƒß(­–‹×§J7¸9|÷Ö)2¸W'‰ W¤¦g—x‰Š •»g•É#úÔZ…”wŸäI®(ÇÏeË«wJNí@–FK1Èñ›K·¨óñzxþr+ÝÊ’ËnAq©<ÄçyʼÉCÕ¹¹¼=ý˜²NÞ5kœÜ€°^+6§)—nã±´F<~^fM(àjR_a]HF9o#‰'Û¿)×Eך¡¸N_¨œh“x§:éšJË8TË)§/:ï³þÝœ!½¾¦ùIÜ;6\M\Ÿ–yFáòA)…ú‰û§5Øò”A”ì b¹‘,ʪé=Ðþ ¤&üD„£ce«44i×ÁJ‰:J`ÕrI]¢`•¦_rMRˆŽÑø’pòB¶\Ê)”Yã©0}5íïéõ|ªìª“d¬ö7‰²§[Ú åõÝ•º9sÌÈ%Ð6 pó:[ŸAÕî0HõÌj˜˜˜øš¸5&ƒ ÃÅ€y-qùƼ òéºÝòòû_ÊÈ~Ýä6|.¯¯«Éì>e%,¼ý{$HhˆsÀaü‹;V þây;W,sPWuD¹6ÿ`Z³Î\RVê=ˆ«åbÂ:…®õ­³>FÍ.ɲƺҶþ£ëä‰öoê¥pà[Hp W^u‹xðŽ©× æÓ牀Mq=¢ªø¡ctý¨þª,‰Í)Ä—V{£µ¿I”›ó.hæsùù[žqX·’(ãÔ·7rd¯©©Í\ ót&&&­¾Ì=!»öWý4au/“þÃ?¾{¦ò/þ~ÉÑ;C•Ûƒû>5-wA´‚‰C{)‹î—ÛÒex¿.Ê’Ø.:\öV"µùª( ÃB–KË9¸„ÄÀÍ£:¡;F¦IÃ{×X/úT7Fˆ®&¤9ÞÛÇxªý›ROZÜSŸ’^õðûÖç¡ ýʽ¤o×özu¥y8¬Ôwµp:vÝöFÀYƒcú(k$Zá¼÷Ž”=°%ÂK#Y¶Û϶ÂË4/ÉDÀDÀDÀðÐ {~¢Ž’ßM{2eæ¸Aʽòüœžzè”ð3ø ø'ÇÀâ\a5ì¡|‰s`.Â`«Úd |˜ÃB‚e9dQh]f´ŽM{Ž(Lf§»@IDAT_â »«swï¯B¾uj b~–îd̬d}l‡tÈ:ƒ8·8/I ­Öx¡Æxmç.ç¹|”k«—¹­qä•"ÊÅuoüí“õ’‹òüëœaßêS"ðµÇýóñWɈ~VèÃN¿õcø2`Jí˜åÚññù­~ŽgЉ¼DÙÓÍGÆÛgGÊ.Ÿ¿0óLLLš¦Z¸ø©üψ,Aßäöqrÿ“\ÑÜ/ÃŽØ_kw¿Ü)ˆ&ÑÑ+&ÁBL ²³#ý¸üî?ˆzÿãoß2ÙýÐJËô-¾ñsß^±MÆé©¢Y0,ýX—mØ£~}îW\äÄ1dõ¶ýòü›+2¬Š¯« ƒû£s0ÊÅgˆÃËPvŒÍûñšÝòÆg›q¦Àýó'ª(ú8OÌ›Š»'êÀ2Zº õ·ûÀq5°³[BŒ|mθaÍŽ‡~Šˆ$ï¬ÜŽðkváׄécJO´·Q¥¥q'.­(Ó­À9úÁÙÒîËÎ5­÷—_©´pÙý¿Zßcçžô#£G|„?÷¨6Ç“˜/TË>üÇÇýr_öá˪WÕݶpÙý½ 0wjNZ^?ÁÚç&ûîV@ÝõQµú©ebתz§¾•b´N5 Ÿá¹ÝµùÑ{g©Ïä~XO«…$ûGw%*«.‰iUyêþ9•V …Õ×½\Z™91\Õã™è„3¶Uç[|ë´¸†!(ߢ\Fx¢šnÌ(Hë5ª«äþ':ù«q6š^×õjäe5ú°ªíZµ ºÛ¸·³Þþø}•ï~`çŠ×Á6sO˜S×=ªËôöœžMg£´¿/eõâùÎÏŒµøYæà™í!~–N‡Ô?þŒ·[º™Ê‡Ë{ØgA——–~ùÖ+¿KÆ©ùVääz19üŸ{¹“(ÃWùø¸q½{ìÜ™ÕLÕôÈiôƒCG29‘ÚL#›WˆÏ‰EÊïÚn‚GNm¬B*^Dœásl FÁÇÁ÷pHï.ÒV“8·xs)ý„ZÝøÝÿÓÿ|ƒÖS~b‰hîælrrÚÏŸ]çúÓL ‹äk=å°;¾ø×žá-—~j¦jøÜijóñÕô{QUI²{9Õ‘d½=¨šØÑ$òôm5Åwà{Â$ûNÍ[¦¦¾D”-ßÿþ3¡¶8Ë#þ?YêˆÆv M§áN,!W»Ý-ƒe³Ÿµ¤¼Ü?#bVúùøÿúÁ_>w®¤¸äÿŽïÚ÷—¤¤%E¨ƒ(ÚûîÚµ/sôˆUXž Âé_n³þ Ë`2¼ ;cWÚ‘±§\’vM©‡…Ÿ1i½ˆ„…%}jSȆTôÂY.2'‚Y~q‰|¾qDcàMâèA2cÜ@E¢© Mi66ubâ‚°c‡>ò#ãé©N×7"'r×SxžŸ¥ž²–Û^ñÏv¼ö<ÃÐwus×Ì<Ÿ‰€‰€‰@Ýø Qöûö“¿šãøO‡ÝÞypï.ŽqCz ¶[ð¹§9?%Öhóî¡:E%¥ˆ_yZv¦g%ìË<õÜ€)cî4´Ïßÿ¿W£:Æl³øûýÉa³ÏeõпøöÉI“žî¶ukýó_òÀfM9;ÙŸ>Y“Œ´šÅÊ7Ÿ‰öDè#ä¤oëRŒ”Ÿ Ä5Ÿ­ß-ЙøÚ܉HßµRLÍ¶Ž“¯_d”øµ=6;*&ö¯v¤7õ” ñêôÔïýü~ ½þÝ7_xö ìYÉ?Äu¤¹`"`"`"ÐÂ(++ÍwþëŸZ,~èÔ>Úq÷ì Ò¯{GÓTævãÐ7lÂÐÞœ,‡OœCÀðíð¢þä¾GŸúÕÛ/ýþUìZÖggJÒá1#Rà¢2 V°Ò²¢býsnÅfQ[‘™¡‡é,×%•[ÒU¬Ï{f«5ؾa.¢+ÂΨÝÔtôô%Y‚Á6ûx,@:ÚYã‡(²lZ—½Ò ÚŠL=„çíÇ!¡áÏvîcê©jà®FO% ³ØçÐïOüó·O¿‚C´ËX5G›«LLLZÕÓo™S×yVõúÎÿ>ó3ä?ÔËòÄ7ùƒ$×y`[Þø<ùÍ›ü‰^ÚÏÝû“ÇéfÁPeyIãİ: ×QÒ$¹JJJKå«éHºO†õé"?¼3Ñ$ÉuÜÜÌØõðÂD…×âµ»å«íéÎî!aŠ'Ð$™#ªBøœñy3õTý v×SÔïÔó8’ï#ÓR?ͽLLš #e¿þççsýüü_ÀËÇñÝÛ§Y‚Û²—EýïâD¼ˆ[XDÔÓ ¾õà|òÌÉ3KÀ—ΰ$ÒNY¹·Ö¿ÔæÙ“îš$ï9x\Vo? ÃáBpï¼ñ®‘ÖÍSß= ÛŸx·%I)’vø¤"˾{E†¬9u§"É|¾øœ™zªaíä®§¨ç©ïQ‚‘ßI »@soV€Q•’ß¼y÷†‡„„½Ñ5!ÖqÿÍSL+C#n7âÖ¹}ŒtèÜõåîÝûÇ-ºxÑ¿Ôaû·ËÐáx°Åzí’dº[”ÂÝ"7¯@>ß”w‹HYØÂ™‚¼vÁ^.˜¸%ÄEɇ«wHII¹I–=‡7õ&¿Æó¹âóeê©ÆƒK=Õ¥C¬ƒúžz%õ½Ô¨‹üý[_ÈÏÿºD.庎痳'_ûT#Y‡)­•[ö©v^ä.îòÖòm*éŒû:sÙ¸Q!‘ûwÒëˆO²¿iInÜ DÜ0¨‹‰F&ΟORº­°ô]ü·©ñǬcÆôi\éž=Š/Æã¤Or1¢8lJ9,yÈDtëÔ‘¦%¹‘P³ý öédñZ»k¿Â—8›Ò$”~B EÊçŠÏ—©§)ïÓ{æLð§¾§ÞGIütØêŒ#Ë6¦6$óHŸE€áóÖìÈD§òÙkhë7œ*DYk‚CBÒ§‹Ã¸×´[”¾€ˆ~àØ'Žo£¤|ïðá‹{Gû"Øb™^«ÅFýDÓÎÒô£Öd«²&".òn¤Reæ)úÜšÒxˆqܘzûc`˜.LGÔ©Ÿp<džGÇÇ}›Ï—©§š€(Õz*ýÈiå—0ñÃÃ[¶ ™ˆÌz›‘:úÀ±sˆÖsí8›ÓrdÙÆ½Hq#íb"dÜà2i¸Ó†±™Ôbúñ¶ež¸€4Ä‘r× cù:pâ¼ôFüô9“«¯G+9㸬ÙyPŠ»_÷¹}úHaÜe¦¯þ|Cš9}AV•Q£  ËFˆÍóW ä R3kàÝ7ŒG=hÜÙš–…œ™’ÔÖ½»¶GøÉÒ=!Nm;Ÿ§ŸFj娍0™;iˆJ»}!;_>Y·[Î"ew<ÊY8Jz Ã`[”þhƒsÀ‰ÒïªáëhMïG{lÝ›¥ÒoH>$þèTÎ8ãw8Èý’¼Ê´Ñý‘(¦›‚¶_d™=q¢D@Ï_7¢/¶÷SÛ='I)‡äì¥\á—æcH_´ç øâq#Ra¯CÆÆËÈK0ã$ŽT‰n˜½)¯Ÿ¼ˆw2Cöê,󮪲92ÕúòM{‘ê¬ Õ:u`¶ÆÚêà«ío4‹²²Ö,üÞ#ãaùJ@¸VgUh‰ʼn£¥Ýõso‡ó‡œ(+û^ŽÜßr,\Ø¢1Ö\Öd„+A¨»£§/JaI™ŠâÐxµ¶s2"F^A‰Â•V{ÓªÜèv·&‡8Ÿ'K;SO5ÏJGê}êlhUVå. &$3ËïœÏ`Ua*á¡ ®?¸sª A6½eÓ„a)´D®Üš.Ñá!ˆd3R.æÈKï})LHr"Ûœºpdö¨Ú7dö#¦¬¾ÙñÎ]ΕíûœÛÖƒX]†KÛ·o¹N¦é·¶hWù_ uutx°Ü ‚Ä/y_lÛ§¶ñ'&"Te|ðö©Ê5nÍö µ×ñïe[!'?¤—tˆ‹TÛ>Á@b&5ù΂)ê<+Ñf_ñndæÄùê#²Ö„qµBg(ýÈÕ6ÜïƒÕ;eçþ㊔F#áÔ2t~´lO?¦:cwÍ'7LŒðir9yÉð¢_¼>UºuˆUi¬÷ê¤Raó¸+yE²)Í'ë…¬}eÒdï?vVk8³þÝ7‚êœí9tR‚Vmû÷d*þOF¦G¦JgÚsJMuP}ôÇD9,2ò;ìñšÒtˆ#ñlߥk"J ýΉÓëá{qB}„w8Ú9rèÆ¦Ÿ¥ñ%8‰² ƒøÊ•ÛÅeæÊ`œdSšŽq$žÌdh³ÙL¢ÜxH5Qæ ¾P>O¦žj<˜UÔzŠúÛ I”ÑÞÔ¥U«^ç+Æ_̇%ŽDv#¬³U%ˆ©¥ã¢#dPÏNbÃþ™°âiaæ»›§ŽPd»O×v°úÉm ÉÄŒ¤‡ÆÊœX¤)Ö·³ÄÀ =Öë4, ÓaçÀ ‹Ähä€îÂôÖZ¢ÃCQ¿aÊ8q,ÙY3©eÊ羑 ê}`<¿jêìcȎʲH’XÞu¨<ê_#Çq$Bšâ$cõ€¥âvï]sôè'qøbÐ.J‚¸SRaýº*Ë4;CzwRëkªƒÚX>^g£´¿Ñ\/HÜýѪ=˜qÏÉD˜êø·o.öØÿð“{\7 ÛkñÚ]òÅÖ}xÀûÉ}7]W&ôÍ]¿424̞ؑGÈù‚‚Âb‡íý@‡ÿ“dP¸/¿A8Zê꜉E3e„„cÖ½dÜ3“‰x¦Eˆ#3Ò²@¢L¼ýøÍ”†"àÔONÿä@ú2Ùµš:è,Üb£î® UJˬˆN€»¸ãî¾¾¹—ÝëÑö'±¥Ü‚ñ6¯|ð•²ûûWV5a¬¯‘®”pt<(ú~`ÛSèîÀÎUÖ™Kª“²í¥…_ (ߘ7A>…;ÌËï)#ûu“ÛfŒiæc†A ÀZ:ÆEËI¸ÒPòð5cñÚ”yY¢Ð™b›‡¡-­V»—I׊zéc몃ޯº9ÛÞ®UÅ÷ªÛšë¿‰r€ÅÏÒ i©+?-M@„@Ûð`<ÎÞÖÕ^ô®ýÇ$¬ä¨‰·œ=Ð0Ò·2(9¿øãiÑOò1Ác|ãÑÄ‘1½’Rsšs£¥Òµâ¥@¢\ŠÏ…˜¢à3gŠç`šoú(gâmJ£ N¢ÎäÛ)Ø?0 Ÿ«F•dT-Ôû—sriš"Î{T{²&¬ A9q!·Á%LÕ_và½ÃOâ$[ò¨¥ðOî×­=\/¦ÁBX*ÏýkE¥²õ¾z%­ÃÕIppüCåÉæ\³™~Êw‚ÓrýñšÝòÞª®ý.U)ÄåР@e‘¦’¾«ß… E¿ndËžLù >°´®h’F"æîä¤ßÃ1ülß¡‹Xû('ùkÈqͽocÚ?œ&냎Ç>§wÅeÖ†±¾®€Š{EÿçX“ªÂNL¦IÃ{+W™ªÛÙ)úñÝ3eÂ…~ ò½3Tù”s¿‹i._‚A%!ÞIœ9¨ž>ÍOÜ?Wù%ÿí“õÜEüÔ {÷û†ëëª÷©IØö‘AÆ|äD”ÙòœÐ©°D„^{'Ô„pëÙ£ÔîŒc.¢Ì fÅ f½« Ûº7SVÀA½=©Áø\rï¼IŠLçäÊ¢/w*²&£ñ9é¦ëG"Fí)Y ®S²Ñ;ƒ£ûpuZ²?úrzfÙjÅdøþ$ޤjºeÏaDu8¤Ž¡å”D~þ”árÝÈþpܯþüu\b›‰§¿ŸväãðPVÖÑý£†ï‚Ž  ƒy²ëߨ³ ïÀ¶áDë‰ÕjUQ/ÊËín¤[ÓÃÝÅ[«Ëõ‚˜¡§ÞP4ö”.ý„xsñyò¤žjlÅZÓqÄ“ú×D©17TÏŽÏM¾Pò“7ýˆòå‹ã&èú·WlãÇ<%ì¼ò}C‚[Ë }‰+Cà&±~ª›@hÇ î)ôq½ "Ûþ£´ZÇF…«÷](w~B繇Çuï‹ç15ˆš×™“ï´³ntØ ?[%h‘îœG½’"M·œ‚"åjÁ2è*²fg|s‡K8ÒÒ9°Gí®tIJ ¤Dz¶WºÉ¨ú©)í?{Â@I=xB20¨s<Ú‡RÆj‡üп=åàIéNMOp›‹ ˆ{.2,T80ƒ÷èæyP‚ÑÒ²nyô‘ç}Hk2Ý0(êë.Ú3úJ¯¡k¥oײn=tïáàÐl ¤PMu [NM¢Û¾=êI|õTÓþͽÞhôõñ‡‰³B…xŽa Ê)¸AI(ÉY2z@eNÕgÉ…²øï²M2>9-œ!H¯*@h)«Ñ ¼˜“/ÜsƒÌWï(íQéÀzü! bä‰Õ[÷˯^_*Ï¿¹R‘2Jrö—EëäýLÕ›ï1-ˆµyï5H0–½DDD  DÄFºxéݯäÕÖÉøÈj¡E™Ñ78@íOﬖ/ݲT¤,÷á3ÿyX)Cú—¯†g{\׫¯:'–Ĥ3¢nÐ!Ðhd‰õÕujlû³S5í£yˬ cnoˆ0ê޾ñÙfùù_–-À$ât›¡ËÏóÿ^!ÏãkEüÍ'!‹vnÞül“0¾3¯÷e,à…à,¿úûR3àÛÜOW’ªœ.Ù$/¿÷•¤e:Ý=jªƒëÀjtÛÇG+·@£µ¿‘Ìv$qjÂMĹG…!ZèÇsèø9ô€:É.X—¸iŠ"ÀúDi͇÷è Є}’Œ;y˜òËÆ'‰Ë?~h}ˆš³—5~γ0ÊT >!ÊL„Þ)…Õzx_Œv^Ÿ¢zô´(p´ðàÞ]ðé"@ÝÔ%°ôQj;¿.·)s(!âªH2æ–-eÖOf‡¿€¡æ`ìS²&êÑ{KÊñ¦œ£¡ÇRa8'Ä‚.vXWØy0Å (¬ÙqbN¥oJ½ XzRÏPÅóTïÌë‡@…þ×Xsn• I›NøŒÎÐ\´Þ1ªLmòÔý׺A|n îBÿáipË`ÙŒ»ë.÷#z…»ÜßRw™=q°pÒ2$—ýŒy‚ÿ(·!2'¾oB*Ü#ô1¡Øï¡;¦bŒH¹:F¯çûêÁÛ®WþØ´SêN I?'ú«r;÷§0,åã÷ÍÆ¹ÊÕÿ7ë¥ZQͱ Aø³øè0…±Ð˜W³{³¯ÒuiHûÏsëŒè ?w k؃ätè7O÷—¹]…ˆ"Q•þÓ’ÿÀM“Ô»”ØÓŸõ¥³Y埮dá’oÌ¿N}ÎzðŽé°o%` >uõ–{çORŸ4è’±róôªó\!s¬6+âZvQŸê>þj'ʉC|ÂóÊJÍÖvþ«jò5™ÂùG—ù%þÏÃãb±”ÊX~±Égh`š¸¾ÁJo†/k |õÞ*Qc]ïƒÌÝÐú‰óÊlÆ}/sÙh¬=Q–ÇÊà XÆ!ç4¨k¼$gžCĉK‰÷®I¦§*ËìÕ‰;ªº]“ªë5I®º^ÿ׃Ìô=¯Aæ¾Äð X¢@¶øc£ :öfû×…±Æ´>srÖªRÕ×]o§ Ä´¦{£¦õ<¾¦z×T}N=w¶ýyéÅŽ'㵿ш2±£‚ôŠÐ¯øÝ•ô ³ÀjÜSÍÝO‚Ï"ôázîá;ÜW«eú»ïÆÉË2HÞ^¾Yþ¹x½Ú¾_?ÿö-²k–¼³b«Än C¬Ë±Ê?™Ÿ/`¾ ÔþÄŸ?Tå0ÄÎ|¾ ¿o°Ãe£|}(µ_íà™ýR8Ã~»7Á<ÌXìª6;Qæ¹5Ó=\®3Åó˜ø6ÓJÏO“K3 ¨ ¯½j:a]ëµE‘Ž/ôᇕqòn¤Êà §ûlQ*)ZJ˜¸‚†úIÿNѤð%Î󖪛ûyu]Z[û3œ`Ke?fÛ/NJÅÈh‹t AÛ²ýÛ”U™þ”+Ò©p¤w˜Yæü×ìØý¬HÒPŒX–N4ZˆÏ"3ô èÑ ¤ƒ£ÐÈ;÷UðôO¦ÛF0C§Àrœ‡ìr1¸9˜bõ¶«ÁITÖ! ³.…Ã||ÔU÷ÚÎï^ÏÆ/_ëÒRäðûÚHù~XÄ1ñè¸qצjü [Í‘ ‡c’ÌVÓœºkŸ'\m1ç/çÉ9Œ@o)Y¾i·ÁóóËÜ.è¾¶&Œ6ÀŒg|¡‡Áb;¶Oå‹»I>|UèÊÁh-%‹¾Ú¥¾¼LGÁ`õ%¾Ä¹ºè-UOž·µµ?ÉÿSßœ«â<·®ºí»FZœmN9”ÑÚßHe¯[a¿ãÌSàës-׌[GËRø/BÄ ÞD³'•¾Ýà?|ŽŽ¨øgò¶crÎ&+iéߟmP¡~zwé ‰£`9ƒõ†©ÍŒ Á´\½]•ÏP]ÝñÿÉoÞˆÁ€5Ÿß[7ñè””ì#cFl€=a&?»ÝºçzÝ[ç«Z®/Ïô¬3jäa½UjO} L;ûê‡kÕ_~6e§'m­c¤r ËHdçࣉ/àn0̼®Ÿôõþâ/Ÿ`,„3×QŸ¼öä70 *Eéœwm¶9ï޳h‰_±ˆpG»ŒÜËÛuh6Üës!úÓ;_ä!ÁÁȇpZ…²—ÙÊVŠ,DŠâ–²ÎÕ§þFÚ‡†&¥½Ö36Hº –o¢@WâÛÌ®4©»&»ŠKÊËù¿Òýg¶¿gî ÷¶ïæQ¡ʶ¯£ýáwígb`hÝVž©P¥‰(³ª¼)1yÎRCúú/¾É²•|ïöÄŠ%çì'_Ÿ]é?ûqb^ûP Ö`¯–òu„‰ãTu°ÃÓ.€ƒ¼áSè»vÕ@ã”2$›dÛ}ý›ŸmTÙ‹èrÁx“´`?ýúbÙ2F_Óù+U²i*0ÖXp‡åS|z«xû*÷‹f#ʼ¾„¯NM»8orà$‚ðǨ—!GúVµr|}Îx„ Ra•–à3ÃãÔä·åú5¤LsCŽ1÷u!pͳãÚâᆊœ>v *•:„ò­[¯w¾Ô¿Öü£ô¿ÆÚPêÔçþ¨^ìá:¬gÚ2íÈ)¹p%OLéŸeC]¸‡+C¿T~r¿€1<Ý¢ý¥oÇ(‰–Ä“„‰øj× ŸººâHº*IYIiNNná5DÙlÿJ05ên{ŽßJ±IטP´{h½Ú?'¯Ða³Z/ºøš¶sÛæ±E£e]XS ¢/quRÝ`Z|ª“ªAÂõ>¥HÕÌT¢ŒŸ™rð¸"Ó½ê.5ß}O-;–”•Êk¸ã,˜O™ÛcÓ&gjOÄGËáÈá $ª¹¤wWmW¾å7Ò]ÚÅF¨è%â£eÏ¡S²¦m5ÅD ±DCÿp°¯»|øÅv’ò+±çÐI$8$ר¾N Ç—‹…7Œ¯6{*RÍ~¹=]~õ½Ð5"HÌh>O|s>b÷†Á,CvìËR_µ’’ÈZ¸žqPó„cǽW—öîÕhÓËÚð"7ˆðp•$iHö‚QûÏÈëŸnv ˆ†ÑÆg¹5ƒËX¹ ÆŒá¿Ô>±~Ò–äÈÈ WxWâK“î$67.%E…ó‹K- éÊÈZÌö×H4l~MÛæØ-ÌŠä2!ªÍëÓþl ¶Iyi陆½é{WÏðš^®YB ,˜>Z¾B‚’7á®A¢LKåcߘ‹Ü앉r ‡{eu·­{NÃýb>fL€i7ÐZR8'zß+'ó±BIzý¤?Âç0Þ%CU%Êú’NÁ‡âÈ]o3ç&ME _¹èî@)Cg›QrNžËF¸ÊÞˆÅÞÇý¶càêÕ^ÞG¼vÆÚ§«_qI‘H¾™xIe*=vV…ºL‡Û€Ö?gq#Ç#Õðâuɲ|cªü±ãM¹Š?¿s0_0ü)i-G[0|/Dì‰ÁWȬ‹yrî}$ƒì˜D€hE!a‡ÎXwµ¤¶±¤Çû€è0"ÀIB¨–Ä ‰9ŽFî¨(L$ËÀ“¸_âÜÌ‚Ú9?éŸ8t`o—^}îfl`÷ØÑ¬Ùþõo•kÚMÚ.È*x9,,L‘äú¶?Û_D-çNß\ÑNõ¯H÷4‰rlèá|Ý5{BCóúþПã$Ί9ôG1‰2@ 1ŒÑít· KÅ'kw«šŒ­eõöýRRR®21ekMDZïoÎMêB`å–4 NW»õC øª.bÜ@?¿Ûn#ã†ôVûqrMqà#9‹‘vxß2Eòtd =€<–c6î®ÐI'Aaò£þèÒ/šî:¦\EÀeU¡C¤ E’5D$QAAþÒ é⯠qÇ•b«”Z‹%ç ’>´ÑÛZ$!–#ö nTû£- ŠÙë—/¦bÔíå««»ˆ«oûº÷5÷hÅàž_q…¿á%Bé›&$à@ Þ‘Ó%›¿}¼ée‘bQLèO>ŸU]À˜öæ©Ã¥+:™b"ÐT˜ôh²bQHrk&EÒR[vÆŒ?‚>|·õ†+E?à÷VlAç.[»Ô³³*f ¬ÈÉHSÌxæ$+lÌ©ª_¾>g[;­ŠþìG¸“@ÐzL¿Zº c@Zhh‰Ä—–ÂâŒÔñøML‰%¥-t>´Ûïâ…”ï I°Ä+løÉ>ɰ(Ó’¬>½»¬ÉÍ?Y“.ΑÖ9È<ø%’_ÜyøÄ9ä?¨ìn×ÒíŸuÅ&ëŽ:“¹ ªžüdÃÖM\yï5F¬6?ùgJ©\,rööH”¿3*mZó9=Ùþlƒ}™§,Ïœfœ]¶‘{›5æ’tŒI”Wëݹ×M·íÊZºø î>0=G—ãF î±=eë½âº¯Œ~ AøÈÈZ¬ˆZB+³;Qž=qˆòQÖû˜s¦"¥¢àÔUŽ{Úâ°DL÷/¶ìSÅ ìÕQ”r*üE0>‚™DŸ½$«`ɦõzöY·s?üŸw8‡X×U‘6¸d‰ÄBRàüO’ŒWp·().A'ÄI”­èd«Ì£ Ëm$ëÛAãÂNÝ)ˆ£Z0ºÝ,èãª&º\p;ql!Ô$™áR­›W,ûª{Ÿ~7¼¿j[äSߺٯj4“–lÿí™Åâ_‘PdR÷@„­ ò:dßšh—?ï(˨C.­å¶µŸ×íÏ/ghdá¶_Y¿ä“Eníädí^¿r“(7Ⱦp Ë3ÏØŒ¹ZüNÖ×juЪܦ‰r ˆ2“Ƹ‡w‹‰ •ÏÖïAÊÖR_hV³ŽmÆaßœzHÅŸ‚¨¥ˆÊ( o9åw–o‘rÄxŸƒÐ•ô—å ½m{3eDÿî ¥ìÜ"5gÒ%†§Û‚mJh»©Ùpäܧ þj@r§–A–¹L¢ªHr)°•••AŸÂ¢ ?f÷Ì£Ú]£5BGë:EcÄ´ÄÜN· âC²¬Â¡CA‚ìL2â´$ó˜fm$Q&I.ãTTTPœ¼1é]¿é7<ôÖ²MŽïÞ>­RÅôµ5wû†5ùDÜSÂñ}Š;Æ„K\ˆ÷;íÛ‰Ü hÞÙë|÷m¿ŒtæöPÞáZ©›PcÔ”ö'ög0þgòŽ—sr.T´‘»U«¼+×^¡wÏg–n` ¸™ÎZeÌé§ügW׫U»p%ÉrdÖøA•Î3 £H”÷ MŽñ•¶™LZÚâ°Ç#þ1c —€´1d&…îŸc°­Í”a}»(¿ä_ÿ} Fú©˜òïa@ )5#@" Ã˜ÑÂÈe†6#¤ËE:+Nk²MY”™å¶`U&6jB/‹ebC«2c$“\’3b”3œ_KY’uãÒ:©I2™ šR·lH‹mßásü¿E>Çý7O±¸[–[¢ýÿ~°Pü‘ÌŒrCŸ t‚kvÍR;yðçx ³ÉÖ“„Jä½£~2y \h‚®%êMmZ’I’weµœ~(ãýÕ‹ÞûE±MØ6l#¶•I”‚!äºýdú¸Á*0:Ã%-]¿[~°p¦!êæéJôܱãhæè‘™Pã}¡ÓÃ=<çHòôyÌòLL¼‹@Sâ°k’ìݶ¾ÒI HþH Øé ÕÔ9Òé—ìnInKD™¸è‰ØèÁ}zAîº_|q„ Y!&šjƒ1&}öɺËçÎ^—8ëÎß^…A´tT-ü £ã,7Gû°½Pü‚Á’!súÉà.×TµÑ‹?R"ÏÌ•Ÿ®¢„ÈÚ3"s/…ÈÌ^•ˆE·3çµµ?ã$3£[pàžÝZžŸ²eÿwoLÚ‚bx2¶ Û†m¤]/°è}©|eÞ?ŸÏ/œ® ±j:væ¢Ëo¯¸¤LžûçR¹wþ$„뢮ë_K6 €ˆÜ9k¼¤áÓüjÄK>…إݻ”–1ºà^þ–¾¬'îDú)'qÙºÐÄ€¤€âNõµ¡ üh²ÄKÕ¸ô²5QÖe3’ä LäI~iÛ·ì;¼'5kâìù×Ù¬åS@æ"qMŽÈÐ`GLt¸%$°ú°U;EUÿ£ìzI¶-\’ ‡ŠQC"/§ÉëŸ8ÇÔ«ï4 ¼·.k¯J}zC‰¤íÚ+!~ä°WŽý¹¶ê®cªpfAd2`c±Ù¬ùÇf$mûbÅjø‰gc—|LlNîe¶Y³ˆÑˆ2/‚ïDÎ]ΓTdÏcºÅíéY’X‘V–7ûåÜËTW5¯ H}¡á½U[eì žrÇ̱’yòø£f ©ÀØ^¥R­ ÚWƒ ÿ€+-vå§ü‹J;xáV Î¹N`éB@cíZa.4ZŸ†dî[Jÿk¬kÛÑ'¶™ÏœO4+ÉOùô}¥ÿ!­—ˆþ+d¥Ú·Á^RRdKZúñÚ¤¥²¾ïÝzôÐ?,"*&8,42 ‚½(‡f#Í£Ÿ2!GŸ*+¦x &Oa$S<‡@yIé£è)Ï]UË–D=e³Z/¶l-̳·a4aÓVeZ2ùÞ¤±ÑÔœÿµ•“ÎÝ5€ÏãSìì‡f¢\%eg&çïO:Š?…0c[„„Ž ƒu‰Ó¡’’¯¸¾¹¦+ÞÞ[z,eƒª~¢'ß}Sp‡İ®:hW q&¦ÕaÝ¢ÖdÔÉPeÖ‡BBw†þ*tîÖŽòÎM-÷Ë.Cḿ qJO_¸"ãcTeÊʯ`®v:Ú÷ìÜN~þí[d×þ,ygÅV‰Ý&·ÍÛ"@‰gqQá%T€l^OÕÖÏÛ& Ξćó¥ÕîèÁ•úó$ç!~r6¯XŠƒ4a–LiıÉ¢Â:Të#Ö´ÒÛÜÑúÙqðy2šžòåÖÐzŠú×AœM1h Hôô‹÷¡þOòL N(&eUƜ<{ͪÜnæ÷Æv®ˆSê°e¯ùÇû8Ÿ2zÝ;;.ÀŸõ‘‡ãìÆ‚ÂMXlÖgçÌ;Oý·çãK†X‚BÚYB";ÞûÇyÇ_¾ë_¬S¢±¥…˜ÖGbN|i9&Ñ&™¦6'bÏíl‹fCú(Ÿ?ubST|üO8yÖ›÷Òwò\¶\Ê)/¶¦©°I qј¨ø%g“=:ÉÎýDZ㱌ÿ·çð ˆõÃûu—¸¨½*®¦{¹Í¹Lé$òðA~áÆTO×TÅañÛæüºÁ,¯ÙÁÃ+\$_¯8&>"X².ËcçeÔ€n>[Û+Ž824UŽqFPcôÆÑÏ çv>O]zõ¹ÛzÊèÀÕ§~ZOQÿcu}5÷1ð$êùF$fîË$k$m$ÉîýªeºaDOýÆS8Ÿ’ò˧¾ÌÛ³zÿ ÄžeRdÄ Aî‹iE%# aƒ}„%7þ·<çŒäïû굨Ñ7ýš¥w4½ýüG>»¸âµŒJ%¦­&Êt«Ð’e’fþçziÍ]°Ø¼b$¢LÐÔ”ôù§i}‡¼Œ0!q ʽé ï–=‡…£_ E²‰;g“ЊOØs&—¥I)òä«©ìWãÛ”b³ÛdÅÆ=òïÏ6  º¿ôîÒAÝâ‘6´MÝŸáV*/mçÖLV¯brá]µ|»e+ïJ †¡ŽC¸»<çbM¼ÁýùVú•&í )&ÎzÎenoK2ΫÄHD™Ò7¤íÒÙ3ïï³X~ÄÀÓUƒ{;«îýßšÿë3ÜK8¹è×Ûž~pòeö³0#¿Ê´Œ?Æ$<‘yø Ô€7"'}cV{óõLN>‹tÖ'ðÒꎻ9üøÑ£ÃqL &¯­ÈÎàóÎ4§ FŸæ/ŸÇ@ÊKHµÛÎ+çm …¿ƒÇÏɘ¾]ë°Umáú=|.ý„rÕ³Äç »»ZROyø[¤8­§¨÷QZõS‹Tв„E?ƒ²” dë«§þúÝ£ø£Êô(­Éœ‚üü}ÿ@Ö½­%çeÇÿǾ½¬4ÆÅ„õ÷B õqǬ:^b(ÎgD¢L‹ eÉÆ•K7>šù殌£–~ºÞatË2êl!NÄ‹¸Ù¿wIʦõt›Ð/oPâKœk$Êp†À€>§àÆ÷šE™gЮ$Ê!ÁÁ"¡˜:D…JÇ0‡ìÅ`ÄwWn7-ËíQ׌íÿîÊÀí” ê%=;Å)\‰¯N±[WæöjàóâÒOXVÏŸ/>g¦žª³Wºë)êyê{ì\/ýTc¡ææC@gê­¨š<]é:èaÍ^¼­.EDFþV—{d̘ÁÇ­þ ÉÈ ` øÞÖÒóôgî*³8B¥ÑÅÏ­^L¿±†ziÜ871F}*‰Ñˆ2ÁâMF³;_B…+Þûïû'ú^BŽþ³ÌN_6SjF€øüþßËìÄëHúÞ%k/ZE1Ñ¢Ì9q­ó³FÏÞ½I®ÙÓã@~gÆŒñš£0]HàA“Láá¡Ò5&XBlŠ,ÿeÑ:å³Ì:™R=ôIþËGë$íÈ)éŸ.ÃzvŽa WâKœ‰·)Bàý„RÔsÅçŒÏ›©§ê‡«»ž¢~§žÇ‘õÖOõ;‹¹—‰€ï!0ðÿ2âíÇ/tÍË3ÉöaŒa%‡õPDZ‰/î»k×>½Íó?Ž¯ÑŽ7\u±Û_ó÷3a®ÿ>¸`´Á|úE¤,ÊÀ“/¡àU¼ýá¸é7œµŸüƒß^3´oWǸ!½-Ãר(q–[²í”¡•8jœ÷ìÖòü]Ö½¿gëF†ƒ#†üœÉ9'w‹ ñ®Vå¢,sôÈݸá'q‡b‡ƒVåϫݹ‰+DÙO‚@äBàr.¥ˆýËÕV«Mº ¨sP^‰œ¿’'¯ºáø £» 쉌žfœe–!à%„ ƒ‡zXçéƒAáá OâJ|é3oåFß°Õê'”¦FÀ¯]²hÕåóçÎ:ýkÐSѦžªŒóµzÊš·wÇ–¿í\÷åìÙ ýT¹dóŸ‰@ëAÀ^b}Ö)•¨lø`§ÑCþ~¨âò²ÆŽà°—/Ô/n¿ÀçŒxåÁaòTi‘,Àut€¡­{A~ö£¨§!ëZüŒF”Ygšáé§Lk&- ÅâeºiÿŽí&Ï»ñf„:»„0/|Gdh°#&:ܨ{XؽmÓç23“`ðO[óØ¿aë+ÖjrÌž(':ÔOâJ|‰s­x·âó"ʱ{(³t¿à`>ú'ÓZ’Œ˜‚t¶ªŽÜYT"Šì’uò¼"„4ŒF€FE„â8#ÞʵÂÛäLKWP,è(1Nr?vB8ÂöQ)QQ˜"#žÄ•øš/š {µú ¥ª¯sèœî>”ºûð¤9ó§ãyœ =eê©ÊzŠ¡*OdürËÊåË ó˜©Qú©É-éåNœ½,éY§årnääIYEvVMr¼|zCÏ—2]¾b"ÂT2®!±Ú½S¼!êfÄJ úóþ~örëC®{ÄÏÿ‰¤é¾¯•Øíå´4+]ƒ÷ßç=wíòZ4ªŠS6j–öÃáWü1íç ÉÿT8OŒzõðßS~ÜÏ'³n‘]ðÑ~€¼!tøRµùñûaÝò#ÇôïÞoÀÐаˆvA¡!ÑÁŒß§Å@?!VkT¨µ¼«d³ø•åŸòTõ¬6kiiqqnqAAöñCJK9†²éVA«1I1É2_B9Ët»ÐþÉ®gëªø&oî¶Ø Õîä¡•.«2>;)’LòGq’h¸fp°_‰t,·JA©M Ê-°:É•ì"#6½³ò^üEßAØ=ìb‘ØÐ‰Qî*‘ Ç$É1ÑÑÂeú{WÓšì‘Æ¨Q?¡tE¢ÑI-‡uy9þ¯î?lTÏý ˆˆ  ðöH-PH™x7»ÅBC‚ÚKÏûÛËùÌKÐÛ¶ZËKËŠKr‹‹ .8|pßÔdÈš¬ŸŒu¡ÎÚð ØÚ˜ö+rÌÎ|$ž¿H<›ÁF|ÝzżR9qæ²Ã–Èg.&ÓÇ ’cÁÝ®mbRêörÛðÞSÏ4Þ‰I²Tï{b̘>0}F1µ ¦¥çô6#·÷úŸ='Ò~ 1<"²°¼ôW¨ç#F¬k]u2ê]ênµÑä×ýU e»Ó\ ²8cNR­zZ˜ëc°Ø²úp§„çÀöù‚ JËÍÿ|K^žþ’Ò”ÊUÐHõr&^Ú·›cbý)“–df¾˜êmMƾHþ´Ënå!ÊÓ~„Zðâ&ÄÁŽ q„;//úÕÒm ýŠ¡lKK‘Úd9‰]hq¶ÛûjâÅ*¶xÑìPP¹‚xùûÑ·–x`CB¬|»ánAK2I2­óNk2£]èÇ£Å/Ã×+P§~Â2­l0:¯û1IJGõShŸQ½"‡Ï{Li·_ZñçßÙËK]–'µ¾e¼®ŸZöò®={:Ƽ»b«dçÂ=¬£Ì4Ätsƒ©îtN±“²xm²$í: ÷Λ(´2k½æ¶{›[øÒÞiv›cºpt0ŸÝù|W GÄÿG¦áx}Ù+9y{Å&CÎÝe± úcÚ“6q(—M‹Ãþà—ÒÿœþèLCV¸–J•(³Ê|Ñ:J¡ÒÕÿÉÜh1å‹(‰2¯ƒ7_F†!ɨ‹d[­W löõÝ‚góÿôèˆë@”=uƒk\H’ù’ÔÖdec’eÆ&äDܸ8ÖKzlß~ìÈØ‘ù@±íŽ×±×Î^MI2§­ TžÎÿaãàfQ¢\¢\¢lµZay&Q¶Ã¢¬ßËõº4ŸÞIãB 1Ý)ˆ—ŽâŽpp¡j"Iæv“${¼Éµ>bÁú9äóÕ,ú)¼ß”þ¡Qê¢Ê³ÏlI6â'M‹Wô“ºxƒü|µ=]>Y³K:ÆGÉC·O5“$UÓ.O2j@75qÐñ’õ{0ðx­Ü>cŒÌ?¸Më¨ÄuŽ€³Éi¯iØ@bÞÉøÙ`ŒrJÖäQ=쥎ûª$Çá÷½ÍÈóŒÇ‡-‡ ’8¦ÑRnµÛ½ã.#×¹ºº™(k%Ë—û2_D$~$ÉÊJ‹yuDÙ0„9­¨øÃ‘a¡7à",°0šî·=¯ð<êÝX!‹~Ñ­B¿¨I–Ù¡à®'‘æË]‹ÅÚ„Ì}i8d2÷tØË˜¡Ï«DY“@’;µ ²ÌeÆW U$Y ô++SýàïX‰(·f¾\aLvâ‚Ó} E:Ý*ˆÉ2­Ê¸G‚ÌõÜN’L,Mñ(úÙkvý„Α%¤ë 1bQÆ%)ÎÚ¹ Wf$¢¬uŒÆÈ+úÉ£­ÙÈÂØA_³#]>F¬÷á}»ÊÂYcðõËÙ.,²MÆl«/LÆzgÃî°Ë¬ñCÚ¬®:»;ýG¸•œ©ª-–‚¿ ÿu¿%ާñV. ë{ïÞ½Ñ}»‘—µãqÔo;ý#q aU«ò#×¹jÝŒL”YW*Z*Yw…«­6¼iÔhsÌ©™8‘ è ‹Æ¯rrÎ}?¡ÝÖp?N¿‡fnÏËz©‰µ#&z"Fî/#bDrÌ9'n«·%ûº~Êiè *¢ŒªóA^íÚ襒:ÆŒ$O‡Ž#¤%™ƒbœÖd›²(Óš/¬¶`U&6jÂmN‹2±¡U™fØ¡ 9fFHg(8iàgŠ×hý;÷‘±þQ ±¼*‹Ãv±0mÕZ,6êùö2Wu“ÆÈ+úÉ‹õ¯³hêº[|²&Y‘ä{ç¯ós‡«Ž-PÂ@IDAT°C¡0[)²d]Š$ÄFɰ~ÝÚYò—ôŽåÅö_»qȳ{pZÿ?6qô [¹ý~õ¶ÇJ?˯ô6_˜úŸ¡;ü)í#(‚»Yßr»í˜Mó…ºë:(ëzò%@…Ë9-£$ÚŠìN’5+0œù¬À&뤈²`äá‚qÑѿٙ›K‹oc…xP46š,ë’þ¯·;÷nè¯Å±W? pp E¹ÙD[BUæ>’Aø)+‚ W FÄà„w•²(³Rm…(óZØ8ý·I–ýAˆI˜Õ2´ÆŽûšâuô3Ö,ú)|ðôëý‚ÃU’¿¼¼°ÐHÖd vóè'}¶˜³ÃþîÊ­ÊÝ‚–dS‡±»p%_>X½CúvEÈÏP<æk½mˆµØþ'¼¼¢œWkɈˆúŠû•ƒ$?w›f’•½w¦lpßî ËAò‹ò2¹ïë@ð‰©_L»éÀcÔï²/ÔßWˆ2±¤â%ùÓ/#ZJIŒ9i+²&ÈzŽMÆßfg/ý¸{—£¨l/Ô(æ•n]n¼.7÷ßM¨~q®'b£'½® §à¡þ{}r³ež][—IüèF@«)ïi¿d’cMõœÇµV!—eÿ‰ܧ׷Öë7ðuñyóº~j×®%0ªÃ\žŒâ(*d Ø5š¸ªˆŠi]¤uçzÑêͧÅf ¨5Aõ£[\É+R>ɦ»E㛑Ø-˜6BÅÈ_»k¿Ì™4Lé7êµÖ.øîÕ×iñ³<œü …ÜFIÖØãñ¾» ù1ÀO~îÜâ[¿û~<ìÈ€Óþ†—õYs¼Â_Xø‘c%üù•øQÖxjKeK!sp²õ·Ò²s~3³³%¨g·×üŽ—XàÀïcö×&VM¿ŒXŒÆ¥‰E^s8|”":È‘˜`IJ¢U¿YE@­<Ý 2+ÒH²\“eþ׸èmæ¼ÅÐÏ¡WôSÜË'‹#¸“Rx¹xb÷\±Q_6Í¡Ÿ<Úà¡û‡ÆÌüÎÿ+ó/ïQSÁÔ5Dœ„ KŒnA[Sš†1$–›RËô1\_ÄÜu]ÓÎ`¼£9€ïLrÚ_\5³X>8øØÐu®ÿXÀ­ö;·ÿöÚ™šêöß§Ž ßˆ¥ì›¸¨(LƒÓNí§+Æ{¾p­¡ËFeìn©àKÃS°%àMäg/`Žv?‡ Í=zZëê~ÝÄÁãÒ'99ÊêDEÁÁ'Šsû{ü$( ÔiIuº8]à‚@7„V>¹_wk~‘4â¶0â!ÕOKèí¸ñé{ƒN’ÿâôEÏÐ Íúõòº~òdƒÇÏy¤k‡»Ÿ}Ë?,æúÚÊ¥5ùØé‹’‹d?Œâ`Šg –y…%rØÒÅŽ8·f9³;íÇ0o á5Â¥"ßø˜ûõfyˆò µÎb±ûÿÒ}»¯-úŸ—p¡ÊPȺ;ì¶§|åZQö¬…¤o¸ÿ¸*l±ûHðmø)WH¹MšÝýBŸÛœ›´u0‚ü_€ãc½lΛ†@ßÇ—MŽwË ñP[IÚšœžu Qq’kÛ¿!ÛÖì< ç.{Þ‹æâ•¹ßª´È¾{&(L3ŽžA")ffm½a?G¼t°‹ÅnyF{é×xFÿÇ}fA?ÁeM¶ˆãŸÝ¶îÎÔÛ}uhñ{ Ɔ­¥µ|â,ßè ×â‹®¾€kuô·øý¡p†™‰zöfÆGì½%åxbƒîŽ›œUq0òņ¨–Y 6„À—ÓG–[mÊ„‰—MvÂÈ¡Imèú½u©Ÿü]›½ü5KqaEø-‡5ÐVrçë\õœN¢l“Ë9*ãc7DN_È‘W?\[éÑ»Ë]жzÛ~‰‹ Wƒ+íÐÀ?™§.J×±ä|½¹c¿"÷ÍŸØÀ’jÞý÷o}!#ûuUIUܯ) ÀOâ£"$qLáu5DˆeÂ[2åw9ˆ2os\JküjVb/ý+8@¤ÂÇ"éÇ ýó7°ŽŽ±c Wáú‹C,¿qÛ쳋 —=ðûþ‘Eò"ì¡Uy¹Ñ/È´(7s !›Î$Ú^ÍÓBéú#ˆøš¹ >¬X.‹2!šå#h`"ÐtlVû©|ûä¤I¿ê¶uëUÍÖuªí””¼·\»‹9,´(›b"`"ÐÌàCôU¢ìgYÙ̧oU§CÒƒ8dB[Q§(¯ÎnMËIúÏ£¹)Ë-2ù‰j¯—w¬ p¥eV FžÆJ‡¸HéÔ.Úu8-Õî‚/²|Sšì;r†Ÿ¨eDÿ.2ÿºa€Ð$Ã#QÇá“•»úà^eÞuCYíåÍe[¤u{cÉFŒÕ𓧘+K×ïU„󎙣eÿѳ²uo– îÕQÖï>ŒÄD2ix™8´—:ý™‹¹²n×A9|â<â±c¼,ÄÛÅÈý7Ömn!]ÚÇH§øhÙsè”ìË<-cõp¿¬:—‰i1²¯ªXùŠ(WƥΠ¾ÃÀÿˈ·—¿ª«‰&ûÛþG‡lÔÿ9?’ŸûCÜ—j,eN@hø îÛ}}™1¢ãmŒ`ø¯ÅasЪ|³‘¯Ë´(·@ëô¾iÁ*|O9ÂSC Æ•–ß×Õ¨÷)»‡FB}+ÌŽîG™ÑfB`äËGcðRä<ÅòE3ºÕfÈŸÒ—Ûì; {]$Ùb·~rùíÿ·$¹ÆŒ©$³œœñÛ½Kà¾Øš.© ›ÓF÷ƒÃÿgï:à«*²÷¹ï¥w@B - ! Ih¡"eUÅ‚]¬»ìÚ\Ý]w—¿ºnp×¶ºk[×¶ ¢"(Ò¤„–„$Bï5=¯ÝÿwæeÞ{éyÉkIîÉïen™;3÷›{ç~sæÌ™ÊÈ=DßmÙ+ê‚ÉrLtÝ}Í(ºÚâÝûƒ”¾Ô‡ –94eL*Ýzåp¿¤¼’J+ªÄ¶ZÞ} Á¹‡éÚ±iH§}ùãnA®9ÂÊÍYpÁ©§gî›B }»‰kœ]HåÈé âº@,‚ÔQ¹3‚É|çÚˆ–¤é+ר•¦ ,]Eyå0uòg’h(Í:+dù£<¥Ú }7l8/÷ÛK¨ßß &”ê·k“æø´N#Ê^xò”yó`šC¯Ê¬1úâÓæÂœJ¸¼ø<(º²óñ²ìZ¨! !à~*,%Wáݳª0Ú¾ó±_\dÄý@´2‡ ²¯0ªæMhɬm_“?ìÿÃØ{/ÚVÙTòÒô¢µäíÕÿ­¡ß½±Tü~Ê*¬“-ã!zÑåCÄ—ÇÞžsÐ÷íÑ6Í!Ô ¦y ¿¬AîÙÕª¥fåÆÜÖM=ÒúÇ›}ð¤¹Bxš`)8z†âáªí›ãzv¡ÊJ£ à¶ŒÙX"ÿÏÏÖÑ_mD>©_÷Fb7|JvFÚ›éEò‚=×àÞl>“1±VÞI%ŽH ådå™õ˜’×߯ãµõíÜ9Éûðê}!¶¶˜žöå{jùØ‘/ßU›(›þ},äñІ#í@úà‰ý2v¯ñÝ¢«<ã¶?—ÏbQdó¶&@@U`vª kIÍì¢c¸÷~Œó¾ …ªÚºRØ.Ü•7w¯ÜT’Là¬?ÄÄvk„µ¹LrYºÃTÁQØ´¢¼Ò[_ûñà­ÙEÂ좼Ê@K~ØIOœ¥ˆÐ`á¦.ÄÉI…½ ‘f !kšYzGÓÆ=bÝOYE4ÔÓ `ß«+M7HL& ¶àPä!–˜£®Z’ï\’øNn¸ù¼ñMùäâü¹)5Þã¢ôô$¬7ûŒƒ~Ï\eñbaËë;w⺒ Fÿ‚*¾Y¤¨ª·&½œólî“É\—ƒëRÒ4Ê®ÃÒ©”j»Š3‘ò˜S x<²Ž‰²<Ü‚0Ë}-ÔÐp Ì®–9@søÜÖ¦`ü_Ê|Cúï€|I{€#ÐÏ_–7;eYÓ)ØcHâÖ:šL­mrlñëTM˜e.lZL§\º±‹7Öòò¹ ;÷Óñ3é×3¯ÆdÀ+ˆí,w_ŸL‘ˆ‰€•B³X? 1 ^¶oNÚÿ¢:c†ŽðÊcpÇúNÜÖ»å~{÷ÎI…F¹Ú±Q§ãgKnòÅû­üÅKÚNË QÛp§ È'‹uæ4l¯k”kkÚ‹ö¡9¹£½o;î7çz-NÛBŸØñÖq'hÚåǶUzï”îßú›*Lß@/ic‘pŸözZŸÔÇߢ˜[R*W´1ì­â¯ÞX'{~‡óÂwÁ«Û+s¾ŽšÝ~˜d÷ô=“…§ ¹¨ˆc‚× ¯l2¡`Ñ} ×nìCzÆàkØ”CæË®ã6ì. ßÀ¥\ÌAØò‡ßl¡­{R¿ž—8fAÏÌÞMű†î©Æ­Øqî­È¾U—ž¯ªäÉxý8Ôñ9ø„ rM),Ü? oö@qTQŠ)0äÙš1Úï0Q“^Ê|î/^´Þ¥ò Â|íŽ5¢ìåaWqË— 5ŒG{(]ÅýËËŪ“}œÙ\T¨(f4Zz”µ—:eJ ²b…dÎuâ»ú7–üãYçìËô†y©ÓsÅepP_nÕœ´s¶F±z þÀýRTXuŽ ¥”¸ê‹pù§IûAµM£¬·èÖµŸ;sÏ$-Èc²X–¡Å¨fwÐÀëÔ§rç Z˜ÛÊ,e;ÔÊdš}9Ù†¤>’,ã´ÐÇ3V¥ XðƒïóØÙ‹tøäyºrT’LÖc¡Ä™Ã¶*I ²nÂ÷j¦½üðrá°L5/š0$Ê\BÏÉ8XWáÅøM›NÉýŽ*~ú÷“é9Ô4ZQ'$ÿcoBÎãm¦ž¾€F”½\ ì*® }È«xHþÎEA‹]ÅùQV22Œù郢lq(«îàù“qØÎÁÏ­Â ¥Õ%“…ªªŒ´vG.mص_Øé1 Ç’§áXÍ©5ÎÿÝz®N¼ú»ÁÁE¬väÄ9*ƒþ¯Öï¦Èð`š0,™~6"Ih0»|ϧ7`þž$h“£9gtÎ­<åùr´•A’§ZTu0³z±P¨\óΜك–´•{ðV9br!Oà[½5—Η”Q:áS.KöÊÞ*S[Í7ùå½=,&ó[öò+ÀËÅgö}ë–¥„þ„^IÞÃ÷¬(.º'x@‡°º°A‘óäÀã‰/íáe¬¯çƒ“‰"y†·}E4¢ì5Ñ6\ÅáC- d<ÌbHÓ­DYd£ÑHÙ…GéóÕX:¶‚x¢ ;ÔOêׂt‹äÕíâBTÀ]Tî“´3ï0-[·ƒ~Dgâö«/¥Tø^ÕéÖH¹¸Zrn@µ7Ã’VQh=UÊ]-¬‰@Òü¬{T‹åmµ~×夿Nw]öì”­5cj{ !0!}8ÐÐiíx3€rG6ù=|+« °r0* èÑÚ—æ–w…¶I~:Rf{r”¶vy¼¹¯è”·á•FeLe¼ËË?븬·7ËÆyk_Qo×ògWqxPþ#‹â³®âjx¾pŸ‹8Ö"³³y#lä*++éû­Ùôî—)U~yã8ºÿº144±·F’«î,0Œ ãÃ+býó³5XÉ+˶º•|¶´°m!€Wa¼,±Jš}²Ä¢vMò\Xó‚œT“dL Ô«c4’\)mßÝ$ÏÏ|ß°j#nÅ¢#ýÌ-%;æËdsrßÀó*8”P+b3v-uŒÓ‘¶Óz§®ÀÙQ¾gt0ºËÈœêK÷¯e© ªMÓ ÒÓûÈ}Ÿ -d÷|Ajw”‹I2k’I®ª$ù›Y”CÝ<¡ÑÕ¦ÜQž¶–&¯ÆõðŒ ¯%?ì ï·d <WMÚ¨5»}²^³O®]ƒL8à#ù%Ì[x CØh:Y”]è+^–ùä Bë¾ö_CÀ3°©–Ýý›Ì @ósæüQî˰ }èÏñìŽä}˜\Tú+ú:g·#„<Á/ï{¶{U•ŸÛ¶}`C#Ê>P \v‡ÕJÞÆ ÷‚f¶Uö1±/:‚‚¹…(;’äÝyiÕ–\âå[ïœ2RÌàö1@|²8<ÓñbÜ–®ÝI™û ²ì“…Õ Õ l爓½8ÔO¥·>‘¼«ÁÈð†gýà#Zdu®¼}à´¶S`Ðø¬'ÒNÊcZ¨!à ReÀ0ê#»}¼²Û¯þ÷µó>–ž~ ôŸmÇUå/}22 lûtCÑû¿ƒ†ÎªÑQÔɃ^Ù/Ú>_€C#ʾP ÕeÀð »’²üÀáÑ£›\VUÆ÷D¨êtìù¨.×x3I6™Ìð`a ‹Å¥ôÕ†L,ïN3®L·g«m5Æ­[çúߪ­0a1jd¹ÙÈùFDÕd.K2¸cžæ?YÂA£>–‘µ¤äÛAR–Ä]]{˜Û~Þ;[ù`%ýö¥tæB™­hßééW¿ ý‡;”ƒÛý·Ç ãauXÞ0ë½)•~:ÝÙ·¤ÔY‚º‚LAõwñ¥@õ×öˆ‡³÷$–¯VÕÕâ:•ôUƪ›MÃ]ñ5¢ì.d[nÜ´é°Ó©v¾mwׂ”Üs‰¢ª‡í)+½íÛ­ßâ‡>C ÉðâÀË´—WÑõã†hšäÂËšåéð©z¾¤œ~ؾWàË8kÒFPÈF”ÑPg´‘R»½˜C^.Š:g¾ð†Þíÿ=¸oêŒ%xÌe¥³7º|½6 à,fm%~âü웡‰°ëH}jïì”ìÚå?0rèhtîî—ÇavñXìÚµ•r¿£‡˜|¾È†Z=¹ÏvÀ{š× ïa_'g_wÇ“á"®/z8W4Þ{oÞ|®Î´à€U›lÚä2øEÞ±ÿ¨ðnÁ6·š´ƽ„¬ßµ®9^0Òë=¿,mËï ã^‰Éiö¡~{ÇEÂ~ç¼Ú^EEÉ*»æf)¤ü)ï©´g÷Ù£ùÜÖ¥©±´‹yä8o=Ýë”ïè© ´|ý:vú]F#ö¥ÑƒâE¼%kvQ'¸~<‚sù‡NQ×NátËUÃáÆ-‡r¤8øOŸ‰¯æö¬R*Ö±òÀzL±Àìܹi¶y2®;C&9‚¤òÏͬH×\–JLd×C;[["AFÇŽÇ*›a”ܯ™?ÿði[´Ðà@š6n° Ûñ½.KOß’ÌZa^(„• ,¹ŽCû‚ã=) ZèDh¯3 މsèTИa/.§!‰}(„\Jdh0Ê—&”—A“]xôŒ<%4Ä7D=å}°«æ6äVGå´®›&Ò» åï‚ò—CÉq !q  ôä¾Ð”_¤óí6Ú¶ÄkmTLäb$Îú²*:tܪôz¿³â/Ö.óôôÐ>o?®QDazaýf«ŠêÓ nxMÕD¹ ÃeøEÀfN×!ÀË|³"ãÌxkâÛ(fËPYKíŒÃ·‹íòÒ¥½¼'Î`VW£-ìljƒdEw;Hòç.Ï̉#`*QŠIÇláL‡ž—ˆÞº÷}³1Sh¥9K(géKØ''ôî Ó‹ñH·Š^x§¦éª¿Ÿ¾FéX;\ŸúÁÞ8˜ž¾§z½ ‡H¼`ÓÍ Ç¬¹þlõúøÛ­¶xg`Î!…·ƒáˆš5ÒlÖ±f{=Š„ÞÑ´iw>-ûqÐà„ =6q´? °ÒŠŸãš˜è(™l“!cYЦú]Òd\oGHY˜Ýß(V´¾¥¨ŽÅysR_¯¯\EéC¦›Uõ&>‡Zcs¡_)‹×ñ†QßµñXîœä}^Ê„}š tCNœ)» 8,÷&šé…7Ño$oŸu§³Û(CÅÐj¢ÌCxücí‰Éd^/ŒF úk}¸F§O1ž k2Îs§Ñ.ðè„”™a :Snw¤0iA΀*±¹E?qߊRíâ¹sR½J’YÃÙ·{'ApyéxgÄÏOGSÇËÍs§•…ÃRØ!3Á5@3̶Ä-•˜aœ+.£ ´lÂöÍŽYÍ(Xk}òl‰˜ôÇ&’Ðr^ìg[öq~ˆ‰€ìêîø™ 8Þ…ØTdõ¶qÁh¦ÜƒMãÄXr§¡{§P›é…53ßúŸþÖ±äÏPØQ2EÉ ~°¾Rž5*¬ÒkòÖã{'vû®µr_ @@GKå´…^7¿Ð؈¬  ÙU\Áò¥xãÑxt®ªª¸Eü—·‹©¨–#Ö>4J¢(.±Q–¦V²Œ·–š¸Ù!a¼5ñy’d õ:Ú•Ž%)ó³š,¦Õ¸k«‹…*0ê?=wvÚ*o"Áä‘ÑðL‚Éj;ó;=é˜í}ã¡-¨ö¡ÌÚâñ˜\·y5Ì2ØC†4‰pö^Ùã{žXµy/-‡æ—Ë:~XõƒgŒxÜØ™wÄÜ"Ã$¼xĸ~üq* sØÓž‡æÜÞ5Rà%Ãj2Àeö¾±õæ¸J˜eðBG=.‰¢»§Œi±iöä‘x×$”GìÖû± ‚KË.ÁìÉD”ËïkRRrö|£‹r¡óæ§èniÈw÷S%›Äp\ÜÊ ]8=%®Óþ5Š€ŸE· jßp$|·¦ÍSUÝ k¤Ÿº{ò2Šý ˜r4&ŒaÞÁ”J:¸¯d  ‰yc×zò\âK{žFûy›Ì3Þ;7¥^cã‚ôô±¤šfÙâ*ºGc×î¼ ÷µ°a²çÜš4?óZÀžˆÕuÑü½#niø ÷ž©ùFº7/-u§п¶BLÆGc/¸Š а¹‡Ãü"—h”%qÃ=Â~Vs_æôcÒÌ ˜vI¬›y‰Í |öjV,**³Æû¬!m•Šæö,_Ê5L?H’ ¦T ²4 ~i׺=ófdÀ“`Ù9ÿöíJ! œK×í"6/h­0ɬM’[“&»œ“$Ù1Ú$¹Æ9æúD’äúÎñ±Px·$Ù1ä¦H2c·tí. ÂýÇEG +¾Œ±/M:0?{ŠJÊ‹òþà’þu¸&|Gî;†ê”)xMÿF›‹W¢(Ëâ¶ïüÌ1޶Ý0xçµ²ZÆ0éÌ—Émo„QöêÍÌ“]Å‘Ny_F7‘ò˜ÜöV½vm)⋜?´“A¼n½+Ê" ‡š¸ _÷a몔MFJ¶¥…ò¶ív¾!\â)þ8Š8k’‰t׿ÎIÛä ·.µ›Làüüü°ˆG îÛ‰xÑÅð]ÜV…í‰CAª½%‹¿ß.0Œï@AAþÀ¤ÛOtF$æÞ*›Ì—G90æù1¾zVΤÐÝÓÓžçk‡E§ŽýŸ2«ù:{*kâ¶÷Î Ç8w©kckDÙµxº<5v'E×T¸Š“ûÞ Õã2o£ÎÔMn·õп¨µï•Ý'íÞw¤öáVﳫ%9|ÙêÄ´Ú 6ûd¼óÂ>9åõìîUU•?€\ÄZ+I©$Uݾ¹)ùǧZHyFžCügzoÞ}TîkaóPt~m1Òˆ² m£ì*½êU|Ú@½¢Wquâyô€ªœ’ùÁµf´ÜnIØ çŠMYôô«_Ðç?Ô5ûëVŠsl÷ÆÂKÄrÜ£XÖQ˜„ò°ßïßü’þøÖ—ôÏÏ× gùŽqä6OLá4žyí zñÝbR‹\ek×¾ÃÄ?W §ýîòXD éãÎæÛÜÍK‹ï ‹v²Úþ5ÊC_ÙßÕXaa›dhí1(zÝ yO¥üàr-ŽÍ¤«†˜Ì¦ŠJ£±ÎqizÁD.(0‚àŸ|@Ï(ê×)€2Aø^_¼FØ,×HLÛ©ƒÛ$¿¶h ÉÝC0Ù¥S0KÆ“qe|ë3½@{­bÒwy­ëÔS­ó-ÞE;©ÐyÇøúZßIL*…¿Öé;K8]_¢ê¼y:RÿÆÇÚŸÏcTdCÜöoÖW;Ö8·=™”ͦW õÐ#éåœ~_á¾³šFÙ}غ2åWdbx`8ÎöŠ|p*Vãr‰)¸(‡Ä¹Å…Ò.t;xúËLÐIΓÛí1L{cO§2Cåwx…;< #Ôˆ3òf§|ëÍû5VV¿p±¬cS&pþþþ‚Ø…††P~ý»GPB?:w¡„ÞüâGz÷ËÂ#†\†Ú›÷â+y3ìÙ‚±aŒÎ_(¦Þ!&êÓ¬ È82žL˜_Æ™ñv” ÅeªÑ`8çxÌÛIó³ÿŸ§ëd:Ò=°wNý“÷8NÁòe@ó|iuü*ùý÷Pç9’éiaÃ̃— ÷“-†jñšVÙÏVmÃg¨í*ÎXUq ûo¯X!¬aZ»Ú:rkï¡_.BƒS E¸1bÙ³ÿˆXþš妄—n½oš}žÀ Ìà^¾aOƒ—…Áu»nâßÞ¢ã´#÷ Jˆ©\0­Ú’K¿ž9IçÄ?>ýA¬„ÅNûy9Wvâ¿ÿÐI±èÌk¨'\*ÕžÝΤváÇßÓ´ËSJ\h°wayÚ`:q¶˜rqm?ø1½bdÊÒ¹FþÚNÛFÔ ·íõRü¶í»i¸ô<¤m8gÄh™tµEfŒkß™7'刯rë†]ü*ÊËΔTT)å•Ub%<™«•(ë(D.6Êa¡¡Ä‹$ŒFê È¡~%täB> /'Y£Haˆ?ÄŽ~‹ez!äe©‹±Š_)°D“F죳Ÿºë@Œƒ†ááa*¶WÆ×êùÂN”¹.¸N Ù·—nöÍ1ÑQt&,ÑÂEZbÇɱȟFÛrPbßn´yOa6î* ž„ȶÆÖ2X?À—À(ÿ¤t­“¼W-´"€ÇÆÖSƒ‚²]i”¡º#2‘uàbØ}>ÑݧŒç¡5“ܨ1 «â_yyIéŽk?пò‘–oP¼q|aI™Ü‰mÜo³àà`A’ÅD?ƒæÐ(WÛ1Ë÷º=+•…‰/KÍ+î±YãÃ.àX«Ì÷˜ [±j’ùGaìÁ­çž-ÿYRr‡Ý¹~$Yv‰Fy–ÿdAÖGh¸­“E2ëTº-çÉj† —/{õ8V”WQŒàø÷*Û4“ Çúkí6Þ—|™ÚÇx¹íÉPSOym×äÅ®âÄ,P<@ì*î½7o®pMÒÍK%8(蔡¢ÚLÈ4Ê\êD êõ ¥%ôÂG«áûXä°‚V÷.4äRh~²`'Ì?)w^=ªŽ7 y®©Éô-ü<µ~]68ž"OÙ=úõ@¾+`[ÌöÐl–Á+cͼfõk]uSeÑη-0ßÉîñBm?D9qaöÕ`Šï€Tˆ·dhM\@ÐÝó±ûôpÍ1áb2°Jü¸M­Ø¹aÝÎN]£Y‹y#}AêÌicGÍ2“:éÆL˜T»Žc2È&tØ­Úd³Ð(³éeI–‘n»ÆFüPå¬QfœX«Ì>’¹CÁäØ_h™­.êÓ$3IÞžS¤Ü—óÉÖ5ßmX\/\?\O\_.!Ê É Q'Ó‘ž”ûÑœ¹i_Ëýú‚‘#Ùð‚<‡»}!vÛÎ]r_ ]ƒžž|̉©ŠÒß5©:—ŠF”ÃËë±}ÁU\×K Ù®@ÃŒ_È© ÂxikOƒ3eLª-Kþx=ÿËëmûAþô×Go´í³¶Øqßv†':î6¸ýÌÌÉ ž«íÚ톉ChÚ¸A‚ðGâšËÒĵLŽ7ì. ßÜs5EE„ûqþð›-´uïÁ:D™?2Že®Ç°¤>Ä?MÚ P6S&‡ãíáÎfIþ üP,Âö´;* húŠÇX3è+ÂÃLü5æ XLÆÊð Â/ð‡%‹¿¿e{€ëŽŸ=¯Þ6ùR]}6Ëü¾Š•û˜ ÂNYdt„y²Ÿœð'=ct¢ Ì„) D™É2†BaÛh™ 3vŽÂ6ÉŸ¬ØLÇ0X”›ý¿ï?ÿ”;+üázáúázâúâzk•`bé˜$ø¸Ley ÞWþ)÷ë Ùä¢àË¥ï#{«D¡qá‘/ÖW;Ö:0‘ϤUTM£Ü:4;ÈÕʼy–ßpw –V^^ÞÛ'ʾ^å~øÔ',gx¡¬Bh•޽H¼@Ê•£’ꋪë`à«osŠ Ò|®­ßþà…y1•× É¦|è†á§ly,¡ØÇîMe©Qæö,?ö|à÷ÃÒÅßž=yâÄðqo_ðßo#Sû÷RG¤Ä)i0ù ù “=&LüØÌ€µ¦Ÿã¥ÂyD^LØ(À¶DÉù©¼ó­ÝƒufåÚ˜€Ÿež«ÚŒ¨Ü¹âzáúázâúj±$ÎϾ3߆NZeÛУKØL®ÓÆ=pé°d³Ñüžk! þÏÅmÏÈlìí\˰z¾ØÃ.i£ÑÓ ül!û™÷¬YšF”[^^»Ò\Å¡8/0ë,•ŽÜw6äÆÓþsöê¶BúâŸ7Dâì¼µ<›ƒ€jÓ(ÃGo›%ÊLx’d½2‘ÎwWÜúqsîœä}ÍAÁKq˜ú°êеÊLŽ™$K¦'HtEE™Úå¯q|Õ€´¡ýúHJ  ëé§÷³ª–qR“æ#`2›*«**Š+ÊJÏÚ—·7oώ±݂úõ |:“­{„è®ü§M§*X«ÌfÒ>¹QBÛX ÌÏž‚ªþä‹ë˜+y|%O]{_,k«u¿’ ü\³Ywþ¶Ç†EýµÁ ´.A8ÃNÙj–f$a§|Ø% 73(7(_‹æmWqèúŸ—­>€-&ÊLÜj Fç„íníãÚ~Ëàed|>ׇ{ËS×®tx§lD9ÀO±uF]•¾§ÒI\˜õhn‘ùá-$÷©´µr߇CG­²lœ 4k3Ù.5p_æÎ½øåa›IµÐ:#”×`S“&ŸÆœÒF¼jÃÉÊc7õ Üá¯$„ú)A%†LQþ3â´Z›Ì«î™Éô9È®Õfž”U ˜Ü˜¯dy…¥žE'ÐêeñÓ«÷j&÷…¨«|¤>†sÀ ÖÁÞö”hDÙSH»8¯»ŠS Q–ÍœªF¹òö‚@èŠKíØ»2»‘VqY%õ‰Žì÷Ú–o›éE`pj›Ô('.Ø3¶?ÚêÁê+ù_¶}ßß`Ò&WUãVNî3Iㆉ‰2k™hñ7Tšgh$`8)_&ÉlNÁ¸3Æç«è½~aºƒàͨ‹N¹çÎþÿôQþ9>ÏõÑ"ብpÓ÷.æ:䑎¢ ]ÀU»g'ži*Á¢ôôQ0ÕxÖOù}ßm»²íûÚ–»Ptj>¦öÕ¼»òi(](7„LÛ8þ Š9‰‹Š^®G]Å¡u» !Â|•k”eÒ$€Ã ^¤£¸‚*ª ÄK6kÒ:Gv-L\Z—švµ»H|'7\=gm2ÞƒÒŒYŠ$kîÊÒåé&¿¼w˜Ådz-’ 2]5¨Oê“û\ž“[”äñwÜf¢\ŽŸð†PLôCX›(k„ 4!Œ+‹ÄWe6«’U}2¶›ÿ#:¥'> ÝNô¿ù£|z¯ú¾Ö)¸0;‹¾|‹!üp¾•t,@OWî~2ñhS ˜4)´ôÌ©Q\®kˆ².þºé h‡æ Ί‡{ÿÃ’ë€õQA>ªÒ˽¹ÕM]#Êu1i3G¼é*N§(çaC)°·mQ¶‘d4]<+ºKX ž­ Ü'ihbï6S¾ZPÆ‘«ªo÷Î6[p_-kG.—î¼¥3³TW›3»Hy=»»±Âô%Š_½Jåû…ݺøEÞ–¸·6ò7.7‡¬Ëâm&ÎLâX“ìH’™<19–?ljÒ [ùc|ùÇZe&ËÆ* ŠùF€^}AŒ+ô8Ž¿‹ŸÓ’úJf¼Ñ`ù$¹ _ŒoÎx½2óÉ´Âæ$VzöÔË(jÿêk/¢o4“=P5çZ-NëPtºb¬ÌXµ£ÓúT›Ÿ‚F”›•ÏÅô¦«8/‡¹Úb¢l#ɸİ¿a^Á)*Ôö´pL™wH#Ê.xêvæ† +êÑ%œ‡5²ìLÝ‘ì&Åp0§jâÙýmFf,Rõ{f}ŠÇp¡Qþóº¿i»µ<µ™›©YP&C’,KÇßM&Çò‡ÛÕl”³Â¸²HŒ%Y–„Ù\bö{3*Àø j £¦ƒ¶Mí>aÄW'ÖŠ«šùoÐ+û{U*WãúÕ—\ÄwfrÖ“sš“DáˆaÓ`®ñsfÄmË8$÷µÐ˜…{@™Q˜ÜðT¨eO!í®|j¹Š+>dBìö]kÝ•-]h”…š@š[L”9=Ö"[Ï[—9åÕ›º…è)ïàI*:z†bc.±e«m8‡ã—wð¥÷ç• áì¿ÚÏ«s©h±=€â§¨Æjî  ­š'²uI»g=–`¼HŒ—ÿÕÓ-9Üï’ĽŸW “7&tL–Y³,'ð1I–?lŠm5iI”9”?ÆXþÔW=»ýÚèpò!NN!ËÃÖòvsdÐKùÑUUåßãú¾U®*ú©M-M-ÓÎ3&Z­,{[îC˰(nÛ®mûÚ†GÀ´¥RcµBÞüç‹K)­g¥ôí*pd<WÆ—=‹Ô&Ê\\'•åe§;2Ž^¿wGÓ «­¦×‹ÔP’ægވѫÇåy•tOç>9h³Ü×B W" Ì[kB§ì-™¦EQ댚&-ÌISMÆuˆgµaV¨­Ýõy³S–Éëš–œÿ=žm¹úžA§WîŒ]»–í¨5ñìSžmÌ9{ðÿ”EÙv35”Iózá=‘Eµ«¸W1Üô2ç‡Þð#þí®¼»­\Y^–>Ï,蹪£aÿjrÈU=qøàƈ.]Ï„9ŨÔ8[ñØü‚'ó±}2k@F#™á7ÑbaÓ5«gŒðòJ:Un¡ÂÃ'…G VŒ†F„㺎÷(ó²ÔÅ¥T ¢Ë€É½"ô×5‚:u §ˆüÐÙ`<WÆ—q®-\\‡öåîÁ9¤dûÕŽªí»˜é ß È¶Ïj”±¨H¸Ërh[”%û榊vÇðhiwlõô¯*3ý ”?Ú»±»¯í–:øë“YŒJÒü=éªÙ´M—ô“\Š¯Ñ´Ü§R×:ƒZá°a—cå·ßY›@¾Ry6nëÎÝΤ¡Åuª¢–ãË$²ÿÑöÕ.ˆ³{r«™jÇc5ï¿}íyÐU“â‚aƒy•ªP&˧'Näa*»–¹.²‚€­ûzIvÂà¡g·evQ¶Ù°–“µ t¼ “d&,V Ó ?žìWIÝ&*E‹YŠ>¦ÁXNçÏáý±Î%²^ÐAþ3çõ‚ÑAXñ%Ø:… sÖÄ3IŽŠŒZy¶÷f\ëÓ&3T¨ ħ8kûOØ•“jD}u(}â6U³‚ÙþÕ½ÕG¯O”«v!,fÓ;x7;óq¼¶ÂÂCî«GÛ×p5i_ž:¹ýÚnKðŽÜÂitô‚'“^Þ3Úb¢8YçEE¯Nqv„披 åãdÿØüp¯‰Ÿ¦­¾W©/²ÁªŸ}HZtc¨e7‚ëé¤ÙU\Aúÿ€5 Ÿ“Õ®âÖº­ ˆ±jµã*++c»¡†ˆ²$]šO;ú¿,Eyhÿ¡”Ч»­x’ª¤2ý†°Ö˜íjÙl ý*°sUU…€,w±X5ÎÜ( ´¢"lÏÿ¸CÁž+/=ÖúûCl˜ Ûn˜­°&™ ³]›ÌÞ.êj“¹²ò(‡òó¾C²Ž3ÔLùã,5q7zµJj”¼í£àîlI?iÁžYä™b½F±à ¼'cVüEgÒÐâj´U¯¾wDe¼#wøóú¯‹Í “ga·Šáù³:?ý¤æúIv,G•¡â¤ÙKS”³êÝÚê{Žyo›4Ú}0Ó:ï L6ÏîÍÏ=&QöԞɈ]Å™ fA”‘£p}ȹƒEÙVÓ[,Ö‰ gÄšJAÄÖ-ý|ñÍ¿zôÖO¾ý©Ó3÷MÓ`-Q)Læ˜ø±0)´îW»ƒ™E%ˆr%ˆ²D^ y¶óŒŽ@’%FÖ³9ã%½…01f›ä`ñc“ >_If廓Åd,ÙøÍržÑ ¦øIÂlµ{‘k¡{0; %*®âÜ›k³S·.¬Ú< Ë¶0gîÀ›€QC • _vòûŒ©ÝÂİ/4#]žßñõæ®­öªŠrR§ê¯IæÎdU8lÈÃÉœ.¯A{y_ïÍ;ääAyX ½„@áùBû>µA…œÛJ§e·Aë„ÙU´Ê«@'á«Uá*îiw”.wJÑ`‰¤Mz‹ýA®›™ÔL23]¸p¦toÆÖ—•—>÷Áò êƒ7Ž·ªIqR’@&wbd™·Ù¿rHp° ÉU˜¼f0@”¡Q®¶c–D¹=+•«•É6Œx¹o?,YÄfŒ“eÖ*óÄ=&È|œÏ3If,k cìôÊX¿ö£òr;[m¾Øî‹ 3ו¬7ljâvt Êö®‰G'«4uoóTU÷É‚¬ð~Y;Ä eÇ=›×Ô…Úy "€vLÝ>-ú]¼'ÿÇÉN8ömˆ2>tTñó¿"ç‰$§ÉÂQé&óüêO·•ÿˆÛ¶c¹ ‹­%ÕJLe&;¿à‘l‹F”= ¸G²Su¯‚çLyY]ÅÍë½y³Ë‡*5Ê:UmŽF™ ±ªM«¾þ)¢s§ÿbûú‚Ô™ÓÆ*R³Ì¤Nº1c’']Ç1dM²Á`¬Ö&›…F™É:eI–Å}·ÓŒøáËÀeÆÆº¢¡¿èP09öZf«‡‹†4ÉL’·ç)ùY»—ïÚôc&׉ÃO’e;mk§xúÔm©0½°‰êS¦/Èü5^³1\<<‚F½¢¿{Åc åµ\ÛÐp+H3lÞö_üéRêù êVuüð©À¾ãóžH*r6ãcéé!åFó§¸.ˆ¯EÛº+.ºK»œMJ‹ïF*u0­aÕ “45¢l…Bûß⮻åK ña‹C["]Å9ÌRoMêökñÁ,EúB`'lïñÙ£8nIÓ &abéÒo?ýð‹I3î`ß±·?{^½mò¥ºÚ6ËL ÙWAØ) s Iòd?9áOzÆè(D™Aµj‰­öÛL–õèP0Fbº!-2Û$³¹k’÷gíújí²ÏWËú@È)®®#n–4¢ <&:™2;ŸÑ(\˜§ÿ“£G`ÿ·wNÊNYP-ÔðÖ¨~Ç3²ÞÊWÕûwwACÎmÙÎËxâ³+–ä8M’ùâ 2ñúɼ1·2Eñ»MY±Bë2 ¾$£Ãj|J‰§‹¦i”=¸òSæÍ³ â Wq¶žæÓ5¥QfJÍä‹™2¶oZµøã¥Ã.ŸpÊ2fÜ} þûmdjÿ^ꈔ8%­ …À„€‰2“?&~lFÀf•­Ç¼ðï”’´ ëMäj;PúiPïÔ¿äy¡,Z–ÄwrÃoÏú Ï¡)]×cŠ(GÏÝ„çtÚ>§Ú«ÂaƒoÁ§„=gQIy$~ûvíÑ–€øP¨˜)ÌV¹šé…ÕL[/Š'\Å©öÉ| kMi”%Q–e&f<¼°cýÚ­¹Ûó/|Í$³Ù4d.‚½°à@êJþì­¦Ô&ŵ÷kÆn_{L†¥ö¾<ÇK…ó*ˆ¼˜&È…9{7ü´ê›••åìÚ\ÜCçŽe[ۄ㚸cÉ|>az‘4?ë~“±|ëx쌳x`ñ-øli¢!à!/Ì‹©„žÎWËÏS¨:Ìê5_%²s±«8o¢‚‡Í(š+Lܘ´±0¡“ûLâ*'wŽJŒ L5‚$ƒÝ©ÛÎ]XŠãçñÓH2@pB$¶L’¹®sÖ&3QfbÌDYÚ*3ö|žëBo! *gñÈ3A&‹^am®Ç‰rÊ¢ìãAË?­¯&Û&+‹òf§|ë-H´|;ø¦(°‹Ä÷wò®1mù\a^³wvJ¶<&C|>B#÷,ïcô‘Í/$ÊùcÆDSUÙßúQheìö Øø^ŸG A–PUtûå¶§B({ i/åSÇUœ¡âNåmWCV&µšV©ð«ãDš’À11sÜf²V~W—Kž ÓcL DùŒÑ¼awii޳©†lÑdˆCšÔƒcÊ"±•D™‡%Eg!“e&ͼÏÇ™HsmÊk±©‰§À7ûûx‘/ViGèñ‚ñú0á2 $¢ Û<ái´ü:C^.ŠJ\õ!ž=Ö [E¡xþ®ÝûäÀãòcè¯Óh0›Q¡¾vçô~QC—¸à‡·™€c!®ÿ qëÎûxÏN¨¡3Ñ ÔÚ;ÄÇ•d#Êp|åñ6ÑrããPjÅ«:®âTzñ\C”¡”­ ˜«3e.*_ÊŽC&h¼m|8&&$.(`ªÐ&ƒ1d•U¾…ã'ñcr,ØÔ¤ WùclÉ2wP˜sÈ?>§i’‚×EU¡Q–bê,·<2Y©0—>Ë ^¸?`IàzIŠ5†ö_C õ$-ÌI+7•B¬Z;‰"Iåÿ`Ý­ÙlpòÖà/åm»¶Ûv\7Zè@‹©ò\ú^íòk»šã™†^G™¿iÓ©Úñ´}ßC€]ªç ݬ%S*g<6ðHöãž-§F”=‹·wrs“«8GÓ x6né³$µ˜šîïÚé?U 2ƒã,êîçŽùÇ™ ³F™…·5iÉu$¾’,KÂ,÷åù¦SÔb¸¦è ²˜Uazáþ<r¨0•þ„ÃJÐ%?,<õ ‡ÓÚ¦†€ËH~)ë6‹Ùø‘‰cÒÞ_n›“ò»y̪͘ú)Þ˜á|-žÝ›Ô Ê…#†ŽS-êŸø¬E÷·¸í;ù»¢I@@WlIà•L*nÎ3!£»*l)¹qUþZ:@ Ž«8‹ÊZåµ­Íkæ™@E2pÕšg‰[0svJ uøý\…=‡|Xµ˜ÿŽã<ьɱ$È2Ä!MA ú«`Ó*ó>W–üñ¾ŒÓH2Ú)" ˆ²5Gh½ØôÂc’<o_¬wù¨-Òý&c–Â#šh¸^iïDFæß̪åI™8Ì'J`qoîÜÔ/æÍ•GýuÊPªÌ¯Žuåþ;ûG$|”Ï“”‰í’ÕªòO¡ÔaKlr±!.F=¬é(§ýƒ”[•µk¿)ÍÉB‹ãEUIÀsQ]Å+DYg{-kO À®âÐ}Çy‰ÞµjÆ„Ö t¾öG±¹ñiq¢X•Ú®YPèµ”láSj@9”æZØ<,±“-M‹ëG»Ðýè²u^1®Ò×ý9ZsHš¿'†›·Ëü0Q·™ú'I²Ãõ¶NF7oâãEË—ÌC£woCÃbQôÊ}6îô¸^Î_“V  ª‰òj̽Ԉ²C Ý„€pW¶ÕU\pkrBÞN”›ïG¹Þ,ÙŽ Æa|„¾< ˜'ñi¢!СPËAyÃÐêzŒ(ƒPü=h1bƒ|?Ï“¶I–C 5\…@Ò‚¬Ÿ›Éò#ÒëeMS±èHùmÞÜÔ›òHb3»‰Ÿ¿ßçòB<ËW^š2 ß§ßÉcøª<¿mç÷ö}m«­ €å—ÆÈ²êTýn¹íÉPÓ({m/çÅ®âе.äb él´ºŠkq©b'Êdi•é…Å¢Ú´É(ݽ7oö¸ÿØ¡]¨!à*Q†ÆÍ#D9yÁÞKÑHÍ›ÉO¯{ÆU·£¥£!ÀLx¯((qAæÛXjú_x®ø"çtzš’ûTÚŸ± ~Ûr²äè>tð2E ª\¥7}ˆ¿á‘Ô¸ë¦?ßòÔ¼’;–œ‡ü±´ök>7E'a ¼0/ ´8>ˆåÁwÔˆà¡ÍFÙC@ûB6ÂUÜðÁ¯á£¸Ëƒ'õ½ÝÒ²áÁµ¹‡sÒr,¥§ÇUÓuÜR"MUG~ÿ¨AÛÑðA~õû?§c¢Éd­/´=ñ솷¶˜ê¹%´.r*›äÁ²‰z>øìŸ×±»òÖ¦ÛØõ;§R/ê­Ž.:›ŽŸI»¸õíËžu¯h¬Ú¹öŠ@±®sHÎé“U%$TÞc°¥¬$­|ë¾sño/öùGa‡z $,,â÷í?ŸÿM†ŒçLˆoÐH#¯1VFøWaK9¢ÜÉß>gÒj .^qAèÁ›ùÄoÒý¯Â‘¾¤Sz@3ÖÀuÚáFÀ8V V‹9Žöî ¼“¬|çoó¶#:·yj•Å8Îv©B›½5¹X#ʶZè ªß» ™°ò…âk<¨hø ±Ûw­mÉÝ# ›F-H‹5Ê&2=†´¸ñA3¤¬ˆÍÈpj2GKÊ®]£!ÐæÍ{/è¨ñè#:2Ûd1õ`MXxH%*"D ò÷·>Ã-IØáší¥*«VºõŒ‰®«r8ëÚͳæPºXVí Iÿ,êt÷¨ÎÝÅ¢ ®ÍIK­#" ÷·»+û`r‰ýÕHð?C—…ë£B.ep•F£åBq¹ZR^©Ã;õ¬ßÿé¸Édþ{DeØ«/¿<»¸1ijRtª²løÑˆwFQL˜~kõ˜ Û*a‚Ì7 Ÿ0azHßá©=‚QÐîîxÿ[UÒ6x±cýcþÔs³~ÿ “Ñüwý9õÕuªz¹¼%t„ÖËmO‡Qö4â^ÎÏ•®âàªÅQÕÕ¢g Þ7"1Ûþ~,:åeÛ¶¶¡!àCüâÙç'0{ßí˜q1ꈔ8J룄òp«ËdÇ%”qÜÚ½úÊËiTL‹^­f•çñoK‰ Œ"î”þôÜäñͺN‹¤!Ðç+` ¼¦Œ6YŸ-Ž„Çø·—‡ÐŒìí‹6,º¼²Š2óÒ¶ìÂîYùGþZVñø½O=ûó÷_za%b²F¸QÂÜùd·^g/9AªÓ`uª *3eã¾ ¶\Z¶!2.õ¿ý‘9“"¢:½aQÕî|ÿ[VÌ6}Uíúï†úÿ‹®›î1j4š«=Ï´æGoÝ¥ûZ`oÝ‘–o“¸ÊUºÙöVZæõB!˃hýļÂei.š¬>-‚I~sÝ^êI·NE }º³–É-Ò#L|7DÚÇJ˜¸G Ï›iU5Iæf ç¡jM4Z‡Àæ#Fzúûr:]fv“»êiþU¡שá>%:œ4*5ŽÊþC'èÓ•[º=eY~ÿ3xæÝ¿<Ç>õÙÛ‘=Q‡b92Öl2¼ïo$Cp…8Sѹø¼Cg7¥™9RÀݳŸy,(8ô¹žÑQª»ßg Ú^â×®ÿ÷Wîèf6b”6¢àÆÎJäOÞºW{‹ì­hùzW¹Š³¨:›é:ûN›^¨3fè1ÔÂvÒBEÇ¡&¾„€b%É´pxr?ݯIvkùb"ìÍòá‹Ì Ü#omçQm«\ëOº4Lbd<-Ôh(ìüMtÿ²Ò$ùÞ!ô¿›Ã%ɵÓäwìé{§ê‡'Ç*z½ÿßîýõ³³‡¿1üÖ褪S¦šL†Å0%Œ ¨tèìYÔkj§ÛÌ}I’9¿ ;êIä¸,žxÿ›YÆvëÜÄ+ô É¢®ÍG7ÏîmÀMר{O`o‘=•£–o àWq˜±l#Êèô9=:QT¸ÿF4n}­€(§•°È|­ågæMfM2>’êƒ7ŽWüÝO&ã:ÛóȇÖ×r¦ÜBßäówÇ*³Ò†<¨…ÍDàÀ Ýöy1½³ÓÞùꢣO £§/ !¬ž×Ì”ìÑø]ãwŽß=ÿÀaú0g™¼2o±%Xxê8ÌõÔt¾e~¨«¿Kê°ìýZÒ«åôIž~߬kBÂ"þèÉ÷ŸïC¢Ý'ìƒ]GúÞ3÷·W¯pV¯dª=ÞGÀ®â,¤ÚL/`?ä4Q¶¨Ž Œ¨ÿŒ]»ÖÞÊz"­_üb^°¿¿ÿ;½ºEÑÌicmfwÃ’à0<Ö=DyÑÞ*b Kz?Jëæôëk½Xûß¡`£á÷wUÑôÿSö)û³:®¯}ykíÃ|³uÂï^Ï®QõÏÔÔQ‘HM’e*>ôvŒJþJæ ³èfã[´ž÷Q6¥²¢’ɵ3œˆ_†À>}tŽîÙëå^Ý:©ž|ÿ)l{Ž»&ø¡~ýÐ4Ž²È‰àû"ƒÃ~ΰãõYáUÔ r«!ó!¡MFÙ¥óýÞxÿ›QÎvå›ýöŽüxŒN гÖ`Pe®#rWfÖîk·Ý »ŠÃtÒÿÈb[Ì–ÇävsBLGµ8¡Q>:bDoä{“ÌCñÓi.á$Zè èîzìéì'.à<ª¹7ŸÐÅÞ4石iËó­ ?ɲ[8Ý20üìYµ&YíÚ€kúîø¬„þ¶±Â¦Eæçç‘‘A É”èÆ ¡Õïb×+o¸uÔíaÁÿ†¢& ZF|J”rp§›»nÜ(–Àtù¼´¹:p:fçÔ^ ͨÑ9F<î5]~õu# ´¼Ä[ï3ÊÛ®£|›o§S¬y® ˜Ùt›ñóGGâæ=ªUÖšÈvý¸5}sì*ÎKQ®‡_ã>¶ý&6tªŸMt³5ÊUªñQ´`">4Ñ?ÄmÙ±§‰¬´ÓžB@|0ý&óbð“ì©|käÓßaB_ ‰2»„Û|ÄúÚb”œnIq½ö¯Æh;íÙà‰zÓ?-¦]'mÍ> „ùó["èáÁÄÏ“;…ßE~'‡>…pª,˜ÇZ@–gAéc]¾ûÊâÅfD\/ËbQåv#¡xïqžíŸƒ»ÆôšàÍ÷¿‘r¶ûSÜÞIåûÞžÐ×J”eý‡„‡_4¢ÜÁ:®âÈòPs‹gñs0½P›7™ïĤI¡ª…Ä™ÈGÕkÚäæ®ÅóòƒÙWÜcßžÞx¢¼ÿœëL/¾È±{ºøYœ?u s3»ñxZž.E`?lEïø¢X¸~3TÛ˳‹ÇFÓ"h‘=åVßÅðà@5XUÇTZ,XØ]%ƒjy#aÇîëÞ°j3¿°(Ô¢ÌJC&_B£ìÃyyëý¯{?çÈ mòÄ~b¡¾{Qÿh“I§ë‹]®+)z=–ߨ&>Š€ƒ«8h†<Qj}M;+tYkß–‰ñ×vÓÜ—?¥Ù >¡Gÿö_zù£•tê\qk³tÙõ›vï§çÿ½ŒéCzæ•żj›Si/ÿq'-[—a»&÷Àqª¬lB¶eñ’ÊMĪyúÝeëé‘¿þ—ö}øÍ&dYSž}ýsq.» ézmmý·%‹òšùœ6÷©y7Îíåaô‚ÍÃXBÐüòD>Gá:Ñ) Oà”¦6âÏÕÛu0Wg ¥çû´ÔUœNgæ‡Õ*ªÒ$Q.™>Úd1±O÷ùÐ.Ñÿ‘—k¡†€ À.ÿôxN9ôšð©ä®öæ9óTË4©Ž7ðÕ>»6ùjxºÚÀ¯Öï¢o@”¯;ˆþôÈÍ4çî)ÄšÛùÿ]Acëóu,CK¶« FZôÝVœÐ›žûÕM4㪔ää:÷]9=8}‚ÈžÉÅ›Ÿý@J0ÌŸíÞwzuëL[³œ#Ø-)JvÁ1úâ‡í-¹´ÞkxÉô‡¾)¥Gð;^Ý©âˆ#zúÓÒÛ‰¦‘ÏM½ xà ü÷«ßœ;ÿìÿΞe"\/aâoÔ2?ÉâXL¸#mTøeã´øvæþ÷?48våÄÝØ;ÁŽ¡òJû{Úh‰qÒÕõßP~î|gó\á0â5±Ÿ?± :‚ù›8Æõdo ëDríštݵik©µ%„«8ÓóxeCAÁU\ì¶ë»E¯³ˆéܬ¨ö±¹.²X,6—p¤èþÕ}Õ*áš®èÚa o `ý`ZcoäoËs0‘CÞ™'Í4®/Ï3j™°iéÊûøÚÖ!M£ÉDßoɦqÃ’hâˆ"ñ.‘aôà ãè÷o|Nví§ŸHù;L«·ffžÓ×ëw”‹tÝ„atiZ¼¸†ÍX;vøäYŠî¿·£).¦« ¬!»ùÊ‘´bÓn:_\FéIýèú‰Ã(8Ð>¬Ê¦¯|²ŠîŸ>Žâ{E‹4Wý”Eۡѽ}Ê¥BûËyGw?ŽPRVIyï+š9õ2Jì׃ò %þà«4„ìtöB -üp%ͺy"mعO˜”Ü2i$½úé÷"½—?^ :Ñ9àô6ïɧo7í¡‹¥Ô§[qW½ÿåzÚÍm”íš±ƒ©_ÏKøTÙµïøû‹8~½‰ýÿ’^oýž³VœËqäÔ9 Bº â ¢ËPVÎû› {¨´¼’Æõ¤;§Œ¦)Æ}]F. JèE«6gQ0›0< õ•HGNÑÇ+6‹k~÷Úg”Cwຖkß…Ë·n« J‡¾ûBþõ˜º>Ñ^W-IߕהùžxïÔ™¤)‰m½„ ·à¶¦pÞªjaO ÿåíz„‰—ü‰4Á]yß­Ò¿w4FN nž_– ˜ùð¤µ-µ:Yõ=ÇÏ^¬·þ?+:–‡Ož£nxÇ îg&Y¤ÏÚãWlÂ3SE— N hhmDÉðË7RÞ#^pî˜Þø³áb»¾w¦±|DfNþ3bdës‡9ÒÛEídxdÇöžFíÈ.Ú¯÷sQÚZ2mjWqÈ"7ÇUœÉb±??Mh” GŒ7r¢H_QL~vo2S-Ôð.¶Æ·º1öjiWËÛíài %…ÚxÈh3ß覫ñq:g.” ­qr¬õC-Ó¾$*œºv §c§¬£×£Q˜l‡“ÍžÑQôÑ7›‰?®,ÿýz#ùÓãwL¦˜®è“o7ˤèìÅRúhÅF•O×FëA™:J·.äï§DAÿiO ¤ýzt¥¨ðPzë‹5Рه£ÃC¡Ý„z“‡„Y²@dlŠ°ßš6“‚ÒŠJЉîDÅ Õ%å¤1‘+âß01î½n¬ØfÒùŸå¨O÷KèÞi—S*ˆ)küXXë燲ͼvŒ¸Ö¾7$¬Ef‚‘ß Ø˜lfåUÀ`3MJÏ?t¯GCû’|šmÎ{ìÐú匟ÑQ`þ#0baÜ÷¥Ÿ2 Eg£o.ô¿U[ÑïJL¶¢"BÅ}LD‡¦%²í˜QøD~y³$ó‹p+<¢¬¸#§H2ß_¹€Ô$3©µ½³|®†èô[ä>sSeÇtìß5™€Bvm7x@oÚ;x)Û±=$±ŸÜaCÏGCõLC“úÑS3§Ð`wc`•Öš^|ë0AæÚj¿¤œr1´§,a!u'ö…Bëy±¬¦yk‘‡%õ…Æs ±6ºðèi* äpìD  ¤Kõ§Ã'Î ®Hÿ†$ö¥+F >Æ9 µe$´Ó;@JYØ>úØéó425Ž'ýÒoî»–:,³f믄•ã%ÇÆÐþC'y“ö£ñéIBKÇÃÙ|<¡O7ñ¡ðµ»½aÁÒ¯Ç%8ß]l3Á   û¯'ì¤KSÅqþÇÇïºfŒ¸‡Ñƒ(§è8úü¶Ó¶ Öpç¥!À‡W“׋¶fˆóù âf³Çb(2,‰(ª¬ž˜È6ÆAv×Î!¼°í̵â ¿~ÂP–ÜWt48îpç¡®ã¼ø>z\%£7+dÓŠ9Ô›¹¤”]òÂ!ŸÞAó&„PD ÏR.˜|W9¬#Aú­ò " Þÿhÿ¦\Ø4šžLËU¡ j|&´;«Í/¸³fÄ$P~f¥¡ç£¡úÇ„7¼kÉ¢£;¨oÂꢔ[tBŒæœ>_L7^1œÒ“ûÑ]èøÕ–+G¥ˆK¢ÂD3»àhƒïLCùÔN³¹ûÿÝm%óÿŽ´&M|ê­óææål<ÍôÂYÄÚq|vW>ä;|d®ÂO¯(ÂUÜ3 ݲάӛɪQjÌô¢hĈîf³áv™æïý]nk¡†€"àÑF¸¡ûïÕù¢‚ºP©Šß¡‹fêi'Ï ]Wßñõ‡ìãé“âíšþز0É«-<<Û ÚXGé ‚÷ÿì}|TUöÿ™™ô!„„4z( ¡‹ŠRì(ÖE]w-kùYþ못úW]wÕ-®ëÚ~v]׆ˆ(M!Ò!…Ð[IHŸdÊÿ|ïäN^ÂL25™$ïäóòÞ¼r˹¯|ï¹ß{ Ô‰ƒ «kôbøVQ)påt´´‚z3’Ä T »Ý#½ îó¶ô.ÎÎiÂJÍVí‰ÐÂÖêË„ XÈÞY˜MOüò A¹XÏô2¦tX?Ìí•LÙ(9q†gó— à,ómk]VYÍt‹Þ”·>/©_´uWwÐAuB)¹{ zÇ’5´lývL*™JIƒ)ýc„¾¾ú1Gð—°§oºT\¾‡>è/¾§pÔ2iÖŸ¥ a¡¬§gK³«RÛh¦·óê9ºžž¤»7¤Ê“§àòí£µÆÕô;è:hH.6³LÿòhYî}pS âö 8w¸*ƒO´Z™m^dÝé}Ž2î¡t¦Ù44i?wê ö%xM qäþPœÎ#*GiéúmtŠ;› *A0ºÎ,÷9Ï(ž1)çªk™–±‰)=§¸Â÷o­uTEž£\ÛËGyŽ£ÛùáqgÓ< ð’É—DʾÔ>PVûšÙhopæ*ÃUܳ‰yLΆ‹²d&·A½0›à›¹‰ä¦Ùš[`íéÛHVÝ¥j@Õ@“F±UyM‰än/u (ï9m´iÂÛ…’ÒʼWAiÀ0°Xta}š1ÁÂ[–ûOñPm2ƒ>piëô líÍFËчÈbèV)ÿö?5øhƒŸ¼©ÊØšÜZÆOaPYÏÃÉ›……“úŒ&#­Ëß'(èC’úÑv¦v ü­)%­Ó“¿ÁÞsò„üÙbÐ~Ùq¬Òà.Ké/®GÝ—oÜ!†¯'3tJÆ ˆYÿsóeV.6xÇèx<ÿu-òUþpDÊómm£%¾Þ­§W7×[ïyÞexü‚`êê³dYT^;b™ÎÊÀØ2œÇ<ÁS¶”["SENÞÞ¥g €› :8ú­Å‘ûC^ƒÇ _qß?vûÑþÝ«Ÿ‹Ãq}"Åt«L+ÂÈ„QŸ6ïæ‘ ³ôü}׋wÂ+-çÛú×V>¶ÎooßÇÛš;êW ö‚ö®i:Þ!mמ õÕcOÂv” LN./6i–1‡¢˜ÃƒbÈ&ºZ_7ß^z•Ÿ8çrpI[ç}4jT(0Ý+ÏcÛÓ«¶ÎsqŸ¬3ÖòÀp>ªt_ øÜ=¤µ[Ù½›+²–ùÉR.Ll¶&cx‰—2Í`]Á~Âä¹Ò²*s'émæÃÚŒ ?JYµe àé‚ï‹aâÔø¾‚ºñ[±0²má5àÅQß¶ý¢#…Î1I{²ù˜æ~^IO¯®m’q}z]8ýõ²Ð.’íÕÐÎû_KùÖ+Ì4ʺ}þž}årþ^Ú“É4ŒŠ°aŠ7Q”Yµu´nXŽ+™.‹0<֬شÚTž;€Ž$&ïbæG¾ß”÷8iù¹/–e)­Ÿ\g/y£k¸¬\QÔüŽš?ê|XË´ZLækyÈK¿ë*{)s5Y—4 æ_ýnÁXv(8“í$'‹coÄÍc(.%k¹h÷Í:~`ê,FdƒN÷·»fÍý…­$ßiÐGFWW‰C:¿wͼnuëó¶ÖÕö+®«Á~“VS"ª÷ƒwͺîÖç¹ò›ÝUsüR& RI£^¿ò£¼G©àJcAU°¨¢jÀ 4},·Z9“¸³çŽg·\¯“ÅÚ²©)ô´³i¬)iþMiå—i͹`”ø8®Ü´‹þ”+À3ð½×_,&±Éü0–Íÿ}ã1þò‹2HZ[1ècž(ô‡×¿§ãÚ~ï\y©Ãkp'?gɉìu ÒÈ=mx£€5;0À_XËÀ³”«òröþ/Ö´/xÂÛxö’aK0Ios™¿X±…ý1o¢¿>r³8@wÑê<úšéàlgI²u¹Í}[xâʆI|JÁÄÁOÙHÙ¹AŸÀD<€tø®†õýñ;.g‰X/þy+[Ê·ˆãàHKÚ‰2½ÖÛQðLòäk_²×‹¦sÌhq è:/­¯#øEV &K=:)„®jò~¢<ÖE¶• Û¶EcÞ©8ÐLç×ÂþžO·s£¦/õ¤¿NóÑm¿„°ÂÝýÔsÙÈàí矞†µ*^Ó€æî»{kägêa³VÛŸ)sˆ^o ××i °ªö(iðó7U›kµüœ1¥Út’¹{ÿ*ÉÝùzvö"ÌB1D‚æ¥›Ž¨lWzö,+Fb‚y‰øåcO};|`ÒØGçÏêUÚÍ£‘ýº}眕Kúó‘NYþªÌ4ñÿ*îáð ^g~aOêØ‡+¨­ù·ºñî¢5ôú·ñH“™­ÉaÑmè¥ë·Öç¸ú–j X²=!ÑÿͼoXXf”mWóE̼•'¢s ò3o~#:##yè] ¸âðK¿3Ë8&Êv+«5Ó[ùuôŸzkÀ¤Ì ¿Ê â%ØØ™|:ûÜ¿}¼Œ¶í>°ç“W_zˆËr’—³¼TòËŽ$ò¦Erç&Å™õuÇÅ/ U[R!)ÖÖçž÷EóÒïýîÕÑà ëìç_QF±iïþhÝþˆ ‰{Aº&T¦ƒ{“e'Wy ÛàÔã`KZ?3måcëúÖûàŠpêT©· åÏ £é)¶óƵhÿÝ…%¹ï¿üüÕüÓn»Ë||ïËÓí®U‹²]ÕøÔíÿïL¶èüäþ)gN™‡;Li§k‚{£úTµÜ.ŒøbÖ³»›¢˜þ´;~@lqŸØç‡Lʼ?nDÚ}Ÿýë¯+8˜R$`v;C5U©Xšât´±Éš¼ùX#]9Øñ‰.›Ž6 Œ2àaö¶@2ÎØjK0<¬–­Ïõ@–y´•¯<Ç™µ-Þ/¬æžö†`‚œÀ¥<Èôc·xJq5oø\†T1àx—£é}´9ä-ÈtÍÐz„#ëu ²R+®o]Xr"çòØ2î¥ôæ[7<n\Ò˜…'J\O±s¯´wÈö—¥÷Ùžà·’q=Œc­Ÿ™¶òÁùíÉg;ë­ “—§µ’ÛKË›ÇU ìMíºŸ6L?Ú_ÿþÙ‡ÙéýK½«Î™/ݵ•ËÏØ7 ¹Ÿg—K; ”~¼‹æH¯>´"}L_SdÔ×ó}â?þÛŸÿÉ‚™‹û®ªu¹Ë5®Z`šàß ”(;®”‚“ÍF¶Iñ®¿îD.Ì8,°ã™«gZ5p Xù‘9àï}»Fe¸rû¿˜%¸ÖÖ“ÜØ(8HXVJVœ=9%˜ÒûºÞþÊôºÞ¶ _\„rÌé¼ê²@¹ëéÞ~‰kx´ ÞW¤ü23Ðgß.=õÉ‘mãËk’õä‚G$¿<ìÄQóì¹Z&ë«b_܉ Û6®Ò-9Ö¼'.áù[zL÷é«/c!fºÀÆ¢R1ì«O=⃘¡HËœXˆe ’Qý\݃ ‰E×4€NÆ —Mpíâ6®5ç³ ‚fº…R†ôÑÑÃìî­À ÊKºå6Hw²fPæ ‚§üC·¬h«:up} 5yž¹„SªÓõ7§2uÛÐÞþÛßÏÒjuH¾ª`“jÊqPËèLXô5`ù™k~yÏE￵´ér f3›ƒiª§©è, Œ`qðq[ÃþoU™èh¥‰"Úçé‚—,}“¢ìÊ&U5_Ïhm»pžþSOð ””(-=ÀyÎÀ¶i4Êkºó¶Y£ÝCf‹Ž4ÒU\w®p¨ÛYŽÀ÷Â%ÜCƒ8œ¼ï\ʾÙ6ÚÙ³o ò|'šé°$ûf1}»T¬7Íé°³&6îï Þ|øð~Lü@K˯‹oWE-]ÖÓ”)«¿ûS¶X“7²UÙëËþ3FÂdHÿp-õ Q_#mtÝÿxq} ^c_Èðh¡´ñ}ã‚èÚ¡]"`ˆ²è^ÝÖj̇¤WžO™ìÕÌÔÄÒÀ¹ÍzŒ|(£…:”@Ÿ¤¾9;Xád˱®ozʃ&­6Ž9É:•ná€Ölœ½]¶+_kÖéb'ΙsŸ0³`¦ƒj¡g%¨Ò5401±Ù¦¡ô‹ÜVé•´‹ žÈ§J×Õ{È£¯8XÈìOÎÑcvZ ’ÑzêÂ`Zö‹ºn˜ ’[·²Ž4å>¶(§ÈmuÝ98Zi¤Ïw5ûü~tbHçĉ\U ì„²:èT´‰Ožy0•½[€s«Šë€þ ÇˆÈÈ;9ÄëÅôp õÞg%¨Ò540=©y}=‡£7µ=ÙÁ‘ü¤(—È}êÚ÷5€·éiÆÇ"XHɹæ°È -ûB¦•ó#éVÒàÏÞ T9_š(ó!ë^&Ѽ`A—|÷#h ܱuuùçæf—…cy¤ì"¾Ý}­Ž]ò†ñ5%z°<š<ï®dzGÿXv§¾ù< \èQãç×çÂYWãäàûIµ*{@¯j§dæbÔ2WyGZkO•7T‡òð¦*]Gðý{¸ø£ úÓºZ:UÝÜ1ŠÔÐã‚éÇùtט .é¹#[bôǧjØ[ËiäÉ`3`ûöw,1Æ;²È ¾¸øËÇ´¿/#ŸÊž”}gôÝþfkòÿ›ŒA^ß(ûV  ~)o˜ÙO²o•®‹–z„>câ¦qðd€J¿`%¨Òu40=¹Ùªœ}¨ýØažô'e@¤úª—ºðåuy™þ±©Ž¦xN¬ñ[ (¿e`±úöHº|…¨v©›vײÒ/ÍÉížïc' ´ù6ÝžÛ›¶ì,özéàëûͯV ¿ßžÌìÏÜé“r ûLî*”0•¸&[Í7ÖøšéØšœ„ˆ{=<˜ˆÇZ~–YŸfÿ€ÀxNTZ”áÀúnFËQMHÕ€ç5p1XÞ/°ø]Å!‰Ÿ¾È~²?Ã3Ë!’ã‰^ªø®à¹mûs7åLYÚxn»;9šÞõÃ(3;UqZlI>ÊÇ…³ß.%ûs°š3e4}¡âož5ÑuïÈÉ2â}wñqÄO§¥Ä~Ñõq:áêGN%„•¿yÖ$Ježâpó×ÏOK7l£òÊÊšLW³¿oD¾|í¿? kòßÿ³œ½Qhé…®w[_‹ö6Ц¦ÀI¸æà7]EÔ·§oµÚÃO£ÕÄ…ÕתmãÁ¶a}jtþ~}9Ip”ašSyÊÔ¯š”÷50†G€— °ÚË^-ì‰r²W|„&Äžž:{Á)=º¼†f|tŽ>b.²$§öÒÑŸg„Òræ ß22PÉn5–F1ÙGÓÇ­¤:ábX‘GJ¤i °Æ@; û-òmvžÈ/>8†§ZX%wÍ*~üýzý¡[fR|L/úlÙFy=WMŸ.]OF¤ÑÕSÇÐÚ­ûÙj}Dåqé)â¼k§gÑWM±^ãêFFþ²¾Ùš|GF ìÝuè`*sµå=LXtZ&,ÐhÀ¶*Òô©Óê0½V‚dI½Põì!«ÉxW˜«uѰ†,²êNÚ%PÙu>H¶kÓ½öƒ\¼ÝðUÝÌËÒÂk˜qÔtxŒŽ^FKn‰ «‡jDv¿ýèŽ2R2kLÑî§Øq)TÕÔÓžƒÇ(chbƒáÚ²«ÈZ€}%§hpR?zzЀXª­o`·¦NOÅÇNÓ”Œ!HG $XŸÏVTY¯Í’D—ŒNS2Ó€¸hÚS|LXª™âIŽëCƒô³žïê@² .ðŒï:ÖdÔY¥^¸ÚòÞ¹{ùUÁ›Wôk†^qÏ9`Q;ЬUºŽ¦¥øY'ÃüTÜH÷µýÁ9Ò‚Ÿ¬e_haP+>Ý©§ÒVABP¾I ~‚b1EÑò…rw“24[”M]Ë¢œ»ç ™Øô’5´lývA“¨¬­Ôˆ @Îq´:wi4Z“¿Ob´Ù¾C'ÅâÃïÖY›0$(Ž–VPïÈ0±/‰°”ð Ò7¶?IXžïè~ß¿Ý×Ü©fjH—›€ªeG[Ûûçĉ…ùC*Pö‚¾™–½JlÕ7ïkž1ã…|Õ$U xJSDù³‰îávŸ6 º¶,ÆGnĈâç©ò©éœ¯¢2#}´½žÁB#é -_5à_ÅVãù£ip´Ú¡9_{Ú£3ŸQÄcmF‡Jޛɀv‘Ü¿ K±Ð*À/^¾qåï=D“G¢Y“GÑ<ñÞ0.8‚FLÅ ´À»‡o¹LX‹•eDïB@ÜïϬ®³f}Å êŠAïjɪuÃA Hðæàéêi.hVd©g¬UQ5Ðe4Êž¦&ùÓÅ Í·eU>¬ÊªÇ‹Žo^¸¹†g’ÿìÐÓ†#ç{(‰ ÕÒÍ#é¦ô@ꬾ†¼ßB:¶([9ý](Ÿ.¯¤ƒLŸÀÄ<Ð$¤<~†rv@yUΘЗ.ÌB:ò÷³t¸RãûR[‰X¿æÍGá¡!´ÿðIY„>¥@IDATæ9·?—ÿ`¡Æù¡AÌs¿eþŽ®bý`~Å“S|?¸ˆ­º©@Ù–V:wŸúÖô®þUì]ýª©{Ys5åئ_H(J\˜Ê0òr“X“?^eô¾ÞÓ@¥5ç;ÔIï«£ÛFÑœìMÀz™ºáe ˜Hc%æòx­…wàå<=‘üÃþbŸ2=L¶û”½Yœ«®¥ø¾½hÑê<1Ïh2 p `=01–î¹n}Ì^2þÀ. Àé÷ÎU&es;48¦e ¥/Vl¡ÿ.ßD}äf–mžlgç~ö™üîV‹—œòØä êÒ5á ”í4²º»} èû“ÿ¸Lv°f&ýŠÕd®mbiÿêÎ8C¥´t†ÖÕ<=«øSñ¯G0¬'þCZ Û×)¨†ÁÍóÿ<[55¡XWl /9ÄôÚÃy<.Lœ‘ê/r{.Q¥ã5ÀôÒžÌa¬áù¨KÈåìKk™’1˜'é fp|˜Våì¦?±û6ðŽõ ôÖ×Ù´¾à€Ê˜ˆ÷ÇßÌ¥:½e*8Púb×Л¸£E²÷ÝpI‹ß7Μ@×^œÅû4ba‹ƒíü5ìñk¬“TÇõ÷áÕÛ¹Ìg«}Zßi·ºZÁwÞLQïýÃáÚøN'Mˆí‰@Ž$‚k#_ÿ3ùFº8öËXßmÇ‘ë>‡‡‚︉zÿðòKâôåm\à–ÞÛHW=¤jÀ+b¬uqr3úýaóD™a½‚ ä§ÞâR/ž\cëñ«MÁA\ZCkZdÐ+îÉ !¦áÅBÉžÔ¾si™4&ëЧªH´è\">z¶‰­ÈåUµÂ›ÅžƒÇéÓ2˜D)ÈÍ Yy¤ímp˜áiÃYyycÕ}%xøÏNïš” Yoµ{+5ák|Ñxñ²å“oþ°§¥ÊGþ—ŒµÇ\ª¹æHöáGÕùƒäæá—kç"MX(…=þ éúǶs¦C‡›tlå);t‘z’ªW4€I3râ ®Wn»’ž¼fvšŽ–ì³ í¿¿žþg|ËomÛÏx¤¨5‘fÏp¥`[ù[îïjk¸v[ÍÁ^`=^w¸ñ<ë1jŒÉJ7¤Ðô‹k7´9ÀŒOÝ2=_^+Û¼3ï?­cnâŠsk_Ö™3e=x<[Iß¯ÝÆ~‘«(*>fŸàR¿ ˜R¢º¶MVʲ5»Óš#éD½ûwª}ç ¾éÒô‰¦Æu›©öÃÏy>ƒ‘Âÿø¸°&‡¿ø{"vSñˇHÃ=Î{æSÀ¤ñdæHvúÅ˨î«%B+!ÞI¦ÂäaËqЬ‹©îã¯(èšYâXä!ýk¨î‹o)ô‘{È?c MÔ°9êÞýÌJÇœqÝp5i{G‘±èU½ðw2WVöß| QDò·SÍ¿Þ#sUu‹ÖЄ…p:µTýÜ_)âß/µ8æÌÞu5_MI~!ÂO»+T«+Ôúmõˆ(˜³ys¥3é¨çª°§€ ŒàÒéð‰³ìõ8•qä«s5µ<4Êœ`V$‘Uk 7âÇå¹÷têß L•$N¦·>÷½B{eöÊþ¦¢`ÀË(~†{G†Rzj<%ñ þÎM®ÔwÛI#-Þ¯§xò¤ô«LÖãë†Ðõ<9¯îó}Pr¬”v±_Ú²s5TÁ\Ò·6½uC™J7Ûnº8PO$CûGû³¿^-£:ò0›Öaî²uê…#wÄÌI#‹/Üþ~Uµ(3RD°ëŽ.º¡å.Úpí[C¡þšê>_$h¡÷ÝA{PÚMÔ½žüF ¥Ú÷>#S)Of d ¾qÍßÞ M¯H }øÒ¯Ï!Ó‰S¤Œ¤€ùד¹¼œêþ³wì&ML4_w9Uÿõ 2-€Ûxà éø‘4~þú‡‡É¸·˜ô+³ÉoØ` }ô^Òÿ¼ÞßD~ñýHÖöî%ö×q9Sè}¿¤ÀÙSý‹[TÏtò4U¿øOÒÆ¹gQÖšÌþ‘:í¸?ÿq½xfp˜NGA u梬Ñûù»¶…ßÝ[8ã-))ƒ 4_~i}±¶(ŒúCÕ€ Hpl4šH϶«ìü½´Žy‚çªë…QxXzÐӨؖC¢ Ñ­%Ëè§<¦ü¿~ÇK‰Â‹ˆL8ðÂ$?š7—VÎîcQö¦ÎœMÍýÛ•ÍEúñ$â.vÞélþÞ<_ÊÞÔn'§Ý°1‡ê-¥¼ld¢†UëÈP\"öØX¨þã3Ù ¼• GOñ‚ýŒg«òwâ\ èÏýêY2óÇb:~ãÈdعWüÆ¿úo~àZÒÆö%ÓÁ#äÏù(\<…Ì55TóÒëâé, y¨ëן2ÖÎ- Î³5P½ôn W¤ç!lŸlŠŠö7eeð±5ŒrVE„E®í›ÝÒÌí¥ò¨Év= `X¹‘Gb`Aüú§<¶ ÖÑŽ–5{òšë?°ëi¢ícBÑÞC§hë¾#ôíÏùa/Ýç(`ƒûQ0l=ÅW­c‚TvI-/j ŸÙr °lK’Ù}¨W2÷ØWV¸êëE™AµATqM²ý¿æ‘…4„] 1 a‡ ”-ªpûÿ‹ëêhÓÑæ‡êÅ‹C©o7Œªe·oßH” +m‚_âesniQ°ªg_iñ»æ©öýÿŠ}2XHÕ³¾‘5ìÊÇ\É?›øÂÕÏÿ½Åµø¡_¶J,ò€­Ð P Éyú8š7> š·?&-ûy4±¿G™výçßmT™Ø² wuö8Z×ËÞ¹žÜŸ¸qc§·°i¡Â±cGh̆+™¢q•FP5HÌXà0La—b©0Ô¿É y‰Y£y/튫—k,°t¹ù *]WÜÆbâ8Éu<±uÝÖTY«§[fMPé.6+,‹×LMo.\C«rwӬɣPrÕFY-sŽ™V±Šý¹‚^š…-Ií¥£™iþ4“rëȆ¶Î—ûä=€²"Ð8É®eéõôµ²ý³™çÙÄf Ž«÷@kšM&ŒZDCÍþËä>uí´àKüS1 k¹ô¡‰ìÍDÉé}ø(ûpãx»h ·È|e¶øº*çdeBÜ¥7UØNÛÞ~åå¾²=07w'—Ë‹Å&ÄjõW™4t »Ï¸ˆ?ÉM ™‚¸7À>õÌ×/^tˆAóÛ¿À÷R7o>å+õPËá¼,Öd /¹†;vùŽ ïêÄ=çu©¼úƒ§ˆµûybßp¶(kœ¢`ì;c¤ÕL§X}¨¶³Ïc{2°·Žf¥08ö'l»"ò@YQfµí]ÑbËkdûoÚYÌ®ãñÄ>p•»Z¦Øò‡ðŠ•ÞÙ¸¡¾ƒ[ªÇé_˜¼÷ÜÏuÖë0s/G¡ì®¢åîÚ²j½:DMÀ÷Îì#“FÇ76hn6‘ù~)gʰå9™ÿÉlÐ?Ë|æoxNÐ)¹Ùò¸ºî–ÄvWÏe;M5õ ‹Cרo—1ö­8)ô:p@¿6'u509C¾˜ˆ—Í‹äHÚªáàh¶30@†Ù‘÷Ú¾²ºžæLNt'9õZ…Dû—œ¤C¬ÛAÉýEGÉSûØ€kÍJ£éy$6w‘gÏUS<ÀÇ6Äöø†µt>¿go÷i#¥7õGC8ÎaÜ)?úëÇç=Pl( ÁfúœRØ£eßj$~F›YüóV6BÓ³†r°¡abÒ¦òù–×øâZʾÓ*VÐf4kõ:?üny7úNY»\Iô:³Á`h+j®UïÍ»ÜßJÞÄ¡¹ˆ3ÏžýTñ©×sßç^ÆSdÊleË¿¿(*>PT”•ù×€€ ˜ m«|òuÝÉÀ»‘éC° ÁÛ"Ž…±§ø‡uDþüÑrÊ”@³&¥·{º˜)en™Ûî<¡ðèiJèÛ‹‚8ô0då–Ý‚—=ÎDær~RÐ#@,pÐsErOj8ÞpÄ@G8Rž=‰ ÒÒ…‰~ Žýé"véèW§¼PF”ÕѶ·Wn{ûܲ‡VnÆ+¤¥ Mž½çª–;;à:+6í¦Þ¡^ÊÐ%ž§òªñŒ2UŸ9OLêãâÇJ5ñá‹ò®¢£ôéE¸zPrðœ«g,ZW›Y”O?3Á†ÒÙÊì©QÙ¾ÞX«@ÙZu3MS£±´:(DÊnêQyyuP°¹±‚cpw°h–.Õs–Ÿb)7:Ý`ÒÜÏÛwð‡ÇâhÒlNcÐüo½¾îYjòr``ð¿TÀÜÁä`v&G¢lÊzŽ2WÃK‡¥ö†ì?|Š–mÜÙá@ýãï7²ãéÔdA¾ñÒ±6^Œ0ß;WÓü…çhožt„±- QLçÉCÓgöcN«w°q‹Ìå=p®¦N„$oqЃ?Ƨ§þ3’ü÷WÙtaÆ@)"v@%=Xg“ÂóTÉþõÑų}{B˜£œ 9F/P/àKûëŸr-gæªgZ·:A¶‚ ]=5C›Ñé8¢¯#3¶.³¿U ì¬Æ¼¾¹Q¯?^¦©ç¡§ æCªâž ÇÚÀ@M]mÍN o^¹¸—°“W'ålÛÅ—ÜwâÂÌgjk͘‰A³ÙmIÆÃë—0?Ì€ùÙ´ˆ¨÷4ÙÙö]€8™·zº{€EM»óÈȘ‚a¢ÐP×^¡»ž Û‹ixJ?ú9ÿ²¥pÒ¨4š8"…1·ñ›U[ÿùÏ.£!béZŽ^WZVE_s`‹§ÏQtT(]3-“’úõËÝ}ˆ¶ì:D'Ξ£Ð vËxê’±C)4$rxÿ„©„ÀÃØB=û‚ôú8pä4œˆËÐ_ìÐ|ï» Tß` w­a…Ÿ¸}-þy»°ì]wÉ‘BPÿÄÃËàè&Æö¢¹gRtd˜ÐÏ_Øj~Å”Qì½b¯°¸d‹Ñ¬Éél¶x•-È'JOÕÓ.¶Økt"¾¼¿æ“Bü5"*Þ”D€c?Jˆpo¬Ìבmå=ÐÀ:BY½%¡A„¢á¿(²†®!ÿ]žC!Á%ð¢ÑâwÁ¾ÃlõßKÍ¿¬Ý6hë¾CbLønÍvÚÉÜZ?ög}ÁèbrȨþA§µÜ Á3†Q©s·‡èÍ4D? @W(·Ý]£|?qTƯ~ÌUÎ8¨LLÜ”Á†à?£ÞŽÌè`Ñìžæ9ò–Ý,ÔNh@¸SG¯ã MQL'.UOµ§èúŽòõñµá´ù×QôÖa4t`‡ƒdYvy u–Tr[ÔÖ5GJÅ=>¬”¶Ú ­û×oæN8Â7ÌG—NN?¬ß!¨02íŽXC·xư@ßîʶkcûòÃÚé0g¸zôÂcGÝM×£œ [|ýSž5Øê&Ð1ÍBOЃ@=ßq°™í‹NhïÎ|¶ì•^Êö4ÓñûñæKö’…;L†Æ³»ãtÞÛ¸ãëﵡGcƒþÜŽœ°$€èˆÅªo¯eÜNÂýóòjæmýgjDÔ@v*w/¿Æ7_bÈ_‰ÏŠÇfæ7fVó~u«34€—·eᛇé &X»–2ãüþMÛ¼‘LQBlóqP?ìÕÉ^~qÙëBžâƒ_§æ{À^é;~¿-`Ñ^غïŒlÍ+fèmcÿàRNsG¨CMSwó6MV Ì\žógHºtï°ÞßËœdÕ’ì‚ù’Áfr÷Ð¥“F°ÕŸÇXyñ%β¾Š\Sx7¸JZ8â k¾]øÙÜ_ßsóŠô1½nÛ¸Jëo´ýíõöZu~Äú3™ôúÊõ?,^Áa¼‹ËRç^+ƒ3 7Mâ{©(+ë-ÆòiÈü00Ô’†ù*³žfÉx!%mÐ+ìR®yìÕ™LÔsÝÒ€üxcí ñçÀ Ž8Ì»˜vÚDkÍ¢øØñá¹óê (9®‰úÞt"ø¦RMðÄ™sô»Ûf Ïo|ý³<ÔîºOd(“*AGÙd c@,%ÀÁ:Éóm­=¥_[i»»¯³Ë¦c¡Wt¨ŽŸ­<¯Jíµ­ûÔ?^&J%Po”Ò‘u/edÔÓ³V†!˜Û@º€ÅQÕ€3ʻĵmk°žg1…'­j›@²Ì®¥êÙ«:y0˳•é©Á ×PVVZµoǶ—΄GÐÒ‘c=óUî r¦ Ðô—¿iíGµµU0‹€h‰E‚eŸ°(·®SZ^Þ¹ùOiüƒÒ¸ký“”Eûó :„$_(.Þ¿½h\æ%­¯Sw ôí&¬Uàž‚v1 _´°:cè Sì-±Ç{Ãö";l€ Côâ‰_m‰pkÇ |À“VÒ&úð¤<| ŠŸÜçÖipR,ÁãúmE‚»º†'"‚Šò©Ò1°À„ÏSg«# û›îOäžžÖŸ0Y³ˆ]ÂÂ|’A8\áuq™,˯1kóå¶«kX“Ï<9¨â¾ ÇêZ=9Ezž(Éœ¶FJÜÏɵÜÊü¯â%̵ìÕ«Zi@R/äê×.]¼îØÁÂ÷öÄ%hgL4ÃBªJû€ž /è­h÷öE[×±§sžóÔ´Ôóú…åÞ'2—K¢þ Ì+¸[«ñ›Äß­/xÆECx8ñÇ¢1£?+œ<™'ªt’˜5ádÖœoÎê¤âx3Ûրћy!íá©qÌÝ‹¤?XÆ“çr…åwþì tŠ=_ÀóÓo~ËÞ ¶ §‘#Ø€Sü-ÿ~ýËlúÓûK韟¯@ÇV9'L£žö¿o-f߸{ØõØ ëi!A4idªð|ðÇw¿çVË‘¬ÁìãJö¶»^úh?SA·Ìš`õ¹lMÈC­÷¶Ší+eÉ`nx¿èúûg+é?Ër˜ žÜV±:v&–2¯üo×Óï__Dm¨¨Â«³óĽo›Ê$á„û&sHð&wj‚²À𼫸(w(»~°¡uÛ éSµ:[d°™}™QOýÔÂS÷ ¡ÛÈKc²:[ÑÝ$Üpð ‡·SÍÿùð³Y7Í÷£´Á· 7_ºk«Vå,³fì8É [À’\´kû·«}¹ŒO­áf¬¡Wèzö§ Ò–¤äåm6Ï›7þàÁý¿áÉ(ÏóËZ̸âÂßDõµ—›q‡Äþ¶­4¼t,ŽÓÍõRÚ>—,>’Í‹óÅ{‚=RHw éaûà‰â/Ε‡Eà…n˜&¬ºðt%.Àê›BáJ—k_²7 X‘A¹€{­²Êzå“•tàp©ÈC™ÒIîMß>SxØAE°_ÊÕSGóÐ;‚¢h•ã¶Ë[™Â^8°Ô³Ç Y6\ K´²ØwÇ“°rZ¤ž¾ÐËtF¹þtÿ5-j… ÷0/ô é¢nÆøaâœöÚ ½û\ùÛ¹ÍRpŸ…°«ABHë¶µìõΩg¬ÝCMàë›pŽfçØ/‹Ï¹“Êog™·íNÀ™¥v2uc¿˜ðz»WTÊ_>\.žá;¯šlõ¥­<®Üv70F„&°KJ¼Ú“ÖˆÚ;ß™ãÍÁfj QOƒø¾óddFgÊbë\·2'ZÂùØß¼øb¯7ž|²ÜV&ê>‡5 ²°(óUwËþûñçã¦_zbTÖ„ßügâ´¨Ô3§ÌÃÖ¤>®úYfÁO2\ÀÁ»&î™ôõU¹«V|¶mãÚ‚&‚H ]bQZ”Ý{ sb%Ma®ÿUÁA‘~~þ|Ç÷(°Ì„½þô©suÕÕe%û÷îÝ¿cë!Ö h°,$W4mƒv!ùÉ]ÆšÌe>OÒr¶þxpZÆHS½ÎÏÞ-8kІ ?e~)5uð3ÞôŒ!žw Åó^áÔªDm“QÚ:4c53U>¬í—襧ú QD¦˜‚ƒ‚W¹S\J,°(cí®ðÄlÆsv°+P.qã¶c-.òÀ<ç&³y~ó@rjªZh`D_?úûU‘™0Œ‚ƒƒ8J³k»'vð$ð'+«üèÝÅžùtvpzTvmãüe ŸvSú—rÐM‘Ô Oe£ÁD#Óã¼æs8úÑÂ5Bˆ§ôïÓ¢”{àtäÀþbÿC¿ƒéUÓÆ‰N±/uep È°`1‡t¬aÉq†~sýTªfNóóïþ .“–à2Ž&Ï áÙ¤š¸gµ•LÏ•5t +2ÚMêÚ•t<}ªw;Ý·Ÿz…FK¿åêÍãéç°<¹¨šžrI)À € †Üáüð /¥¼`‚ -aQnË}Ýi­¬Ÿ¬;ô}@/Ðô}Aoèl@Ý$s½„ sš–»íïZ­æî%A°|ðl Ó«EYŸpˆì`±Ï…xžñ\ãùÆsŽçÝ…dÔK:@r…+'{â+¾Sí•OÝïžN—WS)ûÛ¶%’ñSÎ^ÄÆÖñìlýUü®²˜K5šY‹zªnpžð‰sv§Ôø˜ó’NO‰T<·x¶á⇇´Ld`kl5-¢) ?³ :)ð½ ^ñZžø‹@Fk T)Î"jd¯/öò‘鹺–ÙÓzvµ<¸Î#eY€·Ÿ{êw?ý<—é¡t0sÿGå,Kí¸µ]O'Ö°ŒÂB*­È \`Yv|º³5™«)D¾­¤nðÄË:’Ûò¸åªð?5gë¦bŒaêÅ×üNŸ€*óKçÖ}݈âÉ™W§nØZâŒ'™é|MºF«yϹ3׫纮Ì,Gðw€(Œý‹âèx×_šÑ!šÈ>’ÁM–2“ÓYüó6*8pDÜJn3}¾2W"B#":Ãô ˆ2Ñ·|ý3¿¾¢Í|d~ÝeíQ  ¥°üÔs»yó6 ÿ|÷SÏ/ã†ýT¢ý^õ³ìÖm`ð'Á2¬¤Æ’— p,²\ó®n+(c-èF.r_·U@[TŒyó.**:À×|7Îe Ëhs=m(ž0fvêæüím]?Épæ[1qœd>ÎÛÏ©–ä¶ôæ­c·ÌO½#CxÂO¹ˆÈ7(±¯Ýð¹à9¹+Ö&HöVùÔt½§¸ñº`ô@Z–nØEË7îAAìåxã¥c-oD{'tóý[¯Hd47Loª¦ÁÏð®'ªìI '¼GH ð×Ñs÷^-Š >­ƒ»Lç‰{XæþËá¡Æ“][&ºdÜPšš9X€nøÃVÊøáÉ4vh’ðjÑÚ½³ˆÚÊG™§»ÛžÔ»«eñ8PFA0,»`ÁûCŽ5{€?αÓïÍ5&ÓÝxþ”Yc>Î(ÎÒMqµÔêu-4Àúµþf}÷,ê«áH7²âlé”›êºI¤2dM(Þ¿ïÂ};iÍf~kšûëÍš¼—ïºoçØ8¸Îk!¬Ð0޶ÙÁDØ­å®×Qx·ÀÄ=•“ÜBUú£AháÄØÞÀVhXŸuÌ™ìß'ŠÃ÷_¯Î§|N4O ºfZ&%õëÝ¡õV3ƒ»¯ ¶$GŠåÈ©2Ê݃>+?Éü€þ…-ΰÿ°n{4¨¡'î˜ÅÃí‚çyÝ%cÄy+7ï¡;ŠÙ-˜¿ˆÂ&v6ýËå!ÿ-»щ³ç(”G+`j¹dìP—žL¶|÷Ú €£L϶ Ô€ùRòƒ°8cÉÑ–®Ü($tÞ™À Ô [Òº]À}¶'™ ÔÚ‡~Î"j+{ù··ŸU,tÜÙºV–Ó¾¶”g¹°ÝôQ}…/}å7O¿˜Å3Pgòv/ýy±øá UÜ×€$*Á£û)«)t lN|òlXxÝ[·Œ06úù ~WçoõÓð̽[“R[“YOpóqÁDT?ÉÍw†<›—æý±…@g˜{zèd!ØAæÐ"[ðwn>Fá ¦fLÊœÅpõÀYö"Ûòº|ãN åkŸøål¸.>všn=^¤ ·UAAþô«k¦Ð&Z‹8PÁC7]ÒÕµê¹C2s"ÙþN\âö©è°ìâ‰^à#ÅäQ©Ö4ÑÎ,Þ@I‚xvzŠØ{B‚¹g*èÇ-{D¨äT¾Š‡ã¥ÔÕ7ÐBöÉ{ë¬ñ”Ƞ׿Ȧ)ýH¶ç».É:B¤ž±vF<80ðÜÁª_2œ—éHó†3׫窰¥¯eefMÛ<å>u[Õ€ªŽÕÀܱcy¼O³Œûëñ°._¶+Øe»·¾–WðjÇ–¤ëåf냭e£ &×t„dçíÙ ¢VæD꣈ºàü»Û¦ÚŒ†I;mùNET°k§eˆ © ¶v³VW¬–AT ƒrX–Â(kX½óÍZÁ—Uº”òtÝQ—¯Ú–Þ=§£é)Ëë{Gµý#¥t„Û¢Š=ÀÝ׿¨+ ÷³ÅTån± ÿ¹°6^wq¦øÝÀ“°rÐ È!•€çÐx²¼ûÇÕóqH[¾{Å ^ú‡ö÷oEPê½½l+Vߎ„>MçíÏ\rò'›ÄßöR«Ph C€²"?uSÕ€ªNÒÀÀÜÜÇÆ›TglXÎ6 TþQ”•—–·õ‰N*V—Í6ˆÁReU}‡”ÿWWOÔ ß7®ѹ¤å¡p[ó e¡Úó ´~{‘°ànѾ0èg u «ä+›í˜Lv‚ƒx(Ã’9 ¯ÅÏ«¬ƒ/®C|.=¹ä•¢^0:MLæ0_²f;½µp-=s×VžêPv/fOÐþý™²#ô) ñ3ãûu;™¾IŸåÉ`ˆÃÎúî•iº»®¬®§˜×&Z¬É•OÈ‘i}A¶üénÑÔë{°T Üƒ_­zÏÓ@|NΑ’)S¦꪿c $¾Šf³éñ¢1iùö<8WcX·äÄ<À•uT§o œKɵ³®<á’eÖly¶Ûó:}ÜúhÉ&‚eù"ž4”£~A9Ì-ä.¦]Ä÷mWb§—þAÕù."¤¯Ð±—²q;Y´sEQÖŽl{t†Š·0¯ø$sŠá™âßFäB´Þ> §ç˜ð]ÁE.æ¶Ç„²;¯¾€Ðé‚8ã»W¦çîZ´}=%Ç„ZŸ1gÒ¬w÷ºEGÛ[âÝzBÐ':R×mÕ­3ŽÉr¡ŒÕöUìé“.w2-/˜c¢›æ“–#Ú“ö@ÁÙ£R0b°GÆ@£'ˆ0ÉòX[¾{å9ž^Ëöïß;ŒŸ°æ©#ù|1/!Àd65Š™µÏ§|p¨c†{) zN—Ö€jQîÒͧ^Õ€kHÉή7Ï›w}qñÏøƒ9O¤b6ÿ¶(k´!-oÛ“®¥Ú}¯’ s‹àIBËV¼¨PæÓjx‚Õa»®Ú<¥øE†ÄqÐ ÅOÉHs8é¶|§"½¥ìnl÷ÁdäˆXÇ·Í™ |´ÎgÞëW<¡¾|!}¢Âh{Ò€¼!˜¨“ã¢ÃERçÞÈË•4eyP”eE™Á÷¦äb¾r¯˜2R¸ÐmO’âz ¹_þ”/¬Æ—ŒFûx²'Äh4 «ô·<‰u3±÷$ŒÜ?oG‹³ï»·½<]=]±e;šÝ ⃞¥ÎÛK3¥Îð Ÿ“€ób¢øøÿ³ÄÞjïJß:¾tÃNá¥Õ)ÛãÊ‹FQlïß*h+ ”{Xƒ«ÕU5 5 ùòK£yÚ´[Š+ËuüÉ‹ýüí}¢pL†q`~ÁSòkYnÝN†—Cñ­yû7,‰˜Û ÒðøHžTØ[èú…5Ñ—©ò@™ÏpD<Ô–·}V—i{xP¹ê¢ÑÞnf»é[Ú?W<7ɽ(¾O„x¦ðl9rŒŽî÷¢"ƒ7Æ,)ݨøíÒf„Nççg4ÙºXŽ*xÓv Ûÿòœ„#¥e¶¼FXte§:»îât+µjd…²ŸóyMQCxûÿ­‘;MlfG@š_1µfð€XkuÚò‘®eT”¸.cˆ%¨.´·ßš¨6àC[ÇQt ¤ž±öQ²/´‚ZU> –gÏž[TzbÅOcΜ~T”•u<-/o±S‹€—6œ¿¿¿È¡¡Ìë­×“^¯§C#™Muâ£ËÜ5S3¼ÂYîTx0spRñ‘?U^IƒÙ’82¹/AŸèx@¿rØÝƒYz$)[÷Ênà@;ŠŽ²U¶Rm{4ö]©”-ɉ‘:Ø/‚¸ý¹^Ê }’‡ÄS?šÇ###xr©¹Û?å÷I ’ý쮯næ”g*A›·ýhky2ã…<Ú3ŒÝF†+‹auáˆìœ—‚ðór»R¤AùQ>Aµ²å#ý’qC©ž-ןp‡aÓa¥ÃÞ~™¦'Ö Ž„ùŸŒ•z÷D>®¤¡eW´¦^£j ›j@³t©þà´Œk•šõJÂÕ $2|[¡Wè×âõàüfg×ÏÞ=žCAìeß©jFmûó[ªuûè4”ÖKK lIsêxqLäÔÞÚkd.Ò>4èÓÂJùÛÙõóii½&¼Ü`2ÝxšŸçöÄ›~´áyæ™°' þHéJÛðHV“œ®¨[à•ƒ~9]Q-\.Šü¯=é×38žÂžu¾bO)ÿY¶…¿}¦ˆîhk¿LÓݵô¡Ò²Sànºžº^ÊžÒ¤šŽªn¢”삊âÉ™sÌzó&6<ô㥷™(œ,:~Þv¨Èú¼ÍÁÜ_[P(¼× ãùšü‚Š?èx'ag6Û7æ\0’­Ïµ"½ÒGú•SFQhH?CC9­½ì£½WD¨pGܸ‡!ööŸW wHÚ½Ùå&ô+“óøe*Pö¸JÕU t} ¤nØZR8fÌ2þÌØ&”Ì”ªÑ×~w<+kzÿ¼¼æq½®_U§j`µ(2  àbPLÍðÃd¿zêÇC˜Õz#U7jØê̼¿²Z>×÷¬¤–Ò{ï?÷#ª}ƒ4Ô+Øz…1(VD€ä¨ÈH± Ú¼‰øª5Yj¨½{ @G‰ì= œ‡’Ëë ¤7ÔQEy™B×[¹úÚÓÖ‚ûùS@HŒõw[ˆ"íÏÜÓ>fŠ ÔQx°Ž¹è®ÝZ“æœÌNàä„é‘]Aï{{í#måoïØÁ‰ÉÆFó›F“y&?ÅÖÓÊŒ†µåAÁýØ)ó ëΦ àú²Oñž ×~´[—AùÜã+™g0üûÆg‹[fM`WŽhwÃ¥c™â²•Ýþ­`þ¯–ö>Žéö|¤ï9tRŒøx"` wbg_0Bdgo¿²,îlc” >é#‚ýÅ{”¡²Oe(»Óºêµªº±æççeeÜÈç[æ´éx_«1¾ÏU¾±W»ÝªI@h s¨å Ë!xµ  òd¤:o îrƒåhv½ëŽ ?°» “ /Ün¶Ä³nˆ•­‰°$‹aw«5Ù7'ñµn/GîààzŠæ{ ‘ï¸_ëÚ÷@•ßM'|gUEDü$Š8Çú»õ†7î![¿¦}™ÉüÕ®:Ùx×û…Џܭ aã7OZÖ>dj4?Çø8Džb"ͱœªš?/8ztß/´ÚWå~¹– Ùò¬kiH|/ÚZìY?Ú³' ,¶¤µtyhXêÙãEƒw¥ŒHëOXjê„uÙéN{>Ò¯eïXêyH‚mœoo?޹+Ú§(>þÓ-Áœœ 8ãnÚ»^ÊíiH=®j k -¯àû¢¬Ì˜¯ü†PƒÙ|CјŒiùÿèÁjàÀ‚(€&¡ ·qL³¨g \ß’0Ñ ®š”zH–÷…Ô >z S@_Ò[hàx‡0' (8=v±´yϹzE]J!þuTzðGÑDú²dªN¢è„Év›Ì“÷À‰ÏߢØcÛ­y”7þñ÷ùåEÖlð(YVQñþw gÊÓ¹KgÒ›Ío?{²ôo+OŸ†·‹~òXë5Úb,Ù—¾ïh™ÏøÑn ’•e‡g [bÏGº$+¯³·_yŽ3ÛðâI cõg·üž@0'©c_y¨@Ù™VUÏU5Ð5–·õÍÂ1™éü=aÀÌYC/ŽÍȘ[Àþz¦Hp'¶,c@9$8X€d1ѯ¡ALƒ>Ϩ@IDATô35ñ˜%PîÎFå&c²µpßøøVý,ꌉ{ÈØãø(B—]Ezâ=Óç: +§Š¿ÑÕ‡¿ ¸ødŠŒÙ¢Ùd3J¹{”­[J'¾zÛšGqµñ›»7”ÿ`ÝÑÎÆÉË. ­>[º€#ø=ÂÏž•{ÌüÛê´æ{Æmݹ‹“@œh›ná<ê‚ €>ÔG'õ¢M…§…›CÐTqNÒ‡vr¸™ßääßô>p<àŒs9ºv¶ ”]Ó›z•ª¥4öÑ"2eKÌD6‹ú1Zþ¢x„1©›7[f{ô(mX*+?š à*­ÊnoàÐÏk²QX”ͬ<e –»³Ê$¨×e .ð!D‡àØ_X™-.|Åräl›ôÄ{ ïœ´ýÇG©ºì«ËDe{ÿI ^¥È¤êóÔ=p.Ÿý¿þ¬5íC¡ •·-ßþ¦uG;…c3gל)å1s’$=qÙêúþ15<òMv6v c-›©*ΠӗÉ~´k›üh›»”u›ì JÚ1êÅ^:@ËBÇzÅ{Â~Ð!&½·ÓNž®† ”=­Q5=UÝPˆÞwt„yõýVFz}ø=ÕßdÐÆ|¿K»S$,WšNZBÅG”ÁxÊ 3Õ“ýä„?P/ =(£®ÝXøÛb8•­Æ°Æ‰mð»˜u²%²=å˜rÍ?)ï»ÛI_'8µt4çi{å‡äÜÛªeˆ;÷@eÁ*~ù12³ŸrHEt -‰šr\oÈXjSØKO_ž€üw³Ét‹òd.×J?­ÿ½I99ÅM œÇ÷1ðìÅúÆF\ÖbˆuÁ½ 'iDƒûG ï7;8àêC½Í&¥mDcŒöo ¾! dY‚7!ˆS[gê¹9M5Š\”M«ØíÙM({VŸjjªº­6o>Z4.óf2š—óÛIˈo:OŠy+üD·­´ƒ(ÀRÐ`5Åä=ÉKVZ’{P†^äÝÈÉ}rŸƒêí§õ¨{€'a^0÷Ú²ˆ_ìÑ… '©hÝc4öêH«c×ë,Ї\Ëövæ¨Ú¾™Š^ú-ƒä‘Nð€A´vÄMdܤ]pT4nô/©¾æ¯|b/q±¥ gxõ[žwñ¡uŸÆz}yŹäÓ(Ë6ntS´tˆý«>ÔÏWjkÚþÜ5éç_OQì4,xÁq$àLEeÙh04»_9?+¯ìQ²WÔª&ªj {j -gë…Y£ŸáÊçD ͦß³>5'¿y:|÷¬ºCµR\ Èò·C uƒ“$PBU¤^ºAµÚ­‚¬+@!¤»ÞÁñ£hÂÿ¦¼%wr%MT_¾ ×=A£gYæý¢î®Þ•©èeÉz$Cýià^£¥ß[¸Ñb§EãÇ6Þ2ÍÓ”‡¹»öI .àá„-[Î*÷·Ú0K]mÍ™ª:½¦–}‡0Y ê*‘­ Cý9èP¨_­Ð«>Ô›ÖÚ‡6œnDiõÔËŸ}<ú‰‰¼˜ÔëHÀ´Ú¤Q¯?ÞÔN²Y¼¾V²×U¬f j {i -·à…â±™\Î_Ùhz—ùÊ#{2_Ù^ KÐd︺¿ûk ;ß±©ÓˆéÏÑîì?ˆ†,-^F6sæ˜Cµ}Ð:À"›$#¨}àªo¾\ƃüðõwÕ‰³åæ›fNÔÚâ,[À²FÔQƒYh¬31‘ft"Ä"|Í·tœdÐ-`I>R´ÿÃeÿý” ´ Úm„¶²ô°xÃÛ¢Z”½­a5}U=@©©ÿ\TtàF~w ç×Wx}£þu®öÕ= êjU ¨h¥>IÓhøÔf·q' —ÒÞµl-®H¡ãŸ½aõŒË`Ap÷S¤ ‹h•ŠíŸF­Vÿ}Eå“_œ9ó#Ÿ¡xOˆÊÒ¢\͉ÂÍ’‘ߪE_.;{êäɱM¿ù¯/‹10Á<.=U3r`¼Õϲ´.ˉ~jÐ! ÿz‘ t#'÷É}²ñà'.ààÝ÷LCåö-ÞÈY½r ŸƒöÀk¿´(«@™•¡ŠªU]DƺáиŒ»˜‚±Žß^0ø\U46óº´Ü­_w‘*¨Åô‚JNœ¡]EǨì\ UT×’¾Æ ž!˜ÀH‹ÃG…‡Ptd¥§ÆÓ€¸è¡¥Û8]­ŽÎ}ð5ÕV6Ûæ´AÁ”xçï(zúUNé£,(ø ƒäN]äØÉ’§ Ž2¬—þ¼`¬É´mãÚüýù&Íœ3Ýh4\Ä`.‚Áž9<8Ъ ò÷—MŽÓ­Â.Í¬ÛØhý»ÅÁnö`X)­ãB…# "‚‰°n4&£±òpᾕ–~ÿ]MM%¢)žã¥’´ Ú¦CùÉœû‚VEÕ€ªUÐ@rNÁ†¢¬Œ7ùK`™‰c6¿vú‚ VĬ_ï)«J©&áM €›¹*g­ÎÛKçªjÅÄžpSDM^¼Y_K»¦RO‡Ÿá¨bõômv¾ÍN&ÂûZy=Yžc¤jw—¶(4‚’ÿçyqÏÙüN½ÆÁó‘.†ôa±8H–HO€èººšF¶.ÏûW ™™œ4xèÐà°°ÞÁÁ‘~:?X UqFÜ–C£¾¡®þ\]mõ™ÃöíÜ[·Ÿ“(Æw 3=± ÚE‡[“9O(C ª¨P5à Dù>QÞ¨gÊ…¹?ƒ¦¸ÊúêßsÊOz&u5_Õ2*ÀzÌÜB*g€<$©Íž”NC“c)8#Ø=[êô ´÷Ð)Úºï-\•KÙ¹{èÖ9“(=-¡[*ælöwtìÓ׿4G6ëÌT—f ÁÞ¨Vg^ËkxS•64 ;;Ð7tP0ÜýKª,ÉÉfMæ¼U  %¨¢j@Õ€g4½ys%[•cÜ„Yè˜ÐþÈ¡ñãßNÞ²å grPSñ5 &úiË.úöçêA÷^z¥Ä÷ñµ¢vjyÐYÈ’(–ƒÇÎТ5Ûè_ŸÿD×]òÿÙ»¸(®­f¶±tE@ÀXcC±kŒIŒ-š^^zU“Ó‹±ä#±$y¦¿T}IÞK{IL7Ö5öн¢Ø±ÐaÙݙ̲à" l™…{¿Ù™¹÷ÜsÿwÊΜ{nÞ¿«OusgåE{Óàø×ïCñ¡ªÞæh Ú«A‚Ô?¸dØ8wêSYDÜ!"tÊ"qä#KD™ú‘k}•WÜ38IF0êl‰$“;a®D!bLd¹¤b!ìé8õ…דW\/ñZ²U²ŽÄÖµ•9Ï&{x¯·˜WÈàÔˆÞ¹ Y8ŽàLJË>žùRZ™k8˜–þ-ÎÒ÷8Òä˜Å`µ˜æâú–²óÝ~Š€bE¶X¬Œ$ÿŽÄ¯GûVpëðdp ¡å§Íó¨Úô1é–a°`Eü¸b+Ö%ÃÕýº²AO­ØƒÂ)ŠÅ‰ï>†Â][ªÔbˆnq÷< šö±,l\*l\•’>û£8"fŽÛDÖˆ¸±h¸fýp](sÂŒ \"¦”l¢LnÊËY•‰4ÓÚ¯ø%+eq—÷’LjrJÊÇÍÇÇÉmŸ´H–VèÓS± §±iô¦7ž8õ!ƒ*%ãu×íìñÓgÃëö½8]Ü))ÐË¥$ŠòSjÞÕÈ¡ðæ¬¾½†&lݎΊ<5’LþÈ»2r`áÚŒ$ß5º_chžWÚ@/ ¯%?ý•†–ø0æ†álÀ“Wªg%%{àÄ÷Ÿ@Aú†*tQÐ'iqÕõ hlTãRaãªöý"dDàh]Ý5€,ÉŽ$Yñe&‚ÌI2‚àB"\•…pv$Ëô‚Bä˜Ö´Ð1ŸX’±^–=}Í„”O‚9÷?²$¿ƒ×9¸B–[oÜq­Ê`Ó_ æ£ûÕ+HšåVeÿ=°ÿØÀ½r$É¥îlÓÞlÝ‚ÜkXŸ~%d5†Ô£}ä~¡& Óécpî¯_á,.–‚ª†iêé¨Q·Cô ÷á¤!a.Ñqð(ÊË„3Y¶T{WMch+hÑúr—Êû(“BÞˆÌQRH²íw»PppuMx*IÁVù¯ªµÛˆ2¹[0K2’äOgM»? UÕÏ\Ž@ƒ˜‡/½H’nŸ8}6ÍJû&^ï{]qÃ0 š¹%²å1¬<9V÷ÃÉI·âö R†ö6k²…Y“³rNCQ‰‰Eqð™B¨bŠˆñÝòS}<ZG±é~}Ù<²_Øô#È…{.Ó« ƒW^Qcï]³uTU€¤‘À¦Ÿn†‚3»0:ŽÒ?ný‚›w¬£,ŸeW5¹ó*°bů¦AM£{(`>¹[€.ò~N’'/ÌP%ìºÆë•ÛƒËüŠëþ’ºÆ¦¥áÌJÂ{J& „9%Å-÷E&_{»5Ùl†2œnöÀ‘Sô•€ÅIöŽ ¯Å„~ÕÇrk´nGdŸ¨Œû[cF xӄ瞬cÌjOxû"íÛ9ÿ~vŽ ÙïÏ€ê$9¸sN2zÌ[Ê¢YÔ$ÛZ%j yìLã‡1¬Ay!¤þ~”—ÐdlÉÜÝB=½Ë5Ḳ,£ÏòdôYþ»âº³¶*tÁoYJ‹Ç?~—•»d/ú¬Êß×VŽW6¢lÅA|fævA“ŠãŒ{î˜Läø™pÏÏÏ®>GÀ˜ÏòôYK1Æ“˜½V¢Üvݺ ™É=ɪü2‰—¬Ò³¸âD™Àð£d›XÄ ååhQ6™ °¤ BqZjw¦;Göƒ@£¶¢ïó¯«Ó1\Z¬[§½îžÆG;%ÉîlG}eÑ4ßyE%Œ(Þ¢è™/²Å E{·Aþöu·i%”Ÿ=åTåÀÄˠň›¡ùàQ@¾ÈžH!-:ƒ…ó\¦Êh0Q¦ÉDXœd §ò¶rõ87!€–®op`ßWtý»2)I` øAI‰ô~M@‚Ý7«wï˶m[ë&u¸/ ág Z:‰(›p:f3F¾ lð#¤Šæ-šC\d8Ä`LáÁîŒã̪üÆ—Ëàž1`ñº]p.¿¦Ü?Š…T#2}ìô 6Âð~] gÇV0ÿ—µÐºes…ÓgS:s¾>_¸þ‘9JÊÊ&Fyþîè,B&ºaü²z;—–CßËÚ2R®¸Öž\)Âû ƒˆ+o€ÀÄ.îT¡FYÎÂÆX;:]>£Æ2üGÀ[4ø.G3î¡_•Dq’½¥4¯‡#Àð-t½ËŇٌ›ô©¦^ÌÚí¹8[ß—èz1I³*s¢\ 'µþ%ò¨¸^X,$Ëåàn"çØöc¹yì¯AOs;#¤ÿù}´E—ˆÑ]ãÁŠÖÖ/ð¿Ñ ƒ›¯ê sÎÀ·Ë¶@‹ð`h‹„•,Ò QÞ•yœ‘ú6ÑÍagÆ1&K¡ ?­Üz-ŒÖv:ÆÈ7«6ïɆýÙ§à¶á}™õœâEwKŒˆ°`ö÷æã€Q*:CT³¥Xƒ× Î æõu¿°äƒÂ}Û }Ž ÷lƒÒ£‡jÔÍÛšõaý®„ ŽÝkÌçÉ6®´ oû„Us8}>6o¯æ°qž„ƒËV &ÊØ–¶4ãŸLDE½ÊUáxºÞ'L›u«iëjUAóŽ$[Æ#IÁq¿ò¸œ½Û·Þ¸-ÃÕò<ŸoP\/È–â(#sv»BË7ïÅ‚fÈA+q\T8li¯ƒ¬ÄwîÏþ“%ø|A1L¸ñrHlÉ,É{2OÀÖ}G`HÏDøkë~È>yŽYyw:=;´B7†ª!nóÐÇú\~ŒGíQF‡6‘@¤ZIûŸ„.ñ-!²Y[¢š‡à,„'˜ï4å!Ëï ÷^áV×’«e».©<÷îEë8ºTîKÓÉœKêÐãp$Èqí.™×[; ~ Šó²ü-lœ·àáõøeYcQ÷>ÒŸWËàø¼ö麧ëߥŸ–¶}•axù±H±Äòréq,ø”K…y&Ÿ" X7‰[­ú™[Ù,nW + â;nhhÕŒ‰§º)uFÒª¤‚bÛlê±-*ãöÆF†‘_²*·ŽnÆÜ6h°á©sùpËÕ½•¢öõÑSØv\… L×,,ˆí#‹uÖ‰³päÔyØD[I¹8ÐNIäjA®nOØ^j²‚¹3‹2Y‹K²²¥4ûíOÇAxôÞZs´:éÖ‡÷¹tÍ#kÎì³#"lœÏÐã{_åøŽNßœ*ïžÑ“KåpT†^ûtÝ×훳(¾‡ßÍÇÚš"ß#ý¢°d‰IeMãê8A@!nHãÐíBbDÎI¶í1 +óQv&D‡~ÂJj^AhÏ qm‹–fJg/á¤Ñl;©Sظ3ÂBÑl#Îì€ÃYˆ)G?ã¸={(ª àD§Åe`=¨›C©ÊMgQ3*Ö‹^ ¬h(;yJ‘—>%Gl䘈rm‰¦îØ ‚»ôÆ¥uêòŒµóùq%lÜÆÆAYÑI{ظA·-ô¹n\¦‰@ƒ‰rÓ„·š#À¨ [¶ý•Õ'é0ZÌâqiž}ö4Mký}}dñ2ÞG@!p´öe"Kr(úÿvÆ îÎb:ç—B—[LÞ¤ŽqðÇÚ°ýÀQœÅyˆ¹–¡h€µÛÁ¨A]!Ò>%QÄír #NþÑgÎËE"®£Ðuè£ìÉD–á#¦€.ï ÈgO€`¶YÏk«“¦îÔ‚/CbŒKF¬ +²?¦šÂƉòxl×ÙÏàDÙÏ;P êŸ>s ˆÿ™7sÚ¿Õ +:ŒŸ6k³(Èó>5ã3Wòó<îA?#ËÉ=?Gi3I"Ž|Wœ(<¹ŒYs¼~|¿<æ~µ ô: \Ý·3\o#ÊärѱM4#Њ_³3á#12Æïï€ôC9н}8NÅ=vHwøñ¯m0ÿ·õÌ€ïÅÈ'Êh-Îß¿‚´Яš&!©žÄ#Ût€ÀøN`l× º@`;œÑÎCáäª×ïÿÎÂÆÅé>ƒípµ7ªçupìp¢l‡¢á¦Ïú--Ã1ìШO^¶¬ºÄ'gÏŽÆY§Dpݧ¯N¯ów¤”YLIê6£ºø÷ñž‚$W:ºXÇ#3æ Æ¨ ë@€õófN¿œHTõ¢Hh?FùýæÍžž\ýXƒþ r’ bunP¼0C @ð“Å”‚× ~K—¯>< ©]ü¦ôlzð´™î½1ù&§®³c1-Âá©;‡Cª#_áê„òÁë]$/©ck EIý.k}:·3ú\W÷72ྱ™KF†Ä 0Øë¸÷ÚŠ¯uQض#áN@¤ ±!¦² WÀ‡TbÞý¤¨¯}¨¯ºé!à™hæMÇ*-–¬ò'ÏÍkRåHÃþ·Ì:29åV “¢ÂÒ2 ž8cÖc*ÔŒ«äZmÞ| ŲI|3¬fùnTÃEº"Ë•‹›…7@…w«N’ë"Ž¢aT'ÉŽå)Þ2‘æ†Ôá(¯¶m]ó(h=þ%èøÊ|èñù_Ðý“%øÒ{sÇcÐlàÕM†$+8QظøÞ(!1(=╇ÄÑö|ƒ#àa8Qv;ÀÂo ¤0¿l¶;E?2ãµ^8†¦ñ‘dI€dYxí±i³š†™Ä'†ŸÊÂÇ0¦²- ²p‹²ÍצŽMø†Q)ŒñAT·±²; •0ÂÞ¼>å'¦ß¯ñžYß^3ßhŠp× 7÷:ú½fK²0ÉòÿM˜1çófNÝ\[R> sîh]nÑHwhqúÇ3§®¥²LŸ9ãϾJÛ&³ùÏ Ógš1võY {Ô"›@…ççÍš±œŽSÿò¬›Á /£¥dî§3§Ù¿R¡œaVYxO'èþñѬ÷±¼3æ\!HÒ ¬»NEnÆúSµ Ÿ¢gñëÜ„u>'ê \2Ã˘¾e½ŽÓÏRò8®ÇϘ5$®Ñiîø$å¥lÇcÕ·uZí4³Ùz¥àcÿNIùA©*&|ú©N6ç®Ç Æ€ÐM¸§_Ó¹EÙÍ}* …qÞĉæGSf=lµÈë›NÁ*^­©ùX.Æ»‘“´:±×G)ÓÒ+ò}6~ú¬l$Úoàÿ~ógNý{ü ´ŒJò­~á¿R^4 §ü\Uルýð鬩?"®³Q¥9'ÌëŸÁðuó‚Äÿ5ujnõzøÿK#¹~}afrÒßx®³Q:&k9­¹uþÒ°5©£K6ì†Õ8©%Š•C{u€¾]Û±}Î~þܲ—…t»cWOyH”—oÚËb&âl|<ù'ïÿ,¥¶o-¾–K_Ѭ#ÃÍKÿ©y7ŸõÏq­ÕŒ·({°wÈåAÅGð"xÒ2kR¹(^dÆÀÿAøfœ†ƒ÷þYe…)è¾ð€%@¸$û(å¥ $ÚÙør=Œâ,ã-cœ( ¿²fÉÂïøŠ=†ö#‰½óe}œòR%ñÅL‚ÁÂü½ªÀ ‘m>`¢PÅš¤µ*»t(„Tö!üû;Y¢‘H>êp¨N›1ÚéhÍ6¡¯÷'ä6‚wÀ‹õC‰.ê€åmK-Þ~úé2”À¬Êîºôº‰L‡@!Ý0¾F|Ÿ2•X³ÉoZ‘Å×®#€ç¤íÅ ‹È’ijë¥yΦ€@X°žÆxÉ÷±‘að;θWz ‹ðí×ô;Gök Ð4é6>ñžôCa©ð‘NªþÌâ¹teä‰#à^¸EÙ½x^$mþ«Ó– qü‰ä«Iþ³z$[HÒJ?™5í?ÕÕáÿ ”?ô¸y6™PZèù*«t¿™åò×NÂìÞøw~£²×/& ÀR‰Ð] üödaûh–§Lû¾Z6ÐÍá]$ÏL˜>;º½=qÆë>9eG-Å.:L~¿“f¼þp¹lÞf¶äÍA¹eèlÏWG½`Á8{áŠG_y3i™ɲýP]ûaÞ´i'±ð”É)ïÏ1™ ¾•ÞÛ RRn»èeÈ^ ߸üÈDÑ£$WË))8ì2¥b‡m7ÿmÚ ÁhºiJ´*ÓTÓ'Ï@N+ý˪tˆ‰ … %°yw6Ü?n lߟƒÃ;$¸ùjºíü¹ylÜ•zôïÏö)?'róaUê8tô4z«i@ƒSTÇâD&4¡¹oü´jœÄ<áApð^ж%}èãI-Lú—aÎÏ•µÁP×c™N’ôÑ¢7µÙ×>g±?ëÔ¢+×Ãàe/ô!Pó’=X$ô½­:ûR5Š:qͤi³ÐMàRI båV+¹bTIèÌü”‘TÞ€òÓ?˜5ã0e°…x2d íÇÙó¬w‘A;‘'¢eÕrwaôG–îFëqN ´¿È-㢼;쟧ÍXmì3ØÖƒèò³×Tàû?œ9eF”xÝH&‘ÿµcÖ:é- šoDõÉ_$kùpG™´íz?T-ù¯”' °KßG׈SÖÌ®Uòµ!˜–¶ } O±|²Ü"ó÷ß{ÕV†oZ”›­°'ëlÝ“ ‹×ï†VQÍ섵°¤ŒáGNÃðþ!ªYо¢RÛ‡¤“gó`Å–}Ð5!®éßR÷fWoÙÆÝ Cò=åÑСm4;v×h›5ú§•Û€¦­~è†!HÔÃà×ÕÛ«”å|À¹s¥ò/k‚Ä8~ðÑEö!«uÁÒ·õü^ìûîi4p‹²º’üWÑ—õitøÒV]¥%‚u³…"ó=Àø¯‰ÓçLÁë|‡ &4{ˆýÑÒ¦A+m •¹¦{ûuî8Q\w‹Çë/.3Á¯h9¦º,V ®Ö뮼‡šp ëî½Âéìz9¹`D_䛯²½•[,ðóÊJ›yü,Ü8, ­ÍZHˆ€½™'€,Ø4ØïÈ©ó̲lÔCr—¶0ÿ—µp!¿š…y¬ÍÔ=Z¶œ=VQ#üÅ’¢Òу×ëË0ì'´Æç]˜Õb^´âÝ þß*>݈šÊ›â#¸EÙKÀÏŸ=ý+$|˪W7oÊ”|1@胄p¸¯%Ùt/öeè¾ðŒc^ é†1{ŸG¾{Y9ÈÙ8ñÈAå8…RÃÇÆv|n.ˆÚ_”ý´Öšßh?nn}7åé<ÇcógNû‰É£hý‡I2-rù~Hw+ºÅq¢Ç2®lÏK™º'^™Œí¸Éøƒ®”©ž‡‘tYx1Ãû^ÕäªÞ俽«Q³YÚn2——ÁŠ1¨5w ÐŽR]í$ÉVì§Ù²Ùº¯È\|Î"Y×`HÀ Q+\ûÎ3ÏT%ߎðíqcåA¯žÔ†·êɀ߻ ñ£”‡S³@˜öÐxeâ8¸ktFtw#¡UR»˜§$™Ž=uýšÃ•¬ç°M;;´Ž‚õ;3aÛþ£°i÷aè…„˜Ræ±\´kÈðßið¯ïWÃ׋7ƒÑ ‡“ç ØqOý—ã Cg¸{ªn–{ÛôÒS‚ ‹–e[¸ÚšL%¿ox»•ÑŸÛÅuWÜ¢ìÆ~˜7k:…f«1!Yåìà'Ó§Çý×ÿðÚ;´£X2¼C‡3Œ;@‚ø9þýü‘”×Ú‰Z%Îð¼ÙÓ“²Ú7+f÷»ø)W‘#K|†›ŸMš3'Béã—^º`/찖퇿U6C¹_`&ZjLŸÌœºÖ¨âµ;}™sUïŠöwÀØÌ-‚@”ÞIy†¹ˆ ÜÕs¥èG~{A7'ÒbÂD>ÅÜ/ª ãÿ]G@”Smž°ˆ œ(»ŽœOsÑeáTa)”â$—D"=ˆ4vF÷Š~q­½ÝÛÛ†oqM)ªy¤8j?LááÓ•};Á—l²,íÕ:·kÉÐÂLi<º]ÄEUm¶ÓC?„cQi„Fy¨†Æ-vôóå;¿©¹¿º.Äûýò,ǿž[ñÜAÛOú!À‰rýpóH© bœIÂç]¢Wf»»Dq§‡>œ:õœÓ*ßéªÞ5M`â¬yµõCÅD#ôIÖs`÷%´ˆÙ™yæ$NË.ë)·ÏLNCßåü:ŠáÙ½€‘Uei¤ƒ¬s¥°?û4ôêÔÚcµ[q` º++7ÃŽƒ9@>Ëqè§ìJJlÉ|”É79ÿmA«±cZŸž d‘îß5žp…t·iAFÆ\Þã†ô€ ÀÈ:q–uÇòîÜ&шþ×ÍÆî”ÝTdyκdñ?ÅÉ8f„EÃ@p‹r}Pãe8÷" ©H“{Û„JD”íOÝ[—Vì$I¦}“E †RÃu¤QŠ8q]Èbëî4zP7 ¥¦Daܪ'Ç}40ïn$¼4P‡:Ó ÀaÉY‘½‡Oº™ðÒ}£ <4å!_ä-{@»Ø¬=Ïß3‚Y²©…—óT"ü9½¢®DæÌ=Ugc–;úù—_\<÷•øæ}=k'ט»ÛãmãeCÌ+àpjCoDH”+NI«lòµz +2ÎÊ-úkµZˆ A«²F€_V§3—õh[UƒN[%R†rT’%È+.eÑ,2rÎ@Îiü¦fk"Èž$ÉäJòëßéˆVìËÚFÚ1&¼yª‚"E‡ÅÜÉÃÆÕ?^ª*üJ¬ŠÿÇàøQÔVeà‘/|З¬R±nQ&‚¬×ëqA’Œä.6PB…X°Â¡ /)M/‹aøþÚ²>[¸£_dÁèÁ]aHÏö^UpÁŠ4†_϶Í0”áK8+˜{U™FTYŸ‰'J4:ã8´ÎçP³Ð­K ݈šÉ›â8Qöȼ ŽGàÒ´•åݘ}•1a8¿sýû‡²mþ£ÈmA‹Ž²£\, h¨…Hƒvf‡o–lVµe¹:˜ä†ñÐõƒá¹»GÀŒpAƒú¼•È’LxíÌ8í#ƒ 5Z² W—pvŒí-[=£ž.9ÉÃÆ5¶^õ~{8Qö>æ¼FŽG BZš½2³”Ýù`n§lóµ:P\/ˆÈ A0€—CDŠ¡+gdùë˜Ï²:´V§ä“üÁ«`¾\´k¦‡Ž±á O•ð%‹2w½pOßQØ8AoG7 k…D%lœÃ°N÷ÔÅ¥4N8QnœýÊ[ÅðCä#v¥%hkßæª@€\ˆÀét:Fƒ‚!—  #ûi ¥® Î](€O~^Ÿÿ¾#bä°8˪h€• 8É„áBø\È/„Zhß2´Ç@†#᫸^øXåFS=…Ãh-“•9„Svñ5G FxÔ‹¡á8ï" ež„T©låDÙ»à×Z›(‹o-ÊèK&$åf3X,V0Þ1@)M88Î,BfÎiƒ"8cþÐ`#ºk4½GMK]€!îŠp*nŠ“¬CóT¤Á ­Â !!ÁÄð$\ _Š(Bxóä>(lÆXNÀpq,¦rEظcž·Îw_-\RcD éݵc/ò6qÈŽÕ¢$É'Ê6(TõKî4˜ü“É¢lF’lµ*$§Ñd‘1Ê@‹“ƒ4·–@©©³¤KY9œ++B¢è?äO*+Kaž]Xzƒým Ï¥kpb¸f8W\0F˜ РåØÈˆqXX„"Y%²Œx®„/w»¨ Ýúçaãê‡[S/ʼnrS?xû9*AmGU\øŠ(£hÏ–¥â2³™¶ý‡Ù)àypm·*#¡“ÐÝä®B–d"Éáaal›ü½)š·&× /ÎÎ([,š‡œN&Ç¥æBG(l\ê§±wžÎ?¹K'£­`µ.Xú¶~ð¨gÊ÷8då›;œ(Û¡àŽ€O4G@F¤-µU6¼¸¦/K’Ùz&/¿˜þs¢\‰²R±AÖƒdƒŒ¬§äWKn £´´ Ý2LŒ,[%›ÅYBÂLÉŸ³ñêqpòçÏA*-0—‚)u´q‹EkÅm‚2á¥É·-ñˆ bæÛîdIf®vk2ÄW#¨x ¯ X.7™*Íü•™í×nå.ç[6né;ã$séf|wk•°qý‡?U|Úy)¾·)#À‰rSî}ÞvŽ€ŠÅ#¥Ê¸tÛøR5³Ét¢°Ô$” _i úòT"Dü()VS„ÆÂÆ!^eH”Ë*ˆ2ZÑò,ù§U pìípê×/lœÊÍÑ}Úãâm*\ÈBLî6k²-Zˆm dÙB.tœpåÉ9t-Ò5YVRœë<‡ë{)lÜ’¹ú±2˜×¡e9—¶&SÉïÞn5lÐ3ÇÈbÍGÀŽ'Êv(ÔµñèŒ×’­VË(| µ‘9 6!êÒÐ?´AkA¡ 'PÛ#hÕYöñÌ—ÒüCó¦§el»v'2³YÐä¨ÅWôáaÃâW¯.óò©œ#ëC#"ž¤ð]ý»%ø@uW©@"wlMÊ´MD9ýo‰$³~åå¶~~ÌŠ5Ù¯¼0ZD@à¹cpaó_¬Sä-BXÞ oã´“ì¾ÉŠÛZÚi&Cr« |( «‡/Di?'’LXòäºñüŽÜ¿sYYœ¨e/…[ü¦ævY’¢$ô %lÜ­Ø$›'ŽC€eõœÂÓO¿PP4ošOY$KŒ rH`€(èh¬4OuEýL¥¼‚¹°¤LDLgOœ1û$~~;N÷AJʾ aumB“É/,X`Íèt‹O)AkºÛǽ {øþ½è—=zö:·uOVs$Êœ½8é"uJ3æ^P:ŽÈ ù'—ã€>›5ÙÊ,Ê2²"Ê Yv"Rµ»¢ïxŸÉ‚²GPG X–} ­Ÿœ®8:ÏI"lØ‚ž;dQ&œl3êØ ‘c³2Û"\pK²«íÂkQ–¬Ö‚Ý©›2ñûUˆr½I-…[ò†ø„ò‡Tžž7/™«™‹›,2íã‰#À‰²ïÏz‹÷??}di@é|Qb/Kˆ•ûvM€îíãüìKoº<Õö‚AŸíÈ"7Û–»3ŽÍ=e=ñÔ„é3œ7kÆòú‹æ%Ýr k£¥\ëí¯(Ž^kî‰ãßï„Ç=Ú´twS<Ū X#?eF1\ öSüÙÂÇ©7¯ñ)fÁO‡C³Ÿt’ÈÍòÕ?CÌm8Õ‰H2%66ÿm"Ë´afÛH ìœ á;íÐ5ˆ÷máhÆ?q'9h)‹ã5kÏ_—Ñ/JU„{–Êáùùìâ¹6®. 6ò¼œ(û¶ƒ‰ÄiœòòSîõ˜È0ùöýé¡Ì-Xnîò3¥Oèd¤›îwË6·<~&o1’åç‘,¿ãæê¸¸ú" …6CšŒ4Rp}Å4 YªØCøï_ZpË£“oÿßÒMͦ<0NÔëø;kM¸*Öe"~äF@n4xȱbEV²²®I–j÷‡tá¾Épì‹·˜Š¥«¡ßPéÞï"•¢Ì¬ÊÖeÂFܧ쿨 ßq4Õ7^ƒ’d1®_¼pf ¿´(dY‰*yQYWw`ظÏ}¥=Ú¨¯ge$飥sµ‡G=o¡úxjâðÏù¾9ˆÓSWwÿ ÓŸA’üÏ>]â…î«á–+Ïwaüâýc5}º´Ѹõ6’å§±Vþrâyèk­Ãì)™4VÉWez[òòÎíMÛòΉÜ<ørá:ÿ4ƒ*`ziMÐF–µl*f6¸ürñEUñË¥µ¿.­¯ýDõ -§?{ t沋ÚãØ^ššZ‰‘¬i/u‹_WC×]ƒikWSR‚3·”W, YV¬Êõn'…‹‹¹Ÿl ÔJ’õG ×µÞByÁFƒ'ÊÞïJ"d„»îŽÇŸ­Óæ I–¾é [¬¼×„5aNØãø¹MI‰µs²ì½.pZ“ ËhQ¶%I½M”©b²NјƦ ËmÊÉ<ðUê¾Ã¿þ[&ëO®# g›5Õæ«ks;@7rEðÃ%á±Ð7‹ŒüRþ986o–Óv(m¦5'Æ®Ÿ3JNºÖ蚣k/c÷Ž?Ò7¬Ù…ÇÐïžÐ5J×jƒ-Ê((lœFg‡ïz9ôÉr˜d6ÿ±âÝ húÏSÓE€eï÷=#Éݺõ ÿ¸Ut3ùÞqC8Aó~?° û¸¨püT¬ûl„×g𑾾ZA°[”ñ Æ—®ô¦Áž¥K¿ûúç#÷}‹lùÿ,”Èu‡§¦‹€64Ú=þª€üÔ5»ôû¾Ñpè{ý‹…]s‡v§/\õÛ¡Tv=⚬ʴM×(½¹º…(£ °q‚ ‹&öÂŽd¹]EØ8þl €šhâDÙ»Ox“_¸¡ÇUWLB¾–蓬á–dïv‚cm„ý?FöǯüR¬µ¹0ñk /o£+ŒÝ¢Œa}aQ¦Ï¸ôð5ãBdœiŠ–/øö×´5½Ÿs"·ð­¯–Â߯7ïÎ$ÊSÓC ´çœ¥ïn{Ã}õÎà—eÿÏ7êŽ]KtMѵE×XÎÉÜ¢M+–ügõo?­Biök·éš¤ÿtÒµJ׬Û…Dñv$Ë$›’6Ž´lx4¹_>˜Ï{]Nù%ëq1…„<Ò­}+™Üó^ÔTù,S_ìÍ:þæyºñºõæ[SÝ|50ê…‚¼( ¾$ÊŠE™,Ü4ãˆ~ÛÚÕ[ö§¥f 9fÆ8†£ðCñ“ºb4ÈáaA‘?H«ugcþ+BèÖ‚óOa$Œ2Ø’2¶]þ(H"¬Ö¥ßiªxš“&!?8«Å\”µoïºM˯/++)AY´ÐuH/Ñ´¦ÅÑ¢ìö{u aãþ‰õ> OM ~E{¯ÃíÖäá7ÞÞ«¤p<©ì ‰OË»Ÿx±ï×￱µR¬ êP°‰hijH‰Š€û|ázAH+~Êd.&ëË¥—\ÁÊ_~ø·ÿêÐ=)¾m‡N]ŒÁ!Í Fc¨V«k²Sø‰’¤ 5›Zaˆ öE¦HpÜ"Šô²áÖDõ›M18 ·"€± éô§- 'ï$$sF‹%Ü`5‡åêº × ¹ •­Œ0‡ÿýuñŸý)8O."`±à<˜%¥…%EyG8˜±gùÓý—β1ίX pM×$ú%ê @IDAT]›nóOFY%'aãž[ôOÍk_°þû¢Ì|G£F€eït¯£59 yTôÕd‰¢8ÉÞ©ž×RØ4àFÖô4¨/"Kn·TÔ¦G“?.Ëô`´%YP6½¼¦~WÔDüˆ$+תB¢Í‡v¥À…¾·Ó}”òÑ¢äÃͦ“nŠh>²U€¡ µ¸Øj=ñõ©3ï»»õáZ­ñî5"#Éè¦cYYXøEZaQ†»ëªI^ŸàÀÄÁ!¡7D!œòÐɰ3B–z·¶¹lõ,<¸zKúʯ²ŠÖ$ƒïwŠ]s'-D€É­‚^~ˆ“%™ˆr^Å6Ý#ˆDÓ5êÑ{tõ°q‚,}Œaã²yØ8D¾ %N”½ÓÙ Q¦¼QÔjÛÐçZŒíÛ$ªÞ¼nµPœešñ¼ÉÔK1¢›µGoÂ(Ÿ§jÈø²‚VI¶7™u²ZoýUØd¹R®SRL!дŸøÙnqÆmEg¥ îjÜ©¹Vp™Ñ8P[ÑâíÅ%K°ÅDpÜ–‚DQÿ`t‹‰¡¢&¦B¨´® ès$É4±ÇS ­6_nŒÕë:VV Y3ßÎ(øvvsã mƒ4Ýñ&t ¼uã9ÓœŒ| YByº4Ê=V!Ét}I¦ëK±&ŽdI&Âìk2ÖÃ…[67ú. œùŸɨ, ·øMý 1Ï•ïUòñuãF€eïô/=<‰|‘r€F§"ŸFïTí¹ZJMå`Ä©µ%ú”nµJl"‚Úòúò8N.žÏ/ 1õõݼyò""ÍQQQvõõ5BªÐC›=Еÿô'ke²zQ¦{©rÞøZoTÅ{é–×5×jXT€2Y>¹²°hÖ® +¤ÑhŸy.\#&0WÞ\TòéŠüüu î‚€Û"#õ º;7TÉn•¡dgIÉÿ¾=aÎ<(¿¼Ãüá¼ᯓ¥c,‡¾•~Ûõ+Ͼ¥äçëK" \[D’­ÉD”‰QV|•éÚ£kR¹Mà¦gÓÈçO/}'pœd.ÝŒ²Ö¨l˜`5/°q†?U|Ú³µséj@€eïô‘.šX¥A#ˆÁuøsôä9˜óùB˜úÐ8hÓ2Â;×P ͶµàÏ-°qW”™ÌÐ"<÷ê£u¯¡Àç¿­¥©£á™»GAǶ-kÌç®û³OB»˜``_h]K}‚OiõõO^G-ÊKŸZ”©åʜ̎Ûô°¦7‘d6Ð×tÎT'Êž07Óhô‚GàuƒÍH/)ù‰£Û|t Z­8«uìôf­ý³¯¬ì£ïΞýUèÁŸþ¡AÑwFD>¦û:VsÎjYóß³ç?Ú]Tt^Ù¿÷‚9wá±ò7nmc˜ƒ'Šk{8 ÙÀI›.ü®äáë‹ kŠ’rm)D™Ü*”—Q"ËôRJÿi?i"ÉJYÜô|¢°qKæêÇÊ`^‡5‡`åJظaƒž9F:òÔˆàDÙóKOZè!ÊÈ2¾•ú픦`EË+I6nñ›šÛeIZˆÓµ®„»•Æ·øD)^©WàDÙ+03ë$]Xd™‘1·Ô¼:m?¬Ü²òqfÏNh­=¸ÄÇEÂ{ß.‡ØÈ0¸õšþ¬ž“8(Ƨ„®¿ÚÆDÀË·ÀöG˜;ÄU}/ƒº±|ß.ÙÈHí¹¼"X»ý LºýjHlUEבíÆèš¿®JcÖå*™*þ¤< zÆ é _/ÚwŒ€³XÙŒµ9§ÎÃÒ ;aoÖ Ì£-îoóGn¹N͇¯o€œÓç ªy(– Ø.rã˜þáOpËð~°døPP ÉÛÁõWö ΀õ¯ïV0]ÞùvhñÿìÇoq¦VÍûl/1ÔOÜ¢\3J;‚nÉä}ÁäceŸ=4tQȲò w$ÈtÎ9VΛFO”ŒFmRPº$Ø8Â)‹ùÃ\³ÙmŸ¢ïÿ ú$_¯ôEžÕúÉ„C‡_Wþ{býv»v=’CŒsu ØnˆX v¤„¾È_¼{æüææ^Ò÷zÆžÂÿû$94O†®øE$àÎxãì5çÍcÓO•áãéb‚©\_ YV³ò_9~±/îáaã¼¶ŠªâDÙóALe¡‡©Æåæ¡Apã•}К¿ a]´6ÿÇ5Ìr»|Ón¸éª¾Œ˜¦íÏ“ÙÌHôêÔ°+ã8Ü?n(ä—ÀW¬‡^Ú@d³P((.ƒ…k¶CX°®½¼'´Œpn•=|<IìyظóP˜»¸(fTºM²"÷ìк%¶‹Õ‚õƒ$¬‹Òo«Ó $ȯM¾¾_¾9ãoº‚ûjÑz4êáÉ;GÂ|øßÒ0í¡ëرsùEðÍ’õøRÐ H°ÿ·t´CÝ÷²xÔ%hV§¯Lf˜°uø¡8ž˜ÝqQnäu³ÖÛî/#€èËd}uqRŽÎŪEº‘•‹ˆ1-Žç þeÿiÝhÓíãïB·„VÔ@&waîùy¸é–ÏÐ[zv ˆ+à!Ëüï˜]{ŸWþ»{ý\bbàmÁÆi¢8 ߯é>ÍÞ«w1™'_·ÿ@š²ïRk$Äp¸8øþöÁš5(ÇH„ùƒÞ¡Ï Y\6åRåšð1åþJke!R¬,Ê>Õ@ÄÃÆ©¦+¼¦ˆbýðZ…M´"LJ¨[1ï$´Wç6ŒØ’[ÃÞÃ'˜Õu`÷öhY-gÿ óíû@Ÿ.ñÌêº;#zthÑ!бM4´lÛð¸’ÊLxþÞ1pu¿Ë Èè<4ì7hy&ësa± tOPŠVY"éÞwø8$un‹c \–Ð ¶ìÉ´ç9pä4#ôäKÜõ(A£ Y…‹KM…D|HR'ŒDa€=ÚYŸÏåÑ g[JêÔ–é7ý£Û …|_ÖqöBÐ]<(‘2M$RϤôW=‹óbõE­Éö—wì"¢jLôð¦¹bY&ÿÉê>•Dí2lذ²þIzÇТ5ï~q /R7´y_rωa¢fŠ]¶F³`žF?Þ²ÉØ•œtÅÄÐ-a¢øÞí4T/æRF˜úwqißëöï_ç¬\MûîX}v;¾o?‡ÇY à± cb†àŸF{>4 mäL¸(~È>õEF=\J6_…í~òaㆻT˜gò;ì%¿ÓÜÿVÈ­Ý–Èí!mß  1¢Hƒíh!?â.ñq¶÷0Ä >~æÜ5fàTÍp-®™ÇÎ@êÞl»§ÏQô[jß: z»až÷;$ÄtxãÉÛ”Âô‡¯ƒ$´[Q>¹;ÊGNžEß]v’ºjë^taØbÿè;°g{øÝâÐÊJnäãKI«ÑÀ°äÎpãUÉiF;Èú똹å*Þöíc[Ð5—÷ê?ý• ûÑ’](“ÛE»ØöýD~—mÜ…ÖëlÔ³ŒÔ>Æwärq úHwoßšU`°–O!ù&k±c"”ȧ™§Æ‡$-’ Ö0ä.n'G1ß´H–ä—”šñnöaûÔ4$Ë KY}zÝ‚×÷<"–$ 9øZ½Þx³°q£ÛÏFÈËŠßĺšÙµ„³¢ <›ºýKû¾lhŒÂCÖy'Š O[-­¥òg¸¶ù5@./ªxØ8uôƒ7´p«€7öÃ:ØMßz“Uøðñ³l¡íóù¡ ïôè§Ln vfت±ñ èÕ± HH:V§îgQ)lù#oFˆF,Ì$+÷B¥EYɧ¬i0¹uÐBn[vgâàÁÈ/,aƒñÊÑšKQ-É#?æQ6îúa½Ùrú wlƒ¡â³¬+·îƒö(›È6Y½uZAOˆ‹‚`´/^¿ƒµ«Ül…Ý™ÇÅ׸MÿÈBMVs²x+ĺÆ—>à¶¾»t5ü¨Y®ôõ؈wû!¾¡÷î=íýì-¯µÒ £ðnC5#™øUì¼^+ÞÒ…´pmÀØÖ7Ògy·¥Ì~ý:f$'­ÂÈŸU!É_‚¦‹»H2)LÖcAÔÞ‡7Û­Ë©ãZ>æ¶ÆpA>G€ÂÆ ‚n,¾Ú1¿@ìh%l‹+îs¹nA€›åÜc­Bˆp9.µp–ÁÑ!6²L}p,#¯¯|ú+è%¿a%i‘xöÃvã`¸þÝ•Ý1¢/Ð`¹w12I#–%«1YkK" w‰¯mÄÁyVˆG‹ñWõaúËnA2Lî4ˆÏ1Ñ`»o0šmH®#;æ0ÂNä˜"^´o o†ul€iá‚à®Üä(Êé6ùT“µœ¢z|·l¼õô¬}N3;ÝɬýJ_9ÍÁwz$^ö—A¶ùûx®:.¹H MUŠad‡ù1kÓäNÑ'i0ÞW~F:Y1{‘°/PÐŒŠØ¼¹æ·wE×rr²î°`}A²˜f`‡—1!S…G·n_ᢨ:eë³ðÄò´k£ßE7•§© žÛon»¾åêÞ¿Ú['A<³jàaãTÛ5nSŒe·Aé9Aä~ðÉ´ûV@“x˜ÊÍvŸâ¡½;UÉg±XÑý!’MåRŠƒL³åÑìzAèNFX–ˆ¨^*…‡Á‹£˜È Yz zç§ÐµŽ–êiHRG¤×ÉñQX‰®"s0|ùS>ýi5¬Ç¸ÌD”i Þ«ÞÄô#•3 añØmWW©æö‘ý+ÜJpQ57’*ùõ! Ë•¾=²À‰²Êz(»oÒ ‹UÊÔ³AÔ½Ù³ûôée•-‹ð~HrÐ'9[o¯‰Ý˜v¶!r˒Ι’u~^ëjß/¬ë-½>àw[­íuTl„&„¼”¸à*¼eöħQ²Êߚܾ‡eÐÀ5žÎÂÆaÌå7°i/4‚æ5ù&p׋Fp 8¼SšC³ù­Ø¼—Í wóÕÎ}‘)ž1‘f…$+e]Y“{CM$Ù•ò”‡ ^@÷ rÙ‡>ÎÙ'Î^äÂA¹’$»*ÙæÃÌI²ëx©'§À郟1m¾EêQ®Ék‚ïÖÓYþ*nëÖå]ׇ““;[$Ë2$É,%ÞRNéÍðÖw¸ægUK…çú÷ÍLNúg˜ÆÙÔ*I2Þ»¶ˆZ191mûO“dR‘±¨ïÄ-s#!œ—]èÑxе@Ã{ ‡3T¾m-ÉÏ/ú§æaû¾á·p¢ì·]wiÅÉÝ"'¹w솄I»t% 8Úý§¯Ðã>ï€÷¿ûÝ9ö3+0…¤ã©é"€äØN”ñæÄ‰²ŠN…¬~½z"©C*á( ´²˜Õ+e÷ëo•­èî ³Æ(ï3Ñ&--³^«ÊèÝóÆ fÓ>Ô÷1<§*¾—A’ä§ÆÝ00aó¶ÕŠxô/¹Z²ð¬R‰ ÉO¦Ž‹¡üçëÆÀ¨^~϶ߔÖð°q þ½vþÝÜ¿ÛĵGî;Xõ8ŒØ há‰#  €Œ&‰ Kè-^¬ìçkß# Y$‡HÂí·l9X­röŒ+/7ý…eãXy…ÖäQ ©i»ê#ϱ É6•  ùe Çója€F?‰YÀÓÒ‹xm»Ï¢ScȸѨ ’å¿'’“[`­ã•šE±nÖdšèC*ÌûÉ않 ÄIõ/gàÀ晽“>·È°'ñè È$¯´=SÓS„ Ê•ýj]; WUøšZõåzÕ6®þØ©¥$'Êjé ®G ‰# ¦E‹KÁ@Ÿæ½㶉C^kóKdëSø©Ø²O€íñ[·-­µPEùÖ[5Y`¥i©¯µ—…1~ñÇöÿuØÈìÓëŽrSÉ>$È(ÅÐ…]+„‰ ©éWħaLL?JÕCÆa»žâ!ãü¨ë *…׹ߕ"”m!ãXliÿk ׸&(lœFˆºÉò6ʃ}­•$ë‹ßÔsßôš@SÑ~N”UÔ\Ž@SFÀRZj'ʲ sÿdŸ eE8Y‡Njàþ@üØëvU¥¬>I¡»Å½J~´ü¾“¶ãeå¿+kf‘Nîõ¬d†=˜”½ †Ý@3.1-ý¶ø­[•PköÃþ¶ÁCÆù[ÕO_ 'êŒcñZâaãê¡ÏJq¢ì3èyÅŽ€#Z­dȇý¸EÙ/oÓ<¬òi¥Z4¿.¤¤HÊÿK­3’{¾‰.4Tææ%nK¦òí[½{'gfÜ*Éʲ¹~ u[B«ôû¡!Í.KܶíÚ¥øG '‚æ~lŸÍ*Ž’¤Žkù˜hϵ¬ I/ ¹¶»WàSÿ§„Ä÷»l‰îÓktñÙ3{PÆ3H4Tú—â§ê) ¡á}·¦o­©îưŸ‡Œk ½èZ. p çv¾Èʼn²/Pçur8! Ê•³ò¡q¥ÑY /j°JwdæÝ…¾Émmê ¹:}À¿kS5«wÒ$ä7”|Hp'Ú;0T›m²倓uÆ AQ8ýô· I‹+ëe$ùO­¨ë–˜¶ã aõj‹“¢jרº³ÖÆð°qµB¤š œ(«¦+¸"¦€$C‚Z9QVÀðâš}þ•áE¥Jì‡wj Ó—Ù·çHpÿåPf¥~³+Vh ùö PV¼ë½C)fä³¢(Þ‹ƒõF´Ýº5˾¿ l°q‚ðœÒTÄ•‡ŒSÀh„k6Î?:•eÿè'®%G Ñ#€ŸÜíƒùpTSq£o° ˜Õ·÷M8¦¬ ©†Vá|ÍG—R“bã½ÿÆþªð¯6ED_¿zuÙ%Ëõë×1#9i†|û Ë6sÈûU hºà„$_9ìkR›}žúÁ¯2ã+·hR 4‘Æò°qþÑÑœ(ûG?q-9A”-Êœ(û ÇeI~I©‡}ˆSA#Yvž0¶ñh þ‰®í9‚1bA3¦åòå5öÍÔ‡Q1¦ËÓN 7Ì.Y2xMûm;îMK;kßßD7ª‡Œ“eëçMŠFßìšÂÆ-›lÞèAPyµ*׫W ø Âç >ª*’ã¶²¯±®Ñâeom;þ·à~ƒ€ F<›™¾ø[â7Š_ZQ:I+OÔªÛ—.éå£{z÷¸Æ*KÉT-^K¥y‚™Ü)Ø ºêªìJNe•¤ïðæÃž!‚(ìÉ5Ë£ïL#—¥Lå ÍÔÙ}“fJÖyØÅ]íòÁ‚Wî[z}À+µ¹xØËøßFÏ w~ãè–êDi‚( Îã¶\Ûò±~‹N}êgͯr îŽÿý¬)õV·Öþǰqg~™­»N«±¬A„hPs;‹\¼ðÃÇ"¯žôQni½kö}AÇþ¦mÇÿ¾×ÎE 8Qv(µdSˆñ‘gaOÖq8Ÿ_ yE%P^n®<ýòT¬#Âxë1èµÍCƒ K|,´‰‰­¢##Íœ8×S_g#›²ì¯å¡HVVñÞ§^JÖ × Õi ¢ƒg¦Ý½Ä×pW¯ÿÛü¼:«mÌ\‰ÞpáBpÈ㯩ž Àd ÿ©¤°+Þp˜%ÙªK΄„I¢øãø±·\T@¬¥mÏŸmµæ|n—vgO - òØ›ƒ([­8>aó¶òïìxè…”>ø1Ré{œ;°2ª‹ í#ò9èt~_eÇ ÒïO~þ¥»ËtF¿؈_& q çIüZqT²Z—}ñæÌ4j.~Kœ¨?.‘êÕÿ‹ñ;Œ^(>,ž¥ÙúHF?só€Ãã§FáÔìþ9ó¤8qö=öô³Éôç—ᆭô½_õ?'Ê—8ÛÕtˆ2‘á•[÷Ãêmû ¿°É @ˆ1B‚À kb]‰·Øü¢R8vê––Á¢õ;!,ØC’:À°ÞÁ`ÐFƒ­*H³šú’ëâ $£r{Î߈2=؈8j† »!°mŸn“ :Ýã²(¶Är É$…˜J½ÅlsSPª¶5µSˆÙrÁkûWÃoE^$¶Ê?ß¿†\`Òhå³!á‘ÈhXÛ©;›J¥˜sgÿÈ.ÒÞõÑ‚jtÓ¨IžŠ÷ &¤­Í…É <…ï¬ïÌ%R©XÐ[ËëÜ÷™Í:ALñ 5åƒF²jÚf,0„©‚‹U+×è¥bm\¦ E­æ• Ó^9e±ÈïÝwôÃßÿœ¾)¤éâÂþµÇiÿ#ù•Œb Sûß ”µ0ˆG‡ø •ÚZÀ •J¡r¹„}ÈúÞd2p$u÷‡«WÿJ}OQqTßÿMŒ]Uv ?mI’»3ŽÁÿ–n‚ …%ЩmK=°tn FƒÞŸšâ]KMå°?û4l?‹Öí„õé‡àæ«“¡kBèt•„Ù#•s¡nC?Á;¸^ø•E™dBwÇãÏŽ ý­«1ñgOË—? ‰¹'„³YqGp^~$ˆQê2¼3#cao\áPlëë`xðÅ—þüW—a[èaéÏIĶŒÄIQþ- blë£râùЦð°`°˜ÜÖ÷CsþòGŒA4i p4$2›wŠ>Òæµ–ÝÚ>qOâ‹¿zç¥Ø(…0ùcûHg[ÿë°ÿe16R—!·Öî€híA/”¹­ÿýÖ÷årœ¶t‚KÏè3Ð~VÇÁ½&ÅtK|ì¼µÛD1ÚUÝÿœ(«øÌ#+2‘ä[öÀ¯«¶CˈPxäš¡Ç@;v½,ôêÔš-‡Ÿ…_ÿÞŸÿ¾Fê W&w½^Z­†[—ASã¶ v‹2ˆ~A”‰Òƒ€î£ú{ž™òD€1èÕˆÂ|ùš=Û¡õ…³6W5‚í}ðeºž8B‹Ó¬,ïÚ;úlHèO}å…Ïù?š•Nõ–%'¨±sÛð” ˆÿlVvVœó7YyßW _ Ã…ý´'ƒba]«aQr`į÷¿0}êþ9‹úŸ\Jüí¨Òÿ!âi¹‡áh¡=Âûß¡ÿñeZëvÐ"œÕµ…eã¢ä°¨ŸðžùòWo¿þ>f-Wsÿ3¶ïо©ì$yóøeå6èžÝ2Œ“äZú‡^"&Ý:Œáµxýnö’Qf2Ùla/„+OjE@¶eYV½ë{@"’:\îzòù§‘$Ïêrò˜pÏÆU$ÉjYz>÷n\©!¼`¾ùÐK)O£bô<ò'‚ÁÎÒÚ˜—!ÜpðG ¹Jðti£-Ðf:áõûŸŸFýO×’?Uú¿•n04pžIò¥ßÄ>Cƒ>Õ^tϤ{'B€‹jûŸe•ž´dIÞu(~]½z´owîz]Sþ‚ãzGN„á¶|ó~Øqà8’e×%ñœ^E@ öú$ÉdßVçÝ;I¾á‰cƒCÿIŸ|]ú&A§NµÕ£áDxn¢¨yã¾ç¦ŽBíüé™$’Τ;>ùêì%‚Vªu¦oõt€5!¬3ÂgœýIOF•²äcí\ªÞÞÿHúä>Æï­Àûßä'‹p£{'ÝC±œ#YvEŒ×òøÓMÉk øº""Éeefø~ùænqëp±É×jù]ý„[tóPøcÝ.È/(Sy9X,VfYö»Æ4 …ezH²$€FÍOºo’»…¡M›ŽÍ£¢cÞ‰Dw‹Ñ»RýÉjZ¿„[‹Â9@g˜?zô]KÛžK"éjÐæ7Gw‹+Ž®à}_Ïs‰°kVzBCÃ>êÑcP(Š¡kKí瀭ÿ †ù¡šÓrRÀϼÿëÑÿ„[¨x"cbÞ¡{)Š c‰êú_í'c= ÷ï"ä`µJ°2u/¸wýОܒ\Ï.%Ëò Wô„‚¬Û~J1:F9’e—»`ÔTÃSŸn,‰毨üUÓšˆôi‡FÑŒ3QÖh¢Ñ'YÃ-Éõë&ÂmÄžmÕ5~r¾j&ì ]eAŒAŸd ·$ׯï©a7äØj§iÙýÊË'á.º¶èSë9PÙÿ Æ O²†[’±·ê‘· 1j¦&šî¥(‚ÜïT×ÿœ(×£s=Y„¬É‹Ö¦dÑ-øÀ½†¡MøQ”m‡ŽCqqI…UÙæ¯Ü0ɼ´»À§Ý¢Œ¯2jµ(Ó=“Y“qö`F·à>É ;?ÂQ¯ÓQVE©ZëØ9 ×ë&·ÁèÜ'¹:õøK–ÆG°¸*­ŠͲ÷F·à>ÉÈÔc“ð#CÃÃÄâôEIuýOΓJP¬É‡çBAQ‹â ÕüZ ŠˆQ\V„kY™ Êqô=·*«¯KeÀP£ oLjœTY’PE²x\>꺾‚VÛCÀ©Õò¥Àék†£(Fß:~r?TX­Ev01CÀñ¾wÓÙŰ5Q£o¿·/ŠTU±¢™UúCÀñþwCÿŽ‚FÛ‚î©(Ž|•UÕÿœ(»¡“Ý%ÂF”­°ïð 6™ÅIæ©áŽ49K&er¿03¢låî ‡ÖÝìeœ³WeöÄF“žÆÈ¸VÃp‡Œq’ÝC“”G8ž!!× ª&ʤ#éŠq’›d_y¢Ñ„%aÞ¢Åp”OטÏT4JÿcœdO@ÑädŽÔ÷tOÅÆ“û…ªúßnÁir=£Â“Û…Õj…sùElÆ=OM&rüL¼ÿýJ†€V+Bó Ú«ôíÚ®Á¨XÐÿ7#ç N†Ò²Á²Ü%€p Æ i&?Š~A3lƒúhæ>žTƒ=„Ôœèd¡‡7³(ëô†8œqOÆøÀj×[͘Úu£8Ë4ƒ¡IÛVàLxS\]5%Û9€:ÒŒ{îœLDMô….g1•‹5Ú6X?]ce¸¨í°÷?͸×Ä'qÛiBq–OŒ~bˆC¡ŠEY5ýOΓJ$,H”ó‹KÙ´ÔžV뎑}á¾kB\d8ü¶fÐ w Mž†Åv5TŒÛˇâ4ßÅØ>.D” g›'õ €V;)² †z4³kB÷K2.ÐCÜ Ñi£‚ËJÔ¨§]aÛ@¥Ò5¦ÆsÀÞÿB>ïÿ*½×°?ˆ§@÷T”B>ʪênQnXߺ­4¹](®åå8CºÎó]Ù,„‘d"ÊÛæÀÞ¬“Ü¥-˜1„Ú" ©¶;óêгcŒÜ´Òe±‰_¸wk;‘ë÷¾[ 7_Õt8ûÝ/+·3à×ÿ»:µ‰†¯ìgÎÂO«¶ÁÉÜ|ˆ‚†õ‚¶-›³ö¾ñå2¸gÌXŒõË/†)÷r¦Ž‚ÏÒb‰E½ Á’d¹W0dzòmŸ! Û‰²ˆ!|¦†óŠé$¡…,Êtqê5¢&Ð`6ñ“ÁpW2X-ÈA…`”§|v'|ÕòFk?HGƒÄ¿$¸«ß9„©¨©ÿéSÛ9P¥ÿuB9¿ö•ŽsÚð¤{*ŠRH²jú_m#7Àí¿"× "pÞJTÕÞçXuÑ8E6¥e÷@úÁcpEï0,¹¤í? nÞËŽ•™­¶®˜mÓ•¿PPÂÈu³Ð@h¡!F¸}xÔ3‘åû g 0èࡆ`\è06‰Š"€Êþç÷ ` Ðã”ÓݔݞY£²G™H2-„7O*B@ì'¾ ‘Õxo"èæM >Èeþ ôÄécûš@«÷PçOô†d²ëJ¹ÎÔvØî¼ÿ=t^°¾W^’Ts …xRŠu“qdöãy¥¾Z¼ ÊÐz]f23’Ú*ª«”ˆqRÇVp9ú-S:s¡R÷©•Ȇ!,8NŸ/°Oµ]‚Ñ&Žœ:÷^;ý„õÌb=ÿ—µp­ÇáH¬)µFëòÝ£û³mOþ “I¦8ÊDòmV|nQö$ê®ËÆ[$vLE~Yu$T±&Ñš=ÄñôáDÙõîu9'^—ŽXÓ¶rV¸,ÃCízUèè¡jš¶Ø lé³ãÛj8ìúðþ÷Ì9ZqOU^’ìxûºÿ9QöL×K*^|6òV¯Òu/”Ô±5úêJ°qW&toËÐ"·-[„ÙÆE†Á–=‡Y^ûΊ k-VÙÌc¹¬M?ü™f/JƒëN"™Vˆrçxï ü#|ÉŠŒ(ÛI²])¾ásОŒ®É¶„kõ¬¶äxãV›¥KmX5Të†ÊqwyµêåîvúZž‚3­Õ”½Ô¤ScÓ…î­ ÎªèN”UvŠ™óVêÞ>Žù(gŸ8¿ã`¾Æ f~ÈaÁFÈE¿b%å¢E9@¯eÇ4èÏK:Z,PÄŒ“gó•lN×,Gi<º]ÄE…WÉ£´U§¡Hï$ªSY¼S#¯ÅUðÌ/©Ì+Pˆ 5&UÝÀÕ›tRŲ†¶¨Y·Tö»ÝÊu¦FÅyÿ{¶W”¾W ÎÜ*âÙ÷ éc/ïû³OÃÌG‰f²Û±œÉœ… mwŽ·Yœãã"‘hlÝ— ç Šaãά*mŒjÌ|–ó K˜eºMË2௭û˜»E9ú8ï?rºJþ‡#`C@.U@ïq›_޲C•kÕ¹‡8EIÓ:nºnB Zß?œªÎw6Mp(4¿| ÄÜ2Œí:6PškÅÅ~޹†”çs5K„ø^ ]¯‡A«§1’žM] ÎYÐ×:臭({ö¼qYºb]u¹€3¶‹‰`>É ÑªLDöú¡=¡ud3ø|áz˜÷ëZˆ q—ÛÚµÀ¨º%ÀÒ »á­¯ÿ„H$Æä›¬¤Ëb ºy¼öŸ¥ðÝòTf‰¾ýO£…úuŒp1ã“߀êñ¢á\QÍéÚ—¸;U¨ ï@°[”Aª<©Ô‰[¬៿ÆûÿáZ‹0R‹®_/×òÖ‹ˆq؇¯ƒ®{ÐÄàK.ÎLéÑ„_œ¨}Í Ú®ÜQ•[pw‡"(£Aº&vÞ Ò 0¡³KêbZƒ±mœˆKœdj7鈹uh‚Ãg>s’Ã=»4ÁÐúá)ôõ:Húr-t|õßÔ±‡{„7ww)rÔÿ¿WÜ·Îeu"ZãÞ[*;äÎåÐ,nCÛ€Ål¿½º¬ƒ«c:\Cï] ×<²®ž°z™DšÝ”„»;tðÜ•ã횘 o¹ Ä“oª‚î#ûUù÷˜þÌ'™t¢°oŽéÆ+“`ÜÐ쮡Áq:NI4±Çã· ƒRôs¦H”âãZÀó÷ŒÀƒ¶ÉÖôŠû©p‘ŠO­‰ +8s’ì)”ë)W€ReÈŽ`þŸ½ï€ªÊþ?oJ&™T$!„’„¤'TEÔľ‚kÅº–ûwmØQWÑ]{[QTPDéÒZH!…!Ò“©ïýϹ3w2 IH™IÞLîÍçåÕ{ß¹ßûæ½ï;ïÜïõØ]¶Ö4•nØîSSÇxl›>y4ÿvTìHkw™ú±øÛÔé úÅ·$“~¿÷’ !ÞÚØŽŽ(Ú 3Ÿ÷ n_ÉÎö÷¾ç‹®ùøQ¨;r¨]–J:=„žGÞ{JׯhW­Í¤ 2B@ïXÈ}þ°œ.þw<±×ß ‡þßÝ­-¢þ8G'ÙNûÕŸ¸UKÒþm#›[¿¾l–œVÖø ^ýÏI£‡½?ß v/’d:¯ÕTù©ïÀ‰¼5` ëã¯ú ú¿ò÷¼ßج֬»·=-wyD¹Ë›@½nrs©¥}”‡äÞ§z‚ÜxXÆ»bôë{“ú)8ú‰)xÙÅ`Ùò‰¼ ”ZX~üL«~ý9C!ø¾[A¾–Ýû ö­ÿ‚¶__Ü~h ùØ ¨yû°e"‘BÙéˆÿ¾UϾÆ[oMŸh¨yõ]0Þò'†^ø;/‚yíF¨ûú~pèÇà"ìØjÙ¾êþû%žÛõb¸h*ν4=#Àž{˜•§TVmúÓUaa`KÝ5o}JUuƒ–‘PñF©­…êg^°/j°O¬œ‰@¿;þÖ’"0ÂÇœ ÕÙû¡ø›÷¡æÐèóÇÛ çy—€½®"§_…ÿ]»7Aä´Ë ÷éÂz@åÞmH‚Ÿ{u„¿zM¿N­ýân~„í3DǤ7@߀ޗÌÌÇÿÊæQ³ÿ úˆ^P•¾ Š¿ûjð¼”ã˜W88aXp8ñc_¾ »6B`ßxè?ï 0Æ'!q?GþóæÙ× BÖÒ$É÷»¶•o[ q7=äZ 4Ì»œ¹éÿAâ¸û 0$ŠsVBöÖÑ‘cƒqW|¼É®údÅ >:4º@vþBˆIœ…ò¦f(Øû’ÓwYÁç?•'@Px$¨†CÛ^†cncû¦Ü°Ž\ y»ß†½ ½â¦€"Û¡äðZÈÚò,qÇï¸oÒµ0î^0G±²ÒVݸh{â„û! (Nl„ôõ³íî5:U¸  Ð±ÅR{ ̵'@è?·ðæ™; bY xôð—òSÈ’Ò“/ûë\h}Ê(0̘uÿùì‡rÁxçM,ŽØ~â$Ød‚|ª ª_yÌËf0¸ ”šZ¨úÇó`/(„à{nqÁ£‰î ¡ 0[÷Á ƒyËNú„â(ã7üìkÅÁvåCõs¯CÍËï@ÀÔs!à<‡4£nØ~èN°åâ~<§ugIÖôìÁ¶›W¯ƒê½ÚýÁ0k†ë¼|A.> ÕÏ/å&E:;z$»±7ܲ© ÞýôŠf$˜r–ïÞ¶ª (ß±¿ùŒ<ë{ö†JQºö;È[ô0†e ‚Þ]ÍN¤ ‚ÐQ¡ßíÿ€²m¿ÂéM?ÁÉÕ_²}'W}…¾Ä–-¥'àØç‹!ûŸw€Æ`ÀØå;Øvò>úÇ  ‡#ï?§~ýLGóÙ¾þw=öÚj8ôÌÝPW˜ ý1Äâl)døFüÏv\wÞ#¦¿EYËØ.‚~#þ Q £„© ŽgÿÀ ÉÚú<ì[ó [îOû΀}kÿ9Û_¡çýŒáØ>"±ƒ'·Þý§AΎס´hœÈ]û–ŸÎ!Û-Pub?¤­¾Ëxú ¹I÷¥,DL Œ¼øU$Èûa?î;yø7F† !ÑlûÑ_BêÊÛ ´WôC"ÞT¢ðŽ˜A³`Ä…/!—áXÆÒ¦óÉm£ì“Í&Œøtš+ŽñÊ‘þWækT÷ÉR°¦ígžaÃÅ€nä9`EO¯\zs#Ì”SBï²>i0T=óÈ•`þe„>ÿhbzƒ\âxÇ ïrõso¸NDd™ˆ2‘nžLß­bhMtÈù…Œ¬›YHØ•š¨Yô6ËÃé.ÅIÛKÐK´e+ºk’ë `úz9/RÌÛ‰€½¦Ž,y–å6ôî±ÂPŒó®;œ ŠÍ –’ãP‘Êö÷BRl9Ü0m¨HÛ “.„âï?bû5†@(zçM8½å'¶NqÃ”êŽæº¼Æä!¦D¤»êÀ.ˆ½îNv-„ ÍB'rž½*÷leÇÐ?-çÁ#!÷åù`«8ÍB8?/XQ}Ѷc®ãÜzN™ã§BöÓ¦û>±Ü⼟àðžÿ²}Ϲzõ¿Š2—!aMgÛ*Nà×›2GèE¯‰üµ¸žçœrÑÍÃJº€`Øðé5`·8«Æüí—mgûé …@o¶ImÕ© è5pÍøb“®›¹Ã4è«€ë«D ˜¦ê"(ÎÅ{¦’‚ß ÉuÞî³u÷}±ŒÄñ˜¯ßì-/ Wù¤ûnŸ^DÙ§›O/ð$72ÞßýÞ£Ì[ÎvÈ¡Cä—’„qþM%ý¨áRó0’gRªk@; ¿‹([wîỚœSHEð½·yåÓ§A JµãÓ«¶W$Øs³‡«{fýØ Å|áoü«~36HG 6¯þÆZYR¶=Pìg:jè{ô‚¤?­ßרˆ<·”ȃÝcòEx q^OZ\Ö2o6å«É=Ø {؈qìšxÏB×v"÷A.ÒQIî~ Þþ'ó‚»2‰…&¨,ÙëÚn©+­ÎèZw_Àq½!;åõˆ±C.wí2öLt-Ÿ>¾ÓE’]Ý(¤bø´ç GŸñ`ª)Æp>® ýpó†¿ëÈ~SÁ`Œ‚s¯ûÑURs}{È“»ëmë}¤\þ1#â!}ÝÙ¿>¸ Vñ‚ Ê*naš@ ;! (t‹ºB¯îRwÅÜ:% ¥ÎG\õÄs`Ãð‰ ‰%ÅÙa¶Á>·JÄi@ù­gqÉa‹þϵW®ªý ‡ºk#.Pì´|ê4”ßòw÷ÍbÙPØEk“½®¬¥Åpàž+›Í"[,Íî3&ž1Wý…PTíÛQ³®‡¸[a/FæA)°Ï¨©Úç*ƒb¤)QØEmn†k{S ACâc¯Ã±ÏÃéÍ«›:Dlk„€ÝÚºöWdÈ6åöÀ'è­}¾Q)ŽUÙjjr;ß8c–âFÀ†OÎcqÉÿø ßVs„÷ÆñFÉf­bå Oi´§éUE¶Bʼn½pêÈôŽ·.OÓ%©k«ãîª.›„5@7DûçÕÆ®Î}ùrwÛ ‹€âŽ5½#YØ…-ã(èuºþjn!aŒsÀ¸Ñm‚‡ÊRÊÊYG>}ò(”p«×Öµ¦@sO¦¹¬‰êÅB1$EÓ²mž/ u˜gšž’~k“íÝíàºcù`<4AÁ8¡bç yˆƒ¨Ëþ ¤U¬‹ˆòà¶6Q 4%+Æ)SèDäô+YñSyõ¡ý,Všˆ´!v„¡÷št—«³ö =Ý1×ÜÊòPxGØØsùÜþbúÁà'Þfá59™4ÉÃQè†HmG ¦_„zöúÀö•áDÞÏèM¾"ãÎů:LÂå­.<04Ì5%؇׆$v*ôˆïÊ[zdBb˜ær`h_ŠAKPÅ‚b޹•ɽ÷fiWFçuø#Íf]@(+;*þ"ôP7ü:Ñ8/­ ¢ìK­ÕÈÖÕ¨eüè›Ë`Cê¡{>Y¹ –ý–Ö`›X¨ƒ+èÅ·bñŸ*¤º 7ë¶Ý¬Ã^ÄÇoBÈ#÷0pÕ³¯ƒ¦seBм›XL`km4¯\šð0èñÍ!èÆkÅ+;3[Öoó ”wBIºˆCðíahëöT¨ýøxü<çñé[ Ÿ˜ÜÚSŠãÚ‰À©Ÿ¿A¥‰¡¨K¼zLºˆ)P¡EìÜ»`̧›aÔ’Ÿ lÜÔV—^‘ú;*]ì†s^] Ã^ø2–¹òÚÑ£|ø­§àŽ„áo,ƒDìØGªr] ä½¼‚ú%ˆ·—£Nò自‡êžz`¬4÷ˆ‰Óa賺¦°õdÌýx±Ü2¤6Q°ÿ6õŸ0ã¶4¦€‘¹ùÿAeÉ~”^û.¹'È#Ú§å‚ÜöÙ÷ *Wô„? ÖñàIó!?í}×Þ¢¬ï¡`ßÇ0äÜGaÚß¶BÒ”§±‰5P‚1ч¶.bI#yÆÍ;¡wÂE®||!I÷Äk–ÂEóÒaøôç°3âr8¸ñ)¾Ûçç"ôÂÇ›tŒÝ‘ÉIý ÔØtl£WQ˜ßMè·uk]NʘR쀂nOExÂrûSõËoyÀUó†ß&WBEŠÓ—Ö÷(—Ë+ òA|`…†0¥ :Ž:åUÜþHÁŽXFRÀ` cUÝó:6˜úM|Ýv ‹…PÐ@$\Žï£Ž?5ï|5ï} šÐ`ì0XÅ>ËÓ~Óÿ~`“&" d:'ÚÚ\’ŸhÒ–æŽï.Û)t!uNŠ«ºy/#átK§7®šx"Å‹òëA$G©_ThÒ…÷{M5Æ;B-¨ïÄÇó“R…ûù«-Dµ ôNËΊ“kê?¿—oÇóᤠCi@ ó¡ã)QgÂôû¯Þ9Êmœ¨C!ïTØxŸXw @!®° ôÿôfÿФþxKƒõŒ OCöïÏãÏRÁ° GXEêÊ[Ñ›¬½!,uex¼Âò”[ãT˜þ%ÐÄSYÑ  ˆ„KÂñ}TNƆ§ sãBÐE€¥ö4îr”»ë-Œ=~ Œ‘Øá¯Š©gÔçs,¥ÿö(¤Ã£Ì£l³à}ÃÏ’ Ê>Þ CúGCñéJXý{:̽¨þ&ì^­­ûó`󞨪1AB\o˜1~(ôî ó³!¨‡ ˆ†»³ñÓŠfN&³Öáº1P$ÁQûú±â¬6;Žª·äV ç„û³}™ùŰ>-ŽŸª€ØÞ0cÜP„çzGã›’¬Û•¥8äõ¨Ä¾pÕ´1@ßf—ᛵ»áPáI’…sp˜ìYç`£ùÑ+7ïcÃj“ób4ÚpÙ”‘ø|nÞ÷:‹eßD@Rà(Þž#ÉzY²Åá̯ˆr{Z¥±^1•á"Èí)ò;u“›Ìn·ƒ\îèXØxsÛ'Ö=„’$N’ÝK$Šö&N’›Ëo«ÄМ&RS¹‰ÃÄ&"ÐT 3Å[êÚßþg’äzƒìDj©u(èÔou,5·Ýý8$ÉT?záÞʾ¸Œ7ÒË‘@îÎ(€#'šþñD„Á¬sGÀ¼k¦‚ ‰æ¯Û3XM-èÊ>rÒs‹àJ$¯tÜWkvÂ΃Œ”†ã+6îw¡²=ý0d.FB>.žx¬Ú²J+ª1æIï6ì~Q=à¶+§ áí=Ð#E©¬²–ãÕ“FÆÃ$Ö»2 ààaG(* ZB£Þˆ£Î½xìÍ.„9GY¾Ÿ·¦Ã\?wt"ÌÆúÅÇ:úv5gË$þù>’┭ǪÈ0À÷+$j ¾Œ€ Ê*j= ]§4µ%‘WvxB,óÞ.ßP/5ã^Æ0$®#c!48ÑË›]XÂ>çðcþ0qÛ?iDÛ>c|ŒÔ¦Ž Õu&(9íø”’‰èañ1лG0$ô„¨ž¡°?;i6-)ƒ.÷ü1ƒ W„ƒ(Ó9F Šƒ Ãã‘\ƒ8$ƹèAæé|<Ç€>=¡g˜¢{…AwJiÙG`äà8æ™&öðG,Vs6ðòÎ6'x9ÎmÅúle‹ý@@’r\¥(àø\áÚÐå ô-Ò}êrƒüÓü®P³Úªè´Ù¨6ÛüéÁoŒðVSíïýÖpÿý«¢ýEè…÷½Ugp'mzÌ-ĺÈ5 ¯˜:^ÿj-ókµ ÉöOèÝwè…)ƒcÈÌv ²(T‚RpP›ÇörôT6:Ö)ÜÁŽ#íä‚‚âÓ°Ëâéd¹#^í/³&²u©ðÚ—¿À˜Áýàêc‘4kÙaD€yŠé…'(¶  e§¾ÃN‡Å¥Õu`Ä^ö6› 5uˆsÚÅóžÍ~\Ss³Å†uoˆ çŽ{SùĶNF@úž©Š¬&¢|ÆA‘Ù@IDAT Ûf·Õ™µ:Ú~æ…ÕɰùËéÌZ=ê%׸Õç ÜÝöuö¢Ë»b¯5kô¢í=ÜM€b·Û÷®Â]¸»¶tÍ‚Ëj« Ú߃í`U (ímkJ/Ï…»Oצ¢Qn\sp0Ô#%ŽÎ­=ctd(†7$ÂO[°øcpp\ //ÅßvÕÜ/ ~ß›?`œ±û£]ç$´ü\ÜCÌ×iN[‡ÓäQ ,ŒÃ}-÷‹î÷_w!’èB¦¸¾3fNvè2žtz¤é¸Sªé ΛÓ±˜æÉâ’ßùv:0^ú”“„³øïl6ðãššWV› 4@|@i 5mCÿ!Œ&r$IuåPYMæ²êˆâaÙ•Ž­T)v«µþ“SÇŠóZnÙj/©5„ˆ¶÷0Â5ú`ÅZÕ\ÛÓ\qÔþ¦ÀpÑþÍÔŽí&% ûV9¼híÈïÍ,‚9xÝv”MÎ0£…<Ôa§º¶¤?LLbžâ Œ#橼Êñ‚ŽñÇe5°c™Yâd„ØŠùp ßHË*ÄЉæa..­d1ÊÔñnOöQô›14£D„Á w•x0¿ˆy¢ÓóŠ˜7™;%ò ‡ R‡ëL±ÒÇë; Š‹‚;}ÏE硎‡”š³ílæáXm2A p—OÍ.6w!’ÎÍ£ ª$ÊôËaS]mÍ©ZƒA2éë¯õ.„ÎçOM8žV³¹È‰±Zë„ts‘Io”Ì:ƒZmô9»KÂÔRWG0þ;Sc=Xû[”`É¢ªÑ>Ÿ³‰p$<éžêÖöí`)Þ©º ÊÞÁµ]¥r7 ¦')5aÇ9G¼nk °…KЋËÃ1(_Ò€¦tñêçkañ×ë`"Æ!·7‘êD_ ‡xÿ‡-ð·¿ò§Žßvf³®‚gÿ» Ã(aòˆx×iúÇDÂ?lÒw¦º™¦4=àD®Ÿ^²ÖlËÀØæú/íWLÍÊùÏ÷›áµ/Öb,ô1–§9ØÎfþŽ„gdˆã©‘,ÓŸ“07“Elî"μê¶ ûôŠmS”’¢¦úøÃ›ærᡬ=¸ åöŽí"´üë´„#áyâè‘ÍX3޵Ú*Éì"ÉÖ#¡õ÷9µêkö–„éѼœníOx«)5hÿ¶¡j²Ígm!©í鞊•qb8;ç]^/zÑåMà0€7äoÐÃ(Hš¼·c‡:¤Ùš2“”,§IHPiâ‰Âæ]}>‹y6èÍÍ÷S'9.ýFÇ“§÷Åû®áY!C$Ü׃ƒ pÓe“ÁŽM8T®G#{)=tÃELº¼Ã$ýF‰ö¡ý£P)c³!0 þ’ Þt –ec¡,“ó_ìÜw÷œi,‚1Շ׮%Üó»/§eÁxi "‚õ AY;²ì昻+–»iáB97e4I­L$Kê4òœ­¥å.Nî7ngÛ¾çÖœñÓ.¬8Ø·Øð¢ç/¡‹­ôáÓ#Ž8R¯õôú—Qû»ã­–Z¹l"GŒ(Íí9´çà²LÑöh!ÄÛßR¾kã¯XýÆ\x{ xOᲇ·¡mtÏ~ú½¢ý;ˆ.âˆ!WæJº§bQìþŠsÞ,¾ÃÙ…G¹Ãz®òvj‘Ó°¸HÈ*(†ücô%¢ã‰“䎗„±ÂH4‰°r’ÌˤNˆœ$óm4W˜Î ÃüwòÌ·ñ9ÙÍI2ßFóælp?†– ¿¬‚mÔbì³'²Ñ1Þ"©Ï%ß"É eµ$òt𛸠—m…ùù?çõŠ– {¨Éñ­¸ZoáG8––”|é†1á­¶äºJO”|y$´¿tÜv ­è-Ý7M¶áhi"µÂŽ0”­æ²M+¾ÿK¡ß–¯3Ûßn)Ýgº\¶)¢ýÛÓú„á‡Þärº—:Û^uí/ˆr{Z×KyHm‚F¼#BgÄ}ã£à*F,ÅÑë|1}ìo3™ÎsWØ¿tí.įâB%Â0’dÆq¾„sSê]a§8gC°i\D$Õy”9I¦ñ}͵µUÕ©Û6}r*4 VGR‘ÚˆáFøeíß»èôémçJµy©fd»ÈÖì}{•E†þ‰¶'tÚ‘;ÂpÿÎíïTU1©#úm©õhØþ{÷.ª”£`éÑþíh{ÂðKû}Ó't/Å"Ø}Umí/ˆr;×[Yxè¹@ƒâ@ÃbÃ`ŽV÷ùê>íYöfM•KžäÏWoGÜŽAŒQ¨°  „@šW—<Ê"ô¢)ôº~[`Dï}ôÇH†í$)³fuXZŸbU8…t°vdyé!n‰$eêÒ6oHË=¸ïûŒ>qÒò1“áYFTZ‘'‹p;–ŸóÁ¦ÕË7c6•ð%œÙ5€s5¥×ÙL¶çF ’~8KžåÖ7aE˜vÙ_íX÷ËvÌM¿)5_M¶ÿQëpiWÝuŠð,·®ý '‹p£{'ÝCmïÙöW¤PP¤ÊÖYÕüQâ[aóØtúòÀÓ£L»à`#Œ…pØ`îQ()«„«.ñ}E{„ê J‹“Ñš­±HR$’KéH˜—¼FDà¬8Ñ Æ }(Ãoß-ý ÷j`ø¨+N…„*§§iú•y¦–ïw‰b’)Ü‚<É…¹ÙÿôÕ§rAx®„/á¬F/Ý×Àª/>þræõ7ê qÈ_Ë#”ó 7húÔÐå&RsPL2…['9?3ýëµß~E!äQTû5ÐBûÿk¥ÜKeøQÓKWÐ\Õ»ývŠI¦p ò$ç¦ïûá·ïñÞéhwo´,{WGAD¹£z0¿ƒ(Ó`èQÆPà`0£ðð½QX‚¬Õðî²0t@4ªaô‡¤Ñ@’pÝ=‘N2IÀ‘ºuÜ£qEúmÐ;,aÁ!Ál™p%|Ê¢³²j¯EÚ‚Gé§Û+Eë² @¢Kº{'ÒI& 8R· Ž{v«©j÷¦5_îݺ‰Âì¨Ý=Þþt¯·×آѣÜá·A”UvýR8uæ£xZò€Zñ‹CzB<!’qy'+!ÿh #„¤:‚Ä/ 1¸É®©¬J^3‡†¥®ÄAKªMf¦“¬G‚Ü+ÀQF Æx1…°0œˆ,#ž„+á+Â.¼Ö,)ŸFDžnv&Q~±#k5ÚŸm²íY¹Vžå|Ö²èÓ+Ũû!ïõâ¡Þªxõà?5{Oê¡É—\:Ý6hèÔ¼ÑÑaø3UŒf³bª• v®v¯DÃ|WLÛ ’¹²0ëà/¿¯^¹¢¦¦’Üî4 )}%< W—pVkjò@¿ùàŽí™çΚ}¹=!ñâ#ú‡SÛZk£µZ2ÈÖn×öÔ€4Ôw­>D¡ÁD¨ýmsÕ‘Œ¿mýiåšÚÚêr<„Úß—®Vµ‰~kÿ©F ”*$½dévíOÃ|›”p…ám_ph߯­?¯ZWWWÃɱWÚŸîõØ\£Ã{?]‡I‚(w=/äuy•‘ÐÉØÊD’qÆ‘»€-ô«©ƒ2’¹¬Îf½¼ ýPócÅ 8Q‘þôÐÑ‘HUBô „(lÅ€qȶäI&’Ζ)ì‚T/„7ÙK âÁbõZýF«œv,§,\¨!e¾¡­ówžy|÷íO>sµ½oÀ¼!ÊôkäqÊDމ$ó {€âÀŠÞ啸}Í‘c’”ÒÓ®Óªs(·0‹¹V–ƒÐf0ëteµ:}{½îTD}©Íf5[N–TÔÕVŸ:r(ë@æžÝÙx‘bzPÒC’È-SØ5ºZÃ.Ð4–š½øË¿|óå×xÔʤ1)Cú:"ÈÒ+ (0\§ 6àÍœ_+΢Ú7“Ej«î¹Yy5:ãQ»¤uý`ÚWªwrÙì6“ùô©ÊºšêÒ#ÙY³ö¥æá™ÈÍê«×@›ÛßhÔÆë¥8üªå’€’}µÉn<‰…Qy~™°íÍæºÒŠºê‚ÓÙ™™ÙûÓcEéóš×ÛŸÝë%8J÷þŽ‚+ˆrGôB~‡WY %”`Çoˆ¼ÇWKaìd‚H³=Î6NÚŽDYfÃW“9| /˜¦š"é…‚)W^è1D’Œžx† …\`L2†['™…^¸¼É¢Ÿj±CìÜ™—“<æ^Í}ñz/øñG’‰£íOŠô†ÊKóžø×Ô%Ï>Iëö&Fˆ13y?9ñqxÒv"|0Ä) —‰T3¯3Îy\TGº8"|Ìy¡!·‘5VŒ ¹¨ø ³Œ7–ö'þð'¬h¢²èIØ!æŸÙÉ“L$ÙW¼Éh*Kg½ð…`/N™x´ûW\×0ÎÕ K*­JîgYÕ¯1«ÔõÏŸ¯V·ÿìÉšÙ‘±ÊDÀVãtzɹE°üçÊZu5—Ç­é²ö§{<:gâ­öOÔJeO è…2ˆüñ£D¤Ð±N$%Î0ÜÂTg““(Sg?-Èrw ÉnŽ yˆ)œ‚ð"U R· 0 Šñ6ƒØD!´ŸpÉ7ÀË~#>XþDÖÚÛTœuˆ(÷Õ÷}«Èvô>èñÍ;.™üÞÂyl¨ìv¢AJxL¾ND¼%D”©£‘$ú!óð Õ‘d´ 6UUo¼(<ì½$õÄÕЫ{öLúêÔ©Í´¯‰ãB$™Â)¸'‰w„$²Lm@áFû G_I¼ÍÉ^^Wþ2àµk =&Ã#t“qÐU––Û~Ä…2Çšêþs\üñh±ý£{i#ÿ<]~ 8H™ŒgöÕýZå[Ó¥7·¦ËôÕR§·?ÞÛ²õ䛸ü8«ëû–'@DÙ(z¡ N‰Ü±eluZ&¢l b$™:úY,TÅ@2†h¸e®áÓº¼H„‚%Ž KM#îQXáCd™ÉÁá dÇ #O2åÉW6"ÿ`Dï¶ÓÑê7:bùÂ…7›îxò™Û±ŒU`=ù¾T^‡×ÝÈÛ“ø€ˆ‘û2>"~L 笣ÎeU]ˆ&¼TÊÊ÷ƒ ú[ÐV˜:‰òw´ÜŽÄ1å¸p’D¡üE‚È2‘IZ§í<.™çÅMªO¼~z ܘxaï@M¡cSàÔºbëO¸H«)ñväùã5ÀëvFûÿåmÜø¡ò[øîgüꋟLê`ÿ÷›5Ïd±ó—UÝ<|ñtIûã=]š÷ä³a]†ãt)ÝóqÞá$ˆr‡!ô^D긌 /pJǤ ‹Åʤã(Ž™<ÊøY™y”»ƒW™°a~Å&2áD^eÒH¦ "Çzæev(\O²÷®So• ­Å&,aLæEùÓ¦Ư_ß¡ß{ÿzjÍOýk¾"+¯á Ðûð·x–éa@€?0i™{É“ìN’y,3=Uù€Ü\^ñѹq±7 }¤ä1ôï}úD½~üø>\nO"LøD¸ÐDd˜H1aÄç´LûÈ;ç‹©Ó¯ó¢ —œÞä¢:ù‹›í˜JãíÏ1òÇk€×æt Û—<¢½*4P^Œä T—HrY•ôÙ›Ëu‹ÊÊ,tŒjホ:µýÉ“L$YÐI#=øÞ3O­ñT}Qö’^,‡H‘B6r‘AŒSfájá ÉŽäQ¦Ô]ˆ2ÕÕ#~›È²±"ÂÌ–‘@sìèX‘| ~[SsPO9½2IxÇ ¶WWLǬîh-Þ{æÉב,Æ ¾,ÙJ†`<ÛýˆYæJNéÞJä˜OôptÒuå UU¥Oh¤ïB4šë ãË"#®E¢¼Ž–Û‘JtS¢e"I|"Œø2ß›|:ñzÐÜk×À )ac£ š‘ ) ,ËŽšÿƒË¥*E®;]ò× ‡ë‚ ÏËŠ|?=Љ (PSrZóÀ‚wì+ðÝPõ÷_GÖþ,&Ã-Ðþá’üä랬‹ ÊžDÓ‹eqï2? 3 ¯©ŒÁ–<Ü‚È1'È|îEsº¼hƒó*;½Ë„ ïÜÇ·w¹¡Â€!€#óaü¥’D… Wùrœu˜(SYŒ,?ùÌA\|¿Ål¸ãÉý„×Ìç£fe;u–9äD‰<¥DŒi¢‹•O¸¨N¢L†ÕJðfŒVˈrˆ$Í^ñmm,VÕ‡SèÀk×À„^†¿Ò›%ìtùõÒ¼š|Çš*ÿ”x[Óœ·?Íùv:Ƨӯ¯õ5Û.ÅÇðd æB hÈ*«ÒϽï K6VŽÿþ.ß<ØÔ¾”x;ÓÜcíO:É 8å긇4à–©'=Éd<%A”8øÌN‰Rr'È|íèÿ ž8.|]Ìý|ñY!Û•ùT|$¢|·§jFa ~8ô˜õؽXöŽñ©R#Ëw<ñ¯ФáÕUí‰sa¹®bð<õ­k«zð­nÚò[yTeÅÀê£GŒ\{ÛÜéÔæ„ÁQ®Š£—§Íùý)ƒ'®PkµÞ˜ñéDÉùåð×Áלs۸ؕjÆ©;\±ºÌˆ*ë²akE¡Vè¼(±:¹»îú‹&`ñOâ½Ë‡îž¼ž¼ÑþxS Á‘Vci0|1ÁÏípQ„:îy*&¹1‚(7FÄÇÖAô±æ¶ øƒ¶äåeŸÆbOdÊqùãÇŒ‰ß¹gO› iá`çõe<ä廞z>¥/Áå8ÅâŠS‡“;Itpt¸`/°wà c3÷ïbņ/ŒÝxΘ#VÆEz½tZ¿.Ö×Àè“{ûh™ˆœ6ö./«r¸Kü:UWîïqƒ ðk—óMPRr,ç祛/BòVŸ<Ñþõ¥uû¥ãˆÀnqñ„NòÙDùl‰ý@—! -]jÏMƒál  5’®@cèªù¡¾òŸê—Å’7Ö¯Çq¶5oó²1¼ë^¾,æ]ƒ€T\xž9†ÎŽŸö—ô}×X"ÎúóK#SÙ.$ÉWs4ðµY«Jž¹À†$Y$C@ekQQ€Ÿ! ú½54zÆäCRNrrŠŸUQuÕ Í™˜+ò““'©ÎÈndb—Ü^V¤·¥…ëIYC¤NF`åKÚ?Ûdëvô"®?µ´8hÂÓg>XK!"ù!‚(ûa£Š* ü ~[·Öalï·¼N’$ßÈ—ÅÜ;ÄîÞ} ÉÀ¼tY’ïçËbÞ¹ìº2z"vHgÅßY$‘$œHˆÀ®%)úU‹4¯cì×çxZ#½ÈÕ(GzÝìGå¦O/.Ø~*A”;rqB€@ Íh¤Ïêó(×+sæp•¬úÍbÉ£h´é’²„=ù®Í?ž}úçÛļ“°ƒë%»Œ}‘¼´ød'Yœ é·’ŠÔ ‚ô€  2õú 3±íÚ&üA”ý¶iEÅþƒ@Âì+ס§ˆj„áчç^ì?µSgMH]=˜™uŠ¢—eË]ê´Ô­JŸ3^N®å5Ôk´®—¾M̽‡ÀO/ꦛm¦T¼çLægA¥©¥Æà^ã/~Ð’Á·‰¹# ˆ²·¯¨@À/.”ñ[g}(€,ÿÅ/*¦òJ ¬•;1›‡ž|¦«r³ýƼÚ:ÓèÉd˜cÌø¦1Ë‹Òü¦r*¯†ZÌ·Kö_$G‘©jaCm¾—.çN¿ç¤G4ÖU0ω€ ÊâR| p¿øcáäÉ=}Âp62>$ü{dL–<ùùù9¤¾ R' @’p8RÅ<שûK‹k³Xð,$ý¶j‘ô¾ ¼„qúŽ/”~ÓHÚ³•_òìÙDi¾€€ ʾÐJÂF€@v¤íÅP€]’¶@³¹îV‹wRqÞÅ·¥ÒKÂ¥ŒOú®¥ãžŽ#à&ýv•«4ôä é7ÝrAånÙì¢ÒßD;3½Å-—$å.eáBqã€xi.¤â¼ìYŠ’pgÈû›“~3Ž¿`†~ó0Ø>Vœ™ÏǬ±¹øy½kõ£Ëº/7>ÖßÖiønžÄPÞ ÿžkC#þg¯*/ú^xÙÇç¯\>k¼Â¿kݵµ#©¸ÜäÑ~ Y┊ÛÖµVù÷Ù™$œMHÂuF+“ô[IEÚKŠ,»T-HúM£ÑÜêPµXßfˆs¨A”UÜ8M™Æ‰qAÑ)HÏ;§+j ¼º,«k$Íú…¦Jð“mx'3è <Ø=ÂaX|,ôï  Ž)¦Á.H¢Ý‰´ŸÔºÛW#~ýzSnÊèÿà«ác>Üh A”½|eTœÝ®0¢ì”Š{(~çÎb/Ÿ¶û/$á:¥íÒoKñ¥Û¥j1ù™z½þ¡jÑ)Mà'DÙ'š‰b2F†Û™ ëS3 ¢ªŽ†2…Р@ ƒ¾›5%>­+ªëàhqTÕ™`å–}SÆ †iÉI`0èA«Õ¸H³4³0³HÍ»`VÁß„çN˜0$qÇŽìVd‡´’ŠÃ”Mx:1çRqÿ×ÎâD¶ØET´Y’p-@ä‘]Né·¯ðšfªT(:W–GÞ"T-<±ßÒÍØ•o¶›,Ëp ç(|ùÓ6(«ª…¡b`Öä40‚ B­©ÎlÌÃ' -«VnÞ[ö‚?^˜Ãúz\„Ù7[_XÝ„ßÓ rRÆüˆÛ¯Ä÷%I²[è“é=ëžE@’4‹E>ßYê(÷¬´t©Å³g¥™dÍ]ø©„KÂm’pž¿&˜ôØ_àªès²áäq”~{@ŒçâyÄ}»DÑFÅíG^d»Ý¿l?ï|³ÑKzç5Sá–+Î…±Cû ’ìl;zY <Â'×?X¾ÖîH“ÉV«áHxŠä'(šÅõ5Qn£ÆÕ£á­¥FRq1ù‡³çzë\ݵÜ&$áÜ®óçêÝ’ô›ƒ${î\¢$ÿA@e•¶%‘:ò$¯Ýžßý– #ûÂÝ×Nƒø¾½Tj±:Ì"|î™3áµjËY6›Y&<YVG;uÔŠA©©¿aèÑv**vÙ2¿£eŠü-# ­_oÃoó£Yº/‹¹gŠ éåƒ Ž×÷1! ç\©!ýæ9,»[I‚(«´Å‰Ôí?T߯OƒQƒâà†Y @ïÐ>W©Éª1‹p"¼·5Û3aoV˜ÜȲj †t Eû/^€¤ÀG'Lˆäëbî„Tœwpå¥*Šûˇô¶´p½ïóö#°êEí 6ÙºC-×—"-Òoõhˆ¥æD¹ylºl‘d“É ÿ[³b"Ã`ÎE)]f‹/Ÿ˜p‹î?nÞ•Õ`¶XÀf³3O½/×KØî@ 15õGÔ6ÙKkTl±[ÿ.°ñ.$‡`“TK('¼ÊŒΙ$I¸ÂØ ;I¿­|Qó~Ÿý wi'I¿i5šëf?*?0}ºxi˜XiA”›„¥ë6:â’eøm×AÖqïÊ©£…'¹ÍAžå«. •µfØœvêPÂdÙn!í„TuÙðóô³Ü(üíÜ[:qb_sï @Rq¼d|A™#âÃ9œÛÁõÒë|‘¼´Xô*ë¤Né· ø}¿«’~ ÐOpè#»¶Š@‹¢Ü"<¿“¼É6› 6íÉfê"&¹cm@ø‘JHê¡cPSSëô*Û„W¹c°ª&wüåW}‹d9‹ ¡w"*ìfÙP‘~fIÅ!æ›Xµê¥âü¬–[’„Ã3ÎágÕk´®—¾MÌ[€Sú-û/¸ô‘IúÍÜk¼ÐGn=ŽâH‚(«èJàÞäüc'¡²ÚÄ”TdžÏšBŠ5& ®&“,V«ð*ûlk64\Z¸PFÙ²çøV|0>"b•9Þ›“Tœ[é$'t*Ýië"IÂáý_Hµ¸&Ž_ý¢æ»dÿïL™¤ß$?‚ªs…>r€‰MgE@å³BÔy8ˆ²2ò‹PÒ˜NrçÝq&+Æð’íM”×f“Û›Ý+ùHošðÌE¢LáVF”;VO¯* mññƒ>ÇÈà ʌ×_¸Y¶<Ù®‚D¦V# ¤âZ ÕY’pg…¨Upé7 ¬[„Ÿ—=ß%(ÖHÚBú­UŠƒšA@ 8Ò 0]±™Â.H7¹´¢š¸×™ƒ‰—Vâ`û!É$vt€¾Qð§K&@Xp ¼ðÉÏèᮃ‡þ|1ôŠfÐ!~ì­ïà¶«¦Àà~ìÅmÿjÍ.Ø“]ó®>âzCÉé*xåó_š„óÖ+σ!ýé‹£wá‚#ÒH~¤~AÃ}úh¸k‘|ôž?nÌãzþ=ÕD‘•»O˜°xàŽù¾]3õZORq¹)cþá.̛£S"µ’„C×D½$ܸ¤ï`¥¼-0’ô›ÍT¶ r½ª†Á8wú‚f[ÀÇž€` g@Òud½±H”+jêØ°Ôiɯ;3€¼É÷]7þvÙdôf÷a$Ù݆›ö¸¯ž±lÆ=¢7¼O¯6JÐ3,î;M+Û;ܵÞ?¦óÔ¼Âp˜ïÁÏŒe™ðÉ?ˆßµçüj°ÙY›»Ýâêäç5T_-‚@û>bn"ËðÅyB~rò$õY©~‹Ü%á$Eú·„k[›5%ý†¡oô› ÉmÃRÝ4‚(7K§o%-MäQ¶Xl`Ðw®³ÿȉ2HD0ÉÑ‘'ø‚äúscÒˆx6Ltæáæ_Îæ½NN ûs‹X°N§~Ñ=؆½k=0 óêHxZ­2S½ Î’„3ǼÓ[œÐ+hµš®‚åúÃ))É®u±àq„T\Ç!m, '¥÷;^j÷(¡9é7¤™‹¡ÒoÝã:èŒZ ¢Ü(·ò<ô‚\g§áñ}`Cj6N‡˜g¹ñùûöŽ€1CúÁŠM{n¼ŸÖÓ² áœøÚ?ì²2 N4uX×mC\IG™H2M„·HþƒÀÀi[1}ÕA’]±-òŸÚ©³&Zþ-nb.¤â8­ I¸Ö"Õà8&ýVžºéõÒo$ý6k}iƒƒÅŠ@ ƒ¢ÜA=•{7Gî¢|ùù£`ZÊøiëxù³_0„âxƒªÙT^zÞ¨¬1¡t]Nƒ}´RZŇ OÀj›ô‹÷‹Fâ|äŒãºr½~ÈŒ$“Ž2û\̼Ê]i“8·‡ÐÇž›l43lâ 1vùJŸAç†ÀÀ]»Ò0üBHŹaÒÚÅÆ’p’¤u½t´¶Œîxܪ—t3Ì6S*þ¾]¡>Lú-¤×!ýÖ¯ï×YeïcÜê3¸Èr«sxî@¼ÑÀE†Á߯¿"Ãá³ÕÛáTyuƒ„‡Áô”¡ðÛÎL¨Bbìžöåe1¿¿lÏ€w¾ÙE'Ë!ãðq0a‰ZáK^d rajiÛ‘¸cG6^ÊKx±ØÜo¥¤°¹ø61÷,B*®}x6–„·¢(µ}%u\xÿ–HúMQìkðV^/ýÒ|!ýÖ=®®ª¥ Ê]…|3ç%2ו):2®ÿÃx^q´¤ì S¦ŽÆ Xµe?Æ#;xè ´ÌB{<¸Ð cP»˜FÀ;€ZMÉõ2ÒÅ8« ³EòF^°QÍð•h@­dÂßꨦú©¸¶·†„kf$ý¶ú%Ͳ&¥ß•_i[iâh@ÛD¹mxùåÑäõý½ÄÇÐ |º²¶ÈgõìÝ#ôŒúRç¼Ë¦ŒbñȤA‰ä쎜8 ÓÇ …™“‡»¦„¸(”ŠSQ>£Bbƒß!0`ófzëïØ'+ós'LâwUI…H*NéßܧT_ó& I8Ü\/ 7%áDj•¯ŒTLe»Ð·q•ë‡ôÛØ™ lް×± ð<‚({Óv•Ø•ždüT Y¨fñî·aÑ'kàБpÍŒ±@øšJ#c!µ“¹S–È0©JP'>÷4fHä-ÊZ“ûfU-w%îªÂÏŒIÜö1†mqV+ìÿéÅ6>C*.%e¢OçóE+ ¹:¡ I¸æ›“¤ßP®h›»>2þ®…ô[ó‰=^@ óô¹¼`¼¿ÉÃ:»^Drïž3_ J§5–¦{쯗œaÒ8ÐO4àÈ…ã“øªk>áœ@OúÃ8¾Øes"÷gA’»¬¼~b|˜*yÆÞvØí¬Åéâ¼äÑsR÷~íõ“wÃT\nòè/°ê·Põqè$"‚7вH ØyYÌE‘'ÐVôÄ›…$\C|h¤ßJ*Ò^Aœîã{ÑŸSñð·8T-ÖóÍb.ð:£ìuˆž`Ø(m­®&ªáVu¬‘g¹1IV‡eg… t!w6q&o"°#m/–ïò$Ë ½Z:qb˜7ÏÙËRq­k}ÉMÒL‘à‹ä¥Å,ž¾u¹ýÿ(.ý†/·.’ŒµÒoþßôª­¡ ÊØ4“fhNœ&¿¡Õ˜:oHºN¬Ÿ?œªN6´ßmožïuuˆÐžÆ@ç(9Jßr«ùu‹w8C*N±Þé3ùn©B®å¶cÒoVSš~k'±·sD¹“ðN™–2I~KÒ@p'Rœ¦=`À >&iIü]'´'»È£."·o¯Is/· U0nÎM3›¯‹¹gh §(ó”9s<{ß.Í"Ãè)e˜à Üf! çhOĤ^ú  7mÅœÔITH¿ùö%ïÖ ¢Ü ͘ÐC;ý2ë­ú;N'‰!á:÷vŸBÒ„kû]:ò±Ågúvƒ¨žŒ‰»Ò¾Å˜å/Ý,z¿`Ê”nëbÑC©¸æ$I8Yz/»¢YÜüÑÝg~ë>mí«5D¹ZîO÷V<‡$yªóTŠÕ\Ô §§hèÅà/1C,=c¿X¨(â7ÒÕ–% è^‚Þ«>¶ºê7Õf£?Ø#¤âšoE! w&6BúíLLÄõ! H€—ÛäŸokoÑé”»ùiNKŸ)СН‹¹º0Øk"Òhrä™_¼r`‘º,Ö´~[·ž–4Ú;x^$Ë7ä$¾š¯‹¹çRqMc‰Ñ®ÎiB Ié7^7Ž¿`Æô5Î~Mc)¶ :A”½ˆö KtS4ZÙÕë¾®~Yýæ3OrõïàÑ7—Á†ÔC Šüdå6Xö[Zƒmb¥uès™\[ö†ëhEy8éå7¹ÖÅ‚Ï"°3u†`|Ä+€_ÞÅá­{ñu1÷ $J}¨‹S*Î3…ûh)i—ÇŒÇøx¦-ÝÝ%áHúmÕ"Íbdz²áåñ·X­‘4s/}T~púôõ6mfa¶Ÿ" ˆ²—öåÿ%û2Jg7d;Xñ©î ›Í†z=—´Z üº#ªT<¨‡çjÛ9%ø÷/I²ýG~66uIÒkû&óu1÷e´Ç^BG©øCŒªû;¾\µÚ®Õè\¡-轿6üø†£©Õp/ÙeW”ê‹V¾ì®’p«Å•”§nÄkÂå]G\2´Zýx‡>r=JbI  tj1ÄŸìxé“è`K]Éø f½w±n%{¶én..¶š=]Ï!ý£¡øt%¬þ=æ^”Òdñ[÷çÁæ=9PUc‚„¸Þ0cüPèÝæ‡­ûò`È€hظ;´z-Ìœ4Lf ¬Ãuc .Hc†ôcåZmvX±qÈ;:­Î=÷fû2ó‹a}Z6?U±8¢ß ÎzžëÅO~†Ùç„u»² ‡Ç•Ø®š6ˆàÛì2|³v7*< |e;'>f7t`±Úaåæ}žwHßy4ÚpÙ”‘Ð’ MV¾ëêꔊ]_ßÖã¼›Á6‰1Å&-µøÐø}÷f$«ÅŠ,*@ q÷îŠüääÛì`ÿ‰Ìq¸äëâw¦þOæù $—›2zðCR‹²C*n¡ßT° !I8³ s\YÜ^"\ÛºÁI¿Aé+·ç"ÖZúÚyëô{NVwD}áQöpÃáƒW²Ö•|‚7ƒÑ¬h ,U§´þe¹Õ;øðIt9ÈÝpäÄé&k³Îó®™ 6$»¿nÏ`ÇY¬6ÈÆáªÓs‹àJ$¯tÜWkvÂ΃Œ”†‡‘ïw•¹=ý0dâP×s/O<VmÙ¥Õ cWîï6ì~Q=à¶+§ áí=B*xe•µ°|ã^˜42¦ ±Þ•Ygeê,÷Š€/s/{³ á@Žƒ‡þ¼5öàú¹£a6Ö/>Öñ…¼9\Fzh¡dù5vK Ű²Áð³iŒÙ\÷ÃäW ƒ ßã§·ËòÛ…“G÷åëbî0ÄÀåUÆ7’n+×Ý%á虈¡ ž†“dô}0é·ÙÊ× ’ì™ß›(Å{¢ìal_øv>Þ ®áÅ*6Í]¯þ?Ûv¾ÞÒã'Ñ{Š·6$òÊOˆeÞÛåö6™s׉±‰èåÍ.,!OšëØ?LÆöO‘À¶ÏÀá¨Gê Sdžê:”œ®bÇf¢zX| ôî }#!ªg(ìÏ)Bo°Ãæ£%e ÓÁùcA¯ˆz¹èQƒâ`Âðx$×à ‰q.zy:Ï1 OOèf„è^a…ÄRZö98Žy¦É£=<¡ÛÞœ lg+þ¼ç³aóÔy‡ÑsþGÌc¥¢±äÓrù[­88Då„ÏÇ_Ûaf¦¢DZ,ð•2mšøÂæÁv‹øÎæ¢@Lþáì¹,Þ'Šj, ‡:Óõ/>QƒŽùË’á«_Ò,ÃçÍ‹xÕRix >®‘´30ù•Ž•.r :ñ`ð Î/¼§;×.ÛŸãEj$éµ'ëgõBº“6½N fôö¶&qÂ{ÅÔ1ðúWk™7X«uWžÿ'ôÎî;t dE-Æ8˜&ž(T‚Rp §†Ø^álÝèX§pôºA^Ñ)((> {±,žN–;¾˜ýeÖDX¶.^ûò3¸\=c,’fv_d˜Ó3 O”±ÕJŒ«þ;—BXpTT×Ñ€^ojê,ç´‹ç=› ü¸¦æf‹ ëÞ:Î÷¦ò|hø¦¤—÷Ý‹h-¡ýˆ÷-Ã^9°!ãáŸ4u¼ØæôÞ²¥*/9ù¯ Éë°MµøÞ8%¿ºœ~» |£ê·’¤âpp—ã×vOTd¦úð™ú-÷œ…$ ‡÷Ÿ/ÜÇ’S’–ÁŠî!è@Òo–Šòo‘ ;âóV 6q®PµðÜ5&Jò>‚({ãç>´›ª¿Ââ¦HɶN<ºëÛ|†`$¨GJ*Ú”/:2Ãá§­Xü±£ !yy)>ø¶«¦Àà~QðûÞøãŒéµž'“Ðòuî!æë4'‚­Ãiò¨Æá¾–ûE÷€û¯»It!SÜß3'g‡tz¤iå†jDG:ÆñØœvˆÅ4/øëL—üηØñ:0^ú”“„³øïl6ðãššWV› 4 }P2çzoèKûÏÇþ_¨l|áø÷ð—Ów¥Ï~°©s‰m¾@Bjê&Œ£}­},Vde~Þ¸±›v¥-÷¨ßJ’Š«“lOã‹H ¾LÈOI™¿{w«¾°©¿vg·Ð! çpJt'I8’~Ã(¼‰©ZRøçõ ñSªg¿nÄêB }ÌA]uèrk𠹚<Œ¬×rÐRƒ>ðºöÜÈÃf4°‡:ìT×–ô‡‰IÌSœqÄ<•WÕ±ÅpŒ?.«¨ËÌR½C™zÖùp ßHË*ÄЉæa..­d1ÊÔñnOöQô›14£D„Á w•w0¿ˆy¢ÓóŠ˜7™;%ò ‡=ï,Vºàx©+Ï ¸(8€±Ó‡ð\têxH©9\›X «M&DNøò©‰C›Ýd€Þwâ­žwãC?Ø òÒ”%E®‡@³ÅU#°kÏ"¼$~$#ñ'!!Yþøð„ ñª6Ú‡ŒëÎRqÝQ.ýëá«^Ô¼)¤ß|èG*L=+‚(Ÿ¢³ðü{ÚG‘<]JGâCWÁ Ý¿>|K]áÙs6<‚¸1=ñ?`Ç9G¼nãš_ °…KЋ‹ÄÝuPÒ€¦tñêçkañ×ë`"Æ!·7‘êD_ ‡xÿ‡-ð·¿ò§Žßvf³®‚gÿ» Ã(aòˆz®Ñ?&>øa3¾3ÕÈ4¥Éè'rýô’å°f[Æ6ס»bêhVÎ¾ß ¯}±c¡áÍÙÐRG‚$2Ä€Mƒd™þœ„¹¥|îûö=SƒçàKP-Û®(çTUú·û1bÙ÷Àë@Ñ…þ¯ˆÃd=~5ˆ°Û,_+³f|¯6ê´¸»JÅu7I8’~+È?¸C÷º]‰BúÍ ±è›ˆÐ‹¶ÛsïëÎWdû3¼¤EOÜn_Å×[;çÄ ùôÁ0 ’f#ïíØ¡i¶¦Ê!%‹ÆiTšx¢0†yWŸÏbž zGsóýÔIŽK¿Ññäé}ñ>W?DˆÁ ÷õà ÜtÙd°cB“ÅŠ6èÅ€¥‡n¸ˆI·‘w˜¤ß(qÂ>´*eŒc6ôOc#áÑ›.Á²l,ô‚o§yìÜw÷œi,zù ÐàðP·dƒ{~÷å´¬#/-AD°4(kGö‘Ýs÷c[ZÆxåô¤WÒïÁöþ§ÀMC_Ú·!ë‘QŽõ–2‹}ªE`ÀæÍe¹ãÇÌ;lF#ðA?.¯äø«¸|jö!ú£T\w“„Òo>ôƒ¦¶áQn3dõžû ¤7’¦/q g›Ï<õÉú#Ú¶DÞN-#rZ YÅìTÛ iæhN’›ÙݦÍD4‰°r’Ì3S'DN’ù6šãË;Ö$»ïon;Cvs’ìž§9Ü¡eÂ/«àDµ CEŠÇæÞmM™ÿ«ó‘[¾·†½tðÌ7·Ä¢úHܹg'^sKñ%ïîœqc¯çëbÞ1ðP¯öÐ ¤âº‹$…6%ý†ñt é·ŽýfDnõ Ðv¦ Û»ÔºA(¶šOÑ®¿z*(0ðúöÄ%SE¸w“HºQ(‡f4èà{Ô'¦`_M=0^™:æuE"ܾ[¿ ¨×#0n: €áK8sÌÛj[hh¯{,§S>Œè0âŒKG½T\¯‡×ÖÅñª@ a×^’þûŸËY~?oŽœ#R‡h,——{¨~Ž—®®HC½æq«üU®%é·ÙÈôEF$€_ ˆr;›‘â’‘%]BÙѳªh%í_º©ÎHÛÎ2Im‚F¼#BgÄ}ã£à*F,ÅÑë|1}ìo3™ÎsWØ¿tí.įâB%Â0’dÆq¾„sSê­±s÷¼ØÚ­4«WCÇãKS’I*y»5yÅ1êF <(ävl×l§•!Šbý¡`Ê”ê¶ZýÖ‘Tz•ëcú%¸_ýV·ÓÂãGçà 4ëˆ×’C®E©5›Súm'¾\岑¤ß$cò̶M®mbA à¢ÜŽF|aIÀHô'ÿ“gU$é¹Çî°ýÌ×›˜ã}“œŽÉf·Õ™¬VZoxè¹@ƒâ@ÃbÃ`ŽV÷ùê>íYnPQ/¯'ùóÕÛ·ccT *,‚‚!&Ä•ð%rãÐ Œ»VP_š„¡y[5kéGfà*a8Å+¿š~%_sßD€ô•1’êZ|Écr1H†Z몗)))Ž y߬–*¬&©8$Ž&2_.™Tœ* ó´rýK€?J‘ôX­Ûð.9ØŽ`Á ¡ìBD,ø‚(·±1—,IÑË`ýo,«;‡ÿñÿZ(æ Bl1™ËË+jÎØNX"pz½ž»à`#ŒC¢ƒaîQx{é:Å,·`¯Oä·P݃Hrt â" Œƒ™„ –„'f—‡^¸W¶¼²F±ÛlõÃ:³û! –³ù¶Ùç|#²ì%IoeDòu1÷MwïÞ/A7bOÇoTQ¦åÝ5äµoÖªë­îRqþ, ×’ôÛìòCí ;ìú+SX hA”[ÆçŒ½§ í |zŽe;Ð;")ú›æÎ]Ú¦ bSmÍɪ:³Tk27(ßA”i° ô(c¨@Hp0‡Ãð½adlê WûË6ÂË·0EŒ¶ê,78™­¤B¸>eå•ÐÏhƒ¾H’ ÃÐІ#-®„¯CùÂ)ÙXP[P›XÍæ¢¶@`TîÃRy%Z1Ùê?/·¥ q¬ªHÜ•ö­¢‘ãF¡ôoyãÆÐà$"u—Š³ÉŠ[H‰òeòÒb÷ï ×µY…ô[×â/ÎÞµpµ†®µÂGξh‰>Ù 6×ÃcîžøÇÚV”ªŽÒoªjaL ˆr+A_¼j¡º0÷c<œa†$uÓã·?ýú?îXØš\$–ìÚ–;ñ¢™;ÓóÂ(×»5q§Ë«Œ„NÆ;.‘d~ã%r …~5uP†C2—ÕÙÀl«ƒò2ôCnþu Ž( ‘=¢W Ä@a+ŒCdáäI&’μÊvAª½É„ ¶r\åô†•ߥã*o¯V–ñð¨UC_Ùÿ_$R·² ¼=âõýü}dÛFŒiÕÙÄA‰@bhÄ]y•eñxA\ˆ“$ËöO{dàŽ´­i‡?  ‹Q«šˆ2½YÎSæÌyNZº´mê’p[Æ­(JU¡™­6‰«i(ŠýYÌĤ‹ðvkÃ/-:T-üÂYÞj<ÄÝA”[ÙöÕ…y ñAÉôr‘$×蔀›%iakè)']4§ 6ÉÉú¿¸öБbÜ?¦¯² J(Á”)k°¸Z 0`‡´  DšÍèq¶ápÒv$Ê2¾šŽÅÍü:Ñ %R® ¼´"Éè‰gØ:b’1l…<É,ôÂåM>³µÁœ£ÒÉ¢cä)¤öqo3:ÍYS„>ð¡r³éb$ýÿ^V›ô.fºú¬ÅªF€Ôò§¹Ö^ ¿ãe1 Z6›òs=qàŽùª6^¥Æ‘T\nUÙQü•Å!ž1N©8W¬¿JÍnÑ,& w¬°^N£YÜb•ï$é·U/i>F3¯dwC\À;îqI«{é|Ûf•›/ÌxA”[ç‹ïë'ÙÛ#õ7 iÁ‚yæÜVd凡æ$ن˶-«V¬íŸ8øâ/ÚúØÍ—kk ù#âG‰H¡cH2Jœa¸…©Î&'Q¶Ù,ã·"ËÝ$3PÜp!1…S^¤jAêfA1ÉFc›(ä‚öŽî‰2° pn¹lÃ÷ß.Å}¼Zóä*jûýƒ+‡¼’~ ~ŸÿAÂv¸jÈËûoÌž?òS×AbÁ'ˆ_¿§‰ñl›Í²Érošpy%èsiŸOVª ¦—¼ä1ïàÝŠ<•ÄÀ(®×§‰24% ·¢˜UÏ×þ9¥ß¾ÅçÝ`7Û7¢ôÛuÓç×øf¥Ü*"mE@å³ öᇋ,áaŽOO¬}ìvû;ßáðhž%;íæÞI"`D’飥¶¶ºn÷¦õŸk¦_|ç'+6+·]sAƒëɱÎA”‘,;<¦` b$ÙŒØ, ØlèQÆ w¢ìÏNe§3ÙõAÃRÓˆ{VA/D–™¾PAv 2âð$sO4¶K„}ÑÉr8¸{Çkåå§HŽÚˆÚŠ·;®5ÿ²þkÒËûþ ûÇñÊâ¡ofþšu_R›:¶æ\â˜ÎE€¼ÇùÉÉWàà2ëÈ«Œ—Ç0¹ Vÿá3cÖ¬©é\k|ÿl’ö½:°=EXâK%“Ћ߽_D|4¹I¡\è;ÒÂõtñ¹´ú%í_d«u ntϤߦ.ª.DÄB7C@å³4x‘åõ|ê<¬R£ÞŠd‹HT[y'9I&© 6íù}ãþ½£~Äõ+`(½|ŠäîY&RÇeÌXxS:ŽÈ …\X,صy“íÌ£ŒŸý™GI”IÎ&Ë©xW2k%Ÿ“$é·‚üŒWÐÙr/ÕƒznªqTÁ[f-°ã—¶õl›ø'莢ÜB«?ÿaà@Ùj¦øX›à‚ÇæÕi!Ks»ˆt‘—’H=Pi0š‚ÖýðͯHoÉ›<ûxi™rý%“4MÅ,)d#÷Ä8eF1Ô‚:ûñäQ¦Ô]ˆ2Õ•0y˜‰(³ ׉0;Ö5Îýo}¢˜d · OrAvÆ—k–~± ÷R{PÛPQ[µ‹(ï{$¦&é¥ý·aæuXµÅu¸þnæ##×ÓºH¾@BêÞ¯sRÆÆ`ˆÍT¼5\(W•ƒ’\-íÞmõíÚu®õ$g“­73eNþøñóãwîô¹OûgJÂ8Ù¹HvìlNé·¥x-Or+)C«Õ_sÉüV«:¹e‹ÿB@åÚS±˜_ÃÝø™•¥ÔÇïxúýÌ[è\mÓŒ¨6‘/z!£OµT®'ýú¾]WZ|üäøi]ûʧ?…§Œž ÔŒ>@‰{—‰R˜yMq9W¸‘cNùœeôÓ„%æUvz— Þ¹oçÕ'd’€#u ê¸'Û¬Ui¿oü0uÓzì¤rAmBmCmÄC/p±í‰H1Æ'… r=åÆˆå7§­SÆ®Ÿ.ùäçØ¶#àß9íN[œ›2Ö¨(òó¬}evždÿÕ®GõºvDjþ çë’pBú­ª8¤Û# ˆr3—ÀóKt@-Çy˜‚.Ê{[©rÑT‰œ(s23bÀ8QhöoÿýÀ¡½{ò&ýáÒóì6ë$s¡Hö”Ð ƒ,êõ Ý¢˜‰RcRÜxÝq”þçd™×®ñ:m§¡ÂiDL±‘ìv[ÕᬌõÛ~^µãÄOã!8d1#ÊÔ&îej³v§ MÀ|“l¹ Ï‚ï0#Žï:p7æÓ=áÛ †fLÜöB^Êh#¾«>EÕÃv¾677ûCœßD¿[?¬²WªäëRq¾* ‡×i“Òoxí.˜µ@F‘O9ŽrmŠBA”9ns¦ºRÙ§UÚ¬€ôñ·Ú¶ºÒžEú”OEŠO&ï¥'ê Èed“©Ö¾~ù7¿­_ ÝoÀ¡CŒ!acP¨N‡Á*K¦ÚxnRy 1Ÿ/«in³áX{uu•uÕUe‡²2íßCv’瘈q…sªÄ9µ µM»ã“1¯+í}h象/íûnx6¢WùŸcú2íþÁâ äBÉ·vï}:7et0¾=ä¬Éèi¦kkžo׬ó¬÷e©8_•„kIúm–~뼋_œÉgD¹‰¦*•ÒÀ‡_íB7n…"=æàQMÜúMäe¢Ï²äU&rL$™{‰9‰¦Ïþ’¡ÏIß›‹‘::–‡‹êI úöy‘[óî±ãïòeÍ sÂÖ_ò)&O2e’÷¢e"8<>™òu8éh_³±ß‚×Ò|ÛŠ¨µ˜ˆ4ßÚá‚EªA q÷Þ‡‘,cÜé0J¹#wܘÚÄ]{T‘*6ħ¥â|Pޤ߬ååÔ'cÛe!¤ßÜÀ‹ÆpofãíÝvýµ%Æ>èÿ{Ú€FZøÄí5'\ë[à„{•É“Y†…”:ç´NŽös ‚§šÉˆ1Á|R“]h y‹iâø–„iSXÓ>z“±<–Òç·`Ìûù:²ï›‡¿š>¯‹¹ °kÏÝIú˜×Ghü;z–é¥H¤V @RqÞF/°Â2!oÜhßøø˜$I¿¡\Ò6¼Õ“d’~›pÁ…Ó}äV\ªânŠ€ð(7jxÆcz‡Bi3Þ¼Ó'šú–‡¥qˆ,óÞñÜãIëDžéa„ó*ãœÚ‡‡g¨Î«†Jn‰H¨šÇ–¼øNA¾ä9&bLDºÖ9ö´ŸÚÆ£)sþˆŸ1ã4G¸R$«,¿‰d`’ˆcõ(Ì]Zµ%vä»5/ïz–•9d vô{47eLh®4ìÛ b–[j ÆRqø3¡—Ë¿´”§«÷ù’$I¿9œñ*ª"ÝãÂM‚*I£¹åÒùöo<ü|sB,üA”ÝZrÑ{ú1VÅæºAK’ö^/ˆ¬sGÄÌ}™È7"ÉîýeÕæž(Ãæ–Ô{K˜RâØr¢Laüe„È2‘fZ§í<.™çÅMžLøÞz šˆ jÂÐWÌÅÒÿçÉ3ˆ²ºR»@‰¸r%;Ž;¯\IÖ i¾;oÜØeÚ´›(Ä k-T÷Ù}M*ÎW$áš“~Ói„ô›ºÂ:5!Ѐé¨É°®°‡ïp…\ ýþñÛmë½d2"pîdŽ{•©“Wà ’L‘c>á¢:RoÔsvKÅnË]½H¸ò‰pv'Ë„3‘cšÓDû<îIÆ2])둤ü¤—¼.+Êcl£¢àˆdÊ×ÂÓè‚È/HGIñµyUåaûÞ@•ÂùŸóªÊÂpP’9bP’æ›Ù—¤â|ENH¿5½‰=¶ ˆ²­gß%)Ö«ˆ]‘œF«[èàQμ3#‚ÆÉ2yœˆÀQ›p‚ÌI2%W7m„¨çì–(ÆZ-‰0¥Äñåd™f¾Î÷;Žöæÿ@íË’I¹‰S¶øp$Îô‰þkožR”ÝùçÛøÆ¼ñcË1V™}êÆP®ËäÊòŸK'N¼Ñ:Ó›ÜÒEÉ3÷‚òpÕ̵H”ù„Q]N[8n4w•–Ðî„}äUV$x³þTv¼îp‹H~‹Žà÷*~ü¾_è÷A/HCM²usÁıçøm¥;P1ÕKÅ©T®)é7 H¯ é·\Œ"«@À‰@·'ÊÏþ'`bÁ<{]èMd7A€y•A"}g"M#‡½š~M7©z·­fâν‚FCJ'ôeƒ:øõ·Ú”­9ãÆÎê¶ 4Sq’ŠÃïz_ñÝN©8¾Ú¥s’„Ú‰dÆS›µÒ{]jžœ¤ßV/Ò¼…ÒoŸâª‘ÙCÒoZÍœYÊ{Aµ©««,Î/ètº=Q–ì65z“;ýB'ì˜WY¨ÍíHØáKx•9~eS”ÛòÀ>¤(%åŽÐƒVæÇ‡áX†oâïc «¢¤Ü‰#>ƒº|&<øê«A6“)ÄnÑi5¨ä)[­ZY¯Õb°¿Æ®—ëtÖˆª·ÿïîO*ʨE¯‡ô ô›R/ýé‘K‘_èR'·_‘¢jÝnK”?üp`àqKÁ-¼—Êü¼ïèÖ/Q÷ÎB@+iÞGé0QV`vÒk3v¸³Î/ÎÓu úý÷$~3ò󽇟Ío"KH>®lÛ±“ßå¶§ì:ëºöÌw,\b”ìeƒ_±[4ãþ?{×E¹µÏìnzB ¤ÐCï Ò¤‰ ¢‚b¯€b¿Vº^BQ¸öÞËïÕ«"6 ½i!j€ JHo[æÏlf³„$$aw³Iæ<Ïdf§|åLæ›3ç{Ï{Oääºyx繸¦û4O˜>§g=ôÉ¿\àfj¥iÚÔ1Yäš:Ê¢iÑóÄésbŠà½/ÀÙhÏÁ¾Z£d„Ï^ÊøŒ‡ÚíÐÚ³MVý÷J8A°È­ú:kQ¨ß¬­Q¥=¦Fîû³t冚9#&&D,¢! ¦¢R ÃŒºŒøhJsØÙ‰¼Ý\¨…_3jïáÚÆÃÍ•<\ÉÝÝ•Ü\œàD# S˜×Xx] ƒ¹„—RPR¢sǶ{N~QË+yí²² Äü¢5ž9‰i¦À˜OÓßœŸl4àڃؿqd÷ö»§L™ba£{)áútú™~O»¡~×öâ2ê·_p][‹k™úížá/8RVT‹æ)›Š‡š¬¡L¢ñQù‚Fç¿S¦•¦õä}ÊZÑ€í5 `ƒ eˆ(<4y…8ëÇ)&1Û×­Ôàh·(±o¯¢Ñð- e/kͰ^}¦wÏ·#&LŒ´Z¦7lTò´öÝ@½^w?¬ÕG EFf"gg'CdHKud+ náKþ0Žýý¼ÈÙÉj¯()CÁh¤+¹t)#›’Ó¯ÐÅ´¬Ðä´+AiY¹·ã!ÔnŒOÈ›6;f ~Õ-~Zª^ˆûñ¬|DAø\Ðn)s_Ë{m»fê7ÀK–àcÊÄjê˜ú͵ß×V Ûê^)]ÑkÀj£PCRç¢/]Ãub‰ ÿ††«4N0X$æ¦†Ô ¥­ \NnÂr}¡ð^ÒÒ­]+¹.u~a1HJ¡gS¼Ž$^¼3;·`²š2?~å•—Wåž^9À«4W¢„SÙ‘ީߒÎÔoKc¦~S©»õeÃOD[ä½ÊZÑ€¢j IÊ:±ôA9Át»íµÇKNÚPÇJÑŠ*ÕÀÑ™Qùíþ«i|¼Ša¥Ê•j«qïŒ

L-utø®]•è%:Ÿå/IÚ3kÖYê\<_Û àÈ2G•Ÿpòm›{…ŸÕñàÜÎÚ˜$§Ñ“G1ZmɳOíG€p,-¯‚Œs¹Pƒ$Ü`¹¼ý°Z*‡³rÆ¢Jì!/ó¹ã €>‰Lºèº <Q9G”Ÿpòí—&¯„"À ¸5 Ygið%yÎ#?ß<µ‘ð<š‘UgçR”ö£j™Pû*ç,¤aò2Ÿ;‚ƒòÔ%48° <òoEÏL~ÂÉ·¢¯F^x Àý€ÛQ#‘õ›¬³‹J"\Z*ÈÎ…¼"xûÛ-¬æ :yyÀ˜Ø0¤_D»ÑÐcùÓÎåáÇCp»óê¨ GOÄóRqTUUƒV«µZ *•Šûï(;(APí’$ñ>–#:([ž!¹;õ´£åi|GéÞO8ù¶£†Åaàp;jZ#7@QY /oé¢Ý>yÜs]„øÂϼ†"J¶7<› ëvjo6~¼·§+””W¢7”*ŒYËÝv8“!~í’s@./ó¹c!àh2G’ŸpòíX÷¯ý"À ¸µ ÉOÈ_uuu-è´–ïœðó‚ž]ƒà†1¡'ÍÈfhÐòªm©°øãu°è£u°É9Y¶)¥ž8 ¯|þ+[¦DÚ_úlœB«÷éìKðÓ–ýp±°œ­ûië~¶_^A)¼ÿÃïð«á­o6Ùœ¶ž>8èØ ùEðáOÂKŸn`ë-ñð¬©ÑCUu5ΜƴÒí˳ëî”c誮ˆrÁË#øôС‘íË‘m8œ ÅAä'œ|ÛãÝÂËä¨pnG-+Š]ôÒÌÃZ‰Nu43‡.CÝSڸ뤞<×Äõ€±ñ= ùøYømÏQ¶­ªÖ€nýÊÙ2ý£ã K*÷óv‡ˆ0ðörƒ['†ƒº³ý~Ø’®:-Ü7cú5÷Aro$æ´‘ŽýtõNpsu©#ú³ý-õO”Dü¸©aœáŒxód_?a ¤Ýr© †šy™Ï’¡`[ï­«‘‹¡f(µvŽâý„“o¥^¼ÜJE€p;i9²Ëš[#}±n7üë£_€2‘ß.~ì´D¸czvѨ §‰"Jî;væªEòñpOwpA]9¹ÿ£ 8U5Ìâ=¬_$ê°] ¾O7ÈÊ/†ÂâzÜ îœ: á9-™è‡¼ èõz“ÅZX[²^Ž–7éÀMuâ:p޶à(2GŸpòíhw¯àÜŽZI– X‹Æô ‡ÁHˆÕjD‡2$HjB¤9¸³ ™°(«¨6ÉPLpÁ€²™+¥ôóùìÃâ»ß’áo·Á—ëö°Á¥Ù%¦ÃzGZiÀ&~Øp’ùÐÄ%(¦&°«I¶› $Âu¦e¾àP8Œ E2Ì’åS+•’È÷ºW_üM@÷˜Õãé<Â¥ Ÿs,‚€å…Æ)¶ãeJ¤Û8aݬdÆ`žÎºÄtÞ½a$hÔ*´b»A>ê¶å”_X®.¶Mþü¨œz½äA%ûb±¼[“sG锟„ú6ØGþÐÐâÈ;k$êWù&?à³s|aZãôü-D » ü™! EØ:¾8Ed‰‰‰LJ5¹'la6|7;Gà2o(¥0‹ü™»Añ꼟̔W*Íû‰L¾ñax¯© pò-ƒÁç "À-à·µYË„Ð:”úÒ]?z? GêaRäÈÙY@Öë œh¹w¤ÑBÀÈkÒ±ÓPPR»fÔg„K~žL×]„„È’Þ5Ø<Üt°9é“Ô †üø™ÜÇXëáKVo$u&òm­sóó´!9¹÷þE>¥C&‚#¯ãsÇ@  `¶Òj¥dù 'ßJ»Úxy NÀí¬Ee«°5‹âÏ4ßkÐÛ äécAx€|²f¬Xõ'úzÁ £$;ûzÀðþQ°açaxýËß 7i¿åÔ7*µß>° =š|óë>f9¿ õݹhQéó°ðƒŸÎCh[$ÓGŽ­ `‹J+ñœjÕOfžÉl™/:Š—¡(T~ÂÉ·ÝD¼*ŠE€KPÛtm/8IA^~´!§¹}òÐÞ9mÓ|aÕjJDfŽ‹a® I¸¡FÉÊ´‘LÇRÀ›Gf…J´~“çJ4 sÞ]“  ÝÿQru1®.+Ûÿsz<ü:o,¿”Wן;^‚ýÓâââ£SR’@É2¥ÊO8ùv°›ˆWG±p ¸4-,ßW«:éÁ“oùÚF仹Dnk«‰x×“ïæŽ´îz{ÄݺØçÙ‚ýµ¼¡KÊKÕ^¶³rd(J”ŸpòÝÞ«•Ïè8šgPwžS  2È a Ájãn¤<‘qæX·D+& ð¡|*InÏ™4ÉCþÍ玃€be( “Ÿpòí8÷ ¯‰c À ¸c´#¯GÀáˆÞ—ºeJÇXÅ$É»¢ ÿ6‡«$¯\”Çè Å®‘aÁwLƒƒíÝû 'ßv}9ñÂ9)œ€;iÃójs”€Ê˜>’Ë)Iâ?¤ÄDþÌ’q ¹Òd(J’Ÿpòí@7 ¯ŠC!À_fÕœ¼2ÇBÀWãò’pælåC½3Ö®VlÈrÇj™Ž­âd( ‘Ÿpòݱ×)Ï#БpÞ‘hò¼8EÀÏžI€÷äL%ƒø¼¼Ì玃€’d(J‘Ÿpòí8÷¯‰c"À ¸c¶+¯GÀaðpþVðJc…¤øÌ!qS¦r¼"&”"CQ‚ü„“oÓeÅ8v‹'àvÛ4W/Øz †óÌ;?Âï)§ìüùÚÝðã–ý Öñ¥"òçþ|ôdÒ‚‹ñe®Wjk6_nÅÈPì\~ÂÉwó×ß°'8·§ÖhCYÈ÷æ½Ç ´¢ª GóC8Ê@@¥q]‚%-£Ò"˜ùËÏUFÉy)[Š€d(ö.?á以Wß#`{x$LÛ·A»Jгkä”ÀúG`öÄø&óÚu(¶§¦AiyDu €ñCzA× Np43vÌ€žÝ‚àä“ ÖªaÊð~PU][ñ·»«®‰ë‰aêÃY¾µz†‘?‡3.€F­†‘ƒ¢q{¶íxflÛ²/Ch€/ŒÜ ¢ñ\/cøùë0RæÖ}'àRI9 ì3ÆÆ° >zƒßoJ†SçòA…Ÿ‚}#CaêÈþ,|}M­Ön?G2²1 À ,Ãõ£À•ÊÐdåùJ‡@ jϞܴøA/#û^DB(‹óÆŽý6pÛ6FÊ¢’¼V¢«~–—g#$ŸÙ,MÊOèe‰“o;h^Ž@+àðV€e—»¢kˆ˜&;gs š,¢¯§LÑæÞ4ôH¢7ï1ºV®©ÕÃɳ¹p$= ¦#)¦ý¾ù5 ’Žžad×ÇÓ ÷!Sž{Žœ†ã§sèk‡õ…u;Á¥â2E ~ú=Âýàþé£H‡€Ÿ—1fJaI¬þã  £°ï;~ŽžÎfyR4Ͱ@_¸ ÃÞϾv08y§gÛ6î:©ø{Ä îpÖ/2´3[ß\L…ä ‹€ÎÅýudhìÁË>¸¤¬è9‡­¬“VÌîe(v*?áäÛIo^mE#À ¸6ŸF£‚j$Ç-IdEîʬͫ?Ðä!}÷ï ^®Ð­Ò'Ïå5ˆ¸9iX¶}xÿ(¶~üÞ0 : ÆÄö€²Ê*È+(eùG‹yŸÈ`ðó€¨0ìä‡Ò²Ðzm´Ï+FÇDCgßú …£»ÀÐ~‘HÚû@$Üéhñ–Óh~€é7_P<ö,C±Wù 'ߊ¿ìyœ.A±£†GO(·ÀÓUgó˜ëã«–N§~ã˜ø÷7›˜õZ­nØ%º­ÉO]Q‘„ª˜Åš¬Ör"É%76íìÃæî®Æß$û0ˆ"dd]„39pó’S~‘QpçÔaðãÖxó¿ALp˜9>ɸšíFÄZNÁ|à\n!ûY‚ºõŸp°è™œKàíáÅe•à®sA+½å•5Ð¥®\ò±W+ƒ¼_Só’²*ðÔ¾[±gšó¤,"“R¾Èˆ½uàcðkQ `øXš5+AX¹Ò ¬šðÒ6‡Þ•æ2”Y¸ßgÍíkÍõö(?áäÛšW?G cà¼cñlsnædÐÛ]Ç,Ï•¨ÅvCBÚ’äï…2î°a×a¦ï†ºÃÈ*MúëûgŒ‚á°ó@üŒ:n[ÊISG”åß²E[þMs"îœF19‹ù6ZòƒÇn€äüóÀâ“äSú±Ýòë,èôã"JV‚ü„|ûþSL3þôÝS˜îûý~gûS€ êÑ/Ö‘{¶ÿ]­ ò~ç„cYUtvsm¼‰‘ñËVòv‹Þ'RúС†ê(CqE">$3#íq,ðëv[h^°V!Àd(5ð*„†k3ÇÆøFnK-jU&–ØÙÎä'œ|[¢‘yžë!À%(ÖúÅgêä‹/@½µQŽÑÒ' ëͬÛÇP§-§¢R£ûdÔw—CjÅYª7€Ë»^uÞe,ûOœC I³ˆç\*ap0™zò(‘ÅÛÓÝTh‰&-ú™ìKuGJjá0jÓOá¹è<4`”Rse0ØÄáHxú{¶ìc¦‰,ø*;B ûÞ½'ñ òE¹HHÂ_ÌŒ£~yrìQ†boòN¾âRç•pr8·³ €,á~ž(ÅÐ0²Ûšâ‘µ|2ZeY Û»[0ó|òÆW›àíï¶Â0Ôy·5‘’0”…|øóxþ½U@k"ø$mÙ’t–üw,ùxÊI\!¡¤é4]ƒýᓟ·ù'ï܉‘tÚ˜€{"í/,_ ¿î>†Úq£GÚvã˜A,ŸVm‡7¿Þ„Zs£ì¥¹2Ð1Í¥ý'΢¥=:5—Ÿ4’ÂÖGyù¾Š÷J*¯wwIÒ#MªSX5xq›A;èVÊ›p™d(6MMÊOlT"N¾mt<•ˆ°L¾å<µ¨%—É·¼Žæ#¼pÉ–÷1'åò:yNå–É·¼ŽæÍ•Á|Âí§m©à‚ƒRƒ=eS¡ÿr:–Ê.cn~ _VÝ““ÑG¦êIS‰%x4cpì¦ß|A±Ø“ ¥±üTð-€åäÛ¨ósr,‡'à–öÕ9“…VÍ¢\ÐRÜ?Ìr1ÈÎÊMûZ—½à‡zpPi‹D¸~AºZ8Hƒ.iÀ©Œ1áÍ“²èž¼ÿ}ü–úQ®èùïé¡C#åß|®\°]íB†bòN¾•{ó’sšC€3æéØõÔ]i>]–»l%rHDÑÅÅBý=!ÂOQÿüÕú=b ¿ìÄ\AuzöÞ)ÌO¹OsYÖdù&¼7m ø¸ªA‹–tJ¸¾„³Œyà XײÜV 7ñ_v‰€ÆÝë~l˳T8”£t2ªWc”LO»,,/T‹h”‡yCiñÁ¹£å'œ|wdcò¼8öƒ'à–o‹Ë´‚zƒ¾²ª¶ö²õäþB¼QÔá€J’ÅÈOèâ¥bdò½•[™&ÜòEVîHóý.6%òÝI] \$F¾u:Óp%| çÆîQ×.‰¢XnVûËÚÈl_´ºmß^ˆ’ÜYø½WEEBÞ¿¤´èKƒ€FTž”Š“¡€TW~C)L·v]l-?áäÛÚ-ÎÏǰœ€[kÓ™j«ª ‹ŠË/#w²…¢+FWô[í†S¸Ÿtó¡ °>øñødõæ!…ü[ó@8{DÂ…ð)(*@uøj Œl»êŒ8ž„+áKðÆ”¢’rÉ ×çsL•‡@Ô¾{<`*¹$MO]dú͉~T™ôÖø5euo(¶”Ÿ4E¾qûGSŸ6ÌÅŸËÞŠl`^hŽ€#Pï’‰A°RÕéɦʊò‹¥•ÕBEU5ó$"ŸŸ$D µZ-#ÞîPûTWWC€¾\… ȯÑw.ó컃F«ôFß®H*-‘÷”ô%^Ž‘Ÿo º'ðªÁ[¥G ·€DÛ¥G7ðD, Oú !|e ŠŒµµImuu®ã/7Í£’|™18f Fyg,¶4ežŒÚ·ÿsUƒÕ ›{C±‘ü¤9ò=eža'ßf_ä(NÀ­Óx&ò§Ï:‘ÙýVòmmî—ÛHÀ) $ZÀÑÅŸ§‡’ïÔ~×bxvÙQ·\ ¾5p:ï\ª,†­/äj¼P^ádÞý8G§FQ•EU¨ª¡¢ÚžjŒê‚n‘x¹&+·»;oðòòO¶L¸¾FO(õ j ’,äž?»ÛHn/ë\ü,†@äõ3žÍX³ª¶å4ÊT”¤ÓÇæGïÛ¿¾ÃNÂ3²$CI‹I¢ˆ§xRY†ò™5 @ò“}ûŽÍ4ËJÞO8ù6!Î8'à–o^™ÌÑœü %íJ:arqÒ‘ o$àõ,7’,‚ ’þ›,¶µH¾ #ù¦¢Òöã™[áLÖFfõEN~^Ð3b&F”¬"I»9| pØ–_ ‡rRê ÀOƒ„ˆz%&dé&òíããÞÞ8 G< W·±üÛ¡“ ¶ýò#º¶3pçÓ®!1Q¼4lØíEµÕ i„7†`‚ï3ãâ&D¦¤ìv ª:MUH†‚5"àx—3ÊgÖ¨|“òö¬±ÜÙ9ù¶¶T(݃’“z8€$\Pka_ürÜàî!~LvB–o&A1Y¿¾$—…Ø"º?)üã牀SQ[É톋<) Ȥ¤œs q“jª ;°!ñ–褯5üz6>~l×äät¥ÕÇ™Ëk еå'œ|;óÎëî¬pn–'kªL¾«q¹º¢¢´,eǶÏU×L|äó5Û¥ûoºÆÈ(ëÊC$œ|WS"²yêÜz8ù¸¡wc Ž·ôqï ýžIT£>\Öp‘‘pg ßu1\§ûF¨à«ÃzHÎE¬4:Øš§ƒÀ î0'ÁéÀI NÒ“ö7O„}V~œHM~¥  ¯·Ñµ·€›¥Àåð])i§ãã§êA¿ ?§¼pêR†ßӇŌë¾'õ”«ä´EÆG Ue(ûRŽŽÃëÅŸ.çâVçì1JÝ:¾ 8ùîxLyŽ% À ¸uZ‰ÈYU‰Ü‘¯âJšöoÿ}¿_@à*\¾ cùIwß0J-áDºe~>/üyhº%ÄìØb~A}`bÜ2$ܨ©©1ЬӉËÜ‘àuÆo§ðòÿ˜¨†÷Rô°;ÃÌkuðãôÒÁMý1'’oŠ€IxÊ–s²|ùÞw,S¸™öñŸëWo¯kj#j+NÀ¥§ˆää”ÌÁ17bc®%) vl„^ø=cðàqQûöPzý¹ü‰‰ÿuͬh¼Ãþ[\XÓÿlTâ½]墛òङ_Šj56«DavQ'ç‚„ÏV~H‹e8¦º ï÷Rì,P Ò9µF}ÞÅÛõÜ›O>IÏß+&Afã¶:Wúó1þ¸âQ­ßÈÉwë1ãGpNÀ­Ó’ôð&R‡ÂF¾ËqŽ*nÐmùiå|ΓYöÆìK…Òm“‡«dM8‘ÅÒŠóðËο¡ÖYZ´‘uî3F.G"é†ä›¼£õÛÀ,àô .“pÌÓaaÃ&–Ešnò|²hš^ÜQ»Ð‘ ÉQ^Ý Ð+LC»4´|“æ›d'dù>—~ò³ ß|AÒjz1SQ[Yä…‹ùòde"÷¥nË;M4Hk±Q=ðþÁfÞ†šðñ\nåÆhæt,XY# cpó`l£^*µªO–þ|(>ÎX—UާäôeG»i5jo7Ý­è?ª=„*BσZ|VU×BµÞ ÔÖêÕò³Ð€™ð£»æR9Ì]°¸Há>(÷K*8€?U«õ;øŸÄ¿•Ñ ¬%?áä›5'ÿÇpZ8·NÓ™3·€Óƒ^‡“ Nš-«Vn¸”›“3x̸Û_ÿbƒOÿè.Ò~QBÏn>ðýÖ¿BUM{ÑxºÂm?AwƒFâ.PŒäÛ€ï´ùKLòK‡ýpÐô²¥d´jcÏpš^»%)¿TÀÉBbOþV ßÍÒbDÌj Wƒäí„\Šz}ÉÁ½;ßOÚúÛ˜ µMÔ;![À©Íxr"“öÿž?$ýz¬’'Þ/Á†ö3Cï–tàˆƒTS1Õ˜“¸Ü]U›?S¤i‚ W-ŠøQ$‘‹PChgo!°“*°“7vò_/ðÄÔn:œ\ä^¬«¾»è™XFŠ’òJ(,)‡ÂÒ ã¼¤Ü7ûbÑàs¹…qUÕ5d9GoS…$æèîPX÷á…—b%É¢òN¾s©ò‚r,†ÀUb;³óeL옴Ť'k+EÎ1v›âÂ]¦œLM9•0yÚ8ƒA?æpÚ9ï  /$7]cšºÐWߟ®9Œ{I6dd›1E|ÛÈÄÛ˜£,˜gDœ¬áˆ ³ˆ£Il°¤ƒ êþPƳB¤Ô3¿È–bK~•X+ˆCÉÙ´¿í\¿vMyyÉE<¬§œ¨M¨m¸þApÄÔ=9yûé!1“õ"¬Ç›Åï“ÀZƒ°5}HÌuÝ“RåçŽXu»©ÓÜù‹ÇHÜ+èóoE³»—»«¾w·M÷ð@ˆ‚°ÀNêºoëv—™òqÕQTa-yïÆùÑ#C]€Äü|n¤ÍU?=ôlNÁ°¤ÀáÂÿèz¥¯ÇçJ‡>R9ùnÜü7GÀ9àÜzíNqÙ NݪD¾eÉÈyeey-ZÃ×âú_zÎç¯nºÖçŠ/éü¹ y…•Hãæ†I&Þ ×Zþ—_Ue”|–BW· yÙsÙ".Ÿ;Ü5Ow2hrˆ„æµ2ðöªbòjùç'Ç÷ï9ûÙ¦—D¾‹ê–I~­ß‚#§ˆ¤Ôè|²(ˆð¾ñÁOÙ…m¨ÿ JU~väºÛ²ns¾4ûèþ-І±n:!¾w7õþQгk°¦£w[ê×ÉÛhØ#œW•WVÃÑŒ °ë`:ì׸IûC†Ý7gä#ߟY‘øìÙ¶œÃü"ßë_yñ#¼îî•×SxyáRFƒÏ9΃€LÛ\ã9 m£ƒW,^8–æ<]"Þdý& 8:Dï èQ'ôoÂ&·ÛPM0¸þ}ò0¬ýìinow{a–ÃÃÞ•3K£ï¸O.Oõù£ëμuÛrü-KNÈòMDœ9ÉOdý7.òt5”|ï£Õ{ˆ$Â:´„w¦zâ%J‚ê‰èäýo_­Þ|{Ëxôí·uÕ¹%oƒ Ýïáê SG R]ß4(³÷D²•?SNÀ¦=GD½(U ’øÄ‹~ØšrÏY¾\ Ù…!èA¯ ŒpÙs«Ü]-y àF…]nž²ôÆk±¯Á*Ip®ÎVi¤4—ô×æÍ£gOŽ€"À-àÖoT²vÑ£DDQþMò‡ª ÓTÄKwÒFJ—ò`ç—ÿQ}EŠfãûùïÝð%Šªk»JRîê—Ó…÷õvˆE%Óué;-ìÞ·w\øô±µø“HxEÝDØS›P[ðääý„'TW‹¨ —¢ñFDOâ[éƒc"£®ŸñEÔt,Zŧ_~Ù«äRé/ˆíè) „©#¡;På¼rüÐ2~ãØ8×Kõýæ$÷䣧WÌ™¿xèòÅóç4%KÁaî¿–õ ÒPI”†à@Òâ¹ü¸^Ë,ØšQ=˜aî¢aÇÅZƒ8Çî ÄkШsÁ1üˆø4*.ª‚‡.Í7Hâ \y=¹ìÆ‹r »Ò-Úp!"þÀ̱‚ÇØ’tìþ¹ §bÉÞ£ÒÑ`R¡6ÿ|xÜþÐ K&àõãIÔ‹Ý‚ýU¡!Ðß¼1—‡'7ðöp­ñÙ©¢<(p<'Ôê PTZy¥WXù…¥ù…%/ä +¯¨ú?ÚÏ“¥7H¿ââÁ]Ø´bþülZÏG€# ,0(e]Ñ¥¥g4éÁiNÄ–kïz\¿½çÉoª²][ÕäééáJ¤[žpÑ>R€–”4¦”cZ²ýáÊ&tÓh¸ô㢧ïyc5Fé Çõ>Þcï~MHývZVVY½ {jžœÐää‹™cÇNK‹¾@Kå-ÎgV×Tl?=tè-{÷f:,ß}÷zËÑôpüîÆ‡NwìðDPÈ *úó²æÒ{Cƒëp•*5ÜÙJ6ÇEÐfƒ›˜óÞóÏ_ªÎ+žãÂã˜1âûD8„³' ƒìK%ÒÉÓY æ¼°ä4>aþ¢2\œ‰ƒIÝ|¼Ü ±=»ª»!Ꭰ€ ò’Øâ¤ÆÁãjë!ðt×A— NæÇRNÚ¼‚À¢pâtvèñ3Ùw•WTß •’8gÁâMèñÃMôêÄÄÙdPà‰#ÀPœ€Û¶‘d««ø¯wÕ“5Zñ^¹8¥EÂ3¿­ÒÂßôð•-%­x¤Ë9Ynî‡ÁmÌÒ%³e[/ù¦Äð-:²í¢ï¥³¸D­Å—¦cv\ãõø†Gàé¯â>ò¾ìþÏùˆÜ¶­ I÷ìôÁ±¯ ©üC@‚8ƒ¾:9=.îîî))¿8**O¼ñ†[YQåXA' /¡>jªÏ¦ƒ'»l‚êl¼9Œß§´, R‚˜¥Z- ¿$£¯m#:5~Ï¢b !.HªÞ‘¡’o»zfKÚöÿ8xT8–q!ë÷‹‡«NŒï¡Ò/0vCƒ‡aÛÏÐü‘F·ŒÞ0&®í¤¾WûOœQíH=5õê“r § q\ƧZAûñ{‹ž=Ò|N| G€#`pnûV–|èÑY*–“ßZJ8 燗ž?ÁEzyÉm²«—™gC 8 h´—$“jš³éôË3¶özõàRü±Rõ}ýÈoGŸê·ß^ ÍËa;ê4½óÒJÃÏ6ˆé‚׊Ÿ†Õéñƒ^ŽŠê¹@X¹’zKŸZðr/ S3¿ê¯+/¬ƒÃO]4Zµ!ÔßWèä­ ôó‚Î8á @Üîè{›üí“•“ü bsŠ([\VŽS%›JÊ* ¤¬R[^U×ï§x¬W ¶w7’…@t× Œ´Š2oÙ6ÒxOËÿ ôC·~pݨõ±Ì €DÜ/õäÙ¿×jŸ@"¾ Gº>·"ñùËÝfY¾hü Ž@ ༠YzA¬ø_öt4.Wy{?€CpHŸ,¿ìØ&úgOI×°ÕÞô²D¼åDËRÈà/f%š„”|zMwÑ‹âW oœ‹ßõd¸½•].7Ÿ[è}–£‡”&­Äk¤^8èžMÏ85~¿T%ÝýÀÂů…©Ã–$&þ•Ü­òÄàØülÃFxå¿ÁH¾—ÈEÀnð׿ÏÕo•óyÇ#pø±é¨ó}LÎ??ÔëCFß`òJ>wzÐJATòþëÐþ½ ŒLm!è®â7tUø.zOq·g%ÕœKæ—Ö^$ñµ^ÝB‚»ýZøçÜ*Ò#ù¶çâó²µ"â³® />8S5¸O¤+^Áó³ RZ¸Ì¾Ì÷í¬'?œ# d8·aëÕÖV¾Œ§g}·ø’?íÛǨO¶a™œáÔ'æ ü/j}êÖI*tö.’+s¹3ÀÀëxH|`1ŠŸ'¡%Ÿv'I ^/C×…ÒÇŒ¼J6ÙÂ]HÉ€Xx®SKGN:{bUÃú¼qô Ÿ’g¯PHr¢Òêàõbª^ŠDé´øA¯¡CŠjkóD‘s,z„šƒ:íûf^Κ ¢z<97½#B qÎtul¯ndpx =Õ|¢t:7,¼ö›"À ¸ à—¤D•¤7 ÕÕ8ÈÑ|?ÿ~½bt¥6€¬ÃOyè‰øDúo–ÐñKñËÓ}äß|Î0G€_F'§Î@o w£»¾"Ú†ÖpŒž OJŠRNM0ßßÚˉ‰ß¹`€˜E ÞÝU÷¯¹3ÔCüÆÚø9ÃùÜt.0çæqE÷ÄwϽp.ï{ºnœ¡î¼Ž{D€p´Ê²/¢—ˆ«;u…Jr}ÊÅpúSzzu^†dê,B’‚ÊJÊÀˆÚ·ÿ ÔwÚP¿£ÔÇ w¤ÇÇü7mĈÀúõÖY"•­?ùÀ§ß:i(Z½Ç q‘'Ž@SÐ Í»®ÃXnÈÒŸXIãšÚ¯ãp,‹€=ßx¤É¥òÉùÈRü”øšWg´™^âCð¥gçV^PbÝ hF‘'%–?ynh5 ó°ìrz¤÷KûàÅ_gXù¾¡9×·Ë-Üóð].D§@?ÚÂD¶”²dÚpIºW¨®8™û˜4k]COŒ|Nþ„–ïë ŒÒ×âçä'P>#cz]/ø¿ñ‚a‰©'Pù5ã5à({rCH$¡1é–×ÉBž+áF%U¹—/Á—µÝêÌ-ßyü T‘æªj|z›%EÖáij1ëz½²ÿwŠŽ‰µÁPÛêw°N×›ÕK‰‹rÃÐ\žÈ“‡<Éë”X7»)sTJê‡éññôo" 7QÁP‡2&é­ŒôS÷eÄÅ=•’ò§¥ lÔ|/ùÏ=•,šDªxâ´Qx½ä”’Ïð'ç¼°dËŠç¯mé±|?ŽG ýØ—I·lu¤2©gþõÁ8/_¿ *µ:\P«‚U‚ ý6+;é4ujõbåZäåϬŽ2pÍ}Cä5Êš¯,ª>ß”›UVéëK[Vü»[ªï8üœ@·HÄgüsùÿêìâú=”¹„>¬K%ƒ˜ƒþŸÏV””lZùá;)X=N†º‰9‘qžÚˆ@÷ää³xè̓c®EBü^D,N8b?û‡þ@YÊÿ´ ^ذÔ]$@IDAT599½§hö°¹/,yÏsÃíS†'ßÍÂÄ7\é¨?šyA>¯hÅ?^}µçkóæ•_aw¾©•<œøž§J_ jT>µÁß0ž’(¨‘ô¨h•ˆÏb*I• *ú\ … <•@+tw[pê'â­¥):z ×ÐiSæxz{ÍÅY ¹órw}½ÝU®Z­â­ßzØŒ&H#ßQá{ºKÀÔÞÆø— ½z Þ­úR´é ´ö"X]•Gk‚X6¾ýûŽt§KSÙ©ª¶V,*©J+ªT.®® ÐóAnEYÙû·üñþÁƒ;˰vµ8g~®•][Û–>j_êoR|ü€ Aÿ$>»`i˜ÁIùí5‚aVz\ìÇ.:qÉW:¢¤%.‹ †—ûc@kâ{wD–<'D@…4ðÎi#4Ë>ù%´¤¸æA„àu'„¡ÝU~hÙ2?C¥8B0H±ø†TÐ$¡kmmq`­ñ+[;hΖéé+Û@jià< ü’ðã^:+O JU©U;?H|î4þæÉA°—­Þt~…­›~ïœIAaáoà%Ô¯{iH¿(&¸»ê¬¢¥´t{–”_€~Úmº×nŸ¼Â‡Yú´<ÿ"ð—2&}Qµ(¦ÍÑ{Áˆqax[Ý-,ôÕwc_UÕp(í$É<œv>1áÆ©öJˆtåòwP>äú’¬âÜ~u<¯¸‡œL4/ŸKôeu5#2·²$Iƒ–ê¹55pº-|Ï4/…&'_¼bfWØHÒ“‡þ¹ìS‹ÆåΩ#®°'ßĸ:ÝB:C¿¨0ñhfÖxm½A†¯«Å÷x4ñmïjCÉÿá@þ[ôe†áørWc/ªÔÉÇÃà«éä㉯ÜÙä¦Ó‚+z¡quÁþ0ŽA×gøG’5¨®ÕC NÕ5z(«¨‚šÊ*=.•ôÉ+(éq©¨\eEš`ÎÂ%Y Jß Zøjyâ‚}¼”€-†L¾Éê­ÃÉý¶¿=ñ°—o§çÂü¤[' ƒ]ƒoín|Yì:ü.Þ7Äsº çä»1@6þì©‚›ûºÀ7‡«YIþ“T…\ñª'Vüˆ…aý£hNÍÿm؈ßßÞñ÷yÿúê­WߨÒD9 gˆµï_•û¶Ì¸¸‹‚a ¾cÇSŽ8'áOUú9éñƒÞTy oFnKe. [sƹ/,½OÅѳ§%€¾àyâ´á»«Žd\›óÂK8©½ù9òñä5&«vɳ5ú’ç‘@{t é$ˆWõŒ†nÁü0îH^¥Ñ •W™Yùp,3;?”­©©}|îÂ%k5ì½% Θãý`â«Cmˆ}ðK*Ÿ;~øôñC}¥޲sÃu5H°jÐð^‹áçðù#Ç‚ó ©Î«A}ì?‹ž>É?Â̵Ür»‰îœ‹¶QñV,^8–æ-H²ä„^F³zìo>žÃp¹ÒÝ7Œ\´aðnCiE6¼ÿÓ(Eâ8·_û-Þ¨ öá?l@6³‚ƒÞØ[ŸÏô„!¡ôèX©¦ÖŸ¯Ù.í;–)”—'~ýökD«p¢ ”uж¤Æm¸÷[’­Ãí“>$v~}/Å—õЕC*øRüÈU­}3,)é\ƒmÍüøî»ïÔ›JëÔ©ë³ÿw½òuRÍÔ“¯¶.¥åU0ïßß Yõø‡‹çÓóÀ©Rbâ]ó § >ýй¿?pÕ*AÒˆ¨ÑQIª"Q-\òöP_ò,ÿGe¶aé:W3y(öÒO9BßZ1Ñó{ï‘tXµ5Y_Y­ÏE‚=K’ 1Ø1v­Zc ¢d*F£Ñ#y¸¹®®*­FŽ¿D@R/êõ¢X޽£Åe*½A4=KAEò—dœ’•jcˆªÇ³y@ ´qG~©µ¤xÔÈtN²|{L¹í®)H¾Ÿ!ò}ÿM×´ûc %°Å>»¼o"ßáC9ù¶E#´àœ!hŸÙÛV5>kÞG+øéŽGÀé#—Ýo?‘ðfüuîÉUÿ]¾!¢®gšê>AZßåªP$MÜiXæà˜é¢$-Æfv^(My¢R¬}k~‹C³^Ú»ÿÀ•2Ürøä¢(EŒŽãO®„ßÖ:¼<\ÁÅEc¨­1D´îHeíMÑ?¥ó1ð>Ž„uˆBø…Úó¦ lòÃÏ@OB$«zâ_qQ-~1/Ñã=¬¹ëú‘0rP›TžžßäÁ¦g×Í??ø1?ìwRA:ùzúD„¨)òmHg6ùzy˜ˆu£ÂÒzÓ6’¾””Ã…¼B8›Sàq6ûâÈ3Ù#õýSYâ©ò9óo@bþMˆ&aUbâ8cW~£ ùÏÖ#`MN›ÌÛ¤ùv ìâÕcYX /å»õEWÆ5µep0í;SaG|Ü´Ì칃ÝàÇc5èß`×y=¤ {'Çë•!äé¾ËºXˆ½ðfddŸ™™Ç p5½d"N»ñÔADîKýYJL\“±fÕmñsõDœ4âp‡¤—î@"þjC_¥AM­[·a·8¸o”éåÙÔ~|G€#`D€ùÊON• pœË¿µ`®ô€óõö0t öWwòö`R.Òk{¹ë£Ý¢‹ÔhãU"&UµP^Ye•ÕPQYʹݶ"ßæmØÉ î¾~¤P«7@ŸÈðónó‹ÊÓÝhêì ÙYÔzÌ÷øé8xê¬Ç“g§—UÞœmØ™‹DßwsƒÞš??×¼<|¹õX“€Ó Ãdýyý÷á(„€Û&G”ÈMq0}%ÔêÑ»¦ß^2JÞÄçvˆ@˜— &v×ÂÆ4£\èkÔ„/ã˜:[ºïÐ…êõ/6 :åÁÌÿ#dÝ Þb)Š6£ÝIHL$l¿¦)mpìT$óp'­YèήEøI|ýèæ.|òçþ|Úž˜¸U“¥ßqý$ßHÂåCøœ#Ðn*«j ¦Æ€¼Shóààv¢ƒ3˜óÒK>PVûT¶þÔchµöñtw3 é¡îd5"´3àŠ6Ö.j›³KÝæc¯v å*ý£ÃØtû”Í‘ô ð{Ê1ÈáŸU•Â3HÄ—„jB_ç.¯†dóÛ­õ§IÙúMÚoOÿàà;úGw‘qÀ¥9ÜûO|nú×ë^Ó2_°_î 3ðUÇkàÉánàáB—°ã%¼ÿðÛE:œ÷aíÞÃIÖË–pÇ«´Ô(zßþõX”õiqqñø½3ÉÏ-HÀ)@Þå)¯–—KKÒâcV¡5nÅë°ónwê`'5àÅpNž%c¦„*a·ÒëDã$~;pò ¡\¿@”ïø¾Ý`TlOèÕ-X÷˜Ò«g“òlud\È/,AýyŠ[ò±Ó‹³Å옿øÖ—,`2›NÁ'µV7¦LÀIPë6lüä8¼üÑÕ Cß §³·Ã¥’tvyè´^Ð?ŠËSðåâE§—=üÆ‘ ôͺê¸Ñ3Š£Ö¾î> ˜0c6y@ {”*ïÐ÷¦=µetJJ2†¶¿M¥…hìõ~ŸÅfåsA ùl ¨´i|ê¾´>°zÝ·³´vû¦íˆbU×Ôb×wv»²êÈò´« í<˜ê‘ßjG9­>ëöÔ(½ \:{moõÁvtyÙ|(m~L¼ŠžI¼>p£pÿÌk„Þ!ÌõŸU±EA© ìØÀé>¤ûÑ?8dÖ˜îQºW­õl0‚ÌÿCäîÔÓQÉþîââFlá^üj`UªÑh ¦ ð³Ù—àÁ%Ÿ²é‘—¿€-_» h•‡5¿ï‡ó8€«#Ò‘Œ,xç›ßPºGj¨Ö§+•G®ÇËŸþÒ cô‘O¼þuƒuÍý ƒªj£D­¹}:b}}=hh†åRÚ¹\8têÉ¢ß}ç±Çkm˜“¸´·d¨> QÁ˜{o Í/ЀDž,ƒ@÷.ðüÿÝ N^c>|`áÛÂSk°ÖK–ÎCrz¹ë´®®¡^n:‰ü;j*«È…´óø!^—b{Þ#/ò¹˜ÞKžu²“ŒB$eYþ…k+Xè>¤ûQ£ÕvÁ2Ð=J÷ªµž ¶ª¶Ýž7|×®ÊèäÔÏ¢SRG¢L¿?~½…Ý…µ88Œ –²UBk"ümö bó¿ »0xHÇßq½»Á›Oý tv|¢îô3øA±ë`Z«3'ŸÌ|¿ŠJãzZ@ÚïO×ì0 Ï½ó^ÞºWì¬x-.ΣK—¨Di£‡›kÀs÷Ý > {‹å;¶7 .4ç–±B¯ˆI%Á''.‹n{nÎw¤5^²Ô•MYÕ ׺h;ûúxÐ:‡MÇÎü‚=Ç$£ˆ ¼#›­kî¥ȹhÞëÜì®6ÛpµnáÍ{YR::‘ˆFc[;¹¡cz/â¢Æ´ödÇ 9o{˜Óý¨Òh‚°,tÒ½*ß·öP<§-C·¤Gº'§>®òö -òðþˆ€@¿½6Ãä/}"C¦Qä¾”ãgLe¹T\ÒýI ßlÜìÄy¥0ÿÝïáhÆÓ~w‚Å­Æç£?oKt¥ϼõ¬øa+y™€'Ï1 ;ù+¦”}±ÞørüýÕ¯àÅ?£W†slý¶äãðÂû?²õÿùn3d^`cUÙ¶+ýÃÓÂ5ñ}à§­ÉÍZ²éyüÚçë1ï/aÉÇ«!ó¦h…o}ý;æÍ¯7²z‘ÿlªß‰:É Íé÷öÔ“¬—ŠŒõ?›s‰Yô¿Ù°ž~ë[†Ñw¿î!Ìl?ªóû+7pƒ…ÿùVþ¶÷²*жïýéçó.ÛÖ–ô\}oåf ˈA¥¿¼6oúVfª­”~P ª.Þ:QÒÙW™•Ph©)²çý3ÆàøÐáõü€B«a“b[ƒ€SÅè<ôRg$\­R»»jµNÀWS½Yê9ä®Oz7NkþØ?ÿŽ~ï­”ä²P—òÃË>c/¿ÏÖlG_åÔ ÙtºZ·ðo{É39MÜŽµŸüü'P—÷I äݸXô2<”vÞ´zZÏzþk†Ñ5¡i£ƒ-ÐýˆR¬–|ŸZëÙà`HZ¦:‘Û¶U ïö=å^XRi™“´"×3uÏ1W—ú{dËÞ#€ã `ú5±ðgÊ 8€D™\¥¹¹j!éH¦)w’®D`øs"’v„Y׃{o]q»›‰n-™ÇA¨,hÈ»ßlÂÝÕpÇÔál0ù8¦DîãfŽ OÝ9É­Öþ™j:ÇÕ¦$ôÇçØì1_¬ÝÁÊý÷¿L†0 ´BÖ~"Cú )3Çų2“ÿlµZ0iÖãó£–ž#u ôL,CvÔc@{‡IÃûÃd<ÿ.Ĥ;”¨ÎÇ2³àëõ;!¾O7†£yè½±âÇ­0nH_ ®ÿö&òûüú—ÄôsÇEîY±dÁööæi«ã\°ôzü‚}ë”aª®!þ¶*†SŸ×ËÃ\!â"Lwj ZYy²vY:É–4š³—;Z š|—”_€¬|ãƒU%h WשwIß,Î>Úèy¹Ù=,³º“;ûzâ‹ð"Z\ö@o´lQÈò¦u ÷·`·pSç$ Û“g KP'Ø{8zv nj·[w$= VmK†ѤĈ Ö…¨ÏÁ™…•ì:W £ºÒEÇLh‘”ïQó{¶ù¯2Ç„Ánk¥–Ôç)(ÈÅ¢ˆ ël“r’± ¢²Ng]DÂìz„™ÊÓ³LÖ—ýÞqàM?Ïž' {Â/î‡;Ñ¢MÄš¢ûKFhÉv6&À,ë¦ÌêÒ¤Ó1Ý~-ôª?mØ#œíUŒrz6PÙȪޒ¤EmÏô±ñ@éQ±½°¿§þ•TŽVx²x?xËxæz8ºzû÷WQvRáø,¢DÁu}"Ãàó"€aÂѺÞ6í>ÂÊBë{t buÝu0ø‰Ãú±tüV§M{Ž0‹ضïP7-Yˆè µq¢îdr¼›Ñz•‰/;"àÔºóÀI|à÷†o±›”ÊÒ³[P—)GáÃè!NÖ²LQP´ š^~çr ˜eë(¦rÑj@ƒû‡ãy¼eë†ýî×½°ÿÄ ?£ãÑ¢C¡¦Rêɳx¼¦_®Ýi|ic^”vâ ~ûþ“8ð«<Ý\Ù÷i£ÂHŒFÚÎuÛb٪𥊖³fY£z5‡-Yã¾^¿‹C]Èýº‡Á_ð¸©Ñ.ðßTŠÒNþ⛀³J6¼WëVñ™= pÍ€¨ã†>­É!di¶E"RH%fOˆ¾HB)ÉDÑÜúè…:õj”lP¢gÊ›“9ÍBò퇖ëžèþ’Ýc$ù#å$Ü0f³ð² uÿ 1:%ù\u«Ù CqCò±3Ì’MÏ$z†Ò„Þ<Ìwkv™¢ Òó€žq´,§|„êD½‚r¢qçóŠÀ-Þê_aGê)I0+¿G«ùo»£Îü"Êòò!§Þ5"ö惉ÌÓ3L–¡P¾è´qöøa‘ʤ9ä2´=‰žÕô‘ò{ò 1Ê4Âí+ç§´'O[ûèÛoëjòJÇ÷Žà.mØAóÔ¹\¼ý¤†£›mX&%œÚšdØœ„+›6—ñØé5¦cûDÜhZnn¡u„¥ÆneêŠ$½äîCpËDzÉù³„<꾸¼’‘^9¯J_Pl|IÍ"x­Ü”Ĭ5?mMÁ¨VÑM’o:žHô¾£™˜ÿ^¨ÂÁTCë®4ÕjÞ-LÇ!Ö!9¦èEÔ9’5GN?£™ˆ÷²Gg1Lë¸é¶ù”S(ó¸÷Þ0_¸±øbÞäW´©DVïAhåêß½ ¾¤ô&yéD¿B²< »q=|3 ÛÓ«+#ßd £ç(´=8k[Ü!×ì9™‡urÕiÙ69O-%§Ø^ÝÐPѾ\·³Íú2Π~Ü@ä«ý–»Ÿ[ŒÒÉ7áTSPÖBÇpÝwãëÆZ¿é:^þãVº‘¿žÚY뼎pkp3¼XW·ÙoÇY,,=/‹Ã¬Bjµz†OjS妅8ÔNÇ F"²|µDy²¢©~»,Iyí°¦­Ë”×FÔ_~³q7lM: Ðâˆþ=åĺCÇÇà Ô:RÔ0óDV)z±Ü4a0ê#Ø9Í·Ÿ8“˺„éåBݯ8Òžô“”§cõ /´‚±n\ó\r>4¸é¾cPúBûFuAýd:ÛLÝÒ„ uIc43Ôhúâ„ÑÚFnÒ†’d¦¾ðÈ¢´ßl eж¾^Ì2Gç"K“<§ ºxË^Z-Áö3ÆóÈåäsŽ€5ÐJÂǨT"‹±’Ò”¡>š¤+D¶)Ï-dƒéc}hE¿1‰ ŠúIÏ*¬N:éó¹ht0D%k: t$)K-S ÷ÅÿÑáA0¸o$³`Ë+£Â™§™u;°¼éYx£R¢^CôJ'Q6C†²”“u¼kpgæ–±/Êø(õŠe¿©çQ¶z“!aÿ‰³ìYNcZhy`®lÿ+ý#‚ÊT(¯OW×[å¯tLãm4Eªª©AvŠoî,¿Tñï¹/,™@Ak﫤ß8¼--W¬÷D%ÁÝ ¬¨»g=8K>úYÄ{ð¨ÅV<ûl½%®ÁÞüGSXƒ€·Â&ÑT•µÎ\~Ò=l’GÏ6U€,ß”<=Œ j´Œ7•h ‘y¢ uÓËmêˆAWìŽ}ìöIðÚ·ÃR´TSW) 42OMu‡ÒvÒGRêZ§‡¤¿¯[GÿúF…ÀV”¿Ð`+²¼†’ݰôâ¢õË>Y˦’²*öbe;˜ýÛw,“u'ÿ‚ݯ¯~¶-cÑûÁyæ %3äþè{´ôSþØõ…–~ãK^ÐÅe•¦üwHƒ||A›§–b+3­Gý@3’¡8Qrª{W íúÞ’g°œ_oÜyH̼pQ Efe$I—ïÿPüXÅA”¨çì“ÕÂc¯|É\ûÑÀÄ ÿz#íCpê-£gyK¡™tù®'Ý7ù#_ŠžWF“Ž»éf4$hÑy´œÈp0÷æ±8ާæ£×‘Ç^ù‚ÉïH^Nå‹o’Ñýãßߘ¼¨I.@ĬS'«7ýîƒÏb9݆²Á|®“ó7¿ÚÁþ>0kâ`yóç$×»oúöü$™akÓwL=|Ü}ýHˆïÙY뢹OÄàN›§ž³pñã'¾×¶Uk ÒÁû¿óüóù(§É;~šù1ïàÜyvM!@ïñ-IǰGå{‘zeðûôNøAâ‚Ë]÷4•_gBÀØ×fúi±z‘›O;‘­3>Þ@~rC›‹CV¡¦Y’ÍÉ8éͽ¨NœÉf/9r÷Û»+³Ø˜ïÓx™,ÆdijìÞª©îP:VöBp±¨ ºâ ‰¬Ñ%Hzå4eÄ@x}å’•çZÔwcD2¶‰ÊNVzzÍDëú•ÉOÈòN.Ï(‘¥‰ê“rü4Œ@Íù¨˜ø2ÊEŒhM2y ͨåýIßn®q§gM¤avEk8‘QJOâØr*[Íåq2´­qyh¥¦êAÏ‹·Ÿ¾Ë¸CÝê£ñ.T6JôÁ/§[Q¶b|~ ¬wŽÖÓ@Jy0%ý¦´Æe rϹyÓ|ÓóÌÜÏyã:Sq¥Áèï={7mjS¢†4Ñóuçjê‘Øšt,{Wß4@Ñü9óÿcùâùŸãy[ÙЦâtØAèí÷õC§Î¿L½Ä$ý±túi =8}ÅwŒy(p Ú¥;k'rJ­ä^˜Ž8?½›i<&^+E"ˆ)•UªüK+yj9õŸý-?†ïÙ e9hm=Á¶jÔ®Ð#ìÚËö¤Á d¹¢‰–[›¢Q¶AÖjò$@œ#çMYÐ`žÏQÿ<¸o’Ò‰(…)†_q4~s‰tÛ¤¥¤Hä2ŒÜ€µ$‘‹tŽd‰!+Ï&ônµŽ¾Ž£ÑUY¥ˆ¬›ùX|áîA) ùË¥/i ±œ~“Oß)èaúØ86‘†‚’;3zymÝwœY½£žÔß»ÞxC:M²‘_rò¢B-õON)êÎ&‰ ˜’S/ Kék¼U(4}r¶AÞÄç«#ðÊ3Ï”TÒME%冾ß*‘Â^}œÓ˜’N¸¢F|,úßnœÌÉmãmæ¿IK.“oy½L¾åß=§²5U>úˆ'¹Z[!ÌÉw[òhï1T†Ô–?qçF3$L'tûøéC/,]ÿвeÆ.ŠöžÄJÇ{ûêÞS©Ô‡þ³r³ÁÞz…èlËÀMkÑû9<èÈ4¬w Æ5‘9G@™‹FýwI4ì~ø…¥˜¿¸á—lGžØójÚÌꀵF•Nçì0&²žˆì sZç6“0Gï"/~¸ uŠn8¾lØqˆw-Ê5h@çìk‡2‚<Éë÷›ö‘R"—yT¡D_Ç4ÈgÂP£ ±Æû5õ›ˆñ·÷BÒÑ Ô[G0Ý´¼åG Èë‘lŠÜGPHsËÄ!@>vÿýõ¯ŒH»¹º0w_d™‘ùÊ¥+i&Íùàý »¼hЩ<8•¬Däù€,[ÏÜ{j*Ãa_þu)˜m'/+tî«%Âi;z3xî•è¥ ž¬‹xùÛp°¤ÜsÃ(SqÉ2FÝ»:I²iu‹(8õgß1m;7Y¼ÿùÁOŒÈË>¼)#ÈIA@ÈÚÓšDEú0 ò.§MèåÑõFσÕðíÍõ ò>Jž¿ñÅ8t<ãàço.»ëA®(Èí銚5÷·àÞÇÃy²$.\+âZ|)á@=ը؞­¾Þ-Y>ž·²8•>ÏEQzê«(%J ‰|ÁÁµÔCJòzžwT2— hégÌ5ðÔ‘ƒ˜$òè[þ.ÔÚÓùÉ>‘ê{o äi‹¤M›÷cn*ïÄ÷Wqy|ñËôæu“aJAsÙû2a`Ž–©éc4–‘»`2&BG{dÀë8~‹d-4FÊÝÍèü`oöt…9ÿ¾Ù`å·þ÷+“€Ò)’E‘1Ì’‰Æw¡o}C­^<¬Nš˜8NoÉó)=o.AéÀ4·€SøyK&sÒkÉó\)oúnª40´Ý’œƒE’çúª7Oä·—f×|óU—I_„ç ÝôD–ƒ à"™LkÉ7˜¢ñ™“oZ7,LkŠu8}úÖ(÷åDõáÉ1ø`Ñsû5®Â ½AÚLáßç¿ûƒøJÈ@ñÄh-DòÅ Gm}.·ö|–ÚŸ®Šñö´>ñú×ÌJM11,•HÎC=ÈôL= Ç4Ó;ÎXô|½¶RAUœóÂ’q‚¾Bm{ÆÏ@úRëºB[QWs¿øæA¨g!{#‡ûŽž6m&›r¢xMmhÊ{ו‚F™{&“ó¶Æœ<QpªSgsfâù–ZãœJ='àÔr§³ëŸQ]ƒ‡£Áy;ÈM–JäµEŽj©s4•oB5#à´m÷…ZNÀ›‰¯³+–,ØŽ'ŸðÀÂeC*«jÙwì̤=‡3˜[ô—o˜:r€zìàËAÚ¬ÀüÄv‹Åؼ稾¢ºúq,dýËÍnK\_°G_Å koKñfwWäáýȤ¶¤· –êÍ[â ¬¹qMÃcêT¯u–×ñ¹òÖ¥Þâ½û<ïâW~‹:f >\ô\Ò‡‹Þ£íì!€°‚j‰‘vva7©¢ªðãÖâ@1 ¬Ã“}!@$phÿ( ª §%&nUŒ¡îKoÄAÇ5ZõŒéè9kÙ#³Ô“°`I¶@¸©ÀM-ñÖÒ²^)håAÎH×OAÉe§µÞÓø´[ë|J='àÔrgrvšrê2´Ìø-hñDéä%Tp¸ã´®cÕä¡…KG×ä—Ãî÷9( “þ9g¦jÂPËY¿)„üƒK>…³94~÷ê‰Ü’‹´ŠÊ*æ)éêG´lò{Ü÷®-É\ÊQ¤]gI$…Àá<î¹°³åî±lΜ…KîI\Õ-´³û¿æÎPOE7¶ÍY“­U̦7‘'°nÁþÌØß–}¯}±Ýù#»¶¶\W Ey]ƒ17ÎåÂã¯}Å|—·6ÿ¶ìOtŠG‚N6´åxg:F1_¶öÜ(‹N¢G£OowWðm{T6{®§³—Íï–Aèe_–Ñú½/[“º×[Å^Û#0'q¹;Ôæ/è“tòïœ:‚ô˜õî|l_D¸ë(ξcjúòîЩÀpñxD.à‘ô,Xµ-½Jt‘W9ôœQB`ï»piþÿ` ºÇCw—ji©Dcšä@r4X¿qÀ¥ÆA¨š ÜôЬ ì>hì ŒÜõ6Nƒ4ÑÀJós^)hº¢qY•ØãDÎ,HãþÕú¢JPåyúhß²ôù”ž?'àЂ¹…õÁnÂâ: Gž…½"kFÀåsn¯íäŒåz(qY”$^\# ÐgâоzMPÛ*à ¹^#7¤Yè¥è0z„莑v¯=CÍûÀ‡?þzt_úÃæ},س½ž¹jkìÿøJS¶`@­b ¦EI§Ž‘˜¥Ÿ·¥°¨¸ä1¢{— —nä墱ßcr¥·óÀIÔ÷†o1z4̶æÌõåEr˜%­;¯Á"ïÖ`líï7í…Tt©J‹ÜÉÅöîÆÊXVQ…žf¾Ç8a0%‹?ZîPL!èÉ]œ§+ó MqŸ{Æh ïë¶DÿÎUx\(û±9¢º¶&a˜C¶»$Š¼Øšò\q_I\êçí.Ý7ãÁ’äûŠe¸ÂƦôÛ²'°+ÖªMMŒ¢ ¨­q}‘×ÊOÑEðé¬KØððkóæýö¶ªε³å>Ǽ‚£¦Úùõ3-óÇC WçúoÖã(Cá‰#`<°`ÑDÑ ¦àK¸×wN¨›ÛVä›ð €`Œ‹Ü”ÞÄ–"æ’Þ›¢âŽbT4Pð¯[' cðÑþkþØÏ"ûQ'í* ¶ž9n0ópDñÖþ™Êö¥È¶v„Y×CÿÊ£X_"„‹ºücÑ%­ ëŒîk™+Ô¯×ï„ø>ÝXˆû*Œ¡@šäDÄáRqîk¼ŸF+7ùY¦«·LÌ‚x‘g’døb™(ïqXvòm~åWRVÁâÐrSç.FשŒmTlxpÖx&—ù®Øc*ƺPm®=–O.Sb¢¤BÏT£‡¡f½)·¸ò~|n9(®ÇgkþdÀ*Aúû/.øÉrgsœœëÙ„ãÔÉê51·€ú)B.÷ÿì`eúÿŸ™ÙM²é”$$´ª"’ ¶<9¬(Þ©wž'èÙNOlà]þ¢çY~zv°ŸâYQŠ`!…ÞIBBKO¶ÍÌÿyf3›Í’@B6›ÙÍóê2íwÞ÷óîl¾óÌó>¯ßËÏéZo ÚyœbK¿r;0ÜÚDAQöêÖY¼óºßJ±Ñá†hE É²(/«Ö¬ÖÖúötY±»vr[´)Y³Ÿúëw6Ô”H´@ 8 t½I‚Dó¾ƒÅ0ý|q:l-ŸwÜcm'þCÂúš‰éšø¦}gòS_¿-Åz˜€wÏD.EÇËܳþÒ„]gJÞ×^ƒb›,ÒТNéä†ò{ô_ˆÇÎÝ“.†éÓ>Hè6•Èz§'r©ª±gÝõcÍYÊ4I€Wò¼6MLF.5ϼ·´>—A>väQØŒ¬—|fÄ5lºá†äi³žZ¹n[Þå“*Ñw“Ð$xó¿ß଱:lzùæ·žzü+ÿ\98®Â¼•ýXUS‚>„®0Z!æHôÿsYmZY,ŸnPe@ ¶–¸¬ß»ŽËpA¾ Ú]A]­i³f÷E‹Óç}’ºN|ø–ºxF¬8pä8º™l…û?V³p¯ÌÚ>ÜÐÂÕ¥—¦ „Œs“5_nÚß·G‚6aSN®/z’ð&¦Û&Ü_x´> "mS=Ž•ÖOŒ¢Ÿç½ÑO‰\MôDXÈ]Å3y^;,4È’þÔÝ×yf1Ü:M ƒáëÌ‚(Ì7\å©dfØíŽuÿùßò°ÿp…èŸçFªÑavÑ[¤Ï¿[¯ì;t‡ $0ÿùͧÙÝaø¨¡ìÞJ%žÖov?i%ÍÀ8}P—úÛfç vC Œ^ ®Z~þùçè‰!~‰>Ïáèvb(Ë·/HŸ,wù“X¥iµÁ‹ZBsaI)ì.8‚BÙçNÑvë36'îqÿ^‰š ËšÍ{Ñ5¦‚6Ô äÒ’‹–w²V“ ÞŒƒ1)QÙT—Rôg§)À)\„NZx7r“ùaýŒ†e×ò6õϰ=µkþˆƒKÑe}Æk|Ú–VfïÔÜOB-âÇF«[cõy3sæAU'>Z&Ï~w±¼mßáÆ²ñ¾V ûî•O¾Wþõîb 5X¢xûÜÙ3/añ}v`ë•ÄÙßáÏòôÿN`÷“ñ}ä1s÷1ˆÙ!:Ý`üqëÞÛÑubèm“/‘¢#-«]ë«3¤_wÍïûÿÍý þõÞŒbRÚ•Dó{‹VÃ}Ï̓9_¬ÐfÝMè­]´9qã;GYÐÉÅ%sÎW€³öAlT½ÕšfÙ ‡—ÿ÷Ì~ûkÍG› 'ñœ×{u>¼‡Ñ(»h(+‡G^ù6í>à~ Ð6òùµ_5: á4ï÷??O;oëÞÂFr¶ß®“8 uõÆ] ¨0÷ÕÇ?Ö~5iÙ•ç<õÄ 0 ”WÖîzí³ïaî‚•*Å£æÔ:GOVÀýÄ œ/}ü-ìÚ些<iN~{öÌ÷Ð~ªßUë.ÙaÎnµ÷¾]E´Þzê‰Ñ´l$Ñ»?ú A¿4Wj·›ïøåaçö?çÁ[®ÀÍÀN_ÿüWØy`‰Öˆñ>ÃúM ìqíÏH`c±~¿ÀEÜQ¾žêúãÆ œáÅ–ÃÖ]ù[>|é™›±š4› ½ƒ'ßÏ&Ÿ0šqïãéœ|M€¬ß(À÷ïÝ-þßmõo¸¯ëçËòhö>ݺí].Y› ½FnæÍ‰{Lëà¨1ͿܻlÚ& 5ùŸ[ÂB¦Á—´BêétõÔóx/+1ò‹%Ì ×ÙH jÔ-{YU‹Ð÷­™3©nͩ˴¹sÍpðØ A„8f 69±«ò›ôâ°þ½€ýÃOOg…ã¥Up]Lö,=ŠœÇJ«LøUÇè¦Â*üÎdŽ‹úäÕûî;óäÓ_Š"v^må×àxù^w ì‚âfÌ+$ºõ´¿´~—¾—L - ü¸mïh† cÎ0ð°-ë௲›ßtýÆÄ7íonÜã3 ߦ|Øó/>]=©N¥¨ˆ°Æv·ë¾œhÉ?ˆ:Kœˆâ›à½5}:9åÿë_|©údõ­žxð£%kÌÖ©ý{ÅS¸J¢êôˆïŒõQ¾O…w8¢½9Ù¸û <°W7ñ/׎òý…ZXá½5èetÁ1 jl”Eˆ‰ÉtMƒñaTÁP¡JE•ÿ¸©Z]‘MŽ•X…ˆV¨ñ‹¹ø0ÖBD~Ïμ•ÈË«ë}Íb"{µ²4>=D˜èd ´VþØ­V >‚½¹¡ï‚¡ŽøGq¬Iœá’¿tÁСi <ýhéZÅwv’éqœÅp¦AjvvÕ¨‹Þ2Ïžû×̧SNåê¼Â’ëö(>J4I&¹wbgãÅ‹ñc ®StÄA´QÍæô6¤ýÿ)<å¡#''R÷W¬6;ŽÑjðmLvöÎý—Æþ`ÑbÚ·×$A4AÕ§ËuâoÇA«ÕñM±ÍžTr¢2¡Ž¬]Q•ZAŠQyç¡¥;Ï$š¶½öä#;ؽ¤ŽP-X€·¬Í^#à«´Ì& Zdb[QŸH’¢Dà.ïŒ#•* ð@ª=×5 àÑ”ØèH£u°äŽ4PÝí'ùLËv»\Êô†ÌL!¨^í½‘9“fq¢Oæ½™Ïö°9ÊŠs$Šå ö>–†3¦ºRè 1Ë8ÈV -a¡š¡Ü‰¦mYVAVdµªÖ¦TVÕJNYq߃xž ÅëV´*g -O”¿Ë̼Í:ý‰§žÇºmÙW(ã$TNÔäÛ_}%ð7°m°}_!¬Ûš'£_?jq­7½5sV³ÜŠ^Ÿý¨¯ªÃå4A€x`š³»õ;¢GsNáýÄw-8=(²Ö¹Y/)}V·E£æºÄï÷w=ñLº¬:¯Ú_xlRþ¡£Cð^®³Üj¤%DŽŽÐê.ј´Â7p…!_m+ÆœÇÙUUt}Q0Ú‹ZYk“TÇGÖ%ôׯq3>T¬AXbŠ^öjæ}gj¯ÀK¿`Þ Ü ü¿Y€·eÀÚ-²Þâ]Ì<àú/+ŒoÈ7ØíÎ?DŸÓ^‰XŠh9´öÂ[_®R·ç&¡wN!þYËKá3ZBàÍÙå`~úü#3óý°ÃrñQuž‡Vö^UV{Ï*«-ÝXÐâ.Dã`â(TÖ¤ÑP§k¡ë¸† Zߡ߄E»|1>@Á‘Çûż'Ñ4bcVx<Ÿ“ °oE§x ðhvAiÉÀ;µ;ý4Ö¥Ã,Àu¼ôPSÈb›ÓþÚê»Å?$^ä‡+ò%‚@Õï,üYÁéæIP§Ï™=ëÃ`k£ÑÛCþáXǬºÑ«ËõkõïÑÛ ð`/²¼ÚÓ%)Ø›Ëíó Uog0¼Úæ\>¬ÂÇ¿lÞ§Ï''&Ð4“èÓï.‘q¦O§$ Æ lÞnÉùœ— 0ß`Þ Žžð¶€·‚dàšYë°<ðú/Ðkê?1rCí‹W+Ng“s%z3¹þ>&°a{><÷Á¥´¢ú¨*H—Ì™=ó_‚‹cL ™êUD3OàlõªjŽº7¢"Ýë¼ü(¸Yr¹¡TÛU°9ÉMðמzb?΢1ýÀ‘â‡K¡¨cœ˜@“(^õ¼¥¿À{_ý  z …¤¾=û1ràĘ@;`Þ ðVG¹ûlKH¬{W:ذz?ðr++ ŽÑëÆi圧g}ŒaÐþµa[žðÁ¢Õ*ÍÀlj xX»y/<1çKyíæ=2FÉx&Q¼èò9™3ê­GÞ'ð6`~!Àƒ0ϳªÊ¨Z;gƒsäY–ħ*èP޹¾PnÃÉxø+¨]°õ~kö¬™ÓgÍÖoË{¬¼ªF¹ýêQ¢§8XÀ\qòõþdù:%¯ð¨ˆq ×™ó8¹ÊönW àgÙµ¶zëwXÅéçÔÑÄ¢×Sº¡pbíA`îSO<~ÇOïÛ} øÍÙï|-þiÒ%¦sS8,j{ô…®¹ïP |¿n»ºyï!@/¹RQÄ8Ô¡n\&Àê ¼'çGOHÏõúfú~­º¶0Œª–BL± ã„þNÿÓ}IZ÷ÜvÒ•öêwOœQ!ø¨ûœ¬vâw ¾?<óùzݳŸ;Z¿ûše°”÷öì™ïMÏ|jKUuÞ+Ÿ|?pä~êõ—§ Q¥©ÜŽFЀ»Àw¿nS ŠŽ¡æKñ·á•0SøËÿÉ|€Cå4ÂŒw1ö&p\^ŠŽÃöüÃp²¼ʪjÀnwhÑê5 ~0F’ûÉѲ?k—+=/~´Ü¿}‰Z/4Ä1áÐ9:Î铤MÊ*ठ8V r½ß¼äëJ“`-À¡§âµÙq0?ô»g'‹å8}ë¢?~/Á&˜Ùæ©î´Á™Òb#±ÿc"`0Z<{'u º~osžAt¹™³²ï}å•a¶£å3³Ð%%wWð»óÏ•~wáy` ¢–rSˆÍŒ¸« 6ï9wpVV[MøÛ¿úŸO”’þ[gša1&`P#ÀI€‘È^‘µ VåÅ@”% ¢"à Ôìߦ˜ÍáÐ=.ÃÝ­h‡w¯ûe/W^U …Å¥PYk…¥k·@L¤.Ií£ÓAh¨$It‹q¿Ô© .BýN¢›þØØlìû]°fÓ^­ííÙÿÔÔÞ1"~êí—ï@Ý׌èó‹ýRëÿ%«7CL”ûþøíˆAš8÷´”×גׂ™À«÷ÝgÃöýã®Ìg>p:³¿Y»õ¦³w*é'J‰qΘ@{ð¯j=Ë–’Û¶¯”ü ¥•50°w7VAÉ lÙA¦µ6;ZBJÐ r–®ÙkQ ^wyºf5›ë…øYâo·Ótáíp8´· ~ÌÁ·µ®þ¿ˆû_ïÏþÿú§\øRnºâ8¯_íLÏÇËŽCàÍÌÇò±µxð‰'^);þêêlûE«²w ýz&À¥é!mPï‰?“+,9 «²w.U®¸x¨8ñ’aþ®B›^ÞÞ½ü¿ïÈp¤ÄuŽºÄD q"!2Ü$̧¶´;d¨¨¶BáÑRåDYšœTíªî㪢~ ’øU¢ØÿÇÌÌì® ?Þ¦õæÂ™ðC pÝúùÆíðÕÊЭK4Üù»K¡O÷®¾#%Ñëåá{jŸý‡ÃW?m†÷­…ñ †ËÒÉ&“0Öp½ßi‚»Ý+svÁ²_¶ký?uìî¯ïlcýÿæ+áêˆØóL¿{5‹7[A`ï½ýBË÷Wܹo=ŠofzÛ¤0ØÓùØéïá ½Ï,¡2ŠpipßîhÈH‚0|cÖÞIFCK.ú1¯ÊÚ ¹DA¬Åß‚ýË×ntñÐþbltx{WÑg×_ºz“‚ѳN Àþ¸èXYß’“ä}rOt¯ÃF *Ör 7!ŠjÁŠŠû˜SQ7á+ß­ø+¾UR•­¯ÏžyÀg•႘h†àºûaývX¸2†¢5oʘt ÷×üWvíB´/J'wO óÈoÖnÓ‡þvĹª†€Ýttÿðv®f“—×û,@6ß+²vÀ·¿îäþo’XÞý¿pE®æ?f$‹ð†”‚w+{RR¸ ÈÓP|Ï@·„$}`D¨l…¡Ç6•=¾éÅ·Óî[Wcµß±vÓ¾ «7î‰B•,ãç¤tÎñ=:kî}þ t¼¬ßÞÝŰ#¿H®®µJ&“X‚ø5Rú¬ƒ®4{?ùö×é×]†¿_šØUk“kuûÃ%¿À¾C„ûç>=ëÏ ef~RoÍö<ÂëL€ à pr?ØŠa”¾ZµQ_~°±o³öÐCŠÆkÀwëwA¤9t@/íz$Â¥ø¶Y%›(˜úÄ·Õfƒ-{jõ§‡/îÿ&€5²Û³ÿéþIè Cú÷4t¿7Ò ÞÕ»&ŒªRËîE~P5ÎsH ÆG:"¼)ľ>hÑîJX2“JF·…•¦Hé™{EúDZÃ}cɯøë•98뤯x÷øNbR|'ë‚ëqpŒÁÙ[ Éº}²¼ Ž—UÁ±ÒJØøŠî# ¢Ö&ƒ“D©DQ•ïpÜÊÇod>þŽ_ÐÕ”Oâ©ûq áoÌÿA˜víoÖsý·ç}ó‹’­û¢x×ÜÙ3ˆoêßDè )ÀI„Y­øì» šÛY¾9µœq;Šì–¬Ù ½»¡U+ƪç‘%Üh‰úÜNÈò]^Q¥Õ»[—(í͇ÑêõÑûŸî£þ=»£´±ß¥Që¸}JÎ5µÎûªä²ûÐÕ¤“g=ñ6/Bg†çUQz+cqQ @‰ûð7¯DÆõòã'W('¾G¿Îœ'µ}æÎž5'rlÏ;üÖ¿ß_7Ž)Ò8 @Idå_Šƒ¤Ýš§âÏîqiúÜÙ(õçz2&Ð6 'ÀÉ"^¬ÈÞ¡ ¸$Ÿov;9»Î'nWs¾üÖlÜ —<ý*¦Pïwòù®Å¨.TߊüþŠ‘Üÿg×ý7½ÿé~ºâ¢¡¦ð,Yí´Í×$Ä;ìð`M­ý¯híŽÒÍÅTO|ÌFõÙ˜>Ñï÷uEEi¾yNºj«_Åsâôø³p2L¬}ôþG×¼ °Fß d¾Ø¹ÆQsž*¨CdÅÙ»¼JîŠÑwºàƒ\<ªé8ü¹îŒ–ë(¼ªŒöNôqàÉøhj*Þ×G:W–9/.*¼4¦¶bkªÀ";ÿ{þÊŸnó°r»¯×Ô N6ôîÿxÇf–ÏyiÞò䡸FgÌLÆÐ›FLÃ@Â:Ý{нù:°c^ÅNOÏyòî*#Ö™ëĘ€ N€»¬ NX½ií‚\¶î AüÈZ”»÷0\p^²6Ód"_pÁP. z¿“õ»ãzS}©ÞÜÿ¾éºŸÆœ`ë÷Öµ®ã{mboÙ.ÏÀˆth¹V-ž®&Hc ãgÒÒÍ2Wa`ü£ -{),YµÛ横2Îó é/$!âîq3ªž€™^Ê|ð$.~®ûxžÖ¬õ¼ôôKTÕùfÖ¬ß(DW§Ä'Mo‰øÖ/„–ño1Îù ¥¤âï[÷~|ËÞCݺÄ(#‡ôÏܺÄâs@;%|CÅ87ú³ïÚ_¤î=T¬:Šˆ.5û±Jo††‹ÿ}õñǵSõø²L€ €¡¸n%ÿÀŠ*+L¸¨§‘^•(B NS­ù]ˆ)®Ð„Æ‰Š¢÷»à Z­6­žÕV»Õ%ðh¯ÆZÿçêÿ~½ºin((€ŒWQ®Q“r®IQ¾Æk†REð+·Ëd‰ºJX¶ì«<oNª‹sþ¯¿f¾þŠì<9åXYÅ´¯Wå^€è!÷ï™ õíîqÚØ‡Pœ¬Êש£Ž—VÁ¡£'¡°¤?'ÔÅ'œ§B‹€¢{'–šDiñ›O>¾Ú××çò˜à²6Ã!éŠóÍ©õˆ#ñÌÛމ] ,,ÝÌš5ÜBÌ%À)ö­Cs?¡zrÿ·¾ßõôþ§ØÃ$L($¥ú]¯/›&;91ÅÜãª]¾%wç&AȱŠÿJ_TüµfQ^šyJAßüŸyø7_¼zÝs ‚®hoŠBÜ#ãf”TŸr’vä™ 8l˰Ω8¼Ÿ‹E³0¾÷š5¥>(ÞÈÔÜ8ÞDzÞÿkæ3ý²<¶´²zTÎ΂Ë6lÏw»Ö º3¡kŒ%EGXð·/Â1äb8.-¡¡l‘b%µI´ê–vŽQb´O-ŠíZœ ËŒÃí<^^-èB›ÚA¡1dàftÏAן,ü]ýþµÌGŠ|ÑF.ƒ 0à&`(Nn2†i:£åi†Kž>Ù7_>≢´­û£R\ &À:$C©Ý§6 €\)Î6‰0úo#$½ß]"\F ÂýßýB÷•‘ú½-ÚHešÒÃR\ë¼='kçßñŸŒ_|êãä¢ú¾„ç‡-.Ùïq ÑU -h¯®:%´ ºJ<:a†Œ¡ÏzÜc£×klg~FêL|›u‡~ Ý«g§älzGßæ%`L€ Ô0Œ×­ršöbVßC>\£?ïŠ&¾q®<ÜЙ·§%T¯Õ‡â¿Sý¨žœÚ€B6J¿·Aë¦Hš<§Öj¿§¤Öq/vH×ßw*Ñjüf˜ÅòÒàù@ú élB ž¡È³:œŸ1ü|~Ê}² |ˆâûîm^aL€ 0 #À©Vn1֠мá+Ä—¬Í4â_gí«²[SŽ^ª—V?R‰œ|N€¨ê¬}^8xFÙ“’z*?X[ãø öE„§Å[›.^€W$“åµá_”©0 -øMÁ‚{0´à¿0o¸;¿œæ|ÂC2º}ø>´ û:+y#†_Ž–ïwõ]Ø–R@ú‹¾ÍK&À˜8•€¡8U§¶#  0£q6j½Ú®'Ú§d£õ{ûPðïU7OL8Ï.¨«Šó&¼rÃß\AØQ5þ/Þbz¯çüÂÚæÔ¬©Ð‚(|ß„¸GÇ=Ô6¡«N´3DUä/ñ‡[ ¸uØk½NX¿ž|­91&À˜@þ1h"ï®'Wx ºÇÇBX#<ì9X'+ªqÆIwðúy-à Ø0>ð!œx£_w˜ámZ³yôˆë„Ócwi°Ÿ7:&œ‰‰¿Áw:8%ª cxãÀÈÍÒîÙŒ°K?æÏÇgNzhAEv>Œåi“¾ÔÕæ¡«]áÈ‘=¬Në7X—hí¸…!!ê„.ëÖW4–Ÿ÷1&À˜@=Ãðö°ÌýûÃo¡´¼Z£A1§»FÃU£R¡[×ß“zL®5òQ~oñZ˜ry¤èá}ö(½‡Ž”oîžàÚëúË~Ù«r\‘ÊBÌÄF†Ã¥ÃûÈÁÉžÕk°NXŸ|›ÿoÚ$0ãd6Þéçܽ0ò¼>!ÀÛ‹»7³`ÛF®ÆðžŒ¢ôE•/ônZˆW >›¾äÈ·®có½³4ºÝ ´`ÝKB…l¬B'FŽŒ.s‰oí‡Ç‘”«‚i|ÏuÙ‡ËÏû˜`L !Ãpª‰ ƒçö†‹‡õƒãeU°ì—íðíºíðÇ+Où»©QÃ)†áŸ¹g‘4¶†=z†-òðÑ9û›uSUó¬?=b"-ðçÉCU­ ²¶ï‡E«·Ày}“À‚³ä5–†ôíïHhT|7–߈ûtÖF¬[ ×iï½ýBË *oΙÔí!¼Áy¶­Ý\z¡IŸ¾¸'Ài~2BhAïÚªééæ|‡õKlÓºcvU¯í—“½Í;/o3&À˜@ãWI6Þžï CËwŒö9Tr²wÔÊØ±ÿв´h¦À¢Ÿ7Ã9ÉÝàÊß ÿ÷LúÍ0œ’Õµvøüû,(8rº't†®Ñ8®ª.ÑtÆßýº¶åiS[púc Èû×ëFá fXüóìü„:ã´Vü 9©‚øÈ„‡e ïgÓwûu™§:iÀååtQ´Â«‚·§då®ðk%øbL€ 0'Ðá8‰¡í(’•VÁÆÝ‡à¢¡.ÿmÐû ‘ãå:°' î$­«IÑ1J«7í…‚â“pÅ…ƒ¡ÆæÐW——ß²·¶æÁÝ7Œ†C˜çÃ¥¿Â]ׂèˆ0X³9va„±ÆŒ€Ê+|ñcZ^¡KL¤V.ÿãv‡¬õ} >Håî>=â;¹…0õ˶õ‡! ÐÆŒñ¢€\P¨ÿu‘µ`ÅF `‚±£õ÷ÑÒJwÅ¿]· "ðÜGo¯=Àå>¾v|ÁŠ\ 3ÃíW_¿n͇¯PÄß?UÓ3îóyÅØ6OJèãPáovÕv;¾·‹p)°Ú(JËñß9örÆ—Ž´´%zhAŒ4Îó\tó˜/ á÷Œ›QEÉ´KÚ—‘ú4Úóoq_\f¦dmšçÞæ&À˜h/ÀÉg›r%º!tEkäÀÞ np$Юûí`ô÷î©íÓ­›z†<è©ý{Â…Cûj»ö>® jÚÈÃõDô%FÖ¯g¼v\î»Ðº~NŸn×)BûÄwŽ‚­ûŠ`tú-ÿãÕV|…–n\éDÿþ«FgÑ“ gŽ|øÖQÎÊZVY'Ê«àŽk~£ Êìß+¸êÝ_©ÿ¯ª ô”¹ÆÈâ^cµÃü¾‘%<Òéçô†·®ÖÆ"tª{xÓ¯ÏKãÈœ˜!+ÊCèsq=Šî†á ª/GJÞ´hwýÓX3›a¤Ð‚U9?cØ=nðqýú²Ï훳ñ}›—L€ 0&Ð|^€_<¬¯fÁ&¶ÝBæ~¹þyÇ•n‚ƒÐõ¤±„ˆ ðh ¨d÷aŠŽB–mJz% ÃFÍÂIVô˜ ôG!޼!¿è¸&Â6ï­lÇГ tŠ ‡Gþ8NsÙ±¿ßR¬ƒpŒ;¤_w­"ɉ]ßtð`q©–§;º/Q¢A¼žšúzí–<ô:à×mûa8 mJä¶BrŸŸ£mÓ?tî‘“ Îwä•v'€ý%ä\•8-¿áL¢£½+„]¼Yá…´´s>2Wáë±–¨Zл}´½/cøTUQ^Ña›—¤¤ô»r7ê»xɘ`- Ðá¸ÎŠüw¡UzÃŽ(>oëùh7–ÈRÚ56]\QT(YEõDƒùVdíÒ¢¢$'u…¿f ÔĘ„¹Lh ½]]Æ_tžž—íH€Dò |óAÑP¢uZàE:Ñ«Io-(D—”î8h“"äTU[õÃpÙˆðá’_µ7!—úƒ\(º¬PºÝOè“q lŸ28ÄVsüæœ+»áTñê¹Þ5EïïUI}>cÑÑïµc‹Z.¼Zл´SÌÿÝaþ‹®Wu¯‡„_BB,747|bceò>&À˜@G'Ðáxe hÀÅï^•³[³xÆÅF¡{A½°nêKÒ·G¼æ:2´_°Ú(¶¡Ïp¨–ü…klvm€%‰{ÏȃQœ“¿ù´’&ã€?ò?7ã =öoŠtÛì§·Ô÷Ôw›÷Bß~Eq§f]ŒBUFb_¯Þ¸®¸h°6€Óá¬ç¼vS}äà>š°×Å|¯n]  ?fí„I— ÕüÄé=p2œ±²+ÎXygMͱ»±F ^¡E×oá3…wú¢’-­©ñÒL¿+užƒe¤¸}Ȩŧ-—>{ÙedMoß”7"u„"«_b-\¡a»ÙyeÏ5kš5iPûÖž¯Î˜0./À³ÑâMS{c¤“K†@E,iFºhHŠûû•ÏVhþãiƒziQ/èTÓäCüöW«5—­Þ£ÒÀ¸ ÎÕ®ñŹðö×kµct½['\À¼Ì}™¥¼ª^þôGŒD#bä“8Íg»±øîM]s¾]ôÓfØ´÷f5ïƒSz"ÿÿeÒ’¢éÐ'Ñ}ë„‘#¼+Ü2~$|1)=%z“2#« !žS;ȽªÛ¹(6ÿ†3;â C5Ì«*8ÅûÛf³ôŸa_.ô:Ö¢M-´`uõóª,ßæy"öÿ Ñ2ýŠmûVyj—õüŒŒŠâü/®Çƒƒ8ÑÎ8ß.ÿ«v©_” 0&:´ôÖAô( ¼Ô_êÈUáÙ{¯Õ7ܾu,X1J½hd óÅOpýåi0 i’Ïï¯Ûòa9Æ;ò\MŒQ¬qr[ ëkxX(‹/7Uÿ¬ûÏé\€h¤wòþNœn2d ê Ynà+N¢›"Ý<öÇ+ 6:\ä9ï›õèÞt@à$ÔgÜ‚ßì{Jͪê}mÞnäß{UÒ8Ý“ £ŽÔ]±PÄW¢c"çöÿx_Ekká-ˆÏ]zYø³Òî¡õºèËCën·9ñ QuÕSŽ ‚i,O´£â%`L u:´oºú³ëÅ·kŸ‚‚[@qŠ^^U£ù ïÈ?¢ÅÇ?¶îD±¤É2Ê)p ÐX€PñÔÛHQ(«®Õ¾ŠpLMaOá = oOþ]?4¥‡åhãôïþúwŸsÊÕ! ïÕÿ¤§Ÿó¹k`å)9Z´ÃÈ¡½rà’K:Ùk+—#mä0þdUƒR²0°='&À˜ð S•ƒOŠíØ…Ï÷MãF@.NêC–ïP³]ºh.(›LÇhý¹}qZûðã†]PZY Ñ8Íýø‹kþàƒ€q[™;¹g’*ÛÿZ\븭»]ÔTrâÿ DÓK#­ÕŽ-.i¥¥F-èÝžC^h±×V-F‚k„¸ 8P¯ÅXßYÞyy› 0&ÀΞ€¡8¹xЇ¬QXÀ@N†>FIä.¡µ–,ð:gZ!yÖ‡ª„îòßÿÓÝHqÝ©ÿCð¾ÒY¡ßýYX™†n&(²íFŒæavztU¢Bá]Ñ$¼2ü«â_Õ«ÉЂ‚ðº$Ä=6î¡’3ôöUešQŽ:z´)¿²ôsßSv¼ñUžðÇ”ìMß5ãtΘ`- `î)#0¬ÛÁ£õ¡[ÐÎÚŠ*+DšOܞܛ8µMw7výPtÍ©¨¬éצè …W`ˆÄ^ñ®˜åÔ䯏 53SÌÉyc2z= *ÎK©}(¾=’|¯DбïÍÄ95X¥Ð‚¥Î¢¨²s^Ð3ŽéfQ2Ý1þ!Z“[gYopAl/üþŒáï øvO‚ âý)Ù?ñAñ\`L€ x0Œ÷¬W4F$©ªµB-†ñ£IJ8µŽq¬²Z!.ÜÒº‚üt¶ãqWÖrÿûˆ·Öÿx?E‡»fdõQ±†-f×äQUJÙŸ³³Þ¼+™â]QÝ«ñQô¥´Œ»¾23_Šá¡õ ,´ ^-Ïe^ÆðçpÀÂë÷ O§äl|µ~›×˜`LÀ— 'ÀÉ2×»['X·ã Î*Yú¦÷e£;ZYÄ-[Ð9ÂìvA0š”꣨žù'j¹ÿ}ôEÕûŸî+£õ»š¨³ñênɲî­”Kÿ‚;¢=ËFÁíŠß âÒ—qMCº$Ó3K«Ö%´`cÌOO}Ž?ä>&o÷ËÙ4˽Í+L€ 0&às†຋ǸÈá›&«aÞú>߸û úÿ m1£5z»Òu‚·õ¥·¾½.T/ªÕ“êKõæþo=_ºè~¢ûJgÝúRSBÖ䤋Åù7§S¹ÆËåƒ|mNà×j®(„¼ž¶èPQ[Ô:PB 6ÖöüŒá·âóÏéÇðû±0%¥ÿ]³IßÅK&À˜h†àº0@]žo`÷N°1¿ö>žœ´ƒ .’øí>PÝ£$«(¹–ÄYgÞžô:èý®×/Î"jõæþo]ï¸ú¿†§Äkýo”~o]«Ð—;s´);gçÜ€ìÑз› ;UQ}¹[˜ùÞó ÛdÖÆ¦B âÕ?—„ð{ÇͨjùÜô­Ó‚óóÒS'¢åû]< ( ?¥Ä'ÞÄSÌ»hð¿L€ 0¶$`N$매 D Îí» OÂW?m‚»§\¦MçÝ– ‚±lšZ}áªM" e“É„à®ñ6BòìwªÕ“ê{¬FÑêÏ ÜÿgÓOÔÿtÿ„‡˜´ûÉhý~6mÚ2±W';اegí¸Ïïá]>`|'ˆÒKi_}‹wÞºÜ;ûYmShÁeÜ«ØmOcáîB8€³eÞ5á!y@•{·W F¤^„ó€ÍGß4ío²Úk ,,[f3b}¹NL€ 0`#à/F=?§pÔ-¡$H€YpvÈa½;AÉÉ ˜ÿƒËeó”“xÇi Ìÿ![ã—®@аtí Ñøgùi iãƒzô~§ú¹ê‰"ëíêÿì6®EpO÷ ñ£ûˆî'º¯NÛïðIx~ fóÄ„ór&&̱ ¶B”cÅ<Ä·`ÅïÑ;fQY›8܉7,ÞfL€ ø€¿¸þG–е¦úXe­M¨Aq޾©zr 1ÝLÐŽû##"À†âÒŽð$§ ¦J(,³Aþ¡MhâˆÄ|Ñ‘t¯ðWSôÚ¶ÿ’¦¯¨ªÅIvlZœo ïXÑÌ ºš˜ 'Þ!†QQ‘¡­WâK‘fˆ·Òéú݉ý®(8W Ô‚ÅV ¥ò¸ÿµnóîzðŠ •¡Gl(ÄDG5»ßé>¤ûÑn­%Eëy¯¶ù×cÿŸ’ÃN·Þ„~Ý÷È*¤¹.ßà²Åhíž !¡s3¾ÖGVlo*ª2®á#´`Ã:¥§‡—9­Kp:C® ÞK·ôÉÊ¥}œ˜`L øC€SÓH©hâ—έ~Ùsþåc˲¶çÇ o œÜÖPŠ£Vߺ¨v‰4 Ì&ŠæSk;œàt:5qNVRt-¿¦½;ßs_opÚcîu­/â‚Q 4am6»|¾#",š”ÄwlLŒ¶Nî'eÄHÖoSsûÝj³âC™B°ß£´›¶S¿S½?ÝT^ú¡ ]%ôö´õ²¾ßqà2>Pµ¦ßñ>Tq2–² «¾ßõÖïÓú§ 5†D~Îäî¿SUùnÙ©\‰·+>µxܳ‚P‹Ïÿ“̦׆u¸Î5¢ÄGW?}1zhA§¸¡½[¨N™’Ÿ¿w!rþ ÃZÃ$NCñý™w^ÞfL€ 0ÿð‡×­iô‡í–@ƒ¦ì‡òö-EiêÞƒÅпW·­Ö…v(ºª®?ÐdõÓ¬»è>A=jÑ ®ù£“—…\AAFÉŸBÍîDf%`”(‚ñ’D|0AñMlHhk>ßèvB–oÍÅmý6ÎàKoVÔï{/eõ½¯ûî¿mû …#‡|‰m¢{“îQºWõûW[—öþ¡_tyE埲¯L¸K@¥¹îT½\!_…7C”w‡.=ˆžþþMZpYÁï PEw¢Ð‚¯KBÜcã*©vï ߊïOñ·p¬»Ê‚ð@JöÆwÝۼ˜`íFÀœGÖ4]|“ó¤míÒEK{ÞóÀŸ,ÿ5úÑÛ&‰!f|î‘HŒ‘ ¤¤[ûhð …S E?f+ p+ Ò«³‚Ë8«D{XC-š»SlŒG Ú~UçBíz+¨+z BrA!?púë ñ$®FNÒïÄP"ŸŸºÔ)6B_mó¥¯úÂâý‡ÞrÙêE_}§{Sá­¶€ošÔm0¾Ÿº»¼¼â·‘ž`È‹"ü[AÂA•iw.23[}=Ïò›³ž=7)¼¤¼ø UvÎÀ'Ϡ͢dºcüCŽ,ÿXà›Sßææ¡7 8Ëåû˜ÿ÷9<Ñ7gÓËîm^aL€ 0v%P¯ Ú¶ôÇ•¬jôÇÝŠŸÚêꊊMëV¿#þæ²?\¼Fý˵£\&]®ß_‚zë¤KOK8 =\YFi]·‚“åÛŽòÈœi’\¥ÿPyë"Ëmó]‰„nqõm¼Fl´zv’œØœbiÓƒ ‰n³æìŠxbtË·'®@èwª¯U¡2ý‘|Ñïdù&ñ½s¿p(oÏ×}¿l ÖîIº7é¥{µE°›¯IˆÇ (Sßç¯ú´Õu¨Fìwj¯Zï¥à¯¾'”ôþ¥å™úâ|S¨AŠvB.§³bˆ_ÞÌZù=Y¾ËñCî'tOÒ½I:­ûÉæ["ìe0µ|Ǽ´[)ùÞ¤5x愬×[&Õ47uIáa<Þ®)ØB 6}¾ïÇß½Ùú1ä¿0%*ö6\6ëAJ?—L€ 0&àþàô‡€^m“…L‡4èÉ¥&ð~îêU¿ìÊÉÙ~Ñø+¯@î1(bèG”%T‰ÂÌf=/žVŸ¼Å¶÷v}ζYȈî4gÁOîu¬ Ÿ—ñÞnp0È6¼ûÙ{ÛÍÍ#Ý—ñgß{÷³÷¶^)«Ã¡ÒŒ³4Éòð¾ª8”·wùÚo/Â1'1‰ï2üTâ‡ÜONkýÞxu÷TYvL³—ª7£ƒJŠo<Å# Â:üJ¾f ëú…?¦ˆ÷¸r£«ÁZ°±†æe ÿ3Z"^Òá÷á»””þS…ùóëŸõƒ¼dL€ 0Cð§§{ZÁuõèæ55•¶|B>á‹Î>¢_¯þ‡X""»„XÂbM’)Ìļ*1ø<è¯ïÚ¼mï^}½#,;×V»Û~Òѡڮõo¯ Ýíß¼Ãx}/ËÎZ»ÕZ†#žÚ·góöœõ»±Þ$´u—²|“ønÒúMÖnù$ܨ0Ýépœy$Y”dIUþ+Òë‹‹r]Û?rH£¡4¨Âë&!îñ@ -Ø|ÝF~Ú°0†ûÛø#ªýžâÐ H× ø¦‡)NL€ 0&`Pþà„D8 ô¢Dâ[ߦ×ßäƒJNa;7fUàg ®“¥œ,æº`ÇU㤧ç ëµùß«/dêëaùbr/wÛ_+8Ø¡ÚNý;àù[Ýí7`ßë÷½u"K(ÝsZ"\’à&^S÷¡{Ž»]Or¯ì>LQÑÚ}­ÝÑÞC2Ññign·æqCKþóïÝŽy ‘(´àÑŠâ`hÁ‡°ÎôÛ¡§Í¢¸¡õFx.Ñíd"vØ<ô½«Œ äÄšB'vY¿žú•`L€ ˜@{p]Ð|Ïuô‡ƒ,ÝÚM\Rý¼¸!…8Ö“Ò1×¢cüÛ{z¤Õvvë«Fi?ÝS”ô{KàdÕrÉ Nbœ¶i¿æ÷=)É"*òTuÓdÕQï_ƒ\I°¢…uúœ¿Eƒ*§Í¹J?b„e0‡ôæ[>ì24ç½lÖŽ Âö0)dŠoz«Á‰ 0&À N ‚òc]I0ð $ÈIÐЦ ‹oà$ºõ®6¶fmP±8ŒFã‘:TÛ=Ú­¯©ýt_éºÏˆëšµàKŒ!?×ÈÖn­ÞŸO‘–,¸Ï©ØžÂípÚ§%¢xׄ‡È:LãKƒ+åL*;•°ã:k-„‚Iü]ÊúìÝÁÕRn `L c0’÷&® rïýFÞ&K~‡IRCÞ¡ÚÞH'®ýÙW% d˜F¾Ýhþ>ÅÚÊ{7ZÂߎ1}`dk·Îº£„ÔÛ«/óÒÓ‡(NùG|xêBûð¶;‰sné³>w‹ž‡—L€ 0&XŒ,À‹$×– €À¡)=,%5ÎTA®:Õ =-ÝT=²vã€Ê…¸6wÄ’âU¨ò«pºÐ‚ šþ2ñG6@ûÇ?cCÎ"ÃÃ;dß v¥Ó±ÿÊ$p™œ•³é,ŠãS˜`LÀ X€¤#¸L 56OL8CšL+©µß‚¢;¶‰«à=è¢ñ6­ø`Äâ¢ã­¹–?ÏíH¡½¹\vŽÓ¡‰ï8:†þíå ¿KÞS7á‘÷¼Í˜`B€x ôד x kwq­s ¨Êt;¨i‡=LÞ(Øì¸¹P@ßîŒEÅ+½N7ôæÊ¢ºÖÊÕ/¨²üGÏŠ¢ûZƒƒ/´ gi=?#c ìp¬PUHÐŽ B… ¨cS6lBk?'&À˜t,À½¹þŽùvcÜ ¿ µûV´tk1H›_|LC‚;)©4´ w1"Åf·“«Pw:†½X-€4!%;w­w^ÞfL€ 0À&À<°ûk„¶LLH± p[vö›DÁÝg«ôj¥`EuöNƒôÎðÅÅ+ÑÚ­7¨£†ÔÛ¯/÷_šìt8VbW÷ }ا5—}bŸ¬ÜÕz^2&À˜@ð`<}É- `›oIˆ°— ×  ÞfWÕQèÿ{ŠCÐå 0{߬†üoè’ƒ¥ZsÌÚ­w‘Z°¼øŸªìü;ŠNÏ)U7{hA¾Ì¿hxoŪ®Â­^´û¸VõÊä¬M?éyxɘ`ÁE€xpõ'·&Àl¼*é"Y–o³—ª7âD+Q§ØºA8¦Šð± Â{_—l °æ5ZݦB ¢òœžqéó—]¶ÊÙè‰A¸óðˆ=­6ûJì÷ÞÔ<|ž²‚(LFñ½2›ËMbL€ 0:,Àù«ÀüL wrÏ$Eµßª*êmN§s@#—w¢t9ZÃßS“z,ñV†øütÆÐ‚3ly«¿¡ÍlAáÈ‘=j6r;éC§øÆ×W¥dmü¡™Ep6&À˜P,À´ã¸ÚE`û”Á!5µ'&“‹‰¬ØÆy¹]hA“äbf ûhðü‚bW VC›¨mG-èåàÅÓ¬µ(¾Aí[wÌ&ŠÒ5èóýw^ÞfL€ 0à#À<øú”[d ¯îž*;·ÕÔÿº˜tAWƒ -žåèîý™(Âûé‹K~mp06NZÂÂïp_•+rK´µ¹M(HOOtXäbÒ¯î;>x]‡â{ysËà|L€ 0&ØX€vÿqí H`ç5Ý»T;¿G“?cd‹Tï*¢èFÍ-¬DõýøÐ=çÖzç ôm -øÍ÷;ÛllK¸»=Dñ® É˪ܻ;Ê ‰o´Iv\®G‚àEñú”¬Ü¥…·“ 0&ÀX€ó·€ ø€€:eŠ´Ñºz¬‚~ÝÕçUªª†x‹.&ª üW2Áÿ*.ð>,ÛzhA|ÌHs·IÛÿš$ÄÍ÷PIµ{Z!Ÿo«ƒÜNê,ß(¾1üË hù^Ü0pS™`L °ç¯hÄÚB¸ôÈ·SûÓA ˜¤åég‚!åÐâý%°{/Pg¨l.ž3…œðˆ# ¤¹ÅU> 5h³YI|÷цâßLEñýUP5”Ø`Í"À¼Y˜8¨'°kòÀ¨Jµì†=ï‡åÛµ¨¹]â».Z{×£ð~/:&êÓþï«Ðv£ Ö¤…,?2Wèáj/Ô¢«M‡ -èÝÇÓÓû:lò |@Óâ|ãq»(SÐíd‘w^ÞfL€ 0ŽA€xÇègne+ дð¹9oŽÆÉRn­’Ë®G7î]|{]ŒÓç|$š¥÷ÓÙéÚÜ_‹PmúæYñU–ÿèÁBêý2}B -èÉ€ÖóÎ?€Ýi_ß×ôòjPÄkS²7¢<'&À˜è¨X€wÔžçv7‹Àæ‰ ç9õ–ì¬7~'hÓ„ÓJ=É‚ 6v½Î?¾vrZÆ9˄̎3‰LŠy}ü °úáCÉÅ:Þ'Q`>4a†ü>€MßÝ!—¤#;l(¾¡ÀH'58ÉÎUý8Îw‡ü>p£™`žX€{Òàu&€´‰rdÛMh;§…æ¡·Ý|ÐÅdëGý¦Y“0*ͱ°gÆÐŰ$¸­Ýzãݡեçèûh‰L>ÅЂ÷M쀡=9Ðz^zúÙîüÕâiªE®ì“µqmsbL€ 0ŽM€xÇîn}œ(''ʹVPÕ›Åv9 'Ñ[x£“fÅùTPÅÓ—Éðüõõ¦ð@’C 6¯“ 22†;Uç÷˜»‹v†•ýf|ŸìMk›WçbL€ 0`'À<Ø{˜Û×$ ˜k[ó;UQn©­=~5N”®)jOYQL°€¯Ñ†ùQzÆ ï:’‹‰'¸ÆB ¢¨T;†N)ÜQC z2¢õ¼©#dÙñ-®v¢m|h+A×';g=msbL€ 0&@X€ó÷ ÃÈ™˜˜®ˆÊÍÙµ?ß„¢;Áú1+8SÎJŒT1/BˆY0hÑîJ-Ï’ºÙá½OâíÓ…\Ws›ó˜3¹júÌ':d\oïn/8ø…²¬.Ãç·:¦ùëâØ>¹99Þyy› 0&À:6à»ÿ;Lës¯Mì­Ú”? 8ºYùPNm:ùu£Åò# LÿK]RxØ•£ã‰nLS¡Ež qé Ç~LþAÏÛÑ—û3RG; M¨©±„ã‚$Œé»!wsGgÃígL€ 0S °?• ï ¯NŽ•åÚëqJø[›üß§âF+eúzÿOà£ôE%[‚¤é­jÆÊ¢ºÖ*Õÿ‡¡oõ,Yi¡Ç?lËXÓf]îy¸Ã®çe¤NPTu#È©Ä$ª—÷Þ°É$¾Ã’á†3&À˜@SX€7E†÷$ìiéfõHáL);j'aˆ¼Pjˆ§[7ÊðJI_ŠŠ0oxÆ]+„ÌÌFìáÙüVWú›g¥?Ô(UÿA`]õ‡Ôax-óӆ݀þJó—ÙuH8Œ>ßczgåìòÊÊ›L€ 0&ÀÜX€»QðJ Ƚ2þBêE…7 _w—àn »(¼¿GóäGH_g,.ªÑÚ»43›í³º×‡TÆz>­phÁ¦çe ÿ3à}¿o¢–K€|“2&yÆýMŸÅG˜`L€ ð Lþ0œÉ=ú)ŠófAUn–UèëŽ D7¶NÈF î<³>¶°„Ârò pºÐ‚Hw^ñ°s9@•ǼJö¥¿å?øm«skv„„ ¿ëµvCbL€ 0&p&l?!>n(Ù“’º¢à¾]KnVdûT¹S$7¨Š>VEiZºÙ ‰\þœ9mYÁhÁ…4wdA^•„¸Yãf”pt7˜ú•}éÃfªÌvïÈ ÇPƒIksŽ×çâ5&À˜`M`Þ4>b(ºÃ12à•8ÐífPœW Ãv¿m}ÑÊ]†ÆÈù8IÎGiKŠÖ`4·>ªÏÅkD€B –TgʪóAß’›Š›@5Ý1áG6@ǘÕÓÝöf®äg¤>§(ê =;~ïÖÄšÂ&vY¿¾BßÇK&À˜`g"ÀüL„øx»À™)Cl5ÇIlOEž¬€AñTÕ(²í¸ëQçE%G,éÿê>›VYTEœ'p¦Ð‚—]¶ÊÙø™{¯ªªŠï7P|ß©“Àïßw®AñíO à%`L€ 03`~@|ØÔÌѦœœ=¿UUyjmí±kPlÇ6qõ_Pcd±˜?<¿ðdyx·æ†ô8…W먣G›òÓ‡¿ßÇ›u((¾¦¤ôŸ*ÌŸOœ˜`L€ ´ˆ ðáâ̾& ffŠsç^¢ èÎÉÚy=úvÇÑ5<-Ý´Ñ8vàÞOC@øxèÒ’|ÚÇ©y8´`ó85–K?>4ïè‘Oñ»wµÇñP|߆â[öØÇ«L€ 0&ÀšM€x³QqF_Ⱥ²Ûù¢ªNÍÉ~ãU…Ênt$ÉSEá3A„O3¾.ÙêËëw„²š -ˆ¡?°ðû'ÞWu¬#p8Û6ž92Å÷Wfð2½ ´|¿™’½ñn\6ü²êxɘ`L X€7gñ œÉñCUE˜ŠÊe*F‘è£Í~sªŒ)ñsI€O‡/.ÎòÍ•;V)ZpÙþs*¶'±åáîÖ p€C ºiœveßEÅ—Úª—㫘ázF| ó\ßœMÐT—œ˜`L€ ´† ðÖÐãsÏH`óä¤Nʼn¢[˜ªÈê SKнDŽ¢•ñ œ çSŽ`rF¤§Í ‡Äç-xZRM,8ÿü>NkÍw˜£Ÿ;—(<Ò7{Ósîm^aL€ 0&Ð ,À[OmœÀÆ«»%ËÀXÝÊT»ìLuåjhêFkb©*ÀBÕOÓC/]áö§eëbãPϰ—C žP3祧‘eû·ø ˜H§àáŒßÑ;úfmz¿™Ep6&À˜`g$ÀüŒˆ8Csd_Û;ìÖ0ïT§CÑ&Èñ>µ5N©(,ÂøŸ†Yº~;xþöºó½³òv |óœilIù‘9è.ÑÇ}šµ"O†¸ô-è¦rÚß—€*/ÆÀZôü¾ZQ€ß˜’µiÑiOäƒL€ 0&ÀZH `ø]O<“.+ò8looUP“PÔEµ°í>É^\Rà.gÚ¬äUî ?¬ M¹g-,B÷ƒ’dZþæìÇrüpY÷%QG˜–Z»½SŠîƒîÁ ‚º'Èù,ÁbZÒs~!f¦Ôº‰^´þ—W ‰²W{õYåWSðßѳf¯roøiE§-AÊOÞm=8 Þ´b%×,è(™>þa[€ß«å§Öûö2ù#Ò&©ŠüŠo •ŒÂ»¿³“RrrWûöJ\`L€ 0€€à™™ï‡v¾gE|ýŠQt«aŽ%ÂY-„ÈöFÄŸ?ºØsRÆÃ£üqEýv)D©6E¨Vs¸ˆ<žš6óÉ#²ÿ‰¶F¾úÒKZ1_C¿ýÄV,³§¤Ä@MÍÕä^R®¢ˆÆNÐ’‡øÆ]<ð ©ŸF@ì׃í®lÅ%õS…x1¬"¬ê^I`ˆý/€"T)±B0Íïýß ôº>zøµïéÂNQKå$¡Xî;lc L¬T:I‡ç‹0æO™™·Qÿsj}é©Tå|Õ~ñ{U,˜Äq)ës·4ãtΘ`L ÅF€O›5{ìÇ¡÷PØuïYyHí{r7ôªÜ/„:mõSi·¸ù‚&:m¦P8Õò:ìv0ª×³Õ–Êûÿ4cÖ<ÿú²i•ß|KB„|&)Ü5ÕãÑJª‘ÓÅ7m€1‘…U(^>µ„™¿¬Ÿ 稖µÿÐUDlϸêÐÊ·EULŠ3ïS{š6C‚i·"X;rÿk=`WàÄ99‡ Gƒn…C—àýòç·žz‚r: üôÔ‡p–Ëçðq}›!Ï$šÇö^ŸÅ±æOÃ1&À˜@ë„G1ñÎý|'ëq¸øÐOX]ä)ýZG ÎÆ‡è_º‹>‘ˆ$XÓctB©¥Ëâ??úGßû÷“ÿÁ&Ò„!ZÔ¿æ6—D·³L˜¨ªÊG)L@ÑÞ˜Œ7ÙÍ`®ÅCË»YTÒ:¿’S+G¶ão’dþw”X¢ ]]M¸ÿ=XáCô4o¦pÜܶX'u«P¾ÁûfŠð—<²òjšZ~ÿˆáÏâÔò3t(èv²I0…^Ñ{ýz_õKð’ 0&À˜€FÀï¯í[È]p‰ox±oÙ^ñê=_H(¾[XDÇÊN|®Ù;_ê[¶O@ÑúÜŸžõ  ?²ŸV¸’èι2áÆ¬+ã›pé@IDAT¿°—Â1|-ÿúu_b¥>–´Vˆ#ˆâŒ˜£ñu¢+„Õ„ÃEG})Z¨žT_3ÕŸÚÑü]¸4ü- Å7îæÔâsiÄ\©‡yÎs/ÒýƒyOÛïM•¬ûÕ)SBò3†ä)¾Ñé{U¬)tT ‹ï`ívn`LÀPŒ,À…ÛÍG–o“êåË“‚®ÅœÎH€8/âf6‡þë¦{þ>O"NýÝ@ŒmŸ282wbÂÔ¬‰ñ 4Ñ­ªŸ¢¥û:ô‡µ4¸ lC á¢Ò?ciIFÆââD¹M¼?¨~TO3Õ›êâ[Ͱ|&˜îÿ}ÒÄq"^Äîº0kƒ~oâÔ ß‘Nbòó÷,C0Ћßë…}ã¯è²~}…¾—L€ 0&ÀÚ’€a]P¦MË´€(¼ÛÉz FüÅÃY| ˆÛÉÐNªûæyçLݶm}9ãØ5y`x¥Zv%N¦=¥¦æ8Šst"¦„&SÏ„±ºqúwu>ˆ¦ù‹‹vykÃuM|c}c¢b¢ÞŒ’JÔÔ°/ü Ø†(ZW4r*ä8 •Óõ]¼Ÿú¿õVfMëJ ì³ GŽìasZ¿Á¯ù½%(¾ßLIé¯;½~€—L€ 0&ÀÚ€Q¸(wî1‰|¾Ùò}vßâvIá*qqÿëºeŒºèïOuÍËë.Mªp–5´¡Üv] E~˜o¥ùÃí>»+ŸõY$´é;:ô·£îVAêF>ßlù>;žÄmhØbiMÍíIJgå^,åyü´h,ÀÙ]ÙxgÑ;Vßøv§ÕŸèqrVññ¾9ÿ 9›ŒWa®`L€ 5# p²vK’ëYyPå—­ûþ‘Ox/äXÞù¡02Æ)”%œô‹Õ_+`ä’ͪ Ì7›LóSÞÓº+žõÙZ¿ãÙ!ø±„G„ßÑNxÀåYóÔN$Ÿpâx Rþ†;^Ä=w5öì¥åÆö¥¥ýÇ!‰­Ž©kN%üÅ÷ÇÁØ^n`L€ Ÿ€_í‹7ß÷È +Ý C êÑø$ \Câh ‰J,ñ`WTñƒÿo™gš„KަŽX\òt;Šo¢ç¶~¹æÆ‘‚(Åa¨Aî|¯ˆ#ÝOt_ÕqöA©QD^Z*úzËäó­‰o|ÃnXÒø¾¹›X|Fr-™`AIÀhpÍ j ‡+*ÅùJê~nrÔ^¹Äô=*”–¼s¬Ú9ÿïËvb5hT£¬¡Z¿c]ÈúÖ9>árêŠóÛœZI9jýO÷•ŸVdžoe•ürz^úðGAUþ…s}(Dñ=¡_nŽmàĘ`L ýR€#Ždšá²ƒO²ã³oÅ GžêΘþ¿þsÍâÿbÁ¥ø!/'~Œ$À)R‹E4™z…Õ8Ë¥•8im¢8áÈG˜“±,#õ{k›ÖèùfPÊÏßû*ư¿KÏ€nV[CMaz¬_"œ`L€ 0ö%`4ª„ÑOÃUF«[ûöT+¯Ž<ÉlŠÇb(â YšIˆ…±«ßë,àTÏ0¡œÅ7v¯òé¾ÂòŒÔï¾jž»œâ±c#òóö,D—Oñ½Çöþ†Å·¯0&À˜@;0ŠÓ1P}L蛪8X€éT|°$ž’ FbQ4< pzûa”þ×ú½®^¡TO³`çþG ¾JÄ“î+,ÏHýî«æiåºpX÷ª%«ñ•Î$½`ôùþ8%eÀø¾99‚“`L€ 0C ?ÆFI$¸è#á´ç,¾Ú W0þ1qÕÅ7YBuæí醢×êCßǺzâ*'_¨»¯ŒÒï¾l`¤“t›MY„UIzÁèvòï”ì£oÏï·^^2&À˜p0’§J‘%TB¯dàî.òåŠö`C}N"Œ>F²€ëuÂúñ˜/{Ý]–ë¾2R¿»«Öš•}iîeº„kå‚E÷}³7¾‡SÌ·¦h>— 0&À˜@›0Š£Æé–Pÿò_Í6èî:˲.tݼÛàR-)Ò³ZÝØÞ|ÍÏ[w_yònþÉ͹/-u6è ]|ãz© Ôqšø6h¹ZL€ 0&ÀŒf×Å÷LÛ ‡.3-<ëc¤‡B#°ñutÖ¾.ׯå©ééæ|p¾‰Âûözÿa˜B&&oØÐ^“Iù•_Œ 0&À—€Ñ8‘4Š(ˆ–ˆfåm*SòÝÿ§L)2©íže¤ðHèù—G!uÞHýp5 xòˆ0´©jf ¸ö´¼ž£þ¸\ôèiÚ^H”B .ù·õ;Îb-qàU>é±tÅO§³(¡y§Ð?gÔ“0fúül‡Q\}ÓïnÞÉA˜ëÐ…iýœµ•¿‚ªºÅ7ú{зoÿ±,¾ƒ°Ã¹IL€ 0 %`$î7Ëœ` ¾3^€Îûì’`2Cô°‘Püå»PøÁ P“·ãì jÆY¢%Bâ’ ï™ûaûý×€*;!iêÍ8³YYüƽ‰Úøýú]{‚?ÞDuš·;.ùr8Q¸vü4 n×¼“Î"—ªÊ`«<¹Ko‡ï¤Â¡mÿƒþÎKT³(­Á)~çÞàêg±±ÄðQ6»ò+Ž@§cTß÷ÍÙt›0¾ý,ŠäS˜`L€ ´ ¶3Ûž]sHhWÏîôú³zN{G‹ ¬w?ˆI½ªöl…â/Þ†ÿÏÞyÀGQmüÞÙÝtBï½H‘ÞT,—s£±Ï¼œÅƒ’= mjÒáѦçíbÚ\Ѿÿ(¡{2!’ß³„rýæD÷!“`µn þqçïbÿßóĺyãDtýN¢ÇyÏŠØÆ=Dö‘4ì‹Gö,Ç·Q³¬Î+æ ]ÎŒC¾öbÇÚOD“vC-TÇÊïFŠÌëE¡/ˆF­ÎÆìç†Ø÷÷\±aQ\F²„æŒ]Ï/šu¸†[SìÚ<[lX0ÁÚßí·hÖña±uÕÛ"mù´ãoY>Õ·ïÀ¶ù–¥ßŽîåöF±Þ,ج}uV‡ L+?Ê0Õ«H³¥"¸‰ÌÅŸÛ0Øò‹êÐ~n#`L€ 0¡d÷oW¥·]Ñ-þu¿å/½uZ¢kÔÔ×d=>¼ð«þ½&þ~ý)kÛÕ ±h —’ƒs¿[žDD¶éd‰m:Hu5¿q¤¨ÛûL±">gÛf±ÿ»O¬rûçüWlg²µp¯ :7>=\háá¢ù?‡[ûÉZÞiÜ«ÂU§®Ø6s’8ðóW"oGšu¬Í¨aäd‰MF‹Üí©¢ \MN”bºÃï¸PØŸ(om<îpF‰Fmþ!Zt½N¬_ðŒHß·Ztü4ÆçňœÌâðî¥"/k·XýãÃbÛêw,D=Î^x!ԗͺEdÜ N’äCGÖæþ—¿‹ãébÃÂ$K\“ß7 tª#ûðaù"sï±b|õ܇EóÎWBX_jÕÑyТE—«ÅÖÕïŠõ‹Å‘]K­ýmzÜ!ò?$6/yѲÊGÕmë;¯ÿF½f}Eëÿ²voüÚüþÇkê6 ¶D¤“éxpyó¨ø{@ó,¾kj¯óu1&Àj>P³€”¸‘!¶M/Rᛋ7¶Ü7rþÞ`''õ/‘·³@×íw¶È?°GþãgëXúŠ…¢Þç‹=³Þµ>;à²öþ«„™›c}ÎòäYëÜ©>+7Y´)‘˜Ï\›,Z@´ M1]z[.$›“î+[yè‚<攞"õ…G…ž~Hœ÷ «|X“–"ßN_>ÿg_"ê ,6Žÿ·ÿnÞ.†À¦Å“Åí ,¡Ü²Ûõ¢A«Abß–ŸD.ÄwLƒÎâð®%V)WD=Q¿Y?±|Î0‘Ÿ{Pì\ÿ™xõ'"2¶µÈÍ,èÛdé¶“7?]xóûê ýi+gZó¨Ø6ȉF톈}*Zv¹ƒ4ÿO¤&¿n·ÖàÆ²VzðËfK´û[¼ídµ¯ÓøT‘›±MlZú’½»F¯7Ÿyf“-žl²pŸm_¨29Ü~ O+oá5`L€ TG5Z€çlYïëoÆa!Ã",äÛé·Q§×ÂU¿‘èúÜG÷ÂÙÔN™ëWúÄ·½ïØ5YÜë ¯M(]‡øv`ÛaYß)oö1~â±=X½Ý½n_UôÐ ·™âxL×>¢íè±uÊÓ"{ÓZ_Þ(žÀX¾)åç²ÖNgñƒn¶:Óú^ôzTØê°v×iÔÕ'ÀlýÕª£¤?áÑM`ž(ê7(ò²÷ˆˆ˜æèGð. ÒÌØWàŠd——šS4ly„ÿÑ¢³oætÕ £¥ÈzÑ/ô Ðü”+Ä€+ß·,õ·/,’§&}ø»ÿ~º'{~‚­íë‚øþP‹­7¬Õ¼yO¿ö^3&À˜¨fj´/O¸>#7 ¾Ö{ÄÚ{¯*¶ •ÇSì~{gTÇSE³«ï´\I2W/M.¹I´ºû1 SBÏL·²E4o+²3úw…Ötr?!k|i)²Ý)¢ã“¯ˆ¾&-ü®´¬|¬€©ç–‰…žŸmå[ ÷“c…2ù€S2ôÒ5_»>ÿ±MzˆùïŸeù}Ÿ~ÝçV9¾Ý&ÊFÕmo}¶ÿ(SÇþ|±uíûbã¢IöîR×$èif—³âEÃÖ爚*ÀSû÷¹ÅÆf0’€ÀßÛ€ãú“RV¾P* >Ș`L š¨±>à¥ñÏÃ`Gaš¢Î©ý¬‚ä&’¾l>\?Zapå-‚bm;ë5dq.k"sJ^ø“ IÃs¯,(Š©°³6­±|ÑI ‡·h+baml×YdmX-tXæ›]{UF ±}a=&…7k-N‰›b¹µdo^g… ¤0„ä©ürmä&BVj²*Ù“bYÉ;xÀr;¡A“Û)WÅuZOö>|­tøŸF|ð¾ò¶-ÍN¹T4‚h®Ó°«hÒáBëØÞ-?Àú} ÂR ¾…<¢)í2ßUØ|*&À˜:Z)À X¤÷ýð,ÔŠÞoý$ˆNþÛ»™¤Å £DŸŠ^Ó¿±—¹Ò—ÿŽÈ')âÔ—>Ýžý@˜û¥¯,ïï7 œ{Šî¯~):b@fxÓVpiÉ[^x\D¶î(zL™8ß‹Dë»`5‡h÷Oõá‹NõN?WtIzÇ·Äö8*òüóóvéö¦ý$2®CîZ"z_øše±^1g„ˆiØO‰ Gm]1hQzE~G·­~ߊ¬ráÈõâ”3i+à^˜þúí) úÜ#\ý‘8ë–ESD=¡´~á3–Å}àÕ‹‹îÝ"ÈjQ§ya©‚ÅoÛë.ñÛŠsï\bÅ/ßøû$A1kRJÒ§^ê쯾UB=~ôºä_aáÚÀöË—Œš>z€·˜`L€ TkeW%\æðø óèЌĄ!´®Dr ,Y½bïz,þë6ù\¾ù¨ˆ­D½%ÕÂétJ˜…*íŒÎº „‘?îò‡¦˜Ýö@M»>ÿµ3¶ž0rs…òui¡Év(QD”`¤ÿët­H±}øêó¢þ=XbÉÀB~–r§Íýzûœä;-_UÑï’¯ßÑ€†XšÝúà㯶®{°ÛÙÑo—»Må)@Öo¯ÐN4Û$¥ŠÎ8I‘V(ô`qÉኲ,Ý:Ó/IÍ%\áu`…?Œ½>¤~9° W,ó†·`pу'þ´0ûn±'·yò;“ÉǪRýnŸ-P¿ý­§÷=U×ͯìøÞõËÙu#£om¼hQ¦}>^3&À˜¨)j´ø‰:Éôï#LI*šJßT§žq¤Øªƒ%¼‹=ï´Oõ±©¢ÂÛ®§$ñMÇKÏÊôúŠÚõ·ÆCBIåË[v¤è{³î5gâ±Ã7BV“Zbûäåãáû]ÂÓH5º@n*`L€ 0bÔj^ ÞŘ@ øÞ©BQ™æýöéð %„îì¼âócݰì<¼fL€ 0&P„š'‹¶|ñËUÈØòu c(¶)ˆ]p²ª¶~W6ë“Õ±}Pï–©ã34`Ý {ØàÔÄum—­øÓÞÇk&À˜`5•@( Âô‰AC9Íåû\SáWåuåkaM^l\¾“Íù¸ó놞ëUaÇí¯J^5í\^c³™íw]'…ïæ~ýÎóxä =q¤ßý®ª²ß1¥ühÓT‹…R}¥x±Cl½óÛ/[F±è91&À˜¨•BI€SuÖa÷îûd[6rwt‹ZÙ1ºhâG·oI£ÙõÂÅáGg  Ô +V¯ßí6R{÷y;ÉzÛŠÕÈ¥,Ä8Òï ;ª¤ß·ž}v}ˆï/án2“ëøI™!Žë:¥¬zTΛGßCNL€ 0&Àj-Pà¶%”D‚þÛ×_~bêù¶bê˜)Sù 7âgä{Ž,š3ûGÔ@S{ÒBˆ8Û̱yÒ’Ý«ßÑ «Ô^XÁ¬Î»ÂÔ÷Ez‡¸?|Òï u½ßÿØçL=7k%Ä÷5¾6K±B:Å€ŽË–wj[ß yƒ 0&À˜@h%N¤ÈJ"!ÿС}™W¯zþpdC1¿ÍPiœÊI€¸¿‹¼Ÿ““Ió£{ [„‡’Üêw»ÔÞ¿/x?Ãl"Væ]Ëý_ξ§ìÄøm\µêyú=aWÐú]¹ÝÚ–~}Æé¦˜ñÝÆ×\)^ïØ¤Å ŽKVnòíã &À˜`µœ@( p²„’PÈ[ðÝì…;Ó6¿Z¯“ü¹Ý%Š-áeû¶'âEÜR×­žµbá|Ä]¹…KÖÄ—8‡’÷õ{a;s©ÝÔþÞî29÷FÅ–p)C"NÄ‹¸Ñï‡~G(´~O8°Ù–Ù_ýh •o+´)&Ö9¤iÚUp9y@~÷=øqbL€ 0&À „Zp²t’ób!Á˜=çã÷>¹ø¦Ûœ¢cçÛGÔSgmŸ¯5ϦhjœŠ#@>ßävB–ïÔ?WýˬϾG>š€…,à´&®Ä—8‡Šeù¸~GÛÈw8ü—¯Ð~%4ѽוf#Õ+üÿ´Fέ8Ä©8äóMn'dùÞžºñ½ïÿû¹ž­ß»íÞY..«ÐMìö@|/wFÜÒjÉ’ö>^3&À˜`G „ª·,àh&‰Æpˆˆÿ <÷‚Ý=œ>ê›S®«×&s›êxhƒl“™&Âu6®Qœo 5HÑNhÀ¥‘Ÿ—™òËŸ¬Z¼€ÂCr? 5-þ–ÐPàEúm Ã⤇ˆƒ{÷ìéÎà›÷ÔmâÚ¬Z;WɦΠ"LÒåÔîDq¾)Ô E;¡—ðÏX³ì·©Ë~ýé7 J¿«ë¯›µ&¥C¯íi­ð%²&ÌÂâ;©}‡ÎOËÏ>£ý;·9¥KȨ˜Fa‘uÎèpøŸÖ*¦ºÇsè@znVÖ¡­ׯ߸fÅß`Cî%d&±M"œDØ‘Âmr?±ý¿j'ì Tb¿£m–8ÇC…ÖðoñùÇÎ=û¶kÛ¹k×Ș˜á‘‘uŽÈ𸆪k‚”J×½žüÜCé¹9ÛlÛ´aíú•)Ñ€ õ; ´L=ë¥ô 87fµœ)w´HI!Ñω 0&À˜8PàÔdk¨-ªýšbc–õÈkYȱ&±nYK±¶Ë`³Æ&[@+Zlßy²p“ж]ÈòMB<”­ßhž•NØïȉ%ë°lÀ6÷{ÁCWPû}ëÀ¶|óÕ{à}6+™‡±¤c×-·~òÑåáö÷Ñ>Ìk&À˜`L ¡*À©¹$ÆÈšK‰nîögdá%!FõH€Óu¥œÄXm߸L+Ù\H|“[‰mý¶°â3‰ðœÂ…¸ÑqâÊÉîgj£}}Ônî÷£½fs©’~GxÁa^#ÿ%œ>Æn÷ÒÏ8Wì©[/÷6ß6^3&À˜(Pà¶È ñå¿MBŒD¥%kk ÖÇ ðš,ĉ%›‹-ÄȽĪ$ÂéA…>Ó~ÛïÛ.‹]!™ìkâ~?¾{ì¾³µß)¼ izß2•ºÔ×)Ñ/rB‡&B|ÿìÛÏL€ 0&À˜@™ „²§‹ ¡A"Ã_pØÖP²|û‹oÛWœ„wM߸<+{!FþbŒ‘è¦5-t,Ô-ßh¢/q¿ûP·a÷¹Í((ý¾¥Ÿë Ó;ã*m\çŽÛÚ¥¤,)+Äðø^Gñ`L€ 0&Pf¡.Àí !ñH‚ƒÖdÉ%qi»ð¶Åwmõ÷c¶ £µ-¼éxuLÜïÇ÷šÝ—6»ŸÒïiCúÔ33ÅX½o±O§YœS¾¢ÅÖ×nÞ‘BnÕ4qgûä•óì}¼fL€ 0&À*G : pûJmAbƒ’-¾ >Õ÷ûZ‰…l.öçš¶¶¯û½@dÛýks±?—{½åôÓ›*oÞ+ÊT7ù†ø~§ž+ü¡†K–P$NL€ 0&À˜@€TG~ì¥WZ€[!®¸ßÐM©úÞ ñý`Ö·«Ãí>©iÃ:$¯˜mïã5`L€ 0&85A€Ž×Äj ÔÓûœ¢t1C™æÿKÖ¤|/ÌöH«¥Kúïçm&À˜`L pX€Ž%×ÄBž€êßß•¦ŒÇ”®à-N¡< ’”©R“#:,[ñ³½‹×L€ 0&À˜@p°W®• „´~ýÎØ"ô™p7éa Õ¤J©Ã×û۰ˆ§[/^L±ã91&À˜`A&À<È€¹z&p² ì?ë¬:é¹9MaŒVÊŠd5 Â{™tÈa–®Xu²ÛÈçgL€ 0&P›Øq³kÓ5óµ2ZCƒ,¯KÏËZ‡H…÷ÁòmÿÞ³0•üC®¼ú ßµæ«Àʘ`!D€-à!ÔÜ&(©ýû÷TÂxƒ,Ïõ¯Âû[Ì[5ºcJÊ6‘²Òÿo3&À˜`UD€xæÓ0ª °}Рùù¹0wÑL#O3ÄZIJ±WÓ¶_¶üö>^3&À˜`'‡ ð“ÃÏÊJ@]½#-mã¨üüœ§áçÝÀW¹5ÈRLÑêwûyËøöó`L€ 0&pÒ°?ièùÄL 0Rö=Ë–¯@x÷ð¯î&?:\ÚCíþXþ—ÿ~ÞfL€ 0&ÀN.à'—?Ÿ T˜Àß§ÖÞ0ò_T†yMÑJäfÍ¡é°lù7E÷ó'&À˜`L °…^à60r°Â z²ŸÔuÏ#(î+*E&âz'vlÊ+ò³Ïò}ûyƒ 0&À˜),ÀCª;ÊÞ˜Q “ú¦qJ´URµBÔ){éÀäüøà~_EÃ/ýç<߇*Ú@X½L©ä. 6Üæp8¿Ÿ:alJú¤œ&mÈ•™~oz^öX\sC»è{áýž¦¹Æ¶_¶lHæè&6^3&À˜E,ÀC±WJh“ÛýNÄNïÎû¤2Çè¦Þœ„W”ÇcÖñäÊ0ÝkÇx.¡tpw·>¸ÿÁ=Ãñµç;]ffx¤Ê ×À#qø¸gvJ¼›óúË/ÉC hôêŸÔ!ÎÔ¬Ã÷GpI-‹\‘M>ÐqÙÊeEöó&À˜`L d °Ù®)Ú°áñ.Ü“¿ýmX:[¶?¸Oºs›è¸—Œðz}¡æŠ–¨Ÿ¬‡Ž<—K¤6n!ÖµlÓlK£¦ÏåFd>xçcñÃÞœø(˜Xª¥Wn·¶å›Y7¥fyWбÈeH±Öÿñ’W|ŒÁ–ÕòújÅ7”/’ 0&À˜@1NªÕ´˜öð®b@|?¬”œÓ +£Ù-Ì×/[ »ïÚ* ¾‹É]ûvâA\ˆOý쬦®°ðoî~rüР‡Ìj÷=ß2 ï•[¾ùj¥Rê#¸›@|ÛIî’RÝQ8»v\¾ò#ß6^3&À˜¨>ª0©>hÒRY ¾ÅKÝvo×n[ü«£õ᩸¦VB|n_ü‹£ÛîÒáp=çãñ$Â]XèM¼vB;ýÝ¿÷¹›û÷^lšæ×+ØÓn-&Ò9„æ?Ù©cÊŠ©2%…Ÿ¾l8¼fL€ 0&PͰݓ÷<é¾–ïÉ“êÊ•H—¡‡nkC¨eĉx7—+|âÍ÷=r šG"œ¾ï!)ÂÓúõ»hsÿ>¿êJüw“3üpfiRL¨çŒhßiùÊÉ­/Îõ;Æ›L€ 0&À˜@5$À>à!ÚiÇ»#5)ßjyD\²&9$Ecˆ¢ó5‹¸í‰UZL©=zœÞgíÚ%é8H–c×é$nÐì•©i›¯—J=a(£¿§:,ÞyBÉ©QÑrRó+ކ›9‰íåS3&À˜`!ÀðÀp t-šÑ@ÞohZ‹ þ\á`ËwÅð· ÿ\®™G³^çýã^ÔB1³OºO8…LíßwTê–…i~?ï>¾+ÄÔñh;3Üq |¼Ç°øö‘á &À˜`5†[ÀC¯+ÉÚípHñPû{|šÙú]‰>"Ÿðà˜Z¿ÑHTó&ŠŠbGF©Òè!iCúÔSb´™qøAœ¸‰ÿe¡“³!¼ÿƒ®©cJÊ6ÿc¼Í˜`L€ Ô,,ÀC¯?µ[xb Ò´fjSå €£DxÂÆC¯¹ñô¹_ýoj$gzáU⊲}Pï–ž|ù‘©FàœE'L’ò ‚¾î {£ÕÒ¥qœ`L€ 0&Pà °­¶¬ß®ð°‹°¡(Îwh5¯z¶iä¥jФéù¸š-Óö·-á¿0¸•È´}‡*S„ø¾¡‹üÖ¤[•”/E í?-–§ä¼\!`L€ 0&²Šˆ‚meíi˜%Àq¹íh†ËZ>ÉNÀz℃§Êp:Û ÒH,4Kf>²„Ô eWÿþr”q|¼‡£êN¨«£§Ààʵˆãý|û˜ºŸÈyó8¬ˆÿ0&À˜¨]X€‡VÓ X¦o“ÍdØ71y9Òár’ßu–0,$‰1YÁ+Rû÷?-#s”þOTFƒ=‹&)ÅÓÕ‹˜¹rOžS bL€ 0&PÛ°­'AèÔ„¬nèì~À¾!žWX ª$qLœ¾û•zÈ)Tù/%ÕH¥ô¨¯HBÆÃÔ{R8§uHNÞ`„ œ`L€ 0&P» °þ'eF‹C!tè4«æ´ž ÄÕßö̘´ï¨È ..&Q9Ò$Ÿî›Œ Eü„WZŠ?ЕÓÂÂ"?å‰sN”3&À˜¨…X€‡V§“EÖAÇ<(ýb=ØÐwžÄ7-e²€cœ°´´Í+Ó¼9WéW ¢‹i^–ÚGÒ)¦vXºbU1Çy`L€ 0&À,,ÀCç‹@¢ÛZ(‚Fè4«æ´¤Ðn‹oo\áqpš¥2-mÓyˆbrÓ–ÔM×â­D½âH šÉ2Äï~;6"ê£Æ‹e—‡÷1&À˜`LÀŸ p'Û…'¿%5·dõ¶9yС&¹Â¸rü’ÔÔMA—7.ÀpŒ>—òOMŠOœÊñß6))©5_`L€ 0& ,ÀƒAµru…•«*¸¥­[×À¾ˆ#¢„çÇ_…ÊÉ î S»O|ŸÞ ã½m…)u^:\ ÷’þÚÅ»¥H™ Ùþ?ŒÝü¤SròÚÀ4…kaL€ 0&Àj#à5¤×#ï¾Y„Ÿ}º8r÷Ceº"WïîBß´¥Â¢YFEŠºSžÞ”ÕÂÜ·_¨àaç-Tv¶È~~ŠUÆ[X‚ÎiàÀ³¨À½Ú›¼R„áœÇ pß *¹q82*uøßÛF뺎‹±dT²J.Θ`L€ 0r`^ndÕ§ l;©ô !#"ìE×ðåvõìfYªÃŸá;¦µnîÛÖ×mð‰oßN¿ r-‰¾ï«óÐ!¡5l(TV+‰.0Ææ¿-ñíWD¸úö”«ûjâÑÝä#¤dhZ>ÄwðN¤vsµL€ 0&À˜@Í"À¼fõg‘«1Ë™D×á÷­‹¼oäRlÊÏ/v·½3üêK…Ö¡­8rÏC–ßwìóOÙ‡„™™)\à~LR9yÂRPo¿^ÂÙ½³¯¬wÅZXĈˆk/Z“F–K úÌÿ#YhÍš`@ç%–u^«_åºøÊñ`L€ 0&Àj"à5±WOpM}ÄkwÔÈÛEý1è22Rd#bŠ‘š&ê$Ž f¿/b'‡Xnx‚šŽö|û#kÆŠúŸ¿%"1`Óò/=ßü(¢î¼QÔ{÷5=ìV„Ñ„wÉr+2Käm׋ú_¾-ê}ð†pÞïh¥¼Å˜`L€ 0H 0růlxü„yTzFbÂZsª0JFb‰½ë±ø¯;de ¸yÉü WV–‚2"ÜÊVdøƒËh„ýËÈ<Îg»Lu"¼a‰3b:B«-ÌôãëÖêÅ 3;ÇŠ¦R–ó”7Ï'§ÿCl‹øëÃWŸeñ À7“¦ïä((åŠüüÛ¯4.˜`Lؼ Šo›ùƒcÀfES‰â›*D róHñu—´¿¢íàrL€ 0&À˜U삪=ÃíbL€ 0&À˜¨‘X€‡V·Rˆ<,’Öœ‚C ±5!&sc®• 0&À˜(… ðRàTñ!Ÿ4”‘ãq8}Ÿ«¸5òt‡K!8ù{›˜ó±Dø3`L€ 0&T,ÀƒŠ·b•›^c_VD Êá+¶TVD¤òz<‡‹=È;™`L€ 0&P…X€W!ì2žŠ„⮜ðp™çr•±g+q$ž¹9Ùl쥴b|Œ 0&À˜`A!À<(X+\©% ÷îØ¶2µq‹ WÄ ŽÄsû¦ +±×Äb pZsbL€ 0&À˜@•`^¥¸K=™OÎû¿/טº÷ຖmX –Ьl‰£‘ïI_³lñf” ˜ß´øx—­ÎŘ`L€ 0À`Žª…¬³–@<¸oß'[5•Ûë7 Tݵ²âgqLKûôÂÅáÄ›`L€ 0&Àª” ð*Å]êÉl‹,‰Cý·¯¿üDyóþؽŸéuð|I¥’+á q#~¦ÇsdÑœÙ?"[~áBBÜß ^B ¼› 0&À˜`'À<ðL+S#YdIæ:´/sÚUϨ+¾ë9€]Q*@•¸¿å,x?''3 Ux âÄ™-à€À‰ 0&À˜¨Z•àP†™Xbª¶Ù5öl¶ ļßÍ^¸3móÛ5o%g÷9C±%¼lýNœˆqK]·zÖŠ…óW $ŧ% ñ% 8 p@¨pR²ŽP2£Âå¹ `L€ 0ZJ Ò\*¹ K‹ZÊ/ЗM–n†^,$³ç|üÞ'ÛS7¾ 1©>t®É>á RJ">ï:Ï$^©®žõËWŸ}ìÙXÈNkâJ|mlrª æ(·»‚e¹`L€ 0ZKÀQÙ+ï?ø¼^˜9ýŠÓ.¹ðõäŸ&ë"§À ‡#Zœ›×®Þâp:wÄ´n×km»N»ë7„P—26/G8M6âRœï ÍZ‹ù]{©ß:÷YBe,ûí—÷þ˜ûÝBðËÄ’î·§ï)» BEÓ¨I“ê+¯ù ¾‡_¥,øeAEëárL€ 0&Àj#JîshŽtSO2sÌËðÃÚ1À×LŠšÄ!ù+“X¤ÙxË~ýiấKÖŸyÉeWí;^€Èu%BéEy<*&/G†:>Ö®äq8ÍJ“ìЉîñdnýsõo‹˜óknn6 o²zÛâ›\%ˆ'qeñ •Iô{WJhNüþ+S—eL€ 0&P D´ ‹Ÿ°] ¹vFbü%µb®™,ß$¼#°},–zXêb©ƒ%ºkŸþۜҥGdTL£°ÈˆºN§+\(þDýÕ"é$wnnznVÖ¡­ׯ߸fÅßh8¹—…›Ä¶mý>‚mà$ÈéåáW€PÑ4<>ñ;%T™‰ ­+Z—cL€ 0&P[ TÚnSòUÜŒ'ˆK<=)þ·Ú 3€×ío·EµíN=ëW¦¬Â²Û–…kí´P²Ë|ª™‰%bE‹í;Onòó&±M oâlý„@$ú›J]Œ¯Ùc¨ë`L€ 0&PÛD¨¹ÝïDìÒwl€öˆt54Ã="§¶ ÂõRߨ–ðplG.dƉ…,ä$ÀéAŠüù)@úõT‡D"ÜßäVb[¿­¬øLœ¾‹´0·-ß¶xÇ.Nå!0Ü==Jy÷/–RÕkálÕÅí¾‹Þ(pbL€ 0&ÀÊA pº Ÿ0 ç#¼ûßUJÝ(q‡.G;8ëñlqI¢Ñ›„$ Jß$Ìð'Àk²·¿[6²~“·Þ`M¢D8­‰í·ý¾í²ØÅ©<ð»–#â“ÞE™îX.eñ œ˜`L€ T€@@EÚð„攩^†?øgÂÕøN¶„W GŠ/B–m²p“µ›ÝöÚßtœúÓ^°Y£ i{!î/Âé¡…D7­i¡cd)çTAdù¶®…º^jòáâ_©`U\Œ 0&À˜@­'PN4 D¸xðµˆþû„ì;F}e qÛþÂÛßtœRÀûµ Úúk[²IXÓ¶-Âm!n¶‡Tã«Sc,Ÿo!^G›»KM<Êâ»:õ·• 0&ÀB‘@P„ÜQ.ÄÅÎD˜²6pEùËGZ”öíÔ±c‡"„jÖ&ê3[Œ“à¦ÅÞg÷§½®f—V®æÚܶ‚ӚͽØûËU)g. @q¾ B ªÁõäb)Å66#1áGfĘ`L€ TŽ@Є ÌÜéÝy&éyöÉV¸›˜¶z¯’jNJƒã8ˆÜ~|5!MÐúÔw’Ù€«“ïÂá"­ªÞÍÐkf[©šRœo<ÚíÀWêÕ–®–o°Ïwõî[n=`L€ „*Q-£&õ7Lã"\v[ð8)ŲæÄ˜@ˆ€Ï„ß…fm¥I¶¦N›bMäæ0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ Ô:÷Æ%¶—ø¯Zwáe¼à{Ý;Ÿx]³s6&À˜g€êáj˜¨…†Ç%Þ¡¤º.]*™‹ÕvMªo¦%&ülÃãÿ+…\1=1î¹’Îå•Z_!Œpü£’òÔæý†¡Î”Jƒ/j3¾v&À˜@UЪú„|>&ÀjMt•B´Ö¤ö?\U²¢‡©äœañž öUJ%¾–R. öy‚Yÿwâ€ñ&ó\7`L€ „¶€‡^Ÿp‹˜@5# wNŸ÷~a£ŸŸ” ”xúÞ‰'O7î`°.fzRü'Áª»ÊêõŠ‹M!ëWÙùøDL€ 0&BJ€ï¹ðÂèì#û»IS´0!TºCºRÛ.[¶%$hq#˜@!·ûWç}á]fŒëÓO>™ª`V^Þª¥®éáªQ_)ÃÔ”Ø+Qõõ÷‘àµÙüu?cä›±>8,>ñ?B“_ÀÕ¡‹0ÅýðUIŸ‘˜Ðï~÷k±yÞŒç`5¿ÖìzJŠšS<6ÝŸ<Ú=©“×k|árûºû‰v[G%L<Ç0Í7…«ñéJßÿš”bõŒ ñ¯ÑñO?ýÔ1wõ†§„’·Á-&–ùߤ0f+»pázDBâHUÐŽ6صFJÇ#Ó'Œ[L‡GÄO¼Öj,ë e¾ „àaýÞL|â¯ÂâÖêá—^ŠÌ:˜³Rº´«„n¾ŒŽÓqÂgÖÐ^§¼rà 7”ññçž«s8Óû2þ—]‚v¹¤T?¸¢уÉð„Ä$´ãìó ‹KŠõa¥ÉçðÿñçØç# 9Þ*\&Û40cÄ/íÇCÎcB©®3’âï¡Ï#ã' 3”¼›qÝiJSgNˆ“ŽQ*© Žý;21±¥™§ÐWòe<àÐ[ N~:¿°¡‘zW‡f6FßI¡©ƒNM®_ûPϽ~Ùx“ 0&pB']€o4(Òã͹7àeÜ77§ÿ Óù"µ_ï}øW÷ n¶3:$¯Zz«*%ƒí³ª)ù~q7¥”Ä úC´¡^„³îͯ»È(¥:>TK dE'‡GÄ ™g.‚à+¯iÙÙÈ×Gâ;|­Wå·‚öoŠT¡ôæ¨e—7Y)„öߨH纶ãP€»ñBªÏ…Ϊ9ÚàÆoøüÃïr÷èß@Œ;¤¦Ý ž*½æ(SW 00°÷÷ØÃã'dæéúmÈ:‰òS2”yʬá‘7—æx°ØYpDˆ¹«7Åcû¥9þér8¶ëº÷6%Lr…1í<–øVbœ”Ú—Óñ»×ðŽ0Mó§ûÝÏu%¡‹…K•2Ï„[Í Íḭ‰8Í÷`×ã0 ¸ï©ÎJ7¾„à}UDiw«\9¢ýã¹k6Ñ÷á?”â{òåI§vþÙš^¯xÞ›cþ‡.p:êOÒÕ¡vèO´+zŒ!¦GäÔS†÷ìQîI¦ºÇnq»•¶Ó›t7Ρk;œ‡r?`A2¯SB~N[ò÷¦ˆRNŠp:¿ðÞ«ñ4"=zFbÜdʃ6ÛÇ þ>˜”Ô47×üÿ?†°gñ]§û”?›yóÌ`z½Pžî´Û %¬ó %ºL^³ =Ÿ»œÚ´?ì¾­à ÿeL€ ”LठðÍýúÜ™ŸŸó,ÔAÓ`Ë„¢ ÆÞ&ÈsÂ’Ú¿Ï—p<Ð&%%µh®2~‚Ï*ê:wd²²w“>~Ò`X.Á «¾–å*c­œ­D“7ðg&Åÿl(„÷,…û<Õ¥þä+Z4‚•ûyÝ뽿ÄâŽIüúBÄõÍÍÍOH¾¼éÄþºM–îyzñ%Jß +qQî¤^Âuuaö„܇`|e†ûѾ’RuoR·Ùë<à¡}dÉÖMc°Ó¥õžêŽ[]˜ïAˆê³ò½æX|¾ Ë[R˜÷cm ðG'OŽN?âù'Zuaþ¢+¥R{uæ„±Ë L†àn‚‡ê[팰Z’P}yzâ8K¼â˜Bu„¿ÐǾŽÒ¡]>ý™¸oír%­yòôÄ„w ÏÆ÷ÿüŸ:Ÿÿ«ö™=Ds:;Nsý›òŒˆKL4•š|ßtß›Š<ùØíyÅý°ýF"F‚õJ×ÏÇþ-;Iç`އޝMC\‹ír¿\/Û›=Àá’ÿÆgh~\“&fΜ÷*}FzcÄ7>âñVà%Û:ŠôAAÖ‚¿pj˜›mÎÛÏñÛ}ÆÿXmÝòNZÄ®CYã½¹æˆïðÒ8à7p ¾÷cõ|ã±Î/¬™*ë»â6ÜÓ5³´2|Œ 0ÚM Ä›t0±ìêß? VmŒºWïàfÕ´<çÂÍñÒ|¡¯Hз¡³`5ûœòâ@IDAT¡ÛÈ„I!E“$ ›ŸÝËŸÊAàZ‡TåÈ_¡¬£âŸë‚›^¿ ®…–_ÞoôU wá7Uæß5òÆà751%ù¯^ß®YEÐൟ¡«/áò ¬Ççkq/"“Œ)Z—üÂß´ßPF¼ÑÚ÷æSãÖøçCÃÄo³í‹rÆ|„‡…¶£Ü‰$hEÄ7Ží‡ËÉÏôÙ?Ý?qbc|ê+)çùïdz†¯þ‡Ý/5@ž–xð¸økZ ÐgÁêjUG_9)µÐÎ,´4ûö»!n'ERÞæÅÒÔÛ£¹¦®¿jŸÏ”ê :¦iZ'Z—PîV‡Ò1išxà?€Ë°¸Š,â9zîˆéÝÓÜqk‡»_€K„ÂÿPm®]°ðÿˆ¾ýu]jÛ£û‹öÁÑý°Ì瘳Pg6\ƒâýö×ÚÍž/¯î°û`æRaª±'ßþÐGNä¿_ò®è6y]ÿc¼Í˜ð'PåpòóÎ:°ï'4bCʵ­D˜}>Ý2 ïðÉ+Þ*WYdÆ)7ÿY¦iÜda³ÒðgŸ­«2uWâ­âp{?ÝôvII°p\‰}¸‹dÜ$?åË:wYýAÉê…[jÊŸ‰6„¡ŽŸ”t¾f[ì¬óè„@¸ y 0ÖMÁ®(Dž”a×NO||Si>¥(cùzjBûBã<Ôs üNóàû¿–Î.ãwê›^€Eñ*Ü$²5)?s8êO†.‹ÊQ²^Ï—æ«Ì ðZþg4iê û\‡¦½=홸,^=}®«'\#߇e/WÓÄÇÓ'Ä?eU^ø§¬¬({Ií1>é|Ãð¾‡>¬‡óXî Ò!‡ Óœêtj7½éŽƒ{…me5ßÖr4¬™ô#ÝI= ¯ú¢~¬kÀóO<‘9ÚÔG÷ªp¨8e£~ˆvF?j[$Kò FþmTŸ b%¬ŽÓðÙ9´wçá>«£!ˆë”+šž‡ïëÿá‹YÑÓ ýgääæþ¾êÚ–ƒ{¹sG¹ê‘b>ÞDà­ÑÑ4홢:N“Âç*B¹ (uˆÌ|øZã«t4áÙÁƒï®õ‰ú¾Ë_è^qrü™|;žß>¶ •Vù¼­2„æ0чGývìx„FGpޝðÝ\w4—xÃ¥»ü>ïu»ÏÕý>—¸é0œE·€£W$e8,à¹p±yîí¡ôŠ©…¯(Ø,öï÷øÝ¾Oÿvê‰×ªËy=»,œ»j£s1 ÿ7ÕP<3|O%£„CφrO²¤û’r(9Þh…,éÀ±}`gÆW·x¼ˆßîðá I·Ì˜÷±}¬6®»¼²¾‹G÷ÎÇï©\Æ¡¢¬TGCê º¼´æ‚ cz&=ÆŸ˜`Ö}°ê0HÉ>¸þªââ»°¹¸©j¨ozZ¿~•÷ Üð:Q½ ÁuËý¯½æ{µ(³õ›ql‘r:öø×évcX”€€êaGŒ£=¶§ãüÓÈêFùüýAQï,%»ã&?í{þ wQJNg8 ¼eR:¯Q²/n²9Ò4&b·žx!ÊŒN×í¢u“6Sq¬ntèÓH|S¾BŸÒæäSêrIä—M }J «QÍ!éÁ`·KÉ3„CÞ‡›õ»¼7@|ç;Îó…tŒÂkëa†÷ðC…… ÄnoìSaΰ.?úƶ¢®¹þÜ︣¥cìë;%ÅlC˜£ŸÎWa pzHÁõlÆwý‚£5àÑ[W€‡ÇÛ¼ašÿþâ¶Q~-”Ãл•©fŒpO<µ¸|µa_÷—þl ô|<UF|’¢Å†šÓëµMÖÿÐÚÀ¯‘ 0²¨R xÚ€~÷á†tyÙ›WzNÜà¦0ÞÇ@În­/>Tzî£GÑÙÒ?w—ž”¿7ƒüI-_pˆÒ{4MN†¸>.áµ·ÏŠ…ƒŸÁšàÉUd©þ®DPÊc !^´óÃâ%]yÿ5iRý©cÇÆyoƒ%jÖt÷“Û(|=gf{³žÏ9œßWȧ¯S­º•œ?#)n‚µ-ÄVøÕþŠù@\Ãc…ö¹Í¨ëkœo@až?ÒøÆB¸ÑÎ[¨­T>¹ÒÓó4MooÔK¯ì ^Œª3²l ²]ÿ±ë±*ÍWwæL‚e:}¯üÏÑþ=Äöù8×so,ëaÜŠ&ÁvD0†õPÊï¨=¦×x ¶ÏLŒ»¯°}Aèm€ v ܓȢ;‡öã{vœO0®Ýzpsè5Œ(’OÉçУ"²Èf‡³ÁgºÈP¦g`˜¡ÖMIŠßꟷ²Û-ñ¿ìò&%{uõ"¢”LhæŒÿm·>¹£”Þ¾Ó&Ä}JõÃþ¿<Þ¾ÙÑt^3&¿£xêY¾*4Àš­‘›Ï‹(ÔAF¹æúÚ'åk¨çü&ÖªÖ ¿;\ˆ·A£PïkvÔ_ÞR6f>ÿ¸œ£¼æ£ÝSú¿+¥X:„q ÓphÀ‹jìÉÏ{õÀ:¹*&Àjè ªIÉäi‹Â€ª‰7?ç©òVXpSSïÂÎy7•µ‘ ÑÞÕ¸Î×ÅÕ5*aRˆîg fßÃ~6m Xðêûç-Í”ò Ÿ>Ý…›ä?‡%$¾ˆzþgïkô²¸na=i¦)Î&ñHŸsõÜ3qÍ‹t„[£l>¥~Dªðˆó Å7íBµb;–Ú.³o¬»lñMå^xì±lˆ¢ýʔ倥±*G{¨¾v?àšÎ¦·{Ig¡ÔО§¼ƒ æ/«7œãvÞƒñòIJbw0±\SìJÞHLH͈‚Aþ‚TŠO°¡¼÷A4ŽÖ\Žëme ö:%gÁpœ£OÀÏ#ÅÈ䫚ö x½~ÒÃP¸+öj|9½ÊÔy¼{ žÇÀwù¦iîx_¤#ë;+åÛø­]Š˜o–Ë—_5E6ñöaúÄçbû¾#ʉ°>?ìŸ îAx¸ÐÞÀK‹—½ú¡ƒÊ›»~ÖÓ ÍÑÀ?_ ¶­ÿ1QòJ´=a„;I®©ò7J²ëǵÙ|[D–wþ'ø.ðßQÕîSŸÛy›9›ÏÅÿ —ø×È8×—xóõ²Ø¶?×Oâ~½&Øe˺V­›<†>I×õÃ>ÃBYËV÷|]'¯‚¾¸>Ð×øs»¼ðç?]oqõQxT IIFÿã´xüÄ«qﺃ¢ÝøãíÐ%`÷'= ‡n+¹e%Pep¯7wþåKƒö°§öL«¥K–Ü ÞսƸáîgÛ^ý”ýˆŠál‘j`{BWú“¸¹ƒ¨ûÞåÒxuãCKÆúå,Í”Äß®íÅCˆÔ”ã‹4é0Âáßz¡]|_ÊÒ³#ìØDøØd 3.7ú¬¼eô)Eݹv~ë-³eõÅÃ\h“JcUÖöÛ’ zw^òÓê ÏÌ31xêjô×W$ôàGüµ©Ä5;͸‘7´g§¥Ó±:¹å[ ®Òÿ·Q²O°DT!^ã%¬¯=®® íPn·–²lêãÁ¨"Dº ºo;QýcO”gfbÂeÅå) ñy=˜ŠÝ™ugúGMñ+€PzIøHK‘tl½…onv»ß¹+]ŽzÙ=Æ~+ö•]°à-ˆUWÒ}îçZ¨0Ãã?Y,Óï"/-¥&zøD|ÅŠ&ÄÞ~ÄÏŒ¸¸Ýø|5]£Ü}¸yl´ã`aY+[áí…$˜êhšn—EˆTú>ÑâKn÷]øPüۼř„c“H\Õ÷Æí·­ævácYÙû½ÞB‹ùöñÚ´†1æIú§œd<‰z}S%ƒŒùû².3•yþõÄíbÔDŠˆv~àÿÐURy;rÜ©/LÁærøÔ¥ë^¹Û{éØÉL4nÈ£§opаó޳2ÛJç¶û3Ã݆§êÚ‘Ôk€ø¾=X'@Ý‘ˆ}K–‹iå9ÅÙ…8[ }Œ¥·b߹ŕ‡~‚l2bãN¥ã4òH¦Ñ›Ÿ—¿¸};½áî¡Î s9ºLqÛHy†%$ÝdÉÀÂÙÂD˜dÙVÀ;#‘¿û„wáq\ç:Ü 9áSj4,àus…L Ć1÷Ëõ/XMÓMu!ê.Q€Óýe—wÑÍhÄýÓãf¶ãËʶƒ´Ïƒ·"CU¶®@–/¼?ŒdAª‹"£ý¤º¹ÚZL J8Äwß`3Æ ¤ Cº!ô}oà5ß[%µBg"êu—¾\\œøg6âþÛØ*c¢×á˜ðc¼!Œé°6G!fsDæÍ…ØUÀâqÈf|ŠŸÁw6FF6VÛöÇà |,¬SèÖð¤¤+gd*Bý‘O)y´¸P~>kP—]Wy×ä‹™°²“oì4XÚ&Ql34' RÝZÖúðUŒüm—ž¸Öè/ñÖ`DYËúç;Q{èu,|éŸ3Mù%Γƒh/#f`â§©~ð …›—6ݶt»Ý7äãMÇ\ÓÍf¤=“ 3Üã֣앰˜¿œ­gÇãüaXÁ5éâ©…S~û·©´í™ÆÍ‡ƒäþ7€¾–ÈÒJTöXðSÊ4é7ż²]ÅåCžþô ê-Ð9J1ÿÖ64mGiù¬Ð©ºù òôÅ}b\ßœ>aÜ+Öx ¿‚–ßp–>Ö÷Ëqö|¼¡» yÞ‡K׿lÖ&¹©`æXXzÕ¹ØQÿûS·ò úŸJÊþù¿À5vAøËûq±é¸;¾ƒ±çâ­`×<ÌÜ: qÅb£F=šu0gµ&Ã.µ£|ÑÜŒqÔN‘ÎîS7–¥ ”Ç?áþàL÷4ÕYpS¥6˜0\ÌÂÄX¯ÙcQþ’ÂÝ–ZîŒ-Á3µÐíK`€¿Ê—⚎5aŒÝÜ[~ÁÛì7iÒ/«.oÆs —@KÔƒAo…æMwÇ'S~_xÞ:ÎÓT–>ƒ—A‡"o¤Ñ?'-Ü­}]¼üF‚Ÿ6÷ïûÄd‘/R ÏŠ,FÇ”•A}  I/†žÚæ°-ì*r ôz`Ò¤F¯·ß¿<…4ÌË1¶iNyŽý£¤ãÃâ'^‰HeÓ[8Ïn훸ÀoöxŸRÿ:+º]œolyë¢:‰Óöù·¹¼uØùKkå[ïÚÔ°Ð×Ö.Rî5Í.(c•‡"P”»ðI(|YÓ?ñôuj0O‡Ò©¾Ù7:˜çນ@(èòÂêÏ Tƒ:P÷¨yíI·Ä„(4¯àq ÿF¸ÊŸÍ8:.±µŽAög“d¤xÇÈǬ«†˜‚è]ã§Oˆ›Q0[l^V˜æêñÆ3O¬{øéWêæx³?D=›¢\ÑO—ô?â|*Ä€Óé KÐ]^Œ"’ƒN¹úM÷ØÍÔˆY´Eå!ümn²¦×«ž‡H—öLÄ0*} cP#ì;‚Øó“D„ܤéŽH¯×ØÝÅØäãîÙ›±Âôßuêº~H?RÐV Jaa ]ÿ ÕþâôM§®ö{”lnϪ|¢6ËŠÚ„k _Ã70³íø!tõ*ú9uÒÛˆ£áw…6Æåtüî5¼#0˜ÿ‘—«ëkO=¾Óâ§gíTÂqs´3ò7â—}(w4ŒßŒ+н…ŃƇxXˆlé<»)Ýó¨Ÿ`Ú&d;Š®„1]óq~¼€Õ&áA%o^G!ÿð0§£7¹¤ÚýލHr™S¹fˆhs_´Ó™OŒ¤ËÑmúSc7øÂÝÖ »¨*#nË–?†@P«¯‰43bÍ ¶Õ¿¿K¦¤x}ç ðÅîQÉ: ­EÄ7U™ŸgbÚj‘¡š7^å ¸R4Ás{¤°Žø¥ù”úe-÷f |cQ‡ÝðÒê"ë6òí¶óVt}¬¿}E멪rø9Eý\¦À÷ލù`!Åw=¸7)°þžš;[=¹Kß1˜j6,¨ã!øõæŽÎoþŸÃØh1}-Þ,&öÊx³z.¢6Äç"·§Â{ͼAõBÔåà\Âù…¡r©jŸë Ä9îMj,ÇmËñˆ¸ÄD¸¶ÌGÝaõMµÚ"UgDÞiæ?ã-ŽÿfÊüÛq<ò@|_ K±× —™‘Ao}ÉÐIÈõwúv ‘FÛenƒ_Á‚M¹Ð 8Æ]MÁÀ{ƒo¥ÒÂÝ‚ß$d:.´.&sûóIÜõûÓáâqYD€#wwüOûÉ:Á1ÊÞ†¢Áe­ÿü4œ#–ö‘€Îòf·Dû®ÏÁ´î(xVÁ#‚8:í÷OÓÜqkÁiw–™s.úå+Äú¿Ö¡9nDxјš‚\]~Æ…E-ßQ9C=Pç¾7Ÿ·ÜW|ýÄy.òíÀf–þØÿ³½m…»E´d„»í4 î—ö~^Woø?ahKZ°Ï‚/øßÁ>G0ë·fÆs6îŠh¯ SšâN Dƒ[¦dzµÌssÝÕþqÿìVã¦ôsû¸~&P&² ¾ëRþ]¦¶ Eůö‹gD÷.»Í‰çZe­YœBj·T° Vüã¸g\SÖº!t osZ` nLå¦N›ÒÂwŠrˆñøÝßy$3   cP¤á¸åÚ磵uŸrhšZ8…7´’&1[ô1)¦aÔçpM©;*!ñ\+þ¸ÄàqGIñü'û˜* >–± Å”Í)fŸµË?Ü­ÿµaûÖ0é|¹¤rÖ~)~”¦<1i„¶lê»o+¾2¥º¢(¬ á¬ñ3èÿ¯ó @|ÕâþŽ9ߊFòr(Ç_ÿ w ÷”Âp·þGx»¨ ¸ ‹JyÅÿ®ÇN. \]'§¦îôϾvÖrrÁg­pS^†ÞW³±R8––µ~ŠàðÔSCŒco2e-Ïù˜ÀÉ$€Ižð{ î{Zˆ²rߣ¦>õè~X¬Á¢Ú|~Æo>Òbü˵—…!MÜT\¾B û8ö,¼oA7>Ší!È+þ–ÆÔÀEæc \üWN®X ãòRü^Üùql#†w¬2m(®>ÚW–p·”Ç…ÖÕ¤mëi„(Æ[ Íš­ÿÝe,ŠÜãMº¥ê´pµüÊ#NÂRxÍ·îžØÕÿÚñ}»¢ºXT®H:Ián‹´?œ@•ðN¿ÿ¾/µ_üƒSOÔ¿ T(å·Á¨–ëd¡H¯Œ¿õúqÑ ×VyHµlñGYÝë!(yÄøE«¸Ø›{àÚUùšèÕ3¢ÞÔ÷ù®V¾J®¡šˆŽ‰œ›™™ãÐ…ë_Tn¶sJ«ù~÷s­òuÄýv†ýFoDÐ.wÀÀÚƒ¤².ázYWÞ;á?ü,:]6©»Ë³?«œ*þtäø¤‹"ëEþF‚Ù˜ž×B?ƒÎYÙð·yø–.ôñÒÓЕø6×!Sà+½‚=!ÓEn„2=à µŽü·­2Å„ÖtDÎÍVÙáøÕš°üï ¾Ä`Ëÿƒ{NT÷¼Bw"q~Ïk%åW ¼|~x³qᎨ­=ó|×NÓ\âìâÚUܾ“vð¾ÀÀÛ‘*J4t°’”#•æó‰ Öi¸^&*úÌÞµÖ“Áj\º>0£lšG$<Û7œ¸¡Ü¬ö²^ˆïa©Ú¤+l+×U5RFtLG=²þ%Á©ø§µôÜ^Z庎¸Â|Ays÷ß/€eÌÎvét÷¸uT–fŒD„”K ޝF„T jÌD¸ÐŸá|ZiuŸè§ޒ}(ÓMØ™qij Öèl-B>Nå,Ëx”¼>ß™ˆöAáosM•¿çu¢zé8¹· ½Ûá~jÝØ°ÿ•T†òiR» ÿGFx²ÍÙzÖ^]W–ÓteÛPÒ9)Ü-,Øo m~÷ ±Ç|Ó ÍAn:V¢ÐºØZZ×ÀY8Ën ÊÕÃ|ó ³b¥} ó.xÞ²ü¿i?EM wÅ^ W¯2õEoÆX¿Ç ÌMÓÜñe~ËHuQ¸[%¥î¶)íãT} àwQ5)mÈ#óðFüˆ[úŒšq–¯œèz¹>&ÊR.or ^íú¢¬­Báptéùõ®Rƒ}> fJíÄÛý·Ks>H!Åè˜wõ†Ixm~&„D+üößL­ƒåùª‘ I7 ŠÂ=°>µ…xÇÃÄ.DØ¿‘ê#Žü@øSFâµ3B :†µpvš³Kßôn̸‰‰ª”&F°G©×HŠ·zN³f:‹ˆâèÔœ}Ë.oB ]È+‚?쓾݃W³'NtCQÛ÷oÎÆ=¤¾ïfˆÚ.Ø–å Âüb|¾cú„ø[F=ýBcÓëYïtÕoÓD4ÎßåÝøW½XWŸ¨ÜdzwéIß§òiâ>-ÈÓa-úXĸ&´È{"QnCˆ´KÑΑ*G´3•ñõÐ^Û¯['%ÕC-1>éüH-*Å–ëðæ‹Ãaç½î~b=0@¯G»¦Q>kÂ’L}¥æ’W˜"|ðæ}ŠóMDœß¹tœSí!ÐuòÚa…Ø+–ïo|¬ç­“kcL º¨:jŸ²rž¬éuN`’„eK8o(¯ø&«âŠfÐŒbþ !¿ºú†•Jß÷¶ß³žVý3nÓa‹6æèÙß"_2¶;“Íʇön"AÔ7¯'×ÎÌÿû– ®éMÇž{dbb˼}é{~^µñ†cñçšE Ì%ïÄoj[À®JŠïú‰×²eLÛ^ßÎÅôÀªE;¿D<åÈò]X¡½´d²¦Ms?¶¢ú€Ù1{ŒÔîØÞOÖ«e)VÂo²‰}FˆßpL<õ$ÍvJÇI|#2Á4Œð4Íh¹¢>ßí³NTMdBÈ‘.Wê^–ïõžcŸÃ­rtø5ÛŒplº¯x¿†oèUþyx»vp´•cñ–®JR®“ \÷¨6®† 0D J8qëpÅÕlÿÃ0Ì –«:¦¤¬)O]˜7 ±Uo†å¼H9¼‚Œvƒ™¬zÌp%½rÇ»Ž­›Ä…i*Ä6 ¿ œ…¸ðQ“–%Í?o ór'ž ³“¬w8G¬ˆƒÐƳ‘âZ©ÌçŽ}P0r&óüÛÃÛ5“@ï¯öîsiŽ ñ}>.Xy¯u,«/¯—nw™ÃA Nýðƒ<¢ëôl}\Jšÿ´vÓ…tn¥iˆÔ`Þ1l|Ò%Ãâ“Äï$"˜­¬Åáæ˜AnØÈø‰—#øÄÕá‘ÚQßP zòŸAßåó„i¾(2õ¹´ÀŸ6ÆÔd‹Òê힃Õ×ñÀ:/#Ýó9 a:WqL(œö_y´~u+¦ŠŽ,./ï«Ùþ¼¡{¾ᢩÛWVöJq?Ùqц{ºÂPĉ 0&P”@• pº¹wHYq;nöÏàÆZæ}‘fK±Å©¹·KYõk‘ýeøàÎñêùZdÕý³ÃÊFþ¥ŸØ7~M‰÷áƒz½ÚþyÍæÓ -¶ÙSëN{&î¸t§È þy™ÏðŠ¡£>¢úñzÿÇ7Ý÷"ô‘Óâã1E®4´Ýõé3%XÄï†Åqüjg-¨šÿ†(Þ³wmÒ‰Ùêä’Š6¿Åw6Šº°÷{³ËZ‡å§)U<ž…ˆ —Ñ‚ïÞcËwR-4 ò üº/šÚÑÂÙÒ²*[³¸J‰WýòŸ¦4;…¹\ç¿>nÜ~ßy1XÉ· Ô‘­IÇý˜ýo ½Ìœ÷ßÒêѽ‡GÃ’­ð{‚åKPÉjÿ:ý·‘-Ë×vÝ´ž™ÿoÿ<¼]{¬¿¯ÛÁúáÿ€¯°»$îo?G…GœµúSÈh‰ 0&p*àÔz%Ý1eÕSh5Ÿ~?®U%ì@¹ˆŒçcëÔïÝ.9yE ÙJÝMÄèü‰ÍáŸd ¥4Ÿh…u}+nýÇ …`h6ûòY5Hµ#Oä¶ò¯-Pù(¾2=ÈhççþõÓ6Å…°È´^ïã3¹¨àÊî­MdÛ àØbü¹ðÍ®mýv;7þGÐ÷Ëz‰ø-¬‡ÿõ•¾ÝwWûwÿ&W2'O®ù/¸j|‘›úFbB-±±áx;$Σq{ÅÄpO w:ë=yAγì°\Ö h€¥S{P´j<å ÷»J;)†¿Å[«aÃÝÓ£(Y·i¬F©õHÕ³a$SžáIIÍáòr‘•0utÜXÚÛŸñ›ùŸ¯¹7.±-í£‰4,¿p;¯k%œ’¿ík•ãü¦þ.+üžvc’šá7?Òóœrô¡²¬p>&Àj “"Àmº’W-í´|åYN‡< ÿ¸ÞÄ?º¸yã^x4aůÞG GÛŽËW>ÑdÞ<Ë|4W¶0=­¦…ËëŠÈ¦CÇÖ¬™*¯À‹X ÉB' Y×?o òí4¿L’if4ÿúi¥Ðÿ¿½3¢È÷xU÷L ê^ë¾'xâ}¢¨ »êóÈó麮häJ&ØIÂ¥(r$Aeuݧ¢(«EQñÁz²x‚¨»"‡ 7!Çtw½_ éafrLPó«ÏgÒ=uü«ú;Ó™ýû_ÿr‹L©îöòËíÊ"%ÍáaË —Éc‹! ­%vï…ïÇÜë(úIÈBÜSÛö å<íù«Æ€Óz>á´…?¾¸Oúd`Ç<Ã0®:uäH}ß,,sJo!¥¿³ÇÙöÖ_[±ê¸„|W•¾áI¥’_!.îbͦ/;w5â–}n´1˜1w¡MßÃ¥dÂ…­\'¾íR›XÌgÁUldÿC”©g1QˆÄ–>óyDT¹ãYŒ×y…Áœ%:ÌîŸ@þ‡Xô¹Qî ]=ž·L_ÝÓóÉ®§õ:“Ôkq?=ß¡ 5I ÿ'm-7L󦣓RŽújDÏ9Ö+Ô¬Ç÷$@$M†ÞŸ~ûÁÇÚ ¶„¯9ûìVŽãtq]7ÉPjǑ˗oÀ?¸jJy"F {øáʶžlS„Ú9BTSzÃe6(qE¤Þžúª­2Ãʆ׿çMTÏU7aG°ûÉ^áBTlÜþ0Œø/æå¼¡ó±[Úí°êU}+ß¿¤¡lOÒ« ÿH™l¬õòÂÇ&¨§¹#"KŸ®¾®·Gˆb3“€røªNŽä»êè$ç£,cOžLƹØULG~©Ç“GàÔg6èGáÍú8|üø%&&ˆ!×o†]¡Â.¥ö5˜I¯Ám`<%½¸Ø÷ÚŠ•âþk'Bo¯íéra)G ]¯Zrj*ß^C}ô”o/¯j¢ºÑ{Ï# Ä"ðñÝGé§JúÅD$@ûM@ÿ€1€!Ìg¡€ßñ-r 2h8z±™Žä Ï‹rÇ|‚Cl)ÛK¿ßàæ]”Ï´•®©ëÉЦ« Ä¼í;‹Gæc ¸ÖÍŸ©û÷”ñ+`‘lë½à¶Sl(9ÖH*ß$›€^ÌŒAƒlÛ§]=Ä.»Ê÷ÚŽmýƒ…¯U>îµßbr¸H­Ù¸ßÕ ŸÑ§>±÷«]@SÉ©&”oH€H€H ñ~vðÄ_rì sÇ~Eaþ:{å <ÚÆ.Àâ‹äƒÛ=¢k‡J]„ySù8=R¿Ò¼E¸ö<ÔÛ ?Óø”†ãm7u=(,7añØá>ñGGZÙ*-€R¾ V÷µè¿ªÈ(Ä#ô¸>´^{I 9 ÌÎÍZŠþô«fÒ.0Ãkf6ô}Õnr–ÓÐ~YŸH€H€H ‰ è˜Ú^´O´v3\P õçå׌”ÐÔõ¼~x$                                                                        _ùs¸¥”\4Åž+ܾB¨•Ý00¿b‡ò!廆/ya¿»w¯ÿ9ŒwÆÈígHߦٹcÿ¹?í[BËzÓ·Á^v«ÛÆ?¯x̘í-ášuÓævèPÚ9÷Ö¹¸§º+!:J)Ü[¥”ŸB.é¨N^œ™ùÏP¢Æ@¹$@$@$@± øbg7Oîò¢Óü·4¨dŠ1B(qdt¯P 7„ÿ\(”èT–¹%“åBCøÆ÷ú0ºn}Î3“ªò.%EOn¯„üÌ4Äü çëÓ¾±u\!† á,‡*àq`îj½<ÙÙ&Še¹»U¨€ÇáT[ö”9)G…ÜÊœ²Êí7âöIŽ® å;œ ”p„µY|¸9¿Ø˜iš¦Ž¾í§ÑuyN$@$@$8FâD×.ùÕÉþ“Üöá'P¦×T¾ã´4 @\é(ûƒ—&–L?¶šr§M8;}\ðZ%*>VR†¾•†Ì‘B}ëºê‰Œì`Qmm[ZYF øZK»æ_Ãõê§HP¦ï©T_(¡n­©|ǺFÔéŒvãl{óÊ‚‡}ýbÕa @Ó8 xÉ$ój[Ùïàrþc?. cVÃTÙ7o,Õ¾c]íï™2¥µpÄL¡dqq0çªâ¼ÀcE/â|œ)}©—wy]rZBùàÀ¤PÞNm ×úkºÆyóNH*(6Ÿ„2=Ì”ý¸¶®ÊuJ Àß¶lB$@$@$Ð@Íî‚R2Õì/÷XßÛ÷9;vîxeyQ·>½3×íŽwÝ;wTÜ ò¤íüãjÖÑþØéÙÁ'WAÙ"XÃK ©ž/ æÌñêj¿äµö²wMaZ…Á¬…£&Mj»ugh&ý¡Ôû¥T‹ü©æ]3³²6ë6éàÃÂó¥R=„ ·©¶CÙßG©Íäÿö<49–ý$ÈyMIßô9¹c?ÈÌ R®ø& ¿óÆ¡hcÁuæ°9ÁÀíÑùwß«]›w,ýÆUÂv§A ;þóŸCþ‚¾'÷@ZZšãÕ¯’­Çuò>•ÒQ”›õN渼K'ôü„;€É׺¾4e†pÝÙ>ŸqÃ,+ûc78'ÿ|Ûu5LyGф찵|•×Ó ©ùÛù{O=zçVÞÉvHMEõSñÔ¡’µöµ¾çëîmZFf ÿW¨‹à‹ü7¡Ü©Pú{ûE’fô½.÷’¶êfò ñÞ×÷¤îÑ×áÕiéGͨ`ŽùW0Lk |q›¨ûòŠPv†ûPcd±- @íšÕ¾hJÊQP¾ŸlåÛ»ª36nÛ0Ë{ëý¤'ú{_+†±Ê C¾)•è¥Ë””K%·Î}ûrh&ǹ¾Îoè|(ß P³«ô¿÷ûåeÿ›Ðn÷©½mTW,u³ ÷‡2›‘"¿·lï™Ï—¬Ó¤ôý^¦ÊSÐùné:ºF’é_ˆ1÷ËÌ™x’×BOp>Jkx^¾>šŽ£Ÿ tW¶óŽ Dª¯l2ÁHÐìà‘xüs^a^àoh?ën‡XùÝ+]·×œ ù?ërp$@$ð+#Ðl xÉdß…P(#–Ԧ䨜°å8¦åy+Ô½¸J?êvPÂ+ÏÿÏ.¥iiÃvh7;¬t‹Zvv9W~q/,·½`Ä-smûA(·áK€+…O+@†a‹Œ= ¸ókS¾uÃŒ¢"¿\³ù*WºgC=Ì¡§©*ÂŒY`²â> ÷’‘Ó†/s•ûGX®Ÿ´¬[ËuûXIú$ÜNª¥ï`Ý?BçÜmÝßiW¨ôP\ïï1öðõ@¹Æ5ÁE¸ÃÄN®+A9túôä7œ ¹‚[ËÜÅ+Væ½±båù–5ïíuöJÈ3'TI8A[Ö£¥Íæ|‡ÉËj¥Ü‘VÀáð°ö˜Ÿ™£BZyløÍc Ž0eµs;ÒÊwRµÌ¦ysÜ7Û¾QÚý§Ö„ûå|%Âx‰{•×àÉÏ‹pãz£Ö†MPˆÃOáëûQQ0{Rß9±Cá\ ¥ñøXe:O9¢‡o=iŠGà»}“vùpw;ÚÍâëB+ð~¸½”ÉPÂˤaÌð^P¼0Lã2×Hþg¸þR¬õÎc¡´&‰5›–(á 7”ñ,húĽÑu‹r¯A¹Ø ßîëA9WšÒx$ºNÍsÓñÁ­eo‚KMØåCçT;¬¨Á5åyoìUÇ?$Iß´½­ªŸ]zR÷÷ `UTü´óÇu¯ƒÀç5+¸ ÿÝUò¿Ö:«ÏÿòKzf„svð}-§¶Ñ¾-«êÆHR}©rîQÊ,˜òx›CðáúDÁ€ì!õ’mˆãñ¹nHãiÔ_ŽÏ­'¾%é܉õj߈J˜9þ_ñeqÀ›fZÁÞØ# ÿ€„  hVÑ QÂ:Ö!Uù7šòQyÍÁº¶{-òÞ®™ïIOÛ²2[/2„»ÃëÑåÚ ­Öl‚Ï·,òò»ç¾´Î]¦6¸o_×’?ÀÝ"쾡ˡ”|k8ÈgˆMÞ¢D¯]CŽkC«zCÚ9I~³ÇL+k•n›ž“wò"b X(¸‡`\êæ2{WÞ•ÙÄG»¼@)Ú±‡ ̃‘Ž¢N0!°k(ÉB+Û°r¾+ÙEÝ•hk¸‰!ŸÇÍiR9Û‘¿82‘ê}Lx®Š+þlMêVª<Á5Å—ÑùqÏ}ÆÐT¿ÚZ¶[}Ëù=sòÚ‚ÉE T^¦'ìþÅgÚ3¿8¹{VFEøûÕuŒS¹¶(7ûñª‚‰øÞæà«<~H~þoqrŒFÎ*Ê <Ùh!Z@Hôs…¬3šÓ&û' hZ û¯6ÌòõÆr«jyMüVÚóc‰œý%\.¦añ༌œ¼¡fªñ²Ó©Ó.ã‡-=5„õ{gòoÚF,­Ú* Eõ1×QwšwZRê^eòP_àu¡¼å![Ý7(›ÛÅø¿õö”c¤ R˜›=/Vÿ±òÚøS¿Úe—–cqcw”¯JÏ)8]*{Ø^õ{O+ª| <ÇAùn- Xæ™p­xT¯î„ÅmµéëôŒ-ÊR”[qz’£¾˜™ø7ü­ß;íV¸¶‹àB°$Ò!µøH(ß©ŸØc©öK8Ô8îõuΪ.p`Iƒe~¯µÓg>!Bîé¼±m|­ŠJEù!•vÈ‚-þkqèÁ/DdÖqò`vöé9ùÿ#]w"¯¼7;7kiMZXqìï{“B¶¾§ê¡€×ìÕý_äLp*]íÚ´9^d ½î¢<´cî³þ°fw€+ØG†OŒ,²Ëï° Ž …œ—Süþ>Y£ðzÐQxð$f–ð|¦²7MÇ÷jEqn`º.Çš®Q÷bx3þ`¡¯ø?lþùBÍû*^$ -#^„ýD—{©¾‘‡j‹š”‘ÌúŒ¸¯*0ÑÄ: µUr’tÅ]ÅÁ@äÿ™¶ã©ÂyÄÁ½‹33Cz ˜äŒ„EàxDJºM¿Ç=›Žäú)ã1¸îï”!fÏÉ ÌÒe:Åû ö”îý;(<Ô-Wˆâ„µ"yýTƒ‰H€H šÉ!ùŸâöÅ‘£ðƒ2«-§ÔþI¬Ù¸ÛUö+†’ŸêòšþÚI>óQ,Ìì!¿mÁ³,ü4¦Ê+1™Ø‰ÄW×ÚAøfW®ÄþÞƒryÓ¬á[¤0Æ9Â)‚²¿U({Æ7ÊÄ÷Ñrªú^€±ôö·2íÊP~Sà 3~ìÓBö–Í*T¶ n¡c˜t¿zûwüpOµ=«÷ãòè|Ÿ«à«­0Y0Âî':ϲÒ*¡0•`Ñ^7ED|¹‹­¬¯P|%,æ×”Ú¥k„í|ж]餟§<èöõIsr³Þ‚Íx(\Oß™—÷›ú´i)uðˆû}o*ÊÝï>.ÓcpEÊ·{Æ;2P…½ãELàzac¬!Âoœ%|…k«¥zaà,kìj(¤;õáèëq”{Ú|¦Eã{ÕÊfg¯|ñН8¿Efšß—|¾¯oCùÞ;9Dam‘€´ôYg„]¯¾‘‡j‹šä3;àZà{-çë¨Cɾö}¥áûOÚÎl­ûÑ Ÿq?Ô×i¬ýéb·'¹Ø\lÏ%(ò†ò‹Á?Š ËñÂEPγÃJºW¬êŠÎ¤ï1(߯ãÿC •ï8ž @B4\¨°‚—+¨ E°šw½)Óžqbõ£7àAþcÚre——·y(+kS¬z:¯Ê-¿yû¦âììõȽ:¼ˆrýÖ®íZ››§Žñ½žÌ +­5[ÖÌ/fë0S‡5–#k¶ƒÙî‚è‰@Í:Uýï3^XÀFD×­rÉC^žv QINEM¹h“õÜuþ¯;W]«˜•Xƒ6ûÊnD¾~UK`½ §kßuÙNUèE¤Ñð´à/x¯_ÕR¬ë@4•TÒ/¦(ø0:Õ´ìF7É)&„õºo1h«# G¶·…Û šwã{ Øºç§È@jDÚOÞ¹Àç7NŠDâNLHÏ­ ¹cÑîV¼‘Šcøó×›jmßVq¾‰WGäFŸ(ýÔÊxPÇүʞ…[ûÊÿÁ«V[$ Ô ÷ƒúûDèñÚ×<‚7òP]Q“fYC¾AJȬˆŠ<¤‚¥lûä»Ö)ЖðíPÔÿî:âœ/Ò÷Ui¨´·é—·ãýžèF†˜3'7Ûs-›øù­±ˆ;€§÷{îa7:Ü…:—•º‹ÁöY¸|MÐr™H€H€G ypüB'î¢$_ð×♨Œ}O«”Áj á¾µêΩ²æ~_wÍø5´Ÿ7JãNtLm„õ»ìDY¾âËkHÉ kôºxõµueëã•×7?J©¨oÖ«?·þU÷¯&&µõºoñôæTÇVÏÁêº_éa“¦!³Çg/ÀD,ªãê‘åô„‚¿qÖ½YŸBÔƒBû*:½\g¤úÚü­ÔÞ5u°ËÚe+’ 'T×ãjF‚¢mC¡¬¬š„FÚ!JBÆ#´'’žÀÁwy¾·àí»pyú#"=Z³®«*M? Ãt#O£t>örï?Ot$ Ã_èòª4ïÌè iü=^‹ªc¬ÈCáiµ.×Q“Äž¨I^3ŒG§5é£=§1ÿ¾?öǵû ÜÜtÜý/îÕcÙâOVù68gãjûbÒóŠn™Š¨ù¥ÐœÁSOš#)Ðÿ ¡¸GþÇ×ü ¼Êp99<îÃ¿è ¬“¹±87û½2I€H€C òÏ91â÷HUÊÀ]‚ucUí5‘—“pÙø1Mr¤ê„Ï;»úΫfÙJxçìàAJÓ—PÄÎMè`¥ŒVR›´+%ýïcצÃ2¬üã«Ö „åÃmâRhÍzA8Á?üåºÏb3§É•B]àSâOUEÕÝ;j=Ü.Ê”-/DÁr¯ñ2õ¢ÄpªO$ ¯]SáÊRwÔ$¬E_˜<ìM©Z¿µkKiÛõvð(âæ¡¾±KÓÒ¤‹¸ç/8ÂíÿebS-$=IÁ"óÕ°v_Š·ïxRàK)&+¡®¿óòâaqÿ¬(7gdú¸à»ð,ÓÊÿ¸ÈÊJØgoÌ' –D YðÔ3Îý¸ìý·t¸ºö‰‚‹Ç´K¼G͉꣹äNÈÖ‹# ›«_öóË!`àûŽ0™·'rÄ)fÒ[B4Ú[+æ/éuôg°è¾‰ˆ9“3r&f'›©ÿ®°wÞ†'eg`ã«ó¼FEã³–@±ÞV!Õ (”‹«Ö#xÅ‘c8lgvð(ð·á Ò‡âЃ–ɵ›®@ôžXÄà ›IDATÌH%œÀr< ÿ'âGŠ®ÜÈóúDM‚2½ŽœŒ‰ÛëEкKí&ßð¥0YŒÁÛçË¿qbã9,H¾q´Lõï˜K9rF ‚ËgêðÎ/е?]†¡ƒ!wzC>ëÝ0qå|rçßaÍ<>ê»ôx˜H€H€šžŒ­‰O}ú,±ñÓ÷\B{òùê0¡ã ph­SÛ¿Ënb´cŒ²ß1°L/¾MHÒ “ýí®†2R®ýŠÐŽ Pž‡Ãwù†ÈÆWè9ìn‚èp㾑ƒ©m0Úù³0îO |¾ŠHGÛ°ÉVÐ4ÄÝÑmêŠ]·±çõ‰šd¶1_€ëÇ·bWè{,@ðÆ?f¸˜¨îð§ÖG_×ŰVw•w=e]—! ã  ŠÇšø~Óv\¿þ_X’|Hû¯m}êðCFâ3ÙnÛ[®oÖ# h8ü^5Ozu²ÿä²kó{ÜÿHñæ€QêâýÀ–$ðË#_dÌ‚¿tƒB`Ö÷*±Ãë cÓ§ë[¿1õtD!±~gûjQS!вæ¦l[Su¸ÏÚÄÄ‹T[›ý-ÛsûFMòä .(èØÖ0ìÉ£Gïôòöç¨C v emò¬æû#ƒmH€H€O Ùp})%“'àySS^_*ÄÎ=³ÿ=!/ôXSЧ,øÙ˜<·u;´{,¨m›r¸§>›îžk±cSöCY$@$@$ÐR 4‹ Š·•Ñú.<Û^ë½oŠ#v‰œÔXå[ïȧ#Ô5žúÖ«KŽWÞy:~¹ÞéÏkë˜a¥ÆÊgÞ¯“À¨[K7eè8ÙM–0#/>ÿŸ¨|7R "  }4«\÷¾hªÿ4DZ—`Af›}FÓÀ Xê^èÿÛ뮉·ùŽ'®j[ëg°{ ü%#A‡uìàŠÝŽž‚WøŸ‹r³ŸðÚyÇúÖÓõãõåÉÒÇúȃ?èD(ALa.EÌäûaåLBÓÌÞÆ.uC¢åéóA9y­¹‡úÏó[VøÜ3µùÅBÈ©á½^ÜOü¬¯›á<ßXYlO$@$@$Ÿ@VßøM÷¯äò{Bÿ4ü¾K ùë8ÆHržH9&­.å;='ÿBÛ¶ŸCËjvVQêŽCl—°ùÄY¢ÿL(1Ö kÊ!û[¯¶¾¢eÖÕ/ÆáÊ4Ÿ™ô"üî–>c0v§;»gžêJñŸƒ¹EËjM:ÌqÕ X±öïè|ž· YîD©¶åzƒ¯\ŠrÄ›N£òÝ`rl@$@$@ &Ðì ¸a¿á¡÷“ý)§ÀâöjCG Ít—aÈ;ŒvÿûŠa«+êjošþ/ _J_ÄðZY³®’êêV­Äã:?U@‰—•S¹Ï6×õ­W[_Ñ}×%oP ï|D}X­ãÏ ŽþÒ‹É«ÝÀì}„';!Z^…]9Ó0|£áŸÏó–C +Ó+ ãZ|?~lðUKñ±©ügd¥;‰TÔà± ü: \£¼dxÙÚ+F©Ë i €Ò°/x¥Ôš¶ÂÊ÷€O´êѤ;½ÖšQ……ÖÈú%ÃVå½ÚÊ e¾ÓƒÙÙ{)¿w…:|o-«g=Ý&^_ •7al‹n§ÏÖq¥.÷‰¤7¼²ôœ¼¸’E¹c>ÁPkõ÷Úðøë$ è6©»c¢¦ÃÏÕ½ÖŠ7\Nþ”•~ïic2+±e; @sh–xj»þ£œ”—”Lou˜,¯¼jæ‰P@»AOB|“°\#]ãÝ#Žê±ô„´Ï±ÝòîÚÄÕ»lÈøY­!¿š–éRéŠ#¢…Ô·^t›ÚÎë’gYó’Ö…V @Œä‘5å`CÄè5–i«¸.»#;xxÈUé)¿iwQͺ|ß2 ûÖ¸ò ¼©ò'?œwÖœ‡÷ز^uÄ÷ÝÁ"è˜ê~núüKFßV§BŽÈÊ´Z&,^5  \÷®ûŠae?à<ìâåAi¨:…Ó…ø|ovœé]Þ°Ûœ^ØI†’m•t·F2pRßzÑmj;¯KÞZ{u?X¿ß~Ȧ©H4.ø_ðó¾R~p/+-‹p>búС•xÉŒ@žX×uš3µtRZú¦y·êU…ûŸôÛjsϪrH€H€H€šƒÀsAiŽ‹««¸T¯ÓVd¯ÜOŽÈnt^~}ëyõë:Ö.Ϲ Ž$ÕÜO2³ƒ8ްR[×z[KÉÏïŒë·¬E™9Áú…~€[2¹—Õ5–“ ? ø¸|˜ŠŸµ¥ˆ¾ÇgXS¡ò+ ò8=–Œìàõ­;§.œ6|xYmõ[Á³„íß:;8óã§èzñäš4©í¶¡s»Ýoö$ Ê)8ÅUö éóÿîÁì1õ™YY›Q§Ú,°èoìæ;¯Ãzôx$   ŸmOòµGè6ubn/‡òýž0¤¥#èMoð°þÉÒ­å'ê,^=]æØj¬#*Óõym)º^'϶FoGU ï¨dÐä‡AËãJAšîÍÏúY ·üFè'½è,M®.ýwðî—48=CõlkX¤Pš*ë3ÃgJSf Ežmš á*K¶»ÿ¹êg㮳ô*Ü]t>¹.{]èîò<ꌋ ï}X̕讇ñ÷ßdÃ#_7¼¿¥„i)•&Rha¤Øð´a¾1VÃGDÊäׯ¥{qíÄA¿ÞîaG“Uªì¾œ”=pV|öƒb‡Ù—8œ”ÝÎà"4™|š\-\\™7“aM¶÷®8xØã§4z7y3x¼ýð$¹˜a²ì½ë ÿ.΢É4=çü1¿å¼F_]ô¶ãè¬WkŸÖÛÏK^hb\> ƒø9‡9–¿…ܤ}dau(ŒP±Ié&%Õ?rö£Þ?üÔFçIÒjý·A–y¿Á—ÜiP­ô2ê_ÆÁ—ÃN×z™„¯sáýäßWø9ŠÃ£/—µÊÈAµl>Á^&«‹°—Utü YíI'fßaHƒ@—gZEr ¢c‰Ï4LQJ ·F€È(¦|CHWðÇPá„K¨ ±²ô4‰ÏD~΃˰I€F); ?gõz¿¿:l('7õ¢a?ú+l(ô²ÝÞi2]¶`"N¦Eùú ÀIáâ¸?;¿Æ”ºÖ8k®!¥R6UH›+hU©0‘ñÂ6@û˰wôú•Ú9åOjR#—53–¹RUˆ›ÌDÃJ 1,ú&êÝ‚cņ -Q]niPx?éGYAÆC…'IURk@sc3²0øAåƒ$Ë1£(|Å>yeì•;.£uí>N£8¾mÊTñz`׿ÍþyÐ-Îï…¶]a™Å´]}tQ®Œ¡|u?ÇivJa¾FùŸÎå4;î$$p—ƒúW'ð¿ÐäÉßǧ0õ³“øø”Ÿö"xíœi?̈7šýÿü Å:N¡Ü?Ӱˤ<>KðçÞœÄW!¼æ@6(7ù •/ƒ´{œ}–ŠÀ_JYþ¢Œ{aLä/†¸Îó"ÜæE„È‹H’ÚîEѼˆÒPĵ¥½ »JƒøøÏŽû ‹Îγ°wt€¨È“'ù}õ©òŽ;§Ìûðf{Ï£„~Bù“ç{ÆS·A&šäg¨&Ñ 6áøG€êzÍò$((³RŒ‘>Q’p£™âVsíˆ$ÿT5L®•P ·H1Ä•SÑ‚a¶´-†‚’,ìª2 µ‹bbZ2 ZRIš¸%JXÖšaØí 3ª^¸•¼ª}N$—Êhi©&¬ôØ›%7q>¤&: &j튑ÓÞ ˜ÉÓüû“ÕÒ×ðd8šBµÞð4ˆ”x‰q!¸´6˜sO@ª“Viå3)H^wÌ„~Æ×au‚Y!š²¶-,mÀèü†ê÷¹¥¸A¿ѪôZ*Ñ@°c'ð­QØF~—CXRn'? 5ÓÆšJh,2þQ¥š%4\-ÔÝIÒ^˜ÝèªÜEÒÓóÈ­e>¨–îlA­Ù†§,õ(žV”q¢Üh.×E©¼/­LØ&»•’%)%¥õÔâ÷@š4ÚÕUaÌH…žr†2§ÂtÃÓ,8¥c¨Í¸?£¦«Œ ÔŽw×a_¼^˜Ý$é§cwÝé$ÔíE¸ý&nWI–¥K4`’¦Öϱ_‚Ža8'<×%ˆ@PúvPb‚"(!(­(†ñéf?L¯£NXC¦¡¡@Øj÷ŠRU‹Çm’2ãKÂ-ʼnQ† Z#™ï¾˜Oµg>¶@ækÇŠ÷Äs¤¾L/´vÍgÆbI,Ð'™[£ÌÌX,‘†0a Ø„)±HÆ{ö ;oš†Õµ)$=$½Kzz‘œWå7-뜧ù¬œ§(rÞ}qžnËy’‰¥Z{ÈkÈkÈkÈk³òšjÏkS˜Å¹p®úÚÿë0è¶u3­Y)ÃÂ¥»™5³OlÓà~ÝÅ `;ûìfÆ0 nݸöf)QÊËñA¼^[±o㜺j¶ÚŒ6™ÎÛiZzº¨Iwzž/¥â§AÜÏc5(:cW §ÎïQÜn$£n…^ó[e³²tØV’kº×œ’<‡ÆÏ‰ò© ša-SÃ]îõxnpg ÷w ±¸¿ƒN¹h Ið0LJ–¹¿£@Å”V扌‹>·Ä Šk]k¸Öeˆ^ǵ®5ñUÀ.¤³Gá®ÀIgRùÊ2K9‘RP°å؆'äÐyOø+‡†ÒÙÚљ­ܺAbCb[ ;Í.Ô'Áß¹(-8¡T˜:¯1…nxkè†gˆD7<ä<ä<ä¼Õç<±PÎÔל1ÃÌSa ÚrÆ–[K7¼An9j(PŒ¡Ö°v‘ V@-£¥¢D´‹50—bhä5ä5äµÖ¼&—¸çf¹KG¢†9„Q„1+,ÛZ_Ë=·$f‚¨¢¥IKÇ‘ÉG¦C¦C¦kÉt|醵=àᆆÒÒÙ}Ò]vŒ6:B> :ÓÖ®±¿ˆ”’“‰vþ"Â2J$0"!¶¥¿ˆi¡®wІ22[[f3KÏ>‚;lƒÙ fÁì#HgHg÷™}„.;ûH•Î0H{-ÏJ±ƒ´1H99oõ9,›óàqø”JŒ–çI4áÖÒ„[ç½6 3XÁ- ¥Úm» /„Q+m¹í¦%TÓLIÅÙÙÙÚ1›ÅÓRî<-Å,ÿ´=ïi)µóNÜ)ÛÄ‚Úo •\H­·wž–"µf, µÕà`f<ó°”Á¢¤àx…r®m¹(IˆâÜB3Ô´\Ÿ”¨¬<Åð´”…ž–RËÞ½èÓR2ÖÇ–Œj^d!ôÝ¡OÂjN4¨§€mÌj_q­-—‚X 9 «%¥mÜf›V[pêJ@enÀÌS3 òcÖÕ6XçV‹ÀºP`ÕËV¹H•SJæk)­ËVA¸BÏ|@B#êœë 5Ÿ „ÆÕ×9ÅÜg—6è}³C#j¨5>*­‘!4.éò¡‘Ï­5Zã K/•=Ãj—šZi_S%g‚fNH§jZ‚¦b¨GšÂj]7Ç™šZ»Ú7¿–ñPh<ú¡âªD\]äýÖ8›Wµó­PTPN-Ó𬎫|*2 í«WP@ƒšÙV›º®Âª4««ë« au¡ê*_>¬ÒE.rΫFú.}¯²ÒpÊ,H$ÔfÒ§V¡-ÓÞeÓ˜q‰Ô*sÃ|¥¡whOóXÕÕ‚E`k‰w ¬µ´ÒËV27°Jé[c$ # 0çm`mJÈ×X+};PG`}ç,!®Î¥° ÄÕ…*¬vé¸JÌ"ט²¾‹3 \ ¡øTd¬ŸÈ h¡D$—Ý d|Zåy<­pß ÷­¬¾J–‹«Ó#Ç*v¥¥Î2À¤R ­,À(7ÚEŽ5¥ QÌwTK¤¦ H›Y¶R‘c_:]ùC\¸ZLj®<^Ü~ÆX3n "÷&5Ù̸5á›–º0 ë~º |6Nþ9¥lÕâ¹÷¶¼½à"ì_ðø¾×§b0®>”œh6ùþ÷côEXDô1Ζ9wîÌ—åe‚ ORâ3àD ³qû_¢‰;…p'± ìdäÎEDC#w"w"w"w®wzï>zûiruÃÔ{‘FݳbГtór8¶{&Rª]¨‚‹ J9šÔ5"µÔÜe„ŽˆÔj$Ò©D"E"E"E"]"ua¤–ÒubÓ‰m-‰­#±­¶s9òòòZ#¯É¥óZ-ÌÏúYó~8í¬ä<ä<ä¼ÈyOD¸+À˜Ée ¦ï'®Z›‚’Â(ó¥ pã@φ %1– cÙÖ3– Ñ®Žvléhgæû­ŸFÓ¾KÁ%á±·r@}«@}‚7`Ñ@ÝšsçÔÊ =W‘RSL¦€ÉÖ(ù²“)Üâo]Íy¢Ä”¢¿õw]§'è.†îb¸fîbè.ö0ÜŚ«,j†ÿ~O¥YYYYtUÂ1lé‡kRNGÜaÁ–øJYf4%–k09sui©ä2Ê*0~¥ã"œŠ„ýœ´¾ßÞÿžZlþP©ÅæFêÛh~Vv7­Cª“o#)Ãrß½.%ÎÓv;ØŽ£³^ÓRTc²ºïLßÎÝð4 N:‡˜ìþŒ>š†î§}Fø9ÎêšåM’~: «ò¶Ï ³³ oÇÖ€LuH)ws *>ÉS÷Îó>±áV2LçǸT<Tû¤h©¨|©¾ü™ü®Qße¿f °4-¾å7T¿Ï(íÄ °o1«}ÆçŠÁf íy8;êsÃ4˜nÄesÞ\ÙÜÑ‘3_íöÖœs»á1öØ•¢ÁÖX¥(ßk¡åavE•"aš–<ܦYëlÔ´ñ8šý¤¹EÀÚP‰sŸeBè”4Óòv™+~dù beoÜ'»}o{T½p+ƒ}9-|Í´’ÂåÞWÆñµÂej…««çñ‘žGÍ@ϳvV&ëöv=JèÝF÷Aô¿·aN7Q„[ªô™ÌÕ8ÐÚš`IúeÓY5{Œ$[ÇH2øÚ0’ ½êqm½êW«ž-2’ŒÚá!£0Kv’ ¼fF—ïü¹¥ÜhË­¨ÓçHk÷Ekíí'¹Ü<]S‘â䪻:kHH‚H‚H‚ù"I°1ÿÕÜáÔhÜ= pjiB×××V„×(†L§ð¡[·–ÄÜA„µ°½† BÙ&ðŽDŒÄ8ÀZà~eaÚ[Räà‚ãk x¸ò7䢞çm©è܇Ö7ySƒ¸©ÇÎϯˆˆ!"äBR-!åÂŽŸ§Mɦ??¾"B"B>Ì\bAeæÙ9´Äc7¼Ê¡òfàT-éöX1—ÄR €TgŒ*»ÅmcòÚ+Ž™ O(ž>|=í³¡š¿“\\^e¡·—tÃûOvSŠœ“ ïkjý|–IC¸áÌæ@J]ÌvóȳÝä™é‘C0ÛÍ f»ñè8ß͇7Û{.ªt=Â}â;‡q²ÅÄꤿ ÖÅkq¦£BZ‰éoªHCpú¿FOÓØnÓÁvq‰+n]iÖ…°Z‹I—Óß,*ý •øJP“„ŸG hA˜ƲAö Z’°¨%­“–Ô¼W¨&aú—ùŸÛLHΆ¹Ða(ÞõáM”uνMï5Þ ®ƒ(N¢8ʾxpåÜ{ùáà¸Mc uo¨Q¥ÁéiÔñ®@7{pÇç[‡É´}Ø {A%½¥ªiSêJ £îìUêS¾Øà·ÏA«‚œBÂ%b˜ Q qð¾Gðº´ÀÉ6oÞÊ/iÔ-bÒø«_=¼ :0†qCï@ß”Av2q†»ÐÿIÒÆêc§¾Ê6\rÓwµ&Å›uÚAé^pÙ?Jf(?¹ûü®®¢nØ“t>…ãŠðQ(ý!êG'0œÒMî ºœ…cÑjöañÜËÂt†â¿…áåQrØ £{Œ¼Eƒ/a:éy¼_aNwãwÑî ä^pÑ`QçÝx´^|þÕ9Œ£NØŸm¢ðæÎf§Mp-(%Ó§øv/º²°6*jˆ»Ú‰ÒNò6꽌úYEŠ©h'eC÷rBÈñ2@¥|œóU﬘s4–n’Õã\ çü&ìœ ƹrG>ÖÆY1ãí¤~‹,]F3•/  ñÓ4é© ÛÐy¥Eù>HÏáyXðxLü±T¼Mº(ªù¨_¾éQ£ŒPµIÉ&QÕ?Jù#ÓÞ?Ü“«õ•7TÕáå·A–‡7Þo 7ð|ª÷’,œý–Þ¥zé!7§•†)3sã›^À÷ô©?CY'ÎÓgïðE’eÉÅÛ …LæCÁ‹hØÞiœYY¬‡ï-‹ð²=¼NÒ诤Ä0î¨W™xõŽ:@šÝÆŽ^lgu_ÿÿþ‘’ð_Äì¾N¶Å¿_ü{÷ðÕùöù»÷DÒ“¿Æ³ø¿ûî=ýãüäõ‡x®ï¼’GGì×?ÿø¸G¶w?¾Ú>üÊüü9¾Ùùåöῳê[C•ÛÚßs#-tCóƒ—Æö£~²ˆú(YM}Ô‘F*Ÿ´ÒgZJ¥‰Z)6<#À3ôI´ðëCpñ ¼r ¤ã!Ì„§ô-s*%—Ëh¾Œåùñ®:™¢ƒ„A÷]/þr7¼$7¥=¬[T(ZQ#¬Ã'ZçP}8в:î½ë _RZ5.@!χá´èª·Ýq¹lgÐôòÊNpnO-ë¾ïE^…[ü–»ß_õ²(½í±˜UéüõÀ²žŽê;Wi O"’u½Aeõù2è°ýßàtº 7ÒRM"r8±'$ßç¢. G3S^v[¥õ“gYzn•®\ņ®€¯è$(\žD\¯—¢n»yºÃ88y:ñ®ZH²ªB Yú <+¡W)Mrî@-­O,±ŒYA@;±ÆeÔSßXE-§Js©TÃú#I.ªŠ©/y¾, ¨‘œ0Í+ƒru>` ÷‘£Áw¸A–~˜¥»B™Rúó–Õpgòzƒ =òÿ@ó±,ÿneutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-overview.graffle0000664000175000017500000001146713553660046027257 0ustar zuulzuul00000000000000‹í][SÛȶ~~…N^7ˆ¾_²3ÙÅ%3a†&äLŠªSÂF;BòH2„™Ê?«%cÉ’llc‚†*lÔµZ½¾uíÕ¯þóí"t.ý$ âèçØE/?êÄÝ êýüâãñ/êÅ^¯½úŸÝƒã?ß8ý0H3çðãöþÞŽóbcss«ßýÍÍÝã]çpïèØ>67ß¼á¼8ϲþËÍÍ««+×3µÜN|a*¦›‡IÜ÷“ìz:Û€n7ë¾€Û½ ®vƒNözí§W_ýë×[,¸ô÷½k?Ù‹ºþ·W›æ*Qæ÷üä5zµyóõ¦ Ü;èxtù©èyÔÈKÏ|ùéUš%ðȯa„n|½$ôÝøökâ…¾xµ9¬R©…v ‚á!1Q•*¯6oº.†0Èâ­îi6ºu– üÍ›òm¯óÕÜ2êÂíúçAgTmøìÃj¦FzS6Æ?ÿ u}_wþá$9q&™âlÝ‘Š¸Ji —T˜ðïßÇžÅt¶zi³ß£8 ÊÕšìí–õo&œT羨v”]‡~YóæqŠBè2þZ–VŠ‹òÝÄ»*‡UìýAu80Ù£YºùV~)¦7õ¿ÄñÅ ‹eÇ‹.½ô zA¹Lnæ9Ÿæê[Î[Äáà"Ú ƒ^Ôè7ûÏkõ½t1ªŸø^øšÂË¿ÜT…Ì¢Ýõ2¿>‚0ÛÀx#Ë—”¼dÜù‚ŸÆèL'qRoÿÎË2çwxéÞ•—xõF»Aڽ룎6î 4 ?NçÂùÙ}¯wðKúÇ×ýFcè ^7_`»qgpáGY@GI39\˜©Á‚_i5Ê©%ÜÕŠŽPB0Î׬• W°æBS-´¤9q Wh¢1Eœ3¬5!ëQ.eÐHp¦µÀãÔÕF^%}{}¿À†“GÙ^tO¦XLå«//*\U Ë78v« õTîÛBt}?:ò¢tãÈ¿Nã°[ë&§öào¿Ú4_Îå²»YEÊ—Í$’¢ê;¯ùYuG/û§É$úSŠ[!©1©gAN›Óú¤Õ(ªœV‹‡³€\©~1ýðʼܴ쵶T!LÊ–´¥eÒÚRc«÷Äc-Ç^}}¤ç^7¾š6Û~àrÞ¶Æþ|stË*«cÿ'šQÅ‘˜ôÐÓ&š!‰Ž§¶lŸhJ\—?'úæá’ÈO>xÝ`Ž=cÞJU[Wi¦F>Çþ·lòJ/-iä$ÉÎð rÿéô{ “N܉=SÄ+þI§ð?“hퟓ3@…ì4<9C'gQŸs/Iý 9 `ø÷w¨ß1oüû$ñ»„ó“^âû‘ùr|øÌ -®c¸¥¹Ž1Ö'}/éžd߸@ð®åB™BXþ¡ù 4¯Bu^…±¼ Gyhm>Ϋ UL¿AyÙ ñ“¿:æBôÎ3?:ó:À'ÑÚšyÂÓ“³ ç¤sFU/ƒ®Ÿœ¬m'A·ç¿Mö˜÷À£¸-%iq¥€÷é‚ò•Lh¦H•÷pÉÈucË{fá=r^ÞC%мG †¸V°ÄݸÀ³óÍ¥fø%hNªÙy‚† +"5¢ÊòË{,÷ü$þ•†÷ÀsbŠsÃ@¬¾óÀúÕê ë;à°^èœúU°Ô`)ÎæÓw @ŒTJ¬¥å9–çXž3ÏÙ3@”ä–Ç{PyÚÌm09.Æ #Pm4ˆŠ‚?RösÚ …ð-Æá¡…(Ëú¹ÈÍÚ$ä’öª³9BôSdsHRN£œ ‚)•JÏ ¿PŸ@[àWTC7xvÝJ2,áR1%Ab#Ìò9ËçVˆÏ™û¬`t„ Udt ”ú¹OýJ®;Š»ˆj*4W­T«~E…Õ¯îŦSimzÖ¦gùŽÕ¯V‚í¤™u–©TaM\iœF°RA·’T%ª™‹0—Œƒ| ÏÊ”á9C _"O ²Úš™@°Eí° 7U?øióóó«[8ˬ EÍ  ZÊù@=ØiR´Ó"Èz‡¾o…ÕEúžwÆb«ªï¿…A©»ƒS×?˼S€“!N™?7—&ÊYJи{Úaæ*N¾ž˜ l¶zp³ž—Mšbº>i7irüÝ-I’ƒ’ªhÆ+„%[w".ÇZ€ô )S˜ IXºF‘F•Š×ú5Î~¤<VÞúᥟoV\1AkóK›÷9 æsîI'lÁ͇5„ÐRdsBæ´~”7âË”Bï+çÒ–®évåì‘¿êtóÖ÷º“‡P¥ª)AÃÊcO3!jwVÊe¨…rc¸t›ŠIL¹6¦CdÀ˜ ëÁ•ÆNNAWS0ô{mö­¹P!fØ•”ZP]˜VwÓ`šfÅ#¿ äbJ®à„°™ñˆ3ŒaÞFœ)†žšÓdnxØÜÓ[ô6³œ·’$nµ¬ 6À>0õÎõXõ3/LóÍe% äjö´õ}#7yA8ßH>Ýš¶ž?*™UÙ„ÎMÉ W¹»Ú5¢§ïì${°£œ¹”" ä¬:÷·~.h·Ún(‹vË@»IyýsW#\L#ª±ÔëNÓwŸëo˜º e€¬1R0dÐôš¾ûÐßv8Ìm¢ŠÌ¡Æ…ñ »:Z\¬€Ù¥R„-”-Éœ^…²åƒy³Wß­Â:!–ç„pϯa’¼Ðy_˜ €ÔăÉt Ÿe:'@•UŒ ¬Ç!€“4y† 8þqÆ© 8¶Ç–Y^´¼h÷íΡóÞ»ðÓ¾×¹WÎcl ˆ¹ÈÄg ã7ÀXZÆóC´;ûíÎ~Ëx,ãYÆóÎϼ®—y'k‡IÜñÓtnÞóÌ\ ÷áZÀ‹ºqÜҺᬰ®ëZx®…ey -4.×U31aT»bH Ѓ€‰2Þšhf4ª¡.ÄJ´–Ä5Èä‰5Ü”·¨Ìáñµ*6Bå±+·ÀúØb÷„Zf8‹†K vi]`œªK6vÏ"ãsDÆÆ6ÌÍÒ¶)÷Édx¤I^Ù“Ìð¹g+X} É™ dm"'çX£¯5ú>pÚʆDˆ®nڼ̴”ç™÷• ؆0Q.ÒØÎÊ]l2w6bNá*RÀM˜6v1µîâRA‚E‡€½(-C^£‚0çHlTÐã‰r2?˜•Í¿˜ª§š_âÙF‚ZgÜãôjšÇs.®‚¬sîlõü(›¾ÜÌúaüМ¿Ñ‚nãFgk–b>B YšÄR7NÝ⟬8w)g”pÁˆ&Ôdý%XºÆ‚Ï¥€5J¨~žÞÍBcùY@IÄÈ|Ê›ÑN3¤¥žK‹ZÀM)GÐRZ;ÓR-ðL[ ü2¡Žª… Ž? Ô¼¹Æ~N, MŒPâ‚@ ²%'’,,ÔY¨³Pg¡®á¼% A|(©NºT!€5…LBjü‡ÌU ™ó„ B¶Pg¡ÎB…ºû‰S™ºÛC-+`!+sÐ.5”…ª‰¸¨p~ä­2ÄÈ$›k5ÆOj½@°œ ˰a6`MZ`]fÀšZH†äøÔåF(®É\ß aeH+CZò.{‘o ð&Ç€QÙô¤c!„‚«˜iŠ!ðÊÇ<ÖÍÈĺí­ÛÞºí­Û~U²`ü8wý œjè*–“ÙZÕðá‡sC6Tz Œ !{~‘=¿È2*˨V(kÆJ0«¦ µî"\)±1O,¤Únìù‘{º{VÓdy•åUv³Ï²7ûX¿ÅLÛ8ÕRó5Y¿…Í×d]÷—zÍ¢ÝÝÐnáH¿¶c2ši ,ÚY/í³÷ÒþP^ZŽ]³K€I%rmÂì⮤ŒÙðÔ{’òÝ·NcÆìIÊö$å•8Iùy½ÜRÙ8KƒPm~7 {Ӕ邤¢ ŸcÍ›‡)cbS®c }¼‡)?ââE6³Sêbã€B@y€×´qææ¾` ú»`J’á;ik9¼£ÎO¨5þ@ÓáÓ2NsMPRˆDf(ÎåÑUuÎÒ<8Ü-œžÌÓñÈáqpé'—uÖÅçŽDÕBÇ/,eQ]†¥‰$'Š" åU»…!Ms`û¥,Œ]“‰`„r»-AØ%FpÃ&@D†g#ôðX­8µH89^jˆOíD95)§~| 7[CT…Å‚7ÈcRù"|š/ÀFøØkd´>ásÑd~Þ“ÞK|O)å½üšÝ*F^æ¨ô¨ïu` £; Ù£«Æ]oåÒºqþ'­ÍGü»6ÀÂg_¥¦UY½ŠÚ‘×Oãê—OŸ?Õt“t?î|õG [¤RûS§%w˜p›\:ôzþˆ¸ÚÅ•‚3]@Å`뙟ÌPýwßïÇG¯2Š‘ÈTˆKÞµŸ”wÉÁ5IÂ<ømúRQó½wÑâAËoãàfõCøwVÓÊQtüt¶a|ðÖn'-pm<È&/ñ­(¸ð2¿±€jú£)íI'~D»AšÕÈ¡z]Y÷ÈŠ÷² ŽjÕ‘‹jõÃ|˜o¢ˆ¼±tã¬1Ã2c# ïûQ¯Â Ü÷neutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-flowew2.svg0000664000175000017500000012247413553660046026170 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 18:12:04 +0000Canvas 1Layer 1Open vSwitch - Provider NetworksNetwork Traffic Flow - East/West Scenario 2Provider network 1VLAN 101, 203.0.113.0/24Provider network 2VLAN 102, 192.0.2.0/24Physical Network InfrastructureSwitchRouterVLAN 101VLAN 102(11)(14)(12)(13)Provider networkAggregateCompute NodeInstance 1Linux Bridgeqbr(1)(3)(2) OVS Provider Bridgebr-providerOVS Integration Bridgebr-int(4)(5)(6)(19)(7)(18)Instance 2Linux Bridgeqbr(24)(22)(21)(23)(20)(10)(15)(8)(17)(9)(16) neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-flowns1.svg0000664000175000017500000006403113553660046025754 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 18:01:59 +0000Canvas 1Layer 1Physical Network InfrastructureLinux Bridge - Provider NetworksNetwork Traffic Flow - North/South ScenarioProvider network 1VLAN 101, 203.0.113.0/24Compute NodeSwitchRouter(12)InstanceLinux Bridgebrq(1)(3)(2)(4)(5)VLAN 101(8)(9)(11)(6)(7)(10)Provider networkAggregate neutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-flowew1.graffle0000664000175000017500000001173513553660046026773 0ustar zuulzuul00000000000000‹í]msÓȲþ ¿B—O÷ÖI”yá°œ ]Ø’“¸K¥ê–b+‰Šdd…Ýâ¿ß–d[Ö‹;؉´©ÂŽ4oͼxûfÇy²¹µµÝë…þÖÖË×ÎÞÛ7‡”±µõêýçÉYšöžnm]^^º^–ÊíÄçYÂþÖ^÷ü$½z …mB·›vŸ@5E长ÀÕnÐIŸ?~ôì‹õ|»“ßü·Þ•Ÿ¼‰ºþ÷g[ÙU¸D©ê'Ïɳ­áÏa¨;èx)ù±(y”ÉK/ûñèY?Mà‘ŸC Ýø< N“ø¢çî¯ßïä$ôÕ³­A’±ÔTY—h>Sš23–äÙÖ°è¢ i¼ÝýÏE?U&þÖðþ ¯ó%«2êBu½³ 3J6xöA²,ExoÔŒ¿ÿ&ù±áü-%4HK©4‘B #ņ£ s±.m(“?~Tž%+l'ôúÍrâ0(TËòæe™~Øál¼ï‹déUè—)‡SÜ„"ã/åݱÛÅý—‰wY6«lØûÝñæ@gziø«üQtoßÿÇç3 –/úæõw“à4(‡É°ŸónËyŽ8¼8¶Ãà4j”O›åç©z^Š¥O|/|ÎaŒå?†IálоôR¿ÞF¨Ø¤t“‡ê§œ=ÒùO£uY!qRÏÿÎKSçxéÞ¥—xõL/ƒ~/ô®:^بæ$|œÎ¹ó‹3ú]/à× ô¯zÌ̃zÚ|€½Œ;ç~”Ö'è¨#Y£'³ŸaHË„®Ž´ÚÌ›:R¹Ê2K9‘RPkÛp”¤®UFB¹æ ®e“‹— %¤’ &“µŠn8ÍkÕÙÕ6½Êùuæõü¶ 6èÄ8JßD'ñäùƒ©|µÕûE‚ã±›£j‰k” @¦?mOo¥¶B´eH&d0VðZ†r–Ž=_ËäÞíùÑõ7üóà8»µbrT þòdzæÓF¦O¥²:Z•ƒJ[^¬"é;ï4òÓñ×5T&CÁ£1Èo…¾ÆË; ÂpÚ»«¿ÜJŠ–×;è #Í%'ÜŽ>j¬cZßô(+£œIÞ’:,HãrtðÝ9󒾟§ÿüé;ÙÛƒ ÿ:éSåuN˜óß”ýÏëÄ™›Ó•@RRº”JŒ–«$ÒÙ½¤3†t†t†t†tv‡tf—Éf™rÆ s•ÖTq g¸¤Èf÷‘Í$¹¿l&¸” ؉kcçd3B犡fNb“Ú£¹UL ±!±!±Í©§‘%2›‘fÑVæÖ{£‰¡–TÓî%±…jªiÈfÈfwÉf|™l¦ä†_®`’Z©,V¤³{Hg’I¤3¤3¤3¤³;¤33?›Mà“·Aäÿ›LÀã×~øÍOƒŽ7+g/øµïu'7a¦§¨ƒÄ•§™à¹4óTìÅpé:&П**(§–iaIæ…£” W43‚PjûQë¼ ™P™X†š¡’ ©æçˆæz¨­;«MòV»Uœ]qsc9œ·“¤Ò½¥Ô&1½õO½ÎU%ù‰ös‡Ê2Ìäq'´iã{Ç^Î×’OA·F¯ù£²YÙ*œ{&×Ý–n8“'¬›/Å‚17F™+- "¥Â0Мf„ÆWYæµl34 0Å…P¡¡ñ^@cC]44N6X˜T £[xµ ¦¦Q—ªŠE_X\ªÆ¥j´† 5dEŒûÍ!× ýuÌ^‚ÐÏ͘C¸p •ZhÃy¶þBhÃ"˜ž”› —jΘa PIX³á¥]b$gšhF…˜˜Ù€h91Æ(M†v ^< ŒxF©¥“N)w¡N¨™R–@ŠÊ*À‘ƒ¾b@ñ5¢²‚Ê Úqª¬hË~ÂûˆÔUîJWQó{éê*k¡ÇHÃàeø¢†qÍg†NIõ,'dl #5;t A¤! 7#M¦PA5Õ˜yÕ±H¥º?’F‡Û‡âp{/=”ÖÖÖÖ‘Öä"i šïÚÌ´C„‘X yíÁ𚸗ËN þ= û¦› TÊoåÄfµe•‰ ‰ ‰mNbSK'6J\Æ£0WÖFs4RÞK#%hFJd=d=d½µd=Îׯžè¥;[vSg‹º7µ2ׇLvZЃOÖÂB‰> 7ñY0cOŠŒbùÈHnŒŒCAc!Ð:ؘâ\ÓIÈÈì`mÆ8ȨLZÈ­”[ÙöFLÌÝR7âê:è ¬7Ö;s£F¸Ùº¸Ò‚J…–­ótÃiqQÌå ärPp€´©±z¥ -­/®¼i_ßˈ’ܼE¦£O;n†®2'›· ‹W™—¬ÑƱJ6Ëë”&JÙª™8v?8o20JòóhœIÐ=õ}*Šö'›€•“Œ0åHØ ½ŽŸ2Ó‚¸Uù,*˜›qhæLM¥&³ÔYT«6¶É¢”!‹.€E ²(²(²(²è °hN£{Iü-èúÉ*rhoж;&R*¥k0)±:@ú3ëþèѶNmZ¡G®í#cãÚþЬíÛÅF 1WKiAË3F?™IÌÆ­«Æ¢Þ­hçHlwElf~b[nRöz›o}'Œ/ºÍ·4ˆ4ˆ4ˆ48 šeQŠ ÞÃpìÖ²„̶̆̆"ÌÆ:oß’gÒTZX˜ó¶Vs¹(ÞÀÁñ8®Ò®Žáž—¦~-É+qѾ†¸Š êÏ©IoŠTgÛGf;8R¥2·;BÞ_!!×!"äBrù‡Oi²ÈÝ–gGÈ›à+ª` Êºªhqg*‚º•­ÉA†ºÆX­ˆÕ”(còSfë)³ TY†ÇŽÂ@t±E{.ºØ®‚98÷âû ºÖ~]­ØËÅ$⬯ÍrÕ$Î[q]WâÄØ$N$N$ÎU Î7Q?õ¢Žï°;fªzl$þ©oÜ/ núx}ƒ(Óip dKtZy·!½äƒiê—”@wØ{ï(™À“iÖÖÖV‚Ö–¾?òÚá5†aæÄ†Ä¶Ķô×»À£‰òš(9x. rrrÞêsžÀÈÆë\Yë+NKðJ§|‘ÇÒ4®±™¥±ÍÜõÃðX<–f=Ò†tŒ×ù9d”dÙÈ(˜^d¼SÖÍü¹&¤¡øDd.­Dl8œR×N2šétÐqR涪¹UîØfmF؇죹äÐ £šW¼2ÉgS-¨}$‚éB#|,C|¼Žo–.9‚¨°Èc»(Ón¶õ¤\ e…’óÛÕˆqœëØ®ë¤VŒ†ÄhÈ5ÅʆEðníªÏS˜ixl×m®{ êÀ 4cPÛµžÇvÙ*íâ±]·È¢÷@EEÅc»ÖçØ®Mæ*+¹VCXNEfgÂ%”S`LfQs¿(¡6¶H¦ ÒÚL˜CEêpÖ†I÷ý~N^·çSU˜ƒÌ^U…h¯ªÜø#Žï—v&Š0m4ŸŠæ5Â4–’†¶Ò~ ¾¦f ¹`Í*MUÜê ç™÷ã4—škF«QlºIw˜}ìQ —3áJ,‰eVmme yâÉOX>ïÔªz“²Wšç`A˜Õä&¿!WK üøvû½C ½žaöý†ý7ÆtIdâ¹Ëd¾EèYmð P¾™µ: pªdæsc§] FY£`xmŒ‚޹¨O¢c9²ì3%¼¦ðLÉûw¦¤Ï”DDD\GäË Produced by OmniGraffle 6.6.1 2016-09-20 23:42:33 +0000Canvas 1Layer 1Compute NodeLinux Bridge - Provider NetworksComponents and ConnectivityProvider network 1VLAN 101Instance 1Linux Bridge 1brqDHCP Namespace 1qdhcpMetadataProcessvethvethtapeth0iptablesPorttaptapPorttapPortSub-Interface 2.101Instance 2Linux Bridge 2brqDHCP Namespace 2qdhcpMetadataProcessvethvethtapeth0iptablesPorttaptapPorttapPortSub-Interface 2.102Interface 2VLAN 101VLAN 102Provider network 2VLAN 102Provider networkAggregatePhysical Network InfrastructureInternet neutron-12.1.1/doc/source/admin/figures/scenario-classic-mt-compute2.svg0000664000175000017500000007254013553660046026246 0ustar zuulzuul00000000000000
VLAN network
[Not supported by viewer]
Instance
[Not supported by viewer]
Compute Node Components
[Not supported by viewer]
eth0
[Not supported by viewer]
Instance
[Not supported by viewer]
eth0
[Not supported by viewer]
Macvtap
[Not supported by viewer]
Interface 2
(unnumbered)
[Not supported by viewer]
VLAN Sub
Interface
[Not supported by viewer]
Macvtap
[Not supported by viewer]
Macvtap
[Not supported by viewer]
Instance
[Not supported by viewer]
eth0
[Not supported by viewer]
eth0
[Not supported by viewer]
VLAN Sub
Interface
[Not supported by viewer]
VLANs
[Not supported by viewer]
Project Network 1
192.168.1.0/24
[Not supported by viewer]
Project Network 2
192.168.2.0/24
[Not supported by viewer]
neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-flowew1.svg0000664000175000017500000007240213553660046025750 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 18:02:59 +0000Canvas 1Layer 1Linux Bridge - Provider NetworksNetwork Traffic Flow - East/West Scenario 1Provider network 1VLAN 101, 203.0.113.0/24Compute Node 1Instance 1Linux Bridgebrqveth(1)(3)(2)(4)VLAN 101Compute Node 2Instance 2Linux Bridgebrqveth(12)(10)(11)(9)VLAN 101Physical Network InfrastructureSwitch(8)(7)(6)(5)Provider networkAggregate neutron-12.1.1/doc/source/admin/figures/bgp-dynamic-routing-example2.svg0000664000175000017500000007563313553660046026252 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6 2016-08-10 22:15:47 +0000Canvas 1Layer 1BGP Dynamic RoutingExample with floating IP addressesSelf-service network 110.0.1.0/24External networksL3 AgentPeering network192.0.2.0/30BGP AgentExternalNetworksSelf-service network 210.0.2.0/24Provider RouterProviderNetworkRouter 1203.0.113.110.0.1.1Router 2203.0.113.210.0.2.1Router 3203.0.113.310.0.3.1Provider network203.0.113.0/24Self-service network 310.0.3.0/24Prefix Advertisements10.0.1.0/24 next-hop 203.0.113.110.0.2.0/24 next-hop 203.0.113.2203.0.113.101/32 next-hop 203.0.113.1203.0.113.102/32 next-hop 203.0.113.2Self-serviceNetwork 1Self-serviceNetwork 2Self-serviceNetwork 3Instance 1203.0.113.101Instance 2203.0.113.102Instance 3203.0.113.103Address scopePeeringSessionAS 1234Peering SessionAS 4321 neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-compconn1.graffle0000664000175000017500000001514313553660046027761 0ustar zuulzuul00000000000000‹í]kSÛHºþœü |:§Dß/³Ùl’Ùd7!,0ÉÙ)ªN [€6ÆrdÉN忟·å›,ÉÆ¼LM V·Ô’ºŸçí÷úò¯ßÏ{Þe˜ £¸ÿ—Ô'/¼°ß‰»Qÿô//~;üuÓ¼øë«ç/ÿëͧÃí½õ½h˜z{¿½þð~Ç{±¹µµ=ô­­7‡o¼½ï=8ÇÖÖÛÝÞ‹³4ü²µuuu宕߉Ï]ÃáÖ^Â$ýñN¶ ünÚ}—}n8ðm7꤯ž?{ù5üñj»“F—á‡àG˜¼ïwÃï/·Ü·p0ê§ái˜¼"/·&¿NºÀµ£NÂ)?Î<í$Ià~yör˜&p˯`„~|ÞN“øbà‚ßþ–''½P½Ü7ɵ¦ÊúŒÀð™Ò”™\“—[“S†p‘ÆÛÝ_ Óé¥Óä"Üšt¾ºKö»p¹ÁYÔ™6ßû¸™k1œ›ã?ȆG~nxH ÒR*M¤ÐÂH±áiÃ|c¬†¯ˆ6”ÉŸ?çîÅl§ Ëç=ˆ{Ñl@….ïßÌÚO8Ë?ûQ³ƒôG/œµœÜÎè œ2þ:;š;<:þ& ®fÚ l÷S~8ð°§OiòÛì—ÑㆿÇñù “e'è_ÃOItͦÉä9g9ÿ–³qï⼿݋Nû¥óÓòù³Öƒ §˜¶O ÷ŠÃË~™4…?ܤ}¤aq(ŒP±Ié&%Õ¿pö‹ÞŸü”FçN'Åþƒ4õþ/=¸ ’ ØéM4ô‚ Wº2¬Iøñ:çÞ_¼éïÅüõÂÃRçÖA±m6ÁÞÄ‹ó°ŸèôA²Ò“O̡Ê=?Ó +'·tU°d”fT[«8vÃ£ÖøRR!—F«ÝÚðö9›_jpœ;8½ñ%UFš¹+Ž;œ.è@¬b¶ªCRÝAR¼Àlçî¯> ÂþAÐzg8”=§Yl¡™é‚›»FßfÓÐ0>q£¦¢þËŸhv&€çù3ïÅÃ(Í-€éP‰Ï©à†Z&™µÔÂDÍ ~Ôy?N3Àȯ¼g á¹tÇ“î¹[ᕾ†Ë2W&Ò²¹ÇVùÒ8‰z½eS­ó‹p¾<†gA7¿.=wœš9wñ¡  0†ù£3Ô:JÒz3=Êþé N×ÙQ'îÄ;$ˆý1¼8†¿…&Ïÿ8:E“÷ŽNÈÑI?‚ÏÎY Ôx“uóçŸÐ¬ãÚýù( »LÊ£Ó$ ûî—ãÞEŸÙJÙèå$;@9ƒîGƒ é¥ß¥"ð/4Ê>”q0²CÜçYn³&BdM$Éš@o÷¡hÖ°™dç’~^$Aïè[Ç}‘F§giØ? :ÀpäùóìΆTyG潇©zGÏ?ØÞý¹@b=ç/Òet9)cUnìó áF) ÿ¸IÇ-Ô—†Ãú%„+aĈ\’ ’ kŠ\”ªI.Â(I¸5øÅ jLmr!ËÉ))¢ÅqxÑï‡=ïý›å<áö~Ø œbHèo,·ãྠÓ×D7”s·€£\%n3>ÕZÁÖ_ãhannFtC›¢Vw/Ã`!çv2–XÂnnnnB7€JvþÇÜûÀ†Æ§R ›&©•®¯‰Ì£¹¥Jd©³ÍPJHÀ¡‰µ FZþ®AvZƒ8NU5ÄJm…¨A ÖXÁÅídó <Žã^wU*Ñu©Dª**ùœöÃ4ÿº¦ÚÙg‹uêÏr¶“fÀ¼ørçZT¼Þ)Kÿ[ZyšªzÓÓ®RP*%™þÈŠžIeO!`•ˆÜjžë9÷îësÏë°éYÕ\+Jj7¡Ÿ?iÁ$jö¸èÊZ âíòžÕš3Á•\ôrów?¹¹¤&ûA7º˜§Ø’øõ`ØuŠ 5hÖ]wt@ˆÑFÚ@³Ç9¢Ýƒûøy‰°Ž‰ˆpE­’Ô¸c6 {-Ø=0E)1Ns‚Ìö™MŠÇÈlÚ0N5 "kC­a«ã­bš äQ£ö‰Xo)ÓR Ë2´HlHlHl­#6)}K¤¤N-_[$¶GIl·l¸eCfCf{ŒÌ6UF*kˆ6sÊP¦})åk¡¬0™íQ2Å-nÙØØ#±1îsk´‘†h©ž ÄgFj¥á~à+¢±që¼?žºÇ6k°â-k°b’¶¥NR7çSX×ÉCÂz¢$(-„´Jé;sXÂZc d•†¢Ãúm·Õ\ajˆZR¾|^Í„J€lžÛK«k˜ð){¼p>vx¡fäðbm»^>ï¾÷(¡ 8¹€\î3y/^->É{µ(à[ÉÇ^-À´†jEi60퓼¯‹rÎþ…露ËäÒyö.Ã4ꫲeuiGت=XvCåû܉’N¯B(¸?bûÀ®uD²òþ£ ˜¾‰iÃúD¹H9ι[‚pC2êÃRð£Ä”3ӆїEN¾/ASXRµâÑ w=BÉ\¸ù¿è¶‹0f"ÂÑÄ'ÂtÓ48¡c,͸&_-;‰Ÿ.äüðúaz'_Üq'“P—ªÁ¥§`â^©‚} ±„ •©ŠÔ"L‚Ç ÔÄî—"(!(µ”ÂÞÉæ0L.£NXB¦•6JëB%¡•`Ùˆ¥†âgh!w‡“)Es;YÏ–dn²×™¨IqƒÓÐGr_ ØÜ!\ ‡ÅÈ)õ ¥„QC@¤°2 ·‘ÊW–ÁNˆH)¨µŒ•-ÜD …û¾,ܪ~¸lÐÂý@üº2 ¥Ô( ³U©zêÁàÖ…ŒÖTÏhÑ­ØS ÍßhþFówmó÷Ñó,òÚ„oÒ}¹Š•ñ]&€ E³Â>P ¼ªBÚzj"â‰RÓC7 !=>Z•òÐ O0éS«ŒÐ–JX¶€€¸æ«f™ý8±€Ç0ë•©ü ôJ˜0†3MÆÈ å᦬.çiçìèyzÑ_3½1¥| ,T¦ˆÐ\!»=JvãÈnÈnÈnÈn-b7¤Úì¶€`\ü^Û½¦Þ…A·vŒa1)͸ñÜÝ,¨®µ27Ø nØ‹á«ë¨Káƒ8á«[ŵ.É? Ò‚J…–‹,XEu° •éqoE=6º0KÚ#¥$.,ÐzI{„e”Ha!¶fÒ ’!uW§üÀuóy;IâJu8©’¡>„§AçÇ\ó“ 7̪þ=ˇâÂ_Nð ,Q¯ÞH¾DÝÍf·ÊVe ¸`ýpaÚÈRFh\`X_4š›B£04«1©ˆÕ”(Ë]øž5{„(£`/µ«zæ+­©âøÚñT¡±Õ~o7‚F‰ÐØ 4–L½ë€FÝ(4^n³ÞŒ)ß5¡ˆQ@Å7¼¢b‹)„F„ÆÇ I9 ì|ë€FÕ$42‹,«p«˜àÌH³%g¾ ï7L3b…vZ{"|_k” j¢Tב8Ké!Ù“„Õvk™Uo$pòõ¢ê#·>ׄhEµt%¥Ë4Z1ù¦YIB#}綯¬4 Y˜ ÎÅú.9e*àGÏȬcì1ZÇ2Û'Yž¾{‰¯£˜õd5}g=Ñ:†Ö1tw¬eûôùÀWC|DÝÓðè¾Ç7¤b4´ãds‰GÊlìõ‚Nxæa}A拆ª*b˜÷Óó–úÉÅÒi.9á‹BÜ–‘ £°>øÊŒ¢ÕnŸY©©[êÆv³uðÈñzo> ÚS轉ޛHrHr-òÞ¼÷M) gpöc6¦ƒ$¾Œºa²îpÀÊbH¹Žr%R.R.R.R.Rn‰r×D¹•ÊÝÇ’À )w9å2Tî¢rII¸í‰Ò†-^¶H×îe)ɽ,«¯ñu¬z”0W5%ÄBoA}Í3Lækø“ô”lwEfô”¼‰§¤Dÿóf‘‘­ÅͺK[­:Q‹•½1j±­’>âáðP#6ЇëU†*^—¢b9^̓15SóP‘Q6&íò[<”m¹rÃ+Äd™XË7X¢DÀ²¥°r©d’)‹A2·’ ƒd0H•±$Ó’ §=M‚4ŠûmŒ”Y’Ýî®"e ;w¥r¡¦Óô׆šÂŽRqC‘D› Q‰$Š$Š$Š$ÚÍXtoìÄÓF ½ÎÁèŽx”¹”¦Ú+¨åBp*6<˜I¾äŽ/…sWÒ2sJâÚ1lƒD¥……°…F­{yT›4݇wÝCÓ({©©áÒ”%-•5\š²\¥òvt¿¶J[RTÕqzŸÚ:nª´£{ñ0rboi¨€Ö° %gÊi-)aó ×z?N3‘¹ ‚šåŽ[®atÏÝIvÎ9°“°ÂÆ,csLÞ„pr 5潪HoW½…dXƒaQI›ì€lÎðó‡í]^Ï.ûa&ýe¸ 3ºŠ œhî3™©#aãT…ÿ_âË]ççÚ„î.}%7ÚÈ1}%ÚÈѴГûV– ’ÿIÙ$å+j±ŒD5ÖíÕXmA¨ÆB5ª±Ú ÆÚ/Ò0ñvƒóp8:-Ra}K²¡Ý·úŠi$KãR£IÉ…ÒÎaÝdê+'t*b Ϧ1T_M½Økh¯FÎë5´W™Ïºm«öŠÙ¦´W¬®öJ1êR;ïN”½+õÓõU?øã)+ãø„ÉéH¤viã.Ãô¬ýº¸e<[PíØEÕãË©a0]̃®¯+Ì£Ì ÞnHÇ 1nˆ1R}qÒo—Ç ffåï‹,%‹²¸M»ô£†ûÎÂÌ,/3 'âÉefiw)5¤>¤>¤¾U2pó&)°"Ôìæˆ¼´Á“ ób>Pd9d¹6å]bvzS̪‚O¸à.›Õ†WÞõ!½=JzHoHoHoHo-¢·%µÚ½UÓšç½q4Ï¡yé é­-:ʉ­Òï‰íiú ±!±!±µÊï$^³ßI©JVzŒ~'Âhô;A¿¤>¤¾T¨IÁ|Y¬TÃ(|éâ9À¾aB+ÜÚ=Æ­qk‡[;ä7ä·6ð[ °èÝòpçb7Ÿ¶J~Ó´U¥u’]´cªÜZÔÎZ`Í;¬y÷(òWÍÅXãévÀhîoœÏ¯Ê|Ƭö×Úr)ˆ•BÚ•óù¹l€“ ¡°¼á,BÒ§™Ï¯Ýº,„Æ@c)¡ñ–иþr ìæÐ(ˆo ™£V«z_Wf…FªÐÈ›, ZŒ¸[2Þ¸0(µD<# ƒÆDd¤’ø Ö¡³\+£ ·”>HJ”-,7VÉŽ¥o]N[b `¬Ð+¬XVËŠ>T\ &“.æãrk§¢ ¯KVXeV¸b¾cO Ó…«1™t>Œ“Ic2i4ta2i,,ú0 ‹–²ZÚJùµ‚E÷f‘E)C½=‹R‹,Š,Š,Š,Ú–Ê¢‡ý~Øk#ƒ. ,¿#ÕÊ—\ ÎN[ኈ n} I+ª-Ü.3ŽA5÷ÝwFY¸W!-Õ±s1uê*dÈRó3uÇT]¿À]o.1÷~˯}§_t˯‰½!bo¥>YY½á)t½ÿú¥ôÆFýÒæýú¤X¾tU¬uA:R)å ÄÖÃZ¥á Re•ÖºÖ*j\¹u#©åh{jÔöDåzmú‹7’Ìykk­`¡Yæ3},T " sKÇác)ÚvoUÏh•/Ö¬_S™¬í^tÚ¯ZC¤jÁµ·¨Ö©­ž¤Á1ÈYcÎý3ùjq!,FÆi’ø2ꆉ²ÖUœ|=é/”·} ; Òp¹öbMõ­¸ðÉ\Ö… O’qP <JŠ,û9Õ>ÉÔ•K$[øNÞi"†;àJU©äE{ô(éôÂölÒGÙ—R1ʹ6¶fEvBçNCMÍâìRáf‹bh—¾òF¶ ˜½I¦0¢}V V0P"ÜûeD‹,S·X öÉW‚¦©J°‚éš•`9… *¬•ÆÊkW‚%Ë·ÕXÞõFó`}××wý¼ûÞ£„6PâÕ—Î r÷^+SHc…×'‘ŽE*LÇ‚éXЇéXÚ“ió&IYjÔÿ‘즫2mfHvãÙí±›@—äBäBäÂÄ…c‡Îhǽp¸æ¼›E*´¸Ï{Œ9§±¢rrr[kÒn¶ r‚¦g¤A’5Ò×ðò­±ÃôeĹ P5¯aÍb ˜Fk$Z#lÊ)MMk¤¬m}dh}|j ³ÐøØbãã%W–Gð}&×fzÄì‡Í'†Mæø*Y€f?Äì‡k”Ø Rvµ¦ØŠ†#&J†Ì‚ØöP2qó,ˆ7I~±d˜:ûI“•«ÞÊ•;od „Њ¢šVë’ÕD¡-?­¡1 «c£ ŠÒÖçz`x”6ŠìõN¹ &æêÉ[GÓ0™\],Ì^8Q€¶„Ã:²mh£À¬uLÃ4èipô|/‰;á°I;5“AC Æe §hƒE6¯ÓùßÎÞ€´¼"@"@>P{Áº ÊÕ«’d¤O¸åÊJÃ)sJÖ«$Y†j—&"ô8ª]Pí‚jT»´@íœ{ñ½u•¾'÷\Ö¡hWºDœV™ªz®Ê…‘Zf• ÄYYþ‰‰‰‰óîK( Ó ß ï»€Ÿ0=´QÙ,­Ê.žñ”µ˜]üö±kLPÌ.ŽÙÅ[™]Ü£ÓüâŸ?lïzÔûï‹~œž†Ýÿ¹—<ãTh'*5·iƒ‰ÆWò€3øÏèEÏ2) ¦•r¹ \-e WVCÌ.i0ÑxS‰Æ+ 5ÂÏÊZ¦„±L³Qþé$”/n»ú¹B±o&2,‹ŸæãÜn(ÞåÁU”vμMï ìlÃä2ê„ÞîHÕ9ùµv;ñù îÃFgèý.üÙï‡4ºŒÒ÷$~ø4]Î §l¶ÎÀKYß}É5¡°eYøÈ.˜€rÊ AŠÁÒ¬M•f]g•Ñ<|>RÝÞÍ@‚zÀJ}÷Ÿƒ$Ït)ðeܽo °“ï‚íuù)ˆj!É¢´A”çBúN”W’(*9rÜà8N‘ããããÖÈqã½àú8nfÏÎò·$êæùbú*§GAÆ0½ÂØsÓ7ófgì ðï8©ì>u¸, p¤n¯†®×¬yµúiÔº †‡ñ ígwŸÝÕEÔ ‡âÎ×pÚ±>r­?GÃèx†ô .“¹éî§átiUû—ŽXæ‘†É Íÿ†ƒÃø äF1õæyò?Âdvå©§EAªq7~ݾÔÒ†Uxþ»Ëx´Ü|þ\UsxЋ:ápµa|ŽÂ«kO»h‚Ãhã‹tñßîGçA–&PAâŽv¢¤Œúo¢aZXùúœ³¶!¬÷`.'æTc_h߈ù¶šÏÇ9K7NKƒqÞ±óÝþöOs~Ó¢z·†0Îòe@gýk `i­Ô~~±Á¿.Z=åÅ6ö+ªÑ~«çà,Ì9#&þtU|Œ»ÑIÔÉFý&÷¦''e„ªMb7™öÿ…©_(óþäž\éZÙ‰rku|øc¦gá•÷׃+x>ÅŽ»q®~KŸ’}î!WgE´†)³òÉ6½†÷ôu¸B[·œ/œÝƒ×qšÆçƒF0›9¯ñùNzqÎ/ëñï‚Î/ád®ð.N¢ÿÄý ãŽú…‰W¾PH³[y¡×ÛéºïþÞû×JÂÿ}Ýûýý»x[üóõ?ß¼=Û>ûô‘ôøËß{Óø»ûé7úûÙñ»Ï½m8¾óV²¿ûýË.Ù~ÿåíöÁ7hóë÷ÞÕÎß–ÿCx’Þæñ8TYvþ=Ø &s ]qúÑGåù£a¼ˆ®1·…ªºFÉÞ3 X²ÒgZJ¥‰Z)6<#`ól,È ’h"àÏk†à&â~x äV¬Ž‡0öзΩpÖqúy,Ï^ħ~èPd ²ÝOýÞëáe?¾šó3Y"ú@Ó‚9b>“:Çâ#ðÀa”–‘p'è_Ã9¡eÔãòlNЉzÛÎz²Š¤—uv€óH«Ù÷·~ôí"œ)–ÜýoÃðm?’eýóªBç—¨;ëŨ¾s‘$ð$²'Y–Ù+Dö·ßAÛÿqN¦›ñðXs“Ì!UΙNó vM™K •rÃÓ¼`Òžîæt[/Óä"Üš;¾Ñ›1t©¼¢ã wx —?>¬¶ìæaÕô‚ãw¡[ÞÅ’,ŠPc–ÞOçÐk–ÐßWó?t\¦UJÙžûÑ×zþ;ZñÄ~ã󢤚OÆ>mô9€Íð°;*»+VÓ³ÊóϾäyż–"f%à™}z°Ò^=ÿ¾‡t`Êneutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-flowns1.graffle0000664000175000017500000001047713553660046026570 0ustar zuulzuul00000000000000‹í]mSÛȲþœü Ý|º·ˆyÉÉæì&»8˜$wSTݶ0:’#Ë!ìVþûí‘lËz±±ÁãTÅFêFÓÏÓÓÝšyõ¯¡óÝOúAýò»è…ãGí¸DÝ_^|<þuS½ø×ëç¯þk÷`çøÏ÷N/ ú©søñÍÞûçÅæÖÖv¯ú[[»Ç»ÎáÞûÖ±ulm½Ýá¼8OÓÞË­­ËËK×3Rn;¾0‚ý­Ã$îùIzµ•mB·“v^ÀeòÚKÍ£ ¾~þìÕWÿêõv; ¾û{Þ•Ÿ¼:þW[æ(œ ¢ÔïúÉkôjkôsT®´½ªü”×<.ä%‰g~<{ÕO¸å×ÐB7¾ˆ‚nzîüú-ñÎÎB_¼ÚŠLHc¡]‚ ùDHLÔ„È«­QÕyi¼ÝùÏ ŸŽ/&ktþ×þj.uàr½ó =ÞûPÌHôGçÆÍøûo´á ŸÎßœCƒ$çB"Î$Sœm8RW)-á’ þógé^Le;¡×¯×ۊàhP¥ÈûÝB~Ôád²ïs±Vzú…äèvò“Peüµ8;q:?¿›x—E³Š†íL6:{ÜK£_ż{ûþ—8¾˜c°ìxÑw¯Ý &£~κyò)g%âppm‡A7ªÕëõgÒ­ž×†*Æò‰ï…¯)Œ±ìÇHþ0ƒv×KýjSÂlãMŒ,_Rò’qç>µÖ™Jâ¤Zþƒ—¦ÎðнK/ñª…vƒ~/ô®Zm/¬]t>NûÂùÅÿ®VðkúÇW½Zaô *› °Ý¸=¸ð£´ª ãŽ$µž̾Á….´ŠæL¨‡› T,™VRj¬6J¥K QôZ5F¹4†3íiRŸBνžß¤@£NJýi½ÜŸo[ Òa|Y=òûÁ_~ƒt¥ï£³xºnÁ@,†Eù|.p:qr|=ä2ª8*¡á¨@wJ$±”M’æ”0**W(T|âá çG-/êWJgH”uTQ"S55V¹Ò5ªW D‰K:݈rµ¾> ÂpVWWqnÐÕ»¡îu&ÇÅRë® órê®örE*m(Áêäs@å§ÐTW¡ä'Iz†O`XÙí^hœ´ãvì™S ±üþàþf=ÿûä FXzžœ¡“³(€ïö¹—ôý9£AöÏŸ Ö6zrÿx]à˜Éç56´ŸMŸ=›˜ß3B)Á× q%âó¹Js©™a#J2Ô47@i%‚Á¥U˨7~»ó¦¡V>Ü £nØÑ¹¡ñÏÝѹBÍ.ÙÜÑ™fq]|¦vôÂ|cÃõŽMÑ10ÜÞ&Í Ñôþ‡cÙ—Pæ‹–}À¥™Õ™c™G™”6_g"bhÎv‚$òÒAâ…'ßÚÓìÛÓ܆Í,ÜÿÆè~^ç빯1PTh»B0Íç”ÉÇEkCËl^Ë ²x-³Ãèúóš|Œ¼†üS’säC³bÏÂ[Î0 q¦Bzn¼eLKÉ8¥›«cj‰ÍÛ›¹n~‚åÌFZ;f“K%6Å\ãj’Q„1“¼ÎlÂRÛ#¥6ý(§lÏ©d©ÍRÛS 6±rj³ÎȧáŒÔÌ:#­3Ò›uF®…3/NlS¨e/ˆüuS¿ó½Îô&LöŒðÑP¸t7Sòãæ”OUc8t'`­\–¥$ Ö½–,-]A¥Ô”3¤9ãúg¥÷Æ¥9w5â#`ì1©¯/½(çÜwdú6@ËƒŽ‚#ªZ!)Øvl<)ZŒ†”RðLé5@k†óv’”z·°ˆšl§=¿ëµ¯Jâg^ØÏÒv !ÐäÉTÇYã{4V½4õ“hÇ^ÎÛöY,Õ,¬ÑUì†mrJ b©–Š€{+Œ”dŒGÁBÈjiõ$rÍ5"o‘jµ9#{]¸B)hÃZcëy¨Þµ¸wDZïˆõŽXïˆõެƒwD/Óë_å5BlªÖg߀×MÕ²©Z–Øl<{=˜MY·ÿ:¸ýñMZÒL*f˜bM$ÓˆX·¿uûß¹OkÕðÝ S¡×ìVɼìp#_–´îþe"£F«GFtcwÿ ö×àªu÷[wÿ#…F%ïËÍO¨r±¢L`,¹ÀTn8XP—mœtXòÌÂè yê‹Ôäê§ð¥dZÇð¥dÊÆ×t•¦šrdŒb.:èiô‡q?H'jšL¾×0ùVœ2E–¬œ|Ÿ>ŠÓla« HLYF¬vǣⷒ¯Ÿ…¤kX®kM•¨Ü®Ò³ÂUzÖwÝPê¡çœªì_ïBá[ø´·½ï`„g/Ôcú÷È=³ôá6Œé&Š0žO—ðl] ‡&XÑz?õx3\S&‡Š ¦ ÕŠÍë¬Gì!8ëMr‘tšd ÐK‘¼cW½Xœg–‚¶¯¥Ù×Ò¬ߺñWäÆçË P"\)±™Ú,¤Ú’Þ!½,HÿÄH/wgMÃÅkÝY¼Àb¾ ;«¸¤Mʲ¤gIoAÒc6v}‡kõ/d0uã2꫸€A¤Ë¹FœQÉ„fS4õ0õµ…ŸV|f] ÍÆgnŸ©ÙewŸ©.,(UÃl€>®|ÕÇ9Xx xl8[ÕZüÖâ·ÿƒ²øÉRW_’ÊåÙÎ ˜0.‰d Ħ ±Qí er1Ø šÕyRËk÷Åk‹§ðÕ®½4eg‹0tÖhc ûΆeAË‚é*ßF¤â6Á;½{H„ØéÞYb³Ä¶&‹Ú€ÎùgtØ2WتÅd¦®°uƒpÐSzqÍqÖ®¯u£ÀNÍaßI¼å;‰+[»öÚÈïŃַ[¿ð©„¼×;qÙ†¼o‚Œµü{ v+"Y`'ðÒ^À `‡A»ëlIkZs𡛃šƒKEH±r„äjY‹÷à¦/™{ñž9ðÕ¦@ÚȇjV£w˜YÕ*µá(î"ª©Ð\QL´ÊS kÉ’|ÃÄ¥‚˜ž¤VZ®UŒ¬ñ¡ãõOM¤uè Ë-¸@4+J’ˆ.JÚpmèÊ.½Pä 8wðÃy“®rßMëc–·ê4ùvýÚ‡¡×ö/üI8ƒlyíµ'#®ÙÞR1©±YK‘Èqê,·¤) ¥Ê›k–[ò@y“(Ë›–7-oZÞ\Þ|õS/jû÷LS0Ys3¦I5«»±úNCTM¾  ˜ ÀI’ËJ$FX)»ÓÐÃÙiˆ aßxtûÙ¸åÎG¿eóR߆«NÕ˜ÉîÇpP+F(¨¹"L⦹šÙ¿úÉU ƒÃMb µsµ[ÏÕDãJ·v®fçj–o,ßÜ5ßŃÔOîd¦öÄ’jSUì£A–ºmöÜ; Óh˜U ÌeÃþDO9«ßî0ô¸vbk“£@¦ÙïZ»p‚k©@N1Å´µßWd¿ k¿[ûÝÚïÖ~_û½u¤íóûŽ´4¬!Qñ')‘môÑôª”®9 ֌`ªñFZ–´áÑÌéC3Цw0žèŽ?Mí†Fk ¢g}2L¨Ú‰/zƒÔwöãÎ}‡¬±`0.Áš’JdÃSl8”J×äS"¤–Lq‘¥$#§øSß6ïaÄ»Wææ‚X¹»]ã¶Ã 5MùwÌ}pÜñÏRï@tˆÎæ¿Ñ¡0Šr=LâïAÇO˜_ÆÉWŸäy«b¼g܆Cu‘kV"C[„ÍFÛmþ†™À\R3kÕØ¬|Mw³5Cµ  Ìó×>¤kr¤I„„kaÖÈ®ãl ŒÕå:»§ M£µ:%&HÚ¡¿v‹b>¢µÃVk‹Ïm®.xo’Õ ]…$2^NÁ´€yç†ÃÜliCÉLi’9E Šik%=&+iqð"TX3Én®{3ÃgÖ’’ú›BÎfa íç¦Pÿd‘ŠÇÓaiç8ñÎ΂¶càj߇=ßjŃôÜiµýÈK‚ø^,©JŠ4ÂÆ>RÌ5aebŠ0Ìp¸²2€©r•–‚ ­¸”Šó§áöÓÔb‘uûY·ß}»ýϯ c¼p ³‰8hC%«ñÑÞ¬–߀0&ajütÇg[=¯ m_a˜ìàªr¼¥KÃ`ô⤱ø8G¡ÒÀ|2_öM©B¼Ù¸Í¥#¯×?Žç/î>»«0c/nõÇeBúSÐN |œr™,³åÐëúcmkNÉȱùw€ÚR?™CüßïÇ-.E+Æ 0yò‹wå'Å•ÇQý ›š¿ŽŠsÉ}ï¢aj]ÆÁuñCøsÞyI+ Ú~¾f| üËk«6À¡µ`¦LâÛQpá¥~mULs¶$íøCíý´¢“«û²- ÀKƒ8ªˆ#Uäì™o£nÕÛÒ‰ÓZcLfAlÒöü¨;‘c0^~p” ¡"´´ GQ~@õ‚¹äËÊMü:M{êÊ6ÌaY@¾ÚÓ:÷'_ò?ÖŠq'Ó5kõîÄ“UJ›m"–ãK„_ríüÃô\íZYEº:<ýÁKÓsÿÒùÌDïú§Zp?Nýùoé ÐK\Ì"k¨Ò0dæ®Ü`ÓxN_ûsÈuž®8û­7qšÆ¼ZPŒ‡‰d¢a}gaì¥eµþf¸¬ÂdåWx'Á_qä…Ðî ª ¼ú…ÚqÇï4^èÍvúWçÝï៟1òÿ÷Møåý»x›ýûͿ߷ޞoŸ|DŸ~þ=lwwàïÎÁGüåüôݧpÎï¼åÇÇä÷o_>ï£í÷Ÿßn·¾Ì¯?ÂËßf7&Z·éƒ*³ê?„HRé†êó¯Æúƒ~¼é±9®Qšx4]£æMY\s—HÎ…Dœ™Ð!Ûpî‚yBRUøóš&˜xäró´ã!Œ„#c®r(ǽUT_ÆòìAD¾A‘)6È‘ïu¢ðêzx9Š/KÁ¬¦ˆVÌÈœuhauÍGàã ­#áŽ}÷ú%£%/q6zÖ cEç¦Þv; ¾Ïcée…1`Þ4_°ìÇ(ø6ð‹éùŒ»ÿØ÷ßFiÌêöOóŸƒ&ÛÓQ}g$ÐYOÖmö“ýíž²ý_ÞÅ6]ÁÃCGRBª‰hEfk <©+½áHRIÀÏJ.•W0Sò·JçaÁÐ5xD§ÞÄé"|ò|9¿{Öֵ̓BïôoÔ»:CâUjÈÒG~·„^ãŽØ”rEå“ŹÙC*ÔÐ+_âø¢jº¼ri#ôɃ9p¿…4æYcý£ƒÔEM~Ê~©°jàR|÷BЦ×Ïÿ!d„aÝneutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-overview.graffle0000664000175000017500000001436313553660046027523 0ustar zuulzuul00000000000000‹í]isÛÆ²ýÿ <½4û’ëø–,;±ÙV,//)U½‚HˆÂ5D( (ÙIù¿¿€+š¤H‰¢Ú©X20ƒe0}NwOO÷“ÿ|¹ˆ½«0íEI÷§ÇÔ'½°ÛJÚQ·óÓãïÞ5ÿóôÑ“ÿyþöàýG/¼Ë8êeÞчg‡¯¼Ç»{{û——q¸·÷üýsïèðÕñ{®±·÷âÍcïñy–]þ¸·w}}í®•ßJ.\ÃÞÞQš\†iöõ.¶ üvÖ~ ·)®>õ8p´µ²§~xò9üút¿•EWáað5L_uÛá—'{î(œŒºYØ Ó§äÉÞð×a¸wÔ 2¸äÇâÊ£NAšî—žô²^ù)<¡Ÿ\t£Nšô/ý·ðÛ/ipv‡êÉÞ ÉDkª¬Ï<>Sš23ÑäÉÞðÒÅ#ô³d¿ýß~/Ý:KûáÞðü³ õÙݲۆÛ]žG­Q³Á»š¹½á¹ÑcüóÙñÈ·ï)á´”J)´0RìxÚ0ß«áц2ùíÛÔ»¸‹ÄA¯zÝã$ŽÆTêòêù¸ýpÀÙäØÍ޳¯q8n9|â$\2ù<>;qº8ÿ< ®Ç5~°7o'{4JÃ߯¿ÃÛ ÿL’‹9&ËAн zoÓ¨§ÉpœóažüÊy$î_t÷ã¨Ó­\ŸV¯Ÿ·>¾ Zp‰Qû4 â§æXþ˰)üÃMÚçA–…*v)ݥģúGÎ~Òû?•§sIÒrÿ×A–y¿ÁG®ƒ4(wzõ.ãàëq+ˆ+w™„?^ëÂûÉý^¾ÀÏQ¾ÿzYé€”ÛæìyÒê_„ݬ, £d•‘L̞Þži%É™Aá-WÔ*IµÂîx”rŸi˜¢”nÈ…KùÊ2K9‘RPkÛñ˜ñ¹PB*É@À¬UtZºêÄk,_çÁeX'`ƒALºÙ«îYÒ,0™ÆŸvú|Ñàzâäø NÝjBz&î[#to/ÃîqÐíí‡Ñi·K—É¥=ú;œìšOg=šÖS7+£Èøc ɧ€¤hú:ètÃlrGû‡fýaŠk!©2¨gQÏÓò Oµ(šœNžŒñµø¥„S.¨„ùÂ'ÆeسSÛÓXB(ã¦Ì1@õšžimOKÝV\µ¦VOõœúôåiÐ;ÚÉõ¬‘xv\ÎëæØ/Ž¿3ËÊØ¿Â »D‘Ñ:÷@ ¢)ÈñÌžõÍ™àJÚñŸÆ¾\Ú ÓwA;ê÷¦Þ1ïe&{OÊLI|Þ‡_²æ™<}v,#'ivFO@”£ü¯Öe´vÒJZIàN "Šôú§ðo¡É£NÎ²ÓøäŒœœu#øÙ:Ò^˜¯ ÿþí[îëA‡Ÿ¤a›IyÒIðë~9û!üÌO^§pKwœR½O.ƒ´}’}‘ŠÀßp,ÿ¡ŒûÁ˜Èâ~pž7á6o"DÞD’¼ ôv?Í›( MÜu£´dý4ˆOþj¹YÔ9ÏÂîYО$¹7<=9ëQå´Î˜÷üåÁ‘÷&¸{@Úá·ïi7`|B„OàW£` IJ5Ï­Þbâý‘ ϸ'_Œxh’"ñ ñ ñ|‡x^‡YвàäÑQš´Â^oaîi€ÿèÞü`úe_…YÔ æ…ggÓ5âóË0h7?Â$zϰ§Þ¦Á8Ÿ›!L C%pè{!¸õ9ˆ¸V@ö0S8šŒç;ÒT23J)ó­4z£Þ†æ^04(ô– 0S5ñ‰6šØ*&­Ö½K÷z3íKi‰\ e…a7£¯ïÃvÙÓäϹUÌÖ†q*¨¡œKZÃæÇlÅxô2Z*’¹1†^ c¸cðQÄw0Û Ã~š&µZ™»¢ÇaØ Z_§šŸq/÷8ŽLzifIÇÙƒ(^ìI>EíSç¯Êæ%¸áâ8 W‚ËèôDú$Š9[¸Íy“Œô ·\Yi8eÖî”zeà(ÖÀ ÙRtÇSÌwS°Ä\QwTêW Ô³mTꓪæJ½•ÎØ÷dó+õÆ >ÑS RJ=*õ {“ö;a7k񾂣}­Ð­ŠÔ ™^j»SI%˜e\×ê“MD58&Ae•@n½O+ *B¶•¨¤5D€BГ$*79#  Ú,HTÔ0èm 7HTHTHTËxŸ6‚¬8>µÊm)ð•ÀAÒ0_宬[±œäl#ºã1±B:вÒtkõ¢u<‰æïÂ^Î+óµ çÞ¼\dæ"'É.º!…ÜÀÿs§¾¥e®½dME5KÎÝÜÍq­žeÁ) ÑâÜ_ÃCMXtÖc¤@¢ã0>Ûí…éUÔ =Ð…®“ôól<úÂ\•Â鯵 QíSç„å@t„q¡ )_k Z0e\ZErç4$“š´2N»ž>&ÅmêÌ·â´o€’:—yþBÕ÷<ˆÒV\ƒ”w¶tºÑÞá ÃÉ”»UëL-óµ[ô]Ù¦5 ÖTgN”–—”1é$Ø‚†Á%ª7U#4ª¨Fl„q”&WQ;L‡*ĉkàÌýܬdá(àG»ðL"45„jQU*¨D¥¢ ,êþ*Ds Ÿ›K©å\;·Víô%Šs —¡ó»Ä´ fˆ6ÂÍÅP«X™V!¬O³¾%wÒHœ‡œ¨ï„ѨU¬B«¨U V±ZÅÛ«0ƒ¯¥‚ºmn+$wã«>¡RƒýÉ1¨U V€áS°e‰R’‚ÁC)ªed÷X­`\»SøÄJƒ‰«ÔÎ 0~ƒ„Q+Øü @JhÿK覙…ÕŠU©€[…¸-,7VIP”ñÝ‚;|iE³Âæ"\Ù¯6DeÓȈðÒ‹é ëܰ$9NúíÍäÚµÁ¡Â¸ÀŽ ì«[`÷ŽÎ¿Â ±÷¦Ð=€Ô4€Áì·à:«ÜiX õßñª îéVÃÓÚPôDë ¥Sß¾€ÎºiC‡1o9¨L-¬Ñš­Œ~Þ>]£ÊônHzî¾Å Q°cdÓXï@Q gC¯r+=óe9> îÜVFMoß܃"8¶B‚«î!Ýñ$0Ó%­â‚Ls›Ä£í64ß?='Rbç>C-Ó®Mdd˜;f.'˜.N¾¹ C×À0Êhk¥Úîx wÂDbn×§¶Z…éÈnuC¨6˜Ž Ó‘!ïàzÕÐλ¤¼sòh©0Ë TÈGÐÏ+5¼_]&®|ÖD> ÉÉÉÉgÈçYµ;áÚVŒ$çÎG"ã ã ã ãÜ_Æyh)— Y{ÊeË—M¹Ì)õö2j<)°úŽÇ­ò ¡§ÜhË­MI“©5¾p{g¤‚vÊj*‚@'FIèK¤1D5õÖ.BÃíänÉCXâb.áÞn§ ±Œ Ðñ0S.oxlæ\^"çru¿be+Ë ÆëÀJ¶9X))È+ô3ú7v®Û¾ÄïŽhA˜˜êABe±O¥sÓh"Øb óJÛbµ]h…^Y7å’@O8‰8yË:%[?NÒ;ÂI)}PA!ƒË­²£Vx¶t ÚÝÚ-X0¯²I£ü·2ÊŸoñ6¶Å€Ž7¹¿t㎠=ÂèÆÐþ[Û»öÀtøÄ?S-'+­®§¬ï”8NA,…jô@,£Ô£ZŽjù}-gøz äá*ØÊ=F/ Ó+Q’Rµã ^ºd”ZJ9®c-ðŸÑRJb˜•LÌ `•R CG‰t%d~E^«µœKêîN9BãJ¡±„„иñÎ\£–…Ʋ;Ö.èÌŪÊ "ãJÃÔú‘Q.ŒÃ5y.ˆÓ©p™Ë©øF±®B5WÍË\BûÂåæ.¥€Þ®^›«jÀ)€¥BÏ‹«ó 2â*âê}ÅUƒ¸ºR\5ëÇUqGÆøÀÉ)¥LÚ|×ʸº¦£)¦<šò‹F*ÔÊ)1¾ÕŠkE%#ÜhÚX]ùŽk‰H«¨±w—­`w™ÞÖêê6W<Æ`,ÁýÜ]v| Ó]¾ÅÕëÜ£š"²ªTEQ©ä>É·?1Ð9·Èj@`R)W1TÞ‡°ºBšˆ«+„x¸º\vÕ-³ââ¥Ô´@VDVDVDVÜVŒºý/ƒ×AÕ˜qGÎýRy^EÏíkWÌÓ`ñ1-|çZÔh·ixƒJp­‘Røê«sn)¬¿ô(–ÞBž¹ËêªwM/g=&õ¶†e¶Þ$íð®yEJÀ;ºhc)¹t£«Ñ$“;™¤–F £)ÅìSkñò­Î>Å)0Á˜æ fŸâ¦Ì1#ÅbÙ§ˆâ Ã\kj5ZBÈPh -b ý¥áuÇ+ÌxXË9•´˜ñð69G ääääœm̱ËX4níˆe‡œ—q°¤ÈºÇddddœ‡Â8˜!èAdbÌb`¬ŒÔ†É‚¶¸pɉ'”ÞñªµÑ‰w‹&ÛÞÀri ‘M*º`à„‹ƒáŒ€¸i³`à5L[ òòšT‹ÑN/ º-,\rK)°fÂ-Œ¿œ¹ÎºhBÊ|éŒ[k˜Ð´iŸç2¹ðpÿ<§9¦Ë‹¨¸~ó‘qùôûRøœM@×BRDFDFDÆZd´˜Yd¥Iíú3‹è•e@vÛ8ËuÕ kÌ€¼®"2"2ÞÓÔ  ¯W¤ƒÏBFeolM®„%Öi}ÕýzMȸŒ-ŽÈˆÈxOuF̼Zd\ÑxÎDF³*?£â;ž…CÔ!¢È5Â4WÍXWï'2VÖyo/\ݪÿÖ„µmĪÿâéÄV†›õ†+2¬ª-¬“1àú>®ïßq\ƒL'ßܸ2ºÊ¸2.}P¤±TÃtaJÕ:rÀ–âPX ·„o§ŒÀp÷(7˜sÀ!ÁaÛÃÍ÷Ђ<YÊÓ¤Véiš¿>« Òç ‡0©³Œ+á|ðÚw¾'©ÌQÆíÃŒÛ(,6š»Œ&‚-f¼)¥˜Äj»§¬‚›rI §F?ÓJ×&ËžôÀß ê*êí|P'ïê˜Q¾á–‚É%pi¢Æ™ïêhÁ¦£ ¡¡¡¡®Ö–‚:}WZö¿`͸š©zSá\IÄ0FàŒŠP‡P‡P‡PWŠ«0KA¤wuœRße(bÔ­á+]A0;(¼B¹Ñ–[!êêên°ŒUM…BÊ”ï*Iæ,'m•Å@‰Û ”`r{%6µpm ×–0xbÕÁ¨`ÏgVº³ì­O!‡:ö2ÁÈ Ý ›€vK{N)å>è6†¹$†[ãV´+aÞMhWÍ¢íªÍi_[—üØ:½T«Æ,e‹ÈT*ãOÕ€µ´èÉxðž &ïÊ“!©ï|…yt†ŒV;°©Ï\Ufîd×€$湄Ÿ¯²êr”•Œ‘K°hO~ÃaÓwa/ÇíûS¶’ ±ú²•ë¬ì¸N\æÚ‹´ûqÔéÖ ©“¸{Wö°žeÁ)˜©û×ý5<4£T!)ÌÔ×A7èä^·(Zxâš83–‚>ïþÛcbv\ä'ïª\N¿e2¬ ŸP©—8w ³€Kf O¸J `¼Ñ¢X¼öÉäNPå6 ”ŽI±qW«}6€KmM\÷BÕ÷<ˆÒV\ƒ¸'lÃÔĹÝ~«U´ÔnoC½X: SÙ:*@Ïú¹ÙX ö’a¸¡~É… £"ÁLr¦æb JsN(˜JZëÅ5Œc€ë8ZW+µ®8¦§»oI˜½cdÄ$LˆŒoäJªîãnÚÛ½=E±po÷p&1¬÷ˆõ1þ÷vo@øÍþÑ«æ,©åµªµŒ¼¶…¼Æ´E^C^C^C^Û^{}ȼ£¸ßÙº+å7êcµâD¹ÄZFVk=JáøM8JGc\ö.j} ;Jµ ŠjòÛ=â7i°¨$•D~C~Û~{S¡ÀÈ<ǦÜJ^®fzÌ—Ô*#4£VPtk>·&ÚËHHHaþ…½pâÉ£gýÞ í¿:‚«npG‚ÛN‚SHpHpHpHp@pÇ¿ž9nO1’ƒB‚¯a:¾óðJLè^ü{>ó¢å›à¢&’*¿G«ÍàŸó.±ÇQ+ìÍ÷£ðú»—mšàð´I?kžâûÝè"ÈÂÊ*­!¸³­(m%¯£îó¨—•ÄšÒüÏÛ‡`ôY”tK͉OJíãü1_t;€c•gi'Yåaœ×$q¨wv;¨4¼z…¬!Œ3&J7Ìû/ð€ÙurÍÕ~ZØà?7IOUØQ€ ´ïôŸ‡¡ƒÅÄIÅë¤*æžúùÄ—^”ªv‰ÝeÔ£æGA”Öû—¹Ê½ò MÈêàôë ËÎÃkï7P0ƒkŸrÇ7IÎÿJoÓlú©A».+¨­aÊÌ}q‡MÏà;}îÍÑÖ‰s³à¼9~–dYrñ:Há ÆóaBÏ\ï,N‚lZ¬¿ :-ÂcÈ*îð2I£¿“nÃsGÝÒīި•´Ãvížíg·_þÿñ‰’ðŸÅ¾z™ì‹ßŸýþêøÅùþùÛ`NŸ~ú5nuàßí·èŸç§/?Æûpþà…|ÿžýúןŸÞýWŸ^ìÿm~þ_ü2ûñóì&ÃãPeÖõ‚Ë0éšË?j¯õ’Ý@Ìq)›¥î•Àˆ¡®.­ô™–Ri"Á&14}#Š¢pˆhCÙ´ª^ón"¾ ¯€ÜÂé¸3áóü­s*¼O.×qùi,Ï?ÄÛnèP¤AyêýÛnüõûðò.¹žÚj5Cõ¦%5²`>Ö:ê#ðÀû(«"áAн zSJKÑã"H³ü1œ#µPõö[Yt5¦—wvÊ@Ðm… öýÐþê‡cÆÛè…/ºY”Îöó*Ÿ¢.صͨ~ÐOS‰|$«:{ÊþâËeÐaû¿bˆ'tº1|%éR–0•3ßóÝZŒêOs3½ 9² ¦Œž'YÚ÷¦Î¿ëÇcv®4€ÏsLœÛ“ç§M¼Y/w§/C'ÚeëH–Õ§C¿ ;SÈ5P?v@ÿpÎ݉?EeÂ|5ù‡»½2ªt¬fÌþL’‹²žêËÒƒ¹Fƒ¸_ùpÞ›Yú¡öú5­ä”{k ^ ÿ¼ŒA²ž>úFk;ž¢neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-flowns1.graffle0000664000175000017500000001352713553660046027461 0ustar zuulzuul00000000000000‹í]ûSÛX²þyæ¯ÐÍO÷Ö‚8ïÇl6[„d&™Í˜äîU·„-@aY„d§ò¿ß>’z,ÇÆ4Tac—äÓß×}NwŸ§ÿ|{ŸÂt%ƒ¿=¡>yâ…ƒ^Òç{òûñÏÛæÉߟýøô¿^¼Ý;þ×ÁKï*ކ™wðûó7¯÷¼'Û;;»WWq¸³óâø…wðæõѱmìì¼Üâ=¹È²«ŸvvnnnüÀ•ò{É¥+8Ü9H“«0;¼Æ¶¡‚ßÏúO ›¢õÊpàÓ~ÔËžýøÃÓá—g»½,ú¾ ¾„éëA?üütÇ} £Až‡é3òtgüv\úŽzAM¾+ZžT Ò4po~x:ÌR¸åg0B?¹Dçir}å¿…w¿¤ÁÙYª§;£"¥ÒTYŸ>Sš2S*òtgÜt1„ë,Ùíÿûz˜MºÎÒëpg|ýyÐûàºô¡»«‹¨7)6º÷Q1Wb8¾6ÆŸ’-|Ýòþ”¤¥TšH¡…‘bËÓ†ùÆX m(“_¿VîÅ5¶Ãf»GIMT«òúÅ´üø³ò³/Še_âpZr|;ÅEh2ù0½Zº\\‘7ÓaM¶ÿ¶<xØ“§4~7}S<ÞaøG’\Î1Yö‚Á§`ø6Σé4?çü1—¿å¼F__vãè|ÐhŸ6ÛÏK]=hbR> ƒø‡9–¿…ܤ}da}(ŒP±Mé6%Õ?qö“Þ_ü4FçIÒzýß‚,óþ_zp¤A½Ò‹hx_ŽzAÜèd~¼Þ¥÷7oò¾ÞÀÏQ¹jT@êeó ö"é]_†ƒ¬. “ÉOr41‡CZº:Ój’SK|n¤aŠRb¸5DF1å"@Џ‚?† '\BmyŒU¥§M|¦òs\…m4~HÙqø9kÖû×Ë£–ÒqrÓ,z£ÿ„-¥“Aözp–Ì–-˜ˆÓiQ½^8-]œô`ÇàטJ—£çí5¤ Tʶ i{!­j¦2^ºÃhx{Ž‚Á°V;‡¢üIMkä²f&2Wé£qÓ™hX4FEßDƒ[p¬ÜP©%ª«-…’a”•d`2Tx’„P%µ47F0#Kƒ/*&YŽeá+÷Ékc¯Ýq­÷qÅñmS¦Ž×³»ùm/‚~y~/µíË,§íú£«‰rm Õ«Sø9I³3zó5Êÿô®Î ÙI/é%»$ˆ(þ^ŸÂÿB“ÿ<9ƒ©ŸÆ'gäälÁkï"H‡aF¼ñìÿëW(Ösåþz’†}&åÉy†÷æ4¾á5¿²Y\ Üä$T>¹ ÒþIöY*)eù‹2î…1‘¿â^8Ï‹p›"/"I^j»Eó"JC×n”‚ì: â“=÷A_dáà,èQ‘ÌïkH•wÒ;cÞ»7»û%ôë ÊŸ>ßÃ0œr¸ 2цô ?#5‰ny´ ÇßT7kV'AIÙ˜—bŒô‰’„Í·škG$ù‡ ª `r­DA1Ü"Å4VNE†@ØÒv` J²°ëÊ0Ô.‹aˆéÈ0LhI%jâ–(aYg†a·3̸zéVò¨ö9‘\*£¥¥š°ÊcCnz”ÜÄùˆšhAMÔÚ5#§ý×Kâ&`&LóïONTK_ÓáDhjÕzËÓ Rjà%Æ…àÒÚ|`Ì=m¨RLZe`¤µÏ¤X"yÝI03úU ³¨Ì Ñ”uÅhai Fç7ԼϽ(íÅ- úˆV§×J‰‚€H¥Ûãš3K‹Etß}Ä |wV´k·Üîs°D¥’ •ó=¥vÑ…p£QÖÁ­ÔVˆëàÖXÁÅ·­ƒo…—Ñi÷W¦åJ·@ÏoÁ9°Lùëšlþÿ0Ûe㇒kÎ'×ý5Z´Ñ`×>k-å6 c”3Éç.-(à,°\k [ðÙ‚¹"‰"“:?=Mµ¾½fûSæLp%gKùî;ôCÙ ˜ GÕÕõ[\…îÊY‡=ÓÒ®À3þ?_ïòPë@|$4M „f0Â:5Úø‚Ij¥²Ü*«ò•¾ Ë€Zc>øŒd¾ûb>ÕùØ™¯+ÞÏIZø2 ¼PÐÚ5Ÿ‹%±@[œX@dn2sc±D„1`ÿ¦Ä2ïy8ƒì¢mÖצôô,ééer^ß´lržæórž¢Èy÷Åyº+çI&Vjí!¯!¯!¯!¯ÍËkª;¯Í`ç¹îkÿ¯Â ßÕÍ´a¥Œ WîfFPÔÜ>±mK€ |t'€íìK°›Â4L¸uãÆgt–¥D)¯ÆñfmžsèªÙz3Út:ï¦iåéN5¢6ÝéMxô¾TŠŸñ0Õü¡ìŒ]s€ž9¿ÇpDq·‘¼ú5zÍo•ÍËÐaWInè^ Jò?'ʧ‚jN„µLvm¸_Öã¹Áý Üß1Äâþ:å¢I€&ÁÃ0 (YåþŽSZ™'r0.úÜ+(®umàZ—!z׺6ÄWW¸Î…»[&Iå+Ë,åDJAÁ–c[ž#ç=!à¯rHgGg ·npë‰ ‰m=ì4»TŸ#|ç~ ´à„Raš¼Æºám ž!ÝðóóóÖŸóÄR9OP_sÆ S0O…5hË=[n#ÝðŠÜrÔP C­aÝ"A­€ZFKE‰è j `.ÄÐÈkÈkÈkyM®pÏÍr—ŽDr£cVX$¶$6¾‘{ny^n}»CÈ ‹-OÐ=\ÙÁb³Z—\I 222[GfÓ+µØ2Ûc‰œ’tcM¶Ÿ¼Ù ÙìQ°™Z&›YùŠ(†š&*°™äÀf@oZ¸=ƒl¶©vÛL;m#üü‘ÎαUº¸´‹Óú<(ú(}HÌQEG’ŽŽ#ÓŽL‡L‡LבéøÊ 7 j{†Aà 7¤3¤³û¤3ºêmt„|t¦­Ý`)%1&&ÝüE„e”H`DBlG ÒB]ï 5d6d¶®ÌfVž}wسÌ>‚ÙGÎÎî3û]uö‘:aöFž•b5ic6rrÞúsY5çÁãð)5”-,ΓhÂm¤ ·É{mf°‚[JuÛv ^£V0ÚqÛMK¨¦™’Š!³!³!³uc6‹§¥ÜyZŠYýi)zÑÓRç¸S¶‰µß*¹Z nï<-EjÍ,X$j«â`f<ó°”bQRp ¼B9ׯv\”$Dqn¡j:®OJ T VžbxZÊROKidï^öi) Žc«@Fµ(2‚úîÐ'a5'ÔSÀ6fµ¯¸Ö–KA¬œ…Œõ’Ò¶n³Íª-¸u% 27`æ©9Pù±ëz› ¬ «E`]*°êÕ«\¦Ê)%óµ”Öe« \¡ç> ¡uÎͅƆÏBãúëœbá³K[ô¾ù¡µFÔ•ÖÈ— tõÐÈÖ­ñ…‹¥—Ê‚žaµKM­´¯©’Î3A3'¤3µF-AS1Ô#MáFµnšãLͬ]ï›ß Ëx(4 ýPqU"®.sÈ~kœ-Š«ÚùV(*(§–ixÖÄU>…öÕŠ+ ( AÍì«m]×aU„U„ÕÍ€U…°ºTu•¯Vé29ç‡U#}—¾WYi8e$j3éS«ŒÐ–‚iï²i̹Dj•¹a¾ÒÐ;´'ˆy¬êêšGÁ"°.´DŠ»GËÖFZéU+YX¥ô-1Ð˜ó¶ °¶%ä묵¾¨#°>€s–WRXâêRV»r\%f™ëLYßÅ™pM@®…P|&26Ï ä´P¢Ën2>«ò"žV¸o…ûVV_%«ÅÕÙ‘c5»ÒRg `R©…Và”í"ÇÚÒ„(æ;ª%RS¤Í,[«È±Ö/®ý!.\mbDW¯Gn?c¬·ŠÈ½iM67n!|³òc@tÝo@—áÅç`ãäŸSÊÖ-žë0¹ÎÂÔÛ.ÃáUÐ Oî{xC*Š‘}Ló¡ÍŠ6›N‚ƒÆ}–a}¶Uâ]€@óeu­`Ë“”ø ˆ‘‚ÖlÜ&˜h#Pa§ ÒC¬c t!ÑH H H H ë@ ÞÛwGÞAš|ŠúÀ£ÏÓ¨¾F$zšn_ÆvÏDJµ‹Wp!T)G“ºA¤–š»,Ñ1‘ZDº"•H¤H¤H¤H¤k@¤ŽG¯ƒ0^GÍ®ëf‰ÚÖl Ðúž·áh‰®ˆ@(((èšèkFiEÉ`Y°òžY´žJ“«-¯í¥4¦ÒÜÀTš’IL¥‰©4‘®1•æz¤Ò\ê1?0zߺÄD)¥t“Ø4GbÛHbc›Hlëía޼†¼†¼ÖÊkrå¼ÖˆµÄ6ðÀNÄc;ð999ïržÀcîŠ2frÕÑp‚éû‰†«×¦ ¤0Ê|é¢Ü8гaBI hÀ¶Í hC´k¢[9Ú™…c›‡ÇQÅ´ïòpIÁAxìm'Pß*PŸà X4P·áÜ9³rKÏu¤Ô3*`F…MJ¾êŒ ·ø[ןè1¥èoý]×é º‹¡»®Y »º‹= w±¶ðß:‹†á¿ß“E©EEEE]—ð_ [ºÅášTóÄwb°%¾R–M‰åLΜA]n*¹D†Œ²šÌ€Ÿ¢t\†³qÑÃp˜“Ö÷Ûû/à©ÃæJ6ÿs0RßFóó²»éRÕšûIù–ûîu)q‘¶»‘ÀnÚ–¢Z3Ö}gÊøv¦è‡gYp Ð9Âd÷güÑ,t?2Ràç$«h–7Iúá¤@WåížCgçAÞŽ­ï™šRýî@T.|’çï3œç+|bË­d”Óq!8¨y ¨öIÙRQùR}õ3ù]=¢¾Ë~Í `i[|Ëo¨yŸ{QÚ‹[`ÿÞb<Öû Ï5ƒÍÚó2pv&Ôç†i0ÝˆË æ¼ ¸²¹£#g ¾Úí­9ævËcì±+EÅÖX¥(ßë åavM•"aÚ–<ܦYç”Ô´õLšƒd¹EÀÆP‰sŸeBè”4Óêv™+~˜dù bmoÒ'»}o{\½t+žœ¾fZIáð+cÊøƒZá*µÂõÕóøXÏ£¦Ðó¬]‡ézÀ»ý×%ôn£û0ŒaÚ waN·Q„[ªô™ÌÕ8ÐÚÚ`EúeÛ {Œ$ÛÄH2øÚ0’ ½êqm½ê׫ž-3’ŒÚÑI£0Kv’-¼f £ËwþÜRn´åV4is¤µû¢µîö“\mž®‹HqrÝ_Ÿ5$$A$A$ÁH‚|™$Øšÿjápj4îP8µÀ4!ÈkÈkÈkkÂkC¦¿SøÐ­[Kbá ÂFØ^K¡ìxG"Æb`#ð Ȳ0¬(rpÉñ€…P<}ø0z6d#5/¹¼ºÎBo?é‡÷Ÿì¦9'Aß×Ôúù,“†pÙ͔º<8˜íæ‘g»É3Ó#‡`¶›5ÌvãÑI¾›wov÷]T3èz„ûÄwãd‡‰õI¬‹×âL+F…´ÓßÔ‘†>àô7 ~ž¥±Ý¦%‚íâWܺ0Ò®% aµÓ. ¦¿YZú›z|%¨IÂÏ#P´ L c-bP@Kµ¤MÒ’ºƒã Õ$Lÿ²˜âs› ÉÙ(: Åûtte½ oÛ; ã³ía˜~Šz`VêÐð¤KããtTÛ;Nƒ³³¨ç9Höáf/vŽ’ëìÂ;ê…ƒ Þ‹>Õ¶øÇ)õ ¥„Qwì*Í)Gã—} ”àª.©xk@ZH¸ö‡k÷½ö7†×•­ýMwxóV~I£~“&_åäêÑUЃ1Lz9ú¦ ²Ó‰3Ú€þw’¶VŸøóÕX˜oÉÍÐÕšoWg‹ÒƒàjxœÌQ~z÷ù]]Gýpø&é}'[á£Tú]4ŒN§`8£›Ü ô 8'¢Õî¾Xñ%ÜËÂtŽâÿëã䨔F1q-Eƒ/a:íy²“_cNwãwÑnQr?¸l1¦ón<Ú,~ÿÎk‰Å Š çÆ»(¼¹³ÙYF JÉì)¾;ˆ.ƒ,lL šâ®ö¢´—ü ^Dì&å,´Ó²G!È{5äd V>·ùrp^Î9K?ɃqÞ‰s ~ÎK~“4¹c÷k㬜ìvZ¿Ã–®¢¹ÊW… †øa–ô4…mä·Ò¡ü¤çè",9»"¿%ýÕ|Ô/Jßô¸QF¨Ú¦d›(šŸˆýIï/îÉ5úÊ*ÉêèòoA–]„7Þ?@' nàùÔ+î'Y8ÿ-½M#ôÊCnÏ<:. SfîÆ6=‡ïéÃp޲Nœg ÎþÑó$Ë’Ëß‚F0%¢Q{gqdU±½´*ÂSÈ*zx•¤Ñ’Aø£Amâ5;êiö[;z¾›ý§ÿê×ø_ï) ÿ÷yüÇëWÉ®øçó¾>zy±{ñöw"ééû_ãÞùüßû;ýãâôÕ»x®ï½”ÇÇì×¼ß'»¯ß¿Ü=úe~þßìýrûð߀Yõ-Ç¡Êm퀹‘V@º¥ù⥵ýh˜lbŽ>*VF[õ£±Ê'­ô™–Ri"…FŠ-Ï0ÆŒ½Am@üzÇÜD< ?¹…-Òñf¡SúV9Ž“«U4_Åòü‹x;ŠÌÐAàÿv¹^“›ÊöÕ-ª­©‘ëð©Ö9RŽ£¬‰„{ÁàS0¬(-EKPÈóa8-ºPõv{.íš^^Ù)Îã©cÝßÑÇëpj‹ßr÷¿׃,Jo{ìïæU:ßG°¬g£úÞušÂ“ÈŸdSgoQÙ_~¾  lÿW<â’N7åáÑâFZAªi0sá7œ‘|‹‹º43Õe—‰uPY?yš¥×áNåúáu image/svg+xml Physical Router PhysicalSwitch FWaaS VM2 Tenant Network 2 Neutron Router Networking Node 1 Compute Node 2 Open vSwitch 2 Security Group Tenant Network 1 Compute Node 1 Open vSwitch 1 VM1 Security Group neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-compconn1.graffle0000664000175000017500000001046513553660046027071 0ustar zuulzuul00000000000000‹í]mSÛȲþœü Ý|º·ˆyŸQN6§d7Ù%À’ÜMQuK؃щI†°[ùï·G²­W›d¨JlK=£Ñhúyº{zF/þõõ7‚éÆ~u’]í@eëPÀígýgp™¢öZsàh?èe/Ÿ>yñY_½ÜìeÁ…Þñ¯tò6êë¯/6ÌQ8D™èä%z±1ù:)×z~U~(jžò“Ä7_ž¼H³nù%´ÐÏ£`Ä£¡»ß~KüÓÓP‹c‘Š4žK4Ÿ‰‰ªˆ¼Ø˜T]4a”Å›ýÿŒÒlzé,éÉùW~ï³¹dÔ‡Ë Ï‚ÞTl|ïc1#‘NÎM›ñ÷ßhÍAßÖœ¿9‡IÎ…DœI¦8[s¤"®Rž„CH*Lø·oµ{1•m…~Ú®÷0ƒ²A"o·KùI‡“jßb‡ÙU¨KÉÉí'¡Êøsy¶rº8¿ø—e³Ê†íîU›=í¥É·òKѽ©þÇç –-?ºðÓ½$å0™ôsÞÍÕ§œ—ˆÃÑy´ƒ¨U?nןKýT1•O´¾¤0Æò/Qøaí¶ŸéfSÂlãuŒ,ŸSòœqçþZ­3•ÄI³ü;?Ëœ?à¡û—~â7 mé0ô¯{~غ2è$ü9½sçgú½YÁ¯A¨®†­Â>èAS6`Ûqot®£¬© ÓŽ$­žÌÔ`H‡B×GZCs*ªC$s=Q+©„ k%Üõ#!GçF¹$u%ž$ˆqråR‚P0ϸ®]]êUê×™?Ô] 6îÄ8ÊÞF§ñlý€ÁT>ÚúùBà¤rrzYä2ª8ª!Ú¤À`F$±”]’î”0*W(Õ´rƒÚ½7ÔÑ¡¥ë‡ú<8‰Ã~£šV‚¿tµh®7jª?µ‹5áªUób¢ïüA¤³ê󚎪'³±àIó'4Ï·ýØ·ÂxÔo?î:R¶žõi†óus,Ô$:FøϫC•¼Ò‹ÃbZÒãÒc¬,I:J&Ý%•Çh¥$«•¬ ”æ IÏü~|9¯'š¬1‹6:ênðÑ {¹P¹òŽñ½\èÞü’ݽœ+!÷Ê¿™½<¹¹$ÒÉßFõþkiWM½šv¤¿f³‡qýl©NÇIvŠAëƒü¿Þp– 9îŽØ7§bÅtt¿™DOÿ>>ÉNÂãSt|ðÙ;ó“TgÈiaÈ?¿|Ï<=(ðÏãD÷ çǃDëÈ|9 G>óŠÇ1\Òǘ@é㡟ô³¯\ øŽåB™BXþ¡ù 4¡^.ÂX.ÂQ.¥Í‡À¹ˆ"Éë ’ÈÏF‰é™Y08Ëttê÷€»ÑÓ§æOŽOS,œãÞ)qÞfÁ³Ê¾]g-΃˜aWRB0T™'ð Æ®Â¬Üè»áAŒ© " §õ”\s¸p…G=d æ‚A×Áõ–Ã`!)EXxBO} ÀtH)]O¯Á`3œ7“$î4¨PW€`GüÞUMüÔÓ<öY &WãEóÆ÷©ý \®%ƒ~vÖ&²(qÀ—ÕäV€á†š|{ž£š9(š”®1„$V–Ûó†3|…öz5Hv,ñçëÃé°ú'¢:Í¡ve¨>aÜ[Œ%ÉDýN«ù;pê‡bàMê^ÎP«ÅÛ«Ïu)Ü›ußoÍõõi柀ý56ìÌ“C³,°Ó” ±ý•ÄA_'NTØ_ÇFÀXg›¸ØÀÏfš`c¤ÕnëdýÙÝ’(sQçwÔ8€Îñø[*0ŒaÊ8o‚pO¨5§yŒ³»Œ0܉­8Wº‚³|÷ é…úþ8ïHRN£œ ‚)•jq›ä ”E‚RªÁ‹{ï’1D¸TÌŒAPpyaw0{“ ¡ô\¯a½ºHÎ&fLrŽ Sð Vü¡FÅM'ÚAƲ^b¦3Œ9_b¦1OJþ}¡Ì[3Š˜QøxkYKšÙEìûqd• ãªkÈ:x¨À¶•ÙõIáƒ8Ë'ØžÇ,S¢yÇÖ¶»%ÛîþZk ›csªü¿¡·2ðöags×Áó;Ó»:ôMÕ&hDÎ3ŠÀ7ìLi¸%Kc4”Vž44¹‹ÁcEJ2*Pé<Õ©5ãÔÎÆ@ì!dcäðKPD1í¾EÓw<Û%–ç ¹ÂÙ®åf¬y¼"óø•Ž´ßˆ²Í0Ôìü–ßêâXsÝâ+B"„ {™gµCV™‚ÑA~X1×8\B2Š0f–û#÷Q“÷“q_îËYð8’ y~Æb7$çÎ2+/©,÷Yî³Ü·\rÜÇñÓïaÀŸ,£åج>ƒ©§st%d´ŽñeÒ9ˆY哘ø9Ó9ÆÇ©«¬áh]KpÌ#q¦BÞ’Ç)åØ\Û|Ž•æs´ì´»ËçÀDºœ{0"¨dÂcŠ´™¯Sj¯Iz¨ë”§w —Œ‘[Íÿ±À}HëXÀz³=€Ì®0öňkˆL1éa“Ë ia8hÖáRvE˜–Þ#½IiéÍÒ›¥7Ko÷‚ÞVKl~[c­­²Äö(‰ Kl–Ø,±Yb{´~[+gÏ¢·fÒ‚ôÚôF©¥·DoÌnOaÉÐ’¡%ÇD†¬ Ã`˜ù'¡No9†ÙÜ,ɳžÞcÌ]'v†Î’›%7Kn÷&„yü£[”bR4Fggh…,«¤K±¿SzRx ³ /Â.”PâIFø„fÕä·†W¬j ¯¢Ë®á•’2Ê%”dŒ"²ô^2?›rR¼r+Å^Ü »`Š!Ž ¸JYË·«íêßû¸ú÷vkÁu ÿ‹w%61D¥0#‚2³í îcÅ¢ˆ ‹ì«Cv¾*dçjIdçw…äXq ¾¸R„x`ªÕ|‹äÉ-’ßɲu<«JþŸ‹Èleëxà‰sîzˆsŒ<€=•/³ŒçºE@v]ÆÓ^ƳïgæÕ·´ðgÅËyZ3˜«^Îc7®^9B¹Ê•Ž×­U´W?ümlìJÇ›@ck7&‹vÍ.’w°®›ÞíšIXÝL°ëÜ`†WJlL ‚…ô$³ö µ Ø‘®Ô®er`„°jïa(qGJ¤ü¡æ(Œ—HQt Y& Œñý_ªc"HñxÅ”a“.ŒyÅs¦ Æ4 fÅÙâ/á2™YDAîÅ–|,ù<~òÙ~³µïìúç:ú=}Òljÿ_úg½áõ“«û!´ú\W!}Æ‹!l?z}a³á°?=*ìôèJòö§Gù7‚íŠù·6/Q3ryxµiò¡N4×ûÝá>°¥ù~BÍE¦ù{}[ÊËA1W)¬”‡9ø.B*wYÁFÔÆ]lÜÅÆ]lÜåÄ]€sG_WIÐÜ£¨ËIòåNb. NË#ÏcàÄ+î"êQáqE1ñf¼ZŠ5G— Bh òV÷lZþ'Q–8-qZâ´Äy/^ê—f~ÔÓ?˜§°`0<¤ù(ö¡Òs• @K&±ÉÂa†§<Îp» öûVÁÖ5§`…ÞÒ¢ÐÍ0D ¼mý¾/!h}}šù'Acl3ÿMÍ^÷IÐx‡Î$¾ú:qÀºŒ“Ï>.¬h1~µ·óߣ(óÝÿŸù0u[ïçfÀ sIâa©‚ÔØ²fŒx‚ˆâ¥Xº¨š $LD¸qŒßé;Jïd^`Ä .ˆ™±ÁeôB}ïv¸|D/ê¼Ýi‚…ͽU îMvÙ ®`ž@Œa®<ðÀ¦.Òà309ñ@•ó]68_s˜g ŒïU*¬…a·¸™Í0o»JÚ!?g½´"v +"=^¦â”ŒCv[ñù0ŽÀËI?êÃÏ(Ò½,¸²«d{¸¸:ÅLØšÓž´Î‹!ì¥SœSfr“ U.ƈÆQ"âýèE„c‰u5E`c‰u5yÊéÙá߃6L¯0NiqU}mh9pÆ3'ÿ‰“ÎâÓL”F ?4¾LM©R¼Û,/¤#˜Å È—wŸßÕL’t'î}ÖÓ‚ðQ‘þ¤ÁI‰ô3.“ç/íû=U­îÄ›‚eÎAp lŒL' ˆÿ¡õð(>ìù•VLÓœŠ'ÿJ'å•§SP ³ÆÜøu6Q!iRæ;r"ÍeÜ߇Ÿ‹zT‡aÐÓébÍøèËk«5À¡µñ(›=Ä7£àÜÏtk5lDs¶$½ø]miÖP‡ên!¥ì¡}¯o8 e4äü™¯£AuËÃI[úqÖjŒ™‹MRÛŽŽ• ±év&z÷XšµT¿²ü ó},$_W6hâçYÚÓV¶ñ„ëò)hÏá™®ÌÒªïâ~pôòVoWžô¤R‚°XGÞ:A`˜?gä9VÎ?Lϵ®•WTÑÕñéw~–éKç°×ýKèŸfÁÝ8Ó‹ßÒ^ ×:¹{ãɉ4 ™…+7Øô žÓçtY£Î³g÷ðUœeñù;?”ã¡2ó=®ï4Œý¬®Öãï ×U¸„¬â oâ$ø+ŽüÚD×¾PH³ßy¡W›Ù_ý7¿‡~ÄHÿï«ðÓÛ7ñ&û÷«¿=|}¶y¶÷q|òñ÷°7Ø‚ßý½÷øÓÙÉ›á&œßzÍŽÈï_>}ÜE›o?¾Þ<ü2¿~ /·~›ßüðp¿§{ ªÌ«\Á¤ÒÕõi¼î³®Qs¡º®ÑŠƒMS¹=îɹˆ3 >0xÊŠaW)ìޤðÛ5M0ñ@_¹éíx#áÀ}·9ŽâámT_ÇòüAìEÚ È ä@ûý½(¼º^âËÚÜÓDfäxkôÒê›ÀGAÖFÂ-?ºðÓšÑR”8ƒ Produced by OmniGraffle 6.6.1 2016-10-06 18:12:30 +0000Canvas 1Layer 1Compute Node 2Open vSwitch - Provider NetworksNetwork Traffic Flow - East/West Scenario 1Provider network 1VLAN 101, 203.0.113.0/24VLAN 101Physical Network InfrastructureSwitchProvider networkAggregateCompute Node 1Instance 1Linux Bridgeqbr(1)(3)(2)VLAN 101 OVS Provider Bridgebr-providerOVS Integration Bridgebr-int(4)(5)(6)(7)Instance 2Linux Bridgebrq(20)(18)(19) OVS Provider Bridgebr-providerOVS Integration Bridgebr-int(17)(16)(15)(14)(11)(8)(13)(10)(9)(12) neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-flowew1.graffle0000664000175000017500000001261513553660046027451 0ustar zuulzuul00000000000000‹í]]SÛÈš¾N~…6W»u@ô÷ÇœLN’™0'L’)ª¶„-@'Br$’™Êýþ„Ý«ýw[µÿbß–ü!K2ØÆÛ4`¬·[­V÷ó¼_Ýýòo_¯BçÚOÒ Ž~|]ôÂñ£vÜ ¢‹_|8ùi[½øÛ«ç/ÿåÍáÞÉoGon¤™sôáõûý=çÅöÎÎn·ú;;oNÞ8Gï÷['Ô±³óöà…óâ2˺?ììÜÜܸž‘rÛñ•LwŽ’¸ë'Ù·÷PÙ6p;Yçܦ¨}¬9ðm'hg¯ž?{ùÙÿöj·×þ{ìGÿëËó-\ ¢Ì¿ð“WèåÎàã Ü;h{Tù±¨yXÈKÏ|xö2ÍxäWÐB7¾Š‚‹$îuÝCøôs⟇¾x¹Ó)Ic®]lšOˆB˜—D^î ª.šÐËâÝÎ?{i6¼u–ôüÁõ×^û³¹eÔÛu/ƒöP¬ÿì}1#‘® ›ñçŸhËAß·œ?9×[ŽTôû÷±æù½ÐKëE[qŒîY)²ÿf$?èSRîÞB¬•} ý‘ä ÅÅE¨2þ<ºZº\\“x7£fvpXnôç°#ŸFŠLýßãøjŠñ°çE×^z˜Áh$ º2ïÉò‹ÌKÄaï*Ú ƒ‹¨V?®×ŸK·º^ªÊ'¾¾¢0ŒòQøÇŒË7^æW›BfÛocä`ù%?0îüÁO­u¦’8©–ÿÕË2çïðÒ½/ñª…Þi7ô¾µÚ^X»3ŒhøqÚWÎÎðsµ‚Ÿ‚Ð?ùÖ­ö`¨Weóö&n÷®ü(«ÎÁaG’ZOöfj`¢aÎŽ´Êä(Í¢´«"’IøÍ1æ[ãÄ¥Š*⾦fþP˜>„ŒÏž¦é3š?—^×oš@ƒNÊNü¯Y½Üoo[ Òa|S=öÓà¿A:޲ýè<ž<·` ކŸõBà¬tqx?äJE(fcwì¸h.À\7Hš `"9«MñÒ6 Ãa×Z^”VJçH”wÔ¨D>ÕÔpÊÝ£Šp£¨¹Bô}ÝcåŠÊ5±ñš á£8 ²Ò6z’"*`P*Ê™TŠ–Ú^”=޳1ÊSïÙD|®=ð xéIò­]¤”@Œ0hƒÒ¼Üku”¯uÀy†· µ*ÎOúú0H/½Ny^,´î ;-¦îj§W  Ò†ñ«#Ø:M²s| =ȵ»˜prÚŽÛ±g.1ÄŠÒÞüØöüÏÓs˜3ÙYxzŽNÏ£þ¶/½$õ3ä ¦Í_¿ƒXÛ Èýõ4ñ;„óÓ‹Ä÷#óá,ìùð7¿@iñ=Æ*ÿk ¥O»^Ò9;rà7Æ$ÿ#”ùCËÿ(dþPš‹P‹0–‹p”‹@ióGà\DH1õIäe½Ä O¿´ÍYpq™ùѹ׆CÏŸç–b᜶ωóñ`ßÁŸ *Œú÷Ø=£7î˜nbÃú.á†𖃛à`|½èø((i)Ór®ÐDc ,Ä ƒ 0ÇЀ,™VRj†MÈr)Œ N¤DZ hiý»r×ÒøE †Ä ô¢9ô›^´ÒŒ²ûÑËvË¿ Îâ°3-ÏÈÙy5ð̯ÞEägå×5TxžMVSŸ•,ŽÅvõåŽI4¼Þ!…*Å+³nãëQžS=úi(™4–LÂàc%Ç^øì¤òÚ@­¾l`Uåm^™»{U 4üÁÓw4’PâÖ’ÍM £ àMxEå§<\ùÉ±× zãÜYSÈÖ†6‡p0šû+.´ zV¢ÐÅøß¾ßejOOg 3,)‚yI`fKCRÔ-“U…©å É`ƒ ¼Õ—,Ñ»R–΋Îf7›^*™×[ë{aÜëÔß¶%¿{‘ß2-*Ë|–ùVƒùÐìÌ7{ŒsèÌ3»ßùᵟmoZÌ6.Ô‰ ýÎ÷:³;°Ðíþ¤ † k`‚£¾º‹(á@wŒ0 *„(å hé v0å iƒá{¥C¥«š†1‘™.Ci€f Ó™L*- • Ð^(ÖLoDÀ' ÐèšKAeTßÏêZSèíÓÆX ©b6†ƒ*$"X3‚gd8É¡˜$‚ röš¾›$cÝ;Ò¢šô­÷þ…×þ6&~î…i—{VvüV¼­¢?#„ö‚p¶–| :[³pËNKpÙçvU_ë !ÕÄ'½—• µK€›´åf»¢y@†ºœQ0üæ ñNëôBÌZ e%ˆÙ^tVÂZ8Ä8h8\´O65vr¤¹¦LÆPù>ˆZ˜µ ¬U0£U@éÃZ¹Œp¬¹ÐT -˜ŽsÞÙÈðÙÄðŽe3Ëf–Íֆ͘õqÝé㢠±ƒo¥<¯C ìd—šT„I „®Ä‚S"‘$ô‰IVÆÔDQD`ŒÕŠÔKSñ$]Z+ÈcÖ5«ªf=œ«š»Åè}ÜXV¹_'åžj›»es·¬voµûÇÔîÉ"}UÐ|°Z)ÄçÀMÒúªž ‰¢³ÒR&V_ÊÔ´åÅL’ñÆ"É„"€R¬¶üÉR˜¥0CašŠƒQFË ŒVa°ô&HÓ‡ ý#Ç]|ÙÜE…[jPi"D a3 61£€ò'“Q`yÍòšåµÕæ5a#.w¹l©^zą΂ÄѹƒÊœ»+Œ”A(¥kIWH€)Š3ã!Žhìj³v>€,S€p*„ï˜Ô˜ÃÌã“—h4Üú®xö¦ããŠiõçÁĪúpah¬˜kRL…da̤¬OH&îBÐ-G— P¤ÁAZ`¥åJù>_:^}úFæ~æ§R* ŸZ—+|O£’dj]®pBMÊÓ±ÞëxÜ@±l’… Ÿ¨D«æ“8üØrö %ùÞoÎë$è\ø§Ýȳ¢}gÉ6`åÝ»x…^Û7[H6 î¸E2‹2âò1ÆÄjm‹2]gQL,‹.€E±eQË¢–E-‹®‹æ4zÒ‹"?\EÍzÑ#3(æÜÕˆsŒ4‚gÒ¬Çàmþ؆æ!›?fãì–“mœýÁâìj±ëô‰+9×`À)Å€ø¤ü±Á¾•S%5Õ¬N]”ZêZŸm+ÉSÞ¶Ò%:Kt«Mtr‘D×´fuþDik¤­S¢4QÖH³Üe¹Ër׃q—¶ÉД8t+ð‹…m?ƒë™‚‚Í’ìwWžáI†˜Ô&–DBÀœ›aûÊ1•˜+A æcÓ)“šPèrêÖx}6T>ò²ÌO¢%% .:,yc姆dùËEŸ!±D.’JR˜ÎÂd…ÈŒ›¸A×<øjÒ"äš#d5\hòž:$Z>B.lAI‡œ_í‚»`dåŒT½q¸`„aW7‚]H¼Zeu¼g.™T”æû•ëü0ÜŠ¤ ‹Ú#ËH !Ô¦ºÚTWë±µ©®+à·Îí}]¹$×/gÉŠ­!²NœùBËj”•Š:o®Xìt]yÓ.´´¼iyÓòæ*ðæ~”f^ÔöòÈDÕ”»C1vƈ`³I"PP±3½Ð®!**†ÉÌ–„sjÀ' ýLu§,ÄÉ6Kš–Þ“îá8²§t[DQ×HÏSÒWô÷â«n/óƒ¸óøPª¸‹GTI"¨–T “i¾” Û#̤`˜+àÊ"×}YeˆMИ é° Ñc?ÍÁñá’-¥"u—ômÙ–L˜»3d[b"kþèY-“å-'ÀMË Œ{öó™ÄGq˜]jME.a’cŽ–T#cžŽ™?޳| ˆŠC}xOr{dhP¼ô(×J,]Ê…’\c‰ÈX·=a§îM¡: ¬ šÑz5hf ­<Øw0Âwó˱°¿öwaL7Q„q$¸$_Ù zv£[ç€|½èø(˜ƒœ(sQÙù$Ø–Ãe?¥+®%Ã8÷HÁEå0ªPÐÒÊwœ­€ž¿Øhz#F3ݨèOX©$íÐ_¥Z›uDÓrm—©ÕûEÀô<«pÀ×RP¡—R)V70™ÍLƒVIT(ʸ˜ù¦(•¥•A—ZÓþá”’Ý0¸ˆšôËÆ4¥µ³”;þyæÒÑ×f̯ÁW·X·¨¿ÛµŸ„Þ7'ò³›8ù|Zĸ¨".Ø6.Ú!ìv•dIºDm <™„IÐ ”îJ„Y£¥Õ¥–žo§~r´ý29xˆM}S L °Ö§$w%Xƒ1‰ÂÒÚƒõCWóX?/£5ycò4횊µrYžm*4ÕB iOhÛÀ$&Oæ„¶>zp Ix™ þ`/I§F,Ž´ š"%”jjÄb q…SʤA„Mà±áf{¤öŒ;ÈÅî*7ÎofùDí;j7ÛÙÀQyãz¹µOLµ¼fyÍòÚòš=•ô®|›š•²„EÔlqñ˜}gk›GL\D© €¢ˆÀ)€_Z/-È“ÜfbµÍ.­žciuM÷z¸¥ÕÕ…_„ÀD«í‚e5þ Ôø¥F›¨ñ÷ó0Œ`È„˜-õˆ³ÆaÍž1õHr(&‰à‚X•ߪüVåŸMå·{oÞb¿üåZÔ¾IÈèµåzUþê&/[E` ‰LôM0ÐC&ñM wiÀ´„ ÎáÖZº‚J©)gHs³OÒ^Xq>´ÃCMu{<ƒÁì1XÜ k"àÖ`°ƒ5¬Á°ÃBOTƒÖ»&sÜl6Á9°”´±ï'ClÄ4cš± fšy(êâK§®j•ÙTå LU¦ˆ=µTåbì0%R†Ff[\¡”R’ ÜxBÃ-‹+°© €&Ò¦tY²³æÚ¬œÇl|çÎD¥Ÿ­Æˆ|œøNµ4– )]܆hlˆf3C4íêh·ôVÐÙæN`m8 Ô@×ì8Á…É£«åRa]-@}‚`1_È]=h–S(ïJ}*g¬­´îor ¬Y°x®bn¾ daÌÌQ]µiŠß¤ºÜì:mÏZŠ+ÙóìùÖgaÏGX—ÅáÇ–³oÀ(É·^¹†Î’mÀÊ;dˆá:‹*ÒÄ¢fK•*‹bbYôþ,Ú¸©·eQË¢–E-‹>4‹æ4zÒ‹"?\EÍzÑ#3(æÜÕˆsŒ4‚g"mˆßÛÔ³ML=ƒ×¶‰Lmcô–©-S¯aŒž,v;±Êݼ×tq•k¢%`.R%5Õ¬Nk”ZZ{,Z›ýü¾ÜÝÄ&lƽΊílIÐ’ %Áõ"AºHl\0;wr¶5îÖ(9›mæaï–×,¯Y^[C^Ã6û’‘nMId ÛS7¤$òYÒ ïHh´Y…6«°–Uxäe™ŸDKÊC\pvaͱèìÂ'‹êÏÛRày²žcÝq7ž^-BZ„\w„!ŠbéɶG#nBH2qÆ9ðÕ"¤EÈõ\™Â´>mžÈiu3T½å(î"ª©Ð\QL´*2‚ª’&ÆjW¦,%S‡ÚœZ›Sk¹6§v|¹À¹½¯+—Mûå,Y±Å(DÖˆSçÁÖ¦°l•7W,غ¦¼I”åMË›–7-o®oîGiæEmßÁLTMÉ>õøò´ ÚK[¡ÄÊpÅ ÁB?$SÝÁ) q½5r‰ M¡{RÀ=ÜIë骲ˆú(ˆºþ@zž’¾¢¿_u{™ïÄLJÒÚâÐù™›§WJfN«Tšà"Á’ÕEBeU†Ø©Ë`1=öÓ.;³˜ÿ3¤gÓ~†ôÌ|¶‹ûY&ËcB…e‘ûÔ½á0ÛñÏ3ï €±¸æ×à«[Ñ‘’þ¶!ÐçºudíKgÛiùáùvê'×è~v'ŸÓÓY*ân¿´s’xççAÛ1wxë¥ÙÎ'?ÍœVÛ¼$ˆïåOiu,çÃ2#£;/ûstÊÈ4|á뭮׆J†7èÇj\5®òŽ&nß'ðÏ8i,> ±T¸¢X ߤ¦ÔH¼„ éÈë¦'ñò£§ÏŸªtüô}Üþì 6öZIúcg#L™p›<0wä]øÃYØQ*ðì ÷€ 3?™Büï¾ß=‰[m¯ÔŠaü®ˆÝyßüdtç¡s¥B¾æÁï.…äwÕ°2+¿ƒëâGðï´üù1ðoî4h¡q/›—ûŸŸ·#ì)îð.N‚?âÈ ¡ÝATmõµÁé4ÞèõnöGçÝ/áoŸ0òÿýuøûþ»x—ýãõ?ö[o/w/? ŽÏ>ý¶/öàÿÎáüûåÙ»á.\ß{ËONÈ/_~ÿt€v÷?½Ým}™Ÿ¾†7{?ßÞü÷Àê÷é%·Õ–I2¦7T_7-Ú×` )6žêÓp3Žýk ¿ap®Ã‹86êÔ2ßÄIÜ]Fõãø™¿ˆÃÈ7“x—û^ç0 ¿Ý=»ã›Ý0¸ˆ¦P!@´¢ŽHOGÚ[_ ì= ²:íyѵ—Ž‘Qâ TݼÆ1P¨L»í,¸žFcÊ 6ÎÜË~ˆ‚/=dÞòôRÿm”ÉmÝþqZåíS¡7T÷zI=‘÷d]÷mP}ß~ízL¶ÿ(ºxÔˆ÷õmí¤¬–•Â¥ùÎÔ¬È$"”m9WŽÿ*ÙcΈ—YÒówÆ®÷Â)Öà y¥Ë£4²òõñ,¯Ûž&]+ôÎÞùfvW ^ÕZúyì_Œ×°¶13«r•‹Vc¤‰ÄJåb¥\бDD)&°Ä¹ »œ+Æ©¤ eSŸýÇWUõÐå4÷~ ¬8EDÒJ;M™˜¢i#–4ÄHf׳inW’ac”ºÕhô·ÂÔ{õüÿéûŽCneutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-vrrp-overview.png0000664000175000017500000062201513553660046026157 0ustar zuulzuul00000000000000‰PNG  IHDRʰÿ^sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|EûÇgöîBBG©¡# ¢bÁ^ì]_ öö¾’`WTJrÑ{Élõµ ¯½ãkïŠT°Ó[BGZêííüÏ&{ì]î’»ä.¹$Ï|»·;õ;»3Ï>óÌŒì˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&Àj% kõÁ˜h±nòÜ—^áó .çêÏ„Õ-D„‚3Ÿ`0×{¼Gú}ÂYçž|'ö_ñŒËJ=qZq7‡#óiµÈeˆ7g¼#äøš6 σ5½t¡dOåµnßú·‡ÇŽ-mÚ¥Jlî3<ù­5ÿÖ½ýÒ?@34%¤X'®eÓ=wmLTÊ7{îëU®WŒ‘¢Õ³ùÞqK•N¹®_$„zDóë“Æ=‰J'šx=ž/œļ>N¡U<æ¿¶¦0–_éóû¦æ¹×ØýÆ“]2ñ±—1‘ç5ñÓuõ!ÒҤ”ª>ù¨O\™9yW(e´+ðæL³ç!Rœ‘üÛÃ6•óšê§¶2DâS[¸p÷orçö÷;²›8rµÇ3RçÇ~-Ãso‡PÎn¢{‘Çsm™õÛýh>¿¿‹«çºo¿z~óäÉ]ô ÙÎ~Ý™¢v>:qâæÚžËа@‘Î[óܲß·,(·œºŽXRGiE¾¼;Ñ«U¾Òn~Ó'~ù„(ÞZlŒÎöþêÔ´›¦åf}1’xãæGmU±aÇ]Ê·i¬_¨N„À Gâa¨ wî÷š¦Ý3}RöÇñÆSá÷VJd é댸ÇÄ;þdŒo£˜×O÷éKüRü…üíSS׋y½ü>}:Åeð7Ðî·%²³—¿¾çÉÎÞËò ;žÂkèÊÈË{« ;{]MeŽÕMq%ýd©Ÿ !_P>ýè"1g¸¼V›<÷w÷ù*–£õ46µY×~ˬ÷=4\¡o­@ÛºY)¹@sÈò'eꧬÄxX(u¹ýºŽþ,ÓWŽþl Ú…Ú8Ó{Ø3v«Ý‡†5%|ßvJ“ejOòÓ½ž­MX§¸Ø5Zó(—¢®Æx¦ô+Òó¾RBÝP;4 IMÞ¯Iq‹”ÚBÈo÷~åÿ2#Ç›G_ùuM«¹…+Û°cº!T.:äöàö­”âßBjYšSÁòW²G@`.OH¹¥üZH¹ AµN‚4"è Þ˜íí›´›z¤5°kêEkü'9¿Çn¹¥\Hh¶¥˜“îËÚP“šü7Éw©†úÉpçåŒvçÞ[“xÜWR¼Hñ } XÃÅ­û+.Oî}ôDVÖ»)äv´uOÒÚÙg©Â°]1üŸjøY™îÜÉvÿAçR~i…Åóð?ü-€½i_Œ6xvùJþ$í{Û¤3 }à4¤_€¶ý-æËѦŒ¿éEúÚŸoö<ÚÞæO›1zšqåÖV4 J|a¿!ùÄ‹Â5jšw-wâ«\öãõž¼¡~ݘ‰ü­*ðºOE: žˆª‘E~½ð{!L3&äf¿d›ÎQ†wð2ñ>…2UÓØv{ Ð0G²™3r³¾²˜#ßOä{s£8ƸsOð 1 uz ýöùŒ‘.,ó(§²p†×Ý÷öÂ//dC`F²W‘œ-3Üf}}ÒÓ9øgT…åÛ€ñõ“'ï©“ !:¦Ð7gäyΚ–7cR6Mâj.;+ã4¡HèºÏù‰…¾ÂE …ökÁ>À‚ü¶v¶=òÏíÛ¬p‰àC«*ÜtŽ4ÔUx.†‚yïL÷õ¶R íéÎôÿXïíè»óN—†ñ0êrîŒ<÷µV¾BçH8ÆÍðf½C÷¯÷Là×õâô ¤3@­ÞØil¬|ow‡¾5ñ MÏúKY¬0tÄómNÚ““w_©+16 ùr"‹”Ð^áÍþÝ¿uŽûßã¼}~®{_» mÝ=†ú¯í]¤³<|OÅ{õ#Þ«ˆZE˜<‚üž¦¤æ ÕNZy@Ú mh²±Ð7N†njeñ,Ÿ‰4ÿ´Ò×49qú$÷›ÖoŒ’Ä ÌGaªÅU¸~ä¬Eá™ð®BCò~W뇳íµÂÛˆû¿¡ÎÆ™_ØïYçºß°L3^·žgë^mG”eøŽ6”R›_ûýénw!¬G¾ºoò/„{¿Ùï×tN}!úŒ­hg÷Ø$¦ „ßÅ5ùç{MŸ@Ã}™7}Vͪ_ü¾¬/‰ÞæW¼ãèg¢*œCÂf³-”i÷¡¯Nh4ö–B¿Ðð•ÏEgKÙohp1¼%È–ì\¿¨ø~ÌÝy§ÚÃYç™îÉçCHþ< —þõ@>ôfKH(7|þŸÑi°üZGÄ;BÚiÖ²pþ*üî‰Nõ=›¯AHvàwå%í4„Žy”]W[ñ‡u¿ºšü _Ôÿ˜ÊCv”í/ /”q¶ÎöÙ#¿èL(~uí–yJ5è.ƽRm¢‹sØß´ûè݆ ìE¨ÓÊ!N‰\ÉEô‡µ8#…“GZ‘Ÿ‘æ%FZÛ‡ð÷ê* Ù…dòOéTß"˜˜#×!Ì3øûç1Âo|àZÓ_ø/;ʶiÓ­û~“+h”ã(÷S(c[ªðé áìÔåý{8JƒŠš>²póáWÿC:'áoxC{&~£¼ʸ·P_›oåAj¿B¾Ò!ß\qk^^7ëºýH:ÐÍ^ƒk½;µs ®+_÷?8³Q¾4ÔïÇxoéC.å>CWú·æG¬åÇHül^ªÆR–À£s¼ÿ‡ò>‰w  ¼…ü}ƒw~_¼K@Øy3œö÷™uY¤Ÿ¡þk{—º;÷ž¸:Ñ%´ºC¸xï|à6h+¯×©iò³p~èZ¢Û†4׿vÈCÔí¯•y€ÍoU»` UÙ^ìÎà.Œ6fCÀ}aºSÛ>o ¬­ÑNÝR¬Ï‹vä.­S4Är'ê⸲½½w'±û ™•ùÂî«Ñži¦0Ú½ÀGk4!Ñ÷Iü3WÅð·Òc KñSCG%œ¼hæÎÙÌËÇÅ‹@À0 Ò&Ò¿<š¥{Èk+͹¤Ì_A§•aéÌæ }¸‚ÅËÒÙåºOf Ý¢ 6Γ”¡Ü†_M‡fipAf¦©å¤û$d*½ôiœjN—8všÇ°Ÿƒ}¡¡ü¯ù•ÌG¸ýíá(,Z©^ÈÿDM8ÎÊ÷f}`^ÃãÝß;+¾CÚû®óÞ€KY÷ÂŽg¥nxàÿtºcCy˜Ú÷…‹/…öˆZ×g(hª>ÍÌñB¸‡I_Ù7cr&_7=7kNØøqÑåJùÔç+§Nqd¨Ÿ&þå2€Nv E>áÉ h)fý²ôP\§ ƒ³kZ¢¨ ×ý8â~?àˆ¡Ì”[òóª/W5Ãü¿HÑ©Iu]¾×ý¤•'JÜ(èjý•q3ò÷–h뺦`²S7]FNÞuÊ0f #½ìžegùk G<”ÿWÅùîov®•g·–mÜí“è˜â”?áÉÔåB[ÜùLÿwÖ¬1wO¾:ÅÑî-{½Óè†îS?ãaº ‚â$Ø×.£÷ Úâ™Èã¿JJññ#D5MkE©-åÂGÀ+÷¿ÓÊ;>Lï”N×ïqÏ·®Ñf¡^ø_¤q¹Ð1Z$iëìb)‹=f ß-Úi°½ýÔºgÚ™êçú¿¥¼ÑGDÜ\4ïF°žŸqBÐGâÝ¡‰ïÜQñð6…ûDz²6…Þ·~'ºmøOv6MhEsM”ßxyú¦ /ÇÒâZÙ速‰Ò!Ï.˜ä~ߺ±»]û–oÚq=®?bÝ‹t¤¥E1±øuÄw_j—Áß}v¿9“P† Ú¶5éZÖWßkÑ:³oÉñZŠúp‰Ú¡îN„gÔ\1-;»æÂÞ{€2|ЮŸê·>ê€ì±É`r“­ºúeÂÍÀªVEÓžb\,»DKø„8¹ÓÙÆq£%$ÓM›‘ëÎAç<^?¹fãAôҬʆY¾f’É„_š¥ X ÖVoÎú!åT»L—M!@J4ÎÐ~I‰Š5;ÚH,4>ªËz}îi¡¾?ÿuÉIÈ{åkJè>•KK•ÿïpo¿áŸM* Žá´Ø• *´»BuËôL&Dƒ>ø”B`¹ŸâöéŠ4ë‡û'Ðh?\¬Ç‰®ûЩS®À.' $S”dS®ñG9KZµvdØ…dòß¾} Ù –¡;é³ûÒµD;åýi踦?ŒD >csø :ŠBHázÅ’VE@ý¼ fŸî§¶šKzÆòs³Ÿ³ É”ð4O6ž#AC‰ûÒÑtÊ+­KAG£Ò.S*H¨ÌÏÍšk’)Œ9®ÉéÏwÐóJ×bu1—Å–0Ø…dºEv¦xgo2½)ån [Q§Ã1Ã|+…¼&\úP˜mRDtÉÔ6X™ó©47ÅúMG{»Š„aö{5c 7SS ó”ƒÚ6S`Ç3öR,ó9hô§òCE‡:ø9ÝuŒù¬Eæ)=2³½CåñÕÚÔñôl†ñö’išeè–é\`D'¬g¾Øl° Ülª2Ö‚h•ZC%7G²ª!ÛJþ>QmxW ñe¤™ÍXr :]%¥£ÍEç© lÖ}˜E|Kç~©°®Ùá˜iÿm;Âlä‘§þÖµšŽxÌÎÌ/üÕ„ AV^S2¨Ã#;·tçÞñ”Þu®þ@c(iW±tÐZ†)tHXÓÄ'”huLÁ—Îi˜y<-õì”4 A˜muݳ´Ø¦pfhޏÊèœN®Šû +Z0˧{𮻊1|û…GÆ£b]kZµx/'ÒêVË_—Z¢ º &Ù—胥‡³Ëš ›øåæ5%;…Þ«ºÞ°|¤ú¢2þV~`›ŒwEÂÜF »Á}ßnh”âH|á­˜–ë6ëË éØSK¯JCÒˆä·^×ÔÅŸæ”aßq³¼(*§ÿqº=LCœÓd]| }†g§w‘î=Éž&Úà÷I¸¿±§ó(|ì×ì’¥m°r)#0·ÚU¼{Q¿ë=´¬/Ð>áù:Æ“·¿•F¥Ir ýwj¦0mݳ)-ØÿNæÇq¶w[…P+ñœ_¾ï8Û:Nˆ4’ûþw‡õ®þtáâr,éù ârq9æƒD\ßY*¶Üyw ” ÁFûc¡ûV ßíÄûéN÷${>ù¼ùp6ߢqÉj"€ù«ÑX‹º¤ 9bÍFÓ&¶•H[]Üö¿”Õ¯UÝF£³”N!Žô«º$ªLúáƒVþÐÞ`ÝÛ}¬4Á·?:úê.%ÕX[ý*4ÉzÊ!Ê‘`t“íz¸z¾ ›O²U;'ãÞ{;XšS²3ܾ­ü<4È»Úwhõ¿Ð´ªlyŸDgùÔõ9Þ‘°IƒD/ÀäÆI˜|uÖÚ<5 ”‚²ºSâÄCf¢¬Lœ !LìíHƒù¸|$ Ð4dJ¶€ö?åߒ@ˆ‰¬ÎÎd¾è¯~ˆ@GC¿"Ú¥ëð¬T­L þ¶  ˜§¨ÕºÊ iÓaö$ÄQ<)©Ú—¡ë§Zñ‡9.ÁrQû„¹¸DëƒcÔ/:GZ%t†_¢<³Ð_D*AZeü¾€bBE‘6·š‹•O´H€¿áî)ÇâýØBnüNdz†çh(ÒÄû„Ï4›Ã§q} æÐÇ]–uK×õÊ‘%ž §A£õ‚aƒŽ‘Ñ×LG¨Ž˜Ì˜Já‘^PVœ±c-K ~§( œW?Y…KýË}¾~8†m ª‰ãeNP#nÿB¬x¿+µ.0нIˆ³üšÇ$hìùIuɰï»Õ®â™¨õ]·â# Úµ—Áè4ô¤A6±Ïýu2Ú®xºN÷dÿjù=âáà =óU‰£]§—Ñ ™ërØ£T­*ßÜ…Æÿ;ÞGǾ‘”:V<ðwƨ|o袒~„] IǽÓs'¾î=²Âò±yp6¯âpi¢& hÇ2j7Ð1FéRÖlìUNB&‹…Ÿõl Þ9aúê3pOóo~_Ñ‚ l&a¡«d؉~îÿ12¸ˆ;ùÈö/;Ü­˜®ÑP3lé ©1r—ïB~’"Øb5 S{!ÖªFósÜþ|ŒÇ{¸ß'ÞA\G–ëÛ=¸6–‚õÐÒ¿)4Ö–áúñ4TK„Ø1†ÙoÒ­ÿÛ²+-gãÇË7ï<Õ” oWiôM_uýïË…‹ÓQëNÔaaLñibs]Ó´‡Ã—b6˜Û~αÞêq8̽޿±éò ÈÛ™øn›„޽SŠ3åí Ÿo„Q°AŠ'q^wçQ…ÍSŒ|¢á0únï°ƒŸ‚{êˇzÃDMh椀}±DÛ·GÁ¸ÈæR\®ç 4zqçr<[Ù»;sI¾JâY›wA›&àVVâ§g>…Þm¼Žk!\lF:å¦hA‚I=]]Êb%Ù]ï‰ÞHN™Ï%VWHä#¡×ûty ædÑ~{»ç¡=¬vìMK ç3ѤŸ mƒ=ŸÇ ´ù?ö õ†?@ú§û‹[æ¿kJBqž†öÊ ¬¾´¶»8r­õÑs¿š‚ò½fF€åfV¡ÑßçËD)|K5€´¦5 Vœºpì# ³ÔºtT2òp·nt&¿èq7Zaœí][ÄêÿU Â=¬ë±ï¹G('–‘ý:\âi¸c¡+áË”-;Cü~:rÈà;Ó=îï± Ä5X€V’ AØ”«„ñoPÞ“×ûï=šëåb§~" ,xÂë^E±¸¤ã­ e`É6ƒì”ÿ‹F´Ïð¢>¦c}îê²EøP²rËíhãC‡A_9õvèÜ>F¹=4"¬kº"ôZCÿ¦å»`¿ˆ,ªgÐO-÷UL5ó@]º”¯Š4y»'{¤._±ò©žŸÓ0Àv]ˆk„«Ëk3ª&ÈRú @¶î„];×ÝŽlw! `eq:F8ŽÇ/+ µ„àϞȫ|Î(}¬aWÎwáw8ˆ7±>ì=¹~±b«M)Û…g8( ë~´Çº–ÅŠ¿ÈUÔ çë¬ßÁG‰vÙSF ] ¾ŸØ_4ÁCûO#ã‹ý%üÄã×»ïà :Ãè½¶ó¬)'ÉÐ6ØówÑEÅå}·âœêÉþœ~×Ý~cþ7 fi?ê³Ï†0j¤8/[þ¢9Ò\ÄE+µ\UQbLA˜Œh‘‡tÝîW>Œ°,âóÓ<—G !û×é¹ÙŸGºÏ×[”[N]•”fúÂö ˃©ƒvl¯ “‡‚<„ùáÆtJå*íg5Omqèteý+¯É•Ö=š ÍÝ4x}h+ÑÇ<ã×Z÷ãH.„ŒßIp Yõ—î/)Qdg¸k'ÏŽ%O)Îß–ùh&•îü>Ù¯ŒáX4i iÔ¡QyÝòóDî„ß Ù^ Áx„¹öìš#éžL%³ú;šh‰2®C½÷ãy k4ëL×?ÕÝ1àƒˆ8ÆÄrwèÄŸa+ ZBM‡Éè¼0yUm©ò/²Ggêµq€ ¿rbüæ6¬»ýlhÚøëI×B5ÊUþ ¸‰Ó ¡]‚ã—øø£ánŒ_ìm“÷Çs0_+z:eiËÈ/¹ÒbŸ™>ª «×ÝÕ³,n0ÍàÊh;Ìœ9+ëžÃú…œø”‡Ê0eCú.#ÙשbqÚ6ĒѺøÅcôÚ»)†Oœ±NÌi8:âÙ›Ew±Æ'Ó´ ¢Ô8mõuÁû/µÝÑÄAkù£Ïyõs‡ßçaΊ&ûiÙÐv²k‰hè ³‘爐£³¼‹„¦š8˜k +5BãêžÎô‡Ãú•êxZB(Ü=ôWÓu€ ‚ï˹ô»\¯ˆZ+>¾¿OtnJ–ëúE¥¥†£öÏÄšJ…¿¤?…Agù›=¬ÓU)ðB#SƒÓèžS8ß°ûÁ,“·Ð´¥µgѹMà¯1 jX…‚âÓ¤Þݯíœì aÇ]aÖ‰íz‹>%»fhq„'°<ÜÃØùëÕéÞœÏcb?‚½Á³áÀ†.!Μ+;öËŸ­ºµÇæ5“n h×1<ÓáÚž¶gë çÌð)3  ‹C…dŠL×ýôŒÆÃÕ¹,fâºqN¸LŒÉ™r0ÞO,-&6Ò°x8?õ¾Vû»$ªV™…wö(sMtCŒïò6Î61iJÝ6 -2Ûõ‘Ú…zãª1—ó%´ˆxÅ™ø =Óô«Ä 5†‰p³ ;{&z{©­öëjj¸UG"­œ&Ñ3¡ýÌÌ»½4rÇŽ ÔH€åñ4ï›Xré3t¨˜‘­ºøõ²ŸÂmìAF»ónõÆ;& ©M4—Ž ƒEûm;|ÞÐF‹6£À½ãÍÆÉÙÕ4i°‚;œìè‡\É;G»'‡í©ó¹ÁóD[+L"­[Ëç‘O‚ì¹èPÎǹ³€gCÓ̼;ïäª™í¡·*׆V¾itö­ïÛ=ÐÒ^ˆs=tt‡ Ç€6Oüºc!:€·Ì°&þAãŒN%Vmò `gÛÓ¶Î5ÍqOe‡eŒ#{jëºu$»Uë¼%¥_õ®,¯<˜¶û¥gŽv5‹vs…8³ª¬C]‘ípÀÑ»¥Önº_·æ‡Xà†íÄ\ÎNÉ—ðÎu^ç[<O!­Pó*àØ¼ ÍUiîjmFb¿—é¾4Á4¬Wç²Pâ4Ÿ1t2 Áóý`eæ4¬áÞ$&™¯ñ]²âÇ$Ï:GÃq;ÞY²§}DzW¶üÔvLtÛ išYˆªÇÝæy¸cmù‰÷}Z†mß7ÐÑкð㼤c{WÐÇ[,iöp‚ÂF.ŇްBÞ˜hÃÒäj˜U˜ ±ÙÉÌ £ ÌþZ$g‹,5:@ C‡ÔQÛ·—?ŒÞh´_ÈÏ1ì¿çBx(„·ý¡°ÿKÇù6‡¦] ›­™À¡';ã)1ºÐ—w$Ö¸ü«;l—£1cÖÖaOyG¾ÍÎ’‚“†ÂÆBÑ€á0ÿÛ8ÿ ÒåOwÁŽ3^ÂR@‡¸ô]Cpþ…I¤£•&|<ˆÓMÝf£‡Ó(Ò*>Åq0cø æËñÅiÍ+šé+;Ç4h•¾Hwóïêù•Ÿ‚÷•t¬sCïwwLœ[häm€¦ÿÓŠÑ>Yƒ=­ñ+ylùú" ØÙi]­M40cû'\¿Õx ƒ~ò~ˆt~ÇA?hü/Ó·“­9Ù…¶(×ÍqÔÜ"5ç#p?M(ß"Ÿ±Õ,Ùs‹ñ¿ñŒ.Ršv_ƒl×í30€ž£›QW{£þf!}¬Uí%M\7¼O´9Ši ®’ð1ô”ßÐo³9ï$‚kO…úë&Ž™mËByö•ßÁîùMx-„5úJU\ŽQ'l8"N‡¨JÃäuwõ( ò° yX©‹ŠùXžëU”ùwZ]·o/; ×±=µøI8;O«{æj YË»d…V½÷|[¬Þ´&17, ´g¬{±×6L½g ˜Û}6ýð½x>Ú®§ð!_ìÀ.£4r[>ëæËW¼à7mâU_¤ý²}ã›Xc¤‡ kø1qZ©<¬ÜòZ¸%,ÃÅ[0)ëil‡~=žýC±½wü¸ÃùãkL€°F¹…?4‰o†×3Œs µ}N¨#Ò‹ðwüS d£ÕÑ¥äÐ…dp„&ôsÌF;aœ’ïBøpù<Œ¿cÇÏ“°yBØa6 ]NÓ¤óptгÑQa¦ AÇ¢£ï‡ëO«6Æú†«*ÇÓHÛEÒ›®¯Õ¯ðsúEì (n¥?täç‘°íMfºkðiá4]ðó©§”®×­sëH«Q€á»ôìJÓ½¾¶îEs47 ZØù‘·QHo:†áM3+<–W›`Ö¹0µŠçÀ¶0¦¥›°P’¦FÛòÛRŽëôÙ'á™íIu?Ð}ð N7Ññøy?ÒÈG<9͘ä~¶37"NL¦ƒ%¶kFý@;,w¦:SŽÀ®ãù5¥GC(ÇBä¹5ÊðGAnÖw¡þéÙÔœNŒšHú8Ø W±t¡ze=BÌx|XÝá|^h¸Xׯ,ò£“|*Ò„0¬0B¢hã]x?POZ>Þ£íÅš·ÚüGó.Q4©»\âc¦ZX3øÄ¡ë4ùeKXÛ€zVNéº ÏÐ÷Tßh»ò蹂-ûµqˆ×}ÙÆI›HUP|šˆ¼vr´éåOr£ÄúóØ5³¬Ø¸?ÚpÄËYÞ‚°@.î¢ÝP£ ËþZ´7ì˜@0sMÕR-]9:-‰¦"Ó jpÑqyйþ›b»ý¡‡ÒJþ®Ü¡­\‹Ö€Öùýê÷¥Ôˆ·Ö„sãð!ý7Œ5ÊœÃäùE6Ù%;+:û¥sX}—èÎŽ«¢aÖ% “cSÉ@Ãáw¤¶O[:ônåêªlGé T‘¶6Öáb+ަ~„V==ætÅm\­'†ã@›`ò܃øpü—'6D™é}øâ÷e}5¿Lóõê´¸Ú6îqÊ Å—ùËúcç‡ÂDMðŒGYÈŽÚç÷LXFÂNœŠ_k4ѼKÐPŽ7”q/–¶¾ëûޝ5ÒFô@fcXk»—BK&˜Nš $=”“¾Š’?ƒ– ŒµL'åçºïIþs™@0ÊвÉCR]®~‘V_!a²Ø·ëohj± Jk ‚¶è_ÐÐJ˜®ü…­AÒ娷À“õg‹Â…g͈›^4£Êä¢0&;rðo”G’)Ö2}×A•±ËcO…C4g’o0…dlmÌBrs®i.[K$À“ùZb­s™™ !|Llú‚ÎéÐ,Or:[Mê°‰§%ÖÖéKÏÁÚ×S1çÃÐúŒ@`>iÑÈ$£|ã®Óa’s@ø±’Ȥ „ Ïš!”›a¥r‘˜ˆ‘@kù/Yª¾‚°œãó•ç@`†Ý¦\ [ù= }‹û`’V;˜\¬—ÂqYþ¤¬ÏbŒ½734¡S ÿŒ² ;ÒèÙ Ia˜ÌwëtOÎ÷ͬ¨\&Ðâ ° ÜâúÀ¸u&8ÍÆ¬|žRœC#  °­îuú\¬Z¢N€9F:ã6Ð ÿ„u@°¹c.–{7«4Bö8É$#àtª•~]~lí  +hù½ü0+‹$Y¶9;L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜hª°L(;&À˜°ðxžNÝ o웺‡kõÃcÇ–Úï%ëùmž‡;–ùK÷›ž›5'YóÈùbL€ 45,(7µãü2&0™9Þ1†!nG{aÇ561”’K5M<œŸëžž°„ãqf¶×m1I¶sv*˜0a{¢ä(˜`-žïÌ×âÀ˜ÀÌ™3³.~Û0Ô™ØeòI‡&&ʵTJ½Ÿð‹S Éý±u šæmJ¨œR}׸ö¤u}Ûßù¸žæ)ÏZ×ìÇhÒVR›¥ C+Öˆ#ì[^÷)äS·°ÞJã\úŽ®—;#a_i®Oé7;&À˜@K Àå–PË\F&À"ÀªÝè¦RÚÆˆžpC*m³y_‰t”š|‚mŸ1î<•.3gòQˆ¨?lÚLáµbóÎc!÷ÖâiËUš 3ŽÒ2õB~6}Rv@H&?0‹XA|6òy9ý8).ƒðþÝ4ïø¿×l'Ѥ];áı^ ÿ+(äóPØ/¹/„!HP®t†1’üRë™`Ík”›{ sù˜¨‘€tȵÊ]±R¦ɳRº)PKe¬%?=´¬/ŠŒ¼5¤AÆÏ¯é„âË…E'ÝûÓ|ü–†1€®~q?l…§Ð99Uj@ƒMKω¾•Wªþ—êç ßÖ)!xùc¼ÞžÓÝîÂ[óòº•”¨ažq“å%ômÚÈÿgÈÑ Ÿá¹·òùvHq§!•ù»í&wnÿǽ9+ 8DÙX› š3&Ь ° Ü¬«— ǘ@m\~µ¼ž¤4úÕèWÊ~„…Ãé\Jþ<iÀ,â9˜?\ïñÌÄä¶.&Ž‚¦ùik 9CÊ6ZÚG5©‡Æïr…ý4ÇÕüÐýVŽv3Ë};5Êä%øù¥eêbh­u­µ“ë»hÓÆ´¿OIØ';å½ø\Øjoíî<‚ÿ&­Ð·xg…gÃ>ú¹}×AÐj?>5¾Ê˜hžXPnžõÊ¥bL J]\Ùk õ¼U03¸A òî•0X9rÈ^«¦Uyr9ÏUøüÙEÆ’“5±D‡f¶‹t8L³ ò¢)m© 1 §6«cü)îÚ®?æ¹e„ò7aël ÊÊ—BP~gÚĉG mÚZš˜å/Ur—^v,´ÆgÃìâ=g$Êb®Öñ¡0ä)Ūt%ÒÖ\®Ö(GÎ×™h–ØF¹YV+Š 0h fB`4ÇÐzÊáÂÉö^³Š£…&³-m1ù{“µ&saºq!ÖY…óò=Y¿âpº¾‡®ºXùÔèÀµºŸ<‹<Š•,0ñN)¥öLQE™6™rÀ¤âMøO€|Ì9Þ°âu(ùŽb8ÌMÈ>ù—©žqë­{|dL€ ´¬Qn µÌedL F=]Ù/éÞóhÓ¬fq´Ú;šS-ÅN}ý•_œá*ö/çOÊzŽ„Æõ,L+&U]´ŽæÏéž»6fºóraŒ<kW`d,A'K”níB:žˆvr\ºÓ=«È—Wˆu“‡6yý‰C~LvБ\lik³ a@c.ŒÖ[Óòu¦Kuµy&i–¯BùŸ©ºÌ&À˜@‹!ÀåSÕ\P&À" ­2V—¸@“â:h—‡)e¼¢ûŒ…’_ƒ–õ(˜\\3ÃëÆ*°è q­m_ÅÅöøëØÆÙæåÛ"ß›}Ÿ&Õ¿ àžï÷©ïü>ãC‰§`»< +i”…úôÛÔ|kây˜€?/Ú5Û‘ÂD›¶¦©OQnZKù£‡ÇŽ-µâ{Äs;–¦“_Ó=‡”ÚºÏG&À˜@s';&À˜°ÈÈÏw¥¬ÙØ«¢w×µ™™>û½úœÓ„¹b½¸SÇv)ï?~g}âŠ5lc¦k^Ù?`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜hdCäñúœ)Ãü†ÿT¤ÕWI•ŽDÛ5Dºœ`±PBì”J!Ô*‡æøxZîıÅÐt}s;ÕtëŽsΘ@Ë Ð}TÂeçéÔB_áMBª[…½¤†Prå"$º«eT)—’ 4-h„ÚBPNÇ{ÛM)¡ )Öâ½ýOOWÏÇ=žkËšVijÏ-·Sµ3bL€ 0d!Ð}TBå wî)€:m)åGø{Qk­½?mâÄ¿“6çƒ 0È®Ÿ2¥“Qbœ©”º§áCw5|.ðæ|9TÓºÃíTÓª/Î-`LÀ"Ð}TÜåŒïmÊJ©~…fê–ü<÷×VÁøÈ˜@Ó#™ínñr¾ŸÔĹîGš^)‚sÌíT0þŘhªÝG9â ¦²óQK!_®®gäŽ]Ïø9.&ÀžÀ‚o>_uè‰?'’½¡]{舷/øúóy Ÿ“ø¤ÈíT|8r,L€ 0d è>*neÆTJ~€ßÈ÷f_ s ˜’°cL ¹€,3Ýy¯âÅ>¯÷MÑ ƒÛ©æò4r9˜`ÁÕGiÁÉÔíMˆAÈdn!\]®a!¹n9Hfæ{÷yü 3ªÞûdÎrPÞ¸ ÂÁ?˜`ÍŠ@¢ú¨¸Ê´º…9q6ɞ̒fEž Ø@€½ßh4n¦÷Ý\Õ&p'ùO¸Jþ:â2&ÀêC }T\eZ’üGÕËa™@Ó @ï9½ïæÒM#Ë•¹ävª)Õç• 0&P'ñî£ê-(Ó"ý•ë$ËëT"Ę@“#AùEzïÍ÷¿ äžÛ©&PIœE&À˜@œijª· L;îÑf"´NrœÊÇÑ0&äè}§÷¾jÇÍ$Ï­ÜN%}q™`q#Ï>ªÞ‚2JÕ—vÜãÍDâV¿HzæûŽ÷í›ô™­Ì ·SM¤¢8›L€ 0úˆgå¬of°%u:â(ªo<ž 0¦E€¶£GŽéýOzÇíTÒWg 0&Wñê£ê-(cÝäv(Ù®¸–Ž#cL é àݧ÷žÞÿ¤wÜN%}q™`q%¯>*¦q-GƘ`L€ 0&À’ Ê ¨…Ûz( ;€íuç´I@ôåèlï•(Ó´ó•ÑîÜÙ™9Þ1Öïæx“÷O”ûûæX6.h‰¨ »5/¯ÛÙÞ¦b_ß«)¨ÌÜáà L Þ¦ œß¤N.ónï¹ÊPãwm)>Š2º}[¹BÖï˜ìøCŠË•ó¸g|Bm¹=¥y<ÒH$,µÒÌÁö¸!1ˆßŸÙ¯%â|LÎäc°jÁl!Åœ‚\÷qÈKµíÑ!ÈOC~/Ès‹k”è†ø‚Ê×ø92&À„À힇ö(ÖK'eäx/ÇÒ†)ÑŒlï4&ó…&gÎÈÍ~ªA2Ò@‰Ä»?àv¸*Ž“I:¬QŽS•dää]!ù 4ºº&Å?.y„&µQJÊO‘D‡ÎbÜú8%1šBÝ»êfÏ}½"zhê7”8ìšz18ÿL€ 4,q÷Ý×®X/þH(õO|P?¦IÇ™N—< ís.6ÎÙ©)¹­as”øÔÖp;œøÊã’Šk”ãPÐ$Ÿ­ ãY4ºôtdßeÓêÒý•I¸ãRä(ÆäL9ØoèÍWH¦¢K1£¦SnÈö¾35Ͻ&2 ¾Ã˜¨$àñ…¹‡qn† œ’ît_YèÏû7Œ.BuiÕµC×Çn¹¥<Ú¼PÜÑ8ŠOø6ÝÍú)ÐÜtƒp»Ð!5÷´Ü¬o¬ðµåÉòzt9Ù>Ÿ¤.Ä4Ü;+ô~¸ß±”£×)eŒ»Á°îøùÏG9*ÂÅ ûì“PÙày0ül‚ÿwÓ½²<žkËÂùçkL€ 4B£h$4ÉÃ5MËœš$$GÌÐèœÉÇKÃÈÁ»=í§mÁ|§H™`²í³3íF¥—=*…‰6¡a¾”Î.·±³µò•=€°#ÐŽ·—JÎuº·MõL\j%\‡8µ­³õMÐx?†°ÃÑþ¸pž~§{&® øuçÝ%„qf7g„uÍ:Âôì;´AÏàÞ´šúƒ€ÿz´_ui‡£áiå-Þí0úÃS„ÐÎG¿w’* õñ=ÏÍ÷f?ÎŒÏÊ™€E€M/,u£iò*©‰÷`ÿ{O¡/ïv¿¸ßqŸ%vù¾Â¹~'⟢”èìâý›Ü¹ýÉ¿_:ãúL3¬TÂäë¤öHåoÙ÷Ž.Ô'?Áü4®ãú8’cÊ‹YÍÿeäç»P¶9ðu¹@™Ðpß[ÀÖºa|‘qwÞÈ@h”+Rž~œhB+‘š¼ Ý™dêÆKÐ¥XʇkFf £ûYsˆ+ñbÌ0„@þÕ?ƒ"ÅÌlïÅÈÿGhx¨“Ûà÷\]¨ò°ø7hl~u¥'0èÉh²2&Û{¹TþÏ!ÜþwûVüa8PvÔEÅüÌœÉæü3«}ÖKg¡-X-…ãf´ÍŸâãù2¥o|BògÄÊ¥ÔîÂñ¿ˆï(]×ÿG¶Ã|˜q¨3wéÅ_£MÙ [éÛÐvN®—‡>ÿü<÷wøFŒ¨ Ùý{÷ÚÚýñ‹æSÔÜà~}Û¯XÛá¨y"oñn‡QÖáˆöC%Œ4MÊ,!µÔåßèOû± 2ì¢"Àå¨0Eö´N, Á­lÞGöµûN‘¾äFjÔ Øß¦xsŒ{ò~å7Ó=ùŒ|oÖV4¼­aóü¾~³¬k™ï,Ã'~ð ù\{hFnÖW˜ÜCê–VΔwóŒ_kù¥#ò×ÚŽ¡é®½± ó±æÅg¸sµvÓh¤vÓ¥<Õ“ýs•Ÿ'G»½+…a܇߇[á"åɺîX¡W¤Acò´¹—Ãü‘ σŸxîÜÎ/]‹¶|÷ÞÛAíòMDçfxÝ×YñÝæyøµb_1:ÀÝîæGmU¾qÇ€úÞ oÎyUwžÉtçÎÇÇÎS¡õ·;$Ÿ1&Ððþ!uù¨Q£ _¨ÙÑJE;¶•? „|mAàcÜãùâù"}ölÃ0Á{~¤%dQû !|æŒ<÷䪘ÿ‡ö©5Ú‡« OE»}£uBÛNC¨Üuú{áÚ+'ˆ£=>¸_Ï÷ºï±®ñLù‚òb]/¿×è/jWSö+–v8ž‰h‡Qïù[{:Ý×ÚFkŸŠ&{d °ûË–qÔ€_îC¡X]êBh>³ Éf°éÞ,h`å2|ù^r:ž³_›~Oö¤·ÙÀW±ýzçJºíBr¥ßØóRS•fòg›lzGGí«FtPø°y òôCŠVæó*®ëñ±‘&ôÒ‡ƒMÁlÚ»Éì@¬Ý4©Ö¯%¿Cƒv 5nöÄ2rî=ÃCbŒÏí×£?¯´Á«ðûɼ!:ç¼ \4Süä†Úö‰Ü ¿ý½°x¸C«:Êò9•ÎQú…ÏÀB»]åìtã„ÝWpætaml,åS˜¸ÈŽ 0d'`ôîú ÞÙ¥BÿÎtß?¨¦ü¦vi³£be–¯¨æOW@h]ÓC h«yŒý‚î÷Ÿe5Æó@Wô)C  ÿ6p]ʵ¸ÖÖH\à V×cÇ¡?H@ûUS; φh‡óܲLÇ=×û—ígçÈçL Ö(G"Ãõî^¥w@IDATŽ¬ç ¼ó°Ä̜¼CÇää½è„8£#V{Àd?­5f?Óª¢CÇVÿ‡YÕg+½ô#,…s›Ó•²Ô0|~}*¾¨ÿìá8æ¿1$ðzògºè¯­Xâìªwh.—?tõ‹€çª“¸ç¥­+Oîò]‰EG?Ãê U.T¢¼?Êýó„æ¡¾¿{8çê]„îâTt†AÞhË÷Dž{lמ‡?«‡µr8?ªð«=‹ô¼;¿=íyœî¹kc¦;/ÆÈSPšK¼ˆtK”n¦¨œÒñDAî„_ìaøœ 0Æ#@£|™wç1 õ?´G?â]@8äN¥m4 ÿ`LiÇd?Ç[Ós'þDËf¢ ¸mùsx¿7¥Hõš_¤¤ÒwÂ_óª+ã­…D»ïÃr¡ãÐwlMq8?÷ù.†^þæ<èÂé˜b‘“Âù¼"þ{QžûeŠúÓ_.Ãhæ¿áÂýn©?HTû©Ž…g"ÚaÔáÕd*ƒ‰•_·©ËüeûéʘˆÈÍiZý¹›Ÿ1ÈX£™MÔwhø ˇɣñ¥Ú-Y0ôï!´~Ú;°$9A„"|𮻊ÓZKÒ¬€`ö¶ÏW¾ÎooA‹ñƒ«v,Í®Ž:a›Gsé#¬×‰8‡TµÔjÂï¼L˜°]K•‡Bhý w¼`¨òÅÐ’Œ•<ÆÚ²×S³ÓRò:«»í#ªRˆ¥|=]=¯‡@ÿ<´NO”û* •¡ÏEœ»0ÌzMh†é£G“ê_`}>&ç|ç÷¿`báSè°†I¥uX¡aù7` O Rög©Î”ò'ø»IøtCŸ!ø||ôž‰vÛœ§@9›‘›ý ©ëѤ\R®ä2]Uü‰÷û"׿çf¿@~âéÐp•94ÇÙ†2&”ù*Ö`‡Uúàï 3¶ eÄÊLó@Ë¥`L MéÝoJy†=ûiHP‹©-¡3‘›Gù¸L 1 Ä«ÝgӋƬEN› 0&À˜`L i ° œ´UÃcL€ 0&À˜hL¼êEcÒç´™`L ÅÂõŽ”ú ÛZÉ-–œ $+”“µf8_L€ 0&Ь ä{Ç-Aé`IJ€M/’´b8[L€ 0&À˜`K€åÆåÏ©3&À˜`L€ $)”“´b8[L€ 0&À˜`K€åÆåÏ©3&À˜`L€ $)”“´b8[L€ 0&À˜`K€åÆåÏ©3&À˜`L€ $)”“´b8[L€ 0&À˜`K€åÆåÏ©3&À˜`L€ $)&»áÈõ9S†ù ÿ©àÚWI•.…h—¤Œ–-%ÄN©d‘PjµÃáühZîÄ K¬#æºìæú|ćNÃÄb>Ë~ý4!eŸßN ±Ê¡9>n.íTÃ5è§’¸}hR‚²Çótj¡¯ð&M“cuCïAÛ~¶kjtlßZ¦º\-N;^æóÛv”¨%exx3sòÖéºÿ‘öem{øá±exŒñ 6 ÇuÿzjNÏGüé$,Fyûí¥îHÝu³Óé¸Íl§4n§líTµS†¡êéêù¸Çs-µSì˜@S Àïvj)¤ŸJÊö¡ÉÊîÜSÖû‹ž‚ì×sÈ€žê°ýˆö”­S[9PwM%Jóã ¤¬\ü²´PüðÛòî¿.]{_iÛÒ[¯¹Ë=ú™¼£ þ’Z`æºMØãÖ,ž„щoÄÔÞ»SKSKgh†L2 Û©JÆážÃО߆wÿŸÞœOâ[ˆ+~·ãгZdIß>Ô[È6ü„k¨Ø ¾þâ:&¡1½])ùlϮێ>„vú1Ce¯®„ËÙdäüD` ÄIˆÇáû{÷ë.V®Ûܺ¸ÌwÙAÇWüÓ쯾¯ò˜”Â2×m v’¨ç£!ÞýxAIp^©¡wþsÂÝ·;])O¡jÃíTõš ó¶ÙQ\~ù¡Ç܉þc^õ|… 4:~·¨ Ñ>Ä«Ý7%ùâP—dd¥ %:tß~Ú¸«Ïr êÓ½.ñ´˜0Ägü5g9Ý·¿t8\÷_3Î=…wá>ŠèË8Y×m#ÔDz>NÌIÒûDï•‹Þ3zßè½ãvªvŽ»ŸÃ~šRâ!jç*™Ú§Ú Á>š3~·±v“­}HfAYþk‚çTh’@磮;ÿx™âª·¼«¾á’&NÄ‹¸¹\­&_zÓ§#u–©¾“¡3âºm¸Ç¡ZJMàù¨–ç$¼@ï½O.z¿è=ãv*¶Z²?‡ÔÎS{’¡}Š­ 컹àw; j4™Ú‡¤”32–Ï:<¸Ä-½KGѾcÇiûïDDa Ëuˆ-~A¸nãDz>1%ëóQŸ25`XSH¦÷ŠÞ¯^Ý:)n§êFŸ¸Á\EP{OmCÝbáPL nøÝŽÊúG” íC² Êšy³ß0Ò/>å}Y°‹q»ô´#5C‰îCO8þFÄÐ dØÝ˜õÎu{U&$D’> )kœ#¥÷‡Þ£Vô^ÑûÅíTÝ ÓsxÉ©G8¨½§v15fûT÷‚pÈæ@€ßí$«Ådh’±A"í±ÃérܶÿÀ^ŠlUØÕñ#ŽmÚµƒXH[“‚¿Æ²W溭{U&$d’= )cœ#5ŸaÄIïQ½WÜNÕŸ°õR»Ø«}ªA8†¦L€ßí$­½Æn’QPÖ®¸eüaXg³;–€£·QO×±ŸGä#hi6äµQóX[âU»œô‹€ßÆÔ*'UÝÖÆ­¥ÜO¢ç£) hœªÞ§.ÉÐN5pµå‘8R»Oí?ü&cßT[ø~Ó&Àïv×_c¶ɶ¾šùEçj•r*m&Bë$7V½mú[üoÖ|±xÕzápHÑ·{gñ¯ó†‹íZ›‚ñ‡ß._üð‡ØUR†û𨻛¸üô£D×=Ú›Y~óóbÁ+…÷Æ «tÁƒIµG×n'â"íÞçß ¹ÆrÒÔ-ÊÍÎF Iž[Ž’öÔ|†‘;Ò&§ÒûÔØíTÒ’ªCƬçÚŸ¿†lŸêcÒŒð»ä•Ù˜íCR ʨ¯~´ã^cn&òþ7 E…Oÿy–(.-§µ‰M!™ž¥÷¾ùY|<÷qá‰‡Š¡ƒûˆí»JÅÌO¾>ÿ¡ðÞpHq%VpíÒZ©NgdŸÌ/h7¬ üéøk(u¸Õ5zÝ¢Ììl’äù°å(iO­g˜&ŦixŸè½?ºÎ®žÌçíþÖòò~ˆŠÌ/²}ªgî9x'Àïv’W`c¶É6¼EùqMöÀ¶Ôš·E›Å>ýzˆžØÈcpßîâ”#÷7#2ŘõÝobø!ûˆ‘‡ {vh+ôì"®ûÇp±³¸TÌþyIR>n;´‘—³+2—Š?ËN¹!'MÝ6F­Û´M¼?{!Ìxª§¾a˱~óöê7ðJ< XÚ:'Uù Wi”é}"nuŽV#`¶ûhÿqƒå†lŸªå…/´(MâÝ&3P’Aêã(]§å¦ç«}H.ÕgeÃèÔ„l—êr5jtÐàÞÐÿ*RRœbä¡ûvܼm—©iÞ·?µå»]çŽíD—NíDÑÆ¿w_L¢3âéZ[d‰l”IP¦ºoÈŽˆÒŠkÝ’y iöχfßúAbúÿ¾íZ·—Ÿq4ý¬Ñý¹rè×£³HmEJÂŵ·Šw¿úIœ~ôP2ƒ Jèݯ~Ã/2/8!èzCþH‚ç£!‹[×´ÌgéýiEïSc·Su-H²†#žÔþ# Ý>%+ÎWÈû»mõO”ý´V)¢ÛžÄñÃöG (Q8͵þÞÿ¾ÃÎý¡„#·vÃVñú»ek7š‚r¯®{ˆóF"† è)²Ÿx]lÙ¶ýŠÚ¶CõX…Ç4 $d;yêíoÄ¿-c¯8ÍTÚn%ä4ž}lcµÉ$(“ô@%T°$‘ê«9Ò‹N>\´Nk%Þúbi‹|É©GŠ!<¹¶­I1ìÚ¤¥Š%•÷ƒï$Ç/|HWKH¶f–Óµ0zθæ™Ò ¿¸×-Ù‡“™Ì‘ûï%Ú·m VÝï‡Pý¹˜pÍY¢{+Zfºqܵç—øˆ¢hø|D‘»F÷x†‘j7Sªx5zÆš[ªÚÿ†lŸšB.OlönC*n¾ødQŒIÿ,//¼ÿ­Ø°e;„ÜaQç„àG_ùTp<öŠS!t·ÂÜ©u‚öH°Ü1Dz·X²fƒxã³ÄÞ ¶o?ëvàX^¡‹…‹W‰^Ýößÿº<á‚r"úØÆh’IP¦Ê¤¯:Ä6zpÕ‘Öï¬ãÃöé'^ùxž(xã qOÆy¢c»6f¾v“™o°ÛUR.öÅš¼Îü¡:§N¨¡‡6R·ûዺf oâƒæê³ ‹~âƒÙ‹Ì‰—C¤›“.N‡xì•Y¢¬Ü'~éc¨º5qæqŠOaVs÷èó̯óÿÍúAüôç*1îš3ð¥ÞZ|1ÿ³q¡/ýõhì^þp.l×·ˆN˜àyÖðƒÄ¡Cú›é¿„ëÔmÁèÃ7?-7^|bµ|½òñwæèÝ{ý³ùæÑ+Î<ØñÕ‚?M­À'ÑH…&bF4†£$·fýVñ&’þŽF—láøPèÝ}O1æÂ‘ÕÒˆýB£>±g·áCT>ÕïÞ£Æÿ ox beûßÐíSŒ“Hb y·šÃ4ߤrÆR­¤Øù}шaû ¢£q³¾ÿÃ4뼋 òŽ\×=hÐe·k×&UôKïlþ-B2}^8AùçÅ«Ño¸ÄÇh íØg! y®©o!ÓÀ>øV¬Ù°Å\°àÒÓŽ2MNɌà ö…'.>üv¡ø{G±)3 m·}jh›wÓ…»3]׳FhèáHg}ÕIÀotAÙ‚Ò_mÿ¢Èœâ·åkMá»;†ÜìîØ*÷ë2qééGŠV).Ó¿5QáóA.ó~Yn6>}{ì)^ýä;S §8Þþr) O¹ù" »¥›ÑŽ>ÿx{ôu>oÄç£ÎynÀ€gišïP¯ÌBËHªª°ónçR6û³–Ðw›„Wê?ÖTõ1Tàr´ùßÃÂúû%D¶X…úÂ<Ð’k‚D\Z±‹ú•pŽ´Èê-öß«ú6]ü‚>Ír5õ-Ï¿?G¤¥ºÄ­—*zvé$^þh®LlÙ¾K¼øáqFuÏ=þS9D Ÿp}l P=N£}H&A™ÐYl=0Ö?(iiâÕêõ[Äf {V ™÷«Ø¸u§X¼z½©qîÍÞÞ˜øg9²VnümÛYlÝjÌ#Õ¹Å™Ž å¬4ãš1>hï¾æÄK(C5{´o#Ù§/Ž­ióSKL_ö½¡õ%G6Ê´ 9¶!6Mj¯Þ€zÝ!vaµ“#ØKü¹r½¹–öR؇‘múÒ5Íçâ‚“3µÈWuŒ ¹ßBX·\Y¹.îºê qâáCD›*-ÙýøçJS»=æÂDλ‡Î¬pÖñÜ‹Cöík6<ô‘ö–)$÷ת æpÙTêÓM””U˜ ’.ÇÆz>âõ„Ga=ÃtL¶¶3á…oà,Ö œ,'×B XÏ[Bßív­+Í·ï* `¦%f_ùè»ÀßÇ1´» [·‹½zÕ¬€[´d­˜öÚgbÜfš6Ì'¹Ÿ= óœFÁÿXQ(B_H»Ý Ð Âù²€¿H} ­úµJ¨cÚÛœûs$l¬IûL&!–£>˜úºc,ú@¹ó”=áúXËŽV}Å!ªÚ£ aøds Q 4¤=üøÛ_ðÐùE iÐÄ0„ÉqÌP¡A#ùé¼ß`DË} Sк Ãèö/9Œï{æ=ó>ýwöñ‹31äÑÈÎzÀƒsÜÓ$29𼋠s.à¸;™?V™Ë÷MyêýÝØwßÞ}­êŒV:Y;/Š‚V3„Ÿ—Ì!§­¦Vwß~éâ/|‘ëQ,×BöV|Y[n`ï®AÏ]'sžça£––š{³Ýa­0ö#i’ɵmS9ÔFZrCô0M@(®¯üKP£gGt¬¿8GÝ,¢³ØÐ‘]â0ßıå˜ÃHø»½ #œähµ,ËíÙ¡]Ð~ Öd>ë~»6iX¢–Vsì`· À6â곎5û—³ºh7ÿ¦‰ß{_ÿ,>š³È4“Ø!ƒ¤x‰Ô·üU¥(¢‘YË‘bhíÆmbªrÆÛrXÚZòú­ÌaÅUñAÛ‡ê4kÈYK¹EÂî8Ø¡’ Fk)·ÂÊvGBÊéG`þ•àkë©w¾ÆÖߘÈWŽþqÂ0AÉå’Ǥ%Þ\ÈDæxØ}ÑäË=i¼JG6¾ QŽvã—} 1þøÛ_ÍÀûôOƒ°‘ ­“ýó_«1k¹½iWF«›[­3 Óä6`daÿ½zšçô}±‡:ÚÁ‘²·¾œyìŠOõøi-îÓ°jÆ4L@¤phd〽aêÒ|Ÿú³ Cüx‘ÙÎä§ÞYÿ:;ðA.ÅÚ®Ås†ymiñ}&Ð| Äïݶ3š»h‰Ù7ôîV©±ß‹tÞ ¦¬XS ¿9¢Î™SÔ6AÌ.ÈŽy_ôkäHÉD«FÑ(çј ©oImU)ÿÜvÙ)¦¶Øž¾¥¨ŠÔ_Ùý6åód>lÐ/„h*4Ë¡Brh8²ýæðú#/~X#Ô_ÿnî ‘†8{øæó¢%k¸i¥2ŸùìûßÍø¥UK–BcLŽvQ¤2¡zéi2&Ùš/+Ü(† A¡ú§%zhrÄþ•‚0iirM¶#ӛϱC#€eyjsíÝÇ´y§æðœß¹#1ÿH°Ü·–Š[aF©o!Å­òõÁœ…fÚ>¿øuYaTI‡ëc£ ½§„Õ= ÁªRûÆ9§Bã/1_s‰*i Ý×c®…ëÀ,×$wUŒ|x=áuKÃAÔЪ–Š/mºöÖ0~íÓïMÁ˜Ö\¦-ÇÉvxİ}Ì]ie“ÿ»ýR±'Öæ5±Ë**_ÏdŽA»1’¶™ kÝ|ÉIâéw¾wO{ÝÔIÍØ¥1×>iÖñkŸ~g΄Ž&Œå‡6À!­9M– I!ԀъTž8¹Æz>â”ý„FÓ`lè¦aMZ—ýWØûí…5Z•…žroù£© "[x²_…å,ŸÄú¨öU\h†9iªgâ¹_ƒuX»áÃðèb•}Í8jKcÝæm˜´3ós¨˜Öm¥÷)Ò x3Ò¸üg¶ÿë¸ÄÈ‘0ZXÏ›u¬Å{t··í(yO¾#È|‚æ8Ñ(")Kbqdòy–˜›‰þbÊSï™+2uîØ^Ü€Éé°âR4î{Ã$§Ð$>»£ í$À“ ^Sß’yÁÓlÖl&Gð~cηGö<\K¦‡õs ß>$› \?~ZƒQ«¦1Ά¬‚p¦-´Œšµ”š•—ÓaSN4™fîÒ„LË]|êU&22`.j¦AK¿ÑŸÝÑòo9£Ï5j€hôÁrá–j;l¿Xec€åÅ\z–$g÷êò:=ûÓßB,íóù¿‹É€Hˆ*¯ð‰ü׿s°de3-þ¯q ÐÊ)dƒ~&Ç\…•UÞ…]!M0¾qÔIæˆ-x#:O²Ç'A–:bêô–`t„VqÙ³Cå2–íqýà}ú‰Kðœ/Ä(ÆkŸþ ߀9ù´¦4h)­ìBÏöåXel$iƒ™ µNK1gÀ¥ i|ö¿Îi\`œ:H2ј_†óC“Ê­6ß*Òþ{b"úù¦)¨†Î†–7µ\ÞX§¤È 7?êØƒc’Þ`(^jî[h²û¤ëÏÇJO•¶Ò´J¥“Õòzèƒò® òÐ~$“éEÀÅYlÊh­I»l•…ì«ÂÙ[÷k:Ò$»\“ßxÜ3 EþCn4üFWb«uÚÙ5?4BB“ˆiFùQC‰?WÐê+X›‰é¸B1ÙÒpj¤æ´V뉇ïkî:öì4 AñX.R´Â -ût!–^<Ë>pÅÑ“ŠjŸoÅÍG&ÀâKÀ\;ß&$Ç3öhúw Éѧ^Ÿ>6úTç“U ‰cË13¸ ÓŽõ[v˜;nÙ¾ÓÜü†4¤ud×üôé¾ûˆl}ºnÚÑ“Íüe§en²óõ‹MÛü‘‡ €l?„máL<µ6) õS-) Ú8€œ}6;ý®i<™-±cL éྥæ:cA¹f>|— $S1±ƒþØ55M$&ó¢C‡ôƒIÆBlHó=씫ۨ“Ö™ì“iíï»®>Ã4=÷ŸWƒÀEJÃZû›lke XÓ ø ˆù`MŠ÷-‘«‹M/"³‰úíæFöƒÖR)QdL€ 0 Ð:«´[$ g^e÷N¶Ä¡3ÌI½£¸#­MÛÆOæýuJ$“M›lÀ(™ù¬Å„ÀúÌ€:qöȘH"ÍV£üæç Ì5‰5ÙÔtÃŒÓã‡í [¿Qá§e¿¨£¡™ µ¹µØŽò]¬ª@_dál`k Ï÷™`Ñ å_Æ*-;‹K±SVšÙîÐßäBWq9å¨Ðþjîyü°Á¦Ms4éFù𳇋—>úVÜ3ý Ó.š¶J'{øH3àÒV?š2°&À˜@<4[A™àÐD–›13¼¸¬[*‰°+Ú†-Ûk]˜›Â¾ÿÍO¢ –@‰FP&ÿ䤹âZå9ÿϘˆ–mûjŸén_ …â ­Ôé-/E4=tbMè sšé~*–C¤õÂiŸÝÕ”ù;xŸ>æß®’r‘ í²5Ó>ò x{ì|Θhšµ ìĚƖ ;Ë›PGñáìEкìk Ñ_bi£Ï±8í¼¶7¶*¦%Äh¢Ì0£øá÷Êuç.\Š¥•ŽûbëàçÞ#þÀ'-G;ᜡæÖÖ£0ûçŘ8ó‹¹ýñ9X»×êØjZË”&Û|2ïWl¹Uô†¶†ò@;ç$~­R+×|dL )’­2I†ÝY®ýZ,ç‘6Gˆ”~,q³_&À˜@²V1${n뙿aûö3—GZ¡”ÜØÚø#w\qÌ,üÐ"ÿl^§Eõièñ`,ËtÍ9ÇBxîlšTÖ‡†¯9ûXs7ÚúÑîü¾RŒ:åpS8ñƒ¹æ–“tßZËô®«NBDk™–@ËM[¿„5Hû!Þ[/=‹ù÷Áú§•3Çi­RZó÷ÖËN=±…%­UÚ’}lŒÉ{FÜùðË&7;‹7?ŸoÞ{þ½9öË-îœÌ… ±A;&À˜`L >’MPÆ}±@ÿѦ}órlI.ÒZ¦¤™&G´‚è¤#†˜K8%b­Ò² ŸÒu½É™ŒmGÊB“p4|ìÇÇÅ_«Öåw>>Ph+ñ–îÈ\è§¿Vµt ‰,лƒeÖJÑN%²É7µSP ìB>-ÖÉžeÎ`Íœ@ð]ã6 >ÿÆmÛ‹é·m¿³úgn[ÕÞè{bW3r´ð‚?VACi˜f¤á¥?‡£z²$\¿ôá<±lísùߨšÒZBÉÊYzçŽæ) Æ4,I{«“ÉGMk™f^0R¼øá·bÒŒ·ÄaC`ƒ£²Vé¶Ū¢¼|›•WÛ1ÀÝv-)Oé#†L^~üc¥ižB™\Q¸Y”b‡ºÐú[N÷û#šÉÐÖÏ_Áäfè ^âLrJE=8tŸÀn~5™É¬Y¿UÐŽh¿ÃÞ†·0åéíEÉÖ3’© åÙm?Ï?ñPÓ¤‡6 †©ãaþóÜû³Åö¥æ2_tŸ6’  ¤3±Ü ¼4d~ÖÇ¥-·ÉÕ´åp8s¡PFvq#Pí=©(+ß–ˆv*n9n‚Q;å×õJ Ceþ«qo‚Åâ,3&Є $›FÙDé+//ÚYZ.ÉÝB1ï—åØuìpl¨°'Ö¡ýÎÜŠš‚G2“¡{o¹À§Ü|‘2 . Z €\M¦2´ÃÙ+|_1›ƒ|óÓbñÈKÁ†} 8£ŸÁN~9vB#÷õKÄ/K ÍÎ~°xã³ùbSÕsE[ÓÇ-ÛEÛoŦ´,!¹pæBæ þ/aè}úö¾<ŽêúþήzwÁ²-÷Þ›l\0 0¶`Š „BBB -òOâðƒ$„é:¡™fƒwl\ä*wËMî]½íîüÏy«·^É’¬²»ZIï~ßìÌì̼÷æÌ¼;gîÜwo0ôTÐæk=EýæM5Í3ZáH”íC9{¾‚%Î"ahŒ¸\"?zêU•ÖõâsË‘“ùzwå[ÌÏñ¿üëûŠ$“ë¬W5Å2uƒÈÏ^¼N^ú`‘Dâ3; r2n‘ˆ2V)p´=nw^Öª¯³Ñ`&ßÃÞwÍd‘Kè~‘ ‚Ìe’Ü=:)7 } uq“¡%™’ïõm.…¥™R››Ì D>™¿j³ µµhõVW“»¶´¾m+Ü|ÒðEBwŠ(X„ÓRÛ¨uþÄÇ =q¹K 4ݶ÷Ü{Žý®µ0ზšRÓÛHÐðï7ö§±OÉEÿJjŒž z«›IÔSÔÿh²Æº™´Þ4Ó `h©„QÖŠ‘s÷Ñûßʲ¬»¶ƒ8p@]}媋҅SMBÂò ¢]ÐZ̬V¦…ÕÂ8¥Ì^ ·=Pìñ»¯QfYƒìXœ(×M ‹a嘥µÅ2ýÕÓ•_ªÃr(â§Ë T¬Râ—µcŸµwÇÖÏQ¶ÛoòÇ[WÛ,æ´¿q’ÃQz¨¹ÃýÝdx­ž~yŽÿfµ\5„ÿ<[Êß)†É_ß™§¬Ù—À<´×ÆßU†.þ‚¿Z­ZŸ¡ « -ÍtaÒˆšî]ýòUõX³tüû‹¯±_9œÎkª§‚ÞêfRÖSÔûh2ñõÇ»™œ…i¦AÀ Ð7× Z;ÕChá¬w߯ðçã°êzè¢,Ñ$¹ºòi¤Ó$Yo¯J’õÿzé¬Dxõÿ$æUþëm‘5nã À†Æ+%nÄÏã*ÏýjöG_ >WŤôÄ»ÙÉàÞiêg |}évQUêâ&Sõ®×æ&ÃíóVn–>ˆzrþÈþðQOV_ø ]eFâmùÆl•¢ØA¦ ÷¦}”YWmRÕ]¨¶}Ͷ!àÓO8Zõ%ö+ö¯`멵¶™¤õõ=õ>šÝ¬õS3Ý4Ó `¨#áD”µJÒuêÔ±‚M™+þpàè)D0Xâ5ËÕñ¤Ìn^ˆñË\¼àµ¢"dU)«˜ø'Îs,6á‹ÅÐ>]à6#}»žùµn2ôû¦›Ì‡ × Ýdê",W»É<ôì›jÀ [jaòú ?ýòlyìÈŸ{Sväö¹ÊëŸ#ÂÅ}¿‘+–+ µ>¶®ók/#ÝIã¹×çÊÝ¿}Yž~eíyýéÏVÝ…rTîBtM1Pt_Qú %«¾Ä~ÅþeôTñÖzŠúžz%5kýÔp$Ì‘ƒ@8"N®ć*I>„J—Îýä뤶m^Áò­òžØ·\1Ñ¢ÅÖHíÐBÇϪͻ¬Yë>Z»tÑÁ"z"¾Ä¹ÙX”«¦øýÞÕhþiùÑ·'ûVútM•º¸Éðº:ø§®ÉMfݶ½°(o’ß B ýŽé²ó÷wÈWk· ë«ÙUƪT>ëüÃCßæÌ'3p•o™1»8ãÊ÷š‘SâccàZâÝ|¶”ÃÕ¹ ù 6 @ ’~Bª?±µ9§ÃÇXÿ¦ÑSu‡Ù_Oådo}™ú¾Óf§Ÿê~ÖfOƒ€A ¹!ŽD™*ÊLÅŸ¾ùê{“g|›NÁ×<~ÒF:iGC|–q|«úúñ30-\Û³Ö~¼àƒw¿Ä‰+,1§U™ËÄ—87¢Œ¶Ö[Îæ&SSt“©No›qéwœsø¢¢“ËÎ^i׆ºÉT*+tÓщnªn«m½:w¡Úö7Ûê…ûK%ý„uö©Øù¼ó¥-6_i.3z (œEüõÔžm›ß˜ûöëïáV¥ŸÎ‘Ùl0„ áF”ùy“"†  Ò,Ä%:kÔùG<.øÎ3¯|š<¤Oƒç,F®`¸¬Ö.Œ?ÊÐJ5Î{nWyÁÊ_¼³aùÒ,`£qä'MâÉuâKœK @¨‹p`ç!DŸøQJŽçæ#é ÒŸc°(C¾i5T«ŸpöTB‘x)üÐÁ£c2.¾z*Éè©Ê÷EU=ßîü5K½´zñ‚¥ØÓè§Êp™µ‚ +zP7Oɹ…œb§á‰Ëþë5†•(k‹2•'BQP¦+¶d®Ú1îÒi“ÝnWa@·c£í”äx+&2²ÕÅÇ*)+C|çb» ¸„Oä›7-ùzîì¯JJŠèØÊ‰æW̹ìoQ6D€ÔU.?D8iµh¢|†~"Ô¥¾œn_·vç¸ÉÓÎà ëDè©ÄV¯§ÊËmf0dr¥§Ü®üÝ[7/øú³Ùsáã}¸ýÔj»TË;qaN{ðÅq#’[È-”SÈì[w=ß×·ÐòÎßwF`dŒÒ”'Ì1°gg•Œ9&˜¢9çp#ÊÄXûÒÿPº]ð[¸JUæ½ÿ¿±üeß¡#zvïÛ`lBb›èØØ¤ˆˆÈVcZŽv•'źÊÛ!ø.@)´N––m\³3{ÁŽër€þ4LË1‰qnÅÄ`¼Ä“¸6+ÿd´×ˆA \¨Q?U4Ѓ—T÷‚ß™·àCYØgðð®Ýûõï—”›ØzªÔßSƒí.Ü¥—C5w¹k¯¸8¯¸ ÿäží[7oß°–m0ú)TÀÔHŽI†ç­Ü" Vo–Üüb5¾$ãLb$yZ•àe 1öa€y>ŒyŸ|µ^’be∾È1ÉÕ"•‹¡&ÍáŒM8^9¾ki²Ç¨$ÉÚZ¬RåP¶[1íÄ6eÉÁœûêý°Ø²å–sÚ_×1*2'œ(VßÌã'>Ä"-Èt« Õ˜¤˜–åSË|8iÿdâlÄ `¨uÒO(2S$^^³1íÂr“é§ö—=ð¤>ÅcŸüáoz9DsâE½íÓÝX6ú)Dà›jBƒÇ¯àë‘JRÆq,ý»w”©øú8 GjƒÃ»†¦å¡©…Ó·ì>,k¶æÈ'KÖ«Að×|#]÷J“ÈÈÓ„94­©-áH”yZ©Òú©É¯ÿŠÿÇbR¾˜+‹3æ:Ü>µLy÷ø‰WîíܱsœÃÑ 'uC»¶7aY‘í"¤ÇC;N}"gÿß±™">I”µ¯2q#~ÄÕˆAÀ Ð8š~rÆ&ùŸéIÿ• /k½M’Lw/ýµËè§ oŠ>>’¼|£¼?µ Clÿ§‹ a[;ö|‰¸{F†¼ýE¦Ìþ* ¡PÝrјAmGÁºáó_®½”Ðn g¢¬•,•«ÿ2 ÉI²è‡yuD¹%fâ!Ëró_œ”ôÏA±±w9q¶pŸæÇii?µÿ2læÃˆ/Ä‹îÚ/Y‹u#ƒ@Ãð×IþËa§ŸœñmüÏò¨ÿJ–µŽÑ¸h¢L=D|¨—Œ~ ø¦ØÐ @Kò†í92kÁE’oœznh*nµðeBá5Gdîò-Òƒý†!²…dÙ‰üá$áL”‰-•¬¿ÂÕVeò‹Â¤I²öe&AnÉ$§§„˜ØO>ú—·{õžà´ÎsZ–㊶ɿXRR’±ìøqZ“‰'bh,ÉÁˆA €4 ýäL¬ôøPÏ¿¶¢”~Â#²LDÒlôSmšma‹IrII¹¼5w…r· %ÙHý nGNæËÇK6 #m[±’½!äƒná"áN”5N$yT¸œÓ2J%ëO5IÖȶ¢ŒQ¶ež¥ùw_Û®ÍbX•bõùK×Nw<~|&0Ò™Ø1‚ƒ@Xë'g|ŠÿY÷_ â²Ö9ê"=Q‡ëe½=ˆM1E‡].ÜnÌ[µI% ¢O²q·h¾Ämú…Ãåoï-’%k¶Ë7ÆGI§ðqÍ…(ó*hË„&Ë´FsÒVdMõ›Z¬è‘ýäþýy3Ú·ùy¬Ãñ¼Ãr<´cô°×û¬ZŸÕbÏÞœ˜A ¼[ý䈎÷GŠƒ{C!>ý„ʸ̉º[Oú?üeÄ Ð| 5ÙårÉâµÛTt 3p¯q׎ø1JÈêíûeÜjP_D}•­°qÁhNDY_ ­`©p)š${×Z‡Û…>Wb¡äã«®ýóÍÍúÞvÏÃQâqüË™ì b33‚@Øé'G$‡sø„¾Á¡Ý£q Uݦƒ@ÀÐÖä]ûJ^A‰L›Ð5àu´ÆcëžCB\ B†7dœ3löiW…æ|m¨€µ•‚sýI¯5Ì}ç=sæL3Òñ=œ?ÝR ö„ìô‘·z—ͯAÀ ÐD4½~¢¯ŸžB«}ú Ø#f€—(»eó®*™ã$7W)-§Txq„Ç…dƒ(#9I9’©1ñiŽåpÀ-,ÛÐãëÕ›w¦ÒcË/Ø@Üx¿94yòÛçÎ UìÔ°ÄÅ4Ê `0 E€n$pÇs „÷8ò»—?““H…Mav»Nç$Ë”qƒ¤S{ïøƒýGNÉóoÍ“û®¿HÒÎ9=&á©W>S©£/Ÿ8T[îrˇˆa¼=çˆò¯NIŠ“‘ýºÊ”ñƒeÎÒ,Y¹MíÇóêÒ!E®¸`˜¤¶­^RmôëKžÌäWRZª2ÆD»UŒêpÔ×,ʾfͺ¼‰ø(ò~žÞÆ:?ú“f}B¦ñƒ€AÀ `<°B¹@”s ‹UZêP6iÌ îòÀ·¿!WgŒßjþøæ|É9|¢ÎM uöõOW CÞ!àáò“[§ ¬Ò³s;_$á¬czÆp9UP$Ÿ/ßìÛì…$¤ù.D¿RLLNœ‰w8ˆ!Êáp؆Ι™Ep‚ÿ©.Òû¡ìôônzÝÌ ƒ€AÀ `¨$šœhQ.+sI4âý†RâbŠ.Yôì(ß¹b‚tl›(Ÿ aG]eÏÁã²i×A¹dÜ@¤Žî$m`Mæ :NZœpÑb#`e>oXoٴ󠔀´†Bˆgy¹‡‘¼Ô`IízAÌ›Z Qnê+„ú{®\ý*¢®dѸÉÁÐ +³ƒ€AÀ `0Š€v½hjòFÞ!}Òdÿ‘ʉ6·î9,k·åø¦Ò2ÕòÊþ£¹j¡_׺ùUçÀƒõD!EȤØ÷’dNÄ;$„„Ãé¶Ž60ÒÅŽÑ#ÀÇ%êŒmûzX•Û;3sCë@Àœ¥AÀ `0‡É±wB™a`åLˆ–²rw%‹ï‚U[+%ê(uVËQ$öH‚Uš~É5Iaq©¼ùÙJÉ9rBŽ*”‹Ï¨Â´Õ´ ÿ§íØ£H²GA¬1gLå¦C”›ý ÖÝgÕÚ¯v¤˜…»m:n>Pg×LTwM«4E ƒ€A Å" ‰[Ó;ˆø)1QLRì•;¯¹àŒÁ|z[B\´×ÁÂátÈù#ûa`GIŽÇé ñ¥¯$/%ဴ7YGa0U…‡Óš‰÷0}§]µk̈¡¬ßÔe0 –„É\S 3®Ù¾Ozv:=ïlm¢ï1£^ìØw´Æ]ãa¥¾i¥Ç ér’¬å{ œu›Œ²F¢Î{­X³¶äwyjèÚ–Çm?ÖOÓœ’AÀ `0Z4ùE¥ˆrqþÇûä¯ï.”|„R›vž7ì[]NœøÎi“ ï|‘‰8ÐåT^‘ì9tBv8V—Ã[õ>Æõ¢…_þ‡=/‘Wƒ(;0]‘}îÈѽW¬YÕÂOÛœžAÀ `0†@S[’WmÚ-«·ì‘6‰ñÒ55En¸tŒ´KN¨óùÑÏ÷Ž+'Ê{ó×Ê«s–#üšGèŽ1iôéѹ}Ë õŽM;Ï·%ezyû{zû/‡úš†º>ÿo@\ö_Wmé¾rÝÆìQÃÿ‡•ëÕnûQÌg¨åfü£?ÏèS‡Î¤Ûì¹ÿÀ.û¯»nS~½hzýTy丳ÞgÐðüõQµú©áE›# ¡G ês'T-øÉ-—ÖZU’ƒ¡þÞdÀ‘^@8ËEæ¤}ð1Ë/.‘¯“äÄXÉ5P.3@‘hciVP…ꇙ:È™‘1=®ûè!wÇÄÆÜ~zªÓù¡D×㯧П£žr•»Ÿsž°_øÇ?fc¿Š»Za惀A |h.DÙqû£¿¼Ôù/Ûãé<¨Wš=fp/Ú'ÍŠ‹‰å§Äð¹rÞ–¨—ƒ¢’RÙ°c¿¬Ü¸35kǾÇûO}w§!½ïzãOÏÌÅnL«ã¶œŽ§m·g ÃûÅí9ãÇÿªë²euÏÉC,š ———ËÆûåÝ/3‘V³Xeâ'¢=RÍ[1®I1R~nÙ}XÖlÍ‘®–Ex™¸aÊ8¤ïR)¦fˆ/_kªNdœpä ÷<49)¥Í_Þv¹“xá¡ýø?ú1Ý,b0E–Ùò¬~ aØ]vFFؽ(i’\^î’’ÒRùbÅF™|öC{§É]×f’|–œ/wÏÈPx½?oµ|±|£7€{Ť<Ë)4—Íš$3Ú ûû›ÑSu»|þzŠúzGòydŒ uƒÐìe0„p&ÊŽ[þÙ‡Ãù$>öW_hEE¶f/‹ºßĉx·¸„¤_MÿÎÓptÌÌœ³À—°$ÒN; r¯¬{©¡Ù“îš$¯ÛºGæ.ß"ÃàBpãÔsÅ\ÿº]âD¼ˆÛ¬kdÃöE–ëv´Ù«ŽPw*’ÌþÅ~fôT‘«ØÍ_OQÏSßcS8?“êw‚foƒ€A E ®JÉ1uêñ11qÿì’ÚÆ¾åЉÆÊЀ۸u>'E:tîò‡nÝúµ}ûèQg©í~Éç hÛw6 Ø B’Lw‹R¸[äæÈÇK6ÀÝ"Qf S‘ú#@ÜRÛ&É[sWHII¹!Ëõ‡°¦#¨7ù5&šýŠýË詚 :ûÿÔSiÚØÔ÷Ôû8"\ŸKg?³‡AÀ9K³äÑÞ“…«·ûý+òò'_Ë{óÖTúϬ„/á¨HŠ÷¼—bà“ì4–ĆÝ@Ä ƒº˜h$uÜ´i$ű_–¾†u·rÁ°í‹÷¦§÷nXé=Š.LËIŸäbDqX²f»ä!Ñ•Œ0–äBÍë?ýÂár2¿Hæ­Ú¤ð%ÎF…€ÒO(! S,ûû—ÑS Ç”÷éõ—ŽuRßSï£$~:4Æ‘†CjŽ #œN‡|¹b³ :UµÊ4¥>„*¯¬5Ñ11÷îf›{õ¹œgîK_@D?°³Ä¾[ÿñ½íÛ®1ô³hËš†^–Ër“@?ræ‘¡ýÇkMv)kr!â"¯Þ¾_E·0÷wˆ_ÿîeñÚmØ7û,q: S#PUú ÇGcŠOn×övö/£§(Õzjcö~åg1ñÃÃ[14kúuK•C'òÔ€ôëjø:ºlÃN„=Ý!ù…%Ò«Ë9ñÙ_º¥¶•MH5½lýNé×=Uen'^*§ŒŒñ;ä¾Mâb"åÂQýdD¿® £r|‘ýhÑzÉB”¨èùó†÷Áö¾jÛ–]‡dÁšmrðX®ðKóE£ûKÔõäËŸÉeH…=ÕV9޼Ã0hzÆ<'*{S^oÏ9Šg‡È žeêyC$&*BÊÊÝòÉ’õˆHu‘´D†£ —O*µµ¡¹^H*ýpe­™ñ½{Ï…å+!àŒU!WÇ‹£Õþü)߃âbö–•ýG/GîŽïØ3fÐ:Ödâ³&# \ BÝíÚT KÊT‡&kT ª˜1ò J®´Ú«rƒ/®ÒO8šý%ÆÛŸ¬öFO5ÏJGê}êl0VåJ蘕f‹¾â]™¹yì=\}DÖ”„X•ïΫ/Pî‡_.߬N· ƒÚ·í=,³È• ¯Üï͹+eå¦=Š”&#áÔG‹6ø Y¾q7B…’ë.#—Œ„ðäx.ò’áAÿþµҵC•ÆzPÏN*6<™W$.Z'ã†ö”‰ Ö«&{ÓÌefý»yÚX¹î’Ѳn[Ž ­ÚöÙ²²ë†÷–Ëp~=+Ò`×ÔuP3ý K¢—˜x Œ6â$7SXëÙÄ‘xž“Ö%-‹ýîÞý á{±·Âý¢}vö¶Ëš²Å^¢ìÆ ¾råv‘ ¢Ì7TÆI6Òxˆ#ñd&C·ÛmˆrÃ!ÕD™ƒøbÙŸŒžj8˜UÔzŠúÛ Q® YorÐßù,­W;\0N îÕYYo?DèÎêd ˆëÞ%1>FzÃÊ»-çH%=I¶‚¸SÖlÛ+CûvQ–iZ´÷ê¤þ¯© jc~¯Æ¹¾Xסøín®$îN\Õî̸ˆd"Luü›?’„¸hùý®÷Ý0Dëýy«ä³eYø<ÑWn¾ü¼ØbüÒÄØh;7*šo1‡ ‹m÷‘¶óQ2(Ü—7ަ:obÆL†E!ár‘T$÷LŠÍÀ\âÈ †´,(o¿£©/^ýTaQŽDb¿Bÿªß“³¾µ¶’ý•ž‚Þ/-)éŽS&Q&Þ¾±Ç­sšaŠ€?i‹ŒpJ)¬½uýï›oóÜ›_(k°ÓYYe| ëìz¸zl8¡›i椅®”øXïÇßÎí“Õz\ŒwînèõŽÉdk]‡²´=U oš:VÞ›¿ZþðÆç2¢oW¹ê¢‘ Í^7<`-Û&KÎá“j5~ÕïcÐážCÇ%)>V=›ãð\WmÚ-q±t5lù’’o<ÑgÊŽÊÌ/~çÂä„GÙMÐ/Û•1"¥ç‚µ§š *‰r)|¯ 1%!-µ‘À!À4ß¹…Å g¸ZEIÔIÔ™|:E;##:°_µŠ3ÑIRï?•KÓqØ3 DÍ7Õ´âAP÷É­×Ù¦"‚Ó¸¡½åSç謴J •—þÁwLŸ(}»v¥ëvÈð3öÎQAhu…kRUH°#0ÖK¹qTÝÞ5µÜ÷­o€D稈É+ceÊøÁj·£i®ƒA%ɽ(TOŸæGn™¢ü’ÿúîBõD„C ²?VAÂÕŸø9[ô~ÕÍ阞]>œˆ2¯<'¼TX 1‘‘gÞ Õ¡[‡ÿøF7oP«7ïöef0+1ëU%ËÛ²õ;d6Ô ð&5ŸKnœ:^‘éSù…òöç+ÙNŠ“Q»Ëåç@ŒÚ}2÷ë,Üì'¤+nþ©ç SuÐ’ý¿ÏWàÍì„ Ñ5¾?£ªÖ.]·7à6u -§$òÓ&“óFôƒã~õõ×á4kÝ…x:Î8ìć|Ävîܵiä°U É£t´'3ðÿ?k-$ym8ÑÒér¹TÔ‹òrÄLJӭ„q‘Ñ‘|7vízAÌÃáM=Ä04¦:Ÿ~B!Š,³?RO5¦q-åXâIýóÑ®Äý´i­¥œ¨9f‹õf¾PÓå¡Fú|ùœ*ENO÷‹Ld.“” a¦¥X £ðSÃo_üDÿåkÍ´ó†ã3‹KþñÞ|A.¹iÚyÒáPî¼f,ÄKå±Î’1ƒzÉÓÆ«OtɘóÕ:9Œø‰yJq¹]ˆš¦>Õ¼óÅJ”Óñ ++5·×V?·HØCÎ÷lÞ|lû¨Ÿc}*º‹e•ʵX~&@õÔ¹MÜ@ßÔ@3Ýyë\€Ù±NP%j¬ët€Ù©*Z?qn¬UÑ ìºÆ:°¥šÒ @€ƒ ‡žÓÀ.í$sÇ!„Ý<&ˆ÷¯Ir#šç;”‡„µªpbuB‰iU2¬÷­én¯©Ý5µA—©çÄoëžÃÒ5)†DN^|‰q¸ :7¢Lì¨ ƒ"ô+~mÎ×ê-eÜ"ªZÔbðY¤ ,Êß}ÍõsÐßÍ—M@X–òÊ'_É¿Þ_¨ö#YþÙíß”U›vÊ«³—I›%q2}ÒhåŸÌÏ?¾uš "þÈßRe2üËD|¾`lDÞ`÷Áe£7|}(µÕ¯vÌ~)œa¿}7ÁTÌX|U 9QfÝšÀ’L4‚'ßFc[©ÿ4º4S@Mí9PS…æƒ@mhë&  Ý0„CÛœsLf!>ñÝ3&5Û ²m㚬íLZòþ‚µmI—61¿é¨¨(…/qÖ˜×v]B±­UYEéO¹Bp ]z…#½?ÈÌ,s ~È_®Ø„ý\HÒP,;`ñ¥ÐB|ðè)•Ѧ÷N µÞÌ4+7íRðôO¦ÛF4C§Àrœ‡ìr)¸0|î×§‚“¨Ì_µ#S{Ëh8Ì·K:íà^[ýþíløò™.-E¶ãcÜÊ÷Ã{Ü®1c:6¼ü–{$Ãá’Ùr¯oÃÎìÌþÔ°rêvÔáãyr#ЛJ>Y²NÛ`ýü2· ºÏˆA 5!ÀhÌxGB‡}£{wÀWã|yÙ뚣ˆþä¶)*ÎsS´ÿí/V©¯î]-‰…›H4H29ñ%ÎÕE÷hŠv†“E9è„H¼…ßñŽ}Gàës&׌+3Fɇð'~+xM7DútM…ÿpŽ,ÏÊVñ;"†áUFÈ9·Ì^¼N^ú`F–:A£ƒdŒêå Öªb43‚Æ…éý”›/0]>ºcðß[s—«òª«Ö½í2 ¬¹þ`Ý£Ö¬9‘>|>Ë_„Éáñ¸¦£®¿«¾ªå6ò¹qç5ÈaüÐ^*µ§>‡ýGNÉóoÍS«ô3ãKO®õ¨ÝÔ¿CjÐÈ®CðèãÂaÞpœüÚtý¤ëúùŸßÅXoþG}ò£7!×¥sî¼æ"½kÈæ¼_8΢}J"2uµ‘•wáÅÑ#£õ vB†{°OÄ”Ö(Ï4´PÍaì*.)/çr¥ûO»^ÈÅDGc [„S+”õÌV7GdRTG!Í´‘Ú %™$y=vŒ³¥CR¬Ä"X 'àJ|«s½€ßµ ÎÄÀÐúzÕ^Q€¶†Qæ)ñ¦Ä8K Ièß~~ËVò½«3*–¼³}{r¥u†wãÄœë±pÖç[ åÛÇ©¤´òtX•_Ý9ò.„O¡ïÒiýe‡Ë¥ Ù$Ûþÿ¿øÁbDÆHP.Ñpš§ûW{_6Œ‘Ä×T¥F6n¥c5·­÷à\ñôUî!#Ê<>„OO;¹`½fKŽtjŸ¢”!GúV}Ëýö¥ç"„`Éï–YøŒÄð85ùm£}õ)Sã\Ÿc̾>Îè;¾-^`œI£¨R©C(ß¹ò|ïãA­µä¥ÿ5Ö-ùD͹…$]•¤¬¸4÷dnQ¥ÿ¸â}ž;%22R»x„ŠÚ£ƒ mº!{Ÿ9™'Ó/Ÿå3*o!Ð'™î¿•ã–.)±+ À’x’0_ízáÚ§ò m·Ëu:m`ˆ4b¸eLšt™¾ÄÕ‰?IÖÛiñ©Nª ×û”"Uó)„¢c’5[÷¨ÎÇÑ«þRSýþûj92ÚžUV*/@[X˜2öLœØ¦û’%ÞÔ<ª¤™–ÑÛ‘¨æf¼$½öérå[>q#ý¥}›½¤S»dY·mŸdá-9þðF  E ú‡ƒ}ýå­Ï–«”7a¬Äºm9HL°MÎ9@}†/3.9·Ú8ìk‘jöóåå—ß›]#ÂÄŒæóÈmÓ$9!®`›eEÖNõUkAæ™×3jî°U|qï™vŽ3̲A E#ÐæüFLÕ¾¸ØbHWFŽÐ¢ _Q r1ø?!>^%ÉÜý‰AÔ†­‡ äoï-BßIE4ŒnZ¯8˺ž–6gœd†€[èÛ0pyEºÆ¹äœ¤…abb‚Ä'Ä«eâJ|i`ÔFâÁk‘_\j•—–5>Õ3¼P·¢Õ7}Ò(ù J^„»‰2-•Ý4¹Ù+åPBÒuÙºýp¿XcaÚt•NAý¾Ë¡lK¸ÕEÒéŒ~ŸÃx— T•(ë6ïƒ;Å?»ÞfæÆ"‡¯\tw ”áe›Qrr®ö\Ä]Ñ¿»/ûU¥ÃÕëyñÚ~Hï.j|Å~$E"ùfâ%•©t÷Aêr#>kýÃqWa0r»äxy~¦|²x­ÜƒØñF -NÓI‹ì5ä·ž²Ò‹”—~v¹Š \5®1Ý/8˜þ´´€–£/2@OħOÁWèGódÜ;É/¦ ~IH&Â/È­M8Ö‹/Ý… ¹ŒDWæ¶eûáéÛƒ!;z‰q2rO$%a"YžÄ•øVxÁ8Íø"jÊÙópDi¡“ÖwåB‡mµ5ñAtÝä±ÕnkÊ?q×}Œú½ ³mú£¢ HŒat3Ý-èRñî¼Õj€&c`k™»|“”””«ŒFiRj$Òz37œ 9K7`ðFµ[_Ä€¯ê"Æ ôó»ê²t3¸—Úƒ”kŠŸ€ÌYŒ´Ãû¶ &!Kèdã±³ñ­ Äq&?ê‡CúEÓ]LjA ¥"0ìáÏb‹âfÛåº OÅx\erªÜRgÙ+7îd¯ïS>«2"E)’¬»É]T”SºËI¤d>Yì’RW±œ:‰¤­0Ï“…oÔ0´‹c³–ì)“Ãðf±²³(R<øjvCz²"É)ÉÉB«2Ý.õ¢ª5™ÐãZ@Ù'~ò>#•RÈÓé§=[b¤Õ"8ã ÿ Óp /{ÿQIÁËÍ_ßY(eL±(&ô'g6EŸ°ÞȬtÅä :14&=‹¬X’Üš„I‘´Ô‡1ã³ÁýƒŽÏ‘¾ Á¯Ï^Š—»jÜÅÀU1³`EÎD ]foäʃ9UõË×uš¹A 9#ÐïéKÄý’UVÚÇFžd»€8Âvå_¸/û@A”Ó¾|ûÞCÈPÙÝÎkUvJ´%v¼—¯ÑzL¿Zº Dc@Zll‰´+-…ÅÙ¥á²O±/QZÃ˧v› î ^½:$È››]r (B¬ˆ™8N"vFËC Ë•¬ÉgÆOæ5ÈÚ±Ï:z`?ãì"}¯(‡„,¢Ì»Öˆô¼üªU;?|ÿ$î:0=;mÏØ‘ƒº/_³©5CC?Ð(|bä -.¼ÓÊìO”'¬|”õ>fnh,©m“Tœ³•ãŸ@ ¶8ìÓý³¥ÞäJzvFÔŸTõIt-|S1>‚™D÷<&ŸÂ’Mëõ@ì3å&ø?¯€ èl­0Û Í Œ—vÅ:VøR;ßÖêM2g¹Ý ³>áÄâ׎.‹KHíÑëÁóßøôëÄŸ|ç GÕh$‘_I ½ë$Éqw‹’⼄z‰² F•yd¹5d}7h\øÒÝÏÒŸw—6زöÈpT¬,Y¶Ö8å·SâT´ âIý…_Îp p©<'Îz÷ml#Qæ2½!ÊþW¤/[3gz²ÓG|‰^Ìì|ÅkӪܪ‰òe&ñï–’+,\'…ø|mÄ N0ûWk·©8ð5£Q9 Ÿá-¢üê'K¥1Þ/EèJúKrÞ×ëwÈð~Þ¯#'*Fù3éÃÓ-Å6%´Ù²N—Ú´¥ |fÓ¸ƒÇòÿƒÛº¿*†¦`‡uÊ]’ÿ«Cÿºíã’Ü\Ž¢íPTTPœ¹xÁkŽI—üàå–Øw\}a¥^ I ÉZF9\&QŽ‹U$¹ƒØÊÊÊðiÊ/ùן…‘þQ*¦üëhÄ Ð˜úüöèeŹm×C8_°cð²9μS?ÜþÛˎ㎪§DMk—.ÚÐæœcý›òžØ·\1Ñò·,“Ôé0f´„r™¡ÍH–érQ†—U¯5Ù­,Ê„ªˆ²&Ë(·Å ±QÞ²iQ&6¤ÇÉÿ75B:­vÉ›`†Ïòòƒ¶ÜñI¡üý²IŽñ²lZ’I’WmÞeådo}yéÜO¾X¼.e˜H–E 4¹øþgˆúŸ~ëÒ±*à~“7. ˆˆˆúÜåâ}±í í3¢¬·ßæMÙê¤C›Dyò^fô®,ñˆ—üÛ»¯òýYÝ>zãOn¹T/š¹A ÎóʧIDkãEÕâWgY[—I ùÒI7 ï`X¯»ɱ&Èz޲[¬в*cÙû"áP/^òlÉ]c"¥ml„<¶°—Gdûq·Lå ÝïØ—–U^’¿f颗V/^°›èrÁkÂkÃkÄkÅCB"Æõâ,0óÓ%µšv8êóÛ+.)“Çÿõ¡Ü8m<‡¥©Rþ=køÇȵŸ+ði~.â%ïCìÒ®ˆ]JKÈ„»à~þý¹Û‰;‘~Ê ¸lÄ `0 Í!ÏlèZ¶×ÍÁ_Èàm5ÈZB–=¼å¡¡¯rš(k‹2‰Ir&ò$džåK³¶¯[»sÜäiç¹]å…!‘\01&FRRâí˜ÈH/3ÄÎþR•W]÷ß·¥-k²¬Ï«ê:ÿgªðQ®$;3b8ð³­#å±Ö©ˆÑG²Ÿ¿ññ⼣ǰK>&^NþåVK”yâ˜ðA$LäÐñ¡Û#޺뷗ÍÄ*­Êä/¼6!u»@}¾7%.‡xÊÝGNåÖvC‡¬­(Oþè:yîáo#öhGyæ•Ϥ1«¦…Õrç5“¤#Ìûç,y. Ó”r*¯ÐÆ{Ûɳµ¡wff.ŒåžªLûŽòò’‰g;&ÐÛc@èòòù…ÅH `òF208ÊKJO†‹ž ÜY5mIÔSn—ëhÓ¶ÂÔÞ`T‹×ŸÉzéòfj’ŒóØg9åÂmùÃYH²>eMˆµU™–L>7i,:~Y›6©SڤܚètZ ˜†§ú¶ÿ?þ¶i×¾ä™éìh¼H€9çÜýÿ¾çµÒCÛ?Á²’ȶ]¯ëóË/oÅ 1mk2Ž>Ê$tò‹K­"ä׎òllS ¸ ãþÄ)Ýä¤tl—¢šS†<ïZø»äxµÚ£s{ùÙíß”U›vÊ«³—I›%qrÕE£õ®!GâY\TH²y=UÛXt—`pîxn´mçV»cÿ¤…SO19˜W,ňA‹0;F‡q,@ðû¤¸ ãÆ•ÖêÖ}Çf 7=Õœ¯ŽÖSÔÿ8âlÄ P'Æ>¿=igYÉx`eøðE|tì·×Ü×·¾/^$ËúÁÎûP­_—Ú>õê䔟–Ùvd)÷¾WýM±ÇC«-ÐÚúŒE#u@@cK 1­Ä¼dÿ_númG?pÄ·¹”e8Ïy´Ï_gíøù¸7°Êkr 7¢¬B‡÷í]’Ô®Ý6ìØÏ<ë!Å¿BZârc§ ä³eTؤԶÉÈ"©øenÞ-ý»w’•›v+bÇcÿoÝö½2ÿëÛMÚ&­WqýË å2q„+…•³}ëZÔËMá\1?£)¶å@¼BÞ»ÜÑwÆþCdPeJ¦]B´ì<^,[v–‘ý»¸¶ÖWqÄWÄ}në{i}(äŒýû‡ý)­gïo…ƒž ÈÙ5q!ZOQÿ£)ë&n•©>Üò܆ԓ¥%Ÿâ–9ëвžùöCC™ia¤Mý…÷ž"ǘ«åÓÒ¿Ù&é—m'—âËq±í9ñÖÁ÷*)' ×~Ìš(ëyýknGSŠÆYeºU”ºŠ‹Kö½ôývýá/§<ƒ;:¢ãÿ<äù ˲îšÍõPK8e‚¦¦¿·¡Ï°ÇWnÜÙD¹Ioº¥ë¶ 'F¿‚d×^a_:a˜|¸`<úüÿTö«s۔ↃÔìÅëä¥ SÌöJë ~ñHÕN!üŽ6²åmX¹l›W1ùð®Ú§ÇZ¦K,ÛƒxÊNÄSæqIÆUfPrfðI‰„?­…TÑ{ QâL¹)Ú%‚(ÃìQa½@Ñ­©ÿþ¢úûӹ߸4ý+©©õTK¸ÔSÔÿ8¼[Âé™s#þ°+¥¨¼`>nof(ø ôûã-}fæÃª÷û¹ýdÿþQSb¢_öˆ§{9,åN)^•ŸÛ¹¹›±$Yû2“«4)_AýÍEüû·æ$¤$Ëå%{¶–•où↨!S¾‚&èKORy™¼…p6^7˜û„T‰(óÄù§@;vðÀY–uÏö½‡¤jpïP!TSà]ÿ˜A=…“€~½íWwNW¾Ì‹iøU¦i„ø!ŒµwÇöÏÐÞˆœôYíÛvÌ̃Hg½­n¸›ã÷ìÚ5 ǬÁѼ™{¼Ù{R㜲uÏa ¤<†T»íƒRok(”ømÝsHÒûtô|'ÞF„€O?áh՗دðrw]Sê©I˜¤õõ>šV«~ ³¦›æ4Œ‘\¼·à}M’9h4õv„~{5PMâ›ÛŽô/yl{¬ÞQðE<âr}çW»ö.Fþ$Y+UC”ë>‰2…:U¿”è~¯8Êîÿ8t^~Á²$޹¯ÿ³¯Ô롚‡Qæ9óí‚J²ìĉ#ù[×fþž) ™ï›ÔâFüVµà墢|Ž2å ÈI“åj-ʬdÊG”+ôñï ˆJû ÇðѼ÷¤ O´Tüê·¿X”z[z¡o‘ üòetï­ðÎ:|aK?ÿ œŸO?¡l՗دؿŒžj8ÚZOQßS³ê§†×fŽl  |&ëä}ç‚$"[<ô¾õ,d§|¶Î‡tQËúm¿Ìu/`]qÌõó”ƒùHðôœËfªçGÿ~ïã|H1þGð‘YØÏ+n÷Kƒÿ¸±›^ Å<‰2-6¬dñœ—ìßµãÅU›wYÿzo¡î–åP\°ºÔAœˆqËÞ´~Öš% é6¡;.oLâKœk$ÊpÁ€>¯`Ý8½Œ¹v½ Qމޖ˜Ø‰ÅÔ!)V:ÆÙ²ƒ_›³ÜX–ë>¯ÿksV·}2°s’ôèÔVáJ|iQ6®uòÌÝØ_|ú ˪O±±Ÿ=u&`µý㯧¨ç©ï±ôSmåšm-~Oom¤^Ïê³Äóé¯Ûæ[×ÿ7fž=jÔåðø½.Vå÷z]1ýÿéõŠ9Éu‚ž¨ÌTw 4nœûˆ1–Ϩ8ûvå=Ü€Û¸Ê<ÿ>N0ÅÇÇJ—”hIq+²üç·ç+Ÿe¶ÉHõÐ'ùÏÿ›/²÷I¿ÔxÚ£pŒS¸_ízQýÑæß³ p†~Âþª_±Ÿ±¿=u+6ûë)êwêylª³~ª[-f¯–ˆ€%eOâ™ÔŽç†GÇ®„¤vòMÒ§‹=fp/k(â‡KœåP\¬šê`üQ†Vâ¨qÜó¸ÊóW-šÿƺe‹ŽæW̹ìo±©‘(#ÊEÙŽQ#Vƒ&Ç1‡cÃìc.Z¼D%@äbàr‘/¥ˆýËÕ.—[Ò³2*¯DŸÌ“¿½·áøR £› è‘jâ,ãb0N2CÀ1J@F#õÐÎ Òƒ ãâžÄ•øÒgžxiÕê'”Ä`ßóf½ýéñǾ`Ò ÐSÉFOUÆøL=åÊ[¿bé_WÎÿ|ö¤nª³~ª\²Yk- þóÆŽ®b÷Íú|¡Íî̼³3}[";'Œìn—º?ÆÀ1oBKvÆÅ9®è´x LFšÍ úºßSëÿ&|ÍY~&ýïö§™wZ4üUÂ(ódùÆ@ Z3iaPùÖ¡L—lZ±|Ë„©—]Pg—€&ão'ÆFÛ)ÉñVLdd«{ú3}.3ƒ1éÞ~‘xÚ•¿{˦EË>›=¿¸¸P“cæIoP®tÀ» Ce[:F¡@©z>ç :ì2`Dz¿n}û‰Kh“ ¶vd9ÆåJŠu•·ç•s[޲¼èè}\„¸Ü®ÒÒââÜâ‚‚{¶mÙ²mÚÝ(—oW´ó%ƒd™¡SË|+ÖþÉ5Z“±ø~}›±bYÆVü”™Ïª B‡P<Š$“üQHî":2‚ƒýJ¤c¹K JÝRPŽpåEròDQ8^zoãƒøË(o|=ìcI›Øi“£ÜUAŽI’S’“…Ëô÷Ž®Æš‹Q£~BéŠDã%µÖåO°>·ßБ=º÷0 6!¡mtllr„3": ­¨G!% ½ôî1®‚z9$s¼m»\å¥eÅ%¹ÅEÇönßšµemæ6ÔÝhý’ö›J t¾Ktƒ úþ¡—;·ÓÓ#³óÝïÁN9¨¢,ê"®ì½bïU#a‚³,ö{:ë×b{”O:¸Â/F>¿ýåd_¬×…#Qæ ø[m4ùõ@•Bٮôû*‹3æ$Õœ(úïZþ¶ˆˆ½»Sêã`û|@FmÈÍÿxi^^ :_TX/íÛMK< ±þ”Ép5$Ì|0q­õÜÿ¬âpF­ò¸x?sÈð³ÐÈ4!޶£ÄŽ÷ž­Æô«¥Û@4ú!iÜ2Úd¹»Ð〉fXÕÙ‚ð?\»M0rñr:èÛ K<°!!V¾Ýp· %™$™Öy¯5Ù â àÕ=«~B]±˜¢ñòº ÓV,7™~êpÕÏþ¤ÏýÈûϪŒ^â\wFbýĶ›¢ÃÛ>W7Íጘ§—;϶]ü¤?‰åà‘cÛÇm½We.il¹æøÀ#˜8øOùYßÇó~JO.,+~ó;_ÓéÕ(³…T®Ú÷„JW¯“¹ÑbÊQ &ež-Ï|… IF[ä„Ëu²ÀíYØ5*r2×'%'œ¢¼œË I2 °¶&“(““,Ó‹qãvâX'é¾|ùnÄ‘ÌKN„î8gט1{®\ÔÑ”$$~’BïzEØ8¸Y”€(—€(—ƒ(»\.XžI”=Ðlú¹\§SkÖ;i\h!¦;ñÒÑB¼!ã.VM$ÉÜN­X¨î‡ì_a§Ÿœ±Iþ'~Ò%ÈË— è§ ·Ýf {êP<œïâØ,<ä‹6?0è` šˆ0p¿CÔ›O—e=ØgÕš7O¯›¥pB€>Ɉ¥üþsT»lëŽÁÏn|jッw«áL”µ’åÃÇ™"?’de¥Å¼:¢6„yCQñ[#âb/ÁIX°0›ïXžWxín¨ŠÆE?ˆÊðŸ~P“,ó…‚ëü_[’õ±ø«v!³1 oª™À=mOÙ0Ì‚J”5 $¹SË Ë\f|叨XE’Õ@¿²25ÐþŽ•ˆrKæË´®S4FL÷M—ºU’eZ•9p™ÿs;I21Ptß {ýäŒoãâ¡ð·Ô:FcýäRf¹å#Ps*Y=Ñxª–E—ÂF žo·$?zº ë½W¯}îôºY G¶>8øÓþOo˜Ù$0‡Ëãyí¼;Xm g¢Ìs¦¢¥’õW¸ÚjCK²mŽ9­ÉÚ—™Œ ¬XÁ§Nú~jûeñ' §ã©©ßXž·³±Ÿ@‰‰žˆ‘ÿÈ‘sΉÛêlIƾ>Ÿòø)+¢Œ¦ņ¹¾AZ ©ÓaÌHòtè8’AZ’ËÊ ƒÖd·²(ÓšåÖ`U&6jÂ-N‹2±¡U™1’ùBArÌŒÞPpcIÒ=ZQl³ÐOÎD5DB#Ô]] æZ7iŒ‚¢Ÿüê3‹-»Ôâ3M t}£¹Kö˜‘ß@¦·¨;Õ[ì§½{÷½KV3X”‘pG$ïOP. ÊÛ¾mÀŸ6ÿrË=£Ý¾Ù‚ѨjÊ$É£Â圖QvmEö'ÉúûrXe´U Üò×.QŠ( FN“œü+ssiñm¨ŠÆF“eý@Òëz»wïúþZöz­Hàà@‹rÈD[BUæ>’Aø)+‚ W /Iv£€ŠÈ­…(óx±ñúo“,;ñBA¬–A 5v!»X­»"ÝÇÂR?9ãSü¯NP$þT,‡F?US±ù«e"ë*ÁØm%0Ðõ²Á²côè!âq½‹ n4>¬MLL™°¨|nitLòáÌ ûÁOÒ lâ¤Øu;šýT0šÞ\ˆ2ÏŠ—7±~ÑRJb̉ÄXOX /‹2ô›'>|§[Ú.4¶'VSžëšvÙy¹¹/q[E?ˆ8ױѓþ¯ÅëÜë½ï& Ôe¶€ÖS’??ùVSÞÓ~Éþ–äÖD”•U¹ÂºLlôà>ý¿¾zf2ØßÂR?9¢ãýAààÞPñ h=ĹÖMœëÿ¹ƒÀY˜q× ¢×Ÿ†' å«|ÖƒªÙawzz'—]>e$«Í–싌±.ë°`Çôi&,˜d¹ú?³þ︎±É¶eߊY«'ÊúòiKeKñ'Èz]m§Ÿ'NHT®/8moê͘èÈï£}id‰…‹^ÔÜ«™Pˆè@;##ÂZ°€VýŠ&€$…‚¬×Õ†VðC,´h\ôº™79º†~rDr8‡O8v!T ýªs1õ413-˃„ÞV s@Cšs$##!/ÿä'xM몎·¬<˜b¦uû*ó@CÊ3Ç4-1Î"Í/p7Dâšðôúô-Ë t«¼¬#Ð¥†¶C”èYÊë™™¹Tˆ5\T§§Ô¾ç,‡4ùfÛ²|e¸†‹r“_Óƒ€AÀ кÀgõKõ[¶µB/Ÿm¾sÔðë,Ûók½,ɯõÉ\÷¸^7óæÀ²»#åÃ|ßY¸Ë'û–´`ˆr€€¬O1è¨Ïëý-±oÏ?¾Q¡ntYÁšG:ÅG”1´ÔX”ƒ´)× `0Î@ ÿ [:‹Ç¾Ã·ÁaÍö-ײ°côˆóàÌü2>áâ.ÅZØ«WßÛ½Ëæ·¥ N«²Wà–3V/jnˆr ¬G9½.Ÿþ)âžeó|j[ZZ|s=ù®Ýb“·¡½‘.ìn»22* £yƒL…ƒ€AÀ Ðj°KË~ƒ§¥zî€-ÝúÐÓÄPÈ;¢/œ?Àffð…X›I2±’}‰K¼ÿ›ßf€e¯<}¶Ÿ‹Îé³dˆrcÐkà±ÖÌ™¼Þ¾ Çgƒ°v¿PáàlÙÍöòÍÜQx²·n»™ ƒ€A X Uñðà¹U—o9ÄçF¡ÿ«:?žÞÞ.—Ù°Dµã6I:쌒i=¬=Uu_³ÞüpF;ÎÂÇÅQe¼´+ Æ|R“5¥NÛ;ônÕW/›¹AÀ `0‚ÀÀ§7]êãçª(¯oyp¨ãSS}üâYd»?Äq}¸,ÐEâ°®èùõÚÝ5cþoÞ L\X¶÷úÚ⢠«²R@MÐS¥AÀ `0´ú=½q¢GÜŸ¨d<_K6¦DÇþ°¶Sg¬eOþ©W@’Ç{ˆò ½W®õû4_[ f[sE_éwë¶[g½ˆ¹!Ê@±e4«Pq–l×§ ]dˆ²ÃÌ ƒ€A  ôvãK2s´å@Kºc°P'Ë–Ä ×Ü€ úó½Ÿ=6ßoÕ,šØyŽv…›Ë#&ŠC:údÉòȹ2ëþ§-…¾§vŒ•.â~ÏOõÛÁò¨¨Ø]êô^YRý%ñK¼ã–ûš}‰îÿ±”›H@jjE…ÀœÙñüÞc{ìÏþýû™«pú|š×뉎 5lø§—17D9(6¢ væìô/à"?Çb*BÅ…Q¶23Ëw¤߃&öB[{Nî…åÍls0…™äØíöHii¹,X½E–¬Ý.¹ÅÀJ$16Fb$µUH…êà,· Hö:!ùÅ%òñâu’œ+£ÊEc(msÀïëûߟënkÝé¼/l±ãIIгb"#ÃÒ•m¡ŸWg¿îÎ8*¦@ƒ@#ØPÒγ±¼—å²$¢JËO¬u¬Y0}åÛ3Oâ`’Þ í§vQ?;'Œìn—ÒM£Â‚hÉθ8Çô«,ÛžíÌȘ×}ô»cbcîi.ýÿ4Zá¹TR^î9•Wdç•8ð¼ìÎ_<~ÈUî~ÎyÂ~áÿ˜É¯ëg\÷êÎ_äaBó ² ôËY+aÕÁNÿ1Tœëq´(^…Šë‘¹n~8µmÁGþø´A‚,—M?å eMËËËeãÎýòî—™r ¹÷Ž2uÂÐ#Ub££ØœV-Å¥e²e÷aY³5G>X¸Záeâ†)ãdHŸ.âp„%wkŽ×Ëqû£¿¼Ôù/Ûãé<¨Wš=fp/Ú'ÍŠ‹‰¨R48ÿü3¹†W¼yŠ^4sƒ@“"p¸À–ÿ7¿P¾Þ[îSR|Õ+qvzz²§ÄÍ }Ê ÃÉ qFOí´xÅQü(aÛØ¿#o¸ç¡ÉI)mþâ±íNÍ©ÿ ˆ –£®QI©lر_VnÜ™šµcß祈û sïxñÉÇ>Cݾë^S;ðE/ÎG”ý¬Ë5í_Ÿÿ Q®ZAÚ—¡â0Z÷¿xº›UT„Š ;¢ ¦¼ïv“ÙFú>œC´ÙårKYY™ÌÏÜ"s–n”Ží’äúÉc¤gZû`TÛlËäËÂÈþ]Õ´kÿ1™µpüõù2}ÒH¹øÜÁŠ,ërƒ/¯²&Ýñ³_ßô¿ïtN²ý­Éc¥o·Ž>ëWƒK6Z)od•ʳ0ú”ijƒ˜£mòô%ñÒ¿ýpkûÞTyó³å@Jß½ùÁŸüò•g÷< *Ã;|¨OOÈ÷ûx ª€°Ô²Ó{­X±­b½±3mE&GŠB¬s‡Óÿ‹l ÇÃà c‡ôâ„ëˆ×?uÿ‘“C÷>ò¯ßüŠ_Ü«ýª ‹ÃäóK¦¿²þ?sß›\ 3e4‡íü“>=ô ¼-wÓëa3÷ÈéÈÁÜÝ6M’ËË]RRZ*_¬Ø(³¿Ê’¡½Óä®k3 I> à|‰¸{F†Âëýy«å‹å•ë q5RoÔÃò»?ùHòÓ£ö´¹õr'Hr½ 2 "ÛŽ»åúwóä±…E>’ÌNöÝ‘1òÞu‰ ÉÞ3ìcÞv¹“}õñôã°³­EbBP÷¿`­™„eØo`¾q8nëµzõb®@T¿G9¬+†u³ ¦ÿÙ:áý©{©ƒq(ù*¯MõbIßÛös8óýÛàC” ]`d¨8ÜüÄ€þo;1n. ÓZŸN:‚fÅ¢Lw M’×mÝ#s—o‘ap!¸qê¹Ö_¸{C4¢4âD¼ˆÛ¬kdÃöE–Qdk=ÔqëÃ?›âp8ŸÄCÒ¾ãê -s¶Ö[Áœwc(„åø÷KŠåê·òdÝ!zPx¥gŠC^½&Qž+‘Îʈ}}Ž}/.!éWÓ¿sç4³fÄßá9q‹.ôé¡>«Ö¼é[oüy‘"ɬ“u›þßxPë[‚ÿõ§¦.F5rV˜‚ºë:lËÞ«—1¯±Ò@nʨðöÅ|YþnÎøñ±õ+!¸{ÛÇîÓ5Ø·x“$ÓÝ¢î¹yòñ’ p·H”cP³‘z#@ÜRÛ&É[sWHII¹!ËõCÐ1uêñ11qÿì’ÚÆ¾åЉ•Ÿâõ+Ëìmhµ¼»¹T&¿–+/­+wŇ-’â»Ï‘®O’Qk÷eßë|NŠtèÜå¯ ò=Øö9+[Ö?{¯Z÷‡‚KNÄEwëÖ¯-ë4ý?€è6 (^ÿ´mlêbêdQoµ{èâ#,罈y ¢hSF}@ÜÇ9åÍãð¥\‡Š«o1AÛß‹Ӆ[]O/7~‰®ŒlAŸäbDqX²f»ä•Ê•Œ0–äÂË7òé—“ùE2oÕ&…¯qÁ¨˜$Å΃{ÞË;ðIvKrp3;|dtÉŒ·óåÿÍ+’ˆp«elZ„"È÷Œ9ÓŠ¬÷ñŸ³ïap²%¤žêÝï·%ˆmPŽ ±J?;%N5®ÇÿF,«~ã9Båœe_Õï±O4¦øävmo7ýÿ,ˆ…h³¾þÔɨ’ÿJ7PÖžÍñ¥ÁûÞ²r6Ü5ìt¨Ÿ´±Re(ÏÑh*N X Œ¬*T—ÃFlËgU.w—Īì³&# \ BÄìÚT KÊT‡°9ïfÜFÄÈ+(Q¸Òjo¬Êµ^LeUšñ½{ÏN©WÉrQë‘f£A #@«ñÿ-*’+^Ï•/v2@…W¢#àfëñœ“djŸ†‡ód_,‰Š‘œä6Çÿ}ôÐm?Û±ƒ•œa]ÔõÖs®ú=ŽacΟòÍ10Z¶7ý¿ž(qw^ êdêfTSéºÛ{´®;­ÖËš¢($T­¶Èöõ_]\E¨8½ÚäsĪܧa»í.z¹1s/Qvc_¹r»ÈQf2ÆI6Òxˆ#ñd&C·Ûmˆríªf\bâ%L&‚8ɵïm¶Z9 ñöüòb£Z¯H@IDAT¹äÕ\y}C©Ï™°|³_”"È÷À™„¹1¾È>9¿÷Àyÿ:xŒQ 8à®ajDùªßW”{NZ— ÓÿfÕןºÅWºîˆm?ÎW¥C2}ËZ¨Ý‹>@•˜bꇀ3ÂñgW™[ù^¡÷ªPq Ð{ëWJ°ö¼Ÿ2Ý.HàÊÊ`QFH8fÝK@Æ=“L$0×82ƒáñÜ…3ñ6‰HjÄ–Æ'êÎŒ{ážL¤Æ³0 AF ^¯¬/‘­.‘¼ÒÓ>Ȭv|—ùñ„8xùL`„qvc£í} IüÄÎPq´þ–`bŸm¬ †·ßWX”#£¢ÓXêl»GÃŒuý¡“KKJº£DÞX§¯»eO†ë…‡±Ð»¸_Vd$ÌèñõêÍa*ÎÏõ!XâzT âª Ê¥È2Wˆ) i©¦ùÎ-,V8o#5"@¯:–ÚèÇa2Z+e]ñ*ò%¯äª¤!þ$¹;§üýòDyñÊÄ€’duJr¼åŒŒè€uú“(Ÿá¯ª÷­ç\õûŠ2£Yëªgf÷ #@LÝŒj|×}À3›ûÁMµ'«ÆW€‚¸ÄËÝ ó 4¢*/lCÅ9Nû(#?R£‰2Ý.´ë›1êR¿Kt¤ùØ [IC<ËÊ\>× bnä ø`äätXVBLdd«{P2l8¿H•ânˆ>ž'‡ŽåÖûÐÌÍ»eÕ¦]õ:Ž}‹a.[š ¿²®9OžX\,ÇŠNq»';äéÉñ2 áÞ.è<ÝÍ>ét8〭&Éú|cúª¯ß£\6>Šu´†þÿåŠM²#çp³¹UyM¨›Ñ`ßuGæ…ËýN`~æÓT‚wG´™­¯0†ŠËþhV6Xdoè]*îM„e{öù(–eÄGY»^ÐýB=` ‰ ÊeÖ/$ÄÛHÐxàÄg¼ÆSÍ¡ýË«›}×ݲå;š“`Ó‡­Ì[š±(Õ”yF¨8‘@Æ‹lp ‘ÎßÁ¶Ì¢Ln̈ (~}Óûê1 A gfcQ®RmYâèêVC”?^¼Vf/Y'—M&OÜs­|RÌuóCÚ„*•ÅÅ$åä•yCbrí¯»UŽ­i•ŠBM È´vú+ŽšŽ1ÿ×¾€h¬ët«9B?4[Å —ÃÝé‹åå‚QdÒ˜AêœÛ%'ÈW] ¿øË»²dív¹hÌ@¿Xt7 B4É'‹×‰(}3c”ŒÚ[C·†Wg/•œÃÇÿ?{ßWq­vÕ{sï–{ï ŒÆ…bª „@(!)ï½$äåŸÒ“—„BI(¡ƒÁÛ¸÷ÞmɽÙ²º¶ü¿oV³Z­v¥•´»ZIsô»º}f{¿9sæ;Ê }ïŒq’ݱµjo´Ý5u4¬ÖÛ%/¿PFôí&·N^e².]#þôæ"yø¶ Ò£]PE­Û%›`ѽwæXLò-Wy·ÉLAú)êüÕÂùÕ¿>‘n¾Vútk/ûa%~õ“Õ2€üÐÞrV¹ß¿¾P»k2P.%wO-~k‰Jï¹7J´Õª:LpíŽC²–uN&îÒ6KÝÇã‹UþýñJÙqè„ôDÙn?DºuhÅSÕdÛc£®yýÓ5 ”¡,ô¼Vq–ãĹKj²2ü)qÝ`¹eeÞóVíPýþÙ”5?@Šzÿ|ó>Ü«“,Z» ðX™4²/ê«äœ8'oÌ_«îùÁóïÁúØQ¾ˆQ€† iÞ^…ò»Jåª×$½¶Éytx‚ÜÝ?¶ZÈ醿[ÇûiäÓï*× Ï´Âb@ìÙ¹8zVö=ƒŽÝnE6Ã͇ìë½:Y¾ÚÇé‹`ñQÿÇN_TËãg/©È¬× é‰6ÓO¥Ï¼Þ˜¿m¦T®ÒKàÊàþÞ ¿:wµìÅ{dÅ ÎŽéSFªm_ïLMù¨ÌBûOÕ—Ójy. ®œ,²0ØüÉúÂÒ tff]7 ¨VË¿õ]‘@×fùòüÀ+g?àÛøS#FøþbèB¸Ö΀äVÏËŒ~R\0>¼eÔØ]¸\ ¬Æýº»>Ôº<­ÒS¤uFŠœ‚«¥ Ôð‘Ý—‚ÍmÒå?ó֪ɡ<ÿÚ§«%!>F¾ùÅéÒ±u†¼¹ r> ÙVþ3µŒØCn8\V,zJÛ¬T‰‰ŽR@A_·#GÒní[+w‹?X&»sNèÓ’’j SvÈ–ãc¿ó +m‚‚DùDø[ɨ¾ZT çs+wwuýí“Gȃ³Æ«m‚ÎWæ®’.íZɃ·\'Liñ£Ðê²=pÓ5Š9†ÖwB+2Æ@ ™Ûì6Ù‰2QŠŠK¡ƒµ2mÜ@ùéwªQ3º$_e›yÖKŸ=E¹—¬€Ž(ÔûžÜ“²ng®êltmŸ…ôëÐÏJKÂyIOMRÏ1š†È‰|‡âAžüêyq3ôå’,äGeñýirß ¸ÆÉ|LO`ÛÇö¼7iz¦_e‘ædHïβ~ðZ6a{hŸnzW­ýµõŸš” ÃÐýÎ3eÚØ»‹7ÂJ]ªÒbç-à˜*ºwœØÖ%]о»sÞñp:,[öñûÎÔ”N3„kK›{ÿ7(ÿwçkîí o d…;9RÅé4ñ+ª8½ßxk§ëË„”[m͆츴ÃÌ;IpûÊx°ê&çÄy)Áг£Ph ÖSJrbu–™$P ^A”LO¡yxß®°x^#´Fç28@ ×ã‡ö‘”Ä8;¸§?sIYtõ½Cût•ëG÷W>™üïøó–ѰNo(¥œ»”/§ÎçÉèÙ°èŠ|ÿ¡›$¾É´lýñE °òº~Ý;ÊÁcg¹){À>qD_e¥c‡Ç{ui«>ôêü£ÿug¸EP裬£.à&ÄÇÊ÷NPþÏÓÆªÑ\uÓ”Ï0np/åŒä« -Ü{Ÿ”¡ÐÃîöÏî$vç¨ëˆÓµ¬vGIKNDg"ïµË­…`:`—zÍLMd:ٺϥÉ­“†Éð~]UGƒé°@_í ÜǼøí[¥ëËë´ÞyÖ&ÿ½¨P¦Wð sÒžFÐûÙ”DYxªÜ;™C .áp‹ }6›CÚ­î쬕c(Û¬§økþêÌx×ú©Žîàžá›ï}‡Ï¨ÑœóyùrÇõ#eD¿nò%tü¼eê˜jD§Uz²ê`îÎ9é÷ñ—wš¡ÚO8ë.¼‡IL­òTûáƒ> U^Ñ¡Jؤ *.gÄÐEøáŸ†ûבòwƒ“z=SqZÎa¿/ïÆ<Ž“î®gJîaŸúÜ?Í.Y¾ù€Œ†…èÎ)ê$ñëWÊ% ó><ëÂùä¹Ëò§·—Ê7>Rúb‚ÐkvËæ}ÇÔ‡Ÿ›¯$]Û¹>¦ú:®õêBÉ»R¨>Ü©‰ øH·“Y†¨’m°ñã<¤wP¼QT¶ü þCa³ñÃ64ˆé2q–ÕH5 D ¨V¨PàÇ–Bç-žík¬§tÀ£ ‘Ÿ›Q4 KU{¢UT 9OOàË„¥+@©ðS‹¯Né¼Ç/ߢҤ•8 VmN*¤¤¥$ÂZ=M1PÐBöÒËåé‡nV.«áÂwÀú[°h/†ËÆÑÓ0›ÿœÎ:ßšÖ—ò àn‘©Þmï뺶ËrJFG€¾?tð”M{+÷ŽOVl“«w(`’É‘téÞ¡µÒ×{K6*ÿåƒ`xêÆMp¢»Ç/ÿùierU“†þ\eHNrY¹Kainˆðíÿ Ñóþ½­T6ŸvvÏôµ–G‡ÅËÔ1Êtëy.B·½4V§R6äÞ:eäy1Ûиٔ•Ûå:uÛöS%Pîx^¦EÕÖ>nÜ;¯]ë2…8cŸÉ[ä¬;ÄI›ÅZµûëó†šò£r©ùZﳉ°öì†uêŽÉCÝ®ãgó¤§@„켫…òÐÍãÀÝœ(,ߊíLüïóöQý»b9[cÒżÕ;Uï{p¯Ž>¯mèAZ¿~ü•›a1 ÞkªõÜв5ãûù•Âz‹R$è0+=I1?Ð¥ÃÀZhÑ¥õiê—ß²>~CµÝúÈŽQ ¾óŽ­3áÃè:û-YZ‹=…íHæG›þÉ;à:A Ì÷Ì[È>qáØß]¼^Yh9©l«¶ìW”î}À4±W–ßÛ¥Ä;=½ÏG{ϸÊôaµŽ‹ ìý£Uš¾ËýºwP÷ñÙÉlÀáëkà:~h/ˆHÕø{§¹}±éwÌŽÇÏž¼³J¾ž;èÏózÛE˜ ÷þž2åƒLW o¹¦s |ãe,†D¸T¼§ ÇWE–õ+x°Ó ¨téŒú&Ó‰>úÞHûÐ÷ðuãÄW¶ûï|ùFÕþîßV§Û·JSkº[u[ 1zD‰'>[¿#AågOÜ¥~~ûê|u½¯5åãëúàsZº}û½iN‹e¸J×b)Å+úbp󨚚q½¨ªˆÜ#UP ÃCÍ,/-þR£Ô"°(WˆSY”õ^Ø×Ýðav`¨0çäwÞ;У4üuü¹ºåZÉîÔZZ4 îÑAÃåO8Dݹm†L€?!?Š[ö­v)-¿yu‘û8ů^Y aᢜ:þŠä™çÊÏ_ž§Î½úé:÷õzƒÚß¿±×üÃeÛdù¦ýòÖÂòÌKŸÈ¿çròÔ%}¹Y ÔYôK¼n«¶P“çÎ]º 0wFþ`Z›9áÇS–nØ«(ýtéïËaâìŽm”ëÆÖ_Çê¥Æz 0g¡Q¢‹³ÁeWBõûŽ é—ÝxNbFzk•ÈŽC'å–ëËÖýU' ’Ã!ø{µú»Ç†Þ8gê÷êâš¡ïy] €­ÔZðTÃXô?£,Äìõ$î§š)¯Ø®†Äî›9Z_^eÍá/MÑuV¼u°ZqˆzöõÕå{ÉÆ}°†W÷5«’ˆÙ1¨A7^;X}¯Û-|¶Iž €¿kŠšÄ¦oåLxZ6ô‡j&üM†Š¶¶rÐk˜(ôƒ¿¸Ø™øaðøúÖ€×ô|{áztF³„ €R_N²QК£¬eô³ÔB«2ß)úÿRhM{ÞFƒ¡Ã—p’Þ$ø2¿³h:ëäwß¾W]K ;gÙfyîìëÓÕ×í>mÀÄ'–“ø<…ÿ6KpÙÒñÒÉ]Mëû÷¼ Œ°þøó­°”oP ž>ÒÚíÄ3=ïm‚(2“|ÿÏï*Î]rïzÊÊc刢W*+ŽVaK·ÈÄ©Éy­“ŒÍÌSoáÜf›á¤Ôýº£î«ç\SûðUÿÓÆ RïYT&Žè­ ::UvàÞ^¸A6îÉ…›G7¼ç•À|"ØTþýñ*ùæÿ½®\¥¦Ž çò®ª[}½35å£ó Åút|Ï,±FugÚ „»êØ_„"Ï4 PöÔF$o[¢þeqÚ~ÌÕøTqÇ9“±®ŽèG;>¢ƒt”÷—n‘Û& ‘£°8v÷CßTSÑèg¼äý_»³úð—¾äþȇAÁÃ)´,×Uhý¾}ÒP‰ÇxQv‡,Ù.TZçú…ÞQá} záÚݰˆáÃÇl é™kŒÜfÂÊ¥® ÂáíK-Ü‘îìîicÐ^-Ê¢«µÇîO¾v‡rÇà1¶S—Xäo?x°bÛµzâîë«ì{îðƒü—ï?ày“Û:b¹]Yª (¼ß•ÛAaÅE 'µyçù8(â<å Vrû”8Dð¥N1€ÉÝ7ŒV–Ye¾SÞ÷‘OSây¦w(ã¸x iê¸0 i÷èrÁÎ-È?þÛ‡°ÜRÁBf¢³Â…¾âdag…B:>.ZxÜóÙh5iç¤CJAÜ+ö–É›;Käè•ê÷Àx¹­oœÄ UÖµ«í¹²dûûÓwïwçÏzô¬cžð×>|Õ?Ûátt´øÓ}ÏSدÁ„T~#u'WŸgÇŒî?ô©÷ˆÇû©)f°×xÄHêçòmBâ ”ýÝÿéã8H0¯IêdH—3rè¿Á¨ØÅòò\ê|}¥‹² ‚È jQæfo¼àå°âæ€î‚›Â ð‰zOˆðõžÇHôþîg›åî©#ܳâ=Ïëm¥%ƒG×õ5)­jÎ:öÞ·ÔWü![·ë° ÃlvÊGŸo—58Ná$ÁkW~ ÕAüëØÚeeã~~PùƒGø6ú³6Tlù»ží_K_×Td_gv¬¦|듲/¿_ZÍ9< á<9!_-™øÎ¶-ž§Ô7or.ï>g“·w—É'K¥ØË€Ì~ôÄn1Êz<¾KŒg–f» iÀ_û`ý{ }Ÿý Û¸7Hö¼ÖHÖç½ß™šòÑ÷s½»¼”YãôÞwžT£ìÁÌÃWZ(ûÒJ„#Uœ­Ì®€2Š8+gĈ.ÐÇÂ]\¸ë¨"WKãZ”YŠèh+†[1î´ÐÜ3}TTrÁþõÉZ¹µ¡š|%B?Èãø:å>… Á+éX6¦ï)“GõA`Ì.°Ÿ0¬·;ÊØµ †iJVF¥£ç½Þ?Tžç̶Ñ@¨4À ¤0ÃPg¨²hÖéÞ†+KÀÆñÏV( L«÷#Ä5}­"ÄÆoï.PÖ?Ê•)¦ÆYäÎ~qòÅA±Ò)Õ?xª¼Ãl D¦8R²­ÔmLgiÑo=sc¥c‹m€r•ì¤#…*ÎꈂrÅD§¥Q-ÊZÇž}sÁFPÞĪˆ` {ëKAÌ^ÁJ U €ì?æ¬VþÍDá\*(mliÕGº«ˆd"Aß§k[DÝÊ­’Ìêm¤ÿDú»Êàú€q2!-ÛFŒ"Aô…äb¤~`'ƒ.+Á’ýíòÀñGûˤÂ[údEÉ}ƒãå–ޱƽÂ[9f¿Ijà7«‹¥ÄYYŽçüôåp=ˆÊáÒtðòitª¸„øøseÅ®'Š‹2 Ò>’ô_„ˆZ0èú•w–lvŸã°ç0|ü½kü„¹h¹oÆL²é¨wë´&Ø‹û ÀóüÉÊ`è!içÜi´G¾óá[LhºeÐ/óÇT™tá¾Øl   @… v›w°\ÞÝS* â-±øý›Ù3FîA伡àA6b4Ð\4°â¨Mµ{ý<åç<{eÕ§¾­aú¢ ®ÍÛDe†#)RÅåÌ“ƒ±ý08jª¸—‘·Î£õêÕW¥Ï,‰ç&MJfhk}>\ë™× tgEËìO¿Õ½O?«_?U9ëžÖbÏ}÷…ؘ4²ç®ßí§˜î÷Ü7­rîvð:ß2a°¨æ„ 1¡¯Úž#ßÿò „žMT^Ÿ·^6ì9Z (ӇѳÌÞy ïÛ3—»TÉ×ì  4/ 0 ÈûÇ rʪùóI³3¢än°WÜÞ7FRãªNÞj^š0OÓ5ÀPê?\^éa‘YzúÒºÿ»m^8ua€r8µ„¼"ˆ*î<G¡´¢¢¢ÖØ;P‚:CšD´×Œc™á /+?æS ¼b€”©cúêÓfm4`4ÐÂ5p±È!sàVA€|ørÕÉÀTM ¬ÇÓ²cä ceT39¯%4ºâ1HH¸'Ð5¶n¾²HθÞK¹ô,Øxt]˜ e€r˜”ì"ƒ*Ž‘.\æL»¬þ‡ƒòlÍ<‘þàxå¾Ï6ìS\Ë$xŸyíå¯ÜÌÝ<žÑ€Ñ@ (ýÞÒ\r©¬ïqÅTŠ*wÐ÷˜“ófõ‰‘´xc=®¢œf¾CŠÁ»så¿0 ´7¢O†Z³BÏ×Ä‚ê2,=\®üðu>ãK‘½¤ú¬U}AˆÖ(‡H±¡L6¨â0}$O?£Ýê¨þJŸ `MƒÊ%€šø%“@Ï¥1Dë¹1ò6y  T×ýi=þþÇWJª[“b-rs¯XäXd|«+°aÄÆíˆHÙ©m¦0Tz¨2) ÿöÞRyúÁ›¥]\%%i8U}¹Ä)?Z^äÎ’ï@{Œ¾îq ߆ÊáÓuPsjlª8ð'çé¹Ö§Ô(¸y co”"ü¦‘ài€?´±Üš¾ô¼ÜLJFF5i€CÉ(“9ûÊ$7Ï·ld‡h¹«œLïa˜+jÒeK8·íÀ10$ÅÈ&ò:"`2ð&°îŒ\IjTÒ‡Òå¯s»,,‡,O¯#2äñ³…Ñ2ï1N±BÑãÿ!Šæ]SGËü5Û9¶PF Rß­ 1d@Ÿ?¿µDyî…ýŸý®°ª™ØâéÏ ….H”V‰VùäåwÕnØÿ™±›°«<8’*`gSC£u¯a­Ã'–J‹2 ÌŒãèò¯–3ÉŸV>"~Õ—î®Å+Ï(Àh  Û{Kåá®Ê¤W®Èï×WÉR­òõQ ²øþ4yíö¹µÉAP}“O‚Vä!uÎp×6»Mv:á~¦–oVù—OÍFwÏð£wLTç_ûtµŠôøÍ/NG°ª P¨®ußwñJügþj3°‡Ü:q¸¬ÜzVëã (3ô:åöÉ#äÁYãÝ÷„kãëŠåó#•¤?™”ب®F(‡«æC“©â”,?r|ܸJ2}"Dkôø.ë¤áZWo‹²NC»pnã‚’w8\}Y×OÅ¥eŠ/51ÎD𫟠Í]FõÒbÉ2øYþ×ÂB¹æŸWäK‹dí âVJRŒEî€ß11ò“£ãÄ|š+5Ô²·Ö|ïá“2´oWÅ»ß?»“lØãVÊþ£g•+}‰{ui+ECO«p!šçž<ê}$¿ýc÷ZŸ/"|º–¡}ºÊõ£ûËxÌ›énÿ½¹'•¥º3\<(ôQfxúpÊÂCåòâæJCÙc#âer÷ưj\/ÂÙ‚œWcRÅY-–ƒ¬µ–—õȪêÚ.Óí Þò´`žØh |ØrÆ&ŸÂµbÞÁ2¡¯¥·Ðál\çLÊ‹5®ÞÊ1ûU4°iïaqÀõÉŠm²`õå&‘_T¢\#Žûg·—e›öªßö[ö+@Ìö9£˜•^™»Ê^b|œœ8wY2Ó\‘_»kIIŒot—GÒùþÒJ­ ]cä›cÃfÿÓª¨¶6@¹šJšÎƤŠðrOæƒóG½²$ãËAŸ+kT”¤'ÅÀŸÖ"[÷3@9Íqëþãp»ˆ‘öY)ø1–ƒ S“„Ñ€·v³©€ ó•É™ :+ïkúµŽRÑò81©u’±{ëÇìW×Ý.ºuh%ýº»Ü*è_¼píNÙ²ïˆ\3¤—̸f°¼€‰wŽž‘Æ”A=]Æ¥ø8¼ûÖ§)k±gÊLƒBŸæHv(¿>¯ÀÍÞ5Í*¿½!IÅ"hì2FŽ–[M5/ª¸Ã#‡Nê¾iÛò?,ÊÊL‰ŒðÎÕ(³œ´"G s!Gdtt´´MŒ)>yAºw¬ìõ†ü¹šYÔß~ü€ŽèÉÈ….SßFŒŒ®ZÀæÃj̈yÇó}OÊëb•›Jz–™®põ ÏÙ¤Ð4p>/ßÀójbÝ$´>uTq‡P^ºq¯ôìÔF®ÖGâb£$¦bÒvvÇ6’ +ñ¼ÕÛeöÔQ’’”(ŽŸsíg9ñF,^Ÿš?çXµ¯óös¸'ȉ|×ä=º#ýõ¦dI‰ãØKã‹Ê_ *¢Š1ô Õ'˜Ü7°ZÎíP ]/ÜŸ‹Ô{2Ÿ¶(»@r´ÄÆÆb‰‘Nñr®°P>\¾M¾~÷då›ÊçiŽi—•ÛeÎçÛ$–…Ážc„zÖ:oŽÏlžÉh Ô`O– —W›Œ§óÎH`@XÉ^aÄh >Ø0‡ï!'ñy 'ÛýlW Фc› ™³l³šˆgw88~ü®ÉÒ³s[yìÎIòX2~† ð€Ç+#Öz¦é¹” Ó¾ò΢ òÖÂuò»oß«À²ç5ÁÜþé —ï¾Nó×°$3âd¤ˆyƒ#¥&Pލëó¶2»ÊHfVΈ] 5 ÉÚoµWNæ r-ÊPÚD)€‹†X¼”qÒ)¥Hr.åË»K6É}3ÇÔ^&sE ¼»d³œ½tU®ë×^1™ƒêÙjŒ^z•š£Ö-]óàRAŸãýÜ&‚*%¦õk*&Ý· ú#xž£ià&ÐÁqñ–ñC{c’^o€ãc²tãùèÛèw\Z†‰pï/—ÕÛ* Ì‰x?ùÚî‰ñ ø¾ºÄ"ûÁƒU’}âîë«ìaú¹}ʳ„ÔPõ0\¼µ«Ô7Y_®oäÉ{îÂTl ì­‘&¸Oª¸œCÁïhü>ÛŸÄc|/”â´ZóÄQñÁÌõ‚NQN»ÓQXR^Îm÷gD»^ÈÅÇÅI|†z°´IM²Ùqè¤Èüõ>Ò6”ú gÚ´$$ï…PÿŽi˜¹œ©ôJýÒ¢ìËõ¢¤¬Üi³ÙŠYG^K8‹nò2ˆ œ-pʼ—å˜AA|I"†ˆ§t‹‘™Ç×u–Ó õ¥&s,„pÀŠœwµHMÜ;~ö’[ÆM×Uו¹n µóË[Kª0\Ð=‰¬/‘&(GZÔ¿<¤Š›¦n·8¿ª¸g:¯Qg¨$*ê²84Ï¡³¶Ð=_Jåös—¯VÊt €‹¡:r|©JKJ¥´´T:ÙÊÅé(V`ù\ÞU¹mâP㳬•écMŸä9pW9 ß¶Þm“dP·6JŸÔ+õ«]/¼o½œ_è,+-uSþyœw×Ç1³i4Ð,5@?ãE9å²8·L¶Ÿñm9ŽÃDã‰Å7öÄðt·há¾£ÆÐÀÞ]äÌÅ|ùtåv¹x媤§$)+ðu {‹tywO™üvM%D™+ò/¦&Ed± PŽÈj©{¡\TqæÂ˜Éu™å¥Å_B*/Õ=¥ÀîHLK+(¸PÁuh±¸¸f¸µ¼´ôÔÕâRK€0©j(. l…µeKNJH.“²òr±ÙìÒ=æØüþþöÁ éÓµ-Ø0ºHßnm¥¾=åŠÚd.!O2)àÈ qà¡Ô!Yz`dRr’Ò'õJý’Y„úöÖ뤤¨ð¼çq³m4Ð4pèÀ1€1²?· ZŠÇÏìƒaáX¡%ÙˆÑ@$h`ú¸Â¥) ݘ~¼¬Ð]ä1£å¹éIë®d€²»ªšöFUÜŸâ|ŽO3 #õ… (·]¸°¨pÄP’ÌX0æ“—à/³®YœgŽ]š•õÍp§30Û}5Ý8ÙŒþÉ´(—$ÛožÃJžOwä9„´Ì=~VBâ½dÀÔäÜ×òš2ÃRç#8K)™G$N7R£$»uªdd¤Hj*–”d¥Oê•úõåvÁº`ý;°oTÍ:Ô Uo¤8zú‚ìÎ9)—®ÊeL¼¡a¸ehÜX£x_~÷ZøÞ %éò“ž’(Yð¡ݱUU¸õÁüHå¶8–ãœ2áä<_Bã±ès#7`b^]gà;}Qv#€# ]ÆÐxYEݳ*Zа ¨úOfý'ÉLJc` #-W+Ž–Ë÷ªŸ$jaPÛhù .øýŠT ߯f¤j 9•+ŒTqÅ9ÇA}Iøá·œŸ<™c&•LáÕõªÀ×çŸ~¸»×a7îÎÍPv¿´rÒÚIÖ 2!H&ø£¸@4iob$.®DÚ•Û¤ Ô.åX‹$ïý³ÜI¹njÿÉòFÃV›xD|Iˆ–Œdº­$H À1ArzZšÚ¦ÛõêËšL5¡.œ»=צu9Ø%jPuU±ÆÊH @‡<&Øì•e›÷É$vèRP)¨Ÿ¸Fà-Ö¶òÝà{n)Ì/•cð›¼Z\"-ߢ@ó”Qýeʨ~è¼…gf;ô–ÓÇÆȧ®úÇüXï­€ñ”îÑ’W7*EŽ€±î9ÁŠà¸±ë>Üuí+¿*õÿùVUÿ“Q÷SFöƒ;˜ ¾tÖ\­CTÊo,(F¬¤ô]âßo†1'ÂGhL+uÕW³øvª8 €±S”SQaa!Ý/üeOàe?êäÛ»,–'‚£Ñ3<¦ÄqÎXq~Cø¡¡_-Ýâ0ѯ[ú.'Œda2!-ÎŒZD!HiîÂ…ÌÔW”•¾Ý°ÄC7ÄɰÆÓÝ‚–dfZç]Ödß“øX»°;´1’%ŠÒ‹g1K#5h€mm‘Öã7¬S“kútm'3Ç 0.Bz«t:.,Ý$ËMì¾Ç)+c ª­÷);Zðúô7.—%X.`4Ê—Ðb""€ÝÐ#F­ëëV±;ç(»ÖÊ¥üB¸‡µ“¦î«¨Û³þ?\ºõ¿lFcUýëßµ*7˜f¥†rÿB¹—ñÅ„0Lû˳’%†žH”#½†êX¾pRÅ¡¹»Œ¢ŽÚ¼ðù•R ìó9ï¿{×מúEÆÓÝb©´*üi+<]ûÉ ƒ›E €r €2­v`i€å™@ÙÑ"@²n Z/´Ó‚úÒl!ÆôñNLLP A2ÏSÞBv ÔÃa+¿ºzÞÜ%8Ï©ý\4Xö,¼jáûll‡ŸmØ-·º]Vª<~Ã3éÔ«]p>CÒsQ“NWl—çßþLî¼~¤L3Àëêúí–9eűrYvØ&Ÿ-“üR×GÙ;µTP¹qò¹Ž¯…¹¡Ã¾KÖï–÷?Ûäªû;LÝ{ë›ûÕêÿóíò—w–Ê  »~tŸ¿Q¾Ò1Çšž>­"Ý-*0²´K¶Ê¿nM–6M$:¥ÊM¯ÍÕXâpRÅYœ(¹>D¶(GMúx0ÛåË ölÞðœeÔØŸ¼:w•ó+wLtw)5$¸SÛËÜ&PNLHP YMô++Sýà2P(7g£r…1Ù¥_†ûæÐ5Ý*¨‚eZ•9q™Çyž ™ºôêþÔù˲yåòÿÁÙY¤¬bÑ`Y×›÷­fÐVd·$ à7¸g'ÐŽ04†µ´FÛ|ò®IŠÆð½%©M€¥>Ûi-I)7Še‡Ëdé›l8‰ À~ºw­­25›þÆ12¾ÇÁp‰d`Ý¿®wS÷µÕTåyUÿ³]õφÃ驨¿U•w𭦦·w—ʳËáYQð.iQ $3beS”›JMÕ¥œNëŸICN‡†ï¶([Î@,Ê`c¥k}º.53ã5lY>ç·Œ·hË2A¦1㧦Ž#¤%™“b\Öd»²ä¬óƒÅ¥¹ u£ÌÌ¢E™º¡U™fØ¡ 8ŽQVfÃ…?K2Aò¦½‡-‡vmŸ»mÍŠ¬…uĺò9p¦…‹Él;—¹+w( tßÌÑ-\3?>ßw¥¯ùkìfXcÓ†çd ”ZÕ¹c¼l¼÷}©¤¹Ó`Y?÷¾>ÎPጂÈ`"Ðȹ{÷¬Z·hÞê’’"Rûqa=\­XsÛÓ¢l€2â)lgœ¸WL–u{Ž(†ú\©¿¨?2E,Ù¸OEgËêÙq“tý$™‘àbª˜p<¾ "zr\*L¢ÛÀrÐÿ±Ì¦î®x]ÿ«¶”É#ú¸GÄüý¶5<ÇÐ¥ÀöÁå((É£­9Ô9ánÏîЕ£1Ræd½Ã—ír¥Ä) ¢m³DI8Ôg¦§ÉéÓ.õšŒ:Qæ@ò4@9-5ÁkÂBgq’N ~jʼŽã¥ô}¥?,­—ü¼qŒÍŠÉd²ôÃw>Áög½ íÞµWŸ~=¢¢&%`\.ŠcbÏ—FEÈ P6â _-*È¿|ôÀþ‡vo?Ž[i)fg…–cêóJÅ’5ë„ucü“¡_â²&Û”5™o ŠJ‹ƒ¯k›Ë±óyxÍñ‚·ÉL é#‘ cÿÑ3òêšÓR“Eÿ¢*ùuO·‚©"V&ƒ­"ØþÆU2ªe‡màÈÉój$aæ5k¹ºùœþ ˜Ù”«I(žJ×ÿaè¶W×öj”¬)¹`ð!^ =-ß‚‘¦«Å°Œ7.‡z(ꩦ4á%)=2*ßÛR¸§]-8' דkvJ‚ƒÚK& ï «Êy4‘Þ!2@¹¦ZoâçÂ@G‹¤K,Žx½YÚ°Z5¾MɃ3n]~pç¶ýmŠm•š:É臗¾qöüóù6[¥ÃS ™˜SU4@³ƒ¢;)À¦ ŠiI&P¾\±Mð¬ý“+º@8bDi€BeMFÔȸ³ÔñCÈPêȯ^](C{uRüºµ]oƒÕúÐñsH»]m—ýü¡ç¥S› ‰¯ˆv¹xÃõÜ÷ß86èyy&H=RŸågåjt:~,2¼CŒÆ´wPnlÑm`wî‰:Õ}]˽dÃ^Y¼~oµÛX'Ï>6«ÚñPàs/Z·G2S“B”uýï=|Jºvh¥ü”é~é Šºgç #¡^ê ‡zEÃôäÐþtÕY‘ƒ;¯¡"uÆÀƉé‘ìjc€r¨a1ý ª¸Åø‘» <ùìO¢8ß ^‘¬]®I5N‡%ж¤-—${hO¸.5õºdLè°ãúHiÙj€ä åÖ÷ï1šgJèj¬ýé_bZ“¹Ð’LÀl¬ÉPBMâIvLâ+Wnyˆ¼– :>òÃ[;+ Öî ;P&@íÓµòäìÉ_aAþ #Ãâ­N=&ÅÇK/Dß|bR¼LÊN”tDœŒ$Ñmàâåm1uÏç= »rëàö_ß[.× í)ƒÀ¬Áaëæ*Ô%ß'†üæ;Æ Sœ—É@™í y (ÉÜa8Ô}·NÖmuphÿóãÕ2óšpµé[+•©ïTÃw4²~…Â÷Ü-''§õO0âÞ ØâüÊñqãžé¼v-Rƒ?¢W[O]¼ ÞôÔmñä>A\ÉôÌô>=ââ–Ó‚‡OôÆK—çàx–æû•ÀÃ…@´n ’YWÚšÌú'0&PÖ¾ÊÔ=Ï»z>Ø0RUü ’i…ë zsµ¨DR–º>²çðiY»#Wúwo'Ÿo9Þëh7¸‡ŒØ]Žœ¾(.Ý*…%eò«WHŸ.måöÉÃäÜ¥«òþ²-rúüÉJO’Û& “®í2Uö›à+½a÷9}ñ À&\ÿñ¦\?²¯$%ÆaçA¸xÅóÜê™×ïïf9xü¼ònèß½ƒ:F öϹk¤¤Ì&/ÍY©¬ùÎýÓj­ƒšÚÓ*Ç$Õ¹+vÈ.øÖFÃPqíž2qx/ž ‹ð}º&¾cñq.ŽüHµ4ºA2‚Í|ˆ÷ÒðhÖDèþäl‡ö¼Õ»Ôo*CÚ3"/'ëGâ(‚ÊÕm“½ªU\Yñ}x˜ã,V±'^‰Ó°E™×kG`æ¹M°Vô¥¬VßN޲X”/”ÛWm/(Ø…ãwÕ@Y¯qȈ P§­[ ”éV¡:#X,Óƒû<®ý’õ½8dÄSd[±Ue½!¨HM¬ßOh|÷h5..-—›Æ’½GÎ( ;´wgÉHM”n³äø¹Ëò OQÞ_ºÁdbä‘ÛÆËº¹2gùVùæ=×K1õË·É}3FKg翼³\üŽÐM¶8.‡Nœ“Ó®ÈPøÓÇ4ÜÛ¤ËØAÙRŽçùϼõ¼é°øtæÏ¨yô½ÍÀ$ ;”£g.É[‹6ÊÈþݤwç6ù`Z#ÿ}ß Ê,4L+¬s–o—.(Óð¾]Ôýµý‹Ã‡²@zÖ¡ék»'œçu`YÖÆüb¾²•ÂŽÍeŒnh©©jjwtëXNÕ>´Å»§ŽRõNêÃ=Ú+— ~(×Ô)'É(GjÐÏÏNÓ΃ÇÕ{hx´µV[{rh/Z¿O²àÒ3¸·ë7‚`9Ò|ÓçML—æª hÀâMç”§lp€2 U¹ÖÕþÃ[ à¸&Üævù“;&fÇÇÞ¬¬Éø8ï*,yÇÏb!8Ö 6Ô¢êU/Ô­'Xf…à˜k.ŒtôÃ(GëŒ$µ° ì'uL}Gšè6À2FŠøÒSmuà«ÝÙ¡ï\Pš±C¶ýàI÷ã‡?v8Eפ¶WùÀ£ Îg¶ûÇo0<ÚõmžÚ«¶”ëÇôkHfç(R:Hõ7õÕ‹¹¯4P*Îá¤UyyC‹‚>Ÿ Y%ƒ>CÚ¿:öÝ€¨#úQ'ü9ÀÆN‡ý8Ήfž–d”•Æký§¿ä\ë…•¥}¬Ö„Ì.+'?ÄG´Þ:ä¨À† Ã"ôa¦< · ºMx Ý,rá2ÁÏ÷^+ÝÚ»@«¾Žþ¦ZøA¢+Æw˜¡˜-^xÿs}ªÖu+ðšï¨¢øzŠ$k‰ ð™ôõÕÖzvé;r¬‰º< #«ÕJ–ðJÒni9u1_oº×µÕ¯v§¢{‚ebÜàlåzãN |öp Þ2õŽ1K­óHLÔ;&´x¯ÜvÀðh¡Qhí-ø];°›šÀÉ(³œ¸).ÚJ„Ç5ID²Hç.ŸÅrkΈ]ÜûõÜââ°½ ×ºº^è[Ýë„Äè»­âìÀAŒÅz*Þ!oषO-}kÍR»èìé‡l|‘Ý-­~ú£Í9?–¡m2’UÔ7úžÒí¢K»,euæ$:º[ÐbßQz$¹ÀÄš92²_Ò»“Û·ØßSr’]rb¼²ÜÐOšnZZaRAIî©óÊ÷Ùû{wm«&[­Úž£Ø V`""]1X¾` !™Öu°Ò V:º\áƒÕKN`Á Ÿg/^Ž4¨hÕ¯¬û‘=:'k怚æ3ád¡« óHÙ»ý…µ>2cyØQ&×3'³’ÉÁHÃ5@=rò2õJêÍ2°žPÏ‘Rÿ™3®“B#k Tq°ù¢\!ip[Âïò·ÝÉYäù»wÓÖˆÑ@Äh@¼CýÞ?»=X,ŽÊ/ÿ Ö D{xÖ5rÿÌ1ò&ô‘™Ò*=Yú|i>(NÅ{üÑŠí äÒwšVgÎ,÷%ãõ·o’½ø±ºï:¸P1¼ž+ã0ÉÌÂéÇ_¹¹J½ÁÀq Ø–oÚk¶«‰‡_œ1FY¦ƒ©“`¦Uå‚°ÓØeŠÎÐAtpž{s±¢Ó7¸»,C}CnÆÄÒ÷>Û"/}´Z”x…xüÙäO—DzG‰.!äzFRñ$‡K/¡ÈgÕöCÒ©u†{~A(ò$MÍ¡ Ü£añ`î‰$zÀƒ›@”`®‰ ™*ëÊñ¿Ày”}*#wÔ° »CñOÁ¢U›ð¢Ï ÍA£&ª§˜î.9Ù%¸h!ůŸºCï*>Ù¯ß=IYu V(´$’¬ŒM¹ö.À3*èrAÖNÐûí닦Ω<<óá}œt÷½/OW4p:¨k¹uâÅoJ'ºrp¡§ŒÒ“õz€"^•I´D{>ïyðæqž·šízhàOÞVå.‚ÖÇÜ/4óÆÔÑýÔ5µÕAm펾ò_FÑšÇv–ÀB@Hñ®[×Ñ–õ_û¨ÓÊžÒõùkvÉòÍ_ö Wô”_¿²P½Ã쳓\“440G„Æ€’ROÄ­)/ï@D5][×sšC›£]¤ÞŒ4z@ãzQ×mÂד*ß¾\>@nf¹‹*®ÞOd±Z*²8*¿˜õHV0·5¥{\ϗꑌ¹Åh $h,+b,¼?žB€¬A²>NŸUŸò0Sð–ÖpߨI|d}=Ù3’kO\Óu 9×Xz÷UæH* ˧A²¯²6ô#¥4{5½†&[¯û#Iïš\Ï)õäP×JàèÍnðU{>ßñ³yŠ2R_SÛš®Só:Ô¢åȆJÈ¡]ÚMRoF= ±(‡ªÖ#0]E7rÈóÉ¿gñ°æ¤¾Ô·¨ø»éáêÈ£\%Ëc#Fô(wÚfqÈ i:­ýÇ*˜£Ð?h•K¨¢3A1·ôÈoÌZÛ¬eqd¦(»ÎÙd¿.‹5®\¬1º f!ð s”—ˆ£´P†c2׈Î1…DDÙ›X!*ß3·s_£?.]/Ê‚À£Í ·ä,ÏÁÒ³Skõ|;žPîô÷RG~¶q¿C»ÜŸî˜{rOÉ=ÓGUK:..ZYoÉTS›°hÃ?¹WçÖòµ»&J|šöòéøú(Ï7@­Ý£‘.râð^à>n%¾MÑ[q4³C4Éœ¨e:Òù„¶<®Ú€ö7æùº"ª)_0Ö¡Ò{]ÊVgpS—Ä͵‘«`PÅa8}V— 7\'׋Ã#FŒ±;m:A©%:þ¯‘«-S2£ÆÕÀ§–Ì´D9 ?CFÄëÕ¹ßð¹ôsìóh[Ÿ ¹qŸÂä^ ÆëÚ!=UôÄùkv˵»UPi}ᆑð„ówÖoˆ´[@CÒà½d°ÐB¾òŸ>~«ÞUA|¼ƒ»LÙG¸•ªód¨ $Çß;0Ñõ£úÊÄa½èæDGÁg²C«tÃ÷ï/Û"§qM&Ý6i˜tm—ÙÒTßèÏ›œKršZŽŸ½$›öSe"ˆø5,δÏ[µŒ…òôƒ3`1Ü¡&DÝy½ t*‹×;sUFaó”Mòß°ûˆœF š$ŒVÈþú‘}eÔ€nâ‹»—pŒ4žHÉæK¼ë…¾Ïþ„#SqVÿŸëÚy¦[S>ž×5õmÿÚlêOfÊ_«ªQÅÙߨõ& ¬®8°Eùä¨QÑݽS'e‰¶>§·ÍÚhÀh ºv·8!­Ø!¥v0¬ouý „ö=+SÇô…ÏbŠ”Á‘ÀYÉ-·è^ºRT…;uáÚ]‹ÖÓÍT.Lô¾™£UÚï#46£ï=rÛx õ§¿u«:nþ…Wì°ìÆD/†K,,ÛZϡʧ¾éêrÑú®º?xüœÌÁÈÁ¸\Ä¡žú þ=¥3¬ü_š9F†ôîäyXm“?— úÎ)ÃÔÄøa=Ý×·—ÌtãI…Õº]fŠ”b’Åw¯ûæm°þc0 ¢õ̵£ÆÖ@À঱ jòªQÅYäW~:ÜÑN›fR¶8›ÌwfÚ´¤Â çu§ïŒ2Öd·2ÌF¤jÀ×;ôü«á™úÈ­ã•ëïß>X¡¢siË/Cázûj=ÖÆJ´zGŽ&ëT´¯®êVZ°9´ÿÎâÍ:)5™ìô¥ü*Ü«î“AÚ %³K›4wj¾ôî>æ ϲ$|;w%,%¸vH5™‹ÀüŒ(¼øÁJùñ£7‹öSí z1Âúï—-tßÑB7ž„ØùtÕ.¸ï¤Éa0¡<<ëZuº®Ü½:͆®ó J¤ujU÷O½74}s¿Ñ@}4`€r}´ÖÜîQTqöøX° “*îÙÎk××ö˜Q¡ã¨¸Ãvµ¥¢ çDêׯ‚Ù›6НtmY™óF§¸,œ¹Z,Å¥e D†£€ WM?ᣧ/¹³óÅl¡OÖÆ:yTyõ“uBËòLꋨ_Z.)Âí¢c›Jp¥†èõX€€©‰mB”Cð’M…¯(Ëκggˆ xÜ'è Ñ©M†z ˜¨êÌ&úIYÿ›÷Ó»B 0-ôw¥/2ƒ]pBÙ÷^+ìtQêÂÝ«ÓkèZÕI‰tk•ØÐ¤ÌýFAÕ€q½ª:›fb¤ŠÃ$Ž\Uz§3«¼¬ø¾@ž¤ÜãF¸ wÓ˜Ùï­°PY¨¿Yyå°à£¦¡Z·ô’™«+‡©C?õ|ÞU9q.OÖaRÖDÞò´Ö¤¹Ú¸SWoËQàˆ.Utwi—%I qàÜÝ«Ü-Ê=lü C)Ô#õÙµ]†Òq(ójHÚ¬–1\uLt{Ù…°ÅË6íW£­ÓSz„Z«ú£o2Ãoðð3æˆÁŒ&Œì×E¹md ¦¥&î^}M°×ºþÛe$¹ß±`çaÒ3¨²Ö'asOÓÑ@}©â¬V{”CÃc§Eoù}ðãGÜ kr/^„—”Õæ¿›F¦ •Áal•Ô„øÓZ0Áê˜_ª¶`=y‘)íÁŸË¡øñC{œtMÜ©Lo>|_÷>-v¼ÌÇÜ8Fq´Þ¿×÷0¡\¾”VéÉÒL¡âDµDLlƒ|´®~È0]¨ËÅ2²¬,3ÙGB)¹\hîÓµÜ<~šdI [›tmŸ©ørßýl‹²_?ªŸìà Ånw*«ôG`Hás9@lÏу'gOB´8ÿܽµåYßóÔe<,ÛY© xÃ*;¤õM¯)Þ7Í.5i“e'ov'ÔÇ-KÛLÿ¼ÙMñ9›Z™ Pnj5ªòÖƒ*Îꈲ:Ä…a+Ö”Ê~KˆîßvŸ´XÿÞnÑ"“¾û Ù0ˆL h€DHÊ5+†»¹n`UŒ‡1|íIÞ¬§ pñæTõLû0xËÐÞ…‹Ü©Ç«¶çÈ÷¿¢r&U\Έ¡¯b<ñk,˜ÃE÷yM…´9•®;µX”sGâ°9&«ô,[\tl%ÛFM™˜sF¢Z‘£@Ž’h°Dƒr­CJŒœ/rȇ ÙúúÝ“•Õ.BŠ[¥þ¸Sð˜º\XŒ×Þ)§à÷zülž¢™ó¼9”™ùЭcÎçÛ$~Ñý»¶vë˜úŽ4ñl,ëþ—TÙŸœ¹uOú›ìIÍ tv‡]¹vœ¶g·§øãîõ¼¦!Ûªþñþăû7»Mªz·ô{‰m !ÏȽìÜŽ‘ )üæ®Ü©:«ô%ßÎë1³…<éý0Ÿ€ÖfZâé"Åzd¾;Àp’UÁŠs“oç ßI¼×è 3ÂG üq¤_ÁoÁ'+vbÎÂ9IÊ „מ:ºŸø;Èó4‡k"ï—¨9hµ‰>¨âþì.zTq–¨J Œßך]/ìN·5×¾Ûiýúî¼Ì†Ñ@„k@[ù'@ŽÅ× €»‰áéÝ%›"ü)ª¯?>˜œÀ÷Ù†}òòÜÕ`¿È•™×P4sU¯ íÞ»K6+ý éš*³8¥_êYë<´¹žº.n,+ËìªûJvÀSlÜ+gŽë¯ÀÓ[‹6"äù&ůýØÜ +\¥ã{CöÈŠ•øx¾S±ÛÂ¥Ïq„°gç…<èäG?tTà3Ü«£ …Ë)Y]Û·àpL.-—‚W›n4œjÿ¯¹k¤œÚwL*£+æ!0m韃«ûb~b?™Œ‘‚uŠ¿ãêd øg,Ê- ’}ĺRÅYíÖ(»¸<.jr½8Ë´ \DŸTº«œƒ%±[F¬ôî®ôI½R¿´ÚFâ°»¯6À²———ËN~S÷5>]ÿ´$·KtJçŒõ.ñjÌ60--­õôŒô+,èëý$ž -Ôg¬˜ÿpF{ú0-)¡JQ4…#^Ý_;„œ×Âðó”<€]-šFPï×Ä‘~ý¨¾RËõëèð3l:­Ôtçðw\§Œ5ƒÍDùè{ê=ùÔ' ”룵Ⱥ‡].Ïn—çvKú+‡eÁÿˆäÂg1Ž‹Y¥Å÷#‘—}%”o³DÇi ijÆ5Õ=_<8ÞæÇÁv¡’°Yä¾®ó•~Ç<§}sÛs?€ÛÍ%Fk€?Øp111꣞”„ p%¥RZZ*å¶rim/D÷¹˜'*(#¨ ƒU¦o·¶aãYüiÂ%yrIF–†Ü޲H¯¬hI•dè’údǃúÕ®á/eÍ9úk=Û¥J¬õ²¹|ÕÔ½V«8~vN´Á’̺OhÔ6ЪU+Ë Y_вþðbYyòq?Ï ‡2à ™gîž:BgUmÍÑ-­Ò’dÇAŒdUÈy´? ýÊé~A9¹@q¯«ü«#ý.€ãñ`ÖyL)o,Ø ßûòtÝÑ×qf0Öô±N‰Lo`”ƒQÃáMCãG¾ûÌH‹Õ2¹«X-íÁF±f-eå«õ·âBI-vÓÛ¢¢~ÿèŒ;¾äë¶—ÊJÓ² \/fyTôÀG§ß¹Ìûº­ÅEír‹‹Zó¸Ãj)9žùÔ£3îüº÷uõÙwˆ³@ÎÓ€ÇGËKK¿ú‡_ÒQ¾Ò\ø(®_ l1h¨\ É · X”៚œ”\ßÁr±Ùìø0±ÙKBi±ä•[%çøY9z˜ŒëS1›]¤Z–¦t?-Eù°| SAF³| [ÇÙ¥Szœ¤¥¦HJJ²$%')}R¯Ô/E"Á’ä­çšÚ@´¤hð]_.•\S÷nÕy×?æìIft™bŒIJŠWõÞ˜m`ûÈÁý0ÑüÅb»ãš<ÔamÂ6Ðg|•«7:ã+·Rì5ýÐ!_±å rÅ :8‰s9¸·o¼v¬ÏEÊÕ“#ý–ñƒ% “örO]¾Hk8Ú3R“9¸Ù¡¥ø;î«Lõ9¦ƒÍ´N5 ô«—ú¤Š{ P…VC“¦å«_}&Ážiy †˜o9­Övøþ:KK)¥Å–X¾ÌÁ” »4¢ï%uÉ»pߤÝ×ÙÒü^§¯q:ã»ä]ï7­:ž(‹Žq\KpÅÅY­Ñ‰Ï~õûÏž)-/þè¦]Y¾|Çžø«§AsS7— TׇÞ9™¾”´€rÈÝn× Y”»@TT‰DƒR-Ó^$Å6@gG”Øàx±¤@±âe¨žt³;Â…«(ôä3`KÃ\J|,Ç´ &IZZФ,§,CŸÔ+õ‰nêað¯¶6@ßQû®–Ø¥Ìze˜`u©¤ÐÝk€þXg1k[Y”—^v—'&ìqµsûºÚ€S…mMÕ~rŒS§Šî5=hÌ6àœ=;67÷ÐÓ6»ý¥bqÇÍ.sÊÉü¸x[Gľq?pņpŒŽ¹vÏ15Bjmï2xîÓ÷øø™ Ï™-¾8cŒÄWDÖ¼û†‘pqÚ Ú¿Epk° ÃÞ·‡{†?Žô½GΨ&¶ÎHÁäÞ*;Ç=ËÒml&+9N½_‘Æ£m€rCj7|÷ZþަáÿȺ_8ëìò˜ô8Ê_^^9¾òDJNªsP TNë²§c—¶¹­Úþ¬Ï¸aO¶Øã‰7ŸÿÝ"”$°0GJ¹M9š¨ø¡$âÌ|N–!HÖÀÇ àš ŒÒ€h›ÄÚl’‚!PZw ÑDÕâ·ØÔõ¢&ÁÇÀ¯†ÚiE$HNOKSVe'ê5R­Éú!m±±Ç6`C`ý7é6w€³9[äòÙmJ ¥øŸÙ{–¤¶ Õâw]Ù\ÌHh9#FŒÏÉ9øwØšúé‚;A[z¶Üöïï9:ç¦èèÿÓÇõšÏáZø'EÁÐì€33¯(\|‰7?º¾†n\JÐ!‹GOØ£ƒp),.SÖåhšó!þ8Òo;—Œi°Íëýç¹`ݱ¼)ÑN5G=;YZçÁÈ£!i Üí…þ^c¬_ùßg¿e±X“yõŠó†Ý[¥sÞ… [Mè Ðr@gAœ:ÊÅr<£•,0¼#-ýýûÿëé½öû_ý ÏP†¶=c]n õéeÔ€8Î+Î$—wÔéWK·2aâ¨|—”ÈQëJ®k[`æŽB€L}EYéÛ Kòýg¾ üýNŸpÎܹÉc'Þ3âOèDÈk—FÍ4Ò¹·}§ŸÝ÷ÍïDýçÿÇ „4‚кl\1ü)ÏXü'ð£¸ö+hãàg[ \ÂI~Erš6i‹bÀš©¼Pë…bºS¸,‰.¶ºYpØ=>‰\èrÁó‘òa¬| ÿ[-± ¤]óU9´þ÷RRxбK^îÒjÌK\¢š†RMY‘ÔrF»ÓqÕñzê߇.]JÆdU{ˆŠ|.plσ³ÛËÞãšDÀÏÔØÇIwGö›8ø“vÊàˆRdòh ÜØ-ÅþÖ/ÿÏÿΰZ£~MÑsí¢þx,òÑ‹ë¯ÿÊ…홀.Ì#{´‘•{Oƒ|³òÿõºÜìÖ¢w°™t« 6Ä߃ڹÔ1á¡–zª%Û:Ÿ6@¹Î* Ë Ö™3ïKЉ{) î´$‡%×f– ôf9Ÿœê´´mÿ\—.½×;và"Q¿d´,1¨·h]Ò`€Û¤6ã=-Ée˜ÐGÿTú1Ó¢LŠDe –ëq¸‘:Q F·iQÖV8 K‚ãeev1\4%K²§ú[fhIc?—Kþ[vzµ‘Kûž—A×ÿc •¢±Û€ó™g¬‡ç~ôd±½ì(d%ŠË)|¾Þs˶+ê²Ú÷Õf·—”—ó[Q¥çÆvʶÌvÌ_·ö™8SXpFpfD“ 6Tñì²ò 6Cí6©ñh—”•£¿ã(ô(4ë(äRÙªCž•É @ ðÅŒj3 ûS«µ=}’»E€šóºŒz›¶{‹õ±“ÚŽ½ñÆÇŽýíÀs¸„Y»_„å%ó*–ÙmfàÇ“ @OX£Ÿ²ȘÀåÉ® t½ ´ ÌguéÆå¿M€Á™÷¶VÛÐZw¼¶)‹~Ž–Ò²2ÇJRÌÏd×Òﺪ­p§œÛó¼²,ëzä;AiŒ6;zØÜ¹sþŽwm´»<4’XäÅôè¸ïe­_Os¸_)/)Í»|¥°PÖ#O.õAÝÚ¨÷}gÎ œÉ7Á†üjÕuÂ3ØLÛxÒD&Ì£}9¿Ði·ÙèGV1@9¬ê(3ön£ñ±}*ìfâ^@:ó{}–©Çœô¬‡qf9«I}zb_í¤™~S6'Œ*5 ? t3 Õ”„ÿÚ/ÙÓ’Ü’€2õ¢êFOîÓÇ*5Øô·ZZHt hßòdÿš_ªÊË?1OÎïë*=F}CíS]×\‡º 7.¡¬¬ø‡»ó;è‘Vâ‹ewT”åÑn¶®U…òýÀX-ÅE…®—ZŠÀý!-®:®Î¥> kk‰kÃþ³&àŒV–ÇÚ_°™Ö©óh³.X'ˆ™€UO9„v³²!…6“z`PÖäÙ>5¿(mIg¤á€- kuÝŒY£V.øxRÔ~ÊÚ²ÜðLL FЀO€¬÷Õ‰ðºÐ¢õ¢÷›óZ?kKh}FUìEGäø®7U•žÜñ‚d¶ï#zݪöÃÙrF ›ZZVü7¼t=<Ú(’­?ï!Ö_Y6læï¾?qƒd\à8~pÿ¶ŽÝ{|aÉÇ Ì®rë•£"ô§õäRtPÆåžÏ—Ã'Ι`CКw°jk“6‰Vøx»&ôʥκÀï©åì‰cü†ëúªR7¡Ú1@9Tš­_º ('¦¤Ü€ 'y’ë—Œ¹ËSÐ#Íœ­;vš„ãŒÞW‚ESÆñ…3b4 hÐ’ÄM¢MBͽ œò )½zB._©êc/Ü1’Ó:KFûQa©ŸßvïžuKfÚOœvÇU2´X–[¢bë±aÃ*Ç«ïhÐÅ5Gí;7®=4úúéW6îÎMP®òf}ÖÄ¥% !™óq¨ÔV,—óŠ1ºT=ãæ~Ä‚yw0´Kèl&9Ž£m`¾©—:ê8Ùyiù'ì„Þ<ë,äj4@9ä*®S4CqúwWFÜkáÁDꤸš.&Ï2ô錉ëˆë8»™¤’ËÔw üùÂS100‚,–(z㋲îÝÛ¤àÒ5ÁoË'ȸ»?Çr· äà?‰·úö¸³_BâB³0œ£.Äÿ<€ÙïfoÞöÿwV;Ãï€ÉXÓ5ÏvìÐÁ…`ë¸ûà±3Ò«K%£ïtY•£¤&.õ„„ɪ ˆ4\ê ãRgì:tÂráô)]èz Û·Ûe¶úÈ·h‹ÕÒ>¹¨ÛF‚¤ä’"KTLt$G‡3e¶}£c(ÁˆÑ€Ñ€Ñ@C4›,#g½"kÞ¹EÊŠ.HyÉeÙüñ—eìì$&>½!Iû¼w|jjûo´kû“N11cYX¥Í÷mg|Ò7z¬YsÎç¾jë$A2GËVÏûxQ—=§½¹`]êÓÝb©—`™s(´2»ö —ºRHÅ?­—†p©“!uàÝEÞŠ> Pf±®t½U井ʡÓm]Sæ«Î%Êj±$ÇÙm•¯~]S2×WÓõ›ˆ$óWOëœ/œ££££zj >¥£Œ¸ù_²þƒÙâ°•HáåòåÓGeôíoÀM¸jhåzf¡nK,+Ëz¤më¿'EYiôP˜zD¬–'znÚªùòõ©@×´NjÌàT¥EEW ¶¬^þªuâÔ¯¿:w•ó+wL¬òMÖ °¥ñh¢P==Aë¨!<ÚÔý©ó—eÿ¶Í¿¹téÜUä¯Ý&E9Êh†×ÐÂ…~R•²>g#=’“zeç ™‹±(C FŒŒŒ‚¡´¶CdÈ´?ÉÖy_UÉåZ/;?ûŽ ¾áÁH^¥‘X^ÖÆ¢#öX,öR§¼p".î7ÖBùVK´ëAÝòйl]õùÖŒÖmæ`ûù@œÜ2ÞâiY&$Õ¡„Ü6\êЄ:Q àL}¸ÔiI&HÞ´÷°åäáC/¯œ¯&âëùE´( ¬4ݲþië&~ kYO†§Åo+u¬A²[ß8f,ÊaпÉÂhÀh ùk mÒ÷º˾•Ϫ‡=µïILé,=ÇþwP¾T{7ä~û[GެF¶ ~¾ÈŒÁ´Ô‚óYâ–~øî|!hT™uúbžóžéc­¾|–]`Ù¢1 —º (CgÊ%…fwz`\êôI¦»-ÉÇs¼²à­×èrÁ:aÝhÖª°}·ë´A¢Á[©Ù…?xZÏ\10‘ÐéWy:yî·!pÒ¢­kz¿¹¯» }D ó‚6î õ¨‡6þQ3{H‡Þ·5øÑ‹ã`IÉË8zþ/…ååÁ >¡²¶(  îù,Kç¼»àâÙ3gFN˜|ïï^[6°g'ç¨Ù–A=;ºy–Yǃz¢ŸáRwµÝö¹¦nüq©“'™pd·àÄ=‡Í–¿cÚ6.[¼uÁúàâiQÛ‰ÊÐ|„Iå¯k„¬™‡úÕK3y$óF‘¡ Œh…W޾({Ÿ’Kù…r¥°Hñª¶ˆñ›Š_q®î8=9Q2Ó’d@vGéÚ¡•{X:2j-4¥è?éçR|帛6n×’ÿ‘ø17”6./>>÷'N½‹R{èéÑO™>Ê´^Ò±Úí¢·}íÊ-¶m98nú“ívÛ€¹T€?gJBœ3=-ÉSQë¸ËC¼;‡Þû—6»M‚cOñÞç9† gDn,»=ÿØ¡ý‹×Ìÿtnaaþ\r #)²NX7¬£`×=’ô/(û×9S‹¢:w˜QÃÐdRºh™8‹:úUK† >m\Z¬B“€Ñ€ ðãOpl·;¤´´\–oÙ'«¶”+Å…") ñ’’/q,>’h^‡*l]\])(’g.!ªX‰|²r»¤¥$Ȥáýdʨ¾ DûÍA¡¢sX£j ÒÕyZ•9òH¬‘žÑÅÅ…å°.Šã‹zÖ­kï¾}’“3ãÒ¢£¢Ý“ Rˆu/:6[9âÄ”\).*¸pìàþ]û¶m>sâAòåŠm Zü5ã6Ã#(‡GÏä¢_È@®­vMÂÃ÷JÜø1rùáoU;çë@Ìb;˜[opkIL´¿üJÊ7ïǹóâİIH_Û„/Aîž%ùßyVl»÷+»é=X…0é 4U h€\¾òݹ'åýÏ6Ëeä>]ÛÉÌkJßnm%!Žd3-[*Ãø—>ß"+Й¸wÆXÁ0¾’nŽÚ 7m\tèiUÖßOÍ]–¸;·îÁÂA5Š¾ÇµgþûÓ@EWRY†©síNýkW Z’ ˜ÍšŒ¼×F"C|ɰ„Øò «NòÿKò¿ý#„=Y¯'6­'Z ~ý<@2݆B'–ä$IþÞSÕ¡m02©Ð±q¿†2M-WÚŠl³Ù¥¬¬L–mÞ'ó×ì–vY©rÏ´QÒ½c«–«OÎΰ>Õrøä™óùvyá½erÛäa2uô–›£uÙmY1FÝöŸ ÒÆùPy}¸i«5Þ'ˆãÇŽ@™ýèšAc£vÏ0 ʨƒhÝ$Ó‚:§~ ” Œ –‹*êžçYacQ»ÊÃ!æÓ_~NŠ^z]î¹M,­²¤|Õz)zåmôÛì’ò“ï -Â)¿ü_4=›\~è›bÁxâc÷Kì¸Ñâ„e¨ôãRüÞ'ª°‰O=,ŽCÇÄÒ¾µÄϘ"ů½'ñ·ÍPçÒ^øµ”.Y!Åï|$Iß~Lb†DSvHÙúÍRüò›n‹uÜÔ ÷­bÍL{ιúóçÄ™_ <žp/&x¤¦ŠmË)|þŸâ¼Ê÷£R,ðïsIÁO'©ýMå ³e4`4Ð(Ð ¹¿¥ÉK7î‘…ëöÊ`XGgO!žZRÀÏ”ˆ'gO’w—l–—nQ¼;SÇ4_°ìMwéäº ÓÆ±Ê5€#0óÜ&X#pSlX{ròÓ¢¬²^ã N)Z·(Ó­BwF– š¹ÏãÚ/Yß‹Cá”Ã§ë°ædmÛZ’žúŠ¿=G¹E$=ñ ”ï;(e+ÖIÙòÕ=°¯ýóM¸MÐWãHÀô7.üý bÉH“¤o=&¥«7ŠãôY±¦¥Iìýw‰3/OŠßø@ÊwîKë,I¸ó&)øÝ ⸘§·ýàa)·D,Ñ1’ôƒo‰}_®”.^.ÑýzKÒ=.¥Ÿ¯‘²­“èŽíH¶ff¨ãÅ(G9Ü@’žxHâfN‘’w>®¢+Ç™óRðË?‰µ}P,ÊUÒ6;FFu×Ý-’K¢wÇc²hý>’ï›9ºî‰µÐ;Ø™Púš/2gùVi›™*ƒzuVÌ ÍQ%ᢠ’îÈà<Á3-Éž Yû2ëÑJœ6R‹¨W½PÏž`™z&8æš Ï5Š%ù*1@Yk¢®ËÖn”’9ø†ÄM›$±#KÙÒUbË=ªŽØÚ»\/bFƒx«ØNœÁÂãq׎†Uy®ºÖ‚É8WyVœ˜Bqœ:ƒfîÛ®}jŸÿJ>œGÒD±¶m#ŽÃÇ%ù(ÇN/ÎÂB)üÍ_Ô=lùæiP/]½Aí—oÚ&±ÈÓ(«“æŸÑ€Ñ@Dh€ ™î´$_Á¨Ð'«vÂÝ"EY’#¢€M¬´ÀŸË»*o/Ú ½:·“„V{Œ€ŠJÚ¸€ P÷‹Ð4X¦U“ޏ‰àX/ÈÆGJ¨ƒP§­_ –5`Öûú¼ëêFúo€r#)>Ùkq^ÉKŠâû¿Ý»Ë%—RHzK¡ *( €(êOýÿ°À†•¦AHù¡øS°b‡ŸHQªJ¯IHè- „ @z»¶ûo“½\Bzî’»ËÌç³·»³3of¾³³ûÝwoÞ˜eUTwP`>ÏEqåÑ-ÈÏjy£ÙCÀ> “ òlA6ÉÅøÑ¼+é äiaìÝý˜¹E»4Ëcnï_¬Úÿ$‡»oééÐîã¬å6®ð×%‘:"nDÚd[ZyŸ¬E¦=y_zÆ~«C@&Ê´—7ÂWÞä¸êò7i<#ÊM wÓ&ÔÕ…Á€vÉø7êŸ[€Ì ª øb¬)¨ÇŒ>¬ä<ûšd—ì1ï}Sr!?Th»\)ˆE% \Ϫ³§ŽJÙë}ê¦Óú?ߺõ­iÅÅÛ6çæÞ¨·–!ÐÂ(Õ&—Ú%¢_äƒg.IÞ-ØÄ½ÆÝ„y Ù™|'öu•e Å+1XËm\`%“7"sd’\zÆH²ŒC]÷„§dlås›ÚËØT¥Xe¬‹€‘Ì&ðïSU÷.À¹»Iæú½  <'ãu“¼Y(Ú·E›àÖu®6Ëb6º;$¹Ñ=AÙ­£)¯>é(j˜}ÀùQÀûûJ¦4yP·/øÖþ81p„¤íæ½½0_'S>K8ëõ­ú»i¦¿Ôúï=ºîMèÙcéñ˜žãSz÷2¦ °4àLžC!`Ò&ãdßü?{é–è$/ÕÐfj yÄÈ+(‘p%­=áí¨Avç¤ñ•š¨/ÉĵOíí(PÉPÚËæl_7,̱³é›e;•–ª*y›Ð¢öXóüàýNÞsqBôaL= îq“Ágírð˜ÿ’ÚVu.RûçfœôçÞÿû \pâŸd¯\–›&j×mÍS€×·Ÿ€ë¸ÇðÛ›ýþƒ’'—ÇïU_ƒ×÷Ÿ‚ª_tËlLBÔÕ„8ñðo…Ÿƒ ?’Ú;êZJLä¯i1‘Ï¥ÝÕ®1²Y^†€#"PJ”8‰O/™]¤"Q¦ÅDÈO²½-þs–r±úUwJsÍógáHxÒJ†FôNäÈD™îÙm¯,5 ,Ì9 ä6NäY,örW±z::ÌôÂAz˜L&Lf¨ÕÍ9¶BËòg,¨p^¸ø;(úæ)N^,$Æ$m2çŠîØòÐÇw™F£ î£ yéD»ñi“/Žž’L(ÈíÜM+ô¡œÂÅßBáÒïww!·\vɯk€6ÞËü+—ÜÕUÈGåvU—¶ªøb•êz¾ \ò µuEƒmQ¤¯‚‡ñ÷a(!5:ò þ±¶¿$·¸9»þí·{7Vš†@ËE taò™ŒeôvA«î¹á$_K,&r)3>ùõ \¥’‡Vn08¦#Dw±(à§Ï_…Ÿ7ÅÃŒñ£A¥¼Ù´aÇÁ3Я{(´¢ÇAÓ‘V0¼‘[ e›ÇÉÑŽìÌmœ#wk[ 0¢\8Ž~I&ÈÚIöÊ8ñ¯¡á&’l.µ$BNÕ²«‹7ÏÞØãB'õµ‰iç&ù!ažÖ6(°»[O5¯ìƒZœÈå+¨ÅD;àƒøÚ‹¹ÅZ$Îÿˆ¼¸ÆIͯ Ù”ÑØº°ü {C@À¥ê 8†‰(kµ:(ÄÍ—¥¶d{W_и8Aüñsè2-º…YtÙëáÁÐi\@•$Ù’íh¨,Zæ;·°X™ðn Ar7è=8¹ë©¹'Wƽ-Dô³%4ŸµÑ`DÙ:‰UѲ\C°¯¦¥'£Ô¸ãfL‰Á¥…a¢( ÃÕ±nÿ=5f¥ª‘8@k´úbaqjLd<'Â…Š[ÝnÒq³tì!à€lzaÀiòz¡× àêjÙWˆ¯·ûyA`+O8tú"M¹$i•ç.ßìëÑÝÜBx÷©»%—jD¦/^ÍO7œ×zulË~ß m[ûÀÝp¾†Ì¬|øzÝniµÀ"´©^»ãLzl8ú+æ!Í0~GÆ…Å:èÓµDÊe“=ºÀ[·ã0Åe¹•8±n`¯¸=ºƒ$ó÷­ÉèçÙyE°ÿè9xjôhØx-´WMÕé &Ó ª‹#®Ö'höÓ>ê9(ÌI G’bSâÆ'‚:âbT,0šÇþ_§™Ám@ñ¤BÀ i ÖB  c“K©œðÄÄ#á‰I #ÖÁ[ÁsC€çf£ùE’yE¤Îž8Õf…c©1½#qž|¶d{ótì˜!àhȦd?K~”eÓ,k´óâµÒI]j'ZÛ$BúíÚ=àâì#néF4KøÏK´zxðŽhA’úÓ¦@&íð8ñDº©ZGR/¡E—B|@‡¦]Dnåìoÿ$ºÇ¼pdåIä[θÿØ98yî <<´ ë×Öï&’^êò2=ölÙN¥_…¡ý:ƒ¿·»œ­Ñ{ùƒ„ðnIÜÆù¶½ÕÔ䣽ٗãMçì€!Ð\XVÐ\­pŒråg7Ec‘V¡¤sæ‰ÁB}«U¨DÔ„‘ö¸r0á._àV®$_xÛʶ©¨má8ý¢À݇¾1nG‚PúöƨðAM´ØÃ¨ƒÙ)Ñ‘{x^üYpr]±gO&^f!ày+ÝðïôÈ Ðd3+´lóþãèQCPKìï…Ë=ï–ø±ý¤RIœ•Wãï¿ÂÛøIšäc©yP¯pø;þ$œ»|CÒòFv½:´‘\®™W9'¿H"¾ãPFÊèâDªåpòìeèÚü¼]¥ÍßÇޤdH¶Ó”F‹šß·Ÿ¸Ý¢¦!RÙÖôl)ż%h”©Ývì6Nê6öã¸0¢lƒ}+è™ÎF”-Ø7Î.¢>×àn@@móyÌö)mgGz ü|‰Á¯˜{pof¢!Þ‚J [8mÑǨeÞÈs°¬½›×ŸÜ¶mÕÏPl@}X†@s 7¤oèÒJîËñ}[Ohãï-5“Ê¥ÐI«ò K¤Ã _O9 ‚ü<ȯ¯—´ ð–Ì6h²á•¹ð¯;£MéäƒóWJÁe2h2·§«t™4Öi×!ýJB¢-‡k9¥e:'S 2•°t ÖÊX[Z¶­Ë“ÝÆíY1tE×%wqä6®ÿCk@åìeëÕgõsP,?ʨ&l–¨×j3ŠÜܸ• Ð÷oí˜EŽEj5W\Tx[(½‡Êöõnpè¶dúO˜VeùùÊðá®…72ïÃã±(t8¾Ý$M3¾äh:ý(££Òòs.§ôŽüFÉ;}ÙþÀ³ÏCÀn œL^-Ýáý»I6ÊUÉU™-ÀáSFh3‘¸¶CM3…ëÙ¸hGéœÜÈN!°÷p xºkP\Jœ+Ë$ 1…,4ÅF“Ò””pz›Pâ6 g˜dêQ9/Wå5£ªt ‰³¾ ©KSç)u÷5ì_õ0†´].u×gÌM]VC@B€Ù(ÛÖ ‘¸«ÏïÂ.Õ/ȶjg§µ! Ï gN%cÈðO¹lßàVµÞ¼¹0<1ù'ÜîqV8ÏsÀí@M3É—¾ðA§ ÚTÔ2oF?͉ƒ³T ¶g4Ò${hœa{âi¸žS»¥JÞ"º„JÒ";£YE$: ¢Tíb®u+pÓ¨a'.ÃMšèÉ)’-³\ò¸‘tê¤^È”l¢¯ÜÈ3Ù(ËiØÞ:xDB¯áŸ˜„g]ÚGþžd:g ¦D€å¦D»æ²LämÛ«ŽýãÁ!&ÂUsVvµ&G£N›{$~o ¦“WM2á]SÞº^ksàÀˆ„CKÂ&ß®PªÃi" Ú^–óca’æa‚(®HÍÏNCÂü™qÈ×Ùž!`ËØš†“´¹ÏÜw NÀ+€ùßo‚{Â}:C×ÐR¢L&C¤É}´â]uá.ôŒq4->ün#\º– æKqß3¨‡¤Ý^¶f7Lùl5,þm;ꪦ9T'½ññ¶†{ã[Tw ²Û89¹ó+Y+Ÿ²=C É`š­&ƒºN‘¶S"r723惂_¾àí m³Éb€…† @ø¥ùpNÜ„ùÉV˜6™,[eZy™‰ÅTÔ¿® gºC‡eÞ/½ÒDh‹žó!ÞO‰éõ(…$&¦b6‹‘¶òͲդ‰{s'>P¥PšÌVÕµ@_/xmìP(ÁIud+L«Ú™‡gî½ÅüT:ŽìØh“Cß®í¡wçv Çɉ•í]]Ôðä=$“Œô­qV›ÊxbTY„Uö2ÎVnGB%·qÙgàÂ1²v$Ê@˜«ÁÃŽšÀªê0²ít¢¬á$gرfÕÏ¢^wcs·hA¯`ß3 é&Âð´ÚœÝë×nFäÍ‚6™,˘7D|­yh_hBò2ÍP+Õí9ž›/ó«fÝÐHc¢^4œÆå³?mÝ·¯YÁì!à(8;ÝL’ëÓ6gÝV&ÉæùÉß2‘æÊDÜ< ;¶]‡Ì®à6®¯çæÀgFñ¥N²­W,“Ì0!Àˆ² ›8 '‘8]VVfþ©#‡æ]w÷€ =z¡c¡žn„ßÁ};—åÓtumÙ&“e«h”«ªf›ýû/†'$džùµãð_c|é‘Óaçò¨ªcã^´cÞt®Oä-ò5¶g0-Ùmœ›OG ~Ì@qÆØaŠò¿Z2@¬íVG ÑD_òù¸¹Y½¦-£Ùô‚ˆ\ÉÎ kw]:›òõ‰À6ÜÚÈþ"Ó,×í& œ/Â-õøáÕI»¶'aN2.¤üJ¾¤¹o2¢ŒeIÛ°Aè›ðÄC=9O«®¯4ùo¸Á(îÆ%³· ÿæAr>›Ü‹œ;ˆ\žMÖUŠ!Àpd·q|äP@( >»ïV'6ÏÃazÙvÒh¢Ì‰\nA¶ÛD»ªiމÀ‘O8"u…ëúîç ©§¿EÒ'~?`ˆ@6·,Tá³|Àá•zìðê~_¹‘pÄ4Ê´'\ _™ðn¶ŸôšeŒâ9eWü_w9’fª“Ðt¨(vâʧõ‰ºMŽ·±=Íœ2MX´±º±ê0„¹»à6Ü IÁó9Û?=Bÿå¨Až’[Nj*kŠ!Ðh¢ŒíIÇùü/Ì™SêÞÆhgÕ‘‰²¤Qƺ¹+ØøË÷¿&ïÙ1÷ª“sÞOýÃÊ>·ŠÇ‚ÚùfUĈáA¸>™NNùþÙ¼ôŸÕI& ó DzÍ\£Ü¬DYî»ÐÄÄ“‰ÉO:9ñ‘,‹¤™Ìo¤€ó§îŒÂöÔè^kÏÆÄt–ã›{/w÷Xôæ® +Ÿ!Àh+ÚC|ÎÝhº&=»U 0adþ§-£õ¬•Í…@£g‰)xÅ&ƒ`˜% £°?4WC¨\úZ&¢Dö´¤%6¬ˆßºe×ñûOÞ2bÔhchø0ôäàIÙk´ZÑ­¤ˆS •æ|c.´Ì7­`H‹‰àS“3hµùéÇïØ»iýÖââB™ç" ´‘‰áI¸¾MnveÖÚî=˜‚ žNïÓg¦Á¨›*rÜh»,Qlßh´a6ÌËDµ&¶¹—ȦñŽ$žWâø¯±Qì"C ‰¸† ×yAóâs Š!—¹Ð# ½X8™_bÇv‚ÀÙ¢®ù®Ùë—ö'P• qìsùÔ{ÞÞ³“&°jÚ&Ê‹gNN7mæE|0=ŠmgD¹ñ7})Ó_ð¤U&?¹“Hpaaž°å?¯Àó?;GÆt éЩ»‹ÆÍ×ÉÅÙS©T©ñíТȲÁ€ÔøÚÕÜâ‚‚¬ôÓ'Ož>’t±!³ Ò)&²L$™VÓ£c2» \›ÝìëPmhŸ†Ÿ=×·oœÑ¨{ûõ ¼)hŸ÷/€¶ð±´èÈUj—ÚîÝKmjò w.ÒøoòÂY- {ŽÂ6\T„‚ª½Ü4p[TèÓ­½WÕÏ–Ç%—nìÓå$Ê›÷‡ÁŒ(ß„ŽýD¼û…ðÛÓ¸VÞîð/ª5*˜§ÿ9_qrÔ$ãOöÓ VS{A ÑDYj¨È-B›Êù¦ÆÝ¶dÖ´öÒx®§¹VY&¿æZ{29ñn'± ’Æ÷Dªi£ ç)=sÌ_ƒaE‘_"ɤ1&ò(›Z&™H²Mk“±~7…2ÌO§õú â²[–‰àŽ~˜giuE/¤E÷z3ìà!úxj²@ãN¹o³IMV(+¨Å"àéæÏÜ; еì,¬Ýyºãªy.Õh„Ö»™g´Ø®jÒ†¿ü©rÖ·oüÑòî6*˜…¯7,Pžñ–aW“V„æðX„(«‚?Í0\œˆ +üw|ì’Kc'9ôP¿Ô´”—°éø• îØpò2ú|ÁõÌ¡çúF=Ñþ@ÒÞú‚@~’K]À‰ÒÄ=äIçQÆÈ¥3™&¹¾X:jzg$tyùô‡MÓ"Ñ<‚¼_œGm/y® @¶ÅÕr—xŠnÝÒ@îáÌÃ>`ùû€4Ë·Eu„Îí[K—Õ¨a¦0Í.‚ýˉ¶iÅŸ¼Âñ÷´b Ž+zÄ[ÆUÎçßÂ/œÿP+ñÃê‰õóøs#ßÞwÜV³–5'ÊTiú[66ö›N—ô—^Æ¿j_ñ{±PÆO»*rb2:²eÁB ¾&Iˆ÷ͪÓUÇ:À?M GM§c5®­‘œ‰vê ^Å…Gï>œr#Ó¯4»æ»ÿwÿçÿéÒ+ÝÀó&¼ª Üh¥Mc¡!µ2h ‘wO¢‰{Ì&¹:ÔZN<‘UysƉo—óŠ¡X«“´­ÖBÁˆóÈì¡D§‡C§/Ù,£r]Bx?ÉF™l“CÑÄâjÍÃîäTI#M&DÀeÒÒº¸º¨áïø0zPOpÕ8CZÆu‰¨›ç·ä1áXP\ cKÊn)²FM®ŸËwÆ÷ Í™"²üº;ÅÜÆµ”;À:í´ Q¦ª–½TàႦω1 Æ»ð¸nA¸¹ãÆ‚…0'‰æäÑBâ™;A ÇÅU÷K¿ÛŽ÷NOñ¿õÔÑ*ƒA‰ÿAr‘é©íB®gúlˆì{ò’§7Ùm×.ãÅDüÞJ§ÅD˜Ÿäš jY×d‚ŒÏ´æ¡•›ÒnÃÉsW%û_k¡AZàE¿ü Jž.üàþÁ‘Ù±MŠkè#ižWþ}P"Âwöé§ÐÃ…ћƆ½ÇФã2!'rüÄÈ~Ð>ÈÑþ‡ú>\¾IJîëåŽ$VÎmÙ=ሧÒDEkË–Ð2¤¹ô½íÅâÛÛ#”èÅÌm\Ëèwk¶ÒjDÙ¼Òe/Ûrc/ó‹ì˜!À°(KQÚ¥>}ÚâŸâßàËâNîS˜ïþèî¿{rÿzxbÒb‹È„9<2q#’¨@ÂÊ+pñWÚÓr„¦ 4QÎaÄ-ݶê¹q«ÌãȵÜcHxÉ–Z…u¦I€ƒc:JYˆï:” “Ÿ¼¼<4RšÖï‡ÇÓ%¢LèI—4Ù”ÜËY3$º€“UØÊ]"ã2æÖ,Óe²Í°þŸqÅÙ{ðù× ?>˜Û8Gìè&lÙ·²À`8Áññ“‡áËö5ÜdÃLµ( Ÿã2Ø?\>Üz3’ KÖœRH‹¬@²I›í‚•J%h’k¶³hãkËA­RVð”!×UÀ™ç9…Å’7‹” ™pá*Mþ«h#LÙÚ$™ð;•~º´ie˜ðf¡aŒ|%+O­PÂÜÒßH˜™Û¸†AÉr!l$²Û€!à  AÓ!½‰ÆËMN`ÑnïÑ‚™ûÓz÷îä MgͲ0²v³”$+ÁÉÉ 7´ñvµ‚ƒß·%K¶Ã.֪⺆Jøþ>p¾Z·½_¤ÁˆÝ$7sV-¸’p²¹^½==y(¡gX ôB8˘WJÎNëˆÀo•¤+TÊ{ñÙÇÜÆÕ3–¬jQ®ËpBO†û À7/¹K* ¸ºŸ êãq ì‡å(¶gÔ„™-(‘ÀAV£«45’eg5´qçà*ºM[ùWBMÙmò™a<{ß@x mÇ£‡ šÔ×Ôaå_‰ˆ_>ô÷F-áK8›ûŠnê:9Jyw¿¡?€úÀÇÑd•Ê4¹Ïä6ÎQšÈÚÑ0¢Ü ³"Í·aƒ6"1y<’å§L¦èJ}éýЦ£‹¹ê}l5wåYù6€lzADÙY­ggpÁÍßÃZkD8œr ~ܰßî4ËÍ.i’Üpq»]‚<Ðû†„+áKefza™žõ¶ñ7‘ãÞ’¥‘Û¸?çò±ò9Û3jC€åÚbׄ’åïp:V?ô\pFn¾8^MK=³îÚÀÌ Ûß„™S©TAvuÕ€n®®.ÐÆK ÎF‰,¶r+غÍòMkâÂç³[áHêEèà =Úû#Ž WÂW6½hâj9lqä6Ÿyåÿ¨ø>ºë° f ³(Œ([N&Œ!`û„'&ñR©{£ƒUrmÑï舼âÂ]ä-CŽc{†€9¥D™G7k¨QF“ 7WWpusww7é8Ér[²²óà‹U;àëµ»Ñ#ÆÉϲ¹œ–zL~’ Â…ðÉÎ+€AnЭŸ„#áI¸¾äY„ðfÁrÛ8Dt‹,‘ÜÆ­Ÿ§(Ÿ³=C :šÄ=\u…³x†C y([ûÁ´˜^àz5Ó©H–{–úý)ÑÑ£#l©îÉ–·òÅlš§‡l³T2 odŸLP½^F£ôAL®»•@f‘i®J1ˆï¹!ôpsÁ|-ï•CËRç¡/è‚­ä'Ù '?¶ñP@˜Ÿx{»ƒ‡nø±Ax®„/3»¨õþ/§’í±d\[r·e‰÷CúœœÝ˜ArÇqÍúyê~#ßÖ¦Ö–Ÿ]o¹´¼§VËíkÖr†ÀM„%z/­wÔ$:_âE'4Ãä8aÇÙÞ‘cC’×Ü”Á±#L/\£h,*Ñë霩õÌúܤUFB‡K›K$wR(%Ñ´ºMö+Ö軸@k„=‡vËEU„D±åÁI^ÞTØlg¼]”àíæ,™«&žH²—§§¤•'{oò&´Éf7\¥C\Q4 ²»K󫦱kYùxØ„ìÜ¿8*1j÷ãµÌÔŠuë7.ôp÷yY•Ó³s†!Àˆ2»-°„¤ïÓúD¥‹‚ð;’$ËÔ2¯J‰z¹¥.N"è™9¹…Œ(W16dB¬@t-å'¤5&»Z2PãD¿b\ŠY«Õ‚Ér+¡Tã,àMEï¯*¤:V”l6Až+/O¶Ý¨‰GlˆK¶Ýh¶Bšd"ÌåÚd6‰¯¦;!'¯PÔkµÙ5¥©í¹Û¸Pu¯Ñ`؆úh¼; †üUÇVtÞíácºÚò³ë-F”[^Ÿ³3nB ,>iGjß¾À û©L¾sþ timkõ´™úÈšPiå>"ƒh§,™[ ' òˆAieÏ-…(S•bSj¿MdYD˜¥c$Ð2v6Ó™6ZƒGS.rçSÎlÂ*Ê6™,—º\i`ÝÉm\ñííñF"ÊÜÆ¥|Û°»"Y6C€eëPÖ†€%ݶ­Wë»?-õôOøyd"Ùy>µw=3Ê´/–(ɦdÐmô6ìX³êç&¼ôïŸ7îó~÷éѼ“Š-^X]oÉÚe"~dF@ZSš¼G䘈±¼Qþ–D”MšeÔ06òä>9¾:‡šå¼ðÄCoZ [AÚ)zë²²2óO%'Îã¢ûÌY¾n—øÜ·3ŒZzL&€D )˜dù\ºÐ~ 9ȸÈçl_whìe\˃»·-/*Ê/ÀœÚ²M&ËÒ(SM*»Cêí‹æ6ŽÀag¶³À`0ªAɲ1<1ùI´¸$²,T¾®ãÞ“ÏlO/]ÒRÑK¸d熵».Mù:áÄYîËUÛEÒn±Pwˆ –jRKÍ JMÐÌ|3o·9i®;z-;%5s4öR^´k{"B>”i+ÁÆ( ÈFe”ä6N¡RÞ‹få’ŸfTSËnãœè: -F”[nß³–3ꄾäŰðãpÿ?9ƒ( 3Rb¢^‘ÏhOãÒËW½0 ×ÿôÝÏRO‹/lqî·ë²—d!À°4Æ>üf@c.õØáÕÿü¾r#–Vˆi”iOc“Æ(U³ ä6-ËÑm\©LT Ènã,"Ÿ ±OQ¶Ï~cµf4)¤Y ëð(j[h2pÒËÇègù©²SGÙÉDYÒ(c£èÅ\°ñ—ïMÞ³cî…Œëyÿù~#|úë_âþ£i@¾]Y`0%S4¶hŒ]¼|-ÿÀ?›—þ³Z"É4óq“Æ#îÍ5Ê#ÊÔ r"‡nãJš=±a>ÿ¾|Îö-f£Üòúœµ˜!Ð Èf9#&æbΰ 5-ƒðíDÿ(‰Ë]g;Ør×ôW.Ù) &í• 7EüÖ-»ŽØò–£F£›ªa8 ß“´íî.jÑËÓ•sVÑBÅ,0õA€–ЧU0i1$¥œÑhÈ?wòøŽ½›Öo-..”Éq.ʤ-7“46iŒZÄìåT•ÝÆáÄÔØ ó§G¼mü¹BBvÒ"`D¹Et3k$CÀ2%&¥ÆÄÜœa+þ9…/6…ÜO)ÑÑ·E<˜h™Rš]йV™þu#w .,̶üïçxþgçȘŽ!:uwѸù:¹8{*•*5Î^k‘d™>š¼u%m9A”Þ)ùNêKžo´ºÝE¯÷q6¼ä;¢X©º^¢TYjÖ 24ƒÁ—KÛ+U—y+V(²K”*"t,ԃѠÕçd¥Ÿ>yòô‘¤s˜•Ì*HkL¤˜È2ašSvLf²}2U«„ÊnãЇË7ëç)Ï3·qVÛ¦…2¢lÓÝÃ*ǰ=Âs/ßuWQ¡¸u@²¬AްîRŸ>ý‚ãã/Ø^T#s­²L~Í ´ödrâ!ÜN¢tIãŒ{"Õ²9›œ§A…Û[¦1>>·¸º„R½u¢˜óUÆ•yZAhÔÌÇû||uruù?‹tnë7W¯ý&Ÿ7Ç>ÐÉÉãþV>ÿòW*ÂÌËÏ2¯ÍÎúõ\‰î†y<;®™èÒx£Mž@YDˆeS ú8"Âlum2–!æ6NF‚íQf÷C€!Pow&]Kí9Š3À>4ÃðA²XbÔÿqmàÀA~»wÓ Í½¸I³E^èò9½ÄIÛå‚›3nD”éYJšg"Ê-Š$;£ûŠ~î®w¨ËÜ¡)*Y‹$ù:âÐàpwäínËn ˯g}…ç2±’/5É›È=êã=¸‡F3VÁF.Y]^|aá÷+®ÝØ#DZ}½Ç‘d2§µÉÒdZ<'²\T¶ÑØ£ë4­˜Û8«Cl0¢lÝÄ*ɰ=Â÷'Ÿ9Û'ê£QØŒµsBírϼ’Â_ÄØØÑ\ll“¼È¬ŒŠü§³ù1½¬éÅM$Y¹ªŠ(·Â<>Àoh Jå‡:0wý+ñ0jPåíñ ÷Ëž¤/ÄS_^¸4Wøk°ÌU¤,ÓP/¯¶÷ûx¾îÊ+º›Ë¹f0l^zõÚ’3ÅÅŽòahÞ¼¦8–?zä±%e2« ¾¦Q"Ë´§sŠ—í’å¼eÝ@nã6.TÝk4¶áSÀ –ÝÆ ïöð1ª Ž#ÊÞÁ¬y k"Ÿ´=­wÔ8\í;*5Ë#Ï®[3§[³Ü&”M/dz›¿Ì‰8Ó‹›4Éæ$Y¶e&‚L›Ã'''nˆ§ÇƒNeͽdÐ-¿j4¦7´á£}|ÇøÎà#m=DñÒwW2' Ì¬†Êlh¾ F5#¨õsA*Õs(ƒúZ Îï+Èÿ`ÎÅ t%ÆB# q%o4ÎÌÉ23"¢´§®5Ë8¹ûsžÝÆ +ñ4œthr÷$Ö‰G€eï`Ö<†€µKHZžÒ;²â* },OMë} ,þà:k—Ý„òé-“eÒjÑ \Ö"A–Ir‹²Qþ2$xD€RŽí'pò¿ÎÍû4Ùî^__÷Iþ‹HI;|$wKNÞØ­99)$¿)Ãçáá}»»¨ç)9è —+rœ!Ç`ø|zÆ•çæ6‹v[®‹íiLQÇ—L–eÂ,ŸË×KS7Ã/¹[?—Ÿ„wú*•ä6.mÄ$,80Œ(;p粦1š ð{ÆLO]÷{ ²¥»ðÍÇ |a@tï¶{69ɱb›é¥.kµˆ,“–‹ˆ±l—l®Inå^nn/È -…e¿eܸˆxÔ;ŒòôTÍný£‚ã:—eÖ]Ò뉽pá`½…5"ÃÔŽm<pñþÀ‰ƒ§¥û¸L–Qœ1ˆ>~êx#ij¬7# eÚË‘by“ãnÎÙ 1Ìm\3€nE2¢lÀªÀ°wÈ&ù€cuÚâD´UnÚOθ ãúµÝ»—ì )È/oz™S0'Èò¹tÁ‘ŽF÷ªÅ©Á”ä‰NÿÁãõõˆÐOñãj°$ “žãžqìä_tÞTáXt/4! "´–ËD?ÙyF¦}ªP~±ôP¢Üßòe¶· 4žä -ùÜæöÌmœÍu‰Õ+Ĉ²Õ!f0ZHˆ³Îõîý€QÔïA>gÜzètE ±õ/886ÿr·þÎ÷N¹\þËAIIåçu?Jí+ ⓸|” ÉéÔŽ‰É?Ô]BãR¢[ö%FÝg؉£é›½Z”Ž[£v_j»÷ХƕÀr;Ìmœ#õfÝÚ"ÛÓÕ-5KÅ`0j@ }BBÇñ/ËI,?öÊH@Xp$ÎFG÷G#Í!R›Ðv—wÉn³¾mĉ O I~ߔ㾠OLžc:·âzgáSb¢^)6ꎗ’d¹0Žÿ‰ÉcI–1a{sÈmœZ¡…qW¥x|AÔ­ßù¹§·y:vì0¢ìýÈZÁ°prßW¨$aRŒÂWgûô1ý-dz½ý" €Qš¸I-@ìa{’ÒëÛšs1½†¢ø¥œï™Íáî^MòïCZߨ^iëVïQX„å»Q°8_[ì­Rw‰8xèw¹^lϨ r§P)ïÅG27­Žùy¿[Ñ<á°à@0¢ì@ɚ°”.nðR6±Kô3uߨJÝX=‡@JïÞÝQÂ=$…È%йõ•˜Þ/ª«QäV¡VZr»ÆwØÃÙõ_ܶm4IÒjmæ]RczÍŒbÚÑ÷5ÄqÇJ~ j³_lµƒ¼v˜d±ƒƒ¹Ã¹¼è6Nšˆˆ·³Ém\‹Á %4´Il”_˜>'Æ(ïB@Û‰œ„W÷–.k#CÀÞ@­H>'J=§+xŦÅ3''6¤ ívíÊ>Û;òqÔþ2éƒüîÔ>½ž?ÄsCµ¡<œh˜‚}Šq)¬ M”–ñ®s é߃^¿'}z•fâ.©UêQÖ^Ñ1µOÔP­®ø ¤4èÎ[P´Ø’¸p$ûÜDòbÂC ^Hnãæñoã=5Ÿ22·qõ‚Ï.[(ÇÆ~ã|IéetÍýªA0´Á/.ÿØ"{²ÿ¢%)Y`0l@¬R ŽÛ·³ÆM›yÇí¢`Uð§±±OÓ Yu¡ ÉÛRb"â›ã-)“ Rn¹åψ={2ë,„%´)ÎÇÄ„ëÁø°\)õ²'¾2|¸kÁõÌ?N´“dpÏ+¸Qmöï/û÷A–l¹}FLŒoÿ#…'*Hå¸mœÂiBø§+ij†@=ù¶°},wÄ¿q”UÄØ ó§G¼mü¹ž¢XrDÀ*Dyü´™Ã3 —a{Cð/µÏMæ5üŸ‹'OζA X• J¼0gŽ·P$ŒBíÈ£øðŸãy"ŽëqKã¦o®”´ÆSµ“Ë{Z]ѨÀ ÿ%}¸’B² ýw™ØE›E@ÆwðÇW!ò¦ˆÄƒuþÇA|è!EZÚé_0§äR½\"ÿP胇$yVøÁÉ‚‰FúXó•Å£*<›ãùIdK/DZ=C ±0·qEÐvó[ÜFyüô¸×D‘[MÎÆeHo_7mÄ’™S`$ÙvoV3†@eh¼Ò¸¥ñKãÇtkß•ÓÖtN>”‘”LÓàÞÿ‡îÀFÊçlo?œ„„Ó¤•åy~v}jŸvöÌ"üX’l›)¾|^=xpS}dÔ5mzŸ>a©Ñ‘[piõå•Hò/¢³kgF’ëŠ$KWWÈmœÊËë!ü;Fyð^W‹`\½~žZZ¹²®rX:ÛCÀ¢DY"É‚øÞ(«@éË’YÓvØ^“Y ú @ã˜Sù  q®¼>ª/YOú‹ç¸ïLe ð±#Mâ2ű›G@_"¼‰•T—V”ÛŸTçç{ZLä[xï¼TÞHnVXb²ÉãEy|ãŽÄÁƒ•¸œúÛAÿ *KÃ6ÏÏ ?xèßÌôGF…í-sgiDmCžÅˆ2™[ˆhƒˆ¤•Kâ¦>²4vB‘m4‘Õ‚!Àh,4ži\Óø¦qNã½>2No"Ñ–L¯ÀtHf¤©>’XÚæ@½Eø ùŒéŸއYu­š?ü MxæÉéÑÛÏá‰IÓåsKíSûDöIËËIAœ‹å¹\,ˈócºúúwHHÚ`©²˜†@u0·qÕ!c¿ñ!Ê4q!XÆqâQPù=…'Ó”bû…†Õœ!À0G@×8¾1ŽþZ\V6îÍ“T{ÜæÀH[>(O ¾'‘¯òvdÃà ‹¯âCÝ•ªˆK‡Â’ɼ®Öp®oÔQ¾Ç¼ø$åÝæø´%ß™ƒ»¥ÆD~ Fq~„õ2UŠƒƒ Pô O<ôfëÍ› Mñì€!`e˜Û8+ÜÄâ-B”É»Úã„ [©W˜&¹‰{ÇhBh|ãCc"wÉ«M=ÊãŸ!É:CY8yëtÅïÕ#;KÚLÅ›(Ï+ø9òqMû ¢# Fq-Þ+¤H¡ªO9©]îç6lÐÖ”¯>×R££ïÉËÏ>Žd"òÒû IxšúL ëØ·}bâÁúÈci–B€ÜÆá]ÿ¶,ïÑ'ÖÏãÙ3OÄŽö!ÊäN™M²õ<«*C H6Ë8ÞiÜ×G—˜¨çy˜$çÁÇóÒ19‚ím¼¼œèÆ*G:íGÝ»²¶Š^ìÛ·•N+¬—'Ò¡:9S¡PÀÉYµå­ËuòÅŒ®ÅÉRëÄ·5˳‘WA7´^À­\i4‹g‡ &G€ÜÆá˜!`RÀgÞ tǼþÈ€ØÉ¾ÑD™ÁúIæ~´“6³j2D@ï8î¥ñ_Yè[y Ò­½eYÔz­h"ÎõÃ’6âˆjÔ¿.‡®>çr±±‚|^ÕþìàÁÎZ£n Ù¢Óu¼WÐó ŒnàÀÙªÒ×'‰—9N0êO XÎKDËy—žº/ùœÏö æF€ÜÆáý¹E®‡Â7ëç)Êçloû4š(ÓŠ{´˜ùI¶ýæ²2–@€Æ;û²7ë'’çfš2à1\„ÄßtÎl ´Ì+Ï 9 ”*…K’‡†F|_S‰Èós¾Ã½D ø’–p—úm\8Ó9­wäv\¹j)’ð²Uý$-÷7NjM\~ú§Æ•Àr3,sgyL›Zb£‰2V¸­¸Çü$7u×±ò͇€4ÞKWÚlWßZ÷ü;2ò!¡ráJŠÈí 6†¹ZCžkÒøcŸ‘9ƒ®¦jží5×\Ó‹êä7B“W×”§¶k¸P‰úD~ß(¡½ó­rz2QrpGøÁäg,eÒ!Ëf{†€%`nã,‰fÓËj4Q91·Œ¦¯:+‘!ÀhNhÜÓøoHÐV9®<Ÿ8=`Hî¼ÊãØQs#p¶0ûÿ˜†Jõà¸ë. 0ÙZVU·Ô˜¨çqéÞrbÍq‹PË»¨ª´uK‰”šz&5ȱ˜ÇIÊÇqzà¹Ù¼‡WÏö‰‡¶ÖUKÇhN˜Û¸æD¿qe7š(ã_kî¸4®,7C€!`oи§ñßz·OZ‹š§P^š(¦Ó—° . ÒJyÈ„B¸weñ<%&Éç•÷¥«-ŠŸšâ9nMØè1o˜Îëypvp¤WjtÔ vàÒ¥<;Ú·sÊ舄䩡۶•”dz#†€í#ÀÜÆÙ~UUÃF媄²8†C€!P8ñJÄm±œ}í²Hd0l`®wÔ}hBÑMª ÇåqnðYuÕ:×»w®º÷+’k¥Á~= ÅØÚ&ýU'(òÅ8éi~Dá·˜$4—R9üÞ1ƒ"ŽV——Å3lr‡O¿wäzâ¸anãd0ltψ²vŒ=UkÜ´™»ÆOŸõœ]ÕyjÜþ Óf>kOuv´º*]\¿ARU,µK„è´Þ½ú:Zíµ=èÖb²\w´^º-9G>7ß_êÓ§-ÚÓDn7ŠÇ žgEµftMÚgóüæÇ$+5º×ZAV¢ÉGkÓ5Ž[­v»†Lþ¬¡äÛ$‹0lïóq\™™2‰±Ìmœ tL5U`D¹`=~ZÜ–qSgŠÏ¿7뮪ò¿:kV]Ÿð^Ü誮×+Úd¡Ê§'ˆå/¶ÚRvýùé³D´é¯Þª²›·xüԸĪ®5*Ž#Eàë]çF•É2W@ Ý®]Ùhwñ‹)ˆð˜|Ìö͇@jŸ¨¡8¥$¾%œÒ飪jƒöÞ%‚n=¦•¼bàÎæA92bϞ̪ÒW'ÆÆò)1Q¯uÇQƒlölä28…âÁˆÄäûÛî=t©ºü,ž!`˜»ÃC\Θ¹³Õ~´Iâe«`Õµ^‚Qüâ­ùó¥å^ëš§.é.âÒ'ÆÎmS—´v•F„¦Ç½hWuf•µ N4¹C¢õ‘&‹fBŒšQL1e港Âöï¿j:/;cbTh?ü¾à»—EépžûCOVN[ÓyZߨ^iëVïQ I¥Zi4J&³o•ºKxüÁU5åg×öŠsg?=Ç^Jï+n yÈÏ-™eIÑÏOŸE »XR¦ÍÈâ`*”ç¼85Î|…-›©«ˆõh7úþí¨µ¼B%Ðßíéë~¿Ýz¥1ɵ!€¾ŠûaG ‘ÒqœsâæW•' ŒèËî¤køCÄöéÐø¤íU¥­*޼œ¤ÆôšƒJ…Y{-¥ã¸c %?½e¼Øjÿþ¼ªò²8†€£ ÀÜÆÙGO¢ŸL,‰Ï‰ç‘›…dùýñÓgÿ¼tæ”ýµÉ»DúksñÅ3_:øæ9¤àøi‹gNÙIyŸŸ6sœ > c­^¿eü´™z|5]Wpª ¢~΋š´4núfºNaÜ{q‚Þãynþ’™S(•ä 6ŠÜ"§ú¿ÏãÞ9!¥>ûvœÞ>Ëî‰þè±ü%8½+_—ób™û°Ì·pyX ‡÷0ý`üÊú—36só%§Æ:L›  U¨ÿþ"vò¹ò+7©”Ê©z½qˆ€&wÝssŠ›cÆÕµÞ³fB‘HÚªA¸a׈»UJõ«ƒö&°µõƒ\ Äb8ÿòƒ¡è¶Ê1;€Øì]7u>„†…º"@6§)1½Vbú‰”×~w[阅¦GŸ3SåRq|ÿ¶')]>—÷äÓÉíSò9ÚZNÃ%£ë¼Ø™vhuÅèÑÂq Éb´øÜ‹ Å\î@¢^Žd{†€£#@nã6.TÝk4¶áppÁÑ1¿ ï÷c+º ïöð±›ÞSŽŽ‡-¶i”-Ü+p.ÄoÞì'@¾Œ]Qêû³šrÆ/Y¢õ×vãåG‡?g½‰ƒEc„­ãß›5„²9Åi$b+$œø Ïñq"Ç |û ’´¶¸”®É?FxåtÅ—Ù£ríq‚ÎH$Ã!ƒ{†ž¦óç§Æ=ʉÆ<¢m!¼ŠÛ4Ôy@—0aúì”Æ,tüw#I^²S„¿ÁóŠ f×M‡¦ÍzµRo+\lm$™2ñÀáÒ¸¯!é…“ÇšUsP×z¿û‘—X,$à ¸?ÊŽÛxÙ ×íE 6ÂVêÒ”zÂÔ¸Ûp·íÉ\xŽ›‚2§#fÙ8‹©=#ÉåxÖçˆD”¥€¦zÃäcÙÓ}FÏYySà±MnG¢£{EñÜð‹Ró9aA庞ˆ‰|Ò ±”†6ü¸ý'ÙÍ­œ®ì\n3í¹Œ˜ß”˜ÈïD£°ŸH’ËÇmã”꞉‡â¸D‡$Évs`XúÞ¬pÈ]ÞÂöµö?ºKÔêø'qH‰¾¨F¸=-õÄ—VèK÷omò¢ÿ™FÙÂ#–C ãÒ ô/ÄÆ=g4ˆ»/éO¿‹E|P]1âÅkãP«©TñQŸÇNM.K÷Õ¸iqçhÓ ¨ï²™S¶›ŽšQA|E­tZ÷ߨw.–¦›¨ÝÜŠ¤|pY>x}áB—¬»XqO¾û™Ûç±/•ú¹¹!Hrÿyøá‡dC—£Ää×eqÓLä46vë÷†]»pæùÇø®ìoNþpOEò{÷LÛ$—WyOÞ/pÑY žûÅS«MgžOgй Fü'œÔ÷(ÚG~<>vÁ楱o]7O#×§Þ…úÂWðýÀqN]–Ì|ûL™ŒÕ¨íÎG,é£@ uéJŒCq—¬œötl,‡4)Ö,4PŽÛ—&B!ÞÇ®D ÈûAp|ü…ŠkîltS™^ŒO¼69F¥VØv¸HF º8“ìp›»’U•ÿc~^'g½N:•êÆ5wÏÏÇ ÿ—)©³VëõkQA7ì#)N§Te_óðè8îî·šUq€Ótóýs³]7e^éž™áÚ:/G(›ãùIa I_U‘Íž£¤{àÙ·c{ãú]rßãMîöܨ†ÖúŸ·—ñÙ~^07}³`&MΦg'ÝH¥7SC…Ûf¾õÿôRîÂåœsásCËšõøkÓQÿ÷•W´Uå¥8$²[ð ôþ×cú|ûFVAVÉ0¥N R΃þ9½M8~ÿᇞP ÆôÒK)?_w ~¼"ó+_$ÆÆ1 § ô™*.qƇ4`Óp“ÖéïêÈ/*hÉ›Ç}4‘µÍ/~1sj©\Î\Þµ„§T½€u>†bšeÿxUYêSo¬šqp[—Ä™H²$ÒIÁ¯ŒH”ËC]ûÅaA4ú^ÖÏz ?*>!¼Ê¥°£† @ZÄÔ˜ÈÝHÀФÍ‹DÃÜ-oˆ¬f̃ÃNÒ+£i×»ûKj•êe‘ç[ãQ£Õ îÚbÎÉ —îõf¬gÍES+0¸ô¾!Ù×o-=«ô+§1ê½C²o ªtõ¦S­B)ª5ÜÞ]`O‡®àVR3.j}ùÚ˜û·mL¿)ƒýFpãÇǺ}¸‰ à?d¥}ï¬/\µ…œ“QgÛ}o%Üu '¡Pé*–¨4<¯TÌ?uƃA\týÄùÏÖ®ý顉4[©M&¶Êþwâ ñïÇBsdÂl“ý/5Â&kæ•"“Ô(=¤jÀeCÜK:ž×Un®ÐãŠÍ%âä½y6ž{Íž68s5°Ïc'§ Ñ>Çqp©Ÿeq4/r«¥rDn-¾¡FR<’!˜.mqìär⋉ð)~óÄ…(Åá߆jóú"i­È.Í."9O×"Ùé‹Dò³Kõ: TNCm6·m½¿ ³Ô2Þ\?”XÇz;cþ›´ñ _½%ÐÀ4…úôš‰L Šf? ¾¯i‹ŒçÈnÚ$ŒÔ´SG¢l CLG¶}@^‚*ÜœãÝ×Ý<½~kU˜ï?vß6x(~'×-#$Ûv+š¨v„áA¸>Þ… ¥êç¦Ìx«@8žö¤{€Ú@mñÖfŒ>óܺ†ë}$Û[{¬V_‚0!l#¯’lgÛê§ÞžöJcÈï ýïÎ_ ¤ù nqYεU$ÉVÃÓž„áBø¸ó×ýéYIÏLl‡3n6ÝÿL£lå»mÙS7 qü ‰äl£VÜR¹8$[)HÒŠ¿ˆ›úmåkõ8ÿ åßvI?«?æñuâÄß)¯’S­Ñ‹º9—aV4žF; Sù(ÐVÈB‚ײ)½)¤8Ô𩦸ZÐÌác$oŒŸ6+§-œ0ýÃ=Kf¾{¨–l7]&»ß—¦øœNÔÔrf£Ü41¥«g½Ó1c°)sÙÁ 3ø!¡Ç‡r¹ÜúöÃÒ©S/£¸w'Æ~2[«Ïû Õa‹pâæÊØØ‡oúª\>;¿öîùÙùhËæŽÚýögûG¶Ý—|îæ”6C7)è¯~ôÕI¯:»¸Îèrù¢8âH¯22‹œšzªmöuxbï?Š =z‹'Û,xvr,÷ÕœX2¹Â¡„w}éÀº¿ÎqüüðœñöóñJœñÌBÍfÀýgV*¶‡ S½">|jÒTîÛù³>Æ\ž½Üú¿ê˜鼊Wr¬ÿkê}_e:ÜæºD‘\ò€xºÅá³Sñã¢ù䙊¾*m²ÿ™F¹¦µÐ5µFñ’=-|Tt†#¼N {ij\»š‹ãˆ˜Îhl[9ÚoA/=‘TŽAùÉŸÆM?KiJ]¼q)¢âqõ<àèo)8û¹FžˆŸ»†Çä8Ó^Cíñ…@ˆ¸É,Ô¦Ò’ý,Š R½m=æ +$p¥tu9ýlæ»ÇУćhFòµË•…¾cÞDRý&¯Reï‚Bý%<ªažÅ¤ôef Hž×`ü H^EÊåùMDOO£IÆr\"új¡WÁÉIàôäµâA´³{¼!ÚÑØØ§KÆÇÎ~DÔ hŸM~‘—ˬÏ>PÑqÖ%é‡P¹„“¹ƒrÞúÔ[©RÌ5èOæåjW½;ë\^7So0Þ"ŠÂøÑP,Ë”öuìÄêI2IÜWpÎ,1–tCwY“çë.Þê“d²“z!€g'Ð6ûƒ v¬Wæ¦MLrzvªCB:úø~䛟+i’›¶ŽQjà¹knB–«Û²#í¼aÃ䥇´Š¶x¬««ÚI½Ì»äº¤I¶åÊÚrÝP Ïe©½EÎÃëóž=oéuøðž\¬/¾¶lú(íµz™»âª¤I¶eŒmµn¨çòŒ~"~„ÏÒýçÏŸ¾QÖ÷6Õÿ6ÇÜmµC[¯e³¦}„oSe9Kß}7—wæzãsá¸Q{mš7¡ùÙm™¹tCŸ½“ïvÕx9-_$WjH½“ðÑâÃñJÉìB¾¦àk(Ïã?Ž}=Gާý²™SA‚÷jDÿO+r©Qw'Ò=„Rþe¾P‰yžº/r^™ˆíx Éø3uÉS9DÒEî9ÄŒL…P×z“ý6Úy߉õÓë…$­^w ½¿¢jz f!uí$Éè>V˜%ê' ô…7 ‚qºtÅÿÛF}ôÆÉw…Z³“ZáŒ)(t0ÛÖ}å’-¥n.ýGŽœ *ÃŽ%)˜&¹aE¸ ?vPA ý»…N,Ã×L›Ð0¹VÌ%ÝTWôg8ðÂvÓ$7mÂnÐÅm8NѺÇ[_BI4¶hŒÙê=PÞÿÀöTÿ¡`šä†õ?áÖÓyzÍTг¥¸àfsýßèío·D¨ùL{ŽÀŠ+>Õ\ø¢¡:dJä¸ qÏÇÎiσÇu“ä*ÒÔ7ê¥Ù³[ ',ž<¹¢½r}5qúºÖ}3ûº/ ½ÚªX[?ÐäÈlÕl?ƒžóäÁíŠd~Q›P¼né±O+¶I‹Q V¸ÌÎðÄC·Y 6 ÖU&É®X7ÏçÞy_xÎV4AÍRum©rVö¹ULóñË\6çýPÄ€lý+L¸µ!\¤{`Ü”gC .úÓä4ª›ÝVecø}ây×àkËæÄ’ M·Õ{ÀÔÿþNiþ4AÍnA·‘Šï)~B¼ZÒþÆ—ÎèU¢,Òÿ–zî3Ó ¹Q¨eÄXš@·´†zÕeµ»²Wyé³)Sè/» u­wu ˜TÕàÚú¡l¡‘«˜—6,„§‚3b95²EÓ z!ÊDÙùÖ»ïíÃ)•¾äŽ…Æ#€8ri¾›Øwå²ÿîE‰¶8©Kº¨Žø_¹€cÁ2 –ºóñÈ}6üº|J¥§­ÝúŸ\À±ÐxG.SáKÏÔ×îB‰4©ÏfúŸ™^4¾™†CÀ„¿ïŠ¡™ÏhÎ7úõó°€XKЉ2M8rñ n3#Dô“lÉ2Z¬,‘ðÔ¸»C胄𶵠ÝTGª+úI¶µúÙm}KÂÔË×w(6‚Ƙ-Þúý$Û-Þ¶Tq‘úžž©X/2¿°©þgDÙ–îV†@ F€‹Eí'ý£B0ä Ú0ƒƒž—&²ÊIŒ+î‰ÌO²ez‰p¤ Ñ-e»2œmñýTz`iÅ=æ'Ù2}ORKÄM••!x*Û©ÚÚ=`êZqùI¶LÿŽˆ'z?Q£Dò~aSýok7¡ePgR ûD€ƒLSÅEð2ÛÆ=/É\âj…JéË1“†‰ !€x¢Ã.Åζø~’F_`‹õ³PO4Äç£+°tc¶x˜úß™ËeýoÁÛñä虊"Õ¶Öÿ¬£-ØÑLC€!Ð88QÌ—% GwùØöDˆ¥¿]q/‘e¯Ð¨F”-Ø9„'úPwC‘òßî¶„¯é :ªtêÉ‚E LñK‰úŸÆ˜­Ýú_ÅéXÿ[°÷ Oz¦¢Hù#ÉfúŸe v4Å`4Ž+'Ê‚`KD™FÏKzxÓ†/r³%#1‚ ! š>HlñýTz”ÖÑB fb*" +yœÙÚ=Àú¿bgYøLê{ù#‰î›è›¨„…‘fâ {E@4#ÊœhKDYÖ&Ñ^z‰ã„C¦Q²Â}†‹Î˜cmK›êUVG+´ž‰,ÖƘ oAÅTÖÿÖ鑲gªü‘dÂÛ:¥Õ]*#ÊuÇŠ¥d0¬Œ.¨cÒ(ó"gKD™ZnþàfÏNëÞ 2ÖÖ-¥þÒmµ^õo‰mçq¦½-¹^¶T'G« =[eœm¢ÿÙÃÞÑn1Ö†€#€K‹›ˆ2š6Øš{8BÖ¦àvÜÕµUÝ&^ÕTÒ–ëVM•í.Zg¶XqÖÿÖí¹ïmg²a!À`Øød,ÄåÅ¥€¾âhb‡ û°QV´ UŸ(\ºAíæ­ ±•Ömø¦²hÕnà5ÔÁ“°ŠÏ¶¨üª„ñÎ. ”°{¬*lš:ÎÕ;üÛß "®ÛrñØ/`ÐXµ •tó'š­U¿mó™Ê4ÊõëE–š!Àh™XD»áõõ"pyêÿꆠJ ª¾Hp8 x~ö!¨ztE ˆ%Òz.XKV\{œÚç³þ'PvëTKâ:]¶îu*©öDª‹&¼ D¯LMXçÚKÂêÀ¶àÒ¡Ni«KÔþ¥øÐxP¸y§°ž^L¡qƒ¶Ï½ ‘?ì‚Èå;¡ã_‚kÇžÕU«¾ñ½¾…Õ¾Qõè8p2Üþä®ÄW¼Ôªí@P:‘†Ê;hìfðî.!`Ð5LPrv¸n{b' {þ$Ü9þ0D\Dš-…»%êÀˆ²%Pd2  !`®Qàdå²…d7Z =°Í·F ¬I€*ºhžþwMIj½¦Šêþ9”P0÷S(\üj•QOo¥À¹¹‚ûï€úÖ~,¡Âd¾Fʲxö²þ7¿O-^†$®W¿;,œSªÀ£W?¸²ê+¸øí(J=Þ`Yµeä]4àä©s^…c¯Þ¢ÑAÿ÷|mÙª¾^:I¶ÉÆYÕ•¨6¶IúŸW8IdSíÚºÚŠÔvÁ7äV\e]‡6½ '¶¿‡–lÖûú’l8{p1üóU4$¬y |CnƒnÖVÅê®›÷=7{°Þ'f³7U€!À°7Ý•Ñcž“ì­u«¯ª_ 8ß3 t‚óƒ÷ ID èþØ%ëÿU×Nà:ñYà=ÜÀë›E K< EŸ~жÁÿ("Úpé*~ö5NžA'J |`.ü4Ï”mÏÅsAû×(^±\_ŸªÈîiÖíO„â¯~6™c¨‡ÞÎß¼SÏIòļ x—ððÃÁÃPøé× æWü—sÓ œ"(˜ùðø|^Ý@hÁ©ÚŽŸúÌ pnž‘·@Áé#påË ðÌQ|ð9ðx‹‹ Õ{àÂWó 7q'´|´ÆkJoÈ;´Î/ Æ‚\ðìs;ø¹®ÿõ´yz’tMÐ8•‚üîzNN~BÚ¤‡Ûà@IDATû */_È?–W~ÿ ±\ ÎmÂ$­°kXÐárâ—~þ rv€sp(„L˜ šÐΠ½|Îù!æ9\¡çô72‘$¿bŠËÙ÷´yò Ó9;¨„ÇKÚå“;?€ðÞÁÙ-®¤ü §÷ÎE³ô¾w¹¤Mî;æg´˜ÒÃöoo^é ]n…Öá#ÀhÔBú¡¯‘œ~! î6dä]; .ž!HPÇ™}  ]äsÒµAþ—ޝ„´ÄÏ ûÐàÛfrf#džû Níže2Çîü/ëý2¨]ý%YIëǃ¾$(>¼ï+àäÒ ®§ï€cÛ&Kñæ-º~a'À…Ò]ÑuÐ]¥³§y»>fDÙF»ï…ésbŒFÃÝÀq!è ?«lÍ€"W±Zȹò9‘ËÀØttf¾iñÌɉS°3[B€@)ë=p¯·¥ºYº.¼³T1=fñ—?¢q$hž´Ûö€ñê50= ÊŽáP°p1Y¥ yõ9$¨E?eŽD²]_zr'N–®ñ~àû6ŽŸ‚â ƒq´»ãÁåÁQPðŸÅ ÜÈFD `ÞR|ñ×?ƒþL¸¾ø4¨GÜ%+ÖJåÊ?•kP0牤Ëql_=*$»~ÃDr» Ò¿ˆƒÀ‡ÇK$8õÃ× 'qøxrî‚[×AÉ¥³ òñƒvhJ‘ñÃ'P˜r ÚŽ› ~Cï‡+«¿…“ ¸÷ì.¡ k×F$¸;Aéê ‘àÚú_ ïÈ~©"ºWáÒŸ€îú~t"þk<¤Ìž(Ý S€Äüü²9 t÷€’‹g¥ ‰Ð'$ƒÓÀ¾7eY>Û×caœ_2KÊ ö „ ¿ˆÍI“îDƒt™—¡àÄAéº/’b"¸Ùûþ–Îs“vWÿ;%¢L¼Ú2ÿ²vo”®“Ý0…â‹©&­1iˆ)éÎ?šA yþáÖ©—d:‘2ëeÈKÞ+¥¡…»'¸uè© ÞCnÜØ¶:¼·œüƒ±n¥Δ¸ìÀgÐðêsœ~¯T£Yù:;/GàJÚF8—ü•ÜõÿÐdávÈ8¹ 5ºÇ¤¸Ü«øïMvŠtì‹“ò®žý ÏÓʶÔ.„44y  tr…íß?F]¡t^€:8ö³3J?’(òlò2¼¿xÐ ©Í¿~|Û–ˆrPçÀ ÍC3 úW ìù‹GþHzK 2àJ*>30d¦ÿTfD9e„÷ÁüxÿžÞý!’ökRGøaD¹ù{‘lpø§&M»«Ø¹x/pA]ÂÄ>Ý GD0§qV“ómŽ€ôQ„“˜Ž¤\‚øci­¦\œŘñÚøi3ŸY7}sÃE³œ–F@ÁE–Éß"ˆ²µ´ˆüRà\œ¥}åUÏn¥¤æÍrÛO± íBLDYŸ\9[…s2©p}ùYI{,deߪˆ¥Új…o+0¦œ“^®æ™TQÝAù<Å•GË3å1쨥•Àèó²s¾G"ƒF¾7IsïÙTÞ¾Ðyî÷å×*õ‘çšBj‘½ Å"ÐÂÉ`ÀûIÇ pò ²V²cöèÞ[ºçÚ¿kKäÞÍEª"Ên#¡Ý‹Ó!ý³’ ‰);¨¼ÌC¦x]ñ P(«žÇñJh…“ò¼[÷† Ž£My4>á¦ã¬Ëñ&’lŠ4; “ Ò{öÔ0;»šL(Èô£”œ—“dÊÚªím’öú–Gþ0IÂÅVLÇæ¤ÉNMø <üºBÌèï@ã Ƕ¾kžÄnQnÞ®#§xæÝ÷^S(TúyŠ ïBZÛ„{óBcÙÒñƒúu£;sþ ü²iëK™9ë‘,OB²ü‘eKcÒŠúQvË4¸oDYÔjë—X\ GþÔÙ`@ó‰ ‰Q'ë+\5¨ÇŒ>¬ä<ûšd—ì1ï}Ó5!?Th»\)í´p= ržy­ÒvÚXêãFÍX\úWàèK÷U[¬ ÓU{Mþÿì}|TUöÿyS2“žP’@€$ô’ Q6pmX£«âþmk]ËoqÕU±¯[Ä]W¬¨¸:¨ô"’Fo)Ô@hémÊ{ÿsîäN&„Ly3so>/¯ß{î÷¾yïûÎ;÷{ûCÌ´ BQ±c+D¥ß ]ïÅ^Œ,eì}Ê`”pëm?ßœ³ =ÌÀxÃÕ ‰êÄB1$C˜~ÍÄò¢À8-$£4‘Î’~³—-ÎG ã’ƒz M`0NAP¶m†­4¡Á3ï)Mâ5a “-¨žáŠ´`øåP–ùñ/S|mÓ·éóv]QøÅó´_÷ÌzþÇxÓ™´kò¾»øYí9_‚´Áa¨:a àYéÂ;€µª㌛µàÇ6“wšÔ-šKº0”Ä0ÅÜ8$ˆw´VÛbÚ›;¿=Û—$Ý…šÈŸ¾ó*¾¥õPUÓ5ШýcO¤ þo{ª{ÑsµzêÛŒãêYÂ*HYoSM žßÝ.1Ñ@$ÍÐ'IZÐF€©úÜyyuÄL=£¹"É£l1µÍ¿±±ê^8ZÖqïçïÍ} ó?‰S»Ûß ÷}VUáQn®Å]³ð¦¸pÃà‰—?„#ÊÆ`L² É®ÁºU¹Ò Ê­“‡k­²kí =‚'‰ßD«sÍAxÛÇïŒõI‚Æì€o÷³9Ó+v ÉT}"Èm%Éìü–†±¶Z¥bÎ#ÉtÛî"’Lù‹Ô$IMI2A m!ÉtnK$™å]^zI¦íD]I’© ‘#@1ÌŽ$™öR°©æ|"ÛøÌæ×š#É,oìDjª&~z>§í$1×Rj+In)O5ì¤À}­@Þ{z# À)084ôÁI]“ì¾h®$jj ^ûG<†Ú¨Ý_Zš+Klo>’¡·jÈe"’@@ <†€ ÊîƒÞîMž4ý–áXlg”€„Ì}ø·Xµê+ÇÜñèÓT'~-¢åŠd'ÊR@€Úˆ2¹Y'áÏY³¡Ë9Îj¢Þ.Õ ¯®6œÚnmDNÞþ4WSíïúÖpl{U´¿ ®ot*ÁÑ›lì}…$I ê$»§tQÊE ¶ 6ѨSŸð*_1ç Ì™ƒ÷#%œrÆŒÒuòäRç—ÒæÏ»a[¬–š:­î¼ím.AœuZ=ê%W9@¡&|í¶XkuFo_w°W,¶“&@±Z­M¨Õ‚³Ýj³`_oG•Å©õ˜J{[.¤—çqœQvÏeʉ²‹ Ôètñ¡…´}ERÔ4 "ZÓ'A”=Ð,ÇV®ŒÀ;"ýV0IeÒœ9ÔªMæÚº’JcÇo⪨ †U«Å¢ú!½d³õTµ>D´}Ú¸¥SªôÁŠÙd¢\U'jÿZ%\´¿[©V Ãþ£(è¬Â$ˆ²{…pæñÉF­^\OÜc€+J©©k9°Ÿ—I½v-µt\çV?Ç¡Âq08‰ôv¨­Äoã|ˆ\ºE1™ìaФ¨õaIG6ÕTW©6¤Z=½ÿŠÔ^G–E˜—šI=ЋjõARN8;ÚÛîü|Â’05ÕÔâÿñÝjš³ö7)Á’IÁ‘Ej7„#áI÷TÌŒ·½jî‚ ´»‰[•áLjÔ‘Ï •4!F½¾ÕDùȉ³ðà+óáÈIêêÙ„q¼ðõÊ­ðÇ7¿€ÇßüžÿÇÿ`ùæ-õß7ÀÃ8ìêÃtÿs}ÚwèÔÖµvd#fñnGŽ.9•ÙE6’­GB{º¤Ì”°$LämÃúóö§¹š³‹·±¥šlóZ[Gj{º§b%èK¢ªÚ_×_ZDˆi"/%#Ëè`m5Iv½y—V‚F#z^á7]o>~+ŒÚ­Í†³eÖÖ¬3Y`ûÃÐ5ºü¶«àÒ kÃÑ”¶úàÛÕPZÑ4Ì­u™á°É¼­x»µîDqT»´ŠÝ£Œà«(;Þ¸éóˆuç¶-yVS]Ùž¸xÚ'R; e‹ùìÚ%ßÑ›·#ÞíÌÙi§Ûm"e«ùl~‡>¢í/a‰Ã4—d®ÿe/fI¿1;ÞN*¢½ÙØíáíÔ2D´{QÅó G«¹®Œî©¸Êî¯8·ãí„"Ú•7‘\½Q®'ËŒŒ9¥ÔµYû`õo{ ¬²útôу¡g\gxïËUÛ9nº’6Nœ.…¿ý3ÜsýXèÞ¥#|³ê7ÈÙt:-LÖ®1÷åò-ŒÔž-­„ 9à¡[®€Ä®Qlÿ7yä ¾âà‡5YÍzpsü¤:uÌø|éf˜1ehµ¶÷³£'ÏÁŠÍ;`OA£nïÓ¼qà ðù²Íp´ø,DuÃóFBÖ‹Â8È‹}ã¤ËГ½JÊ« µo¸~B h5xÿ«Ÿ™-ï|¹t¸þÊÃ7ÚmmÕ‚í%F„^´ ,ç„:£lK ¨1V<ü&N#nXŽ®Ôöé{óÑÈNЭ„¾ŠÔ¿‚NÑÒÙ¢ã ð|ޱcÔí×ÀÙâS 4±ñŸŽ….U-"R[ „ÆK' óÄ<è·¥Ök Iû'=|Fß:é·µê~ÞKw8eN’Žì[YßöªkáQvýeÊ=“4gdÙ™åaÁ0}B{?|¹b3 ´ÄEÙŽ‡1/ò^Ý``bW@•,÷˜}ÿk³A~õ‘› ‚íóõý7\Îö¶tõðØm“!®s$,X±Å~y¯¿X¾ †L„ë/Oa„~û£Œ(Г7}B*d\7Æ~Nkh=˥уT¤KD€p#üöïÜ>÷ܹSô#çJÛ[þ%æçâÃí×Ùz`Çö¹%a]ü$Ñömž°# wnÛú¯Š ü”‰¿+œÔz 4nÿíÛç–ËQ[{ƒhÿ6´?áFøålÞð)ÝKëÛ^uí/\ mhÜ6žÂÉÍ–(ì!kïa@íQF©³MGܯgdí)„.H„Ÿ*Û§Žª9 ùÇNAæžCv;ŠÏÚ<Ê´!©[Þg½ûbÛXó¯?v3’ò xþ¾ë  í6̟»í*,/Öž-dî-d¶,YŸ +6í`aåÕµ,4‚Èqÿ„.°&s/é3ï4bJûddÿ“ÅÙ:ý#ù¶c§J¡CxÛÖ‰0O(ë†Þrâ/NKNm#§Yåãa|8u¤dIR¤†·"¾±s|ŠQ~]Ú‘JIò&ÒCœ~¤ùY“³q]Nd稠ÿàF(é;3%=~=©eÈ“L$yo—®Òñ¼6,_D?x•ð%œÕJ”í×Ùù_è™tôHW.?ò³¤Ã!†Eº8äI&’œ‘$>°wÁok~ÚŠgÑoJÍ×@£{@Cû¸à%Ùø¤“Dû_¬õÉ“L$ù˜y€”¿gÇ÷t­o{ç¶¿Â:翘=Û¯»Øb¿+pNŒòág0Æw§¤®Ù¶¾ÆØcÞUpä$øjů‡^ÖΑa,Æ—j£Ója|j_˜>1õ‚•#ï¯czðƉöŽÀ9œ ô6Úþ‡¡ûÐsÝ”(Sç½±ìÛ)¾xå–è½>£†ô‚)£ÿ°ã©a\‰1Òƒ’º±bÛeùG$ßä-vL”%ŠiÉç°½aµð剼 NKH¼éÓÊ…/øÖ—B‘$zÒ Æ „aõ÷ W`× |Ý™PåÊÝ9³ŒÈ4“(&™Â-È“|4ÿÀ'+¾úŒB.O•ð%œm?v\PQ:ïXöå' ¦Üz§{ßUbŒPF]§1Ë-·Å$S¸y’ ÷íþæçÿ}E!ô›Wû5ÐBû¸«\î¤ 6,ш˜åæÛŸb’)Ü‚<Éù»wü¸ú¼wÚÚÝíOÎ‘Ìæ­iÝÁ6Z‡S{Žršw’¼ÂV«íÙAdö\™MÙ!ã”),aóŽ<›õ—¡½ãYxÄÚÌ}0b`CxÄÐ>ñ°C (.8 C¨ãåGdúBɱ3_9vÜw¨úôÀëËÙ´ý ˜Ð›Kªމbž)Ž™:æQ˜O…Eg`ÛîBF”WoÛ IØQȶ!@ zìXH)!. BÐK¼lÓv¸iÒ0 b^ð‰q<›fçÔñ<Ôä5 6bœs[oö„–w8­íZ.Fì%0<‰²íâ•%Ù©D³? ’ý‡W_üןþÔÖŽ‚dœÝ›ˆËd#Å&‘죎nøg‹OžL=nÆ—#Ƈ'œ)Vú?"%ž.#öð÷D:É$GêÔqc»Ëwl^ÿ¯mk~ZØ–49zmƒº€»à5€Dÿëa®<1(mø÷ú]D|Å%ñÜ~)¾¢ ú’,é$“©[PÇ=«©¶"wÝ/ó³7®%O2}ññ†k åö6ü­¿ˆÒç)ÝtÛ¥hÝ~è’öïD:É$GêÔqÏj®­ÈÚ°jÁö-¬Sµ»ÓÛŸîõÖ*K4 Yn/ú‚(·ÁÖO„ËqjÝYMŽr EˆÅnÏÞ{ ‹ñ}qÞš×OaĘŸFŠ—aG»u¨Œ1|P"ߌŠÀ:˽‹Êä¡%2I^ãæˆ²ýD\™…K|¶t vγBOôOŸ˜Æ:ô9÷’a ß N|މ:Û}je•Õ¬ …ŽPG< !rLÄ:©[4ÌüÝx´q3<‡ ”ˆx¿l_$QL5yËIÕã«•¿Â[Ï`õ»Èi»uæsØ.]¾ß„Ô0@NëÜÐ ­F»Ò"[^‘«å«±Ÿ·£.8ròÔ –¤>ã †D‡á_ ª«SBj«%ƒÕB÷¿J4Ì7`Hƒ‰ Ë@‚TW~tÿžŸ6/_º¸ªªü ‚Q†Å~ž„+Å­¨1ìÍbé‚×þ{~ÛºoTúÕ×Z¯<Ò=>œÚÞh®V‚Ì•’A6û]ÛZ4Ô7bHƒ‰Pû[LuGöîZ½eÅÒUÕÕ•ÔÚß›®Vµÿ)}kÿ©J1â@£zÉäwíOÃ|Ó†4˜oûÃw¬ß²rÙššš*NŽ]Òþt¯Çç‰F‡÷~ºÛ“ÚÝp<ÿÒZ2à×_Os‘ÎC€¢8‘»¶#N1w<6û½!ý{õ{âÎ)¸ÚþTg27Š)vÌñ³%›à8JÃ=sñƒÆ‰1ht½` §@’rI‰¶ÉlÅrÛö®µeã¨Cà3W³¸cªÃ¼ÿ­eï¾vŒÝ>ú_ œv­Oäå¦w“¦a$-åðög+`OÞáÌßxùz<ŽÜô˜<ˆ"]gÿöóR“¿Ç7·iT”V’¦÷ÌʥϱNK÷?ÿÒQ ¤]¾ü|z;3%RL™Â.(\„~Û8QgDгƉöé{Ú£{ï¾}CB:ÃuZu å†?{)¼®¦;>ÅÑ/(²j´ÎsI’b±˜ëL5µe5Õ•gŽÜ¿k_nÖĆH1=(é!Id‰~oÜ£Lîw5e4áÕâ5Ð79µw|¯>ƒB:Ãu:½>›ÐÉÎJ!Öê8le=®kµÆ3&žpTUBµ£ÚºššòšªÊ³Gìß³GvHט7_½´ÔþZÉb4j«cð=ŠýîðCšR'ž¶(ôð™„m_‡m_VSYyîð}ûìÌ9„•£ß·ËÛÿç_^ްü÷Ë/tk/ mc9í-UœïT;ÞñŒi4¿GŠaËÎk~Ý-ý{{¾L/¾”<Öþt—e Þjg9PA”¢ ó p‹âsåp×5cÚ#“沚 Áøé“¨´±tÃvÕ¯"BQ;ŽE¹7‘ü…ÅúÚ*/I¤|àÔ§û{‘åØ#( óþsæüpÎ̶ ßh³Š”<ð˜|ˆ yKˆ(3¯2Îé>ËÃ3TE’Ñ.(¬5ýÜÇhdD9بMÕj?¬°ZyÝè¶&Ž ‘dúÄÃ=I¼#$‘ejš7ÚO8zKâmNöòºRÜr LŒ2L ÕÛ.§³uJ6’ä£*Žãâ‹×À%·ÿôqš©IqÊØN¬áp胒Ÿ3¥×w¨²íœq9¹½ýñÞ$›O¿/"GbuqwF%QvŠ*ÌãÎkF«ÐªÆ&M9hI P€=¾Ÿ"N'ÊsæÜS‹á"÷cYËÀ|z>~ɸ¿ŒÐ¼-‰?ˆ9.I"âG$ÙÞÑ—›eÕæÕ••gnêyR/I1hgÐ=QúþíDñj\nkâ˜r\8I¢6å$’È2½PÐ:m'"MăŸ‹‹ªO¼~n¿ :¦_„~„®þ*Ú_nZŒhVb¼9F¾x ðº]´ýuwóÈtë#"”ßñ+‡ 8üÝVͳ» ¬jj7g]Bi¼§K3Ÿe>VbNSéžóv'A”Û ¡È@ pxg¥OÔ,i@!âäô„})V=ðÂËO)²òÞP½íð,ÓÀ`Ò2=4‰üQ]ˆøso2e¢5|ÂEu$‹¡–¤ï;ëõ ‹Æ„†G¢ÜÞð „O„‹#Q"ŒˆÓœ&ÚG$Ù“G®Çû]mÔt Àð ɹ%'ÍD” G5%Þþ#_¼xÝhN×0Õ±Ñ= ¦C@ÐìÛÍoêa D¿Š3d-øIûèîà ;òÕDÕåáâÒö'O2‘d ¯»IÒHøÒ «œ¬ ÊÎBRä#´ üT†ae¶,,Šëâx?|éùw‘, ¸¼)YNõÆx¶G³L–Óƒ’>‘@G‚ÌI²ÓâS1§¦ýµUŸ ìø VBê Ó^öHlgÝûE§‹ÛQH}KÚ½ÄüAÉ–|ÝÛ¼ÈÍAÂëá–kà²Nº)Fír*·À·¥UæSÍæÁíþt \°ýo›hì0uTÝðÞ6’Ú_jpp,X<÷{㇣OÙ·“ÛÚŸÅ$c¸Â9ÀF’ŸיР¢ìL4E^@;@êZŸ4r= àœQ]ìíÿéóÆ˜°Àº%¸±?Õ’vš,Ò¼—v˜uèÐZõõÄëHs>9­ýI'Ù&§ÜN÷(&Ë™êLO2o A”9b.x /Û¨íÞ*iëe“\h…aÌ™óqŸãæããçºÇ0ã3¥J–xîåbERŠÕ:Eª óµ×ËádÙ¾M- ¿Êï0 |Ìä'kŽ:l‹^Œ€ Ê^ÜxÂt€/!€Lê¤Á ÅuáËbî^4ð ºÅðË>%eâ‘ÑC9³­‹ÿžB%°2Ê–>—æ¬uÉ < eˆ¥–XóND„R“¿ IòMü8|Y(&^5áñÒR¾M̽A”½¿ E >>pìDÕ”QöP«öü5÷"Ië¨xdËK#AóQ~^lδ(‰u ‡…½þ|³˜» •ov«1•mÄâÆñ"ñ‹ØßÓgYo™úh^ß&æ¾€ ʾю¢¯G@ Z;QÆËñ^_!ï®À|n>jJßÉ—ÅÜ3X,µwà•Ž1äÙ©‹NíðŒ%¢Ô¥o ²Xk·àKä€úö@Áé©©O˰Ð%‘Ï! ˆ²Ï5©¨@À;0+ùvË%¥2gޏ?ÙqïBHǨo±D6à ÆÅö=4,y”{-¥9"€*0öuщÏ…»V¼®› ™ÍðSK\}Ù&IÒ܆$ù-wÛ"ÊsâAä>¬EI@ tÙs=2et’3cÁò[8\ìr!1«VUa[YfÉ"Û´•ùº˜»ÒNFor*•ˆ±ã84ºvûJ%qH#Ù ÖèI§mØeøl †[|ÅsßD@eßlWQ+€w" ÀAn¸Æ ýù²˜»&šÊ7 Me÷·•(´“=ƒ»c©B#Ù ÿ[DÙËÛ;@ŽdŸ¬V|çõ“ɱބƒHÞzÎöòZ ò‹äëbî^ÎÓT®(½Á½ˆÒ„v²g¯E™£Éžm5”ΆYTƒ!†Ö!@„¦Ã8 Óî‚ãp®¬ J+«Ád2s='ÖU½u¹yñQøÝË ƒðà è ýzÆB|—Ž€^0œ4¤3Ê&/®¡_š.i  2ë<†=dQöàU@“òS’Q]A™SoFÎiä1‘Ü„Àµ“—»©tÿ.Ʀ‘ü—Ïðyk—CDH#ùz!ÿæ_׆ Ê^ÒÞDމ ¯Þ¶Öfï…²Šêý ¡F 1‚AïgM‰ä²Ê8v²*jjaé¦c’{Áø”¾`0èA«ÕØI³—4³ß›©U ×AV„^xøŠ MeÙÆŸÞm€i*ÇoÊ)ò°Y~S<Þ÷ïn¨¬ÐNnÀµK¤‘\]“ÿ#–Ò ÿ†É`L¸s‚s-ø*ÌÝÏØ• [ &QˆÁ®¼c°`ůPRQ }ºÇ@úÈзG4lÃÊ·"Ÿ=¤¦ÎûCÎþ£°tãØ”{~wE* Hˆ½¾0û,>T1)04ªm#Y#I觤§¤åË….©‡Ú˜4•óR“×ág¬ñØZK ëÔ÷W™ãWÅ’v²Õ\s-ÿD(´“ÝÓüõÉ4ê¡ý‹~]y;í=F_YÜc…(EMˆe5µF[È‹LñÆ?mÝÿúv ÑKúà ãàÞëFÁÐ>ÝI®Ç‹^Â…ð1âúm‚ŸÛ µµµ`6[Ž„§HêF ûÆ%ø¥d_½•†ÂââËÔm±_X7Ÿ×Rh*s$\?—-µ· íd×ãìXB³ɳåGIvDÊ¿–QVi{©#OòÏ[wÃ÷«³aPbü¿ÇCϸN*µXf>Ý4žáµlÓ.Y®«cd™ðdYíÔ’Š"­·ï×ÈöOŸömbÁ­Me·Âm/¬ÑÕB;ÙŽ‹«„F²«õþ|QVi©Ûyð(ü°6'u…ÛÓ/ƒ½V¥ÖªË,‰ð"ÜVmÝÛ÷†Z²¬.k…5MÐh• |¾/ ¢ÌÁðÐ\h*»øÌkcûbÈE•ŒÁáB;ÙÅM°|®v†ÐHv1È^œ½ Ê*l<"ɵµføzÕoÓ1 nšÄ´æUh©ºM"Ü¢;„Á’;¡¬¼êL&°X¬ÌS¯nËýÛ:EÖÙ=Êø`”2~¼èKááKBh*»¹dËïy‰0¶8m1ʉä–ÏÕ<Žßo¿ÀÌm~$8®èõc§vIÕ…þíÖi#¯~´ө…‰Ì¼A”UÖ|äM¶X,°!÷S·1Éík ÂTB²‡ªªêz¯²Ex•Û«ëÏ–€z³¤HÖi|YÌ=ƒëȤHŸ8”žá°,ˆ@V݆)HÜb)KÄýTJZ?ûoÁ‰ÅøuVõÉ_¡Ãä1 H#yÌä'kŽ:l‹DYE÷&? 啵LÉAEæy­)¤ˆQUkµ¶¶Lf³ð*«¼55І4LYBgÏõ|YÌ=‡i*£ÇKÇ0MeÏYãÃ%ËrFCí„vrÎY"d¥&>oí‰à ÉB)0ñ*1ˆs0öµ\QVQ‹Úˆ²ö±ÁDH'Y¤ö#@8Òà,ùH”)üÂ̈²U„_´Z—åÐ#1q 6Z9+@QóÒÒº¬0‘q« MeE’ÖÑÁx¯ÒškÃhUâ ‹ @Úɨúr ?Lh's$œ3gÉæ²M˜›½“0’dÒH¾eªHÄ9 û`.¢“ŒŠ•Â.H7ùlY%qÏUƒ‰?U ûz5«¹N§¡Á0nh/6 G»Ñ°`üoÞÑS8JL»órV„cŽ`H#ù‘úph4Ø:õÑp×"©iáBS^Êúä| Y§ y•wѲHE`>–>ÞfrÎ_µ-‹ÿÎ@€´“ñ5ÄHyáË}vê¢S;œ‘¯È€i$[j—ã7‘¸z|ñDzjê,ùm˜ž‘Í XB3Àxb³,+`A¢\VUÆ¥vµ 3&ƒ»¯ q#àÇõÛF¸ko:p¤–mV_?ˆ0æ» ëW‡e™ðIÅh4?pëðÒÞ¹‰os÷#pž¦òeCEGK'6ƒÐNv"˜Y5§‘|5‘d‘A@å‹ä®ÝvÁC/L& ô®wöwŽ …ÞñÑpí¸Á`FÙ´='Xuiù‡µ¹ðòGËà¥ÿ,ƒEH¢ÉSL)wÿ˜ûé*¶Lÿˆ\¿öÉ 8ˆ^äC'Îâà(9p¦¤Šmû~M;îÔ¹ ø×ÿÖÁÿ}°Þûê8|òÛNõ¥sŸ.…¿^›¿‚mwÅ?ÂÓl–™êu–$Ï=ÇÜå‰<Û@H‡Î‹1—J[NJÿ‚´!b”¾öÃÚ®ÎÓT¶(íÊPœlG@h'Û¡pêÂ…4’5íd ·øÊ©‰Ì|A”UÔ´<ô‚œ»µ§ð$+.5›)­Ü²rƒËSzÁøÔ^µï£½‡í«5[Q¶®Š-Ó?:¿¤¼šíȰ è×ÂBá–Ii0jH";î8² ¿ýûicP:œ ¢Â3 sç/Ú ÆHåâ0T4–t”‰$ÓDx‹¤^)ø†[¨Èš{ù²˜{FšÊ7+ééÏYãC% íd§7fsÉé³,k^˜ÈÐgDY%M˽›Œ#³®7ì³e¿Â‹ÿYDd‰¤vŠd…1NîÝÆbÜ2M4Â]æÞÃ5(<8ÂC‚ ãžI–û¨Fµ ò Ðã„ µ_w(:]%e d»[L¸#}8 Á2]™èõCf$™t”Y‡$æUve™"ïö! h¤ÿÚs”[Žh_ A ©¦rÁé“7xÄ*Th';·1›ÓH–ŒÆB#Ù¹XûCn‚(«¨•ídÙM6%÷îiH\µZ %ŲR)Ä‚ÈmL§p»qáíàáö¸`½ˆW6ÿØiFF¿ù) Þÿz-|¾l+Pçºçl‚”WßžîéøGø’ƒ\˜MîôÜ;b&–[@Rfî&ìÔt€ÎÀö 7ÕÕRÖzø\räyšÊŠ¿h/ÐB;¹½6œÏ4’ç¾ø5Þ/ã[ñ²Ž4’§>ZsŒos@kp} lk-Ç1ÜIÞ%űŽ|‡ŠÎ²8ä{® :­½ÂpãŠy:]R ÆÛ§Å;Ùh±È@Š'ΔñÃ.87ày”îǰ‹¸¨ˆFÇðºêµÚFÛ]¹Beòɕ刼‰€4iò_)GERÆ 7+’ MeÙÆ3$pÅ‘ÑCcã7åyÐ$ï.Úê(µ'´“ÛÚ˜¤‘\]“Oìãò¾cÂ]„ü[$bé’åK‚Ë7¾fì Øw¨v×w棑ìv¡–3yƒ p¢å¾=mçžq‘hlÛ{ΕWÁ–@‰Š a1Ë¥ÕÌ3Ó‚ ð˶½,Ü„1Îû7:G¬ZB@1}„ïgµìFä¥%néx±ÏõMeçaÌ´“®å9 ídŽÄ¥Í/¤‘Œïq›:Ûz«ÐH¾4,ÅÑD¹1[ãÞUOУKG“¼Õ-ˆÈ^?ntë ÿ]¼ >üaDE„µcmí:EÈ °bó.xëóŸ 3cŠMæ©BŒM‡WQÁâ«U™Ì}'Æ£‡úµOW üT‘m5$O⮆ú{ƒ I›7ŸÂÑù>ç¶J <É—ÅÜ£Ìç¥ãïùN¾,æ—†€ÕZ{~+ÚÉ—[££—¿0Øb­Ý‚×aÚA>A#=yõÓòc,T¨ÑÑbE piˆÐ‹KÃË¥G»+$€B ^¤q¨çŒÉ•·î˜:œÅ$“Mz]ãЈé’™¤}sÕb¨ÆÔуì¸ÐÀß<j0Ι”.(QǾYw^µ¨_LÉ`ÛN_m›ÚÁpá?"ègA’]´“³Öé5o[MÖß³OýŠrýÑ‘)Iݶdç9¹‘Ý% @šÊ•gŠÿާ„௪ß!ÔTîñ[ΖKÈBŠà}(ÄDaF"] ËÞÐMTdówxïXcÂ!Šîºz–õëKÉG+háQn±Å$7%ÉŠe&’Ü\"¹7|“o´›rIn´K¬ZD Ç¯Ù{ñ‚ZF!YÖ˜Lòã-ž vº¡©Ü~ˆ™v²¢ £œðn‰ží‚öçê?9F²"[—ã=‘dİŒ4’1ÜBdÿ¹ \^Óæ™ŽË‹Ö# å­†£•{ îZ=Á†ÂÄR3Måf€iåfE±ÜËE²·8mqѾ.æ-#°ô ͨcD{Ø‘×jôc„Fr˸‰½—Ž€ Ê—Ž™8C ð=²¶¯A·Û¯T4†ÐkÍu/xÀ Q¤BSÙŒK\$ídô€ÞÎOÓˆ° E‹s®‘ ²òÞØgKü·›4’'Ï2íjñd±S ÐQnhâ€@ÀC(Úçì%Kpï‘ÔTÛðöbÁMå¶£Y³a2=&'„8žJIë·¼í¹ùÇ™B#Ù?ÚYmµDYm-"ìšE );{5†¾¯f(ŠÎ Ö›=Xìp ¤©Œ=Œ`‰i*»¥`//Drìćª.Òœµ/¯’KÍßðÏðH¥6ÿ'¼Ðnl(ˆ4’'Ox¼´´a›X8A”‹§ÈM p1G¯²¢Ì8Nž+‡å›wÃÍ“R/hË–°17*ªj!¡kg˜8¬ÄGw€=…'ØÔ½»GÃú¬ ÕkaʈP[g‚5¸dÔÃå)½qÔ¾n,_³ÅŠ£êí€]ÇQcY £‡$áþ^l߾“°6çœ8S±#`bZH²^ÇÑø®ÆIÖd8äõàÄ8˜6>™i0[¬2|ûsj ̼aXìþ²u/;Îd¶À#Ű;¿®GòJÇ}µjlÛs˜‘Òð $Æ;íynÝ}ö:‰„|\9¼?,Û´ΖU‚,+ðýº\è ÷]? oˆ fç•”WÃ"²zÄ ž0‰uæ¾Ã°çÐ ¶-¡QïÄQo¾2 ¶8 »òޱ}+·ì†\\5$®ÆúõŒíĶ7gÛ)þù:cÈ 8ÉYª4þ zZ+Êfù*«¬£¦2¾ºÜ¬¤§Tf¢*ÌÚÉo†¦ÉH’ ä‹ã&Žp ‚(»×6åJ#ÙÑt)‰¼²b™÷vѺí<µ׉±l„Dôò8zŠ ã̾jx?¶ÄÀ¶}â°¾0()Æ í•5µpê\;tz ûõŒÎ‘Á×¢:„Âμ"ôÛl>vªt:›œ"lD™NœÔ.ÐÉu?èŠÄ8=È<Å2ºwé‚ ºSìGâN)çÀÔ«+óL“G{@B¶½9ØÎVü#x9ΗŠu+²‡¸î7– GùY^¤Ê3ù©©ñ|]ÌÝ‹@cMeˆÌ?}rº{-PiÊœ9t·ÚÉÍ4i$/}]óž£F2*%ï‚@ãH¡‘Ü hb³Ë¡.‡¸u8’66º½½­Iø©“vݸdx÷«Ÿ™7XK2öizgw<Ž÷´ã@`šx¢P JÁ¶Žb;±Ñ@1ô¶NáVY†4êðÉs°óâét©-,ñŽôáðÝšlxgÁOÜ«LŸ8I³–F˜§˜áp´¸„­–c\õ÷Øéððɳe•5d@¯· U5&èZo?÷b6ðã.4¯3Y°îq¡ãq¿Ðyb›ºH¼nÚò? s þ‚¬ï£Å"^Ù͆¿%%?%ùôïÏaÅ+rοbËâC 3óƒ)x­6ÖN^bsø;DõÉŸ#vù7tl¬ Ô‡O›ð¨ó÷ëÓõDÙ“è7Sv0Ô#§ÊšÙ{áÍÑC1¼!VlÙÅâëõòòR|ð}ÓÆ@¯nQ°y{üˆqƶñŒlyéê -Ï™{ˆù:͉`ëp98…q8î£ånÑ‘ðè-W ‰>Ê7·”‘Øa§ë=Ò´rC5¢;ÚˆóÆœƒ,¦yö]SX\ò¿þ·Ž¯Ói ã¥ÏÔ“p¶ÿ]Ì~Ü…æå•µ > \oÞ&Í™#ºlèÃV‹¼ _ýðMH¹.?mèŒÄÌœÞ\/oµ4•eü™Ú¿M:2zhlü¦œ"o­³íFíä»\B;™ãKÉå•ù?⺣üj$'Ü5áѼ:~œ˜ <€`ž@½…2ÉÃd`!5Ø©îRÒUÃû2Oñ^Œ#æ©´¢†-†cüqIYlÃXf–îÖüЋÎ`øFÎþ£:qŠy˜Ož-g1ÊÔñ.÷À1ô×ahFˆ C€ÞžßžÂ"æ‰Þ]PļÉDØ)‘9$È8t+‹•>|‚…›²}I]£`ÆNIJ¨êxH©9ØÎfþŽ•µµ`DNøò©™ÃÅf/C Ço9[ð"ú'7=Ëïçe»ÈøF1w BS¹y˜ëµ“¯ãGH½ï"–kQ½@IDATýô^`|Sd¼G¿7u¶õÖ©‚$‹ D¢¬‚Fà&p×=¦uNÂŽs—öI.Ã&£—‡cP¾}»Ç0¥‹·¿øþöÍŽqÈmM¤:‡áÿþq<û€<ÀDÄ)¤cõ¶}ðÊÇËà•–a…Fìi/&>¦#ü÷Ç@úÎT7"Ó”F¢œÈõÿÍ[«~Ý‹±Í6 Úwݸ!,Ÿÿü°ÞùògŒ…¶…{4gÓ\" ÏŽ!Œ§F²Lõ„¹¹sÄvïB 4$âlÕCÌjEé(ÕUÿÝ»jàSÖÎçµÁ{‘ÐT®Ãj®_<Œ´Š·ŸìÔE§ðÓž'ÒH6×ÕnÁûsBqQð¥÷É©³å?â=º îÿÆSÔÞ5ˆÐ ×àzɹrâ†7 è‚a$ÍFÞÛ¡}lÒlÊ”,š¦HPiâ‰ÂfNËbž z[sóýÔIŽK¿Ññäé}ý‘ø©ƒ!ŽëÁ¸ûš‘`Å„µ&3Úh KOÜ>‰I·‘w˜´)qÂÞ'> •2Ò˜ Æ€†K®GlGxúîɘ—……^°“êÿEbç¾ÿwÓxvŽ‚ñÔFƒÍCÝ’ Žç;.çì?‚ñÒDëAƒ²vdÙÍ1wr#)¹:/uè£Î+AäÔHS?$бØ~­©LÚɈßk'sd”7ñ%¢˜„F2ƒAüS7‚(»§}è¡mŸ,VKM­ÙLë½ "g4 ÄÑ/6 vàhu_,ßêÕžåFuñ y’ ¯Ø0&H¨°@ 4‚‘&Ä•ð%rÓÐ Œ»VP_ºÍãmåbKEö®@ !kû*E’Þ²ç­Ès .:ľ.\޾$+ø÷©½ ›¦²}ÕŸr²>˜ŒÄ0ŽêŒ¸œJIë·ÜŸêOu­×Hư ÅþÒŠ~”uAúð±S­9æoxˆúz‚(»¾½Î#ĦںÒÒ²ªó¶“–œ^¯gÄ.88õˆ‚ÞÑÁŒôýcáj§Å,»¾Úž)b’ßGu"ÉÑF+t0@0f‚XžD˜ _záheiy•bµX† ´fÇCIJ— ˜Ðë9¤%üSŒA¶( ŠRSq@‘Ü…i*sÏ>*’L:”šj“»q—*)_¾3Lñ?ídÒHVjóÂÞ8 Frâä ‹D0KjE@e´LmuÕ銚:©º¶±Žº(Ó`èQÆPà` †È ãP]ò œ<}>øn=|´hSĸTeTÕ-E¤B¸>Çp°–ÍUp¨BË0 a8ž„+ákS¾°}ý##©-¨MÌuubp·´šk ‘.4Iz@9. /˜”~ÕŠõÛ²øïšj*[ÁâwRqþ®,4’ÝñKe¸­.W—$òçŸó•#öíˆë™x i7Õ5¦pêÌGñ´ä­¨: ;„Ä® *N¿ a×ácÉp•Ð !HüÂp0ƒƒìš¿@MÃR—ã %•HrñÓ&èñµ¯¢ªÖ²€¬3ÀÚ¢Ó?º‡…B‘eÄ“p%|›†]P[`<«tòèáMˆßyÞ~Áԗꙸ5÷`^Jò#ØœÛê¥<—š¼9)+÷_ª§Êë2íO6âÏënœ½NËþ’üY;¹^#™”-ê‡ì¿,<™>[~f78)üåZõô^QvOÛñ⓼+ó×üᓦ”mÛ]†D¹ÑÃîUFB'ãfë¾·@B’ˆƒŒ¡0ºÿH0›$(Á!™Kj,Pg©ÒôCvOEÔT éÑ£<2tD‰å½!”~ëyÕr¼ÖŠ!>9ÿäU¦° R½hêM¦:a[ OVέ[úýn\åm¥¦ê [Ú€@RvîüüÔä«°mÉ»Œ}‡”òRRv%egó°Œ6ä*Ni-çi*§¦µµµç{ýq~ªÜT#:øùTswºóúKÚ+ ˆ²ë[“.š“ΛŽäíÿ ¿¸ñà‘“Ð+>¦‘6¯²öX'ÎmC îFB8nðfˆƒÊªj$}µÐ±®Ìf 'mE¢,³á«)#$òóÅz¡ DÊ„—VC±Ýè‰ÇÎzŒ€×·˜µAp Ÿï×ó¹7ùüN|Ô»òŽI§‹ŽcgÖ>ŽmÆÊÿ¼àŽQ÷W-ˆ?‹A8A²~úÊ©]6ä8Æ£{oUl9i*ã‹Ê·xOÊ 3e°ÐÜ/ˆòöëbû˜¬¿ÓN^ú†ö6ÔHþۙ˿•I’vZú,ËZÜ&’@ÀëDÙ=MFþ^N’-¸lÙ´lñÏñ‰½®\°â×Ðgî¹VÓTk¸¤¢Öo tè-%’œÚûØk<’ä*0`¸EmM-ÔÖe‹É2Ž–GdÙH2o2"ËŒ$ã(NAD™T-º¢çø Eù`ŽÀ§7ÂZ¸j€&$žrA Ø8 ·\²î‡ÿ-th'?ôÑsd}kNdíHjêt3X¶áP$~/èVU­|£Œ¥´v-ýEr!¤©lµÖwhSà%=ýÒòå;h¸°|Oem‘-÷ò²ñ¾¼dØß×N&dPä7ðwÆ<ø4’Ó'Ï2íâXˆ¹@ÀÛDÙõ-†÷ ö)Ÿˆ2=”qT&0UWWÖdmXû…f•~ºx£rß —Û\¤¸S–ͰxÓ£è)®Å!—b#û¤‘³Ñ{,3à À@F’ë°›Éd‹=ÊV›W™e_v*×;“Ù€!D”iXjqÂ*H'™Èò­ÝŒ°¿Î ëã[«‡9LÒ5:ÙaF¤û¢Ó¥°'ë·wJKÏPÇ/j#j+Þn¸(’·#Ÿ••_8,å6ü,ņÕàåø‚ŠÒ7±^ôöº©Ý~ÒTÎOK.À_TbyèLñ4´™¾Þøl"íä¬Ìúv2i$/›û—wP#ùQv礖%d£1}²óÙëÜ_*&ˆ²{Zš¼“œ$“'…M¹›×ïŒìµׯƒï@¹ëÚ1y–×å¼eìfõtØ)mÚåïCP`˜uf&mFdB.L&ô‘1o²•y”ñ&Å<Êœ,»§jž)…¼Él»1Å“Üy•I#™<ËDšÿr•nþ®ÎÔœ­xam5ücj3˜<ÉD’3÷JGó÷ºyÕÒ_qµ ½ÈP[ 2‚àK©ç¶ìð<þHþJõÂßÉcÆd&lÛþ¹/ÕSmuÁß©’Ÿ’ü)ÞæmV«5g>M”/¨¼¤˜ªïsiÍÇ=ŒËç¾øVìF^9¼=¯ Ô‡O›ð¨㘈¹÷" ˆ²{ÚŽHy)‰„!e¤nl \óã·¿à„ÜœWŸ8[¢\1<@³uÏ<\µ¥ )ÏBT‡>Œ³‘ûˆ ¢¼#Èj6‘™B/(ù Q¦º’G™<ÌD”ÙT¯BË!H _½R÷/®¢Cau¡î1ArÈ9nAžäÃö.XµðËïp7µ µ µµ• Ê‚¯¥Ä¬ÜWóS†¤âÏåwT7E–>D}ß==²²²}­®jªi*Ë&ø3⎼Yº’4•ój²Ñ™¶Xåî†ü|W;™4’ËOþë:–×Û÷k0&Ü=áÑÙn»g¿´®BI-]'éMå9›×œ½aíf̆B.¨M¨m¨xè.Šäk„†Ef”W”öÅÌü­â«æw8IZlVÖ_««ZêCšÊ(Í·1˜k­ÓT~]-ö9ÓÒN¶˜k®çyJù¾ìKózäåø!³?¯ÞßMŸe}ï¿¶‡ß!æ/F@e÷4'ÊÜ£LÄŒH2õ ¦6Ðìܺy×°‘¿õÖë­cp °6gPrv}¥D„KF½ÞÆ i§CjJŠ›®;ês‹D†SÓuÚGC…Ÿ+«Q‚õ£¤*)X2Ë)[X\ðþ/aœø9<¤'jš=ÊâF€øbŠZ»¶òèÈ”i¦:;÷)8u¯ë…ãÇOê¹v-]"¹ù˜íxÊÚ—5•­–ê[±Šä¡¯]Ù©‹Ní e_J-j$?Ýø¾ìKõuñOQv_»Ó§|Š}%×&y/IÏ»êaÇ"LÃ/×tOìgEË”ve†ýræ\¡r6ªÓé‰X«*EÔV÷@ƒè®¨”ƒá\uÉbÁ±öjjʃtg•ª~·’T“d »îé~Õ_½° ×Ëp*ljڄÚFÄ'#¾žºmÉÎÃf·K2,Æ7" ¾`Ž–+J¿ÄNX7Js戰\~£©¬HxK´!(Ió]¥G³É…_îQvèŽ^e"ÇD’Ù«wh¨VsÕtù\g¤¹ä4ìýú£â¥ßÐ:Mª|EŸ×…:•>8~â´Sm‰0'ÒÃ^Râî[eìž<‘Œ K¾úÎêí«—•ï]SŠ«äU¦° Ÿ\ÿ”Ã-"ù,‰™¹ËòR‡>ŽÊïQ%‘,O/XòÃßpñaŸ­´+æšÊõÚÉà füÂ…÷íBîô¢›j$ã“ ïŸÚiSg[Ö9½0‘¡@@%0b¦[üÁŒ¦^eòd–Üû¤2ÍÝìW»hôo\¤}DàCÈ멚)ã¥ù¤&»ÐJÁñ#,ËN|þ§Os­ë¹V5ã/8¼±ð&#~›’²rþ†dßà(²òP~êÐgøº˜;ÒT¶çhÓTVÝ—2»}mX0[­÷ðÓðÅkIÚbßÑN^6Wó”¤ÈŸcýø@"Çt’~¬ ɼÅÅÜWDÙý-Kd™:ŒQ,dåëb£»(wq3 JØ-Àõ³8Q mIýDžO¾¬Šy*KðIe¶9bE–gͥǗo_õ .³¤ Ÿ’ô?Çj jj‘ü žÛržFï_ƒçO‘ÿвqô…G$'#@šÊè…, lñ³ ×Tvr)žÉŽ´“ARìׯGÂ.H#yéëš÷ø¿}Æm_7I#9Ð8R $â™kM”ê^Dè…{ñ¦Òx8€9))Ié30ÿÜ@ñÊ`1öÏÿ®ûF—…wôãqÌ<ü‚Ïé¦(UçÔ20›à8“‚…' «¨+þú…#a¯øR2†ÜF¶k¢_í3{ã¢ýsÇP|2?—v‰ä' IV”›nÊ((8D`"^è8“þ›?lèÉÄm9?û n©&aí«šÊÙÙó®Âë'Ž€ÄzžJIë·¼\;™i$¿ñ"y‘Ç/ì ¸V1íÊ™%Ô¿C$€Ï#Јéø|mÕSA"dÖ{Ÿ*Ì@åÌ, L…ûuâH{äýt$É<–™²jH2ÙÜõœÒI‡eO/¾|"¢ìH–ÍU¿ýø§Ë︃R£qêQá/à1³=m´(ßsH šÎ>½Ô\·U0ãu¡Ç0Œï ‡%ë¹-7×s–ù^ɾª©Œ:ö ­åýÚÉvdå|ä+g 䆶K¾Ž€ Êjá·ÿÖ¡ÆZñ/^±JoÏß¼שMˆó‰È1‘QQŽD=g‡D_-‰H2% ¥`/%8·æ¢Å¯—õ¾|Æ,„õS:ùcÿ·w²ç‰»mëâ¿?"ÐqëÖò£#‡LÅ‘á·àUÓ §Pì3°¬pDò(ÒöGL\Q禚ÊõN,g®+ÊrWž™7%„+ÕU>£,4’Ýuåˆr¼A”=ÔJ5ÖÊW±èNT<²ßCcÿpŒ>ÿS¬,cšˆó ÕåQÁe‡DæÔ’8Qæ^ešiæ“rà©äÏú¼±ó÷è=¼?—ê-²õŸ¸ÿrµT@ØáºmÙ~üðð¡S,fy#^4‘—ÙÅj†_Ž6>nÛ¶£ž±Ê'KµÏj&ÃÝ8÷j¢ 5•3°>¡,4’ÙU)þ ìÉÍüuž~8†êÝÏ‹Õh5>qó1’'#"g§Åe"ÎÔÑŒöñ9-«b2`°ŸÔbS¬3Â&ŠQ&l¹—t’æÿaèÅ„üÎãz¿¹“<["ù9Ý·æì‘´šixmÐõC×FBÕ¼½Í,þÔÏáqJõIS3"uLJÿ‚´!¤qi'×›ïÅøê5’7 ó –jƒ¿:¤¹5}¶üŽ÷6ް\ Ð>Qn~—|6õ VÀòO¼±0 ¼-zæ>ëâ dÔÔ ÊCT3×¢ñ|BûUcW½-Ü{l'ÆM1ÞýÔ€=¸Íá ¼™:/?¼éqbÝÿHØ–³^‘47`Íé% “’d2ÁêC©©]lëâ{ MeìðFd™%©/£~Ñëf¤Œ_¦†“áX'|!÷Níäzäåøl c`ÓHž3¦Ô[-é µæ<ÚÛê¢F{k*+·*éé^©©ì ÚÉB#Y¿a“ZDÙ-1o^lŽö2/sÿtOí!¾.æîG`Ǭ˜*ôícÇ>[BGÿc}ßÙÛƒ¯‹¹#˜½D«‘nÆ—)¢ƒd¹¯l®[}bìÐÎþLûkßTS¹°¸èúöçêÞ¼];™¾p"Iþ›ÐHvïu#Jó.Qvc{Óœœ…ZûUÔAޱæF3DQM8ðä ¯‘,ÿÊ6+ŠA¶Z¨£¥H†@ÏÌÜ%æV$ËãŽIé_]-ÿR”šÊ:ãÚ¶‰ÿ—Ši*ãß§ü<|IÍàËÞ2¿ v²—_¯‘ü ’äG¸ÉM·6 …a ªQ¬?9²ƒßƒÓHS;kP Òô¹ÊÛbÀQ;™Béê“÷h'“FrÍ©Ã?áuì0ˆôu÷ý'‹Dx{й@À†€ Ênº¬–Ú—ñ¦ÌŠ“ ÷™û_°{RÜd‚(¦ö=>x ’ {§Y±¾ÕÂáb—"½ýô*ßi'Ë  1™j~É5*ÊápJ•ISY‘¤u”:´‰i*;%oWgBÚÉHñ§ÙËÑ*óíË*^ äòŠòMøv2Æn¦$½“>Ë:cÀÍ»Y<¾}»X˜V¯€Áż6/`öж{4’öIIšCj "© Eÿ !¦r€íQ}ßÚe÷¶¨ÉLa‹çHÌÌY€Dùô‚²ß/^'ÉRmõF”¤‡ç¬òú’çÛk`ÓT¶¯ªyA©­¼é½];9íÇâj¶—l[ñ–~ˆ¹®v .ö£u µP4’ôÄÕ³å'ðº¶yöi‡H€áQ¶Cẫd~sgXãiÉŸî·¬v]i"ç¶"°VßB|T¼ÏÏ—ùÅ9Š"~#1g$dæ|†1Ëžå^VlÊKK( ºt¼USã«3ìµõíäå¯ë®ÀþëúÉd{Š@óÐ<6NÙ3÷C}2zŸØç9z{W$ýsNÉXdâtZÍ«H,s|ýæî›]RÈÔ«`dY’nÀß´mPPb%Ù¼þÐeCGzuÅ<`¼7j*3ídEaýð…ɬÓ}éèZ]$i$Ë`]†$Yh$·5q @À†€ Ê.¾,`yoNÈ•Yúßs÷›v¸¸H‘};ØýÄ€s )ïñ,dIù³ð*s4ÄÜ$ˋг<¿_—Óvüniµ*?K™âxœX¾8Þ¦©l–­¼V~³¤ß÷ÇÏòuµÍ—¿®™%)òçhWÙ†£c:I?vêlË:µÙ*ì¨A”]Ø*¯þ[ŸŠ$ùºú"d è縰8‘µ“Ô†RG¾2Ê‚}¼µs†“²Ùø4‚ŸV—#ù8EUÃë%È*Ë‹òÓ†ŠkæÚÚ›4•™v2(wòêaŒï|¾¬¦9×H–A™kwÖH° #'Ï2íR“­Â€šDÙ…­#Ë–yöøyî›g0íæëb®^rïY Éahkø¿›¾Q´êµXXæIznËÍ 0hGcÈÎ!f‡¢èA–¿(HI~È“vySÙÔ‘Ì[4•³·Í»‰gá‹vŸJIë·\mX dµµˆ°Ç›DÙE­7wž>³¾º>{|©×ÙI³‹ŠÙ:РwÑKXBYâC±÷Î#"VÙ‰ðú\Vݶdçé¥Ñ³Ì žŒ÷‚úŽ¡J"Ëßä¥yþbçúëþ&šÊ3”ôtƒ°P£v2i$c·ÑÏ»F²V§#4’ÕpÅ|A”Ü’ø‰u&ÏãÿÅ—ÅÜ›Ðü³ÁzåÎÔyEA ëbI Ð2IÙÙ«A/ Ç0žt$)bà¿—òS“¿(?ÞØòÙþ·÷9+O5ç£È¦¯Ð>–лLí­ÚëmsüíÐoI$•!Ð3;ûW£Ö0ÉòJ»iŠ2A±Ôå Š#üùwR‹¦²,A†½%$i¾}ÙM B#ÙM@‹bÍ ÀÅhfŸ»7ÓÃÜNŽïúãŸRõÕ¸¥;h¤.ø‰‰I®¹Û¨Ö”§ÕÔjex;êÈìðã'ºæþg;Oj͹Þ|Ì‚sgìæßŸ~ãûŠ.È¥+5›#¦Z¬’F‡±¥}oùó?~ 3Ÿ­Rku1–º?éŸPdåˆlµ®üøÍ—²ÐVºHÉ%¼P*h¸®¿ýv¯¥ôü´”çñe{6Š×»`+­ÎOò\Bfî\õ’¦2*ƒ|‹xd°¦²i*osg³åNëm6™GR™Ø¦`îKw–OÉŠdý}zV®¥ø~>mÊ,Ë:wÚ!Êø3j ÊœkÇŸÔ=màCÆ@ãò¬ÄÐ"4È(G„IF½^µÞo²ÀRO’‰×÷è2XU£I¹ìïÞÅžuo€±ö^8QSy掬†šŽ=Sz˜|©*k\k6Ë¥åÕJEu­F£Ó¾8ó…—OšM–÷Îì=òE‹þ[FsÒ¬JûýŨz"üRþ°¡›%«ü%’å($‡ôµâµ‚´ä1GG޼»Û–-çüÇz’¦²ÕZO”A¹5•Ÿ–/¯s<ƕ˳%ƒçm²¤ß÷ÇÏòuWÏ—ÍÕÌÆŸèkH’é‰DŽjtút!ÿæjäEþÆxš(ù¥‚~ÆÃO^ùOÕ®Kÿ„8eØ€”' ´_Õié¦U°³ÀfâèÁ7À˜!STm¯0®íôÍ3Á“+mNdsdwxâwƒÚž™ëÏd/—Õµu°3ï8lÛ]½+ïØ«]'¦£ã’‚>jÐjÁîÒ9¢ižô‚Ó%åp¬¸òŽžÔí;T4ÉóXE6¿:ó…—NfžØÖ±ÿ™hÆp~k'×k$S(T?n(i$O™m}ª>†os€@À͸›(Ó͉Ê4ÄÇ÷îÛõ¸èHæIvs½V\Ñél{^qSíËbÁwHéÒð³É®'ÌÞX[ü‚#)QNJðÏÁƒG Ù±c3}Ú¥ÕäYIÄfeÑçªò‡ ¹G‘á=lPô2“ÂKùù§IM½3>++_¦¶hÂC/¼6ÀÖwdÙz¥Á ·¦ôé®6 'ô뫣РO¥Î‘a@Óо̡­©¨ªÝEðëÎüè,y˜” ñå‡Î‰N<?>ï3ë5’I£=– @80[z"}–ü.<í9l\RY‘©@À hxâ»ÞxúÅÓ'3úN8bêÔ™øDަ˜do ·p„êøé†/ ];§9îË>ŠÀàè†8åg1&ý°6ñ&¯ª1ýîfL¡yë³1ƒ&Œ}‰ò›X"ÉD–iI%$nÛþñ¡Ë.[kµÖ}Š^åúOóÊH“bÉ-HI~"!;÷ß*1µ‘sæ|Pd9ø¶Y1ÿ!Ð`€)£Á„´¾ZŒ3ntœZVÐÅ #%Ò$-­€õÙà—ÿÏÞuÀGUeïófÒ{B¡÷^¥ ¢"Š€½î ØwÝU”¢£×Õÿꪫu×®ˆŠ‚ JïÒ{I#!!Òë´ÿwîäÍ aRf’IòÎ//óæ•[Î÷Þ÷Î=ç;]et¾ÍÏ,_¼àµiëêÕ«Õ›ccC¥Ru(IúP8N‡$ ëÆP¼ð¨%•AwºxçSj*÷UcBG­2h !ÿJ*»aÓÓïzº¿÷Ì3 ÆðQ›~)Ç*hIhÈ»•Ùš {û· z´WçvƦ¸gýã()Ë¥¼¢±ÉEíºÖ»•õfªoW‰¢ƒÔt ™ƒúÎ^ÖÑø-7EaŸe¾OǧÌEûßÃÂ>% –›Ô”>ÚÛì%òÀ `b“°ní<üì^­°,ûȸ2~@¿Y.j×9&8‹"þþæ›Þéq?† 7íI·ŒîGîMç²U€/Ý1~ Ý0°«ê»ßú;ŸôßÇ,ºjéÂÇ«Òñì…‹£Œ’j08±‡ Øpø–ã1ýdt7ZOÒàMÇE¥2p0¢V¯’Î•Žµ6³‚æ•ÏÓe–_´4â9¼¶ÆÀ#e¿A­ÞºRóâŪêW¶+P4` 4ÔÓÝÚšì1ú¦ÛÀ3,3\S–+ù–ÏVþQ˜-ãwEZ‚:Weîë…}“ÊÜ~\‡hãB§Üõààß~¶›$Ë–e>D'Ñ€¤Ñ𸼞8hЯ:£î þÅÛ9ÀØDA{*n@¿W¢¢¢ÿÕØ¾ËlINˋ݊[â G¦Ý XWœD…µnÎ1Núú×ý´ãð¹¹ÃÇV.Y´‚ â Ä4]ÚÍ`%¹G%©&Âï:Ì”h謊l,ý¼=ˆ­Õ¾^X¼ÝÉÓÝÍü°€ÿ9iuÒéuø“N~±ðÎÌ)Pe]Éo“•SÐ:-+wtqYù2èñ——&é ÆßðÀÙêá)myoþü¬ZwJ9AÑ€¢k ¡2¿-{†„·Ë Ì“\ã–:áWò-Æ› ߦ úP½NݤÈ@ósŽ’r›¶ñ×!ž¹’1 8x"”ÎÑ©Ì3Ë–eÅýJpF‰‡lu¶,³…½ÿ{‰¤»i°çt:tìwYk^ï¤×é¦ß\ž•?ØC­’ a!Æ@_uh ?úR(|'~‘bZ#>å{·TRVNyà)6-Å‚³¼ÅE¥å4yx/¹ÊfóÙ¿kʸœKÛ³o82Ã[^˜²“ Ê;´i%–©cú«OÅ_¢=Çb[ŒMþ{¹VÿÂìK¾sqUÏÿ@óR\C¶K©KÑ@sÔ@Ce¾©òÂw–Õ*µ—‡+Ïä5m)-Ï5wÀÓížNó>^Ñêtp/3’»›s³pûø¦_ÓéB¹seåZºšMÝ"k÷¢ÀÓˆGÎ'ÑÍ#ûÔªN|øÔj0Œ:Ú|#w°ŠO«Éüò¦”ùz„¥ºË×(_«òuÛô;WÅ6§ÍIJþ º¸ïµ¤_Î>ËÜ?X;4ÿ;¨ÿçïŸú{™›û³:­NðX†ùzv WõˆjG]:„Õ)Ù|mÙß–ÂZù7'uVÙ/š1qH•ûcß {wn'–‚¢Rü¨é÷ý'ï„¿ó³.]¡rq{m¹æùÌÆh›R§¢æ †Ê¬'Ùõ¢,›HéÙ¹üТàŠHêÝ#¹ˆFd¨¢5›QLR:¨DaÁô§Ûo _¯µ9G?^»ƒÞùû½Ä”O™W ÄKAxh`µç§d^¡u;Ž «SM­2˜f¤åk¶ÑØÝènø :ZÎ%¦Qd›`›‘ú~Ö@¹¬¹`Iq]ò5Ê‹bQvôÌåWp*ß·‹‡à[ü¯r•:èDûŽt(ªÛ%®®´òÕÔúD·çŒwÊ;` ³H_ ÞzC?3°›ê—ÝÇU»Ÿ¨/{lo„»vù‡F3‹ãQ4 h h ,[¦øS<„›PÖØEÐ$..òªù3%ã ½ûõïÌ*@¾} €¤¼œçhÕÛÉãî‰Ô–œÆ–_v§r­Ž^zôV¤â.£Ä´ìƒdnû€nÔëo&ÌßÙu”Bvöz@™e±Ìâš¾W÷ÿÀ©j×:ˆMŽfÛ^¼lÕÉéo—¯ÙJ/>|+…Ùx ò°ºrJuÍ(stÁרõ5Û<:gk›ñ¶/¦ý<¯ha{­«Û¢2IåÖ%"ŒîÚ‹zG·ãñU¤™k€óÝ“‡Ò„!Ý¥µÛŽx>›øjš>fÖš¥÷ Yp¬™w_鞢»jÀêqo×r+fýàm6V â]*D¯¿öEý×½')ÀÏ‹¾müûLÝæi»¸ä,Ú¸ç¤Ê'´ð?ßÓÓ¯{OPHîv‹äDÂÅ€]"Vÿv€ŽÂšê =“†™|ÿ¾Ú¸‚@]”š™C§`¹Ž Wi_DL‡ÈÍŸï|¹‰:bß´±Ä÷ôìtçÄÁ°„éµU?Që ¦IûßÇùr6<4íûƒ^{|:mÚw’ž¹ ÜKöÃz?˜) Öt¦VJJ»ÌV,º}œ©—HåàLcÔ¯K{ÜSôÛþSÂeB>‰AññØ‹âkbêeam>—,¬Ï Ùå‚Ar¹VKÜ~މçé\oOwêß5B¼t &½Þ@ït—Ñ}S†Ñ¨þ]¨M°ÉM…Áêžc1Œw°ý€›Ë¶%GÏ%‘\;ztj+2[—eùiûa_z¦ØÏÛ›>Fìþü—=á®ô콓)<$€}Ÿ|šh÷—÷€ã5Ц@»ŽÆÐñ˜d1^œ^—åŽqE_Ì'U¬˜"üM_,Œ¨•j’ßåë´I6¾¥7Œs :Ã)¼0ø¶ÑôÔÝUô¥HËÖÏ^¾2gš Ï(à¶ûÁc —¬Ôh¶5”¡¬e+_é}“×@Ce+e‰)^«ïMsÕÍ…iKMR¦-WÍŸùE%`ð€iy‡·§‡„l-–´CD²Yl1ðe9pÚVÙÖ­|pÓšÂ24ÊÂé÷Ý<‚úœïMç.¤ _hy?EJÖ¼‚bŠO1Ås9›(¸MÙuaæChòˆ^p¿üá(ò9Ý:¶þÆ ¢³.Q|‚…9æb,â¦rºVXŸùxv‹`ã oâŒoø‡:HϘ8ˆ†ŽÜ‘ô~¦ðÿ6Tö×f Ê ¡æ}Ö+ ŒÁS*Ü-úÁÝãxÌEÚù˜óIÄ–pÎú #âž­ÂìJ’p)‹Fõë ¢wË9[Ÿ95­,¬· CzÝ3 8 K5 ¶G_XØG™ûRY ¬ü’½Ü[*¢h ñ40{Å Wðé~‡€á{vnëñêìÛÕœŠYE²øYñøÌ ҭȈ‡Äc©ºÝßòïFÞ¯|*P4`[ ñFÙlQ„Ÿw[³VóŠ.™×å•Äå•É_ÍŸ…Å%ÂÕ€ƒßتÊÂü˜²„‡ —p”˜¦ €{èL¢¼ôDùæõa&@Ç”3øæ2­Y!Bá3ÌöGaIæÌPlé~à–‘¢ >Žoœìîñͦý­á•Ù·ST»œ7‘sÒˆ¹BùXpˆ ÙåÕ$-»4°DpÚ~)`ñA¶*–2X¨+ ƒüóIià¾ôæ§„5œýª9¸ÁwNmhÛ¡³¢};œ€˜Ë8ŸÈ/ FútÝns‘ü°HÉÌ.+¼Ñº]¾öe(·&’V`¡nÓ|c¢šíµ[“1n*ǰ“œ½“:·Ï€«ÔÄ¡=•qk*ƒ×ídcŒŸ'}µqÿRræ7q¦Fƒ´1Š(P4`S ”¹b¾q[/6ÓÔ6úy·779¯Ðä¦`Þ€•v`~8›˜*,¥l¥”…9/;TX,åmð¥•%óJ>gvVQ¶Ð2ËÃãÊ»¯úd·‹šÈàžQH¿z†|}`6gëó¨?ŠT³óÞ]-ú\wnJ.e «ì·Žî Ûž”+u‡…¹&®,èûWö)¾ž°ß37¤Â‚çT¯lef |Óˆ>ô!ãFøo÷îlwSÙ¹wÒ5>šò JMê·Õ¾”|‹“r[ßf‚KLA¶Íî:µ5~ÍeƒdUJö½Ñ8•ƒ·ÆêÞ\º¦ô舑ç2=U¿ôuT5ÏÕ)E+hÒ° ·&ÝÆi|°´¹âô+'Íëò 8OЪwÐEdQb*6Àc6Œ›Gõ•ŸìÌnl%¼Ä@” îÿ8ëh±…™­ÁY9‹òU…TóeHÏHœWHPƒKöfŸfn[6ÜØG—EæDí_êCpÓ`Ë8»Kt‹ }àãyŸ-ásS³DÙ̱Ìà˜Áü&*²%ü,,ÒÜÿÚÈÓ p?é ‚9 ‘j<ƒ² ‹Ki+À{gøaîßUø?3»K§ðPáþ±aÏqán"~â—”š¿(°-úÅpáµ|îé, PŽ ¼>Ø—ÏS> ØKøMJ”œ¹Zo0N½wÊ0IÉöÒlË(‡Á2fÈh0¾0÷奓[F¯•^*¨½”'|íuf>£m0|½*$ãÊÒéJÁLá!oî¹ï&Á ñú'ë¶Hι3ÆS×J :Ô½ûõo`rò޾Œcá©TH{ç«ßXcv>ßÚÏ×\a5+¾ÞžpQ§Óˆ~ž=}¬88NƒIbØ9ØBËîìïÌu,l5þqëa3…¿—`­`Ÿcn¯-aæ f¸øË[_Òƒ·Ž¤}£E âW¿î¥W–ÿ ¬är ­ó+oK¿œGÁ–qK¥ ¤Ž®"lmf*:jä@<~™`ßhfçèܾ5͹s,ô·—€Y„…pϹÓ+WsÍw¶„³%ŸGØ%åÿþzp—‘<’fqÑèfæò>åSÑ@Ch{Oàžrû]“›¯Ñ†¨W©£ùh€g*a¼Ð¥eç}4[³¢ëJÍœ*Ó7Ÿ^7\O4šÿz\¦Ì`ƒÞè_nÐùK’Ñùß™' BFTz‰ôÅI]‚MÅä¡Ïn«‘¥ÑŒ³<`®¹JMUh ÞsÆ šÙÎeƒnf,ÚFõÆ\ŒÀÂîö…÷íÝý¹nÂצ-Ÿ¬ŸD™9çD'î³’ºv°Ý'Î(˰®Z÷˜-•/ûn£Á·El…µåûËnL‘Æ€ pí) àÙç·¦n׫›Ë+)-#¯ · ùxf¾ð€u™iîì%Ô÷%¬ô/>|‹ð;fý­ø~;ùÃï¡©£ÌÕ°îX8‹Xm„õÂ^Cn®–6Ç_ÑÓ­_›¬ú^H0¹ÿÏþäŠÛ^S–}þ+<—pâ³·_¿ý¸Œ…;ˆ›7YLçøb-5¸ö­WÖí¨¹š×#qC9'ïšØ´|vÔ‹RTí5À3€ÿøï/x®¨ž[¹dÁÛµ/A9ãiÍ»~eº¼aHòÞ Pý‘ 3@q$žù&«S­T$%å!æà"–óHýK:¦ò¤½Ë.¬Ù”h­êS¾ž‹òõ4týÝ"¦šò‰¸¯«Ê «S6»‡2¶’¹ ìãÌ–RG×k/Ìíãò*ƒdÞn‹„·×W °"ç à_:’áÖ‘nhæ“¶–Údù\[>Ìߟ1n>fBG·&’å¾*ŸMCìrñøË¯ÿ¿M÷û¦ŒP@rÓ6§m%óîcöÐp>)õ9ü¶ÞËLŠ\OóMïü¼Ò‡IRÝY®ËÇ‹ˆF]»Ö­Ô¾^çQÀL,ϳ‘Ȇ5Œ*ìÖÇ ?»ÊaDc·@6ò°«& Jئ.çùg\Îëq%¿Xr%=̳-M‚¯ÌjIrýrÅâ¯å‚½^£•ýuÒ@uØ­N¶´“zv¼vÿ?€4Å_Ú†äÇà'kqɨ‰>‚ü¼ÉÝÊbY“s”cLèÛ¥¥Ã÷™3 ^Î+€?µ·| ¾fGHV‘¾9cÉÈ8­›«#ªQÊT4P¥f¿¼ìh(…â»>Ëa©Öb->y°X§HFc ¸WS@«zöCÍK–$Ö·àu{ h‹U¡¿O{b°|*ᡃõ{Ÿ£GnÞp•¯ruÊá·ÊeH”¡HÝ50yx/âÅÑÂf–…ÛŠ¨¤‚Á®wkÙ^ÊŽÖ»R¾EüNÓ/[ˆÌ“†‘ýº(ÁØÕ(kõÐ@÷Š˜£žF ˜”™=ÆõbV[£«¨'£‹Q¯bž*X|%•«”kp¥ËmµÑpI›©KÓ-ý –÷[vïhœ<¼7²ÀYüòê1UÊnœY—ä!ÊÊuêýÈhûóŽ£SKËuƒæh–L7ê¥~0Öݨ–Ôcu…:@OOÎ;`ôôp7𠯯§› ,Z’×jõÈK¤7 ×€”_X¢Æ±p•6Ëœ…KópÈ!èƒ*£qS˜Ë¨Ý-ÝgZÊUý:k±}tß¿QLò&LŸÁªG«·>HwŽýî –Äµ(N9Ô 5 ÕéåíÅ´3ÉcÁ¯ú/Œ`×{E 4œÒ K§"©HÇÑU¤zo¸–(55' p°·««¹æáK@IDAT‹^§ÓE6§~UîËêÕ«ÕÛÏÄöÖé Ã#‡©HÕ þÀ É™A0¯ª„Uœ„˜¢Š“õl{…qäÜ…%ii)@¤Ç½S†s-?\ØM’ƒæ»E¶uѬø±Qo<ˆ 2äïë¥Ç :Ô³m‚T+nce /oÛ9¦(‰ÅàâA—²®pð¼RZöØK9ãtFã‹iú=ùˆGÙ ©¤o&ôê²~Ö¬Y²j¼ïU¡”í y¶*OºŒÖï~V”v1c?-_;ŠFöþ+ èúÞêê¦fƒAGÅiXÒ‘e.›ŠJA»¦Í'=Ø5˜aCg0%3a@΋‡›?²çu¢Ö=|æm‡ž9W VÏeë)>Ç@¹¥FâìxùåF$é8b¦Mvs‘ÈñzA^* öTQk$ óæ}u¿§mˆ-§í+¡KVIFæ ô Amë6®Î¥U¥5MIððºÇÝÕÅ0¸g'Śܔ® ´“›À[âhm͛ȩºÓtû&áIqß–“ço‡[°Ÿ—}D› 5gœÈ4¨>–笮ø3ûs¦WÎòZˆ¥¸¸Ô#(ÀÇ)Xf8Â}S†Kœd¬;­@ÍZ×HI<âÜ^:†#1˜ÉsTÍìLçz"6ÅïDÌÅÅ¥wo;›:{Á’ÿ«ÇÊ•š¿gר‚fpò¤·Ó öêxR'gÓÖC‹E‰%e¹´ùÐ+´ÿ̇Ô1l(׆“Ÿw¸³ bkuÅ`²È¡â²+¸3q1f “_ªXòðY„mu• ¿Ž6’zvšNíBÕµ˜F?/沞Ö/§]µ¶ `å:I §DœA—0_5…<‡La{€»Š¼®ÙMœÝ*òË ”Yd$¦€Ûƒz“ò®NZuWOwzv˜bM®Ó@('ÕYâ¯ß=uPŽ*{ßÖ¹AʉÍFÌ T^®g|ÈŒ7ÍBžÐüÇG«ÍyÑgánÊOýÀî‘jXb)€±Au–ΤœQŠÅá>r ;Çñb¼y¸Ë‰Ød$-;ßæLBꕾô%æÅî­ýþýÞ3Ï\›~Ø™”d‡¶(@ÙJ”‹Òý1ò÷jG›¼ÐlJªQkðÉ„5b‘kˆÏ+ù0•rŽÆ|¨ÛHQý( êöÞ’›Æ½áXqW)¥ó° ÛCrJŒ”S¢§3"QH…“q- f«ô_êkÊ6XËÓ•Ã ÔKiú?z²5¬2f*¢hÀžˆ»ÈF$°Q«þ°g¹Qûñ_Ò-{Z¯Ï}õöE²ªQý£ÁìcqÝg£/ÎR'ÇRU€f‰sü´ýˆ÷ÑsIoh³òŸ˜«Y2k¹fági«#Ú¡e;kµkĤ§F‡Ïÿ—ŽÄ|fÌu©¼– T %_¯6äã eDÔº’IM\Ô°€z¶ vÇ(//®™9g‘e´‹F“/-×›‹ôÚ[ièxÜW4iÈѾº´§!Ή…yñÎ:˜z-˜å[\d€Šº»PkoXW}ÝTäì_Šî–ÁÜ\¦3R\2.—(VáôBe€©BwµQ¸V]a®ä;º»Ó#ýÜ)ÜW™ñ®•ò”ƒí¨½à–û1 k„H^eï²­ËÛ}ì<Ó–å„©Úî²ÞÞÔÖgkÞ F°ëW P»±GT;ÃmcúKr­¦Ögm/gßsç8)&)>]¿§]NnánX—[¹tá§ÎÚæú¶KÊõÕ ó==iTßçhdŸ¿€ºì%¦ï¡ ¤¸f+3»Z”ÃåB­r%Wð+º—{ÄÞž&PìçÕV¸iøzµ®“Ø`ÐÒ¥ì£Hñ¼–Î&­G¶¿\ÑÊìÜúê·YÔ«ÓtóðÂ˹Þý£„V.½Ê½Â¿Ð‰à*žÚÕÃ'سMfo ¦uK/0R€3/Ùűí2,Íìç\„¥@Û ¾Î Ð „stІµs%μçʹ”Q4Ј@ÐMk®¾1€2gÇ\†ì¢,Ì àK“Gô¦a½£ùºG)Ûì”O'¤ÒÇkwÐ;¿÷ÈÚ?¢lEUµGîûbÎ{øVÑþwðt}…Œ¢oÿí^ó¶ªVÎ%¦ °ª8ï«:¯¶Ûå~„ú:(ǧd"«i ¼“h4X¸/kÛàF>þIͲ.zCÙ6•$…݇¬°#ûF+V ŽI—ˆ0Zð§[կݥBÆßÿÍY¸¬hÅ’ùkXe£]û»P£5µéUÌaæT®-¯r}{ʸ}è±Ü8ø5X·?£Ýàz.Óˆ¢O%|/üŸ§ƒ™ÃÕ¥ñ}mȾÚµï¬yp¦;¶àÎäAÞ°èÖGøìPX Cߨçšàú”¬œ«h á4à„‹9p½˜ µqäÏwŒÁÌ–;í9‹Tõû¨×—ў­Ð-‚z°Ö$פ¼”à¿ïD ïÓ¹&§˜Ñéõ´|ÍVd½•ÂÜýÍÛ›êJi™–þ·n·Öät•:ðMµO.[ÖJ_bÜãÖ½o²Ê/lMU Ùn/wzbæxé¯6ñÂõéãš×4Gf(7䯪êbÆÁÝ¥‘·Ñ¶#KÌ|ÏÒvÑ7›ï¥Yã?mT;v‰˜·¹ˆ˜YB–áí\èå1ÞÂÍBÞ¦|*hé=Sª¼®ÈÚ%ë4†>BáöÁSÙíBƒèЙ tä\’°*s[.çÒ러GlD!hŠn;É¥ôï¯6Ñ}7§ˆÌgÙ´ï$,¸`šÊ\°(#³^:ŠjB÷ß<‚b“3iõoÐkOIÓ²sÊ÷ p º+º}ÜêÝž¶>G[œ¡¼Âê ëÖ”‘}׬¨¤šL‡5vPwúqÛaý›ÙPÓ³óè‹ {‘éó2qŸï¹i8úHï}³mÕÒÛè“ Bß^|äV¤^O‚Ù®"çamþlýš2ªê×….çп¾ØDsfŒt]ßo>DGÎ#¹Ú0¸G$MŸ0ntjXt“iïñÝ¿}‹¾÷‰nG3&¾ª§â.Ñ7›öÓ#ÓFCWõ÷SgÐÿáš­Æìœr&ß·\ódáU6¡/ÉßÃÿ¸ÃSwMP@rg Æ ´4ÿ½5 œê_là&8¼:ejÂá*vŽ Ø¿ùÖ‘ïи Ì º”uœÏ ?góÆ^Y¸õj<³‡}<ÍWÉ <JuίÉhHáVæä#—m#KRº‰Áà t1²õÀimMÓŸv9OÇ%ä‹Le®ËÇí?/\xÊÿ×½'hæCéáÛÀ „¬fœ¡\« ÛnX= ¼pŠ´¾ Â† ¬.prYœÑôŽqƒèo÷ß!=²s“«¸îçMHPÄ郫:çó_öˆv?{ïd  Ös¦ ܳ£(ûŽqE›}½A'·,vÇ`9—BZГè;Ëyøq–” —@Ѽ LfJ´z`î3Sq}µq/^2"„ÅŽŠ¡ï•?l£qƒ{Ø$3ÝÙ;_þfˆIJƒË…ôèªÅówX××”Ö1å³Þ`sפ¡*NÇ­HÃkÀiº»wl£‚LkøÚ_£”¯c§ªah94œÏ²0XÞsòßò×ýÜWN?úM–‡ájñÚ8o¤RDÑ€¢Êp‘\úÊÎͯ¼«Á¾¯Ûy”Þüt}¸z €m+êm²súu‰ ‰C{0ËûÎÄ \÷†.t,梽™Wò)-+—†ôî$øjÙ²w1"ÛÓMðy®,qÓl©ž1q éÅ™É($ÐÌÈVåþÝ:¿'±¿äMöë­‰¸‚ rÚØ´õàYʸ }ZÑ3ˆL¸”‹pWR»Ó0¸g$§_¡Ü‚"jd,‘õÑÂÄ:Ø(öb†Xç6p2ˆ˜¤ ÑÞÝ¡µèë¾ñhOÚzê)¬î{áþ! §*¾cü@XÌ }ÈÛ™¿÷ýo7ÃÚM†t—7×ù3.9ƒ–|¼NŸ’¡3ª¤Y+/ø¢Î…9É’qaHŸ~d¿hghM‹mƒ^¤÷36K/…fÙ©ûK­aÇûw¹V3çóžïR§6ã¨mˆ‰i¼†ÅÔë°ŒB#½ŠLw²°%yÞHÁ/oR> (°ÒÀ˜ÞÎm=——‡0¶Ü6†0íƒY“†PÀ"‹ NËâëå˜óÎÐ^è‡-MH„%¸KÈä gì±óH M½¡¯°˜ÊeðgÜ8XäºÄ—ŠkqÞá³IÂ2ÌÓ¿ÈX(–šR€±kĸo°«¯Ër>1]ôéÓu»åMľ˜)™¹²ö0o“WØå‚}¶Ùå$5+‡þ+ôïûOÁU$›âàFÂÀ™Ý€[ûÎ2èÞ}4Fì“ËêÕ¹¼jþ\·óã\˜›wÔr¥¸´ŒÖ£¬m‡Î±9Iå"ÝÓÔi½4šÕniº˜ÁƒÀÌfŠ4Žøå7öbºÁ § ÓÇÖªeÇê×iKgÎç„”­‚‘ÃhDR=ÏУ·nj°à¾·R>hÜX"üUôÒ¨æ—IÐi_iX“Ô§Ž}lÑ’NÄ^ZWP,q&­†–ÛÆ >ʶêuw³MIã«lŸ.í„;BrÆRá¾Àe 0 ‚¯îºÇXÀ­@{˜‹çÀA– X¢;…[¦Õ„þº÷$={ï$Lù¶¥mψók3ŸêîÉCé­Ï6R¿åþãÁ)>!AÙÖàŸ·]€¥¹²t‹ CvP=톻I;€_vÇ`Ÿéð;ÎÊÉç)iá‡àçëužùt¶®3sû(Ëâ F‘ÊÂ~Ôî°€³ÏtG耧¹k+»Åü£õ%eZBHøú¹Í{ëùçMo!µ-̉ŽO£¸ÎH&âÒ&$À‰ZÕ²š’ ?üå?lcƒrŽ—«÷+ͱ÷ŠëEsÕöé–o›ùr Áûüy Ϭßa;µ´?Ådmbþ÷Nô©í[ýZ¡œ­h éi@­VÌÜ]°D6%÷ ößMLͦ¡ ”cIÉÈÁoœlH……¼2ØfpÌY7Á—™]$Ø7`ûJži6Š­Ó0gva¨™ç…YuÛ·Pï(,ÂòÆNá¡ ëô  {Ž‹²Ù%âTü%±›ûØrw¶òòX°µ¹CX0±ïu€v–®ÈþÆß‘&ÙlEîÕŽŽž¿(ü–™ƒ–×û Æõ„øôñƒDYÿûÙbå¾ÞyÖûœJ0"ûP8“ÓóóÊߘ³`É œœÃú¸¦¶yñæÂ>ìŠ4¬ø·ÏÌ1‹?úYŸ“Ÿÿ¥ÛÞÑüÕÄEÛ°MqxmMú"q¸všy¾Þa4~€%¸ïàÙU˜ºÔ:¼×Ì•,ËC}=¨xŠQ4 hàúX®y)@çë{NS¯µn^¿„Æ9¢gT8’þàހŔ”„-QŸü¼‹žùç‚rm2ìZ·º:™ [”žzƒð~eù"°ÏëÝ9\ø%¿ºb-ø×÷k;v'˜'\],B¶òιs,Ü(riÁ¾Gû>,À‚o,\)VÃúý÷w¾ \1ƒYv½™=ØŠÌß»[%M¹{ÒPø6·sÆïôö—¿'n˜ ßëšsWÿiÚ  oþãtMN¹ê˜çî¿IÒ̹ì##¨_×m\\Ts dÜ‘nx=~öÂÅOÎÖ¬¨½™úªçË{óçg†5ëìÓ‹Lã´¢eÕÊ.Nì²´ð?ߨ=I«ÕïwQ¹ö]±xþ¾æª‰z;õà"ÛÎÊY¹dÑXþ´!Œ‚˜¬—ï€ìÄvÿ³/ü»oèîÏ=p¾*Ò˜0tôá#© Øµ}Ó°7¨_ô=kÒá4Ýÿƒ‰Ï™“{l}ÐZyYR«X)¸Z üëó_é乄Ÿ½ýúý8) 8bŒéªÌ!^ƒk§+bo <­y×Og,:éïíÑnáŸoS;ÖÞUؽ<­NGóßÿžnÖS°>XWK'/]¿l5ô€u™A£,œÅ¯²ZÞgOnKåö• ÿk Ò–¶Ô´>öWfkœ£x¢kÒ±)pY9k@p³dà=๕‹|U“óé˜9 —Î3 ÿ`É ’¹ \?s]ÿýÁ)ªyßBa­BÀÑý圅KÖÍþÇ?šT&µKÀÌyêƒÕ[õìÚãL"'¨É-°®7dû~Ùu.>Iv­’ݤ悜g€¢#Úøã÷ü7£Öpøñ——zlÑÒ»íZ™“Ödƒù8éiÜ€™:ˆ„å°,°8â9ɈY5ƒìà@žÚ´nux•«lAÞ{ò¤×Î%öUŽ¿´¢ÂÇUy|]w$çëig’iœØ7ùÑþîu-JD”Ÿ†ßà•¼"ÊE¶,µ‘§ÜxüÐÅIz"™CåÀ£–¢–ØÏ¿tx÷ƒ~ì³¥Ÿ¬w{bÆxÉšQÁYt–×Õ¿­Z+¸3Œ«3¸t–þ4Çvp€à¼GnQm?tcuð©HŸóØ‚ÅòmÆé»¬Õ^m4µ‚ºpÜ”‘}ä·Æ3pA}áÚ¸÷¸`iØ-’¦!)ónWNP³ô©ÂÂË®; `ùÅo<¸±™[›å«ûD@èåÜB‡ðä]‡8»:pšßöB¨æt°|Ž­ä8ìÛ¿a÷q:ˆ¤@ŒöCàê0Ð9^˪ÂeÔF˜a¦LyHeå:5' Úuô|¿„”¬¯ç,Z:ÓSíõ§æä¯Ü¤€²¤òÌ{¹Î Ž9jÙ×Óƒ|}<ܤºR›ße•Çæä—ÑE¼A€Ðž3\qü8øÏ1ר+ôQSºNcÝ·ó=´_X“ ™±69(oI0ðq‡ÆF"Ͷ_ͧ-ù†¤ÅC—Çnˆúoñã_d5þœÌ€A3ßtyü+[Þªü);š¬–/^°z¶fi®…_ÞøßúÐé«FƒæŒdÎ"ì¾±Š8¿8[!’¾HœI±\‡t/ðEmJ¾³LeðßaPN‚Q…9ºmÍ Ø«ol¬ûr#²1œ3á,’‘ªƒ$Ê j@›†Ä8aÐ01«ì<K'‘e‘}ïóŠŠésdräTðÌΙ,™«œ¹ÁoÝWø±ŸÉ»$feŠKË ?—,( Ù²ËþôœÇËÓ 0“i'žœZ~ÁŸnÙ+·áå§wçöà2B¢Ç0ƒp°-§‚Ç¢Þs<–ý÷§•ê‹;h4Û†k4ã,}{)¼Êi2èò4Èë¿Ü°OH0õÎMÃ{"€¢µC/€F:UÉ~tç3ð†šLk˜øí󞛆 +3|Ïj˜£;L6å¸K[êÔŽë´=Ñ’\dBGÛTR•Ë`€Ì´ØzÌ7 d‘®V³ª¬Çÿ‡­‡ˆ-Cœ2¸'¢ìiÞ`¿À¹š7ûêôå_}óëþ äwãÐ^ªtE­ÉÜÞ›÷ 5¡Þ±%òé»oDÚí_›äì¬ íaå„5l&&v˜à_šÿxÄt„°uu¢h‹g1Û= ì.Ö j‚M-§tÙ:ܺ•¯Xx;§‚Ÿ<Ü”p‡Ó¹/yâækÜŠ¦í/¨yŒØO˜³>vn*]çÎoNŽóΗ›[ S²›Ï8×—ƒ»¦:Ù7\ãžêÿ|»e`šaïßpÞ5=×™kwRŽòý~Ë!¼]ùÑÜé7€KÒDtïÌŠmȶñÛrÿ®ÈR…åÂ¥lZ»ã8}¸fRÉöÃÅÛSX˜0W'áÁýAsDÅ¥W0Õ“Aé—O@ß}ª;¥Vû À™|(ÕôrÉszc#®¼Ã™}· 5îO;Ž™ÆÿFeü++þšñ'íûßn!Žæç `Š4o ,×<Ÿ‰Ndº¯¢’òWÖl>0þ—=Çô}¢Ú©9c/r6»æ­ ¥wöÐSæÝ ëèz¤wq•†¨Y¸ßå6t³_^:N2¿‚­%,¯¨D£Kͧ0kÙXk·Gëd;•‹AªmÁ\ÂéÛI4ïÙ!+¾1øµå{Q‘ÐÇÇÛä²X†´çÕ%Çi…—ƒÆÞHšÃ,0±39ÚQÊŽ¶&2HZ³ùõògNØ ~GŽî—#Ëç—ˆ'gŽ¥ï6†uù¨°Æ2Xæ©øê­ËÜ-&ÐÉøïDóâR6Û(H%}Åë>ÓÁyUí'[‘ÙÕ†Çÿg?eükö«ã?Ã4þk6ÄIFñ²TS7œšÕ¢åŒX±táN´kÂ\Í’!%¥åO:“4ùS "ƒ’Tè§Œì­æéuE \Oìç»åÀ}Iyù_qì]×;Þ™öÏ^º´±Äð®Qo˜áí塟Kòèþ]ÔÌwí(©éì û-³•—-Ûœ®Ü–T$j«Žê’ãØ*»¡¶µnå/Áå¤sCÕçèzª73:ºöjÊg°Äîßo9,@Ò}S†( ¹}Yïâ õÅàrÝ® º(ü{ÙBËz­J:·»Ñ¼+6åwóº=V¶]°Û‹¬Úš,ƒdöGævsû¹Êø×|¬ÇŸ¯¾Žª÷š—¬Ù4Ài‰W-YôkˆO„D n‚—H˜œqa¦€xúGœ@DçÒƒ²!½:ÀSºeõêÕ³ÄÚ»×s^^2U*¡óÈpxÇ´±èõ§fªÙ¥Á‘ ¹º>ØJPÃþÈœŽÉvØÂ|)3Gdp¬®œªöU—‡Ïa®næ[/-Ó6hÀ;®iRÛԞ𻪎6òv§ÊÌbñ%"@ÙÝ‚-ÉŠÔ^¬·ÖÈ$µ`©°¨Ä –«*©SÛ©k±WNSAQzU‡Öj;[’Ù¢,Ë8òU% æ$s{¹ÝÊøW¥©ëo—ÇŸ¯#™æúg)G4 Ì^°d”6;ÿ B²f÷íÒÁøòìÛU†8ΚÌ,Ds—þ.¦3÷õ…é☺ªÈ ì%ÌËÀÃÂT_'‘]°¥Hç­ùÛ{óÉ“ó­“wÔd"“øOm[y!€T=edŸF7®qÂœÊ jfLÌt|ôÎW¿Ñ“¯Fo}¾±Wu£«.9×X®“Ósè/o})¸Ÿbùú‹OÉ’0…¼©!êkˆ:œÒG™Á³äàÇÃ>ÉUME4„‚šr¬·ÛÇôEö"ÀëFL§ñô®×&ÇAF‹2•`Ç  ÝÑÂÌ#_lØk€‹I¶JíÿŽ£ëk¨ò(ó1ßH·ƒ¹Ù ”À½úýX¬ÇýÕ¯3©*@² ˜+—Þ.d(gçš"†+SÛï±9†˜a¶rò¸—$—ÀÊÄíUÆ¿¶š¾öxyüùzâÀ¾ªÆýÚ3•-MMO-\ÜQ«¿¼$Y=&í!!J^ÝX‰-˜@Ra]:€(Dê3Ý|iÕ;Ä 1h3CÏ‹Üj“?6.[œ!~aL7°2ç/ Ó!2w+3Dµ ¡û‘š™ã•yc xžì=Õn‚R‹Ù8(jâ?1]”Ån K?ZG÷ß2B° ”kõˆ‹9@Çb.Šë…i¾úw‹m,,.¥ï¯ŒBLùµä£ŸÃŒœºúãµ;Aíå!h¼ŽÇ$_S÷̇Ƃ »O hº)¯ÛІ†1¢³µø§V™  ¶-µ(Ëñ‡_ðó2þùö±Éuí‡-ÿbó>^u-òšóª¢ÀcÞP¿¯O×íbBI%©žü@ódá5l¢÷ÚUG…°U1ñR–¸92‹ƒ"õ×ë‘Ó¿&$gk­)-Y϶$Ø¿‹ysvÞyóz}Vâ/[ꊪ֚¬íãvr{•ñ¯Ö-ç²lðuUÕ¸[ŽVÖš¢æ¼¼t‚VRõòpíö×û'K<½ÛX ™õÇ|°k·Fô¾ =Š,‚¾£‚ë›a¾ï»& åU3ìé„3lŸ7øgÑßî¿ IôpÕ8&ŽEÊeúuï šyãPzø¶QÌý+€@Ÿèö°Î¹ã¾!¶óKb9˜ÎÂzýÕÆ½ÈD!,s¥ »œW Êâ¶Á\¸ Y~‚ÕøÀé$uèN3ÀÃ,ÌËlh×9mç—{ëóøÜ|$?*@ßYlÕÍüïœþwTÿhš;s¼pÙyÔ> Q©ÿå¡/,..*fUqZÑhŒ*p>ß0¬W'þ½)Òð`÷©ÏÙ+â`ŒùÛŠ%ó×4|+W£Sýªd«"ß,ù-ˆy’›’À/S~"UkåvÇ\Ì \Ì•w9ü;ë‘õɼ‹œ½ÍÕÕ”q¶Y0‚¢ÍíÉÎ5¯×g%.Çôâ2:^ûn&;?XJaÝáv6Åñ¯«ŽNÄ^ݾ]3¥+?_W ߯žý¤®½PÎk, ÌY¸ìf¼­mÖJ…©[u Àœ3PÝK/Kvn‘°³•2ª½É*Ü&8Ðl!æc*óÇ2ðeapÉwœˆïÌÀà6‰ب{Ƕ⸪xcßqË@óôõõü¨ÿ8PÝiWÓc²^S³sÍœ´Å%×O^¹îÝÅü0j–^pã8 Ý›Ðg“dø›#ƒªÎÝÏÓ>‡up» í4–U¼è8¬¥`›ȇfÅ÷Û  ¼ÃÃ]zeå’oÛ<° otB ¬Yv¡È¸WÕTBmõ½qï)LË™ÞÚ¹Ìv³Soè#Ýj[VUdz»È'ëöÐLX úÙ<1IÈx—I”¹Ï>ðƒâdìÖà˜À2SÆñT¼µùuÄÍÑ… F²¥À’SË”§õ!µ^½bÊQA×þäL@Y >­h·“Ûk¯ñ·n0ûüß—¶=þ4m$uAKCË1LѲ”YœÁ’¯+=’l{C÷Y©Ï>˜ûòÒɘ_Õ¾µúé»'ªlMñÚ§¦Ú—Ò!,È|’—»HÝË¿óª¤2,[¤ŸM³ œ–ÚžÂ)!½miäÓægé÷?ÎÒýSLÓ~ö¨‡oä¯üùVP§R©¹k~Ðg|âJËÊ‚‡»^<€*õI8Ž–³óLÖäËø¬Oâ‘"­‘Ò M"W¼öwð¿Ö¢,;33pû¸Ü^G[sžš5NÍ¿ÐfÑôqÄ÷ÀÆ!hwD?+—É×_W|}±¾+{åã•ïίÇ5¯w2è ß!]®ÓdÖ^m§Á­ƒ¶“Ò²á^qiy' ‹ñ¶ƒgø ƒ•i\nÐU¤'f_cÞÕ®µ˜-«jÔØåC5›ØóKcJ¦…žŽ¿s;²ròåëü”¯!ž “…#þåTÅò6ëº=ðÒÊ–é%Or.ç6($¤d¹"¯«‰Xßy›*Z† Ò¾L¹o’ª±èàœ\MvkÿÎWÿ~Àp>1 aOª£’‹êášùgìV“äT¨Ž­LÁÃé'9gº=…§ë˜SñƒŸ÷$•˜% “ÝÁÓ‰4.œØ¢;2ʰµ™ÓAoS@~a)ÒPÒôñý0ˆ>­ÑÝ`bˆ¬È’³yÛ9hƒÁ׿¾ÚLx£¥žÚ2dá‡tA›ŽT’Á¸9Z ߈¾ßv„Ò²ò¨U€7ÝŽô”°Àœ¹f³=ÖçÖeõ™_RBeˆŒe@Êzf}Û’à€.f œ…€¾úekÿäH¸]€˜ó‘ÇÛÅícD?/ûŽ¿\)üíÄxòwO7*‡¿6/˱óé·?ÎÑ Nß9Šøßßl¥;Ç hø'þ¸íúzŠ—­sp¥‰ÄxMÒ ç‰ïŸm¢[Gõ¡­‡Î «Qï¨pL©ö4»â°ulËÁó Ä*Ãômkô~¢ ¢2ÿãñç몺qwp”âí¨æ¶Ýz2wŽs*K²=ºy%ÏäË òrníEÊ^!¸e¥däàþZ ŽP6Ñr&3kÞØJ“eæfEwh#\7v#Õp/\£;_íYÀ®Gp­r x¤ã…ÛH`à²w96¦"aaèv¸w>ËÇmH˜hf0WVi¥o—ö"­2’y ¸º ®I-e”³´3ÉöCgx¶Q+¹¸áLíªª-h{láâiݰäãõÆû§ SË–U£l¯½8>`ÓÞ“Ð$ÂÝEU€kl^[—ù«4É2-SûbþŒkÍ{Ôd~Ãç…-^ÕMÏÙ£yÉ™¹ÂÖÍÅ:ŠC:IÎb×':œú!ð))ý }óÛADG T²îO~Þ+,®xu=›bnçhg Ä7e¦³ãòXv‹¥D”sÓðžˆø¦#aÖòýÖ#Ĉº}”ï\?‹­öXŸWŸu¤Ì*ÁÁ|²žméÚ: -Êõ‘8+Æ‹è@‹UG.Ózܹ]ܾª¼|Ž£>Káã–SPd.žg‰yL¸³°¥{ã¾Óâ!9sÂÊ…‹ÈæƒçÌÇó±?àåg@·4yX%Ò™„4±Ÿƒ›V#[âž‘ôÀ-öóøpÁ|nC¬X__¶Æ½!Ú Ôa l=qþQ½Aß÷‘ÛFªý0[Öܤwçpá—üꊵ´ì“õ`­èjî"×O~ÞEÏüó Z¾f+Míekðí³Ô„764È—Ø"Í®šåk… ^€¯Å1 † Fû7xn¯úIøsÙ rÛ†ÑKï}GŸü´‹7ÁGº¥ÂØ1ïÝÕxÑNÀ]ì¨âû]s"ŒŸw¥gßüBœwÒêyRÅi º™/w>à#}T‘½Aë¯keH²³Ù(©Gæƽûõï´êÇíÆÄÔ캧œW¡¾Þ6î9Aš?êÞúl#N¸t$³ <]¼"W.Y´¢¹ƒdVƒcÌvuü‰ÉSðŽxˆÁŠ÷ͦƒ”Œi6,™ò}Ž`ဋ;Ç÷„o±)xdíöcˆžvƒ¿±iJÞ ë­ÝMI°÷Ú2nƒ_\6|>ٕ㎱ý®é1çrâð>Qb(SÐâ/Űh3€Ées4öªwQNž ¤UnÏ5…×uCÅt#ƒdy ÞVQ~‘æÍÅõK:’Qd±ZÛr»àŠäqç6ñt¨0÷˜[à\+ìï˳ ,L¿ À™µ,=;…Ó¨¾Å׃§“ˆƒ88ŸKLƒ5Ú ¬¦À£®˜¹8 ^Ö±ƒ,@.ÃQŸ2Pf}+Òt5ÀQþéú×_ëj„ÕÓÆMÃ÷ƒ„­¹]+óÁëE¼°ð5d},o«|<»@<¶ ¶¸ÊÖb·,Ì Ë ÏøTŽc°ÅkÍA+ À?öqž5i<9$6«h:bKdiZ»¾EÌÀðµÂ3O,¾°.¿øÈ-bFHÞÆÔq¼X·S.§2ÿ­¼aðÂ있ñÅ™äûßõFc¹Êƒ–:S»jÒ–U‹_:¨Ñ¬îsIóâ±sI;|&Ñ3¼h©˜Pqɨ^‹ì¯ÏL.ìú‹gWLRºFàDɼ´3Ÿ{z}áì~ëÕ÷²ö{(óCÜ´ ÖÈ£ö}ªò nˆÈùŸ·0òÇMÏZº¸È’‹`‹°àù+µ­Xç ³þ¸Ø]Çg|r‚6(LÖàž-¢)°Zìi.ƒÙ0Î%š@'³cðñ«?lÞÏ7ü´+¿8ëö˜ªç ã9ƒÉœÊšÕlÒyå€>OWsMeZK›Ìk±RX‘h„Oñõ¸ö™.·ÛÃ)·Ï wÖ¢&ûj+cXxˆE7Þx€²µ™û Oó¶kmùÝx#€Ižaˆ½˜%(°Þûv»¥¡×ªÃ²Ïkb¼«wGT©”i ¤ë–ŒE¶Ë° Cª`³Í _¢ ’mÕ\$ËÇðµX“€®ëÔª|¬m•]];åvUþôõvLFåzjógHŸK„¥EË.¼T›såXfV9ÚòÚßß|óÿòóJF ççâ“3;Á™ÖØ%² õn'u«LÛv/±{³ùyÀ “ÙLÀÅ­ƒ›úÑi7Ø¿¢Z¶œwï~ý›ÁÕÅÕ£ 3Q¢Mb†„ãÈzÌ’ªÑ~±]+°¾~ê[ñ*÷Ý‹$ײÊfs¸ÓeÖ(ÿÀÄâõ²?Ù¬jRa[ß4ƒ~™²K–,L=°pä¶Æ°žð”:åþVhùXþdKup€ðe“·ó4½,ò ø1¸]0€¶f@`±nõþú¬³nÙB-›um«<77óæ²r ߨyc-V Ê-ÖK_7Ûž>–q¯hÚÙ¢ÆM“Û¢Ó˜;”Ò²ó®iÆõ‚5ÙÇ–¸»» 'Í{h²­Ý ²µ*ëºA*T*qˆ ’4i†n‘ml_P©U)´¹k€-‰Ÿÿ²G@ytBŸ.o¯jâ~ëùçyŠö?¸ç}0÷Õ¥:ãí±Ii3Î]HSnn.úÈ6ÁRǶ!ªP¸î„øR0ºá†SSüÌ3 l@KÍÊEªèËHá~Řp)Ó€8(„EI¥xœ9p*aD€MCÐ8ÇJ5†ð ’aÂÖ˜R®-ÛTT\ÚV•%Á:L,Ëõzc1ú KžvÇc’ãT(Í?ÑÜ*j2¶Ÿê59ÓAÇðƒ¼±¥KDkøÇÑîãñîkŽXáŠÑùÙYØýb߉„1óÆ6›Õ.”NÆ¥RÐqÐ`,@µ/€6 —ÃÀƒ§"øËÙyàKÕ õ:Zd TžÝ]Mþ~Ü–z[”ùݾB|L3˜ò׫>kÒ®«NpÀ¦âŸßÁ³œÓZŒ±½ªéÙÁ>ðYÞ}<Sµ‘ÂÒÌtm<ÑRݸ7d;”ºê®Œa§@?^Úç©[÷¦+g:©ØÝoÅšmú²2]¡+gA,œžNÚæš6 Vc‡*–…œÁ²\R ×–ë‡Æ]Ìëo?\Sæ§À¬ÁÓÝÕàááfôöpW!–勒iÆêÀXX\bÈ+*•´ZÙoÇè€;O tQë¿U!W¼2»dî¢%oÿ¶ÿÔ³'âRô‡ôP³KŽl,«ij{?Ç.¦gÓ©øKœ RŸSÀ ’›qæÊÏ,¨mµÍöx§ÊΠiæÒenÎí‡Î‹T§~^tïMCÁ^`RWšü¼ã¸pÉ­‚RlDïNðïÉ w¿Ý ë2ÌwÓ1Ü?.çPÓ­A@ß?À”ÀÂè®÷›ØĆFüçáf¡I+­·EÙòâãëÖè³OÕj•ljy®ïöú]'hdß(òϼÚE§ÚªÙÙ@™;Ûw¿©bÊoÌ€èÊÕ4QÙÕt4àÅ3Š(°‡8.äÃ5[°†Þ=ï¿¶è‚=ÊuÖ2Þ_"úÇ}üŠÛ(dÎÄ·ÇTb'ƒ¤ê„9׎…%åAE¥e~—…þFÉÈ–#\p`!ÒaBV +lžQ’RA–*©äJ±nA~'ß{晫²Ð¬ÔÌá*þ‚¤@¿eåä-þbÃÞ_oÚoˉ— ‰‰ÂCAàSg7æýf:ÃÌœ°hå«ö…KYøðc’Tâžmà8~{ùkó7pc©½œ(;ÒÒ5eD/âÅ–pŸÄg½@/¥`¼`v kñ‚oê²'o·Þ$~äo<=ݼ#«™fìêó-Ù—8½êó`?¬Í,n¦:ªj¹`;¯ØÒ»›«•ë…Ö~®>î×e[õÛ¹‹UwÏ$K|Рlcz@n)ùÜ<Ò2f|i-üòË,ÖãÏÛ¾u¸¼K|ŽCà/|cãß” Êg©\®Øèà©ww­ùo”2HzíÅÔü{®ôÐÎà¸åßo¥s.‘~ê¡å¯-Øhç*œ¾¸ ëy"ÊËV,v$â`ºáqÍ’aps˜{1}jLRZwX~ÅC€Ûèçå®ÇL‘òµ»›˜ÀÍÄw_1ËÎ13œ~Ý=@`@ºt˜˜T°p[½5Kz•ÊxÆhTíÏ÷n/µç†·5Ïá H‘úhÀi€2w‚ÞÎö¯ ’k«ìë/äÚ–[×ãùš“õ\•®Ý]-åzû([½_ûºÚ~¶[·§âžP×îÙå<ÀÚ¥0…°¯{c‰¬ëƪ_©×PÑApŽ?Ì©˜ew0;”ªÑÂ4ÀAç­Ýa<A ²Ç–/Yðe SAƒw÷CÍÂý¨”——lè—[Ö>Á½ŒFC‡¼â’öpçhƒ'ÀgX±U>xX³UXLËâŸQ4…xFæaìò±9 å¤d§ÁÚ} ucÚP縊`Æï[s®Ð©€rsVtSê.<¤­ö£ ‚{YËûžÑÚô¥°\\ãâgw½¨M¿”c 4–\ ´OÌ÷vQ1Í™"Šj«¦ÖûdíN‘8‚TÒ“«/ø¤¶e(Ç×OÁ†{Q /Š8±¬LöNÜJ¥i ®7W/sZ]‰y½¶+%: Pö¬Â¢\Û2•ã ´d |°ta2 ¿Þs,Önƒ™¥%ëFéûõ5†$qÑ#ÐK^ÜÙ«/üàúg)G(h¹P€rËûj{®7˜2 òAjõÕ>ÚÕžXi§«9&˜ÕŠ(P4` ` öPŸ–~ºn—Ó’+¢h &àtÛ¯²ÞÆlÌŽY¾dQSg«I·•c ÔK P®—úšïÉ:]©¹s.ªºûÔºWd?äÂÊᧈ¢Eõ×RÇÆÃwññ ©Ùª/~Ù«\XõWi³.¼¾ôõ¯ûiÕÛIo4ì‡ñ£ßŠÅó÷5ëN+S4`' (@¹–Šä¬z2SEåS9]ñþS •77Éïzƒ…ÙÅ¥î@Á»f)G:±¦,eZÅaü«æHNL½\Õne»¢»j`ÅâŸI*éŸûOÆKÿûy’ 5íëË®ÊQ 3k¿Zôáú‡ÏƒAzË<öÍ H.¡ˆ¢E5Ñ@‹æcãœ2E€ëXyî›2Dìþ©Ë=<\éO·¢ý'h-Àö³wOOU> ÔI+ÏÿC£ùoï4cÊËûOĽpÅIC{ªoÖ“Ü+2Ö©`å$§Ôû¤Ã­‚ŽãÞuä|’.¯ ¹’Tá·þ–Qò Ò'3¾"Š ÔSV0¦ž%ÙátL ‰TÐìÿÉS !ìSÌ@¶ÓïÁ°îuhm®–Ôã{šS\ËÖBù€xé~ÑíixŸ(±éÂ¥l|ùK<ÖÛÀ×Ù`©sûP±_Øç`­îÞ1ŒB½ÅÂé®O";ÒØ]ÄqŽøW”ªÆT,t,ëÚV]WòÕƒñ‚Ëv«ë…ÜÓ''‰¢ÿ¢Ò2Z Ë1ÿÖ”¦í‹º-®"¬³#,Å•õ”[PL—ó é±;FSçv!Ý!/F—̇ñøß1¶ŸÒà³|/MlÁf«t~oœ¶ÚÇÓv U?î¾ò/YæBì¸Â}qÃu%ëÚŽE+E9‘4šG˜®fþœ…ÿüo¹¶lÉú]Çgn9pÆ0¢ogõ ºQë*â/œ¨ -¦)ü<á뱦¢Õé(~Ç—2s(&)NÄ$ŠËÊqËR•ɰ‘ÔÒWã{E¯­HÉ\Ób•ã (¸Žœ([ß0¼á#z13ï:M·Ïî‘}£„E˜ÁÒz¸C¬øa½òØ­æÂ»Áå–ðÛ|Jf.€N¤ywxh€°ó†.Zcúþ¨°²UÚßÛ“¢˜9_{Bj¶KÇc-À* >ÒŽ”üÂRò±‘ðÃZïrýeÈÄ'‹«‹§¼Z§OOxœÈR¤•m±òh·|3­¹Ã­!¿ÀBOWy¿½¾úzѼ‡& w‰3ÒaõßG^êìÝ9\TÙ¦•MÌ;/¦çˆcÂá¶ÃÂÁ Ö@—Çzωxñ Üêõ faw ~@®þý°øÎÿøÜ´+ùWoÞi§LÉS‡PS[¹H[ãn§ª”bœ@+–¼‹fÜ5[³ôÒòò¿m=x–³k·È6ÆÑºJýºv/nNÐT›Mà@´í‡Ïáþo¸idÕ”½m×T7žOL£w¾úÍèááfÀl• 3‘RH€ù`ÆQ§Ó#+ª^|–ã3¿¨˜R2r Y9îYã¥;<ÚkIR¯õ òøýíçžY¡Rä¦ú‹PÚíÌp l­$?/wX•··\€ë}ŽZgÿÒn°ò8“Hé—- }ˆm [ƒ|0o•le”…ƒÂ¶<'X0"ÛÓƒº p‚ÄíäËâp¸xLÑK>Ü¡Ÿ¬ÇÂÒR ñªè-*É0·ÇÛ#ؼ^—•`O‹“rVѵ@ÙV™žðN/(i°ñgÐØ 3 Ì~qÖ^(Ûb¶Û˳,WàŠŽ;fD)•eÜà®ôÙúýbfá†þ]H~árw3]rÁí‚_¬BÄøãzòó2Íl4DJΡ•šGÐ’ûž^¶ì/åÅÆG1ƒöĹĴð]Öê©îU€g§PfãÃ1øÙn?tVXLa)-ƒ¥4qãîcÑ#zG©üñbÛ\ä—]Ç ¸í\)-Õ~›’q¥sjfn´NŸÔïБĨÜH‹WÚR•$eë úcd”NªÕÒI•J}2”^LÐh$'‹úh.££ôCÑÀÕp: Ì %",ö¹ëlõ‡°#¥ ¸ ÓYyžõWÁÞ œ]\u1¹MüÉíL¸\âðñgë>=óbI†ï¹à5°Fºf A¦áÚu4–nÑS²H–=Çâ‰-ÒC{v\ÝÂZ‘·§;m9x–¦Žê#ü˜y†º£D¾®ªwGÕ¯”Ûøxoþ|¾¨ß€EòŸs-™\T\öçGÏOÕÖ ©†.¤îÚJ½œÛ†Ôì°G¯ØÐàNla=w‰òðBç«Ãéâ>ßËÅó“*óÃuóÍoûU³§kò¿_~¡þbÃ^ŽwÀQznÕÒŸËzdkñóo½å5$"¢ôúî/ɧ)ŸŠ 8XN”e° K­X"L:(‚™=]#Àl1ª7y0CE dDïN°|dлßnþͺu,|*ƒ^ö ]µv—˜j%Ð…&ë!êX³å­úiØÇõ=xó0‡å£ö`>c?øAp;“Í€ÙV7 K2Í›½=ëàB¼,>xYŶ-Êò¸s»¸}ÜNn/·Û‘ãŸWXBÿþf ˜GT`º>Ŷø±Íʨ´2Aœ?ï8NÇb“…º#^zdaÿô döä Ž¼y(8–ƒé)Ci ú˜Ç›…g&º‚IホøzâëJÖµC*R uz `üù"ü•—Ù+V¸J—²Ga ÿ¦óI·€^®ç[‘››‹><8PÂK£ª-^ùå1<$ ^ ü¢žSPÎ ˆ)ÌÎ'¦Ã _¼I{AvÈLUݘz‘:ggþ'êБ·*”yeö¢%Ï=wñ½åk¶"&`,¹Øà0wzÅ£`ÔH6€ 7:é©U‹šA2·¿;lÏ@IDATbl®o¡i UÚ¨h iÀi€²üg°ÀÔ[]ÃéhB:n¨Ùd @ì©ûœ\eqýº´7ñÉqßxzºü•xúý…'Q)/,ືð/[¾f͘0€ú"Ø}R9c߯àiž4´‡MÌÕÌÖ¶fzy¸; $±þð¤p_µÐ« ®$¬_Ö³¬ss‡*V®r½ð ©¼»VßC¼,®+¶\/ä6Èã.·/.ÜnG?»½TçúÂÁv•¥òobHHÔ-‚´zýU¾Ì Ž™Ù䥇n¢?/,øÅ†?àÖ“$€2ÿžŸ¿Œ=‹‡ LãŸNý;…^wÜ+÷WùÞ¼5°rÎþn«Xæ=¥y£m¹V{£N«ëŸ˜~¹ÏÅôËýð’g6/{¸¹}<Ü$$çñöôÁ¨l`ðtw'€mq?Óà[‹ œ:\ìkË/£ ޝ ðUo•9®k0 nƒÛæömŸ–“=Jh[’â½[….´Ö<’ª¼?gÑÝñ˜”Þøß/Æ»&Uun_¿xëò½Î³•v§=ÇâŒèo\Jæ"}ôG׫”¯h@Ñ€}4à4@™»ÃÖDµrjêBçS®ÐÚÇèÉ™ãÄôµ}ºlÿR, ÙT6?4@Ñ#øuó ‹…뙄4ÁÕÌ€P¬lit”°+ÁÛ æ‰¶¾®°Ä¸kŒ¬cÖ·-±¶(××õâj‹²m—:ëqgk·“Û›Ulíj–óŽ?ûª»«®½Œ Èl˜[T"^’Rá󞜑#hæ¬õíH€Ìõðøóõã¿h¾ž®7îÖmSÖ[žÞ×ÌKE¯?­X„ž¿´õÀôíÿБêÁBµªÔÕƒÊ\¼õq~íÏêÕj?£Á Ÿ1øÔJ’÷7ö«Õò§šJݽ¥,/é2E¹æP™Ñ÷¿õ?Çõ~!Œ^¼È>¶ñûÏEP’qkÄ BýpØo¿]cU]±xáò¹ —¥¤fç}ðÖgÛ÷ÇìÝ-£úS»Öf/Úë,ÿ˜¡â(èù@ÿh„¥se¿,ã¿]Õþ¯½§yÇŠ(P4ÐT4pí¾‘Z.[ùaÎ@ÉVÖ¾´?.‹¾Û|ØÌAÛHÍ«Uµì“|ÏäÁtÉKØ’ìîꊩýVÂõ¢VÕóàï6¢ °)Dú"B`É –K777¡_Ö³¬óÊÕ]å£\?ËMˆ·åÍ€oe‘Û ;·ÏÔN€e¯JDû¹÷Á]¡©HŽm» m9pNL7ûÁ7edOá¯Ü}àë†ÇXçq=ñuUݸ7dÛ”ºœ_ǧ‡·+?þï/ÑÒ,­•®àÝðáë3ÖY¶]»öË?UOâ-ñ}«=?ÜÜqÆcÒ¬Žü/QÒàÁ´úò7åc`\ø¿N‡ï–¿Wþ\¾dþzðDoN5¤Îwð‹pÇð@@¬ahïΪ!=:‰Ù›Êç4äw¦mcP k㹤tƒV«Ãå¦J†Ùd¹‹‹ë?мÞíQêR4 hÀ>p ÌÝaë³L0PreVûÊÎ+¦q)D4sâ §¶,[ ÓÃñÒ–D—'⮣@LºczÔ ”õÊúe=³¾m‰=-Ê­¼Tpǃ©ˆ}Áö©VFÜâURyÜMítG»K¨¨¼Lôƒ6þѤƟ9±É‹}•+}±ÿèPoqÕdÜ+£|mÁ845ôöò2ÝÇP%e¤$íÀ ÷ý}¸„rÕòëê dx¡YpùÿѱÇ=Ò¬ïÌÑ®FF•°îÇÿawE³t¦cH›EDÇø”*¥‚'úÕ§5ï¾]®/¸;=;6üª²oup ¯>º}ku‚¤;¶ ®q®x9´·pì »“p&W0V€&4ǘ”–m()-~f n‹ƒÇ/ _÷á«ó·Á «ÂÞMQÊS4 h 4`ÿ»H=-OÁ3ó°ó€\—¶¤ÕjÁ.qId>»}L?‡ù,×£éNs*û¤²»[[¹–S¨— téAžÐ%ë“õÊúeËbU®ÅÃG}]/Š3XÎ*2Y“3ðî{5R¶5îÜÞ’´_KC¹ËœùNÿêjòøgòLB ›¸~j:îÕ—¬ìm ¸ðp¤Çå¬âÿƒçÐp0uY"= Þ«=¾TÒh®²R̯oª§ †OØû¬bó^µ*ôŽž³N—[FÖýôW3ZlƒËUJ7–YSÝz…ûÂJ³ò‰…ot×’nRvNᘜ¼¢±ûNÄUøcHF}ë UHŸŠiGÁ[,bB¼ðé‰WžÑbá8ù³\kcQøŠ…“å€úóJn¡>;¯ÐX\Zf~nâür¸œœ0>Œ¼Ñ] Æßÿ³xA’( ÿ–+©£eU(ŸŠš¬Ì¼3ô€oZ à\áªÀ@ÉÛPÈžÖ9ÌÜT¹”ˆ·øå?ìÙóúƒ0¿[dëãYvýTÕæÉe 0f‰à8WàÐ0×R ðTWèÑDöÀÁÂzeýÊSð•Ë,(NC މØË#ü ƒOåkñ½ƒŸ('æ‚‚­P®jÜËð°Òê´¢/"W*¥ËÈñ¡ŒÿµŠ¯<þœ 1º• Œpƒšûµ¥*[ZšŽM ë™]ò úÝKî;˜h’p¯¸·ÿO©{é¼ÙæçÆ7\&Œúo9åçÊq/7ÿ[Æý5ã*ŸãÄaºëµú%r!áK¢Ž±dà‘wÔðóƒ%óÎâP^þÍkO½üFAÇ}ˆÎ+*‰ÎÇþè΀ÃØ_3J#s݂Ϙ³A¥K_À’ˆ`¼D ëD•«t¶58£ÑŒk˜4²æ6)+Š 4¤äZCÖYe]&À¤8ƒE>Ê>ÞÞTˆHlj‹jo—JÉ-£„ä Ùàƒãü|<áVàT]©²öÜÁS€ùˆ*/ÄËDØ¥!@UF®¸X¸Œ°}}}ÈÛÇ[¬³^Y¿&æ ÙèciÕå¼xó— ßNæõú¬t TÓá4Ó³$1GO#Û_ý¬ªnÜ9r*T_Bže%”£UQ¼2þb8*?¿ …¸ë©]€;ùûùÖjÜë3¾Ê¹M_§†ÎÑoãFbÎJüÚÕsvÿµ‰¹×ëá¦7]‡ê ºµ¸‰èdÜYbÔ*ïIãþš{չƱc]òs?ÃíÊÃT¦t¸“_ÀÒë•_Óý¸—°iøtÅrÍi³5+¼T.Yê2—üpo7AÌjUjÀ|½ 竊] º\?×ÜΛWpM!ÊEŠZ”œ]ò4<±Ÿ*[@ÙíBª!X2QÇ…‚”¾ TO…ˆ¯.5Û•Ò"y’PƆÁò,7‰[H[yµÚÏâÒlPŠ™¬¶*I$%a8þZÐZm!|ž .è¹?>=ÕzòÄh²¥ØÍ ”M°3Hö÷÷%?€&?ËÐ'ëõÿÙ;ø(ªíŸ™ÝM²©Ô$J `A$!Ĉ ‘*ʳ=Ÿ½#6À®<ŸO|øöòì]T”6@Ä’„ ‚B $´ôͶ™ÿ9³¹›Mlv7³É¹ŸÏfú-ß;“ýÍÙsÏ%¾Çr»8Tî!”cRNT…&ïÝ\m*(£ï±£Ó‰úŽ 5`ÄjœÕ`AÝmQ àÀŸEÕTb¿7ŸÕâ Ek­=ˆ„f¬Ç)GײþKÅ÷sT÷º¿Wj-Öîö=¾‡àôäQaô ‚ë娹ýîï:sþú$°il¯ŽV¨ùÆ›À¸—®gÅf5>Mw〽7šRë%óB8ìöex®öóÞŸ»M¡a\pWe]@öÚŒò+ÊRAͨݴš ê5ÒªUµO`SJkÙ9¯Ïº…¦O¥ÏÞ–åÄW3&Ð^èN(»­‹(è\±9nñëSèša¤è 8%¯Ý±:Ñ.€Gú?³@vÞÞ5»‹Køû4÷úñV¬í·-¢‹ƒË%Ï›}{\p¼KŽyŒxYv…3aÄ òIŽˆ0kEÉbb´uÏUâXÖd*äHy¾»¬NѾ±('u¨‹¥\€åÆRSû½ÆZƒ/OÁ~‚-é÷ ûðm…’u½¦\5 â=¦Ün¬žžû¶å¾åÞìqf`gʪëw‹/>-íwwCx¥ÝÈÛí«dýõqOÑh¼¯6I&ùòôûÈ•á„iù¼Ð>N‡m%ž¨ùã¿£I A‘lÙÕðâÂŒŒ4‡êxDìGßÞGzgm ë/'&À˜€n èN()!ˆCÕP#„•£ÖZŠnÁÁ‚Ve͇E¹w‰e×¹ÌfœAN$²Ø6%E@8œÞoü‘ÿ…vzIY6ôî>:F%6åríJ”(b„fm•É·-ñÈÆeMFßTt· K²æzá¶&{åwÈC(wŽöE9‰Lžµ‰|”•Ùï¥5*¬Ýo¹Öe‡ÜARâÐ,ÛŒ„ãÜ©©}ï¾ÀËõ»—ÕáË‚€:eŠ!Ûò㣠8A‘ì~8Q¸¾5½ï ;š4¨î»y櫽æD@?‰Ñ/e©}ô…÷[·5Ä¢ff†æ—½‡åiþûZ›4~Ò<È9~”‹†ùð6`L Ðt)” ‰&~”„õŒ¡Q˜«Pô³­A¡\Cƒ½j­Ê4Ë]K¬‹ZA^ü)5Õ åŽbšœCÇáÒú'Ù¢]“_ü5ŒLx hnj\ÈB\gUtE !áF®ä§Lr¹ žÄõxé°§²,Ê=q0Ž/œ° Š*ÈÕ=^0<ë¨~ÿ,ÇNŠþ…ÇFÊ0q`úx×õ¥g޵ޮñîÔœ¾w_äåŠ?úÝ˪ðeAF`ãø¸¤œêÕïcµÏrW]’á…ë\±‘‹Ý»·òóQ]ª•ßâ9‰µçUƒd{áL{£Ê7¿xßøøkƒñ)«Âß¿®•NAãxåó1&À˜@ èV( 1@âN[G„ËbŠá}ГD²6ÐÏfÓ¦JUjý˜…5™·"•†Ô Ï®]:7«ÈQgÜK™‰‘Èeî •®€Aý®9aµÆd÷ MûL3Ú‘[½HP8²*ÓÀ=È®IF\–dby¬äDW²ª½ÚaI’ÑÂÝûX§6k? 2ìbyg™+²T!.Oêì6dÕË+ýþC¡ ¬è‚A/a·Ý<ÜCêUè81x½HÍí{q]s–¢ë#_õ{sêÀç/œññ×ÚTåylA”hØûA–Cþ>háîºÁâà1–KŸïm©9²­Ã'מbÃ{qrætÇO]R88õL§Sî>&É3zåäÔ †pà&À˜€þèV(*"ŒYi]X•É’lÃ]ä£LƒýÈ¢ŒƒD4?e!–ûphPŽ‹ïÚÌ"»Â…†©°&wŽvݾ²• šFC|ç'̇ØhüÁ“,ÊƬÊ#™^(HS°}W(8òa®«ç±2?\^€‡\o"{á5M·n+O±ŸÜ/„P.@÷‹c e:ߟý^‚qœ¿ÚSœ­Ò¥§†Â'{7xG·Üh~ßkÅ7û?ú½Ù•à ‚ŠÀŸ't®²;ÞÀ_Ü.Ç÷C;þ™5hðmÏ4Dz›ýZ÷ðâ²}‹ñßÄ -/Œ±Œ¿ÿ]‰"y…ÈÛsY”ž^ít¾‹û´@xÿ~“’“ûŠç9¼Î˜Ð3] eŽD m7ƒè‚¡ dÀçÉ®äzA)B9†B%Ô¦ÎjãÜ‹MXvî4Tü…û\Ƙõ;žƒ+“>ƒñøâxPr±qùo“XÆY¡4Á¬­£€ìšPt©WÓ¥C¿¦\Òäsúv’aU¡ëô?8aLʉ/u÷e¿?™U 6ôK6 q"Fã˜zn$ÎRèbyâÕ?#ÚÃ%Ý›¾¯Ÿ[Ó¶üÑïM+™Ï F9º©´Ù߯ºk~ÄZ$Ø&©†«Ò—ìË9QldÏ6/}¾OhIiÞW¸ïÚÿ‚0ØŒ|ÓØN×` Ï“k×-’sžÕÇu¾Tj ½¾‘Óx`L@·‚B(=$þH<‘›YMq6$·_2‰c!Å2Ô#<|miàœ7iâð§àíeãÐB^Vel(xFzø¸Y ÁDKñ!6bpŸØwÜLÜh“{O|§[µÝ'7aåÔXºÕ\c„6—{@_쨾ê÷ù[l°ápÈøBîÈOމ‚N^¸\ˆ:¢w‹;yÛ÷î š¸B<(‰þ¥eKû½‰EóiAD`÷”æýÛ\tI»Ã³Úx÷¼g™Þsþ‹çþ­g¿–n*)[ÿ9þsû\IºE2‰ðFSÞà´Q8ÒúvqïÕi=~ýuØæ%`L P0…@ q@ÉS ‹mí@€þ„yX#É/Ø›d6÷‚‹Îœ‹ºO»ü÷‚`@ß Ð3vðq³#" .bÛ›åþC¿»/‹ï|º{Ý+§u­óIÞ| ùaSEû¼í÷½8ˆð…õVM®>º1=2zyŒÆó¢‘8§‹;yÛ÷î š±âë~oFÑ|jÀ°oéÅûè!|ˆ©ÖûÑøºô…û–7· êgS Ë ?ÿmãĵx>|Ñ åÿÄvÃå¡¡C£8jHD‹7»¯’³sßkxo3&ÀôNÀã«^ïUm¼~B@5~Ôÿ{=%åÓÛtzŸ)°m×ÈÛû½–Åò_fÀã¿Aë¹wâ»ùõPë¹^øÚ¢Ü#ÚÑ¡”[U Ðlý¢{ƒ©¬›Sçæöûã«q’'~gc‡¥t2À´¡h©®{ÑhNÙâÜÚw5m³%}/òã%h -ì[ÍšTÅù8ŠZ÷|V`DŠ›Ólnþª:K^6÷‰w1¿KÜ×JÒ“ÍTžro7²Rê°þ…zOí$„Ðð[9w1&ÀtO ÎÁV÷Umṵ̂F,ÛÑ7\¿ÆFâLæ.>/»¿§U¹î-­ÈÇXá—=.+6½Ø<}^8˜Z(’[Z'¾ž ø’À¦±qÉÙÕ«×à,MO¢{„K$KP!Kòõ‹‹'g,òF$«Òò¹O¼†¿Ú]%ꊣEæ©<*¶[æg¤MÀk®ÇP¨ßÚgݺ±ÍK&À˜@0`¡¬£ÞŠ ƒ ÿÓ]£ì­oÃî’ßÜÛþ\ñt»èÖ„¨ÞÔ¥׺0¼q¿ð¦Lr¹xv];æƒÂ`@\]=¼É“¯az"3.þ:«¤Rüâ3E½ð}ð§I˜¾xÿ1}ˆÅ¹ÇZ.›cxgG½QGÁûJæýŠË?Lìl°Ü3dHgUU^»ñšS²s9ØOœÇK&À˜€^ °PÖYÏœ–| ¤$œç®Õô[v8jÜÛþZ)>\7/ÎÇùDO­sMÙŒ‘/‘ù¾ ªíhcÃD.SÊ•%­ã2Ú3ìñÝ»d‹ûRQ•·ÐÍA VˆÙŽV߇ÓǸ¨¸À[>KæÊspxôTq=~;sFýâ˜çÒê´½‚nq®}R‘Ñy§çq^gL€ Ê:ì±ÖpÁØw°n _·N¾È'ŸVO(7@ŸÈ§©Kv¹h*)>/ØäŒ§*ŽßÑÅ¡.6²$mÅ1ÃÒ—?%ÍŸïõ›è’Ùò,tá˜áf"IgÎ|ìF´»Þ8ÝꯠËÅÕXŸ)b/Æw¿¡÷ÚµGÄ6/™`ÁH€…²{-Ð.Šâ€ý‡ë„rS&<ñ[ÏÚ}tí‹ »Ë½þ.?añìrqBD|BÈž’“=>ö ¹«ïù¥8³i¹íZ:[¾c =.²ÀÀ: ŸûIšå R/4X’„n/Öí–^OÊZßìu×ó`L@X(룎ªE ]0ö¡Û…ÃérïèÙòu=ª>¾Ú‘Ö­Î?8k¯ÿ¬Êìrá«ã|ôB€&KåfUkDÐÕb†}˼¸djsc#‹<ÄrÙ\yº[<#¶1ïe½O½|äÈUÇ}P)Ú†Ói{cuj#‘ÑMc{d—Ø{E>¼dL€ 3Ê:î½@¹`ì.þÕM¡WÜP÷º?V2º× åì¢ã~ÿz]<»\xŽ/Ô!­NŠB_ä7pòeèÿ› ªˆ®…@Xob#‹<Äré\ÃMè6ᎋŒ–äïͱ½'÷ÿÛf›8çX˼üáµgiÇ%ÉFéÊø•+«Žu>ïgL€ Ê:î­@¹`ì.þÅM¡§Ÿ…òànîð®å¡Ì.î®ä•6@`ý¸¸Q•Jé(D=£O”`˜ÃÉöíªÓ—ìj±ðòÙ†«UEyE8‘µBÖ¤Ø #¯+<á(â‚ôt|³V¨1ƒÇS~ËÍÛ¼dL€ ;Ê:ïÁ†.K×M÷q ö”Ô}¯ù[(÷Ç}aµFå=å Ww|P³{‡].šŒ/Ð!WÇE /òËè÷û Šä^¢ŠèÖðNÒ?}qɱ¯%K w…SRÞÆ<´ï´$g¹ãØÑ3ŠOh.1"Ò©:q@U{¢ñÚ“ÇOšÝ’úðµL€ 0½`¡¬·i¤>ž.G* }:Iñ¡Í`µWh¥ÒD#"ÝßÉԤ廌xÇ¥Å×¹_dí³·<ÓÚØåÂg(9£V$°~|üpÛø}‘oÃ×H—•g·ÃéÛ/ËXR|™7“‡4Öœ¥s —)  o1ˆ¸£"£G_4ípycç7ÜWQ^ú"Õøïá†ÈóϹ½¬I®yi—àÀ¿ë<êq{òºÜۼʘhêK›hNÛm¹`üY¸òö~¯5ò‹ïž¾=#5PZQ 6›Ë2Û\G†ƒ¥•`±^¯åi­N‚í…Ëü±ÂªBj…+4Ü΀ÿìöî6 1¡<:ÅDÀ»;âÄ"Úœ ®‰E†ðÄ"~ïH.Àg²&t? ÇÛè'Ü·.Sé°,©ÓÒX·¯åkKŸ5\ª:•0'Í’Œ"y3˜#FœVq°)¹ï60Áfk0û^ÎÊ`L ÍðN¡´9 ÁÑ  2ž†=¥eéè§ Û 6A”9 ¢"à Ôä]WvŒJŽ.}©Ap:ýÛXPǪ¦Åã×smò¶Ì#åVØUt*,5`Â7„3 a°Û”Oœ3ÈŃÐ;‚kêyEò=(’Ýw-Z‘CHØÍé_îÜçË6,{Ö0EòÇø¸hÿ0Яx‹AŠ8oô´ÊM)ý¥¥üôÔwñúNt>úL£ÙpGS®ås˜`ÁHÀ;uŒ- ò:oÎÛ.ý—‡“zÇAÚI½àäÄ80‡†yËZ^}‹Õ[ ‹aý¶Ý`Úù'Ìÿr'ÇœýSý:ɲå…pLÀÇÖ‹vè å-Ÿ'‹¬ñN-“dùnt³xGìóÕrélÃ$Œnñ‰ɘïŸ!!áçweISË(Èt/^?ŠÎÇç ߪ OÉÉ)kêõ|`L ذP‚ûö×ÍðÅwÙß9n|.$%t ‚Z®Šô²vROíS°÷ |µz#¼òù0qx*ŒÒp*]M0®F\86Šha?O) NE‘ìaE†¦Ó¿Ü»çØW{wdÙlÃUR>C«µ+>£[Ã!ü¼‘wW75Çü!i‡ò”8­ÉO'çä¬Û¼dL€ ´E,”uÜ«ø% ßý¶>ÿ6NïÓ¦œŸ!&1@]ÇoŪÑKÄSFÀüosà«U¹€£4±l4غ܊ýÂE»à€½ 퇯ã@¸Þn&Tàï÷¡/òî}>\ÁÉDÆãs0îi"­ÖÉ&óy#ï©ÚßÔbvf¶Z-䇬ý„…"9+)*æŸM½žÏcL€ +Ê:í9ÉänñÅw9šH¾*sˆNkª¿jÑË„Æ Ç%.úqtE'ì}z‚ ý¸ÙCýÕj´yJN–ÛÇü\>Zl7ªæ‘ÞSÝ,ßg›Ý2CÁJõB¡]…’û*iÕñ§¶öU8&À˜@k`¡ÜšôS6E±øpÙÏš»Y’95Ÿq+9RŸãËFR·.aÖIJÁÀVùæÓä+¼%=6~JµÅþZtãD(J Þíëˆ"Zbt‹L´$«µV`Øj 9êÞê"ÏóN´ž—‘z‘ª¨î{èC}Wò¯¹ÛOtgL€ ´nÿ¸¶Ð˜¶Òrø>ëO8R^ ÏÈî^v,Y–' e•X•ý'Xm6ŒâÔÜ1¼Ì’/cM&°~BÏîÙcã¾ÂxÅŸa\ä:‘ ÒDzY:g×óiØ7ÏŠ-Ÿcƒ7úüa*”öãxÖ<0“H¶ìõ<ïDë;Î<3ëþ–8þ‚äìÜ7Å6/™`m[”uÖÃärát*°*g+F·ˆç{-ìòY&Ž¿l)„³Sû€ŒŠA¸_à—~ sçË™@ã²ÇÇݨ8­Ïb„ˆ3öȲt[ú¢âÅû|¾ºtŽñBEu.À²5‘Œä a#GO³ìina’µêMü—T+ò¥¢PCÈMÍ̓ÏgL€ 3¶(ë¬÷Èš\¸÷€f¥Hm98R %‡]Ógû³Ä±²Ú ù»‹k­Ê¶*ûx;Î;{RÏ´"® o‘Œ¯cª$ɯvè³ëùY$£»¨Î¯±l׌;„„¡H¾ÏÒlè¼ôÔÛQ$£îÔÚ`®éñÛo‡Úq÷rÓ™h‡X(ë¨Ó…5ysþú©T‹“Ü”ê-[÷Ì~wESNZ«·6y°{“òlêI;ö€›Ã}ú7¿m¿lvoûk…âMÏm;÷CMlv»fµ'Þœ˜€/¨S¦²ÇÇOW¶ß1¢ÅH<ÿ’ †‹÷ßÖ÷Ãåû}¾JÑ-p2tõp‹äarèÈ î²ìjna…g :¯yÖ}Ï¥då~ëÞæ&À˜@;!ÀBYGíÊN8„ÓJÓŒ{þ˜Lä¯]ŰtÝïo5 ô÷—ü åè/,ÒedÀ•£ý̓8F"Ï#8Õ·gñ³kBÙ‰®—,”E_ðÒ{9bOÏ®^ó Næ1o*smN ¡6»K×ðƒî[ã}îM»’fܵnàš€ P$5½fgÓr¨; EˆÃîüŸ­-ØŽMɱݪ;ƒ×˜`í‡û(먯É킦s.­¬Ö¦¥ö¦j[ öÁÏ›òáÔ¤xX½~;„†aØé)pÆiIP¸ï,ø>ªjlðÌ»Ëá¤^qpñÈ4Íýá‹ÖþeйCL‘½ãµj!}{Û\û•ADº<¢evTÆÉ Y¸èiɰpÍF8%12Ï: c>çÀöÝаݵ}2^óÖ¢uš5ù¯~Ô&yàš1°põ&Íâ’Qƒ´¦æâÌzßá ÆòÊè×&Ÿ—c"5A;û½0îìÓáûì­š[ÊœuoÌ™ý!,D {BTÑ8ÍwEu ÔXÑ¢ŒEÂB]ƒúÈ_™ð†M?}ø€åÕ©ÎDQ‚\“l¼!uaQ®7ù6÷ ÷7Å©|ˆ¯}Úÿs|Üvº[ŒòÂ'™Ê.(Øþ$FèH£uü%¦Æ(«WJË–Yi›`L ½`• £W]#œèGë€PŒùëM²Ù@V㜭»aìÙ !¶£&dÉå¡ct8$&t†è(3\v~œ90E+â‹ï×£p4Á “ÎÆpt1ÚDtã¾Â—«6Àðô~0ã£ñ» ?ŠßÁýÑ}Á;ö”hçžÞ7RÑ؈3à%Äv€«/ CkñÆ¿vÃ;ö€Åhj?—¿uæ™§ieSþ$\+-®ïßûÃ'+³ 7†q»ä¼A`±Úá­…ëPH»¬¾äKóƒNî£Ï8Uï[ò› –xÚ1â…§»&¡LœEÞTNL 9hâCªÿÀÙõÆ;´V$K5’,=˜‘qê@‰dœ–ú*§ª|äÉl3‚yøE^ŠäÂô#Ñ¿ú>Á‡¾ÎìµÑÿþQ¢@^2&ÀtF€…²N:„Ü„ë…/\F;'ÙHÐD%EÑÈÇ‚1G8&2BŒ²M#®S4T£&‘:´º'„@ú)½¡-ËGʪ4 4]Û·g,D‡‡A|§(°:ê|Œmv§fÕ3¬¿f&”ç¤õE±Û :¡(ë ÛP´ÓÒÝ»ºÿ÷@áÞØܹ[wA–?­ËûõÐ,ÑÑe'ZÁEꟜgìC°® ];h/âXS–$ŒmZˆ8‡f¹Ì›r-ŸÃˆÀæ)‰ñYãâ>ƉCV 8u½iâ~´¼þh 1ÌXTüŒ4+0q,›c¸CϽ‡o°Z`p´$o ~áýÍ‹“,z¶(=½‹C•ÞÇv‰ï…åÉ9¹/ˆã¼dL€ ´GÞ™-Û#©´Y¸^øB(÷@Ë.¥³+BY€Ky8ÀŽÊûì›÷aòéÝw¸zÅu3º6,YûtC¡[Pt®Ÿp–ûÙ¹ËÄ6/™`í™@ÓÌŽí™P€ÛÞP@úºøS“»á@¸ðô;õg¬»~™puæPøô=ƒ‘%(ué 'ýýBtOP|Š¿Æ¨$rÉÇ—¬ÎwL¡×ðϰ)ðé7ÙðØk µëÎIí ägL)<,† H†Ek6Á׫7Âã7jó¸³è‡8ÆãtÝ«²·á9µ‡WŽªY¦}É„òwá¼ÂÈ7Ñv^ÀkîY0Lš½+žŽNŠ~¦ï ;-É3U#ªŠäÏÖqÌÈiè'åeÊKOQUÇ'øPh1Ï_“TÃÃ^fÇ—1&ÀÚÊm K)’}(Qt a‚¶)Åì;'Óª–(žðԿЬºé‚ ®›qõ…¾Í®m‹kóQ<“™\.(jÄáò*xöƒo`û®’£Ê¡ »w†û¯­…#׋†iâðXÏþ¸[Ò\9þ1öŒz§œQ8èSƒ/DÝèéžm }׎F NLÀ§~ŸØ½§Õá|#?L¬—±$}gÓmi‹÷l(©w(KçÈ¡Hþ·»,<­v{ö´ƒ.%÷æ®8ßÀwÇ$º _Je\.ý’ãúGÐܬø|&À˜@$p´šiƒ †&ùÒjÚ”öšÑÂÛ0 ì¹ßŠƒ)®±SqÂyE(Zº¢ûÆñRc"YœÒ„°wž"Y\ç¯e ¹û«œoËÐÌz9–ÕwYŽ¢SûÇ—´ Ô{Óز¼¿zÉlyÞ§»sà£;þì™ÅUî}^¬ä¥§ÝоÎSÜ—ÊÒI¿äº·y… 0&À\ꙃ>èÍ% CÌ­ÁzߘfÖ‹ë·L>W›DÄš_ ´ž¹]/X$7Ÿ_[¼"{bÜÐìêÕ¯bÛREû´m ½¢†>pú’]GÄþ@/Ñ’ü,Þ§÷‰r±^ßt0&L<óÞ=uS\ŠƒÍXætºêp>'.Á‚—S²s¿Û¼dL€ 0¶(ópLÐíbú sbm‘ÀŸ't®¶9þ3ëÝ„íslF„ß Fíi_­k­v«ê,yÙœ'^E‘LuÓŠäeæØÞ“ϼ®°Fìóf¹ÿ #ªŠ/at=¶wcrl·{6x“_ØhÓÜ_mº•Ü8&À˜@-Љœ5>ö–J»cF{¸£ö­ªÕ É÷§>ePkŠä~a\:÷‰pØiH–à‹ÞI§NÙB‘Lª•PÔŒ“kqTJ²ñ2ž¢º–/˜` °E¹ÞdL íÈwFNöË/a„ÀAè„ãn(ŠäÅrˆ¡–¬Õó±jãEåÐâûnfâ%7H›ïû¼]â¤"WcˆÄkÄõ²,ßžœ¡f81&À˜@£X(7Š…w2&Жl¼8.ƒº<ƒÑ,®EyŒ^ "Iù²wã`½EbOk-WÌ‹pÖä}ÖÞQ¢(’_̜霆B¾NÕ‹ƒÍ\æ ÒOqX_—a¼ów“³sßÛ¼dL€ 0£ °ëÅÑLxÏ1PŒ8åõ±ÒÚ; §¹æÄôB€¢Yd›f·©á(ÎëÜ"Y’,¨–ëÒÕÜ_"ù‡ç:tpª%ßxŠd¤§/º_¹Ó"YÍÌ ‡í3ì-¢F¯ÙÞ9ö½ô׃ 0& WlQÖkÏø¹^{KJáùO¿×JÁYÆ 2,úâìx—ž7h»±D³í}¼" þyóx0 GB³é =-I‹§|ÔAÞÁL`ý„nçæT¯y}}xÂsºYÜ£¹Yxh¥õs#c«íe+ÐÝÂu­½fÎTžñU•òKöÍCÚÈ\É5’ÑpYüÊ•- /竺q>L€ 0=`¡¬çÞ @Ý®=:Å„ÃÎýG´ñúöŒ…´“Ü“‘Õ«Á€”8馸FEr½yƒ ´"õzvWÛ\§Óye½jH°M’ŒÓ2­¬·¿7–>oîᬩúEòIT ±ª¤JSQ$»]$ZZ½¼Áƒ&«NçíuùÈ÷$ÿº~SÝ6¯1&À˜À±°P>™v²¿ N’еôŒë?nØ;÷Ò„ò–‚}µ¹-Äɰ§”>%1RzvÕÖgàôÖœñ/Ý0¬Ê…*‹ ŸÚ[›½OÄ&¶¡›ÆÊ_¶ÀùE`Á™öÌ4 ª€Û/®­ÓTÖäïÅ™ pÖÀ>0|PßvBœ›é/Ù7§›¤¢½w+Šõ1tað˜4*1°Å¿¤î Ïe¼®ŸYç–Î MÁ)2¿EwD‰8XO¾>ó~ç{¾bTpFj¢bSÞù¡5ýó”œ\Š͉ 0&Àš@€…r µåSHìÿ.NKéÔ™´å{$Ú–5!ö(Úó¥`ð 1ŠÓ9]\€4LÎÞð›Øæ%`L€ 4 å¦q èYÑá¡Pi©Aß^úó†¤lš®ºw|'ôQ>\¯<ò!n,‘˜ÒatÅH@aït*PYU7³îi)Ýáû¬­°}w FÁè·gœ¤YrÑÊFY†a§'Cæ™§5–µÏ÷ÇÊšènöyÞœa` Ьzë³_½ÆR}àßn ™æ×{/T‚û,,.†ÅÅ­XJ[ú¬áRPœ`MCét¬ó!ƒl=zº=§ —7ù”]g¥u·YòsÖÞ ñepiRöúy¤Ê91&À˜@ópåæñòûÙdáìßC¾¢‹B¡ÿ¿ì _ñž’#šåwKÁ~èŽûš’âÑ­"ý¹Û¬Ñ?nØvŽEªMJ®* ÔKF¡lF1-RѹÛvCŠh²0ï?T®¹qˆã¾^GâÙ)¤‰ubÌ–d_Sö~î-;ûålEUÞòÉØ™YèUsVÆ¢’k,,ñÿCãES—ΑoSʧx ‘¼Çb:Ç×"™^$l5ê‡øÞÐÕUMi¯ ×àýŽÈ81&À˜@s °E¹¹Äüx¾p±"!£D˜,_·.z*D˜CášqÃ4wœW8';¡vû#¿m;‹!!Ê qÅASÚ’ÊÌýQ.çÙrÛ¯ê]^Vþ°M…»„»åŠýV­‚4$yΠEEÕ~»yZØU%/}ö‰pÚìºÆü9öü©‡Z˜ýQ—ç N…?Ñ–¥ù%cYûdÙt¥4k–"Êç%`L€ xG PB™¾ò­‘È’L"yÓŽ½Ð5ÔqÐah(~P$WâKœ‰7'8}2ny~<úwuÃÅ ýìvdzNUïYz“ïD郖â¹_¯ëKŸìZVshRLuDáúßÌÎ{pIl}žòTef*â%;$U¾,)+K?³«ø¼Åœ!`L p!”úrp8–»ö³RñèkázAB. …]˜9 úuïv»'䨫Í|7ixª_|–=ªÔ«ä“LîdIîl²Al¸Y†YOâJ|ɢ̮u]¾â*–kèÊpÔ³[w…oÖr'%vp8-;lö;0G“ÈÝq*UIz¦sgó’Þ)¬‹;(NÐárÙsa‰JMÕJÉ}ÝÕ“¥‡.š¡< 3ýó¯./#í UQ¦ŠòP‹ÏLÎYÿ£Øæ%`L€ ´Œ@ „òQ5´×X”–U±Pn@-Nš€3¡« »ˆˆp°ÖX¡O|4„È¥PXZ¡M rRï8Œ†Ñ '‰ XœåUÕÕ&ÅI¦pÝ‚î™ÐoªfC-G3D"KâI\‰¯p½ÐUCZ±2¥åUªÝf«DÛõÁ CBpÂÛË#8Ú³³Päø [²ô.„„=<øËûüXŸf½d^ÈÕf¥™õ\SRKà”%ù´$¿éÓ‚<2Û94íT‡]yCìÂÿŸ§doxNló’ 0&ÀZN B™¾ µ¥ºê`…Å*U£¤ˆœ\\BYF÷ ´(#—Ȉ°¢´¡E¹;F’ˆ0bÌãR+äï.Ö! ‚‹Äó¢#ÍèVÈ®ÔGQø¹òJ N&bE­8‘ @Ù M ºX!'!†QQ‘¡­WâK ‰7'zéy´Y,ôs½xNý‚äI9⯰TÄ CÔD­4Q’$­6ʆ{RåŠ]Á°\>ÇxŽÓa_ˆà´ äxWÕ H¾|Ì ç×þªÿ³ÎŠ*¯©üËŒ 2ðVÞq½¿Êã|™`í•@ Ô•øò¥¥²{û¶ I)—‘;ÁÐÓ’Û+ûFÛMî4˜üiÉJnø“8(81%x±8k_E*í6«×T¹õ Æö’ÈåÓˆ-Á¥Ùà3ÞÍd) Õ,Ç$’cb¢ :?$–‘'q%¾ìvQw—ÐsHvOþŽ,Üëù¬Ö䃵¬ ±d›%¤yf‡Â2O2f¦/Ü÷¥çþ`X_6Ç0EQïa]µi´±-exN=ݱƟõ/¯©zŸõ“¨ ,³ _/éúÓOþ,“ófL€ ´GÊž_¼4u›ó÷¬Ÿw 5º,ks~4 eü?ÏIp[•QÐQ˜5ÉBüºD´LFŠÞPÑv8MD“¦‰=Ú["^ÄGùkØ„7È'9"¬Y’I$wˆ‰ÑÖÉí‚¢^°5¹þ]‚Ï¡Š~®¥Ùk¾ûÐ3êùÌÖ?Ù‹­ º§9œÎÙ¾ìÏ˱ï¢Êû—9¬Ë«ýço¶y †õ%så{ܳKû†öI²iÌèé¶Mþ¬^ÆÀ{06ów²|Sï¬ÜÍîm^aL€ 0Ÿ„P¦Ê’9TɸtÐg׎í+pò‡¿mßµúöŠÇ]œ!ˆCÕP#\â—¼4k)º P Z•­V+Zœ8Ç€Ë⬠w'¥ö ˜…ÛE® ^_ P$ÄšO2º[%Ys½p[“yŸ¸ÏhIÏß;öHE; ÈM€žMñœ¶8´Xî¤øD§C}ï^)Ä$•‰}W?<5§ï‡;Êt9ë4UµÑD‰,›ûÄÿ¡X½ÓõÄái8Ûžd Í̼§¦°Ñ‹|´3/=ýlUuÎq½ËP¦ò‹)Ù¹û({Ά 0&À„P¦ïúÐ0}“åÈöÓÒ…+{¥ô¹ðãå¿D?pÝx9XžaÝ’Hü‘ð£$¬¦4›…¢Ÿm åšZ¡LVe§SÑ,ËíA$‹\ÈBLî.k²+Z¹Yëù)Ó‡\.è8qåä"@aôðùCÏgÙ‹¾ú ÷Ò³)IJxn›ëÏ‹:WÛ£H¾ïÇw8À TéM¨7+]¨\™Ú±n^ó²¹ÿü¼™äÑ®£#£'žs{Ù÷>?¬ä§:¬Ÿá›pí?ø%ä{ýPgɘ`µ!”©(²N ‘lÅukuuEåúŸV½'?ê{‹Öª7NÎ.Dª6 HâN[G“2­“P7›5‘¬ ô³ÙÐý-ʵ~ÌB(·e/ ²®SŒhZjšqÜ*ˆ…€#«2 Ü#ìšdÄeI¦k8¹ÐsWt 6ýúÓ+¥•¸—žM!–›mQÞ=¥‡¹Øb¿»Êf¿UvŒöz\ ¹’áÁŒÅE[kwÝ‚b$©Ù»Û5TTÛõ)„%_sÎí;ˆßÍV˜—·ýS´9¸¢j€t Ì:EúõW©À‰ 0&ÀüE B™,Êô%L1Q-ôÉ]»:·c×X²dM†/AýÇø³%¶,#ÚD¢N„1ÓÜ P «2¹\ØlvÍG™ü˜É¢ŒQ4· !–E>mqIl´þæMeâDVeŠ‘L/$ŽMš•Ùá‚-ÉuwY’I$gÿY íüëÏûá›_ñ(=“ôlÒ3JÏj“…2‰¸ìš5×[lÿÄ´¼Öðµä'ƒÑ83íë¢uîA¸²rNh_GMM$’⮾,ÍÍœî¼ïC²¾û5lEòp*™*`®ìñë¯{üZ(gΘ`4 0‰¾HèË—¬ô…\…%úý‚ùËñˇ~Ÿ°ïÐõòÑgÈ쳌4<‰<…ÚÌ}$ÑCs·À|.‘ìð'"c´¡Lˆ\l\þÛ$– ÈŠ³¶ŽZ°óÀÙ®WÉ'™Ü-È’\°uógß~ñ ½¨’5™žIz6é¥gµIâ¯ïè±Ë¼@IDAT¡mqÊi¼¦žêy¾Æü ²úPÆ¢Ê?¨ÓŠÿÏt8lþ­³ÖŠ‘¬JÓ2g(/à ÿÿB±cÐÀ‹qüÁ Ù>šœ•û­Øæ%`L€ ø@ …²°(Ó3P&ÿEã÷_Í_~¨xÿþŒsG^ñŸ÷—ǜ֧‡:¸²4 OÇY®í{Ê$þHø‘›YMiðžˆvAâXd±¬½´M.ˆ%Zбƒûľ6Ùøf6Šâ$S8ŠnA÷‡½"÷§5ï¬_»Š,ÉRŒžGúxZ”=u/ªŸÖOèvî­Ÿ¤v²ˆñ<»£{dÖ ó¹oIóç“àê´dŽá§Ãù¶Q ÿ†©–0FræLç¢@4,ohj_Õïàí‡|'eç>7} Šç2˜`íž@ „2~ÊäËGÖ+š®Ö€mtÕÆŸ\ÿ׆õÛ‡¾h¤Óé8¿Ð£Qì¨QæPµCL„f2éò›a6@¤yï/«[6Å ·V‘V(ˆÄ°gj¸íyÌë§zdÚ}ïQ|£«4M<Í€I“‰à}!ásU±ó¯­ßÿ¼|ÉÊêêÊR¼¨¬öƒ‘'´g’žMKpL·‹¬qñCpò¼á/¢HÆSÝ©d £1žK_T„SaÏwÖ•¥så»1lÞP¢jÿ£ðv+‘dã¸ÌévŠ7í÷T”žnq8¾@Û~´V˜ùr”t5ý_ô{á\`L€ h)”éŸ;Y˜ÈªL_<$’…ÒÑD´ÅReGëòÜ¿²ß€´ÄÞýN>ÙÙ)ÔlŽ1ŒdÖ],*µ~ÓŸùb½=,;ÖXÜm?fnWm§þ=µ—¾ûòÕX-–rKUå¡]mÛ²mÓzê#²Ó‹*Y’I(“`¦ur»þÉG ±œ ±§+Šô/P• žUYVdU}ê=™±¨è æô©6üÛ< ÿv—h Šäm)4sôôš±ÏßËjÕñ*–1€ÊÁòk ’éÒÄUÙÔ_œ˜`L @„Põº¸›ù×*ºøõ'AË&$ÈdM¦Ÿ2#ð…²˜DÖ~̸$QìiqÖ,:¸¯ÅõÅ<|𲯯¾(2¼¤dªXoËY=ÜmŸµ{o»j;õï’Ù’»ýcïWõÔ~¡eé”>b|YŒI“›}È’L"™„3 h៌«®”=¾ûÉ·wš¤ÿ†™Ö=è§[“| «û™…sæÎ&Îö効qNµäôt˜$Ú‚^+›¢&޹·ü°Øçïe^zÚ­8™É+¢I–oÀxÉo‰m^2&À˜@`Ò¢,ZD_Üô…L‰¾ÐÅ6}‰Ó—5 eÑ$”©~Â=£îKwê4ù5ŽªÞÚ>Ó©]µÝ£ÝbUoíωdr§ gŽž/Ê$ŒI(£‹„ö¡gŽÓ³¨¥ãã’ì >®ªŽ¿ãSjÊ-›^Eú½äÿ&iìâü¶°\ú¼¹‡ÓRBƒöÜSl£›Ã|s×^ÿy]!± HÊœ:Xu*ÿ…!ó7Y$ ¼dL€ –@keñN_ÌžëôeM_ÜZ4 \jýpÙP(ëY0×sÚĺ·éÔ £Kx¤vÕvv‹U½´_hZñl ¡Lnâe”Ä2 ?Ú¦ýÂ/YÝ0®GNŒþˆ]QnÀ èeÕP4~eRáÑKŠÿ 7§ºýÊòy¦!Š¥æ+ls7Ñlï³™3œ3é¼gÈÎV§ís¬ƒk¢ ÖËQõôk…ÀÃK&À˜@» POé°Åô%N_àž_æ$œé‹›¾œ=E2 eÇ⃫ºMûu[3?T¬+†©óHíªíí«zj?=WâCÏ™§X¦çŒÄ1-éCÇ”ÇÅÚð ]±ßŠ—†á>Ï´#U?’¾x_Žçζ²¾tŽá2§Ãñ6¶‡~Í"`;F¶¸ Eò›03pïåê¬YrÞ¢bÏõÒêpÄ`E¿äU³fS¹œ˜`L Ž@k eQú©Wˆe²jÑ8ՉıøÐ7•n}”±nžéçF[_ïˆaê$šÅGÝ:á¤ÈJõÈ=6¥æ^¸ãÙŒƒü+>| Z\ü­çþ¶´þÃÛ‰a–»ÞÁXä—¹Û%ÁVü1kÜE3­yî}Z)HO¤¨ê#¢8´hÿ;9'—"qbL€ 0V$ ¡,šïù…Nû<²ØçêuI¾Ÿí&…âoÔ©]µÝ£ÝbUoíb™ê'ž-Èž’Õ•Ó*œ¥wãþN¢òµË 8aˣ鋊7Øß¦6˜_]²óklÔÑ0¼•Wšb:üí‚[ŽPȼ€¦CÓNµ;”÷°PíBKþʤñ‡œÜ€Öƒ cL€ 0£ èI(7¬û˽áo“e¼Ý$C}¡Ü®ÚÞH'ëºýèbÑѶ»ÁR9 ¬.íìjZÿDö8ú Èk0ôû®•sL©µšfÕë! Ãö¿˜™xÉÝÒß?“`ÁˆÔör•D{”VIÊ3š#/—fÍ"ë?'&À˜hezÊ­Œ†‹gÁOàÏ‹:W:÷Z•Šœ­Ù–k›…ï9;dÉðDZú-j¬þ‹Oð7¾A –Î6Lˆ4µæ‹æ[Šä»2ïW^n™µÁ{ |ŒÕìS[ÕJŒ“z¯]«·Pƒ Hò&`L ý`¡Ü~úš[ÚŽP ‡]½¯Ên¿Åqd½¦k³ÌþvöGÒ|´¢.šUïp[ÜX6G~@å)ôÇvù IPŠ/ SÆÌp´švþ¢¯ŸBÖcˆ7V £U®IÉÊÖBïµÅ>à61&À‚‘ å`ì5®38ÍSã-–šv»Š3»A¸çih=Ý¢ÊðdFúmŸº~ÚŸïy¸M®¯›×Ã\ê(zÊ]%ˆ¢t‡¤†Œ3ÓºMì ô²`ð ËœNçý¢\mð^Öú/Å6/™`L@X(룸L E\…ØgVW[nFãd½8Èèw¼ gÓ{ÒíƒÜ,Èó»gÃz—:ö~‰/ ƒ\ô.Y%£. ätÔ¢l±Ì’6Pq*o‰m¬Óâ¤ìõQgNL€ 0& /,”õÕ\&Ð,¿OìÞÓêp<àPí8“žêy1ê®õ(½ž´hÿWÚ ½v$Ä–Î5žW£X?E‡†.‚ 2x=6&mjÆ-94ÑJ«¤¢ôô.‡gT5k?vÉÖư«Úú ÊVÍ…2&À|@€…² rL Ðr'Å'¢òƒV§óZœ†3YR˜Ú$IY²O¸Ã¼µ#LÐùEuÎ%¯ßZ"6Y’ïÌœé| §vWàêˆƼŠÒϰ¯©tÇeÞëüë¯zŠÁx0\"`L@ÇX(ë¸s¸jL !œ‹»¢Úœ3våïẍnuI’~–eù‰ô…û–×íl?kÇðGÞ'ÉÚL{?·6‰üŠÒgAUGR=ÐÒOáß®JÎÎn5?éÖæÁå3&À‚ å`è%®c»'3>î Å÷£HžˆÚ¸ž3+Œ”U鉶<“Þ‰nÍÙ¾w²Iç"—Ÿe“ù’1÷TïûZk¹#=õUUïåK²ôXröžyOá%`L@§X(ë´c¸ZL€äLè6FQ”pªåá ‰àO÷ßc´³e,Þ¿ªá±ö´íöG†úþȽO¹³ÿß6ÛZ›EÞàÔÁ ¨¯ ã?öÛç(’ÿÝÚõâò™`LàÄX(Ÿ˜ŸÁJ@2Å[½fŠÔû§3Õ³p4%“Ay¡$Ã3èƒü‹ç±ö¸Þ˜?²$ËS/šá|`s«#É:4N±[`E´H$håþ=¢sìµ­^1®`L€ 4‰ å&aⓘ€ÿ \›vè`õµÙÕkf Nö,²døH2f§/؇SN·ï”ýZ÷ð’²ýùJAEh‘$.Íœáhudª“š™šW¼E²š@ÛX¿ÃF9dRüÊ•U´Í‰ 0&ÀôO€…²þûˆkØÆ dOIŽKÕíZîB{qœf4®m3NR…“„¼*æ øºhwGѤæ-›zRqپϑÕi¬CäKõà,ê”W\„VmFÛènáYº¬wVV¾8ÎK&À˜Ð?Êúï#®a%@³èÕXªïQª+oÅ&F×k¦$’A}!Üd|ñ”{Õ;ÖŽ7–Î1\¦ª¶ÿ¡H®›–[’^KLˆƒ÷®Û¸¼/%+·Õ¦Ëö¨¯2&À˜@3°Pn,>• ø‚@΄}§}†¥Úr :7˜$DÚ…eÌÉðFú¢¢j_”×òØüYÿ]…ÎSTåw{$°€$ß1v†óm=ø#‹z¤§NB—ÁzÒë)9þ+Žó’ 0&À‚‡ åàé+®iÈØýL‡Óq¯ªØ.ƦÈ" 5 ],¶àß9Ð=ᣌ×[oæ8="^ö\XâÎÂ-Ÿax¼ÁîúI°]–LèlÛäÞ§ƒ•‚Á©©ŠSý€F\jÕ‘¤RÀ0UUã*0&À˜€X({/aM%@,rªœŒ~Ç÷9Ž¡t§@FçÕŸ *<“¶xÿ"ôcÅCû›šu»8oÉÃ8Åf}ÛQ49ÍV;Ýpö̃bŸ–ƒÇ;ÛB¬K„«>ÒŽÐó¥ÒÏ?·Ú”ÙzàÂu`L€ 3ÊÁÜ{\wÝØ~UŸèÒòŠrª×Ü¥‚Úû¨ŠJ°Ì ž´pßí†DàTG@ýlŠaÙÎ/þ ª2Óm°¡Hž~ÑL倃u'ë`­`Ĉ0¥âÈWøÔ“ªƒ¿”Ê’a|ÏŸ>¬ƒêq˜`LÀK,”½Ç—1ƬŸÜ­·bS§•––߈ǣ=­Ç(ž¬¸ï£,=—ºhÿæÆ®ç}ËŸ ï¶´àóOʽ±âÛ—ÊÆ¨«ÇÜ[®KëìŽô`„ xÎêÝÉÙWŠúó’ 0&À‚— åàí;®¹Ž¨õ­:&dm€ž Ïuîlþ éÂUWwUùá‡Æê¬5ÿRTçL|ѵ JàDîG3§;Ÿqùoë®Ú—‘v (Ê¢Û±ž¯àôÔ/ꯦ\#&À˜ð† eo¨ñ5íšÀæ)ý#-–ƒ7ñŒÃÒ·Yš—¶pßr½ <=uà·Ï…%Wÿ¶úc¬Ów½$Ø>¾Wà,{«`†>ý· ÓÓ9Uç{‡kù]rT‡iî6ð `L€ =ÊAß…Ü€@Ðü­Î©–ê7¡8Šñ,± Tõ#0Jó2¾.þ];Æô<5º¾t¶á*«Ýú Œ' ¶•!!áÿ8ÿîªb±OoKÉݪ#\¨áT7¬ó_FsÔiÕ*‡ÞêÊõaL€ 0ï °Pöž_ÙN¬7JQáNô?‡M®ç,˜ÍæÞýçrl7ä˵³»D•ɇ_Råjxy6tWy(s†sžž-ñ»‡ 3Û¬–¯Q$'P3Ñ’| ¡ã{¯]{äÍæÃL€ 0&dX(Y‡quC@s¯¨9ôPÔ©NU=E+U8¢Ò†ÛðïIÚ~üÃ"Y8ñrÙ³¦ÁåÊ¡Aq6ŠÍ¿dÉxŘ™öõp¿>]-¨®8hOÊÏHCw Õ5ù‰$9@–¦¤üöÛ_¢-¼dL€ 0¶CÀ5h¦í´‡[ÂZD`ÃÅ ý²ÇÅý·ºúÀ^´v¾„‚È%’1W”o*Z<¿‘ ò¸ŒEÅîý-*°]L"séy¦ª8~ÂPjn‘Œ~ ïäØAšHÖ9¼7Ûq©¨&Þw¦då~'¶yɘ`m‹[”ÛVrk¼  Îš%¯Ïyå"@S6û…bp–GVå(ˆÞ5 / \XD–dÍ)uÉlýZ>=ꮋU-6ò\ù=t³8_Té•I²|+ºZ`Ìdݺ#‹êB^zÚmªªL;$Yú¿”ìÜWÅ6/™`L í`¡Üöú”[ÔD›Æöêhƒšë³³^¹ÅÉ /Cqü'úʾ!ǼwòÂmºš.¹a]õ¼½ôYÃ¥N»åÉ]D=qðÛÏ’)ôÊÌ{j Å>=/óÒSÇâ=‚3ºÞ ’ÇMº²7ˆ]¼dL€ 06H€…rìTnÒñ àä 0Bï6°^….µ ÎùŽÛ“TxaÐ’âo]9ñ½ãmüè/Çt¬¨¬xAu*Wyœ¡ «ÅSæÁçþsäÈàˆá çøïm 'ŠäßBBÌWI³f)íâU&À˜hƒX(·ÁNå&M@5˜½u¨Ê89ˆçÔȵ'K‡eIýŸl”_IûjáÑ9ðžæXö¬qtyeù›ø¢E†¨½v—$þqÑLÇj€UÍÉ®ÕÎÍKOïåçbÉT |‘*PCÃÇ÷\·ÎÒj•â‚™`L `X( 5Ôr'Å'*võÆì¬-7`ùñÔaƒ¤º†›>î9‹ŸF5g׊¹qNõÀ\Åé¼­Þu8`O ëp×EÓ—×Û¯ã É+Û±uèFÕDŸê#2/JZ·îX3Íè¸5\5&À˜ð† eo¨ñ5º& N™bÈ®Y3cyÝât(£ÑX/º »*I_‚lxað¢Ÿtݘ ªÜÒ9ƳœjÉ»õ"Zà(=ä›3gÒ䇃¦5jzº)_u|÷Îiµ•¶a´“IIY9[ƒ¦\Q&À˜h1Ê-FÈè…Àï»÷´:”s,«o?ù£Ðq'üÙ|¯ òÿ $ôµÁ_îÜç>À+-"°ù³þ!; ÿü—ª:)"„û¥ya–#o9½â`‹ h…‹óÁù:Þ;£¨h|±B·uéºä¬Ü5­P.’ 0&ÀZ‘ åV„ÏE·œY×׬ÉD+æ-V§ã"Íz졎]ƒó` ×ÒÃÎ],ÍŸïly©œƒ ÐŰ;bgÁ–lD>@ìCeY*Kòöí€à ‚aàÃ0p׊ö`ô“G’s6|$¶yɘ`í‡ åöÓ×mª¥ÆõHpH¶²-knD‹ž4n?Θö–Á ½Q78o~#§ñ.o,}¾OhAùâÄDSV/Éhtu%|1Y aa7dN³ìû‚i‰³î]­(Ê?E±=o¢H~Jló’ 0&ÀÚÊí«¿ƒºµ41HnΫ£E½Å¶q82†ëª3k?‘ƒô­,˯¥¥Ÿôµ4+8Â[§`D‹³Õš¼ÿ%šòz‹º£ ¬’TifæLåe€àY‘:©(ÿ«k“´29ªÃ­b›—L€ 0&Ðþ°Pn}t-Þ89¡‡Ýæ¸6'ë•pJéD­uú½*¤ÔËo‡¼~ú’â|íø"vAöuG¯Ý%ª\:<[Uœ·¢«‹§yµl ¹q̽Ö¾.3PùíšvªÃ®.Àòð6"¹´©ƒ1tŠ´Š_¶Õ\`L@X(ë±W¸N°ýÎ>¡å…•ñgðëì4­4ŽÂóÄ¢Y%éɯ©Ýd¼žc÷<Îë¾%°|®al¹rèUì‡"g§jrüe™ߣËGb?x¼ºˆ3‚c¹wðàž‡mÖ¶ƒ«ÆRQ¨)tlç_ šPvÁAškɘ>,”ƒ¯ÏÚts'%¤:íŽëKó+p67µ5Å™;¡ ;ˆ;Þ1H!¯§-Þ³Ýu€gÎsòñÊÒç#»ª5ÕÿE—„+<³FW‹…?TÝÙµJé` f‘¼{ذN«…D²ë@‚ ƒ c{üúëÏöò:`L€ ´O,”Ûg¿ëªÕ›§ôèTmq\  \ï°ÛÓVE™ªôš”ߊJŠZÐ÷…Ö†çð¶ï ,m¸J­©ú?tké"rǾ(¡ˆcf8?»ù‘«Äþ`\¥§‡W[-‹ñUì”ÚúÛ@5`¬äõ‚±=\g&À˜ð=ʾgÊ96 Ì[ŸõÚФ\g©¶OB±zÔe’T€¿è¿`|'cqÑ.×ñâ£Nã¾%°ä?¡ýÀi{ C¤ï1V’|Áß“‘÷Œ¹·:‚–þJXÎ…’ª¾¥€”Ыb—šrx›Ô«¢B<÷„Õ »¢’ ¯ÓIꮨ^PM)²Ùí7½3÷ÉØ$€ŽV´Íè(ŒZ1HRœÇY?®ÀËâ½T’¾CëöûæðÎ_ôŸ¿¹²Ñs¼ÛI÷¨|íŒGF‡„š0¤—Ü=Ö´CíiÜ(Å·AˆäÖƒÞåÞ®²©aPì8 v;ª%ö>’,©{U®Ÿûîj°äß ’úF‰v7•¢Y€ô†YŽxtäôŠƒîýÍ\ Ô³ßÌj¹Oß=l`‚Í*­UAMÔvJÒ!ÎNÊÉÙê>‰W˜`L€ ‡@PX”ñ ùU•æv¬9gí^ ݪŠZ,ðÃ$èÑËBß#[é#í‹èk{Œˆ;bî¼èú{à­gžø?lùa6ËbˆâødŒp9¨ÊªâèטÒ6ØMZcSµúeÿ~¾ œO§“&+±Ûq·Á`z&J.VO] ]Œ;¹ÿ]°µ¿ô²ÐÓ´‘>ÒASoØT3>¾B‰]¶­´{i¿Ð¼Nž¯In…l4Ý7æ^F©ðÈ¥m­î;'­kuµò­ÉôK‡ †±,’ÛV?sk˜`þ& w¡,¡HÆpN0/¥t»:|×·’Q±û›IPç/pñöù†Õ½ÎWó:ô™síÌGäwæØeCcçø`ŸfEÆ|ŒXM$÷0mVSþ”÷ÿñøâKœñš!×2¶X/è$c·§„ü 8ËŠ÷™éX`;^A¬`Dj‡ê u%þß8¹¶16Y–/IÊÊù5èÇ `L€ 0€гP–nx`Öh²$“HU¸Œ­ˆM¼5èeB㕘Ibù©+¦Þ·íãÿƒ HeÖË›§$Æ[j,SPT\¡Xg åøhÎX ÊðAú Û¿“fÍÒ¬Ó;uibšušÉ&¬÷“)ô)ÉæO®W³²m?'ÓËÄàðOª/ƒ?¬™ªC1¾4ä{FÎZE/Km:•ŒY^^Š>ɪö¢‡–d'ŽÞ»2)k=Ýÿœ˜`L€ 4‹€n…òÍ7Ï2ƒ,½Ù±æ%¹Y­â“5ÄíphGUŠêðÊi§ Mýã_)¤ýÏ‹:T9£8¾Üb©‰Km \=÷ œJÇÊ-‘%Ã';‡.Iz§°FËtá,máÇ?TÖ7&*&ê•(C±fIöcym6ë4ó—PQÕUÙæ5y[‰Š“l´m¡\0bDXEÅ‘¯±C‡Q§â? ŒH(]ŸœûE›ídn`L€ ø•€^…²ìì$݉SËv'Ÿdv·ðî ngïY%/ê{Iüàs‡MÿWìŽ]]C Ê­ö²Zß{Šcv´¾}ƒÜ? —¢¿:yá¶@;±’H¦z…ž~Þð;0@C<ù$³»…—ý–åÓÃÖVßÐ]é¤Ü‰¹P¿fùª{Wrà¯RÓÓMùGæãKßyîÒ%i*Šä÷Üۼ˜`L ™ô(”Ézl0àîžÝ‚î5³GœN>Ë%¤8¢Ó}f‡F%a@[AÖ>k×ÖH*|büâ”{¹²Ø× '¿ojýŽ¥„àÇ~+E·à{-ãN>ËÄñ$ß9Ѭtônäù~Ô²tp5M—ž·pÁûX•q¢:èKÿ@JΆ—Å6/™`L€ xC€,xzKòß§Ý?½â)œÞ*Œõ!ŽÖH©Ø 6‡êáeŠ*ý‚?Kß !æ‹‹G¦/)~½N$·J+ÝÖäó/¾l¨$ºR¸V©I+”8ÒóDÏ6Mϼ×ÄUÄ¿pÁ˜Áeu™HÿNÉÙ8»n›×˜`L€ xG@oeͪh +*ÆIf¡ä]¿Ö»ŠâMÏ‚˜>û”Cû_/©‘>»ý<<‰÷éÁº¨õ;Ö…¬ÉabãFQ}1N2÷?ii¢xÓÄ“ž+Ì gß«? ³¥ù·æõ©ÿ‡7ðõ¢èRô|rΆGÄ6/™`L€ ´„€.…26(‘fÜÃøÀž³ˆµ¤íúZгŒ<Õ­1}²f­[øÂÀÙ´Ú( ‚ž„2M1l–Æ^!RκWÃB´4Qœeä‰ë¦DÌ‹ž)½ô{‹š¶cPê“øãÈ4‘ Þ,o%eoÀ Vø¶Lxɘ`-# ·Ÿa©>ŒvÑ-Ü^©·ºµŒt+_<%ƒÉ‹ÕÃYnI0é…±«ßk-ÊTÏ0©ŒÕv¯ò”é¹ÂüôÔï^7oGFêL|Ç{Ø#ƒO“'\|ºéáÅÏ£Z¼Ê˜`ÁL@/BI0¤úe¢B; %AÅKâiäHÌ*?$”é×½ô¿Öïµõ ¥zš$÷?ñU"žô\a~zêw¯š—Ÿžv'(ªÛ È‹S$ãÕ"¾·W™òEL€ 0&À! ¡DU#aDN;Ë"‰ˆø8aÀ â*D2Ys—Ô¬ìD¨>$âBjëÙ¬Løä¨}®ôÒï'®p#gäg œª¨ÊóâŠäï娎S¤œò·çĘ`LÀ§ô$”©aTzͲPöi7‹Ì´£$–裗þwõ»«NX?~Q=æÓ¥ë¹ÒS¿7«yšHVàqºYüÑ9nBÒªU®ÉpÄ^2&À˜ð½%jް,JòÉGíãl<ÔZj…Hvóö8¥5V=ë¡Õ-Êþé†ÚçÊ“· òC®ùƒRïPˆäè°ˆÌø•+«üPgɘ`L@# '¡L_âÜ=þ#@}.8ÓRɳ>z»'õÀÇ—u¬}™§_óÒD2¨/Ö"­#‘Üõ§Ÿ=sd]x 0&ÀÚú^oI/âí„\ ‘Ñ“v6„Æ%@iöj°þuÂkZz‚f¥ÆÒ’l„PÒg½Ö«QÖ’d€N C¡c÷!Pyx;ìß±¤Ñó|¹Ó[‹µ¡Þúý¸ˆÉ1æˆ1,’‹2&À˜€´ë]xÊ)0h~„'ŸÜ$4Q†€lŽhÒ¹Ç:)ñŽB·)7ƒ!2$ƒÿÞ9 á‘ÐóÆ õƒµúÞÐï‰ÿAD¿ÓU­ãì—–æ×sø5?A¿38NÛëɆèšx^Ý/Öº4ÒÇ¿ƒvÁOG/rhÚ%$ÈOþœËül†á׬…”ô;švqŸ•—žz»ÒÀ’Ì"9È;•«Ï˜2zʳtIÆH™ñ,„t¢°ÂÞ%Éh‚èCaÿ—ožwž…ê¼-ÞeÔ„«ds8„tíyOß›ïºT§º_~k®lÒ)ã~ŒÚ¼ü.½‡ÃIg=tŒê4mw×ÄQphÏϰeõ#°ë÷šv‘g©ª¬û`ý’àûÿ¥Âî?>‚¾Ãf€9ª‡¹Õ»$àÜë•~‚ ÉèSýRÝiÒÏ,’ëhð`L€ †€ÿÌ ÞÕŸ¾¼ñÓ|‹bÃâzÞüØKŠ ¬wˆI=*ÿúöþXvåAŸ‡Ÿ×¬É}{E§þ¸}¼vyçã þ’ÁÝÊ7þ»^ œ•e@yÕlºXt=ÿb(xîAˆýöÎ<ŠjíãçÌî¦'ôÞ¤HQ@  (èÅÞ®åzí^¯¢— (v”’èj@A±Ó®å³‹ + ¨pLè*-¡Iè’žlvvÎ÷'™eIHÙ ›ä=Ï3;í´ùÍÌÎÞyÏ9ÿ—ŽPÑîöD‹Ko›ÆÿÛœ·¼òVáhÜ\äü–"ö}þ†ÈC¹ÂÚw1­Â‘]z‰¢ƒbÏ¯Š¬”ÿ‰°vEÇ‘EDçžÂµw—Øõß)H³ÞLcý¸€Hö@&2W,íïxØÚ]Õy c¯ŸrUÓ"¾_ëÔ²ËÅ¢cŸ‹ƒÛ‰Î±£„îʘý?SÐ6i3Pœ>l2¬ÀMÅßîüYܱXü¾x‚ˆlÒMô¾`ŠˆiÑ[äenǶ‰"sßj\šiÅ]ómœèqÎDÄë,þÜøhyÊE&Êcíü{DΡM¢÷EÓDóöC…2<âÀŽEbóòd¸Jä Í&z}B´îr©PÊÛ¾›—&šÛ{ë­»^.<—Ø¹î ±}õÌãø¦¯žáÝvh×ÓrnE—ØUöÂ0ï+‹µ7Ï`[H‹í? }}’I$_ÊîÁv¦¸>L€ 0úO ˜,Ê~¥í€Øm{Æ%€?ïΙI"¤y+S“5öȲïͲö¼÷’Øñò“æ²£i Ñ ®‡}.ÒŸ}D„wìfŠbÚIyµ¹éÑèŒsÄ^ˆíü]ÛÄÁù˜é~û¡ØýæTs¹èð~Ayny*Nh¡¡¢Íõqæv²>w›ð¢pD7»æL‡~ø\þ¹ÝÜ×qT‚ðä犭‰£EÁî4Ñ.' Q§Ã/¶D€Ÿ(nCÜo³Gˆæÿ&Úöü§Ø´ôi‘u`½èuÞSÂ%òsþGö®…¹{Åú‰]ëß4õ¾ðYᆠþuÞ­"÷ðfqÚ°d/:²ÞÆþý-ìÏ›—%›"˜ü’IHSyGÒ…á)9û7ˆ5Íë=$Út¿ø 3îg?.Úö¸Vì\ÿ–Ø´&À˜@#le¿âóäe‹]³ŠOh‹6¢í-£M·…ü›ÍròÓþ…{Šk£CEÑ¡}âÈŠÌ}Yk–‰Æƒ/ûæ½e®Ûàþ°qÌ5Â(È7×s]Å]·ü™æµ“…˜‰îœ)¢-ĵÐ4Õã Óub[ò}"{í/fú±A8GÚG¤M{TèY‰Ã‹¿dåiÙNØãç»Ðtèå¢ñ óÄ–'þã»™—Ë °õ—©âÐ m×ëÑ´ýÙâ@úBQ‘Õ´»8’±ÒLåk,š´ V;B{6},]ûé rŠÏ‰b²[Á]”%Ü…G¼yÐöíkç˜舘޲ˆæ§ þ1W´ëqû}-ÒR^¶’›óæpߨ«7 íâi›)®}-ÈV²‚G·8Mdï[W=om®Wsr· K²¯Hn⽬÷nQ¯Î3 `L .¨×B9?}“÷\¸³f ïFŸ…较…£IsÑó™wŽnE‡¾VÈÙ´Ö+’­mÇÎÉ‚Ýäì‹ðµ^J×!’mX¶™ÖlŠ›wŒsLï¦>å^§7+÷áp)K(Gõì':N;_}JämÝèMà eÈ„%™BQÁ_æÜn/»ñf³öç˜×Eß‹Ž PÖãèæ=½BùÐΟÌ<Êû l kï$Ñ¤Í Q˜·O„EµÎÄ%*hì—} ØÇJ/5»hÖn0ú@Ѷ{±ëí‹hÚÕŠRj¾üÃË ú6§^%^ý¶iù>¼{Y©8uy%=¶ß£†RÅŸfè@¤XÑÄ‘¼rev]>.®;`L€ ÔmõZ(W¥5OA®pÞ'6Þ{M™gT¹\en·6Ft=M´¾öNÓ…"gýJÑòò›Eû»ÆÂÝZ ='ˌ֦“ÈË9êì)±N“ÛY·+ á§œ*ºŽ{Aìy÷%ñײùEå}% ½ R,ô¢<3Þ*¸]+hÉG™‚G/þ‚`®”ñsJ¿ÿˆ˜–½Å’·‡˜~Égýó3–ßci#u.•J:¶‰ß[–O.µ¯¼ÞÔ˜¯ÇxѬù¢¾åôØ3ž†HNð·)’Ã.e‘ì% L€ 0&p’Ô[åŠx¢ÑœÀ0_ѧ 0»v#÷ˆ¬_—Àå¡=éÝ*¨¯b{ãf‚,¸• äMÁ ?erhvþÕÅI¥¹[7˜¾Ò$¤CÛv1°^‡ŸÒ]än^/tXº[_w·™F 1ýaÝ<&„¶î NøªéΑ·íw³k8êŽ\78T@þ_Û¹GÕ—¬´™ûRM«s×÷›îÔø®E§aUÊ8,º­påÀe¥Ã?ú<ô¯<È›þЮ¥¢õ©Wˆæ·ÑÍzŠ–].1÷íOÿÖä²h“…™öE4:Å›ŽB"[À¿ù‚,Ö¡Q­D—÷ ›#®ëe¥T¸B#¦ ì7ÝPÂG$ËÅ1ÑM.f‘@ðœ5`L€ Tš@ƒÊXx|ÿ1,¾Š3^_(lèO™ü‹3ÐEÛG‰~ï,}g}'bžWiY«FO©â´ç?½¦¼#-úÌ›–ÊÛñJnqú‹Ÿ‰®hØÚª=\9òDú´ÇDx‡®¢÷«_¢Ÿäå¢ÃpX¡!®}CøJ“po|Öù¢Gò›Þ)¦÷Q1柗+&°ûB‘sx“6|¥8ã’—L ðšoGЍfÝÑÃÅrqɨ-¢'ÿÑ÷ÿʆ]ëß6{Ò¸äžMâÔÁŠíkà¯\þøß“h<¸O ¼ö=1äÖ¢z¹ °iÙÓ¦{еï‹KïMd…‹nS’ªxFý(wê;\üíßËÄùw®4ûÞòódA úêrPN§–>°ße¨­ãR~k‹n|yËÅ‹s­mê¢:"™ÒV$’ͼ³3É´r D2åÏáxäóë+’) äêŠäâôe‹dÚG"÷X‘LÛ•á.ipXŽH.ŽTm‘LɃ%l6,,]x>G}¼"oXot½êÚ[X$ËYâz0&À˜€E ^7æ³’çL€ œ|† ‹ÊÎÉü \Ï·jw‹»¤¬yó ެؙ`|ëá­Õ÷€»›<¤w:™u«óe?“#î§’sïË; Ç·-¶ßi|ÌÍÎÁ!’ÿÂ~q电u{””€ÐâL™`L ˜ “P&Ndí4ÅÒ’yŸ~lèE‡—µfèCÕ 7âç)*ÌZþíW‹>}›“%HŽ´Qõìý™Â{Þ­:R}=î¬õ…Wºâó_ØÄøá8L÷òøyÇh{ѳÅ[˜Ì®'¥;5aÒ55uYuŽÓ0&À˜8™‚I([–.z˜ë™™‡rÿX³zú‘ðfbIÇ‹h‡* nÄoõò¥ïåççÒ|4Ô M$˜‰³Å‹'-Xu0Ï;jaÖ‘ê»zÙÒ÷²–bmáu|þ«qzˆñûcõêét?!‹€wuà ¶´ýg¢ º$«ªèöm­MÚÎºÉÚÆs&À˜`u‰@0 eâF–Ez˜“Xrý¼à›»Ó6¿“Ö¸›üá”Ë[–A¥8/â¶m㺯×þü¿ HFcf[“%–ƒÉ¢ì=ïV=©ÞTÿ?ݧ˔‚›[–+qò…8/âF÷ÝG%LrÞ3bc#ÒÓ¶|ŽÁàGZ5„H^yÞ)©©{­mÉänA–ä[þø`ÁÇï†x;ïQ….{ЄÉÿ,Ÿú¼ÓEØî–Ë—»}¶ñ"`L€ 0:G Ø„2}b'ÁDXz¸ça ÃÃ~Þ€s‡è7xÈð¯Nýg£Ž9»T׿6ËŽ9ÛE¨NFÒ†¨ŸdêŽz· †{º«07eÑüO6¬üy#ÈXéÓ;ñ¤uâKœƒÅ¥¡ÌóŽúQGÚˆýŸïÛ{pàyç_¿ÌswLKÇ6ÕÁ¾N¶²o!’Þ§v ~’© 8êÝ‚îÁ·;gí/?¼¹zéâŸA&`ç½yNvØ¿.냞-"¬3€†{Sº¦®o­óœ 0&À˜@]&¬BÙ²,ÒCžÄRú«6¥¦l|é—èݺÛÕ©c ^æÎWî\j¸±Ú° óM#Ò`"PšR/*Ìݾaݲ ¾]^X˜Ÿ4Ü’9-“²´,ÊÁ&”;ï¨+]£‰þ­ëÖ¦¾äŠ!zC„t‹¦ó"óT˜Ì’YÔàÎ? óM#Ò`"Åçß•³sÛ†Å+¾ÿv|¼ÑÓDàÎ;ºœ¿â§þ¡nwÊ¡D ô7CR¿Fë˜`L€ ÔÁ&”‰©å§L¦b²€R—6LþâÇÏç~åNíÓ¯s§S{ô Šnc·G nPA×1ÖZþÁœüÜìÌ[6oÙöÛºÝ`¹0嘄qVÉ”9ñ$®ä,n¨ŠÊ=ïÖ~ˆÏâ/?ùq±Kº~F‡NÝ{tˆŠim·‡7Ìó_p(» wû‘[7ÿ±uÃÚí`ðóžÛïVCx^u{ŠE²”hìpkçÔµóJÎϘ`L€ Ô Á(”ÉÊi‰=jlH"Ù²ZbÊ Q°S:ö™GÌ)®‹ &/ââeƒe²“(&K2 åÌ’eQÁfMF•ÌP©óŽ˜4®µ/i˜Hòy/~ñ!wš€žw¥”Ü>°ßS†R (Ë .{HQ¤Ð/ïÅgÎâR+ç}Gll›"WÁè}dÏ…óÝ;ç\Uâ :p`L€ 0&P/ ³P¶Ä‰$ße|$þH$“E™ü$ËÊõY0 K0‘[…õ"Ab™„%­ÓvË/ÙJ‹MA¬câó~üé±ÎÅ(àç}{lìYºò ‹9ÕÖªŽ&ÅK»t"ùkÏ™`L€ ÔGÁ,”‰7 ¾ÂÀ².’Ë…¯H¶|™I ×g‘ŒÃ31±&bä+šˆ‰cšÓDû‚Ý’Œ*zŸw/Šã¬sn1 ØyOØÿ.¡S/ôBŠ»Jê’zL—Ô53Eê:ß×ÜÌ?L€ 0&Àê+`Êwy$ hnàfY‘I ["¹¡ú(ûŠ&K8ÑÜÈ´¿.>ïÇŸ5ë\Zl¬óì·ó®† ³§çf¾  ã^oñRJ»±ëêÕ?z·ñ`L€ 0zN ®e: –´Ä2YJI[½øZ’ŠEÙâBlh"6Ödm£8u9ðy/}öˆëüÒÜ:ç4·¶Sœ*‡½çöo‘žù ü‘ϳʼ£¢_ÛyÅêÖ6ž3&À˜hê’P¶Î‡%HPðÈÖº¹£ü +X\¬õú6·ŽÏ{±¶Î¯ÅÅZ¯öœü‘óòõ‘AŸL> —¶»Ú®H¥v˜`L€ 4(uQ({‚ü&ŽÍ˜×ƒšŸw?žžôýîõ(ýydé3Òž˜€á¨Ÿñc1œ`L€ 0:E >å:œ+Ë‚‰À¾K.‰Ì;|`¹Åª—”â/M³ÝÖù×ÕßYÛxΘ`L !`¡ÜÏ:3¸ZôÌ=tàSxrœf?r ü‘o`d‹Ï™`L !°z‰hÈ øØ™@ƒ#6 ßmpµøµ´HÖfuiÕfhçkw48 |ÀL€ 0&ÀÊ Àå2 ð&&P_ ˜®‡¼Œ^-†[Ç(¥ÌÇtO—”5ïXÛxΘ`L€ hǘhÒÏÐ7ïðþÐ ²§u¸ðGÞ‚nȯºÁÚÆs&À˜`L ˜»^ð•À´Ø~£•îY©ÔQ‘Œ‘öÞŽŽnÛ5•Er¸ø™`L Øõ¢Ð8 ¨+ÒÏ:«•ávÍVJ]íSç\MÓF³«…^dL€ 0&PÊe@áML >HØÿzCwÍ@ƒ½æÞã‘b´…ÞÜeÕ*¸\p`L€ 0&À*"ÀB¹":¼ ÔA;‡m¢ä¾jÆÑ¾‘i4?)_êÒ²Íãrþ|W<,®2`L€ 0Z'ÀB¹Ö‘sL p¶ ì¹;?÷¿ÐÅm­RУÅ.¥´áÝRWÿ(ÄZk3Ï™`L€ 0`¡|@¼› Ô‡ ‰Î*Ì{NÆßúb‘7ÛCl¶re¶ïv^fL€ 0&ÀNL€…ò‰eŒQ “c=†çRT®“’ª­"º¶+úþáƒÞ"㮸~±w¥–ÐÍYŽT2C(µËf³7#q|j-TÅlÔÿoÙyoÁŠ|ŠU1tû¶O(Ûˆ®«W{Bªe@IDATmmã9`L€ 0&P5,”«Æë¤Æv:ß ÛãÞsŸTÆÃº¡·8V.—í*!ºû¤võ×áðÁ¿Õ6œ"»ÃÈ Wù¡¡x$ÅMxz¯G‰b £^ž>ýáBÔZºþ†ÝgŸ^ä.˜dxŒp ¸J‚”sCm!£Û¯ZuØÚÄs&À˜`L êX(WÙIIŸxɾ¢Ýo AV»Î‡¨Óöì]fÈ0·ÛvR*…š/…‡HkÑVüÞ®cëôæ­ž)Ëyàαñ#Þššô=ªi`ªw‚9}`¿‹‹\¯b„½S½§BÊÃ6M»·ó¯«?ònã&À˜`L ÚX(W]í%„H~H)9µin–¸ø·5¢Ã‘CG­‡µW - / âôŒ4ÉÝMš‹§hu(:æ«»Æ=1î)O¿€Š{0‘`®óa×þm‹ ÕtÃP7ú \-¾Ö4LjοþºÏw;/3&À˜`Õ'pR?×W¿Ú &¥,Éâù^{wk·ÿò“ "¹Á|u”øüû—m½öþ)m6dzw>ÿ0òq`"Ë{}ÁPÆÙÓbû?\Thl‚O¶W$£±^¦Ô´»»¦®»ŠEru®NØ`L |,”Ëgs²÷È»Ç9/%K2DŸºzí éðè'»Nu¢|âD¼ˆ›Ã:é–û¹'±L×{ËÛö’žsdµRÆsp"ñm´ùŽ ‹èÑ5eÍuâÄp%™`L€ Ô1ìz¤',.ήIùzÓœLqù†”:'î‚+q;£´¨è½{ŸÕoãÆ•Y¨—¹b}Ø{nÿùùêYa¨;Ži¬÷›¦ÉÑ]~]ó¿ ?® `L€ 0:L€-ÊÁyò4OS9Æ£imá“lcKrõNq»ä·Õša³µî{ÁßîE.¡˜èå0¨¯{åtjÛž12?ÏØ¬”ºÓG$çâåil×èÆýX$WïšàTL€ 0&ÀªB€-ÊU¡U;qÉzl³Iñ`çCû7Ü«tòYîŽiMšßƒœ^ÃDú¬ž0‚®7Œ´Aý/LûrÞ3¨c¬oå0ºÞ'¡öЇگ\ù'öq`L€ 0&Àj@P[Öjáøƒ±í_÷?>HiZktÇ.~8CÄ-ûZ\ô›ÎBvAiUÞ6`@lZl¿Êc,Bov±Öa£±ÞV›°]Ö5uí ,’-*Õ´e« ‹Fï³ü”-Ërù‰¼'íÌ3» OQ’Pžë•:ÚÐä¬NîÒ²õ³rþ|W€«ÁÙ3&À˜`e`¡\”“¸ÉÊ(ÿq¯&â·Ó@ý,ƒ§Ê¶Û;"ÓpL4j_&êFÄ×ëµÌþ Ô“ÊStº{;zJIuz3$D=Õá—µ{j§6\ `L€ 0&P£è²öò¶Ú&@®06¡É6QyyìãGúQ…ùÒæ°·D–a˜B0‘X&ÆdU®µ°sèÐ&z~î8w¡nÉt²xcÔÅO„-$¾ëªU[j­B\`L€ 0&P.Êå¢9);H¸Ù5!£C=:»]øñO›#$ Y’2 eºökíeäÏ3Ïlæò¸ï…H~ÃN7¶2ê},*¥ï–ºšÜB80&À˜`AB€…rœTƒ„±éz!Å"9ç¥ÄØÉÖH}Ä:`î»bc»º…ça—§èNtõá{Xh¨÷«Ú¸®©«ôÝÎËL€ 0&À˜@p`¡çÁªY8môÞÚÀs0_@èš'‘LSÀ,ÊÛccÏ2”>Ö­ô@…—*GJ±IH-#ê}êϣ㼘`L€ 0ÿ`¡ì_ž5Éı9ÁòÈB¹&$ËI[bQ¶D²—7¢ûÅ¢Lçmû Wa¨é±¥=®R¬–R›Ö¥s·¹òãëÄè€Ço`L€ 0&Ѐ°P®“m‰·àªUýª Yw-Î~y!98dHt–+÷Öôý‚ïá‹ @„Ëïàb1 >ÈÅ.)k|£ð2`L€ 0&¤X(߉ñ‹x«Ã²uh+ƒú£ß%\ ~*¿ 6Š­i~É4Ìtú7_œ/<êÎìÂ¼ë ‡#Ž1KÁù=»M=×é×µ¿Õ´Òœž 0&À˜¨}¥|'k¿x.Ñ_ÂïºE4~ã…Jgç8ãt!#¨KáêJÛèÕ)Âѧ—°µi!Ta€ÇÄ€coø7‹¦ß¾/ì§—2ÚVájîÒ²ûìÝÒcû'¦5o;¢‡†—ÿòm¤qœ‰ªN±Kû)]W¯½«Ó¯ëX$Wá qT&À˜`ÁD€-ÊÁs6jÏ’ì°‹¨„‡EöCOO~õÆ´pôïƒÖì"÷™W ’©KâÀ)¢#lm[¢r?|ÖY1Yž¢à{|§Ëå9Þ÷µ‚@^‡nÞ^ŠnôfËÅ‹sQQΓ 0&À˜¨],”k—÷‰J+q ¨¡åSÓDã×§‹ü9ïŠð›¯²y3á^¶RäÿßGBx<"úéÇMkrôä ÌY™Ã24DDŒ¼]„œ}¦PÉÎõåw¢à“¯ÍúFŒ¹KÛv ËqØeˆ‚w>a×^fîk4ãáZô?Q0÷ ùÐHáè×®†(Z™* ^ÿÀëŽzÑy"ìÆk„Ö´±ð¤í9ÉÓ…Êδ=ü–k…ˆ‰úêõ"ï•7„Ê)­3eTòɹ‰Ï‰˜×ž=Ãí÷u½(W$§ÅÆb?ãJLWdê® a5.Ãü.Âzü>p¿Õù×µkOT0ïgL€ 0&ÀêÊuë|Uº¶Z«"rÌDÁGóL·ˆÈÑw ÷¦­¢è+DÑâåÂÞ»§Èãa8dæ LþÆyÏϲI#ùàHáZþ«0öîZ£F"äöë…:rD¼ÿ™poø]ÈÍDø?¯¹ÏÍÆá#¦àölÝ.\ß.ÒMéµp±°÷ê."¾G¸–ü,ŠÞ\!ìíZ›"YkÚÄÜ^€z¸·¦‹ÈÑÃEèåˆÂ¹_–:NcßA‘;ù%¡µ ˆEÙ,K fßž—uŽ2Ä$•Ò¡ø­àã},¥[¿±IñÖ)Êö­LM¥uL€ 0&À˜@=$ÀB¹žTëŠ~ùUΛo®†^2L„ÄöE?.zúNs [Ïîb× Ç™ýa^#ô?÷ ‰¶‡9Vå¯Ì¸2ôç „¾q“Uœ(üü[ô¬ ­UKalß-(„rÈC…ÊËyϾj¦±”%•éPw-_eæáNY+BPæ±BÙ[€ŸÞïÝ£Ë!a±ªïÒôìÌKáoܸÜ"¤X÷Šÿ ¶÷Ú¦¦¿]”™w0&À˜`õ åúpË9ÂVPYÙB†…Y«¥çð5¦Fydù =o°wŸÖ¡wYÿ}³W${7ú,KEä}w›yý%´fÍ„Ê-v¡°Áõóm‡)’}’GÿÞ†t^L:ºâ;PÁáqG&thwkïȈ.í!½clZS;•ÿ ˆäRÅbS!Hü ¾ßÈñMçkw”ŠÀ+L€ 0&À˜@½'ÀB¹Ÿb£²=Qè:ü’uQøÍBAne†¢¢27[C¯½Bh]:‰Ì»4ý’cž}ÒÚ%ŒœáèæãÉP²Gå ãÐ_"ó®½q¹Ð¨ÐÕ±khèQVïc„1•‹Æx»0û†¦ð;üòKmçÀ˜`L€ 4LÜ=\<ïr›@ƒ;Gï^BFG™îî_RDè°!hŒwºÙ›…í”ð n]i:|–Õ‘Ìâ|ôEnݽiÝk6ÂÂÜT„]w¥ÐZ67]1¨ñ`ÑŠ¡µn‰†—›Ön­Iãtýæ-®Ò °!ÿ…¡¤ç M>.4GŸ®©k;aéÉ•ÆÈ™`L€ Ô[lQ®·§¶ü£Þ&\°GÜóo1úqäæ{DzȈzp„ˆNo gò)ÎIzù ª+\ß,QM>y]èh˜Gþʶ¶ÅB›ºzvwÞ$"þs› 7ÌŸS„{åj³'ŽðÛoq·›"»à³o„þÛæJ”Xõ(º¦îÕÝ y<+²<î%‰»2Ö!²ópÒUÇÉ)˜`L€ Ô{åvUÙ#‹O\Lqg'% £9‡j°!%uA3|lü]r³Þ²rIµ3«LB†fl¥ ¿²ŒDwlÙ9ÇùW*O DRî}6›Ð¢#…‘u|ÞZãaä国gT¦œªÆùି‰Í!a¼ûâ³ -©ÿز1±P„ê¾÷«CÓ0&À˜@]"Àåºt¶ü\×RÙÊ›ü•añ­n(W$S†èÃÙÈ,;ïò¶W·œŽ 0&À˜`5%À>Ê5%Èé™`L€ 0&Àê%ÊÁuZ©2åQFžËf§e~"à²9”®ëäfa2ö™û©Ά 0&À˜¨oX(Ïõ cÃí9á]ž*Öݚ䆅«"— Ýr˜óqHx`L€ 0&@X(áuàv¹2òCCe¡Ã„µ«{U"Ž&Ïü¼ƒu¯ö\c&À˜`Làd`¡|²È—_®Ú·{çr˜9eZ‹¶åÇâ=•&@‰ç®-›Ö#Y­©ÒypD&À˜`L á`¡\çÜpK¾ùü7Cwþ½]GZçPCÄÑSäÊÚ˜²" Y˜,¡Ì|kÈ–“3&À˜¨ÏX(ÏÙõožCû÷”Þ¼•ÜݤyðÔ°Ö„ø™·§-Dõi`kòå]Œ«Ì˜`L€ š å@®Zþdí4…Ü’yŸ~¬ÜE‡œ>ÀpÛ¸»ëªa,ŽM܈Ÿá*ÌZþíW‹°U/™,±L¼90&À˜`L L,”ËÄrR6ZNqzfæ¡Üß×®ž~(:FÌï3]ªqJˆñKýyé{ùù¹Ô5\QÉD‚™8[Ì±È 0&À˜`¥ °P.Íãd¯‘…“D :×Ï ¾Y±;mó;´i/¿ì7X±e¹r§‡8/â¶m㺯×þü¿ ÄÓg"¾Ä™-Ê€À 0&À˜(›@…2Lr9˜¢ÊΞ·V‘€åzAB®SÁw¾ûÙÎ-¼ѧÞ9û|ƒ}–+&J|Þ>ûƒxmݸö«Ÿ¾øä‹%ñ,Y&¾dQf¡ ÕJF %Ë“¼Ú™rB&À˜`ÁC ÆÎ¯RÉ NlðR® ¹€sc"Q—‡)lÁÇïÏpî°ýÏ2üýÁÃu9´_¶g—ìz0C„¹)jÃÔO2uG½[PÃ=Oaaæ²aåÏAÆâ˜‹eâIëÍr½À"‡jhƒt)ÕLËɘ`L€ = eáN!U«Q“'7™1~ü‘ ?âà® %”-‹2‰»PL!«—.^µ)5eÛàK¯¸DïÚ}Xú­b$|l#\.U˜/C=:VV a¾iCLà¤î*Ìݾaݲ ¾]^X˜Ÿ4Ü’9-“¥Þ²(oÕ @÷»'Oo‹òÎj$ç$L€ 0&Àê e›fû^7ôd#߸Gün8êதå§L>µd¥áùl˜44H?~>÷k,ÿpjŸ~;Ú£WxTt“Ððð»ÝA‚ºAÝ1 ³öåäçfgîܲy˶ßÖí²“&Ë1 㬒‰\ˆ'qeÿd@¨I û])¡Ùqÿ×$N˘`L ˜ øÅ 9">q·rãì¤øËƒù`ëPÝÈwœr&òÿŽÁÔS#Lј"1Ñ>ŠC/;Ÿ&¿œOäS—Y…éåÂzÁ · ²“(&K2 åLL$”-‹2Åaÿd@¨nˆ‹Oš¯„ê=')¡CuóàtL€ 0&À‚@-Êæ*ù"šSGNL:oVrüÿ‚ý ë@ý,ÑGÖOKüúºeÐöpLdEöZœ±Lb™‚•¦x­~þZn–H¶|»‰eM&aL™3[“ÁîsC©Ëp™õG~œ`L€ 0`%àAåt¾–¡ÿ¹Y)™)-ΞíI¾¡jF€Î _Â$ˆ#J&²0“E™„²¯UÙtÏÀ6¿œSäS‚eM&‘Lî–5Ù·Ÿå«LÚ²$["».cPÕ1Î9+B¹þ"¥jÜÖÞ¾‡Ó9œ¬÷˜`L€ ÔK~±(ÓÃ2.>q}+ÜßRJÝ$ñ$­—Äjï ,HâÎw™‰?ÉfC?Ìé<+”ë³`¶®-‹‹%”É7™ø˜]ë•Ìi¶[~ÉVZlâP¸¯åÈøä·ætLW°HL€ 0&P¯ øULÅ%$=¨ 5þÊ G‹;Ù²ì·k‡,Ë$„ɺLSˆÏÜÉ´ŸÎ§5a±^¼ÖDBÙW,ÓˉcšÓDûØ'ªÈ’l¾ uƒÔäC³ã_¨n^œŽ 0&À˜@]!àW¡L],–Å4”7¢åûÙgÙo—+K0“(öÈ–Hn¨>Ê$˜-±l fk2[‘¡ºÁôIâe¤?]jâQÉÕ%Éé˜`L ®ð»P&pø³9è>ª#\0¾Ãôž¡}Ãý,ûåò°,Æ$Š­ÉÚfOkî—ƒ4KüZVeš[ û,qlÅ ÒCÞjQ?ÉÅ]À©Ûàrq™”bj;bvR‚à­5׌ 0&À˜€ LPQ¿=î=÷a0’`Ïk­Á ö+©2P(õFÀÁOàîâÍ ½ìœz ’¸øxîAR«º] — ¶4ˆõ“ Gž?qI½ØÎÑîöI®Ûç–kϘ`U'P+êbTÂäXá¹Õë¡Ü…R_À˜2Ê9%ÃÒï¤Á„f$ŽO ²*ru˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&Àj€ºá†ÆE)§S«…â¸&P¯ (¥äºÛ[En¿ó”°z} |pL – ûIÙ{¼¾)ú†¹ÊVKEr1L€ œ$ò$•[ªØgžÙÙЋn1¤¸;ú ¥š™¤Ô…;PÉR“_tnÑú+9¾«Tb^a'€Óù“}Ÿ¾l¸å˜;{ܸ¬“P…r‹¤ÌÔÔÙIÃsî©¡R‰nJ¨P39RÈß°ü“&ì øzϺr3ª;î˜ÔÉ#ÄЙÉñïÕƒÃñû!ÜëœÔ½È0úÌy:þS¿g^2ì>mss!Ü7J¡.Ãó)V ÑÖ<<‰»K©=RÊì›ïˆoÝ÷H=:t>&Ðà ØO&ƒuÑ ÷îú'þx4ˆâÒA)ªò˜ õ¯ôû¶ÅöŸÜ5ºÑkrñb½täÊ­ÅMLºCIu£¦äÛ³’ã?:6YßF&$½‹2‡ÙÝò²óþìcãð:ÈL õdŠÙ²ÐX A#”SÿÞê¦Ô”ו§š·~̹uÊ”ˆÆ“}0V{„{|Ê•­~ÒlÚc¾Ü›bE©ÊܺŸ(T²³ÝšT_ÍLJø±*ùT'n\|Ò‡ýkf%M|¦¼ôn©õÂ3 ûY(—ÉãQçH¥Æ.ÊeðéóÚú&EùòI%\q¸‘ÂKÝKÏ ü¶Çs£=ö][”/žï1mý«¢IHÒæ»{攑%obL Ž8i® éú€HÞˆ?˜ðS©zàáßZ(ãÅôìÌŸÉ ]-Öšè‰tƒ !Æ••>î‰Éç %/ÇŸâFH®£¬8¼­|qñ‰Î“Î)?†ÿö@(-ô_nu;§õWvlòëß[}a(õ!‰äÊ Dóù†áY™ze«¤j¹:á~‚Rè I^:S„½ %¿Ÿ8¥²u¨n<Øò¾€%oYuÓCº‘Τ#ã'C]¸¥ ô˜úÛEyòw<£ ‘\zoÙk¸÷"1=¦Ž¸ë1}ã²cñV&ÀêJ TPÚ€~ÏBÍÆP¥þ|Ž-÷A°B¯H?³ÿÇî«Ì:¬P_ááÞ랄ɰ6•RwcËÇ¥·òZ\g“*ྰ£âŸéë`@êUo£®¾ºCÛ"áZëÖÕÕ9H<Ø5ÜSR^››[—C¹gVâÄ·g'ÇO™±2/›Ý;iR± Uu*U‰4ø"ôÁÌÄ Ë+5x£¸Åex±h¼l˜5ëñ܆áKÄ÷øi]-JuPõCÏi®«VzNĘ@Шu× X’'à¡üXM À ÝRxŒï·î7¸óе;ª’„A”b,iw!Ý+mÜ”)TŽNBïjqÖv§Sižäda"í0¥À5íƒYI ¯Sœ‡ž><÷pþZéЮº1Ö‡³`Yƒ¨œwQßS_¸ñÆá&)¬­]±-‹ç !Èc¡’ö—æ$Žÿ•ö›å蓸«‚ø”­„¢¾PÊëf%=¶õ±gž‰>’㞎o~°zKòøÞa{ðÕ S#â“þ« í;üÁ_€|þ8…p5ù¨½Ç{ô­Ó$ò»Òùè ·2)‘tÜB ƒåò=|͘®"„ú.Úo¼ôÉ‘ñÉ3±n¿èŒîqÖ9·öj¾éêÑ9Fæ÷¸N«qJüSeì¦kúß5ËËxéŸö¸ÞÅaº&…&?Å'þ¸‡èúÊš”0À¼fÜÙÏà^¹ÖáÆøˆ½F³‹±³œñ)£“»¹ÝžùaÇù/;ÿÓªSa¼&-ÎRúÁ—p ¬ŸÿíŸ;w®mÑúÍOâš¿ç2÷Íÿ¤ð|‰kµT(ﺢHåï×’ÿÃ7“Ê^Çݯq IɸßÁ=ìÂu|æG”&Ÿ‘†x/çZå‘ÅbúJÙ±ÅÀÙ#Gºi{\|òX¼õÄË ½Ø‹{âGx”¼‹]qÜÛ•&fÌIŒöQ(ïï=ú{ORR;£Pá\Éée¹¦Y—zMÝp¥G©·p„¶¥R¡øÿ Çs/ÛüHïŸj”'fLनU‹òöAýÿ¡‘诣…(ke¸ñ©ù†ªô‡†‡+6©7ð0½uÌK/7rB¥dž~ ö-WvÛ>ß::xt ±ø‡lQ¶ÎXž…‡ïÌ1“&µ x6Uw¥{>Ã|žˆ§ÃH7âé©E¶§8ìöPb¿Ji¿JFÈþx ç£ÁÕäâ½BìÕ“.AšÑÂîø·èв#,ß3°¯Qd³ˆ3I$S<ˆäy(£´kW9ñe+w¾ñ¡•íƒ%±³×¡ä`a“÷á¡w†{Ëfˆä"»Ý~!Tå(< GxÜG´Ò™âA‰ RjO†ØCz@.4 cáç3í)â$<îP¸ó ¶0Ù›·C´Ìí|¶õKOŽÉ‰´Gýüò•ÔFG:"E4‰8îó{eYUTŸæÑô¹ý!ðË¢rhj«³,²<º¸”êKõ¿u 7 uSñlÃ~K‰ä{œ“OHþ·É.CpG AN·|=÷;¾”¦ø¸ÕJy^“wlv{¯–ö3ÍsaåéÉRõiãx°¶D2•Ÿkd΀`êmÕ¥Æs%n‡Ÿóˆæs ¥7DXzq>ªð('^ú.‡ÿòÝZ˜¼Š¶»ôì¯pOõACÝ{…C ±¼ÞÐÕRj`öšsü6°Ï)ÔõÛ‹ó(þõЩ6ÎvŽÌ7ï!¼VëEë·Æ#ÖJ³Ýè°‡ž®ŸqŸ–º+º®¨„ÊœoŠWÙ븢ûÕnk2Çß`ù)]áöFIÍþ+®§¡£œ“»P9ôò ‘|.ÆfÚžCжâ`P»S¼Cpß‘œˆÊ¿‹žpd›…kb¢)¦­èø_(ëxwcáääVÉ?àÒÿ¶¡Šä¾/mmãwp­Vé™âËÑwÏ»œÏú¿´Õ|Vøîãe&ÀêZ³(«aÃìé9GfâÏzÄbù¬íi[ïAޝV6W¤‘íñ‹2ôä¼¢ýÙ×"ùWÂ`(îÖ49u<.ÀjõŠÏÆaNp(²üþ×ÚŽ› +ó›%ë_"Î÷(‹N™q^s>¶ËÏYñá>ð¼®Ü¿š<¹ÉŒñã ÜÛa…7Ë9nÅyÐ9}Nž;÷Ùü#EݱºŽ|!ä†iv{×™Îñ;(ÎȉIIðK]BÖjXéÒhä’ÙÉ­’ðý ÛáÆBìÑámC^_`a Ÿ’"Úé³’&|BÛðçîăvd‰P)óR9l‘ö[©®çÑ©S³² 0 ÷ÈVM‘‰|•Mzr-‹,Å++œˆUEõ™sÿý“aùË3ðä÷-"è;ˆâ QÞ3ÅÖù¤ë„ÔàN3ÖÆ‘ÅVÁr'çS ·ç0Ø='i"Yâ(üq_|âf—ép˹ëßÒF°è*mÚßa•þ†Ö)àØÍ,›nSq “^„ ¢‘/ŽøßÕou!,_·ù»$<ÜŸM¹ªíç¿Ê8T™¼?z”3¹/¾}4Ò…Ñ 9<^˜í|ôhz©º‡¶lÔúåûï7{­)¶ö{γ;´3f8'®/)ç\«CŠÜÆx¬Çô:^îÆ`n^`™•éºhºgäK*µ­/4ˆ0×DKœ¿Y‘+º®Ç,§¬óm¥?v^Ñu|¢û_sÒ§yº|®ãl4ܤt®ãô=žÉdY΂ þÂðúŒÿ=þç¹óÚò?X/¾w51gNâÄiá|݈ÄKm<®ûç½/nÇœƒâ¨Å¿ä&Sg,ÛOæ$Ç?í»¯!-»Š ŸÃ‚]a”j•_TH/lw×KzÑt»3𵡔 áÈ„)gB‚¯o+f;'®®ºp5'€/J—Á¸¶fâø55Ïs¨ZÊÛóŽÜ ÑØ³:•/ ®/oâs|òë7ŸëtÎý9Cߌã³YBàt0Yè›É+I Û!^¶)eÅÜÊ8ÁbM/Çr“È¥9lÝfÖr7q8÷OW!?lÀ}ÚXSúÚ@Ú\_<ºú ×t®Ñpu¹wÆSçáAí“V~j‰dÚèQžÞ¸¾¼öä„ ÊÞxž pÍ]J"ìQïåé¹ÓF9“ÏpƯȆHÆù=wžæ$%xÓÐ}Ý)Ìó4P^\j‡p™›*}]Up¾Kç{²‚{¾Ò÷ë1™"Ý÷av•9GÆõøú]}‹mâ¿aT†>yî̽3ñãœÓš+wA+\Ó‹|³Á—¡ø¢2å§ßÓ:a{zñ¾ÒçÀ'¾¯Róg^¶}OšO”ú¿xú´ßNs ã†@)^&ïè1uSÒæ±=·Ÿ(ÿûœÏ´-ò¸¯ÃK}¡C;•*…mŒøbyâ@=™à¯ï1Äô e¼|ÍU†ÞÿwK”Gb_PåÊô\sâ#®ß1 ü½ÁسGÉBù$êZÊÊ#ñ9·øåÿcUív¤o½ùΫJÞøŒþ–îöLˆsNéèqëw#í{ô0éœT*Xgו>é _ß9Ú!·îyÉRñl{žï<àðÌ+Þ‘’±{ËOøÃ”š²MÞíÒæ ÕÝÂüLM±àû|®žwÞwò>ül5„?4ÛM^k“”¡³RÓ¼Öí’¼`h¡Þ›yøÖ£d¹\K§Kè!î"ŸÃ¢ý»OÚWÊ–a­ãPJŸµ½:óŠXU¶>Ç–{ñÝW.\¿Ùå:”sŽ0Ôµ`ó9YÓàŸ‰Þ Ä?öx¶ÑÙ(¼¨O·U³°€ÜQŽÍ‚Áñç{oìw:Ï׋G¤ùò·Ôãö<5²øÕJ€Å·'|çª0\§w¡Œ'¤Ó‰ÿé)–À§–,ðÞ0óéÒzK“p]ò Ä:„_ÑÑ;¤x'®AôÍn!]÷8wŸâ¹{WÀæßxw{ãØ4”RÙßB³¥®Q\…tÒ)Táº*ÿ|gåý­è:Æ›f¥îWofG¾ÃßÄÛô¼G7¿ŠÜzAŸË­Ûbßç™|6Žö"\ŸßQôaÓópÁ‚g©ëØ|ýÆ™ƒÀö^ÇÇž«8¼|÷Å}ÿ^ââ’o8ñ}k_Cš{„ç?øS(ý§î/¦+‡>Ù=QQ–ôuÑ•W´çw+Ü”ÞÁ ‚`p!]¥„ò±ùSÛ‘ëþ‡fk7Ó9öÀ±ûOæ:ž_Àõj×ɬÉʦži„®ÐF(a‰âòþúIÀû'ÈÃÛ9th=?gh Ë€ûÁUÈ¿JBy†s|:ÄK…G‰7ð¡!ØùeÕ‚õ~üqM……lí§:™9ø@}c/+ï¶=î-psPç„8l=^uNØBûF$$ßlʵ’ˆyÂÀ£l‡?Ç;Ãíá?{rÉ~|þäÍíš8h5X+ÙU£¹„à“w&T}ËYOÇ[Ÿo«•'òðàù\£ëª2õ6DnérHÃrò ƒ8ºu¡Ô‚üËÓQµ,l_d}ŠÆþU$×ø(Ys\î¢Ó [±ÿ§ï¾2—íÚ˜‡:R¯ÖÀý(>[O+3žŸ7ÂÂp•%ýœµ™òn“ºf=œW$éXß—öqÎI=g;'l²Ê€»zÍÞu4Ê¥Æmf¨Ìue¥óǼR÷+Z¢,ˆü£!¢iä’Ü¿ò¢Ñfá\Ÿ¶vöñKo¼‘¾€%}éÆe*[±µŸþ#ðh¬Ç#‡_¬\àë}1^*\î6Ͷ[ÛÊ›ã>Ø8+1aìˆ'’VÀÔÿ0¬åœàûÂ\^Òzµ¾àWûþûûàðŒAþ e#OÑ3¨¥æõµoÕ¤.Z®q..²>ùÕ$;¿¦¥žküša 2£ži÷L´u%Ï šÊ¤§ g¼0z.À0¸Z¹+ñþ߀@]ëã+Y*+ì[kVíXÕòH¬Ëèq¢*¶‡(GÄ&X‹ ÑØ¬;2ß2"aò ©ôûÁÅl¢°þКàµ1”92aRŽjß,ÅjéÞÎÿc†;9Å­«çÐÊ=±µ=þ{õ©]¥t÷Ÿ™8q®7£j,àxž‡àµÍfoú±. ”áâQ¿¿š¿³ÒY*¹q¯‚5l¹T:Ý1OXŸ(ÇJ‘£‡ƒÃ0 n±Ø›\“ä§ _lqAßKgbG;íÔ2<[ZÃðv£Mjä+Xì¶w…Û=">y|”=|Vž(lY¤»øÜ¹U´kñ¥íDó'NÜ?"aÒ-øLþ=|oWÎHœ@ ÐIõ®÷ªÔÊ@ï-Êöé²ÒŸÀÿÙ¸„)Cm;]zÎÝpß8SsïKõ¬§&,†Î„‰ÿ¿E¯%Çï.ëÈÊŒ—¤×©±iÜÉ«E»æË䞃WàZéÿ„וoä.Wæ~…è]Šÿ‘²úY#Ào£êÐì+±çÐ%x1…|_²þ;¼q+X ÑùpÿŸ«ÜƧ£¯òí§‚dõb—9òžru èÁ G ³ŸßþËÃÊúâg÷¦¿ðbWxÁiÓWY¡¢žTŽ{â1üWŒÂ½Ó×ÔVì?„—y|•8>PoB†GÝ‹[é\|eø ó®=:{âĽÛl[Q¯Hh?ƒûÐÛ;zDºn ¯T¥ç*ÇêÁNųð„C¼Õö§¢:PZß`ö`SAOMVÜŠxcÏ4t\p /öÎ.iÏd ÏC  âÕª2>§v±–5ÇM]­2¤£Å§¨S¾ÔÄëåÕ bvDgdèËéE[ñ¹þ/<¬^*/~YÛ§;Æ ö>ïÍ‚õöˆPú<<´ÇA”í²âãAˆ|E.ÄÂÓø£š KÚ§j×ÁðÝKqÌf¬JäÀú±Ÿe U´ ºFYyTwŽ.ͦâ]æ”;Ý­ÿu~!üfz4[Ӫ䉆"d‘¾(COÚ?iòp¨V8Q}HTà{ó3àðÊùâèJ*Èn¨ï!øñ2¢™n´Íé¼±/:Ôu]#L`q(±d^ ôuyzÞn8?kk¸ä\VA¹ÍIœ°®6OAÈ|D½”ÈYµ®÷*UH‰€•AVýPG̵¸Üp!YîrgïƒÈ}_Sn†ïí*«ž¦›zsÀ5îŸrïQŠG&àáº"qØ} ¾˜I6 ½£ø„]W>Qk¼X™û½è|‰ë2ŸÆwáÁû  ñ]Çð÷þĪHk{›EøŸk Ÿ–¨¦}ÔÐ×Þl|e™.vÌÂñÓKó·h<™`¥­ì\uh9ç$K×ü·²iêC<‡¦ìZ?ÊGiÙFvç£ëÇ/‘ñ_rðù¾¢”·¢žTŽÍÑîhò¾¨%cûêY%Ìsé±qhÝé|3 _%ÞÂýóÝÞ´3ŠþõX0k„}´ß¨'ì©tï@­µs~ÀõZ¥žkð¢6ÿé“qßÏÅg–^fïNB,®L(Žo@ÙöÔdÅ­ˆg°õLCÏ9ˆäÙh”t‹dë ~Žû"ðaÛÀ~ÁgËÀ†®Ò"SSÝ*…Î\tZÇ#Ö§ûꔃϱòþÉ“›¿Û–ÓóÑyhØ»F—nÔóÑ= Éw¢ÅÇÐàõ4JC~çxþ&Âu°\‹9uZèvï‚“ÿY>½ÆPr31>÷Óg'M|Ž6Ðs ½"íŶÑkÐd³LÃx“zšíÓ;P‰‘g êe6ä/é¹f?^Æ®EýáåðôéŸbõ·ø”ç[xù_\òÑßÕáhÌâ%ª¬ésÐSSK«ºY~Vá!²vSÿÿ•á‰8o"ÇBXâ½)ÔåøI>¯™sð5ñox9óò^.óÈ’žiá…£ßLçÄÈc¾©£gšxïË+µ}‚q`ÂE}{4%MA,PÎ9a>½Ñ‘Ðv V î/0®}ˆ†û×Ôù–èÀêP¨× |€/òu1(~C¤¤è¸É‘½™'=Èg×0wÓB†–ûÇfSTh ë7‘­Ú´Xç»7_KaÈp!†•rc(±xîòë¯åWœgÔ4/äaÕ¡¢¼ÈZŒxæ§A+~uæÇúƒW'ÚLƒkÞ4UºX J+¹–ù+§sx!ò¢©ÂPÑuUaÂjì<Ñýj=È«‘u©$ä$ÄÄRÛxåĤ²¹¨1hÀƒvâ{ª¤ ËÅ48’¡ËÑø²·¢*aVÒÄgð<¨\ÏG ²ävÅ3Ôáø/ êáûˆ2ðÙˆ„¤ùxs …öˆÏ,Wà ò1¶Š|ÝÌ®$pGèÈÚ£÷‰=ÌŸófÁB@³µ¡“íFh¥ï)²D‚Ïè‘ É?ã9ðÚÈ<—áY^ÝžT¼¨!’#°ÒÆÚ Ünx9À…'iât|ÙxË“gŒ†0{ånÁ»óˆ!´ÿD½"!Êq½ÅT¥çšAOMu¤gˆäKayšà1ŒžùáÌøøJ_G¥€ðJµÔŠPV¶uBÇ { É¥,±,* Y“µ:Î9«§f¢>–Ïç©Ci«0¬Ó„W“&î H¡œi%€Æ9ë d/ ä`8åõÌŸófÁB WëÞ;ÖíÚ˜ÿÞ˜@Õ ÷ì¾õc»¨jþ†Ð6cT™–ûìË[)½æ=Á…àgÔ¦ãBÉ—d4p{éHNѦ½ú¶«i*X™«Ý+Re{®!÷9¸^äŠ~1*¶Ê·r걦îôL#_Å‹L"z¾iå)TÁ•ëüª¶£ñåÉËU#P+B¹ëªU[ÒbûíÂEÙ±jÕ«Bl©Ž¶¯B²`ŠZ<,¯øu¢‰(—úÄ]„®Ï.7B wÀzá¶Ù—Ô0NÎêo”žÓ6ü 䵬ð ŸQÔ‹±)ôˆëÛŠîEž_b…ò$¡‘÷²"¢7¡½èùèçÔ–Êãî:óéñ+èËlfNQKüD‡N<ð…󨔫Õ+Re{®1Ëâeø[Àˆ³ÛÚ8†|zH_ÖÁ#BBf$=¾¹&u(ïœÖµžiG;FÉv§ ±2ÎGàþÿËãÕP·Ã©ÖÂ{+IJݪ}°ü9c&dúÇö\µý«–”_÷Ÿ·#3`ùsÆL èh{FáX!@ß=Ñ!£!J/Œrù½rÈËp/ÏG#1ô>¢Ö„ÚC¨æ€õ|$…¥”g:ºC;D Ï ¿@q/ÎJOsQ“ÞbL—ÈJö\Ò²ÑS$gÊs¾,ß¡W{DÑ5­¥/+˜.'èI*˜z¦¡Æ¾R³_c‰nâhΡॱvÂŽØØ6è- Våp—ˆñ­®©k‡û;_Î 3”+[S«é£ýBû±²6)Îðõ_ü˜%gÅ‚šÀ s• î›à~ÑÍß…ÛÅúMöîgŠÆd>wî\ÛÒmÛšº‹²¢ABNÔ“Ê Š)s7p0ä—FõxT½ÅÀr®e8&µ¢îéÊbˆ:œˆ'÷LSæ%Ó 6ÖšP&ši±ýŸ@£„§üJVŠœÐÑ«Ã/ëØ¹Ý¯`9³`'°ýÎSÂÌßëK¹Ý>UçðPÿ`à7ûo­NZNÃê2žÏmø;©ùʯÇWˆ½ 7?Òû'¿æË™1&P+jÓõBt‰n4  ÊlDPÝ£Å#ý)’©_^4ª£VÁ' ÔWâ #!‚¿ãQ™4šPEeyé¥P²LT‡÷ÕmßÚQˆâ·á!L]äù%@$ï ¡÷V53²DQߪUMÇñ™@0ØôHŸ¯q?•7(^µªŠÁªžc‘\-tœˆ Z°eÄÆ6/úRtgv@^ š&㻤¬M®jèˆüF¡þëpÈó^sN\k¥GË^úŒM¾`ù˜~“Zþ§¬–¥4Ô'FÍ›Ñ= »quônåcÍý~c[i(P—^t%ºŠ¹C{faîÀ¨KÓç$NüÐ*“æè>¦ZÆnÔ”=+9þß}¼\ÿ¤þ½ÕMhƒ¡cE^Œ I‡v^ìç{ÿ¨*%´V_ 'ÌÿÒ(qUM[Ûñ©OVŒÄؤ¬{¶¶ëÂåØYÊ‘“½q|ƒ¯¨iíÐÓ'·<Òç&§Äh˜¨“jÕ¢L„Ú¦¦ ösñP^^mbh¼§Iíþêˆd»£õÜ‚?0 W|4 •íy°¦ÛÎ>´/F ì–~4FñYiñiîu)B¯œ?#ü wÿã-þŒ7Ê™4_ïöP§ð¨Åv Kz6ê8Ô.®“ÊxÆô­ò©¨§@¼ ñ¾Øg/Öc±_ïÿHò8Äìê&v‡}HuDòÈ„)gà¥í|;ÿUÝòk3DòMøÿ¹¸6Ëä²êÔ‘ÒÓë‹ÿ«I­ñ<™Ñ·cŸ›Y$ׄ"§e'Ÿ@­twìa’XVÆ Ûž›ù8,ËðɶR®f>è&CGÞ‘\ªŸÅcË(oÝ!ìï¿’øøïèL}±oXhñÇ(?°†\ƨdoÃZœ€8¥ãûaö3aiØ5+é±­”ž†ÁÄp–o’•ÊwÈ\Æó¸Åmèì=*V°4§@݈OòØöf7ÁêÚ6"!ù.|ê[ëbSZçÐ0@,•rm‡è¯ü5ÜS—Tú¨1>:âÎ o1îôË­t:߈Ê=\iÚa3ûϽ SN5qÜo´›^­ß<>Ôçàe²=®I7^ø~Ç5| }Õ1”º÷R'ˆìPˆ’ ¸‘ŒAM”G ’šÇÿÂh!l#ÚÚ»}›¡o}BœÄK¡¦‰±3ŸŽÿœÊ(/ŸYONXƒ{üuD¹åæàÞþú!ùDÒUÊ#’Á(_iFÙbä gü ¼(Ã*/’ð‚‰:ν°oGj2T=ÕCÝ%@bµ¿³Ç´õ 1˜Õs¸öZUáhþÄ Qnz¤÷§›«ˆ£2&œjÝ¢la‹ëd–öÐ.‰x¦[ûŽ›KóOë;l¿®ëUÿØ%e]µD2åKqX“ðœ<ÖŸR¶UJÛe•7ˆxˆw°Ö­¹!¶xè{ã™Û¥ú³P´·âÐÜ_ñLßOˆxiÿÄ7ZŽsNêI"Àj=ÆùL{ÙpÑ¡ù$ƒ}†?¶,^~çíNøõKñ•c¬Ysq“ QÙ]Ë!ÎËšÒsÐ×î«®H¦¯†WK[ó/q[}àVî;¬X¿™¬¶àŽq®Íz&Dh3»½émÔ_e&£OÐëÛÚãOý¿I“òÅÙΉ«qÏE ÍýÉ‘"ÊÑ­­}ü—î-· w覩&m7†x‘DxEùÐ=ŽÞ‹°GöuDjçᎼîYOÇ…]oCˆ'aà…3H$ÇM™Òb~ŠÍ«¼#¬/\­û.\¿ù|ë8xÞp l~´ï{aªEW\Ÿ÷ãúÅõYNÀE…ÿÜU¸îFµm}*‰ärbòf&Àê“bQöeÔeåJê ö švœyfgeõ5”„U”Ndi6™ѤEjë òÌt«×ù&÷ß²1šM—\ÝŽ°<å.<®±žf¨|¥ê,Ý'}¦çyÖ@ÄŽ/±ÆB j)%å€ó!Œ(µÏ“Ý#¤>A…x\Àj-|¬[ZuÁ×P¸@sŽ;ƒŒh;p…Ô´™òóŒé*W¤ý°nË©Ù`).?ŸYOOüò|têÔH·ðüZävŸ‹Õã|öU¾~î±/0Ûn_Aéò ¹qQz ›Àú±­éÿþešúNÝÖÒes †§#þ÷ÃqmçãÜ)Bì©›îëu˜H±™(p`õ‡ÀIʾ(OYµj;Öiªõûò_0{{’° wŒGˆ#ÇUD“)CxãïWÑʦJÇõW™Ì¡v­«U]×»à‘ Ó1AÙwHáÆ¾â`vŒ®'u’¡Úk›9÷C<ê¦NéÎocoóß¼G&$M…X/œÿ¬w»¡®ƒ>û⊷ÉPˆ˜»`Y&+Ú(o<^`~"0fÒ¤…ùžXÙ!!ðm l‹¤¼ =Â܉Åùmµ Köɇa¾Riê§¶Z;²Ò êIVäç %®Ò˜bw\øò„ÇÒ>3HEþ¡Þ@_llR33iÂrïÆ’…òòHWD5')~E…?òx L[ œÎá…T4+qû¶Ž‰#‹·ÿB4_FRÑÖ¹Ùèî.øa²HöÅË~#à*0nƒ;Ç'¸æÒ^IJØNSLL(z„PßáûŤÞp˵ۻ¸w÷y¾×1îµÑ®= Ú·xõçãU k¿£^«É!AIDATAO5#¬þÍÉZì½OËËGª3Ðö!…òKN&WK­2 ¡m‡ÏIgkŸ]¾Áú?î˜Ô‰¶ÑË/ù-[ûyΘ` —[”KÎýÌÄñk B?ÍÐ7¯‡/%}²þ=´EÌë´Ûg\«Ó$,šR!mwCŸ‹x9h”&í¶ïÛð€ò¦@=käºó&C<ÌГö üâB›‰ýO•¬ðŒ Ô¥îÔ4ûpߦ›ëíמ¼›m†øVÜSuýÈÚ…ëØq„ÿñxø/Þã^¾IºÅîƒùqñIx¯ïÍJŒÒ7/kY‹²Å빞7ñ⸠×|†[ÿ«Y†=çl4tÍ(/4ú{Íc¯!ï‘‚/”œmY”q¿~.t÷<Ô'e8g&Å/Æ——WÜB­CþéîäŽRWcŸyÿ[õà9`L€ 0O€Zñ[–+ Y˜hœwkÝškuòw<«ž3ºF€|ã!:·ÜŸèµÜŽL˜t6„ëlŸ— ¯›õ`ÑšfZ~+8P§óͰ8ç´æ”7E«L>cœ/Å”—当&5óÝGùÞãœÚòØ~É}ãð2`L€ 4,æ§a2-`&@Ýf¸—¯³;l×¼æ¿^*ež¿ä6pÇ8òôJñòÈÙ³íj÷¿áèaoÙ‰zϨlÝ ”Ÿ÷G>•-ã1&À˜@Ã#ÀB¹ás>b&P+F%L:×ßbôŸØn‡0­håx=³À*ô 4${Ã' ±»Úf“ÍtNÜX•Š‘uÙùT¥LŽË˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&¤‚fd¾”Y±ŽƒÙë aôUÉK ‚‘¼²mB¦ÙBBV\ü@Á® eX©jŒO¼L“öƒ3ǧV*AŒDÃïÓ— 7¢sg—Õøõ'½Õ#×-„êŽû©)nvæ4©ýÖÔh¹bäÈŒJí׊qfL€ 0&ÀêûÉ®ç÷ÓBzz<úÃ2W߈‡x£RõÁÆ¿õŠož•k¥³Ã[t|óüá; KÅ«ÄÊÈøgOªèA%Eo‡FJÈ6M|:óéøÏ+‘¼ÆQ !ÆàhR århæF¦„z2ÅlYh,EÊåpªh³RNmÊo0 c´ÐóÎÅK§÷e·“ å‡äÞÂI³´¯„]›>áný—Šòä}L€ 0&À*ídxʬ¶óŸÕ^Ñ ÷F%ÔˆãDò±S¢ŸRêµüƒ;7ÍÖvű»+ZñDÒ?•p­URµ‡î~Cj2A •nêݸ‰I³*JÛÐöÅÅ'-lhÇ\_ŽwòGì¤9O¥B$ˆc:ÏW$wŒJ„á¾»A鞟“gËO’çD¶:.o`L€ 0&ÐÀ ü{çEuïñsff7!á €¶"Ы`µÖ«  *à«>nô^µm @ HvÙM ‘w¢õÞz[QQ‘( EE{[Ѫb+äHÈcgæÜßÙdânØÝ,Én”ä?ŸÏffÏãÎ|w7óŸÿüÏÿÿ½(ʯ$þè@ùþ÷,!2 ¸ª§ôvž%¬—×ÏW¼Ñô{hÁ‚d˜¥—3Á‹‹=Ù·{ÝOÍu¿ˆãY*×®a\Ü?n–÷úhdµõ6ãÝù <]ÚÖϳ-ž_n‰ú€%Œmø=ýì”ÏO°Û¹8ñAþcŽËO¹/u D€"І ´ºëŦ…‰çUûjÞĽoK¸Âºœe¹Ó˜éÖäHrŽ«¹Ïž];;f5n'ý…S]ž?š–xu¥°.¯W¸x¾Ð“]b·•~³ûŒ7ßQ™ªz²^šžŸßéÈq_Ü7FCùvp.JIêƒË³²É>©nÏcLák¸˜w .Ê¡”Ÿ¤|¦¹sÎgŒKe,NÈyMpmIIÎÌ÷Ò³=ã„Ån„R£=¹GOÒÏ.ñ¸X>eÑ¢‡Nü;”[˜a€í¿ãû? íÈÁ<š’’bÚíëeËy‹²9W§åd½>Ë;Â4}Orκ‚É.Ùž«ÔºÖEpëØ.¸PõŠÂ_ç‚ ’u‚ó­¦àÛ•YÛ®‡¢}¥õØ,Ë¡$¯EËÞ\Snr8øu¦ï„ÿQw}7ÑK¦tœãh(Ý(‰ü¦@yö±¦%Hò=ε›x¿ƒŸà–™'ëªã%ÌyTzö¼‹íöRaÇñx(—þyØår¯š&>G,Ø2Ìç°_Ë’ø…‚) ÀgÎÆwµÛú•dÁ²8Wf;5çÈz é_›¤çŸí<£Ó›h7ó(Ov$ÿB¾ú(C¶âüÊMƒ5XÜÑþ.ðè·•;m¹ê¡è—KÆãô¼AIÞÆ8ÿT㎡˜X*Ü\ú0*6HÅWöAÛÎ(#„¹søUÓ~ÚK»Ü¯œÛ2”d.á&çAR’m2Áû¼"í:f‰%Á¥Í{e9™qãÅüUI}š'z"@ˆh[ZÕ¢\î+óÓÅnƒÂõȆE ¥£¦Öì%U0ëÇÐÌ‚,•í ºí…E³Ç$}IçÇñ?ˆ,g¼î¼Rwíð·3Å}Píþ\¬§Ÿ€¥uƦhÚù…úÌÊút—Ç’7¤…–ãÏý}¸èŸÐ«ËYK'O®ñ¿ñg…>ýk?bWÁía‘!|ÿŸ—×méÌ_AÞ‹P$SQ?Q¶ùÚx{”gg_µ”áДÒEžì'êk×AF)”Ÿ+ðþ1Y+µ, Š6…O*Ùã‡Ú¯ÂÄÙXNÚÖá(ì}RE ð›ã-Ñ®u¥AŸc ÅRw"@ˆ §˜Y¢š:óZ_íd(dqQÌqa¿µtAâCÍŠán(wCÕÉ2DÊÀßc?Úç*[ß⻥«ƒu”î» u÷vΠ,WqEYf¿  ?ª¨Êu–’ðWüQ8Ûg‡ÚC¹t²½· ðÝTE(oaQá  ý ¸0ËQ¾±áFƒ‹í¸1¹%PîD=¿O¯öBKeŸ–‡=Ö”IIq¤ê„ø,Ñ•xÝ Ã¶m§ûk÷JWœnñ:}|FÊ„%Y÷WŒ×­!7Ãå9ÏdìªB¯û©Öït#CÏí_kYƒJæº×œns§ù"@Zƒ@«(ʯ.L¸'sf?¬6⢸ÏGˆ¦· ëó9H‹ý4æó>n./Â÷g=¾×ßÝ<Åi¢p®O^ÞŒ“øV›®{.Ãkn« Fƒ"@ˆÀ)À5.þ2ðMCƒÕ6#BÉ£3-Uº,D’/cÕÕ—feµè‘²tÛ`ûôZ˜™äiüÀ:é’29/ïŒHsûE!õu+ñdߨ·¥ÇÒB8ÍšÀ[¦ô¡.sìêQìrí·Ëš³—¾Õ¼³¨‘‹›ÓŸú„'à-âò†d@ø±¨áÅ®t+=’¤´lOnVìqËKÿ†ïl6¾ÛsœÉJÏPß/»]kìÓܹ¿†Ey%n–{·Æx§:¢æ¸-< {Ñ)ÝlŸê8áÚËöþ …§‚\hK D€4"Ð*®¸Æ}X6ù–-ÃUœ_èbõ'^¯´µXq«·ŽY€±mòm½RVa—1‰ní.$ùU“ÂN±Á2}FY¸.ÒZŒº)ÉRv`˜¹pcQy³ Äý7…»hGófgý/úÍ5k-MåP¸$<2$cµïX>¬Ð£q{ÛO(þ¦h,³Hw¿?AÏëçó™¯$:×ê3¾²ç!Þà à ù$H—àyGqŽ{‰¬G¸BÑXfÃGþ^¸ƒ V7û gæºÆwÎá’îHá’áH.YooÑ&ù‰” 7^„lœ†{ûøâ#Ä¢8"žçVâÆãj{,iq†•þ~nÏËì§2¸ÉÄ¢‰HJô€lw©TÄ€ŸˆÃóqÞ_àÉÐÊ’÷ [F¸ÏÀ®·÷ã<ž¾Vµ@Â$„‘ôºåSÚˆ íš@«¸^ Yy+P>1|ø–ˆJr+Ì!&CÀåa8.„.D}(ÅÅóźëÿb"˜„´%qÿMA =ÚL`×É~KÜS×?tžãØ‹plÄžÁÊP–wX†Ø*˜­ÐgîÆwÿ¸Œñ8SX ÏG2®9\zzC)ìa×oܱËãû„¢¦84¹.BÙ%9È$RÒ)c6™ G¶‹6ÉO¤EšÚ-çß`¾F&øIкŒäŠö¬ñW×ó~"Ç‘±Ë¡$ß/ÏSÙ÷mÀ ³u;Îͯ¼Cáž%9“7U»J´Ë¯LK!þ-ôg`×ʽto‚’¼I®½&%9 "О ÄÝ*%áÂÏ!Úâ‹õÝñ¡õ¤ãîÅirÑ]Á»ÞÚUdét!€El»à»~iœçÕo óè$“ô0“w1˜5²ŠÝ£ÅúCß6̯QžºTèæ5šC¹¸!¹t5ø6­õY3Ño,^«8³&aŸ'å !MrùÑš;`†;EˆMȸÊb™¾¾vã^P<ï±[GJºƒ6þqÐþ¤d8vÿÆ{üVÃ&ùi*AÑ =ãs´‘Oojž¾ÈXîŸ Ãò=ûÌѹ.¹8®a\›8):‰˜€›ÂÛøºßS¡„©LÅo ´MlxL©iˆç`Å,‡EöŸŠÊ3VÎq­…[@@Ïà$<¦0/ÂÍí³³>„¢ÜÐŠç«ø_/ ’´ŽOU Çëž+VêîwŽAI†~1Æ7Ág¿¡<˜”›Û³ºÒìEyKPÂÝúÿ Q'݉ 'X6lÁ’ü€E” Š‚¥¢_©‰ð|(-ÁbÛ;`h(ÅMóz”ýæñeFÞ0(½ûßý£4}áX”{&lÜA7ÔHNô*"ýÌ{ýãÏσœzË~ðg0ª…è:k!³¾ÊZ@:$D€´O¸.Åžq°£lŠçHHž±6žòI6ø!PÎâ:Îþ•™ZûATcpö|jûA1þ9­Û 纟o¼¨¶qüã‘7εÛ!!²×ÝP×YYùÃÇî“óÀÍÁo¤{Aã>þºZÕïO­¨VÐÂZ(ÖÕö9œBÒðÉplaõûHI~0ר5)ßnÀÉ—nðµFÊx¾F†eĹh_›yW‚ÄHÜœl “ðê:\’YP’ŽOÔCÁn0N4þ d¹ÁÕb0nZÞÁáOBó¿ü…ô‡"@üþ‰ÆŸ‡Rë”ßRë±ðOþóQçÞ ‹Ê3±MòˆÀ’À̱ÕÿÌ-â TŽŠÇ$á ¥Æj,ÁÛ™å;;MÏXRÐ/î×âñ¿Œèáß࿼JXÖ³Ý9ók™¸Fì·õUA»¥³§ï‡»A•0ø/Qñ¾]‰’rq›‹&éŽÝ/{¸p4 « 1VТɤîÉoT®ì´ßðÜ…Yí«ÍÜš’Â-Ä^g2k|¹G•ù-¿òfqâwÃz|-ä¼mϾÞ×Â=§Æ×»ÇvY¸=,Øådg¦Îò¼ÃLñdºžû÷"=ëãpí©œ"О´ŠEY=={.€\¤žÃSž‘Úˆ@»! ªÚlÜ$BWŽí™ß$'u][©ÁÒF úÉGPä^GršùiÙóÉX|6–ÒËU?b·.š“µeGkàw ËéÆ^÷^».p_¯Ô¯‚¢ý€1,Â7¦Ïò C¤Hj'“îq?"IŒ /ï(]RgyGË ~íbq,áíEä…8Ý=ÿ„dó'h‘c@é•–âaióæu±ÇôGæ|+ñ=Œ²çýñÛq 0å9x¤ S?ž¤Á0P¿q¾2ÆÊˆ2l¥Œßîñh·ÄŽ’a7´—Ùù,æ1á³ÖH6‘ÚR D ½h5E™sŒµ4€ zDØRи@¼výtã-‘#/Òò"Ö”ŒhÛ5%Ç®?y2• e÷ µOÓ‹’B•SYÛ$0ãw¾íPµb¯Ðreâä{‹'5¹À,ÁÑù×Pæ|Â2ÞªñûJîTøÖÞß[œWÝæW€ánÛ1HÒ³Ê.µïÚÙ‘…ÿ µú«H*tT˜Ì£*lJ`Û¦’î¶méq4 ŠÔŽê:„¶ÜÃ*|_b!cÃMþÁµBô‡¿÷³ö<ÎÒzo„õ·>ówdr »áñ–Á¹)è Ø—Ëqþ2Kèú„^]²í6ÑîÅ9½2ñ™”ƑǢíCíˆ m™®+­»­_ ¦âQjqŒFÝ£)ÉW^ŸYq ’¼ú˜¬Ï ôz\TVÉ@5'L鯑ˆWW\¤=E9®“”îhÛÉ9„KÖÙ[4òdf3( _cAÕV,|Z„‹©ýq݆ÐM¶,{/ ¾ì}W9dn»œöm›À’õý*¾ú!½ØÐXœ)¾s‹²Ò¬i±­Œºä=Ç»EɈ¶sˆvºþDb9;’$3`†¨n(Š”t§¡QŒšJP4>/¯['E1æÏ˜q¼%CÊoÝ|Ym+tKdQ_"@ˆ€iâû€ 3õ!¤Ô#2IH Æß“àL¸vä”êúÝ¡%¥fçþ’ s)|ýÞÀ³ËcŠ2Â0-ÅB™/‹=®þGŸÆ_-aH¡ž¤xGÛ.ÒX³kJ|9Õ|îМC°ä©›©™\ú Ö—oV™˜SèÉÞbËœ¤çŸ]íó=šûjCû‘¢l“iû¼§ºtÇ^†z5ƒ›ÀÊ™ifF½CóQO"@ˆ m„@“îñ8ÏÑÓ­…+·BK>Ôùð¡\Ï;$_Ñ”’,e«ªãEK %ygã±°¢ü×:°ÿ–åþG™‚½"ÌÚ“b´FÛ.ÒXc7%oœÛ{55ï–‹dF0{aT`pîÛ°ëÂ@y5FírEÑfÀ]5!°œŽÛ™w—IìÖw¾%Í:cΪ±xoRVº5”äf¤ND€"ÐF |/вd9*Ó|¡ƒÚq .̰겪høBIüXU”;ÇL7Œ™\q0š>Ò:,_VÚÀöÒ:‹‚î‹]®oÊa]¶˜8§á=¢m'û„ëTåÁ2x7æöT`?y,ç‚?×k̹ٮKÍöÞ3ÙQ”óð¨ŽèÃl÷¡}Û#05å«*¸L¤q®Ãû­hÎíLüþþ„§(ÎL·°XŽ6"@ˆ D @+†‡ ¶îxøCÇeæ®É[Wt™}¼¢âV¸c >¨ E±.âÒ'·–Õ=pxq’_5Í÷V¬,^sV$Ëé¿Û`é­ä;÷»Æ¢mØ'ÒqSòt}µ³Ì÷Ù Xè”ÙXÂ_a¡ò¦´2˺ .Ï9>K¤&žÙyXã¶ô¾}ÈJ3ÞÀ™_å-qf–qB Åo¨?~SÝqó…hiìöÿ€×Õ몚øìŒN”„nŸÐ謉 D€„!ð½*Êöœ®žP~Ç׿ìâ€=.ï2CØC¸ÄÇhCúØ ¤•ÊxÆèþ·ä\¶hÛ5thâ )yûŒÝ£p–ۖ꓃¢Œ›å¹Õ´ÄÍüœžÃí!°b¯ÇÓ–LšT‹Gx-VÖû³ØA²¢ýiGÀ•Z»“–¯0›üMSGÅD€"@ˆ€$ð½¹^üðÃÚV&­²ö\àvq>€4„h²Ë£mg·ojYžy7(‚Ü.Ò]žkL“éI”Ûí¸¨¹¹=ÆéjÈ*MÏö“/Œ{ÂbF‚ëššÕ"@ˆ D€D&ðƒ°(Gžbüjaz}Öàü~Œ0G&`¾ê1Š#a–‘)þ#¹GÒK2ø¤vãuÏÌpYé™qÒbÁÀ™¶ 'oz~~§£Ç|C{+ýïµûŽËλÄÆ2®9n\ìz¸ÁŸzyV–\ÙÉn'÷°è£]Õ‡¢^R¡c"@ˆ D€4@»¶(;µ.ó°Dî Ä,~Jò»LẌ4!“{àÁôŸ*T–Xõ“u¦!fš¬6UGÚÛ…“%ù6ÈxY×S’²X–¹e?f†ï]XŠ÷Ë—¾‘£:"@ˆ D€" Ò’Û83Ÿßµ¡‘ðPíêÊVù:7êæª](y¡úR D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ Í#ðÿ2;ÔòŠf›^IEND®B`‚neutron-12.1.1/doc/source/admin/figures/scenario-classic-mt-services.svg0000664000175000017500000006174013553660046026333 0ustar zuulzuul00000000000000
Service Layout
[Not supported by viewer]
Controller Node
[Not supported by viewer]
Networking Management
[Not supported by viewer]
Networking
 ML2 Plug-in
[Not supported by viewer]
Network Node
[Not supported by viewer]
Linux Network
Utilities
[Not supported by viewer]
Networking
 ML2 Plug-in
[Not supported by viewer]
Networking
L3-Agent
[Not supported by viewer]
Networking
Linuxbridge or OVS agent
[Not supported by viewer]
Networking
DHCP Agent
[Not supported by viewer]
Networking
Metadata Agent
[Not supported by viewer]
Compute Nodes
[Not supported by viewer]
Linux Network
Utilities
[Not supported by viewer]
Compute
[Not supported by viewer]
Networking
Macvtap agent
[Not supported by viewer]
KVM Hypervisor
[Not supported by viewer]
Networking
 ML2 Plug-in
[Not supported by viewer]
neutron-12.1.1/doc/source/admin/figures/scenario-classic-mt-flowew2.png0000664000175000017500000006345213553660046026064 0ustar zuulzuul00000000000000‰PNG  IHDRކõµîzTXtmxGraphModelíZßsâ6þkxLÛ`à1ä’¶3I&3éôÚG…­‹°¨,ô¯¿•½’œà&9{%­äýv?izÞårû›$«èV”÷Ü~°íyßz®ë8“|iÉ.“øþ0„’Ø)<°ÿ( û(]³€&¥ŽJ®Øª,œ‹8¦sU’)ŦÜm!xyÖ ÍŒ¹àaNx]ú*ʤc×Ïå¿SFffÇŸd-32 ¥XÇ8_Ïõé+k^£+}Pï Œ(…5új¹½¤\ÒØ(³ÆõžV»HIc\Èó&ãlÄák|ÐK±\­á€_ Ôç m:“pê«lP¢vÆ:›ˆ)ú°"s}¿€N‘Zr¸sàr!b…½i@’ˆê%è›DIñhêdÅ ‹ÏBIÀW¦ Æù¥àBBc,bÐ#Œ :MßÐÂÉŒò{‘0ÅD ms°…NÓ'*4o*”ÐK%œ…Ý/°a&”KûäE£Íõj¼Ý" á@Å’*¹ƒ.ØŠô1&è'›Ü«Ü!v‰ åöQHГC«9.ãf¼}¯†÷q¢H ÀµC:õd‹ÝÜ3 w°±ù è ÊèqÕàq Ex̸£ÐAŽ, sGÕFÈGþ)ÉbÁæpu­­éöÏàsEZ¿Sý•a§c­™ÿïZÓIÚp–¤Qx¡ÁöWÛ¼1Ç»êÆ? €f²ÔΛ•e`ÙÌûôÔ< Ñò‚»”ヽ!þ:Ä’žf* e‘:ß·d32Âe4‘SNd=ÄxÞœh8jp"÷Ç8Ñ÷Õ‚¥i[Þ ©"Š˜ð«\:Í£º_†„n™ú[‹Ï‡x÷i‰aa…&}«Û´zÏÍ!É5‰í?¨R;´9Y+¡‘²ëº:ꈾŸ¾j›Æà%0&b-S¾Ò‡ûŸ"2¤ØÍ£­ö,Ø’r¢ØSù€ptx˜èºNAxÎÔÍÏ4i½ÄèÝ‡Þ –rFᤄ¤r£!UP²«hGÜõcÔ-™?)ØÚ?_¼aZâkO‘-ÀkO|†ÀŒÉGõÝsÜÀ{^¼çô¡ï‰‰ÏñKÌw>´LxO%ƒGЦÿXŒhØ¯ÈˆŽ‹“·ŽØ1ƺçà^v bÁ²*äÝVºœWÚNã*Ž–)|-Øà¯›‹;<¬gM,p“…¦€79}‡’±§óƒ±ëÕCÛ=J¡]ðUûbÝ ÷RüÐi>d©ö„ܾ8‚t|ŠkíÆM SCMAGè3D,³lÏÜ.Šýߘ…ÍNg|„ñ[Ì1Qk9Å삆ñS÷~‹Ôÿƒ®â¥Âà” dŒVª5™Óˆ9–Ù`= 4P–IùBŽ+kÐþ«²ÖaíÆ{ÏÒšãÔc®P[kƒõg¯­yïY\sdƒ_…‘i€cB©œ`Šu00€WNÚn¿jE%Ÿ¼Lb!hmíCà ØAÝÕLìã}ù¢‰÷žUëy§cÅñ¨Ì‹9M~ت‰eÆ×•MöÑ¥ÉÛöðK+Õ<üÉ©«Ò‡µÃçª}T.§,~X/ª˜4EyOÖœDd¥…s.Ö æ´ÆªVŠ<¯žøšm¦h,¿:C‹íÄ×;]æ ·ùßU2ÆÈÿä]ýQ[i IDATxœìÝw|SåþðOÙ³d‹l)â–{õ^ñºðêÏ ŠWE…2Z6®K¡´ ¡lÙ{•™6-«iÙeƒ€€ˆ‚(ËQýþþxNnNÒ¤MÛ$'9ý¼_¯ï rÆ“sNN>yrF åÚ¢ÕeÜLÓZ7ÍwJléŒSÀ8Öûî<´`›® ojz~<­|©{®^Œ[¤ö²–i¼ß6Ïh™Œò2¼ß;2f1Ý*µÿ$XÇrnƒór°W7~¶K;%œÓïïÏ…&"¢ÜÓw@¼›i|Õq¬­µ?À»yl#PÌÖq, õÚéŸë/Ʊã8ÁÚq¬œß·Õü  €Wàüe´´nº²®éÆ ¨nüíp^Ï{|³ 9ªÐÉ&""C¹vÿ€ Q=_uÿ¡kg3TÇ*Xùªã8Àv]í‡óöÞï2¾o~܃:ºçÌÐ@#/Æ}¤[¶Gü°\úŽc&€pÞúzH›§ twÂ󶘈À½¼yß>§¯à8¯G=ÝtM\Æý ¼‡çº Œ×#;¡”MDD†rí8 €…p>šÁŽcÞ:Žî¸Q©é£vsR_÷œ§÷rœ¿é;ŽÛàÝ>Q:ŽÇØ×!;Þ¼ogø@ ¨#ˆ‡tó<«›N4Ò^÷éÆ÷Ñ ·zx.¥l""2”»Ž£¸W7MvÇp#œ×Æ_`ð€nšìNÙ¾çkë×Í×K7|ÔõQ€sçë€rÚðB^àw8ŽZ,ÐÔͺ§»<ïÓŽB]ƒUî;ŽaÚúê–uÓ¶'Þv³[¶"Ú4M,pA›î¦¶ÌQÚòÛ—×Ó¶Ÿ›Í¸9ÚüÙuÒŠèà 6þwût‚ãµÊ‰¯;ŽÞîökò|±ßùJ~:ŽÞì zw˜à,ïÛ­ÚÁ±¾Ùí;ÏëÚ*àŒÖžÝhÝ´ÃtÃÇkø¨ý¿“nüjÝ|®§Œ½É­#iôÇݺÿoãÚ#OÇÊPpîBW¼­M—S8ÇéÒ-›E7ü*§­ôË“¦µ_êô§e±wRõÒuãô˜ðÜql§ö=Tg"7òÒqt]¶"žÌa]“´éüÕq,`M6Ï¿î;,®|ÙqÌí>à‹ýΗòÚqôv_°»ê’OÓ¯‚zï{Û©j  k£öOÝ´[ :ô…áȘIÖjÿ_¢ÍSÀ Ý| uíy›5þZG""Òè;ŽóLÖ=~N›Æ]DZœïžÜ  'Ô)+}ø6ÐÚ(縭PwPðwÝðMPa^Ài—¶ì×Ý}¢¥ kï2m:T'B߯-8wÔÒuã2uÿ÷ÔqlLJÑïÈÛéë¼t]—­ÔÑG}‡ëS8­-´¶Šh¦~@ÔgvãÏ4ýQ9°À8Xwób{x{ãsºy<-Sn÷_ìw¾äí5ŽjióCîö…ÂP9ýsÄ@ ÔïgÏêÚ÷ô¾µ{O›·ŠnX¨m-~€ºA¦,€Ÿµa¯ÂñÚÓž§"Ô 6u”ÐþÅ#·Yãu$""¾ã8êîÈÚãSPwÇÚºaçá|ºöݸYºáÿÐ ×_G¡{Ρ>dªÁùCA Ž…Aí±kЬžñp„~Y8w‡ê–']7ü/¨›Bî€:ºázã¿ Nǹvªs+/G×eÓß|pêCÎ.Q7NmY^¯qt×I+Çé?ð¢nú§tÃÀùŽYw¼½«ºKË”—} ¿û¯åæ®ê&Ú<¹ÝʸÇ~UQ7ý›ºé§è†{zßBû*Ô%ú£š… :`öùêBuÀõËoo÷O• N9ÛÇÏ×µU[7Ü›¬ñõ:‘ŽkÇpþ0†ºþ˵㨿È}²K›wêÆé;"žÂ¹Ôû¸†pœêúÀíÿcµç¶_e_–Zºy¯Ãùƒþ­ÿ%péºáq.ó¸võÆ)È»¼tÝ-[U¨NŽýw7+AqûI7ŸþT›/;ŽõtÃ.ÁùçVJø êää|ºÕWǼìùÝï<éífù3¡öÿìä¥ã˜Û}¡4T'Ù>üÔüu³Y®ì:Ueµçìf¾¾ºùžú‚!Pׇ–…s'ýauõ_Fr›5¾^G""Òq×q ‡ã"ûŸ¼¦›Æþ¡ù_xþPÓ×-­= ûpÖŸú|ê¨êÔ[¬öÿÝpîä,Óæ}H7l7²Þ˜¡ÿ€úŽýtÝð']æqí8êë{¨£Uy‘—Ž£ë²jÛµêD]„ûåôWÇñoºa{àý0îøêǼîùÙï¼Y'}µÎa¾¼^㘛}! Η£èë¨ÓÜ®¬ìÞ·÷ÃÑñsÕB7ßPc´ÿï…z}JhÏ)z@e´O_C×Nn³Æ×ëHDD:î:Ž€sgqŸîÿöŽã(xæÇ‘ìÂYÿ!3Ž çãà¸þèg8ßœÒN›÷Qݰ-Èúåuããf‡tÝp×õì:Žõa–yé8º.[!¨£`úvþ‚ºöí n˜¿:ŽúKÒsXßœøªã˜×} ?û7ëäïŽc^ö…bP‘å[Ë)p¾Ë<»÷íÇPÛ3Y•†:"-P§­í7ÆLÔMc¿Á*êÇÀêŽký%yÉ_®#éxê8𲆭½ã8P7ì#¨£žÊ.»p. u$H .–ÿ^ûÿSp> ©¿è¿–6ïݺaî>põ§³ÎÁqXºnxNÇÕP§ÛìoÁù¨ˆ·|ÑqÔßÌò Ô©<ûé÷™ºqþê8ê¯EûÎ×¶êTi5¨›%rúöUÇ1¯û@~ö;_ËKÇ1/û‚]ÔvÇ‘?{íÓµãé}[êˆóx~ÝVjóÝ„ãç‘^Ñï¡ ÓŸVãÒF^²ÆWëHDD.ÎûUÔï_Ú§¿Çé|OïÛJPwÑwÎfù\ït8®ÏÔ)n×ñ®û{n³Æ—ëHDD.²ë8†óoÚé;Žåáøi ºù L>C7\ÿ£Àúp>ˆ¬wܾåò\öއë­u†]œ;ç >ÊBý¸°~¾ëæK‡ç+O9Ƶ3Ý ¹ã‹Žãºqg¡Ž€•EÖkÁüÕq,ì²|PÇò–ë†Ïö°nz¾ê8æuò¾ßùZnþä  j_Êí¾ðOݰ kÃÃtÕÓßïé}k¿ƒÞþsEîÔvYŽ_à|Wt%8~‚G þ¡ëM¹Í_®#¹È®ã8ÿ„†¾ãÝ]ÆÝryü œÛ­²~ˆè¯3ªç2ÞªçúAèzÃHM­=ɦRàü®çmDZˆË|ë»›C|Ñqlˆì×Ó^ïéæñeÇPY(»çþÙßÅjçËÏË>äo¿ó¥ÜÜU-P¯[n÷…ÒP¯qNÓ¿­[.OïÛ)Èù¨rq8ÿp·ýÆ»bPGqíãwÁýû)7YãËu$""9uÃàü×8ôÇBPÇ» åÈúWUŠ@}pë§sýÓeßèÆõÖ{B7ü¨ß3tÕêÂzwË3Î?ä­ã8ßÁënÞìø¢ã ²®ãT¨ô¦öu_wu'¯þH½ŽÃý]¶îøúOævò¿ßùJ^:ŽyÙêA%v׿_P×ê;oîÞ·¯Bý•ýï-z2I7ßD7ã—êÆä¡Üf/Ö‘9†ˆÈO*A>ú@$²ÿ‹*E¡.Žÿê·ùªd3m^†ú‹ ý ~[® ¼;òŠAý“®P×q]VI¨íÝ@¨J÷×5€Þ*Hû€]^ö…&P×~à}¨÷eÓúû}ë­Üd šëHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD> @tñCÛ ð¼Ú&sëc÷Û¡Þà^ƒ–ˆˆ(èø³ã˜®µ=Ým“¹¥ÃØ}‡G"""7Øq¤`”v‰ˆˆ‚޾ãXN÷¸=€Õn¸ uÚ°˜n¾Z–øE›þ[ƒ”PÀ×p|ð €ãЍ`…n¾ïŒPFk77ËP@OGtm®›¦$€‘Úò €Úºör]< ð€ýþð+€&ºir³.®¼Y¦œ¶eU8¶ÿk¶ø Àžð$€CÚø¯<¢kÛ›í¦—›uÍ®mwûÎi'µÿÒÚ¨¤{¾^Ú°»uó4Dî_£Xà€;‘µã¥=Îp¿‡í@DDdjž:ŽöõÕI›§ äàŠîÿsЪ##P»ÞÊCuìÓÞÔýÔ½·ËPH›Ç>ü7Ýÿ­Ú2°Y×Ö)Ý4q^®K!7Û, À§ºéþtYÆ¿kÓy».®¼Y¦p/¶¥¾ãèMý -³7ÛÍ•·ëšSÛ…à~ߣ=ÞÕÁl¥›Ï¢µý¶öø Tç4·¯Ñ—Ú¿W‘µãøÝãÿËÚò T§–ˆˆ¨ÀòÔq|F7MœöÔ…ãCõ'Ÿxª¢—çÿ¢PGïð€Ap5ûê4¬·ËÐŽ£qöS³…¡îÂ}굉pIuwŠnÙÛäb]ô^…£ÓV^7üy8wî¼]WÞ,“7ÛRßq´Ÿ†®¦ÖJVC7ì^x·Ý\y»®Þ¶ç}§TÇN´¤=ßNmXm‡µÿ?‡¼½FuÓé¯q´Íýj»Xž:ŽÿÐM3 ÎþaPÀ®×¢éîY?ü‹kÿ×Ož;ŽÙ-Cíñ1x¾Vpµ›åÓ×»¹X=û‘½SÚ:ÙµÔÍ['ëâÊ›eòf[ê;Žhmë‡Ù¯Ýs½Ä›íæÊÛuõ¶ít8ï;… Žd €h¨£Œ‡ Ô†½ à¨k8«!o¯ÑcºéôÛD_úu#""*pòÒqÔk`€ƒp|¸Þ§K‡ó‡¿ý”è/PG–Šh‡¼ußÒ_†óÇg¡NÇV‡ãÈ/·Evë¢×ÙÍú êæ¼v½Y&o¶e^;޹ÝnÈźzÛv:œ÷Àñea›öï,¨#©`öï—P§ óûé·I›öÿƒpîˆ(yé8¾¯=> B›Fº³µ6,]{<_{~^¢õ Ô›bbÓ5‚:Mñ9œÙW0ÀWZ;—¤xÊMãµiàGíñw¢´v߀úÖ— à:TXUÔµaïì‡ ¯¹nhíìÐ@q—çµjãë»Y¦ÕÚ¸;¡>p¶!kgçyÝôÅ¡¾îð;€?´çíu Íö î `¨öÿUnæ÷Ä¹ÙæÐÀ´éOAmï¶pľXÇžðºV›ÀŽ#åóŠyåë¼* à²6o97ãïÓžw‡m{¡ÞçaÞ*Nkó_Çu" »Ëôö Þà'Ó´yìÓÛÓ`2Ô›ÙþMÏ~:ÊÄç \Øàè˜ØC­¨îy½ bx*äÀfÝà¸Ð¾$€ Ú¸ÓPá–uµ}9õÏë‰=ˆû@…Ö1íñ+.Ó¹ âÜnóà¸îë[kœÐ¬Aì«ut5ì8Rþ1¯˜W¾Î«Bî‚:‚]ÈÍøZ[ósh‡¨À°_¿Q%—ó…X Í; @ ݸ@}{Ë„óÝö þêú7»>pÉKºáµµ6n(« ÓŸ= žnúZÛu'œ]n‚ð|êg 6| œ&Ž ©_EÎôA i¯Àùup âÜnóâŽjÓÇÁqä¥×ô¸±¯ÖÑ;Žä Ì+æU òÊ®%Ôë)Úÿ‰ê›Y&Ü¢ÏNmÞ«Ê»?êÍÖ[7ÌÄ®w;Þ­ ÿΧ•Š8¢³/Ÿ>ˆŸvó¼ÿ‚ãÛ·=H|Ä%œpꢚKÙŸw•›çpåÄaÆjÃè–Û5ˆs»Íí§XBmK½"P§^ôAìËutÅŽ#ùóŠyˆ¼*G§T|þ$ÑÿØO¯äö|Km¾4¸CÙ¿•êß´ö ~ÄeÚ&pœòÑ+àÜñïpFå ®!úê´ à› ®GˆdW;áþt‡žkÛ—û¬6Ü~÷žkçv›¿£=äa9úÃ9ˆ}¹Ž®Øq$_`^1¯üWOÃq$øw<,?Qe¿`¹UÓ‡ºð{-Ô·»¿kó-ò0}#8¾IÛÙƒøï.ÓÚƒxµËðì‚ø2A«gÿ+Ôé û ^ï‚Ø¾ŒÇ¡®+ú·‡úrwA ­µá¡¾­»qn·¹ý”Ú{¦ÎAìËutÅŽ#ùóŠy导* u«½Ã9@e/æ#*p†C½I¦æ0]]mºß BÂþmr#Ü¿)ÐÆ'é†ù2ˆ=}ƒ‡ú¹ˆ+ðî¼ ÞqmmØad=’[ž‚¸¬©Pwºûïí6S{ãa9úÂ9ˆkÃwëèŠGòæóÊyUŽí~ @ó|¶Gdjuüõ†¹ÏÃ4a´iR¡CýJ„›yiÓ÷Ó óe ÜÿœCmÜ8~ŠÁM\¦ ‡úio‚¸´nÚ†nž×þm9ÊÍ8Wž‚nÓ=Oœƒ8·ÛÜ~-–»k†ŠÂq‡ª=ˆ}¹Ž®Øq$_`^1¯ü‘W1Ú´V8Žü‘aPŸS î{ο}åz‘ð=ºùiÃfÁùŽ¹Ç ¾aÿ õ­ÐÎ×Aü-Ô‰]}ݸöºá‹µa}uà Áñ›dž‚x’ËòŒÔ†o…óÅùÍ ®Sú@Mä,» ÔéÑ•þ.ÅÜlóâiÓƒó]Šú;Cõw)új]±ãH¾À¼b^ù:¯JCý½ó?TÏaZ"Òã.9{í‡úý²ßàþ UḈø"€•pÜùæî›ž/ƒø/8~H7 @†îy-pþÆÚZ7.I[Ž=P¡åîbsû)–Ÿ´íbÿæ`Ÿ6.ê÷ÃÖêÚööoãæÄ…àøðÐ1ûm~?T Ú?¸VÃñ“ë‘õuõÕ:ºbÇ‘|…yż²óÅ:Þ©›þÔu´îêS/Ú"*P ~[k%Ôõ6õmí+3áùšŠPaeÿqÖ Ne÷—|Ä— þ¢Ã|8þÂÀ~¨¿Sëú— ~oÍþg¥êZ¡–pбýÂzû´úÎN¨Ÿ‘Øä7¤#ë—ÙÉ)ˆ¸?!k¹ÛæÐÀRçáäp|à¸~Àúb]±ãH¾Ä¼r`^åoó—OåúzQˆÐq)ƒ—…ˆ(;Ì+""ƒ1ˆ‰(T0¯ˆˆ Æ &¢PÁ¼""2ƒ˜ˆB󊈈ˆˆˆˆˆˆˆˆˆüÍb± ²X,’C b;l‡í„~;¡.ض'Ûa;lÇíPÒ^\[0í(l‡í°ÿµÊ,Ì+¶Ãv T;„¼xaCv‡c;l‡í˜‹ë²¯ Ûa;l‡ˆˆ‚‚âƒÔDì˜WDDÓË7zYˆˆ²Ã¼""2ƒ˜ˆBóŠˆÈ` b" Ì+""ƒ1ˆ‰(T0¯ÈŽx>ó–p@ß-Nž°@s£„ÌAl(3äÕëŽûÜeìâ™1¯ÈB=ˆÃ< ` T³ãH~Å 6T¨çU×üG[ŽN@ý-k"Ÿc^¹½íÝÄÍì0ÀU—<¥MSÀtø@¢=.   Ôµ8¿ˆPÎA\Àp‡l‡ºQEÄžÚN0ÀÍr ƒ:åsEWס¾å»ÃŽ#Q.0¯ Ë«J’|uÄ‘ˆ *±=áò¸ÔEÝÐþý@O8q'¨;+iÓ½ç öÔöxÓtËÓ@$ÔõGKuËhã Ã=v‰ryeH^•…ºžr4<_vCD…‰ƒø}¨kynÐ*ø¢ ‚ø'5t°ªW ÀíqXmß õíü¨k¶C}£¯uQú Pá>L[^ãHäÌ+CòªÔõ’  nˆ©  6TG“ˆ q€$¨S1¿˜¸Å¡Nñ\ƒº#1@&Ô7ù7¡BúíÚƒúö À_æAV€ç|£=ïv¨ Ú=aÇ‘(˜W†äÕxm¼k¹»›ˆÌ.Dƒ˜ˆ æ‘ÁÄD*˜WDDãmïD*˜WDDDDDDDNyõá£kßN9’ÚþÚ!k[aù¯[ÛeImíÈÚ·Ò¥´mcôkOÁƒGê¼Ã¼b^‘ñ˜WØÑµÿ™zl]‡WOÌ–ß/­ùy'Ëõ×OÛä÷KëäòáI™ÇÖw¸qlÝÛ‹¾±u(‘ó+EfÇksƼb^Qp`^PGÖ¾±øÌ¶¾×þ¼œ!ru;+Àõו-rzk¯GÖþg™ÑûAœ=æóŠ‚óªúÚújëcë;ÜüóršÈÕ-,ƒê¯+rl]‡_[_mmô>AÆb{Ƽ Žb^‘óª:ºî͹WŽLùiËàúñ`Bæ‘uÒÞ'ÈX bϘWÁSÌ+˜WÒ‘Ôö¿üþÝj‘+6–ÁõÇ÷Ir$µý5£÷ 2ƒØ3æUðóŠæUtÈÚVäÊVÔ_—×Êá”v™Fïd,±gÌ«à)æÌ« çÛÞYÛŠ\^Ç ’:dmË7_g– f^™¿˜Wd–¼2-¼@*ˆSXAR b2K3¯Ì_Ì+2K^™–ß‚øÇdVƒ˜ÌòƒºÌ+óóŠÌ’W¦å· þa +HŠALfÁ¼21¯ˆ‚œÿ‚x+HŠALfÁ¼21¯ˆ‚œß‚øÒrVƒ˜Ì‚yeþb^9¿ñ÷KYAR b2 æ•ù‹yEäüÄ‹å©”Ö IDATYAR b2 æ•ù‹yEäüIJ‚¤ÄdÌ+óóŠ(Èùíu/ÎcI1ˆÉ,˜Wæ/æQtÈÚVä»Ù¬ )1‘gÌ«à*æQtÈÚVäÂLVƒ˜øƒºž1¯‚«˜Wļ*€TOcI1ˆ‰ÂË3æUpóŠ˜WÐ!k[‘óSYAR bb{Ƽ ®b^óª:dm+rî³€Ö¦ôE2tæF‰N°mõk“§ØdÛ¦…Ý6 bb{Ƽb^Qpa^@*ˆ'¬6¥/”Á3·ÊÆ/~”¯Ïý´õåÙ_%㫟äé›eÛ¦Û> bb{Ƽb^Qpa^@‡¬mE¾°úhJšX÷\’/Îüµvß;cCÀ¶ƒ˜Äž1¯˜W\˜W â1«è›ì8~CvŸ¼µãø é1Ö°íà &±gÌ+ææUóÛêž°ŠN°É¶c7Cª¢lÛ> b2K3¯˜Wd~fÉ+ÓòÛŸð:3"``“ŒC×Cª¢lÛ> b2K3¯˜Wd~fÉ+Óò_ XE'ØdãÁk!U*ˆ³}Äd–Ôe^1¯ÈüÌ’W¦å· >=4``“Ôý?û¦6/‘{ÊýM>JÿY,KûIÍÚ=dÒNwÓþ$Ëf–VµÃ%¬Py©ýÔhùlËU¯Ÿ':Á°íà &³`^1¯ˆÈ`~ âo†¬¢lbÙsÕ7•¾Xîo%ӮʊÅýäŽZÝeܶ¬Ó­IK”GË——ûÞß(³Ö$Ë›w••ʯmD/Ÿ':Á°íà &³`^1¯ˆÈ`þ âA«è›¬Øq%uIfN ÷Ô —0“ -»Ê€UeÅæòVóp E+µ‘^zË5ÚK‡7•J%!a÷ÉócJâŽ+2üsRöö2z“jsáä×$¢JGžáÝ2¨ Ìöa“Y0¯˜WDd0¿ñ©OVÑ 6Y²õ²×µhõdiQ®Ž<»]¦[·K¯ÿ«#%šÄËøŒË²díB¹+¼•ôN¹,óçö‘*(&õÞK’Ï7–˜÷î’’ âeü¦ïe|¦RòÞy2ÓÞæÊÑR·äCÒÓú£WË` Øöa“Y0¯˜WDd0ÿñ‡«è›ÌÏøÁë×·¥”¾w†LNWç­™)MÃï“NË¿—ùÖÒ,üaéaùAfÍê-UÃÛH«šnÖŒ^R½V/‰ßp^F¼Û@Ê>ºL¦ÛÛµÌ;K¶w/zµ *ˆ³}ÄdÌ+æÌoA|r@À*:Á&³Ó.y]±¯× ¤”*!¥ËFHé²e¤H‰ÒnÎ9™m™/MË>,Q«/Éôé½¥zn28UÍ7}zo©^«—Ä®½ #»5–-çÊgZ›3*µK> ]V|ïÕ2D'ض}ÄdÌ+æÌo?¨{¢À*:Á&ÓÖ]ôº†GÝ%å],ãíÃRŽHìg[e\êE™¶rž4)ûtY~Q&Oí%Õkt“O’Ôt“§ö’êµzÊ ä‹2>þY)S]ýÚº‹21áu‰¨ÔA>´x· Ñ ¶€m1™óŠyED&t$µÝµ?Žô9Ñ7 `“ÏR¾óº&ÎùT”k*­ÿ»YF.Þ,]Û¶Òõÿ+1–ïä³Ä¹Ò¸ôÝòƼs2~rO©V£«|°ZÍ7~rO©V³§|´æ;™´t¾Ü[®’Üûþ6IHL“v÷DH…ç-2ÖËePAìÿmóû‘žr$µÝe£÷ ¢`żb^‘Áަ¼–tew'‘ã½RÑ 6—t!uFÿ·›4¬RJ€¢Þè-ysÚ7jÜÊMòâÝ•$¬Ä£òŸ¸îRµFWé¿BÍ7zbO©Z³‡ XyAÆ%“á£>”¦·‡K¡"¤ú£Ãe`ây¯—!:ÁmseO'9¶¶]¢Ñû‹?¨ëóŠyEÁ…yULz¥Õ±µ¯ÝüóH´È±î~¯è›ŒZu.¤*:Áæ÷íòç‘h9¶öµ›_[_mmô>AÆâŸðòŒyż¢à¼* Ž¤´›ÚöÆ­¿ŽF‰óoÅLµÊà…ÇdøòoC¢b敘©V¿n“?t“SiíoMi7Õè}ŒÇ ÎóŠyEÁƒyU@}cëPâhJ»©ÇÖ½ví§=ïÈo;‰íâ—ÚºþSé;Á&ƒ—Ø%g‚¶†,9-ŸÌ="ý'¥Ëö Ã}¾þ:ÒEþ8)Wvw£ë^»vtík³Þ(80ˆ³Ç¼b^Qð`^p‡S^}øØÚv‰GRÚ];dm+þªÅó‡Ê€‰V‰N°u ˜˜"K ñË68œÜ6óHJ»kGSÚ&ñté1ˆ½Ã¼b^‘ñ˜Wr¢Çd r Ð1¼@—BƒØ¼˜Wd6Ì+ 9 b2±y1¯Èl˜WAŽ·½gÅ &³1K3¯²b^‘Ù˜%¯L‹/PV b2³¼ÏͲ¾Ä¼"³áû<ÈñÊŠALfc–#uÌ«¬˜Wd6fÉ+ÓbgÅ & NÌ«¬˜WDP â¬ÄDÁ‰y•󊈊Aœƒ˜(81¯²b^Q@1ˆ³b'æUVÌ+" (qV b¢àļʊyEDŠΊALœ˜WY1¯ˆ( xÛ{V b¢àļʊyEDd01… æ‘ÁÄd6ÏøQ¤,”æáKϤ³i÷™2èA)y×t™š¡µ·q›¼Z»’<4öL¾×›ALÞbçóŠyEÁ¼"¢l2/ý‡|×ìE*ˆß·^”±ý”ÛZÏ•¯4ºÑ»evÊ*iu[ci;ï[Ûï~)Õ` üwÕ™—þƒLÒJÊÜ3_¦¦+ƒ_ºC*<›$Óµ6§M!/öÝ,3ÒÏˈ÷K…'&Ë›wÝ.Œ8!sÓyÉ ¤YøÃÒ}ÍÅlÚ½$Ÿý÷)z{?¶^[ÞäeòpùšòÌççò½Þ bòƒ8ÿ˜WÌ+ æU3úug¥]ÊwÍX0TjF<#}“.Ɍţ¤ADC©UùnymÞy™•¼Rº­‘¼2猌ìÞ\J5!ñ)eÚ²åò|ãbR¨ù¿ñ’L-•Ë>(/ûR>[ž,/7)/5;m“ii—dÚÌþR½tU)}Û‹Ò×¢=¯e¾4-ÓRÞYr.›v¿— ? @Yi•" +¶KWI±=%&%ÿëÍ &o™%ˆ™WÌ+2?³ä•iý'¼¦oø>ßõùü¡rG¹g¤÷šïeúº}òf#j’¸Ôïeºe¥ÍòP1 OÉ3¿’ã6Ýêž>Òè׎¨ a^1¯ˆ(te{£ûØŒ½Ñ ¶[Ñ 6a¹¯îcÒ³i ¿¹Snñu}‡yż"ÿb^Q(@tÅ–LÂË”˜WdJÌ+ % b2%±)1¯È”˜WJÄdJ bSb^‘)1¯(”0ˆÉ”Ħļ"Sb^Q(a“)1ˆM‰yE¦Ä¼¢P &Sb›óŠL‰yäxÛ»1™’Y‚˜yå„yE¦d–¼2-¾@NÄdJfyŸ›e=|„yE¦Ä÷yã ä„AL¦d–#uÌ+'Ì+2%³ä•i1ˆ0ˆ‰‚óÊ óŠˆAì„ALĘWN˜WDx b' b¢ Ƽr¼"¢Àc;a1æ•æƒØ ƒ˜(ˆ1¯œ0¯ˆ(ðÄNÄDAŒyå„yEDÇÛÞ0ˆ‰‚óÊ óŠˆÈ` b" Ì+""ƒ1ˆÉ”x¤Î”˜WdJÌ+ % b2%^hJÌ+2%æ…1™ƒØ”˜WdJÌ+ % b2%±)1¯È”˜WJÄdJ bSb^‘)1¯(”0ˆÉ”Ħļ"Sb^Q(a“)1ˆM‰yE¦Ä¼ r¼íÝ ƒ˜LÉ,A̼r¼"S2K^™_ ' b2%³¼ÏͲ>¼"Sâû<Èñr &S2Ë‘:æ•æ™’YòÊ´ÄNÄDAŒyå„yEDÇ v & bÌ+'Ì+" <qŒ­WÄФ®qìÝO±D Ë·›™oú1>%j\ÜòÈj¾~ýˆ‚ ó*t‹yEdþ â!Ö.-ã’»]³}ÌÍͧ6ÊÁï¾dù¸¾¸°W¶}c“[FÞŠ·Fÿ8ÄÚ¥¥/_C¢`ü Ýb^™„?‚8ÆÖ+".¹ëÕ¤¯Ë—ö±Pë­úchr·Ÿbl½"|õ:æ•9ŠyEÂüÄñ)QãæìHømÿ…= \ÍÞ>ú·ø”¨q¾z‰‚ óÊ<ż" Qþ¸í=ÎuÙvÂ*ûÎïd°l'¬oívÁW¯#Q°a^™§˜WDô?±I‘™»¾Ý,{Îmc°v}»Yb“"3~ý‰B óŠyEDb‰”]ßnaPC,‘ü™Ê‚?¨ëóŠyEÁ…yU ±DÊŽo3|V‹úÕ”âw÷‘U§õí2ößár[»ñ²qßÒ´dy‡Ís;'¦É·CP¶µŒ;ä¾u[w©ÈΕͺé7Îk%åš÷’U߸¶•.SúÉÿÝ_UJ‡AP¤¬ÜñH[œ¾V¶g»é’:©¹D<6B6úpÛ¸ƒ˜ÜáïzƼb^Qpa^@C,‘²ílšÏj“­“Ô)y§ôß¾Á1üЧòÄm·És‰É²eïiZ²ôÓw)›åe©V®®ÜY¡œ´ž»æÃ7o’ú€ xsé·eÝÿ†¯Ÿû°”kÖCVœrngë×£åÙJ%¥itœ,ÜŸ$kwN•!ïÔ•’µÛÊÌ£Ù­ÇF±Nl. “õ>Ü6®Å &wÄž1¯˜W\˜WÐK¤l=³ÁwurŽD7*!‡.MÚ°õ‹Ÿ UÚÈäÃdóž÷¥IÉúÒoû:m$ËçoU‘ªo}*“:U•rOÇIêi5nÓ–nÒ ¼™¼ùrU ÿçYyR _7ç!)׬»,?éÜVzÚ»R«T ú…nø¡ òÖÓÏJ쎵²qå³R­I'Y¬Íçx¼^¬šIø}ïÈ{OÕRa…¤\£Ç¤ulöá¶b“; bϘWÌ+ .Ì«hˆ%R6ŸYëòʒ˜úRò®hYvr­l>³Z&¶»M*µOõgÖJÆž~Ò¤d}é»=Åýü‡äùêÕåõd‹l´¶•êå[Ɉ©²ùÌZIßÒED< #¶ÄÉ3UÊÉ?¦,–Œ3k%u΃R®Y”ö|º:4VÚÖ,"åz^z͘$+ޤ:_¿²Tkòž,:éú8U’&4 ´<3V–ï™.ñïÖ’âw¼"ÓŽøn[1ˆÉ±gÌ+ææU4Ä)§­>­´-ÝäÎÒ ¤÷‹d-ÏW«(Ï/_!§­bÛÝGš”¬'}¶%¹7ué“R¹~{™}Ì*Ç&Êj—•¿M["é§­’¶¹³Ô¸_F~•$«¦üM"*ÿK>ý"Y¬sðfÝdé 7ËòÅ<ðyùç·KÙ¢%¥Ê=ÿ”w§Í”õ§­²vÅ3R­É»²@›Ïñ8YÖLh"Åë¼)skm*­+T–W׬òÙvb“; bϘWÌ+ .Ì«hˆ%Rlß$ù¶NÌ–¾-KIÃÁ3$yÕsRµú32ñ—¶«·4.YOzm]ãfÞe’ðbY I±Ťx‰bHñÖÉêSI²aS¤Ô¸O†TÏ1ðÑ2Rå­á²dæÞ¬‹,>áÜ^Úñ’rÄñÛN br‡AìóŠyEÁ…yäüqÛûK¤¤}³ÚǵR–Žl"ešwAoW•*ÿ!)Ú¸»zJã’u¥×Ö•Yç;8D¯XYþoÞ4Y¶s¶,Û9[Ì{F*—½Wbö­’õ›:Jýˆ{eøA5ýºôHiV¶º<ß§¹”mÖYз·JVNh"¥šw•Åúá'gIÿ»ŠKýÁÓ$iùSRµÑ[2縚~õ以t÷eîñU²r|c)^÷ ™uL›ïÀ`i]±š¼n]æ³íÄ &wÌÄÌ+晟YòÊ´üñ ±DʆS+}^ëwô”¥JH…Ò•äùå‹ÃwöÆ%kI¤uެÜ?O«ùb9ºBVÍj%åª=+éÚúz„<_¥¤Ü7qޤf¼'õ"î•øöñKezÿºR4”Ç—aÝÖîÒ²T iÚ©Œ[?M–n›( qIÕ’µä­”¥’’òªÜ^¦‘D®˜#I;c¥}³bÖð-™sl…,×XÂPFŠ!‹wO”!ïÔ”’ ^—éG|·ÄäŽY‚˜yż"ó3K^™–¿‚xÝÉDß×ñéòáß *?-ã¿r _»#Z—ºsØòdi¹­}¼XœÚZ m#¤ÄßzËBÛ;R/â‰ûR7þ«‘òZšt”yÇ\—c©,O|Wžz°†T(ŠIùF÷Kû©“Åz2QÖ›*Ÿ¼V[JR²f i?ü%ip×dÖÑe²l\3©þì[òÚ£U¤ KD³Ç¥ÿú…²Ö‡ÛˆALî˜åu™WÌ+2?³ä•iù+ˆSO.aP b23敹ŠyE‚üÄ)'± (1™óÊ\ż" Aþ âäãóYƒ˜ÌŒye®b^… ±åø–Å &3c^™«˜WD!È_A¼úØ,–Å &3c^™«˜WD!È_A¼òèt¿×uŠKóɲŸnOiQ®¡ôß•Ëö}*}_¬.¥J×—.¶Ï²>¾(1™óŠyEDó×ê®8:ÕïÕ¾Nqi>ylöÓ퉒åHÿ]¹loiU±¦¼»á³€¬‹¯ŠALfƼb^‘ ±DJâ‘É~¯×ë—æŸ%È¢5ÏHÍfÏJÏèfR­l˜ªPWžŸ>R–$o¶,)¤h•Òwë™>ëßrOí’†"RáþÇ¥¿m‚$™,‹V>)wÜõœtïX_*Ôl!·,-¤HÅ&•6JF~r¿Ô®PX€0)}çÒ9y¼ZŽÃãeüÈG¤Aå"‚¢áR亮dìŸIâ‘IŸËŸÅ &ÊæóŠˆ 6Ä)KOô{½V§˜4›ô©,Xý”T-RF÷è-ÓöÄÉ ¨R¢á‹2þàDYº«‹ÜU®¾ôÙ1Qg¼--"*Êã ËŒmKï¶¥Ä]¯È„ƒeÁÊI•"%åöçÛÊÇ+ãdþîîòp庵y‚,Zû¢Ô*[[ÚÍŠ‘[>–¾m+Hø3=dîቲÀò¬ÜQ®Ž´+Ó¬]äÅ&%¤fŸOd~6ÏåÏm &wøƒºž1¯˜W\˜WÐK¤,>4ÎïÕ®N1i:i¸Ì[õ¤Th.wªáóV>!Õk·–áûÇÉâ‘Ò¼\=é½}ŒLŠ©)¥zG¦}­¦[´õiV®¶tN+óV<.•ÛJÿícUû»£ä¡Êu¤ë¦±²hœLL&ó“…{ÉG*K‰‡:ÉÌC£dø;¥Â‹Ýeö¡q²øÐX™µæ]éúù ŸÍsùs›0ˆÉþ /ϘWÌ+ .Ì«hˆ%Rã÷j[§˜44Læ®zBn¯ýO‰ß¯†Ï]õ„Ô¨ó˜ Û?Fíì(ÍËÕ“^Û?•án ˜” /%¥ÃKIéðâR´äÒ>y”Ì]ÑZªÕr´±hwW-ˆdÑÿ•¾›I½º•äŽä‡ËHñ‡:ÊŒCÃåÃ'ËÈ}>”ùNË–ýsùs›0ˆÉ±gÌ+ææU4Ä) ¾å÷zµN1i:q¨Ì^ù/¹½Î?%nŸîôxǻҼ\]é¹m¤Œý¨†D<)Óím"Ÿ&iFÉìåIµÚŽ6ìê,V®-]2FÈøÁµ¤Tã§dÐæ²àëQòù¸zRæ¡weÚ×Ã$ö Rá•(™©µ9#ñ%y)¦ŒÌæ¹ü¹MÄäƒØ3æóŠ‚ óªb‰”y_ð{½R»˜4™0Xf­l-·×~T†îUÃïè ÍÊÖ÷6“Ùë^’Få«ÉÓãúÉg›ûIŸŽ5¤L“gåÓ/FÈÌÄH5]óvv’*×’ÈôxIø º”ºç±g¸Ì²u’î*,…îy]>;8Bf,ý‡T ¯-¯ÎùP¦ft“W[”’Z½ûÉŒlžËŸÛ„ALî0ˆ=c^1¯(¸0¯  !–H™óÕ0¿×˵‹I“ ƒdÆÊÇäöÚȽj¸Óã}}¤ýÃe$¬Té¶)VÆL|TšÜ^L€ÂR®ÅƒÒÙ+s¾&Ó•jº6æìì(T®%Òãeö–NòïûÊHáBÅ¥|ã»åµa­¥aÙÒ$n Ì:8Xâbî‘Zå ÂJJ¿ q»ãeÎWC=>—?‹ALî0ˆ=c^1¯(¸0¯  XK§ÌYbeöÁ¡¬ƒ˜Üa{Ƽb^Qpa^9Üö>4¹Ëé©{?–™³X“w}(±I]Îûêu$ó0K3¯ÌSÌ+òÄ,yeZþx[:ÇJëýÛôƒ„¸eëusHR—8_½Ždf bæ•yŠyEž˜%¯LË/PŒ­WDlrçïÆïè/Ÿù1+5vk?’Ôù‡[¯_½ŽdfùA]æ•´Ò IDAT9ŠyEÙ1K^™–¿zö1IZÅ&uþîÓݯßÕO&ï(S¾ø€åÚ¼ LÚó¾ŒL‹þ=6©Ë¥!Ö.-}ùæUèóŠÈ$üyH8ÆÖ¡Dœ5ò“¡É]NÇZ:e±D Ëwké”›Üù»ÁI‘ üæNó*t‹yEd¼–ÀÉ ¢+&' "Ì+'Ì+" <±1Qc^9a^Qà1ˆ0ˆ‰‚óÊ óŠˆAì„ALĘWN˜WDx¼íÝ ƒ˜(ˆ1¯œ0¯ˆˆ Æ &¢PÁ¼""2ƒ˜L‰GêL‰yE¦Ä¼¢P &Sⵦļ"Sb^Q(a“)1ˆM‰yE¦Ä¼¢P &Sb›óŠL‰yE¡„AL¦Ä 6%æ™óŠB ƒ˜L‰AlJÌ+2%æ…1™ƒØ”˜WdJÌ« ÇÛÞ0ˆÉ”ÌÄÌ+'Ì+2%³ä•iñr &S2ËûÜ,ëá#Ì+2%¾Ïƒ_ ' b2%³©c^9a^‘)™%¯L‹Aì„ALĘWN˜WDx b' b¢ Ƽr¼"¢Àc;a1æ•æƒØ ƒ˜(ˆ1¯œ0¯ˆ(ðÄNÄDAŒyå„yEDÇ v & bÌ+'Ì+" <Þöî„ALĘWN˜WD|´–*ÇÀ …vÚ·o¯aùûßÿn3Ãz±¶ãm;¡.ض§?Ûa^±‚Þ©`ÛQüÙƒ˜í˜¼›7m…²}]˜Wl‡íÀ¼2­ÝáÄl‡íx¨œÚ e!úº0¯ØÛ)€yEæÀk†ˆ(T0¯ˆˆ Æ &¢PÁ¼""2ƒ˜ˆBóŠˆÈ` b" Ì+""ƒ1ˆ‰(T0¯ˆˆ Æ &¢PÁ¼""2ƒ˜ˆBóŠˆÈ` b" Ì+""ƒ1ˆ‰(T0¯ˆˆ Æ &¢PÁ¼""2ƒ˜ˆBóŠˆÈ` b" Ì+""ƒ1ˆ‰(T0¯ˆˆ Æ &¢PÁ¼""2ƒ˜ˆBóŠˆÈ` b" Ì+""ƒ1ˆ‰(T0¯ˆˆ Æ &¢PÁ¼""2ƒ˜ˆBóŠˆÈ` b" Ì+""ƒ1ˆ‰(T0¯ˆˆ Æ &¢PÁ¼""2ƒ˜ˆBóŠˆÈ` b" Ì+""ƒ1ˆ‰(T0¯ˆˆ Æ &¢PÁ¼""2ƒ˜ˆBóŠˆÈ` b" Ì+""ƒ1ˆ‰(T0¯ˆˆ Æ &¢PÁ¼""2ƒ˜ˆBóŠˆÈ` b" Ì+""ƒ1ˆ‰(T0¯ˆˆ Æ &¢PÁ¼""2ƒ˜ˆBóŠˆÈ` b" Ì+""ƒ1ˆ‰(T0¯ˆˆ Æ &¢PÁ¼""2ƒ˜ Œ[‡C¬]ZÆZ:·¼¦ó{ÿ+K§7b×tl›Ô±±ÑËHÙb^ŒAL¦›Ô±q¬¥Ë¨aÖ&uÉ‘ÚëÚ$[ÌÕYÛF]·×ç›â¯MHûèêˆÔ^×b-3G¤öø2>%j\욎­cl½"Œ^úæ‘ÁÄdJƒ-߉·F‘ÚëÚ’ÝŸg¦M–ƒß}™cí;¿[ÒŽ&KâÞ™™Ò>º›Ô9s˜µûüØ5[½Nļ""2ƒ˜L%vMÇ:ìчÖ½#õÐr9pa¾jÇ7²b߬Ì1ë^·F_léô†ÑëX€1¯ˆˆ Æ &Ó’Ô鵸¤®¿­Ø?K¾¸°×çµîð*™”>èÆ0kôw±I_2z} æ‘ÁÄd êÔtô­Í'×Ëþó{üZk¯”Öž7‡Y£­±k:Öq]–[¯ˆØ5[¶DFÅZ"ÅZºŒ–Ò}n¬¥Ë¨XKç["£bxýd^0¯(èÅZ:·‰µt5<¥ÇÑ8k·k±I3‡&u»6<¥û)ûµÓF/#Q~0ˆ)äÅZ:·‰·FßÚtrì;¿+ µçÜvY´kræÐ¤.7c-ÛÄ®éØzhR· qÉQ—†&uÎLØ0àê¬m£n-Þ3E–î&+¿˜-K÷N“;'fÎÚ6êÖø\OŽº9Ìõ]|JÔ¸˜¤N­ŒÞŽ!€yEA+ÖÒ¹MœµÛÙ©½®%î›)iÇSd×é­²ïüÙuz«¤O‘e{gdŽ^Û÷Z¼5ê<ÏZP¨bSH‹±õŠˆKêrs푲÷üŽ€×º#+dDJ¯›#S{]_ºwªØNX½žw÷¹­²þØY¸û³Ì‘©½®Ç[»ñÊì1¯((Å%EM‘Òë¦å«E^}é´|µHF¤öº—Üe’ÑËN”[ b iÃRºÏ]¸kâ¯{Îm“P®]ßn–”CËdRúàëñÖnçy:Ë-æ¸ä¨Ä ?¹±íl†ì>·ÍëÚv6CÆ¥}xs˜5Újô:僘BVÜòÈjñÖ¨[ÏÚd×¹-¦©UçʈԞW‡Y{Ì6zæ•ø”¨qc׸±ùÌÙñmF®kÛÙ4³qàո䨑F¯ ‘·ÄÔblJÄ®éØ:vMdïø”¨%Ã¬ÝÆY£.Ç%u½6Ä)³¶ümç·›Älµý¬M&¤}|#.9jKŒ­C £_‡ Á¼¢ »¦cxk·§SdëÙ y.ÛÉ$‰KŽºÉ¿\E¡‚ALA)6©óKÃ’£R†&uùuÔÚ>?ÏÚ>ê·eû§Iê‘D±L–ŒS©yú†JµílšLÙ4äV\rÔ£_ Á¼¢ —Ôuõ¼]ã27N•üÖ‚]2‡¥D¯0zˆ¼Á ¦ k‰|!Þõ݈Ԟ×W|9K6Ÿ^'ÛÏÚ lm=³AÆnx§²0¯(ˆÄZ"3ן\)¶o,ù®µÇ–IlRd¦ËOt}`'€Â.O½À UÜP3›Å,à €«Êé†×z½ï2ýÓö(â2<·Ó»*à2€ 9L—OÈæ‡¶É 1…[¯ˆ¸än›F¤ö¼±êàÙzv#K«'ÖÈФn7â–GV3úu2óŠ‚B욎­6ô»ºáÔ ñUZ×çz¬¥sÝÓÜ Õ1¬¥à€Gà]Çñ~WüÕÁ²³wo¨«žSÇÑÛé]±ãh" b2\lRÇÆqÖnçflþÛ–3ë…•µæî“Ÿ5Îè×Ê`Ì+ ƒ-‘QS6ÇÜZ{r©øª¦l޹5Ø¥{š¢èªö€óJûŽã$­FXG窀ŸÌGÇ/»Ž£7Ó?à(Ôû3][¶b¶jÃ.AubŸÑ¦à¡Ž¬îð–¶œíœð«¶¾p7Ô‘ØŽxŽŽc ßkÛ‰ü„AL†Š±õŠšÜõêÒýŸËæ3kYjí±e24©ë/F¿^c^QPˆµDš¾=^¬'ø¬>ß›kéôËSõ°ŽŽÙtSµÿçÔq, ušú-ü ¼6®ÔÀ:PÑ—µáÙusš¾ºöm¡:‚S¡:xEá|Äq€1P½õPïå¿ˆÐæ¯u´õgϨ `€$… :Ž¿˜ÕQ|ªãXª£ÙÖÃö a“¡â­Ý¾Z°kÂ-_\`nöŠO‰ºUÀï¼d^QP’ÔéÝ ^_sl¶øª&d|xÝåˆ#Ôð‹öoigØÿÊTNÇGP\«ãž×ÆÙ;‚á^pªS—SÇ1»é;ØÇQÍŠÚ<ÕáÜq|À>mØQ¨k6x@{\ÀGVèÚª¥m‡ÊPÇ+pœö~Z[ÏSV‚§¬ýŽAL†‰MêüÒèõý~É8mVÎõij¯Ÿ‡X»´4úu3óŠ‚B¬¥s›‘k{ý²òètñU}º¾÷U7?ü_ÀvQP«3Jiãrê8Î…zŸÜÔJ ŽÚ…Á¹#X@*€‰Ú 玣§é‡iÏqEWסŽê;Ž•\ð0Tòeô0K{¾ÉZ{ve¡Nqׇê8ƒê`ªã(¦Au.õ×`’0ˆÉ0ñÖ¨ý+ÌÛ7I,/j$;ŽÌ+ 1¶%†&u½¾äÐ$I<29ßµø«ñ›Ôù–‡ßl}Àã|¦ž]Ç1ªsö€jZ=uwuE8wuzø*ÔÜ9u=M uôЮÔ)òÂpî8:ý>êšÉšPGR7Cš€¡Ž8ÚÕ„êVê8ÚLªã¸ê”ø­ ¹Ù&ä# b2L¬%2sé•’öÍ–›Ô9³€ÿ8óŠ‚ÆÐ¤ÎI“·~’¹äðxÉoMÚòQf\J—%žªÔ‘»_<¤nï8Þuí¢½ŠAu¦Î(©›¾Ôõ‰¯"kG>€z_íûÿöÎ<<ªòÞã_!Y (Ô²D”­"Ø[[©r‘ÖJãµ{«  3“•° ä̶0Y !!;aÉ,Ag&aßB¶`À[®H½¢ˆèÜþîïg˜Îdàø}žçû„œóžß{Þs_~ï†Æ£»òý &¿¼ 'DÆðc¿ð˜|­=;9ž…ÈŠö‘φãè/Ç*„˜ Óî£}rŒ€:¿sÇà1㤒Àq¥¡Ö]ç¶«qmªÎ¤…¥!•Þ~o^†¿WL«AÒ‘tÊy•‹©ðäò[V~ÕRTפ’ÀªzbÉ—ptSÂ8’} ‘­Ks+SŽõ8þÝ>aÈ*Ð4ãè®ü«>—ïcD×2 ÆXî€0u½ Æ_€¡òù"Ç!L& Là{“]¢kÜyVµ'ã‹tŠÅ´0ü!f¼‚¦XÑ[c ®oÉuÐîe¥Z¢ê5† Þ~o^†¿WL«BcTE-ÞfÍ«^DùÇ7[yÕ‹hÑ'¡×ƒb¼Ý†i*ü!f¼†Ú úzËÉÚq¶¸Å”9¥uxZAkÎ8ϧ8ÿ‡©ûÛmÝFƒ}PxùÏqN.¥·û€Ðé´è˜ãø6óGäгÉèT~köPçaTp¦åÚᬒ“¹¤6]uÙUâ~„¿WL«"Úà£Ñ«6,ÞVŸYC9Õš&+«JM‹w„[%½ÒS5ôJøCÌx µ^¥IµÌ¯ÿäìj)vŒ§¾¾)¼¬Èq¼2Š^ìÞÆ­Ë§mûCh°ï +/ò£d³?=Ò¥/ ìÖ‰FgçütÜhžH~¡Ã 5¯ûéøæìßPçaSþ™–k‡]¥µE´È8ùÇ·¦ŽÝñ÷ó~– ü½bZѦŸr¡ÚdMÙ?‹VWE7ª¤½3ImP]‹Õ+Ò¯aZl¯m ðÑ‚ÿ¹öX m;[Ô2ªI¤Iƒ:Г±)dm^3šº=2–â«‹¨t0 öíO¡åk=ÄÈ£„ =©×„(Zúq/êüòlÚX+ÎÌÒãÓß^ïIF‡PÁq|Sö3ÔyØG”w¦ˆ¶L iïøQ×¶ ´ïNOÍ£¢3·Þžó<Û‡±¯¸ÇÄâïÓŠˆÖO%镵£Òo™L)fÐÊ£‘”~l­<I)gÒrK„McTZ%½òœËö‚ s×ÀÆ‘ñ*ÑúI£4Æ`kÞa-•Ö®k­¡ì¨þäûÔG”[³ŽJksiéÛÝè¡Íµëȸ/ˆùö§Ð²B÷×WÇÐ_úRIàXµA•¡6(k$½²NÒ)l’^Y§6*kÔU"FænÇÕ8šàýî(Ö}¦Ï<–µ1К°3òƦ“d¬-¼-éÌ鉎~lÎ'cu4ù÷îNþë³ÉX[H†}*äÛŸBÊ Ü^[¼v õ|ü J;YHÆ“qôN¿‡é÷é«ÈP[H:óä×uÅ+ ÂÔg©K¯Ñ$^C³FRça”S³†Ö¦ §‡ý…®O¡-gò©øÐJÚpjÍ-·e}u©·Ó¤%¯S·G:¼oÖZ“€a†ùYYïüY,j×¾-½ðæšS8‘ât“)÷Ð2*>±Š Ÿ4_§)l„/ \ ¥ ŦGúü‘–U‰sú½JäÛŸ‚-yn®Í¤¸×&àjïÓžÚûô\p8¥WåßZ;déÎäѪ=Ò¿¢‹ן~ñÉL´³ßJ4 Ã0ÌÏÊ´ÓÀbÙÕ®}[:jMÍ|—bu J0Í¢­5Ù¤ÿ,¯Ê¡ÜEORÇ_ÿ"?èE½Þ›OÅò9Ý^ òíGÁ–œ¿®béу^ÉYNy{“(oo­Îù#õè4œ"æÒVÓxòëò4EWˆò[v½OC;=JþC¨Ó°(ët.}2Ÿ–›2H÷Y.m4M§·ûÒãó–5óþÝ«°"áGI§¼«S>†a†ñ Ð Ì‹e—ÏÃ(pÉk»EEùG—SÉ™œ[ÒÖ= zÊ·uëø ò_¿Êéx =éûMÔ%Sá¡TY+hý‰lZ›ù[êÜûO´¬Ê)Veù÷ò¡g“iÓ§ï‘_—á´à¨ý|&¥LëGmÂÐ÷)óÔjJ›ñù{–§ÑšíÓèíA¾ôÄ‚e·ÜWT$ÚT×ÀbÁ Ã0 sÇïw7±X ûXǯ¦–Gý¸õLÝ–N'ÐôQm =ÇÒÒJÇñ-{>¦'ÝÖ'4Í{©#uw.­¿)VŽݙ|F))kç»4 ËpšÔé|å|z«?C&PÆ©,ÚrxMÓ‹|B»Î4𭉔Zy›mqQæ~m¡!ä†a†aîG¢M>’!諬ƒ‹hKÍjV#Šß9µNÒ)B¼ýÞ†a†a~vÔ†àÄ$Ëì6×d«qV%’Ú¨ºäí÷Æ0 Ã0 ó³#éõ몓hÓétVWv%Ö¨éíwÇ0 Ã0 ó³kTŒ+ »²ñt±š®dKd} wW3 Ã0 s?!Œcè• §R‰Õt%—ÍùAÒ+¦yûý1 ã $Þâ–`Y6-¥¼fr’¥.4Ád›œh©›–R~nJŠ%1T»kl3Â}ƒ›'ò]àfÞÖSNðiæuvºÈ÷ÒÝåøù¾fºà€v··%À Mʶ°â91 ÓÚÑ+z«ªúõ§RˆÕt-ß5­.F7i¼·ßÃ0Â0F$YÎÏLÛ]—¿óí:v‰ÖÖQåùïé`mí:v‰òvœ³ÍIß[‘Tv!Lky£ a/ø€nz˜Àà7ͼ½®þ an…ÆŒc=?§ãw‹ql`4€•í`ãÈ0w ƒêóì# ©èT«‰R”74ÅŠÞÞ~w s¿3%Ñ’>cÅžëÅ»/På?n4¨#Ÿ×Sñî 4sÅîºÉ –ÔFB_ð¬Óï|a G8`1D6ñ!ð€0xT¾Î5ãøG5fÉ  ¯|¼ € .¸ Âtî‘Ë~a`í ð€\¥pEWã设öNqÿÀ×þ"—Ÿ àG= ÌîÈ÷ç©®Ïãu8ŒãÓ¾ð"n¦-€¹òý׃#ÃÜ=Äꃋ¶‡__{2X+¥|ŽMmÚáí÷Æ0÷;S’Ë6Ææ¾¶·ÆJ‡ÏÕ7Y{k¬“sèú”ärcá]ãh?@ òŸó!ŒÑ`W¼ aîÖÐCtk;Ç>]Þï@³tÃõ €aÀ1A¶Ùh8ãøüó€7åãÎÆ±¡úœã&ÐB½†òyãz Àãò=yj£ëóø „qa4ßià9?à`ãÈ0wѦ!èDry¤­ðärbyÖÊ HÒ)¯s¶‘a¼Ë”$Kâ¼Ì×ÊNÔѾ3×›­Ý§­4?ëÐå)IeK總üÀsòM»LÃÌ6ÔF×ç1N®7Â\:Át…#ÃÜ­HúÀ!’Aõ•Ö2ãFþñÅTpbÉ}¯ìcjJÞ=Û¦6ÕiŒAk£M]½ýžæ~gr’eë Ýg¶O×Ñí*]_k›š\¾ÉM5®]ÕÎŒ€#ó ²qvúB¦Gp³q …ÈæÙi`$D3 ÂhÙ @¦G@t'_‡ql¨>ç¸í1æ°/€óÊ!º¦k£ëó`?D—x‘,O³ÑÙ82ÌÝL´)¢«Z¯,Õƒ­éçSþñÅÍRnu\³¯ñ¶r«ã(·:޲©)ãP4¥î‰¤Ä²™¶…¥!W$Ý$›Z¯*ôî2 ÃxP­Éf8òm¯¼zÛ*9t‰Bµ&[„ÖäúŸÂæGûG=BLi‹›c?ˆÉ(¯Éåâ LÞƒ³µ¿ð„Û‘ì1 æ1—{p5Ž ‘é; aªÏ5®=;9^¾×³®Ctk7ÖFwÆÑ>9Æ@€ßyx–læ^@Ò)ý5Æ 1Øš²oeVÄPîñ8·Ê©^H+Ì¥e;'שª«I˶&e!µAùƒdP~/ë1¨RmPåI:e$wI3Lë#T»k윌— G.SKivú>kH¼ÅߥªæÇ6Þƒ˜BÝÆžfU¿ às¹Ü>ˆ®^{ŒIFï_M:BÌæÞa¾\gU»LJäûª€cVµ§ú\ãŽ–Ë •Ï8a2kcCÆ8èË6Ž s/!•ŽÕU%½êŠÚl]´=ôr¼iŠÕ.Mip¤›d“ôª 1ºIã5FUÔÂÒkV•šrª5­VG£…iÔ)]ÿ±`¦o Ѭ©ªßrð;j)iÖTՇěîÔNP·»8Ã0ÌÝG´)ÀG*  •Ž)Q~l—T8ÖuÜŸZ¯*X¼3¬>«*–²«¥V§ô#óI2(ëctʉÞzž ÃÜ¡ZË‚eNÑÆ}ß¶˜°…Ä›#ïÐ-¿a;Ü¡ø Ã0w7ѦµQµZS|-ãheUÅ´¥ìŸE’^u-Ö xÏÛωa˜æ¦µ|“Sa][~‰ZJ19Ö;”q|b1ï;eJ†aî4FU”dPÖ'í™I««¢½ªŒ£Q´dGØ5I§üŠÇ.2ÌÝKH¼ÅÆŠ½WóÍ_SKiÖÊý—›¹5Ã0 s'ôC$½²6n{h}êÁHʨœÿ³jÕ±(JØ3ýGI¯¬WU9ѦgÄ0w1Ù&Ÿp­Ùš±ýKÊÚõÕm+}Û–`®È6ñ·a¦5m ð‰5(Â%½êRÜöº¤ý3hUå¼;ª‡gS¼%Â&é?¨ªRIào?†aZ†É‰½´æ„-ý“Òí*6¯Ê‘h)òPU[ˆ™ÇÎ3~í{6Ÿ‡Øf¯7Ïl~b1lû²8ÃhŠ»øð+ŸÊ1ªØ—k`2ÄÂÛ±}àPxÆS|gF8À`-ÄÂÞÎ( ¶AlJ»†CÌôîÒ@} Ã0Mã'iPVƒ¬KÍa×’÷O§´ŠHJ?6ç¶´²"’RÍ¢xËd›ÆtEÒ)®Çê•©læÞ#Xk®µÜHØržRoY %ç),±ìZèR‹»ïÄh+!Œ’³ñú5ÄBÛã –£1@ìÛÜb‰+€  k/P À×%v›â?±]ß\ˆ5—@Ävr¹:¹î_ˆ‡XzÇu™›†â;Ó bùŸýìXÜN[ˆÄŸnB»|ä{±#Ã0-T8@cTD©ªJµAaU—ªê–šÂ¿_^A‰û¦Rêá”zx­¨˜Ei²VTÌ¢GfRêá”´iË#h™9Ü÷IÐUµX‡ñb¬^‘­Ÿ4ÊÛícæÎžX5=uŸuyÉ” »Ðl-/ù‚¦¦ì¹>9awŒ‡*æBì¢R›W,nÞAå׿«ÄV}G  L–»-÷Ú6ÿyŸÁ±|O¹Ž ¶ üÀ ²œ3k66%¾3/A,ôm¯ë?!2©vCøKˆÝlšÒ®™ûb_G†aî$òR?c¥Å”Xƒ2M2¨Ì’AyQ2(¾‘ôÊï%«S¤S~«SÖIåEa8Uy’nR¤¤SúóV€ s!Æ:–m˜¾b_ý¢ ŸÓ²-_4YK6§é+XôfO]ÔvÜ-N½7oã7À÷[ðu‚cQl@ìó5Oß'wñƒ êÎð%=Ä0væBd Â6Ô«ÒØâÚ{RÛùDF³‡üû»þ ·k0D7öo ²±l†a†i]d›|BµŸ. O(³F垤¸çUTîq O,»®5§7¡ wÆkÄÖ{ÏCl ¸ÂÄ9gßð„ lfüùr¼iã3!º€ÛC³ï¼àaZ“Ù¾ñ™ Øé÷_È÷ü˜Ü†8L¤§vµP±­a_°qd†a¦5¿kTX‚¹6<±ú0ÖÓIDATÌ:3ã(ÍË;M ÏRLÑӂ³4/¿†ffTØÂˬa æsn¶ô„;ãõÄ•ËãþâåŸÈç{BŒ{<‘™knüY¸yËÀžÝ¿!Æ;nt*ÛÂÄ>ÞŒøÎLûŒcO9v>ÛzjW€ ÝØl†a†iýˆìã®±aZsF˜¶¬&Tk® Ñšl¡Zs]˜¶¬&,ÁœØ ÃhÇñê‘‘³3 b¢ˆD—n%„™t·/sSâÿb<¡Ý8ÚÍ¡€8ˆ 'œÎ]…çîêÆŒãËã)í»Ú¼ÇÇWü]>ÞP»vBdHðpt¡ÿÞC Ã0 Ã0÷$}É™±™¾ýpì ó7ˆåsž€0z~³•ÛÉz @ïFâ÷p ¢+¸'€dˆYÕa_‘Ë¥Éõ?ØŒøÎåºCLì1^ñ¹¾6]ä}šÐ®ž™Ê_x"c9ž»Ï†a†aîIܯ6ã¯AtË.„#;˜ÇÄgÙ'ÎX!–Òi(> fMŸ’¯5Á19¦ Ä„•ÿ‘Ïí„0lhF|×rϨȮ…Ȩv˜dŸEÝP»œá®j†a†a†a†a†aZ)ÿÿDʺlIEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-flowew2.graffle0000664000175000017500000001204213553660046027232 0ustar zuulzuul00000000000000‹í]ksÓHÖþ<ü ½|Ú­%Jß/³ [!0Cf€d“;S©ÚRìN¢E‘Œ¤˜)þû{Z²-[’ƒmäÄšÁ¶Ô7µú<Ï9}9çñ¿>]FÞG“faÿôûè¡gâ^ÒãóŸ¾9þyK=üדÿïÙþîñïϽAf¹wðæé˽]ïáÖööÎ`™íígÇϼƒ—{GÇ”±½ýüõCïáEž~ÜÞ¾¾¾ö›Êï%—6a¶}&“æŸ_Ba[Áïçý‡PMYúTsàj?ìåOüðø½ùüd§—‡ÍËà³I÷â¾ùôxÛ^…›aœ›s“>A·G_GY î°äPäÛ²äq¦ Mûå‡ÇYžÂ#?úÉež§ÉÕÀ߇o¿¤ÁÙYdÄãía’‰ÔXhŸ h>5‘äñö¨è² Wy²ÓÿßU–«ÎÓ+³=ºÿ4è½·UÆ}¨npöÆÉ†Ï>LfSd£{ãfüõzä¡/¼¿8‡IÎ…DœI¦8{äIE|¥´„KH*Lø—/SÏb Û‚¬YîQ…UƒjYöžUéGN&û¾Lv”ŽL•rô8åM(2y_ݸ]Þ–×U³ª†½ÞŸltö¸—Fߪ/e÷fæ$¹œc°ìñÇ ÛOÃó°&£~.ºyò-9’èê2Þ‰Âó¸Q>n–_¤>=(bœ>5Aô„Â+¾Œ’Â;hŸ¹©7… ̶0ÞÂÈÃòGJ~dÜû‚¿Fël!IZÏÿ*Èsï7xéÁuõLÏÂlŸzAÔ¨dþ¼Þ¥÷“7þ^/àç02ÇŸÌÈA=m1Àž%½«Kçuw$iôäp`fCZzz¤Õ$gBt˜ö9H "IÍ„¢ø‘ǤöçsŠÑpÍ Æä‘GkâÓ&?•]Ó&A£^Êͧ¼™ï÷çG-©£äº™ôÐd៦%uç{ñY2[¸lŠÙÚ˜ø(ˆ³©"‡²\ÔTå(«ÚIAl`Dõ*%Ÿ’ŠVœh4ö,Œ¢Éª«ÛíH1 *&›8Ä ‹ ?Ù±–]÷nÊ®÷rm ÕÚ0L“ïM¿…¶²*19Ió3|Ã",þé ÎHÈI/é%½Å+dW§ð›Iô௓3aùitr†NÎâ>{Aš™y£AöÏ/¬—DI éþy’š>áüä<5&¶_N£+ŸÅ EËëj²×A!÷IßœåÁ©$èÁÉ HûÅ?£KöGž_ä&> z€‚èÁƒ¢1AÞIïŒxG&:ÛÊLú1ì/6ùu’¾÷ȉM–aá½}½çaR5ñ±P>݆°/3اìÂw îM9~ŸŒ5/NQæ# H%¥„cͦ”ôñ'D 1eLI@e©$ÕXµPÐþÚ5Î:D±e‘æ…‰>š4³y¡Æ’À¢XƒÚ°Æ>Pó9wôµ@é7BÓ®ÞS">‘¢Lr:y{ø°È—XÂë’D"!CˆM<ý(çykNÊ1•˜+Aad(ÆHKδ='P¡ŠqTk„²„°âC!ûAi‘„ê" cEŽŠ$Û~\$C·¦q_¥Atò¡7Kç=-5ÚBëýÆÿòµ”ù‰ +æ[THFÆL 6-|³ oR%5ü/ç%6ıݱ-lË3­:$6GzŽôé9Ò[éÑNI*_i)¨Ð ì6%I“ô„pÖÜ=´æ˜–ΚsÄæˆÍÛzé’Ø ù> ¶Rˆ)Î¥$\›Z{ jÍ]·¾e‹O²ïÍÊB„2ÅÔüÈ($Ð,|» “0`«ŒÂQ£ GA‹QüûɃ¿a¶8Í`‚—alÖ}Áù… ú³›0 ä7 ù0ñÔÓÌØ>6ï’7Ó-dqÀ¥¯qÖÊg¿ ¶¬Vì‘Ç0¶`,° *ù,6ëc¬0RЪ ½Ý-Å|J˜\ *Ç߯7w½Æ|/M«j8ï¤iÒª"¡6#þ¥9zŸ§’ŸQVìj­$Oî¼i|@9£ÅZò.ì×H¶xT2/G@…‹Jrcæ{II^FCWÔ¯ëݘa_ÂQDµCã„[oYÿ©§…-¦äwj ` !a´ ±˜QÀ| ‚5#xA£@rÈ&‰àÂÍK9£À ¼0 D§“SDøRbà.B@¡ÕîÍÖZ%uÔw©O2·ÕÀ­È8æsÌ·+2ÒÍ„}Í~®[2«˜ “Ë΄µ-‚L|®#VU¡f΄ié *¥¦œ!Í·Z ç¾¶g ‘FP:4n&Ì̈́݇™°†bv{3aõY/©¾Åp›°6h–dÂmÂr*¿SùÊ¿*¿îtw±T>V“ÆX ±)KlTûbbϱfM^£ÔñÚÆœ¨‘Œ¯”×f}’«¾;ùîXб cÁoaAÕ% Ö=&PáÌ»ïfµ‡9óΛ#6GlkAl¹[š¾‘HW{›%m®èH1sEg‰õ ·¢ãVt+:Až›4^ÑPç+;|µ+;ßBòÕ#$^!k+ÔR´ $ž…Ëà«CH‡›ŽÂ!d§)VhY„”vzF`†)ÖD2åB¹¾:„t¹©»‚Øí "C·ØŒžh°Ú¬läO:À.ý6öñGž >„ FÒ+-‹oß­Cõ}œÎ-&ë«©BÄçÆ­rÚ¾Ê9?n•ó÷UNæ¦sÝtîMçΈӰN³¹À¹WŸ¼§iØ?7'wÝ´ ³²U§é‡›KØ—=cå´€ì´^»q2PF± 4#Ì·IœªðPß¶2['Î5[oÝTâTŽ8q:âtĹĹgy÷ÌóT=’°~ŠläClCI¦4ÂT¹XHu·µxsc!Ý€ßy¤Ò•Á’i%AÉTVn‘/‘\I¸R¬p\¤¤‹´ØEtä"-ºH‹kiqÿ£I£àó(Èâ8Ä"¶á“ñÝ…U´‘¥-MY1"­ÿ&¹è¯aa.ú«Ã¤5ŽþŠkÑ_qýõ.aJrßF©¦ˆI¬XæÎä™l69ü«"tÒ„á ?¸kãÉ™vOW%ØÇ!N™”R :aÒgT`Ë)'jxŽ´ r_´‹eO?Íü¹Ï?•>>8ÿTØ÷üÛæýWwú—´mp·{DÞ[ý+$Y˜‡IÜh*à WB)Ž$â [LÍ™“<°™kûU*¸½y;Ö(ûÄ£”‡Ã€Yµ\P 5£©E§®P;\_}Žô=¬J}OëRÔåuÊËŠ®Ãy5C>ÔïvUZB‡-!w¢­ 7­˜3 üVÌæ!áF´R”k*‘p¤çHoƒàÎHO,HzÜ.Tb¡5“ 3­Õm‘¥È°³^Í1¨êTQÇzŽõëm0ëQÊ|¦V¡¥äöT-VrÚ¤ÀR×K.kî* 3Á,tî*6ÆkÆÎkÓý‹Ô±–‡eÜ–.çÚ¢K×¢ÓÁW鸉إSB}аb sw¹sÜtýòrDî¥ã¦{±ãv);Jû.Ü¢Nñ*æÛŒ%£c&À®£û é‚¶j]!¥ :uí:¦ùwtÊ1c:ÇtÂtúÛ­»C°Ñ—':g¶mÙÆ4uf›#3GfŽÌîŽÌ´ó±ûÕ xåþÑðòQ—ˆ{Xå†L>¥H"A¸bðŸÕ)j%²ïÓÇ¬9iK8Hë*ªƒÆ[„F±44 ‚ôb¤$¨ÚJ nŽk9Æ4:htÐh×™ƒÆ¡±«þ¼yW‘ì\% 5*¸ŠPB°™È¸ ®~/Nuí܃¾¡D*èd²ØÜG±#ZI.p«;Þæ>”bÖAR0ŒêŒÚdè™–«FÚ•»qdwQIí[\Ä’AÉ n³½SN,¤ö¼8.ýï`@I z£`ÎÙ¸s6~?Fêp±S…‘¬IWjl0®†ÂÈgc+ªRéÛuOJÀ†dÕÔiŒNc¼#½«0 TøE`z‚)£\qëY«~ªØ.”`ÜiE…¶"h @ŒµoEô,( +îœIw°7;gÒΙ´[ïwΤ×`¹ÿ0¹ÊMê½.M6zk‰áCZ4펽\×ÃI;/ \i9•2%(¬ [ÕC?p`“ƒ2mâ1pP´A9#š8í`—œpêÔ¨#PÆhmõg«áYçMQŒ4q†g¼Io:Þt¼éxÓñæÚòfë¡®ÚþYømlÛu?}†%% o’õÊM8þU"Ë"® @YàüW#âv\/.î!£„ˆ¹§„ºÙ~ùÞ[·‘kʹgrÛëal×Iÿ®c" ×Äœ‚…,Î8cL}äŠ ”FuðÇ9óXï3ÎbQ®£õmÐ߉3µÝÑá,TG’î(tëQhæŽBßÒùÉ›¦-•Zú¼V„¡a¦pN‹=Šõyi5óPËç¨ÝE·Eq3·(ÒUŸö›ßá‘T-Æuî£Ã#.î­Ã#§ð;…ß)ü›¥ð“NùIå[“Æ%‘¬…×ôd$©}S³&­QêhmsÖræ‘8t$èHpI®2 ß²Ò㌻ Zéa„8ãÎñšã5ÇkëÄ­æÜÒ$ð«9¬+e’¶¬æÌô8±ÄZ[Íq«9Õœƒ ÏM¯hý§kÇÜùpìt¹›¯ îÊ%-7Ó‰ã2ðêÒ!ä¦#¤pÙ)BŠ•#$W]9sÄmJ ™íÍqq|uérC–rg;‚êB¥yŠûÖåŠÐ\QL´*w5öñÆáq¥¥;<ÞÁNê»Ããn2×w‡Ç×öð8#¾ ‘­˜Ô˜ :nƒ8u±ØÚ¶,[çÍ5[lÝPÞ$Êñ¦ãMÇ›Ž7×€7÷â,âžYC'5FR²ØÛzKIÙiŠQŠ«Û¤©;ó"(îÞGˆs£áàÔ¹ÑXÌÆnr9¸ÊÍ:¸ÑàÚå\I¤°Fš°Gó‹•’!"˜‚;åæJë˜é‘2Ìk#l†¶T¦Ž&±b”ôÐd6:GS·E"„ G"ßRö=GÙ¾9˃SÀÅ!àÚF—nGJš3Þ–wd¢³­Ì¤ÞùÊN)¼á½è8 ÎΞg!jxdùö;“åÞQÏÄA&¹•ߤ5±húóTw‘÷x°Id¿ðñÝ£AЃBÆ ×i|5­ðV‚;œø_’¶f/¯ÔÈ¢<”\g6W•¼„ËÔq0ÈŽ“9ÒWO_<ÕUØ7Ùˤ÷ÞŒ3¶öÚDê·ažV˜2£šbQî 87c)l_M*ñìîæ&#ùoÆ Ž“£^0ÑŠñÚ]¹n|6iUóxb¥Æ¾öÁ¿6\Ê”6†BËB¿­ÆÃÍäðs^þ<Š@€²ùšñ64×_-vÖ‡Ö&Wùì!¾‡—An¨&>ön/L{É«0~fyM°ªÿ"í‘ò0‰kÉ‘j飢™Ïãó0n¶¥ŸäÆØÉĮԾ4ñùÄ4Ϩ2šÒ  e­Â"ÿ ¬„s¥Ÿ6hâûYÒÓ¶á4âé3ž£ 31÷Xü±T¼Jú!ÀkÑêgozT(AXla´…Øá?"ô#¥Þ?lÏ5ê* šÕáíWAž_˜kï7Ð:ƒkèŸzÆ×Inæ¤ý4åzª“+m§ †ÌÜ…[lz ïé}6GZ+γçõÑÓ$Ï“ËWA -¨ÆÃÄ|î°¼³( òi±~gxZ„+È*kx‘¤áŸIDÐî0® ¼fE=0`ú­=ÝÉÿì¿ø5úýFæ?O£?ö^$;ìßOÿ½wôübçbÿ âøôݯQï|~÷÷ßà?.N_¼vàþîs~|L~ýðÇ»×hgïÝó£æçOÑõî/77ÿ%(ßÒ=Un*ÿ št ¤[Š/?Z˳d+`sÔ1¥¬·ÕѰzFŠ!×Ü·ˆ3ɃN1ì+¥AoàH*P¿|¥ v š@n¦E:6a$Z5p•Cá8¬¢øi,/^Ä~l,ŠÌÐAMÐߣÏ_‡—Ãäz' Ïã9THZS#KÖ¡•Ö9TŽÃ¼‰„»Aü1Ȧ”–2Ç%¨èE3ìŒF©êíôòðã<š^‘Ù*vzÁ¼oâðÕ©LÚžþMfžÇy˜ÞÔíoçU:ß…1¨³Q}÷*M¡'Šžlêì-*ûóOƒ aûoÙÅ:]ÅÃÃ9‚t ©ª½Q” {0¶˜ÖÁ˜Ã)kÎ!ÆÖÁÔ,Êã<½2ÛS÷¯¢Š¡ à·«½o“÷§·¦Ýôð uGQpúÂXñ®[H¼®B YúМO¡×¸#àÁ±OkÅÚ6S<†PKŸü‘$—u]Ô絊m¢·ÅY+:´¬Ô¶ÈË­å.Ra4ù7=“SÁUZªÏA²ôäÁÿœ×Þ<:neutron-12.1.1/doc/source/admin/figures/scenario-classic-mt-flowns1.svg0000664000175000017500000004556713553660046026112 0ustar zuulzuul00000000000000
Compute Node 1
[Not supported by viewer]
Instance
[Not supported by viewer]
Network Traffic Flow - North/South
Instances with a fixed IP Address

[Not supported by viewer]
Macvtap
[Not supported by viewer]
VLAN Sub
Interface
[Not supported by viewer]
Project Network 1
192.168.1.0/24
[Not supported by viewer]
Internet
[Not supported by viewer]
Network Node
Black Box
[Not supported by viewer]
VLANs
[Not supported by viewer]
VLAN Network
[Not supported by viewer]
eth0
[Not supported by viewer]
neutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-overview.png0000664000175000017500000035542113553660046026436 0ustar zuulzuul00000000000000‰PNG  IHDRàšgì®sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|EûÇgöî’ÐDjB ö‚ ö.꫾âûJ‚ Ѝ)z’ ØþŠ!áµwì EQ”"¨ R)‚”Ðk õövÿ¿g/6—Kr—Ü%wÉ3ŸO²{³³S¾;;óì3ÏÌÁŽ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0ƒ€dL€ 0ÆNàN§ë [Øs²Ò4võ]þ‘άU·Þ4Ñ‘´Äé¼½¨¾óÃ野½gºG±MÏL]ºX9&&ìÑ™mÎ5¨9dç“­µð8¡Ë$Ý!VÇ7ÿãé±c kcÿ3Ù™¯xö㑞nЦèBŠmÒæø{ºóá,}JFÖ-º®5ËqeL g:ªª‰ø[꺮H)õp¦U]Ü£'Mj£–Èff82'í-ö<ç¼;Ïô‹ôãhçc‹Õ’Rľší·6˜üºUíßo§ØÞÇuÁÜްæóÐ…ýPuõÝéœkß!wö8´Âœ´´máÈO4ÇI¦(ƒ#šËÁyg¡ Àx((rOÀéÔ•­î¬û!YÕÝ…í Àšð¿ñ—hïøj$k‡K<îáº.Ò„t·F~Gà/j]ÙóêÞ{²²ú<“–¶£²Âìr/LR…þ·tËoæ‚ʱ?`L@aL ¡áœÜu«šõƒ.ôÇ Ül çWR‘+RÜ-¥2Uù#ïÑ=ß'g¸²H‹ÕЙZ¾¢yÓ5¡gBnn?J)RIU„|,WBÈ:‚xq ñî¹»ï.ši)&ºSË =¤•ÄGÓ²Qi®.ÁÄMaÁ{êç4)d¸¬ër=xŸ„¿é[Õ-¿v>ÛZ×érzL€ „— áå˱×3˜È”ô¬ |Ÿ A{¹M8†Ns=¸Ú7[Ée] ÅºU[x×÷ ÓØ~§dL:SÓ<à üíTì¶Ó§;'üãË`¤sr÷¶büz!2|/…ä7LO®ôFT>þâ|í¨áû ‡-6$ E`$º”/ÎÈL{Çšµ”ŒGOÐ5õÔÓ^EjÞt\û—õz¤œgOL#Á»k¤ä'ùÀ{°?×¥¤Oº&Û•úa(â $´]·£íZHXØ@ô` xô<+Îi @{t ßÐ"þ“äH:ÝŸðMÑæLLûB*öAèèȸ"•ìÄInæÌ™¶šhÌΗã‰ß7 }Pi†¯8~ëB;݈W—3ý ßtíç„uN§Ô‘~•qJýŠ*¯û¹HÜ‚}Vô|ýDUo^ٙ㗡žÞHB¿’L«ªÊ 忺úBש^UO¸®QºNç̘šÆ_Ó÷(.¾yÂhÄ}ä§ ÏÔ;'O6̱|Ãó›êbuõ19ãÑã1ŠpT0ñ†2lmêFMW—÷q=Ö¬¾êjuyãëL XõÒè›IÏjB€:€9Ë×l@ƒÝI*ÊðœÌ4šÜU¥KNs½ýZ˜§<“íÊc LöΘ¸ùVÊHÁäÀ#^²s= /$­ÀûÌìÌÔ'+›Ä—ìœÔK¨ž,ˆN§y2÷-V¶ûý ¸¥é-Czѯë!qݼ†<´ÀwB®Ôå,Å›QÝÄ0*†°¿@>¶IûÍÓ2',%?_w§3«¯GÕf"s\éCðñr&@>I&Tfß𾿑_ü®ƒ‰ÊD°~«Âõ4×§ÈwOäÿ%ÄWa„axº+Be]ÚRfd¦þ@÷ `þ’™Þ›¸ŽHÏ<ß#ÄDHAgÓuäm=â„%? F2w†+Q‡î?¿\ó0"ŽFÀ sñ•õu’½çH§sh …Ÿ}¸ÐwÛˆŒ¬¡0oÿ7¼Ï@Ü-ˆ"ÿ"ÁÿÈÓα{)|88¼aØ€+ÊM¾p3]äwòÔ*Æa;fª3u ù{Y)¿æ¸ÒFÜ™1¹Ÿª{îÅçä%¨3G€Ó00Õ¼ÿNçänUE]ýðw4žÉA]êË©¼‘™–c†þPÖÅRÓž¿E3²Òo7ý}eÏNØÆÍp¥~ê}?´T0Š÷è9ßð)¹.×41ù; ×âñ·Ÿ³’lg?²U]0ïÕ™ŠŒíéogÞ#ß|ÒoóyÄ%ØÚbäeµ¨‹¯âæžL¢J„þx Þø^7碠^]ƒ:xBéõ?ÀéeÔ=˜ÃyMzêNÔsIï| hßÉþ¿lR§¢È ¨£ôìBú¾QêÖ kº@ë^é{Öe-7 ³”K6¸œ‹Wôì¬tjKùçz& º6Œz£^Äã¥b¹ ¿¿Š=''sü 3|dÑD JíI4„óÊ| Ìýóï.¥‚îDÛY¯ø^÷ûÛ&NPÓå@ßëè0{àïüá®L¦/C䳡c|Â|ø£sЇ ù§ÉÙÙå:Ї†­!|ÿA†L*VA›– Qp-uæšÛó;ËÎêŒô¤~„ïTœ¿‹°G¢ÓùÔ{è¤lø¬¹‹¢”BYŒÎëU×n³¦a=W=úmùúÙë¯y;6ÊC#(ÛjºååÖxéœä÷roüú0ßëô±áûøgkú›yÝ`@q–:MÊ>(ûV8û /)Ö 3^NX›å/3‡gd‘¦øw¤y#žQ,¸}‰psñÜšàïDSø¶ÜsiMÂh{ ô?„ ¼ß¯æ/®N›l‰+¬§ôü)¬ÌA‚™áˆ÷~#ÓëíÑ<ßàâU(ÇJ|@,ŠRƆ>0 |/Ãóº<â¾7ð·q§iZ6lë?6@Ä*•Ö? \">\nÁÄv¥I•;Œš4éHÒÃàÙéˆf¶¹tQÓÅF]²ÂH1Ö<âÜs6ê=ë—qÜq”ûrÝ gÑý•¹š¾Gp©,MÓ_Ë×â›ÄËQT÷ЮÜ6⡬!æµ@ޤ½ÍugÍB]z ‹mÏ{ ü¸'‚Õób?B¼xÅ…hâØÝ Ϧ³÷ù‘<@u¼ìOÓ‘‡Ð¿oÁÔ Ê•é­{fxß#&ÈÓ‡Í(ÓŸg—Ù»ÃÿÌ1yáñ!¹:0Ë/ÔiqÂß%¤§Býò›3H%@š;vL A€0AÚO´Ûúz§s ±˜DuUìk‹<†RÔ{o…ôîR“£¡!4Ý•ñ½yy”sRO·[ûèeÊæÃà?üF«®¾ŒßŠÝ!ΙæL/³ç„@q¦{Þóè2‚ûq9))†6×¼BTGä‚"l—Áî´L8¡Î|ßÁ’Ÿ^ïmîÜ‘ÿTÙ=þNì¶W¥ª9þF {õåaŒ,[sôùú+´Îߤd¸ðÑ N•î¢ù#2&ÝQÕú½GÌ7nw1dŠ/nE\åSÐBÓ©‹ã‰—©¹¥´æ¬Xw üi¢ç‚ª–ÛËÉLÁŸÇGÉ/8ž"EÌÝÙY—¹é|¼½Û]‚iW¤~G¶+:qÑVm§x¢­ùÛð@¾õ:qQÕâñŒà”kr\©›×Ivçkô¡w%´¯ãàïÌq¦@<lþ[Pˆ!ž1ÛǒB Nºå;?øàAÓßß1Å9©®z&â¹Âs¹¢Ü;”ñè±%ºúÒ:Êß½µyªãâ/=_?]:âŸI{p#êÞ½¸ö Ú—Ôãª+³σ%p>Ïæí¸¶Ío7&ÃcŒóé–j>Ê­_…ù*4òòjéJ+CinŠîÑ>ßù9Y7›qÑÑ[ÇC÷¾[7¬y¡óš2Fó-¨„:1;ÉÑí“·­¦úX’ï¹ õjO‚=¾§uôéþ'žH8¸¿¸æÌóÍÿfÑB€5àÑò¤8ŸAÐ5­{éM½ùH1n+ëz–¾ëà÷>©?o( “¾¿‡Î¡=O%!Î §¦z…;ùžUø¦kªIƒÁZï©lÚ5Ô{ƒÏ)_° ßtÕèô¥ü€Î5)ȼ¢J—ã¿ B&Æém¶«‹.ò üÝʵ@ðêMç< ÿM×!¡éJœ¼<~µ^Ъ. Á79#ëZw¯0-ÐÛ‘ eMÂÅ5èH !ô=NþnBžÅáúùôZÑÙªû!z†ˆì2Ê„oŠlÖ}W3!ùš`þ¦£•5iR­×êú<Ùùhg¡©¦yOv…ôa–¿0#¹ÉWø¦°à‚0ÑT*â«ðMצ¦¦î‰MPn¢ç„ŸÜå|,‘üñø’qÚ­Þ£ÏMþŠÐKÃù\·ü„ð†ô±Öº>¥Â;”9þ¤õ°%xùÓÚ¼GÕp)Ÿÿ_šÔâèŠ×”GÎF9:Ès?ê?ty_n¡%Fû u»Ý–n ßjŠóÞýÐKç“NÇ@\¨ß·Õ kFkÀ£!¾ï§QDéhsuTJ/Ñ;•F¿Ø*|“Õm˜©°ðmåÏçQGà°uYç 3ê(^-§.wWÒ¼^:¡Ð°óµ»…ß!waW>4Ã[°­þÉV«]wº³’,×.£s›TÊ ‚æu˜‡üHç©oúY6a›iýmžÛmÂ1fíWch†3xÙI Å^<)˜]xýti„1žž›h?¦?4þw l«P¶Sða3Ëàm¦ÎÓw™¢ˆ¯é^hí šÎÉty<ÂÅ‚˜&6Äl¡_E×L­û :×[Hpô–Æ=ÕL#£´Ë*Y#ã±$­êÂH]»Œï#“ |ø<—œîš-T÷<ƒã $‘hOŸè7Ey„>žü^º×.Ùf¯(¼ã†çRSwá9¿á2ÞívŸGqÀöõSÂÔGæ-Öxi<Ë3ðµ¶aZfúÖkþΑ©þ䝨í~…õ¸vÍgâƒOów/üjõÁ § .•¤X‰·tØ“‘ÏCÈèwfL:·’`eÞªÇ}>1۹Ӝ֗](=I²_Šk4ôvô½O=©À\hß·àëF…\ÀuÀx¾0¹³½‰àøKœ½Åe4Úb¯­8k%êù4C`Úw7ÒY¯ó9ˆvl‚íOó_)ôã›J¥•ò¹`ØooÞiØÆŠ&›|.—þŒõëOB&ý@‰º¢tÅqs©iGW\Ò°ÎøHt:#K#±¼¦20þ05ö–kBÄÄi[Êy”þÐÔ˜=B“Ú,Æßu_¿ޤrÕ-ûáEò£¶È?þ…¡áÜû‹¯"¢y‹Ø÷}ï+ÕJÁlAéÎ ×@høG Ñk©ó„Íî%XzH™ÙLpí~]$€“¹ˆ(*WBøÀ˜E,÷$˜ÓP;ÙUí¸ÿû6T 1¸ç6Q .ÿs¬“¹+ÜÌL ‚ 6‚h¦¥¥mE7IËÐÅç(H!=B»ÂAQWª­õs—[Úm?WÈë(Ãßnÿ‡Ž4,~z3òÐt´ó±ŽÆµzüG‚3¦?!0ô§Iv¥KË]a-ƒÁd-ÆÞâG Í=Ì Ê9˜¡à3F×úCø #Êfˆ©4ÙNÈuЄ 0Ìt} ]“qd¾R{G¶¤(ã6zî#œW;©} UÇ@‚즟ôýƒ0=³ê;Ã{Õ; E厣I•¥{ü®Æ5LQʹ—é—&”éˆ9Cs‰NÄð'¿ªœ÷X’yI­üQEØ$ºæQ¡ÿ/u‘ö™ù2&_ ʼn2ÙtUIq7ñ#~£nëúã)z™÷†ðXë÷-u#ˆâHFs.Áž ‹`—6 õ1Fjž™ÕmnD“¡i¤D{Ï>ø:„©œÞ¦+;‘0eE€ðˆzœ™P AH±Éû)NhL¨N3ÖõÕõF7%ÙŸ®4/ªv…¿k´–7„¾Î¸g{q†Ån[.¢ðÅjI²¿ûêÚŠ8Lº,VÕë 5˜¢è6ü|%Ø|”x Œ HI¨;»Ã+HCè8¾Ñ»°—›¸ŠÉcÓG‰ÌÝ=&§A`^I>ÇRÍ™¹À)R­L$;s,RrÙ™tCËèQ¡Iôãh¤Tסk¡üd Û®96R˜ ¬]K»W¢]çv É‘ñåž­õ?ç¿“_‘zèz?×ħë4úpòw blD½Gfõíñ$¸,E¾ÇZñw“¸m^+;Úm?L1w‚–×+ó¯æï–QϱÂ:<ýzüÀ1`>fT½éÐeß*…oäaíœÐ“V,€­Cûª?N—„ëV˜©Ÿhïx–ïz¶T,¬^1M‘öÓ`³íéFÚè¬qü„îòe=¡tõ º!ìÎö2ÒvÐvEyÙorR¼V+ædDرSÜC ®Br¡KItô¼Èªí7ãA˜oÌs)eöߦÙœ‚ågô_!¸Í3¯r46Ë‘J*Øy·¡Ho::oÃÜȼ†+c¼ñ̽÷“Áïf¤ˆ=c¤¡7Ã6¦# 7´Û'øcWRúHÒŸ›À ÏbJ¢#©_eч L:4Ö´^•“™ZÎL%ްã½ÀÐc"î(|è=Ñ#z–ˆfösð.þPY<‘÷Îi’­g&}¼ö)F‰NmOÅd܉h/Pou¬(#ö¾SòJøý…½ʽ+dFg—Ž£ÎþŒgu4ÞÅ,( ¦Àÿkìˆ'$ï[mê†5?ÁžOw¦­DQG£vÔ‰wIûmÚ<ìÄu–±Y¦Oõ~àè´ŠÌ6»”ãLû5Ø´8<ˆhƒÙ1ÆG`ô¤ImJ •DÝvÄZ³?"ÐÜíCÃßZVuŒ†LGt‡Í1õáñ›~þîµúÑšÆ?ü¹Ž:ÓxEØwöïsÔƶ†‰¤s2 (8XÒÚ#í­`U_ Ú[n ”Y¸ËAkˆk» ºk6-®y“µ•™CÐæ&Ey…=âD“-•-/î¼FbüôcÇÁ&‘[k+p¹’4Ui–(&¬ vÉÈh{|Ÿ¿a†R ºiº-¯E3}{u[Ú“9ÖaïugéÊ ¾Q†ôw}× ³0Æ;¾#¯½êmìBÉs‹#·DJûcæ‘L€ 0&F$€OË„" ÖÍì˜`L€ 0&PCl‚RCp|`L€ 0&À˜¨ ÀkBïaL€ 0&À˜`5$Àx ÁñmL€ 0&À˜`L &xšPã{%¬p°KvÔÉZÝ0š 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&àCà.çc‰#Ó\|¼#úgrƤӓO¶ŽèLræ˜` €l€eâ"1&Àê„À¨4W·ÔŸº2&À;6Aiì5€ËϘ@Ð ]ž““]ÙÓ¾©ì本ôRÈ\]g•†Y†ûþ;ÆùtKë=ã{¬~Gc¼Œü“³³º{×BœÞ,ñ9dÙû IW5mnòCY)ŒÅ ó—‹ |ŒpëE޵IÛ‹8ïîQµ[,á¼§ªç„ï©5ULAþX¤ÛÎ HÚºP6"ü÷:ŸjeÞw§3«/∲žòÐd°ñºÑÎg›ãáZE×ÿ1ýøÈ˜`B°œk`L xGC3M‚hÕN"Œ.Ž. ô*4â…ê¡kñûEóÆýyîkàßÄ&b^%?}Ë®áø¢Ý¡œô‚3í÷Òp/Owý#4í1ü.pðºH³ÙäEÓ'¦Ï. +’Ó]H¡ß¤ëú«6öêÿ‚ÿÇ9ãÇ0ÃZ¥-mstMSòÕ¢þ¸÷cº_uëWáƒd ÿX!´+àõùkÏEYlºâ¨ôC…±cL€ 46¬olOœË˘@í è²LFvV‘¾[JÑÂÁdãoà `rs¹û¤øÂü4Íõàjò‡ñÆMøÿ»Eø6‚C›þNú‘ý¹áQúþßNŸ˜V&|“·Tä«ÐFw‘žØëR2&)tý(|8‚¾éo=’vNæøÈïv)<Ì{!x_…ŒÏ…¶{®Ð à^§i),Ýczñ‘ 0&ÀXÎu€ 0&<)¶@5ëjn†y‡¾¥,Œ”~µì.WÒôôôÜ{²²Úèƒ`:r—Úðnª›ÀvúOÓŽÐf“ɇâÙ›Ok¯#?ÃIÝÔ’›>¢ƒ’:w«–µ™4ÞðœGpÿͰÑÞ:¨ï1ßd—…,hÚˆ÷[Ä8€îNv>ÚYw»O²Iq¿&u;ÿ1w¥gõ¼+còH“µßå1ó/&À˜› p`L€ KBæz–W@¨…Rûð„Cßxp¹+4À_›þ±¶f3‹ÝyÏjEòFøý_a‘~yU‰·a¢¢×a¢fâþ%ß4ýŒ#B·-Ô8¹Ëêôó­¿éÜé”&X¾úN§s&&?¶ÑrÝ ‡B3þòСC=¾áÍߦCÂ=Ù¨ùWêRìmo?‚þ.%×½æ`‰—ÃÖýµõЉ`0ÅŒŸL€ 0&à%À6à\˜`AP¤¾@ÓŰäŒÉ—ãÖOýÝŽ5À/Ô<LU”ïÍëÏ9ï΃`üì \×ÄM«?6aÂ>3 L[ÖA.œîJ{Åô«ÉÑa·½Vâö¤mÕÖ^¨ˆµ*4Öm¤Í |å.д•&bާP—‡Ô¢s å¾æ'ŸÓºâ34÷_ MÎ× ÿA9‡#†5à•#ç+L€ 4RlÞH<› 0šè`?‚¬\;h—¿­Üi¥Ý£O‚)É_ßÃWè}šéSRÒ\˜Ä¨ÓÆ=¯Xsü;ÂÒ.›Vÿ`ϧ:S×@û¾ù¸NóèCqþK¶3µœY‹oœ¦Mæ3PƯR„ç|ßç„æC3.›.?Åj/ý¥nدxÁ9n»yL€ 0&à%Àp® L€ 0 ¶KŽñpý­öd˜Züfw`81_ÍË”fÐ_íkò‘hOŸ³Õ• !ûy¼Ûõí>»œMvSG–<ä¾Õ-Å·)铯cª–/,> æß§Ã~܆ɜΠ²û*ò1±4¼y¬üö ÒVæhB»eÐâ[Æ—™ÙÄ9¾€éIáÿR¾Ryb|… 0&Ðx °¼ñ>{.9`µ 31m.L1ŽG‹ñ÷´Ù ±ß"Ø…?é%¢‰Ò—Ö÷M‚ì³±¾÷ë°#§{ßôÐi‰@%Nž‚øþÐuí M/^ƒ°³u¡õ«ºßñö¦ïBnŽ¿– ö„·« LÚŠ¢šúÕÓcÇšqÓÆD°ŸG×lR– ææu>2&À˜Íèaǘ`µ&0zÒ¤6ØpFy&-mG­#+`æÌ™¶9ËWw…0_pA;}…õP¥ã/žúLÛ_~Ø 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0º% ë"¹;3&÷óhž!H«‹.õD$Ú¬.Òå4˜Ž€.ÄA©Ë­¸k£M±Íž–9aip1Dohn§¢÷ÙqΙ`µ!P}_Øp§óå¸\wî]Bê÷]t”RhB—; €oE¢‡jŠïeL <Ð5…žˆ÷¶® EH±ïí3IޤçÎۋ“jýÅÊíTý±ç”™`‘B >ú¾°àÉ陃u:ðÎRʯð÷¦¯|1m„}‘›óÁ˜@åîœ<ù­@»T×õ›ñw> 7!ôðWÆ×•ß]W¸Š®çŹeL€ „›@]ö}!À“3\ctM<)¥¾š´»³³Òç…ÇϘ@ø¤¤¹úkB<‡ޕЏ?'3}JøR«›˜¹ªΜ `L Z „»ï³…Œ·SÓŸ–B~ m/ÏÉ».”ñs\L€ Ô=¥ó¿ÛxÊ ^“ZÁ1І=eÀ Kç}·¸îsš¹ GŽ… 0&Ð „»ï ™œ†su]ÎB„f»Òn€Ù LjØ1&ÐP@ø–)éYï➯÷%ÑhŽÂíTC©\&À˜@ÝWß§„"û4‘ ñÌ ³áh3Œ…ïPPå8˜@d0Þk¼ßÈÕø›QúÞGV&«È ·SUÀáKL€ 0&à—@¸ú¾à´Ú‰1á6ß9Δ¿%`O&À¢ž½ßh4FÓûn¬rE%âv*Šg• 0&AÂÑ÷…D§¥ñ…ðO¸Œ ÚÂYaa"@ï9½ïÆ£aJ#,Ñr;¬)`L 1ußWkœ6¯ð®ó-ßl €ËȘ€Àߤ÷Þxÿ£·SQð8‹L€ 0'ʾ¯Ö8ípI›ìÐ:ßγǘ@ˆÐûNï}é·!Š5|Ñp;>¶3`L ±eßWkлЗ¼ÉNc©~\N& „ñ¾ã½7ÞÿèÂíTt<'Î%`L b „²ï³×¶”ØZ>ql­m<|?`ÑEï>½÷ôþG¼ãv*âg 0&BÕ÷ÕZǺßÍ@ìPTPãL2&2x÷é½§÷?â·Sÿˆ8ƒL€ 0¨ ª¾¯ÖxTЪãLÞûÔSMò÷æ'6o·ýÉȯãäÃ’Üð4×­XçýÞìÌô~˜„`l²4<=s"åð›–D# ÒáYÿ‘º6›ÎœÙá,0&P‡b[^‡øê%)ôU?)RÏÉve¼X/àD™@€XT ÁRr]©kúƒ‡öäŸIáì/ö`ç½?a#ÿKŒÃ‘ñ¼óÁ°šê8ºâtJ-¼Bw[ìu’õ>|ž€ßßZýÂq>"cÒÙ˜D·@H±0'3ý\óÀšÝiÈÏi9Yéý¬þµ>×E;ÄQ®ÜµŽ“#`L ¢ Ôw[^×pBÝw$§»¾Aqͦ\4}bÚlßòÜ“•Õ® @Û®ØäÙÓ?ó½^«ßR?QJûZÅÁ73: ŠI˜uÍÈO"9#ë_¾?„jXU¤øÍ!OW¤2T—òä¾Ek1n{¸K‘«º6Žv>Ö1ÜéÔ[üº8;%Ã5²ÞÒç„™hð"¡-¯KÈ)é™ÿÍU³r‘¦æÑ§ßÿÄ ማãdÑN€5à!x‚Ж\®kÚ«¶§$ÙÒ°h¡Fôz“HAJ•G1"còIMm¸Â7]Š™º.'Ls}úBVúæÊið&À˜@ð"¡->×µ»J£+îÚÅâïnù &«]|ð@Q®Žñ‚ý˜@c&Àxž¾æéRÈ-Iöž,Âw¥1';³ãuÏ®L‹ Æn‚]a¢²f3;ØSŸ°ÞŸœæº Ø%²©#YTŸB„¾5$Ñ•ŠÐ3¦»2¾£DF¤g×tu"»ÝßÀìÅ8wÃã|Š÷ N´§ßšëÉzÖÛ7 ¡·‰mÛ¢íswß]h^(î@Å'Ü»C£>&!í 4/³I%}Zfê|óþêòd†ó=:ìö4·Û3PÅ*x¸v™ïu¿ƒ)4_wè°÷»ž0sÿ ùÏF9JüÅ û÷ ðÒÀó$„Ù…ðŸ%Ú;¦:·ù Ï~L€ D>°·åö&£tµèY)ôh?òÑv|/ímîâ`¼î.zmδùÍ¥.Ù¶1/8'¬3©¡]_l“¶4Mëˆ0÷@ÑíÎ:ìŠõ Lóž5ÃtNmêvï[ˆáíIعï]ÓŸŽ#2²†y4ýÞYé'$geuú ]— Ýýˆß;ÏE—™è;Þ£ð´çΟƒö?š.³×‡“3&½“™ú“¿p¾~Ã3&'5-lú¢ïr#oKì"fü ®WYÖæÿøƒ?$¥/tØcïQÕâ mv åƒÁB(× ¼@z¤ý3úƒEÙ®´'ü™>ZóÃçL Xl‚,1ŸðNç̼¤d =ÃéZáÅ÷ .(¼îÞµHhâVj8Eþ[*âsØË=œëÎz¿\x)[#îËÄ!÷ëFØ h &£Álíâ‹»Ò3¢ði[ÿ™Æ½R¦/.]*S¼¿ek\;+W4ÿEˆŸ‡ÿ8¾ƒÊ‹YÕÿ’³³(ÛB„ºY LèîÃn‰ñª¦ÍM~(k`ÙÝ(Wey* ãçDJTä4Œ—Ò0±Ÿ 异)> Æac:Åß›¸/Æ Ó#ÿúÊEŠ)i®ÿ¯ÐHÛðLÆ ì×ðž«æòfT¾°ø7ˆuÒ–«…sÐnl’Â6íø7ø€ÿ—®î|Â÷·ðŠ¥TÀñ&ÏTUõ}²Í¶à;Ö£{þO“zü^6q;µW0}|&9ÃEZfÃÙ„÷è}5Eiú™GMmèývHY‚öë5ô1dù;õô¡÷Wºp{Ný8MÈxÙ¹ÍdÒ«„¦ýøú VÎkDšëf©{¾Cù÷¡÷à/el©Š’%)“Œ¹UtÃçÓ-õBm ú³3Ð'8…TîBØmª»d>LP¤Ã.Ðr ]﻾ԅÖ‹ ¤"Î ¤½}oW¾óä³Ð` x-YnkúB ŒUHÀmU׎‚`z¾è³|Ñ4"}Ò/h\?KIŸtI¶+u–éxØ”ÏÂxªé—âtÍÑÜâ·WÃï©™©?`µŽÐÜkùì9çƒ[̰tDþºBãÒ7ÑqÌÙÖ„`óbÓß¹¾e×p¤v¢Ý¡œô‚3í÷Ò0/Owýƒø1ü.[I¤²<ù‹×ô+QKš`5’· }¾Δdç“_ç8ïßm^÷=Z¾äGm¡rOÀý¯Íp¥ßaƃFþ½|w>:ËÃnô³ÏÆïÌ{P?ŸáʸªôÊ+°£\‚¨—|Ÿßá;ùŒ 0H&Pm9FJgBû<©”ÃûhËâÑ–üÂã hãG™þjBÏܦ>q4üÖZ¸“`oÚeŠóÞý¥~ }U¡Ð¹w„ó‰g¦;Øi [åéÔÔÔ=0Z߉ºë§g¦y•8¥wÓžûK}N“œ”÷N×U_˜ë^3á&ú K~d+ž·¿ø äå]´Ãe §sîë[Õ  ùŸ‚6ö †Ñ.cÔ@¶“2¦wvæ8“ÏÇÃ3\Ñ’Ð^–L å€ÐQM±7Éž~»e$ú¥²ˆø„ „˜Þ{vµ"à‘½è~|so ,ý:|¡k¾Û¦»R¡1–ãëûFßxt»í5«ßô‡Ó–"½Ý¾Ì­þUžë2Ý*|{ß—ªÒðš·Èß-·´Å¢ ¯åî÷›§r!Êý"Ö¨¯Òî¸mp¡>].@…•OÒÎF'Øû9Ö(ŒNNŠÏ­~%»žƒN ´ä/[ýõ& •ŠqÄÅŽ 0è#Pm9FôÄSÃŒP·Ér£Ÿ}3ü¥(îd†-=β߆—Ã._€B£‰æ.>Ï'l­~ÝžWLÍh¯§9ÓcU’©Ð¬§¥8'õ©Ìësð`ÉY(G´Å4J[æœÎ*úÌ©h]OùÈ£GÑ´ÿ—¡œ›í*¾ð16åõ²KO-F%–Óèò6wÖý¬œôÉ¿CN€+Y-‘bhciA|'^ð£!hXIX|ÉëÝ}¯5Opl¶ú‘Ë<À7~µCzæ}vǦFÚô‚uEðy)»ÙÏ Ïn¶›@«ó§õ2ÖVø­xöæSg²Î¼æ/O浪Ž9Îñ› qNÅpê³8¾‰õ^¿"&T « ´|ºðt1î³9LMJY4hä7£C(s°MìF?`+ú8Ê9Ù¼€áPúÓ¦7.ó™ˆ uÑ–ëö¹VøÏóÀžÐ¦Ù~ļ†U´ò!fíýE6¤mO°í¹™G‡½eªªî½RW=0EÑÏÙ'ÌAKh]'m¿°9bËúóªT”5:]ªRÿ¸(«üÀ¼ncšÇm.ÙS~ë@Ë‘ç÷1…6|ÒV÷±XÚ6'.A™ö\jj&¨š9æcc&`|¡6fµ.»ÝN4Ÿ²‚à\iܺîö{M4y²¼–Ÿ¸ÿþ¿áƒðœúðÈò­’yoy1oów„6'#K1éòñrŠcéÛÕ8Y®!«4Oþ"÷ñë`O‡FD.†Ø;&Aø®„ieþVÖJE¯ÅyŠ}’º&ÊqèC…¼ÿl¹2¢Ì°Ÿü/ìgøÆÁ¿™ˆuЖg?œ\è„G”¨þü}ý t©Ðg»KJšR¸€2ºîð³²ßÁ¶ç•ÅCþ/8GÒe„Û3·©®Q0>/©,¼ŒU+¶å6o;{o³ŒC›_¡½~êÞ{‹@Ÿ4‡]0倉cºˆ—GÁå ðS\àù‡ìÒÇÆgL tX^K–ç÷9zóœåk %ÕïÀd'Éæ­Ê(¥¾Ãiþ5Rï |M•÷‡òbˆó‚Fk»Âé®´WB™Mq‘Þ¨ŒGï(ÑÝ¿ºÕý“ C„&Óê,´ö¡=²HKÂá@¹(¤lÏ«2/EWÖA#„]Y4Ý™fLV*»È'L€ D-hhË1ê×ÁpaÞŸü ™o¤£út:¢-6…UúéuR&úŽš—|¡nÏgLLûæ·ÐšNòëßø¦§ ÛRl£!´Ù×ö•»®~(¤òw©?••ÚërîÎGžlƒöÚf|Ž”^ ¶9iiÛpëøÑÎg'»óÞÌg0ô½Š&œå’æL h¾¦ƒŽ¡‘ß0tèPfL?DÃ\bË®‰0·ð‘}éò'Œ „XNð°KÎxôxhNÀŠ(ßö æLR£!J<žNßâ¼ \4{ýÂQi.ÿg,°€S3ÇÿöÂòc°÷­pW€å³ë~hMÜ–Ÿ:ì¼+hçöÁ™Ýñ3ÂæënNÙ1&ÐPDN[^9QLl¼Ðw5´»ÈüMqØŒeþHÛ ¥ ÚkL¥íÙ`«cÒã6ÄQ¡ßG{oEI±P5ÌßÙ ÅŵIX5>4Øê-oï©®Ý3ÃÍDw¯y Úv\ì»Éæ)¹À÷Þš–ã9çÝyÈâ³èÛÜîùûXßxù7¨-Ö€×– îooK}-W˺ +sŒOÉÈ:k­¾é „C­%VÿÀ$M%3ÜiÑ¢eìÿa¦÷åºZø–¾cwĬÓ4÷ šG}æ_lgÿ¯&Yºðøî ¾Y¾z/–Ò»ð}ŠÃáñ] Å7Þ祩#Krߊ…[¿Åj ã!­.ÓEñQ(ÿéè lÞsúæ¡¶¿;Øzf媫¯G72 z9t å›š•¾ö~¯ããaV“Ùk³UâÑܪf݇ü•[Ê‹VHIÏÊ„±÷d<¿Å!ÞDººªŠ‰F§ i›š“9~EmËÅ÷3&P÷"¡-¯¦ÔqXMd&6^{ÄnSv`e¨Ë±ZÊ2ß²®Ž8æ@нËŽQl¶ulÝ.D¸#|ã‡ðþ VQy+q]¦5Uæ'ÅØžvŽÝ+ÂО“=5ÚÚ{¡…Í›ä¨ÔÑÒ¸hoG?úÚÖ]1RÏ#bb4é¾CÓôkaâw«©…Æé©nÏmyŠ?éÌzPönÕsöq˜ˆ~´¼™O€å@𷑉 &ÄÎKq;‹2P` xH’9––º E†ãk¹‹¡©?Cþ 6Ê÷¡4&—PRO>ð@~“xyN7@àûÄí.ÞæÑ´ñåÿ‹#A9‡f|×$K¤½ýñˆ³O‰ÐÿÁ†<Õš²„:/9ãÇPâä)†ÿ@Cø†¦¯V6Vv[“2rÑ ëò_m ÝLù’IwâCáu}O-v—äꚺqÂç0ß|ÐÇ6™ø/X_ƒ‰R?yÜÚ ¿„N¥ŸÔhpØ1&"¡-¯Š›Ê$ÚàÆ£yæí”SÐN½ŠMÀÊÈ)MÄ=P//€0û´Ç­nÐ4u>ôÍ»a»Ý7~»-æê‹4]›©tïÏw ="\í9úÊבçÙ¾ù ß32ÓÞÀ{'4Ï7ëòoU/ù mëõÂ&¯ËÎL{ü‡>60¯hú—nn·ö›—…û_ØnB¹—™áèh9 |{ÐoeénϪCîü=ªæ™‡‡Å./}zìØòB½5>g5$pøó³†À¦ë{ºÚÍtdçÝÀÀ¶mo§v¢ýÖªvF¤!Á]î¬NçÐs‹!@‡Þç䮊h¾›†"2Ôy™9s¦mÎòÕ]E¥à‚=v†²|–É.ÐòÑòSÛÅâŽdHžïu>Õ*_Í?¢e³˜?øàAkš ý<šÞýhÊkC¯7ÑT>ÚÄ¥>Ûr++Ôaj_þF©=Û.íÜ^œ±¥*¥ MPWÔ½mZÛÓ7ZÖ¶¶F[v>î±Çšåç¹[ш`™géI}´ç£&M:“Þµi&”·÷É™s&E3´ö>×|VWâºÏ1©ê–-Ñt»a†â ÿnô"¦?¡Œ˜™iôO…0FD šÞýhÊk#ªB\Ô  LÉp=Ä-” 00UÂ&(ax8%`L€ 0&ÀÇ]¿@IDAT˜¨Œ à•‘a&À˜`L€ 0&¼ J r”L€ 0&ÀBI@¶›íºmm(ã丘¨?,€×{N™ 0&À˜@@f¸R? ( bL *° JT<&Î$`L€ 0&À˜@C!ÀxCy’\&À˜`L€ 0¨ ÀxT<&Î$`L€ 0&À˜@C!ÀxCy’\&À˜`L€ 0¨ ÀxT<&Î$`L€ 0&À˜@C!ÀxCy’\&À˜`L€ 0¨ ÀxT<&Î$`L€ 0&À˜@C!ÀxCy’\&À˜`L€ 0¨ µñÜ™1¹ŸGó å.ºÔ¥Í¢‚x3© qPêr«ÐõM6›ý«i™–†0úz‹ŠŸmhÐ7Ôú:u‹Q—=êEBÊξb£M±Ín(íTÝÔ N%R ð»š'SÖO5Âö!ªp§óå¸\wî]Š"ǪšÚAJ©7‹ÓZ6—qG£Óæ¹ÝÚþ¼ý`A‘®”Œ¬mªê™Ò¼¨ésO?=¶¯êvt8~¶¡N ©~„žNØb”÷ÞûT\^Ü¡Ñv»mŒÑN)ÜNYÚ©,j§4M*É‘ô¼Óy;µSì˜@4àw; OɧŸjTíCÔàÉ陃·{¶¾™2©O·$ýÔc»‰ã»'Éø¸X[êD´Di|t‹ërÅ/¬o¿rÝ–Ç ›Þ3ìôá¯<áš‚hø‹hAœŸmت[ƒ¨a£Úˆ1'¼wC ã g(šLìÓ-‘Û)/cõð ´çcðîÿ'Ç•ñuhÇÆBJ€ßíâ¬Y£mj-¼öëþ0¹tÞÜWè‡Fú^]—¯&µmÙtø5”‹Ïî+;¶=B8ìQóý,eqâqÚqÝä1]Û‹¶íŽÏ/rÿëijÏÍÿmÁ?—ŒH!œŸmÙc ÛI¸êG]¼û¡‚æ¼RbÿÏø‡îµ;b^B;•ÀíTÅ'ç§&äåß|Êy¢ÿX\ñöaõN€ßí:zÑÔ>„ª?1¾<êˆoM’‘^MU_Ñ ð»]O·±´‘,€ËÿŽwæû tjúל'cµVØ×c•ª»¤‰ñ"nG줛îºïb¤NB8=ïHèäøÙÖ]u¨RÔ yŽ@zè}rÐûEï·SÁ=%k=¤vžÚ{Ä íSpáÐ ¿ÛðDCû±xr²³‰Ãáx±c»–âß—ŸÃr ^â–Ø¦¥hÞ²å´ãŽ;½¢0…ðĺ[øÙ†ŽembŠÔúQ›2Õ὆ðMï½_Û¡s;U3úÄ f;‚Ú{jj ßÅBF€ßí¡¬}D ¹}ˆT\ñ´’£=š–xÃàÓmô%Ä.xÄí¦‹ÎP4]´ï{þy£C,þÈp¾>Ÿ;?ÛàeXîˆÐú–²†8Rzè=Š¥÷ŠÞ/n§jN˜êáCN·Q{Oí>bªÏö©æá;~·#ì)6äö!:ÒvÛìÛ˜ãºwÔɈ]Í ?â˜Ð¬ÙÄBÚ¥üÕ—=8?Ûš?ʰÜaõ#,e q¤FFœô5¡÷ŠÛ©Ú6ë!µûˆ­¾Ú§Ú„cˆfünGèÓk¨íC$ àÊ-w?x*Ö‰m¥é…`WK¥Û\põ §#ªúÔ‚ó³­å³ ÇíT?ÂQ¼PÇY¦!+}ŸÚp;ÄÄ‘Ú}jÿc$öM¡)(Ç©øÝŽÔ'ƒ|5Äö!Ò9ã Ô3„6ÙÁ:ßõ^ܪŠ&+_ÁÖàF§Qïù¬*Ä‘x¶jÛnÂÅá¯>´à÷l«bÖ˜®EHýˆäV Y½O‘ÒNE¼êòhÖCjÿ–µàÕãë¡$Àïv(i†!®†Ø>DÚBÚæKЕv¸¬ÏMv¶îÚ'ÞŸ³D¬Ù¸]ØlRtißZü÷ªþ¢E³xCàþòÇåbî/«Ä¡‚"\WD÷NíÄÍŸ)Ú¶jnT½¾[*–®úG¸F]†ª\”à(š5‰Õóìöθ“ÌPh÷¹ü©ø««¯‡ˆy¶(3; ©–Eì©Y‡i2sï½WàGþìjIÀ¨‡h÷÷wET$€×eûTËÜóíQN€ßí€ ±}ˆ4œ4ò6¡ÈØ^¾^µó_Ì_&Jܪ˜ðŸËD~a1mncßTG?Ÿÿ»˜½h…¸nÐ)¢oÏÎâÀ¡B1óëŸÅ“¯)\#¯1ŽHÃ*DË rû.{[dßÔ€“NŒi§Ìºpól뢰іFÔh@æ­ÃÞ¤8›ÃÞ–¸ECÆ£%Ôîï=×ù%¼.Û§hAÄù ~·ÃÃ5¤±6´ö*]$9Ê]²YœÃQ¯Û†­»E¯®Dv˜ìÙ¥½|Æq'2I™óÓ¢ÿɽÄÀSûˆ#[4Ý’Úˆ;®î/抿¯$žey!ž6©4…Ù€“ }%Ôåó˜g‹r×¹Û¶k¿øbÁ2˜3ULzÇž<±}÷ŠêÐ'êG–¶ÆIuwÓûKïS}·S5.I„ÞH<©ýGöêº}ŠP"œ­:"ï6™Ã’ RGq¨ª§6QÔÛ½ ­}ˆ$U- ÜÆ0.ôz¾©vس´Ü+ELŒ] <¥WÙ¶÷»÷24ã½"%Ía׺e3ÑæˆfbëÎ}‡=#ì ïq5…oÓÆ’üüˆ…!Í<¥A¶P>[2󡑈k0a~ Q®§¿?W4‹7_rý¬ÒýõÏ6ѵCkKVás[vîŸýð›¸ø¬¾d_.¡Ïæý&<šG¤\{~9ÿºþQõ£®‹Z“ôÊê0n¦v3¦”WMââ{ª PÚFÔeûTEnøR# –wÛ쟈_“ØÑîÈâ¼~Lj3ûv/CêÏTuËŽ}Âõ¿O°£õ¥â((÷ÈmÙ±W|ˆþîï-; ¼cÛVâª'‹>Ý’DÚÔÄžýѯÑ¢i‚èÛ££À²¨†ilYB–“—>™/~ùc½{ËE†rÑr),§¡îcRûI8=|ú µA,/¡„¥ZTéõž&â›ÄŠç.5l½or†8ByÌMÈ5'KŽò.¡Iœ8Xà½^þJ¤ü2>lè™SçfñÖUæÂòlÉþžÌ…Î8îhѼip{x¨„õïÄøa—‰ö±´OQý¸Û¯<7üŸ@­^ëG@9¬ç@Þ:ì}wðÕ¿¢ žy„'yoû_×íSxʱF °¼Û0™£o¸Päc±†Uë·Š7¾øQìØsÂs¿€¹pýì;ߺÇÞ2Â|,æ¦m´ÉžéÎ>¡‡8÷äcÄÚÍ;ćßþ"ŽÁè}¿Þ]ÍËeÇâU,[³Qtl×Jü¼r}Øð°ô± ¨}ˆ$Üü •"©wœ´”—{¢è׫«xgöb‘óá\ñpòU¢e³£2Ì'êòîPA±èŠ©®Tcg ße¼‘ߺҀ‡üÙ @.Ì;>‡Òm—Ÿãý¢åëĬË ³}º%“eív›xî9¢¨Ø-ž~k6ìžqé¹'ˆo`^ôÐð« mÂûs~¿ýµQŒv 4 ñbî’UF£Eš‰íhDßþræìG`bîeýO§ô9ÊHÿ-øS·£%ó[#FÝ0¨B¾Þ™ý“1ZB×>øv‰1±÷–KÏBã¸Yü°ô/C‹ñ5F`â 9€˜þh\ÉmÞ¾W|… À¢1§¹v|€tj¤qÝÀ iëQõ#جÖGxëûb¼C¬Ïc(mÿ­¼ÃÝ>…§ k´°Öµ¾ÛvÅf˜±ˆžØ“ƒF_¢/Я· á<7ççU†yëX‚”‚äÚ¶"+­Ã®YBœèšØÚø[Ž>d1ú<øïk6¡ßpˆKÎ9ÁøÀF}ešòªú2‘|cÖbóŽ=ÆB7]t¦azKæ,éÐÀ_wÁiâË—‰}yù†Ìt%´ó6ô©¾}lÖ]×Ît ÏRû@_}‘äÌ!bòÔ_™ÿ¹²¿ðx4c"æ‘- s…?þÞR.;÷æ‰]ûòD—DïQ¹‹‘õƒž¹É™ŽuåÌ4Cš5×_pŠ !{Cî® q8X ^ýl8ç¤bÄõç‹\˜̓PLéÇe„¿ÚˆaWœ#:c¥²ÕÎ…¹¹_±ŠŠç¾f*äþX·fFÍa.¢‰ç!¼Bx¿Bs·ŽmÅÿ>úAl‚0N.gdVòÇú-†PßCV7 ¶à?­ü[Ütñ"6Æa„7GNJÜn×¹bñŠõF£Ö¥Ã‘âݯ2>(ŽO¾_jÞ“G_áÇD#Úáלg¾¶çõU?j›ïº¸ß¬ÃtŒ´¶³.Ê_—i˜¬ë2MN«ñ0ë[XßmŠ©ÿØ\ÚÇîb´ù?Ã$Äü[á#[lÂ]`&i ßU="Ò8Ó nÔ¯øs¤õ>¡G'qÜÑÑ·©bú4ÓUÕ·¼þÅBÑ$Î!îù×‘ÔæñöW‹ÌÛÄž‡Ä›_.§cúÊóN6”N¤Hò×Ç–ÝTûóyÕ>¦zŒ!;[¯Ž´¢4anÓö=b7†H‹IŽ)»Í&.Ą̿¯_/^)vî=(ÖlÚnhÈ;Cy &lšŽ„· ¹»ËþöÌ7/ÕçѬ¸õÁ9äiãébL˜%AÕ×QÓªy‚8¹WãiWPC«MšˆNÐR“#pÚi«c»# Ó¢5›và¹æ‰CXýæŒãý³ÝX ~ìïÈöÝæF½¸ö‚S ­÷¿/;[ÐI?â#ÀtEŪxàß—ˆA§õ ¥Z ²Ñûõ¯ müˆëÎZB4ï3W8IœÜ»‹Ñ ÑÇßj,‡InõÆư!Ù¬÷èÜN• y_ŽõY?Bý°GÁ|ÂŽØH ämEÝd›S‰ba·›Å{Í$*(ÃDK¿óÕOe³1Âiu;öGw¬Z±·|í1í½oŸgf6âžq¬5 ãœFíWmÈ'¢/¤íÝûtë¡ÿï²p•õ-´ Üz(·Î9ñcnÕ°a'm9™Æ˜Žú`êëÎ9©§è ¥Ñ*(‘üõ±føDûI&(!x&¡‰‚%ÒvÎþq*³G…¡šÐG6¹KÎî+hP¿Yüì­–~$ÀÝsë—' ܽò¹qþ]~ÞIâR ýÔŸ«ÓžP—4àähÒI&&®,\¶éáwsÕ†­Æ2‘“_úâpÒ‡/ö+=£•oþ†EA«ÛôÀÕ[ÆÐÛ^C Ý»k¢X.r°BŽé:Axß M€éºwj[®.?™5½À&q1°ç;|¯yõHšorM¼Cޤ%!×§[Æâš÷ëjAaè\ë¡cã/¦Ðñ¢Ñ“I/}&Rÿ{yY;ã/ÅêüB=᩺ôø:h˜B÷n[ùìLj,9Z=ÍtG¶hVn¿s¦y½YB,…LÛvTî01¦ â¶ËÎ1ú‡½¢h·dÕÃÔñóy¿‹¯.7ÌEò ü“‘:•õ-«KP4’l:R8mÙ¹_´*-ièM‡=\ Õ¯ÝJ-f\ ýXñ)Õ_‰«‹ê6S$Dƒ/Év´x,VB±:~.>ëxã¯_‡/}:_„û0³¨Lèºúü~‚þ¢ÀÕ÷°§A¦BçÁ®Ž&ÍvK¢åνŽl¨€<Ð ‘zAÃ=ûÇ•ÆÍ½ŽJ=°Á­óþûêM˜ÅÞۣܰÕnÈm‡–œ„tr;0rÜчwn% ƒ¯£S©üøû%ÄÂnû|ß e¿+[Kþ"¬¢2 Giƒ(‰9¾{§²{Âtög¦|‡:Úˆç– O¡¦||Ï=ø"ñF ÎêØ¢åk ís§v^K :ÂäcÕ†m0ñ#ðþî!³’ê&v’ù Ù‰÷F¿FŽ”W´ŠÊž…Iœ•õ-q±^ùgÌ¿Úmkú¦¬²þÊ6Lçuöì‘ÿò’e8R.N‚‰¿ð|}—ÒXb±_áÛ7²Ë~õhºg‰)o~-ÆÝvIЫqøÆæß¥ŒËìÀÜ\Yôa¶—÷?“$ÿË×nÆpY#aZ¹f!Ì…¾ýùOøõÅ%n±¶ú´s)íZJSdBÔ“XH3M“hi¦ºöqCÎ<Þxþ´Mj9›.‘#m7MʤI’לŠX ›=í8Ë?UçN<¦3´ñâñWg¦MçbÈ.÷v_í›ósO:y³ayÌŠ‚~0ñù [_õÃOV"Î«ÎØÐD^Ò.Ѳ¦+1œ{4ê MîZ:Çä“ï5:N2u¢áé¡XµéE,/fTLžH³>ó›Ÿ1yj¯h‡ú~Ö Ý1©··¶º4¶íÞ[ÏÅb#â -{Ö}e²B÷´Œößdºh9&&P9³¾™ÇÊCqEÅò²d®AJZ}„ÌYI1×Ëäê.<ó8ñËŸÄŒ¿7&O6…̱u׌”µ*Û°º¸h~Í‘¢Éúd.b:Úïä—?6xe} )´hÕ·Y —a¾Õ©¢YB¼ÑgZNf|¾G},õ¹µs §}ˆDðÚ=›z¸›4æéw\!N¹Òïò„õ¥F™$ ‹]9àdãËÞ@ù}Š5¸ïyâ ñà³3Å ØË‘#Ûìýz»˜Þ?åCx9ë¹Óšî´QÙ²‘#³”½˜ÝMÚqr4¼7úÆ ŒI·MûZ÷%†i‘) ªøGBÍB°$U^!+^¢¡~ýk£xòµYbbÎ'âä{LfØ5,4‘—FsHðoLô¥úGóRÈÑó¦•p®¿ðtïäa ÿÒ0µï¤b Ûþ'õêŠù‹ðñ÷Þ7¿`Þ@1]2&ÿV–Í; ‰Æ´²Ó͘,L¶Z{'W5!ˈ˜ÿ1&`ØŸW ²^üT¼úù¬œ•gŒz’b'G¦¯wa)ÃmX‰dòKŸ‹ô>0>ªiþO îgÙ$§ÐäK«£6ã¯ÈL³ÀX­Å_ßBæ))׀пßXsüîÇ_GŸù“a!`Ëß¹¿>Ö_¸Æêið¨} Œ†…qÖåôgâCËõ™Kö™y¹6ûôG“Ph&7M¤5Ý CN/5’ÆÐ ùûš«Ðƒôgu´Ì`Æð+ ¡6ëG½¿%O=¶¤neQЗôGÎÞ7åuzÚ0#Ü2,!õÝ/ŠIÐl’v”´ùÙ| ÿZC£oâ †}Pš›IíÞŸp~5:=Z]€ÌãÖaäfLáÌ!eë¤âö¥Â2-u6è´ÞF]é s%Ú ê¯ Û ¾ª²4hÂ1­np÷MÂ6ô°i•9!‹Ì§h³+šƒ0åÍÙÆ„,úxeǘ€—@ f¨þÂÐbf›o²<®{¸Æ0‰UÐÙÐ2º¦Ëu­yZ鑿žù›F£ÂôW]ßBsÜ&Þy Vþò ý´±×É y9tP¹|øëcËhÄ?XblÄ¿±ÖJõçjc¿î4}ó«a «}˜ÈC¶wdVð†É4]Ã#@C̦£!kÚ‚šž;™Dýëâ3µïçýºFéÕÀSû˜AËi´çK ïÀ|sí`Z~Ìt•¥Aëù’³N®¢ßUMÈbœ±cá#P›¾ªº\Ò·¼«‹­üõpæ»|JÑõ‹ðèz^œÛFL€L\h“vþÜsà ±)iP‚µ#oÄ£ªèUÍ?¡QžSútÅzó˰NüϰoW¡l¤%'ûoZ:óÌM¡ qϼ[.\ei˜Kg’ànN4¦«šU.bþÁ˜@Ôྥ~ à!àN›·üºz£±!Ë;Ÿ3&À˜@U´nWletOÌâ¥èKh‚`Ãο_ú—øëCÓ´…<­”Aœfa¹/Zw“†u-['nr†è_ûl¡XõÏ6¬v"-|Ok@['áÑöô_.\aì¢x–¾3;̪Öâ¥IR´¥=iÑiÊ­jþµv«ª| 0H'à+|›ùõðd Îæõ`•­Y\YúÁÆÏá™`‘@y•H'ЯwWáÁ*›!ì’k…¯xŠ¸ï–‹Œ-翘ÿ»áOkGÓìIX°~Øç@(om˜––І_‡]~ޱx=í euKÿüG |š!ô¿9k‘±s]¯l-^Úñ­¯‰®ˆ÷ž›c“‹ÎØð»”¯µk%+Œ EFd½"îúmc;]ëÕ¾["èÚëŸ/´z7ºs2›ÊÅÆ-ì˜`L€ 0È&i8æí üI:†Ü‘½$9Ztž Ú4ÌÚ¢iÑð?7l5–ù";G²ånÕ"AÐp+M:"wÁéÇbµ¶Ø¨¥©!dÿñw®áoþ»bÀIâä^]°qÅY†ýøzì…¢ I®@$f;Eí?ŠSþ…nåã"0&À¬"É…òetn;¶lZÐüÈ#ï!MàéÇu³æ7¨óý0ÉzñSÑ,¡‰ zÄuçËwQ$ÇwO2„©G²?ñq1âJ¬ZBCü¦;¯_/ñöW‹Å˜'ßÿ¾ìlq4ä¯|º@ÜóÄÆöÌœv¬Ø¹ï°M6móLf MûȾIÐ7w™«l-^>faW×?™'07 Á{v¸#/”kíG˜BÈÍkWÿŽòiø+"ÌòFË‘V²!3”¥¼éœ„ç^];æ$f1"Í7¹¦ ^ÛñbhÆÉUe.Ô+áÌ]²ÊXÒmÞ¯«Å¥kÊWµ=w«Rs§$Œ€#³’,é–Ôîã7ýKˆÃ6ãnÕ˜°fÓv˜9íd×nºØýÒt•mÎ@™„Âz´¾7½OIG}CmÛ©°æ8Š"7Û)jÿ‘m“u•€³Ê˜œ@$ àfƒ«ÿù‡+º÷=qÏ/¬o¼£ª I[tÓ_eŽ¡±Xý„´Û´Ü 9ÚÞÙt´Î.mÏZí±9ÁÏ5êZC#n]zŸŠÝèèÜÐÁ§CÃY~ÍݪÖâ}8å*ÃîW‘Š!P‘à_(×ÚG]óxòVü²ˆÔüžÒ¿2ÞfšÑr$Íõ›°Ç'¡óä^]£5ïVs!zVO¾ö¥õ²qî»TyV·u÷EgõÓÞÿÎо_»ìã1¡–œÕdˆL_¬ŽlÀÉù¦gÙ¾Ž4ãd>C›©TVwÍ:ß{ùwØ Xßã¢÷é´ACàýj^Óv*칎¢¨‚ÛKí?²måE¥à¬2&À#i&(¤5:·ÝÛ¶¾½rݹÁp:Søö—ä»2áÛ¼î+|›þæ1Æa+'H›þ$ðûn„a^sØí•^£ÉœµYo—øÇMëÖÎFzjéŸ)„ï¨sÇd|8-€-5™Ÿøº@Ì…|ï¡ßU™ Ñõï~Y%ºcœsO:sZ£äJ“¡“ðá÷Óc"î6C#NË î*[@iUå|ͦª Ë×jD ¬}ÂÝÆ»DïU]´S5ÊmÝd¶SÔî#ÛQÝ>EvÎ*`õH ’pSãA¯:ï“߯tø=0ÑÈš]ðˆñÓTuÿÂYŸ~JþŸ½ë€£¸ÞoïtÒ©ËM’-Û²åÞ‹Ü1ÆãF3Å„:Ø”@ù0$„À@HH$`ZèÅ€Wp/¸È’{“å"÷*«žteÿï›ÓœNÕ*w§•4ï÷ÛÛ½-3³ßî¾ýöÍ›÷Š'€*1çņ#ø`éÓ¹-»X©K»ør ‡»üêá.4wyÁ]¨:‚r¥»Ðïÿñ…è‰A²Rþد~4ž›ùýáŸ_Pzæ ËÐÑSY"=÷#¯|Ì‘LÖ ‹º<¶ºóƦDެòÏÏÑC3>¢W?žOgÙ•ª:·©Ìãç„Û\t”øù¬ýÄ%‹g Ïž/¥§jµÔSÐ÷Ðû\RƒÖOµGB©P4%Œä‚Üaa‚ò-:{ödÎî´”W´ƒg|ôý*ýÞë.©•+ mªÜ@ 7­^öQ~~N.ã€Q­˜@€sƒ±€—MÕ}ßu£¹ù%òÛ[ÆyþtnGÕqÂpùðN^™»Ðæ=‡Ø¾ƒ^äˆ9ðë†ëÒÛß,£Õi{ õUî2¤•*u¾öû[0óȳ÷_ëYFÌù¦\&|ÛI'<ÔÊ.6îÍJ^‘Û”§`µà <ú‰ Ïž+<_¦KÆþFé©ÚA,õô=ô>—ÒàôSíÎ\¥P4eŒHÀaa‚¶­œ?wUd³fïóò½ô-é·_5Rƒ‹‡’ª€E /µ;÷kûvl™ºjy*à˜l<_àÜ`8·µÆr!w¡Ê „»PE‚xñˆã ¿îÌg9JÎiºââ~¥v­‹»wApW’  ¼×_h¹"·© £¶Wá¦ÛÐÎÛ9§ß4~˜ ÖF%#_Jt‡Ãò½oû–ï–Ì™µ€÷Db XÀ1®À8o%Õ@rs4’9jÍ™ó9œŒ)\ ”DhA%Mrú‰ÏÜÊSÈ’Ùüœé—¾«•žºðýà­§2÷íùpÁÃõDé§ C§öP( F%àÂ΃4†°rþrð˜Ë¹†Œxàï/ˆéݹ­ÎQG4„D\ä¦.ˆŸ‹^ˆ"€a.‡=g㊥Ÿo^»2­CtëKLÞ&EÀêÊøá½ “’&‹€$à¥ô£ÌS>vÏœ8~|Ш17³žŠVzªô}R^O9²·¬_óß K¯à=•~* —ú×H@)&)ÞËr]c{‡ÇŲ÷ÿÆzÎ59/£p´]úYÂÇÄ4³’^µcýº]#&^q‡Ô»œ‰f4_L=24D‰׬K“ó·QVvžS`î9Žœ»v¬X»pÞÒ‚‚E Rýĵrοv¶†ÿÈÿuí3 Cb×îÝC#"š‡„†F™ƒÞeg >Ÿ$°EgÈå€ÌÙHâpØ ‹ lç òsOÚ»{Û®´”=\·ÒO¹ª’@!Ò ’½dÃ.Z¶i'Ï)è#y }d„•B8Q_“Ö”çs è0Gæ‚‘ðÇÕ[(:"”Föï »sÖo‹¿,Éx“¦ødzGx[™$©ö~ñ²ßÌÓ.>a!ç9Ⱥ°Bñ\ËS.ŠŠè|ytô#&MãoIj9¤  ¿Õàr 7ˆ¶ìÒ…åD\Y¿%  pAýÄu„ò²gkêžvór½é§«nÖÞçüýçú?䲟çòXa’c{”~ò3ðªøÀ#€úÜÛ%²gc ~·ÄxšÈîŠÝ;ÄÕ)GàÏÄ?5"¢×®'(uw&ý¸j‹ˆvýeÉÔ+),–"îŸÚ[ªQ 8ƒÒvçw”ÿ¡ÀaáÅ  @Àq°”ã%×èÉ7Ÿ#­ÎÎÝ0 <ü».Vëµfö­5ù¤Ý¹rQVÖvÞ,°ò$A¤17à •(uC@ê#”²)ÿN?…†•:Ñs¥þù÷Ääno²wNé'ÿâ®J°zƒ|ÿ´~;ÍYšJñ-¢èþËGQÇ„–jAèÑÁtk'¦ýGNÓœå›éý¹«iâˆ^4†óW¢55k¸‘ ¸TÞPÚÞËxÁPŠè< x^–€7f"<èƒSgÞz­C»î‘&s³FA÷ĵz4½¨èÆŒü|¸œà%‡à·é÷-ŽåÿJ Ú#à­“¼— §ŸÂ#Kä©RÿüóGꉋ$àÐCòEé'ÿ`¯J ò½n;Í^º‰úrr¸)c“I…J®úàãä¡)£iÖO)4oõ6Îyá¤K÷¤=˜­áAMŠ„™€ã*BËpy°0ywcÂòíM¾¥¯8ˆwc&ß|zBô<‡C_™÷è¯[5ÿ–/d[Â;¼Ù¡í½wìù#ï—>^07eùf”(|ˆ@ƒÐO‘Q¥Îøx©þûläýƒ F©—ä\é'EIÃC–ï­{3iβTA¾o8¤áD=µ)¯ùWàAŸ&à‰­[ÕÂn=a oÌn(Áîã[¡á§Íšå 2›òœ®ß˜>¨ÿEžÿjA! P( Z!÷ð3çs .AT%ƒ{&Ò£·\F×@:÷-ýë‹¥”yâlµ«‡5ù³ë9!Îq&ÖýèÉ;&pYý©c›ž2@îQÇäÑý(+7Ÿ¯ÛéÙæÏàÁx"s¦­-àœQ8ïÆ,Š€7²«Ûa}êZþbü\ž–æ¢×øÁkÜ#äɪ¹B@! P(ü„€Ë¥“ƒ‰áù¼‘^ÞOÕTXlD˜•#®DS÷Žñt×U#(¾y$§wßVá¾­Í婪“R( …€Ÿ€,³EE áxÕõ%p×èÝ9–oÚSª »ž SÖOJa†|¸åÈ©ób¡k»ê¹Íd²[ ê æ‘àYçbl‹Ä Wà,1o¬n(A6WOÕáA SJÊ¡ŒäguñG¬té®çõ‰¿ÔæÏG:% …€B@! PÔé‚bX߃¥,ÔË6î!üdÛlLf¥€˜G±~ß•I^A!}±pež$'¿£žœ£´=‡é¿ß,§¶€OºÈ^°:íÁÀËVÍ"èëŸR8Œâ1ÊÊΧƒÇÏÒ£§«sxÀöñ|äÔÎ;Q®Hð@¢àºàó̓(^ôT«éOMN®Ü̳£ZP( …€BÀ(lÜq€þóõ2ZôËjNÝ:–b9Jun÷^3’‰¢O毣. ~\K‡‹gV·µŸïP>à¾ÃÒ%%%uy7#cïüUÙž?(ã È9•úOC6V5J! P(D ¾,߀âÉÛÇW‰H'ÍyùáëÊíó‡ÛJFw_=B¸w ¼ wó‰#z&£I}âîo,GŒkï8×ÞËþƯ¾Ë÷î ò÷Ñ6NÎS”>¨ß‹¼å-÷ zT=ú mÙ2ãä¡­в›JÚ˜RyŽrî= ËÞÿå>jnê]?•Éea 2Þú¨BýÀ¶¨ªuF ì{§ÎÖSxgx“ïzjF…ÕÂóDâÜØßë ‘€‹Ú=?;H3iã™z'’Ik­éTý¾˜ /{Ã[ÉCr9Æà1&×í……‹?úçŒ> ¤ŽÂäyá™#š}èÊ>÷¯ˆåº}Fîù)¼Ý“¬‡— /òÄ ç?ÄIølv'EȧBŽËZþóÃð§Uó^bÌ£ßc"¨9wGöJJ Ä6-W„¼æ°úðI¸áÞgºýwÓ“-!!—K=e"-‡uU«¨'zö»ï©Ä¥ž?Zà4`9ROé.}á{¯<»‘«öè§5CU£P( …@C"àÚԩφ:›kYÌ¿cÏDC ³º8®¥fµXšœ?»Ínwñ@ ='ßf2™ÿ2íOÏ·ØÞ8¸qۛ˖ÍÉç; Qø]—-³¥'xƒó×>‡»O×]ÿdzAÀA¼Ý±W]œ!ËNË6í¢Ui{EÊZþˆé€#9ƒV}&E¦ÐÌÎsªàÃ<ˆ&§ÀF?¬ÜLÑ‘¡4z`ºtpwAÎPêÄ:Èóñ?^zw+j»~}ÖÞAý?`W‡p˜¦¹~Ï3CpiõFF,¤¥]𲋿¯ÙNñ-¢è¦qƒ©cBKœ‚’bàË7 [;1í?ršæ,ßLÿýz)M3€3™õ$\YÃýr»H«7ôh0?oXCßk£ôTpW §âŽœ<÷ë÷Çß}ñÏ.]ç*8Z­R( a™0è)‰—Û=ÓŸ}”É÷«ƒztÔ¿ãJ3“oƒ6×Í>OÜy¥x1xþÖßþáQnðXòì:ȸÛÒ¤ë ÒÑ­.i…$ßv»ƒl……ôÓúí4oõ6êÓ)¼a´"ß%PU¸„“‡¦ŒxÍ^²‰~Z·ÝØ  ÄT­ÿ­”äiè¬xÎð¼)=U=À½õô;ô<‰÷pU¢P(=F&à¦;þï© &“ùe~©é÷^w‰liÊÞ&Õ¿ðnaQž|×´I|´µÿæÍ‡œ¤¡»N—&§³! %p;‘ä{óhÝ.êË®·NBêúWïR'àÜæ,K¥­{3 ¯ÞÑj¯j"Ý)È7ž/ŒLìómV–ÏÚݘÀã™ØÚ7lÒ¤i\JèÔŒƒKœº~a˜g¿î¸¶v¥ûö(¸ž8.áó]ÀQ=V¥î¥lN½{ͨþÊò]K¨qý'_ÒÎåäÓ’;¾ÀYIú‰Kæ)Ïž/¥§j)îÓ›Æ5CßCïsIèêTF—ÚCªŽ4f³‰~^¿“8Z™Z¥šbŒ8SX—B¬Ö‡{uJÐՀ˺Ý&ðµähú6Òïæ’f¦ee9ò\íþi¢?š9ŽŸKÓîçõ_Õ­–ºí¶~;„õ;ãzoÚ{DD;Q.ë†-ðCÔ˜•i{x@fO©‘Ù¬\¹ê€ªÐO||OáÑ-šßçKé©: ʇJ=µ}ßðð„Ž:„QU¢hÐtmGÇÏf‹@7VÒ›»vk‡×M§œ<%µmÅ¡d»Qû¸æ´cÿ1Z»%ƒº&ÆÑŠ”=dæÕ Ãzñø('ØCaV ]2°+õïÚN`dçäïWl¡m5,ˆõüEý:óö.bÛ®ýÇiYê:vú<¡güÒAݨ3×õòG 銋úÐÒ»é çÕèËã­&îÏï 9Ø(öõO)´7ó¿;ˆzvlC/êMÖà *²;éÇU[8BÙ1ê@ÔÛpåÈ>TUô…ôCãñ21’ëÒ”û–º85¨¬ >¸:nµ–O¸z0g]eËý„‰·CØBu}Ì¡ääN>¨¦ÖEx¬ßnÐÆ!÷9Ey¶"Õ£Ö…ª= BJv®MàŠ^e÷@SÓ¡Ÿø X¿­îçIk©ôTMa¬xà½ýÏ{(+xÅ0©µ îu¼Š‰iÊ΃tèÄÙ [*BëN»n”pÃüyÝN±_#Øsèmßw”®aRŒý¾X´6ì8(Èn4'bû~ÅVO™ë¶à´Ç鯱ƒéò¡=9€ÁV:sžóõqòºÙËÓ¨]l3º÷š‘L¤[S³Hx{ËΧ¹+6Ó°>i$ö»ÒŽÇĶ &áHsÛ¤¡tãåƒhóžLâPÇbÛµÛ)ÿè׉®àóëÈ‰à •µAlT?¥0$‹Œ¼Iv8Îw©Æª?µC8ÏV mGs ¡¿Û½ÿŒÝåZ(ìºó×µ+Ù7G¹ ¸“_Ú î'û˜€ã‹q¾•Ôà<‘9Ôét*^{H%Ǹ‰Pœ¬×3Žž¦ƒœy3—%åTV®XüõÄ¡ôíÒMôÚç‹©—vtí¥˜Œã1ãA,L¬¥Ä7¦ÌçÄßlö[ŸÍƒE?CQá¡âÝÆï‡ÃEyEÔ¶¸]òØ µAîWѽ´–Ò¸`?oÜ+:®!¯3"ÒLZkN/ï3†€ èä›y÷A|¶ñ\¯;PX(\9¿ÄD‡kÇNÅò™â„ƒ_=yzñ+m[Cƒð“¬wÞŸœ<´cJʺú@ŠÆÁļ}ÛòxŠâôòJ|‡@$ãy>¯@àì­Ø}WC“( : :o½³%(ÏU“8ó$ôþ™¬ó0¥gŸ½Ô|UMA Š z°<# 1 Õ‘8Žè5¬O'ZÀF?øw -ÂÂ* ÿë{'¤.íbiÍætúŽý¸½‡!eYÆò”÷ ž†÷Mî,e··‹kFüê2&ç™"Kô†Pš0¼—ØíT±N³¡&Ž“ÞA >ãß>Aø}ÿ÷›åb}PIG8]LîÅJþ¹Pä~eçÀ1×f£Va¡e75êÿF"ร0q/„aµTð)TËK/Ð~üÅ·içGÆÀ&|Ie²*®Ý’Nóx`A.ùõän£['$=+'f-Þ H|Tx ì‘HW^ÜŸc,¦E¿lã‡è,µã‡jâE}E°¼µx=Iž¡àF°oÕèA=Ĭټ—oì=âXzñ0id_º¨WpQqýµé‚̳eÁ—Ë£Ÿ¸lAÂñ<ùROù¡Í ®Hà ýÏ —.(À½ÄØàÎH5¸±!½™ߌÖî8ÄþÖ'j4ViÜÐî”¶ûíd?í!=;h²r Ä<šý»ÏÏ£ ì+.¤w}/vcIÝI]™Èw`nsê\.»Ë˜(’‰-tbÐ%Ü]b"wSH0<éܲƒÝ{³K x¬ßpGˆÞè0+ñ3)|Ñ2¯éÑÁíjÒ¹m,mcßônâ¹|+åœð-¯¬ -¢ñXW,À‘_IÔ<Ü"ÞKÀ¸)¼ŸŒfa@{̬n¡t}*}˜€§òâIÙ™A»%–Ríç9\ۇ߯¢‘ìótÿ”K‰Ó$Ó &ÊEüÕz*+‡¾érš0¢7%´j.º‰>[°–:´nA¿½yõíÒžZDGŠýÑ]3 {úÃí©_·ö‚¼çóÃü‚BútþZ7¼7ýõÁëÅ›¥?où®ª~Qhtà ò€—›ùœ®!‹dX~U_1Á¥ Š›„sàƒâk$Û¦æ¾A@~èo%µBÀ­ŸÜÏ?GâyªUAê *pëé‚RÅŽj“B °HbAa!AvkÒXËdzÕYòÛ£T!òÉ?>ý‰^ÿj) e?ïÚ ¢$°[È;ß­¦§ÞœC°XƒàõeɆ]ôÂóè…÷æ±;‰•†÷v“lÔÕ>¾½ÿÝ*Ÿ<1¾¹ éX?œ-öyÌYžy{.w²ïx¬rõ¨~¢œw笢×>û‰}ÍÝn/•µAWÑÜ,8H£¨P‹pdúí!âíßXÖÉÌr(&¾91÷© ü¤ö<Î_l­i#[Ãï¸r¤ Ö²¢­<º·yT8 ìÎÄœ…ËQ*ž0¢ð?Ë]3g²òhHïNò1ÇWáö#Ë£Ž¥pW*]6¤‡Õ³ó‡vôýòTB tÝ EϤî ‹-“ªê—åÖeμ¸ òÍsíÆã§V¯lŸ©éÔŽ5BË “ór^?¯.uÔôX("÷Ä1Ç Ö´µõXãûÆyS°0T˜jíåÑO¼·x†ŠŸ§j¬vª>Åúßo¥ªŸÚÓOHòÍÆY¢¯[B3JÍ8ÎÑ¥NSeár'²±®¬ câ‹I 8Á´k/ƒ:ÑS ‘Û1¸Q†Äz¸¨¼üðuXÏ®"ÞÿÃÙ¥öŽ+‡‹w©¹ÆU ½Çn+8¬Ù1Á»Ò­},GN$Ú€ƒR:´iAOÜ1žÀQ¼×c{3æ8N-ŽÑÙÔÊ$ªÚ v(óüv
þq5½;{¹Ø$ü©»¯¦;2è“yk©Ùª0šeŠY›5+à 0$1TäÛ7¹²R¾•!Síõ¥žŸj¥v¬)~{Ô´!j…ÖXCÅP¶.÷KlF¿¤Ÿ¢Yœ¬æÖ‰C$PÍ"ÃêüñPÛŸõÓFa¤ì©S0[ÞƒÙ/=88Xà œ%æµ-ßèÇ5)+gÖ.!™\<Âû!“ÓiöóþyýÞÏÁÉK (-ÔX´Ê¤º%¶f²ìεaÇ~1pþßp_ Aˆ¶tgs6Ǿ±HÑ/%òA€–nÜÅ#•;Ñ èÐ"ªd`BUõ{·³öËå]{žéÒe«–£î2õV¤¬}ù÷H„]Räµñ^ßÚYùç©våTï¨g²é8G$¨/ùqÕf1.õ£'q#ë>% F‚€è äs‘ó O ÑGaDïúv­¢©s«pÚÂÎ_',ºhЕ ¸OÞ9AÄ)daù^[Øo¼Uˆƒš…1ž!ì=,p¾À¹¢h/ÌWä5¼A‹‘,à~·xXø«µûu§>ɾTñå.\_vQ¹fô@šËþÚ³8‚ nÎqÃzSçvqìŸIë¶íñ/ã9çµ—âІNš·r3}ðÝ ˜?)!–FìÆËA<Ȳˆ1Žˆ*—$wî*¨®/‰*«uùHŠ1–Xs©š6›1œãc\Ëk~룺ªU ^î%Sµ èN©»2©uËaåÀÈï²_å·Œ¡*ƒ9ÁšÊa˜*ó‹ hÃ+¨Lâ\Á&µê”v.|L­ö@T¤1ƒº‹c¡C w]s±Û>'þ5æ¡ÿ%ÖùDÕ¹»­ð\Öù¼rÜý>7“Åb„1œCrt³ÎñQlÊ¢ü>ëÛÔ-1ŽÃ¶Ùœ«'Ü Pø¤YˆóPƒ›8ÚÉpI®|jfʧV¡ÑÅ8†Rc u£¯ÊoÈå`$ùNNàt|}º`ðÝïÎqO½¥e³ͦu‹hÚ¼ç0mcKH27P¢¨-Ѭ0HÛ[¾\¸N„>ý5EÙ¼'“vì¡‹t½i}¹§eÊåC*Ì#Æ)£¯ÛNÏÜ7™u €#ºÓãwN¢èˆ0v‰ÛIë·eˆ^¸e)»h »àa0z7ƒ@Ç„VÞÍPË ÆŠ·˜ òóNçjŒH"R¤A-˜ ¢•×G„‡‹äqEüNoÃQÖƒrèpV!e°ë*"{ày‹àý¢8Æ7 nMM¤+o.ãÈü‚£œåRÖ©Õ”—µŽNXÌÚýzŠN‘‘.ð®À†Ki|n¸¸&öÂB¸Ìâ:5xizwD=_²ÉcÒOœ¸ç}v[‡eõ÷¿ž@­š•&àl¦¶l™##¹ÿ÷l‡¾õêNמ)Î €L[ÌAÔ•Ã4!^+’”%àÀ r˜Ç@¼ˆêG!ಹWn¼ð5)“ÓNá¸Áý»%zò\{i2»¼µ¢Ï9ßòôîÔVŒ_9ÂÉÂ@ê‘Ld>pL„TÝÎþ«Rÿ`˵<ˆ¼Et8Í^šB?®L£ßpî% FŽ€ Þ|Ž˜»2÷îNKèØéWp+)—n(„ eXlíü,"‡…̯âËY2slNʵó³Ê½ægmyÆ"Z)(ýì²¾ET ïkfp*o^ ‹¨|µ¥ À™jÊçt¾«ù[UvrôèˆØeËr›À©Wzа8î;rŠbø£é¿_/gëƒCoøë#{©GµNœÉìªQ}©-PS¢¨+H6”³ÐA@ž+$ “RUä<ØÇÝâÄ’Ø¥¤ “ëÏæ­áƳb\KmD1sØê©°AðÄ qLeÇ=È:Õ\!ÐH@×’ ߈l/,,d ¹C žv“p÷¾5!â‡On¤£çÖPp±'L»Ø!œ>ð±ÅIÖàGº@w/³ ¾óAÒ’:u|–¶ü˜Nœ_G<þ’Nf¯§~™F¿ºü=Šj#ö÷® ×`[úaíô±£ŸózyÜÝÞ;6°eEÀØóWs[¯L=•žÜ/¿+0 ·äæeẾ÷W} ¡\øÙsW"™HqpXŽ ø¸a½„¸ÜGÍuE ®y”ˆŠt¡r¼kT•G ;ç$X¸Æt¬{Ç6*Nøy§ñ ¨8‚̽¦ly‡µ½ï³tÃö/_/ºÄ.Ôµ]!ЀÖT;ï"L«çÍ]Ô·m›ñ{^øWT«kÆj1“.+uŠ • ”iåÅàA„' a?fp[1‡\dz®¡üä¹tàÔ÷æ®:®y/ÒóVî¥.e”wo4à¯Äóp+^2zL§¤¿ÐÎCŸÑÖý‘%˜è\î.úüçëéÆK?¤¸æ= áˆv2¶Íc”ض+-M}–wvpN•ãôé¢ëèÚQoSÇ6£D}¿J?Êã«v§¥¼röìIÄgÅ®•±WÃüQ¼Šëæ#¡á^ð«ñCEÜ*k¸›ÌÚ"rêãøùx“ÄsEœï±CJ¾ÄFо™3%Æ·h²ø¨7UåhÁñ»ÃÛÆd¡Y!pKùYÂ:éÓ9Aø}ÿåí9ù!XäDøŒr*Q4@æ`U¹³ÍHlÛãÚ-þ¤ëÂö\È–íOVlÓo¹!N æèR@0e¸<áfÁDSZÁázRÄÁnë·SXÀÙÀ%üÀ% —å”?³•6e¼B¡ánCODh]5ò%Üp\˜˜MÀœ`GŒo|¨€Œ#‚Ü ž·P\«$únÅ4*´ç°›g}µä;èyJÝÑV߸s¿vdú{+çÏ]Å8ÙxÂ5µRœAhÔrQ¿.4fpO:ÉAã`îòMôÀ”Ò]Q€¤–ñ«2N+`åÊ_çÝŽ Ü.aÃ†ÌÆr~59Øf‘ôòÃÈITZÂ9Þ÷Œ‡®õ¬¬h¹ñÉÛÇËE5WTº¾Â}ï¿aŒg=OÊÁ—ž•¼PUçË”{å¨þ„I ,TqD&XÎe$ŸQœXLŠw>ï¶Èíj®hÀ Ÿ¤Î¾ g·Ë:Y­ÿ`#”HøÁéxòß‹ŽMÉÉ8zQæÿ¾×o?ÌT‘O¸ÛÚË™2A2™° âÍ.'¤)jÂR?zz­Úþ4Y¬6Ñ€0kKºî’ÿpfíöâØ†ò< ø0Á"¸˜ø?0rÿÇàL%µI·M˜-ˆwv¢8iñ†étöÜD=s_‹|ññç\ðð„‘§Ò…®( ø®âq·k&¦GOyºe lEôü»séÖIÃ9JF‚(å½9+8®®•n;D¤®_Äásè¯vú /FøUY´ùó Ù|·Q0G›î@ü±÷ÜfÕ6…€B <uÉ# ÉwùRÕ…@£E@ð­úݦÑ+üGdÕ+Òéô3™™/º9§_NÎöA£ÆÜü÷Dóàg?€5ôÉ8á œ • ”ø˜…•×=ˆÙý¤[o9/‹æáShqÊïÈl±ñDË>ÑQ³ÈIJ»þ¿$à˜Ë ØÈA™r<‘0k"õéøoúeÇc ù€`ïÍ›Í7ÅÇj ÞÉÛŽkÖ Eð \¾ãg²)“å mêºí4º8; 3çsE(0YDvn¾ˆ2€‡î³kiPtýeƒ(=ó$ÇÕ”»zÎÇb˜$8z€Š"à†¾bªq …€B@!PWÒöÿ Û¦Ÿ–¬ŽÉwú3‡ÿß×gÎÂâ½yíÊM{Ò6í>~Ò§Ó1Š£rD1‰Ô#CCô˜èpÍj±¸M¾eR–l—ý/wwÑvl~ƒý?‹ˆ£²ÄËñ}€—15<Éö–²ÿ±Íf·ëÈ:Š$;ŒFúÍÙ±-Þ´‡Ed·Äö®½õ~Útüß/¸Þå¿ðÉiþß87£pÜûºSwåá¢ðr髇Xvd¡ýŠ.;¯€b›GrR ·µ»ªfÈ]¡ìÂ2vhϪvÈ6Îè¨s—ºoÆ^óRõsÀ±E×*M» ”L©|üeÉ„.+~ÊØN‰ï@F²`¶ÊHœ}Wr“*©Ô³ãp: Œ¢§ËU€žb,]ëÆrjê< Š€>ztÐþœ¬w9¦÷²‰ìß°îåc'îùúÌøŒÄð„¹£  ϾdάyyQ×>:$víÞ=4"¢yHhht9(„××J"#XÛ'Î7™œÂòî°‡9ögL>ZXxŽ_„çjUfƒ8ˆ¹…Ãa/,*°/ÈÏ=}hïîm»ÒRö´ˆ5;|’ž²†Sw¶ujñíõ¿üñï¦SÏÿÞõ?>¯Fá~‚ëc$î!y.»ó$¾ˆ¸}õNÀ/܃&I~‘³o ¿¼þñû›€]9Av9)Ó®CŸÎ_CϽ3‡÷L®*õÙµ›•§ºS5ÊFºç%æÿRR¶îKîwœoúxî/kypð`8‰¦–>Äwÿ@ËJØÈÎAO“_!€˜æíc£=ÅU„»g£Z¨RÏ v(²fEOUÔà†¸zÊépœòj{9ܽ¶©E…@8>n\xÆé“³Ø9dbIAÚÜÏÏfÝñáñãXÎîA> þƒé'CölMÝÁÓn^iÄ™çå_h¼²*™p©¯>úü*eØ‹èÜgÿ-ø×žœ®ê¸¾M>×ø°Á|ÑÓ| Μtæ~ôfЃwýÎù:‡(ìÍëL¡úžËœýÇûŸóÿF!F"à@í……GÑ‘o+ôøWy6ÖÓÈsoö÷ZÅa¾Žœž˜|Ÿ™õžöüž®“¼®±‹Ää]ÞÀø¢§>ïà>GîÂ\q½ýK¶/¯3išë£3ͶéS³ùƒ#pýxæÁÕQ-Züvkúâ4°õ 2,‡™ÇÏÒé¬\Z¸v«ˆ×<šãXZÄÀÌ”¨[bkڰ〠Œh,ÇoÞ{ˆºóú¾]Úszè-"öe}p„+É¡=»¶ppÓË©Â&éši-é.AÀyOðÿT¸£WJ×Ì›‡[(ãL§Ê>Aºµóa-M³(àÈ=6±™pAiš(øì¬å³£ãyJèØéWFÐS>;»z,Hê)ènpV¢ð û‡õïP`w,d½Øµ¤í…Î)i¤”4¬™!Ä}èMÀóù?È7ÜM`ªˆ€_ˆ‡„iLw>Ъµ>™Ëb/¤C³?5=½=ÍyB®k¤sùlKl%—= ’„ÛÖ.**ì3(lB‡Ä‚…¼sÆ#ˆ3ò|òÂ;Áß¾¯|¦A‹Ñ8.ˆ¾üÇÙÛ»ôpfÃöŒæLÀ/x3ûó ¬Ù¼—0!ª@oŽ}ÃØÁ‹Óm•?¢/Í]–JO¼þ•Hv1„CƒAœ.'Í[¹™>ønǹ4sêçXíÎËŸí­¨lÆQç$ÙÛ6þ²·C™œ‹çåÑLúZŽ$„‡D€€ûU<ä›uFIG…ZØ_Y㌓‡÷òÈÜÆŒ±1ʼnxz?7. ã˜Ûg¾˜Ÿ÷·"ÆÚmy«ùšuæuašË>{Ƨу¦ßzþÿo°b$^ꂜ:zäËmšöàÞCÇ©lÌÍ@¡]Y<^YÿàžÙ¿»c©¸¹rÛŸ§M&;§Ÿ5i@Þ©úàÇ£µµCé»s p“ËÉïRKr™Ó24‡ov+›N;»x@+¤ª/µ“þxÈ7«.à„ŒY˜· 5Ñîƒ'xìiÎØ'Cû¨Æ¦U ðÛ}ð8 HЏÂå^bÞ´¨óÙz?/òrâ¹â{ö†úÔSu>3 õô>7øzãm€ª&4$÷ãpêsø½…óa}ˆ÷Ü-SR+si+Ìá&b(­Þ Þ’|Ë—8‰r³?Óõg¯Ð´g±OƒyÓ¥ñR¿|Î7³x8ü™Ïüâ‚K‡‘¥²Á•ÈòTŸä¸?—Ã~~õ¼ïb qc—½¹ËA«¥¤ð—§–"7Ø´arÙsX½e~ÄOEþ6‘l7k4{YZ©Pþ¨¿±–‰ë?gy…qÚñž‰­<o%µBÀ£Ÿøhñ,á¹ÂóÕôT­Î8I=}½ÏUz¿|ÐUES@ c`¿:-ðoÒ²4Ý<®ó¦Í•‘o È"îIXd y‚2\QŸ©Ñ1Ár]åôàS–ØË¯¶Ïõ&߇öڛϵ¼É7\Vy|#Ý.ñ–ÀØc` Ì%Q§'§mgƒæ¼No˜0cæs•ÿâÜHob-otGVÖéÜ)ë_;z*‹>ú~•ç"4Dë«ÍÀ ø¥¬\öi~~.nl|ac’$\b^®‰<0_šBøÅè77iw§© ©jƒ™4¶ s‰øë³~Ú(›¢æ5@`ÖO)¿~‰ÍØm*Ä“}Lb^ƒ¢Ô®%Y¼ðüˆg Ïž/¥§j‹H=}½_Œ¯|ù*Ý_{hՑŤ'x„o$DÏpûjt8ȬLÚ´ie @½ˆpùüƒ(bòø,ó2Þ³å&ŽÞqYBÇRÞÖ‘'XÞ<,ë¡??äúéS§Êí_Ql7fGÉM€q…Ï=¾ü†ƒ¿ÈÛÝ¢éÓyPfIjj¹¾ÌDÀY©›{Í¢ÉÜ·ûã;÷kï~»\7º%Ü(×8/à–¾móikVlå¶É›sï½ÂfkˆY,ü$øÙÎé{™€s¤™ŽzÌSH5 ¢V!ÚƒH?¿NYÂå¹À×xmI?L[…S»VÑWà œeœú £6—G ”~âÍâ™Âó…çLé©ò€UµÆ[OAÏCßczAýTU¹j›BÀN°ó<ø¿ÇŠù޶#Ô<"qÃfŒ5¨‹€$B'È ¥&v‘Ð_|ÛôœÉäšÍ”R„NcòÍÖ^Óä?ÞïBpƒRû7±ÿ7Ì+$ܼ¾œLŸúÌŸØÛ~>6°û')q}øâÌ®åvl+Œä¸p!pCB‹/¤_|òí¸)·`ÐÃMÇΜÓo?ÌT_>áÜà |)Ñ‹ÜÞmi?,û¹Ñòk_ÚX¾ÀxW(Á×/… ]¬O™bÖfÍÂ1>é‚‚heâm µR(OVŠ ³s:ß"AÂOžË¡É—ôW>áU\ø|Ãmç$gníÐ,˜º¶‰xWà+S%WQ„ÚT9åôïŠg*téw_ÿ̽Fðÿ¼Bé©Ê”[¼õÔÁ=;?_4ë³oy[µõ“,GÍ•!PQ‚îý[qUªU~¼÷êÛ‘-g¼ó—Ï™YŽõÐKök¦ ë§ßkO­¬Ýj}ÕÀçûµbn)°Ÿß€A™$AœoïŒ;Vý²hÞj›-0C tÀ“·üÝ>»¡è àÄa ‡óÌÜDÁL­üAÎä»H$9r8œl‡á±€B èœÝDû2O¢ ÁûGE„²ÛŠÑne æ_AzùlvíÏå¶…;X[…8©mLEGERdd…G„ <+ðÅ `EÀk}]$/§Ÿ¸DÜ€&|ôîÝœ–1lܤ‹øyÉz*Ré©2zÊéÈ9°{ç²_Î[Ä>ôg·Zê§Z_Gu`#F ª;þêÅõ†sÆLÓý—ã_¼ÎíoÎ=Íìö÷Ü“÷:Ÿƒ.ðÞW-×ô"¼ð¶i³“?¹KÑŸŸñ¾õ»éwÛ2j_j`4"kÛ 8@Àb ÷„ú1±²¦%³¿ú—îÒ§ÇÄ.Ýz„FD6 ²€¨N"ì…­‚œÎ4Ìn2çä‡`´³OÄáàœqù9ù¹ÙY÷ìÞ“¾}s&Œ|åƒpŸ/ž0ÂxWà œ«Ö¿0±»;q÷:ø[UP‡pCÁ LøÃbkç £N§$ß|ñE¤Ù©¹3Ÿ ø \frpä¦3¶\& MçûK°qÆfÖçÍøéˆà§$ÒjfKw¨ ÜÑÑ‘Å$< $œñ®À8*©•ê§âR]üñë\6÷ë%ËæÒòνúµKìÚ­kXDTLHXhd}è)‹å|GyÆv{ô~¹¨¹ÐSÙ¹9çîݽsïÖ4´¡Îú)PíWõ4ª‘`Ço'óöÛmÂNkÇÿëÒõÛ=•htÖd2ß:ý^Ç‚é÷5w”çüý¼Ð’üõŒ¶éæ)È”ær¾Êóëü\­ÏŠ7"÷¶2-€|Ë;W¾üì¬Äwó”ÁÛ„å‰çØWîÇ‹Æ>aaí'6‹~­áhÿÖÙ'N~tÖá€eÚW¼€‹^†•d–%ð¬âe¼ô¤ÿ7Ž«RÌ&Ó/a}°úÀ*w®ãFXea f¢È LoXu!nrÎ.*AìˆRhcrî `ޱéâáÜ>¶þ»wlB¿À ¸`@%ˆµÅÄþóV&Û¡Âò ò-–á~\•õÛ'7n6ù‘[©~â}-ÏÂÅûxÚÏËõ¦ŸÆ_§½Ìõ Yø­î·hYG™9ðò‹~*SúÛÄp'Øq-à×A< `2PQ‚¿ ôò{–A§Ç>aKU7¯ 6™,!×O¿ËvÀkZô!Ó¦¥Ø_šô€“œ«E±:]ûÂÛAcžž&"Îø°&ÿeDŽ3•dÖZIª½_|XÊ¬Þ 9/ã%‘ǸÿÕãïÖüüãc¢E˜Mí¹–q1Ѿ8}f‘š< òåRà§TºœÀò "^#ë7ïOaaÑ[s²ÏÞ²UëÆ1ƒ¹ Þ/"‰vˆLz¸ûô`í…ß2Ü'¥ Àæö gŽŒ£ à.&â¦@ÄA¼! ÞÀËl‚ï<÷06 ÚÂç›ÝN`ù.(ë7¢ŸÈÇC¡~j@ƒÒOÁ¥û¡!~×O8 UGÃ@ $ÁU7ÁŽON쫯¦˜÷ûæ)‡Ãñ èáS¬§ßkmiÿ›»î:c˜?"ðäTÇŽ4ó÷Òß"ªÑœÿ`.0¯ÔA~¬½nE{n˜ºã—£ñ’¡„Hùä75¸°2ñç!ÜTxnòÍmr¸¨èÇ!áàOÿð°‹¾=—5«ÈéÄùÔU$. ßp+^À–n¼hAÂ¥/8pÃöj×»lYnzrÿ f¶x²ð@Ì|¼_üÀ¹\! ‰ ”iåÅàAžý˜mLÀmœÉœ•[ÊAÀ›–\â‹v‰õÛ=î&ð¡ \O€§"ßâ–òåÔG(S>‡òØPú)4¬ÔióP怉ÄÅ/ú)`g¡*24Å v>æÇPø\sœhN°cººSÍb|×øg¼ÒyoÖ×\o©0½ÙÜsüð“÷9?":Pã2ÕµCÀl yÂá²Mæ£ÃX÷é ,Ï®]i;ÊÈ\*o¼Ô¼—A$A*A¾aÛÁCW7 _x.ë‡Ëc¢næøˆÚâîV­º¾uüøJ^®­ˆÄE¾à`–( á øõÒï[Ë«ª%[y¯NbO—ð³ò+—ä¤Q,³Å×má ¦0öoù4‹Š˜€³¼ØO\Z¿³7J±ñÛóaÂ)Й€#~:⦋ް‚cÀ%ˆ7Öc;È7°TâSä³gxýYê¼O•úçŸ?RÇHŒü©ŸüsªÔ€H°£»ùÂݽ‡;&}Bâ†MÛýy/Î4ß§»Š^ã7p¸¬‡UìJÍrû“ÊåDB°ù÷~q¦é-æ¡RvN}šgŠ€×ñ @Cy{+rie‚ë‰7ù6ó° 9ñ¢1äH¢®}l¹-yp¤\®‹9#ï—0鯶UÛòÍûz„}¿·p%øšdÑû¸çþýY”áò@eˆBLX¾‹x ¦ÛúÍž_lç®'á~"I¸[W¿¥1ñm 8°1¾ñ¡Òm>áîˆ'Êòí×ë…çÏðú)RtÊ{p8îYòï‚ÔM#¿è'ÿž‚*ÝȤêÿw>…Ì-H°c™°aC¦\ãëùß>Šˆ-*È{W×]WyÊÖ¨ˆMÏ5#[À½qyÄs†9,¹ —Òê â-É·tr5œ¹oKAÎÌžá±÷r¼À fdørRûØ'2íä¶×V¤Þ‘Øà''`$—åöÚÕ£™¶p1q,ŽÄH〉´ÜʆðÄ›]N!,Þî0…|ƒ4fów1êÒ’íÆÆíÎÝž‚ˆ‹e&滀]¬¦]‘|Æ07œ~ õØèÄE: Ký “QÕO‚—~‡lëE¿'Øyéó5ö‚¼™\glI½´=ˆ‚~ýøT{Úô©ÏÊÕj^<~WÞq¶‚¿Ëæ¸ß z§î|ˆgŠ€ûèZ@¡ƒTÊ—,» ܘ¤Õ[o9çMÆ=•}g«øo9VóhÑØÈ((ëÐ:ù‚Ã\NÀFNr]ªàCM–­ä‚ @ÖJÀEÅÖpJ¸SÀÊ‹A—Òï¤[o9m¤?’€ +x±5ØÈA™r}#=}#Ÿž7Cê'+FË”eB€Dê!Ì¥nÂ\®Ç>JÕB >ì¼òAx¼£¨àu§Ë5E6’U/¾Õ^·´R ´”¨ÔÿÜl¶¼áp Î×hÒËï…µyâžü£õß²Š[ÐP,àÞ­—ŠJ"É·ûŸû¿\6ÔEÏúD_Õ]ê]?³Ê&ÃJ¯D!Рt‚ßî9ãÓL~x/*”¦½o ü?•N¾*†úÃzßá>òKÜÒÑkaXî6' ¾ÆÝ“Yÿ§×Þϱµá¿nlÑô-²ÎzpC‘u«¹B@! P4-Ü vôµlÑìŠ3ç.o;žÞßySÚ}Äëó:‡°ñ_y´×ç!ßìΰ‡3ZŽyzªëE¾}ºoË3™›Íå{DøÍò5ì;ãkßÖà»Ò÷–Õ*©cË6³øæ8‰¹‹«ýþýé×VëÀzÝIC(Âb L$Y›š+ …@ÓD v\N}©x ÀDØÆVé:oÜü¶¯AÅœÌ}[ù½üG®Ãí¸ÅN¸Ö¿F´íÔwú}Že¾®S•ç{ž¸çt;è/‘%ëEEWËe£ÍðÑæÏ/äOø·dµÎè¹lÔ¹™CzÚ¦~ ¦§nµ P(Mâ; ØR…v'Ø1ë¼i³Oã;¿ø~D«fš>à1Ô m]¼À]¥Y<=ÍõÌ#“ÒÝ‘¼6ªEã"Àa!çzZ§é—y– ¶Ð|À é…›c±šÞ.²éÓY±XøËþ⌡û&­ÛTBr/\D@÷àt.[9ïdqzÏ€V®*S( &…@q‚¸k"È~}ž`çí·“-g´Ô‡Éž÷ ×P2ÈR£,“fzâÉ{ïðX#6†+ihh´ÒE´|¨QÛ¯,àõpeÚ¯N=ÊZåkYµnwþV.qÞ&%å4·÷ÚÆÚ(ü@rrk#¶SµI! P(6"ÁŽîú¿kÜä›`'xDâ†Íàyé]óU§iÓvv7ù;×ã!ßL¸¿²XÂzL¿Ï9S‘oŸ@]/…¼¹ç¿4m¯l'äé,—Õ\! P(uE v2’ûÿÓF?%Ëb¼Ú1ÒWÙ-_šÜ‹ÃÔ-â ÊpQðv7ÙkÖL“žšêúºÈúÕ¼a" >ž4Z/[ïÐíý岑技×ÓÕè¸iÓ/üÏ! Å`ÌТ"ÛÔzjJu«M—;ê&Ý[qÉÕj®P( # ìdgÍeãÎ%ksƒƒC/O\µJô¾–¬¯ùÒ‹F¶`?ï7]dßÌÑ˽JÈeßò'º4ëÙûÉ©Îù^ëÕbCG@×öÈSà¬!Ê.ÁPs7œ½ð_%Xè;$¡î!àܧ£,à%N-) …@-(N°³„IÒDOšöN§N]®k·vmGÁÏ›‰÷oÉ–»—ß[2ùa‘É’ê}F¡]Ÿšæz寷s´% 2<ç£S’gÙ@ jf=^ŒŽ­â¿Ê8qôUöA‹õ Ièñ ¯Ç¦•«Z3Ñ^½$…‡"àåR+ …€B &T”`‡@>Ÿ”’ú'JI«IQåö}q¦ùÊÓú¦WyàR7~Ç–ˆF¿ÙüØô{k‰òKÖ«¥Æ…€¦€¸MogÄ“S¼¯ BîKî÷+ŒÂ&Ý¥?Ì3cp§™-à®\Pêñ¾QU+ †ŽÀþÁýû»ìú|æHîßœ`‡ÍÒ&mL­SŒï—fpêΗ9ÄïHoŒxPÞ]3Mzªó Îé콩¶Ë\¤(*ŠÀ%ÕGÀû»ËÞÿ«_J%{"l¥§@"+Ù­^W+^¯ðs´2jHÂMKÏ/¾›¹«°S=ÃÆ¶Ü ùuËñ^®ï¶ù»~`â©ËÞÿ=ÔB hH/bÑHpT] ²l0`ŒÝéú†õv”[‡‘ͮӭ=6Ö>Æ7X²÷‹L¾Ë&^É6iÚ‹ám“þYÇxÞò9ÇØ99ÉuR)ËyÙSVÿK# ¹1ærrñ²œäºÒGÕàŸîÒòä'\.i®x=_ „$Ü—Üÿk¾ÛnFSŠCÞSÏÍ*W=BîØŸ¿(õ~\"3†KZ·îD¹ý´Bn—ËE.—N‡Ž¡ûÒÙì<:Ÿ—O…ElÑÀ#ÛØ¥X½cl¡˜ˆ0jN½’(±MKAÆ!÷ûMøµ{vfÒÆ³ ,‘LZkŽlH+ ÑŸ=€Ü÷ÔsK=Ô‚B ³´ú¶ ¯+ëk¤ —Ã~64âç,kXïÛGM:öÑ?g 8Aµ‰Rs¶Ãgºý6>®$°g±Ôtí-²„=?ýîÜSD܉[;A3Q.>^17™¯½ëþ‘1Í.3™Íí4³)ž]g"x½’ ÀÀê.Òs¹÷ÿ˜îr²åå-þ⿯mä"ÐÝŽ ÷A­ÞììãŸ/ûìY7‡r9†EÀ pI’ÐINAÀ™A!$áxðÉY4­l Áa¥îpÀ ÅïĤ›ÃFQa¡–mÚE«ÒöÒùܤ%¦ÈP+EFX)ÄÒDnåbU„ÙùÜ|:|ü,åØè‡•›):2”FìA—î.ȹ"â¸S}*Úԩφ:›kYÌ¿ãÁxÆX ³ºb¢Â4«ÅRòò÷iµu+Ì{tY×ÄÖ×­4u´BÀ·Øìö ÊΟ`ηM2™ÿ2íOÏ·ØÞ8¸qۛ˖͓v…DìåwBÛ:ô§õ¢Â{XZ¼ZåbòõY‚ÿ4ý.Û¢\¯M5^”Äå[:wî9dÒ„©Q‘ÓøÕëõü›øùQWRCøú»²²óõœ|›Éò§©|þDaAÁûÖ¥½¹jÕ¸xòúרd-H‘žF|ÖèàíÜDXK€Ð¬e5I˜>°?õëÉL8eH—jYœ‘PÜ\wvæŠVù±2ñ¶Ûí´=ã}ós e1ñî–OGô¦îâ(4$ØŸMheÑ®'(uw&}·|­à”›' £ÞÛ’ÉdHNØ p-ÓHÓÝO<3Þly—-5mz&%èƒ{%QŸÎ Z˜5ÄÐn/}\r&Ý6¡äZRWæß…´5ýmØž·-ýðóÝFz¨uïN~þÆßñv;O‚ˆ½òAx¼Ã^ð”Ó%B÷†À!…2ótÍ2}ú}EœYÚ&W×fŽ6Ay‚#á%rÍSÇÅ%´û×׫SÛóüsÛ.â%åuýcùúÿµ×èá¶ØãÏ^uŸœökd w:uÛ ßw#¡¸A®B²Eí#wsDH¿i³fAáFø&Þëåv î‘Vo‡ÃIEEE´4eÍ_³â[DÑMãSÇ„–~©·¡ŠÝÚ‰iÿ‘Ó4gùfúï×Kiò˜4vH/A•5¼ÖWW¼ˆï}ê/¿Ó4Ó+­[Eë¿7”º´WÖ®ZCªT”G€?dihï$LÚÞCÇé‹…ëb9.ø7·=öä3ÿã¥×|:4¦m»Âß9Šò`\Ê¥€ßMËI3ÿé©û+‰¼û|Ê×S5’|ÃêÂSØM=ú`dLó§âš©ç¿Öf—J®ÿ·wþáé§ÿ÷·þÉeâ#¬$\÷v 4$Wæ±ÚÜ)~8! ù©?‰¢™€¶ß¿?ýZ?TS·"5S‰V*‹XÝÊõ:Z’o»ÝA¶ÂBúiývš·zõé”@Þ0Z‘o/¬*ZÄÇÉCSF ¼f/ÙD?­Û.z€«’# ^Ä÷LöQ&߯êÑQ{üŽ+ÍL¾k\:@! ¨>xÆž¸óJ3ž¹ˆ×óOý½ã¢¶ím»uMÌ›|óº–C Ž}jª>ÚM¾«_G{‚|[yЏñGfòýGõüW˜7y_K°u“ðßq¸&Õ笺wèA혛è“âª2>©NRIȃ¹Þ’Û‹CÊ¿†˜ó`‰}²!<¨¡£\öåþÞ’|oÞ}­ÛE}Ù•âÖ‰C(Øbèž~_ÂP§²€ðns–¥ÒÖ½™‚„שЦy°éŽÿ{j‚Éd~™_¾ú½×]¢©{°iÞ꬀­è8µKX¨µoûšq`˜7ñæÖl©ã§é#ž¾×ñ³[NÏX¾Ã'ÜtÛ„èæ­žPÏ¿®fQейÀžIø 7ÿæ÷ðŸÃµ©oåûÅ›£¨fµÝ­Z'Ð5áÊ’GÂ׉‡ýºC ŽPÍrÈÓ?¶ù†ÛI!»œÏÎ¥Vme·“Hš26ÙS­Z¨>À-®y}¹h=ÙlvE«ö4Mœxk¸ÕöN[îv¾ýª‘°†+Q(üŒÀ¹œ4oíè­ÙSêžO88¸Ãóì9ZjÎyÓMOOÓGrêø…>n ê•>ß¡±±m[´Kê2#!6†Ôóïc¤kP°oÓ*†¢bbþ›œ<n%¸Fž{¢²¢8Ùi'¹ƒõî—ËFš+n «„|WÍ’M*I(ÿÖû¼Mxø1ö%vû¥ë§''£KÈ' D:ÏwGõX•º—ÆÒ5£ú+Ëw-†aò%ýè\N>-Ù¸Cà«\Qª¦xÇöêø0û ¶fŸo³²|W 7µ“B Ö?³…欸ŸÞžs mIÿÄÛSVlÌ`:~ò.Ú¾õ‘Å/=î\Î@’«EÄ<…\x|Ècý¾èÊ«ïaƒX«›Æã±×¨JI} ì9¨€‰£Ç÷5üAnCµ¬àû{€l¯Y7í”ËFš+n¤«ÁmAHBO“Ü! ›{þ×ó‚¶l™ƒyòq4ƒ»w´g‚¯šä¶~;„õ;ãzoÚ{DD;Q.ë†0ðCÔ˜•i{¸wÁ¡¬àÕƒS¼ˆC¬Ö‡9’Œ®|¾«šÚK!P[E_,¾™þ7ïJÚupÁo—béÒîrºcâ\ºûªo¨sÛ‹õèÍïæMˆn‘j±â¢.4ݼˆ½ðýn«zþ/[`¶CãZ„†‡=T|ªüøúÛG±|u­ÓÈM}·¦¥5«Eðšáå÷½’¹-`0¦ Iè÷z«]¦gÊ}õ"S;¹\—¹ÇúÍámŠjÿ‘S”g+Q=êR®:Ö"¤dçÚ®èePVð*ï ñ"žrßÃC§85ˆÿJ "ÀiâiçÁ˜t_A_üt 8¾ÚS:x¦^&Ó=W-¦ëG¿G­[öÛÜÏ¢Öòâ Wæ ɾ´‚KŽ^ÝС—ŽȽ½-Ôó/ 7Ä®c'ß1 ¡«$àv[Á0¯Foš6-T 'ø‚Tb0Œ’}©2Ù?]Üܺæò!wòàK»p?ÙÇœÃJ‰8ß»4 ²9ˆ—<‘9´cB+ 2óÅ++¹˜âEy9c¤#Îw%û©Õ …@ (,ʦ´ôÏ)eçìbx´ÔÑ&“…úvº†õzb"KmÃ~¡·ôV mGó_©lcúd.øŸ^çB¼,൭sq\€;Ó%b~³œC"Ëeg¸TIv|.›s8N82†ž9ŸË~àN·JÐS)¶î±É”ˆ —FO²SéY¨ !€•w¾G[ö}EvGA©–Y‚Bi@×[iH©_j›÷Ä‰Ž ÑχÀõQZÀA« bÞ%T¹Œ2À‡@ÀC,VkÔÅuªð*a ÜFqýY'ÛòóÛs­¸V¸f  ¯’¸Ge~¥<]îTOsEÀë øªªEHÂŒä~oswËŸÄ~.ýžƒ€ktØckpi>±€s"r01/䬎ys. _iÒ×¨Ô í¥·ƒû8u{bñ e7wõ[éî01Þ)Ö\“ïe‹‚¬¦·ŠlútÖþA2$aÒºMœ^·~E#» ÷úø !^Vî–S D»ÝEááêÖô啱ñŽCXÀ%æÊ ¥ž±IÓ"¬K£|Í_³…–nØI¹ù6Îkb/ä['§XciY³y/,9yŽÝ©·ötÓø¡ÕnÚ÷+RÉérÒ´ë/Çì:pŒ:´nIÖª9mؾŸõ“‹jBÀßÿn%Ò©Óc¿ž@]y´?†‹û±{†O:#}ÒT§³¶ïŸCØâ}*kW¹2c›u§A=î¥^'“Ù]}Á3i6™ÃøI¾+%bÕ/Uì‰m”…)uøóùŸ½$…®ÝJ#t¥_O! þøæ7t:+‡¾i,qÊ{¹ºÂy]¯ÿâuÛèâݪEÀ«ûÌTØP­Ä51i¦.שB‰SsÜ,)9»Ÿ,4ªÿ7 ©ð°AIý"P.$¡Ã +x½‹¦›2KáiªdmM—¤ Ü#œßx5-Bí_ ä‡ðVR)î±~á8³•–ÐÀ6ü°2æ­ÚLWŒìK/üæúým9‘ƒ^ýx>qFÚú–Bîûjñzê×¥=÷Àõ4åòÁ<>¤fÄö®k.¦{'§ÒòÖ×K(‹ÃsúZ€Ûæ=©m\sZ¿-Ã×Å—+oû¾£ôí’åÖ×ÇŠ¼‚Sméïôæ·CEïÒä[ã(&céæË¿ »¯\ľÞ7Ö˜|—œ˜s[ª%a®+‘Þ˜‹2ùä÷ïðÐJãdsÐËR=Mù€ º¨ëïÏg¦ºç*÷c¼¼¯W©ëôÕWSÌ èír_îÉûT.q®ÌŒF¼*Åm2›µ;úÍî¿Ú-™Ã‡?ÞníÚ³õÙd³Õ•é,váãl˜UžW£¡Ò „n[¿KÔQ5 P»T8KÌ•¼tÅ^¬äËíÐØVØ94åOë¶Ó¨ÝiÌàžâôZDGн׎¢?ýçZ•¶—.܃Ie&[ ·G" Wn&x¨\=z ëÓIsüôyúdÞÊÌ$2–&ìGڴĦr’¶çû/XÄ>Ÿü¸ñ‹Ev„í8|ò¬ã‚ço·õ"n+êž·j‹èè™Ô†3Ù§0&hÀ}y gîÒ–­ÝÆû`=¨;_¯n´ïðIúlþZqÌÓo|ÍÖÒºåÿÙûø¨®+ý33ê]¨½wD7Û`°÷†kÜíÄŽí$ÞÝ$NùoœÝì&Ù$›¶Ù8e“¸lŒÁ66`Àô&zGET „Po3óÿ¾;º£ÑhFš‘FÒŒôÎï÷æ½yåÞûνï½ïž{îwp]G ù»wþ»<±:nL6#ã‡Ü‡:\’ã¤hhR|N5øv>³Ø×–O‡k:mô>Ýç¾érôä9rò<:”½Õ5٫ɦ[Ý:ožÚǹKW<Öÿ©s—T‡5ÿB‘ Ä6cü´™‘*}æõÖòMh3Õrõø¡‹²zFy ûµeåž#º ±Ã{×õ“Õ¶§g¦¹|Tfíû£ë«Q.yÅ‹oÄEŒÇ«0Ù–^ËìFçÓŸihÁtáT–Ûvm6J¾7Ü~_$mK³/X@_¼6‰„€‡j‚ ÿx P«Z×O½Ë¤èñÅÞeîÎíF.—)+÷È §&ÅKZr¼œ…Ë¥ EGññÞ× ‚Ø D|óÓÍê£Í㯲~¼áòÍçIfZ²¼ý^]õÂÉ¿o.ß(ÓÆ –Û¯›(ëB ,]¥gJ‚„ÃgšDË–½¹ èè¦ÜNþ´xÈ=­K|lÀ­I84NÙ€\ ±ï˜#m‚2õÊLO–€õÒŠJX]ÍòÕùwΞ$Ý6Sm̾ºlƒôë•*ÝzŒॅ’B+%ý¹¹y†šÈÌÑoB«7˸ÔYëdÊD©¨¬†6ËÓÇÈ¿ýn…éFCð}–xæ=sÂPyfáõÊÍftD¡Þæ‘-ûòT'†þºï®Øª:)‰±pH—¤„Xu³ÑQê(áDÊÝÇÞ–¿r“âïÞŸ÷A#ð›)³'ýPž¿{»Ìòã€o—û#vÑÏ*×@§×l™äKÆë+;1Ï@Ëlg  ÿªµ·öá­þb1©Üo?²@Æ£-Z¹Võj•;…‘Ýì¨ÑÍå<@¼–0‹Eú¡}}íîYxfÂê¸ì<|Âë3Ó\>:Ív\{¬s«ÝFžp‡ØMo³û iðúº Ö) ÊfÖ¾C,(¦—_†ÿ‚ý<‹@g:~å¼cÁ^Ê®¡¾½((@» ýú¤H/wŸ® ±“J`í¥ÄÅ4ô æœ+Šå*´zOÑÚ`³¨“¯üȨt—©°¦ïØ¥•ÈÙÂË2uÌ ô÷E¾÷øÍq±ÊzýÛ·V( ÌóFÌ”c§.pS‚fóºI#”U‘íœû‡öë©„:?ôoï ÷ }Àu%çè¨yâök•ùWQçð‡ûé§Ë{˜>n¨ò¹öd# EþÐñ3’ý0zߨA}dÛ\•N>GøF ʔĸtR’¤ î*‚ôÑÔk„‘]‡zP'àçöYdâÈþªÃtع /|2®c^¼Þ©Iúôv[^>,+¶ýP~ÿþ$ùlËwåBÑþFyõI›,w^û'yöNt¸F}f£N Ì>£z LŠRQnöúO]Må]õn(ìÖ“mÖU¼µoõ‰ŠxÖFªô¸!}1÷Á&‡ŸW£O…—Kä®9“eÒÈòt(Ýeî´Ñj*5)Nu\äžñúÌxËÇ=ÍŽúÿ³?GŒæ óÃã$á쨼[›á‚ÒZÍuÐuAIIh7á‹gw¸ŸÔØø¶hüµè Ý:›jø»r²bkd/"wò£?~˜C-­IÃÓ5¹§ ñ"L3Eó“Å<]kì34Ð’ø§<º ‡©ûÀzì*Ž?Ò[2hVYyµjû´âj!eØé‚béwJ€]- w>kî2 –é¥kwª4iÕN^³3$ÆÇÀº~£ì€…œ½¿,^+/=~‹r=Ù7™"¸¶° ø•p]9yî¢ää(@îž§ÿE%ep;é¡À¾ûñþ½Rœ»âÐÁ`ǃϺ» ׎CÇ•›ËÇëvËg÷*ÀS‚I­tŸ˜‘¦ôõþªíÊ?üXþL²»A¥K~~º½üôoŸ8óQвáŸbªà߸X‡U¾–ñŽNªdМ]Gß3…M‡óÃ,Q2²ÿ-p3yLz¥Œë€bÈw¸° †»QM­UŽ¢³¸ûÈ)Õ ³¸1ùÒ>\ ¿ïØiY¾q:G%Nw-ŽÆ°“La;§ðå3¦åJYÜS¶Àµé:w1h¿ÎQ }ŽëÚ[>®çtÀ¶³î0ƒì_»™'ŒƒKðÕjÇ0R¢µY´m´67ã:¿5Œ”„háhàJì6‹ÃaÓï;«¿Þ“)ÉÇ´–oÚ/k³ÊT|¸ï¾~B£«~þêçê£üÄm3T(ö3¿{wµ|ãþë•õIŸ\… ^Ÿm: Ù‡O©+‡xoºz¬ôïåxIéó¸þÙkŸËå+åê# ë[/¹íÚñÊB°Cêà´tý >y a±È 0°gY i¢ç˼ɑ.º#%)V1еƒÃáZh¦µlî4‡_¸ÞCÖ&É–R ºÐÌ´ðuý2­Û®¢ÛY„[‚úï… ø4X¿Ý…l$WÕuÑÊ­Ê¢Ìɘd7Ù°óˆ¶tK摽xY~w×÷ôôÆ8tÞáÊ¢÷鵯´‡´¢Ó7|äÀ u)ïLÆŸÛ™YCÐb'ÿÜèôu§_7;4?yîne“µ/úkrQw•äÁÍä pw¿NDq“ÔR‡HÖÐ¯ÈØÁwÃ@à½c“ÄÚ¾£­Ï.¯w]Ú^"R kÓ8ŒxÐõŠnYœá.¾´} _뜰ÌvÿíGoRëïüö]u¸w='ÝÎúÁ½Šß=ƾØz#W—ä'_¿G½~ùÚrl“usù499à;MÂT©ÿôoQƒìµÕ鯚Ånù¥#>SÀ3h‚† J@ÕÙ>‰‘’¨O™Œà'}mÞ´‰afð~3&qŒùâ “ÙÞx¼ÌûU^ðCÕ°x=Íã Àp¶þÐó¤ü —|³Ñw¹´\¿eºüËWæL[`A;è1/îœ2ª¿<·p6†Ò‡ÈVøÈÈóüÑöš€8\þ£§n (øÖzö£ÝíÔúpÀ­lA©Gú}Þw‹ »ªIE¥‰çåÏð·¦uœµ\eõ¶C ØÒšþÔ.”™®\X>…ÕiÑÛ¡ëÖÈ4¸¡€Ó‚M÷ 'WnÙ—«ÜRèÚ²,#iÉ ªãK+ý¶y|T=ð> Cý‡/,ý¿Ý…ÔŠ´^ó>é›Ígb®-Æ{`%&¤ÁgéÑÒí«ìs4`þŒ±p™¨–;àc>¬oåKË<Öì8 –Á2ƒ”ÇÈÓgLJþ¸¤Y$‹ AQ,ä¾H¯”D¥sNn¥+P[Åf«SÖî·WÜ'þh–l;ø×Fà›Ñ*G ¸M¼ñ=yú¶Õ2eä¾]Á2·CV&À„£8œ5 ®DîÒ\ûp¯ZºKà6F 6ŒVlÙçL.s3ØAå¤kŽ­B{smãlC pb6Î; %\‹û3Ãë¼å£¯éȵ½®æ'ßz˜xÝK_«kŽëÈ‚ø™—a÷Saq:) s'e-B{€ùÛ”„OuFYTžv)ÐyãÃÒ& ¸N§µë°¸?sQr± 铦’Ù‹!8†_ßu¤ñD/Oy$â#ýø­W;œ!ËÀFàMè+Û·g²Z‚wçá“`(P“®—p(qÅÖÃòG8![”¥ð·ï¬†•~¢ Ť©³…Wð1>¢|T9ñÌf– øo>róUÎ4¸Áö¿µJn½f<†*{ˇkvÃ6gJä0†,ÀJ?gê§Ok£‹?†|ÔÀMWSÝ•[Èâ/v€™>Å=å™{®oL†Ì´Äþë?TÌ7_›å ŠÃ‰[¯c‚×þðÊ•ìÑÏÜåc N£o껟oE›N;ƒÃªZ _Y²“Ðú W,Z÷èǪ…VðÏÁBÿj ¿‡‰ŠSÁØâI8¹r|Åß[±MÞù|‹üêÅÔ¹ÐKÖdËpás>axO—{Ü· q–“/]…>ß;LFÎôJ‚r¯s´à»ÝŒ÷G_Ø—~¹ –ýmªs@tí~ãšžû6Á™j¾÷ûEŠ3šÜÑ­FªÜ{ìÙ“û.&ì]j’DR\?}à}p]h::Øäc‡Ï`›ádâI#ªÑU÷ ›kžêÿÆécÕó@Vë& kÄØÃÎởo“íóàî2@ÍÐù]v,Ý ßüÅÊelîÔÑRp¹TöôÌ4—N³#Ö?ýkø»­î~—Ådù^(X¿Y^€ëZ òuPQšàpX/m¶€ë„Z³¶Áj0àt|°<ç @ë8Ÿ¸{ž9ð¹võùt?®ÿ×aøŽÔiCû5íTÁH«ºªŠþtœ`C!XˆÅþ¥ÇÈÒu{”oÞC ¦êÓ­yæb.Űÿ sÓWvᜉÊR¿jûaXï›N¦i”ˆñÇÐ@3 \Ë-—Jp38»3/'h$­Þ½7NƒõÛ¤,Ð:YNü·gïRMCLòÊ«ßv¬¾~ïœFÿ]ÿðCÿ‡ï9i|Õ¡1C211ñNeY'P¡åÝUîU-œŒèžç3 "t•ûÄçÎë'a;Žyí ìsï S•%™jA'¤ñu¤]ÔÔ‹®éÝ jB.îB:D. ÎCzGºžÐ¥…ï½ò!,gUèq¡/>ÙdØ ¢ö‘‹îw½7Zù_ÂUZò9YÔ©©-‡µ{&¾'§ w4¹ÔdBG¬ï 2n&3šºF4¹ÀØá³mÏq:Ûßï¾ó°óZÖ£kó€·öá©þÙç¡Çg˜£¨®Â¶8‰ù-rw­b‡nPœ³à)@•û3Ó\>®y¶÷¶ÝZ÷+|bõ(Ȳ—¾Z·©½ó Tú”&Û9RæLÌ û$XF£kjª¾Š,ÖÎÙzLÞd¶ØX’Ç›"PWµÏN+¬ccGgÊ«wʳÆËI Qóå2Ð Oos¥ 7­ÚÏÞíýcèsÞÇÁµÊÀAׂ:Ì_¡µþÎYYº “A)rÁ4ÜA…·4 lîª÷w/8ÿ|óÕ"X0ÄÐ@[5Јã0¹¬žòjÞžŽ¶m_sù¶&eO~Õ´òs˜¾=„'ü‡|Ëd¾ @êúEWimÞä ÷UNžß$ûrß“Ã'?EYªš\ÓTŠHÖ0Щö•&eëî;¼µ÷ú§o¹7awß®çzßú¸û3Ó\>úšö\ÿÇ+–@^¬{Éub ÿ>È;Û3Ë€¦mð€ª³}#%!¬P¯9rQ”„¿0-ZÔ…Û7û†Ô,(õÿÛîÞ°ÿ[tÓ†ž{-¬Îy¶ûá“=)Üg’·”2#-ú"[î;©Y—ĸhuÝIèBÒ’ÊUxÝFpó¼eÿq™z1ÊG_î‘MØOáäÎã,_j'~2Ó&;ÅÂRÂÎuàÉb©¯1Ö†Úª—!UB;·5©nyý<´ ì,ûhà´Òÿ3BÕÓ—½#¤¸ìüòßÇ„ÊEðÝmêŸo6…aq&Þ'ƒ3¯Çû¤±å´#ÊhäahÀ ÜvL‚É\ùK=o6™~ÿ½§jöû“FgŸkðή?ò'%aî…s¿‚<  «_Þñœ;p¹ÃéÒtÚ|ªÙT Ž®—Nµ€³ að¡&# ©9¤{ÿ¼)ºl>­Ï!Šßß?Þ,7a>Ë… ÂÓÅœè5úhO‡œû,ÖÅäyeÙ˜¾«Ìž2û@÷„õ†9Ãj_ˆecáƒNIIn˜¤åz­»Âõ˜±mh ½4@_S.†´Nì¼Ðu§#…ÁrŸüDY»O]Øâ1ëôä‘xçÜ+£Þiøv{Ô±3X50ùšªÿÀgV Ñà“{Öléñ#‘‹ÁZ\å2¸GµçÎzJÂW€}ÿŸ*¡Íþ ¬;€›ìa˜íæ±ÛMA1FI?Ñ·?ÛŽH„ ] zAOÂÙV«£óÀ!ípä¿.Ù¨üÇÉ·}®%”tDdZ­‘*·ÈöC'@‰Öa¦ó%³q7"üa}¹epXÑSA ÇE A¼!† ðGù[á×½H±™ÔÖU4¹4:2€û¼{öhÞ˜Ðäbc‡¡ ÐÀ·^¶Üd ³=¦‹bó‹ß}ò¢cƨÞk€‡@%¹‘”„5UöïÁÄwEI8hëNï´®h{À€…y¹G iëMatÎNq…q¹Ÿá˜Æ ZcB…ò*ï­Êv£ÿåXõ®z?ü°¹hyhþ´&ì&úXKk‚è«@¡öxÊ?^¿Tnƒ%± ÚyYo仾Ûô7§{ 'ž=rÓ´F³Õ'† hAäì>xü#¸à} t7qN¨œ9  û> WÒ bh 5QžÖÛö ]v¸_¾ýý¯ZßÓÿCimðPª-”UQNÿ>À¯¢Ýé JB‚íœIY—Ð HE9ÌÇOJEÑHC;H§ fŒqæDKò¿?s»ó?'’üü…4Z·]ÿ;OÄÆ¬ÉÃ]ÿzÝ~é‘y^¹SÞ9;Kn½vœò˜åLt÷¡toØ“+ß{t¾0”/yÈßøt+øvO6àôëv-³{Gô•T?¯e204Ðu5PZ~^ž\ŠwÊ’&!áõ]§& è^(cÞŽ>_Ó†e Ø%=}1'GÅó.`k;a¶'> ž±¼)€‡`µ7úwð/®ç½4=˜?}úwúnÞ\Ô‘·Ë÷E€oõF7›jI Ûá¼#ï·5y…¹Q@é4lv›—W*?ñ³à6fà ¹ÓFèÃÆÚЀ¡C5P‰ˆ”ôë>tâ#ñæ×ͨ”£Þ.c-”Þ©M©=&lì z Ð%‘Ñ+;›y¤3e•O$*êœ|›0€\kyøûÏ]n<ɪ3 ègÞ÷SaÁpzSJЧQ®ŸwdÙàû.' 8ÅjknÎqÄøuÓÀ( áÄË/¶V\áŒ<¶àêÑÊÜíT㯡C†ÀtT!Gó?W.&ÇÏ®Ãü÷¦:-–H’9Q*ïDŸ9`jÝüCÝÁ«rÉo?'ÿöœaý{µ{A#í€Þ©9ÁÛ=sœ)Ì«,s©©1ýüåçë6;w„à†ÀC°ÒXdPþ”„¯:Šoú:ü°Ù‘~Øú)®‡ßÀßÒ&NW‹†…÷&Ê5#D«¦ÅbÏBt2.) qÊD­çŽÌÛÈËЀ¡ÿ4`³Õ‚%i­r/9vz¥G¾nRè5SY»‡÷[€ Ýž™“üËÙ8;5À÷÷ž£'¥Oϲ ÁØÚ€“«þ•÷WËKÝ"½";]%ùùhýó(‡uÔÕʶ7~“£c‡ü\¸J;&AP¾ JÂ_v%¡ NWõÜd¶·€ºK$\7JJ›ˆp?Ïøï»Ja¯_zËԓÞ}OÍ8ÓЀ¡ÀkÀŽ@b›•¥ûÈ©O¥ªÆóÈzfêD€î;dä€[ÁÔ”øb)v=…V¸Ü„(—o|²IE¤ÕQ.ó|î³M{/ijéúØ·WŠŠâJÖ¯7>Ý7ÇK’Þ#×MW,atgùá>{æN•å›ö Rs¹L1@n_=ƒÂýþU*"æ¯ßú\Âðÿ?ž¿§ÓtbµÕÈkžpò×Ûmáu{6ÛŸÏÉ)j`£ÓJØúŒ ÞzÝuê•v›ýèƒ+” ›¹ÕÜSy£1¡ò|i¥ kÝž‘õ<åÝ÷UV×(¦—„˜N§lïŠê5îÉÐ@›4pîân9Ÿîƒ'–IyeÇ´Ò’F(Ð=jàm’ÛÇã9ÆÎ®«Z½Çí+c÷ÁhHh&OKÖpÇüÖf#zk´üô……òrôäyyú®ë”2^ÿd£ÄDGÈ7œ'벃ªw³üàÉÛÔ±KWÊäÍåá9^"Üßþl‹ …î”Qe (r:/wΞ$)‰ Ô¸¡á7¼(§ wÔgm’‚›/~óæѤ:£pmÌÓwÕFvæå¤$„OA½C ýºÜI“ÆvTyLÊÜ‘ ámàÚ5‚ë±á˜ (rø„1¯3õI=RŸý{%+”@¤i¤ahÀÐ@ë5PPtPÖíþ…¼òá5òêòÛ3àÿš€ï¤¸~2cÌóòÔ­«äÉ[WÈô1_7ÀwëU²W–bôòÐñ3’5¢¿Š1jPÙvÀ1™7uääå’B毡ýzJEU²b—WV#:t¡ÌÌ.ñ1‘rÕ¸!Bkù¥âºì¬áýeÎÔQ2ó’ú!6Å¡¼3 ó…®.ú€ÅogÉ—;>û¿o‹Ü+¥¥£= uV!Û¯aoƒò:ûRwJB‘ºo¢LOuD¹€ç0 ³^ìö$½Ùšµ|ƒTȌᮄèpø+›dבS2a¸}¯5:u½fב|‰ÁË9=)ÎðwUŒ±mh 5pæâNX'—Ëa¸—\)Ë÷˜sltšŒì &SÞ!i<žcìì^Øqè¸`¾—|¼n·|¶q¯r)©¨R."Ý£õ–5;©wûºGЦ†Žœ8¯˜¶^]¶Á©°˜¨H9]P,=óú`k‰‰ ª¹W{޽-›ü¯.\d“}‡gàÿIç¾Pß0xˆ×`§Qš¤Xû€7·ÚîßpgÏÛl±¨uZ´Yõì#\ûÀ̆—DˆWW‡Ÿú;‚!É ƒÒ•^ér¯uÞá…1244Ð4`Ýè© p È_.GN-—² Ï#z‘áñˆ˜{@÷í¥šçÓ˜îFͤÅ[¥ûÉ€ŒT90CKÿíÏ7OÈŒñCeþŒqòGL˜¤ëÉ W‘±CF«¨H¼ûÖƒ7*ë¶kF:Ê2}ƃQÈöóÙÖï;‹6Á£æN~|…s_WØNíwÍvÐ=t%¡ÝfsZÀa o5§šhõ¶(àmQ§aaa’.…6ùpínyþÞÙjè­ƒTÚe²©©µÊ’/wKLD˜ŒêŸæÔ1õmˆ¡C×ÙKNœÛÀý©¢¬¬v¾&e‘ C3çxߌIq³ ÚÀFÚ1þh ^.‘ãp#yæžÙðùî¯wËñ³AIx\ðÕÛÉ>érÍ„áa‘p°]Qe¦K¬ÚŸnÜ# çNŸxŒ…_÷˜Á™Ît¼mpÂ& 5(m t°éÛS¾v÷,y¬)?ã …Àzô3 ¢]ÓtÝŽŽUîyoÅ6yçó-ò«P ÜõœöØ.,>"ךּ_jjËTòñ±½dáì SÝÙuzšÆ Ó« íè JB;8"B8 oò‰†'c\wt @dQÀ;22/,‘‘’ ^^S-{s0áyùVôâ'–p7ÝyúKË7;-ÔÛÐôXé›–}::6Ô3õÝŒÔדr0jRWÍ\g24Ðm4@€p `›î%¹§×xäé¦2 †÷] ÃÀÓÝ7}j—´âu›J´ƒ\ÜefÖ0L®Ð}JVo?(ÿ š@úuW×ÔÊŸ>X+wSœ(ÿíÙ»£Óh`3É+?x¬Q²_¿wN£ÿ÷Í›&w^? ûLòÝ-„åû­•÷¡¬ŽQ#ºf-œý:=•«+ý1x¨ÍΠ$´[ÍxJ4œ@i-MÂt9«ÝZQU[ËÿN¨]PhùŽðŽŠÆ–ÊÊ(I©Å”& .—Ê×e>áÍ´Yú|Óm§–ïÉ2,#Ié“z¥~i÷ä‚RUSk¯«««ô´³î<3vè6`0cˆHI÷’ãç6à½TëñÞ“âú ãp1&RzT‘±3€°Áê}¹´BM¸Ì¿P$'àžró5A{ðö/ãŽòo¾á¢uÿœ7%=y„±³ bæ­¸áò§êÓ÷ð¢N”„ƒ³³÷y;¿­ûâ£Kk+C­ $Œ÷5=[­µ øJy#N—Ãp ðŽ…¯ZuUµTWW# s­¤YË%\ªä ÿ+‹×a·'ØQúɈ=]zô¾– ëGžoR ’5†”T“ M Cà‰ƒ.©Oê•úÕ.(îZ(.)·×V{qXu?Ùøoh ›h ¨ä¸äœY%9ù+%¿`^¯žc¤& S–nN¦LOÙM´cÜfgk`ü°~rþR‰|²~\ºR*Iñ±Êj} hCEèóý¶«å»|÷Nm܉•ûñ§œ÷G[A|nßÍ{ÎäNÿ>íýŽb¶/%a¿ÊÊ2ÍDj7‰ƒÓ¨eýä-­¬6U`“‰âàf sÁŽ}q±±ß5𫕺:+,MüèUJtu¥\®5Knþ4Éè‡óâ¢á¶Òýš2×”UJtIžïpÌ­L‹´JŸ¤HILˆ—øø8‰‹Uú¤^©_2ÍP߮º`TV”_Ä~vŽôâzš±Ý œ=âdô Ì&Œ í]Òçįü‚íxǬ’cgV ¸7é•2V¹–Œèw“ôHÔä´Sç.Éð*3àI1,”5õuχ«»Û€ªÿ8Ö¬Œ†/3ù¦ œæM#\BQ<ïæ¾%½RÆ…âíø]æî‡ZüVQè\Б”„¦ììÚœIYµ@~Œšf_¸0´hQM3ÚR îÂéSRR¾¹þÉÓÆ4|´èÁI˜ôÿ¦Å¶àÛjÕà[3¥TI>b=¬R‰ðC•6‹Ô!èÀ¥ª2Aî›)A9¤ñ3ïØÂ°˜ôÓ( ,ÝÑ p'&ÆK@xA8ôI½R¿žÜOX ¥2å;²I²·£xw i¤÷ª­­ƒ_æ!YƒÈsW¼X_ñˆ‹Rç’‘‰d¤NvžÍgª£¥¼¤ZNaX¼´²J>Z»Sñë§Œ’ë§ŒD›t06tt™\ócÈ÷Ü3kà˽JM¦¬®)q=ì²m’>i“ºo’˜H™Û”M‚Ö=ýr º;»î] ßi›êÿË]ªþg£î¯Ÿ<£ré´ŠéäŒ .ª·|«’¨;oÞ´Ñú;¹2ûަ$4Ùíe@hŠ‚ðôéÓ´‚y¹' [ûñâ}CÆe]Ú~ ¯¸5;­àŠ6€Zu)pNz%8¢TW)€QW'ñ˜Jë¸æ4uœÝ=~©/êEM`°æ‡,*’î;ÑÊòM𔘨¶é~BvOÖoj ua·Y­%û¶oÎÁ_"4.Î:ö!-h€mm‘Ön†t¦Oæðþ½dÁôц«T½î\¥òeñê²ÁCºiº²Š¶ Þ€¾t%·ÞµdÂ\oÇ;ijk Ùö¾4ose0hc£½Ç$8{Ì›a5/Wu?ߨûFõæZÿ®ÎFý»ÕUªþÝGå]hüér8ua‹|°ö)Œ::»Ý|³R ÞÅšv#JB»éYX¦ Ët»˜½`t†éÙÀÍuu±P¥7N-ó §ÀÝÅsgßÞo2= £®an5ÐŽ´Gˆ=Ö¾iA¢ß2Ý'ÈŒR +šò ‡•ÑjsXÈ%ŒÒ€¸þPxS_3}ç1rÝh+Ÿo¸Ðò­\PœÖoÏ“/YûsN›Nåû*ĸ‚Z4÷ŒJ¨lCœ ð¶ZmòŶòx×{¥$È37\kLvjȱÁ‰`ŒlË…“…—¬Û#ÿóîr÷œÉ2wÚh·³û×f«S>Ü9§W‚*ð tNxÍ€–í¡ÜþÁÀ8s„×sõU[È_ìpÔý]FÝk½¸®›Ôÿ—{äï­–»À´Ápè|ŸÒõ5pàøùdÓ?;'1wWðÍš6xkï( ÅÞ?ïDîí¸ÅÅíq›ÀÆj6%Ó®µX›ó×ÖT»ºu-~û®¯=÷,…É/=~«9"¼aš/a=,©­¼œ<¨è áÇ\^ÅÉ™à`íPÀ§»YÁµ^hѦ[‰Ãúí`¡» }ècb¢ÕB×÷ôq#]!êØ¤®dã§KbŒ.D\ÄYWºÞ°iˆ»´Õ›nßK(Ç éºÌIBÛå^žPúÏè¶ÏÝ3 t™Ùòþªí(º lt“ù m¹'²–äž…k &PæýÒ+?7 ™2Ò²`å¾ÁqæJšÌ l¬û÷AûiԽﵥê¡£þÙq±ab.êŸï)mdð=5ãÌPÑÀ–”µ;ê,n\L/¹÷ú׺<Û‰ó†Ý6 î¦Pÿë $œð'¸qü÷b·Ú¾U»p±›À–ï°>ÃØgQhM%°«)**(=²;û¿L§üôµeìOÝu]#W ÂЄ Üaáø7|« š55Ž šõ~âÚú­ÝV˜aWŽP47cd€>´t/a…Tƒ´‚sÂ%7÷ó¸·u¶°Xvn\ûZEE)êRªë  8îA4øfGp_N¾,[¿W°‡Lõp¶±Ë“ØñVú¼Ð¾È†õ8±Íîô+Í;»¬%«äÌÅl¯®%á±20®%Üt-a…m€n',;Á·Q÷þiеþ—¬Ù%=“dìо^ßWþ¥nœL ‹×ÊmÿOv}ÝY¬´¤á߯+®|çÎn¶að.Xáá¶W@Iø¾>¨ßö£$ô.×ê3Û….(͉vA!¸«Z¿|醸ää¿aû)tìÜ:Ó¤-ᘚ.àQSdðMÀaý¶* ¸~ 5o®¡~ŒºQ ¬v´€S7Ž¢áª£BЮ¬âÆêÏ]hù&øÞqè¸)÷àÞwmÀÌ(ÒÌ8–*¬YG´€Jð$ua[,+¯”÷xLP–oOçûš×G ÈñÿæòÍòòWïPÉæ¯h8ZQUNîuÍý¥Z—W6tÛJŒë[ïZ2Wú÷¼ @3—Û |±Ìt9â=Ò: èúQ‡ôµl4ç«4ŒŠ¶.Uãª`Ñ#Å.Yÿu%V—‰®]w]÷Š|f0Ö—v©µÀ»Tu:nÆ’Si:з ,X¦-Î6›¹% 8Måuäa#Ø+ÿô­WßžÿÃlƒœ»tÙ~ÿ¼«Ìî>ák/"eTÒ\oåsKð혨I0Dé.œ÷ê°j;üãù±²hSGjÀܛ՛>ßt;¡å;÷ÀÞV/Yô’cGŠp®Y7¬#í‚‚MC\5ÀöF·“jŒÂp"áPA>pãÃíÄUI~l³ã}ÇuãÇ?DæÍ«Ú¯§$Hxº0[nZºÏ_j.Ôƒµd0ü¹éÓM®î@ ÛËz¹¤BžÏ·6*ýî”N£úßq”zc£|ÝI]ñ^+ª.É¢5˹‹$ØrÈèwÊÍ3~Ùæ°N/”×åÚk¦ì®”„ð\x(úôïöݼ¹¨™Kü>ÔÈniÑ®¸²€#3¾ÈÏÞyýÝ)³o8g›:ãÙ_½þYÒ˜!}ìSF2’©xµ5œ€’îtGá¤K~µÕ[o½öûFBèꃢ¬àõÖpêFOÊÔû]o‰<ߤ$Û '\ÚêjKw¬[óöžÍëùVd=0¢×\\-à¬3C\4À6Æ —5ßœ¼åà ÅxAŸVCZ¯ê¬1kAÝÈ ™®íøJY¾òᦕûÄùˆÀfêY¢#“kÉ ÌÙ28c¶DG)’&Ï'·r¯n,+ËlÔ}+ér™®ÿ ¡>{Òp§A¿ï\N ¹M¶×o“ëvÈÝŒfçxÉúg¥¤üŒóªcŸ“ë&|×ù¿»o¼‹¶EI8iüNxgLÄ]SSA øÏy»õ4„*IäÑ’œçÑTM?púÓÚÊ1`Ëö5+7ܶõðŒ7ß :¼ñâµÇÁŸ99!Ú· wq‰¹ÿw?¿+ýwÿ(¹ÿ×÷ZU[kgÔQÙ~LVk]é‰Ã×mþüÓ5••åt_Áù\ÈÅ:aÝ°Ž ÷(Á]Öï:eýÎC@¨²ŠjÅêá~žñß åç%çÔi±„åÈÉ‚u’wfm³Œ%&“Eqs̸Naéˆl'Ϊ‘3Æø£Æ5Àú?rò<r ehÿÞʨŠ®(üq9 î{bÒA¸è²ä´h87<ª"äw–ÁìJÙIL›%aæRÏ®xÏ•é£T£Øz„ÖÛ·+äàã ÜGE…âif“ù·˜]þª*{;P6rAiÙœÅpµ‚Ó9™Ž~ʤ[^^b[ùþÛïáÿ'#²& 4xè¤ÁfóÜó1Ñ&{X„”FDž©3›  ñQuÖºêêÊÊ+•eeE'>|tß®¸”î%´rl„|3·é~¢ý¿»ø'wê§ð£ª¬ßU…Q‚HŒÐÓÏ”BëôÂË te—ôíë¯I=RŸÿøGˆäºFÀ°©þ»k‹¾Üƒ0r`ïëd@ï™pÿð¥ïïžJëþë6p ïtHÖ}suIWª‡NÊô±ƒ0úØÔèÑ:ù~•®ÿCÇÏJÿŒTåN§šï©tÞ™lÙ«·Ó\ÓJ+UéÌ \¥ òås©Æ»²z()«K”]¾Ü¹Xff •YG`®GÃ<¥î Ä ÞY-´òmwJB“É÷4Mk¶/âjWà¹óêû³÷\s.DFT¤ÙŠ—Ú›õøÿž>÷ËúÄõ5¾äÕÏÑšúæ¢ýïÙ‰!ÐÖ.'´||Öo(¡9q€/+&_Ö*÷Ûá( ù}‘Ÿ½ö¹d í# ÐÒ’ÔÁÍ%'¿à¾WK§üxÎéB铞,QŽÏÃÊmUÇãá›® x^® Rq~µµñ ¥Sïu˜ÁpúõœÖZ¹g!ìû@×Ë:t[·KÅe*º©¯ußR!—oÚ÷›£ê4¦Ù'=In½vœôì‘ÐÒ¥~o®.‹ÀWl9(÷?ßÚ´_™·p²ªÙ°W6ÂÕèî9“dô L¸•6ñšH—;lð.W¥ 7ÔÞ”„ÀÆtUP‚說œçÒK!PÔÿ «z„…%LŠ™ÍDJ+—bu‹¾¡?Dë–jd]ië787AxEýBÝó8ëÂàG–iå"fiE•$ ¼|{ÈÑSä³Íû;€ø¿þÉfynál‰ª·xßwÃdÇSÚ7ê–fB\4Qâ%5a‚tÍÂr½ôMŸ4¶t(.«ø×}"îý‰Ûf`bi‰¬ÚvHVn=$_Y0ÍMCmûÛ‘uÙš’òyºf!>cQ‘Ž@k´‚«°CÆ6± \ð¤R4‚py®)v®áB¦¿-Ý( fŒ†Ïÿˆ)s=§ú{ úuØì´'%¡IìuDx¼†üiKð¹nV|¿oæõ=#“jñr«²Ù –^¾òqýy€ë5vâA ÕÒ`ý&§{‰êä`MNWþç~'øÖ×bÓW pòo]='}-˜Pbüiö ©<~N6ïÍ“Q{aXöè¸Âdú¸ÁrÕ˜râÜ%ùpõ.)¯ª‘Ÿ½ú™ ï×Sîœ=A ŠJåƒ5;å\áIIŠ•;fM@¤FõŽƒ'dÛrîÒ‰<»ªs&ؘHL¾=!ÓÆ R‚F¢¾àê1“-Çò leÈPû\Uþ¶l“TÕÔÉ_–¬‡ €Y^zt¾,ýr¯wÏ™¨n`ב|ù %eUÒ·g2"N”Ä8å¦òsXùo™9NV#Ì8]ÆΔùøÈFEøÖ?Äy=“&É7|OñþGøx]ƒfÛwK·jè(Â)d2":—JÔý²õûPµ*ê•#ŸnØ q¹¼ôØ|ñVùp½ôE»Ð#-l7[¶Qî¿qŠì:œß¨. ò7ïËSõ3 mÏU¼µ7‚NOåq½¶µÛÔ)'7€óYÓQŽ[›^{^ç߈‚ú!žK#“oÚæ„Ûç:‚0}ºq¿2j\?e”0¶ £¾Ýmógö Ò|^ÆÑNÐ@{RÚÅ Ðæ0˜âñ·-èiª;&ât‘˜û8N„î'§ªj_-¯«;‹ãÝzÁ¦!-h€úÕ u«­àÛìøè5·y̰|C Þ„[.´€“ “,(m5à§•»²ºVnž9V8¯rÖ°¾˜x#2S:½XîÃ0m x‘)¬Þ‰ Káòä3e @Ó’µ»ä›÷ÏQ`mñÚÝòÐü© xýá½µ2 zÊè²ûh>øw @vE²†÷Åo†„XgÂÅá*øúÖâ~Þüt+€t,Tý„ù3L<'&ÇǨ|iéç½SNž/ÂDÉí2yÔÖ7‡£°dm’~èåïJZ¾Å#×OU îKÖî‘~ƒGôS×ûòc2‡)=kËÇX—ÇAêЇ/÷ÓšsX÷ô‡µ(…zýôÜ?#EŒØl=ôï"ÛÑ!Ó|_îÕYì׳‡¬CgO×幋ÅÊÒ>é (¢ï²«xkožÊãz][·íìèª(Ǥ™uÐzŸÁìzÔú;n|¥Àë£Kþ°}÷¦œv $ÄwÒù•7Ù¤-m‰`‘€Ð–a~Žº 5vyõ­Ó…§±Ÿ_ýõÐkì2¤ hε^ˆhô¢÷5“„qHk€ À± ¡ì‚.S)Uoíš“í(±ÑpÐ2îIr11’ù¿·2Ûy˜~•ç¾iÙŒ†«Æ'öKoèãg/Á—øjçyÜpÌY‹6]\Nž¿$ ±ÑÊU$iù"œ¨×+ÕQfžŸQ¿Í ©ÄÀûÀš®….0ÞîIŸÓd­tÍþMpY?uyP¬€t¾šÜ7v˜1:qÍ„a2®I‰¨W}Zš«ñÓ5h?¸ÿ9Iø<Ü’î©ï0éë¹>uþ2:\ u•é²Ý\{Óܵ<®é¶uµ®ž1êYë<˜,à,ß«4ˆíþ™Œ@L­­s× Lv“9ÓF‰¹|³Îƒ©Þ[{Í]×ÐÔ\ºÆ± Ó@{PšÌð×F*¶õ–s§N*uÕ7Ùñà!1;lê¿AšôQÖb€o­ ßÖ„ Z À­5Ñʵ´€ñ[%¼ÞÅ ¥tè#Nyî'tqº›äÁu„´'n¿ZY>]Ï s íÍ]R¾óÈ|ÅtòǾt=µÙíÔÄXeQÕ'“@G$@[‹v›Ðÿý]«† ÝB¿þæÝÒù m ¥3ý?ÎNؽ̈́´w©Ã–ê! îD›÷æH"܈Ғã w/G8²œrî¦Ï¾–æÚ›®×òèë²Fà3¦u4˜Ÿ}ºÈ¬ß}ÔĽê L;1RsÕ˜Šù†Q9bÓÕ]Qô°~Ôh$Ì %!ŒÈ…,#^mýóNäÞÞÖò Â¥¡ÖšoâÝ«AöÇ#·í9ŠƒÚb˵v›0Ö¾éÂUwmG‹ 5Õm·4(Ð ¤½‘ÐD¿ßbXØèЯWв’sò#ÝNèVrøä•=˲io®LÙOÆëãôÝöV6­¸˜(ei¢:ÝG´¤b2%­Nyg •o¹û}ëßS±TlØ“«èâèSLW–/âžo ÓnkZÁP¶–ê!kX&ê§5O)ß~O÷<¸OšjKœÀKê¿mû;Ok®½9Oj§ ¾¨¨ã`гû-²L´~3X'!“Ù#”eÞ9³ÎꑓΩWÆX¨Õ+õŒm º2x µÄi‘’C;ÒE´[mßÐÛ­]Û휄é µÉ~ræÌdäÇÊRý0(é.€Ñ‚|âÿï•dÙº=xdècW¼Ýáÿ­•ÿüûrùÝ»«ÅêŇzúØÁBãýÓRð>’kC °0˲u{åßþïDüt>âê”a`d¹õÚñ²vÇù¯×VÈYLä{pþ4U>†±n ´Tt=á9g0™ÓHìß»‡âü^ôÅNùÍÛ_`¾AššøÉÒ“Þ[{kÿ» ÞÜ* „~ªâùnMiÉûþÝß/ÆÄj‡Ï½k?õsuŒ¾ZÒ†ÆîÖ ;й½¾ã!©=DabYÈ‚ÃX 1Ù¹=îÅŸ4ÛšüÉÈ8·ó5hJÂÆ4„­ž„©SWYúU`‰Xþ˜ß3 {÷šÎטQC ÂóÒ#óœê$Û-d&ùù wé¿Êw÷ù{g)+t"ÆQ8Tûí‡oTÔtü¯©ý…Œ%t=!'Vþò•rìTÊÃ5^ÇÉ’ß}tžú˜ê`;ܯåöëÆ+~^>‰tiáÄOW™9~0&Y:¹šæ^Órîz¼æ±[¦»^ê÷v ôîw¦^.hϲuÆ[X{Oze½Õƒ.>9ÅÝŵ.I{ÈIÕ˜w@wùÏš4Ìy‰·öÆvá^Ï΋Úa£=õîoqõ$\Ž0Â%ça´VØÙe¨ú»fg©Q'¦“á²bFò5ÍŽŠà)>€¯eôå<„‰£sŒ±*<ð¾Ü[sç¼9ít±c¤$Ì™8þÜÖ}¼5 ¦Ñ þ4·[#ðopº ÀÐê¶dŸ5+,·ôòóº vÃú­Ua¬ƒH KÇŒ,ë~‚(‰[mVÙŸë°ÎÑ÷·9ñ¾õùdSiI\ÁwKçús¼"÷ ì}úß%6Ì$Q s#ïð'©€žËèœUm(GôƒŠ‘wK䨬€¦ßÚÄÚZ-ñ™{Йâ@IDATjo­-«¯×5L>Ô²Ì5c¼ÊUJÊ«ä=ÎšŠ‰Öc4Ь6[ᯯã$ÄGË}s'Ë ŒJQ8!zÌLyöžkeôà Љ¡à)tyá$é;Ä«sø®Ð¢ã<|Ó4¹Ño÷ †ÀþœÓ‘=ŠÆQæEi.¦?kvjkm*Æ‚;¼?é„Ò¹¥Ú @YIIˆÄL /”èêÚʧZ›,(³œa° 6ŒEû› Ýþ-} ‚ûü¯iÑ"ÇÛBï4Ö† xÕ@ÜOnƒ_ö ÷Í–œ#Λª¨ ½^`04`h UÐ.(ÚjܪDê/²ÖÙ”þ~¸¡0=ò¶3ºîÀŒÔFÉ>qN¹˜’!IÛÃÍvF¹HSÉxafåžÖ³G‚º.1.Z¹'õÀ„ê‘z«¹ ä|ç„nºÎÜ„À_ã†fÊÝsüï®™1>çô@‡^Ç`D\×øtM¢xËÇ5=¿·¡‡:耜`æ÷û¾¼\Ðj«¥—ôŒÝ! WJBðwݾpá¯zÉ,â—ÂåÄ”¬4•Ãú]#¯øUãdC†º”b’±/>- ñq ?Û`¡"# à±’R0†,ÝØ¥tnÜLËÐpN|bnù‚Î`zÃúö„Õת(D÷瓱°~[Ü|®Žã ¬à¿wmCŠš+¬asëðñóà(?,…ÅepAQÓª˜%ï;%35Q­éw ZQ-þÆð–N¯5kj•1 ( l8]•Üà­i%!~ ) s/œû%šwš %áboËdxýEàînU[‚ÛÙ‹:_»˜Þè½~—oS²õEÆÚЀ¡C† t€œ <@y…ÁzÍ KŒVz0ï¬Ü?oJ“”##Ôµ™§[ö –Âÿ{hß4¸ \'eðÿÉÿ}ª.Ó‘m‹@oš‰¹%¹eöZü‰Ð\>:½Ö¬©_cî9Z×­I'T®1\PB¥¦XÎ@Qâqº  Cî· Êñ«²à¶îÔ·n¶3ðŽ!† ´AœIÊ0o,Ü¿ÞÊgìo› /—Iè*= Y&¦žÜò†´N†úko?xSd¨ ÝeôÀÞŠÙˆÏ-ŸíRD²=qö¢:Í=^@¬Çe8N÷¸r|™}Ô™\¯”å·½¸è޲~wŽryÑ'ø ˆ¼å£ÓkíZï@ë¹µåiÏëZeµlÏiwŒAIÐ]ëò*ò»-Ùjí/à]fácˆiEÿí»tÌݹ i€œÞÒÄÅEEÊPùçú‰ê¿§» 5ÙÛŸo—õV SW£ÓÈý;mÌ@EGØè€ñ'è4@Îw^¢Ðe wj‚Ü~]–Ly“•Û*ëæÃ75¦äù a¿bËAå{LúhY±ù âç§;Æu‡âùMÆ Øvऊ0¼/!õäu ’\ àÍŽÖUxÎûöLv¦9é,ýrì>–¯Ú€öçæ ŒðîÊ*>@Ÿôdà"ÜX(®ñ>Âõ?zê–fóQ?-jÀoÐÔbŠÆ !¡@Pšá‚ÂP‹q¿,à…W_¥ªü)u%®7›Í¿V ?† 4Ñ'VöHŒ‘“ðãdž¡}Ó½X¡éð§{zßM6v½H#wõø!B0´|Óù|óy´~õûÀ`¡Ì©AgÁUÀw‡ ›Ú?2âÑ”pË<ÓìèÂâ‘Çou2YWÎwríÿû3·;ó!­¤;ÏúìÉÃ…KYEµâÞ'c …—ÜãÌ™2B®›0LyN t•©£Èäý¥–rwúIã4—kžmݤÞÛZ–@_oð@k4„Ò#%!&@*Npt¼IIøÝ¾›7ùz 6».(޹›ÊãW[*©*N^õfÓ¡Û²?÷Øý÷µ0Æy†º°RÁé™–kV SáãáðÃ5»¥wZ‚ [¿uÿ yìÖéʽ`)@ú·¿r#,kæf¹Ï^‘5ˆjy VsZË-ðIÍHMRÁwèÆðÁšrç¤`2× .ë߫լ¥]¸vÚ÷Öâb¢`ùNTKþ…"Ùqè”ÊÀäç°ÓÒýé†}`¸(——› ç^åG{÷œ‰ê¼•[Éæ}y*pG>\…aè·8!ç@m‹Ñ0dÉœÉ#d (ï;úã£à9'N²Û­×¨ôM¦Ú0±üÁ—¼Œs ›¢ÂJJxt;¢|ŒzI?ì“ç¦jxb:Ñei‰ûwö”áòÚÇ[„–ðk1qk¢ìQhi¥< ÷“ÌôЦv¶ã]ú¥;…´c6mN:VåSWÚœŽ? °“E°½ n$ GN¦ J¸ÅÅˆë– ë?ûÈ)ç^RÍi¡?1}½óP÷œøÄíW ;s¸§uzX—”UIZBÛØYÐq4œœ5Ë Ð9Jw‹M¢áòبxõ§àvòª)Üô·[vçã`4ÎIjZjt…ñÇÐ@à5Ž!Ý]¤$„ ÌaαÛgÁj0Ö„›XDǧ±B“Éö¢N^ßïÈÎ>§ÿkCÁ®Zãô…‰NeUURYÝþœÊ…—KåtÁeÙ‚ÉtéÎÕªÙœÎZâþݸ;W.ºš¬i0߯WŠÄFG‚Êìr;©A´¾Ãð3oO¡Ëi2À°-VÏö,#ÓfÙXF–µ#ê¾ÌtÿÙðãœ0K ž–ïÓmê¾ß C¾ £Z8± £“GöSî+ÉèÜiiŽ{ZŸ赪Éøgh 4`ðNPz0fÙJB³Ùjh½˜œ[zûº¶Êþ<¾°j²&^¯ë`ýÞé~ŽñßÐ@0j@/‚ORû™1ìŸe˜)Nµ§û‡;'°«~¹¹i°•¬a}…‹oÜ¿ÝöäÊ÷/I 1ÊÇøO·ÂÅá¤à Òñí‡o”ª‡›Yü…ÛSÈðŽNF¼ùZçí™§?iëò° °Œ,+ËL:Èö’—ÀNâMXOmõMXÐQäMúÓU…t³¨…òöŠB«7]OhU/*)—_¾±””ÊÉ÷´·ò´u?uW˜ðÝ󣞵ÎÝÓ¾%9yðÂÔäÇGEß”`1'™Ðy€/·ó4lÙíæ·,ûÿ ܾ{·ó@o,ß´_±Ý°˜ ¼ÔÏÿ­×Ž“ž=ècHWÓ€á‚ÒÕj´•÷ÓJB“­a¶ ^ÍZÀÁ1Ž¡@û×tñð0¬ßZÆ:$4@«· †Kü¿Ã¤gŒEQ‡m0‹âþÈrÛâòJ妶ü œ´×Ø›À»½Á7õwääyÙ'Å©cê;ØÄµ °¬,s°×=uH€Í6à.æô»¦OùÎçèM缫Ðò®'|ºîä¶®ÿþ©±êÙÒÏ™§6TY1ðž”ä?¦†…?h»s‚îÎÀ¾×Þ?(=#cÈÎ]/„øÖúdùœÞýñˆ\Z¡hFõ1cݵ4Ðôܵîϸ¿4@JBû?x‰ÉAIø+Ó¢E^-ÛV‹Í\ï"˜eÞ,¯©©z#¦Ž(&Sî€[ïX*Ù!c˜ðK‹ÆÉ]OÚçßa%\ú$GIAy¹|V’çï­†ùCåîGÁÍ€/¿ØvX.—–KB\Œ,¸z´¢ìÈ{ ù’/wKLd˜ŒChmvl¨g­óŽ,Ksyéòè6À²Ê¿¨ÊþÜÂЪ{Þç‚é£dÝ®còΊíR:ž°êí®kan À›ÓI Žéú 7˰މêÙj® „ÙlQ®ùâÃsÂf2ÿ#Êþ·ÌíÛ9¡2¤…£äÝçB®öeë÷©Q(N–ÝŽ IÓÆ Ú‰ Ó´Žsä€s5Ø‘bØù»@9©ëðX‹– tüt¬“1ÊE6ºy ²uñ×íäì‰5åØ!™2wêHñ¶?¤…7xTB°Á’07÷èm(Û‡ÞÊg¶š-VL1Wb²{êðU4abÌ7Ò1ýÎôòËÍö†s-CÁ¡Zà ¼#1< è}â+$ºE«v€pZpÖÇRÐA»$øxIÀO[´*[. ØÌ5#{K ¬­Ô/õìÉbðÌýLе °¬“§ËúCçP÷ÙNZH?“ì´ÓɨsÛµã;-1ŸÅñ™ñêyâ³ÕR€»Iõ«mÕéZë«?(.þìâÅ‹HÏë7Hçjëü‚b5*N)låœ.@p¥+’·'W"W?;P“b~(E¿ÜyTþ†ÀLÿüÐ ŠFôïË6©Ñ»fgI98þ5+’ ²*ÙâÄnÙúæýsäK{ºTR¦è(/bâ7;BoûCMŸÁV^€[tbyÜ) ñäÅñ ÀM.p`l¯€:wÊÄù`Á[ƒéJ||âß:ñ6¬ 4§Îá²[í6|¯ËsÜ^»DEFJTt”DcIOˆ–òš2Ù›sFdùVY8wrHY›SF{£å“ÀuoÎi•™6–J¯Ô/­ÌžÜÚ³<¾¤íÞXæ‹Ï{傺ŸdÔ½/ŠÄ9Žúß¡ž›É’™š ž)>[ÞÚ@uM­½ÌlÎÿñÉüoœ¨©ÉE2—°xýöøXŸOÓ£ íÉ_Ž(¦ï`Òu~A‘\,.WhÝ¥Îî¾~´sŽãÄ"PÖÂ9Õ=Ä`û¯K6€¦ô’€ZXEÄ}>þÃúõtÞ£²Å¹:ÈÖ_>\¯ÜÐÌpžg¬FXÍÞÏy·ýΰAx Œ®¾ÿÔwW€wåÚmŽ‘’°ºÆôºÏaX%áàìì}ž’ª³ÙNšxN¼¾ê÷E=3¾âM_»¶ÌSzÆ>C¬ÝLÅVk-(¾RÎÿÎ/?†ááá xÇÆbâbUµTWWKŸºZÌ‚¨T`¢–£;®ËNb4ijèóKðpár‰ ë+c¤ õÉ õ«]P<_Ýy{=µ–½$÷åž–ÜQ÷-×ëŸn[°|÷M´È^ ‡úo© \.)·Ùì9ßån¹8Ÿ]·ýûë Û›ÞŒéU×À=l$xßc]™ÅÉÕÏ+¯{¯T§¼dÔo_ˆÖ¢ùâõÿæ‚lÍ™2Bª`i†„Þ©‰û•[‹·ý:Í@¬éBî|Ý:“tÕ»sgÙ0xèW$[¬k«uÝöûî@IxþðÄñ‹š] ¢ˆÑuÄ9yÒ5ÁJÄ€ˆh Â"obßĉ£ëlu7ð:[ZYmªÀŽ› ž̰pÂŽ}q±±ß5°äÕ€Y%4~%U T2Z%C†O€iÄ€žŠÑÀ‘r÷ý%Ï3©æÈsœâ‘`1›'ƒÑQ‰‹Uú¤^©_ F›^eí¢hom`tÿ4‰#Α e*R©Q÷MÕï^ÿ“ N6KX¾ãããZl|ùLVU”6M½c÷¸òÀ“­$Bþý{1’âM螥%51VöÃÈ[½—ª-Nœ%­$¥°¸LQ{ª?øi)ÈÖ=Ý3Auú>¨+ßúl›|÷Ñyp ŠOûušm]kø´˜Æ¶¦ì×<Øk¨iùà~ò;/O6™Mó½û‹ÙÔAm|‹ÌÐ4½&{þPS“RV¢öÃè÷Ø×æÞ1Úf±è—Îóÿ\]”RîxàkÃÂÆ>=ÿî5΃õo”• ®q*©(ºŸð§oXè~Z«þÛÄ^†q¶s€Ý'˜V¾ö›Ÿf#!v¸€¼UZ5.bÛ9ŸrcBJÊ7÷Á­„“ž´Ð~‘ôÿ¦µ®àÛ .NÀ7…Çã+ª¤ Â&yùÐä(j€eØ ôÇO§×ÖZ.¥® Šýu¯> ”– ÉÉñ’€… ú¤^©ß`t?Ñuå­ DH‚_m^a‰‡¯.;ݽî©3÷úÓ ôDlÊ>IŽWb¢om€Ï"ç:zx/’Õïw®;TØ ëß+¹Ãxà›»¹aèä¯ß£èDG¢£¿nç1å’Â@Z|'‘ýf-‚7ÝtõXŨB—× [·Î'±˜l™wö¢Œ@Z‡ä+9!VÑ2ˆÛ0ÅÛþæÊæÏ1ÍßÔ®Ô¯^üI#Ï5xèÔšé«_}9ÚÚÃô¾_ß²›Í½ð]·ÇTWÛâ«+MuµNw€ÜRƒñÉÒ§ärS²aIýyk]R¿Ë¯Ñ»­ëω©«IÃ9i޵áOMX¸­42Ú^i6‡Åüø«ßûñùêÚÚÿ9¹cÿÖ®]¢'åh0Þ†œŒK»‘Ô‡ýËO><0tü„KÛäõw> N (€"}, ¾õ ˜ÁE%Œ“4«¤†r˪­RVk‚•~•E8×™T·Q)Ù9²œe’äè0IŽ‹ØŽVVO‚ï¤ÄDµM÷²Ë«õ[WXKm "Â"}Iíˆ!õË•uR]W)Å—+ˆt »®¾pÚ™adÏ>ÎíŽÚ@ ‡ooj„]#-m¯¿ÿmÏ¢Ýfµ–ìß±…¾ßú½®x‡ÜކéJƒˆíÍßÒMÑ·ûVL¤%È^f2<8hCÐîÞ&ÃÕgøÝWÀ¿Ú¬& ÷†›Š· [‡NœW#TdÅICçxÁÕcT¼ío©|¾ç¨c*$D‡«Î7 x·á÷µ…tîyæ'¾û¯ó0ã¯Þ/^°:sJž5EÕÖ6ŒGun;#wÕé¨ÂunZ†Ìì×3/µçO†OŸð\ï1ƒ¿þöÿüj EË=gÇwÒç¯3ÔbäÙJ è9×Ö³gÞÝo2}ýØ©ó2´_/g’hGÚ#ÄËSéžÂ=`HA[ŒÄ$²J„)§ox @xŠÍa!×C¾D0tf¢*N£¾,fúÎcäº!ÐVþ¾°~Òò­ÜœÖïàœ|é^ ¾´èè*IA¨E°vbÈÿðgñû>ù]çv{o² ðÜŸsÚt*çÈJ”›ïs½¸>³ízK|;žu³ ÏL–]yø@Í÷X0cŒpñ$î¶ô9táRU]«"¤êý\œ¡–òÊe cðjˆ· [w‚-…KF­4ˆçùÞöóX[ÅÁA2ãcÁ䬥@LmÍ3X®7x°Ô„çrð+f~êû?þ–Édþ¯¥Wì7Ø%}/_t|Ý<_Óíö¢"£ÏžäbÊON•£'¦Û“>xøŸ^ú××ÿûg¿ƒBèS‡E[MºŽŒöYl#êãþå’Ýóì ÷½ýÙ–ä—¿ÕÁ±óz!# ¤ðÃìø_OOw“*ðªzðÅ zVX”8$ÜÀw½ŠœzáÇ”n%Ô—f¡» }ècàóÉ…®'×àZÁ·VàûÉï½ü"À÷/Fž;m_°o‡9ÜÊ÷Ž!Þ4€Î‰<²yµeùØÉöC½ûüä¡o~Ûòæoñ[œ_…Öp„{Sž±_[Óøa¯+.¾Xv0{Û¯MS®ú·×–m°?u×uÎŽ¯j œÛä0މŽVà[MÐÄüNÐÄÐy#®ÝVº¢Êi!¤h™VøQ¥{ õCN+8'\xs?ócËkBEôý{(ŽhèÔ¤¥¦tˆzu5jµ¥ ðÙ;[X,Ùë×¾YQɃŠ6ªðYÕÏm[îM§¡×MÒâ½p!8$Pd €ñý“eKNaHòÀ7¹ÁNØ¡9àÄÛñ`€³ðú÷AsÁ¸àÓ˜:ï„;nœ¥Àë#˜þ™ý—ïÏ7›-?'ø¾m÷–Ðù2u²ÙIqèë*‚ðÝñø×Ž-ùûŸ–×K»¤tr)ìƒTì ±—Ë|õ¦ŸlIè‘ü:¶•ÅbäÖ™&m ×c 2øa&…A&ÝjjÈŽBë·UYÀAÁ©,àÝÁ ®Á }9iýÔ …XV‚îpew0ž„ª¥+Ú@QdïÙ+`SpðH4/mm´Ž|ï8tÜ”³ϲݛ¢ÑaH¡1…‹ámµ€Ðù$®˜øœ÷MK|E=¼Ýˆà“Qq¨[‚oÆNH‹¬“d°¶Ð=rê•ï ƒqù\w>­CO3x‡ªÛçÌÌ <ù—¸Ðòí󕯉N @o¦Â¸»©gï_÷ë7lë©SG´A÷žÛúÒvæclt) °]ЪÆ{–ÊÏÞycñ äøîýç.]¶ß?ï*³»O¸„#R&@%ýÀð†Ë‰|;&jj¦”îÀ¡¯z«¶Ã?^ +ÃÊM©mú{†˜Õ›÷äIô}(€dm !Þµ¤ôHÖ›í¾æ3AqèÆ¿6@ŸoºÐò}lÿî×~ôÁHJ=XÓ Îm>£|Vþ.¯³ÖUºâB>ê^Øv µ;Õ°Œ$ņD–#µÔ¼hxF?M ¯‘ôº¦9‚š1¸Ys˜ªˆ ïQwøæ3 ⣾Êá[Ë’>zà 6³¹7}¾ ·“ÖUõvãæ·®šÕóª›núÚ©WŽþ)ñeÍEñÖ%n\ÕU5ÀvÁ:GJø¡çË>jÅ¢·–L¼fVmƵÿêõÏÇ écŸ2ziìLÅ®-¡t§ •—“.µß7A·Þzt»¬hðŵ^¨=)SïëJ à=œ[ˆ»„Nzí(¡>(º®¹n® ç› –l'œpi­«-Û¾vÕûû¶nÚdô³È n|&ù_fÊ ª¿ öÚªêËÓÅ= E˜‹åx¸ÔTcqç€gŸ°Wx•$‡îhdEò%S11YëêȨ:o\ÐþgðV¸ÙÑ\+Ú ƒÀvbL¸ôAcÍœBŸpê17)å œög,z¶t6sµq¨j@pm矑x"v®_»ípöŽœ«æÝt£ÕZ7 ! ÂiOJŒ5E…»Ìv«Wœ;Øvÿß•õ«˜¾G÷ÿzW_»×¹ûÿö¾ÿY˜à¦å•¾Ô›²v¯s÷ÿ,-Í» ²Ý(àwèà†-+>ÝXUUA:Y.|t‚k.®ð@€1'øFÚ6ûqÄ…ýõÜs0® Ìõˆ +•ÓÅÕF * âÎO–$sµ$‡ƒ£<2LMÀædl1ÕÖT3ò®/•O¨þ<¸jŽoJ˧_˜ SAOR Òv @&Ц^3ÿ¶)ë?[º)jˉ¶„·=#…®¤¶ vÔègJkÝOHbÆ$0Yýá{cû‹¡c³ö:|dt\|rdttBXX¸#d&ÒXf›Í_Si»¢’© ‹¸X´`‚3ÇÕÖ¤YlÖtÙRQQô ¦“ ™õõ&¨ »kÿ‘ãzÛÓÚd³…EY­±á6k¼ÙnóD«a¯…>kÌ–²Z‹¥2:­«CµŠÊÒŠ²’â“GÍ9°'eÓn`´tp_©_!ŽÏ$ŸM>£|VÛ*ÌqÍ|­§rŽîÊ8ø~÷@\ÌÈ1Âá=ç<¤ƒ©´Š1à'zÀ¢ªr§É¶+OÀ¦~\…\î<ëh‹U@EDh:Rÿ1œ9½y¸Ö™k–!µmðàª.ÀcâãoÀ†<ßÁU¼Ð, ôˆ©`bOËì3 wÀh™ÚzÂ8dC ¸j@ˆiçˆA£~58¯=¶o÷,y8Æ÷(Ïã¢Ïæ!Z§¥Þ—Þÿ«írùµó~Sa³±#0I‹½·G'â,'ø>S]»õÝ¢¢%uÚ?`¹…NBÿ|súÏuißùÿ¢·õ:3ý§ÆÅ9¥GXXìg;n$¨«óyUÕÛ·•–íºXWGhá3ÇgËù|a›ïiæÅŽxqý6A¹öÿÔû›ù*ðuݾm›ŽNsc1Üa]qáXƒ› ÆQ% ˜ŒÍù l~Úit'ᨇÃõÈA3H÷¼ÖbÂçúÒš¥ï3*ëŠuÒbðàª>ÇÇÞlîÏ—Ý<ÈNÀj†<áЧ=<"2‰Fa!)*_îÔwÈ?ĸC¯ hiÓ Ú˜s4Z½rlkð¢¯Á®î-3â† ŽŠœ¨µ°­¤ô]:‚©€É ¨¨”{Sz<e6¥éDUU/óâÅOôc­4àÏ£bb2¼¯ê>nâMœÃëìRy®¦&{WyÅæåå'ÛI@óyãB`ÅŽŸ/mý¦œ–oq–?Öo$ç´¦2oeîkòss>Cßä~÷@\8Vo·ˆ/Á¸:;ËÛ¢Ýô¼¶ãÒ˜.ž?ÿ&î…uĺbÛÑ훡'®:ãÇ;Ìd6õŽ«(×òà*aˆ–&®ªÂd KGñ•?/õŒÅÐqˆÖg›€@[iù¢×ÿ Ø#g‡Žœí‰–r¶)|C ”x‹%ü–ää…Qõò ë¬[×^)árÀdz||ÿ;S’_D$Õ'jhüû›…W,“.’PjxxõM=¦‰Œ™g6 r¿-4\û›íà¡ÊÊ/?-*ÞVjµêöï~j ÿëgK`æÉ狜€›\û‚óÙãq>‹¦§Á7ó¨ÞøÉÒOú>ÿâ|°±$¸âbÆ´êÒšKÑV^MCiãj¤—@b»­hýG¾Ž„ÙAb]º ¨2wä£õtdŽF^Þ4À6‹ÙdŠ‹´ÖqošjÅ~êÓÁ¡iZ¿5XÒ:é^t+Ôa\â›40àßu›h‚o×;ïöÏð?gö~4#<¼7ôįeÅ«‹ÈDTÄÿ¯¤¦f]Ÿ”ðCôzn'&SͦÒÒÿ|½ðâ¦@¤ßÕÒøÅ€~ÿ?ù&sÐÀ ÎÔÔ¬X^|ùó­%å:è¾õ{W?[€`ñÓ œkþw^úZìˆÌ1æ¡ò-//)Ù½yý_Í×Ìþ'÷@\̱tÁ¸¨W©ïo;uˆ@LÇöíù‹Ïq$„uĺ2¸«Òí6k€Ö3 >õÝþÃÝfMzLÀN½jð­Á’Ç3†ê5À=_ö® €œ€€–o×Û”îÔuûgøáž=LŽ]¨‘WUõë=ee¡£€ÈÏöŸ?&*ê?ëd/ù²¤ü…?Ÿ/Ø B<‘[{ôè}K¤ÛdÇͤ‡…9Á7tM‰Õºú`Uå’_Ÿ+ØR^[Û€†Ï•^øœqÑ–h>g[\siOÐÅ20}æ£-ïQÙëVoKNK_„}÷¹â¾zp‹h’†R[Á`\õ¤x%¶&—k ¦óù'þ¸vÙâ5õu£Ûë,¤Å°€Oõ9?ܤb žbu’`ö9õJä”BþAî:µ´wBpÂvµ®9ݦ؉¦tûgø‰´Š4™ØAвïúÑù‚W°·xݧz‡‡½Œ Qz¶ÚåܺÒ+ýâÌ™#̯»JVRRÄwÒRæõŽŒxn?×@?hôàpH28êkžÿR]Ý;¯—-þðÜ9NnìLÑï^ý|i®¸þ¯·WYYæ¥-àTšÝZõÁ;ͽû~¶³{<âÂ~åŽâ°ˆÁ¸¨Jƒ¸Ûøè¢s¢+¡ù`\®˜ÎžÌûó'oüýU$É:áè„¶€ë¶ƒ]¡)®zãCÎÅöÓ’Ö³¡ëöÓsWLY¤5§%†íÉS›êÖmkÃøQ_I0Y¦«F`2ÕåÔÕ`²›ŸÀµñî·Fh³›Í_MÞ |oÕÛA´æó¤E?[úG®™·¶‚³S­GµXqm:œ}`Æ‚[æÛ¬Ö¹Ä•Ùl .^èxÉý?ÏéªBí*îÿyÌ=“Íf½ræxÞ²Ÿ,[TZŠHzíK?éZ¼ß6x‡«¼ëdhé›!áS&ð5/Õ+Öˆ½‚®sÁ,†kO0×N—­3ACЩ-52ì¿€:RØ-íëd´X~„BØ´ZÎßxclÙÅ ï#ùj<›)™LYâ“v-‡¥»…Ø_~Ù|üã%sl6û“xñÞ!vÜ$Ðü\ŽIò`´ýG´9ì™Û·ço¿9íSK›êA'Ò…×®Vp©?Ì+*J«W}ð6}Â—Žœ0eH¿¡ÃÇFÇÆ¥DDG%…YÂ8)Ûÿ4`GT᪚ªêËŒ>z:÷Xö¾í›9O„î&\Ú“~Ò¿’¶ÃÙo¥¶2Iý°·êòè'șӤø‰oùt}øøÑRw,¯Õ Ù-‰ø™Ôfï[A¡Ø«\:}*‚'áýè}}ïmRòíKÝ#þ]ïýìÿÏÞyÀGQmüÌÌnz’jz3$¡Ú°‹ØŸðÔ÷T{yê{*ð^TÅÞ‚°ëSlŠH!©I€PBOHß23ÿs&™° )»›ÌîžûùÌN½wîýÞÙÝßœ9s®SÜ.–÷0ß#°'5i”¬¨·×´\„ûâ22)bL‹SÁùƒ;”?ú0ät!ÂÜÄ„ž÷ ø„ Ü;th¼,[Æç.úöNÔÛ]Ns¨ZBËbnÿZÔùÝ2²We¶î1¼Þd$ÂɽŒqÔ×éOŽnö´ð£Û7¦ãDî<š› Îù¿!4#élé;LïÕsâ«¿Kœ~;h"ö´ŸúÂk pcu%}qr³¥c—†Lû?úoË[æ6i<㉘ ôÅ·Q|»×%„CÈ¿).ƽUÍXû±äLWå2˜PGöÏ;zè]ŠÀ‰ÙKôõ–ÌIx–—YÄ'=õü8NÂ3Xnde뛼r¾Ĉ@«¥âFÔ ²Ýrª•3~¯ð C:Z¿ç£ÅûóĬ̪*­á^IÆíÒ…! >Çe$9ü¨s]@L)éluN/Wê79$ÂIXÐ:m×ã~ëyq“ç'àžß‡g¶ß2Ž˜÷”¿÷ Þ|=íÛmõz Âû>H“!ôÙY°Cg>÷”v(ºóaüý hòmà7b(¨8r¤åûePñÕb­ì '€’“BÇpåÅPññWpý•Ú¾ðÙ/‚åçUPñåB~t2˜“à=ªÖõ™P1ïó »ÿ¥@À¸ë@ŒŠ9w/”Ìx ƒ†•m¼åz€°0°gm†²·çƒZB7¾§“„å”Cés¯@ØgÞÁKL€ †Šï§Ñï»·V!A(Ƙ;S¹=C’’Ðê»ÿqc©ô2–Šï¹Î”kô¼¹C§‚¬L´Z+nÁpM¦Ôª´p •ø'&I×-=ûÏZ»xÅUHè‘0t‰$ÈIÒË™~!8‘ˆ«>gGNœItÓœ&ÚçU–ol–X€ë$¼l.Æt€à/¾ÓÜC‚ï¶»ÁºjXW¬Ó€>P>ÿst¡w0Þ kòç.{u6‘áüÈd ,”‚# †‡ƒßm7ZXŸ}¶-Û@èÐÿ2J_™ Ê |ÿ …¼¼{X–ü ‚É ÁSyGX–¯Sß^ü{À²r-Xß_¦N±šø£"µíXºÃßw'ø¾*¿ü¾Vo(‡AéÌ7Aìè x­²y… 0ç 䥦öVÛ¿ô’P >Õ=3³@_oî<'9ùbYQ¾Å¿è0Ê‹FÝJ|+î–ø¬ìïš[–'(%¥}…*ÿ­ÝU»‚V J¤OªÝ|àÒ2Œw7?A ™™$L8¹Ÿ ?]„søQ×ñÖ/n/]ßúDœõe}¿ëÎl ’X€¨3\]ëéPùÝR­XÿËG_Ê °þºìyû´m$˜åýU.(桃Ñj½ìð?'ÚîîP´‚/ÒŽàÔÄg@­¨r7QÆŸ%ì[wÔT»ò[|ÚŒÖw1&”=ûÁŒç#îwñy –•AÙ¬w´<ú?SÆËšª ¶ŒlðÃsÖà5'à&À I@QíïbÅð…@M,¯O¸æúw!³e."{†$ÿU–åPöh1ÄQÌã¾éÚøÌÌÕ†l| +¥Ž+åíÙ}¾Ä>¡d0G5;hn­T¼ñØ…D?À§ 8sCÓÂ*r¶*$I’Ô}•9üh›–~êœæúD|õIßÖÒò=" p視U’¶žÔSÅ 4ð’6úr›öÕ,Õþ ׳€Ø¥cͲ}ÛÎñ]³Ña\K‚˜¨•¡œ< b»v –V¹’Hè#çìÕÄ·C0æ cúéÍzì²Ó[x‰ 0ÈrΪ¬ŽÒªˆ1¿I˜,¤¥Ñi³SnJÒÃâí5̈º[û<`áÊné™^ãj‘;,©'ʸ;sswß?ŠqZ;~÷°áehò_€áç'dfý®íç#ÐE¡~mÓ5ZuVÕÎqÙõ5zˆ§žt¶úºOÌY€{q7+MLb·£ß·*XäRo²’KVÃÉÿú«@LèEÑü¾Ãfý§æ`¥¤Ì=ªŸªÖlÅ¿žòJPŽŸlr䇬¼È˜€:´]¥lÅ3ªþKE^KذqSs«F£ÿæ¥&ÏTU¥ÆEèŸ&ÿ+;¯_ ¹åíx-Œâ‰£7al’ ªM½ þú kEQ˜þEôе_„©?om[>)Û¹w£pò12¹à‹’æ}A ÑÜFld€ÿ¨sñ%ÊþZt©{ô¹Žm2}ÂÕ¢ªr“©¯š¼¶[Ñ"7Ž1º½æ’B/}Z×e€/tŽÖ¬óbdæë]“˜06߯¢+Z{ª%ÅüP¥´æÖX5Ê”›:øGñ®«Í!ç{ºøÞ;tðˆœ”¤÷JO)@NàK•µÄ7¶ó0Šî—DÑܧGVö¹ ç±ønîÄÇ3Ï$ÀpÏì7§jMÑG,híºçvºï(¼ù(È)!Ü ¡ÓŸÒ9ùl—L_ÂD±Þ„dùá'ùǽùÕ<-¾8ùƒKqUž^ú´ôéAãÿ Awý ȦhmØÖgi‘Yo A“nÓÄ{Å7?¸2Æwj·0&ЮˆùM–á\}€½8ÀŽˆìtóÐvò† ‹Ql–Û1÷»]é£7«fŽn:¸üƒˆáãC—+VÐ:'&À|Œ p/épr©qAëöÉ«n­Õ²’g^®µ^6ûC(ÿÚ6}’g^ѬßB0†ý+.©ñÙ.N.™µ“eÙ¯@“žì[wj®$ÞðŒ1Ñ¿±löP6÷cCƒA9uºìÊ/MbD(eb]aJ‘¥n»:–·3&à>®ˆùíMìOé©«U Ø-cðÇÓäàÖ]ÝÂvtÑ™¯ø}ÔcíÚ£îë.™ 0O ÀÜzÉMuÔ…w­âÉ-Ô-MgˆoÇ‚0¹RTÙ mwÌÎËL€ ƒ€³1¿½e€½Ã“û*våμ’ÂÛQpŸ'U´6_Hª8/>+k1zkÁ˜€°7B/p˜`BÀ٘ߞ>ÀαsÏ -©,û«‚/TÚ­òˆúº ýáWáxÆóƒ@Z—™I£'rbL€ Ô"À¼Ž6_ÑÞª–U¥Ì"™h™Ã¹¨K,’YµÛíXœþæº>wѸ&àœ‰ù]5ÀŽŒ£ƒA(ÑÒØàæøÌì…F§——œ|¾*(Š+JÇâGð™õ‚ú›Å÷»ü‘•sæ~Þ˜8M€øim½D‚PKŠM>ZDë,À«™8;+ T­E¦åŒTÃýŒ=¼ 0Zr’“Æc4QÚÆfÆünp€ ã°“îà8[¥zº—LP@îQmÑ‘‰¤©¢8/1¾ÇO‚4` '&À˜@£X€7Ѝõ°Y,‡ÊCB„J³lú¸‘­_o9#q,÷÷*ËËŽyK›¸L µ TÇü~I¡Í‰ù]ï;&ñŠnë3·µv;;ŽPé—·'ç¼Ñ˜€âû ŒQ.Õ̓.&›qÛû‚ô º˜×ögl¬{¯3&À$À¼A4m¶C=¼ßš°víÎíýík³Šxˉ‰#š¹…ü];èO“,Þúä-Mäv0·°ÊÖWP”6+æ·' °“›’2TùÎܼݷéít„Š¢»ŸI~’0?qÃÆ Ç}¼Ì˜h.àÍ%æÞã5a¸ò‡oÿì1`à‰mºF¡g7'™#GU¶ZŠ·f¬ËÅ¢ha]€Óœ`¨Žù}GÍa"Ü—qö— «Ø™‡böv=ú|¯6†\ÛyõêB}[[ÎQt‡ãOÂ-díVUû­.¿ øã«¢èþ ïßçalòoâ=46y[2æs3&P?àõsi‹­ô³¯Oòñ#G¾;u¾od{èRXõ„³-*åéç$~yíc„ý;·-Ƕ¦>é¬iΉ 0´$æwì|‡"ö–¶`‡¬ò(¼/ð…Jü9¸×ë6FõAýP4 ïǯËÞ[w?¯3&Àœ%ÀCÑ;KеùÉ:« Ä•ß}½@µYOüÔ?Y±I|ŸÔÌÄø)–ÊSk–,úË Q~hÒE8ñæÄ˜ÀYä+x _Bì­"ÅHŸåp¨`ç7<æÊÓÇ szÞÔ–dÝ]sROËKMÊÅŸ€_PxÿÍQ|SD´xÿ‡†¿<áÚë37ý‡Å÷éä%&À\K€•ky:Sšn‘%qh/*:^º-;ëµþC†?»t`ªzmö:vEi&]âv<4 2[þiyy)… ´VOº×™7³d>œ øÜ¡C{©vË“zkñGè©î™™úzݹÑØ!ëýÞãG®W´v«öK±¾â¼Ø(ªÂ<)0ä³nºkLFvݦñ:`LÀ¥X€»§Ó…‘E–Ä! EËÚŸ~Xù1$ö¾`¸:zK†`–i7§³ Ë7‰ïí; 9[7-Ê^»j oq˜ˆ/d 8BàÄ"€/O¼‹ûüi?Zˆ×'\sý»Y¿85Ò;{SSÛyŽØy+Zï£ê¶Ûr…ø§¢$ÎKذqSÝý¼Î˜p7§8þˆáP»ÐÑÝõ‘òuˆ•8U,ûß'ß\>öV3ôê{óñPõ²?7ŠìÞðÕ@>ßävB–ïÝ[³¯Xøõ/:KâY½L|éI p„Ð⤠4˜ÊÁç猆&Póû"­’ÄüÎ2øYQ¾Å·XÚl€ý#FDÙlå(¸…‰vÅ–DõFñ]“Ðz¯àè”ËÑ>?>:n¡°t)Ý”sbL€ ´ §¸  ‡°æ)mR{ï;)ý]0¤àß$Ëp øiÁgß%Ÿ?êèàaçÞùÙðQá Ǩýæ ‰ÇqœpDq¾)Ô E;¡.åÊÊÒôŸ—~µeýÚ­K«yWâKœþžqSs Ð7‡ck.58¾91¿sR߬ÊʇØ,?j ÝBU®‰ÏÈ^ãiiâžEß^JÃÂ[,å×ãùÐZ_çk-@n?@ôû°Szúþª:±ÑÛÝ}Ãå3&pvN p,žÞ¹wæÌÈÙO=eˆÐRgo²¡÷ê\·€“h¤Ç¿~Y¿¯Ø°#3#gøW]nOì5*0ü£Sƒ,5¤²\ð—í¸ê[É"™T1”ÙAp‚ÝRYºg˦Õë~Z²¦²²¼iÐD é) Íi¢' º¼Î?5îáÔ$ô}—Ëì1 b´N ¸wÚÌY¶_‰þ]UAÃ/©f!n0ƒAv¼S\Ü;ÐfiOÕQ©² "bĤ+ÿ²¢nõÂÊË;}^Q–Hß¿ªcE >}ʵšÍ3ðxípÜWRm¨Ù'‰Ò³Ÿ{*³n9Í]'_sE¶Ü™»èÛñ¨·»ÔÍ/BnÿZÔùÝ2²Wàz«×µ¾Wä+°nÝڲ~H5ij§uY¹{Ýýïî:sùL 58-ÀéÕ®Øg(åʬð'­Qi/?¹E2=% ¸'‰MÄ á×o¿\ŒË¿ô˜ß­gï¾!¡‘þa&“YóÓÄ}—‚ìÖ?»=R¯x¥d>‰VíSúzCs» Ç =u¸¤¼´¸hß®»rþÜDÖ-²l“À&K7 n*‡¦bœˆ'qeÿo„àL¢ï;>ÞMøýw¦/Ì+<úè«Å¥J<‚¿Ñßo”+•PK…àg·y\ä)I•:ž8ïl}¥ßýKªâS\”êx¬ÕdVJüéFY¤ÿŠIO?[  â«ÌÞNK»“nˆ›”ÐÅ$Ðj©¸ >d»å"]ô;fF¡½ÅîüÉÿóvë×Ów^s^w<ÆËiiï´|@åZߣ‘$ÀV®ÛË?ÙÚæ}ß±äà…îl}e[%?¥Ì¬Všƒªúʳè ôZg¿Îo5§ÿë+›·1O&à´'kÆÝSŸ;@! p篲Ôè"’~°I|ëÿoº8·íÞ’½'z´J}HÇѤ‡‹ž•nïÐáæX?Ó`ªu@互²¥«Š‹iäÊÆñ".5lp™þÔIl“å›ÄwQõ2‰r¶~#g“ö}à€+¬™ÎÖÅ ùé»'Žbꦒ÷DQŒ‹¯rtl6úûjÒ„§ƒ«X,ºŠ½tغÿ‘ISŸ›0wú´ŸÎ&wHÒôÞž`µVÜ¢‚^×ÃöŽ¡ûc“ó»¥gÿy¶²Ü¹Ûrymÿ|¬O§.%ûÕÄ“;¡kÉÁßnñå¾'äZÿ[Lþ¹Q½cóC»Î:dÍdâ“iç½F7ñô;Ή ø§¸FKÞÀÆ—&O™~ÁœSWùA÷4V“d­ÕEµ£0§í4xY½k,ä·ÑÇÒ@IDAT¸¬ýÐá\σ‹ž‘¾8qâƒ{cc‚#$±ÖXú×JU=º¡¤$·è?غø¦›òí&6ºõ›,àd#!ÎÖo„àŠDßsEU1Ƴð„+Êó‚2è{'MxòßH’ù…¨’Sô²4  åqßCwöÞ„ŽìK“Pý²tìñÐð%(\Ÿ@þšã¹)–xy…òw”eTYPµOÿÊã•'ô}_¢8?A ™™ôÝo³„mx_þ|)²ò8œ»%t,;Ä}_§7ðFzî I(ŽƒÕGŶ_|÷”gÿùÞŒ¿Ž‡Óo9'&à3\"ÀéQâ!ûñE˜·&¥Í17m2ùÞrrŽýé*ôÏ£¯“À$ / ðœH€S?’•…„€Gþð[>9vü¥G;Æþ'P:S›ÆEEÜmS”´ee¸^_ҹП1¹•/bCœ7 pÝœ¸Ñ~þ‘G-MøýRlÇÞB·Šü8S§·[ZŽ—ä£ï}çLãÿ9Uß} P¸P‘Ã…ž½‡)’Óíü*U‡ },ܲú—·º–]NÖî²rõZßôÛV+áu· ‘¿/øáÙâ‘×ÊäÞëþºc½šX´[½0ÿgÁ¤è?Ûî=±'—Ž7(pÃîÒÊ®—ª¹=^¹ëég„ÿ{þ?º?}§åÉäº3F¸D€“þÝçZ¶càãé¿¶ÅK/´ÕÓvëâ’~Í—IH’¨$ñMpŠc¢Â?À¸Atb‹ªl}<ÿà„¼òrÔz¢h}"î(Â馅D7Íi¢}lùF-MdùÖn®A+ˆÂ£sŸ›JÖ*Ih XA•EW‡Q4wA¢ï›yÀ€aáç^=:»]yi‡ÛþøMbËwóÉÒÀY¸,&3L\¹ üTý«*¬Åaá燄„½b=Í2Tš4)-Ú »#-'c®ßõ•Ä–ï–u]4÷=ÇÊE‘Gó6líûóÏ ¨¯é7›0$WýŸ°sY¢?eúsFUt#Ø®%_Q—îÛÑ?YpÉš«û7“_3Y…)ô#M'p:Y=§ef>¼æ¸Uži6Ù#MÄšÍÞ‹ï6=*0Po§Þ6½½:âA\èœ8/Ýò‹œZB€¾ÇªíØô½öDñÝ’67’Gs;Ácü]|áýŠ$Å¢Ï7‹ïF 5´›nZÈg¾806Ä÷,Q@xIÍ}zdeŸ›±qžÅ7¶E”£„1šKú|³øn¨s›°n\Î;°BB–;ÓëAÌBF%—j“&Tƒa­N€.t—&áxw° }Ou天ӗ¡;ʧbøÇ w 5Y{uK®îïL?R4éVoÝò­Ï:a[fŸ“³píÀþ„H’æg"Áå?öî1mHö–ǪëE<(Ñ\ŸèFEŸômt §f 8ßU¡Õ¿Ñ —äóE\5÷¹³G¬hæi<ñpún‘õ›žBÝCcñ —Îu%ù„Ç?zö/ÿÕ—S°4ú3jÒ®I‚Gº”ä«üÂ¥óÝD>á]‘e~Hç°´7p¢ßqþ w-—``.àÔV +…ñP{Sß:Yºá¥ßMÿKoøë0A’:਴ú>ž;A€F÷Åð„±è_C>yóÅõX”QÝDª#~=b)Ô '×@–†'Œ¹þŽÉC¿ûpÎZ,•D¸Q¯×4šKñinàD´úÏúe\|ÙqD0\Ã)'N."à(>E©‹ŠoÓb~¼/¢¢2c)ÇPEÎݳ3¡,(ز±[±6­˜÷žœ"ÎdÒ—4ÈÇù®ÕÑšå·õ; *:æÜ RœïZGñJ‹ Gz”§šýý®À2pÒ­ -*ÏM™´k€êHu¥8ßn:Ï‹,µþ‰¿ Oý¯?ñ­1´øn°Wp›w¤Vý'žé¸—™@S Ì;Ö/÷¸° TUýî²?³/ß¶y²Á""4µ9|œçÐÄVŸ^€M¦®A Žthcæ‚>¥8áÈS9%ݱ8ró!7£‰/ýèN#\ò ;ØC.J'œ˜VˆR7,’¾czô*£].j1ãëèq*'&`h‚V¤pô?«+ŠÞ(ö…y©©½ ]q®œ· ßKÝÿ;@2›¢C*ËY|»°—‘§¢Ð±š³ÿŸª®¬c­Ôˆõsao´~QÄT´þ§§Lô]cÆ­ß |ÆV"Àw+æÓ8G­Ý§P„_…/ô’‹Ã!JUmKsFŽŒv®dÎÍšL€~/é©!‰ICüe; ð&ãkü@â‰/‹"q6âÿ“v Pý~òÑx6ïbŠw`!˜‹¾cF½š×(>š 4@Àˆ?p T•7û:áù’`ƒ´—xQ„Ç •å‹¥¤hñÂ}·ß­HhëîšÇëÅ·W¿HNÖO¹ÎÒ¢"õúHÞô²{‹H¸1ø¥ßèñpc˹h_#ÀÜ×zÜÃÛÛ=#c£$IcÑE S†„CÊUùs5-¯eï[¨>]c$ hBî=‡ Å¾êÆ†ñ;]u ðÍ—/í{¥ÏŒx ¸±í\´/à‹Û—zÛKÚŸžµLTá¾ÓÍQ¯Í[üÝ›§×y‰ ¸œ€ný¤¹&ØîrÆZhudMËFI5õª®£QêåUõ¨fKß±Þ^Õ@n ¨&Àœ/$•ýó¬^yŒ…~^JÒãú:Ï™€8 þít`‡"uÖ› ±hÔzŽ +¡s¦9'&à•øOÄ+»Õ7•˜¹é?÷ü½µh9™•—|Î8}çLÀ *æ}Žç®ÐÚâé0î:£"@ÎÝ«•§—m¼åz€°0°gm†²·çƒZ¢E­a „a9åPúÜ+ößY5Û›¿Pë%ÌægwoŽêþoþÓºÕê2éi°=Ýz@xÒH(ݵõ”íÞ ÿrD{ÈåÐaÿ¼Yp*ówh7êjˆÅ}¦°H(Þ´òç>ré)r!´¿è:8þó×ÐùÎ'´}þ1A0ûC§Û†WŒ…OݮͣÇÜ æˆöPògþv>”áy)tNЬØÁ }Ázìüü8•± :ÅC×ÉS (¾X ò!ÿÿ^À<›k5Çvâ(Šï‡j¶­û:ßñšõf-TE˜qÙ÷¬Yçæƒ™@+` x+ÂæS¹Çw[.Ù=Ä0§ ÿ‹ÏƒŠÿûäݹtÏšŸ¶|䨷îåx!”¾2,ßÿ¨U"èá»@-+‡’§g‚¼o?ß?¡¦rbLMû§&Œ+æÊ¡Ã`Y“®Z¥•ñëj¼/µãyö@éó¯CÙ˳Áï‚‘àwn•ÅÚÔ·ÿã°çâ~<§-}#ø£"µí–¥¿Aéô×@êÞüGŸi™UƒÒ™o‚b±ÕÔ‰&`F÷·A©¬€}ïN¿ö1š¸¦E™«À^r Š6ü{ßú&ÊÍQ º”œøù[È›õº§ô€—Þ @ò „ÐAàËÝOCáº_àäïËàØÒϵ}Ç–üö¿ÿ’¶l=q~ú&ìzfˆþþè>IÛNÖòO¿æÐpÈo&ÿå[¨<°GÛ×õÞi ——Âîçý¹Ð]MK!ý‡j7Çû™€/`î˽ïem§áh Ç1½¬c½¼9-ëêõPññÍ]Ä4°('Nj“j±hB\Î?ZÃÍ}zBå²_A9U –å+AJìbl‡Bd /}-æ«Ö¡xÇ2P„Ó°±š˜/8¢Wùí°oÛ ò‘ã ìÙ¯ÝÐ?¼PËÊ lÖ;`û#*¾Z¬O~èòÑã(æ7€‚s[F6Šö¡Ú>þpŽ€\V ùsf@Ñú_áäo‹ là|Æ"@ÅÞ] Úm`=Z¥Û³À^\áÉçõøaM`ÓüÔÆÕ1ü’š ˆþpè“·àЧoAÙÎl(ݱIÛWq ·ÆÊMí¢õ¿­ð8”lÍÐ\TèéIHïs4’¿Žâ} EÑn9¼$ä!=j‚Ü~ê$œX±ã{ƒ_t§šóÖ]ˆ:o4D ¹@«GÝ}¼Î˜Àiì‚rš/y—æ%'݇ž®s«šS#ü/h7Á Øwçi­"QMI Ðæu?̃úk®&!ö­UKË@êÖ…ñ íp[zvÝlµÖɵ$ø‰@ÖnåäIÛµµ´Ê•Djß䜽š`wÌd<$ÌþÆôÓ›QÔsrž@yÞŽšBl(²?ì{Œ¨k¶ë ¡ƒ†ƒ9²=ôyñc}Ó}E¢ül‰,î‘#.ÅSˆ(ðq,3QÂeI³¾S¾²:~âaRµk®ûýi5ÅÒMC ºÍX¬Ù¦/„ôI‚n÷Mƒ}ï<£Yíõí^ ƒæ,ƒ°Ô š|öSYk1òI&ô{uô}ácŒ˜òMM^-à{ßž†Ây ôãHÄ2)ŠŠRQy/ÿ»$€w¾Ç8ßk  FY!?ulj¾ètC1ì"è=ãýš)lú´sbL ^µ¿Eõ™€çØ3*)B.†5øœµµÿ7N ‚ydBFÆNÏm×¼¹&M}nå™;}Ú(š· I˜‡†ø éN±øŸoô¶Vö½eýJ\u_BC´È'Ž1¼…à í„¥%‰èÑCž‘_’@ Æ=K4÷ÇýbD(tNŒ¦âŽôù° !/$,ãý—¦“Ÿ9œƙÑî8yãeÖ\w>1uaWëñÔ«sN‹ØÆ³·àüÁ’‚ôPƒŽ¹MáQ —•¢wÃ.'ŽÇ;.“5]©v-qÜ®/›Â0%º;©¶Ú®Q4Ø%Šˆâ®´¸Ç°GŒÜüÑk3ñîèÅ£]îj:—ëA\𢵖-àÔé\Õæ á‚ Fw”ÊM1ÂUU‹ÝüÒ8h}Z¼í:èðn©ø¦4(¾i§,ƒR„º§ž-µínßtjNu`Pœïº‰"’´D|S9gß´ß^\t†ø¦í$¼Ý)¾éœ˜€/`îK½í£måá>ÚñÜl&À˜`%ÀÜ ÃÕr-ŽîZž>ZšŠívœ|ƒ»›-™quÝ´:º„o–/ÔúŽoNLÀ+ °÷ÊnåFÕG€b„‹*Üwz_MŒðÓ›x‰ ÔOà !`—íÉtÆöú³óÖ¦°HfUQ”2‡cÄ·¦.²*—[DsͺC}yÑ VÑO•e¹î‹ ÌÙ ¦œÕ¸X€·o¸fn @1ÂEžÓ‹®Žþ¸¾Îs&ÐT¶JKai@‹ƒ¦kÂq¥ªl·k¡mzˆb“–›C¸ï]Ü eæ`Õfµžtq±\0$à†ì®”; pŒpwÒõú²ItiSEyÙñr¡ÒlöúF·F‰#ñ´Y,‡ª·Æi[r b9Ti,&ÿ–äç<õ –ÄÔZQqwëß³zŽäMLÀ;°÷Ž~äV4“@uŒð” éºþ£Ü””óšY î[tQ@seÿîÙtíävˆó- nj-q$žGäÓxê:k7­ÅÅjõ¢:R]óCã[\g¬M€XÓy9é¸Gïšsb^I€¸Wv+7ª1‚V)nÀßûmÕÇ¢)˾0/5µwcyy¿Op—ZÞ’þGŽlµœÚÖ©+‹\ÄQ±ÛN¬XüÍ,Α· JwI5u¢:*²íDnToî{—  –ŠÝZ˜±êóž¾c5¼]t .† Š pCuW¦5 pŒðÖ¤íçÂ%š0 q@#ÑØ÷ïÙóc^ûad{¯h`[5‚øÇG~ŽuÐnppN¼–j®GŽ~žÚU(æ' Îv1ÔXîß¿ˢ¯g›Ëù™€F€8_>M€c„ût÷7§ñº5Nß4¡uÍ’ïR,–¢Ÿú'+6ÉÔœòøØjÄøáÛw'V-ü†¸.ÀtæFa¥×G»¨®h±=±ºó(Å.ò{-í$bG ›¥ð÷Eß}‡åÐw˨×@K›Éù˜ÀX€Ÿ„7øc„KCOsb:²~’0 @ãt[ÊËKJ³ÖýþÑñÐ0X:0•§f nÄoç–M³Nži¸Ê–ð¦±&NÄ‹¸Ü“3ÿ÷¥ßÓË—Ä•ø’•Ù¨œê¦]Tgª{nDá—î£U¶„#™&&bËݾ]Ûÿ·á·åë1+}§Œ~ 4±…|8;àgçÃ{}ˆ@UŒpqºÞdŽ®“ày5²r’ø²áDBŒ!‹]Ù¯ß.X–û§&ÂÕG\¤°O8R9K">¸XAñ­îÏÝõÁ’Ï>$×âI\‰/q6¢UùŒk€êNm@!©~×ë&…}±çIÄèÛžcb¶gÇŸ_ü´à3r=ѾK87ú5ÐHëx7h~ÄÞ4N|”xýPÁŠ¢÷æôÀæªnòetŒÝþFÁ‘?}W63å‚‹ÇSÃ2WýöÍ]ÈxA9~Ó勤gǶý8ˆÌÁðøÄ^[»÷(ˆl‡bMÂ*ËÁ¤ј‹µnÅDq¾wÆv•}©«z PmÚ´aíë«»«qªz"NPró0:´šk gëæ<Éd:Ò)aÐîèÇ‚;ª*ö}ˆ½ûžî%8Qœï=á=!½Ó¹jzÇB™*ãÓ£¹k~\ü ÒÑûŸæžt pÇú WýŸÐŸ'&Àª ‚ ªcÇNÈÍÛÝ Tu)(œ(Fø!|as5ƒòy$ I’Ÿ* zûޏö4qÓ¿gíÊÎÚ=⊫.²÷è}AÞ91aZrƒ,5¤²\ð—í¸ê[É"™T1”Ù¡ï†n,Þ¿sÛòµKXTVV|iè*ƉxW£‹ïz¯È]«ÅœÌ,ªŒq 1EÈïvhİ–«Aö2ðWl>×÷Ø—`Í*JƒìPÿÛ­–’üí[ýcÙ?•——á!ºøö¤k€šÆ‰ 8E€¸Sø8³7 á{F%Ý  kP;õÃ6ú£& á#22vzc›¹MM&€Bs `Ý$¾ua¥ ³ŠŠ2Û¯ß-ø·ÿÔkààîÝzõéån’š?t¢¨(¦0›¥3ªMä[L¦Âr“¹Ë7~ÂZ»Ýf±;zª¢¼ôxþî[wdgئ.I|‘£er= ®Fu?Áªi©Ö5p]çàcºø?bÔ±%vE(ËÏ€Ê=éP[x0và.[H;Å/0 Üd öÇ›zýZÑËòú¹]¶WZN/®(+=‘¿kç¶›³ò°Ñô”Ó¯¯ï7n û °w?c>ƒ áhõZ`ªªô<9 ]S—æŒ9¼ÇÚµG=°I\e×p´€ê‚ÊQ”‘7'ÿ][6nÉnÚtwª…ž‡–ÏšDQŠ~0B’4ñ]®¨ï~±BQÈJläD<(+šHT“o7±!¡Mþ¾4‘Õ“Ä·§X¿±ªZR&¥¤(ãÚíÿ›êŒJY(³“Ó‰ØS–ƒEò·ïoÏý¡Ìº‹úÍñ)‰Ö¸­É×@Õé<òÓۯ쮴q°7N_pM F€b„ïMMcWm«°j!(Âã…ÊòŇRRFÅef–¬º\Ö%@¢’%%ú: L²î‘À‰ÄýÎên*Í^··owYW?¿^˜Ÿ’òó©“ÿEñ}¬jÕðŸ:ßtÃ@¼ˆ pÜ$Àé{Dq£ýÄÑð)㺘aªýÀ;Š )¨»Qx 8aTñ×/ö•¾¼ô€…,û.¹ ãìôÚkàìÍæ½L q,ÀgÄGø0Šž“:xz†/BK¸¤‚:¤\•?G?ñÑU…„'ß$  ŽË$$IP’øF×%ðé>Þ¨saXè_õ÷Ù¬_¬..]å=J:]€“{‰~ƒB"œÄ8­ÓvÝï[Ï‹›Œ—2®‰k¯*ò °«wcíHwcç⇨æZÅ'n]u”nÖ©ïuñíÔ5`<M®‘Þ^w 4™È!À¼@¼› PŒð¼ä¤{ñŸdn õÚ¼ªá0Ÿ&@â‚Ä¥£È AN¢’,ߎâ[÷'=­kj\¬?ùùù wŒ›$Š$äP¡ª¹ó~E[¬?‹Ñ¶}"FŽ"œ‘è¦9M´ÏЖo5-MÌʘ=Å÷‹Ø¬Hj%Tà8Í:hzqÜÒtAýî’k€Ê÷ð¤÷?Í=þðð¾àê pv WÉx(Fx^ÊஊªL¥ÚUÇß›™ý²ñjË5je$IdМD‰KÝêMÂ[ßMöÿý2¡ûøh³i(æ%•'ÿQZúP~yy¶îºFÕÙèLâúº¾ß°­"w“ÌôÙïàÓ¯ÇJ¢ð^,™„wxoõvº±rÙ5àx.]öškÀCùsµ N€¸Á;ˆ«gñYÿÎK®(£Zá|V^ò9ù Y›¾4N-¹&mD@·ò錬 $¸iÒ­Þºå[Ÿ×[ÕOú÷èoöZ?¨Ô×ËÛG® ž”tñEs}"6ú¤o3l›ÝM°²zw¯wŽ(ˆ%/:¼´Nå©Mtcáô5P§\O]%”ô¾¦¹Þÿ4×·Ó1œ˜€Ï`îs]Î n)´xqŒð–Âó|º¨ A‰„[x«³Lûk¥Q£F CJ ßE Övâ¶}‚ô\&·OKÄBO:}ݰsÍÝ$}ö]ènòjÄwTÞè·.Ì ™Ù󭜳õ‡ÞÖ]†Ó²Šyä5в¦r.&Ð<,À›Ç‹öq#ÜÇ/€æ7_cMÊ9¿¸è~Tm‘™oød´Ÿ¿&#î4 ž j†»IsÎÖ¬k 9ó±L€ x.Ý'Ñs[À5g­L€b„‹ÂU(4Ÿ\Š®ªZŒðèV® ŸÎ‹ìžÔ]­®U ‡ly11=;]_ç¹û»Iú˜˜9ÝäG_or7‘DñªÔÅG®qðõv_E¸d&À|† pŸéjn¨+ $¬Ý¸Oéj,“b£?8è1ƒ\y.Ë7àû‚bSçakC´ Ÿ‰:>ë­o»V’»I昘I È»ð[< MÕUîBèn" 6 _ï¶«0Ÿ™ 0¯!ÀÜkº’ÒÚºgff(ŽÓ\ðä1Â)ê'&Ðdørï½xw1e ëIáNaéÒ³ù7¹l>°~Õî&ð©ÃüîFêG!ÿÅ&“Ð/eñÑgñõÖ³ðœ 0&Ðl,À›Œ30Ó(F8 §ûNo©‰~z/1³ ×´€c|éªÄ®': ÷ÌÙÝÄ=\¹T&ÀšG€xóxñÑLà =²²q€a†¾£:Føãú:Ï™@CØõ¤!2®ßÎî&®gÊ%2&Ðr,À[ÎŽs2‰™§á£ëOõ Õ1ÂÇéëÊMI9¯ÑÌ|€O`×÷w{cî&C¹–£›¸¿ø L€ œI€ø™Lx hŠ.… ¨½·Uà#“/ÌKMíÝ¢9“×`×÷v-»›¸—/—Θ€óX€;ÏK`58Fx C-à‰œªBü fìzâ¾N`w÷±å’™@ªŠS±³,X€;Kó3:8Fx XTáNq¨ °ë‰{zÝMÜÕKeLà q‹6ß{š±x3`ñ¡L ©(F¸ ¥˜Î”‡c„7•œÛŽÛ‚sïÌ™5ñžÝv¦³Ì®'gÓÂ]ìnÒBpœ 0fÐþCð¿3îkvæ:X€×«LÀU3²—pŒpWÑt®I”~ľ•reŒs%9—›]OœãW77»›Ô%ÂëL€ ¸“ý‡Ð ý§8{àÎäüLà,8FøYà´â®ÙÏ=•‰ƒŒ@ ôßZñ´µNÅ®'µp8µBî&WÇÌ»ú>]JÑ È‘Dñ*Žn¢á9`®$ ý‡à‰öŸâdÁ,ÀÈÙ™@cªc„¦‡_àYyÉçpŒpHkÍUá dåä)Ó/h­SêçÁó ŠM‡ëÕ/‚ Û;t|VßÏó¦ w“ôk¢'ƒ"ïB¦wS¸O-§ Tˆ¤…LJ H^txiÓJ㣘`M'@ÿô‚/`¾Ñô\ ɼa6¼‡ ¸„@uŒð;9F¸Kp¶¸NæNo£…4㳿5)mNP‹ jAÆ3\O$/,]jiAQ>›Es7Éøo:(ð.Z½Oûòó`:>{MpÙ@k ÿ úï ÿú/qÅyY€»‚"—Á!À1ÂÔ »ÓÒî¬ÄÓÜS°û€¬Ò­pZŽzâ$äZî&*$ëű»‰N‚çL€ ¸“€ö_ÿxŽþ8Ý]ý_âô)[åÈéZrLÀKäÜMµ(ëð%ŽXjŠˆ=ªððk×õ’&¾“¦MDUÔ×€¹Ãø¹i“ËÝUiúáÎKMúûûâªs ëILÇd¶~7NœÜM22ÿ{· 3ëX¼+pàÙ»‡½Øó­~ŠÐ8J>‚ 0О–’ÁÔ±ÙìѹÏM}½…E‘øHxp/½))ÉvվϢù£L¤Qq™™n‚îm‘ç•^%Âáer[1>øCsfL]åŽVä¦$݇"ü*[ I)ˆÄôìtwœË›Ê$wÕÿâKÅ[k_•»ÉC<|¼7õ6·… “ù|“Û Ö®¿ Âã®ßÔbàÆìw®•—ÈMMº ƒƒâLªjªð}bbÏÑUE‹îåÍ7Dó&M}îr¬È{(òº¢8^†Ó§bøÃì§ž*tE)ê‰lU·`YU7Z‚0313ûiW”í­e» ¨òó ªwÕ¼`‰%wQâ,½µç¹]LÀ(ÎwU¨Aõoøÿ|%þöäcÍîž;}ÚO®®! pWåò˜@ ä$'MÂ!zæè‡ãã­w0vøú:ÏÝO -íý€ƒ¶ƒà =ã Qgü±Uð ÷#ª ÂÇRgjpûÚ_Åj/ ž )›Áå™6QD]É©.zià‘ÌŽÉG2âM²Õ¬ï—EIÞÞ~àþô¸‘ùvAbv:ž3&àRøã¢–Œƒì QF¤°µí„^¸t•ÏwÝ ³¯K„×™@+ÈIMšŠZcቄÌì—[± |ªj÷N›™"+ò¸Ú xþ8†¶Έœíqçïú³'åWÑÏå‹!çoÌo]ÒÒò¼9_§’ý¡çXÙ3¬²°ï‚ÐÎÇWw•SäÉ~ÞÞ|pÛ˜€ /A~«²ÙqEœïƚż1B¼Ÿ ¸‘@ÕKzƒ?Áù­tüBRhŽ›²6}éÆÓrÑn$À®'MƒËî&MãÄG1&àèÉ'&ÀÚˆÇo#ðn:-ÝPÕp'!ºã3n:G{¶ÁtðE§ÿð`:Ù­\i&ÀšI€-àÍƇ3wØ3*)B.†5hïGå£/òIA0LÈÈØéŽóq™î!ÀQOÎÎõ,ÑM¾—üð%Ëo ö½Þ˘ð,À½£¹^@€c„{v'²ëIÃýÇî& ³á=L€ ø&vAñÍ~çV@ÂÚû$0ÁªiÑ7ðMìx¡²|ñ¡””V6Ý€h _%v=©¿‹ØÝ¤~.¼• 0&Àp¾˜€ÁpŒpƒuHª“›2ø^UUþK‡¢_¿ŒA¬|~Àv7i…Ç0&à³X€ûl×sÃL€c„¹wj×]Ojó¨v7™‰ƒéLÄÐ^5ÿ1<˜NmN¼Æ˜€o`ßîn½A ôÈÊž ¢ð¼^=UQïÏKIz\_ç¹1°ëÉé~¨ãnrz$KA¨àè&§9ñ`L€ÔX'`Æ"@â./•c„«Wj׆]Oªx°»Iíë‚ט``Þ!ÞÏÚ€:v¬_nîîŸ0<á…ÕÕ°‚éÒÄÌÌÕmX->5`×v7á¯`L eØ¥eÜ8h‚V) ®Ç‡UÛªOè`_˜—šÚ»U*À'©—€¯»ž»IÆÕÑ÷€"ïBìnRïU™` ` xÃlx0 Žn˜®Ð*âË®'™×Ä GÑý†ÉL®Õ+‚ÀƒéÔÂ+L€ 0† °o˜ ïa†"°7%%YVí«0²D0UL!=PFÅef–ª¢^^_u=aw/¿°¹yL€ ´*vAiUÜ|2&ÐrÝ33³02Ê8-Î4£‚:¤\•?G?q©å¥rÎæðEד³º›ü;<>l@ò¢ÃK›Ã‘eL€ ø:¶€ûúÀí÷89©çLÞÕ+.ˆÂ;‰ÙèëÚ\2`LÀ{ °÷Þ¾å–y1Ü”¤çÑû”ÞDQžHÈÌ~Y_ç¹ë ø’ë »›¸þúá™`ŽØÅ‘/3!±q º¢|¦WÅø¬¼äsÆéë1±ç8œ½lðª²zgºžÀ ‰™›j"ϲÒTJs7QäðYÉЩæ÷Ÿ äˆ‚øÇón ïfL€ ¸ˆ» ¸$ÃÚšÆ_¢ êý§ë¡^›·g÷§×y©©êw=‰Kkj~£WËÝÔ‰5â[*P…ó`:Fë0®`^O Æâõ-å2!À1Âïhor=awç¯. 0&àj,À]M”ËcmL€¬·y©ƒ?Áù­Tü’«8ÝœµéË6®šGœÞ[\OØÝÄ#.7®$`>J€]P|´ã¹ÙÞKèQzÞ‰Ò{%µ’Ü €rSRÎóÞV»¦ešë‰Uý?,-¤ªDa[B´g¹žhî&cbïEÞ¥²»‰k. .… 0&àbhãĘ€7àáÍïUOw=aw“æ÷9ç`L€ ´àmAÏÉZ‰@ÞÈÁÝT‹²ãƒÇÒ)1ÚÅÕ?xxµk¶R<æ4žìzÂî&s™qE™`vAá x1„µ÷aT«ñN»Œš‰B<^¨,_|(%%È‹›Ýì¦yªëÉYÝMaZx|Ø-ØìË30&ÀÜN€-ànGÌ'`mO€c„Ÿ½ÐõäUUfÓQèC/ƒ#Ó³ÓÏž«m÷²»IÛòç³3&Àœ!ÀÜzœ— xœÔs&ƒïêUDáŒþ€¾î«sOs=ÉÛA)WgÖ7˜Ž J¦|_°ÌWû’Û͘ðì‚â)=ÅõdN葱iú€ã(ˆUIUÔûóR’××}qîI®'º» ŠïgD7©v7añí‹W1·™ 0O$ÀpOì5®3h!œ#üSœßBEà€OÇ÷דÝMá;ÑO|$ù›‚}-¼$8`L€ ´àmOÉÚ’€:v¬_nîîŸP{_X]‹ ˜.MÌÌ\Ý–õjís{‚ë »›´öUÁçcL€ ´à­Ã™Ï E`ßyçEÚÊK× ïKC×”“‚`™‘±Ó™Š>þÒKÁÅer;QVÛɪ½¨ í@ÂñG O"aÙ"Úܵ¹ŠUÄ4Ê— ¢Z‚ÛK%‹lÁp`î“Ožr¦åÕž¤$-ÇAŠ.©:VØ–Ó1YXºÔÒXÞÖØOî&™éïNÆþ™î&‘5ç„ üÑ~><>ô¥žo墮5uã&À˜h2àMFÅ2ï"àLŒð{¦Oï$[a8Æ é~,½0¼aoA„>èWá J(ÌËP”TØ‹·{TAÝŒ/f›Í‘›ÿ›v©³ç¨ëz‚ƒ‡âÍǦ ΖëŠüìnâ Š\`LÀØX€»¸vLÀ­r’“SW¢%8˜N$€(H£â23ËO|OÚKѪ½rŒ¢ H¢p±¬(]«ö jxp€Ó>BŒŽ ÛG„Bh?@Íü!(ÀDQÄ KÇ_•:†ú»,ƒÅjÃÉ•8§©´¼ KÊ¡¨¸ çepòT™Rp¢XµZmd5Ç$¨XT óßP¤/õ3…üüVÚCÅUûšöI7J¥²Ö†›Çª¼˜¹é©¦åvßQìnâ>¶\2`LÀhX€­G¸>L • ä¦$ÁS.D· ]ä~Ÿ˜ØóƇÎ?ßd=Zz*(ãA…+iHP€Ü£KŒ”Ø%ztކNÑQàg®ÎæÆzŸ(*G á N{Swî=¬ZívÔô‚]„5hyŸöå[=tV· #ºž°»‰/.š 0&`P,À Ú1\-&Кc„+h¡^ÙgÀŠŒø>Ui,í/ c¢Z³Z ž -ð$Äa[Þ!X¿5OF.I¢xBu¶)HzuöSOÖ—Ùh®'ìnR_/ñ6&À˜€÷`îý}Ì-dM"›rÎ̽QÑO.”'‚¡w·Xõªó“œ7)[´sßaXµ2¶ïEñú‹O|wúÓ‹ëÔדÓÞ “¡,Úf“£ÑÕ%}Ð$Aµ¨ŠhA—J$‹*)¸,…'óÓÒÅñ\-“»‰\¡¾ ¨êèþSó;Œî09<˜Ncôx?`LÀó Ôüð{~S¸L€ 8Càîi3&Šú±íB…±—ƒ~ œ)®Mò最/–¯Wr÷Eïxmîôiÿ Š4äzrï¹ç¶+ UTŠ:x˜$½eUi.-~Mmú³ÛÐ/~¿,«ÛñeÎt‰É@Ìì(HOK»ÈîXŽæn’ùî= ¨Ó9º‰#^fL€ øà¾ÕßÜZ&P/ISg<Š¡_س3Üuý(ð÷3Õ{œ§lütÉZø}ã.|ÙS||îô)¯è®'~~°³ceuÏ~«-þA}íŠÜÚ„Vsµkl”Ú_& €ÐàÀª _$ Ãe³É¤½4j³ËÚÜ.+@ËÅepüd1-,c…%êá“ÅŠþ¨ÉÔ•è?ÿ‹d~½+ëm“¢Ès0bL²#Gð<˜Ž#^fL€ øà>ÐÉÜDï'ðà›oúËG+dAŽE1£%Ö„ªÒŒ~&P³**Æ 9‰!H ‚ ððëiéTîI›>T¶ÁÚä¾]¥»o¥E(Ñ÷yêE.ÌùúWuó®ý•] ûÌß½m{çnA{¢: wŠ¢BåÄNÑR÷¸ö@S—˜v€Nä.inqiä8 »öÀö=Ê‘“§´‚U«’x|»Øçä6ˆª8NaØÝÄ%Ĺ&À˜€ç`îy}Æ5öa“Òæ ö£¨ öG =Ñõ¡ ¹ÞÒ/Eg“¤ VI© Dcn³ÔíÙ{oÃC‚¼†îNÀ¯}ú#¶£•ƒ*µ †!ø"éþ ½åôØ6înð©Òr ºlÙ}6îÜ‹tÂäŠCå¢éy“_û›FÑÝõåò™`LÀýX€»Ÿ1Ÿ 8Eàþ´ç{Yíêh4Ð^£¨êùºrhP B±·;D†¢E7 ¢qN®o#‚hñ¶Q “{Ne8…ÖYšÈJK–KÊ+äQ)}¥ó÷rªžFËlµÙaöW¿©±QaBjÿxH݉mÊ+-µÖdïV9Ia+ðÆiŽ9XœþÎÓOŸhëúñù™`L u°oÎ|&Ð,÷?ÿ|;[¹úZ·Ç+ŠÒ2wì¡ Lì,öKìñq<ÞO»Y@¼ð`zateæX»™ÞÛ„r\èy0wx}nÚäZƒ yaÓ¹IL€ 0Ÿ'ÀÜç/`$L}.Þ âcèÃ=C]ûèÑIMêÝUÐ"¼Ç=ÄHÌÛº.‡Oœ‚ï~Ë„ìùô´âº¯O“žžßÜІmÝ>?`L€ 4 ð¦³â#™€ÛT oa&º#ÜdB¿‘áƒÅK‡õ‡Øván;'l,¹øâæ·¿f*9û ׈’xûì´§òŒUK® `L€ ¸‚ pWPä2˜€î™úÜÝèƒðºŸÉäwñÐ~¦‹Rûh¾ÜNÉY=˜À†­¹ðù²õ²Åf³àð>“Þ1õSnW 0&Àê!À¼(¼‰ ´ûÒfÅ*²m>«>ºoBœzû˜s…HŒÔÁ‰ •”Áü…¿+»ö¦…^¹tPï7£¹pbL€ 0o ÀÜz‘ÛàqîžúÜ¥›äKÉ$…ÝtIŠtaJkWؽpôNøú—tøyý6ŠÍþEœéÜ¿×YÓ½5àÒ™`LÀ]X€»‹,—Ë 0ùß3.Á¡ÈèØ!\ºç¦‹M"Ã8’73€_6l‡Ë7¨øcýMœyÊ8~9“¯ &À˜€ç<¿ Ü&à9&ý{ÆE‚ªþ×!Âï-E„²Ë‰çô^ÛÔ4¡S ¶æèWª® É\õÛOmS>+`L€ ¸Š pW‘är˜@#&O™~úó.ëØžÄ÷•bp #9x7¨"Ð=®=Øì2äì?:2å¢K¶f­üu;³aL€ 0Ï%À.(žÛw\s"@ëØËÕ1í»m´)$ˆÅ·uŸ!ªJ>á³>\bß{èDa°)°Ïkiÿ8iˆŠq%šDûOxè™·BÁ¯Ô_TUÑf3 v³MÀQk+c,±iiwV6© >ˆ 0¯ ÀÜ+º‘at“§Íø­ß·Nx­ˆ#Z½º\?ƒ A{ž™óÌšûÜÔ§ ZMŸ¬Ö¤9sÌRÁñ¾²M ‚ÚTꆰ]0ŒMW\ǵÐߌ\ùëOøû  – "ÃÈHxT¾|›£‚°[a{G©ãFéõ³ã­LÀ 4øcà‰á:3#˜<õ¹+–^}A\}~’«Èuò .Z­®ß’SûòO”yPÕ½®ª¦½Uf/û+êê1xSt Z¹¨‘’$*‘¡ÁJ»ˆ)"$P '^~~àf“[ŵƃ\‹¬6;Øp*«°@Iy%”–WªÇ KäÂ’ IUQ—cÂãmø™…çX" ~ŸÏ™þÏÝZüÁ˜€G`î‘ÝÆ•ö¿ôRpI‰mGlThÜ´~ã³§TëiP8R&¼üÑRqÒ_æLúƒVÓ««uïÌ™‘r™òœ ¨E ˆi.÷“zt‰†NÑ‘¢èüß+ZÂáDQ)ì?rò ŽÃ®}G”½Çñ=nUÀÑR—« =6÷¹'·Ô…Mû'?óbA‘{£çRP•HA"A„p´Æ£=¬ € E}Þªr@4™HQÁûßzè!KÝòx 0×pþÂõu♀ט4uÆ£hÁzõ‰;®‚ÄÎÑ^Ó.nHÛ@A¾ü©Ýb“çÌ>å¶«‰÷ž9-MÅ;å¦Sa¤ðâG-Ž¡'M{a $È˰b/Ü[újG…‡¨è¯-E†AxH  •…*¾öˆŠ›¼HÐG[@«sXi…5 }´»Ym6õÆ‹‡m)¾©îþ~&¸á’ñ·ôí~(ºtnÛKÕ®åtH£ÉÛªM“aÎT„¢|[Þ!Øš{ nkîÁÉV«ý¾efþ¤)Óß1‹óÞyúéyx‘ 0f`ÞL`|8h*|„‹foQ6 ­ßM…ÆÇ5‰À©Ò <Õ¤ƒ}ø ûÒfÅÚmÖ–×ܞϑø¤|NÏ. V  ýµE Ø\B†yrL¢'·Ô‡ yNOšYV¤M»öê¬]vì-xÑV®F ¼ÒGOc 1ËÇšÞäæ>’öZD™½ôIY¶>Œn~©ýº‹Cú'B߄޸t³w“Ïë­ÒËãÉ}»Ñ$/‚ek6¬ßºgJi±mÒÝÿž~ß{ÏNýÚ[ÛÎíbî"À¿Dî"Ëåú4ŠB€/D jf¸ïØ«7ÁÁ£….ëŸûC…ÅÚâò2·ï…Œm{Zœß(‰kÁ±"·WgýÖ<<FÁ0©KÝ~2<Áä´é©JÅŒúñÏaž½÷ñÎë.€=:¡?³á¾ŽG˜\oˆç“wŽºÄD¶YýjÒ´é3«^\õ¸æp…™@›` x›¡ç{3GŸy=ExPûp{£•S~Á x~þ"í¬&Œ9Ü!"®9†LÄ*,Z¹Úã6 —ælÂGÓðÖOˆ»å@IDATÿ–ÃלCúÅ·¨¸ô?÷`½H­“_oG|§öð¯ñW×”þg|¶l¼öØ­5ÛZ8z²}xí.ikCç í:×èÈPpç@KV› Ë×ý)ctŠMï¦MÝp¶:ùâ¾{¦Í¯ØÕ9‘áAÒä/Ð'Ú1´J›»Çµ‡'Æ_%~õsü–¾íÉCÊ ‚}w«œœO¼€›¼ ¹ Æ#P–0ªUdxH›Uî®.„ûÇ]¢‰ÏÏ—ý+ŽãáâD¦_ýÇ--ßU‡^(Û‡7lÎiìÐz÷ÿðûFظs_½ûÍ Nõ9qªfÎ_ '1ôXJßîpý¨d(.«„7>ûþvÕ@£UûÇ?¶Y¨§L¼¾GËyÖŽ½Pi±cLóð÷«FB †6áÑaìeC!©wWÑO†¯~ÞÙ»òµ(d¿éÒ!8ˆÈ ørùm@‘¬×ÈszÀ¨Ô¾¢Aƒ½v ÏÁ½»A€¿ùŒ<‡Ÿ‚O–¬Å²O`4‡0¸åÊ€/Ø-A—ttmñ÷3Ûrຠ“aáÊ,¸ýês¡w÷ްso|´x Œ>oœ—Ô <)W?ù&ßt‘Æ\=–¬ÎÖ´ţµ¸tˆ Ó¬ÝSßùZ;î›_2Èfª?ßeñßWfî K#EæxsÎôi«jÐÇWî›úb_Yµ}Ù-Þ÷Ü4J4›ø¯­5/‰k. ¹©»÷žŽ®(ó9TakÒçsy*¶€{jÏq½ M ÒÖ³­· b×Öißáªha4¶ž~Ýð' 韀¢t0üžµ6íÞ¯…" 0k‚[?nÝ–\莢&÷ÀQX¶v3 ía0þÚó€„NP ¿v‰y Ʀ´pE&l@×7]’ =ºÄhÛÂapŸîðÄí£áê –§7ùæäÊã3ÿgkeÕýøø‡5x#`†‡o½:á€(dí§4¨gÆ:’p§:÷KŒÃ!ÂÀ(Úþ­9À&˰ÛN‰|ÙK+*µ'yÁû W¡xŽÛƌĺZÑÕæg¬Þ`¢6¿óÅ/ÈÀnÄv:¦|ä=÷›ßà¢!ý\&¾Ù°ýztü~¸dPïÇÏÇËv°?7ZþøÄ‡Åw]£Rz 8rgÌayæˆ6ªŸ– xàÕ]\YO!–6Ίq„s·¡_´j#¼ôá˜ýå/(˜ÛÁÀžUVmb˜Ô«\:¬œ7¸—¶o[î íˆA½4ë5ùv“e—^*:0A{y¬Ñ9ù‡|?¯DŸòúЗÒ7®:w&ðÉ*N)",.Ú-È¡0¨G !¶wì9\_gl3›%¸nT üš¾Žœ(4­×ƒq™ÄòyI½!4ȆêûŸÔ¬Ùc¢0γQè‡ß³k¬¦o|'ØDË¿mÏ!¸0¥ ï­Yµi{Ï®1Z[×ãG–GÖròM¿áâGT…±›tãEµ|×K±>oñ3œ?¸§ÖÞšŠ¶pJ!ëþ‚åëéÿ×qR§›Æ'·°8¯Ìvÿ´ú£þu—  …â§¶!@Oš(©‚Õ65à³2Ï"ÀÏé<«¿¸¶DëjQ._4µÅ#qz1°ºZŒCMÝ–¶Q"A®§P “¨[°1f9|óK:8=„â;2,z¡x¥tëè@® «²vÁ5œ£Yxõ2hn·ËPZnA÷3ÿ·ì>K×l‚#(êiàíx¹Êj®­4òA."ä‚ñÅOë5wýð{kâùÃE«õM8âŸ?8ZíðEÓº‰\OÖdïÖ\o+„GÐj¾|ÝVô3?9ûj‚œòШ€ÐuGO]bªx8U†Výª­U£nZ´*h¨püÎ&r‘ùxÉZ]c0â ðBœ4e?Ú?“ªì’ì9(ñ̼¥ÕlÆï8d?Ѽ±ÕNÊ'bL€¸wWÝØPëþ†ç»“|¨i@‹ÖN×¢Ï3ù€×—tkUÝ}dõÔ«³æ–CRÃÐþ§#›\Ü­½ÝaѪM(„7hî]Ä6E\ñCkõ±B´R;$ÒüäÿÝ7¾#LÚo|ö<÷ÞB؈–1²_>b üøÇVxìÕÿi.#äGÞ’ô|áÑl:ý³EQQ&ÿe”æ.3#“<4ëcøÝTª=m4—’ý‡ÑÕäåOü¡D"™¢¿è‘^ÈêMë}Ñ=EO´^8ýñÍ0í¿ß _ù ¸ë†QõFaÑóèsz0*Ù…þò?¯ÿSßÜäùmcÎÅH¡y?¼!ˆ¡*êk•6[þä©Ó›üïé× ‘&Λ\ —¨ŠÂ&Œ#/˧Ö!@o-]»ž|ë+y9^ã‚ ¾ÞQyÉÜ´Éå­S> ð|üCîù}È-00´´ßL‡•µ¿û™LCŸžxµHa쌞hàš§ßþ.Þ.> Vué7Ðÿt4•Z;V,V»¹„Âꉬeè#Š‘HN h}Ÿ+æúhœuëGb¼ÃAêQ[š{® Œ€âØŽææwöx²ÀgâS|’"ŸW îØwð߬\ü¹·2ùÖ–4ôŒ]4pÈæôŒì»0Ú$/:·Ùδyä‘§-FˆÎÍ7c*­°ßáž&/„Ǫ$üÓª¸dºŽÉÖþ ÃB$lå¢x"ê…QšÑ— ¾æºÞWκypìtMüUçÃj+À} k+1n¨á㯗¦ùyÇv-däƒdƒðšç.4½S_ÿBÇka2Ò®ežùI%šËIH€:J_Á'ûKÖÂE ˜9ñ{<Ÿ7u¶×ÃâO‹ÞçuØUðÕ€¯Á^òÐÓ³K;ñ ̬B0B4°ï¹‚&-“ÿ|ØGÃ;P¶ešEæ]äM¨¤Å£(¯¥k6Ãå¹–ÇÊ“²¢…Ÿ¨M_-]cyèésîÙâÖA½ F£h¥Ø/\k•s~Ç6ân”A¦Pä»~ L¡bþ…ࢠFùy>ZùÇZìÊüsßaÍéÔ–iº6#>f§?òç<˜@m%PXEU[[ÉíbÕLà·ï¿ÛÚwàUÇOdÝ_ÙŽÎí[ÉÆð]“iziÞçv5±ƒP“XUg]h¡+<·tißB®]’kIXÙpÈ'@}W`Tšh›Á7Øå‚ö7Èòïî/H¾pš@‚w:Fn¾ò"¸ôôb¢ñ:1 1‘W ¶³Q'Z ŠÜ`’ÛÏYïg-âôOØûƒkË¥k·X;åµpÙkÂs?x "Ÿú4yù}LtÜ´3Åz>/Àü„/°ÊkKL0&O>©i™âéÿ}%®íß] …ÉØÒ5[„{‚FMÈßþï˜P}×u—Z«Ï6mî/ùÐÄlšÓ5dl7îØ×*[ׇ_2øê÷—/^x´ "ï0&Pˆ@`Çé Å?˜@Ý&4)æ3‡röÍÈÌÙýÌÿ¾4?]üL=ªo©úº}5ÎüÖ“07æÎ¡–]=«ÀO^OÃgµÒ¯1QpêkŸ[“‡cg~(CS¨pëÀ‹,­û­pÿI£´²*\#Z¾ôI'ßð­›5²žù’¢º‘'Zø‰ìË}'+_—41×^<ëíyÞ~HÀnýÔqn‚N}wØšÓäg2ÃiÙ€ûÃ7½•Y)ÿ‘{ôC7;Âê…„bõÚw°,=Ë¥ðâSu›@å #ë67n=¨™žqŒœ<ù"=C^þ¹pù² Õ°ŠŸ_m4+U9NtÆ ¡ŠL>Z¸B8¤ãæDOÔçg\#Xaò# ù:F OÏ‚bXlÔ·CþâVáaÁV‹H^\(mñ(;> ÑEƒ½˜÷]<‹\~¦¦g‰É¯~q2I5öÈhô ÞŠ¯}º¤oŠšz*U9¿£'[Ã{L V`¼V^VnTM&0k„c¨ß}‘1Oyà#û‰o~Y âÝ:´‘p‰‡¿6þ¨kr;¸n5ƒÀÕ—\Íî&^)Æ¡F,€ÂèhO¯T/˜†º%<,Ä o>W\ØÅA~àÈ£<¡´Å£–¯ßneQÜÄß’ò'›sZ±Ö3ú/å)¾JâœÓ¦¹UŽfê=±Ãx•PçBÎ4<€­'ùÏûÜ{ÆâÔˆ¶¢n–MêçKVøÒ®ãJXȵãU½»9 Ý½b”û©VuËðhÏ=º¸\®ï€ßøÉÿÕAx …ïÒ˜ÓbP´HÔ1øš'“³²*-¯âÎõêÚ^Ÿ~²'÷Ÿ'  ߺû€µE“–‰ù¤ÏDÙ4_ *‘Ô4«¸|ÚUåqLàL$Àø™xÕ¸ÎµŠ âIq1cƒ´ÐËà4ï5j>–põ˜fîÚwDŒˆM섯ò„eÐʽôÞB7o~µY?x4ÍZ-³“ϱl{ƒ°zø}޵ÉípåÖ´a˜95#ËÎÂÚfA@? Ìv rfÀÔ¦Sûæ‚l‡É¿2åM.á¨3ò:*? Ãq]ÿv’³MϰÝYš‡kL¥ª°"ûÍ”«Le6¬‰×¦ 1TkQdã>ý­ù|¯§jÊyÍ,wdfµVˆ g5œ à5üqõj¹sç:¬Ýò¢2Í‘çvl£¼åJY’檪ZOÔ¿UÜáãâø(§ùZí[5Áäµ [R_?ÂÙ9ºðŒºÁÒ`[‰ wrñy˜浄öÏ`ºqÇþ‚ÕwÂ쥯.bè%ç4‰†É›@8öÍ×ò|cK ¦ˆ4l_ZX¶n»5¬ÿà­W29ðõ\”s ý¸cUä¶mÿÊtÀö¯\…¼”ÃäXAÉ-y;uëeJË^ËßåºE±ò­%ŸçXñÓÀjº™Â©®å·©ò¹qJ&P7°^7®3·²†ó Á ßü„Û[n¾ê"qÃå½|MB«­–gAж™ixuÝÒ>—T¹Îí[œ¾‘ÌL¾úi8ÓMòl×ábÑÊ2ò/ƒÄ[_-“fÏýÎïh™“éII¡8Ⱦqí…VèØÑé0;iRHøö[Ú> 9åt÷Îm ŽÕ4ÿÊ+fg&œÂ´ØÛÚÙvY1§ký!—0Væ©àÆOú.^Së^Í ¤¹´ØÑKÖ(Ì?@çÏuK’ûñ:Ù ¬æKÁÅŸX?/WùÌ$@šï…¿oýÂ÷ÕX–Z^Ú£SiHpPÅ^´²ŸHSNößða.ÆÞwƒ ‰`?ÿž}ÚZÍ/êÁ[°<øvñæ—?‹Æ?†Â¬£oÁù¢;¾>5-Ï*'×gQ“½°U·ÍJÂë…ˆ?öï+šE±¿ÈËwq”=ŸÏ×cFMô¯|J…q€øÿº~zLò·ûìââÔöc/ybwDD{æ`‰ø:¶k!ɉC` lÀ|‘·¿þÅ8|,ÍÎß[­ñÈ™“F§¶TÎ ÔÕbsZ{ðqK˜@ù ,X³ù9h]¯þ×íWÕ(á»ü-(>&iºOddbÙíPØjë˜pyrÝ 2)¡É–é™Ù¢g—³,“Ž`,BlÇÿL9d¹F#Ó•âBlÏÛµllù'›ì¿n¶çŠ.-:ž–!¾ýu=ì¹ÓÅ/¿o³´÷Åù?î|VKË}ß¡ãÖ2áë·ï)®È‚c¥ùW.ˆTvÖmÛ#ŽŸÈpbÇ95 :ÕV…6®®#áúîGL¶5±Ê¬ ÿõüO€F»¦½þ¥ñÂ;ßÒ3·]shCáíäž™n¾ýO›s¬Í*¦öªÍ$¸mL €†OôüEêa2;©mÚ9Ò_sY1ÿçu‚<†\Õ§«¥õ&œ†iˆ/ái„\’f›!7q®‚âw¾þEüßÓo‰{oºÜ:o(òßµý{ŠO¯²üwnßR\Œ —v }ò¨2oÑJñ!1  aò¦¯ÿãwçÿ"žyô.¤;GlÜžb™Â„‡ÖCùÝÄ×?ì,ØyÚ[_ÿÊïCÃOæ8´  Õ¡&…O¿ÿ ^'´­µþŸÔ¤zUu]Üîa¹cÜ/Ü”£Ÿxé“E+ïY±~‡ù—!}´ó;ž4+ªê:Õ–òh‚ò²õ;Ä/k·èŽœpb=˜ ýTkG×—‰{mi'·ƒ T%“I«²T.‹ Ô!ÿ6-,=-w –gnùß{¯¯µ£NºnXBªÃqjɦ\“š(zŽÌ'²àú8mEoÒ’—d;NÚNZep7Ë–F¾¨ÿcZ-°¢&7%ùW.ZǪþMÿ×>]"Bþ31>æ­ª.¿¦–7<&ᇦf†Ù¦MóFæ‹Ï×úœw޵NM­sMª¹ðÜ CÛ°‚æ;öî;›3©à^p)Ù™C{tyoذa']Õ¤Ês]˜ÀB€ð3äBq5Ï\Ãc=£àäzÆøn*Ð Ÿ¹­áš×Ô)˜˜ø±‘“ûsR\ô•ÐгͅÏÅq»9SŒ¥Ã4©Æ†ê‰ÎŸyÞÙ­5roÙ£s;Ñ&S ä"sÕÆäSÝ{ã’&7Ww˜·è7µcïArÕ)©ý!˜ Mc¬C 2³½âÈñ4µÿHª‘šž ké`‘ü­P¦š‡!ªw“ÝãwUw¸|&P[° Jm¹’ÜŽKÚÉÛµjjÂ;éªá[k®XM&@#ðQnBøÖÂõ/¾O½Zn÷ rÃó6ýt{.5tóŽ ;öýuݶ½çPìÆõCuLØt’ëIr[Ù¼q}ѸAx¡ŠWž@sví?‚¿£b×¾ÃbÃö}æE`r¢´Ì3æ|ºÄqÇ@YÑ‘—ò”]ž8$`õÓZZ° U’kÙ£F-àgCFB—‚m‘¦àÆÇ·A3·E8´ŸÃÕûeúcv _ž9`e` x™ˆ8¨<Z¡/Eß“qãj7ayyLÀ>_²Z|Ž“RD$zbgû#Ϻ’Çw|wÌ ï%0©¸Ü4̳ì¶ÃSŽÙ¤a¨Ù¤A} KºkäWœ¡¢•$É̉YÚ’ÀЉÇÇÓ²Œãi™*=3ÇÒS>°Ç?ñJ“5w~qì„÷6,õj£aò¦½´H”¯·»ì@la¯ Ì;|ÚëG1Qõx~HÏnÿaó‘@Ðæ<™@Ű^1^› Tˆ@¤;á|Ók¬ÿ'V˜¼â®JË‘™@qh ¸s„}€x-Ùó`qqøXù ŒpOkaš¹HÓìˆT”•Pm4¡5ÆÄ…F¸bÀ!KØòHŽ³ç¬ªl¯åÃONî’k¨ÞB™d›ÖI ­½&T%U#Ï!%U‡4ÚкgÁœä8òÙ Íö>Øuï“’Íðƒ¾É¥9׿èWº“û’2çãL€ TÀ« 5TW DÄÄ/kÒ(¬wü¨¿h¨ê* nwåæûÏ41CðÇÐFõ®ã‰q•ãx¦¤¢Uslß®e;,×)® ¯ÊÉ ‚"¼~f²;2óLiד 0’ °”’Ùð&àPXýïȱ´~k6コÌ÷òKÞœIí&@“þ>X°B,Z¾Ör±ÃÑèÖéf¯µû²‹üI’©µ¼™Ü<&P§ °[´:}ù¹ñUA µ³Ýl)µ”w¿Yf÷L <N¤g‰éoÎ7-á[“OéÙõ^î»<ä8`L æ`”š¸†µ€@dLÜu¦’_^Ôí,ù×AüÜÕ‚kÈ&ü±#EÀw´žž‘Cþ™ïOŒ‹žÈò8o&À˜¨Zl“Zµ¼¹´:J`å‹¶ö½jcßáÔ«È]XÎí+µàGÅWgšš–)þ÷ÅOâãE+±B¡±U:åÕ‰“¢ÕÜP&À˜@!Àx¹ÐÜÌê'!|Qß«7„ÞËIUÝ;·—X»ú+Æ5¨v´¸ËÂeD⇋”ƒÇ½è¹Û8»þói÷Hò1Í 0&Àj ¯e”›Só DF{bàjìÉ–Mªìh……88ÔMºaòpòÍÏ¿ë¦a¥Bù¹t:Æ$º'üY7‰p«™`uƒ àuã:s+káã¯Ç‚oc™º×]ÞS»¾O¿¬†WÚÉÕ)@fVŽXüÛ&ñÝò?ŒôŒ,¬€.Waq'’&Å|VB>̘`µˆ àµèbrSÎ,ÖØzöóXàîÎæMê·\ÕÛÑïüsάFpmËM ×kˆ Û÷ˆÕ›v‰•w^¯¡IM|­)õT¢'vq¹3âˆL€ 0&pÆ`üŒ¿„Ü€3Àˆ‰ñ×ÂÝóSJ™=Ûµll îw¾£ßç`jvÓ&_[t¬Ä¡cibÛž–нaû^+5h»+SÌ•.íù$wÔ†3¹\w&À˜¨À+ÇS1¿ ¥©#&&ü] 5Q™ê¼zÁ.󲞵½»‰ÖÍùµ,g†º‹µ[v‹Wm6;¶o sšþ.¢ZóËÊÎ/¾÷­ér8eÓFáô'š5ª/Õ&zN¤Ù&[nòñž‘•+ö:&ö<&ö>fâœ5ËÖápìU¦ñ^¸ó÷ì¶$¡•jmΘ`ÕG€ðêcÏ%3b DD{®À²‡#a#þ7S)Wçö-ÍK{vÒºwl'5-6MuLÏÌ?®Þ,~X¹Ñ‘ÄÃy3šA Òp¾ðš×IM»ÞÆ•°!r:œÆ9m›Ê¶-šhmš7ô×¶ycQ/$È/•&³Š#Ç3ÄÎý‡!tïwîSGŽ¥Yï ‡æ8ó‹¹Â¥ÍHvGm´ ë%L5£Ïyg‹o½òŒõìBöÛß-ß >X°BÁUûOõÜ7<5n\šÝNÞ2&À˜8,€Ÿ=N˪À£Ï>[/óhÆU0¿þÄûiBëi*³¾]•†aõôVÍiõCC´ðzÁ" ¡ù[Ú§ ë¦ÐMC@µkÙ/C“-Ò3²ÅáãiøK‡¥™ÇÓ³ð~Py·Cž@yßa¢è§ún¦gÜvyE·±žG`Çþ|׳Z™wßÐ_;ÓÌQÖlÞ-æ-ZabÕRXÈ÷Û8ÚÞëv?]´ü› 0&À˜@e °^YrœŽ Ô cÜSÛåê¹ÝM%»Ãô£»ª3œK·„üÜæ!0ÈVeÚª ‰4„¦m7¼Æ&¼v(Mn‡•ˆ"j­Û-Íò6yD´çn¤} å6¼ª÷¹òƽDxhHy“Wy¼ÔôLñëºíbéÚ­ÆþCÇ1gRnQBŽNšým•W† dL€ 0ZO€ðZ‰¹uyXyôÉçfêéM¥S6&°Wκ3Ç!rs…3('W8rEëú©É‘‘^ñzÔýl“tof¼”*¾÷DÎí´Kºw=º´NG™ýU£Ø|ÈÄd÷#âûà›{¯¹yç~ÌyURsh?C%¶v]þ¾Û=H/61dL€ 0&pšX?M€œœ 0Ò Œv'tÍÕB{PWfó  £ËY-0‰´sûâì6Í.iÍîGÅžÇÄ®ýGÄÖÝÌlô?„Tè¬SÂüÌåÐ^ŸáŽÚ\zkø,`L€ 0Ó'Àøé3ä˜(·{‘s¿þóu†2oÔr°i˜])Ì^Ì& ÃÌM:[4®/š5nÛõ|¡‹Ðد[.XÆä¿® ½Æ?AZl]7DfNŽÈ„¯î¬l¯ÈÈÎ'2²ÄÑÔ q$5 á¤GO¤Cϯç«Ü¥I•[M%–!“…NWÐü™îÇ÷—£ú… 0&À˜€ß°î7”œ`!0Â=­…Ò½W*a\Yº Üù‘@Þ¦ aɧ¸¸˜=™åöŸX'g»F[)7c²ê*åhº&Ù™Y\>Ƙ`L ª°^U¤¹&ÀÊEÀÌ…·™4DcSªFR‰Æð¾ÒPHé Cm£r+#ì ©åH% MÓ”vÜPúqî:œ<~|j¹ ãHL€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0:A@Ö”VîêÓ§S®2†HMôJ´A½‚ðwB ¹M õKPP½ïÚÿüsVM©/×£np»9÷ë?>`†»æ&ŸZÓh(÷@犯B]ç§+ž£Æx® !ÅAÔu½&œ‹{¾wMM«·¿ë3:ÚÓÁâŠÄø˜·üwmÈo´;¡k®iö˜=)æÃÚО@¶aÚá-r³²†J©ú¡ÎBY¡BÉ ½Þ”ÁÚ”ŽKWí,«ü¿"¢=÷)©†iJ¾‘ó^Ñ”J)ëy¥6 q6¼ëE÷#'ŠÆáßL =lE°q\$Ëls hÔ|ýßÚ5ÉÎô>¶rùxššç==ØËŽì!¼bÅM-7âYx®oŸó_‘îÅze®ªýt(áçeJ7&x³3nÀO‡²*+>~Ð?”ÙéÞ„$ù™Ôœ †{WZ§ù?&À˜@ ´ŽüðöþuØÚûÂÅÊ0>Ä Ì¾Ë(4ï¸*Gý±µï…“ \FüâOkâ\œ¸rþøâ"DLœ|%´×£N7˜Aé®ââð±’ DÄŇG{ú—Ãg €}ë¿ÜÎüœVÜÜò™YÞͦPѸ›—Õ"æñÇ %ªa‘#Ýž¾‘1q 5¬Z\˜úJ³úñÉÚë¾—âçÍøþ8J9Ü…¿;”Ò—'$k‰IImBK‹Ï瘨۪EßÖ§Ï*Û\½ÁUÅ¡¡ž0ÕÔm}/úx÷e—Õ«hzŠ­ÙgÎ;Ú±ÂA*ó!y¿ðQþUw8¤ ©@üJE3µ„ÌÞ•J\ËQgtùÍ-§+S½U\ÓŠ6ÏWÒ\Š<îªhÚ¼øroR\ôÉñ1S’=1W )ãщ}|tBB…ëR‘ò1‚õNb\ÔOISãâzÅuè°4®qõªãš<'äl]?²ÏÓ½EAÊp<“‘G侟§¾J攘`§¨r”½{Cûl̇¦àô´JÝ ³”TŸ>·È•+½§´¬”xAfI)晦ñ ¢±£FL™ÒP¥é$@Þb(!ü¼àv+-ň‡¶ði‹¿°|'Éû ÅxôÙgë¥É\-]Ú­B7§C{~ ¤üõõç íÙå¹aÆÁ Uh‡;áX‘㇆÷u\ÃF(g Å•!Ls–Ó©Ý9Ó½šŽŒM ›æ«šCŽJšmiËG¸ã»^õa㮾O—6Ê¡îUd¢Ðœ2Óü0gØŸs?zœòˆ¤v 5šV·æÓàÚ×%‚HðßEçí@BpdL|"~;‡öêa_sû| ·Ð|¿ˆûdôé”úA£üÖÊ›Zš}>?pŠiVÅò6ßFüIF®‰û]¡{RhòCH%Ýð Ñý•šì‰ímÝ3ÞSñ¬\²aLk•æc“Ü1+F¹'wöz¯B\®A/ºÇí±Ë§kj˜æLáj~‰Ò½€{`mr\Ì t~îܹŽk7={þ\Ëxn~Âø÷j¡PÒ}E‘JºÞ3=ãþðͤ¼÷qiÏkD¬'ÏÛð çà>&ÛâcJ“S¥)þ™vy¤!‡~£<«yßäÈHë]?Âá¹èôÂ@Œˆ‰n(ù0v;¡Ý;”&fÍŽ‹™içQÒ5°ÏÛÛO[3[áZÉéÅ™èÙñjó–„fÝ›µm¤w\¥Þ…= #kQ«áWD=˜~¨ÒqB&Àj%*Õ€oíß¿…)ñ±?=áû䥸n»Ð+<Ôv0ØWñ‘þǘ^À~^ú]8÷“r:öÛÇhëvã“(Ä^:Âç`? õÄ1 Ö0¿Ã0ÀQuÅPåGØÎ¡ò%´ihç“ ~ßòåAÁé &o¹”Λe¨¼‚B¦4Éyg…ا{®AšQÂéºW´oq4õ³p®aXÓЋIø¦x¾ç¡ŒÖÒ©ÝìrIÄ—-½™æ»vt2u ö¹”¼T8äÃø˜>’âݼ Âw®Óéiu$>üà ï±ÿ³ÓYB‰QRjO9ƒºAàüÖ4ÍoǸ§¶£8H ¹O ó!l£!²'ï€04o”û©V/<1&-Ì~øe*© s…õ mzʵ)/«ÒêÔ¬>™< ~©TýµÑú/‹TC×R}) þw¢®õLSý=ïŽá<Ú’JÂ÷÷ä³!|/Eã6:¥ër\ÄᘴØ9SOÿ×Iív«”2f€ÉÿNçy-œ[×ÂΓâZ·T=5pý_U ߘ‡Ÿ®ð]Ð4×TâµßnjÛË>VÉí5”Î!ÛóÒ«ÖÂPnÔóz؇?¤…È›éxŽ~â3#÷À‰ÛÎÒøA(}HÓä4|ØN в½äsð}h³cs²iª_¶ã ; Zñ9ù¿?Eœù(‹ìÛ­83Ý“`ÿŒfÏêÊ»~ääÉgM˜p åÞ­ñ¼$÷ø]çÿÜÓggxÓŸÊ<–Û?×m5ÄšÓÙ)Ñ=áOŠíñ˜J}OÚuh·Ñ1¦ß'ÇGÇYûBì„=î"ë‡6Œ…IÍÛŠ¼>ÁNßü84ù5ðéIž¨è„7>à‘ùP^'A*—#Ìùª+Åùï´iq©©Ùÿ6Mo/ä;‡Ž#_åFº­A¦xÅ…²X•VŸÙ<2šÊ Œ¤(ßr \} a{Ê›š7šà¹CH-fE“¡ÌŒ4ò+ª“é5þ»g{¢IsHá‡câ6å(±æI×ã÷—t,:I‡v´è_Ðo h{0mºCEÄ&<©·w %ÁžŽWEX}S»¶¸0âÏ B0AsŽr»ûJ·›:žeŒ ÔéŽï‰±š†º0{@òŽç’Ýÿ=\Xª®Á-¶zñ‘GrèXÞè„q¥Ó¥õšåŽ^›ï߸W/Ïõšðûü½‚Nãl­ûÌÃRçüszfO Jý×ûy{D ¦ážhë÷O;ri÷âXåw½íôE·¥ÝÇe=¯}Ú†8¹È3Çç>>‰­•®Ó}¼}¯1™4á©Ô?1 qöçã½Ð(ÛÑ×á’ÿÂï¼gW³gÇE?O¿^B‡0 åÜ÷Ït‹\ƒ¼¨yÿ“¹PV†¹l?˜3É÷\]Úÿyó¡½ôÎö[À7`ð”Ù“F  ¿eÎ1&pƨ2|kïÞ}„2†ù›(iæTä{eEò&­6„Ö×””"Ý{$@À4áWóúŸäMïT4¯‘±“ûJ'óÐ(“K7²í+d»)r}‘t; îže‹HJrÉÝGn5¥y¤ºvtáóéP9¢!âP»Ã4Å©~YzV|³ë9‚wR{@;•eêúó¨;‚¬£œ¨‹Ð4­3~æ àRm´Næÿ‡úîÁnF¾ðm…€´;]èÇ£îg›¤{3ÚB¸¿ùZ!¸ <|ö1¤m%Èû/žéçÓcÇf`"ä!eÊú>qʵ[« Ô§PY`7íK£M¾ 90Kˆÿní¦n÷Ü¥)ú&´Ïa €É·¾™¼ä‰Ý¡h«R&iø-×à(4ìÔÁ8%ÊKÂûHÍåèœXÅîuá}ÂoØ)•:ÝJ\ôÛŠ™w!›·Ê“î¯Þ†®>Â=Š{ôO˜üŒžõdô<˜Gø$—ÚÂ74”Ñ÷×Á™ODý¼ ÚopÏ]KBáoeèéOt{.åŽùå„o\ßC0kZ8Û[†vh4*;Ãh |q¡BþŽgÌ:Tîûª”ë]8o<“¥<óå~^‹dŠtó !†âðlišÅ;j¾&Ô—8ö.Þ #SôÉñdîKtǬ‹p?ÝLy³Zâž^à› F²¾ÁДE¶uÀñíyç _Ÿø&FÑæ!Ï tâ}/šO”Ú¿;%©qCC?y3ú±ÉxNÝÏÎm7ç±a{²ü˜mµd“¨‡BêiŸ?} Z*Pà Ÿë0Ê} 1nª^U®^5pVYùÒ|$ÿ;è÷"¡a°£ß…ž³|õêŠds‚×t¯ážr–áÕBÚ·HHˆt'ÊÚ¤qºÒÇãã¡îk—K;ìÕ7ñù-Ïa8abr2àÉoiÞoA)»7/‹XjÊñ4Îì#X÷ k¸žbÁöøÙt=ãÊ½Þøýl·˜ÂÌÆ‡õïÚ1)ƒ!$gIM+ÐÆç×à9S .xØ‘wq/ù5³9B¢òa6ó14ðh??¼äRŽûšR¨}öñÊlKcUÞú-÷ê^]ýví¦œœÃiý1Q÷6°ù˜´°ý¦·ï5¶ÒÕÈÚ£ó²$ìà‡Nf9Eó ’¡Ò÷Ù8àvÒO‰G¤åUg‰á5žÅ/ÒPVIXuÛÙ oÖ½* vüÿFÞåÀÁà{Ø,ÓˆAAHœTXŽÓ$L¸|meîÉ'$ï$îA @ S‰@÷=®Ý‡xFîÃÏ_`t/ú„¯MCqU®Ã±^h³Ð=Š{ ›.:… ÜW%_L þ/í>F¶\ÏkAf'w¾Ækâ êˆïÕ­Qœ îÑíÇk6;÷“/Ck‡âþüš¢‡b ލaò- ÝÇV·ÞDÇ<Ÿ%Å-z ètê{â¹ïшˆØø$ÇE¿w¦nýoÈÔûñRhˆVƒmóìÔ}ÿ@Þ¯”•¿e¯O&C¸b³ f‰¯&猖•¾²çiNFŽžºÉ)‚óà›'î—YY™jޱî &ÝŒºþ‚Ÿßäb¢ñ!& |…Œ€áP:·¥»=` ch?I»^!|–{Âv¼ì–C„²ôŸ˜À7¨¸:B~¼iÐèÍ¢ó4±êxšÓò{KÙëÝ sÕ?Èåè6õ™ò'ŽÑ®2„ YÙÚ«ûë9ë--¼óÏch|¢7sjâ=Ñ0ÿÔimÈ4CÿÇÑ[h‘4)ÆÆ®TžÈÃÀ è´î«òÔvîž —CÂ6†ïb²ß@P½uyÀj„&?Æ 2UKÅñö<Î/ƒ s«oCvOm“ãͽÀtäÙ×úž+vß© u©cø ­‚æü¿¾ºØx~>¨ëÙ·àÞ±Ì`üœu^vJõûíŽÖz´og òÇüáe°jáN87ÙU0b³ xQ¿aþŠ2Í`ôT®PW:•¸¿¸ú¼øÄãûÐQÎRº¼ çWØq0™š&%Z¡<÷•ÎÛr=¯˜%вÐy8B›„}Ÿ~4£>æ„܇ûÓÑÖ9aɰa4bçùÔæuèÔQ˜Œ!Ð;£V[¡í¾?¶s-ýÕè¬äx[7Ýa+i‹ç`]R\ìØá=¿`hâu( V'¹£|;â%%­UÇ¡ÜøÞ Uû2/SÇýŠ÷—\Œë¼}«^xoºwÞ/ƒð~Y¨ 6cÒS„g¶® ”.*+ùâypƒÙ7dÈúsÞu‡Ài JåÅ´#ýXoŽ6S(oþúðV<(ñ*R¿ Áwµ-j¡|pn´oiõÃÃbY‹ciІC‚ õDyC¸+t#´ÛÙ˜$Øi6ÜO*ýß¾CdwÆ×¸1:0ðâÈØ„4Õ®é ÛóA[gÌw)Þø^]=¯q­œ1?ìÓ§u’Ò{Qb\ôÜòÖ¥¸xhÌxÔ¿1|¶Õálò¾.²B”™Ó/ÈPfÄÇ”_Sr ò¿Ú»dFS\Yå9Vf}Â]¿Š4½8 Ä¢/‹ òÔ$ÙÃÖ]…îÙmI"N´Õº,L16·ÂÇl˜Cj''å9o ¯9jxLü„pg½¤ ‘Ý"W÷ºa±E´mþiAžeìÐPìðØ„»`.0¶Í¿ÎŠ‹" èd\é{ï¢0#WÑ3õF òÒ£ã:htÿS±S¢ƒ¡;sô´‡ð _¬¹Äv™IOF-†`}C/A \03>f·}ÎwKZqt¾^¡IÂãm›ý(÷º÷Bd¡xþºÏ}3-a¿<Ïkž%bÉ“½¢êôÇË‚°µÏxdý±ýÁ»ÑG˜ôü îãŽ2Ôµ  X)_@>ÿÁ¼ˆuª}ÓÏÄÞÃ× Ã9ï§ìwGAÜRvh5L<ÿ”×üp”{F?_I¥$«§æÌ9;d_î΋Ù|.® É˜R–cn…”K“=ѯÛõP¹û“ð7Ô>æïmþ}6ÑßùVC~äÉì‡j(—‹d"P%8fñŸá®B«hdh/PFŃt5ÿPè_’ZÉš Ô<¸çSô '^¤ÓèãVÔ¥´Ò§»;ŠI a$AÛŒ%ŒõL™wácú?;>°G i¦C™„|fW5W»…ãå;¶™3è üŒÌ‚‰À7ž&‹Òž}¿ÓÀá:o¼H`•75Ý«MÄå‚›D±ËМ4ám§]Dz¶pø¼2Ô )ºç¢0jPH*+½}¾¬ú°2<Ö3Õ4åG('Þ^"“1IÒiªù^¡fcÀ=ÉÖt»ÝÃr1Òñ%Út—YOÌ·Ë Í+ÒÞavz†žAÅ üýÓ¤ëfå»{³ã–µõ=ù'! ½oÚ>¾R÷{Yíð=_ÙgÊ7’öéÚ`Èû6 yÏQ¦þSŽy‰ŽçM:îLtG-³ÓY‚uLü«x&<" b‰h¢Ž§å&CøüFì>˜‹gh»Ã!ÅèØL;QY÷•ÏÛò<¯ðªô©žnÜ-Ó½»ð^8ÛööT6LJ¾ÆüŽg¿ §™IDAT4‡6Ê®K+gkLßÓï_laÎÑqÜ{PpÓÅ®CoâB/Û$LzµÓ–w«Ú· v—êú±—‘æÎò¦;Óã¥)цB#þn®Jؔצœ…|ÿ¬pÞJ¾ë:’Ò¸À¬ï¼X¥ë3ðm½ß¡M(Í­*:Wcð-&-ú¾åËÎd¨J„6 ý/Üé®ÕdÐ ¶×­ã¯ÅèÊdÄï‚›jæMÀˆT¾;Y”æj“"Y&5Ÿ!-È '/žø ®˜.ÇÕÑÄ;hœ¼`; ¨Vuó9ì^„gü Þ3á.÷¹Gž|±~q®pa~ØÏÌ6L¶§vä¹+]³yxÂt.ö1:Fß‚ïðŽ™IŽJs‘JqK»tÞ7kSŒ~L.ç_mFä[.ï×|'Ÿ¢Öu{Ÿ‹þÒšïÔ©«S¾ÿ>”È 4áièùg³»Ê”W>2yr³£¢ ù…µ&‘e»4§@~í¼‡Ç$Ü‚Qê¤6Î+ÚûÚ!Ó„N±ïXëaŽ#4ÒŽï-™a¨ #Çö-^™<)fâ⃾u®L>”¦´úX¶õ®-M“££÷U6JGž%d•CÚÇÓɧªÒ®¸©Åf|л²<|Ø^îûùá,ƒòλ—Óòšr…ºÝsBRűPêô––Mi÷Uié*s®¬ç•át”@ BöàÍ„ÕÐ&4Ã]seºy5Lÿ° µì Áa êò#Õu[ˆN@6\ãÆB£gz½ê)(o¤µ(—u>î ìÍpì8æ$L!rKbLÌ^JëÐýù^‰ú¿¯SÿCsC„®žÇ{. †k(î¨hO{NÐá˜,ë‰9F.<Öb¼—MLœ5ûÑ'ŸkˆÑã½X´ô®0g½èžq4kFÆîBçÖòúE£¦èˆ¼‰½¶Î+ZÒ7Šò…âfWgÓˆ/®Á÷(¦ÚdLžØ†‘º‘ˆätô"Ò’®9)Èk‡ö F0â0w#™uÁKÂÉqQ¿ú¶—÷™@•hÀñpV‰ èËIBòiB=dQHø¦,s³M¸'Tëæk|‹@ôÐÔÃûÎô=ž?´¼Ë÷˜¿ö_r;m@äa·§´¼H»x§%|S9Eííí²kð6ðÏÔPUÑþü{ù°¿Êr»ÈF^ôWj(í¾*5a%N–õ¼úzªDöIòF^âÈ£  Þ©<¸’,ß3eªv¤éÅ„šæÐ8_‹Ñ»Ë•¦]]¤äXlé?ö1hp'áãR¢[Uhv¿Ähê¦Ì½i¬Ñß·@ìµL—Nœ À‚`z-šµ&~Q´Â0܉Ñ[ŒPå5¦Rås[Š Ì‚­ù#Ft&ØÇ0Oj&Ê'Ù¿u!C`„Oþ±1²<æø¶ÒçùW¸èHÌÇúO‘$ê”›Bƒ+Só5´áŸûÌ¥W!ÍB¯&¯Æ(ÀF¾Ëé"5¿xQèØi A|( ß5¸+ö²ðí ‡÷-U"€+M±¦:¾Ô@j¿Xu+k¥…ü§'9j÷Á`ZñzøÁP&ôÅ‹å<À³F+ÐWà Ë_I˜C©Î¬5:drÅ Ô~švD@:xp:ðLyËSÌúnþBâ$­ÇšÇÂd®ÐÄX¬Büv‘ŒÊt« á¾õ•#±ó•A÷aûFLî Õ-$€Cøî&…¶Ø·ŒðÆÁëÒ@ ÎxG”Ï5.›òuCj§/º…–|ï1(Òw@î뙦ÊlŒŽÄ'v<´å,¤ëhÿ.ºMtG¯Cçd_º™9s“>Æ<ª;šãï†RM “©ËB˜¸ E{¿¢´åq‘j—QÌ5°NAãÞzÇ±Ðæ? áû';>o™€/*À1޳ tø–€ý¾¯P@@³$íz„;é\ÍÂ1 §BM¥- R*j†'zg@ çÌÏ<îwS\ØŠk›?çÎjff³­Gä>Èd°aM‡Ná7ã]þ~ÙEhr<4Á&(Å%p(ÇßãÐ~—éV5¼ièG2_ës¥õYYæµÊáø¯o>'÷i~‘™yò·—µk—»àè&LOÈ?ZNW›%¹ÀôÍ;¿Py§œ·V±¿Ã4$Ñ>GºxØ—Þ{’âiÊ!ûÕäýø¶Ê–Ž ?í3B0Iû5åc0gi²¹‡òÄü‹2]¤R< E¯AÞQü¯äµ¨×ð{|„Çónq&7qy§Î¨¼C‡Î+·oß’‰žjh Hc´|I ò®ª|ó–×Foœzä˜@)òîwu_)QNÿ”æ<㟩ӇÀ9Ô‘‘)™ Éò7´µ_ Ú lé°a›£Íp™nUiŽ L6ÞÆÄÄ»3³ÄZh˜—ùº-ÒömИ_å{ ‹šN äé¼€oz@\ãú–黹ÿ”=«ë{ÜwNq… ×3äëI¸N‡ˆmy‚W³E)ÆOõö{ãï‚_¿«í”Oy]¤ú–Yt£ 3,ðOK#[½‡9 ƒ*⑨h~ü»v¨¦!¹[{÷ú ÿ(Œáø Pys¾L ¦Â!X}>HÐRù?`HwMŸO÷lõΜ#¨¡”Ä7DL‡dGù®ñåt«ŠéWt¡S” °,\‰Óš0[éeh‹ÂÜcdg»9ûŒÝà±( (hDy\mú³Á.áš®+ïý°¯žÍ$Ù¢aJΡô ð`UàþµW¸õõd¨Œ·P—Û4aŽ :ÑäKL¶üêüX\›ÅùsGDy]¤–§]껆;I·»NEü+åIËqj?Œ¶TQÐä‹* ½øåç¬\É“˜ó­qú~–rS›ß XÅà‰ `ysÆL adòQº Deë  X_áÍÊ&/Oº|M6¹U½nUwÃâwÈÊ­È­ª¯öuVÜ„•Ð"ï†vûü† ‚Þ+)ï¤øÒ?‚¿)pyÂ÷"h—?FÚõvknR¨¼"y<‹kÜ,Sån‚-ÏH;Ž?·´B'\Ý^:Ü–£ä6L"MÃ$Õ…(ïb»r…‹ý¡ù®p­ œù‘V¢Í°>Ä÷v\hÃ?‚F¿ÚeÙÓqòrìjp:^ËEª÷Ä~Øt?O1p‘Sà"õd%ï‘W#©9ÿŠ‘pGH[L €î㪠Ûúôú 7ûMþ..‹†vZ¾ŠÍ6ü –ó«ÑÖÞØ²#\Àl€»,(ƒüСýC´m׫oòÊrÍó_Éœ¨^ ÉÚ“0«˜èïZÀMÞ㆛ÓüoIùùÓ­*xÈÑONk9㉱ò'n[lY®6‹Mtiñª lV’»[¸Âõ·‹ÔÓh.'­…ªTßÛ¯_ûl#w Ưû%|w^¹z¸¿ò³üJ‹cÎ|{ìR³¥—\yÜ×ù;UŠ?(Íg0ùwЪ•~:>ËKm<Ÿ¬àn¹Ä3~¬Œaa@ŸÏüR‘îdˆŒö\‰¡÷X«gÞŠYÒ+÷~àdŒ¼½¹sç:0›¾TƒoÄJ_—Ãh4ñ—Hv|Æéö\ µ½/ºÇÑK|‡ÓÕø2Ôñ G=q‡TæTk˜Ì.[#K`ñ±ØçïÖR¤qîÛï¼p?C¯|@z®½FWFøŽŒÒ ÁÃpŸé—N@å[Q¾”¾ÿnW—/6Ǫk&<˜½]8]ƒÑîݧÙöm.4ˆ…ïÓ¤ÈÉ™@-%P%^Pвë´bõ—;ûõºX7ä[Ð ÷*z¾´ßøp˜ðñLGሑ¿þZaU—p¾ýRܸ ‘±ñ‹}ËF“.ä;öÒéšo n±ˆSH¸ð÷­˜ì¡vÙKù&NŠž'ÿsìU¶ì<ýÏðŠ»á÷‰fp hí¾±Ë ß¢XØpì;A&=é8V{³×WQ;ok7é^L+cÜ·ü¦VË1k îÛ°Š´ÏÔ.Œ¤Üß÷Óý‹*’® ®ò>€Uú^ÆJrÿ;åZÆšÎQ'nË&Ãä¬?:©ípOzÑ_Ø€{øV…2•zÏRïÁXX/E8åäÑXªŸÔD=h÷GÁÓîð6ÎÎ_¦è[ž…€gTdkš›8)æc*£¤|’žˆZ…güDJÄðl¿KnÁ"'zn†Çàxh731ª4Ò鑳Ü1¿ þw¬TàAÇ5 uœ;¤g·ÿ°ù®›!êÁÜ ¯†÷zÆ«¸W*ßÿ’+Øy.„×Y¸ÇËa°ñ¢„tœWYá›F_L!o‘ŽfŸâ±zÇ«¼÷ÙÌ®ÝDZæö0Kàp_Œòš:Mî¦yEš ]mãŒ9BõFøé}>Ù ?ÌÖZ@øá®Îmœ>Mñn¾ ^š;Â=4éø›iŠçI¸/-zÆ1ÒöV¨3¬§+L»Oä=ô|$MŠù §Þ#Ïìø˜^$|Ód.t¦8\òvá é «žžß®Ý4Ènoë&¨ÓEE¨›12t#îѥ塀oÙbÜ£W#Ýß&°ð]d‡ ÔYÕ¢·iç/?¿goëwa?¬ìm•ÂäÃäßXª0 Ù†é/!JûªÍÊ•‡í´~ßb¹[Í¡2ì|½® åÍndÿ¶·š©à]ŽcQ” aȆvÚú+Þ^séÕ\VÌš0¡&…&Yæ<‘äêQ»Ül=7IHçc³±¬0[°ó¶èõÑ^2Sµæž–cãŽë0Wárè-ä @C!Ð먓øaþ_ ­~¿„ãå?¬”y?:Ì—BHÞ gX‚Gëo×m¹9|³”o…i>?|büt®»¢«ºc¦ûñýКÁ­GÄÄ ‡‰É>Ó4o ¶´Ôùåb! ÛŒŽ@8Œ|ºˆ´¼å¦ñ[˜šl#Û5ù°¤|F¹g„ëÞc“! ÷8‘š“…Nr7hÄçç—PhCKT£n׋4¦2Vy4>¶¦P$þQç <þ@Î@ ¿"ú—*ä / ÿϘ@%Pcðê¾Ðî„ö½“]]×;âS X‘ œJáŹ¼àv+ ÿ;È`m¯}ÌÚú!^„;)Téµv¶þ—oÞ‘±žièd'ÇLj/‚LLr¥¼Z÷û±ûU-êû½füè­oTšZÔFkKZeAž… õ~êø¿ i~ät y1jÜ!:g,€aïÒ–F˜°zߘDOÔO¾Çi¿¤| |B×SÍöÄ ¤x°÷žG’|qÑÐYQŸ$ybÿ¯¸ó|Œ 0&À˜€¿ `äŒÐh){¥þAv¥yDäƒ88öIЀïzÚOŠOš±F±SzÐïýfü­Ö‘‰ˆ¿ãIï¡[¡ŠûÆ^"—ÊÄÏñÐj·k㊎¤ßv€0~&·Õ·ÿ`ž 7„ÂΕ…ooýJ '˼–ÖàžÛö’'vý5h Ab0ù¾? ºc¨>Øél4þêî]çùÞÇxÖF §öoÑ®ùŒ—ÜãRJ«&DÏEéCJñH»]𜖔T½0qyň'“—kiŸ‚)´ÐYž“÷ Ýl¥¾ÀïÛGG{:Ð1êT“]¸}ž·L€ 0&ÀüM€5àùDã&¬‚pûaоi-lUsqxCpó¯Ðio†y ´d ص>И´vŸ0õ¹ˆ—†Éd!Òéˆx î†tðåM<­¤{3&C(?­û^”ŸwBh‰ðððdþÞ0ª! Ôýšæ|À·°§ÇŽÍ€¶ùó,#ãN‡)¾€Ö¹‹®[ýíÚcN<_‡`ß=öÝ‹÷zÚ(½æwb÷¡Ìˆú‹â­¤¸˜'|ó²÷µpGŒžnÌA‡tîù¯~´iŠ3í2LPN))L„›i˜æLä)ÈEÉd[Žçõc¡{ç¡>}P†;ѳ#E/y…Zƒü·§xãÏ’º‡sÖóo׃·L€ 0&À˜@€W[ÓfA1L0#W…BQ-™¿ã*Œ03ˆÍ=€0»ùᘸMsdlÂeˆ¿Çñ§a:R`îAM o³4Õ¥´Ñížá~ºåMÑÊ“Ï÷ JÊrtBBSßs”ï÷´EýêûÆá}&À˜`þ `}Èü‘çÁ˜° ûÌïOkœ.Ç­3ݶRgUfè1°ûn ³”ƒPyÿ /%/F&';ÕîƒWáEôŽp¶è@ÞTì<ÊÚBÖù”UŸgL€ 0&ào,€û›(çǘ€E`dl¶ۘEÙæ‡ñ÷K£p×+dzÁBÏŠ‚¹;lC¼¢s8ä{‰îèuAGÚpäS‘29.`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜@Uø91Ö‘Ó~ûIEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-compconn1.svg0000664000175000017500000011651313553660046026743 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-09-20 23:43:48 +0000Canvas 1Layer 1Network NodeCompute NodeLinux Bridge - Self-service NetworksComponents and ConnectivitySelf-service networkVNI 101InstanceLinux BridgebrqDHCP NamespaceqdhcpMetadataProcessvethvethtapeth0iptablesPorttaptapPorttapPortVXLAN 101Interface 3Overlay network10.0.1.0/24Linux BridgebrqLinux BridgebrqRouter NamespaceqrouterPorttapPortVXLAN 101PorttapPortInterface 2PorttapPorttapInterface 3Interface 2VLAN 1Physical Network InfrastructureInternetProvider networkVLAN 1 (untagged)vethvethVNI 101VNI 101Provider networkAggregate neutron-12.1.1/doc/source/admin/figures/scenario-classic-mt-flowew1.png0000664000175000017500000011217513553660046026060 0ustar zuulzuul00000000000000‰PNG  IHDRŽÐ/bëúšzTXtmxGraphModelíZmSã6þ5|„±l’„ƒ¶3à ^ûQIä—ñ|²B ¿þVÖJ¶cC Ö‘†’L&ñêÅò>»Ï®V9 .V¿ šÇ_ù’¥G¾·|< ¾ù>!ÓSøR’'-GZ‰d‰*Á]ò/C¡‡Òu²dE££ä<•IÞ.x–±…lȨ|Óìò´yלF掕ànAÓ¶ô[²”±–Nüq%ÿ%QlîLÆSÝ2§‹ûHðu†÷;òƒ°|éæ5s•\‚ç0úµz¼`©R¤Ñ‘ÖÆÕ3­v‘‚e¸—L'zÄM×ø |•¯%á €_0é8…Ùfs¿"õK*ä“ÑÎ&N$»ËéB]oÀ S,W)\øòL"¤°Æ`¶¤EÌÔÔE!¿·J @’§4ÉŽ#AÁVfa’¦<å3žÁ8ÂA¡³ò -)³ô–‰Lxm ЃN³&dh^ou\-•¦IÔÙýæ\J¾²O^W0ê\`ÆÚ-’àŒ¯˜OÐ[a‚v²©¬Êa—¸fQ¾‡BŠ–Ù™+€ábÜ÷8háýGVHšpý.-Ùb·w ä¬oÓ&:ÄkÃC ux̸Aè GÖйarÃÅ=ÿ4 “üºRÚô½cø\ÒB¬ߘúÒØ)_k@6þ±VtR6¥ž+°ÇùcÕXá½mÆ>  ƒ'aÈJ û^¦—mz¬Eßÿ¹ÙZö°(yÍhšÞ.ßÁ;Íb•,—ê63-ai‚_b1hÔ”„Ëè¢(¦díÄÚRË”Fg¦d¼ˆ)M1ºÖ4Í–Ðð’ óˆg4½¬¤³Ê·½&$ì1‘+ñɯþ1-,¬Ö¤.U›š€eËsy+È@r@bûw&åꜮ%WHÙu]såûtVè8} Œ_‹’µ”ßa”TD »`”Ö^[°”Êä¡™& ‚S GÐ9á%Uk²Ÿ+êzÒÝ)‡Þò¤ä&ôÂé–Hèf :ÚBÉ®¢}·“©¯tñ !*Š¢¯›–øúSdðúŸ!0£r“ ׈oÒÁ{Þ#ô}gâSÎP߉m½e"PŠ?,>4ÜWçC¢²Ãý¢YNͯþº>¿ÉÝzÞ•°€ÊCåL¿$›ƒÂy¢ DÕrÄN‚²A¦­Ð[Á¿«m3ìúlÆÙ± Sÿ„Œ''䦸ò!_Ý©b¸5ìÑ•µ¿@iB?`FKYXïÿ‹ùì±A¬“¨×÷l8ksËæ+¼uçž@ïÐÛ¼r'0êÚ F uWoÌîÝÖÞ/–µ‡á†3Å`dìÆD½šÝ†ê†ctP$4¥)בprV…Udl‡;!ýÃÝظW†Æ)ç® ev¶C®’RØ[?—Ú’³³¦}a ©,DÏøÖ„Ö>ûVä-—ô É1Í•p‘ò5Ló¾1uÔô7‹S=¦v¸ÛØIâ‰îÖÎR,_ÿ§c¥3 ¶ƒeð~Á’xè5˜ŒqæÿÍþËlDí–·vN;RK²µ3~¤í­hÿéžq¥FíCIzå{ðÉ›Dd8É/ [zk{W‰ww‘xçîªÄöñ·Lœ¯E—ˆqŸ•bk{Ÿ•ât‰ÌؤKÌøßJ—ö |¾¸´U6ÞÎ%Þ³nL|Lõ\» l¥%%]„:У¯ <ú F[õ!Ã¥;¢ÚkëP­ûÚõœÕ¡ŒÂ¥ì³ÅÖC®¸K.öYñÛÿè:bëØ›#6ÿ0ŽØœÁÕªš »¯Âeõ[ÍGÕŸ–ƒËŸ{ž¤B IDATxœìÝwxåþðzH¡wU:b»þlxõ**(ØD5 ¨ ½C褷z $ô$T±¡¢H±€½¢÷Ú½\Ï;»Ù ›dëä|žçûÀNy§ììÙ7³3»€ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆH¾-@³~PÑÃ47[¦ù @Ù ­]è”ð.œÛ}qÚˆpÈÒ†/µ °+ž‡vÞ±,k°ãVY†Ý€uß÷Í]AZ§P¹¾ï‹#Š…f5=*ãøÙ`5œëy®ëYÀ›–ñKÜÚ‰ð¹eüÀ@®´ˆˆäŸµãHc=L㯎c‚ÙþxO°`±[Ç1Æsg]ÖPÆ©ã<áÚqLÀ…_·5üÀ€ë£1–éÊøÙ2þ=%-ãkÁu;/ñÏ&\P"'›DDBʽãø!j寎㠖vöÃèX…+ug8l©£pÝßGÝÆ'vލoYæyÝ4õaÜK–u».ëeí8žð*\÷‡µ®2ç)*Ç#ð¾/f!x¯_^·w›ã¨×íhh™®¹Û¸?Tð²¬sbý¸y‰¤l )÷Ž#¤Âõl†:Žë8zâ~F¥®ŸÚ½F–e~  ´ãÍÚq<ߎ‰¢Ðq<à>yñåu»À'ÊÀ8ƒxÜ2Ï]–é¬g#u™e|Ëð /Ë „HÊ&‘òÔq$€K-ÓäÕqŒ0ÀæøŸ¤¸Â2M^Ùv€ëµu·XæëkþŒë£×Î×Ïʛ˸@6€¿àWâ¯Ç€ãš<wþR˜Ž£/Ç‚U | çëö €Npno^ÇÎ=–¶Ê8k¶ç0Å2í8Ëðæ°|mþ¿«eüfË|îû’5ÚF1Y;ޝ[þÎk¼u«Âxƒóºð¸9Ý…ÂyŒåñ0˺¥[†ÿçÇVÖõÙk¶_ÆÇwÞÖÅÑIµÊ¶Œ³v`Þ…÷Žc'˰o`t&ò£ G÷u+à¶ lëVsº@uKØ’ÇòwÂs‡Å?;Žù=üqÜùSA;޾ —À¸$ÅÛô›`¼ö}íT56‡µ· »Ñ2íúâpfÌl;Ìÿ¯6ç) àCË|M,íùš5ÚF1Y;ŽË̱<¾ÛœÆSDZ\ïž< Œ¬¬áÛØl£\ßฃ²€k-Ã÷Áó2θµå¸în¨eXOsXg·i³at"¬mü׎Z¶eÜyËÿ½u[Áùfô öñuA:ŽîëVÆÙGk‡k\ÏÖ@³­RZZ†Ÿã3¯q€÷Nšõ¬¬0®oÖ=|ؾ^ãx·eoë”ßcÀÇ?ùzc€zæ<¥¿c¡8ŒŽœuÃaœ ´gwYÚ÷öºuxÚœ·šeXEûš¾ƒqƒL9ÿ2‡=çsÊ\Ne7ØÆYBÇùÍš@l£ˆˆ˜¬Ç¥0îŽüÕ|ü1ŒNЧŽc‚eØpý¸ö˸Ŗá7X†[¯#Š·,ó{o25àú¦@g‹¢`œíq kÜožcá ýrp펶¬O¶eøß0n ©ãì†û5Žÿãã8÷Nu~¤ãè¾nÖ›ÎÀx“sXgg½¶¬ ×8zꤕóã?¸Ï2ýí–áïÂõŽYO|½«ºûÖ© Ç@a;ËÏ]ÕÍÍyò{,”ð œÇUeËôZ¦Ÿkîíu óÿÛa\²a=«Y FÌ1_pëú;Úý/€*0>rvŒ_ai+Á2Ü—¬ñ÷6Šˆˆ…{Çp}3O„qý—{ÇÑz‘û·6/²Œ³vD¼…s1g|ãšÀùQ׿¼aþš¹lǵQŽu©g™÷¸¾QÀ?,ãßó .Û2|ŒÛ<îGk§q. ® GOëVF'Çñ½›U`œqûÑ2Ÿõ£6vZ†} ׯ[‰ã äL\øãVu r ö¸ó¦Ÿ‡õ?ãøÏKA:Žù=b`t’ÃOÀ¸“¿Aë•W§ªœ¹œæK²Ìw'Œ?0ãúÐrpí¤_ àËcë#ùÍo£ˆˆXxê8ÆÁy‘ý²LãxÓ|ÞßÔ¬õ»Ùw8[?ú¼ÆPÆGo£Ìÿ¿×NÎZsÞ«,Ã^Gî3¬oPßÁù¦Ÿm~›Û<îGk}ãlUA¤ãè¾n€±ïÚÃèD} Ï먎ã5–aoÀ÷a<ñ×5Ž= sÜù²MÖºùóôÇü Qp½ÅZŸÀø˜Û½ƒ•×ëör8;~îÚXæ `ªùÿ7a×ã* Æ÷_:¦ÿÎó½½n«À¸‹þÙ<ÖÏýNwÂy}&`|Äí>ÞýxÏoÖøsEDÄM^ÇâpýN;kDZœ_­A7D™ÃZ†[¿ØÎÇûŽÛÇÜ–åèx¸ßÑJ7`8DÁµ3ñ9Œ7¤r0¾\Ø:ß?,óeÃû›•·_ŽqïL·Dþø£ãØÅ2îSgÀÊ!÷µ`ê8w[¿Ç Ö[†/ñ²mVþê8ô ~Üù[~~r0 Ʊ”ßcáF˰/43‡ÇxÎ2ÎzG¼·×­ãzÇ×y’à¶ÿ†ë]ÑUàü Âø B÷›ò›5þÜFq“WÇpý kÇz¹ûÝíñ'pýn·–Èý&b½Î¨¡Ûø Ë8÷7B÷Fêší1Ê„ëB¶eœ¯ÇnóíDþnñGDZ òÞNG=m™ÇŸGÀøe¡¼–ýò¾‹ÕÁŸ_^c(ÜqçOù¹«š0ž·ü 10žã Mÿ¸e½¼½nçâÂg•KÃõ‹»7Æ8”‚q×1þ5x~=å'kü¹""âæBÇ(¸þ‡µãX ÆÅñžByrÿªJ oÜÖéܺì˸~–q·Z†ÿÆ÷ºkãÂzOë³ ®_¬ã¸ÞÁëiÞ¼ø£ã`roã<7XßLoêþî8ƼÖ3AŽ: ÏwÙzâïŸÌï1þ¸ó—‚t r,4„q–ØS›øŽÐÚyóôº}Ư¼X¿oћٖùfy¿Æ2þ%/mä7kü±úå‘©ãã£tCÞ¿¨RÆÅñCa|7_µ<¦-ˆâ0~dŒï–ëßÎ|E¢¦0~Áä9ׅ⺬hû»€þ0¾(=P×úª(9šÃ¸6ðEÏÃx]Vô2m _·¾ÊOÖ‘¹"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""à<耶Ÿ0À=h»(š ã¹: €X?™Ãî2§©à5sØ÷b¼ O ¸ÇH°—箌ç. Ñ:ˆˆˆX ;ŽÙfÛ ÐvQäKÇq8œ“m¢½ Ùî1ìå¹SÇQDD"š:Ž‘Ã½ãgc7/0§9 *aá"ê8ŠˆˆD kDZ¼åqg›üã¬Öx¥,óÕ°À¿Íé?0@Y%¼ç$œP@C,ó}`ŒÎò¹ÅôpÂÒÖTq–i¢L4×>4·µ¸eš¼¶Å›(]ð_?ÈÐÜ2M~¶Å]I£| à˼ïgïEî}N_yVчýb]÷XàKs™ù™×ÛvçuŒXågæµ^ž–wÀGæÿ'›mT±,¯¯9ìbËn‹ãÌžU€I–éþ붎ךÓùº-îŠÃè(ºOï(OÇ»aìsG'ú[c v6Æóp¡ýb]÷wÌP)ŸózÛîbð|ŒX;ôùÙ‡z®½-oªùø sÙí,ó¥›m?n>> £sšßçÞºÿÜ;Ž7XßkÎ[cRDD$(¼u¨££àèxäÀè4]çYS³;á|#­lË6‡9>¼Å|ü+Œ3*Ð Î3*eò±M,ëð¤9Ìú&|)€;Ìÿÿ @Msy÷YÖ³N>¶Åª¾ež 0:•á<£õ:ŒŽ«¯Û⮕¥ýÁ0ΖýÓ2,¯kKïGîªÃ|Ù/ÖuÿÀsÿÈç¼Úîl¸#î|mË—mò´¼ÍÇ¿˜Ëêç~> ã˜\a>^‚‚=÷ÖýWÏ2ÿýæúF§Ö¡ ǤˆˆHPxë8Þi™&®geÀùÆö#ŒPïFî»u³áú&]ÆÙ®:n0 Î3_ÁøÎ×uè çÙÇÇÜÅaÜ-û(ŒkÉf™Ó|`:ŒÄçZÖ½}>¶ÅêA8;À,Ãïë›»¯ÛâÎÑyùÎ&‹Á¹? Ûqôe¿X×ýY˺åwÞ m·c›|é8æÕ–/ëåiyatì -€­æòŽ˜Ã|`þÿnì¹·î?ë5ŽŽ3îïÃxm8ä˜ oÇ,ÓŒƒë›tŒ7JO×Ô9ιߤKÃÙ‰qÔ§ðÞqÌkú›OÁûµ‚›=¬ŸµžÊǶX9Î’~ ×kòÚZæ­Ÿmq7ÙËxGG¨°G_ö‹uÝo²¬C~ç½ÐvgÃ÷Žc^mù²^ž–W Æ™LH„q–ñ8Œ3½„ÑQü€¿Ô@Áž{ëþ³v­eݶ‚“"""AQŽ£UcƒƒóÍí2s\6\ߤ»˜ÿ ã PIP°ŽãcæãàzÆñ.Ãø¸Òq d¦û"¯m±êˆ¼Ï:ý ã&‹‚vû™ã¿€ó«s¢ì:޾ìoë^˜yÙqôõ¹ö´<Ç!‡Ì¸Îüÿóßw`|]ØçÞÚqÜ Ëüÿ1ä¾1ðý˜ Š‚tŸ7 Þœ¦6œol7›Ã²ÍÇ+ÌÇÍǧa|ìVÆ5jé86·,ï9¢›,Ã.ð€ùÿÔ5Û¹F‡ìS×›ùº-VÖÇÁyÛqsØ›È}[~:Ž—XÚ£ƒ}·eXa;޾ìoë^˜yóê8®€g¾¶åËzy[^k¸žÕ{Ƶ”[†6§-ìsoí8^×3•O˜Óä˜ Š‚t›Àõ.fëW¾œ…óÍÎzgðjoÈôRß"Çâ0¾âÄ1ÿ–ÿ¯5ÇÇx×2üŒåÿÓ`t¾|Ý«(S,Ó¹ßíëéÎÚütKÀ¸£×Û¾*lÇÑ—ýâmÝ 3¯§ív?F¬×ú!mù²^Þ–ãøs oãìŸõÎævæü…}îݿDZ€p¾* `ǤˆˆHPô£êËa|Ôæøž¹ÁøÞ¹ú–ù.‚óÆ‚Ã1æ´¿Ãø>GÇÍp}>ס$Œ³3šã¾6§³ÞD`œ_Ãr¹¿ÇÑ—mqW ÆM8ïÀ83õ/äý]~ùé8FÇe’¹ÞÀè ;>Ž-lǸð~ñ¶î…™×Óv»#í8ú²^y-oƒ9ìG8/}p ûÙ\‡Â<÷ž¾Üzö|”9¬ ǤˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆH„»@*œ?Gõ_§ÌP/„ëe7Ýáüíà¼~ëVsºZûÍöŸ PûVMÌeÝ„eIÑ ¼ Ž¢’Wц8 çö¾ã'?£ò˜O¤H*çïš:~”þ5Çàúõ†jƒìjÛ;#@í;‚˜v(áeºHb‡(Ó¡Ž£ø‡òÊ•òªðb„ó=f9¶{1¼o·H‘ÔÆ‹ã€;`ü@½C4€Ap¾ . úÚ_0ƒ˜÷2]$1T± ËàÜVu¥°”W®”W…7Àlû€xËðƾ3ǵÀrE"RmÂÙ–^¦±ž1z%HëJÁ âåæ¿?¨åaºHâhŸÃõ GG),åUnÊ«Â)à³íË=ŒïdŽÛàçåŠD¬¡0^³.0]SS¼×SöLðžÙη2Üî¡æ4ÍŒð½ùø+=Ív»Àø«ï<€_`„UeKŽÉQáµ À¯f;o ´Ûr3Ìñ<¬ÓfsÜE0Þp!wgçËô¥aüuú*€¿üÇ\î30>Bó…#ˆ“Œ6ÿ¿ÉÃüÞ‚8?ûšX àKsúaìïŽðÄþØÆâîð°Yû Ž£žòJyå*ðƒ9oyã/3—ûªm‰ oÂxQ\]€y«8cÎÿ%€up^'B½Ü¦wña?˜oÎã˜Þ˜éæÀx1;þÒs|åâÏa8dÁÙ1q„ZIËr} b¸FÈÀ~=à¼Ð>ÀnsÜá¶ÆEÔŽõ´.×G÷‡Z§ÌǸMç)ˆó»Ï¯€óº¯Ïlð¡ùø}äbm£»PÇQ Oy¥¼òw^ÐÆìbÆ÷6ÛZqvDŠ ÇõÕò9_€•漋”±Œ»Æ_oçázw£#ˆ?ƒqý›C8ƒ¤ƒex‚ÙÆoʙì~ ¡eúÆfÛ„q'œC~‚ðþÑÏ`sø`¸LœR?ˆ ³1\e>>×çÁ=ˆó»ÏK8iN?Î3/Å༦Ç=ˆýµîÔqP^)¯‚‘Wma<Ÿ4ÿ/"0þ2;ϧèóRÑœ÷'<ŒãÅÖÏ2ÌÄîw;^l®+•pÂçX?kßáa¹ÿç_ߎ ñG—ð)€³0Þ j¸•c¹›<,Ã{G˜f[iYo÷ Îï>w|Är ƾ´*ã£kûsÝ©ã(þ ¼R^#¯JÃÙ)%€— ¯äùÇÇ+ùý ¾­9ß^x~A9þ*µ¾hA|Û´ÍáüÈǪ€wá9ˆÿ‚ç0*ã¢ï`|¬ø'ˆkÃ"yÕxþ¸ÃÊ=ˆëý©9Üq÷ž{çwŸ?i>æe=Â5ˆý¹îÔqP^)¯WwÀy&ø/OxY‘"ËqÁr» LWÆ…ß;`üuw­9ß*/Ó7…ó/iG_ë6­#ˆ7» Ï+ˆ€3h­ãÿ€ñqwooAìXÇÓ0®+ú‡—ºOA 7›Ã¿†ñ׺{çwŸ;>R{ÚËôÃ5ˆý¹îÔqP^)¯•W10®cut8ç¨êÃ|"EÎx/’y˜®9ÝŸ0BÂñ×äx~Q^aŽßjæÏ öö|Œ¯‹8ßþ‚Ï‚oAœ`û¹?FÉ/oA\ ÎÖ<wzú Þ×}þ¨ùx¸—õH‚k'ÀÛèNGñå•ò*yUÎýþ1€V…lOÄÖøÆ æ2/ÓDH6§Ù#0¬×¯Ä{˜g˜9ýË01áùëÚ›ã^…ó«ÐÜmÚ8_ áKÇX¦mâa¹Ž¿–{zçÎ[@%ËrÆÀ5ˆó»Ï×byºf¨$œw¨:‚ØŸÛèNGñå•ò*y5Üœ6Î3¿"âEŒßç$Œ»Çî‡ëw_¹_$|‰e¾Uæ°Åp½cî&aÿÆ_…þâÏ`¼‘84²ŒëlžfK² +çw’y âÙnë3Ñ~®ç·„qÒ_êâÂò bÀøx…–²Þ¥˜Ÿ}^Àqsúqp½KÑzg¨õ.Em£;uÅ”WÊ+çU Œß;ÿ€š˜VDL%à¼KÎQGa|ÙŸðü‚€êp^Dü5€pÞùæé/=ñßp~‘î^9–å¦Ãõ/Ö›-ã¶šëñŒÐòt±¹ã#–ÍýâøË?À[æ¸ó0¾?l‡¥m_÷BA\ Î7kùßç—ÃDÇ×f8¿òbr?¯þÚFwê8Š¿(¯”WþØÆ‹,Ó‡q­§šäC["EJŒïÖÚãzÂøkí=‹àýšÊ0ÂÊñå¬ßÁøx(¯_bðG ãVÀù GaüN­û/1DÁø¾5ÇÏJƵBmá<;a bÇ…õŽi­X_#qFÿ ¹ï¼ÌË…‚0÷Gäb ûZXà 8¹7œo8îo°þØFwê8Š?)¯œ”W…ÛÆëàúGˆ·r¾E$BXƒ¸lˆ×ED$/Ê+‘S‹H¤P^‰ˆ„˜‚XD"…òJD$ÄÄ")”W"""""""""""héééÃÒÓÓy¦vÔŽÚ‰üv"]¸íOµ£vÔNàÚ‘0d>¹Yát ¨µ£v×N$KW^©µS¤Ú‘0äñœÚQ;jÇ^|Øþˆ}^ÔŽÚQ;""̦ ‘p§¼ 1ë_ù¡^‘¼(¯DDBLA,"‘By%"b b‰Ê+‘S‹H¤P^I œpOç-àý·:VÀ.­B½"bo â²C^= àx @ëÐ®ŽØ™òJ!Òƒ8 ÀõæÂbu% Ä!éyUÀ/1×c€aü–µˆß)¯Â\„ÞöîâV^0 ÀO¾p»9M üÀÆÂºƒ0:kŸ¨ ÑœÞÐÌœ?¯¶£`„èþ°@YsÜ-Nšíe¨ëaý‹x ÀR¿CG 0»±ò*$yu?€7aä`tÿ  A~w„ˆ/ì’W¶¡O5ˆÿ0@9ü  $Œ°;   €«œÐ®Á7ð#Œð¬`€4syµÝÂlãFÁÕÁjø@G•ÍöŽ˜óxRÀY¨ã(¡¯ó\"t;"=¯ÊÁè´:\à;ñÜ"yŠÐ×yÑ¡O5ˆ¿‡®ÐÆ_Ïe<à3탌¥àÄeÔ1ç0Ànóq^mO°Ø²>-`üuÿ¬9”9¼²¹¬š^¶CG Š=S—‹ò*¤yU F'ógÏø¼õ"ùd—¼²-±#a>>a>.  ;Œkqþ `€¸qiã|à0ŒU¬Aì­íuyX¯q0>ò9g©_`ü•ï‰:Ž"ù ¼ Y^U° À{0Î8ŠHQeƒ v„#Ü7…qQ7ÌßЮAÜÆ‚UÌé:À5ˆ½µ=À|Ëú´Ð ÆõGk,ÃK˜ãŠÃ3uEòAy’¼*ãzÊ)ð~Ùˆ6âça\ËS @cÁ×Fÿ 6€^öÃèÀÕðªù8êm_ ã¯óë`\ktÆ_ôõ`\”~/Œpg®ƒ®qñåUHòªŒë%ø!¦€M)jlÄñ¶Âø(æ/ aniñü ãŽÄçaü%ÿ(Œ~ümGÁøëÿ[Xãc%¸À'ærø ÝuEòAy’¼šaŽw/Ow`‹ˆÝEh‹H¤¼ 1±ˆD 啈Hˆé¶w‰Ê+>È|ðê“;Ï<±½óÏÇ3:R¸ú £ÓùÛ;ÿ|bÇc{gvlêç^‡ÎÔùFy¥¼’ÐS^a'w<2ïÔÎ'~ýéÃ%üëÛ俎¨Xÿxˆ}»“?|0ûü©]Oüzjçã«>Éz¢Ì…Ÿ)±;]xaÊ+å•„åUubG—´³‡’~þï9äO‡UA®¿Ïà™ƒ}=±ã‘µ¡>$ôÄyS^)¯$|(¯Š ÷3¼ùÔ®'~ûï{ÉŸ¨BTŸËá©Oüú~ƃ7‡ú˜ÐR{§¼ R^‰ƒòª:¹óÑeçNÌ&ܧ q},ùü‰Od‡ú˜ÐR{§¼ ŸR^  ¼*’Nlïü￾ÚLžËR…¸þóÍVžØÞùçP2h IDATZ bï”WáSÊ+”WEÒñŒŽä¹Ýª0¨¿ØÁ2;õ1!¡¥ öNy>¥¼@yöqÛûñŒŽä;UaRÇ3:êÅWÄÙ%ˆ•Wö/啨%¯l+OÄ™ª0)±Ø%ˆ•Wö/啨%¯l+`Aüý6U˜”‚Xìò…ºÊ+û—òJì’W¶° þn‹*LJA,v¡¼²)¯DÂ\à‚x“*LJA,v¡¼²)¯DÂ\À‚øÛõª0)±Ø…òÊþ¥¼ s âo֨¤ÄbÊ+û—òJ$Ì.ˆÓTaR b± å•ýKy%æÄ_§ªÂ¤ÄbÊ+û—òJ$Ìì u¿^® “R‹](¯ì_Ê+‘"èxFGò«%ª0)±ˆwÊ«ð*å•Ht<£#ùå"U˜”‚Xô…ºÞ)¯Â«”W¢¼*‚Œ ž¯ “R‹~ÂË;åUx•òJ”WEÐñŒŽäóTaR bQ{§¼ ¯R^‰òª:žÑ‘ü<%¨µ/{G/ÚÃÄ䬰­>Ó²8dníK ê¾Q‹‚Ø»PäÕ{ÛpÕ¸4¦$¥†m͘ʥÃVñøöÊ+ *åUdñ¬ Õ¾ìTŽXt{ÞþžïþWØÖ;ŸþÁœ÷~ä‹óöóо•AÛ? bQ{ì¼zoÛ|®³žoì|Ÿý,lëÔ›gytÏ\2l-oŸ¯¼’ Q^AÇ3:’ŸMZ½4w/3Þø–oŸý3"jÇ[ßqÔÂÝAÛ? bQ{ì¼Z2lo}›'^û$"êHÆ»L»Jy%A£¼*‚Œ ž´JLÎâ«§åëýõêé_Ù{ZVÐö‚XÄÞ;¯R’Rùξ“3:h•˜œÅíGÿåŸÚ¿š—”¿†/eÿ‹ék°nBoÎ>âiÚ¹vɶKˆcT± L¸} Süäór“³‚¶ÄbvÈ«”¤TælxÝ?µ"™—Ŷåˆe¯s÷Ô§˜Pë.XíiÚ#LÛ‹×ÕŠe±bqlpí .\ùšÏËIIJU^‰ˆ!`AüÉÈ UbrÓßøÉ?•Æ‹ãÚqðÞŸ¸!mëÔëÅé‡rO·eï:^_¡/{~oÙÆG[—cÕ‡vsËILÎ ÚþQ‹]Ø!¯R’R¹gõaÿÔâɼ4öb[x˜Û'=Áz5»pÞŠÜÓí^4•7ÇÅñʧ2mV ŸjÃjw.`†ËIIJU^‰ˆ!pA<,h•˜œÅ ¯žËG}ËE3ñ’ºqŒB)Vlûmúšö¿ÊÇZÅKViϾ3û±NíÎ|¢Ëõ¬ FÅ_Æ{¦äºWÏqÅŒ»Y®VoNÙg´™:ç!ÆW{†ãs|[£ãœý£ »°C^¥$¥rûòýù¨l®ü/¯Ã(”d¥¦ùò´½Ü¾xŸicäU…kùüàGY·ÚìÚþV- F•kÁÏoaÆòýÜ0èÆUíÌÙ‹Œ67¾t+TêÀi }[£ã¨¼0ˆ?´JLÎâêƒ?ø\«6Ïa›òõy˨Ã\q˜}ÿYŸešåŒœ¸zG*[ǵc¿Ì¸bYVC)6|z+_Ùý‡?ÝšÑÇrƾo8£w F_ºœ‹mnœÂÑW±OÆ÷>­CbrVÐö‚XìÂy•’”Ê­‹²|®-É/ò’Øš¼ý¹%L±„ƒn¨É2õ9~·Î˶1­ùÂÌ,nùk ÿs:WÌYÏñÿlÌ躉\°`7<Ü€ÑÍF1ÍÑæäþlTºÍØëÓ:¤$¥*¯Dĸ ~1h•˜œÅ9ßù\Ó“Ú2æÒ…œ“m<^¾e[Ä]Æ®ë¿áŠŒ•lw5{§ÇÅ‹û±z\{Ì0¦[¼°/kÖë˱»¿à„§³Üõk¹ÀÑnúB^݆O­ûÚ§u0:ŽÁÙ? b± ;äUJR*7ÎÝåsÍïrc›åÒ9Æã “‡²uL3&NØÉSG±ML+œ²‹k†vf˜k8tª1Ýš!³võ‡9uV&gÜ]‡qmÇ3ÕÑl^º1Ÿ·Ã§u0:ŽÊ+Aƒø£AA«Ää,.Ùû­Ï5êẢY¶\¶íkÎ{ckÿŸ¿ókÎJ~˜ñUžà‹é¾­CbrVÐö‚XìÂy•’”ÊÔ)é>×ôû1¾ÍH.t ›”ÊÉýçrÁ¤t¦Žy™­Ê¶dŸQé\: #kU½c'Ó-Бµªuäø é\Ø­c+ÿO’ÎÅ=oe…øö9Þ·uHIJU^‰HàœØÞéçÿœèK~˜”JLÎbJæW>׬¥“ظ| Þüò~NLÛÏç:¶aL£—9<ý+¦¬[Æf1³ËòÏ9cNÖ¨ý_ØlÌ7cNÖ¨Û‡/mùг׬à¥å«ðÒç1yÝ^vº$žïIç4×Áè8~ßüu¢OlïôC¨ ‘pì¼JIJå²q}®ÅƒžcÓ˜Þþè,Î2‹ýolĘšsâè\6ì%¶,ÓˆO ^ÏE}`­*÷rÔ(c¾E}`­ªp̨\:t¯Œ‰ç•RøÊ°i|¬q,+_=žó}\£ã¨¼‘9™ùÐÖs¯w%O÷ J%&gqúÖ/óQg9âålR­,’Œkúÿ‰1nã>ÞwqF•¹žŒéÅ굟ãÀ Æ|Sfõaõº½9hã—œ¾õsŽŸü"[ÔŠc±Yóúñ¼î Ÿ×!19+(ûæÜ]yjG§u¡>&$´ô…ºÞ;¯R’R¹häº|T'w¹—ÍâK(Îòunc×¾«Œq/OçC ãUª Ÿy¢kV¾‡Ã_6æ{¥çý¬Y¥G¼¼Ž‹F®å¬®]ئRY+^޵[uã¨!k}^‡”¤Tå•òª:¶õv§v<ôÛO$’§z¼“³8yÓçU‰ÉYß/ÿ=‘ÈS;úíýŒoõ1!¡¥Ÿðò.Øy•’”ÊyCÒ"ªR’R•W4Ê«"êDf§g²ºüþ÷Éžä©ÀÖðy‘zŠã×5|ÅIŸ—Ð}òß=øñÞοÌì4/ÔÇ‚„ž‚8oÁÌ«åÃsöó+9{Њˆ¨”+¸bøbå•òªˆú$ë‰2'3;Í;µó¡Ÿ|ãIþy¬+y²{@êà®ILš™Åa+OsÔê³a[#WŸáÐe'8pv6ïï÷ýð÷‰îüÏñn<÷ú<¹ó¡ŸOîxhI¨  â¼3¯Þß<‚¯ ZÎéý–rZ¿%a]3ú/ã¼AËøÁ–aÊ+ åU÷AæƒWŸÚÑi݉ÌN?ÏèÈ@UÚŠÑ4+ƒ‰ÉYa]ƒferÍÊ‘ÙlëxþDf§ŸOfvܪ{ÄJAì›`åÕιƒ¹à…ELIJ ëš?xw¿2Hy%A¥¼’ˆ“85g˜K‡ojŽ.Еˆ¦ ¶/啨òJ"Ž‚XìFAl_Ê+±åU˜Ómï¹)ˆÅnìÄʫܔWb7vÉ+ÛÒ”›‚XìÆ.¯s»l‡?)¯Änô:sz‚rS‹ÝØåLò*7啨]òʶĹ)ˆE“ò*7啈•‚87±HxR^妼‘ Rç¦  OʫܔW"T âÜÄ"áIy•›òJD‚JAœ›‚X$<)¯rS^‰HP)ˆsS‹„'åUnÊ+ *Ýöž›‚X$<)¯rS^‰ˆ„˜‚XD"…òJD$ÄÄb7:Sg_Ê+±å•D±Ø® ´/啨òJ"Ž‚XìFAl_Ê+±å•D±Ø‚ؾ”Wb7Ê+‰8 b±±})¯Än”WqÄb7 bûR^‰Ý(¯ä‚zMÍéÐæþ×§ew @•Kõ™–ýÇÀ”ïô˜œÕ%ÔÏ™D±ÿÌNJë0К×ç$­:Ÿ’”J•çšû|Ú ^\÷Î줕Ê+ÉåU˜ õmïýfåL>åào›~ÁcŸýɾøÊK½sæwn:øŸO9ð{ÒŒ}Cñ|Id²K‡:¯æ ^;}ÑKë~;°ñ-~øÖ§üøÏU^êÔëgy`ã[\8díïó§)¯ÄgvÉ+Û åÔsJNûAsÿòæÇ¿ñØg©|¬£gÿàà9‡~î55§C°Ÿ3‰Lv âPnÇÜþ+Û/ºþ—^ý„§ßúTåc|ã ]ÿóì¤4啸Ä.ye[¡|‚Ì>ðNú‘oøÎÙ?Uù¬uû?gÒ¬GƒýœId õ™: e^-xqí;7½Å¯}¢Êge¯}ó_X£¼ŸØ%¯l+”Aœ85ëükýÆ7?ùC•Ï:|êöž–ýG°Ÿ3‘P e^¥$¥ž?vð4ß?ü‘*Ÿõî¾Sœ; My%b!í8&gñµ~W°“³t_Š”wylÿ)U+%)Uy%b¡î8>õ›ª€¥Ž£5¡î8¾}BUÀRÇQÄ&BÝqÀè[ÖrÍÖqŸsÜ]ñšðñ ßúeYÿ«œ4¶kDzüØf>KGñ•¾P·ðR’R™µöH¡k÷ìþl0úª©L_c—ÅäëËHà3ÓøeYÿ«¥SxYl[_ìÇ6óYê8Н”WâUbr·¼þc¡kãæq¬¢ü½¾×2.g+ÿ¯"ˆ¨‹øØºoü²¬ÿUV/ŽkÇA{üØf>KGñ•~«ðR’R¹{Õ¡B׎é}Ù0 DìÍ·Ð2nñlÞQDT}>=%Ç/Ëú_-œÄKc/æËóýØf>KGñ•òJ¼JLÎâÆ#ç ]ë6Žeý¸¶¼î¢:¼fÚ'ÿ¾ê•GY½á?Ù´B3>²ækn<|†“<À„ % cl£ùÜê/é_ý’³‡?Á&•K%«²Ñ?frfÖ1öhYŽãú#ç¸ñÈ7œÕ£)c/{÷¶Š#–¬Òž3>öÒî\:úV¹i½¥)c‹•d\“Nì‘ú97øa»Õq_)ˆ /%)•;V(teLíÆ±ÍxSBu^ÿüöÿ ß<ônÖ¨s[Ä5à““²¸cùvÎzüV6ˆ+aäJÝÛØgâcúå{¸ð¹²i…’D‰Šlrý œ¿`û6ŠáEOm`æŠܱ"› ;Öglógø`ãX#¯*\Ç!³3½´»Ÿkz^̪—÷ãÓWÖgl±,_ïvö¿›Ûý°Ýê8Н”WâUbr×>WèZ½~,âog¾íXù–u\vø×þœcîmÀ&=f²]åfì¼úk®^;Š ±—ò¡™oqñ¶ÃpoÆýß&®8|Ž«VfݸËÙiö»\°:.*ǺÏâÌmÓv.<Çuûßàc+ñÒñrí®Ul׎I;ÎåÑî\<ò*F!žm{grî¶#Ô©)K×È)9…ßnuÅW âÂKIJeÆ’œBWú¤^lP®ûvnÃ*WLâÚ%9ÌX²ƒSnªÅfæuñõùÄøÝÜ2¡”mÎGŸ_Å´KùÒÕXþÊ©\¿$‡›Ç<Åz1-øÈà5L?žêÅ0áÁ%\ðXSÆ4}‰+ç0ca*Ÿ©SžWôJç¶9ãØ6¦ _œ“G»ÙL{®£PŽ—>4‹Kg,çË·ÕgéêsöüÂo·:Žâ+å•x•˜œÅÕ~(t¥®Ë„øö¼b›V¿/mÿ«wïàmu.ã“K×óÚJÍøpêWLÛ{’³6|Ä•~àª]osÈ# YæÒ•\|àKNx¸>+¶ßÂ¥~àêßsÉò%ì1å]._3– åoaҶv<T¼ƒ3¾çêí©l׎ý2~È£Ýï¹`ø•,Yk'g™ë»}ÛU¨Ï{YèíVÇQ|¥ .¼”¤T¦/Ì*tmšÈå®å°ÑϳE¥›8rVÓçÌfûjÍÙmÄ$ÞP¾>³‹[ænäÂI[¸qa7§¤qTûÚ,Ól ÓîàôÛk²Ò5Ó¸vaÓîåšQ#Ø·ßj®ŸÈ±Wð…é{¹iB/6ŠkÇ!3ö2}ÖX^ÓšƒgdåÑî^®|¶%KU}Œ³æ™ë;k¯‹«Éû‡í(ôv«ã(¾R^‰W‰ÉY\¹ïûBײÕcX/¾=o{—][׿ÕS>æâyÝX£ÙXNÍØÌv•š±ÓŠ/¹rÏQöërÔmÈ:-ná—Uf©KWrþ¾3|á†Ê¬ýì.soïk|¸a^1駸Œ®_ÅWr¾çÊÌT¶Š»š}¶~ŸG»ßqî°+Ýzçå˜íí9Īðªig ½Ýê8НÄ…—’”ÊMóvºÖéÁúå®á𩫘ظ*¯ë³™«_¸µzrÞÔ)¼¾|¹›f¯ä ··cãêµY¯Á强Yywíîâ²gZ°l£¡\>×loö"v®Q×öK/ôv«ã(¾R^‰W‰ÉY\žý]¡kÉ*£ãø|Æ×œ6ðJVºy?И _ç’ÌMlW©;.ÿŒÓ\βóåM_ryöwœ;²c/YÁyÙŸqD‡:¬x×V.0Ûœ?ïKÚÏ…Ù_pÂÓÍXñÖ9|´u-^1áC.ËþŽË·­d˸«ÙkË×y´û-S^¾‚%k à¸]æún[Ë«+Ôå¯|^èíVÇQ|¥ .¼”¤TnHÙYèZ;ê9ÖmÇ—“·ó•.-Yù²á~s6z` ×NÄëÊ'°Ë°t¾Ò¥9cê<α3¸!e'—ukÍØ‹FrEJ:'ÞX•®IfªÙfê ½Ø±ó|¦¥dpæ= ¬tÅ`>ݨ ¯î¹ëSvrÔQlÓŠ'mÏ£Ý\òT –¬ò§Í0×wÊx^W÷ ÞZèíVÇQ|¥¼ s¡þðÅ{¿-t-\9šuãïdÒÖo¹0m2Ç7a½ªó¡å_pñ¶¼ªRS>°ô,'öjŲ­&plæ×œ¿v=ïiVŠÅZÍåŒ=ßrÞÜDV-w%ïŸþSÖoãýÍ+°n×Cœ¿÷[Î_45cª3¦Ò}LJ7—›¾‚-bÛòÉÕŸçÑî7œ9ä åØªg&“7fïš²Tí>žYøíVÇQ|e— õ€¯‘YèJö,b¯æI™\=¢7›–«Ãóñ¡é\;i¯K`ç!›8û†ŒiГÓ&gpÕèq| ^ k0ˆ §gråÀY½l >Üg—Ž™Â‡Ê1ážù\5#“©/ta­èJŒ»C&˜Ë0œ­£›°ûÈ-y´›ÁEO4'P–w˜ÊycpÐM ,]¥'N)üv«ã(¾²K^ÙV¨rpÁîo ]¯¬Í:åïd¿-ßpÁηøhS Ã8fû7\¾‘WVlÊ‹?çü ؾu/Ë :°ã þlÇfÏå+»>æðþY·| "*žµÿoGlýÚXFæ^¶¯–½y-g:–›y€.©Â¨èØmÑ:¯í&¿t%£›ôáW&°4Š3¶Á|lþÇœï‡íVÇQ|e— õO¦MÝVèZ9¤b¯æ‹ã·1mÊ>SD§9mò6¦Ïkâêñ¡6sÕèq¼·a<‹‹fÅZ7ò‘.³it [v^ÊÔäuœØñ&Ä'¢bY÷²DNš°ÕXƤY¼·sÉ.v,wÒ<>Ö$žQ¥Û²×à1^Ú]Âyµ`Ù:ùÏæ5XÅX®æMìúü:®òÃv«ã(¾²K^ÙV¨;Žóv|mãúŠS^¸’eÛ.ã´´¯Ž£øÊ._¨êŽãÊI[l\›9·KsÆ4Âùh_Gñ•]òʶBÝqLÉüÊÆõ%'¾‚e/^Ê)h_G)jBÝq\6n£kg?ÔŒ1 _äÜ´¯Ž£ˆM„ºã8sÛ—6®/8nÑqœ€öÕq”¢&ÔÇÅ£×Û¸ÖqfÇfŒiøSо:Ž"6êŽã´ô/T,u¥¨ uÇqÁð5ª–:Ž"6êŽã”ÍŸ« Xê8JQêŽã+CÓT,uEl"¤Ç©Yçǯ;ˉ>Så³&lø”‰S³Îû9 ¥vû§žŸ;8•s^X©*@¥ôOU^‰ØA(ƒ¸÷´œ£C—¾Ïqë>U峆-?ÁÞÓrŽû9 ¥v¤5pg=¿\•Ïš=pS¤*¯Dì ”·½÷ššÓ¡÷Œý¿ŽXõG¯9«ò±F¥}ÂÞ3öÿÞsJNû`?g"¡ʼš”Öavÿ•¿Îè¿„ÓU>׌þK™’”úûÜþ+•W"Rx‰É{“{ÏØÿë ‹ÞãðÔ9"íŒÊK Oý˜/,z½§ïû½WröÄP?w"EÍ줕ɳû¯üuZߥœÚg1“û,Ry©©}sZߥLIZùû¬~+”W"â?=&gué5-çÍÄä¬ß“³¨ò\½¦fÿ‘8ußi”üÒêúÏ줕]æô_ùfÊ€ÔßS’R©òZ¤ô_u@g%¿”WI† ¥tЊ-è'¼lIy%¶¤¼’H¢ [RÛ’òJlIy%‘DA,¶¤ ¶%啨’òJ"‰‚XlIAlKÊ+±%å•D±Ø’‚Ø–”WbKÊ+‰$ b±%±-)¯Ä–”WaN·½»P‹-Ù%ˆ•W.”WbKvÉ+ÛÒäBA,¶d—×¹]¶ÃO”WbKz‡9=A.ÄbKv9S§¼r¡¼[²K^Ù–‚Ø…‚X$Œ)¯\(¯D$øÄ.Ä"aLyåBy%"Á§ v¡  cÊ+Ê+ >± ±HS^¹P^‰Hð)ˆ](ˆE˜òÊ…òJD‚OAìBA,Æ”W.”W"|ºíÝ…‚X$Œ)¯\(¯DDBLA,"‘By%"b b±%©³%啨’òJ"‰‚XlI×Ú’òJlIy%‘DA,¶¤ ¶%啨’òJ"‰‚XlIAlKÊ+±%å•D±Ø’‚Ø–”WbKÊ+‰$ b±%±-)¯Ä–”WIÄbK b[R^‰-)¯Âœn{w¡ [²K+¯\(¯Ä–ì’W¶¥'È…‚XlÉ.¯s»l‡Ÿ(¯Ä–ô:sz‚\(ˆÅ–ìr¦NyåBy%¶d—¼²-± ±HS^¹P^‰Hð)ˆ](ˆE˜òÊ…òJD‚/A<<«oüè­Ï—ÑëÌÈônTù·Fmív~lFÏïÇföœ>f}·þ~þDÂò*rKy%b â‘ÝÛŽÙÖã§¥‡§þ¶ÿã=<öÕ;*?×Û_¾ÉCŸdqቿÍHü~dF÷¶þ|EÂò*rKy%bâáY}ãÇl{î§­ï¥ñ/ßR¡vßôŸÑÛzü8<«o¼¿žG‘p£¼²G)¯D"X ‚xlfÏéK_Mþóè—oP¼ZrxÊŸc3{N÷×ó(n”Wö)å•H„ Ämïc2zþõaßúâˆ*ˆ•õaÇfôøÒ_Ï£H¸Q^Ù§”W"ò?£¶v;ÿÚgûùÆç‡TA¬×>ÛÏQ[»õó/I”WÊ+ ±‘éÝøÚgT!¨‘éÝô5%’‹¾P×;å•òJ‹òª™Þ¯~–ã·Z5 .K_ÜŸ›ÎX‡gpÚ?âX©Ó îy불nÌç_ÍòÞ·óÙ¥ˆr7súqçðƒ‡z±À‹^\Æý–é÷,oÇò­úrÓ'îmesOæþóòꌉQ¢ë\ב#²wðpžÛ‘Íí³[1þ¦ ÜãÇ}ã^ bñDßèòJy%áEyULïÆCŸîõ[íËêÊúÑqàáÝÎáÇ'ñÖJ•x÷ºm<ðæ ¶ˆnÌÖñn••~?k”oÀ‹*–çÍ˶üoøþƒ=Ù J·â€;ÿ7|ײ«Y¾eonøØµƒïOá]U¢Ù"q SnåŽ#ó8òÉŒNèÈE'óÚŽ=̘Պñ7ã.?î÷R‹' bï”WÊ+ /Ê«"hdz7<»ÛõÑR&6-Ãf£WrŸ9lWÚ­¬X­=ç|°›ûßxžÍ£qÀá^ÚØÆW«ÆêMâì®ÕYþŽ1Ü~Æ·ï@6ŽkÉGï¯Î¸qãGÆðK¯bù–½¸þ#×¶²÷>ÅzeÛpôÛ–áÇgò±;îâ¨WwpÏÆ»X£yW¦™ó9ïbÆÌ‘Ô°¢ IDAT–Œ»ìI>}{m–*ÆòMobÿŒ-ÜïÇ}¥ OÄÞ)¯”W^”WEÐÈônÜv‡+ƒ«‡7btëD®ýh÷ŸÝÌY*±Jçdî:»ƒ9o `óèFL:œéyþ’yOÍš|x[:÷dtdÍ í8áÝíÜv³tgãø+8áÀÞY­U¥ë<Àù'ü·¯Ä≂Ø;å•òJ‹òª™Þ9g2üZ{ôàE1Ùï@:sŽOá=5*óžõ˜s&ƒY¯÷góè†ìh«Çy·¯¹Uuæ’SÌ95‹$”ã5óW3ûL÷î–â/çÄ÷¶rÓÜk_õÿ8éímÌXzãZöàš=¬ËÛ39bð=¼ñŠZ,W2šÕ.¹‘OÍ_Ä]g2¸cìÑü)®4çs>ÞÆ-3›³týG¹ì´ÙֱѼ¹bU>¸e“ßö“‚Xtmoïé Ì<á\ÎÞ–púèÛX¯LvذŽÛ×ßÁÍŸäòÓÆøíëngõfOrùétnšÑœ1W½È-ŽöNÍáSãyÓÊu~ÛO bñDAìòJy%áEyæqÛûÈônÜûÉf?×F®™Øœ±­žà°Ç«³Ú#˜iŽÛóZ6‹nÀ¾7æžïØHÞR¹*ÿ¹|>×YµG–påò;YµÜ¥þÖ&îÚ÷ Å_ÊñÇŒéwfwcËr5yOÿV,×òY®úÐÚÞ&nœÙœe[=Ç4ëðs`ëÒl4b>·®¿Õ›>Æ¥§é7Ϲ˜1Mç²Ó›¸qF3–nÐ…‹O™ó½;‚7W®Á‡3Öúm?)ˆÅ»±òJy%ög—¼²­@ži¯ÏäÈ'ë0ºñÃ\pÂûHA,žØ%ˆ•WÊ+±?»ä•m*ˆw~´Îÿuz_¼¦8QõÎxÏ9|Ç«‰l nuѸ©y[ +uËt—¶V2¹c<Ë\Ó©YO²aü%óŽeü{ùP}ÍŸáòSî뱆ë×=ÅÛ¯¬ÍŠe@ +4½œçÍaÆGë¸óÔ<}(1£ë¶açñظõ#\|r-×NoÉšw=Ƈ®¯Æ2(Îø–·pà®Tîðã>R‹'vùB]å•òJìÏ.ye[ âí­V… ÄbgÊ+{•òJ$*ˆ3?\¥ A)ˆÅΔWö*å•H To;½B‚R‹)¯ìUÊ+‘¨ N?½T‚R‹)¯ìUÊ+‘¨ Þ|j±*¥ ;S^Ù«”W"(PA¼ñä‚€W—ú¥ÙjÎ̼§{£7Û”o¯å³ý㓘t_M–iÄîY¯e{üQ b±3å•òJDB,P_¨»áä¼€Wçú¥Ùjδ¼§{£'Û”o̯å³ý£ýØ®r]>µ;%(Ûâ¯R‹)¯”W"bC#Ó»q݉9¯‡ë—f«”d®Úr'ë¶¼‹}[²F¹(«Ø€÷,˜È5džñѶÑÀ’ÕÚ0éàL.Xü^’Í(”`ÅËoáÀ¬™\wbWm¼uZßÍ^Ï4bźmxKÛ`‰ÊÍÙsïdNz9*'Ř‹®à³ÛfëñÁ ΘxW-A”ŒcÃqÚÛ)\wb¶×e²Ä"ù£¼R^‰HˆLïÆ5Ì x=T¿[Ξĕ›ogõ±lÖ»ç¿1†ÃzÖf™&÷qƱY\óZw¶.߈ý_Å´œÇÙ&¾2oIÂ…‡†°_ÇÊ,ÓúÎ<6‹+7þ«•ˆf­{:rÈÆ1\ñz/^]µ{îŸÉU;îc½r ì´x8¤Žwgo.û`W¦ßÅ:åë³ãÒQœŸÑ÷5/úý‡rEË ä>Q‹'úB]ï”WÊ+ /Ê«"hdz7¦ŸðêT¿[ÌÏå›ncõøV|ľ|ã­¬™p3Çδ#ÝØª|Cö;<•³‡×eÌUOrþûÆt«>É–åølö4.ßp «ÆµàÀÃÓŒö_ïÉ«ªÖçsû¦qÕÑ1œµwWŸÎÔ7†ñ¥®UY檮\t|2Ç?Y™ïëÅ%ǧ3íø4.ÞòŸ{eg䱬@î±x¢ŸðòNy¥¼’ð¢¼*‚F¦wãªãS^ë—b‹Ùã¸lÓ­¬•p#Ç5†/Ût+k׿‰ãŽNåª#ϰUù†ì{xÇw­D ËÆ•eL\YÆÄ•fÉè:ì¼m2—m¸™5ê9ÛXõúsf'sÕÛ/3é™–lØ  ë´iÊ+®Že髞áÂããùâm±¬ÓÿE®pY·¼—È}¢ OÄÞ)¯”W^”WEÐÈôn\ùþä€×ƒõK±Å¬Ñ\²ñÿX«þó–1Üåñ«O±Uùìsh"§½T›ñ·wãGïŽä¤uƒ8ÿÝÉ\²þ&ÖHp¶±òµgyeÕvÏ™À#ê±l³Û9lÿ®|2_™Þ±W=Åùïã¨.Yñž\d¶¹p]vÞŸóXV ÷‰‚XìÄÊ+û”òJ¼±K^ÙV ž éÏŽ¼·ßŸ ÞFUðjrVßßFní>Æ_ϣ؇]‚XyeŸR^‰7vÉ+Û Ä4<«oü¨mÏ~5ãÕ|å!ª Ô´ƒ8rë³ß Ïêï¯çQìÃ._¨«¼²G)¯$/vÉ+Û TÏ~øÖ®íFm}ö«I{zý<㵜st0ç¾ý‚Ê5çè`Î~ãyNÜ›ø×¨­Ý¿™Ñ½­?ŸC‘p£¼ŠÜR^‰ØD O Ïz¢Ì˜ŒnCGoë~fTz×ó#Ó»Qå¿•Þõü¨mÏ~5bk·dýå.Eò*rKy%bº–ÀÅ0´”N“‹„å• å•ˆŸ‚Ø…‚X$Œ)¯\(¯D$øÄ.Ä"aLyåBy%"Á§ v¡  cÊ+Ê+ >ÝöîBA,Æ”W.”W""!¦ ‘H¡¼ 1±Ø’ÎÔÙ’òJlIy%‘DA,¶¤kmIy%¶¤¼’H¢ [RÛ’òJlIy%‘DA,¶¤ ¶%啨’òJ"‰‚XlIAlKÊ+±%å•D±Ø’‚Ø–”WbKÊ+‰$ b±%±-)¯Ä–”WaN·½»P‹-Ù%ˆ•W.”WbKvÉ+ÛÒäBA,¶d—×¹]¶ÃO”WbKz‡9=A.ÄbKv9S§¼r¡¼[²K^Ù–‚Ø…‚X$Œ)¯\(¯D$øÄ.Ä"aLyåBy%"Á§ v¡  cÊ+Ê+ >± ±HS^¹P^‰Hð)ˆ](ˆE˜òÊ…òJD‚OAìBA,Æ”W.”W"|ºíÝ…‚X$Œ)¯\(¯D$ü˜!Í Ô+Úéܹ³5„yíµ×fÙa»ÔŽÚñµHnû3í(¯ÔNQoGÂT¸(lGA¬vlÞN–/mE²}^”WjGíÁ¼²­=àÄjGíx© µÉ"ôyQ^©µSóJìA× ‰H¤P^‰ü{çU•ÿÿ7„’€PPQ@¬À® WE\ýÙ».h˜Bï˜I¡‡’Z¨)P’É0éPBKOPY‘umhèn¾ûùýqîIH™˜0¼_Ïóz”;wÎ=·pæÍ¹÷žCˆaCL¹^`{E!6† 1!äzí!„Ø6Ä„ë¶W„bcØB®Ø^BˆaCL¹^`{E!6† 1!äzí!„Ø6Ä„ë¶W„bcØB®Ø^BˆaCL¹^`{E!6† 1!äzí!„Ø6Ä„ë¶W„bcØB®Ø^Bˆ-q¼©•¾[ïÎrï_{ÈÀûÊÐ/íœãòÅÜØ‘ëcœ‡è Îýl]GBØ^BˆMМûéc]ùÇ~£uý?ŸØq.Ý=ëÕÉóÿ\“²àRhú¢Ò•É>%ûgŸ¿kB‰>Ö¥lþ®qÇ}âÝüõ1ÎCDÈ Û+B±!úX—a>Æ1—’‹÷Èѳ™×ÄÃßeHxfp™—Áõ¢>Öe˜>Æyˆ—at€wœÛ¿½ .eKöN?š¾èRÄá²åÈ*Ù~ll9²J6\^š¾èÒ²}3ÏûĹ]ô5ºó‰wó÷0ŒdëãH¹ú°½"„âašÐÑÛàzqwÞ69röÀ5wOÞ6™?áâ‚]J· S‘Ñêïú.M b$ìPPÙ‚]J}Œ£ùL!v Û+B±1¾ñc7„e.ÿãðwér=›ùωÏÙ*‰sK}Œ£Ïò­HBì¶W„bC¼£uÝ|ŒnÒΘ$ó»T»qÇÉ 2×øó¾Æqël}Œ !Û+B¹x˜†;êcœ‡èct}âÝ"}có½n?{F•xÆê$4}ÁŸÿ™,öfÆ“ìŸ}Á;Î-ÕÃ4ÜÑÖçR;l¯Ø^Bl„Þàò–oœ[¼—ÁõE»'ýš±èÏ­Y«dW^”˜Šã$éô.9ðÏ$»6ýÌ~Y‘ìyÉ;Î-ÕÖçƒR=l¯Ø^Bl„>V÷†ÑíÜü]ãK·•”oöHÆÓ kÚ·{eé¾¥Þqn l}n!a{ÅöŠb#µ93àRò7»„Ö¬O¼Û%½Á¹Ÿ­Ï!7*l¯Ø^BlˆÞàòÖâ„)¿'}cZ» wMøÍÓè:ÀÖç¶Wl¯!6ÆÇè–µíÄZ1}m V¸€ 1!6ƒíÛ+BˆÑÇêÊöžÞ.û¿Ž¡V¨7¸”qp]BlÛ+¶W„¢qîé?¦tßéBkwÛÉÕâïvÜÖç¶Wl¯!6Æ;Z×ÍÛ8úÒÞÓÛ„Ön`ÒœKÞq£¦Úú¼r#ÂöŠí!¤ àçú㎜u’PÝh®žØ]Z?¢“Í–Ë7Šï°›¤Ó»zÙy`¬ôuê)ãR¶V_NÎBy·;íž’yÇÊ—ïJü\zÒgº¿-ÖßúWiÿ€³l*h¼ý°4&g½xÅúÝÃ4¡£­Ï!7*l¯Ø^BlŒ—ÁÕ;(é«K{Š·JciLøXîrºGÆ%G–/?>Gžït³ Ø(»¸I_§ž26%²Ú2b·“[;ô{nn'ƒC×]^Ÿ8Bz‚ÖýdlbÄåå;Bÿ"íøB64Þ~˜ÝU)óŒãÿûΤ!{¸_‡>w.B®9l¯¬k¯î™x‘½„«‚‡i¸£wÜè…[.»Š#Ç|y_k¹×s¹Äi˶o,7ß:DŸŒ”ø£¥¯ÓÝ2&%¼š26ÈÒOºH×OæÈÂ/ºJû—gHT‘ú,.ñÒ»}_yÿÍ.Òn°›l*PË·…”ö|. "eWÎR™ü^/éèA«NòȨÙYPÿýYž8»ìž¯ €ëU÷«pérÍa{U»Ésþã?:ÎÖçŠbÇxFò6Ž.ÝpØOâ‹"ÁÍ:çnqzèsYŸ!ñEëeá»7Ë-Ì•íEbÌ%÷9Ý-c’êþþɹòJ÷ÛäíegÌër[ÇGÅãH¸ÄEˆ!ñÒ«ãñHœ)/vm/O/‘¸¢‰^«âõùá¹üai{×0ñHX)›¢>“‡:t—Owmª×¾l9"ž±:yüÕlþ Û+kÚ+}¬ÎÍÖç‰bçè Îýôq£~HœUº-g•‹ÂdlâéÓ¶—ŒNÜ(Æ“2¬['¶%TŒEa—á*÷9Ý-nÉ›ªüntøsÒ¥÷[œ&Æ_yﮛ䉕W&±‰ŸI¯ŽýÅóØ& |L:t,úÛ%jíiÿÀpY—¿Y—?,mn{JÆlY.; 6Jô¡²5ws½÷eËÉ`ñÚ9ZF.xSn¾µ­C ƒ#¹Ñx•¿¸gàËçD9—.Ý;óO¶WW¶W v/ÕF¥êcœ{ÚôÌBìuhÔTãè–ìR²þÐ"‰Î^)q…›ênž¿Œíï$÷¸ûÉÖè—äÖî/È¢ê3Cº‹Üçt·ŒNÚPÅwW‹ï7 Ð\Z9¶’VŽ-¥ ­ž› ›$Æô©ôêÐ_æf©mLz¦­týd¶¬[5@Ú=ð ÍÛ$q¹²ÀóeØ£•4kÛ]ž=NBNl¬ß~hÆl•iúÿyDë.>òü½«aûçkÒGb?¸£Šµhå ϼÝ_¾ !¾±ã…íUÅöjUºW™·aôOž±.OÚôìBìÓpG½Áå-/ƒK±g¬N–š¦ËÎüP1n¨ƒëdý¼{¥íƒÈÌϺJ×¾’hí³ØtÜçt—ŒNZwå÷²¦Ës;Ë«ë–Ȇôe²!}™¬Y÷‚tn÷°ÌÌ\/;MK¯ˆG–ZǾOåþv·É° ý¤ÝŸÉÚ¼õ¹û+YbZ%±…ë%Ê4EÞíë$½g/ªcý«6,kéõ±.6ñƸò-ƒ#¹ž©28Â"@Þ?¨§LZý¡°½º.Û+BÈõއiBG}ܨdßø16]"1ëêåÎ4<äÔZnn{‹ Û²Òb¹³Üët‡Œˆ °CšA²%;TÂW?*í»ýM°(ëøÖÕQúȶýI¯‹ûQóç«eùä»ÄÜÿ©¬Î]#ÁSï§ÞŸ”`Ù¼g²¼{Ÿ“ôq_Tïý¨ì¦¬¥âeUÒ„o18{¢ÆàèxSkq^ð†xîp¶W×e{E¹žÑœûyG}œöÕ; ÖJƒÌ[*S9º ‘…ÇË—ïHûBî­â ·^f¿ØV:}8K¶T(+X|ßm/Žƒ\díÞ¥g‡‡å«£ŸÿJÞ¹‚~ŸÈªÜµ²ãð—òÉs]Å´h/÷¼3B7p_*¹ú€w™Oœ[®mÏVµ08{¢òõlB…g¿ø=0eÎÙ^]·íi úX—aúX×EóâÇå{G—è .e^†Ñ%óâÇžö‰wó×Ç8±u‰âaî¨õÃÚÌy²# ­ÅÅ{'•4Ñ·‰=QåõÌöÊnÚ+ROô±.ü£ÏÌß5¡$êèZÙ_/™ß¤Éѳ‡%ó›4Ù_/[¬)[¼{r‰Ñí¬Þàò–­ëLì ¯¸ÑþË’füg{þ*¡µvÂ_¼Œ®?Ùú¼Uƒ#±'ª¼žÙ^ÙM{Eê·Á-d~ü„‹±§ÂåèÙÌ=ü]†Äž —ù»&”xǹÚºîÄŽÐt—"N.“my!ÔJ}ãÇþæit`ësW GbOTy=³½²›öŠÔï8·¨€}s.¤ŸI’Cߥ[mú™$ñßÿåE_㣭÷ØžF×¾ñc‹Ê j½I3/Ímz·‰=qÅõÌöÊ®Ú+R|âÝü—&L¿òí^9ðϤ:›~f¿øí›qÞ;Îm­÷…\稆xÌo[s…Zo@ò—ÿÑt“m}þ*ÁàHì‰j‚#Û+;i¯ˆ•ècœ{úG_Hú&^ÒÎì­·¦bƒxǹ]ÔœûÙzŸÈuŒw´®›—ÑõÒ–ÜåB­wɾÉ%scG~lëóW GbO\q=³½²«öŠX‰·aÔΙþeÉßì’†º93 Ì7~Ì6[ï¹ÎñŽsý:ôˆDæ.£Vêçò§w´®›­Ï]%‰=QåõÌöÊnÚ+b%úX]YBñv1}Û`wl½AWæašÐÑbsàPiÓ[¬p€ zÔPMGß8 ƒÅòžP§UZ(€#ZTZ^×õ+ÓÀÏ:Õ²^} @³«Pöõ…§a”nÞžqÃs– ­Ýå)_–yÅJ°õy«GbOTy=³½²›öŠX>ÆyÈ’½SÎï=½MËE{&•êc]†Ylæ>¨`x—Ųöþ àX𠀡–s¼ —ÅòÚ‚£µëW†ÁñZ ÍU2³,,g‰Ðê]qÐ]ô±.›è¿Þ‰=Qí8Žl¯ì¢½"V07Vç¶"ÅãÒîâ-ÒX®Hñ¨ü²TK'Œ²Xö<€³Úºà¨¹@4ÊÃUO¿X åÁ¯¦àhÍú/ȇjµºµ¦-û7Tˆ}E[€ÿè Õ³zÀgZ=ßð€ÿ0jû ý¡zbçÈð&ʃã#~ÐŽÓ‰w´®›WœË÷Á³esö"ZÉM§ʲäieú8—óMxX GbOT{=³½²‹öŠX>Vç¾:ÃGŒE›Í•iú2}ìÈ™•65À!”³ÕB´ÿ¯-8¶…ºM=À?¸Yû¬'T`O¨ ú¶¶¼¦àXÛúݵm¼C ^KTìq àô Ú‘§tԾߪ·õ7¯è `3€æPÁñ?6BÅW ‚cO¨ ù^5ÇãÆAopî§sýÁ/iêŸOÍ—MÙ nxCyI@êŒ2/ã¨oã¨ðJÏ…45‰=QãõÌöêºo¯ˆxF~ôeicÍaS°N’¾,­bx¦»ü®ý·-€3iŸÕÈÐZ³ÀëÚgæ ØÀ[¾‡ uµÇšÖw°彚µïtGÅàø€£Ú²|¨g6gxLû³#€Y¶Y”u—vºBÇ_P~Û{¨¶Ÿ§loY+„jÛ ÖÊ3Óê÷=PÁ±*`*8 €UPáÒòL¢æÂ•ïm]º<ãKY5WÖŸò­Òu'}$èà,Y´w|‰—ÑõÏ5ÇôÕ®Û”\ut®xŹüGçò‡æÏÞÆQǽâ\7èc]f^‡·x‰=aõõÌöêºl¯ˆx˜†;zF•Fæ6Ê`ð§–‰ÞàrÉÃ4ܱŠÍpÀ2AËk Ží¡ÂÙ‹ºi¾õvugT ‚€º=|êMîÚ‚cuëê=4Óê¹*ÇP·ßý¡ž™ìÕ“šukfCõ8šé o… ŽæžI@ÇP·Ä#5›WqLnlô1ÎC¼®Qzƒëo^ÆÑ¥óöŒ9¿Ø4±Ô¬wüè}ìÈ2½ÁõìÜØ‘{]çøÄ»•®=á%ëNz7YWõPpÅ7Ë®w‰=Qçë™í±G¼ .†à´9e14S`ê¬2ïx×Èj6Õ ªçîwOX,7LJ¡ž]4Û *Làd±~¨çßÅ•AfBý>ŠÚƒcUëßõò˺@õAù3Ž¿¸Cû®¹wòc¨X Õ+Ú]û¼$Øo° IDAT/Ô3ŽÃ´²Â ^q@ÕÁÑürL/%©ÓpG}ŒsO}Œó¹1._˜ÕÇ8©ü—ÁuÓü½c/­=á)¡'õMÎ#_‰>ÎåÒÜX—¶:žW GbOÔûzf{Eì ½Á¹Ÿ>ÖåÏ Çç7è-û'ˆWœë}ŒsÏj6Õê%’ïQ~›PÁQªð ¨Þºà*ÊZ­•ÕWÁ6P, ÖǪÖ À×Z=2 n-ê˨P×êùKp¿öy$€SP!P!ð#¨—]êÖ¸å[ÕÕG˜ Ó¢,R_²õqº 08{âš\Ïl¯Èõ€·ÑuÎü=cK7œœW¯gc7œœ'óv¹èc5×ÖûBHµx]çèã\.-K›&kNxØÔUGçÈ‚„±ô±.?Øñ³@ ŽÄž¸¦×3Û+Ò”ñ0 wô6¸nŸ0öÒꬹuzÔaí /™Ÿ0®Top©î5!M½Á¹ŸÞàRä»gÌ¥ÀÌ™²êøW×Ô•ÇæÈÒ´)ÿÕ\.y]×Uó@°½ÀàHì‰k~=³½"MÓpǹ±.>^q£J—˜nÕ?B–¥O¯8× ž]Hí[ ¤‰àaîè§§7¸þä»Ç­dÙ©²òøì«jÐá²8iB™Þ û—Ñ5ª†g:ì GbOØäzf{Eš:†‘ƒô—"o£Kéâ¤ñ²üàTYqt¦„ûRV)Ë3§É’¤ eÞF—R½Áå4_ª"×-—ä8×Î5QçrN§ûYopùC«+óŒÕ‰>ÖåÏX—}œË9Õ€»nÐÇŽœ©uÆ©µ4¡ZB&y=³½"„b/4ÉZBê ¯gB!ä*ÂZbOðz&„B®"ü¡%ö¯gB!ä*ÂZbOðz&„B®"ü¡%ö¯gB!ä*ÂZbOðz&„B®"ü¡%ö¯gB!ä*ÂZbOðz&¤Ü' »4iÑäå)ùã—%•ŒYj*ïŸT2yyÊé‰Ë“üÇøíR‡â~FÅ¿o?˜ y«õ€<õg½ƒV—N•–÷Ôê5­Òò¡ŽhQÏrƒ¡’4³b] Plj›ÃZbOðz&¤ Ü' ›°,éÌ´àÔ’{O˾c?IfQ‰?ó‡d•Ⱦc?Ɇ„Óe_†¤—LX–|v¬_Ò[V{ÀßÜ  +€Êü¥ŽÕëàu¨€Tj Ž—ô²X~½ÇfXµ ޤIÀZbOðz&¤ý“B¦¥]ŒN=+Ç¿ý³F|}I¢SÏÊ´ Ô’ñK“k)ú€Ç,þÜ@!T€ìà €ùP½‰m¼à;ÿ`p›ö½Ê=Ž/ȇú;œ ‡¶¼€Oœð'€µP¡3M[÷;¨k¦'€_¬ò X98Vµ½VåþÀ^ÑÖŸà¿:C…Ý#>ÓêWÝ>V>o¢<8>àÏ£"fiõ¿GÒDà-±'x=bÁÄ€ä(Ïu‡/¤ç—ÊáÓ—¬6=¿Tæ®;tqb@б†â+ÇÁþ€úkÿ¿*õð€× ÂÝf¨ÛÚ–Á±;Ô-ï÷ ‚YTàj à¨ÀçÜØf æÇŸµÿžð¶¶Ü28Ö´=ËrøA½¨öåi¨àú€ÞZªÛÇÊÇã¨àØ*h¾WÃqnà[08’&h‰=Á뙉˒üg¯>x!9»D2 .ÖÙÔ¼Rùjí¡ó—%/¨f?¡âß7 XPA锇¹Y¶¡üÖì]~‡ X–ÁÑÀ^‹õ:C…·îµØþ^FíÁ±=€·|¯­ckÚže¹/8ª-˰*´>¦ýÙ±–}¬|<†ÈpÀvÔ|ËšÁ‘4)øCKì ^Ï„³0©ç„€” ûOþ.)¹êížc¿Êÿä‹£ý’úU±™sP¬TH»Éâ³þ P~û9€¯Åçí nÿÞƒŠÁÑêïî/–BõæE˜^E=¬ Ž-ì°À0”Çš¶gYnWÿð$T€|À>QfkÚÇÊÇc¨¶ÝUPáÒòÌÊ08’&h‰=Áë™ã—%í Š-,ÛªDjˆ¡¨lR@ʶ*6SùVµ%ýQÞ³¡zãÌô€ L·¢bpÕ›g¦€P½˜Ë ‚–™t°.8êvòysPkÚže¹-àõÌag¤@Ýš®m+¡@ÝÔ¬îmtGÒ¤à-±'x=`ŒŸ©,îȯ²çøï 6æÐO2ÆÏT6ÁÏÔ±ÒfêÍÏ8Ð@ÔË#¨ï‚zå m=_¨×êmí_<Æ2 z ;@½sG¥:TŽ0ªm8 kÚ^årͽ“ku-pê¶vmûXUp4¿Ó @ €Ç«9– ޤIÁZbOðz&7w]VixÊOÒXÎ]—Uz•zƒÌûj…RBH#ÃZbOðz&7(6 fù@åYij*Ô@Ýë|À5Œ™Y(¿ëp ª—³.õ·dÔœÔfî„êÑì¬ýùCGíûÕê6ö_ zc¯%%%%î%%%B)¥´þFGG‹öq†G;ex¨ÉqŒß~ŸqK“Kç¬Ïߨ3µ:gý)çŸ|aœ_bˆ›¨*x ‚šzïi¨©·C…8ËÞ·æÞƒ aÎu,ÿ+­¼ÉPÏ/®†ºÜ *˜ý ày7ðƒ ™­êP¾%_ˆ¶øó-ZïÐöaÊCduûÕ @2Ô´†=Ààxí)))qÿý÷ß…RJiýµì•´u»N®.£ï4vibÑ8ÿäÒi«ŽÊì yâV,s#¿÷°b™½1_¦­Ê*çŸ\:viâé*¦¬Žª‚Ws¨TÎC=÷·Xûï­Úç] ž{<Õ3W×ò§£â”] nÿÞõ¼c”ź¡Blï:”oÉDTÝãØE+{#ʧ ¬n¿ÆØ u›ÁÑ08RJiÃep¼±P½û†ŒõK\5Ö/9Œ_b‰›Ÿ©lŒ_bÉX¿äü±KýëÍT¼ÚBõÈ™õ¢ˆ#Ô-ÝãPa²ªy™­)ÿu¨ç ÍÁÑ{ð…êá„Åg¿£úÛյǗ¡ž§4Ïjó ÊŸq| ÀÚòšök/TéüÊo¡?QÍ6Ic¢ÓéÜ—/_nŠŠŠ’ß~ûRJi=ep$@UÁË<ą̈ž¾(Ÿæ}¨ású@½^Po+·Ð|@·ZÊï à'¨[Á]@½UÝ@¨ øª¶^°¶ý–u(ßr½NP/ö| õ¼ânm{Í n‘w·b¿º@õTÞ àI¨ËûQýísÒ˜X6tçÏŸ§”RZOI#PUðjõüá¨Û²>(ï\†ŠCf™5¿8S 5”NMåê­é\í»&”¿Ó ê…•jŸí… l¨Cù•×{@>Ta8Tj¨€ÌoQ×´_–ðVõµ†Á‘RJGGBˆÝcÙÐýú믔RJë)ƒ#!Äî±lè~ùåJ)¥õ”Á‘b÷X6t?ÿü3¥”ÒzÊàH±{)¥´qܲe‹ppBˆýbúé'J)¥ ðçŸfh$„Ø/–ÁñǤ”RÚ0 !ö‹N§s0EDDÈ¿ÿýoJ)¥ ÁñÆbŤÍ§†-Zýå–üÓ#K‚'‡—…L(Y5sËéU3¶úO R‡â~FÅ1 0jÊÁºð€<¨™eêC­.*-ï©ÕkZ¥åCQq智–Û „òé «ãCPûqjpuRW~üñGw[7¶”Rj28Þ¬˜´yØŠigVÏŽ*IØ”.‡÷dKvz±ùV²Ó‹åðžlÙ³1½lû¶’é‘g'G¼eE±çü ÀÍP³´ŒPà/u¬^G¨ij[±j Ž— fr1s½ÇžP‘¢Õa>ÔÔNW¡>öÍ?üàþÃ?¥”ÒËàhç„̈Y3+êbRÔ!ÉËüºFsKRÔ!Y=;ªdÅ´ˆÀZŠ>à1‹?·†š×yÔÔ¡ÂNÔ -ïøÀÿܦ}¯rã P3µ€D”Ï¾Ò *Dð'€µP¡3M[÷;¨k¦'€_¬ò X98Vµ½VåþÀ^ÑÖŸà¿Pó`;he}¦Õ¯º}¬|<ÞDyp|ÀžGEÞÖÊ6êNPÓ)ö©?üðƒû¿þõ/¡”RÚ0훕Ó#£6ùÄ\8ž˜'§Ò ­öxbžlòÞy1dz„±†â+ÇÁPÓò=”þ`#T0ê à7¯A…»Í P·µ-ƒcw¨[ÞïA³”ÏCýTàsîƒ U3PsãÏÚÏB1 bp¬i{–åðƒ z Pòi¨àúÔœÜ÷Õ°•Ç+PÁ±'TÐ|¯ŠãÛåÁž °«X—Ôƒ#¥”6Ž ŽöKÈŒHÿus·_8’-ÇóêìÑý9²Q¿ãüŠi ªÙÄO¸rn樲þ~Ay˜›`ÊoÍÞÕ{Öƒ£ ÔüÒæõ:C…·îµØþ^FíÁ±=€·|¯­ckÚže¹/A=cتwr Th}Lû³c-ûXùx à4€í¨ù–us¨`YÀ¹†õHuœ;wÎýܹsB)¥´Á28Ú!S6ô ™yá@Ü 9´çT½MÍ’é'Dô«b3ç Y/¨v“Ågý¡^ê0ß~àkñy;¨Þ³{P18úBÐ_,,…êÍ‹0½ŠzX[Ø`9€a(Ž5mϲܮþàI¨ù6€}&¢<ÌÖ´•ÇPm»«Póíç.✂êq$õáܹsîßÿ½PJ)­¿aaa²lÙ2n‡O‹Ü¹=hoYFÜ1i¨;Wì+[9}˶*6SùVµ%ýQÞ³¡zãÌô€ L·¢bpÕ›g¦€P½˜Ë ‚–™t°.8êvòysPkÚže¹-àõÌdg¤@Ýš®m+¡@ÝÔ¬ü6z;Ç,ÖÖ#õåìÙ³îgÏžJ)¥õ—SÚ/A“ÃÊ’¢2%eÇákÚr@‚&‡•…NØ^ùÙººGó3ŽÃ zÑ ^q@ÅàxÔË(ohëùB…¼–PokÿÕóv+€ ¨ÈP/ÁÜQ©•ƒ#ÌDùÐ6-jÙ^årͽ“ku-pê¶vmûXUp4¿Ó ê6ôã•êÿ>€úhëôp7jœTæ,ƒ#¥”6XGû$xJص_EŸ7m=(åšÙ[KWLÚ<¬Ò¦ê›øêEºm\Ý[Õ¯øZ[/êV¯¹Œ‘PAïP/š´Ðê…•\ùVuåàØF«WÊXuÛ«\î`mûµÏ#¡n!›{kÚÇš‚#̉Š=‹Ëpå3¤‚ò·Ì‰5èt:÷¥K—š6oÞ,ß}÷¥”ÒzÊàhŸMÚä¶É'æÒžÍiÒXnò‰¹4i“ÛUªrC'¤z,ºþóŸ”RJë)ƒ£}<)Ü=l¾AŒë’ÍÍóbÊ‚¦„ͼJU~*8¶¾J哈îÌ™3”RJë)ƒ£}<)üóus£KcVí“ÆrÝÜèÒ«ÔãøÔ`ÞW+”’GJ)mí““6[93ò÷mA{¤±\õå–óuœÇš¦eC÷í·ßÒ&j†÷ƒê!ÞVOˆOjqµëålzYœéô~´ä6z7ºÅ‰2³-Ÿ—õÙõ-çt“9ž'V<.ä‘…™òµ­-m° ŽöI¨G¨ãŠ©a¥KŒ¹´á†/6HðÔ°K¡¡|‘\X6tß|ó m¢^:€8>=_ÒŠª^/{£9èDIN=·•»í#¹[‡ï”¼&°ï,2É -8®;UßrНéñ¬ÉãÁ©à¸à œ¶õ±¥ –ÁÑ~ šnXëU¶yaŒ4Ô5_m- žYͦ Þ<~Èb™yÎæ3PÓìE¢â›ÍB †mçáv¥ªòàNûµ2N0PÞ Àx¨·júÀûQ5ÖÖc€\e¡ÆW´D5 ¢5å= õ¦w‡j¶Eˆî믿¦MÔt¯-†p’g¥KQëÒ‚ÎÍïo•ìzn+'úCÿ±Sr›À¾W°°<8†ž¬o9E×ôxÖä± ž@Šm}liƒÝ¸q£øùùqp;$pZD¿àÉa†zEÉzŸmõvW´O ¿0eCÏ*63À ¨¶É2Ø=5ÐöP¨áhâ æmn5DN)€O Öž €S¥²›ÕP~¨éúfA¡¸* ¶ÐÖ+Ѷ};ÔàÙY¸rmkëq3Ôð?nPc(¦A nÆjñG¬(ÏQ«K¯ Ž×‡æ Óã7¤' h3Dü2Š®XÁÑ‹®éñ¬IGûóÛo¿eh´S‚§„ÏY15¬tµÇ©¯ASÃ.M˜[Í&fAÍ¢r ƒ'*Πò Tøj5UߨÀ¨UÕ”{5”ÿ4€B”ßÓZÛFs¨©ðT/§@"®4ÛÚz¼5зy[ÏBõ¤šáíP³ÙXSÞ4¨y±ƒãµÃ28ž>}š6QÓô*èÜû¥Q¢Ýz ¹éEÉ(¬¸ÞÉ ZÐyo«œ²,#/UÖÏ|_žëßS:µ€Àá&¹íÁ—ä ßh9b.£øø ¬<0j3y28^¼†·Éˆ9åe¥Ëüj=‡Ar¸¸|{§"Þ‘›ÁÃÞ’¦•_xt‡ÌÿâeùkŸÛ¤ -;õ”Gž}W¦¯K•Ü û›-Ñwà!ñM="Ûg“>m!Íò–ôÜý2½-Ÿ“µ'Ê¿Sx(TþÑ´“ç(6 fù@åYij*Ô@Ýë|À5Œ™Y(ÿ]¸Õ»Xkë1jNj3wBõhvÖþü!€¿[Q^_¨ÛØêep¼Vèt:÷%K–˜6lØ ÅÅÅ´‰šª@ :&É?¹]\îV!ièòR`±Þ‰õ/iAg‹œ4/ÏÞ%³µÒâöòÜ«ÿO^}~ ÜÙJ5^Z$ÉùÅR\œ-ûWúÈì/H+@œþ:RfÏõ•5É'eߌ>@zOI|s¹YkäoNZcÒîuÙtÂ\\‰}§Vßý’_\,WÊ»·këv½_žúª9ò/Ò 8´•Û_^&r÷Éô{´àx\­_*Ÿõ†íåyÏÝ’]TÛñ,hÐñ¬Û¾ɉmcäµ~ËÛA/ –=ÔùèzO;ç¥KaÏm¢28Ú1¡¡ŽË'mò œV8u“,Ÿ¶±V§n’ )›/M±bU»APSï= 55àv¨gÙûÖÀ{P!̹Žå¥•7êùÅÕP·€[A³_<à&~P!³U5å×V¯D[üùmÝ;´ïnByˆ¬®¼V’¡¦5ìÇkOqq±{hli V:ÅEr2ÊYî^•àƒ—×»2èäÉž©ýh-º]Ž–—Yxm`uå°ê66ƒ£-(..v/**ÚtMñ4ý’WT$E…Ç$røí@:¾¾R2 ÔzÇ×iAçÝH9YT$EÙ;äónt.Ó%=½¢‰ë?•ÛqüÛZ9¦mëäV»~¶MN™ëpj›üãVZ=+Á‡ ¥¨(O¦Þ#@OùbÚÓâHßY&É/*’ÂÌäAwgÙ™]$…‡WÈs­ h÷ª¬9RXißr$nì]@_ò‹Š¤¨èäåàØéÝÍr¬Ðbý¼½2M Ž+÷­ÕB£“<=w·d[}<óë}<ëº/§¶—[Á½ÓdOn¥zä'‰ç#ÍTpôM—‚:ž/[_“´joB=Bƒ§„ ž¶*hÒæü Éa%A“ÂÊÔ7çN ÷¯C`4SU°k Õ#gfÔ‹"ŽP·tC…ÉÊ/¬X[þëPÏšƒ£9öà Õà ‹Ï~Ç•·«­­ÇËPÏSšgµyåÏ8¾à+ÊÛ õîÿø?”ßB¢†í’Ƥ°°Ð½°°PhÓ5Ùtfî“\mYÁÑÍòI7ÐIÞ\•)ù……rìrЉ……’—¢—«žÔ½¢Ï“ô|Uî‰-æà-'/×Ḅ½ÕQ€òææãR˜Ÿ.‹þÚLpËž0Oâð„Ÿ((”cþ.í¹åƒH9QX(§v:Ëí€8< ‡ ®Ü·[?’.æðZX(……'dë‡h-ϯ:ZqýÜq‹ô½­¼þ=GïVϼzϺíK¾¤Ï{XÈÝ㌒sE=r%az-8¦I~Ï—­¯IZ­ ޤ!TìÌCÎ êé;€ò™aÞ‡>§TÐëõ¶r Íwt«¥ü®~‚ºÜ@Ô[Õ-ô‡ НjëkÛoY©|këÑ êÅž¡žWÜ­m¯Ô-òîVìW¨žÊ;< Õcy?ª¿}N›ÂÂB÷‚‚¡Mפ¹æ ³Wr./Ï—Ã>®€ ó;²öPžd…¾x9è/(lã8¹Ü5B¬‘ª]a’ùªÜã‘ï«àøi´œ°¨Ã‘Õ/K@nwÞ!'†Ê+m!­Ÿ –CÙ{dJon&ë³NÈöÏ» Ð^^[Ÿ¥Ê [:rÓ«äXû–½k’ôHn(bnIDATY*™RPp\¶|ØY€NònÄñŠëçì‘©÷há©YùT?N¶€ýd²ñ”•Ç3·Þdznû’+{gÞ+ä!ïÉ»bý|9°d úÜ'Uòêx¾l}MÒªep$ ¤ª`× êùà P·e}PÞ;¸ UÿÓüâL)ÔP:5•¨·¦sµïšPþrL3¨Vþ©}¶*°¡Rùu©Ç“ò¡z ázT;@½d~‹º¦ò,á­j[ÀàØô­:èHAÞ! }·‹zÑâýõ’¸¦bÐÉ5}%ýA¯I²+ÛºmUóÈÓ-!¸w†¶Œn€<¨O’Ü‚²í· ÐMFl1ÈŒ{!pzABåKAAœÜá,ݵ^ºÌü+·w"J=+Ùê¹Õr´ @ʃã-ò~duÁ±»|¼ö€äœ’øiJ3@Ì‘«ö±ºàXûñ¬Û¾äIú‚@î'ÙWÔ#GfÜ[!8Öç|Ѧ%ƒ#!ÄîÉÏÏwÏÏÏÚtMôPA§ÏŒÉ®ôYîÁUòf'5\Îë®Oˆ# 7¿.Çòó%ÿX¸¼Û Ü+ÓvgW*7[ö̸_šòÀWû%G[~,£ä¸åú¹)2o`3A³GdŒszÈèØ“’ŸŸ/GC‡ÉM€ÜãSdשü+öeæЂcŠäÖñ|Ùúš¤WºnÝ:Y´h'„Ø7ùùùîyyyB›®&ûU ™¾GN]ñyޤ¯xM:ZtçßüN˜dååI^Þ1‰þB½ôÑjÀ$Ùr(GûN®dÅΖ'!hñ¨èMÙ—Ë;ñžÜHç"åX…íd˾9ª­ èð†l<ª>ËM÷“' hÞJ€f2À'YrÌßËÍ”•¯¶Wo"¿¹XŽ———¾ÞUlAóþâ¾Ï\‡cùÁ-Ü"ïE«¸¯§vË-8®<œwy_ŽFŽ;AÛdIJv-Ç3»þdz®ûr ãz«·ªЭ•´lmýœÃ²mú“Ú>‡¼ÍÇ«îç‹69å !ä† 77×=77WhÓÕän:»ådUëd§ÊòWÚ—·7ËQí³œCáâÚÏA}æp‹Ü÷ÄßäŧúJ¨± _˜¿¿B™Ç¶}.ÝA»òÿ>sOñ˟0NTÏðÒb¿¤çhß;µWf÷5­~2{ï© õ;•²\Þ0¿ÌrK_yâ…—dð÷Kk@€¶2hŽQŽ_^?K"ÌÁ1<«â~žÜ¥ž§l9XBY,ÏÉ”Ð÷Ô-æ–ô욎ç©ϺíKŽÙâvyÇ·Ý/O,ï¾IH÷'ÿ"íyÈ+I²ëy¾hÓ‘Á‘b÷èt:÷E‹™BCCmÞèÒê­5èäæÊ©y¹Ý•A'77WrŽÄÉÒÑ—'úÞ.íšCàØYz=úŽÌÚ”reyÇw‹÷ë÷ÈMPo/]s¸ü³cÛe¤6øußY{åÔåï—®jÐoôž$Æ“UÔ/=Bæ~ö‚ ìÝUœiqsyðé·eÊ*“EÐÊ•zÇÜ\ÉN_!¯u„åÝÐLÉ©öxZk9žÖïK®äæfËáØE2rè_åž®Ž@oë/o~&‰Û\åÎJÁ±Îç‹6 !veC—““C)¥´ž28Bìˆ.;;›RJi=ep$„Ø= Ž”RÚ828BìˆîÔ©S”RJë)ƒ#!Äî±lèNžyân¹¹5pvw=)Ÿ.Š•4íû黂ÅmÈ}ÒÅÒ¬ÍmòÀKeÍÞ 9xð ¤n1Ç49xð€$…O–ÇorïøI|FuL ·®ŽJÆî•2á¥ûå¶6Í-o–»}_ô‘‰rà`†ìö{U:5¿[F¬ORßOÛ&Ó:JÛAî“nûó@›¦ Ž„»Ç²¡³u£K›¾Îw@Ú ”}éé’žž.©‰»e«ßé×ÊQNۦ epLÙ,ŸßiýÐG2sñJYè#c_ì*h=H&ƒ‰kå“; ­î{Wf¬•µþSeXwH‹‡§ËŽtËà˜*É‘ÓdP»ærÇ[‹ÅX]x3ÇÚ꘼Q¾è A÷eü•²6ÈS†l+p$† 9˜±Gü^í$½FÊÆä4Ùñå£âÔîÑ2l~hÓ•Á‘b÷X6t ´Fï¸òÅÒâ~gY•®ÖÛ»HÇeûå@J´øNš, ·§\.#qÍÛÒ¹ù½2mGº$,,NÍï—ÉÛÒ.nZ=B=ñ‰¬Ü@R"GÉ]-zŠnñTyº¤ã˾bH«¡Ž)aVÕÑ”}é’‘ ‚ã³þû$##CÒ“ceŸ§Lõ‰¼ñ|¹Ó Ü+Sw˜dÓðÛ¥Y·ÏdCrÕÛKŽ%wÒ²y3iíiÞwŒDT³nFF†d$o¶¢Ž)²mriÖþ ÞoñÝ´ù²ÄqðRÙ—‘!i²ËïUéÈMC|$.ÍöÇŸ6m !vƒ#­‹_Üi÷J°ì¯´IËæÊ´m)埯x[zÜò¨ÌÚ‘*I®rW‹ž2fK²¤§í‘À·ºœž’¹1©U×1i³UuÜøŠ´Gw¾!ñòç©F_ÜÒst„$§§KrÔTèÔ^ž™8I·s{t%± œÚtep$„Ø=– ]ZZ¥5úùÇG'ÉÒà` –àà@Yê=AþÞ³¹à–×Åwª¤í^ ¶t”g—&ÈÞÀW¤n–!S–KØöhY¿xŒ¼p{sn“÷b%q÷ry³ ¤e¿÷d–ÿjY½tšüýŽfÒzà4‰JN“ÄpÝ"“$--MRŒ å¥/-cJuLÜd]÷¯“wCÐý%¿ DV/w—O´8=)_mO‘´ÄH™ôˆ£Üô”‡ìLI‘OIÛ÷Šë&“ÍÏmº‰'„Ø/–Á155•Òý¼ÊOÚH·GÞ’9›$%5URw©à8Øo¤&Äÿó'äv'š·“;¼-³Cüäãûo´yR|âRÄ´}±|þT/éÔ‚–7Ë=CFɲØdIMMS˜‹ މZ’e‡ÇSâ„ÎòFÀ.µ=KM­«cjª$Ǣ¿õ•[Û@ТƒÜù—wÅ#l¯¤¤š$|܃ҪÍã2k[’*7i›ÌzÜIZô-›L¶?´éš––ÆÐH±_)¥´ñdp$„Ø5:ÎÝÇÇÇ()))”RJ&ƒ#!ľIIIqONNJ)¥ “Á‘b÷$%%¹'%% ¥”ÒËàH±o’)¥´±dp$„Ø7III‰B)¥´a28B잤¤$w“É$”RJ&ƒ#!Äî1™Lîû÷ïJ)¥õwÙ²eâååÅÀ !ö ƒ#¥”6\N9H¹!Ø·oŸû¾}û„RJiýep$„Ø=:Î]¯×›üýýeïÞ½”RJë)ƒ#!Äî±lèlÝèRJéõ,ƒ#!Äî±lè(¥”ÖSGBˆÝcÙÐíÙ³‡RJi=ep$„Ø=– ÝîÝ»)¥”ÖSGBˆÝÃàH)¥#ƒ#!Äî±lèvíÚE)¥´žúùù‰§§''„Ø/–Á1>>žRJiÃdh$„Ø/–ÁÑh4RJ)m€ Ž„»F§Ó¹{zzš–,Ybó—RJ¯w !vO\\œ{\\œPJ)m° Ž„û&..ÎÝ`0¥”Ò†ÉàH±{ª Ž‹/®0´DU.^¼¸ÖF”å°–Ãrn¤r !vOllìs±±±î–zxx˜jk(=<ôq+ð.„hg¯ÅÕ7· ¹ß¯úÊ0ùƒéåêã'ªÚTq( F/8¨:Ab“x#HH6(Ãt z§äwÜ(Cié*Ïž­ž¿&~4Ÿøâ:€ºõEÃÔ°+Go†Ò”yÁ×3ZÎ×õö¿–Ï‚O ¨¦³žM ÚÜ£m þ§³élAøÕélQî…G¯>¢V.„Ì׬«S~€eá¼£j¶Å°Í:5Ï´µ×˜NÍ;º£žVûÐV™ýíáÆ¬àgk!cÎÖÑøNö>Í»ŒmQçr½–ÑRƒb¨eÁž(·ÄYrÛmP”ÓT½æà­Ë³oÜôÏú6ý@Ä1ᔨüÉ-ü¸Ç}ïWíŽä8®n%Ç í£æ8¡ÍöuϱÞ4¬Þ¥@=AÔ3„7RÄj† â¶•"J\‡¦ˆ·2»ÛÑX÷š›’`,¦v¯Ú*¯Ë%ËÓ®po³¦=y¾OûeÍîVÄñõº  ƒvEܳyùU±ïÑÜ5 ¸ÎhätÆ2{Øä޵º‡ËÓží£ß:ýzðXýi¦úS7¼ú†*—Í IDATxœìÝw|uþð'ôPB@ºHŽØåî<ÅóT,?+(6T%¡ VBBB5Ò)¡÷ždCß„Þ UE¬`A@=Îç÷ÇÌ^f—Ýd“Ìf“ø¼_¯Ï‹ì”ï”Ýy˜¶€ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆÈÿÌ@³Î¨ìe˜v–a¾P6ßæ.xJ8ˆÌå¾>m„Øaißšž×ÏB[,ÓìG¿…–n`žÁÿuó@>ÍS°¦Ïb^Þƒ0¶µŸ˜ÿ>`=€/-m` €ÆyŸå€(ªÛ¢ˆˆOÖÄ{Æ®U=³ý^Êeù¥¨Çr0Þ;ë´Þò£Ÿ‚cþÉÏÏ¢?êÁ÷ç5/ïAMÿÐÙ|]ÀFdÿþë;£³DD %ÏÕ`|QZÙµ³ú§¥­È<ÚPÙÇØi©ýp_ßû=ú÷ÏëŒûPß2Í˺hâG¿7,óöÌ—58^° îëÃZ·™ãÕu~~ý‘Õç5/ïÁCæx Í×£à¾ÜVØîÑ=·ŸÃ¼ Ôz)”÷këî¶Œ××Ò}€âfwkø: ¢Ù½€G¤øÃìÆÎ·¹—eOó˜î}ŽøÆNÍ[p 1—×zİ‚—¶}ñ78f5o%ÌašX àŒ9ÜEsž#Íùwͯ¯u?'‹~³Íñ³Ú1–ÐÀ!³ÿ>ЙïUvìŽþn®ëììØîì’×Ï¢]Ûÿ‡0–ÛÛ¼€÷¹~ãhdvü½Æñ!Ë8¾æ)§Û€ÛrûYìßþËÂ÷çpàÉó&+Ï£~/›Óªn¾. ÷Ïìà^ÁÒ—œ.sNƒ#õ÷V^ÖƒˆH¡eý²œ ãŽÇ æëÏ`ì@¼í¬êYº}÷#¯Yú%ZºÿÓÒÝz­P¸eš?À8µSî_¼„qô ÆÑër)¸©xd~±W€ûŽi˜e~Ò,Ýÿ„qSÈ50ޤz^ãø/§ØŸ ޳tÛÿo„ñÆ®ks» äe»óg™¬Õ.›ñrûY Äöäþnb_ìf³Ûí>–ßåj³Íw†û:<ã³—›etpôw=ˆˆjÞvV€ûêCË߮ո¡gU®£bY}·¶ô{À:óï8d^cô3ÜoNq]H‡¥Û6\>*Yúÿ‚Ì›Ò,Ý=wêYGÂι‘›àè9oÅ`³¶ó'€tdÞáÈàh=]š–ÍòfÇ®à˜Ûm /Û?Ë”×àdÿY ÄöؘÞ4§ÿ…xîëñ6än™EDlàkgU ÀGpÿ¶î¬[º½ãº7_å’Õp9GãZ!׳Ýî…ûi)ë uÍq¯·t;Ž+OZOŸFæ5i–îÙÇÕî·¼¾ãs9eGp´ÞÌò ŒË\§gZú*8Þbéö®¼c´*Œ#>Õ‘}´+8ævÈËvg·Ü~±ýö¦â0ŽNoôhçÿáÙ ãÞî>.÷#ÿÊå2{ÇVãÌ÷2ßv¯‘BÏ×Î 0~)Ä×ÎÊz§á"ñÊxÀs0. w]´žÕ0àýquÌéyÞ¥x Æ0N]]´ô»Ù£Ý–~k-ÓM³tÏ.8¶1»í´t›îe²cGp|ÖÒo–¥»çª ŽÕáþ•z–á­7îXCŠ/vÇÜn@î·;»åö³ˆí°70U…qÇ}7a›z,S/óQÆÍK®a®Ïå2{~¦ï± _ Æ/Ö(8Šˆd#«Uq¸?Ó곪ãž«ûƒ0¾P+Áx¸¯«û K{Ö/àC¸òŽÛç=¦å žw´Æ .!pÿ? ã:ª 0FmïAËxi–îÙG×£w‹voÿ@֟ל¦÷àûôíÙÌ·µ¬˜ÊÍ2?ççt¬AÏÎõ "Rèe·³ û¯3XwVÅ`\ðîí‹w®üU•0¾È­ÃYŠ•6]ýúYúÝcéþd>kΪ9®¼ ÓUspåO¥Yúû÷;:}ít}±#8†À8*㹌S ´¼þ™;j»ƒ#`ÜÕm=êìªãÈþÎY»r0§Û÷íÎ.yù,önÿ@֟ל¦ÒN˜ç}±Û‹õúCÏú ÞC{N—¹ŒgËz»È,oAÏ®õ ""¦ª0N½ Yÿ¢JI7s¼ãÙ|Õ³67ŠÃøE0ž×þù*ŒšÀø“šÁþŸÀóG(ŒõÝ À+0n\Ô5€þú+mžì^ö@^­B`lÇxÆ*ž€qÄ/«m;§Ëã?}a|gý#›öü]"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""âÅeÐ=mw0ÀÃh»0)à'ëù³[g¸¯oÃÒÕö˜Óû@õ|žþsZ»óÒ¿"Ü·Í’>1_{Ögú™Ãøc‰eÜ.6ÎsN‡)tÓ̶§ íÂÄ[(LƒûºÉïàÌð” j>OßÎàèª1~L·€ó–qv(nÓ<çt8‘BGÁ1”ƒ]!% Á ŽÓÍiíâc)/Áqµ9ŸaZ[ºŸ7»eå!\8¯±ižs:œˆˆH¡cÝ9[wÖ`ì /Á4#”²ŒWÆ)¿_Ìá¿0@Yx?:t@i×Xaï[caäpŠèàˆ¥­wáBŒ2çN˜Ëj GY-‹§’2à~„«ªežûšÝ®·,û È …øX7WY†yÀRü`€^æÅ%»uê9ÿÞŽÜÕ{p°ôkoŽ;ØÒí6³›?ë·$€XßøÀ\ï#÷Áq™Ç°[æ«—¶\B$›ÃeXÚð2¬¿óìÏpÖeyÀBg`¼?vmŸ9Ù†EDDrÍWptýk­®æ8å|jé~Îò÷l!§7ŒCÁ®ŒÓ„'-Ã^´ü=ÆŽÝßy(fŽãêþ»åo‡9¥lµ´õ™e˜8?—Å[¨qQÚcßÖ2N’9Ì æëSª 3”=äcÝT´ c]W=ëe>#$g·N­Š™Ów…íï P îÁ±Œ£‘ð1Œk"]ý§˜íú³~‹ÃS¾N/Û³´WÇÇzŒ€ ™ÛÑËài§þ\×@¦ú¹.²ZO®àêº0À?Ì¿×ÀýÈV ƒ£?ëÔ‚c€wàDž³´áÏúígóŒA\íºŽþÙq£?:{,‡·ª—Ãyöw8_Ût ¶ÏœlÃ"""9–›àøªùú0€ps˜ÚÈÜQµ3»¥™¯ç™¯G™¯Ã8VÆ5‹¹ ŽÖëïzÀØaßeév=€'Ì¿DæM7ÃØÑãº6—ÅS+¸‡Ž§a\ù§¥Û0sج‚ã¼,†²Žþ¬Soü Ž7Z–%Éü÷'G!ÿÖï –6€ÖGâäGp,ŽÌ€ŸUEšÃû;Ïþçk›¶kûÌí6,""’c¹ Žá~çï×–¿O!sçe½ãt1Œpåk§ýr‹#óú?øÍò÷R³9-ÝOZþ c§îï²x*gγkØf0N[ïnmkë-”y®›J^†²Ž®ð‘Õ:õ&»àŠÌu·Æ ®;ÁçÃXwþ¬ßÈ Þ*?‚cËôyô+…ÌëQ?0ç×ßyöw8_Û´]Ûgn·a‘Ëí©ê›aÜâznÜÏ0ž#Wß2ÞuÈÜ)§Ã1qæ°—`<{ðIdîäîÈá<”„q´å„Ùïs8ëMa0ã =GqåsòüYoV óˆQynçÍe¼GÏu“ÛàXÙ¯So² Ž-m¸pwK·{Ìnþ¬ßÒF›Ãü#Ø»NÓæGpìiŽ÷'¼?®'ʲ\®÷Üßyög8_Û4`ßö™ÛmXDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD¤¸ÀdþäÓ0ÆÌ‹=\?Wö²þ¬{Ìá¦h>7Û)@í[5†ûϳ‰ˆˆH!U™¿K¿%ºÀ!dþ®(<¬Ìg·ÃXÞñjßú;·”ð1\Q Ž!ÆAÁQDD¤Hè c§~À}ÈüÑz0™G ¯Ë÷¹Ëù àÃöàXÆ2ÌAæ²*8Šˆˆbµü#¶ð1ŒõˆÑûù4_Á”_Áq®ùïyW{®0ÇP§áEDD ¹·`ìÐ'f3\§U߇û©ÕÊÞð±ÙÎwRÜë¥ñæ0MÄøÁ|ý5€H³Ýgì†qŠüW᪊¥ W Ù#lÍpÁlç”ö˜®ÃìßÐË<­6û]# ïÀ•açaË𥠰 ÀþcN· ŒSþþpÇþ†™¯ò2¾¯à˜“uÍ,pÆþ3뻼G;–±8€wÇÓ0'8‘L\!¬¤eºþGxF(#€­z"óÆ PÍ~'a„±d7¹¸æÓ:]_\Áñå`Ü€DOx ç-8ætß‚ÌëT¿°À óõ'¸28ÚµŒž¦CÁQDD¤ÐûƽzÇ 0ß7@K¿Â8Ruîwc»‚ã—0®sy™Áç1K÷zfT0»YOžp­eøFfÛð¤¥{N‚#àûTõ`³û`¸_`“—éúb Žp2¯1µ¾žÁ1§ë¼4€£æðqÈvÇ2¾p F ­éQ®é®ò2 OžÁ1ÀX³Û|Ë|{Çœ®ó›Ìׇ`¬K«0NE[ƒ£ËèIÁQDD¤pÎéÇ6æx›aO®£hÖá Žÿð¶2OQ[•pÞƒãðž*¸æñ{§{‚cm\yí£·Ú ÷#uÞxG×|avoovó Ž9]ç/𝇸˜pŽv.£'G‘"ÀuƒEÛl†+ ãF•u0ŽFýÝo¡á› óÈŸ‹+8þÝcXWp\íÑ=«àx™ÁÐÊÕÿ7§W¬ƒãø]óxÆuú¨¿Á{¨³ò ÙýG=ƒcN׹뀗} ÿ4܃£ËèIÁQDD¤c‡>5›á˜Ãý#Ô¸Ž~m‚÷q‹Ù?ÙÒÍÎàèëˆcŒÇÛœƒGð/8Ö3»Æ•§}sÊWp,†Ì€5Æ]ÒÞŽ8ú»ÎŸ3_Gû˜þpŽõ`ß2zRp)øÆNý&ÄH0‡Y #àX¯· ÷2Îsø–nvGÂûãgÚ›ýv!óÑ1®àØÌcØ0²ñ'8–³ ÛØËt]G÷"½ôóä+8ÀU–éÄÁ÷5Žþ¬s×µ£Þ®q,‰Ì;ê]ÁÑÎeô¤à(""R„xÆNý"Œ‡B[ŸÕW™wÚÀ –ñÂû¾wÁ8"ø_G±\ìŽ_¾. -ý:Yº/2»õ·t+†Ìg(ú Ž“<æg”Ù};Üo&jãºÊ?ÔAö² Ž€q:ØzM¡õ®êœ¬óÒ2Ìá‡Ãý®jëìÖ»ªíZFO Ž"""ED dÞÕëªý0ž·ø;¼ ¨ÌÇß|`%2ïÔõvdÊÎàø'2ü½@ºeºIp?ÂÖÎÒ/Ùœ}0B–·›c\§„4׋ëHe€Í~—a<ïp¥mË;»àX ™aל¯ó›a<ÀÛ´W#ó=påûj×2zRp)BB`< p%Œë ãèÒÇfhéc¼*0•ëaÒßÃ8Õ/ÇØ¿ƒñ 4óù‹(ûaü®¶ç/Ç„Àx>äd 'Œ€è:šj Ž®\ÃZÃNy½Ù#x^†+ïÏJvÁ0â¸289[çÐÀ_!3@öFf@öüËèIÁQDDDò58– ò¼ˆˆˆˆH¦à(""""~Qp¿(8Šˆˆˆˆˆˆˆˆˆˆˆˆˆ’’’†$%%1›¢vÔŽÚ)ü툈ˆäš¹3r¤›ÚQ;j'p툈ˆäš;¢B»ƒT;jG툈ˆˆˆä€:‡(XŠˆˆˆH–¬G%ƒ=/""""R€)8Šˆˆˆˆ_EDDDÄ/ Ž"""îŽx8—ãVp@eûfG¤àPpq§à(⃂£ˆˆØ¢=¦Ã[Ø `€Ÿ|à^s˜2¦ø€ßİœP@”9ÐÔ?«¶C< à+¿˜  ¬ÙïnGÍöÒÔ±o±E²§à(""¶(B;kpüÀ[*ˆp@I8 1€ÛœÐ îGøFØ«`*€Eæ4²j»¹ÙÆ®ð€Ájø@UÌöv›ãˆä‹"ô9‘`*B;kpüF€Ö0Žö•ð€/´P F+÷àXÀ5æ¸å °Ñ|UÛ£$Zæ§9Œ£‘ÝÌñCÌîUÌiÕÊó‹ø©Y‘`*¢ÁÑæ`¾>b¾.  ;€~  ܃ci#°À¸G_m/0ÈË| ‡qŠúœ¥~…qTRDDD¤ð(¢ÁÑæàñº €úf÷ú>ÐîÁ±+€T5‡{ îÁÑWÛãL³ÌO0®—\bé^ÂìW<Ë*"""’ÿþbÁñU×^  Œ_"aÇÔÐ ÀV7¶Ô°Ë|’MÛ7Â8šø×Fî„q².Œ›hF‡›ó kEDD¤pù‹ÇpÉ0Nÿ`Œ€XÆ)éó0î NpÆ‘Çç`„ʲi;ÆÑÊïü `.ŒÓàð€ÏÍéî„qŽˆˆˆHáR„‚£ˆˆˆˆ’‚£ˆˆˆˆøEé‘Âãpê“·]÷BꑵÎg8:P¸:ìèxùÈÚNç¬{~sFj‡öÁ~ï¥àЙ)ðŽ®{vê±õ/ütbÿøn=ùónUëÏwðïÖóìáI—mè|áØú~îì\&ûwJŠ:]Ë,""Ú‘uÏ,:µ£ÿùÿžM'Ú©ÊçúóÜ6žÜÞ÷‘uÏ. ö¶ Á§à(""Ö'Ž'ÛÛÐùâÏn&Ú¦ Rýy.ÇÖw¾ð‰ãÉvÁÞ&$¸ED¤À:ºþ¹9çŽL"Ü¢ rýp(áò‘õÓ‚½MHp)8ŠˆHudm§_þøz5yΩ rýçÛdYÛé|°· .G)°2ÈsU þ<»Ž‡S;^ö6!Á¥à(""¶Äc:2ȳëU¤2þâEDÄØ¡Á1UU@JÁQEDÄ Ž?¤¨ H)8Š.""¶Xpü~ª€”‚£ˆˆˆØ"pÁq•ª€”‚£ˆˆˆØ"`Áñ»åªR Ž"""b‹€Ço—¨ H)8Šˆˆˆ-© H)8Šˆˆˆ-¿Y * ¥à("""¶ØÀ¿™«* ¥à("""V†£ùõ,U)G)°2È33U¤E‘ËŽÓT¤E?9(""V†£ùÕTU)GQp‘+ÃÑ<=9_kKÚB›¹‰Q Î[}Æ:ùæ{NîØ² _ׂ£(8ŠˆHeljùV[ÒpèÌíÜôÑüäô¶|ñÓ?þ‘¯OÝÊ[æçÛúQpG)°2È/Çå[½ñÞf:ö}ÇNý^(j݇ß3vÆÆ|[? Ž¢à(""–ßÍ·ŠJpr×ñ Üûé¥BQ»Ž_`ï±Î|[? Ž¢à(""¶ØÀ¿“o•àäŽc UE%8ómý(8Š‚£ˆˆØ"`?9xjd¾UT‚“鿪ŠJpæÛúQpG±Eà‚ãð|«¨'7:_¨ÊŽù³~E[,8ž–o•àäÚý?ÛS[ó†Šãi?3iÉÖ©×›“v{öG.5”më…1¤X%Ö»÷NÞö“ßÓ‰JpæÛúQp[,8~“o•àdÒ¾Ÿì©´E¼>¬-oþ‰+ à5u{qÜŽ+‡[³yï¨T‰7½º‰‰kRø\« ¬öÔF.ó•Cõh IDATs:Q Î|[? Ž"""b‹ÀÇ!ùVQ N®Øu.õgNÄê„1¥X¹MZõ WlÝÅç[†KVmϾúñšÚØù™;X5 ¿‰=Êe»ÎqÞø‡XáêÞ|g‹Ñæ‚)O1¼zŽH÷oŒà˜?ëGÁQDDDl°àøÙ[ùVQ N.Þ~ÖïZ¸z [W¬Ï»cwrºc'ûþ_}–iÏñég¹xݶ kË~©g9oÎ+¬ŽR¼öåd¾¿ñ0£_nÅÐFñ¿å[ŽïÝœ¡7ÎåLW›+ßaƒÐÛØÇñƒ_ó•àÌ·õ£à("""¶\p|=ß**ÁÉyéßû]ãú·a¹gpJšñzîš™lv».ÿ–óóÙ"ìvöNúž‰‰ýX#¬=:Œágôe­º}¿ñ+Ž|©+ܱ”Ó]í&Íàu¡­ùÒ²oüš#8æÏúQp[,8~:(ß**ÁÉY›¿ó»bŸ®C ”e+„³\…p–«Pž%Ê´fÇÙ§9+i›W¸‘«¿ãôéýX«vO]kŒ7}z?ÖªÛ—±ëÎpTϦ,Óf'›mÎ\4šõBoa÷ßú5Q Î|[? Ž"""b‹€=üÄÀ|«¨'§­ÿÆïÙŠïXÄñ®n©G;y;Ç­ý†ÓVÎe³ ·±ûòo8ej_֪ݓo%ÃM™Ú—µêöá”o8>þ–¯eü=mý7œ˜ð4ëvæëIþÍCT‚3ßÖ‚£ˆˆˆXGÖv<ÿŸ#}Éý󥢜œœúµß5qöh6ªØœíÞÞÊQ‹¶²G‡Ö,×ðmF'}ÍÉËæ°i¹ëùÌÜÓ?¥kÖîÁ×VãŸÒ‡5ëôák¾æ¤%óxcŪ¼ñÕLX¶™og凓8ÖÏy0‚cà×ÍGúðÈÚŽgƒ½Mˆˆˆˆxu4õ©äs{»’ÇûæKE%89.ùLꇾݓ«—%P’aMžçsÓ>7ú­ÜÂG¯¯Ê2wðÙ¸^¬Q»®0Æ{gbÖ¨Ó›ƒVžá¸äÓ1æu6¿:ŒÅJTf­;Fpð²¯üž‡¨g¾¬›sûºòغŽË‚½MHpéà""R`J~¢í±uO]üï‘(òX¯€WT‚“cV.T•à øzùï‘([÷ÔÅOO¶ ö6!Á¥Ÿ‘íHjÇy'Ï\úóh$y,°=ÕÁ¡ ŽqÄò/ EEÏ;Ê詎€®“ÿéÉÏ6wºx4µãÔ`o | Ž""R }îì\æhjÇ©ÇÖ?uþÇ}/ò÷C]É£ÝRÛ7Œfÿ N™œ±‹OØŠY|’oÍ9“ҸsãÛ×ßGºó?<··3®êüÑuOÍ öv ƒ‚£ˆˆ ‡SŸ¼ýغŽËޤv<ŸáèÀ@Õ¢yÃ8h¢ƒQ Î]ƒ&¦rÉü˜€¬ƒÃ).Iíxþhj‡dž+G‘‰z7}ˆ[à{7]7H¡¦à("" ŽRÔ(8Šˆˆ-ô˜Ž+)8JQ£à(""¶ÐåJ ŽRÔès.""¶ÐåJ ŽRÔèÌ‚ˆˆØBÁñJ Ž""""^(8^IÁQDDDÄ Ç+)8Šˆˆˆx¡àx%G/¯¤à("""â…‚ã•EDDD¼Ðc:®¤à(""""~Qp¿(8JQ£3 """¢à(E®e G)jEDDDÁQŠG‘Qp”¢FÁQDD$@¥¨Qp‘"£×»é½2aëÞ¨±ÎËnMåV}Ʀý6pò¶=Ç8Ÿ ö{&…‹‚£ˆˆØ"Øéè71}Ü«“·_\µý+úòwþê?*uàä%®Úþ_¼íRÿñ[Fãý’ÂIÁQDDlÌJä;éí½·ó×>»ÈC_þ¡ò³öŸúƒ§ì8ßëÝôÇòû=“ÂIÁQDDlÌÊ€IÛ$íþ–Ný®Êa-Ûzšý'nÛŸßï™NÁ>³ ""ED0ƒcÔ»ÎË{>½È>ÿM•ÃÚyìWö›ö[~¿g"""òÔà˜àäžO/©rYQ Nv‘üìà¸óØEU.KÁQDDDòU°ƒã¶#T¹,GÉWÁŽé¿ªrY Ž"""’¯‚7|^•ËRp‘|ÌÇtD%8¹áà/ª\–‚£ˆˆˆüeD%8¹vÿϪ\–‚£ˆˆˆüeD%8™òÁOy®5kF°>Àл—rÉ>k¿Óþ@8Æ|aÅw¶L땾ˆmÂÚò5§mæ°Å_z¸ˆˆzQ N®Ùûcžkåêᬢâ#ŒÞlé—žÌUrŸ_ö­-Óú_9ñú°¶´ÉÆ6sX Žâ/ýä ˆˆzQ N®Ü}.ϵle<뇵á?®»†ûùÿº/|ÿ9Ö¸öÿؤRS>»ä®Üy’c<Áz•J(Æò ŸdÅgŒáwá¤èÎl\¥4Q²>8œ‡Ø³E6|ˆËwŸãÊÝßrbÏ&,Ók|¤e°dÕöèøÌG»g9{ØßXõ®‘|îî&,_¬$Ãwdϧ¹Â†åVp)8ŠˆH¡•àä²çò\‹—dz^ø½ìÙ·-«Ü½Œsvžã²§÷H6î9m«4e§ÅßpñÒXÖ+#Ÿšð!SvrÀ#×0ì_«8oç9.œ?˜uÂnfÇI9}ñ">v]Ö鱃¶a¹6ïqúös\¶uŸotoq‚K7,dë°¶ì¿î\ížebÌm A8ÛôNå{)»9¨c–®3ï¤ç}¹Å_ Ž""RèE%8¹xÛÙ<ׂ¥ñ¬Þžƒçg“òµg¹xã:þûš›øâìåüûUMùô‚¯¹hóQN\ñ)ço;Ë…>â›Ï^Ë27Îgâ¶3ùt}Vn¿†³·åâm?pÖÜYìùÎAÎ]Ïzïfÿ”¸`é6¨|:~àâµ Ø*¬-û9ÎfÑîœ}+K^=€cœæü®]ƶ•êóáÄ3y^nGñ—‚£ˆˆzQ NÎßòCžkÎâ8Ö oÏÁ)ÙµUmÞþÎgLœÁšMãù®c5Û^Õ”çáüMûÙï™ûؠε¼¦ùݼå¦*,uã|NÛr’¯ý³ kwÛÍ9žíoÞ篭Ê[Fã¸7±Ò ù~úœŸº€-ÃngŸä²h÷{¾7äV†¶šÎ©éf{›vðÉzUyÛØSy^nGñ—‚£ˆˆzQ NÎMû>Ï5k¡_u|ñoåUíæpðØ j/g¥®bÛ«š²ÃÜ/9vÀÍ,Ûh0ß^u†sÓ¾ç{1mYþ†yœšö%‡>v +?Ìéf›Ó¦ä£ý·rFÚWùrSV¾g Ÿku5oy‚sÒ¾çÜ”ùlv;{­ù&‹v¿ãä·oaÉ«pøs~S–òöJuxÿû§ó¼Ü Žâ/G±E°ž¸ù»<׌ùÃX'ü~öOþŽ3a£ðƬ[íz>5÷+&¦¬ämW5á³OqT¯–,Ûr$ãS¿á´¥ËùpÓR,Öò=Žßô§¾Åjnåããpòò>Þ¬ëtÝÁi›¿ã´™Y«\ –»êQöO2§›4ÍË·á‹‹OgÑî·œðæ-*°ed*Vìdï'š°Tí>ŒNÍûr+8Š¿EDÄÁþÉÁé¿Ís½?o¯©x?û­ù–Ó×È皀¨7„qk¿åô¤•¼µr>–xšÓV¬`ûVUY¼XyVjø; z…ʇ±é«ûùþ†ÏýJÖ©X‚ gíæÐäoŒi¤nfû`ÙvK9Á5ÝÔmìxCU†„þ“3—ùl7á[Ú¸￵K£8Ë7x‚ÏOûŒÓlXnGñ—‚£ˆˆØ"ØÁqêºoŠp}Íw^»•eÛÌáØ´¯à(þÒÀEDÄÁŽ“S¿.Âu†£ß²×Ïæ;h_ÁQDDDòU°ƒã„”3E¸¾âðAFp€öEDD$_;8ŽMúJ•ËRp‘|ìàøÎêÓª\–‚£ˆˆˆä« Çw—G,;ÅQ+¾Tå°F®ø‚Qï:/ç÷{&"""aÁ Ž½Ç¦ïkö'¾ì UkÈÜ#ì=6}~¿g"""òÌÇtôz7ý±Þã·^ºðS[rJågÅ.úœ½Ço½ùNzûü~ÏDDDD‚&*asBïñ[/¼6ócF/øŒCTù¨èŸñµ™³÷¸-—z%¤ ö{'"""’ïzŽq>ÓklúQ ÎKQ Nª¼W¯wÓ~‹zwË6i”œÒÀEDDì7-¥¬ úÉAû)8J‘¤à(""b?G)’EDDì§à(E’‚£ˆˆˆý¥HRp±Ÿ‚£I Ž""b =¦Ã‚£I Ž""b íPÜ(8J‘¤Ï¹ˆˆØB;7 ŽR$éÌ‚ˆˆØBÁÑ‚£ˆˆˆˆ/ ŽnEDDD|Qpt£à("""â‹‚£G_Ý(8Šˆˆˆø¢àèFÁQDDDÄ=¦Ã‚£ˆˆˆˆøEÁQDDDDü¢à(E’Î,ˆˆˆØOÁQŠ$]Ë,""b?G)’EDDì§à(E’‚£ˆˆˆý¥HRp±Ÿ‚£I Ž"""öSp”"IÁQDDl¡Çt¸Qp”"IÁQDDl¡ŠG)’ô9[h‡âFÁQŠ$Y[(8ºQpñEÁÑ‚£ˆˆˆˆ/ ŽÑξáÃ’{Ä wô:“A•½›q9ÞùC|j丸å5í~ÿDDDDÜ*8Æ8º·‰KéùÓìï^ÜúÙ&úú€ÊæúèÌÜñ¹“3¶ºïˆú!ÆÑ½ï¡ˆˆˆˆ›@Çhgßð¸”?%¼ˆÎ|¨Ê‡ZŸ±ê?ÃRzþíìn×û("""â&Á1>5rÜì] ¿ï?³ªü«Y;ßù=>5rœ]ˆˆˆ›@<¦#ÎyÖyÂÁ¿Ú­ÊÇržp0ÞÑóŒ]ˆˆHÀÅ&G\ÞóåVî;½C•µçË­ŒMޏì÷_DDDÄo1IÜóå6U*&)BU’+èà""R`Å$Ep×—é¶ÕÂuXúúW¸ê¤µ»ƒc ãUÇsÓ‡¯±yh#¾ºËé»ÓøÌÕ *´ã¸ŒÌîÛwôbC€×½>‡[-ÃošÛ–[öåªÏ=ÛJã¦Ôü¿›k°\ˆxÍ?:phÚ:îÌr9Ò¸vRK†ß5’›l\7ž¥à(Þèy­""R`Å$EpÇ›m«-ή¬zîܘÙ=c4ï¹ê*>´,…Û>Äæ¡8ÀÚߣœI³fż®rE¶›³æÝ·ndC€(Ý’¶­ÿ_÷ sng޹â3÷v¶ò¨ÊæQq\°?™ëvOeÌ‹ Z¯gÍj96Ñ1±%ÃïÎ 6®ÏRpoED¤ÀŠIŠàöSí«Og3ªI66Ÿ[ÌnÝÃÊÕÛsÊáܺïU6 mÈ;×ûh#…ï?_5žÍI]k°â}q\{Òè·e[O6 kÁç¯Á°;qå§F÷õ³ocޏüS÷¶Ò6¿Äºe[sØG–îøü}0v×:nZùk6ëÊEæx™¯7Ð1¡Ãnz‘/ß[›eCбb“»øŠc ·Ú¸®ÅG)°b’"¸õÔ:ËÁÅÑ Ú*ŠK?]Ç­§VsbÇ«XµS7œZÇô}Ø,´!ûïLõ>þá>\«ŸNIâ&GÖªÔ–#®åÖS똶­;…ß‘ÛâxõŠüç{‹˜~j×ξ•[DšÓ³TÆXv¨S‚•n{˜}gLâŠ#kÝúoXÙž5›½Ì…Ÿz¾^Ëä Í ”ãíÑc¹|ßtÆ¿T—¥¯y‚ÓŽØ·®ÅG)°b’"˜~ÒakmÞ֓וkÄ~Û’˜žñ®Y…/_Áô“:÷¾Âf¡×ò•É^Ç]»äְ߬gs0ýØD>[¯ÿ6m1ÓN:¸yk76 ¿™£>Næª÷þÆðjÿâèRè˜} ÃZôä’^æå£ :øaÞyËÕ¬P2”Õo¸“/M›É '\·â~Ölöç›ãe¾Náš ÍXºþsœsÜlëÐ0¶«\O®YeÛzRpoED¤ÀŠIŠ óód{ëÄ,öoS–‡Î`ʪ‡X£Öýœ˜aôÛ¼§›†^˾Û×xw)­@ K•)ÅÒeJ1`évopõgÉܸ%‚ ÃoâˆCÆ4ßQžÕŸÁÅ3oaX‹î\t½½ÍÇW0õHæt6žÅqÃþͺeªò±˸vù}¬ÙìEÎ=nô_»ì^Öhú"çOâªñÍXî¶×¹ÆÕÞ±)|©Q8̶õ¤à(Þ(8Šˆˆ-ñ˜Ž˜¤nþ|µÍµ’KF5cù–9ä…¬þìH¦šý6íéæ¡ ØwûÊ+Ç;û«TãÿÍÆ¥»gqéîYœ?÷~V«p#£?\Å [º°aøqÈ~}Z[T¨Å‡_iÉ -ºqá k{«¸rB3–mÙƒ‹¬Ý?MäÀV¥Ùpè4&/¿—5š<ÏÙÇáWO¹žå¿À9ÇWqåø¦,Ýà&3Ç;8”íªÔäÓŽ¥¶­'GñFÁQDDlˆJLR7~¶ÂöÚ°«[—-ÃÊåªðáå 3»ïîŦ¡uá˜Å•ûç˜5—IG—sUb[V¬Ùž3,m}2‚WåMgqmúK¼6üÆtõ_Ìéë³@4ïÊùÇÝçaýö(¶)[†Í»ö㸠ïsÉŽ Lˆ»“5BëòùÔÅLM}’W—oˆ³˜¼;†Z”bHãç8ûØr.×”!(ÏÛ¢GpÑÞ Œyñ†6zšÓØ·ŽÅG±E ‚ãúO—Ù_ǧóõ¿'ªÝÇñgv_·+ŠM£®þ.cþ]ŽWuŠg’[[ó™Ð!œeþÖ œ/òÚðwÀÒÿãQ|ª>ˆf]8÷˜ç|,áòe/ñÞ[k³r(ÅJMnf§©SèøtכʷžªÇrCë´f§±Q«g™xt)—ŽkÁZ<ϧî¨Î2(Îðwsà†\gã:Rpoôp±E ‚ãÚO«‚P Ž"""0 Ž©'ª‚P Ž"""0 Ž)Çç©‚P Ž"""0 ŽIÇg«‚P Ž"""0 Ž«%ª‚P Ž"""0 Ž+Nx=S¿4[N™õpûz³uÅÆ¸'‡ígŒfÿGk±l¹†ìî|?_–ÇŽRp‘€ ÔÀWðêT¿4[N›õpû"Ùºb#Ü“Ãö÷÷cÛ*uøÒÆÉù²,v•‚£ˆˆˆ*1I\vdJÀëéú¥Ùrr®¹ŸuZ<À>Q-X³B‹UnÀ‡§â’CCø\›P`Éê­ÙûNO|7Ô eJ°òÍws s—™Â…+ÿÍkZ=Ä^]²rÖ¼»M9`‰*͹y G½u3ëU.N „å®»…ÝRÆóqx<ÇúU+A” ãµ=ϱMæ²#“|N+¥à("""…JLR—žðzª~)¶˜4šóWßË%ʳiï~œ¶/ŽC"k³LãG9þÐD.ÙÓ­*6ä+»&rQú l^…w'¼É;Þd¿UX¦Õœph"ç¯ü«—åÕwà›+ã8oo/Þ^­#·NàÂu²n…zì˜ÍÛÞdÿ•voÎ9<‘ó“à5ë³ÃìXNstç£ÍʰÎ+oq^Ó ä:Qpoôp)°b’"¸(c\À«cýRl>iç®ú7k„·äàÝF÷¹+ïa­zí8bÿ8.ÚÁ–¯e¿ïrRt–»íENûÄnáöÙ¢b=vK˹+îfµ°æ¸s¬ÑþÞHÞV­>{lË…ûã8qópÎËÇû†ð®ÕXæ¶®œ™1†#^¬ÂÊö⬌q\”1–‰k^b÷‡p|Ó ä:Qpoô“ƒ""R`Å$EpaÆ»¯õK±ù¤áœ³ê^]ïNÆï7ºÏYuk׿‹Ã÷¿Ë…»»°eÅkÙwçhŽèzR,V–åÂʲ\Xi– ½†RÆpΊv¬Y7³…{{˜Á1 ?z›ý»´àµ ªòšÖMxËíåYú¶.œ‘1‚¯ÿ»<¯yåuÎs›·¬§Èu¢à(Þ(8ŠˆH“ÁùŸŒ x=Y¿›OÆY+ÿÅ«ëßɸîn¯w½Ä–°ÏŽQûFm†ßÁé®6Æpô²Aœvp g-¿‹5ëe¶1O7ÞZ­»§äø¡uY¶é½²u$ç2†ï»–åo{‰Ó>ÎØg*³ò‘œi¶9cÙc|,úŽÊbZ\' Žâ‚£ˆˆX1IœûñÈ€×õJ±Ù„¡L\ÙŽW×»ƒÃ>0º»½ÞÕ™-*ÔæË›†sÖúÇØ¤RMÞ7n'oÀWºÔfùfpôG#9sÙ?YÓÒÆÜÝ]yKµºŒH‹gÂkµXö†G8rß&:»ò‘VÅY솧9ùÐHÎXòOV«Ç'g¿Î©é=ùd벬Ûogd1­@®GñFÁQDD ¬˜¤ÎþxxÀëñz¥ØlÂÎXy¯®÷Æ|`tw{ýá+ìt{y†”mÄž[bùîÄ;ØìêRгbë[ÙÍËÙçôew°¦¥Ù»»ð–juÙ5-ž³¶uåƒ7•gñb¥Y©éõ|jx;6®P†Íâ3ñÐPÆEßÀº•Š!¡¬ýà#ŒÛÏÙó9­@–‚£x£à(""VlR×Ë3Æ2ñÐ0U>—‚£x£à(""¶Äc:†¥t?9õƒ79óàPU>Ö”=¯36¹ûWv½Rt(8Šˆˆ-±CšÔ-~Ìæ~¿O?8„ªü«1ξc’»ÇÙõ>JÑ¡à(""¶Ä%ÚÙ7<6¥Û×ãw äûÞTåCÝ>€1Éݾvö ·ë}”¢C[êHDtr×¶±Éݾ½©×ùñ{pÊþÁ|ï£×T6Ö”ýƒ9iß«µ9êØäîßÅ8º·±ó=qÈSXÑÎÎeâo Ké~26©ë嘤ªì«Ø¤®—cSº}=49"AGEDD$àtí“›!h)ÖqQpt£à("""â‹‚£G_Ý(8Šˆˆˆø¢Çt¸Qp¿(8Šˆˆˆˆ_¥HÒ™û)8J‘¤k™EDDì§à(E’‚£ˆˆˆý¥HRp±Ÿ‚£I Ž"""öSp”"IÁQDDÄ~ ŽR$)8Šˆˆ-ô˜7 ŽR$)8Šˆˆ-´Cq£à(E’>ç""b íPÜ(8J‘¤3 ""b G7 Ž""""¾(8ºQpñEÁÑ‚£ˆˆˆˆ/ ŽnEDDD|Qpt£à("""â‹‚£G_ô˜7 Ž""""¹e†JfSÙ¬ÂÐN§N¬¡‘ÿûßEa¹ÔŽÚñ·‘<)h;¶@¶£à¨vŠx;NÚɵBºƒTpT;jÇGe×–ˆˆˆøG×8Šˆˆˆˆ_EDDDÄ/ Ž""""âGñ‹‚£ˆˆˆˆøEÁQDDDDü¢à(""""~Qp¿(8Šˆˆˆˆ_EDDDÄ/ Ž""""âGñ‹‚£ˆˆˆˆd¯LùR±5¯­ÂÆ7Õá ÷4á}/·]=tM·—‡&u}&vM—v±É]š{EDDD$Hb“»4Mê>f¸£×ÉØ¤îÿOêýûØuoü6}ËÈßglu)qǘ_ßß~Âæ7~¹¶ïùؤn—G®í} >5r\ìš.í¢}ý """"@C“º½ïˆüjäÚ¾çï}ÿòæ£)<ôõlëïöróÑ.û`æå ›ßø)6¹ÛåáŽ^ób×tiìeÅ®éR¸#*#aý«Öf,çÁ3ûóT»>OçŠ/¿»aðOñލ3C“º>ìe‘<ŠIîúT\rßWìOäGg>°½Ö^ÅIiC. wD}›Üí±`/¯ˆˆˆˆä‚qj:êÒÖO7pÿWûZë¯äHGŸ‹ÃQŽØ5]ê{ÎK´³oxìš.í†&EDÆ&E ‰Mê>fxj¯9±IÝÇÄ&u{mhRDd´®ŸÉ±IÝÚÇ;¢.mùt=?üjO¾Ô¾Ó;¹pÏ”ËÃ’»_ŒMêÖ>vM—vÃ’{NˆK‰ünXr·Ë ý”¸cÌ¥EûÞã’¦qåG³¸äƒiœ¿{âåÄc.ßôÚOñ)‘‡;"¿ŽOܵm°×£ˆˆˆH‘íì—Üýâº#+øÁW»ò½ÖYÁ‘©}/ŽZÛ÷×%L¥ó„Ãïq÷žÞÎ ÇÖpÁÞÉ—G­íûk¼£§®¡ ”᩽æ,Ø3ñ·}§w°0מ/·25c)'¥ ý5ÞÑó+ÝÅ-"""b£¸å5ã‘¶áäžÓÛŠL­:4‡#×öùi¸£÷¬`¯c‘B#ÚÙ¹Lìš.íb×Dô‹O\<ÜÑëhœ#òl\ró1ILÜ1ê÷Ý_naQ«_89aó›âR"·E;;— öû """R`Å&w{lxJdê°äî¿Y÷Êω;Çü¾tÿ4®=²ŒÎOS˜þÙZîú2½H׎/6ó½-1—âR"·ûý)pb“"‰wD~=rmŸ_WHäÖ“ë¹ó ç_¶¶ŸÚȱ›ÿ—9*ØïˆˆˆHíì—ÒsËȵ}.¬:4›Û¿Ø¤2kÓ‰5–ÜóBÜòˆšÁ~ŸDDDD‚L;†ø IDAT*6¹KÓ8GÏÓ3vŒø}Û© T]Ysv½{9>5r\°ß+‘ ‰vö –Òã§%ûßçÖSëT>jݱ¥–Üã—`¿_""""Aïèùñü=.m9¹–ª¬+>5òRlr—¦Á~ÏDDDDò]lr·ÇÞÙ0à—ô“ª²¯ÑkûþãèÞ&Øï›ˆˆˆH¾‹wDî_qp&Ÿ'«ü¨Q Ž"""òW›qyãg+¹ùó5*?*6¹Ûe= \DDDþrb×t©?<5ê×MŸ­¢*ûZqh:ãS#û}ÉwqË#jÆ9z^ÚøÙ ª²¯Iéo]ŠKé10Øï›ˆˆˆHP KéþýªŒYÜðérÛjz¿Z,Ý:‚óY»ÏåðöåYùÉX®ÞÕ‹MBë³÷Ö¥¾ÛÉÍ'k¨ð7Žø(³ûÚ´—Ø`£Aãè° ¿:ñ&†5ïÂyÇì[k­É˜Ía)=~‰vö ö{&"""Ã’»ÇMNûÒúO—Ò®rlx†uC²÷–řݼŻ*Wâ}‹ærÝ®H6 ­Ï^[ûl#ie{V¯X‡ +Uà‰³þ×=5íE6ˆÒMÙ+mÑÿº¯J¼‘aÍ_æÜcö-‡«ÖžXÌÑëû]ÔÑFùK‹vv.—Òó›…MäÚOÛSGDZëu¥Ù8f"SÌn+çßÁJÕÛñC‹™º«'›„ÖcÔÖ…>ژñÏVeµgßâè—«1ìÞÁ\vÂè—’ö¯ kÂŽVe…;"9ï˜Ñ}Eâ kþç[̵cÙ¿C†Qª2[÷x“‹å~y&myëøÔž)Á~¯DDDD‚.:¹kÛ8GÏ_çì{—©'ÙPó™øV=†¶|‰³.bê‰Ùýd%^õÔP®<±ˆŽ=x]h=FmYà}üCCy­||õ\®^ó0k„ßÌè2õÄ"&§½Àámöï©Æ¿OœÊ”‹¸|¦g]ÈÅ[±\ÝöŒÞð>ç-{ž-+ÖâskçåjY–šÊ˜¤Æ&EDû})b“»4Méñí„´7~]‘1Ž òTIi/²Q¹ì™6—ŽCÑl_³2Û/I¤ãĦììÎëBë1rË<¯ã._x'«^û§d, #c8;Ô-ÏÛ¦¾Ï” ˜”ö<„_ϘæqÁ¤[X±ÚŒÝ7ŸËf¶aXóΜut>NlŲ5þƨ%¹êØ\.ßû—žŸëeYrh G­ëóklrm±kºÔö{%"""tÆiëã=¿MØ8àüì½c¸ü“÷™r|^ÎëÈ8öº>” ‡¼Ë¥ËÿÍêµî昃F¿äÝx]h=öLŸãeÜéþHyÅXªL)–*S’!KÝÙ—‹ŽÍãçslPñzÝoLã•”cµgßä¬imX¡ù L<2)‡'qT̽¼¡N)†”«ÅÛ{öæÔƒss·f%›Ãi;†]ŽKîùCLR·Ûƒý^‰ˆˆˆÑÎÎeb“»=6,¹Û§1IëÄÕG™||NjghÌr-žâkÏWcµNos¹Ù/iG¯ ­Ëžé³®oÿ ÞY¥ ˜•À9;ÆsÎŽñœ1ënV©ÐНí™ÍÕÎgØ bkFï7†_µé96«Pƒíû6e…æÏsæ‘Ù\¼îm&8§1éøl.sà“MByí›cr8ÿÞkÁþ±ÿ‰Mêö»Â£ˆˆˆ€hgßðØ”[†§F]˜ûa×›•«Z½=‚-CK³R¹«Ø~Éû–î]Ø8´6_LšÀ{'™5™K>IäÂé73¬æ¿8æ ¥­o±}µ2¼aÜ®ØÜ‰ *¶â]ý§sbÿº,ÍžãôÃ38e`m†6„ñ[§pþúþ|òºP62&×ËáYóöå0Gó:m-"""i±É]šÆ9zœž²ýíßV›É<Õ‘±ж8QµGÈì¾jûËl Õ(.–oÞSŽ•Ÿ~ƒKÜÚšÂáO†±LÛnœ¹ñi֯؊ohéàm>QDÓg9íðL®Ú÷:Ÿ½³ËD‰06|âEN:Çeñ¨é»â.ǧDî»%"""$ÑÎÎebSz|;sÏ®::C•M½³ñ•óºÛZDDDþ’†¥ô7>}ð+N£*ûZpp‡9ºÿì÷MDDD$ßÅ&G\Zth­õ‹v¯òvßìcö`ïï©«±ÝÑ9s†a†igXé¢ó1{ð‹ÔìõÖU‡f}.Šæ¶)_Înógì/ gË…³emžŸ¬Ìö‘%©S%8irí¬½ú_Lnµ~‘Ú¦HÕp{7]‹#Ã0ÌÅ ‹#]2¦Íÿh÷rÿh]õâôÏdÕ¡™òEÑìF³®p–,Íœ& âÆUùEk_gjrÚΔ•¹3Å/Jó‡)Jó›%?úG»çûEi×›"4SyJºs°ÝÑýüóÏ Ã0L;ÃâH—œ)\5Ú?Z»Ý©ýÅ/ZW='Öp&ÀìUmÿ^]•)­Ö©=13ÂíCÿhíŒY{õÕk üd]¡§ÍÊ\¥4FhÆØ{Sólwt?ýôÃ0 Óΰ8Òeãcvu2…«\LáªÑ3Ã5ŸXc WnxÝŸ_¤vãÜ8cÍš_Y[hêt Íù\LQšš™š±öÚžÔz¶;ºü‘a†igX©Sò1»:ùEkWûïÕ]™;CÖÌì4Yœ1EL‘Ú³¾Qê콨uX†a.N¶nÝ*!!!|8uNþÑÚ¦(MÍ¢ÔɲºÀÇ®Y™;Cæí7ž5EhNñÚÅ+‹mq<}ú4Ã0 Óüøã,Ôy™"UÃM‘šÊÙ±†š%YSeeþç—5+òfHPêÄÿ1Ejjü¢µë|Ì®NöÞ&Ô6¶Åñ‡~`†a:GêÜ|Ì®N¾QjS¤öôìX}Õ¢ŒI²"ú%ÍÒƒŸJ@¢g­)Rý‡_´v»)\åbïí@í£V«½CBBÌ[¶l‘ï¿ÿža†é@XéŠq¾@FiËü¢Ý«ç'φdL”e‡¦JhÞgÊòCSeIö HWëíþ‹)B}Î7R³„…Ñ1üðÃÞöÞÙ2 Ã8BXéŠd W¹øG«gøEkóý¢ÔÕ~{µUóÍ¿-Lö”àôñ²äà$Yrp’,=4E–Y²ôÐYš3Y–œ$‹2&H`²§,Hð¨ãþ«ŸòÆ“¾‘êPŸH·Qö^?º¸N:å}êÔ)a†a:Gº²Yõ3Ú®öòÒ,3EiLQš“¦(õ¾ê?|ÃÕÿë¡–™»ÝjgîQŸ3EiN*…S»Þá6Õ¡ßtl§Nòþî»ï„a†éXXÉÑy›ð_ø.ˆÅ‘aæâ„Å‘‹#áäÉ“Þ'Ož†a¦ÃáߣäÐX 'OžôþöÛo…a†i6mÚ$‹-âÀÉ¡±8Nœ8á}âÄ a†aÚþä u,ŽÄâÈ0 sÂâH]‹c§V«½ƒ‚‚Ì_~ù¥|óÍ7 Ã0L;ÃâH]‹cg»£ûú믆a˜v†Å‘ºÇ.ÎvGwüøq†a¦aq¤®€Å±‹cqd†¹8aq¤®€Å±‹³ÝÑ}õÕWL'MºÿýÊ£½Gʬ”#MNW²ñoâ ÈuïïÒN°Ü=Gdê]ôz^¾(nï8G;Íö,Xþ„‡ægÉ1{o[¦Ãaq¤®€Å±‹³ÝÑý÷¿ÿe:iÎ@œžž+©•OW¼ÁZt¶KI;çUºó¹›\÷HY'X÷z©4˧–⸮¨½ã¹¬Û³¹ä/{\)Žó2娽·-Óá°8RWÀâØÅÙîèŽ;ÆtÒ¤ùÝoóß©³<» M*™®ÈRt®}›·s^%;þ©Çÿì‘ÒN°îõRQW×¶wœÊ˺=›KÞR¥8>87CŽØ{Û2Ά $00'‡ÆâØÅ±8^±Áo¼!.€ Ïh L¯¼`:ÇÖ¤ò²nÏæÂâèxùꫯø÷(94Ç.ζ8=z”é¤I5)EçîÏ¢e‡þ ×¼,éõ§+\o):ïm“"Û1ÊRä‹©ïËs»Èu=!èqÜ|ÿKòÉì’cãH¶>Ro @7yrÙ^ñ{Ü,cw—ÔY™&sG(Óõ"ÔͯhË;r- xÐ_R-ãWäî–¹ŸüMþ|×ÍÒ^×¹ÈCϾ+SÖ¥Hi½õ-–à™’#»¦‘»®†t{À_ÒJãeÊ]ôzNÖÔ}¦"{­üçÐWžó‘â#ÍmÏŠoÏÖ¯ËQ9zôˆÆ‹û«Ë]7^%äêÛ—÷gn—økqL—ʶ~_L§Ì±cÇø÷(94Ç.N­V{/\¸Ð¼~ýz9räÓI“bºÏRtÌR^¸K4C•’ôòâ 9l3]Á/YŠÎV)´¾^¼O¦ê#¤ç­#ä¹WÿŸ¼úü#r{oå¿ûë^Z IåGäÈ‘b‰_1K¦2Bzâüg7™>s¶¬N*”ŸÞ%䎉û¥Ü:î¡ÕòWg˾£ïë²±Àº¥¥»Ý²¼ñR~äˆÎ\!ïÞj™öÆ{åé—_•ç,΀}ä)ïX)9¿E²ýƒüIÜܕހ ÇÕrëßIFé™r§¥8æ+ÓÎZ+ÝúÉó¾1R\ÙÒö<Ü¡íÙ¶u©”‚¹¯‡2}¯›ï“Q/<##+ßÇwöUŠãœ4©hó÷ÅtÒðïQrh,Ž„#GŽxw‚-ÓLê#•R¸]%·‚þ¯Ê²ÌÃç§»°è”Iì¤á\%ž´Kò*êÆ¬È—ÏžtÀIž É:_\жÿC9UýÑ.)¶¼Vº× ƒÁó%í°òZñŽË€ôèÛM€AâQ¢Œ}8Q¼ï…CÅ3¦LŽTæÊÚ×û ¹åí`I(±.Ãa9¸É ö„ Ç#â—d]kq„·Èë³#$Ç2Ï#åõ‹c]i /øÅž_Þæs¸ýÛ³­ëR%ÐSÐm,ëzTäÉ®OGI˾·®8¶þû²÷¿“L“áߣäÐX GŽñ®¬¬¦ó&Ù×Zt⥬²R*+ò$ÌõV ^_!Y‡•éò×YŠÎ»aRXY)•Å»åã[ ä*Ò$-­~¾ø·Ü ˆÓ_×Hže^…Û”âxãG;¥Èº E;å?7AÐûYYv°B*+Ëdÿ¤;p‘O&?-N€Ü3Í,å••R‘"£z@0H%{Š+¥âàry®7}_•Õ9 Ö­D¢ŒC€üiZ¼”WVJeeáùâxÝ»_J^…Íôeq2ÙRWXc)ÎòôÌ)nõö,o÷ölëºír•›ÁÝ“%¶´Ár”'ŠïCÝ”â8;M·ñû²÷¿“Lãaq$GÇâH¨¨¨ð®¨¨¦ó&ÉZt¦RËk‡s¿”Ý®“7WfIyE…ä/:[¤ ¢BÊ’MrýÿÆσs$­\·`«µ8îÂóË/›Þ @yóË|©(O“î&¸þ²yÿH‘’q¸BòÖ¿&}¹þaRPQ!E{Tr+ =F†Höá ×­`Ûrƒµ¼VTHEElûç@®’çWæÖŸ¾t¿Rq½ÜssÝò»èvKA«·gY»·gÛÖ¥\Òæ<(d¨G´”\°¥²Ê]–â˜*åmü¾ìýï$Ódø÷(94GBEE…÷áÇ…é¼Iœi-:qRrþõr9¸þr# øŽ¬É.“Ck_<_tò–âh †Œ•yËC%4´ñ¬Úb–‚reÜü°÷•âøïR`³ 9«þ&}¹Uµ[ s×Ê+WC®zv™dÇÊÄ; ¸fŒ|q¨@v}|‹ýäï_RÆÛü¶ äšW×K^#ëV¼o¼ IÖáÃrøp¾lýç@®“w·äן¾$V&ÝiÙ_u»KþmòGzB€á2!º¨•Û³´ÝÛ³mëR*qSïò€²”]0}¹d,|DyVŠ”µñû²÷¿“Lãaq$GÇâH(//÷.//¦ó&ÁG):w}º_Šmß+Í’5ï(§uo|˜W[ŠÎ;›%¯¼\JâgÈp@0ÌKöµn^y[¬Åq»äÛ¼^–±HžîÁ]S$"l¬ÜÈý¾ RRž/;>ºI€[dlX„r׳óhYž]&åååR°K%ƒé1r‘d–]8¿ümʵ’½Ÿ[)9ååR^ž'aÿ(Àõòþ–¼úÓ[‹ã ù`uº””Jô¤û¤ =FL—ØV­cI»·gÛÖ¥TRç2Ä)E,G±ÄZŽ8>0+YJÛù}1.ü{”‹#±8^i²è”—KiæJyó:åq9¯kGŠ“MÑ)ÏÛ,ï^î–É1Å Æ-–ØOï•n€Ü÷y¼”X^oª8–—&ËœGº º=$Õ] ]D¡”——KîÚ1r wªŒòpwHÏQA’n)VeYKä/–ëWYÊd] %Òr]à]“c-ëÖŠâØëYY™cy-·èï„WɨYæóëÑtš)Ž-l϶®KÁn¥h⮉²¯a,Ž•©÷¡^qlË÷eï'™ ³nÝ:Y°`NÅ‘P^^î]VV&LçÙç^¥L‰•¢ Þ/‘´å—6ÿ-_ûÎ&9TV&eey²ãå¦Þ#ÆËÖìËgJåPÄtyÒ ‚ž‰É\|~¼¼-ïÉõ€ ü LòêͧXÌP–£wú¿!r•÷JÓedº÷ ›Œ˜•$%ÖÏ•fÉŠWû)w"¿ ûóëÆKûB+÷÷„ ûÃâ}Àº yöë¸^ÞÛ’W]‹bd¢¥8®8Xv~]rÃÆÊm€àêdarq Û³¸ýÛ³­ëR)w(wUß§^#©Å–éKÊÎ)OZáyÀߺ½Úþ}1'üÉAê X ¥¥¥Þ¥¥¥ÂtÞ˜½­E'F ›¦8E¿Ò¯®è¼ý¥äZÞ+ÉÞ,Úá=”÷z\/ùWyñ©{¤?”g¾07¾Þ˜y;?–[AßGäÿ}¤ßȼóïD{)×ðÒsT°¤•X>W'Óï±îG†Ëô¸¢zËW”¼XÞ°ÞÌrý=2ò…—ä™o•«®–Q3¢%ÿüô‡d‹µ8n>T= ÷)×SözFB³m^/É’µïÝ ¤ÿkË$­¸¹íYÔ¡íÙ¶u)‘œ­úóÏqìyó½òäègä‘¡×ôä£Òü¥¸ßÓyÂâH]‹c§V«½,X`^»v­ÝwºLÓi±è”–JQr°ü­ï…E§´´TJr¢$H÷šŒ¼çVéÛ§2ì±wdÚÆä ÇËÿ×ï”k Ü½üòêƒuïåí7Ëïï™'Eç?—/{´ÊC¿qÇx‰.ldùÒ¶ÈÌ^Gî¸Qœéyí`¹ÿé·eâJ³MÑ*•vÇÒR)N[.Ê»k³¤¤ÉíÙŠâØÂölýº”Jii±ŒX n/ÿYî¼ÑIˆÓÍË›Ÿm’„Z¹½Aqló÷Åtš°8RWÀâØÅÙîèJJJ†a˜v†Å‘º‚†ÅÑlyé"yôÑGåµ×^µZ-ÅÅÅ Ã0L;ÃâH]7ZzÐ,ãðyôÑGY†a:Gê ¼Ñ Š Ó9ŠcQQÃ0 Óΰ8RWàNP\˜ÎQ †a˜v†Å‘º‚çÐ ®³cìÛk †a˜vfÕªU2þ|>œˆ—íÿCÎÏÏg†a:–F"r\,Ž Ã05,ŽDä¸Ôjµ÷¼yóÌ+W®”¼¼<†a¦aq$"‡wèÐ!ïC‡ Ã0 Óá°8‘c;ÄâÈ0 s±ÂâHDŽíСCÞ¹¹¹Â0 Ãt,,ŽDäðrrr¼srr„a†épX‰È±åääxð4Ët òD/gy~IR‡ç—¾C/C{“q»2Z÷™´­­[F†¹ •9sæðàD䨲³³½³³³…aZŠÛí§'&É’U«dÕª•²bI€|>öIéȚ͒š-Ùæ…òD/gynqb‡ç—¶ÝRw¦·î3©a­[F†¹áOQ—í••% ÓRÜn‡ô*‰¶¯gî—¹#{ nw“-©Y’o)Ž! ž_ê6 í9Lq`Ћ2nþ Y³ÔW\¹Zà’õIÏ/)Ì]†Ò«{7¹ª¤û=ÙÒÄ´ééé’žôe+–1YvN¸Kºõ{E–ÅÛ|65\>qz&H¤§Kzzªì |UrÍèY•jÿíÏtî°8‘ÃcqdÚ’Onƒô}e™Ä7x=qýG2Îò|ðzÅ15f±|xWoéyÓÃò·{ŠoÐZÙò¾ÜÔãn™´;^Öt‹t»ÕU66[»ÉÿZ,»Wý[nC/1e‡$7µŒ–âØü2Æ6Q#dÚ½Þ±ÇdÙ9õ1¹ Üá&ì¿ý™ÎG"rx¶;º4Ë5a ÓT”R¶ôüõƒÖ$¬ÿHÁIž Š“´ØùJq Š“˜9–ÝŸ¨ÔóÓXöº è~·LÜ•,ѳ—^Ýï—É;“ëÞ_þ¶ ¾þ1™¶;E·heHO1lM’´ÔXYòÖ ç§dfxJã˘øe«–ñÀ’W¤‰ëú„óï§DÏ–gú@\t[$)-M’¶O’GœûÉ_¼ÆË3}{Èê ’Ð ¾¦ó†Å‘ˆžíŽ.55•ašÍÇ·Aœ/A˖ɲeËdÙ²%äï)¯¹t\ÿºǤHj̲'9Yöø<%W÷¼[´Ívÿ˜Î›¥K—ʬY³øp"r\¶Å1%%…ašÍÇÞxÒGnyè-™±q¿$§¤HÊ>¥8>+)I‘üñH¹Õ‚î}å¶oËôÐ@ùðÞk}ž”YQÉbÞ ?5L®ëA¯kåÎÑî²("IRRRļI£Ç- –eH’Ý>O‰3Ê!û”ùÙÆ¼¡u˘’"IQËÄð×{ä¦>ôì/·?ú®ølŠ“ä³lö¸_z÷yB¦íLTÆMÜ)Óžp–ž÷èd£ÙþßÓy“ššÊÒHDދőaæâ…Å‘ˆšZ­öž5k–yÉ’%­u»Ø©IDAT’œœÌ0 Ãt,,ŽD䨒““½“’’„a†éXX‰Èá%&&z'&& Ã0 Óá°8‘cKdqd†¹Xaq$"Ç–˜˜è Ã0LÇÂâHD/11ÑÛl6 Ã0 Ó±°8‘Ã3›ÍÞñññÂ0 ô?‹-???>œˆ‹#Ã0LÇß$¢.áÀÞ†a¦ýaq$"‡§V«½M&“988Xâââ†a˜v†Å‘ˆžíŽÎÞ;]†a˜+9,ŽDäðlwtû÷ïg†aÚG"rx¶;ºØØX†a¦aq$"‡g»£‹‰‰a†aÚG"rx,Ž Ã0',ŽDäðlwtûöíc†aÚ™ÀÀ@ñõõåÀ‰ÈqÙǽ{÷2 Ã0 K#9.ÛâÍ0 Ãt ,ŽDäÐÔjµ·¯¯¯yáÂ…vßá2 Ã\éaq$"‡å% Ã0L‡ÃâHDŽ-**Ê;22R†a˜Ž…Å‘ˆ^cÅ1  Þ£%K@@@‹;QŽÃq8ÇéJã°8:(S„fŒ)B»`Î^rÿh]•)RSë©«š³×xtÖ^}°)\5ÚÞËHt¹DDD<ámsK;JsÃÏqŽÃq8Nç9{ïÓé"2EhÆøGëŽÏÝçYµ=wÄW앬ÿ¦J’õßT‰¯Ø+ÛrV×ÄL¨š­?aŠÔ¼eïe&²µZíÝÒŽRÝŠg•qŽÃq8Ç¡+’¤>tî^ÏsE›%÷DV³9øMºDm–¹û<«ü£´Kì½ìDD]˜7± ÿ‚&¢KË?J¿=äÀŒ³iÇ%û›´V'íx¢Çvnv´!ÚÞë@DÔE±8Ñå3k¯>8hÿ”³É_ÅIÆ×‰mNÚñx <ðéÿ(ý<{¯ QÄâHD—‡)\å2+Zw6ñ¿{%õx\»c>)þQús¦HÕp{¯QÃâHD—‡¤ûž YÁµIÿÝ'Í—Y!µ³÷vÚ{ˆˆºG"ºfÏ6³˜ @³Þ `5€›œ0¸™Åtð€3úÛ¼îe'9¹Áô/ÈгÁëm¾¡þ~p] ÓµÇËt»c‘ccq$¢KÏ®½0n♸£;åbeAìøjS„fŒÍlþ¥±y­€ïü­+Žø ÀP –•µÖfózKűµÓ7ÄâHD‹#]z3#ÔúåÉ>51G¶ÊÅÊòdŸš™j½Ílz(ànóÚóN¸­+ŽK, °uåÊÀϾ°uů¹âØšé_Peœ`Y¶ÞR-¯}¥Ä¾b™þSÿ` ”#«9>²,çû¾ð¢-ë C9;@€7QWpʲˆˆZÂâHD—ž)Bí½*}–DW~yѲ"ÕTkŠp›Ú`V²QWÌVµüï–ŠãÕPNS?`€Ó®µ¼çå  ”"ú¶åõæŠcKÓ²Ìã=(E0JÁë…úGCB)zû¡ì¬Ÿ0Àòù; mýÀßÜàK‘ºC)ŽØ¥(¾¥8º@)šï5±=ˆˆbq$¢KÏ7ÒíãÄϪï“‹•ÄϪq€¡~µüójÇŒ²¼×Rq|@ €«,©ðºå=kìà-ßB)u-Çæ¦×ˆCÝQÍ–Ï Býâø€\ËkåP®ÙüÀã–?;˜`§ÍXC,ÛáF(Åñ'Ôö~Ù²žGìOYQë±8Ñ¥gŠÐŒ™ãùë®òUr±2¿×™F~Ǻ€tz(Åê+},ïµT×CÙž³D µë†úE°'€}ƒ–‹cSÓ϶Ìã'›TC9zh[oð€'¡È·à`­e~Ë,ãYõ…rŠûN(Åñ0”‚ (ÅQ¬„R.m¯Á$"j‹#]z>fW'¿H÷ê°’%²½lY‡³¥h‘˜"55>fW§Ff7ÀA‹,µy½¹âØJ9{À-–¼åîê¨_åôð(wr·T›šÞåè¡UO(§È{ ~qì åô{0”k&C9’š åÔ4L‡rÄÑj0”Rx”âh=2 (Å1Ê)ñ0Kº7²Mˆˆbq$¢ËÃ/R¹,uFmXé"éh–¤L«õß« kbV·@9r÷+€‘6¯[‹ãƒP®]´¦7”2u€³Íô} \Ÿø..,‚0ÊŽ3-ÇÆ¦åæ—7Üåˆaê®qüÀm–ÏZN~¥rTtåý{ \ã8Æ2Ö&(7Èô@ãÅÑzsÌ0UžQËX‰èò0Eª†›"4¿¯ÏŸ+›J¶; æ‰_”ö¬)\åÒĬºC¹‰ä[Ô¦”â(ä(Gë–52Ö*ËXwàÂ"ØJ!;„ÖÇÆ¦ÿ;€c–åH‡rjP®±Ü¥ÔÝåúKp¯åý0EPJ& ”À Üì"PNÛÞUÝTq€9²lÆ""j ‹#]>þÑÚscÕë çȆ¢¹mÎúÂ92'ÆpnV´ûL{¯ QÄâHD—ÙÕÉ?R»mî~cͪC3e]¡«³¦ÀOæî÷¨6Ejš:EMDD—‹#]^>fW§™šY~QîÕ‹3¦Èꟳ(m²øEiÏúFªC[ž]$ÏA)‡Ö˜ÁâHDöàé6Ê©©ôÖT$ޓř“dyîT ÍûL–çN•ÅY“ea¢g­´¦Ú©9Úà版èÒkx„±aX‰èòñ1»:™ÂU£ý¢´+ý¢4å¦HM•)B]kŠÔTùEkÊý¢´Á,ŒDDvÃâHDDDD­ÂâHDDDD­Ò°8šQÿšÇçì¶dDDDDÔ©ð.j""""jG""""jG""""jG""""jG""""jG""""jG"ê\ô‰cŒA‰ &,N.·(±Êd®œX5aqòQ¯Å‰Á†À£Û0ܨ¿“; À@÷6.ÖÊ8µñsVý-Ër]ƒ×],Ë5¹Áë/Èгã^ /HЭÓö°Êv""ÇÅâHDƒ> qŒç¢Äã“—¥Tmˆ;*òNKVe•äÿM²*«ä@ÞiY¿ÿhíg¡iUž‹’NßjŰ'üÀµn0@-€GÛ¸x¼¥ µGKűÀ0›×¯”âØ À3–CYG"ÇÆâHDöçœ:iiê¹)'$ÿ«ß›MαÙ‘rB&/M©”¸¤…¡OxÜæÏW¨€R  `.”£‰}¼àÿ ÀÍ–Ï5<âø€r(;΃-¯wð/'ü` ”Ò™j™ö(ÖÊÀϾ°uE±aqll~½mÆýÀ^±Lÿ)€ÿ0JÙÍð‘eùšZdžÛãMÔLJœð<êë`šeùkÀâHäèX‰È¾¼B’¶û®;x6­¼Z­iuÒÊ«eæºìs^!ÉÑÍ ß°8>à(èaËÿÞ¥Ýà‡Rî¾ å´¶mqå”÷{PŠY(”ÂÕ À}PŽ>àOP Û§hþˆã–žð¶åuÛâØÜülÇ ¥è퇲SJq= àË25µŽ ·Ç+PŠ£ ”¢ù^3Û¹€¯ÀâHäèX‰È~¼%O_•y6©¸JÒŸksRʪåó5Ùg¼%Íkb§Q''P V(Eé'Ô•¹iv¢îÔì¿B)X¶ÅQ ÎfºPÊÛ ó¬µ™ÿ}þ†–‹c?oøÖ2mqln~¶ã¾ ×òZ9€­PJëã–?;µ°Ž ·ÇËJ° ÍŸ²fq$êX‰È> ó]À¬÷ÿ² fwÖÏr±âÿeA>À¬¿D‹ÜÑ€uznnnÿÐëõ¿GDDÈ™3g.z222döìÙgFãIFÓšgñ†ÀDïÛJe{úO-sÊkõ S/Ñ"¿¥8^u‰Æ'"²+F3ÖÃ㦼¼\~þùçKšŒŒ ?~ü9£Ñ­R©\.‹§§ç•J5Z­VëÕjµ·N§[àáá±^§Ó-Ðh4SÕjµž×Ou!ÆÀÄg®;T½9ù´\¬Ì\w¨úq|Êü/U)%"²+F3ÆÃ㦬¬L~úé§Ë’ï¿ÿ^¶lÙRëîî~N£ÑŒQ©T£u:]ˆÁ`ø^£ÑÔNŸ>ýÌŠ+j¶nÝ*;w¨(Ù¹s§lܸ±vÅŠ53gÎ}Ù“™™)&L87qâÄêíÛ·Kaaa«?ûÝwßINNŽlÞ¼¹vâĉռ†’ÈA¹®5;y&T¯ŒýVÖ8Õá„îûFŒA 5®kͼ‘ˆ¨ <<<ÖoÚ´é·~øA®äœÜ Þ2Fëƒq»åÁÛåçïEÓšßÖ(¥jl†ò`o[j(?ƒØšõzÊÞý›™9µZ}‹Á`8{üøqùþûï&±±±2a„3^^^ë콉è"Ñ&÷Lü=h÷qY}²Ý ?.Æà¤³†ù‰\` åñ4Ë¡%Ûâu?”m¿ åq4QP~·¹”GäTø”kÏP À¹ÁØÝš¿”Ÿë›åŠó Äž–éª,ó¾@”Gï4|ÌMsãÛºÊãô†H…òp«P þP+ÖËɲ,µ`q$r®®®N–M¼ŒFc˜§§g¹Á`øQ¯×W©Õj ýý»ï¾GË7ß|#þþþg CŠ««+ÏF9ग¤W/ ÿF‚"N´9 ÿ‘ñ‹SÏ J™ÙÄ,¦Aù•Ô/^¾¨ÿ *÷C)_}¡üT_”Â(%«±ŸÜëÑÌøO¨@Ýã{®²Ì£;”Ÿ üÀSPŽrš$ î™­ßÖ‹Pôm׳PޤZ á­P~ͦ5ë5Êïbÿ G¢+šF£yËÃÃc¯»»ûoS¦LùeõêÕ¿ïÙ³G233¥¤¤D>,'Ožtè|ýõײpáƒÁbï.åZǤm—¦×ÌÙvLìþ¦Õ™·ó¸L\šYm LhêµUc§ž‹ú?ãw€ß ü__Ô=P~æM=ö¡±ñÝ¡<¨û ߈„ò 0VÓ E(¥°±£¥ÍoËÊoR[Ýåˆæ@ËŸÿ à5´¼^÷@9ý(”£±,ŽDW µZý†Ñh<9a„ê}ûöɱcÇäÛo¿í²9~ü¸Ìœ9³Úh4γ÷wCDëZ³“!0~–GPRõŒ/Jdööã-fÆEâœtÖ#0!´³h¬x‚òÓ{OCùiÀ]PJœíÑ·îÞƒRÂTmÿsËx \¿¸ Ê)àÞPŠÙÏžp €@(%³wÆ·õ9€6¾Þ²Ì·YÖa#êJdSëÕ@”Ÿ5 G¢+ާ§çƒÁ4qâijqqqrâÄ Æ’¢¢"1 gÕjõ-öþžˆè"Ñe J¨ôNªž¼2W¦¯/ïMGdfØÅ{Ó™¾¡\&¯'¢NÂ`0mÙ²¥æ«¯¾¦ùÆ•J5¼å­JD]YcÅ«”ëÏB9-; uG¡îÆÛXoœ©†ò(æÆ”»¦K-Ÿ5£îæ˜nPnXùÚò^”†6Œßpº'”C9b¸ÊÕþPn²ÞEÝÜzÙâ©j¢+„F£ykÚ´i¿;vL˜–3iÒ¤_´Zí{oDDDD—‡‡Ç¡¸¸89zô(ÓŠ°8Q—¥V«kËËËåÈ‘#L+¢Õjkù0p"""êrT*•˸qãª+**„i9qqq2nܸ|{oDDDD—åw¦k>,LË™?~^¯ŸdïˆÈ.t:ÝÉÉÉR^^~Ñâéâ,#—l~ºƒËå©þÉ¢¬‹7ßK9¿´´4Ñëõ¿zzz6õ `DDDDŽÍÝÝÝ?  ¦¬¬L.V<]œeäÒìæ§Ë^.£ú?&Á™o¾—j~EEE2eÊ”s<ÚHDDD]š«««“Ñhü.<<\JJJ.Jƹ8ËK2%o·Fî¾_/óôÏȾݤÇuËØ)R˜)GôÒû¦Ñ”’/ÉkôòÌÐ¾Ò ½ä¦Çþ#‹äKII‰Úù‰Üù€Qfò¨ÜäòÙÖĘ%%RRRØø8̯¨Më³páÂ? C”½¿+""""»sss¥×ë«·nÝ*ÅÅÅއ‹³<±8CíÒÈàž×ÊŸ$)Ë,ët÷HŸ»'ÉÞüb)ÎX*£úÿYÓŠ¥À]Vî4KF3c67ŽíüÚ².qqq¢V«E­Vëíý=u *•j¸^¯?5þüꤤ$)**jw<†:Ë!’»K-C¼ Ë2”×sw¸‰ËPWÙ–S$EKäÉþ–…©÷ùýÒod€$*Ó&/”‘ýS|älÿXnë÷¬„¤*c49fóãÔͯíë³ÿ~™¾Wú÷’=ó$3ÌU†ibŒnnÛùµu}&<<\t:]O[Q—f¹Öñ›Å‹ÿvèÐ!iOôCåñ dÉܪ’aC?’Í™Êëõþœ¹K&±F¥RnxÝŸ^¯ß8mÚ´š˜˜‰‹‹ëtÙ¾}»èõúF3Ö^Û“ˆˆˆˆ ”L½^¿ÚËËëì®]»Î_CزzõjÑétgÕjõöÞNDDDDd¡ÓéfèõúšÐÐP‰‰‰±köìÙ#ÞÞÞgu:Ý)^»HDDDÔ ©Tªáîî}öYͦM›dß¾}—5QQQ²dÉ’ÿÑét5z½~«««“½· 5ÁrêÚC¯×Ÿž:ujÕªU«$::ú’&,,LæÍ›W«Õjÿ0 ÛU*•‹½·µ’M,3Õ³fÍ:»nÝ:Ù³gDEEu(ááá²iÓ& ¨õôôüE«ÕžÓjµKX‰ˆˆˆ®p*•ÊE«ÕÎÐëõù:®ÚÓÓ³jΜ9¿ËÊ•+eóæÍ²uëVÙ½{·ìÙ³GöìÙ#»wï–íÛ·ËæÍ›eÍš5,óæÍ«_¯×¯wss›ªÑhÆð§‰ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ¨3øÿãDÈw]›«IEND®B`‚neutron-12.1.1/doc/source/admin/figures/bgp-dynamic-routing-overview.graffle0000664000175000017500000000731313553660046027200 0ustar zuulzuul00000000000000‹í]YSÜH¶~¶…®_îÀÈ}ñО`ë¶»±a(lO;ˆ¸!ª’Bc!UK*0Ýáÿ~Oª-¥‚ªf7Yް„trÕ9ß—'O*µþ¯og‘wnÒ,LâŸ^a½òLÜMzaÜÿéÕÇßWÕ«½y¹þ?Û{[‡¿ïïxƒ(Ìroÿãæî»-ïÕêÚÚÆ`™µµíÃmo÷]çЃ<ÖÖv>¼ò^æùàõÚÚÅÅ…X)¿›œYÁlm?M&Í/w!³UHà÷òÞ+(f”{­:pµvó7/_¬5—o6ºyxnvƒK“¾‹{æÛúš½ 7Ã87}“¾Aëk“ÓI(;ì9dùi”ó4Q¦=y±žå)4ù ÔÐOÎâ°Ÿ&ÿg¿¤ÁÉIdÄúÚX¤"…ö±„êA©@‘õµIÖ£* ód£÷ßa–O‹ÎÓ¡Y›Üß º_m‘qŠœ†Ý©Ø¸íc1+‘MîM«ñ×_hÅCßW¼¿8×>‘œ ‰8“Lq¶âIE|¥´„KH*Lø÷ïµ¶ØÌ¶¢ ›Í·“DaY¡F’wÛ¥ü¤ÃIµïGbü22¥ä¤9£›eòµ¼[¹=º¿eµÊŠ}Ø«V:{ÚK“³òdÔ½™ù’$g (ËVŸÙ^öÃRM&ý\tsõ))’hxoDa?žÉÏæ_HwA²˜Ê§&ˆÞPбâd" X¥ÝrÓ¬ A˜­b¼Š‘‡åkJ^3îýÁo¦v6“$m¦ä¹÷<ôà"Hƒf¢í0DÁe§D3%ƒMÂÏëžy?yÓóf?‡‘9¼Ì$Àš²…‚m'Ýᙉó¦N;’ÌôäX13‹!-]×´†åTL‡ _h¢1Eœ3¬5!+ÖÊg„cÍ…¦Z"Ä ãj‘$ʧL0.8ÓZàºuµ™Wi_§ÁÀ´ظ“8Ÿ$ó픩|´õû#ãÊÍi±ÈW‚¡ MäûíòšKÍX[‚tN¥m$(­´Ò¾ãÞ˜¸ÄÙjÇœ…ÇIÔkdS Jø§©&-ÌFNͧVX­J¥‚Ök$ZpK‹4n‘}ôc“WíT_̇zh…É™}FÑUϹ©5‰U÷ò¥JÀˆbÊ0¦•>lUŠiJ¥„*PÐ{`š–”ikJ)(SˆJ‰µ¬¥¬©ISe²Ó —\\Õ›&À;mSËßw:×hd“n±£U 4ýá…;š!‰[®LÙÞÑ”0*¸.s;zÒ¸46éAÐ ‡Y­E*UM]µ¯†©šoù|M®ß-mä(ÍOð˜}Xü×ôa¤BŽºI7 ì-†Øèlx 3‰^þut’GG'èè$áØ= ÒÌäÈ›‘~ù®}zàŸG©éÎú©1±=9ކŽÅ EG×1i¯cL õÑ H{Gù7.ü׊ƒPö@+ Ùƒ Úƒ.D+D8*D µ=\ˆ "6ß0ƒ|˜ÑÑ]{!û§¹‰O‚.p7zùÒ¶ðøè$ÃÂ;êžoßÛm^Çd–.¿_7úÛt¸¿â)î#ª©Ð\QL´jeC*>m6´mrlèØÐ±¡cCdžbJ}BÀª94P¡ ’£~•䨲t(©Z)•ÐÐ Æ5b :Ä+åèðéÐ!'†­*ÌjÈV” {³šñPäiõ†k…¸Càó…1½Ð DH RÖˆ'ÏB•°"E#ªn“<›³ó¦s:æ|jÌÉÆÌ™&çaϤG/?˜ü"I¿f·HžLùØ`D ì'4ųä)ä%Ï‹6`ÀÔˆevGjŽÔ©9RûQImç[n It'¤V a) *L| F(½"Lâ':Aú4IMHá&.‹‰Ëé/9qY¦¤ËM\âyÐë&.S9¦ºfâò Uyÿ¹c~œúH"¬Ã´ TrÇO÷ÊOn™‰ã'ÇOŽŸž(?Ñ»æ'¬} !Œ%Â*¬ˆã§{]É?9~rüäøéiò¹c~j®TÔñÓ=òUÄñ“ã'ÇOŽŸž&?á[ä'ЍOídS@4ZP9bÊ-ª¸×ù=íU,»¨¢‚±hÉE¬LI–\TQ¦dnQ…£2Ge /ªè˜èd53éyØ5Ó…·¹jc¶fäJaÇl÷ÊlÒ1›c6ÇlŽÙž³Ñ»f¶FÌ‹;Ÿí~™9fsÌæ˜Í1Ûsc6rÇÌÖ-sÌv_Ñ2æ|6÷Š—c6ÇlÏŽÙ–³Íá–Ý067`–9ðÖDç&»Á¢ØÉàÿÖ½ùU¨RѪ±p­5óÉa!ÇJÝ4¶ŸÀ¥ë؇áØF„`h–´Ã‰Æ5¡¿7zz’œ sɤ¢Ô®õ¡³‰¹¼ÙR§ Ó†r\Áa·L I)ÂB )år0-€ ‘RÊ>k`ÚjþFš&­ã3Ô¶Í®éÝËšøIeÅþË¥}uÏÚ«LaæA-W“Ïa¯±ê¥h*Y”[ Àå^ÜŠÑ;¹ U¢pô cHSŽ)SJ²%@´™Xc¢D ¥DoDÙ݃¨|0mn¸ †6Ò á0Ôaè¡3ï™9XltÑÌäí]À¢x(XÔÚ'˜p-Z1%è¸ØH¬¥sÐ.þ¸8ó~“[ÞllÉÉ݃èM·ôØKóÓ¤ŸÄA´¤öó`gA^~ö«®ÿ³ n Èb‰|$•´A)`©ì'Š8ñíxRP¤­ Þdmäž…yå+L“'µZ ½Ü×\mk“´ô•RSÎæö…ÇŽ Ú?Cç&kÜ-§ÃDû ÀO%㨠»Â‹O9;.p\ðœ¸ÀÍ9?9.ÀO‡ ö¥ENE@•ø1pÁ¢3çŽ <#*hN;*xüT€ž `Í}®ç1Œµ’äPÁâÑÇŽ žÜy¸àŠ×}ˆô™H QûE'¬°¯«æ¯û0`Joñuž0o¬«Ÿ³[ØH:ª>‰èÉ r¹5»ú¥žßC½éJ¿¾6sÔzPDü;y?—75zæ$Ž%Aã—6ì“K×¼`AÉè‹£ÿUüû•êüLuÖÆê½ÿ7 †`æc ®S€6ÀàÄAÌM!†;ˆqóÜ æ¹y´‚Þ¹G+Ø£ôhñ„bRcpuf#Ê`XR‚Ás¡Œ!qÏmã-x)ô¬GKð9"Σuí1»ÉØCy´û6Ì0Á´’`˜rÅknìNŠÏ‘&‰A ¬†¦ +ž >¤%È:ÃôCK·ÅÍ·fbÏþsY?ÜfKn»w·+Å¿Ýûæ/ûÞFßÄùÕníìý(èš3SÏNp¬7ú;ß0QÒG ŠEsŽp ©‰âRûÔnË·¸&vý• _r»CU0Ú„{ŽÔn¾+UŽÔ©9Rs¤öÄHm—> N;jhIp¦qhÄý¥¾ÎQãÿ4qœÖä4µ¼£Æ§¹]q¡9B{J„¶Ÿ&çaϤã¯s=´¯†)Ðg@T„)è¾â1_qE„dˆ¦4Á¯a ÎÓÏ$à}+´VÚ‚Q„‹‡»x8dô`að2n§Œ¶/ãà,ìxÍ=Z&¯ŒŒwDÝ;·»¡š Q¿­ˆ:Aâî׈óûØ‹eMVü"Á9*ÞþÁ>)ÂɈ.‰fKÅ¢««¥Û¤ÊÅ¢ŒMª˜Û¤ê1¼<Ã?0º×N0:`¬c©ÝEF¿¤a¯Š Ó‚¦w;ƒ  M›–7n†¯Êï ­EÙ¬ñ´á“´5ù´Ÿ~ùèk$ÉEfS•âíþîH:Ùa²€|Ùú¢Uðg²Ý¤ûÕL¶ZdEúS˜…Ç% Ì)¦ÐÙý o¦ÖÞ®V£gx‚[É®¦ ˆÿfÌà0étƒJ-¦ª]boYòÅTh~ƒ1’üœµ|¦(Æ#³âûiëâÏÖ©ŠNvM¶X5>Ïtm¶s™ÿvš‹ksö µu¾EoÄáY›{iø—ön7L»Éû0Þ³¼aýX5̽íðƒÚÝ ð£†|TTs'îÏÔ¥—ä3•±aÜÄâö®‰û\@&sôÚîI@kX¤_¢‚ùE2’¯c Tñë<°˜Å–qpe ù À¢sj*™‘OAà}Ò O`èik½]yÒ“Laì$V‘ZÅÄÃâ5å¯óþa{n¦¬"£ 4o¿òüÔ\x¿Á€:¸€þi&üäfñ&í¥¡‰óZ'·¯—HƒÊ,œ¹…âMxN_³d­9Ï7œÍ$Ï“³÷A 5(õ¡2Vçw%A^7ëñ9Ãu.zTÂÛ$ ÿ´kÝ¡ÞaÜP¼Ù‚ºIÏôZ ÚÜÈÿì½ý5úý3Fæ?›Ñ—wo“ öïÍ¿ëìœnœî}Dþ5êö·àïÞÞGüåôøí§hîoíðÃCòë_>@ï>ïltþ™Ÿ¿E[¿\]ý]s’ߤ{,ª\•ÿ~00i ¤[²Zó³d5` ”QóÑÚʘ™OŸø\sŸHÎ…DœI¦8[ñþR†I\LêoÔ´TÁ*â9.7-Öñ4áÀN‚Þ¥*&ƒ»È¾ŽåŃ؋E‘9C®pPöâèòzx9H.6¢°/0ÒÑÆ¨yÄ:´dGËÀ‡a>‹„[A|dµAË(ÅYæE5l¬l4²Ýèæáù"Û"± q×,™öcþ14¥×rEë?ff'ÎÃôªnÿ´èûsƒo>Õ·†i =Qô䬋Òâ¡ì|1Ûÿº¸2¦+yx<7”ÖjMD++b…DÁ©¤háÔªÍÕ¬çéЬÕî £ÊLKSžÎqP¹]:ÁÕûuõªvƒÁu¢àø­±–Ýôysô4&èӯ״V™]äÓÆ¯XĘ/ª?ºâQ%ë²²¥Ï¾$ÉYs˜êóFŬЧ Î<·¹P-¦ô¢5ÿò"­®I¨/K¨"Ù ê”ÇAföæåÿ„&NÊúÅneutron-12.1.1/doc/source/admin/figures/deploy-lb-ha-vrrp-compconn1.graffle0000664000175000017500000001664013553660046026617 0ustar zuulzuul00000000000000‹í]msÛÈ‘þ¼þ8º«ÈÐô¼ÏÆÙ+ù%±¯¬H^o²¥ª+ˆ„$Ä!ƒd'µÿýzR$A"hP¦¨Þªµ$bfž§»§_žÿï—‹^pgƒ$íÿé)„ìi÷;i7éŸýéé/þüÌ>ýߟž<ÿ¯Wï_~øçÁëಗ òàà—ïÞ¾ ž>ÛÝÝ»¼ìÅ»»¯>¼ Þ½=úà=vw_ï? žžçùå»»777aä[…ôÂ7ìdéeœå_ßáÍža‡°›wŸâcÊ»O ?í&ü§'?<ÿýi¯“'×ñ»èkœ½íwã/Ïwý§x1éçñYœýÄžïŽ~uÁg'(Ç[~,ï|Û)ʲÈÿòÃóAžáWþ G¦ýä,K¯.Ã÷øÛ_²èô´ëç»Ã&­A»3>׸hò|wtërWyº×ý×Õ ¿}tž]Å»£ë/¢Î'ÿÈ~wyžtn› ¿û°™o1]»ÆþÃvöûNð¥p@F)m˜’FZ%wcyh­3ø3¸úý÷©ïâoö² fï{”ö’ñ€*]Þ¾·M8Ÿœû²ÙQþµ[޾Nyo™~_¸\^•E7ãa¶ÿ~r88Ù·³4úmüK9½ƒø·4½Xb±¼Œú×Ñà}–œ%ãe2šçbš'ßrÑ#í]]ô÷zÉYæþ0{ÿ¢õÑeÔÁ[ܶÏâ¨÷“À5Vü2jŠøEû*ÊãêP8ù à°Ì‚ÿ(Uð†ÿÍŒÎß$ͪýŽò<ø¾ôè&Ê¢j§WÉà²}=êD½™'ãžÄÿ‚ÎEð§àö÷ê þœôâ_/g:G¸ªm‹ö*í\]Äý¼ºAo'’ÏÌäpa<†Ôlèé•VÙ9[‡•³’ §p«H;à*ô)Æ´ÕZ*å7€…U–kf…³f'P:ÔŽ;L) Îq>½»ê¶×xG—qÝNbÚÏßöOÓùûÓøÕN_/ÜL\¿Á©GM잉çÖlº÷—qÿ(êžÅÉIÚëVnSìöäßñd×b9ÛÛe=õ°*ŠLÀÓS@R6ý9:ëÇùä4Þ¾ìæoÑ& x4H?í³oãe/½êξ…i›y§I¯·è T_ÑT‹²ÉÉäåᜱÐjÉp]2e cJƒš˜ÅQϳڞ¸Ž´àÌ2ÄüšžY}O‹%à —Â:&¦{N-”ꢜGÝôfÑLTÁ|š×Ü»B-Î2~OÅ4»ý–žeÉ §.ìY?Ë‚K¡•ÿ7w–G_.ëÇÙaÔM®¦çofwMm¯ÊNûÉç/ãé«ãítœå§pŒ»>)þé\ž¡Á;i'ü%ÉdùÇàêÿ–†=ùÏñ)H~Ò;>eǧývΣlç,˜Á?þŽí;þía‡?gq—+u|–ÅqßÿrÒ»ŠñgqÁŠòsÀGúÏ8ö>¾Œ²îqþEi†ÿâgÅmýÎeñ7þ¢h"\ÑDÊ¢‰bEìíh(šhƒMü}“¬åWYÔ;þÜñäÉÙy÷O£R*{òÄÓãÓèà¸sʃƒó¯8GQ/Øó›4û |gÎåUoÿ~—ÔÒ€µ zZ;ãWŽ£‹$ÁIe„æJHéHI>!·M ª$ ¼pd…B¹Ê rx囦y!çN Œ?ÌÕ*f¾ñ¨ûÄW)` ¾Eé¤ÓB8ÜÑ\ON[ Ôþ „ö]Ér•{o4›4 1"°%‰8· $2¦ûo`0*Æó{÷"o ÙÃ5]Ç^y©¸ìPÇ¿"ÈÏv^+pNQÂHaŒ³–ƒq%7qGµ°F£ý°¸!jb!CdbÖ6à&ÄRDÔÜ„¢ ËM²-nâ®!7¡–„ä`Aã>ÖBëû¢&î7ˆLHn¹´€¨éÛô¸BÀc•jym¹ôì¨ Ž=HIž¤ý™¡²PJ´5[$qPbZ™*:¦yä;W\!Çé »÷ŽºO|•âBXÜ!ÖŸ°€ÐÒòIÝšäÀuÊ›+Ù‰‘d¶”ìœÛ eudþ¸ÿvQNÄñüÆ=\ö×ñ®é:‚ð'WaY•Ì{¶Ö1Àš$JWj礓ŒKîUY!*¾((#J —˜‰˜ ™‰µÄL’›†Ì$™áœáNÆU*ŠèÆû!&…ïÄIÉQF3Ƨ´ÒDLDLDLë7uhÜyÂHaÀ8j°¾5!nD¤%¹Ô¬Ìlˆ›F¡ª¶9•ª È©LU›JNàÚR›XcrN£Ã…ÑJkkÄ}±÷{D æ3`pia*Vø1³Óö…5>f¾Eœ®( WmÝ^ÇùùƒæZePÊUR3Nã$ãã9„Nh¬ô6J¢ZRGLk[cZÝ”iäÉÇ`™œ²®×>©8ª ¨wj¯žY D´D´¤ØÓ¶àS$Œ7·ú¼ÆéÀ’OQ{\eȧˆ|Š6Û§ècQ[+øï«~ÅÝÿÙß"-€¤ «ŒF‘Ë2ÍQö'ç¢ Àèì\´u5ˆ»Ÿ‘ ‹95ŽnDÅýF¹wÒe°Ë1"ô1®V;„.©“µ¢ÞÃ˹‡:²°Þ’zs­—^u7ж.Ì–Â|(ÌgËclß"fá»Ê© ß]Þœr•Ô© VMºJ=¦‰Ô©Úþ"0¢àÁ0”¥jN“ø©´tJkí ¼4ÃOmpU2cL3üÔ`½€E1PPÕ6³¨¶UÕm¡p£á’’ 88Äߦ ׊/ƒ¡î=Ç´+œ8éônëâøWÐDká¬áé6Jsæ% Ck¬¹·ã;®C*-¾#½ÕCÐñy•l¸WIi~Ø>œ6ôòVû²LÒGT˜"¾@éPOW µG1ùìQ… ¢»”†‡ìRÚîÙ¥Ó k3ÛƒÈñV†êJ¡ä¼Öd]ÃP]Æ´λ:Û†Q»ÊXéÏ^4§$od€Ú$”nyA–(ÎÙFZ ðjðÆF¨ùä'••DU]JüW9ïU¹5ä·ÉEW %ùèH‰û 5ÛÀ¹Î¨I·¼åë4ªVZÎKTALDLDL´<‰™H„€qð7j¨jâD0'p»KkK&šux¨nÛ©†5O§ÍÄ6¦ÓÞ>Ç%"8"¸G@pø=ŽŸäÑåºéËÐx~³†ûŒ[ƽm%½ñm¤· Œ"J#J#J»/J«!òé·ÀÙPÜÚ §±tp¶ù”ÖØ?C:ûøl”…6§P-³ måëNmN:LI+s µ9%„ÿtD}D}D}+PßzŽÏâBëG@Xƒ„'U  Òê¶P«CdÞb£%ÑÑÑixÕ2³Ho B\¹–kf…³’4¼­Ôðk$4‰îˆîÝ}üG~0?‰t :óHtº­ÓéÔ±±±=@=î‘¥šh+š}a2¬š—BZ­uF3g€éBŠ˜ÍU1OKZ%«Å#ÉK1Œ«cRëfa’ãÆÁI ÃL‘»QsJLÑfbŠ™—¶S6wYçt:GÜFÜFܶ)öË çå`âüœµH²Ö„É‚‹–8t__AÈ´àSÒp®¬¡ìC¹ø¶‘®ÊÐ%†7 èºPô¨ï·Pôúb`mÖ»¦5wà¬ÍZ{’˜CPgxó&ow…ÍQà¾Í¾Dygý5…ʵ]¿GIªlÖ"@òjÙ±5$7m†ßÜ;î=ÛÒgö“¡Ì0ÜæZ©à‘}Üè¤O¼ 4Îä.#´«N‘¹‡Dbe´«º.Ötó+‡×¥câ:ôùþŠ$€ûÜHIܬãmVfœôÆÀ.™ˆÓjõP½§ þX‰­cÅ9âòîS 6¿‡ÝÊåëe„… S,yå{×4.f«–Ǽâ…3@Ãî#gÈ}ŠÜ§¨G£qu£<:~r¥xЦ‹0Vº•ùlÝDË©þð½æTþ‰Ê?ùùlù¼zóò`#ËwÏ;—÷Rê±™ÃÔúOG5´i[ýtôîÎd #kØC?Õt8Ú*>®ÿpT­œ7¹Îâ?“ïgîyÁ èJIù@ ª1²÷˜6¹„]dàªf›yE·q …—“u PsÑÆ’Õ¥…Ü!‚¬.du!« Y]¨èöÆÝ®«KTá­ ™pB;epçtm%2_ž[óÐËR w sì†Ê?Pâä–ˆ“ˆ“ˆ“ˆs#ê_ò¨ß‰¿3OùÊò†Y¤)é´F»@82PF*#hEx2Î%•–ÿö_.JËSiù(-÷NŸ âì:éÄ3õå?î¿]Tp­IÄ€Œ¢¨Ð…Ð#Ù0I ×NHË¡HU&d“.@Ú[‚+Ÿ©{­ã{/çs …ÕAËœd°IÖéÅ” vmóÖ{:°´˜×ЮTºA£ «E(€Ú-Ê2,¬Æw²–u^¿ñ¥ãGbÅ·n~¡I® T«I ‹2D>ké žo°ñ³è:JzÑIÒKò¯ÁM’ŸŽ›~/îòTK'ùâöUÞ^=ºŒ:8†Û' Ý6C;ý`¼p†ÞÿJ³Úî·Þ–•–&×ôfà{›×Û ÊÖýèrð!]¢ýøÛßê*éÆƒwiçS|Û±>&ZLÉÉéç<¦ðÑ=ˆÎâÛ­Uï\Z²Ì6|‰"FgK4ÿ[_~H:ÑÄ(n]yK7ÞèkœŸ|ëfQ‘jü¿Kí/[ú °·ÿ˜f›àŸËšzI',7ŒI|sçmç-pmz•Ï_â{ýä"Êã™T1ƒø«$ë¤?'ýWÉ ¯l°•õ_´=Šq¿O'ò½ÍÄUiß+†ùº6™°x4–nšÏ Æ»z¤Þqû]Ü?›pú=€èÝYÆŸÌ<îß`€K—ÉRí§7ñÓ¼Ý3»Ù†NE Úp÷ÇžHå¿Ý?§Ýä4é£~5ñ¦G7å¨V>cîg?Jñ#ˆà~æfžUÜhb¯/ÿåùy|ü åõèç§Úq?Íãå¿Òû,A@Ÿšäú´Ñ£Ö¸d–¾¹Ç¦øž> –hë·óü³ô"Íóôâç(ÃŒ×Äw×ð~§½4ʧ·õðw Ó[x YåÞ¤Yòï´õpÜI¿²ðfÔAÒìÖ>èÅ^þïöþù+°ø/z¿½}“îÉ¿¿øûÛ£×ç{çïa N~ýk¯söÿî¾ÿ~;?yó±·‡×_¾V>ð¿~þí×}¶÷ö××{GŸ±ÍŸ¿ôn^þeñðßŧù·LG•E÷?@U0›éšÛ—?jïŸ Òg‘\âS*TÝ3f}n£•œ ¹QJ¦¤A}WîVBh­C¹A1cQüýŽ!ø…x_#¹Å5»ã!¬„C/ô­s)|H/×qûi,/^Äû~ìQdŽ rGÝ÷ýÞ×»áå0½™ò1Y ú`ÓŠ9¬:3–:‡â#òÀ‡$ŸE—Qÿ:L -e È‹ax)ºõöüáÉ2’^ÑÙ Þ­aß_úÉç«xlhXðíįûy’-šöË ¿&}Ô¬ç£úË«,Ù(frVf¯Ù_¹Œú¸Ùþ¯œâ ™nÌÃCËM6…TçEÀ|:µÒ-þaL¥(ó­v0eÛzžgWñîÔõëޘ¡gà+:‰&.Cá&¯OGª-úò¸ëŽzÑÉ›Øo惘¤ª"Ô¥ã³)ô»†Ê(ð‰´|žU¦¥ôÎÓÞÓ]˜+éP1e,ó¾:¨‚† ´5Þc‡ ²fÆ~KÓ‹ª¤*^Ø ˆƒ+£ô}>F¨j¡¤ÆÉ»fsý°Ìã&Úˆ)kÜÜf€hüó²‡;ï§'ÿE—áâðneutron-12.1.1/doc/source/admin/figures/NetworkTypes.svg0000664000175000017500000224450113553660046023330 0ustar zuulzuul00000000000000 image/svg+xml PhysicalNetwork Project Network 1 Project Network 2 Compute Node VM1 VM2 VM3 VM4 Project Network Neutron Router Network Node Provider Network neutron-12.1.1/doc/source/admin/figures/deploy-lb-ha-vrrp-overview.svg0000664000175000017500000012615313553660046025762 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-04 14:46:19 +0000Canvas 1Layer 1 Compute NodesLinux Bridge - High-availability with VRRPOverviewInternetProvider network Controller NodeSQLDatabaseMessageBusNetworkingManagementML2 Plug-inAPIManagement network10.0.0.0/24Interface 1Metadata AgentDHCP AgentLinux Bridge AgentInterface 1InstanceInterface 2MetadataProcessDHCP NamespaceBridgeBridgeFirewall Network NodesLinux Bridge AgentLayer-3 AgentInterface 3BridgeBridgeRouterNamespaceInterface 1Interface 2Interface 3 Physical Network InfrastructureOverlay network10.0.1.0/24Provider networkAggregate neutron-12.1.1/doc/source/admin/figures/bgp-dynamic-routing-example1.png0000664000175000017500000033327313553660046026233 0ustar zuulzuul00000000000000‰PNG  IHDR’d¢CÁbsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|ÕöÇÏÌ–ô„Þ{S@QQ{}ìúü+±ó–÷‘ì >+ê³=»b  (E:¡—¾ÙÙ™ÿïL2a³Ù„M²éç~²™™;·~ï½3gÎmDb„€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „@°”`Š;!Ð ¸\ ì{é×N¾y3ˆôp ÏxÆõp†¯}°çñ®´HÅ{¸'z{C1š†¾ÏfW÷´¡ž]®áþáJƒÔü³ûtÝ;|øp¯¿û`®ïŸ:µ¥V Äøºõ’¢µ£Óv¹\çj¾ör^šÀȤÔ[Qn1³S’f–¾[5›@å­z¼Þ–Žö{]®;ó«zíø®N^µ“#‰U` ˆ ,)q× ŒvMëáñh›fN!}JºªÐ‡ªáxyfÊø ÝYŽr¥ Ò5ºÏ0è"#ª´[% nžCµÿûÅä k¬ûe§AìgìVHY Ú•é³\ Z~Žu‘˜2— ãwËáv3Êzч³RßòwÓØ¯ïî¹0÷ÞÌ,ƒ ‡©¶›°;”LÊ.oÔ6…†²\µ)3Ò¦$|Êx«+¬êæU]é–p…€ 54ÁH(B žP(CQÔ™üÃë|Þèïà¸a[]7Æj†gùHWÊÀ@¹|ï½÷lñ‰©z=´Ø0ŒÛ ‚ßOU™Šßêª<î3‘vqWy½¸Àø¤AQ”7áþ¸Šä0½cU|bòȾʵB8ßçK¡7H¡¥·%òõ/sãR¾™8½g¹4²›Ï?ð€üW`µ¨gÒÞêÊ>êÄÔ³ÿðåô:~ó RrP6é^å’<µºâ®h¸¬á‘²òÞ„”Îþ~kŠ—¼r-„@Ý `¯ÉTÚ%ájï씄Ñþ©ˆì±8#K{•/݃—>ÑIþn¾[µa´@ð(PHýw;ûÓus—·ö÷›Y††+P~ꩈœCycÑÍ:]îÏÅ'=¶xvò„Õþi(ëÚ€2'9á¿þ÷G%M=S7¼3!¤žOäþ /¦&n÷w×X¯Ñ¥}UaÞ“ªb왓’t·o(eTbê:Ñ›ºA è9;54µkÜ9ú¥hýÈa ”’á(b±B Ö ˆF²Ö‹@P— Ìž0áˆâ/Jc_}Ó;rrêx÷…R·)¶ó Œ&"ÙÏl×ÈÜ™®Ä_}ýëüé1cò8L„ÿ1ä$]»ìX~‚¹?+yÒ¢vö!ý!ü¾!8ÚCôb0þ*ãÆåz5¼2þâÓÒ•ñçr½ç,Ï_eÓSV˜cgÌ0Œ¡,×åÛC+i¤¥&¾ƒry‚”ÉtAù>ˆ¸k™Ðc¹«Ò}Ÿ²Jþý•˜|4b/¡›õ÷9)‰7—º^ŸAš.6ÕÅZOL¸¸šÓIŠâ¸!ÒæÜ‘ãÉ¿Cvi;G·hò°NµÑÔYS? æ=®iݼšö¸w)xv3þÞîûÇ2»bŸ<3yâr_¾ñ)aÚ=ßx i>k—¶¡#4ykÑe¼(Ì3îy×™÷¹oWàñ<ÊéI÷¤÷‘˜|D1”_‡:6Í5i­o¸|ŽxÃ!6-9±7 w¾÷Y¨•”râŽq”'ÉÈo÷Ùΰ éÔ´”I_úº¯Ì9*ÔrD:B7Œ>ü±JŽî}GºLLÍBžV©Š:7-9a¶¿Ÿ‡\O7ÉÕ²ù#f´‡Cýïóu‘–ú?(§yi)I™v¨ ˜í5gòµÇ£…¼â»µã‡Q? Ñ.›—oU´N˜Âcbê­~ÌȤ”^HC$Êõ †à8-ƒrœ á;ź–£5K@4’5Ë[b«‡îuM5_bÐí|6!¡xÌÜè„”Žx›ž ÂKŽð„êÌšAªùчqu™¡Œç%׸=¸^( Ó¸˜mìÇqWjSäíÆû“»Þ+ùŸµpº¡ÜÍ_x„bN AWlS¼ì3 Ïõ9ZÎb¼ð1鈶CàxUFÆ@¯×ø„ KÃ["P¯æ}þfÆ’~ ?oÃA8Òq©fh‹ã]S÷õ`ÅG†ço>ý C òý†ø~‚ÖBíH·–ù!…ù3ìþÿ[ðû¿H¤çCó.‰OMmë.ŸÃmO΋¿= dÚ¾EÜÏÁÑĹ‘µÅ-7Ã×éÂBôLUc9nI¥V•”:÷JÄyâǤ š‹ß¯p{‚®ëiÚ?ö×Þyœy6³lˆºûçɺÆP‡¨ÂòSÚÛ)J„» õî°i§ÐFĹŠªAë-weñ²Ê¨2uÂúÓ`ú˜vÖ7URþ…6¸ùæöˆ²Q¶Cgû¾ªª?Zé£5O@4’5Ï\b¬GF»ROöhÞÙ²²=è›t¯¢žF† +ek-}ï…þ\R¦þG¨ÃVtZAÆèÍÿy‰¢ø¤”×0ë{¦­ß «Élïk²2 ”af:„¨ç'MÚï{/yÕÊGJŒýN`Ý‹OJ½ÛÐõ9º¡c é‚Wü‡ w¬b×=i®Äe–îM×Ò_6g kú$ØßnݳŽV&BØx™:µ={äHS[6Ê5­‹®i?B«5Œ¨` Ò¹ Êuƒµ¤Ó½S§6/ÈÕÑ­oœ§äé ¬1Vxå¡ÕK«óÞÏ¡Ü0Çg¼ë(׌Vmh`à‰TåêwÏÔÄ%¥\QdÍB}±¹ 4Èÿ0£^Ãdë&çÉ“£¿ ù*Òöƒ½ËºWÙãìäDþÈxšÀ¥8TÈù@Zê¸M ¯¢uâWj?Íc<€x²ÈѲ )Šó)¤eÊàqÔ¿}s’“’*šq/„@h „èë9´‰’Є@0”.è\æóû/¬M_‚´,TíáÝÑeù?ßtéd´7¯ hiªÉ˜3“RÀ‹Ú=e¯ÝöI¨£Â"7E‚Q¬ñ³ÛlsX†ß?].£Ôs]úÜ奜íÕÒéQrx\©¯Énbc<ö/žší£_»øûKKžô‹¯É÷ÍuUå->‡€°›—g?7‰uŒ±„Hv;Ë5q4iŸúc-¡1Ú"ÙîÅI“B{‡Ï¡]ëgñ/ÞõX'¸½ó¡^ï¿,Ð,×#ûü…ãcYê6ÏŠ.âé,°_ÑÎq¦™wË¡¦y\¨ Ñ;ù¬¯É÷9OaQêMà‘‡ËG¸;ßòWûÇŠÕ CÓOã‚Á¨‚Ï|„H3NÅx¿(?Ö~¾$B@ˆFRê€`Še©u1 C ƒÕ š.èt¯Vp„º|Çüš¦|ãwûó9•’Ò^Ï£­>VÖé<ÌĽҺ°ŽÐ25Ã2Bÿb…æ†Nm¾]µaRÐÂinvE[Bv´A›ŠQ‘†‚®iSÕe¼äš¸9>1å{\Û¥¥@«GßXѱVÌã)-í¾vöÁ¥Ç*Æ‚@Ú'y$Ý®?"¢‹¼ºÞá¡;øØ¦½ÚnAºw';,ÕmúF|ÓÇÏòI±Ñwä¥Ñ(ÛÈ]Ú%ŒjS¿C:¸ì;•¸QÆ…êÑÎÂ’Ia(‡¹¾CÊp~LkÔ®øpY˦ þm Ȉã¶ŸÚ¢lÿtMô_<ž5¬06{šyôûÇÚa”Û6oóx<çàv¡°ìç®Æ/+X'0l¢iaÓSK}¤)­âv){3óÀ)Ž?r\.èÔÅ!PkD¬5ôq#°K±kä8m…;,¨iÆíéÏ|»jãÕ&‡YÂ$^þÿA¥ŒæÁÒ:ÅKõÀm,÷€PXF»3ZBp{‚Ãi™ U¡g•pš1+11ݲ åQÕ<ݼ…naPqØÍÆù0XÜ…c± ©y nD m9çÔÀÊÎâ0üNüA9,+9ýn™—¼^aAŽÎqv†4Õ ZÈ&˜Î7¡aòÒ†Çð•¶eјÇbÄ$);ÝçÝ‹r(›]L‹¿0*_XZ¸ñwÌu‘ðQìa0¤*)èRN*¶/:áú¸K[.Töèø³]˜çа†ÙàOƒžo׆eë„âÀä2´”âMø÷oß${d^ŠzËEˆô%#çB v”ñB«ÄH¬B .(’~Å üxóbãC¿[½éN¤ñeN§¢Ø0ñ›Ï(Ô#Pº¹«ö¬{˜íz!„¨¯­ëRGLæZô¶7ÇÁ[ØvßîØRîCdY×½Xr… ³¡DZ~¬ìØ·‰¹êa×SÍžv9Ä÷ÁÁìÖVÈþZ ÷EKŒ™ äÆßî~×s±níÈÌü\ïu¸ç„ð‡ÉúNÈV 0¸YødIÒß_cbÎÁ@öGí k|ÝQ«Jœ!òΜ ¤gw%¼ò²šéâ—{\)§kXÔúµ{0Þñîªöõt~kv°I/!ðû:¹JúóÃ@¡ 4­–wLZQ¼Þ£_0–}(Ž­m)á÷]”ºu­†˜¤©ŠñŽ¡nPóô˽ºaŽUFrÿŠ´IB@T@©±OU N| †G H ,ìÂ5Œ!Å9´9 ùact扅ç•ÿA%gVJÒ|þ¥%OXYB¤™zC?ÇLµbÏÂåksÁéUhÂr¼¹7³Ý=‰‰skƒÊZ½Ò×­ï9F¢áë¨Ïmn•»ýc!ÿl³)×M¹ÎáPO o× keέÁ¤HTB@”C@ÉràÈ-!p”€~.ŸcÁéGíÐè ûYЂŸ43Më1µÖuhx RŒé/&OXãŸú¢I*ßAXÌ3ŠÑ>ŽÝXJ'd“8tÑ׌³ß¹Ô4ïYþéªk•Ôå…ñê×û¯ÓªôðLpÔ±.¯†9O¥fÌK9.¯æ¹#Pœ¼¾'4Š×AÕÉ¡òЦ)Ôn+YB›Å»Rû[ö%Æ…%¯}®x¶=Œªhe °>®CrZ y¦âë­o”=úJ^Ä~ΔÄ_r%¬0÷öI ˆ¡ ‚d((J –À=Ó¦55wp1輜3U»ú¾ofÍåj ·³³‘®ÿ<")%™·¬óuS—Î!ð*<›ã5S¼ïZ¨‡`øg;{/ vKáîDÒãavÓO­ñ’}TÌÂøVö-çPÿ!¼˜8fð–êæ­X ¡qÍ;ë `ñqjahûŸóß‘…¾Ñ®£«[[{ϧAc3!H÷¦Žò O¡°4”×AL¥ë/òdœÌ ÷3,,bÑ«³]“J U@8ḛ̈¼Æµ¾aòùˆÉ)×¢n”-HRay ïþ~«ëB$üWVq1k`Yxç_iẺR á ! {0ŽÄhè _uÀR,Åc®ð²¶C`hçÍööÄ -רâO½s¦kâ_þ,”Ž-ï¡v û7t#ѽ7ó~jx‰+øñvnJ„ÕçÝüýVûµnLÄr0wšñ ?Øj®ÒbvoBpËÃ:C“ÃZÅLw=p§»¬´›Bïß‹…¿ïCЭ¾V–ÛÊØ·¦3—íRþAæO®¾iüÌÓ¡S;Ñ0 nÁø:,HN— î2»e+oeüØíöG<š6e}—±cßX®éL$É›éžÔH룂`å /?rrÊÝ‹x #3Ùß·}OK·idÒÔ+°°û÷XkqÊö}”ç*¤¡UºgÑ`t2êêZ»Ý™X*6ÅE^ãKh•¹NtÇ€…ßСt ¢˜ ­Ì‚ÿ‚kqªò.üÞÁî!´“Ψ;ó¡ŸlÅûÀ» ñ Æx>Ž4†´w<:WOž»FXk+Õ3"1e'V3øÐ¡>æ?)Ét$ÿ„€¨1¢‘¬1ÔQÝ&€]Z ºÜúA¨¹/ç6èòÅþÉÊ3©”ZÜÊOJÁ õßv‡2ÈÔX)t‚é „q^ò¼.ä xAŸ÷ùÂÞ¢" Ÿå¿zF?ÄÙ⦶‰5<‹Ä!jÜg§½4¯ÍfS»©É!ÿ:Bgu†‡ý¥ÇckÃÇU™Ž5ÇWW\¼—u¾7¿+ÜN/Z>©º¢ I¸¼ßº×¡Æ´¥‰ëkcMC^wÓ]`tŒpPzEIgž<¥Cl´º9ÐbîeÁáaúþܺÍk Øôô˜1ye¹­¬=ïæ„…ø·áåÔDµ+çÏ|4a)H|W5<ôH†“ŸŒÖvÕvöÌäI?½+gB@Ô$$k’¶Ä%ê!h¯t7o€ª§â°õ0ö®æJ’\W ŒLœz©nx¿€vu6–YV:±¾$´ÖÆC¬eœðvYîÄ^ê% ]ÛÕËWBõž„ÈѦIÊ"DÖûâ¬óÀpÌDª´º¼Äb­“Íû6,‰$FZ# “mj ½D,ê6îÆtï˾/öÇ‘R/fWO©Û)–Ô5ªú#a¦Fk>Œ%µ–R‡æ¿ó8]+o£]Óz`ÂS&" ŘãùiN܀붅€¨aÒµ]ÃÀ%:!P× ŒHœz¥BÞ9èÑÆ‚ÐF f˜ló`ZJÒóu=í’¾†AÝÖãPí¦A®b’M>^T+QÑÛM­!@væ: »Ï펦7¿äº7»aäZr!ê'$ëg¹Iª…@µ¸Ç•Ú‹aOÂK¼[IU_™<©xqëj‹X>F¹¦uÑ5ý¨&{`w[̶a ¶Óv§b¼ûBJÒVçr*„€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@!  YG²±”´ä³N‘r›¢§%'PpR[‰Ä"§‘͹e¶kìòÒPWÒËi‘˜¼PU”¹`7ËJ³Ëõžs—¶!×—¦L?›œ8Ѻ_W¹žn’ïÍë;+yÒ¢ºšÆúœ.®+Š¢¾†=¹_®Ïù´ ºH@¶H¬‹¥"i2 Ä'¦|kư@8T…h;­@Ði…<ž(5eçr-°ïò,ú‘Œ¼—ç+^—ËP].E·®ùXÒk¥_Á'áü{ëš»µ .,^ýöCy[ì­UTÊð½_WÏó<9÷ô”øÇk:{„#u5ÇJW :s,?5qŸëŠ¢mj".‰C46"H6¶¯oùUh©J’² ÝXáo'ו#àr«HJ£’£„6,]KÙ~¿ëñÁÏ»Æï¬\È5ï ZÈ[°{ÞW³““ê¼Ò—Žª8Þ'Ó›V…HÎO}¬3¾å çB@Tœ€’g&>j–À9S?¬Ù(_ls’YYlF%M;Å«kŠ-êÁ kÃvi) ‘œY’["‰3SÆo€ÿê­©u¦Þ–„ :D@É:T’”Š‘˜úœb'£åå³]#s­â“R@—ñíávçÕ¬Q3… Oêm¤Ð­dPoC1¢ÐݵAQ•Ô´)‰ŸûKH¹÷.Uì÷Zþs ç¢C7š®{ˈ²" Oþ ø w±Š¡übwØzÉ5qsÉ0è¢h{ä}9ZÎóð{6„îÿjƒÛY®‰Û,·e1¦kâMÀ¨ÉSà?†O~ÖÎÞa’Ëug~ ?÷$>~œfxÞƒ»Gf§$}c¹19åZòÒdUUf¤%'̵ìG%&õʳÅqãK)ã×Å'&ÿj(Ê,PâÞÝЦ°[·Çó-îyþ³SÏc;E!}ôc5‰OH™íß9°jjò§]Q'ÏLžô3»)ËÜ;ujs-׸[7ŒóÁpüoDØK@çõ4Wâ2Ë_Eó´\ŸîIÿõ@`,çM8ß;'5ñB+Lÿc(ÓÂü±ªƒ tMF™ U‰C9udgZjâ½þñ#ÍÏâ~9)I—áüø3;%ádß±²\N:ê‘ÑþÃñÛ (¶Ó’'ýÂáÅ»Ò"ɳÿqø½ùnú½Ò¦¨‰Ç*3>®çÑŽxÊÒžBPCQ-Pºªd$ÍJIšÏá[æXñ”UglŠãžªÔKŽDÒÔs]OBû!äs™œ¸ÞZéãcYeö)¾î¬shá“1üaÚäMÜ&ƒ­–9 !PHÏ:1B þpÆ“x¹ ÀËtš•‹‘®©}ð‚˜™âC«[–ÇúáE4Ú0(îSUE¿ÍðÒ‡£ïmù…”Ô/ªËIËû/￲Ý?ßB »ÙÐö½ !ò{¼èݸÿŽ/C¬iÚ,¨– ø,[Ëù q€hÃcö¦@ë¯{¼ËF»¦—;VkdBÊ ð7ñÛ >„€Y0‘®¥Q‡ßIkû¸MHwGü.-qËK·@¸ì¡ú_{ŒÇ»: í×uc‘}_ÄÓšÏ½Š…»÷L{Åx¬R E}¦ÈäpÒ´lï`åŽD¸}–í¼ºþõ½ )-wŽžý]ƒŒ{Áî+„y;Ü|‰_oÅkóZî+“U1öã£à53 …¾±áAµÑóV˜Ž!NK_ð¼BäÇà½å6FUm_!Þ•àuO¦ñMøÇÁõ0_iÚ£Þ ‹ÅÎF&¥ÞŠrâ1 :x=JŠú/ô›²›ŧ¥9 Ï~Žp ê×çˆ÷_(‡HM×ÄON=—Ý”i¬zžíùåèAz'¢§¡®·@A|q_brWËo0ñ”UgªZ/G%¤Ü¢Þùhg‡ÁêAüÁ ‰FËF&Ml¥±èXVø9#™˜ŠöoŒ³Ù—õaL}(X!@va ê2…”.Ð4”Ò(ÄÆ…O{â‘Gr^LMÜíÓ}ÈÃëÐ\üï‚{,ünÕ†× …–]Я×ô9>™ƒvê4ŸK‚ù³Ç(X«)ž‹`_¬ÝÀË4ñ¾÷S‹Ü Y$^Ò·ãEöRZJ‚¥]úBO–NFònmFw¸Ýd…0bñrþ0-%ñQËn”kÚ—$7jš{,ìøWÊÜÿÜsaî}™3×çÐT]]ä൑‰ÉË ¾22qê¥i)“Xø*aXP§Z†Z7~ꩈœC9A@xvwŒv½ý’ëÞl󾡜 ÁcþðáË8ËßœäI?ŽHJmKºñ@˜Ýù™%Œ[÷‘6'&±|‰°-»‘®”ït±T#åZر†«”a„vì;ÂîCàb yŸú:¬lþ¡AûÁ ÿï}oA;úǬä„BAØ7pŸóêH Ê<‚Éų¦$~mE…2É€àœ”§e3—ÿXö™žk`a#çë–ï‘5€§#/Ÿ@Ky-I­i^¶Ü;÷Ckœlw¨§¼äJXQdÿŸ‰)ÛH×Çõ Ëm #×s¨o¿D}ždÝ/,GZê!å°3Ë1˜xÊ®3“XSX©z9vÆŒ¨Ì ÷ 0xwNJâÍV19ìÍ]ÚÂ…º®?ƒ6qºȆ¥ËÀògã“RïÖu#_i7Ïš’`–U0õÁò/G! JÀ{QŒ¨ËŒæxƒ^íÿóæØÂ¬TCà{Ú Ñýõ „Èþ·Ýn¿=dùácaט²‚M{_{>Wçû¾vèžœÏ׆MùÀ×#Ó^!wG_{>W ÇÛ¾v¬ù@>¾„`w‰¯½ïyÁ¬!x9v„6íU_{#B…†ñ+ú™¾ö¾çx¡b–»Òïa×SÍØ>ûPþ88ÉaO‡G?Ì3afp®û³{¾®Œ±Ž7|ýÍz4a94® :ëâkï{>{äHò¿¿»F&Nïé{Ï:¯Jþ­0‚9VGZðññ½%˜XiÀ0ƒ-à¼år‹egº¼–,q‹/ ý>‚f[< Ÿñ”|B´¼ 5u…iÞF:¸® `¡Ü×} sÃn;f9V5žÊÖˬ¬“>\^ðM;OÏAiÐè?ÖÕ÷^ 2°îcè†1rrÊU˜¨7 y£}?6‚©V8rB $ÑH–ä!WuŒ„ŽåÐÌ}¬d9#m£Ð5µš5вÜ=Ó5ñ/?ñI¨v»¡Óq¤:à~4^FÍ1­Ô•aK÷õÁ.Ó ÝM·ïòµ·9”L/Ä#¤ÓékÏçáÍûÛ©*mÒ1.ËßÞº†0ÜÏu/M‡tšeoä!•›ˆüu¶ìü6›í;]×”-Ÿy} ç,€ÿ2Ç5áoh…–À÷•°ÿPÉÕÎF?¿j³Û¸Ë¼R&2N-‘7v°\Óå¥8øF Ømך¸{=Æé}…rxÎwLgUòïï9´Æ¼¤kŒ ¢~­îÿBž¥Œ•åu”hÚ¨””ö³ÓLMm›kœa†5é † tÓQQm§5ô ”;Ôºnœ"POÖúÞćH¨ÞC9üq³Ù÷žÿyl”c‡¯] r¬j<•®—†a–™ÍV*Šªn4ÐH¼šÖé?ÚÖË*Τat9Â÷rŒží›o>?V}ðw/×B@(õ0B >@EV!4™õÇRHæäݳ ïæ®y>‹h¼jW0ñF ¸f_Ú£ñy8x©@ dÈÎæõ–j_Фð$;^ö8”6ÐèE±-Òø&ML÷ýA¸¸ 2ïœÒ¾ mx‹m y‡ŽÙ4®P åcó®¡|Š/e{¼Lѽ¬üHØ.+lûcÇæúÛs=Û5i=?ã¯ÆÄÆ_£+vÁ=Ó¦5eÿUÉYñCÕ cìýéüA¡N „°œ@i³Å¼‡zæÖó•ù~^¾qÊBS#mïrÏvÞ"ùur>hÉ£Àp¹o1ÏUeºmïÔ•ýüùÚSŽU§ªõR Ó<¾i6Ïm†i‡zTBëZV°<îÂC)ŒAø°ºÇ Ççß±êƒS9BÀ‡@©®Ï=9õ†€;×û2^"Ùæ@óðÔ½®© ^tM2µ9æn'žÓqñì”Äë¬LÅ»žhAJ^Ä6Ë*¤ÇÜ­-,©M1ŒsðBÛ´àPÚ¨†ºzˆšê/³\ ¿—vqL›ïðÙ˜Á|:\¶pBhfvÅñ ƃNÛM©ýq9’ê·l_¦(ïŸ!îÏðB¿Âä—z¶÷N\?‚ü—ÊÆŽ*eYdQiyÞõ@&òù$$ŸÄÇÌM¨qŸÎœ8ñpÙ颭\AÜ”Û ‡Œ@î o†—7+%áµ@÷Ce¢x*\/¡]y›ô\…”d¥™vøtT·›O4¹g ýŸ˜zÄ ý©‘I-NKžP8Ù©(òêC°ñˆ;!ÐØ”Ò˜46’ßúO³[ã¡à» ÉíœØË…­ôhÞ¹¼c çn¿gc[hxÂð"gáå¨ñæß?Õö1¥y½—Œh”kF+¤£ºsûÚ—8·;~ƒ`›cx L¤¨¸Aw©9N/MLÔQV¼’´•C)ºÙÐØ»|rŒní™Á^/wV›ánm¤õ7+瘑T1ÿUIh5§åu”ý@t³Ÿ &ˆ¨¯•—VÅIËPFn¯¦ßZ–;£<›ù‚cÍ”/˰öÁÇSv©L½ oµ ßxÐÈj¥ú­úv´¥%?ÔÊÉ„îC|»½Ýhˆ7bÈ{<­,/¥êCYÅ^4rÕömä\%û!"€‡ËQI©ÃýƒSmêïÜeÆ“6t½à)Œrœ.ÓÅÜï »; r¯H÷.z—I- ;°Põ>’w`\ÞB¬m·kÛa‰º/냬ù µA¸,A4Bî!§Í>ßãÕ[êš' b±ÛŠÇ>úÇ;ËõÈ>,M’ŒÁÓÖ¬KøÂ\CÓOŰÆÓH±½8;yÂjÖµn ›Oz>wós7Þ³–=!°}û{ð5"ó}ïùŸóì÷oWm8dèú¿ ¨üKu8¼¥foû{:ƵËõjø.m×Sпn¬/ð¨QºW†´<óv<{¯jþ‘„âÛ5–vöÄïvyRÓþf{Îï×ãë´âÔ”>á±”Ðb¾€¤1>3†(x•8Ô‹Aâðìä‰P´#UÉö܆…¿Çlþ Ð;c©!wW¨é°:nƒ ä*r%l‚Œ§¼:S™zùü¸±zÀŠñÚÂ~h×ß÷’Ó©+žu}-8Üær /¨hŽx-Öx×Ô ¾LÓÍ„„sìºYÑxĽh,D#ÙXJºžæZœã±>ụ~šv ku£`.¤Ííl½Xh4MZʸMšÆC›8*i꙼4Òë!Dm‡ƒùšQ°Ç[TÅv=4_ú íÄ•oSmW`ÂÄ„|OÁìÃÝÔ푎óy,Vy±a9–DZ.â]:®ÁDž%^¾“c^A—ýÅP¡¡)ÛÌv=AåÉÍÕnvk[®mŠí¶ÇõÒg\ì.µÜš3Þ±V&ÒЧ€ŒmX˜Ü&`ݯÜ1 ÙÐOÀÒ4 rsõ=šGÛbú“(¿'ÚÙ'=i…Y•ü[aûX³i1÷,WéMhÀODÚÞ:ÖŠœ~ŸãPy!î1\Ð~†€8R×½æ8ZÞ“[ WBùçê†{#ÂÿŒÇ;ÿÁ»6žòêLeëåœä„ÿ¢~Ü-þnCÙÂm•èz²)×ù.°|n ]rDã5bo…°úDÁՇЯ#î…@c €wŽ!ÐxðÂÐî°|¥¼ñiU¥1Xÿ‹ý ŒÇTyÌ/Ç£9ómÏOštÌÉþq³_ìŽÓ´IŒsßôñã³üï×Ä5Ö¿ì¢Rìâ5(«)¯˜“£µ¶“ZpVŸ®»Ëªª;ÿu)-åa½Ïõx;…"²y¼e wï½÷ž K_u¡5wXÏžûÊcȰvÁÆê:Ãéãg0K¯Î¶[‘ú,3q'„€B BX‘Œ‰ÙgfW(0q,„€B ®í:\8’4! „€B@Ôe"HÖåÒ‘´ ! „€B YÛu¸p$iõ“€BŽOEÛZ´&]ýÌ„¤Z! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B qPiö9ß¾y÷=o¤H*”mÃÇ5Ÿû^ûÜ’S!Pc¤M×ê2#ò}Ès¡LLrC4,E€2_2ws TTå"ˆIUÚ*Å4¬â¬™Üèdd“nì†ø¸ÝãvûÆ3Ó–#f½è'/š)†Æ‹%8ª¡ÞþÐÄް° ¬¶­’ÝØÕVþ …²¬çƒ¡_ÿgºkÒ"Ï…Ú*‰WT3†.H*ññ®o3å~»Ãö®mxÿã˜Èp½Il¤îpðKHL ä{ÎEp^üX°ôÕPT0q.àg·[ÛСWGvx½áá÷IÛȪV,=4÷Û!ãùÙ³]yH”<j¥d$R!P=² ©þßøÉ9a/{u½Ý =:§öí¦œØ£=E†‡UÍFjn¾›VoN§¥kþ2þܼÊ^ewfÆáÑï¼ðä7@áÁÏ(Én50H„í¸é¾]Û¤éKºa´•¶]M´«¬ÿóÁ¦ª» Üw¿òø”¯4hŠB hˆ‚¤©±¸{Ò¿Ruz‡ÖM.<ÍÖ³S›P\u7 ›þÞCÿýz‰7}ßa%?/gò›O=öR[€Ÿ†Ÿh'ënÑÕ‡”YZH;ë¼mÌ„Â#¢¦HÛ®EW˜Fßçƒaèã^žúè3¸#Ï…úS„’R!P&[™wêç ó…s×D×êj{b`ï®4zø0µU³Øú™›z”êæqÑ4¸_Ouÿ¡,Ú—‘s~ïþóW/YÌc'elT=*Ç:˜TKˆt má·<øÈCQ1ÉÒ¶ë`I•“$ßçÃîG.:eÈ9Y,üaI9^ä–õ„@C$mwŒt±Ýá|…_4w_sŽb³É0Èšª‹Ìºï.ÊžGŒý™yçtêÑkõúË·!~K˜”±Q5U '~F™BäÕw޼´Ió–ÏIÛ®Ÿ…ëû|Ø}ðȰO¼tå⟷ 7ò\¨ŸE*©&†$e©—\rKTxxäîòºýŠ!¬ÉS ˜}»–M¨U»OwêÔ«’ÀƒR¹[²!Õ·Z Ûè¢äúÂõ&Œë×'iÛõ¿ðó¡}«¦?«ù™És¡þ«ä h( ˜…F[«¾]ïçÁ÷<&ÒéhhÊÖúSK™ýMŸ®BÍÐúôK/‰”Gàçä2ÂO|@sLf›†+®7\¸>IÛ>&·:7^tšŸÕüÌF‚å¹PçKM(Ê&ÐPISs~?Ïà”‰5exMÝá2ಈkÞìÿ'kD+YSðF`ê膗’Â6]¨ÁçúÃõHÚvÃ*hóù€g6?»‘3$å¹Ð°ŠXrÓH4„†Ëy°c[•¶Øö°!ä§AU½&qQŠÍao…LñIîÚ–ñP ª„«%3f›.ª/a\¸UKLh­àg6?»‘y.ÔjIHäB òê»àÅ/³ ÛóEcïìZÙäåóf.U7šæ%Œªz@µ—‰MµE"–iuaÕzYÕ2‰>0â6Û,\8¹þÔ…¶8¹¥móÜÁ=¸}s;ŏ˕ŸÝ` Ï…Æ\$ïõš?¨ë»aa؆%m«E0ùhþrúú—Õ&#^¶¢Yl4]pz_:óä^ÅÜöa7—÷¿[B·ï!wF1QáÔ ³–G\3Ôt“ðâ‡t0# ã‰â¢£¨_Ï„eL¨¬ÅÒWlØN³>X@CO7^|zq<Õu²~ÛnêÒ¶…‡ñ´ê0— ×5~YHVu nXa¶éº‚zcÖŸ:“CnϧöéBWŸ; 8MºnÐûßþF¿¬ÞLùnµhCgžÒ‹.9ãÄb7þ'¯|ò3ïSOcn½˜zu®Þ-\5¯—ÖmÝM'öèàŸŒÚ¿.|vËs¡öKBR *E ¾ ’–ö‚gÿU‹ ÉTÑýB÷ßpeåæÓ¢?6Ò»ßüF§×™"#ÂèHv.M}åSjÙ4†F]wžyü{Ï!â·¯9ó¤žtVÿãhÓŽ½ô¿ï—Òq]ÚÒ€Þ]|Ÿÿöç_Ô¡u3Z¶n+ ‡À©ªÕ–53³>˜Oþy9µ ‹+NC(O xá XBdq™Á®þ«\C JÂb¾õì3Eõ§NÓá6Êω{®?ŸÚc1þ…+6Ñ'?üNƒúv%ÞÐßðçÊÛÍvÎí½ºÉ5[vÑÇ?,¯“‚dѳ۷Üå¹à_aäZÔaõ]d´Ö¨Ú0ÛUa'3üp§–®ÝJ;÷6µŽŸÿ´ÂÔ@ÄCûزiážÞ¬ð7¬¥ìÒ®…ù[µqýºjs@A’»ÅVmÞI£®=fÿo­ù+½øá¿ê¼Å«hí_»Èé°“[vlÓ칄m iî—‹iÇÞƒÄ{‹ßtñ`êÖ¾¥Ù=ž ÊuÃÑW‹WÒáÌp|ºêÜþdSUzþ¿ß™éúí¯ÉŽëÔû®óOz¨®YËd•ŲøÖ“z3üæ¢ÁGµ}»µ§,7ÛV L®Øø7Ú°ƒ.rÍýb1/à_ÜCêv¾k½ýÕ/”á„> ¾ÝÛÓÍ— ”¬Ú´³Ê¼6Ó q !P õæ!}Œ¼U«`âöhÄÝÍ‹Vl„6q9uF7p÷ö<„h뮦i ‘ÇH§©Ü!4̸ùõÛÉi·SŸníL’µ–ù §Ý½yŸíG\sŽyûÍ/QD¸ƒ¼ù"hDšÒ;ó~±¼ÑÁ#ÙôÖW‹è´ºÓUçô§Ÿ¡U] a–ÉS¡1aótÓýóÊ!Å~ªáÄzQTkYUCº%ÈÚ!P/ëËÖôýôÓïèíy‹Ñ¶º€ú#äv}RÏŽtB÷x&h´– u;oE=:¶‚Æ4ÊlãçžÚÛŠª.å¹P—JCÒ"*@ ¡’ÈrÅò—ü;ó–Ð{µeç>‚±OVw3køŽG75îÎ~ä™w‹™¹Å‘­Ú´“f¾ÿ={ö=òhš9βø¦Ï‰ù‚éÕÑ ÿäã;Càû› Ȳٰ}¯ÙÆc{vjM¹˜ØÃÂ`Nž›þ lÈÉÇQLdÞ¯±VƒÇeZædtÅŸ?¨™öNm›Ó:h:yŒfGt¡³á1’Õ·#Põ ;°ò'džL þÔŸ· ùcí_VŽ›N?±[ÀBÉÊÉÇxÅtâöÍã®ûtë@¿­ÙRì6Ôí¼IL5…ÉqqoÛ¢Iq\r"„€¨*úÞµ]#_±<Æ)åÞkÍnbÖäñ˜Â(¬‘Ø¿wgji r\,Ô]sÞ³›™'èè>C œ7ÅýŽË‡àÅÑ–Ð:ú›#Y¹wS³¸šñú—T“…HÖ†‚6‘ý-X¶Žù6µ,0²Ù°m™¶×?[X$¯Ñ¶s_Â*ŸÅZTË`½Mb-k-›)»ZΣD_qõº^$Þ}%åâÃŽ‡¿ð°‘o¾zwmW‚}æÉ9<,fÞ¢Uæp“L|¬ò$þHl`í¼DÞƒ¸¨×åDþĉhpJK3õ/‹üàÁ¯úµ,Àñ¬Gþ²ßš¾Ï$Y£·c <^Ó~0„» ˜½mÍô¶pr7–ï,OËÞ÷È/îºæú–ñ`yÖR² yñýh&„Xž~Áé' -Mgáa…Åø^Z¬mô5ÖBn-›¢r*'YËÉ‘èë0z]WxÞY§G~¿ŒÖoÝUJäöÌã¥-“Û(?/~_¿ÎÀ¤¼zÞÎ+Q­Ìg·Uæ•ð/^„€¨Mµ.]Ôfæƒ[Ó½fWq^A-[³Õ;áEÀ†úKðbànë+Ï9ÅÔ:î9lÐ%Üý†¥@úõìDW í_lÏ]RÿýúWs üü¥ë¨G‡VæK*ÌiƒV“'µ&Õ´¢hh¿\´’®v*–ФïÁø«coÉsX@f÷±˜î4¯‹ 'B@”"Þƒ­éŠíã¢Ãi3VdàÕ¸#bÑÊMfo¯¾àkö΄¿ýæ9nbk½Ï$«£·i‡^Œõ¦ö“?*£ ìŠB@„‚€’AP䱎©ÿù³¤mæEžayjŸB­a›qXî", ´„¦£;šµ ,˜uÂljž4¬Ùsðý½û ]†Yœ¾¦?fX³ ÉÚJž9Î3A¹{Ý«ë¦ðÈ3¶{tlM#¯Jobö'¯qdžľ£®ñ *à9¿Px½Ê÷°¤ÇóäÃ7™Âd@Çb)„€Ià×Õ[ˆ–sÛÅæ°“7¿øÅ+Ýšÿ8o 9áÆrÃÇß ,òD;ždãkxÒÛ[Xu—«Žv~Æ]ó’DŸ³¶;Ðý7ó^Î…€•&ÀÝ õÙ°J.¿Ø;Iü¤OÎù^[†'Ûäå{°¾¤Ó/Êtð¤È?ៗ™ãÝJûð,pAw\qt¶µµ«FDo$¼)œÐ£˜ÝóÁû:¶Ë§ÞœG+×nZ7÷ÙéÂõüâ—‰_~%Û„…˜FO ¸MƒÓhsëƒãž=©OÏÞµÙ¶ƒ-þäa.aX&¬2¦ºÛ9߬K½ü|X»yû²Wg¤\^ò\¨L¥?B – Ȭík,y½HžtSF‡ò0ºÔx6ö:Œ½Ú†î0ÿ®3 +*DrZ¹»‹Ç~ŠB ò¸7¢²B¤ku¶s¿Éi#„€Ê}6‡*v 'h'õêD{fÒ?¯ÄºY˜-…®³/yt«Æ ‡B@ÔIÒÎëd±H¢„€(‡€’åÀ©k·.|ñOŒ —€´ó†[¶’3!Ð TOlC$%yB@! „€(A@É8äB! „€B X"HKJÜ ! „€B@” ‚d r!„€B@!,$ƒ%%î„€B@! JA²¹B@! „€–€’Á’wB@! „€%È:’%pÈ… o[¸cÏ!Ú¾ç ö³?@ùnö´æ:K›A't£!~‹üÿüÇFZúç_¥ÃFÜU…Ïν‡Ènww×תªjªBsÓR'Ïö…=2)5Þ0ô›}í¬shBÞš•’4Ǻ棸?JCø”] C)PUenZrÂG‰ÉYeˆ YjâG:OÀ‹-EYµ™æ-ZM°­(›èÈpêÖ¾e…Ò^Ñ Å}ùx+ÌÇÐñ)¼QÄ}¹°„O!ìE†W.,¹жé ­AG¼9t~±w>’øIŸ޹íâŒ^¢:§ÞœG+×nZ7÷ÙéÂíüâ—‰«„¼ø‰¾ŠÛ4,›ã׿ÖÇ={RŸž½+Ò¶32séñ׿ Ã™9¦àxÞ >Ô­C+jå—œ×2~>¬Ý¼}Ù«3R®BRä¹PËå!ÑCƒ™˜ü £O¡éž/<‚# c$ƒã$®„€¨Gìv•NìѾå"÷ÏËh`Ÿ®"DÖ£ò“¤ Ú €a0 ´ß§ë†2/>!åŽÚHC}ŒSÉúXj’f! Ê%À]Ø7_2˜ŽëÒ¶\wrS!`PŰۛ #…¾7ÈxmdBÊëžË& ‚dÙläŽB@!Ј¼äº7»ýÌ+0†ò¿ÐO>>Ê•2¨e¿RYA²RØÄ“u‰€×«Óº­»êR’$-B@ÔS.×¹Z“g<ÊN]3Þxø©§x.†˜2ˆ Y±B þX°l=ûö7”›ï®?‰–” !Pg L?>ËPÕbØd¼ÃyWÔÙ„Ö„‰ Y A’ „@Õ,Z¹‰ztlM‘áaU H| ! ŠÌIžô£]qôÕ;´øH ”M@Ö‘ôc³ ¯Ù’N‡ŽäPFv.¹ <~.”Óé &1‘Ô<.šúvkOÚò*,b„@Ý$°5ýíÞŸAw\1¤J ”¶O$m¿JU¨^{ŽOKsD8ÐLó(q鱊fDC#ÇËqaxR1›YÓ½FžÃ¡ä*¤ä:)üÀS>|˜'¨ÔëŒ#ñ3SÆo8†“F»Ñ ’¼ë…Ç£Ñü¥ëhÁÒ5t$ÇMÜ.•< SŽ_W™Fa´Åˆ£|#‚>ùáwŠ‹£óHçÚ»PðsEŒ¨;¶ìÜg&æä^*”(ß¶ÿýÒõ”•“K˜­IšNùj89m|u݆ç]¸~€ìF¾Ùöc¢#é¬Á)m¿BU«N;íz1Z× ÒI=…H?ÙÐÕž˜TÒ™vìk•kPÉ^Jf¤à@IDATŒ=ö7O¡ÜX@9ØE(E‘rïÌ­p· òæFE5VØÃÕÅ/NšÄk‹i­ É/;_°öñí¯Aûè¦ÖöÍÔ#b%ŽÈ©ä7‚â/?‹F8íÕŽ£ù'Ñÿæ»!hÿI·^6„úvïP¾G¹+jÀ¡#Yf—vD¸3¨X}Ûþܯ~¥Lô<v´¦½‘½é Žšâ*œ†ìÈnx¨¹g/µvïDÛ_FßãCûöËKÛ¯§…>vÆŒ¨¬L÷mºA×z<‡ÏA6PÉuÒT§–匳(ŠOJê¿öv|HÙÉÀ—ÿÌ?¼3Uƒ?¯ðÃÑa¸ùgÓݭ½9-#ôœN#ߎ[T㥠©ëUÕxÏnSßzÑ5ic=E'É‚@£$Y€äYžßÿ¶†>ùñеí£!‘ŸQ ûö 5',Lwt¬4´Î´:÷JzáÝïèÚóO¥a§õm< $§ušÀA CiÜŽ5%Ûþ ʱÅÐÆè3鈽EÎcM'Ž…é½Îæ/N;@½rÿDÛÿm ´ýš.Œ*Äçrê.OêØ#î"#6×ë=hoeËp´¤,[ò(ÎPÉ6Ña…hïŠõfPmßqM<Ñæ’ µœks„åz¤°û (Oã<&3ÏÓ[÷PotvQHo hSÅPšBx ÃZŽ˜9íÁR<f3ÃØ©¨ÊN¯¡ï ³;×¾à_#K5ð@^¯Í˜“ï+ß®LýåuÑ~gcGXqÕZÙù$õ…‡õP†GiíÞN]Ün6<§r¥^khú©:)ŠzÎáÌ‚vÅì 5¿ÎahªCÞSQ {Ì ïІýÓíFb' —æKÇí) »Rö£/þ7¼ˆ–â7/mÊÄߪcÌ&Ý©JᎾÊÓ+'Å i½Hx<äêÍ;賟 …ÈS#ß-†QÕ“ˆØŽÔþøkÍ`²m¤;‘æ>RÕ`Mÿ-:žEm»šV÷¯„W™@XØ6yåÞ@~M›Ç™]]"LV†¦ø k %+Ï”nû«ˆ…È5Qåû++ÌAwŸnÞÎt´î€—¦-Ì¥ 8VÕtŽS)Ì®ÐÆƒUËJË™ôä…‘ôÙÆJý™·¸¯¸aa›yõÍ!´ýåÒö+ްÂ>X`ÜCSû`ŠË`ˆS§cc_lÓ=)-˜)Ž`<:ŽÕ”íqSŽ’¢§¯R0è׈Þy2ívv®•/~„Âá=舣…­Ö½¼ZÍ èAÚúûljß{8tÑó´ø¿—QÖÁõ• ÐÇWöá-´oë7>6µwÊÜìÜ–ôÖ É5êZ nlZí¥XbnÌ|Ûþ{ß.7»³YY³7Û {¾Ì¢îMltÏ©áôà ýevU‚4ýŽF8;2t’•øü0¢8€ÉCZh&Ö2·Èì,zó‹_hʨ«¥íû¯âµËµÀ¾[[8 ÕÍéZÊ? üæ ÑíͶǩn%ÜËèV"È£òs:IˆZ†Q(kAk‡q‹˜,U :õ‚è{ ‘ULUÕ½s7ú–ˆÌ€£[=G­”fTGXžEyj! G)|õ˜]êMµ}Ô³§Esmïm6¯çÎIS·Ä'¥¼@QöWgO˜NÕQ4È… É/îÎvÐX¸øHnÆD~^-ÝÙ>¹6.žfV–þ—¿JúÞDë~z”lŽHS[Ù¬ý´gó´wËWhüPÞ—aß÷Üi´}å«ÔµÿH:²c¹ H6i{*ü}MÍ;œAÑÍ#›-œš¶?íü…¶¯zt¯›š¶Dí ˆímhy ]3|H+/k&O ûŒfßeÎv¿èŒIU‹?C—&ªBÀ¿ígåæaLdÿ*wg{0kaÝ~¯ù‹ W(ñìHŠr*”S`P·¦6š|N$ÐÊF{³uzñ·|úr3†xÁ̸0ŠcjìTh0Ù\Ñ+ŒFC½ä­#tÏÀpº¬§“rÆÕ½”òS.ý°ÍCWï4ï5‹Piáß¹~Ì¥#ù:×ÕA×ö£w׸)á¬Z°M£ÇŠÂ5Ç¿ÍmôÏO²iò١٘ƒ5“#N„0¹HÚ¾9GžI­i§k‹€ðØBSìÞýŽv6º²!„Apª”à‚¤…,ˆaÝC–@ܾ½ üƒÖÒÞ“NíÜ[»Åy=mdiÆ'&'¶³IãküýÊuÕ 4ø·?wkñÄš‘yyùôëŸ[ÌÙÙÕ=±ÆîŒ¦¨¦Ý(ëÀ:³”ú]ð,ŶêG¯~º¼Úô¸¬\û–]Χþ—¿Lù9{èðîe ;wo³áóÞg»(,º ¥¯{×6[u½€ÕA§\’F{!¨nYò ⸔¶ý1ÇôêÌg¹/X¶Æä˜źDÀ¿í/þs›9;;”k"0÷up;¥gê¦iÇ5íòhŠP9y~.­Ø£Ñ“EQßV…ßì- 6<ÚË Jû˜Âëäôí_4á»Z¹W£VQ*M;?ŠÞ_S@÷AãÙ«¹J7ô)TÃD  ü t[»†FÒ¼-úbS¡°ê[ã¿Í¡?÷idç•Cd˜Ïrÿ~ÙziûUdÊÝ×#S,ðÞnú”Ž6ÍWG ¦ÅqÛ¸Kš#Ö¾‰ žk-÷a¢ØŠ˜³”å1ç&ÅáõôÂNÏ¢Õ£Y¨ >8q/Hj$4Sù׎½”¯QÌD®.£ÚÃéìÛ~¢¡wþF9‡6Ѯ۵ì|¹s÷QlË>÷R›žW”io¥m÷ÆOiÓ/3Š…QËž^O>­ÿy ´›_Ñ ©YûÓÉÙÜüeXcv§³†Ródùz é9sÌÌ) méûÍ¥”B¸&‚$°aÛn⟿ñoû¼}â^Gh–®j­Ò7ÇÒ’»›P¿Övzä „ÐÖNbUš¾(×ÔB&@˜äq”ÿ€VñXf=ÆX@Ó¹+Ë e»4: ¡òœÎvÚ ­æ×ÙþÇ¿=tA÷£Ë…C>}rq=ýK­†àYžÁVoåÝ®Ð=æÈĤíW[ Ç÷OÚ2]Kýš ý™ G«&,ô¬‰¤r´‚R²Á¿šK°¨® Öæ®Š¬¬Ž>ÇeöôjÆ’‘‰S¯©®øk¸ ºk»X#áñP>^"¶ïÁÄ.Ã\'²º \÷Ð_¤ìC›ñ|(œ Í! “†^ø ?¸ãGrç gx“€öVÚ2÷›ã’­ËÇÜ#ÛÍ®q¶Ôµ|h#±tzöh<ߤ!7bÏᬿió’§a··„¿P^ðz›ÌsÍ_;©s»f÷¶L¼ %a +_.,ü0<®KÛbçÚ>¯…ÇëD†ÂdA8|c¥›.ëåÀ¶ ­(âZCƒÈfýÁ£ x2N;žŒíšÂ3::©e¤J )öî¯ýÿq{ͯ8arOiûÅÅR¡“x×ÔãórôùãØzcd?Úã츂T(Tq\CöÖ´gM"/‰.iˇ®êòLvN{ ‘¼àÍ²ç ¸C7Á»¼¤”¸g.ÑžÒöK` ê‚5‘¹¹Æ7^ÅÙjeÌ*&žåïXŽø(ÜÑFÛ9iÇ uÓ‹úXvåøö¶8sLí÷[=Ô «=6,’z·°Ó¤ïsè«¢1½Çг²÷yHÆå½œf\• £¢þx±õ•QƒÕþÙ õHï‘wïs=Þ'Øu(g§&ެh|É}ùO±zN‚»¶XÀ)À~Ùùn7eåBðQË~ Wgv¹:®M:ïÿ–Ñù#VR›î—˜Ñ•e_¹´(è>ßO'_2‹.¼gº×5ÇIV.¬à|ñ6’¼'9sfÞb„@] ¨íçãEj³ 3¬ç®rÓ#gDRl˜Jë°lÏþîêF¼”Ïm'…Q+h#ÄøG6ËÒ5êîïÍltZ{;Õ©d—÷_‡½è*·™w¢ ͇?î*¿áD⺴“Ü}^ o#)m¿â%‘—k|„ŵۯŽl •É©àú‡Daµ ìW½)–åêôß?Ý?਀ûΟæ ëe#š¯üß:7=ókÝZM‰å™‰^Y‘OËw—?²:òé&o–'mߟJù×ñ‰S¯ÆjgnÀ2JX¼|Ǽ;¬›ƒ^EÝxšr_à Wç¤Aí¦È‚¢×çQ}j;]y¼C¥ˆ¦žIã¾-\QÀ ãâîNº°Gá}þxRëÔÏŪ¼êÀ{<ôñzÆëtk¿pÚtPƒfÔAM°šoÕÍ“Ç~ÝYX_:=ë®jXuÀÀ•¾û ûììÎHÒ˜ Mÿ¬enÚ™é5'¨ÝØ×I‡àöL6Û»P˜|5ëZ6§XÏ¡«^j(Âlìa4XA’»¶¬î-MÓÌYÛür©m¬×S²‘Z°,{ë~0Çü¬t¬_yµÅDgX‘7ІÅãµJn,Îsë+·JŠg!PIV=d-¹oÛ¯dp¥¼ñäþY†ÇKþO†uI3üyèÉÀªxôßÕ%-¬MŸŽÅìG}žCøÃˆ kÚ/ëF3~É¥N3—¼rýK'B›>ËXÍßV@_á£iH'Ýrb˜)Hrýe­éÕïfÒI˜ vFf’G·õ £›á†—Éâ%´çét\ ½|E4Çê=¡½OÃù¥X"+TÆÜA\1B3Î%T‰ªÇáÔ¶\U­è¬î­Â ¾f ¦ªÖÔlà»7}†5#'`OSÊÇ ñ¥ŸÜFûþú¦Úa½L¤k»ÚQKA¨ mŸ×”ô"}“n ‘¾vÖ9ûáu"ýÍ!¬?i ‘þ÷jëZÚ~ðäã]i¥ôSö:Û«¦J/x¯A¹œ¹,ßܹhôÀúê–8SãYýgA˜ÛºÓ»¥Jû0ìâh} kúb…€½9^úËU€¥ª.ÄÊücÍ!ÿî8)œÁÍ¢…šuÖ~îÌöRŸ–6hÖ ºZKÖ,²ù+ÃK‰Ð¦³ÛÖ»±ò€ƒ¢! ^Ás –¢Úä·kÓ°nNz Bî§ èhèyü1‡ÏKjuÁ&<®¸†‰°p åÍÄŽ8‡ ÔAO¶á½¶G$M;5ñ7Ä0¼F’eG^GR‡v¢¬‡zC*X^s’5jLí/Ëé…Z`ÑHÖ(ýFY¬àk¬zØØÚ¾/ƒ;—¶4jÕ{è8(“ÕÛÑøA{Ò!O’áaÏ`íÒ1§GÒä²Í-7yÜ$î@ð+Ï @—óiègó –ºä­LS“8ZÁ÷ éœ¾0xù«=Ù…êÉô,/ÍÄð"9ÂâÑ.hÞês~,^ÚÓAÿÄ4Ãa±0êkÚ`õîgÃIçå´²¡U­ª ÷æPßÜ¥”Mbì)Á†'{m—OªÁ ’œíâ DHÖPðµ˜Ð`ªëЇ.! ò ÚkÛª¬”¶_>¿ªÜ•¶<=U÷š»±;Œê™dÂ{ªÿ†õGU,É–‹-1b .oåù´Œ˜@ob¹*6¾ âJýëpÇ?6¬IŒÀ˧1N—»¿¹š»£¿D·5w9¿¿6—ò–,ø•ñzåÕ xǦã±ËÒ(Ÿ.u3üûÝà—à>/Èà±41f£[ü<Íѵ¨>Ob«¬á®ìÖ;¨GÞj¯jè‡TU¹júøñÁ/²,{m—‹¾A ’œóâJ ‘Øo»u· ÈŽ1ŠGöüN<‡'¾TÅ8#[`Çš™´ìÓ;°yà1–U ?~EHE #”jºí‡2íõ),iûÁ•V+GBzºgꑦž}qÕ±ÿõƒ§…cç#y oñ‚öO¬({û8´y#ûGÐOwÆ‘ƒÙžY’oNò &Õí±bÀlìÔÄBbºœŸYR&·¼ûÂÏC:Ïþ^¸£ Ô$+üÏ7zh™ôõ_Œñ--mòªbÎÿDXè½þ~ÆTz ½tÓ[×Äiæáļq¤Â½Š Þûm=Û©£{³m$†¨¿(‘4|v¤ÝVúäXu ^¬:¢àC°9£èÌ›æ™{ik9˜E})HB`—š;“vmøBäÑAù]Š­B@ÔE.—¢Ç'¦>ÝÒ³ûÑXï!%ÓÖ,¤ÉþAÙÐ¿Ì äó^ð–Y¹ÇK£±½&ÏÞö@3iÝúúÑ b¼•f ³:Ôs^;Rbr»c-áä9ül¿i˜ZI¶Ïæî7ëh\ìnóÇçÛ¡éó5&ó 7]óMûóxùƓ̎æ‡ýkÚl£žy«Ø9ÄQe?ƨ~Osº¼’ÁB Â]ƒ$kã+¹y‡3È[[¼/ÿØV'R§o£‚¼C´sÍ;Ä;Ó„Eµ¢Î'ßE1Í{c'œ X¬üeV¡–²3t/5ïxv±0Ý–:ôN‘ØË›—:´ó3ª¾çN£=˜pÓá„›±¹›vü9 žÿᛌj?¯ îÕž)‰ ^¨Ž:xi'õŌԵÿÅ]pÖd^Óñ|Œÿ♯¼tO†áIÛÙ‰»7Òé‡í_„¼tJßV6Z¥|敳vßð¾a´õ°NKw•ìõ·gAálL¬è Ëw{hñtiè äîÊ›1Óöst3îÊ à ’¥]Ü+™”ºë-ÚöŒ‘ã½ùÄì%ÝWFŸaËñ@¬9äÉ)ŒUgÝ;–]Y~¹¾³p Ã]äx– ª²B$´'¬“¹ A¬v˜b½Í£õÃIŠÇûè݉©?ÛI™>+eÒç¥"‹ ¨“‚ä—ÓÕT<”Æ"7%Wë-7{©Døè™÷|¡#^Û±ÀyùD™è >ŒÑÙ±ÿGM¢Ë ¤J7¥/!Õæ4ßôë”sx³^L‹ÞtêUoѪo¤èfÇÑ€+^§Ÿç¥î§>HΈæ´å·§)®õÉäÉË ãÏš\ÊŽ·XlÓãRúsþ8|T¹éÌçÑÎõïÓí?P¿ žÆžÛɦ´e—ó±çö`lø5ëx&8ìIÄs^•ò¬g~‰È‹$XZ ÃÝÔ4´SíÔ¨H;­LÞg˜«¿>öf`¿ùù]Íáá[Í#·} /¥|(ðó hÉÂÊ7Šãÿˆ"à¶ñÂiÖÌûa›Fÿ<9ŒÎîâ05/ýÚØèÕ«£é£u,qâ0÷Ü‹=·G ˆ îر†…ÎÛú9é<ø}øëì±Ü‰Y°£±Tˇë (k÷ÅbÍ=ÿõÿXóÄQX“ÏN³±¶ž%H–eÏnwb¦ë!,Ÿ’<4ŠÒ~Ï7—|ñ˜Böâ(̆µa윷ʂ䟘…{ö«¤†yHu`銟 ™ÓˆZ6°×ŒáéòVOÿ€t‡EçÑIÙ‹¨ÖOôwxOó§cMN1¡'À\÷9;˜?„®Ú1>µ•g'µÍß~F´~ä³øÄ”WcãÂîâ‘G«dCŸ¤b$+.DÖ²á.èŸß>Âà£4ä–oiûÊ×!乨u÷‹)/sE5é aK£È&‰…ËÜ#[Í53÷­¤í«ßÀXJw@;ßÜ5ï0ïmƒ6,L1­£šv§6X7rÏæ/Ìë]ëÞ§Ý›>¥#ûWQǾ7A(ÅVŒÐ‚Š¡&P3Bä±S‘9ÔtÔ¦H<¶Š¹xå|šƒµYW²|·f]Cã)‡®9>Œ>ÛP@bý<Þ}æk,@žús®¹¹ÃNÌhtf¤uY|¼Á”Ÿréøç™­¼s¿ yÍñNZ AÍ_«X–=w_Z¦&'œ A÷íÕ–Má‘?ê—<šv~TÉrUcÜj-9‡ºç¯¥Îù¨½û/ÚïhOûí(ÓÖ„¼!ÚÆ³Æ2T"â­=w9»Ò.GW[{Ï_Ô=wííî®9ÎÃj#¥Õ¡VÞd¯m‹DÀc$‘Ò h"æ«Ö,yÿìU_?@;V½I§]÷!íÞø1qW4wO›{qoX˜Lš;‹¶ýñ2eí_K=NK]NI¿¼we@;ßÌDÆBUÕ2žüÃf¹u±Å<Õ5¨baµ®±™<ùWŸ T»&2H8†µðHî+èÌWˆ]@Ví-\ö¤#„GkÇÖz1.×»;œç¥þmìæVˆ¼FÞÓE|£e¿[‹öAÞrXƒ†°ô®;ÖÌÙY—•ìF)ËžÃçµÿú´´cÁgýûÇÒcª­]KìÖ¢¾‰’ó#ÀÂâÆˆ“ Ôt¦vîí…Z2Œçc“o‹¢aÝMÇ´¡'€GFº³aÿm[ŸœeCã'§ÞƒH^*+"Ùk»,2…öu^ʸl|¹o îà'pì$~Ò§Gçcn»ØÌ/Bž‡~í̬l:xè0íݳŸŽ¬ÜNTPØÝ\˜ýÐþi~Áùz úœ×ÿëƒq—¼IL |ŽóâÕO_G±1Ññÿì]|eú~gf[zB‡ÐDŠTEEÅ^@9=§œžçYN{ù+Ip…„¢žõîÔ;Û‰½cG© RD&  @©Û¦üŸw6³™l²›¶¡„ù~¿Ù)_g§<ó~oq‘$5lŠ–ÿ»u›_þÒÙWâ$ù ™Ù/9vÒ œêØÌ)ª½ ¡NÕËçß0 ¸bz¦, .³mV•Ò?¡áˆD2¦¶ÀÆŽx"y4ažÜ¶?õŠÈ2ñ­0E½6.™Nþò}p[H;Kgüé¨9à`U¦ï^>‹zœz\ Ù_B¥û7 ÍWÔÿÂi5ŽÅ%wÁPò¸î»,:ù²cš\¥=¿}J;7¼Ê·6,,bÀié6ʾ Æbúø·AžSX®Qj\P'­W9¼GáÄ‘:x9ÓËoü)ñ†!?x,áäÈ5iƒÈœ‰×¼ßTÉýNÁt9§ÑE—yn<]özÕì…žaýQŒÎÉé¨yÔçðß_ÊWOÚíì.ñlc«Y\ÙÇ Çä~=\'·“ì¡Çë€ãðàLUcÛnJ½‘}ı³Ùº¼®nHVWù¦ä—‹Éb¢RÒ§)mëu-"Ã+ ý;Ä‹d«îª„oóÒè‹Í™ššþåëq´öÛ‰BŠ!_“µc ïyÏw tûÏÿ%^ذÇì£rþK§…ÊxËöT«ʰ6,,„ÀLg?…xÃYëiEwÒÌ>õ8J;hž‹°n—¡ò~8€f’É¡Ü6ê®§‰ú¶±éŽ¡"Ì\ûDº§HôC¾Œº2]‹—êúB™FõãXÄAr:1ˆ9´âôÓÄÎãÒ$=üœRÑ0ª1Úhl› éß*ÛpÆLʾ\­€Râ·¹zÓNçñ’*4]ÉDònr±šÃHĵæ”nÝ¿uÉg.IÔc¿o®üXjøYE®ÁÚu©TD2$‹ÜjÓsTøÒ´µMoéØmÁ"’ÍðßGó÷hÖoä®5UÖ•øÍèí˜9ߨ6“H㘵¶°ˆ- £Öñ"½xe•®â˜ËGØOgwµÑç7&ë~ô&|]®¿´o†E6»áaçÊ¿!îp6 pØMÊ•½]ô§~ºäÕÝàetÞ’JÛPæÎO‚†2÷‚ì†+¡Iß%‹õ=&’3`@Ãáæ8²Éê…þ£Nm³¾}[åŽÀ謜ÛTEË+•RÔu §J^±¦AVÃ[­ªÁ\,!¿ù$ýÆbá‰?jþÜßEízž~‚ ª?ÃÍ»²bk÷É´m´‚´ü¥Õ^èþJ¹GsÐÇ˾?|}³còþPoøS_Ô<ˆÞüÅK[BäŽä>?žØÙø­ƒ]´zÅì(ÝHÜÿ¸3ãa˜æ Iø9/’!™Q/Öë$¥˜åÖ=ù2ZÛk[Q$mΔ Ë¢•;Vó,"y¬þóÖy[´ÂcmÇú´þüNäéá{>+×õ Y'ÑHL(9±~¤ùø¸ãáénNùðßxÅÿJj8z¾í±ÑŽ9­$™æc¼m>ÎýŒœ[¢ë^ ˆzl8ærµµyr^gYé0 p{Fö™šªÍ.´·§uñ§A Y]‡6CâYÍáé‹n°zd5– ¾ÿçH½ºáX."×\ ˆu¢óÉÇýôë~™Ò“$ºÿ §N$/:Î/6ÑÓ®Éëàu€I$Kßÿƒ,¡è„zÇû×%ÓEÿ-Ñ¥óçwsÐÐÎ6úaW Lã@NNiq½te2½ §ä†šˆžŸh†dF™X­áœœzW¬R4Q,HJs֮͌k;:º·÷è¬\   #޵][¼íC5f3Y4÷~œéa9Çz3¥pGÏ>ÄH6[‰›ŠÖk“ëšI$Wjj›õêØ*ToÀã¦ù¤8í×øS…æ ‘<ÖÙýŽónPæ9ñÕf½ØY>F ’kö2°¯BÓ âú}Au –FþŠí%SÈ1®Yyn7;½¼ÚCçÀÉ~ ¤”lL¶m_r¼}ÈúÒ‹«¼pW¥àX•~ç'ˆÉý,H&GÈáÄW.$ñ‹¶ûé_µHJõB‡âé[¾‚•bQÔ;Ÿ~ðÁš.ÌãàXÛ¼X©Vp9YÉBÀBÀBÀBÀBÀB ¹p»ç:@HÎÞcïbSb i¼†3ga9©àyp¢o¤ ‡æ•70½0<±®î*HωìâÈ‘›Ú¡îÅ=lºïS“ØD´åŒÄºÀìÃÔHë*É©±ßípOƒ;°êGì%±F?ÑÖ"LãûxVRÛ@>«Ü—;9óƒhå­¼º¨úÇë.{Ô•€ƒQÝNp º ½.*Hüer’Ã&…p>"i ê˜C ¶{_‚·+Åð´îý†áYØj Á“N5¬ncJ”iôüºgˆ3TýkH*ã [còøßÕ>z SÌë¡)C¥¢ ¤°-ô´à÷ô!´:”,Ùf)å ƒ\º4’ˬÄqž¾fÇ.ÉÚQ¢/£„ûdIçï–Âs‰FãÏD$¤Cœš—N*_¨vðog#×ÌÜì¬çñZdwUWL ;=~‘„'§$’WM ?Ó}A´S¯¡ã¨"И“Í™Bú\M½Ïš G³±»RÍÙ¡í6ÝΧô® íóFGD®ésvuì}%,Â+•²aéÝ*ý êuæx½]¶O]ý¸½æL^5™â]Uëjý9û·Ú¶G ¶kï}øç /jí7§æ³îýâ÷ü½÷ú [ÜÁ·¦‡æÃ†ÉâJ#±¤‘¹ÆŸGßÝœB_ߘJÝ Yä¯Bïñ„М{M’^œ§¯û´Ncóž*çý… ˜œÞ@ù¥ —Ÿ\Ÿ¢£±Žc¸R/húaièƒ_TÐUˆ u t.UjçßI§”Ì—“”"/æõ¯ÉËÎÈ9T}·ô~Ž©9ÿ8˜™í¡xòk.rÍóRißc8uø7âP†Kߺ:tý ¾4Wß>¸guîÿ*GxÄ€ÁÁCI}ˆŽC„.“¿>èòøSî¢v±¸oëWÔý¤Û©m÷ éç/§ÞC ­{SÉÞ5ÔíÄ[¨ÝqÓªyìœ?˜86w¿s'ÓÖUÿÒcrÇc¹fñ*¡äøª¯ÝX¶oµe!P6lÛ­ëÓ½cÄâ|ïÛáëšcí6ÅG_Ä*3εvëxÀ¢ÛH§t´éÖ°{`ÍÓƒlÁž.ïí„õ«D« á™E¢S—½DXêþ†oò79ŽvøÍûŒ£ ÒäøvæÃÖv=$ħ¾T¶D\“8T…ؾ†9ÚÒˆW‹C#aÛ­«àÈœ>„A /ìn‡‰¥aßÅúŒ¹Ë=0ø b˜öYe˜Åž x1_ÉY ¥“E‰%šæ+ûüÿ˜ßoDoCòÉ §% :§z¾žQùc6$3oÌv‚RB=½¿¨©}"B½­mâ͹î‰ëÓ–U§vZ¬DÒ8]óW«;ÜE T 7ŸïÑN'\K[~šII­û"¦vw}6Gˆå™´îÛ ´iÉã´ìýëA1†¨¯SÚ¢¤V}h˲ê’ö-+sõЉ›—=KçP‡ãÿ —çýÕó+fëKZÇSBí9ÚRï3³òÏCÇšcƒqd<»uHÓ§¶›£«M ºøtájâ%<…ßûü–k(/“}~íÞ÷(3/M «!i1Ò 8$éªDJ°³Ch'b\×tór ÜM:te#ŸÉbxâ>[×fžG§Ã9zm©'|G¾ Iƒ-n›3é8OëÞo8ʳݙ?Š’vm²R¤ž\ú’"6¼‘Õ(õU‘H£I6 7Ø2ò"­¹¼™DF*w(§Èh`ùRõÔÒùZj °“”cÓmÏh‰äXÛV¼íˆ_‹&’¡ Üaˆ¢HÉqPðUÚ81" MÉpÄ·¡¶ÝÎ¥]Þ£½[¿ MQË›X¸cxÉLjÓõ¼Z»(.XM+>¹”ÊÙF!ö)i„=lÕét:X°ÊÈÒÃ$ö8ù=N÷FT# ¼ðI8? Nу¾éŒã±^3Žq‘Ú¥&Z:’±×j¯Þ@Ý 4Õ‹×vïKð{Ò>°³zÁí €#ñ^ r³—%.F³#A*?Úà§Gç—Óß?,£?ôtèîOŒ|^_ b6\µL_XASxtÂiÎçmÿނß#O‡Ž>Å©û§d Ss'ÆÑé°[÷~#†ÇG6Q¼À¥•o?©l‘vBÅ bÉ™•š†€ Þ»z7Ñ©¥ßÊ'•- ´À¾"< ²l‰âqyÙY¹n·`òfYÿ¾8Ö¶o;2^-–H†^$HЖ×í •,{Ò~¹[dT™“Þûj*;°}9é bb§÷­Òu\þá —_ ´á:íê7Éæ¨rn\ŸîÚB×±ëÀ›hÝüŒPqÉØªëÙ$ÙãÖ”ttpdíÚø>Ý…ÊÆzƒñcOèÜFÇ•»20u_V{ AÀ¸ùš4ßûl­š‰dм¿!ÍÕ«,;\¾á½0H0§.ˆ2²­ÒAó΄A„ä†]®˜—ÙZ̶¬p`^$S÷Ôêù|œuÏþù#«vñ^íi"ï|½SÎì(°ãÇ8öëœfÝûMÀyÖ”‰ ’ZÅ÷‡÷ã æ ?4¨|±ÖÖ¿‹$-x=4¡ù_U€ ”‰c»À.êéYCCJ¿‘O/ùJ;Îû«– ”þÈȤÖq]XrÖ„ Uóó-™C‚µÏ‘úq4K,…”t)‘ –Å6›:%Ùó6@?{/§srÉ&•†c1€N}¯¥¸ätzí‡zsvWŠ>¥]¸s±ÁfóÏÐöµ¯Ñ™£>¢®n¤-+fÕ«[6ªpÑ“ôÓÇ·€¨n Õùýçÿ/©Ó×¾¢ú pö$:¿” †R1ÝÍÑoJ1®ËPͦmÈš]Ç/Î.P¿nmC3ÞV²8ˆtïÀeI¯Š5´"é\ŠE8ººÎÕ…{ÄoÒ‰äèñah$öçÇ‹¿RÐÈù°E&Œ¦jFñþApzá¥í´Û¬{?ÿF¥ÿÂGFOŸ>M+•oO ì¿R´tMÕ"©­¸ßÞŠím¨Bl˜à¡ÞCÃwOŠRW8» ö±G>`ooÛ7¨ÞÕ›«`*>V”ÿ *d×|bœè¡ü‹±²q¶x‡áœâT+ðÉÝ*$Ärl/ÄGä×vM›;3'ó÷æ›ÕnMZ,‘4¤L$™@:,v, “ µ­´-­ôŒ¤!ñoÖD¥G’Úö£ÄÖ½èÛO%EPçåÄKž‡Tr•m"WB{èE"œ'n UöÃØfK½zayÒˆhõ÷RyÑod&éÇDÚ”î_¯·Á’Iù~ x iíü ¡vã’;“ éd}û U¬cƒq+QÛÑÐãÛPœË©ãË8˜×QÝʶhVŒë°¶{¿sŒJK¨/¦×& iÖqpã…奯?°Øo‡­+ôTI-Ù‚µØ«Rœ<ƒ:êkÞ?I$ŸOߊ•úìÉ=ÚZ÷>£”÷È#ló¤Û­=U L;K&õª4eߟZÉÝ`#†+îÛ[‰©(xÄ$òˆ ä•`8*ÔÔ§4$­8/%ª%ìˆËAJ)vòK c~0²5|[Oñ .ÚîìÍ¬í°¤6 µ}<«e|´liü¤±^›1 cò‰¤Â…º°‹DúMDXzHƒÖ\Ô¯×ÊQ£FYbÜÃòácø0õËnù©Ìß*UOçÊÖE|¯Ø@p˜@:èô`q:)-ÞK5åûúUü™ǽÛdÉdú £¨pû‚‰ä!ìÚð.HàlH _Ö%„:±óSÁ¶¯¨ ž†0}ΜH¬{9äª×+ÏŠè§n¢p Ô¶ë˜F·SYÑfZ÷}ô+=´ký»¡r­»œM~Ï*.ø9t¬),‰d™/÷§^í¨KÛWÆ—qf¼£¤Êÿ‰ÿ+}‰RÔʲhuÝûäÛEý˗ѯñ'7«drÑŽÝ8ÈIsñÑ¥½zè8 Ç~÷º#bÈù2-ÚØÅýðå'Ó(Ä0^º3ø>½KÖwÜRùýXŸ2MC2X›%‘L¾ù%߈{?C8&Ú¨Ôá[€“ååÿF»§ž ÈÊPA“Ïh%ïÚ*°·Ÿ!K*M”ÙÕ€hÑ)Éx“ñãÎ*Á¹°àÇ®Kï¼Ü …êá}ÉÕäÿ â¼ÄdÇ'§uëæýêçs05|K[y—ºÓÑSÜëè„V‚BÍù0±mu‰þJœZ&aä?ºìökÿé¿«>ýæÖ§PÊX±¶£ƒw´ÉyT4¥ÂÐã…ØŒ1½ÅDÇéŠsQÇEí⤪~Êô£ÒŠ64Èù1µ±ý­(¹ë¸käîÛö-}9»~|Á«ç“]F5̘&¼ÒVXiób¤%o]alV[sÛœ¢µ¹æË«ÕiÊëD²:K"»§9¨w§TOÆ•ñeéOmSÛ^@“e0Üš)ôßÕ̲ŽXÔÚbm×çÞ'¢“ËJ‰§òŠmmêßaJ²«”³»Úèó“É-ŽÃÍÒÆ+áîçOpô|É«%ºÌ,„[xK*ô)º³2¶ö½§¹h7\±Ï¿H©>e"Õ­ïqÖ‰du€H²rï×·}«\dòÜyÚ‰——¸Ô=Ï=ç”÷zºË¢ÜCPÕ¢ vU_+§âKÑ”òd0ÇTöxŽ5®6¼à4•&j‚° w ¢° ã]Éñ«#„¼ulVÎ<èºO¨ø©/"ÀèSì1½^.%S¹˜ ‘`ã‰Kp½F—ZÉv)%)ŦÖI €‹€Ü"²£ ‰Oå=6ñSÌ,1ï+Ö6_}‘ÓÑN$Cg¦”½‹1„«Ñ8ÈS\Lpìv»N âÉçõ‘Ï磀Œ8¢J9|Ëù¨PnC +þNím›©³}5ÖšÅÏd]$ÒwCÖÍѦÑ?û‰d?l͆5X¼÷nm£®í’)X2žLÌ_cjÛ¨k¬–”kŸÏRt6±Ö1G ¶8Ûõ»÷½„°Â«Y*²µ§Gg*´·o’ŸIŽ7Ì‹‘X?òžÏÊÉ…'­9ööœ^=>1—Ë/UéŠÿ•P<ô)ͱ·ïF=6Î1§pÿzµ•99¯é·û‰d?íý;)M.Ð xz4ðÞ7ÛÚŽ ºCsHÑ/Í’fOɘ«iÚ[cz¾¨ªW¦Éû.Ç{£3ú›˜RWýb¼i¨¨ˆ6Rà “e †û–†Jù(®#I•5'y‡âG*©(^ϪJÂÔZ ’»€ì®OòÜé–py“3ŒîŽŒµg;êÿÐRˆ$“•]¥ŸP¢½=NÁ—‰HŽ%&$€DúÉ€SbºŠ¸Œ (q8VA(Ó 9 _Mb1æþ«œ¯ê?F’#±³qþ¸Å¤ µu*Ô9ÕI)ÉI””¿x‰ :žŒ+ãËÖ±Œ·9ñÁÿ‰§ œÁél~+V3š+XÛ1B þ÷¾G¿÷E%&Lü. àîg© ¿µÄÑV8`óÊÚ*­qïk¸µ´óÅýÃÏéo+—ûï˜6-M+—«‚0”±‹¤y»Ä)ÞŽ(”‚K†ÃªÁ<‘$ûâ‚D›eˆ–b]Œã{Qf72°ÐVÉ.lp%ÇmŠ E+M´„'%_ðZÁÎí “[·¾oÍæ|:}@èãIŸnecÖd Z$RQ Éñ·Ù²ÛK6€Z)>ò@BáQñ-}@¯ÚV'RGÓÚ”±<é  Ï‘xQ¦DD°JrIºä‘‰xJJ%ƒL&3™žŒ+ã[Û´6ÿø²vlÚÀÎ/™µëÿUå++Y4|MÖÿÞ¯0Ýû~DéÀ;0hÚ|<‚Z6ßûî}¸‡5ÝûqúGcCîý#èÔ¬¡ÄJ÷9ß )^¬d!P £H†ˆÉüß]ÓsÐI…ËÖni"…$ <(žI¤á¼8ø¢ÁÔ· ¤ÑçÉ„,N–) ÓI,­ ªÖ±°Ãx1.º±¢n>\NHcœ.‰d™š’¢oó´6[Ã×&d¬ð_hª¢”¬Y¶d3vY¹›—Іm+Y4Ö½ß0hcyï7¬g«´…€…ÀÑŒÀÑN${–té$eÿî]oü"woÚ¾‡zuíú_ ÂèÔ¤%É!³^O˲%·Çã êN‚L*ì¢DR­ÔO:%¿D81d¼$‘uK!É6LuHLg³$RŸÚI#k7²áÿà—Í;…í›7}ŽfYᛃLòf% ˜ -Ö¶uï× q¬ïýº{´JXX´$Žv"iH·˜ Èßðî#ÇÜuýó–¦=rËå¢Ã^åí€_(LŒ8_Þl$¢»‚žŸDÒËF8 ’°2†ä’‰ä±%•4pa #O ¥‘AkwžÆæ©íøø8}á)mÎg\Ó? þU•å’EŸ~øòYÑ”ƒLÿ[xUkßB Áq¶ûtïXk]ëÞ¯–jcuïWkÔÚ±h)Xq¶£þ“G;‘ä“céÿ{K7¬úéqáä!ÓþûÑBí¶‘çU›â6^(úC¸ ÄÍAñqq:‰Ô qüÐbCœJ=JCÙ’g¹+…‘!‚Íá$9O[3Ñf?,•dÃ&|œóOÆ2<1ö»ö¤‹æÿ·¢¢”~Cs__ 2iI$ÃA³ö@]÷¦A’ø~·îýê0·¯QSïýê­[{-+Îvôÿ±¥I–H2Iñ.øìÃ…Iii/bû6z—´¿]~¶`H&ùai¸©ad¸b²Ä’H? n‚ÒHE—HÂüLד4Èdt(î\ÆF_`^ÇIƆ¥’ì#’_ÀLíº”2h¡Íø…'–D2‰\þëVá·u?¿·ráw+Q†}HòÂ>Qø?âÿÊ"’ÁJ‡ëÞŒu,îýÈ­[9-–@$yš”É °ê„¥üÓ×ÿóƈënäsûÛîÂ"íºKÎÃu&ùái”°ž¤N 1•ÍÆ8†ANÐ=¬Cêy ££=1œ‚RÆ þ(“I „‘ ¥¾ ‚I É:‘<Í’ÈßÖþüÁ7ï¿5Í•ca‰$¯™LòÄÿUPQV²8”ׯuïW¡ÞÔ{¿ª%kËBÀBàXD %I]"‰?‘‰‹sÞÿ^ysÈÃw«§yÇ?^™—: ggmHÿÂÀžéºŸI~x29â OÓ²Ôk ½H&4Ö-ù1^&¼6ÆÆ0¾1Ž™1`?‘ì⇭³Ù°F•¥Ë¿ÿöÕK°»þJ+×¼m–HZD€Xéð ÀײuïWaÏxp2îq^×uïWÕ¶¶,,ŽuZ‘äÿÐГd]<–~Áû!I˾ýráºXæ.»:ÃAvRðÔ’âœZjJ‚€Xž5üÂIcø>Úm±Éx¡'¾oçP”EˆAQäÒmë×}¿äóO¿õxÊ òXŒò¼”`áÿ„ÿÖeµ¦µ‚•?|}ó¤‰ßëæûݼ}øGÛ¼#0ßë.ÍÛ£Õº…Àу€k;úÕRˆ¤1½ÍRI~+°¹¶NËËKÔ/ß~c.ö?9á¤SzwíÕg@\|bGœ+Åf³;ñæ¨A&QÖJQÙçóxŠ=ee~߸~ýÆ5+·¡8O[³Ô‘I#“I&‘+·yZÛ䤑ÃJ±C ¶XÛiÝ"PAͪc!Ðò°bmGÿ[ ‘ä³4K% rh&˜¾õ«~Ze=ÊêK¬™tV#F²R "Èxób觲đ #OcóÂ’H&”–4 X©ù¨-Övóõfµl!`!pÌ!`ÅÚŽú—·$"É'ÊĆ%cœ˜ðûLrXZ‡Å……‰$Ÿ;K.™HZ$ 4 Ø2‰äéjCÉD’‰#ÉŠÊ…±ç|þ/¬d!`!`!`!`!`!ЂhiDÒ 8L\ÌÛLf˜Ø0‰tbq`©HZ„ÀDIŒ)'[ƒHò´µAÖ™L2iç}>nèEuqÈJ-–F$ù?aÂb¸˜ ŸzeI¤™Dº”L - ê‘_caœÍd’ <“G^óÂy–$ XÉBÀBÀBÀBÀB %"Љ¤ñ?1aÂÃk=ò Ö†’ ¤A"-I€Ñ€Ä˜r2ð5ȤA(}#?XÚúµh&¢ÅÚn¦.­f-,,,*hÉD’O‘I!3tù C iH"u%4Ö*‘äµ±0i4ãX„êÖa Ø"PW¬íØöfµf!`!pÌ!`ÅÚŽú—·t"iœ¼An˜ìp2HdpÏšÖ6p¨ïšñ4’­±o­-)Ç@à©CЧՙ…€…@u¬XÛÕñß;Vˆdøy[ä'kßBÀBÀBÀBÀBÀBÀB ú ¬f·°°°°°°°8Ö8V%’Çúÿn¿…€…@VˆÄ V„Ÿ° ÃÚµ°ˆŠ€E$£ÂceZX´T ⨪*©ªFÛwÒ¯[wÑ’r*.¯ Ÿöyfmà– D¥™!¯;¥&ÆS«”êß#ºuj£Ç#7Çân©0Xçe! +Öv$d‚Ç-"+×BÀBàG ¡±¶™@2yT•|¾Í_±ž¾_±‰J+< M°¼Ó³@ƒ»Y•=„cID,¡4ÁG/XMÉ qtÁ©}éÂ!'è$Ó"”-ïz˜;w®4õÆNpüÛE”¨-þüÒÔ•ÄD܆—QÐ4ö€R¡ šGÄrM BM÷:Teo kÛ]ycưïà™¬XÛÑÿV‹HFÇÇʵ°8ÂhH¬mƒ@Z»%ŸÞúê'*)÷¨¶&»| J+IŽ[pl'M& 2µ€>ønÍÿi=Ýð‡3h@ÏÎ$Š–jýÑzu¸ÝßÚ ”%CM;ŸS' $œòåê]ñå¤5©LCbx5´e>Ê5Íp€¢ê(„{åÛ3²wàæù«ðE¶XŠÏš0¡èhŪڸ­XÛÕàß±ˆd8"Ö¾…€…@‹CÀBʲB~¿Ÿ¾1úlñZȲûcÚâι)'ÄdZÛ“ˆER©¶‰f½ý-]uÁ`vZLZÒɦ |hëŽv眬Éê}ùEWƒ&±Ü]ÐâAM°‰Z ªGA’xæLÇëÀ:è¯R÷¤ÀRÐ ‚—ñ ^ÒD/ {ŽS¥ò®šàùÄù‚\¦)·gä|ƒ†^~bï×G¥ÓÔC{æVo‡‹H ”#ôÁ_‡{ä…ç*šp6nU|É9VÙÕ1sÊ#k#T‰x˜§'Ö­+·»Ý·pœëˆit攋‘y\^vVnÄB1Ê=)犗Vä=òHqŒš´š±h0‰ dè=úé›eëè󥿒¤´#É×/MK T&Ù‚çRœ¿Ò{߬/ vºE&£av¤äÝ5ujk™ö/- ^‚¨ˆJ;IT!uWR|,4ðý·”.¸”@6™bÆÕvšÈT@#KY¢-©Òþ U±|8¤žY·gM»aΔ ËŒJc³³Ó5¿Ð÷g_8ûB­¤;˜i4Ý¥`ß-½Ãî t×ÞvH;wbš}' oz@U@IDATâ:›¦þ43'ów£Mk}xhà…txy¤ôzGÖ´SdU^®G /¾é¶à¢Ÿ>''ó•ÆŒ_‡×cj!¯±—ð.Û«i¾Iú)DW4´½/Þø æ®›9å‚ÙÙYó£Ô›u8òcJ$ïyî9§o鰼ɟpßn÷\G¾¼áS*§[°û?>v¤¦1™÷ÒÕ•7å‘5Gê­q5žÎféõùèçÛé‹Öë$ÒæëßøF²š‡I,žVLL¶u¼ >úþü•Ô¾U2 ìÕ…$éÔ#m$†M­ævkâ~z¼ƒLΚ,v†d0 dJÂ4¤Šš¤AD BV$ b¡hSh$”ùËÕ¡æØÙæï érºtH>š@6E ~|€H’&SÀ±±‡Få_ÎÊyTmˆ&ˆ#ÂSêAX4;¤£qivQ@$cA5a…eÎÖ¥ Øüéšàï§ ^ òN‰4E`†9:#§Ìöœï<Ô!$ù­©X[õ†€E$†—^Z$á:ܼ…ø$» Wþ ãfÌxÿññãKÚî“Ë4ҾŅ×…„ò_…­Ö4øÉ|§{f¢,¸7Ò*ŸÞ€¦æs{‡2y÷–]‚'ÇTôYI$Gùzâ‰6O>üpù¡GcúÒÈ7 s3›Q×"’ð0׉k›I$Og³$²¸¤Œ>ü~ ÞKñº$²±Ã¾|D* ;‡¿÷0™çQiçn?½÷éÊßÝt[ƒ¶­md³‹´{¿"›–\.‘þ8<•N?9‘ìv¶l÷Ò‡Ÿ¤m;|n˜%¸šXAoÌûzué@qx÷[:“†3jÅ»²¦÷2Äk( žµKÎ>ºÊwvå,q¥®¢®±XâI…M ŒCðFÕî;I”ä¨}5g¦ ¦Ý?H Ä-IÐTu$£ª(§‰L65j%ñª£¾ï½Êrð´ –ãZ,ÁR–¦J‡‘P1tó̺my~Íf³ÏyÁ=nOsž›Õv‹H6âJÐì¦9îÌcÝOü¬¼>X‰©åW¿Z½q3Ù…?Q@{_H½@»Ý™9£o€ü¹ø¶Œ®6 ‚tWKFgædA¤ÿGž ‘½U°Óµ»äÅ7C.™ˆã·ŒÉÌùYyÙ™×a_ÀþGh³mÞ>dE9xŠ”C­%ê+s ¼Ëí¥¿‰F»ŸlCïQ÷,v{FöCçõìCöÊ÷©mÜ<…~ž¢ ý/ìÙ8—6|.hstbîF¹_FgN} /{âûc3§þ·þà ΃Pn•( Ss'g| ŸP?DãÐ^&Æßà·Ñö‚ªþíNò›Ôµí8ÃZ}ßÄeqm‘÷E‚=~ìÓîëÙÖnC‘?u—ˆvš<Ûù㘌ìLŒé:(ž—¡þÍøš¿‡\êJÕCO£üÞîA·€ÑdÆÈJG‘bmã¾Ñ-³Y'ÒãñÒ•›¨ÌëƒNd?ÜZM›Î>XŒíÕ½Ô¾­.¹ ….–Fs^ÙÛdp.¹0• å˜I§S¤Öi6ÊýoH´B£®jM—‚X¾ðbA£ÇɸI¾^T,¬¤o–¯£gb²¢/nÔªB@Ÿ¡À_píÞèWÇs† 9 ­K’°@ÒÇ\| 9Ðc…é4žë¡úD2Yp[¥Ëê°Ö\¢ >i Œu0mZ¾V”dÜ|æ1¥²A@¡êÉK0Ù ©$Õ¶Sê…½±è19àŸ4:3ûMþažR7*4hmÅÚŽ WÓž¦Q›nù™Šê?…Ï®ti$IwÈ¡Å\„|çŒÍÁªòb”üfòq–q+ýŠe)–›”¼d®¨ÏP€èdã)pí˜Ì©#Çfeߊ*ÃÈ&åpÝð„‡Î hã#Alûž,Î]òæKCeïÑNÄ-<kˆÎʯ6LÏýk=ñ8p2ËÜn´µ› Ö8O§º },À3m'·!ÚÅçr8•DÖ‰|5—‘`»ãøTû6.SW? qσtÎ&yèw´¦¾Â6ˆ±ð4öï¶ïãs£»Ý3:aÿeô?‡$ å4°­cbX£@ô¿s—Ÿ~Z]N ––Rÿ>qäqãÄäòî¿·§Ç'u¡‰÷w¢“&„†ö·Qmhäe­Bû§ž˜H™¤ëûLHO”@ç•D>œNýOê 68²ìD3²ºÐ-×µ¥øø Pf@ßxºý¯m©úæü«/­j—,.–A"÷Òæ­>Úrºú— :¾ˆHëL2Žß­Øi¯¬»Rjb“Ç|õ;Ü9ƒð!ÿ‰ªù6§é£Ðcìa÷÷!‡g(–³$»oÙÇazº‰ìY’<&gAã&”Ƨ,QÁu®ÀX ÒÀ#%麶 OÚfHl,$:“Í{¢èðœ.ˆÎP¡”®!UùRÊWîqÏèÜØn9Ö¶o;2zÁ'_ä|+§`ýöØí™S ŠÆRßíõé\.Š÷ÙÒ9ÙY׿æd¾ÉŠÎ /g€®ÌÜ+ˆÂðÂ|wæ”ãfO™°eY·cÊ~ÿ¼ûÞsW,-CÝçTRž…´qˆSNž{âzsÞ†T´zòônž{LÊ~9Ì ¡ršv¤oB’93/;c Æ»(”'¯‚0ÑîrOí ²5oäW£Û¨k>ÏÙ™™ùßNŒ£‚Ï%×=qQŽ×>z~å< ›@HY3žÎÍ·©>ýà}'wJF^')ý1=ž°ù"oJæshã |ÜîÅ”Îù܇,û/Ã>±µwðfÛ®‰ô)ÿ‰ó8¡nQnvÆØÙ“3ß Dÿ"Ö9zÁ± ¹>ü/ÛôÿaâÄ}hCÁ£®Éb÷YÙ¯çfO|7ØŠõ{´ €k:(„›/¤[ó÷‘>#%X!Ç2±îaï.:pu0áy^cnjGq˜Vþß{hÛvÝt]ê’Î$¢¤$‰ª».—@iiAbøËzUT(ôóÚ zõ­BúSÐÉÉ6ºáš6´dY9ýëµ}Ô±ƒÎ:5HL˜®îÓ3ŽF]ÙšV®õÐO?G×"é…qnÅxb‘Dz ¯Ž+ûãd¼­Ôpp?ÕêöÌìW倶 ªŽ—Øü=Èå=S°{ LuKê†7{Ì×€E:ˆwOrzÏ”¤@7ˆmÅ¿x˜ºþ˜§¨z¢5Cã-¸I¼.èLGŸ R3äù{ï =A™ÀèI®  @®$h@¿ R¸LS´·˜ÐøHh”‰¶v&ˆSQ¯#Ñ®„VqOÖVVU|£ð ·‘¢¹!Æ_ KIÿxû¹d–bûYªL­06¤¾æÛùÎÇrNÔk@·‚l-ªÏ¸Íçi´iÍVL/0©KhŸñuøôJ“êÓD·«¹]¶Fé…RLp_ïKàI Ìp°°bœy~ö-,UÔËVð8x/½ý Í÷̯õúO°%dcºèUSçŽÉÊ^ kÃ!¦v¬Í£ ‘T`dЧµ‘dAû‰ŒEJK‘tiãôÌ.Ô½‹ƒþûæ~½Ùã»;õéä÷?+¢kÊéõw u=JÖS¬+åC×êœ ¥ ý¶ÍKeå* èã¢"L£¯¹,Âñu=tâý’×›cÝÇæÑÇŸÑö¡ÇP®N91ö£¾8X#¯1t›À“#) \ÁXD²Á0ŽqgŸZ&{Vã£öz&;NÏPI’»áüèhpƒV…šÀð‡%¹vÏ風¤¸ðÉó:„@Ók´Ž4KG²è 61+Ï¡²\wfµðlõQ+…Î"I¢4|Ö” ?Çë»öU(ãA<÷€Å•V<€zSÃëB×úHâ0eL_ó@ñ4mœ_.þ“;û‚—pÓlÂñãBõ4 –r%Ò?³³¶"Þ—ƒa]ˆÃ¯1Ù;)'⸇CG’ëšÏ“÷ëJ¬£ à©»9†1·¨Ô¿CÅo#ácôƒv+ªµ-èRÉj‡xÓê% ‰;0ý ëÕ(À$´ž‰õ*Qô6L7=‡)ð ªÂVçµ·[Ï6­b‡žÖf‚ã÷C" kíâ2v:Î>òì1ˆÇ£Ñw‹KèdLE³Ò0`IMJó÷°ð<˜Ø'-µöG-×–úG)I"=tg‡P±pÒ¶ä2Zêé쿌lM¯½”rF+[ß¼àTª“ ‹ËtœoË覾èÉœ2úC@ ÓÖý¡ÇÒõ«#VÉz#À’] ²}3)öã¡7_À3cõnÀ*ÚŸnQ«X™õEÀ.&-Q”’}Š&gb:ûA¿­]¦î?/ﱉó éX¤¶FgM=î @ȪkMLÃ'ÿ cÜSß7OC²+>í ¼ŒF§lƒ­aªä4lýËK˜ÖþFSµ‘p]4ÛÉŠbª›BÚöÐ~~ï€;ÀûÁ—Ç-ÜB´q¿õÖ[\¤F’4m=âØ;3²» 0ë$ ‰>F»sã5eÿ{èÉo${ÚGZ ì[,mL?5:®<€`¨Šö÷ÑYÙ÷Û¤´•'BÙº'ˆåÂHuŒã¦¬Ã8¥¸qq­¤T9OÄ­çõë±ö«5¿M½Ä(k­8v¶\I$}>?•cÑØP!FÉëWiÑe´q‹—&Þ×I·ŒþaE™®‹È]´ƒõõ6LSÛë6Éˉép#uÂTu´äÁt9K$'?™±¦E#æ¥w´Ó迵£!±d}Θ&HΊˆqæó²Rý5} ªÊïˆj¢Íî= ³"±y KpîÓçx'\àÚôÑþªšðÑMz(Þû¤ˆÖüZAmZÛé¯×´¦ÎôÚÛûi%ôi›3õéé"–’¿Ž›C—àX(Ð ³Ü~D?Ýû_Ïrgü\Ÿþ­XÛÑQªuj/z+·¾°Þ#¾óºý|šð›Ø[Ž)èg{¬R$¡!öÁˆ'ó‹Èþ âœ)/Bâ¶D“•Ùš¹ªš²(”vÓ?«:†#‚ðÈÏ…£sr:™ÈLÎÓý_ªj.Œ._1—µÅ‰˜n'øôV:˜·3Nüçù blT¨¯šû W‘]Pµ,UöæÃMÑnœý®[ܬÆôS­]ÓNîäÌ ½Í€„vr PTøaYA—›ŠDÜ„±Ô ˜^ì —ú¯ÁôÇ1¿Vç´7BÅ +[‡޵mŽ·Í;^X"ÉÆ lµ Õ |+˜nzß~™¾[RJWŒHƒ+‘X‰ºèœdbW>ç™D))6Z»>8Qñ _ztsQ‡vvbž½‚5Æp ö¨[g‡n¸ÃV׿à%Ï–×çÇõ1YÇ’%ŒõIL=mü͇io?×Õ©/ññ1zì#¹ß/‡¦¶Ã%¥õã±V†Ÿíš*I¤Óæ3É8?7†\­ñhtB¯xTFƇxWK ©ózU7y­^×¼$’{Ü‹ëü—_£KÒ£½)y61AÀ"(MœÔ§-H§bfjZ}Ê‹eª> ³ÿdFP—‡tÙxP¨FOŸ®›Ï®(/÷»ŸN}Æý@HJX_H:nVÚ¯sqmá°x꩸”’ΊášÈ<††öc®kÞÆËL¸ã±'ÛÚ[9‹Íº«æ2µm³áM1=Z9­ÍDD¸÷±ç“ j«{¬ËÉ­ºO3ÆÄì>e¶Çì*Kk,þz߸gOì׫ïƒ7ŽÀn݉ Ø”–•QQQ1ìÝOó–m¦ƒ¥Ápˆu·½û‘< !$%dÚIÿ—®ëD¾õáJ‡TçFHw:vp€ÀjôõÂúì«à­Ç¤ð/jMÇwwAR¡E?”Ѱó“éÿ&m×;Ô/ž®½¢%ƒ0²~å?•ÑÅç¥Ð°ó’‰‰%N íC/ò náý·?·ÑëÊ ቉Å/I ?L/¿±/&§€s%uê Ñè«Ï¡¤ÄDr¹fÏ`&5z­ýÀS¯Ì£u›_þÒÙW¢2»0Œój¯t…ÎõXHogašVÕ5ÿ›¦œÚÃwu„”¼”/+«Ö €œ68‘zç¢U¿”ëD³PôظÎôÎǨ¾P¯ Ýa\BÛ gûÊÜêRÂÁÐÉ=ž0³¥“?6ê⿹ÿ ñÄ”ý“þ¸²\×>wh2í.ðSoHâ$]RÍÆc,¹çtÙÅ©”oeo@—˜ÛbÏC0F$ð_|[L…E²n vÖiI0êRi)Îi?¤¬±JçjH%‹Ö­ۀú´‰Ù¿ù\F«çóÚJÕˆL½z›-rÝ3` 髯žœV*ëUá⡱M4©^y œýD6¸†Ž»,PA‡Uk_e…„ÉÀZóÚO¤¥øjí'R½ü@°ž‘ÑŽ±gÝRL“_©¾uüÈA€‰dp#E± ÉdMšÕøñ2‰ãÅHHr&äì0vá˜ÜOÓŸß­K}˜gòg$~A>ÿ¯]ºhDšù웪¶~†4ˆ§ãðf nN_|W¬/‰‰yñÒ7H#¿Ð£Yjù} ñÒ¬IǤó†ÉfÛØ8f8† $2æ¾v.–ªŸõÒŸÊCê7^ÛF'‹ ~(…Ä’ƒáÀâÒdáÏ>Q÷€›,Øj-ZZ„²4ýF¸¬b7R8Êç#NçM‡L"}C¯³NK„Ë+Íý ºBšÎn¬~Y_A+® z»èœ3’t"É×ïðsShî 6Pcý_v\ÂRûsNO¢yßëKLY%ãNHÓ_Á4{ÇvxBhK9OïÒûŽÅb„ã!!T?ÙX4|Œ¶aÉzþñíéÌu… Kú׳¸U¬…!à¡Xc¥£ƒØàmêáqOÃî€"%ƒDÖ–ÏÄÓ ‘æü²²#OPÇ´ÂÀÚŠ%ç…Ô¹“]÷.0ä¤zëà$sïþ½i:§b\³l(ÆQ–NF¿;ò}ºÄ’‰¤‘¸­ïá‹uÙª*Þý’t©$«‡°þm›Vv}ü±ˆ…øßГ,e>ýš1kÝ4,"YOüÜî X¤"V²°80¯­Ô|XøÖ[ŧÇ%]s£~uZŠdÖÀé-×·¥?^ÜŠÞ|?ÂcŠÄô9­ßìÑIc´vïî€þnPw—Øg?³‹†A’8öæv´dy½ÿi¥ÂýÕÁ’à,[aQ€ædZ ;ò«¾¹9Ô'/ƒàzj0<°¤4<¥BêÉ®®Ì)þSyú“Šû÷ýÏ€è6ý>V¤B’¿â‹LXÓÑ–û¬`'Öo“°ˆd“à³*[Xn¢ÅÚ>Üc³ú·0°‘}W€|˜QìïÓ(Û˜õ pN¿y«W't¬RQ)#O[³zœï{…qJHˆnt6ÒK^8±$Ñ_¥Q¤[€³$OG¯XSA`0ÆÄ’¥ë¬Ók?½¢éç‡庅vgè çý\3ŒèZLƒ³´’òÇÁA¿×§ÑJø`eC´•¿x¨„•Ûg#¶Æ&Ÿ¨Ø·“bËGâññÂHw¼ [)&XD2&0ZXX.Âcm[²ÃóOX¸GÇ}æ£@÷z·*v¨sÌu$/žBÛ·^0Á¡½>\X¤ˆ¥yÃÏK¥)ã;ÃÃÑÇ_ëF9ÑGÌm)î±p¥À–ÎrúÉ—Á6@—ò¦PÖÙ(‰tVÔ0Ò1Ú_·SW]š¦‡èd}âðÄ÷²‹ÓhòøtDè³vÃÓ€—–ÂÖý£Û“ ¶ílKÆ.°<¹€˜ã²c k ÀsU®òž¡Îm2Ÿ3¦a–;V¬íð¿­Ú¾E$«ÁaíXXmÔöraRSµ4ýŒ8^vìÄ´ÝHP ÇãáÊg@¿8H~dÝ› axŠ#ܰ –=Z»¡‚j{rœm¸ V¯Ñüö9$Q×uci“9…gëÜþ½ã(½““¶lóàåIT˜G¶?yPK2yáÄqàÙ“@øøØÓ/ì¡¶ûÇh+ÚZ•"íæ"à‚ c§“¤ûo€‘7Ÿ~ðÁzû²âlGC™Õ5ŽÀ+÷?ì`º5ÝSævGa³o¯]TdãXÕ±‚£!îv8\àããÇçj»¦áp~•zšµ”8´‡îÊšÞ? )#‡ ê5µ67?±M4|"áÌá!Ë–;Ÿ|øášJ9aŒÔFX±Fí6dêà0V2»ÿ9ŒÃY»Uï¾Cûõ5O±É7xñšðàß/…á²`¿•ìÒIˆn1¸ÁC×;l°So ëµgŸ¬¿x»Á`à¾Û;OÝõîá¤ßA29TâX­¶ooé ЉýãôІ/ý¯ºzõg%ÓHtXgl(bgsHÅp·-üý,fÙyó—óáB¨Òº;ÒñÛolGÔ²îVèlXÒ²Åö1É÷Aà<­Â²FçÁòvÚ³»BNÔrõ]³ûŸ@ÀCZIor8Èfc÷?xTY) *\p=#”àZˆhKp»c¥æE@ð€L–B¥ „]þ ¾G¨¯P^ÿ¼³]Ê”†¸‰kÞ½­ÕD’ Ö.%'þ­kÕsŒ(&c³³Ó½ {ÂÓpÁ”â‚yþŸrÃÿ&®Ÿ/O}ê¼cp•IðXüNº­óM×¹¶²µõ^n̤ìË1ž!Á禰Q´‹W›£ÑŒÉš:TÓ”ïE›+]“½—A~ñ¾è»`ü[ðþgxئ{ž{ÎéÝ[¼ã ÀçÕ‰áý5Çþ˜ÌÇ{i‚êÊ›òÈšÚÚçÀ÷³ÔÕ>ÅÕœ7!ˆÝ ŸH8ß‘9£¢fâ¿<® ùú^†oÑGæL™ø]ø¹Dj#¼ï³swòhoãšêk©£¹ |Œ]Œo|8_›“q/ç5d\¾¶6øø‘œŽ"Éa ƒR7ÒˆÁTßCîí4êÊÖ˜Š#Ýß#Kú²àK2sÚŽj†'ÁßHLëMzœu³ªR¦¿œ_¬ûß;ý”Dº.PØe9±r&md'´ë "鸹î5—·‚ !¡Æt£ ’H$’Ó½··§up”þÕ‚bsÕzo[D²ÞP…„“|ébIr:Iˆ«ÐaY»µ À¸³®¤bÛA\' ¶«s³Çmª¥¨u¨žµŸìJ¾ÿ>…wkÃÏUñÒ PˆÖL.A"gƒPÌëžÖ=¼Ün5çrÔ;À6„s­‘È–È¿;¼\´¾ÌeÙᶦÐ+¸:ç vñô]ˆh4/›Ëà…tæW³}hÆu6¦;²»½Æð•L¿/'§½¹¼oñ£Ø¯vÌœßÛù&!<#ã5µ>oÄÍ”€B†×ŽO4œ6Gj.—È1@²K=q”a~ozø0£µ^–É?Uh?áxÿaLfÎx‹‰¾¹^}ÇÁu"µanÏÚ>|ðô°1}ÎŽÄG”NmZÙ0åüÛÙ?$[ƶ»N=EfØ9)tܦ|üe•ŸH=?\· ²nÁ>?µmc7²Bk6xøìëbÝï_è 6"ç2'‚¸²Ógv¯2QM7y‰ä(<a,Á¨­t¨°‘Ã{*Âõu™ÙEþ¸Åp®ƒ´¬0ü1r¨vLô'¨‰ÄÑm¾ÁŸÛûhäÿü¡'žH8&N¾™Nò¨Õ‘”ÊåTâiÑ&\…PGÕ$g\ böHnNÖBà¶Òž›TYeÂù„GèK\ R·4/;s&‡"ô$¨£°ù¤¹\´¾ÌåÊz.Ä+'ÅnsÞó‚{ÜžÛ³¦N€7äùLbg»'lãiÎüÀ¢Qp<Èú2¨ûw£þh÷“³)à™RáNDZùøhwÎÉ÷MT¾ Â<Ì(k^c̳ñ–Û†OÚhpBý-í4y¶;óG.wg挾òçžÓÛ$Ò]³'OX òò*pî×1iÃþGh£õ7ன-‘}³(ˆ÷ÌΞø±¹Ïà¶ ìR6ý Øf¡úAœÎ †Ô—IZEaÅ“ t#‘ÇbÁ7í“DZôòöÌ©pê­>}•7 ¹„>”É„´õsÑð|hùÔŽ3¢÷lC;mA'i9°«AŠëú¯ŒúÁµ6ÿÁ?'œÌÿy:nYÙ›p‘ ý2ò*£Õ9Žhmm ëÃÙ†££pzðF·¾æÐˆVže˜ê-¢‚=ûè«Õ¿SIM^¥—¯ïO?v®|z"=ÿj:g†sp>À$6sJïäУ{ØañÊỉ%œ¼ z£žxÍ’BÖo‹dùj®m›£—°Þ¥þ–Ù¶¶Ä ·þ¥­n®wY[ùhǺvvÒß¯è «ÚDH@]0怈¶éX‹lÍîi=Æs´&í½- ¤éùZœb“»HR =¼¸µC%…$ï[ÀõS·Òbošž©y+Öv$d‚ÇZ"ùÏ쬭8…Q 2Ç›O±Òék¼(Hœ¯'¼ÿ·€t3öMët’-¦ýß°]C©/S=}Ô0Æm^&‘|À%I¿yUDÕ(]±»-_]<„+A’ÒÞ×+˜~DÅw¡Ã8§MZ·GçæÚµíû^IxmI¬5ˆ´ÃtëÀ<<Ãoƒôä1EÖc]Î@"ç`Uy±¦©w`Z}&ØÐɸ1²UY[2&sêܱYÙx€ Ã0Ow’Dš /Ä{ Íý¯¡—íñôk­kÚmø„~ù—b|ÏA'õ )X~ <í]1eó«L´,LÏ{°ùˆ¨© ˜?_·…è>RéV”ÉÅþÜhÓäf|ŠüÇh8ßéž™¨ªÏE‚~nÀK»Æ¢®ÿÊ|ιS2òxÿöÌœ À:”UIvoä¸혆åñF½Æü ˆØFµ­Z0ÇÙ®µ@ ö<Ή0rm(ïÕ}´goP YZªRB|819cÄÒJx¬›ÈK÷®Xžv¤Õg¸/a ';O¬Œy€5ï7•Dòi¾ýÑýl9<"OoO…@sb{óõm09ä@ÚœomZ^pO،Ǚ”ó%Cþ,ÍÏ™C;¢c«7QM‚ì(MP…ƒ,¨©ñž0ÐÐcm#wÖ*Ð1Ê«ëêŸÊ-ÍG)|ª¨•§ƒ×;¶Õàמq×¥‚U‡@6˱_³œQ Ž5$l© O¡~e§Ü–(ئÊÓÚôá ÉFîÌÈî‚—Ë3 &OþÓ=>øäß±"ƽ#7'ó žãÖ5H_QnvÆØÙ“3߃ÎÇKÃE¬ÿy×Ô©­q>gIs@ ÷•ÿ@¬ß9å8]b© Ï©¤<‹æg@▓瞸þwÆ*@ÊmCßß??q¢n%À’3^ÌãD1 z©o¹ìö,`éðÊeçëùšð7´÷?ÔŠ´÷*Þb® :–9græ;vM‹‡gœw_ÆY{ ǧ.œe¥ô8UÑÞa‚?y™$‰KÃ[®«ðòõÙg€Ìåê3syÞo#<ßÚ¯‰ÀÈ‹N%^š+õ„”ïæëÚê6ˆ¾õXz¸.OØ' ΙÏ;3™Jáç®`¯_ÀÁbx^Ò;8áÏOÑ£ƒ°ç^=\ú0×oöÒPÄNHéÌ!B.8Å̾ömkNs×unL;¶F aÔíV® ’ðmr™[@"UU@Œåƒº¤”-e­tøàg1f~>Sõ M³·³ù{’£âLH#;7zP¬¯;âÂT\‡ÁëbU]x8ë?8¶CÔ!ðõÈá9ž}Ÿã]ú=µB 2ÓbMµ¥jM š ç£VðVí\ša§ÅIÕ%1N¢*„tðvǶ¨¯†¡@𰕃 ýD\,5ËU«eG€n…ÚÊ”D.­jâA]C£«@æ^7·À–åo|Œc‹;IYœ7:kú@Ô'H³£ÝSOk Á•“ 8ÌuMÛ+*¥cjû™˜±•·\A€HI‚¦¾ B¹ ::¡ÈGÚ'r&ˆSq¼#ðq%´Š«6oj›ð {~tf¶ª/YÙº1 ç‹q¢.=¥VqûxcLdã ôÏãý™q‚ªÁJP¬vœ<‚cI¶µ¼ýBN挩XPUžÒ¯‘jÇ¢àÌ °‘n¼ÍnÓ¥Õ€²˜Õ ª5^GÕÊ6r§^ãhdÛVµC‡À—¤Qâßyk{š‘ÕE_N€³d¶ÖætldÖÝÿûà€.UäprÙt¦i™]è´“èíèSàCNL ë®j¥ü£ÏR‡¶6Ê™ÐЮ;|æŒË†A¯ò줟Ï(ÿõšÖ”3± M×I—v~ŠØÅæ6Ùú›‰/[’OË謟Ç=oßྫྷ ±AàöŒìá{{ÑÃmžäô •$¹ ¯þ¨jhoL$ÿpQ ݈ë?x8% v;“˺GÄéÝÙæHøŽêCô—¿Cå¢'ô9¦÷_#ü ÏæNšT‚É@5ø.kîÎZhûM»b@Pòy¤øöÌ)UAíÉÃc)ôÇå[c:êwïoœ4àz‚í0öºÆÔñ6Hæ\w»gtbÉ¢, =Ù“*¦½v””ø¯D_^-½mP¡ »Ý/¹vÉùÌîu¶K¾Á}¯á¦H¾Ä+^ÃWjPšŽÂ¬BÕØ Š4̃ª¤¯Õ‹Z)×—Diø¬)~2çñ¶¯B·ä7®¼°âš^†÷ÁñœH¾wô<'mÒ: .£Á'*K/wìòQ| ²êÆ·‹J`Df§ËaÄõÕ÷ň—í‡Ëª$èõ’®¾Á:¹LøX×wÑ¥!4öjðý’ºðœdÚ5ó5Éý_9"8>¸¡*Âç=ç•}!½c\Æ4¨BÈßd,p oC±íƒ,Å#Išðnxžµ_Z‘äSÇÔî\ܤ·aÚàƒ1YÙ—âP["‡~¡À¥ÌßaéƒÞÛ«˜êySSÕyмÇò5-p-ê>Ím˜Ëñ~¤t§;ç$YVÿ:lPŸñëÖµ¿K^¸×Œ¿Ç=ã ¯ì£YÅDäöyl¿5§Ò£¾nx#/| ýÉã-Þ‚’Sð…J6Á^hk7K./ÿѧR.gG^oK8Û8VŸµ]LZ¢(%ûMÎúmí 4uÿyyMœ7fÒ´Ó`™ýœÉ^ÇZi`Ü/ŒqO}Ÿ]Ä®ˆ§³40.Σ=>^w°ÉèÓ ƒÆ~͵ð&Á¯=)çCo/pÙ«qî™ËA?ó>èNò•«÷‚Ì$[Ê÷æühø´·gôy €:&ÙVCÉ »¦Êw£ïr%AØ\ßÿŠ%§¾½ÅÓ›ô/³ë&óøêÚ®ï8…κÆjå7 &qµ¥ðã¬øàóÁÉ¥)ŽÍC@õ|ãx}×LÃSSÛ oÏÚoc'å\¢(ê Ié¨Ù|'4Ϭ XØÛÐU2V Ü 9ñ‡Æø»;éî¤~‡Õþ˜›ÚQÎ3»i'œ³úÃOˆD“¿ÛO­ Ù¼lx²N$9Vv÷.N: ¡ ™Hž £3ÖNïh§»!Õþø‹ƒd‡ÁØø{:’hXÍ£Ÿ8LO;õpŠ[át¿kzpB*êwAºÿÃòòj$’Çh6^ëÉä–­A•óøc¶ ]TűIÆûççYÙ¯ÏÎÉŒYÓÇZCÍsfí "ëî÷iÂoÊtÄû ?Qª&Üý¸[yˆ¹“3¾†¸íqè ¾£ª2K«&§:Ÿåÿ·á;PÑžÿSøs9—÷“ê;ŽÆ´mÕ©ŽÇÚ6âmWϱö,Ž< ¿=UP㛯íÍ“l[¸vƒ‡~ÝäÕ®ÌêÛu‰§¼™P²*6< ¾JÙȇ°öéÛ¡ÇË1®YÙ¤p>$“}{ÆS<¤”¬¹mŸ‰áz¬™T~³°„¶ƒdžÔ?>tR?ý\°Š«EP Çùë7y¢ú/½lx*ÅC¢úõÂâP[±ÜÐøòt®ÆgWÀ'ˆ¶[ Õ°Xöq,µÕlrS@ŒUdŽˆïWnŽl3ÚŸ\®™Ã#±…4í&»9²Mmå"Óî§Z=í~ð€9ôôé)L^ÌÇç6‡û¯ï˜ØX§˜žN ?¯†œKöl²,š±6œ™§Ûû89šP'J“ÝîQÁ'XC¯, gÖIM,?Õ ¨¾ÿ*Íœ8±°CªV¥!ã¨Vñ(Ø1;$?œîž~5¨-òÀ_G許û¯×grÿ³—¾\ÅîÙæ(ø+ËÙ!yÇö*ÝvåÙºû—Ëi¹ÿ©ãŸ`uüÀN-p‘w¯£tã²{àkÔU­)û©|â)äŒûÓé£/ŠèêK[ÑY¿Ó“häe­hÞ·C°”‘ý£>5¹+=þÏ]!âw/b]¯ZSA—K¥¬éùôØÃéð‹ZD眑L3žß¥G^òù5=\"7vÛ_ÛÒnL‰3y|l\gu ŸŠ“ÓÐS麫[Ó¶ÐÒ‡äó™Ü=Õ$z!ü\ÿ«CNЧçàf«¢¢iz£MóZ}ˆ¿½FUÅ2³#ò&g|kίmª¹|Ü •X:MÕê­½Í#æh0ìàøjã1“E##/8Ý ›—ªT[¹ªÜê[µ‘­ú¶ê-5ß^CÇSI¾«‘ㆎ.š;ŸÂV{„¼{ï­¡cÙÐ>"W¤Ðˆõý¯bA"ù\2ކž»U>ˆO›¤ æ]}›%0ê×8nhÀÓÁŠr•©6Ü'-<,s²'~ˆnx±’…@³"À2cqÙD¶ÎÄ€Yó ƒúöŽ£$ü¸²ÊÀáøn.k؃Å2-‚Õ¶Ù ÀàÔõˆ4Û`”°ò—š‚zö¯Ç–±)˜ªÜ´[ 6&‰å¶X‚s¬o—C7­–áÍ•GMôSr¼SǸ¹úi‰íÚìÒƒpIvVÀµ2Õî ‹íšŽ8šrÞ|mð´¶‘ø:áéjsZ¶ªœxáXîL,k‰§¤¿@ xÃ9>KÍ–þ?¬(ƒÛ« çnëýï½B8·g×WÐø7]“>¾ÓÜ¥n}½dy°.‡?¹ºƒ–xšûªV¹Ñ;ÂPîQÍÄ àq ¼šdËÎV6ºQ«b–D2 k×BÀBàèGÀ  ’0,©ubÐbT“š¤­žMgÖ;ý”¤P¹nˆy}×ßÛéQoÎ=#‰þß~á邳’éÚ+ÒôÃ×l ·,5}çÕÇ¿ûÿcIÒe§ûlÎÄ82ièÖ¡Uˆ¬7g-©mŽf#Ø…á$øË®eŠb«"}‡ú<=˜66“? ˜]õÔgL\>¼úÔk¾22ɶíä_,ËŽõ°÷ ,‡¼!s²3îµHdìQ·ˆdì1µZ´°8Œ„H$È¢‘oÝ© v²CÁ_±ãdÇzx]àÚ„‹ ¿xætúÉI´’Ÿ7ß/¤^ÚK'L vbNgB‚ÈF ï}z€Þù¤ˆ˜p†'ö¯÷âûhÞ×Å´xY©î_/¼Ìð󓉳„©¹ãèrØ©ck„˜ÎæÍÝoKi?ϱ‚ìÒ•¯eÇFò»–©ª~mšDz-ådÕy@gW•öBòÍ¿PUì´EþN”Äasr2‡Îž2aec‡Â±¶oÏš6¤±õ[z½êO´v¶ì‘­ë:-·{®ƒ­´ë*ÇVÈ\¶®rœ¿ûéºÃT6ÄÖåÑÚäs¨™%Z…fλ+kz8yÏbWAÍÜ•Þ|4|"áÌxéÑ„ê1ÀHmÔ£jEê;vœÏ±¹ëlÐ*P޵o›¥$/6èGÚ`ÒÑiT±K•µjÆy`;üñý q¸ýa>%Ù½ÊÞýAÞÎS0ýצuui!—)¨,S°ÏOm¡#žÌÓáì_o;,_ÃÓ+s iͯºElx^,÷?Ʊ×Ö!Œo+5 <÷#Ûó²3/A¨Ù4±|CÀ±Žüq‹Ù±ø–4¬±X”†_Ešÿ#¤¾C ª¢°CšäšU+#þ Ñu]˯kCÉý{p9>%Šö“òr2†énþêÛi„rz¬mU™!û˜?\ý‰v”ÁÁk—’“¿` ëXV-äS›®xè]øA<-ŸrJáøúá¼ì,Ý|ß|Š\?_žúL¾¼a ”§$8 'ÝÖù&·û–jZÐìÈ: «ÏÁiø™»„âòÛ3³?IIqÞ^›E."©\Žñ¼X(kãE»xµÙ©õ˜¬©C÷ú{ÑæJ×dïe •ø»À½À|Öÿ3/;CwˆnŒ“ÉkþÞ앺rщÆñæ\É|¼—&¨.ñW[?²*ÂDÆä{ö<Žüf3……Sø["á g# _œ3á‡ò<í  à?X†¹ÍGæL™ø]ø¹Dj#¼ïÎÉéHím<ÔzàZêh.ƒëëb<êÞ@ÐÈ×xê„óê;v_$”ÊSAÌoBôŸ8\ƒ‹åg|KÌ}XÛ‘³mHÇ‚$Ò«T;uNsÑž²r¼x6’Ý{*OzGn4F9vè6šI  M-Œ ŒÄ yᨠœxmƒcgæe†žZ0'øÛÜþõÌ}Õ¶ÍDC~qv êÑQ'茳ymu¬cÑÈ›’Áþ…_ǽ? ÖÜ÷©ÂöŠíw¢â*¢ÒFÔ.“¡Û‹È·1NLÖTÛ~–äÁN© j)šÝs®>ˆ™câé~¾Oƒ ^ɦà[‰‰® 7ÅjÕM¤ÁêKCÙô{¼'¿ÊÍÎX„‚5ÙgSÎGÐŽj®Ô”S¯Oݪ?£>¥ 2wL›––Èù+׆KñÒ L·´fr‰ëi6.ÀYcÝÓº‡—Û­æ\§wà–yNËG"X~ ‘Pª'8²î‹ t­``}ú¼¢´ÄCõRD<õT¸¼‚+x>Êž‚¾ áäûes9HžPOøjÖ£íÃ¸ÎÆŸ-ØíÝ0†—psL¿/'§Zð[ÿÞâGQ¿Ú1s{ͱ­‘o¢Þ0QSsZ^³„.>ÑpvÚ¨¹\"ÇÉ.õÄuP†7òôð‰ÖFxY&ÿT¡ý„ã5þ‡1™9ãqü-\A±SeåúŽC”åD<»# úrž€k#€ð“ÃÇ`í7 &g6&N§ƒœ “qðsØ5‘ßEøåwþÚ°Yš=s˜9N•ªëJ––WI}Ø@¡¢BÑccs™„xQ߯D²½'ÄÑÌ— (<"×=‰qSß^íà˜Ú©ãË83ÞVjs²³¾Âry¼=¡-K)¡Cù®jÏ/“uIåRèû-”Î5šlßý¿¤H,]/Ç£R»h®­§F ’d‰%x[(àúYóÅ-TüqKI¶oÆ=Q¶ £ÏU…ƒ"¤zø~gIàaHz´™ßpnRqQ}Žç÷X¼G'ãüæ¢<›ù9µÇQ¸OÄ? v©¯Ð­mÒœœŒ ñïfaRÌIäa€áhëò¨eÙR¹œªP"Ú„«@ôªIÎ𪸵Grs²XB¹£›TYeÂù„ùB<è«q.ÅôÂL>©Ð$¨£°ù¤¹\nN&¤M,q &HÄËþìåÇx]~Ðs!$u)v›óžÜãöÜž5u©Ê|&±³Ý¶éaÿ‹Fá¡û`åÅþw£>¢ß̦€gJ…G8Çt«æÑ1î›ðµõ2Èë0£¬y1φ–ó6|®o·%¢&ÏvgþÈåîÌœÑ7@þ\à1»›Aºköä KA€>ÀGgÎý:&mØÿm¡þœÃu µe8Ï›EA¼gvöÄÍ}·e—²éoÀ6 Õ!ûÓ^0¤¾LÒ* +ž¡‰y\0÷Ô+ðEùü–½Éí$ôÉ_š™ÐayÅÜG4|Ÿ‡U»kÅѶ¡GŒ¶Fge/v5Hq]ÿ•Q?¸Öâ?øã„“ EèÑqËÊ Ø„‹´é×Q¯2 Q㘙ɚö—õ€çÛøŸ öã¯j£“c`mLm3‘t9äŠs!†°‹Ú%ÇQ¹¿ŒöTìEÔTèPúû6«drÃæ :wh2-F â“%ê¡ã öúu¿{m1d,l×#‚ÈÐÓ’h'â#süã[‚ÓÖ];;õŠûÉ¿ž¹Lsþ­º$$RZ¿ôêÞ±•Ž+ãËIkj;vèW>;t)%«}µfK?LüœNª|º*žJ6­'ži5Ô`Àþ Ç%é3D0|Á³Š%wæé‹y|B×âÂ_†çûrM’æñ4;—“•³R¥ýÏûâHR £$*HT£j^™nÔväÂê[: i¶B<úHÁwɳԹÍÄJÏj7–•ðègk8Vº´R-µDòŸÙY[q>£ðâ=Þ|^,©”ËäxLr¾žplèfì›Öé¸Ñ¶˜öùB©!á4å“Þ~¹<7â[æã¼»6·­—I$ï»$é7¯Š¯Á€Ò»ÛòÕÅÃA¸$)í}Î7'Qñ]¨pse“–óq޶£mß÷’HÂChsÈE­ w];<(Æ€æáy~$)2e¡ðå\$rVE ÿÏÞuÀIQžïwfw¯÷ÞèÅ VP±GÔXIŒÝ(c䟈R’‹{‰% š˜Ø¢(ö‚]PA¥÷Þ®÷¾uæÿ¼³7{{Çq׸Ý{¿ûÍMûæû¾y¾ÙÝgÞz>Èïï Vß0'Àxx6ò]/Cžñ·Ïœ‰r.D&ÇYHƒúíNHs?Æ=þÇAÝ訪ߊï¨?£ý‹0¾§`“ú?NËX[V;í]ƒ1ÍF¬ˆ˜i/ª¬Ç潪®!"¯~¾,t´p4e· Î<¨ð´ÀÜ—Ç}qk8³­¡¦UœŠ:ý\ â> }7)‡š+ÿÊÈËn¼0Ü6cÎ}ÀÚwªì^ÏðÚðÝí;ÇmG“ °ƒÖÏæ—†¶›Ÿ–ý6"ü ‚c³Ù Ad»q8”åFè­ž íE¤[jÉâŠË6›4·qÞj?­ª¥áƒÃ/Ó"¾ºR$$ljÀËQ4ëñ<ÃAfÒ É4ç¾>†=å ¯€ä¢L87ñÿÜpÄ©0âëñ1ޝg–?gçøê¼ñn×x£s_lÉjF–äM¤QýSˆñdbÎøšªms\²î<&NœèAk,(áåE³eμå¬W*š§?¤sñøž‹ÅP 1Æë®ªC³×[MÄp%êäá»7Ïbµä%Ó`5ZÎ&Æßu0múF#çT-÷m¿ á²<Š'΢êQ³D¢›p,x ã¥ÅPذ &´ÐÈŽ + ¨Ô9K®Z…lÄn¯Èž”½j ùç3ÙSóÚØt·TÃïö@|§|Þ-`'K$†µîÀ ESõZ³~Þ±­ñEÓ¢~=”æAüöÔêšñæaj²f)Ñ䙳ùÜ›aö¯&'± [˜¯_w¨Vk$°·oÕÚôAó¼ÒwLŸÝÇ­éOâÃþ¨ï´¯dƽ¥¡°œÛœ¤ø÷>Ëa€¢CBÙ:á9lÿYò@¼³V;…Õö EP—üy^¿üÃŒYžÄǧ4Åó|3(sægOÛÂíNš1Û/¦=P,1ûá{çm‚ƒöfÂù‹;³Z†Üâ7ÙÝ5g¡Êø¦¸ßÀ˜çk`S:_7cÓ'¡ašñÂý3–âÞÆÏz޽¸æœ_Ìõ›—æø`Ü­âìöT@²ôwÁUÃ1èwðC÷có69WÍ/hÃ>pod™¨ß–qø7;iæœß€ì_ ‰ò8ÿã²Ý:µõúvåºð´Qp4ñZëx‰¤ {DH$¡ÒŽŠŒ‰tÂÆ;De‚Í…TÙ)¿ѰհAƒãˆ'4„³é@œIÎ7Ì‹YØ&ò_¯ƒp)FXóø—ßUÑĔ䟅¼Þ@IDATR†x>•51Þ$‘nÎ,/¾Vb„aaõ÷Áâë™uÌkxý—¿Â%ÿC‡½Í?üâ‡ر&"¢áéQ4(3‰"£Øó<ÒÀ•ñeÏxÆ[J÷!Ðy«=.ïì^ç;g;Ú¼öÛÿ§×z~Aºû"M)9UWŠâÎûCüîhx]Ót݆X(êlßÀÏ/‹Cù»UêX ÇÆ±&®­Çc½]Q!%Z†Oê÷ÏÍžºµ³ï¥3Úcs3¤jŒÂ#¾³3Ú Æ6‚ŽHjaJ9“7US"Í Ã‰½ÜÜ÷­*ÃÓí«òW.ýÀz €D>„¶NQ¬¶S[|£SÈæñÊÖP”¡~Ðtµ‚=ˆ++—!·ç5æy^³g¹ÝUõƱ4Ã2}&øMšùà(ˆ1ï Èe“²ç×]Z">˜¡ìÀqÛ*“àË”àC «ìoUwÇ6Ìï´ÿðÇ:þð—;H…®w‡FªsíµÚ¸'{TBø£<ž– ˆÛÓ —àsP³±˜·Õpu¯)!¼˜ üåÅÎAöÂÊD|¥¬3Îáz^¯•Ûøœ³Ðû#«D[7òùΙ±ís\Vé/æcþ¥%|ðuPœùÚ'¡ˆße?0Ðãv?‚à¿KaVÑ$ïö!ÚðC{·Û4ކÆÁX<;ÿRõæç³§f íí··]W\^M.^M‰±QtʨF«[Ù[›í#Y‚æ‰ä<ÜZƒ"Ÿ±Sòù–»JÉ›3.¦´¥3íëñ•DÊÿ‹“7ýŽ5¯cìû~¶¯óßjKÿú‡Ú†t°äÈYñë™m¡É1M11X¢£ <WÆWÔÚ‡B50Ï7¤Ÿ}£çÅа¸hE¡)1×fÿÑpIÃi÷9 ^ºGûÜçÁzȵ'ÃÌë}k¥Qúê;† ¸àUóõÄzÞs³î[鎷už© –°e ¯-­»‡æ6¯ÃûŠò”JŽ…Æ¹PÚ®×Ó¼mŒ„Ž¢iñÚAÎÚo’a¾3º2_.U|nòôÙÆaÕîæó?ß9wn²½Ö S„ ¾ú ç5œýÛx.û¾]ðÌþô…žÙã݇A[Ûðo¯½Û­ƒÛœ<óÁc5Ý…ç@™ÚàÁÙÞ®zåuú'#9ž¯ÜÜ„HOCZÆ^Û›$’å$\¼$‰Ò¬ìŒc§4—›jªõ˜jo7ò!¼÷‚^òŸºœ‘.žãñáVŠ iD¶G&‘q±±Æ6«µW‘Fö’·Ù I[ŽM^:TžÎžÚ¡ë»óâ§§McÁ ›§I9AG$ù>¡ª]Bx+T¸ïCŠx%òþ>5ñoÁ°°y¶soêš¶v‚áX.LJ®ÆµO4¯Ç×°G5~—þäqQ BÊœÆuà ²”C¹ÝÚuç3lê¦MÉß"ìLÔgS¡î}ÄîvNÁhÖ0‘¹}Ûo½0y²!D0oÜß¿…þAJy3B ˆv!°•Z“ßs×Öþ—ûàâ©uÏÄïßùÖȱÞ#mûoS£—yFêߛ۶†TÔ[™Û‰Lƒ„õo- fÕhÿd6¸|$ÕÀâsÅm8ÊêRqퟶ »›øØá–¶ŽCsy®Â3…þس\Çâ-®Z#zÀ[澬[GàÔQƒé‹eèßï-¡é·^Báùc&“LŒ¸0iòî7„‚ý¤DÒ'zØO"h¸‡‰$§ŽkòY7› ʵ‰ KY]Íx™ÞîLÙ&2""ÜX˜DòyÆQŠ ÞWцÃÇÁ£¡L˜ÚÔH×<Þ–5gD‰¨¿§6;ÛTÃÆl"cjõ'¦La2iö¦|²ÍÏžìSÕ¶TϬß|}wöã OdOiâ6ɦlLšW?"û<cbgJz"®ù}ÎàY²gu»U¬Aæ®ÁOôë™¶a¡yTnÍ xw‹ö¦mìè`8³MjTíhG»H´ÙÖ¹b¯ÈÎÎ8ÚxË=¦ÚœyŸÓé“Ûÿ9mvC0Ì'޼ƒ%KÚuwÝócG1åú ±{ð²'¯˜ùï§tì°>4éŠñM*2)drÈŽ6N§“Xìðâfi$“HÃÇø¼Ö`GiÉ`æ“ ÂHÁæt’œ ˆÕÖ“É$K%Ùa‰ $çóL"MIf sçñWѦ{W¼ôÈì_âÒ*,ü½ %†AàÈ!€Ï>›ÄzÞ±Cjðœ?rƒ €žƒšHþ½nˆ&‘ K k-ÜO¯&o¸§I†ð›å›há×+é±»¯ñ9PáÂd’%Žìxã‚ô‘I%K"Nöæfi¤Ç8oh À M2€ÓÓæ!3!4¨(X"Éá|X*É1"YòÈäÑfH)½Ú)‰"Ùæi’ŠÝ„€!t©q½Œÿ¥œXÄÈ‹ÞM}j7~ÓêȸMQkòäteVœ€BÙ%Œ3’Æ0ÌÈjÓR¦$í ²;Iƒ@“I$•@„“Ko!’|¯^l¼ö£L&9”cdlƒ`šØq])‚@0"pû̹§k5®—ðÐïW· ‰lÛ, ‘lNR«“xaö´Ð/R.A€Sö5/û ËŒð@áa!†ôÉ#VÓ²ÔkL»H&&4×ÍÛ ¦}SEí“L6Ø‘šÎ7æñ`ºg¹AÀÛgΙ¨éÚæ9òù\s8ó¹ì?úבíƒ# DòàØÈA@ŠÊªhö‹Þw—ĸhŠŽ@èH.L,oùå8¨ÁmydâXU[O¯~¼ŒP7/ìÄsÓ%§õÍs\ÿµO¸>b06+S¹œ›Ü¯×êÉóÖªjêé•€i ÷ë‡g[êçW€Ø7¹ækîÈ~8Íír¾G¹(³ s ӵиÛõOZm“ú&,A|÷yà¾M{–›ñr99">ü»þFteë`‘<2r\‚”„ºç¦ ´uo—gÂqºôç0%oÈ?Ìä¿+šs¯ª·QêÉárŒkº>Oõ÷€Ü„ Ð ÈÀöÃïþ>wÔs›¶ŸcŸ£o+—È©fôÈoŽÎòÚnv¯²+ˆ@Ot¶éÄÛ“¦ºq¶é¥ A ‹@`] °4/‚€ ‚@°" D2XgVîKA@.F@ˆd,Í ‚€ ‚€ ¬‘ Ö™•ûA@A ‹"ÙÅKó‚€ ‚€ +B$ƒufå¾A@A@èb„Hv1ÀÒ¼ ‚€ ÁŠ€É`Y¹/A@A@º!’] °4/‚€ ‚@°" )ƒufå¾Aà°à<Û¼˜ÅÛ<¬kNñh#ݣ߾y\Ö‚€ ´„€É–P‘c‚€ ô˜ÄQÓ4Ò4ròKióî<*«ª¥ÊÚ:r8ÝD¼2xñh༠±Q\T%ÄFÒQ3©_F’7¸Ëà¹3A ƒ‘ì €r¹ L ™QÿÂ\½A ÙÚ¤êd·íIRhkºÞ¯[EKVl¦k.:•ŽœÎ ¦õ­¡×“Ïý~æƒG¹4÷Âqª‡Fk®ú4ßxñ‚ƨ…x4_¢xQ5ÐK0E ÿÝVÒ] –.‹N+Y4âw/>éÁRO“§Í®ÔTZ‰£K]ÿŽú¤|3òd—¯ÙJ„Hå´ÊM ‚€?¦ÒíöÓé¤oVn¡OØHIµ:]¹I§Ì ÿÚ²†Ÿþáù:¢Ü8¢Å#íôÜÛßÐeã§sO:Ê “" ŒçdRö¼ÝS|“âѧ85× u\-yÒ*uKb QbBQˆ!…Ǽó[‚ß›‚ÿK•ÿ6è#(d=^8êBª ×±(T¥ÇÅÐYå‘t–¢MeoQÅäé³_±Ùx:{êþC!–­«•ôD\­»6^±ZB-¤8—ÇåŠPœ‘ááOL™ÂQ)= !’=lBd8‚€ й˜$ÒårÃîÑI_/ßDŸý¸™†»A'Ä*RŽ“ì«ÔèË£z÷ëU†êÜ“…L±žsæ¶™ŒQ% <ŠÞ?½’´‘¹º!}t«¬JS‚h=Ôš¥”N^tJªæÚ¾vT–ZîO Ú•¬ÄmM×';®ßNš>{òü93þË5ïš3'µÎ¡U4e$D™#UM9Z#½_®kN”—¢â¿ËC¾fN5uµ4iÚ¬JA÷骞£*Ê&LWªºmåó³þ²/6¾pRº!’݇µô$GVg3‰´;´n[}þãƒDþb]/ùݱ(¤Ø,¤ÛÛoôÉdÛÀë…Þûv5¥&ÄШ!}Èbñã#G`n{K—“|0Vµ»‡én5KW´,ûèÅá ¶B÷ E3¿å˜é ¨­Kq¾ö‹µºæ~2¦^ å&I?Ic×"gƒ¦{@1/:ÝF–oF*êö4ý߷͘=΢ë§ÕÕi#¼#ÐõèzÒkt5²ÆP·N¡0-a‰8ëÍ5܈ëLFÎÒÏÚPŠ­ Õc«Ãi8¤Ÿ’¢Cûî Û§Ï©œ4cö"¨æ? ·E|úDö”²®½CiÝ!’þhȶ L"YÍ’ÈʪúpÉ:J„:›XÛ[R&¦¤kF—kÕªßQN…ÏþDöÿí ÉŠ!%ÄJŽ]ÐVŸXJ¿ûTŠ<fpøA®ß\B…óWPÝÚ‚öÞº[y”Bo|ú é“FáPgŠÍd»á<è…wΛl¯×/A<ªÓ`Ë8N«vñÊd04<º YZ¨ †‹;dÑŒp’²[@,Á(Q“œŠçŠšÈ#V0N:g£¦ìMT5·E¿9«\Q²J5ÊÂ#žXKŠÕÃOgk¥ÅϪ•Ifä—EÑDyq»7Y¹²6DÿU«Î3yú¬/t‹úL†:íÓìlÅ'Øl­9×~„H¶;¹Rz0¬ÒfÏl¶‰¬‡Gö÷«·SµÝIçÃ&²£êlwQåLÿ’BûÆQÒ ÇPò-'оi_vä#gn5w‘t—×SýÖÊb™¡&ÌøËé”:i4íþýGí'ãv&Š ÇÔÓ×+6Ñ…§o ° ÔnLÍ ³³_ +på^‰h¦×Ùk=çḅ‰`:lÓ ÐM†ú8ÊNí`©iËØ"ÉbGÃa > K÷Žta åßk^[j«3 »’³J—‘yܦn-Œ:=…,›2”ójCµ óܳs&ÍœýH†åô糳Ƿ_$ßâ6„HñäÊ­ ½¯4ÒkY‹¸+·î3ìÃ:ñF÷xȾ½ÔX,Ñ6J»ëTR#m¤Õº(¤/KO£ða‰ä.©£â—VSå7»©Èœyy*œTðôÆ~칃(ù¦ãhÇu )ùúc)öìp~uQÜ…ƒ©à?Rõ²}wÁ`ÕcÉF5Ëó(ÿñ¥ä©rPôé})î¢!TþáVJ»ódª^ºßŒšs®Õ8©hÞ s¤²”bÎèçÛoïãÇ^îß­Ü Ç›‘HB·**îöÂI ,°|µnëMyÎý÷C’˜kWÚFûf|Eaã)þâ¡F}}GΤô)§QÕâ½TõÕN_»æ†Ž:c2½$õ¼Tüß5æ©­Ǫz/®,õe¼¥>wdÏ9[7BH÷bz5¥]±R§¿Ó,§î HÁôðõ^ÁÒÊÁp¦»r¹¦^ºš(¦^Hí8üü#;û µ؃\'Dò ÀÈaA@\¼DÒ'—¡ÖÞ "ɆX'²3Š-%’¿| ÿèZ ‘Lûg-6š<&…léÑTøÜφ2ï¡ï‰í(YÂx¨Â6–ì9]5T·®’K;EŸ’E®¢Zƒ(º ë¨ú§ýsf_S ¤°Ô±¶õ[ŒmK¡>³Ï¦äßž`ØHÖo‚D'Æ‘ñäL@Hg…H>¨“g̺ÙåÒ~ w(ƒ™ì\õ“®²Ý ”ÎE€ŸÕk—ê–öÂh€ô?æº~ø‚Í:·—ÞÝš0óÞ=ÿr÷‚@P"à <Î1#!‘„·veM½ª¤³Ôkž•¾½‘bÏhäü0 š5É+]´ï,÷áÊÎ8¶Tx´T Ál­DBâhK §ó.öUk.ü«þq¿ï\ó vÚÙ|Á+¹MýÝðìÚzåDžŽjÆ1©PieA$oqºiŽþÁ÷a·÷'¤å|&úùë5%¼Ø1|´†%”ð7ì)??šÎÌså>‹»úmàßYϸƒÖ¿ÅzÆe‚€ œ;Û II‡ÃIµX:ÓsU«sRù[)ê눑É81ÆçÌ…‘ {_›%4+šÜåuÆ®5°†8& %tP¼¹ÙâÚSë4$’[.yÌe륯7©«;áÁpˆâʯ¦ÊEÛ¡V£°Á‰‡¨Ý¶Ó‘pú¨¬­7pf¼¥´ Ûg̽"ÜÇêté*½ÓH¤bU)êäLâ¨lþ’Ùø ¶4²!ÿ»Š¢Çö5NqÝþxÉñÙ sÖ€–ªwê±ÈÑ”qïØNm³-qýSw @é·Lþëvh’Ò ‘ì¥ A@è9°šÕTm»ÝnÃk*Ä.ÉíÜ_E¥ 7Sêí£I%–DºKëŒð@L&®AÖäHªYæ•Ö¯-¤ˆQ©Ú?ž"O£è1YM€sî­4Tå츣Fبæ‡Cš˜x5œZ`ïhM7®orÑAv¸ýˆcÒàò˜@ñ—0bIò˜;£p ='ìNMÕ¶¨·*ǃÔuÏk©ðÂ>£•éе±FÒoŽÓÕ)¤»5Š<) ¶»é­^©°ƒTƒ·}ìyH·—–ͽ Ý=­^×'9•TýþÎhê°Û8a§BtýÆÃ¾X.hQm·‹@FÀTm3Éá8’`–]v;Å/¯1l Sn= j'gê”9m ~õJÒâóÕßçýW|µ‹"ÇdР—.#Âó”Aª™t­7&%W({ ¥ÿéþñuÄö•Ÿn§âWRòÍ'PêïOFtfJÞÜ@uëáIpˆbMˆ0<ÂC²b‰%¨µ«ò);L:«˜„ñ–Òj\w#K4ÇUDüÄN-ÑãúR)ž öâ÷/üwþ`Š8>ª¾Ýã%Š~¦ ˆ3{þ@#>eæ}c)wöÿË!¡ìo,:ž½¿J<Ãl }Z_bZ¿¡Ê?ÛiØ'\1’»Ë(âÄL²Æ„_S½dñìq£É·žHŽ¥†SYĨª†c)RôÉY{Èl‹Š_YG,AgµøK‡‘§Òa8›9ó:爣·#œ’š“¨nr£²Ón„H¶:¹Pz"¦D’¹#{kì Ò‰5œ[üÂêp˜u3;Íì¼å}o8 ü0úwÎ?{îúÔ.jõé¡ÿž …X«¿ßKH7Ùƒ›Kñ«ëŒÅFl39uþA7~Ô½ÿ™çٳܸÆ<X»G é/ót¯XâI¶Ž#¢„ÿn`‰®pŽëÎ.ÕKöRÊÍÇÍ–²¿—ÜgNCÈ)8|•½»Ù¥€¸U~ 2ØPÜÅuFìR~ËßÛb6Ö,MÏœq&åÜûL2ðáňKâ•#)æìATøürŠÿåPJE"‰’ªªziU}³/M™”I8¿Äðó›ü›Q´ó·ïæ ‘'d4´5‚â/ÏÁ²@ ï©´ô~œèK( Òû¾K;®ǨßÑü]P…W2U92"ÑŽÞ@¼^TÛ=pRdH‚€ Ð1LbzC,-ãýî.SÒŸDú÷o’Hÿc¾m&À $Òw žr»Dú?Ô¶‘±+H$:fTM¬5ŽÞ~þŽì‡Ó4•R²ÊºæY,~e-å?õB=GC^½Â cìÕuR&¹Êê)|H‚×4f|ÿ&SÁ’>Žà.©¥º E†$0æÌ~ˆÐÏxùYL¼úhãù«Yžk\=®9 «)|(Ú,wK–,rq쫤¼‡@ÌÓ\ªX´ƒ¢y€Í+bÇ úm¥X6:¢q}n«$·ò‹†Dž¥’l»éʯ¡P˜‡èøü†dÄ÷Ãõ;ZVöÇKXº«Ê“mK®÷" DRžA@JL‚s$HdPz›|L³ÃŒF‹Q/ájvºSv«¾ÝMÛ¯{›ì»+(å¶Ñd‰ %%ŠG·W:Y»"×¶Ö‡°Š½`ˆ±è·! ´CÝ÷áóa^q’q©59Â'ñt#†j Ì7ŒxP8ËïÍÂQ»Ê)Ä0Áö+>ët³bƒÔ“ɨaI¨n¾ü@=Îá´<üw¤pJÅeƒ‰–QuŸ7çß?ý㎴'×6" ªíF,dKA@è,ázާÜúF"Éçk× :gGrC‚Í©<9(>ÉÒ…›ŒûbÏýÖJé[›ˆ.,Id•vÑ + õwÂ#Œã•_ï¡°qTöÑ6ÃËšÈIJåûb;ߘsRØ Êùz·q½ÿ?ÄsvdqÊ!5гC¹Avw’ÔJØtrv(nŸØÚ[v¦ý wìµ]»®Êž_atPøÏŸáÐu, ]økâAEpÞâÐUm)!éQÔ÷Áó §6‘àk¹”¿·Ž\ÇC…~¥Ñf $ÍtÌö+¾ÚmH2«àtÃöÄÍKÙ;›‰Õ†¾õ+Rl*íú݇†M%ÐO_d´ÏÒÉí×¼uPS‘æmšû%ÑDŸ£zÊ# YNåöù÷ÏXhž—uç D²sp”VA !À¤¦qéøÀؾ+tx9`ãÅŽ¦cÄ1©†=—ÙgÊÞßêµa„­X$޳£*Æê÷µøÊy¶Ù3µ~c¡áM{°QÆ_2Œ—R·Ò&¿Òü¸‘Þ¯aCÌ85+ǰÁἌ%;¬f´Ä"o÷Ïû[̆cÖmëÚĹ­õ{s=„Ü™S­ýwc&ÑQ^sÃNƒc÷íˆ"ú(™4®ßXLû¦}i8xéƒÅD“˶‰ošU(wÎß¶ÿ;Žm»êÍ&Îa|ž¥„ùþ@6„BŸ†.Ž·ÔgvÚ|ÞËþÍRùÇÛŒ…r8ªýÙß;…±äÔ_ñ¿W/jTH‹ŸŸ& d§ÙHëX¾’7½4iÆœóÅòܼY÷®=Èerø0è:cÈõB¼Ð†2a*ÒØK‡ÀœyŸÓž48ŽD㆟‹½?"P%V#jˆb»…úÕö§+Vø¾ZÚ<ä¤k1j–î£èÓû¤Ž0ÃG&Qÿ§& DÏŠ0«Pô©YqTJ»‰¤¯!Ùh3,¹p½N§Ðg£Ú™¢ÓÐ2G5õ5is{R±mÄà¥2¦^§!¿zÖݲ-M¡ï†Ò8Ê Èy~õ¼Ù3µ­%©ÕòúØ*rL&r ‚LÎS·¹Ø8’Mœ¡ƒ LJdÂ’ £,”¸¤kFaSŠ^\eóÿÇ×:öU‡9H©ëÚØf‡‡â—V“¢6ýš>Øq¾ˆC¶pÐgNÛh:Lø7Ìs8cúŸO'w™½[²˜ø÷/ÛÐãqºl•—@îIRè¥q }€ðÛS‰jC¡®F€ñ‘§Ó ?h–Ô*Š MYpÇôÙ}ººß`n_$’Á<»ro‚@7!0}r§™ À芠ˆ"NÌI¡Ó®»ëž;rȈ)×_ˆÝCÎfS_’T]C¥eåTXPL_®…ö Þ )œÇ8“÷Üý©ÑŒ蛳æ4݉<Új…í#OÌ0TÈl»Ø¤@©FX¸>^+!^[³ƒy¾6¹¾•ˆã3Û/‘4„m±€46/j¸ éó2(¬_¼ï½r[ŠYÙüºƒí‡Ãntè=§SLt…‡‡‘…ÓîFyü•E´iÇÞ/=2û—¸ŒS—@vD F[Wõg¢»æÌIµ×é·AåzÇž$Jç{@x ÏèÝšå˜}wG5à0˜¿\´VW_>"ÜVå¯ümu=h°M_u{ÐÀd(‚€ ô"K£Œ{ÆRÔÇŽ=^I¢AžU8­pQàtÀÒ>w…WGÉYDöþù3Êb)¥Ýuª¾Äw/\2yc‡.ëÏ s Ÿâ»® O.£Ýw|D5?îG¿§p;IäÞ¿¶‘ïãMüõÑÔ‘݇À?¦O/œ7gÆlͪþn2†ûïy-³¥GSòÇ5 àÍyà“¯?¶ûn¬…žâ~1„–sß¼: øç~vÝlH&F°I¾6Aw\·ð€ñаèr&Íœý¦é÷À‹X¿YSØ^¯S ‚ü,£>O•ÈÓ¤àEcЋ—á¤ìxáà€ã;n|‡ìÛËŒç§òË]Äñ#mil"P8ÉèÓûÂ+ÅP;Dá¥øYœ@ý¿ÐT®ÂÎwп~IÛý¹ê)ú”>°N7úçì:á œ\8³NÿÇ.4¬i&â?>v ’d8Œå=¾ÌÿT§o¯é£\›nUuå_Þx/j°u9s/BnUö àOý¯?à8¤MZ]SÆàO"ùZÎmÜ<ˆ¸›‡ÜYô—ü´Ôæã:d£R¡3¸}有:HäqP_ÿbÞù$ƒe›Ýšeû©úç\ÃVÖ?ëËW òfU3G`ӋسúƒH–·Ék^jWå‘-!ÂFF’E¥oo NÃÈRJv&ã€öÑgö§šå¹Ä6Á%on0ÂUÅà˜Y*‘±Q ˜˜Ÿ–Äó5übÕRa‡±¨Òê0ÖÒ5í9Vw¾Ÿ6áÔÂNuI{Úk¼‘”'AA@è&4MËN®&mÜ6°ª..Ïü»Üt#f¨Ù•æœrÐ,œî°Áó›­Öeäéf§ŽÀ™›Øt"æŒ~ˆ}Znd· Ɉ"_ÚCO%œÈÃÔ,vd‚ò/ah‡CiE2ËYqZ(‡rká’Ã>T ÷NP=HT¹Wµ…ßqØ ÈM"ÙÙ`@€ÓÄ5.ÁpG=÷Lœ{î{ÎÈî~üñpð¨C tµ;òB¹‹ê‹tUGNñ©Âì¢$¯ôíMTöÑ6²Ã¦’ífµZ'Yã‰`-¤ŽlY·¾È–׬̥„ËGBҹߵÇ£ ¾f›ÇŒ┡ÕKöp–tî¾ó#}iêïÆ4­×ŠÃXÓŠÛË…/ÑÛ£Uê—Pˆåœç³ÿRÔ±åj!’ò ‚@P!ÀĦyá˜ÞN±oK‡öÏk£T©%Ü;ÔA^[u<\kÈ^Õ}7ÇdÑ…\ðfaIcÞ£K)õŽ“hèÛ¿¢¡o\m¨¯YRX uÖßÇûÂô°jÞYv¦(´7‰445Ê¡x®]¦[B›ÆÍïvˆ— Sh]³[ÐÃd¤©iDXw}±EQ–¨jÜâDs°ºð°ß7Zö"M ‚€ ÐvX$â¿´ýʆš*dV&¡¡! 2J}bëh§®Ñ—G+œUä°Û=Ü 8UœŠ0&\8³ è®p46 ¥§ AœêXbC}jF"ùÔÌ%é7£Œl%;o|רïÎŒ‡®74•"àhÃø2ÎŒwû‹QÑœïö7àWΟ>.þÊˤì¹ÃÉ¥]ZCJ£Õ1p?ìM!pÇÕ*Jl½nÁbËP uãÅA ¼ `¿Šéð@·«ÁÑÌnÓ©Žf5aŠ!É+‹V´¢(¼³Xu#vÈX>$}o+º¾²6TŸÿÆÉ ³Q·d•w/ ÍÎV¼¶¾e£'#ÐøªÒƒFùñCÞ·#Ò„©,ð–"= 9ó?§Ó'wÚç?AÄ9Ú¢±À¥ƒÒ.úÍM·e <ñ±)¿V" žnkñx<ätº¨¶®Ž*+«©´¬œÊË+°TRUu5WÙ© ÎB.üõ+%A`ÿ½KâL*HI×$>ÿÚ‡Û¼Á nˆcE²Ò,,}4âóAýmØR‚{5T: Žyq×'’Cü°w6;Ö„bV†¥GÑ Ì$УĄxЦȤÆkJn—u öÿ÷øzeIÉSo>ÿ䃸¾ [ ‰8\0»¨þíÙôÇçè"|ÒO‚±É xúF€/vH–[ Y”MHbºU!m¥U±ý˜l™ºYÈcMb74Û¡¢Æ']‚@ïC€eæ¢íÛ¾uMæ€A¿Z¿#—N>z`›Ñ`u+;ƒ°}$KÐ\.1¹„*ÕhƒÏG‡Ø©¨ÎC¹‰Ã>‹ßZÃ,mÑ)ÄÝ™ï°Ügóö˜4úcÁSó}ò3E>­Õáúí/°›ƒÔROØÔqo6t•m¡É1 у%:ÊÀ“qe|Û«ÖæùÔu])ÜŸ1­o¾Û?x¹²ÓhH!øO4Ì egëj =œæôxú`ÆÒYJøA~y5Û56¼\ê:¤¡J ž¡ªÖuµNeù4° ;{bƒ6·h–{Í Y B$pÒdÈ‚@#`H^3³ò¬_¾lÇIç\P¹|ã®IÕ* ^õ¶ ‰ÒÀAïÇ$+£xI&TßVvƱSšËM5ð†®ãç®L§Õöƒñ$Gñ ˆ¶Â.Òø¨0ÆpŠydkl³Z›qå8Œs{ æï;hðùÿ[ôc̽7_¢† ”N[ “#&F\˜4y÷ÂÁæÒ"i‡Ž DÒívCr©êïÞ@"M M\XÂÈêjÆËôvgâ‰p?áÆÂ$’Ï3Ží)NäÇcü5½²(öèÁYú˜£*£gR[âL2brÄ’5VÓ²Ôk˜<šRH“@šë`†Ý$‡¼6ÆÆt¾1.'’Cü°w6;ÖhnwÕºŸ—>·ü›/– -žS^ü%’<ïRA À"`&Ãz ¦$ÛH²ä !²‰=l µË¾[µmͪí§^pÑxÇ}ˆJ ªÇÅF*a6Ž€xx¥9il¾x­Vm&‹þ¥ù¾ÿ¹Cms:KÎDT]ï@úf]ÍiUÎŽ­_,ýôãkk«Jp=çw¬ÂÂóÊó+ö‘AŠ ¨‘ Ô™“q Á€¿T’É#“H“í$³¾¾ÖéäÇ8þùÐQÇ÷ï7tøð𨨄ÐððX«ÅÊL)ÝȼÛír8ëí•õu5%9Û·nزfå6 ƒI#;Ö0‰äÄàfÓ>R¤‘EŠ ˆ‘ ÄY“1 ½©¤I"ý &K³8bè¶õ«7aÙŠm&†ÔkólJébL"ÈsÆ‹iãÊsÄv¦*›%‘L"E ¤Á€€É`˜E¹A x`RÂÎ\˜¬˜ûLPØ¾Ž‰$;â°ê›¿ÏLõ·H€ÑÍÅœ&‘¬®æy3½î™82™¬kXxþø<ϧA@`„HðäÉÐ^€€IN˜tøo3aRbxscm8â`ÝœH ¡(]\x^¸˜ócIV[›„Ÿ¥’L*yŸ›v‘æµ8$E!’8k2fA w!Àdà Ó\mÊ’HiÚR2Ù}Ï Ï‘¹ð\ù“I~ `òÈk^øœH"‚A " ³(÷ ô˜|0Yá5K´˜œ˜êl&&‰I€ÑÍ…ç…‹9G&™4 ¥¹ož÷Ö–ÿ‚€ ð‘ ø)”zLXL‰–i‡g:ؘRHSi®{@GèfM"ÉksaÒh.æ±#4<éVº !’]…¬´+]‰€IL˜¨p1I¤wOÔÚ&ݹæ91‹9?澬A H"¤+·%ô2„¸ô² —Ûž€iKÔ3F#£A@A@„HÌTÉ@A@A@èY‘ìYó!£A@A `"0S%A@A g! D²g͇ŒFA@€A@ˆdÀL• TA@ž…€Éž52A@A@!’3U2PA@A@zB${Ö|ÈhA@A@„HÌTÉ@A@A@èY‘ìYó!£A@A `"0S%A@A g! D²g͇ŒFA@€A@ˆdÀL• TA@ž…€Éž52A@A@!’3U2PA@A@zB${Ö|ÈhA@A@„HÌTÉ@A@A@èY‘ìYó!£A@A `"0S%A@A g! D²g͇ŒFA@€A@ˆdÀL• TA@ž…€Éž52A@A@!’3U2PA@A@zÖž5œ®Íïf>p¢Gó\€^ú銞¡EwmÁÙºNT­èJîn¯Eµ|öܬûVçÊ] ‚€ ‚@k=‘ÌÎ~),וûUU¦¸5wº¢(ztD˜¡„Ùl"‘míé8È9»Ë¥UTÕéÕuv˜Î™æ¨Ê¨Á™j6/ë PgwÐú¹´|ã.}ÃŽýŠEUs5]»eþ왟w Y¹4@˜3¯ñs:}rû?§r»­ów3¦é® Ðtœ®“>dä±iQGUíº¢8IU4œä¯9Ô—Ôõ,VÒ´U×-Í^p!ÿ'-e9xçRÕª/~îoÓ—óKqÓdOîG Ý$­+‡ÚDrÒÌÙÒ5z4+5Ž~uþÉ–!}ÓºrȽ¾íí9ôÆg?yr‹*HQéÏógÍx²×ƒäôv"9){^¹‹n¼’Æ!<Ý6[¥ÛfÛg )¢k1Y¬•d±T’ÕRƒ³mã} “ ”‘äöD“Û€%ž\ÎT²»3=W,K2ñÓ÷ƒ€þ'ÒùØ“ÙwWð±C•»<ÜUåŠ×].›b³¹œdqR˜Û•aŸZm0ÛC5!çA@8 $’^©?1zÄý†KÆ*!6ã»÷€›—‹€Óå¡—?ü^_±y·¢¨ÊÝB&;ßžÖZo&’“gÎ=•t÷›š®ô ݧEF­RÃÃv€0Vvé4y<‘d·¢ºú‘z]ýÔ´ ºñkæÍž¹ˆ;ž”ýh’¢ÙOÕ=4‚ ’{)ZÎðØ–‡7”줫98¿4u#Tí+ÃÂõ•ÿ˜>½°¥kä˜ &AG$Y­ëÊ'£GôWo½âÌy&øÁº~ñÅ “{4hÞ.5w°Î2Q0É??òHdm­sæV²`«˜¥‘Þ²9Ø1’:h ÏV(!pÔËRK¡b.ys£Þó6[‰%)á]KhÈþ#2ÉO —^¥ÙýÜ “Ïéº6^'Ë(H>ï=‹ZãdÔbµ–‘j©‡:½ž,*›bzPÅމ…¬äÑ"ÈãŽ!'VsyâÛE‹¥Ú“‘þ¬Å¢Â?æÔßûòþ™£–£„…îTÂCwAÅ^Dªêh×ÈXµît¥‘Ó™I{?ª³ñè:Ô: Á‘NY»ógçÍš¶¬]ËE‚€ t•×6{gãû46‘$$òÈ=«Œý¯/8ÙòØ+‹2s5žzôÈFzî푳ÔÔKÕ5§ít¦¼1iæýp4²Ýù¬ûàQ.Ez#>UM ß<×4-Cüú½Ëøy.xNxn‚åžä>ÉÙ³G¹nËfM§ÃB÷§¦¥¼D}Ò³ÄÇ~aH ã.zÞ(Y굂2Òž±$%¾»ËÚÑŠîþqòŒ9S{ÞheD‚€ Ð ‘äŒ5¬“â8‘Rz<<' Ù„zÆ dAäOÒÝúªZ=0%åJK}A Ûô÷ÝÝ7¹†²2ž°DD¬ƒý¨öàm3f¿š­ÍoJwã)ý Š@0}èûqÆ 6ÞsEž žŒ¨_Ï•Œ$˜˜4}ö4HÁç……oµe¦?c‰ÛÌ·{Äï%”ɉoS|Ü0OׯÍ÷ÌùÛ” @º !’œ;›Óv+zÒÙ!à9á¹9dE© tÈ«šgvTäZJMz (]Ÿ­36ª=æOÆ2¼ßEÛÁ»h¼üò3æQFòñ¶¤£;¯ZuÀñ#y 6æ;¨¼—ûfþ.{Î1Gr,Ò· t/AC$Á £ƒ1wvai!ŒN‹Oò]ÓÇ߯¥Úúö…ùh±ÑN>ÈsÂsÓÉÍJs‚@nÏ~$"±ÿ††í¡Ä„÷šœëÊ8ÉÓ¹‹¬Öp5h"ýñêU”?¼SºÜ²÷#ª¬A|ðæ©£,[ó£G|Ÿ¥’pHÒ=ní†#>€ tAåµÝm¨¡£œüRšûï.­V %ÇEÓ§¢SF êÔa|¸d5BŽxhò•gÐnYu-}¸x58¢?E†Kþð’½Çg=,9á˜å" d7æ¢oW=`ôxÕø—èØÁ×ЗËÿFœü­Üú9*j­bµ–7;Û}»!¶(ƒ¼—o6:œy]yÖ‹T]W@û‹VЉÃo¤ñ'Πû>§„Øtíùo#ØwÅG÷£ý/ñ^“u6õM9…ʪv_‘j¿â¬ù”?’6ïyŸFö¿ÜwS)ñ#èWç¼FÛr>EêÄ šxösqQ}éŒãî¡£]M[ö~Üå$’;EFr¹<à^ä l‚@Ð# ÉNqJB $‰”•’@+6í¦U[öRÉÏ.¤ÉW§w¾ZAEeU4÷Ϋ© ´’þ÷é2Úif|t]|Æq4zäzòµÏh@f2ýò¬ŒÑ°*ûé7¾ [.;ƒ~Z¿vG:]7á4ãÜGKÖз+·Pxh;ah“Ñ»ÜnZðùÏ´zë^¨Ú,tö˜‘tþ)GÃ^§–ÆÓäbÙbz`0††Œ.G¢X-a4ù²%@í-XJw7ªÖ7íù€¾[ã £zþɳñýðâcÛ@&§üz#%Å ¥õ;€^F+6¿dÄ »â3Û(Ue‚š™týûã_Pqù ±FÒÙ£g·:´ï…†ú;!fˆœÛ ¥L.¹Ø¬aDÞ‚,5µÆ~Wÿ«¨<ßU*¤Ïtu_Ò¾ ôD"ÙIs±· Ôh),$ÄX—VÖгo~Eá!tÅ9£¡žÖè™7¾¤z‡‹®)˜•B/¾»ØP‘ÂöÒu;|#Y¹e9A d$SU­zãÜþÂ2úè»5tܰ¾ ]º¦©Gê’UÛiýŽ\ºé’3è’3Ž7Hlq¹7Xqóñø:“ A Àð¸=†3g¦9Å£9éÝÅ“éÉÇÒÂo›ª ËÖû†Ù—ìN¯½3«œ]îzŠ O¡ »ß¥ÔÄ£ ÛÊÁH®Û±Àw o„‡ÆÃöÐée‰q¼º¾Ðw>:"´Ò‰\ÛôõÊYЊx¦³T³;H$K"Ë+. ÊÊ3‘S’^D¶›%¾ʆ ="‘ìà³ c]½“öä•PßôD5$Ó×"K']1ÞØßº·€J*ªéîë.¤aýÒ Iäš­9<{ÌÃifçþ"bR¹jóI%2õøÚâ-{òALC麋¼ÒI§ËM¯}²ÔWgÃŽ}tÌ,ü(EKZR¬!!e©$ÿñø.’ A À€>;¬ŒÜî¸#r'LàŠÔÙ­ `ñ |ö'@ DÆ:j”Sø#¤xnÚ¾ï :ëøû¨°l•V6¾Tr{ìpSQ³ÒÊKasù2 ís¡¯›-{>¤§I›¡¾®©+¤Èˆª­ë>Ír]ýª¨ïq:Ò,ðzäÜQC§½àl‚@o@@ˆdg™ÕÆÃû§ÓÄóO¢~éIFk|ŒËÑ~ÁÑ+ªëŒc}`Ki–>©ñTÉ%«Çûg$ÑjH"£#Â)·¨RËÓÍj¾õîÜbꓚàÛï›Ö¸ÍÏm9ÄdtŦ=¾:ìõmÿñ˜Çd-:ãGÚûÅú-y5uǦGE­júöÕƒnîóŸ§Óg½@wM\k¨¨?ýiªA"yˆëv¾ ûÆ—iÑðj¡|½rŽA4Ç3…6ìzÇWcOÁ¸v]wÁB²ÂQÇ£»é¹wüîð]ÐIg•”]áq9“,¤è…ªE¹cÞý3ÞŸßIíK3‚€ 8‘ìà\]zæ †dKÍØ¬ð&Ç{#àÀ^r $•\ ˪a ï•`Ž9j-^¹‰â¢£ MôËæm¦'ÅÑ2ØLš¥¼ªÑöÉË$«ÅBg8œ.?»iFB“ØúÇlCÖ‚@ #0qâDÏä³´ÛigN¹¾Ñ½çÜQÓ‘Øá¹bkB›Öh}Ïát#´‡ågó*Gƒw¸AÊÃÚ݇ÙVG׿²Ž‹çÏžyVGÛ’ë{sç)9ðÒÍ‚Ip›>£Ý3z…*«Æ‡¢! +<|+EE¬¥°°½ø¼4š‚tÏXzg/ìôTXrƒÇíJË 9ÿÞ{½®ê½¹kA èÕö˜â°ÐŽ¥7 i}Ú,?žaGàΤËÞ„À´ÉzS£¾qó,$]B·Ï>?SwÐ${ýðÛëêF }"Á†¸Òû%&êç1Ò`„ÕZA)‰ÿ³äü>M¯uÿ÷é z¬7,÷%ôrZg$½¹}A@ ,¸Ú²½ò“$UqfÀ%]Ñþ†Ø’º»ªfÌŠ²ŠsÎÖ´ð0‚e³”²3ËØcþD…å› uÿÜ×ì¤2ܲ÷ÄÍ÷ëÎ Îj3é²oéÉ7GuKÉ–îÍf+†™AžârfÖÒy9&Áƒ€Éà™K¹A èøæ›³¬Kw,Od‚¨x´ ÈÓuE7Ö0ÑYľNÛËßNǶª5 à„HiÙÅdwôG–§JŠ‹û†¢"W+ªÒÔ[ºc€)4öØ)FúÁ>8Lj÷Èí4¹µKÖ¶J$ÇŒ¸×9ŒüÛC WÃxUUø«½sIs =µzHQ]°\¥ÐV+ÉIA@x„HüÊ ‡Äå{–'y<Ît&ˆšB,Ad2h¬™b;}ÙöÅpéQL‚hÜiƒ‹OÃꀛ¯ª>ÕÈ´¢(vJLøÎ «@©š´pÀ59°'ÿ{:ÿ¤ÙôÖ×7ÐŒÍA£]I}SO£­¾9±³’O¤“GÞNuŽr8Ç£å[^¤cýŠ>Yöãú‹Çþ – ^äB#5âQ¯ Ïž³étÌà‰3¬Þ@:ÆeFýÑÃo¦âŠ-ˆc;ŽÂCâé§MÏûÆ¡ª6:çÄ™´¯èGCJê;Ñźn%‡½¯¦èÊò.îJš#Œ€É#<Ò½ L˜‘œÎ ´t‚ˆx¦9„À,‘%‰"³ƒ°BÔikAWT^RþK­ºúÄäˆÈzRüûŠªÚÛz}»ë-^ó0]Á;4¼ßEµK@ íŽ Zµõ¿tîèl8¿©”W²©«Œ¬6L.Ë‘Ê âÒõOSXh,õK9•£DrXÿ áe\wËÅ‹hÝ®·hbNN8ý úzÅ,ô÷1¥%G§º‹vìÿn>ôÝ‡Š´Š—Ž}Š,T¾1(»³TT‰îìFµ¼ÜýJ_‚€ Ðý‘ì~Ì¥GA à`‚¸fÛÏÉ.r¥»­BŬ³jYÏ`rˆ›b â! âaCƉE‰e ‰ùˆßum>b:äûª’‡ØyÅ’ß7æ’ý_­?îMÓ^‰‰^J ñ‹I*·Ò…¥ÎQFKÖ ‡»ªð½zòKÖA¢xº‘{{íÎ7èÄa7!ÇvõO‹Ø°ÏQ¿ÔS‘RQ§oVÌ6î$>vˆë%‘äeÕ;}͸è~F3ŸJ±‘YôêçWáZq¬;þ9}¨ªjœFºòŸçgß·º;ú”>AàÈ! D²±÷ë•ôÙ²õF‹a¡HgOWŸw2eaÝÑÂy¶û#cG=¾;:¹>¸0 ¢]uyTTÌȸ b¨§7!ˆ¦Ñ]ƒ”Пúo·!/A9Ds ‡ H"È"v È"D]³æ Œ¿8wâÄ·üXPCç¾Nø” Ë[4iú¨Y¡!yZ|Ügpªé¾‚ñ"öéèWѸcÿbØ>rïL­–0Ò¶ËžüÅT[_bl7ÿ·+ÿ[ê“rÅG÷§oVÍ¥”¸á4¬ïE”7Œváºaȯíp5¦;µC-…¼ÚfÉ/ñ~ï˜û¼N‰A 1 OCòƒ½þ§ºlÛéL£Ââ< ýÛ­‘öØ–"AŽ€ÉNžà¸˜úýÕçR~i}òýZú`ñ*ºcâ9êÅíñÐóoM÷Þt1¥Aõ%E8-DM3UËN* ¦šùL›Âæû‡êç}¶†‘É!Žç« ‹ª¢æ.AlCŸ4)ûÑ$ÝUß?2j-ÔÀÍÉf[ZèXNU¸è§ûè†_¼Gn·W^][@¹Å+Ió¸hùæ—Œ"ÂŒu=Ò%F„7AN‘8á´Ç(,$׬¢Ýq‹éH% J×Q½½œö£ !iÌJMÅ•ÛhHŸ hÛÞO[ô›_]bûtÙ¸Ò+‹.'ælµ~GOÚ탨¨äWˆb’j =ç™û¦–w´M¹^z>B$;yެª…ú¤%KÝIo~ö#l¤\†$± ´’þ÷é2H&J!yˆ ‹Ï8ŽF`Œàßï-„!”&ž²±ÿó†H‡¸–fÜz)=ýÆ—FO¼þY‘S{ή2ê,[·duÕÔÙ´ˆ×þâTü8…Òëè#+5J+jè;¤Qüý¯Î!Ί#%ð`‚øó®ŸSoÒBx1CµÌÑ+9Ô fÜ%«˜JýÉ¡ÿö!ÐÑÐG)êäûD¤1ñ’E]ÍC¶÷üÑ ¢¿Ã<]GTïq¹’,‡ya§UgÒ·zë«tâð›|m~µò~:íè;éΫVÀVÑFlO¹zÛ«´nç›ô‹S¡ã_C/~x.ì&WSdXí†ã«¡wå.¦ N~€¾_÷¤ÑVIÅ68Ṳ̈+Çÿ ç58í|Bëw¿íëç`,ݼñïÓÙ'Îðåç>XÝö×!~¬ª §óÀàõ-›íâg²§ò˃A@è‘ì¢IvºÜ´ew>%ÄF$Ò]á3 „a!tý„ÓhÓ®Š2}¿3[ÒB ”ÞQTì ö~g9¼b9 ôðNþ ®&¡ˆ§ž•€§§ÞyŠå,‡¥©§"Uº´TZzv³³3ÿçÍ.›M²Bx¿ß/™Ùo¾ùʳ3;ϼuôiÔ.)ÎlWRVI¯~ô5]ÑitR—&A] ÒxÅ9C©´ÂI-]…ÜÞ1tõùéS;‘bÖ·~ÈÉ9ÍVlý¹Ô´¦ŠY÷Ô„¸©Q5ƒðuQàÑ ‚ØûØ­]꺟k·®õÉKMBÈvˆP1+,=„ÊÙT7«ùQ6k^÷økòBU1×ê½™>Ìu¤V"»Õ‚òòS¯NLøÎb³Ö¯Bït šùzZ]~±|Z-–·w½³èNd¢Š…DápðÇe3œ_¶îùÒ$—nÍ{ï?õÖ_%{ëûÇM¯Áiç5Óy&Pºøß¯ÿì?wX8¯W\Uëx8?À§Šö¼Îãru±@ü¦bM¹{¾‹pŽ!} ‚@ËF@ˆd˜¿Ÿƒ¥ä˜ó„ã(3³Ë¤Þ8Úaëî"Ú‡ºûo¹‚ú#G6K"Ú´‹¾…T‘‰d°ÂYjºCºÈ…m$;µ÷’µ[÷Àþ)ŽF èiÒ§­Ú¸Ó$’\áti”uÏUf*F³ü;&D¤®D(„¸©qLñ{1ƒ îW~ìSº ¢©°5ÿ…´öbf6uÈI…jœTŽ#‚ÒJkY åOšb=£ ð®”Ž)ÿ°Ø‘º¥·v8·Òu ÄÒk?y$ó $‘Gr^8Ûj^~KΧ²Šx÷0öª¤ü%'+ý­pŽ!} ‚Àñ€É0O1Ñ6ºhä Z±þÙÔ«+„L(Å réžrÈñ†q””›õuÿÁû´nU­Ï?ÿ’¯Ï*šñò‚Cõ4¤O÷!‘‡ ûÄŠè )nMëÌ‘ƒcƒãÕ¨–á¤bÆC¤ÎõÄ:d°)ަý¡OÝŒ<«jÍoéİ!èð…ìôÝãÓ/ÒݱKò &´OJX¦&%- ìÈÉZ$æ×úäïeeg•ƒØ²Ö…Èç/´‰·;øá²Ö°>Yƒ 9B$³ψ¶Ûé‚ýæ£3=šó>±ãÙÃúP‡ä󼂥~rYx Œ†ôîjÖ³ Ûåöª½¸bOQ±Yì_t”‰ã(ëÞëmb·3S±zçs¼Tú¢Ë¥"Õ^A„ä…ƒc›ÅýôcŠá<$A¬Ã Ѭ¦øw|A·¦Ñâ^Ì ˆx1Ãܨsƒøƒ°M´æœtM~KV1]]3˜ë˜ºqÂŒ<åž'ËðβòÓ<‰ ß[–“ª.l¦i·Ã†!A»ÁÛ|UVœ¢iž8+^’JA ŸŽQŸyvêÔ½Çíâdâ‚€ „H†ÆÃ;Ii›H£Ï@ï~¹‚†÷ën†JŠ¥Ïÿ·Žn¸ètZ·m¤”4´o7óä>=:җ߯§ü½ÅT ç™õÛ÷ø;å¾x:l†db\4ÅÀÎ’ûüæ§Íôåè¼Sú‘«ÚMEK©O÷Žþód禊YÝСXjœTŒÎzMö&ˆà{lƒØ¹!‚è·; òØq/N9ô9¦"ˆ;©(Ö|!ˆ‡¾«¦î½8e { ß•š–õªaÄ?|°ä’+‹KG±1?«±±?ãþÙ Rù@å¼Îðâtž„¼Ód±Ô¯}hêzä|žOeåR-UMT†9UÀÍW¦nÁËl+cèÂÓúÓ§ßxcÃÅÁ{Ôihþç?Лðÿëý¿£a}»Ó¯F@x¡Uôö?˜D󲳆œpDÒGIÑ@&x0ƒ´ÕÄ?„s A4Ÿ˜øÂëAA¬SÝà•rÈ ÷¡/Ó9Åë˜'ÄEéÏg‚eÍësMH„2bs²Ó—¢ó¥÷:¦÷ƒ°ÿÞʪA¿­¨œÂô.&z§µCµGíAÈ=a•Vjž$NHýaábUíöB½sÇ9ª¢„ÄŒØÊƒw¼ÿÀµT^qjð¾# 9!Dzߗêñê²¹“'—øËVxmyeÁ¬Cà®~ʾ <6÷íÞéÂI·^BëcÛ„ÃEÙm ‡ÏÃU­áXýüž=ÁÙ0©®Úº ^Úl›iµ´„€{#<þ7bœ¸¾»hhï5cÆŒ©1ÌL?¬w©A ©‘¬ƒàÎü}´~[®™U¦¸¼Òô†®Ó¤Õ~ô=ªì°ßlƒŽí’âip¯® L?R0êD¿“ŠâjgöbVmù¡J… é7 í[9™“WcügSÍ®w×U­›â1:Að˜€{#„0žm€qßxEèºâF€2Xh–ãx)ª‹ìÊ‹±ä×ËqŽoÙ ‚€ !Nx" £sÉkôÕòŸiÑòõHUÃz¢•*ŠRJÈŠ˜»'Z»m3’È ÕÙ‹DªÅ(ºxäPºèŒpB0ýŽ·Öa­ªZïˆÖmyûPa(*f!ˆG±4>ΨQ³oÂ2øOŠ Ç ',‘dÉÙcXúøÆ'ßPq¹‹:Z·RŸ˜ÕØn"»Ò@ÆDKûµ·îlá_WóNÉtwÛjóoŸÖ“ÖV^GϽµn¼ø ºäLDN‘"‚€ '4'‘ôI!‘çË$‘.]M]­ëéÔ˜÷È GL)Á`’}¾e­ªºÞYÈí ¨»›Æ[ÁÏ’#‚€ ‚€ К8aˆ¤D²=äÚ­»é£e^yFì[aû~c»S×ÞÜ×å6Ó¾Ýßæ O2ˆöÝϧÎýMkþ_Øæ{¤1Ù6ñª¼©‰:µK2ÕÜì EA@N<8‡ê QXÍ$²¼¢ŠÞAJÁDµÐ”D†sñ± ’½Gþ™Tk u8†.¾ûGJh7 ,C”ÜFE¿|–¾šÚ KpÕ"úׂ¯©9¾¥‚€ ‚À‰‰À !‘dÉêlWu5-^ñ3•TVÃ&ò¿Qg7mþv†y5¸æê6øwôóÒGÈb‹5¥•m»žC[Pá¶OXØ´~ðè´sõ+tòˆT*)ü‰*@$Ût>ç}FíºCñíú#pq4%w=“ìùí\ó&vQr—‘ÔRјÄn†–‘Ñõ_…7KK&‡F}D_—ßez»_~ÎPN>aÞINÌ_ Yµ ‚€ P­þéÏ*mv¬©‰¬ªrÒwë¶™ÞÙ‘v¬±Úã).¹•íóÆ5véß(1eíZû*õ>ýOÔ©Ïտ׬¾ÃIÓˆk^"gEÌ_b؃X½Í…÷^à ¨øN”ûó[&ÙL9ùRä¶Ñ©WæP!ˆê¶ïŸÆWÑŽUóÌsÂýñc/÷E+Ö›ø2ÎRA@AàÄB ÕI¯4R3¥‘ÛwR¹S£nðDŽTQ­ÑtÁ­KiÔ?PÅ-”·ù}¨º£©CÏ ÉUYD‰R§¾×­÷Í-ó‡´å³ýdÔWÏ[ÛI—=éæ'´oÏ×Ô¶ëYdmgþ•î[Oeû7šJÍ]xZX÷ÇÒŠjÚ‘»× ¥¬sªÄŒZôÊñ“<ØB¤^A@j!ЪUÛ~i¤ÛMN§‹6í,0ƒsœÈHÝSM«>I¥ò[ÉнöƒÑ2™4tÍvÿî%äªØGöè6õÖûæVºw­o÷°meÉNS5ÎtÍ i$B§—@âù:÷ÛÏ©²lmýþ)Ôvn¸*GÞ¾~ûêÙ¥½©Þ®Ïñ¦ÜnY´»ºrð«s !·Ü6;0‡Hö¶C1”Ų£m7uçâÑwHðÎp}9Ò ‚€ Ð œDÒ'·©Ö>XV‰œ-•‘ 6nè‡I xÅ+‰í'wüôŠùµÚcÛRuåþzëCûÞëW%·éx*mXšNE;™3´¾Ž®Ç™ä @û‹Ë¡Þö˜Yoê#’fïÈç†wÂ~'ü³¼uDÈ ˜íV !šG÷=ÈY‚€ ‚À±B UIoàqéYìt¹¨¬ÄG O8ž#ýÂX Ýëô‰tÑ+H±ØhËwCzøOS=]_ý‘öïm¯@}¾—N¹rŽùÑYž_£þþøèº á,N#É9É™H2Þõ9ݨ†áD„ +Ì(ƒ_oB4C@[š‚€ - àö–5Ï£š®CÚ‚Ã!j\®jrÃsÛJÕGÕW('íßó-}þb¿z›¬¢ÿ{§é¥­C2éS{«_üÊÈZýìÙð&ñ—À}þ¼îˇx§›ñäÑ*0‡þ°¬¦“Okzl‘ä\ä®jÍÄ™ñ®¯$¸´ïE·½x«»´›æÑOÒ ã$C¥“lÑþ$Ì“@4» Ѭ=©A@Z.­–H²}¤×FÒƒÐ?šéµÍDçX{yÜ•õ^ Áêëm¤ÒY–‹ø•7Qg8òØ£Ú€DÞL›¾¤uøª}8û0¯O½ýö˜1ŒÈ9(ùoIÝÑ3¾Eˆf]Tä³ ‚€ вhµD’a÷©¶YíÊq$Á,[ö·ÑÄÙåoùˆXݦÓÓ3|ù·“ËH‘d¼¶щdSç#D³©Êù‘F`ÜÌ™ITî>S!õTƒôS ÿèíI¹îÍíqw€3¢àGˆ‡êû-ò¾‡¹H/'—–©éвls;šoÁ ›!áÇküwO;î/殤‚€ ÐhµD’Áõö fi–~Ìc ‰ ï¡õ(D34œ¤Ux0Éc…v~[n0ÊÜç w ~m¨Ú¢jUÑ6‹Óª(اj«JlS•4–C¢˜¿FØåp*›Ý`kÅï”ÍcàO·Ú5½SŒ[O‰vk#­šÎÎjTá®0îNËZ ‚ù–j³¼1Ç1e‡Ù™üAà!Ъ‰$cê'“ÍD"co»c¯KÉ Å’‚‰pØñ¥)ÅÛk^¤ÞŽ@äõÛX6¥ÿpœÛÒIºÍp|ËÒ‡q996cÏÞiz™{¸`lE´Ís0Öf)‰µQy´d]ÃSL8Ì8$SH¬Ò”äÊê! N÷[{l\zöÜXkìÔºRÊ¿8žjS婈×犡÷€@4™JÆKu2öm`¯nÌ?LŠDv?8j®¢*{ 7Ý­ªöõs…gúÒ‹ ´vÂõc×Úq i}{û»OÍ\ÚZu¼¨'˜D2¤“h¤¹J)oÓ@"«h%‡š‚€ͦ ×:·¯e~é¬{Œ$Ýâ±ÀªÚjxT ¯N1´–8ë&O.~ôÑDåææ.‚àñÌ¢Äh%/9–*£,f»H!bJ%±vóow»XÕsŽÅUÔý€s\¹»âÌ{ÒgÝâQ´óuC¿³½ Â]žâ› H"–¡èšÕ”†*P™+ÉÅ]uHA H? $ FMÔÇIw§gæ#YÀ ›?¨ªå“9™SVùú“­ ´Z"y,$díºCžêJ8ž@ S†R¡·RuÕÚ³þßÄ™i¢âR¨ç)wQB»È„³ ÁÊ_bêau†î¡vÝ/ð“ÉèøÎÔmàŠE.ot`ÏÿÌ¡žAp¸é6ä÷Fî¢Ýëþ‰€çÍûû,pÄ9RûB4#…ló÷Ë„ñ«µÛ†é†vHÇÅr/V›¶Œ^Bè÷óî iåAÅ39Y ¹±K‚²?>ªù€Y]¾»]•ÇØ,ƒö”ŒpÕXW^m³xŠQWa·PU”•ª°uZ- ¤‘u‰.–p(ˆ…ŠeÚá¥Aò‰p^ñNwç—çªh—vGײǦeåƒ~hQ”½˜9uÙ1Y´ *-þ1iqeÁ,¶jZaÃu2V9‰J¡ >ˆ”Ó¹‰wR›x¢óâ^nZçAζF%Ò…·.£ý¹ÿCÀñ'¨âàV³eBû4òú·hͦø¶ý©Û ßÒ²Ž¢A£²ÉÓŽv¬Ê¡¤Ž§€ø½AΟvX§X¼à¶e´pîÒªËéâ»VÑžoSÙÞõÔt<Ó”‚r~oV}oýþIjÛý\jÛå ŒsQÙ†§úëŠ;)*yÝuݹ”˜O11Ñd Î<ùú§´ewÁ’¹Y£Â3âñÙK“Ã5¶lxb .§¤  ‚{Nç뛯D°ßCµ{-ÞØb¸©Û¢z Š¶° £ nþIóºÆ(^;F´³‚>Z!„ôδa,‹±¤‘AFkÞênv—Åqvª²Õå‹M› ¯7¹¢š’+ÝFr¹K·è†¾æp­=CÖ”×ç:R[¦­MÓ–-g ‚À ÐR%’lTh?‚u´ˆ¦¬‚^öÆE ƒÐyø‚v®~$ÏA{_AU¥»)®ÍÉ8jÛ¦'1¹¬,ùÅŒùXZ´šv®} ¶”®zë×®ÛÙx¸´éë,³:.¹7uBÜÈ‚­ ÌÏy?¿Mù[>¤’½k¨ûàß”"#¤ RŽ-"Ñ<6øO˜1#ÙSéyžÓ@“ܪų/Én)±Ql]¶€·žc3Å&º§ml“ûÖ“j&̬ÂWxKû2u=èërÏ1´½ŽKËz8'+í5H+›üòlR/-I$ñ£ôT¤ºãŽLrþì5ŸÝG»×¼NgÞô.åo~ŸXÍêi³ ¯ô¦¯3Is•Aù¤Š¨ÏYÐI§¦Òÿæ_Wo]à%›Øç–ú«Ü΃¦ŠÜWQ^¼ÍÜÕ5ˆbQ”°Ùü›ÝÉ¿! D3¼À²3 íÚû »\› µKüÞ¤*Lˆ¢R8Äã9:¬a[é#•jb•›NÞWÙ!¾ªúã2²&NpÌó¢cÊö£ëYÎãI$¯zHO¨ür—ž¹¸o÷NNºõ óB^½viY9í?p öRÉê0"òª›Cîø&´@îê23¸®»L瘪ò\*Øüµ¿äBʇÔÐU^h?WEÅ&õ¤y?Ð÷ïÜ@£îøŽ; $èÃêÊömôÏâ`þJ ‰frçÓ©ìÀfJéu9mýÄR—€}Ùm-Ñ ý›œè˜Õ͹»èœqæþ„(Î)J¥½EþÔ…¾¨Ö’%º«»'©,¡ì]X~Š¡y~›žyÓ¼¬Œ…-lª2A@ˆ0òëF€; ¦~gO&{l[¨¨wÑæÿͤꊽ´RÊ=?ϧ³n|—Zƒ`äOKþq.õ:ý^„ ºv¥T¶om_Hƒ/šqX]Lb7ÿ,ËA7,É Wÿjr ¶}L{6ñ3SʉŒ€Mï·?!cÆiUîêÏ UMÚÜ9žà Ó"íÀ[˵ºR^˜Xæ—ÅÇ9ݢ~þÜ̩߷–õÉ:A qZÍl(É…HZ!‘Œ”³n‹-&h¨vÈ TM³êÁ8jÅš¬¯Î×wàVµ€”61Fe`G³/Î6GƒZË;§58sÌì¡kÚ x.·]×%Ñâ„Çrs” ƒúÖæ?¿ì¦öòkåÓ«FÓ¬ÕhQnaDgÃq.‡ï*Ö£Üza¼5nPݸ–\:cŠ€H$#CñI$mèH³[gõÕÕib~<Ö$²¾9IÝñ‰Àñ.Ñä>Ÿ¯Þ¼@W•¶ë›‘Dò›ø=ƒûÑwåÒÞòÈæ)ÁŠ ñ ß¼ä<ºñó¥Áš„µÞŠ—TŽýéÂq.7uNT‡ï<عR+¿ãåDzLé_ZB$[Æ÷ ³Z4-h~±nëÖ3ds§D3vbsƒùÆ–´öÀ¡Ø×õìJ}Û$Ò_WÿL±V eœ6”þ½u'Ýtrwê•G=û4Z’W@îÌ¥AÉItsoØK»ªé½í»iwE™ÒŽz"œ– ¶ÞWõèB÷}³’nêÕƒ *«Ìc)³õéî|üå™Ký]Ÿ“èÜŽ¨Â£Ñ§»òhQ^d%õá[nÆ­´ê1ní:"YHR'´B„H¶Â/U–$47ÇšhVY{Æn7ÄÛ#/~«Ü˺w¦!mÛ˜GæoÛIK ŠèþaiÓÁRÖ® uˆŽ¦u š#Ú%S5bRþkË/T fÿ6 4÷ü3iÊòUÔ'1‘^8ÿ ºöÓÅÔ5.–îÜŸ6•–ÒÇ;óLB9¤mÝ7¤?=³nm‡áãgJ+öî§}N }yÓ6êC³QáG ‘—[«g¦‘­2#or F)‚€ p Dò„ùªe¡‚À±C ’DÓŽl,1Õš²=ÙŽQéG 6ïÏ© Õv‰ËM¬XC³Î<Å4]¹ñóep²3hsIyà$÷ã>ol× ƒûRne%õŒ3ë»ÇÇšä’—…àè¿^A•Ú!BøÓþ& åããô¡ÓÚ·¥ÏöäÓ2×SARÛGGÁæšhHrú¾h7k¶Â!¢«9£òE³ * ‚À1G UI¶U:ô‡|`*Òœ¡)ùEÒÐ4Š¢¨î|87ÔVŽ Áh ÑŒsiÐo§Êfr®©o ó6n­¥Úæ6ù•NâÜ,1,©v×wuŠc^MŽk&šOÀ1¦ ) ¹ì,«¬E"¹î—Ò Þ˜Å‰Ø´HYH] …üèÊ éÕÍ;èçƒÅœeÉg|­šgëòPÿ‚2NùKWkß¿5Ϩ2Š ´Z-‘dbS·D!KÃA=©nuX?+ªúœy?í߽̟›°F%QÊIQ|ûTV´Žöá¸ÛyȦ*p=†Ýf†Ú·s1GGªÃ‘Ô®ç…T±3íøÒïõÝm;£RôWôËfzD_?í{Ž¢¨˜ö”»1²¡œz"ÅFŠ_î¾9ÉV8Z"šãÙCî­iÌ1*L£k’ä°]£o­3FžB/®ßBÃ!)|ø”A¦„ò`uµÙ.ÒKV=³ã9g ‡T±ÀtÖiÛÇ} x|×·ÂÑ];‚tVÐ3k7ÒÚšäµ¹`à|6Jªœ¼ãÏÈ©}Uï «=Úu;‡–½©bñ/AdzÇu ~çLFñÏüm¶.?¤%ª*ÝEÏw˜Ç¶¯Ê¡m+_À¾Aò Ó¯}•ÖП) Ê„¶ýiûòg©m÷sýýDb‡q„‚zvJ6UÛ‘CúCà©I“ªGö…”פü61J¼‡›«°„pèÛÞ<÷cþ´ÿ ým­·†jÎyÿsÿá»—|G>‰$W>¿~³ù—`Cþo·WþÈ%ÿ–)ßÿø‘®úx±ÿó5pЉ‰I•Ùl@¹dÁ—ŸÂ·Ûµ¸’zì3Õì°k¡=ŠN_» V÷x?Ã7”ô$-V-‘ô“HüÒ©P5%"­— ¯Ò{ÜÃ#òµØcÛS‡žPÞ¦ÿ˜ªæ®n2ÇÑ6qÿîohøåÏSû{èEOÐÖïž„Šº¼VN‡ØkÄ3÷æÿ=nãX“¾tˆm»œIÅ…Þ‡LIájúqÁÝä©Éµ]«£0`cì*¥´‰É0c+Ý!Ö˜™¸Íw ÙSâa{½–^êó¨ö‘È£{]y´ý„r^^›ÚÒ) üÂníb(F&äÁ[Ǧg}6nZöèPú6‚€ Ð:hµDÒO"a*im¤ û%Þ¦@*Y¨õ¡}Zϰƒ]û]Oå¶b¬(*FN쮽D’Zñá­ —ŸCåÒ?Æq@·ö&®&¦ ÁXÛ!d‡®USEÉvÿØêÖ/žBù[>€7÷RrURUY®Ù&¡ý@ïy8—%“ÕûLoo&‘§\ñ­ùâ/Tqp›i‡©(Í“[˜q+ÕS蔓ÚS âÖ1¾Œ³sÿÂdGhf^œ2åàKÙ$Õ:Òª¯t,®ª¼§„ÎÚºß8 Ž!ìa,%r€@Òž¶±´½Cœ î›ÆgdGÖã/rK‘žA DšÏ*=Ä …³™ õ‹‡ dT”¢@&£¢¢(9¶Š*ª]”ëLTy3óY¯‘ûÑŽßuÀÚ¿kUWî÷w‘·é=½9´sÍ?LI#{j³÷uᎅTàPcÊÛøžÿ<öØ®®:@%…khè¥OR‡£¡.·QùÁ­´ai¸h5õ?g*±Mæ¿~ÃÞÊn§½;ù?‡{‡%‘L"sµÁÔ7%ŽºwH2qe|gÆ[Š Ð˜—9e9æ±Õ6hÈhÄh‹Á_UU4¥ÄºI׫)×=ˆÊ*ÛÓ°¨ÿR{ëNß©G¼Ý¸ÌqØ9L꾘Ó߬_öÏQ’DCé<¬]ÝŠµ_LòWùöëžû¿·9mðò ¼ºù/\…m"YÍ’È“’íÔ¯KOÆ•ñe‰d}ªmg5¾C) ×<¤A ÆgeuÕÆ3ºG¿Á­ªž\HÉ ÛD[´0˜_p¬ÆÓ;´£nÙá÷²œH}裹¡L/"m2²q*9¿wK(•6X‹W+,^Š ´bZ ‘„QN^qie-y«Z™àØVƒ dò׺eÂår‘[sSOÙ q¿Öž¾®¼‹:Z·R7„êhÝDv¥qÂw¤×E($2XŸM97XŸÕsœHñÃÞÙìXö¥ýÚY©GJ"ÅKÆ“qe|}ªíº}—™ßI~Ýzù,D Ôôé7xœžWuRcv·¡¼6±ÝÿzÙôQG¦´§ñƒúšé3æ?=¯@IDATô†MÔ&‰®ìÞ¥A"i‰}ó’óèÆÏ—6}õô`…F@eó™Rb«™]¡õ-d:2 A@ˆ­†Hdsª• б°Ûãâ%’*ÙAt¢Q|¸.W5U#V›†xkºÎÌ*ŠAÝA· By²I˜è¿¢¨’¢Õ²Ò± rl.âüã4’Ndâ`ã'Òªx¨C”‡ºµ‰¢$H]â)¹OÆ•ñexÆ;°ðwÁß êŽ^ÔØ¡ì  06#ëÜ×Ï•ÅÚõM,.䫎Dùº`/]rwzçZ[OðAÉItsïžt¿-ïmßmæÓÎ1„z%ÆÑ_Ï>–äPï¤Z^t€¾FžlÎNsM÷®4uùjâÌ8 D_åš9¹ÏïÜ®îÑÁÌKé?;vS1úl‡ûnÜÀ>ôñ®<º³/ú;Ÿ–3Av¯îÑ…fþ´cÆÓ ˜kœ³î@ Íýy‹™ÿ;°}¸÷ÙÁ)ÊíÁÏ‚º0Ü}K‚€ вh5DÒ¢Z>Ót-{íÖ\:sH/?ʬneg¶d š$Òƒj/‰ôyv;ÉŠ\¸m=.ôµÔ-¦G·9L¤N”âã¼b+Èt¬ªQ<Û¢-¦ä‘‰c~‰ “‰L&'ãÊøÖ§Öæï€VÕz(Âú‰¦¬³Ùàø…=óÜþ„(bÉ[wWym--£GF £1 —ÕZkÿ6 4÷ü3iÊòUÔ'1‘^8ÿ úÕgKh{i9U#ã¿¶üB¤|Prº d‰ä'õ m͇«ALÿÐ÷$zcëú]Ÿž&!åT‹gulOÿu6ý}ÅZ¬4¦wÞ¶ -@šÅÜŠCNDœY牳N¥ûÿ÷£™«;*ïÿ‚ð~²+—ºâž­¥¶©5óð|à°Kð–÷ª²å¢¡}_Ÿžn¥A@h¡´"ùb攕©ÙùË×oï"ég~©$’Áš$³xI&TßV9]NLÈâ4*„‰&HP ýÚ"7-Æ‹q1•@mÈ Åf1¦$’Id›¤$sŸÕÚì _Ÿ4’gˆïÂ@_üÝDnÆÒ³ àEÀÐõÙN»EßÜ)>¢$’Gc5õÓÈm}y·Îtkß“©´&# »¨k'SÙR{Ò$v¥¾Iñ´¹¤Ìüüã>oò—o ‹ÈqÚ03½á)íÛЫ›¶Ó¹RÈsœ= †{**é2ôÿï­;éE–Pþï×—Qo܃Õ/{*=µvÒ,îãaÍÒ16š^8ï zdåZ±×ëø·¤÷êžÝÌ>?Œ° §ŠŸÌù¥F ³¡ZÆ3¦åG‡÷'[A@8*Z ‘äÕëºñ亭{foÙU@}{tòâ#ŒQ†Œ8/9déÛõ±Z–=¹« !0m'A&=¢Dý™}œ„Ò§–fÉxAÂk’HƆ £i‰#K"MÕ¶_Y¿“ ø.€²ò¤ÿ‹A BŒ›93É(sŸVˆ ÙºO´¡±¸[ä–6ÓÎøi=Íy*̓ºØW:ÅÄà7ÄûÛÁ/¯O¬Þ@eÕµóZÜøš™jídD“øÕI]icq)-É/¢Ùg0Éæ2ìséŽû¬Ú.¬òvâ¯CLå–{%»;°œ×±åWVÑH8-ÜS`úË·+¡ÚîFÚŸníw2ÝúÕ·Ã;¿Às›ºÏ’Èþ…¥FrE5ÛjþqNæÔ%MíSΖ@«"’]m]Ÿ+ðäýåÍϾïôð¯±Øm‡â*29béŸÔD̰@°r‚H:Ù DRƒTÒƒ7þM*éÃ…%Œ¬®öJ#½Þî¬ÆfÕvllŒùÇ*m>θÖ-Õná;ðÀ>ª “¥ËsuËgA ÜÄ:£” rKÄš³,Ê-4%‚„"Û0rùÒÃs Nþ Dn/~WÚãEl¶I ÑxyõåÙfR¸bïúmŸ“è£{L‰eAŸZãõ¼^µï ¤’ ,0뙘®€]eg܇õ•w‘ŸûŸP¿{Ùù¦DòsÌ%£ïÀNó»Âý´àÊQ”€( lgÎ{~`^©Žô”lÌ'ÎÉÊ@È%)‚€ p" Ъˆ¤Ãq‡s\zæ¹EÅ¿öÑׯÝ7\XKÅí#“&a‚Ô‚‰ÉXH˜DšŽ8Ÿa:âÔØQú¤‘xo1×—O€ã#’œN’3±Úšñá?,•dÇ&\ÏÇO>§naìñà˜q''uËgA Ü<í¸¿xlZÖO)e®¡{’Ãë¥ÝØ\g¬ZO\~¡¿Ù÷Eûé}º×FŸ µµ {kƒ®Xðˆf œgÒ—W_ §™=4 ÒÌoà´óÀð´ [.ßî£+{t¦å ˜\²V­£§Ï9–^w)mjüÑ•kÑ; /,‘t¬XCž>Œ6€ÜþíÜÓ(/ÍL\_Ú¸-ì$’ƒ¼Ÿ´·Â_¦Z”19¥|vrDZ‡³€V°ÂqY1tã©ÓžlÜvíyJ d’—Ç’F–8²ãÒÇjG–DVC…ä•FzÌãÌÒ™HúÈd+€&蘚PÙ±D’Õþ,•ä‘L¸™<ÚL)¥×C;˜$’I䊟QU¹nfúÓA”‚@˜Ÿž9 !Ç¿(޵Y6tNRÂòçh§Ê®,À~’ûñI$¤O;LMªars´…%¡L$ÃYœnê_åFZ+Zl±Zï˜ã˜²#œcH_‚€ Ðòh•D’a÷’Iz¢[Ç6tóegZm&ù8“C¡d/n“@š“I¤ǽ„Ó×–·­¹ø$‹^)£×~”É$ÔÓ&¡4÷A0ƒI!Ù&’ÕÙ¦$R¥„D¶æ«¥å®m\FöïñùÏÊh‹¾%%ÁRݪ”.-xíÓí@¥žT‰˜i á­/i‡RlµˆYÊ$A ¹hµD’„šû2UQ_öèz×!}ºg î¥ íÓÕg’Ûø$Ž>çŸ]¤¯Þ׆·­¹øˆ¤_2 %“FŸó¯>ŽÉ!~Ø;›k@:suC¿snVÆçíd_hNø¾‡ò÷D°ê\­ä!«MU€½tsÎ¥µŒÈ¥.j_æÔà‘ v®äÃ:ú ‹-yî Ž{Ë[Ë:e‚€ pä´j"Ép8¯DçºsÿB4 $±3‘‘­·IŒU¢m¶ÃrˆŸ3|dÒ7㺟}õœŠ’³q°qࣀpæ³Ç<;;‰M¤%ÙKš5+¡¸ÔÅB*æa?o§‚¤(¥8aÀê±ë=–smicƒ€s0qJ„ê:Á©$Z4È#`ƒ¯²W»ØúýÛá^–„ÌGBB ÕÉ@&dÌ8 ¡}.G]OC1º`ñ Çe?4ðœ)ã””h½“ÁKœÈÐp“VÍÀ8Çí·s<ŒUîÃýÞÁPI?kWÄGQI P"$©d2–Xå¦öå.j[Qí)LŒ²ìj×üÔ±æÓ¯ Üã¶(TmQ,n˜¯@/ƒp=HÎ{ž(·®ÙÝ:ŽÖ„sWˆ]Ñ—âèB%†æÏMK˯ӥ|ŠHžàßµ,_8ap8Y ´¯/ðúkP¤]ïÂ`hU+‹±YÊ¢, “Ê*»Õ$—¨T¨Åfsë‡X‘ñ.â'±lLGŠ,…*ÑÓHò†më§$Õ¶'Ô±šÒ.¥ÔI½ á]MúFô³/ƒ]u…RàNˆâÂ<1Wc4Û¡ºFÎEËêΖ‡×:JÃnâM™”œ+Ç=¡ÿZ÷K•‚€ àE`\ÆÌ¡ yΆcΙºbœ ‰\ðA¿©‹®¨(su7X•fU,P‡{+!iÙÂvM÷Ø4 ÀZÔk ªëÑê˜/è¬v5Óƒæzv¿úõ ­znrŒz .Ê'ó‹ÜW‚¹&¸Ü”\á&Hç¾ÆÜ>еÜòüÔ©Þ´7‘]z!’'È-Ëà°-u‘Vt2ƒõÖ½8XO¨§“Á“Àø’e;‘φ™ † l7øäA˜\çAÚ˜‡pWyd16ÚÛ&®}ö¾û\õ”šžy‡‡” Å0Nö¨Š§$Æn)‰µQE”•*£,äFT„£-6äðŽF38˜Œ U,5X¶ªa=Ÿcç¯þÕÑŽ!ç ‚€ PB$ëCEêA@ˆ‡¡ækY—AøyëµPwó ²ª»¬ª^m³X4Öñ@  Ί?ŽG†Â™{Ø–ÑŠŒøÓ!Õ!UANX¨¾jlþ•KëÇs'O®KÑ7¨lA@h"B$› œ.‚ÀÑ"0qúô®JÏ© ‹ƒÀ»ƒXv‡#`'H9“ åLDTöÐa’èe’Ši¨QâÈoÅPUALšO:å#ÔÙv«¢nR;Än &=ÚyÊy‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ ‚€ œ ¤¦g^1>cÆ©'èòeÙaB×Ñ]ÎÎî¦îZ]7'â}fmuߢ,Hš qiY·Š1†T ¥ ›Ýªb|4'+ã«æ˜Ä¸ô¬7RVåd¥ÍjŽñŽç1t¢‰Š¡‡5¬:ž×ÑÚç>6=ë%"£3¯S!:€Í6²Ú^žë˜¼+’kŸèx&Ñ¥•l²’ý¢²þ9ØX†¡¼XUilÂñÂ`mNäúñ>k1DrØë9#<]¢1Ì ÂM¤Ø 2JUR·aûE[>]?æn¾©¤‚@#¬Èé[T\xØÝ¹x(õÃ=•Œ‡’"E¡õU]|Ùÿ¥­(üî5¡¨4@1¨»¢¨O†ÑO¾‹uC™06=óéyY“›ÐsH§bìU‰è6¤‰„Ðh\z¦ø>/;ýÛšK“†ÀŒ—£{‘V}§z`z±dP)ÊNlWÚ-öO¸Û¹³©ÓÆ}:Ê e1®íe¸‡ãš¹ž4÷”±iY£#yí´£‰åy”5O‹7òšº†cy¾ÜgÍþ1%’øUUßymÞ!Ñx@Ó¼|ܤ5Å»§“ùœ›¨Ty܃_óYìYëo¹sƒ¯•lAàŸÏŠí¢‘sJQqþqÅÜP5»x-ÃÍã¡g?šûñãê3m¬]ž=gÒ–&eQrs2Ó^«9yæ¸ôì öè½Ó§Ï~~êÔýGÙiH§åd§ÿ;¤†-£Ñ ÅXÚ2¦"³™s­—ëäIÓÝ®óë=‡o(¢±ÕºË˜ž£|e%;-U[ToÛP+åÛ¹Yi¯úšƒ­Áþcø»ÄWî­Ã¡ðÃvZ¸û=ýÉ}ÖÌ 3"9äµyç¿>÷Ÿ Ž#vÁÖÇŸ Ç~§èÕ¿ôjÎã)=ú=²xôh-X{©N4@ 'hFÕã& eñuÅ}5ë –{ÏÇ[o¿ê!mI(§5ÞFmóTë½±5‰djFÖxC§‰°°$g­¢Xþ/'sêÿ|}ÝãÈ>EÓô§ñùT¯ÔTyÇŸVŸÿÉ'cÊ÷Wþ¤$XGåÚóøÍ¸jôÙs³Ó§³Ö5s3ÓŸñ·³©¿"M ¿+gBBºÚ÷/Ö÷é1cÆxx¼qŽ™= á¹»—a¬NØ–p=†Z77+ãzÞ,¦ªQ¥OÝ8’¢pL‡æä}Õj}fŽcÊ_Û`k¸ïÑgœZÉÇàCuR^ƒd©JUé øcÔmó²ÒfpóçÏ·,\½ù`´ó˜äëæ_AâûBNÖÔwXýèt—κ®„Ī¡Ð*ÕJæ8ÒWp{?õ`åëÏ·MMŸ~ƒaèSÈf½1ÒjSߘÇÓö‰œ„ö.¥ü%áùU(óÆ÷ a"] ÁÿÅÓsÔ·bì‰ãï¿£¸8”smc(oຘÀíúŽÍkÐmúÌkù‹ï%Ò{ÿ(ï*†Ñr!þ (Áõ=Âw¾o+÷™‰ão«‹)~mîåéË!9ìbjl>¸YA~©{woúìÜþžÐX{9.´v-eýx–ú2TË/„L"A1¨§Až/?™­¦V7aÿ2>W§èí¼5I¤ASA†±[íýA¿ÀÃ鋉ŽYÝøø=iYÝA"¿9\¨F+ƒÈB“±–‡ÇO›>–[<üNA=_îþêBØpe‰·¼ÈÇPß´v¼ço§yÞCýû« 6H¾]¸vËÜÆ,ní<ðÈšr–=Nˆów‚Œ.«DzOÀºñ0M—jU¯R¬ÊMx@Ÿáq{æÖôØàžydbYœ5þŒQi(ê=q¶¸3b“cg¶vhú¾>¾Z³‰%^ñÞîp,2_òýÑV¿—(.­C¡Î¿—lêY “ktÍXv¯cz?>îÇ ^¬¸…·Œ›–}5Hä\RÕ? ‰ô¡rh;=Ç> šÊWâe$$yèLï¾³›«Ü%ËMux݃Gñ¼ír\³ÛøÔ`ßñxÇŒ“4·ñ-®ÓVÅv.nš±xáéS©•Š{@±X­Ÿàô_§¦?Þ×7…‰Ï<…5•õËš~ûXÝÎÇÇ;²‡x<Æôó"ÅÛº©d™ ™®b|çó$ò}ó>´ª×Úl ¿œutWêoj{Oá–WÂ~ú.Üã×:¸'÷Y ÇÓ~³K$¿6ï"üx~ˆ Û¼X,Ê‹–¸ŒZôÊe‹Gßá<Ú~äÏ€Za>ë aÌâã(ïâøhñØ÷“5ìÿ2/3ýÿ¸AC¾Ù9Y¯Ô´ùjÁÏð{q>¿4.'ÇfìÚ{³ÅFÍq¤V¢®r\Fö›†n<ØPŸ Ž_Cê9Å×flFöó é9¾Ï ­Ä™×P I¤aQ<å,âóð þL×èñûO¶}Ê1é€NêM¤èÿÀX·äëß^ˆ&_ºUåRH‹6>Ÿ¾sBÆôó5Ýsզё¶†û@ù3ìQÏ­vC²Htˆ,7€Ú_‚û«.ýjNæÔïÍ^äŸÙó¢Ovë®E ô,­>ê‚k®¹]‹ž|5æœI·WåQGºÑ¥‹:€¼DÒ¹†ª^Z§Z÷$×aλ!áþSM»Ÿÿ”ž¹ÉeÐvxë_‰ûïcÜ Ku¥š+2¸«°ô:¼˜¸îí>¢ÒÒZÏdÝm°ôp#çæÕô÷ÎØŒ°Õ~À5jVáš>du¤ó½}ÒùÔ´¬,ü–,ÁX½ñrf’_ôÓ/*%©Ó³÷Ýçªé+ÈFî³ À´èjüæ6_öÏyÝðTy»©$Ò?cƒÎß·«šÕaRg«ÁýÔ4€ÈÑ ŸÍ¶PÕè.$‰#<šñžGן†úêbÕB÷‹zŸÈ$ D³+^¯Åƒåþƒ„ò}¨¿ð$2XõÍe(>'ûŽók:õ¾ãf#8½aî4òC¨³k•_ ¹Käš¹©©nlruMa¢fÈs1™M¾Ïõm!M\Xiâ/ fŸ5õ!­!°9Ž´u2å—ë•£C…†¶]–ÏÐ÷Ç z7˜m ã<„Y’„*ÏÌ£è…G¦® ì?âŸcþÃë‚aMPŒùˆý ÈoÏ‘}¢g>îUm¸Þk*‰ôa‰~z8ηYkà« qû'HéçãÚœF¤Æ+6õ”y™S—ž[Ïw<˜¥ýmžËÊø¤o+^ÊÌë/lÇ=zî/\2f¹ç¼Vs_žŠÓŒþ ©‹+ã“£jߎ£«*]Óþæ»uÅx˜ÏÁ‹JŸCç*ï6N"1¤Üg‡ ;ŽöŽôânÒÒð êÍhÛ¤NꜬ“‘:ø_9o­ÿCê¢:‡ä£ ЪølvôÉÝeÚ×…k¡ G6i/ãÁ7|ôèÅ´…P‚M_ú•-ç<–n~t‘fJ9 bþìäž³–<³‘BQØ®UTuޝ~ÊAìñ}æ­Å°ì ülßâ±Â6ìP1ð¤‘ôô}“Z–ˆbÆÏ¨ÜçoPÿK/ƒ—×pX H ®\\`Ì(Àƒ[éh™òM¾>=Z‡*ärbž–›;ây£@ªk¯³'B'£ÖoyP¬ år¬ÿû‡Æge½9'=ýÈ$e‡- uUTänŸ @O çªpéýíæ¥FŸ ¹_U™ øË µ¯ûc /‚Õ‡£.\Zæõß.öŠý•ÏNÈÈX뫪ôË ‹åÃÎ1+®“Z×ýÙݺU/<° Ýš3% É*Ü»Ïùú¨a¨OëjÔ*_ªP¨×Y­ñ|çû·rŸù¡hI;µ~|"9±!¯Ï=vQAl#š8²fÌDG$Eiâˆrº pÌðÕüžG‡{" “ƒª–/c)gƒ²PÆ}ÁñPT©Å`r)9¥ÿ­¾s°†Ÿñð•óXZ-iJ}mÃQ§X`SéQò¬6Ë Zç¶;ë•Æá@¡¬@SÉ6Þ‡Šª(Ÿ‚4>Šèå ŠÿaÏYØG.Êó|SàÎþètB[×¥|ü~ ÝÝmœcú€¹Ž©}½€_ M£ÿ³¯¾¾-HèóðÎDüÍŽ§ñTý£Ã±þúÆ:ÞꦿšÐÎp•7bâp”«RŒôççwȹwÌ^|Ï‘)¸¾~ÀER˦óOŽY]\îêÁº…~æQŸš4© f#ìèõ‡Ê*Zé÷×R™mƒ´ñÂÀº…k6„ßðBodsHe{«J{_p¤ýØ6ûrŸEÕ¦÷YëG­éÝïÜÄàG›v×ÈÁÿziäú?Ü Û¯p$}U±î}1sÊÊÖºzv&¨Öõ¡óK·µ®ñHÖµð¹„vÕ•å ‘) '|¿6™Hòì ™ôg\§[-Ö¶okTmè®3ìcÛþÙÈö”f¸ÿÂ9¢É%%)ϵ·ü ‘ýÅ̩˽B¨ ñpT Ýš1HÙ]Ô ×ÖŽçS77eœÖ`(¼–k!i\Xj…b,1 +ŒŠ¡þ×*™6¡äpŒÖ@ôþ ±Â()‹¯ øÅC{­ƒW÷"r뢙e‰ÝéÒÊî‚J|¤j£óŽdþmlc–ºWÐî"|7dš!Éù­±­R]q'V\DÖûÒâý· o¿Ô=ìãX-ÿĵqÏØôì)ñÖ˜œ r¦TknÔÅ[¨k‡}ãYõïi0‡0Ãù+ÐÙ×ÄÜBŠÿnÞ/!¹ŸÐÅÚí•|OaC×2q\oéjMÿ*Ͻ÷Ò_ǧgfv²¦/Í×f÷V÷©s2ÓæûÚ…k+÷Y¸ o?Íb#9xþ|;.½ëÂ;õ:½éž›êÔö7Do<¬ÔýC=‡K8n ¤uòDßcŒ¼Ϫó‘c<3|ueå¯X ± A½÷ñãQ½ÃÑÿ%Ãû͆´í9H7žrköîª½Š®Ïñ¨ÓÌ…³h(åJH9~í‚g*Êà`ð%¢§ Çø‡õa(«å|…’ë8|Q”-ñ×x¸»ñ@ÿÆå.-áŸoøßÎq¤ÑKôã?\¦¨VþÍLE F;½sjÝÿá Q`~ð›H"X#Y¼67Th»a[Z£¼µ¯”:³À÷Ún\sƒ’ío›b´²$œM>fæi{öãš[„ëñ?8×oƒl¾Å*×áù^†ÐBŸçjYUºQ½ ÷î„`ý6¥^ ¹sqMD¾ }uÎÙ°íù6¢#)Ê÷nK=«¡18ϬG×~„\þ>,Üékë!KἬ©þ76_}}[NnÀ•‚‰1Td+r2Ó#J´ %ù¶p—Ö‡K¤ëÆgdÿQ7ôIð鱎‡þ<®¾IÔ#:WU½óê=¯„s VµvËc®nßãfÎL²;•¸ö4²ˆ%su7õ3¤¢wá÷§h[â…Ï:î+åþØñ 5#ûUH)Ë¡ö½§©c4¶Æ ©ëcïsÊ/KòyÆ7uÎ'úù9ȵ_É/y·D ÿȃ…uísûö^“;'ÆYö?ñàƒµì”Û…k_î³p!Ùô~šEµ­+jŽÉ‚›4diÔk¯¼à¸·ü(çsBFÍŸ>«¿FÕ#Ž39-Ì@ê0À¯_ sß¾îx ß~¸¶Ï9ö:ØépîäÉœÿ"R|4~+~ð‘H„ŽcÒ ’¾]á´±54†A(s¨‘0™á•Bi/mF`Ÿº¯ÂVEŒDòè¸_c¶•Ô»ÛžMÓúBL5½'ïý~ `ïÜ`w5×dX9(÷Y((5O›f!’°Ñh7½ˆ¼5%ý™!Þ®É^q4ƒ!,ɣ໺äfß›×Án¦o´5q|}Ù)|R@3èrÌœAöN£Ðç¿@¨Ÿ€ŠãtÙGxÈ3R¶KUƒE@è©€ˆd"¬ÃËsKûÌ·†ñÓ²®÷èÆý°5„ºB<ð @lvÍäkVïÖŸ¡‘l|r°5¤N˾Øãq¿Šï± ¸[¸-ÔtãH×_´ZÕßúŒ¬½qîô—U‹rÏq jë6ÞMN´Îê´†2/˜s‚•Îε§]u§l ˆO˜þÖ=ŽÇ;i°Âu0Í’1ו0 nîci NkM»í#½ܳí"=Fó÷¯ÂnKš… ʵº¢ô‚-4Æ›Õâ‹¿×üÓ’-ºÞ<׺æáq"N$-˜2ú‰†@³ØHÂö$Â4òȾ66öçˆþ¾?NMÆ=$ÅÛ@òèT'Õ@0bùg%gËNÁíLÖ@æ<ÑÇU†áydòuج L±ŽÜâ­§ÛaçtjS-Ñ «mA˜¤÷™qß\y«ds®Ýf¦ª–[0Ç«” ÏÍÞ£ ÿ÷gAh$ÛGCk°·Oø£Ü{²ÎÊÁ]Ôs–AãQâÑq¡¦À+ÿ·XG ¼PýsCÀå˱Î&‘e^àn‚a僷[Ñ$òKŒÿ1“H®s»«ŽÒb³Ù/Tb•áˆ6Ϫª›øXk,ˆñ{ & £¹¿6aÁ}ÔN?`qCy|Õ  ËMï¦:Ü4÷Zd¼ãÄXmu÷Ôñ÷-ÈŒÃ@óH$ Ú黨ƒ¡J#ÙØßYxÈtdaQÙ4›ÉdgBÆŒß"{Ä"H×Vz ý50í{ÙlóûòSp%Èfƒ™;Ì6†Ñ[±¨×@Rç—ÁîÌɰY⬿qÊ”ƒÜîÙ³3KJœÖu÷p|,à:Ÿt÷Qò9àMH5oÄ~P;³eÀ?¬#h¶nÖÐæÝwß HU+àÐcªL@>9tÉÅ8}ÆíkÊ)p XÂáfj>¿ç;з ì|OõäÚ`ŒÒ*U§5ªå7ÿI ÓV÷ACq$tÕ‚{ŠcâKZÍB$Éjù™´ÚªÉðCÈykC+[tGä¨?d#ÙùÐÍmˆ³õ¨¦ëðP£ÎÉNÿWC½ræŽrwEWo8sÇÜ ’5PÄÀÌ €ϯ®è3ÏG"¹Ž”áÔ²×Б ¸¦°1¿~ †5»NïöÁ{­ßYÈ×®¡ml=øœ×Pg]'dá Y²[¸wÆHH,K†õ}±Æ²9o°Ã1ÿÛи°>/TͽÛR3fGöÍp|èÛÃwlŠ-]Q鈥mõeûðÍíÖà;ÅÜ^:¼ß÷àŽ.×¾²s Ž¿ ˜ý‡¥PÅÏØës=[ÏÆúœíc†&Á>0 ’y¡öxýXÊ0ôï0ø@äGþ}àdø ‹-­¯a¡ihþ±¸¬z;òžئuí+K"½Åf ùžŠô\¤A ’L³§ /°æïT¤ÆÁïã×ÍᱩùK¿‚@0"ùöåsåé©îÁ¯å¼IÚ­þÊ0ïX Œè›^Øa9mï‡-âÙÈÐö6Û¡fޝgHke§%s‡ïÜ£Ù"v×A̶#,É}Pƒ‹Aò9Äühº«÷œPÖg ¤ßªý¶Î¤À/ArGab×azw˜¨Êàˆó *AýB¯ŠÛ´l4óB½¬©Dÿër23;-ë;hÙ_MuLÿ)Ç1uƒïœš Ïïâó»˜×ßÁ[Àþ;ã­icKø°Ê]âÂ=ÅéÃ^@ØW\y¿sGØ;–Š~_߯Kè9‘šì¶!ŠŒ¼ú~oÆÌÁþž9;…¡pv ?vfæø1êî¸Çñ|ü8ÇíÇN˾òÞ´¬žþóŽ~ç'¸“}*Ùáx%šƒÃÉÄïàrôÝÖ>³Ñ5Ä۾Ǻcة֙*§xƒÚ]1bPyëªöýmÙYh ²(|âoÏ™p˜3/0Ö™žiÙ¢d^ð·²ÃÙmTÅxÉpëï2ÞÜlü´ìËÙC÷agIi pcUU«,£ï/.†êùõH-N5ÔgÂÑ7;²™ñåê錿/Ÿ£[=‡ýU0‘°säÄú+‚ì„ÚŸÎqö‚tS«šçÇ×S­Ê€¡öpŠì¶Pb¬‰ÿÀoQ¤4gûÚRÇ7B]zC×òDÇ3‰ÏŸÀ>9¶bàç`û õìœPë»gû9’y<4kVƒB—ͬû—ýæAÀO†"=Üú[Æ-‡á`øC ¦‘|øHæo¸=?»ÉØåûÓ5çû|~¹»ò%HxVÌÍL{‰?¿èHÿÝ?éÖµ7™Äq]}Ù)ËÜÁçu±ux$m92ìÌuç†>ÜcŽº¿ '6¶5ê< *ë÷ íÛ3nZöÕÜ•U7`÷iôã<Á>É£Ã1¦^ÅËþz4ùíBCͼdеªî)—M;h~WP­ÿ¾â@ÅvgÏ--vmɪP£•‡jÔÊ>ÑQâÁ× iÄQ.yõå'ßòCNcc`çZÊ¡|ãñƒn\FÖŒ/Ölúiø¾Ç˃Ÿ˜Â¡í4Ø/)ß_ùÕ«7¯€óØó¾óênñ}ÎÌÕ6ýdhEK±ÿj}¤ôHúãPVèo®Ô*8ÃÕ ì÷³¦¯oø%ÇÀÞwyi‰ó \ó_±™‰¯mcýøÚÉöøAàþ;Š‹ñ<=3VIy455¯²±¾º–'NŸÞ×ëb—Vò97 ˆþ-ܤ¨ ßk¸F6ÊÝÕ\שõ¬úÚÞã˜Ñ÷õ*ô ¯ÐÚ¥¾cÝ3=„257Ñ1«ÖœŠµ­=XVý ê¾›‘ýÛÀ¾|ûø­ùÐYXúoßgÙ6Ú4_òÆ¼Ž†¦¯Æ…ß1\£b[ûø¿„«¿Pû –¢±Ì¡ö_·O*ÒZK‡òl[ÚÍMK˯;Ç#ùÎÌ ã²tùÅGØ‹— hÕ[ùd¶š Ïy¿éESW bê²’õ¬ËrÿJ_æCÆ0á+ÅjUîõÅ…äú"¼tÜÓÅvîoÙ6)_Bä“ïaÞ=é³z¬…ͼÁì¯pìQ[8&æx!™ÑÅzî…¾>Á釜̴ZÑ Bí%_¬Ù¼M%û¥9Yma)6°{ 0[ZOâÍtkWK×—s=CçeNYÎs2mruㆹÙé7…ÒOà:dÿøA 'ç4Û>åǯáÌ82\³Æ=µxÊØG.V!4\º–A¦ž…0fÌœf›’Çrm¥j:gŽãÁ¢ ެ³:Ò¹+ø>™0cF²§Ü³ž¬ëÆn¨À™Í˜~!L“ž±^çÅÒ¹™éi¾ãÁޱ°%Ø=ã;×·ml¼fÌ„Så(Ü“—Y­m¿å$"5ñƒ¿¥î)}Ó>²6à^ùÚÍãß8²mšM"ÉËY÷û±œbéÜXUáX¤NŸGV G_GÚ‡©Ïy†ëƒ¥;Ò1Û3lÉc6´–66•DòF(黸¯ÀÂ?ª' ‰äu_ù žƒõ¾ˆÁÑî㾄lC½3TÉãØÈúlˆù‡»–NêCUÞñÝ#ª•æ˜?ôhÈùr}¶­ü]aÜpl0÷X`ž€¼Òôo½âù›À6¼j_®Ý r`ìbÉç™Aÿ Ìfü¹F]x£b‰žïpÜáô‘H>†Y:1SmØX?Ü^Êñ‰@jêJwLT4_Ïð®nzÁµ½Õ‡“H­¡k™ï‡˜zۙѠO Oõ¯ù3kÏ|÷‰âJt£m9­6>Xê#°ÅbûYµF_¹)°ž÷ƒkèž©ÛGcóð¸é 'fÄÉÏ}™è椧çÂgÀ£æ—&ûúd©%¤·ÃÑ5l/Ô¾¾eÍJ$yJën÷-‚²^XÚëo…ô¿Ñmézvä©¿…Ô 'W>8íO dM²AÆýäVõ6äÖY¥Íè>Ÿ9y=“AÄ5­«ÝØ ™pwß7`†³2”¾Ï¾-K$A_/·’ý+_Ý¡­ÒÅ0Ô]¾Ïð Ü "èïÓW¸m¨?]Ñ»€úû3ÏSŒ=NªêÆûùúôÑx ­óå¯6m33feÕ’üÆžÍíë‡ÛH9~˜t{U®]º+0_8Žv%¸!ÖEGEzð¶ò¢£é#ðZæ}ô×öoii…þ¾ DÆ4ÿý0Á1£Wê´¬k5÷Á íß}×±¯}(}øÚò ¹ùRÎ÷gÒб`÷L`ÍÃáXde¢©ÄYß <÷Ù®Òâ2žƒï˜S«~‘¬ê$Lï£RŽÍN$y‘kÿ0~)œ0Fàôõ‘/š%êÔßÜšú«•צ6jsräýË‚Àñ…K;®zH¿A•oÃÌñìÚ¨¨Ös¯|ÐÃÎPa)²¾ry/b²ÞµÓ]†n h¿áªÛyjz64 ê×,‰©{ ŒDU5*|õn[tm|ŸëÛ6ÔŸª#ÔQÛ¦Ò ò(¦¤süÈ´?nlus2ܬÞËÁüÅ^C,ë§¾yIÝñ…Àƒc¿X¨ÍxFù¯‡PWÆÇvû·Å¤œÅ¤4Ôóê¶ ¼–ï}ô…8ôZëþÙªuòß·çaÝCO ~¤s_Ôí/”>êžs¤ŸƒÝ3ý46\ýÛKÁ^WÆWæóMGMϱ(Æý¾þø·Xãå/íG_l›cB$y™knû ¤“¨ªÊy­M;¤†–6¸AçYuÐ†ÛÆÍp(òöÑ^rìÄCàŠ‡=¯ÇZâûÁŽj&~ˆ÷7мåápoǤî|ÀÝè=Øh ^ÌzxSlŒr&¤6䳎BÞó'ð Ì hOû¬ëñÐ»Žº·÷?Cr 00¿…ÜH5œ(7Öæp}Öñü4 ‹qí»@|/OL´à›«ÑØF v‘í‘Fq<È>65Ðï\ÙÿLN=X2uœ~‹…,çãÙÃß}c/ æXTëY8ïîo+ô¿)u¯eVíâeï2‡Šj i…bø_q¦Â>°¿Íu‰á1þ=Þ‘0q‡J(}j}t{Aî¿Ð‘å0ï**y /— `Kmj/ع˜¤’¥C¦)åT`LÞNv›h‹Ž]9Äu@óùoø«sûyT¤ÛÓi8*;ㆴã‚(E¾ßm°ÛúÎbD¹æ¶ÛŽúÆú𶉄÷kO%JÍͯȿ}/ f‡ÜÙ–öV®;;çÇã<=h?¾¾eÛz˜œª±æìêÇ_‰ë¤¹–§A¥Üu±,áÆu½×ÎJÃóùÔ;Ë÷65b°kË<&O/d§›÷æÐÛBtØ ÇFT„ÿ±u4æ¸.ð›µÀsŽv?ðž H]kvlæ Fw¶v¾;p\Äsž4¾Neó÷„i*ÂàÆ@ÒŠ 9›^T-Ð0Øñ[ÃŽ ~NJ3!pL‰dàWß>n3>óŸA@h"§Ãi],¬ù è‰#—Æ„+ÞVáøÏ’Ûøcœ-öLîo|ÆŒSuC{N±Ú®ù[ÚäC6_8ÆaAœNãôy¥}¢’å$x vWϲ“ÖwÂøÿ}î#°]¨ýådN^ {Ç6ã`÷87sòÚ=ûWxð¬cêaÜo™Ç}sa¨uNÚïó Í×füÕÎz°È‘©àA]?æÉò¯U"ðÐÿ·wðUTÙ¿wf^BèX@ÀŽ‚ bÅ‚ØPìkÝ¿mÕ¿•„®’ÉK‚ Ô$ØV×UY»«+ k¿®»4EìJh IHòfæþç%óxïåµTœáfæöû}·œ{n™;K7"cÏVÿ…äÑ­S»¿ºbY«Çxej¶—,‰: ÄCtV±ð—_®yR'6ý·” mfffBˆu?y[v9”0ÏÐ;vH_ßêÀ–ÿ Í±Â w´SZXžm4›@ïµ½âÕ!H*Ê¿…êä»´qÇé¥Ò¡]<Þ[„ðºÆ¢ú˜¾Ç]ÚMŽaÜË»¶]"Mwo6‚dÓe™cbL ± ÐNèR«ìUÄ£9–)šqÍtsÔVŠ×qì)ÐŽ%,ÿ2d¤ +^?Ï—ÝÇ_ê\ mE   yßk_w$ÅšÔƒÛ>ABÝ%ùÃA°· ÇšðvâèŸÒÐo £¨J­’>]Œ¾×Üà?¿SÙOü¼e Îäûok|Ùé{]Ó¯­þzRÔp\¿|gu%¯,§í&UX;^¤s"!DvÀR “4}CóòD}øu…N(G½ƒ²NåÐ0lŽóZŸ/ÝVÞiZ+ J/ŽÛ/Då:<ÒÁj}Å«3ɦy¸Óõº‘S;Râ/͇¶q fֻ횕ƒôëŽïL€ 0&° Í=È?ÙìÑt3ƒêž!ü²M4w¡~ÜçXî’ùù%Í'é†yO&œH?üÎêC€¾ðBe32 úJŒû¥¯P;4Cßé9ZUfóÃÖaFúKô¯Î$›ŽDq°=`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0zõòÝÀž•Ròøks,ÙE(‘¢éjGk=å»e#ŽÝÑÀQÕ9¸ oÎ¥š4¶ÌÍÿ¹ˆi¾o[ŸþQ(§]ZKùÎcYY›\;¾7oô{Jil*ÈÿEóNiÝR7rä’4[WÝ”T¤­áQln×®ï¦)­º…ذ¾¨îl´>¹S¶2^š;~ü67ôtsêAšµëZ¡É_ç>”õš”R¹v|o¾ÜßÓií™_4nÜöæ›Òº§ì»3r;•éÚáºR-¥æ”êšöã±gn©{ˆ ës¨™×ÝïwN,Êõþ=4äŒìI':Âê'u¹´ÈÌúw¨?7_{Ceìi|¦RÚ‹ÓW]m;âÖ¬¼d{7M6ººmÖ.Ñ}êÊ5šoèš|bõ¨^߸öu¹2}§;¶¸Z9büo@õ¹úkE9ãV&ž#Äp!ìÏà6 H1>d½õ =ÿ[H¹ÝòËOñÜ,Ét¯ï)ä…¾¬ÉÉämtC¿§TÎRä½V‚dzvî=8œWä˾­¹q»7si'UîÜå(u_8' [è”F%üâ×íKʆ\ü F‘/ЮËó¦yTy]ó0|ÆŒÔÊÍ%W8ʹRHÕ[(¹Rj¨­Œg“$JZ}–jÿ.ŠRÊä¤! HfdçÞêøË§ÙR¾!m›|µ®ékHÃÍm+¬ík ‘rÁߨ/2ì}%,÷÷”åÎÇÈSBAÒ4•VlçÝ…ºt%ÜwÆß;Bzþžl{ÜTÜVŸá;åûG‰KK•ÝUX¶ŒÄl!PÇĪÓs~VR¼­Iý©^K3—Õ']ÃÌÉ]*mÿuè£þ(¥HCÍý\ ý“B_æ+É„kÛê, €Û  ™žå›¯«7êÒ‡ÊTß›… É}Tâ_´¶}T†™×Óñ;é=–J–C.yKúËE渟ÇV7{TüÔ•W™x"ÝìAòÔBå)Ù±j¦-TFd‚â¿+ •õöJK]ÖcÊê›×Þßë_ñÝGØ:έZÍËÍb35ä¹ÖÐú\Aò?µöØ s½Ï7A4õâ:]ªêHSù/ö/~q éb 4Mo¢xFŽ]Þ«Ü¿óÔî‰Ü†Ú£ì„|Ì>zɵcÇ~öçÉ“û$Ô"¹þBµñÍ´º¾¬yÕæuêìv‡ •å;š<׬¹Ü±€ºšKz¢¥c°wrKTžÍ®9šµ4Zçµ]½¦yCeuú>è͹ÐVÖxÌúì©t¯:Ãw#~OmÒyóB¡ì¯>c"„É Eµñ딪þpßQó¤ž"ô=]›0"Ýj%Î9ñn /ÒÉ{ß+ú(¿¸Ô²ÃƒT‹ˆ5Gþnšó ©ObÏ÷o{—üQ–øû"¨wj\ÒN›\ì5uJÉO+_Ce»,éTF:T¢#4‰ÿ¡¸'4yב÷JÏö壻k‹ :t ×÷84;+Šr¼3Ⱦ*Íöd¡Ô%˜ú8F߆qÕ\süwd/ dzÒ¤œ‹4©-RÂÎÄïyìb½Ð“³¸n‡˜¹'Y–ó(ÞOFz7#s¯GG<4³M¹µýmðëJú øíÒ4ñ7LuÏoçù²ò)Œùóçë ÿûõrpù¼F¹á‚ç¿0ZŸƒéž—hʱܿc2ø^†n{L1}¡âþBÓKKÄÈiÓÒJ~+ûlcœ®J,h¹Ô( S°†¨†°BùRÊ/<ÆŸ¢Méë(Ž]³ÑY׿ƒ›œ=z¿wôâÓüþÊHNÝ;¥./«,ÿhüøeäçŸVÖcgî|ütŸl³5í—Øn„ˆU"5 Y¾s×[>òÑ“ðÏ£LThq³û;†ÆAKJ,Ëmv ãí€rþ9ÖSÎ*š˜õ¹K'Õ ¸Y*Õ3“Tï¶£¾o„èÕ_㹋47IDAT¶+ –±te©.t3퀋PŽVh2åòBßë(žAÙù';Ž=$¼i¨W æù²IJ˜rã^Á2êÑ®–3 Î@y^ºðÚ€Ž}ô†nj¿cÕÓŒ ¹Ú¶ÿ/`ÑìéÃz¸tá8s C»iŽ™ðÎÎ;Çrœ'5])œ˜õ¥a™{¼íW/whëéóðر;üüŠÙ§ O¥HÇ‚VF«1ÔQ‘ûhm’G¤ƒ°Z÷žáÍ¥—1àÄîé¡ù pÜðè9äBû‡\ìÁkõ9C y]“€¶!m[!¦ÄéµÌ;1Ùp0ɱ/¿ ç¿Å¥&ÛGQœÔï ƒ¡ñ=¸ºLüŠ>ƒŠ•ÇVCÑ烟m+4mLQVÖr«Üì¢ôQè ®Á4û¬Oÿ™æØ`AåÏvœ9ÂsðÊÚ2#´¢°ysÚJ‡cQö¶a ê…6ð©@\@ëÕÐ)f`šqQUØÈ‡V=#W%þ7±N®*Âx‘z*úèNbÜdŸ( nî½*Mâv„w7âÈÔ[H¿GñuòänH–ï0‘‹!´-ÔZÈžX©7ËØAòÎxpøÎVFkZT¦¤6„øµìÐr<Ù¦·ª®­XK•µD¶Û©S'S ¿C9ÿGïÖŽ7!ôÆ4þPáÑ΄0¹Â±ÔÇ´èœìuÛF9WÝ)ÿø6aÛ™zk}.Ù…^´ôBd‘дaÑ„HrK1UTÌ7!Û{þ=zñЊ¿]/!²:`|ÂÎrëÍÂÂÏ’ÒÀ†^”¶šCP4ñÊ@¤û”Nm—I#í:2G™¸ÊÄ…=»G]» ÁùA4ҺǓržl)O”B›ghÚZò›\œª³°• n—Ax¼åóJ%åÇèÀ†PîUì,¹y<Ö1üWu9:ÆN Ù2ó„æl ÊÔ/x/M‘êd$ü1²K. ä²ê –QËÆÀX½&ZÊ^X‚3õå¡…+×Ý麋WOSjCmÊH¤a{ =¿.ÚY£ ÛŽõæ—¸a m¹ yJsxÉÜ=êôv")_–_!_ò+Czú¡ò DûsL™Uò †ä>Z›ÔÑ8= ¼’=]A!u³}[Ï}‘Bd•«ðÿMó©h/úAÀÿg¸MÓ½a½ãHÔ©: ‘¡)…÷Ъ3s†šÅ{>Äð~„ßo'Ï/óæÍm²}ù5<æ`À”‹ÇõT&ZxÚËAhØÄåâiü¸ÏÆGé,1À’£ÂÌÌä.^¹#ûhåáí,Lñ+4)Öm䯽låÜözU‘™Q†RÖGA ,ýÜ|4BôÃU}lÂ4¸á»÷ª4Åï£Èm<ž†Þ!i}9|™ø¥íHÍø?”í³›ùG“Zç‹4ßvj뽀̪.çOhSÑŸA¨Ìö CÛ’POB°>Nh¢$+ lV»&‘mRЪú!™>ÊõCý%g#í³`¶°0gÜ]»†¾:æ†4VxÇM]u;*Ö­±ìkm®hÔ'Ÿï5{u¯ÕC{•$ò_“¹Bä­èýò·o/ÏÆú¼' 4üsr½?“_Œ8ÎBƒy¾fÝ Ìñ?ÜûШ|·Ý0:ø–ÌèBAG¿ÃÜ—ò#ë€[¤ñ$Æ’Ñú—‡ÇŽØ ÃÕÿaÄãEÓI»FF(¤& XFu̯6ë&uíÐ ¼Uí #žœYù¿xÏ&³ŠM;®B<~uØoºnÜ{•Â>G׌SBv(£‘È 8I& nXÁ»T½•ñgw·-„óp½×qü'ÂÍFKˆQÈû*ŒÈ|Õ~^F¾úCX~4åó;˜+]Ú%.?hF S{x¤9í€é樭ŽÐþGHçi¿uƒ³ä<øYä×äE~5;×ûcu¾Î5<Ú‰sͬÕñÜ‹é±~•~h…¸³ÚŒnßc8:ä=ø÷P‰ÿ¢iÚÕ('Ë‚Íø:ê£?‡‚xPÃ%Sõ]ýUùD„Gì^CÇ[¿ Z«7Ðhý€63;ëÝŸtGÅñÊ›ö›9bD?{=ÆXRW;©L*;Æu´Î1ttpœ™H:N©º§vlwÅKq¤çæ>­ÊDÎ`3÷„`Y²Õí({/P§9ø´-g>ë¡™¸ƒüW_ßÓ=é4¸¾ªïÚ¦¸Ú½:¾ï3ñü89‰WOç‘mE©ƒ‚ìÖ'ò!à$èOln±|×a°™MfùÐøgT x ÚÞ¿=eêgÌ #ÿ¸¾„P³¶B‰ï %%ÀÛdˆòW£M£T²Ó-]¥gç=†ÎõÑÚ3€T2Ot¡,ù ˜ÿtˆqöG‰Ü6†ýš3sOÁ`xrƒ†­ÔŒ•gæ|Ò{iv@¨ˆ6 Î0¾Ào9³+°N¿ÿ[P*O°øn*^E¿hë¾R–Euê»õv>);¶£ï}yi ¼€Ö*–úKû@ ¿ïUõùš—“hÂh4õ­ ˆñ¢N ²"Ú$òë^µé£°Nṳ́\¿-ð»>ÝÕÓ/éÁŒ_mîM&Höxâ«6ÎÖJšjÐ š©ÃýåN&¥¿„¦|_Ä÷Ò¢U_ÿßH4ø+1s>iš öF߼˱¬ÇPaaJÙ€ÖJ*íÉXє߯®ríS=žÇѺÈïßvn…Çq=#DêlwjŒ¦iW°W"ÎsÉ~È•(–P»á âm…vaAðH+*©ò¡AŸP-ØÞŽû3…ÕªõP·˜*ì…0;qÜ H†Z‰¤Óæ+ðRì ‘ô6õþûKQ€·`³E›j§Ø%¨: _¯W¿Sçs8Òq´ûy/0³V¡’n(qÊú£Ó{µØŸ{®é7ÚJ€þ‘*é"2tž­ìãÞæ9f®Dç ò»àwIÐÐÀB#]óBe>•í~@ˆ5]4O“£?½ yì×ЩÃ|õè÷/}rÆ”3×% Û4ï,‡›QØ$c—ÒNq'k½ÿë;ñÛõ¥Nvµ.¡qb½O Â4“j ¦Žš>Ìvìù(+W¢ã™Û¾µç/!ÂJ’qÊ—]!’§)<„÷¶ÅhÚø`—ØWAÛ¨—Á4T/yï_Ã’LC¸'iÈÕá&â{4‡“Y]ë)6k,€@w?ÍXlÚ’:ʺÂtùS W¬Í%?/)¶Ö"úÄê¸ÑVȰ|Íòeõ iæ! HFk“ªý£ªúI¬yôc ’<ƒÌ‘hϯÓ[ˆsLo4ÍMA+7-+”a w¡Œ´…µG±b˜mf~ »‹h¹ŽÂ"1;¡#›Nßh¬>ЦžÁ´rìEX>Dû9» Ç»€ú•¤Ë]”> k`Ÿ+µJ¦6}gÎ5½Kw@ˆ‹-…9ÞE¨Çaªã9D*¬ì‘£¤Óbà%nUWžð·À“_æá÷ùÌf,ÀŽ·aöÚ¾ÁÅVþùè£6˜ÞUt”™òïê„úµ04y˜|åmÒûk¾=æßUÙ…·I®ûÚöQ©FÛaŽG¶­ ŒÄe*„õw›nCÝQ^šæR¿W’D|pcĆîjØ3ÖµM6l’þ 'z_Gaîû}˃~¥LEÙ…)ðYîÈG5]»ØÑR?O&|‘-᮳û§ü~ð°.å¡ç¡ã?P©ÊÕè°c…°Ód˜ÆzÕ³ú~kŠ4°V3xm2ÍðƵõ-_B§Ðnp¶¯ÿ½¹¹—K”®=ôòÕ{*^˪Î µ’OC˜?ä¥4Ì òE ŠÇÁ„ðÔå(t×F: {—â]Lš^¸ÑÎï‹<ÉNúøÅÐt½êHu *)6Ù´+ï„e!•‘ùO(PDØ@IWú/añ¸/JÒôåS´?€…É]]ãfW*©ÁSíó¡<Ø©zmüÑ€ëƒÑ§‚e N^ÿ€ÿº–êÈ;`#‚Û]Ÿ4¡ê9íèíâÉ:Véb'wü¾³ò»Ùù§Õ&N¤õÕÑoØåø–‡ÜBÓBN™}#,Ö¡3Xtö ¨½ˆ^ê˜oÝ6ÂÂCEù\u­§Ø}dÇŠŠ_wžmuä¯R¨„|íµëíoú¢®”_Øû˜@>ñ !Ωtã Þü†Ö©mÒn·â8šÛ~{ZÐ,Φþîƒyo*êv×[ãw‰ãµÁ¬¾ì›‡öYÕ`†„2zñ—gL<5Ä(á# ªQ§†`ÉÎ]$F–ö4RE‰A\ÓÖúX&²eàYÃõ äjQîj”‡*-ž|Ùò‹Û),Œø_ ØŸŒl¯É®Ô³‹ú ”0Ycv±iáþ‡|„Õ'×j¦9bÍØLô¾ ínҵŶ¬;‚ùnà‡`8ÜÁaT}~ÔF¹PùÛü^±ëOü©ÚD@…Ú²u(ÞýÈ*ûh24±Å]˜^›ðÈ-Æ%¸Ñ_«úl´Û •| øKéfáŒ9fÆFŒGR:B¸uÕÞ5üF3˜>jÔ.Œ"iƒÊ-e»Ä (1—™™_Es ‰ëkœv8­¬ñÑô`]Ó $Ê~ï/Q€ÏwôGq‚ú§0` 뜰¤GÒTÜCТ¢AÑ^%ÍÑ÷‹íÅiý¹7£R¶éâéú…‡ýPË…ã?4ÝÌ;.4ïhx/BGEDBPfÓ&)hS;ÙåêÅôÂÂþî‚é§Íæõ¾1‹ûBsÖ£t“i~?¢¶gLÎ}pÌL¥mEeê´a#Vâ2/ÕÃÁÑÜTk<_†Ý˘aeÁóõ‰³³Öï­bçµÑYr!–´ÜŠ)àÇ£ÅMf¨o_£ˆf_Ÿ4D Ì’©§ØøfE{‚„FðY„u‡ç#]W¡Î݈CÃùœŽ3Up;̺Ók°_ŽŠuuh:èlà e/G¯ZójõÙІ·ô¨m»ÊÔÐdŽA»uý8ù%{”¬!÷œ;«Ϻ‹šÎCÛ¶«¸„˜5ä£-$…Ÿ”B"4^,ïY‹¹ÑŽŔո}Tõ S.6 Ìض³ò« Ö7WÑ’«úô„ŸÀšò—°<âáJ¡Î5 „æÏ}ÌxsJ …½faƒ·dʾNmîIõùUǼyBÃmy@«K¶–¶Ù`ùnG]Ñ»ã?¾áé yÃÎ¥w.Ä ×K~H˜†œñ ´”¯OÝp°Žÿ"´!þÎ~ïšÅº×§"9uŒЊ6ÎÕ$‚ä‰ÓÖvÝeWPÇÒxvì"ð§âE@cjæÓ.ÆÙ?·XÖŠŽEÀT\:üL"] ï¿0•ú™ßR`ƒL-|Þ`Mé&¥ÿ䂜¬ùñÂŽgGçP¥ÏFš>  Ž79"à¾3βŅ‘ Öä¨{!`~£üÝ»Z(§â´[­¡QE¼°u©=a ë]hézaƒBQ,·]µ³Þƒ ö¾x0-ÝÌ :´Rÿeë ©z‹o© ×' ±âôÏtKùï@#4 CÍBÙ±]qÅ–’“t!Rææd~ð§$ݯ—…Õ‚HÓÓ–ªÒç`~&œ€¶˜´±¨¤ÿÀRƒlTܪ§TÅ…½^……éï ¿ópzö¤¬T½åÖλÑ1Ž)ɳc¥-šyû6žÛvø??o¦5R£¢¹i.f´©¼7Ú…¶ÍÖh€µ(V$´¬Ò¹‘FÊGEæ˜_is–eÜŽ²Ô‹äß%I•XÄ14!÷’´öiÑ`*°îj{yG4”;ê'•3”׿`÷ê½OMi.P…&I—úl˱—a0—ÚRè•iþ QzMá5V¾ÖÓÖžeb§•†öë|ì>ÿ ˜^-08»}JKÒœÀ¢«vì¢bûëCÐÝ€v$ÐÜú_QŸ† ôæŽom¤–ŠòŽ•Ø!Ýè:Ñõà7‚a&x /| ÌλS °–yY°Î‡ø£™ht†ã” hJ•F}\ë zvûÙn]³Æ¼C[v)-cj´ ‡™' ›~7,o(·ÅÁ+ºˆî•Åö§§â(!Ê⤩E;¹¡1ú¨A攎Êöw+˜8~) ÐðS}j Цկ*|(óLÍÿ^!Õ,N Ý= ÑX ΙhãboÂ7=ý^þÕúä0[¤¤Ìõ]›°ìG 0Y2}>„ÂÁ?›N=qÏa¤vÂÙÇP‚ŒCeE…³t_ÖÿÔ©£eKÏÂ`ôRÎ@8£±†yU`Ãú_/Æ n0ÂQ¥E¢>jˆwò°FºÛ!Fgô§w–‡nNp¶Á45à´­•+«V*ýº¤>aiªêëbë“ 4¶ ñº¿üL‘=Lq™–ò*4&;¡1|G‘ìrTåZ¬Ï\—4¹~”_àŒ?ÿ"¼Íh6Cº?ên±X;ªÍšÇé~këoXO±o­é¸aĺÓ*ßÏðÛ³]Û”c¹£ÒðèW¢à¶Áñ"ÿ§~Þ\‚5f/UŠòƒÈO}Ò+Núúò‰Ã†Å5X8þ-6íÄÈtxžîú=ia@¸Þ„N¥Ìi“ nŸ#OíÑé}HfU—ö Öõ@;÷Oׄ:šTOÛkPqýørÃb¬CÝHÓ@XrSì)I×wøÖØaGÞÿÀ4žîÍöÂ:Ƅ彾‰ÇR‚¸q`IH+Ô¡©T^ñÛUbÞf)ºœ£± úòB³ê0ædÊ@]Ò‰ÆúÏ¥[K¿ƒÐ·ë®Ö¡ƒ*ÅÎë(¬úÆ™bèOb–ƒõ7"ý M+Õ=u-¥Îz¬ÛdY*«!ÒOès¢zJæÐð±ù ~“_h—'ù7µƒÕî¤áw…³Àéø Õ)§…€}ÕU­Ù¿ ÌëJ­ÒŸñ—•ð{N‹¸Ôm³\·‰î´IÓóá÷z1°ü&„ȱho»à¥%¶ßú>ôï£5ß7š%"bÝ8!C©FeªÛ÷çOowè;“?Ø–Z ü›Kqfm„ÇÁþ‹T#å*r×X}”8f_ÙÓ±ÄàWôShC­k¬0g<ÝëÕ?`JÓÙø/Ç´yÔ¥W])Û=„©^:ð)¬ë+­ÀxlQyÙ%*û䦶W2Ù+QåH§äœÉëýëËÖß0‹°åz¦ùþof0Ùøc¹Cßø×qSV C™Ù˜1!#ÖÚûOS?G‹œûwì8ȱÞVuü@4WØÁYXè¶unÛJÿ6‘DwU;SÑ´JI©¤ÑL,Ÿ4‚P)vE¼,–ßdÍ1¥Þ²•(mQ-°ÕðÖi ¼§”ËV‰Ó7G®õ¤Pœ±ìj$0†AÕo¶³iÇb8ÙgŒ‡Z¼ZÃã3Cš&çÌx¤ßÐDqÐ9ž¶G»Ù¾¬_F ‰Ê@ oqI“BÓé±âmŒ8£%hx^ÞÁ¢uë¡›w\w•†xõ”Ö¶{Öèž覥¶wÚ}*ÛªŠxmVmÃlŽîWöõ%lgqc§Í#õ[<]"jttvîÇß|s€¿Ò#ã"Þ}­ÃÜ’òi»xýO¼r5Cu0 ÌÜyò:ÑñCÑêvc¤!OÚ|×FÓ¬M}uÈ™4 êàÏÜbëBbOt6féÿiíSm(ù%^¬M"Hö˜²b,F»§N⥨vmÚöNùIw¦(o7à ¾Ç=ynè7ê©Ü"MôE:ªhµ<¬ã=ѾŒ’1!÷B|ñh.:ð-øúG*¥‹|ÙtNfð¢C½U‰õ¾Žq ËÐ9ÿnúð9æøo2ááëëZx<ý+¬Ê+°îf¾°²w¾1}^NÖ Áˆñ0ÈçëŠo°¯Ò”‚úö|¨?ï{Vž‘s/¾òhÃåLÞÓ{yöɆGƒ–òM;(þøFù±®¿dûždÝÑW”­|¨K¨Ÿ* ŸŸYÓÕèwG´>#Q]vû(ÑÊx*™zÊ}”û«î÷„š·†Î¦q¸v:Œ·ë.¦ ðE6uSm„H|÷õ<˲^AGUC‹ƒOœMÀ)_o¡;S´öœaפ¯fD¦3Ywñâ 3QxH’+o0ô”¤ÒÊð ãÁh\úù²Nq¤èIßf ¾ŒNý&|’êÇPs~Þ7 <6¹ÏOø¬ãeX6²­¾9D‡ò_CÉ+k#Dâ“j¹ø$×Íð‹Ïèí¾ð­ÜsQGÏéjœ}Â<_vÒòÈ_~½s·‹ª'Ò„à»áOH‘zE‘ÏÛO×4 îdA¤;Ñ¡C™.´×PöO¤òºÿËrFEºK&¼Á¦ïL|ÆlýLsì/ðÿ½áéÐi<[O×I|^,ð…‹€í]b6*á!Fü¸è½,û1”ÝÉ ‘E©iÙµ"1#V±yû‡(oÁOñ¹éHÔWÔÖG“ëtOê%TïÐï‚o·Úà_ü'7÷ž¨.‡öQÉÔSî£\²ûνÉÉÕ7ôªìÒçø«QQ¡¨ã%å&h.ýzÌ ¯Õ&]÷|©-àTص‘þ ‘¸&-Mïj74%h&âúP7ô¼hå7§C(ü©Ð÷À:z/˜˜µå½×HsÚôî^¤É,ðeþÃ}—†\&„ÓÓ}wïÉ„gûÅ-høž#?è@ßcŲÄíõ®‡VÆÖ6ìè@ït Ìν \¿€¶ò§*þ püòìqš¦Ý  ‹Ú_h›Ë1ë5ðø¥^_m|Ï4GìÒ3Pó¤âÛáW¢¾Âu¬»Ùfæ×Ÿ`,G¿ÚΠǽ'ªË¡}T2õ”û(—ì¾sorA’Ð}Ð_â»Ø½‡©ã#î5…º˜x¥ÀäS-TÚ kGÿ~Lw1,¨ÒП hùv;¢ÒqÀcYY›‚¦8ŒÙê°à;’uG~bÅUÛð0ey Òèô"ý"A—"å_®9uzÈɊœqÿE–⮡týð}ß ðèÃ}¿<°mꩨд "æ·ÜkäV |ËV»{Ö´~WNžÜ§ÖgÜÍηñaµiÎC/ÙE)-(|ŸÃë¹v¤ÓÈ »@RýR.vZg¿¸e¹ ö“{¯¥ÞÈ*:âÃZeYŠ÷ПrüÒ 5„ÁdÂ)Ê·2ÕSVŸ’í{’uç¦#ÝœzPFv^ߌlßCèkSzj”ÓOâ×åX}T ŽˆzÊ}”K~ߺïAÒEøõ˜^ÿ¼yLïžRׯ$÷:¤šÚ*ÍÛ*4äy©ºè¾vLï»VÜLƒîþúМVè+ÜtÑM©tDûP³dÝ…ú‰÷œ(*²žråÝ÷î{T$œ¦”ÎÚQ½^_;æ„ë¿}üA)š<;Ü΀¶òé‘'Êžv°ëýõ˜ÞY+Gžð]cü4µ…Q mÁ ëÛ©Â:›dÝIð(¼õÖ7—"ˆ%鎰Mð]‹Žô*hJ‚¤…B8=cøðJ•’óâÎ_‡jC‚àÇ}˜ÀÌüS¶ÌœÖo2þÎ8°m×6XCÙKúÙR}=©©ÝÎ9³_»™ÓξlÆ#}ÿ2sıa¨†Â‚¸U9¨CÕ—.ümQÃêSÀJ“[á6è®Ê¹j£ôðºç†3Ä|ø¬|A3´ÿ ,Aq-Ü{¢ð ¿^CÃuiCùV/; åLÏνÒÁWEÇ}Ðò N)T¯‘šW7b¾ïËzê]rü²ìtLyÞ"ÕèhHÑGƹº¦Ÿj´1‚Ý‘½–g9~Éøÿk ‰ú 7Îdݹî‹r²þ†5Çg¤vjÛ 3\}˶•síÜ{¼º«ŠVO¹r‰î{wÌ85Ÿ #"ÈsEÆQG}™RD]L#¦9¹Þ@ܘÖ¬B²î’Mküðì[¤.Ã:=ZølÛÂlÙR»ø1¬£x†æåè/³ÏAG½SÕQËVâçÍ[Ó½9¢u`ɦ‡Ýí[ª_š«é¡/ô ç蘺¹Ác“ÛÑÐÛÔ¬×ÊøA ?ìª.:6¤Øò!Sµõ®™{§ãÊü¥o⤆ ÌÌU®yØ=Nxt܈²6÷ïlt¾'ÔêÌhdÊ‹r½Íh<¥:véUf2U)ç®tZ³,Äà ;~ØïûqædšþšôŠßWìNJ²îvûbæˆÞ¼gQÆÇÃüÁP»øu¹f­žrJtß{ÞãÉæ‚BØK–”wQzhÝDÚË5#õÍÀ{–ïz:»‘ž“uGnc]´kt°wr²k‡í4ý:kÝÿé†3(;ÿdèEfIqeèzÎÙ™™¿AXlú?¿v1ÎîÀB¤KïMI@úK$ÿ\=õ…¨Q·4ØGÇ“ œ{¥‡ÖóâÖ>={Rozßèä^©íU4õêŽê_™Uú:êËÔÐM7ä'Ô]¼ð¤ËÕX³ò®iÞübOz–o´Œ‡vñdePXî¡òò°ú$ef)î‚ö†…Hß›”@¬¾‚‘LŸé.0Õ\ƒÀÚaaߌÅÎZhx±êr´>*V=å>ªô>zkVÉ=É8Åh7©ÂÚñ"ÎÃúLøË;`÷šI;¥©b”üVö|é¶ò~Hß²dÝÅË‹m)Œú*i—ê˜Xᡃ»öo™æ Á]ƒŽcOäy”°üË i DîúªãUâÅÈvL i äŒÿeøåbkí ”U*ÃkRn8KÏ_ê\ ­IÌŽ¤Jê· Çšw;qP iè7y¨»²m»nÃÊå³0ÍÖî ø£ÿ í ¸ûC2ᡮ܂å*A¿´3¼Ä_šár ´ ëÝ:…•›8^ë¡`üÀšX}%-™>%Ò_Šqžê9¨;Šý‹±”K[f´¦ã·ÂËU—ß´ã&8 ë£âÕÓ"3ëß6_L`Ÿ'@£¬È/ÛZ>2ãɺ‹ôGïU~i‚ÖÑ ZòØK ЙŒ‘_° ú58?¿Cd–èÐñP³XîBÝÐs,w‘áEúãw&°7ˆÖW$Û§Dº£Mf±û·ð>*Z]Þùqš™`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&Àš%YßT òæ t„¸%Z8šÏø²ç…Ú57÷¡iãg&À˜`L€ 0ä @Ö«ß"•<©~¡°o&À˜`L€ 0ýŽ@º7çúÛï2ÎfL€ 0&À˜À~N ÞÉýœgŸ 0&À˜`û-$÷ÛŸž3Θ`L€ 0úدIÚèCõCƾ™`L€ 0&ÀˆÀ~%HÒÆ X;̹80&À˜`L€ ÔŽ€Q;ç5]C}®¦)›0&À˜`L€ ìëê-HFž¹¯‹—¿ oÎÝ-Ò´<–•µ)–»ô ¹ý¥’å…9™ŸÆrÃæL€ 0&À˜ØìWSÛµùA”Rr`–ï• oÞuÉúƒ—¹»ÊÔ±qÝ;Î=ÊqnŠë†-™`L€ 0&°¨·Fr/Èc’88Û×_Jq‰N'ðJaOL€ 0&À˜؇ °F2Æ‹M9…”³„'g˜y=c8cc&À˜`L€ ì·ê­‘tÓÙÖJ&»1hh^ÞþRçZ]êý,a÷–}JȨÐR2hBî%ŽãäÃìX%ÄZMÓÆcʺB¨ÝŸ/fNîRá÷Ïɹp·Mjâ¯J ¿ÎÈiÓÒJ~+ûlcœ®J¬ÙB¨+°~rJQ®7ïÉ“ÛlÛ音˦GJµÀÓR¿ovfæoä?cBî…Ž­†ÂüL©oÅý3‘¦)ÊÊÚÏÎÛ½1>IJü&¦òûì´°Ÿ MÎ*š˜õ¹”²ãØSðx þ4…tÌóeßHvCÌÜ“,¿šJv0/B.he´ó¨9òw²§ü•n-ýÂ)×ú…–ªÒºfåxgºåµÊÿϘ`L€ 47õÖHîMGê°›ŒÀëßåÜFÂáÜœñŸkºx ?Úm¦9?Åýñ™¹ÇÛ¶z ðæŠÖžC5¡O‚™ !)ÍuCk,+­Êw„T~ÓÒSxäõ"€†3¸>R·m¡º«ÿ›BŠM¸ÎÔ[ës) ‘¯Á®³4´+=y1‚ëä/s^ ;Ó|ª„ا! >o¥{äŸ l¾[˜™¹1žù¼üþÊ…RºÇ“ržl)O”B›ghÚZr7ÈÌ?ÒVÖ¤íÍ#.M‘êdÄó˜k!vò+Czú!#!(Sf•¼Cy'7”?乇¥ü¯ÀÝs©FJ/X<…øreçÞAn芗×*ü?`L€ 0&Ð Ô[#Ù3Uï49b &U…ÓYë÷Öz{±½Ñ^w ^ç“™ãWÃ! ~"”¾40;ÿG¡¬åÐÊ‘1èÁ¼ DõöÈ”ç˜l„ÑFXw¦gçž+!¥F\ßÏËñŽvͰÉç,eçk†Ñ­Àÿ™gdù|ŽRâ»æÝ~›IóÙUiúsÌ¡%°^EÐdŠáæäƒbÙQ8Q®“ Mü°:d\j[Î()äú"Ÿ÷ŽßÓ³ã·G#?Ïóe «¶ûr˜7gm…ßA‹yÌÞ®6J/ÍËÉz²ú}òÐËqÀPˆ§åµÈ—ý­ß™`L€ 0æE ÞÉæ•ú§†%TOGÊŽéÞÜ‘ÅÖ’á˜6þÉvMoW]Rõ€æî÷•î­;¤’0¼ ôõÀ4ñæ9¾±_º†Aˆ\ã¾»w]Óþæ>ÓÎzC©·Ë±¬Ç t½NŽTcÉSèÇÌ4Çþ‚°F Ç^4Лó&ŽºÔÕƳ#ÿ‘—¡éêëÓ³|«Ó³}Ãhšy·Õ Âð{»ßÞz! av³|Ù2å7J9'„ºÔ•Zú®Imñîd–(¯¡þø™ 0&À˜h^X¬ù{`ŠV®’J ½[úƒæíG8@S½UÎ¥æe¡^ûzh%GÌôW_X×Í`˜²$¹ËuâÞu¥ÿâ>îR¦BÀÚ%5m–ûòQM×.v´ÔÏÉM‘/kºÑZ?íR¤ïÙŒìÜOÜé÷xvañà…¦ï»x²ŽUº˜aôŽßwV~íêiUî¥kk^ˆÓƒÊ6RU@'¦év¤žgGùBøM"¯5â`&À˜`L Yëð›EŠ1‰6 7g´­°¶ß Ýe…¾ìB“­àrÛ¶ï†Y6þ¾…Æð¼Pû…+Ö íV5±MšCõ6ÙnN:¼È÷“ëöÝð\ì¾G»C [ƒµ”šØ2ÇÌúO47d6wüøm¸åB‹8cÛÎʯ6Xß\…÷—Ù‘}èeš’à—éšÉ' #ŽÁ󨿆è; Ô­û s9„Á«ÝwºWm.ªìåè"¨…%s¬%íã2z¦Ë‘âBä/0el^«|òÿL€ 0&À˜@s"°_ ’–n©†?/ÚPiïø3Ì.Ìõ† ‘·R+’Ž2çÏŸo.úï×cjv„ËÁ]ŒCŸÚ`oê¡+î ¨«º:kÝ;kR–•a235ÅÙUQê@U'ÂÅÇ®»h÷®†÷_ÅþÜÏü–zÂoÎ!†÷£ Ö”nRúO.ÈÉš?ÈœÒQÙþnÇ/¥érh;B@mMZÂxvÑâ¢ÝçiíÓ>š>jÔ®1S¦´Ú¾½œÂÚAn±k}¶åØË0ÅŸÚRè•iþ QzÜ\Ó»Tú_…ß2Л;¾µ‘VX*Ê;Vb÷7´²ëD׃ß‹Kªñ'äþÚUË\PlOî%ë*|åÝ$7‰ò¿0&À˜`ÍŠ€VßÔ €ç课á4ÿ ›l i|i×òd×M¬{@CØR^±t§ƒÝØë-ß.GU®µ•L~¤°Z+eOÇšÆ_±¦s#Òõ:Œ+Ìÿz<»hñÙŽógÑóÖZ®ßñ{Å:h!Kµòrص.µ›°Ë:BðúR«d“e©,²+23¿Âí*,¸®Ô*ýG$­„|ˆn—edøÉ{AÃ;Bxa±åÛ¦”98<= w÷¹dŸ(¯n|gL€ 0&À˜À% âô×P‰  .t#Ýã…IçRŸ1#5ž›Xvé……š'ma¤Ó|ß °#Íé=ž]4÷Mfœ| ÏË;8Vî3§·§3##Ã¥4ÌÊQC³'õ";ŠƒÒéÎ}—W× ß™`L€ 0æC ®Ô|’Ù0)q…H)s~ÄȡÄ#˜*ÿ½¼$Eó?;g\P[ÏÛ1&À˜`{zOmï=Yå”2&À˜`L€ 4$$’&‡F ué~9Oê’v—óŘ`L€ ìcb®WK6Ÿ‰ŽÔI6œ¦p·¯l j V m8B8é ‡Á˜`L€ 4?õ$©Óœ²òIÃæ”,N `L€ 0&ÀöJõ$¹Vò$w# ½“æ/Rh#ÍeˆÐkO¸K¿0&À˜`L€ Ôš@½IñèZG̘`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&ÀššÀÿšcódß ¨IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-vrrp-compconn1.svg0000664000175000017500000023512413553660046026222 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-09-29 22:17:07 +0000Canvas 1Layer 1Network NodeCompute NodeOpen vSwitch - High-availability with VRRPComponents and ConnectivityProvider network 1VLAN 1 (untagged)InstanceLinux BridgeqbrDHCP NamespaceqdhcpMetadataProcessvethtapeth0iptablesPorttapVNI 101Provider networkAggregateInternet OVS Tunnel Bridgebr-tunOVS Integration Bridgebr-inttapInterface 3PortqvoPortPatchtunPatchintPortInterface 3PortqvbvethMasterRouter NamespaceqrouterVLAN 1 OVS Provider Bridgebr-providerOVS Integration Bridgebr-intInterface 2Patchint-br-providerPatchphy-br-providerPortInterface 2OVS Tunnel Bridgebr-tunPatchintPatchtunInterface 3PortInterface 3Self-service networkVNI 101Overlay network10.0.1.0/24VNI 101PortPortPortPortInternalTunnel IDInternalTunnel IDInternalVLANNetwork NodeBackupRouter NamespaceqrouterVLAN 1 OVS Provider Bridgebr-providerOVS Integration Bridgebr-intInterface 2Patchint-br-providerPatchphy-br-providerPortInterface 2OVS Tunnel Bridgebr-tunPatchintPatchtunInterface 3PortInterface 3VNI 101PortPortPortPortInternalTunnel IDInternalVLANPhysical Network Infrastructure neutron-12.1.1/doc/source/admin/figures/scenario-classic-mt-services.png0000664000175000017500000012631113553660046026314 0ustar zuulzuul00000000000000‰PNG  IHDR=¨­ÒTlˆzTXtmxGraphModelíš]s¢<Ç?—Îii{YmŸíÌj·3Îv¯#DÌ4$<1ø²Ÿ¾'’ð"¶Õ‘âno„ çÿË ‰öÐ0^“8™EHXÏsÂuÝõ<Ïuo.àK+›LñýËLˆ$ M¥B˜ÐßĈŽQS’E¥¢‚)šTÅ@pNUѰ”bU­6¬Új‚#Ûb!LÌêê/ªy¦^{~¡?ÍmË®“•LqðI‘rÓ^ÏC³í'+ޱ½×öAÑ=Q ·ÑGñzH˜¤QÿÞ(Í;) 7yÿ߸²Ä,5:!rIâoDªzžÏà^ƒ©„£He—,ÔÆÆF‘µÖç*f ¸p¸PR¼¡`B‚‡šƒelGÂŒFNè/}°$RQˆú­)ˆiêfJfL¬@›ƒF p0\ML7t«ÛóŒ…íh9&@ºbÑÌÃì%7PÅ”"ƒŸ¡Öž® .¯Œ6/Ùi4l¨‹òfÀñã 3«š9Ö ý¤üÿSÍ̶ ¿ØÆà*¸~². ­C¨$cqÏy„¡ºÏãLNfm½aýl¢{¬¾šSE& |ÐÝ Á>*ìðA{cxJØ“XPEÅ^.F;”Ðm|ˆÑT(%b(H¦¼¿HJ§ãáWñpÍp+óá^ìáóМÈõé€xûy$j%ä åTcÉ-ÖAú0qÁã1ÉÓÈá '#EHÌab8k¸%ÅRQ&ÅÒS&ÅÖ; 3}*({ØÀ±¶–Oúk<Ò]xbiÔ§¼C©9”nÚDɾ•P2¼3—¼káÁvùá±ûqð ì…§¹e;\¸5¢<…‘ìä®íÚõSQу—ícüc>ªÎ’Ø!j]bÿº,µ›ÙÑX¡þmÔ½Z6Š rZŦ¾…Ð6z›ÂF‘ ; žóãy¢¥ƒ©Y˜òó`jdËÃ5›‚­Ât÷0|‚ »<Ôt²H´“‡ÌNl«èŒ‰Â!V¸Ã§y|.ÚÆê›­C'©ÒŒ^׿úÒ [dßMϳõéÖ·>»…m£Ã1~çYŒÔw,óáØYõU¿³5bUÞßV'^,˜Û½ñ7Nj—žúòñûó„‡M¢‰^èÅ]—®O×íºxŽu[·ÙÖ|ò‰S?œwÙ–•þ@„î_$t_´ IDATxœìyxTåÙÿï9³Ù3Ù7$Ī .ÔºÖµúÖZ}KÛŸVÅVk]J­¢¶Š *Q\Xd•¬BBXEÜq£ú¶%D\=BHH¾¿?ž3äœI&9‰™™3“ûs]ß Îsöç¹ïû|ÏÌ9"†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a˜na"¢K‰¨’ˆ6ÑQ"ª'¢ÑKD”½C ÉT"‚¢¢|,DDWSÛñü;ÊÇÂ0 Ã0LHD´˜Ú.ØéÃX¨aÓcÑ㊌0 Ã0 Ó!Öà|HD ‰h‰O|íG‰¨”ޱ#î#¢µŠ~åc!êÛ¦çbj;÷¥Ñ=†a† ÍgÔvÁº5h^‰ x`þ3‘=´˜‚M›†aÆÀX‰¨™ÄŪU™¦€Ú.håAó¨¥Ðæç‰OˆÄW"ö m UÍûª]mzïà~¬š_ª´y¨Í|´|G¦Ç©jûŠ´_c™H\´gRÛsC=9Pt×ôXˆ(]‘_iK&ñãNÕ¶Ô_©÷Q©´ID´·ƒã´Q µúÓ"a¦:2*lz†a˜>C&]EDOÑG¤5>ÛI\Lï j¥#ªíªMÏ/;دDmû$LúÂü· å;2=§ªÚÖè8מœG(zú ó¥D4›´"«¥6=꯸‘ˆ†©–}Bµìɪvõ³PRUóÕf‰MÃ0 Óg¹ŠÚ>1Bâ“=f$Ì Qû··:b‚j™s‰èÕt^в™ž‹Tm:έ'犞˜žqAû¨'¢Ò~¥ü@ðBÕ¼KHû;KCUË«j¯é`ßvÕü=ªv6= Ã0L\r:­T4µ“å>¦¶‹ÚÅ$ž¹ L?DâÁÕP  ÇôüDµÌãDô©òÿà7’ˆ:6=9ª¶÷;X'•Ä×IiÊtOÎ#Ý5=ùÔö°ô×$Ìeàáß)ªm›õ~Æ“øIø¤Hýužú“œMì°jþ{ªvµé9%héªylz†a˜˜"Ú.¼-Ôþ"G$žÙCmµd"ºP5=#hy7 rýBÕ®ÇôH$žÅ‰·ÈË?ÚÁ²™ 5)mMÔfnˆÄoÒÎõS¥­'çŠîšžTËO š§~È8Øô¨¿âÚLmb?´œ‰Ä³<íœ4¬jÞlUû›ªö‘AÛÛ¨šÊôTÃ0 Ãõö"ú3EÂ]MDïªæ×*ëØˆè ¥­Äs)&"ò‘öM¢'UûÑczˆÄïµ4¤ƒåB½½5OÕ¾ŠÄëï^_wÚ-÷äìHßø´ª§ç µééJ'“0>M!æ«Ûo×±¯/©c£á%¢o»8–¹Aëüª“eëUÿW›žAË}Eú>c†a˜ˆs:‰• u±[KD#:Xï7$Þ" ^¾šÚþ\D½¦G"ñŒK`Ù'B,×ÙŸ¡IÂÜ×gÔñŸ–èÎy„¢»¦‡H¼=l|^!¢{UÓ[:ØWðNèä¸N&í×Rµ’xF'øy%iŸ)R›#uûoƒÖY´<ÿ"3Ã0 ch‘x ö>"ºŸÄ…¼£¯–Ô$‘ø½›¿’øÛV§…ñøºƒƒÄL$ñ•ÝO©ó7°¢u‰èk+øm©ÎP›ÈóºXV"¢ÑM$Îï twÆY$LÙ_I<`ÞÕWVf¿†ý ‰ÄõšE†a†a˜˜¨í-«o¨ã?¢Ê0 Ã0 ³$’øaÇÉÔö)Ï Q="†a†a˜0 þõåÀÃåý£zD Ã0 Ã0aàTo¦}ED+Hüu†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†ˆ,Dô"z—ˆ¾'¢V"ª%¢»ˆÈ¦}þ†ˆnýÛpÑ~"úKˆéÞÂJDÿ!"ÑÐæ÷Sæ]Û ûú#""w/l‹a†a"ZHâ¢ý=NDÿ ¢·•¶©D$…a¿5Ê>L?`Ñ0=Q{#Ȧ':„Èö†ï.ýHÜh\¤sy6á‘%7…áâ‡Æ7Çóü–D>HZsc"¢±Ê¼‡a¿±hzö‘è?ÍçDá0¢½—Ý凘6áá%Z7…áâ‡Æ7ÇÓØHܱ|D"¹ƒI!¢ $‚/€•ˆ#¢$‚ûÿHx ‰œDô-ý?"zŽˆŽ’(è•ÊöÔIP>í!¢"z‰ˆŽÑ):ö¥ÇôHDt»rž ¢/HÜ騷£}« s5‰x˜ˆ²Tó;Jô®ŽˆÈKD³I$ö!"šGDcH›è]_&F4–L›ðð­›ÂpÑ[¦‡c‰IúgËKÔv×ó" cS©Lߢ,0=ÿ&¢ÕDt5ýMYf‰dëG"ñ6’0<^Æc “ð2¥éØ—Ó(L3‰èwDT¢LŒ\Àô¨÷tÞD¯$¢LqS[ Nt=ý$“0› ¢g•ãù§2­Nô®Ž¿/Ó]#Ú™ìÈŒ ¢õÔ6Î*íËTÛ¼Bi;K™ÖsSl²/'­é±Q9‰8ÒÁy³ á¼)E"§ë‰è;"z”ˆ²‰h5Ñ^"ú½²Î)DÔDD—ˆ½F"úŠˆþ®:.5¨jʉè"²SûøÎ¢î§Ñcï,×–ý¸‚ˆú‡8¦r‰à¹TçòùÊòcTmA¶ƒÄ…<Ô;•ÿXB"X­Ê´úŽ#p!ØMD‰ÝØWW¦'Dx™ÚC"¢2eÎûFmzˆDÁý\™Nt=Ç>RYæFÕ2>0èzŽ¿/Ó#JÔµ 6ã…$.Jeþ£Êò_‘Ci{…„9òþ›‚`“}&µ™ Í"1î# ›ðÈΛÂýD4ˆ®$a @"Öž&¢_‘èû&"Ê az@DGH˜žÛˆhŽÒ6…DmèÊôHÔ>¾-Ôýñ4rì¥*ËJbÌVúôj»î0}œsHÍHËÿ¯²üIAíªö@ROZæ"ÚLmßwdz^íæ¾º2=—(Ë ÚÆÏ•ö!öL°é±ч$ Œ‡Ú'ºžc†D‚z‚–™@m‰®çøû2Ý1¢z ¤:.ÏW¶q†²ì[Dô> ƒ’G".þEâ¢e"ý7Á&;ðõÖ%Dô‰»Ô‹;9o6á‘!œ7…ïRÛ§49Êz¯Q[¿>UNm¦ç Õ:&†§…Ä'DzL‘6¾{2žF޽Ÿ(Û9Gµ_’¸9ˆaˆhu~'#‘øjê!"2“pÔÇI¤š‹•íœBmIý eô˜ž‡UËëÙWW¦çZÒ~œ¬3Bì;˜àD'o.€ˆž"ññ©:ÑõûLÏÚƒ–ù3µ%ºžãïËtLjê5ê¸L qw}‰ñØOâí—£Dt‰¯7šIܽé¿)6Ùc]­ü»:7lÂ#C8o ŸRÍOTæ¨ÚNSÚΤ6ÓsUÐvË\J=3==O#Ç^&‰>ø7‰O‘Ò:8~¦c'¢m$>Jìèã¿DE¿B™p¿ åÔí¤þ{Ð2zLσ!¶j_]™žÀÄH…C­!¤½óVï;˜ŽL‰ˆž'q‡þ ê8Ñ;;ö'©ãDÚ]Ïñ÷eºcDõHu\J$žéYCD§“¸Ó@DŸxþàbeÝ\e]½7Á&;p¡=¡ü{'çÍ&<2  ðݪëcÀô¨MMG¦çÜ íf(í7PÏLOOÆÓè±7œÄW€öÿÑ5ŸÏœ1=ävÁ1–Dâ0Q[þ…Òê£ÊwˆèKÒ~|ûCMž}uez²I܉ßt,÷ÑZÒ>ÓÓ]ÓC$> øZ9=éª=pçòÕ2Áéê9þ¾LwŒ¨^üvËÝ$žÙ¹›Ú ó³D´…ÄÛ‰ŸS[±Ö{SoÓs+‰¬$ñ€hfˆóf"uS¨×ôü*h»?RÚ/§ŽM‰Ä³c¡LOOÆ3VbÏJDQ•²Þ:8¦b%¢Rñ ·þ$½§´-¥¶„7“x $Þ<¹•ˆ–+Ó·(Ëè5=UD´‹Dû©ý…@Ͼº2=&e¿ ¢IÊ6f+ÓOªŽ·§¦‡ˆè¨í®"èzŽÝN¢ˆÆ“ø¡³ÏH\`Ht=Çß—éŽÕk ƒMÏHó-$œ$†µ•Düª¿¦Ò{SБéQ¿½•Gâîxu|‡Ê&Â?NâÍ?PûW5­$~ k‰`ú‰è5=W“êE¾#ãÑÕ¾ô¼²nV¦¿P¶ñýUu^?ÔôîÌÕ‰®ç؉D¡šM"±ÿKDó•mì§¶7º:þ¾LwŒ¨^©6ãvj‹gÑÍÊ2i$ ÜyÐ{SЕé!7 ñq}0lÂ#G$n õšˆÏQD4W™žNÂÐØH¼µtDÙïõ$ÞÒú–´¦Gß2u<{'+ÓŸ¸vV¶s˜Ú?SÄ0 st׈ê1j3ž¡´^ |Dn%ñ°d%í[ÏMÓ“@â‚õ>µÿj…Mxd ÷M¡^Ós5‰Ojš‰èe?êýŸLmæá 1ö4iMOp|ww<{WÑ&e~3‰OÇBýôÃ0 Ã0#`zÎéjA†a†a˜X†MÃ0 Ã0}6= Ã0 Ãô Øô0 Ã0 Ã0 £“ºêë‡lZQp튂9ÿ©)ø°¶¦`ϦåÇ6Õ€eý·¶¦`Ïj >Ü´¼ dÓò‚?m­¹!Þ~ù¶S8NcBq§1¡¸?æRW}ýM5Olª¹~‡•ÕsíÜ´üúñ›W]—í˜ §q£˜ŒSŽ¿¸QLÆÓ ÔU_?dÓò‚!«7µ¼àXmMÁôxIjŽÓ8UŒÄ)Ç_œ*Fâé¶¿q‹¼©¦à‰Ž¡vÅØ¹áaØ2‡w–£iÏZ´ÜÔÄ2ˆZ¼ƒæ½oàðÎrìÛ4 ß|ò¶¬þ]G‰}¬¶¦à•íoÜ“ÿˆã4¶ëqÊñÛŠõøcz‰Í«®ËùÏò‚OƒþË÷þ†úm󢨬ž©õàüwû"ìÜðpI}]]¬ÝÍpœÆ§b%N9þâS±L/ñŸšë.ÙTSP¯èíoß‹ÆoªúYq¢Æoª±ãÝ¿´»›Ù´¢àŠhÇ BÇéò:–1ÕøÍrCÆ)×ɾ¡X¯“Llª)¸:øãÙýµS¢x¬ð¨õàû8°e:jWÞS ÍqÚ·d´8åøë[2Zü1½„rçrbPëÖÞ‚£|×Ò'tìÛ•Øúú(ÍÃ{FMhŽÓ¾+#Ä)Ç_ß•âé%¶ÖÜp†ú£Úmëÿ„潯õ°úˆšö¬ÆÖ×o×ÜÉÔU_?$Ú±©¦ã8]×abŧšö¬Ñ^x"§\'Y±P'™.ØþÆ-²úa¼­¯ßΉÜGÕABÿŸQÞVà8eE3N9þXÑŒ?¦Ù´üºñêï¦~SÔo`õQ5~S…Úmß]×Ö\?6Ú1J*N£_YÑQ¤ã”ë$+ê$ÓÊjøYôýµ“£L¬èë`Ý,Í÷ÖÑþø¶}œNA´/º¬èë`Ýk‰S®“¬Žd´:Éè vEÁœ¶×-ï‰z±Œ¡Öƒïj_Ó\qý<ŽS–Ñ©8åøcu$£ÕI¦ 6­¼&Cý‘‹Cï³XÀ¡÷qdçbÍ]L´~«]œîZõbÇ2ŽŽìZÖ8å:ÉêLF©“ŒÔßQùÞQ–ñ´ãûU }ÝxCÄ©.´,cIs·ÝËqÊu’Õ•ŒP'lª)بï·/½ÇbiôýöEê7vF=NwEýË2ž¾ßQ¶8å:ÉêJF¨“L(¿7qâǵZ¼õÀaO­ÞÆæU¿=‘Ð[kn8#ºqú¢}WÇ2žZ¼–8å:ÉÒ£h×IF›j®»30@ß|üxÔƒ†e\í|Œú;ë?E7N£eSáˆS®“,½Šfdt°iyAI`€l~8ô.‹Õ¡öýçeÕß*˜Ý8~qcSáˆS®“,½Šfdt°iEÁ:üeqÔ†e\}¿}¡æ—G£§%ˆö…•e\=WÑ+qÊu’¥WѬ“Œjk ö¨é»ºöЧã«•— n^jg+†´y–uó°sÙ9¨ÿ¿ÇtyóžÕª_-ØÝ8Õw<ôé#øjåÏ8NcPÚ8Õÿ•Róž5½§\'ûžb±N2:ØTSðßÀµì_z§S5}]†mÅý¢¬ÞÓ¶âl4}]Ö娷ì_§¾ƒùoôât}—¾¦¯Ë9NãLÛŠOBÓ×å]Ž}Ëþõ½§\'Y±P'¨lKO"o™íŠzð±z_[ç'êJhu¼D/N;¿Ójúºœã4N%â´¼Ëèí8å:Éj‹?ãÖIFÝIæí‹‡œüÍ3Mø®Òco>ÄŠ!µ~˜¦ws°oy26Ï4Ó/Jó ›ÌÝ1=§ñ¡ÎãÔ¸¦‡ã/>‹u’Ñ&™¾RõŸý]“Èoö>ÌgŏެËÒ$týgï4Œnzê?{Œã4Õ>N;Æ"¬¦‡ëdŸS¬ÔIFz“ùË¥CO øže‰ÀYq¢=ËOŒí®åç2™õÞi¹tÇiœª]œFðN›ë$+ê$£m2¿R[ç·½}ÐôV?`C+NÔôVÛ—uó:£›ŽÓøU»8šéá:Ù u’ÑÞdV?ÌÕú^ð~.+NÔú^NÛGò³¬†Lf½¦‡ã4~Õ.N hz8þâW±P'ô$™ñ^+Τ_&sOLO´û”æ85¸é‰v_±Â¬“Œ´ÉüfHiûݬ8“6™CÇ!LÏÁ·CŠã4¾¥ÓÐq^ÓÃu²¯Êèu’ÑA’ùþ¬8“Ñ“¹G¦ÇýÊ gœ†ŽC˜ô+œñg¼:Éè@›ÌëCJ3Øo÷‹¨šKý8ÃïÌ@£ª½þy’ù±c}ÛX™‚‘^;Ê–÷þ±Õ?ï@êÐdì{KÛÞ0݃¼ÜÔ®‹l_õTÚdÆ0=:¿^èå>j©LÂÙ†)åíçEj¼c1§‘ûz!ê$+ü2zdtУd~뤈ª¹$C‰@6;&gŸh¯/T ý]lcE²(ôÕ½lõ…¤M¾7µíǫҰô¹tÔ¯l_õTFO晞^¥‰Âô”µŸ©ñŽÅ\_œÜôD)—Y‘Š?ãÕIF=Jæ7³#ªæâ sÛ1æ2 ÏIÄ®u¢½¾PFÒ ìP¦wOð⊓$˜È„¬an,)ÍÖ¦aü©ˆö ÎðI¸z|&ðf6ßa™í(]– ¼‘)ƒ$œÿpZÖgaëcnü$Ù2I4‹ÕbGgyð£!^̽ю¬~n¼þ”, ýúlžåÃ…~+î)‡§º‘—ëEíÚl4Îöâ´Á^,ø½Œ|Áœ`ÃèÂL3XŸ…ýÕ‰¡>¬ 6Üþ'/ÎÊvG¼žÌ=2=½ÜG-‰![1¥´ý¼ã}dŠ 9}¨{]¬§žÞñ€i'ûP»6X†Gs̸îÉ '1œ -a‹Sƒ›žç0+ü2zdt Iæo„”f°×gETÍE> óʨ*JÂ-É®}"Ç×g¡~¼Rè_ÏÂñr?.óZ0ê±4ì­LCÉ/-pñáóµYÀò$qw»,üÚŒÜÿMAÃ(9Ó3I¸mb&P„ÿñZñ¢L›ïÃ.+î—†}©˜ûs3œˆo×eáèL7r-N™€Õ³Ò±û9©Cñu‘×$›ñ›ÇÒÑ´> ¯ºÄEpM_ó` EÂ…·¦`wM:޸Šw®[×f¡aºù^;ž™˜ŽýÅÉx(ßJrE¼5ãÛIÄ”ééå>jY⦧¤ý<½ã}d²brÖŠõ4ÓkÒP8ÄŒKLæ»mÈþ±»ÖÅ~.„-N£ez Z'Yá—Ñë$£ƒ%ó™Uó"†ydÔ,ÏÄî'd¤$9±bY&ꟓ‘”ïÃŽµ™Øù+üÃýØ»N¬ÓºÔŸyl˜Už T'b¤Ç†²e™¨/t =߇mÕɸ-Û†{/6cè¨TzÕ…Ül7þ¹*µ·YrA"(Û:^ìÃp§ ‹*2qt†¹•Ëļúçd$õw¢ ƒà9?û•uÄEЃÚ5™hœåA¾WFMµ˜×8Ó“³Ýظ*~eFÿ_%ãˆr®ÆÉð%»"ÞÇFOæ=HÚË}Ô²8#ìVL)i?OßxgâÈ$'rzQ·FÌk7=݃S=fdûíxuQF\äBØâ4‚’ÆBd…_F¯“Œ´É¼.¤4ƒ½.#¢j^èÅ05ÕÀšTÌ9[Bÿ«“°ó»(ôkÒñéf™à‘à÷Hð{L°ËVŒŸ›T)…¾2-KðS¿E“<83߃w—‘yFÞ¿ËŠ~—'áðºt¼÷K3ݘŒÆÀ1T'âjŸ…ó3ptºƒ²]øx¥˜Wÿ¬v2á¼+eœä´aòBÑÞ0E¹®Î@ãL7†d»ð‰²NãL7N9É…+Ó±ü gÜ“Šfe_G§º—æŒxk“9tÄ”ééå>j)WLOqûyúÆ;C19Ô­óŽLtb@^Û4Ö¦bÊ`‚|¶{^\[œFÍô³N²Â/£×IF=Jæ×Ó#ª…¾JL7Í÷â<—wÞbCb¾;V§cûŸ­È¸ÈúÀz«SQ;-פËü¢Ð/MV§àåÁf\u /MÄ¡"/F¤Øqï©füò©T´¾ž†M¿· å|?*Û:^äÅp§ ÊÓqtš ƒ³]ظB̫֎ä!>|½*Ë.–|±{Ö¦£a²y9nÔ®Jo»è)ë´M§á+Ìè÷«$Qöuài;’ïc£'sLO/÷QK™O˜ž¢öóôwº09¹l^%æíÜßê”é…ä¦ÛpF‚LI‹‹\[œÝôD8‡Yá—Ñë$£ƒ%óÚ´ˆªyGúe¶Tüû6 ¬D ìX•†æ…^\ä³à¾±Iس8 U7X‘˜ïÁ–•i@eFº­˜Q$Öýô3LdÂEcRÐR“„3d·bN‰Øþ±Ùn uZ0úédXœ„Ù—HðŸãÃ7«Ó”BïÄÆ±lý3v¤žîþ5ihšïÁy3þ21 “”‹àÊ44ÎpᔓÚÖQOžèDŽÇ†ñ/§àÀ"?d%:#ÞÇFOæ=HÚË}ÔRêÅ»…ÓSp " T|¿2M÷x7Îp!ßiEáÄ.õãá|¤nÔ­LCë²DÜ—eÆ…)Ø>Ú†ÔAnlZû¹¶8àƒ¤‘®“-¥^Œ­˜QÜÖvd¦#=.¿' ßbãd/¾\­^/+/’~¹xp:Ü$ààí2ëFJXå+ IDAT ²`Üì ±©LÀŲ…sµÇòõ_­pòb×ꞟÏ•:¿¢u íâÏ€u’Ñ6™_)Í`¯I¨šç»1ÌcCM¥ª½:c² ”çÁŽ•©Àšì|Ò‰KÒL "¤q`þܱlM& — 9l(.KÅá 2’ÈŒ±3S€5)Xs‘ 4À­+”m¯NAÝÎM6È„¼³\XU"æê…~¹˜®gGêi>ì[-ŽáãëÍp vã³—ÈËq¡vE*§+=eÍôê||—Cœ{Š üQFn¦3â}¬MæÐqS¦§—û¨¥Ä‹D  =:‡'êï•É(»Ò<™6<2Ú‰3»Q·"ŸÜdAæù>ì^• Ô$bÜ@ —ÜŸ„æÏ…º½Óÿíâ4j¦'üu²¥Ä‹²3Š”šëÁU &\òÇD^Ц×Ü8ÅnÅ« TëU&àFŸ„Û&¤ y¾CMòÚ±¢BµÌ2?~ï'É‚q¯¥h÷»Ô§˜í±|=Ú g¾»VE¶&©u¼Ü¥O%¢~uôŽ¡]ü°N2:èQ2¯Naõ’Ïò¡¸0 MÊtý³v¤çº#~FO晌/+ŒqjtÓóγ¥Ø#LÏ¢4-ðâ†dοÕÿ®R–©IÄó9&œýçD4+ëü·PFZ’Œ·—¥ yžÃ<Ü8ÐŒ‚§“Ol÷ðy9vœçSLz¿^azæhÛ¿m¦ge2V\ áG·&âØê`u2þy½Y—%`ûÃVô?ßg/´À/RòdLŸžŒVe»ŸsáŠlLDÈ:Ý% DûÑWøÑ`æX‘•iÇrM8çÞ$WÖÛ|«þ³½øò妢&¨NÄœË-H5È*á§7ø°ge °*[q(?¯` ³X]ÜÁ~²½¬“Œz”Ì«’Y½¤Ã/Ê8)EFÅÂd´,õcÒ&ä]—ñã0z2÷Èô`|YaŒS£›žpžÓ3ånM%œt•Wj—Ùv§îAnì¨IV%á­Ë%d]áÃáUÉhžëÂ0Ÿ ³þhE¿ ½8´J,óî•fœ{›‰fŒ›´ß%^\,›Q8[Ûþõ_,pæ»±kE2öýÆä|7¶×$Õ~üã$ 7&aï+$2áò;|ØYìÇÒ_›áÌv⳪d_äÁe^3F=âÇÞR?J®0Ã=Ø…Ï—'ãèr-&œü37V¿šˆÚ»-H8݃oW&Ëýx>W•ÿH‘Wdäå8Q»<ûþn…/ÓªY‰øê%.r›1vF2ŽÍrá §÷õc_IæþTBâ9b[Áû镸3`dt Mæµ!¥ì•I¬ÞRM"ÖýÖŠ§ ²C™?wáӊȇ6™CÇ!LÞW†£=¶¬0Çiä^Žtl)rc„Ù„“üÎf†3SÆ»Au¡y® g9,˜<7 XêÃR$üaB¢˜7G˜žåÓݸ Õ†U‹“€JîÊ²à•©^Ü”hƸ™Aû]ìÁŦö_ß(ß…]5â¸.öZ0gaŽÍpâÔd;ÖU$aï ä þUØ–×'˜ñ·É‰Øyþaì]!æµ–zð3³&áèdrÝ6T–)ç4Û…3¼V,.çp¦_ÌkxYFÞ'j«“°ç+¼ÉvLàGÊ$|_êÇ¡êDÔÞlFÊO<8 ìçø<†;¬XTÜ~?½Æ«“Œz”Ì+Yq&£'sLú•Î85¸éùçٲȅ&.¼Ý‡Ã•><—oBîÕ^Ôר–[ž€i'›pæ] ¨IÆ€T;Þ]*æ5Ïvb˜Ï†Ue ˜yª„žöãÈK2vb[¹G˜žAû-wãb›„Ñã|Ø6;A‘ïýÞ G¾ »–‹}ÎbÂ¥$`ómfd_âÁW$bïCx䯾ÀñUùðd? ×?ëǧ’ø · ~EvÙ‚ñ3qt²ŒAY2>®l[o|Ž„kŸôcÇŸ-Èü‰jÑ𒌼ÔV%U xën—f˜ 9̸úF76WøñÞ•øÐ8Ÿ%\í5£pVû镸3^dtУd®ñ³âLFOæ½=c€~e…3N#÷öL¤ëdËBFÈf̘/¦§9p¶,ážñ hU-÷Õý$ t ì* ý¯ôàˆÒÞüšÃ|V¬ZìÇÎ{-èw‘k¯6ãì;|h^¬˜žéAû-sãb{ûö¯ï³À9Љ]ÕbzÛÌHáÄã%Ü4.¨ñcïƒÈ™2þUÙ¶­$üå•l¿ÓŒŒ ܨl³*µ½8XíÇÑI2gÉØ¸T5ÆÿÏŒìK\xùT ÿû„8߆íÂô,óãÐ4>xM´~Í1¹& ÿƒÿ÷;3R~âÆAe;Çç:1ÜaÁ‚Eïç‡ÇŸñê$£M2ï_RšÁ^ž5¿æ¯YžçÂÁjõ<Ö]b‘㦅g߆U© #=”‡w?šñí$bÊôD{ìºÐH‡„±¯jÛv¶À5Љ]UÚ±E_Fme^¶c`¶ŒÏ–jÛC-íó [œFËôD N¶,p Ó3¯­mÓmf¸ÒìX_ªZn¾Ë&d8$Üõ‚ïD{ókazÊp|Žç%˜1<ÅŒf$ånaz‚ki©K˜ž ö¯ï5 ÓS%¦M“ñ#› VŸ «ËDÛÞ- 2áò;Üøj¡•W‹¯ä6V$ y¶ù̸ïQö,ò ê:3óØR™€£íœeÇÆŠ¶ý5N‘1Ä%!1цUʹªãyËïÍðäÉX?χ}3\x8Ç„³ïôâÈ4CfŒ~Ü‹‹<˜}‘ÿYN|SÕñ~~pü°N2:èQ2Wû¢æY²xÍÒcÅŠÕ¼r~Ÿ@ “„qSóoêD¹…w?FO晞h])K;EÛ¶û/f¸:°k™vì/rcécnÔWùÐð’³íø¬BÛjùhŸgØâÔè¦çœgË|‡0=sUí2¡ß. Œk•EÔhÆÅmË6Ï’…é)óË<˜:ˆ@ýeÔ-õå.az‚ki‰ÛÛ×Xaz”˜¬ön<œFð^àÂ!e™½³À7ÐŽ‡Î–à"BÒ+¦Mò¢U™¿óö¶ŸP8Ù†ù3DûÑW3²D}žnŒI#$\ìB½ÒÖ0Á&LÏRZJÜxZÙYL8k¤›‹¾¨û« ç&‘øy…áv¬šßÉ~~hü°N2:Ð&óêÒ v•7,jž)c˜ÇŒó$<î9Ñ~ø9+òXqžO¸©^`™ÿÁŠ¡>ñ€Ó¦ˆe'ÚqZ¾ n²ˆ¿pí3côXŽWu¾–yð¯?Y1ÔK°ú̸ýv;ÎÊ´aC¹˜¿û);®ÈR^¹<Õ†%¯©ö—kÃó—›‘a%$åÛPò´Œú™`"Nÿ…ßTêØFGÇ\áÆø!¢PØ“,([ž~G•7(™CÇAL™ž0õUoI˜m›0=2v-ÖŽý¼Ø7ÀŽÚ /^´ ӳċ†”ö²ÐËw{XìÆœ‘fåõ_~z{*£ß7ºâ4j¦'ºu2êªpãù®ÛV£÷>`†¨‡¢}l‘Œ?ÖIF=Jæež°¨y†Ã|ÌeF¿ó8´Ì,sãÝË%œ{³Œ¿„q¯zÐ4ÍŽ¡.3ž|Ò…½ó\(¿LBÊN|¿ÌƒÆ‰6 4›páoœØ]âÆ7Jp÷·ckEçë5¼hC¾ÇŒgžucÿLÊ%ߊ eŸ#ã2„QsaïJ.“àdÇçbyd¯ïqáÐ|'žÌ%Ç‚¹SÝØýŒ §Ú%LšÙõ6B3Šé1£laxú< £'s^cõ†FÊÆNÖ¶í¾_1=Kµcßð¼ yýí¨]âAÃfÛðÙbm{¨å;‹½}Yà˰¡êU7¾*´ã"wûc2’¢õʰ‘êd4ÕRîÆçÙ0ÐoÁªâ¶vaz5;~eô:Éè GÉ\鋚§ Ó³|²¤Z°ªÈ ”:pW¦¯¼âÀM~ 㦸ÑZæÂŽY.«tãx‰ k®•à*ãP¥¯Øï± ¦Hl³ñ%NδbcYçëm¸Â„þW8pD9–ZàK²bC©;ï’à*cïR1¯u¾ŒŸ¹Í˜5[ìopŠï–Šy]%¡ßåÊvʸ;]ÂØI]o#Ô1£HÆH·e ÃÓç=™{dzÂØ_½¡‘²ˆ uÛîûÌpåÙ±«B;ö Ï[‘×߆ÚÅn4L° ÓS®mµ|g±·çA3¼IL}Î…†¥n|?ß…C‹£ß7ºâÔè¦ÇýÕÛÚÿ°~YÂõw;ѨjßûW3üJ-ö1F,þ X'h“yUHi{©+,jžfÃ0¯«81ónx܉#…V ηaÛBY˜žÉ. Ì‰ŠkÌ8;[ÂiƒÍ¸v¨ ÎÓeZêBãËV É´â“R±ÍÆ—­8%ËŠ¥­çÄòá&œ1ʉfåX޾`E^ŠJ\øôZñµö•K ã'm© ŸüJÂàk8ºÔ”:po†„±]n#ä1/R.d ÂÓçi“9tÄ”é cõ†FÊÆNÔ¶í¾O¦g‰vì SîBà V ̶â³2m{¨å;‹=,vâ­?Zqi:ArH¸ºÀŽÍÅÑï]q5ÓÝ:É2Hü°N2:èQ2W8â橊éYäÄλ$ô;ߎµ¿”pö­4Ù…é™$æ%äZñî\±Þ%$žnÇ¡ '_ 1O=ÝÙzïŒ4¡ß/dQŽåÀ£f$$Y°¡Ø‰í£$düÄŽúÀ±–;Pû‚Œƒ‹Ûïï“_™0øG+œ@‰,LÏ+ÝÛ†fz¡#ÝÊæ‡§Ï2z2÷è•á0öWoè— £žÑ¶mºI‚ãd¾[¢û†ñäõ·¢¶Ì‰†ç-˜eÅg¥ÚöPËw{‡&Éø`ª­Nžjǘ& ¿Íõ¾Ñ§|eØHu’eø3`dt Mæ•!¥ì%ް¨ùU«0= 8>ÝŠó|& O–ðÂd°H1=øâv C¬¨-ràØ,;þ>ˆ`bÞÅ4¾hÁ)Yl,ÛTOw¶Þág-ÈqK?NÆév<’G ¿ŠhžfÅE^ ÷=(cÏU×HH̵bKYûý}r• ƒ¯±ãèPl¦çåîmC3½À†‘. 3f„§ÏÒ&sè8ˆ)ÓÆþê U]`‚ÜÏ‚9ãdìš)ãƒÑœ+›ð‹{d4/ÑŽ}ÃsЉ)u ¡Ð‚Y|V¢mµ|g±·åw<¹V¬ŸéÀ¾É6<ÜŸpö(9ê}£+N£fz¢['Y‰?ÖIF=JæÅrXtÂô,2;¦æ¨Ÿu¥2°Ð†›ü&Œ›(£eŽ cN1Á*²rÌxþ Îw~ög;ŽL°à”L 6‹m6ª¦;[ïx¹Œo5cˆƒ`O’ðÀ-fä¦[ðq‘ØÎÎ1\’*ÞúJdÆüÉí·Å²0=¿¶ãèb(²áÞtƾܽmh¦‹í˜pº ’,¡øµðô;ˆO晞0õUo©e³!a [Ä„7UÂíwØp \YF5ö³Ç˜‘×ß‚Ú ãÓS,£áÙ¶öPËw{-ólxúL“xý×lÂY—X±iQôûFWœÝô ¿XaŒ?ÖIF=Jær{ÜéðËV?nC“2]ÿw é,ø¼$úÇ =™µqªówR Я¬pÆiä~'…ë$«}ü¯N2:Ð&óŠÒ v™-îtø) '%I¨˜fCË|+&NȻʊ£8¶HH›Ì¡ã ¦Lú•Î8–éé»u²¯Ëèu’Ñ&™÷­©º¹m¿Fy¬Ø”ZãKÅV¬»Î„Av˜pæEf|:×Ç5[NŒíÖy¾NãÀ覧n^œÇi–&Nçû¢gzúrìÊ…:Éè@›Ì5!µ«úÇ'|Ï< (±°âD{æI'Ævç²áÆ1LOèßÇØU}.ÇiœJ§gvá5=\'û¢b¡N2:Л̇>¹ÿÄ€ožAø~›Y1®†…6«>²=ôÉý†Lf½¦çÐÆÑ§q¨vqºq´!M×ÉøT¬ÔIFz“¹uÏR|Q:@“ÐßÍ5áØB (bÅ’Z‹$[(ỹ&M"Q’Ûi Ä‚éiÝ[/Js8Nã@!ã´4·Óˆ¦éá:?ŠÅ:Éè@o2c_ ŽmŸ†Ï¦jæbҶÌv¢éË× ›ÌzMö¯Æ±3ñù´¨÷)+LqºsžaM×Éø–Ñë$£m2/ïRǶOÕÜɰb_ÛË)‰ÜõøÃô„þ%Ô€Ží˜¡ùćûÚ^>M;çêÿ𚮓}Q±P't7™±o9Z÷TààGwaWõÍÛ ¬ØQÝ\vU@ý§è÷X2=Ø¿ ­{«pð£?cWõ¹š·ºX±£ºy>ìª>õŸ>¤{Ü`z¸NƇb­N2:Ð&s5‹Õ©ŒazBÿ(‹…ý½ÿãp\'YÝ›ÃÉÌêŽØô°bAlzXÑ›ÃÉÌêŽØô°bAlzXÑ›£Mæ*«S±éaÅ‚Âkz¢Ÿ‡,c‹MÑ$óÞ*«SÃô„þ›7,ö÷þŸà:ÉêŽØôm2/c±:›V,(¼¦'úyÈ2¶ØôNfVwdÓÓÉúc±°M+ºbÓc`zšÌ‡>¹_՜Ͽ?ƒÚ<Ë‚º¹>ì¬<õÿɬ5=ÿjðßBújŧ1¨¶8ŽúOÿÖ­q7Šéá:»ŠÅ:Éè@›Ì•]ªéËéØV”õ€dõž¶e¢éËéºÆ?VLOÓ—¯a[QvÔû–ÕÛqÚõŸ¿éá:Ù u’ÑAw’¹éËéØ2Ûõàcõ¾¶ÎKЕб`zš¾|ã4N%â4òûˆë$«-þŒ['h“yi§Ú^žbð7Ï4á»J?޽=øh+†Ôúa>šÞÍÁ¾åÉØ<ÓtbL¿(éße Ä‚éÙ^>ˆã42NKDÙôpì ŠÅ:Éè@o2×o¼_“Èoö>ÌgŏެËÒ$týÆû ™ÌZÓúoÞÔÿó¯§q¨vqúÏ¿vÑ2=\'ãS±R'èMæ/+~tbÀ÷,K>ÈŠíY–xblwUŸcÈdÖkz¾¬8•ã4N¥Ó†4=\'ãW±P'h“¹"¤¶Îóžð¦·úòXq¢¦·úÛº¹ÞNãÀ¦'ôÏ¿oUý5uŽÓø’6N}ÆAxM×ɾ¨X¨“Œ4ɼ§"¤Ôsµ¾—¼ŸËе¾—Óö‘ü,K§q`tÓÃq¿j§Ñ2=\'û¤b¡N2:Ð&ó’R'3ÞËaÅ™4ãÛIÄ’é‰vŸ²Â§Q3=\'ûªŒ^'ô(™ßÀŠ3=™{dz Я¬0Æ©ÑMú‹Æø3`dtУd~§?+ÎdôdÖšžªâ8oiMOè80„é1@±Â¬“Œz”Ìo÷‹¨šKý8ÃïÌ@£ª½þy’ù±c}ÛX™‚‘^;Ê–÷þ±Õ?ï@êÐdì{KÛÞ0݃¼ÜÔ®‹l_õTFOf탤¡ÿº1ÇiŠÓþ•k#ÔÉ–Ê$Œm˜RÞ~^¤Æ1c¡N¶T$b„lÅ”ÒöótŽã‘).ä ô¡îu±žzzÇv¤ìCíÚl`uÍ1ãº'3ÄøÇpŒ·ô¡:Éè@›Ìå!¥ìõYUs‘ü2ªŠ’pK²„kŸÈÀñõY¨¯$ÚëY8^îÇe^ F=–†½•i(ù¥î!>|¾6 Xž$î.–eâƒ_›‘û¿)hx#%gš`& ·M̪“ð?^+^X”‰có}8ÃeÅýãÒ°¯"snFâñíº,éF®EÂÉ#°zV:v?'#uh"¾.òãšd3~óX:šÖg¡áU—(Bk²Ðøš-.¼5»kÒñÆ-V¸s}غ6 Ó=È÷ÚñÌÄtì/NÆCù&P’+â}¬MæÐqS¦‡ã4¾ã4j¦':u²e‰_˜ž’öóôŽã‘ÉŠÉY+ÖÓL¯ICá3.}0 ›î¶!ûÇ~ìZû1Þ—ê$£ƒ%ó™Uó"†ydÔ,ÏÄî'd¤$9±bY&ꟓ‘”ïÃŽµ™Øù+üÃýØ»N¬ÓºÔŸyl˜Už T'b¤Ç†²e™¨/t =߇mÕɸ-Û†{/6cè¨TzÕ…Ül7þ¹*µ·YrA"(Û:^ìÃp§ ‹*2qt†¹•Ëļúçd$õw¢ ƒà9?û•uDò vM&gyï•QS-æ5Îtãäl76®ÊÀ†_™ÑÿWÉ8¢œëq2|É®ˆ÷±Ñ“¹G¦‡ã4¾ãÔ覧—ϽeqFØ­˜RÒ~ž¾qÌÄ‘INä ô¢n˜×nzº§zÌÈöÛñꢌ¸ˆñ¾T'ô(™×eDTÍ ½æ±£¦:X“Š9gKèuv>c‰¶&ŸÞh‘  ~¿Ç»lÅø¹é@•’h•hY’€Ÿúí(šäÁ™ù¼û¸ŒÌ3ðþ]Vô»< ‡×¥ã½_š1èÆd4Ž¡:Wû,(œŸ£ÓÝ”íÂÇ+żúgí°“ ç])ã$§ “Šö†)JZÆ™n ÉváeÆ™nœr’ W¦cù9θ'ÍʾŽNu!/Íñ>6z2÷ÈôpœÆwœÝôôò¹·”+¦§¸ý<}㘡˜êV‹yG&:1 ¯mkS1e0A>Û=¯ÇGŒ÷¥:Éè@›Ìe!¥ì×Ó#ª‰V%¦›æ{qžË‚;o±!1ß‹«Ó±ýÏVd\äG}`½Õ©¨–ŒƒkÒe~‘hKÓÕ)xy°W]cÃÀKq¨È‹)vÜ{ª¿|*­¯§aÓï-H9߃ʶŽy1ÜiÂòtæÂàl6®óꟵ#yˆ_¯JŲ‹%$_ìÇžµéh˜ìD^޵«ÒÛŠŽ²NÛt޹Œ~¿JÂe_ž¶#!Ùñ>Ö&sè80†é© )ŽÓ>§ÄAxMOtêdK™O˜ž¢öóôcº09¹l^%æíÜßê”é…ä¦ÛpF‚LI‹‹ïKu’Ñ&™¿+ )Í`¯M‹¨šxD¢- ´¥âß·Y`% ô`Ǫ44/ôâ"Ÿ÷MžÅI¨ºÁŠÄ|¶¬L*0ÒmÅŒ"±î§7˜a".“‚–š$<˜A »sJÄöÍvc¨Ó‚ÑO'ãÀâ$̾D‚ÿ¾Y¦$škIJõÏØ‘zºûÖ¤¡i¾ç9ÌøËÄT4LRŠÐÊ44ÎpᔓÚÖQOžèDŽÇ†ñ/§àÀ"?d%:#ÞÇšñí$bÊôpœÆwœFËôD©N¶”z1ÂnAáô¨(߯LÓ=Ž3\ÈwZQ81‡Kýx8ßi€u+Óк,÷e™qGa ¶¶!u›VÄ~Œ÷¥:Éè@›Ì¥!¥ì5©Uó|7†yl¨©TµW'bL6ò<ر2X“‚O:qIš D„ô!ÌŸ›"–­I„á$‡ Åe©8¾Þ ×`7>{ɼjW¤¢qºRt”u4Ó«Sðñ]v qì)6<ðG¹™Îˆ÷±6™CÇ1LOè¿nÌqÚ‡â4‚åÚu²¥Ä‹D  =:‡'êÇ•É(»Ò<™6<2Ú‰3»Q·"ŸÜdAæù>ì^• Ô$bÜ@ —ÜŸ„æñº½Óÿ±P'ô(™W§°zI‡gùP\˜„&eºþY;ÒsÝ?£'sLÆ7^dÈ85ºé1À¸±Â¬“Œz”Ì«’Y½¤Ã/Ê8)EFÅÂd´,õcÒ&ä]—ñã0z2÷Èô`|ãE†ŒS£›Œ+ŒñgÀ:Éè@›Ì%!¥ì•I¬ÞRM"ÖýÖŠ§ ²C™?wáӊȇ6™CÇAL™žhm<Ɉq5ÓÃu²¯Êèu’ÑA’yE"+ÎdôdÖšžŠâ8oiMOè80„é1@±Â¬“Œ´É\RšÁ®ñ³âLÚd†0={*BŠã4¾¥}e8t„×ôpì«2zdtУd^žQ5¿æÀéDpŸçÂÁjõ<Ö]b‘ã¦% e#ìfL™Óñv>ÿ›gš !%φ©“|h ^n±7{´oG˜=f\‡‡ªÐð²³e|¶´÷ίáE;ò¤t`¢ IDATȨ­Œl¿ªeôdî‘éá8ï85ºé‰RéÕH‡„±¯jÛv¶À5Љ]U @© #=”kÇ^[š˜±|´Ï3lñgÀ:Éè GÉ\틨šgÉj"ÇŠ%ªyå.ü>@& ã¦úÐ2ß!.&³;Ú†#d 7vcw‘5ךáʰãã%AË–»p³_ÂmOyp Ø‹ý <ØpŸ ý$ yÉ‹†—ì˜mÇg½w~ǹ±ô17ê«"Û¯j=™µ¦gIHqœö¡8í$ az¢ÔGz5R–0vжm÷_Ìp t`×2P¢˜˜"íØ«cK!–öy†-þ X'h“¹(¤4ƒ]娚gÊæ1ãÆ< {N´~Ί¼Vœç“0nª-ó”‹Ékí·±ŒÞ<_V*m‹ø©ÃŒ—f-[&.&w¿ j[âÆc„sîõàð‹6‘ðK¼8RhCN®Œº ±œfz™ÿú“C½«ÏŒÛo·ã¬L6”·?¶†lÈ`Gm…í8-ߎ7YÄ_Hö™1z¬çõŸi“9tÄ”éá8ï8šé1f쮄éѶ Ó#c×b7Æ¿³cO²`Þ?ÚÆ¾A['b¢,ôò¨òb÷Sv\‘e‚‰Y§Ú°$û‹Ý˜3ÒŒT3¬&üô:'öTF®~Pü°N2:èQ2/óDTÍ3ìæ³`Ö(3úïÀ¡e`™ï^.áÜ›eø%Œ{Õƒ–¹2FØ%L™Õ~ÇKÜør®-Êô¡glàµ`EQв¥Nq1y¾­mÏ36œn6áwO»Ñ0Á†Ù6|¶Øƒ#ã­Èɵ£®B,§žnxц|Ï<ëÆþ™<”K ¿ÊÚ[Ãó6äõ·£v‰mh6áÂß8±»Ä7n”àîo{=™{dz8Nã;Nnz"ÝÕHYÂØÉÚ¶Ý÷+¦g©(v`¤ÇŒ²…Ú±WÇ–º=ÔòÇçȸÌ#aÔß\Ø»À…’Ë$¸Ùñy…û²À—aCÕ«n|UhÇEîöÇd$½N2:èQ2Wº#ªæéâb²|²¤Z°ªÈ ”:pW¦¯¼âÀM~ 㦸Ñ2G¹˜Ììd{K]¨}ÀŠ3eþçn'ŽÏ/q´{V‚ˆpú•2¾­p£a‚U$|¹Gž³"'džº%b]õô†+Lè…G”íxÔ_’JÛSÃóVäõ·¡v±¯Øï± ¦HÌk|Ɇ“3­aïc£'³Öô,)ŽÓ>§Ä!LO„㯻)K;IÛ¶û>3\yvìªpE2FºÍ([¨{ul©ÛC-¿ó. þ¡2ö.ûh/ãgn3fÍvcσfx“,˜úœ KÝø~¾ ‡G¿otÅŸë$£M2ï^RšÁ^ꊨš§Ù0ÌkƪNÌ<Å„wâH¡ƒómضP“É.´Ì¶‹‹ÉŒÛ™'cÜ™&$ô³`Â8'š;Ú_±Œ›ý&üöA¶MsbÛ4'v·ÍoxÁŠÙV|VæÂ‘ç,ââ±XÌ;ò¬ØP·Ø‰åÃM8cTÛ>޾`E^ŠJ\Øz«'ˆL¸ðn'¾/T D¹ /[1$ÓŠOJÅz/[qJ–5ì}¬ßNâ ¦LÇi|Çi´LAëdw5R–0v¢¶m÷}’0=K\À"ÅÄ,p¡A5öêØR·‡ZþÓkÅ×^ nüŠì²„ñ“]Àb'Þú£—¦$‡„« ìØ\¹>øAñgÀ:Éè GÉ\ጨš§*“ENì¼KB¿óíXûK gßê@s‘]\L&9Ñ2Û&.&ÓÛo£u‘Œ‡˜pÖUv|UÞÉþŠdÜœ`ÂÝã;žß𼳬ø¬Ô©\<¬Ø¬loÿƒføúYQWîÄ;#Mè÷ G”õ.ê`›Ïš‘×ß‚Ú,8%Ó‚Åb^`:Ü}¬MæÐqS¦‡ã4¾ã4j¦Ç˜u²»jY`Çì_HèqæM•pû6(W–)¶cÂé&H²„ÙcÚÆ^[꘵<ËØ9Æ‚KRÅ~Ò™1²r ólxúL\D ³ g]bŦEÑï]ñgÀ:Éè GÉ\ngu¡Ã/[Qü¸ MÊtýß%¤°àó’è[G2z2÷Èô _®˜ŽS£›ô+ŒñgÀ:Éè@›Ì BJ3Øe6V:ü”„“’$TL³¡e¾“N'ä]eÅQ[GÒ&sè8ˆ)Óc€~5ºb:N£fz¸NöU½N2:ЛÌusÛ~˜éX±(µ²:S±ë®3!ÇA&œy‘ŸÎ5Àqu ¦bˉ±Ý:ÏkÈdÖšž²â8íCqÚIDËôpüůb¡N2:Л̻ª†Ÿð=ó$ ÄŠí™'Û•§2™5qú]YHíª:‹ã4N¥Ó¡ÆA´L×ÉøU,ÔIFÚdžR‡>ubÀ7Ï |¿@ŠÍ¬WÃB ›UÙúhT§q` ÓSR‡>º“ã4Õ>Nïì4ÂkzBç×ÉøT¬ÔIFz“¹õ›×ðEÉIš„þn® ÇJ@+–ÔZ$áØB ßÍ5iù‹’~Æ@,˜žÖoâ‹’~§q ÐqÚ¿Óˆ¦éá:?ŠÅ:Éè@“ÌßÎëTǶ>‹Ï$kæbҶÌv iÛ ]Æ€ÑM¾+űm/âó)QïSV˜âtû¤èš®“}VF¯“Œº“Ì„Vßɰb_ÛËru%r¬˜ž€ñQâÊ}m/ËÓex¢mz¸NƧb¡N2:èn2ãÛyhýzn¸»ª†£n®'êÁÈê¾êæz°«j8ê?¾S÷¸Çô”èRë· pðƒÛ°«ê,Í[5¬ØQÝ\/vU…úÖ=îø®$ꦇëd|(Öê$£m2Ïe±:U,™VßUxMOôóel±é10œÌ¬îˆM+Ħ‡M±é10ÚdžÃbu*c˜žb«S…×ôD?YÆ›ÃÉÌêŽØô°bAlzXÑ›ÃÉÌêŽØô°bAlzXÑ›£MæÙ,V§bÓÊ…×ôD?YÆ›ÃÉÌꎌazŠX¬NŦ‡M±é10œÌ¬î(MÏ‘ÿ<Нj~Œm‹Ò±y¦%ê¿ýÁêZ[f;°mQ:¾]ó3|ÿéý1kz}x¾Z~ÿNO jóL êæz°³âTÔü‡˜¨“Œ4ÉüÍk,V§2„éÙ½H—š¾x_.>9êÅ“õõ£,M_¼¬{ìÃjztäIÓçã±mQzÔûÕ{Ú¶( MŸ7tdtÀ¦‡ÕÅŠéiØô8êæ8£^(Y½§Í3-8òïG ozš>-³Qï/Vïkë<¯.ãæÇÀ°éauG±`z·ŒÓžÍ3Møv‰oõGë‡ùÀGƒXWˆhX¾.÷bóLÓ‰±Ü2ÛcŸÚôl/ËÑÄÞw•~{{@Ôû”Õ=µ~˜¦ws°oy²&¿(É6ldt MæY,V§ŠÓóå’¶¯´¶Î6ÿöÎ<>ªò\Àg™™sfŸÉF6’*‹àÒªU«åÚkÕº¬Ú^­[«µu_ÐZÁ‘Í…Evd«€;ZËÕê5.UI !$çþ1CÈf˜Ø$sfüÞßïùãlß¼sÎù¾ódøÎ û_Ê‚·ò1JÓKY|Tpd.Ö§yQ–žÐý£þï7„§IÜ{qÁ¾uñ©ÿûͦ'EDBzÁÒS’†Þgø9zÿ†žðfoAŒÓð¢ñ¡ÓðþCaïƒhIÏgKú´å¸«&!êçMÐyìªIh»¶;j›rœA;ól ,f—ž/×\Ð60}¹ÄõRÐy|¹ÄÓvm¿Zwq¥'tÿغàÈ[ZÍ/gÁÆA«RÒ³¢ós«Ÿè ep2_¿j\°6ªñéÔ¿ý‡É÷zè˜]z^ÍÄBzâ ùó™AX„ô„–žÁ.‘ÿm!áÇIìxùxÒ“ÍÎç|\ÔSA–d2Nñ°dI6lÈ`ÜI’Ð|VNñ*\61 ^Ëfó­HªNym/x%“i}ÎþkO½šÍÖÇÜœ™,#É }~ìcuM@¶öÏ÷ò£þ> ®ÕÉÈòðâ˜#ÒÓPàç¿»&gÑ0+øKφ^4-ð2à$…7ÚÉwI¨>{&õäàk½àÕlÞ»ßÅ ¯ŒÕ§qÓ_|œÚÓýÕž0ýÃß+Y‚8Ãp}M8NŠˆ „ô:‚9¤gaH¢õ—vKEƒ=v–V¤p}’Ê•OdrðÕlê'ÚIì“À¶—²9X•Ä…^ 7?–Îîåé”]jÁÕ?6dÃÊà/=µY¼y¥…Üi4¾’IÙ© ª¤rã´,X™ÂÅ^˲8Pìç§•»žÎàë¥i SI83‰/_Îfÿ<¹…“.L`õüLvN°“28‰Ï+’¸"ÙµeÒüj63Ýäåù¨[ŸMS—Þ…snJeçêLÖß`Õçgë†lçzÈ÷è<5-“o*Rx°Œ”èŽþ_ÚaîSHÏË=q†ž8!=‚ŽSÒÓ¶”ûì±S»2‹O8HNt²byõì$öñ³mCÛïµášÄî—Ç´.KâÆœÊ,X™Ì0FÅò,ê':HÍ÷óñÊÜØS㎟Yô‡4öÎt“ÛÓÃÿ¾Ø“º›­$Ÿ“Ìž`[+âÐ(®Ébÿ\¹n;ÕËÛê'ØIÌv2"MÂýÓd¾ Ó8ÓM^®ºuY4xÈ÷òç•,šæy8©§‡M/ödãå²/ïÁ¾àwÝó´o²+úi›]z^ÊÄBzâ Œy†@SHÏ— B­¿´[Ê| öèÔ®è ëÓ™ºBöå)l”žõ™¼s­I’ñ¹ün¿[FÓmŒ+Ì„IéYÖ“CÕ‰ü̯Sò‚—¡ù^^{ÜNúÞ¸ÝFÖ§Ððr&¯ÿÊBŸkSi:œÃÊd.óZ_’Éþ9núdºy{m`[ýx;š$sÖÅvz:4¦–fÂË=iœá"/×KÝ‹=išï¡_O7ÿÓ4ßCÿ,7›Öf°üÇ §Ü™FKð³öÏr“—êŠþ_Úa•žÐýÃ߆ Aœa”ó“""!=‚ŽSÒÓ¶”¥§6°Ü\âã,§…[oÐHèãcÛºL>½ÃFÚyIÔ>n]:u³Sùv}&Ô¥gi&¬Kå¹¾*—^©ÑûÂdö–û9#EçŽ*—<•NëK|p£•äŸ&ñm°­ƒå>†84 «ÒÓ·§›MkÛêÇë$õ÷óù‹éÔüL!ég‰ìZŸIãty¹êÖfÒ4/(9ÁcŽ,gðêÅ*Y—§°/øY{ÆÚñ%¹¢ÿ—¶Ù¥g}º ÎÒ!¤GÐbJzºñ/À–o@z–^—Îû7[±JR¾m/fÐRæã\Ÿ•;ŸèÁ®ª,½ÆFB/®Í€å‰ sÙ˜U8ök,Ȓ̹§qhu ¤KHšù‹íXàaÓÊ=c{°§*…y¨øÏðóź öÏvÑ·§‹M«ûÖÓI”À×ë3h.öq–]åîÒi|Áž54ÍuÓ¿Ý1í—¦¹ÈqkŒ›’Êž²$î##%:£ÿ—¶Ù¥g]š ÎÒaìÌÓ‚°é‰Tz2`e #{JH½ÒÆt¶qq~ªŒ$ɤöw²°(=°ïšLª Ø5J+3hxÎA¢daôütØÎšŸÉH½¼l]l{}:[þêä'I¶òNs³jI`[8éaC:o_cÁy’—w§D&=¬Kçí?Ûéç”Ð’5îû“ƒÜ ×XzB÷C~/¦ â £ô˜oœAétsHOAHÄ?/t>  ”NêAsp¹~¼Ô W\ïçó•&È­ÚÇ0·ŠJär„ôÄAét„˜’ü4LvÐ3ÅNey‡–'3e¨BÞUIÑÿç³KÏšs°:™7F¨Øü6ågKQ"o?îâ’™ÓnIä@´ó«ò2Ìm¥b‰ ÎÕ ÒaìÌ/a‰)é1ÁÏáqÁšÖ]§‘ãÑ CÿËÃ;K£“‹9¤'tÿ0ä·:Ù,rs¶®rßÔ$Ãú¯ŸÐzš‹j“aU[¶sf’Œ$Ëô9ÍÁêÒÀ~M3 èmgÂ/­¤Y%ûØ)çâ7Ù2²$3ðb_¬Lf÷CV²Ïv1ö ~E"9OgæÌ$ZW'³ïYœ<'[jm¶-×$0®¿(Ì™¤QQžÌΧ\”)#Kí,)Œþ9<ŒQzÌ7NŠˆ „ô:‚9¤g~H„ôÄ7Fé }˜BzV%™‚=£lx³Ô-½Ï9NNq¨Ü5ÚÏ×e> ~¦pº›/W&Ñ4ÝAž$sù=~ö–ûxÞ-HàŸ<<8XAMÑYZžLó\'ý5 /,lw\•—«½ 7NJ¢µÒÇõ 2g\íåƒò$öû)¹Ì‚+ÝÁß—F0§"Ì}` é©õ›‚–yN·)ÜýŒÏ°þ›QV|nkûùàw*Égºø6¸í`ƒ!v …Å~š¦éé©òC­\¡Ðw„‡ýµ~¨ JÏt?»° §ë¼WüŒ Wûî~Þœ;›k‚Ÿý°o¶-5ý†¹-T”ùùôV•´Ÿº¨?œçRu“=|»,úç‘Z¿žxcgž*„%¦¤ç8ÿ&ß&= ÝÖø¼N^N`îCÓtú8(ü|§„êµpÏ“ ÂQËÛî´Ò£30¢ÆÇ_{) ÿ[;GZIø‘ƒQgª¸e‰Ä)S9´*‰ýÓìü¨¯ƒ‚V22í¼<þH¬Ldã-6ú¹$—ʯ¯¶s²_cmU`Û{wh òJX½núƒƒS3t6ÖòÚ9ÖaœzœïÌÊD¶>¤55°íè¼þ^s並éz[TþöB¢±½e~Ë–è5ÜÇþå~&äÈœöçZ‚Ûÿ=N£G¢Î+ÕI˜íà$ÝJEE»ã«|s°ÌˆKT·…GFº¨›ëáõ‘:çÚ%þÖMãrfè r¨Ü3ÊÞb7óÎUðŸêà‹¥>š¦êôÏÔØTéƒåÞ€ô w³¹*ÝéyÁÇîÿÍÊ/nqñ¯"Õ—©8Òu6UÚÈwX?ÉKC¡‹‡òd”l;[ª}Pîd˜SeV–yÎõªÜùW7»ŠÝ,®’gçÃjœÇå¾£¤Ç|㤈BH #ü¤§·Eáœÿñ²³*õ¿µàÊq²uù &e.÷3þ$…ÿºÇÏ´’yº›+“<%Iæ¢[}l/õSy¹Š=5 û§ÙɵȜt‹Õ/$ðÍsGò¨¯“n·ðèS~vxÕOF²¤§qª|·•§&&ðÍ/ö–Òé„ÐpXΫqå‘ã¶Þ¬âìïæ‹•Ǟǯ´â NœýøV ®>.¶Õ&ÁªD^þ…BÆE^‚‚sgšDÊ@çGûøWÍñE'f¥g™×²mÏhÞDÕ‘O 7õ輎À[—)$ëá»ãœã† ©‰:¯W'ÒRàäT»…©‰PååÉ ˜”жoK…—òu†Ÿ¬â·Èd÷Ó=ÊÏþH&’†¹L!=K=?(vß§âä`¯ ré*„ôÄA;ó ,1%=Ç™ˆx¨ØÉšÊ´‚c·5>«“×+0á³é;ý2tþQØÖ~2gØI™+`¹iùúP»‚“:w?hÁ;ÐŞß×n’çþ©:}2tÞ®>:?‹Èœv§CÁãZæ98Åocí?ËO•9åV-ÁmûŸ×ÉKÑØXé„ÐðXΫ=[oRqöwòEí±Ûv Lœý`ià\Ì8Ifèm>êŸÕé•¢ñZUp"íR? íÚn­öñÒíým ~Æ≤Q“žÐýÃ_ûE@zìì5A.]…QzÌ7NŠˆ „ô:BLIÏq&"*:,=Çnk|F ÈFÍÑ“7Ëa'eÖúÙó¤Fn §xUî{60yt÷ì™v>¨92Éó7>…§øØ?E§oÆ‘Ï:’‡ ç+dÿÊMc0dž‰énkûxõB…¬‹Ýì nÛ3ÊŠ/QcceäBÃM`=:¯ö4MÕsz¦'ÇRíaL®Dίƒ]kýüë. ¾Þv*.UÈþå‘|w?hÁÝÛÁgísZîeN™Á·zO<‘ÔìÒSíúA±û^ÿ ½&È¥«ÒaìÌ“‚°Ä”ôg"â¡Bgh*ã§xÙSv„ïª}AÙЩ«>zò¦q9ܤÌÖE.îLW¸eŒ—Oï°ÒÛÎUIž²$sñÝü«ÈCÕ¯T\Ù:ïWùØ?Y£oÆ‘ÏjŸÇ¿Ÿ¶‘a·0êI»æºÝ_FrXY»ÈGÃD—…q<ì™ïâáÞ2R‚ÆÆ%‘O 7õ輎ž»ò"‹ÛÂý÷ºxo®‡÷Ç9xh°ŒšdcYq»s¾ÐÁyºLš]ᶉ޶õç;¸À.sæ^›áá‹7/ýÙFަ2jª÷ÄI£&=¡û‡!¿*§ Î0JùÆI„AG0‡ôÌ IDÒ#IHGqÚ"”jÇÎÈ¡Iž)½mÌðÒº<¼ô°ÌË›7Yéç”*#~oçééa™—·ÿ`¥ŸCBK²pßÍ6rÓ5Þ¶cœª&„G^¶Ü§ñ“ÄàÿÖ>DgUa`[Xé û¿whü<[Á!IX\ gÿ—— ݯd°Ô&dí·}3ÑÎ﨤Ùh{äXyø"y{&Ì}` é©tâ !=qBz!¦¤Ço{t% Ó”ŽqÓ\®ÜJjŽÎGUÑÍ«ÛÞž1»ô,± â !=q†Î¼ãy ,æžÙ!ù!½=Ó0ÎFÏ$+•ó=ªp1e°LÞNö› ·ny{&Ì}Ð¥Ò¦ò[¬ â Ãõ5á8)"‚Ò#è1%=&xÛ£K©r±îj 9 Ý.3ô|wÊLWw½=cvéY¤ â !=qÆÎüœ@–˜’¼í!è·g¢&=¡û‡!¿ › Î0JùÆI„AGÒ#Òºl)8’ãR ”[qBs©¥íÚn]à2å8)"‚Ò#èæžY!¯ Ç7Fé }DKzvTŸÜ–ß® ”Yq®JÛµÝ^y’)ÇI„AGˆ)é1Á+®¦¥Hc˜K¡b¡ rù¾¯ ›Pzön¼¦-¿Í³$¾+T TÄ8E ›ÛÝ{{7^cÊqRDaìÌÏ a‰)é1Á+®¦¥Ðž&Èåû¾25é Ý?Z?Ï'¥iñùª@æ@‘%‚X¢µDá@‘ÂW²Ax>)Í0í8)"‚Ò#覞Ïg†$f_^¤óöM*ƒ§„?ˆ¦ÉŒ{ÎFÓD•~i*ÿ(×4Q¥ºÊ¦bXæ’©˜g‚ïþ}뤘\z?\„ô˜8 yû$ ,æž!Ù<ûH-Ceѯí)Ûÿ ãËQymv`yÏ=2 Tö–[yõ™¬ -ì î»çA_‚ÂÆ…V>½A&í'ê·Ub¥îi ß–Ziš Ð?]aSQ`›ay~PzæFÿ»GJk¹µíÚnžm {t©ô˜  ̇èÌ‚Ž`véù¨Ðßö`l,V£^Û#R>ù½Œï$…ºLWy¤·„z’Ê®R O(ä8eÆ=naÏT•‡s%$¿ÂÆZ¦(œë‘¹ó^•]³T–^&“£ða±…¦ñAÉ) |†ayžÊ0§Ì¬iÑÿ¯Hm»¶§é˜!=&cgž(„ÅìÒóÅŠŸ´=wÄNq¸C³UFö“°*½d&üIál§Ä·Y8XjáíëeúÙ%´D™ûþG!7Uáí…c·ß¯p~ràm¬Ô|……ÏGrŽ^.T™4@BÑeJgFÿûGÂ΂#Åá¾Xñ“(JOôû¡ÀÜé1qˆÎ,èf—ž}ÿ¼ëÈ?Ì’Ø_ûÅá&)”>¢Ò\®X&5[ᣢèçÖ]ì/6‡k|ÿ~!=Ó"¤ÇÄ!:³ #˜Cz¦‡å³%ùmÇfK4Åxa¸†Ñ2=e*§*š§0e€DÞ% ûM[wÐX¨°uöáùlIþ ï!=‚h"¤ÇÄaìÌ‚°Ä‚ôøpÎÓ ¿ø|9_¢q¡Ì¡bbB™uWJäØ%t]bèOeÞ™k‚¼ºCÅ2M…2_Ì?êâyvš·>eé‰~?˜!=&Ñ™ÁÒó ixçOl]à‰zAç±ežÆ÷ï‹èú›Ezö¾qÿZ:HÔé‰A6ÏVÙRàbû’>Ô¿ymLŒ“""cg/„%V¤‡Ï_àÀ‡²½²_ÔOÁζò^4o}<âkßµÒsâ~ÒüáH>.NŽúyt'ÑüáHS“""!=‚ŽKÒÓþWŸ/Vü˜‹S u|æeól §ðÅŠóÝ?néð5¦ô48’çêQ?‡‚Îgk;"ñÒcâÒ#è±(=‚Ñ”žOË{¶“7™¯ªýx¥ü½ †h}+Ÿæ×røzy›gËm×ô“Ò4ÓŽ“""Cgþlœ@SHÏ¿¦ aéRé Ó?ê7^mž¦—²à­|AŒ³o]†A|ê7^mÊqRD!¤GЄôbhIÏg‹sÛŒ»jàÍÞ‚8aWMBÛµÝQÝߔ㤈ÂØ™ŸÂ"¤G t­ô„î[8ÛŒÍ/gÁÆgxX]øŒýsÜü¨Ÿ‡‚«52²\¼ø„ž ™4ÌñrŽßÊ]ϦÓ0ÝE^®‡ºµ™4Íó0 ¯‡Âßëä;%TŸ{Ƨsð¥LØÁ{÷:䕱úlÜô'§fººý 鉃Ò#èf—žú·®3¨ˆS÷­ëÌ/=2º•–/ƒ=:KK¹>IáÊÇÒ8¸!ƒúqAéy1ƒƒ‹ü\è±pó£=Ø]݃²K,¸úyùhm,O üÒS“Λ—«äþ:™Æõi” •Q%…'§Ã²D.öX™XœÎ…^NqZ¹kL¾®L¡àç* ?NàËuìŸí"×¢pÒ0«ç¤²ói”A |^âçŠ$•kM¥yC/8Ò³&ƒ¦¹nz[ι!™µ©¬¿ÞŠ+×ËÖµ4Ît“ïÑxjr*ß”&ñ`¾Œ”èìös,¤'ÂØ™Ça1»ô|¶¸wÛ $*âÆ†Š¸5¢(=¡û‡á¡¸>½[i)ö2Ø­S»<é$':XQ“NýÓ:‰ù^¶­MgûÝVüCüì^8¦µÊÏns¥Ã²†¹mTÔ¤S?ÞNj¾——%qc¦;ÎSts {_p’›éâW¥Qw£…äŸ&°'ØÖÁR/C6Š+ÓÙ?ËE®[§º&°­þiÄl#Ò$Üg'ðMð˜€ô¸©[“NÓ7ùÚemM³]œ”ébÓª46þJ%ûWIì ~×=ct¼IÎn?ÇFé1ß8)"‚0tæmc‚°˜]z¶.8ò––¨ˆ_+⺢'=aú‡á¡¸.­[i)ò0Ø­Q», Ö¤0ÿ4…ìËÙþ”ž5©¼sµŠ$ÉøÜ ~·‚ß-£éVƤÂÒ ôT§qh‰Ÿù5J¦¸šïæµQ:é§øxã6+Y¿H¤a]*¯_¢Òçê$šç°,˼Æ/LcÿL}2¼½2°­~¬†&ÉœõKžS‹ë§¥guM³]ôËtòà1M³]ôïédÓÊT–Ÿ®pÊ_Rh ~ÖþéNòz8ºý®¯ ÇI„±3?)„ÅÒ³ãù´”DEÜøâ˜Š¸a•žÐýÃðP|1µ[i“ž¥åæ…ÎrZ¸õz ù¶­NåÓ?[I;×OýáãV§P7#‰oפB? =U©°:™çúª\z…Þÿ•ÀÞg$kÜq²Ê%O¤Ðúb>ø½…ä³ý|lë`‰‡!…‹RÙ?ÃIßL'›V¶ÕÕHêçåóU)Ôœ§tžŸ]kSiœê /ÇEݪÔ#’<æÈr^½H%ëW‰ì ~Öž'5|IŽn?ÇFé1ß8)"‚Ò#è±$=Ñ®Þ*è⊸f—žµ=º•–Bw@zj¯Káý-X% ©·›m«zÐRäá\¯…;G'²kq"Kc%!ß͇+{@µa.+³JǾóY’9wd2‡jy MBÒ¬Ì/ ´`ž‹A ÷<™ÄžÅ‰Ì;_Áº—/V÷JƒMµ}ëŸÒHèåë5=h^èæ,»ÊÝ“Shœ”ž•=hšå¤Ï#Ç´_n˜ì ÇmcÜsÉì)öóp)ÁÑíçXHO„AG0‡ô<àd‚ ®‚ÎÅ(=¡ïSHÏš”n¥e¡‹ÁnµÕíÖ/K`d¦„”çfÛÊX“ÌöÇœßCF’$RûÙYXØ·6‘IC»ÒŠ&é$J*£g'ÚdÖœ+#õr±uE°íÕÉliç'I2’$“wª“Uemû§;Ò³<°\?F#e€—¯Wrxû*g_ï>k'/ÇIÝŠšf%'xŒayu2oߦÑÏ!¡%Û¸ï:¹éŽn?ÇBzâ Œù ,1%=ߣâªÀܘCzB÷C~«“DÃ/¥ãi.×ÕHÍuu{Fé1ß8)"‚Ò#è1%=¢"n|WÄ5»ô¬Jt ÏèôLÖ©,JâP•Ÿ)§Èä ÷u{Bzâ Œùq ,1%=¢"n|WÄšô„î†üV& :‹ÚÖýÖJŽCF·+ ý¹“w*»?£ô˜oœAétsHϳ!q@qÃܦž ‚8CHO„AGˆ)éqã»"®Ù¥§Ö/ˆ3„ôÄA;ócAXbJzDEÜø®ˆ5é Ý? ù-÷u+-sí ”$\g9ùvYûm^Ö/#I*cfø8Tèà MeÚüã·óÑýç¥Ë(’DržéS¼´½ßb×¹%$éª[åª[Üì]æ£ñ9Þ™:ïVuÞ÷k|F#¯—N]u÷ž×ö¥Ç|㤈ÂЙ?}L ‹9¤ç™ˆŠ¸? Š¸aîƒ.•ž0ýÃß2o·Ò2Gg,!¹­¬(k·m‘“ßû$$YaÌt/‡ÚÒ3ïxmØ9CW¸î;KÜÔ^©âLÓx{ÉQû.rr_áÆ'Üì)õðM¡›wÚÈRî~ÖCã³½35Þ­ì¼ïw°ØEÕ£.ê—vïymáúšpœA;óh ,1%=¢"n|WÄšô„î†ü–zº•–Ù:ƒÝ*Wç)Œån[ßð´•¼^VÎò*Œ™îáЂ ôÌ=¶oFZðäé|V\Wlçgv•ggµoE@znŸØnݦIœ~‡›†gléYâaßx9¹:[*û–kܼ÷'+ƒ<V¯ÊM7iœšncã¢cskœh#¯—F]¥‡¦Éò5 ¯±æŸyUîíþÎ_$¥Ç|㤈BH #Ä”ôˆŠ¸ñ]×ìÒSãîVZfi öZ˜s³JÖÙvöÖ¸¡ÆÅk¿PøÉu:#ü c^ps¨@ç MaÚœcÛ8Xæâ³‡‚Ë{Ÿ²ÑËcaEÉQû–;Ò3áȺ]OÙ¨ÊüîI“lôδñîb7ûÆYÉÉÕØRدýrã36òÝ*OuñÍl;æJH~++ŽÍ­q‚¼lº%nš&Ûè­Êœs­ƒe.Ö_­àÊÖºü 鉃Ò#è1%=¢"n|WÄ5»ôT»º•–™éY>Uã§)V•¸ ÜÎmé*Ï?oç¿Â˜i.ÍJÏì0íU9©»ÏÊP]æâÛ8z{™ý˜9=’$1ð—:_Vºhœd HÏ"ûž¶’“ccË’À±í—7^$“}‘}Áv÷üÕ‚7ÑÊÆòcsjœ`%/ÛFÝbMÏÛÈw[¨- lkzÖÆIéÖ.?ÇBzâ „ô:‚)¤gû¤ˆŠ¸? Š¸aîSHO•³[i™ac°GeU¡ƒÙýe~3ÊÁ¾ñVúæÛø¸HHÏT'‡æi院:c†Êø²,Lã åxŸWªs_æ·Øùx†ƒg8ØYzd{ãD+½3­¼[ádßÓ–€ä,lÛ7ÖB¯^6¶,v°|ˆÌ)7ùŒý­ä%[ÙXædë IB’dιÝÁwãƒÒ³ÈIÓsVú¥[ùGyฦç¬ôϰvù9ÒaìÌ£‚°Ä”ô˜ ’l¼`ÊŠ¸Q“žÐýÃ_¥£[i™”žbÛoSÈ:[cí% §Ý`§¥D HχæÙÒ3óØ6Z‹uì%sê¥ÿZæóJt®óÉÜ>îøÛ'XèaåÝrGPr¬l¶÷Í*Þ,+[9xu˜LÖëì ·ç¯*¾D KÓæ8 yÙVê*4=œMåm‡—»ú¥Ç|㤈BH #˜Cz&†DTÄýUÄ s˜Bz–Ø»•–¬é)²sp¦•³¼2C’&NµCqPz&Û947 =ãŸÑÙ³àß•Ûùê…„l+/OÕùø… Óuþ]qÔçkéyúø¹4Ž·Ð;ûevšž±ïP?V§a®ÆC¹J–•-vÆZÈq)Œ£³g¦ÆÃy’ßÂÆ’ã´ùtPzÊmöϰ°©,°íðrWŸc!=qÆÎü¨@–˜’TptaEܨIOèþaÈo±Þ­´IO¡Óó%¤, [Êu(²q_fÌdCs¬œ!;ç´?il¼H>f½$)L›qÔçÛÒ3öø¹4Ž JO©åÃd¼’„;Uáá?YÚ;˜×"·oPég—Ðî»^%7ÕÂÛ%Çis¬J^¶…º2¦Iú§[ØTØvx¹«Ï±QzÌ7NŠˆ „ô:BLI *¸ º°"®Ù¥g‘&8 ÏY)e£9¸\ÿˆBj/ •E?·ã!¤'BH #Ä”ôD©jëÑ„­€[ãaÙ¯-äØU§äÙ˜>í8pÛSéæÞd ÉieUyç_îd˜ÛBEiôÏã1qÍ.=6Á hxB¡g¢Bå ‡Z™2P"ïR+ûMÛñÒaìÌÂbé™’hVÄ E¸ ¸ß޶’”`eþ{º¨¸HAÏ·³½&t{Ïhôr© ñÈ íéÚüË‚ÒSýóxLEÜ0÷A×JOèþ±eþ‘É®J-Pn„£Ôʺá29v Ý.3ô\•w L×qh.µ´]Û­SŽ“""CgþäoAXbJzº¹"n(ÂUÀÝ7ËÁòñnZ–zØ_êbí¯‡ÚÙYª=7o\¤}‘ƒ—.SH:ÓÁžÃûž ºíÎ'4.Ê‘%‰Œ“m, 檺íÁJãúæxh‰* £.M!=aúÇŽª¾mùíZ @™E'ìZ ´]ÛíKòL9NŠˆ Œù ,1%=Ý\7á*àfÏHE’t /LÓ^¹ƒ[’î}ÆÍþgmäº-,-V¬ SÝöà| Ý 7ßïdw¡“² \}4>ª ]Ývk¥Jí s«TEÿ<S7jÒºì}í’¶ü6Ï’ø®PRUã4)lnwïí}íSŽ“""!=‚Ž`é’hVÄ EDp«],s°ìRg/,>þ>ß±Ò3ÓÆ{‹\°ÈÁÈ4™KvÒZ¾ºíöÛüƒtvW¶µ.Ô¹À¥2g^èê¶›*\P¢3Ì¥RQýóxLEÜ0÷A´¤§õ£‘|R’bŸ¯ d)P"ˆ%ZK)|U „ç“’¦'EDBz!¦¤§›+â†"\Üo'ë¼5ËIkpùàl!ºBÁ¼c+Ò¬r°î¼À?7é6 »MB‘$ì§ê|]¾ºí;WŽó¹düA4]aÜÔÐÕm7•;¡8(=…Ñ?ÇTÄ5¡ôðÉ#xïV>Zhœ$ˆ>œ«Ñü6í8)"‚0væ¿ a‰)ééæŠ¸¡W÷íKe2®Ó\nžfåd»JyÁqÚ*Ò¸Æ'sÛ(;_Ì ðÉ( =*‹ ÂW·ýôf…´35ê·µÈNÝDo‡®n»©<ð™Ã\  £©ˆ5é9q?9ðÞ ¿øbŸOËÒiþ¿ÛM=NŠˆ „ô:BLIO7WÄ E¸ ¸ÿ~B%Í¡0ö oæjÌ=[&a¨/Ûη¨$%Yx³´ÝúÛ$.¼WçßaªÛ¶Ì°r®GáÎtvÍ×Yz…BB®•+BW·ÝTf‡BÜ ³fEÿ<S×ÄÒÃ'¥õ£ùö•ÿfGUÃ[]‚ØaË|;ªúPÿÆe11NŠˆ Œùa ,¦žÏÆ…ÄðPìæŠ¸¡W÷Ð"W~§rZ’„$Ëœtª…µsŽÓÎ"egȤ_hcßQÛ^¹@Æ9ÐÊÎòðÕm·´p~Jà³Sû¨,œj¬f{tuÛM¥:”jL(£è ¥s£. ×7Ì}еÒý~(07BzL¢3 :BLI *¸Šê¶]XWHÀ¤é1qˆÎ,èæž§Cbx(š ‚«¨nÛ…qÃÜBzÑDH‰ÃØ™ÂbvéÙRpä Ÿ\EܪnûWÄ]àŒ¢ôD¿ ̇èÌ‚Ž`véÙQÝ¿íÁ(*âÆ†Š¸•}„ôL‹‡¡3ü@³KÏÞׇ·=EEÜøá˜Š¸¯žô˜  ̇±3Âbé’ÖOŸà“ÒTƒøˆŠ¸±I芸iaï>ÛÅÒý~(07BzL¢3 :‚Ù¥‡ÏÆràƒ»ùh¡?ê5>χs5š7ß3Ò³÷Õ_ò¯š~¢NO ²y¶Ê–ù¶/ΡþõKcbœAét„XžÃâÓþAìóiyfDÂcéi~ÿV>.JŒú9t%Ðüþ­¦'EDÆÎü @sHÏSÑúéã|ûê¯ØQÝÏðV— vØRàdGu?êß¼*âëÎgOu±ô„ï#Íïÿ‘çjQ?w‚Îgk“æ÷ÿhÚqRD!¤GÐbIz?\¢)=Ÿ–¥µ=$7Ï–ùªÚÏWzÁßûbˆÖ·òi~-‡¯—'±y¶ÜvM?)I6í8)"‚0væ‚°éÄ]+=¡ûGýk—„§é¥,x+_ãì[—aŸú×.1å8)"‚Ò#èæž1AX¢%=Ÿ-êÙö`ÜU“oöÄ »jڮ펪ަ'EDBzAH ˆ–ôl-8ò–VóËY°1O'4¿œÕvm·Ìw˜rœA;óýAXL!=ÛÆaéZé Ý?ÚOzm}=ÞÈÄ ­¯ç´û§KՔ㤈BH #˜CzžÂbéáõAœa¸¾&'ED†ÎüÑ}AX„ôb.•ž0ýÃðP|­— Î0\_Ž“""!=‚ŽkÒ³÷õ+ø×Òl™/êôÄЏN¶/Χ~cÇ®»)¤çÕlAœ!¤'BH #ÄŠô4×ÝËÇEIQp :‹i®»7v¤ç•¬n¥¥ÜÏ@IbÈ­i4µ[_?ÁNb?Û6œ •É óhT,ïüÜê'ØI”Ä×/×7Ît“—ë£n]÷ž«ï‹ž8cg¾W ‹9¤ç‰°4×Ý#*âÆ)[ \4×ÝsÂ{ k¥'tÿ0<_îÙ­´”ù$IH6©¥™mëëÇ¥gý ÚX‘žeŸ[ýx;)ƒùú%ãúƒK{Põt*õº÷\}_ŒÒc¾qRD!¤GÐbAz>-Ïh˜DEÜØ%dEÜÒ±!=/ev+-¥>»4F^h!áôv¬ ¬¯¯“ØÇǶàòÎI.ê© K2ƒ],)Ï€µ=w²‚$Ih> §x.—/e°ù+’ªQ^“ ëÓ˜ÖGáì‡Ò8´!ƒ­º83IF’úœáaueà3öÏqó£~ ®ÖÈÈrñâz@z6dÒ0ÇË9~+w=›NÃty¹êÖfÒ4ÏÀ¾ ¯“ï”P}6îŸÎÁ—2aCïÝë`WÆê³qÓŸ<œšéêös,¤'ÂØ™ïÂbéy<$õoüÚ <¢"n|pLEÜ7~ö>èZé Ý? Å+öË4 IDAT ÝJK‰—Á¥%‰\Ÿ¤påciÜAý¸ ô¼˜ÁÁE~.ôX¸ùÑì®îAÙ%\ý¼|´6–'~é©IçÍËUrLãú4ʆʨ’“ÓaY"{¬L,NçÀB/§8­Ü5¦_W¦Pðs•„'ðåº öÏv‘kQ8i˜ÕsRÙù´NÊ >/ñsE’ʵ¦Ò¼!ƒÆœéY“AÓ\7½- çÜÌÎÚTÖ_oÅ•ëeëÚ gºÉ÷h<59•oJ“x0_FJtvû96JùÆI„AG0»ô|¶¨WÛ $*âÆ†Š¸Õ'™_zÖ§w+-Å^»uj—§³ó1äD+jÒ©Z'1ß˶µél¿ÛŠˆŸÝëÇ´Vù¹Àmc΢tX–À0·ŠštêÇÛIÍ÷òñ²$nÌ´qÇy*ƒnNaï Nr3]üïª4ên´üÓöÛ:XêeˆÃFqe:ûg¹ÈuëT×¶Õ?­“˜í`Dš„ûì¾ 7ukÒišã&ߣS»,°­i¶‹“2]lZ•ÆÆ_©dÿ*‰}ÁïºgŒŽ7ÉÙíçXHO„AG0»ôlm÷¿©‹Š¸ñ…±"®ÓüÒ³.­[i)ò0Ø­Q», Ö¤0ÿ4…ìËÙþ”ž5©¼sµŠ$ÉøÜ ~·‚ß-£éVƤÂÒ ôT§qh‰Ÿù5J¦¸šïæµQ:é§øxã6+Y¿H¤a]*¯_¢Òçê$šç°,˼Æ/LcÿL}2¼½2°­~¬†&ÉœõKžS‹ë§¥guM³]ôËtòà1M³]ôïédÓÊT–Ÿ®pÊ_Rh ~ÖþéNòz8ºý 鉃Ò#èf—žöƒ’¨ˆ_S×ìÒóbj·Ò&=KËÍ =œå´pëõ6ò=l[ʧ¶’v®ŸúÃÇ­N¡nFß®I…@zªRau2ÏõU¹ô ½ÿ+½%ÎHÖ¸ãd•KžH¡õÅ|ð{ Égûù6ØÖÁC6 ¥²†“¾™N6­l««‘ÔÏËç«R¨9O!é2R‚£ÛϱQzÌ7NŠˆ „ô:BLI *¸ :—˜’ž5)ÝJËBƒÝ6j«Û­_–ÀÈL )ÏͶ•)°&™í;8¿‡Œ$I¤ö³³° 9°om"“†((v¥)4LÒI”TFÏN†5ɬ9WFêåbëŠ`Û«“Ù2ÒÎO’d$I&ïT'«ÊÛöOw¤gy`¹~ŒFÊ/_¯äðöU*ξ.Þ}ÖN^Ž“º)4Í JNðÃòêdÞ¾M£ŸCBK¶qßurÓÝ~Ž…ôÄA:óÖ»‚°˜Bz>},$†AéÕèWptaEÜ0÷A—JO˜þaÈou² “h˜ã¥t|"ÍÁåú±©¹®nÏÃp}M8NŠˆ „ô:‚9¤gtH ƒ’¨ˆßqÃܦžUI‚N¢ážÉ:•EIªò3噼á¾nÏCHO„AGˆ)éqã»"®Ù¥ge¢ ³¨M`Ýo­ä8dt»ÂП;y§²ûóÒaìÌw a‰)éqã»"nÔ¤'tÿ0ä·"Ag¥Ç|㤈BH #Ä”ôˆŠ¸ñ]×ìÒSëÄBzâ Œù ,æžQ!q@qÃÜ]+=¡û‡!¿å¾N£e®’„ë,'ß.k¿Í˺óe$IeÌŒÎû<–û ÜÉ0·…ŠÒNn·»è‚üÒc¾qRD!¤GÐbJzDEÜø®ˆkvéYæí4Zæè ’%$·•eí¶-rò{Ÿ„$+Œ™ÞyŸÇ2/”¥¡¤“Ûí.º !=qBzÁÒóhHDEÜPEÜ0÷)¤g©§Óh™­3Ø­ružÂˆQî¶õ O[Éëeå,¯Â˜é¨qóö¬ òJH’„¿—•Ó‚íÔxøðng$HH™Snçƒr³úÊœþ'ƒmnþÿ@ûN ÔòÑ-T,ÝîîûT²ÏÔ{¶‚_‘Hε2sЛ֣¿GØÜܼ÷'+ƒ<V¯ÊM7iœšncã¢ÀöOh\”!#K'ÛX27°¾i²Æ€|Âk,yh^•{F»9Xéb\¿vùvÎuÒaìÌÂSÒ#*âÆwEܨIOèþaȯÆÝi´ÌÒìµ0çf•¬³íì­qC‹×~¡ð“ëtFøƼà¦y†Æ §Êã;Ù½ÀÉ¢ ’êà»7MS4Nv«Œ~ÒÉÎì<”'3àœl¹UÅw²Î—Õn¨t2!Gæ—#]´–ÚæV©( ßîîûTIæ78ؾÀIÕ¥ Ž ï.2~‡pm4>c#ß­òÔXß̶ó`®„ä·²±ÂÍÁù:ºn¾ßÉîB'e*¸úh|Té¦i²ÞªÌ9×:ØYæbýÕ ®l­•nh—g]£ô˜oœAét„˜’Q7¾+âš]zª]FËÌ€ô,ŸªñÓ «J\Pnç¶t•矷s_aÌ4­N¶Íqr ÚÅÁ2'k®Tp ÒÙ[íbÓå Øù.Øæ¿'ëÌå¤q†Æ)n•Å…Ïê³P]ä‚a.•Š¢ðíî¾WEOµñÞ¢`¾%v®ò*Üÿ¬ñ;„kcãE2ÙÙÙÜwÏ_-x­l,w±ý6ÿ ÝUÁvê\àR™3ÏEÓó6òÝjKÛšžµqRº•MÆü;ë:鉃0tæ-ÂSÒc‚J²ñ‚)+âFKzÂôC~UÎN£e†Á•U…f÷—ùÍ(ûÆ[é›oãã"= =SPá ò •Ó2ôU¹rŒc ÎÞ*k~"sòÿØi>ºýÅvÆõ’¹òo¶Ýªþc=•N(JCa¸vì¾GÅÓOãëÊ`{‹ì<ÞSæªÇÆÏ “Ûò!2§Üì %¸ïþ‰Vò’­l,sòΕ?|.MW7ÕIÓsVú¥[ùGyฦç¬ôϰ²©ü¨ü;é:®¯ ÇIĵ#þ}øâÚ|{ÔªóÒúa»Î¼|Än¾OÛIÏßB"*âþ€*↹º@z"' ùU::–éAé)v°ý6…¬³5Ö^¢pÚ vZJ´€ôL lóåZy­ pÜžjì­tðÖ/e2‡é4Ûün¢¿ÝªÓXé îZ…Ìsm<×_æ×Øi­t@‘Æ0—BÅÂðíî¾GAO³ò^y0ß"«}2wO0~‡pm¼:L&ë¿uö÷ÝóW_¢…¥>½Y!íLúÃm-²S7QçÛÅšž=,9m†åvùwÖuˆDz¢9NŠˆ êjGì:|š?¸-êVy9ðþ­G:óŠÛ»ó>ý^Òí*²ñ„+âv£ôD:Nò[bï4Z^°¤§ÈÎÁ™VÎòÊ IR˜8ÕÅAé™lç“›|ý¬Ô•Ø90Gã‘>j?»Ûio¡·KáÉ'töÎÕx²ÌàßéXb§é9 ý2 ~•U…ÁÏ-´1Ì©0kVøvwß(ªù‹4þ5O§úGš…M¥Æï®†±r\ ãÆè왩ñpž„ä·°±ÄNË +çzî|@g×|¥W($äZù°ÂNÓ3úgXØTø Ãr»ü;ë:D"=Ñ'EDÔŽøçá Ôðî-°åvà¸4¼{KûÎüf7ß§G¤ç“¿…DTÄo ×7Ì}пôD4Nò[¬wmÒS¨C…Æô| )Ë–rŠl\ã—3YçÐ|#ûËX‰Œ• ±p¶Sâ‚?k\¤óÏ?ªœâ‘d™çXy¯8ø¥#S$|?µQ¿øÈºIe]¡ø¹Ðíî¼KÁ›gáÁ¡2NI"1[eÆ$Ö£¾Ã‰r{û•~v -Qá¾ëUrS-¼]8vûH ç§ÞúJí£²pj`}Ó$ ýÓ-l*=Îr»üKçvÎu0JùÆIDÝŠÓ_ =¿1êVyùæ­Ú:sÝŠó»ó>5JÏ#!qã£ô„¾:ý—žÇIC~‹´»ïRð°²÷?h£á9+¥£l4—ëQHíeᣲè¿öD"=Ñ'EDu+FŒ<|¶¿ô;Øò'à¸|¶áÚv“á·vç}ú½¤§ª¾·þú “žcåÑÇ=´´« {ž®2~–ñ¸ÏïPqôv°cipÝR/oÞfã¬t‹$¡ùT®ø­“Ï«ƒÛ»¸ÎÝþ³$T·ÊU·¸Ù»,D~•nîM–œVV•Ç^õÛVÄí^é‰hœ4äWaûA°ûNÿ {ÿƒ6žP虨P9ÃÆ¡…V¦ ”È»ÔÊ~|¿ö¥Ç|㤈bkíoN9|¶¬ú ­Fÿá*0'›W^ÕÖ™7¯žÓ÷é÷’žn¨ú:Ü­pë{J=|5ÇŲ¬dZþø¤‡Öe^(s¥Çx\@zìì¨ñÂRo\©`óYxüa[æ¹yûo:—øeN»ÁÍ`åÝëü 7>áfO©‡o Ýl¼ÓF–¢p÷³žãæÖøŒF/—ÊÌðÑÇßÇÌÕoOX·¥'ÒqrËü#ó>”Z Ü÷ì¾CÆ?@eïÒN©•uÃerìº]fè¹*ïDÿ»µ§¹ÔÒvm·ØM9NŠˆ >]½þAíUÛ_¤úM7Eýá*0ÿþß›Û?Hvv÷}ú½¤§+â†b¸[á®gÚ¯sóæ¯'Ûù¢Æ¥ÎÓÆÏ4÷ù_T½uvT{88ÏÎÙºÂ}ϸ û|ý¨•¡Cu>ªô@E@znŸØ®%.M“8ý7‡ŽÉÍÍ)d_äà¥Ë’Ît°§ÆSÕoOX·¥'ÒqrGeN[~»(PfÄ »(m×vû➦'EDíºÝ¶þÚ¨?`æcÛú#?ÙÖ­1²»ïQ£ôü5$]U7ÃÝ wM2®kœh#Ëke}¹Jíé™aܧMzªÜìyØ‚·§FÝ’0ŸUîHÏ„#ëv=ec *ó»']ÇÝÿ–d…{Ÿq³ÿY¹n K‹ƒùÅHõÛVÄ stE”HÆÉ½/Ÿß–ßæYß*Pª bœÆ"…Ííî½½/ŸoÊqRD„±yÕðœöƒDû7ÖÛØr ïþzვW¤u÷=ú½¤§+â†b¸[æ®IÆuÍÓlô³«,-qA‰Îy²q.Ny;*g'éì¬ óYeöcæôH’ÄÀ_ê|Yyìþß±Ò33X%w‘ƒ‘i2—>ì¤5†ªßž°"n7KO$ãdëæ?ðIq¢A|¾*9P¤@‰ –h-Q8P¤ðUlžOŠ“L;NŠè@Ô­1ÿðÅútÝ5QÐ Ìç뮉úÛFéy8$]U7ÃÝ2wM4®ko%Ëka}©ŠuγÉÜ3ÚÎÇ3Aì¼þ;{žÆŽ%N¾yPÅi£n‘±Öb;…7k¼Sì„Rëü2¿}àH;;KCåå`ÝynÒmv›„"IØOÕùº2vªßž°"n˜û +¤G’"'¼s5-4þ*%ˆ>œk£ùŸ¿5í8)¢±eÙUý>X>â@Ûk™oý>¼Uðç›·®7TÖļï%=X7ÃÝ2wUyöÍKe?²ñÅiœ§ªå¶ßçóÛy6v,vÐ2ÃÆéV™»Ç÷ùæaŸKem±Jt®óÉÜ>.‚¼Š4®ñÉÜ6ÊÎs|2ÊBO‡Ê¢‚Ø©~{ЏQžHÇÉÿûÃ/>‚ØçÓÒšß½ÖÔ㤈Æˇk3ÕÚxï–¨?tѣ韷P×îáQW{Õè¨Ý›ßGz:±"n(†»en}Lgϯ¦kÔ^¯’©Êüq´Nk°ªíyšÌ˜ÉÆã>ÿ“‚#ÏÊŽEvX¬³á"Õ­ðȽuÓu^¿×¹v‰Wk4.±C±ž§OœÓ·¨$%Yx³}EÜÛ$.¼Wçß1Rýö„q£ =’ù8ÙZw ßn8‡KzÞêÄ[æÛÙ±¤õ¯ü<&ÆIŒÀ #6¾x[W_Móÿ ñù!Òü·°eõ‘W/ÿoùˆw>]½­{ó{IO'VÄ ÅpÝ8Ç&5[áoÓh>¼ÏÂÃÒc<®Mz*‚ëÊ56\§24IB–$ì>™+®¶±£,¸½Øž±'Èi‘öÿíÝyxTå¡ð3gö-“ɾ±$1`¨@­×º`µ\¼¸Ô«Í Ëí²DÄîj•öö©E…ºQvƒ²B $bŠPiP˜É‚¡í¥(ËÐY ïýc&a$!™„LfxÏóþ139gÎÌùr¾7³œ èvbÆkpö²Û>¾_ãH5ŽåúÇÙo¯zF\•'™zœ$/Ô¥%Ûm’óÊ_èÙÌu’æé8¸m’çÄáôõ˵òÒó»N#›À\büåì·W=#nãàZ–Aàq’˜ÇIò’½Dšàù¾u]Ù$œÿr–ÏsísnßLÙ_.öb©ÉQ’z‡ÏǤgé9ô»N#›À\büåì·W=#nãàZ—AàqòzÎ@=NR/ØK¤ vÛ¥_hG‰„SŸMjžd4'öL•åÒ&59¶Nœìë±(Ý/=µ—>ìz½œ7Ï~{õ3â|^zÇÉë1ù8I½tù/tÛ×4Ïî›áóÇô]Îî›!ûºeÛKµé/yé™ÛiŽnJjŸyFÜÀŠìŒ¸ã»ýUzÇÉë%þpœ¤>P[”–| XªºlGã›ÊÉp~1­Õ¾ŒLÏÓZý$œ_LÇ7•WüÃ^"íhïMw·ôœÞù`ûÄÈ3âN®8#îÎLé'5þvœ¤>âþ¶ÂüËÿš±Û$Ô”¦¡¾r N~6 UÓÑ|`&.:Òf ä¢#-ö™h¨šŽ{¦âï;¦ ¦4íÊ_b×þïëñÖ‘î–žÖºçðuV¸¬øðŒ¸þ™Nψ›ÑåðEé'ý=pœ¤>æ>1WNƒ€ñçKM›´| ÿÕâ¹½W›ðš¾LÇÁµòÿÈÍFjVkѼÿ©YzÚð8 ñƒã$]#µEiÉ®¾—ZëóÈô"©µv›4ß~‰=·‡^¸jš¾œ%{ŇñÿΉFóþÙÝÚÿžãÅWc–ÇÉ@‰ÿ'©T—¦Æ;J¤¹îÿI³Ãn“êí6éŒï*ã‘3›tü€MÚã(‘2ìÅÒÓu¶‰£}=vzÂóñtgÒáÐZ÷,N}4G7%ɾÕÅøOj3 8º) ÎOéö~(¥Ç“~¿?NQ€—žç¦Ë ´ÒCDDÔm,=LOÂÒCDD~‹¥‡éIXzˆˆÈo±ô0= Kù-yéù-Ãt–""ò[,=LOÂÒCDD~‹¥‡éIXzˆˆÈoÉJÏÁçºÓ?€¿oÎóôøaªW*Q›a@}þP8?y¨Gû¥‡ˆˆüVOKOóþtÊ ñùÄÍô]eZѼ?¥‡ˆˆ[OJOóþtÔ¬Öø|’fú>ukŒÝ*>,=DDä·ä¥çÙ.s8'ª}’¬^©À¿6[ÑôñPà³aŒ¥uOš?‰ÇwÅa¨^©hß§_g…]u °ô‘ßênéqî|PVxw ö$1~ž³±²âãÜù K¦î–žoòâÚ'Æã…!Àî˜ÉñÂKŸÑ:ZÈÒCDDI^zžé4ukôícóGƒ]‰L€¤ù£Áíû¶6Cßå8`é!""¿ÕÝÒãù¡×ÖOã¿&0’ÖOã=ÞºT²ôQ`ò¦ôàÓx&À"Û¿,=DDˆ¼*=Ÿ e,,=DDðä¥ç7F6)îÂX䥧óqÀÒCDD~Ë«Òóñà~MK®#cžŠF£ÇõÎ?ë:ÌŠ#•WYÇÖpŒ Ò"¯¸ï·Íùg="RÂðÝGòëÏ-7#1!ŽŠþ}®¼ KM S‚uå±8·ÜŒ¤ -^Y…Ùax!I!ÔØïÏ1K<¯JÏö˜~MËz F™u°ÇàØ|ÂC ()Œó5B“,8RƒúߨacÅ·®eZ7Yq¿YƒUù1@QÆ™5È+Œs‘QI* ÃŒ8 ~y¯)³"pú=#âLø[i43T¿;'ÝëºmÁƒë bp~… f6ºns¾¦Cè¤hæ»Bp½Œ«ô˜á؃ÆUf$é`+rÝÖ¸Ò„ãLØ[]?VbÈÃpÖýXO.ÐÁfì÷瘥‡ˆˆž¼ôüªÓÈ&ÅŠè~MKfF™µ°EÛ"q›ˆ!†¢þ­«ôl‹BÕ$%A`³«Y„Õ¬€V§ÆÂ5QÀwéÙ‹ƒñC«Yï˜qK’ŸÌÓ!ft0þ:GÁ„¢¡" Ÿ>¬Ä°IahlÛ†¢ßêºÍùªZA;ÔaAƒ%™®ëϽë.=eÑh\iBrœ_¸—i\iˆAFìÝ…âï‹ý‹´¸ïëüR## ýþËKOç〥‡ˆˆü–W¥çè~M{éÙâºÜ¼6wUxjš!IA8R…Ã?W#z¬ζåÊ"àX†SÛ¢€B««ôlŠÊÂñöp%yLƒþ3§³‚p{¸¿¼I‰‡_Ž@뇑°?¡Bø]Vœr¯ëBVÆ4X—…óËŒgÄÞ×mÎWµK¶à¥(¼WDؽV/¹%$Æ›à(ºTrÜË\º‰”üãPœuß×É?ifè÷瘥‡ˆˆžW¥§<²_Ó²Îì*=…m×E`ÿ Ô‚á3Ž”F¢%3c-*üê¥Pߊ-ÕI2£fk$°9ãLj¬Èr-[5Q … Àعá¸h ÅóÑ­9®õ7½oBŠA…gþ†“Bñþ}"¬ß·àŸe‘îÒcÀ^›ëg¯h1Ò‚ï¶E¢y­wê•øÍâœ{Ç]z¶F¢q…#]ZÆórÃbâÍ,|;'×[ñûa !†~ŽYzˆˆ(àÉKÏ/;lRÜѯiYkÂ(³¶Í×…`nœ!ÑŒ#[#€má¨ÿ£÷E* ¢’õX»&Üõ³¶P¼1F„¨× ;/ oè*(ñÒÊp`[8¶U@jB]‰{ÝeᨫÇa ‚‰·QšãºíüRƒ«ô».;hq³ß•¹¶áó4%ŒÃMØ÷–‰ñF8J"иÜ]rÜËÈ.—…ãó9Z$hÃ5xn¶ 1†~Žå¥§óqÀÒCDD~Ë«ÒSÎôQVY½(ÍîËÎWµˆJ0õûv°ôQÀóªô”†1}”†7u®CAf.n²âÑ $¦÷ûv°ôQÀóªôl eú*¶Tüñtz·üȈª‚þß–"" xòÒó‹N#›KB˜‹¼ôt>XzˆˆÈoyUzlV&ÀÂÒCDDOVzjÞid“bqp¯Ò²Z‘‚ÓFœ*ò¼Í‚Šû%,»ÊzrgV!/»g÷}ánQ£<ÿÚ¬ÅÁ@φ Œj”æöjz³‘íß.ÆKù-¯JO‘¥WiY¥CŠB€`V£$Çã¶|#ž (D,Xz•õä¸'û¬žÝ÷…5zWéÉ»6ëG‘çÞÔb¨I‰1A ¤¾ÔëçëZm§gXzˆˆ(àyUz¶õ*-+ueVbR¢iž¹ýú†×ÔHªÆ –º®;ö²bPboÒ`ãê  À„…É®óñhCUÈûÀŒÏŸT#Å"@X‡ª±ì]÷ýš±k†ÉF¢IÄO$ n²ªQžëº­Ãåz²þ+bÆ_'ˆ2Á€Šû' /mËWO«‘$@mQb¶ï2ÀIDATæL-nÑ`W~uKkqs’ë&«\ÿ¥Ý¢Ä3/™qáòí\çý>aé!"¢€çUé)4÷*-+´eQaÕ,%ߥÇéB3PhÂ'ˆ¸cª’UÄ‚÷̸¡Ãx³ˆY¿5âÛuFäŒa¦ÅÁ3­Ç8³y™f4/Ó"ŨÄÿhÄ·‘?^DøÝü»Ð çŸÔˆÑ+ñâ|#Ž­0`Þ å9]/×Ýõ_ñør Hñì›fœKƒ³ [Ö»n;÷¦If%^yÕ„+õx!A€`UcW^×µq±7(¸gŠÇrLØ>I„iˆu—=½Ù',=DDðä¥çgF6)n6õ*-Ë]¥§x‰wG¨Pšerõ˜£Ä_þ¢Çd«ˆïšP?G„5E‡o7¹–k]«Ãý&%V½o²tgR"/Ó„Ö<#ެ2¢i³ rŒØö¸SЧ7QqCÒã¬û¾^U#Ú¤ByvWËuwýW>¶/PcPœ_囀|æF+ðÈïhÝl® ™pi[Nþ¯ –P5våvýXÿ¢A’Y[–ë¶Æ·4¸1F½yòíìÍ>‘—žÎÇKù-¯JÏ&c¯Ò²LƒQAJ”®3`å&Î3àì"5†'ip(Sç*=K ¨zÜõÖM°I«;Zˆ…KŒÀz÷d¿ÎäPð˜·Å‰¸y¸§(`©ÃéMløž·ýÌ€‹÷=ÚªByVWËuwý—?6*îum³N#@¯ ô·êð]Åc=Ë€÷ÏŸ]Äp5vå»|¬o«‘£Æ¹®åßVcD¬{s/ÛÎ^ì–"" xv›t¦m»Xýt÷JO¡WiYê.=ë ¨Ÿ#bð]Z”?,â¶éz´di]¥çÏý-œmËæëáx]‡S @¦ãL"òÖºÖœ Æ'k\?wòy!#µ8]`@åX†<¤Ã9÷:^Q!ƤDùú®—ëîúe-S‹ÉÁ Ì™§Ç?W»òõ<”È_cÀÎq þ/κþäÿ*ªÂ®ì®kã[m%Çu›ì²ÇvöfŸt§ô´Öx”žb©É×c—ˆˆ¨G6éxÛDÖlŸ Ô>Ýad“âF}¯ÒòžÚUz2õ¸°\;- Œ ñú=°Þ]zëѲL±A"~õ¼Ç3tØò˜ˆ5jòôÀ: ÆE¬X¡Ç×3E'«áÈÒ£i•&@™¬Áñ zœyY…X½ˆyót8¾T‹—n ”(Ïìz¹î®ßóqúƒaa*ìÎö¸>K‹9!Æ?«Ã™WUˆ7‰X¸@‡“˵ø}¢ÁªÂ®¬®kã›*ŒˆUaoŽk²ËÛÙ›}"/=¦ý³/•ž©Þ×c—ˆˆ¨Gì6é˶‰¬a߬ º^¥½ô¬ÓyZ,M V¡6Wdj0ÙªÀ‚Å®Ÿ­Ÿ«Â}®oME Sbí÷z²µxc¤¢NÄú·5˜;Bµ( 6^‰?ÿB…»Œîÿ¹òµØ=M‰d£…Aé§*<¥Dù:.ft±\w×ßö¸òµ(º]˜ñœ½ìñ~|¿Æ‘jËÕáóéJ$ëhCE<7M‰„(>Ïêú±6¾¡ÂˆöfwpÙc;³W{¿OºSzöÍò,=»}=v‰ˆˆzÄQ"-m›ÈN~6½{¥'_Ëx‘†·ÕÈž§A³û²ó"¢†ªp0Ç÷ÛÖÒsbÏôöÒã(‘2|=v‰ˆˆzÄQ"Ím›Èêwü´{¥'OÃx‘†—E Q°Lƒ‹kÕxg¤€ÄGÔ8?¶­;¥ç›Ê)dN}Ê×c—ˆˆ¨GêlG·Mdµ¥ÑZ3¨½2µ—>÷Ñ”­rÕLO“­FEªñz:½·ŒU¢jï·«9[Õ¾oëÖè;Üÿ¨ƒê­ií¥§º45Þ×c—ˆˆ¨GoŸ¦³ÛÒŽ´Mfν3;œðŽ mŸ 9*&@rü±}ßÖoÔáþ?󷙞_W?æëqKDDäÏ·¸ŽlŸÔÅóóTV|þµF¦LÈbü)­Y"š2EükBVx¾^Úaái¨’½ÊûÖÇ¢}=f‰ˆˆ¼æ(‘2Ú&µÃ“;œüšþ6?ÿŸ&&0R³Zƒæ}¿Êw¸b2¿µEDD£¶(-Ù^,5µ}}ÏÏW|ÿÏáìˆN ω=Ódga昉ˆ( Ø‹S¶ÿEo“ÐôÕ, föiuÌ©ʻqtãPÙ·ºÿIm†G7…óãû;ÜǨ™Æ/gÁáñ¶–Öö’¯Ç(QŸp}“Kªn›äêÊ&¡ù@z§“"¸i>ŽÚ²K_Q?P,UÞ>Mçë1JDDÔgj‹Ò’í6ÉÉâsý¦ù@:n›äùáe'ßÖ""¢€d/‘&x~¾§®lÎ9¨y’ ðœÛ7Cö ½Xjr”¤Þáë1IDDtÍØK¤ vÛ¥âã(‘pê³i>Ÿ”™k—{¦Ê¿šn“š['NöõX$""ºæ./>m_g?»o†Ï'h¦ïrvß Ù×ÒÛÞÒâ+P,U]6!â›ÊÉp~ñZ«ÓÆßÒZçOà›Ê+Êì%Òn~†‡ˆˆ®KîouÍ¿üU»MBMiê+§àägSÑP5Ífâ¢Ã÷“:s)éh±ÏDCÕtœØ3ß15¥iW–×þïëñFDDäsîæt0Y2þœb©Éa“–óÕ""¢ËÔ¥%»þIij­Ï'l¦I­µÛ¤ù,;DDDÝP]šï(‘æºÿw×»Mª·Û¤3¾ŸÐœqؤãlÒG‰”a/–ž®³Míë±CDDDDDä•ÿÞAÅ› Is³IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-compconn1.png0000664000175000017500000025071413553660046026252 0ustar zuulzuul00000000000000‰PNG  IHDR…wËsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxìxU×ÇÏÝM¥÷&݆ˆĆнcï•E+¢+)¨X°!嵿¾Ÿb ˆ¢" HQ°!Ei¡wHÝÙ™ï&™°Ùì’ÍfC6ɹϳ;3·ßß™{æÜF$F! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€µ„€ª%ù”l !PBà>÷3Ír=y=\.mËKî”å¦ö:­ýu¬w»ŸhWäñtu­™•¶.˜¿ºfòŠDn÷·ÎÍ4¯£“´¢Ü#ׇÂ$9=ëÓ4NÎL9ÿµÅÍÂ7¿&‘Gq»Æ»ïÛåkîù=î':êECž1)óᕉ'_/8Å$sZ‘n¼p7T&l´ú ÄÜAZÁé=ºl¾êª«¼Ñšoÿ|…ûLD[úÖ‡·m³u“““=þeõ½¾';»¥^¤¶¦6Üî[ |Ýêûy¡®_IdŽ×•–Yõ…‡õ¥¦+(çš×Y÷è+¼Šþ†×#*ðN÷<ÿ|láæ=¯¢Ñu%ee}<95ucEaj‹»ÍÂ?¿¹´†¤e©MÑšéúÏË™#™W¥M‘×3Ä4)•”§­tu,@ æ:Ê8sÉr/˜oT¤¾ÕœêɉîÔߣµèué™ð­µn 7ˆÜ05¹Æ“h@oÙ@9§ÁÓœ Å¡ÞÐêMI¥ %ðÂðá…¤Ì/IÑí<)›#y´D¦h—RÚËüƒ@1‘”ú?ñâ4Û†ù nz%»3û„•]¥f#¾5dšß„¾®òa®”z ý¶ß¡¨ ¦iÞèõ˜K“Ò2’£µèuø™y—;ëØƒÉ5!CR3— KÍìt0Ó•´"G@4‘cYïbBwÈ%Å…N¯“eGöyrfê]þ…KzüñÆæ^ý5—õ Üñ÷SÑõ¤1©,Tt®È_}sÄü¾gž‰ÏÝ‘ÿ ºàÆ@Sö|Rúã?MÎõ[4²©kÏ„R´ ¶ºn¼Š®’¾n÷@V(U»)Ì5ÎÇóÕ‹\ŽØjOL¨¢¹¨¬©?|y*VûÛׯëÉ£FíV®¸¤’¼•äž”PQ9¦Nê·üá†s»_‹ãt+Ê›¿;‡ó·«Éëgï¿?B^ºÉ¤2ô *ÊO(uR“å ·N¹Ü<‚1ä —2áLõ_pÿÆq9úO•q ó"$îʼ8Ìè#,¤<I©ÒŒƒÄc[»ÝScª’;žƒ} ë=Ø™”ô¢“TÔ?#g&e¤ 6>*‹eoª%“³Ò†$¥f^‰®“;’Ò3û’©£ÿÑýÐ; G¸Ÿm’§ï›‡ÓMøÀvþfhzö©†é}_¸Ó'e¦`÷ä´Œ{ø0"mV M«V=Êœ†ô=ñ êüçRS«Ü3Ùýà6”3BSB¼k[Cd#ÏÎk1m1ò2ôÎô±½uÓ{ß×K–ŸA˜MÁãpyÿn—x ÊñÊñ‚Ö>Þã~¾Q¡¾÷IÄ?°`óžC¡"Þ‚n¨wð˜¡{1î4(íß>&¹ïHºž‰´ÎÌñä´Ý°„ô!i™ëá× cûKp68Éwpj’;ûÒ½è[W'äxÖ‚|æÀï<Íåxp¢{ôj;\MQØY¨çKQê^v>lŽšS»âŒ‡þÎÜ^nz¶öF¹WOÉLëjûe7ù‘¬aP®Æïè úú†àº ¸Lí;’÷éçH¯›C9¯9cô";ßãî¬^^ݘ fk&g¦ÃnÁž v ·N9¬Ûmj†P¶=v>lކA0øóEäy,˜5Å×ö7(÷÷¶?îÊJJËša&9óÔǨ暺ººþ%Id•jDió˜™n7Ûqøu¯y3û+²-Ãuo±¶-JŽáÖ)ø‰'æx²¾@Ùž@Û åyÿÏR;¤õ"Ôø>÷M2T.¾a£õJ˜˜1úÜGO"ýXÒWXÐ ä7˜]eî/C©(×”q§Ÿ¢åhW—òO3iÙ÷ÛpîByO¹3í‰òïS]kÕ ™=öìÓÏóÏ“î1Ïfw| ýc»UöÞ°ÃU•ñPwVOÓQq]œšö -X¿[Šæâþ½ ÷ÓNÜËÿE}‚r{QîAøŒ»:Ú f$š ûN‘cd ˜ÔÂÄhNÊLùÂŽœ_”;÷ÍÇCräFOÎ]°Æv ÷Èéð ^‡¯Hn'Þå~rŽõB„øR½Ç~8'e¤²ReÃÛ<ºw2ú}Š÷ŠWWЦã¥üUãÆ±·?õÐC¹ü²3t},ìC#ù~\«F7XaÁ/õzf&^j£…ÃçÝÓxq6‹óîF°ý@oÁ–Ý›àÖ$Æ©^|ɺÜvKr?ÕÂÔó_õætQ¿—Ýi¬9² jæÞóšjRÒ¤I=+šŽh‡«¾£Ñ¯8nã—riF¤ŽšC;wâ˜Ôþî*WOCãx˜~A \×MA×–ígHzÖ5d˜¯’×| ëMtÖ}ÿr:ÞPºáÆ}z º î÷kÀÝMÐ’\‹ÆUm¾nÇìnr|»÷q}Ÿƒ{éÿp?ÜjßÅš¾ÜiÈã¥ÉiY7ÁÏ쿌©€K¿.”™ÀÖ±-Áì°ÁHë” Þ¬»aõ| ïþv•½¿&g¤½ˆ8^ćÉû(Š>)«ìTm4¸_Ám¨¡<q,­Å]E íÊÛž¬Ñp8½(Aô›JI Ñ_ÉaTPj‚¯`Áy³Ô”J}À熢ãù ÃSq3?Œ—JsÝSôŽ“U±xŽE£³c$î¨t:¦ê UäBŸßïxémóèÆ|Ä5GsÆuCù> ¯IÍ`¿͵•,J¾Æ®bÁ%ÎÙøv»!á4øk¥­3- /•ÒD+w2Û+r½ãëÎq€Á'xa9 éëFz~ $õž¯`Á~P¶÷­Æ˜Ìôµ[¯*î ^pCŽnµáÈç`äs³Ó‹²”5p;NSÚ- Öè¦9œ™:7ð˜ßÐS2Rߌ0÷NS,N–Înü eÝš-7ésÏõõÏç³~_1þÛBó1Ý«üÝ}¯«R§”Û %«L§Ó‘æ{?p·÷sZðƒû¢¼9—ò¾ËÛà«:Žm9]¤q;¢Í¦lÖÄ”÷À¦î/‡Ã.ù8Ã7ÅMúœÓùùGžßÅ}û¾ô/€Pì²ýð9xœ†2äÅ´h8‡íù7ìøìceó7x¿Føø8¹÷O© cÅišù¨¹ û^g‚{tÀçßÏk_ŠpQãUPw3à G™¯»¤N}Îçx t±í"qä± hD¿Ä 樊ï$ñ^0±Ê¡næ1•NC™„AgÉÏT­ð‚m†|³Æï$¯^tíLjÚcøBBÐ úYûáe…çR}ð‚{øÿ,` Êwýí!DñŒÆ(~[gËuåÝ©X lª¦~nòµCi¯øÙ[—èø‰O¼Ê<:{¤íPŒfè¾xc!„@1Ç×f.ý{5º3žCÙ æ*[+å›6ÜNÌLùÌ×Î>×5uZß0}ïåÑ£wÚö¾G‡Ó9¯QYçÛöHë5>÷’÷FÛÎ>z ³ØÎT–Û>Ð1Ü:å¸t¯ç ÜÜýöíËîÑ¥ª|;öŽQ‹àV„ën<«Æ¶·âbû õ8%+í'ÜcÐ,˜‰÷3%Äp¿¿Œç,Ô¥òÖ@ë¾/ɈA„O|´@öƒ>鼫œmJœ‰Ön=‘óŽ:þÎÒ½7JãÄI¨Œ‘®Á+±bñ¼¯ñ°¢™ê–É™)ûÆUrn >è}ùN÷Ø®Ük…¿$Åj!g¬±¡Çl'*ä79^ø‘5°yk~¾¹¹¡ÁÕÔ3˜öÉêÇpÌ*¨%ð Ȫ×Íôc]7Sñé4~æÒå—BÀhÉÍY^}ïY€sHѵ"€³ee=^ªeœYˆVå;¬r¼.…cí®/çøº_ÊG6%ªýÎjxMï]Èî*vñý7ºñ^ÖÝ}m'»³{†q¤¿[LœöÝK))¨óPŒÙe{Š}âËÔ6»°hÙs*ŽÆMLKã¦å j:(kt••ä]Õ0´¦Q«7P–wa.\Ÿm]í?ÊÑ×ï‚ÝÅÜ/ok<7.q÷®ÂKYÒ¨qìûå2ãgnZј%ã0õó~QÓ=“ëzr3&O>×Õï¾~ÄÅ×_¨çšÄ¦ìÙUx1êhRß> PÊñEúþ²óÈõ”–‰®U:å®Ç²YèÅóŽÁºiY—¢¥ßqV¯Ãæ³teÓôÞ®.~¬çOÌž•|dî½Qºø?TÆø()ڧ罇÷Þáx3'f¦½í}®:¶|Î\»µêô¯®ÿzÿTÓ´É/?–òUe?Vì8kâá\Œ¨M=)[#32<£Á ®„¼ðL<‡¤'Ä©'ƒû®¼ ÷½s÷A{g¿ËÿÏñõo+n S Âà³Jè9Ú[Amét35õ_Ya knxOý†¤e=½à2¯óäév¼?üùo[¼ÀŠÕƦ:ÈýºãËl'º\¼v¸`GÃk\‹—ùûþ?Ony#XhÖ;Ð/Î?Ms›èjÐ^S4b#‚ ¾ƒ²Æ¸ˆVzA¸±k„Pƭ̃¹ÛakS½ƒÆ Níó\aÅ¿½{Š.ÏD¸M ¥Ë«JuJf§’t»©Ÿsà^ÀuY¬œ°~ .vy*säòB˜ÄaÞS|ñ9îIKÚuø<Ÿ‘¾¿8ý¦xÌ„®Ó@¶K~dl_Ô]{0ú”ØV ¾Ažö˜ÒÊ‚ûÁõ™|$‡VÚ î½aÅSò*c“Œ dåÇëyÌŒo<ö9mjïJ¬4íz@]!j°×kL‡ðôgRzÖu¶¿h?:£=ƒ’¿ÚKàÑGÉt»#›ÓkZ$ë…á;÷o{©è^¼l ñR‰ÍË7аbêa„ hÀ¹OSÉÌ~8þÇ?‰ðÊonãxÐ ðÅÃnh¡þÆ«b_Û†Ç$§fb~šù:'zŠ,U?kB i½Kñê>wêþEœ\Ûi»q˜y˜ÛÌŽ'Ü#ž”ÊŒgิxú7Ô8Q©¹3Óg…êßöw /:Ü 3-€kaû÷?–|ù6gɬ9uÙb»;\ôš×CCúFØY_éXõ†÷×l>†_§h·–(©Ü¨#K£sà´ÊºˆKYŸ¡_±&Bë«`v[¡ÇÃ÷Ø¥È#T‘eM¤ï/ߨ‘6k#ÜÂڈ砥€Àguï}ÀGîöÀX©é¨ö+îzäñãÑeôç¾¹'áAXã;•¶*÷§Ã&TÆxe5GGë•èc¼Ïä-¹žÜW|°‰ß »°úÿЧ«zïc <6Þûq/`NtÑ\DwýԫܹŠâí¯ã„`‡>¢ø+4€^lÆkzþ‡‡0ÁTE*~¹\54=ë–Þ#`…Éq–Á½”o5G¥)3h9Q&K»/¦r& ò€6ÓIï×H]ciâU¬ pÿþ/¼8ºDÖWdVÕ"à…7ýòOùÿ¤q¨jš!…×h¡åO©ÎÁü'ggcà$êÑTkÝlÝi?£ú¿§óWú½YY<gý\^ÛߎU©SÌDYnÅ­*Þïç@yˆ´[‚+ñpÙÁôžmƒû¨Ül¨Hß_¾e8ë˜Ãæ#ýÝx1F „Mušù½®V ¬.öë0ÕÇ|Ätð³ XœŠç&—_±]©©Â½QGˆ'J£qSƤ} \-‡ñ=v—a\ÑˆŠ‚OɽëµÜMÆ©¸ïtܧÃMí(žƒí.ÂÅÁ&.é%P¼¨“Ú‹—@³$wÖñ=šg¶'ô?ÿÍ‹GO‰)S2S>qºnç>X o§~ ¾½1Ãb`ǯáÇáÒ +.¼4/ã>k?×âK,¬ÈžGð¯ã@/a*곓²ÒÞe-Àw5—ã ³¤@qÖ»X­á"Ôn ó /°LyæÍ–½2òwG]¼††@ñî–ùùºGðýiª×ýý½®BbJì|4(øŠ5/–é‰Ña¬gUiÅct ÚA-.pμ¿Y;«jJo(^îú@½Ì²ÞfŸ‹Êí ãs{ &‡‰s%b!4åÁz&îV<•Ž·àëªÞVœ!þA²„ÖÉîä<—æäay¨Ù'“Ò³1дb39#e>4ƒXãwy­8DÍúá¢fùKêå üeYyMk¢¯óG2/ÇWJ@áëY`þºzÈJ¬+q‡{Ñ=rfAÜÉ_æ÷mŒé_¸çwŽÛƒÙÆ#ÞþxAìÁêÖn|¾á¬ùŠ~@Ü¿Y²|´¯ŸM˾/ÒóüíùÃú)¶WÇaÿ£ïr¿Ô€—">ÐRÓ§#/_[ðà´ì‹ÅËÓõ8®@nµÁΚu£/°ÕƒŸñ¿xUÈ€¿üñ/SB6PÃ#´—àå>˜{tõ¼áï/ØuUêt’;åO¤ù Z“&EyÞ×ùÞóO‡§XòÂKþöÕ}mÍtP¼Ž„Ù‚ÛÒ«Âýõ/LJÆÐH6šV¢…ðšÖóŽiV—ˆí»X¢ïQïI§ó’¨%~c»ó±ª÷†o\•9)cÔЮbeaŒy2½Sy‰žŸ]ÿ{”ݸž¡­´>’”“5Ñm"ò²î"Jî*EÀT1–`q°04˜Åª½`îU¶w(7Þ(_`¦ÀhŒï†ïÍŸ¡ôlˆÁP}0äKhcwR2‡ú¦Ã_txñ¾‰—‡… nôd71#u*Ês „’ë6x| áR}ÃèÂJôÛN³ýà+ȉF¿wŸ÷P¼¬âq­Å­¦ÚaÂ9:êa¯nÎÀÚ ˜Šy:ºw¾GÃÆ°Çä½å,Ç€Óií8yîóÇé(ë¹dz–zŒVòþ‰~Úh@—ššöÄ”1©_Úù⼃ÏýpÃ’ÉÞOpþ=_н² ¶ƒ¿c‹  réûzà¼Xð³×¢c{gÊ̬h6Iô9'@=÷ØV ½tS¿evóCÐø¬ò/—/`îGàW_Xäæ.‡Y§SL‚6Ú“çí‹{ïB}Ÿ÷/,kÎSWâk¼ ²Ó S,)“¿È¯.ŸrõÚ8œq÷`µÝ3Yp ”RØ÷—†1B^L÷5Õ<ƒPÖYÐŽ´ò]uYÒbžÅÏbÃÆ®Òûz^LÞf®±¾…9·X;ºß•Ϫro”©rW#õ*ÞqPŽ÷é¹oàx1ß0 }\Žñã±pûy_ «½xïGë¶ð4õCqϾéwNåršoÑ\„Æ©ÞøÂK"7ðqÁ~hàƒŠ‹$nøx”4 žny5'>‘xü5Ù ѯhïjÜÐÎî7xñþa;<„Ùþ A±/ì£1 _¥ëñu5jHzvÿý!+:ã„ÁWÅ?äç\¤Ñ†i¼ÌÆS‚ ¾ˆVEQÀË€U¿ûàE3é ÄÛ&ƒóž¼èN{×aÌ…yþ~f£>/Q³=øè(ïø}Á?äõ[4B<0°?^ØŸûk(°ˆÏËšröåôàïDÜ#0ø‘W´¼ g;Ãþ5¬lt6†_6¢ò’ȵsv¸ž”öä´îè>z¬Æ¡n¯A†ÿÀ,‡þÜ•<óŽ×À[˜. <‚û ìnrlÖ4ÞCZ€û î=/îãëQ—b¥V òÃ’íÊ\ ]õ ü‹dÙòþ@XVÿ€ã¹¿&Iýu•bÍR¸)”µÍä~ƒxWáÞþDzpåûQaûŠuÆ|bŸC8)Ó%bÛWýÞ°cªüS™ïD–ñ;³AâPÏâ°ï›[q¿e¡žŸçsØ5Æsÿ(îc>zPŒˆN÷dg·ôä«h+ý7 ŠÎG>WÜ¥¡ïØ{x,%¬ ôÕe§ˆ/ëd~ãE59Ñ•0ÚZÂÚv,9òâTTžÂ—÷, +ž–çç‡Çy|ÿçÊnx©%häÜrz.›­áá¬V]òÀßúêNÊé‰mC‡.÷Ày0 jË‹Õ5’G] Ó±§qCsSmz6*{1+ck^wÃáuÄ5Š_ÁƒDƒq‰„}Mß¾e`¬^D­5¯ŠWGÎK>´™5¾~¢ù\„‹h®É›‘„‹Ÿñ{|œËÕù÷Èõ‚ñ¼ú\Ï>Þi¾úz} <‰u“€t‹ÔÍz•RÕ#P™ò -¿3˜`Á8 ô}ÇcQAÇÔÔ#lRT! ª‘€ÕW¢ƒ«J¡‚œn^"z ovå›.«zy‡Sìn:Ý&ô‘Oñu—s! „@¤ H·H¤‰J|B $eeµ¥|ã{Öüwt}¬Åà¯Õ;Ñ#î;bðbCØm€Ð{¬ÝNk ’¤õ‡€õ§®¥¤uœÏߨϽvÒ41{†°`”¹ šMdjsÍCšOã} ê8)žB@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@D/GôfMr&ª—/›½+&¦uŸ“Ïh±à‡Y»«75‰½¶’žu[ŸÓNZ4ûÛJ­hzß3ÏÄ÷:¡_§_ôÓÌ™õr]‘¤ôì{Ÿq^á¢ï¾Ê µÞyï›cOëß{áìoÖ…FüE/Yþ;zëFrVMîs?Ó ;„¾˜”ž¹#/ÏØTDæê¤ÔÌmCR3§sƒRMÉÖÛhÝn³v¾gLjJ;.ÔŠK~$ó,¿þÓ¾í¹yX)uåî]…»q½÷Ö+w»Ÿhj<µÉ_ ºåÅÜÈ0¾'=?¥2eÉ÷äÞí5Œ’[‹ï7ÒØï*gÑJ v>ôÑJSòõ~≆¹zît2ÍÛ°<í šr\àt©“M¥2H™{5(3¢¾µ,ƒ9zæš{ÜOt¨eÙ®Tv“Ò³®3 óC쇭kŠns¸Ô‰šÒ®Â}55nAoªT„µÄs ºu»ê'ïWÊõFeŠ¡)×{hœ Aí}36=?_9㇙zÁóŠÌØ4,/Æï”³åp¢½ ¦§`4%à¯ö0ët9FLp^i'\ÓÀ™p74+/ ìéHÏ÷yøè½ÚöËÇó––1_ƒ£¼šjŽr ‡ºþ8äc;¢ø¸I#×#O޹×7Þ!iƒ÷TÈÃVl¸:­³CŠÛ}kí¯´¼ \I´WöÀ§Âý®‘™>13}ûš–1Ä0õ1|^èñÌDeÚ69+íŒaÙÙÍõ<óÃ4ÏD~ú"ür¸ÍG‰ß˜äN[Èa™ä´'5Èsö˜ˆ0f)5Ѩ~yôèv¤U©rCóp‡iCßa(ó2äiÊ_dÇw £á¥4ì6»¾½ó°Ñ¾÷E°0CÒ³û+ÃHGü½PÒZ褘Q~Â.U¦ •ñËù ¥žÙêp€û{ÔôÂe~+å¸W™ÞžÁê–Ãq~ l½ÉBÎ?Á=¿~RVÚ0vó5¸—žÃ3Ñ}JfúÖ}EtÿäÌÔc“³³ÛPž9÷ëùà³ qôµÂ™Ð0]m€wLÉLKò‹ÏÑÕù™tüäÌ´³üÝäúàÀ{GŒ¨'¼æ hòÏ<úÐW**1oSnz¶Î%ƒnDãõº¦©›”FŸaè£9ž¬÷Ë„Wª^€¢ùk¼„×òΣx)ÎD}©oy‚Å7J •ÒÂñ?x™ž¬ëúûeú’­8Ì öé¹³ÑÀm#M Æ`ó±ã w¡ï6ê•ÊÑQøŒ~’¼”¼MÅ? ÚOøÝ½k¯>ηÉ©™W#íéðç@yGÀïWp’£ç|îë<ŠË»Ïó=Î=ð;áÆ¢Ì-¼DŸß–Ñ…ý{•ƒ†©VXe>n‚LSiãùÚ“k¼‹ ֆǗ°» V_àw¤ò:ÅŒYt|‰`1ÃÔÔmðùÆ›ô\ï ~¡B.7µ‡¡yÀ Mõ«æ Qî)`öwùÅYî’ëu ¦¸ÝWU(Œ Mͼ ó,Ü;‘νøA¥¡šèT´09=ûd¿B.Â…ì7ÔzNNϺ¾AÜêéQRÚ,@*‡Úx º-)ÃQ(Ya³‚Ôí<`³ø²øŸ»(q6þ–X6¸¯J„r)U„ûêM°åî¤_ùÞáîÛŸþü­¾Ï‡gÍ$ê "ýÎ×bj–€h.j–¿¤~ àEÕ/õ®ºêª7`ÈÓ}>³'¾({ú|Q~44-{×ôNKNË>[—sƒh4¬ \¦NÉJC#n™÷ñu˜€ÝMxyN˜”™jµ½—û^ƒÌŒú¸nð¹¢Ä?Þ‹Ô/Ô&e¦=jÛ uýÂÅr]/|vü«tÞäȘD­óK))¬±`ó.ÔÆhܯÃùP¶¸çùçc ·ì‡ü~†¯ÈKÙæuôy/„@õj òblÁ(WJ±W¢dwæ×†‡xH]»g¦d¤|²mÉ0‡Ç:c¦½à¹žý&Mšä¢u[¢>F ¬¶Pð©ÏŽøú}îü³ÍàÜ _ÆÚ>Ç Ë̓Í}žÑó&¾„ï°Ã¢!|/ד AñÀf#-ïޱ»³üÀ>QyãÆ%îÙU8Î$õ.Òbö–ÁÈ·6èsæ†1¬OBC޶¸ÔTX†RŸ¬gK3æÙú$òú 4 —ûäé?%é­T·>yñ=}ŒÒóõ}—ÃòÛa×Ï`ØÇ;(æ ÛÎ>–Ü«S¡±ƒ<ü31#µXH…‡{³²ÞÈÏ£L]÷\˧í0õò3är©rñÙ~äxðà½'FÔŠŽ€°1´ÒšW`¦ê7>‚…lbfÊgxÑ®2ɸÆ?¥bÞóµCƒ7‹¯M‡*£éÀ× e¯¨ð_ÿ|®™®ÿùÚqwZš/ 8o¿}eó¦>÷,¬hÐ~Í@Îò̶(Ú¶·¶CðåþÚþtà#^ƒ&GeœêkoÙ9oúÚM|4u4-Ûð™ÛÙ×Þÿœ·}G™þÂïvîæðw¯ìµ¦Ô·h¤sã]6lÅåVûP.“š8”c²oØñîûv‘‚V¤"ãUG°”»ÂûjïÞ¢S϶HïEßh­¦z ”ûÞõØã]|Ýs…u·ßÅ~C­ghܬ¼¢ï#XìOªg“3ÓW!Ž9xžXØo]nóKÆZì·¯àì¹ÔÔÍÐ`Lƒ4~‹¯WÃ4 °©¥Ü©¿úÚËyÍÍEÍp—Tk€^ìÐà· %i|uà ñà ~¡m0»û»™ÎÆ9¾vh¨÷x¡#qÎ ¾ö˜I°Ç‹ækŒ¯=ŸÇ5s­õ·Ó4Za4ȶ¯lÞð/¿n€Òv’iP!éV0 +ÇñOB0¶4­|ô¸+èLêdÛÙÇF‰®2ñr#””–¹[(—Æ>*§ã S÷Bè*\-Ê—h,žG#ô•íìÈkHäíȽ_³A°3ê³9„¢ì_3 ´…ûM(å6É[\.‡ uZÖXáQI2Ê4¬º…Ö¡âûÊ4YSEWìJÿ8•¦-7ß«ë|_ýc»‡R†Êø µžÑÑ5ò³ÜŽ¿JGÅÚcÒÐÌÌöÓÒr }h—gž MÝÝáÄ‹{ ]Wæ—Жõá1:ÖôÕ½úù¸YSÉOÂDž@™‡1òÑKŒB z@kñ±ÎS§Nu„”+ÓôôgLŒõw›ôhR¾¿_{©HdÈÎáõ–{&ñÅ—ˆ—©(%¦ry˳ƒ;BÛ`}õC>xÞ¡´'}hhnGûÿ°ã|°ÂxýÃØ×“Ý)Ë&g¤­4u)4 !¼Ì’–ùícÇ6µýø“ÜOµØ·=ïWh~Æà u…F¿‘ (¥ªv¿0!äOãAŠdÄy ý²ì•ëoWîÚé´ ^,'l–ó[b¡bõò÷•£ø^ÿû*„2”¦T¡ßPë‚[ÇC®Ò¼¥©„qëh8ÏL¡Q ,_~y5êM×¾]\!ÇÜÖ™ö¯µ v3Rû<ƒñl;œN×Û!G"«•€³Zc—È…@ÀL†½&Ý<ó·üBzõ€YSæ"´åÅ_µþ‹g)Dæ‹Î/î¼\½-¬Ê~Ùšf|@¯+UOWCÞ4S[‰ïfˆ0Ú܉îT4Wí¦¤<ÓÐ4ô­Ÿ ã cŸ÷V\?(q Œ½ £Ã0ÛçìIcRgÚ~ ù@_>…a p®áªÀhÃnß(¿6Å{Fnë¾^º|Tôw`,ÉSÜåã‡ï9ô‹Ðž’‘§:¾tf‹åG·ì ~ÑVù†‰ôy¨õŒ†û_æRHyœ×]UÍÇ îá{PÇ¡šX¸x‚Ûµ.>õáS™4xV½c¡òn|,Œ@@›e~5Á]7ש ›hñ[î+)Z2&ù‘&`Òêu4#+ñIúX…}ý<-’èlþZöÍGRúãG£<3GfùÚGê\÷zË Lê× /Íø*û©4êÈ›Óõ3ØäšsHi:9)ãRäõr è¸KZ“Ÿ1à¶ÿüuA£·¯­vÊ·¶ÖB¡¼Ö¾®ìÑihð•—<Æ`ß°Å3yŒ3|íóà`ŒùxuÔ•ÖoSF»ä ®eâRhi  Ðoðs‚ cÜaf][ê^V°,籊!ֳС…¼ ½ºQ>¯¥Y­nK½ñÀÎ>Ð|: Œ«Úë>nOÑýµÏaÀ{ÇEôî™æß,]y â;Ú¶7F"–5B@45‚]­ üU™üHÖPÃ0ß7©p1¾|Æ‘C-pšÚÃð†©ŸðéøxbÆè_7‰}#û/2õüéø2átŬ4 Ï1†WŸ€—î²¶ŽSÿé2 ^¦¾>Œ)€;bÎY¯ÑÒÐ 1 Ÿ»NGé8ˆêÈÛD÷C[’Ó²20¸b,Ê[¤¹êe•gêÆ &i'’r¼49cÔo•-óYGwŸ3séß;0Õóa©™h.—·9µÚ¶Aßð ºÞMˆ3—y´DÃk B½ðL‰‘ÁÒ@C‚™+tËý§Ô$÷ã¯i†Þûë%ËoµlþÌü”•¶÷Á[jFcöÆX‡sz‘×l¾AÏzÑ5%Ê6Ž”7sŒ¬K±Bç(Ô]Ÿ¡éYo{IûCSF,ƒŸZfÕ<ñÂðá…HãVø{Œ·Æ(ó=/ÅÄÊsÊ~9ºŸn e:k(y æ'ÔzæqÐ4¼íÝýö(—ús‹ãþè‹|~ÇÏH ºµgJ¿3íë ž¬°~æ3{uŸ1)G;ÜûŸà™x³´.4h?$Ä8žuß¿ƒ½LÈJ[‡ñAXmׇÃ}®– ?ñ *§5L@45\’üÁ%uú7qΘ£‘*¼›¼Æº¡/4°h^œàËÊêƒꡇrã¬éÿâeø‰ÇS¸û|Œ†l+QëÇ#ü#s´ÍqÒ*ð­ÃÊ–Ü=уÏä1 vzÕ•7n5eÞŽòÆ€Óù^ñ›a¢ûÈ4{+SÃwå5íë{ μ‡ ÓBwR"¢5z¢áý–÷vÑ=ú*,`õ4„‡§Ú9SJ§ú§ÖÎÙo>…·™ÏŒ|aþÑ(æ,¿•¹nïj'€·ÐH½Tè)Ê1 }.«}袸%”xXE)È—bPæÜ?m!(L&CÿÕt ‡}­œ×”ŒÔwç;¡‰º¦ÐT«t³h`\ !÷ŠI©ÿ %½ªú µž! <Œ’¼Ø×ý|/ð¾`Ÿ AÜŸ¸nƒçÎZ`L£· òó÷v(SÂŽ˜wøùÃ31ÕÜëÙ•ëÉ»Ã7| La­‘"í]Þ|Ýä¼f @€#ê/{²³[šE׋î‘Ö¨ÿ@$XE¾Õ“uHÿc[Ê 1PÙa#µÐÀ>…•¡eW&OÕc /¤¤l=PØêʧ•B›6i³ÅÏåç@nX³£³F¶MpÛÇþxêhn®ÞÚIZÑi=ºl¬ [h.:êÑ5§2a”7vã ·6Ѽ<Ž¢*ñò:Ž;iMm6ø®lêŸ>¯RŠ–F¸ãüã ç:Ôzæ×Åïã±Òñ¯Û@~ªbgí ´ÇÓŒ5M¾ñ”ŒÕ™¡iŽS&e¤@(#„€¥X¸’š.{Ÿ!¥®r"„@ èù]L•î® —ØE–€Œ¹ˆ,O‰M! ª™/õ½Ñ“sº2yïž"&ʈpe"ÙB@à0wÉ}}wŒÒ!Xda ù°ÖÊž‚¸D‚€‘ (q*PäúT)ý_oQŨ$¸¨Ó0èù,Gß,¦EÃ92ˆ³NWµN! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@h'€k«ßÜ™>¶·×𞃔:™Êl‡DVª’‚B nÀÖ¹{•©6 tk°K茗3F/ª›%•RÕVÕ&\¸Ý¯Ååxrî&eÞK&uPŠ 2Õfè¾Ú Lò-„€¨i.@¸h‡÷kkÓ$­Çûõ¹ö®ö/ºÝ·Ôtþ$}!P-ÂERZÆÙ@;7}G¥ÔtüÞÖ´Ï_=z§ B@ȸsìØ¦Fžqiš×ãw.>âÖ"æ!“3Ó¿ŠL ‹@Ä…‹¤ô̦AO)eþÉzø¤¬´ÙáeMB ! „@¨’S3O7ˆ^€ÿ£”FNÎHjXñ'"MÀÉ‹ óYEêrµºhrÆý+#¿Ä%„€ ,úaÖš>g^ý¦2ò‡ãþ>Îܽhö¬y}‹­¨^Ó\pWˆiª/ᇓ2S¯FWºÅ! „ÀÁ$ÁB%§e½‹ð`¼†Ï—.’ƒI_Ò² höIUŽ#@ Ý"ȆD!„€B@ÔUÖ\ÔRŽŠ Ü÷Ì3ñ¹;rÛ5j·é©‡Ê­8„ø¨iCR3çkÊœ<)3ý•šÎ‹¤/„@ý! ÂEý©ë°KšüHæ%¦aŽÜ·=÷dŽd÷®B/–{ÿyĸ\é/ºGÖ©17n·©¹Ý {@EÆ$¥eÎĒ̃í܉cRgøÇzoVVë¼Ä>º¦è6‡K¨)í*S©™`Ѹ=¼©.1IN˸=GÏš\e2¼æÄÇK¬Ž¸%N! „@4ÍE4ÕF”å‹‹LÃx‚ÄøöŽÔ‡|¾æFV?,ÎnZ”åºjÙu1bØZµX…VŸ`Ôþy{wdÁuD b'„€¨+D¸¨+5Y å0¼”¦H­oï’–1ˆH¥b¿ßãPÆ­HoZ;g‡·ûÖËïþ0îaµaª,äõѤôìÿ›œ‘2ÿÞ-§$÷¤Ó»54gƒOgtC-ÇŠ‹SÛ:SÆù×GIþŸCÀ~ø!)óG—3ö^]/,òO‡ã%ÏÖ'P†³QÖÖ¤h‰Cii/g¤üàïW®…€án‘p¨Õƒ0n÷Ô4<Ç ¨ØUñªr ”?‚¡©™×+Ó; ÚNÜT÷╆j¢SÑÂäôlk¬†O˜£¼DO¦1îD¥Ñ(¸5‚ 1ÝãÑ óÑw”Ò†¡A]ëg“Ó²û„çÓ£¼¦÷iC™©8t+Òû~ŸKJÏdí€eäEVÌ^†FÍm;ûh˜Ô’ÝøÚ¥T‘¦©7Qfîæù]?™üC£¾˜Ý“S3¯F^¦Ã¿þF Ò¯`=$GÏ ieZƒT‚êØr,ô¿È0þÃ|9Þ`†ÝMÏÖ¹dÐ,0!Í›Àé3ŒÝx4Ç“õ¾o¸îg›˜ùÆB 'A@s“ÒîFþ6Ꞣ¹ºP¤ý&iÒ$âý6×⃀ô–‡NÐ ãÛ¤G²î÷)gB@ð ˆæ"|vu:äFZÞË$3VSŽå”ÇìÙU8Î$õî”Ì´ëlÿn÷·omÐçÌva<Å“ 0 m-6ÊT]®¦'¸‡Yk¤Ü–1£Ð¤ Ewƹ\Ý^p\Ï>ïyþùw ·ìÞa’÷r\–tÅÇÿà :wß·«Äæƒ!i™:äû†ºÇ=7ÑýЖRŸœ¼”’²^¦Bc1åøgbFêT;ò[¸eÏ84ŸMÉL¿´ÄþuB Q®W!øœ?)3å Û £"3~rr²çNwæ^Ýü1dzœª1ü²Ý}Å0àê ÍOOÍÏGCÓ²@¨šæ›f®'w8«ÖJÅ9)ãá%q~<$=s/t,ä•&c®ß:Ò±N—vÜwê¯%¯€Ûj=OàºXcSBN„€•'€1B ¯:‚mñÝ»1€k«½{‹N Òn÷@_Î/¡1ë{×cwñuƒFàK[°`û3ÓÿEÌjüæÙ‚Û¿0|x!âøRÉ!|íg¾ð,,'—SM@^â Oa?¿a_mÛÛBÄ!šcÆkÐd tÊ8ÕÇ:Ø©õ¬½ìN›‡Ù/¡ü©ÉîìÁ<#Ö+Pîo| ËëÄÌhÔ*“Œkì°B.D=};)³T°°œbÚ[¶ûñîZÔê¯>‚…åî\–Þ,HÙ~å(„€—€h.Â%WÇÃ)Ó°¦—BëЮ¢šf7öãpÅ®ô÷«4m¹‰Á^]ï·|Üs|έS{ð…_nZ+¾»÷ÀC#ÿ|ÖúÛ%$Ƭؽ«rNäöRÀ8’®œŠñ$Æ]ŒµÓDW _Æit²íB9ºœMRt}Ç%¦îE÷ˆÙo'e— †8»Aˆð×ÔØþ 0™§m¾úÀ¾°1âÖm/» ¯®$âQŽ?m|„ðÔ Í»#—…¸rõÈ~Ä! B% ÂE¨¤ê›?§óòèø*W¾Ø)¨XÝS΃Ãô ›ÃT™/b4œùåü[èAìËYCè(§yó5`p;à˜+2Ót•‹4€…¡T"Z_æó‡Q¶µ†/© jÅ›!d %¯ñÅF=s˜ëü/D—òþM° dL‚}žqÈ_¡¿×g )- Ã[ötG%Î à|{¿-ÎŒÿô8U 3eʤ$B@Ô"\ÔƒJ§ˆgôè¶îë¥ËסѺƒŸâñÁâ1ȱË`‘§:ÂÏÎ2þtËb€¶ªŒ}.0 ³­4ùyæél©c ½ä°Zm4ªe„vƒ´ÐŽ…†ŠŒfj+¡{!rjs'ºSWä?÷)cR¿ÄøŽÿ!õlo¡9³\e.B·4Œ2;Bêó ÃemïïóÎÇžj „£Dr°œ1àv%XäOÌL}Ýß¿\ ! "E Ü—_¤"–xj7«®ºÊ«)õ«ÑiýÖ1P›[ß¶J×2q)>z `ÜPÎÝ4n€–b][êqU;ºPÎòŸuLà® Íå°¦z²–Ý»¡à›7^…vgûÚñ9ôw ì7NÖõP¹¦ÇÄ`ÈÈ™ØÇ …¤Ï"þ²RŽ©æ#g'¹Ÿjá›bRúãGC:3Gf•ÚÃ/ÎÏö_ Ëð *õSr‚8yFÏYÃR3 .þäZ!Ñ\„­¾iãHy3ÇȺÓ;G%§gõÁºo{IûCSFÌ,À€O-aRfê<èrHzÖ­ð÷fRjæÖe¾ç¥˜CyîÀôÒËÑ•pc(ÓYÃà‡YS‡¦}ÌéÐ6éEdÞôÜ£}…™¯ÑˆŸ)ª#4‡ãcÓ«cΑð×Ô?M&Ÿ ‡âqÌʸÐh ýXãxÖ}ÿ–ä´¬ ®‹òi.B—‚Ê3uã Î<‘”ã¥É£~ó«¢ëRR¶bïû =y³Ø/rTb7‰}3p.2õüéHs„Ó³Ò0<Ç^}„¥em§þÇöët9žÐ=Þ›÷ì.üð.wÖHœ[<º÷Ó4ÆÀoÙî§®,µÏs#!ù3NFA³Ä¤Â.¨Ë!l9&g¦»íxå(„€—€h.Â%WÂñBMX`êR ʳA (L&Cÿ«vNÇPÆÐ[9Å”ŒT¬KaÞ‰ðk MµJ7‹–¡ÛâJr¨+&e¤bLAä"-›§òÞ =E9øôÇB[ê ,lUFàÅÓ½p›Ãëex=ú¿†¡ÿ=ÁV·úçÊéˆy_öŸ` Ž©æ^Ï®\OÞ쇅(¤u;Ü{=æ|¯Çø å{‚AoejÐÚ„gÀ3:Ô ÿм1\|‚ºöÿr~<žÂ^Ãø¢®D­Ïıð …Å7΄F£«ÇcüRÌ›i*ǵ(÷Û'µ[‹S} £ùÂÇ ³p9ÂÍÀì“û}ýɹB *ö*… ú¿ã øâÀG1u—/À䨏ãÖÔfÃV¥–݃ —G.;þ"‚hpßíEtOó—6wql¢Ç;¶¡“Öû6ºþÉñjš¾£e gÚÿ.ýý>üÄ s÷x𽔕¶Æßí>÷3ÍrõܦMÆlyräHÎGµ.ßVOÖ!ý9l=wW(1îFI$Í€¶eÇü±ÛÔ©S_/ý»3Åkyƒ=tKEqWŸ¸G'yGGg½H®* À7®}óVàUœ…@İp‘œžùXÄ"”ˆ„@% ïè:Z±Q^,é‰ò ’ì ! „€¨mD¸¨m5&ùB@!åd¶H”Wd/0“×;MÇŠÀ®b+„€5I@„‹š¤/i‡M`Jfʧa–€B@!P­¤[¤ZñJäB@! ê.ê_K‰…€B@T+.ª¯D.„€B þá¢þÕ¹”X! „@µá¢ZñJäB@! ê.ê_K‰…€B@T+.ª¯D.„€B þ’9£r@IDATá¢þÕ¹”X! „@µ¨µ‹hÝ™>¶7¶Ú>t:aKêvØÞµaµ’ŠÂȱö^eª Øö{­ÃáœþrÆèEQ˜MÉ’B@Ô3µJ¸p»_‹ËñäÜ­iê~ÝÐÛ*¥Ì† qF“F *ÎåªwZ˜Çص'ÏÜ›W GfrzÖF]÷ŽoTÐà…gŸ½¿÷2ä1B@! .Z#\`Ûà³7y7¼Šö²}®íÍŽêJGwo¯âbYT¥f Ty…ôÛÊZðÇ?m~_¹þ‰üù÷ÞòPÚ×ÇeÎ@n üDȈªj“Ì! ê6Z!\@°¸Ï4Õ¸¶­ÓÕgŸH‡vlƒ^16XtbÏ®üS+Ön¢wfÌo³Å˜vÛ¨GF½úø˜ñðçÅ… 1B@! ª@´w%¨bÁ‚žésdgíá›/t@°¨v(µ9æ3ò– }Žì¢ד·<œv?ÊãÂ5<"”ÕæÊ•¼ ! j h.Ôí£Üç°Æ ¥yÇàþ*ÆUŸ{@B¿£˜óbn.Wlöµw?pB³€Áõ-Fè(ŧB@„A j…‹¤$w¼Ëåz¥Cë&tÓEý¤A £r™[»–M¨Q“&/÷ìybcDa aÄ&A„€B@„F Z… ÍÛLÝã5ŒvcáEh•é]{îIšaR›^gô÷XüxœM´Ö»äZ! j!hldXKápº#zvï`Ê‹ªÝUÌ9&6l81ÅãÃ|ñm ˆB@ȈFáB»aøÈ Ãlƒé¦ÒF ÎK8¶tÙÕ'":Ñ^D€©D!„€Á D›pai-\±1çðYXÇ"xÎÅ%dÌ‘y6kÕúLŠÃO´!ÓB@!PYQ)\ yåM^¿ALÕ 0džñ±¦ætvDlÜ5"SS«ŽUbB@ ¢M¸àü8HSm±¤w´å-ÂÚaݤq¢r¸œ­[_Í…0®Õ'¹B@Ô*ÑÖ¸p~œ©†Ø+$"ã-ÖnÜNC³^§µ›¶W©b¶ìØK9[vV)Žš Ì<Jk€<°:ˆ»EdÖHMVˆ¤-„€¨Ã¢I¸`aÂsa’Á"’õöù¿Ð/¯‰d”=.Ó´øÚ‚…=c$êXt0’ B@D”@´í-ÂÂŽÛlU[ƒ÷¿/çR³Æ h´¿ÿ“CÝÚ·¤ N;†:·kiýä»Å´xÙj*(Ô©[‡–tÃù§Ðw‹–Ñ‚?ÿ¥ØÍ]²’®9ç$:²k[zsÚô×ê„]Zé˜C¡Ágô¡¸X-Y¾Ž¾G˜^‡v ¯æþ»Ðç:ýøÃ­46nÛEÿ7}­V¥9òréÀãá÷Ú´m7ý÷‹ŸhÝæíÔªY#¬Qq2uEþ"g,¡ëœ þE“p¹bJLB@!P£¢©qaÂú™X󻺨ìÉ- ¿]AÁI7]p íØ“KŸÏYb%·rÝfšþÓRºò¬é–‹ûQǶ-(!>Öjøq<îðN–}—ö-ÈépÀ½9%_>€n¹¨v$ý×J8¢"‡þ„à2ï·èŠA}©ü½ûÕ|,òz zñ¯i_^!]ÞIÔï¸Ã¨m ^<“è­Ï¤ø8Ý{Ý9Ô¾eS s-ûHý•h.lÁ¢”w¤â—x„€B@0h.8?vƒÇçÕfxöÄõÐH aáä^‡Ò²7^rhšu\‰E;·kAçžr´•‡­›YÂD³Æ‰¼#+5Hà1‘DƒN< ÚVÔ¢Ijߪ)ý±*Dz·ÿ.pd'º¤ÿñ–Pñ÷šM´rýÚ¾{„Ž>Ô·g7:ã„#©eÓF”›_Hÿäl¥~ÇN bé¤^Ýiݦ´}×^;ºH¹ÎmÎÕ&ÄE*³B@Ô>ÑÖ-«½ÁëØ¦YiM5@CîÑu&uAÄuçLA³1{ñrºèôchà =JýúžìÞ—Gÿûr­Z¿™š4L {òˆµ¾†5l$ÛB£‘_XdÙu‚VÄ×ü½š“Þ˜6§Ôš… õ[vQó& Kí"p"‚E JB@!œ@4 Ás!î fx\DŸiÚì%èÊøš‰ÖV÷‡¿ÿoæÿ ÍÂvʼë kœÅSo~éï…b\åÓ±Í;ö”O[ìwÄugL¯\äaYT_wSXÙ‘@B@!P' DS·Hµk,*ªÁõ›wÒß ÉBAߣºZÞy'›6ÍÓê [­q…Eh*r©Qƒ æÔ¬ñ¬ÁÅðMnf`lÇæí{è¯7ÐúÍ; h´²º[¾øq‰ÕRäñÒï~Ý,¡Ä†ŸçFž%ˆB@D1òŸÖ5›Ù•}Í|aoÃø†ÿ›1öææcÜC‚5·\t:ýoúOôèÄ­qC÷'×ÁƒCßúü'J}é+Mž1rÔÐÁXº;¢•R¸tÜED#—È„€B@D›pñáSo)wèKÏù䤣»Y?>?öðŽÖÇEÄcú¨¯a·cëHù…Ö vËv¹¥Éàé§¾…ÿlÃ3K|óp܉8?Ÿý°„–®XK¶€Q£«¹Äùž´‹KoM>?¡A£Gñ6ïÜ_Ÿ¤¤æªERŽ4¾Ÿù¾æû[ÓOÜü`ʹH£^½“#ÍTâ‹õæFfÁ¢ ÀCÿ7c®ÕÒ¼ù‡ÑS È çÇ….š÷¿^@¹y¤ë^KƒU™¬þÌðýÈ]!±;Ö¬U»ÏvhÝÔ¼é¢~ò5Wýì%…"À÷wûVM͸¸„)çw}"²QoÞË5„\’=êÅMÌ] ^¯A³þI»1x³i“i5®±ð¯[Ö`4C¾öäÑw‹–aK‘•çzÔ=«&bð‹?éüó“Ñ1Ôc,¢±ð¿[äº.àûûšsNtð`åVGu¹eãç@êºTÉõ°,õB¸`­…®ë4{ñ2kVÈÁ¼YÙû‰óųV~úmB¸à´Ç[ĹbbÛ7Œ5y]1B ¾àûWœÅKªÊÌÏC?×—º­å¬ó7/–‰& ôî\,éíØS+êXsìÆÀÎK¸à¼sê¸áûÐZ0 ÇX‡ËÙªIãDé©ã•.Å+OKÙk {åÀEÆ]”Ç#6µˆ@.x¼Eñ˜ /j3D*¿WHç6§bêêÁÝKSEä)òZ:ín.G5,DØÝ"–€áÐ Ø+D„ €Ù¼}mÚ¶;ª«¾ûó,[½1hÿÄf€³GwwdÐÌd¾ï5emÂgw‹Èspë@’‹ :-\0"»[$œÆÙ¡ÅÐàS ª<ø«BÚBQ=ëá* ì#YÏLêK`9úEåJ=mö/ôÉ÷åíËyŒÅÚÛihÖëÖï®±oÐÈç¦ÒÓæP{öÇ?è…wf’³›.¾[¸,“Ø"`– Ûuþý¨øbW7ð—b5Ü@ÿPÄJ~ù»œñtÕ™oZZ‹kýyM½üá)CçŸúunÝQziÅú¯éÛÅY˜Ù±Î9q,6$ÛˆmÕ»Q÷ƒ0ÎcM›3ã'VWš1r^2S„³^\ öªt<µ Êþ±pá@qëdAé‹[/9­Fv›¸ã²þÔ¢IZµ~½7s>Ñ¥µ/F 2D'êùÀuärÖé×I ¢W‹žwûy°uVmY-%Ò¨ PçßvÃ\Ù§Ókèô翟Ð!­N¤ï~‹Å·r¬ óE´yÛoôëßÿÅ.¦1tYÿI´qÛ/´tÕTJŒkNÇv=ÍÿsÍœŸF§÷°õûdö]•¯ld˜ ;ÿ• V…°_¢|”¯5Ÿª{wÆ|KkpçВåëè{,°ÖëÐôÕÜß).6†ô9‚N?þp+Ä«ÏÆTëXº ›»±ùù÷UÖ’òcîluK|=u¸á\jÜ0š„¿¬ÅÚøºab¼åß÷¯U³FÔ±MsêÜ®%}ƒMþþ]¿Å.8?-YN§w½ûÕ|+/‡ujKSqÎéðξûò éõOgÓJ„éÔ¶µjÚ°4ê"ìóé÷‹é—ek)¯°ˆPL‘ ‡o:{ìÄ žŸé—¿×XøqB:û¤ž¥aëÙ‰ýLÔ³bKqë zñ"纲Æ0<´yçV°Û—Òº-óK£øù¯)´~ëBëiËο¨Kû¥nûò6Ñ·‹²è÷?¢EË^£N­O.u«Ì 縞6ûeÊG1%öä`}–|ëŠ×=á.†y¿ýCW ꋆ»¹ÕÀïÖ¹a·M>ìwìε.O9¦;Æ)zËËïÂL¤°#ðɽº,8Àßk6ÑÂ?ÿEü?SÆTô=º›ç§uÿïËŸ¨÷‘k3Xë±ðl,†Ù°0² ë´\:àx:¬S+¿–þ8ÎE­¡‡o9Ÿn¾ðTk×mÐΰÀ3{ñ kKò[.:.:ý8úð›…´ugí„m—/‚Gy"S¢:øê¼æ"ÒHZѹ}³©C«ðÒß„ñm© pWi2[w¯(=gÁ#ڌƉ0[e}©½œTD þ¹¨ˆˆ¯û%Ž£#ÑMѵ}Kš»t¥%sØ!¾^Ê;bíÇSo~I›¶ï¢VÍÒY'× Ìøi© +¼Mx«¦Jã,òxé² z[‚[ú ËVo ztVåH+Ìʵ›!ˆ HË!´thÕ”7H@Š×1á¡l~_¹ÎÒ„´nÞø×¦EcZ¼l sòÑ–»ü !P{Ôiá"EEU×÷È;ðâëI/tª5ÎâúsÞ/¤9Æ[ئi£.TèÙ‹i¥l«°ÕQ–°3Ù€ò…VIž¬±`Ó ±xBhÿõQºÒšŽèÜÖšÙ‘4x –SŽ~øµg[Ý";öäÒÓHþoú\2x@i2XªºôÜ÷„§M¯Á Ð“{ZjÍÝ+¿­,®ìÚÞÒzp÷NΖԤa¢%(y±Íòµ›0Æc ´«KÃòl™zn‚WR=#ÅnuZ¸`ôUéZعç_„7¨cë)·`öûØME;ìZº… S§.íNǘŒhꙥµÜîÇt¿†r¶-¦^Ý®¦µ›æYq”z¨Ä‰÷:,XØ4øêû³íå€@Œ+ðcëÐ4³ûõ[ökÔ8šÐUñ÷šÔ®eSš1÷7:¼"d€ö[5k”hiH¸Ñ÷5.'½-oX`áñÛwí+uÜ.Û4¿œ³ÎêÖ¡ ºGzYyp ¬]ÐûºìŒÞ¶÷zz,3 ³ž2b×võbÌE¸•”îŽÅ¿IƒNxŒî½òkæÈ/¸ŽkF\³ŒN?æAšñ¾fýÖÔûˆ[鎋¾† Òšæýþ’¯³œ  víÍ£s¶•þtÝÔo ‡î[ÓÊu[hãÖ]VWÉÿk Ø//Æö&¦”öéÑ•†_;ˆ6ïØM_Í+S(®MÛwC±Í@ºdÅ:ꈙ¡š#:·³º3xZ+ÝøËg Œ?Vå@P/¤¬yò›è³ ëq‡w¤ù¬¢¿áŸ5¬Ùðïr 5âOš%ø¨fóU©Ï\ðˆ5[„«ézvþL?êg <ýÔßäì¢ÿ~w9ÅÆ4²4þîr-‚˜÷Û* ~\UêìzYéy('}êB˰æÄ˜)cÖH<õï}8Mÿñ7+èç³%zÕY}1ˆ3Îlùþ× ‰Çj´iÞ¸\ô<ó„M{Œ8ã„#é̾=Êù fÁÚ"²_fí8 ƒAÿD¾Ø°V£ P§ñoϰ´Š´-<¦ââþÇaê ôÖç?Òøÿ}e¹ÅÇÅÐÐ+Π–>ã=‚¥)öB@D.B¨^<Í×k ÁÂ×»PÄP d »< ×¡W ,µç™ü³ w#LL½Å¾Äº éþÏ…P«C¸-~´/<íXËý’½‰¶áÁ–ö€KÛŽ1žÃ7N_7>÷ÏC ;ˆ™q×`Ê/(‚–SMK /²5î/éÆ OÁ€Ï®–ñýâeÖâa<;¤ABÝyå™Öú.ù˜¦šdzTÅ!P ˆpÁJË+ÜYav““¨„@@¶`Ðñ Zú œ,öä1<…7æÛ¶k/-źZ5+#DðZ,hˆB öá"‚u7}ÞÈÆ&Q ºE 6ÆE·_z:Í[º kZ,£¸˜kÜÅÙ'Ÿ[·Hi„@ý! ÂEý©k)©¨q=0•b„€¨Ûd¶H•êWÑ‹gA¥[~Z^›æ½èäžwYˇ‡—DåW / %„€B ²D¸¨$Ï–M§³ûf–†jÞ¸{¹qlwåÀ׬MÎ.ê7ëdœTê?Ô“mXésÞo/RÎÖÅ"‚F¨ÜÄ_Åx—Ó÷± ¸! „@un‘奻ûcó±VMzÐlNöíâ±tÁ)Ï`éâÔ¶ù1ôÆ—#´IçŸò4ulÕ—VoœC_Ì}ˆŽèx>­Ìù†æ,ùú:ªë`Z»yÞR*ïd^úsõ§ôïÖOéû¥Íéèî`%à ­½Š×›*Fl„@(xåN^NŒB ºˆæâd{u»Šöæn„q5JlOíZƒÝOßÅn_[vÅA.úŠ&~|uk?xùï„ø–´{ïZËy×Þ5ÔûTÅäl§EX¼ë_]E/¼ß›fþœ^"¬ˆF£*\ësXÞäéóèµOfcÙíŠ½ØøŒwaå•<'¾? ‹ymÅÌŽúô»Å4éƒYÖ¦i¬õ#„€¨ˆ@×\ðòÆ-q RÇ6'C0h‡Ý'OÅŽ’NìƒÐ) ×6|iv:íÚ·ŽÆ·±‹„øâ y1­@ëdŒÈDziÃŽÔ­ÝÅ´eïOä¥m¥.¹ùÛ h¼aý‘Æ/ Ã;] RBr Þ¿ãòA}¬ ÅÆ¾ú=sÿµÖî§³ÿm }±YY‹& d|‹µ*\tÒÑÝécÛv–_8.”ôÄõ‹@.|… Þ Á0÷/èJ5¯ß²€\Îúþ—'J¼+ìr%9ñe‚ëÞ‚2×9[ÑéÇÄœ~'V8ìC[v-+ã^Ñ…iÆRBlC:ùè{¨y³tÚ¿ šŠ™ô÷úé”›¿4*")îÁŠ¥·û{˜åüã¯+°×v+mÝÄZv›÷öà Îx+÷r¶PÚ[û‘ytzo¦ŒÕÆTì…€ØO Þt‹$b¥@Ã[~™ãý(ÊŸ-]õ5kØ•n>oÝvá jÛ¢ý»i6Æ[ô¢[Îÿ BFà…~6îXJ^½î¹bµnzý²üÍò‘ÀÆëm„5ì(ÊÚmPß1ˆo]wöTê}øÍX½°U™lACºNÊ`‘‹ âb÷ Úñ±.Ú—¿_@î„]LÙð ¼ðUÃ’­7(+T‰Z¬…€T§5vý²£QB,¾Ê𲌃Faÿ‹Ôöè¸/o}4;»5ÆY3M³x#)Þ[„ Þkäñ·:–}kúþ½ ¦ÎºÉ ç¯Õ(õä„óÇùLÄ ¿|—޲fžð쓳 l¬Ý<C}NËÖ~Y¡Fà–awɪX×b+±½ùnl”¶/¿6`£³]ÚB³Wv OóÆ ¬}=þµšú-Ájšb„€¡¨óÂ…Ý@wjÓŒæþ¹–òó§ÄÄ%¡°)õH@`Á¢"(\Ea8&v\n׬ö //âÛ½³?|å wèaq‡Þ÷˜¶q½öñÌcfnß^~Ï”ýñËY]&p8º=^ýôì<ºƒ.8íjŽ}Ií@zù™½éÃY iÚ÷¿Òá[×e$ÿÏÞwÆU\kŸíZ­z·-÷ÞÁ¸6ƘŽ) $y$ˆCB:äOH^°IÈËKy$1NH衘f° n¸÷^å^Ô­.mßÿ|s5«µ¼’vW«²Ò \ß6wÊ7«;ß=çÌ9ªo …@èÒäB ?ꑞHV³ž­ßdžM.¢ˆw‹E¡}•ôäxBŒ´]ö£é‡C#ƒÝ˜‘Cwdäxï=Þ^ìñèß÷ùôÿùÅ·~¾F§{F­Mlà.uç¡;¦ùûƒÐæŽLŠ„è£ƒ–]6´/GNíË?/KÉò9z¡ÿYu P(šB ËÛ\ ¤³£GbÑ'‹%ƒÈn¾ê£)Úë:Ú…öõËLbµ‹Öf´}=iDãú‰ÿ}‘Ñ%ö&™ƒïF£gåü…¿:ÿì‹úæ½h¼Æç{&œÊBo–ÊÙ)Ä¢¹Æà‚誤P(BE K¿1ä?&h£ÑHcô`é…Ž.”Íbý²)TŒÚ%ÚƒvYŒ>Ò#™ÌÔ mFÛe?ÂoHÑ0ÓïèÔé{N•Ї쟫8°,Vµg³.æ[lS²Z@dÔq )I4sÒˆÀKêX! PE K“ ô!žÏ[cw¹.6]Úü–/Jµ&ê8‹…úõH£½’©¦v$—Ü×á H,д§_ª™zeð2Tkœh+Ú¾Z$8&v§Ëçv»aÁé«©qzüγñßñþÀ•M¼6MG_à{Šh‡O]U( 0èL~2áuyŠÊ+jpÞêOv¨0A›L&²ò¤m³ÅÓè~Y¼ŒÔMG G²÷Ì JKYBqq§Â€-:YacQʪK,z'hK-¸}h#ÚŠ6KµHkk,¯¬ñ9ŽòÆå<óÌjr®ÁÆöß¿ð7SÉç½×G>ˆvü~Ë…DƒXuBžo±ê¤m4éuúw~úÈÏ×*cÐÆ¨ªs…€B@!нèLäÂ?.‡ã|UCWËN|âYEК¤‘ =™y¢Žã²l6r8œ4²o&Åut¨ÀGEódžG ñ»y8d?‘´ ~,°Ü«B`¼iÒ{iPªžrYb‘˜˜@¶›h#ÚŠ6k+FZDZ€#ð´×Ö\dkѸýõ$ÁO4~ûÏß\íõxç4E4¼>E4c¨Î …€B€:¥-_Á™Së“ÒÓŸØ›wŽ&qŒƒÖ&¨F`ia#IH\.y<êÏËëR8nÂñâJ*¨@Å<Ùët>Ž#RKcï­­Úÿ<\{Üɬ†‰~,Œ:e[}”›G‰L(’“))‰7 n#ÚŠ6‡·RÄ_ÝEÀÑÇÎ3N9´‡oÕSýþ¢|'õDc-_[ ‰Fw §òKhÿ±st¡¢†ãÄÔ’Ãé „$¢cÐB¨·Râyy±FÌ¥>=4˜ãÁ㧃ÿþœN79YzèpzÄfEÆÉÊ¿ûÄ„8ÿòØï®j¾B Û"ÐÙ$bâ[³ôýýƒÇ^^ºuÿñ4&­ûlç¡õK/øÅ/„ ¼ “·™]m÷æˆeÕv*«ãžÛJ.¯•×öGïwadÒb5èÈÌ’Šd‹œ™Ù®j«X€X¤$'‹c¨D°Z$R ô€qôy=žÊ}Û6ãSô*$‚g‘º*Ñ`ÂÅDÓM+·¤•ÛöSeµCK£¾ŽôL.uäÐhå¿>²—‰¥›S®ÙII ºnâ(š1~8«¾:ÛŸ`+;𸃠đS¼åә ”_ZÆ^A[v>‡"ãÍ”™–L½³Ó îÈG èE)Iñ¥«C…€B 3#ЙÞlž§øü¹·öétß:ÊnŠ÷Éi5†šôÂ@– øl³ÐÖïóJV?XØØÓjµSºÃ!&¯×„ã |i!a"Š$Ø aÅÚ`ÐÃþƒ¥(¢¾8ÍÆ‚%Xµˆ_j®‹à­~ûòÎêNç^Á9à¿\nx8ÈÕ®@40–[H)Þød=K)B-–™Þ¶j±@•Øû+´jË~úÊ­W i†ü<¦.Û3…¥ôÙæ´óÐI¶iò²ÐG&s!K⊘@—’ÞPÇ$ÎEzr’NÏ{>†a³pÏï‹þxþ°±[òL:y>‡Öxµ×Tï¬Tš>aM=@¬¢Š)`TcÝ ÎD.=¾ªÅä·æƒEïÜóØã÷½ùé¦ÔŸ>4Ko6É@^‘&wù¥ˆ—¹vba& Û8Ø9x“½ž\ÀàÓã¹ðFL,dKe]D@Õ6`å V…@;øx«Ø Á}´­µÉéòãÇÑà]Uë?^ü—ç®ß$ÁðËfÞ~û^Ü9ïàzÈ)JD#äú¢‘ã‰qýœ'vH̦"ÊÉZÜ.½ˆi×óØ`Ì[V>‹^xÛN³gŒgIƈ¨Œy40Ф Hßæ¨©_ì„Ý’“â­»)=~ÿ}a2Ÿ^dÉéÊöIEcéÕ¥e´lã.zô®Ô»>ÀJ…”ä$«µàμ’c¦TÖØ9ðšS¨WÙÌS!Ÿ™–ÄÞz“©GfJdQO)!!Йȅü’Æç.//©>°}Ëÿê&Lþõ+‹×ù™}MTÔ#’`ˆ Ÿ †&E0ó‹Ð*ˆŒ=N'¯&aÉ¿,É¿?ÃJõB ~±jDFÏ«Vày* ¨@`¼ R¡9ÎÒ$x¦µ ¸!(Õö/V¿^[[e¨0"Á†7=p–˜ÓÑòEÿ7ïEÃÒ§¿áYÂ×ÃNA‰†—Wø|wsa—¬:ñƒ.пWU¡ÿháoM{Û2Ö‰”V`\A,>ZË“|ü~JOO|9‡ÝáV>€ÕIÙÙ „¿•EŸ3«öyiæÄ‘‚`Dcì[Ù¼ CŠWÁ¶(nþ»ðð1þ6¬ü;N`ÿ1ø%ý룵´ãÐi–Âm ””UŒktÔJfS“ƒJNZGuöìln6ýéÍOÁ€KrHH J+ýªNÛ3X:b`õË y3q[Í,Eiˆb϶VCûõ¤Q¬n?²¿°÷ÚiuQ! ˆÎD.Ð|IcâÃèØ°|馤´ÔWùø«ôùœuµ®µ ¼¸åòN ¹L“=ôïN6âÓ¤üå/\^)!$˜œ"I¨Ol¬Á‡äõAz#? “fh+C¢%±±Øvð„.oßîÅ»6¬Ý <6I0ü’ ¦2.n¢?2Õþ‘”æôÕÜöä#îW¹ýau¾ÑxBƒ6M4KLö<öƒyžâÊrýçû¶Ó¶(𪋆J ŒïÞ¼3´ø Xdf¼uQ¾ö>:@´ýœ|°Š(›c{ŒÜ»Ó ¸îcÕѱ3…ªáNšùïžÑÏ[ø«7ȧÛbñü+Ù6·ãÂ0½>ùÈ“—T!OSé¹Í#Ÿ|Ôq@’P‰——™”â»ÿÊëè~ç/¬”šÌ!åS8Äü$¾Õ:)ÆÄ¢švßY±…ǺPH,šêC{_‡ôÄU˜Io.ÛHƒr³YšcÞÖ«#éG5«Vo?Dk·¤ÊZÍÀÕ\o/‘”xWQ•³,€y(Oê>“oOÿ­$²±*¯t2{x’ÿ"’jÃzÆÄª¬Ô”Ü7ÅÛö’Q_ÒóPÕ˜Í,áMFFv8{Quõ´õ€ƒ6ï;FG Û¯¹\D‰ ©P•I! Š@g#øBƤ‡5€˜kx‹[þÎŒ›:½È{å´‡þçÕO“G ÊõM9@7zP¯VùÁ¹ÀK?Ô$@ô+U! ’TÈ=·'¬„:°—ꓞòZX…6Ê ?XnŠU!0Þô¸]Õ[WöîÞÍöqV‰#ÞÀÀçÀ8û%ÜÊZŸž—5pšÿâ¯ÿ›çóZV‘üJ§7Þâª+zÏx<¥™Ï.4?ò³GXÒJ¿û—e°ÛåúñÓßð~çÏ.´e{}µ«V¯¾¶'Ÿ^¢dF4X:4‡±½›áñ[í"z«—VÓËW“ÍšACûÜBÃúÞÑÀXBâ`U×êm˜0lc±¤CT!À(X‚#-e±ð·²rÛºqÊhñ[‰†+X}¯á· UÇR–è@e#Lø{ÉLßÃvAÇX½\иœö<‰±˜Ï‘%í¥ú–QeÅT&V휤nšBSÆ`izëˆm{b¢êRt&:+¹’ Lˆ˜ðÌ;¾X½åÐömy“o¼åÇ='Ñ$þÃ÷%Z-¾”d›.ÎdŠÚ[ 1‘h|î6~A5>·<™.Òáɲ¸‚T?x`ݦ寷Ûk1#`†6`-Prá'>Õ0€¦y w’Î{[JJÆUüwj²Ïë}™%,7?ù°kÛoZs=>û¶ ®è}A·ëËn—ó9¦MÍ=9õ©/W”é¼u³ùmüѵ׮¾„Xp½¥¢±~èÐŒ§®½«lfRªïÞ„ß #¥ËÌ5u%´ãð+b —h`Ü Ú‚ Mënä/S8Këo¬²?MíÑ&´míN]{ÅP¿$+Z¿•¦êù:_TN/}´šWgTŠ/ú¬Ì5L´Kšz¤K^‡}HJÊg”¸™J/ÜM¯,ñÒÑÓ…ô•[¦t˜©K­:ÕmèläÀCTÉ 6øÒ†J2b=%ÒÊ÷ß^ÂÇŸ}Yÿ¾ƒ‡·&$¦Z¬Ö$£ÑÔ:Wž\h¬%·›}™ÖÖUÕVW–Ÿ:røHÞþÝg¸H€œAB2QQ¿UòxWàÛ á!¹ðÒ¾üM“Î2óÛsŠ«ç-ÐÏå5·ûEv÷½œeï°¬#¥–fã2ÞqÚ‹þ‡¥ãû¼Õ•/¯Z5}öÆ£kæè|†ù>ß/ôõäKn9>\â;ümæœy‹ñßýhö‚¾}JúY­û©¦®È_À%D£÷Í4¬ß¬&%šÔŽšœtœíjìnþßí/¯³ÀClqé :q®˜÷í!¤[m¥ñ>§Î—Ò_ÞZÎÒJÊÊz%G:,íÚ£¡Šm:^f×ÐF–Ï}?xë•‚ìµkCTe G 3’ ü=Ë ¦Þ R*!‰‡ëèÞ]‡y;Î÷ÐäÃ&óña·IÀ ¸ø±ácH'@$ ±¹@Lƒp€x\¤ás~‰êγÄâ÷>ŸaÖµŸÀ5Fs+s JþT^ù¡8$û|íÌ÷¿Q›ÿì‹&´öé¹Þ?sÎ??»p+‡m§«Içy’cüáÙºãf«í‘?XÝÀD¡Íÿãp¸}ÕÕýêŒt==~Ït¦h :µ”·/%G^¥¼ ‰F#¢á—Z°7H;«³3'Ø @Üß\ÊIM_»e©Èâñ8رÚiÚ´ïÚwü½ækö^¿œ«è|énŽz ®×tBÛÐÆƒ'ÎSßž~•]´¥6!±ÄÂ[Æ+W²jð’Ð3M7¶ ßÁ¤$­æŸº›61ÁÀÒÕë'k+yºp·U×QE 3’ tPN”øÊ–„!tຕ7H+ü’ >Á@’Ïhg]ó_à$‰l)€”Z`6ƒÄÄ¢I©ß#¶¯øè_ÿê—ñÐC'ALDbb±Ÿ=}Ü“™™é3g?$FKxbÒ=ý À‹˜#úw4žÂ€³J„´)Ý7s·»X½òU—½æ_œñVQXDÿè¨wÖ$±]?áWa!}n£ì”ËØÓ%T"eìû¶ð5Júpí·©ÎYFc?@7NšGGÎ,crÃK½™fO_H/r;•Vä5û0Ú†6–VT‹vÃȶ@Ñ$’X`UÔ럮g×Û5ÔC‹ ã‚¥¯Goúx½ž&ŽêOÉì½½ì`‚6H]TÄ•\BLš˜,‘0‘ÊsLž˜!@.°päýªÞwbÁÝIâbUð6 ˜ A.¤ípÃ}à4 d˜DT¯›×ùæètúùsÝ.ÆÌ`Ôïg{ŒM”RxDcÛ¡WÉlH§^iÓ)#a 1·`Ïâ„–.TŸ ÂÒ}TTv†ß;‹Jo¦½ÇÞåø é&=ÒÆp0¸Z·ëtðÔbQè“æsþ}< õ¡Ë‰>XóMºjì÷xuJ=0óMòø\ô·÷®l¶hc¯jÁägÑü¬DkB“Ä+gàèêT~«‰–(‰E3#’š²œ—½§-ûŽÓ ö ¢M²×LÕê–B ¦èÌäBNœ˜¬1Ib±ƞ¼F.º2ÉHI. òä Dç¸.í,ä³|©ùTo˜9kþBãtŸÏ{ž|(‡¶ßbçøÉ׋«Ÿ|X'V‰¯vä”AS—¯ò@⥼ӘçÖš(.EñŸ–‰û¤¢j{)í*YDµÕ‹Èí{”,VÀ^‚š * ½ÞH÷Îx™m8*é“?¡þ½®¡;¦½@eÉÓ^²Å¥ÓÔ±?¢j¶Y¿çT\q„œøPH^VïœÏ¨ÎµX9‚剠^ð·Â+8¤ûù !È…\9óÅ®#L^Îù—d†ðx·Ìb2•2ùªfÛ”±T«¼Úʦ[¬:ÝeèÌä c"ÄŒ8‰‚l`ÂÄW1ü2Hb!m3@*º2±àî‰LäŒ 0Àî5)±à{ͦ§u¯æ ØDúÉ׿S3á¯ßÿ¢~ż-ß"oÝuü9wÑ*^Ä{/ÇQy²ÕÊêZØ_J4ž\Jû/¥2G1{YåÎ3­bÏ%-”sñí©c~È“o2õ̸L‡¼³+™$Làè¦}èÍ÷Ñ©‚Bb1¤÷4zà‘%˜M6zíƒÙ~JaÙ~Qp~éžÕ"²>^-¹`kíײ&µ€¿N‰Ö±/dU‰L…e(ÙÖKž²]ÈV?±ð_ ó@#š#7LjÑH+gΔŠ"áÙR¥æ€7O7QKå0ð.·‹ÌÍ“nkÉ^󵪻 ØG VÈ–(Þ¶˜<1]® ”XtÉCà—^Hò|$¡Àµ6KO=d?IÄÿ}¢qråzçDòêÞg9ªödtþÕ¾öaå9™Gô£þshÉ–ƒü£9rkw?/l.(«Ò&ãôÄtαCÜJOìOÇίògs»¡‰j]bY…ðÏÁ !µˆ†äê|ycåÌ…JÍ0ÕÈaåUj^9E¥e·±{~¢¡½3„J)Zd¯éZÕ…@×@ –È…D&6ùIH*§; ô 8È$q‘ç¶úQÏ»\9¶KÁÀ4 >ÑÇ.ª[×H)àwcÒÈoÒªóh`îµ´+‡òÎ~ÞdÁe•'˜ x…/Ž{‰°×Ày³‰GR´¼^%ÒlÞn !¹`ýó±íŒx aÍÕòÓàÂ~QÉýŒWš8(›Wñ0}60­’¼FuU!еj„XîÞ”x[ËMªºÃ^ö{mƈ呌rÛý"€($·ÇNo­|R“úÑÜ;×ÒôËŸ¢õ{ÿDGϰk©ÎQ.¼‹Î䥴OÜ‹ï Mäl¸ŒÖʶ7\üeab„ä6ñfhÙp‰'Îh¤þ=¦Ò­WþO4Še ¬ãÿß%åÁÞå'_A€²¤KîEóBMíH:{þ{"ÌûЬê›™(l^”*$š(«²º:±(¹èêc¢ú×`ÕÇs¯6=é]8@ÿ\|ƒ šß‹âòÞšomùŠ­¿ ¬4"j“ •4sQMzÁÁÅâ L0Ø»ZÕU¿—'Ά>4SL“·J+ÑѳM,ùà„á_gûí<òº¼tÏñlXü¨ ‹2ð2P½®m_Yg.s”Z¤³ŽW± êµDX¬HÆE£¢NAhÛ¿Ô Uª‹ ¶E“i[§–¼m6®ßå®k|)äóhôe€\h›ú¥ÆÓÂT^q=Gm™4רÔÄ~Ô‹WÒ9½Œúæ\É¡ãGòl Ü¬ñ´çØÛB²Ó;k"MñMªu”qž©´xýlÛ“&òµä„\:_²ƒ¶ú7Õòòa$“1Žn˜øŽ`šH[.¼Äþym9ìOä+”Ȫ©=yoÑY6¦…Vt2«­r¹¾êºB®ÿ-Ž™²ÙCJd–™ñ.ÕÖ aÉÅ`Ú~ÊGg+tÃef¦)'Z!¡¨2)´ …@—C@~­kûØëÚwæs:šÿ.O°ZÝÀ^ôï9¦ŒúVà%vqþ m?ü¯‹®á6'=Ó®¢a½ð‹÷3Í_tŽ**¯&7žÒS—ð=ØI‡Ÿ ®èÏ„a?ŠããNØe:ÔE·Nù=»uý>/}=Jvörºp·s)·ÉÅ«/ìL¶Ðî¼7hú¸§™èií®çE†s̘•ÛC Ö,úÚÍKèosØ›€„Õ<_¿u9­ÛóÊ/ÙIsØÿÈ‹]K½3'¯ª‹×—Ò —€ÇB8ô‘-~ØØ-‡cG¥•3èÍÏ·Óƒ š $! ¨²(º‚Í…E…€B  Æ×Õ;2rÈY/À–j,"gÕvž@/§³ç~@ÓXm“ÒLI¡Ý²;*8ºëï„Ô"ïÜçÔ¯Ç4*ç-w5;;%ܸ赪6_,óÍJÁ¶ 5Ô'g²¿‚3…›i÷Ñ7ÙÙŸD¾~, L„衪`’‘𨗥Å4¼ï­TQ{VH2Fõ¿‡PFiűÀÇÂ:f°zäü;Î:K//ÝÈ$©JØÃ„Uʬè†(µH7tÕåØ@``î º|øíl-.$¯CûÞLØ'¨BàN¼¦¶V ³ÅÌaç-mOÌÕt¡š´¹-TV1SlŽ š’´†·4.*¤s¾ÊiE|\š<½hÓäß² e¸P]@bµ¤úï;\‡£%»³Šï]\FR|–~Àf[K;¼Lùl/s¾x—°‰¹ú²Ð7î\M}ñ:|úS™-¢=â½de¾Açó¿OK×í¡¯ß1Õ/Ѝ@õB   $Ý`U»7\ÀÞ%A*l¶xûóÆ{—”xõ´¸)Ùà@y< lzÄ 2D±<7—m0@*ú÷œF'Ï!ž¬³_ xVsÈ4ŒãµlÚÿWV‹¼Íö=åe±ï=‰—ùfSfê0ÊLB'òµ2d¦#gW»ŒŽÿ²õàK´‹¥ˆƒºkìÅ´ø‹ïÒÁ“K¸ Òùl${ƒ¡šÛ¾—Žœ:/VÝ€°©¤P4€’\4º£è€\ØQƒI’ &ŽD'Û<¸…c('/S=Y­£Zž]ž§ô´YÂQqß˪NÒåC¾Bƒz]GGx "Ê"Á¸òæÉÏÓeƒ ,žIÛÿ›nžò<]Ç‘Yž\L¹™ãýužÊ_O³®úe±dª‘Šê3”Âê™`PûéÆ'iÆÿO¨Aôz/þ å¤K„,ù¨cãÑYr­d0•Qy¹‹ìì+ñEÔÊ‘h!«ÊéŠ(rÑGUõI!Ð)¹°˜Ù3Þ*ü]ÀçEEŽ\à Óãå©ïGÈlwÞXúðX³xÝ<ùYuÑ`$ŠÕ$p8f`"€<ëvÿ6íû«Xžè¦e ?+S`åì!5p™ð¾ï6ø½Ù€"ÙÂVÃdä>¶b…ެ?p{”x‹ •sknÀU³@„Ô±B ¥iÀB).BÀ 7³Î]‹Õ¿ôÂdª “ÙB’ËSM99 #"ÍáH,d>\ œô5ûŒ¦—+C–%÷ˆRÛØëi`2_ëö:–X fI ¶_S2¬èQI! Ž€"ÁqQW4€Cª_{ùÓ] H/ð¥mäÍÄ$cÃ3TÇeVÖ«d1åG¥Ÿˆ»'@Š•B;A!55£ÈíJâå¼é,µpóò]ø Qä¢ jB'E@©E:éÀ¨fu,Ðÿß4i>YÙ(ñ±Ùèø¹Õ´lóÓ4nèƒ4~ØÃÂÿÂéÂìÏá/ÂIü.|ó®u´Š}3LõáïáÐé¥üÌÏ…O‡ŽíMCíÒïGYU-‡Œ/¢ÔäUìm´ !C+àá[WJðRV~ %ÅÙ_H‚ JjÑ•FXõ¥-P’‹¶@U•ó”ל¥Óì੪&Ÿ–¬û>{ü—èSeÍyZî¼ßX1GC»zô÷ý}M¶åÒÌñ¿b÷Ö¯ÒÖC/јshpï™þû}€ «:üØÍ`3ÞÖµˆ@ô1ÖQiÉÝŒ[<]Ñ/ÿj&• ¦P’‹¦±Qwb¼ü6bãBöTéá€!¦êÚá¼É•<„>mö?•wö3qŒe’§ 6ÐÔË~Èõ4pôƒ§–ˆe‘È4 çtê›}•ðœé/ …ŸÏB&^ÙÑÐöèLb 6Ú„'"£VÕjîÈ úŸ-4­ÛÝöùtTránvI>˜gZÙ'ˆ•—óÄÊ8ïR£Ûý$T‡Ã@@‘‹0ÀRYc`/} OØ•uÉ­îÀ5ã~*ß”@IDAToÍ6Zj˜d†äÞ@½2ÆÑ>7 ©Å©üuõyZÞ¡mhcNªÍßî–Ÿ -È…$n^Fée ÆL«IÒ©¨ø¿ÈËê˜îž@²Ê+§Ó¹üï0>Ô7¨_z<%$ØxK ›Mx4…32E.ºû¯Eõ¿%¹h !u?&¤‚é…ð¤˜d5±-ƒ—ªkdžܟ£ìbº¸ì}ûîÍ4ëê?SÞ¹U"ªç#³>§¯Ý²”m+^¿¤¬sÅ;è¾™¯Ñìk^¤³ÅÛÙCåŠKò4umƒÐ"=ÉÊ­n FMåoíõd«‘ú'ëYMÒŸòóç²-FŸÖ“ÏûXRY5…IÅ÷Ùç J`uÇ e'Çs|%'%нÍf’ ©Q’‹˜nÕèvB úò×vj¸ªF!Ð~bÁ¡g6öY¬ 8W5ˆ'Ó¾¨Lþº(¼ðÞb¹#‹^•›¨§ì[};52„öGk¹#&D”UV<À8n¬}ìT I#b,áaI†Åè¦V•Ôñ­Z£‹ö¦2&2éÙ7†ÁPÅ[-cWË$E9D; "=žD.7U¨÷ÌLPRø™–Õ°”¦Œ W ¿Ni©Ky|¢ãvuùXµQ];Š*+¦±£°4JY2`¹pƒýІƒæ]-ÆÃljä“ H.Ù$ ¤ÄêHTR(ZF@‘‹–1R9b©¹ÀäÇ*¨êêâØöÂÅâr'•Ö Wa¥¥,á ÷T»ö6ø‚w¹2)'ÞG½S­¢}h§6™iË£9™¡, ¯×,¾Î±zDL¶h`’­6QÙÎÒ‡Îeaò‘ÈŽ·<–pxäà¯~'·WÏ"…WT¤‘‹Ë@bÁ—¥IlL(L<±›õ>²ò†ùõ@-affdéH©#—Î<&¤"6Û>îóIžä ¹”†e½|Òlòz¡âȪ–:&?vû­½›zYœ”`‰° B Ip0Œ‚,ßL>!™öRÀÞ¤B,=åߎ4àŒæX4Û1uS!ÐèÌäB¼¯0ƹJ¡#ø–Æqàyè¥ÄhNL&Ú—©ILB À_åž8áþ:ÓSC&rP‰+ƒ ŠbÿÛnÎ{˜'Ÿ¶ñZ ý?–›V׌¥:û 2ò}ïxK,0™±8ž'49©iZtuûr‚ÅW¸LóCU"IXMm-·Ñ.T&ˆþ ¬¤ê$ž¥Ö¥-kõò¾!œ:ÊCùrÃD ü±Ç†ëp=G^&“‡lnU8õTŪ‹ ¬‚Ñžwó¤_NFÛHÀFçæç<¼¡¸.ççê·D>oðYbâ<‰Ld’Í.&:îOœ L èc`[DŸÙ–j1f V‡XÙÞ„B#qâ¾æ‘3ºã :«þQtq:¹drGýƒß{ê “År=õå7M~=ñªs•ÂE€?6«XFÏïæS>¯oÙ?÷Ì6.£Ë“ Lf˜DÌ<‰H5l œìþ“%&:¢:²òµ2¶1¨â•Å<áëtü¥Í¢~ƒ±‚÷­7dÄxa%‚ÇÌûxž¹]Žšê½@¢ÑLòëCÃH3v…ê¨k­•ìL. ½^Øà2\ Wà é;C¶åIÉ€W¤È‰;0‡*c`ærãL.Jƒ;r·“ì, qúôþ=…ÉC ±Â¦þ?¼ \Ѥ"ì&xoÔ³ ‡ …ÅÈH¤/Ü/M¦©:@ „ý ·m$CJjׯ2ÓÔf8ÇoF³yi Fh¿J …@ètr·'Þ †éÓïŒï;~Ô·Y<ùþBÊá—•/1>Λ’¯‹3Õû+½o*'#`w¹¼å•µ¾ªZ»ž'ƒ_Ïý¿)p»<4\ðýåÅŸõa—•h`"Å$ÉL’ørÖˆÿèÄ×µŒü…žÆbÿ:þ@®ó²ØŸíܬ®hm7€!!dF&+ &V˜ðe­IT@&„á ûRHÁàvjË¢}­­?ØóR’ ÚŒx²’ ¶Å€JÄ«Hìv;¹I0@€$G©^ÑÈŠ6!wLöÚÄ®I.ÛÄɶZÙ\~=úÅÆ÷P¤!¤ÇH([î5£Õ£¡FdàšÄÌ/}`IÈ‚lò"&H'Úò úŽ=p×%F¢RõB@!6M.©àV›øÎoHJIý«×çë1b@/ß„‘hô ^ºø8^¦RkÆ|ÉA{óÎÑÖýdz÷å}NŸ­ÿîןüÅ#/ýö×Ëø¶fÕךZ:á³r²Ã×,ÿ®±¨Ÿ«ÄÄ-&V Ø<‰²[l3l ê'M9©E£[“¡œtã,qL$´%IL,R’“…ô#Ú‹ PN¨ÑhCã24l4µ¾æÑ.iØu\„‹}=±> BêSOÐ42 •Œ¹ß_¦(O›¸¥ô¹@ì d¤Ž‰K-OÑe{ÜõÄO#-Ï¢l¹I逴A€ý„Pm°Š#>ž¥0qVA.d¹ÁvÜgŸ£ÿÚ¦Ô£’B@!Ð::Š\௓ê7ÿ×~úÝ8«í×=³R|÷Ý0‰÷ÉQÝ­×Kžf’F“F À¦;zº€þ³lsö¹¢²%<ý«ŸücÞ/ÿÈ€`t9)†˜€ø Õª ŸM~k«# þÆ„¤Ùh“œûãË©5$CNRr2ƒª_Ñb„ñ l,X‰…P‹ø¥í³*ANÖØË¯y³‡113‰ðÄùÉ„$Øk’&l˜HdY  ¿R Ô<#áYH,@,âãÙ¶v0¼zG’—'–ˆrç"æIÃÏÑ_¾ šôA#Eºhª ‹”Z°Ä à Ò!ÚÀ}”Ò”%‰xDàuÑPõB@!Ðj:‚\HbÁ+ÌÉòå'~ü‹_Þß÷ଫõf0¥RÛ"ÀäžüÚm†W¯óm;xâ÷?õŒîŸóŸù_®µË LêHrb\èÛy’6r‚ƒø^û*o˜ÜÄÃü#ë““7Ú!W¯@µŒ ±A‚û˜ìÚ3NÜZ; ~µÀƒÉ„F,:5ãN´e i“¶6yk“ºF6p„ ¢ XãZ œKµËî'™¸û IÇ#~‚!p¬'.(WÇIµ0ãÉø‰1…ݰª O$ÙNÙgqQý£PDŽ xs‚XÄÝùÐÜ›ã’~ bñÈìk´¿þ¨wQ 8ù{‚ñÛ¯þèéƒ/ÿ~Þ§œW‹ì¡½&&&žŒ0 i“”æï&9©û‡* Pì/¿œ> CF ~®õ“¸ôÆdˆIõ‚`ˆU øÂæk¸Žûò+:䊢œQ#¹Ñ¼ZBU¡ Iº°i `r–xk{Mf¢ )dA’ŠRç¡ä“ 99Y~b!»(±‘v Ø$É{I*@”ä3² µW(Úö&xk¡NKŸ>CÒ²zæþo¯ìT!±hŸîªZ#ÀÒ"]~i¹7¿X·ð替<ì“O^‡ûÅ.gƒ‰N|ñòŽ¥ô“&:)öÇ—ºX«À“ªœDãʹ²å՘贉+`@¨­lÀDˆvȉ0”rÛ+p L‹Æ{™ýEj¼—÷õ¼²CJFLBýbdª()I`í°IOKɺüXÖŸT5‰±D¬”z|±¼'Û!ëW{…€B ýhOr7t0œ·N¾å–¹üÍ“  ¥ i¿o\°¿ÿÆI†ÿyõÓY#û?NŸÐï8>G>I?ÃçòKVÚÀîB ^© ©/÷cx­ñsê\! hÚ“\ø¥ÜM[rzÚ×G Êõ)ãÍöôÆ5Âc±ÿعÇùÞxÃÌÚåÔ#²ß˜ˆ´/\©·‡×JLr FŠ’TȽ|6œ=êA’ö˜¿¬ežpÊí yÑoŒLÀ›™`”¦’Ä÷»+~Ma£®+: íE.¥qSoº}¿2°ÜT¥Î…Ž—¨fßûèãßYø—ܪ.i܈¶œ¤0á#É NæÁykSàä'ëkm™]ñy‰„¬ñèŠýU}RtuÚ›\ÀÓšÙ+w:¿L|ðcÑÕŽ•þñXà+П˜x=·y ošÏåXé@Ú)'¸(¥Šˆ‰#æFø¤zL! èL´¹À§¡´·ˆ3™-½­û^P䢓üàÞPˆ¨¥ƬËvv¸U3š@``îŒ&î¨Ë …@,! ÉƒÛ¾Å¨DÆœƒÉ˜•’lSÄ¢íq«v³Î«üt=ø!ŒU{ý6Âj£Ê¬P(ö˜@@"°Ar!{*ŒçX!]š\8xic¬%Œ ë¼a¦±’ãkÝPíU( F ½Õ"õ# ¡¢ÜÏ^XD¥åU¢Dþz§ÞÙitÇ5ã(—÷H§óKiÞK‹éé‡gQŸÍs ®ÿâo‹hÌà>tÏL¶Oåäb÷Èo-ÛBŽŸ£ •5”–œ \nß1}½¿r;-Û¸Wäƒ ¢otšsÃDê‘‘"®uš|~"ØijÓt[5D! P(¢‡@{ ù,¥6ÂïtR‹«Æ¦GÐ…ŠZ½ý=ûÏÅì"ûê×33$´±²àﯡ“çKé›&Sïœ4*()¿èY—ÇﻞΗÑÒ/vÑâµ;鳯½(OGŸp?Ç Ç­_2ÑÑRõÇ ëvc%4ÑÕc3mV U(.E =Èj œ°:åq¢-Žze¥Š}>Ðoþñ!-ú|;ýð¿nºµ WŽ-¢ÝGÎÐWn¹’.ÚGäHgÉE`2²'AYGmƒÞY±•ê8J¤•ÝAw²$Ç«“5K5§«#pºpSWï¢êŸB [ Ð^ä`Ê ûN°Ôþòa}iy½C6vß±sTPZ!OÉîÄjM-.¸ F è)/5»?Ū,}´°‡ÈN˜:ýuBÌT“ …€B ö$ w.›‹€†ùyY&9]!Y—mØ+\7Ëó:Žè(S“Žä„xag!¯5ÞWÕÚé¥ÖÒÉüb*ºPE·M½LxklœO+ …€B –hr“_ÁUµd3_¤²€Š¤±A§üV«ÔÚò´‰½¸iæ¤QlšK)‰¶&òušË19v=Õ…€B@!ÐMhrh1InnDÅܼÿ ÊÍ ¹½2ÓÄj‘C'óiX?¸Š¸4%r¼„¯ÎºúÒêÊEªemݘîîþ»-úî˜!Æ ‚ȵg‚ŠR&å©U"¡ö Èh/ry ÛéÉÊ;¯ô(¡b^’úùæýÉÅã÷Í ¹öˆö¦ìô$zeñ:ºŸW‹ä²qhyu†5¨wvÈ娌퇀œLµˆœ>±äøà‰óbqEM-9`SÓšµ2õó•Ål¤dTf61 —X†,#zNjí×s­¦6ï*.Ÿ,žÊ/ø4‚§#|¤~œ°3›M”"Ô›6‰±ê™!l£:rœ"ì•zL!С(rQÿ†ÝGiÓÞ2êëHo¬`Q[ƒMMkæ# yÝÉäöZiéº=ü»0ÓµWŒ éW '‹Å("¥¶çäÕÞý»s%[#y¬ÕÏŽÕ’/vS’ÍB׎I3& Ä£=Ç©ÕQ(:E.üg¿}w³CÐ‡ÉÆßöµKòüú±‹ŸKK²Ñã÷ÏÑ5±ÄβdºkÆ„M¥ŽE@’ —ËEûÙÙÙ;Ÿm¡J&Vke¦ïæýažìímÒH¯7Žêê†RuíXúh­“Öì8H÷ß0™ÃÝ÷æðãÅÞö«´;²ÿmj8V®qКíûéK7_Åc•Û.ãÔFÝRÅ*Ú E.Új|Ý‹6¨B&òkÝíöÓé¤Uì(í“ ûÈl*¤œ¬%w*ÌÃÏÒb³íâÃ](ŸE/¾ï YSÇÒuG’Ñhh3)FgèøˆuÜÁÆêoﮤ;¯G3y¬¤Z«ãZ¨jVtn¹èÜã£ZäÄêr¹ÙŽÂI+· e›’-~?¥§¿Gz]ûÇ™ÉÉ^@¥¥³YŠ¡™v\7a™LÑW“tÆþGaXÛ­ˆÀ±z%WËv83')‚Ñn *ŠIÚ^“°¨Fw%  ±°³_’݇OÑòͱÈÌx«Cˆ…ĤmÉ~ÏÑÓ¢ho4Sgí4ûØÖeŽÕ«wÐÞ£g„ÝN[׫ÊWÄ*Š\ÄêÈ©v‡„&V¨B ±¨¨¬¦8¦ T!Xt–„¶˜LEôîg[©†­¡½Ñ"±ÐÿÎ2¡´CŽÕ›Ë6’ÝîŠÚ8…R·Ê£ˆ%¹ˆ¥ÑRm ¨°"6uuvZ·ó(U×¹)5eI‡J,w_Åi)‹©²Ö)‚桽h7Úßš+ýoMÛûY9VÕZ¹í@TÆ©½û êS´Š\´ʪŽA@ûj×ì,jØoŶ#§Åªö0Þ ·ÃhV¬lØ{THYÜnw«¿Šc©ÿáâÕ‘ùåX­å%ÌѧŽì‹ª[!ÐVÄ4¹xåvúæ³ÿ¦å›ö]„Ïßß]Åþ&6\tMt/ü_í¼äÔnwЉsÅ'ÆC ñ»;-h[ KVNr[ÜîÖH/b±ÿv`‚4 cUÅ’&ü®Z3NAŠV—]˜&øXÊÆp•ì S%…€D@›\=l é*‘c< ÀAüXtÖ„¶¡‡NˆvÃ6úIŠÅþGÒÏŽzFŽ<º¶fœ:ªýª^…@[#óKQá¢÷\q9½¿j{иð”ùÊâõtc~èõ:;¸7Íž1žâ,&Ú}ä ;Ç9$\2¯`éü `{mƒ>å¨ ñº~ò(š8r€‹ªß^¾…vòŠäÁKoàûH{ž”³E¨wvÝ|Õcäç/,¢»¯›Àåí®Å¯Ö`÷à E§9ìúÛ+¶Ð™Â ”–DWŽDÓÇå!"ë»ìài‹òá7cˆþtÏÌ "~ISmªP à¥ït²ä‚W‰T0ù4èkù7мƒ¬œôÑôµ[–Š2<•UŸ¦Mû^ }Ç#7í—s/ÝÍÒˆêfGmC˪jD»ã,šag$ε"í³ ŒðfGba“[|LŽUiEµøïHƩŊT…@Œ"ó’ |¡Ý;sÀ^w1Y¸ÿÆÉ›‰¾aw—9‘R9jé;+ܯÝq”öæãr¦Ñ¬i—Ó{Ÿo£â²JÖûèO7 ·áOÒ‡«·Ó–ýÇÙ¸çºñ~âMµA<¤þñ#€1±¹p°·ÔÞô† ÿý–>\ûmzgÕCLÑ“æñ “È"Øôfš=}!%Æç´T¥¸6VòŠ´í—¼Bz8 SkûPTÔ; Ó¨u QAzC%UÔÔµjœ©N]˜'n¶ª¿lh_!%xkùæ ‡79ÂiFJõâ€bû»(ßí×\Γ|švÅP!††ÔaÜð¾4sòHŽ7QG%Ú¤´/žžHCúdSNF2•SB"‚O±$ÂÂNfNAYi‰þ:ÆïGW_>„n›v™Zu„¥(H)Iñì™q8Ç0I¤1ìÚÃ_?‡Nˆ{›÷£+†÷§[¸-Xrr“¤¦Ú nª œR-ƒ;¬¾p¹øËRç ¡ Õ'èdþ:Z±åd2ÆÓÐ>7‹gÓ“Ò7¼E?¸ÿ =zÇ*Þw–¿Ì'ͧˇ|™¦{оwß^‚Äâþë_gb’@Ì|“›Ý²Úèrz„¡ ·£/á¤hô?œúBÍÛQ˜†Ú¾póéuþm¹ýj‘pÇ)ÜúT~…@,!ójù}ß “è¿ÿñ!­çdVÈTQ]Ko|²‰Ž-¤”Äx*«¬%›µ!æòA”P½wÎÅçNvÀ„‰ÿÈé.§ˆ¶8)òãŸÂÒJq<÷îkéõO6Я~À*Œôå[¦ð¤¢ÁÛ#3ÅŸ¡ÙOpôU$¨R>Y¿› /TúɈÛã~ªkÔ§¾òá–Ú ó©=‰•˜œ±Áo³ˆ`H * ½ÞH÷Îx™WsTÒ'Bý{]CwL{Ê>>I¥{É—NSÇþˆ—»Ñú=¤âŠ#tàćÔ;k­Þ9ŸU3“Ú¦„ß´l;Äí‘$©imÿ#©»¥g:Ó–Úé}9V‘ŽS¤õªç˜'`Là×p”ÉØöb@¯,y™Ã§ 3¥ô›oÝ#ì,~ÿÊ'þ{òÀd2ÈC±7è.è8¨T,Ó¯4Y?Íüô×ogâqœ^ãȨ©ëâÙ~C TVX/ù@áEL$zr[1×ÁÞbxÿôã¯ÞÂ;ýäOo‰úaÏaæ6Aå˜ZjC`Þî|Œ¾¶OÒ^ò2Á—ZLóCþ½$SÏŒËqÈ;»’IÂÇ݇Þ\q*ØHO-¦!½o¤Ñçˆ<Àê“×>˜Íd¤F AaÙ~±Ï/ÝÃ*²bÅH°6d¦&ÉGÕ¾ùÒÇ>Ü´v÷ó¬òºØJY•95=qsìE¦'ö§cçWù‹w»›_âÏØÌA´&¬Öô¿™æE|«#1¸Ñ!<Éï+„bU…@L#Óä⮚Ú!p°*›LƒzgÓo¾}·˜Ø±ü40ÁP›Lüýg_“§¬¾H½è$â±{¯¢æ:^}`³Æñ2Q-û/çÞ)–‰êù‹ËL‘äKgÄ€ž¼Lv*ëê]B5£=AtëÕcéF^Ê ‘·|FÞKON Ÿ|íV~FóÔ"ƒÔ\ä³jß6@JQÃö“F~“Ví˜ÇFÂ×òxäPÞÙÏ›¬°¬òÿ¼Ô'{ÕØK„½ÎUÒP˜ª_‚B k"Ð¥Ô"Í QcbÑ\Þ–î`’—ÄBæ‡4¢1IÀ=éDÞ`m€}E°gd™³‘$±×°oª yºë±$uÑî¿Ûc§·V>H©IýhîkiúåOÑú½¢£g4éR°úêå´ãð+4s¯è‰{wŠ•#Áò5w-Üþ„›¿¹ºÛú^GaÚýŠ%ÜÛ¢ÿªL…@ 1-¹ìHg=ö F;k[»R»ð¢oØBïV}<÷ª¶ì7ØSEÐ?ß H‚f´Ù ryoÍ7‚=B+¶þB¬ýC¨j“†¶7”´ð&.>V¨‰R#»ÜY0¬õ-?%±n9§Ê¡è>(rцc uǼÇïmÃTÑ…@KÞ6·ËåVîécÒø\aÚu®ˆ]ºZ$v‡Hµ\! P(±…€"±5^ªµ …€B@! èô(rÆÁçÄÿ¨9ºÚÀž@ç½´8Œ§UV…€B@! Pt¹cœa¸‡H8V.ÃOeU( nƒ€2è¬êýÇÎÒ;Rnv*Y-ÊNO¢Q{ ·Ü+·`÷Îåì×àâÈ–p¾bó~B ²!ýzеìKKA<öqôÔM{ó(Îl¢›¯ÃqDÎP2GY=rª€ê8òéuL­ONz·ù¡©Ž* …@÷A@I.x¬áÎ{!{ÃÉd鑎 H ­¢³…h<#Û®¿Ó©ü*ãêSƦåìiS†R_¹õ ˆ2†=j"Ü{"ûÄ8Én¿_[ºA—Ì´$šÿÒ&¡Gé ¬W+º":úæ\IWù ë{k»t±`TR(¢€"Œéãçi{眊°èS/£4öމwÝÇωdWpØt- Lpõ}Ï̉4nX_Î’‹£,•@Úuø4M?œ&Àn½‡‘…¥HCúæÐÕ—iu¤r„ÖC'µüâ¦ú§M°³«cçVr—óa—oЛYZ5#ìçºÊݵž®÷Óº“šØ2S/þûéÁ€L#ûßÁQgÿÍnü3Xz˜p'ú‡c8ØÜ·ïÞL?¸ÿ }ó®u4qÄ#ѯD•¨èÆ(µ~eMð¸)) VqXSgÐ’lqâ!Ûâyèëû›8¼:‚™!]à`i‰6K`Vq,C°ã^7«¹|•¢@­ýí=¶”vúˆŽžÜNUì#Õôuj%Z8¤ú5—=ÉÁîV†ö@7ÎuÕØïSyåIäw(bæ^'¢Í.ßòóˆËõÁ\Žp»~ÏŸèøùÕ4zÀ=ÂÛêþ°{÷’P‹Pù fPä‚ÁA”Ò×í⨦²;tº€Õ s$=%‘¿Æ’Xr’%C…D"ËÆî¿å½1˜l;GIE€²:»‹#¤j0ç. ŠªZªâzΗ±´¤‡|Dí[‰@­½”Ÿþ”qô“ù›Èåô’~«"spI¹™ãé¦Ió™¦Ñc³7°k5-Ûü4ú ö0%X³ètáFÚ°ï/<–;Ù½»^|¯Úþš2ê;"4û¡ÓKù™Ÿ3Auµ²wÿøàÞ×Ó¸!r•ÏX"÷ÇH©b·æ/ÓÎ#¯ÑU£¿K#ûÝ.¢ÁŽt/­ØòÿD¼•ôäAtÓäç('mî´|óÏè\ÉBYcÞO»Ž¾&Ü¢#6‹ÝYNƒzÍÞï¯ù¦¼™ž¡¬”TVu‚öä½EÛ¹N$£1Ž®»â"ä=bµ8õ­Üößd4Äq™ÏÐÐ>7³7Tm;ôm>ðwñLà?oü±ÿtÇÑWiêe?¢ž—³+÷þëê@! ˆE.»1ƒré/3ýù_ße2‘H©šZ°Þ}ÝôÞÊí´ä‹4yÔÀ¾–U"®ÞNOþùm­î¥§ºM<—“‘B/}ô‡P¿@·²úäE¥È$§ 7‰a(Mº»äÿÉJBµDœ ý«´¼æ,.ÚB=ÓÇÒ’õ? :ÇÑP¨WÖìœO5çÄ×îÕ£¿Oos¼¤d[.Íÿ+Z·ç, ëÁ“îãâËøð©OÄýpþwW¹…óœÌ+ŸÕöòjä{“!žú÷¼† ”“yÿ5 ìu]ÏñRðµôÜgtۇ(ïÜç´7ï*­<**ºyÊïXµXAÿYñ%ºœIÙ “ž¥-½™´²¦²ÑôHñü‘ÓŸ0sÓàÜëÉõ»ÿÈdä8«èð©¥´œÉ î]7á—"¿ÝY!$J#ûßI›öÿÊ«N“Û«I/ò%&)3x̾dzè–)ÏÓá3Ÿp-¢m0úe_%`~éž`·[¼&±n1£Ê èF(rÁƒ ÅWg]M_ºy²…þÛ/õÿ.Ú—C÷å—W »ïÆIâÞUl;M¦ÿºõJyHYi‰ôèìéü%ç&ƒ¾!JjŒ$º÷úIB}‚ë*…€$‡O-¡S,9a/ûÜÌ+˜L§t몬pÓç{NQµK3Ò ¥ÖêÚ–0å“+y)Úì_îH ñÙ,áÚÀ_¼?R ™á ·k×Ñ7Å逞ө/O\á ŸÏ“ïÅ¿ ô'œ,?~n^O¸Š¡Kk]»ëy:‘Ï™%£Þ˘SÄ×¾Çë¤Êê³~¬¬–ê•1ŽÞ[ó(Õ8Jiß±wèþëߤä„Þ¢PHVoŸON~诤ŽIC£Ì_Üo=øÇq± ‰ÉÔ±?¤~=®béÔÇ4jÀlÖöþÅÿ<0é9ÊcTÊäDly4¬Ï-‚„\”±þ$‘I $›,`Éea°,M^ÃX™ÙîJ¦`¸Ë{j¯èn(r0âˆj,áÝÞ\äÒ`ÏàZ E`E,Ñhù8BAy¶7ëчò*ƒ¡¹7“^—D•UÕTz¡ŒÉEÅñ$PY—Üre-ä€ãp®+<—ØãØçóˆ'K*ŽøK(.?È¢öËüç¡xoÞ–eÛ`LaéOZâV‹œåôþ[´ƒÏ {ýeã@¯72™›,TZÃûÍòßKK.q4›Ø€ôe:[´Öî|ÞŸ?ÔŒU<˜¨¤P\Š@ðÙôÒ|ÝêÊ”1ƒxÉhZTûŒe©M‘¨VÔe «b}þëüÕß´„"P ïs+Ùâ³Dï!eªccY|IÊ-Ψ'/ž%Pq< i"ôp¡ÊICSF~‹þóٗخcÛ^<$l ;'²ÔÄþT\~8äjÐ67žl“¿Ý!?$£ì;öÑè?ª5›ÃU#Z¬50èw‡8…MDÓI'$ 'ÎA¯}:›'ñtzüží"»Çã`u£íZú_ô8T+n“vy…Vï˜ѽÆ' (÷ÎøÕÚKè£uóíðŒsäX%Å[ÄX5._+º;Š\ùL74ÈÕÖ]ÂRV•šGàò)Æ´±¼·æö{£“γ1d°~ƒ„"P4.Ù?±²DCÏ:ô /­cÒ1”l¶Ý³=¿À"õqCþ‹ý”ôàI«Žm*zŠ|P—@¼?š—3j©Am1$÷vªö± :Zì©W‘­ ÑE´ÍçÓQÏ´nu1j”-¤Óhô?¤Šê3•0V=Ø ÒLÔ犷óÄ}®dcÏÏÙF£Æ^L}XªÃØP’‘'[\&«* XUi¥I#»è1Žaýn¡çWQ5—’ЇÕ3ËéÈ™el\z__Ãê•-£R„¹—’IÓ]ÓþN© }ié†RNúhQ6T2°õ%ɱꛓ"J*B –Pä"–F« ¶õ™l™WÝ“ÉwÑä¹’1Y'p®QOC#ò!ÿÄZ¯ÎÒ³'Õ›‰m|Þð#Úsìm^ñ1—`Ô HX1òâ¹é⸥ ™€±æ¤‘Ñ•c¾Ë+S^£ü’RøÙÖ_ÒíÓþ±~]µïø"A.Vnÿ5Ý<ùyºæŒƒžWµT »@r‘œKP¹ Éçq|ˆG?X{1‰Áõ` ce5ë©Gz"×Ó€y°¼êšB ;" ÈEwõîóó¯$d¹êjïâ/Ü{IW;Ýg¡à³‹¡Ö÷6a'U¡6Ò “ lpÉnd{š&g*‘ÝÞ—ââNµX|¼üÉíÇÆ‰˜¤`<úÆò9LlbÙ% ÀRL$LdHøb^²þûõyªÅµPþA›êêÑÐI¢½²íèG$)ýÿÛûWù«†áe ñ% 8Ÿ{µÿþ‘ÓËèÈéåŒU2¯)×aûâ‡×ðªDqŽå«HËùŸ·>û²<{ø¡Ø´ÿï÷ÆËy+x5Ï«ŸÜÉRxakñA‚´dÑê‡ùš‰mWù¼Œ¯^ôÃ+GÛ. ã9VW ÊòÿÆ"§0ªUY1…€"15\±ÛØ@Bá²×Lç×½ ÞûÞºÚ_U}Mfnöm9_¾ñ¾ˆ:,¿Ü5ba$³ÙÌ›‰rS㨠º’JËfQœlôé ©|9YÊÌÎz{y¸çXN>&¡ ¯ÏD¸M£†ôHíBûe_ëhéX>­þ·T_Ã}ŸŸX4\#á#ð<œcØW4—\îÚ ·AF@4¢äXYÍ:3 ‡ ­‘ŽS´Û¦ÊSt&¹èL£ÑÅÚ*¡à¹xce¹îã•Kõë·~á6}剉ÎÍNÎi ðœŠÀr ‹™¿žÍ¼äØB}’Œt¬<‹JKgSfÆ[­©â’g+kÏ““m3ÂMh‹Ó•E—å&Š6¢½h7Ú/=À†[fGô?Ü6Æb~9VÓFô x6ælí8Ç@Q‹Ü‚gQWE.:ùÅZóB%¬§^¯óéÞöRÜ¢Ÿ}³–eÚ¾$öÄÎýÍq{ÜìØÔ…—«&ˆ©ÀË?ޣܯYãÈÊ[V’•jœÕTP;’¨ø>JÏx/d FsÍ€Ê䯋&7—å’{ø ÆdUÃmé—jf¿¼ •Ûˆö¢Ýø"ŽTÜÞÞý¿¤s]ìBàXè•Ìþ6Ò¢2Na²;]>^í¤-µÑnâï@%…@Ì! ÈEÌ Yçk°ŸPè|sXåq ¿ ƒª<$¡0˜­ïþ䡚íã¬FÖÀ^¹ì޲òŠšV‘ ¨09›L&A*l¶xrp¨{‡ÃA¹nù¼uTX7‚\…”–²$$ŒÀ6¶öz{¨g\,±èl A9I”ÀmD;A‚Ðn)n¤®ÎÞÿHúÔQÏ`¬ ¶‚tiH¶F÷ËŠÚ85îSyeÏãv7¾®Î±†€"±6b¤½A Å¥ßX^nî:=éÞ¹˜P~˜ù;„§ÅVW[SRUçÐÕ2ˆgUF$I›\õlXÉ’ .#Áfcbáä ÂÅK=Ô‹}a˜+í”_—AE³:"óìæ‰ý0K "óƒÑR;áK±ÒÆ›&½—¥ê)—%‰‰ dK°‰v¢½h7·¡‘¤ÎØÿHúÑQÏ4+3¯4Ó+ÝžgDuœû‡ß;~÷.‡ã<_¿ô¯)0³:Vtr¹ Óù¥´ÿø9*­¨f‹òZr:5ƒ¿Öþ•cz€˜;%!ž—ÚhäÀ\êÓ€ØJm@($~bÁ¼gŽÞÕ«ÿÀûöæaëe¦p÷P À0ö¸˜Xx<áÊeá~¢ÙNEµn*s°Ã+û žÌ}¬&©%ƒ±‚÷Îp« šßë3“ÇL^vâ?F‡²­>ÊM‰£D&Éɉ””Ä·íE»#U‰ÈFt–þËöÄÂþ’±b˜›d ™I”Êq‡Úbœ$.ø½ûøRxöô:¾&ÿ&ämµWÄÝž\à+våÖƒôù–½TQí“‹Q_Gzž\tÔ¼¥z8#í# yy‚q{­ôáš”ÄN®›8Šfp3„k﬩ …ì²|‰bÚž½[7æM¼îÆŠ­û'M5 ²Ow.ÈÿõΓµ×çÄ‚w"i/«MŒ0ø´SÇ©vðæÒ‘“% nOœ ZîÖýkdÂb5èÈÌU²Å@‰V3ëëã˜HX…ÄVJr²8†J«[Z#µ­í,ý—í e_^}ÚŸ ޱÚ;™XIÏJ=øIµØÑ–­ÍÇIö‘ïÌ-|V/y.Mÿ.dµWÄ wVk÷ãµòK×±”ÂÎâð<ÊLo[±8º(n}¥ƒVmÙO_¹õ*!͈Tm¨fÞaNŸr­ûk¼ï¿ Eý¤PWÈ* zÆ—è+öhMaÀó‡(K Þ»±Î;ºŒ?Í9Êaê÷‰|áˆ$–ølZ' i€=T6ž„«pØbÄ3ÁH÷j’ ¯WËË/ûÀv†u,Ç+7Ѓ6 ,Iá:A"„O^Xµˆ_j¹!gãvdÿ·%”óåy/ù³M˜øßþã¶<è ã„ßù¾¼³º’üóor_åßþ.TRÄ$Ý–\|¶y?-ú|+%QNÖâv3èƒ>î§±ÁP¬¬|½ð¶fÏÏ’Œ­…Gú+Dp0/­¡ÜÞ«‡ ìÚÉsª!ȼ2¡lG)|•?Þ¯àk^¯?Æì /T è"œë?þhyŸƒnxóÓMI?}h–ÞlÒlDù^Ø ¬”a"ÑÎë—¨²}ƒÉ…É…‹É…Ûíf ‡W¨NZC,d#e}D@ÕvÈÕ+PÀ$>Þ*6¨Cpí‹fêÈþ‡Ûk|é)­4×PZóG9NN—‡øwîåe"ek?|äø{Í7^ÝUtBº¹À„ñ9K ÞýlÇØOééÑYŠÉØÂKdvö±qÑç,ÕàåŒ3'Ž“‹üšŠ¤ÜPŸÑ¢~Ân—·Ö>¦<×&5" H+æÎ=/<ùôÖ÷t¾Ú_­Z5ݸõÈÆ¾k¿6áÝêÕ§²Ÿäö¢I, ‹rÔÖVUïX¿úý53¿óÊâu¾Gf_Ó*õˆœ`Å$ÂC“ ˜)ÞjÄB{:ÂØÓ[o—!ÉE#<¸y-'i‡)'-¸à†§P¨<àÇÒ o‚Tà:î£Ñ{Ùô¹½úß2BÁsØ4gžâffFÛÛ%u†qÂïû|q9Þµýw.UqçA°ñ÷ $â— þ‰Eº¹ÀdUÈ¢Ï5bm'J‘üà%R´£ä>ú`QvjÜ»M&´ïBÁ„&H‚‰Âõ¶Éb}§aÙhÐUA'*¥ü×ûÏŒ=îÊ^=Œ™'ó]§öo<²ö[>ò}‹eÏ’Î7†œŽxLÆò„‡FàK /U,Õ€'ªºëÖìLÍÌú€gÓ{ä{pÖÕºH%˜TåÒNLàr™*&zH,`¼«I-Vn;@7N­‰°#üš •PôΚHÃúÞ*¶ïn§²²SEËÞ_¶Ÿy ³Ù®"Å¢·}Pá-îÃk5–ë|¾AOÍõþÞ™Ï.´½GÞÚÿsq¸ `Ê/tß¼õoð¸Ü˧kyÃËTJ.˜.̼W~ðΧ¥…ã§]ûÀÿ¼úiò¨A¹¾ #èFê‘ L&˜Ø1ÑB) 8ñ‘Ò I*äžÛv’“örCÒÀS^ »àV>€zÛ£ÿ‘43.À ]Û#$9Ø·Å8Á–›bUŒ7½nwåž-þ¶uÕ üþñ›Ç(¹Pä‚Q)6èäŒôVnÛ/V…„³#†íª•µ;MtíCý_´òå×R›"!6këDϰ£Øptí6©óòZç­z4Ã7nZ¥õì3N{ÑiV$>ùpIVŠÌ{Q·–> ã~œD_ô>Ë»^}£Ï÷Ì:Ý3@Ï › |Á™xƒèFè voübÇ‘];ŽN¹ñ–k=÷4~9'1.¾D«Å—’lÓÅ™LÛdp"5&Ïe¾pöÇ®ñy8eµuÞÆým|ÞÖõ£|^ìO_´ÆÜÖÇ¥ñykê‡+{xœ…ƒ,ÆTÇ6=•§ó¯ØðÉÒÅ55•%\voëŠß=~ÿÊÞ‚AP)¶èä_¤'ÏSUS,7íÌC–¿›ŠKÑ nïà¾=ÄW.¾2›JA(Жy t;žžë·áÈÚÇùC¯äé¹Þ'qýÙu”èvþêgzŸæãUn7ûM&z÷ a‹ø{øèSœþtnÝiÎÃä7Wói ô„–„AººK1–òõåCF_Þ¯ïaì i«5Ùh0BÒ¡RŒ#0j ]صçðqy³{&Àn·ËᬳWÔÕV—œ>zxß¡]Ûp@$`¼ bõ8†JDÚ[(©ƒ¡Rì"Ðåɾ¾ µØü,‹=Ù™»wîÌ íC;ž8O}{føÅ×_RaŠ~·Ñ°>·Pk$óÿaÄžñ?{Ô¹ؽýö½†£eïÄ1‹þü4<û¢þ9þ,»¯ìc¡òJÍMÿ¶Ž¼s6A.W\{íj|Qœ1nθþ“ò‰Vã4Pz!‰^²Re‚¯:Î-Göî<ÀD’|F;SÿÆÏ.ÐýŸlô›/üáò8÷’à7 ¿aØRà7 !Õ X€X(©ƒ R×A › •–W³!{Þl£¸ÑúI }}­pAwÕ08„]@ã>°l4è* Š(ŠÀþè<žÎóæoZ§<ùhÝÙ²²õžÅÅ2SÞ`÷sNÿÍA©Ã1gÎ~þò»”hpÊìEyåï~‡Õ!6ئ š0sSÞÚçwÛ5ç‡_¯;SO,pŠ„‡5¿ëšA›<ÇKúh {B€Žß/$ ŠX0],•Åx@0$±™Æï¿a  üaÃï÷µ?>PI!Ëtyr•V”W×’Þ dçOhgEMUTÑá3èDÁ2:W²™ ƒ½wÚ†P¢ôÓ¹îuó^4üÚí³/úóǃ¦Õ–˜}Nñr¤8SÒj]óyj7ƒX<· 5ÙKÏè úesæ¼ó)Û\ÜJÞÊ\³1>¿^Zñ¼¦^¬á¢cùBÆ‹6ð/_¼„Å*Þ›y F.É``ºH Û°¸“ô[J-äïR ¨<$IÁÑÀ9®ã>þÀå³|¨’B vèäÂÇq"<äpºy…þŽ;BÀ¬Â’£´ðÃﱓ%/™x5\4RmO(£ôô7< ç-Э>{ü/¦¸øŸKÉÅ÷*/Ÿ¿Ðx—×çù7ÛO,dWÁ¶¯ Jº’h?Á˜“Ë:¨½?—Úä9^°x!¾œA60€X H.@(䯇*u b¸øíÊ ¿å@‚ß2öØp/Ø—_VI!›\4eÅfšn5ì-4› ø.Àßyì$‡«†,±jn¶xCq4Í>Ù¼l4Ê*p™2dÚ÷6]³Âå¨ý7b]‘žzÔ½šúÍ{91ýé¯V•j-Þ¯ÝlÝ¿òK{ùå'¥ ’X(›‹ÖáÜYŸæßRÌ&ù‘¿aI0$Éçò~ÌvT5\! .M.Ða©‰5r.„ðÜ=ÓÇј¡·Ò˜A·SB|V°1l·kPkü~Aâ½N_õ,'8ѸbX4¾Úês¼¤å—Ô[K#N)­ÀIîµ3õo¬#cÇXM’\`/7 ¹Ék±Ú?Õn…@³tir!%Bhc’‹Äøºÿº·)·ç@JäÈ™ˆAÑÒæV•0Á˜zÅà+xùÜêöl’|ãåŒ$‰…v¦ˆ…Ä¡+ía—Ë ¿Y™äïWž«½B K#Ð¥ÉFÎO0bl­æÿÏÞyÀGQ¦ÿ™Ù’²é$BïÒ¤"ìõì§õäôÎFó·JÛ_ï΂੧ž'*‚JïH‘Þ ¡†„ô¶}þÏ3Ë„MHÂ&ÙÝly^>ÃÎÎμå;Ù}ŸyÞ§Dcr«èªþ{S÷IÀð°`QÛðùǺ6*þuŒ4V\˜ðAÊZµvÝù.ûÚ’Lž9QÛâ‹}wþÎð™‚@IDATL€ 0&àB¸hÊKކ±&<“ã )ýäk™`L€ x ¿.šúÔ¯µpϵŸÙ?4wiêXš»ÿÜ>`L€ €±¹hè-Õ¨C`ìè¯e­ÅC×VÉ s~ŠÑ3µp˰÷ ¹Õp\²°BzæjX·{&˜ÌepÓàÙPV‘ 1‘1múõeó,Ý< KO7´yù|(”­QðEL€ 0&Àš€_k.šÂÓj³ÀáS?ËU¬ß3~Ùü¢¼oµQ€«°xãDøeˋнýrîúPÜ®éû”UæÂªíÓ 3ŽŽè÷Š|ÿǘ`L Pø½æ¢±7Òf3CN¡=Tvþ~ÔB¯ªê#ŸaÂ.¢Â’ ·ð´O¼öŸX ^VqÖíš)ïS²°Á=ž©ºŽw˜`L€ .x—uÈjÌ YЦå@(EA"<´5Œ”1Ù^òŠÓ•]YðEmF¤® æ ɬ:Î;L€ 0&Àü™  ¼»ƒº= ­Zô„9? “í,¾iQµZ ½…R¢#ÚƒÑ\ %YÊ!~eL€ 0&à÷Øæ¢ž[\Xr *m˜Óc0´Š’—BÂC ¼"s~X }ÂLq>°Z øyŸ”!6ª3ôîødœß&×Qí$~Ø`LÀ °pQÏÍ­ÄåŽÝi_Ãõ߀¿Þ¿GöÙƒïC‚càï…}^‚íháX2óv@ÿ®OÂÓ·¯Æ%“V°íàÇŽó>`L€ 0¿'ÀË"W¸Å«v¼ä-‚N¡`±àlîðéOÃeAƒÜOk–JCüwý½¤À4ワw©æÈø=`L€ 0ç°pá'³åòüIµ ŽU±`áHƒ÷™`L °pá»]a,D» ÎúíB¤\`L€ ø ¿.h²§M£V¡¦Ö­·hù¶W]R¿$F%ÊýVúï’Š¹&À˜` à׎Z]°lÖH mzVkkUÕ*rKµø `L€ 0/#à×Â…#ëˆÐ °ÚBÁf v<ìuûÔ?ê§Në÷J%¯cÏbL€ 0×႞úÛÅGc¼ *+»¸†œ›j¡þQ?ã£uUË"njŠ«eL€ 0&à~/\(6 -£Â D+bÖÒ>néªJ©¤´hh-†«Àr=L€ 0&à1~-\(‚9p¨Ð@²k›XÔ\¤€ÁÐÎc€Òõ‹ú×.F¢J%÷™ú®Œ£!uñ¹L€ 0&Àš‹€_ UEœ¤i¢VA÷vq¬  ðvôÑ4óZÛ¥þP¿´* :´Œ5z·(ý¦1paL€ 0&à+üzÖRžøi’V«Õ¶;ú%Çb±–ŸWÝ#êõ«Slk@«ÕÊ}¦¾+ãðªsg˜`L€ ÔAÀ¯… ³(  Æ Z«Õ@PÚÆEB§–:L>Öò.<Ðì ÒXP?¨?mÂEh¥ÃÐáZ¹¯Ôgê; `L€ 0_!à÷þŽÊ²MÔÁAA ¢Àl6ÃéÂ`¶ÄBLÔ/¨-8ãñ{F6ù¸b6ÇA|¨m£C ûG}¤¾RŸIsÁË"¿5Ü `L€ 4€ß ´¤@´F£‘'n.Œ#¤ÄG€V,‚S…qp>÷)\29aº}xNNæ†& ­ÿRŠcAî¦eå} ÒjÁ mC­¨±Àغ콆Ê}¥>+Ë"õ×ÊŸ2&À˜ð"\ˆ˜Å5hs¦ÓÑhBû3$X¬ S—Bf‘ Œí!'{A@*@¥.ÆW“Ëî…·Z"q&TŽc¡B¡"Fm‚¸…‰`¹_ááa  ÓÉûÔWê3y¹€Ä… 0&À˜€¯ð{á‚n-+A'Ù2V€–D¬V+Fë´É÷‰&ð–•(5˜¡Ì `²¡ €Æ•ÒÅ»();ß7ô…dk£Ì&jXÂ4„ × -{Ð2 <‘‘á ØGê+õ™—DJ›ÏgL€ 0æ&Â…}iµ8aÛPR ÁBì‚.›¨ÉCÃf X,Yð áCRNlâ¢>P[Š £Ñ¨Ñ®"‰  Q‘‘ò> ÔWÖZ4:_Θ`ÍB „ "«A¸Ÿ 0&À¼@À Ê  Iœ&}9‚'Mö¸<"/… §]°°}*%M.¨M{{v0TØ> ò> J”þñ+`L€ 0_%°ÂÝ0E‹A;-W& 8/GES…‹* †,Ð0a7ðTŽûê÷› 0&À˜@M-\(0” ž„ *ŽB…ò^þ ‘ÿQýJQÚRÞó+`L€ 0#ÀÂE-w”€Z ð!&À˜`N`G'AñiL€ 0&À˜€sX¸pŽŸÅ˜`L€ 8I€… 'AñiL€ 0&À˜€sX¸pŽŸÅ˜`L€ 8I€… 'AñiL€ 0&À˜€sX¸pŽŸÅ˜`L€ 8I€… 'AñiL€ 0&À˜€sX¸pŽŸÅ˜`L€ 8I€… 'AñiL€ 0&À˜€sX¸pŽŸÅ˜`L€ 8I€… 'AñiL€ 0&À˜€s|6·HFv>:yò‹Ë ¨´L&³‡âO³Á`“ÙÌÚ‹Sô¹ HˆP´Á1-[ÆƱð¹xc‡‰#ñÔiɰ“8o.L€ \$à…Â…ò‹ÊäÈ›M ¬•Ã;ªÿÏæB¥Ñyëøc9ðÛ–nÿc!!æ›_·B ÚF¸£Ç0ŒdZˆaÒiiÄ, ìšêÖ^T§"\PÈËQ­N 6WH¿KÓ GäIÆKɸ±pÑt¤\ƒŸðª Z´$BñŠÊ*äÞMaܺP@©¸¥´‰“«ÚŸž)kC(’¥cÙuä ¬Ù‘•8'©Ü3ª/ä–ÂO¨ù(7˜à­¯–C<~÷¨~p.·HvïÌÊ+‚ب0ؽ éÝQ®îj~ÂØå•&ù8…$W„ ­Þégó0 @÷ö pó°ž âðÅÒ-²Öâ³Å›ä€W¯=>¦Þvûîì~†H/­0€Áˆš ôÄ ²vrp-g úÜyôà@“Ù[«4ê–¡æ2~ºvámDžb©6ºõEÎÄ›-¥]È—«òm^¥¹°Ù$° paÄ妿 A“EèÖ¡5H?Wu‡ö?=1—c))7ÀœôõH†Go½ÎçÃöƒ§ :"’[@Dxü~ý¥ãÕO¹t÷Žž:kw…<´ ‰ÒÉŸÑRNÆùBy?1Ö.(C4¦BWJ .IÐ2Ë™óù‚ Åž­'éZ]í(õ5ê™Pœ ê/mï¢Q}å"zp á⢀!O‚¾Òwßé§]h#Æ^ó æ;ð¸§þLÀk„ ìâÆ}W5jºµÇð×çàðÉ,xð&ò«^‚‚Ô²6âÕÇÉè»þBÝZ‚á´;µÃe‘‘P†6©Ÿÿ&_Ô2†´£€Y[+ I)–D -JÙ¼'²/Ã+`­æü°Aùè²×úÚ¹ìä ª6Y° 8„ÙΜ³¦6¢oœª1ð*i[™ì\ùí$ûˆ@Šêìh7ìtäÓ£}k9;Ť0b‚/2z¥et˜%”–<*а“ìÊðsZ²0¡`îcUUQÆV²“Ø„B¿iïqyF94a¡ÁhÀ)y¡œÁe¥Äâ Mð'³ò Û1ã²E]í(×4æ•ø’¶EŠ*Á¢1õð5>AÀqÒóªï¹OÐkX'Ö »ŠÏf~LÀë~thteé’v ôêÔFŽõP³în(\ŒÒVn= ¯ºf~± Ž\ŒmÑ B[ÅDÂìÿ,‡ù+w‚F­‚‘ý;côÐcðæg¿ÈtÛVÑUUÞ„õD y—œË+¬²Ÿ †ôêˆ^$Fx}îX¹í\Ó·SÕuä6;¤WXºq?¼ùù¯²¡e}íT]؈*ÎŜѾÄý”I^½¿ˆ*ï9Zß7¢‡\ï‘þŠ!¡®hÇ7øºb¤\p’€×,‹8Ù_§N#¯ ¥P~ïTÞ¢ ¦Þ~þžª÷´3j@y£(–ô9yvP¡Ø{­¬M ãTFì #ûu–2Æt,ƒº'À®íÀŒŽšÞ.É -€–^(P-‹Ô,wb›‡öÀœK¥¾vj^Ëï™À• 4Üæ¢çÇK `Ë*ÈúöÃ+VÔº-ˆš`¨ÌH¿â¹u3ü&h7a*\X·Ô%uæ’ã-FÝ LM‹V`ÊÍ„¼e ç—o]R7W˜€Ý’Ü+8¸ZcјAѲFm%5 Ž…l9ê*"Œ‰— Êùµ ÊgZtu,õµãx^Sö½{SúÏ×ÖJÀãOÒ­ïÆìÌ& ‘ý¯’ƒ»àì¿ßªuP®<Öµ/dÿðo(Ù³ZŒ¼ ~ò7/KÑ¥åÊF´çqîè#_ÂI@¥ ˆ¾C@¥ ‡s_¿W]ƒ‚ÁK¿i¡ÀwóP¼{3 KÁpî”<Ƥg¦ƒµ¬Òg< q7ÝIO¿G^ùP]á½CHû.P€Ú‚¢íkA²Z rÀ°¢Ð’½ð30dU¨ж­Faåmˆ0¿ËçS >-®¹rÆœs`3ÛÚÇÝp7Dög>~ÔQ±Ðî™×åú9™urï1$‹*N®óþ€ 0†¨[¿ß°zøl&À€@öü9P¸u dÍÿµ 0@åécòälÊ͆²#»ÁRRªðHëÔ .¬ù ,Å¿~©,Lh[Ú#ä’æ!ë¿ÊöÇAå©4° ö‚®¥:l•å`ÎÏ…Ü_¿Cá!S`•"zÙÝÉcFÜ[WAöŸCÁï( ü±N¦qÕpù\ù3Pvh—,èD_=ºÎ;C6$$å,ùÌyužÇ0&Ð0¬¹h/>› 4ò“ö§{Kq‘ÌAT»·E ”D'ù9}/Zúi—Rõ¾xÏæªýÚvh™$þž?Cpëv`8V>EP  uDTžÂ»÷‡°.}p™æÆªÏ‚“«öwĤLþ'”§í—…%ÇÏxŸ 0¦`á¢iüøj&Pl†Káê8Ù_P¡%‘ŠÕ…€˜acäÏlŠ¾Î‚q_H£P²o¤M …‰hèýÙJùt —@lFh㓪]NK+füÍ]¹µ"ÿªöYÍ7$ ¤LþjJ àÔ?§’!RÍSø=`M ÀË"M€Ç—2&`'P‰v¡hIÚŠQ†ÚZ∿ç) ¥Z‰è7Ôi\4ùk"[€—*èZ2"u,¥û·-wDô¾B’:AÔÀkå‹v¬‡Ão–ãe&ƒ> Š·'¬ºãitøûÛÔ² Úl| ¡º‚®soJhWu ï0&Ð4¥¹X¶å ‹Qî6˜¥ôö½1P–=)YÓPòÕL p \X±Ú¢Áf߯7™OÞ@ÃÎ%pò½WРr:P¼ *ä1rhoõ3u“LÈùù+Y¨ˆ¿ï)ôLùÈ6C)g¿|Ú¿0R¦,Êßð+`‘ùÕûÐnâtè4ýyYÆZ^ 'ß} Œ—Uèä ¸ÖUž)Êõt¼pëj8õ>y©paL ©J¸ XºûÏw ÅÔê%°ú#°jûxäæÁMåÈ×3¿#pð¹;ªÆDF“´)E²˜`÷ýý•·²A%Mî*]zˆËÇÉ0óФ»Ñë#L~Ož TjÖ%ÄÿŽÏxNÙ•_³}çF7R CÖ£7‡c1åeAÚÔ'A ÒPA…´%'Þù›llJíZJÑ6¤Æ’ˆ:öݱ^ÞgLÀ5N¸P¡‘Y|‹Hy£<K7À¨™f9sêŽC§apϰdã>L§/k5ö¤…5;Ž@I™(Ô÷=×õƒ˜ „JNA ,Þ°ÎåB4¦Q§0âN<· ~X·²óŠ¡fN½ëÚ~Ð.>ŠË+á—àĹ\Ç<#”þúAÝê<îš[̵0ÀI\,[T„ ÇcÎî“}E}Åf¨¬õcFHÐà˜@óh›‹³¹Ero­Z &LZv<3¯ß½;%Bß.m1=zæÙíZǽ×]•F3|±d‹œûƒ²ž~¹t T`¾{Fõ…A=Úƒ’õ‡µ»å0âOÝ5\b¨N*”è,¿¤ 5'Ã`æ(!!§¾ãò‡ü`L€ 0#àUš Ê J%£ ¥î(”"u!ÕªîŠË!J)Â4éñ±QÊ[H¸¸_ˆ‚RÚ´¼”•ŽÈÌ“Ãk/XµK9E6ÍÆ%JFf@¡é¿Ë¶CëØHYBK-u¯ªÀ;´¬¦¹ÄX©Ò‘»rŒ_}žMvÕŠÉ`,ª`ᢕ¦½)×è$«Ñâyë2îMk¯f¾KÀk„ G„˜@¬¬Ò€Ë&ybvü¬©ûº {ý%C´šõ©UªªC±‘:ØŸ~®ê}^‘ÝhŒœá‹|<¯¨L¶§PN º˜ñt.‰$¢7JÍr.¯ ïÓ­Ù ÿ[þ‡œ)•£Õv¼æµ}OË0>A\huAª±õñu¾GÀPQžgh/1U¥~;ßç{L šPÁ\’ŸåùÖ¹E&àý¼Î悞¤ÛÅGËÞGOç4+ÁÎíZÉÆ–›÷€üâ2ظ;]^&IŠoIh IiÕ×ïLƒ¼Â2H?› ÙŠðx <'H6¥¥Zj9zÆ>Ž£§ÎCN~©ìúÚ±M(‚H]Ç]5xâHó1: /‰¸ ªoÔC°¼e;ºw„Œðö¾Ñs/ï%q$žçÏžùý"c/ï1w x–€Wi.;€–Qa¤òÔ臆•ÍUÈ–âö}db)zGÈŸÆ %múØÀOh¬ùÞW¢=ƒß<—;¢àQtm]„Fo}½Bîz,ާË#7‘ÓçqL`Acиèp¸yXOù󺎻jÜÔ¦V-@Dˆ#2‹¸èn·má%WöÊzª ìíàÎm']w}ñ‰˜. ^¾>æ•CðÞN!GÉf5løõ' ¾¡°öÞsϘ€‡ xp¡dz¡B›ˆ.‰Ñ°çäy8uî´OŒu –›‡öÚj+dÄ©r:~NK´ÐS$ÇÒ³cÐV^i’µjµ]Dý}ùÑeW:?Xk¿înô*¡Í€Æ•Š€BŸ×uœ>kj!~i¨9I WÉ\E\ö!¾Ž†Mmƒ¯÷:ÊdG¯Ve;{òÄ*U—î÷eë u9kó{׈_Fx’pá\æ÷Ù:ònlµ|ð+^µ,BOÕ*yòSA÷vqŠö ‹7ì•—š›zMÁ±?äQ¢ŽÇI¨P‹êÇk—éÇó»OK2?¡Ç‹V%@B¸û¨Æ„ ûF¼¹ø-ŽL,ÈõÊòûoKW[M†âÍm®µYÄꂲßRpñÀˆñ³YLùÿ°Ð1ñæÂ˜ÀE^3»(š šøh ‚>í¢å@U W_ò¼à;ç<…«wÊüBm EAM‹ÂŽVK‚Z0æÎ×ÈgúåIš„ ,(C˜©¢¢¬r÷- i’®§s¸4q#~Göìþ ¨è¹¡_â¬0o`|:ðOž.”/žòZ+MŠ7AÞ4 aî¶q‘‡Ç3á[tߤ'q.W&@œˆ×þãç .ÈÑ¡jä‰^(XWâKœ•øÕk”{D¯\|—=I+‚¹‡ÈÛÞ-?¸ï—Q)šä›%Ö`8wƒ‰ñ"ngO¤}³eå¯Û.2%Á8³æÂ9”|V€¨]?ïÚÁ_6IQ@ƒÙLÇ«–)Ë"4ùãd ¢ÀŒi”àD™[X wìë2 ×Ó;j# Z ¡Ðä-4&h‰‚E0f• A–Ä“¸_ÒÕ\Á0èÈVî0’ËîÃg¼ëÝ”ešü(O:Åɦ-dÝÏ‹ÖH г﭅ÁQÒ°³D¶Á@2u²± ¥ÒXœ9v仕 ÿ÷#žJ,‰+ñ¥§.& ð„p¡´Uõj6 ‹ŠË/.HMO“žF£‘'C.Œ#¤ÄG€V,‚ÓgâÓ7Btí‡.»&·ryŒªNúÐű wSò !ãM ê£â5ˆ Q]äaÈ’x’A|‰3ñv,E%å’ÕR-(ãǼï[èûE“eü¢‰„Æ`Ü‚pÓ¬ÿù‡uùç³ó\3ê¾¥îH*Í:¤ I¥§8¢8änJ^!d¼‰¶*¥{7®ùr÷¦õ[ðcZ!žÄ•ø*Ë"¸Ë… 0"àIá‚~ìä­²¢üBi¥Q¨@Á!m+”b.DÐâäŒÇÃt:0âÄiBÍE‚Å :u)dáäÙy¥¹1 Ï‹ÀL§JÌ¥®@x¥Þ%E´ 9R rV‰­±áò‡B1h1 ]˜NÞ'®Ä×î1rI¸ {A÷Äl4’Ý'.¾M€î!MzŠæ‚&Dú²iq£ï½x`û–ƒéûöž¼úÆ[†YºtžÑ.)ÿ"¤`s…j.‚læK xA £¨‘*4aÈB€‚Åd,=søÀúm+~[‰6+È€"éKÚ5üA \˜€BÀSÂ}ñ”Ív6=mobûŽÐRe!u,¤ª'ƒC² 'mZ±Z­Ó®u¤I±%Fï,5X¡ ŸLè"Z`(¯š i’ ”"àÏŸG‰¯!*+„àÝ$„V$k(H°ˆŒ ‡ˆÜHÀ@žÄ•øÖ\¡{!I’“™±ù)÷*PPúë8éKCödoAOÚä"B!h[+›ÁPa]¿dÑÚõ˜W/¥GŸ¶í:wé®Vc(Z—siû‹MJ%šðÓn,¯+óJ*ËJ Ϥ§I?°÷ö4$LPNÚJp#žÄ•í-&P“€'„ e¢¢Wz’²رõø Ñ7ï8t2…‹jOGUÚ œ)9 ŠÀ`«œ‡»ž+Ïw {ûbkÂü£eŸz®e¹%âE\Ùv‚ ÒX`QtqŸÅÞ‚®ã˜€OÔ}YeÁ_IÒ·dO_Ʀgœ‡NIñxèRQ„ˆ I ’Îþ½¥%ù©UúäùP‰Ú £Ñˆš Xmv͆’ï#„ Å^‚<>ˆ—J$[»W ².…ÆB^©ÒZ\nÈI÷ààñLáBvÖwx”ûD÷Œ‹ïP&JzÊVúR)K&t<7ÒR8j6í†r ~ìþ‚‘d Mêž(Šp@¬”ß*ºˆ¢µ Íi,HÈ`­BàÂê#à Ⴞ¸Ê $í›~ÿmÉʤŽ)7~·|[ÄkOÞ.j5—†Q‡i¤ɒŠòtN†ˆäJ„v. … Ò^X1¤v i/.¤‰¸¤µ°{ÙÐ-‹Ým´B<‰«c!·U¼6t)Üøó$\Ð=¢‰G¹oާó¾o  “&K*t_•÷4yÒ“9 ²ö_éK§,T›éñ¸ÛKxõ̽…noðR úÛ§ïñ"6$\0AÂ¥C¦¸ÑçÄ‘ `µð„pAÍÒ—P,è‹i¬¨(-Ûýûú¯Å‘×ÿå륛¥§ïYí‡L™8iB”÷Qua2×BhHˆ,XÈÆž&. æâ¢]†¢µðçÒâPQQHoмIK$|‘»©ìzŠB öÀYv]ãXˆ}V^¤íÝõNAA.=•‘ðG÷Š8Aùö¾2qÒ„è¸OßEš,/ÅØ³¦pQýÆ,¢µÕ„_Çtæîj•xPQ¸(Â}á‹ 4è½ã÷C¹saLÀ‘@“… üvÑ„ÔÚ±ÒZöi¢RÔ°ÊÓ@åžÍöDǵ\ŒŸÝ?‚ôØíÃG M„ŠË¤¬ú¿è¦J(-‡˜LfÙæ‚ì2Hs¾û²Ý…"`ÔÒ¿9Dlä 5ݤ¹ N¤½ $„‘@¡‘m0ìž!µi,H°Øyä”pîÔñÏ7-[²áн¡OºW,\ ?*4Ò}uœDIØ  “–C Å6ƒ„ Ø´ª&\œ§c(ÄDÙˆmÊÃ1¢ï½Òæ{ß IÇ~ŸÃ ð& ‚$ëbÿ+ôXùa£/§¢f”Ÿ–Öþ´p9~­éåŽìüBéÁ›®k³Á ‰TŽàI(.ÈK!hÄi,ìFŸŠGI Äœ„RFp!oøž„ û{2ô¤Ï«ÏdcAK!¤±8{âØWËçCË!¤ú¥{£ü€Ò=ãâHh¤{K¯ÊJ¿$P(ýÁ(³|õ?üÀ%’‚´\*ù—vݺ§ü­+lC2”÷ÊçníŒ*§‡¿n¨—«duh²p5ŸAjõÌìÙÑs&O®kT.è €žŽiý’ Èä§¥µ‹.ÏÏ9~ÀˆQý¿o–GöLi# ìÑAè•’Xƒ&Hš0i²¤%z:'NÅ΂ E¨P^±~¿-ŠÀ@¯ÊFlO嘀âX»)y…ñ¦Íb)ÙÿÇ–9;Ö­ÚˆçÐý ÍQs¡üà*Uð«ÿP¾Š€A%Íê´‘0¡l¸ëYÍ…®ºA'Pz¢(ëôªlÄFÙ”cžè‹KÛ ßek¹¥d=ãÒŠ¹2&pM.ÐKa…Åf™i«°ÝŠmý·žö”2RÃÒS²£e:ìÛºi÷±½»Ó‡ÜtË(«Õ2'Àœ ¥ð )*R'kª[z)íÔ$j¾WÎóÇW KÍ÷ô…Y§h¨ Ùh›R’qûJ¿ËøÜ%ªñwÚgÁ÷IM.æÌ˜¼kÜ´™8q=Œê.è KêEÒ^Ð’²®‹»vmee¹µ¿âû•{õKn×¹k×°°˜ Hµ ãñriÎ((©ÒP\YQv!#=íàѽ»Ža%$Ü‘­ ûí7Œ¨¿ŸÝì“*zV;ú½àÒòï²™ô;Ý„jøR&Ð`M.ä%៸(ñî„©©#æÎœFjöºŠ£öBùq:H«!ûÜ;°ç0niø^Q×RÊ5´Ï¥nĔТ֥iR}_zT–AØoapaþH€~1áüÙ|ÙÇÇcòn..5‰eY2ŸGˆÇëç™§Ÿ@îmušðh¢£B“ òž&>Zó÷Ÿ{ì‹/…- d¸G̉¯bPKûí#.LÀßàïp¨Íœ÷!®œf$¨?ò·ññx¼Ÿ€K„ ½þIÃøi3ÆápsÞP÷ÙKÔ1|eÒ£ÉÎqŸ„ ¯ò¹¯£ÿÞ~Xá®°U„ ZŽR80HР÷t\‰k¡\‹‡¸0&à‹ȶj´™ÿÁ¾÷Àíú}ÆW.LÀ£\ºÌ0~zê ’Múý‚&î‰+h0h ŠíwÒFÞ#Ê+ >Ž®qÔW—öëó×BB‚²‘pá(`PG½ÒFŸ‘öˆ hv;nmY%àü5—¿ï ¼#¤±ð@º_…ç͘öVÁ§3—pù—×.`À{¨¸8ˆ10&]ÁƒA}P„ &… Åè“>§âòþÚ«õ»ÿ•hh_0!Cy¯|îwx@¾I€…‹Æß7ÙÆàC¬¡‡ ÂK,X4ž%_Ùtn™¬q‰äFìÚgè•„Ë#ËqûV ­'„ú¢Š§rLé§òJçs©›€"\Ы²‘ ¡lʱºkàO˜@3`á¢aÐ)Ž…ÝÝTz—CÆÖ0n^êô• «‰Ïf®%à¶ÉZ¯ÿ2øœùÜ_0ÀÖ_qzkƒôŸ[È‘) %cÂ+\b©:½QÜÖתFüh—¦ªà¡zÔFÆCñgãö|8Rßgýžß ìókuøåCÍp0¤8øX–‰?‘ÿ$ãz¶±¨ÎŠß5—tÖÖõ‹àïágï=3}vL‹~î·Ã-7ŠuÅâ8):N–W¼O`L€ ø7lÞ.мI²8Ž…ßl_?Òúâ]ã>3?%ÀË"~zcyXG@1” ¸ó€™`L€ 0÷`áÂ=\¹V&À˜`K€…‹€½õ×ôÁã0â#5?ÇzOZ%ë‡_~™\ó³ÚÞ£@3Sla±µ}Îǘ@ `á"ï> ø"Q˜o“l(]ÿ_„´ÅÀ]¢¡ïÓŒKVë“’ þ¯-{kž¶‡R½âBI («³ÍS×µ/ êÿMo»³>[ê:‡3@%ÀÂE Þy7ðQ‚ Y€©Æï“$½üûe0ÇJ ü4aÂ.s}Cš;·¿ÆÒ±R«% ŠßY%ËãŽç£æ"4D1—\Ÿ5WÛÕñ³Úö_o:$˜ø%’Ú>çcL °pÈwŸÇÎ|Àä§Ç±ÛÙ³?K½†ºóû ¯¸$’{oEQ`ë„ YBhØ( Œ]°à~•‚@’@õâ“EE(xL0¢çW&ÀN€…‹†3ã+˜hf¨1ø^ÛØÙ_'cW’:EܽþJ]’Û(PŒž9O8l+/ùÏo}²èÇk^7uœõ¿t 9E„­æçüž 0+P_ù>ƒ 0&à][Ð÷ ÖƒÙt%»ÐZ_g}–òþjI;LY`0ÛL·Ûé ¼nÙå×jžEíÅ*° ïò¢Çåtø¸Ö\\‰Î˜€×xmBeqžG¯ "\yI¬“òÊ㉗ÇNѦ ‰ûí+®ûà˨¨šœ2ÁtÏÿë©ægüž 0+`áâÊŒø &À¼ZR~ÝŠyu¼™–8ª«yƻȦ ãW›ô„ R}íxÒË唣ÁÆ/sɃŽÇ•ýàè„TÔZ°'ˆ„_™@°•s`ñ©L€ ¸—ÀŽ[[bØ{øk.ÿ>)0ø• øÖ\øØ ãî2&À˜ðv,\xûâþ1&À˜ð1,\øØ ãî2&À˜ðv,\xûâþ1&À˜ð1,\øØ ãî2&À˜ðvDËÛï÷ ø(—Þ}WWRnmA.ÃDɪRɯ*+¾>ØøR¦ÒJ¥bXXÙ‡“&}t¨Üm&Àj`W¯@ø-`W&€Á§„gÞx«Øl]$›­3¾íŒ1!:c¯DbñóhÜ´W®éÒ¢ X‚­•ê0c èL¥p:2å_è—zVÄã¢ZØû©~òéKgó`ÞL€… o¾;Ü7&à%,X ZsøX?0ÃŒ=˜öÒ¿š¥t]¯—Ä,Û¬‘èå2—R®F»Á«MN¹®Q©l-¢Â B"†ë‚!\a¡Aô^²,h€j¶ÚÀb±^#¿æ—A^a©”SP,¡QjUÜ \§É·I°Œµ¸¬´öýäãJø• .á.óŠM  Õeº3š«U µF£5IMž\PÛÓô¸i³îÉúóÔTÍ·ÜC^.»ŽœFãÓt)=#Í5 ‚?«5Bê'ú©{ý:‚ \$ÀÂÿ)0/$ ×/О·¦ÿÉ*ÙîÇIè:t Æ'm[çvñb÷m M«hŒ/Qáº&õ'7 I/H럙h|¨©ñº’“_¿ï=›÷¦[* &\­ÿ©%˜úñÌig¼®³Ü!&Ð^øµkÄ(ø&à'^ÐUa.Ÿ¨„¿Y$[\«˜K¯NmÕ=:&B§¤V Vùϲ…ŸÜ²& ££’®ÜrÖì8l³X$«¶4¡ª™O™’ߤŠùb&ÐÌX¸hæÀÍ3"@v’Az WMDÿО)m¥ëwH¥ÏÅÿ  á',ݸ~Ç% ^"HÒsŸÎœö­ÿœGè¯X¸ð×;Ëãò§¦>, ÂÇèÀ>¸gŠxÃàÐÃjs <ç/Ã7¿ýn;q6“±À"!Ló4‡5¼¿1 þpy >IàùY³âLÒ§˜ìž”¶­lß>LŒÃ<\›Ù‰¬Ú~~^¿ Mm„“*I}ÛœÔWÓ› Þ×°pákwŒûëƽ>óftÓøÝ$cî¼¶¿xý ^ixè°}t'2saî¢u–² #åMÃñ1|ôFh·Y¸ÐÏÃn>¦ÍºGë‚6-c¤§î©ŽoÙ|á–½š@Qi9|ðí F5¡sñ˜93¦lòêsç˜ÀE,\ðŸð ,lß·OŒ'=tƒèËa¶=ˆ- ›¢ÈŸïÿw¹å|~q…(iñI@ÿ9øÌàkäç3½çŽ2"0ñõÔ»I°èض¥jÒC7²`áC÷®9» “¼QjÌ¿¼ôî»M nÒœƒá¶† s«y ÍIàYý̾òyA‡6qâ_¸Að× UÍÉØŸÛŽÂLµî¿N¡V;”gùóXylþA€—Eüã>ò(¼˜€^¿N}Þöûn]pH÷7&Þ¥ Özqo¹kÞLàÇ5;ɓĦmÏOR_=âÍ}õö¾=«ÿ8Ìl.ìýìˆ ý’ðµ-jã0¦kNŒQ˜¡_1~>P®z•0çT`Ä×rÌáSŽï/`Hþ\´ÄÎÅcg•tLR¥Ï™<¹ÐÛÇî‰þùgÌ_Oã6˜€“²,[^Å£^ß2X°pŸV+1Ãza2¶£”íU<á‰ZO⃵xåí·Ã ËÍ7 V¸E…‘(Xt@A~ÀÆï'„i-‘a!¢ƒµ¸©A…'¢à!{r¡_0˜, —oƒÑd+)7ØJ+ ‚ÉdQ‘û0ØpÃPú§Ï<‹îådx»QTÿ¨ÉéXsQëŸ!d®!0A?«;X¤½ý»'kžºk„k*åZšÀ÷+¶Ãú]G+Ô‰-ôú'  ÉÁ?75µà%žÂI?$8Xkí’¯JjÝ㢡ulDGè%×h²À…¢RÈ+,¬¼"8›“'3/X0A >¼ V”OVª@5}Όɻêê. 7¤ٳcm&ˆFÙ%Z¬Ñ6PiE°š0¡Y”“¤’ ƒ!4óú‹êªÇ›Ž³páMwƒûâwÆOMýŸ„Ƽ1ñUXhßäyGNeÁ?ÿ·’ž¦oš—:}¥ç{à;-N˜šúF:ý·J­ ܳƒjhŸNÐ>!NÖD¸{ÙŠ`ï±³°i×QKAI… 'Û)ófN{küܹUfþÕ˜w¸¶î˜Á¸7.·t"ÁÇ™>¡.¥j2Q 9€Š—]˜)y—*XØémùhX¸pænò9L žöv7³d>4fhOá®QýQ_Â.'PZn€—ÿ10“ꤹ©Ó?¼ü ÿ:B6K9Öm}$›µ+†Éoƒ+mqbmƒBC$NÌAhäªARá«„ÁÆòñó|\È(Àu Òê¼Ø­}¢ðØmC…¦fn,UZNY¸êX·óZnˆâ±PÔjHÑ¡¶„¸hUëØˆ Ç%™  ÚeQ’B‹ÕV›¬øZVi„Â’  Ø'…%åq¾€âŸ`&CZÚ$ü{Ø%°D­†¥Ÿè§îml]uÛ\¸Š$×ÃjÀÕÙ§ð0´OçŸð[&ÐxŠL1¦ñµx÷•ÏèS¯¶Zán†e[¶ À§z»Úl#B´Ö˜0€D,²M¾È¥Ò`–ÐB*¯4J˜qVÄeÛÄû®µœƒ›©`^¸gtÔbdXÕjÕðUÝÚ·†Îíâ´óhJÇÔ£2psÑŽŸíwêÜ…þf³ôæÄé©i(h}®ýúW_-mŽ¡³pÑԹ̀ âý’ZJ-cÂYCwÜ3ƒ¤4íTðé=ü§LLMM´¤ ¢ >f1ÛÚ©U¢-91Nhß:VhŸØÚÆGCt¸p‚®oB¦ïšü}##K\:ªï\ÁÓ :aöó÷»¼/ÁAèœ/o7ë­¢€kûÒi)&­Óéì —™ß™0=õI§Nõt<.<öçÅ ñÓft´ÙlIWuK¤aóX=@Õár+8 ö@snoâEýû1֊ɸˆñ<]jºwLúwkýº$5)Ð W(àkÝ\—@IDATÚ0´+ÁMÌÈ·µ;‡n?xòïB¹õÏø›4eîŒió1ù¶¸½°pávÄÜ@ ÀþþVŒšE–è\˜€+ ìMË ¥sˆ:d‹+ëmŽº(j- ÿA-CøÐÞ)­×ô˜È°æèŠßµIÞ0OÜq ÜpuOm>¢ŽžÎþtâôY7b„×ÇÞ{ùe·k½.®TùWhVV :RZF‡7k?ßuä4ì<|ÊñP“öÓΜEEߘŠ\ÝŸÆôÁ×Ð8hóD!cÎ-{Ó1¢‚ô½¯¸$ÖÆeÁ‚ªñÓSß¶Z¥ÛÅdž½1ñnáÑÛ†±`Q¬&Kl /<|“xÿ ƒÉæõîÒãÖçõÿŠhbµW¼œ5WDÄ'0F¤XZ3Fÿy ðS?þòÑçžT‘a:èÝ© ÝíÎ%¾£ú‡ÆÃv; œÇ›¥Ðšë«OÜ£w‡M{ÒÈJÝåý aåý¿=ÔhÁÂÙ-XµÍÙS«wèDü¸vgµc¾þf嶃5$›óôSŽúêxPc1Ɇ‚Ř¡½`, ¾,XxìV’P~ÿ Et…½sÂÿͺгæÂt¹î€% HR¦U’Ä’²JˆÀž,áº`HNˆ•·ýÄgÛþãUOÕèÁÿY² öÏ„”6-á–á}äó((>ÕàÏ`¹«Ùið£ïWÓw^èÞ4±eæ@ÛV1€VéÐ5¹5¼1w1ž?úvI’¯ÙŠí,ß²ŠqÌI­ZÀ„ûF¡¥  ÿÙœhCû¤Àµº9…cäU]Ð íÀ¾Ö¦0c(æ+ÿ€=igȃ®Øn¼º'œÈÌ…ÿ-Û d9?õ£EУc"Æ0@‹ˆ0zJ&/ x󳟱?‘0ûHå#ÔÂt¸È àà·Í{QsR‰—b¶ÇEGÈ×MC ‹r|ä”À¬çï¯6j“¸uïwŒìWí³¦¼Y1~\³ Ã< æÎ˜2k^êÔ¦T×l׎׿•Ë{Wuk'aì—4¹l6ôU ¸ª+ü²y¿ÿVƃ¿V}àâÖ\¸(WÇd¢˜I¯…¨Úo®BKYy…¤ÕTuaïʼnø±[‡B~qN¢ûäÏÐç¶ì?!ë¡»Žž£Ù íZÇÂÿ–o…d4ûëC7â2K´ˆ´/SÐõFÌ¥@…&ô¯–n†¤øXxâök '.Çè0 P„.úuM†—»ú ²pÕ¨0åk®ô_Ûø0°G¼f{U¿¯Ù¸;sØÞ¸}D?y§Ì-"uÒ¶%DaHç'î£PõN‚ľt»çtV>`¼ö,扰`ž3:™‰-càä¹<øòçбM+xUL¸ü³(óÇ߯¡ü»`€cw0­„>^°‚4j¸…6WZ~¢ñÏÇßXµŒxܪlWô¹Þ:,Ö)*• zà†Á,XÔ Ê}RÜø˜Ô= ]Ü׊œíÍÕsÝL @ ˆÂ ¹â6èI ûÓ3aÎÂ5ðÊ?ÈÞ W÷¨jž§=rËPÔ6´ƒ!½;Év `H¯JƇ1´4•=GÏÀtÔ F€Êtk£Ióz\jQl ä.þ÷>íSݾs„¬É JNk½qhØÚ;¥-F´ÁÑSç/^Uÿ E'¼÷º¨A¨€Õ\îuy…²)iÕ"ýü[A|l$ìÆ~S$F%WD'Œ@;]Qœ[PŠZ• ó9èŽïcP“q,#ŽŸÍ•;Ò¬íN©z É6åîëúËŠ™9UmŸãïUÍvE…?ØÿY²bâªû®“m\ª.häNfN!Ìþr©uÍÙQ€5SïûpÒ$ç$³F¶éÎËPcDrÑ£W÷J#Ã1@%—f!@¹P049ÚœKéîì/‹¸“.×°hM|âë3oÝ—Þíš~=ú”†?r°¡ÇoŽêùÖ( \úš·Cm€Rh¥§mZ& !Ã$Ã.ô&i“ô¹ÜB\*Ÿ:áÞQðí²-¸”°m,:ÈËZ4KAI.…ÄȆ¤ŽÇ  ³ì÷}ƒKŠPbA{Dg õkÌÐÞò2ÅÕ=;T]FBʱŒó²ÆdçáÓUÇsòKªöw:¶‰“9œ8›‡ÂM6 ĺp¦ƒ4¦È~„–?( …U& †RÚâò•üârÔ†ØöLI´ï8üät6T 6„4-MÍ|KKiK6îß÷¦SHç4Jx|Þ›S—LwhÑ÷v'N7£F†vÄå'.ÍGàû•Ûeœ ¨g»³Õ!ÜÙ×ÍŒ€d³}ŠjöÑÒæðØèûtj uå2 ÒÖý•‚öó—oƒDÈÆ >¡S!û)¾ÝXOÂÛ Ñ›C/«?,$ŽœÏ®6FÒˆ½…:~ùñ[€Ü(_ùç÷ÕÎqæ iAp¢• 4A‰Œ)Pãµý»ÊÚ…+ÕCçÒRÉ©s¹òÒÇ£· Å,˜jX‡6!(T(Þ'$í<|¦ªºœ‚by?íX”¢ôAyO¯:ÌñðÍCàß?­‡^¨M„BFcˬ/~‘ÐS†d\>Ò€t°±uyÓu‚Ú fú[áây$ü~õËf؇vXh{5cîŒ×ìk¢nê Û\¸ ,WËBÕºoq†0-ÿý€OÀè×9 ðÉÖï<Š“c{¹Ï&Ìÿ¼µd¨Hö1hǤ½l<´ì@®£«¶‚´KØ†Ë •F#””Wà2E¨lÛ°r[ã8±æ}×2´tÔz`GØ~褡ր4¤m!› *ñ-"e—\ÒD}•®(äìÄxÔžº&ÇË6)çóåÏè2ĤqÐRÕµjÛ!y™¤†Ÿ®¯P,rE½iH/Ù˜”l3[s5æ£étѶEzçã3¦ÏÜ6qÚŒqzý—><3&&¹ïísq?J´Zöû~˜þéV\2¥îO¡Aðîn™… wæú–Àú¿`–Bý‡NÊ“­·ƒ Iœž¸)NÆà^r 09#ão›öÁ«ÿúµóå‰ùZôâ¨Yè:òY¼nLAE«w 6_7âd»bëAøûûóååÒ‚4¦ôëš]ÐC…´!J¹=?h™çèéòÜì¯á½o–¦¶–?îÓ¹-j‹b`ò‡ ዟ7ÉÇH;AÂF÷ö‘a¡8‡áÒµ4vá„ ò˜Y±Œ?ù΢àñôÝ×ÊK&J»õ½ÞŽ"d_òÅâòxë;·®Ï¨ïdó‘úܽjÜP3s•5Éç·žË75õ%JÛ]×õÞzüSý˹¸uØÌ3½üií. g F±” |=ß•ç‘M È®,OœƒW?\hýyýnZ¶ËAºù³S¿peuÕåѵàº:ÁÇ™€¿Ðë%ñ¼ôÖ:µç>u‡ØÝ1½¹|óËïpÝP_{òÖjÝ$Û i~ECEòª Må8 @^T,8y“ŽŸlÜQÈ£‚"…êPÝ®´©´Cª`² öZ良©ö m³¾óŸÍ!—` Ý‹4 MTðØý´mõ]ãmŸŸ>óO¸\ø- pdèëîBÂEO%!íJ…¼«^ú`>¼†1bÈ8ØÓ…¼”âð÷á¶kúº¬iú›ùaõN¹‚ãøð ˆ©ÙEa5¾|<ºgç_ÆŽkuYc5*jø7®Fü– 0ú ŒŸ9³µ`€ƒ¨Ž|nìh©Ð½­P’#òœøqíxñᛀ¼,¸x'#h„ú-Ú¾ä—VÓäxgoké z¨‚úÚ¨Œ¨E VË>ä(\á2i1h‰mÙ–}²«×d¸sÔUr /r9NG#aòdQ£MÏÌ¿Ü'<×K…:D±TÚ }ÅrÙ´ç<÷ÀhÙÛˆr£d¡âàÉsÐí–(_Jr‚Ý~éü…b´[Ú‚q_òÑÀ93D¶m"—ð_6í•ÝÆCqÙñÁ›®–mw=ðÒ²a& ‡Ñ˜Ìm£ED—à6Q;O?uwÓ]òÖîgæ’ª¸&Àj#°kíÚ²A×^Ž.`wmÙ—®Co !ÞËš-Ù°Gö¼ µ~´½àâ½hÙ…Œo)Ðyáøb0+:Å9äÚK1QÜQÈ3Ý.áºAÝåêâr]Ú™,y ¯.ýŠ“z+´ÏiƒB4SÀ6´y‘ƒ½Ñ’Ùúi°ã¿<†žWípIï{Œ72¸W¹¿¿ï;ŽËÇÑ®¨R®ŸËQ(x xÖ½“nÜC6ž¤àsŠïœ…kå)Ü: ÑiËþtYÀ A‚ܨé»wûˆ¾˜^>F68v:²5êÔ¶Å}:¡ÛõñÌÜÈÊrÃÓ¯}r禵3ˆª§ƒîÑSÖÓ Ä‘ÀýÔý’ é‡Ë;>]´^ú#K’Ë¡·Jõººú¢­…·ô‹ûQ;šŒ&`< ²EñÅBîÒT(L;Ù¶P„X²M*ºh3ãÎ1QŒ—Ñ(l ï×(sèÔ0Ð’EŸ¥’ŒãÍ]]±T”þQ\“—»E®OBÉsÈ!– Åu![!Z.¤ mÃûvpt¿ºw ÚôÈ6N¤!¦ GmÓ²¢; Åtyýé;U=SÚª$þýœ~VgW·W·_š«[âú˜@€ ƒ6½~Áˆ,ë±w7ì<üìæ=ÇÄWuoÒ“ŒqP¡ÿóhðð)Ò"-›‘·Äz¡°ì\kUB½þICƒ+jÆ ž™=;ÚRf}*-#çy\î‘%% €f½ih/¹»£PÔY¥„ã$®D™UŽ)¯ÎÄR!÷fǸtmj”âK&í4 ’ÉVùœ‘ÌÜ"håùìÉdÀlL¨1Ì/cŸÆ)ýrÅ+ ® Èu0' èõcMxê_Ÿ›šú¾Ùf›†qžØ°û¨Ø-9Aè‚Å„ðF› '‡Ç§y˜ÅÜxxÌÕß®HÊ‚¬G°ù{¸ MjnÎäÉäñÞ¸i³Ž¡æ~ν eø„O“°»JÍpuµãL,­ærË‚ºbÉÙ§Û0«/iL¼¡pƒ9†T‡NfÛ“ ¹°S,\¸&WÅœ%ðñÌigðÜqõ³gÚ¬Ö玜̺×{eÏp]ˆõÖá½UÎ&ør¶ÍÆžG¹@(‡ÇtQ¥]¤Âåâ=ÈERKö…â?c¯|J¸¯ŸyXàI²ŽŽ ³Þˆv \EÑR=]ÈÀ’¼Š(ò+M#O!%– ¹/§`ˆy2È$‚â¤4´»3-wü†kïG7êp]¨ÜVOL¬G…b³œÎÊ“]aÉæµ¦F¤¡í9{¾ÙJ‰s¤Æf©£!¶¹¨ fž ð©~òé¹3¦½¬‹ÐöG7±O©MtŸлÒm¥¡¾ÿ”3ã×M{°_ÙèÍUs‡_¿Ò7r+$ã¼@)½;µUƒd8^?×'Ö×P3!ŒŸš:E2K„ë‚ú?~ûp˜ùì½²@Ý‚ýÍ-ÅwÈKÿ˜/OòõÅRièßk½×bà¶"À¥˜ôÎ7ØÖvÙƒê‰mŸ=_/¼÷­ì>ÛÐús>¹[cÞŒgs¹K3»¢6æŽð5LÀ…&N›u›$Øæ¡±gkŠòxºÆ5æÉÈÙ.9ºç]éŠ!ñWü±£ÐÖCÐÍ•Å~ýJÿ(Äñâõ»àÿÆß¥òëWŠ¢úùO@-h»ÎI}5ÍÛ;~ÚÌOqB›€SÒƒ7 <õ”î Ê”‹‚¾¬¡Pί/–ŠrNC^). 2Ìu,´T‰šÂP7yÏ8¶Eûßa¸ÿ»ÓÌhÑýýäã5?oÊûËŒšR_˘€ÓÆëß‹0|Ä6É63±e‹Ðgî-ŽÆ¬£ŠÕ¹Ó5ðDG÷<Å÷Ÿ2„~¹d#KjúíwDC5zúÿtÑ:ÈÉ/–ÝTw= Ãúv’ctBñ—ù’Ϊìõ»Žb$Ì8¡ïÆüy‹j”™” EüÓ¶StÊíA‘2)L8]CYc7íNƒ–¨f>íü´v'þàÉiÎ)Rh9jKл®Ã´éTh‰FÿébŒ¾%G¤ð䔈éŒ@™S‹1ƒ*MT¤m¡hœèú+÷?]ðôŸþ$'eS·Ï1ŠæIÌvJQ9I©ÙvTWoÝæý°–â8)Zg7\‚ÐÔHÚ&w¬™ÿ+D/ ÊL‹q ¾Þ½q=µm3÷©®æ1ÖÓ(X¼AiéÇÞ8X / o*ä9R3àÏ’­-W¸¢P¶a%ã°c}T¿§þ¾Èõ4hI䟾9u¾c?\±Ï6® Èu0˜¨O6ÓO‚(Äß1â*Àä\h?æ¢_®ö…ò`|»ìw¸yX9­:=Í$cìÝ“qRï‡ÑU&w%¹ÿñ÷kP‰ƒ{F[£œ#w€á´uð†ÿýåÁ0y,ß² Ý€Š9u;=•¡Öa<€^˜jtHïŽrZt E¹ç ¬¥ä2Hy?(X”RèÉŽÚ'¡‚ÊϨ F 3&g>¥>ÿi öÄíÃäun¢¯£kK0ýº‚Üd6_Ö6 *_-Ý,'F£ HßaФ,iÌÐ^t¹WåIX«’ì™Ö¼ªwÕ;ƒ!¨_ïÔʆ1UxY¾:½£ïôç‹7 ,*lÒÄELuGÃ,\¸ƒ*×Éê!0aÚŒ§l˜£Ÿ¹÷:±5>…7wQ|ÿ©¿ãÓ>ùþSÌ  ¨´Ž–ƒ)Vüøhü=£äÏè?¨Ð„Ü}è—bÊp:—,îI 8ŽFrè^X% Ôôë—/ÆÿHh¸ûÖþrêr:FÂE}eûÁ˜0¬=Ü2¬wµÓHk’…Á“”xüJ¥fÛè*,'j»ªk;ùÒž)m`>íy£p‘WH˜` ÑiÎ]iœÍùù ú¢ÊÍemût¶3mξjÛPcˆv%hÅ)í‹ ÓÜþΤIWþr4 €Æ—0Æ?=õ´­ø­Ð%ò1÷–µfg}ÿ•q÷L±[¸+ï)aÙ®#gÀ†QI¥Œc”7B(Ì3i36î>†Ñû`„@{´DåÚš¯4‰×Uhí[)”³¤¬ÂX-®€ò™3¯²‘|Û&-J1:›ýů—ÎjåÒ¥öëØKÏȱ¢JýÀ{/¿\^Ç)^q¸¢uh¹˜Yf8“}!Ø+:@0Íò"-õ¡ÆtèÔ¿óê«—Ô‚.fÁÂ…‹ruL .(XL’P° tÚÞ:¬1¹´êªºÉÇõýW¢ø JÁ‰—>À_ÑŸ–NÖí8Œ?bMœ\(-±,ݸO>Þ±M«zýü×¢i)…4 $HPÐ ¥¬zOnJšuåxm¯"jP¨Ðò‡R(%-¡8ǶƒÑØŽ4 Î$½r¬ÃÓûùñØ™,Ѝ½ÜÓm7´½y&˜ÇMKýv÷‘ÓOb„L‘"Erq?¸t¸pÕKiE%EŸ1÷Í©oàêÝWxÍË}l¹f&PE`Âë3GcÄãÈã‚Òi»Ê0¬ªfÜ)(¶§9§‰˜Œ0·à“‘\ð§+3§Ò05 /J~E[ãè×o4]šô‡Ò)Éžf}3.ÕPœ »ª;B0³5&¤e aaß± ùrª›úBF4ù’`›VѲ‹-ݬÞ~XΤêØVÍ}J}Nm®ACQÌ #‡k'o+ô$jµbg•ø•·õ­¶þ«#þ†Y9|4•mošý~Õvk:ýégáÿüj%ãåÒ ãÌÈ6p^êt½» êù¥Ç¦ƒk`L ´Î\i­X„Á§¾eH-gøö¡^¸DBvoÌ] ¡xèÎk¯’3FÒ¨hrþnÅ6(ÅäNá¡!p†:oÕ€ˆüúÉx”üúIàrÔ(DZƄˉhÙeѪ²aiTø%mzÈ.˜”›‚ Ù‰Pò' 6ïM‡É.„ÛÀó^6½aÉú=ðê¿ Ág«*aGi«æ+Ù‘ÐXÈ#fáª?äKhx+_[óÜæzo¶X`å–ƒœ,ÖÏÓO9Ú\ýhH»ê'•ýt{:ã×On±¤÷¯é"¨´Cš²÷ ¨ŠŽ… 9éN¾U‡ëëgÕI5vJË XFN.Uã£f}»ì÷ý²«¯(ª†Î1ek³v¦/X°@µjß±qÅjÞ[íUÝ’EÌØ)§ o`U:eȥȢ8NBO mžpùCØ‹ ïŒîÓyÁرcíîU$uéçÁF¹)&(HkQa)ÏyU̓˜Ê™ p\L·þæ¼Å64L]8/uÚƒ®ª×Óõ<;5µ­EžG|& ‹ˆ²öëÜVÕ5Pdì‰B1Vh ¡SÛxlóRb3O´][´”·cÁ£`LI )‹*³Å&ÇŸ¡å¾ è!”WT&a\Æ‘ƒ… mÑiôùŠÁÿûxÆk‡j«ÛSÇxYÄS¤¹€$Pi)»ŸÎ5ÃûÉiC’Úõ(cç?o²Y%©DTMr} ž«ñ“™ÓÎbk¯`èr½A¡i슭G.Ûr@YE-)mãÕÉ ±Ð7òjrExðô:ƒnΧ2saOZ†-ûB‘l¨ ¶NêUTDóEQ§¡_.Ù$Ä|>¨Û«S€qqŠÑŽ+×9ö¡P±QP‰)€çî\ý-ÕÙñú/ãO™p†Àøi©_àä£ï¾ð òÎãsœ"@Þ8ä•# ª{ç¦NùÑ©‹|è¤ño½)•[o$é\Ó‚^V•‰#ØZpy—ÃÅXôö Åd`!••„òð¡BKdhä ›ÊÑ„‰"ÜеXÂH«Ö’òÊ‹ßGÁŠn™ñi€x¨—à2šîþë©öh/Çiñ6²•Øzàj,[0Àv  ZRZ,%Ø$SÚMXEL’J2†@HÖ?ô/yªoi‡…‹ÆPãk˜€“09ÓZôR1õé;¼+Ʊ“ýçÓ¼ÀzŒl:Åvœsáÿ¡åÿKÞ×C×÷H6жU \e©#N\íñi=E²YÛ`H•+ îh€`A!"'ìL«d;‚ö»Dw…Ehö:ÆùË´íÍ‚ø 'ƒ);ñàžíUä‘DÁØê®}% äµDQdÏ`&Tôv²á>ÊR(âˆð úZLŸ;ãµ}WªÃ›?gá›ï÷Íç  æbEÛV1£§7››÷ƒÿþº•â,NÔ «×¢,[[0ŠðÜŸèD(‹À¨·‘6µ9ÜfÅà(*ÉŒë&Á¢6kC,Eÿ˜2% âæT7mÆõ89>‡×ŒAãÈ`º66JgÅȲêØÈpˆD¥¨ðÙ;ŠŒ˜ƒ49¼=©¨X0ØMV¨0e͉¬=A"§ Ø†%4r–°´Vw¢uóbµFµÈÕ Äœ°Nbá P¹J& 75õKFõè¿^~Dåà° |̯LÀiÛPe޹NÈ `y¼ªÓ]zýX{jM§kàJ@¯ÿ28Û’y­â@\l¹ …Œ^¨]HD¡¦ÁFñZ.mäJV[:ªŽa¬£*ÔžGïüào«lhß¼ýü+ª“¼}Ü?&àÍðÑdµÙlyâðÉL9Þ‚7÷•ûæ½þÀ*$XàµV~·~ ž¸[zý“lgùÅ­ªIÊh,‚1^² á’d GUI~(ÚDôµ‰YZT`-³ êRA%•ª‚„œ9“'VU;,\ÀMæ!6`îWtE-[óÇsbMaóÝ Ÿl™BŸ/^·Vl=€Æ›ÂI{LJ“&¸%Ñ”Oj¦NÏÓ¿t›¦Kx¸0|˜ ¸‚À¶õ+ ýGŒÖbþ‹Q -£åÔ⮨—ëðë`΢uÒ¶ÇQa!~ mã™÷ þçýc„¬¹ðûÈ£ðfêØwKÞßüò{çĸh•þÚ›»Ì}k^§Ñƒ`î¬E%¨Yaü¼Ô©_5o¸u&Ð0¬¦m/>› 4ŠÀ³ÓÞîfóÑ‘º—£Š‰¤%Z.L :Jƒ ~Æ\&v%ûŠLIwÍÓOÝ]ý,~ǼŸ Þ¸‡~B=G†¢ÇÈÊŒô×?Ý Nˆ‹ö“‘ñ0\A€¼A­Þi-¯¨D¹>Ôª#_§$_®¨›ë`ž&ÀÂ…§‰s{M`üôYƒŒ¨R©ZüiÌÕ*JÁÎ%° Pºø_7í³b:w†Ú šàë”ûŽòè‰ üwÀtT®¾ä<Ö1²y¨NêÁL¨ùw³+ aÅÊ:QæÊòȱ¶bí&†…¬Xó& ˜\[¢yãÕ8 ÓÂ〼?‹ÅÚ’…ü¥e{…†…^,œÅ•U‚ÙK°RÅ)±±û+›?…#u‘ uñªQ™ë-è¸é½1p4F ÃÊŒaXQÕîqwåÒó;x³³©âÒn`ýÖ=lÕº-bûžtï»ä¼mÄã÷ßX¯Þ+ÿYö-[ýóvŸ„¨TÝ4UÁrØ|£eÙ?#ÂZÑ@[–2yrÖÙtý¨,D *õ ²”6 '¥Ï‹°Í#÷aÁ¤Q¶°/’ªôsÚ4vŠT.ìÉ:E¶ÄÒ̧ÿñ•ÓQÿe¢7íØÇ¾^¿Í:vWÖŸàù¨>lh0äõq¾>1½V—ŠLjÀé;ÕZÑ)!"ðç àÕfØæ-\å7 [\Ž^ vYçwh£žÛ¶‹lјEbjñfš˜Kªø·ü¾ŸmûCþØX*Úk7€FõSÌô4'eZ̧r„CTr²KÙ—±Ñäê2æžjçv-ëôúú§­ì­åkQEeuD×ÍÏN˜S§+D…'§‘ §6eEjJ`âŒ Ž·aßÍŶ-:À0Ðûc¤‰Ù²)Ãä\J3iŒ_ “û`ÄBƒƒº[˜iÙ *{fÚ–÷Ø„í@&ìec‡³²YFf6;|4[ddf‰Ø Èr» ¡0,ûdö%s…¬ô-ÞT¢Jr˜­RÀ¾‚Aça·]¥Àv¤„]8ù=í{oÕObëî4Žz|ÔF‹â[³.ŸÊHÎ $\œ— AªG@ ÇóŒ a Úƒ »Ôó=¡šï¡£Ú– ™§¨ll·2fïÄ—ûú UûrŽ>i_eJ9&1±Ea®ø]$ýºŸÛZüu`_~Nëf•‰zÆÂHû‘Û÷±o7l¶íå˜äì 4Dã>óÍ;qÆ. e\‡ pQ‡/”G@×¥¹¶7så«MMÛlÆUÖ̼lÁ2ÑT¸(°—ý…x  œ ¡¤‡²]sbb2ÊK·²îº.”4+ñHÿiØc4îŒ~=:ò‹º{ÖÌåQPh²m{ÒÙ¯;SÙ›waÚí9ïD*³ùš«ÉìùúèìÊÖ—Â"P’ %yÐ µH`dRR+Ç~¶§Ã¡hä².êÚ^írN+Öµ}+Öö"§k“Ý={Óàw˜mÝ“.vïË(z—”m­u_M³cVæ*R"Py$\Tž…$D –ŒÒ“ºX¦y`¼?—v"Œu…FƒÒÉcjt+48È Ò8à@¯:u¼EûüBƒå¼°Ð,cÞtÌ„©ðƒHsFÙüŠÑ¼?1®þÞ$dìqãòJçEçD€Ô.j%¥Dˆ@ HCÐü»+†ht€ÑÔ;„š`¯&*Šï¤9BaGáv«Íe\É”sM0Ûõ{PË=sÆŽ-¨A‘(* D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D ¾ë9wD¬çþúZ¿šÖk´žØmøTÏ_kšÅ'D€´‡tTQ±ža‚‹¡2.<»½ .ôÄ}Yôª'Êíy‹3þs²'vFyñ ®\̘õ,üß(/ÌŸÙݲÄ\ˆq`°äÏÌêNˆ¨MJm&ö§LKaçqÆÚ+\yõ_Ç8ëa ¾l¸;~z ypÁ>àœ¯t>L?Z÷ô‰vÇ'2J›"@N/Ò\Ô ožšûº/©éQî„8&شщ‰ÏÌ‹‰9\+Y”‘Hr‚û¿e8×-'ƒÝd3Þ¤nšJKˆ ¨÷ÂÅw¶9Ÿv/›³¶ÜæAŒÛYŒk»T5èû‹ßÿýhEpªïg¿‰¸OY…vgìw{^d _õ{wf³1è?ÉJñÄ]2FŸÝ0ß86ÚŽ›¡…h,8ûYÑØ„dݽn”žÔÅ0¬OB\®ëæè“ö9e—Øß²íùÌÕâÿ„™1›s¶!%Þ=[ú/^¼X]±aë“LðÑUÓ•¯9³– '²oç!ŠÊqœ6r®ŽOŽùNzG»ï²™¸‘7˜°ŸLôq± Kæ{&ýæ‹îÝyà {46_fÅ sá±BU”—>»Ü ;qÆŒ™ÇYŒ‰›Q.çb¹+L}\ ]Qqž”c<Ü †ÇzbŸ)>ƒÛìñ»¿“†Ôl@t ?§EŸ”èhCºC€›À„8/%Áýˆ<áŽn þ(;£Þ»…Â,ŠwÏ—~r+ïùžø?Â㉴ó®ŸáMj£NË–ôbp!Ì‹À©î…`dz]^»5öÿ¢:-…8Ùà:?ª|ôBlì3\”³.{]_©¥›«¶#\‹S&OÎ:ë H"¥ÔKáâ—»"ÛcÑÈüÍ.´"½uF뇆RþÃÏd–0íu·¶ú zJŸ«ówÞ±J±©Éé 2²ÍBv%"Ú0‹é"ŽÂã·K÷ó؇4T®(£!|ìä†=Ò6Å702ì=OŸ²-Ê<ß4DФ¢t`=!ìGgSŠ‹®—6h€R¿¶»qã<)Ä o!h°öRU}°%ëW,Ü8é…C^ÌîÎ1Jßhå‹1|Þ¥?Ýq¾>1]†…`ñ>€çsM¹ 7›mìi#×~ ^ƒ4µI’)Žt€µJA¸+|œÅT»€å6–qÕH=©Ó}Ê.]Jª‘ðäa*©‡®G<ŸàbÿU0þ?œ3)Z6s3ΓB4mIeÜÁ#HxŠ'öe(óùý,!¡U^žý…üM-,žy=¢ea^žØVaG§,ò-Ú@—g‹Äd¾×ëÅÎÏûÏС¿:¾UÙã^êŒt½B('y¯ïG¾?µÕÚ½¦ëçW%­ª†•Bt™µUcA×—RýÓ÷y¹b+ÜH¸ðƒãìðuÁÖQ–ÂóíopZ%á"*.áŸøP¸4ò]B8-ê•pñë ƒòòÅæÐ >ñ’>™¥ ö&âZ×þ˜ûµ{Ýmm£û|˜¶úä§vÐÒ`¤žÐ‹Y¼‘ÉìžhÊÝh ŸOÑÿuâ‹“‹nÁ-µž3vlLQj LÛºZs)½è±|¹<áÊBÞ‚ó‡ñ{‰3{ ö^áâ_Ï<žu´àn)wøÂ—Ü ñãÊ ‹â§üàóxÂDK!ÄN@h d#<+Ùãm˜á§£ŽöbàÖ™«Ê­ÉOÅ~ìÄ+sÏ…K ×þ¶`Ê”LéòÅgeå?fÛFoœ¦CqcEÓ:/Ô§ü.ÃDÇz<¶_ÉÆn¾>z'ÂÈÆ²àyý G‹t F²[„i€û®T+©?öÐ6‰l‹Ý…ãåë³ç9}Tÿ'ÎñÞD¶hQ|ì òÛÜhhšÝÐæÌ:t¨lL¥Åm‰kàuóýCV³¼{ØþoQ‚û)¿Ú>reú¢§&ù9qH;Ü+ð–“ îSÜJìr{—ïÈÜ›ô¢6zÊ?ÍOË ^®³Âµ†–mV%[°« Iš–f¤>¥'ÿŸVË\CflLvó,2#DZ “:£ÑqÏê€>Ãýñí-H%3—ÚŽ4óÛç…-†BÃYô T2.#5%€¶~l¿éÐ:7ïÐj4Œn4öR­\é á/€JzÕ·¶–BI•7| ^b™â]|µ?¯þŠÊFcǸ’ ñ%Ž`!Ý-aõÀpþ“1ýÃá‚|†Ö¤—t Ó"Þ€€tîHÝs™³)Â@“c߆åòáã}h!¬]8EgGÚ*Wø4Žc™û4G°¾ÏN˜MJ†°yyŽt{âeœg›æ N~6“¤¹.r_Ö†xËñ&(ý¸mC˜âËÁeXÜ.5¹fÞµh ÷/Ôc7EéÏ6GÙ[!ÅþiáKÿ34Ì WnÞyî ÷’×à„;4*¹öûH3_wRû°mÖ+'¥L[k:Ê^•ŒPÿN¶–%¤(‰¸Ï¥ÐQåMU¿™ï^¸Èã~ÀÅ]×á~èÁ̃V9¡*DÐunƒëÔz Î¿Kå"¤ U?£AÓŒ5oAƒwÞGÃÏhA(ó?%­>ÔZ y¹¹«Q— dU+& “göÓè*iØç£qUŠÎÙW°¸Ù?ΧJ¶Q gÅÝ2K h!lÐfœØ P)À ß{]ä×yºJ§ŠìmFé ™†xÁ.=®;_®…ÿKÞ£Ð4-Cwß{~½H¦#7ùÕœj®^«2UmòEöáÜ œì™èí15áFtÇI\WðØ asаÑ+æ'3Ud¯#ó¨¬MQ8ö ·Å•PIíL°ÄûШÍv4j2=oMûy^Œ{â ž³ù°Ez~ì´9 òͬe`ßÆÇ¯CÛ–‡ûåMh ¨ó‹<±^Í¢×æé—mßãvÄû ®øã|¾„°;_j +²³’exbæÌP°ZÏhýD¶9÷ú-¸×eמ£•“Á 3íÜBj{w‹*ÖÎy}‹þi.îiɺnN··K!Òχ‰@à à\·7Ù’›—'«/Xø!šu·´æçCØ6~ç½]”žxžPåÂËi‹ãÆþ·>êŽïˆâÕš¯:~þû9ONÜÁ#O˜üwKœàⵃ€Ýä™–èòøÜÿ7/~ò¯þñjã,7ã…Ü\UX†^ò¸¸ËHªk9ƒ`tb kþêÜ`¿é†ÆFT§|ã}yr¶ÔböMx³*ó ²qC}v ¡t"´¦F›f»ýÝË:FüMøšŸcÒ@…œ­'^PV¸š¸É®›}÷iµ‹ù 1"!Y_­Š'ê3Ú‚oDÛ)£‚Œ€¥½5 ë¿UM;¿¥Öoû=©‹o`‹Æ]Wâ¥1áºäšÙŸÊFÚ²o`L;Ê?û4ûÛ!Hvµµf_ª–…(¢‹Æm¯p;BOèùE>†ãáj§0u:‹$ꟆÏ^§´×q¹ø ðoå³×ñ“v4BÇst³´e‚ÝÏmþñO‹6,^Eù MÌ5~7jÛ×2¬'̨XO{Ó´× 2+ÎLe“Q·I#¦&Ÿýä˜ãáZÄ­Ð4æ ®Œ‚}Pß°&aÓqÁÈÙ.ž€íË [û#½V¸ÆÃ¤p%Ó–é¢a¿N¶·«ÒggÕÏõhæR.çÅŸU7ÞǪ›È6`ÅÀNå25B] ýœM2÷ Ð 6nèz¼,ÁB†¯Ç®×õ¡…`[--—“í‰@uÔyÍ…´±@cÓ·:•/?Ž˜Œ•°ÁØS~˜šù èÙi¾ÄWb$ËÓQqÓcƒÕ°? Ìã .ýúÃ}[ò´˜Ux‘-àb.^f+æ'¸÷:~þ{©ÍÀÒKN‰ššð‹l¾š§f ¶m]"œà3𢠖ù;T­é;&Ë vAß Klž—àþÃ?lM#5÷—iFÂ:ÃÏa4G|kÍýõ~ó™Îœ/Œ],ÓÇËõ¼D㢦Ooä¨Íg—‡¯Ão x˜Œ ïIµº «0Ev==‡Hx˜k…tónœÏF:ãñå½I´oö!K=t³í‘Hw¶3ºÄ ZÑ~ÑSî%àÒ_ö’Qú¼¾° É®(|Uü=5` ¨*ñË ‹>)áÅ å±ÿ,ÜT–Yn‚eö¡{lÀ¶/0GÑêì Ö½á„EÃu’½ î«ñh0÷â ýQ_¸ß ìn…Šm׈¸¤›!¼*rY¼´;*¶²Ä0\Ý·¤-‡´rÒ—{ÛrÄÔ?MÇÿ†Ç%ýCëïÇôTö:Ð x"¤S®žHw5º¥-“w—0öLÉÎ9ÔtãÐfo‚!¯Çç¶¶H×ÁÖež-)„E™`}me;öA’–Û&{Zv7ÎÒDZ™‚.<ûUäõÀ~ûÛkç Cáƒp÷n‘ÏV%í¬|Ù³ÝíT,<: USQq‰/€Ò%Ì>=iÒq'í‰ÀÙDuw“£BðžPÛ5À—Œì¬ÄÚN×?=ùµìjx^ކ°Í5Ʊtã >½w¡îÆK¶h“B¾¸^F=s¦¼ä¸—µÇWL ^:¿ aýŒí=xTXÌ­Áþa¡B…ŠU™‹é,þʖB»±‹e{`ÌZ,8áÆ„fBtSÔ¢!²<­µ6+€£-^ÞkADº£Ñ˜‹¯ç ‰Åödd¡þRpYÚ8é_•M´o9×$Ë43_¬J¼ŠÂJ; ØâL«(LuüÐà»ÐRË®ŠJo¦ u¾Á^AƒŽŸ±Úesô±ÇŠ(ÛÞæB܇Ÿ‡ÁÁ\OÜnÄ߯·WJl,´fl™é(ÐW“”$»lþ!¹ìû•‹î¸—Wù§Ñ$¸„€„ë\I{ríhü“Ç#ÄJ¦ÏønÜw ýõDžMÛ ¹‡ u%üËÕˆJ›h~ögÛ¹×É.<|À&C]Ž€eШÈîÜÂb î§Oäaeì¬d8¹aH÷›EG%ÿ[ÂxiŽR\êÿg d(:#gž@Ö\˜…Ƽ`ƒƽ^&÷~7u_Eéû •n‘'î–²ü|/õ¿F%'»Øþãù.ñ‹€á” 8•¿[ét}_1÷éú+g±Ì0ù5å‹ðžѧBõ¦'Õâ"È*ðŸè …WVþ*ÜÊ Û“sü#ʆçwÈ:òý™m†«‡¥á§Ægzƒl( > ‹6Ì11GòW¼ù†LzE‹}¾~ï$9œ´‰›áh;œp¥Y9î¥ëáÓt\æø×Æ>ß<6÷i£ÚHë¤4»qzrPÏÉÑ…Oò+ÃÁ¥5éu ÌIö6(»‰¯ü“’Ãü$èFð¾C¸Ê^‚0›Œî€Iûs×܃°Ûý…ä’q¹ ú‹£S.o×®pÅ‘­˜bŲ’ö:åÙÑ”ÌÏ{V"¿“ü¹÷=²öA ?LPD VmØ6.’Ò!DðVê”5ûíÄh _…À1&Íô @ôe˜Oigåd¥ µì÷g²õtéÈg£H€q"ÑžœEê´p÷Ðýc O£À/Éç–‡/a_ƒv¨¶òñ5Âù§Jo®>)íTajËßWÇr»™üGÔ$Ï¢ ˜bk’D­Ç…GàîS”Öâ¦Lr­Ü— ¾èaÄo÷O_ ¦Fá…¶Ê~“îm”+?N³W‹tûÛ óŒp+Òüì„æìÿô0ù[h +mÐl† Ñ\ƒ½Ž´ðˆc¼K~CÞ×J{ òÒô&±ïL…óO!HLƒ18ºÐo¬•iÖšÐt#á>ˆ' Úº"¿–izí¬lÃkg•¢Çlqò)²³bÅçŽ{™{M晘ägtÓü Ãb«¤¹*3Mr$ PâA @úK6ç¡;!2` a¼pbpá"u ´Ï,Ä—#Z#§vŒ8Ë« \ËóªwMýlƒF w'L‰ÐB“sX~ËBÓÐÑÕ°E¶X*ó£‹Ð­õšmɹVØ¥Aa%…ÿr £ïE|宇‘˜Äë•ýÖîx–ã\ÑV{'lmì]Ì5ËÆC¨Ãt¨B“yËFiÙ©Œ-ˆùÆ›‡àr4+ÍX¨º"Gä¼÷;fá$ ,*øÔ0°㫜IÊ*kg%Ó¨h“ôð¸Äûйvÿ¯¸|E"?"pš @SW77¨i{ºäxy<@×Ò?³¸•ß3à%Àbyr4J òñ}eÿv#wå˜9{¡ÜGŒhÑ$7ùÌiêËèN¼åXêßÕVº\èò’_òcñ›žfî; Áb%´ïAsP¨Ÿ“.üåpÐèê8#W¯1¨¯ëñGhb_ß«ûWNXh1Þ…&¦;ê嵷³:‘FùG‹âc¾‚Ñ48¿-»ËI>DàÌÀ³T7·oiõ8¾ f²ô€#.ýè€ê5ª dF”v½%°H}¦ÿtyHDó˜aÇ:9;*o( äˆžšæ…®>zÚ3­æ=9á@EÏX‘MÒÉö:5Í¿¼øräRP>oÎú,k¾Ù%Tž_yi–vw쬊‡d—@çD Ž¨³ÂÅ·¶žˆyŸ1¬2°lÔ'åG#°¹Pêõ•@RŠú[Øeš¨ÅJ‡±Ð¶ODçJãYÚˆ gœ@ÀT©¯™\Ý4Ðçy$Xr½O?ð÷)š¡ N '­÷H©‚D€œíê°p¹¿Ž<_ ÊáŒP™²3ЙÃ6èÀ„¿(Þèü(}"@ˆÀ©ÔYá‚G|—*F†nCŸÑw•M]N÷+û+žÂý9ôízå&ܧmø1Äbퟃ&Õ’ºB Î }ÞÙ•…a^_4$ï0»Êä‘f®^=5atežé0rºb ì|¦ËñgÈÿºëV™¸O—²®˜Õõƒ@¦Oi"@ªJ Î ²¢(|ñÂCU­ø)Ãs¾W´m÷é)Ã!@tÜôÞh@aZÞ*þL‡ÁpÂ{`?èL—ãÏ’?ÿ€Ý§€35júΟ…%Õ“ºA N«ñÅ!꺼¯×£QïQ۸ƣ/ýø@¥,võ¼Í•µ˜*øŸ.E{ÌYaÔ»ó†­IP[_™úä:(ƽoÆ¢K·ˆKŠ™ Áh×s!˜câ¡4¦ñ1X󡉰D_®°Pt³ŒÂ*ÃÛj]–¥™Ûg¢žX‹„åc¹ç XÒý=YçòÒI~2æçè¸9Ja ò=ŽÅÞÂ4âñÑS=·aBã×àž‹rÖ\,zî^ë¹}LÜá(ã⽺/oµE™/mU#˜Â¿Æ¼ý«ëÔ¡±ºflÌp; ëàœº‚"P’@Ö\ðwÞ±˜¢Ž@ƒk•¬VÍÎÐÀ®¹¤ïÈŠ¦/.Î@ŽW·ÿ W›/E£ü_CÃÏ/6l•ÚöX¤¿ª÷CºÍ4­éýº¾8S'5nົ­æ¾åß‚i„_HÑc‚°†8c!X„cÕÃ.mµ)KÓŒm÷aŠàXäªòGµTIDAT«§ÂÕ!¶Í^‚KEéÈyÐ𼦅÷r…+Wc-‡Çè3Ú%?åþ^¯Cxñ`êàÞR°ãú!hLW]üNæ éÓ‘^ŸoØzSÚל€P\£¤`Xó”N¤€ûiSDd§Ó2ƒ¬¼×NäLGÕ!€‰·bªâºH N xߥik0ð¤Ú‚ã»´`M»ëzåŒE÷¾K_'—–Vµw±„éPÙðûʃF]Y'ú…ú„ƒ˜òë‡äD¤[;/Äq†\hÌ;!gë1!XK§‚‹à¶ÚU“媇Òçƒå‚JÇ„¿á|_ü²íÊS¥ƒu¾ËC‡º\²Qû¡Ð0Êür¹¦œæ|©¢îe!¦™ÿ>€hRb- §l´¯¹,:Šwzèê¥P2‹L,Š:dìà%}N>ÃSac3³´¦º^-5Y#Ý3º£áû­´¿sí~ºkª±-WNå¸É=–$C¼}gÌhàïŽü>—t©¿›s·î–Ö ñuï}xýýªr, +7ôü moeãaÉé‡Ðåq^Ô›Ím>ß´ýÄÿD(Êç˜ñ…áS6c\K7h%vÏ×'¦CÛqK¢·áŽŽný¶mßìÕ.ørÅþ3¢!¹éteÇ‹VhÄ9³Þ–·kº¤¼tFéó"L#3 BLÏcYyzºC“±Ü—C‰\Õe»™7Ñ}ƒÚà‡q/¿”D'5&m½–˜¬4Â5y]$sõ6,‹Î„68&º°R‹]iÜõ¶É W@6Þ™ Sj±òÍÂóÛ(Ý–°ww¬¨ ‚>„®µN¥MÑÓ%Âr‘›u¼0n—p/ãdŒ>»aÁÁ¬Ï°$ùRÈånXÕ7$ÕHê*ZÛ§Üp•ñÀÔâ³2Bw AØ€Ï^Z™òÈ0m´«^ƒø„Ñzb·yzÌ¶ÊÆ£pD .¨Â…Þçãô'1kçNŒN‡wDU/Þôk\A®{OµÄººc[äçZ—"î•Ð{5…œß†5 B¸OÚ*1_¥Ú ‡aGq‹PÄʶJ¤W ×cˆŠó[Øì‰qI¯-ŒŸòs‰È¥NæècEÅM®hêË(x¸”wñé~sßý‚¾›çqÿ!4#õ‡”wO'€ÔÂp¦®HöÄ,“Çèÿûž {,x¶G>S&͉‰É€æc¹kŒûkÿ™‹¿Ò¶Së‘êI¨ŸNù2¨kÕ–]}?:øwꀗáxŸ`ŸŠŸW³€·t~˜Øˆ¿¨(ü¶>}Îï$G…TÚÆÂˆ)Šúº¿Ó³&äàåøQž•s/7úÅYWÓÌ\ÿù†m;ñ%õ¾ M¸Å·`ë/Ùތ߰<óŽè8Ï4ÿtü•Õ¯žÆÌÈØƒ/µ †ydkÛÕº¢tðòš£ÑñH{ ËÿË4ÅI“kê{y2åYßU =q«ðº›‹ã/Hÿ§4#á Ï6îvÂÓ¾ö Lø{öÁ˜({„Œs ÜŽÁý¹-î싌“¥ñ'g¿¡e}MáÊÝí:ŸmÏŠŽ®Þ:7𦼠!òn)ŒÐ“: FÑÀ~yªšI-´ÿ•á`tü_4äÃüã ÌÚ"OŒ4fNOµj˦¤+WÄÿ|*<üãEñS~‹©AõT^øzì†ùú”im¶ÁT‹­‚Öåj_XìO¶’~xöúsEûÁŽkëpM¼öQR«·ŽájØ»i!»BUMù/ì£n…¶‚Æt-.“ÓR‹¬¹¦‚pmÁ!bNº:ç´'õ@½é)}a.úh_*ܤ[±!›Ð¯Õ¸ŽIü·ñAXÍ ‹‹ÊŠŠQË †­è.é7×ãÞ-ÃEÇ%^ŽÅÖ°Ìôê›`ÿ° _?wHw¯Jö—mÛ aX˜û!œä¯x[0eŠRîðªˆÙሔ'Ç–}çž-/…±Þ.”Þ²Ÿ[ª£‹ÃAŠ>yv—ŒNLlæ,aªÏ ÌÏŽœöl »MÃÌdtÝøÇ¡ãÀBR–Æ}Å~+W^«y'ßòf‰&лU¾­õE(±[ OÙ…ûe_ºé¹š+h”%þv=%ûN¼6Ʊ;aËÔBq"„Õ`4ž¡5'»Süã¸[ÈÄ·£ô§ß5ŒÂ"AÞ?@U޹h§m_yQ € vbÃ3ð£s¦ žm1^n·(DCÃpÛ“!ë‚ÇSÆB€(Ç>Jú!·åÂ4⣦&¼jÙæ(Ì["5£,ÍN»åˆÌ5r>gxZd¥!x¤EO{®ܯB!1ú«hkÎ&¦§1O1—bc ,ä3]B£éD =¨ãê­pQÖu9I°(+P-¹M›¶JE­a»TU&éífÈ1ïBS±/©1Â4@RR4 û¼/¢†Ìh‘UQö¾—P>–iv‚2Ò‚…QîÁÂq“ ŽecGÛ$pB°¨íB(oÛÌ,˜è‹®¶'O•ºaf݃–uq0Ó¼aq{ÀîbAª½í6œ/ñ/í `Ó1Û0 gâ6ªTW|ÿcÜ„©–íáö£©ixN¸*Ÿßý I€]¿œ8xžJ:Ž{éý(=©‹iZ›†õ”Zޱžû-.¼Ý"åÙGÉ4‚˜ý\!fYEö¨f¡û½tBɆ–äx²'¶_qÙ¤6hs¾SÑÓÓÇòyõnÂZ›F!Fƒ=,GrÑFê⛽ÞÕì WH~ªŠ2/³©²ûe›Éò¥Ù¤k$ÓBñfê€áhËÅÞƒ0´ä×(šrÎZ¥b×V:UÊ”×UXüf1îÁ[QþsÛ¸b½†Õó·<„¯ôdÇžHîÑEó"Äâ‡ÊŠ'Ú7Ÿ ù£7¾ß¥}Aµ7ôlCÓíí2˜3vl„•MÑÓ½iJç½*›84 ¿ç²\o#» !˜l‘‚EÑü4ì~'Šì£ ¹<ÑæõHµ[|k»Î‰©¶ÞU… cÖ¿:nrj}yÌUt¹>HÚ^ù)^-¥Î2 a*POÚˆ@ý$ð§Ò\œîK¸ >æä)¥·ãpWÚ±ªçÐ`ªtªš/…¯›æ'¸÷¢{〼ó«øWƒ‹.PÓïwœ„­| åZ‹äø˜ï7¹jñ!†“Î{,!¡U^ž¿ºÛЕ†™^GBÕðÝšl0v´; @ ™i™öë(_®aX˜î…-vüN¹ç|ž0Äk¨÷ïv{'´.c1rê; án†tæ@غA¦áµâŽ}T¦†‚ t›L!è ä¿ÕsÒÌ­3–¿M¬vŸ>ùá,¤u/Êú örØw¦Ö—]!·/œ»4*.á–3{'ò–\WA“¹Gæ%7+—–㗢3úOêš½êª g˜€Ô*À&bƒË¥Üî?„œ¬KN]Mª  Õ"02.±¿…¹d`“Ñ]:‡ð[Û8Âõ’D0Ö£°‘ÂðV†ßvØqÈ ¼Þ­VF‰"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D òþ? ¡; +°UíIEND®B`‚neutron-12.1.1/doc/source/admin/figures/port-chain-diagram.png0000664000175000017500000002750613553660046024307 0ustar zuulzuul00000000000000‰PNG  IHDRgµ•¹ÏKsRGB®ÎégAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ< pHYsttÞfx.¯IDATx^í/ŒÉÕÅý1ƒCCKVЂ(ÈŠ¬E c¶p%“ ÊK 4Zh2R@@À‡­F« ÃK†+#³ t¤†ùÎ{ǹ{·ª»ªºªº_w¿Óz²ßô«®®>}«~}oýéÿûïÿ{O›R@ ¬X¿ÿýïøÃþóŸÿ¬¸Œ(Úƒ...¾û²‚š¯_¿~¦­J~øjÛ¨¯^½ªºí:è ÚÞ÷-‚75ô:ø @ÉF¸‡ãŸk±ƒ-”CÔ¬³Uùš•„k—;ÎA ÇššLEͺª!ã_ÞøEÍ:[5EÍåkëžÏ(jÖµD¢æòµBÔ¬³UQSÔ\¾¶îùŒ¢f]K” æÝÝÝû÷ï÷l4³]ÛÛ·oy‹šu¶ºyj^^Þ{üxøƒŸºˆrsÓ!Ÿ’’¤·?~œ®³U=e|Pâãdµ˜DÍÓZo¯³¿}{¨€Õ-aüO[Vy%ˆH7¢fI³œNÓ+F²èh ÔUl¨´OŸþâƒ=Ø^¼hÞû÷‡L›ƒò3xnoo¯¯¯QXÞ75K*@Íš¸ð~Æ 0•š'´Þ^ugVj¾@¾‡ª-jN0y˜+tƒƒŽFãæææòòòêê*>^Ô,oœÇRn˜š1Øno(ÅòUKƒæàäÔ„ÅÃô±›×ÿϟ?ŸP”´YåÑpóF`lƒxž jžÊzIÍ™Î>µÒÅ­ž¡9õ5'Y1ì“æÊF *jNµÉ’ô»¢&.øåËF ñVQ°óîî°_ ŸëëCÈ{JlL“¸/_¾dížIÍG¯ÄlÁq p#ð=‘mj.c½cÔÌž=®Vwp,«˜ÿRR/ãG»o(´ü^·ò|ò))ÝXhD¾f‰eî6B;D Ôd>¹ÒÅ¿À¡)B·{øëññöóþ·8Ǩ‰ €À ê\Ïó©öë¹R>µààF¤#佨¹€õ&¨™8»UˆÁê„dPqX¹¸=^ôÄ?ì>PDÍŠŠÀ²AÍAGyŠš¢fX9‰=ÒŽ[|XÕñ8|ì.ù¹ã“5Ÿý£h2ðëz|M4Ö´{6%UH‡4*Ù•E´–ÝÌû5Ÿù°Þ5ýÙQ_|ÝAUB%âž1j’—'NquUCM/¯¨Ya½è†ç6ž¹Ç,VÔ Î;6Ÿ0ËE?i*›8N°g_“‚Ñ@~ˆ"´uOG ÌAÍ™¬·šŒÊúQrU«íÖ¿…šÁÄ<ëfôUpˆ‡°»mìð–.yŸg»_´gjâɈË6ü'AMÀu™g¨^7`ë•yÓ埃š3Yo!5ùpi“›1b€{DÍM* ßNMN.ò>Nù©ðàÌÂl°K;ÉòxËXP³¸prÔ`&½í¬C›íâp› Í/~ÄìØ}²Eg¯°õʼéòÏAÍ™¬·šÀ¤õkòчËf±¹\å`j[©Ñ@Ë›};5ãVÑfÆO5€I.`vªqVl)LúØ^ö¢Ôd€;%®µOFñÈø±|  Ž*?Eõêu–¯~;>ã§OŸ>|øP~“¨yZëtvgPqP_¬ùºc+êMª ãÇ„}-$Yh?ýôSaÊ.¾æ /,‘3Jñ€Eû±%¥h´+˜"[fü‹”äO±!%¨ÿƒôÞ°Ÿ^Ìñ“!ßqv$`úxÕÕôkNª™+O,j–WøÅR™á_|ñêÕ«|N¢æÊ rÉâÉø»˜ô£ãöÝwß½{÷.›á¾&l†Ô´'*[IŠäA•³˜?#>(8æ5I\ º2gΡ°líåA˜„ɬ˶$rÛËnõ5—¬ØsŸ«× ÈÖ%(W€Ô´-‹OQ³®šÈøËm2‘È4[Íâsjò>Úoþ ™ßypÚ¸ìÃȳÂ~͸c¾#ÑHf³Á¿/ò¸¤÷ÏŸ15ù+ö£ä%ñÅ^v+j-¼9Ÿ³ß¥ú)*P3‹OQSÔ cP‹Â²ËËšAgg¡¯Ÿ§KŒ E¶é Në5+½Àº†`êQýëÁ `÷ÚV£ÀÿøÇ rŽýÉà­¨9ÕìmTˆŒ¿Ýê²J’ÅùØžÁÁJ<*¡å2³Äú‚ñ½p´°‡Ùâ¼>|÷ˬ–ÔŽ^v;£¯É翜º=¼ø©¬íÔDnSå+‘8¦× èRý” øøñ£õY–hÒ=BË…=iöÖ‚ ¶Û¢'ñÚí¦¸|2þëʦùñÇÙgY²µDh§Z̘£m'X²¦]6Cžz¦jÒËnç¢&ÝíÁç ÒÔÚ”vjfïÄ zÝ€’ £43)К4ûÁޏÌÊòw2þ™Œgál—¤æ6³†<{5Ú³PO t±•âÜUkYŒš¶V{0‹–™Ø¯\5Øç̵†¹º0¼n«ú.ì;õ¦öºÙº7ßZØÈùéÓ§WWWÙ2ì5A_jÒ$\ä²™´4[ŠšëA[²|ö˜ý[¶fÿƒƒ ¦Z{~1ãOVŠŸÏ2Ÿ?þâÅ‹ùòŸš³¨Ùh´¤¶•Î×L,×_9©Éˆ¶uï³I¯ƒk û~Mž]§>7‹Œ·ën9ôºéʪ]^^N­`…éÑè<~üx¾ü ‹qÂd}©É0ìà3_lxhÿøn«qÒtËg¡³xN³µLð¥°T…dãO†Ú1Ÿñ jœóå?5gQ³Ð8ÉzÙí,¾füö™Ä•ˆ¶!–ƒ.ßÏÌ8˜í‰©é£dÁÄíÒ›SÛå±%]yΜjS[–©éûR“vXØ»Cjœ\<Ìž…³Ëgg©Éli¨Ca»Ø¯ÖgêýòéÌY©ÙR¶9Ž5ÛM·—Ý®‚šA[ÃkcЕ †ÝúHSÓ6B‚î­FÚÙ¿»»cìèúúžþ jÑíí-â¢ñO8 Çb< ~¿ô_¾|™D!ÖÊ@.³GÄàWìdž8»•‡gÄŸVf$(ŒÙú«Cþé|˜88»;Qž1 ¹[K»/5͌Ӣâ•ÌHÍf…Ëgg©tŽø·R·7=-‘®ŽUƒÔÌVò3¶ä_ê«3«þ¥ùŵ{d"OQ³Ýt÷FM¯HbaCްâ[Ù¬/3¦fšcjJâ .1¼ƒQ±ù¯± Õ {ì'|±šÏ£Žéá Ž²ïÙ@.nApÉJŸ°«8ïQÆCØ—ù-<08±ÙuñB,’烳øÄHo Mì.˜oÖÐ2¤DÕýO P“¿‘y¡õ–?82ÎW‡Á sq„6…×wÌ]uëÓ«jФi{i[MŸ¤fÀøbÕ–ÏýVMìŒxòc¶º†ïÙúÒŽRQsçÔ$ÕçîŒõk¦©É—ÈX¯'ƒº j&ÆÛ¥ÏFhQQQ<ëÁÓ.*ÿ;QǬ•çOØÃJå‘F×Í(’e˜¯ð¾Ã5üx/˜¤OiL ¨iøÉ6(¿‡=þôô òaãÂËÁÆÄ,Oššc¢ÙÂÙ-Cþ™-ó €}©I@ƒæ9Tö,xjx;jöª±ñ ÞâleôýúD£UFOM|Gåå³/Ò·V¡¬v·£1‘ƒ¨ÙÞtW?í÷e–mü~QÁð}Ð5n)_“_φËfÈkýÔôí8[vFu¬ZòN ®…AQ{¼µ›TNÍ [«ÿ ËÍÍ¿ëÖÜÄÔÌÆ»¬A±VÃö˜ÏzÃçÁ\ÔÜ95­+1±À’uU¦©É±B[RíADh-Ã÷¤z3¡õå1 ™®‚ô-ì…]À×äøŒ½V¾„šñ°ìòÙ¾?•0ê g‚ý-mPõ3{üÔX]5Ê©9Viì g‡ƒ=ƒ¾¦ç«u²°ÂNZX£¤Š ¦5[,6 œt_fñ5QD6hÜ‚Ä^cC ÓÔd>A?e°„Bܯ¹fj’X~óÔô´ô>ƒ=ƒw7AMFkùtŒ¬PÃÇ|Í q3£ jYYª©I/bâs Z˜=ÝM?Öª:§)§#´Ù峃çE˜c¬µAF¾Þ1Û“’¶i&jNª]¨IRâ_À,d7ç 5ƒ0 ôÙchuÏzyc-j–Xf:MµÝ·i.j¢ôdê3š߯P0MM^'gž EÀ¿¶Lâšû5+¡eO§Eh«©9¡eÅöA¤Fj’剭/IK„vLÃ`ä-ðYøl·P}#´¬±¶xØI³çìanæFh³ËgÛŠ}È9p…ÏØ×db•ayú®ITÝúı‡êª1‰š>ÚœÑã°šAùôVοº”¢æYP“§ á!üP{ƒg^î Fa§A‘™pôl\lx=9ʘ8ð5}‚vé³Î> x®Øx4ñøÉ£‘UÎFTSÙZÿ¥ ÄGi«¨,@K„Y¥G-ã¨VŽNâ0Z6RFtêæûG5 Žâø¦|jÂ<°ê´_ÛìÖ[>§Qƃ]ÓËg#sóY™-þôæmZ¿‚|°m¯-ÔìU5Ê©9vFš±™ ©Ä× ìͳ¶Žˆ%G‰š'´Ûå|M»Èºµ€8ã­]¸^9dgž”Ö›h!DÔ±ã³ÿá'Î3±™qëÏjœ]£ÄºdÐØ`zÞi¢ûñ…Í„ Šgž”Ô^¦±Ð©l%;®Àr¦aXfB7‘ž+A&c±yT¬!½seòÖȘÃ× žÿ`´KQg«ÌXuð.,3éeð>ŸjŽÝV>E•W ÖXHÉÌ“AC²'HXkMðë.—ÔÊé—WŸŠ”¢f»%WÛí ¨Ù~µ+Ì!KM´ã„þ †Ã°OŽ?ù¡ìwôwÈV'HW3ò‰™m0@Tæ`.2ÀUPÿ‚(ech­_“Sñ'À†|µ.O"èâOHL°‘—ðñ'%DéerB#óÁ—åÄÈCp¸õ\"™bG ÞnùšgX tÉR@ HR ¦wþ¸ú ÉgxcY¹P‘QÓw:ò'ïâ;Ì~È.~ÂÔG‚™ð;Ïh¾&ÒW5KMG餀g¨ÀÔ¤çÇͨɰ­ï¡¤×8HM…ýÆ=¤#À‰gƒÅ¬zºpjQ.웳 jÂMÖ;Oΰ6ê’¥€X¿ Pӓ̨i_¼DžšÞDbr1Ø|`–ÂdvFøŒÓŸæõâûØ(ßUP3k7ê×ÌJ¤R@ H9Ha ÎX¸z{Я9HM ±Ú)k5_3 ¦Ø&tàÈ 8úŠË亻ÈlÎ ³Ý 5ñ@AÇ_N»äÊ08ìªÑš‘'röñw^/÷ÏqÆÆëp) ÎPŽÔ„zÀ›ŸûaTcÐÕ“ˆõ?éˆýü⣯v,¾øüÊð8çœì‡šæ›ÛXä«EÒû–×hOdE[Îñ`ŒÝk“R@ œV¾Ôä4ìhâ8žvÌ×ÄU“©H‰v’Ñ]Ã*¿#7þd|åwvv‚¯FS¤÷]­¡?}Z‰ ÏžŽÐú‡Žà¤0ÿŽÉf¢&¼LC&ˆluDüi>hÇ QVR@ H© R¤3Ç)"¾ž€æ˜ÑbPͨ9¸Ò°ó2h‡ýTNâӮ˂<á’rÌQâÂ÷¡MS€+3­ÞÄaÍ^ߘšL€ gVÜ?¸¼õ~g|wDzEJžÔIŸhj…Qz) Î\Bj–¨g èn#5gjÌKŠÄ4›§&}vâSƒvsp”Å3 øãþQ‚Z× _,—½1põ,g|ñ#¯,^Ê(7Ü2ò¤¶Ù³JhÏJøÕž¹˜•Ex½Üã9RùíTLÆ“ZcðD妣”R@ H@ŽÔDÎh¯¬1X…à„ÊožšÐnÌ×ôëJPqÜ|áäÙà힘€©ƒ÷†.¼ÝN¿Ø„1,èæ ,bÝÔÌܯOá{§ý!¾_ÓûšüNN¥"×y]L†?qjNK;Ñ mQ§–R`ë ô¥f°Z­ÁàÜ¢햚ñºð÷[wÈ÷•Sú`TÕàýˆÉŠè/zjú/<ÝÍqdæ¾ÏÒÃ?œôzK¨Iúú[9IM_ž±-l:{R /5©ÌX'שtÛ-5éÿVóJ “|–¡[fîZv"GbÌ‘—"CNuóˆ4²™N!ÿD9}!6:…Hï‹QBM:Óœ‘ÂÍj“š¾|ìD§2DW H(05×&Ën©I.ÛhÇà'èeËYô2>*¾g…Ô´Õú‘žnŸy´8µumrhµl³1Ö,U¡¯Ij²l~#¹ãDƒ'Z›ª1¡èG€ ~²;¤¶ ~éßIL_3˜kà—f+¼S¢f¡PJ&¤€J¨i |ú…ÌDWBÅù ¨žïìÄŸŒßÚÆ¨,Ž‚ÇÉYØÃÅ·YôÄÒl…wWÔ,Jɤ€R 5ãUÌ8K0^Å )}¿¦/õYúÜÌû„WÊ8-ñi3&K³•Ü`Q³D%¥‘R@ H¼徿ØÛKÁÛxÔÂYspamQn ‰¥Ù—*jæí@)¤€R Dj2èê'Ç3jJ_ž¢_”ïáó5ƒÄ8Ü÷kúS˜;¶4[ÉÕ1Í)©Yþ¦4Í×,¿£J)¤€8•…Ô$ má6ŽÜa×#üBöqò­MŒ¯ŽQÓ#ËeÚl -‡Ä —Ú&ŒK³Šv2jrUØÂRŠš…B)™R ¡ÑR(˜ÜJè>øa÷J™â×Gý¾ ÿä"© ÖÆ…G›—¬³†£Ècº¶vŠÄÒl%Ê”^aI^…ixø®..—/Üd%jj«dR@ H„¤H‰Dœ×H·Ÿ-ê™8¼œšc™€Á’@8»ÍÈ,)ùÜiN@M[‚ãš°eŸbD͹í@ùK)°o¸5[Ýì•2Œ‰ô0®'à{3i§&»*í¼~±‚l±—IpjâÂ0±”Ï/…«¥‹šËXƒÎ"¤À.àèSÛ²×ȧAppgU;5Iw_Ú¬[•½œ¾ NCM>M/¼ôöáÃöîrûꫯþô§?ù=?~ì+„r“R@ ìRå«™ #´ƒ=ˆ_“ÅÙNMÞ[—m…wä4Ôäk)!8źüøãPl»ÿþ§OŸV¨¦Š$¤€X›ˆíÙ*t…Ô¼?ÒÕ øÌmh´ýŸß~ûíÚÔh/Ïi¨™-7 øàÁƒ1j~ýõ×Ù”@ H) ª©É~ÍxëŸÿü焇sqq±¿[°RjBh<Œ݌ׯ_ïïN芤€s+PGM"Ó¦TúBþë_ÿJPó§Ÿ~šûŠ–Ï½Ô Ò*<»¼•èŒR@ ìC˜š|é‡mñ<@CæXæ£GÁùäÉ“}ˆ\Åz©9¤Uxv—†¨‹’R`bjrŒmÁÜ =ÉÎÔ Òî2<‹{´^jŽiž] jéR@ ìR˜š(äß´å×àT“ìͱ í.ók§f¤Uxv—5Y%¤À2 ”÷k™…Sêã í^ók§f¤Uxv™ª¥³H)°K ©ÉÕÕËW¬ƒ´{ Ï®šqVáÙ]Öd]”Ë(PHM.<ëè±ïƒc‚Þ½{ ÚkxvÔôAZ…g—©W:‹{U׬‰5`ù^­ÁmìeÉ8wžÝ5}VáÙ½Öd]—[W!Y£æŽÃ³ ¦Ò*<»õz¥òK)°W’5jî8<» j2H«ðì^+›®K H}(À í¾Ã³Ÿ©¼`Ä¿Zd ßÿùÏþêW¿úòË/×P_t€ïÃÖuR@ Hv¤Ýwxö@ÍôÛE ê'(€ñÖí¦¦¤€i~øáÿ.‘u~ÿío‹Vÿ®ªxùË_úZ×á¹×‰uŸ=[ïç‹/îýîwë*Š„ ÆÑ÷~(7) ¤@¬º¨ä¨T+ðïÿ»£Q}¦&Ðy\]OŸRÞ¼5;Ú¡²’R ¥š›‡®­—*.Ï?þñU¢A:ôBv4/Q³“Á#…¨ÙÑ ••iŽÁGRiª\êOÔ¬ä\_OZÔœj¾J/¤@µ¢ft¢æ*xIúŠšuF¬£¤€¨P@Ô¬ ‡ˆš¢fåè() ¶­€¨YwÿDMQ³Îrt”ÛV@Ô¬»¢¦¨Yg9:J Hm+ jÖÝ¿­RóîîÞåå迲›iø}õkֱޒR BQ³B´ ÷k¾}›šœŠ_I ¿oâ#jֱޒR B:j^^^⽘xá×àñSö­aS‹Š3N=dÖô[õ5IÍ—/PŒ?d¤¨9«é(s) 6­@55é² Â,Ô:­À`äYwìLGm›šàb‰5g2e+¤Àh¡&H†-~›twj©5ûKékN¥&B /^Ü{úôðyþüÐñIè¢ï{ð“g0àã÷ A°§oàWÚ´Dº)°Z¨ù¾{h6Ã8í 5×·Çíöö6;ïîîüNü‰Üƒ/¤&¾ÐÌÊñÇò,v¸ýd‡£c%)¿kgäk^_@ûøñŽøà 6ì$ùð'>FAë7}ÿþçH j–Û–RJ)°fZ¨ ö¼@3ÅijrÀÞ±á=4¸ø¸õ0‹s@à÷8å°ùÑ+$4!Jfc³>Tì±Sçž,•¥‰K2é6m›š`X<’ÖÍúíííЏ}ö+ýKlô8yŒ‘<ÛÕÕgjÒ4Êöõ2µ6Ð$«Ub) Úh¤&œÂ8Në©i ®®®àä ­“\ úGšƒ¾&ÌL9#!ŠÌé^__3 â'œ” Ž þ!ýÔmÛÔôO"öÝÍzjŠ’õ”b`¼<Þ¿ÏŒd×;—tO瀥å©íTóUz) ªh¤&Î{ssCÇÎÊà©I˜Q¾„ÝÃPm–šæ\Z¤¦-²Šîâ)Hfž(‰–p»PÉmSsp ­AÈS“Œ™GÔ‚´þ;T…/kGù”3±SÔ,´Z%“R ]vj¢ AœÖS“_PN@ôÃ;øˆÕÔô>"ù°9Àm\’ÁSJºmj–*¡¦y“¤¼Ïãí8|¡'jÑZQ³Ð¼”L HÕ*Ð…šAœ6 &NAbùÍœ¼:j$ö]/µ|Q5‡§S}MÂÿÂ…¥‹i°DV؃ÎÑ™x©~ÍÕ¶,*˜Ø«]¨Äic_T‹7KEM3­%ÞJ=uæÉ±:ì×$ý°Xްå¼¶e7ç¬ÈDæŠÐîµyÒuI*Ћš§å UëæŒÐz©É¯€ ækÆÓ7ékf#´Á$ùšy_“@ È4bC¶á™øØXYâöR5WXñU$) *èHMÆiÙTŸÈ¿€g؉”F„B£{ °Ç_ó)§&3 –ñÃ΀ߢæa½,É‚µ8Œàäd`V6°9Œål~âæLN§|ÍÊگ䀘®@GjZœÖS“£[±89r's‹=„ôdjLMP–#€— "ž¾ÿƒfÕ¯ù¹ÇØKÏÁ¯HãirXÄÆ“/Kžã=3!SÚéµ^GH)P¯@_jZœÖC´#ÀˆO"Ó€µ©“LÀµ<5€5ÖŽ-°_ÓŸÅ£5g”ÎÇÅtÎò5ë)¤ÀDê¨éW¼‹Ohk×ùŸàüq@P¼Ö]Løšø•C„âüAY$H,¤Çs!Ï÷q– ;ƒÅü õÛêÌ“SmÖóŠš…V«dR@ ´+PGÍöón=QsEž«¨¹õê¤òK ) jÖÝ,QSÔ¬³%¤À¶5ë)jÖYŽŽ’R`Û ˆšu÷OÔ5ë,GGI)°mDͺû'jŠšu–££¤€ض¢fÝý5EÍ:ËÑQR@ l[Q³îþ‰š¢fåè() ¶­€¨YwÿÖBM®ãÃVG ^}wwX/Ø9ÓÔI”$Xr–ë¹s-¡ø×ŽÅÐÌ“:#ÖQR@ T( jVˆ†CNOM ˆ«þr•;.ww\Tðg/°dÕÙ^ô ÞÄÉÞY6,Î7öžÎ.g5ëŒXGI)P¡ÀbÔÄz=Á+6íOüTQòà¬.Ô%ŸÂ’œ˜š\(ò~$—~ņõI£%©x“¤¸AQ¾f¡a)™+W`Ijr‘X¬"ë7®L¼ó¤B´x™ÙŠLÊ9%5AG2)~Û3B²þ§%©xô2»¸’ÙLäk–®RJ)ШÀÂÔŒÝA¾Åðë¹W\ÔQ“¯î{o%ö#ð9èk0ÖÝȮнðb\ë(MüЬøJjvµZx–cûÕˆ×súùb0¢K÷”¯RáåŒ}DÍŠ £C¤€¨SàäÔD±ù†“¨ )–VO/¹Î_¹Ô;©Y½ûTõNék’I%ï­ |M{ &ÍmF#{•&¾ðEÓØ,œþÕz.©ôkòÕÈŸ4¥ŒÒúÀ²oЫö5§š¯ÒK)P­À¨Éyjâ_Ö뉟üë¦ÁÅcÏÝK{¶½GìÐGô­V&}à)©t&œ°€š<Ðûm¤—½/¿Ò_4zù=é_.Zÿ+¼ÌcP><‘EtYl+Xˆ.YÔœÉÊ•­±k &|DÃÞ Æ?ÁB¸›ãƒ?í…¤&=Kü„ѳó5«© %A,œ«ßcÕü?EŽýZNM:— éÈÒ¯e©Ê»EEM5mR@ ,¦ÀÂԄȸ+7¼“¯’6(bOì/œx¿&d1jïÅ<£~Íjjz':³×ð(÷g”Út|A 5[ú×rj²ü8©ÿÐëeIHM ¬Ñ@‹5:‘Y¦æçÈ›û´óXÆfáb%7(’šþ¦<#j’OÙ¸e<]¡Ö[It„~`¾³;óèÑøÔᓍÉÌã§æØp§¢ò5³õ\ ¤€è¥ÀÂÔíà8ÚöæÍ›p:Á¿øêjÆcqψš fÂt°4â¯>kóU@£`ÜMÌ'Òê N4øë$j¦£¯S'̈š½šå#¤@V…©™]ˆ@Ô¼wq‘™æHþǰõø9F¹CÖæk.¿ÇÀ)òIÿ ¬–SsÐWùPBúµ¢f¶Þ*§R`ÔÌFhÏÚ×WlˆÓlD¦ O©éÝJKÏ„±C‹6r'ýë$jrŒCk‡órDÍS5:¯YÖFM ù9:?¿Xc;1’ÈF;5¹n{¹"8lތЂ¸\ Ç•ýìÝ2Oìä8özþZîkÆþDA1DÍl½U) N¥ÀÚ¨ 8ó„#f±á çnrÐ,G RiÎb•ëeäJ=6¦ºÙÌK¦±Evø'¢ dyÉÄÈÁû—X¤±3&~ VÿAžÞ›Œ× ²òSb‚bk í©ZW HXRä#8 Ë£OrXô‡…£&g° uŽû~ÊU²9· 4‡‰+O) XŒš\‹ ˜d™¸)^Ë¡¶ø$Ëûá˜6®g[h'¢æB+³—<ˆš…V«dR@ ´+°5Û‹ºªDMQsU©ÂH)°¢fТ¦¨Yg9:J Hm+ jÖÝ?QSÔ¬³%¤À¶5ë)jÖYŽŽ’R`Û ˆšu÷OÔ5ë,GGI)°mDͺû'jŠšu–££¤€ض¢fÝý5EÍ:ËÑQR@ l[Q³îþ‰š¢fåè() ¶­€¨Ywÿf¤æ³g‡×žèS®À7ß…zöìYݽÔQR@ HrDÍr­|ÊY¨ùý÷ßsU@m |õÕWu÷RGI) Ê@ëtÿþ}<¦k›¤Dƒt>|(—:›òÞÇ/´Õ*ðîÝ»¬ÄJ ¤€hTàÉ“'õ: <|øðÓ§OúûÃÿEµ~Pöî=IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-provider-overview.svg0000664000175000017500000007661313553660046026454 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-09-21 16:20:48 +0000Canvas 1Layer 1 Compute NodesOpen vSwitch - Provider NetworksOverviewInternetProvider network Controller NodeSQLDatabaseMessageBusNetworkingManagementML2 Plug-inAPIManagement network10.0.0.0/24Interface 1Metadata AgentDHCP AgentOpen vSwitch AgentInterface 1MetadataProcessDHCP Namespace Physical Network InfrastructureProvider networkAggregateInstanceInterface 2IntegrationBridgeFirewallProviderBridge neutron-12.1.1/doc/source/admin/figures/lbaasv2-diagram.svg0000664000175000017500000005221413553660047023603 0ustar zuulzuul00000000000000
Load Balancer
[Not supported by viewer]
Listener
Port 80 / HTTP
[Not supported by viewer]
Listener
Port 443 / HTTPS
[Not supported by viewer]
Pool
[Not supported by viewer]
Pool
[Not supported by viewer]
Members
[Not supported by viewer]
Member
[Not supported by viewer]
Members
[Not supported by viewer]
Members
[Not supported by viewer]
Member
[Not supported by viewer]
Members
[Not supported by viewer]
Health Monitor
[Not supported by viewer]
Health Monitor
[Not supported by viewer]
neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-flowew2.graffle0000664000175000017500000001346313553660046027454 0ustar zuulzuul00000000000000‹í]kWÛHÒþ<ùzói÷,ˆ¾_f3ÙCHfÂLB²˜$;s8g°h#$G–!™9ùïoµ|—d° ư-uK­V×óTWWW=û×׋ػ ³N”&?=¥>yê…I3mEÉÙOO?ý¼mžþëù“gÿ÷òÝÞÑïï_yí8êäÞû/ÞìïyO·wvvÛí8ÜÙyyôÒ{ÿf¿qäÁ5vv^<õžžçyûÇ««+?p¥üfzá vvÞgi;ÌòooàbÛPÁoå­§p›ÞÕ'šG[Q3þä‡gŸÃoÏw›yt¾ ¾…Ù~Ò ¿>ÛqGád”äáY˜='Ïv_UàÞQ3Èá’{WV ²,p_~xÖÉ3xäçÐB?½H¢³,í¶ýwðí—,8=Cõl§_d¬4UÖgšÏ”¦ÌŒy¶3¸t¯ Ý<Ýmý¯Ûɇ·Î³n¸38ÿ"h~v·LZp»öyÔë?{¿˜+Ñœ6㯿ȖG¾oyI ÒR*M¤ÐÂH±åiÃ|c¬†CDÊä÷ïÏâ.¶êuiTª²ÿrT~Ðál¼ï{Åù·8•uò3 ó ÖIР—ò£ðk^­÷û«FMé8½ª= ;ÑŸaMé4É÷“Ótºp¹5²õ®& éL\²/ËÅF5ŠÁj†ƒv\+1z•FMHE-NT{Åñø­G§ë‘bTŒ7±AçAk¼c—zí¾-çÚå^.¡R&€iü=É·Pw­‘˜gù)=†aÿší3 vÜL›iàN "z?:Ýø-4yò×ñ)Œ°ü$>>%ǧIŸÍó ë„9ñƒìŸß¡X3Ó Êýó8 [LÊã³, ÷å$î†ðYœ0¼wœÂÜqF¨}Ü OóàD3òä¸d­âßàû‘Ggçy˜œM@AòäIј#Þqó”y0>Ýî„ÙeÔ ½$̯Òì³ÇŽ]±UÞǃ}zj™O•ñè6L|ŸÂ>½.üâ^•ÓÉ÷9ÆX³â@¡€TÚpÎ$µ`Jp_(!§Úªˆ.`J*k£¹¥J1i•ö—ŽI±D[i^‡ñe˜ƒf6+Ô8˜kHÖ¸ª>ç^”5ã(½%4í¹á=!âc%zENÆO÷–øÞ*Uši¢„ˆ±§Ô<«­É%åšJ£8cÚÁjjfõ5…¶Œ!‰dšZ:YsàÖ H÷Ò, ³Ã u'oQ!©¥ ïÌñ­ÆÐË%t´ƒã‹B˜•åVY]èÌÀkpb ¯@kÝòÊÇÈ pyWzÿ¤®#‰o” j²7k†í¨¼• ‚‰º Ù” Æ .n<7(:Ûð":IãÖ¬0¤æF!)kPèmpŒ3þº†ÊëÓ§ןM;»–‹]/Â&Rçuð¬­/_ v´àFE†tæŽ^’¾¾æ”Žf‚+iGS;znl^XͽcÅush°î¾½¢§ÂÂäÒ©°…Þš•ŠÀPk‹eÜÒp†¸΋"ÜE€J܇$E¨í>-Ѝ¾öÛŠ²$È»YiNS‡OzÊn¡ÿÙ¿¿É¸²8ç)í¨¬¯µö©LÎÊyŠ"çÝçéy9O2¶DÎC^C^C^C^»¯™¥òZ‰ÃztU>†Ä¶Äf¤ÞHb3ŒSA åÀNÔ6;Þ* ) –ÑRÑZJ¬Ç[Ê´Ý@—¹u%ØØØæ#69?¯Ma–7Q®»•ÿu´¦7a°¯™Šô O<Í”5û™IAÕÂûÝÄ 0Ø| ¸KŒ–›b¹†QæKkã@¢† e¦™9a>'€¢àÔ2¨­œk˜À Ë©’Ój3Ý_ç6ÀÛ(Å«jŒSk—[.¡¶Õ¾âZ[.±†üíøî¾6rš8¥Ý,›èÝ‘6V§·½ ς淉â§AÜ)ܘF…EÆ]?®“­Q<_K>E­µÊfe&¸á¼(R1â/ˆ" Ì6(š%RRb ð‹0´ºt¤4Î66r¶!ÐŒ†f4œmàlc-fœ.ÓŒæÜª´”–À0mWk3Í'‚[_ÁHæ–rÊ¥U^ãyí¾xm~'PÉWÊkS<¼â´ÛB/dAdAdÁÛ° Y& Jå+Ë€Ù`Š'¨už½‹;âìîA9¢“òòòÚšðÃŤ;2_Ë dáŤÒ‘VÕÅ$Mg^Йa) tpA§² ó>Èó0KV´´ô…¾Ú…G‡|å)ì¢I5ñÇwBj[ƒqÓÌÀWDHDȇŽr©)VfQ„ÔÎ<£¨ œZæA:¹¾"B"B>T§ voNA‚úƃaŠ XçùW¾b B¹¤v¡#˜ï¼®‰àŠXEÕke6®}étíuÄFn (Œõ#S!‘3ãVÏl?ª9;nõì÷£š¸5͹ëdÎh¬¹À¹Ý¯Þ‹,j…Ç÷Ý´½V}9É®Ÿä^þû8h†.\ ÈNÚF NÊ(µ ÀÚR©„´†8 oÚòÂ,WUâ\³õÖ‡Jœ ‰‰‰‰s ˆs?éäAÒ =vÏLEðÒ‚J…›¸IÙw™eÌ.2l=ƒÈ£ &AÞ¥™Æ\׸MŒÙ™‘ˆuü>„#E"E¢«Ð|®BTüýøÉßèR£…• •æ6t‡Ž°i›#T…ðOÎTÅLƒÎƒªX¤4¤4¤´9)M®rW…É›PÚ×TIÎ4ÑŒrm‘Í6’Í8nëÀmHlHlëAlz™Äf­Ï¨[#3BY VHl%¨³¤8MÃi²²Ù=²™Zj$gf}Ѻˆ‰ fj†löh¦i¹ûž0. ÷ÿ™9LËqö›Ã”Ð0øGÒÒÒÙœtÆÜB[n\µ’㈹ §¡ßȃò¡è7‚~#HwHwkJw¼ »¥P+—¶Âw‚`|ì͜ÜÃáI Ií>I¤ÆV¹Ìær³ 7ä£ 5m-ú ÿÒÒÛzЛÁ° 7‚)ÏCVÒ‰.œcŽ+ã»ä€£ÖPÐ ¶Ì È'06·’9¿‡:‰Ax^©)ƒÅ,Ãð¼·ßöË16¼£n’„ñÚe‡9ɶónrÏj¤ï|唕†Sf•tÖ!&@–³¤†i%ÌT3«IBj”â t*@‘@‘@‘@ׄ@÷eA¥É:²(`å=³(‡â…Á–mÕ¾gWZI!™&E~Rn·<¾L–Œò’L ^éxÒEÃNA\w·÷­X:sl~+VLì›ß %·¡úYÞÌËðÂÔíysë+s¯]”÷I Ö$;‘“ÖJS‰Ú›sM#ÜPM¶'(±¨|˜æ…¨—Öz†÷d×/ª=Jo Çhx„ç•ëeËÐLn±Hx¯ ‹\{­™p䤦G€ÖöˆÑöŽ[Ò;lø:ðâˆ?ì{”P Áæ)ïÿº!ƒÚ'€ð*öN¾ãEâÔlˆ”:wtEa>GŒL¤Îw¦l>g ÷i`1~§ñ¯–„¿×ú’L™bÕù ?RüÁ‰ÆÆÂëšÎ/N;¬¯½„ùUš}öÒVxï:¼ñ ÷Siü —æ+­©âúÅÑRë+·Ô€V x«NÀ00ƒ1ƒâ¾bP˜ù+­6…{½Õ·¾§ÝVõmc¦[Yå)‘)7?H)b0ñeÆ`âDø\»gQ‚Y)L$¸>«àÀv„Jbц…6¬åii6,bæ´a˜¡¬”–1Å´²weÃ2>9pL mÝz'ڰІ…6¬[Ú°¸ð]´Ð†ó"”»Øò•>ô(%JšI-¨‡jŸŒûû)³å•Éu°a-wCTý*‚­Û¨9m²eÍ8ÄÙÆƒšm̬/„IûJ}· Œ+k¤ÖÆ.ADùΊ¢¬Ë;°= *£Ü•ñÞÖ -A³õÝ©»qt–Ôiµáœ¸žæÁ è}¥ÄýºÆrKú~!—aß¼¤gÁ=î9e(Ð!|˜¹ød§¼#õŽt jûc7F0í̸eL¢ÅŽè¥ÛƒA Ai-@©ƧÛ0»Œšá™<:ĦþŒ¦ 0éºWœÒ½-øœM ¡ç<3 y¸sžÍH½û¸§<åm„nÇ>3ÜwÁÓÍm`º9MÙ£L7§­ à¥ùœaø8±TIeæŒÈÇ„1.S¸gˆqxÎb]¬/5yx%Z©¬2ŸÄ=˜¢G²Üˆä†ä†äöÉMä¦1OÏM!F)[}HL³p Ê| Shæòñ&œ¬œÜVÌ—Òðjm®oìôµå5 Q¹@ˆÊŠvw!*¥òÕd”Ú- âùëƒzÿf¦æÜä|Ó”R£à…Róy! „Q+Ó IK¨¦™’ ss¢âŠÿœŠ¿-Šÿ¼9úV ø“…ÿº<¥¼›Üð©èæOïù¸Э+¯¡â¿P–².Š?W·YéFÅÿAü%*þ¨ø£âŠÿz(þ¦Püå2—³á|—¸ÓÅB’¸ÊECªä¯CG® täâD<6G®Þfuj¨ Q '6Ÿë©PËh©hmêÁk\O.B4#׺‘ùùæe>4vݼ6&WmìLß±«\Û寬‘3´v¡µk3­]ˆvÓ¾Ô‹˜YÜMGö÷¤Àn¬’0ObÚw[l¥à vº—gÒw.>’@&\v‘jÊ®iµËH'mý¤}¼¹g×VŸGð[üøý¥¡¥FøEÜ-8¡T¸Üxe¹×ªšHÀ|³è­ÈO0 &B;&Â$@# P%™;­²¨1u¹h…ÅTz«aQj‘E‘E‘E‘E×€E1íMóP`KK¤¤Äxv Òš5yŽ.gèr¯m™×Ý‘©‘©àº;[nè”RLRYÃkv<µÔ`+ƒ¨ÒçHk&³”3K! " " >@\j~©ºýD‹;\ãäî9\ Æpr‡¼†¼†¼¶éÑ©úŽœ‘®õIK Fk|åT·Âù=Ñ«½ +^…ïƒ<³dE~ˆKö.¬"–í]øÈrYýy@*º(@V}¬k ŽNÈàò¡#¤B„\*Bª•#¤\Z*Z‡ljôÙðòaîL©˜ïîpgJÍ~#}ÿMYi8eÖô<‚Ê%Ý+îLY‰§GŸZô©Ec.úÔ®-8·ûuí¼i¿œdk¶…é qÚb±µnY¶Ì›k¶Øú@y“äMäMäMäÍ5àÍý¤“I3ôè=U³O5çHáK©o(%Œº˜àcËãTû.‰6…“„K*õäÁ Õ¥ÁV¼nià–p sÒÃ4U!¢Þ ¢>| =í°¾¢¿—^´»y褭𞴲uàQø…s¥.%—±ŒöÜ+%œ±KÊ(/ °)úR¯t<ƒ¢‡a§€Æ»óÍìIÿΙ=¡ŸÃ9³uu»yÉê8„q…r›ko8ȶÂÓ<8Xìã­û78t-6rÖMñ.WQÞ<÷¶½FŸnwÂì2­ó ̯Òìsçxž‹Q·_Û;Ê‚ÓÓ¨é9H;¼ :ùΧ°“{f˜Y”zìzPþVÅ¢Éw>¬3šruÉ¢Ö82 _øðl£4á"ÃôWj|3©ðŽ·oø_šÕV.°”¸¢·(½ê¸Z£âõ Ü+íÎQ:CùÑÓOÕZaçMÚü+ÖöÚXéQ':aÊ”ÛËrp(…õëI=<»€‚{À„y˜ÍPü·0l¥f0ÖŠáê]oå.øf£;M+%òu~Ópé•<.jöe·ñhµø{ø9+6b ÎlÍø…W7^vÚ‡Ö¦Ý|úßM¢‹ +¨$>îl3ÊšéÛ(yuò’8PSÿEÙFPÄ*'>)•‹f¾J΢¤Ú–VšWãÌ;©[«}&gc†žÁ ØÀe aœ1QºaQŽVµ£™ÊO 4ñó4é© [ß8GùHOã<³>öþP*Þ¦­àµhõ˱7=¸(#TmS²M”GÍ”üH„÷×s•{“Õþé·AžŸ‡WÞo uWÐ?åŠiÎþHï²të‰Ni;T€Ò0df¾¸Ã¦ðž>wf(ëÄyºà4^¤yž^¼ 2hÁh<ŒYtû×;Ó ŸëþwA'ExY½;¼N³èÏ4 bhw””^õFM˜¿´joôb7ÿ³õú×ø÷O”„ÿyÿ±ÿ:Ýÿ~ñïýÆ«óÝówˆ¤'Ÿ~›g{ð»õîýãüäõÇxÎï½’GGì×/|: »ûŸ^í6¾@™Ÿ¿ÆW{¿\ßü7  ܦ{ª\wý÷0¡É&@ºæò½ÚëGt;3ÜcBY¯»GeÖ3P ¥•>ÓR*M¤ÐÂH±åA}c,è ’hŠâ÷šàâax äÖHÇC ‡N \åP8JÛ«¸ü$–/â]:™¢ƒ†Aë]»^Ó«Ý8:KfP} hIì±i}õxà(Ê«H¸$—AgBiéÕ¸½h†3hôT½Ýf]΢é•2àLÐsÖýD_ºáhJ{ÍÓ脯’<Ê®ëö³*Ÿ¢&¨ÓQ}¯›eÐEOVuö•ýÕ×v€°ý·×Åc:݈‡û6‚l©FÞQÜy˜&…¥dËÓºb8;˜°¢<˳n¸3qþ°ºR^ÑI0vzäý6~~Ò9íº‡©kÄÁÉëЉwy†$Ë*TŸ¥ó ôí-öTJ àê7Ô-˜ºÕm¢}mW ×Dfºô ÆúŠn-S”silMý‘¦eMÕ—¼0Ù*j$'LóR+]Ì ;µPR³°[#\?Ìr»±2bÂî3· >Û1HÞó'ÿ7ç)E{neutron-12.1.1/doc/source/admin/figures/config-macvtap-compute1.png0000664000175000017500000006735413553660046025277 0ustar zuulzuul00000000000000‰PNG  IHDR M޶,"zTXtmxGraphModelåXÛŽÛ6ýíC]ÖÚõ£³Ù´6Û‹Þi‰–Ø¥8.MùÒ¯ÏPJ”%{X ZÔ/ÏÐ#òÌ™áгø¾<ü Ù¦ø—³(ȳøý,ŠÂpqƒ_96H’Ì ×"£Ið,þæ„V"ãÛÞD ØôÁ”â©éaLkØ÷§­Aöߺa¹{c<§LÑßDfн‹’ÿ‘‹¼po“EcY±ô%×P)zß,Š×õ§1—Ìùª7? ‰ÝاòpÏ¥%ÒqÔ°ñጵ]¤æŠrù”“íóÊMe8‚OÃY”Htôn¥ñ)·OͶæèˆ©·Æ­¿ÍûBþ¼a©µîQ ˆ¦”d–lÅå'Ø #@!–â:¹FÃŽk#íÇ“ ¬&E>:}I†%6’ õ戻„ý âÇþš;e¶¬£t9”Üè#N!ë-©D4Þw¸qXá…?Š d$»¼uÝE( ãÁIȇG6ÏP”4m ÈA1ùСïºp}êùA˜ß½ç?ì”·s;R¸0k Þ·nܘoÜð×7`ɯs•-mVáP²/F䃰{©írcޔŬ2€P·ÞG°Q­W²5^ø=H°ž17‚úÓZ\®Ù…œç*] ÎFŒjÓ9§YsÊDKÞÅ k.™»~Æ_AZŒ—^?Y¯mzDš†ÙÕ kÀ-ú‘OþªlM¨ o¶5¯Kœp·9t6çä»J©ª\qͳïC\iã³ÿž©òºI»\s®&Ⱥ8¡ªJi͉H/ít|øYvMÈ»AÌ~}\>!¢¸Ùƒ~ùêÐ#K6).”>ݬûlå+E–Õ튟äk¾+}í¼ic€AèÅ ¾–>çµWù&(|#y3…9Žˆžtç°3²ó‘¥;ƒ›Èt™Û]þÿÔ»8 kAýµœNÑW©wXZšÃï4 ÏÕjz½Í?Ñ]LV¾#G_Û^ )^Œ0"ZJ‚hè&E4ŸD4™„~׉(›ˆþ¥o¸vÜËDTHD ‰ÈMg^Ù¢yõ"¢¯IxÞC;,¬Žå‹§é¢3LÜ`¿ª-‹§»qÑË¢é܈hºvýËIxæ¾™éjjê’…*"º_4NC² ï~ "²&¢O‰èˆ¶Þ‡HØfáT¿,4Ǻ¾+šÿ5"j_Ç8V$<‚¨ïomÙ\QÙ|iIØ÷ÕÛN|gFŸzo-ãŸ$Ü 8@Dé.¿¡ÇM­W¦h⾈ʓDå QùE"²©c=9§ÉI£š/•o/œ„+ÝåDÔMT>în䪹ADÏèÌG, éuLSAB'˺æ·Eg^â†_=ÓD40MsÈ‚' l]ãTPÝÂRWÄËVQMÿ‘$ª¹“Ó,è»l‰hc=ãÿ"ú¿Xšk]Å ßÜÆëHµUt#¢EŸéŒÿ”hXÖ=Ô[, “Јƒˆv¸üúdAŸz})6UT.¾sê>V¢‰Ãápî1âfÿ§ç´Sí/¸­¿Ee'‰ÈY4MŠÎ4ÙD¤&¢Û:åWˆ(†„«^qy€h^ÙuÌkÕ¾"­"¢Ðz¦iŠ,Ø“p·£º<Ž„+Èê+5ñ•f*'¢´Ë ½ûëºrÖxÙKH¸“¢»_ê“CöÃ(ª½”D´ƒîÞbYhŽuµ£Úiidü¢qǑЉÊÚŠÆh¦z‹eá¢èÿ» \~}² O½»C4\ª½¿‚´åSDeï‡ÃáÜC©öMw=§ß+švš¨ÜYgØÇ¢abYXN5WB¯‰Ê+¨æö»5 ß‚7ü‡ˆÈI4l©hØ·õLÓY ª¿Ï‚/ O@B_{Ѱ¯êY~}Ñ•/"*Õ~þ›„;õÉ‚¾ûÁF[ßêò1¢i^•‹½æZWù·jdühѸӵeóEe£Eãî•W7²†Ô[, eDô ’êkÀò‰ê–}ëeG5}‹ŠI8oÜEã]Óþ[}Ç+F4¬#q8Î=Dü\S÷*©±ØSÍ-âKTûª•ˆèYÑ|Å ¬Xþ)*ï-*ÿ]g^ëEÞ•‹~Ý«§Nõ̯9eáQùË'ª[ ©W¬¨¼=Õ—%D´MûÿÙÚq«ßd:F–×!™Ãá4s¬©æ–'HxÛÔtM—UÇðVT÷žXú‰Ê{‰ÊÓuæ%~¬PŸ,üCgkªyÖ|…j¾0›S&ˆÊâ5žºdÁ„Î ¢3Dô¢hœ"í8†ì‡§Eeëê˜f™hxµ,4׺zèŒß¹‘ñ“EãV?«·&¡# H¸ûbKµ¥èÑô†Ô[, ÿ®£Nú,Ÿ¨nY0¤^ï‹Ê‡RMçä"úLûÿ$:)W§ûF ‡ÃáqŸ€ Œ7˜„ÛÕcHèðˆh:eã;ˆ†Ÿ•KúÖQ‡êçÍ•TóZ^sÊB8Õý_¶Ôpê’¢ÚÄŠ´Ã Ù#De?Ô1øYzµ,4׺ZQíßïh¨S¤- ¯UVûÑ0qŸŽGˆh賸Š!õÖ}¢®4uùDuË‚!õºOT6Ÿ„·&@BçÇê;¥$œ#ºËãp8œ{ʪùbI©g+":+ï~ª}ÅšSÇ4â×-÷ˆÊ% /éLã)v¤žiîÓ™æ'Ѱ¦ÈÂhQùdú€ÔGc©O¬¨ö³p]Y0d?ˆ#¾Ži6‰†WËBs®ë ѼöSý·É_w“„gôÕwø §šß9¨3CêÝYhêò‰ê–CêeE5ò”I5wž!¡DõüÄç´/q8N3Dü¼¾+‘‰¢á—Iè gEÂ3òêòÞ:ÓL [)*7–,ÄèL#~—_|Å!*ÿ¥Õîy_Ÿ,ˆ{¢?!*ÔY¾+ Ûr85í-“úd¨vã®+ †ìoªyüTNµ;º“°ue¡9×µŸÎºL§»…¡7Õþᦟt†[“ð†¨öŸëŒgH½›" M]>Qݲ`èö¬êGl a ýÄÃê‡Ã1(Ö$¼¶%þòŽ$áï@„‘p(î×ðhÚ9¢ò<®oІ=,šÆX²PÝè„’p›ýš¨üyÑ4+EåÙ$üøMªý…®+ â/öß©¦a³§šÇ8×IxÅÒŠ„çòâ«sÝßz¨+ ÉQÍ-g]Y 2l?ˆß2ØCBãÜj÷ËBs®+Ñ*夑ð þ Þd¹.VJuw¾ýFg ¢®:ãRï¦ÈBS—OT·,º=Åw[@D‡Eâu†évœäp8œ{J[ª¹¥Ù¿Rí_Qt§ÚWu±ZgYÆ’ñÕ°˜lª}Õú|uÏC, A:㤚+¾a:ÃÎPM¯xðhÀ‹Oc²Bµþ"Ñ0CöÃCt÷ßf¨F\ñï4׺ WÏYÔ$H_}ä:ãÖ÷&†¾õnª,4uùõý΂!Û3@gšõ¢aSu†ý‹8§™Óšj_Ñè~aFu?îBµoßWSE­cÝiŒ% ¯’ð vâ:¤’ðz¨8V$tê««1ýAg~âibtÆÿÜóH~*[wžñt÷ƒª/ÉQí÷û‹t†é»ˆ„;0ºõþƒjÿ‚¦îÏ=7ǺVlj„Δâ[êbvÑÝUı&á罫ǟÞÀ¸úÔ»©²ÐÔå7ôsφlÏÑxˆÊÅ?v“î~–Ãápš-$ÜêœLÂ/æ=ABgÁ†bMÂOñŽ ¢Hxs¢®¿1ÑÜÑ}³Á‡ˆÞ&¡îÏRýòû‘ðúÚG$tVkì]t^]ü”„Îiº_äÞ$t4ûˆ„_GìÑôÕh¶²¼Iø«£ÿ#¢þÔ´?ÝÜëêN¶ý/ WÇcHØŸÍýû¦°êЩ֋ÃápZDz ’Ãáp8‡eÃáp8NÃaYàp8‡Ó`X8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8‡Ãáp8œ"š@D‡‰Dt–ˆ¾ ";9+¥] "/=¦ &a=>Ñ)†ˆ~#"[=ë@D§‰ÈMÏé8‡Ãi±"¢¹Dt†ˆ^$¢öDôo"*&¢d¬WuîEnQˆ¨œeÃáp8ÒŽˆ®Q_òÁD¤$áî‚ #¢“DT¥-o­¯ ¢‰¨œˆ~!¢Dô öRî\ '¢h"ÚBDDtˆºkçñˆö³Îg"Ú­ÏI"jED‰(O[–N‚Üè&˜ˆ.Ñj"J¤9ËBCëdMDÿÕΣ”ˆf OղД:p8‡Óbò<å‘}ãt&¢Ë$ÜqhEDë‰(ž„Fµ æ›DäKB#ˆ:ÑãDtˆIÐûÑ""Ê%"Gª_ì¨ö…6DtžˆÂˆÈ‡ˆ–Ñ>ºûqI°vš`":ED/iËŲÐÐ:=IDWˆhv{ˆ¨ŒYhj8‡Ãi1KBchÕÀ8ŸQŒhœ@ÓV$ÈÂ1"rÖ[DÂÝ""'"* ¢ûH…"rÐó ¡oD/jº,Œ#¢Q=|´ÃÚèÔ·Z܉h ¼¨¶,4´Nˆh±h~içç¦G8‡Ãi1y‘ˆ4t÷w"zWûïR"š-æFD%DÔY¨¾C@D4_4® &«e!‹jY{"ú›„Gº²ð(ýNwËÂlîN”ЏJÂ]qIJ`KD*"ZB£•jYhhÒ‰è-ùU?†hj8‡Ãi1 "¡#àƒ:åÏQÍÕôç$\…W§= Wá~¤Ÿ,¦ÚwÎÐWâ"úƒjda }teá]úBîö‚c¡ÉI œ«yöHæ»7®]/ûÉÁ0 ÃÜMYÑ&Éœx-WýZqNbØ`¹ÛŽ…$W=4X£z-9?åÍëeE›d?†a˜Æ)+Ú„üäQ×rÕ¯+sÕCƒånK8-89‰Ã†çªG”•ü¹¨—÷a†1ª.îÁÍ÷šÄWÊs‡¾Õø·>‡£GަrÌS¿º,?åÍë7NÇÉ~À3 Ã0†s³8ù)oÞÈOzcãÑ´QŽ·N#9š6J‘«~õÏãÙS®U–f—³†a3§êânÛóÑõ\õk{øUKÎ=%G5Ä_£yøÔoÓoÊ}`3 Ã0ÍKÕÅÝ8ûûÜŠ\ÕÈ£ÜcPrTCüs“^;yþ Ã0LËäÜŸ *rÕ#ϲ0pôÊÑ´Q jäáó9KnÈ}3 Ã0ÆçÜŸ *rU#ò# N“r4m”£&qDީߦÝÄ¥=`†a,ƒ³çVhû0p§GNÃÉS¿ºìxöäkr´ Ã0Œ´T•fáØžÿ]ÍS¿ºLcÂÉI6þ .f‚1}n_‹ ûßEqÊ38Ók=·ÂšHbZ(¹Ëm·Âk=p,º#N' D鯢üèϲŒePUš†ÂôñW¸ï‚'W5rGiî÷²ŒLýÜ<¾'ÿü•Ž(\ë†3;|Qšè‡kiípkO*÷u~íÌ´Pª~é„Ê}qkO®¥µCi¢NoóFá:Wä¯vÁ™ÔãzÁ²§LËæü¡…ÕkÉr·Y’£â¯Q«¨<Ÿ"ûÈÔ¦êB .œŒ‚u^(\ë†ó >¸µ'Dö†‹1-ní Áy¥ެwGáz_\þã Ù]¦erû\"rÕ¯^ã¿aɉúÖ±=]ÇÅ 0¦ÃÕ¼E(Xç…¢ n¸’ÒFö‰1®¦¶Åñ-^(\ï‹«š¯e?Ž™–lj½“¯å$}Kî¶‹#q4‰#ÕÂ#ùB&•çpRù( Ö8£,µ­ìcž\NöGÁ'BÕ…dÙk¦åp)ïGä&½ž.wÛÅ‘8¹ª—o‰.¦32s½` ÖyâÌ_TýÒIö‡1oª~é„SÑž(ÜÐ åG#e?¾™–Áís ÈU(“»íâH˜Cñ/?š—ôê ¹>&eÏÀáÕN(ÛüÒ‰aš²”¶È[a‡2ÍÙs¦epxç[—+‡÷–» ãH”œÄ¡oúeêU”¦‘K>AþJ{ÜÈ ”½aaZ&WSÛ¢p½.ÿñ¹ìÇ;cþœúå³9 aånÃ8E£ze~ÉŸód?ð,™²¿"¿Ò7³‚doP˜–Myf Öº¢,gŽìÇ=cÞ”ü1·"O5b¦ÜmG¢ä&½ž~¥àg 4•‘G–¡`­+ÊùŽ#WS…G7­”ýøgÌ—Ëù˸“£%%7ùµ¢ò[d?ð,‘ʳ±(Xç…+Éþ²7 ŒeqIíÂõ¾¨<+ûyÀ˜'å'¶ 7ùµ"¹Û0ŽDÉU¸p«x»ìž%rZý$ÎìðöwdÉ)ÞæƒSªþ²ŸŒyr«x;rU#.È݆q$Š&qxyeI"Pº“‘«¹óQ°ÆUû:ÈÞh0–IÕ¾(\놲œY²ŸŒùQY’Mâðr¹Û0ŽDÑ(‡UT]H’ýÀ³4 7´ÂÕ”6²7Œes%ÙG6¶‘ý|`̪ IÐ$«» ãH”ePšR/Uç•(N„¼²ÿÕ=9É_íŒâäA¨<»½ÁíÕ® DZM²7 ƒýql³'® ¿çãš± úÈ‚»‹õ[ý‡6´Gç@ûFe¡zÜv~¶hßÚöÎUp]²€}°c¾?ÚùÙ¢,=¤–,¬úÒ¯–hÜÈ …³£*öÖnXU‹Ûàñ^N¨Êî€ö­m1ñeÌ{ß¿¬j‡®AB}}èƒ7žu»3¯sª`ØÙ ó:´¡=ìí¬P®½·ÀOôvB·{¼>Ø­Þí(•,4´¶Ìj;ì[ÑUÙP¢ÆÍ]Íÿhû: x»7JyWöˆ1X,(‚,¨ëDI@F[FAêÞf qZõ8J•¾FïWQ–&t.,QÝ5ì—•m…F*;šÍíÑ©½ÝašÍíÑ9Pø¼wy€ Úa{"kFv(ôsBØ@×:ëPœ;«;Ÿ_èï‚ÿP n¾?ú÷v²C1Î nÎÖlm{…›5NÇ õ¶±&ÜÞ‚«iÁps¶F~T{ôíâ€óZãÅ']°ø¿>xo¨ŠO_Sà«1žµêàâdâ„ Zß¡5ë7ßD„ÿ¾¢€«“5NÆÖ¹6Ö„Ã[k÷Ù¹# ¢qn冀]®i+È‚Îö®oü†¶CÅžP,Ÿê‹žíÑÚÛã¼p=#Ä(ÇMi‚Š“tl3– Ë‚¥QYH`t0TŽm ~ˆÉ„d¡Zê’q#·e¦_-YH_Ú÷w°G€¯-®iÛ¨,Û.<&˜1ÞëŽ,,Ÿê‹!OºÜ§bP¿ª½µVd‡âñ^Žx?Ì_ö@‰*­½m6Ð;æµ²C±h’F=ëvg^çTA°µ±ÂíÝ!8´¾]­õŒ›ï=ä d‡bÔ³nµ¦ã«°ÁžÈ€Ze³'xפ慆¶C~T{‰ig>xŸ¾ûŸqŽ›d‹•½bÌ– Š ª:ÑDêÏè ÈBÝÛ¬! ÖºãfF;Éd¡ º=.&ß¡<+¤É²³©lm¬ppM[œW¡o§;²Pž‚.vÈü1«¿l…G{8¢rOò€ìP,øÀNVwd¡8!¾ $,ôG‰*Tà±ûk„ÄÆšp19ÈÅWc<áâdµá­€ìPt ²‡‡«5.¥à ¢…G*)ßµAir0^송ÚF½.Yxþ ¡q>W'kü¶únáów<ÔÍÙ?àT| ¶}ÝÞ6Ø<Óï®:ê# ºã7´æ¿ï‡º9âŒ2GbÚ£gG{,Ÿjœ»S·2Û£`­»AÇ6c™°,XP•…­õfF;+ ˆð3hZÄzb›=¢¶0­D* y+ìQ¹;X2YÐ%rªo“e¡jo(ÞêW'kôµGÔ¬š; ÓÇzaäÓBC\¹'wwĪ/Z5* ·w‡ gGû;²€ìP¨û£[ˆ=œ­ðT_'F×Üöè 7gk\M FÆÒ6 ªy,ðöóîBgLÑü·ÎÞ†ps¶Æ ý]p61¨QY¨‘þ½îÜѨæjZ0>|Ev~¶p´·Â}Áw7ÔÕuL_Ú¦I²P×ø m‡²´`¼Ðß.NÖðr·Æø!Ë8!*w#o…½ì c>°,XPYH¬M$É­ôfF;+ ˜îkдØîAnvˆŠ1`Z‰d¡îmÖšHBÕ^ã|Ñ3̽Rµ7¹Ëm :¶Ë„eÁ‚’£ Î'Ö‰&’µÞÌhk…áÞ(ÿÑ=:9cÝköèèB°ñ°Åÿfz¡BíÄybÕ3¶heC ;k<5Üçb=1÷>+|ìµÉ¿Nt@OáÊØ3ØË"½µOÃóVy#ïG<äm²µF¿¹"'V¨Û™9ÎÜÖ VD¸ß 1kõ_?M$Õ»ÍBIÀÞ†1Y =¶Ë„eÁ‚"È‚²N4‘¨¼õF/”ÿè„¶VxâuœÙ扴WmáäŒÃñÞ8ÿ¹<Ú8"îgOœüÆý]m0í'o ÚM¸³å[+œÑÓÕs(Ù¨ÀÖÁÖðíïŽ2•wƒó._æ±’ÉN IDATŒîn¶˜6×g–»aj+ôxSëë]ñ´» Þžª@É6¶kgÄë·~ÂjÝÛ¬!XSÇÐc›±LX,(Ê‚ÒSof´µÂ€/(ÿÁÝí ŒÊË—8¢K€#l÷Ĺ©vp÷±Ç o»âö&g r³AÔw׊ì^'L_:Å^=qIg^ºóÞÿok´ý— ®jëS¶Ø_NtAÎ8øÿ׫ëºÝ šÅ®¸¸C¿õ¾PëÞf Á²À˜:†ÛŒe²`AiTv¸êÍŒ+ øÌåßÚã¾;ˆÊÅŸóÞ°†[¨=ÒW¸àüR'L ²Âãœq{£#¹X#r…+޼m Å}ÐlqÅÍUNø¢Áæ>GœÛîÚ༯/´GWÌœí‚K«0³³z½îŒk‘èïaISœqn3â^²†W¨òbô[¿{’øCX S, Œ>°,XPYˆ¯M$Û]ôfFaÀTg”/¶ð-B¹øsåzGÌìg "úýÓ9›]€(',ìikGlXâˆ)ݬ`gm…€[ÌŸd‡¸XaàûθÖÀ¼±ÍN°EowYY¡GÚ$Œwü3; h%¼]Ѻ³-Ö.Õý„/Ôº·YC°,0¦Ž¡Ç6c™°,XP•…'F–¦¥Â²ÀèË‚E…¸:ÑDíÈè |¡Ö½Í‚e1u =¶Ë„eÁ‚’£ JâêDI@”=£ƒ&’êÝf Á²À˜:†ÛŒe²`Ad!¶NòW:âöF[`3SÍí¶8¼Ê¹ÞmÖšHv1ŒÉ"È‚þÇ6c™°,XP’…bÕ“(^ël²a´œYk3)Otb±,0¦Ë£, ”†d¡êìV®óBñZ'ÜZol°\n®³Bñ[®óEåéX, Œ©Ã²ÀèË‚E…õRu6 ŪþÈ_)tì³TòVØ¡X=•§74¸½Âe¡jw®dôÁÙÔ'Q”2yêaÐ$CŽ2ŒifrUѯ†¢/ãlê@\ÉèƒÊÝd?î–ÃŽoÆò`Y° 4& LóaJ²p3³+N¦<‹ÜÄa(Êšˆó9KPV´·Î¥ êb6pù¦™©,Ý…[çRpõøVœÿû[eއ&qŠSŸÆÍÌ®², Œ¾°,XPYØÎH€&’€]²r+«N¦<‹ÃɯãÜ¡E¸]’ \ÞÏÈÄ­sɸ Y‚ÃÉ#q*u0neu’õødAþs…1X,(, Ò!·,\Nëƒ|õ0œ;´U÷ÊÞP25T]Ü‹s‡ W5 Óú±,0fË‚E…mŒÈ) gvÄá”7P~:¸¼1Qn'âpÊ8›:@FYÿ\aÌ– JŽ2 8·‘¹dáÔÎgp4c<*K3€ËÙŒ‰Suq7в&âÄÎgå‘8Wó€eÁ‚"ÈB #rÈÂéƒXÌjaúƒ òŸ+ŒyÀ²`AaYM$Yí%ãrZ¦ŽFå… àÒ^ÆÌ¨*Ý…‚oârZÉŽ–FX,(, Ò!¥,ÜÎ Ežz8ÊOí.íaÌ”òS;›8 ·3CY“ƒeÁ‚"ÈB4#RÊÂÉ”gqî¯eoì˜{çìïspjç3Ê‚üç c°,XPX¤C*Y(ÏèŒü¤¨*Í.ífÌœªÒLä«_ÁŒ®, ŒIÁ²`Ada+#RÉBñÎáBÎbàÒ.¦…pþï…8³s D² ÿ¹Â˜, –éÐDÙΨTe"O= ·Ï©eoà˜æãö95òÔÃP™lÔã‡eÑ– Š QŒH! ei÷£(cp1‹iae¾ƒ+i½$ùÏÆ<`Y° ä(À³QŒH! Å;âüß‹€‹™L ãü_ p6å)ãË‚ œ+ŒyÀ²`Ada #RÈBQÊ”]+{ÃÆ4?W WáXÊ È‚üç c°,XPX¤C Y8¬†[g​L ãö9%òÕÃX“eÁ‚² šH2Ú•\Õ0TžO.¦3-ŒÊóIÈU 3êñòÀèË‚E…ÍŒH! 9Ê0Ù5Æxä(Ã$ùÏÆ<`Y° °,H‡d²PšÆ´PXS‚eÁ‚"ÈÂ&F¤“…T“ãöÁ‰¸Ÿ®ÿžƒ‹ÄÃâ‘:Ì Dí1kRó.·h&yÞ¨Bù׿¹Fä?Wó€eÁ‚² šHÒŒŠ)ËBO+yõGâQѰ‹ð¦/¬Yš* F<~X}`Y° ²°‘‘éda§ÉqûàDôòìŒWzú!,jÇò«ÊÁ½¯?ó Æ¬ýjà|,~ýz zúØ‚È žÝbY¶Rÿ‚y?>‡‡ZÛì½ÐoäÇÈ9¾ ‘ýœñàÂM¨(Ý ”ª‘ûE<Ÿ…pÁÁÿ1Dån¯g¾)(YÞÿ~sž‚§-|{ ÂO»PeÛMidAþs…1X,(, Ò!,¤˜·N@/Ÿ‡ñó¬ûÑþ…9¸Tš”Æc÷¨<òÕGó ¬ý*ÜúuzztE͵(Éû[ßðƒï‹óPVš‚ò=o »ç}˜»göÏÄÔžÎèñåräÏï Åc“Q|>8»ó»{àÙ51¨*𡽳ÒÀ|“Q²¼;¬É ÏÌXŒãy+±ý 8wxË¿ÝtaY`L – JŽ2 8³‘–…Çýo÷$ÔE)À©ï0!´+¾Ýõ5Fhe¡ª8E‡¶áfi *N¬Gò¤¶píK¥Jx· F,DYi PšŒ+{¾Âª­qýÀôözÑɸ}ð]ôõ}; “‘,Ô?ßd”,ïÇàWq¨ZŠæ`˜o|’¡”}»É" &p®0æË‚…eA:$“… É&Çí‚,¨ 7`ù#­0|ëv\K~ûL@á‘ù‚,ìKN¯Ã¶ÁÛ¢G¿ðRœŸˆÀ¥ ;üoº¹·tçfævóÄK[¢Q´ +Ú<;¥ç“£äÙQÉ Ì7 %‘Ýàþðç8^;¿âŸÑÙöÇʾÝtaY`L – Ë‚tH' I&Çíï —Ï£PSáø7ÝÑ~H8RÆ·Ã3WáöñyZYˆÃñE÷Aqÿا.$¡tõýðzb:.]ˆÃþ·ýÐöµ…¸ªgYÚ{øra$®_PBóYÚ†MÆâG|ñòæT]HÉ‚ªùªQ©½³pZ[ߣsðŠokü75^öí¦ ËcJ°,XPYXÏH€&’€´6FEµÉqûÀx­,¨Qñçx̧=út‚_€;²‹#s:@ñð»ÐœTáfÎ|Ñ×6Oƹój\O CEwÌŒ_K¹‹0³Ÿz}ù3n^P£|÷«èêî /¿'¡>ª]îÑéäÑ ‘‡â˜¯ %‘Ý@äŒgf}ƒ“y?cÇ;Apާäßnºä(ÃŒzü² ÿ¹Â˜, –é`YdçÖâÇ>êú6òϨE²€ÊÃs1åQOØÙ8!àþ§0éHüÃÿ_ŠóÛñ碢· ÈÊ=ÂÞÇ¡*a§—bJ Añò\®^îéåXØßÖ.}°aïœzæ»g"»Ã£çpLþ—?\ÈÞÝbYÖvT™ÀvcY`L– Š ë NTL“IDId7x>ŽK²×¥q¤‘ùÏÆ<`Y° °,H‡t²È4¥Hä®Kã°,0¦Ë‚E…µŒH& ç™&#’ÙëÒ8ÒÈ‚üç c°,XPX¤CI@ª¿QdAÉ´Pr”aF=~X}`Y° ä(Àâ5ŒH' L EY0s…1X,(, Ò!,Ä›'§㣶rê#F^Ö‘/1ȳ;¢›ÀzëËcJ°,XPX¤ƒe¡a®'ÿAŠ ôñvÇÐÍ›YX‡eÁ‚"ÈÂjF¤“…83d+öŽn…ÀÑ3‘1Á>Ï}…Òí°’(ZøzzÛÂλ#ÆD C¿ÁÈ>œßŽ31#08ÔVd‹€GŸGÌïÛ€óq(Ï C^¯`Ý'ýÐÑÝ 6Þ]ð¿-ëPqúÌ}°ú/R>„¨ÜXXÿ¦!,È®0æË‚…eA:4‘ìlmTr”a@I¬ùqìkŒmëvnÅÔ!ñzq‡w%±¸¾ó%tôì†Ùñ+qáÀ4Lîa òŒì㱨8ô_<íÕo/ÿ%yßcóë­áÚçmœŽEyf:عã‰Éspæè*¤}×®£qøt,PøyvCT¾ ¬»ä(ÃŒzü°,0úÀ²`Ada#, õSûO´ë4‡NŧcJ°þ³v=ªJ¢=Ú£çáZI,P²¥ë„‡ÿ`dߎãó;Âó‰OQrN˜OUÞ§èÙ?ÚòÌ0tôzÊBaXyúËèú"œdYhXä?Wó€eÁ‚² ÒÉÂ3cRÃ\Ad GG{89ÚÚNƒ¦àü¹HèŠÞ³#q[;þ”çÚn0²Gá÷ü@ä…ž Wx*œààй{£Qž9]C_Ào'…éÊ3‡â¾/âÀÉ@áçZY{Ýõƒe1%X,(‚,¬d$@:YØn^™†¾¾˜õNÿµ§ÿZ‰#QÏ {lÍÛ„]¯z£ý[sqM;~醇 ðÿ?dÆÑÙ¡ðÿO8.WÏ«x4©ßãâ™íZ9xN Ãj}¾# &°þz ,È®0æË‚…eA:XêæâæÇàð,ö•ÿZ;áéŸ×àJ⿬è¹Ê(=8Ÿõ´ùý²oÇíÐß;“V/Á¹Ü%ˆû ¼î…¼ÓMŽˆü#FöõgY`Ì– JŽ2 8½‚‘M$)~FE…mfEü³®h3j®Õ*߈¬‘ ¸ôÿίǯ³EW7+8´é#"$ø?øõÄ6 $ Ç×½ˆíAdƒÖýžÆÚý[€’m(Ï|Y+Ûîþ|j öWÀÚ¥'6ibdßM%GfÔãGI²Ÿ'ŒùÀ²`AaYÉdá\L "WwOŦ˜å¸¥-»õ(Zw…‚Sr×MzXS‚eÁ‚² , †q5á_h0ÛþˆBåÑo°ä 7„Nœ&P7–Æ’aY° ²ð3#ÒÉBtË¢x%R?î`7;8ºz¡oØxü~d«üõ’idAþs…1X,(‚,,g$@I@r+£"ÈÂV¦…’£ 3êñ#È‚üç c°,XPX¤ƒeaY`Z, –éN¢˜ ËcJ°,XPYˆd$@2Y8»…i¡H# òŸ+ŒyÀ²`AaY–†eiI°,XPr”aÀ©Ÿ N63-IdÁÎÆ<`Y° °,H‡&’€$_£"ÈÂ&¦…’£ 3êñòÀèË‚…eA:X–¦%Á²`Ada#ÒÉÂF¦…",È®0æË‚…eA:$“…3˜ ËcJ°,XPYø‘‘M$j£Â²Ð²ÉQ†õødAþs…1X,(, Ò!,¬gZ(, Œ)Á²`Ada)#ÒÉÂ:¦…",È®0æË‚…eA:X–¦%Á²`AaYéda-ÓBaY`L – JŽ2 8ù#’ÉBñ¦…"‰,˜À¹Â˜, –éÐD ò6*, -›e˜Q–FX,(‚,|ÏH€t²°ši¡H# òŸ+ŒyÀ²`AaYédaÓBaY`L – Š K `Y`ÌCä?Wó€eÁ‚² ÒÉÂJ¦…²À˜, A¾c$@I@¢—QÉQ†§W0-”e˜QAä?Wó€eÁ‚² ÒÉÂÏL …e1%X,(, ÒÁ²À°,0- – JŽ2 8ñ-#ÒÉÂr¦…"‰,˜À¹Â˜, –éÐD ô4*‚,D2-”e˜Q–FX,(‚,,f$€e1Yÿ\aÌ– Ë‚tH& §~bZ(, Œ)Á²`AdáF¤“…eL EYÿ\aÌ– Ë‚t°,0, LK‚eÁ‚"ÈÂ"F¤“…™Š4² ÿ¹Â˜, –éÐD 0*‚,,eZ(9Ê0£?, Œ>°,XPX¤ƒeaY`Z, ”ep|!#’ÉÂɘŠ$²`ç c°,XPX¤C:YÿïÜ3Æe1%X,(‚,,`$@I@¼‡QaYhÙä(ÃŒzü² ÿ¹Â˜, –éNäÿ;÷Œq`Y`L – Š ó NäÿktŒñþÊŸñeAþs…1X,(, ÒÁ²À°,0- – Š ó LLà¯Ñ1Æû+Æ—ùÏÆ<`Y° °,H‡t²°˜i¡°,0¦Ë‚…eA:4‘Ĺ–…–MŽ2̨ÇË£, ”epl.#ÒÉÂ7L EY0s…1X,(, Ò!,,bZ(, Œ)Á²`AdákFÌInæNÆ™¤Çqdƒ7r—[CILÉ[a‡Âu œM雹“ÍPä?Wó€eÁ‚² šHb݌ʽþ|wUÑ\œQ?Šüv8çò¬ TýÒ øµ3ÓD*÷uDyVÎ'´BÁ'+û¢ªhî=íñÏëóøaY`ôeÁ‚"ÈÂF¤“Ã~­ªèkm ÀÉ­ Tfw~éÄÜ#Uû;âäVŽG£ªèkƒ÷øóŒ/ òŸ+ŒyÀ²`AaYS—…3êGpj«Øß‘iFªöuÀ‰-8ЋeiQ°,XPY˜ÍH€t² ÿ/±ÝÔ|ŒÃ«P¹'Ø×ifª²Cqx•=ÊÿþРý#þÅ<ãË‚üç c°,XPX¤C:YÐÿ}éâÄ~¸à+{£Ú’9É´Äïµ³,0¦Ë‚…eA:4‘ìp5*†ÊBá:ÜÌhd‡2F¢<½-ެ÷¾wY0âñòÀ胥ËBxÚ(ÇéÊñ½#âÆ ž;î­;Ľ=2"vÌ€ˆø1]å®c³E…YŒH& ¼/»ÜU{‚½!Œ‘¨Ü„¼¶íñ{íÆ—ùÏ]n‰ôý“ñCò$LË433ãÇa¡ú]$íûeMÞ/–( ñcºFÄŸ?[ù^ÑŒøñ_«&•}Ÿ~iåžùW«ù)sVÙw©Ÿ]úZ5©,"n\Åת÷ÿ˜•8qqDì˜ái“r¯ƒAÉQ†E³ NôJIÀž`Éùûm;8Ýçƒc»ÄåP °Fëçüq9Î9Úã‡m Ì'=ùÈÕꤚòÛ[½q?úLh‹rÑø—:û³·,ë{¯¯&J" &p®ˆ)Î ÇüÄw±nïBdÙ‰CÅ0ÍÌï§Þ£iX½{.©ÞGqnx“ö%É´¸qoÎRN<õµjRÙæ_Bj^B“¶íS¿ 5/[[Qñ]êg—"âÇUÌV¾·."v̹×I¯²0“‘édAÿ^ÍšHvIέž¸ÏÑK£EåI~xEaƒ·~hÊXAbêŸÇõåîr³G ]ÐþNùí­^èI²wÄ÷QwÊ//ÐÊ‚ ë{¯oH# òŸ+ÕÜ82ó'"þ¯MøãôF’5Û0?q"n™Ñèþ±Yˆˆ<[ùnΤO®©r¢ñçéƒ÷DöÑ ÄXY±(yò¥YÊwOO‹{{¤ÜëØ¤°,HËBd`~¨ø¨-nkË®|ç ?7dí j‚,bï[iŒá¶ðéï‡Ò]5²ÐËÕSþÏ^ûàD&Ë‚¹ÉBRö'X›½OÿÊHÈš½ ”ýI£û§¥ËÂôø·‡ÏŒçfÌÁ•øýôoÍN’f;¾OÿêÚlå»Åñã†È½¾ F…ŒH' úwTÒD°+P ?°‡k/e»Ú#ó9ü§5®î De¬· ÑõLŸÒcýlñÑŠö¸±Â!ΈK†ÝŽòB/w'ÄEµÂ(¼4£-*vâò'xwò’e]ﵡ4² ÿ¹RÍBõ»H+PâÀ©}Œ„¤(ñmÒ{, Âc‡wod&ãà©_ŠZ³ _+?¸>[ù®2"vL°n]ÂÓ&)"bÇ ˜7vbDÜØ¯"âÆÏŸøÞšˆ¸ñó#âÆM™7vb¸±ûC°,H‡&’€í.FÅЫšH²ÚËÂí(/ôs¶Ç÷Qíd?ŒkeƒqKÛYíQ¹C+ [ëž¶l‰+ÚzàPj{ µ5¦´µÆf·EUV{ÜÞâ‰^îNPªÚãÌ gøz» 1¡=.Ïw‚w'OYÖõ^;æ(ÃŒzü˜š,̈‡ý'²ðëÉ=Œ„ì?‘…ñãÝ?-U"âÆ ž¥|÷FfaœÚ/ ¿žÜ‹û—V̈="nÜàˆØ1fÄOønfÂÄs3âÇU,LùôÒÊ=óolúõGlù-Û~_…-¿Ebý¾%+÷Ì¿ñíÎ)—f%L¼>[9±xVâÄÅáño?Ú¬E…F$“C;_f¶“‡ô6XÖÍ}?ôÇåe®òsÅî$aXåv/A¢êš¶-RŸ¶ÁÑÁ NV°&‚Ó#>8ŸÑ·7+ÐËÝÊÄv@Z¬zÐ/¶Âñ¹ZYa]ﵡ4² ÿ¹RÍô¸±Øb#ÓãÆ6ºZ¢,„§MRÌŒ]ƒßNeKNRn ¾Nœt}®jÒÕ-¿-CZ²ÉÓþrr7’óc±á—*æª&]¥œÐ|}"X¤C:Y0°?EF[Ù89يΈb‹Àç[áš¶¼r›r´Ã[ê˜N鋞6˜° 5NÇøãtŒ?Ž,pE;Glm‹Û›´² Æ¿µQÇ\lñÎhxuRȲž÷Ú'Àe!ûDF³2>ÔýV(/g6TôĬCzÎÿèVÌ W×øtoj³×½†D/y}‚Ý`M6PtŸ&©°·—a©²0;ñ½5ö/)—ûîNsÜJ̉Â÷éÓ®ÎRN8uÏo_°,H‡t²`à#’ôÙ¨ŒöÄ“ŽVðw¶Á„ÚÔ”Çxâ!G;ÌûÙ¥q5”%àâ\'ø´rÅ>µh^*_Lð±ÂÓáþ¸¹ÑC…„êámð×Ûv°#uTȲž÷z›ßeaÏñÔfel¨#úþ×ðxÏăŠû1óO=篙¾1iWR³×[LÖî÷Ñ͹³ñ®Ç‚wBá†ùÍ· K”…™Ñcýg)'^Û}< ûOîj1l?´_«>¸4[ùþ*ƒ7ŽÐ¸Lg$@IÀ6g£b®²€TlìkòvA¶J$1žxˆ¤Ã·BìãÖhó_\«5¯6ÈzÖ.}½prƒ®,ªV˜ÒŽ@ÌXŒxüõ“ÿ\©fzÜXì>–Ò¬Œ qDßå;®‰Ðû_ÇW>„vnV°ö¾¯¬ÙЬ‚˜Ø×D»ÖbÆâ6¼ŽGB\`E¶ð}ø%ÌÚ«Âîc)HK†à^£ðùøð | ÿîë&LçÛS÷lÊéÐÁÛDVpë:ŸìLêQ”€Mß<‡ûüì@v^èöÖç&c÷1u½Ë“øcO¸tÿ1G´e|…>.¡ø KÝlÛI…†÷9ÊBxÚ(LjØ1"bÇ~8+qâæÙÊ÷òf*'^˜ÿNÙô¸±X¹gîÍ}'2ÑÒØ{< ߥ~~mfÂÄ]ái£õÞp9Ê0àètF¤“ïz¤µaŒÌ½^¹K" &p®T3=n,²Ž©›•·CÑgy RÕ¯ ­­zþovälÀ·“BáÔe,6ª‘õ×t< èˆ?ÔÈØÿ ôôÇ¿¿DüÁHLá§^㱩P‰ahcëŠÀ'b¡jRþžƒ§üîÃÔ_UHË‹î]0fÃ*Äÿ‰#ZAñ﯑tLÔäע芷6¯ÅŽ´¼ÖÝ!ŸþŒ”–%^‡ŒœØúk,2Ž©‘uLUÔ híõ8æj¾í4=nl£ûÇœd!"~ÜÙ gÄ/Ÿ¯þïå•{çߌ: UîV¤& ãˆÊˆLƒ=ÇSñcæô3&îÒ{ ²0‘édaºaw=Rý#s¯WîÒÈ‚üçJ5ÓãÆ"£HÙ¬Œ qDŸÈ(¤¨†£­ç#˜{H(OI Cûà—°î´ ®™GÒŸ_á ¿Îø8; êßÂ1üAlmœàsßã;ÿetwwFïyË *X‡e3û£ƒ— ÈÊÁ/ŒÃO‡¢T¸©ÞeÕ°ß½îu×BDÁx/kK³m'A?ÔæœYÊ ­ßÿÝÌ"˜†™•8ñFDü˜®MÚ°, Òaò²äkTmS`”—zÅ9[|PºÁ_°…kgü×ð´%Síàó€.›ÀzÔEÆjdìûŸÁlÛ6ÑûÞSýš›iqoCU¸™‘éqc>éÉ™8²ˆè+S!ì“_,HþèJswŠm©ÌSMº<]9¾7Ë‚‰!,|©7šHÔ>&ÁÍåÎèâh‡¨(Qùv¦>æˆÍë}píG‡º ?A&þ\2Å^Ýœþ¨ ܬÞÁXò½7*M`½ öAôƾ˜7–уĂŒ L[ÇÝ ÓfòÚQÅ1®@úÑx¦ ÌÕ_ôo\d)dáÈzcJ²€í Lò'´ºßßNSàdlíáÉ‘¿£ÀñMžØö¢ œZ;á—X‰×e¡ÙH8¼Ž‘s”…é±c+SŽlCêÑX¦ DÄ«hò4 Ë—Œh" ˆv4*÷$ *o“áv”¶¼åˆ¡Ýmàik…À®˜î‰*o\[äˆàPgäÇ ãŠ?—L±…S€3r´ÃíŽaÖ˜ô­—ìë•7Ë‚Ä^ÍÈ€¹É‚§ŸfĽskçÑ`'æÐϘ•8ñ&‰B,è߸06È’ÈÂçz£‰$ ÑË$¨ŠóÄÕ¢Ï;Èx×÷Ù[ã½Ež¸¶ÐÁ!ÎȆ_[à€ íç’ɶð¸ß¥Õó‹ó@D{k ›ã)ûz!ÑKè³ý¡Á}2Ñû"#û¿&ô¸·±#%#ÓãÆ"üƒþ bJ}ZzÍŸÿÎÍ”#ÛÀ4Î÷_ܘ™ðÎÇ, &ˆt²ð™Þh" Pzš%“máÖÁÇâEå ø¹“z½ãË ì„ÜXaØ…Ïìàè„üXO”|j §¶NÈÑC”+†+¬1y‰Böõ‚ÒSØÎìŸjr”aF=~„úÉ®T3=n,¶å-gd`zÜØF÷©½ 1#a|ÉöœUH>Ýl,ÿ° zŽÅúÃâòµ˜=Ø^C#°cß{èâŒ÷wEÕ?Í< mC ·Ç0çšrUÆh„¡ã§‹¡¿ce?¸wƒu‡›o=ÄÄjVcFÂ;WÂÓ&)š¼q ½e ¼z7eYHP˜«œ1ÐÉ qÆîeî8½Ú ïÙ#ØÁáß{ ü{Gtt¶Å¼…¸ºÎSC­`è„ü ”|j +²ÂsãÝpr½;¶?o×@Güµ]þõB‚ÂLdAþs¥šéqc“·Œ‘AÞ?&' ñãgþñå¤Â(4Êä‘tê€÷37×”ÿñžòòÄ3›ÖB=]œ‚ñ^Öæzç·m0ü<Ú£ƒ§žX¹êNybú›!9tÅ{é›î”o_ÙîÝÞÂÚüæ[jT›1/éÃëzÝU bYédaªÞ˜’, A œðføÛˆ¬àl‡Ï¾pÇÍ뎨glàA7[|ö#úvª‘…O8aJ_k¸¡U{¬^æ*X§YÐÿTc‰²°5w)#æ( ái£g&L8³ñ÷%Í÷iÞb¼ÝÙ¦/A‚¶lÛú'àé7 mFbötq »Yë™Ç|óª/Z½úæ½Õ îOOÆÖaXBúuï‚a/úÂ퉉X—/”Ǭì÷n£±&3T9ßàa!PØÈÞ =ßù›ó _Ÿï3¿¸5+qB‚Þ×Ð+QÆÀ«÷­F%GNÑM$ñŒ‘ÑD’Aû§še˜QŸ{½óÑÜL‹-š%Œ ²Ððþ15Y " ûÑ™Ê W×üº‰›šõXùEœzŒÆê¼MH,XyC=á=|¶l‚rï;èì„w37Ô=ý¡iø¿6­ñÒŽµØû´V<€ðß6"±`âÓß@ˆ¢7ÂÓ§`P+wücÉ2$lBô AVçmÄæ%÷Ã%p0“º­¯£‡G¼¦Zgкl9$Ü1Šˆ;Qï ˲ &/ qaYÐéqc±)g±™ùáÏàƒ‹Œ¸Œy˜öjOŒNºÇeü=ï ê‹·>„qÊ…FÞ.‹ÍVˆ„¿ ‘ðÎÙïÒ?»“ eÁ†{".ýMtt Á„ôµøÿöÎ;<ª*ïã¿))@ÖUwÁ}WAAðu]q•¢˜]WWH&€@03I(Q:R’̄ҋ”@z¡D”LKh1tJÈ̤Д* 0i„$ß÷;y‰,% ™¹‰ù}žçóèÜ;s빜oÎ9÷^ãñ`x¶~ž¢`ÌO„a¯š=‡ÀâïùÛo“ÞÅSÏŠ9‰0æ„ÃûÙæxcÕjò¡Û9íZuCè‘x$.?ÝšC HŽìŽ–}mM@Ò²®xìÏÿÀ¨ Ëð}n¾=¸Í uÞ— ÇW`nÚØBÞÿGMŠ_ÛT ÎÓyaar­5GÒ‚u°BX¨ýù©²)†…¤œ…3ñG?|0Üks¾ÆÜ±/ããs‘td½ÞŘqÑõß=Ðë“®è>p‚'Wÿ<SGÝõý/; ó¿ÞÄ{½Û¡ã?{ ÷€WÑíí>98Á>AÇ^Ç»vD×Á#°8e >øè5ôüøU¼å7 ÓÆu@—÷_Á;Ó&c­¡ú¼á˜ù>^ëÙïü§;:=ß S¶*ÑÏW5'w\’r6ê°@TÕ%á?a–1àÒ‚mãm1çáÛ«ëöÜ ËbŒîÖ /Ì\ˆß¾?µéyÇ„yú=*thövÅÞã·kþIsIáêî WwHˆàúnÖåÆ#%}Ú=Þ !YÂ:¾|ÛO6 ÑÝÑ¢ó0DYâa0/ÇÜÐðê_]!ñhƒc°êXÜ#=CC—‹ˆ=Úò0}À•PªGh]û¸Ù:Ž pJX˜TkÍlnÎ:X!,ÔþüT霰 þµRe¨N‰„ófÄê×ñæâÄŸ˜9c:ãß‘5Þ½ÞÁè öxci(âNÆÞoÂlõÏ=0By×÷¿x =#´ˆÿñ3të9+²Ã0Í«|Ó´˜éÓžIáHÈÖ`ü¿žE÷nBÅ{øhÄ;x¯Ïß1pX[¼¶0ñ'Â0õãÖwæõîŒ×:wBà_#!k<¼Ö“÷ÍD ç?0í ãŽK‰yö°ðàóÓÃBÁé¾î½êS­^UªSbQú$l¶FAŸ[ £3ûEx¼ìƒ)CŸÆÓƒfà[û<Ý%:4{»¢ÿûwY“ðî“OâŸÑ »g b÷,ÁÚèÞx²EWL9ƒÍéƒÑîñWœ%|ÿûíCЩşáÔ-:E¤%ëÓf`Azty1HN¯—šáùiój¹ý÷61kÑmNu«FA¨\¦²NÃÛ(ÂB¸Vª Õ)—=Ça.ÖtA¯( â²Ã>¦#þ¹N‹¸ƒxÿïo!ð‹Nè«AÜÑIÖ§ÔAÕ?¿ÏGÞçûŸáÕ>¾X•­ÁWŠN–‚銶ðLÒ"îøL|ù¯çнÛsøÏ¦0Ɖ_)0nl'ôKÐ".;“>jwgÞ”áã°k6â2¿Dÿ׺aâþPLú¤+FïžíÐcªS>ôü4ްÔJcðÿ!Ü4ª(.sRr£ëäæ %º4sÃ<þÏ ««M÷ËÍþ‚ấH<¸Üî7Øp" Ikþ†–­û`Þ±jË::žO»ãÕÅK±iÇ ´{¼+ffVÍ_ƒeãž…ŒÔiÖ˜×bÅ„¿ YçO0k÷ $l¯ÍÐ~æ¼:ïÇÝÆg-‚Öèo{h—D]û¸Ù:Ž ØèêP…ó9±Öš#øÞƒu°Be\ûóSeŽÑÛ¡åçQÇTÔ·¡:%b²ÃæÊU¯¡ÇÂiˆÎž…oÖöD·÷ºãþ¯¢Ûko!à‹N肘£0¤O(ƒªþ¾\~Ÿïg Æ«}†bev¦*:aHÚLLóùt~¯3^ëÙ/ûÇüo? ¯÷î€×Çû#d|'|Š˜ìpD|W}^ DöB÷·_Æ};¡ËËÝ1aßTø{öÀ”ƒŽ;.1Ùáö°ðàóÓÐÂFï×1Ìèÿ󊌥›s#ñHZaü›2ÐS½ðõÑ;Ó¿ÏïñDÉöaLëë'~… ¿YÖ „{µ„û›*Dnˆ¶wÅŒÌjóÎÀ€çÔñ3D˜#ñý¡©øìݧáN’·Ä †cùÑGÜ—»\³/¬|–!ÐüÀƒÉaÁy:-,äO¨µæ¾{ÌajŸ!ôšÒìÁßKpCßæRlŒ«åò“›aÝ{R<ÞLŠ•«·õ¡9‚êt~ªljaA«W"ò˜Qǵ1ò‡!è3l8–sÌò²Í»‡¢ïP_‡osc Áé¾îƒÿ¥È³ñ½u-ûçoûÒöÀ»$ê: Ž­ã †ø‚+!,¸?ø{ñ®BXˆ­åòÝàÕJŠE+û’®ú°q„ñ¯•*—¦`Õáiˆ<â §C;ãø§Ítà:êÓ˜ÖÃu3ºž¦bQšÿCÏOC ZCÀâ%»&—}gûp-†Ö¨¾rßÊaÁy:/,Œ¯µŽ~`”¶ ¡×dw”.£ËórÄ+¤hÿAÖRŠqÓÝP¾Þ s;ÍwnOH±1ÒƒåðlCá™ÎrlZ%,«džÛËó‰ÏüY¿\[I¹Ò ‡üdx¥¥ýå2Ïʰj±};’Ýa+Ãߟ \‚×z¹ 'Q˜w¿u9ä¡Gu8?U6µ°°cïX,Ü„5Çf²NtÑÎ ¤ïûÐóÓÂF¯,Yw| 6YV±54Ü4úþ¯¬®ë€8¶ŽƒÆÕZG¾Ô¶!ôšä†Ò2¼ “ § .ƹ"Ý[‚æ•#o½ë"´,D»¡|µ´`ä—®ø%ÊëûJÐü9ò×»¡dž íd„—zʱež+Šã\àõ„ÑkÜP¶LŽW<¤ÐÌtÅ/k\‘ÜW‚§þá[²JÊñrs)BB\qq‰ ¦¶#tìŠâ¬Ë!ëp~ªtNXÿZ©²$o¦ùcɾ X}tëeŒÇüT”ä=üü4Ô°jTw7¾!öS0›KwM) ¹_W‡çiŽ `ƒ‹Cm a¡} )Œ±ÂôÒy2¼ÔF†Ì¤ß†…³* þÐÅ¿l¾Wå‚ÞÍ¥X»Ú<¤Øm_~µ°P™äŠÓ+Ýp+Ù åñ®Øú šwqÁõd7dþ[‚gÞ‚’Ýps¡ ¢§¹¢àëjaÁå§¡…LÂÙìqX˜ªÆ¢ô1Xr`‘âoÏHÐåE)úw•à±.r\ß芭oHðò`”ݵmZ—Cþr¯Ãù©²)†LÂíü‰øaÿX,Ió‡V¯D¨Ž­Oµz%¦ª±eϘµ(TÙPÃBØ·ÊÖZ£ºDìGf76lg Ñ|σZ×[íØÚ댰`1y¡Âä}Q+-k$¨tàviÛzMtAé<):µ‘"3A˜þ›ÏÑö°é‚SŸKк‡7ª–‘äó9~]ç‚’¹Rth}gˆ±‡…Õ.8«’ U[2Öó®“à‰.2\ßà‚Jð—ÞrÚ—i›#à ¥9XW}ƒÊ .°¬‘ÔúÜTY™³ÑË aAük…m6Ô°@DfPŸŠ:< ëÍKت5¨n…}«l}ÏÊaÁy:#,XMýQf]ëŠèd\K”&É€õr‡¨mCè5AŽÒ¯íá ^˜þ›ÏQ2ôõ â9n/“â–—ár„ ºO$x¢­ÖD9JæØÃ‚}ˆ– aa•'‡KÐê%)̱rÜZ)Ãô²—d¸¼NŽâp)^ð ,DŽë«ekOè6H†¢¬«>AQ‚ ±u ·NŒF¾éc lƒ±!‡…P½¿rö–1ÅI9‹À>Üe»§–k þ[ï{@ëz«[ÇÛÎßÇÍ#j /¨V^ÐuÄ¥X©¸a!^†]Rw Ö­–ãìD)z=%ÜÕðçöRÄ-~ó °P±F†) .RÂ3ÏI0/PŠ·<½ä(_'Ç1¥Ý[HBèò¶Çc…eÜo]õé/qR\4t­õ¹©ÒvDÓÆ~Ž àZa‡ 9,Øß qbéî)å‰9 ÀÞß•ûgB£Sß·UˆÈlô.¯Ì/z¡k Z×ÊP±Þ±aá‚®.ïûÈ[+K³!w­ I2`[ßV$É`]#CiÖ ZŸ›*¯K›_uXÙ©XïëZ™è× ÛxlÈaÈ>vÁ :¿bï4‡¾G£±Ÿý5–ü0±\cP]¿ï-“U˜MŠÒÛ–/ð(÷~³53?ÆelêÇz9nnzg¶{ש2º { g£å@’”­gŠ‘ã¢áå:äÅémÞ(Lní°²s+ÉÞMÒ®¶á[‘;f“¢ÔIõ~^W­¾´pׄ[qÙsbn“7êˆKœ\®5úÛÂŒþIÁéA­z -©Š«e– Ñ ^SðTâ(JplX¨Üà†\STXGycje¥5g7¶ÁÙh9*%ûÈV&JðSŒgÖ=UëóQÝÛ–@äšú£rƒ›ÃÊNa‚ g’žý:a‡e– XRWPß?2ÁéA­´z•)ÌP¸êÀŒZ¿T+æx¸C_Úåcއ#æx8¢Žhq0Ë3¦`ñËg™oht#˵zu¼Fï×±ÆÑ’ê}¾ôÄ<ʽßlͼ ë„kqŽPåyÝ[¸¼×È]k+-8¿¹=ò"å¸M(#T$ÀÖØŠá¸]Ž– ?ÊEhQ¨Ã¹¨îå}ÃqA×áåæZœçSÚ‹~°ÃÒc`Ù2ð´ãªøúG£Sy†ý­aÆ€Âe{§bMVÈ}_¤}|¾ÙÿæmkÓÕ·ÖÑ8ôÅ]õeDf´U™Æ *µ{5ÌèTkPÇjtª)ín¸–´;o ½à5¯î|c]J“ŸD®É –@ wT¼ud0.è;¡ Æ–5˜#ˆ­¡Öµ2œŒmK¦®(;6´Îç Ê K ¬©^(I~Ò¡åæBœ+®íê#úuÂ6odùÃ’:pG=×çNA“â×+̨NÖèÕ7´Æ€ÂÙ[F]ŸŸþEa•a¦›F7²\£WŸ ÑfTOŸe ,Œ<¦Eôñ°kDf°t*Ïz?hf£bÞ•ƒ#E/xMÁÒ#CPåâ”u?éÞÅ¥ŒG¯¨Xñ½”1?§¼íð2“邲l?ѯ¶qxåàH˜Šyõ^)9‘àt_wMŠ_[MŠ_¯Õˆ*5)~½îîÇ×êÕñs¶.‰<ЍãšçªÃ3 1¨JBtªá9X£÷çç~ô-|”§Ê±5³2w ¬ke(Kt|X¸½ÁVSÜÊV¹l#µô¸ VST¬oæØò’$C^”«è×ÛxüiçPÛ £âÞOûûœîë®5ª×†™Š"2§7€7ÞqÙ¾IÐèÕE¡å ‡€<£O÷Ü4Ÿ"± ^Sñ|Ê‹ø%Vâ”Ö…_7µGþ–¨°ø¹l#³Ââ¼4/Üøö9‡—•«qR\0týú`Ö->…–4¯¶«œ(aFõtAU²$c"Ö ÕˆÌ阻ut‘F§ºTç±5åTº¯»%UQrÛ2¦ÎO–cknáNëŠÀ:.}ÿ*NmW Ò*~åÇÖÜJk ¶zãâæ¿9¥œD» ø°è×Û8¼mKªâ†C+§ŒFï×Q£Wå‡oU²üÀDáTW™ŽEãokôª­Qœîëî”·š|ô×*E/€MÁJëhœJ|¶x©S*Êõ®8§{ gv(PaVV¶[aVãÔ6oœÓ½å”2b‹—âdüã¢_lãñúarÓ&;¥‚j §ûº‡”c4zõ•ð-6g¼Fý›C“1WP¹F¯,ÓÕÉš?ç¶ìä½üOïl«ëchÙÚy#£Îĸ:í@•ë\pùû®Èß2¥ÇFV5Û@-::y©^B‹‚“ÊÇéhWÜÌøPôë‚m<žÞùÙ­£—¿S+ªÊÿ‡ƒÚ¢5ú~½stÑÒ}ã±"k V™úH®Ìš‚å'aþ®±åaFÿ²8T¯ZîôPÅ©tßV–TEa…u¬è…°)Xi…‚Øæ°ÅKœúÁ›Ú"ר~ü •ñ+FöŽ•5.e Å8¶ä6N+¶8 b[ˆ~M°ÇÛ–Ñ0›¥§Ò}þÔ¿&†&ům˜Q9]kTÕ”…Z“Úöuú˜Ò»ƒ°xï—X~h–š€o²&a…Ýo²&á›Ã±üÐ,Ù7 waÞÎ1åáiþ7µÂs.„ê•«‚õ#ß{ÿˆH芸~x$åq´lÍ-<ð)ò£ä¨pbX@’eë[àœ¾'rSàòÞ!¸3°ªX‘,;1WöEnêœ×½‰²õ-œV*’¤È‹”£ðÀ§¢_lãñÚ!?ä¦)štDM°ß–ÙK“¢ü"Ô Z¡1¨wj ª ƒòªF¯*Õè”å¡:%4:Ui¨NeÓT„¡ŽÕèFNÑèTž5z ³³É1y{æmT(vAlJþ¼©-ÎÇʪ¼µ¾ÎëÞ‚ÕÔ§· À•ýÃ`ËŽ[ÇýPaVV¶¾­0+Qv¶¬á¸¼wNo÷‚Ùè… N Už‘á|Ê‹¢_lã2oë@Û £W/±ë,F$ìwEXlGÔ¢ƦbUwÄXçvGT·r lÉmpqóÿâ´±òMÃb€£7[ÏZŒ`5}ŠÓÆ~¸ôýÿâfòÿ r‹(çýfœ'ã½6€kmÚŽ¨aIUœ»¾bDÆœê3èäöA×å…7lí,ÍëZŠãÅéÛ4,Ž“ 7Ò%YE/ÿlã²`ûà›f“âS±ë*Fd„ÖŸŸlG”xÔß°5×¶ï#äG¹àÖÁ–ÆK)ƒmßG¢—{¶qy3K Kªâ¬ØõÓ@È1y{æmQUZÅ/œMI[ƇÈvåÖaÇK)Çõúˆ^ÞÙÆe¥u4rÓ|Šy¬óÌ©Š­—÷~^*vmjÚö}ëZ)~¿ba_ÞŒ“ /Ê…Ü¢ÀÖÁ‹{|K-&ŸÍb×MLÃ’æÕÖ’ª°SAì7î55oŒü˜f¸çŠÊPɰÛÊD .ÆÊÓ ¥™ ÑË7Ûø,:ª„%UQ’“úik±ë&¦bÝæ£ÈÝ2°°Â(zamjVZüqþûv(ˆ ãïW rIDATvÁXX¶öÞŒ%äG¹àBJ{T䌽\³Ï K rÓ|ŠrLÞžb×ILÆšæ}:}p‰Ø¯émªì‚XœŒ”ÃÆ¡­¡¶XÂé(9 bF†‚HäE¹âBÊ‹(Íô½¬²ß+û‡—›M +¿ÿ©9©Ÿ¶¶¤zŸ»¼wX9rÀŠk¥E…[GâêŽà‚®Î$ýùÑn°®•ÁAìïTˉ£ÝpfÝqÑÐW¶¿aâ—Kö÷á•ý¾å–TŸŸ8(0uB¸CÂç§‹{†Âê–eYö÷幇Ü2›V¾óy$N¥ûº[ÒO§.¹£½`³,˲n…YÓ郊­iŠ,nQ`ê…Sé¾î“÷òÜ- [á?Ñ 9˲,[w‹ŽDnšOßõÀ8kš÷'“âÚÅŒ!å•5`eY–e‹•5~Ù;¬Ü’ª(áç(0%W¯èhMõ1ånQØnf޽ð³,˲×–5¹iŠ"kš"É’æÕV캄i"䘼=-&ÅÏù[}Šnf~XU,˲lóúáá8¹}P‘%UqŽ[Q8•îëžcòaIUœÊÝâSøëA_T˜Å¿8X–e›²•®ôEÞVŸbKª÷¹FÅ`±ë †±‡oO«É[oNUžI\xý/ÊNŒý¢aY–m Þ:î‡_ÇOéƒmf£wynš"™CÓ`9•îë~ ž›¦H¶¤*nX·( Jd»²®ŽÒc#Pv·süPiQV–eYöaVZ”¨0+QvÂEG?ÇÍÌḴgHù¹ÝŸænñ±YLÞ7­©Þ&sªÏ Sé¾îb× Sc„g4xµ=aT 6½çYS½MÖTŸÓ–Tï«“Âf6x—ç½Á²,Ë>X³Á»Ülò.µ¤z_µ¦*¬–4Ÿf“÷Ì“׈<£Ow±ÿ½g†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†a†aÊS£SÏ›mc 3Ø4zU¹V`›m}r–)p±&ů—ØÛÈ0 Ã0Œht*Ï0cÀÙ9©A¶äÌHìÈ3áÀé dž;„§3°#Ï„‡×–ÏOg›e <§Ñ«>{›†a†qaúÀUsLAźì$dž;ð@ý¼ºì$ÌI ²…ÔËÅÞv†a†aL˜!0yéöéE{ÎîÂÁŸ÷ÔØ=gwañŽ©ÅáÆQF±÷a†a1˸xÑÖIE»ÏlþŸvÕÚ=gw`áöÉ×à sÅÞ†a†aêMŠ_ÛYÆ€¢]§MÈ8»­Î¦èf,Öèý:нO Ã0 ÃÔ#azÿÍq—ÿp:jÂ¥åá¦Q›ÄÞ'†a†aêNY¾µà;¤ŸÒ=²i¹¡Ñ+˃ӃZU[Åt"ÚOD²»V½ˆÖÑŸ‰¨ˆˆþú€Ít'¢3Dtˆ¯6½-ˆ&Þõý~Dt˜ˆäµ=÷áq"ºJDOÔÓòªÓˆv‘ÄËf†a˜GC“â×kÁ¶ñ×·Ü„úrÞ–/ 5:•gµÕt ! <[mZK"ºLDoSÍÂÂ߈èýBBåZEUX(!¢vÕ¦sX`†a˜ú D§ \¹;¸$­`êË•»ƒKBtÊÀj«q!¢cDä_mÚ{DtŽˆ<¨faa¹ÝùDô-Ý©XÛѯDCD&ºîºÑA"šIB+Åe"ú ÚüÞDd%!€ì´o“+eا]&!°|hÿþd"ºMDO’Ðrr˜ˆ†Ú·OAD?Q%íûIDÔ„––9Dd!¢ÿаð ]²†a†N9sÍÞY0æ'Ô›«34åÝÈ)w­j •tU彆ˆVÙÿÿaaÁƒ„.ˆW‰¨;]!¢?Øçµ%á/þ¶$„þöé e$t´ ¢`‚Œ µ±/Û›„Ê •º ý¶ea)-$¡rßJBˆx‹ˆZÙÿ< ­)7ˆè#"zšˆˆHODRÂBÅ‘>$!,´%!\xßç80 Ã0Œó Õü|鮩…)¹Ñ¨/—îšZxWËÑsDtÓþ_":KDoÚç=,,ô$¢"r³›GDÿ¶Ï« -‰èS":OB…þ °p…îŒ{x…„–w"RÑ6ºÓjñ¤}Ùmè·aá}"Ê´O³’0öb2½^mY_ѦjËzÖ¾ÿO“®Ñ.~öý;IDßwG0 Ã0 Nå97-èæwÖ5¨/¿ÞúÅõ{¼7BFD{‰(„Jõ =fŸ÷°°KÂ_ïÅvAÂ_éúmXQ*-#"OºX¨ªÐ«>[ìŸÃí˾VÍBZ ª‡…§‰è"õ !4ô'¢íDôEÙ—»Â¾¼*ZÐ}ñ a!·Ú6ô³¯7‚„@Q}ìÃ0 ÈKpº¯»Vï_¸>g9’-+ÙuÙK Ñ«J‚Ó}ÝﱺáDtˆˆ–Ñ7Õ¦?(,´$¡bîKD­íö%a¼Á“ôÛ°@$t\'¡›á~a¡*Üýy ­UÈIèöÑoœ„.•Å$Œ•ø+ -%»Ièv "šFBËB%!ü‰„°P=°ô#¢}$tw¬·+½Ç±`†aqÐêUúÓË×›—àQ]þãWåa&õúû¬ª5 ©ß$¢7ªM¯ ]I‹P¥+ éY"jVíû‘0>Á‹þ;,M!á/õLª]Xx–„ŒŸÑS$´ ¦;c~%¢¿ØWÕ 1ØþÛZ=ÚØç¿D˜Oû²Iä(£{‡…ªŽíˆÈFD¿ûà1 Ã0Œhhô~5:Õ­Ø£s˜³ ÎÆ› ­A]¤Iñk{ŸUIIxžîtA a÷p ¯¸Ç²ÖØ—õ<ýwXxŒ„Ê8‹jˆ„–Söõï%¡Û€H+±•„ŠüiÆQ€ˆ:Ùç¯'¢l‚‘Pñ"aÀ"Hè©~7ÄýÂÑl":PmY Ã0 #>aFõô9[FÆŸ¸ì9µ6öølÌNU<Ëè"ö¾0 Ã0 ã‚Ó}ÝÃôês¶Ž.Y“‚èãa56ò˜s¶Ž)ÔèU÷ë~`†aæ÷@pº¯{ˆN5Kkð/\¶oÖ ~¨KöL„Ö . Õ+W=| Ã0 Ãü.Ö|S£Wå‡U…ówŲý°2s V™Š•™S°ìÀD,ØTfTjôª“w=Ú™a†a˜¦@pº¯»&ů—Ö ŽÐTV^eÓè”å½Ê¦5ª¬Zƒz1‡†a†a†a'ñ¸EÑ0 #IEND®B`‚neutron-12.1.1/doc/source/admin/figures/bgp-dynamic-routing-example2.png0000664000175000017500000040364613553660046026236 0ustar zuulzuul00000000000000‰PNG  IHDRÊeÃÜÔ{sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|ÕöÇÏÌî¦'„Þ«)¢T±£¢ØõY°?õ)¡ØyŠ@]IAÁ®÷ÔgËß*ÅF¥÷zz¶Ìüg’ ›Í¦l²!…s?™ÌìÛæ;íÜ3çžK$A! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@!PJe2I! j»}õ -içÙ"H ¡ã¯ÙŸ<î_Ñí{J˜â>Ö…t­µ®èѺ®¥[¬jZ ê²Ånæð.ÇW,¤æ]Ü£ãÁaƹ½ÓWä÷£ÉÉM]%Ò3­›W+:w¿Ý~©Ë3^¶KŸtÎ[äŒÄøi%÷V-Æ×ùVnwS[ëƒvûyU+½fr?j±M¾Ë1R¡àÿ¥$ŽÝZ3­Z…€¨mDP®mgDÚ#ü$0Ú>©³ÓéòýbWB­’ª*ô¥ªÛþ3-ñ™Íe?Òž8PsÑ#ºN7éá%Ó*™xẖ©Öç§&Œ[oî/½ d[ý€BÊÕªLžn]gæ)o=<.ñCÒõ»½Ó¡,'„÷m¤+›,D_NOŠûÈ;ÍéþûÑ7ÞÎ?˜‘©“nSÂÔV3bc’IéçW›B‡u]Y©Z”))cç²Þê,kD|âóš¦?«(jÊŒÄØ‘ÕY—”-„@Ý! Ö¦JK…€(“€BÇñ’ŸÆ Ä•éX>ÁúO¼-!<åÒ+GØûû*cÖ¬Y–˜¸¤çÜNZ¤ëú½vŽ"ï7Šª$cyRU•—  ~!Ù !õFgˆû}6(ŠòÒÿŠTa\¦Û©¯‰‰Ká#W™Q(gnÑq)ô>)´å6ÅqýÃMú‡1±‰sGÄMîRf!§ÙÎ7{,”Ù`µ°•sÂÁê:|\'pý—œ§ÿa™£“’s3Tsk8/ ÉÕU·¿åòŠá±‰«ŽMlï3¯¢üŽãØÎYî}‡D !PÖ€–&… !Pc <„&l´wb^x¡žéz—KÍ ¡†èlï4s×lž-òÓ¬ ©Ï·²ž?É—y›dXéPïi¥h(}µáÉW^ Í>šûÌ&Â$ä˜øÍH·Ö» ¥ýÖ!„ÍLˆýÔ{ÿÈøä 4Ý= BøåDùAê55)n·wºÓõ7L.n,8öøêC èi3ãò¬çC—t‡Fô¦Ó8td~œ‘‡[͆ülíܽÉf öÕ’Bíw_û$NÓ—€h”Oßs/G~š˜1nÜ ÅSx¸=YØõ<ôÏ&]Ùæ)h‘5‹b¹ Âv‚/!™ó̰șf[♿¼íWÇŒÉå2QþפSi®kËËS‘ýÓ&,le½°/„ûYò#œDS+’¯2iìöwC*“/&%ÅV™|vû¬ ²òU¶=¥•ùÔ”)>ÌlJK]v<´ÊzJRÜ'8/ï@0ÅǺ¢ìDl*Âvy骴_Ño¨R~¯Ìþž>>ï{Ï«Hù)„@-$ åZxR¤IB ÐfØŸ: ³‡¼¬ÃBm‡y€\ŽYÌ2Ʊ@ƒ¿™Ó',4㽆4å›PSï@•ÍýhûäÇ]®ü«plׯÄ%ß4#qÂ×#ã.ƒFóm˜ü531î®ÒêƒiÀkhÓUº¢ÚYkpÿ„æ{‚¢Øn³íÍvæLBÞ ÷»ö‰Ïö¹8†ª…’§OŒû?_e޲Oêäv¹þ…}×€g'}OzpOG+¬ŠõÙi ãWzæó¬O vÖòôÐæ‹ö»6·…&vL[#Ǿi,ãû‹­NçsÜžTgj÷áq ']Y¬ØÔ§Rì6x–ËÛ¨wVQ) qÝYxõÜÏBÛÈøÄGP×0Ø1÷:qØ{?ÿ.üÊð_œ§9)‰ñOq¸ÜDÑ™º€;Úl+úU¸êa¿ë_$ˆŠÎ:\Èû&Çq(НÄ5aÇqI÷ ˜1°îŠ6„á¼YÌa£ðÂ8¢s‘è'ÛB@Ô¢Q®çAZ!ª•ÀÃödã% -ß¾×cc‹lVGÇ&¶…´p)$7ÙBb«³:©FÇv­¬çmûØ4”o”©_ÅëÖnü©¿!ŽíŽGâ:ì+þŸµ¨š®<ÄmH¨bØ¥ÂT !„™nºî¼-Û•½ 5ÒnTïB#ÁOïïvë_Ax25ôÅ u»Ü ,Ê …-÷OÈó1„ ׸t×¢{ò™žÌúHwÞîΣ?ïBˆ´ËPßï2{Bh‘ïÊø’m°!$ÿ¸ ÿv,ßb C{®Ö]î¥1II-=Ëåm¤íÂÇâÏ'„Ò_P÷HÔunam?dçmÈ5Bf€Þ j× !°„ç•‘ñIÃСX:oGýtHbY‚´½4MKA§äkoí«3(×bœ¢3¼Éü Sœð‚ó§´*ŠS”(w?®»cFœB[Pç^T6¥+<÷¸–š˜q¼6ÏQe® tF^Ó÷Á´-°~ ’òo܃qÜ|?âÜ(»¡sÿ\UÕß<ë”m! jÑ(מs!-ÕB`´=é§Ë=ÂP–B–Ç=+q+깤»¡pTv±ÖÙs_à·µ ÊÔþtÙŠFk h!èÝù?»°‹‰O|³ÆÂíLjz–ã=Cf†B'<{@H|s„Cžû Ä Ó üŸi}€MWÌ}1ñIéš6SÓ5Øp/xÇÛDvO)VÍ™b[aæáOô©®Ôÿ<\ÚÄÿÓÜg®!Œ‡0õj×tôŒ# mçHû¤šËõ´’CˆëÑÎáÖðÛM—''7väh0;Ñ/Srµ§QÖ³¼²ÖÐÊÆÕå(ï UnŸéao>Ò>¥Y êï{ fY…zí34©ñ‰×Fs§¥(à @ |ø/„Eõfþ`îäcrfkŸ¡p#¹ÒÇ"Þnî«ìzFBw¢Þ‚&w9Öý z,%É÷oþ^£ìI½]Ný1Ô™I¶¦íØl©ð^A[Æâ¼ˆë/}fB||a¼¬„€¨…¤9¨…G&M§]é€O×+<–ux!vº´¥@ñ§j 9ŸÔ¿òÄ¢‘ÞÚø­CËVMÁð¨Ÿø hg•ƒVkð7® Nж”©il­ËLÖgb¹ßn×K<ë`rŸġTµ¼[²=J6Ûu{ Éœ&**ˆmoó©Q:-éà/%aÂbO!™÷~…Uå#Þ†èÓ ½GDGÙÆ˜B2§n¿ šÐï ò±–Wm É7u„#4?ámhG{ërþÅØ_h‡´ð1„…ª·y»›n:Ý[ø/§È»Ù«DA'….ûU­lÇn&t¹œv\ °]~ÝSHæý|LÁáêà‘‹ŸO³¹‰™¯æ×þ]ºK;—O ¬^¾ó’ÃRôÏ çÊš?.ierYtdŸ¨Kn…©yQ“u%Rb#h*±¢An—ãN­oyN‚ñ{ y'ì†÷åóØ™˜ØZË¥Qææx2¸Áüa®¡%l7sÿf"¡ÆºF-~Y³yZÐÂO>Ú2Ì0•03lm6VɺÓ CU©¿m¿-&.q~ÙïJ„V–~6«c­¦Óé-{z+ëy%íq}/ ûKO? ³€ßPÑP·¦uDy0W(?´V[-Huïã„%L$ŒÜ¨oò3Ïdz—¤Xh.¹i4äý-°Ëe“‹bAµ¨sÑî´+¶£”ªÓu\êã<|èi‚SJòr£qítDÇl'D™*ø·pÞ€¯8°ýÖn¹ß>Þ{rÖ#X¬)ÆÚëk÷qÞ¾€0}¯Óé¼» :^éNùO?¯ ˜õ4,¸õÔP¥YƒýÊÁŒ\pjÀ8»ßD$!P+ ˆ \+O‹4JTŠÀv¸ê*Ò¨r 3¨-ìïré°›Õ^ûeÍ–› ,1…e7°¿E€ Ë«Á 3€ü"WnH…Ä!ô–òìЛB0}‰Ël†ãªB¯+!4ez\\ªȵêrvr¸UxEeë4ÛCñ ÖE‚²Ëí¸-´@¤þЧUWö•áµâ@ˆcY0Èk—ñ“ýõ:²5®³=¤ÅvÐ"Gcð]ÈK¶¡-Ë¢?Û÷Âb™”½¾öóì‹Ù”Å)|¶Å;Ú÷–Þ¼ÓVäw¡pZ”Â`HUaò__¸Á×ã~ןàBŽ”çžÙ;ÃŽq¤>!йëÓÅçôóšPl¼Š»gñNü{Þ³ÉÎÃ×ງ•"${’‘m!Pû”ò²«} • !à?B!p ”@@áÉDÏ]»õ”ô.MQ,Ø„Éóêì«tþø~æ>x ¸BâOæïk „Zû>Ž×Û‘PKðnOsé¯]Y2‡Œ¶¹X‘íš~­ìMOGcn|ÒþJ£WícŽò~p0Ì.²¾W,}áÃÅl–}¥ñŽ{ÔþFT¾ëÄ´¼÷­ØáƒÇ´}C Êgáš%eï|üÿŽøŠ?§›ö­'£*±…ÊÛs3О•Èî+Ëv|Y(Ꜳ'raÒèGGÁÞø56¥ðÌt„–µ+ؤëÐx&¶JZšÑñQ¨Bšr3;Å)n÷Éšˆµ¿×DKŠýk?%mÀµÖ&P)ª¢¢…ª›Õ\í:·¦cÐÜçÑ6)Cê#PÂn¯úª’’…€¨)…s‰®_XÔ‹ š;z{HU°]ùÿIJ§'ÆÏç%%aÜêS!$­×µKŒV+z‘þÍ6¿ ß…ö.8ÛsÇŠ{±$ê~¬Í+m⓲„8.Ã;ðçsx§øBà]zPT[o`.ÁëË¢³ðlHÄÞyù·¿õù*£Bq* ØÔôFJïg"ÃǶ¢Þ<Ðp’wöÆ4v?:eYè”y­¹ImÂy¡žNó.£¬ßº[æý(ßg‡¤¬¼åíó÷±¦Øa¹ÇûxĸuZ çhû±æ¯ !Ôß›21î»òê•ýB@Ô,”k–¿Ô.N!8Ã2‚lVZ 1V–°–Ï™£×IíÖ¨øä‹`–ÌÀœAŠíóØÌ5$¦™¬Ê…Ù”5Åi¬ÿ®™¦ªëƒ”Ü šÃ‹!ñîlm»`˜·ž›ílmÔQ œ?m£ííP:ø“ÏŸ´J¨:Ž…atFiOè™·ÀÌ@ù ûBØNÜsŸç¶¢ëŒßº9H“Èæ5”̈óLë¹ Ëß&Dž‰Ná¶%_e³”(HüX,ÊÍdQnµÙÔ>!Ít‚¯èOaS¤*! *I@åJ‚“lB îÐ.å6cB‰UžmWmô( ’#câ“1R¿îcÒ Mƒ†DEŸ<5aÜzïÖ‚›‹ÎÀyì‘Ã8®Ö6HLsê=zá=ėͳËå¾È»]5ñ[%ueA½ÚmÞ~ŠÕö¤k,‘ωۅ1•%<Žè˹.·ËyŸ¯:Ù¿54·BØÖȦ²Ç#|P2!d7б'õ5㋯õ+‹ÿöøÅÞJTÅUª€î‘: ›—3½ÓžáÖˆx’š™ã¾|Û»êÍÇËHRˆÕN@åjG,š%0jÒ¤†Æ t:]á#CµªŸ{¶ÈpgV0ݰ…4íáñ‰ <¥°gšÚ´ ^ao°—Nt;Ý *>‚ïºVÖ6Î|ÌöÆŸ»É¥èOrzl~kÚ+ûÎá_,:;9´Ôƒ½MXx²x@(a†à_ IÍ3âÀä"ÔDwzÃ{ŠmjGÛ§FTµ¶–Ö.¯‚Æ66qIu'ô,O¡àœ¯#ª÷”·À˃ý2Žç¿ÆÂ0(¾;Ã>¡˜) ÊÙh”åÖoñ,“·‡?›x ®Òe*8GnÝ}½wÞêú !ú(6˜ý\ÅtîœðR²óP]-r…€¨*kU üB@ÔÛÀUW‘Í#„+¢Vî,w¼°CñS0«L³ßáÝb¥mÓQ´÷ð^˜'Äb*²¸üƒB…¢`áév•6(«=¶;yç­ößš>îÂ0êÁñ`*àNh‹ñù‚i.üÐ=Ü,r²ý±JÕÒémC{ÄÄà`!¡¾Èv7§ VìWþü‚Zg޶mü?0O…Nô,]wÜ ûVL8BW£îRÍÙž²Ê²Z­O;]®þ8×ê{Óχ;¿_1P-lú§:“ú¡­Ï!?ÝÊžðeij‰c47fÔõ$xùÜœÔ%%qìÖñÉ×câ–yð5¼çösœÏ5hC³TçÂ+Àè\«¬Ö ¸-°(vrë?â«_gÀ fÌ-"!t÷W4ž$”éÈ_L0/*CU>CÞ{!¸>û¤=®ùÐ/7›‘›P”&À°±~m:ÐOœ_ž<6–à‹1É9<.q¼Á|i U_ðôh$’B@Ô ¢Q®§A!A³Ìét¹@h» ÂG ˜$¬àñ…)%&1kåAož·Ú”†ÆQ¡£¼¢Œû İ_äÛ!€ BúeóñÁëÀ;8 Ô¾û/ïÝù§@¶™Í-T«õf™sÐÎ30™ÉSº¦¿ aùZrÏ ˜äC_È:+[û—†)ÀYàð>L¦[ašìQÜ^\7ÐÆUð½®²e{æ+¨¦ü„r£ó²µÉÅöabÅ¢\au-xÝ‹ó2×Ï.Øš¯ÕV¶Öý|ùÛž91v¶¢ªèxÐ>ä»~N^†g‘g‘ï x¸µµ­õ“اyÖenϘû®Û 0ë€+i†ú TkO™û½f/+¸V¯S_sX~䅯ĭ€Ý”¹Ã•à÷Þšý@·EÊB ò𜒠„€(I€?[épW—Ûâ¶(Çò)ú¨÷ c%sÕÞ˜qIÏ`êéU™ ¿ÏTWKŸ°¿çÎëˆ 5R ÝëUWU)wtlb[·MlIã7Õ„O_ö;ïÐÛ†Ú(ÕŸIP8Ÿ3Wi¡nó5YKipجH;”ÓY³¸-!Q¡[_3&·´´•çÙ(1ÑÎ.¼`£U«rù´çb—C@F¿éd`[ìÇó1¸RonU-OK˜ðÇɽ²%„@m! ‚rm9Ò! ª´ Ì!6C‹ØE±Yºû°}­¶º¥àÓÀˆ¸äk4Ýý´ã3àpDià__ô'XK>#!öãÒÒI¼5G@L/j޽Ô,„À)"!y´!$“òƒɧúi\ ÌYz‡¯ÒÚ²0ÀWË9Æ~ \æIB VÁ|µò´H£„€þÌžŸžu5—QžÞ)&¢\)C”I@U#Œd„µô“p¹¸œÚ4þ‹íäÍ<£í“:c@e<:†Íÿü”çÆoFÎÜ-k! j1½¨E'Cš"„@` K¾A!÷LX\`Â=^;t æ{<%1þÍÀÔ ¥² À¬b,.»Iø’¡b_^¶«q=ƒšC@nÏ×$â¾·ÚÞõ¶ýᬲK“½B@Ô”kмÔ+„@µeOêÉ.&@Hi„‡ÜNRÕwf$L(š¼¢Ú*–‚…€‘öI4—vTËá£%Fó±@Œ Yhw¢öVbüNä²)„€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@!à›€øQöÍEb…€ðƒÀðØÄ{E2%!®Ÿ‚ ?²4)fA;—,AÛgØŸ:\^ÁÃãþTåC´yzyi}í·ÛgíwmŽÃ¾ëpÀA8îï]ÿ ÛÉÁa–¡oN˜pÈW¾êŒ{Âþjtž;·çô„ «³žºPöðø¤)º6rFbüÀòÚëOÚòÊ’ýB@Ô/2…uý:Ÿr4µÀÈøä ÜšûOŸMSèèÌÄøÆ>÷Õ¡H‰Ít]ïS“M¶ÛX÷;þFzîÛhdz-v»®ÚíŠfþ6×Мíyæo×\›í˜Dâ Ì»ö:¦*Þ ¨t“°5Ò5­î°Øü-Ïßô¾Ž+×™ýtbÌ /4œ1nÜ ˬlzó·(tùôÄøù\Žw²L%}¨}˜nqj}ij¸õ'÷UÖŽðˆ*vMú“¶š*E !P{ ¨µ·iÒ2!P¿`òÚçÉ¢Ü깨ªrý:Êš;»ýR—®ÒE±ýϳ©®ÄÝÚ_lãˆmhŽïÆ,ijg$ÄŸ™÷AÊĸïQnEÊ—ð`ª+i†wZU±}އúS§RHönC‰ßŠ:AU,×¢]Ã!$Œûàr§æZ>ÚžtN‰´!„€¨eD£\ËNˆ4§þ€ ðûô‰q†¶­þeÍÙÌ„8Ö&…‘ñ“ú¸5WÀ…dÖæîw%¶FyZQe§pBú ¨®„iÇ´Äg6#ž—Z,¤-5µÌܨÑöÉ3\NÇ:—K{?ÿQk* B@ø ‚²(%N5öäºËý1¯Wg$ÅiDGMšÔÐíþžHùyFbìóÜ®q“»hä|ö°—B£ÙyŽ’¢üd WÇO?þ˜Ùö˜¸„%ÎÇéŠÚNÓô‡ajÐ éW)ªò2k?câ“î"]‹Aú~X Œ·¡}­(lâ}ºBC#¬ad»²ß„pv1êb“‚%›å‰éöñ»Ì´¥­a<m…Õrä?„Oïßµ²¶™`·?ç+Ϩ¸»¹tç,¤{¶¥?›i†?›x ¹éYh৤$Ä~hÆŒKìÖ•×mŠíŽ·ŸÙÈǬ+Êû,0cßpMwMä´ùNç/ØçDûƒïef~h8]#â’oÕHÅmŸÃàðQQÁ_zúél3ç:&6ñ¶TgÒpÄ©`úì³ïÄöAh•¯ôLç¹=<>ùEÓâÁ 7êp"ß +ã6{¦+ïÜÆ$%µ¤}¦®Ó5(ã8Ž©ÀþVWp\Ÿ£m÷¡Ž1¸VÎ1mÅÍëÀ­*a€òòöAÞ#¨÷ëè(Û³“Ÿy&ÓlCŒý…vºËõ‘~ ÒD#~/åó~h‚3f$Æ]d¦­ìúmûØ4´sÊíUVå±ðÌ‹kù!öÈ8¿]qílBÛSÀÁá™ÆÜ®hZæ†òžRmäМô,ÊŒûé…”¤¸D.«"×öÃÉÉ]9úCš®_Ž6 Ĺ߂2—â.ú_Š=n—ƒzpݨ7ƒùôP¤[†4‹Sc§C{J9½ˆ6\‰ýÍI¡ÕE›–0á.ƒCEê*H)ÿ…€ð‡î{ B@Ô4û„ xþŠá›Ç&¶7Û!ùuÒ©£-LyËŒ#ÝÑ·PHþIW•!þ{Òõº²Ýo¥)Øèé&zIÓµÇTEŸÚqˆŽ‚Ð< Ÿî_Ó5ýy¼ˆ?Uõa¼˜÷á÷«ñÂ. ŠÒD!ýÚ,WöïØb!ÛãN„ÀÔWsºW@3ØÂLêk="6ñvä›À÷ Ú îÝqKa|OÔö©äV,,”Ì2âý UQÑi(êñº~“NîdçW¨sâ–ƒå˜Ìãù“ý>þå!t6Þ3v)ô³EU’T y³/Ê926ñnEwÏ×ýÚö8 T¢]äX1">ù¼¢„¼Qιµ)Š,ßG[Óz/`ð—QÎ émlŸüל&££‘Œ}³Pÿp¤_„å‘ã™®)f²Gßx#Xw9çàzë®’åq]±†‡/ zo°ÿF±ª#Ì´U]ƒE4ê?Xf9å°0óBè »ð™`º çá^ßLï¿qÿe¦1×þ¤EžžhãU’¿Æño÷1ªj™ÍeUôÚvfkŸAø}÷Ùl\{ÿDÖ±tWÜÜšF9c5['-K'¢Æã8Ž¡WÒ¡HHNI±éÎC<8ónÜߣ-86 siÚ‚˜g“.år8”WWA*ù/„€¿¬þfôB@TŽ€›”A{t™gn\KMÛÖ–Ö6ãö;S¯pýá1ñ“®×u÷½P[^=uÂ֭ϰÁ‹¾„v«™¢+×™æq¬¶†íÞ¶?œÅqÄ%ü”¯ÓM¡Q!6ÛoÚŸÙÇñ’>ËO?qBã-øùÇq€ö1 —)‰qÏÄ`–}Ò”·¸\ùO!Ž—…®üôŒ)x¡ÁŠ7&xú Û;ȯIIœÀBC±ÀƒîÀh: ƒÍO¾òJhöÑì¡zßAÜ}£íS#ÌãAÜ¥æ6Ì<Ì<¼ž™0á7x3hIšþX°5è;óX=Ó@êj ³tðàû„©´îEº‡‹¥-ü3‚_c ¼ÐžôtRþžž[ ŒûHüÔ”)áÇó§ Ýg3ãî2“`àáû]þ©iÚkà1ÈŠÊ;·…íœFQ掲ê6ë*\w WK'„8nÓHN“8ó|’ÑqúÎÍד•01ák¢#wæ8®ªaT|òEnMöÜKå±àœ´Ø@ÏrŽÇæûàûY¼|žíÌÞƒEQð'­™ ׬Ţ\“©ŸÌ¸Š^ÛÆ5²7ýR´á Ü?fGê[³^£Ã0«£­­qx 8åë¼(èûáë…~ŽÕ¦öyÛ»ªpLJÇ%î"M{¿V¤®¢eC¿ ó-ASAŸö/€ æÉZ>ö¼`6G€Æêü¸叫STRÞNIŒ‡V¶ìm„K½ fÅRb°Y‘P‰o%ÆïTHÙ…e‰§àøæcåãSøB´­m±üø¡ê¶=ãØäé~Ä[þjÏxÏmÇáÌ !¶…†ï]Ïx=T5ŽEW´ <ã=·!4þ‚¶ô~ÒþJ#ŽÏ:šwVAd³&A¸²9µcC9žüîËéùwe‡‡lÁB‘iÖ_™rÍ<™™Žóq^ZB}ËŒãµ1ðPW¦¢ž£Ÿ¡£ç>ïíRÏ­wÂ2+?”wœªNM Šp¯5‹*Þ”M8†–ÞŒ÷g Ó˜Û`žò:s/ ò½[wÿ ÓÅÔ¶Y’?åpZoJ®#¢-Še†gY¯ÙŸ<Ž:¾÷Œó'­™÷ɼéc‹„dޝèµ=cÄ'®¯Xd3³LϵB–5Æ›p&=Á^[<÷™ÛxnÜÉÚr!ÙØ…¶ñ½Ô÷ŠÔe–'k! ü#àóÆô¯I-„@Eà“ýƒžƒš|噞0þïáqIÏÁv8&›u[Ó§½Ó±†5çhöÐ(^ !¬´Æ!”6átª¦yw~S½óãÅSƒýÞñÐ|e .Ê;>¤‘mwœªÒVM#Ö†ù °ÇíÄ;47M†€4ÉL¤çjlë “MjoÆy¯-Ë\Ms)Ù®<þ,ý5’sçbñLû¸=Ф.Eîÿ¥’ãºXƒ?6‹Õò³wý Á«Ä±áó÷1ð§|rU´œRÓéú¼Ïb ÞæFQÕ-:¹]®ÎØ·ƒ÷ûyn½‹,õ7˜ï-±Óë8U‹íw·–ÏjÎÛ‘ÖÐö>ž”Ô<7W;ùYsY©€ëêF­9ÈœŽŽÍn‡·ã:ÿÒÔ¢—VhEXà HÁud±mõ.Ç8f\8fð'­™í55¸'£ü¸¶«åVŒ=øúúMè$ÌFyoxÚÞCsÿ®é$ÜÉpm8Š!áê4OܸÇ;A(Å}TL£{ž;’ªûh6wn·•WWÑȆ~AÙ/\’XT?V~¿C‹g¼®b‚oŒý¥&YGrBðÃËs†Jê×°oLGžkg¼wë ŒäzÇþv•_"Úâvk'@ÛÂ!l[ñ²FÐyyMQÂÙn»Ø68Ûk7éÊNï8ó÷ÛöñÛ Tì‚F}0>ý‹Oÿ׫ºR lëÊ·(óß…^'.ƒÓìã !ÓÌï皸jJ°ËY¢‹î„ÿeÈåJ0ïó÷Ü–(¯ìˆr“Ù¡“6ç4 üoÁIÝ !™µ÷Ë£„uvÊ®¦ä^tïñî B@,™Ð#¦â,ÔtÇH qCÂ/Ð×ßfÏ8Òäõ]âÚõçÚžaŸm¼~ÖÈç’®C{žÂ-ñL&~µ†[n6ÞBpŽÃ Í©èD>ŽNïù9îg`×3=)î#nâÂq¿/Çà=ãwÑǦ+D1¼ŸT¤®¢¼²!„@… ˆ \aT’PT?‘öÄšK{Ò$ í—󨫍u¸Y³îÌ 1µ«ªªW¦LŒ-29`Áñf²€®s²]-Q`q¨®_‚Úöú’¹rUW·AW ‘_]<ÝûW%4å_ I¢ÿ—aUlß8uǤ”Ô?C/bÀûk[ÐȲ’ÈEZŽÒm+òHb´ÓeÄ–º×Ĺ5ÚáñÏf¥ÙNÀ¥ô%fA°cO(óJ;ÏYºYQè(íæ«^ÉÓZcUl‚´¹…ç=áOÚ²Æßk»Ýw(ó;h¯Óµ,÷øýŠYÏŒØØØ÷¨ýä|gÆÇè?½Ž™?·Û‡9`Ǽ ÂrîôÄØ÷Ìô¥­+RWiy%^ßJhŠ|'“X! ª›Ûk.ýC|úþefbì$Pá³ëC1qÉæ`8nBGhɲZªç/0Û3kÖ, „…;Íß^»ÜîbƒGÚ§4C»z@Õµ¨Ôº¬¶e_²u§^$ä—šÖÇ 4씡=Ʊ+«Ø¶š“¸SS¶é.ާ³!ü”cv¡°B·»„í5ÇWwi¾ÚÀ<Ël{^<èÚ=lö¶¤Îf'¤ÂçºúP(ü˜œ.í \_ÁÕ\2ä70s.ÚȲè©baÕ5tD79µ›=È_ÐѼÌ3Ο´žùJlWáÚf³ à\á÷’å"âMûclžû«qš{{ONƒóÌS®ðô†ã+¯w\yuy§—ßB@ø& eß\$Vœ´DƒGÆ'¶ÄfáxæÃëÅ7üûĉ|Ö7VÂã%:sbìl|¦ Gg3áŠm ÇK^#èþý®E±ðyû®ª¹úÍ]½åC«q‚(›ŒŸt4ÈbïtkM5Wþk¦\dµ”ú9~ºýéôqI 0Fžm·¾hñÙXÉÑ]Ú˜ŸKŠeꌄqEƒÆLæZ³Ï'-ãËèAĽnÆó ¾Aü(t(ô0[è|Ï}ÞÛWœÕùÏ_Öl> ÷aÿ† ñoÕfs{bôNèßó\ðÒ|‡ —|„’á°CUøÖ¨¯a¤ >S•,Ìdf¤ke½0ÚÖ—ð‚ü—îtîÆX°ðRÝ¡RÐUn  æYTËõðÅ<.Ï騋YîØŒ¢5ªËÙ&ÒG–¢(L˜ð"`„s³Û©/u;µµ|÷޳Ÿ¢«Ð²–fØŸ: MÚßÚ °Y ³ 35<|Ãñø½Üðn`îð±6ÜÆ)êÓhCé»0ñÈɪ5jfB,|U룠)¼#_W¶»tÇ&p¸§2÷œ<ÅŸskµ}Šcúçe–žé<žíÌy¨ªƒI-ÐNÖn7A[À¦v0¶o€ »Yp[xƒÉ†TµŽŠæ÷‡Ek[ëQ?ÀÌ©ùNGª®¹£Û‘…/2÷{×çOZ>¿+vm‡ã4k½àÂmANŽ–ærº¶cR”—Áø¥VÖ /sy’݈KÒîYÎì#.Íý;lÿÃU«rí«cÆã x:r5Dé3’õHû¡¦çoÁ³ä'”=æd›Ê¯ëdZÙBÀxIB .àYÔ†ôè”êˇp Ž'&.éßx1¿„ÙØàBÑÙ]š+(Ïâ9"¿¢õp^Ìî×0:2(Ýs&¸ŠæD:øî RÔaOwy(ן2xö4 ÓÌ\¥å­è¹ûâ‹‘ÙÎF¬--­¬ŠÆC3 ³|›ˆ°ÞÊ™™¶²Aû]›·¢Ã¶&ÅÌpÌ4Õ¹®( v¯–FKÚ\Ö㌽åÝþ¤-ïØÊ»¶Ù¤*;ÛÕÜJªã¢x·ÍDŽÙ’›ºœJ•"Ò ó‹R*e3«¹k6w P5gH—.éÞe•WW)ÅJ´eA¹ 8²KœÎ¼åÓ™E}?và9ît81¹Sqà}¼0øqjMÊÞm‘ßB@SI@L/N%m©K!P \Ò¹3ì‘•М<à9Ñ OfO #`ŠÁ“Ë|^ ›.MB@T+ÌW­x¥p! „@í'ÀŸðGÆ%Ü ÿÖï°­lL\"\Öé¦5g·k‡±<mòÿjÿ‘H …€% ‚r`yJiB ÞPȆÉ=\;kÈ=X½áXW„' ìóÖîè¹µ®ê¹˜3ú@+¿¥`*ëºr$ÒN! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „@ý'€K%! ê<~–y>Ï<·ëüÁÕ‘Ð=ÚÉÛž¿=vɦB î8]_&òR­Ú5êù”bÕXJîÊ0ïaÙÍÅŒ3Ÿkæºr5H.˜ÏóyÀkÍc1ãý)SÒ ! jœÀéò"1^ ޵÷WTe(ôNí15kKE§È?u°éY¤é /ÚíÌÏÿåý×&­Äa˜/Ey!ÖÁsZ‡šÌ÷2 Æ–ÂÅÊë<0²odtÃËU‹¥­bQ[¨Šx §˜NŽÎÏÏ]Óöäegÿòé´WW îÂ…ŸüŒ „€¨ê» ¬ÄÄØCÝ”G­6Ëš¦·PE Ñ¢£Â”›_¸ü$çtjÇ3rôÌœjª‰R¯B ÂêóËDý×3Ï þ[ÓZõêÜFг“rVçÖ\a@’°t9yù´v[*-_¿C_·m”õÊŒãÇFòÖË?#—‹¼KÇ'{*N€ŸS,$³ö8Kð÷Ç\Ù¼uÛW šl.÷6ˆÔÂàëù™q|ÔÇo¼4Íua‘Ît-í 3…dÖ"s7쎇ŸÝh‚ÜÛuçZð|>8y±ïMIz ­çδËuç4JK…ÀiI€íüêS0^ªŽ·?©ª–—úwïH£‡ Q›5ŠªOÇX+¥qƒ:¯wõÐÑLJ?ž}y÷¾ýóÖ.]ĶËb³\+ÏXi?£XHÁ1lÔcD5l+÷v9FC=Ÿe é=èüìU‹þXŽò|¨[§RZ+N;õMP¶Ü÷Ô„«¬¶ wøEúÐÍ—(‹˜!Ÿª«šY÷íÞAI;|B?”‘{I»Î]×nZµrê7_†¼– *J€oÞ"!ùª;Yëv/ʽ]Q|µ+çóÂòeÝÎîû׺e‹w •æó¡v5XZ#„€ú$(«W_}wDãv­~nݼa8k’EH®™k¼Wç¶Êê-{tÝtѡݩŸ8q„M0ÌO¬",×Ìi©kµò×!¶I6Ì-š5kÓtàåW¾{;ìáÛ¯{»®MöšÏ§F—R¶þßvñóAž ŒdSÚC ¾¨[ù¥jiֳ㣚®·d›ä [}êÔž ¦"-aöw^5HÅ›¯ù k®<¡XxŸ>W„@yÌÁ{,(‡_pÝ ’¢4½cè UîíòÐÕîýæóAÓ©E‹ÏÖr‡¨¾¼‹j7|i~¨/'ã¥ò(€—{~_ÏÀç€ÏEƒÆþ…Âñ°À#/Ä€“®—_w® ÛäÆ-ZÜ-÷vý9׿ó!4<ìáÂó,èúszåH„@½"Peã¥zÛðG§osvW¯ÎP>˜‚s¡4¹èªà0Xà­r>Ÿ§°é¦ ÌöÉ¡ç^6´/|ñ6–{ûžSPŸOö}Ó}#ú£:”Os©Bÿ ÔA9,2ò žp€ý$K¨ø\ð9iÚºÍ`´ˆÍ/Xð‘" H(“?—ø:14Ê-Ûu¸@îí2yÕÉæó!2:zHáù®ï£:y.¤ÑB@”N ><˜ ^ªªÚžgÜ“ÉDJ?Ù§zŸ‹ÈÐ`Ý̽Or}¸îN5ÎÓ©>¾>ØL‡å`[HH+¾ŽäÞ®_—€ñ|À3®‰Úžoy.Ô¯S,G#êúð`2^ª˜®%¦¥®ñãÉÍãÜU.—›`JRõ‚j¸„èáŠÅfm†f°2 >b§\Ã礖WÏf¦é…!,Û‚lMø:ªåí.j^n~Åž|ó}~:~f«•gƒ2¿4Õ™ó|:Ÿ79v!p:àQ]E/ULŸb³ü!ûóWÒO‹×Œx´v£¨ºbPOºàœ®EÜÒ1ÉÆçs—Ò–Ýi”ïpQdxuÅ`¶á76ÒÄNý’ŽÏ„QƒˆpêÝ¥ Á3•æ¾nÕæÝ4ý‹4¸ß™tÇUƒŠê©®M»P‡–M($˜-#øœXTKJ5…dÏbÝï —”V@€;¼|ðbåë§:î킪üÿÏ÷ó€è¦KûeÖàÂáó_–ÑâµÛ(/ßIM¢#é‚>]éêóÏ*Jã½ñÎ7ðôï4æž«¨kûê9ÔåvÓÆè¬Îm¼›Q£¿ù¼ªŠFð¹®qEGÂÊ…€¨•꺠ÌP ^ªzõ¹ƒÖƒ½ý ÊÌÉ£…o¡Ï~^F}ºµ§°Ð`:‘•CÉï|KMFÒÈ[/3Ö{ÒŽ¿˜<Ãgw¡‹úv£­{ÒWó–S·-©_÷žIж—­ÛAmš7¢wÒ0Ôªpù¿¨.nçô/æÓ¸û¯£Á Šâ»¡óðµf ?òB ,àúT_+æb\/P¼Vß  r|òsbÔm—Së¦Ñô窭ôͯÑÀž‰g¥óÜ¡^½e·qŸóý^Ý‚òúíûéë_WÖ:A™¹@³nžos-hï F~ !Pc꺠l>X1zÚxØV H«j¡ÖÍe‡Yiù†´ïÐ1Cküýï« R ´ÇML•ÍÚ$ïÀZ歚˚-{iÉšm>eþl»fÛ>yËe4ã«´~GjÑËm/ð9‹ÖІû)ÈfeUµmÑú¥„ÙðèÃÑÞƒGˆ§ì¾óªó¨S릆ùF4`·H³­¦cÙÔïÌtã¥}É¢ªôæ§sö¿úñOdÅï¤Gnõnz• :¦\tÎP°¼«L·^àyÔ™NÕÐóNj{vjM_/XiÜ[¾ÎЪ-{pÛèš Ï¦XÄ~Ç‹¾0ú>ßè8}<{1e¡£ûÖÔóŒÖt×ÕçùjVMÆ™ç¼&Û u ! J¨3/¡-?QíØ|§‹Øbáª-Я¤ö0S8£5›ÝíÜØ¢M!ùd³|o±w?„ìà ßfoÚMAV+õèÔÊYÛd†o byÒ£·û9~øÍ—»?øa!…†Øèñ»†B£Õ>™³ØÌFGNdÑG³Ò¹½Î /éK@+¾Â: Ê ñâð|F¾ÿ† ‹òTÃ_kæ¹âµ!P:y­ìL=D¿ÿµ™>ž³÷V§¢¶÷ò}}v—¶ÔëŒ6øú䢵è›!Ð÷yãáÔ¹m3h¼Ã{üÒÝͪjÓZž µélH[„€("Pe>˜j}Ȳ&æ“9Kil·ïK§ a{hšC°†öL˜Qp`!øé×>+ZŽgäñüoÍÖ}4íóy4öõYät¹ ;ç¢Æ ´k[£üsÎlv9 ¨sؼû ñ‰–m‰»´kN98ÈÂnvn>íÀ úÂsºQdX0 êÝ™X+ÅvÑf8¦"—ìa´½]ËÆ´šj¶‘n l£\͵ÔIÁÇä'ëš$P}_‹}TAsËÚÛÌì|tV'ŸÅgfçÁ^8•øþæq=:µ¡eë·¥ ô}N !$s]|·l]T—l! „@ÙêºéEÙG ½lc˜øð-†kbÙ¦7®ÏúvoOÑ‘a† ÊU±Ðzóeý 3¨yXèØnˆÖ}×]ˆcK²AkìNdæ@>@DÒ”ÿýHÔ,$³6{ ´ÁœoÁŠì›ØÐZ±@Ìaó®4£mÿûîÏ¢"ÙõÒ¾ôã(«À>’µàf€=b-ù© uGÐ9uL¤¦RTk§·”:÷Ð ”ƒŽ+›g±YÓãw]IÝ;¶*V>=àÁl¶5gáÃ*qÈàº{Ÿ;ÌÊþ¨Ó翲-ù„€¨½JJkµ·­¾ZvJª, ò¨qÖÌìLM7eÖÈ®‡Í°Ãé6âσðºÞ/LOf£ù3«ç(y3ÞsÍ/W6­à@fpÂ}k™YP¾êüÞ4 B:{׸bP/´¥­‘,$¸à4>—2k‹=ƒébŽË­eᔞ»ZvìÒœ² ðµá¹”º–íåA¾õéF_Î[A›vî/!(óýÌãLšïQ~^üµiA¿õì>¯ÀÙ1:Òæù®@zI"„€8ujôT‰C/|ÀVŸÖÒ¥¹ S†\‡ƒV¬ßiÅíð¢ãÀ/µ¥xñ±YÅ —ô1´Æi‡Wâ0Ÿ_wÀu\;ºqpߢüüÉôÓŸ–qæ/ßHÛ43^ÂÁAh¥y|aÐ^3Š€–øÇ…«é¶!àž.Œ¶ìIƒýcù³òÀ?îpú( 8 2~5 pæ‹Ð\®d)IœbÇñõggêá¢ZD„Ð6x´ao6ü!iáê­Æ× ö^ãË@¾CÆ\6‡2uXŽg ÊÕqŸ·hÜ_¡6Úkî4‡C˜— „€å¨‚rùGYÅlkœôßoáeÂbØóõ=:¥¶hÒ~P‡ÂeÜRš s Ö±àÙÞ(xP^ECÚ‘´çÀº£à=C_x¨`A™µÍìyƒGÒ³ù‡[Ó á˜=^tnÛœFÜ2˜>ÀèyöñÊàž#oö,Êç6¿0Ù_ó,¸¼ãz^~òNCXö™X"…€0,Y»x1Ø{¯2Ì¢>øa±1V¡#:Òÿ¸¬¿1 ÏLÃëe†y /âó <¨ö#x­aw“ÕqŸŸqì²nü›ŸÃëEzôŽ!žÕ˶B@”B€µ{u9°J5KÔOÇ}Ó£sûþüª©Àƒùróœð¯dØ+²<¨ »ÿZÃî8ßᤔ/Å&¡tßõ'½U˜³‚…óüÃ|¤â¹ÊOùÊshõ†­?|}òãH†å– ,¹XŠ;›F„„Óž@Ñ= lGÔâžÇǾ~v.ÝkòÞ®èYáŽ2›aÃdeBußçl?]_ü>d~>lض{Å»SoDfy.øMP2!PÝê‹×‹êæT¡òYãÌþ’yP_u ZäcøäËÞ,6Âöq>×zÚeÙ_!™ÛÊŸcÙöZ‚•'À_“*+$›µVç}ÎöÓÜF B@!P1•S{T¬lI@gwmGiG2è‡?VÃ/r&¼m„ãÓn?Ø+ŸœJ;€ÕIQB@Ô¹ÏkºT)„€(ƒ€ÊeÀ©m»†ž×‹x‘ „@ý% ÷yý=·rdB@Ô=Õc#P÷8H‹…€B@! „@1"(Ã!?„€B@! „@”åJB@! „€>ˆ ìŠD ! „€B@”åB@! „€>ˆ ìŠD ! „€B@”åB@! „€>ˆ ìŠD ! „€C&]@IDATB@™pD®! ê5]×ioÚQÚv„ö8LyùN:‘•ëó˜öêDzÍvùÇß[hùº’ÍgßÁ£dµu{pœý UU]ªB¦$=;Ãöˆø¤]×îòŒ3·¡éùhzbüLó7¯%ýI§ôëAׇª*¦$ľ’˜l ’DP.ÉDb„€¨ÜšF‹×l£9 ×Òáã™ÆE„…P§ÖMý::ůÔD’¾l`~óÑ5tu*I_&,áS€GQ(œt­A™°d§ŸYµ š ÅõÀÓqßôèܾÿ˜{¯ªmm<­ÛóÊshõ†­?|}ò㑆å– ,¬Òsc‘ < ÝÓˆlŒ¥Å=}ýì]ºûsoÏÈ¡ÿ÷ËÈ6ãËö NmšQ£¨pϺd»† ðóaöÝ+Þ’x#š"Ï…>R=4Ðq ¯A0ú_*æ !ÀÄFY®! ê«U¥³:·¡'ïJcï¿–ú÷è(Br½;Ër@B °`¦¥àëÅ MWæÄÄ&ÞØÒ¥´ºJ@åºzæ¤ÝB@”J€M,îºú<êÖ¡e©id‡BÀ“€¢(ºÕÚh¾µÏÓIoDlâÏý²}zAùô<ïrÔB@! „€·ígµ²^p=l˜?…~ùÅ‘öÄ^IäçiF@åÓì„Ëá úHÀíÖhãÎýõñÐ䘄€8ÅìöK]Ñ‘A1¤+û4—þþ“¯¼Âc¡$œ¦DP>MO¼¶¨O¬ØH¯ü3åäå×§Ã’cB †L~æ™L]Uï‡ÙrçÜc¹××P3¤ÚZ@@ÜÃy„Ýð³º~{*=‘Mdzr(ßáôJQš.P‚‚lFDPÏN­©]Kv> AÔ^ Wo¥Îm›SXHp¥)÷>‘Üû•¾|$c=$03aÂo£â^ìéníÛ‘ú)>æQñ“ú¹5÷PTÛf!­ðÎŽ<ÅM¨ñê0Ø2SÑ•ý¤ë{,ëœi ãWVw£N{A™'#p:]4ùFZ°|=ÈÎ'ØóSˆ’KÁÊ ²Òé§¡rQ0m×PžJßüú5ˆ¦ËžE— èŽÉØ{—!P{ìL=L§û®¿Ð¯FyÞûó–o¢ÌìÃa¦K ¡<5¾ O¿kÝ‚ç]ˆv˜¬zžqïGF„Ñp­'÷¾_—VHÌFÄ'vÂD/5Ú§p;D5Å@¶xF‘¢Gà†ÀM XtTEÕx]²[Ϥá›å°ªë験޼{ ÀlÒ»Û®aÆÕ+·ŸÓŸÙ\“'Ôn7$Õ™ú&GãÒ\-yÀadXˆ¦„Øl§U@žÓ©Áý§ž™“§‚G"&:àr¹_‹Ê‹xóÕWÇäá\áR l8me~Ij˜€µÇÏ^íq>5·n£Î¡«±ÞLA ó>½ƒC¡ƒ®n´/ïlúj~>:ëèžk/¤žg´9½ÁÈÑ×*Û÷¥í9§k» µËóÞÿpöÊÀ—£c¶æt0¬;ÁÚ¥Ø*TN}NdÕÔØyšçïý¿‚æA‘ðÏkÏ“{¿ŸôY³fY~Y½å\E¥k!ø^<<.©4’álß_Ý­ZÝN\ÿn=HqY¬*ܤu‹ÙX‡âÈ­)š[·*Nݦ9°Ïe!HÙf˜»z³+&.q­NÊŸŠ®ÿüãKO?mî—µbâ®Lsï²_ëZëzv‚ÛËÖ ¾œ~½ø“èŒÎ›Ù­Ý–JË×ïh±nÛ¾s#r¿ÿé¸áïMIü Iù¢>yažÌ[©­ÓRPf™ó–­§o~û›¢,étaØwÔĺ»Rëk&î,´µ­6–îö´6çzë³¹tËåhȹ=ëëaËqÕ1GOd&¡!Aå¶¼ø½¿Š²-‘´%â:amRnÞÓ)wµ1–®ÃÔ5gîýy¸÷û˽_Ç.„'ì¯Fg»rFý¼zËãv›kš¢gYêÖh5[¤Üùj(–(’Õòd‚bLòH°–G¡Z6¾DäP˜;Ó©8'Ây¼·…\ž8‘Ÿaï+›ÕòüTû„-e¡c ÷“Ï¿ÖÀåÊjè°ª ·¬’â ‹ætcm%ëñ©Ï=}5ªe•S_öÛ“@2¥e³tû•çR—v-ŒNK}9¾ª›ÙÛ«/ÊÖ=iôéOK›§¦kßýkܳãÞyaâk(Ÿ¿l˜½À*UWÞMQ¥Âk[fS“5½!$ûûjjm]O}B¿"ôk[skU{¸q‘e:ý{3}1—›¦Ã£'ÌTäÞ­U'ê4lÌŒ'hÔ ì÷|Ýû‡m­icXÒøë²„R p'beÄEÔ=çoÜûË‘NîýRaÕ²1Ï&]šåÊùöœÍŽ5§4[;:fm Ô7k˜)…‹Ç¡C”Õ-Qî£ÔÄ•ÒÒ±{˜îtÝŽïžLIŒ“Ó=lOîêpëb:íð,ÑSS”^0i…½‚¸S3Ô†”S(ê8!÷ŒˆKt=—”Z-eÿ•8”•A¢Ö¾ùØcÕf'É.âÜn‹>3a<ßÕÉO€Å+ý»wÐÿyý…JMžQeAG'‚ž¹ÿ:Ëûßý©¯Ø¸sòýcãÔ÷&'²°ìÂReíòi#(›/J¶G^»m/}÷G< ì³²øûµ/4ª-µ>ó#OÖÑ-txïBråŸð«ŒÒ7i{µìv­ûïÒ’T{¼iY$Þ‹w&˜WO|DÿrÞJ¹÷½øT÷ÏGí/¶qh®î°lY‹ÞzÕh¨V­J-¼æ6@ú=ŽýG°>‚ë>_P’rÔ–Ma}•,Ké÷H ÛÏ'¬egÈ™ÖÎ9k ó˜íîÅç9œîV\§¦¨¾ê(Ðj+¹¸]J±é¯amŒRÙ@‹UwP°žg ÖrÛ„h¹­"ÜçZtÇpXNSÞÁ L>æë }§X¬ßϰÛÈcÒœ”¬ JÈr}”¥<8Î>”5É,$?tó%éÐø¨§ÞEqgÂàõ±°œ|ç#ÿÞüÉ[/ÏÁ²´JÂòi#(ó'W’³²sé‹_–Q”zÐÐ$òj ƒ |ÆÀÇiç_3¨u÷atöÐ7iѧ×Ræ‘MU®&ëØvJßùs•Ë D¬ÏÊiJýð'ÙGÞBÁÁåòD½R†ðEàf˜”<ïýY¿¬4Ì-X“\ÙðïóCé¡>!FöŒ|6vÓ¤?sh3ÖU í¨ eß–#U/ËlËmmôò•aôÝ%ýÁc±*˜[XV&}ðÃbš8ò&¹÷+²ÌœÄ%ttzFÒœ¯ëêEyNGó“tÝ©iNKL!iê*dfØCkÕœ: ”VÝͬÕF¨{uÄy§Ryo0'ë­Ü–†A±ÛÂ΢¦Î474À7³6±µ6¥ãXr-áÅL9ü¨ó©lîá>AQî#AMiWÀä*]sN—¸ œ7[Yº|a·søQ®ï¤˜°Ï÷ŽÀÆÆÄØCU›í¿-›6 Ö$¶ôÓ£4æ¶ÿð1Ÿ¦õêuî9ëÖ-em% Ë•~¨ž’“_Ó§‡_”ln‘ïpЯð·z"Ç›äï«ÅÜBw;iË¢IÆ!÷½î]jÓóNÚøûsd±…ÚæF­Ï§´m?ÐÁí³ñ5 ãêK‰ïyé$Ú½ú]êØw8[JÊÑ- ßOÔ¸ÍùѸY,!Ô°õ¹ttßbÚ½æ=ÒÜùÔ°Õ@j­vhTh³3É öúùÏô°fù¬àïèϬ o!CÏ?‹0r: uHaB ¼ïýÌœ\Ø$÷­²¹ÅÁ,Fý˜IgD[hÔ€z|`(þ1«ÊMrö× (WM 52¼oè/®À˜u²fyKèY–ʽoBÐzì‹/FËrÞ ùöÞ|]ÄJ°<5Ü}ÂÖÐ’amDÙ*<Á–Ø¡³-±¯oñ'+œn9 X,µÁ´ˆ…åÅQC-ë¼O¶²ÊäL³Ã¶–´#¤—%ÔA¨¨•s÷€PwÖGû´-oÄÄ%½Þ(ô•WÇŒ ÌMUåV—Z€ên¤<ªkZ+¶Is‹R9•¹ƒ¹ÝyÕ õåæ´è}Ù%CP~øÈK¡!O™E”ØYï¥þìÊ÷’ssóhɺí†w‹ê¸g Š ð†(óðFzï+^§¨f½iÏÚÿÑý¡¯-3¾i‡Ë©ïuÿ¡¼ì4:v`ßvÄæx»ûÅv ŽhA©?3„éf¯ EµQŸ«Sè ñíK_C×Ю¿gyýù±—+Ö|™³!P›xßû‹Öí2¼[bàž#ý7rÓ÷[ôÑÚ|ÜÑFáA@§†zï¦HZM?ÜE×t>ùÅeÊ•á4á°"L×w ¦Ùw70~êB×v ¢žLsÿÙ€w(ð¾qÓ™AôÓ=Q´|x4½:4‚„<¶/CS¯‰ ‹ÛÛŒýã<Ê5+èÜØB÷“Eû3+õ~0‹)¶f~ì%dÞŠMrï#S¹¾ñF0„¹'e8wÃÄT °¸#´'-‰º’–F ÙD?ÚÔf ŒAw¦o™µáR„Ö¹ÊÂ2ëðs'L.*$ûªjÚÒ™–E^nY~> jÑH×µÄÌ£¹Û†Ç'Ýá+O-‰ã‡‡Åj³<Ñ«smn%TžócŽá‘‘#Q ϪÈaî\<¤±áO¨÷‚rFÉeh“wì=HYy.jOÕTk]|ïï4øe”}t+íßò5ƇPÓö—P~N:E5íá÷ µèr}©ñfÛlù–¶.žR$l›ñ¼v;óhÓ¡žM‡÷ýIZ¢ °ÆÆ’qx½aîÁf—3Ó3[@·™cF¶ƒv¥2\í´p)LT‘€÷½Ïî„ÚëÚ0²ìym¬”š¡Q¶î³ðDM¹.‚"!4?;?‡V¥¹èå¡áÔ³YÁÇ»&¡*5 ;ù¬ŽÀã»udÁﻜt"W§_v8hÜÜlZ}ÐEÍðezÒåáôùz=uׯ*ÝÞ£@ð…‰Æù0«°£9Ûô„vïðÌ/Ù´.Ý…v¬Ó;Me~3G6c“{¿2ôNæŸ|A^zæ6s¯µ5‹þ+r0­ˆ¬î îlÅ'SÊ–¿ŽÛšÒº°Ê* DͶDµÀKêØ1¿ÇË:éÕ{{f€¦é-à.°7ë)h|m¬¢cÓ!ÿ¸ý\´Ï9?„+%óÖkÓ‹"’ÓIyxInÞf `?ÉÕ4·ƒþž=‚²ŽnƒöO!Ðü²°¬k<£,öþFùÙ‡)($Úg¼Ù¶ŒCkÍÍ뜻 Ó Þ¡¹ò MÆÔ(YiÐX@Þ©|3÷ж¥¯"î`‰¼Š`Žì©gýŽ}Ô¾UÃüBöŠ®”SQ›w0’vëв(‹¯{Ÿu ì'9¡E„jh‹Û¦ø„ÛÇg¸Ší×ÒJm¢TºÿëLZšê¢·9hH§ ú´Âë!°–6ÁÆÙMõþLVì/H{[ÈÒè'Â~Ûã¤+ΰь¿òŒß!x‚¿¼(×§l$ðø‡B¿ª¶ipDqrïWž#ü?ŽÎÜK0P6…÷áo;A•oV½ËÉšø•—¨íó¶P‡¼MÿDǤÇSS¦\Z‹ü;óy·Ø‚ƒ†âý©³Ÿäzwjà€Àò‰¢7jÖürTϳ÷™vÊ~ì; e7ñ9 ³‹c™9èVäTïd"kš[˜×FØãi+‰í—w­z׈ kDŽœ#>ãÍ|e¯}›:D7ïC~£ô] ºì2ª¶—ý,ó †GŽgá¬Û˜µOåª1•ÜþøñÏ‚/D%åâ÷>ϸ¨ÉD21ˆïýÕùtmW&&Sh´¿šŽMÚt䤩ökÁÚW°”£é=¿m5 Sé‹a'gªõ6súmwí«üêŠcŽÌSîýÊ›§éZBzPk}sè9ì®­rI® ØÒ•àùC陽¬ÿ‰Œ|~«pf]©>íZ9›tà÷NóÉD*|JÊKÈ~–#Cƒá/ÜÚiÙü‚µ üÙÖ¾(ìð|?½}¥¬ƒqüé•8‡åü|”‡¢òÈSØL¢A‹¾tÙ¿VÐåÃWS‹3®6QZ|åZ¨À¼ãsõtºrÔf˜,1ì”+WVÅrñ4ßÇ1³sfÞ„À©&ÀæñÞ&ò¾îý¼ŽüφKªÏÖçÓ³ rèìæVCcÌǽçDÁ=Кf3ðöáÜ‚x7æá¶L)Ê YÒ Q>÷?Ç‹–Aÿ-þ ˯ôXî²j.Oó-÷~ùœ¼SŒŒO†«aâÁ ¶´!¬À„d:]··Ò˜óÂè6˜ç´kàk¼ßÉÖüroºvî8íÇ·DÒß#ÒÕ6õ'Sv‹M†’aRtªÃ[ ‚ý7Ü0ë·ŒK¾®¢õÏHŠÁKEÓû™Žô¸[bZê²¥;õKúz+M+ÒfRjú±Êàœ' WLùß4öõÏ*]rtƒpÅb³6Cì¦È´Sö›q½îÆÂÞ‡\…‚r~¾ƒœð|a5:•æ^fÆ#ûÑÏÓºúLs<íoúëû^.4h–M³ŒÒâ}w`±römø”xáà¹Í¿×ÍË+ êInW6ÚÐ 0Ôñœá†×´m?û«ã >`ÿí283o B 6ðuï»V*6삇Š×äÓÓç‡Ñ¼.Ú·n‡²5z°o0½´óc@^3h“ƒý1‡0ǸïœêÜÈBCº¨]½±ÙªÇÜÔ»¹¥`` n§ùÈwk`ºü>ßà 0›BìBnå²Í8Ìòªs rïû øáääÆùÙú;™ÖFú–PžA:p=œ\ß-ˆfÃ܇¯+Þ‰öœ(½Å‚µùð†®A”û3•èt®…'KÚ‰ë|þÎSÿ%„[܉ÚåmC_%ï^üüþd«jl‹7 oP"Cl¸Ák(üðÇßÔþè[7kXC-8YíŒ{Jùb<TMDežE@Él£Ì[.Pe@0ž,°StÃ5œËðzÁ/O¿ ™hívæø,©´xŸ‰K‰ÌËL…ÿæ[©% GCH¾6/z¡”Ô‹69›Ì͇oàj’„@Å ˜×!åð¼÷+^‚)ß^‘Kì™â‰A!4ñ·Šù>‹^Nsî;/ȳ¼^¡PðýV'ßÎJßÞEGs4út]>ÅôãgxAødƒž½8”VÀÃE,~µ1Ÿ^[’KžJìÕ‚û¢ï¬Ê«‚2·XîýÂWÁ•3W{ IÃ6‡£^ *˜¯"Ɇt²Ñ»¸6fáK‡gàÎÕ ¶¶ÑÏÛÆâöÐi he£δròåa4ö—â飼΢+;ìgá–²4w)´Ñìµåï4'}½ÉIùÝÓ;„¶qÑyÐG‡`Šˆ£<8uɾ‚ŽÝƒBáwÜE'òtêÛÒBsw@jAaì¹å´1_j¦¯È§}ncì=ƒè(Ò~Á¬{ˆÀ>§3­ Ô†Îü³Q^Ë`”¼`Ò˜À "X½e/ý¶rõîÒ†~^¼ŽB0ÏÁàþgÒÅ}»M]»uý¼díK?Jm›7¢«/èM;!˜.ß°“‚ƒl´xõ6ºcè : ùE9ó—m Y¹Ô­} #mÇÖM©¼:0¡ fñ\F«¶ì1:czt¤[‡ €²ÒE³~^FoÞm˜j^6 ]9¨W „¬Ù2¨§ñ|™»t}‰ýþD@d¾¦lz¾à8» ì«&®—]vï5?¿¼0q“yŸ­ñ¶¶~GyY(&ìYcù7÷ ÏÕÌ—¥˜^T7i)¿¢ªóÞçÁs¼˜í•Ïûïqó'ñ ¼?Í0´Â9ÐÒy>Yøçÿeša 8¼µüdYs!TÌÃp'ò e ¤¬Ì3–Fð–‘ ÈY(å°ÀâËÓ…‘Éëßͳ2¼bóSî}ÿ8Â:mø![K%ÇPrù—·¼Ô,>6¿0“ÑÁr\>è´…Ñ \£Ãá„‹B!¸ýèá!…M{ØdÓ Ð§k‹{Ná¯!“1YÍÈï³)Òwü8Ü‹/×v¦)‹sèö^ÁÄ.í¿æÐYø2nçïrÐlt /lg£»Ï 6e¾~Yë}Óg†¹ÒùmØì#—îíLw!ÍÔeyÆ=s fJÝšXè?×GÐ3ðþÒ__R°}ÍGÅMŽŒ†Tâæe¡pw&&?¡]•È^YXgÁƒ‚…·*ÆdmØ‘J9y§™ˆ?ûy) ìÙÉÐÐ~ø~!õéÖŽš6,>käE} ¾ÌÏ^TºCƒŠƒ2: Ü’yñ[_êw†Š7®fSšZ%–Ù²í’ç «f[W}µ³ÏåÏ [¾9%B²q$€ÌœMæÕwtR²(Ÿ€yÖô½ÏîâJ{æ˜B²¯£á<¦ì¹ÿhŽ^$${Æ×è¶ÜûÆÿˆýÅV˜™®q¦v®Ž0mEž1óâèþ¡†on6Ù+ÊEVáÚéÞT¥t˜] í°g`Míxn9˜í¦¿àΰ\^ Ï*¼°æ——ûΡ£H³po¹k¯÷e¹©GS ¾Œè4Zgs\êŽãnŠÃ×Nû›òéh‹#à.ñjÖìùe«×¬“ì†}‘»ÅAŸà Ûÿsùìr±C´Jl×ßæF|<íó7S°–c±(ú䊖7Òž8pxü¤MïG:ŽÏ­€ÊfÝ7îC}»·§/ékÈ@ìõK-AVW`ûd6óy ¾»Ç £gÍ2¦DwŽaáõ0Û²B?˜Dœ 3 ?Ã-áÕešà™Ðê΂)Ðä?s‰Ý#¦e¨—S3Ý4 æE¦ä±>ý¤‰Ož ¾×t±ÑW›Šk¬¹.‹…mÏÐÞcØlƒ7}òÂÊ‚V¼*#ø¨sÎ:j娉¶*ÿ™ž7¿¢åÁÓk2 I8ùŠæñ#)¼ù‘¥ü¤í[tÈ" L»ò¡iæ0â–Ké£Ù‹hâ̯i@Nt÷5ç&¾Jä‚+7bÈO‹Š5?OOž_uð,ÈY9ùÔ®E£bEºQÆ–=i´}_:­Ø°«hßÁ#Õ󵫨‚‚ V ›œÍKÕ+Ié?뵠̇]ôÂ!¹ô« {¤ˆRÄÿ³w€QÔÙûÍ–$›MO „Ð{ïMPAä°£bûÛ•b—³Ñ¼Uövž8{áE, b¡7é„^žìn²;3ÿïÍf6›d“l’ÝÈ<˜LûµùvÊ7o^©cð Ñ›h×¾7Tü¿M»ö}ÃTÖ%‡D!"l€K+u}k ŠRC`ü7âoëÛÞŠ”å°çTë[¡%w¡/ÎÅ3á·&?C9žXXl‚ó[°“gó 6“`s‰Å0«`“ˆù{¬dÿjÄĶ„?)uÕ??€sÆÉNÈ9ÁÃäCÝ¿fÿÀ~N¸†æ‘à ‰0Û¸ÜHK0g-8·ÏN²5&ÈñE'àÀ—"š$+Gs|qöŒ©/ Š…ïÍ r ¹RµÉ[Uƒ¯ÈŽ Sî»dõ}¹xE¯¥./=zò,-éÃ㷤έ›Ò²{èØ»ßˆ0o} z%ýö™¬ÒX¯Ó)Zëa};¡¿¾U ßßûkL’y ¬é…¿Qöµ=SDsjÕë>j7p²ñ #¾´W»¯íx– £7ÎW"fxn×–5444Î>˜<±·„=MÇkÆøª8ÌÇ†ÐÆ"iÕ½Qd¹eÇ>–W å†+ï¤u÷GÒH˜:ø*‰HžóÝÍáô×Ý‘t_oØCsÌÂöÎl®±é××Þ…¨/kÉIq@CmP"¸°=Yá¨1…`Ù+¥p€M ×Á¦Ùís!}5&œV ïy]W&.fÑÀÜ?ÄŽÖ­’œ…×ÌIšnáDeÇÑÖÙÉŽöò*·GûÄ6ÈÁpöciIGÒÎ ”®Ñl”™ãrêŒF™Œì+³CG¬`G;6ì×Ô@¬á;)Ñò£EpÂ+ÿtáˆ]ëiB½-A4‚Šdl×`:œ%ÑÆ´Òá´Ênçè—µ3ìF7ŸtÐÚãÐ$z¡d¬%¼X¿@»—–ç¥@E©b{ p¯¢Ëón· fšÅܯøóZPk¿Žìwy¤Ç¹Ç*S‡Ççñí§DzéÏùüOrïöY‰*§:÷&ÉpLúiN)çS.ÇZÞç—t²¢UæíÞÚáh=>,é‹Ë}·§P™xù(4Õ“~ƒ³ ´ìN¸Ø©cwƒx f'Öò××­Jô2âû+æ©\RŽÃê‚qS“~LïÏž9meUõë`?†Rw"J"-^µ>ùq%ìôÔ&±1 +ކ1šÞyKÖÓ¯Ew]=Dqþc»äf/¤Ð ºnXúú×u> vìÈôÑ+诗*åuoK=;´P"_|±h ½í|¿0¡Ý 7]^ΙϧNjW¨Z¸W«píÆå{íůêfD£ãû«o™æñ²B…0¡²á¥:/FYyˆŸqÁ“.6\¦´V·A]‡Î¢eŸ”·ùhÜZt¿“Šl™tb÷<âÔÁæÆÔ²×ýÛ)¯“‘µï¿l+Rn›Œ“»ËÐ$ÚùÇ“ Y K fÇRht%¦ræ ×ÉÛõ²—è"_4ëv;²òÒñ]_"óßVÿ\­¬.¸‚£{Ðýסˆð02™B`ËTâtñæKhûžý{¿|çÕÇÑÄ)L˜ø› ³~Åè sMÎCfÍÆu*à:•k~â°ùÚw‚SÚq†ÙðüÏÃÙ&ï£üо´-ìâjw9®o G(¬åGœ‰e CˆÌ„ G=}yC8ý°×Aƒšéi'HôSK àõo¢¶H$¤úJ8D¥"%õ“¿å—ê÷^8G=„ßï…g:’C¼ºÆV.¬„×G˜jË@s2KŽQÑö÷G‡!¬–§+‰nž4׬ùóŽßüÎ(3œ¤ôH³_Ž|{–­j¹Wþj’Šl´ÅЋtÁfÒ¡UÄçUM*G ³u5v¤R²©7 jQyam¯_0Êbír´ó 5.†:Y·ÀÁÒ»é‰ ‘UÍs¸ºªÍõÞFùªg+5ÄfU 祸÷éi?viײŸúÙ‚“ŒØ`w‘›—O™Y”~ê ål?JTt  ?q‘5ƒvüößñ ¼é{Ä3^Hl*ÁæŠÈHî ræÑ‘­ÿ¥¼3{¨Ý §¨Uïñ´îÛk½nóphD Ô-ñ&uسµL~öAeQrºn¸‚®ÞÿÄêеùyŒÀÔñ•^§Õ92÷5JߨÉÿ=þÌ;=»´ï¬^ÛeãÏk,“î|A™{»öÿÀµŸ_ÚÄW)[?—¶4(¶½wý;.Hl>‹<\ý‹p‰‡\¬–í„Ùd‚ãØ²m¨§ W-ç«%|•T„ë 3!öhγŠÏËÖ« ìž91Dx¨8æî[×Fz5»òeÐüÛí9ptÓ'¯%]‡ò Ê$+ÜÁòz ‡ˆÏ<cMOdÌŠ“xÔÔ^ïofn[“œ‚öšûÑ€œ?Åy~1wæ´{Kö–^âß*@â¾ß ‡•)~"@kÍúŽ@}Õ(û~õ¨dxl' WîuÐ**öĶüT:•ò3‡ÅÓI8ùÙö14¾¿-ï…F¶¤Ì´¿iÃwc M¶SD£Î^·ybÖÉÍŠF::¡‚#¨q›+éô_=Ѝº/MÚ¢†€†@­¯ý¤ËÌ4æ 3]/½2E™\·Pvj2ÃIŽ#°p6Çø×Ššvi¨ÞJk¨9¡H4²ï±ðœ×kK’¹­0ç¸ f"«Ž:”~y›&õ9–§Î"LÙ˰¨ؾ©Zè¾´öÏ_4u£»ˆF]©ÃEÏQPh L(ŽQʺ—©¨à ¼g(¦ƒnü¾.A î¤Ÿ¡6ý¦ø6£!#—òÎ&ÓéCP×Ë_*·ÍÑÌ=ÊüÌÚ³b:õ¹ê#˜qHtêàb:‘ü{¿¶ ! !à_øý&Òø> ³…ÃY¢[Ö†ìaœ¼ãÊ~‹lb£ö,âÖ2‰æ b)ˆÀÒ9ΠijÍFv³ø0ZEêiCªuts× Ú—á¤±]8ů«|w„ñbûáCè§:ÂêÖH#ÌYÏ‚¡Õn§=µš¶Yþµ²5C`Üô—»Ãï}Y”..BÅc¦v”nl®¡í¬©0Q~Ž¢l†3é¢ù,Ž¡0!1Hå¦o_¦8(~¬é¼ÕckŸªL~Ê:ª–ôâ­]¿l+Î&â—¶´F.8j~5^pPÔþ€R÷}O<é¦Ò¡Ü@h¬C™X ¬šNìúóÚ½l Ú‘ñFtÙzÛÆ2–¼[â}lÇçÄÇhVëñè—2À}öüS¥ê¸wh ÕBà©‹Lª£¯CÈœbÿK>Òó¡…~»3B 5ùÏ…”܃ˆ¦Íˆª‘Î7i•U w]‡º.®ü2Wq¨ûàª0Z˜·GPæ¡E®¨àœD¨¹ç—UÏÁ‹‰ò+WÀéYÎ8áÄöt‘Þ€ÓKMÛ,>Tm ÆM›ù¤,9^sê‚äC¦.tÊØB/ûÉ»‰3âñŽ{z…(N¥e_ÚnéBñf òÊf„(¼Z\u"Ðî3N:€6bðµã“ívØÞë‘éÏDsá Êi¨oÆËŸßU¥+Ìonì3$¢ovÙép±Ý¾eX(qŒäû`«¿výȵâîÿ™Á¡p|µ»¿ÐðNÕQÕ[XCwe?.p2’hñ¬SGòz?6Ц8’ÄÚûá—a¥N­¨S«p—y×ßHÍé©Û4kŒÒ­”qpù]OЉSÔé©;£'ÿðޝ¼bË>ÊË·)i¯[5mä¹Û½Ì‘*VlNFè¸ÖHw]r?ô¶='ßJÛ’Q¾ çH›Dò–A•>•‘ƒä'‡éêKz¹ûQâ6o? ׎:PTD¨{Ÿ·òîZЈr€­,Þ±J’ÕneD§(k,ám›ZÞsîI’=·kËþCà–ïK|ʶú诊2۫„™…í“=·ÏE¸66É`IEüâkÿ—[.>í#h=eB1‰öÜÆËžÛ¹Ÿ1ßæ*¶Ïø„ïŽEËå¼µÙgr_hrÎ7mÆ›ø"øäéàfòþ‚Qü)L›Ø ç­‘aÈrçR¨í³fwá-‘ÐÛÇÔÙW‡Ñè¯si¾‚ð ×/)E´÷¬‰?ôp VˆòØ ª£Ú¢|+¢¶0Iæ¯'Ÿá’3÷Ãühá­tÅç¹Êוa-ƒè¢fZŒìz[°;5 G†ùäºZ€Xʪ“:65#à‡x‰¬ iißOzÉ©G@©+ëo‚%i€(êå¹3&o¬¬\]ì›ýý2 2PÓFÑôéO«éÚ¡½hÈävÓ"ÄHܳ}þ˲ڋè’Þ装+(qqÃÌ&úrÑZ5¤;qv[õKBK)c8b=¾¥ÓY³4AÝ”£§Ôªîù}×]¢,s²O©h;§ÂÞš|”ž8MÐf÷éØÒ³ Jr•£'Ï–Ú~Ùá8¶*¼œž™C½;µðZ^-ÈyÉÈ^ÎQÛl·S2£A4nUœÈáj‡YÏPÏG×¼žö<žŠõy~u2üq–Ù0¶”:œ1&ê`!pIÏ—éÝ¿môhÿ’LkBÓlícrü9buχ Ä>_'L~òAzÁ_Žžr3ìê·À†™¿v°–ùŽ!Š6™ËlÅv6¯`›ãæzê— §ß+IÇΚê;äÁ¡]¦g‡¨ÝÔé<ÖyŠzå­u$¦ ¡4ümË“Ùu:€ò±ÒTüÕŸ–oA½"9¨̵8Ž»M¬ë·c2ít"}w±WÊ‘“”Œ‰É© ÖSX³kô ß\—·ÕV8£ßžƒ© 97›‚È ûf_„M5 ÉPø˜ü0žbŒ•Õ™Ç\rEørçQ¾y—¶_w8Û.gÏkÑ3ƒÄ žbޤ¦o C&S„CJÛê¨eãZ£ÄN7©«ÊœËw¼x*%t¸Ž‚ÅFípäD%í?«´ËN‚e¥E»ˆÛ ¤8)˜‚ð6ªŠ7ÜÕ}Ú\C .ðvòµ¯G´Mü‡€xj×~õðÞ%Fê‚-ÌYwÜŒÉð)fUXSÌ΢Ï1ÑŠ{"éÏ;£¨%4Ã\âKØ¿çj76¯èë2³àúlÊÁë«A Yæ¡üzçE·E*ήlc\V‹¬ôøÃÚìIK­t=2[^ ›çº伕}/uËÿ[Ün2†Ì™:õd]õ_A?îF”E«ÝQÆ6«‚JK×ï"6Qxüö‘ ùeç<6wȳºÌYò‘C‚×uʼn…n…yÃ3÷\E]Û&Òÿ~[_ªUÖöÚ‹ŠHÕ n¸¹äŪTáj¬D†‡Òý7 ¥é^GöB'ý¾~·OµÃÍ!”_|\!ßZHá¡5½È!;N—WséÞݸ—Þì}­Þ|Nð><ÿn ¡Ë”JTúþmÝÕZ|›Ô²û]ȸw­Ÿƒ»‹Þ£g+ËÙ§¶P³®·S²òq:ë@°Ÿ¢ÖH<ÂeR÷}§ìjÛ÷ajŒÌ~gÿA­z=HZ]N;–>Níú=JæØ”{z'µìy/5n=’¶-™èn®I»ÑÔåÒéð¶ÿ*©®Ý;ü¼`—"(´Lw?w¡5§!PkøÚײžÕÆR #phH`ï§¥:¼VÆŽ+Ž›š4¯‘óä=‘ÎL]ŽÁÿ™SןpÒ¨/sÜh±Rñz„‰ó”Ÿà°Ç‡ccâ¬ú²=ñl¤`%—¢‰Ioç÷²ÜU9Ò Oª0Û˜Òýâ ›¢‘ödÃ>ó|¾}Í5O,'rEê7·ô~eGñOGUÏí5]Žtž¥Öb¨”ëmá“àøˆ‡Þ}ì1×`jÚ¨ŸëIñtv‚³ãMª²¦™$¯Ù–BÜ2\!ÂqÂD][7¥Õ[S¨y|¬b¯Ü©UEËœŽ¨ìøçp:éÔÙl˜5D)ÍïŽxÁNÅa&…èf¼£[»fÊvHSl¥½è2•f»´iFßÿµY1ÙñrÃQ<î}QU]zÝŸ[ ; KN`¯¥ªÞxÁeõ³ ÏC ÂÇ­Šä ãHдÓÍthó{Ô¶ÿãH ÒŠ¬ÙGÈ®¤ª^õÅP*È>ìõW‰ŒïAá1éÐÆw)¦ùw™C[gÓÁÍïc]¦Ì“S¿k>£ô8ØøŽ»Œ-÷u¾Äâ^27¢ƒŸCŒåßÜÛ±À82žxÛó¦Å DŸZ›eàOŠ,&ÉSÊ^ûF²)ÉüaÀ³ÏK.6Þÿd¢o‚A »u aßæí*òj—yM‡`„Ù‚ý&>e/©äÓ5'b8œ%QEñeÃìvD$øe¥!²F „“bd$I m¬]ûÕ9ÔhþgžÃ:¨GÁÚN»B貌«Ù‚ÿŠçzR[W».çÔòÛ+뵬ieeër_8l‘[ØSä8Gš º‚^÷ðœ§.ªË1øØº´<[¡`µó ¨w *“ÝnRšœþþwÓÿ~æNºŽviâé·ÿ[åzhìph‰eúä§U”…PrlîÒ&1Žn¸¬ŸMbî‚eô£Ó€nm鮫† :ÆJúµKÛ¦ˆœÑ¡\wg>,dä "Ç*%òk»™´_Ü«=OÏB䌿èåGÇ– ùæÙd<ÿØñð¯S^àF^ÔM!úže|YfO›µ€ ù„V'_ª—*sAe÷ƒo6:üX±aÁt(ÃFéÎŽÔܸ½þX £F-/¥=Ë'Sx\Å„bÿúבP$2ޝ¡žW¾‡$$¯ÐÙc+Êu—“¾¶,z@Ñ({îäPqªÄ4HÙéÛÔUâì|Ñ ýsʺWÝÛ»_þ:b6¿I±-\Æ÷î~^`eY –Mbܶà~îBkNC J¯v]ËžD¹¢k?Ö‘NéAͪl³ºXôâÌÞß+˜¶žÝD¹G=}r}ý°¶ÝÔ EžZê §öq/â.?„øÉß#žmÒxñDÛowª»•¹/‰Ú!ÉÈ;ølÞ*J¯Œ!D™qäÇŽví—ú™|Za›ØÇgμÜj•þêQ°®KzPs:hêJÁ;9ò©Q­ŽÌçg³¢ÃR¤ã Œ®t¹ÈUðVXŒéÕ·&MòöÞ]·ÂYH®p_íw(.ýıÕ±±ïð©vÚëvÅë´ù—{AÄ÷»ëÝññÕ¤7 ºy!ˆø_Ôéâç)3u=õñ6E%ôU2÷åÞå"Îîšµ_`üÒí¨o»ÆnŒoM4êÞ®ý3\xrÓ‰ís q~&g{dq>݇LhžÒé„Ùf”åD.{¦»b×fÙø¾í.s8ǵ~0Ë °IÝåžû’ˆá¤Øf©*M°»Ñ.0~ÑÐØuk×D»ökˆ!W³XƲ±ï [fÍs8åI EGïN(<’gˆ–ÎtA dÕUO¡RÓáå"ŠÁoZ ¤|}yÇðš¶[ÓzˆHA …G‰SyAÓ^¤ãëJFô‰tˆ¯Çþ& É~0…‹Yb¨˜_¬¶Ôå $ÏLJÎy#z´_Æ6á5CÖcm§BäΞL›·KÙì¾5©Ã!\X]1~»œŽØÿŽŒoÀ<)c^m›´ –(«Z%I6PPP‹¡ôü\Úa¿†.5Ï&ƒàòÞxµ’ðF](,¶=-û¸Y3”¶z^ù.´Êc)/k?…˜ãáx¯O ![œEpæ;äSL’{zŸ¶/}Œ ²*öά1ƒ#_ÞÙ}J¬Y.‚ŽÃ–A»aö¡Š)¢9QÖ×¾ÔzUͲQÁ/ö=Ú$(/ Œ³ŠyUõµýD@=½]û'ó ¨½u'm ¿”$¡ä“` ÆbÚ·DAÄi~C %_z8šOøZªï‚æÿ•tØWMÛÕÉ"u°íT2ƒi×~MQ,]ï=Ë”l™0ñ¥—&‹ÒáΜ[ÃÙ½ÛØ÷v½Ù™¥3€Š tä4×lžíu¸˜ Mì)9ÊqвàBÄMÃôLNÏ•`ìxGñEÇ*¾ÁìV8‰KŸÖ…•z#­â6Ï?^y¨Ï­´v½Ù©j8™Ä9Wþ¸`Þ˜ñß6oÉúèçî½FçM#[oF^OÂæÀO’œÎÜ5‹ZŠaò )O*YV1÷ù.X¢ÌphŽ È9êþ`eŒä›Gè`vcÚjCýC¿ñ¬Ê &vKÇV¹I2—MK^’û!´¾Ÿ*^…¸Ús(ýÈ”]ÇÁSˆmŸû_ÿµ»ûÍ?ßMM2®Q‹Ë`æa¤ü¬´gåt%m_‰ló‹©È–I9é;Üuý±À¸åIé’v  G>Æ—qVCÑø£­ Ú Pѵß2Šk?—:[·ÐnsÿÚtáSÝ 8±G™\_Z8[š/—¶âÌ¡²rìE#Û+ý_úüçÕòc†–¼U×ù°ÎÏ·4¤ÛÞ²fùçVk^>Ž‚íáxRÉrƒÔ(+o¢,Ç tTiPþ@IDATŸXêçW&r!ÁÁb !¦Æ&„ÄɧTkW"ë-ÔÛ´ Öšå}«,øJË™#Ëè÷;*W}9Œt°%–œö҅ʬF” žTY7ÿZu±ÔœÛf©¬Í¿O*U§¶+¬If’œêìJ]#©UBŒ‚+ãËÚ;o¦q Õ·8u^Ûáhõ5*D ªkŸ¬iÔµ`#í íPÍòšãº³G0}»«F·¢³V‰fŠJ‚†VH ¼!ÕIkŽ;éæ®AHúऱ]‚aªÁÊ%¢î°{f{ãCY®uoëKoõª»5ÉL’ã©>_ûÕíC+_‚À‡Ó¦¥bmóû¼G¦Íhí…P¢ö •óښļ¶`Æ­aœï»„ J$œÖÉòãÍøn±Y6è6͵ì@ Ù¡3 Ù&2¥½e™”YUO³-SöX,r·4ÇÌCdÛ£­ìɃ[Ù÷é%Á èÂ…|ºC:»BØ ³Ý°šlìû0`Κl— Ázlå$?zh´9æ¶*ò`„4Iù*å;CD+¾±Àš"“p\/È `JñÝܤ)«‹›©73Â,ÖÒC†`Pü,äÆÙd„‰]Áâ¯?›7êÖ;ù‹ÿ]'3²ä[¯¤Ól–FÂ6ÉlnÁšäƒ»wüø×ÂùKP”à ñÍ”çŒ+ãË8W›{\0¦Þbº–:xrB£ “‹0³$¹!KJzÇD¤| ʵÓ)[­¶ÞOñ†Ô aãâ É‹³Œé¼Ž“Ì!à8º;î#bHÄ0)#s˜YÁ“qe|Ù;•ñö5Ž¡ÝZpÆs»¶¬!àOÆ\ѯ\s¾^ûi¶<ê•¿†² ñJظ c|ñçërMú´áãmvâI¶O~ôW8áNëŠOëÚ3w‹¾BV3–TÄ;¾ö¹ {f«Gb®GPÿ<¥l"oeúÌ©u|}%Þ4‡Øâèì¸Çf#«qí{ŽY[sž{.­óä7±XÖ¸ÍçéáY³ba7=JÅáRVïðœPÈEÖ¼3N` WÙ ­v2›{ »ãúw-Ïž¨y›uPSÉ•T¢¬h”q4Lî‚—üï‹oú_6â¤4`ðÄ7¾X…„rÿ®m„îí+Œ³\HÔ›.˜_p8ŽnÁŽ{’Ó‘·iå²yÛ×­âppŒ!‡á9OžåÒ7Uì¬JùãWÕ·?÷˧Ž]ã-!‚åhlŸÌPH²(rt—ö÷‡Ùé´ÕIŽÖ !r=˜¬¢ËÁ{2Ÿ» K8Í·÷BN&Âq’:‰šEè©M£ŠŽ§ˆLáa žŒ+ãëÍìBcx,eIóÉ©N PíhÏ ¾_û𤌶"60Þõ ÿR4flóÙЄ5€QÀÈ&}¸ZõðóH¨ÁµßÐp»P÷½)SØ3ý«âI9̧^{Íœ[àh‹áYÂõ². 2‡ç€]1ž,22ÒÈB¾¬òQΓºÓx ž,&öJíO)˜°2¿=³”s{ë7.û}õž¿7ìü«®A¨³ „‘Œq¸)XŽŠ4 !F¼]70a[Î`ÈÉD8N²(:óŽìÛ³rÝo‹—Ùl*9V_ sãɸ2¾.⇅êÈ…ð$PÈ׊E?ìnß³7Ç ŒA°n÷ÉãÖ,Ðqf&ÉjÌU׃¦vö³S‡“ò‘“<ßOu Ÿ–8óœëËPu0=ïËr”7Ö€EbŠ6é)Úc3òÁ‡ƒ3IŽŠŒT–Ùì‚£‰xÓ&3Åq swmZ«|‚ªDÙu3åBšhê_ûbñµo%§l…i–û6 Ö¿fùÚç|ÁˆÌm2àکѵ_ÿŽL‘¿xýé§™xì+žüÕlCo‡Ÿ‰lÀš9\…°\)¶ /(È•~ÿnÞ·X_Ô©Wß-Úwìf ‹ 2…D Æ`E›… Eœ¢³°Ðf˱åçgMÙ·/eçÖ#8v6«`­1Ÿ›L–™(³É'/³Ù…jŸ\#îq¾eOâ%žIKý1*ƒP%ÄÁrÉfNl%Àvµl6 G?›Í®Ø.‡‚,Çr7hœ¥âÏžœoüB&,-€ñÒëØ¶šx`Ä8 Úx6·`M2fÖλ´ÉÞøJâ&ÿŽfù Nž¿w©‰†@ÀЮýª¡õ÷µ_uZ  /xj•Õ·t~^ªºpß¶ÍÛ1ñKŠ¢qÆœI5O,j×Ú…ùW%cŒOŒ “dÖ3!VM-X“Ì$¹ÖÚd´Ë‚ó_T°Ä ¿ŸÓÄGoñƒ˜LüXøÁàZ/;[;ˆ²N~e'ò©‹"e /kêïrþUÕ¨¸°†˜Í)/5Zc¶ñ 5)“dÞÏ8–•’8†Ž¼5‹þûù“O*YæßL ¿ |ä¤ÒŽg kφµkß ïËþºö½·®mÕÐð~62ñcaò¡®3d)g#âì+L”™Ð°æ™ $ã0Qqa>Á¼BÕ&+ŽXg²ÌÐ<1n¼¿Vœã|'Ê O `ÙÙgó÷lþû-¡ÿ ËÆ TLî”ee^æøÊ¡&“B’G¿¢"ÅÑö@¥ˆò…Ì—‹•ÉîN÷Í™ Ù¬‚ña²ÌZevÜc‚ÌÛy?Ʋ¬¨q 7¯Zþ•Õš¯~öàO*YV·²Uµu j#°xõv¥NEDY»ö+†T½|UŒj{íWÜ“¶GCànÏÏ\N“ê!À˜3¶n|±ÌZã€üF»šhxE UÓ8¯Ûµþ@@rÐ,A¡4Üíimœ_\(D™QW k•U[OÍÛ9!›e¸5ÎXv}w-©ƒMšT‚cÊ¢’dÕ>œñUµÉ¬QökÀoîP oŒ¹¢Ÿ·ÍÚ6   ÿ ÈWò& ¨• íÇgòÆÆî,ªÆ“יı¶S Ö ü *¶L’ë$à·Ƭ5¡! ! ! ! ! ! !P-.4¢¬8&ÇžËL”z°îjfLYTlU¢Ì¶ÉêËk•ù¥„×y;i~QëbQ       úÀ…F”q&dLà<ÉœªUnèÁºŸÚ ãªNŒ³'Yfœ™óœ'ÞÇ$Y       ó ‘(«?‚ªÅ乚¹W Ô­:üi6Ê*b¾Í™$³¨øªdY%Ì꺺ßUZû«! ’œTZ®(…u€ºÕšÕÐÐÐh\ÈD™>&uªVSµ¥õŒvá‹Pul?{­Q%ʪV™çLŠÕIÝ^«N´Ê¾"°xõv¥¨F”}EL+§! !P-d!¹ZåµÂ:QV,•¼1™cñ$È꺲Cû㌧**¶êº6רSdϳ±N{Ö:ÓÐhÌ™9m|C8Ní½#ÐPˆrÙ£×È]YD´u        R¨ö¹¥6j+ †ªQnè¿»vüeaÃÁ“*žËê¶ uÎé»UÑRy«Hhs z‹@C6-¹I—Dà è¥å€Â«5®! !P_P‰±$I$I2;™A{§QfnåX©°þ¿ž·äúz µW1GæYP‘¢ÂB)&ÒL]Û$RK¤׈smÖêŸïL°$ E½ºãƒ¨[»f¤+ÒäüC`Ü´mÁy¯é½ê÷í)ƒA†ñFÈÂlØ ê$“€D=~xlaj€9þÉJ(”ÂYÌà!fYcIp€\‰$É’Täâ…9í7uæY'/בî»Ù3¦¬Sº9Ÿÿ¸©ë#ÐÝ÷ìóWꂌÿÀM»´I”ûwmCÝÛ% ¡!Á ð-Þ ¿r²Ú içTÚ¸ûPü®'’:^Üïá„nmš÷Ÿ7–¢¤_žiDÙ½¶ ! !p¡" j‘N‘ŠŠŠhˆß¯kwƒ ›ÉXØs(Ñ4q#À/ ‚3žt˜$]6åÉûéƒï–Ñõ—õ¦áº*dYÓ.»áª× §¿ÔW”Ä _  %p¨¬ct:)„ØÌëPëjʆo4hÂårΗ<éóz‰Bv/I'4>zêì§Ìcm¨7°Ÿ93ÞfZ ²F¢%Eƒ¾c€õ"’%‡Lú,Á ÐSЉÆôôi‹:ì S-þS^xBt¯&4Š”o9Ú·hÂÛ5)F/ 4°[ž„ýÇNÑÿ~ÛÐX’åïïœôÜó_¼ùò¿QŒ³sþ >O¼žwØî“Ôôâð©q­†€†€†À¹F@%ɇvÇEôׯ=ôÛú½¤“¾°34e¾†4(HÀ BÍžü!Øú’¼—~øk‹òÈ>P#Ëçú¼ö¥ÿqÓf>é”Ä×)ˆ ÎDA'&€{âdð³(×^:2“×ôy:,5$]&IÆ´"e~…±Œ¶Xä»ÎÒ«M¢s¸,É—€8wÃõÙÅj•"xH®3AmàZ«lPæÊxE’¡'„Á¥Q’sÜ´¤dÿ‚Ž`£ªÛ8¼{»McÇŽåÏîç³($ùþÉ–'A’_ë×¹µ|×5C©ÜÈUÿœx‰ gï¹ZÿùÏ«åM{'ÝñøÓú¯ÞyíÔ,ÄÄÚåZ‘e(Wýh%44ÎcØÜ‚I²½°v¤£¥ö)$ÙPصFGuͨ(~I¤R×f“èÄÉ"úaq&¥žäûqí¤Q¬ 0;—_&¼“å]ïøý ?™ýkq|s®ñK”F”}þêÁ‰/½-Y¥«pãlm0ÐÏ’SŠ 1„m{ÛòdvuGo±|”–%Ï?¾Ò§ÿ¸éIOàv¸ùƒSVU·ê”æ•WÂs œf¿8õÏêÔÓÊ^ø$9©¤·ÖL’ÙÜ‚5É9¹ùôÓÊü©YÑ$×™ì‘æ~yšâéÊË"iôðhšûÅéÚ4©Ô½òò(ÊÈpú…(ë(6Ú@³?ODZ‹4öúX âüþÇé5'kàe•æ-Ù@í›7!ÌZ5›åÃéµâC–÷ÂD1¶ÄÒ ²¤»Dv8ñF²N ’ Ö±VØeK¯( y§":3vÃÍA”A(aw*ê‹:ƒ!Ÿ[¾¥w6#É •05ÕKÑ ÏѸêdî‚'¹Ç1 æÆ¬ø  ìx*ërÑ~L6ÎÞ* Žÿ“ä¬üqS“>5Æ÷ßOzv¯»Rý^Ðýãw˜CBBç6m¥h’ë÷pëçè ÒÎf±ê[-ZtØpìXJFÊWOüÖUmшrµ!ó_ÌWðùénvXYø³Y0èŸm™²§&½ˆùâg¸—v ²7ÕQ~)pLD[_T§½'ß|Ó”š‘’.— àݬ²ÏYÐüw¿9hUuú¨ªìøi¯¶‡6!dÎŒçvrÙ¬ÇH„„ùc‹zkÒ$[UõÏåþ²c?—ci}/^½]9̲D™M.8²Û$ÛlvZ½u?åÃÄXØ—ZíÌ-D¨÷N¤)S¨IG7^C! ¦öBI!Ï7_C-ƒ(uÉŸ9´eg2Æ»ÆÆQ~D e*ëýz†Ñ¨Ë#)é­T…p÷ía©—h@_3}÷s&íÞg£½Íʾ0³žöí·Ó7?e’Õ*R·Î¡tÊ­Ù˜O7^M»öÙͶú›çä8A’KÈûö]Vºat´º»FsÆM_Øžr„­ôצ=4jp-|\,_i¢%iè¤'œÎÌëpê†âÔ‰Q R {b) šW¡JrYÜ23ãzõ|7Ú{ø:öòàT²…£Ô.Í5—2ȺrÒÂdÃé‡rÑ#NMZ$uOͱLÙWI3•ï’…äÊ Ôz/ÿ^úÆ][? ;Û¶IÖÌ-j†)ãvÛ¨Aº7¾X?hôèñÇ>Ly -y|šP_+}o¿^]H¾û*)Ð ƒAw•ˆçž—œâ 8º›«{„¬Nu&Ð º‰Θú)×êµ×â_úi׺ Ú²l×€Idâõ+òÏ]û/GÕß«QÝ/Ee*|j‚hL!Ês_œö=Ž'æõzN’ùàËŽÝ/€hTˆH…Wqi“]vɈ‹¼)å„ÝŸŽ{lûÛ¡Mef³i‡íª@ãïn b.Ñÿ~ȤNíCèî[ãèL¦ƒŽ§Qxxi®:íÚ¶ ¤øÒAá´;ÙF¶Pú™"Šˆ0Ð7ÅÑÏK²éhj!1ÒÏL¿¯Ì¥`˜Stlg¢Ä¦Á´i{íØÍ‘‘*–öçácÕ7»(Û"ãÇQBVlIc_å˜5Œ²(ù¾>Ñ2³‡Ó!ÏÄt5´ÄN½o`;_AŒÔžÏ¾Ãè.)àÅÂX {æv:ÑF¢ñØ(Ùá ó{dl4¹&¡ëæÌœ6ÞÝA`øÍÝòh×¶‰²æ¸W;Ùfzä]$߇–X‘ÇN}ªc_µM0´ ±v¿G­kó·èËÔhhì«ØÓ÷­q/¿9¼M›ü?¶§ £p#9äW¡)n?'izK\ìwƒLÃëg#ÔYj6†N()SóR¾Á:´Èò«hç!”“S¸åŸÕõ›ñ7<ÄgÌž1õó‡,/µÃçè%ð?ž>{æ´yeB’¥;`ö+TE‘Ðxßýn¢<îù™—!íØ'ŠOd vpX™8핎¢ìX‚±Ý1wæ´µÜ&ÞäŸÂ8okjœÚÿ´óÕŽ*šõÞØµ_ôsØ ŽçYö8Á\´y+<©ùóÙ=°9{g¶.7»ð¼t´XÆA?öz7¢kvY ƽe™”ùдW:WÚÑ3Œðl†~¾t†÷%Ùñ–›¢o¨E£gØÜ„µêÖ·Qn Ê#v¨îßs’¦¾†2§”Ñ÷ìëŽ}£PwÎH/~h™ö÷ø©IÓÊŽB¤­’ÞBùË0ÞS¨3½ÈmiÜÚd„³C‹|8õ Ù3Ù{MHt¤ž¦<Ñ”âb TP ÒG_ŸUšmÛ*X1wøÏ‚tÚÈ®h’{t Uì„§º´ÈõŸ [gX‰€t‹tðˆ])6¸(eÁÌc[1 Þ“b£žÝB¢ÌØöøç%Y´yGåïÃ}{š©{g½3·æfžãf»×|ë×vx(±ù…᪗qŽ ã¦Ïzy!›ÁÑQFšjûµ£êžF 6M18Z’ÞÙT/Ž€0§>JŽ3Þští{3§­G(à±Hú›|tΉx§IíŽÂÆÅ]2êÚþ«–ü´-²)“äb£wßûà·Mê ¸or@X©©} òÔáj’ü‹,ÈY cϰL>R»6ooí“eÇXþã| ëOÇÎ\ÄíägÚÆav•@zƒî_¸½âŠCŠñÊrc”} 8äëu À-D§<ëy;Hò3gÁ\‰^Æoµ‹Ëj8ðÁìB„ŸC1»8¢Ì'$ÇIö‡Øl2­X›«hhÏf‰n¹¨—v8õß“]ÂÎ~ÑQÞu¬®L:¶5Qd¸Žžz¨‰2 ‚cÛ{ÊnçʤMË`º}L,}õ}­#Ÿ·¶•xÓ:g2dœoM|G€}.ðÂý ¾"΂öXl»H¯w¶À)ª=’}GÑ·’.ÂÜ&W= ÚÐWææ –—ZùV»NJñM@>Ï"q’ë¤Ó ½Æ‘ñl”ØlŽ•9 Èçtå7](+ÚUY‘:^Çã¥BçÌHA× eòËËeü‰@ì_?7iúÍÐü~ãt]#µc(ó=´ºÇd-F¡ß}ì±B9L·F© èö|`™¶ÞU»ä¯9:Ô‚ó#'ßaýgÉåФ>à-e³àfº¹yã? Æhh•e£bЦššÔ4± žÁ§¨ùú0ýƒ%=`I¾DÝ1xh [fu™„9‰üåófÅ‚TÂ9:ù4Rp~†çjïG¦Íh­Ö÷<Î÷-S·¡±B”9‚ã^ùî”)`9%¢7’ûÀååK…0ãFŒåeÞëK?8ῇV}NS}â âÌf–Ι1MÑÛÓxÕÆmaÜWaL?ƒn% ²´8ìÃ1ܨìÃÔÍš4u‡/Nû¯'ŸàEæ „>Òy;‚ƒó–äÔµú iê׳“¦,PÛÑæAÀ•X„c&C£Œh9ùœT$ˆOg¿th‡ñš¿óé 3¨Uó EcÌ Ÿ#KcD¯P…—óò]Û9Uv°Gt®¦M* æ¬Q~.é¸{š<ó„Ú´2‡F²ÔºçJb‚‘ÆÝÕ˜~ú Zg˜gøKG¶ ÍÈÉWˆ2ã­‰oX,Ë Y¹ŽïqOe,ê¨ØÌ»žß¾Õ×JÕ ÄŽ¦ Â>¥aˆ’âB‹å“šµä÷ZÌÃà¯kÉ÷8>°&. qÿ®©0Žá¦`ÙÌoü[ab¢\mÞ[í èD?"€Ü/Þ|ÞÔ›¨9L&,žÍƒT2VD’… k ˜6ò„È9÷²¦RÝ_ÙÜå'Ãt@î…:?¨Nreëà‘Ëfz:~f¹Ó™õ'Áqév.‡G=È.Q¨Î´•çLž Dá0/³‘ô5ÔÃÍzaæ@d™¼‰÷L®qZé2ô«áü”Ç ­ô|&š…¤ïîª <ŽSÝVÑüýMÝMÜDò ”ç<<~ÚŒG¹¬/ý€^+ž_˽HÃ*# «k]éK2ñô7{6³€¡Øv'—'üLMÀŽ;)å\¶ðÛ*/bgñR€7Öå^¯'³ÁœçÊaÒò-Òmn}púKý=ÚÑ€R'4L” ‹¨“ bço9sÖI+Öåѵ£¢BG¬IÎÍéŠK"ˆC½ N‘05Ý G;–ƒ‡ ©MËjÒØHl3ܹ½ûc²?ýŒƒZ6 R9jÅ®½VEƒ1¾ ½niA½aÚhéØ.„n¿16ÐÝ”kŸ#Þ÷ëž©bšò\+WȈ –¤¼oóiÀ30i©ktqN}ï{Z¸ á¤k!§3ó(õtV-Zð_ÕµÛ÷ÓäwçÓã¯}EÓpll¨Q|ŠŠ4 z£¡1FÆ7M&ʬÁ¨6Æ%jÔÖä §¡½§¢žAT]OX¥Î5;Žòm+*_Ñv~{Nu¤N·Û‰GÚõO¹ë{3žÛíYžãuÂéábhV¿)<®ì“éHãÕl’a#Û^Ú+»XØû2•rŽ”&ê#ò?IÓ?8mÆ<7¯Á¶ËÑÆWL&'-ZšM£¯ˆBÄ XEáÂúsu:ZµS^ï®& G®ž˜ó¤Ê§óÎÐVDÀ¨µHz`ët›^0æ¸6kÝì…ÜÀ8ËÌ>²CžÀfzg‚_uÄ¥Ô¯W˜bß©=âã§8›Q±FfcJDذÛí=ó"ßöÕ;º_‡Wª±Óx!ܵ·r“¡Rü¸¢CömlÆGëmhöc_š†íà,Á†w¸/å«Q†/žðsa!ìtpŽdѪ­ÔñÑ×.2Ž?†ðøi}qO‚c#­ßq€~X¶ øZS¸¹ä>æK?Œ'x¿ù©$™*æ>ŸèQöízRþ2?J¢|?Ç.6è£ÿKTætµq^]ÕÓÄÔÀY£ Ýì\à´~U$9?†#Ý`ÏÐo‚S¼ gN~SCâ8Ö¸r›,¯5…×YEÛMl²‘Ó¢ìœR=ù¤s?ʳ&µäÚÆÅþ%¾ÂND_¯¿—Û0êÂ׉bîÔ›s‹IE†Æé²tv蜦,™?>)'xæî³ø¨åß&“MÎÉ-¹áŸþrOd65˜ÍkDkþW°ôx2òzóþêöS®ãâ xÈ,ăÿö Ófü&5o¼ŠNdu2èeÛû–É*ª£n/;vh‡"Éá¡]ÚìþcgÊ2<ˆ®TËjóÚ#0æ 6í/-ªéÛÏre…a–.Rã5Ž@Á“*6ŒÉ3™`¸„ò^~÷¤¢æPoLnUÉÈrÒ»ÿMW´Ãj¦¼_ÿ*ikÇ«BªM&½Žë-]‘£La ½vDÓpŠ®Ù¯2'>ŽŒÁS E}!ÑL/|C$ùE$ÕôE­üþÖÖN£á%j-Âz ‰€¹ö­ChÛ®ÚŽsÌÓR¦¶÷ë .Óꎛbè‹o3<«+æžÝÍ0§–rËç“ð®B‚͉lþÞZ Dz¹ô¢:™^Dð2hÆ9Ì_8K [Y®E©«ˆL|Ô_Fø|綺t4QŒ±¦FKñbÉ×Is„W2 N£­Ç1E俉„8‡Öf>·'ÀÛ2pÂN˜„xêѾ-]· ÷¡ Ö¯]Ú§£ÒêÎý'héú]tât&5¡ é¡8ånÜsfaFZ·ýÝzå $jFËÑÎ_ïQL×:¶l¢”m؈ªê£È!ÒwüMÛà‰_žûwiM7 ïO(.¾]ú7mM>Jƒž.ïß…FêVîhï¼zˆ{Û¥}:ÑO+¶Ò¡Ô³Ô³¬?«-üÕ^Ñ$ó5ÇSµ5ÊÕ®Pí1jü†Àì§ý ÀT8ѽèpÀÊÍQ„xWtMUŒ·$õC¾áIÖnê z&²Ýþر’g]D™¸ÛVI2ïûÐòôiÌþÂñ¥¬ › Õö ät–Hꃋ`¥²½øÁ¤ƒYg¶©q+ßµ<–‹í&Ú.…²pPvœ. Q~û…*¾1 }çû0wèmwääääÝäÙœ[ƒú|Qx’’ôõ«ìLX“~<Ûõ\ÕýDZ±¨—ÊÇÎÀ^Ú±Ò)I>]¥eǪtµCv¬Gt~‡QpBœìÙ—¶ì_˜¼¹&¸8#޲²ìÁUýÛY%­q¸8O’ìYT%ÉžÛÔe®Ãq’ËJ~>H1I.»ïœ­+Xó{ˆ ós6Žó c¶MÆ0GÄ&0’ð;OVHçèáQŠÙ…§“è7Ç)¤s¾VŒI½º™K¡ÅÉsÎf:a6ä¤5ëK“l6º±¿×mÌ£-;¬TÒÊréEá4rh$­\ŸS¡`ºf¤K ÙfC÷ÜÚˆ"ðRÇ™Ï×Kò…_ôF\‰°‡NŠE´vTeaó¤1ˆï½_]8„"c¶­fCL¤mXw#¥¬_þp†?.&B¿´YóF˜Ä)®!ž×ZŠàÀ¼çP*­ßyätµLˆ¥o–n€¯†CyqùzÉ:j…mß6dºÅF†cÞ/6ÁÔ»cKºçÚ‹©ubœ2Ž˜3ÝpY?úçÿÉiÑ*¸A*ëƒ÷ÿ¸|3ý½ûzgº ŠŒvÍãy3­Ü²ŸvH¥{®¹”®¹´7-øsɪüe>ùèIèÜtʘ•Fªù·&ÆU%Én¼«ÓL ß’ª3ŽY¶ø³¾×OûÅšÞr´Ç³pA½4ñ…×c‚sØ‘Á›óÜs9˜•*dž/BîßúCËä#([únÉm$MïY9A;#ÕsfLý/nøŸž¦=!ÞR„¾7e «#ø3G)a§zÊ­‘=z2ƒ÷lOÃvq÷i@:ú˜H3q bU˜@³ääY©4Ê?¯ÜZê^ZQv¤¾[Óhh«=e×㊦;>6œxjI[ö¥+/êîY̽œ•[ h YëÎ5æB*ÎåøFU­ºÉSUµýþEàÁ鳆¹mJMZ?}¦R­ðt!bú&Õ¤‰ZÕIs®Qêרïbï_ê¦9]ÇYaY´UÙ>d…û}D Í™¢x1¦ÑÌjµUvìêoV2a)â2¿áã0´b5@@%pI®xÕ¨¢áëXlºSy~uó­B J±íùN$®¹÷¶FtõÈúfáYÄÚÖg’dÙwÀ¦âÊšnÛ*ަ.m/gsLz;†C<ážÆ´nS>-\œ')=²N:•f2²°ÃÏvÛ;³VX•“§Š”tì=û7²N®ß\B†Õ2QÐZs¶JO‰B¢Õ<™êhᯙ òµ{Ù•;9ƒwK²>—Ó“ïY¦àæ^o¤Úä­ª‘³&™%ÌÌ~lD…Ð4³Œ¿ñ2úê×µôâÜ…0‰hCwŒ¾H1¹Pv–ù›÷U” ¬Õå—OÇ]o}°©[¾µZ4‰)Õšˆ4åØ):xâ4mÚsĽ/=ûîˆ5àÿùæjÛ¬];´»| jL’¹?(×õÚV ‘÷áÂ׈Rm›"%МDHC;­ß’OOŒ‹'>š³iþ‹¯§â³ÙÚ•¯Kú3äÞZL–„ XªNLÔMùhjåËî•…ä²›ü´îwMreãb'»íûQ§– Š}rLÄ †³K“ØH:’vF±efËÌWdœhØ)gdçÑZ˜o(âÃoÀ&[ ‰îÚ¶™BvÏ¢~Ï=رmØ}º´iJíZÄÓ©³9 ©n]ù‡5Ö³,ÑΣ»¯Bl&Âf ¹FI9ecÍÿT w(×h­¦†€†@=@ÀÛÓI[ÉTûAö×s8*ÀgåЀ©Îxm¹[4wp„B¨7v^⇠§²æ]§Ò´;Ù;Aè×3Lq²:‚¨•…k㘸lkÊÚBO)»£tí`¢Ä¦Átèˆ äšÄâOîj=Îê×§G(!{'í„£Tmm>¹]gµm^1‚‰f!SçXgÈŽhƒ½'bËû/çÅœ‚²t' èáôyäXÍýâ´i…I´ºï_¯žpôËù®ìî Å |~L%µT”ÞÅZÞolç{[È·z=xkdžH-ÿ|þX©¦YÍ 'éá…|þzŽï×?²‰'´ÚÞv©+X¤0¤GdȤ!°ã-Bþî„4ëøøi3ß“ Áͱ<åýÀË´ÿ•ñe6ùs•I¦Úk>«”(‰´xÕvúäÇ•d„æ·MbcV c(4½ó–¬§'^ÿŠîBÔ‰H¥ÍvÉ/Ì^H¡!Atݰ>ôõ¯ëªêBÙ?vä@úè‡ôÎ×.]à îm¢Ì‘/¾X´†ÞÆv¾o˜Ðî„›.'O¢œ‘“G™ƒE­ÏË};·¢Ç ãÅšH1Æn;åjµÁ•ë,zÅ•ÈvÕ³?yêÚ€4ÎfÎ.¹NÏåpO¥ß§tß$þceΟ€0dz#L« &‘y0Œ÷‘Qß YÐzW{¨#†F;íÆ§án°µ<¯}&-áôøƒMˆ?-whLGA¢?ÿæ,º,’âã Õ%~qV¶HŸüïL©~/A£ ‘c›Í‹ú™aƒ™U.¬„»q€ã.ÿ¾<—Ô0rmðÎÆ”èŸùâaJh¸Up’R% L&?ÞT ?çpÊ4‘ ^z'ÍMP-çëܼq¢m$çv  `3Â=ƒ¤©iR)ˆ¸-§+z€ÞÑñ”+-¯íôˆùmÈBê«“$ê9·”ÉG:Rˆû«ƒj·ƒN_YHE…¹‘B;DÓ¤;GU»êVà0m:\«lwì)¬t°Ù )0Táìx6®&Rˆ8ëìLÍ„ØS8‘ I Ìгb!PòæKqÿÞ/ßyõqôq «§Ù(OW`lÌ«M£\%DZ  †ŒÀŸ«òèÄ#æ‡ÈA$úOÑ_"ü÷À>á´ žú¬YcMíô&Ò‚_2•#*^ìèÄ¡¯ÊÊ`ÙïQ–ãϦ! Àe‘U6þíÀ¾f:†(eµÂmg­¡*lÓÙªE­Z¯nÁ‹ÂÕÍx3f×·SÎØ³‹™þX•SRH[ 8¬å4Úû‘3(S ìg)dM/Æ‘ …C±Xš¼|@ ¦^Já-ºéÙ±ØÃ,ƒ Žâ–†$FƒwêǤՓ$3&5%É®ºÞûa‚rÞ@Þ`®È',oEùò«p‚ „%ó.^lœe¶OqMð)BxÈò^¥±ƒ8§?Çç &••75éfØÝÝRYí« ŸÊpfL‹c˜V:”ÊÚ¨´¢;}‡ÍiÅê&ªL’YÚ¶ ¡£H͇X° ‹…$pd8„³biƒtÑÃ/‰¤ËVë—ß³•mž¸nzqÝô3EÔ(®¼Æ†ª~ý3‡Ó»{VU­¼mçB=‘~˜“:pø­åkJÇÄåý*IætÛ pÆ:rB‰.É»4©CØäÂXØS…‹†T¼… M+¨(dȳæëÈŸÃ…äfw„$Ëö:}jmŸ¿x§ûçáñŒ›93lòwxoƒxÀn—âñÏ']ƒŒB8òã–8EgÔÝ0Û2eOÙCœ””(Úh\ H¥™yÈ@÷4Ú™]¶¯WÔ—gY&³©ÎYo§:“ÇÃôHÔÎß'šÝí™Ì}¼‰}=(Üp£çœ5nÚÌ»‘ðÃÄi u‚þÙÙ3¦”2â|ó©Ž5ku´Æ‚¾’<û Ôòøçg^¤ ßXQü_ð‡ÛŠs¨1p¼äŠð© ç§Ï¼Uå$‡#³m­ÉÞË †˜;ËÆ®¬ oÇ„vFâ¸ç!ÁÏWs“¦>¦–ávÒÄ™3q¾=‡Ä0—¨}·SQjõu>u¼ßL¤Ø%Ÿ½œØk#S“ÿ{ü™wzviß¹¢O’üyeÒeÎÙøl6;¼äóaŠEé§ÎÐÛRnyÞ¨”÷õO—!tÉÀ0z÷#öÃEÆIØTz&a„P\,‰Mƒ”ìdœ‘6&žÂÉ xÂ×OExn€Í0Û—V9À³~eËœ}3š!u6Gð&ì0uß픈eíž½•¯l[ $›¸ÿZ8섇‘ ŸQõì-V áßnÏ£›>y-é:T«ö'ÑjtU¯‹"è0X }ƒ²Æ¤ƒÇ›ÓÊšz}àçxpzG;˜cdˆNÓ¶ñ¼¿±¢áð³WõòÜ“7VT¦†ÛÝ÷»{Ÿžö#Qtùð5lX«æ?JßÁý×n¶4~ú¬‹È*oF§ñž?ùæ›&Y¤/@j– F]_Ð Ù)~êYF]íô >;Ä2ÁA¹¡Üý`‚å¥Vê~u^Q_ê~u~Ršy@ÒD<ª&A%4Û‡§:RQ÷3!I¾UÖ _ëœÎ0I[Á³ãj•á .’Ç–WCCuMôFdÄ“…¢#k\Y¨*k£lY8<‹móñÛ¹T‰Å&¾ôRtªcæbI½¹l_ÇQYeÛÔÖKàpG<RÚµ¦ÛÆÄÑœ/ÏЩӮŸ>/O"s¨‹2ùdà¼âx°lüþ'éôíOg鯫c”ðVêøXCÍ™÷ÂB]·_3æ¼^[’Ìí÷s&½õá)Ú›l£›þŸ½ëŒ¢èþo÷îréH(¡I‚`Á.VTì]@TìJIâwš„ªèç'R-Q±VT‘&‚¢R¤×$HÏ%W¶üo/{¹;î.—†”Øìì”7³o÷fóæÍ{×6Ñ›tŸ ßw{3L"$·ƒwf8rÔ9À¿yØVV%–¨Š±©*¦Ê~¢Aj]ç¾°Еá £Z/”W®€îü¿úœKO?\íÌÂ__xC{ý»öÊDxîk\ýUnß{2ÈñUƒT|{Ç Š<Þ_ŸÃi'>N‰²Úá¯ðÒ<Àî3úc³WÄ¢i‚Éhù¦åùpò1þl—2®òN§ÅÏE½ vôŒìŒåH\©á½Š¤0è™ì.¤Eü·å]rm…nÄÏpõ̬ô©œiñ\¬± Aôe¾Î‘²âGÚDˆ1ÌŸîòª7ˆÓ9 íy÷S\÷ãÚ0•#oÍ€_·Î®B>?ùä\$oÇfšg’Ç¢T”§ÆDz÷:ž4X *^Ð Ú6ȯ^‡Ã‹ÉÃ3²Æú)³Étñ–Q¹hûQä?j6Ó {~é»pÄ;Çb¨X8fd¥uƒ‘íÀvRðÿCê5hs…*2õ™÷0˸SÉ©¼‰Jg£O98,ðž§IŸÁ“éøîW:T¯AßþÁDáîYÏoólazz:¯CúåyäóŒ—Ò~Ð逗ùh/O)IOÓÏ5=+w9Ü(øuº`.Q¤=W=Ï`•áµT4 7ÈNõo=Ï¡ö# Ozá¸7ÃEjc†NÒÞw[3˜¿: ÷»NͶ,›ÊÚ“XlWv%LmÙ3–Ê`çõ`¾Có –{À¦S[˜aÏVÖ¼›±“†ä¦&Ú¶Ó‹6ê{ní‡C†~çÄÑÖ.–ÐÚ¡KÌíÔ&0nžlÒ¼ž±ƒ‰””ôÅEC§ÉÖîHVðƒ›ÿu±&éfЮ[,¨M{á²õçÀˆŒq¤ry>ÆÄ¦©­`t´Ã¬«vy½` |Õ%pUÝ=Š&MÍÓ&`1Q <³÷¾`•|ûc±{2¬lmóðŠbÅ‚„±Òa‡å\üŽî‚ëío`õB·Ž¤Z½²TQSÚw-¢$¨'V tƒáô`8!þŒÌ´™|“CӳǢºïà+¸ÒÆ ™# †6˜GQr[\îæ4<«—Ê¥h¨;ìr¥€Š*ìèn§_ëç@méùçT€ô×;¯–6ªÂ¸þÚ×3—Ç à\¥ƒÒ‡3Æ÷†Y—¡¢)²§"Ù–CšÉÅŽh¯=6rÎ@½lQUY¶5U-“x;ÏœòÂÊa¨5óç{UAmõ€ÙÅ "’ãß±,}Â!9§@ý䨟ŒG¹¬fr—œ¨Î³' Êç–ñ$Eî IðÕÀøxøô.èû† 4ôÂU÷7_ƒw&— yWîY»‚ôŽ®ï¿¡ô£&ÞÃWG‹×]‘Dq±zäê«ïåkÖ.ØÉoâc°ùþü ”°»ßÓà6˜M°1à‡M{¬¢qœ.œwv,6ÔåÒW‹ŠiØ=É”=¦¦ç¬oÄtiÁÔÜGŸÖêöXãá®››Âcš»ÙUÚ½ÏA_~ïÒÖiþ¹±R³ÞÁ„{uwI,÷BG™ÍŠ…ÃÑ}Èц@IDATåT¯ÇÎÿyдLöÓEA‰iаG<3¬¦°Žü~6kò¤'h‰ñX^)§Ø¤z>6˜ö„©C–>ï˵ÓA˜6䓟V”B÷ÞD×B÷}ñ²¸¡vÀRKÔ…›EË4UŸó -f"¶Ï¬ëí¹¾)œœ”ÒÀñ´zýºY:¾Qnÿú+“ˆ»¾BÃ鳿r«3ñW®g÷˜Fʼ0¨ŠEŠ (˹ýã!°%‰•mƒgÄ :õ”–tjû–ø|2·ˆÖÀu4ÛîÐ:E3§Æi\~ÃŽý˜”PG¸§î†:ìüÃ3°}åŸÿ^+éÌní°B—ì™í޳¥ŠŸ×mé¸Sàîºz;•¿ô’ò Z¿e/•WÚ©{‡Ô€«~ Jàüd]3à w;šÝæ?·k÷ÕÿŒ.xO]ۼث߆íû)ŽHÎ<µ-%Æ5ìïÆÝŸÈ ”}îÉ} 0˜©­UOÌŠ•XÈbð–*ªvÒÖ¤^{ª^;Ä•#¤z~g•·ëåð[UEÐèY,ïDæJû´Þ¯çëçaÙw@íâ:ÑD87¡A/ùmôgÔtËsùгÆ-)ÕÕë¼}6+3ý5¾FÙPôrDçùÀü @z joh…Ã{á¦ÿ=þø$H‹‡/Slê©èçöVÆþS,–‹¥–¬õ¼lLkß̵™i²”›Ï^ YPKff¦ç4HmÛ£T¶¦k›3®?À{Á „Tõ'Ôý€õYUø¾YU’|Ê‘™þÇQHD}žã‚/|ò™iHNù.´‘i5„ד)éHÉA 4õÁÒÐBHýT¸ÔQæÀ”éy[|ëÃCp,ÀLZõ«ðÁ¼ÃZyßôÅ¿”Ò2ØZæPX,Ñ„×óްO;ûƒÃ^€‚ËÎ(÷<Ó¹ýɲd™¿™ž D§ÉÒã'Òöø#N;Šx4cBw‡,},*ñɽ °WÖð­ã˜÷e!VB’áEÏýYÓÚá÷cÔc­4sƒ{0Q~o e¿–Gûa3™ßŸup’“ç &Lº,^Ê삺}3Ñ#ZÊý¡«Ï 9µ¥‰{°9<÷Ã.¯@£F¶$ ƒðêJ÷®QPŸ0k^üvÁVxÛT³Ö~lŒHbÒùëZ«HæLOÿØ8»s—k¥E«Ø$¢U‰Œ&í›ÙM48Éó¢È#µJN¢w¿\÷ÎgÐù“‹ÝH a#¹_¯NôÞ×+¨Âæ ½»Ð[ŸÿLÍcáÊ:ŠÞ_¸’®<¿‡æϳc³?[J X»H¿2g¾¥¦xß¼CÅÄm,,OveéÇS?þ‘zviƒ±Q¢Éÿ÷ ¥?tµLÆçÚ#üþÏúà›•dEy(;P~"Ê·NiÂxƒVÁÑÉ‹#nÔ&³üLçãþ¶ï; Ýoö£7i`Úƒd£D½§ÒÄ¿HT ÜS¡\Ö¦@Š*{öJ‰´-¯¢"¸Ëⳇx=¶Â TˆÉM5`Mk'OÊ» UH‰]èÙÖþÉoA}âþé–ô5œ—ç\1øS “ø+«1 -.%YÛPèY¹*. ÂZ=ãá!Äc5É® ^ˆøÝh÷7>@²P*ô¡ñ!ÎN_޲KðNöxÇ ™Ó}¬[\ ®ÂT-Ü3pÐZ§g¼Ø‡Ž£ ΂­T¦#FýÉçª2ëñö·åkà³{c%Ì:þ…>¤øÓ ç²þøƒ{Èg®3+;ý¥V¦.±ª`ì‡Ëç%gÑ+œîj áU¶Ž!õ£Ž´ÃÕþ]x‚dÏžø¦ó´ŠÕ*<ƒ¯ÚƒÓ©@*‚u \×$3™úÒ¬cWÂÕpÀ¡:'`%ß`´÷hŒvXÞÛ=õÍÛlš®ºçœžá¸“¸,·°®}ïÓ£h®ËšË~H“9¾êAlf¥ÉÝz—B²Ü­S4ECÊÌúÏ›@»$¾ÿàÌ yÉòR˜3t@å£ÚÈÓº¿*àͯØË±Í0Øûþg[eP³„ƒ.K„“ ‘~\^€‹õO–9pkÕA0Ó2zoý) HÃnº˜®¹à ÿ°Éá—?¶Ð­Wô¡[.;—n½ü\ZºV“mÑ#C.!vrõù=é xÆÛ¹ŸaAu()« ¿¶í£'"îÔ’èv´|ý¶êU±oáΚ]OGš½-ôøKgsc¼V¿ì;õväxOø/ým3Ý~åy^mý‰¾°ùºÇo¿LsŸÍ£áßÛr¨#¤äY ¦k/ìM^!—Yž]B /pqBe€ÎÝ@b‘Y&¶bÞI¡Ÿ±ñfŸõ ©?T¬Š–A¸‘:âW9½|(g<\~{;êe¡=Ý i=|ÊXíb>¤¹îéòðŒ ½€¿˜533íÃêztX¨‹lÖ•Ú²`ˆ*•IzŸs…Ï5±ëT€c;€èT€â&î#+ý.«mPÔ‹!‹Úžb}g_| }ç?¡bv±ë0Žt—ª%ñî4-"°º Uȶ®é]wKuüÝyŠªvFJžÖñ¾äO>ëmZ,C¬3¶!íR=]?{Vz™†8×Ô†hãd¤±ewñaëÐÌTªÂ¹¥QP½Gƒö¿` ÀN$u?²‹ªõÏ£¿üZªI‰õ|ýÌv·wí³[{i+.Ë×”kª¬ûœµ öÎÇf+àyOVlJep­|ë×­A‡Ï)m#5wÕzºçù˜UìþNÅfX߉¤g¹úÄÃAØQÞ†O®¸ •1ý¥úÐ:Úu–e¶í;HíS“µ..*§æM\6[6KÔ$¿zßXrûÅÒßiWÎ!ºäÜnz²«^I9Ôh Ô®ª9´àº.û†® y×cg!ž!PºÍîÔ¤ÁïCbϪ]ÛyVƒ… £æ\%9)Î+ý0\W³;m=pü`¡«?ÜO;º±¯ .­«Ëéåãll ¢Ç ÍTcÿ¥¹Òò|‡Ó9j¤eâd›äxÓÇõÓ,cv²E{~ÉxÁh˜Íæâ ý’ЇKÏü¶®Æ=àí‹Xà[.ؽ OÏ|.ìÐc~KXÃbÄwÃÓÇ]´UuÞ‚6^Õô¡­ÒÕ°pq•NëËøNXžX 4v>ÓZ¨L°ô“"bÄ-1ÕeŠâ~Nr¹¼3à¹dL§×å ÚŸcpÌ-RÚ¤üBû‹N5ÔJC“˜}ÐQ~@þcC=õ‹M?þµíYМ(FˆÛ°O–ÈqÁS–)ûvh]2dÈ¥¡´Çe¢“"×ca<ó<7<}|,:®‚:Lªh¾Öi`ЖùfL«!iyâŸÑ†¬çó9$›ê—Ï\è{YO‘b•;û`²p-é%œʳârX²Ï$å®K{våÛ/Î%„ÚPh…ËÉo–k ÔºzzÀ„S[’s±6‘öœêÙásCpüŒ€äGçuC<Ñi”Äÿa¢Õ¤%ëoÅ%pYŒ qWÁ ¤þÞ\I·Þ@šçÈ{lšúV"µ• ,ÓÅÅòç†u‰þÙ‚M§ÐGff^™Ø²£’œi²K³k¯.î¯é³»c;3}·äH¥ÃÎÿyžÞœ®¿*Isº£çñy hO•3þïæ#!Uæ>5dM;a;y/o1ùÉÜ<îËãBÍ ¨Â–†ìƒ-ž;¨PI´Ú\ËPUÓŸR>—_øVB½âòóN‡ wláÇa2ºdŸ ¦%‰-騚:ÍVöBG™Ái%¬g`É®É|s]N«o`~›väPîábÚxª€~³¯3m°ª¿Kzà{òìO!€ý[pÁ}ûUçQ”9B/æ÷lƒLI’*‘©ñØãì·| ÄêÞ*q§k*¢xná›Ó±ŸÎnмŸoÉ^XÑ‚ã'ñv]Àצ‘-E¶«štÀí“l}Á·— àLþ!Á8:¹?â 2 ¯ï|E‘Xwy|¢ù¿ªUºñÃ-ŒéKqÖôW‘¦¢?3rê ýpZ•“F*›6fL‘~àiãG-Vδ ?Br¬Óów6G‹Ïàþ~‡¢ïÕ½‡ìîË$EiãÈ/ùÊ77ÍOVY— @ý"«yL;¶u¦aü×*Y®Ü”ã=õó×GÚ«O?]i4 ¼‰®“¢:¶@åb<ÆãÌ/±½HWÀGv9îi¤CqnÀD¢• &èyú9ñ¹ªnÕ©,U˜Ÿ‡ixaúç…ò¬¸,X ä÷dɦ=MùºŽ!¤~Ô‘öI_UøÐ7ß`æWp/àøf‡¯ëÂð3ÚÃ5­?¾×…ì‰\‡ÇDði£b ™‰㳿g_›gÕ Yë£Ë^¶×½ûë‡WðE9ÀNýhcÒ€²j($Aj^gš*ò õ¥‰š)¬Ý{n Ü®¤}0E3%wÁyqÔ–ÞûØ{ÊÅçÇÃÆmÌtYáÔ¤©fžkåoå^M…b_¶\R³Ç=–Èí‚y¸FÊà#ƒ¢v-’4{u6|”3²Æ.€:Öl$/Úœ] /h¼É[°ÎTúQkðÝ„¬¾žWP­×iô3d]²!—Ó^IìÀ:—àòof¦±¦ÑûRsê}{VÄ7múÄßÛs¨Ïéþ¥¥ vüèÚ¯Ÿñæ7Õן¿›nÀF;¶4ñÜkAW9›ø.Õ&;ï|ù Á”ƒè©ÍèÆ‹ÏÖè¬?Ñ]W÷£sOïH÷ :Ö1–ÑgKÖÑi[ÁrF—#ʸ !RPb…EŽ_4Ë,í>µ} êFgÚw°–3–Є‘CÜ&ß|ɱšo<üÐgÒ&â ô?[²–òaqƒï_竲£Ï|äýf{·þóÚ` ²~ø6YãõI”käD¸ÀÑç€@°-†áæ@s@È,Õ`)g|”‰ŒX†”Il Ü¦¯Z67Ѱ Û¹# ˆ\¡Ï™q´v½KÝÄŽ Øæò‚¯ ©¼Êƒ—ê ð|¤­ùÃJ¹tq¿8òʡؗ½ì¢xÍ>ó°§ÜØùÈ&ªR`vJçuc·y"ÑÇ2þ„aY6Åxh‚l(2Â#ŸÁ ±Ž}x8¬ïsVÅrälÑBÂÊ”èÅ_1} ο¾¯/íFª¯¸Ÿ~¶±s¯Þ¿mÜÙ@ÙïJ:[ƒÐ%¯¾}i »Æ/ »^³›lÆoSi^§é/3L4xèþNzâ6·‹Ó`çø•§o×tYYžeô´×Ÿ¿Kz=Ó;@RýÒˆÁ ‡} ¦êÍœþê³wxé·kÙìˆ{ºº/º¢o¾Þg¶žÁG¨|TY.ݰv5`é±”ù\«PÍ‘ZU s þ˜••>¬þTÂÂðæ€ÜXÊ˃¬ˆå9>7‡JDNœ‚À:¤¨$zWªç;î˜ ÷Ö¼É3°U€­ØÅ¡ HÂKÕôAË­¼°ã \æàa×›ƒ‡” ‰°oðT×d_vÎ'.-2ƒØÌ·zƒ^3ÿ±€ºÁ\ó•ù¬ó¼A:Á‰ÁöükZÆ}ƒJoI[ûËP ¥TæVP ¾IégM-oryC ÞÉ"˜¤.±ÈLpBL¿Ð ^žž9vE- QüaKÖ¹²lPu³G¨{‚'x“åæ|¼AÙ¶÷u†MãºO¬×÷u0Âé¾fÞ8Í$*Ãé¡O¬×©iž^NÈúumÎÌ?8&önßòêñ.nýðäwÈ$Ã@9dV… †9æÀ±Èö­Ž†,E6hÙ¥Ãf„$¦Uœ‰òáN „L¶³YÖÜè·c‚n±'È•à-7›–ôÀ›mø`¯føl„ãVõe=Qßp4ìËú¶éy mO‘Ö.ÙÍãÚê&{Ò<™ãS-c·âþ°zH?ŸX¾&rñPj&äd€æêŠÆàëð²JO~x)Ê)ÑLíhb£(;Q aL´‰ËXYߘGØŸ ¨heµ cÞË$8ðç¨K,ÄæñøÚ5¸´â¤qXâ—.Uçþ¥k@îçÏçz󈑷ÎýnuÒèû¯ýÍ:·r’Td)6ø βß|µ·Í£+:Xö3²gN(çO87Ì0Žqøê¨éÒMÊ #""p˜p)5îa¨^ŽØLFG÷F¿3væÀn€9À~;¤8"•y¨]ð¨ ØžvçœùÚHÖí˾þVãÙ—­‰!²y3@Š•z·çM@f¿Ìgç5ÕçûçÀŒìôeÈYƦ0e§|›*–Þ¬D”ô’i@¢Yåx?Ž×´¨ÂRD=$Î<ÙQÅ2€cB)Ô‘ àe@Û—GF[¤È@ýß 0ùO’yƒ¢eNÌfË n|ìúTu *UØÏ·‚º ñ¿ ‚¸Ì˜»&T+µ¾7xˆ©uÐ*èNqRqñáòMëÖ¼*œsÞKï}µ\}hð…<=‡Zp€ù– /‚ë~YúAE|rñò:XÖy2ÕÆzø!wàh Õª‚?ë þúÈÖ1\ÿüåV§±•‰\*2†²i‹Ï}ñ͘7-zïä©&GšÁzºHñ´á‘}ԣؔr ~É"ú»ñ`ü ÆçG,ScSè4[ ƒz¿ƒÑÐËÔçj?ØvwÓÂjÕÆËú4yÒÖe)-Ûe€lÆŽp3À²Ùl¦¤èJ²:ìØÍš¯ñÆ«)YÞ²½‚.èO+הљ=c5×¾óšÝÙdx<ÛëÿÀZßsãhÿõ;'-\fìÚ¶6kžür´/ëY¦16ƒ+ɲ!Ÿ:§ÄP›ä¯Ì_æ³n†ª1ûp2Ð~Ó2f;î3‹GÓ²Ú9D¤ªŽ~ŠPp¾J‡Ú3šÒ‚j581 &ùYU“•ª3>#˜¸èX J`Þµeƒ0@±ËšJæcüþf9盌†õv§óg)ò÷¢ÔV48S¤W5Øø'à=åA¿XÙ KC¦¿”¶ˆ`±yN…ÅLð­MöFÄ›þZ` 'ƒ8sö•ß/\ß$iâ÷ÒRï¹¶¿–,ƒ5–$3H^»y—°}ß_­_¹ìoTáÁT?t°\k‰ò19[©‹y8Í¥s¥:Sí07Æ»"´0ü…¬kaÛømÌH±>+lMâì`DÏ×Ïge¥Ê•x-I=åxûèÌÐóõ3UÜ h/´µÁ ³#Ò3³Ò^Õóõ³Å¢Š9Ҹװ€:£bóS­ïµXîw:DÁaé™SדâŒ7 eÒ8<½{Ñn†¸¢`5#sì*Ÿ5=)'­ÄÔÚ`ʃi£‡á/d_!Æýæ²Zqdsž&ÞŽÌm˜6«ˆ?Áø<4#û6AU³ð¬:âY•àüd46¹Ûw"Œ†¿;Às»Ïh.æÌÊJ{\/Ãtråìl¼o£ÑÞlØYÎy¡öƒ›8%åu¼ýðÀ ¦°0!Á<´!¬Žè}lÈó±bÎß=±‰8; Ü[+`&­¨„. ‚‚"*,,¦Ò²2:P.Q3‚Dx¸7Ø»4˜Î2ë(w?5šþ7›-¹Ô(îÒLóŒÆ^ÆÞŸ_@6WÐe°c…XÊœ’KM4ìždj‘AùÐUž5'ŸH4â¾Ø¿•°Ñ¯˜^¶´9â6Ÿµì¥‡îLÖÊ|ôY¡;òÚÒŒ÷òiû.÷PãΫK„u’Y]…%Éí“"¨;T.š6M‚.uSJJJ€ÄH„TÛP$Cs.½áAV Y°w?ñ–QûkE² ã;Ç^vÌÊN‡¯ƒ,-ç »ÆKÂÁ®öš]~Ë·¶ëÒí¶Ô”DºíŠóĺê,ƒÖ X'™Õ-X’¼mÃú…K¿˜¿7Ífàx`d3C°ôE¬ŠÃ¼8× ,Qá¸ì‚Y­Pæã7ìe€ô©)S¢àn†Ž“8ž$å U’ßÅ  {ÙFñmŠêªë0M{Ø2~Ñt˘ÝzI–hOÏî/ˆBÖ¿œÒ= )žÈÎþð¿iiõr|ÎS²¯H蓪 îƒzÌ;9ΜÇpý2çk Ê™%6!Ý I±´Çí5*™sTrL‡+é—Pì2.ËA“v:·¾…¾ñz™+±‘ÿVy%\ +•ç¡)~éþ• áO0>C¨ˆÝM¤¨(á 8è9Er ‹ego œây#Áhx–ã8žÿ(HׯâTz~3ØãbNyö\¼Cüó ¡öÎMº¡¿ñ®ƒGDV¼ý®¬Ô±ç™^Ã5r€UX%À7§QQ‘MvØÕ´ÛáaLrR²l%Ùˆ÷Ð9#ÿ€^fS2ÈÍ À?1–ÐÕ-,Y^J|èu’ßúðú!À³àGUXüK)-ƒMe…°w<áõéeWĸ%x¼¬¡?V˨=0÷}…,¾Ïè½´WÄ6…“Ñ<òMËó†fŒCмƒ@{OÌåjoÄÏ|ôŒìŒå¸\©á½Š¤Ü‚ødÎçâÌൠüÁ :9+3+*…>¸üROç3\Q߈«a’f*_KÏž‹qf¢PΑ²bài‚Mó§»ìëârÐö<44Åu?.§9òÖ 4¿¾jÅÌUÐã//O-þsëvAŸÁ"){lƒ²óÔ8ãX¶#\5ix #Û`жa¹ùuHÂ'Ã]÷õžÂ þb–. íG‘ÿ(V©ÙóKß…$>Þ)8CÅÂ1#+ æWÛå¡é™ÿ‡ÄkÐæ U4d껃ÙÃ@ß›¨t6ú”ƒƒ¥áššx2ßýŠ@§‚ê5èÛ?™ïfˆž Ty ôËŸáÙù ¯}?ètÀË|´—§ˆÚŒ]OÖÎ5=+½°6QÊÈ:]0 —¨NÒž«žg°J‰‰”ŠFá^^òq‡Pû¾@JÍ’jWÏ×`Â4Wa ¬3ÅÏÙZi§¥ëþ¡+ûõ }w7¼k:@.•ؘM wöU®]Y PIQb%9¡7 ¡ƒ€ƒ¶qHÛ@ÔpÃ#К̈ÏU×=Ó|Ëh×,k B)¤úY*@ ${6ZïjÕ”!SëD3%ÄÇQ\\,ÅÄÆhüd¾2]–/P0þUT©ã•xvâžµˆW}G¡Ê¨–ñ$Y9µ3ÁÚ]*Û«µ ¾­q¡’Äï±ð„…Iû!çÙ‰ïÄV¬³n‰MëŽ%ɱ¿û ¬yE…‹!e‡¿üHão*8þÑñ2ƒ;F¿ÿ²tÍ?ëÖn?/—eé"ÂxþÇE™ÕÄ„!²¡Ü¢±ã%°›oö`XVi|ø@Þ¹yÓòÕß³ÂfÃ"iSæa>óÁ|eþž¼@yFfÚL0€†¦gñœ,|¥âGicÌù‘Û‚³N¹-.ws–JåR4Ôv¹R@Evâ‡ÑN¿öweû@B ³Ñ°ÖO~*’éü#càí ªp'"_ûs†_Í@ $«tPúpÆøÞ²"M‘=ɶ_1Š×íµÇ{0õ²EUå-CSÕ2i5 Í)/¬†ZƒîUµÔf/ˆHŽÇ~°ô ‡äœõ“g ~2岚É]rò„­s”/@{P% =z¼u]Ü€¯ëlà’‡ ýzQPä $_§yËÛ{h!âÅøÞE·¯€plñÛ§eŽY‡—<÷: ×W0 y yY*Ù³PþVYÿ=ù3,=+(ŸµA^‘{CÌ®´ñðé]?TƒÒÐËWÝÿÝ| Þ™ ¡gÑY»p1é݉‘ûᮡ½“V©7ZøÔŽøåÀ¡¢2úêç?ˆíˆž×£šý¬ À›ùX?™% N€dYærøe ¸,cذ©ÏIMðâW#V*’T1uð;óÛ‰ã<cø¥WŠÀ6RĸH$ÇQ0NHˆ£x€åxËà'ó•ù[•‹ãœ]'U÷§YÆðwŒ(R­jÄNó „HS °âÌËZ"6¤Ñ’Ï>ùñ;÷8ã”v»v‹ŠK2GEÅ&Ô'U$§Ý^QYVQ^Z¼gë–­Û7þ‰gãžh°ä˜1Oùàå=æ'«[0]ƒ?"µ '2©M«G©,À`"¤¶Ì$-HfŪ ß ÞREÕN \@UwYüèW’\5üûHZVIQ_ƒÆË¾z^Zi• Ô%l×kâCdUA£ÇL®´0@ëýz¾~–‘}¤ˆ×‰&Ài¼y/ǹâmôgÔtËsùЕÂ-)ÕÕë¼}6 ö9ùe@Ñ˃¯þ ̯ð>mAí-Àÿß„M“ ¹ @¼L±©§¢ŸÛ[ûOáo#,YëS˜ÓÚ7³Fmfš<ƒã³hÔØÎiÚ¶G©lm0g\€÷‚A©êO¨û ë ²ªð}¯ÓèåÎÈLŽã¨ÛuŸçx àË߀|f’S¾ N‡˜³OaŒÉ”ÄR ïP ïÂÕW<­¾  ©U$˜ÇôÏÆeN+c×·‚Sç²y8öÜôóºÍ^@ï£Kª @Ç^©$ëOÌ¢¡ša„†Ý-a±Z¢8ÌÖH×âÑž0€ùÅ|Ñ6B›àx ÒÌj+pË pÌ 91!A‹³Ú[ K“O˜Ç_ë©ÚoÃ’:×ÎØZS8>*üoìØCèiÛFî-Kd,õä ƒdí[‹³¢Ûþ^¿ÇN¤1vãr|èå=ió‹ùâæ âü.2†cI2ƒäâª8ƒç:K“QWc6ŸOÌ …1úÍ år,Ç»S<‚)1€Á]O3dX0÷Ø2†ÍYú5ÞΕ­ iŒÃŽ"ßMÂ…•ªÑË“ò®HU#šÇ.ô¬ÀÚý} ê÷O·¤­á¼<çŠÑÀŸ¼…ÿWMA’¡É¨&óæÅ™iiyžõ9. ‚[ºöùË’]u_þ…h³Ž[¹œöË‚ÊÇyÓÚ^q ¤ê·²Á¶>!Áý™ë@ÕbŬ¬ŒþG÷Ö1Hæ8Æaü"Z*"’Z0AŒú“ó¸ ÚYBmùšò7¹bÈé/üSü©ÆpüÁä3×Áæ‹— ß=!GÞÑ‹Ty¡ä,êŒäáœç5Ðp—«G$¤~TÑHž¾œ'M}gIL=ØU몜Օ>ún5íÌ97­Éîú: 6C•BÑ^Q¼‡ø ±E ¨ °%ŒÊJ›Kw`+7Pf½`'`f€Ì2óË ²n7$ñà bM'ê,IÖT/ÜÒd¶v¡ µúá?a„9P/è ¥Ÿ®¥ö‰thNÇ·USËpKœq­ÿõ:H:aƒk`®É<¹` 4æ b]Õ‚%É ˜8×KšŒú'6PèÜ $ù˜eb+–úÂRd'’aÂÜÀË(ÕÕüŠAÛMÌGÒMXJ Ï«K¹b.ipÎWø¶ä›Sâï´<‡ò~žæÔï®gA{º¤«Z»mAíB˜ïiâfxÆ„^Šê]aÔÌÌ´«ëÑU°½È)ov§Á_½P©4ÅõÝzšÇ™ut¼ÂÌáÃ.ÛD§Aò;Ê+ÚfHU¾€wx6 úΟùÛ}ç? å΋µú‚‘gl® zÔû,ìàë ÙÖ§UUy]pþ¶*ÎÂqÎÓ¤~!./xZÇ}ÓõĪs@þá³N¢ lþµˆE˜(\ª§ëç`ÏJ/ÓçšúÁmà½{/à“pÑtËè½ ÑîÉ@£oNôê ôöçË(í¡k½\¤2˜càÇA¡ëºÊlôlmÊ6Þä ,Aª,ÀÖÉ&UÖùÂbV§pI“ÍYµ’u¼£££´ƒU.8? ’O†_Vø2O0ðãÀ P¿f°ÇSÊ‘8(ó Æ’ç“Mª¬ó…A²„ƒùża ÌX„Á²®«Ì|ã|¿8 é!××#¤¢Ç_¡Tcÿ¥¹Òò|là5Ò2q²Mr€yôϾ =$›ê—ÏLè{É$þJ‘b•;û$_‹_ØÎ åYq96Ù&IÊ]—öì:Ê·_œJµÜ',üOÀ³zRvR¼þ@½2”vNæ2ì¶uèà iòÿ}Ks® aƒ]ó9æ‰Üiq0˜ãl_9ú· ’Ù”œÃÝd å*=f]š¬«kœˆü+´ óˆÝ}Ãl˜¦VÁü‰¬’*óÆ=ȬnÁù ’¹N8„9p"r JXÖ÷²^]~­ë¸_G¾è Ágœƒ?Éf8üåùGÉüà óEʬV¡O$,3hækNg Í Y¯‹h타վê±_ƒU†½}¤ÈØœŽÇ1°ï0š„›¹çöŠæ?)ÈÊV\n2ňcò|»*ì€”Ó pû$[_f™ÐV/—«.‡}6ᆲú½ÎYu~ŠøE‚Þ*?¨÷¡“ûã°Œ¬IŠ*χxÊ„oÊWñ‰æÿ–—Øî@þáÆô¥Dˆjz´è“ŠþÐ`œq¸‚Óª ™”=–i»ÀŒDˆ•¡80qWBÄ->c¯gË*LÜì=„¯œZ")â`9¿ä@‰æ£ùÉééÏÁ2DæX ¯@Íã‹©cÇþ7 ó¿VÉúÆÊM9˜Fd¸[ ¶ÛaÙ{ vÝâMÛÎÂù÷!:u ùÌ ã œü±$Ù$ëºÌü8¾Ìý`¾ðÁ`؃y¤Ÿ9Îyõ’$£¾ŽIÆÖÅáˆ~CÎüò³Š…gþ£ãÆ5tÙ„ªÊ`ƒëÑ•Ï[==ßù+çIG³ÌˆŽ‰·ª ÝiЦ<2ÕØêõúÌö˜ "¶'4uŒ)ð¼Ç`í<;yrL©5^­Ï=øóxп“»\ªçÄ@ýÔ,l೿g_›giz“W-O³áòz…PûQ¯FŽbåcÕሉ0{‹ ü²j«Xð? k× ’]þ8ŸÃÉ”ù^]Rb—þ6ƒe6µÇ€Y‹@7†9ìp„9Ç Îw>„[8zߦÿrߣ±jh|èRdýZÏGÖI”ù^u)±–uÀ¬_ëù\¶Þá¤ÊõæT˜@ƒsÀ(78ñ0ÁFçÀ± ”ýÝøþƒ…šù¸¨HƸƒ`4óæ=]/YOçR'PvK–!afP¬oîÓÓ«ØÖ §0PnPv†‰Õ‘gdcEXyCB¬¯äLtó4Kúê:’kŒjŒÕtPÌÀ™=MÇqú¹1Ú?Vh²4™ƒ.Uæ3ƒbýÐÓ¹Lƒ…êµÉ#&æ@ˆè0Ôøa4ò K)kö—ý¦‰pš ÓfгåÀÀùë@MäbÆ¥ÖJzá*²Ãî²oˆ‚ªÆ}מ¯•×ó¸üßpy^ôÇOùÕÄÎXªƒë›ëÉ=¯´¼RÓgõßPÛòìr–q¹'G,“ZHNÇl¨‡iVŠ<ó WœoºÛÓ{[¸|5‡Âüá}%µ{Øš’¬(vnÆqxtRÔÏUájîþ«12 ä ƒd×ÕÉ!MÖï•y¡/úu£œÃ@¹QØ& fe¥ ¥\¸L˜õá@J“xzþ¾A´eÏ:p¨ˆØ“ŸÃÉ+tÞA—œŠ˜»¼áKt¤€†Ë¸T\@›)°95­îq]žïäÈûåÔpsàDæ<ήñâ¸Óþ3v#~Çž ìX¾í£e;“#ccè(M¦†Û sàdàÀñ¤zq2<ãñêÇãS ÷9Ì“‹^K^'×­‡ï6Ì0Âs Ì0Âs ÌÀåÀ¼ ç„9æ@˜a„9æ@˜aœÄ8¡€2›v ô,Ùü˜¿<‹å'#›?ó—盈†o¹º\צÁè7TÙ”™¿v°ÙIxÄ2õˆM6þÊÍ´G3&t‡W» x¬Vm¤Ôă@Ï 6Ï8†¸%~~Ü—† ¦æ@˜a„9æÀ‰ÌBGÍî‡õ„`ÚT9–xcfVÚ«üà†¿u-¼Á½ÍìͰYe«hodO|#Ò'v…£©ØY}!°óá7ØC=+sìϾ< ßr|=,;»%Uªó`½¤l0¶ô,g—C.,»|0++íqΫM?¸¼?œ¬‹*æÊÙÙàÃhðgœt,ç:z¨ra½Œ”ض$XÇ‚'÷€_ñÌK°f6ú:ž³P&ÖÛ{±ß! ÷±B £fdŽÕ]Sëäå Ï~UA‰œ™9úo O˺]!õÃÈæ ‘ž®Áý•­kZM<ô jóŒÑð×ç@ï—õ—74#û68ÄÉÂïîÙ…<矌Æ&w¿iy´¼¶ô¹|XGÙ×Âiµá@XG¹6Ü — s ̃ǽD™¥{øß»U³“‰„¿Ãn€ŸÈÎnþÔ”)QªLsê– &ñ,¼U’ßeF›Qs-œµžn0:*—Ãê߇Œ†oYœT¡®Czó#òÒ³G!íSx¹ó´Ár?˜Þð4‚õ‘]fç8³¿ÕÁ[|û¤_ƒ‡wb±x渧ò¶†"c )ˆÏ‚ͬ̇-ãÛ‹’«j{¸Þ¾F ó©(ïTUå%FcŸU²¿wˆn¯…ÚkZØ‚w7Jƃ`Ï QÞµïßx ÷D¨¿“IÑÑb Ø ½¿›Ëdg‘_Ë#h4 cÃDÃs Ì0Â8F9pÜ/¿V™syPçï0ËËÓÉY™YQ)ôì•2\ $˜Œæ‘oZž?04cÜRä¥ ü^³<µuF»ëed­˜<ˆY‹+áÂØ?é–1LÃ#¨=`'êïÞÏè| —˜§ FáÕISõt>£Å8ÕØ`4‚õÑ`•™„RÑ(ÜwÌGHcy >ǹbœ <Íý´™]5kááŒñ‹dE/?-=ý/$ªÊb‰å< Ò)®{­6©Ãª‹ÿܺ]Ägà÷l,&.mU˜§ÆÇêža·ò^ÔMG»Äþ>Æý°ì‰pU½ýÈœ‘™öÞ#–ñ$Iþ”3P¦#žÁm˜•£î}0ß5rzÖØ¯õ¾TŸ9WÞvú––cbô¦îY‰lEAÅ˘ v=úØÜ<þy–>Mwì•?% Â\HÝ_€¤UF™ôYÙésªiMOOÏÁµ_ ÿϸ€ïIC¿kÁÞ…`yp«þƒ~?(wåÙyŠHIzš~FC/s"ž÷ä¦;r¨°ÄJÅåpƒí5§=oÙ}Oø-j!ö¤ã¢5ç,Ý;¤RÛ–MÝe‘0Âøw90"cüY²"_^´Ã·¬~·ÕMÿÝž6^ëÀeÀ¹âí5ŒßMËÃÂÉF Ç=PöåŽ(ÛÊ9ÌFÃZ§,]£ dcÌå" †6.jr[\îf]ME)>°è¨Ü 6Ö—€Zj0žåðfòõÐôì1.Ç1®Ü*07_Ä™ &âʨúR?\öýÒÖÇ7²3–¡™!h·£W£U9ÊÊË@:Æ`Hú\ÏaÉ:O–Ô^Š*Ýà?wš%A²WÀË:/몪{óÉSÛÃÍú äe‹ªj…ºÆTµLb/Gs³Llew:ÞÅÒÿ(2¿‘¬¾iuV @Ÿ0,#k¤Ô¯Àeô×åΊ7P¾ •)íã|×S’Ô‘¨³œ{×M›½t_¨üx‚ÔþY$]>¾×Ùsÿgy¼ÔZh͆üvô9‹¹éx†-¿„]U%jáPAá øøyefŒ|ýõO‚©qxò`xFvÐ÷$¤gâ»ì} –ÇlaßARäÞ_玽Ëéž¡&žeç8&ä„“%¿m¦ŸÖl¦’ŠJí—í(ƦRÄ‘þ4ŽçÛ ©ïÅø"슨£ÔK§Ä˜(ا; <§ÜY7ºúH} jx®T¨‚X)*×±Bk4Šù™òSè¹|‹E@ñphhX,ïDæ8sca–¤H-y|†Ó$%1>Zˆ4™Ž{­€ÚòËæt*Å¥jY…M?²ðí̓Píµx[ìÿ^}õièáÓܰá„ʤeµ‘õ5A^~Ã2*ÒÇD€+«Î2ɬX‰…D—M’ËNÁ`ððQƒp$À`Î+@”†Wá/ +âõ Cé‡/iOõê£Âjô¥§žªä¤iH;¼Û) ªÅ·íaÙwÐ^'šh€ož~ÉÀg³2Ó_ãk¸ª~¿íË#IŽA¶ïÅ»<¼'U¾Áðzò&Ä$E[¬…7$ÿˆçÑDãYUƒïúaéYvpm75 ZL_ªB°Ó®QÌ`ÉéHËÄU6§ã>›T~2¾Äàú`\~ šžuª@êýˆº¥ùx(é³^J_‰÷h“Ô½¶Cåç!ÿg.ï|yPÓ3å×D÷úµç» §égßôÝÉY‹Àa~1ݯnè÷_ò„ÊÆÛœ¥_¬leHË`,€¹p!Û¢…P.kÖUduªÚÍR6Y’&;òJ¨"´²X.®–%Õ@Ã?[k—R?‚‘¬cÙÚGI±ýÁ ÜîI*½!Õmb•¬·b¼üvhzæe³²2sL>úƒ§oAµâþé–´5žõ<ãPcX«_ã™B¼Š÷Âeˆó&Çß850¬kf·¡&#y:`ð'³lÜãzPe9€Z¬GÍ([A7œ£$F‰®v›D¢ƒ`c5’a³í`ISˆFÜ’q€ä?P(ç9–ju…8ãF޼™¾÷Y4Õ—Ge¿<¨á„ôŒk ¡u²žðl_²X>™#ïèï…’³¨3H¯'Ùã¦:dYVèÇ5鋟×S³r•nÚQ¾6"7·Ñè„@áÔ<QN"~§UÒÿH7]r6] s8_`«@Uz"בuÆÖ&ÜûX» %—’!ɪ ±ÃÅÙж«d†Ü˜WSL8D «¿µqZADD‘Ñœ*#Tªˆ ²š*"sq´Ú­0–:—FÒ QŠKNŒ•ßAú~ ÃØya‰síÞ| Ÿ‚0crË”ºõò>Ô¹m ~áPÅL¨Ïéø¶í=@-úµyN¾òÕ£_ýö„—XH‡7Ù5§«/ÓN ÌK¹RÎWTæ›Sâï´<îZ Ûl$/÷³„Y2°ÔOFƒ°Ï“qÓ,cvÂÚÀ»PÁ|P^Ý yn—PixÒ«kŒYûñòí‡BÈ&£hZ§«Vº¨žŒÍOB3åìníÕ{®í/D˜˜ûመDШû®1¼÷Õruíæ]“î{>]|wRƒezÖ[ºìóC Ôc7]ÛŒ&-ÿ+É!½ßv°ô,,õ“Q047ž»4WZžïp:Ga)~²Mr<¹ñz+Ã^ȾXãŒB9£½ªH!Ï*ÇÛñƒ>C’”».íÙuÔ¦MÉi°4Òž_2^0f³É¹ºp)Ô~ 2„‡(¿!ÕØ?`ýV¨J0ƒÚ…ðé¬áõ¡ïˤŠÓq‰-,‰p…<çŠ0 ƈªð'o®ƒ>ç"|—p®û1™ˆqËÔ±c±pZEúB‘Õ¡ü¤Ñ4£m¬ättp^ž+ç¼ðšcŒéf•*>p(ÒÛØØïƒÆ?kôᕃ¨¨JKz+=[ÔÁ®gšw\ø ýFðûKQQ œ © _y–Q$õ P/Ø­ÊãP;qŒ Ë<óƒñ ÙÑ7à3õ{×<ßÉ`ï‚g}ãîÜK&ñWŠó¨ÜÙœkñ‰[Â傾o{Çʵ’Yùïíûè«_þÒ@òUy~à•Þ6B? ýð¡Umüͨ[àɄƯžÍÿqµhš ©a„ÁrÝøY×ZŽ×Tª[«‚܇$¨k0fI Ï¡u(aœ+r½€\@IDAT*±á¢¼ÂQþ-¤Â§ôÝ ]º}ªÈRâÆf´Ñ_„¶*õßJ†ý_/é&œ ÉOøn<{ŽAQ¯”•µ_ þdAn‚#‘ÕÀõÍø"ñÎÉ@Î núÐTiR:–GQïòFÅÀòB§l§ái™~Å·å;ƒB yE°±ï±é ޶\Á’dÉ ¾0< ‘Ù<™Ðøµ€,»ý±g¶Ì}ã2¾ÁÛij»º…ã(·Ãí_mW¬$«ßël€äO¡B1 å6H‘?€¾êãÔwMÂÍ\ ¹f©ÿ`茗² ì'ÑË£G—tÁ‚ðä’M{&M· ÉDÃ^XÑuŸd…Å™uÊ¡öôó¹ßþ«ŠꣿòœÆ³y’*/7ˆâ@½ŒbtGRͲ“3ð‘ÄÑÐë 5ïî—-ƒ¨8\ÁiU† ö©~]ÓyÆKé_VÀöKN¥ˆõ…1ºÓ+Ã-Y6ÅIÏ@Çø–fÃ2ÉE’6.þk&74ªo’ª¼is–”8$€§wjjË3?Õ˜úx®3ç}UQ¾Á/†%?DG Ïèe0Yc Þì_ôÔÛærÐïŻ㟖ì‹? ô B}ÆÁÞ5X,q¿“èJÀwA¿ŸçþªS™ˆ¯KsÖ?Çë#³Ñø—m úšýw“YÝ‚Ar9t‘ç}ÿ5…ºK’ëR†ŸMÍnï¡UWÊìT¹½ˆNý•lÛ ëJÒ]/¢u< F²ïlZm¨åS})¦W xD•›ÓÁ™k©âOmo³»ÍÚD˜oE±}ðõJ²Œ¸‘ÌfmQ§6$ÂeCä€kb.÷Åw«/ÔÀÎÇ﵋Ã*GºV”«¿ú“qÁ89Ug.°‡”ÁëT±%Vþ­Ð¯ò൪áÿñèè{FIUZ©bëBAS}jRÎj ØFíüý>µ4`lÂÅßÃPæ;Їâ…9‰ê ¨\Ï\:6ë/Œç)òí™–g!??~°a–(Ñdz«er±$ùøéù±ÓSæ[îá"õ€@ÓN?½Ï6üZ‚Þ1X(l¬©÷ÇäƒX8ÓǪ0hæÇ ØY„nžÌ“ëéÆZ϶{é%£ëè2`ó,ëÏðk#Qõ¤ç¯M?<ëùÆýõÑ·LM×lâmé¦CQ¾@±¦zµÉçÍx#^|9ÙÔÄ\̲„'MÞˆRB¯&ú>Ï25ÅyÀ(I"ëCëeug%©¦®æ\*2¶¢$©J=A/R«s gP›g솿w²V«*ì6gÕ–þñàpDÉ6»~Xµ¯ÝB7ýV?dʉ—t¤½i‹ÉÜ6‘šÝÓ“9e´oìâº<¯:©ih´½û‡Wz].ÄØjvgO*þf[ú¡VÏOb¤‰v=úu]ȹë°ÎòüsºþÂÞtE¿0»±_CØáˆ7ãMËj‡/úíØ­|¾|Ý87>[”¨†ä2 PëÇÚYúê­CÌ_M» ºÃ°XbÃü¥ñDLýS‹¼Ûø·®öB² á0µhçsC‡¢hv%íL!%7Q³Öá?Ó”™£ÿlèöžøà蟃ÀnÂ3w_É:ÉÐÄÉA’u–_™óYËÊ2?|}Ò+¸küjÜ’åZ3¡A@h­[­¡Bcåš gŸÄÐrczõ;Ù{¬eV¹`d›ÍFV˜~{yÎÔ4×A×ÿ^¿¯4å„‹ÚÓ¶ÛçiµÉ §R‹'úÒ?×¼OŠÕImY’Û¢º6%épzç*ùi—V65ã’‹tà.; —v¤äûΠíwͧä»{!Þ›”J'Éå(óßÕT¶j%^Ñ `¼#©ü·\Ê›²’äR;Åß–¯îLE_m¡#ûPÙÊýšd;л–2ìlŠ¿ ÖV 2¡¦q¦@Em")û±›5³quUÁeÇIŸØM"¼f P Ãnç"•ºÂ%V›’ª6*‡úlÂ刊b°s»5–ySI– ˜‚âw¢£„´ÿ¦¥Õhåç_âc1#6©ï…u‹æÝzé1‰Íþ%ÞÔ©Ù7>^¬ÂÆáYÙ=A€5ê,C±Kµ uÔ²¡pñ0ŽU@÷Ê¥º½éÕïX½÷¹_,M–$ ŽC´sßA*·;¨+¬84d"sV+r(Ó@2Dj7ñ22Ę(oò ªØ˜O©ÿ¹ˆ"»6Óš5&E“!±Z]Ë™šÇhy Š—þ²‡rÆýB›‘±Y4µ3€Š¿ÞJûÒ¤ÈI”tM­¼ˆ¶cÏN¥–O÷£ÒŸ÷Pé;ޏ51 eÎIÕ@xÂeèÐÿ­?¢L]˜¥6ÚsH3µWá:PÃJ'L_¾íT›œÂõ}vªÂ¿(Â-kU¡'4mà ¹no ,yÐ…[Tzp™j8s‰Ð‰¾¿ÂªîúB›"=ƒx×ã£ÎÁö™0É ð„ªø˜|é·²õ*3V5®æ=îu”€Ÿa'9feý,à#N°4™ÍÀa#/$ÊvÚ²ç€fæŠí$7D0¥ÄP§÷“©U$Ä6Ú÷ÂlLÏ2µŒ£ÜIß’õš$9¾?$¿Wv¢[‚«K²Ž³ ¸ó@9UüåÒ#fPìÌ·j@˜(ûu?Å_Øžø·Öžsªù3ÖRÉ;µkß?¦–ñÔ&k f#•.ÛC•›ù©Ó5ózî´qç~jת™¦~QW©r:pTb :B©ô9\mÏܭн÷@‚5)áÁ8ÀfîÎߦRØÈXÔƒ¢ò„O!µ…÷×ôÿ4X#õ'ÄÀØ`2G\ßÊv’ëO2L|„Z¿ 6Ii~ ¸±‡®§ÌªìµúÔ ]‡A˜a„9p,sÀ”:”++mTTVsXBƒ¹ÜIó6R%$ÆŽ¼2756sI‡m;ªCy³Ÿ©9vù @ 1›šFQ‡×hG7qÏP¶z¿ç¥Wœ7n¾bŽ[M䔩ƒðI®ÿw˜ó³ ¸8xÔv”y5¾Âá/d_KK«£ìÔæ¦µªØw{$aW½³â±#eðoªxú~U€ÔöX]z¬ÞDŽ€”A®={ÜcûÀáP0ã¢Ìªh4¶µ(p£Äû[5™ N¡‡à£tètÂ%Ãs Ìc†.Ç"29(c#ÜR œ)4TP*TôåÊ…zEôiɳßÂF¼RíÌÖ+ô`nGR‘˼· )7o¨Óƒ¹c’õ{–­M¢üϵ’~l¹îC¯²ª£æÍÜN€ù’ï¶Aí#’";5õª_× ægqy…”™ßáX™$åó&åjÄ«C lnkˆ EŠí“J¬CÏ+©Õï ?úçÞLqXíàÀeÛcÕmÑ=Ñ)þŠ7hZÌÙ­¨Õèþ J³&büünV©c>VCõU¶Ó_S£”Ï8Ì€í‡-á–:ŒÉ<˜^ u¹ú„Ä„Á`2¦€¬ÅÀÚ• (ךǵ®PŸN7vÝç'Nôµx4Ȇ×=.ÝQ¶ÃÌÖÜ A"h©rVmúŒh >²µxd«VŽ Fy¬;ç¯$GÂ#–©Äcþj4ô†¥gg°ÅŽÆn±&zµyÆh4Ľñóã¾ÔD«¦û¬©þ¿™©It2P¶c°uB¥—a:8ö—RÁüÍÔüá³IŒ3K’¥‚ Í|ƒå&7u#cr •¯rI}+ÿtäwkiùDZ·„ÊVìkÌ&Ò¾†\±»Otô@ÀBG7ƒq¦9B\¤ ¦JêҦΧÏb킺‡üÂ2Êɯ^ «;¥úÕä±äãE¿Ò“/@O½ü!¥ãÞ¾]éR5«-eæ§A¯°˜žñ÷j{küX‚è1†§gÞ9â‹E¥Ž6°ÑËöaߘ™•ö*w÷®…Qö·­Îòf0v¾U4ÁV2œƒŒHŸØ¶–§Âó…j± ï7(Úž•9ögßD÷_ÃrKªTçA¦Ã̬ ¯‘ Î,.Ççd.žÓ³²Òçòµé‡Fß NÔGv$á””×ሥ_®Pbšžµ0!Á<ôåçžsûUž1®¯ªÊËH‰m Ÿ+c1ŠÜ£–9ã™—ª ÌF_Ç3pʤq£÷Bí' n­WÀsݨ™cWqû†§OêŒE³È*WÐG4')ROXÒ~é—&!³fÛjN¨‰žAmžq þzçï}ÒËùËš‘}¼…g9…siE Êüd46¹Û× `M÷©·q¬žY €V àÍ|lækL wè½õšrÊCgÒWWÑÞQ?PêØÔéý›HµËÄùeË÷jì*†.qÌ9­¨ã;7\TI…J7»³‡›•…_üC-Ÿþö7U/óºÑçœÔT ðnS¸u½…¿üAÉMâ)5%ø*W]é‡Z.Ï ’uqË%”šœHË×o£/–þNçv?…š&Ô^F‡OóXɺê§UÿjèÜq”1@ ÃÓ³ûÃ%Üñ¿œÒ= )žÈÎþPŒŠ*µTÌÁë÷ƒ`Çc¹ë U’ßOÎ5#Z%ÇZ#E<ªš;6ÑÌ‚3Š Èëëɳ§¦L‰ DóÇ5ÀY¡ÌÇcq­³z@GÁ¿ÐXü*=5dBí‡F?`}„# Øâ6âþ‡‘Sá­÷ß••:–â<“irïD™Å3Ç=•p¼†"#… ðP÷ 2³àüc.‡SÔöxËàÜÅœ£’c:}þ;#¼’š~!íýT†ðaâÇ¡éM§QüÀŽtpúo”t}j{âl¶0ª[3J†)ò•{©ô§Ý˜¦R¬dð$ßßä;zÐŽ¿ÐÔ•bÎlUE«%ÝÐ ¿ƒõdÀ*Š\bÓV@ÚM¾Ö_–Q$V_ÚN¼”¶ß½@+ß`cšA’Û–~CЬ uá:ÓÔÚŸ[÷ÑÏëþ¡ž[Ó÷° §@}*]pfWèßÛöÓ÷«7ÐþüBjÓ¼ ]u~OÚë5¿mÚEæ­ús;ÝvÅyÔõ—‚Î’5›¨¤¼’º¶k¡•=%5™jjÃ1jÞâ5´~ë^ÞPGçœv Ý|é9XÙ“è“ï×Ð[öhf%žs]~ÞéGÜì}«…Ý;¤jÒr›ci‚6a¬Ë ™æw­Âq”yW#îøAý®áqn:9+3+*…>‚½Ÿ5Ád4dðC3Æ!E^ à×þ5ËS»Qg´»^FÖZ¼¨ƒõkýl-®„G4ÿ4¦[ÆìÖ˹Îj¼¯0xÇ»ñŒž§ùŒ¬Ó£p‰ê¤©z:ŸÑÖP«±ÁhÔÐGH°YŠí ÂkpàJʼŸã\1³¸§¹\I³DV gŒ_$+Òx”øiéé!;\ÒÈy`ü×½jÏ@Ë`Õ‡ÅnÝOzÏ($Å´­ ܋σ«ð±ºÃôá^ÔMGÌÃÓ÷1¦è‡mdO_„~`GrÚ{ì• 6p¿å ”éˆgp&å¨{Ü´Žœž5Ö×AΕ·Ýƒ¾eà`@}RýÜ1¡/Ãã`×ã¡ÍÍãŸgg'CÓÇ]QÝS¢ Ì…ÔäHÔá2[HŸ•>‡ëêazzzâ~y0ü?ã¾' ý®{‚åÍx)íý^P.«yŠHGˆ‚ÝgÕïM'sLžqošD™±1[¾Px³Ù¿ÐS¶©(è Ùo>÷» ${æËE6ÏËãõq]]c#¯µÉ¶ÆsøÂÁ?X +›:ÔÕ¯¦²îÔCsþ$û¾RjñÈ¹Ô €t_Æ\—P칩tøÓÕ¹‰f×;þâö^@™%µl¹…Átņ|M’ÑÂ¥X¶:G{›Þr:ÆÊ›;ä7 9–QTÐ,²CbÝ‘ò^[¥åq›¹“VhqgA¥¶ •Õ.>…*·@â\¤e­@­"€ø’vèIšî´3¯œÌP_‚‹"ZÅkà¹!¼_în&À›£ÅÜ þ{þÁhÝ Ñ ¶ô³igUاçÒßÛ÷ÑÇßÿ ‰lŠ0éÃïVÑÙÝÚÓM—œMÛ÷åCJGØDH?­ÝL=:µ¡¾=;RËf.MÕ&ñ1tãÅg£L }ÕŽ…¿¬§Çn»L³&¨H³ àu´fãNº€791–LFÔ\öû6ô'‡î»ö*±VМ¯WPï®m)9)þˆ[gð¾ï`!­úkÁÔ[%Ý¢˜¯:Hvói!Ž{ ìË]Q¶„ÿa‡ÙhX딥kðêÙ$s¹Hƒa‡MÁGÓ)ó†Ý¬«©(ÅçfÞ 6Ö—€Zj0žåð4ð94={Œç3¨wsY€8øMòþ=„ÔׄÀ/Pû8büø$É*õFëŸêýÎQV^Ò1CÒçzÚKÖy²¤öRTé~ÿ¹Ó,i ’½Þ°À«ü'À”öøÔÏ@^¶¨ªV…hªZ&­9Y&¶²;ï´Ô(ì¾ÿ >Wß´:+† OÀNäñ¯ÀCÜ×åΊ7P¾ •)íã|×S‚kmÔYˆ¾¿kЦÍ^q_À ªª>©ý³Hº}|}¤åõ¹ÿ³<^j-´fc<º}Îbî+‚šaË/a‰ÂhQUbR/Ò¾ùz^á@™Ðëþ$˜×@O ÏÈúž„ôŒC|ׂ½OÁò˜M#,ã;HŠÜRç«ñÜA¾ËéÁ‚ç}+w,å¹Á2n¥| ÏæªÎ놧~bQ¬ Š|Gì-¯±BéÒ]š4·µåbJz6å¾²\3 ˆÕT­IëÚ’ ƒO¸ØÄato—´—ís³$—wÛI—SÑ7[áÔf $ÍÑä<äÒÞ“`CœA´þY«Ü‚…«ªÀVWì;‹ˆM$Æì/‘[õ,÷Ù©5ƒmÏÀ’l|—]IPù88mæ„dzL]â{°õ»$‹ mº¤wç×µv]5lþ$ñÑ áú‹zS·SZQH€Wýµ]3‘Ù«K­=yÔ¯Wgº´Ïiî6ÐYo@ìé°ggWùX ê‰òWËþðKµñë†tV·SèjH«=Àv–t7o§-š%Ðïÿì!O ²^þƒoWÑ~åf‰qtÍí§£gÕåÌRdϵæõ ”IËj#)êkXZzù ˨\H®Üº¸’Y±j–ô .)š$—¿Ïì¢À¹ƒÁœW€„1( ¯Â!^@OÔk¤ ¥¾¤=i„ÒG4)À~älÐÉieìú–›žÂjô¥§žªä¤iH;¼Û) ªÅ]¶*2,#ûÚëDÓÿ³wðQ”éû-ɦwH£÷*R- €ŠìŠz§w¶ËYÎÿyÀ‹‚í¬§ž xç½ņ€Ò{ =…^w³eþÏ;›ÙlBRvCÊûå7™™o¾y¿ožowö™wÞBjÓ÷ñ0ðÅ‚9³^â}Øß R6›ï8•S¡t:„Ÿ×ÏØÞR5(ß(.ºÇž‰ N)Ë/¿$ù'ÌÇ2˜F¥¤à(Ñfå·µ Ô+°Ï_Vχݛ¬+ÃlÖœÞòÌj«½òF«£t"š/yþHÿ‡óæÎ|χ­ö@…Ô›°éÑæcRf-x|Ö*|ŽÖâ÷!ë±ÒÓp|9·¯]jcp¢9hÌŸHFí1èûÞŸ½N_×>­ýõn´æ8¤ÙI¼paM0›\ä,Ø ™gD_K>”¢¥ÈÒ+’ò¿NÕDM1ÁÐÔøYÓÚñ?¶³?·7YúDÓ¡¥û=õú'Ú ŸÜY(‘!Ô zÌ6ökšð¢e4-8Ëg'ÙæÆ|CO…6wWT¤ÏÞ¤ÓeÓ§Ow6WžÎìù¶ôHÀSJhˆûgƒ¦™ËŒ+'Ñ{ß®¢Ç| “ˆÞôû‹N×L.´ƒµþ±ƒà†]5eƒ¡,Ù×ÃÛߣ®>8ji¹ºÇG׿„Â"©¥ÓäÐú<ÇŽæ{¶½7fÝz •WØ4“W>ü‘îýÝø{·iÂ6ã«/M8ÍÝ”Yv‡(ІãcÀ¯ãW%gÎÖ.ʠ䃸ß!¡B)uj–à.ÕPÈÇÙ1 Ä+Ød6õá}»Ý¹ê¸h'Áçµ´4j uÒˆ1‚$?ƒÛØiŠÉ|QJÊôJ§EûPé2û¸}m-Yw¡þŸ]K¾ú…Ãl¢±% !¦S¿§Sìµnó >·àË]ï‡Õ_^G]ï]¯ÈŸöSȈxhºA¸`Ï_»ä¾K³¡ïÿÉ5ÔïÝ«È ³¶if‚Ýë•‹¨ÿ'ÓI‹ÿÝ *Ya&Z:H¡ÿNP\[»“"^Ièf•yYí¡t¨}6³¨«ôD‚ Gn¾„n¾ô,Ú²÷0}³rK]Íè`V.-A´‰ß]xš–ª~융â5uõa2aâa¤c5 °Ñ` ÖZO=ˆ^ø¿ë<˦Ygÿ\HN ™ŒìÞŸYo»†T+ÕnWÿÑA”SRÞ²ØÅ‹ ©ÌÝéï«´Ð:ºàÍ«…_÷3£Ò—×&£r˜×zy=åá} ‹ÿA‹;êüí½ž×•á}Ns·GC2O4FØ¢>Rz³Ñd8~ÊC‡tYÅÅ•—â3oU“â–èuúúÅ”ûóMã€j²F^#ÊœI 8+Ο3³¹ÖÏóZ×ùø,{ã%Z_Ì­=úòÆ‘,8¢@C%êâÄ‘ j—ÚõÝ€eq¿O—Ói×.¬™‹¾t ®FPÐÀØÚ‡[´ïÜ[4 6v2¿ùRMÊ3cTeœï·ÿöEZx·=—~@‡þÑcQ±¶žüHi¿ÿ”R¯øP‹ÿͽ§NÿÈuÛO¬ ì—×7(¶ N½ê#Jÿãç”ö‡ÏµìŽÜÈ‘[NYÏýª%²I½öO¤ –sôÕšr8Æ®óÞ¦Œ9+<ò §Ò¾[¿Òö9\á‘”e´çÊi7ÂÓÙ8Y44Çþ³I“ŸvÓ—´}4Þ¢ÔÓ íïB´=Ên\*Y¡üé™eO½²)¡R«¥ùeË‹vúE~ ¡ìdÇN{¥ˆ+?¼_wbä@8ûq‰‰ ™Çw!5V3¿ÈýS…6y…%´ æZiÄc›|l„&zÈ-‡œÛ§>.l¼fG:í9_S—v¬6¡.†ãàZ˜n!F;›|,Yµ6ÑJ†ã¡K“p7ù°ã“"JsFs¬üo’û `ÞMƒ6 ¯úɤ˜óºšÆ.Cø··?ˆWñÏZ•÷ƒømf²rÛ£OLRœ°‚ 3mì‰,IfRè QÒ8¤šÃáºþÜáܹ3®^¬´å=¥˜ŒorȹæÐØq4ôš(É4¾Þ1"tÞ-xYò4Há}N;…Ö$4‹«@Ì~kþdÁŒv®»3åñ.{å9®`ei€Ý¤dÙ½w§ƒªlaç:»Ýñä,á\¯Ë 1ìù×#xˆ*Ëi¨à¡ò+—S½öÈ÷™ŒQ0) uØ+û‚4¯Ìtf<†ß’ÈSÈ 2Gù{•.Çàx_;´¿»q“Ço‚‚*Ô<øà*ï~f`Î.Ê· é—ï…¼;Â;·Ëq틼Ïq9Ô{aCþ(¢}ܲÝhЍ¾³£aCÄUž^ï4vŽú¬y&ú,x_OímhˆÿHfò²¨Ô>Oãž·”ÛyË_¶3½WSçú‰y°”÷[y–Xõô;ë÷_‚ùYñ~¢y%x4SÌ7ã•FÏÆ ¨Õ*ö÷Ã5ç¥ÒU‡)fú =-Y#Aƒc©Ç @ó•†ÅÝÉ‚øÇs–SÜõ§P^KÛ`«Éq”9©Ã‘”ŸkH¹zˆº­à›4JüÛx-ÊÅqa½@°“q€“3ä"êEù¼VGaâ]W}Ò쉫…Ly;—ø×3)÷½­”ïÅÀˆp]ÝŸ:š½Ã°[5jZºô[’í@óc§VìÎ¥=ÿûBøX™éËPã:O´s\@£ïûxƉÚv„ãÎ\ôùh…– WèâM*%çûøªØ}½FÙ é †Qß¹Z4í×ãq¸>§Óº´Ð§5ŒOU|!lœá+–©„ަK ƒÕK Êöп¯C£ö«Ä‰HNVqáÇÔêrP©ÓFß³ ÉÏÅ ­o~ÙBo}µvFØ/w¡‰UÑ0Î5>Xò›¿˜µ¼ìüÇvÉÍû’‚-téÄ‘ô>ì†S¦OGÿþb9ýóýïµæ§ ëC§ôï®E¾xgñ¯ôêùÁ:ro¿jr g>Æ„ ßY¼Z‹ÑÞ ðË'ÖúÓw=m˜{/õ4«»ºÝeá¸/ dþœª{Vp­ˆ‘üIJʤé )×bç=ثރ'út“Y¹Š¡Iއvôï ý , ûÙ`DŒå‡*éBå¾¥;þã”é9õɰå—wŹ÷)NW*D6‹(7vC³º ®ÓQßq¸ ŸG~×÷²÷#9¢ƒÄ‘£b ^‡LÖ¥ºL•FÅ»Ünì ­k dxv½€dÁ–WÅõrd‹»ØË\Ó±…ÇýÆ•yÏZY3ñ]xÜî*`{a~[ŸŸ‘2×ê²ÓÿÁÆø¬ÍFd’;\ÇŽ·î½mž…v÷5¸?¿fµ!Ç<½Õ¸Ý­’LI÷dÚ3Þ…õ7àQø¸ü¬üŸ.Gv³ÙƒÏ+ ó¶Ûæv°ïÅg§n Rž˜ôI}sÐØ9n賆0žÏ$†RïgA¿žzÖãU»ë²;»²ý9ÛlšLs[où®®M=×õŒ¡ÝUç~¸rßߪ}}Ê·eS÷§¶ š³È úkžúYÏÿª%é÷Þ•”ýÊ-n²~‘•ptŠ¿k¬¾ëYs|XÖà~—g§|â„"µ‰rÔ…}©bO.îkl¦_]ê«g­¡^ŒH{4´ ‘QæHûn[¨7¡°Ó“)xH—e0ÙhFLå% ÈŸ"úb”BÃÁ.7:]5h<,ó2( ww”ìHÄMVè7\îGŒ ·©Ä)®Ûsyâ®+=Ãç¼è…ÍÞ˜y£¾KŸq™¦jÙîX/# íe2[aµi&\ÿõhÚeÇE1w¢>8ÞñßnœŠsš}3b.¡ˆ®Áñ‘9gÝ ²àgH;äùBÞ8M#Ò¬ 8ù4µÖ=c=©‹Ÿ©ÖNM}°åö%|1œDAOæ}ql§Z6ÚÆdÓ»‘¢™°y×Õ%ã®'ŸŒiŠFÕ[ž÷vSÆá}^ííºÆX»Í‰ö9Ä۲ǂjÅ×”ãxšTîxì¹8st`‘·ùCC2RRTC½Y{^:§ö1~ `r8 n{h÷Q<]‡¸ï'™fR)‘¢ºwíó³_ß4eŽë’Q×g²1ã©Ý¦.Ùܦ©òý«E®=êÆï×§QîQÖ“®Xïþ±l¼´š-ãþ8BKÜpð¾o©ÇsçSÙ†LÊý`›ÖhР¨çWÝÁ ©œ/l|¼êÞ­boIƒú#í¿{1UìÌ¥ !qÔóÅ i×”·½›x¶»?užF˜½ã-óÁºêÃÏîAýb‘ñ/‘2_XMVmïÂÿÂ'ôÔHt@b˜¦é®+÷9õm³f´ÜYISÖX›­Q®OvG¯w‚Ÿ¬é£Ð†°yîsL¥þxaï_fëëè8¶äú2@˜Y³Ï)×®QÉt\ûjh”ÇY(vdwºÿ~Ï"żðÎÚ²sï®wÿù{!_ÉñÛïb,ühÔèï4D HêD@'Ê–®–Æ’ö:u²Jo¢ì¿ók*ýíú=í¸ÓØ +ãñåÈŽö¥6ÞD2ròøWÊ#© hQ»æPÚSME @Ú¤ ëäá䯞ãñUNDj*ÅESüÕ‡Èmÿœ|ãö¡\A;G`ÁÜGØp³Úx³_ ß·pĉäG'ÒØ%Ûi±e]V;qò†è+SáÂ=1¹·Fœm ÜÄÒÝV[;Öç:&Ʀ.Á˜Ne›²©çF]Üå<Šš6À£ùåHì8e;è&ã¾h©»GhYÏ”@ôˆ¤JŒ•‹.ÓÉ¡¹àdÀŸÉy@×PjQ[6c?4ä,±ªÝñ/y¸(Ȧ:GPip¦jl‰Í²9!LË”Wòë!íÛ€”è1— $ÎÞw²Jä…ýˆmó÷ßñu½CàÈ-¡ã’µ·,å[ù{‚g‡Z6úõžÜÌ¡Vdé_næérZ'@@ˆr'˜d¹DA@h>]gŒ!ctõxþ|Cý@…KÒˆ“<ô}ÿ*R‘à!ã镚éD4"Z„MF¤ ƒF¬³_þM‹‰pp‘Sû#L×g”3ƒbÀׇÇbM5Ž…kÏ)¥¬gkuñô[߆bR(é‘ ÄYÎz¶ÏÇè(úà¢Ë,\¼mÎ"#¢f°ƒ ›e NÊÉAàö™sï²;Þ„m¬ùÌ4•†åJM¿Íf ŒcÇÝt*±½zúŸðü7&å¸O=!QæŒ~l²Ó’H(õ !XI15ü;ivÑ[ê“Ý’úìø–yÅýo‰¬Ö8—äV!­saq9 ì•@{&À!Îý ‚êqÖ½ÞÉ]¯§6n¿=ýÉΣ>Ýã‘´#Ö_5çæ–oÜM%Í6rPê™XwüBvÀ[¾a"bôB¿PÏåÖUÏáÝ6ï9D¥H2¤wdÆzÚ{odç!É~dßá©ÖÂÑmIÓ®küˆþŽ„6(™Ç 4™AH“ÍYcµ¯5ŠåÖ@Yúv‹Àþ;Õ;öó—j¡ÖT[µ_HæS¿hí9›w=;ýéáÚìÙ¥”Žø°¬Aó½uxÖRØ×dK‡þ¡Îþ½ë¹Ÿ}LŠ YæßLo™ Çi×vÜxë.•~E±íÿÏ©ºžë–O®)ÛUCˆÛbÇg}r$CpÅ^$!ºM½…óÛ¨‹’6ë…ßì¥òmG5moøØÕ‡PEjUÂĈC æ}²ƒð&„ãsóg˜D£¦Àƒ ¢}ž-b)ò¢~¤Vº4ÇÕÊÃî7 ÷ŸAœL$æÚ!dÝ…è-^Ÿkî¿ëícúz‹Ç”‰Çw¢È-Þ×à‹í} S̼QùÒòZCƼÏ~& "A$ÆEÑ®¤KÎAg‚Lþ¸f-þe RS÷¥·¿þ•Ê­•HÖÑŸþýår¤E†¾ zwñ*ºàÌa4¡à¼Ë›_,#&¬ýA¤Ÿç;z覩”Ô%Ê» e+$îûh~1ˆxœ‡(×UÏõþõÑO4)³j”žýß7Ä™öâ"kÈäôÕï}³ŠÊÐ^'Ê3ù´Oî­E¾àôÛÝq9í;rŒ¾Z¶‘NÐÒçÐw«wGûðŽÜQC¸wj>VøP°ˆA 3 àM†½¯÷¸zøé¹Êkõ&É|®jƒ ~K^5ã\oBR—ÌãÆå=hÙö;3}â<İn`¶¢^¶Ñ÷$Y»<-eýsµ«Û ›šÝ>o^¦ÅÛ.YqáÏ#vò´î…¹´Š~ÜGÅK÷“ŸÕhÄ çÂŽ©ÁÈî§'ljºt¹@Œ-}£©'b‰3©væ–QŸ_J&¼}ÑÎ9­u{b2Ùa¯¥ÑÖjÑRh÷|aÇПnï_uH[±&<îÖQè/Îã(ë}ÜWÛVD<[>Ðà@¶Ä- ÆGÞõ•\˹súdº i¨§5B#›@6¹ü²i]sþ8ºú¼±tÍ”±´ ±ˆ¹Ü9ýâ¸Æ9œ8NïÂI=¶"Cß}¿?Ÿ®Ÿz4Ñ=håæãß4}‹,}—OEhs½K]õœQï‘[.ÖÈ/Ç@îNé5£š2!^¶n]wAM_ Îh6™èžëÎÓÒV³{ó¶½ZLçþxM9m¨vNaIå—zÅoÛ¢Qö´"XA@¨‰âî?YNÎsv¸ŒÕPk¶ié;—–®>B%k34§NŽï­—d“Dr.bSˆ€¤0͆>bbOÏÛëÞ<Ͷ¹2³˜ÌÑÁš6™“ìä}º"¦ô¥šï~K‘“_‚l ·µTØqQÉÿE4ÊþÇXzVF€möª—Vu§ãÜÉ.»Y—ËqÜa¤3j@–ê{ä "ûÕß(äÔ-¹ŒÞÖ;v$çÒw©àË]ˆÓ}|rUW™]ÓsXD9˜ÿÕÍ>ü¬H’S ¥±H %ÍI´Jš³Ñ\ìF/V˜px ä0Y²2_W9Qä–ºÎiJ]9r_|9Rqå„ÁzÛ ^öúœ‡ÝÆüMâÛ¶<XšöØ´f0¯` +§ƒæÅ\eÎdÚ‘HB«4)£9mt€Ù„D5ßj±f×ìE¾ù\®kiá4Ø;Ó34r@å°onLaS Þlè…¯Ik9-v^Q±™5_c#KÆŒ³¶4ò4w³êÑ4é4i,‚@ÛD@wlñßT+åý™7$-Þf< ‰ÒK]¸ëÇdíF€CP„ÜQ€ÿ­P9åtì­š#ŸÞ]ɪCd€ý<Û!ç}º“ò¿N%+lšÙÁÓUVI¦¨j¢[ÆZcØ#—oËÑÌ‚J7 ÒË僡©>¢‰«@}è¨-¢K@b8C[ÍæõÖVï¿ûÍö´ëcj6ãÈ-½Üv±µ#·ÔlØü=Ž›üþéçÑpÅ­úÕÍ÷}ó¥ùäL7“…(§ê,·Úížý†¤ÿÛvb…{7E#¿ìœÇæ%åVí´RÄ‘ç}Ø^kû×¼3å é“D~÷[ ѬíµVVjÙòø@Î ¬Ñ¦9;aÁtËågÓì?]JV¼øá·b¡ÒªëàJËmì;.rÊë‡ožF«·íÓ0hH¨µÒ®:N.R»4 gý$!Ê:²‹€„®¬å÷þ‹Os.¬ ñ~ƒ«RÓ6çüÎxgüuù~W¢â,µ´L†í9Èá\UXSœùÜ*$žKý?½†úxµf^Áz¶<˜L$?6‰z½>Mkͦ}¢43 ®([›©í3æ’ÿÕn*Eô”¾o]¨0S(޵µÈZCï äG[F‘ç÷õØ<óa=rË€/¯£þŸL'âŒç¼µÙûÌfoÛñ<·²’‹ŒQ\fõ Œ÷ø¬¯š-Ð'ºìΜ¢²8&É¿nNaœ¤aŽ8ÁeH¯DZ¹)U#–l¯<°g¼¦eæH\ìeç¬ÁíP·-í±Ó]"ì˜Ôþ ›æ|˜a°Íó€ Ú9zm§ ÿ8âE1"hpa¢Ër»Â„‚ǰi÷!ŽRYoÜ;™ÂL„M6Ò`ZÂQ<€ Í+†fÙ­Af +®[75©OXaq™j·ÙÜÔרõ¢ciHÒDÚºI¯-x}—…W®ìÀãÏÁ¡ gвÔHYÍZ¶0Øn:@VøõµZu³÷F4¡ã8z@ÅŽ£T¼ì€÷!m»±ñe ! G«¢Ÿö“ý¨ÿ]Çò•¡éMòqÓÕ`…Élü "^üöÙh%âÊõª!Ô­lðœ¦ä°ÝÄSðú³@z—¢Ò‰Bj¦U¯è½¹‘r9Ör•i“ÞßòœZ¸aɰx OÖ³¿Rö‹«5´÷KíÔéyšñFÁâTmámι{껼é) Enñ4j↠O%©ñL’gy [eÓ›Á!¾”ò—Â&Šòws&t™%6¥ÜjÃhÝOõL4?ÿi½6–Ù¯}îÓË».ƒ£Gšxà¥A ÃáÄw.´Ä*½µð*@(9Øiï¤Xº|ÒhM£¿àóŸéú‹Î ±CûЦž‰è+è‹¥hpŸDDÎè\OgØ`Óˆÿ¢_޼ÁÚn&íãGô£ÃG 9c)=}÷tOÈ·ÚâºÂñÿùþw3єӇj>Ø9qÉêmY¶ÐEãO¡ä®î7µeð>ãÈxV”—±4Ss}áÃM*n½|“NñãÅÏTÛêL}ßTI¶†@[Maí‚­ž•o’¥e”—W@99Ç(á„Ö!‰Ç”íD³ø~éã‚»Ç+޽f(ì:ÑÁû¾Õ:K=_žJ…ߦQÈÈxªØKs–×è<q—ãnAߤá5w?:úú:*X´§F›nOž rQªE*àˆ¹ïmõ8_é {FQ·Ç'Q@r8Òl/¡ò-Ç;äèm[ºÞ Ð÷C‰®<‚õI¦°Ð²àÇÝÀ6.M(5…õŸf?5Fq:6;UËY©ªqpF@“¦'D€5È;‘Bdc/ƒ£$PE”qÚDãóç¯1$™Ûñxk™ëk‘ëê*Œ£Ëé,INÃq¶ÙàŃw]çÔWWýèP_ ©A !ÀšM#2/úÈ„¸œ]CMtÐå  ˜è%ùøÅ+kŠÏúIÓ({CFe2µ*~ÝÌS’«¢°ú};·±vÈv¨áêÐHÞ²8¾l`¿X-¾læ «½iÛsWhëe?;îÄ&V0~¼`TXÆMÕ$7±ËÙüÕ¹³÷ãÂ&ß>û‰sÜ3¿®ô ±œC3TcŸ•bYïÕŠ…ÍiÌ ¾Èè‹a!øB]¦RÞ­Ç‘,„rÂʈ&×Ñ0¨ä¥T)O5(Môî)³Öúb,~–ÁšMÈåfe~°]Qþ¼÷P6¾ÁfDJ³`ü¶§Q¥íý8„/:Yn´&Yï\ˆ²Ž„¬A C  k7Ý$ÙDXÌ” Ûá£%¥´lBÓרdnòí²éð(€ðýÙ]8{™!Ðë¶Ëa°p=^+P!έn'ê>ÜVˆ/«÷UßÚŽá2~ÁxøÞÑðÂ8ë˜×wžÔ×ÀsfþšÄÿÝ>û© +]÷­é­L^Ó™ÆmŠ„Ù˜T R<ž£BmõËhôOÇˉô®53R¥ÄBE½b½‹ÉæI-l6ñã·zݨ’+È® Ò™ª¸àoàDL+ð%q´²+Ša½ª¨ËaXûÓ¹§ øyúôéÕ_:½aÛ\ëN¯cÅWŸpÅŒ»®û`ÉoQÝt±¡.lÛ¼Œ¶3*6÷~.—ÃQüë7 9ª {;ò¢“eóFÚëŽÝès¤¡ m‹Äñ@™ Â.d96´Ý"Ê)]uÑCºp«ÿÙ€#¿‚ ðÞçÂqc 0qz1h˜Åˆ=[ÕÆ¨í×&É|>Ç—åû»aZ‰ô?~¡í·æ?Æ-?”hBÿ® G>Æ—qÖÃPµæX:R_xÐàã7¼Ü–ò\¬Ái›³Ë¶v£ó·tS´Pp¥âˆ/r™"À"x ÇÂWÖ¼êù8;ñpÃ_\ƒÊ, å…¨šöh9SXã8wžvÖdDª7-F4iWÝš\cnƒV{c¢ =<9*+}„2…@çÁ“Axàµdƒâ8 ‘ÒÓ‘5%=8* õÅûïî²@ßh?k~JfW™ŸŸS²gó†(#Ç<õö¢•ê­Wœí~Zh?×rÒGʸe"ÝöÆ_—½]^ÍßpyÑɲ[+Ñ„‘ QnXÒTÚºé9K` Y‚,„¥Kx•U–ÛƒÒp…ÎÝî_ÍrÙzĽb0.ÜC“{gÛÁ-îl`R8•mʦR´‰º¸?²¡åQÔ´ÄQ ¸ Œ%Nqm;RLÝ#ȶ¿€jÇ—õ´£¢? k’™$ïíJ48)‚z&Dk¸2¾¬QÓ ß¡??å¯ì¥ÿ_^n›7ÏlÈ:vŠêPÇ•ªc÷Å)c”8êNì~úòêÖäRœp„bUQ]UŠ› 9ŒªGûênnp¢Å^˜'¬Å´®7šLß¾–òp»möÜé]éŃ1Š24Ãeˆ¢¿M@X«}(F9Jû»@s¬@/¬^³t {à•{îñzªôºØŽµÉÄ5ÊL䬿|»peXTÔ°}+}Nê.¯ˆfhœ °&™Iòú]û•ô[¿Ø´rù&œÂP¼°­ãË8 QRA “#À¦LàÌf³FCB‚ɆH6›’vR] }F*@ÖÖ³wùÞfY‡¿pI…ŽI¢¾ï_Ej…2ž^©™TD"\äÔþ”öûÏ(gþ-­ï€/®£JâCpÜãÂQ4ì9¥š£_Ò#´¬h°À¤ŠÇà´·¡F›¬gWiûþøÇ6ÉlnÁšäþ]ChXÏ.Äxòƒã«›^ø£ïÎ.sþŒœFm}Õò/ÆfÊÝý#¡Ònï ¢Û¼8šT%& áN£!œi&4­ø«vPe;´¯EpÌT †L—jÌ$cTúü”å,«v™?gÖ«3Rž\jWœoì¡\í®1& É.#æh³#Ë ±©š»öùõí³Mq’¬”ª°+V4Ûb|žÔìʼn¸ÆUZmåLŽ>†IÔ«ÿš;ó`}²:`=¿ `Çsͤ®ì›÷ÿ÷Á×ÞÀ¸ü!+¯@½öüÓ b³ 4ê)l“Ìæ¬INß±õ«¥_~²M9x8k”y͸2¾Œs=ï^p¤žÒ&Õú®žÙ’jA  !ÐVÃÃé9NªDÕ¦tgq@IDAT²òr***¡¼ü*((ÄRDÅ%%t¬ØJÙåFÜ=ê A =뙫Öé<¤ËlîZA&4Žë)¸ó‚Ì5"Tp¬dÖ ë…µÇªÊ˜gh¶ÌàÿFǵÑOnáš»Ä*Z”vÜ #J}‡5**’b¢£(""ŒB‚ƒ5ó &ËÍ)5<\s°jísØ„ìÖ© âÁÁït§BݼÇ`r*Î@‡ªàcmvÀJX#èn‚¶ªÝŒŸ›J“¢8 5µÚøƒÀÓ>Púuù›Ñdøåõ”™5CÆxwÖñ·ù Ä1ÓðèNìÍ‹oE™tÞYÃÇžq‡ÁdŠÚ7Y3¤·2¬oR½q–qN§)'y[Zqt vÜs9ìEëWüüÁ–Õ¿lÅX °äcÁÝŠ°0aÖµÊØl|Ñžäß\Z ‚€ àwø‰ß{iV‡lÀÎflŸÌP»ÝŽüNâ8Ë\øxX€•rÊ”cÄ«_(âP„Üi¡šÆŒ÷|U¸ÏÚò˜{×1‘®½Ïýëuº"Dßçc:ùö®ãúæ•J“ŠZÐZã'›{3Clr˜‘zÇ…ƒ ‡Qx8–°P OÆ•ñm¹Ù…fŸ«Ïwó.gù*ÿA0/tÇSOE9KCðV£;f¬4ØñåF%ªé0éVckƒPµŽ$¸íJ Õ. HÒ}d㣜…Peû'9Üi—åç|ƒà›š0¡Ã£*×ýüÃÊk×ì>ã©#ÔÙy „lϨFF„(3C;Wá4ßœÁ“‰ðÛ§ÓQr`÷Ϋ¿ûæçŠŠ2ŽÃZd&Ƽ0af<WÆ·J뀭&!ÊMKš ‚€ßÐÙ`‹;r›_ ñDö1d¥b’¬§Nu“h˜f˜ØÙÏJñ´_jÃ=W%=Å ²ÎVðì@`7aHÑ wT¨¤8ˆÂ@Ž™$GFDhÛlvÁ¸rœSÆÙ‡ÅgóïÃ1‰( ðúó†n¥/ÀxÍB:– þÜóS/k<ùƒ5ÌÚ«¬¬Øõç|ŒýÅGŒê߽߀¡AÁ¡±A–“ÉØÙnT§Ãf«¨(ª(-Í?˜º{wê¶M€ ›U°2“b&ËL’Ùqƒ·ÙìB×$7ëþ"DJ¶‹nŒ¬EÀ›ÌÈtB¨ o.ÌëØT öµpô«¨°j¶ËÁ Ë1.·Æ±¨´¶ÐXhëŽüO'º¹‚ñ2ضšx`Ä8ÚødÞcM2fÖλµÉ-wâ³VÚUhøùÇMŠ ÐÙðÖ*ë÷:¾éÚ¶{ó†-Xv£NÓ8cͤڭÉoÆýç¶·¢ß+^&ɬ1fBÌÚd^X“Ì$¹ÅÚdÈp‡†á )‚€ ´!ø†¨-v«­€_µa_ÿñhÒ0™ü1ñã¤н_6!㬠ÊV8ùÙA”4Ï.Í<£3dHÖ³9ã¥G ab‚ppAÚÂ$™3Ž--…ÅeªÓá89ú`KEÊù‚@{F€É?.üÐ÷™²Æﺴˆ'L”ù¦Æšgþ"6ëÞˆóÚcÑqa’Ìæº6Ys„Ä>evVå…qããŒc³‹h”› œ(~B€o„úâ²–—c{4vÞ±mjÑI “;md™·9¾rpPF’m¶J8þU‚(C£\eǬ厬TÖ­&tŒ8Ý7g2d³ ƇÉ2k•-À 2×óq&É|NK Ï'Ï«½Ò–9ú|·D¤œ+´wtÈäÎ{› ? È7Avü«‹(·ìK ¡m¸0\t\t¢Ìfúƒ“e~ à}®×í’õsQÕô"D¹é˜É‚€ à?ø†¦/|#tJKݔثϵìáüêÜ+¯emòUYyêµçŸfh©Í² îØ&™Í-X“œypßüÅï¾õ?ôÊóÊo t²eÿO…ô MD@ˆr“æ‚€ Ð*èvÊl£ÌÙ•8,{y@–¿9ab¦ëŒ³o|þ%‘Cû&«c†ôV†õMjVœeÈ”â8N2‡€ãè츧ºœ…;7¬{aÕ÷‹—¢;6¹ðIÖ,? ]D ‚€ àA@ˆ² Ù6„€·V™½íôPH`›å…ƒNÓ·{¿ÂBBc‚,‘&£‰3XIi}T§Óa­DÚqΨx$}ï†mëVïÄ0ØÌ‚Ö$3Im2@"m!ÊmŽd„‚@gF€É2;{qa’¬ïë)KƒPgÙµi]1N\¡™g`­klJi%ôùaGL=櫵ûLŒ™(s^^xþx^y>¥‚€ Ðf¢Üf§F&@@'_Lª¼·™h1ábͱæè‡5ßÏje!ÌÅÏ…ç…‹>?:Qf³ ý†Í,˜4ó>×ëq“õsQ%E¶‡€å¶7'2"A@¨‰“)&_ÞDŒ‰3“.vò À¢“dÝ–™ ²d€ÐJ…çF_x®¼É2Ï“c^óÂÇD“ ¤‚@ÛG@ˆrÛŸ#¡ ¸`r¥“eÖH2ùò&È:Iv§Ú¢ìF­uþó¼pÑçH'Ë:aÖ÷õãîÖò_6Ž€å6>A2öP5Q! ;áIÒà8T¢EU2qà Ñ`üîõ9o8®‘T‚€UU•{{÷›Šp‡ƒB\&§Q!Õ€{Aq˜ì.“«‚ÈTn àò×Rî*õœ(‚€ 'ܧÛ^YüŒ.æ.ST[4Æ””·,öŒ? Êý.—+AQ5,ØâŠ V,f³hÔu ›°¶Úí®Âârµ¤Üj``0²\.õ…$sÒ«))7Y› Jš¶cž˜Wý=9£eßÓv ƒgèü±ñÇmû+ä<ÕEê)ä¢>Eí®’ÚƒH‰TUjÜýF!›Ô\)Ùx =€³ö*ªšJŠyc‚ñÁm))ŠËÓ©l‚€ ø‘PÌWDù¶Ys¦Ãœ.WÒоÉê˜!½•a}“(Ø诡w*¹åVmKË u;ö©ÛÓŽ(Fƒ!Ã¥ºnž?wö÷ ˆNz±B”‰n›7Ϭ:vÞP]Fªz‘JJ8Åå2™ó]&c¡Éd,"£±u62¬XW¢¸.ž3 U&RäRÍ8ÝD.—…œ®`r9CÉá SöX§Ãnd‘U³2…”xÞÿD 5>ÿ¡‡Šªêe%‚€ àô›¯D7_¤/ˆòm³çÞ§ºè¹ä®‘tÍ”qÆ~Ýã›? 9ó„ì=”M~·Æ™‘SHŠþ:ά—Nx’4h×tv¢Œñ;@tu©J<ˆ°3ز×h±ì§€€L2™ò˜,ûd~UÕ@G,Ù*©Ò–Lå¶~N‡=ʲ\Nd|:Ñ”ølí79ü¦çŽÇžî%Á pöî¤*QÐLGA«nní¶ƒpW‚cMyŠA9‚w09œH§§¥¤Lrødð"DvŽ@‡$Ên’¬¾8zP/õW̬‘âo*íNz{ÑJuý®ý ~xÿ"dÙ߈Ÿ\ù‘(³†8$«,Ì`5:+MF#©&»É®ºqóSf€˜i&[÷|Òy‰%(M[©YöµêdØí]¨¸d•”Ža•ô¡æÐkË]ågº\ÊyŠêšmõ@pe‹÷ Åé2¬.¬ÁqOä…ŒÐ`ðŽ÷oA%Œ§·â`=LGÖªA†%ógÎÌò–%Û‚€ t¼oŽmæš[¢Qfs ü@|3zPOíWœÝ&¯¯Íí§¼ùùråøAV/3 ?ÜĶg¢|gÊ?âÇi*9OÃMb©®žÐ²&ƒ@ÆÔ"ÕH+j%4È…à”å0šè½ˆÂB×U? [eåÃèXîÕè™íÅU¸`8\ËAÅl>Šå˜Ž‘ÑœOF¥ng#dÒìt†‘ÃŽu$UVÆcIrñ ‡oXJ“º ¯Š*d~ÞÜ¿í­ST ‚€ Ðh“D²¹D™÷²™i qñûã4£h’OÎ'–5ËÏü÷kgvnQv¼1±oí×Â'gTÒ«¯hoDùö”¹cU‡ú;˜%\¢Ûñ`[bJvÄF£±¶ÄÅd0‚TÂÄS[ƒ~Âf8öüƒLS`àŠÿÅ×6K^~þ42*È”N‡ë%ÄÍΚëòŠTnè´Y»ÁQA_ô£K5¼šdzøkq,lªrŽ ´':Tx8Žn·$Ø$“ä“÷1dì¯=œñùw–$e¸xN蹓7é¹3#p÷Ë/ZsJnWTç=N»Ú›Í‚‚÷‚aK ²`Î6Ô§im/¸EGí·¡šÍ9ÁKø/F—+„ŠKGSiÙØÉ.{ØyöÇwã Þ½òÖÈoð‹`A@ht(¢Ì!à÷NRá¸×&5åm`¾[mì<É‘Fvî˼ Qn5ä¥#F€Ùn{ôÉ›*s ‡EBR å+,d=ï1FÔ )MFÀ`(£ÈðåXVËËSAÑyýìöèïþ4óñÅ–ÓM¯<òȱ& •A@hã4.®g¿'á8É® ·S ‘ç‚ç„ç¦S\°\d›@àŽ§žŠºmÖÜ…°“ø7´Æ ñ]þCñ]þm Ù³!É-Ÿ$;(1áectÔ6_¹ÐVnß|ûì§Nm¹l‘ ‚@ÛB ÃeθÇÉD8N²”¶ÏÏIU6Ķ1(E‡FàŽYÏ p”Ù7ƒ¼]Ä& ñ],–úšOÖűwxØ*Jè:Ï`4wu©ö•wÎzfÐÉô+‚€?è0DàôàŒ{’LÄ“æÉä¹à9á¹iž9Kh<w?ùdœ“¬ß”Ф„®ó á¡kr3[F„v£ñÃïÓ–=."K@D3%Úågͣĸ㕴½&ÐÝWm<þ„“TM ñ o¨,ЮXÞ–2/ø$ EºAÀçt¢ŒÌX‰œ–ÚçdGóŠ Ñ#êÒHÓâ•[¨¬ÂVçñ¶PÉsÂsÓÆ"c踰M²­Üñ4ÉÉ]»¼m䤭Q"A”Ï~/ŒѰ>Óéž«7R\Ô@Ÿt½ûà×TTzøxYÈèc4š¯?‰5œy0.ö¤4ôUœ¹WÄ¡Hׂ€ øã̆f1›[øÊÊ£'ÿ³H› 䨥¸È0:ÿŒatÚ°>> E+6!!€“f\9ù8¹ù%e´hù&5¨'…µÍ´Ü<'<7Ç ^*"pÇ£O^ ®|zlôW‘Ö:$Y¾Óe§eŸÒv¯šôÒ÷:úqÝßéüqOц=oѸÁ3(+o3mJ}ú$M¢Á½.§£yÛië¾lM¡)cŸ¤ÖÍÂo.R\дñ/ÒÊ-/R÷ø3¨°ì°Vl‰¡Ñƒn¡˜ð¾”q¬¦¦<>fÚÿdì˧-{? Â’ƒÔçF‡÷Fld+ú»Œ>ûùVm[³?ÖD1™ \Gôÿ¶?ú™‚€ ´6­F,[ûÂZ«¿[/?›îš~%u‰¢–¬FšY»O»¾éÒ tëe}*S„  uL»|Eƒ{^î¹Ä.QƒèšsÞ£ÔCß’ÕZHÓ'ÿO;ÚÎñ7ÚçjÚ}p±ßI²g@sy0ö"‚€ ÐÞè0å“5]¢Ã¡ù‰¡ä.Ñ´~ç~Ú¸û ¦Užõ¯ÏhÆU“èóŸÖSN~1=y÷Õ”WD|»š@LÓÎA£÷¢—ÞûŽz%ÅÑ¥Gj—Á¦¯|øÝ|ÙY´f[:œ÷Uº~êÚ±¯Wl¦evSP`MÙ¿ÆeÛúøûµ´iÏAhvŒ4yÌ`šrÚP•Eu§ÆÉ²#´SnKy.VµW ÞtR®Àd´ÐŒËVPˆ%–f¯¢û¿ôŒcç…ôËfwtÄ)ãæâþð_âºTåû¯ÝA±‘ýi[úÇ ¿—Ñú]oi$xûþÏð­Î¢Ç<)v$ýgñ…t¬`7²í…Ðäѳµ>úw¿@3ψï…ô(t3yæb6Y I¾™*íeÚ¾¿ÿUV&ÓËYüÚF&_°ÈN€h”}4ͳó4I–€mWTJÿúè'   +Î ó ½úáTa³Ó ½½“»Ð›_,'6áèƒíU[Ó<#Ù°ûU‚ôöJŒ£â2+4RÚ±#Góéë_6ÓˆÝ5’½jsÍL²+6î¥mitãÅgÑÅgª‘ôcÅuŽÇÓ™líÕY9„/å“r%NW%}±|½ôñ)ôÙ²š&Gó·yÆÒ¬•n6‰°;*(4¨ mßÿu¢Ù6÷…FykÚÇžsx#(0 ÑcŒÐ>çjõ%Õל@®*R­ª.ZºaÞj•híX+ÝZ$Ùá§cyÓÁîÕìð˳ÚäŸ Ñ(·pÙ†¸¼¢’dæR÷„Ö¯:<k‰o»b’ÖÞƒÙ”[XB¹þÐ#^Ó$oÞsH#È“Ç ÒœòÒäh¤yã®4šf$P©1ºÝ²@¼éú‹ÜÚåJ»ƒÞûf•§Íö´Ã4¼_2~tô%>6BÓp³V™‹÷x<'Ɇ ÐÎ0Õb«pŠé“Q˜ æT™[4Ôÿ‘cëñÝŸ m2Þ %ž­±‹ý ãvÐÞÃ?ÐÄS¦£ù;)¯¨ú¡™å±C_aéAh›/ÍóÛÔ¿ÛžnvXD½Ï8›vÁ¼¢´ü(…w¡²òÏñÖØ(«Lyy—9I ,VUÃÕÏ=ð@먰[ãâ¤A@èôQnáG€ÍöL éSÆR„XM×qêÓ¹°¤\«ë[f½tëEùÐ<³ùFÏÄXÚMrXpeä@ë|¦Þ̳ޟqŒºuöìw¯Þfuê¡lb²½~çOŽš¡ïñèu²Ú;!!éE…ÖÊ k¯€ààímör¾_;“®˜¸€î¾E3¡øv̓IæoMÿöÅoӒ߬süK7<¡éñÃï§íû>÷´9ý+Îý˜®?ÿ32ÁЩ:èõÏ¿wxNðÑ'¡ JEÅg¹*+»ò#ý2›/_òÐ!u!bA@hQná4\röHÍF¹.1fS5¼qQnÿ–lØ+÷†¦™ËÑü8Û¸5Ðc†ô¡åvRdX(´Ánâ\[fBl$­†Í²^ Š«7bJ&£‘&ŽH—O®™O'îÞãÑeÈZhï°ó¶™sç—–º3$^üUŠKÆSAáy,Þç½#¤^¦Àê§ru,rA@heÄF¹•gíqDh0}¿z;œûJhéº]ãTSdmc‡ô„–©”ÖîH§±Cë1׿g<åÁ|cÕ–½h[L+7¥Öý©°]^ƒó÷ÀDƒ5̬™Öm”k4”A ƒ!`2Hv{,eçÜBYw‚ÄýLaa«¡ì„ñ²Ÿ#`2”RD¸'¸…ÑnïB¥e#,¥¥£n@ž áŸ9oîÌg9…½Ï;‚€ ´"5½ÅZ±ã†ºZüLõÍuêƒ0†kD¹mÖœeýºÅŸ}ÿ ÕŽ.8í¤4±"òE`€žìÍëÞVé@è'ãqÎ~º4§Ó…è•HBbivº¬–®_xg •²—ÏŸ;{bKeÉùm 'ç+.˜ã7óSìŸkq¹B(¯`*•• …‰C±ÍrHÐvDÄÈÂ÷¥Ò?ŠT.WN¡âÒq*HòóçμÓsP6A@h‡ˆéÅI˜4K`óI270ÀT/IæãF£BƒO>Iæ±HéÀ¨t{mJch0”Q\ÌÇßõM ²ìa©i™™Mó½ p5í†;ð윔K㇑èè¯)2b©ûé;fÌšÓö5')éTÚ bzÑ^fJÆ)´1™¡Öô>kSã;H<Ö¸bWÎe•ö„ù[ítDÂCå@+ÌSDø2*)ãt©a¿CwKZ¡KéB¿ DÙ/°ŠPA@ð|µqoÑ7±¥2ZËÅE‰ª¢&Bµw"úMp8Ã’M3ÄÛ*“ƒ¡Î_),t½Á`p'ïñÕØÆ¿Žì„yÑ÷‘cÝ‚´Ñß QP–§®578}õm—-£—>Öj Gj_›&f­Ö~î îµȾ í!Êíd¢d˜‚@GGàçŸ'šÖXëtV&(N—F|½ 0ì¡5¼·àÓ`aÐÝô4Û*^•[ûSnîUph5QdäRŠû¶úv?À§ÐøSî'β·`á9Zbîdì ”•»¥A¢ô-%Ç¢qƒo§r[õˆŸ@ëv¿I§ô¹†¾Yý€vþ´ñÿD¦ÏH,ò%ÅŽ¤!½¯ ï×ÎB2¢Þw:E…÷Fªúé`öj­ýè7ѱÂÝHx4‚¢hÍÎ7<ã0ÌtΨÙt8ç7MËí9àç [e29ìÑð8V–û¹+/‚€_¢ìWxE¸ Ðqð&ÀÅ¥i{ñÊæÂë6¨“W‘_odpNS ¸61ÎDLœLhN³ð¦?³¸dl"^Üœ ‡¾ ŠâlŠÌf·]¾ùtÃùŸÓÀGF/éµÚ iãžÿѹ£S Ù6Pfîf²"1§¯fò\P¼_#À«¶½B–ÀêÑåtŠ ë¥å=§‚ô‡jçÝv1ì^J;òRY/ò\‡A1Ò%ã_öíA²’Ö*°Gä‘Kœø,dÆEü»µú•~A@ðB”ýªÈÚ1L€7§®³“=Á›#‡¦fò‹ËK¨¡Ö5¿µoS 0äæƒg1VT$Ûà– È0Ô“™FŘÕ=üâ#Ó§âÅ‚ÝΘ5uµÉT슋ùÈÔZ$™§¹Ü–O+6?«á}™P VE·4™,Ô;ñlZ»suL%G‘îþbÚy`!Ù¥0Õ8MïZS­ÐŸI¡A]hKú‡4jÀI=ãÇ#‰ÐëÔ£ëéZlôŸ×ÏÕÚGEô1¿X#Ê\‘_’îÑHG†õÐÚœ}ꃒLï~Ïf(^piGý÷/7ÿ ª´% Fºë•{î©™‚ÐÝŠdA@ü‚€eÂúÅÒ ôÝêmšÄ`K q6¾«ÏGÉX·´ìF¶½ž ±Ð8™[*JÎï¤èØj°»àªL ˜³&°°é›@ăz‚¯¢T_oÂë½­µ9ñ?6'ÎÁ΄8h« °ÛÅ¥ºL™½£¦eÔE€«Å3Ùc[ãOª«ª¶îNy9Üj/ºá{ýa|\—ž xÚñÐÞWÑ„SÐlù ]“ÑB.Õ¡µ=µœÊ*êN¯½/kuë2–¢ÂzÒÏŸ¤.‘i@÷‹(.ríÃyº]@6{u†h+Ì6Bƒ»xÆ•ë¾ïx*°Ñ%jE‡÷‚ÉF<²€ô>ä§m…ró/AH¾á[äþyϪVoû©G+‚€¿¢ìc„#Ã鮫ϥ¬¼BúfåZ¸|#Ý9ýœõâp:éO—ÒC7N£x¼š•"x#Àxí¾µ]\.{‚Ù—K7}¨ƒWyÁyÞú¶½û©cÛò›‡ú,jÍ‚µ¿¼Ï¯à3 dÊj ®£Ï:«LäÀ½¬qɉêÐÂJDß %k¦?\ø%9VMZIY6eÛ@.§ÖízK« ¶¸ÓkWXó‘þ¹šèîcsŠ3ž'K@ÎÙHû#—ÓÈ7RvÞVª°Ðȹšâä¸Ñt¬(•úu;ŸR~Ûà¨?úé÷ÿ£Ë&¼Fï,¹ií+lß’ƒ.W0åä^£Z­½eDy|ÞœY/µDžœ+‚@[A@ˆ²g„÷Ý⣵¥ÜZI}÷líš&8;¯ˆ>øv5Èʃæ(˜¦5‚Fî¥à?_®€†(¦O§í¯ÝžN_ÿ²…fÝz ½òᚌßÿŽL=ñç«´6«·¦Œo¥Òr+Ò_'Òï/<?¾ô>úHîMy…¥ô˦TºëšsˆÓ_Ki_Ô&À.gU´*M0È­nm·¸Ö%¶ˆkä—Í  V˜üV‘áV$Àµ.§ÞÝSîÏÿÓ¬Çw••ŸÚ?"l¥±5M/ôA1©Ý´ç]5ðF½Š~Úð81ônºûªõ°6Û3oJ}—¶¦Džö,è{½¹è\Ø-o¢K,í‡c ›IìËXNç{ŠVnuóÍÜÂT8ôͦ+'ýÇ]p ü†¶íÿÔÓO}¬þã…_ÑäQ³è‡uÖ׬Eõ`“|©ÃéuI¹å9³üÊ£EC”“A@h6B”› ]Ã'VÚ´{EG„i$Ù‰÷Û¯‚ð[膩gÐÎ}™ôæËñz4œº'ÄPQYÍø® ×ùEeøq5И!½hï¡lº|Ò(ЉÑ:.*)§ÿ-ZI—OE=ã4¾¤ø‚3†Qq™•­ØD¡A4uÂ)#Zè†g«uΛ7Ê\hÚ3Í‚ 0ˆh•éC•&!`ѵ.ìM~yäµ÷¸·Ø‹ƒ]ÃþdX3‡0dšM™ÝB§e6×¢¾ý~H1@¤……Çr§#âÅGp€ÓÈù«k•ž~§{ áLF½ iæ±MôéÏ7#å|04ºv‚Ö_kŸ 纴#?iäÙîp÷_ü¨:äpQÙ‘ãdoÜó6œßÖœó¼µÃ_¯¼·ÆØÌÂ{\o-¾¨Æq_í8‘”_x‘Z^>/ÔT“Ùðû×Rfnö•|‘#‚@[@@ˆ²g¡ ¸ŒRÞøášJ´4Ò3®œ¤õv8‡rQ÷—ë/ =â5Mòæ=‡h´ÂL”ë+œŽº´Ã\ØF9>ÖMz·¥ýaØC;6´o2mÚ}P#Ê\aµ9hî!ݵØ4kµÂ?&Àe–]ìG‚q€Á`¼ 0©F†ó”]a¶Š]¯Re ¬×h»µêôcu¬9 ¿jNpZ`ÝŸ¡L“Á”Õ^ p×[gÕüÇg.¾mÖœ?—W zíè±›]±Ñ_L&¶ 9ùÅî(?n.—ÄÙm¿|ÜÁ*¼IrÍüv¨ÒÞ•ŠJÆ«åeÃù ;åï‰æñ/¥¤LjúÅøm”"Xß DÙ78z¤YÌ4yì`Z¿c?œxTêÅ!J!4À\ºu©vìcg¿ü¢R­¾ö?4Ð •]û3á5_AOýgqu3/êÕ·[!ÉÕÈ´hK'À6›I0 ÙßÀ4Ç7ެ`h„óhcÕZ?öð^ÏÆ ‡¥`w48Á‘Û Î›“jÊì1-«=j€OxõÍh0îì×ÿ4û‰›­ÛëY‡†!<ü72Êš!MNÑp¹‚¨¬|œOqZ+zQ_[ä×U‹ò‚™3ON B}p²AÀQö1¸–€:kä„J Çæ}IlG|úð¾¦õ”_ì!ÏGóKhhŸ$­žM,löjoý#9… ŽÌ€Ä!4÷®+ël`æß2) !àM€ÝNpj‚«ÊîW‹§8Ö×&Àµy®¶_»²¡Ž‰<$Ûíslg|d"Ö.œã„7 aýGÌ™ùáí)Ï.u:l/•œ}MQÉ Þb@ k <\ÿ‰ră€Ë€ô×IdµöÄÛ©^.›­^(ˆM­¦áí¿Áæ7^JùKÃ7)4ÙA ý" DÙOs×%:œ&HŸý´žNéßM L߯ÞNWLMÛÓ@Ë\FÃú%k#èÛ½+ý´fe+¤b8çíØwÄ32–…˜W” ;åð ÁΙeþº9•~Z»“ÆèO¶J;åSßn]=çuÖ Íذ³+ÂrUEp`@Ó5œ6¤4¡®Et=v¿µêÂò9~Y.Nö¡ÐªÂ 8&°¢ÀQ S–h€BÑ7ÇÞHy ’~wGÊS³œvç½eå#n--;5Øh(sï2í$‹å¾W•¾é°)v{4°CÎn®Ðà͆°°5u´jÝ*^ÊË¿Äi0”L†˜u³¦ïHT#^T˜Ú.BuT"‹¸+¨ê·AAâ×VøŠ~o6>äÖ/éMN>B”ý8SÇ€F9¾Z¶‰®»à4ºûÚsé­…¿Ð£¯†l[Fš:þ^·3ÐX8ì톃ßã ¾„ms=j-ùÕ5‘,&ŽH¿–>Dçÿr ï×.8áç6Ñ'?¬Õˆô”Ó†vh¢¬`R`¡"2B ”‚ ÖUNpÐÃ8ÀUQ¼ˆ®‡üÖ¬Öš5ôO'ÀšÉ¢?x`h‚áhÊLš–-& !ÙúÇ^Oyxz½q–g[%9ÕËKJGOÅÂ^±j€9×xÈp„Ìæ<¤¼Î‡³\1¾O^œF ›5°vØîÚ쉜©ZØNNáŒÏ[`e¡.Áh*6íj„4ÿ4±Zû „ÛÕNU Ê ŠI­$W7d8HàÛâPmaÆÓ¥AuœŽ[Lá«^I¹§:€³†&RA@h³¸ÉDÞâgª¥¦>ظبpâYÖ¯[üÙ÷ßpA»šã‡ÃáâØÉ?¢Ç[¥Çê~~áH¬ý©mVQ‚(lm2¶=s‹ÞYB{g/‡íèÄã.¶ª‚ p©yg¼ÃUå­/.Ó‹£.¤£Ôê“Üp½F€U:†VY Eš êÜQ @€Ù Îl2eO€–+GÛ6w¿ür 5§hp8ÝÝ8Â3ð±ò8@ƒê©u˜á*`e%£âN0§’ÏKо"`4b«Ng„aÑ—PýåST|¦”5 ›‹-å —=¬Âê(üažÏ ÞAáa«ZÍÄáGæþTZ:Êe«L‚eºÉ¤.ÿ׳¶íY’Ñ ‚€ Ð6¨›‘µ±uØQ4”]¯>’Ì`˜ëž®0˜c´å¢öä<ãel÷ Bª™>° ‡ô(qª½®¥ÐÓvkÕÕwÍ:Fs-ùÌ´µ[#ŒTÈõàÚÔŸ ®¾¾¥¾í#P•VùGŒ”­Ü–òtwdéƒÏH|.{;Q=Šbò »''ÂÎ(áüéÀSBU f‰*I5ÂÌFÕRmógL1¨»Íó†WSäÏ[’’òóÄLǪË*ÿNq‘&c¡3(h¯Ñb9€ïu64ÙyxpnØ·†À:v˜;1TéèŠÒÐjWvƒV;F#ð÷VÄ8~.Þ4þ#‰NQxR%‚@=Ôͼêi,Õ‚@s0›²G"ZÄÞ¦,ÇCK= K¯‹C¦Û ÎXåGæÌ¿Ýü·lEIñbµ;Ü0Òïèü”‡áªyùÙW_ENŸøë³Ï¾TRdîtE^RZ:æü’Ò1AÜŸ¦Å6–@“]h4Š‘ÝΆ4Ð62° 5¿`Ó?°%†öÆäT‘Ûær:Ã]g¨f}ìx(E¸@eCW@cþ%ÞèÀ”BŠ ‚@S¢\ ±ƒY¹´#=CKöQXZ®9ÉÕjÒawu›†˜…D"s`LD( éÔ`œg_€Á„—¯²Øô?ð°¦Lw¶ †œÁœÕXüà-)¾–È|ŽÀs<ÀÞsoñ-³é(­ì´«§’b 3ŽîNgd²Mu%Au†ïc(ìák¼.Â÷öW*Çš,y.F Í–…öp U÷Oï1«Êž×æÎ–ðFŠ -E ÓeŒÌÛߥëvÑÏëv C49ÐàX” TŠÈDnÛÄ–ݞ·•4¥«dUƒàˆ¸þ霱Ãhò˜AxE\mŠÙøk2—à~¹Ç N æ&À—9ó¾?ýíhc4ÀB€¸´lûTi™·b¤¼ÔYpR{ =þîÖ)ÃÄC×-×Ù^*A@|‹@§%ÊL9©kßÿöW*,µQWSõ Ú‚õ Ppª“—J¼Ú=ê@G¬§ÐçKmxØN×OOCú$7 8m|ä6õâê÷Çúénˆ¿ÌHÑ+d-^Tc5%Å«R6A@Z NI”™ #P(bï ¯^-ܘCãƒQ¬é`«ß:⇅næ-Ú’ëèAÛÊ/¡W?ú‘®'“R¯Ùhvàù!b‚ñ ÚTq}ªÅ PaŽ1f*ºes¾x¹4A@A@è”t¢¬“d¶GÞ–v˜ýâ&Éc‚?òÙÄ…w£¤î”Ò¥ù©”{øWrØŠ|"?¶ÛJpmûñÿ|"¯9BøaBëüd$ЉÐÌ0„,7M9GA@Ú:žpBm} -›[0I.-« O‘É.ÜpTÓ$·T®÷ùÁ Ê}ÆÞKS% šNçܺ‘Âbz7iöviA:åìÿ¾ÙçûòDÖÀ‡rè½Å+‘…L4ñ¾ÄVd ‚€ ‚@ÛA Sh”™$³¹…­²’–­ßEEå•°IþÚ/æªÓN©«žÒfxä´·(yÈu´kÅßÉhÖ´ÍÑIgPvÚb:šþ-²{9ë­2é):¸å-ê5rÝLe Ê‘ cpÞw“|…Æ £ÑBQIã(ÿÈj:¸õ¿ˆ©j£¨Ä±”­vPx2´Ù%d‡F{ÇÒ}ú‰cÍò°ÀE´²ô-ZÈùg #¤0ói"LA@“@‡g7lrÁŽ{• ÉVúm{ºÝÂߎ{¦€P ‰êM%¹»´9~Þ?)¼Ëp:´íÔgôŸ)¾ïÔëãzžC#§½IÖ²l*ÈZâÛØü‚ o:+…Cã)c×G™îÒë<$(0Ó©Σ£ âék^BÑM ´s|ýñã(!?¯ß¡áË8KA@A #!Ðቲ[›ìдÉû¥R«ƒ’ÉÁ_Å`²ÐY7¬ ‰7­¥²ü½”™ú%L1,×ãl²•çPxÜ`ߣßïâzëõ±e¥.¤½«Ÿõm½ž×N»•vÿò8´ÓßRî‘•tÇhKqî*ÉÛ­i˜öïÓ|ºÍ8—UÒŒcZ¨=Ÿ a‚€ ‚Àÿ³w€QUY?ïÍLzBïUŠHµWt{Yû.ºëªk!¡ˆ.X(Ip4 EXÖò©‹e] ö‚`QšRDZ „šž™yåûŸ—¼aR&ÉL¹Þ›ûî=÷Þßkç{î½B@42fízá·&û|TRâ¡õ[³­ÅDxžäpC÷ÒÊ’¨`ÿ&,3[ê¿Ë/+˦Eµöm_BžÂ½Õ²Òx»ny{VÛ»‡m‹r·Z®|ÀÐJ`MÆÒ(Ù°X¿Dgßø1åo£MßÏFÜîÃò†*‚9òúk7ï ÛZî• ì+Œpöøbæ4S¡-Š©lq(Ž-­»ª[¿v›LVª“!r„€B@8 eƒø|–ÛÅü"¬9WÞÅDLã0 p ؃Ù+ˆý—·üÌ«×,¿­É[´¯ÒøàÎrå®-;œHë¾J¡œ-_X tp²ê–ŠçYæ ÷,€û…n­ÚW™¢¬©J{|´LäµÅLü§QÎvÅøÂü݈ڂ<[Pƒ-¢H×íÊ:¦†Ó¬Y/X‘kìÑ‹º¯¨ÒÖV_iâ*"Kò³0ó_¨ FD¶„’|­ÿfz©Cms¶™Wt¿ˆÔõÝënNz(°Ä¿.ZäØäËëªéFO ´©ByE:‘ì ! „€L Ù*ÊÌÕv½`ÿYžGšs#ãoñ»6¾KìnѲãIÖÌ?þïfbå9ÜÁV”™w°áõáÃqBhkÙß’ŠùD‘®HD~ ! „€ M Ù*ʶu“ucžGÙ€²Ü¼ÕäÒK‡ç\æ¿ –õž¿CJ­ø-Êu©‹(Òu¡&yš·ÛTwkôÕT­—bÝQ§nDj[|º·À3(A!%qèÜ2èãR2yzœ"Ü>ŘE¦Le/¦“É!…vÃEi«¢ª;ÒÄmn·ü—hS!uB@4ÍVQæsc+n<Ó[;ù·„Ð`ª6ëÐK¯\¢(Ò•s‘؆'àváÌÖ–m*Êðî:;Ë—q<îˆhâN¬Ò꘺SŸ;TÒU‡¢9°Œ¥i`0+TdWðç4MCÅM¤ê¦¡Šj>håLJ~¥{G¤¤ÿŒ_+ª²¤“Úå#·[¦Wlø³-% !p´hÖŠ2ŸL[%9¼—vSã+ŠtxÏ·H'éžÙs˜ß•¥-» m+|Q.*ˆrª˜;œŠ#äq9ÈëP1}x5WKa–>¥™"4ƒ¢|:EyuŠñêqíü¬èæ¸,cGçÿ(Nçà Ü·Uw>, wäôwÛ Ê{+rè.Ò¬xÉaø"#hßœI“ö¢'¨L¯¯NšB@]š½¢ÜЧ3:¡uèu91˜.7û'â™0xŠú„ˆ˜¶Xšz-çV˜¥* XùGc^Q¤Æ³º6'MI¿RÓ¨êv°8J?"ì—Àñ#1%Ý{gjZ¶Š`ã^«±ÂáTWhÚ®]”Tºr’_¶B@£ˆ@³U”ÃÂ鈈¥¡ûËJ¿Oš·Óµ²åú^Oš'v®ÿ/”äâúŠ {þÆàŽF‰"ªMOæH÷´žðËêgFWè ÝàÑvÕì;¡Y:1Û74UË=b?|‡÷#~â5C§‡ b\ʆöqj ,Ç  ~0&ÂúÛÖ6ÖÑ'§@m›çù÷ˆ”´+1£â0¯ákÇu1TÅ(Šp(üWâR î„…`HVY7¶fÿÁ,3Ä6d\;`½ŽˆÔôî‘>£k¬Gê0ØÖl²}OqbJÚ'Xúó]Š¢÷$'ïj¨¶J9B@¦@ É+Êï϶;p Þ D>YŠ•'`ðÀhRŒE’ó`„=OTp;µäa4a mºžEº·È?¯q`1 íS÷Á7“·x?íXû*ñÔ‘±í©Ç wP|›þXòz=Ví{†}E‹3ñVnÓíO~e9*®uí?œbZõ²æTÞ¿ã[«¨Ã¦Q6f¾è:èïX•ÏCÛ×¼Œ•ÿVV#lû¬ 7%9H¢HC©é¥•2£Ÿnú®††x¦n*çè>­M@-M¯SÑ¡T²«lÃð†b …Òtbˆƒ®ÄQ×TŠ#úÚÎ ªå³±—ý{ûX¥u£ü׈w9r£]P¢]ìòQWÛ¶•Ý=àæAñžè6…ÞË#}úUT¤˜#R3¾„Õù© †ôùßðÒ™k«ùR®B A4UE™}"„@ ÙŸõ=©Žke¼ß=J…6YÒãÛö§S¯þ?ZõÉ=׺|å ´ôåó¨÷©÷PDtúý‡ÙԢà ä+>HÇ3å°¸¨¸ŽÔ±Ïå´æóðÖöÐÐ?¤¿½N{·~IC.šÕ÷Ò,+v»žPë.gÒ¦ïgQënCið…¡œóCØB,Q¤ƒ%þtãܳZjÅ·B×½E3½'°ê[á4ò¢œŽ‚H'Fa"'|‰PaE®¤F~m˜wœ°ÀêŠê•›E%å¥a\àw½Û@wõW1è¼Õ%d+9ÿí¤?ÐäN­ =JÇÜ’?Aiöñª »SÓ¡®íž׌êHÊ1! Žt•½½MTò(Œ8÷¡"G”²Ì.K_9ÊîƒtöMŸÐÖ_^€ë¦½/¥â¼íÛòX]5ŠiÙƒXy.ÊýÃZ$/çÚºúEø2{* ù½ÊòIœBàH'Ð$åË0’–ÿ‚ð£û²o·ŽçŽ¿ùR+/2R ¿‹¼üÚ·ÿíÎÞC¹¿l%ò–Zyƒ\˄ޢ}´ê£»iûª—èô¿¼I»6¼Mì*ÁîV0 (¹i¤yòiËÊg(Ï:êsÆ}ÔóÄ$úvÑU•ÆV!&¡;òæù£|%,;¢ààïÖ.Fã[[Em’§Ø®®l« Št`‚ŒÆóàâƒ=/ÃÎÚv¬¢ÛZÇ@ÑkbnmiRÉtlÿ©- ½tÌÞ¢1ß»I)iOtrž}ŸÛ= o„€͇€hQ!<—ñmŽ#Ÿ7ßZ Ï0<Öà»â‚,ÊÞð.µ½ð\Ú«¯§`·¥Øz s(¦EÚ¿óúþëè¼Û¾£„vý±Î€rX\þÞßüµ<°k „EºU§S(ÿjßëÊÙôÿxÙÌ«¿e·9Eºò³Šž(%)uj †³¹‹#憎qJ!Ü+$„žÀA(Ì?ÇD8zì- .ŠïÉÒ¾˜˜™y¹¸b„žµHB ñÈ$„ìÚ ¤cÏœH1­áB±6|;¼…{h/¬Ì;~]Dg\ÿ&ÖአîKþ=”z2SÉ]Š2ò(ïzÊÙü) <ÚaqÑ ]ýµ,€r¼nI*tųpã0(û÷Å´cýþã²#˜ÀѪH'¥¦?†q¥ãxº¶ß;ÄO³&!|x°ã–vq„ê“¡²mÏã(mLøJÉB@†%ÐlÞ"Á¸^| × '\/ÎŽ}.¬”.,ÊUÅTnÎÈ„r®ì¡(˜²)`®åÊâ*«0 ÌWYšpÇ-+¼"[ ¡;®J ñqEÇ!÷ÏY/}H·g/Yžz^¸ë"òCGோ96ùòºjºÑÓ0Íž¦J=1ûCO”ЊhOèŸ]±­û‡6·€¼ÝðùÝ‚^”-»J×Änq(Ž-­»ª[¿V»•çð HBæïlM´ãô6¨l`5 oàOúïÛ)›§ÛiáÃˇь_ÖÑY»Ã^›{ ©ëþ"rº”3ç¹S¾ {R€B ÔýEו;R‹¨JIæöúóoÓÐ04¯|¨,®|ŠÒ_­$WV'‰kŽ4‹ôèäôn>Ó|r\¤ùG»¸3pA£KïmË¢=eÊ1»OU\˜¥âµ Ϧë?þªª$!wâ#‹¢„TfUÂv´‰¡Î‹ ÃG7#(ÊU’x! Ž(¢(Q§K*+š¦¦HïÙq°_B‘Ïù{{(É £–;¯lÜB«÷c"÷²pU.Ô·e=ö˯ƒ©çROL¯nÚJ9¦õJˆ¥ÇÎ<™–ì̦w¶fÑ€V-è†Þ¯àñÒ[›·ÓöÂB:½}ê%_Þ½3Ýýõ úK¯î”]Tlkž›·ïÂßN«Ä¿õéIC;´£B]£·í¤/v†ß‚l·ÕÞêø@(ÂüÍñ^}'[! „À‘N@å#ý Jý…@$Њ4œõÏ€’L9-¢x.äF¡qq·N4¨uK«ìE¿o¥%Ù94nHZ †´iIí¢¢h é“Ú´"¯nÒÿmüƒr`î×2žœs:Múq%õIH ¹çœJW~ø%u‰¡»ö£õyy´xëNKaÔºÝ=¨ÍY³ž6c6ŸGÎ8‘–ïÙG{KÚô8— ÷m œ-Ÿùýœ;a¡‘„C(òrþø±VwYhÛã<ŠŒnKY¿…wF ")]Ëv¨Œ»}L¶B XµQ¤[ù.ÜIžF²&s›þ¶©œëÇí**¡H le‹o®×ÇQ‡…ŽÑøk”ŽR`EúQ ¼Ë‡âÏak~Q9%™ãþÈ+äJ07»ZqgX‘ß½ì\zaÃúõÀA؉rem,ëM½r ¨}^ —;c~Ú¤•ve+„€8Ò 4N?e#QʵTb´kéz]D=ß‚iâ°X@8ñòLê2àká‘®ÿNS¾$³–ª𧇩M·³­è>§Œ¥nƒo&­$—z 6Êï}ò,R’Hº§€zž0‚›V&F¡¾gÞéãžAyÃE‡e¿ÄH ˜¨#jŰp¡ K€é•7ݹuõ-‰KúæfréèöoØJ”Æ qTÙ»Hó ½i§@óÖn¤ ói ¬Ô¼^+]l™õ™}ŒÛÅDÁ*œM/oÜBb»~ÈU…Š9ݰ. TҜտÑA¸`p]"°«EǃÅtâÖ:”d¬ê­ŒËLOÜeKB@†"Ðl-Ê6@Û%€·Q°8y(†¼fE(ᙾ©óq¥Í+ž¦Þ§Þƒ¥ª{RÑÁ-䌈§6]Ï¢¥/Á*|ð»j‡m#bÛѱgMÄÜÈùmúñ ÿ~qÞ6êŽÛú½ye&ý¾b.öMÚ¿ë:åÊhÝC-`aŽoÝ6ÿø$µî6ÔŸ7;Ì‘y&Äpwo#Œ  G£DæG +y{–>u ”µÙ-¢åB|þ¼3üÜnþü:·s¸Z–ßp¸C¼}ÉŸèK °ãAv?í=@Ÿ]qýwËš òÛ˜NîÅagR”k æàKßÿÜ/+˜Ï1õÛßz÷¤/þ|ý׎Uû*ï© FV°iZbë~»òu§n°Vž¹5þž™–òU°ù%BàH!Ьe¿’Œað*^Bmâ"ió¾bÚ­õ£n®_B~Ž"bÚR»¢u_N¢ø¶¨Ëq¡ß=ŠEòáŠñ5ÉÓX„díݶ¤Ò²Ÿÿ(múnµé~N¹ã¼ _«N§RûÞ—"ÿ#Ö1žBέ;ŸNwÿlýÌÝý ýôþt –Äw`ŽXztlí÷w™"_T$àv+FRjÆ´øb_f‡ÜÚA} ØÂ;øõ÷+îç}è‰Õ¥Ñóû)iKÇ$§÷¨­LI/„€hŠš­¢Ì°ÙŠÌ«ÄñŸÖ§ÓI ,ïÖúÐ^-ôÏñÎýÿjùŸù×w¨Ïiã(*¶£årÁua ð¦§/_ —ŒÔ}ÐÍm^a︳§ÃCC.zܲ·ë1Œ:{u|ëªhùÛ7ÑÚ/&Ò€sÓ(2¶½ߺË4è‚GiÅ{·AAßX*¬þe~Ìq@÷ö~ÆÌ[‚h Ãá³×:–ò??vWÜò(Rk<ŸåÆ`Ðeú`„8a­„øC¯6Ž ȧ8Îô(´:iJÆEQ')S!JÍV³±­›¥J²“"""ð碮­àŸŒQ(«J®„? +d,ãÛ  ¸6}é«—þDŸ=s¼õ·kã»°*'ö=NhÏsðÃRŒ‘ê†æ¥ÂÜÍþ²MÄ­…»Æ®ÿÃl_‘§p7çgYiâÛö/͇¼lYöîµfË`%ù„KçÒªOþE…~·ü ¥añ07æ¡Ð^¬æl3÷7Lv„@˜=~|ñÂôä‹0SÌ¿Úæ{=§lÞgÈÊ¥¶°0GˆÒö3/,Ú“I+iå(ŒtÆê†±h´û‘Ža/X B@„‘@³öQV1W‘ +È‘‘ e9kº%8é÷ƒíieñutjÌB‚·ËqÃiß¶¥ä-Úç—·sý[PfçÓÖUÿ¶,Å<ÓÏ^±{˧´;`Àžiê´ó··üùxÆ oñ~ÊݽŠ_4‹ÚuwØDë¾J…®4gM&ö‰>õšWüùV¼{+íÙò…ÿw¸v˜[¾ÑžÎéÓ‰b0ù2gæ-A4&|¬±Ûð#Ý3_5|ž;[kcZæuæ:y\}GëhGv‹èƬb³/Ûßåß:Å«'o9¯ëÞ{Ñàû›}£¥B@4[ÍFQÆÛ1¿ÄgÍå·’Û®¬ÈEEFR–}Æ_û„h*ô`¢ÿDE7ЉÑo‘S)DS×3ýÛR÷aYYiýd~?+~éËç‘ cC«y¶ÕŸŒ÷˲÷+æýöõ«üi*Ûù³bð_([’YIÎÒÒø%öìÔÚâÊ|Ù¢\™ëE‰çÄTòBY‘%j"0ß}ÒLMš’¾ÚPh>&‡éìÒL²Ôèš2×püÔv­é”vm¬éÜl¿âãZ%Pß„xzwkV ¹Ãw8íÔãé ¦Ÿãe³;ࣄò£jB‰vzc×EÊB@Ô‡@³Q”SÙy0¯ˆ­IþÀ®¬À¹0šœäX, ëÁäÿ‡ºj>ø SVÉÊ/jKC"ߣ¶Î­þ¼áØ FI®ªÜúä­JfmâÙ'™Ý-ò`I>¶C, îÙÞâÉ\™¯ízQQæÁ|ëœìª/¿…@8 $¥N?Þ0µ¹XPù,O„CÏjM9 Q=½§µoK#ôµVÁKûiÕŒ-[ÐeÝ:W«(óÜʯ]x6]ÿñWaiºmÃ4ma‘]7¡ü86ÄQ¼nð$—M„@³Q”Á Y•¨EP„cà^Á¡TQV)Š\ââ°Ì¬ò{1“†i”ºàÕ¤²‹ÛÒ²¢;¨ƒsuÅ´qœëÃ6ϲU±#äž'™§€ãÙ-xà^$f Ò%ŽzwiK±Xr—y2WæË3‹0ïÀÀç‚Ï âÂûX¨ìÕLÌW˜”’q¿ahS5§bþÑ6ŽöÄG9Ø6”aYöºö˜nôöÖ´º’y‹´jA7ôîAûñ¼ykóvk™êÔ“Q¯„XzìÌ“iÉÎlêÝ"ž~ÌÙO˰ü4/òçn]hò¿G×éãÐçÖœËûéœNíèŠî]°ê^æ^Þn-*Âs3'öïC‹·í¤Ûûõ¢g×ÿ^®y§C™¿¢{gšþó:”Gס®ígÍþ\ZðëFkYírBüƒWëÃt}&ž ߇X´ˆB@4(f£(;TÇGš¡e¬Þ”E§êå‡Èî<Ûû'³EÙ%YÇ‹È(3tðñøˆÊ)ÒhŸïK!d7ÇH*¢(5—œäõË:Zvx™o^Ááy’]üØ5ÁA½Ú%P«Vñ”€.æ„ø8‹'se¾•¹]ð¹`ÅÅ©:­ r´@”v6 ‘)O™dŽÎimnn«„‚\YC¶Ò¦¼|zð¤!4üÓ¥å’ôkO Î9&ý¸’ú$$ÐÜsN¥«?ZB›ó È«›ôÿ ž[y@«–t9”YV”¯ïÙŽk™@ìÖñ ï›úö¤W6m¡¿õéa)ܼÂßÚÒ¿Ï;“®¬‡“†÷îNÇ·nIïcu¿¬ÂbŽoÓŠ=ãD÷íOÖØpÉx ýÛ²¨ žåºÝü¹B»Óco!©&.§úlh%‹4! „@Ãh6Šò¼´I+°èÀ®×nîEÙo?ò[•¡Ð”dl¬PªDÃ5ÃɃýJ¨£O£þ| y(k?O¨MQ {~ëTÏò…+£þZE;¨¬Ç±±Ñ嘕ä–-ZXûìvÁ³‰TfMæ‚q.L0ÎæsS§ŠH&!P I)iwལM mmëÔBDÐIÙâq,}I×Ntsßc(¯l¡p~—Ž–¹î«óu‹‹¡¾-âhVÍãß?íÝo•óõîrŸ<³ð8è„¶-é…õ›ihÇö˜ëÝA; øî(,¢‹!ÿÕM[é(Ãlaþöš‹©7îA¯n`\…J³W¯§ïsöúëÝËaÏ=ûTzpÅ*Z¾§t`ñV(õWôèjÉ|§|¨[z±´u Âéɧݓ7ø+';B@#@³Q”™½a˜³ÖlÚ1sã¶lêÛýЬD¶BiF[ª%³—ûÕ²Û@$úÃÂþË1P–Ûðn°8CžuJa=Omíªl»MðÌÌ zøÃ6¬ÇÁÅîlIf…™­ó¥ÖäÊññ9À¹eeVíj"©…@ÝèDî‚h—%™Ý}Âh¼ÀÇ´Ÿ×ÒôÓN¤…pg°CÇèhÒËžüqþ(–©Î÷â¹Rêf'³Ü.Za6ž«{v¡ßæÑ’]94óŒ“,ez)ö9tÃ}–ç-hÌ.%økIY¥äurýòxçìíhWQ1†Á†ŸîȶŽýë›p½èJ÷ îG7{ ñÛz˜ži Å>ê¿3ÏÀ¨Éb[Ç&—«œüB@š•¢ÜÅÕå©l}ç¿^ûèûŽþùgGF^Û•?Vü8°RXú»lÚ8øî•@Q.¢ìƒ¢¬i,Ϭ(c†0½Pìz5¥­Í…-ÄìNÁ¼ìÙBX1fŸä˜˜hë•d>Î+¯O'œÝkrGGç§*—ßB ÔÆNÚ®¤Pïº7¾‚6ê‚*Èû"k·eÑý'ü„Ù‡˜Ã‡°þžw‡ ¨îÁs¥->4÷bÛJq>Îíå«Yé]¾g?ÝØ§'½»e‡eqŽÇ=u&”ÝÙ«Jg®X¹÷¬ÊaMζâYñ^¿æN¸+ obÙë—áÚñæÅçXåQ¶l¿?éïvï£÷/;®f.ËϹ²üõ‰k—硾»ó E1~‹uÄ_6{ü¸Cþ õ,y…€H Y)Ên÷m%‰)i·gå\üâ»ËÌ;¯;×ßýj+¬ÜYûP–yŸçWŽˆ•dk ¦Wâ~^bŠrsÖ—í1x6#^î›W2d· æÃÊ2[•yà+ÈÏÇYIæ<³Ç9À1óv>'Ëo!jΨ¨¥°P‹ñh þL›¶r-ýï’sýMú>g½ …õÅag­BÅÂF&]úþçP¤sávq€>»â ÊÛA3`þƒï;¾?-Å–Ã7»÷ÒeÝ;ÑP 9¤¯\CŸu2}uÕE´®­X yÕO$Áe÷òUôÐ)Ch”÷'†žLñè9cÅü™ß~¹’Œ! Ôsou:½X¡¥Š#æ/»Ç´ ÿ! Žp‡k9Gxƒ¸ú‰©éÿ2 sö)ý1o¹òl%вÌÇYf‹1ìóÁzì…rÌ–d/º8K­Éºuƒ‚,‹òÑ`Uf…×úÛŽ-Êì–ÂVež#™?(X9vYVæÒ.ª²$³’¼ü×?EUÆ-HKyœyK A`DJÚ0ÍÂõ¿tkÉ+Ã5D‘5–Á ª=ײض(Û¿ƒÙ²³.au lÉfE9Ôý‘{çh‘>ÝKʬ‹û/'êrDžB ±4KE™a–*Ëôh×-醋Owú,óqV~m…™øY ²¥@³’\:àÛi­füm.µ—úo³² ÷ Ka¶ö¡@WeEfŸdv·°,É*Ý'Jr3¾XšhÓØý¢¨ÐøÎT•ëº$8ò¢C·D}mr£U‹}‘»ï/2Zzá{¥ü†ÃÈ…i“—4Z…¤`! „@˜4[E™yÁ ãbUQŸÓ £Ë >]ÍSöR÷éâŸg™Ó°Âl+Í/ŽÔ[y¡™'98n’ªa Œtg Ò}æ½(õ&ü9óc\æÞØuÔ ˜'œµŠÐ b?Þ\¸4EÝ w²ö˜BÃ4^¸RiNÕZ€‹ƒŠµH dŽò…éìâ=šåÕK¿.ÚëñŠòê܇'-cƒC8¹‰l! „@S!pT)ÊMºÔC†#0Ò=³½¡yG`ÂÇ¿A½È%G8õÜ—#+ëà¯VáJPï€.ŠñèÔ>¼­ ½fB‘µ²§âAy+»·t„k¥À`*ÎÊp¿ì<³u¾§†–*:ü¼³`=þ êóWEýjØàÞ?Ë ½`(K! š˜Í­¹Ò! Žf#ÝÓzbêÇ«`F½ :홥Ђy`¶ ½Ø¥š°ú:=°6³Å™­®üôY¹ÕËüœÙŽª #oÙ-!‚ÿ0o8[Žã¼ºåáÔ¥ËyBæj‡I‹ UÙ„Á/–D8ÕMíãPÐô8Pß¶°"w9P¤G{uBSFá³àSMqt6£­b¨h…î5‡G1õgou»‡i ZI)L!ÐD ˆ¢ÜDOŒTKð`ÿú‘©3û(¦÷4ø2Ÿ¸—a*}Pê1Pvcƒ/]á•î³a±]ƒU¥W¦º"ÒéXþ”{»'YaDê´SMSeô(Žt»ã£ÔÜØ*Ã4v<è.Öã£øZûLÌLa \êø \¦î_˜‘ò]/Ù ! „@õDQ®žBà($0jÚ´Vä¡V  ðsN0JéxÃða*Ö€ö).u4Eï vq ·û ç.ý›¿cf˜»`t>…íØ†¢ê…ÑN¥0¡–À_؇?óá™:tÌ<ã·d—~İR“¾ÆÝ,µjÚÍíX¶áSŒ9 ä¶­Úô;Æ3¾¥8•E™î”åGá©”& ! êE@åzá“ÌB@Úí~¤£¡û.…Þ …Ù8 ÆíAPœë1¸X)†?ÈfÔäW<Ô×C•^Žƒ¾›ë~ »öµ“B@!`EÙ&![! „@#x`ÆŒøÜb½›¢é1˜.FáxÓ4ØDµg™0HñbŠ(Öø3ò0ÃeNl´c×#&ä7bÕ¥h! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! êN )%íÒ‘©ÓN¬»É)ˆpÝqOFFaQ9¹Ï*çr$Ç:›Råÿºh‘c'ï™HÑ#tRóbõÈÍ?Üz뾦TO©‹8R,›Ñ6>ϙۇ ³™¤»\jNç.}8|­7mHLN¿ÕTÌá,K1•bl¶«ŠùîüôÔÏC!¿&‰)é¯)¤¬ÌLOžQSÚ£ý¸A4V1ïÀaåÑ΢>ퟱ0º«iúº›D1¦ƒ ±uÂE;ë#30ôgˆß Ñ~l~'§ë¹î‰ÛÓ…z¬{N‚GË]龜óç¦Oøµ*ù¦©Ì+.2×ãøîªÒÍñrŸ5¿³ßèŠòy_|áÜ»cãu¦iþcmñóð€ˆ×üœ * bðÂüª¢¼£8é™ÕOúÍXv„€8ŒÀÇ3b:kJɸ§®ËSö 15~ß–¯W§­[ÖyÞDùF%õµv-:¼|JÒÎ"ûx­·*§˜ÔMQÔGQ^g”ta*£F¤¤=¾0=ub­åÕ2ÊþŸ¢*aU jY¥*“'¦¤¹¡d|¼0#å›*É&I #Ó9Œ㟊i^ª%íý•ÄËJÃ;*#SÙEŠòÃTŸŸ˜¤-ó¯ÃnÖóLR¾Äµ½_ŸÇãš¹–4ߤÉéÃÂyí´¡±;)}¡g†Lé¯CóëEî³z#4ª¢<ø¥Ìköl_ÿ˜iR¯ õªø³¯aš÷’¦Œøbæ«Q Ý·âæ¤]Éo!p4`ëq®²ÿ!ŸY<Öãf{ë°€¸HD3ȶ;wWÆâGÔ)—ݯÏW¼šë”¬Ì´ä˲NOLÉHEù™:uæÓ“'‡µ7(3#åÕ:U¹q2]çP̯§h)µ.f<ë:EÓµ'ÉÔÏÀ5ÍÿW:áf»]'ýv(Í_‘ê›<»ªªÄ5Æ+Ê7 Ò“_°ÓAùcYãïB;.Ô[·[1”¦„Zn#È“û¬ 7ç"EQ¸hQ„Yr`žn˜·× .>­ñ ú;úw/ôræß×ü#éãZå—ÄB ™ø`fÄÒ4h,,dÝ·ZQ÷f¦MþÖÎ>Úq‚¦ã÷‰¸Ís ´ÏÅñÇYy7kVtÁ¾¢Ÿ•xçifö4Ô•+àæ1sAFÊTî¦VZµ -eŽ?K½š4c6Xœ ÷Zt`¿}á¾>\çòÝÓ»ÃBw7v/FY±ÍåxµfAzêµ¼¬®p•>P s(,}×ᘡù¶êtΙÅN[Uî~èÉø-w1>P¤¼Ë`±ªÒ+†I]Pêï Ó“§±ŒEp=ûô— ?€ÑÔc¼-î-ŸÃb?73}òÜ=^âË›v]†‡aKS¡•ª“îÏt§,çô~•°²åÙÛ¤”©èÉ3&‘Ëy}¸»õí2¤í´…êýš¦MEkûžü™¾3ªã“G¸^CLå\£XRuçØº}æ£HvÞ–…¸¾>ŠuÆÞ÷¸{ÜAøÎŽEo[¥ùö‡ÄÔôiP‘bÚÄ܇ûl•ªD\ž™þÀFN0rJÆ%†aðõÙïÞõªªN2 ÃC¸ðìðÀŒñò}³q_^†xî£\1ŽÙÉ¥÷ò&¬ñý¨ô‹ëû$;¿½•ûÌ&!Û¦F@mè ]¶xq$•쿾ë\¶ImLÃ|ÐK ,ßÈ:Ë‘ŒB Xü˜ë Óð}E¬vJr@Ûñ¼(¿ oÙâ9qí¢ëº{1g4(j3o-%Ù¤ÉPöŒpFôƒòû ^¾ŸŒuÏèÊÇG'§wƒ’ü5”ßOÕ(e9h"Ü8&Œœ2uwè:žSæ±fï](ˆ»áCy†#Î1!¾^ÙmxÏŸNÓßBüÛ£ 4I Y}ºzãmœÆ >íA¼ÐãÉÙþŒˆXµ?òo…²½´2%¹4Ê0ÌC“ö¨NõrÅ©ü È©ºO_P&±Ú6Ìypl~¬3îÏ(£ÈTÔѱ®ØScZÅL‡o5ü@ëmŸ¯Zö;à\Üêva)gÌ2ܦñ#§óhy``†»Ér©g@Y^ehæÒ1î©Çòq?ƒJYqŠÒ8%ã (É HUï%Ù¦rh;5Smæ#ˆ©­’\*„{t ó©© Ô´CRë¾½ô\³¿³„ªÎñH÷´žšÏü×éoNÅ57Í|Ðõ)Ò >Ä= 8œÎýš¤”GúÚ5;gN$nÔ$RÔÏÊäöq*FéΤëæû3â\]UrL‡’œQÑv~ÞBI~Ûºê•.—Ÿ|EÆk‡ÒÀßZ7Ýøp½ ãîÀ=~å¡c{rŸÒý¦C nzÔÛÞíÿÆ ý¢zˆ°²Bên¾4䥅»VÝÔëƒe˜èÐÇ@5¬þB(./À:xõü´Éß[Rä?¶$CIþ—?¢;PPS td”}Ø)Ì0»²uX7¨”S(É4ÔTÕŠïÍr÷zÆs`;z(î*+å×»RÒÖ{LÚŒÙN.Ãý·÷ÂW†â½ÇS9gwÞUøðò™ÝÚ¼Kyy–‚\–— ŸÉ=@¿a`7F¤NÛJ¦ö®Q+ ×ôYPÆÏCïJo»w%)9=®’KPVo||ZÊ=äÙ¾EÇ'ï¾Ûc˯|+÷Yå\$¶1 àÒpaÐ nǃãÆP•Yºi¼2ôÏÆ‡J¦ÈG 蛪nx^ÁËѲ¨†¨Þ§mÙú«­°%–à“tÍ|K7ŒÇѽzê 1˜…Âr`%ŠtXN¯Ä‹óü óÛèžÅ›Ö´-àƒñ»•}œ·¸·‡¢pû¸U‡ª¾L…`ñ][!ݰ¼&pÜ‚¤$6Y†¦°"j(ÉCQ™õöïʶ°¯ Œ‡5ø(ߖ̲ø Ú(c¾;y ¬„» Œ¢an·©¢— ¾•Ž {1ÙÒîqÓ¼J[¥B=ræ>8yu <Ä?Fý‡ÆUÅÊ$£Ê| .Ó $˜Gö‰Ø'JòÔв0gO_1°–2ïB/Ë"\›SˆÔ8Å¥ž°0mò’@•œãÜ[˜æ©ôÔ? ÔnÂG§u}àƒôYÜ£·àþÂ%c…[‘çŲû"0+²™ýR¿ ŒŒkYþ>@ïDšö„}ÿŠ9óàC¬Ï¡¼Ê›5+É(Rî³CÈd¯Ép6TMNùå„|£p¾ŒCpÃw=˜«¥@¨us†T¸M˜ÀâGÓnEõØRÒ7ƒq̈|ö² žj•G¡ Á§6å2ÿoì̘oIX¬H³¬Tpø/ür×Y‘¥ÿ<å2;­]Å\¸ZQÕùöq~‹£ºý›·Ó±#ðwUûÝ ßÌCÁ„&ÀƒìÙw@ad¥-Ú0›¿Â"v·}¼Š-[Ÿ«A¶á0 ”\C¹ Ûœ– ÅDéà˜ôõ.cj”µCy»SK¿y¸ž…XoùÖ ö¤Âhh–{–WÉÊT.AûŸÇ‡Ë#ÓÓ_›Ÿ’’uXŽâkà^]Ý-ªà†K/Ò ûßW´W‘Ѫ2=ÏUàðûåhèåð–G1=¸´¬ë#®MÌ…ûŠž•š> s!¯-.6.1ŽûËcE(.\'å®û3»võ~º½á+J$,ÊŸwŸ²e”iàj¤BU¡`¯³råÙ2ý[¹Ïü(d§á”{¸†³ØB³(“¶á)Ã}ò¢Ì©+†'YƒrÂS†HM‡[„ÏT¹»=ä/\§I>vE¸³¾ÂçºÈFWÿAhªí3Ny¢2y(ïW¼\ÏË|8¹œ5¬²´¡ˆS°ÀëÊN§ËqÖ©õÖJ­iµ,(˜6€zÒËû¼bÚË¡?„H  ý—g€ò;õ¯£³}ÃçB|gW—¯¸:'õúû&º§·À=ù7»ŠPú/BO¸ÿ·_ÙJöÓ˜M! óOwÐKÌÿÀeX(Ú_YYGZÜ´…Îó ³[„! ÷áÂi \§NJôY¾æa(ÖXó\$WʾË=£³Ççh8èWŽŸ=~|1Üšx éMEÅ´ ½?^Ky±ÿ;3çÆ}ºj}O´zoiÀsh”æ¶N•öÌu'ÿ˜6ûrŸ…ƒªÈ¬‰@ƒ)Êxü³¦ÊÔõ8nÜ8O üûˆž­«Œ#9¯¤*Î=óÒ&­8’ÛQ]Ýy°’×0/|8åÍêÒ-Ç>˜é: /¨¾áj/¬D7|3«ëسÆï(®o°fÍ€šw®ÓMgë×1ïl”ixN…ßÔ:ö½u‘k¶fúþ …zz$Q¦Ò¾ÅNÏž‚DóÒ&/­oùó£;/e·O3(ÛszáÚÚò´{ò†Šéjó;¨6˜ ·åJXŠ?-›Š‹¢ÑŸš…ÿ‡økT²|²Éí¦A‘}f;L³§|évßVÂu¹`p¯5˜ã ò$¦NOŽtÄlõhùwÀqšê¢³9M°¡e¼kÄ<ßrÚžƒsC–›L°y›k:Ã4n gÛ Òÿ ùaS”Ééxׯè)“âœÑ™…TÒÞ«ùÜpgØH]Ú½c·Í¡¨Ïj¤Á]LjÁ¥cì$Ö½0Ï —ã3ô¼Œêììúü.}w?ÓÐÒpújièâLù|§/c9î¥ÇF¦¤¥ut¦|µK›Ù[Q|'ÎOK^d§ ÕVî³P‘9µ!Ð >ÊC^^Ø÷Vm}´jÓ¼+ôKkÊ€¾7^ÆïWüC\s_àzü/òúÇX|1ÊUPó1õÜÇYZz±az×ãÞU•ÜúÄË}Vz’·®p͇? ~aáÕ˜ˆýíp–„.Å?ÖÞ’Ô«º20ò÷DÝÐ~B¿ÑÝh¸e¥áô:9v/LŸìÿâ®N”j70Mju-VüÑ…»<3-%¬Š$¬\ŸÀõ¢êø„ëØÈÔŒÂâ3£¨‡„«Œ#IîâÊb(³—…³Îè–xùƒ-Ž! ÜŒ!¸{ŽÕŠ‚§OoQ¢Ä¶¥ÓrزZñx}ê}^èwE¹Î}Ò}wËc7–¤ÔŒ`e.€[Âèú–QS˜A}ÛdzwЮüöÌ"õ­óÑžŸ—¥ÆŠ{ÛÃÍ!&¢E«q·<îrþåžÝRI0=ìjQß²øþóÐÌO?xÿîŠþñ²K¯Ébû½ÿþrãÓ…j_î³P‘95h× (ÉÝkªHý›Ý‚•îßççºÇÀ'°Ná¨\õgTÊŒ~yOª1Éa¿§0CÈËxÊ=¡t_DLœÈã Â6Ö ~ ÃGò¶’ÌÕà—?¦¹êKí¶*ªU«èšÚPƒ` +³ZÓï“^ÒTOÀ4}!¿Ö++±H+âr®(ÛSVV‡ÚÆ•)ÇÙoPmÖ²k2$÷Pµ•”û,J’&DQƯÒPÔ·Jï‚ä ñqð—aøô{¡œT¹r’,æt° WÎ ÇÆLÚ¦¸ÐøÓa½±0#åaN_NÀ­%<YÙï·ìÍrk_ïƒÂÚ¶Ò2ÂZDc/s}x eóŸ!€²ˆ-¤z'„Iì6L…>ñ"YhE9 ÍŠÃìzÅiy°t®¨%òQîD>;/Æ<“i†¾ôòüŒžº©ÊÀ+ø »ÀbÌ+ý‰B„e*pàÊb í‡ÖïN p§­$s‚À ¹=°<øWd *æ¼¼äq°¸vÊ>03û#Ȫr·ŠÕÊ,¿¼ ÛPAºaV£ûÙ2¿{Ï´Ó`q6/Ò÷y̵™ñùªõç¸Ý‹¾Ù©­‡­ÐVºrÜ9ì•£,Eç²*V†¯Èxd ñá’X§ê¸K7ôE˜MàJ¬Ì0¯eœëþð LÓ¼ö\ïæál¬ò(Ãg"[4 î(Ú™‘©ìBe:…±B'&›ÿ}ë-¢…ÀQM†Æð‡7'í‚âøKXK2ɲZS†J1E ÜI‡þJ—µ ÈjÄC„S¬éWV–Û \yŒW' øûG„âœxwe£ø¡LWÛ—”:ýx¬ž´Sþôïï.Å•¢¨Tkkie«•Ùu«Eì,Öö¢ãýº±Ç³7ÿ,¸‹üÌþËVd¸Šü3 \›¥o:í+¹`pŸ8öÁ´Š•£Ê/ÄP9+S‚ëè;ˆêŸ˜šñw«eÿðNgWr_ÓAS`‘þçÁ|ïæ©ÓN LÓœö.GéGE˜…s™kD÷ü&LâE¬hzÊz¾ÂU1ØN‚~G…«"WÚh‹2W s}>õæ¯}kήýÜ–-œµV+“ÌÒtS_à3±‚îë컋{óí´°è–[]+˜•Çì¼uÙbîÊBñÜŒi«îF;¡kÁr.w¨2! Á´ƒ ±U1Ÿ1}ƛ̛ӎœ’q ÏðÁûðóŽ…¥»=¸•Ÿ*ƒ6£€iprKðF¸"°HHý”µæW­DŸ/{ m%‡ýQpá‰HtgÖ8[@°òX0Ï3ë/ š®_OU% VNUù%¾é˜˜¤-Cm¾ GðŒ[<éNßÊ`eWw-uÏI|ÿÊä¹…Wµ_Œªò_Ó=(§6õx`ÆŒj{x¹\v ”/ûB üÊ^(„U'cÕ-·°‹ÁøêÒÔå”ï?fôŒÚä5}ú¯>2·Ù†Vò6ç/ð= ÝòiÉÏðïyî”ï`Åœå3´×XIå¸ÊVתiå1ÎWçàj÷:”бÒÒÖ,_V¬ò#1Æð:Ë«"cMmà©èði0.oÁZ»#qJ†åë4Lø]›ÇâSÈr»`ñn÷p¯‰Xö3¢Èï—ìÊQUT±\´Ù­ýýà’«i¬s׿î/ÜŒÅW²òz6¢›³PR(—©™ý¸ü>íU¼€—„ºY`7ãÂq%›ƒ•‹A©ÃágžÇSÿÙyøEž˜š>í“Uë¿Ç2ÉßããhŽ} fO†þ’‚}EŸòˆåœú´}¬âçsz–¶þgSËù û/T¦t×FOuˆ™ EZ!¯Ð¹û½Ë,“õ5Dãøûð·ÿ1/·ä\󟳔¶&9v:ÙaT×XtÖ…z´]Vc ‰ê®å±S§¶Ãõú¥GËý§×a‘œ°L¸š)|¯áýÕ,ð½[v]'UV^U2*K;Ú=­îë•}ØyeÇjºg˦<éX÷Œ®hsÚ¶ú@¾÷Ä}?"5ãÆ@Yö>ž5ï”ìÎ{Õþ-[!*0º5lôBæs˜Sø¶P” %¡˜ÎóÖÞt§å ™Áʨju­šV V~Åt¶U«!V<ª® lÝÛéÚØfArò®Šu¬ÍïP®X.÷Ì{ð¾=¶›Jà±æ¸¿xNtW³¸ƒî¨CˆÚ÷UÌiç^0lØ—AYã­—¨iÂýCiït*cì9µÑóp>>ªFwv ½‘}ó±Dô32¿Ç 2 G§Ìè¯;u…ÝfJ_òŸãØC8öe` @ÿ \Ó:;‡žkËÀ ”?d¦%—›í%XylqúdÕ†ßUЏ(3ýÜ Y[î \m/åY¦¢lêâèò\–ž=xaÚ¤¹N–O¼a^· #å/ÁÈ l‡ìY2ªcÈ0Ÿ U­1(öŸ““ô‚‘Wݵ eñIø0nƒÞLËr\ ­P‘gÍwߟ3Ê~Fºœï“QÓ¦µÒ ôµïì_q®ýêdÖoDêÔsá:÷$z—`ptÞ‚´”dûxUÇØ˜TÕ=cçµ·5ÕƒÛ£kæt Ú>÷äÅNgëox‘°²ùó¿¡níû.Ëͽ¹v|)êëÂlH×ÙåÈV„‚@ƒY”íÊF¤$X¬Þ±×cëQLuxc(É\gžbª²Áy_Õò¼õh«5FC(ÉvÛªj[‹ë«$s<Í\(–W­È”_G‹’Ìm¿üîâ.Åy)¾x÷UdQÛßøðü Kì^¬’Ìò]ä|>üüb*§X¤7Uå ûQ4ßz‘!áÜô ¿Ú¾å|®Pî86å¸Ï\ëÞ«~&½Åú¯ix?XyŸ­ÞtìoÛXIæ|Ö¢>& d7 þ]Ö}½âˆZävßVb+É| µ,A]¬níšäpz G.äÏwŸŠàÃnb°J2—WݵÌ÷Ct4½Èé,ØMÝ{ ÿæÞOû>Q< >¤¤§‹†êd¦s8\¿ªÎ¨ ¡$¯ŒçýªŽUwÏT”QS=tÝåÄšq ÊòÇöJºóSR²0fGWwåµ²e²ÕcanÅ@zÿX"û˜l…@(4¸¢Ì‹‚ ˆn…ùv•9un€¢ìÂt`­¹5ñ½:ËŒB ™¸øßϦ3â,(ËkëÚ$(«o'˜mÎ6îàÁÚÈx:mâZVv1¯7Š/¶cè©YykºCS9®\ ü`‹2þ¹ÄIŸW<õ´³iªÛìxŒ<ÞŠ~™v|à¶:y†bt†L¿<+Ÿbî(¡â®¼¿Ë˜: /Ü5 Ü÷íåß–oɪ “@IDATtêôÁÜõ‹EîEÙ39¾&9œF‘M`r¢‘ ƒÎh\ÔuÐZÄ–äI‰F­Ü©^˼º´~"9ùÐ8X—Ñ;ë¿F¹§õJš’~¥æ;ðo¤}Ö¾Žm™ÁȰӲÁÁ2:ðýY!Tw¬ª{&PDMõp»¿p²"­Ä:ßÌÇû<®Ó'åsìc%šw9Õñ¨*¾·%ÐhpE™›ð:fKXwKÒ=˜Yâb¼¸‚~¹ãI¡á¥¼ ÎŒ¼êæÖÀ±Ð#‰BàÈ#pŽž -]]NÅË= ÷IaÐ-P(ËAê-—?`^{ö„½Aˆ­I¾ƒœo@yƒ9ÉÇ¡[ôÓ0±(y˜Ò‘”’q?üÛ—±%í0™&%¨ªéo‹ÏUˆ—aËÃÒDT'O5°B¦Yž ¬S…¤+–¥u¼ ³óXV,™íÜÐŽLw?g@þ#‚ȲD×$' :²{˜œdÌS×ɸŸ>­M3~±¢DœXKreò¯å1ÍÅuXîþ2YÕÐ?è>}‚¡Ó£ˆ?ÖÕO*Ê FFÅ<µý]Õ=(§¦zdß\í|yàúœß¨§™Åôû{ó³:>n“ ,Cö…@( 4Š¢l7`ÍÍ#>Y{Kâ`‡â¸7Lò%Úi°5Ðå¹R%å!(Õ½×Þ’”ôí·Ö»›9@¾ì fAà¬ñ;Š/Ÿ`L‰ˆ‰ëAªr/ѰÃ&!>ï’ªÞÔ³ç€^—NÐ_ 5€yéÖÇD+§ÃZì2%õy/ú匜’~-^êWQ·¶þ_àqزö.¼ã –‚§i÷k’‡:ì‡Ì #çÍxÓa`ÿJ(ö—$$DüÏ–Éݼì# ¿ä¶Xæú}ŒP]l«FŽW¶ÍƒÀÄDïÚɉæEN‡ótô‚ÎÅõ·¹²–á~Ú„Ô9ªÃyÒ_19ѳ¡²tÁÆU¼–ÙõkøV;T‹R)¦ÿ~ÀušÿÜ~.gä…¦n¾:Ò1èPj¸u!#0}]ö«¼g„ÕXþ`%‡ÿƒÕÎêÉÉ}Öä÷1–Áê}âÁƒ`’DŽvi–•¾˜ìÉÅûvÙ P@ofãî¶E ød½„.z¾£³Xïd*züóâ£ZlùvøðâÆ­¥”.ŽÞ•Ï’³øoyæÉ®ý¹kzN£¬O:)Μ‹Çí(½ït$ ºC§Öʺ‰ጰ,'£L/ÔÓurÇĨ?qØ‚?¥EAùØ EÛ?+…¦i½ð±¼½²Š#Lç…|QØ'³ôP"Õ¬]…»þ Ù_T5 “+ù?Y¾ŒÌÑîG:êºQ¥[¶l› wú~@‹øf?ß²e‘VÔÝIf >äÐã»mb"¯¸Ç¯²ú÷þWu-ãU¹“•ù)Ö=·‹Þ"k°)×Ëf•yÏÔŒaˆ[cÇó6XyêºxÏpåTUž3Ü ëäìtg`z¬g0Ó4¨Öó„i*¦I5Íþ¤åìæÕpñ!î@Qž5<М§'• BB Q-Ê•µ`íðÛ²¹õΕ«nIú~Í-#~%¹2J'‚#pJÒ ßÅx6ò%—Üç[qɽÅÛË>Nƒ‚TlÙ‚•çŸ1Îè…,ndê´a÷yJq9¯,çs‰cL„~+Ú"ÈËÇÔpQŠÓ1œ§*,Ô Néì<ó¯Vüã3¼Ciûž™˜“v”‹8(Ò8Tǵe«?V*ÇÎ+[!PWÕ]ËÎÓ=ZÞxždò•´‚+‘›-µc¦NmƒûáKÜ+<3K î;[Í4vQ`ó•¿Zx d(êô}U2¸¾˜Žm<µØŸW4­u¨îž ¶hÃMp'yÜ.œŸ#¾ÂiPž÷ ÷'Ë~^à±2Óä=d§“­B@#Ž+ öB=ÁVžÝ!xØÀôlÙ­¸2_eéóØûU¥ f3ÎË–:ž7Ù–WqŒœŠyä·¨^¡Ž¯ÍŠ2x•;{¥ÒÀc¬HþæýÊd”ÆÙ½7s÷»º{&ØzW’¤B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€B@! „€a& „Y~Ðâ›õë`ÓÔ/ Ó‚Lñý<…ÔßMÕø...öãI½sƒ¦„I)i—ªŠsϼ´I+ì"Üî/œ;µoÿL¦Ñ":Fùð‰ääÝö1Ù6m|>Ź{~Ú¤•M»¦µ«ÛýGÔ¾‚˜†9”H9V1©•©˜ºBJþÖªªùåì™g}§(8ÒˆïlmÙmJ¬óy“&°«’è~´­ª_Kª²wÞCÉo7v=ízɶzöù4â\‹LœØèÏëêk[»£kÏžÞÝÔ¼›LŠÒ/Ï“ÌBl·âo…Ãùáq_OØY;©¡O=Æ=õXŸÏ8~AFÊëÒ“R§o6Tq(ß-p'ÿxLö›.æúŽjºÄ¯™ó𨆋1MSé÷ØšàU}Ÿ¡ûXA>,˜dÿŸŸ_ä9væêÿ¨ª3ã·{ûo8,a#Ýé§:]‡Ý…Ȳ /à¤8Þ^6qu0"P•±Dúr¤µåÑîG:fiËxÿ'<'ü¼Ð_ÃËè|hµ"Ó÷Ó¨Ôi'7v=מ9«õšÓÓ^7týSuÎÇ=Uª$WQ1(Ë*Ò\ŠëwéêÓÓ^ÚxúœšïÁ ²FM›ÖÊ£y¿Ç}yª˜ÿQåyo*Æ©’ý3qúô¸/¯U]‘ø°O¹0-ù¹ 3‡9á‘ðŽJr§Ÿ«.?ï›|ø—{vKÓg|ŠóÍ©nÅAO@ǺÈÔ|ßZ×A“oAÓ¬ ³1ªuìck/0¼:¬Df|mËLJ¼ £Wm]sæÀ§×^¿vÌ@~y ãP’?[˜‘|w@†Gök½kàЧçÏµÎØ23R^m€bê[ÄuÅüª¾B*ÿNßׯ¡¬ÖªƒF@!œßPåVWÎ"|4~õí7s ÃL,µmU—úðcPz+ºþåØñß$=9묠_¢–Ë‘ïë¿AÁ;?=ya™ä·/!ø˨¥Ÿ§*Pº›Xp»whJ«V¹êŒJ™ÑO#ïIå"›ðgÜÔ–Ô%Åíî-«æ—#RÒ.ÐMíü¶zí£úëNOë«…AÉ8¦N囿?J(÷Ôug¥_2à›”­ÁÊ0 ÍaHÛJíÉóÝ÷ç”åûw°ù+K§ç )'@^eÉ%îˆxGùèRƒðw„ÇÝãºÝ‹zÜO42=ý'½XÙªøÎD><šÑäªØàŠrÿG×ýI7ô÷ ìFÕ‡ò_¬ï\6gãeÙm «Å~tœWWî3fÄÈ÷͆Âq¾ä]°l}äŠqüëéÉ“÷泾Π´L(îFoœVÑ+`½}_ìiéì}t}=€²Ñ½hYϹÛöctÛû_ºI©é#¡xqwIw[­(Ž{3Ó&³%¥L½Î ó<Èÿ?tQ>жŸåü1(ýf¤\Çi옚> ¯ó<€ÆŒHI]w«¤¥Ìáã¥uÖgàá ºæ:"êw§ÓyÕ<÷¤Í|¼º:ðñÀ`ÕÉ4.‚eþ3“ôɰ¦ôÀñOªúÜü‡“?²ÓŽvgœ iÆãø}"ꛃ6ÌE»¿û¡'ãK´ÜÅà7¡Á¯XUéä.àùûÂôäi,cÑ¢EŽOÙð¸,¯ñ¶\ðü\QÔ¹èŽ|ƒ»ÄK|y3À÷2X(ZÂî³RuÒý™îv‘¡q³fEì+úY‰wžfhOã\ka&¬•‡)cÜ.Ó4&‘Ëy}e]UN—’Þžú®Ë66¢–;\?,ûîëLÔâŽúÔçÏãÙ»ïýVŸóØ™/'ë<œºetUÝQ]úª®\ å&%§ÿ)KKOÖXÅ_Å5áQ]ô7û<–Á.OšæsCÉgÅ¢®óðg~jÁÃÉïsºšÊä{éßTL³¬w|ßåâ~φï¿óÓSm¥Ÿøc®Uß9ÈáŽnõ®£Uªqyfú¹œ‘©ÓN4 }&vY9Uq_}„Þ%Vòj¬§±ƒÿu©W“f̃Óq=¯Å½ðö…Cú>>|øpè:¥¡ªû4iJƺî{,Z‚U?ø£&’aÌs:Õ纓­úQ©SÏÑ ã9Õ¡ŒÎ|8ù–:Ò1H÷™o¶JpòÈ„ ù?ŸÉF„“ЦBÔã£Xgì}ü"æô•=“\Á ¶ñq;°{]RJP:/<þØÄÀvp[ž¾l‹çZÑHÊmC7¾Ä9¨sOciÕÍ~†n~¹îœŒ³,MÞLsÒ÷£á%ç象U‚}Gq^~ïÀÂ= .xíÊ®‰½xgü{gE•îýsªªaYUfDAÁ}ÔQAG·ñŠŽs]FÇÏmH‡EYÒÁÖ$,¢ ¬IÐÁYœQÆÝ«# nŠ£×¹ È&È’€ì„$$©å|ÿ·’jº“Nw‡¤„·ž'©ª³Ÿ_Ÿå=ïYЦ•¤÷!TËËñ³íÁý+‘¢ÎϨL{m厊TÐÜ‚e ³šù|ýfÆÛ*¶ã̾©¬3Bû( k?k ­äPp8eo/âþ¼ìÌùnêTØ« ’É£SF²b”¾¸Ê`Xdüâ@l¼«çÌÕ]0Mû*•z É^Š©“ÜPQ6Í{u—šöwÄ}:ä‘£§NmÉ=„ä7঳4´›|>y-ŒŽf©CZİ+o̘¢æF‹A(~ÃâÏ-|-.jÑ.%¢vä*”Ó4„™Þ¦eÒIJêcàç3/@·Rb<¿Ç’Œ¤žè#>pçƒa)'‘TÎVR¨_c³ãlØýU7Œ3u‘ôWXÝ’îòt/Z/ŠN?]@x­4C>„8Á³WÅæÛXz!*ú8Ÿ‘üs4d£:б?’}¬4xax÷Ê4‰{Þƒˆc¼ÞLž»hß !†Ü ÉÈ>BòR¥‹´f²—ÐÅXüfcM˜8pÆcô0Ri ^©’Úâ×¼móÉhÐ1ˆp~ëÅóá·k¨1ê‰ê^Z¼p!¤ö3”óoz/·ŠÞ† s¶ÔäC§]ñë[,±YB›ZÈ^·m”sÕƒòßá'¬1½DOÕç’]èEKs $ç MIH&·$lPC„5‹Èö‘¿†Z: ƒ‹z É¡¹ üxô³ CÍj{& + ¼,m5‡:ÙHêî“:¶Z.”[Éeâ*W÷êñMuwônš¡Ò}¾¤_Éæò\lúghÚ²‹/NÕYØ*aèÇ¢|Þ¤¤\‚z…á]…βëÇÓã„«ÊÑi†t’È~P`ÒÏ ù\†2µýõIR„?Kvñ¥\V^Á2jÙÐÈ«7DsÙ[ m*êË㋾[w¿ç.Z=Mjß’Ú”HÃ~·=¿.ÚeKІí·-qÚ–;‘§Ì@¸=™;°GÞOB2åË2ò%WÒוg ÚŸÓJ­â÷Hð%÷‘Ú¤ÆÅ®pNöt‘[WHFÝlÓÊ÷ÇêBr¥«ðÿÀüfh/ú`ó¯p›Æy[yÛ‚$w¯aàXO!¹2½çgN…³@ݶ@' ÿ§øý@9ðòPÖÏ#ù‰·"¿†¯í sðX@e¢™¯U°„†MÜQ.^ÀûÃh÷sÝ'‹t¼Ÿ7~üvr­Ü‘}¤òÐI»l1Ì”YÖ=䯻lå<ˆözE~ ½¥$¬‚À‰öLNB?·À'Ä™n?\ÙÇÆLƒ¾w¯LSô>ŠÜFãièm'!­Yä«Ä/ÙhÝ_jÆ¿Q¶ûL:•üÓ,ÒüØ 캊Ì*/ç·è·ÐŸAhÎÌж%  þ„ÃBy¨ ®0]åšXTo“‚VUñôQžw™`Fv_¤}Ìåeý?ÏŽïu#à uóR×åæl°öõ¡†W„7Zê—¿ÝëÓ–Õ r³Æ/…ü{ôî“öï/ËÄúØç tlsrü[È)FŒ—¡C¸R3Œî¹q?’ÜgcyÅ'pÛ£»ÈŒ.TdÔ±æ&^ÊjÑŒ¸nÑ_œ‡ÊVœ$R·>9fø~âZTýÈÕ0¦“v”Œ§*PzU3©Ê¬»Ôµÿ‚ÈÕ˜‘âþÔ‘ÿ™ô^þSÑÄcª“Ox›ÞC¯J-’}¹®„œð€F0ÃuOBÃsŸ¥òé-Œ»¼Ó 0øÈׇÇ<öÛ-!F"ï+0¢Î®òû*òÕÂØ ðˇÙ>0Wº´‹=~Ðl-D§ýäˆÀ´vÓ#÷8Bûo!@ü÷Ûœe¿‚ŸÅ¦&¯Áˆ~õìÿ¦ª|]aø´sç2¾­ŠçaLßö©0¡âþ*3ºmÄ~TÈ{ðîû£‘ú³¦i7£œ,ZÅ£F-íV¡DƒnÔDÙK²,9ÿù„ñF¿:'-´¶Bi¥Þ‚öêGlä›ÙYïñ'O«­ d*ÁkæðáåhÜíŒ!¥®P™ ©ˆZ®ó múdNàQ·#‡›à’¸ã”ªGr‡Ö(^Š#-'çU*²rÎ –%[Ý‹²÷uêÕ×¶åŒÄÀ®š¥ûÈÕµ‘îq§ÁóUu‡P:ÕÓžÁè-Ôñ…¶.Áósä$Z=7|ø$h›J ŒP^}"?rÞƒPL™)UK[nÅ`:›X'aÆ&½R€UЖIW8uL{”b fu†’\ßCh[S®ÄhÐoÀû»dˆ²R£M£d²Ó-]¥eN|ÂÃ"Õןp2u¡,ecà±¹“Ñ7f{+¬Ã±W[Öb´ª¡Å;œ°ŠBœx¨! '´LTÆvèÿn±£=ã®JÓ$?°YAh»]GÑÊD룞—˪ÜPÚ¿¯mº¸Å ¸Ú?”»ñ"çågepOצÊ[ô²7n<·Á{Œ>*O°øn*^y¿"´u«•eQÚP`O"eÎ~ô½ob“4 ôÒZá³äB 8þ€÷Êz‹|a}¸;†Ñ, "[@ÑäGœDVk“ȯwÕ¥Â>…¿`&ì6øm†ßõ…®¾>½pø^w&(ŸùôªKlÇŠX9êžìp¶°HXˆ8îR,Ixó•Å+Öþ:÷èоÃ4㕤)„ì‹#êäADzžEåzÅ’Z'(µÓ`”«‡ë½Ó”¥iªÞ{²Ï÷œ#RóMsïåfÑ1ÿEˆäÙÞÔ- „ÅfIWè7!Î+È:6Èͨv˜¢ñÂAòÚ¡…Áw< ­h„T6â U‚û½¸ÿ%¯jê'Ô-¦²{#Ì]sŸûÊ¡V"î4„ùr_ =!™Þžzä‘TÐÊ‘-«œb—µj‹|½YõNë)HÇ©Þ{õ{n c¡mÅNi?tꯚ9·êš~‡­T;ôÿÔ-F ýAÇíÔmeŸ…ðvÌylüwn‚ÁAàxü® àtÌ(Ô¼ÐX†Æä@œS5]&’à™ÒЩC˜gí.Zö{„ûB¬°ûËàf$6!eÙ%Θ3 ̵÷ã·»”:}ØÕ¹ „ƉõvIolÐLªeXÚ°ÈÐô¡Xµeå&t¬sÛ¤úþ"ŒÅ§|Õ’)|šbFxïZ®p,FÑÆ*»Øm±[/ƒi>¨Þb?¾†?Ä™†pOÒXnvmDCp ™n=u±ë#4ãôÓÎI£¬+,瘿èÛ594cÆË ­5È£þDUÌh+dX¾fegn„À°ƒ\š9råHmR•TU“„ìÁšO?-7Îcâ0ˆ¦M¨·êÍÄå?4Í{mê;©íÓ“˜XÌí×N×éýG°Œ%úUµ!üZãXrˆÄì"„ªL:½(Q}-ÿQʱC ûíçìÜ,ÿBêWâ.wú(¬A±Ä*~jp û’¹ÿE’Qwæeù£‡¨Š§“.TXÙ#Gq§!,D÷%ju¸<áo¡]¹™{~ŸÿÆlÔBM¨waöÚ¾Á…Ö¤+ÑGmË øWÐQ—Ê<ØõkQhò0ƒü>´ø“?ZõC7˜o¨´ o“<÷uí£’VC³(ÓÖŶ#n€Ð¼ ð˽e–^¸|êCã\ŽcѨ214.=§}wa¼Óè-ï ÿ›¨¬ýP˜?²,1Èõ+e2*ÀA,јåýA@~FÓµk-ùëx‡Üî:{Ê4}4BǺ°[„fü ÂÛ JU¬D‡ìvþåÂr§q1ÍúºgÕý÷IÒÀZéàõS Þy¤žÐütz­gf÷{8'§#òrÒµçƒ>B05DšžÒ*:ÄCåøÓæy‰ÞèKAq⸰žº|…î7aU‘â}Lê_½Ýžt)ò$;êã–BSùº#Õ-h„4 h$ÿyCXÒQQ=_à ­»=téJßzè-äIIš^Ÿ¥Ë£ØøÐ5Äæ¨}1bY;$î®D%`ê+ ˜°>ÿiÝhö °ì¹Í™ØÏMÛá–ªŒín·ÁªO8Y£YÑ9æ]|§ãì› ìïÛw bÃÀÌI¹Þ〈Àuò»ÄŸÇò¥»iÚÒ)µï€Õ:tv_†8 yTÔ^D®q¦!$0÷Q·°ðÐSþÝëpëé5çöXÙ¸¼|×˰4Š•ש TB¾‰vá7öúKQWÊ®>û47Ÿx†ê-üBðZ§j´IARœAz‰mÚÓ‚fQ05ýGÉ'£nçúý5~—(^̪ش@¹Om°CB¸ívï+£ÁgÜ) P§†`IÙ”FºKÏÔGQ¢×t#Uï†eL_  üÇ4~FÕ:”»å¡R +_µLq/Å…;ÿ ‰?Uo¯É®Äwú ”0Ií°«ió‡|„Õ§0Kz9|žï!3¤Ì¡} ˜¡‘¯^uNÏ%T?Ü~K`†FI·jŽùD„%É"¬N)]•S `ëT¤6‰Ü ®:õQ3ËhÆ5÷ ÿëÐΧ#];mËºÏ ‹ÿÕ™@ðª³Ï:xøEžòøîfÒÌ&ìrÔmû«º„O•ÚÎu(…}È:ÜUHc{C;½/u ÜB ^†ýÕ¸ªÎ½ZåÑA½’È›1'¾#ø}HJïÞ´L ¿‘ ¦yZÚwwéAñ-”Ð_æƯŽäµq-ÎÏ<…ÖíV?ÂŽ¦¯7 ãª2ÄÏý=*è•Þ†¡Hn‘o xÃ:_,©“4Uü8´àh0µ×I3‰Nâ£B{iÊv3çwhtZvñuý”ÂÃ~Ë/…cž”˜xFhÞѱ\ƒŽ:2‹j A9˜M›0¡ ïh—©—Óòòúy2ª9=j^m¡nFyuY‰I”º`ÄØŸ:}òEUšŽøb™ûØè˜ê݃Êt6|`£gì2-ä*ïàHnª4Ö¯ÂîU <1@´FãùŽúÄÙYëóN¡ó™Úî,»K®~% ÏEŠ›ÌPßÖ¢öd_Ÿ4D Ìâ©§ØXkUf Åà³ë~¯Dº ÎÝïÆ¡á|jÇ™Ž*¸拼é_؉Šush:èlßr³¢·£W®¹ µ‹ølhÚûÔÞƒ¥êh¢G£]|*¢;’=Ê öpø®˜uÏzE;ôßµ¥±!̱äÂÏ«kXX~¶s÷¶K;*+±}TÕ a6¸ÍØ{ bõ6kýZXŸþ‚þóØÓñ –ï÷ÂÁ>škІ”›OØè™Õv¯OErê r¡Õæëp4Š \Züý/0¢Ž¸yîpÉú+"™‡šÑ‡ 0uøy£ï¦ÂfË[б9˜*Nƒ›É䮫áÿSý_™–zð²hcÅ6kjw)Íós³2„†U—g:‡1Iø¶Óô@qvh7×gaÒ#O,QC€^¯íþi‰ƒÍ”S~Q’­VѨ0Z\ºÔž·„õ>´¬½±*¿6·]µË>€ ¹_lš–È™ :·ÿNߺçœd½ÙT‘듆Úâô ßtK™÷¡‘ UAžìк°|gñy:¾º87küן’t¿ \U >"EOYT¢J^„ù-šp\m?iÓÑý–Âd¢aú¸jÊ_\}ö©+°ñå#a:O¦eNÎHÖ›o*·<ˆŽ÷bL™÷­-m‘ÌÛ´ô Ü[d~%¶ì ¥<##¹9ZÌM\ÍgB/«¢‚êÔ†Ú"¡Í¦–}±2’>ÍŒÞE›°lè^”¥.Ø„ó>ù‹« ÔAóAr®Ki“ò) ÝuûË: #(ªoœTÎP^ÿŒÝÿCþERóp14IºÔg[޽ƒÕÌäæ2W¯H1ËEÉ4Åœ¨|Ǭ§©¾å―‚öëJœÞñq0½š;ø|}fsÒ|墫vúâB{m'4@·£qÛ@×½¡ÿ õiÈ@θT#%¯D”u¨À #Ðm¯]O|+fŒúBéÀ̉¿ÃÔôBì%X¬ó!þhf ¹a8åšn¥ÑFBÏúª^Ý·x»g–¨û–K§¥ìsŠ/LTøUáö¡M}òŸ‡N0©ýnX~Sf‹¿í"zTÚŸÿB(;eñÒ´£Ü–ˆ>jP`je›ÝsŸç~­34TŸRѯBZ¿>*ïññcéȾr©fA0\äí ªžw7!f¢ˆ½Aë;ûú¼ºËúìd[$%Íͳ&fÙ` ³xú|½KÀ?“Nò>¢Dí„Ï%h‚Ç" W™CQiB{ ³6O£N*›û£—rÂ…=+Ü=D»®Å u0ÂQ¥L¬>jˆʙأн“Ñýéýe!¯¹'ข{0Mü7Ì$þ”Ú‰S}1.Å4•º¶Ðú¬É6h,FÉž€)Ü'É«+¨5— U>ïû8ªê £*Ö`}lDVŒè‚ÖʽËLs1*Ö4r; èÝB{‡WAplŽ—ÒfaÍñtÓÚ³ë™v¢sɵ5¦×£^4m üöjÝ*éåÚ“`øô›P1[âø©«-;бÆó• QÖžüÔ' µÅI_/C>o@únÁÆ”°Ùð4 ‹ÁóbÏìI‹Þƒ‡ŸÐiºÚÚij¯‘§6èÔ?ñÜ‚íFï‰Îþ_žu¤É¾V· a2ñå©¥X¾¦)±þëÎÚ§Ì=ßáwZãŠͤõIGƒF÷£ö‚vðÌFH\Ô:…%K-P‡ž¢òŠß®óŠ_ K=§Hü:/0~¥/ž2p8ù@gtWÉž’ j °îq:àœ\ñhCÄ™dèB›uÂz«ú±¡i¥º‡ãïÄ ,½¼Ä)ÀZÌŸ,Ke4DBã }ŽUO©#Çï´_¯á7ÙJ»äɿᨅŒ÷@r—]™{z‹tDcO§™€}åU533eìÖ«d‹°ìïà·NÛ¹Þk³<·±î´ ËGÇïõ²»<¬šÉcÐÞvÁ{ËlÓÚú÷骦+ŧ!‰Ua?ÁšMkN©† ì‚×™¶¥*sG Îl/…0õØ“l$ ‡‰ê£¤Àgt”=K`v¡ŸB*h_ɳyYãè^¯þ4š´Ü|e—Rt%uhý8–"¼øçc]mI9¾ h‹Š«È.VÙ'7u½âቒÞBÙ ŠÍÍho¶xq@xzêŽY|Å3#Ùí‚vð O¨&;Ñ: õ 3>ÓÅæûñ»’âí]l&ÎôüÆsÕG9Òi9gJYPŠ´îÆ,PÚ§¡˜¦½+ÚÌnçëD…Ïá2£Àôé—íÁW®è¬é„\ø"匄Ì2£”@ª¡ÏÇYÑ ™ùD¸{Nh’°úz”"åd1&A ÑåUô^޳_K5]ׯ$"\“ Ít#˜2nàKÊïúþò’¿5p¨8ª tûlÜ^œu;9‰ÄÆØ':½ß0ÇŒ&"}&`µh4A™’ “}ÃpÛY{rÃFÓf­Ù»ÁÖRÒ™°t†c¬”Äë.V8ž}]Â1mZ µéùt§³’#™³YÓ!ðì” 7c6呆Ì-çФ~?>Çõ<òxã¤ó|ã-‹tfo¬p©n6dx^|tî¨÷éNç¯Çªs‘ü±Ù±E@;¹çTlêkÐÙIh“—œuJÏY E"Þ¾"^wôeËxÊv]êr<õ4ÞzÜPÜ8&p¸ååÐÄõš¶òrËvðy[Õ,ÔüpžÑ}hœ¢ß°òöÞq¯Õ˜tšiÚÿ”šx_Ëq¿¢EqÓÊKmZCFéjƒÿÙyY5´jñº£0k‹‹ì¼+žðð…É`¶ëB_b+{¾”ÿÍ Í/ËËñ?ä…åÝeæÜ‡/aÍïêëë‹ôqÏß›a#—ÎSJÅü0HìÜâì)ï›9í²¿Äv{ÈÊÛí¶£žóùäsÿñl¨Ü♾(VŠ¿•òäˆôe·ô 9Wã‹s1”Þ‰¨$£Qº#?;ó/ºÓG;T±õ4¾îu^KQ÷÷†>lN`ÜúPwôOxøúغf>_¿r«âF|)s(¾·w¾`5}^VÆK¡aÊÎîj—©š’CPßþjÇÏMÀʾ“OqLs¾hÖµÞ¹“r£–’|YïÝoX4(+û©è¸ï?/Çü¨P<}ů;új£²U6êê§JÖ{UW£Ï}‘úŒXuÙë£D c~<õ”û¨xK»;ÄÔœ6t"Iû‹ÏXÞˆeEõ é{ɪÀºÉ3'þʲ¬×à÷³êqã´ðy×wÐA_"R}¿„à ¯~®»hq…†+^¤É•·zÒKRi¥ÒУñ¼4?;ãGŠ^ƒüYW††7,0å$-w‚ï¦Ps~nº.¿ä²A(»¹õÉ!:K ÔUHÆ'osðÉÔßÁ/>s|èJÏȾÚîË»}Ï™—y!ʰ)·îºÿ‹Ê'Òd9Žz^Šäó³ý}tMÃàUÖÌKÛ¶¥ºÐÞ@Ù?—Ê?:öÿ±,gäá„78} >3[030f+üo4|m/Eûê)âV‰Ï¿º_è Ø>(f£~bÄM˜@ïÏÆnÖ4ãJdqC}²‰Aç÷šÏwe„dÌh–ïØÿ Ê[ðSÉ^bõuuçÓä:Ý—|Õ;ô{H¡Zl3—þÖ Ç»ÇªË¡}”ˆ£žrå‘åû±B Ñe³æ‘Þú4í˜âú¢® ÐùVÀßcçt;û¿¾}¤S>-­ë¾ï5£Y¡‰5Õã…Fé–”ájÒÜo´+ñ/eWÜr¸î¢Åf¬xùs.‡mýœÀ£Ûçdù>/0~ùG# íŸøÒ¢whxÐÍF#?^r¨9?7]´LbÖôËcäAŒýuΩ”ëuM^1sZŸêê×'Œ¿çeùo…¿°3h©\£žþÃÓNiJüêmÕÃ_üÝú‹!ônÎË~tÙå>‘±å½÷ˆÀ´v¡nI›=þ<3iÈåB8½¼wïOx¶)îFÃçn¬‚€ðþœÀCî®\¿¿Z5[ÛVÔÖ o`fΨkß@Û¼Ù3ã{Ó'Ðûóqë[%ùh€·àpr‹2óÃHú% Ýuñ?30¼HHß@Í—ü\u±ú Ï}¼îfƯ­ö‰ì2̨´öÂñî±êrhO=å>Ê#Ë÷c…ú‹#s­Ù{ýšQg_aï÷hŒ‚Óµµ§F–¡ã}AKÒÏZ;úœ'ç+|Ô(Пtµ´‡b¢1FðížÍÈø)h*åfG¨“ƒïxˆ×ù©-®º†‡/°Ý´ÕØ-íŽâ•ºÎIzaR§Žœ|›—5öÿ¥¨k˜=?|o:  þÖãôš|ef_¬œ¡ÐoÄé”?ëpÖ3Oõù<–ûHö³³Æ®¤A–L ÊÐKvQJ †›PƒÂê¹v¤Óõ?èÎ Aª­eâàI¡¡Õx6Åõ(ãÁ²ïÙÇ /ø'ë©[d ãÏwO L<BúOx Írv¿8¹ýD @¸>y Ž“;mî;ûËÌ;ðÓ_…¾ç(iÐ×~¡8('ïJÍè{ÖòÌ{ÏX:æ@í®k·ÉÏû]²¯"¬>ÅÛ÷Äë΋=-ðTûổ—¦gf?ŽÌ¬ôäî£×åÚú(7Žjõ”û(<ß%迎ÜE,b'!ðE¬]îuµýñ|:Ôθ'¡#,Bkñ:þ/Zû’?X>üôz-×@˜¯‡ŸÓý|y¨%:ÓéˆSBÍâuê'Ús¬ðhóD¡¹öÆd_«¶Òý9àÓàŸ‘–™â’‘}²é¨Í:¶º2Zœl×´ <õTŸÈáèa3ÖeˆM»®Êéƒ÷øÚbe£Æí@§¿òÇÏL½ä˪:ØðP”h¥é*8ãcúš•(³¬Mõˆ4GµB=º#{huK„]S³åùÈ9Ƕœ[eª¯ŸgæÝc…Wà,»mÊWsÇÛëù¡;­ -ßQ”§K5Â3/³*ò„4F΃6;ÍŸãóý8#pÖòŒåÖýrÊIeªâ:t[ ¿8õ¨9´'%˜¡Ü„2õµôé {-ÉØ–<±ú /ÎxÝyî…]~-–>=Œô÷D?;¡E+-¬.ºî¢Ôåh}TõzÊ}T:?cލ Ê k—Wâþý¢©Wlî¡ rÁ wZ*é„u¦ñº ã!VxÖúëÑ€-s§ãBÂ4!û7X‡<@ž|b?ÏóÞyx5cذ üIêØ ;¯…w¾ŽG3‡ŸN¿UýEDðìSÄúå=PO’Ð…Ù Çh„Õ'7"MîQŽº«Œ\µTºªé–COv2­ò—tC»=wìØšËLb…ç(ÌÐè5fh°.ô9?ïäædºZê´Ìœ?@K¾:ÿ‰±_áObz[HuŽáFå¿A8q ÇÓ—»kÚŸGŠé¯Q¯X}…—˜xÝyîó³2þŽç¿W÷¿Pº·ô¼?æÙÓ=Z]®­ŠTO¹ ¥ÊÏÇ#¶ôâhƒ„¾¯F¼^º°ì¢;àÔØP¯;/œX÷èáÙwcÂ7¬S§¶-ÍS´ßz'<4qâ ˜¼a-ÄZý!ÞöbËŽ=iþ¬kc¥í™@CÀ²ŽMèb»{ábí©˜>ªQŸ„2~ÄzúS=wt¬FwÝd²Và™yw:>Î2+ÞÆé/æ2Vxæa÷(áUG¥úu6:×:“_Ô—©ÊòsüOÃr6ö©4¯>!í•ržÇtNÐ ?0F$½¯8”xÝò!ÄÌáÃË1€ü+ÖP]jNÏÑërÍ>*R=å>ª:U~?–° \õk¡s~Ûþ WZ·…)ê_kFòÛî{Fömtv1=ÇëŽÜÖvÑ®ûÁþ)=ɾ¶ðè\Wl×ëÓYëAZA÷”9é|, ™%}ÆM¡ë©g¿›’Z†þÁî.Fß¶´Y©Ê;ߘ@£Єþ ¦¥ï¢©ÙÊHQ·4ñ=ÓñU'äÜ@Ï´ž·6i™“Ϧ÷íNÎÍXz±‚–F„º£úWj•¼‰úòTè¦>òê.ZxÒÜy3â÷ûËÈ]iÙc¡%>©‹/#½Ò¤ò?„æ_‡Õ')ó1ËôNÜꎟ™@c¨­¯ øãéSª» U ¹k÷…ý;Êî44¼Úêr¤>ª¶zÊ}Tc•Ž'Žš¥‰È\]ÂL2ZO.·Š^Æy_ ³¬-vÿè¤ ªøÅ»KÿQ²·ŒÖz.×]´¸mK¢‚vù®-Œƒ˜ÀQ@ ¶¾‚’OŸR݉ӣ°äf"·šK[bÏËr#•Žg ¯¶º¼ï§¢;á4¬ŠVOóÿKaóŘÀ1N€FÉÕ¿ÌçNUËW¼îªys_+ýzš¶J‘‹ä—͘À±D€Î$®þ.ª_ƒ'M Áæå‡>*â=Ó½6w¡n¢¹«^uüÎŽE‘úŠxû”êîhmríý[x©.‹ü8ÍL€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ ÷d} òg t„¸;R8š/æfgÎ µ;Ú܇¦Ÿ™`L€ 0&À˜€G²lý.WHVò¼ú…¾™`L€ 0&À˜@#æÏú˜þšX¶8;L€ 0&À˜`Ç9zk”s~œ}&À˜`L€ 0&J€å&úÃr¶˜`L€ 0&ÀêGÀ¨Ÿw!hÃ^}Ãh,ÿ´‘⪾Á°±âçx˜`L€ 0&ÀŽõ”%¡3ätް“8ŽŸ‹Sʘ`L€ 0&ÐXxéEc‘æx˜`L€ 0&ÀŽ),(S?'– 0&À˜`L ±° Ü€¤ÓýY>œ“Ó1Zirú¥gN¼4š¶cL€ 0&À˜8òê-(Ó9o“Ü‘ÏNÃ¥@)%fd¿–îŸxk¼¡ÂË܃¥êô¨îçÊqîŒê†-™`L€ 0&ÀŽ8zoækªägf÷“R\§„Câ׎ø/Å `L€ 0&À˜hTõÖ(7jjë2û"ýÅ …”³„ç§&öŠÇ»aL€ 0&À˜h:ê­Q>–PÄ{”ÝC'ž`–8¿Ñ¥ÞÇvOaÙ@>G†æuЄœëÇ™³Ó•k4M‡%åBÉ ³¡)]ÊMsL®€á^©‰¿)%LÏÁˆiÓRŠw—þG¶4.VÅÖl!ÔRÉ©ù9þ‰N™Òrïs:Ìn@˜>)ÕB_sý³ÇßMþÓ'ä\íØê!˜_Ž%{pÿJ¤h£ó32¶E³óâöîCOv²,3€¥&ý`ÖZô¯…&gå?‘ñ¹”9é|DZ§âñüi 阗yÙ äœg™ê)²ƒy‰ra £Åèg#ö‘=å¯dOÉ7†Hú)ÌYR¨ ‘Öíà0;?Ë?ƒÜÐ+¯•®ø?`L€ 0&À—Àq¥QŽ­yй‡„ß¹Yã¾Öt1þî $yþrβmõàÍ©¾“4¡O†œ!0ÅsCkœ+¬Š÷„T¦áKê%|ò6ÉÝ ¡®OÖmA¨ªØ|[HñÊKôT}.…!ù Øu–†v“Ï'¯EpÍRç%² æ7ƒþðF»Ÿë>ù[Óïç¿=šù­~™fÅcB)ÝçKú•l.Ï•B›ghÚr7(0ég¶²–!m[5Ÿ¸>Iªóϳž„dØÉÕ†ôõAFB>­Ô*~òNn(ÈsOK™¯ÁÝ‹ÉFRoXÌG|9ƒ2sî#7tEËk¥ þϘ`L€ 0Æ'p\i”ãÆëˆšT¹ä¾³Öç{©½Ý^w ^™cªa€W‡h¨_˜9i“P֗Ъ’1豉WAH<Û'“î˜xt;Œ¶C€¼?-3ç )¼Úµq^–”g†M„—Aè¼R3Œî¹q?’yzFv¶£Ô'iþ¬î»ÅÒ\wUšþãœÀCŰ^AÐD‹a)ík³£p"\çAüIUÉ:¸Û¶œ‘RÈ‚ülÿ}!þ6Ò³cÚ£-ó²3†VÙ}?ÔŸµ¦\‰ ÐBß³w«Ì…ÒÄ+ó²2þTõ>yèí8`(Ä ±òšŸùƒß™`L€ 0&ИX£\6 nJ¨^Ž”Òü9# ­eð¬a³í(Z~QyIÕš×½Wº§¶M&a5xA¨í‰e ;ædùÞ3”Bò*ïÝ»ëšöwï™îpv6”²ËzBå›ôçH5†ì°Äã´™1[Ö(áØ‹ú³ÞƱt×{ZÜhvä¿úehúP¤ê¶´Œì•i™ÙCiÄ!7ª7„ý½‡=õFÂìfegBˆ–ë•rÎ u©+µ8ô]“Úb 4zY¬¼†úãg&À˜`L€ 4&z Ê î r™±HqÅy”–ÈR©S¡7½þ 9Ý„ðúÓR„Êp¥楡q\zÒIŒ±°êºbhvÃÜ $僞ï®+}«÷ìÞ¥L†yPjÚ,ïò3š®]ëhÉ_“›üìŒéFªÞ ûHß_Ó3s>ó–‡D³ ‹/´¼¤‹/ãt¥‹ ¶ïÛw b´ãUºS”¬=®y!N *jØHUzØL…#õð<;ÊÝõG^kÄÁL€ 0&À˜haÍáIJüàp¼7ªH±wWE8/RÄÃ3Z•[ûo‡†õ†¼ìÌOCÝ@«û¥mÛÂ,?@ãû«PûEß®ù´¯Zå Òüª Øl÷³´ÀäSòc7{naßÏ…Þ{¤;ÖUXËÜÞÐÄÎ9ŒÿDrCfsÇÛ‹[´À3ö¨X½ÍZ?ï¯Ä²#ûÐ+$à¿JÐ,?x4žïÀ€açƒá IDAT-Dûþ¡n½gÐ_BؽÙ{§{åæÅŠÞŽ.‚Zt2ÇúmÒ/§gº)®FþÜ%ñæµÒ'ÿgL€ 0&À˜@㨷 ÜxIM|LvÑ]ˆeK^Ž?LHvc–Z¾tT`Á‚Åÿ·ö9,X áypã¤ùÛìŸz*ÇÊ‚;(Z+¯ÎZ……ΚÍʲ&BXŸœä,/q h«sáb‰ç.Ò½«áÿ°ÐÌùÊ´ÔÓЂgu2üŸn³¦v—Ò1î ZÎ-p੤åf).:½#¥MʧÓGŽ<8zêÔû÷—QXEä§~̶{9– d&7—¹zEŠY.JΘð! ýoÂt† ôçŒK5RòJDY‡ œž­ú:ÑõÄ·Ââ’jÜÀ 9»ºjãÚSz Ç 4 7±ò¿0&À˜`L  h×Q•Â&>hŠŸ‹”PCoóÑV‹V¬»¾Jw“ ­­»!$yõu˜+=¿À톡݌5Éç ÓZ_^jÿášN}˜â¹©íîjx›Ë»88Í¢ÀÊ>訊5¶RƒÉVªRöt¬)Þ…5ÕÛ‘®7aül^Ö¸7£ÙEŠÏvœ»p„Û¬u.(ÚW¾Zä­™|”ܺ§~HíNœR‘!¿ Ä*þɲTÙåƯÆm–¨ÜZb•lÁzßaÐI7ŒëóÓÓMrã]ÐÐOÁ #¯ÐÊÞ«”ù%8¼ÐÿìsÉ>V^½0øÎ˜`L€ 0&@ÐL m £sˆé-L:—yØŒÉÑÜÔf—–—ç£å¤í­î&øÈ °«›Ó{4»Hî]Mt”| ›8ñÄÚòðÇÀô6tfrõp)Í3²ÔC™“{“ÅAéªîÎ{–WÏ ß™`L€ 0&ÐX¢ xñ$‚6È‘»ca­²'$ãȱ+ãÉ»©w)Ǿ²â$ÍwÖ쬱Am{ýBeßL€ 0&À˜hõ^zAäB6É5Nª9&À˜`L€ 0&`µNƒ'8Þ# 5 2 1 °ã2Ý@IDATxì]|TÅÖŸ¹w7•Þ‹Òì¨XPÄŠØË³÷ò”„bÅÉW²±bAHðYŸ~Oì½Q, ‚€‚Ò‹ôÞ!uïÞùþç&7Ùl6Én²6áL~›{ïôùÏ™93gÎÌÁ†`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€ˆAd 扳ÄÔKîuéPäõvNÇÚ÷°µõ² (ÓƒÝY'^Õ¨ƒ³ã·û®‚T´ƒº(v;s±æÕ,׺úòͦ¦¤¸Ÿm¥ùÇ %;*§XšÔ$iá C‡æ×4¾ƒ!ÜtOgŸT‡)%Û ]n—šØt^÷#_ýõ¾h•ÿ>÷˜C ¢RÄ¿•íyly´Ò)4Œë„Pc5Ÿ1i<­tB‰×ížæØ"~ëäïWZÁ9Ý»n‰&ÖþéEâ=5#ëV¥ÌÆ9žŒñáÆç5ÌצçV±ù’þýëÃ׾źœÔToUñß7jTk£H6n+ÚmäCy¤ìvfHÍ—¬ò®±ÿÅL7Ì:r»•¶Ñ›õˆb¨òæ·-æøBÊÝ™kH÷üíд{Çg¦ýfÔ Ú{ªÛsŠiˆ1EJ'˜“>ú 1yþ²).Ïçýz9  ¡Èç ”HÒÛ lÐ@—n«ø­‹á5Ê 0 ¸ý0™o€+s“ršæOOp§ÿ«xÜ÷ÒKñ…[ö¾*q¦de}š“ž¾)VóZ]¾üëC®ÛJŒ‚F¥¦ ×|mäÎbÃÙð4½RìPïÐê]Ž`†ºGwÙhdý¤„#¥j,¥üVjòiMŠû¥ÔÆ !EöŽ÷)ß)ž,ÝÀìÆLÒC2ž:VjšÃ•RlnﯡÀî)|,¤ŠGÓ- ×AÊŸ…”kþ”@PR\Y`BOÚ7˜o)vëñÅ?à.Ä([’Rê6ŸW-Hqe¦ÆjY_¾ÿþBÐÆ7Bмi[b5Ÿ5È×ã$ú®A¸¡™3&óIÒTãH8`D`¦"Œè¤dª+ë#0Ü“Á\èÂyýxÏãKƒ§ŒÈº³··à?m£9cÜ1b=¸WÏc¦Ùˆ(Ù#Ó‡ébòRfÜî76Š-mÊl"û†4‰Ùv +êó.ÔçoÁÜ‚˜ì–Oú`ÿ²<ôüó‰¹;óØv$*⥔Œ§~ÍÉö—¿ŸXy‡XùÊâ¼dÄJ–j• 2·£-´2 óu Ê{¹Ý}IuS˜k^‚máÔ1Àes à™nˆècFt+1\ˆåVwtv<-Ã¥¨rF¦%5Çùèȉ±¤Ñºo(I(-~ˆaÚ…ò]Óp¡Äíï‡+À­—e§'<ÈpÉžÖ¬„â˜Íñ˜ývõÏo(ï<óLr(þl?±&ñ ½0âL0€O!å¦q©×Êž“&MÒ«£Ñº¢©`y$:s»'Ås«ÎŽê§¦u”âÎNª.þrîJþ¸ÿÆ{Òã×G˹Õð#$Ü¥º¢†ÑG$XHy¬$¥°1®$Ûšè¤6ù±ã©éa6Õ!@ÎäËþAÃ>TjÚ€œÌôת “’îùÌæˆž_Ìöd0Ãs¯òzø‡âœh†0ûIâ!…–•íIûšÂpe= ”9öOÄ%kãŒ\Ì$¥:ÁÎ{ñ¾HhZÖÄ‘éߨiEë9Ø=úp/Öt‘Ï¥˜-,TWæ}¦/Aôþ=®›m?À¶Ú<0æƒ2F÷4”ï!Œœ.AÙ›ƒFïæo‘_Â%uDÖàq~Ç£NÃjÄÁ¿;•î¯Y þWÀà0]:nŸ9|®–ÿs;«‡Ï0'¿59×…äVœÑ$;ÓuL°[êÏå¦)†"'Á;1À¿”&¾î¨ŸùäFcúÏ »Ó5d ­›<_6w¥³y|xa©¨¼±iHsh×’âôˆa^ƒ°=QÞÕÀ¯[ùå¿ìúЄ|Ujúk¦2fƒþ|©˜ãN[RÞ7h)Ýó­éï³w…5ÝPû€®Ìó|B€ÖÅ™”â[…2[J\ ç NGÜ-†Qø#\vsË^€|¿DymÖÄÙûéÇßçïn×pøk¢'ã:Û-Ú°Ã„Š±Ýΰdâ–S¤1úT¡Œ7ß\ét\gæS]OaŠ¢ÇÑ/] m l…؉óðöIG=}‚Û-M;/Ñ|òL7t§-ZÙDt(*hOýŒ7C" ™k5\SɾþþÑÉ5GGp”Ƶ¦·p&ê³b4v)þ ÂÍ1\éE³ŽÈ²:ÿ°ôžêu5îï »-áeƒh–£C¼ÆôúþD#ëCþlc§§”÷º\#÷Wêdà¶ î ¤9áNñùÔÇ ø;LeO©µú év€tøÖ²²@¸ÍQ£Z¢¼.‡6o¬O+ñQ,ºÌS·U QÑi,%ŒÀÀ.t¥5)äùrr»3ÐHhX7Àþ°½ÑäncæbIÛ÷N_¢D$÷à{AéÏT»ìxt¿Ð Ë ?XÌI¡ƒUrpûy\ºêNºbB:E¹æ¯ˆÿztf›Ð¸ßÄï/«ãó™_ƒÙÝeÇ ŸJhÖò蚥tx8jÏÁ®1ÇøLßÈ÷UÅXA ¯iƒHyꩦþ|§Lõ2p8î3PÖwAù{ d¦ó­¥–’X18ùêÌPæ¥ ¼>uGq½ 0ýbCy!;ûÛÿ‰ôÓMŸø ôv&ÚÕõxîF×ùðï k äïß~l̘ƼY_ƒŽÆ ´Aù?@Þ?‘ m¤õ Í'hï¨Ú2cÓ|g(£½‚°£Q§ÍA S€ÏOe>«~C¯ž4!sø Å§G¼0ÌÿРêPå]ÃéL)»£\QÆbš–b™MçšK^u?¶ßN”÷ŒA®1p 7Yu"T÷½û‹ËçDlÉêOî­²ÝÂ¥ ;\m1èÎÂ€Øø×Ê¬Ø wˆ{Ô‘J¡¯ÿ=í¢¾õýÊíC¹û~n¨+†Keµ]h~GÀ4ÍÃ,©V…º¯9–øŠ(XqØ€¨MS †t´¾'ÇšGÎÔÐÑQDGæ2}jBJvö‘þ[ HT­Œü7àUs8ÅYãݮҵH4ÄkMåûÀ§d6Âç®8nhïRÇÒØqWΰa´Öl™”Œ¬{”iN4•9"6¬3U¾ÆDùÄ,rÙÝyùâFDðbI4¥¢| O9ÑÈÿgŠ5¡ò ßÔÉá4kÚ8n´íVÐïÅéŒûÁë-å,äÅ«‰ ô\h8!ÇSƒçN[fŸü׊S`ßÀô—Ý÷—c&¶ŸÓÓI)çzb Êg~‰üþ’“•q‹íîÿÌ3ö»ÐPÏGû‹H”7LôÓ è~¦M;qÊNÿÖ»2ï#Æ,9ï †5â{X¿SÖ⌊…yVq´¦58ñOL´›!оÐß7m÷³>Ša™‘¹pQ—¯QΛ'úÑÔ€Œ¬1ÒyÚéï<ä~þðÜCw ‡þ–4L7hüF”}h`Ù-iÒüe7 "iõfYJÁßRÝ£º+Ã7”°_ƒèt‚'ãGÛ')î)ã ÐAWÛÎÿ¹g_Ñp|_Hí/¡M“»,…-XÐ+ÏÈE8uZ·Ãê-ÿpÖ»if‚¶;iºvÑ„‘éßUp¯ÎB*š‹øÖMFB3ûj¤uÆF_Ö½°z©º än“ézÁ^tèwG²>Äf¸äNⳉ™® 4ê9 ò.ØZpk¹pF~1øüÀŸá’ˆ8?´:@¡ŽÔÖnà +и%!ÅŸ &Mâþá 0 lAÛCUøVâõb;3ø¬Õ–½&Ðé–˜ ž´/Å`”Ë‹Ñìð]{½Ð d[kª¶'¿§5ú†èص¥ÎÕω&W#ž|ÌðŸ&{¯¡¨#(5p?>0z ¿C,¥ø%ÅýT'Œ†ï%|’µëEãÜn dVÚRäÅ'é•b:lÑgç€äêì“´ëïG>®R[ŽøÏ*$z€Ý–ìÌô›.IL¥ ±/ö;’·ÒÔÄÌôÿ¡^ 7ÇrFÅM³ ¢¦ N[o6f^˜ÞÔ¿—÷ƒÿöh?C¼»2Ð=ð 7ù×à¬?Ã%ã2‡-=?†¾»Ÿn‡ö¢‡CwÙ —ÜÆºÚ­¤>”ÞáÇEÏ@ƒ4OÒ¤vg."“BX:”.Ò¸‚—‰Q´3"0­ ßµê‚Æ(tÝbº, «íؾ6ÓÏA´DžßGÆ£-\нÓv§wàq6Êתñt²¯ mØñÙÏp1¦}ø^Ã7á[I¡ßú)Xq*Õ‰žšÄC˯º‡¯°Žê'3ÝàÕÚXޔܒwx*WX³ ‡WTÅ‚?|ø®`ñAËw"Ù£“:+Àý2úÖ¥öŸ{ë"¼_é‡O_Á]ªi9îG*äßêP•üÉ gš]+„ °ÀZ.ÒQ KüèïL£Mäº7FÿŒÏtYqÚîhã±V}o ¿‹GcNQ¦w˜ïäwÖɶ?û©i%Ï,íH¤ ÜÎDg5=.Q‚©‚µ u•†ž˜%‘¸^˜š^k¦«y³‘Ïx¤ùaÉ옢®Þ(9íå´´mý±FÆ«Å:0|M¾AC- †}k„€ÑަµÂ,] iÊ‹¼¢¸¾x$vM{’‚.†&ÏFýÇí*£aÝáx•Â!ð%vx¤õ½Cêq›mg?}fÉÒƒ}„`ï9äMs8JwþÁÚ6™D ÍßŽÞ Ÿ÷ ’ˆ©p8Í,´­3?9Šì ˆùBÄ~8àÎ z9kʂ͔òÝ‹%ä’—ú‹ó艶^:À­)mP<¶ côCEû¼‘£Ð+x&x\ïÚqø?e§Ö/ªµÛÎB^é3Œ¥¨÷Ï5MËÿdÚ÷Áú)ÿ°Ñx×¢iƒ‹S•v.C-[ܺ­‡X  ÖšV…€&u>ÁCÛZì J™ü/‹þi¢)í(y!D=Á~‡c”¸ ‹bàå :û 3¯ò>BÿŠs:ߦ‘1Ý-Ô8ËBJ0]©À¹ß*³«øFbYŒòŸ‡¶¡üæ \ñh¬ãéLUÛw{­Ã/hðˆî\[Ñiý‹Ü¦ø¤ØŸöaœ_ .§ïÂíûÎ@L@¸É‘PŒ@ÁŠë[–i6§[ÍMl¯ÆGHΦϼ ܇?onEF\i„R¬‡fÙùôÓ4ç‰ÉÎFͱ®ÕûƒU1\Н•€’Mƒu×C,k)hm<¨!üA‡Ûˆf‰vÉm ÝþêHî÷^kÜ··õª’á6)P”mû)÷Ü´«=Õ;ì¶VÓiVÈòc·áÃ*iC"ÞjG£T„bF“rù ãƒÊ+u™BAÞ³vÀÍ R-¨ÒÓ“Lmû€âX*û_¼&k¢/ùH1º°êŒ>§S\›FShPdbýÜn󸶤JÈ$ÈŦ¦´a‡§g¨+a>”¬üây ­ÉûÇc¿“~KGgúÕèÞPçƒv®öùÌo1¨X–›muõtÔUBõ9™(VŠ|”Ê%4*¥c0„~¬uÆ¡TÖ¬µBù•l]Áζ0Lk†ŒÖVÂ|¡ñÖĹCìð‚fT¶Ðz[X¦šÎ)¬¸^q?¾báïA¼Êðœ‹À?r{zC“ñ0tVSÆe¹Ö„a‰²W*f$=Ð{ã²€ î- K4ìAy/Øì{êÌŒV‰})4Íç)Žß)õO‹”éþA^CÅ#o¡JGÞW 1Ošš*,¼Á Êæ”5NäfŠïÐ‰í ŒBKÿÚUö ÊÅšçÔÊÜ«²â ¡ÜîŠ>PÇKÑ!.VôZ+ž)µD›1[Š®¥t¬;Å>¯ˆÐ·!œ5«ƒRa±„¤Dü,>»&ÉúŽ=»½”ƒ ¬íÍDШðW”£ØFv0n´#K`ûåÉvd§‡Ã[~@{z˜ý»Ðë%±üUÈc¡ín?kÛØñ{"mš½º!Õ£6ô"fµÖKY‘KràÊü _;xÄS'Cô¾hÿÎ\ZJZã¿å©6´Aé cH$Z*]\‡õ´Ë@pwæzs_GpK$nEä÷¯dþ¬Þ+ÞVä{CáÏ|ØŸZxÀÏ{T_i´È¦Ƨ§o)ü‰JJÚ»§(ˆX·b>a>L¶—ÌÊ*ø)ź OèZl'WÛnt :‘uÈC#R°íàó Jë>¤Å,БÞBO”eOï¡”k&ùUR–ÎtKÂZ#hŸ2ÏÃíŽ3ýâN€Ü-…!W€±ô±DŽJõ%{™P¦IIß55è@VZa¥ìRÓ8jŽög’D ðWÝ µ6i†Vs,Uà’:j–°%Fɵn÷õEv¼Ü®ÙÀuxò94«+ÙzÖÌp9•×öWÕ³xÐ+Itœ\Ùì¦$|Gzú ÌóK Öü—Y¯RmÛÅÂ3É™ü0pÙ„2]IÚßhå´Å)Ñì.8áÈYHúž³hÀ†é‘ÜçlÓÈ%Súº’ŸÒÛÈ.Ã=[>­6Jö–©mØQ„úÄ2Ó3Gº>’ÎÖCˆ¦€Ý¿ ·ð`uá'fÿûªoÅÌ÷LÐ:½?Øv©êâ©©;3Ý£‘—¦ËGÈ+òQÚ*RU0k­R}P¡k;::¼Ô/Ħ´_0˜ÿ²‡˜enywi1'(¤”·¯û¯ø¶Mp@…€¢˜y ð‚<_G6±eÒÇ5ÈM×â0r¡X‡³˜y¢£<ö‘›C8ÊÅ…+ì™Å“Pð@GÑ yø;T¦„ž¸€âÄl²=&´üÍë"}*N`Zõé;^k \ `¥Ôµ•žÎ•§,†·_ËZy¨Kº-&?ß„˜ó%ß ôWÍ÷Ÿä^`ì¿.˜¿nO/$Wpsè³Ð.!‰P×Оò îÈ‚4§qLñ€ÞT/ap™<+5ìh©F“FPZ'2êe*C‹”'F]„ÊíŽnï+[AŠÂ&8“qÀ 40 VFÛéCve—“*Õ–6¬8Cü‡±5˜£ NÍq=ê55ûtJÆ((xUor2Óf¡í@>•ðQ}ˆÈø`¦"ŽÖù½´'[|FÁ‡PØÑÐi)>ÓüÜú–ÚpkËvô{¢Ch²{¯×c¯WÚNtpÜÎmUŽ6–øÍvÓ:N²¢í=ò‘®QWØöþORÙì×Èß.ïÔÁäÞC^[mò.{ ¸ÂÊûWÒ¬|Pƨ³I $X> @…«Ûä•hÌ»¤#®\=Þ¾8lƪñÉèÎAÙžÔmkô m\Üü¤œ8f üÈ;X¢%vP¦ø‡^Á>Î6c¢““>1uìÞö’5›ö‹ê®.°öK2&^­ýÏRÒ-¡ô<íçôÏbPF}Q§8ÂßÞ“’$.¼SW¢Ó»ï&Dæoú«ê[—ê)r‡Êípkk—ŸgÌš^a¹ûY[¯Ùî´EHó?èe›åùÞ4ztó@?TÏ´ÿ;Ð>Úß–æ­¤}°ª54÷K¯}€EëP¼´ÓØßï‹ípÑzjšþºÏ4î…ÂÌp”…Z-­©”3…¦qö9O¼`évà5xmÀèx;ücM[íÜã©ñ¢L7ÛÒ7œˆT¼a3ËEŽvúð™̬-@ô'7täåFÞþý¿_}bØ?ØN3õÖ ‡"̱ÖÕ¤ÌÕ…ù—½êp8õÆ)ÈÃÝjÝÖ3àÿGýa¿ñ)¨»ž‚<8ƒK3ük`ïiBÓ·#pOÁžãSAÃ?€†·v{ ÷ ã”#`ó(Žé+Ñû•Ÿ4ÍA»ÀŠ‹1àA¥ÉïC•NØÑPýàhÉI¨—ëq,Ò益Pù+Pÿ]0˜½þö£þ:#)I9—¤ ÷æùh&|™±ß·¾Ð–Z¦h†ìtÆV˜¾EÒ¢£Ê¬ƒÝ‘pNª;4t^ã>@“ކzy¸uFY§¢Á¶¡3¸íb¡.KDÉê ‚ó7u~c»•=×ÝߨŸ«fZŒ¸ÌÑz« mDÖ'ö‡¿-ì>(Çmûܷ𼂤”Ê4žÙ`Î8n_#ï+`µ¯“ĺ­¥‹#@³ÿ ¶},¬ÄÃðÌ3Ý0À¢µ$¬¤@Ô|ô4T©º?¬#¨þ ä\t ï:•ìQ%ÃEš˜¡MNyÂ8ÐY=ŠðÏÀú*û"lÐïWnûŒ_i¿«&½@LÓÑAœf¥¬ðü7˜_Ø¿]›ý‚D핎³Á’6 í{\LâšÀÄh6‰2}„Ùl<Êx¡•OS=†qòOÿžæÔzTØÐ^â.]SÂùÑåFÞä…$€áôNEÇ!?Ó{(†$öߎ³‘ŸÃ·, šÆbú;†¾x)(×àxU©–Ø Da£ýMšýا_åºdMúºˆ…°6(¯¸倥!kÉÌ.â]‰6´ÊúÆ #˜Âh¼#î3Û?˜vÐníi£,…pß°ålʰe¼ ÚÉèWi. iIj_qŸ£²PÏ/Ñ;욢í?:¦÷:3è¯ØÔº§²(_ë ôæËýO—ª,N¿ÃºÝ蔞$´ ?oWÑ‘MÉUU'‰lZ´‚˜E’&[ÏéÞuKÔî¤ L<ÌoÊëŒåË[x%FðfŠÜ–Fwë‚÷xC'aßñ! 8Úg¶ËÕàtÏ¡>§Ö¸½¾¤D#2˜·ƒÊŽÖô7«;K‡7¾8b™¿âT]1ÐãéhZãb8Ò‡Ü' c‰˜óD7Sé{›6V›Ãiƒa$¯áö´—ÝÜ–w¸©ûô„&‰Ë—ƒ"ÉX  »L´†o‰¶šO&J]ß0î‰G·ˆþ‡™®]#uô´™.²qsÊu”,'Ã0Œ#°x9*³À0Œ#pp ÀL÷à¨g.%#À0Œ@ ÀL7*³À0Œ#pp À[†ê¸ž¡À¾Z•Ó¡1¹¶Ž“æäF€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`"‡Ÿ½9,Ú˜pUØt)µ7s2Ó_‹UdŒ>Uš¾'­›„X'4Ç9™ÃþäÎêa&n’Gãî… J:ÓÕ\ýeÒ CÑ.®;¼ é>„s¸éšÀ˜¸ü!ÚeæøCCବ¶¦×á|ÙýøúÐBÔ_twu/ÿØ ™i3êGŽ#›K>#‚xâ¾ÆpeT?]×.š02½ÂµWÔˆòòÌÍt5`öH—u]8ÉÓ¥éáÞ Nü5õ‹‘Û ÒTíB Ÿšî¹ÁêÕùפ~i¶'î]­•Iq?ÛJy ¦àʶEšÔ2q­Y[‡ÞøŸûÜ/5)ôÀÖà \ù%[6ÒãWî÷æ}…+ $Zá.Öp2bÓC°0š÷g{2^£mš9)˜ŸX´˜1êLŸé›4oRìœèÉzlPÿaXVGûƒÜžÞ†WÍthú9†ò݃¨¯èèpµ l/¸Ç×LŠ«5§<5ÛíšãŸ…TרKLåû us1êæ[·Ú¾W—ÿøq7ñý¸î3 }E[!ŠftOñ\º±º+Cýã‰Õ÷|ojdÊSO5Ï6lO¤òI7mô.{·®Ý:ŒT>™éF I¿xLŸšðÈ3Ïì>J?oa¿n0}ƒ <©4YŠ%ågÊE« ÕѾϷãÎã^9|úÀ£ºš¦yûßS4˜™kç)%;Û©Ön»ß>e¨+ð,Çt!Üè »¢ÆM~±ÃDêY]þít0( {_¾ÿ“BÿÏ-¦R½pf/SÊ\Û_}~jÒùPÞ¼ì2Ü!éžÎ¼Ë&AXÔw鯴a¦ñꑟá¢è‹÷í)ÈBÔU^FNÒ3FŸä3C «~K ëíü¥ŽÈÚ«|¦p8Ô´ñn×o¶}¤žJh¤PùôÇ 1¬,Z)ÉÞŒkݨsŸèIû¼ÌS­ß¶Oéú¨Ö±ÄX`°?OéšZÙªŽöi†³ÁXz$.$9P¸[wŠÈÂ0}ÄDK™®¶nëÙI5#{öÄtGøç³Ë>ÿ[¤ËÕåß?x¿«¯è×ãÈ[ýîÆnPbØñžÇ—¢œô‹ˆ!†[$Õ¢9E‘é#Ï>èðŸÊÎryR]Oa ï}8Ϻ/ò× þvâ\ëïôdmøøáÃw‘8??O¥a–p=0ßàÍú¢:L¨å˘á£ìÁ‘ ¶ìMöºOOÈr½;ÀåyMS¢i ñÏ'ág ™Ž²÷€}~K¥ÔÈö›%ûû¯Í{uõ’êÊ ,:£ÞÿåŸêj2•õ~1 Û ëîïc.ºe¢'ý~Û.šO§nôf݆¼Ü ŒA{Hm-öYþK+ÖÅâyêÌæÎ‡{/Ôã2Ôã,éo‘ø·2ÚÏÉrg瓱â2¤Ñi½Cv\® Àa ^ûà÷,~–ñIy)ê’í= ò¾Lq?Õ)Ç=l-9BÔÙTì÷ž¬”|²Ø7ìªi—ä/ù·Ó³žJîÛýn9gÿPèõÞL<”pˆbjõEÇ!in÷]v\ÔÆÐ¦†ù4ÙRšâ~HNBvÀýÓfMœ#ž~üñ}¶ßêÚœí¯ªvKý ò24Ç“~b9 ¡/²ã÷ŽËr­IÉȺ¯ßñG¼ÿ󂥂vHþð;°fIÐ)'ÉN­Gƒ¨ Ó|FáUÅo‰¼¼Ûh${ äs_¢Q<Œ$É0Íi)#²úRXŸÔ©#šdÅ#ÕKX—ô(©mëxl9Ç¡ø]b¹Ùÿ|âÄÓk…·ØVôÄ:Ê%Äœúôèj‰p¦{n‘Ê7â.Âø¹Ð@›¢hNjƨÓKÃJ٠韱Á5ò"tn¯Àí±Ênª+ëq\èð˜®KwmnIúÇ"Ïá~Š2­À@b(ÄÁßXnªèä†ûDÿ†Ý—H÷v#×÷2¹CgÂ?aK³ç>ýÀ]çé»P¦m¯dÜl ‡ªê¯+½Û&5#ëVà7߈V>Îý0|þ-u¹Éö©g(õ‚±Â¤wÅCîç[Øé’R2ß晩#FC$Ylhíõw¦ÔjÛ.ÚOZK†ƒ‘—\ä) 4 š:„ö v9ÆNß›k¾Ð`ú húvØÓþ1Ò§[âýÊhßOO%|Š€¬,µ—’ÖðÏž4iÆRÅ´r©¦ä4¡µšÿÂð^^ê–gœƒ¼R8™ìBi—ä/ù§xl£4ñ!Úc_0Ôól»`ÏPè‘t'€ù·@HG;x…£ù€ Ɔ¯â<´ý4ê£àwü v¿âwïî}Æ3åüVÓæüüVÞn©?)¸–z…æK=yç{4P)²V9‚xˆ!+žéF¸2 ®LÌIMõB±ãŸ¡f`a’YY2jý¶ ÀNí¤WÝéö:ß0ÛZ ¦=ázMÌLûi@FV{aªûãq_”­é¦ Œ(§¡sëcÇÿÐóÏ'æî̽íuØÝ1Ø=®Ñ«î!û-w%û‚qM%âÄšsòÞÝ…Ï(!ߟèqÝl‡w»§½³Ñ˜>kbcÁ´{Û#QtŒ]P¶œGév_Ún0â¼Ç4˜›vs0e²à¡ª¶Eg˜å4×wþ>1ÅìMÐÏ6adßF*yY¼œ–¶ IPLÁš±ê „2o€+ë0ðÜkìí¸JŸ4ãQÞmO«Ï0:¿ÆÆ^+õ¡—Pë%uĘÉÊ4µ\£à$ý)%E¢«0˜ƒ:ŽÇ؀ħ³È¾ÐÜw6u¼Jsþ@ß‘0>!ÿ Ú;Ï?.ÌbgùÏb'f¹Nów³ýÅ«ŠÒ{!ìcë¶öÅàçÁlË(Á¾œh¿rÚ/Ž™”ä„·àb]ªrR à0t3dê¢e=ásö ÷èn†×8Jèòáwjf[Äx £qÖQû"̾ŽÎ3­W(í2ù§´ýM\¢ö~Qžo0˜ I´>ÅÌû9àø«¿ŸPèñ¾—^Š/ܺ÷ŒH¾„rèÂ2oBB2máuR PR<&.Yë2.-f¸dÞFMÑî©ohÙà_umÎöGÏÊÚ­¿z•æýÚ]`õêƒ6FÀ”Ö&1p‡Ñbzª{T÷ÊÒÀ¬ê&4ö?ý®å롊žÔx* Kö DhLËöŒgÿ΂ `'œŽ,¤íôš»¨ƒ+ŸIu2ù§ï}ûŠÎ@ƒjFI³ÖRãv÷5:"Õkð“O•›í‘»*c¸ =}êÏ•è¼&`T=¸:FVš`/ÀbJ¨ ž„¨¦ÔCˆ:$/ÊØja…ŠvÇ„çž ?bAL¨õBÛ0Kß,…¯ ªà*Ôù4ÔÓ4ˆ5ˆ¡ÓìK~)ŒmUÛ'èöL ö®òÿa¦tBUñ/YÈ-`²É N~1~w“زª°•¹AIî&Ì }f#ç$?IŽÄQf¬ÊH0S A ãRkvK³\Ì(¿ôyl̘Æô ÓØýDôO¡´ËHäŸÒò7ÄôœŽ½1p|íK3°ô1³V\Y&z,Ú¾ï,0×C5]¼a‡£§JÔ¨_‰˜gÒ³Ìȯü®e z W5¶û–2¿åß*ks¡¶ÛPi¾|ªõ÷‹gºQ¬;§£Yšaì¼R>ˆ™ÕY»Hz`Ðð»81C[äï„CbCÍ·3÷PÙ4 iÏxÐÏPG8s"ÖªÐÑÏB·CïGâ3Èû4Ý¡o…Wê0zêÎø qKM[¦LŸðÆáð²Êògs{&n[•=•ꆋY½œ‹õÄœ2‡¼Éàš¾4«ÏÛ™{#f }ctT-›ÅÌ4Ì#²D£ÝLhÙèθeáÆ‰Ž5$φkÔDË ´³¾Ã¨”{ :Å>ŽÖ'•×{’.Å#¦TÌ2¼×•ÙõOÆ?èaû‚áT:ËE‡>QXô@qAt>³ ­÷JþAwáî žŒ©•8[Ö)OW·º£ÀÅe#ä·%–`JëŠ/×¢}|ˆùø̬¾¿—üõªŠŸÜ0#¼Ï·žŒu?´ô?kÅÄtÇ@ÏâR<¢Y.ž"Î_a–»gŸïÂA£GOñå0ho‘™PÛemó_œZùÿ%’)ÛýƳ›Œõ· ÝC9§¢¸³ÖÉ¡Ð#ô4ºQ¬hÆO#Üh;•ôi0¢DgÛŽž°[çÿm½KmD¢P¥Kdaµ¹JÚm…t ù aë¡3Ý(V5ž#² Ÿùõ&Ã3D‹wü×ê&üÒ£HF‡ø;§Þõ³F+ /tY ’D¤•:ÀÕjšñ€±¾Ñð\Žu«âF¦äç­>l)µž¾`î«Æ»‡—2QŠTÆÞ ‘ëÊK½Ä…åfÙãžœ‹2UðN`ôw#Ëÿ‡·›ÑÙ BÇ9>¨ÇXbvYA‘Xqÿ޼€( åÊÑ„ö©æVäáäex ’©4:à$r„ø  RO•8 ìÆ‰™¯Vâ\©u(õ‚ÙØ`¼7ÓL$ÏȽRaŸl;ÇY?C_Fòƾ").ÇAoçûOĬile‰aPp$èòÄRwi.-}¯áKñ^Sï‹ ¢@ƒŸ@yi½ÔÔ¬Í]LÛÅç¸Ó–` tüÀ'².Cÿþ˜ÁwXZùÑ‘¬_MÊpU%O$Óëë)u퉠þ°®‹%‘ÁX2qlðÎ8 ¬>Íög)ߤ{þÆ`ê|‘«å!]-NÓ'Ûî¡¶ËÚäßN«²g‰²Ó0(øƒ‚™Àòø =Bñ ™ä»Àžt@*´,ü®5 °+÷n› ÖnËEð Í©—ŸÌt£\mG¦&ôÁ(_¡ª0Û€hlxþOú›µÈ ¢8Ú¹½G«8êä`ÒùÖÐFoY'ã³Z`iú˜·ÍÅ* 0ód'¸•ïÜ Ëm%ÅŠAã F;4Å•…ÓœÌçS3žú{b燶&~pØÅ`0ø#5MëŸ=2½´\€\c ji%a0 øøbÄŸGXí®Ä[D¬Ã©-QLöåCÝÕ(8 ³ÙË1KüÒbvó T¾úçªüÕiNg\)FÅ:uéš] [M¾K)xî¯9×µvÖú«Ìo æa[YOø#x¿ ÚJðНÍý¾»ð]AB`(ùg¾Û×–ÚƒV4ˆR 5l“1ó*ÐD²Cw|]ÎDÌ\â@ sêxó¸Ìa m÷pÚeMóo§UÝ“Ð?øÔ;Èo(ô¨)mdUè´™ÜéóªK#÷hµ¹ph>”|ƺŸR1O¬g´>ç/>I"¤4æ h2ÔÁ”t?¤=|í5+µ úR¬%[äóèŒ5Tk]}:ù§%R„§’5´8ö85JÈïí° ­“ ïÃÌ͸ն+}*óVt$ëÚ‹Ã+ˆžKý¼`à°“¬:8: EY—Aä=‰”¸¼Eò³+:Ÿýíµ3¦Ù‘’¦*À½ÉþŽÔSƉ9À®Ðg˜±ŠT"%ñ„S/´5u¸X¾ó@VgƒJEÂ:¤À⩬õÜ¿^u?¶9ÂY­4ºmÞeí1‹³'FZf|7‚¡V9Ð'Ñ2èx6ÚĹe+Ò>Io@s Sõž=Ð(ó_üÖ^ë@’S˜ÐŽË+ë 3‡ßyÅ¢ú²Bo—eaè-Ôü—UõÑ5i^£]­%Ÿ!Ñ£Ã9>s•WAQ3b&*m.šXI`DU6€˜¯•4iÑBâ!ˆ{Þ..˜ßH¿‘3Kî÷Þ† ²S Qˆ5QÇ|líŠi&4?MØMa.8þðé?,Xº«ƒA?Œ©‹ÏÖb6õø©Â,ÀÖRq7¼B¤WfС|ûAh°*É™8Õv¡í>Ј¾ ë°ocv¸ ³ã|".Δ^Ò>¾"©Û*Sš²ãö$‘XŠ{Ô ÊkÎÁz6‰˜o 毶v(´0Å_Ó±žù†f='Ï_v—5@#i¬}Ÿ®ÌW ¢ŠõϽÒ)?Å>®¦Xëœ~œ9üH¥~½h“ÁTnE™IÍ’ÊUÎä¯ VN·cjôf¤ògÇcBr20#«œ2H¬ÚËŸµv¦¯Ã2ÇV0Ý;@[Ó±Ÿ|ö“cûЏƒ—võ`Í2a£±ñyˆ·ßOJPKмZ²é3ûþHkþq;­`´¿Éu {G§C+iS¶ï²'Ñ"DÕ¿¢Ý¦[®]¯~'9mj7òu.8ø¿ËBâ-„vY›üÛm×?MëXRcÏ[¨Ë/qê2ŸÐvcë|¯É –^̺Aªsù…'¸ÝŠ­{™ ÑѨƒ"ìsÇò•ÌS†y*§ © W±.Zm.|š÷G­þ½kõ/Ëõ3ÇPûD_A F Z‚<⯅8¬á¿¦*\fòD´¤ÜRj¬ÍòR{[÷"¡VãŒe¶cŽû‘íh¨ óh!5Ç'¶==u©Föxý”KüÝ&f¦ÿ ifI7*¹ÒPEK ´q]먆˜PÞi Ø}(Ç­`ìå;³P"ÁOÇYÙ˜Ù= ^óo(­ÂÈ»(Ë*MÄ]Bð°½à`ÇÀ¼è‘¡>¯ù—Ï4AÚ©¦é‹˜–´©pêEÓ°dQ\¿ß¾0th¾Õ5fo?“›.Ë$¶{mŸ¨Û `ð¾ÿÌòuŠ—öèbŠ#6Å|N%ºÂó0’ë0(òñ&ƒÜÌã°5nIŽ-=+ÑžC=>ÛÁ‘öÅE&ícðI¢åZÿÅ!üÿKkÊßøÛÒ»¯–=$–?ÛOhí²æù·ÓñŠxejÜ&à4­Ÿ•é]€^Ã÷QÖîÑ&K¡Ðc¶'} )¼¡Ï¸ÚçU³ˆnßë„ô”Jƒ”+<Í6͇—ëØó¾šM¬ @b$Œj»ˆD-¯ßGl-î*æn {tM4Ù^ºÿ¶¢—°mèd:L¢:å•°#®ƒ¤¹Û¯{· •áé,ÜëÓAŠÄý/»ïßé¸ã«ÏõBe¡e ã dUtEÛ»rs¶¡ݽ릪ê1´ˆyàwuí2Òù§m‚Þ½{[醖œ˜ìÜPݱ”¡Ð#)Ûå¹Í›5ŽÛêÂT`YCýŽf›«ï4*†ì`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F  @jU„A£{úLß…ˆ¤³’ªi\«90#À0Œ@½@@ ±O*¹™]£kúwã3‡ÏvÆJ¦ëv¿‘°Á»á^!ÕB‰C¤¦Pr ˜îF²?Ú süŒ#À00ÝF`ºÀ Ú*%4!Åzð‚;:;¾âvßUtL7Å•Ù@NÀ¤”ßâ÷®–¤}5~øð]јãdF€ˆmÝÜÌ3/UJÝ‚ßE˜ˆ­EŽäx2¾tÎ*¦›’áyP™âY)ÕßÝÜŸåú9Ò€r|Œ#À0õÔtÏ9¦/£ÇJM<’“éÉÒ葌,–ã*f¸ê)äGÂÙæòœÌ¡+b9¿œ7F€`ºG`î/Sלrþ oK3ï(Ìz‡žÒçü=sžú[¤rrPÌtI¤¬”ü…ý8Û“~DÊå³aF€`‚#†+S]YïƒY\ –qI¤DÍZðäŽ-)M¡4I¤,œ­ïd†Ûpê–KÂ0Œ@´°xxâ_ˆßÄ^Rëä<Ó%-eKi k¸9îÔ¼Z#Æ0Œ#ÀÏ“¼xˆµã%¥nðL—¶aÄò-+ME€Z8 F€`2ˆw±¶˜F ì šéÒÁÅûpå»ÀŠ£`F€8Ó}—x‰ÅSjYþÍté¤):ø‚öáÖ'Î0Œ#p"@<„xIÉé…µB¡A3] Ó™Nšâƒ/jE#˜`ƒ‹‡€—„εÂQÛb9<¥ŒüѹšlF€`#@Ç#0ñ”Z™Ít±/—./à³”kE"˜`Fü„xI­/ÄiÐL—É„¨‡ž>1wgn‡&M6?û裹Õùg÷À€tÏ,MªœlOÆ|n8Œ@x0Ó /öÝ@Há¹R™êñý;rO§"íÙ]èÃÉe‹ ð{œÓ™ñŠûñµ,áv+Íí–8R62&Ååù'öôÓuí¢ #Ó¿ Œõ¬¬¶yyæfM—Wdt}è^«o©NÄ…0íjG@`·{RÜFï²ç”P÷jR\ †þm€þd"‚@CW¤ŠHIÃB %#ëf0Üq¼›öߺSž¦Iíz%å(iÓVâ±Í ©Ä©®Ì»7Y9Ñ(“éSyæ™ähÄ]WqI÷tÞà]ö öa^QWir:/<Ó=xëþ ,9f¸—+Ó| vlG=ýQ¿Ùßlòq1(®… ÄL¶E¾Pò3(—\¼oOAâ~0òñG?Fb¸ERÍÃE(s¤C¿Ky}täF j0Ó´q,"`ú„ ìúŽŽ#‡û1ÜJ³: cÔ¹Ò43À¸z`&ä…2ŇˆöªçñÅþ šþ b£aJjLS ¿£àÿO©ÉçH¼J³k¡Ì„é‰ß&!å«W†QºÔ5Mó0³pÞzwœ¾~߄ߗìô»Ç5òzwÍ@z£pZÎû¶==fdÝé3ÕC³\'¤deµyŠî޾ùÙø{Y~•ÌÌÉr}@ï)îì$áÝ6åë?mq‰÷|]j®ñ™i¿X~«ø‡uÕÕ¦’YÈë)£þ/'3mVÞ-'JOù¶eBÐÝøt8˜ÔÞ‘öL`}”äÿE< ?$¥f8ñFaQ`:5-Ǹ,×ÔÍ}ýŽ?âýŸ,íT13F€ÅË”£‹]hÝŒå䇗__¡ãÌùÀtÏ-Rù¦¢³ß…†ò~˜Ëf†(š“š1ÊZ ö s¬OˆgMeÞf4÷pƒ[0àI8ûI0—ÿI© £YïR]£®ö O¯Çú”ï9Sªt¼¿!tqÒû~_ÄÕ”4›´Œ.|ÈŠêaj¢¥mg?M%Z“};¥,Ò4ù6ÊLâò?!B÷ÐÌn¹§dg;•wÛ ¼Þ"4ñ%üÃ8u'É0Íi)#²ú’ŸªŒ)d’ìÔz4öbaš¯¾Uù'w¤7S˜â6H o·§/±6üÄoÖ‡þat¿ÐLå›s0è‹[Hí^z“á-š‰ÁŠTfj[ŽœÌô÷®¿þzI°a¢‹Ït£‹/ÇClËz@Q&^“ú²ê²Eë”{w>£„|¢Çu³íßížöÎFcútÌFÇ‚Yô#Ï)6RÉngóN¯º‡XÛÔîue~W¨Ä*SŠA Nça/»_O>ï{é¥÷ ·îÙ©„ï|–ˆ´‹ãÀÿ£’:u?´»Äæ£.FõÐ@÷3/Np?ºµÔg5/ãÒÒvÀË$ÌpG¢«&d¦Oò·˜8@IDAT¢Öo}¢Ã©ôª;ýÏ·ÿ ½Õ`¢cð]<3öä÷.…JÌIMõr{îñjÖEi 1ÒÏK¹×Æò!€ë8H Žó“|2Ð5êw 6¾À ä’lOÚ×(×›{?m¥Œ;&;ó±å%}: ós^ü”Æ]Ûr”FÄ/Œ@ À#»:™“ˆ|òhÊ æI›ªËѾ}Eg€A·ÇÌï¿nw_3­q`V½?ùTW7Ì ¿±.Ù¿âÉø¢ìÕøýf3\²ùþû Ç pëCé;À|íÇp-'§C¾Š¼$šÞÂsüÖêÃ…›€ÆŸ~ ׊ù%ÍÝžÄW“€ÕŒw»~ƒ6ñ8”?=Õ=ª{åaÔµ(÷?†kyàIÃ,[®T¼Ñ æ|êiZ¶§”áZNqºöŽíÇ~F vTüd¢ŽÏt£1'+HeZÛ€0KíPmž”:ŒüèÎø~¥¦-SXöÆáp[åç¾ÁïÝzcÝ‹a…íG˜§í…‡&þ1"Xh—”·|ÏîðÿÚAç7y70ØÄ´UÊÏ`ߟšog. *”ßÏké«ÓÑ,Í0v^© ÄÌê¬]bT©›ý‚µåÃÀ\gö¶3f³Šð´Mg >²?ìg\“„uE;Êo§Žd9ìtøÉD fºÑB–ã=ŽUÂk`IUúwîUæSÆÞ t典ˌ²ÜL %¿‚ßb £û Ö`ƤOÞ¢¢FänU®™Z‘)å¬i%‡'#Òß¡8õn9/HˆR3dÈÏ4Ã0"k ð™_o2¡[Ü Ì¦Ó'7Ì”; |Ökuÿ  ¶qäOð¤¿YßPÜ'ŽLÿëÇï!õQ¾BõC…0RÍ…x3Ø FªN¤ÉìçBeíè÷m½zòYRÓ­!H‰c¤Ë˜&3‘D Â¨:’‘s\Œ@,!@ªš”#H)Öo 1ª5§ –Ç„ÖÉ 0Ù+ã½µ‚»2oŬv]{qxH¢× á«°€(ú‚@-`d²]+¦9ukKÍ*!Þƒ™dÿ¨èÔ)Øõ÷·£w(QmBÖaGšÙÐ^ÕÀ05ýŽOÒó,†ùcyî¯ä,¤Ù?Åýl+ÿøS2ž:ã„ É<µÔ~ñÞ?ðà ÓWÔ¯ÔOÉK4ʘ3‘B€gº‘B’ã©´ÓÓÞÞ`f]…m8ÃR3²NÁ¾Öw}B[¨I³4v¡h¥%e{ÒDzӀŒ¬»àïí”t϶8©>ð‰¸8SzïÁ6 k°õæ¶P¶Õ”hO˜1úI‡®m)2Š.7…ºŒå½WÝÃý™üd0·K°•èAM×?U>Ù§N=ÍÓÃþ ’Þ§ %|™ÙHû%¹ NÁ=t§häÌ’û½·aóñhCóØ1_‰Â®Àà40o=Ǔጫºï—ÓÒ¶álä‡0Û~»Ø/rTbš6‹á—+#ÿ[`ú Ã·Â4½'˜>ãU "–´×Ï|Íöëpêc ¯ï޽{ ?ìÎz\Ž­^Ãw†RæHø-/ÆB9ì|ð“ˆ4<Ó4¢_L#@0ààˆ«  53Þö` 9Â4f㔪o±ïôa08KŠ 113ûjÕ LØn,Tr¥¡Š–@ü{ÐåµÙ™éX³Œ¼‘BE‡Nà²ì_ ½E0U‹ã[‡`{O™ÑÅp›Nû}}^ãÓ4~Á¼r›&ô»Ê|¿9ô¸ÿaFûöORû¼»s½y÷Kΰa{´y æÂ ÁÌþkªÂe˜q~-â¡q„ó |¡a,¿ CJ$&ÉË`ÿåÇë-Üä3ÍOi]Ù™¬Ešáv`èšv>òÓÍë5ÿ(ÆÂçQR¿ åžoû£g´Ê៿3‘B l©c(¬/ýHÙÁˆ½=Ù0ÐÁ ú¦‡¶í6ºÝwAœÜ 5ª¥)¥i]fÜK­mA¯ûÉs4Ã$QñfñT§v¢÷zf˜N¥;[·r¸Öžèè÷±1cçîõ¶ S˜Ý&Mš¤O^°´‹HÔòúqÄVÅú‰ä7•o›7ëÐsO8r}ui‘8:Yh¦5;¯&u]Žj²ÃÎ æ'!T&d‚wöÂPˆé¦fxž< ™àÄF (‘â',^ /[2Œ#À0‘G€™nä1åF€` °örPXØ’¨{”Ðoq(}yݧÌ)2Œ@]!ÀL·®ætj˜èIû¼/ìÌ0õ/×ó äì3Œ#ÀÔ˜éÖŸºâœ2Œ#ÀÔs˜éÖó äì3Œ#ÀÔ˜éÖŸºâœ2Œ#ÀÔs˜éÖó äì3Œ#ÀÔ˜éÖŸºâœ2Œ#ÀÔs˜éÖó äì3Œ#ÀÔ˜éÖŸºâœ2Œ#ÀÔsøpŒ(Tà ŒÑ=q5Û…ˆº3®0뀫œG!™˜Žׯí“JnĽªkuÝñíøÌásc:Ã1ž9¦©¯ Î^Ô(íS„X£kúwõµOa¦!Rq»ßÀåãîÕ49Ô0ö±6NJ0›5I’ NçA'Q(À%¨»÷æ©}yððàÂøM†áÛ¤ ÑË/¼0”®ÐCbSLSU¡Ãn}Jõ)>ŸùÂ!q‡¼\Õµœ±†3ÝÔ®|ê¿Ù·ñuð‘ŽÝ»uT§ÛMxG™”¯G úú…5ÐÈ+(­Ø ~_¸ªÝß+ÖÉo”ÿÀº¼ùŒ‡.97ñc椆™¦‚€ÂV;Áú”§Ñ÷>x÷0÷ÝÿyÊM}JÌ÷' š)ô<ç¼;‰Jçþ<íMzFàs|H)ùVÇ6Í ¸ºvñ™=ä!mš §ƒÇ3„7á@xô:®›<ªK;±zÓö¤ÜïÍ'žyvîÓš]R'1ßP¢A;•ÅÉ4U2lÏïSöçÝÜóœ>ûçý2m0ŠJ)~rЉ=#H´²¸sÏŸrLí±;.ÓèÔ.‚Ñ7¼¨ŸÇï¼L?嘮R×Oßù˜k(JéÄXú>è ÓÔAO @8”õ)]ÀËäs÷¤=ùÂÇtÂL7œ.ó+!θ3ÜgÀ@Ô=WŸ+㜠ZhPVòZ¾N„áætƺéÞ‡/F”Äx‰fÆË4UKÚâà'þ}Š”ÚÓw<’vˆÙþ„™n è4%Åèt:ÿsHÛfâöËÏ:˜E Ð+B¸uhÝL4iÖlüqÇÖ¶6ã­qœõ9 ÓT}®=Î{, @}JÇ6ÍUBBÒÄ~ý®k„<Å$‹ÉLÅBV‘Í×BÞç3Í7ô?MçnHUáD¸ÝtQoÍT¢]óίñøÑBøÁH“LSUÐ ;1¡ @}Êž¦›Jµïx‘÷!LLö'cJýUæ‡fµºÃ©?xÜá‡(^í ¦Ðì ?Â1¹q㑈_~1½ZÉÂòÅ4\왨»O‰OL¼¾b²?a¦[yýsÑn½ÿñSMSµÃ¶ +C(L»[÷û× §!èÁ8Ûeš “fØ;#PÔ§(¥Ú^uGj/ø‹¹Ù.3ݪj¯¼›5#qÆÇ]H_`nyWþª„#áÙ¢MÛóA~19:­QáªÄ4U=Fìƒ »OiÔ¬ésý 3ÝЫÓê á½ 4…ƒ/BÉ>+E€plœ¯4‡£<‘ˆù`ÚBÄ4U)e°#P3¬>}´ÔôΈ!æúfº¡×+a¥ M¶ÇÑŽŒ[è¸Uë³YÓd©;màѦ{0`Ì4U-u°F |¨–ºlöL7fú“˜ÉHø°ÖyÂÊ¡ Ùg)Ó …M„ EVŸsý 3ÝÐꕘ¬% TBÕ˜á¦ûH|:­v—ílݹOlغ+´\ׯ=ûóÄ3o}-{ñýZ¥¦”…¯Ý@l æc]«ÌÔMàˆÐTÝdõÀ¦RY»Y¸j£xù?¯aÔY)/³Þ´~CŸû?ñü;ßVÙq ¿•Ç¿WnšGbº?ÎYÔ-k‡”©¬A-b‰©þ„flBC€*PÇ©ž”|õË¢u‹&›ÀCËu}­Ú°Md8MÄ9#AFÖ`†"¢B¿ƒa@4E‰jÔ'ÝY÷ðÍu~Îù™'!úžÚ]lݹW|‚Aôç?̓®;?hYu]Ͻ)Bm$hlY)VŸsýI$zËJ‹Ü€ìY ©¢G„éÎ_¶Nü4w‰èqÄ!âû™‹„ø8Ñ産Å9'eÁö×òõâûßþë·î‡¶m!p‘‚øLî÷Eÿˆø8§˜96‚÷Ç#üˆgêìEbÏþ|qTçv–ß®[‹êÒ(òúć“g‹?—­% bqj÷®âÚ~§Z3‡IßÏ,]#]œ‡¦ïã*T'͸ûõ>Wæ*1yÖ îáX”ÌtíRŠ7âˆÊáåáä-J~KË)šŠR>c6Z¢ïIßÏ#]-p¥¦pazm¿^â›_ç‹]{sEÏ£»ˆ+ûž,Ѷfÿ½R|ñó|‘9øj«¢ï{ö£-¦?z÷›â´ãWž{²øåeÖÀ“/(2àw_i>0ȳÂÒÀóŒ‡~¤Lþ]ìÞ—‹ÙëÿFqØ!í fîaåA‡è;†Ú¤ÝúøŒ‰6Çkº¡“U~ÑŸ‘‘ˆŠ¦öçXë¹´fOJ0íZ6¤¼Ak¿4ÒÞ¹'ϲo?;vï+Uê(Yæ²Ü*ûGbíy˜!Ó:¬çC¡ŠÌIPø˜µp¥XŠ=4Ê&·@æ\Yœµ´/Á¸t]·–ÑÅ|ð:£©˜G¢š îÆÒÉ?¶—þˆy†cŽèÔLWˆé9oG;ùinÙÞXÃço1]œ‰Òý7õ[vîcåŠ{s ĺÍ;)<Ñ€—˜oÛt;eõæè.hsXªY‹/-%-öÛg¼ÛŠr¡àEšÑGvj+’ýN½;€m²úBŪâ&þ}JLä”gº1Q å3á3}âë_æ‹7>ûÛ!t¬÷¶}J´šÏÅ ôÿ¾ýM<øì»âv([ô‚²)d†dRBœ¸ ï}3³|„•|]ßÿ4ñŸO~/¾÷½åƒÖ¾N8²“¥qùÎW3ÄXØÓì ñ¼ö<Ѻy“JbbkF ºüö×J(­,MÄ=ð_¥ï¡¼Ð:-í  ¥˜!&1‰oÉ|õóŸXŸ-×CØ4“IÉéÃÉs,Ý äš_Á¸éG~;¬XC™ÚH(†$HÄ\G½þ…µLmnö“¡YpA¡!ƾûÕî4HHâŠsOâ6 ¸õĬIqeþH…Ëñdô¡g- É‹hï@“»u}ÖýðΧ ½í¢ZDZPÚô¯á\Z§õ74bÏLj8 šÌ¶¡Y/¸kb ¡ÙIëÁ­{ÑI:ɉ ´s!j†˜¿hùâÿ¾øôHd3~;ð£Eä|üÂ›Ò @=1„¦ê 6QË&Íj%þÛTÔ¬$âü‚¢ríÚúcc'‰›.îmépÐ`÷§yK¬AÂØGn-muÕ&+Év½±¦>å¯%«¼ýÂè[‘éˆô'‘â'<Óa2r:‚W1@†KE¨)Ã-<ê˜A… #ÐP µÑX0\R²¢5_RnÜ…mK$_€}¿‡´iQÊp)ßÜ&c¡öj—‡à½míâäÐŒ#À0a @ƒæ»¯:Gü¶`%Îb^"ââ¬uÝþ§W¾u)ŒèÙk !ÀL7†*ƒ³Â0/ݱeˆ~l6å vY¹tŒ#À0ŒÀE€™î…Ÿgú€­±ý6ƒ_ÑWÊÀyŒ ˜éÆF=p.F † mbڳΆ¨-¼¦[[9<#À4hKÑL(3-X¾VôÂÅ t6ò…~ÊL¯\/~_øNk/Î<ñ(ëT7:®´Œ—à™ÜüBqóŧ7<¸ ‘G€gº‘Ç”cdzŠÀ7Óˆ)¸¦²÷ñ‡‹?qrƒœÓßl3wñjÑ7ÍÀÕš_à]2«7n?þQìÅÕšt}F *x¦[:ìÆ0 q:Ô¹¸×ºç1]NLÖß\rÖ ¸$¤¥u+Ý!}M‰#\uNˆcÃT‡Ït«CˆÝFà A€®ë£Û‰È4Åö'±ÑþY2‰ñNë ëÿ:·/[—mÏOF fºˆð7#À´Ð% óq·8ÕÓ‘«þfîâ¬›Šæá²Z×eÄ‹3ÝpcÿŒ#Ð` ‹öîÏCŸÿ?1éÚrå¤#QéŒóá¯|(vã¨Æ~U\t_. 0~ðš®üÊ07Û4Ýz‘  $dËöš.ݰõìCÅÕ_Ž[èÞiÛÜuå9ö+?j(£œj½²F€`*»l„JïÏp4¸”‘D€™n$Ñä¸F Á @Ìõâ3{”*V5˜‚qA(,^> ðsâŒ#«xùJ\hφˆ$<Ó$š#À0Œ#PÌt«‡F€`H"ÀâåH¢å¸èÐuúÙÆÿݶ ÷I"4Ûлÿ·mÏφ@¤éÊŸ†˜¦b—v"]ïTR®ûêë›™nõ0v£0q»‰i*±vÓ±‡ªïÜ›+öäæaÏ !D?Ÿ%ü6>ÎÓw’D‹¦ÉÖ%ÚÛ·”H¸Ã Òú¦«583x᪠bçž\±{S‹°µ”œJ_Â,Q )§Èëy…ÖU~y…Eh B4o’,š5JFÛ(¨†‰Îõn×;÷'´3Ý‹ÐÔ©qPàSq qØú󖈟ÿX"öåRãW¡å ͱGHQ4|¸–JÄ Óh* 3Q|…ß'lj¾=»‹>=ññŽRn¼ì?¶°˜ ëÔß—ˆisн¹…Q¡'*u}¢)Â…öå.[³Y,ÂÙ˯\+¶îÌ Zy´=·IrŽ}l-ŽíÖQœÒ½«HJŒê7V,¹?‰•š(Î3ÝØªRfëõz­YÈ“g£s,‰‰+Dë–óñ\ &X•\›f‚ÈÏ? gÊž >ÿ¹Hü4o±¸±oqÜᇠ]×,æ•„9Ò¨#@ƒ¸¿W¬ï}3³ÚÂ:¡'*T,ÓTñŒÏâ䩦þ.víÅ`V3D|ÜjѼéjÐü>!õB¡ËB,ëH ‚›Ã× º%°<Ò:.òÃ)³Åù½ŽÃÖ¢ãííõ¯F½RCHÀ¼sXuä…™n]]2öhÔ0|ó‰i¸Rì›_ÿÆ}[D»6_Š„„5ÕEQkwbæÉÉó­_AAg±s÷å"ç“BqùÙ' c9V8:ÏzkrÝF`ÓÕäÙ Å§Óæž¶‚ž¾¨z¢’Æ"MÙ˜$é› ÐÎþqq[D›ÖSDbÂr0N_•9Sa'yâ0ÅÒ5›Äëú‰H†¼hÃKWok6mÛwºH8ºHNˆ‡¶m!ºÒÆzF“AÛeäþ$„ª¬c/Ìtëð`ÉÙ Äë5°N[ñß"ñÝo‹ErÒBѲåÇB“e÷y  ;bòíÚf‹;®Æ¬·xéøüS»£ó`qs4ðŽFœ6]ÑtŸ€áHz¢ò(š"HÿÎK.CTÐhœ/VoØf1\h¶jù –kÌ0ªôø5¢5~I ‹Åªõ׉IßÏÂ3൘cQKAN}?¹·Ç Ó—,¦ÿY,ŠnÓ¼‘8ý„#DßSŽ£.¾¹(ŒÄ«ôj×;÷'UÂtÀ™é0èË&5‚ÂB± öûYÅ ·u«÷Ë<€7böV¶ß ¾üEˆÖ͉Gt²¯®ë Gœd8]ýµ|øôÇb†{ é‰ò^4EL‡~ë6ïÓÿX.–¬Yg^…ƒt(D9õ<Ѫŧa2Üòµœüô-NÛXâéFIsERÒb‹)Κ èNäÿ'‰Ï~Ü/¦Î^$®ÂÄ€Iy1†û“H ½8˜éFÛb¦B" šáîÙ»_|þËŸ–H™f¸±b(/Þ-­Å‡“‡ul#Y÷ŒÊˆu±RΆ”¢«‚¯ø¿ïfZ"åX¢'Â94eÏð¶ìØ#þ÷Ý,±l휓Œ5ZˆŒ›5Ùˆå‘Bjc}Ö0𠝝f©¡Š“«¦Ž¦M¦cƺN4nüÒ̫Գ ó0Óž‡6ˆØµûbñÎ×…l7_t:âpÖj]˜û“J¡fº°*¨“ u%ZÃÍÏ/°Fåûó k ÷@ˆ”+ƒ‚òÒ¢ÙbóÖ»ÅXkîßûX‹áò–¢Ê;°ö6]M³HìÒ­áÆ=:‘¦)b6Ô–fÿ½YçˆͧˆFæ!­ÈhùWU«‰ +±¼²*/ÜâãÖ£n&ŠÝ{Ïs'¶ïÚ+î¿©¿%n®Éz¯]ïÜŸT€:¦,"#ψ©"ÕŸÌJ‹×qs±ïvDˤ¥\JSá¢Dy¢¼ýú×rkVn`‹åŸMì!`ÓÕÏØj«ôD¨E‚¦ŠÏZž¡òþ÷›™XþX%Ú·+š4žY' ·¶ЬÉO¢–’þâUö‡ÓP¬;c@®±ë¤fÜŸ„‹^Ýùg¦[wX—K©tTŠ­AØ„ÿ”:ò }¢QÒürþbéƒò–‹™8) !ß4³¨IçKejhy±éŠèi_^QLÓÓÿ³wpnÇ~¤“NÒéz±ÏõÜ{Á6¦˜fÀt ÄC„Hã…òòi¤Òéy”PBï.€±mÜ{ïݾ^$Ú›ÿ~·:|EåS;íÞï»ïÓWvgfgvvfwgAûXyJS4^Ú¾÷0/ûYÎÖævž…ü¯eoH«ªµÛ6RiÑ{ÂÍ<Ÿ×PcÈ)Ù’õ¹TíIjW½RºIªMHÐCw ×òn$øëpS56À¸™—Cn¯7²†!UñêNpI¾Bä²Tç'Ð=ž’ ·¡ÉÁîb^W[dz_c¼9R[¦Ü\LÀÚL,\Ãkó›„|…‹†¬wÕž„K±ä½§”n’hJ ¡÷0k¹¶Á!&`Ä+ð…h6L©ÆÒ †ð•R‡’¯*kRžŸ@µhy JF®A]²v;ó¤“JŠxy1þã·ñ¬í¢‚O؋䣥붳|!ükxò%ë]µ'ñ¬}òVJW:Fœ b){Z”®‹c»6òa̪8ŸDëšœBé~à¡RêP@òUm#GWbË/R4<…æ Ãº`õVŽ"u€Çˆw¦ºÂhæà%Ô±qç!áM _éªö¤S¦ÐC¥t“Pè¥KwÌ6tsïÖhhN4‘ ÝÍZƒ'ÝËÀE¥äS ˜¯š9D"fíêu¤<¥ÉÏúç!lÚ€ÚyyKõ%%ò0qº£ÕµaÏ›®wÕž¤Dv „Rº’'~¥;Hë±cí`ú(.­Ñó*÷rüØ#êœ%_¡ŽÒ)EÂSÀÊ¢-¼¾)Ƕ-ÐíV^GŒñxÌžáX»²ÞU{Ò9iSá©RºI¨Ù3E»(ÆmØU–NM$ÛénÀ/qIU‘!u!ômº)ÝxJºÐá!c×Yب ý]BH”?ÝîÞ”o³Ü˨×ÎRp½«ö¤3J¥Æ3¥t“TAáÆ½Ô®+I`¶_,”-þ¸1H+¸ÛǦ[Ý•uÒy3‚(‡ÉSÀOŽçÂÒ­or‘)MƮáºÓ5€ÝÊ%4 ¼Hó$ ëúËÖzWíI×ÔJîJé&‘þAé¢'›DÛ- º„½ÝÔͤRu“n)\žnš{Y ˆíä}þîXÏç·PeÕ ²YŒ4¸W˘&gáÖ¥”Ét«ÿpë>\:¤ú{Jé¦z )øRˆfSNR¡‘JWX»^Ž«œÅûÜzò&¨ßø&àn0Äg£¯7¹ƒÇp èäe¼7/¶ÑÄØnüñŠ”jFc÷èäDŠ·^ï+¥«%#È'=Ñ«§>KS'þ8…=§ÝtñÛôÝkVî4ôºýò9ü[_AŽ> ÕEØл$OY²óèÂS~IÿuÃ>6ѽA}J' ¸’ÁSÀ,Þâ< [ºfžTÕ7l:…ó¢ÍZD÷]¿‘ÊKÆÒ¨Wз¿öã¾™~0s-͘ú4+E­ó1sÚ¿éÌq?'ËNß©®¹W1”Sn¶‰lÙÙ½«¬t»Ò»z×;=ðkt×U ¸ì¶*mÌ7î¤âüd·• ž(-Ö)n‘>Œ>‘ÂÏ÷ÛR4ž%©¼ÛP@6ڹͣˆô-;™ô:“–m|R|‹ñöK?b¡(m“×Ú¯s0õ|3èkmîGó#þh¾Wßć²^bÍ=˜§Ì&;äö£×?»…ž|çVv:뤉"’ÅSÀ©Gƒ€ø©®~Šø­×¿)c¾OûŽ~E‡+×qxÖjZ²þŸô×7&ÒkŸ~ƒö:›& ½Qµhíãtʨ»x·¢â˜Š.*œÍ±¢S£»‰>]¿—¶¨â[°vaY»²ÞµsL p™f:{Âýgý/ܹi ÎQ^2ŽÆ ¾.y£ã­Ùþ*MðPà^´ÁðG›Gº|§üéRSÀyúØ{i+Ô&g•x«¬h+à§øÚÀ ·_¢±\±é9:}ìwiýÎè·M~½ú‰¦Xwâͤ[R£ã858Žð^¹âÎ~VÌÇk·ÑÄá·ÒÂ5É×">gñf÷ÅE³¨°à :zü:úd%ÑÊÍû(?7‡åËÌ ÙFw\y+âVA«¬©§—g-K§þÑåpðFT_ÇAv,v²²|F“F¼’cW[¸x;ð9ñeSþ(Ú•`ÚÒ OÐ=W/å½¶‡Ó±šÔ a@$.Zk0€Q DG…£èH5ï Ý’Vo{™–²ÒmoìåpÕ:*ÉÄÂk‘¯«³¢À å©àú÷<ƒög­Ð’ÉSr;I(]“É$³×Ù@W«œ!¶Éƒ»9–TR0˜ó5·‘©ÂÜþ4¼âºôôß ë\™€Ï¢ÑògLg,}êYö»¯9èo÷™Œ€oeE#éhÍ&Ɠ㴤)ÜQ¯®ÛE;Η·Ä¹¾é{j¨GѨ6÷ÕŽ) ,ÝŽi“OLYVÞ»\D8WÕïâ× T”WAÇk¶†ó‰z'Ã(ÐOx ëw½8ûêUôâ)(Ò¯_:…­º\ÞÁ*Ü«BZ.`ÝÂí Ë×ÂcŸ3¦BÇŽUÒ±ã5´íX#ÕÔœOuugQQÁܨ£Tç d¯Q%oaY(~Ì 4eÜ÷Xr 4åox·­cg5,S=OüŽõÂÀûW|Æ8ͤi§¢“†`Ëßt‚›¹¤0¾{ý4±´ûq×Õ7ðìçj:rø}²f9[=ÃTœ7ˆªXÁÊÔƒ•ðÄa7Ós^Änôy;p†2.Êø­.:§€RºÓ'åŸæÛ{ ë†k}ã~ÏOù9½£VºÂëΛC%ÅE6aÄ/=öâ,Z³qÛ¦—þü»ïóO„ªäƒÙQJ­ÝmþѦÁ²sžòo»ÿ¿ß5¤âäûnº8&ô0£W6¾Ï¾·ˆ*ë£sµvÄS}{L–Þ‡‹ÿ‹_€Už dÆ…´r-–l¡œs8D®ÝI<¡ª’7ñf³…½o=ßÞ‡wùi+O yìvñú¿SÏâQtí¹Ï³’H³–hc™x7ßÞ; ÈÃÅfÕ"lí?REc÷net6‘€Ëþ£ËDQ˜H·òÚ¯‘ÅœK°ø‘ s+xôr{š8;ðï#î«]S@¹—»¦QJ¿ÆGÊË) Î\žÕ K·Á¡…Ï ë#õRFQ =žBGëÚsÿEóV=Jv½Ó†‰ä)¸—±”ÆÌ®e‹ÅÂûçZÉ–ccåk£=õ~rù T\ü䷎ö6ŒõM‡Û•'ŸÏÍ5´óÐçbâ¢Ì ²‡oôLF#o€bô°òw°bã1ëî] \r[ÚÌPîY<†N}7}óŠy4㜧š×ÿÓà qüõ¤eªå¥,ÝT«‘áAO³o1Óîkév–\gHUu»ÅYýS¥@(Oå  ™çÿ›v^ÌJg-õ)›$>©ªÛ!ÆóÍS¡–®Ýn£•ÛQÃG%%ïñpKë2¹PÜÂù]U·S,‡ÉfË®ÙÝ@c_C¯âŽêQê]:†öF{- dU$ܱ;¿õ¸ðùØZ÷™ÈÊ L’‚+®õD¬Ûþ¥Ch[ml¥²Â¼dìuzþãét¤J›GRÄmʪ­/ÞQS@)ÝÎé“OTo½Ñp€-/Ë w'o+è çuõN†R ˜§FT\*–Ÿ ï1áéwÓæ=R¢y J³x¥µÛäòÓ¦•”Ç›ÀǪp[eívVxnÆk í=²„àVŸ:áaAƒš†½´q×{´hÝã’ ¼–wíbëWÏÔìÖúG»ï%‹§47³¦|×ì8È&^**œÛ.Œ‘Þ„’ùj3/­sPºù gñ [›ym&W!ßþ=O«^Ÿ×º¤*ÒòÚ{¿©i4¯?&êU’«Å`f¥ K7 ÃSÆ~×ä^+Öá—y´j#/mÓÆuq4Úºw«Þüšºî„jL·â¤Ë£ƒÇVÓÆÝïÐø!×w òÈŠËèxÝV~÷ÝNßSÒ§`ýaÇ¡-ûŽˆõ¹F#æÝé“A0–-SðlfyoÂð›é‹µ$‡³ZÞŠùŒÌ©wq.™ƒÖåÆœq˜`íõg+~Aã†Ìä/:Ž^‡™ÌƒúœÃøGÃÌY½ (K·›ðÖï™õ»Þ&*) „CTå)X|P¶˜\TߨD.·ìyúNd‚‚}êÝ©]’éÝßéòH_¨«;ƒƒ}XhTßb±Y®MNÄx®„uÛ¾¹„£³„eUO¼}Vg¯¨gíP@Yºí%·‚‰'d²pa·”è—9$f”áç^8zßÁð'ªlUN×õ‚€ùéÀOÀ(Rž‚ÒÅò(7‡ßr¹ÜQxã÷îœÎ ª­;›zX¨¤ÀÎ.fÍ…ŽqìÎ’¬wí¬Ú“Îh•ìg×d²¡ë¦åC0B“…™wIõäõæ“5»ízÁöðIu<º#|Áõ`·òìWoêóê!Rž (]·‡5¶—²M±Y@º×©×›ËQµfR¯[QÌK¢²)[l~ EÞ ®ß`\Û»Ÿ6í ï•ií‰RºÁÜ›Äk›9‹¼þv›Y“EçE6¯/‡ì¼ŠJ©Mü‹¨«Tæ'P0žÒ”®O,¥Árš¾%yÔÈq—½Þ(ƒ §@UúÉ(®Ïg§1½ó¨ÀžCV«…•®™LY+ÝöÀO—öÄÃm^]Ã&î<%nvv{ôJä=¥tIí²ÐC•G±ÝÌn6GòVêül€±¼H :ß^;u Í\HP/åØ=µù 5 Oa/oîÇ Û'Æu–AÙ9âUçt«yLœ:rôf‚kyp±•·MÌ?i Ö.B@" HWI¶%8§K{BÌ£G+ß§¿¼Ÿ×|Wu…b·x®”n’ª1 <;ã5ù63•ú8ÐÅø$AÔu±€ FnI¾¡ní0tý¥z#Q|Õ£ãgSšŸ@“Xx ¸"åZÍBY5q§°ªær¾Óµ‚¦À?§«?:t7M¤E&Ø3Ÿ×Û)—ÄŸ†{Ynñ׸²Þ!—iÓž4Žg—£½úÒ¡ªµ¼«Ó ´ÿØW¡Ù-ž)¥›„j · Xäo䘪8÷È1sψèñ&¬N‹D/°UÛð¢Í“¸tú±z˜ Ⱥ@½€ŸFô-MY~A¢å)à‡ÍÝ¡\°ñܯ}ŠlÔ3Ç@õõ“Ùj¼…-àÜ„Ð<ÚB\®~¼…ßL:|äNö±ÒPV¸ÊXáòf¹8ì¼5Ü˼­êuÛQ ­÷´iOœC¨ÈZÇí ê’½®JÂzç%þÙªÝ⾜KR5¢Á@VmÊzç™éh£›*«§S¯ò'¹ai™™™$e±Ø*­ŠaÊÎòÓ ù¢¡“°•R‡Á|5ª¢Œ6í;&ê®<…ø ÔŠ…§ d€'öÜExD +'(¨Š" eùésí?øCÞ©goȱ”Vør„9 55ˆ‰Yå=Ÿåoy²V ÉÕÜ›Žýe™ª¹sPÏó6,äæ{^Ÿ'Lù¨Ôê£~…*`E[PGùùyÂÒÍáxÒVŽ--·/ì „àz‘4iON6™ÈÜ—VïúùÕŒ¦>_õ[:Àïô3gv…zÚ=W-fªLöL5…k.$L˜Èfßm»'ˆ”QåñI€¬ý"+yŸÒfwZÊ »òäŒJÀ/qiÿKu7‘u!ùÊÆŠh€RQw¨ÃTJ±ðð„2‚ÌXyÃ;O:‚eˆs9/µh÷’gW×\H{÷?Äc½W‹ßßá’<GšêÓKèÀR}ÃDñ»¡qBL$Øz5+p/OŽ2¸{‘Û9”üÍd7˜©Üê¥EFT’ÃÛæSaa>+]>ò5k®et*º²r`h½C>Ó©=éW>‘®?ÿyê×c’€Q¿ãÀg¼•à%mv³Š©2Rèceé†_ˆÁƇA—Xl˜ab¥áÀd ÑcçžmQŽƒ›]tÌ1šèØL*)}+i/¬4Ž’®ož‘zÚ[àÔ„ð‡3Á#|ƒ¾mŽ>MËWuå)P ”¯ú•ÐñÚ&Úv”ù‰Ý™%%Éã'À§OiV{Ê)—•-f1{}^žH¥m `3»xøfªqEt§ú†“Y9ùX‰Õ²Â®e™rò»&^Ve§fO™¸Æó'™Ÿå *VÐPÂ&¶PmÖí=¢äñ²²¿’]胩‡¹™ ²±°¶æLŽ‚%‹ŽB^n.åå³¥ +—-^X¹h`±×pRh½§[{RPPD×_ð-ÛüÁù¤@¹®ñ½4ûºìŒÇ8ôá¡í;†6m‰.ívÛ¢û¥”nxt T˜×ïmrºÝøÝñ KyJwè­³ðY¹ñ@âpXyl×Í33›©Ò1ŠÜGJ©¸ðîÑï #Wý^ÁxÜܰºËsüÔÇÌàDc¸aQ…Û(t™³Ùí÷x<íÅð н³ïÓôY7½x th¯†õ.Jiwõh(‘~lzòð„%he÷,&!B•T¸Pn&?wQ~s³Ø¯±ÙOM^ö"ùóÈÛœÏgŒ c¡Ž xØÄjtSŽÙG&6³ü~î s(ÄCN³˜Ul·¯gž’ù~+îŽ]Õ>vc2W“c$9š†ó»Då6/•ñ¦P²lÒ Üâr/à¶Ò~9yJº•µËá)ÜŽê=ÝÚ“ÉLçNú1Uô:•>XôCB l/·ƒï}q/oxNõm VB›ÂTšB^È\Èý„þTJ7BrûÜÞ£5µ¨¼˜”.\BB¹á€p Çërºx£KDÚ)ó6’™\tÜ]J‡Þ!zÛ¹ö5üînXã³CÖLb GÏ*tð$“ÁKýr¼láòÚ\Þ> ༚ëKs/GHÂ^¯©kô»]. èddÒ‹§@¼ŽøjHy>ekhWuYÂø ðÄ‹§ ta ZØ"õÛµ¶JN*6ìµÛÔä`E¯É“ih±šÛn 1«e¬ôÚ@y{<^Þ´ÝMÇ~ªiÅÖòx{¥p”¨z2°’†ös$]§ˆ;5Åœ.+lÞë×è¥b‹ŸzØM”k“^,(^ ÍðafOQ@éÚ8nt[·èÌZ…×KZ¸¨ËpSGõžŽíÉÞ:ñ¶Ë?¦×?½…½4Û æ­x”×ó  Nù9ÿîš.¢MinNÉ5HJé†ËÕÚ{Pë.C s™E›4!1ŠÙ‰˜‚1)—«™ÇßÜB൭¼dã{Õì"«ç%ÇXÐ;74ñÄ ¸Èš£-¾ÍwèåhX>^¨ŽF#‹•m±©™ÊlFV²pßqOœÝ^v,eàëpgU¶)¤ƒ #èéhj<ί õ”G_t»Ûºñ(Ó_õfEb7ÕÓþUÅ‘ŸG"xJ*^VUoí·6Ö‹ÉUXçêp:µŽ,+]ö¦ˆu½PªH •p˲õ e  Vs3[È.>¬Ù,|nhöÏqdë‹Çf©œÕ/U,œãÎ)»¥9/³ÉKyÉ5ßjÊÓÆãͰ\Ö+®oeŒŽdI/iY^ÍÀ ÀIê¬ÞÑH·ö¤ÀÞ—nºømzsþb§'ÐbÅ–ç©®é]yÖßxh®ã B²Miv8;åÚ¥tÃçlQyGöï]˜_RòýuÛЩc…ÿu;oÊFã/° ŸÇ”ÕDº djvS±×EžHéðe‘‡ÇZ=ìö…‚Œ5A¶Ì—ˆúlb%žkö“Íl=rX´P²rVe>/é7iQrb-täFаoÛ–Õœ—Ö–¥  ÄŠgßëÎS(§+¾‚réápR½ÓM ¬HšyÃt/O”ÓT_^ttW·ÅSš¢i̇eDÂúe+ ·É¡Yºè̺Y†<^Mñ?(^ÍÊÕ– ë’/’eÐÉ'Ó®­ü}ßGÌgí{(mQ}‚Þ’æp C>¤÷*GX±ÚÐ –ÿ@±â9®+ÝÍÒºE>Ñ& CwiO,Ùùtý´³«ù¾ÀÎhÛöÍ¡—çΤk§þ{ÿŠÚ%•lSöïÜŽE¿Z%içvßOôM¥tãx âæðÖº!ãNªüjÃÎbVº1i½@ï”…ÔÇ’, i„1 3 ½“²‡²¹§žÇ½p(eÙ[üÎß(O -¸ºàR†…‹e …≶`?òÞx{P0ýÜÈÕ­ûêKÌVñ¶z·÷M7¹ÀQOžmÂå«ìl'å·X€à)=ù*Q<¥áª)^ð/,Vð'¬GX˜Â{Ä ›#[ºšÒ•<ÏŠ—­]#ÏxÆ}(g|÷´ƒ7¯Pܘ¬Å; :µtQðV.ÜÝš… ëJ?Ã1¬t!3¸'•­¶‰¶\ |‡®>÷zsÞBñÖ4ì¥'Þžîß½ïfÞ­^Y6oîR'eÛ¥tÃgrt“Ñ@ò|OQ¡½üü+_hxó¡ÊjÿõfÔcŒWä–PwÜÛÊî1î}ã€e R¬Jyhƒ¶ÆQôÌ…»¬u…l<ðn, ã-p)ÃÂݱaí»Ÿ½óú,ί‘ôJq† €¾ sö;•^)!<’È:”cž°ââÁWàÝÖòâÏS¢0ÿIåšeqà ¹J¸¦¥åÜÊšò] Ýz­ufu+ªìUï.‘u?¨÷TººÅâõù쨡¼ôoõŸnp-`PRº=QJ7|V– ¤°JZ*Ö2ëÕ_›|î‡|§L¹û/Î*3¤¯òèA†±CúD½ŽÌ åÑÆŠ8\]Ьåàž¶J7¸±@™rb•¼>‰N|kæ0…3 1iÊçq×/_0ï•5_~%BŽú–3®ƒ-ÝÖ–tÓ”0žýÁW(C–%ùGož$è_0 *R÷bQï:QußÚ¦xü‡Ì4ôèñ2Ë‘Ï`¶8ó¾þmzøÿþlºuÏOʶ'JéFÆâ0/1‡ñGXff>²¾š7wáÆeK7O¹ä²é¼üåV.Ì€þ<›Å_X`7XͼðU‡ª`CGS„ùmèoy?Ò3Be"r_0œ¯×S¿{óÆ_ÎþhžÃÑ(m-狃7Õô]AßLp-3š"%•§A(…þÖÀ ÿ(…þ?'õf<)ZÏ¡¿£);´®CG“§ü¦½6eÏÖæÏÌd:xæ…Íßâþž)ÛBýoû¾÷ÏüÎ6sÿ~GJ¶'JéÊ ïl™`€£DB¡66Öùæ¾ñÊø÷‡#Nš4¬ÿÐácl9¹¥Ù6kǵp˦‹â Ìä¿Åk].hYlà@IDAT‡£ÖÑÐPµgëæÍ[×­ÚÍPÁu k(^(Üš–k¸–åxn&X¹Œ®HЧ$%ÔYQ  p›âä6¥ÎÑØP¹wë–[Ö®Üɯ‹ö¤¨ÔX7f’ÿ!|έíw>äúù#÷ÒwøgÊ U)¥‹ZŠ,[&R‘7œ®Í«W¬ác3g+,a>CAËYHò›ÈJM¯·¥Ò­pȱpô<¡\áúÁ ÊJ8%{¥ W"’â©DPY•‘®è²=yå)ßÛ?zÔ˜[Tâ¿Hòr±Ûõ¤qýOîòý)ÕVJ7ºA# « !Cq çeãÁA¡tAcXÄPº™ pM‘$] pá2–V.”.”,”.vÁºá9蘩Iòð—´MOe*G(¼ƒ) e¢ÃöäûýÅß=&ÿµÚ‡þ?üæÉ¬­Ýåý08£d_+¥] H@£|J »!ˆÆ|UºÝYù‚H’.RHà:– ŠüÆ}9Ž+¿å[—$½Oe\Õ+„;¡€l¤|tÚž|µ¢øŽSO«ìíÏÎÓè3ø^ùÍ“Ùg%§f.RHp–h#y/5¡O ¨@#Ù1‘câ™ÌS©Q+ ŠdP²€$Û œe[‚³¼wDzøöæ¿~:ë>ÞáIíŽÿÞ_?iúàÇwyæ´¼’´“Rºú’^V>I*\íWf¸–%® …L’.ò·:‡OI;ÅSáÓL½Ùý(q{òãozŸúÕ“†Ë™ÓÙâå0aÞ=öLþØûJ&y”Ò/õeƒßRTî™DÅS™TÛ ×˜(m³ßév4®c¡éÁŠ··Ó×ðOÎpfL™Æø±gŒ1õ¹¢€¢€¢€¢€¢@jQàþ›Ž’Áx‡„Š£n]÷«'³¾.'㬔n2¨®ÊTPPPHþ–÷GÙ2¶‹"}¿ãÙ̈¥”¤”nRÈ® UPPPH²²Šïç 6G[Êëãª9øƒD•ZŽRº¡Q¿º¼ã8‡›5<"‘òùýýáɼRù;‘g¥tImU–¢€¢€¢€¢@R(pú°³Ÿfk1ñ‘ò›©ñ§Úebÿ+¥›Xz«Ò’@sÏïáIU¶í¿ûwOZ·þNÌ•Rº‰¡³*EQ@Q@Q@Q ÉàIUï1 /#2{ÈýóDƒ¤”n¢)®ÊSPPPHx¯ûdá~ƒÿšGŸË-“¿qVJ7TVe( ( ( ( ¤¾Ë½”ãS-ÀøycOÓ-‰L)ÝDR[•¥( ( ( (t  Ƨ$0ãNyˆ³Rº‰ ²*CQ@Q@Q@Q e(`¶”¾ÊÀÔµ4ü7ϘÎNpJé&ŠÒªEEEE” Àý7iäþ-ñù| ³v•Ò•TWgEEEEŒ¡€Ép1“ßÍï_èiOòJé&‚ʪ EEEE”¢Àßr¯æ`/²yœ•§'@¥tAeU†¢€¢€¢€¢@ RÀð¹ÊO¾³äu<ÏJ鯓º*oEEEE”¥€ß`XÎïWJ7@ u¡( ( ( ( ( 3LY–€Òeó©O>9ɬs'd§,ÝH¢n( ( ( ( d¼£é Á@Û[pÍ©1®o¼•Ò7…UþŠŠŠŠ)K?µº˜½~ï”xª”n¼)¬òWPPPHY °Ü*csÜwRJWR[2†ciùûËëx•ÒeU¾ŠŠŠŠ)Oƒ‘J—ìö‹7ÀJ鯛Â*EEEE”¥€Áo (]¿òã ¨)Þdxþð„7‘jMÁ×­wÕUw¤®ƒ¨‹°) d)lRu»ƒeGwY2øZ•.3YN¼©§”®¾ ÞUåÿëSO=â`4¢’%CCsÀ(â5»®x“C)ÝØ)l¼ýÁÿ¹È˜m~Æïóõ5¨òèA4vHCŽÕ’{ö*‡4¥€èh59]´nûújÃΞë·ïÿåð3O¾§×˜ÁßyåoœÃx¹ùÊ7MÑÔl¡l9Gó ÷þ×…ù…Eÿðùý½”LéJãtά=™ú±§ñ{ÜßùÜo>›‘CG6¢ä7zÊäW¬µÑ!ŽkRJ7zòŠùÿìƒñw½Ê ü3/<•†ö/Ç}•¸ãE§Ž„ðmïazuöÒ¬H޼龇þçÅÇ~ó~ ~-™lõJëíQ6Óæ{V›ýç½{*™b‚¨Ô–íÈTÏG«?à¶øgýßÇùíÈdÉà/“%ðP`£¼Ž×Yôâ•y7ÎW4wüø‘²ÂýÃÉ#¸åò,V¸Ýe…Z¬ÃÍO—m¤íûŽtø<^ ì†,³©çžÜË]ŽEÅ –å+d©W p ")Ûíñê3Ú” ²-ì}·eÏar¸´8½ïgÉ–5´Ñh«Ïeé±çmìf"¸ 1žËNf[B,]¤R×@ƒ \bFƒ!—c)GÔ@t–=”ìÇ‹×Ò¼¯6QC““²²Œ4¤_Oºñ’Ó©Gq>}°`5Í_±‰~ÿƒëᆠdõËgÞ¥¾=ŠéÖ+΢Åk¶ …ÁQY¨0ÏN' ïY|wq±÷P%=úÜûâžÉ”Ee…ytÑ”±tÚØÁmÞ‹õÇû V‘×祻®>ªêéýÏWѤ‘Èn‹ÍSŒÑh ¼Ô›nºì îà¶Ò)ˆ ;Ò³ï|Nÿèëd6Èús—®§³& ôþ.Þ×à§,cv7‘ ½s #ú^œ‚IâQ ÙÀ=\™Ú¸ó½þÉWܱ«!‹£Ræã¯7IðÔ!¾÷³'ß¡ïýB9°wõ­¥¹_®§?üðzZº~GزòðÓ©yI ŸŽ.ŽVÕ:¼\¥£WârßãõÒ¦]‡8äl_‘¿×룿¾:—n™~&M5P·2ßþlÍþrÈ3„‹ósé‚ÓFÓ' ë°ŒdËø m5Ø¡,9]®;‚Æs?yøö†À"¦Ãƒ[2í¦Y wžÅM  ÝÒ_¬ }Íù'Ó¸aý©¶ÁAÿ™³ŒþðâÇôËï\-¼³mïV¡E¼:V]GûTÓ•çL½ýÿÌ]Fçé¶+ÏJ¨š>¥…¹´cÿqz}îRÁ-Â.¶—&ލ 1ÿÕ¾ÂmïýÄÞã=T4%„Fø­»&M–4<¹ ¸w‰ëþ#Uô—WæO”¡;¯:‡;NY´`åfzú­ùd½~Ü—z•Òê-{Û(ÝÕ›÷ÐøaýJ*Yé –>üb•q9ÑJwÃŽƒôÎü¥‹ûc÷Ý@Ùfý›u¶é»3/ ÞÀƒ­ÚJ¯q5axåtÐyN YÓÚêveÉïÄøèS?»SÖ1/zZ ƒ.ïÄï¬íÄÖdæ,{瘧›ÒEïø“¥èì‰#èÜÉ£~%¹¬ϦŸþãMZÈŠñ¼É#©¼´€VqC"•.®m–laÙAÉ:]nÑõ(Îcë8°aF»ô‚õŒÞ;¬äåwÑJn`íþ÷ßߤ»®9—Þút9­ª£G¿{-®¬¥W>þ’v³•\”—C—Ÿ}Ì=èÇÿ=›ö)£+§NeÀŒöíWMK×í.òo\6E<Ó,õÍÞ³&¶ít0VmÙC°¾Ïc\xÚñÝË\nßžÅTYÓ@_°ß3ó|ÜضIâ3 w[0h×þ£Bé¶÷}C“‹Ë[J?¿{†ð(À÷òÇ‹ÙÃà¢3Æ%î³õ¤–ûWÑ,ö@ld똉´~L·o3€ïK-&Ðåßpñé4ˆéKâbÁWRáøïuWK7€o î]’oÖâu„ÆÿÖ+Î䎥Ö'¹fÚ)<$pŒ>^´N(]xQÀ/¼ZäWYÛ@{WÒô³',áHdE^*f¹<Èž¤õli溾ì¬ñžûˆ‡K¾b9BÇõË5ÛÙÃtÚ—¾\»Ÿ­Þ+x`โ‚jv{è½ÏWÒªÍ{©‰ÝÀ9,Çðm>pó%bè%”çÍYY¢£°;=™×¦ŒBS¹ƒ½ƒùpÁ;ö“¿½Á¸÷8ÃÒ¿ö‚S„· òõæ'Ëi%ËØzò¨4ƒ;÷&ÎsÍÖ}ôùŠÍ4ŽaÃ^+Ã1õäÜ —h·9›ŒYN…5Û$pÞ¬š†ñ²·T•µ–¶º]YúíS¿¸„ì$™üÇŽx—hCœãõ£;÷¦õ¦™¬<Ýò=Î B8r †Z\feEyBÈq—Ãâ±bÚxaUKÏ=ÛŠ^eÂ¥üä[óhÃŽýwººØÃ ’5M"4NíSn²…`z}>úÛ«Ÿðø›]¶Sh+¼gÞþ\¸©¡üs£"ÓŠÍ»©™| 7BuNî c—-bk¼Š`¥ÃÝ …½˜;ÁiÁÊmbž[§Ÿ-E(|XñHÈ®ê ;÷‹®¼¤ øÓÀ5':èy;yŒï”wy{ß7»ÝO^ /¾éÃÅdaEûun×Ô‹N†Ìø]¶  lÍ4šHßœqŽ8¿øá"²YÍìÆ¼ˆú” xû?È£ä3œ»k Æ1ì6Ã'#*z®$ägÿÑ*ñJ·¶¡‰vÐ<…Pn²ƒ­¬€—Þ™·‚«‰nfY¨ªk ŽÚOxŽ`õ¡30°O)ÕÖ7Ñóï/¤3' ¥o_{îÜ@¯®Ø´‡¸õRºåò3?ÞÎÞ¡îԶdzùvM1€îg¥<žåèõ¹_æ~”Øy¤wBì¢Üs¹sŽ9vq›‚ôîü•´lÃ.Ñ‘½èô1ô%wˆ1¼ƒY€«~ɺ„ŽKE¯–¡¥¢S"^ù‡ƒï[­7 Ï_w݆æÖßñ½ ›áã FÚä®kCXÇ®d¤Üë °Û¬TÛØ$îOYA˜„´ûà1!л¸A™ÄŠ ü?¾í2¶Dílm~B~yŽ<ñ°Pd¿þ#úç>¥þ,hc‡bÒ¬–`½~kƹšݾï¨PDWO›,~ßÌ fBٞʊ zÛH+7ícH¡c©˜´„Þý7."¬ÏóO­Ôòýö}¢§Ý³${Ì=…EË[&§ËÃÍ¥tþ)£:žÍÖ諳—ð˜øF˽ýEùòsn@:þ¾šN(xôüÑPKË\~¼eæÎ·Z̼sTOnèšEcI`hÐÏþøÂÇÂêü挩"‹Î¾—–PÿøÐ¨–°‡A¦QƒzѼ囸¡6ð¸á¡\ñlËn4¶~aÉÈwñíþ£5m¾—ÏÂ?ûOhÂÿ6Ýß ÷Ba ž8ó½=+6kv`r6@ Hd%ð_ó.?<ø xr£|Ó®ƒbnƯŸûPÞÒºRükä >bH®]9ñ1xÒW(Ϯ۶Ÿ]çkèùÈ¡#W³d[3?ñ ã×è 3C²Åg2ÁÂEʵk[Àí% {ýòž«ÎpM?ñÆgdg¾‡ÂF …;8T“5¯Áõ3vva(±Ï¼Ãµ%Þx_+¥…ãÒ –Úy,Å,ÜÂèË„1UXaÓNÕÆyq½Ô5Ûö²[)/Ðs—ïË3Æ[kœbBz­è݇¦+¸GÛÑŒÌà½po#fXäx垥9†­I¤É£ó˜ÐFvmçRÏM ‹AÿÐû†KK&ôxe˜,„©“FˆÙ§ò~ð½øp, À)­o|×Ù÷½ØB‚‹¿?[ã —ô<àþÅSÆÑ?¹aer3e+ÉjÑDæÜɧ Î).|g˜»Ê>jœÐ Ý´û ¨«`Þ^¿ãÉÎ ‡"À°Æç+¶ˆá9< X8²ü>\Ëá&ŒÂ …’ MGô§yx3ã1¸o9»™ÇµQÜÁ<‹qXLü‚ûüþ[.óþüZh–íþ†lÁJ>Âó2dB»‚öÏdŠtÒ:˜- 8w8PºÁp˼å9EdMðÝož5÷z<7KØxW¢ÿѶ³–wâ>±UŽ™éZ*ðzåá æGƒ¾ÇIæ,YϘê…õ÷Ï Æä™&²ÒEϳ˜¥k϶° w +6Üœk·îaWT~» WæÎV-\`˜d¸>ã%M5¼ä“DN=€; ¯ç÷|!õÞûÝéYz¾~÷MÏRU^ \zÆ81ÒÜ%ÄÌa(bŒ!~ûšóÄŒ^ù"ÜDpëÖ±%Üsw{|b–-z´pÅ¢WŒqÊX¬Þïò2Œ½÷ýÏ?ß=Û˸a?LÚ<žà1Š]e˜¼õ­wnh™˜p«ã…iߟuOŒjUP×ðx1&%=ÎãÐpÕÁE¼Ñi7=÷Ž&3 fzcü7Ü„ÙׯÍ^Æ31wòøÛ16&¿E~˜<W:P´˜¹Œ5Ôw]=•á^L?áßH¨—ÑßžW•Jq¤ÆkpãÅbú¯yÍ9,Að xf8™'x†ˆ%¸ƒ/Y9‡½5¯ÌZB?øÃ¿ s¦pg¼õOZz-UX‡˜•Þ¯`ü+Àó}¸¿¯8gB0øâ2x!?›Í_Ì„>gÒ01¬#_„rÇ ‡ÿõu1sû^žáœ®¿ðTzîÝbe—]ÊC¾vZtmæ”üêÙ÷„•ŒU˜ÉZàTµ_?cºØçõ^q•xÉd~ˆ§”“,!×ݺ™øÖÿb>¨øÔ/:çÌù·ÝÿßïŽRqò}7]CvíêàÉ:pÿ´7NÔþ­wÑóÌÊ2œ0³³õè¯Ðƒ‡BV© ·K1t¢•„®]DÑÁ„˜hËyEzFp·Ç+f¦Êo×lÝKÿf ê¡[/KE`½<ùæ|¶üm"ð€|OFþÁ{,é±gÑšÛ6½ôçß}Ÿó9ÌG%ÜÆL»Ö8þÑ R@–øçË¿ñýþ<~ÔБ‘È”‡ëÌËu‰ÛWÒ.²‚€ƒ-ØÐu«˜¯™îèLÓòÀãÿ¡.Â$”îç¼Î»Çôy¸¢Mv©K\pnâ±[tX;j70† <|ü}¢®)k©Û÷,ÿ×ïy%ãW÷È#SÙ½>_ÉÕ$ø™VO?ü­àÌ]SA/}¢,Ý®i°7 8Ñ¦ÎÆT¢ÍS~‡Ž@,©«†I{3¸c)3ÜoÑh>[·Õ<™ ÖHî>x\,] Î7Veœ—ºŽŒX×mãYAg1Tá# aÈ¥~Ã$Éjª z-{R°f¾³Î&pí,µWnðûÁc¸Á÷}LY3—/¸G*\Æ»Éæ·þ/ŸMQ^´|›`U¡Š‰ \è‡yõ‡_ð¸mm½_G¨Á³x™„JŠÑRÞ¢;8€Ì^ʃHZX#qÝ y m¦¦DÈÚ½˜GÑóž±,+ÿßýð›M‡’Es¥t“EyUnJSÁp¨¤( '0‡J­ˆ§¬ h5——»ža…+\ìXUì›ð(ÑŠV|¥”n‚ ®ŠSPPPH n¸·ù~vÝkSÛ ääÉS߸ëöí/FN H©Ó *N£( ( ( ( ( Øí;rí¹þ»[K2<ôðíÍ['çJ®WJNéªÔ„R7îùõ <Ë2ñÓä;Bë.ÿÈ3 ÛÛßô=Ž‹èZ*) t7 Ä"‹óÆó9ZšJQÀA=ËÞÈoÇÖî'Óû—ξHÔ3¥tEé(3q±æ4•flŸÂ[ñµÇ'àm™ýJ@+Xb¤@,²ˆÉWÒÒUBCÇjºz­Û=÷û½ä¡'Èhj‘Gx·Úç¶ÞÎË„R¢1QcºÝŽåºFh=´øŠwA 38p?–P`À޼ta3ÇEt켜𼀣d!² Ö#bó„“„¥Š…û“YqÎ᮱п71Àvg9úvB4-Ä_]ÎeÊ|EÑ®®æ@›v"@ñ"Ê… L¤9h_PÖ¿´–#N-ãMÈõíÉAç‰ eqi±8Ý}í¹I_L7u­(Т‘Ž 3¡†r‰“›9&8⥟˜¸+vÊøaÂŒ}vfôàÞâù¯ž·hË Ú[ÞeBt(Ä©E*.Èç÷'q´(í)âÙÂݦ’¢@ºQ YÄð ää+4•ó._2es¼fDSË´t¤j½·ð^F[k 4„ŽþÚîT¤ƒ²tS±Vâ ÓŠM»è“]¹y¯×m¯8lºyÀvÞa›×׳¢ÆØë(vga²SpžŸØ [ú¡Œe¼¯)6h€‹m=ìFþtÙ:}ìàbÅÂR~þý…b7¸©7î<(b#ŒU¡Ì³& “®j „yŠ„JŠiLpdûí﹊n{!$ÑAE˜RìRDTúI·ú]]¿›^Ÿw+ǶÖ:$…¹ý©©á;Ü6U¶mœRkeé¦HE$ Ä8†[ùÇ{ClÕ7íÔð£.•óþ¸ÏñøÑã/Ïâ8Ä'u¸iûÝמG‹Øý|ßc/‹INÓÏÖvPA@öÓXÙ®ß~@ìÄŠ3&„LçÝVy6ýìéwÚlÀ}w¡ÌúÏ·èGz•Þü4yeBáV¿¢¡@,²ØUyPÄ瞃ǘq¨¤(NˆEƒe5øøCîdº‚·¼øŒñ,çÝÓ#$îÜkYá(›M6ºæ¼ñÜ!ü»uXLÒ#UÎJé¦JM$©4£)6ÜoCn¸e…*ÜàïijÎ7] ~]]+ ¤<•§h‰ÇŽJÑÀ¡÷75 ¼ý&[¸õ- ×”ee ÷yê×㽋Ò=?¥tu'i÷Ìpφ”Öe÷ÄPa¥( (¨mØG/φÂÕ6 ’ ·ÏÓÒü¨·¤L äúQ`¸PIQ@Q@Q ™€Kùå93©®é CS¸ÿGåmƒù$Æ®ÊV–nWRÏ’NƒÇVÓóo¥&g•€ ÷šsŸc…;%é°E€Rº‘PK½«( ( ( (p lÛ7‡Þýâ^žH©Å€Â½úÜgi@¯3K¬*¥+ø=¢DmàxÆU)ª†×©béO¬ ó³9°DanÏú³sÀо"¦k¬ùêýý^ŽÈ³açŽ’Õ bÌ6·àëB¼tÁ_ozªüˆ2§Ò¥=Y¹åš³ì§Ì²š´çX‹éš©ÿÇ‘íNJK6VJ7…« ;‘¸ÝBÜáy_m ÚFqünÊñºÈÖ\OÙ^}¶èkÎʦ]ÙyÔ”e¡w?_Efñ<^¿{ÞÉ#Ûl:hR!²pÿ„ƒi &4ÖšŒYdÈ2p(5ý–Aø˜¦~¯Ÿ<>¯À??×&ƒ$ÿDÓ;ÊËdž nO>[™âö„7Þ1¼#O-KTktºXxÁOòy Xžl-òdayŠ®=™¿ò×´dÃ?àå  ™ç¿H…y{év¡”n Ö„¡Ü`Õ¾òÑbªntR¿ú½4±j õ¯ßE>ŠºËd¡½yi{ñpzû3Íç¸Ì7r,fX¿l‘È´ã7¿È,`ç+GÖ)áU=iŒ#ˆ®…•ŽfÆÍ[¶‰¾qÙéIÁ?‘´Î”²2•§‚Û“—?^Ä^2î´Û¶SYÉ>oáÈnšËVo>ðù¬äp çu´ãE{2;Ï߸,¼öÄëk¦ÝGw·îUлl]ˮ͚޶”ÒÕ›ÓbÌÊ;î T"¬Îbg%Mß;Ÿz5j³õb̾Óϡ̇VoÇ!{oZÔï\úû>¥ç,,?û¸ÓLtxˆMÞüt9/QÊ¢žW6Qq•¡Ðí+\÷ÕuMŒÿgŒÿ¤„⯠U!ÈTž mO²ÍG©¼Çûdµî ¡þ?¡Ìíö5âp:+¨ºf:Ë“³Ëö¤¶q?½ýù·épåÚPÃú]DWœõWšc ÜK× ¥tS¤ædoî/(Ü÷¬¡A5ÛiêÞOÈä‹}ì6R4¡ä¯Úúšß+@â8¦>švÊh±é@¼¬^и¿ñÉr±Wn1[·É ¦E߃w]ªªi€Dài©÷»¦@¦òT{í‰=g•”¼Å2•øöJ¾gÏ'©²rF§íÉöýŸÐ‹~È»‹Õ*wÒˆ[é‚É?ã߉õ¶Ðù¢5 Î«ì§€Œß®Ý¶—ÞÿBS¸Óvœ…+!‡² PþØ¿vo"ž3àÕ;!O¸ÿS9Çj¡Ò¢ä)\‰>à<ïÌ[Wüe™ê¬2•§ÚkO pËJ_KŠÂ•5 eKh{âçN=Æoߘw{@áfºà”_°Âý9gÑ=.h¡”®äˆ$ž¡È pôÆœeÂ¥ 7U`)rTÑ«Éc4ÍqÙ: ³‘_â1\¸”aá¦RtÓEoѤᷤ غÁ¡”®n¤Œ.#\Ê®æfš¿|Õ²R›Âc¸Ép)w„`9cß<ªá-ý>[¾QÀ ¸õJÈ ³”kx†2öÐM–K¹#|OQ~ŽØË4øwT®º=2•§BÛ“º¦f**ü ©nh-Ââ-.|ŸåÉEoÍ›EÏ~x í=²4ðÚà>çÑí—}L½JÇîu§ ¥t“X›paÒT3+\o¿„÷ Å,åDLšŠmÀؾX¹EÀ ¸¬IÒà3îp`–r¢&ME 7à| VmÕÿHáPïwMLå)‰·lO¾\¿CÌRNĤ©®k¥í€ 3¨—¬ßGuõ•ì=Ãs#3áA±5ŸÕRØöƒnôK)Ý$V¦Ö+õ+wç¾#ÔàòÐ^”ª °Õ³%¾ëÀ1¶v=º¸™AƒÝœ_=/‹²Û²Suàk`k_OüSá4.Sy*´=itz(7gMÊÖ"`óùòÉåìM¶ìR^û2>æž”…W/À”ÒÕ‹’æ蕺Ýätºh˞ÎõÛ¢t]®fró f½b)GG¤ïÆæfÀ<¢M’.Î/Õ­\‰#àÔ ™§:ëGLå)‰7”—lOŒ}b³ëW;'æ3Mž”Ò=‘â~® é„$Ì6ŒEyÅàüè00ÜÒ½ \"MÁ4ˆæûHËÓó}=ðו—FLå©`¼3µ=I'PJ7Iµ%ÝAP\X§ËZ8ID^,„pëå^N;¥«þ‘S^}Ѥ\eOI¼3¹=éŠ7Rå¹RºI¨ Ù3…žÅ `+°ôQ¹ØÕR[_ ø%.‘’Q~‡<Ò©Ã<õÀ?Rz©÷»¦@¦òT0Þ™ÚžtÍ©ó†RºIª‹€ pŽ^*~§M‚²Åà Üòû4Â\«"ðO›úN#@3•§ZñÎÜö$]ØT)Ý$ÖT@Pâ¤p fK\°ƒ’”°ÇZòI·¤'þé†{:ÀožBd2½“<%e2Þø§"îzÃÏüÔÖ~ñ¤nóüÀcä8¸›¾ô²öDýî|r‡%O] Ÿÿ>zõŸT:íkTvñLÚtÿ içÒ튔×N›LÇky­N=è„×ß¿Š¿iÒÈAôÌ;óOx®n( SPî¹ö|z‰7ù8|¼†rxåK§Œ£½K™L4kñZZ¹yÝpñi´ÿp}±zkðçi=zà×èÌñ?¤§ÞJe…ÃèöËç´ÁéÍùwÒÁã«èÛW-¤ç?žNÇkºþmÕù‡Rº:4œìôî‰æOyãN¡=OüBß뺻ÈÏÁ+ÖÜqåKC~úwª^<—*ç½OåWßIÅg]BU > Ô°Þ‰Ÿh¾é˜¾=й1,£®¡l“‰6ï:xµOY!êhn ªÅ†gœ4ŒÆéKë¶ï¼ë…ž¸Ä K&¯g=œ9~íce …‹ý£o¹ì Á?,\Ía@Ö§/äXÜ3/<•VlÞMMNý–èD‚K$ï†ÃØRïì ÷ÓÂ5±GËG6K‘øì•¹×ó ÇãuÛ9¾@ ­Ùþ*MðØ’/œ¼ÃyGo|Â)3‘ï(¥›Hj•Æj=‚DqÙsÆíT9ÿ¶j«ÅצüBrìÝA>G#9ömVm–5‡ü^ûèU*ŸqGÌJ·öèÝÃ2(PnóÉ”ñCyâ}¢Ñk¢fÞ­¨)ð|2[½;ö¥êºFqï« »ïÇ¢tVÕÒÓoͧ»,V+™ÌfîÛHÍð¡FþâoÿhkN°èþNî$>úÜû1!йÎÇåtrÀ…fÊÎŽnx›[LYA¯ÌZ"à=¨ØÚñ5Þ&³ƒƒÓ¾#Ut¼º&(6 ~éµ”œ#MÁßFñy›âF¼’#EYhýηÅ}(]×I{/nó~,ÝðÝsõR¶†‡Ó±šèCNÃB!ÝìFl-E7#Fº¢“S1Œ»Zþð[ÏQé¹WP¿;¤a´P¬XÞÿúæŸÑÐ~Ьæw‹ò~«‹Î) ÜËÓ'åŸf³ÂEj®<€uЮëuw]B%<~;ä§ÿ m?»›6­$wÕ1¡€³Kz’sߎÀ7éz‘Ï ©Ž· N&SMVA‹×²{=(a A¤¶‚sMêQ\@×7žJŠ‹(?/—wr±¶±|"Íó±gÑšÛ6½ôçß}Ÿ¿=ÌG%0³|´õgò4Op1Øø€iXÂGù7¾ÿÀŸÇ:ò¾›.æŸÑ'¸~±/u]}=ûÞ"ª¬ù"Oðó“M“C•µôÞç+EfX ‹yŸ-Û(~×q,r|Ó]R¾½7[öËèìãk2Ýzé4nðLÚ¶o®¸U×tñï#«sP–nJõÇîjV¢œ²‹ËÄ9ËžGùãN#¸˜=µUtäƒSãö TxÚùâ¹¹¨TXºò;q3ÿÕs#‹”ÇÖHpÇ¢Éd¤Õ[[=xžÛò^}St rpêº{R›óÆl­§CX¯Ö^Foc=O¢ÚF…§œKF«lý‡RNÿ!Ô°a¹xn)ï/ÎÎCûR§Hs»½¼±¼“Ç™ìm>ê ;ò ˶3Já:DªªmlÕæCõ#ã)PÅ[WÚs,biˆ±}ÿ*/-  Ã+¨¬(Ÿ­Ü~´i÷ÁàZ®äåjÝ%UÕí¤’üAt†W\B½JÇS޵˜†÷¿˜†õ»ˆöZx^”7'*î üVS@¹—;§OZZ½F ˜ÎS?²Ädª¢Ü4mÒÏxîD9¯…ßFË·ü‹–¬ÿ‡@//§—PÆGª5W{ºâœH¸•ÒM$µãTÖá7ž¢¡ÿûzãiòÔTRÝê/Åa´ØÈçju£LÙÔãò J¹;¥/Öl¡/žÂë ·Rc“K,joùIÏH=eÌ`úì+Õ@t§ú×xG–mÜE§"”.ò_ºa§8àVî°UðÌæ’B;½öI«ÖžDç·a×;4eì÷xÜöZ±wɆŽls.5‡ìù}ú˜{hëÞYt¬zs¢ÁLÛò”{9m«®ðÆmë©já,*;ïªÖ›|¬pñ èôiäØ¿“ªÍjó^ºÿ8x´†]Éû餡šë¼#|FìÅk ëùݽ¢î+  ,ZÍC4¹9Ô#d)P°ÂÅ‹G «¶CÇÀÉ®ŸÏCŸ­ø2“A1À U¸9ÖÔçš·òÑÀ;ê¢k (K·k¥Å{Ÿøe—pV}ñáèŽé£…k»DkýŽ„C%E®(€åBO¼9¯«×èíy+º|'_ÀÌd9;¹#ø›œ•ôÄÛguôXÝï€ÊÒí€0ñ¾Ðr­G¼KÓ?ÿVØ[{‘–"óˆô»Tx_ÂŽ³J©CY/©QøHØ£á©àoÓ‘%ƒáŸbéù¦RºI¨·ö„ Q›³ôß½Doô£9«-Û´‡OWåcæ5µ¾Xc×uU NϧøëŽÊ&ˆ™ÊSÁxKr =ñùS¿=ñû-'Om[OYcêœp XYñ4ñD…TO€ÑšÝºFQxí¼£‹¤CœzãŸx§Œ™ÎShO|žÖý©Z^o~ÆÉ“RºIäFôPåaå@“•\¦è‚´' ÀísîXËþù¼&ÒË»™¤ºµ ø§žøÇJ?õý‰ÈTž’m ÎhO¼þ^úÔ6h̉ÔJÞÀæõådœ<)¥›$ž ÏDpõ’\ Ç65Ð^^hžª °ÆÞŹü¿µÃ-¼’å¨Î1S|(àœzá-ÝÔwS SyJâ ¹ ´'~‡ÅlÝ1Õ’ó°ùÆL“'¥t“Àoá98Y<>jäµ…v3eý´½8u…°YÙ³\R#àÆ„ ‰K¤d”ß!^%ydË6Q££mô¨HóŒ÷û€Ï„øÇÖLÌ?Sy*ïàöÄœåçhmãS–›…×Ïdš<)¥›$–DoÛËá@p~o¾Þ3×LûòúÓ!8žj 0¶Šù^ ;ðˆ6Ó`TÿR²Qîìmžñúp!h€²\Ý𬙜o¦òT0Þ²=)玼Ã1„œÎŠ”c ÀØ”éמ¤’}‹ÙA†êv×=SMášx³íl>ÌÔ·ÈJÙ-ì7•<ÆÖë]çß7Ë"† Vî°^^t¿Ä%Räw’ãõbk7KÄDö± 7•à©âЀ“A7üS ¿îK¦òT(ÞÁí‰Ùè£Êêé<_"uÚÀRÅ0YLþŒ”'¥t“Ôâ±S ”­Å’MV¼6«…ú˜¨š#½Ìï?-IX,`©b˜Fôb70Ãx7BâhS0 rx2Õä!=ÉíñPU͉{ãF[†ßUq+·ÇK#Ëy?ñ×6•G[ d*OãÜžôÏ7‘Û݃*+g´%T–f†idy~FÊ“Rºá3ì/?Ï^mtºÝ1ÛbÒåeµXÈÊ{²b_Öù6*ÏñÓÎÂ!ôÉ€K’jñ €e@Q6õ)åéý #àܰRG´)”zÓ¨>Ôätñ^· Ü;6g}¾CùØs·‰'PÅ ¥³Ùí÷x<’-x,è,_énç6xz¼‡2"e*O…âÚž46¦cÇf&Õâ……{ìøLŽ>:®ò>€Lù|~ôÞ%¯ávJ$2¼j@ʼnäs{ÖÔ6âwô&>æDPZfÞ§ÊÖnÏ!+—ËE}=nòûBÙÕX éŒ}ŸS¯ÆÖ­Ä4Hâûc¸psÃêîWEC¸WšË0NÀ ¸¥{9ZHÚ£ÁØ=ˆm=ÒHžJïÛig/@â]cÃ…Kn¼ð—t«©kô7»\5òwÐ9ÀwA÷Òýòœš®=d „ÉTžjïÐöäˆc¹”RqádµîI(a nnXÝñ–' ™òz<Úfã¦'ð]B T˜RºAÄçÒír¬w¸ °ÆrØÕmÒ„ÄÈ;w°¥ËùäÚí¬p›Ùíâf¥ã¥>¼µXv“S½?ôjêW¿—†Tm¡þõ»ÈâqE[l§ßa.–a–2&Ma|yH¡‘ú²…›——Kö\»€ðnÌ”ѦŽh0º¢Œ×hóáF¶}VvgÛmp¿g“1†òº‚ëp±,³”1iÊÌFü¢øáxÀGà'gScpѨÝê9p×C¦@”L婎ðmO9JéðÑ;X–¶³,¯áôöÄg›O¬ÃŲ ÌRƤ)Œ/Ç[žÀR¦ÐVãwª%¥t#«ÿá}{å—”|Ýötê˜A‘}ò6\B˜„ñ\XnV¸^Þç{y"áy^¶“Ž6yèP^¡ ì-±¹”ã©§l¯;$Çè~6g™©É”G³U¬Ã5“zÚüÔ·ÐJy¬h ò(?Ÿ(^†ðnÀkꈙ…V3ííç/¼¿ÐçŒsZµ[×yð†dT(4qÍŠ×Xo—c³ …+&Y57‹IVìŽl£t;2vk²[•cYiI›r[ŒÜ€ %ÜOp£\(^X»˜4e‹ûxŒz§xÑ #8% ²ÜDã¾9x¬†V|1ÿßMM X.„5P¾à3És|Ù­’ÄKÈc&ð @ ã¹|[™ÅdÝê-WÕF²yJ•h¼5Zk¥Ë²-O(]ÊÚf´Ñ|+%eI)]WÂùÞ9*„kñœ—ä½È×·Ð[ä¿yú™†X,^0«\‚Å&—AÂ¥ÚÌKX4+×+,]?·É°t;³v«,­J·gyƒÚ6¡Lqðê'Xº(Ö.Öࢡ‚¢5 ëW›© ¸â™âAƒÎàMþè£qX¾i—aûú5ï¯^¼`ÈéèòŠ·»[ºY’¸ƒEe=>àßWè!Sœào½å ùv”’ÁSíÁ’hY ÉÂ=X¦öíØòÚfò”’²¤”.¸%¼$]b¨H̱wÌzõ¥·.¼öëXDzý¡ÊjÿõfÔcŒÌ+"Ì@òÒ¡lÙŒ™Í8`Ùʉ)ݬyiI%ÅEò2pF9Hšõª#£Êbå å+®±!Cœ¬Û !²<=h’u›Ÿ‰ÆãMp)ÃÂݶ~õóß}óSHðŸaíâü+°»+]à%¾þ¶yï¾ñ)w%Á˜—é%SœW€‡»O·ÎR¢d 0$ZžPf°LíÙºé•9¯¿üßNiYRJ5^‚[ Öé Rù°r%¿3ñ¬©G}Sξí/Î*3¤¯òèA†±CúD½ŽÌ …3ÙêÄÄ)9Žláv¦tí¼ÖU&L† MRHp–Ê”«ä½ÐïñeëAƒÎ`EHOœõÆk±„3*1iÊëq7|5ÿ“7Ö-]¼ž‹–|Wø ¿Á_à3ð[wMíÊ#‹…ïfîŒÌ«<|èØä©Ó®a™Ê×C¦@ÈîÂSÀ%’”¼Ê‘g\ãÐ[ž¨La^ĪŠþµò‹ù‹ùqÊË’Rº¨Åð’l(dï•‹F"›+{ÙæË·ŸvÑ¥z½ž©Ü¸æ3ÃùólaÝ`5Íh ¯¬vß U°¡¿C?šÊË`dzâÍÏåe›³y3ô·¼Ÿ*çPœCG g(¾¡¿#ÍO¾°†ˆ²„  £P¶;7m\¸dÎG‹œÎ¦&~x¨¾åŒë`K7”î ²Ä4@›dD§dÛšÕ;O»ðÒ3¸£r&ËT×î2Åe0D“ª<XõL¡x†þަ¬Pù ýMžò›dÊë©ß½eÓü%³?šÃsªø½´%¥te†w–ãº/€e×2¦.¹Òé³·ÿó_:tìI+†iËÍ+²Ølù&“9úÐUœa´é< ”ß®Z¿e—¼VçøSÀãáx8MŽú¦†ºš=[·lݾaÍ>.UºTaÑBÉÖ¶u|?¯0ÖÙ]ËŒžHÊ’|Îïü÷Þølþ{ôùÑãûU >,'7¿Ð’cËK–LµÀ¦NI €)‡£ÎÑP_½gÛ–MÛÖ­F›–v²¤”ndÌlíbÀ Wš“²q33lác'?½v>ã]ù_&&ý×e=~+Kzõï=!¯Õ9a¿€/¼Á×°f¡`Ñ+‡ÒEÌe\ÿ?{çEµýñ33[ÒCBI¡7 XPÿú, X)V,ÏŠB"*TZ‚o…{{Hð=ûSA‘ª`£**U‘$´„@Ú–Ù™ÿ9“̲ ©›Ýlɹ|–iwî=÷{'ó›sï;tóÐûsé¼Põú[Baø3âCËnüíÅu¿ü-…zeAùBæo‰E·áW›~%¯DR÷íÇIËû§p©y¸ÔG5éçà®& t£çÐ4èz   ®>€® ýÉœ<]òpIp››—‹EÖB°þ-éöóÒ÷Bîo‰E׳‹†núÄÇúmÓM•<]í —ÄX^ .šÇé?MF@¿.Hp©É˜®º6HtIdItõ¾]ºnè8]WÍ-ãßRs«#—7¤þ–Xt=»œô‹€n”îëtó¤) ®6È —Õ‰®?Ä·Ù~Åë )]ôëB]j:ÖÊHxI€i›öëý¸ú¹¸«ÙS0ý-5›ÊñsAõ¿ý ‰¿%]ϯ*ºè"p¿ èÆA7R`eŸ.¸zß/‰­?³Å¯rh*tMè?ºFÜotÐÒ’~t¬9z¸XlW¶¿%—á¼âsúß‘~ýß‹n㯺aÒAK}–w±Õ×ß}ºù/*§POt=PЯ ]xõ†¾­/Íÿë<ýo‰kªé„Üß‹®w.ý)L¿YC"«´t÷pýåéÒ MC@¿QÐRÿѵ¡ÿô}McMpåBlè¡$ÿ–‚‹hp[K×ýo†–úß-õý'(‹®w«I¿èb à.¶ú¶vÀÿQ?"‡¦#@ׂôëBßæeÝtfø·T·õÛBêo‰E×›—Æéié7ŽÓ4ýò80`%HKÁÊíz?c˜Â&0&À˜m,º¡]¿\:&À˜ ,ºTl `L€ „6ÝЮ_.`L€ ݪ 6… 0&ÀB›‹nh×/—Ž 0&Àˆ‹nU›Â˜`¡M€E7´ë—Kǘ`D€E7€*ƒMaL€ 0Ð&À¢ÚõË¥cL€ 0"À¢@•Á¦0&À˜@h`Ñ íúåÒ1&À˜@`Ñ  Ê`S˜`L ´ °è†výré˜`L €°èPe°)L€ 0&ÚXtC»~¹tL€ 0&@Xt¨2Ø&À˜m,º¡]¿\:&À˜ ,ºTl `L€ „6ÝЮ_.`L€ ݪ 6… 0&ÀB›‹nh×/—Ž 0&Àˆ‹nU›Â˜`¡MÀÚÅ ­Òœ8µÓ)_‚ÐAÔd º¶.,ÊqNIo»ÜµQeE(T!wï—DiéŒÉã×W‰Â›L€ 0&à,º^€èÃ$„§Ÿ~5ìdXñ(I„§dENPÃ%J¤\"˜œöz·T$å\Y“vɤ”"U«1BÄ<2SÒ&å)ªðZ;S»·,–aÖšÎãýL€ 0&Ð0,º ãÕT±Ñ‰ñÁçÒ¯+1Í1¹}Q¶Úµ`;t(Ú+˜e›äeC4ñ¶ÌÝvÇŸ™˜Ýaz®=û©‡ÇYþÏ‹–¥˜:Ę`L 1êí)5&>·A¨N {~´Ñd^g+H¼ó+¸~÷|¡ûñ¿·A‰5$2¥MyP^”g ëñQ2,‘6éiL‡¯•†Àä¸L€ 0jð´(~ÚEÞ-y°ÆǤ–$ãô®…»„›w|)%•PwkÓÊó–s¤®…;ñ^>á^²ìäÀ˜``ÑõšNÑš“1]ãÝ?sƒÑhž‚‚«^½ï[Á 8|]ý’¤¼É²EÄé<;áz<“®Þú!äXL€ 0JXt+áðÛÕƒñœs.ŽŽŽžo=¦^™ýCÀÙW–¯šMæY B[ùºñÛ¥Â3&ÌøæéÿÚ£: mæó®ºò1U”ÿq`…äO·*²å²ƒË%U“Úžƨ {ùÚ© Š·™`uàg€||˜¼Yê'5á/<"2â‘8JÙ}¸u•“l"ÛLfÓãörÿn]Ðø8`L  Ý*@šxÓåå¸å΋Qj¯L³rUšm‚˜pó©á1òÎùú© ‰·™`µà›f-p||ÈÝË ‹o“p5îPñ=\gëyòdÙÕ"öL…¼söv=ÇÉg2&Ð °èú¯ÒuÑ5¢ á¢ÁÐ!ÌQªúò=ÜÆ•lCôÈ;bZd7‹nc¡òùL€ 4+,ºþ«nb¯÷ç†IFC›G1 q@´Q$! Ô=]¾†ºÆØ8&À‰ß0ýWÄžúEI¼Ì’ F™GÀ‹.Ù(‚H¯ ‘Ýܯ‹80&ÀêK€E·¾¤¼ÄUo^Ö„WUƒg UUõ½y9à¼[}œ`LÀ3,ºžqóÆYÄžD‹~(¼j —f«n;_CÞ¸8 &Àš¾aú§šuOW÷v¥ ótu»ÝËá’œ+`L ˆ°èú¯²Ü+ëÁÝ~ÿQ䜙`AD oöA„·NS}*\‚Æ:ù,è¶û,N˜ 0&jXt¦F×§ÛuÌ«|ïZiÂ;t‡Î£_„^¯€sÞ™q—^«ío5à8ûåÏqé%&À˜hj,ºMM¼ìq^Ú‰å¯7év³à6Ž&ŸÍ˜@3"À¢•Ññ (Û»]+‰êA• EÐüØ’l0‚hs».Úvéžm–Ü£YÛæÿ˜`L éÐÄ ‚˜€` cË6`E¯–‚*ÛáÄÆŸ¡í?GA mTÞ †…8Úqë!Œ‡}ºæÄv`=°[ÛÇÿ1&À˜@Ó`O·i8û, .{þaWûg¼‡ç y´Ç–~©53; ŽhÇGQ™U0µLpÅç&À˜h,ºMùÚ\âÃÃÅÇÎŽé5þܘá1ŽâÕFªc§ã8Š(S|kWL¥¬ûr¿€=/?'·üáºBñöÍÚqc\+ÍÓÕÏsÄ+L€ 0&àsܼìsÄ•3øã¾„È¢õ:«C¹Õî®?áp¶Ì·)P¬Ê±áÏ@е È{ KêÅÛ6j™‰8rYuØÁŒ}·eûwAñ_ëµcæÄrm·æ¨lo1&À˜€Ï °èú1À†!퓪m°  Cìp•Q…0'ö«ÚAõJî¥ûv@x—³~*O.éŽHt/Ç{bÓÏ5Õ•Od—³Á–»T»ÕµW˜`L i°èúˆóº›ÎY½ß®¢8m}0A­FdU=z¸LY[,Ežeí=1çЗYÐý_ïBÞ—³@.̇ÜO߆Ãó>ùDA¥äh†ª6ƒîœß¨´Ÿ7˜`L i°èz‘óÎ{ºÅž Â{½çú«ò±m¹ÏRìܾ©mç®wfGw†îÇÿnPBM™lC£…ƒ{v‘7ïnS™Àù0&À‚š‹®[õí}°SXÁ1ë(§£l<¾èçvˆV‚(¼jjYzµîɹ }‰À¹eíš]ö¿úÄîø3cPt²‰mSÙ^¸nåÛÈfü¹—ý|¼Î˜`Õ`ÑE(êСÒzëÊaùÇJ-ø-€¶•8 °]R…éJÛv÷ÍZ¯þx-(˜’&¸¸¤9 å{ö.•Î<뎼ÈdH*ÉõZFÞHˆlÊŽî äíÝ=¿Â^Ýv*&À˜¨†Oö[Dƒ)ÊúÁ W­/]¹EUÔYî‚‹£wâë>wõíûhÞ‹ÿ×Ë‚«{ˆ$\$¸vúýüÍ‚ïœv[áêvýY4 F²…lR¶ã«ΛWa/Ùíî팽l`L P 4[Ñ]wkǤuþ§(êØ”|¶^AØ®›‡‚;²Oß=ú,:ü…`±øÊ“£tuÁ¥‘S¶ÒÒ¢âkV}t<¼%¬è0€„9 ÙB6mYûÛŒ¢¢Âb4Šì¥²ßW|0iL€ 0Ð"ÒÍ˨ZEX]IîU¦5%—­|\µ•MÂý4#”N ‚ð"Ò}æ–¢òïÏê}°$±"O‘Ä‹¾>@s@–m\½bc\ë6ó Çy·B§Ô+³ Š7[µ1—zòpIpw·è&ìß±í³ß—}ÿžJv’½d7ÙÏ¢‹80&âT!K˜ÓØR†´èâW}¨c”>6 …?†$Ÿ¹®tå'Ø‹[1/rù~Ûÿ…‡‡?ÓsÎ>z¦©y²$Z¤¨$dôÊQþÌ?}=g Q¡çyCއµPÿq`…ØÔ}¼Ô‡KMÊäáîý{ëì¾úœš•ÉË%;É^²[o^ÆUL€ 0&@ܺƖ0¤EáìÇoû$Œœ:5îá_^¿×®8§¡àÒœÇZ@؆ó!?Úwѡ廚r¡‹®îé’ ™ñgŸá§ys–ä>t¨ÏeWܽ°ûm±вծÛ…E{}ö/½‡K¯Ñ(e4å´[‹6­øñƒ «—“‡K­d#ýÜ=Ý€iG»80&À¼N€4ÄY"'à$Hû›xH‹®$JKÍÖ™7m³J£ÃB±µ¡i¤v¯\èÝÉzõ]êýºÔGJ$ž¢y™µ¾ö?Ö¬Ú°cÓ†—\wc¹û™Wdwìƒ}Îj˜£Tp fÅá•W‹l¢Q-5F©4ñ*¨ ÛmEÙÛþüiÍ’Åß•–¢='*~44ÙIör.BàÀ˜@èPJ•8ÐV4 ¦4¶´^¹i7Ö_¿a`€2É´Ôä´¹Ûdá¾óþÓWù60]²Ä–š–£ðGýÌ4!G,þ¨¿Z¤3žqî:žqÖYáQQñæððXƒ„®©Îûlµ••,+)ÎÏÞ±ý¯í›7ìÁdÉ›%%—D—Ä—DW÷t©y™ûs&ÀB›@JzÆ·8àöœYÛ7¶¤!ë鮜ð´SU_ÒG$£@ÓÃÃ[ý«çœ­Ô¤(ÁÝÛÕ‚Ü›žÉ«¤&qóŽ-ÿÂßv\'¡Ö$ôspWƒƒÞ4L6ÐOïc¦<©ßVoN&±%ñe/!p`L ùHM˸BQÕëQ?žóF©CNtiV)œäb&6'߯*5DÊË:]·/7î‚ÉY–Ô@\ÝD<}ˆ2 ¡¾MâG'‰.yÂäSéMÐ\LF z~$¸ÔdLvPžúà.^úªýÈöp&ÀBŸ@Šef„â8ú:mÙɆ¶o{£Ä!%º›µk{ìXÙ×8ò÷BªÒÏ+:x3'ªÝÿÇÑðó|wâheÝÃÓ£ù{©  šû:‰‰6ª—Ú +\V]OÄWg ç§‹.=”èbOÂKLÛ´_ïÇÕÏÅ]˜`¡G€>åššžù–¬'þn´X†Ñ½°ÑÁ“›u£3õEënJ8W•Õï0íD=}×YØœü85'§LÌx ½ß×pÕ0¶~=^³@ ÔlL¢J^-ýHhõ¥îåÒqª;ý‡«OýG¢ë.¼ô@BKKúÑ1òÀ90&ÀBšy¸@NN,ˆÂÓY“Ó_÷VCBt7Þܶ—,Ë?àGå[,Š„ødßŇf¸ƒ*^xÝ?ñÞ'ff¦¯t?@ëT/ºø’Àº‹­.¸tœBcêP÷XILi]^]|õmý8åÇ 0&²´>\€·°€=žõ¦à´ÆÜ°úºÁɽUÅù=jF¼V  EQº©÷‚¼j5%}òµæYðü}*Fˆ‹gŒ< TÙª]€Idé§ïÓëN_V>³~[ºèêÞ.-I`õŸ¾¿~©q,&À˜@ ÷pË_ RïÁfåë©‹1"+c"µžz54æ†íUC  [‡öŒr:Ô.Á„#¢AèÇ‚TÕÈÆ2&Àš% ]zoª¬ìØGØ—y.Õz¸6콩÷üC5ËÚãB3&À˜@P*Ñ]?$éÞ[tÂø… ”> ÿªoó’ 0&À˜@ š>Ý ƒ“nÃO_ê0Ñð—û.>╹0õ4yɘ`LÀ—‚ÂÓÅwq[)ªò®ß­ý¦Ï…ŽÕ·yɘ`L …è‚ê|Ã5p „ý±±Ñw  MàÀ 0&À˜@ÐxÑÝ08ñÜêDqò‹ÔîŸî¢OÍq`L€ 0&TZtéõ üŽ¡«Y‡+Œ³M- *Âl,`L€ 0 ýi?|=(½Üd+öã‰4žæšcL€ 0&¬vô2žêм §é¢ÏÚá·î„»{/>üy°‚f»™`L€ ló² :ŸÓ«i% ._¬L€ 0&ìRtÿ¸%¡ M~ØW‚ ×:¯0&À˜R)º;ŒÆÒ‡kLaí… Žà÷r90&À˜n'ºoîÔçVéÂ*@¦kW˜`L€ 1€]ÅQö òŒÑ˜ Ÿ}ZÄ|Ùt&À˜`.'º Õ­Ã/½¯ áx*L€ 0&À‚Ÿ@@‰î¦AíÚâ;L—T`•ÃÃŒsƒ1—€ 0&À˜@9€]YpÜŠn­öî0~+wYÏ9 ¸¢˜`L€ „ €]UQo×ÁbÓò}—L€ 0&ÀB@ÀˆîÖ¡Qh/Ó  àÂ…y¡˜ËÀ˜`L@'0s/—–Y/´‡lZ^Õ{Ρ£º‘¼dL€ 0& ÆÓA½À è¯në¼Ê˜`L $Œèâ‹A½t¢8’j“¾ÎK&À˜`¡B `D§}tyº’Ѱ1Ts9˜`L€ èBtéøªP…ƒ©ŠÏ?Ä.Ý@^2&À˜!º²]ty¹ª ›‹?2Ä 0&À˜@hÑUE¥½ŽG.oÓ×yɘ`L ”„èb£r‹SPÕã§Öy 0&À˜@è ÑUÕXRA8áZç&À˜`!D D_ryº*…!Ä—‹Â˜`LÀE DWSž®Äž®«rx… 0&ÀB‹@€LI}ºåŸÍUU¥I=Ý‘§öq*Îë°Z;ª‚šŒ^wthU1—&Tà_H‘  ¹Xžý’(-1yüúP)—ƒ 4!º8UX¹ä(¢jõ5|‹åý°GÎã8õ䓲"·ÃwƒÑÙc¾tC+öuþœ>ð½ËÞ¯Û¼n3G¤O>ˆ×ímmß¶X†ùüïÆC›ù4&ÀÜhß®uÛöËêÚA óqFª!”¹ÂM½^à+CRÒ'_‹iÏÂ÷;‚°ŸŠââãÇó¨i_Açt½J`äÔ©qJ©2PUÕ{ðw=>4fc#²2&~çÕŒ81&À¼N PD÷3Ý»¨t(ºw£è~îõ’b‚)3žRxYÔ?±™î‰™™é+}‘§ÉšŠ@jZÆ8“Ì[˜_OA„g³&§¿ÞTys>L€ 4œ@@ ¤B³ËÜLp[÷Új¹àª¯áSÆ\0´¹”×kh9!? ëX0¶¾„®kUQ_£ëÜæpÖL€ ÔA <Ýuƒ^Çf²'ÉVl*{¦ï¢#¯ÖawƒS“²ª ßÐifFÚØ¤¬w!7(ŽÌ•þý©é™_à…}+^Þ7rSs ÖÛÕÜ „§‹S?æè;íôuo,iЦ3‹š”ÁØúA\oPå4€v]ãõvmÅ߬Šë>ÐÌd{˜@³'¢«êA½&ðIÝ«¢K£”µAS؇›eI-Õóá%5t}ãô(ºÞµÑù¡V@.!º’ º<]Uð®èÒkAè,á>ܸZ¹uÐúxñz§ë¾ÎÈ 0&'¢+ˆj¶«äªÚÙµÞÈšøçÜÀ÷p…O™ŸÎ‚†€v½ãu¯]ÿAc5Êš€Ý^_çíÇ~Ý’ ä‰ë'·ò~šiŠ&¾ ÷p½‘§Á‚]ïtÝWÌ´ &³L ÙÑÕÚrð‚r®—j #Í4Å_x‰&'´ë½|†µŽAa0Éš€]· [\ÜUÕ+¢Ks)ã¦väÀšºîéúoV…æÂ2 s/'|‡öOýåY}ÙËì0MúxÏ¥ì ˜œFPÀkŸ®{ºþ90&@šÔÓ:SZSÙEƒô»~Lõr}—L€ 0&ÀB…@“‰nf–ø¤Jæ´ÿ´ªöéÛdŠ[‡ÓQiÓA¢§ÛmëÐN‰¡Ù½øe˜Õ©3qßçËõ§,¯µxdâ”ø*¦.¯Êá‹tG¤eÜ—’–±ÖisšL€ '&i^ž2S|§©{,<,¬ÿèŽU‡ªçœ­vœò7lbîGÇËJ­äíΡõ@)éßcù4jTûL  îñƒ$©ûËv½ Øìw>®ÿ¨oûzYæ(y'ÄŸ”òâ‹qYãÆðv~õ)ÎG’Äëß”¶´ª Off&”–*‡DI2sRúªÇëÚ¶XTÑb°˜p€`¼..,«Ø&ÀüIÀçž.z¸£TP/Ü2×$ÕZVéûqÚÆ+ôõ XâèkQª Ý"ˆ‚…7WQÔwsäøÓ~Q0ÎÁJ~Ö‚ÛÐr)NõÝg_z)²¡çÕ?GÎØ?Ê2Í«3™Õ•'gL€ xBÀ§žîÔ™âãŠªŽª—à¢õØ—»=݉Tônð¤@~<§`fÆ„oÜòÏÂ-d`SyZêħ͜<î·cM¶:#cìvÌŒ~~Â|M{CÑ k&âµ/á<2qêNEfÁõsíröL€ Ô€ÏD7s–ø ç#áfjR>åá.[Öϰ1û·›ÁT:þž•>¯&µ[¹Šp©hß®ëoI:»Ï×yÛêW”Œeç‚CIù´Î%ºøA95}ÊíØÇ=¿wtNÙw Û¥¿Ž‰1Ozù¹çJRŸÏ¼½ÂW%QzðÝÉã7º—ìËÔNN‡s>~xxÒ¬Ié_Ñ”Ä[ñ1e¶(„csïïèe¯Á¯)½Dï?cŸâÈrtVFZ/í}èŠÄIŸÜO! Ï;wÑG!¶ ‚ôäÌÉÖP”ÔôéÝpŒTµ?Ú×Ó,@—J‘âxOÞ{±¹]Q…LÞ¥LœòYÖä ¿Q>µ…ËÌp†ö_‹åJÀ§²?pÊÐô“'h-"X†Š*O¢4lÇ÷ÈÂv“ãHYuÌÆâ?çþµÏgÜNx^…—fNNûDÏ›X8Uá £`¼ëßcµëmÄÄ)W Š2ó>ËïÀü×À4N?®Ÿ‹yþŠy>+Á®8àyŒß[^Äé3ô8îKŒßëÿ¸oñ¬ÌtÍv÷ã¼Î˜@hÀûƒ÷ÃÔ,éAAQ'ãM~ŽÕjKËœ),˜’%¬›2SÈ[³cEi™ÕºV).zºjÎ}³ÖÓÍíTŸŸCT5N0m‹²Ò‚ìu êáJv«êÍ*8§ g?Ë;­E×~tQ¡m:ÅK“ÆýíÅùP¥ópCq8ÿ‰ÇÎ WÒÌq×·*(ᢠLAÄVá8Šc'—À Îî¥ «+©Ô‰™÷b(õ++ï_xÞ3xÞŸ‚$ä¹"©öÞ‚»T²cÚx¿\â|˧+(ðB‡ÖSQ”¶¢¼g±Ì6ÕvzÊÌ™FÕqôgŒsˆ°ô|‹eYÊó™ýé\§ í@Á›­¥#¨oŠ‚˜¡ âë †1;‰þnÔŽéÿ9áL§> Þ£ï¢%²¸‘,ú×ym?’–q :„ãøò$þÒ‘O ìëR'N¹„⸅žxîõ(¸ó0í](è£EQúÖí¸k5%33Iá{Üq¢­ñŒ]x… 0fCÀ'ž.ÞÉsð}ŸãÝLÁé7ˆ ,U1ÏƼ¶qÜqÇ' AõUqÞo§ƒ(&ƒqñRõ{ïìÙ³¥þØ1oÈ…‘†¨u•¬Ô3ŒR§w&LȯØÿz¤±xó¿·³X†Yqûc¼éß‹âôŒû@,Ü÷OšoÞš0á(zM£0~A[Cú0·Dÿ­”W• òQ̦ãÍ>z¿·¹Äà=÷¨è©}ÛôÓÃW8R¹ Ö«GBø€ž•šêiÉî”ÕŸs;Æa“ôÄ«.ÕƒGGàÐË`/ø·%mSÅñÿŒHÏØ‡¢= ·/š5yŠ3“@QŸ0L ß²Œ=Xo ›eäuVœO¿újxIAÉuÈŽø<ð¨å¨[£wY1¡? æOwÜq‡“úœOÚ^B>_ÌÊHÿ§vÿ³X–}œ+¯^­( }ûùÿ᳤ܸI’€ƒÄÒO=0ê'V,GNç,‘—b^‡c[˜o±Œ¤¥Z¦ô¨)Glv¿K¸ÉMpµ¨ø·WúŒzóMsMçÒ~,Ž(ÎÓyX¯ÁÝ&021o£C9~ÅÃQݱ¸Ý›âÓvQ‘]ボߦm=X,ýeLﬣ‹}áÅÎú~Z¢M?V7*[;†Íd«³Ä‰-‚eŒH]îçó:`͇@½nÔÞ‚0í=ÃôÆ«Š3ZT¥ÑUÓî=çÐÑuVàÍ­?Þø$›"Ü…qÞ¨/ð¶Uj:| =Ñ2ŒllýL ƒ7ßMŸSÕV¯ìªû°‰÷8¨ Ø@Öš]³&Û‚ÞÚoØ9 ã~EñeYÁ¦e!?Q}G€.×fe¦w•1ñ\Þ˜59}ì»éé§ n…í¥õ)ƒ0 oè×=j™žH,xw¿ ]¸ÏÝ››q P:DQì?ÁOÙJû¨O²¦ôQÌ"èº|Öšâ¤X^nUœ_ºIÑš…"H¯cÜTLÿ?5ÓýÔ¬«Šâ#ø°pIžœñ˜]íUÏG‘Dr=œš^é' ã$Q&‡ 5>XPZÿ¶Œß…¢½Og?z⋪0OËG`Yn¤ýª¢öÇx{fXÆŸQŒ$˜e‡×ý?IÕöá+a•¼lô’kô\©eaëÍF¼öß3mZ´{’¼Î˜@ó"àSO÷Íoº™KîöËÎcÐûÛ‡£cŸŸ"ÿ€C``| Þöj’þ¥S-{…Æ„¿‹Öi×­Ï‚ƒ§y5œ2»%CüÇñל²mè#ÏO]‡‡UQú°j³ÒÒhÔ¸Q–7§Ø'ÿ‡bùöÏqgý¼ùïE=êÒ¸¨Ôì­ÇQÖGQ¤ÎEñÚ™“Ò´fW:†ýÌ·á~=Z£–³&¥}‹çÿ0µ)N›êÊCO"v¡ð–½›‘ö¾Ïƒå˜þ9ŽÌÿÃs[™õkJà ç;TûÔ<Èì›ýðAÆ•?¶¬Ç6PJâSit=ÈÚ>ô³ÅÝ”N}u•D¢.+•‹[bü…Eö,\bÓ9&Àš#Ÿzº%¹ûúã{º“ ‚áŸRÕ«Ê·nÌÌÛGbàzç› ï­û¬Ð‹¡ ôàsÑ¡ªê¼ =²¿fM¿¶¦’¾eyâ$zÆo¢gÕòswÏêâ &X‡éØœ²RÓÎ(ÅIâ¥Ëô4h` ˜WÅÂ!=…ýœØ¢®¼Fm´z^´ÄG2=|ÍciÝ÷Ÿ¾^>âÚîtRss¥€#‰µ~]ì/¿SÜôvÆÄ½¡üµa—*Ó~8y”7-ãFXëÈ͘9¶TsͩʽèÕH‚nõ~Ä •úô±Eb7zÈäÝßE¯:‘˜h~|êéŽ./Áׄàí´Æ¦Ìšc_â§èUÑMƒú:tèdaznfAf ªü ŠF'¼áã@žS=Ϩ©S•„•‘vÄê´ö”Ue<ŠÌ±ð8óß§bžZ£&oô0ßF1 A_'#6¹:…X|8ºû{—Ó{Áxþ:‡så_ÒR,/¾/*r‰=L£JÒx*]OÖ¨ïû¡ŸFþ£òóÝZ?¢Œ™B±ã>|‡ìG|§yú§¨`ëŒnæÅ8º]B³Ð9לÛmõ÷›·¨Šò ô3¢ÑèÔG1+’ù'P¬"šü0F­4.Ë8÷DÑW#Œá?QZÞzâ Žˆ†ÍÎ!ߣèãEg2)‚c8Î0v2º¯º„ò³kÿ?krÚÿ0Í«±ù”‰/þJýöµŸÁG™5>õt ¾7ù¢ Îñ '$·Í~‡µóTµýÆÒ•šF(įðl·"ɶB˜ú‰{™Ppªªdªç¶bGI¾¬8Wâ«9‘¢AøÚèÑeîqÝד éc-Mü0ÚéP¶8'›PRñ½àHŠ—l¸l&z‰/㾇T‡c?ŽúEj¦kÜÓñÆ:Nñ1>P,­šM[)† }±Ü[±ŒŸ(ªm>,Å×È* ¾£×|pÚs¨=ì îÃI2vèieYž=†2¾[ÄãÑ 5-ëÇ$AšOûq{mÕÑå³&§}Žå‰Î÷]6UØ-«ö¿Q(‡‚$Üî>©†žVƒ–ÆÖ£ÐÖ}ªêøÂÃQà ÊŽ#3&XÜ\ ߦªqjÖ [ &ÃÝN ÁéŒÃ/Äã›q˜yÌèÑô¡ÅåãRŸÐÁl¼9±ö=îFß²Á6EƒØõ‚y‡öéÇëZ†ÂçKÆEN‡ú¾n2”^ª«Ì|œ P¸ö¹&™@(ðyó2Aó¼"-E킃©zOHQ¯Á¦Í°iô×´TÅ"©ÂÜSĵî‚Kç‘À¢¬5=’ðâ{ª8ª¶ù„G-S»áô‚ïãƒÊÖ$Ãø¹Í§ä\R&À˜@hhÑ­ŠNÓq*¿g§Î4\‹3E¾)˜ÌÇ +9T5mã„§©ºóžn1ÕÅ ¥}8Àénœ"ßáwb¹¬‚AºÝmšÇP**—… 0&ЬøEtÇ·í©¶á¤ÿâa†ŒfÝWõ>óó¾ÁW:¶U9q²8¥¦¸¡²_Œ’–à;²w†î8ÑFŸ,Ë„jG"‡Jy¹L€ 0æBÀ§¯ ÕqÚ,ãÿ9UùbŽa×U‹Ns2ãüAe–×Ð|MFÝ00áe9óÚÆWCž\—Òç ú"m‡b¨´ãš°!ËÈebL€ 4GMîé¾8Ót. îW HwÑG:Mqøâ,é&|§÷œßUþé^Ñ]¢ñ]ЛŸÛ‰y¹w¹çu&À˜`Á@ ÉEßýÄW[ÄáFÈ«\s2g‰›ñ]ч±w Žl. 3›+}k´û[»lèñ¾©Å9l'¨‹l×-à%`L€ 0†hrá—*¯Žh×ù§©Yâ#kv®Ø†»$ ÷¦¥ªCpâƒáø*ÑôÑ”åT-ŠdŸ¯ Ñô䟵aÿ+yÃUãó6`L€ 0@#Ðä¢KŠrö]¯õ5M7âwwï÷°ãß“ã{¼ºÇÝæòhÝaió1 €sô–ü:Ýó85¤¤oó’ 0&À˜@ ð‹èj¹OQ†f£Wb`æÌäl^~ ÒHý#÷ÔÏ;í=ãEîccb^Ç¡UÚ÷Hñ½Ýîs?ÎëL€ 0&À™€_F/W’‡&â$Í?ÒˆåÙ³‡J» çÞ…ÍÏãðµ¢Øï;¤ÿå2ÓýÓ]'×Lx¿º“IÛ8ýDÕÒïÁR~œö5·0bâÔ Åù¶tÇéÅ€hx’'ÒonW—— 0`!àO×δY¦³qbùû# ìyêçÝyüËí8aÆ-8Aýƒ8‹Õºàêç„E´zU+ßV»lX·m˜~,Ø–)éßã$+=µ›>6ŠóGä(˜ŒïW}k¢÷zšž'ç• Þ“3½wN Øà½ÒpJL€ „2¿{ºNÕ1G,o/…2üÒ°Jã  )vœ ¢ú×p{ÎÙZ¼vPâK8œjU 6C¿ðÇ} ÿ;ÿãÃ%¡\QÕ•MË£‡ ‚qÄL?|&.5}òÃ9ræ%hÛðêìkŠ}`CS”“ó`L 4øÕÓ¥W†czhJvUZŠ2lB* níAÄ·q$³6ÂE'ÉQ Œ©ýŒÐ<ŠÓQwÀٺʒ¥±øé¿¦È~HÓçZ9Ç@°¡²E¼Å˜¨™€_=]j:Æ>Ü僧\_ô«ÙÚŠ#}æ–®˜˜†½ºh»õÙMƒÚeõZtð´WêL,À"à×a~Å'¡qNQh‰__zŒ]€}µùhæ¼1Æç§[ôdffBY©:Ë~ ÁœãÈ܈ƒIË»“Ò¿¦Ûc“óµøÝÜûrœ™/à¨ð»±M µ¹Ml›–£¹ŽÌûð¼{±…álü¶k$¦¿?@‘9sRúB}ÒN.U‡ãÇí¯Æãaz;°ßý7ÁªF!JÕYhÛx¬m.𦠓qÚÊ9µåo;RôÚ=°ºÏ-b~à È>Àc3t;R,3#TçÑÉø)Èk0ÿnho¾âB‡ÉµÚžù\}ó©Í^ú¨=ÙŽ£ÓPà¯Åò& »?$ALŸ1y¶Îp`L€ ÔŸ€_=]2S­\“ËcöY”÷z»úŒV²`×W54ŒßŠM'LA¡œ4ïü=^X$c³:J ~ßûpÆÕ_ñg•P0égPÔMt[Z¡@]š#Oy…ûz‰·qïúpŠÇ£(˜%˜f¦(ˆc1®¤:á«GÓ§­ÿ9J”/TPÃþóoUA¼w}ƒ¿³§ä4 ‚óÿˆf Û„idÐÓÓꣶüÑú$˜ÞÏ;- àžƒ;ô£Þ|ÓŒb÷ .5_ÏÃÜwÑþuÚЀ|j³7eæL£ê8J¬ïÁÏP.BŸÁ‡•YQ–¥<ŸÙ_·•—L€ 0úð«§[éµ!§Ó9Z /Œu›§Å@žÝgð&¾LKGû× N~½àò}Ü8g›"ÅNïL˜@.…/ЋE¤ Ayk„£¸œ21ã|ìÓî÷îä´ÙÉ=`ÜN¨ç%Ïü‡År‡ÝýجÌô‹Ý·QlW9Tû_²à¸÷o#¡Gú£¨>53#ý­Š¸ ÜÏÁõÙèáNÂÉLö44ÿ*éÔ¸i;zr$ lOI0ôywòøÕD¬Õ†jâ׸«&^êÁ£#ðá§—Á(^ðoKZùC Î>"=cŸ§q•^k«1>À˜@~÷t명±Ã¿ãçý²pÀÕ‚©3ʼnîñ/\th9öiΧ}xsF‡Èùªûñà]» ®V |ÆÀï «ÑO[^¯w¹T!½ªàVwî¿3ÆnC|‡QdÛÒñ¬ÔTòį@ÁééÓ»WwN½öÕ3ÿšÒŠÛѦå5nM§y¾¿{Ë›æ…Mn‚«¥×Ý\é£yãžçÈg2&ÐÌ´§«½³{bîíªâ‹‹¨ðËCØøê &ÃÙî¸q(ó•ëµ¹§Ï¢#ŸºE ºUl6?pšÑ‚x?±6M§«a‡Á§{f•b¤L|ñ\äû1¹3ñQ¥ŒBAo©àä×zDú†¯*;¿°ý^ö·ïMìkýN?^ŸeMù×ç\Š#¨Blú­êa×÷ôÇ«Î^ô€» À†ã«]¹'ˆôð#: JÚãr—û1^gL€ ÔDÀu“­)‚?öÏžÝÓ4u¦4ßÙݦªÊý8BhÎÍ|åøù‡ªöôú:gî{CßÂñʺ¡]bõí ]–zÃîwþõèi¯Qa“ôªâØŒ‚۽篱a¬hpP•pÂ=Oú†oÖäôs±õfìŽÆ>à¥Ø¤ºläÔ©qîñj[¯.ÿÚ⣠Æ*Ç#P¬Uö5~óô|´4«³…?Ë¿NM¯ô…q’(“ÃjêçÀ˜¨€òt_Ý.¼ìxî#»Žÿõú9k$ƒa(ÍË  MHUcÂ#Z¿PZzô.ŒÐ½Ýµ¬8ׯñ„fzÀb™mÊu옎ýá¿de¤ß®cÐ&ÙÊðAsÝõ›ã&h^ˆý·×¢ð~£;‡ávcšñÌå4olPeFw 0ó½8誃›I Y­w>µ%Š£¥w¡ð–½›‘öAmñø`L >ÊÓµ/1áM·»Ád Á½ªj_{¿E ÷}´Nfˆ’ô¤¾Ó¹~`R}›—åŽ:v$as©;ÀIHO§õ.ÔZÀ¨i5øw¡+õqU²¦æÕúA8ˆ6Ä>2qêî'©ë îÛ´Ži¯Æ<¯Ó ª¬Ø®Ñ†äSCÒÚn´á',ó5¥et¬-cL€ Ô‡@­7Úú$àÍ8ãRSç£ØX)Ù)³¤q¤èˆ)Y/\…wâü)YBÁ1h̰ýÃöÐgAÞܵƒÚ|‹‚r ˆˆŸ |Ÿ(J*÷WJ¶Ùm´6¦È•3Ž £°Ÿvµ$ʪãNܾû-óÉ­¥`±¼–+羪 Âaêßv‡©8•8×õÿ¡÷;¶<Š¢ó±‹ýÅGÒ§ R¢ÄU‘V“ôšet~¼º¥†e 80)ç‹©ÏgNLêßN›p9¶P¼€ñ+5%ãW¨2eÙö Èeßaܱ¢bÈVù||ç©mVFÚk”~M64$Ÿêìtí‹2f ÅŽûü˜š>e€ál±;G€+>ŒX\qy… 0&P€òt«ÚúÊÃÛO™),ÅD“ð]Ñ/¢Ûwmƒó1wÁwB'Ëö²×«Æ7«6) 7nµo¯¼µÉUã4çmzG›J‡¢¸îG?ɪ6û×=¢ Ňz·"D¢®)çàƒÎ²ÒRåìwcßú+èu¾œl˜ðŠË ™>G/p¾¢*³Õ"Ga‰£t¸~¬¦%NWùˆÃWÔ‹PÈp–Á|sx¢`h¤2v%œ ÿ¶Œ9d¾’#”)Nõ[²óš…¶õÒcÕdCCòÑÓªn™5nÜ 1Lè‹Ͷ"ƒOÕ¶ƒú·Ñ†ÑÕÅç}L€ 0Úà}.0Ô£[‚µx%Þpߟâ̪è_ÔŒ9³1_Øðç„õ̪֯Øf"ŠÊ$Ú/‹’óë³î^;ý¥W/©¯¹o?ey­…ÍlfŒ¼&ϾôRdI‰œ`Ñ~yÎywÜq‡³º¸c¦M‹.9éˆ'3ļ^>RpÄ‘ÙÖdŒ,zÝòta]'‘÷}Ô‘“ðvFZ¶ûµ ŸW“ ÍGO¯ºåìÙ³¥6oïábé€îÝÔÄ£ºs›zöÁ/§<ÑïGKL€ ÀÝ™âh\ôøT…EàhUò@IDATU ø5¢ñØlÜ çj~¸ÒÜØ9ª›ùÄž¢ èIi³L,¸qöæ–Uãñ6e,º¡\»\¶`&°ÍËè­Jø«Ô?øÒ¬°ÎSfгñÉ› RüSÕïþÖ. z›K5¯¬uÉ¡øµƒNçêÎå}L€ 0&À|I  R¹Ô`ûBv–ý9KE|ùû¯³+6È3­¥ÚûžÔ‡×Wÿí?L¤ïüÿa3ók(ÚÏRšØgø ~aI(|Á¯3&À˜@pXOwìÃ¥¹¢h‚#kÛâ —íp²¤q U» )ÊË©©ë4'3z½ŸO›erMÒe눉%Æ(m’ ßX¥;Óý8¯3&À˜hjÛ§[Sg®Âù–g¢üöýN®zÎä'†o¸~÷éZ^NA|çkþ¨j<Þf¡F€ûtC­F¹<¡B `=ÝÚÓœÌSgIwâœÌ/ãë¹èãœÌ§‡1OîŠ?+ÇuDUÞÚpkRG×6¯0&À˜hBA%º ™“Ygøs»+÷â ;+¶c»ócÕb ªrëeá%`L€ 7€HåŽÕÓ9™) »d‰Ä{qö ŸqÓ€žñåë×Í Y•¦ÒqL€ 0&ÀšŠ@Pˆ®ûœÌc†ÙÐkmØäŽØû;ŽfÆ®—OšASâÜÌßõYœ·ÞÇ ©/Ù½ßÜ}ÝÇYsò~&€—›+кû¶ë¯0&м…èÖ4'sCªªOÄ•SÖ•®¸Ϲï~F|è“uƒ“ûô]˜ë•ÏèUØ¢‰ìÃc,}ñ“xסÜvQHÂoõD7ÄVŽüP‹AQóPj÷;l¶ï?z}*=àÑÓ"ýX„ƒ¿Š¹LÀ#A!º•¬ÊIœ9ÎÍî³ ê&¼åá÷aÕ³A~£á„”K¸3^e0JOálþ‰4UatD˜Ò"&B3¹¹Ñˆƒ+«Ã¡ž,U‹J­¢h^H˜qÈZf}{ÿº?ßY¾|=èÑä-ºWáØZ&À<&ÐlD—·øðžõƒŸÀIóß§müûÈ ƒ“~ì½0ï+Úö0ˆ}þ:Ñd|OU”ä]Úªöìçvk+D„™%ÓäÓ‚Ÿ€ö UjµÁ–]9°vëž„?wÌ8ó²¾%ÓõÑÏÞ~å;,"Mð¢‹oð—˜KÀ˜@šÖgá¡ð3vst2ŠâüzÀ]ôí,©)Y>á…§%ƒqQRëØ„gs€pñ9]·IqÔP%@×]t]Ðõ‘ÔºE›¨Ø_Ý7zÜÓXæ0üñGgÜß80P'ÐìDW«ÐˆÈxÛCëØ¹‹_ïýbëО¦T6Ý Å‡Ç[žÆÏÔ½Ü÷ìΘIÝ;$6 ŽÚÜÐõ1öÁA]/aá‘÷<ùœ»ðÒß" os»(¸¼ÍŽ@³ݾsöœA¼û]íå5®ö-+;öRj_|à٠׋¢4 o êð[¯LFnIn¿f•®º^躉ˆŠù×ÍÃRoDºÇÛ,ÿ›íÅÀo–ší¹öºÏ鵎«žX?¨Í-úv-Kñ†î‰ ‹˜Õ.!N½ðeìÔ‹UO€®›äÖ- Mr»×:t8#cQ±h¶“Õ“â½L ´4ë?ð¾ ¿‰Þî×z•bSó7ÞœØIß®fI+µéÙy”¢ªIw^{±Än5”xW躹ûúÿñšKø¿oLÅÂñG]Ü¿['=ŽÀ‚—@³]ª6Éþ¬ÚGë8[U §¬|µ÷ÁNÔÜW] ^sXبsºµS¹·:D¼¯¾èú¡ë(¶eüCxN$þØÛ­/<ŽÇ‚”@³Ý æí+TáN½…·wþÑÒÕÔ§æå1ê"lŠNÀׂ¸Y¹H¼«aʯ#¡Õå×¹Ϥ‡=öv†c3 "ÐìE—j‹¦‰D?÷I½æ°ÉïÁuGêÛKMt#¢£¯¡‰/ð=Ü*‡y“ 4œ]Gt=µnÛ®žMMÌü QÃ1òL h°èVTUßEGÞÅ»ßû®šÔ×7 js‰k»|€‹¢Ø‘fšâ÷pÝÈðªÇè:Š7«F“™žâÜ=]þÛô˜*ŸÈ—ÿa»ÕM«Vábÿ®ölB6á}_nÒ&¡" ±2àœÊI8µc³áfwЄI Û÷‚2[ÅY 8}ñê? çÈñœüQ[ÄF ’ÑÐKB}ºÔ¼Ì£˜ƒ¿Z¹L ZÍF<ª-}•?ØgÍâmèñæÓ!ìßM¶:a¶jéG7A­yY„(œK9¤ûsñ>Yü <÷úðäKÃäYóaöýUhÕ¼ét*ðÖçßß»s´H²Ó‰S!¬ù„Š#”ïÂá`3]ºž$QŠ@ ºàê#˜Cú:«ó‚àL  °èV©ÔÞsóöK*Ü…«»xW¬[÷×ëXI8…UÈß7þ ¿þ¹† ¹ ҇ߗ÷>Ú¶‰«BªæMIáÕÑwÃ…=:k‘¶îÎ…¹?­«ù>‚Tº®è᎗~ü·‰80P#@äªè½øð8*MåEí ­¼¡õö+¾=:½±Ý}¹G¡u‹hÀ7hÅ×7kî2h· ¸[T˜„pB|,gw‚›ûõ†03/ Ý€8éºÒ—ÖõŽëãÀ˜@¨à§éj²ïâCÓ°™ùsýp¸(¼úáe-/Õ·CyyÞí5QüÏ×+ ¿°ÈUTØ?vfkÛûr󡤌¾ sì¬6lÝ“‹1M®@ÂiÃý-c#¡[û6Ð"&DϹÿ…g5?¿ýù(Ä6¸ç†ÿƒË.8’ZÅjçÑ?ý¾èKM7]y¬Ú°ó<à:â+ô÷¨‹--90&bXtk©ÐÄpãCxÔÛE gÇÞ‹·Ò`—ÝÚ'À“ÿ¼v8 Ï¿û5ÌýqæÙžÕ9ŽÁ‰âRض7zàv·y`Ñ­g=_½ôèœãõM=úáEŸÀÑ%³õÍZà VÄ·æh#Œ/9¯;ŽH>_+#y³$ž=º”{µ±QÚèc‡ìD1¥ùNçãÀ¬äÖñ0ÓûïüU@žîƒƒ¯Ð¼å½;WTuÌmÀÖé)ð&À˜@hé§é”ôÉË©š²2&ö£e#½ÊóÉà³þl_|°ü¥UQ„®Ï½±}¯hDÒ{*Í(n¦ù¼JÑË 3Ñ\îisz½!nžáÕ—ÀíÜöÉÓiþïCø£ÉY¨í½ úûâ¸ZÿàÅk¿þ™rL&Àê$ÀžnˆNE(--…¥í®Ýs2®CùNE½¯‡ÒÝŠBkÞ\®»àÒ¾¨s³\bÀ 0æC€E·um ê–‹ïsBùDŠÍ »^| ìGµÎ L£3&À˜@s"À¢ëAm;L8áCÚÛ E—Oè æÃ®Ì'ÀYrj" ’åS˜`L Ä °èzXÁæ¤öÐuìk Êû<­9{aÏKÏ€*;Ž MÐaäD×yºÀ(è+Š Ùyù°mo.œ,%¥`³c³ô) vWï• }7› ñ±‘УK[è˜Ô[‹š»‹R½ÓõbDÁþÜc°uOœ(ÂâR°Û§Šî)ƒ (¿QrRL€ ]?VBÛžÅaƒ£KçhVûi’íFŒG‘UÀéTÀfsÀò Ã*ü,µƒ€R!Û ÜQ&§waL°ß ¥3,^½b"LЯoè×çl0› .nJT$¶$¬?­ý–¯Ý 'JmåewbÙíÞ+;•)Ëß”¬9/&ÀšŽ‹nÓ±®6§öÃÇioþ²ÚñÃß}Y†ÄŸÓ<»/¿ÿÇ틲¡OÁvèP´Ì(º¾6Ýìèΰ+þLX°Ò+×oƒ;¯û?8§[{$Q__ä[5Mzàøs×Aøì›Ÿáx‰M+{o—l”òWåÁÛL€ „ݨˎ#ŸÇÌN8¶b1Èèáå~?~. ‡_œ­!Þšƒ³—CRI®Ï-%1ï~üoí—™ ?·ïY_¯€Á—ŸW_Ô ɧ^/y·$¸?ü¾æ-ÛФe'¸þ.¿Ï+˜3`LÀïXtý^åݲíQxíØo{hùbø³M/Ø,·†.…» _ö`PMn%‰üÍ;fÃòÐë-·ñê {€Ñè›æf—àþ¶¾FÁõgÙ vS—¿É+˜3dLÀ/ø•!¿`¯œ)ywÙ mxŽ]t3li¹&:ö}ëÁÕ­#±'H­ú6ï̇CÖ¼Q=Ž·–Ä`ËÎ0oy¹àú»ìT®¦,¿·8r:L€ 6]?׉Œ‚k³ÛáDQ ¬1u…x[¾æáúÙ4WöämÇ•ÀW߯…’R«f/Ùí­@iY­ø|É­I™ò ¤àëòRYÙ&À|K€E×·|kMšTi„²·¬Ì «7î„"ŸK¬ð«‡[Õhòøþq`œ(³Ãòõkö’ÝdcƒÎà§uA!šºû¯ýÑœ^[9|YþÚòåcL€ „]?Öi¹—+k^n ¾w»~{¶6R·)M5´Ød ^³y—f¯Œ#¬½áíê V¡˜SúXvbå«ò7´8>`ÁM€E×Oõ§{xv‡›Vm°7ç(”ÚÐ _ Ô@¶£'¾m%»ëíê ¨ì'Ñ‹ä²Sx»üZÏl`¾#À¢ë;¶µ¦\.8N˜äК–w£ðÐÄôn ²lü{ß!Ín'¾æDåð4è h–­@/;•ÑÛå÷”ŸÇ˜@ð`ÑõSÝQ³*‰ͺdµÙàDq„ËVŸM|ábÒ{¬dãqðEv“ýibÖäŸ(ÆY¶»ìÄÏÛå÷FpL€ ]?ÕÍ¥,Wˆ®Íf‡üE8ŠýdMý³%Oâf]²ŸÊáiМ(ÁŽ (;•Ó›å÷”ŸÇ˜@ð`ÑõCÝQ³ªÞ´J’hô²Ã¡xm.e_‰æ{v`ß3Ù­7/{ÒÄìÎÀŽnðÖ<Ò¾,;¥í­òûÚNNŸ 0À$À¢ë§zÑ›VI¸è=]Ta?YÒðlõo5/{"Ú ·Ú{gx«üÞ³ˆSbL X°èú¡¦t/t–F+(¼Á#¹4%dùûÅd¿^–†bÔÏÓž5‚èƒÊéò7”ÇgL 4°èú©]¢ƒ·pòzi;h‰-ýC›c·~~•¼¼Š¼Tþ ©o6” 0¯`Ñõʆ'ä`\,&‰¤n{ÃK]ùŒÆˆv唚nË›åo:«9'&À‹n ÔBØ Œ šÃš '΂ 0&Àj"À¢[î÷†wgˆi½>Z ]Ï.·T ùžQÐ{Îzˆ:«×)ëq»‡žƒ^¯Âø« óèA0šÁТ%ôúd5„µïz*®‡kž”Ç“sÜÍ‹¿üFèùæ<Àü–ﮩüxÔ× ÎÌ|Λõ+ —ße¯0&Ь°èú©ºItNýnDâ­Ã¡hÛ&(ݽ ¤¨è6á-ˆ»dÀi µè{%´¹n(üðUØýò³sîEÐæú;@.̇c?͇¶ÿ|ü´sê³ã”íž÷ÈêiÔ'?÷8äµ'ß=Í}°C¼ÖòGžqœ5íS0ÄÆ»'å¯do0&Ð,ðG샰š¥Èhh}Ýí°+ã1Íz)"œe%°{ú3Ðã•/*•(ö¢þP¼c ]2[Û_°z)Ä]z ^ø1žÿœûî7Ö¡X³wU:¯¶ ûÑ\ØÿÎ<8n‚(“ÌF$ô4E³:¤¤©U¢ëtÕn…½o¦ƒ\tÒµVœøÐá ;ôÑJûë³Ùu ¢·ž¿ò[-zmåïØ Ž,üD‹—0ä¾JÉ{ZþJ‰ð`L ØÓm¬@‰Ö¶·Wº·üãö#9°÷Õ± Ú¬§™hjÙl‡s\û퇂©u’¶íÈ? ΢Ññ ×ñ`X ëØJ÷ïD/ßoÆP[ù}?W{À §?_kùƒ¡ŽØF&Àª'pú¨úx¼7€˜Ûƒ|¢œ¥uOI^ ’wÀe½ÓV¦5Çê;¬yÙ`Nj§oÖkij ïü´Œƒ˜è(I’ª=W0…A—g_>íM¬Aß>YDe@m@Kê6´ÛÁ“ò{#_Nƒ 0æI€=Ý ¬ws«$°£—ZŸ Ÿ),ÂU2G€÷éÒ1azÁL-Á‘È+&cù½RpN„ 0¿`Ñõ öÆej/8¦ø6õJÄ~4̉§§¥•ûÙ;7{&Žx6€â6Â9ñ–a@^oYvp‰nÁª%xÛphÕo0¾kŒdT„šÊO‡}ý¾öÓãÒ2XËï^^gL ¸pórpÕ—ËÚC_¾æ6ÉZ“±kg-+ªS®$¸4YDL¯K ç“7k9+0QY~ô´¼ú&|?Ø“Ìå÷¤¼|`A€=ÝÀ¨‡[A¯ m}òÖŸ§Ÿ@¯m}E+Hɵ+€~ž†`/¿§åæó˜ð/ötýÄ_@íÔ¯| a»dò“5õÏ–l4J¢›ížyš”£^~£A‚`(;ÙìÍòSz˜h^XtýPß$6Uƒ…¬ÔUuwÀm“a¦ÊaTWžº w?'2Ìe§2y«üuñáãL€ „&Ý©×p£e†0°ÌbÑéfmdc$ηìÍQžn —Êë«ò{“%§Å˜@``ÑõcýèÍ«´Œ4âÇáÈŽîìG‹jÏšl#ã"]MõŸQ÷Q*{Çĸ€/;•Ä寛Ç`L ”°èú©6]‚‹"&â7acÂ`UئŸ,ª;[²ÍŒ-Ë-cÂÑêS}ÒuŸY} A›83~©(ËN%ðvù«§Â{™e,º~¨]]l¨kW¾\?@ËÖ‘8Ýò"“ý`UíY’Md[‡–‘.{É~½,µŸ}úQý<ÁYí[lÙÉzo—ÿt"¼‡ 0æ@€E×OµLÞ-}™‡~½kÀ‰+’£ÑÛXݾÈ¢ÑO–ž-Ùò3ÚdFO¼K›Í^Ýv*‡§ÁAŽ­!Ü `>ýªìT6_•ßSn|`ÁKÀó;fð–Ùï–ë^^¹àÀd2á(%G p<¬%,ï0Àïvê-hS·ÖáFv𴇲_/‹·¾KýLjæ&¯;1B…öqáš}d'ÙKv“—êæewg$·‡Ã{ ø«ì„²)ʯW™ÕîPeY.Ó·Ý–®ëÎm¯2&ÄXtXyŠÃy¤ðDI£E—šWI´ŒF£&f‘‘`³ÚÀf³¿ ÔÚYF°B>ÄÁÂî·Aû¢lèV°:í³lk Õõ‹Nï¡Òk14J—MÁ í#dôp# 22¢ÐF²“Èn½y¹~©Ÿ«&Ýcp$w!ì+ˆo²²“uM]~HáÉÕa³×·yɘ@è`ÑmXÝÒÍ1·¨Ì&”¢@F`?¤§¡\pD0¡x…a:Q‘‘(¸v°£—'ËNôtLº ÂE;wˆÝVB[ÃVˆ‹Àätxš}¥óìøÉ¿RC4”Ã0ulöF±7Ø¡u¸ˆ"¦Ù‘Q‘Ú:ÙKvÓˆk*‡§¡6ÉÈ ÒP m¾+;Ùî¯òSÞtÑõTVZr 7éaNÿÑaL€ „ÝúW¨v3<|0{uLË–OnÙ•ŸÓ¥þgW“šfiÔ2õç’IͪN§.¸z´ vÄ;mP&£ +È8 c± '”P=<ÝœrÍTQhb;DPÔ‚Ö|L-= ÄÆFCL þHxÑN²—ìnLÓ²ž] HØÛ”Y¡Èê€b|ư‹(1ÅCqEª¢‚£à¨ßÕ•"£AŠˆÖ“¯séïòÓu¤bEع}KOZºèÒ’`!F€E·~êº._4wK·ózå¯Ýº'E·QªçòôPÄUÕZ(#l~6`#3~×áÁ$Ë"C^0Þ¨ëgy=b‘”Ÿ.€F£ûmqºGlR&—·El¬¶NBL£—ëåêfÕ—Éd…d€}ŸZùuÇýJwý¥%'…GBÂM÷ãwƒÖàÏòãu„Ï Î“[Ö®Ù……pVü\כΉ—L€ „Ýú×#y!ÚMñX^îg Âã;³A÷‰õO¡š˜º¸šU¨‘åBJÞõ—R.h.COOëëEÑq¢GG‚£ øRhŒø’ØP QÄd‡$R3zÞ˜g¹—‹}¸Ø¤L®Ö¼ìòr7€JËÔí?O”ذo›ö~/%×êŠë êü]~º~þÜuPÈÞµs)ÚŒíÚO^ºÞ80&bXtëW¡ºçA7Dyåü¹ŸÝšúØÝŸ-ù5nܰÁ¢ ?VИ@¢CbGA÷ºh ’ö*öŸZQt­4ÀªÂÓs:It½ãíêù‘çJMÆå^nùhjjJ¦æåˆˆpíGÍÊtœìõvh(‡Õ 'û¢°)œBd÷ó M¯ l–¿Êow8¯E‘å“?³à;4Ü^ñ#ñ¥ëL¿æp•`¡B€E·þ5IžÝíGжoZ?]è}áÔ®V‡ßze£›™uÑÑD=ÐrÓáášàjƒ¬ìöòAVý¾º—ëIKs…“ëyšŠ’fÆ¢¦c{z-ˆ¼]4Eb[>!F¹‡K6z;èâG它Ü9ïAŒí¨&ôÐ#¢ Ãm÷yЗ{ê!§©ËO×MîÑBØðóòJK‹¨‹š†¤ÓÄ—®3öt&jBZtÑU( KòR¥ÑM<º)ZW}»`ut\Üq}8ÌõþÁ— ñxIhôWpH€õ׉HÉõã`*êϤVäéªôÕV^´£ÁòÔ~8b™<]Ê“¼]zo–Ą֨y¿å#•}ááº]_ÇV~Æ¿CK³¨¹ƒÉw‡˜. Ôæò“‡K‚»nÛ^a÷_›¿Þ¸zÅFd@ïèÒÏŠ?º¾è:kœèª&ËÁ&Àˆ@H‹®  4£D/ñ¦æ>ºÒ(ºA–|ó¿?»þ®ûˆáýyùÇÕ»®û?Ñ}¼$ÚlM$€ØÌ¬‰-6'— ®…ïȸM¡±¢Ki˜’óJ¢«ýp›Ä·|»|€ÙÔT¡Üžêã ©’…B,~•ˆÄ_~#$_=È#Óô25Uù©—š”ÉÃݽuóüŸæÍY‚†—à<]ZÒuE×—Þ¼Œ«zØ\çñÙ|"`>!Ò¢‹Äö㬠#§N›1~|c'ÐEWót1mºQš—|þñö¿&O¹èÒ‘¯|¼¤Å9ÝÚ©öì"œÛ­­Çïñ’à‘P“/y4pJïÇu÷p½!º”Ÿþ£<õUú>Ÿ\yu$ZÇI«¬É€/WŠGïttO}Ÿ<û8åAA/'-½]~z—^ ¢QÊ4hJ‘EëV.ûì5«è!º†¨5†–ôs÷tézó(Ðõî,‘@ö{”ŸÄ˜€Ï„´èâhÜ¥²"g*¥Ê@$ø‰(’sEýmÔ÷Fž Ýí¥µË¾_ý×ï¿ý}é ãë×àÍ5oàjt¸Ym)„+Fû`䯄ª[uÛ“´uáÑÏ­º­ï÷÷RTd8oÍ[pŒ8ÚZƉ<Öœ1~üú—F™Vµ¼U·=Mœ¦ ¥™Ëhâ ¬'Á锋öýý×Ê5K¿YVVV¢ í LŸ~'ñG×]WîÏ¥ë[CD^ÿ˜&Àˆ@Óµú©Ð#Ò'@?îϬŒô¼d Ý%± Ã_þbð×ô® õ£EžÕ«ÏºŸyÎÿ³wðq×ûíõê&[î ܰq£…:ØHL !løCB ;Q° $JÅ&&`lŠ©¦Øc¹ãÞ›,É*V¹¶ûÿÞžæt’U¯éîôF¿ÕîíÎÎÎ|S¾yoÞÌ$§¤uJHNÊ´Ùì‰ÐÇ=ÖH{ØÜ„_ué_¹‡ñæE¯yOß¿)µWeØ>dÀnÛᨪ*­:r¤xç¦ 6­Y±A²ê˜¥Y&X&^&ÜLºJÒe?AçNš2ýcŒö{qúÔžKœ Dq-éš8ÚÓh€›üàôÓgΘòM°÷—v‘ú«žV.[…c¾eJÂ83QóÁN½ãý%ÿ›Eà…“;\Ö¿ƒ­Ÿòøé>Ç{þð/Õï(<+Õ0—>”-K²økJvÐàÀ J2&jíŽYÓ¦<p`ò¢ „vCºŒ¢—xéqº?bïïC4ÆËA3ŽŠ|™`ýÉV.?g×®0÷&¹uÿçœÕéÊ©–ÇÙ—ßt´òÑõUWÍÝ^Î㟱à”ÄÊdÊ׊xùªßêyÀi2Çp‰žAC5 Ý%„0”ò¢ Ú@Õ|} z/¨™çãxÝ’bù0óx9ÃOEÀL|¨{ kuÆ#qõXpA—kÒmôœ"\0Öò÷÷x.™±ªˆ-}cÅ)Òå³:˜`ա‡ëd\µò<†‹€nœ5}ê§(/ ‚@Äh——÷rÒ^×Þ[±pÆmh{ ÑºŠÚ~C3 ¸„Ìñ~¯ÊAý×.ñVéoî‘—“œðïž³÷ÀðHœ ‚€ ^âštW]Þ½‡Ëéž UòI F$Ø…*þž™‘öÐÀ×·”©ûrA@Â@Ü’îò‹sÎÑ5z„ÛÉÄù VÛí#ælô»'—‚€ ‚@DˆKÒÍ—s?,“§«ù®^éV»k̼ýªòA@A âŽtA¸@º½O¥«.X­¶‰²BD΂€ m…@\‘îÑ„«}e·Ó•#ÞÛ ­–ï ‚€  se#õ#–ÏùçòßÍSíÑ=ç#˜Þ†fäBôO2Hy]A 0ˆÊ5C+@TwZ-ÖOžŸvÿ²pG;&ÔËËÆçœ¤ë†iŒ«õÌQs÷}(8yy/'íuí½•4ã6HÏ=°‘èý Ý„$Ðpå=A@ØA¤›ÒÍä˜[Áj´\ðtw{÷äå]_Ž”Äéb÷ /`DõSùê˜ü:P0&M™vÞ}÷‚Ä<Çë–ˇÏßÿá@Ô÷A@b›y$[¯Ô/Ï\ƒãðÌ.¤æÆYÓ§~êTE=éæ_Òõ,ãÉ Gd]v‹vìˆyû·Ĥ©Óo7tz\ӌѻùýÌS––ù¾¼#‚€ ÝL~púé:Ñ3ˆåPÍBwÍš6å©PÆØÊÀÂwº/\ÍòRp„k< â~—l]NÂõ¡*‚€ 507höÎ'3W`3'YX %8Q-é.×õ"2ô9Ái{¢mÀˆw÷îi-¬RÆßęӼ*e¨òÅ ‚€  #ÀëBLž2ã-Åå Œ‹B¥jŽrI×_Ê¥!\6š¤/²J™ì¯Âm¸€É]A@ZL®gàÎZ/ÖpI­‡¯¢–t—_œs,‹GrºøÊD‹ñH id+eÓh c¸³ò&W†¼#‚€ Ðþ`ÎIþŽ9Äœñ¢–t1}«JœuÜÜûÕïV1-¤=_Æp[…šxA@æ/8ÄœbD¢’tW\Úµ8žÓ‡qXâٟ $­¼ð…w®öz ïË;‚€ ‚·×™KLN ލ$]Ý­ßlNTFâ åÎ=wÏ–@ÒÉ+MñÂ<7÷åA@A€9„¹¤fõ ‰:ÒÝ=±G²nh7Ô¦ÊòÚëV_õ敦dá‹Vã&/‚€ Ô `r¸?{ JÔ­½|°Òu5äÛœ0¨–·Žž·ïc^†*Çk)ã=^WSœ ‚€ 0¼L0^fN ÊEézÈø/E=gšmûn´îTÍ›ÈZÊ­ƒM| ‚€ ÔC|Â\œ”‹*Ò]>®ûá:žSÄÓ„¬¶ä•:yYh;žx"¹¢¸"7#3©ðñ»ï®hÆ»<Žn|pú‹fÌš9}êK¡ŒÎ­yÉÕIÓŸË»§0”áJX‚€?QEººá†jÙë`HõîÈ9;JÔo9 ¡D`ò§OÀo÷)ª8™Ã--qx°rÙ:Ø,M°Û§þ#ïÞ¸–ÈË3,yyfâ…ÆMš2ý3¬ØsŽÕj¹à…‡ü¤~¨·Í˜‘SY©Z¬Ú%3š2¯þó ~kÆñYºFÍË·ç=™Uá®|62W;\ÎN|å Ð¢YîaÚƒ¯Ôx““ 2¢ÆŠ—ÜBª®ò¥LÓÞô]Ë… B&Mñ î»XÞÍmÑè7V»v"Ù+ Mû ŸÉìDñ%éLž2í·{Ý3f…B_PºÇxá®ÇKõ݈±‹ WÅ¿4Ã8—7@éÈ«Fg# ßytýåÉS¾(Æ’#Ñ¢FÒÍŸÐýPy-Ã4­HËíŽP´<1P†b*ŠpǺþoìSÝ­Þí'ýý€„¼ëMÌ”˜JSs‘Eçâø9Øœ¿Ö?×Þ‡qÉ…å¥Õ3ðnH…o}\{#Õžú›Ên)3'OvÕ„°õž¿üeéá2×ù:é¿Ä½ YÞF jH—ôZղŠwFÏZ¦*AÃ1—»‚@èš‚Í3öt·s¿á6ÒS>CÓõ© ®áX°ÅuL¾î{nú½ëý_‚Jò{¨î34K/]7n¿cá¥fÑþÆêU–®±yÇ$¼3Ç>-} Îßû·»Ùx¬~~ÔÄÿi¼x*|ÊXd·%Þæv;œõ¿h:žÊ»ã¨!¬¿Þ{o9°Zo$×ÿŽü‚E *ÔËÆÄ‰ÐêhUb4«å u-gA Täå½bð°xùG5Üõ¿sÓƒÓ¯Ñ Ï—h죢܆"°–å&gþä©›cÁ~ï õ=®úïAF/`Îûð,ü6Ô»OAýgË5Ír ˆf~? õåå~ïóåPáù›®âúe²ÒõøÞJø}Û‹±4i:+yc¸n¡Žêž:ëuægüÛ®iN‹E{if•ÑJ¨Ð§ó²[ÎÏ'Íœi7\áò²Ð ø?`Õ·®/˜ôÇg±Ÿ¦ŒŽR´^a¯']ÿ'ãÛ”~Žï-&®åŽâö+àô†–þ´×5ãÿwy¬Õ¨ÒóÑ 8 —<Ò,·"ÑûÜ.çbtF¤Zl:jCò^™á ƯÏë?“ß‚@°D…¤»¼rá9]j³gäûߢR›6y_¨ƒÀ>Ú4å,Ñ¢Y7ÕyÐÀ§,+q²Û´ç—dÝå8£žß ~¢»€ÙÚJ?Â5ÃC|çãb4:‰Í|Àl?žÏ›ò=¬‰ŸEúœœ÷ðÆß1~ŽtáG¸¦×¦?)[Ûj^ÛhÆ8äÓ‚™Ó}„kúM°Z^«~Òá ò¦¼Ã/[,4ƒóÏ÷@.!Ðæ’®‘—gY¶ôù‹Ñˆ™I²@¢´I0‚@4C7§AJͭ󠡆џo[í‰[ê?FÇp“ÁaÛ=϶ù=ßëwm^¢T—A"4¿ëÿ rZ~gøß3¯5mWý{)© ›KKª¹†ô®ÿ,˜ß ò~ Øäyª”Ÿƒß?-žâ î•~?¯¾K»-ë·»x‚áö@Ílœz˜ö=S[îr­/Ù«Çf ÆS9¤UûŸú¡Î I»Eu§S‡*7å=ÒGwy°› }~öqÇ<4S}T΂@hsÒÍÏa *Æ¡LWÕò2Q-‡0‡%¨Zl¶märcHUóoÜkŸ7p¥%º6è³.¨{1̨ՑA(U Á·ÜÜ?ê6Èø(í“ËéLcxÖ䘩˜aØ ´‘P‡§"Ð¥0œz½Ž|ˆ¿æNÒZlñÌþœqyôö¹§ßbI´ý¶Ou‚5À®!gî×Á3‰ ÃQßëwÜQ=iÊ Ÿ×ºP¤ƒÆpº]ŸcââÆ¤.™W]qÅu¾Qû5¹‚C ÍI×Bú…¾ª©i‹j9¸ •·Gà§Cúïþ|õ¦ÝhÌo€±Ìã<Ù˜o¬Ë˜+õJ­ü®ãÏmÞ=Z¶Ö¹‚0„êV?˜ªJãt¾6ÞÉgYÍ*²©Cúü Ö\¤Ï¼lî Ķ Œª¦?øJs~[òüŇüãÇoàë{ÆgG½£Ë ^nXZ׌^lÉì÷§µ»ßoóòæ??ΆbV³ Ró0ØtðBUUÎ/1î½[³uÿÌï'Eöõã!¿@8ªWh@¾‡‚~‘z ÀÇêZ΂@¨`éÃdu$í9øPÍ‚, ~&©sêj{Õ Þ_åÁЉÎáîn4 Eª×£ÞoâTÑçÖ·†ày&Æ7u‹ÝjNÉa©jáRH’gúÅ«NáÞyþ÷øulÂ`Uq‡{l™}î-No˜ëønÙÄëí O¹õ'AŒuÙßЖà›çMÊ{Ü\ùI…8iê£Ç¡Ÿ0–Ì_ª{ƒÓz^ý…7tóŸŸš‹`ÒÁq©¬Ò¿01²u¾xVÞäÊúáËoA ”´©¤›?>·n¨—Mç¶Ú“Žî‡2µV»G «õW÷ê3.Å4œû&O1óZ_÷e­EÓ³`± C+KÊÌéþ…nœ:ãzø{uÒƒÓÂ’u¶‡tÍu¦ý So®mÉ´£O‚ðÛ7M}äÏ6«e¿Óí¯“qˆåçòî÷'ùÏALa*Ñí«uŽáÑ2±êÔ½ð—]ÿ› ì÷¡é}VÂãô4Ë·©Õ Ö'óî,¦4û íˆëZL>þ–Ã÷ÙVäè Ny[gMŸšW?¬æ~?óÀ±6ò—/@IDAT¶_õúEŒj\fVâß`>ÞpWͦ·Ûì [tÝ5B÷¸ŸC'bC7ëOþ©üÚìÖ¿¸]ž_—•:Þý¿¼÷ZÈvÀåöœb:–l¬§Æ05ënŽÎ~LózZ÷ºó©U̳M7¾}nÆ”ÝunÊA ÚTÒÕ4÷èášq@ðXÖZ"'åÕ!À 0`áˆKa u#$Þn ÐYX˜å¬R5óNÿ‚3 ¨8°§=ˆyµÆÍØ®rÚV·áÜõïD²j?Ÿ9íAŒY†Þidy˜ÀfÙßb-ཟ‚Äøï\[ý¿fI¦Ûðl!Ï÷õ¸ÜÛuÝý-äʃ²^ïï¯mÖ„ÿB¢}sˆß6Ê]%®Êøþ¬ûî+µ$ilS±döÝplB}üVÄwòó@ð} qþ¤þû¼¡DrŠ6÷·s|\.Ç>,·8‡Ç•í©–SÙ2\½Ã «År6âÓÏåÒWx±ðL74ëÕH÷*åϦ£¢ÌÙ™%l”ƒLA³P^¯¸¬MO›ò‡\ -A ¶Úß!ö“?.çu¨øÌ90J¹̼ý†ò_úŠÃCýL>‹ê#À !X÷÷Ì¡®yy×Cܰ»åá‡;ꚦ››Y7ì%è»(¯åäo,a²ª¸íÕ•NÚãOFõ?«SYÜÅ;٦쬿¢S}¿XÞ0½¢ÌÕáÙSvÖööÛo[?_½±%[*Ï8ð@¸ ‰8}]3zž1â˜=Í}‹UÀ©dÑMé¼~ÄëýŽt:ê}^~Æ1¡â“6#]ž*”Ÿÿü¨¡ÌUu¬šýøQì­Óƒ 6ÿBR°ñ÷– À¤‹±â'fN›ò§–ø?‚€ 9BÅ'm¦^^¾ìùáBµ\jÂ\VÈ—A@–!Ðf¤ëoµ ƒ±ZnY~‰/A@F Í¬—ar¡Â ȧ 9·[ ²^c3¬›Û-’pA  Ð&¤»ùš¥¥e£`9ÈΑžúy;ÀZ’(4‰À‹Ó˜Û¤y(1@›nIyÙ‰˜±¯¦ ­øú^‡Vœ ‚€ ״ɘ®E§Sª˜èþº–³ ‚€ Ï´ éB«|²{ é*0ä,‚€ ×Dœty~.TË')TmVm±º–³ ‚€ ÏDœtW/}~$ÝL+sì9§pG<,iA@']·V«ZÆÞ•¢ZV9!gA@¸G ⤋µ–k¨ ÕrÜ1I  ‚€B ò¤Kµ–Ë‘tU>ÈYA  Ñyºë/ëÞñˆÓu ㊩BŽô¾éˉÄÌ7O}d4¶f; ë-Ìr1vw‰l&A·/Ç>¥X_{—Õj›ÿü´û—5óŠ(5c½ï:6/4oãHOŒÜÇrϯÇYA¸±™’Åšñ¹÷ºqÖ1ƒûjV«ý¯×Ý3åN|ÚŽƒ5ÒYR¦"Tå3q@m›Ò\¦ýí†þ|ÕíIÄH—W¢Â®B¦ç¶‘”ˤ«Aq>K¸ ã†ËÏÐìíY“ÜòúË81^Œ›ÝžøðÕ·þ·xdâ岨ž‰WÊTË‹‘ø|ø·)šfùë¯ïzà<ŒÚö$b¤»|ù?û1Õ2RšFcfo+õ¡c“&å%Ûíö—zädѯƟڞ‰"àœcÜr;gQFVÖóÆÈ+”)â 8ÌX~QÊT,çžÄ=à6¥{—l#))åÅsΙ˜†8EŒßZ“þÈEJwa<×ë !®S×1x¶x:h¿óèz.Æp­"á–ƒŒÛÕœdÑ ê:ü§gÜ‚Pq°AäÊd`QÇ[R¦Âª„Ù®à6åªóO´ê†Ñ­ûˆc~‡ÄGe{±DëÏ…h«ªe–j­6»õöaz2†\füÇÔôô›R2ŽÆG{ÒH™B†‹B€jS““oExQÙžDŒtÉ¢û$]ÒŒX•t-¿üý½cuÝèŠiAí‰BQ £ÇÎç\vå‰ðÐ¥])S – ¹)†·)Xn8çÒ_O>!D´1Òõ—t-š%%]S"±'&œÏ _`n`%BÞªƒãÈxvè’s6ð˜TöNëD:t?¤L…K I0PmJZV湸uíIÄHFTƒ|e"‰bQÒ5H¤¡¯4ÅsÅÄ㘞œhXl¶^UÌíi ‘”©à‹„ ÔAÀlSÐFkko<ˆºö$"¤»fBnO¬â]XÓŠFÍ.–Y™©šÕnë‚ü%Ýö€±”©à‹„ …·ÑšUë†JÒšö$"qF_?T6ù]ÇÒ%ce³–޵”YBWƒ€Ãé ÆÓªYØÄŸÕ\I¢n&¨6þ²”©Æ±iö —» ;ö5믭=lÜYHUgƒÑX·m/}³|cƒÏäfàp›b!³M‰ºö„·°;C×¹Ça:Œ½ê:†ÎL²¦*ëGL¸>û?;¤]zÖ耓~ ¸œ\n7a>ZÀa„êÅïVm¦y߬¤ÃeÔ)+Î3˜Î9Ñg¤ÞªÏ`áÆUUeÁÌ÷¢~-ÕV%´Ö3§k0eª6¸ø½j¬Þ¬ÝV@/Íùšžºëd·E¤)#ŽKQI¹ 6«1{ ^yþ‰ÖG,ÈOÏü÷3úõøSQ÷û•ILºë¶í£ÓG{Ô3¹0¦2;µ%ªÚ“È”Tƒ|¤ Ίþ®iÃyÍhpCÙfîÃoWPçVòHFlëîtÑ©#hhÿîôýê-ôÞ‚|¬±Ü—ÒSyh¶µÎìÌpyä Âãï.*ÊT¬‚°}ºøGJJL€¤8È×ã]³y}úý´ç@1õÌé@þd8mß{–®ÛN‰ vZ¼j &‚ŸDÇáý¯Η?¬£Ò#Utlﮦ߾Ý;Ssßpº<ôÎç?ÐÊM»Ø‚Øì‰ÿüœ±¦$ýö§?Њ;Éf³ÒOÑÀœwÒ°£ªðµã~â»wú¨Ah|Vж½‡hÄ1=}÷[zQ#éª âÃïÇ»¤²2ÕR¬ãÅ—ï·?]BÝ|9aKMš)ôççœ@·ÊÔ¾ŒÔ‡&œ5Š’Q·~øq+´2«hÚÿ]n&Ÿw¿šñÏyôË‹O¡ƒ‡Ëéó%k鿼€2ÓSè«üõfâß u ÓS“¨GN¶yì(8Hß¡ÃÉŽãóݪMtÚÈAôâÅu›ëÓŸgΡ‰çž@ÇÛ‹ŽT:蕹ßЖ=¨w·NÔ%»v«m§ËmøŠ »¨êèÄ›—ï»çWRJrÒÚ|4#"ÿLjÚjnS¢ª=‰ˆ4¡Z®*hÜc‘t9ú*ãTR‚:;].¨•öÒ÷k¶™ EïnÍŠZípæÓóSÜ»íêóPy{QÇÌtœ{Rjr"<¶7]wɩԷ{'32R鲳Ƙ†Ëí¡¿]iÞoêìáý¯–Ñk·™j៟=†ôÌ1ßûfùfs;¾ëÆŸNãOIï~‘†©Ì|ÖØ¿;÷÷ê9ÎA8. g>Ç»Si÷t†%}\¾‹J Wæí—ñõë/‚¶¥?M8c}»b“I„üñj§~½jaþNžù.wÂõ¯‡ìgÃŽS£Äöì¶ìÚoj¨øzê>g¦¥ Mð†©Œ[['9¡'þp•Ï –6õ]û_päŽôÉÃún÷êÚZ¥=æïÁýº›R2;í=p˜²ÒSÍykë¤/p¹ˆJ"BºHy­!•nEõrؤ‘Æ ,úäv¢~s ˆoýç£Å”½0….û阣 ÑÎ}‡hþwkè¶_œgVÐK×AMýC™¼¡oðX-/^_mlµXˆ{þgŽ„ï>ê{þ7˜¸Ÿ}û Ê€áÔo.=‡ŸBíBb¨cxxñœ¶ÀQ ò͆Ê:iŘ/K\f¹ì³­„¿ÛÉ•‡Hr;g›j㑃z™vþ~ê_³mÅ0¬¨¶på&“$Õs;ÂoÈñ¸sA•ñ=.† Y¹QøæÇ W™Òpÿ]¡fnÆÁŠŠÕÒ:©Â’sƒDE û˜îöëú$aÌŜ߂»FÏÝ[Ô Ñ“3 Gø%2VQ±ÁÔ‘Êjs<—ÇlÙ¨®kÇLbã –0¹§]\ZiÞφžÊ Œ:|ê|óiÃÿX­½2CqÏz ªØ„ÁÇ’µ[i#æ@r/›ŸÕ'gîµÏ|wi„rÞÉÃÌ„zòõ²Ú¹±n‡^·Æ@£ôû«Ï¡ýÅ¥0b\ÛhðeÕ´»°˜Øà‰;¼L¾9xwÊæÝ >¨sªÙ‰—‡’ÖûÍ3^»u/UÀÀ‹-£é•C©~«Þµ¤N6ÿõvæÃ;ÓÄ¿M‰ Â.é–Uù¤\CÓ yݨHyG£{è£oWÑËïƒù‡VŒ÷v¡3kæñ ôÍùßÓí¿N¿‚±Å 0Ö`ƒ ¶LIJÀøí(¨¨·(uWœw"½ôÞ×ôôŸšþyìkÄ1½L‹Ë×>\DOá>KÉ÷¦Ÿÿ”:ggøÂe£¶°f§ÞçëуûЗŸÉ—â!ðýš­0:Úê /ï¦Ë|×-¹àqZž ËC1ï@%Ì*bVß²ûsÍ™H¯€…1[&³‘Ó;Ÿç›¶ÜÉ­ïx~:ìwX¯…2ב–8Ö 1¹>ü¯yæx0×¹u˜oÌŽ¥àj‡›žzý³ÞY ub;ŠKÎÙ¢:ْ¶G€{au+&äžâv»ñG@¸?Œù`?ï&7iÊ´¯øC³¦O=“ÏA8ÖñäÓŒëïžòþ½ÇÜyíAײWy laŽÓú¿Á=ö*ôˆS`ɬK½ÜãÄ9`ÙÉãÁõ÷â•tR““¡:öEõ‰×æÓªu›×ÿçé¿Þ†›…8XƒÈU8Z'Òà…qmR¦b›°E“¥Z liß–®ªÚY§¾q]¿ç©·éê O2m8¸³ûõò f'á©»~é«‘ª“m‰M(¾ÍmÊš ÛV¿úä#¿Dx!iOBÅ'a—tÝ=K5s¬ª–U"znl•;õ'\ŽT „ë}·ábÀ SŒQÄ ñ‚Fƒ«ßÁåáóeãÆÃ˜¶Ä*ðÕ˜÷Û£Kár¼¥NFC[Ûà¬ó6Œ’=µ eïdòCöwš cÄïWoÅZÌ()!Á×e{ qñ…@ØI×Ð<µk« Å ‚€ ÔC`¦ ñ!.¾ÿÀ†®ùHjQ!Ýø.O’:A@&;éb‚t!Ý&2C ‚@Û"ðoLzËAŠÂ…@ØI—ü$]ÌÒ WNJ¸‚€ 4lÅouW« :P @ðC cºtk ©0EWH×|¹èC€§ù/æÂót¹³OŠ¥R;R9ÖYþháj;¬v[cΟí‰%TÃðiõæ]t6Yàu”χáS‡Ì´èK”Ä(j»¤k!ݧ^ÖÉ"¤5Y/†à5Ç{€P»`aŒGþõæÄ;±K‘ VÅé­O–˜›tÊJÃ’«é l xÒqh%V™â5“«°Rœ8A )Â/élHåuEÒm*+ä™ D±ã©ÇcFeÑÊÍX »ÿäd™K¢òzä¼1»µXIê ìÍ«°ñZéËÖï0ïË?A )Â.ébñŸ¤‹ˆˆ¤ÛTnÈ3A@hs’jÖ9çˆ$'ÚéHUµ/N½¡jVŽ·öãŒØebÓ0lø¡>%ç8B ü¤ë]>Ñ„L3dL7ŽÊŽ$EˆK¶`kKÞ›š7ú(8XBCúú–¯“^Þ0dV⥗cS{^žUœ Ða']ÄÚ¢hÜ+N(FàX¨ÿ5÷[zòõùtñi#¨cVzƒ±åMÊŽTÒO¼I«6îjÐÜê#ö1]ЬSÑ®æÑZ¶GýXÊoA@"€ÀõN÷}…·µäý¥Ùñ[/ŠJŽÐ‡ß®¤üõÛÉíÁ€˜ÅCI »Éb=²e¬ªáO£ÊêLÚV‘A›wu¦E«¼k.Û»+]|êˆs4’¯´'ÑT ½q ;éVK D6ókÒ:DÑ#Õu»=Fœ´Û„}üÝ”`ßO]»|@II;ÃQ&óÔÔUæQ]Ý›ŠKÆÓ¬÷4Ká}ÂP²Ù¬"õ†=BûU®>ÿa-ÍY°åéÊÓ¼ˆ”'NI´–)EFvÐKs¾!‡ÛAi©Ë(5e-%‚p5 C7M8‡³;UU¢­{ÇÐ¯ÒØGwü#}ÄËC?{SQ鵓ì¨;iɉÔ&tÄ~»á&h•ïÒž4‘‰mô(ü¤khTÚЯ쬮å\‹€ª ¬Þr€p¿\ºŽ>ù~½Ùtìø.Y´ÈïÑÉ$ß5g&]©×;t|öØ!d·‹º¹6ç¢ûJ•«Ï±çë{ \&”¶*OŒT[—)ÆCaÂC7…‡JèÅ÷¾‚õÏ~êÞõut*‹[œ¡‰ {AÎ{)3ók*:<Žæ/&ÊÎHŲ‘-]·¶ìÙ¯d£ÂLKN 1CúÑ)Ç ž9CNÀ*Òž}TÜ;éÚ5:¨&êb\¤KT¤:Ê"Á½n® Õ­Þ´‹>]â%ÜÎÞjÓ˜2Ù›q8t%}ð-ÖŸÅÆÝÃö2‰×jµ¶iÜäãÍ#ÀåjÍæÝ4ç+/á¶uyâGªL1ñ0±®Þ²‹6íØO;ö¤ÃåGÈãÖÍÁ®ìôdrº r»]Ô=÷ß>)kÐ|°DÜ1û}r9{ЛØàžÝ^ ©y%%n‘— ì ½ñIC§:õ¼}½¢Š¾‚6ëøc{Ñ¥XT#§cfÈÈWÚ“2*Šn…t5 ’nªÆ ‘tëe>WV±„[Zv„æbl‰UÊ,‘D‹ã¸¸öw¦w>_Jý»wAƒÂ{‡j2Æ-Ô@<¸\UW»@‹M•r4•'Žn¸Ê”ªO,Ý/È_Oå•”Sˆ°€lE¤Ya…¿¢2lJïéH«&\;7¦¦®ÀÏa¦ä›’¼A=ò;W™ßIH( ô´|Ò1{²¼ì$tŠÎ µ[wÓ/Î?…NÞ?è:¥Ò/í‰ôQvvÒ=îƒ%ùãr˜um0ÀÏ\;qhÂÐÙk•ðepD6:ª7Îc¸UØ({áŠÍØ0ÛmŽá¶…J¹±Ôs\:dͣ¿5{çç4Ôl˜xÃ=6ÕXœä~ã¨rõeþ:*…ÑáFSy☇ºLyÓì1÷¿}Ûòí;TJ)É›)§ó¨µ7£œÖþÓ8z=ÉH_D|´ÔY` ÅªéÔôeTtè ú÷‡:L—ž5ÚW·Z–ò§ò]Ú…HtžÃ¾"eØýÑA•|WU©H»5`x{¥ÞqÜ Ì»Í‡j™­”#a4¥ò£¥gŽÇí»5›M©ÜíÌ8þâ¢U®¾ÁT³h-OŒZ¨Ê”’îvì=@O½ñ ,ÙG]:¿Žã5¤SØ 7˜`³¡œ./SzúRúlÉ:úhá*Hà˜ïõxkÊw–r¥=i-z‘óvÒ5“¢i>Ò%‹.ãºÅ×+ÅÔ êjmß{ªJKY¹Üoå—8nÄw ®NÄ›ÇÌiZùYñÞ T¹âòT^éŒêòÄÉ ¶L)Â=T\JÏÍ^@.O1Hl&¤Ü­@­­½ž‡a›æ<ùõ˜'ÌéjMÝRùÎõRÚ“¶ÎϦ¿ÒE§ÍgÁì–q]3G¼•Ä*—©ZÞŠF’U`<7ZÇã¸aG¡ï@{äÑš¾xˆ—*W¼rY´—'Æ;˜2¥—Õ©¯Ïÿžª]UÔ¥Ó˘žs8&³²c‡y/Æ8ü÷æ¢8œ¾–:•ïÒž´±¶óÒõW/ÃÚO$]ä7W(&-^!ˆ­–KÀÐÂR‰ñœð,|Š"Æqã8.¯0ãÍñoMÊ8HM# ÊÏöòÄ) ´L)ÉŽIfÕ¦˜¢s²²æÃ`êPÓEñS¶„ÎÎú˼VšibMRKë—ÊwiO¢8ƒk¢Ò…± OÒÕ MÆt>¯¥ì®!]–š«Àa±–F}‰á8–UV›¤Ëñçtˆ‹T¹*­ÀR¡Nƒ‰tj)SL2lWÀÖË6‚l˱|i~¤£òï±Ñ—Ý~«cí0µI-']iOBža 0"¤ K*ߘ.´“í^Òå^ºRqÃÁê1—K‡Egôus]XØ“ã­ÔËœqm€¹rbE$¶×Ú2å­;ºiW° \ì9PŠñÐï No¹:6Zqá!»m7&—i¬èm+šŠ¯¾K{ÒRÑñ,"¤«éÚ•\4Ï}Õu{>+uÏÓ Ç ÞFÏc’nK{â1“¸¨*WœG±äZS¦8Š\6lßg&3¶ §šÎ‹ÅA—w9XNgKòRå»´'Mc O#BºXy‹/±õ÷]·Ó ®DÞƒL `ÄKM$boÆ›Ûu•–vš•Q•l•&߯é¶¢L™*t^PC2ûŠÊ0.Œµcx,·~!r{:Pjb‚Ùo‰Ý„¾›ãÀÒžÔ‡4ª~G„t“Èí#]¨ODm_EAcýTþ3ŽÉ–ÿ瘊wÌxDUžÄPiò&¶…eŠÓgJº–qÁ¾ Šl1` ÑÒuc‹M‡£'õ茭6Y½lÖ±æß®ÍwiOšG«m}D„t›{`?/:ÂIEÊZ;±‡ì6db›ÄÅ ºªäœ§â¢ ΛXs--S>Ò!±-„ _ìXkÉm4¾ÅÅ—`,žè˜Ü¦‘bkòRÕÉÖ¼ÓhD"ø ¥yÁ(…õS!ÝšlU)©¬r‹´«ÀáÙjI€ªÍÞ`ˆ|ßfóîÚ ¹)´.o‘vŠtYÊÖó ØçÙ£§E:æ÷¬ÖÄ~ëAƒp+«ÒPÈ$i)‰0 Cw‚ÿ‰‹+"Fºè|ûH—4£]놢'šœ”Mw^µŽºv<ŽºtBלÿÝõ‹Mtû«è’ÓždrMAÕ蜱¦»®Þ@¸jM8ý9,¼žH©ÉéWo¤NYÇ] C‘ž #!­êÚ÷2š|é7hèU³ Ñ£î£û®ÝE=ºŒõ!Ü9{YŽî¼zÝtÙBÜ{¼ù,Reª–x=”šû‚$Xÿ?)¢~úSSºÐµ¼G¿ûù2_Úù¢Sæ@ºú¼·èÎ+¤[~¶„N;þ.óùñ¯¦ßŒû×­'ÊâÃRù‘1”™d£¾]3QG-8¬‰×77ƒoô_¨êßÏÎ|‰Îu¿ï;}sOG{²Úl?ÔͳÇü åaçQÇо—R0éWáó9Téñ3š®Uí {œ 2©×Õe\— VíÑzøOví>°S ÖP§Œt¨dýsÞÙôö—¿¦=Î#n@Ø ìy.>öWôEþCôî×7¢BŸJc޽Ë9¤U[þKg޼¯õǵq=Uf@ Ž‘—T¾´6º¬ 9}äÝX[ûïÈ[’2éʳ_¡^|TP§ø%ÚÓè¥yçb>éK4î'O`cö+SœFåºd%›êزò“Ô­€ÎõÓß½Ó(úÍE¡sÚé¨ðNq'ÔÚNzzöHúè»»è'ÇýÛ^K«·ÎÆv~4¬Ÿ·îõb7R’×a5ªƒTZí¦¯~ÜKåU.“t-h8["íª|÷ž›øP#ztC}ºJ?¬›iú8ièÍtÙé/@‹À{ÕÔºü ÿ¢ÿÌÿ™ïø2:jجaePé÷í×âóʱd$ÝšÊb æ`pOLÈ‘^Kÿýü3˜u;Þ'>¼n+­¤9'ÒÊÍoÐ1½. ½‡–Ó²ÿ6¯íö> ê=Ž– r-Yû‚ÙSçã`IË—Ÿ¥GvRfZówyå>l²PB]²‡øžËEûD sö`:P²R.æŒÃqÃ:ç››±@ÝeI=—I.I ev\l)Ô1Ã;Lî2ń˫^Õá©,%ÓA^MeGND¬fÜZó¯~ú¹Ãº¤k±-—,^ó p{Â4ºæÜ·ÑáK{.7?WX¼XôCÜïåùÆ]s^Õ»©¤ ½‰¹.YCê´',¼ Z´Õfþ6…ŒÔ\:¶×…èпâólú}ÅñÅÑ%*L‰Õì–­„]tØš&¤ÎÒû¢GZ„-öÊ å“Ãî2…rÿk>ãÆQõBù†ÓU X¦ï½ÃV²3úø~ËEûD Cz?Sjm.õÝI[÷.0‡%:bl³'4*v{ $ÍZ5d eŠ%Û_\t ¤º4l„d’jýø°4ÌþXòMJL¤ËÎK‡ÓþƒÅ´éP_L%%ç`æÏ)#mIý×ýÝÒôs{СÝByÌ5¦*~þ÷ãÙPUFxâÚ7\¦x,²%ŽËå²Þ„Óž¥‡7Юýßû^ w™òª—™t@¶I” )wo©“Šª<Ô!ûcÊÌXä‹KK/Zš~‡³ F‡蘞˜$6¸ê’5éÿÎüý°+.Ûažýg2 ¤[t0ÜàY‹Á†cáp.w%4ûa¤Ö²üç8°vƒÇ²«aâïB•~ÿ0ãí:bêec¹+Qr®6¯-Bº&¶eõq×ÃÌÆŽÇV¸wÍóqýÝ£¯õ¢…‹hñÚçéò3Ø(ÄN›÷|FK7¼dzKOéF)°ÄÜxÿkrÝ8£¼!}ÆÃHÉŠ*Ú´FÿÔwÐ ƒo„}@m-X@Ÿ,yÀ‡X$Ê“.Ïcei7!!œ~ÜUD©)k0ܱØ—Ö\´&ý_.›A?þ{ºýÊÕTk_¶TÞ´ËkxßµÃq Üm ʺh­‰ ûu»:"4¬Ál÷.Y³TlýqÝÖ†Û˜nrО´ÄõË=´þ4wáïŽòªôp݈(ébÒýJžÈJ$Ý Rµ³ĉ© Ãn1I÷å/l2¤oVü•®z¤kC®m øýM»æÓAH*âÚ7k·Ï¡S0ßt8Æjyþ¶r%å;1•©—úiž¿Zþ(}ÿãs¦zµÎüˆT™òª™½Uk¶B*ÄBj58õãÕÜïÆÒ¿øÇg‰·½àk⃠q¹ÙìÀëx…®±èŒ,X>CÝ ø\Y5È|7·czÍæ"á]ç|Ñš'éêsÞ¢Ekž6ç[«ˆ7Ô¶ð¡úe‚ý‡2ýêûñxލzÙnó¬T bÛÈáF^^D¿¯¾g^Ä 3­'¦û nQrx¡?á¦$u¤~ÝÏ@ñp‹ÞOñ—/—MÃT˜+‘Ц—@b- gÖw‘.S¬nå±Îõ;ö™sty4Pךô«oø.ßã…D•mò›3¯|¶îÌQÔ1cÖ‰6(á%\Ž]ÁÁ•ˆ÷s¨ªu±­õªô׆ŸW•tG¼·ÿ@þÅ]öad¢ŠQêªU/¬›âÚ𦊧 ÍzÿÌ€?Âæ ïðûòbü!°y÷gÄG .ReJ‘-¯¿\]†*1±CÊÑÖü­MG°éÿqû{ÄG°®²ò8Ou¤Aý³Í9É,Õ‡K­ì×ßëÿ³ÕסJ«?c/D\ÒD'Î'ízÜžv«bVÉ{ö®è¤c6U´;ÃH$;ÆÓüãíqnOñSùbÇ<ÖX(Oœ7­-SLºlXäÂò[•ÞáMsÆE6{<éT|ø"JO´R¯.YPÙZ1,T[ßK¤ÊwiOC(zîGœtÑgó‘.*O»$]®õ]"ˆLÇ^šÑî<ž ¬Ë[wÅŸ†ÒíéˆÇøùçCjRÖŽþòÄùÐÚ2å#]L¥±¢*¡£®à7“a«‘FII‰æ´(^ÄÜq¨Îzci‹•öÄ2jèÞõžKK¼Ý8éZmV阪]“.÷PÕÑ!ÕnN¨Â‚çÑê8nläÑ5Û»è|C=ìh{{ŠçKï®ÙQ_ž8O)S<]vSÅÌãºýrRqL¥¥gÅl6—”eO¦Î©6:67Ó\ð#9)ÉœeÃ:Ó¼ HsNµ%|Ž•öóHiëîbÙÚ×#2n݆‘xqÒ1âÆ- š NT­¹¤KN$mßðUXвê(#ÙŽ±R“ÔGD[T}ñḱÛ1#±®í0ø<ÈE›# ÊU—,¬_œ`‰êòÄ`…¢Le§&Rn†JËN…ÕïÈ6σÖD@‡Ä¡¢Ÿa½è³©c²…†æfPZj*¥¥¥˜K\ò ,érÑ”SùÎõ2fÚ“Šr±Ú«Q@˱iÂ<Ìû­vñê¯ñíšÎÉ0¤Ý\’ Ÿ´ëòh'†á3Q¤¯‚ óÊ“ü-æŽ)ê’bGÏUW÷Žºøsœ8n½;¤úâËÚ.•–¨‹p;ŒÊ Î.WƒztŠÚòÄÙh™2ËÈÅ»ËÏÕµQÿN©”;ÄCد¤ô§QŸû¼RyÅh*Ø{: #¨kªFCº¥QTåié8@¼¼¦t¶õã¼ä¼mÌÕÏ÷˜iOªÝØ‚N‚×”WõzíãKhöægqÒe0Q~|ë´šñ“x¸±´qoTmMÆ;¦pÑ›i×¢SÑáñQ5>ÅceňS‚Õ ~]2LÃ÷æzà¥_î‡ÿr5¤wgJ²kfÞEÛxg0eŠI†‰ˆëL6 æ5˜“`8vL§ʲë Ý3i_áMØ…Ë»…ek‘®¬2âöxÒèP1w΢#GFC…ü:X4‘öì½ûæN »–B³5Ð… 7GVÕJÇÒ–)ØE {é"\ךsþùKíÉO‡_ˆ¹Á?cN lt¤ºþóÉÏi)ö%ŽW×&¤«kÚw PŒÏ´;ÒU=S®LfÃ¥ìxÇ”èn»£Çëru¦¢C—+ˆÚü\Tt¹i:°S27ާWåÅñWiióHJ|y¡ÊU2 qFöédæça4¹`Ê”—t­ØRÐfñfLR©Øø O¶º%b­bgWï$*ܦ ÅøvÓFVl9\Uu ¸Î´ f⮪î4d¥e§ccùãÍŽÀ¡â èÁX ñIѨOš‡wN€D:effÔé”I×”rÑ&4'årUTùÎõ3VÚ“ääD;ìzÚß°yE&ô<ìè¦/òó°ŸóM(»/xtæ…)€61Cµ&ißy*1¢k:m ¶ùKÀ6Ñ>ÑŽ#Œæ_!plÁ\9xÜ&…{ìÙ)UT ¹V %:x%uìô.vq…à‹­‚¥n¹Ñê‘n¡œ¬Ôšxz+uK æ»þÿÌ:‰]ŽðnÇìv„õmÒ`ØÅól¹ƒÊ‡B²xyÌ•›.Ô%óy 1sG"ÎlSù‹í ïû•Úƒf/¸›k›äûîדè\l_:zÐuþðu­ÚgUU!F]{Òf¤k±Y¾ÓÑ“4]lS™™·Ï®…;Þ¶fË^:qXp†f#^¹Ûá†{ã^ÂUªÂj²¡èàqP*~•n%7Æ›ÜPû2AëÐN˜Ûe' H<ÍÕ,^¹7Γ,5™–•L¼è™s|ÙŒã¬cÑj»7oäyÜÓP•ÄÛ2ûè~?äeŠ“Û\¹b‚é)°¼ds§ŽN–QT€×pRÀÈEªL)âEq¬élx ™t¹ìVb3–v ]^¹ŠÉÇ¿~©÷•µ/¯sÌΣ{Ì1bSRF§0!¡š²|Ò2ê(ˆJh³þñšXLÎÜlžqe)•-ªùûlàÅÚ¡dœÍÕ¥@¬JÚ5ý¡Ž™’/¤_¯•r-ñ~sùÎÏ­ÖØiOºu:ž~uÁûôÖ×Òáò€Ä Ï–þ‘Ê*öÒY£l"Õ¦ìÙ¶e©ùRm›Òè;‘|Ðf¤;êÝ‚ ùãºBü7:@9ÔyŸG~°gs$ߊoùˆà«Þ]3`øñEK×nëÒ Šõ|½S´:Z:¯úË+ob£ ;TdÕè©»)*²tleƤ¬zë­HC£^9ü=Ui¹Aàé ¬Rf — 7+3Ó¼æÆ„{êÜÀð{Á:àhèOÙš¥‹· ,î…ñáÃ;Øð£ø}_CY¦8½--WL&5dÂe*”å*ReÊ›V¯½I*(ÇÜad’K53«WY{Ä’«R1óv€þ81á1ñòû옜«aˆU ®ÄÁäkªi•΄ëWÿT:™°9 ®\GXÊMAýáN*ÇÃÔáY-Á{%ìP­iüki¾ÇR{’•Þ›~uá\zçËëhï¡åfR—¬› â- ñ§>…<;Ú*ÛC×Kò¿ùb=^ˆºö¤ÍHÄÈ—ó¸f#©kîŸà­¤kFÿLR8´¯àÍ5íÖÍ» i`¯®ü,`ç%:¨Ä â50ÄŽ¹ŒUL¬Âå^»¥ç^¸·ôú †|¹’²ãq0oƒå†¡$s ×4òH3 ·VÊ­m ÌüÇøý¸e¶kËæOäxó01Æ5K½ñî8!/S Z[•«¶(S^²©%^&2®;Lt¬=â+®R³rýa§ÞóW/s}bL²U ÞdHËL¾'¤e·’”U½óƒÉÞ¬³Løn™lùì#\Sí­;\çø]Î'>‡ÊµU¾+<ùêö$91‹~qÞ[4+VmÜ5ß„jýÎyäòTÑågÌÄ÷j‰Wµ);·¿ܦD]{Òf¤k"Ç‹dÔ. 1“î+æýèû§¤Î@÷7ï¿ûæå“o¹úÍùßgßwýxKë—‚p\QXºd§*"«Ìq T`®ôl¢^oÖ¿·ħ}ßcÉ•+¯”뵦f’eõ²Ù€ ñ`µ2?çøëœ.?ìî.[ôÑÜOOãCU…y°ŸŠÖ÷UúÂR¦8ÑmU®TŽt™âôò·½ßEÒc'Ê,%Œ5k:«µZ"öëõ"ÄØ.s«Ö¹cërARu@R…‘×½:ãÂLÚì±æ}þ.K°ÊHŠÇjÍ1[Ô]îÀ²µr(%Úæ t[å;Ç+\yoµ&Òe ØÏ—æù,›·ìùœÞÃ\ÞËNgâµA£ámS`¶\úí¼9s¨lOÚ”t5ݲJU³ AÅ|šy½ÿ¸{Ì„à,.>P¾q岿j£Æ>òê¼…Æ —ŸTWUT&4󕙯¹âroYUz'zÛfo½fÜWõ¶¹þ·ÖqÃN}›—ŽSê1eäÁj2î¥3Ù²ÚŒŸ«†Íûvàÿ·‚ƒ%´|ÑW¯VV–óDHGÍ¡*ŠW ü±ðfØÊ'^åm¤ÊU[—)•f•n+VP㹸Þ*T'5jaUoØ_ÝÃ[dXýÌ’1¦WZv›3×½ZI×ë×'ÕÕÔÕqef²å3wBUo¼_mú¿J¤òc™¼×LËæ[*}÷ã?L6ïþ ‹hÜL—žþ<½:o‘Ù¦¬^²èùòòÕ¦D]{Ò¦¤›Ñ/õ‡’m娅ÚH‚Ä{ìòKz掚»» é"ÕfO¹ägbõ·Ï]˜žý/\ß@ï’ñ«ñ§jÁH¼\Q”Õ"WP¾VÒ.K¸þcR,é¢91ÇuU‚x´Úù…pÃÀß4Us¦‡×ʲ~ÃÑêÔ{{£L¸ùë·k[×­~oůWÀ ÏÑåƒw$g|çöBºa+SÀÐ$•H–«¶(SœÎ†œŠ ×'vÕöçïØŸ’ÒY]lŽƒ°k —ëž÷ ~•ÃWuÖZSwóH“­8M‘Ìwþ¶Â›7]g{rúÈ{€¿N‹×>g&yÓîOèé·®06m§íÜ´ñÍ|¶¢¶=iSÒøÌÇÒq9¬b>›Ñ3ÈñSœþÃ×Q踚qéÂÁZñÑÿ~ó‚«®e µ¯è°qÕù'YB1ÆË…—{Ð&Ö«ô\ñ¹Â«q©ÆÄ©Y§n0¸ñð6h,ðÛ« mÃÁã-¬Rf wëÚÕï9g6ÐðŽSÜ+å3ãÊø2ÎÈïx+¶\DÊCâÍãð—«H—©Öd·Š[sï°?® Œ™WmÌÒ²·’ ?@IDAT“[¿Þ±_u¨z¤°né÷š‹O0ÏU\â­=9cÔ},vÐ÷kŸ7áq¸óµÌô»¿ý x.nDu{Ò¦¤Ëhanú—iLÒÕu-H×”tk26qþ_{kìYçîÓO8åæ¿½6?kØ€ÆØ¡ý´ãtxoc•Þ_=¦ÈVËÖ:Õ(¨FƒÏ\I•ÊLÝom¸þþyΛð³E!MénWyþ7 Þ\µø[ž"Ä•£¼æÌ×þ’n{"ݰ—)`ë“z9ýÉ$”å*eŠÓ §Ê?ãÅNÕ5uVqðO³ºMgŽ_CˆPæ;§×ìBÙžðwT›òãæ±FiÙ-3c!ߦ¬¬C=o™J7=ý'û½0 ‹Úö¤ÍI“D¿TŠD¬ûÀ¤ÍŽUž<®Ëã,™±Ùœué‚Ï®ûaɆS.¼x<¦¿œ rÉD¡3Ò“¬ÌT-ÉŽ‰¯!põ+{ýß|BUõnýßê~kϼT&¯ÜÅ _ žšÇã.ß±aÝ7‹?ùhAUU…"ÚR„Ëo¢Éx2®Œo{P-#™¦kÓ2Å1¨_ŽêÿöF³åÿë—¡ú¿[’ø 'õó¹þï@¾]?¯ëÿ$LõNCmÊÎM¹_öëoMí3Às.ûËîHgÞþgÏu=@ágT¶'mNº£FÎÏÏ_WMA:Ô½W_œÓoø‡û·) £ì¬Ô,™p÷—Í–MB­¨(Ó?{çÍ·ñûÃAÇ>¦×Àc‡%§¤uJÀÖ6›×ª ñ"ü˜pX ×ᨪ*­:r¤xç¦ 6­Y±gÕ1K³L°L¼L¸%5׬ZVã¹íAÊErM'eJ!!gA  ЦT£M)«ª8R´kÓÆuW/gž¨þíÉyš£S7ïôӬޯÍ÷ÿͺâ‘?x^Åó¨ªjsÒÕò¾rç_œó ÷bÆÛ¥™ªæh%]Ž¢¿d¢ˆÔ¿átlX¹lŽ ðkJÂ83AóÁN½ãýŸÿi2V|¨±pîy2¹²ê‡–p™|Û«”‹¤›NÊ”BB΂ÀÑ4Ûž¼ðÛŒ»ugbMŸÓøõô4ý™‡ž³møãÿ¹¿=:¸¶½Óæ¤ËÉ }TMÒùž‡[/¶-,Í~I–ÚØqP¿™TX’KÆXMÒeŒY"fÒm„‹dšNá„Ë*c%å2é2É2éò. |0nüœql¯N•!N¿ÂŽ1‘2Õ^K„¤ÛU'lOªª\G>ù_âo/ù…sZÙ!ðœh³yþ÷Ø‹I'Þ}cõvÿ€Úú::Hצ}Š…½XÚÙÆÄ‰Vmöl7Z*Ü(ú_sÉ$„˫°›Kã\Ÿtã™|k2Ò‡‹ª$¬:VÂÄËþÍ÷Õ8®z·Úó/Gþ×R¦Ú]Qû! ÚU'mO–|ãpœ~nâ¥Ù]œ á¹ fytvŽ™}Ê}“óPVT¸¨ ÝQï®Ãþº{€HHºÙË«¿‹ëÆ#Á…€ €aP’ «•ý Wý2ÙÆ3á"y¦cLÔÁùWƈ‰–Ï|ð³ö,á"ù>'eÊ…\>T[¢êG£íÉcS[ÿò¢}‚Ûp/@ ”„†x´’—Òå¾ÐÚø"*H×Ä@ƒ´k¿áës´“®Ê:& . |f‰ …qe¢Um{Óõ¯(ª²ðY‘-?W)Suñ_íÕF¨z¡ÚÛ“{ot}ÿÈ‹Öë°9ÅMØ ºìá­×?p£çåh€1jHŒô)5IôÅãºE@-Œƒ"E¼,Á1Éò¡¤[%áªs ƒŽIoª’ðYŒ:Ô½˜L\„"Í©Ž‰oÏe*B°Ëg¢® ìT»ÁgÕ–ðYÝg?¦»ÿFÏ[ϲœ…iP“͆þ4Æw¿Š†ñݨ!Ý»íó —‹w¸ÃztbþÄ~™cfo‹=|M^6wR™Ï"\ï¯ö¡ZVie,”S¸¨ßrn9 ;)S-ÇL|Æ­nO:]ï,ÒöxééNÃñªaä¡iyª.µ JQCºƒßÛ[„%!—¡ÓÂã¹6ì@ÍÒîì6A%tU fèB”Ú;R¦Ú{ ô·É“ *™eÿ%¶¯ø/0×úȬ‡îÁùÑ&O¬®Š"§}¤"chÆ…êZ΂€ ‚@k¸’k)V–¨ö=ã¡GþiYû;òWQFºä#]U]}|{ÿŒ|®ËA@h'ôÏþÙà ’Åœ\¨‰ì†Çýl[&=ªHw̘›ò±gÇÁ@º.»¤{›öHÚ2cäÛ‚€ Á#pų=¶„„_ƒxÙÀ•‰÷äGfY|È…U¤«ååéP+Ï÷%Åð\ä»– A@A î¹Þ±›/<§^Åt¢G,8³Mlš¢ŠtM@4íc z$Bº 9 ‚€ Œ€‘˜: óIxƒvÿŸ½ï€£¸þ{EwêÍ–,¹÷ÞM1Û˜Ž±„nB'TCòsøNHBH€bÓ~BsÁ…fpǦ¸7¹ÊMn’mɲºteÿï»§9Î’uº~ºyú¬v÷vgæÍwgæÍ{3óæÇÜUŽåDŽû ý;¡g6.f`°>‘OêëÇe· 2!‰€D@" h‘L»³´P§(¯ˆÌ)võ~·U¢¸Ö9ì„nßÙGŠœƒÞX³«Ú¥¶¬Ò Ó‘H$-ø¶]þÉÚÜ!d‘-©VkÑSÁÎnØ ]ºE…ìãĵ ûŸœB*tg¼cêVU|ôgE%›9µíð'n>R)r¬L_Á{ˆºl€`£ëÅ3y–H$‰€/tK™ð +vùˆƒ'TµÙwfþ _âó4lÈ„î+3õ7¨öšÕ éþ1mªýW+˜g›ûqÍk«np^Ë ‰€D@" ø€&T‘¢¬Q°îw‰¸ä9èB*ü+³t&Åþ®~nŠí½Æ2¯KùšýeÖà9Û݇°wª½+—H$‰@³Pi•ó}»Úò„î_>HÈÈ-ž³„3™jÖ%^Ð)å†mÎ 7pÑkáîRÏ]Z÷È&µÝ:0ä•D@" ø€€N1Ô ]ÞäÞ‡¨<TMW­ªÎfãy'Ö[ÇVÙJ÷çžžSþòLÅÂGù+3•">Ž¿2KùÞ•{Þ€ø ç½JA]ÄìLW^H$‰@‹Cà™ÉÕ;8S§1×Íøó»¦žÎdP·6zzŠe3g¨³k¦°Qýÿý_gS~ÌéXs™ÅlUìf¢*ç+1:uaµþÍæek½—`„a‹Ži 9_’‰€D@" h&<|©¾XõÜmgN§Ädœ¶¨–þ3Þ3wïö_x¢€¯ÔîUÒëì¶ñâ™ÜÍÔýÜjOÃé=­a¸÷ééø÷t‹ÛޤS²¸Ã¤ôe2a‚€Ô2²«ÇYÔ²TWÿÁë360köÚÃï G˜dÛŸl!‹Éžº;{n¨ÑdºBÔ)^J˜àÏÄd\ဪP©¨Sª]]üî«Ó×3×>×%½Ýt’7<ÐàÈZ¦Ð}zªe#¯Çm gOÝQv“Ï=hQ~κ±{øÝ¬é&”,½‚¯¿ô$lÞQ¦L™kKS6õÙíjÌÊNŒ3ÛS’â³Ñˆ†CR!Pe±Ø‹K*ÔÒŠ*ΠÿãÔß¿”_UYõæ¡õÛßZ±b~C®5„0 Iu@Ø¢¾èG¾!®ã°~šcÍÉ:UP´^¹Ö)^jú"ê”Õb{]_¤¾1kÖtøí÷ª.ÙcìÒüjÈ¢N”B¢é"G<ž;Ýjµ¢·ê‘ÐÕÂ(Ê<3›¹Ã£™˜ÃAèêîyæWébŒï¨v{vŸ.mÕóúv¡þÝÚ*qfSPv­’­£UQUMÛrÒºœý™Ûs¼Ôsݳúuýí'oþí;æÝk!|Ã.!`H¶œ®ñÖ‡~weRJêÿ³«j–¬S!øá™dCuêOºLÝ#Üß÷ÞŸ_\Ìl7[hÚk,­Ev¹Ç‡q@)dBW5è7©Öê ¬ñ–qÿ¤˜¥ðÖ`‹YC,æîÊ6Éâ|êù©ö¿ ô,t­µB—¯NmpìF$ÞêYë‘ß7íñQ¯fµNVuåÔ½Cü.I" !À/º _ÊÞ¼|útñ/,Hæþæ‰gÿðákú'¿„>6ï¨ÕZ¯ÐnÑÅ06˜cã_ÌÎH‘uŠ‘TêTæÑ§¿ä¶øéw^ùŸ×ùífYT½µÕ,ƒÊë§æÿ;­çàÿh›ŽqÚ=5;žŸ¢¦¦«Y=MFóµœÙžŠ^?ƒØ{zò- án¤¨“±ŽWÄ6xQþ:Þâ°v¯ªé›Öï%žù¬5÷>7ýqæ÷¯ÃzwVž¾ó:= Ü ³!“‹$P>ž¹ë:=Ê •—n{ô©Ç™öÀFF>P£±Ã&.00`#ë£!©I\ëÚb´É¨™uIujº–'U–B&tE¶¦N=Vñ»{*³W¥:»šß¶Ó¿á½sÕ„Ä¸Ô Ÿº£ ^σk¨Ó3k ˜Å ÒÝùä´«u:ýŸ¹qPï›0J‰1JKr(>D¤¥‰r‚ò‚r—ô?7Ü=õZ΃«à´,ùÊ/Ú Mà `"딯FWx×:…6m3#à¹l³× ]¶´ 4zž3`NXèε‘}jÙá}+9©Âç&ÿϸGn/*qOVÇ&fço¼Ç.|7;ïƒs¡»æšÛâÍæ¸·Ûe¦ªwŒìôƒ“K™J@@¹ÉnBÙíþÞ¡C4NÌÄÌ«aS' €#räy6`!ëTPo¡I NµÍHUÑ6£ælzT—x3Ë ¯6p^èÂ#¦”v½hYtböÙUªN÷æ´)ögezƒâƒÍ—¬æÞˆ6ùŠMÒÙ›oh;¼^D½€Õgôíü0&xð®^j¸¼¥ÆŽrsëÕò&”yáµ×Nå|ÆòÃL&ÑБÓêRmžc°uŠ‘ä¨S·\um3ÚhŽÄú¤^ 䥞[Åu Îa!t_~[÷ ©ö· :èç'Û>rÍì«3C^~Û€åA)³gc¶ç‚Ú[²ÙíÁ41k=s“Ùüp¿níT9†+¾‚<{ƒÊÊQrzÚ==óhÒvµºT›çx` ë”7¥H†qE@Ô)´Ñü{“–£™3‡¹³çTÜt:Ó®ñâ:äB÷O3cú³‹©;ô†ØáÏÜgY sñŸß6^8c¦î¯¼¿îN Y+ªí)×̳‘¹âžß–ÐÕzæ“&?|>§™ÉË‚¢A0Ës€p”#¥Õ%W?“ÀØn4h»®Z®Ù‘w¥•¬S*dQ-ÊÚh´Õœõsj»Åº­Cù8@Ä…rÿ3÷VÃu )äB÷™)ÕÛ»§´m³Õte–¼­;`³[_arþAÎ<¯eŒyȵmö2E©õ©ª7ÏÖÜCº¾€k­¡ˆKL¼Ž/xn’QF(G(O­Û¶Íy‡‰“ŠÎÙPðóH'!t‘×Xä]Ö©Hÿ¤áÿ¨Sh«™«sÖ%›Ý6²ŽsÞW7r¡‹Ê¶¯øØ#¤ÚžUTe«bˆ?oÚTuŒQŸcWmï(:Ý}Ó¦TÕ“†ÍÚ`a…ØéÃj·CÛVzÒé:ÂÓÖŠI’øŠÊQb¬I5ƘЋsÕtC^7}ÍÛ9Â;ê’C«7#ïÀ@Ö©s &yŒ€V§¸F[Í t­K¼NÐÕE‰Ð’ÏM±ÿ™í5ÏMµ½3íž²“Ë—6Ô(e³Ù¿êÛÓ&Û¾Æ;î¤SÔºYÌïTî¯øûÎÀíYìÚ±ÑèK¢ð^ÄË¥¼Ž¢º>BK…%”êLƒL°[Dújõ*¯¬nðy ¬®±Ð®ƒÇMféÚ”{¸ Ñçz’¯è† Ž=9˜—›‹ /AŠW«Kµy5!ïÀ 9i[¬VÂ÷ô–¡®xËû¹Âí>”O•Õu> Ïõn Ÿ…º®¡F[Íyl´.Íœ™ÇÓ:¡«‚¢é‚¡°£Ÿö®b/TÊ Æ3c.#6æÛ‚JKÛî㸷ÒgËøìžÛÝØû>þŽA3‰ñ’¥ö¥Ü¬â\iCÈ~óãVZ¾n'•UT‘^¯£ní3é¶k†SFZ}¹j3­Ø°“þòØ-0C:£zéÔ.#î ý¸e/A`°WJIŒ§A=;`Ÿó]\ä/¤WÞ[¤ýf0è©uJ"]uQº°×zïùz³hÕ&žÜf£©ÇœUQi9-Z¹‰†öîDñ±¾Y \ó£Ó)”K}ºdÓoÆ^ÌÜ:œ\™Èٌ޿’^ò×d4œ]ô¿ÿe;]2¸§†¿k¸@_£<éuzŒ+ ‹Þ92Ãû^CùBñbwOëÔŽýGiö’uܱ+ÆncÔŠËñc†jeê8ÿöÇ™óéÑ__I½;g;³þõš­ôýOÛé¯ßB¿lßçq]™vï8êÐ&ÝOc'ŠJ ^®ÒØ+ùÝj³ÑÎÇÙål;-~›ÍNo|ú=Ý9n×§³ßÒübÙZüÓ6->ÌNKJ +.ìKêÑh¡®k(Oh«™ÁFëR‘Rp —¡D- ít·¨6š9œÝòø¡¯ÁgÌÒߣ’}¤ÑœqQÍxÇÔì–‰,X'ªŠòŸç'ÛßBíg©ä=v¿áˉ¸çˆó+¸¡w®ç&†ßèË6kú¦Ë†Ñ€èLY%}þÝZúë‡ßÐK¿¨5&xgo^õèèðxuòt )8Mתõö?ÿ~-Ö[t‡ ‰=z5Êß}7ŽÒÞšÍ{é“o¢Á, M1ZóÝ}ý%AÈO«”ÚwäÍþþêÅ-Ü.6DCzu¤~¿kXà6ô~pÓÖ›£>¢‘ÀòÖRÉQ—ùä<{¶ÖþHAýó“ï‰'ÊÐ}7ŒâŽ“žVmÜEoÏ[Aæ[.§¾]ÛQV«Ú¼;¯žÐݼë ìÑm5§®x þW?l¢ÖÜA¶ÐÍÙwŒæ¯Øàºè°¿öÄ­cô³Îš#=ü«+ˆ7ð 5›öÐgÜF îÙ‘âé<‡E]s´ÕÖ%UµOq~cUy;X —ÿ¿Ž3Í¿À¬e›jý_…ôwZªN>γ—o²Ûkbà8Ã@†)OO¶lv•5?l€  ]îôb\7PBWôÎ1+®q‰æÊœ×è/ù%‡FéE—ž×G ‘žœ@÷Ý8’~ÿÿæÒjŒcÎëMmZ%Ó&nH„ÐÅu¬)FÓì d«ª-ZC”‘–ÈÚ±£ãÖXòОÑ{‡–¼~ÇÚÈ ´ÝÞšKSoº”æ-]O'Ø'É+O¢üÂ3ôÉ7?ÑAÖ’Sã躑ƒh÷ _ÿïbêܶ5]?zˆ– ÌÉèaßsÃHúeÛ>ÍD~ûXî3194õ]¿— ©ß3FþÑÁØ´ûAûÃ\ya?-ÜÇœn»Ì4*,.£¸’?ø«Ë¨k;X`ë“ÈO§ìÖ¬ÁäÐ#'4¡ÛPø²ŠjNïzñ šE¦¸¿ù‘- ÕtñÀîĽcÖžŠåáü"ú–-;X;F#fà­=ãv?c„ü~ôõì‘þ­W§.Œ‡/ÄÉ¢\ ë,oü[KÕtù­Í{“ð}ûã6Bã×øܱtôInºü|8I߬٦ ]XQP^x ´_á™2ÊË/¤q#4áæÔÁÊR×ËclIÚΚvWþÖc/H(s_ópÉ:®Gè¸þ´%—-LRÿîíè§­¹ül«f½‚–+¨‹•®ÜH›våQ›ã¸³ ‹ž¾ãmèŽÌõz­£p˜;™\Ö.ØFs{—sðëØóoÎá¼·Õò MÒçkÖ.Ô¯¹KÖÓF®_(ÖçõéD¸soà8·ì9L+7ì¢Ìëwl03£‡õⶨ§Èv½³A§wv*Ì1-ÏGNž¦¼ì-\ëZm[Ý`]úÓ»Æ6«U3ò ÕdŽÿ€xçÀ`PXõ¦Yà¾ÉML¼lAæõ:ã­ìŸ¹;ËøýÓSê \Ðh‚&O¥?üj ÅšlƼŠÚ¶NÕ,Úßÿ¡>Šr†sK%×À ÊPÄ ó>w÷XÖDãYÛ\Bÿøø;­âiøAö—ÿ|Mÿú|)uàŠÖ¿;&Í:Úë” —jÚlîáš šxùyÚýÜ@`V „í,ØÐ° · Ú¸ó 6†ä>–ŠIKèÝß~íEšöyÙ} Õþßž{Xëig¦'r9SÓè¡y ªª¶rcs-]v~ŸFÇ€³6úéâŸyL|õçÞ~Fj’Î HãáOsà ž?j¡™‹À»9Ìùf“‘wŽÊ䆮Fkì1 úˆA=yÙDèFЊ YhûP¾Äá‡èÂ: ‘Oœ=¢’òJ®+gÏ@] Í.‹]kÂ2“2‚¦etP›[W\™BÙ¿Ëñ ®Ãt§]ò5íš)4Ç4‚pÌ€º¼-÷{ÆL¬i¬™³ÓÖlüìáNb;ûMNˆãζ£î¹Ns/³Ðì/;¿·Ö Э=œiicîF*§1V¤ ³º;ý´u[À:Óå\ïp œ»v–ñþõ£Ú—ëG !Œ £ÛA£þäÛ_4­õ~ÄàõæN¸óíGˆëÚYåK›@¥ª· y£„·Åu0Îae^~n²u™c—3ϳÎ~ôØg3]‹Š]35ó$¬È TP)÷¤Ý &OTNP6kSÐô¶°Y9™+šè¹‹0è%còzÑè cŒëÙ»¯ëa>íÕ)‹n¾½ÛVÚ3aRíç²ö¸˜…*¨½Ëäö™©TÄ=i˜T;e·â†ä 6 &0¹:í¹QÔ¡MÝ5=¼Õ*ðúÅ+„ÙςГoj¼ù‘[¯ÔÌåÐ>þöÁ7šÖ9yÂh-Šs…šP‡ZþШ¦³…APŸ.Y´|ýNmòÚª»5áŠg»¢±U5MF¼‹°GN× /žy~VÏj<éoz–÷M<{æ{[VbÍ1ÎÉq° ý²}¿6Ùgß‘ºjx'@Í©+Î@|áZv!ø!àQ\'7Š÷w8¦Í͘ñÞWâ'GWŠïzwi« iÀ´+&>ºNúr/³ÛöaÓù*à!1tdµ94ÙºÈÏ¾Âø5:ˆ®ą̃‹«YãÆ3AÐpA ñŽÎL5kÀ †½^zp¢–g˜¦ÿ=gÅs¹‡À¹óíG¸Õµ"%ÿQþtÂt¶ïÙû,ËŸ›¼êVB×õCyzm×é’jµ±ÅPÏ:ÂðœIÚôÍ-£) _$=%žÇRŒšY½qAS…vùŽq^üŽ^ê–½ylVJtöÜÅûâŒñÖ3eUÚ„"ôZÑ»w§ñÜ£mlF¦ëŒ^˜·AùÌ‹¯,àYšýX›×·+ í`Óve¦;„°öÀåzß0i BWÆä !ŒÚK›}*~w=£ï)A³ŸBûF¸s…ÏbmÖÆ—°<à÷«/@ÿ↚É<ÎÜŸµ Ùä¨2q'–‚S@Ê]€yn*z¯ó„Nè΃ǴoåZ¶·ï;J¢ó„Ä!0¬±rÃnmøD ¸2æI]q}¦eO ã£ÐB!¤ÜiH¯ô ïâ|tm׆ÍÌê n×2Ë‚AÓ,a>êÎkµÎùÓÿøÌ=ÊïQ· %ð¼ AhWÐÞà™ æNºB'³¥Áç£'œBוo·8‡I]ÓÊÝ+ï%´¶[ÊŸ¼éH÷'Î#<:»U^Ú~IiØ¢c§x¢ÕJDÆ…TWYQu£_">;|4><땟üì_PøÑ ¯æq’ï~ÞΘJ5íoÏB ÆäACXè¢gŒYÌ´Œg»Ù„û3 6T(˜9·î9Ħ¨¤®ˆË“3´Z˜À0É|-ã%Mżä“D@ç÷íă23ÚGç÷ëÚ`”=:µÑÌ®XÒ„NzÙ®„™Ó¿pxäš/ò'Æt]ß;×5&{:~J›²eïa„íý\að ÖƒÄx³6‘ Zò^ríEòÛÇ®±„fÊ‚ºð8Lˆ_³ö“rÅFhôýDµe¬Å›˜½Ê':B¥lb~s xNÂ1žÈƒI<˜;píˆÎO ,CXj'LËx¨º+†~`↩ibŽ–ña=0:sbýw—•r“ÅÄI ©@[lŒ Ñ–ð4|Ìýøîçmõ^Eº(ƒèÌ6´ö½ÏæÆ¤K˜ŒÑyÄõ€îêÅáé ; ÒÚX§0ö‹rß­]žPèêšÖV‹²æ`ÕZñ¾ÐÆ øÁŽ®©Þ÷$þ|Çóî›?Sõ2.žÍü)ºyÓ¦Ø>t‚…î<ž3¿ñyŸþåú<œ¯¯½xèèûŸs´™ÃÄC¼ÿ¦1ÚŒ^Á;ÌD0ë–°&ëÚs·XíÚ,[ôhaŠE¯㔾´Þ‡yÆû  ?ük®Ö³Ë ÛÀŽJ›È<ú°© “·¦ÔšsÝÓÄ„+h|¹Æþ’A<1ªN@ÝÄãŘ”ô:CÃT!òNƒ§ôÞüUÚ«À3½1þë)aöõg‹×òLÌý<þÖIa&ÏÀ”†-f.c õÔ‰£™ïéyžñ Âwé{ÿmÌP„—gÿ#€ñÚÇn»Z›>ƒ×œCDYA™éÉC&®Ë±¸vPUWF±µæ“o¦Çþú_Â܇‹¸³Œ²µ'-Íæå|Ð1+eeãŸX€2ºó÷øQƒ]Ù×®Q¯äg‹¹ã‹™Ð£†öІuÄ‹îXáðܳµ™Ûñ WºåÊ è½«´•0¸™vup|ÓÈY—8/°Ï·¹ýѧÿ1°O÷ÞÍ©SVþf6þv®ßÍSlQWШd Ö}Ý*æk`¦;:Ó°¤<ýúçtë5X]4¡»’×£s÷ú“·7Zö‘W´ ®&u×¼VðØ-:¬µÃ…€w>r ¬ë`Ö5Ô©¹‡Ö¿ÿ——®çüiuéåYÊlÖÊ„%tÕóSÕQÍÉ»¿äID˜—ÿüvLoÞ á;/çv¿?‹ÖCØ’I6dáácì8ãçÚ{CU•u¼xIçsUœ¦ò1•@\¤ëè4ÅAãÏÑ06&p 4É` \¤ ¾j¸í¬ÝžæÉd0ßabÌÁc§´uÃ#ÂÖW+â’çæ!€uÝ }7Ob D]AÙu¸àCb Bå “$OóPM.OòÚÊ–¬™?WÙG^¸Hé6&pñé‡Zà‚PÖµ?¾ií"pÃSà)¶æåWgYɦy¢²Ùyv¡7î~ö^Ë‘‰6ÔË e Ǽ‰1LÌÿ‡kIæ"z>Ï¢þê·=Sª¯ÃÕà%¼LB’DÀ[`-º—ÈüÌKyàI kä1®{%¯¡V F]K\’!¦ò]Ml0Ð<ù¶‘ æa)tá-Äb³¾¯y¢RŒŸ™‚õº´öFqb_BsÙKÆ_ðÏE»r×øž‰¯5ú¶| 87p&€C’DÀŸ`Iuº®7²êo,>:"E6HÆ’ùq"Ç’È:.‚w–BסÍÏ,€ eQêB~·U¢U-ég3>>µâ¸x4pQÁuc36óý žLe*·_+žÉ³D@" D¿¨¯Ó«Îù?lãžòøä:¹ DÂRèºñÖç­JNÞe'õn›µ°#wWöV¥ý˳t žŸbP¼Ï&æ¹lb„{¶GŸ½HN¼(ω€D@"Т0Î[eª3D&ÙrúÞ*vž¸Õ9ì'RÍxÛ8´¸øÔV^q5È 3<øÜ”ÿÉ`7‘ÃM”ÀÂUöÊ,Ã(žN¯Ôª¨×U«—ómEŒ-댵ƒÎø€gYÖ·„2—Xwù7žiØÐþ¦ Ùw,¼kI’´4|©‹ßðÆ+Ø[š¤s#™õEg~#o± 8˜ŸòȹCçiX ]ÌP¶Û­ÿå™f7³/æûž™lùYQ¦k»ON-=Åû%Î!ÕÞK@5dAþÖvwáž5Ý„ÎE¹iâ™<U[sNX`Æöù¼_CžqxBœ¶,$œø•¼Hü€/u“¯à¤¥)‚“ã'‹›z­E>·Ñ|^uq4©6sv«M×#·³¡0 °6/—ês²xæ^Ëzw¬^™ÓËn·LÖMWU9³F<‡‚§á‡Î%¹­ö¤÷šGI.lg‡ëx÷8Ò¸xPOÍq6 Èæ¥ »xy ¼Û`çWÂódö’Ï6XˆÍ àNš*îŸÇ‚ó;Þè ý³Ù‰¶;ÛÁÞw°Ã¼iÁÿêzNSÄ /Bðv5‘yì¬O'mƒø ß²û0×U‹VO1û>Zhûþ¹¼¸ýkgv-VåµéZW;ñEXkº‰¶¾Ç¸­íÊÛOTÕéºï›;ýi¦~,¯Ùý€ËR^ÿÈs÷TíwÅPg×9MÌÙ¥Gӱ뼤úlØyPs࿆÷ÿ\Ä{{‚°¦\¸¬ƒ t'<ÇVxíØ=$6ìžñÞ—š™Ûé¡‚¶øÍ£6”ájmw—a¼Ùøª{hoÂÝ*•Ö°;H¸z!m,ÚÇ–€y«38È€Ó‚WÞý’°Yçv­YˆÖ}Z¸eüöÇ-ÚžŸ‡ØåÞüå¾a¦Ãf?óîGð~ëݱ“÷Æð¶.­ÝÊõ^ã2yí^܉~wþJ­cƒxRÃ.Cð3-tøÄZúú§§ÙµÔÐ’Oþ™ö¢ó‡0¸k¡;uêÞòB?ÕNö¿Ì˜õÇ2{MuŽ]±?ÉK‚Ö&§´êùÜ}ÖoÝ1úÕñ <`~¿l5Ƨ÷¥º¿í÷ðS‹mÇà2qk¶‚àËõ7ìÆKàÏÛì¹nµ÷o#õ ëØ#6µGï„Êž4â‹áýiÑ{Ÿ8æ<- 죻ã‰ç}z±a6)LkØé¢õ×½B‹†ÃÄ¿Óbs¤±…·hÃfyù§Ùဉ÷$=¨ÅƒgðôóÛ›/Óxý$‰@¤ àI]ÄÎ^¢.b‹=wêÊ–ørÆæ!Iì/}ïáÞÝ Î¾°;R+jï²»—{Ø–tºô Í[q±TË–Å’V±ðã˜vï>VšWX›—ÜóS­ËùÔåý÷;™;uêd½ôÒ앞Äã‰ÍÙ3ñZ,X¼Ï3¯Ü ÆÔ2×DzËIxÇÔ1«nûéÂt ‚€¹Ž»b,¶¬²®èȦfÐÖ”á¶.ŽŸƒØ[&nA‹…ý”}ÔÂO.¼Óôíš­9†×^䥼E["{¦GïpR‚w(lØ JKNb÷ƒ0n¯üÙÂÜ&I"ixR1|K(“˺;µá]¾Ű¿fxS‹6ªª9C³—ÝÅîdcØ ïipôØ-¹z+t rùa/tßwß}[øƒâV;¿2S÷áˆ1·~ò©; œûÆ©:ý<²[5¡Û¶4¯•:}ºN™î˜€U/‚(½Ù°ó]Ác²wåi㺠Á­Õ}ó€\Þa›×—² ÆØk6ga²“+aÏOl†€-ýÆZÞ×4@ƒ…æje3òÒµ94¼׳\×aŸßÿ,Z­íf3õŽýÇ4ßLjcUHó’ÁÝ5᦬BÜ®iËk‰@¤!àI]Ä~»®{îºç±!’ØH.J±KQË6öÙlÕ¬áN¦¢Çp”^o"í!²Z”ðY¦áòÑÂÚ¼ìÂgƒ—¼éÁ¿x.øvKÕ‰ï/ :åGnÑ po²UÅlØ0ë"ñ,ÚÏðq þϽ9GÛªïò <÷ºÔ†÷Ç}Ç^ÿø[Ë&f×Mß]q}`ÒZûŸ<ñÚÇÚ$§q#káöB¶Ûsj;±¸†Á5&„ŒãÝV^ÿx1ýñíùõ6à†é Âü÷ÿšGOþýSš»´¾P÷¸ä½D Üð¥.6•7âK‡õÒ6êøÓû_5õzÄ>‡À³ü^Ê+øÙ™‡ë.þ;‹…NÎûp»àOyôÚ;IiUjùõ¼\hsoq»T¯3¼ôÌd¸‹tІ±™ÿfgSq§(º×†}™ÿ»ÚGÞœœ;£r—!oó6 Æa›³AÂû¼EXBœ‰&]q6†ëIX˜™á¬½¹„ VÂI¼{X<ƒãxWS·û;‘v/wjþ.C‘öÏÅosëâ¹âr¦í¨ÄC9çÚ0Á=L¤ÜÛì5šÀ=pl¥“åK‡N£ úÜO í2Ä/ù´c—¿vŠóòËoÇg*öÊرòÄ*{é´_ëH÷ï.©½&Þ|s›ê[jMÌšÐåÁ?g_„®ó£¶” O„fcyõ4¬7i6&pÏš/ÇËŠü]"r<­OÞ0Š•Z"AàÎ]~¹ Ü‘ƒŸÖn¸ç7b„®¢VÌæíûrt¤õÂî#V8&TÞœ†1ÎÊZn9žo5Ú-öÅÜióøìÁƒÛÔðËòצÀ;ï´$í²©üÊç‰@x"  \ž¥¼ÿØ 'ƒ— z’.ê÷ó>œ/"FèN›¢ŽtÌZÆÌ¼Mb:lÖËÜ[/(ìPr /[Uû|’B·Iä~ahïN ?¿J$ !‹ISû®p¦8bàtqÿGœ÷á~Ñ©š÷Pj×Sâ^ ¡+I" H"êšúlÉí´ïèr'÷#üöÆz³”¡ÝŽøxÝKr1æeoð´êöñY§³Jh2ªut=Çówoâ ‡0ð•ÃþŒ‹Î”S1¯SÅÒ_ Ó×cرD {²IOŽg‡í4Ÿ®¾ÆëïðyÇ )gÿQ*dï<ð1[S›÷ú«„›Ÿj¤ä¿ù9“!šB ÚËT¤´'E9ôù²;Ù'ü ç'½tèór<ÿ1Bî-+Áïðòu9t¦¼š×Ÿ©ÇkÓbkJ)ÆVƶ·e®FCb©‚•/X¹‰’ÙÍâ^¿;fXïz›x¿·á°äy_ÂÎ4àk :=)z…tŒ„¿ˆ—–‘jSÉj·iùOJˆÕƒ„:ÿþÊŸŒ§h.S®íɲõ¨SÜžp£hÐU’Îp†kTwº:Äš¥’‰}%s}Š­­O&®OÞµ'˜ca¢Å Ý})= /8¶Ž“tÜ´^’3©]ZßÙGŠÂÿÙ@å€+7hµŸ|ý#.¯¢ö¥y4¤h7u(=@&«*‡{âÕå%v¦Ü´žôŲjZñKÝÆ¾˜¡ý±E0)‡ý7È,@«5³gtöPד¼cÀØ@!ËÊòÊÎÿZ¾v'Ý>vxHò°LFqÄÑZ¦\Û“¿YÃV2î´ÇæRëô-|ÞÍžÝêܹú³xØífª¬ìÉ®fjíÉrî<ß>ÖóödkîgôÍÏÏòŠOÇ¡9&™&^ú.µÏ8ߟl=®/tËb,*)¿ðÌçá¬Ðè««lc僎´‡ BØbǸJ„Ö™VUHãòVPiš Ì»ŸÞ¥Çã³iMûKé­Ï—Ò„1Ã4Í®å‚AKXØÏ]ºž—(é)“ýÊ˯2z<ûŠÆÓýé’ Îÿ2οc3‡`å?G[ÑZ¦ÜÛ“ã j“±ˆÌæC/æññ[´£ªª#.Çõ©ªÉöBvņôËÎYN“ãÛÑÍ—}ÈC`]¿EêE‹ºø0Šª,`-w8®íª &æ°º¢7 óîÂU[x³†\·„ µ»f€ÿ`„ü {>§.gÜìtùù}¹W̆Ýi›ÀyŸ³d½¶Wnk·ìL'$AŸÁ».—i€`ä?$má‰Fk™j¨=‰Ë¡ôôy\§|Ÿ ÒÜb!Ÿ™9“ 'œ³=)«È§ù«~KGN®w&‘™Önóî ·vþÉÁQ]BŒ£,taáê½w3¹Ü‡üRTŒßnÝ›G‹~pÜË~+°þØ¿vo"ž3øõ7!N˜ÿàS9Îl¢V©¡¸"oøàüÌ_¾) ùiʳÿˆÖ2ÕP{ÛºÕg!¸â‹B؃ðÒP{rðøjzï««ë Üîí¯¤Û¯šÓb.°ˆ ¡;ô‹ã;Y9Û‹ sÛžPr¨üR\‡ AAà–•WÒœïÖj&eh¸áBà%µ²ˆ>ýæ'£© ÈÖa˜üᤠ7œüÙ‡ô'‹ XþÃ)¿-…—h-S®íÉìï×r*Ð4Üpù®Ð¶læþd±hOl´zëëôé’Û¨¢Ê1ÝFQô4zÈs4qô;\÷âÂ…u¿ðBWCJ¥1Õ®béPX*LÊÕ55´býN:ÃBí"à …I¹1@ÀËŇ—S1oé·lý_ðí/B\˜¥\Ì3”±‡n¨LÊåü¤&Åi{"ÿ¥+÷h-SîíIIE ¥¦|R ×ý+BãMKYÄõ©š¾ûy}òÝôÃæ×ø5‡-!.“~}ågtaß܃¶ˆû¨º¼Ï†‹‰Ù>ŽM0!-¬+70aÒT ÜJÞþgÞƒ³”ƒ1iªŽ Ï®Àxûaãn_ð þ}%Á2îp`–r°&M5—oðþVmÚã×ü7—ù~ÓDk™ùíÉOÛ÷i³”ƒ1iªé¯Rÿ ð„ÔK×o¦}GV³õL³BR§6Ó=c¿øÊõs[ÿ.j„î¸?ò Í-$ËŠ¶›®ÏZŠàß9z¥VMËݸ€Êª­Ô—…+·RÖÄ=ÉÚ®Õ/ff`pã+åeQñ±1ášu/ðWÆÚ¾?óÖŽP梵L¹·'åUVÞŽsKØ~Eðf³%PuU¶&t/ê÷0ÝrÅÇ<‡"=lyöcQ#t•Ù³m<Ù¹›3К˜½R‹…ªªªi÷¡|ÍñÖá†+78çØyàÕ0ß¾j»ƒœýG4ÇX‡Îþ0qÛ_ùç¼F*oÑZ¦D¾Q/í ;¾À:Üp%7æÑbíKã/þ7 ï÷æÜ´xŠ¡‹/Éß·n\WUCêÒÄQIl<Ê¢™–O³ˆXkUÀ_ø£$c/x„+Fðmã å‘oI`PÈËrôìi*Ž/¼åÑ5øŸþÊ¿kÜòÚ?Dk™ùvmOôºŠ€9¾ðÇ×Â:^½RNí[§¶­.ô¹=ñOÁˆ#ª„®9¶õ÷lb¾m¾®]Û`€ÜP0Aha†eUu5•VTQœ¥¬¡WÃê7ðx†gYƒoð|xKø‘Ö…Ûì©F2>ý•ÿF’?û€@´–)‘o×öD§?ã’Á ª7”Pyµê—ö$8ûžJT ݾ³sÊØ:ºRÀÆzæµâ:Øg»}ýÖ Ýêê6±ØüæK9y¿çš«VIÀ?òá- ª9¾p×rEÁ§¿ò/â”gÿ!­eJäBW´':§~á?|ýxŒ¶úUBW+0 9ÇuÙ2 —A'˜‚„9’0ÛÐáì ð’+m"•0/#/Í%W ¼ ßÜôüù¾?òïO~d\¢µL¹æ;ZÛ“HªQ'tÑ)tùC]*ïTÂÁ…uº‘4ƒ•|û˼qB×Où¤†"Rxõ*ÚÊ”Èw4·'‘RF£Nè]x$—?Î| Ö7ãϬì%z¦P1ØÎ¬ùºb°¹®KqÓøÿ"/uO=»áG$u8;äß3”ä[ÍA ZË”k¾£µ=iN9 õ»Q't8»ì¯ÓvUmס gEá&½TÜG AØâyö…o>‚rîøD~ÊÄ|ïb4ZËT]¾£·=‰”b•B—ç¸]5$ãº( Ί«3ÇroCï—r !)x÷5BÄLòÇIþÌ0ó-i»LùW”)Q'ƒ™ÿc/£óm}½?òîo¬8¢RèÚ³Û­b/¥™?x×-ã³{ p¥cHJ¡A¬¢¸®½QrO"û¶‡iÈì ”Ðk3™Ô‹¯¢¾o. A®¦ï/£.Oý• € )é4è£Õdn™ûSöëÚŽ¸iL½mGëMÓîGí3ÓœùzÞ’pê„KéÞëGi?ÅÇ™è©;®å]„Å+ò帗©„83ÝqÝzô–+ë!3rHO­œ¡¬áxö.G¿}pÏtß £ë½I7G¿«m2 xîœ=’»y+]~ÞÅOµg….ð(=8a-=qËštÙ´ßu¿•î¹î;¾f2’E *…î°Y,,m¿¨XlÖ :ÊðGO´Í„û¨tçfªØ·“ô IÔmÚ”:ür‘%çÙVz† ü‡¶N¾’r_~ˆ’^H­/ŸHÖâB:µlµýõCÎw½½ð&?Þ„üaOßQC{ÒšÍ{5>‘o¹êêÝ9K¼rÖùn(]ÝL–³;ÇÍ{òèÒ¡µ–³Bxþƒ/yñ<ùfSøòÜËTÛÖ©tÏøKê•‘>¶z>‘Z_uå¾ô P—H¶ÊrÚ÷êï¨Ïß>«YÉÖŸ÷´–Ó§H—àÐî |@ýÿý5™;t£ª<Ì/óœêx‡qÈ;q47t¿.mÉÀ¦òm¼ÿ.Èl4°ÿjÍY²ž&ßèÐd]ãlÛÑà†pKîêœÕÚùè—m¹ôЯ® Ö©Itòt‰ó÷¦.N¡·ç­ ¸øx2™Íd0Ù¹‡oýמÝ;õþß7ÿÔ„¨¤*öpöÊ{‹|Ê;æFX9žêª*:ûëÄÄx¾m¶{™ÊHK¢_¶ï×t¶ úÕ·År'¯¨¤Œ¯c:Á4Ò_—s€.ضqYk.‰ú€ssÉ5¬Áixÿ‡¸>Ívn­×*µ}üý-tÕù/ÕcÅ“LCzÜIs—ßCó×Ô{fg½aç{×ô}ÿ¼zÏšºqå¿©w#ý¹o-EçÞû5AjK·:bý¤.É‘’sÛN¤ŒTqÀáWµæÄQ:ðÚ3¤VW5˜…˜Ìv”ráêøÀˆgmQÑrGãf), hÂq{4.\lžD,$EãV_,_ïXzåÆ4„áu— ¦¥k[(å^@IDATº>.áM*«-”É ¬¤èFÀ½LmÚ}ˆ…î¾=¥Åš”™šLãF¦Á=;j»O ôò ‹)=™Ç9õþ™?!â ô9#¥שg2_®~”ò ·rþÎßpÑ&­ô샜;½7°&˜¯*Êz|g–¼F¥²üêHùæ¦6íÉz¦ˆlž¹L¿äZêüØ J}ZòYÎ:³ZuèÁÞ]É{NiQéËÌ ØÃ­Íí>ý}º÷~â7¾õ{áûR—”–Ñ» ×Pa àóŒ<-Sˆmõfm™¿qJb<ý–…n¯NY´‡;°E%(™Ëè©ÓÚ\Mí>œÿ%Å;ê ï™ÝéÞ˜DgÐÌËUE´;ï[šrý ÊHëC'ŠvpþÑ.©”—M§Šëpj*Þhzµš.>²Þh¬DRéZuÒ¤ˆ° Õ ˜´Œf•SÕj¡ŠÜ*Ùò% 8ß6&=ƒ_$¶„xs“,wÎnEƒžn½úBš:ñR6vЮë¸o"ÇSÊš°¤èFÀÓ2厬(5«ÓZ‚Ï ÒŠÈ)S¥Ž|b\™Ø=ŸâÞ!H2ⵟª­ŽÎ«QçÈwBÚ%…Ê*óEyvC ª…îàùG7³Íñ00áiM©›ª~á†OXÞV;¤-ùÑÅ: þ¹˜„IcÀú¸mæròБ<\×…©ºšã‹$*äI,éÉMç}Ã΃ôÚ;µ<1ÚÇëçÌn›ªOך?Ê‹¨CÀÓ2`Îëәͧñ”Ì’QC{Q Oä;Ä&gʨèL…vŽ„k•±àMã±Ø¦¨ x•T£QƒŸa :Ñð¾r§5ŸŽžÚ¨MK쬋J6UÔ>jó2¾z­‰ù·¸æMê`b^‰ëp¦ªc šk|—^Tš³áœ¬ÆóšÝ¶·=¢ éšG¨hÍbÊŸó¶ƘžI†¤Tª<¸÷œq„ÛÃ…%Ô§s¶6F+&S5Ä£'Ùx'AV¾Ç¦HÕ¼ (‰µÜ8Þ˜>Ÿã“ÝxZ¦€R÷Žm4a‹1ÝCù…´hÕ&Êã3(‹gÊcè;pEœÞÁ û5ɲ•÷Óž·b ½è¯4õ†UtøÄZZ¼ö9g¸6iýy¾Å~ÎÓ:/Fñ…ºzý"ÕfÓ„.©öñ\ž ÷ò`++¡ß|J™7Ü]OèV¡“†Öc?ïß/QÿM×}âU›ï¦â_–Qe^d Ýí<)êâAÝi`÷öÚZ[‘áÓ¥åç\v²býNÂ!hø€î´ûÐñf-aå¹e!ÐX™úqk.áp¥¿ùI»5òжä,çóò¢eëvˆŸ"æ¼fÛßéÖË?£5ÛþAå•'|¿ÿÕ5ÎkqYÍï.º’‡n̼b N¸Â3Õy½'ÓòŽù"â}y®@T›—ERÇøåìÌI˜àõmÝ#Å;UþœwÈ”‘Í“£º×ÿ¢Ü¹ \Cr% NG?úg#!Â÷g¬‡\ÂK€ôèà5“q±&êÒ®57uBØëÈdÀˆGÀ›2å*pœ³œ,.ÕfÊG ÇNn¦çkN/<åÝUàjùï8–N•ìáxxET¾õšn÷7r«×ÍÀ ß”‹j‡‰Ù±?„)Aˆæ<ª±ì‡Xr”óÐõ^… ‡@{óò ‡·TQYMÿš½ÌÛà2\ DÀ×2µ}ßQžÁ|4b‘ùæ§g|â}û/‡¤s#õš.àQH·Ð “ÃÄì¼ Ôï×J%pñÖñ®xˆˆÃëBPðŽ³¤ðA@|—ðáÈsNïÞ”)×°‘X$]ù÷±È|S ]þnº8úš *Ï£ÒÖë^´óƶX?0j¨RÁ‹` {z wFž@âJ åÇõyC×®a06f÷Æw]Cø7ðéü˜Í¨Œ>ZË”k¾Å‡G{bWÿ=QUSÔÕ§ú­§øbQv2;ÿ$·¥?kÙVI_Yc ºÝÕÌ‚§‚·É wæÿ.gŽçÄv›Öç ÷ìk|ú;ÿaŸéd0ÚËÚ»59쿜͖ä÷ö$Ü3-…níbMw®øXvE½Y\òŒª8ÌUòlÀjCøú,oà1Þdtòí+>Èo³gc×rá®í‚?ðéÏüûŠŸ 6ÑZ¦D[‚3ڛǮÖN+ÎF)ô¿€7›=.êꓺµeϤ7ÌæÑ9^ÅÉÿTº,&f­’`D™mAé &N\¡¼ÚÅ塯gsÞÀcvZÿ¯ë0œý¦g¿ˆF¢c›4`N•Uukj=‹!¸o?ðé¯ü—ûèH-ZË”È7ꥳ=Qv‹Ù3l?ªcgé)ñFŠÑ©”›¾•¼™Ù²œž§ñÍj¯5^W ²Ò)6ÆÀëÃ[è‚?kþÈED>öh-S®ùvmOŒz•=M ôÉào¼ýpÔÕ')t]Ê—¢S>·<Âø+qˆ3z£Øþ ü ÊL0ÒáÄtœwò7Oà­cF’Ưàùð–\1èÓ¡U±÷(á-ÊÛ8|¿N­ü–ÿ@ñÍñFk™rÍ·hOÚpG¾²’÷Ê®êvE<·N­ýמ„]&aÈû³‘#úg£y6koµ3zÔÑ[nÌ„÷n¿“è™:®7ÛŽáÃHíRÍ£ØiuûÑduÛÇÒïL4#Bð²†y‚–Û#+Yãð/òÒŒè´WE8Á€.Y¬íê5ŸµpÕN~ŠÎ”s¯\ñ[þÃ)-…—h-SîùvmOŒ:;žÇó%Œaó™ÁKód2¨QYŸ¤Ðu)ŠÃæ:Îc–?h?ñ,fK2Ñå±_/u:EÛƒÂÖÄ[ΙXðÆšMÔ>Ù@§Íé´¢Ãå~MÏ—ÈÀKóÔ+‹ÍÀÌ#øߨCùð–\1ˆãÉTçuËd·zV**vì\âm¼þWÄ^†à}¨wÞÆÏù÷7Ÿ2>^þçR¯¢©L¹æÛµ=éd ‹%ƒ 6ZÖìb^j˜§Þm’¢²>I¡ëVd¸×ø¹øIUTW3ô/•g¯–WY,>ëbÂáe6™ÈÌ{²b_ÖŒ¤Xj§Òþ”n´¤Ó5!Õx¡á‚ðÒ)5†Ú¶âéýÌ#øßÐR‘oɃNYiÔ§m2UTUón@eÜ;÷6fÿ„CúØ•¨‚'P"ÿ‚˪‹jµZ+ù^+c.gñJK;×˧Õf­ôGHÑZ¦ÜóíÞž”Wô¥“'RîÉS¿"ðÈú„r€:e·«è½‹²†ŸÃ‚¢Þ ¤ûW0Ô9<|÷OþT¼Ý¹zÉú ³Xvl8É/Û-¶ÅgÊñ!½Wñ˜íØZF£Q¶ññqTͦººšÚñBª½RvÅæºøðJÊò`ƒi÷¼ør1\˜¹¡u·OÖS7î•&0àð-ÌËÞ¦Óý;e°u+í)('k!ö)g+@ðMcÃ…In ò/p+.)Wkª«‹Å½Ë嬥ÑYyª©ª.öGPÑZ¦Ê·{{RPÙ‡,­(-åK2›µ\a fnhÝ®OÈê”Íj­Û¹Á!|ƒšçÆ“B× ™_œXw]æ þF—ñòb©º‰_yK¼f©®>VZY­@‹cS£·ä¨$:Þ‹“5]Ž'!>žn ›],,tlÔ–úÇ”TQ>¥Ò¢î©}iu+ÚMJÉZím²ç ‡u¸X„Yʘ4…ñån):jÇnbbÅ'Äk|‚_ð™’ȇ·Ô};¶æu† íÊ/§ÞvÏÌæìøX˜ßcøƒxŸ^S|b.–a–2&MY‰ï–¸üƒ”#”§ªŠr×¢)V[ÔsäÝu  Dk™j,ßîíÉñÊV”â^®K¹\—·pz7[êv ògÁÂ:\, Â,eLšÂør ëøu mµ?ó㯸¤ÐmI^¹ó9ϦºL{¤Òí|BWÍ?|hMRzú£ÛrÒýº4ÚóŸ`„$ŒçBƒ´°Àµñ>œØñ„ç‰1Ut¢ÂJÇÛj‚µoеTQœµ”blŽ}a=O±á7kôFª0$R¥Ṉ̃+dä¹d™±*µK1S" ÚääDJJâ‚—ù¿àüùJaЙ1H1iÿÉÊ/«¡B‚·:³¾y-"ŸýEvvxa³©lz³këp ŠÊù'ο)àùG9â=•¼=»¶r~ ŠÃ_Ù ×xD>Uä½m箿òGBf£µL5–ï†Ú“ÓÕédU7®SÐ,*Ho8Ãgÿ,ÙƒûI{ò³s¬Ã5(¶ µ'øþ¢N¡­æÛ³,+x'”$…nè›ãŒs*+,ÿTI5qƒxþÚqmzŸ¿(ÿ¿ª®üꋜî®ËÙŸÆB×'µËÙ;e!¦y;bËÊ–FŽ Äæg&ZUQ‹•ʪù°(Tcˆ¡ 5ʹ@Ÿ‹°¿® Sf;qyÖÏÈPâ¹'šlÒSb,Æ™ÙóT|¬¦áBà¦$'k×0-cv¤¯Z®`¢) bxFsûòJ:]VE§+­TͦgŒ¨Ûë¶1Qy}†eAŽuÒŽü‚–.GªÝf+Ù¾þç}œô¶PÄáužÂ< ÈÎväý‚˯>ÃX$ùZ§ïh-SMåK‰lOX#µÚ¸ÃÝD{âi™ª«O¢=‰ Z}¨SLEh«ùV”5OÙø{Rè6qßÙGŠÖÍĦš;HÖ©îæëßóh;yìègÛå·Ø ¬{‡6ü“÷$„«‰{‡j<¢G£§zÍ„kâIK••UÚXo Þt–6è¹ò$í].\Ú¹¡‡¿øÔùsû{ÏÞ¶ •„™àC¯Ã3kÞœ&„«6†Ëš.4\ͼìÔr}›@ådªö bc«(Ç»-Œ­¸§åzêü£ülÏ=¢äåîþžùB7Bø¨âpe¹%\‹|á,òkì$æ&Ô)€­eÊ“|{Ûž4UøB]ŸÀŸ¨Sh£ùå˵¼á•“º|E¯{_µÙ4¡Ë‚íö)C³ÿ0kÃ1­‘X9îì›xøWŸ|ûsê³wÓÅy«„ŠaBÁuÜ×.%âñÓ*ºUµ“Œl¼9ï¹.â*6Öi©) ;?éAs…É|ˆÙÔ0%c¬9..V;`VÆsðço Mñªü×XlÄåÇn·ZJ×|½h ói­=„ ª]/ÞT"ò9ò&ò©åtèÚý Æ$Ñu ¨D[™%!TùFú¡ªOH[Ô)ž¶|m4ÿ$ÊXXÕ%)tñµ !C¦~·aýÿ;ÊŠd[–n™w´¶^=‹h¿j-.>U¶cÃÚ¿+ç]øâ‹V«÷MU'݈«©ŸDA…@Ó®Yðâëíâbc5«M²ª©Ñ&Y±9²žÐmLÙ-ީޭ[Õß­°VÉuV¸¢„ù ¦c¤ Á m“¦ lñ;ž£BƒGS 0hŒO‘‘n°órsìd1møaÅ+*ʰ\j8 „ÐXˆ:_¶(ùB‘W-ßÀXè.½â~Ô) &¾­¿ëânˆB]¦OÁÎ7Ò ‡¼‹:…¶m4³–uI ]QRÝÎÊôéöuã2ÿÃw™ôÊ|úŽ4Õ?~÷ÕÏIi©òõ4Ô;ÆP|ÑxQQÄ6±œ&Õ^ÂâÐrmš¦ËãÍš¦{.m·ÈT't3Û´fVëÒÔž<MiBÛÅ\4T´FMûuÌT_¤@`p.~C‘ôÆÑ8¬ßy@ÉݾeÑæWmc1](_h,ªwÎüø“7!pE¾«EjëŒ/ùÙxÔ)0 e ùt§`ç釢>!]×:uxßîÐ6óÏ(WaY—¤ÐÅWk„ ªñÿXÜMÃcž5|í[#ÓR\U­sì+¿ýô£yWNú5‘Þr¼ð´zËUêü1Ƌ«y˜ä¥9š°es2f6ã€f+f$žKè&cÍK-¥§¥ŠKçé€Ú«c‚WÏÂÂW»Æ† ÒnŒ¸]ˆôü[ÔõnƒŒ7Á¤ wïöÍ_®X0w)3¤•%>‹r…†Z`KºÈ#òêšÿØå æ,åî$ æXÕ)ŽËY†[Z™BÞÎEÁªKà!Øõ iºÖ©C{v~òÝìçñÏa]—¤ÐÅ—k„ydïúë2V³ÁrÎ88Ñð+~õm>ðQËù0óGž?ä’Ñ'ì¼ûo~›Ü¯[;õ¼¾]”þÝÚz½Ž…F›qÈZ'&N‰q\Z!lÅ™y9‹ây­« L†r'QIpÒ«Äoîá‚q´ýÁ¹xE ‘Oœý¬Ą̈Ĥ)›ÕR¶nÅ’9Û~ùq;'-ÊLa(O¸Ç:0$˜a[*!oÈ#ò*0ÀƯXønäÎÈòÂüã'Ï}ùM\§’üQ§8^í;·„2…¼4‡‚Q—ÀO0êÒq¯S˜±éÇUïoüaÅü8ìë’ºøŠç ª¼Ïºå¼Cš‰ù-¾DïD ìµ»6¬Ï½ðªk¯´Ù¬£¹qMâ¨&ÆšÔ”äxÅlt™Ñļ%wë~ïïh^#èßsWŠËzgQQÄî÷â÷p9»çÙý¾¹|ºç×ý¾¹ñ‰÷áÖ^–àôyÔ„íþ;VÿüÝ×kªª**ø=(C¥µg\£\ M7„®Ðtu‰ó6I‡NÉÞ-›÷_xåµsGeשDþ6~¯Sœ–³‹kP¸–)wþûïžO÷{oRr¯?î÷ÞÄ)œU§lÖÒƒ»w®øyñ×ßñœ€"~/"ê’ºâ‹6r6ŵú¼²âÖìò‚µÿʱƒG}uãpÐL`ZÆÔetZöÅç_òõÒîýuîØ½gïØ„ÄTSll’Á`ôÞuGè-Q¨³»iûîâZž€ÕÊþp**K+ÊJŠíÙ½'7gËaNU˜T¡ÝAМ©=JøŒò„q¨–>žËYÔHŒë"Ïõê’xÎÛŠ…s–­XH+»õؾcž=â’RLq±‰¡ªSµ¼ÉSÐêTeeIeYééC{wïÜ»m3Ú´ˆ«KRè6QxúÎÎ)c·Ÿ±±ï¼GÊ|º ˜Bà uR4". »ùØÏÏ´^{í»â=¾ ýnlÆŸEJŸ¾õڿŵ< h«(βÁ×Ðf!dÐ+‡Ð…Ïe\£ñˆ-—³©°F뿳³‘;-ûø@#‹wƒ^—8MI¡E ÅÔ%)t=(HFþM‹Íª ]ö»yÕU­ž¹øÔi**¿k‚ž;;tŒOñYÓ„ùŒÆ$Â8î‚÷ ½¤à €òWŒ_¢lˆž94]h¸¸Ñ¦år–5à"êE$Ô¥Zöå)´¸º$…®¥fÐÂc›ÖÍXÃ_ÿb÷ˆ‰‹ÑMæ`/×E¡@ãI!h< É@èj=t>c!xEÃÂ?ÐI<D¹€À…ÉåeBBBWŒí¢Üà9ÊQ´‘¨;È·À,ÜëR´}£PçW”‹Q—¤Ðõ°8ñ„€7Xà^¬½®ÒÔõS†Î6kQ DC†TÌÄŒá놄n(„oÔîb£}³àýCy‰r! ˜ŽE§ ‚÷ø]ŒãаüSÔÀ)’êRÔ|œgTÔQFZD]’B×ÃR¥f·›§;|ŒWëdãÐ=2‘ƒ~ʇk‚¬\®û…° …Àådy—@IÁBeBh(\ ”ZœqàY4j¸œm'+à)uÉɸ¼8¢‰2ñuI ]Ë ´Z61ÿ›_AxÑÃ|‚ÐEƒ) h,hP…v a+n¨Çt ™IÁAå$Ê q ŒˆkñïJªÃ ¸„s]’ß*x´¸º$…n3 ïz7«Ú®¼€q]vÑúqÙC†-:¶‘¯E/L4Ð` dqíVh¸âÌ‚J˜´#)8ˆ†gq lˆCün"+`ƒNI8×¥ÈB4²¹Ey‰:ƒ³¨G8‹ßñNDºÍøLýž(X76ósr»Ìn…¶{·K¢ 0€„Àu܅δŒô1Ž()x ,åBÜËsÓ̱.5ͽ|ß´¨º$…ns‹O¨b—5¡«(¿Þ|]»}yäh#ш†£‘ÇAýÚƒ$‰@¤"Nu)R1”|‡bœ1 X‰ Îû2-¯Õ] naf¶(–'#ƒsÉ¥D@" „)t½ø:E÷Ц¨4…Çv[‰{y–H$‰@cH¡Û2çø}È¢üox´v^am7ŽTÛ£çx]>’H$‰€†€º^„:m—EïC»Æ÷Lô:*P" H¢)t½üÌÆÝ?µÝÝÎÎ2RÊlg~ëeT2˜D@" D Rèzù¡•éÓí<¶û'\UÔÇÜÕÉ,îåY" H$îH¡ëŽH3îÕ¬¶ÿeŸÌyZUÍ,Q"ω€D ÊB×Ï@§y’·þ+C´<©ªÿ†õ;¥Ã ?c,£“H$‘Š€º~þrC>¦îEg´ªúG¹„ȉ†¼H$Q€ºøüjvÛ×yFó.DÍ“ª’«Uús’‘QJ$‰@„! …n>fèTzصîXw]›ÑÎ{y!H$Q‰€ºúìC¾*X“ªf#zÖvÙS¤úÞ–ßdÆ(9­D@" DRèò#™Ì²¼-Ò’PÕΖbõ/LNÆ-H$်ü>Ãæ:®èê™™ïßx]æeLRF-H$aŒ€ºþ8Ã|Ì“ª¾@203ÛUzw×øž‰NVF/H$aˆ€tÜ„b4Òý‹r ûdnÅkw;–Ú‹_çdïmnÒü~ÆP›Íz5)JUQ³y øœÂ{QéQgS^h»ÂyãvÁREUŽñχô:ýâýïsÜ^‘·‰€D@"à¤ÐõˆME1ð‹‚ëǶù-뺟kïªê=<›yåy_æÐDXåñÇ_3—˜ËÖëè1«ÝšÅŽ7T³¥Üo-Wbl5[*²JŽj,­}Œ½Ü¯VãtœÆËSžñ¸]UþÞ.¦ÝÓ§ß]ÕX8ù»D@" 4)t›‡—×oû*öº±™±à½‘°ìü×–±™~U°½HY‰%Ý]O½pU¹±ôm¢Ën_š§v-ÚMJ(&kµ¾0¾ü¤ ïjƒ‰ò;Ó¾´žmò;¼z¬&ï±{Ÿ~ﻚ¾˜#g…X’D@" ø‚€Çš’/‰È°bÒè~6 çàŽMÍq5Š:'gRß7|ðM ÷<û‡'Œ1¦E©ÕE™ãöÎ¥«÷-PºŸÞE,pÝ^÷ß-âFH i¦TÎÔé _N~þÅÇ9YVüµŒI" ˆRdCÄ?ðÂrEÑß$|3³îس¢òÔ;µ,@»…k¼ëéžÐë¯v-ÎUnØ3GŸUŽáÖàÒ¼qïl}×â½\F”¿Ý7í¼à|J’H$/B× Ð| 2lѱ]:U™ìŒCUµöºLl ˆoa¼õ¡ß]c4š^a«^vðÅ`·8_ öÒàEQt¯Þùä´«kù”‚7ØC¦'´¤Ð ÁgdoUŸ²¾ø–3i»úò’+[ßÞ¯ßɉ‰‰ÿJ«:¥ŽÊc‡VaBà%µ²P5Ř޾üòI0‡Ër&ßF²!D²ñ Ñ÷Š‹mý¯ß]ÆëvÉ¢ªŠA§Ìºü⾯ª:}›‹¯Ô‡RÃu‡¼Œ8²B¯*º¬¶{À§4&àɲ㔼—H$M Î& Ôã¾³sjJ ±m<±Êj'ª²ÚclI­oïÀ³”C1†ÛT>Áx‹1Å<ÄïÆð!Çw›M>—H$nH¡ëH0o/°tG¹ý†jU=^›AU1 :^6few,4Þ]æ wN=ŸŸIm× y/Hš@@ Ý& àcWý}+Nž\qÌr瑤Ž5¼vëp˜¤oQƒ7fZMHI¾‚c’Ú®opÊЉ@" …nè>º&t9yã+ÛÏÜŸÔym¬¥R ä:\_³ ÞÌ– »¢Ówß|H³¯ Êð‰@T! …nè>7°‡Ð‚Æh®2%èc-e¡ãÆÃ”ã,e:E¯dÕò þeò;ùšD@"  fèʰǸ(„®I¯èLvKØŽç ˜À£ŽtX6¾å¸®Fž%‰€H¡ëHxÂU˜—5Á«ª‘ãé‰]XŠƒ0/‡}g!ßPF)Hš€ºÍ†Ìo€=„¼j .WÁ»,C~+2"‰€D ¥# ÌÐ|a¡é mWaš®àÛ5¡AR¦*H")tC÷±\V$~WþC‡¢LY" D‘ØØG¼M²4Á¥bH1`•ßHðî·eD‰€D ¥# …nØ|áæé’RhЫ(®koGx¿ÀìÛ¦!³7PB¯AÎ\ÅvêA=^|‡ÿw |o)uzìeÒ™bÉ’Nƒ>ZMæö]ïÊ ‰€D@" ,Rè߯b‡–èµ™p•îÜLûv’>!‰ºM{ƒR‡_~Vœ±m»Påá}´ã‰I”ûÊ#”2l¥¼†¬Å…tjÙjûk¸Rö™|ÎÏÈ$‰@ …nè>’0ÏŠ³Çœèã©õU7щ…ÿÑÂèãÉVYNû^ýÝYq­ù–¿=ƒªŽ¤²]›©<7‡zÖÞ+Xð%IæÝÎ wÎË›ß8K’H$B×ÂísÛNÚølÅÝk5'ŽÒמ!µºêœ¬B#ŽëÔ“ÊwoÑÞ³­ô ÅuìqÎpò¡D@" ø)týƒcPc1µiOÖ3Ed«hžÛÈŽ÷ÿ,E'èÔÒN~«Žç‘)«ó^^H$‰@à€7$I†€©UÕ°–Újû›G)¾{?Úýü]¤Z-Πˆ'†ã“$H$G@jºÇØï)Ô°¶“–áq¼™ãï V—^O{_~ˆjNå× “žAˆO’D@" )t±ßS¨:vH[ò£‹o2îVcn ¶·?BÇgÏ"½9Žâ{ ЦêjŽO’D@" i^<Æ~O¡êØAÍDߥ•æl8gü­¯¾™·VP¨Ý=OÕ{o㤡dLÏ$CR*UÜ[‘H$À …n`p h¬¶²:ñͧ”yÃÝõ„nuÁ‚0u¥OÿÚõ¶Þu›ï¦â_–Qežºõ€‘7‰€D @Hór€€ t´ùsÞ!SF6ÅvêîUR†ä4J4œŽ~ôO¯ÂË@‰€D@"Ð|¤¦Û|ÌÂ"– å<:Ák^°ä(ç¡ë½/J$‰@óšnó1óW•#r=üoàãQêñ–H$ …nhÊ«À²1 ¶më~ʵYªÏìKë‰gaIàÍn­9½~ÕÒÌ øvÍGXò,™’H$á„€º¡ûvNZ¸|¶â8¼ÿÀâ¼ÄÊñøìÐqÕHÊàIãíðalQ~ïȇ$‰€D@" ð)t=)¯ ‚ ¬Çš¯~g«©.^Ýn´Ýª3 Yï¢/àÉn©>ýâùókù‚WäÅ»Èe(‰€D@"EH¡º QÜj¾®®¨(-ÛôÓœŽM§•.33xOÛÖýò¯ÒÒblâ ~ÑQÿRÓe$I$OhÑB—¥V) ž‚w„y«ŠJ›V¯Ü´oÇÖùûRº)K;]£†RãEÚà¼Ú³óӵ˿ÿ¥–O𠾡©K¡Ë H’HZ8ª’HªRâk.[´HEUŽ1@õwð1ÿ…‡& ¡…å!pËù0óaZöÅìoyŠ’ŽúÚœ¢^|x¥.«Y a &eh¸vå|¾dî§0+CËŸà|ƒÿ°ÑÈ™I‰€D PdqÄë}\ïká~èÈ1HQÇÍ•o¬_ºÚY¸,8Ð Â7Ñصã°Íj=šÜ±{½Ì'ã³T•÷èK°–’ÁYçÂ:ÜÉÝi]Û‹ÕuYÕrUWš÷¬5‹¿\Ê©q9 x§4/3’$–À3f¤ªû‹¼bò‹ ?,ûÁ—ܶhMW¯Ó/¶Ú­/Û+ìc¤|*@aŸ.ÆH!È0{ BW3ûoùé‡{6oÜ;üªk/µvï92¯c‡$…5K³¥B³”)&»…o}'¸Ÿ„7,8¾`µU±ÖT—æíܾì§o¿ú®¢¢¬˜S¦ð ~¥Àe$I$-ÈÖzt–)¾æÖ/¶¯L2üäþ÷°BÊöY/½pM Óñ!nX[˜–1þœÄG É|$òÏž{ôÜ©c^½bÒL±±É½ÜW±ßçªêÊÊ’Êò²Â¼=»wìÞºq?§M¶”]_]˜˜ñ æe9žË H’HZ6S^xé•Ô~o¿ôûö¾æ´Ekº8ªòë/SŸiäÌ—_Xå+`ïªíŠNïÅd%h•±|˜ölÛ´ƒÝ| A­iÃ|aø²Ù$ÆcÁØ­!L‘&Æm!`q@ØBøJ-—A$DvU½š›Ú§ü‘k_l¤ð8¦Oß|Ìzd·ª*ÅŠ±õðYÓ§ºïpø?®&e)pI‰@ËE@“,38‡}ù˜ì ´ÂQ/¿Ó”ß¿ô˜jWÿÎã»³ÉØú®0Õx‘ot„0™ ã¼8b\ÎBËÅs|;qð¥Wá)˜–q@Û…YqÆ5žA#–$HZ4Ðp5%ÔIŠNy|Öÿ¾ðº¿25B€9/ý•MÛy ï#a:Æ Vñ]„ð…€u¶Bà +…/ßPh¬¦¸‚W_q/žó+’$‰@ËE@Ã%zƒsØWÑÑÿoïJà£(²~UwON¹$€îzẮ®Þx¬ ꪸš„CPQ„$hÉ$QPäJ]ÝÅE9EÁcñ/DY„›på죾MÒCOÈL&0ƒ`^ý~3]Ç«WUÿ®ªWõªºêáh \‰Ú¡tØG%ê©Þì+ñØþ}çümüf+‰Ê›ÓÒÓ‹ÀÉ÷ã`)dåÏñsÞó<˜ì;B×™íʧ°ÎÏñ?Þ‡ £ùnÕgA⨕¯âœ­GÆS |YïD»‡ÒaG;/‡ŸÜ\UhÞ‡ƒ3îÇü®¶q¼×ÁE‘ë—G¬Š<7ìÊ>ä÷u{€!Ô(Þd!BàŽ:¿ÆÐz&£l+¿ÃÅ”f#ºÕ§Ú{ÚOŽÖnM |/;0+¯›e[W˜ã!t“ˆü6– !@„ÀݽÕÇÿ&Sš–þÕ¼ÈT¢Oõ«ëÛÝ´¼wÛ×¾Z>u‡UaýÏì˶?0,ókQºo×òkÛ®Âoì7½;œ|H(ìTÎXGó—Ág9ãìL[ðE)Þ챇Ä7‚È\°ùœó" =bIÒt_wÌprØ Æ0cRÐæÎP¯É-P^Ì+P¶•åëMÛüÔ¶­÷mÛ\^fíÝ‘[À×äæ+sfÆy(YQ¸Ö” v êé|®°E‚³$$ÿh‘Qø¥¿c>æuÄU¹gÒüwdG|0¹ïñ™tePj¹„±ícÔ³[˜à§»‚ÈzÐL7h_]×¶÷WeN‚…¦‚‰ÓÀâ4‹U_Þ»ÍâöHç[r„Í ó³3Ÿ¯ÎÒØToN:¸GçæŽŸ’‘±#DVÙ;?Çûâ!3ù½ì*›ñ¿w6wúy3´‹!h'aØ9\Ú¨Ç'aø·ûs økªH62­l}¸8áÂTµù SõÁû@3}wÜi†¨\ÅÍ­wÁ³YÐ4ß9{•¿£Ö ôŽëd²Ê®GKîÏÉi[Vf­ÆDÆùqP#-Y?bóIB·Æ«YžÚÍ# 7N¶m‘Z#(¬uQj þ¯ÂâWÝ»íí]ߨ²4l„ˆí@6ƪ´OÄsGŠ×7“)|.Ô˜˜Í†@­·ªž®CôIMË=ã0;¾³Öæ˜}|£hlx¾î]>HÏ;É0¬·<žKžÖGlt’˜•Û3ö©ÌÓú¯ÂÜ6‰s¶¢ ÛëWΙ3G]²bõhŒjï‚Ê»)fàÿåÌZP³¹¥eùˆª|¾ßs®>”Ÿñ©L#Í›{Ô›c=› ûq Nº{X\ש¾Akž~:a_Ž™ÓRÁ¬ ¤q<¢/Qå™éc2K^Ò<2n\“â½ÆDŽ«‘/çb±'Q}@FR³|9ÈÇCð«HÉôõ³X(|·Ù>oÏ*2OÙ¹Ð\Ëkݽ -ÍþØ GGrjAŽ÷éàÍN±¿ÖQîuBaÓfd{§Ê0iB½ƒªÐýÿ|¾öv¹À»â1¨‘Ú‹¨!t%oƘ^„%•×±¤2ÃIM®Aš}¦2UG9ã³SðN¯vŒʹҶí<™6ÂW+Š’.l»uÄ!aƒôœ³MÓ~Á¬|+êäTÔÓ'ñN˜ÐhߎÒoyí±Ïœ‚úu-ÞÙxÔ‡ ­I€Î£ÜÀL{"Ã’?úy½ºœüdß¾}¥ªÝoBµ‹´Q9—Y–ñÚZsÔÓ5’˜«<•Ùö4MSn›ªg~+ýdÛ4mûEåƒòÇdúgÅôœ3-CÌmÑÔÓý±#öúËd¹ÜÓmk×|q’–ôð“úƒ»$PïAA-©%IóæL‡¿Öë¬SRÝå|žÊÌÜ’š•{}/ãsY÷ª¿ÝÈ02‡€dªø¡Ïq¢pÃ4¼z \7€.­m!Þ’ji·ÿAÚ¯ñl–ðkU|ÑŽYBGƒ¿Ó=J¿NúW˜{Bwæ Ì<ʹ¼+lS,“›{¦êé¿ oÙ[nšr0–°ïAœ•zZ)ÊÛ©¥¸dÅ/ìý„¢öõhñg0¦|‚-HÍíïXËà\§ÅuBö.:¿w‡èã:H>HÂZ\¡0aÿV5í´6Ú9þŽÆIg?Òbö=ÈC†šÀ»ÀóéëÐBàÎóçSS®óxøè+Ú¥öK2\S[ä¡,XKås“r¦_0;äÀª¶w†EÎ p—"‹b(pg£|T QwòÙQæf¶°_…ZÚ?؈(R¢ûôqÉrRm½V’„z÷uá+8_ü¹“)²?¹ïìd[kùžä‹öu¼îD–xØ4ÖØÓAaêXÜàÞÈ¡”éëû1é´—Ó™ÊF·Få¦HÕ²]œ"öhCl‹ÆâÎU«ÓœøÎ3@gZ¯~Käg`œ=¼]òýš»ºpí"®U“@÷ °Ú-ë©ü%+ç/C~w[&»ÒávtÊÜÿ[~GùwK;@Ïû“iˆOôO÷\€¤ ­Tjî{ùAÔÐïÀá'Ÿ’Ö/pÑw4oêy ¦Àuh ²3>—vô+~ÞŽ?=šéºp++Ûþ œW¹¼ÊŠÍÃ,û…onH¾ô/ó‹>‰” :”&õœ.ÌâÍLfw†´õ¢–?Y ?¼=Àƒ‹SâÛ4;öé¡C+¤_ըغHó(gMÓ3WTÓݵà * ;î»ñ›Å™=O9#`Ÿ´{WÅ-hB7J÷Fˆû±Yì©Ùé_V‡GgÒôN‡3K/ýÄ|_Æ«Òa:„SZµp÷§¿¹ªôÆhýM'^­O.7Ê=Ps®M%¼+œ‘>ì{0súI˜æe°ÿZhåÉïnôóm‹ÉÑâô‰ÍKŒ’îª‡ß 7&ä(“ÂfÌÈÎ|Jºa&£SJ€À‹Ùÿ„@§TãT‘Výc) eY‰½Ø¾Š ?cÜaѲ1Ëâ¶(ðS ¤ x7e¤X‹êÃO°â3è>Û²YÇ Ó¸õt-;³µ½{¼£°ø.Y³æ_¢”eË6¨Ë–è‡îþ%98•ã°÷?mCHmÏO®™ñ«)Yy¿1a~8~“±a-+1sõUGž‹zz‰ö¸ ªýäc4¹ÜµZ!àÆçû²ž­\€ú·Ÿsáž)ýµ‹C‡æaZbcÔæª§ mëmXYOÇÉÁa‘é» í/‹ ;õ.­ªÞ hpø[2 Û°B³a†/ó>é†ùñ>oöê Á~•'µþwYÛ;@ÛŠ—TS˜Á>”ºbÀÒK séO&ö ‘‘,¿öØ~¨¤wD Ðx˲^Äì¹q¤<1ïj™â5Ìòž„ ù2Eeƒ±£xXp|>׸ÒßÖ™PWm::#èS¼ØwР䬑%jg£‘?P÷ÉÎíÀEØ6¨“—J·Û ÉÍm¼·À¬ã·?:±ÿõ Ç€¦=šÿuètæË:Žy‰ƒ­81³ÉTÄÿ-EŽÀ•A^‚ÝÔÛ„Í›H7øvFÇYf›æSNz6#dÔ‰'Égmñ[Œõ’aܶ1Èà‹Ë"`qƒìÜJͲ‹Ñioš®g®LÕo…¼·Ç%n^˜É¿ƒNµéû«Ö¿ß?øì÷GXjÏϨý½.ÿ¨Ys ÔÞlÜ5†(0ԛϾq›úð4Mö‚m°g¡Â‡Vˆ¿Ãšhç>­ÝàQãÝG‚oAfæ&T E¦_Ðb@™—'×è¯WZòÝÏÚf+ï<” Æ þŽ,cÿHKà)gÌ#TQ$S–2 æ;pˆ10è<ž€NMÍÊùGAvfD¹¿®g~~7Ïvöõ¤ºèêü[V–•ÊidJ¤q=Z‹.Õ©BE z÷‘âËU6KX,k¹#6•~,Õ«k¦ë^Ì\k3uÕ.u‡œ×¡Cå’«± Ú—39³ûugºC'ë·‘ŠË¨BÝèr†´ª–TW18 ´¼z´‹ þ—ŸuÊçï®X]Q±}ïùÌ7"¯ËÙ-Úì|[°¿Z¿ÈÒ”÷ê|Òù°ÀaBsTOý ±§õÙݧ½ƒ D9;îe–aMÀSj~È&Ü/è0%yä%³Ï.NAEn‹œAð ^Þ焱Ñúž·f±§è ̯:¤ê¹§è?9áP‰^ [ÀõÞYP%¾ 5Ôc•5k‚õwhÝϧG?² *Õ2aò¿Á¹†O¥äÆ"¿™ª?²êë]èmÚäñ:ªX‡4êO ^V¡§i¥)l›³Ùä€D°Ó ~è„÷›Äc’>Ü·³¤É&Ó×ÂWm¯¥/ëÛ—ÛødcÅì«0L¹L¨P)ÃÈ fA¿`=ír8?u¸`mür P*Œv-×9~¡žÀce~vÖð”Q¾Ï ‚x.MÏý6_ÏpJBEÈ¿Xùöv þq=‰0¨é7á¹Fú°~e…õŒy¤ø¶S.x³ÈþHl¶?¹ 3ú;¡f&µèÈz0Øø' z0.ª2h×?Â~±³!)@K$í›!0YP¿+,–B–B»u1ò{=êÑÝþì)üuLƒ'raí†ÿgyá_ >ßà.‚\W¯0*ϰժý î°Zíš2$Ñ#Šñ=ó7PûËoŸ¯•Ž<£Ž@ÐË:÷£„!ö ’£ZÇÂÖD)-¹¬åzqÔÍeOX‰™Ûṵ̂KÍ›¯&þVa£s –»ÐI0ÿÑŒ Lwa(<BdÉÔï'Ìý”ãvt³äƦÔQ9_³ö­>â…Û®ÁšSZàã€ÙýØü‹ªóŠÉÊ„]Ñ#Ϋ¦äxsÓª½½æ}¯ÈÈYn˜â ì~Í>Vóþw“9þDοLÏΜ#ùC€.à #+uìØfŽºlâ°aeèP–a¢:$¯ë:”¡0 S¤ ÿ D:'z–H?¿á|ø<„u·•¢cË…¬pûèô‚ï$g·³Cî9cŒw.pé) {î }J:f„áX…a@Ð?È#ŠÔ~OyyÅ`ùXÙ³Š_©Á€î9Û’û X·¸Ä`áâfˆw5ïp)Kq8dz›¬-„mfƒ&И=Ì3ÑFù½;¦¼ù¼M³¢ŠmûÎV±zZvÆ27¿hØ1Ó ß.{>g{ÍF¨Çc-úƒ@š —ëºÃÑü/íÒi™œ–·WN^Zdý|,ÊØWåÊØ­¦þí}PŠ7'½±Ö(¿„•·©4 ËLkXûÖ tuXäÎ䔬ÜÛ±ô²{C>ud¡AC›Ö°Í·½;´‡€:3–( §?äÍY¡ò'G¿ñž¦7¢aèp>®0öl†À†µÈÛÜj9¿ »rÑ]ƒu°Y¡øIìdÌÀhü;œw؆­» ˆó© v]º >/€:^™Œ™ÆDÃܹCeÛÐx§[ŠzŒ‹,*V¿°Lä×#ï{±ù¨ŠËlQ¹Úb “vœ.À çW¶ÏX60 @}ð)øüâU‡öX­ÝtnÉÕŸ9Z†a{2Ô…˜Ldë·íFù¥@_„kYNÜHŸ¢c›áx'»M³8ÜL-Rvlüóm±¡‹õŒ8ÂAŠ˜ÕS™HñÓÔg !’‚„û>;Ãÿ š¡ø-27î@ýïõuÔÝàç7òs&|–s5ün¬|mÅ–={¡ñYŠºsŽCÍg]íBÖ7¬aŒC=~ ƒÛØ^+Ó×l½â´)¿jYúézßJ,-B½îd'0„W™jÖõ˜ßTb–lÀö[ˆcñ…ÀUõJn3²3>DƒÐ—å®ûê$èCl|§IDATP¶ùúÚ¶×[LÌ- ü×on91¶ià›Óü|Û´·YÐnçCHTןMØÍŠ'êÃv†c#U["Ϊ×A†‹_Ÿ°ª2·kš¤î®jÆ•›oš(Šy¨»1eÔÂÈØæÌŽk¦s¸Ýc ´ó±iîã§»'3M4‹q~öÑÄË|ð£ãÛN=|ËþÖK!µ qå<©;gk${ äP?ŸpíBnŽ*ò¬i)7Õk0µÜÏ›Š ©Õ !בŠ@ƒºË¯k;DØ"ê›Sj¼p³Ç›[ƒÖk„““‹@Nz¾Gy1,Qãšµxðî]þ¢ÀŽX„@ ¼zƒäF50‰…S“']Å‚1ñl ¡&Ž’VÚ•‡%ÃQJƒ8hðB/%蓃½$³{ÁWFŒxÛ€öŽzŠs%âPÙ7x©ˆ„ÀaC Á ]Uˆõ±G›†4b_ Já÷C›~; ©ï™VL% )‰†‹@ƒºL |‹«j€…󘧫¼ß#§å äÄŒenðÉ ÕÓXL¼  Ðà…n×аãQvh138˜â­˜1'Æ áÿ·¥Bñ¿±-,ÕÓØâKÜ ºþ:€ìŸaeØÓ´iã×bÈŸX7WbWO9«TÕ„¨[Ù@^ “¨ ~¦+ÑJHl5³Ý­õB.Bb…ñ§OžýËžÉñAüûšüî0Rz¢k8´²Ï~¥]“ þ¯÷”ºÏÍŽI2U‡÷Ç„uSyÇ´Ðcǹh„cfWµ×.è_ÛòxJ°º^òÇïޱͽ¡N• •wy;Qù–=O"~/œ¡|2ž~Í| ÌÊë†ÃY&±ÆÚ5á0qÒF^û㔫gÛ{.ôÔVn‡®!>£2ü#×uᦣ‘DQèòrt·ÕGà¦e= ™oGtçÑ€)î­Ð\~4äõ’Ç‘©Ö[À|B´Êƒ:“;Õ»ê¸2=\˜þ2Ž¢ìãÖÄ ÑÇu@Ðií”Sdžʛà•ý¶¤Ò0¬ß\”îÞ[™*®ÛçÿÍ4Í× ä>rûK{E‰= «foBø‹{bÿмêôñmdXj–/Çöí(sàšJéÒ´hQª2eÙYøú†iÚþ‡œÁâ¬dÜS-n»WBðsyd2ó(|ꉿRú#]q™DÒ&ãcyþzÁÐa`Þ³½va—¾¬îˆoðÛï"ªv„Ê»œÑWlÝý!°}êÄf>,aÞýÛl']Y'0°¹ “‡ß?zîG€.<ØcÉïå…Ôê?ÕT;:EyfêmÝnù¬>ñ˜0îŠ2Çòß;8kìS²GúÏ• ·§ä¡Ã; ³FÞf«Ð o£_ÜÆr:„ã!°ãqH|zÇ!àÑ£íüÐñ ¡)ÉÚI‹ŠÌ5 ÔqV3+Ç5tÃquàë2¡øäÎø&-+gHzɳÑ^*ðef§ò]‡3™sp.l)f$5K›¦{?CGr+¾)õ¡HBç\Ö¥ÓCÎí(õ‚ˆC"ž2jøØ‚1mpÝÂ+d¬¤ÀÅ9Ü÷¦ßkâ|êºÍ4߈ÕÐÄìÀÅ€Ú/ð*L³Ç|yN0f¬!™Hud¡‘sK<Wá äy |,ˆ—!àøq ûsÓ³ÓÃ]×ÈTÕó#D[/Ûªè íÜ|pí‰8ÎzÆ9Ç8ßø-aUÞ(¦½09{Ä*Ôçdx]¦zÆù†C‡»t?¦ÝGº—~ÿË9hsëó}¬‘îéc2#­g¥öǦþ,ý\¦÷p¼¦Ì+ÚÓ‹ÎLPìyô=YˆWàŠë·†Ê»¼Ã¤(šºÅ2*‚ö=#*ùï®ÈôÝÌÕ„ná0qò\aVNQÏ!Œˆê•¯¡P‹eº>|«üáÀ4…ýFÎÀáqŒ¼='àËùz N:J·ÄÊú Á/@[—Å`W!‰÷$™Ííd €×Eábc9+“3¬g>Þ*-+÷¼´,ߣ([G¡Æ×²™’' ¡x`ô°ôç5ˆ/áò^=òûxOe­eŠF>ðî.AVÖz¦» ™ç”¬œâ-¬ÈÏù°Rk–ƒÜ´{ù€:ÀõÌîon •ÛÕh ?@ÊCÞçŠ[|ÔF¼s76Š,¤ÿ†×¢ÿü´@O+U’´× ¸ûº6a@Ø)Ëe‡!;t^Ûqï{ãÍÖÚ3`ß&×Áüósö-:˜6NIÇ'kŽ”#~÷5ò2o‹ã> $þ+ÒÚ¥ßý|A]|péRyj#§¼¿„z°§“†û)JÍ^p/°Ô ,Á4ÐøæCdßà¦!{tu!#ÍÎd\ýzÛ°3CwŠRHCX¿¬ñ„Î#ﵺÃ"±kšò2ÄÖ-R0Ðóþ„8Fó ¢pñ±öÙŸ3Õv4†/ra÷sÓ£ Ú _†pn.´r¸Ã"µ~tjJWá¦G/Ö§ÖNnººìõœ.àuo¢=%i[4ÅW%îxh³%ÐTÍh­Š+p㓘½ßw5'©©Dë'XSEÓPAuÈyuç‰E!8›þ¬Ïâ GMLeú:b蔒к©¯&-¹÷#€Á™Úè¶`ÓÛB×ßùæËi½mÆîDOu)Ô²-Ý´è( ·øJ<hœÍ:ká–uîðúØ…°ûƒ×¹ØX±Ê,1eÔvï®\sžoAåü.®™{*eTÎ*\b~ ’]'/ÌÆìx®Þk‡»9S >Þ„F~c¼6*£Ã¾ÌQ]I'òy)øœÌöB1\åf¶Â“y‡cæ†âƒû`›Fq„{ç=»+ÊбtÂÌw±Œ_Óp›_†D¯Æ}¡çË0¤'{øïjÒ‘;zd¤˜ËÀ­k^¾†ºbßÁp9¦rmk¦€wñ…o¨\{fDj%Ô³e5I"rOÓÓ…ŠyãfÓw‘ÍÌV•¹î:Vÿº£±çïвœƒú íL<òÙ–aR-íŽã|h%Ÿ Ò{Í0*Ñô"7òÞb,uĹc(‚7Ü.vûÕ׎¼k˜/©šÒw:°þø ß Ú$˜—h"TáO« ;ó„½ 79aÍõ_¥Å¥²ïí¦GÛØ‰)s€‡ÊŒ¦h˜‡”W7i?Ô|È›Æ ÍW6k?ÄÍ»6LÐkåƒæ¡IC†TâÇårCQ»Ÿe7@Æ… ]5­\×e£_ RuµâæímÃjgr%ŽskO£¸Vëê³Qª&Ç-w\–—ZÝP;/@áïh*9¿NÎ@óV²’ña¡³#ök…"ÞOVÚß ãÊõl°xÂìÆí·â4ÏeOgŒØ¿wìJ:ÇÈ‘8.Ã2Ý—ñ±ãç\œu f›š“ÓN”ÙW¢ú×§0ÓY‡|ýÙI jGðgÏ ÎôM”§¿#J×Ä}Q¼CKÏØ!ð`Z©¼£UþªÍNÇ•'>$Ÿcpÿ¥ê í<™1æ Ë{úC=ùðäìŒu!ÑLÛ¶úÃ$te¸èØj_¿M~:×DºëcÐé¿jrþOÄyT®g2£üÅ?*¹Ÿ‚oØ:@hmfÉváÐ>8aB£’%óÁóq Tªd¸\³„0iŽMLåšêf;ç jWNKO/v }ùÝ}‘ùÑí³û¶€].ºÏ“ù–ÂÔWmaÍÄŒÿéª?ò­°y’¿›NºÆ„ËÇ@Ýw.3=Års\¸|fÝðNú¡0©¾K;è}a€¾KÉuiBÞ åIB÷HxÓBôWínwVäÈ•ö2«ä6Õfobvy²iûîŠb »$·A=—ŽO >(4>þ‰ö{löÒT¯ý›ê;I¥±ê5÷YÏrcÛztE†¹³e‘¶÷n¸ò¹ÖX~RĘQb_ÿ\X—»´Ñv晫W€‡TA¬ŠoÝÔßVÜt2ÞÁ˜pù°L™wåð}8T>䀿ÄÜ×=Y;¯“~˜8„ô$ŽV¤Z òçû¼Ù¥Ü !ø!ü‡Z8 Ê•¯Ðˆ×Êi¸òÊu9 ¼%]$|äº\(žRµä“|å·‘ráö';!p¸xdܸ&uí‚vò’æÍ¾Bz¬ã®ïSîÚ¯G®åÖl’Fæi`^^ 7½l'5OoªÎ'R{¨|Tá3«YûMmùØJ¶h!àït£ÅŒøD¿zÊøø;Í£Þ O‘ œ—˜^¬ã¶ƒj;™ùÿ°»øé´‚MlØú7¼Ð™Öæx·š¬®\AèNˆŸºÒ¡pBàHDßœß  K.#Ÿï‰ù¤<ý1 ¡{¼ÇY¹=qô_ öÁ$Cݶ¿Ïš7öÌÚU¦Æ3³,Ë3¡÷5 8¿VUþòt=sÿ†Êç_ûŠŸ’"B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ B€ b‹Àÿ:üņ¾=9IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-flowew2.svg0000664000175000017500000013544413553660046026651 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 18:10:04 +0000Canvas 1Layer 1Open vSwitch - Self-service NetworksNetwork Traffic Flow - East/West Scenario 2Compute NodeInstance 1Linux Bridgeqbr(1)(3)(2) OVS Tunnel Bridgebr-tunOVS Integration Bridgebr-int(4)(8)(25)(9)(24)(6)(27)(7)(26)Self-service network 1VNI 101, 192.168.1.0/24Overlay network10.0.1.0/24VNI 101VNI 102(10)(23)Network NodeVNI 101VNI 102OVS Integration Bridgebr-intOVS Tunnel Bridgebr-tunRouter Namespaceqrouter(18)(11)(22)(13)(20)(12)(21)(16)(17)(15)(14)(19)Instance 2Linux Bridgeqbr(32)(30)(31)(5)(28)(29)Self-service network 2VNI 102, 192.168.2.0/24 neutron-12.1.1/doc/source/admin/deploy-ovs.rst0000664000175000017500000000107613553660046021314 0ustar zuulzuul00000000000000.. _deploy-ovs: ============================= Open vSwitch mechanism driver ============================= The Open vSwitch (OVS) mechanism driver uses a combination of OVS and Linux bridges as interconnection devices. However, optionally enabling the OVS native implementation of security groups removes the dependency on Linux bridges. We recommend using Open vSwitch version 2.4 or higher. Optional features may require a higher minimum version. .. toctree:: :maxdepth: 2 deploy-ovs-provider deploy-ovs-selfservice deploy-ovs-ha-vrrp deploy-ovs-ha-dvr neutron-12.1.1/doc/source/admin/migration.rst0000664000175000017500000000024513553660046021201 0ustar zuulzuul00000000000000.. _migration: ========= Migration ========= .. toctree:: :maxdepth: 2 migration-database migration-nova-network-to-neutron migration-classic-to-l3ha neutron-12.1.1/doc/source/admin/intro-basic-networking.rst0000664000175000017500000005541213553660046023615 0ustar zuulzuul00000000000000.. _intro-basic-networking: ================ Basic networking ================ Ethernet ~~~~~~~~ Ethernet is a networking protocol, specified by the IEEE 802.3 standard. Most wired network interface cards (NICs) communicate using Ethernet. In the `OSI model `_ of networking protocols, Ethernet occupies the second layer, which is known as the data link layer. When discussing Ethernet, you will often hear terms such as *local network*, *layer 2*, *L2*, *link layer* and *data link layer*. In an Ethernet network, the hosts connected to the network communicate by exchanging *frames*. Every host on an Ethernet network is uniquely identified by an address called the media access control (MAC) address. In particular, every virtual machine instance in an OpenStack environment has a unique MAC address, which is different from the MAC address of the compute host. A MAC address has 48 bits and is typically represented as a hexadecimal string, such as ``08:00:27:b9:88:74``. The MAC address is hard-coded into the NIC by the manufacturer, although modern NICs allow you to change the MAC address programmatically. In Linux, you can retrieve the MAC address of a NIC using the :command:`ip` command: .. code-block:: console $ ip link show eth0 2: eth0: mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 08:00:27:b9:88:74 brd ff:ff:ff:ff:ff:ff Conceptually, you can think of an Ethernet network as a single bus that each of the network hosts connects to. In early implementations, an Ethernet network consisted of a single coaxial cable that hosts would tap into to connect to the network. However, network hosts in modern Ethernet networks connect directly to a network device called a *switch*. Still, this conceptual model is useful, and in network diagrams (including those generated by the OpenStack dashboard) an Ethernet network is often depicted as if it was a single bus. You'll sometimes hear an Ethernet network referred to as a *layer 2 segment*. In an Ethernet network, every host on the network can send a frame directly to every other host. An Ethernet network also supports broadcasts so that one host can send a frame to every host on the network by sending to the special MAC address ``ff:ff:ff:ff:ff:ff``. ARP_ and DHCP_ are two notable protocols that use Ethernet broadcasts. Because Ethernet networks support broadcasts, you will sometimes hear an Ethernet network referred to as a *broadcast domain*. When a NIC receives an Ethernet frame, by default the NIC checks to see if the destination MAC address matches the address of the NIC (or the broadcast address), and the Ethernet frame is discarded if the MAC address does not match. For a compute host, this behavior is undesirable because the frame may be intended for one of the instances. NICs can be configured for *promiscuous mode*, where they pass all Ethernet frames to the operating system, even if the MAC address does not match. Compute hosts should always have the appropriate NICs configured for promiscuous mode. As mentioned earlier, modern Ethernet networks use switches to interconnect the network hosts. A switch is a box of networking hardware with a large number of ports that forward Ethernet frames from one connected host to another. When hosts first send frames over the switch, the switch doesn't know which MAC address is associated with which port. If an Ethernet frame is destined for an unknown MAC address, the switch broadcasts the frame to all ports. The switch learns which MAC addresses are at which ports by observing the traffic. Once it knows which MAC address is associated with a port, it can send Ethernet frames to the correct port instead of broadcasting. The switch maintains the mappings of MAC addresses to switch ports in a table called a *forwarding table* or *forwarding information base* (FIB). Switches can be daisy-chained together, and the resulting connection of switches and hosts behaves like a single network. VLANs ~~~~~ VLAN is a networking technology that enables a single switch to act as if it was multiple independent switches. Specifically, two hosts that are connected to the same switch but on different VLANs do not see each other's traffic. OpenStack is able to take advantage of VLANs to isolate the traffic of different projects, even if the projects happen to have instances running on the same compute host. Each VLAN has an associated numerical ID, between 1 and 4095. We say "VLAN 15" to refer to the VLAN with a numerical ID of 15. To understand how VLANs work, let's consider VLAN applications in a traditional IT environment, where physical hosts are attached to a physical switch, and no virtualization is involved. Imagine a scenario where you want three isolated networks but you only have a single physical switch. The network administrator would choose three VLAN IDs, for example, 10, 11, and 12, and would configure the switch to associate switchports with VLAN IDs. For example, switchport 2 might be associated with VLAN 10, switchport 3 might be associated with VLAN 11, and so forth. When a switchport is configured for a specific VLAN, it is called an *access port*. The switch is responsible for ensuring that the network traffic is isolated across the VLANs. Now consider the scenario that all of the switchports in the first switch become occupied, and so the organization buys a second switch and connects it to the first switch to expand the available number of switchports. The second switch is also configured to support VLAN IDs 10, 11, and 12. Now imagine host A connected to switch 1 on a port configured for VLAN ID 10 sends an Ethernet frame intended for host B connected to switch 2 on a port configured for VLAN ID 10. When switch 1 forwards the Ethernet frame to switch 2, it must communicate that the frame is associated with VLAN ID 10. If two switches are to be connected together, and the switches are configured for VLANs, then the switchports used for cross-connecting the switches must be configured to allow Ethernet frames from any VLAN to be forwarded to the other switch. In addition, the sending switch must tag each Ethernet frame with the VLAN ID so that the receiving switch can ensure that only hosts on the matching VLAN are eligible to receive the frame. A switchport that is configured to pass frames from all VLANs and tag them with the VLAN IDs is called a *trunk port*. IEEE 802.1Q is the network standard that describes how VLAN tags are encoded in Ethernet frames when trunking is being used. Note that if you are using VLANs on your physical switches to implement project isolation in your OpenStack cloud, you must ensure that all of your switchports are configured as trunk ports. It is important that you select a VLAN range not being used by your current network infrastructure. For example, if you estimate that your cloud must support a maximum of 100 projects, pick a VLAN range outside of that value, such as VLAN 200–299. OpenStack, and all physical network infrastructure that handles project networks, must then support this VLAN range. Trunking is used to connect between different switches. Each trunk uses a tag to identify which VLAN is in use. This ensures that switches on the same VLAN can communicate. .. _ARP: Subnets and ARP ~~~~~~~~~~~~~~~ While NICs use MAC addresses to address network hosts, TCP/IP applications use IP addresses. The Address Resolution Protocol (ARP) bridges the gap between Ethernet and IP by translating IP addresses into MAC addresses. IP addresses are broken up into two parts: a *network number* and a *host identifier*. Two hosts are on the same *subnet* if they have the same network number. Recall that two hosts can only communicate directly over Ethernet if they are on the same local network. ARP assumes that all machines that are in the same subnet are on the same local network. Network administrators must take care when assigning IP addresses and netmasks to hosts so that any two hosts that are in the same subnet are on the same local network, otherwise ARP does not work properly. To calculate the network number of an IP address, you must know the *netmask* associated with the address. A netmask indicates how many of the bits in the 32-bit IP address make up the network number. There are two syntaxes for expressing a netmask: * dotted quad * classless inter-domain routing (CIDR) Consider an IP address of 192.0.2.5, where the first 24 bits of the address are the network number. In dotted quad notation, the netmask would be written as ``255.255.255.0``. CIDR notation includes both the IP address and netmask, and this example would be written as ``192.0.2.5/24``. .. note:: Creating CIDR subnets including a multicast address or a loopback address cannot be used in an OpenStack environment. For example, creating a subnet using ``224.0.0.0/16`` or ``127.0.1.0/24`` is not supported. Sometimes we want to refer to a subnet, but not any particular IP address on the subnet. A common convention is to set the host identifier to all zeros to make reference to a subnet. For example, if a host's IP address is ``192.0.2.24/24``, then we would say the subnet is ``192.0.2.0/24``. To understand how ARP translates IP addresses to MAC addresses, consider the following example. Assume host *A* has an IP address of ``192.0.2.5/24`` and a MAC address of ``fc:99:47:49:d4:a0``, and wants to send a packet to host *B* with an IP address of ``192.0.2.7``. Note that the network number is the same for both hosts, so host *A* is able to send frames directly to host *B*. The first time host *A* attempts to communicate with host *B*, the destination MAC address is not known. Host *A* makes an ARP request to the local network. The request is a broadcast with a message like this: *To: everybody (ff:ff:ff:ff:ff:ff). I am looking for the computer who has IP address 192.0.2.7. Signed: MAC address fc:99:47:49:d4:a0*. Host *B* responds with a response like this: *To: fc:99:47:49:d4:a0. I have IP address 192.0.2.7. Signed: MAC address 54:78:1a:86:00:a5.* Host *A* then sends Ethernet frames to host *B*. You can initiate an ARP request manually using the :command:`arping` command. For example, to send an ARP request to IP address ``192.0.2.132``: .. code-block:: console $ arping -I eth0 192.0.2.132 ARPING 192.0.2.132 from 192.0.2.131 eth0 Unicast reply from 192.0.2.132 [54:78:1A:86:1C:0B] 0.670ms Unicast reply from 192.0.2.132 [54:78:1A:86:1C:0B] 0.722ms Unicast reply from 192.0.2.132 [54:78:1A:86:1C:0B] 0.723ms Sent 3 probes (1 broadcast(s)) Received 3 response(s) To reduce the number of ARP requests, operating systems maintain an ARP cache that contains the mappings of IP addresses to MAC address. On a Linux machine, you can view the contents of the ARP cache by using the :command:`arp` command: .. code-block:: console $ arp -n Address HWtype HWaddress Flags Mask Iface 192.0.2.3 ether 52:54:00:12:35:03 C eth0 192.0.2.2 ether 52:54:00:12:35:02 C eth0 .. _DHCP: DHCP ~~~~ Hosts connected to a network use the Dynamic Host Configuration Protocol (DHCP) to dynamically obtain IP addresses. A DHCP server hands out the IP addresses to network hosts, which are the DHCP clients. DHCP clients locate the DHCP server by sending a UDP_ packet from port 68 to address ``255.255.255.255`` on port 67. Address ``255.255.255.255`` is the local network broadcast address: all hosts on the local network see the UDP packets sent to this address. However, such packets are not forwarded to other networks. Consequently, the DHCP server must be on the same local network as the client, or the server will not receive the broadcast. The DHCP server responds by sending a UDP packet from port 67 to port 68 on the client. The exchange looks like this: 1. The client sends a discover ("I'm a client at MAC address ``08:00:27:b9:88:74``, I need an IP address") 2. The server sends an offer ("OK ``08:00:27:b9:88:74``, I'm offering IP address ``192.0.2.112``") 3. The client sends a request ("Server ``192.0.2.131``, I would like to have IP ``192.0.2.112``") 4. The server sends an acknowledgement ("OK ``08:00:27:b9:88:74``, IP ``192.0.2.112`` is yours") OpenStack uses a third-party program called `dnsmasq `_ to implement the DHCP server. Dnsmasq writes to the syslog, where you can observe the DHCP request and replies:: Apr 23 15:53:46 c100-1 dhcpd: DHCPDISCOVER from 08:00:27:b9:88:74 via eth2 Apr 23 15:53:46 c100-1 dhcpd: DHCPOFFER on 192.0.2.112 to 08:00:27:b9:88:74 via eth2 Apr 23 15:53:48 c100-1 dhcpd: DHCPREQUEST for 192.0.2.112 (192.0.2.131) from 08:00:27:b9:88:74 via eth2 Apr 23 15:53:48 c100-1 dhcpd: DHCPACK on 192.0.2.112 to 08:00:27:b9:88:74 via eth2 When troubleshooting an instance that is not reachable over the network, it can be helpful to examine this log to verify that all four steps of the DHCP protocol were carried out for the instance in question. IP ~~ The Internet Protocol (IP) specifies how to route packets between hosts that are connected to different local networks. IP relies on special network hosts called *routers* or *gateways*. A router is a host that is connected to at least two local networks and can forward IP packets from one local network to another. A router has multiple IP addresses: one for each of the networks it is connected to. In the OSI model of networking protocols IP occupies the third layer, known as the network layer. When discussing IP, you will often hear terms such as *layer 3*, *L3*, and *network layer*. A host sending a packet to an IP address consults its *routing table* to determine which machine on the local network(s) the packet should be sent to. The routing table maintains a list of the subnets associated with each local network that the host is directly connected to, as well as a list of routers that are on these local networks. On a Linux machine, any of the following commands displays the routing table: .. code-block:: console $ ip route show $ route -n $ netstat -rn Here is an example of output from :command:`ip route show`: .. code-block:: console $ ip route show default via 192.0.2.2 dev eth0 192.0.2.0/24 dev eth0 proto kernel scope link src 192.0.2.15 198.51.100.0/25 dev eth1 proto kernel scope link src 198.51.100.100 198.51.100.192/26 dev virbr0 proto kernel scope link src 198.51.100.193 Line 1 of the output specifies the location of the default route, which is the effective routing rule if none of the other rules match. The router associated with the default route (``192.0.2.2`` in the example above) is sometimes referred to as the *default gateway*. A DHCP_ server typically transmits the IP address of the default gateway to the DHCP client along with the client's IP address and a netmask. Line 2 of the output specifies that IPs in the ``192.0.2.0/24`` subnet are on the local network associated with the network interface eth0. Line 3 of the output specifies that IPs in the ``198.51.100.0/25`` subnet are on the local network associated with the network interface eth1. Line 4 of the output specifies that IPs in the ``198.51.100.192/26`` subnet are on the local network associated with the network interface virbr0. The output of the :command:`route -n` and :command:`netstat -rn` commands are formatted in a slightly different way. This example shows how the same routes would be formatted using these commands: .. code-block:: console $ route -n Kernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface 0.0.0.0 192.0.2.2 0.0.0.0 UG 0 0 0 eth0 192.0.2.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 198.51.100.0 0.0.0.0 255.255.255.128 U 0 0 0 eth1 198.51.100.192 0.0.0.0 255.255.255.192 U 0 0 0 virbr0 The :command:`ip route get` command outputs the route for a destination IP address. From the below example, destination IP address ``192.0.2.14`` is on the local network of eth0 and would be sent directly: .. code-block:: console $ ip route get 192.0.2.14 192.0.2.14 dev eth0 src 192.0.2.15 The destination IP address ``203.0.113.34`` is not on any of the connected local networks and would be forwarded to the default gateway at ``192.0.2.2``: .. code-block:: console $ ip route get 203.0.113.34 203.0.113.34 via 192.0.2.2 dev eth0 src 192.0.2.15 It is common for a packet to hop across multiple routers to reach its final destination. On a Linux machine, the ``traceroute`` and more recent ``mtr`` programs prints out the IP address of each router that an IP packet traverses along its path to its destination. .. _UDP: TCP/UDP/ICMP ~~~~~~~~~~~~ For networked software applications to communicate over an IP network, they must use a protocol layered atop IP. These protocols occupy the fourth layer of the OSI model known as the *transport layer* or *layer 4*. See the `Protocol Numbers `_ web page maintained by the Internet Assigned Numbers Authority (IANA) for a list of protocols that layer atop IP and their associated numbers. The *Transmission Control Protocol* (TCP) is the most commonly used layer 4 protocol in networked applications. TCP is a *connection-oriented* protocol: it uses a client-server model where a client connects to a server, where *server* refers to the application that receives connections. The typical interaction in a TCP-based application proceeds as follows: 1. Client connects to server. 2. Client and server exchange data. 3. Client or server disconnects. Because a network host may have multiple TCP-based applications running, TCP uses an addressing scheme called *ports* to uniquely identify TCP-based applications. A TCP port is associated with a number in the range 1-65535, and only one application on a host can be associated with a TCP port at a time, a restriction that is enforced by the operating system. A TCP server is said to *listen* on a port. For example, an SSH server typically listens on port 22. For a client to connect to a server using TCP, the client must know both the IP address of a server's host and the server's TCP port. The operating system of the TCP client application automatically assigns a port number to the client. The client owns this port number until the TCP connection is terminated, after which the operating system reclaims the port number. These types of ports are referred to as *ephemeral ports*. IANA maintains a `registry of port numbers `_ for many TCP-based services, as well as services that use other layer 4 protocols that employ ports. Registering a TCP port number is not required, but registering a port number is helpful to avoid collisions with other services. See `firewalls and default ports `_ in OpenStack Installation Guide for the default TCP ports used by various services involved in an OpenStack deployment. The most common application programming interface (API) for writing TCP-based applications is called *Berkeley sockets*, also known as *BSD sockets* or, simply, *sockets*. The sockets API exposes a *stream oriented* interface for writing TCP applications. From the perspective of a programmer, sending data over a TCP connection is similar to writing a stream of bytes to a file. It is the responsibility of the operating system's TCP/IP implementation to break up the stream of data into IP packets. The operating system is also responsible for automatically retransmitting dropped packets, and for handling flow control to ensure that transmitted data does not overrun the sender's data buffers, receiver's data buffers, and network capacity. Finally, the operating system is responsible for re-assembling the packets in the correct order into a stream of data on the receiver's side. Because TCP detects and retransmits lost packets, it is said to be a *reliable* protocol. The *User Datagram Protocol* (UDP) is another layer 4 protocol that is the basis of several well-known networking protocols. UDP is a *connectionless* protocol: two applications that communicate over UDP do not need to establish a connection before exchanging data. UDP is also an *unreliable* protocol. The operating system does not attempt to retransmit or even detect lost UDP packets. The operating system also does not provide any guarantee that the receiving application sees the UDP packets in the same order that they were sent in. UDP, like TCP, uses the notion of ports to distinguish between different applications running on the same system. Note, however, that operating systems treat UDP ports separately from TCP ports. For example, it is possible for one application to be associated with TCP port 16543 and a separate application to be associated with UDP port 16543. Like TCP, the sockets API is the most common API for writing UDP-based applications. The sockets API provides a *message-oriented* interface for writing UDP applications: a programmer sends data over UDP by transmitting a fixed-sized message. If an application requires retransmissions of lost packets or a well-defined ordering of received packets, the programmer is responsible for implementing this functionality in the application code. DHCP_, the Domain Name System (DNS), the Network Time Protocol (NTP), and :ref:`VXLAN` are examples of UDP-based protocols used in OpenStack deployments. UDP has support for one-to-many communication: sending a single packet to multiple hosts. An application can broadcast a UDP packet to all of the network hosts on a local network by setting the receiver IP address as the special IP broadcast address ``255.255.255.255``. An application can also send a UDP packet to a set of receivers using *IP multicast*. The intended receiver applications join a multicast group by binding a UDP socket to a special IP address that is one of the valid multicast group addresses. The receiving hosts do not have to be on the same local network as the sender, but the intervening routers must be configured to support IP multicast routing. VXLAN is an example of a UDP-based protocol that uses IP multicast. The *Internet Control Message Protocol* (ICMP) is a protocol used for sending control messages over an IP network. For example, a router that receives an IP packet may send an ICMP packet back to the source if there is no route in the router's routing table that corresponds to the destination address (ICMP code 1, destination host unreachable) or if the IP packet is too large for the router to handle (ICMP code 4, fragmentation required and "don't fragment" flag is set). The :command:`ping` and :command:`mtr` Linux command-line tools are two examples of network utilities that use ICMP. neutron-12.1.1/doc/source/admin/ops-ip-availability.rst0000664000175000017500000000575613553660046023103 0ustar zuulzuul00000000000000.. _ops-ip-availability: ======================= IP availability metrics ======================= Network IP Availability is an information-only API extension that allows a user or process to determine the number of IP addresses that are consumed across networks and the allocation pools of their subnets. This extension was added to neutron in the Mitaka release. This section illustrates how you can get the Network IP address availability through the command-line interface. Get Network IP address availability for all IPv4 networks: .. code-block:: console $ openstack ip availability list +--------------------------------------+--------------+-----------+----------+ | Network ID | Network Name | Total IPs | Used IPs | +--------------------------------------+--------------+-----------+----------+ | 363a611a-b08b-4281-b64e-198d90cb94fd | private | 253 | 3 | | c92d0605-caf2-4349-b1b8-8d5f9ac91df8 | public | 253 | 1 | +--------------------------------------+--------------+-----------+----------+ Get Network IP address availability for all IPv6 networks: .. code-block:: console $ openstack ip availability list --ip-version 6 +--------------------------------------+--------------+----------------------+----------+ | Network ID | Network Name | Total IPs | Used IPs | +--------------------------------------+--------------+----------------------+----------+ | 363a611a-b08b-4281-b64e-198d90cb94fd | private | 18446744073709551614 | 3 | | c92d0605-caf2-4349-b1b8-8d5f9ac91df8 | public | 18446744073709551614 | 1 | +--------------------------------------+--------------+----------------------+----------+ Get Network IP address availability statistics for a specific network: .. code-block:: console $ openstack ip availability show NETWORKUUID +------------------------+--------------------------------------------------------------+ | Field | Value | +------------------------+--------------------------------------------------------------+ | network_id | 0bf90de6-fc0f-4dba-b80d-96670dfb331a | | network_name | public | | project_id | 5669caad86a04256994cdf755df4d3c1 | | subnet_ip_availability | cidr='192.0.2.224/28', ip_version='4', subnet_id='346806ee- | | | a53e-44fd-968a-ddb2bcd2ba96', subnet_name='public_subnet', | | | total_ips='13', used_ips='5' | | total_ips | 13 | | used_ips | 5 | +------------------------+--------------------------------------------------------------+ neutron-12.1.1/doc/source/admin/archives/0000775000175000017500000000000013553660156020263 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/admin/archives/adv-config.rst0000664000175000017500000000440513553660046023033 0ustar zuulzuul00000000000000============================== Advanced configuration options ============================== This section describes advanced configuration options for various system components. For example, configuration options where the default works but that the user wants to customize options. After installing from packages, ``$NEUTRON_CONF_DIR`` is ``/etc/neutron``. L3 metering agent ~~~~~~~~~~~~~~~~~ You can run an L3 metering agent that enables layer-3 traffic metering. In general, you should launch the metering agent on all nodes that run the L3 agent: .. code-block:: console $ neutron-metering-agent --config-file NEUTRON_CONFIG_FILE \ --config-file L3_METERING_CONFIG_FILE You must configure a driver that matches the plug-in that runs on the service. The driver adds metering to the routing interface. +------------------------------------------+---------------------------------+ | Option | Value | +==========================================+=================================+ | **Open vSwitch** | | +------------------------------------------+---------------------------------+ | interface\_driver | | | ($NEUTRON\_CONF\_DIR/metering\_agent.ini)| openvswitch | +------------------------------------------+---------------------------------+ | **Linux Bridge** | | +------------------------------------------+---------------------------------+ | interface\_driver | | | ($NEUTRON\_CONF\_DIR/metering\_agent.ini)| linuxbridge | +------------------------------------------+---------------------------------+ L3 metering driver ------------------ You must configure any driver that implements the metering abstraction. Currently the only available implementation uses iptables for metering. .. code-block:: ini driver = iptables L3 metering service driver -------------------------- To enable L3 metering, you must set the following option in the ``neutron.conf`` file on the host that runs ``neutron-server``: .. code-block:: ini service_plugins = metering neutron-12.1.1/doc/source/admin/archives/introduction.rst0000664000175000017500000002340613553660047023542 0ustar zuulzuul00000000000000========================== Introduction to Networking ========================== The Networking service, code-named neutron, provides an API that lets you define network connectivity and addressing in the cloud. The Networking service enables operators to leverage different networking technologies to power their cloud networking. The Networking service also provides an API to configure and manage a variety of network services ranging from L3 forwarding and NAT to load balancing, edge firewalls, and IPsec VPN. For a detailed description of the Networking API abstractions and their attributes, see the `OpenStack Networking API v2.0 Reference `__. .. note:: If you use the Networking service, do not run the Compute ``nova-network`` service (like you do in traditional Compute deployments). When you configure networking, see the Compute-related topics in this Networking section. Networking API ~~~~~~~~~~~~~~ Networking is a virtual network service that provides a powerful API to define the network connectivity and IP addressing that devices from other services, such as Compute, use. The Compute API has a virtual server abstraction to describe computing resources. Similarly, the Networking API has virtual network, subnet, and port abstractions to describe networking resources. +---------------+-------------------------------------------------------------+ | Resource | Description | +===============+=============================================================+ | **Network** | An isolated L2 segment, analogous to VLAN in the physical | | | networking world. | +---------------+-------------------------------------------------------------+ | **Subnet** | A block of v4 or v6 IP addresses and associated | | | configuration state. | +---------------+-------------------------------------------------------------+ | **Port** | A connection point for attaching a single device, such as | | | the NIC of a virtual server, to a virtual network. Also | | | describes the associated network configuration, such as | | | the MAC and IP addresses to be used on that port. | +---------------+-------------------------------------------------------------+ **Networking resources** To configure rich network topologies, you can create and configure networks and subnets and instruct other OpenStack services like Compute to attach virtual devices to ports on these networks. In particular, Networking supports each project having multiple private networks and enables projects to choose their own IP addressing scheme, even if those IP addresses overlap with those that other projects use. The Networking service: - Enables advanced cloud networking use cases, such as building multi-tiered web applications and enabling migration of applications to the cloud without changing IP addresses. - Offers flexibility for administrators to customize network offerings. - Enables developers to extend the Networking API. Over time, the extended functionality becomes part of the core Networking API. Configure SSL support for networking API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OpenStack Networking supports SSL for the Networking API server. By default, SSL is disabled but you can enable it in the ``neutron.conf`` file. Set these options to configure SSL: ``use_ssl = True`` Enables SSL on the networking API server. ``ssl_cert_file = PATH_TO_CERTFILE`` Certificate file that is used when you securely start the Networking API server. ``ssl_key_file = PATH_TO_KEYFILE`` Private key file that is used when you securely start the Networking API server. ``ssl_ca_file = PATH_TO_CAFILE`` Optional. CA certificate file that is used when you securely start the Networking API server. This file verifies connecting clients. Set this option when API clients must authenticate to the API server by using SSL certificates that are signed by a trusted CA. ``tcp_keepidle = 600`` The value of TCP\_KEEPIDLE, in seconds, for each server socket when starting the API server. Not supported on OS X. ``retry_until_window = 30`` Number of seconds to keep retrying to listen. ``backlog = 4096`` Number of backlog requests with which to configure the socket. Load-Balancer-as-a-Service (LBaaS) overview ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Load-Balancer-as-a-Service (LBaaS) enables Networking to distribute incoming requests evenly among designated instances. This distribution ensures that the workload is shared predictably among instances and enables more effective use of system resources. Use one of these load balancing methods to distribute incoming requests: Round robin Rotates requests evenly between multiple instances. Source IP Requests from a unique source IP address are consistently directed to the same instance. Least connections Allocates requests to the instance with the least number of active connections. +-------------------------+---------------------------------------------------+ | Feature | Description | +=========================+===================================================+ | **Monitors** | LBaaS provides availability monitoring with the | | | ``ping``, TCP, HTTP and HTTPS GET methods. | | | Monitors are implemented to determine whether | | | pool members are available to handle requests. | +-------------------------+---------------------------------------------------+ | **Management** | LBaaS is managed using a variety of tool sets. | | | The REST API is available for programmatic | | | administration and scripting. Users perform | | | administrative management of load balancers | | | through either the CLI (``neutron``) or the | | | OpenStack Dashboard. | +-------------------------+---------------------------------------------------+ | **Connection limits** | Ingress traffic can be shaped with *connection | | | limits*. This feature allows workload control, | | | and can also assist with mitigating DoS (Denial | | | of Service) attacks. | +-------------------------+---------------------------------------------------+ | **Session persistence** | LBaaS supports session persistence by ensuring | | | incoming requests are routed to the same instance | | | within a pool of multiple instances. LBaaS | | | supports routing decisions based on cookies and | | | source IP address. | +-------------------------+---------------------------------------------------+ Firewall-as-a-Service (FWaaS) overview ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For information on Firewall-as-a-Service (FWaaS), please consult the :doc:`Networking Guide <../fwaas>`. Allowed-address-pairs ~~~~~~~~~~~~~~~~~~~~~ ``Allowed-address-pairs`` enables you to specify mac_address and ip_address(cidr) pairs that pass through a port regardless of subnet. This enables the use of protocols such as VRRP, which floats an IP address between two instances to enable fast data plane failover. .. note:: Currently, only the ML2, Open vSwitch, and VMware NSX plug-ins support the allowed-address-pairs extension. **Basic allowed-address-pairs operations.** - Create a port with a specified allowed address pair: .. code-block:: console $ openstack port create port1 --allowed-address \ ip-address=[,mac_address=[,mac_address=`_ for your distribution. Compute ~~~~~~~ If you use Networking, do not run the Compute ``nova-network`` service (like you do in traditional Compute deployments). Instead, Compute delegates most network-related decisions to Networking. .. note:: Uninstall ``nova-network`` and reboot any physical nodes that have been running ``nova-network`` before using them to run Networking. Inadvertently running the ``nova-network`` process while using Networking can cause problems, as can stale iptables rules pushed down by previously running ``nova-network``. Compute proxies project-facing API calls to manage security groups and floating IPs to Networking APIs. However, operator-facing tools such as ``nova-manage``, are not proxied and should not be used. .. warning:: When you configure networking, you must use this guide. Do not rely on Compute networking documentation or past experience with Compute. If a :command:`nova` command or configuration option related to networking is not mentioned in this guide, the command is probably not supported for use with Networking. In particular, you cannot use CLI tools like ``nova-manage`` and ``nova`` to manage networks or IP addressing, including both fixed and floating IPs, with Networking. To ensure that Compute works properly with Networking (rather than the legacy ``nova-network`` mechanism), you must adjust settings in the ``nova.conf`` configuration file. Networking API and credential configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Each time you provision or de-provision a VM in Compute, ``nova-\*`` services communicate with Networking using the standard API. For this to happen, you must configure the following items in the ``nova.conf`` file (used by each ``nova-compute`` and ``nova-api`` instance). .. list-table:: **nova.conf API and credential settings prior to Mitaka** :widths: 20 50 :header-rows: 1 * - Attribute name - Required * - ``[DEFAULT] use_neutron`` - Modify from the default to ``True`` to indicate that Networking should be used rather than the traditional nova-network networking model. * - ``[neutron] url`` - Update to the host name/IP and port of the neutron-server instance for this deployment. * - ``[neutron] auth_strategy`` - Keep the default ``keystone`` value for all production deployments. * - ``[neutron] admin_project_name`` - Update to the name of the service tenant created in the above section on Identity configuration. * - ``[neutron] admin_username`` - Update to the name of the user created in the above section on Identity configuration. * - ``[neutron] admin_password`` - Update to the password of the user created in the above section on Identity configuration. * - ``[neutron] admin_auth_url`` - Update to the Identity server IP and port. This is the Identity (keystone) admin API server IP and port value, and not the Identity service API IP and port. .. list-table:: **nova.conf API and credential settings in Newton** :widths: 20 50 :header-rows: 1 * - Attribute name - Required * - ``[DEFAULT] use_neutron`` - Modify from the default to ``True`` to indicate that Networking should be used rather than the traditional nova-network networking model. * - ``[neutron] url`` - Update to the host name/IP and port of the neutron-server instance for this deployment. * - ``[neutron] auth_strategy`` - Keep the default ``keystone`` value for all production deployments. * - ``[neutron] project_name`` - Update to the name of the service tenant created in the above section on Identity configuration. * - ``[neutron] username`` - Update to the name of the user created in the above section on Identity configuration. * - ``[neutron] password`` - Update to the password of the user created in the above section on Identity configuration. * - ``[neutron] auth_url`` - Update to the Identity server IP and port. This is the Identity (keystone) admin API server IP and port value, and not the Identity service API IP and port. Configure security groups ~~~~~~~~~~~~~~~~~~~~~~~~~ The Networking service provides security group functionality using a mechanism that is more flexible and powerful than the security group capabilities built into Compute. Therefore, if you use Networking, you should always disable built-in security groups and proxy all security group calls to the Networking API. If you do not, security policies will conflict by being simultaneously applied by both services. To proxy security groups to Networking, use the following configuration values in the ``nova.conf`` file: **nova.conf security group settings** +-----------------------+-----------------------------------------------------+ | Item | Configuration | +=======================+=====================================================+ | ``firewall_driver`` | Update to ``nova.virt.firewall.NoopFirewallDriver``,| | | so that nova-compute does not perform | | | iptables-based filtering itself. | +-----------------------+-----------------------------------------------------+ Configure metadata ~~~~~~~~~~~~~~~~~~ The Compute service allows VMs to query metadata associated with a VM by making a web request to a special 169.254.169.254 address. Networking supports proxying those requests to nova-api, even when the requests are made from isolated networks, or from multiple networks that use overlapping IP addresses. To enable proxying the requests, you must update the following fields in ``[neutron]`` section in the ``nova.conf``. **nova.conf metadata settings** +---------------------------------+------------------------------------------+ | Item | Configuration | +=================================+==========================================+ | ``service_metadata_proxy`` | Update to ``true``, otherwise nova-api | | | will not properly respond to requests | | | from the neutron-metadata-agent. | +---------------------------------+------------------------------------------+ | ``metadata_proxy_shared_secret``| Update to a string "password" value. | | | You must also configure the same value in| | | the ``metadata_agent.ini`` file, to | | | authenticate requests made for metadata. | | | | | | The default value of an empty string in | | | both files will allow metadata to | | | function, but will not be secure if any | | | non-trusted entities have access to the | | | metadata APIs exposed by nova-api. | +---------------------------------+------------------------------------------+ .. note:: As a precaution, even when using ``metadata_proxy_shared_secret``, we recommend that you do not expose metadata using the same nova-api instances that are used for projects. Instead, you should run a dedicated set of nova-api instances for metadata that are available only on your management network. Whether a given nova-api instance exposes metadata APIs is determined by the value of ``enabled_apis`` in its ``nova.conf``. Example nova.conf (for nova-compute and nova-api) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Example values for the above settings, assuming a cloud controller node running Compute and Networking with an IP address of 192.168.1.2: .. code-block:: ini [DEFAULT] use_neutron = True firewall_driver=nova.virt.firewall.NoopFirewallDriver [neutron] url=http://192.168.1.2:9696 auth_strategy=keystone admin_tenant_name=service admin_username=neutron admin_password=password admin_auth_url=http://192.168.1.2:35357/v2.0 service_metadata_proxy=true metadata_proxy_shared_secret=foo neutron-12.1.1/doc/source/admin/archives/multi-dhcp-agents.rst0000664000175000017500000000036513553660046024344 0ustar zuulzuul00000000000000========================================= Scalable and highly available DHCP agents ========================================= This section is fully described at the :doc:`High-availability for DHCP <../config-dhcp-ha>` in the Networking Guide. neutron-12.1.1/doc/source/admin/archives/auth.rst0000664000175000017500000001663013553660047021763 0ustar zuulzuul00000000000000.. _Authentication and authorization: ================================ Authentication and authorization ================================ Networking uses the Identity service as the default authentication service. When the Identity service is enabled, users who submit requests to the Networking service must provide an authentication token in ``X-Auth-Token`` request header. Users obtain this token by authenticating with the Identity service endpoint. For more information about authentication with the Identity service, see `OpenStack Identity service API v2.0 Reference `__. When the Identity service is enabled, it is not mandatory to specify the project ID for resources in create requests because the project ID is derived from the authentication token. The default authorization settings only allow administrative users to create resources on behalf of a different project. Networking uses information received from Identity to authorize user requests. Networking handles two kind of authorization policies: - **Operation-based** policies specify access criteria for specific operations, possibly with fine-grained control over specific attributes. - **Resource-based** policies specify whether access to specific resource is granted or not according to the permissions configured for the resource (currently available only for the network resource). The actual authorization policies enforced in Networking might vary from deployment to deployment. The policy engine reads entries from the ``policy.json`` file. The actual location of this file might vary from distribution to distribution. Entries can be updated while the system is running, and no service restart is required. Every time the policy file is updated, the policies are automatically reloaded. Currently the only way of updating such policies is to edit the policy file. In this section, the terms *policy* and *rule* refer to objects that are specified in the same way in the policy file. There are no syntax differences between a rule and a policy. A policy is something that is matched directly from the Networking policy engine. A rule is an element in a policy, which is evaluated. For instance in ``"create_subnet": "rule:admin_or_network_owner"``, *create_subnet* is a policy, and *admin_or_network_owner* is a rule. Policies are triggered by the Networking policy engine whenever one of them matches a Networking API operation or a specific attribute being used in a given operation. For instance the ``create_subnet`` policy is triggered every time a ``POST /v2.0/subnets`` request is sent to the Networking server; on the other hand ``create_network:shared`` is triggered every time the *shared* attribute is explicitly specified (and set to a value different from its default) in a ``POST /v2.0/networks`` request. It is also worth mentioning that policies can also be related to specific API extensions; for instance ``extension:provider_network:set`` is triggered if the attributes defined by the Provider Network extensions are specified in an API request. An authorization policy can be composed by one or more rules. If more rules are specified then the evaluation policy succeeds if any of the rules evaluates successfully; if an API operation matches multiple policies, then all the policies must evaluate successfully. Also, authorization rules are recursive. Once a rule is matched, the rule(s) can be resolved to another rule, until a terminal rule is reached. The Networking policy engine currently defines the following kinds of terminal rules: - **Role-based rules** evaluate successfully if the user who submits the request has the specified role. For instance ``"role:admin"`` is successful if the user who submits the request is an administrator. - **Field-based rules** evaluate successfully if a field of the resource specified in the current request matches a specific value. For instance ``"field:networks:shared=True"`` is successful if the ``shared`` attribute of the ``network`` resource is set to true. - **Generic rules** compare an attribute in the resource with an attribute extracted from the user's security credentials and evaluates successfully if the comparison is successful. For instance ``"tenant_id:%(tenant_id)s"`` is successful if the project identifier in the resource is equal to the project identifier of the user submitting the request. This extract is from the default ``policy.json`` file: - A rule that evaluates successfully if the current user is an administrator or the owner of the resource specified in the request (project identifier is equal). .. code-block:: none { "admin_or_owner": "role:admin", "tenant_id:%(tenant_id)s", "admin_or_network_owner": "role:admin", "tenant_id:%(network_tenant_id)s", "admin_only": "role:admin", "regular_user": "", "shared":"field:networks:shared=True", "default": - The default policy that is always evaluated if an API operation does not match any of the policies in ``policy.json``. .. code-block:: none "rule:admin_or_owner", "create_subnet": "rule:admin_or_network_owner", "get_subnet": "rule:admin_or_owner", "rule:shared", "update_subnet": "rule:admin_or_network_owner", "delete_subnet": "rule:admin_or_network_owner", "create_network": "", "get_network": "rule:admin_or_owner", - This policy evaluates successfully if either *admin_or_owner*, or *shared* evaluates successfully. .. code-block:: none "rule:shared", "create_network:shared": "rule:admin_only" - This policy restricts the ability to manipulate the *shared* attribute for a network to administrators only. .. code-block:: none , "update_network": "rule:admin_or_owner", "delete_network": "rule:admin_or_owner", "create_port": "", "create_port:mac_address": "rule:admin_or_network_owner", "create_port:fixed_ips": - This policy restricts the ability to manipulate the *mac_address* attribute for a port only to administrators and the owner of the network where the port is attached. .. code-block:: none "rule:admin_or_network_owner", "get_port": "rule:admin_or_owner", "update_port": "rule:admin_or_owner", "delete_port": "rule:admin_or_owner" } In some cases, some operations are restricted to administrators only. This example shows you how to modify a policy file to permit project to define networks, see their resources, and permit administrative users to perform all other operations: .. code-block:: none { "admin_or_owner": "role:admin", "tenant_id:%(tenant_id)s", "admin_only": "role:admin", "regular_user": "", "default": "rule:admin_only", "create_subnet": "rule:admin_only", "get_subnet": "rule:admin_or_owner", "update_subnet": "rule:admin_only", "delete_subnet": "rule:admin_only", "create_network": "", "get_network": "rule:admin_or_owner", "create_network:shared": "rule:admin_only", "update_network": "rule:admin_or_owner", "delete_network": "rule:admin_or_owner", "create_port": "rule:admin_only", "get_port": "rule:admin_or_owner", "update_port": "rule:admin_only", "delete_port": "rule:admin_only" } neutron-12.1.1/doc/source/admin/archives/adv-features.rst0000664000175000017500000010311513553660047023403 0ustar zuulzuul00000000000000.. _adv-features: ======================================== Advanced features through API extensions ======================================== Several plug-ins implement API extensions that provide capabilities similar to what was available in ``nova-network``. These plug-ins are likely to be of interest to the OpenStack community. Provider networks ~~~~~~~~~~~~~~~~~ Networks can be categorized as either project networks or provider networks. Project networks are created by normal users and details about how they are physically realized are hidden from those users. Provider networks are created with administrative credentials, specifying the details of how the network is physically realized, usually to match some existing network in the data center. Provider networks enable administrators to create networks that map directly to the physical networks in the data center. This is commonly used to give projects direct access to a public network that can be used to reach the Internet. It might also be used to integrate with VLANs in the network that already have a defined meaning (for example, enable a VM from the marketing department to be placed on the same VLAN as bare-metal marketing hosts in the same data center). The provider extension allows administrators to explicitly manage the relationship between Networking virtual networks and underlying physical mechanisms such as VLANs and tunnels. When this extension is supported, Networking client users with administrative privileges see additional provider attributes on all virtual networks and are able to specify these attributes in order to create provider networks. The provider extension is supported by the Open vSwitch and Linux Bridge plug-ins. Configuration of these plug-ins requires familiarity with this extension. Terminology ----------- A number of terms are used in the provider extension and in the configuration of plug-ins supporting the provider extension: .. list-table:: **Provider extension terminology** :widths: 30 70 :header-rows: 1 * - Term - Description * - virtual network - A Networking L2 network (identified by a UUID and optional name) whose ports can be attached as vNICs to Compute instances and to various Networking agents. The Open vSwitch and Linux Bridge plug-ins each support several different mechanisms to realize virtual networks. * - physical network - A network connecting virtualization hosts (such as compute nodes) with each other and with other network resources. Each physical network might support multiple virtual networks. The provider extension and the plug-in configurations identify physical networks using simple string names. * - project network - A virtual network that a project or an administrator creates. The physical details of the network are not exposed to the project. * - provider network - A virtual network administratively created to map to a specific network in the data center, typically to enable direct access to non-OpenStack resources on that network. Project can be given access to provider networks. * - VLAN network - A virtual network implemented as packets on a specific physical network containing IEEE 802.1Q headers with a specific VID field value. VLAN networks sharing the same physical network are isolated from each other at L2 and can even have overlapping IP address spaces. Each distinct physical network supporting VLAN networks is treated as a separate VLAN trunk, with a distinct space of VID values. Valid VID values are 1 through 4094. * - flat network - A virtual network implemented as packets on a specific physical network containing no IEEE 802.1Q header. Each physical network can realize at most one flat network. * - local network - A virtual network that allows communication within each host, but not across a network. Local networks are intended mainly for single-node test scenarios, but can have other uses. * - GRE network - A virtual network implemented as network packets encapsulated using GRE. GRE networks are also referred to as *tunnels*. GRE tunnel packets are routed by the IP routing table for the host, so GRE networks are not associated by Networking with specific physical networks. * - Virtual Extensible LAN (VXLAN) network - VXLAN is a proposed encapsulation protocol for running an overlay network on existing Layer 3 infrastructure. An overlay network is a virtual network that is built on top of existing network Layer 2 and Layer 3 technologies to support elastic compute architectures. The ML2, Open vSwitch, and Linux Bridge plug-ins support VLAN networks, flat networks, and local networks. Only the ML2 and Open vSwitch plug-ins currently support GRE and VXLAN networks, provided that the required features exist in the hosts Linux kernel, Open vSwitch, and iproute2 packages. Provider attributes ------------------- The provider extension extends the Networking network resource with these attributes: .. list-table:: **Provider network attributes** :widths: 10 10 10 49 :header-rows: 1 * - Attribute name - Type - Default Value - Description * - provider: network\_type - String - N/A - The physical mechanism by which the virtual network is implemented. Possible values are ``flat``, ``vlan``, ``local``, ``gre``, and ``vxlan``, corresponding to flat networks, VLAN networks, local networks, GRE networks, and VXLAN networks as defined above. All types of provider networks can be created by administrators, while project networks can be implemented as ``vlan``, ``gre``, ``vxlan``, or ``local`` network types depending on plug-in configuration. * - provider: physical_network - String - If a physical network named "default" has been configured and if provider:network_type is ``flat`` or ``vlan``, then "default" is used. - The name of the physical network over which the virtual network is implemented for flat and VLAN networks. Not applicable to the ``local`` or ``gre`` network types. * - provider:segmentation_id - Integer - N/A - For VLAN networks, the VLAN VID on the physical network that realizes the virtual network. Valid VLAN VIDs are 1 through 4094. For GRE networks, the tunnel ID. Valid tunnel IDs are any 32 bit unsigned integer. Not applicable to the ``flat`` or ``local`` network types. To view or set provider extended attributes, a client must be authorized for the ``extension:provider_network:view`` and ``extension:provider_network:set`` actions in the Networking policy configuration. The default Networking configuration authorizes both actions for users with the admin role. An authorized client or an administrative user can view and set the provider extended attributes through Networking API calls. See the section called :ref:`Authentication and authorization` for details on policy configuration. .. _L3-routing-and-NAT: L3 routing and NAT ~~~~~~~~~~~~~~~~~~ The Networking API provides abstract L2 network segments that are decoupled from the technology used to implement the L2 network. Networking includes an API extension that provides abstract L3 routers that API users can dynamically provision and configure. These Networking routers can connect multiple L2 Networking networks and can also provide a gateway that connects one or more private L2 networks to a shared external network. For example, a public network for access to the Internet. See the `OpenStack Configuration Reference `_ for details on common models of deploying Networking L3 routers. The L3 router provides basic NAT capabilities on gateway ports that uplink the router to external networks. This router SNATs all traffic by default and supports floating IPs, which creates a static one-to-one mapping from a public IP on the external network to a private IP on one of the other subnets attached to the router. This allows a project to selectively expose VMs on private networks to other hosts on the external network (and often to all hosts on the Internet). You can allocate and map floating IPs from one port to another, as needed. Basic L3 operations ------------------- External networks are visible to all users. However, the default policy settings enable only administrative users to create, update, and delete external networks. This table shows example :command:`openstack` commands that enable you to complete basic L3 operations: .. list-table:: **Basic L3 Operations** :widths: 30 50 :header-rows: 1 * - Operation - Command * - Creates external networks. - .. code-block:: console $ openstack network create public --external $ openstack subnet create --network public --subnet-range 172.16.1.0/24 subnetname * - Lists external networks. - .. code-block:: console $ openstack network list --external * - Creates an internal-only router that connects to multiple L2 networks privately. - .. code-block:: console $ openstack network create net1 $ openstack subnet create --network net1 --subnet-range 10.0.0.0/24 subnetname1 $ openstack network create net2 $ openstack subnet create --network net2 --subnet-range 10.0.1.0/24 subnetname2 $ openstack router create router1 $ openstack router add subnet router1 subnetname1 $ openstack router add subnet router1 subnetname2 An internal router port can have only one IPv4 subnet and multiple IPv6 subnets that belong to the same network ID. When you call ``router-interface-add`` with an IPv6 subnet, this operation adds the interface to an existing internal port with the same network ID. If a port with the same network ID does not exist, a new port is created. * - Connects a router to an external network, which enables that router to act as a NAT gateway for external connectivity. - .. code-block:: console $ openstack router set --external-gateway EXT_NET_ID router1 $ openstack router set --route destination=172.24.4.0/24,gateway=172.24.4.1 router1 The router obtains an interface with the gateway_ip address of the subnet and this interface is attached to a port on the L2 Networking network associated with the subnet. The router also gets a gateway interface to the specified external network. This provides SNAT connectivity to the external network as well as support for floating IPs allocated on that external networks. Commonly an external network maps to a network in the provider. * - Lists routers. - .. code-block:: console $ openstack router list * - Shows information for a specified router. - .. code-block:: console $ openstack router show ROUTER_ID * - Shows all internal interfaces for a router. - .. code-block:: console $ openstack port list --router ROUTER_ID $ openstack port list --router ROUTER_NAME * - Identifies the PORT_ID that represents the VM NIC to which the floating IP should map. - .. code-block:: console $ openstack port list -c ID -c "Fixed IP Addresses" --server INSTANCE_ID This port must be on a Networking subnet that is attached to a router uplinked to the external network used to create the floating IP. Conceptually, this is because the router must be able to perform the Destination NAT (DNAT) rewriting of packets from the floating IP address (chosen from a subnet on the external network) to the internal fixed IP (chosen from a private subnet that is behind the router). * - Creates a floating IP address and associates it with a port. - .. code-block:: console $ openstack floating ip create EXT_NET_ID $ openstack floating ip add port FLOATING_IP_ID --port-id INTERNAL_VM_PORT_ID * - Creates a floating IP on a specific subnet in the external network. - .. code-block:: console $ openstack floating ip create EXT_NET_ID --subnet SUBNET_ID If there are multiple subnets in the external network, you can choose a specific subnet based on quality and costs. * - Creates a floating IP address and associates it with a port, in a single step. - .. code-block:: console $ openstack floating ip create --port INTERNAL_VM_PORT_ID EXT_NET_ID * - Lists floating IPs - .. code-block:: console $ openstack floating ip list * - Finds floating IP for a specified VM port. - .. code-block:: console $ openstack floating ip list --port INTERNAL_VM_PORT_ID * - Disassociates a floating IP address. - .. code-block:: console $ openstack floating ip remove port FLOATING_IP_ID * - Deletes the floating IP address. - .. code-block:: console $ openstack floating ip delete FLOATING_IP_ID * - Clears the gateway. - .. code-block:: console $ openstack router unset --external-gateway router1 * - Removes the interfaces from the router. - .. code-block:: console $ openstack router remove subnet router1 SUBNET_ID If this subnet ID is the last subnet on the port, this operation deletes the port itself. * - Deletes the router. - .. code-block:: console $ openstack router delete router1 Security groups ~~~~~~~~~~~~~~~ Security groups and security group rules allow administrators and projects to specify the type of traffic and direction (ingress/egress) that is allowed to pass through a port. A security group is a container for security group rules. When a port is created in Networking it is associated with a security group. If a security group is not specified the port is associated with a 'default' security group. By default, this group drops all ingress traffic and allows all egress. Rules can be added to this group in order to change the behavior. To use the Compute security group APIs or use Compute to orchestrate the creation of ports for instances on specific security groups, you must complete additional configuration. You must configure the ``/etc/nova/nova.conf`` file and set the ``security_group_api=neutron`` option on every node that runs nova-compute and nova-api. After you make this change, restart nova-api and nova-compute to pick up this change. Then, you can use both the Compute and OpenStack Network security group APIs at the same time. .. note:: - To use the Compute security group API with Networking, the Networking plug-in must implement the security group API. The following plug-ins currently implement this: ML2, Open vSwitch, Linux Bridge, NEC, and VMware NSX. - You must configure the correct firewall driver in the ``securitygroup`` section of the plug-in/agent configuration file. Some plug-ins and agents, such as Linux Bridge Agent and Open vSwitch Agent, use the no-operation driver as the default, which results in non-working security groups. - When using the security group API through Compute, security groups are applied to all ports on an instance. The reason for this is that Compute security group APIs are instances based and not port based as Networking. Basic security group operations ------------------------------- This table shows example neutron commands that enable you to complete basic security group operations: .. list-table:: **Basic security group operations** :widths: 30 50 :header-rows: 1 * - Operation - Command * - Creates a security group for our web servers. - .. code-block:: console $ openstack security group create webservers \ --description "security group for webservers" * - Lists security groups. - .. code-block:: console $ openstack security group list * - Creates a security group rule to allow port 80 ingress. - .. code-block:: console $ openstack security group rule create --ingress \ --protocol tcp SECURITY_GROUP_UUID * - Lists security group rules. - .. code-block:: console $ openstack security group rule list * - Deletes a security group rule. - .. code-block:: console $ openstack security group rule delete SECURITY_GROUP_RULE_UUID * - Deletes a security group. - .. code-block:: console $ openstack security group delete SECURITY_GROUP_UUID * - Creates a port and associates two security groups. - .. code-block:: console $ openstack port create port1 --security-group SECURITY_GROUP_ID1 \ --security-group SECURITY_GROUP_ID2 --network NETWORK_ID * - Removes security groups from a port. - .. code-block:: console $ openstack port set --no-security-group PORT_ID Basic Load-Balancer-as-a-Service operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. note:: The Load-Balancer-as-a-Service (LBaaS) API provisions and configures load balancers. The reference implementation is based on the HAProxy software load balancer. This list shows example neutron commands that enable you to complete basic LBaaS operations: - Creates a load balancer pool by using specific provider. ``--provider`` is an optional argument. If not used, the pool is created with default provider for LBaaS service. You should configure the default provider in the ``[service_providers]`` section of the ``neutron.conf`` file. If no default provider is specified for LBaaS, the ``--provider`` parameter is required for pool creation. .. code-block:: console $ neutron lb-pool-create --lb-method ROUND_ROBIN --name mypool \ --protocol HTTP --subnet-id SUBNET_UUID --provider PROVIDER_NAME - Associates two web servers with pool. .. code-block:: console $ neutron lb-member-create --address WEBSERVER1_IP --protocol-port 80 mypool $ neutron lb-member-create --address WEBSERVER2_IP --protocol-port 80 mypool - Creates a health monitor that checks to make sure our instances are still running on the specified protocol-port. .. code-block:: console $ neutron lb-healthmonitor-create --delay 3 --type HTTP --max-retries 3 \ --timeout 3 - Associates a health monitor with pool. .. code-block:: console $ neutron lb-healthmonitor-associate HEALTHMONITOR_UUID mypool - Creates a virtual IP (VIP) address that, when accessed through the load balancer, directs the requests to one of the pool members. .. code-block:: console $ neutron lb-vip-create --name myvip --protocol-port 80 --protocol \ HTTP --subnet-id SUBNET_UUID mypool Plug-in specific extensions ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Each vendor can choose to implement additional API extensions to the core API. This section describes the extensions for each plug-in. VMware NSX extensions --------------------- These sections explain NSX plug-in extensions. VMware NSX QoS extension ^^^^^^^^^^^^^^^^^^^^^^^^ The VMware NSX QoS extension rate-limits network ports to guarantee a specific amount of bandwidth for each port. This extension, by default, is only accessible by a project with an admin role but is configurable through the ``policy.json`` file. To use this extension, create a queue and specify the min/max bandwidth rates (kbps) and optionally set the QoS Marking and DSCP value (if your network fabric uses these values to make forwarding decisions). Once created, you can associate a queue with a network. Then, when ports are created on that network they are automatically created and associated with the specific queue size that was associated with the network. Because one size queue for a every port on a network might not be optimal, a scaling factor from the nova flavor ``rxtx_factor`` is passed in from Compute when creating the port to scale the queue. Lastly, if you want to set a specific baseline QoS policy for the amount of bandwidth a single port can use (unless a network queue is specified with the network a port is created on) a default queue can be created in Networking which then causes ports created to be associated with a queue of that size times the rxtx scaling factor. Note that after a network or default queue is specified, queues are added to ports that are subsequently created but are not added to existing ports. Basic VMware NSX QoS operations ''''''''''''''''''''''''''''''' This table shows example neutron commands that enable you to complete basic queue operations: .. list-table:: **Basic VMware NSX QoS operations** :widths: 30 50 :header-rows: 1 * - Operation - Command * - Creates QoS queue (admin-only). - .. code-block:: console $ neutron queue-create --min 10 --max 1000 myqueue * - Associates a queue with a network. - .. code-block:: console $ neutron net-create network --queue_id QUEUE_ID * - Creates a default system queue. - .. code-block:: console $ neutron queue-create --default True --min 10 --max 2000 default * - Lists QoS queues. - .. code-block:: console $ neutron queue-list * - Deletes a QoS queue. - .. code-block:: console $ neutron queue-delete QUEUE_ID_OR_NAME VMware NSX provider networks extension ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Provider networks can be implemented in different ways by the underlying NSX platform. The *FLAT* and *VLAN* network types use bridged transport connectors. These network types enable the attachment of large number of ports. To handle the increased scale, the NSX plug-in can back a single OpenStack Network with a chain of NSX logical switches. You can specify the maximum number of ports on each logical switch in this chain on the ``max_lp_per_bridged_ls`` parameter, which has a default value of 5,000. The recommended value for this parameter varies with the NSX version running in the back-end, as shown in the following table. **Recommended values for max_lp_per_bridged_ls** +---------------+---------------------+ | NSX version | Recommended Value | +===============+=====================+ | 2.x | 64 | +---------------+---------------------+ | 3.0.x | 5,000 | +---------------+---------------------+ | 3.1.x | 5,000 | +---------------+---------------------+ | 3.2.x | 10,000 | +---------------+---------------------+ In addition to these network types, the NSX plug-in also supports a special *l3_ext* network type, which maps external networks to specific NSX gateway services as discussed in the next section. VMware NSX L3 extension ^^^^^^^^^^^^^^^^^^^^^^^ NSX exposes its L3 capabilities through gateway services which are usually configured out of band from OpenStack. To use NSX with L3 capabilities, first create an L3 gateway service in the NSX Manager. Next, in ``/etc/neutron/plugins/vmware/nsx.ini`` set ``default_l3_gw_service_uuid`` to this value. By default, routers are mapped to this gateway service. VMware NSX L3 extension operations '''''''''''''''''''''''''''''''''' Create external network and map it to a specific NSX gateway service: .. code-block:: console $ openstack network create public --external --provider-network-type l3_ext \ --provider-physical-network L3_GATEWAY_SERVICE_UUID Terminate traffic on a specific VLAN from a NSX gateway service: .. code-block:: console $ openstack network create public --external --provider-network-type l3_ext \ --provider-physical-network L3_GATEWAY_SERVICE_UUID --provider-segment VLAN_ID Operational status synchronization in the VMware NSX plug-in ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Starting with the Havana release, the VMware NSX plug-in provides an asynchronous mechanism for retrieving the operational status for neutron resources from the NSX back-end; this applies to *network*, *port*, and *router* resources. The back-end is polled periodically and the status for every resource is retrieved; then the status in the Networking database is updated only for the resources for which a status change occurred. As operational status is now retrieved asynchronously, performance for ``GET`` operations is consistently improved. Data to retrieve from the back-end are divided in chunks in order to avoid expensive API requests; this is achieved leveraging NSX APIs response paging capabilities. The minimum chunk size can be specified using a configuration option; the actual chunk size is then determined dynamically according to: total number of resources to retrieve, interval between two synchronization task runs, minimum delay between two subsequent requests to the NSX back-end. The operational status synchronization can be tuned or disabled using the configuration options reported in this table; it is however worth noting that the default values work fine in most cases. .. list-table:: **Configuration options for tuning operational status synchronization in the NSX plug-in** :widths: 10 10 10 10 35 :header-rows: 1 * - Option name - Group - Default value - Type and constraints - Notes * - ``state_sync_interval`` - ``nsx_sync`` - 10 seconds - Integer; no constraint. - Interval in seconds between two run of the synchronization task. If the synchronization task takes more than ``state_sync_interval`` seconds to execute, a new instance of the task is started as soon as the other is completed. Setting the value for this option to 0 will disable the synchronization task. * - ``max_random_sync_delay`` - ``nsx_sync`` - 0 seconds - Integer. Must not exceed ``min_sync_req_delay`` - When different from zero, a random delay between 0 and ``max_random_sync_delay`` will be added before processing the next chunk. * - ``min_sync_req_delay`` - ``nsx_sync`` - 1 second - Integer. Must not exceed ``state_sync_interval``. - The value of this option can be tuned according to the observed load on the NSX controllers. Lower values will result in faster synchronization, but might increase the load on the controller cluster. * - ``min_chunk_size`` - ``nsx_sync`` - 500 resources - Integer; no constraint. - Minimum number of resources to retrieve from the back-end for each synchronization chunk. The expected number of synchronization chunks is given by the ratio between ``state_sync_interval`` and ``min_sync_req_delay``. This size of a chunk might increase if the total number of resources is such that more than ``min_chunk_size`` resources must be fetched in one chunk with the current number of chunks. * - ``always_read_status`` - ``nsx_sync`` - False - Boolean; no constraint. - When this option is enabled, the operational status will always be retrieved from the NSX back-end ad every ``GET`` request. In this case it is advisable to disable the synchronization task. When running multiple OpenStack Networking server instances, the status synchronization task should not run on every node; doing so sends unnecessary traffic to the NSX back-end and performs unnecessary DB operations. Set the ``state_sync_interval`` configuration option to a non-zero value exclusively on a node designated for back-end status synchronization. The ``fields=status`` parameter in Networking API requests always triggers an explicit query to the NSX back end, even when you enable asynchronous state synchronization. For example, ``GET /v2.0/networks/NET_ID?fields=status&fields=name``. Big Switch plug-in extensions ----------------------------- This section explains the Big Switch neutron plug-in-specific extension. Big Switch router rules ^^^^^^^^^^^^^^^^^^^^^^^ Big Switch allows router rules to be added to each project router. These rules can be used to enforce routing policies such as denying traffic between subnets or traffic to external networks. By enforcing these at the router level, network segmentation policies can be enforced across many VMs that have differing security groups. Router rule attributes '''''''''''''''''''''' Each project router has a set of router rules associated with it. Each router rule has the attributes in this table. Router rules and their attributes can be set using the :command:`neutron router-update` command, through the horizon interface or the Networking API. .. list-table:: **Big Switch Router rule attributes** :widths: 10 10 10 35 :header-rows: 1 * - Attribute name - Required - Input type - Description * - source - Yes - A valid CIDR or one of the keywords 'any' or 'external' - The network that a packet's source IP must match for the rule to be applied. * - destination - Yes - A valid CIDR or one of the keywords 'any' or 'external' - The network that a packet's destination IP must match for the rule to be applied. * - action - Yes - 'permit' or 'deny' - Determines whether or not the matched packets will allowed to cross the router. * - nexthop - No - A plus-separated (+) list of next-hop IP addresses. For example, ``1.1.1.1+1.1.1.2``. - Overrides the default virtual router used to handle traffic for packets that match the rule. Order of rule processing '''''''''''''''''''''''' The order of router rules has no effect. Overlapping rules are evaluated using longest prefix matching on the source and destination fields. The source field is matched first so it always takes higher precedence over the destination field. In other words, longest prefix matching is used on the destination field only if there are multiple matching rules with the same source. Big Switch router rules operations '''''''''''''''''''''''''''''''''' Router rules are configured with a router update operation in OpenStack Networking. The update overrides any previous rules so all rules must be provided at the same time. Update a router with rules to permit traffic by default but block traffic from external networks to the 10.10.10.0/24 subnet: .. code-block:: console $ neutron router-update ROUTER_UUID --router_rules type=dict list=true \ source=any,destination=any,action=permit \ source=external,destination=10.10.10.0/24,action=deny Specify alternate next-hop addresses for a specific subnet: .. code-block:: console $ neutron router-update ROUTER_UUID --router_rules type=dict list=true \ source=any,destination=any,action=permit \ source=10.10.10.0/24,destination=any,action=permit,nexthops=10.10.10.254+10.10.10.253 Block traffic between two subnets while allowing everything else: .. code-block:: console $ neutron router-update ROUTER_UUID --router_rules type=dict list=true \ source=any,destination=any,action=permit \ source=10.10.10.0/24,destination=10.20.20.20/24,action=deny L3 metering ~~~~~~~~~~~ The L3 metering API extension enables administrators to configure IP ranges and assign a specified label to them to be able to measure traffic that goes through a virtual router. The L3 metering extension is decoupled from the technology that implements the measurement. Two abstractions have been added: One is the metering label that can contain metering rules. Because a metering label is associated with a project, all virtual routers in this project are associated with this label. Basic L3 metering operations ---------------------------- Only administrators can manage the L3 metering labels and rules. This table shows example :command:`neutron` commands that enable you to complete basic L3 metering operations: .. list-table:: **Basic L3 operations** :widths: 20 50 :header-rows: 1 * - Operation - Command * - Creates a metering label. - .. code-block:: console $ openstack network meter label create LABEL1 \ --description "DESCRIPTION_LABEL1" * - Lists metering labels. - .. code-block:: console $ openstack network meter label list * - Shows information for a specified label. - .. code-block:: console $ openstack network meter label show LABEL_UUID $ openstack network meter label show LABEL1 * - Deletes a metering label. - .. code-block:: console $ openstack network meter label delete LABEL_UUID $ openstack network meter label delete LABEL1 * - Creates a metering rule. - .. code-block:: console $ openstack network meter label rule create LABEL_UUID \ --remote-ip-prefix CIDR \ --direction DIRECTION --exclude For example: .. code-block:: console $ openstack network meter label rule create label1 \ --remote-ip-prefix 10.0.0.0/24 --direction ingress $ openstack network meter label rule create label1 \ --remote-ip-prefix 20.0.0.0/24 --exclude * - Lists metering all label rules. - .. code-block:: console $ openstack network meter label rule list * - Shows information for a specified label rule. - .. code-block:: console $ openstack network meter label rule show RULE_UUID * - Deletes a metering label rule. - .. code-block:: console $ openstack network meter label rule delete RULE_UUID * - Lists the value of created metering label rules. - .. code-block:: console $ ceilometer sample-list -m SNMP_MEASUREMENT For example: .. code-block:: console $ ceilometer sample-list -m hardware.network.bandwidth.bytes $ ceilometer sample-list -m hardware.network.incoming.bytes $ ceilometer sample-list -m hardware.network.outgoing.bytes $ ceilometer sample-list -m hardware.network.outgoing.errors neutron-12.1.1/doc/source/admin/archives/adv-operational-features.rst0000664000175000017500000000727113553660046025723 0ustar zuulzuul00000000000000============================= Advanced operational features ============================= Logging settings ~~~~~~~~~~~~~~~~ Networking components use Python logging module to do logging. Logging configuration can be provided in ``neutron.conf`` or as command-line options. Command options override ones in ``neutron.conf``. To configure logging for Networking components, use one of these methods: - Provide logging settings in a logging configuration file. See `Python logging how-to `__ to learn more about logging. - Provide logging setting in ``neutron.conf``. .. code-block:: ini [DEFAULT] # Default log level is WARNING # Show debugging output in logs (sets DEBUG log level output) # debug = False # log_date_format = %Y-%m-%d %H:%M:%S # use_syslog = False # syslog_log_facility = LOG_USER # if use_syslog is False, we can set log_file and log_dir. # if use_syslog is False and we do not set log_file, # the log will be printed to stdout. # log_file = # log_dir = Notifications ~~~~~~~~~~~~~ Notifications can be sent when Networking resources such as network, subnet and port are created, updated or deleted. Notification options -------------------- To support DHCP agent, ``rpc_notifier`` driver must be set. To set up the notification, edit notification options in ``neutron.conf``: .. code-block:: ini # Driver or drivers to handle sending notifications. (multi # valued) # notification_driver=messagingv2 # AMQP topic used for OpenStack notifications. (list value) # Deprecated group/name - [rpc_notifier2]/topics notification_topics = notifications Setting cases ------------- Logging and RPC ^^^^^^^^^^^^^^^ These options configure the Networking server to send notifications through logging and RPC. The logging options are described in OpenStack Configuration Reference . RPC notifications go to ``notifications.info`` queue bound to a topic exchange defined by ``control_exchange`` in ``neutron.conf``. **Notification System Options** A notification can be sent when a network, subnet, or port is created, updated or deleted. The notification system options are: * ``notification_driver`` Defines the driver or drivers to handle the sending of a notification. The six available options are: * ``messaging`` Send notifications using the 1.0 message format. * ``messagingv2`` Send notifications using the 2.0 message format (with a message envelope). * ``routing`` Configurable routing notifier (by priority or event_type). * ``log`` Publish notifications using Python logging infrastructure. * ``test`` Store notifications in memory for test verification. * ``noop`` Disable sending notifications entirely. * ``default_notification_level`` Is used to form topic names or to set a logging level. * ``default_publisher_id`` Is a part of the notification payload. * ``notification_topics`` AMQP topic used for OpenStack notifications. They can be comma-separated values. The actual topic names will be the values of ``default_notification_level``. * ``control_exchange`` This is an option defined in oslo.messaging. It is the default exchange under which topics are scoped. May be overridden by an exchange name specified in the ``transport_url`` option. It is a string value. Below is a sample ``neutron.conf`` configuration file: .. code-block:: ini notification_driver = messagingv2 default_notification_level = INFO host = myhost.com default_publisher_id = $host notification_topics = notifications control_exchange = openstack neutron-12.1.1/doc/source/admin/archives/arch.rst0000664000175000017500000001053713553660046021736 0ustar zuulzuul00000000000000======================= Networking architecture ======================= Before you deploy Networking, it is useful to understand the Networking services and how they interact with the OpenStack components. Overview ~~~~~~~~ Networking is a standalone component in the OpenStack modular architecture. It is positioned alongside OpenStack components such as Compute, Image service, Identity, or Dashboard. Like those components, a deployment of Networking often involves deploying several services to a variety of hosts. The Networking server uses the neutron-server daemon to expose the Networking API and enable administration of the configured Networking plug-in. Typically, the plug-in requires access to a database for persistent storage (also similar to other OpenStack services). If your deployment uses a controller host to run centralized Compute components, you can deploy the Networking server to that same host. However, Networking is entirely standalone and can be deployed to a dedicated host. Depending on your configuration, Networking can also include the following agents: +----------------------------+---------------------------------------------+ | Agent | Description | +============================+=============================================+ |**plug-in agent** | | |(``neutron-*-agent``) | Runs on each hypervisor to perform | | | local vSwitch configuration. The agent that | | | runs, depends on the plug-in that you use. | | | Certain plug-ins do not require an agent. | +----------------------------+---------------------------------------------+ |**dhcp agent** | | |(``neutron-dhcp-agent``) | Provides DHCP services to project networks. | | | Required by certain plug-ins. | +----------------------------+---------------------------------------------+ |**l3 agent** | | |(``neutron-l3-agent``) | Provides L3/NAT forwarding to provide | | | external network access for VMs on project | | | networks. Required by certain plug-ins. | +----------------------------+---------------------------------------------+ |**metering agent** | | |(``neutron-metering-agent``)| Provides L3 traffic metering for project | | | networks. | +----------------------------+---------------------------------------------+ These agents interact with the main neutron process through RPC (for example, RabbitMQ or Qpid) or through the standard Networking API. In addition, Networking integrates with OpenStack components in a number of ways: - Networking relies on the Identity service (keystone) for the authentication and authorization of all API requests. - Compute (nova) interacts with Networking through calls to its standard API. As part of creating a VM, the ``nova-compute`` service communicates with the Networking API to plug each virtual NIC on the VM into a particular network. - The dashboard (horizon) integrates with the Networking API, enabling administrators and project users to create and manage network services through a web-based GUI. VMware NSX integration ~~~~~~~~~~~~~~~~~~~~~~ OpenStack Networking uses the NSX plug-in to integrate with an existing VMware vCenter deployment. When installed on the network nodes, the NSX plug-in enables a NSX controller to centrally manage configuration settings and push them to managed network nodes. Network nodes are considered managed when they are added as hypervisors to the NSX controller. The diagrams below depict some VMware NSX deployment examples. The first diagram illustrates the traffic flow between VMs on separate Compute nodes, and the second diagram between two VMs on a single compute node. Note the placement of the VMware NSX plug-in and the neutron-server service on the network node. The green arrow indicates the management relationship between the NSX controller and the network node. .. figure:: figures/vmware_nsx_ex1.png .. figure:: figures/vmware_nsx_ex2.png neutron-12.1.1/doc/source/admin/archives/figures/0000775000175000017500000000000013553660156021727 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/admin/archives/figures/vmware_nsx_ex1.graffle0000664000175000017500000000722413553660046026230 0ustar zuulzuul00000000000000‹í]iSIÒþl~E¯¿.4u^Ƙ±g1°Û;"Þh¤BôºéÖ´Z–™ ÿ÷7«[G_ sîްŽÊ:;3Ÿ'ëÒÖ?¿]FÞW“Ã$þé%öÑKÏÄݤÆýŸ^~8ùyC½üçëµ­¿íîœüv´ç ¢p˜yGÞì¿Ûñ^nlnn‘ÙÜÜ=ÙõŽößuN<(cssïà¥÷ò"˯67Çã±X)¿›\ZÁáæQš Lš]íCaÁïe½—PMQz¥9ðm/ìf¯×^l}1W¯·»YøÕìW&}÷Ì·­Mû-$†qfú&}¶6§o§Y î°dPäÇ¢äY¦ MûæÅÖ0K¡Ë¯¡…~r‡ý4 üCx÷KœŸGFlmNDJÒ˜ Ûæ$¨b%‘­ÍiÑEFY²Ýûïh˜ÍªÎÒ‘Ùœ¦¿ º_l•qª\„ݙؤï1+1œ¦ÍšñçŸhÝCß×½?ë§âû÷Js­üN ›Y;IÎë¬ey·;—ŸŽ))o!ÖÉ®"3—œ¶¸H„"“/óÔRr‘¾›ãy³æ ;8,7Æs6Ówó7ÅÍç$¹\Bv‚øk0º‰B䬜<`_ †Du|&9ú rh.5c­YÒEY”­e©<Íú“^½d|]Ÿß˜üÅE›Úü¶×©×Õæ£9é–ÖÔ¼ÿ>F_ñ0$±”+= .BˆkÁ´ilÒã ŽªCÕ°ŠÔlåÄ|Ëks5uîàNÓ쟂݆ùÝANN»I7 l¼ø0Ág‚ÑÚŸ§çಳèôžÇápxzÞ½Ò¡É÷ÖD_MœéßA²kˆþã45=Âùi?5&¶o΢‘לGQÐ5•Z|ªR9Ywô%œBg8ÑRšháS)5'X3‚©$çm($ Õì B×=©A!©ˆ”\I¥éÓÇüÀ¸ƒÑÊÀƒy ð¼ú±ÉÊã8c/sÃ%šÿ  3̸\Éb¤À…®ä?ÖD>‚9HúëB¡l’@d‘€èã`&®Mÿ­Y¤BÞ‰‰ƒ8ó°w`²q’~ñbÖ-ðG#_"%RJ ‰Wëž >Àº¦ÖXYüÁXøöKPEAX9 U3jzRøsÖ÷@¨& Ÿm,¶¿  ÑmÒö Œ¡zO/¾’Ü…W.¼rXæÂ«û ¯¼är0ÊŒwôŒ÷è¡V¬Ö=B•1„RLQbaM»¾á‚9rt3|ä ÎðJlŒf͉$Kîføt†O¸>7Ãç ÉÍðýõfø[÷°V>#Ðp¡©ZR7Ã÷?8ÃGsñ•‹¯˜¹øêa¦ø&ÖZ€6ûalžËÛìÙ/ãkœÿ[ôw¬ %lºŠ …ðQ2 ³ÒÖËYãJI­9Ó”Ùݘ+’j›ð;Jà«›Â.‚”$”TADÍÒ–œÂgÅS”BÃÄ÷ÚxÎr á3.„Æ`LR™©ò•Î'}—R©…y!€GPK…0ÓÄN#î#©NÛ^…ñM!áõz'.øÈñNÛ xD~ÿP¤…ÀJº,z€ÅÝæÈ {<Àbƒ™èfšDÑâew€ýö)áÛîSj‰%ÅýÚ|ä‚5J™oi€’ˆ-%“7ç~¾{»W[ÜAº¾’g$iõÝÔý3 3í×Òûøí·;9Çr7c4~wj)o%o½«’hìk4…Ê 2µW[TÏ—H½p[%#ƒØk…h‡ÍM}Š$R\iΩ¢ÊEqGQå(Ê\nѸ¾à‡¦—ÕdA’1¢öúïæ_¸ðþAÃ{÷ .¼wá½ ïâ~Šãd”¹øþÑðÅð- —·¥áŒZ‡.0hÁØÞפájáé¦ÛxGà 4œ¯LÃ\n‚Ýå”A}ŽR’¥}•½K“s_#Î1Ò0™)w{…óUÏÏW 竞Y»ýå=LùÊ^¶#À×1)‰]¤“Ð!JaŠ%fެ9øä ºO(˜s€ÏÈjrûhu²3Íz<.…½Î’,CD"B)Îÿ9ÿ÷×"€-ëMÿ7Wä¼ _Ò°Wve³Šf©AÐ…®Íê+º1¹m¾%dÞ­É©¬ÿ&iköÙ8צ‹Õd<´¹æâíóò…t †'ÉòóÞç½…=3ÜOº_Ì,cë|~Iúc8 Ïæ¿ š\Å‚¾™ù¯vµ*žá%î$#ø6]Bü_Æ N’N7(µb¦Ú…ZW&×.{‘ÝÇÐŒo]¤´Ð‚d”-VÛí8¼ 2ÓPŠÚEZ6µ¦Ýä}ï†Ã¬¦âXÕt:—í˜A•ߘýDHM>Ê›¹÷ÁÝ4ÚÒK²FcìªTbÓ¾‰û%ç1­€ âhGìAóJ…yþ˜“A¸”|Õ€ ‰_YDÓ€&4W‚Et.LéTg¡Ì3MŸôÂs <¶Õ»¥'=-” Ì7ÙÀÈCè¯õþnG®QW^PÉþ&É»PŸ‰½‹ ®g9H2³|gÓÐÄYexÛ‘~* ʲtáÖÓ¼'ôe¸„¬5ÎÅ&sÐy“dYrù>Hû%À-‘¯iÑçQdUƒž¼g¸j¼sTÔð6IÃ?’8ˆ Ýa\S¹fEݤgz­½ÙÎþè½ý5úíFæ?o¢ÏïÞ&Ûìßoþý®³w±}qøq|öéרÛßϽÃøóÅÙÛÑ6¤ïìñ“òëïŸ? íwŸö¶;¿ƒÌÏߢñÎ/×7ßœg?2 Produced by OmniGraffle 6.0.5 2015-02-10 00:00ZCanvas 1Layer 1Physical RouterNSX Controller Network NodeVMware NSX pluginDHCP agentNeutron serverTraffic flowPhysical connectionPhysical Switch Compute NodeTenant 1 Network 1Tenant 1 Network 2VM1VM1 neutron-12.1.1/doc/source/admin/archives/figures/vmware_nsx_ex1.svg0000664000175000017500000004672313553660046025430 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.0.5 2015-02-10 00:01ZCanvas 1Layer 1Physical RouterNSX Controller Network NodeVMware NSX pluginDHCP agentNeutron serverTraffic flowPhysical connectionPhysical Switch Compute Node 2Tenant 1 Network 1VM2 Compute Node 1Tenant 1 Network 1VM1 neutron-12.1.1/doc/source/admin/archives/figures/vmware_nsx_ex2.png0000664000175000017500000026566113553660046025422 0ustar zuulzuul00000000000000‰PNG  IHDRKþ|4 AiCCPICC ProfileH –wTSهϽ7½Ð" %ôz Ò;HQ‰I€P†„&vDF)VdTÀG‡"cE ƒ‚b× òPÆÁQDEåÝŒk ï­5óÞšýÇYßÙç·×Ùgï}׺Pü‚ÂtX€4¡XîëÁ\ËÄ÷XÀáffGøDÔü½=™™¨HƳöî.€d»Û,¿P&sÖÿ‘"7C$ EÕ6<~&å”S³Å2ÿÊô•)2†12¡ ¢¬"ãįlö§æ+»É˜—&ä¡Yμ4žŒ»PÞš%ᣌ¡\˜%àg£|e½TIšå÷(ÓÓøœL0™_Ìç&¡l‰2Eî‰ò”Ä9¼r‹ù9hžx¦g䊉Ib¦טiåèÈfúñ³Sùb1+”ÃMáˆxLÏô´ Ž0€¯o–E%Ym™h‘í­ííYÖæhù¿Ùß~Sý=ÈzûUñ&ìÏžAŒžYßlì¬/½ö$Z›³¾•U´m@åá¬Oï ò´Þœó†l^’Äâ ' ‹ììlsŸk.+è7ûŸ‚oÊ¿†9÷™ËîûV;¦?#I3eE妧¦KDÌÌ —Ïdý÷ÿãÀ9iÍÉÃ,œŸÀñ…èUQè” „‰h»…Ø A1ØvƒjpÔzÐN‚6p\WÀ p €G@ †ÁK0Þi‚ð¢Aª¤™BÖZyCAP8ÅC‰’@ùÐ&¨*ƒª¡CP=ô#tº]ƒú Ð 4ý}„˜Óa ض€Ù°;GÂËàDxœÀÛáJ¸>·Âáð,…_“@ÈÑFXñDBX$!k‘"¤©Eš¤¹H‘q䇡a˜Æã‡YŒábVaÖbJ0Õ˜c˜VLæ6f3ù‚¥bÕ±¦X'¬?v 6›-ÄV``[°—±Øaì;ÇÀâp~¸\2n5®·׌»€ëà á&ñx¼*Þï‚Ásðb|!¾ ߯¿' Zk‚!– $l$Tçý„Â4Q¨Ot"†yÄ\b)±ŽØA¼I&N“I†$R$)™´TIj"]&=&½!“É:dGrY@^O®$Ÿ _%’?P”(&OJEBÙN9J¹@y@yC¥R ¨nÔXª˜ºZO½D}J}/G“3—ó—ãÉ­“«‘k•ë—{%O”×—w—_.Ÿ'_!Jþ¦ü¸QÁ@ÁS£°V¡Fá´Â=…IEš¢•bˆbšb‰bƒâ5ÅQ%¼’’·O©@é°Ò%¥!BÓ¥yÒ¸´M´:ÚeÚ0G7¤ûÓ“éÅôè½ô e%e[å(ååå³ÊRÂ0`ø3R¥Œ“Œ»Œó4æ¹ÏãÏÛ6¯i^ÿ¼)•ù*n*|•"•f••ªLUoÕÕªmªOÔ0j&jajÙjûÕ.«Ï§ÏwžÏ_4ÿäü‡ê°º‰z¸újõÃê=ꓚ¾U—4Æ5šnšÉšåšç4Ç´hZ µZåZçµ^0•™îÌTf%³‹9¡­®í§-Ñ>¤Ý«=­c¨³Xg£N³Î]’.[7A·\·SwBOK/X/_¯Qï¡>QŸ­Ÿ¤¿G¿[ÊÀÐ Ú`‹A›Á¨¡Š¡¿aža£ác#ª‘«Ñ*£Z£;Æ8c¶qŠñ>ã[&°‰I’IÉMSØÔÞT`ºÏ´Ï kæh&4«5»Ç¢°ÜYY¬FÖ 9Ã<È|£y›ù+ =‹X‹Ý_,í,S-ë,Y)YXm´ê°úÃÚÄšk]c}džjãc³Î¦Ýæµ­©-ßv¿í};š]°Ý»N»Ïöö"û&û1=‡x‡½÷Øtv(»„}Õëèá¸ÎñŒã'{'±ÓI§ßYÎ)ΠΣ ðÔ-rÑqá¸r‘.d.Œ_xp¡ÔUÛ•ãZëúÌM×çvÄmÄÝØ=Ùý¸û+K‘G‹Ç”§“çÏ ^ˆ—¯W‘W¯·’÷bïjï§>:>‰>>¾v¾«}/øaýývúÝó×ðçú×ûO8¬ è ¤FV> 2 uÃÁÁ»‚/Ò_$\ÔBüCv…< 5 ]ús.,4¬&ìy¸Ux~xw-bEDCÄ»HÈÒÈG‹KwFÉGÅEÕGME{E—EK—X,Y³äFŒZŒ ¦={$vr©÷ÒÝK‡ãìâ ãî.3\–³ìÚrµå©ËÏ®_ÁYq*ßÿ‰©åL®ô_¹wåד»‡û’çÆ+çñ]øeü‘—„²„ÑD—Ä]‰cI®IIãOAµàu²_òä©””£)3©Ñ©Íi„´ø´ÓB%aа+]3='½/Ã4£0CºÊiÕîU¢@Ñ‘L(sYf»˜ŽþLõHŒ$›%ƒY ³j²ÞgGeŸÊQÌæôäšänËÉóÉû~5f5wug¾vþ†üÁ5îk­…Ö®\Û¹Nw]Áºáõ¾ëm mHÙðËFËeßnŠÞÔQ Q°¾`h³ïæÆB¹BQá½-Î[lÅllíÝf³­jÛ—"^ÑõbËâŠâO%Ü’ëßY}WùÝÌö„í½¥ö¥ûwàvwÜÝéºóX™bY^ÙЮà]­åÌò¢ò·»Wì¾Va[q`id´2¨²½J¯jGÕ§ê¤êšæ½ê{·íÚÇÛ׿ßmÓÅ>¼È÷Pk­AmÅaÜá¬ÃÏë¢êº¿g_DíHñ‘ÏG…G¥ÇÂuÕ;Ô×7¨7”6’ƱãqÇoýàõC{«éP3£¹ø8!9ñâÇøïž <ÙyŠ}ªé'ýŸö¶ÐZŠZ¡ÖÜÖ‰¶¤6i{L{ßé€ÓÎ-?›ÿ|ôŒö™š³ÊgKϑΜ›9Ÿw~òBÆ…ñ‹‰‡:Wt>º´äÒ®°®ÞË—¯^ñ¹r©Û½ûüU—«g®9];}}½í†ýÖ»ž–_ì~iéµïm½ép³ý–ã­Ž¾}çú]û/Þöº}åŽÿ‹úî.¾{ÿ^Ü=é}ÞýÑ©^?Ìz8ýhýcìã¢' O*žª?­ýÕø×f©½ôì ×`ϳˆg†¸C/ÿ•ù¯OÃÏ©Ï+F´FêG­GÏŒùŒÝz±ôÅðËŒ—Óã…¿)þ¶÷•Ñ«Ÿ~wû½gbÉÄðkÑë™?JÞ¨¾9úÖömçdèäÓwi獵ŠÞ«¾?öý¡ûcôÇ‘éìOøO•Ÿ?w| üòx&mfæß÷„óû2:Y~ pHYsgŸÒRÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|ÕµÿÏîª÷j«¹W¹wã¸é½ ÁÔ4^:§G äŸÇK%@B Ó»1.`Ü{“{“dËVïeËÿœ»šÑìje«íjVúÖÓîÜ{ç;»£9÷žBè KÏÓOËÉY–ï88ÃJÎl—Ëír¹¢##B’¬Vkˆ^+ í"àt:íuõŽb‹ËUí²ðÇeÙ™:kCNÎ\{»*Baè²”“³2$ßñõ§ë‹Å2ÇérFv¸8@ M¬VKÓåúÂâ²,Îõ(Nm†B    ÐaíR–rr\ÖüÆ'o·Xè¿yiˆÖj|l ÊH¥ÈðP  ¥°›vKhh´S=jêèpþª¬®Õk²å°ÅâúuzÈì— 4éX°   ]J ÍÊÒ}9¿ëã´×¿ÍJÒléA|LÍŸ>ŠÆÍ¢ô”„.í*hI àL)í<˜O+6ì¡òªš¦– ¡D7üõÉGO´<{@@@@:C MÊÒ}=5Ñ鲿çrQÿȈ0ºlöxš3y…†À-©3ðq.t„€Ýî U[÷Ó'k¶QUM=ñLo‘Åj½í¹Çù¼#õáßΩ,=ó䄯F×"W´Ì =pÓ”/bœ.GúæU+ß1_OÑ#.>g–/^lãJÿ-—rÉÌqÔ' >JÁu[ÑÛÞF ,4„¾yålöÀÖx.ºížGž˜ÙÛàzA@@@ « øT–Vlß‹‹\#c¢"hÞÔì®nõø@fŸDºpÒp-hË3~hU‚€€€@¯"àSYrÝ(DQ’k€@p¸ò‰j q±ÿÒ´ûrž½F/A@@@ÀœZ(Kb‚gµXæIwÝNãæì8z Ð’@td8ž¥f—œ×­-K`€€€€@[ ´P–VîÙ?ÕérÆŠ ^Vߤ¶Öƒr &!0uô wOœt‹Iº„n€€€€@Ph¡,9eº3835(/ÞN`ÌÐ,ââNö;Œ0â½ýÛ€ëè Êû:ÄH…Qa©ç‚t›’ãbìÒ|ccãÐnêš 'ÐBY²8I)Káa¡Aqqe5´çp>å–Ãá Š>£“ ào}SâÜ9Ô,®aþn õƒ€€€@O%Ð"Ô5Äï´;(ÜÄQð*«kiñçUΚºzýÞØ¬VJOM !Y}èš¹“(2³c:­¼ðÞ*Ú¸û°Þ£ùӲ鯋¦ëÛÆ•w–o¢¥ëv©]¢ÀÿáÇ·ûuýw/}BÇN«6FJ£‡n¾È¯íueå© ±JYâ°xML]Y;êÞA ¥²d¡ûÌ„bÓž#ôÚ’uT]Û¬$iýs8j†If™rЃ7-@B] N+ËÌó­eõ£ßw…‡ù÷+ YS²rS.?i$¥%Çw«ucYãz‹‚~Øaç™J»ƒé³8ž}öCs]Z¥˜â5 F 4X‚€€€@; ´0Ãkçù-~(ï4ýãÝ/[(Jòr¢¿ª>WÐSÿüˆäH멼²Fÿ°ÏZë…ýtÄÉ¡ÞZ¶ÁOµ£ZèÿN!t¬O>Ï’êW?]ëqlxÿ4ºéâi”Ù'‘÷[èdQ)½ñÙzÚwì”*W[×@ÿùd-=vÏÕdQ™g.œ1–®7™Ñï_^¢Ñ—Ô@ägߺœf¸smÕÕ7ÒK×ÓÚž÷@;éÚyShá •žKÛÕæe|L”ò 4QWmÉ¥9S²Û|~Gï¥4 Ñ_ÿl­Þºß£=ÎQD÷\{¡Ç>ï 2ñn Û    ÝC (”%™98]êž)LY}iÐY’æÎ7”äãK$Ôø“/~HQ¯5‘—§ÿù1}÷–4‚g|ÉÚÎy–¼ECÉ¿ä-ÝÁÄ»Ø  P–Å”Qhù†½ŠŠÌ}ÄÓ$_‘·ÈKî²õ{ôÝâw2axµí=;³ÿø)üKRHf­J9úž&âÏâ±X|ϘHÔ¿“E¥zt8­mIˆkM‰‹à2£HäÀ¸˜f“À‚VÌç´¶îÍ+.:ŠýŠÜ,[;§³ûÅ òê9“è•¿VUµâÝ»oí¹—’DW¢ŠÙ ˆ/¿4‰hhô×Rù?ïvÁDkKÀðm‡Ø>´©µËgOðȽóõö*b™ñ…Vf#þüÆr*2˜éÖ¢9ò›ÈèÁ3Ipq‰J§‰(ao/ßä13aäípÀ–ÿùd­òÛÑ”÷#Í3KðA‹¤ç€V|~4‘@Zd?mß¹–u_‰:(Qó4ùlí=òì^’ØöÇÿ÷†úHnª®Yã‡S¿´¤³VÕÙ{98³^¿|_önæ& ·Däó%ÝÅÄW_°@@@@À¿<§%üÛV§j——v1ÑzË%MB;¯ßuˆ$ï’ƒ3†ÄµÒ˜øÿÜ~éL½]Q,.:oŒ®@ˆ™ÕÿøPÍ–„…Øh÷á<Ê+,ÕËšEX!Ð"Égô¯ÓØ!YTßhçùåIB[k24«ù¥_ö‰r$&{¢Läèb®x6 k ®•ykÙ&•ô÷¼±C”é£Dæ“0Ú"Aîñ¿¿¯fêÄLp㞣õÏŸÖìo¤Õב¥Ô-íŠ"Öštö^^3w’J<¬Õÿ—ÅËy–±%òwæÀ‰Sß­Œ¶ì&ZÛX‚€€€Ž@Ð(K‚D^Æ9YìG_nU³²OüqNú05“hf‹®›£ò&I9M.feIÌ®4Ó+Q&$¡ª·H° í/‘ÜB¾Dr'¥sÀ¼Â•ãɻ̨Á™l’8Hß- áÜ)Ù´’s/ibœ%IOIP‘ßZ‹ ˜=0C…)—Ù‘œWjãnÎõÔ?])K¢4í8pœ¶ì=¦ŽKt¸¥ëv©uãGö§…3Æwuj}Xÿ4šÌaɵÜT¾*ë̽˜‘êÁMfá¶qèsMyVMf‘Dqõ–îbâÝlƒ€€€ø—@Иá Ñ/.9–~r×eä+ì·†j&sxüþkiˆ×¬‹—`?¼ýºâ‚ ­&u6z0=ú+;[ëÇÙ–|)>ÅÂ>Vß½eò¥ áÙ.MÄ÷ê¾v9æ-7/œNs8‰«øûeƸ¡*‰ìÙòRÅD…Óuó§´8×XÏ¢ëæ*ÿ0o“?)#ußvé º÷úy¹¡Œçwt]úedà]Ogï¥p»ñ¢é-¾2üáo,äò­«è.&Þ °     à?-ÞÙÌùõ3ìøþµ——U3Km]åsÄ4™…‘™™E‘0К?ϹúnçYªSEu­„8°Bfj¢:?ÜË4í\õtöø§_ï¤÷WnVÕˆrðçŸÞ¡Ö%Ì€EG†)ӸгOÊä䅪ᄷ˜BóÕjKÿ$¸DΫf.›Õ¦rMy‡V—Ù—SÅ効|qDa•€2ÖÝÒ™{)œŸ*¢RÎ5“·'O”Y™¼Ã¾w2È?‹ß=ÿÄc?éîûƒöA@@@  œýíÛäWFCûõUŸŽtUòeõMTŸŽœïïs$bÛ˜¡™mnFfÞÄ„¯#"3WEÏIÏ»QžDA:Û¬ž÷9Úî̽Îò=ꈘ™IG®ç€€€€4èþ)æ¾` @@@@@LCÊ’in:     `&P–Ìt7ÐÓjŸ%ÓPìdGfŒBÃú¹ó%µR¼“Màtv€²ÔN`þ(žEò€€€€€˜‡ÌðÌs/Ѐ²d¢›®€€€€€˜‡”%óÜ ô@@@@@ÀD ,™èf +     æ!Ð+<¸\.óGOLMÑM}{Ð9(£,‰Bäþ;YDgJ+©º¶žjëùS¯Ž”, :a!!NÑaAC²úRDx(‰%*èn§);|ïcO'§}$Y)Ñ夗ŕ`!Þ‚€@¸,–z‹ËRæ²P)¹,E–°¯ŸÏùQQªÂ)½„Àƒ9¿ÞØèo±ZøäJÂ3¨—Üx?]&OE8ùOX¹Õâ*åµR—5dÏó¿úÙN?5×mÕµ²¤)Hv»ƒ6ì>L»æÓ¾£TÓ`ï6 h¸ç°Y-48«”I3Ça*ŠSϹ½¹~FYî}ì©«]世Uî…Ngc_Õ0ÿyQÂi0ïÝÄ‹öABùi_"{­sÑ#OlqYiI¸Ëõ⟟xìHû+Å=À½ýz†ÓåºÕB®ËCäúXQr_¦áëÓÓ®×(NÒ¾Näl¤Eþª€o–¸l–·þþø#Ÿªþl'(•%MIj´Ûé«mhéÚ]TVU«sŠhl ´Š2Šhh p{£úðÔ’~+ à‹€Ãf£ÚÐ0ª ¥ÒèX*Š¡Ç Õç³µ;hî”lš?mEòÌ“ÕjÅl“/ˆØ§Xô误Yôè“ÿï%ãd§¼Ô†‡…ÒÀôdŠŽ §¨ˆpŠämÌZêȰÒN§ƒ-(¨¦®ŠË«(ÿt)ÏRº¦ðüôžGŸø—%$ä‰çs~v¼U£x p_Γcœ®_;Ž+årä-($ÄÆÏ Šcë õ j²žè—‹Kèò>.\5uõTUSOGÙ²«¡ÑžÁß¶o“ÃõmVœÖ[¬ÖÇž{ü‘Ï»¡{]ÖdÐ)KN'k°ü‘ò¯÷×Pÿ‰««¥ñÇÓ ¢BJ+/%Aé2H¨¨w(Ц#©i´3sŠO O¿ÞI«¶î£;.›Ic†fé ^v{ç÷£µ«~(ç7 öÆ7\.Çl)#/$çO¦¾32SicEþ PY]K»І]‡iÏáüPþ3xËÞøÍ{yâçÏ=ùèÿú£MÔi>‹/¶-Û±ï7N»ó‡ü&dµYm®óƶL9€F H§°P›ù:õv‡ƒž8M;§Õ[ˆâ4Ýåp.½çÑ_}rç³?ÿyi0^(›Ë{ Û³>ÃÓ´?¸ø¼1tÝü)ž»qK´WQ’'­Ûq/ÛHv^e%iÆ¡½4îÄQ²±üA`_Z&­>šŠ£ãx&€èŠÙãé"þØlV]iòG»­óå›héº]Ò×ß=ÿÄc?éh=8¯}Øj6Y\oòã*MüÝLMó§óldxXû*Biè$Ãùgèƒ/¶PîÑ“îš,´84$éî¿æ<èaìdý8Ýœ¾›óǸúÆŠ×y&ûRâ‡ÑÔQ-WÍ™H©‰qæì0zÕc TÕÔÑdþbË>³±ó¸²åYB® FŸ¦Ã ÓæÌ¿Äát7„G@³óLš DS”Ä7éãÕÛè½/·±}¤‹†æÓÍWSVi1{Hc&É·ªÇv!¥ª’ÆŸ8B5áát*.‘ö³y^Ye júÈì’™f˜ö) Cy§EYúzóª•A=ý,_ª{ýõulzð!?‰â³ú&ÑÝq)MÑŸBÙì&M窂Õì=rÒÅ>*c\®ÚË'_¶ðµÍË–Õº?hÏÿ~óLB­½ò+nifXX¨ëžk/°\Æ{Ѹ&ÂïH™4~X? ?ƒ$èZ»ÄÜ9}΂՛V-*Ó`ÓÛƒˆ¢äài=Q”¾Þ¾Ÿ–®ß£î÷ûwѵ[Öºý‘ý @{½’€g6îÚB woáñ:­Ýyˆ–­ß­¾›ò•ï*¤w¸ç±§¦òÛè+<š:mô`úé]—Qr|Lï„«6 '¤Ýq‰%>6ZQã©ÒþnNÎbLušê.u¾399.kMcõ«J2£T[WGo,ÛDlHÙ'OÐìƒîÙ¥s]Žƒ€¿L>vˆ&œ8¬f“ÞäïfMmò§“ï,f˜üEݤõ6ÚÿÌo iýÓ’éîk.P>m&í)ºÕ‹ d¤&Ò½7̵p€~WqÝÎþu7öb=êÒ=öôXvÙþ¹\Ô—ϲÈH>ÌF@"1>pÓ|JIˆå®¹†;ªO›­­õÇ´Ê’Ì¡±±‘VoÛO%åÕS_K—îtÏ.µvAØ"°`÷6J¨©¦ vb\¹)—ä»*³ P–uº¿ûýÕ<~ó¼Òf³¹î¹îBŽ2…¥î¿+èAkFL§Ë/˜àìd¡ßÿèw¿‹n­,öWããüòi™:jÉf%¡%ø;.Ü-оYûjì—)•%ÍüÎÎy”$êÊûTŸÏß¿›BH8k¼Xï>}Q|çDVs¾¯rø ßYÌ.uß= dËüœ²8\–ßK›s§Œ´ ÚT 飭ޏø¼Ñc$¹úUTÔÿ¢£õàØòe.8A§G$ðwõqâ?y¨üKf 'Þe÷ )‹ë±#˜4|€%2ÁÅðµã‡÷#-n/­¨Nh8]u%÷ü­àé=zj$à²ÒÅÄ)&§ŒhÜu05Im0zH¦eûþãäjtÞÄ}ÜÌ6¥²$#󒸦¶žŽ+~ÃN˜™ã9ûf·ZiWÖ@Úß'“òRR©±·)Fç$ä.Ê T¿â3*‡Ö˜Á‘hx7WŽKÇ K¨º¦žÂÂÂ(„¿ÃV¾çfÊ½ÔÆ[€bm Àƒ:–{{Rò*ÑÄìm8E@À\fŽòñšíìc鼓{eÉ\·§M½¹/穎FûPÚA#¤µé³˜Ì ¾(Kì¾e©½7Ef–Üy•ìt¬ ˆù¥3®®–R+ËÛ[•)Ê;¬6ÚÖo ­’M•ázŸâc£h`z E†‡RDXh¯u oh´³ [#Õ²©åÑ“EÊïçpJ_’ÏWC³iÆÁ½4ÎäJS2'¬¯­¦òÈh:vª˜b¢#Tèh›Íª&ý¦c¥ÇxèžÆÏª~áá¡®ìén‡ùsu¸Þ@`Ú˜!¤”%‹kaNÎÊœœ¹p²ÏËçI—f¤P8¿G@@ ˜ŒÖŸ$:'ç]ýPÎo2þœóSÓΊ˜jfÉm‚çV–ù%º¼ªFÝ÷„šª`ºÿz_‹bâèÝI3¨$:Ví“$•s§Ž¢1C2)-%^/‡•f'‹Êh÷¡|Z±a•T-=‰¶ B×qb1Ó3«Ä×Ô(e©¼²Ús)TEÆÃì’YïZÇûewºÈÙ© 1QŠ! lÄ¿%,4´¾¡±1ü´}Ã0îÿÞ`»†ÞÞ_‹Ë™È–À”Àƒ¯6áa!”œã:]RaáI’!Ü(Km¹‰neÉÉù”$¿GÂãÌ"Qõîe[ê0K™½iYô鸩ÔÈþ+’MûŠó'ÐŒqCE‹6KMÙô”’ÏÜ)ÙôÕöôñêíTÄ=}iÖ|º|ûFVhÎßRtƒû;ZÅßYBÜÞ¦x¦ü¢u¶SNg©").¦³5á|è6© ÑÕùgÊÂäÍ€²Ômw¢c [ÈÇ^ÞÖ± pt3ŽÌiee‰¿Å.‰y¿º›»Ójó¦{swçWr¨Ñyyñ‰nhhõÌx`kÿÁôÁÄó”¢4jp&ýò;WÓì á(µãfÉhý“FÐc÷\¥l±ë9Æ;“fÒ®L5 ßŽšS4ª¾^5TÅ~vâs'¦¤ò]†ôP‹[YâÙb+ŒÔ‡ôÝérŽ ÖkèÍývY\*ü]ÐC@  ¤$¸ÿ†²Ž(K¦S)KÚÌ’¦05p”4[åV:œšFŸrç:X8c }÷–‹(*‘Y;ú f?¸}!Í›š­ªX2v2OJíhu~;ÏætWù;«åZB¾%¿áîöŠùY)ãH–VQQáÊߎ#;BëÆ›èr)G%X¬ãÍCŸ…@¸žÈÝiêÙ¦S–Œ~KÁ62_OïóŒ’Ëb¡ó'§kçMáhhøAt–€øüÜtñtš>f0IØõwy†©4ÊœÛå;ëþ¸”¿’|Ÿ!    ÁIÀdÊâå—Kí…“7‚ŠêÒ19oR‰éÝ­—̪¾Cgï¼b6 ëŸFuœ“jÙ¨ æì²áû+ßå û ›“)z   ÝDÀTÊ’0—Kí%3˜^4÷¥eÒ‰Äü›WÎâјRêêï´ø1ÝuÕlNúj%1w”ðâfùÎ:Ú¬’Ùz‡þ€€€€´‡€©”%ÍdÉ­,ñ 'ÿ ³»/FŒS]½dæXŠAOÝ7 ¿¾`ºn"Z™=Þ_Ít¸^í;kü.w¸2œ    ЭL¥, ýeS¥àЕTÀ²¨hŠåd¤ç~‘ïÖ»ÚÿtÖ8ŠŠ#Éc•—l®«U_[÷Wû.›«ƒè €€€€@[ ˜NYjkÇÍT.7=KugòÈ‚žþ¾7’©|∪™}MìýÝ&êè} ,uòž‹ Þþ¾™ª–IÙ;YNo+IÙMÊ'ÿ5»h&yfï'ú    àIÀ4ÊR°¾P–DÇPMX8Eòl‡Djƒ†@ö L•ã¦2"’È›+<¿æsh@@@@À_L£,/0˜^6Ë#ÜÁR’b‘SÉxý¼.ÑS™¹Hy¤¹”%?_:ªS*Kºö.i¦¢éE=9ÞýâÞ%•¢’6HIhR–L6³Ô¦Î£€€€€€é A'oQed¤ª!9¾ëÃ…ï;vŠžye‰ÞÃìAôýÛ.Ö·½Wþ²xí<§ïþ墫)#5‘^xomÜ}Xß/+1Qôÿ~x‹Ç>mã7ÿú˜ŽäŸÑ6Õò\mK~¡¿½µ‚œ.'…Úl´èú¹çûc#©IA­ˆìzöþè/ê.P–:y¿ì÷äœÍjëdM-OÞ¿/‡#¤ÊêZuðÀñBjh´«Ä·Þ¥íví;zRßž’ %Ùáˬª¦ŽN—SZr¼~ެH=ÇO{ì“ _u mÍ=FÛ÷W»ÂB»ž…±-m=”“ÓŠh÷@Û%€€€€t˜áuE?ÕaáH{“Gº£¾Iv‡ƒr ‘±ÙýÇO±"åÐwM5P_omåЉÂ‡Žž,"‡ÓÙbÿÙvlÛwœ¾álEp @@@@‚Žf–L~ËDéùbs®ÞË݇òiܰ~ú¶¶²ë`³ùì›:j°vÈc™M¥•Õ9ÞJ‘ÖýóµUÊê›D}“ãômãŠ$t?&‘ƒ>f–å¹g›âc£H«Öd;Ï&mÞ{ŠRk€°@@@@ è @Y2ù-dK<šÜ”€UºZ\^E§ŠÊ=z}º¤’ΔVèû¦Œ¤¯{¯ˆïѬ>j÷é’ ß%£Èl“ȬԳú)‰OÔÀŒý##     ГÀ /îædV~VlÜ«÷tסùšÖlÝï±     Ì0³wOf‚âšMñÄoÉ(»YyÒd`F*‰¯ÑÙdHV_ýðá¼f%cÀmöI/ˆèe ,É Ÿ’=Pï©B\vH¼ýœI“)mˆ‚×')–ó,…«SŒ~Kš ^çIꟖ¬U‰%€€€€ôJP–‚ä¶OÎnöC2†Ï=Z BŠk—1ÅPNÛçk)¦x"ÇN«ÜJ²®)NÒ“ÉÖ”ÃHöC@@@@@ 7€²$w]”›¤ø½·ZT<£IÞÐ~}=ÌõôÂ>V7™â9N’ÜJU5õ$D`‚çv€€€ô:P–‚è–Mñ4%ÉÜ¡-‰hµËÒ4³$ÛäáÁwi0Gƒ€€€€€@o'e)ˆ¾FeHBˆoÙ{ŒJx)báã“GTëmùOÂ~Ë9"’[ÉèA"áA@@@@@ ·€²D߀é)”bˆt÷öòzï‡H£¸˜H}û\+a¡!ԓ׊H`Í_I"éµ§žsµƒã     ¬ ,Ù3F»“Ù%MΖˆV+ã½Ü”œVr+ifxðWò¦„mÞJÊRÝycT<­ëV«…& m¶y983¥EY_ûZÂèBzÁ5ö¨Kì—–D’'étI¥~]Ùƒ2):Ò7IßÙ†_³HÆ„µm¨E@@ Û ØíªærMb¢#ÈfÅX ÆKÿ¨ª©#‰0,b£¨¼—ù¿—h¡# ,u„Z7Ÿ#¹”>ùj‡Þ £iž¾³ +âŸI•ÕµªtxXeõMlÙ( `v­ÚF­ÞÖj7ÃBmìGi)q4oê(’ÔF‘2¯|òµ¾ë—‹®¢„Øh}ÛL+;æÑóo¯Ô»ô‹o_Iý9_\wÈŽ'诋—ëMÿrÑÕ”‘Šçª+½†@gŸAê©?¢3¥îÁáéc‡ÐÍO3-¿ÿ}å3*8Sªú7vX=xÓ‚né+žA]ÊR×3õ{WÍ™Dòi«|çÚ I>¾äw?¸Ù×nµïGw^Úê1ï߸l&É `.—ë¬iht¨?ìòÇ]£ã†Ò7¯œ­Ÿcw:¨¦®y¶æÕéça@„@gŸARGm}½þjhh”]8Ø)9óX»ã ­Ùºß|C@z<ƒzÅmÊ‹ÄÌRPÞ6t@ÚG`Ñõsiû<Š8yšèHAmßwœ6ï=ªöɲéÞì‰Ãõí`Y74‹žþÞzwż `.=ù$¤xûBrðŒ¼Hh^¯ˆòîf¹‘¸ 8„Ø(?EMRãhÚèÁTóêRÚ{¤@í.¯ªQ&/Q¾Æ44Úiס|:šF•Ïbåk˜‰‰j.”•°ò*·¤90ÂÃBUyí¿ªšæt²/«O‚GßÄ|gKî1*8]Jgʪ(œý«⢹­ ”™ªU£/«Ù\ðØÉ}{ø€¾¦ok+å•5t8¿ˆòO—¨€‰ñÑÜv"œ©i±‡mÉCwª¸œ딫dÞñì»5¢Z·ùEµè$v€@èŠg\f>?rž¢Ââ2x§ž Æ€Uuõ´ïØ)HBl$IžJo‘”)ò, ±Yhô,"HkçÁÊgJR¬Hÿû$ÇÑäì>Ÿ/'‹ÊØlÐm*Á·Z>«ÄœùpþiÊ+,¡SEå\O(%%ÄÐXn;žë÷%xù¢Ø}P–Ë­€€© ÊHÑ•%éØé’ ˜Ñò|%Gzzö­tüd±Gÿ%èý×ÏÑ•˜ýÇOÑ;Ë7éeîºê|:³²nçAzkYsRí_Ü}%iáŽ,¢—>üJw”6ž÷á—[¹o)ì8=_§ÑŽIbísxXµe½ùùFj´ÛµÓôeZJ¼ò¹ôr!/?/¼·J½Øè… +™¬h=À}IŽ1ìÅ*€@{´õ$u®Ù¶ŸþÃg¼}(/œ<’nºhÙlV‰î_®¦ÚºÕ$zâÁë=º$2~c™^fܰ~º²$u¿³|#­Ø¸—gŠÜÑíŒ'/^º®;‰ær`£¼þÙzý¹å+ÀCiE5½øþj:ÀÏHo‘~Ï?Œn½ä<5 £Ç3H#ѽKø,u/´ ÝJàPÓ,‘Ö‰ÖÒüåå-%9§¬²šžùÏ’‘W‘©£©¥ößNŽç-»yvJ“¾À­Øì;VHvGóLŒøMi"/#šÄq†Ò•Y䀻nÞ$.ÓG{K*j´¢g]~̹¦DY¥g_ƒÖ—™lö"y\ÄôOÄèç°iÏQµOþÃþÆÔ©ü¢¦)KrüÄ©b(Kg!ÐÙgTmüýŠ©›<ÄRDRTÖÔR@â§(²ë@]|Þµ®¶ ³Ûã‡÷çÁ›:v‚Ÿ‰FÓ»!ýú°_”{IÌt¯¸`Üì¯d4Á“ƒbâ'Qð4)f…iù†=ê#ûÄÔm‹¸`âˆVgÀ´se)ŽÚEMO1Ýñyùò~ÓÊÈHõ×Û’˜ž(,&ñ©‚€t=¶<ƒ$Ê¥÷Ìw,›åÅA” MY’çÓ.~öˆµ‡£ʳA$ªéYåÞ"JKN ™éó@ñ%Úy0O}d[žmG  ùÓFQzj‚ì:§œ)­ô(Ó‡#‘Ef¦Œ³^Æcxitß:”¥îc–A@ `$"„¾Õ$œCk§pèl™ÙñVW+§-c£=_J´ý­-EÒ”¥Ââ <ÁÊ –¬k25{¶ª–2#UÉÑ Þ_¹…Cðº#Y ˆS¶|Ö²óƒÛ/ö¾×X¾²ºVŸù’ýÆë7–óµ^Æf~¿e‰GЭœ„)–°Ã¶èì3(.ª}ùÓ´™kñ[ÙÅ3ÜJY2˜àM9€dàGQÆ$Â¥’ÐR*hÇdY;⠵–#zÞvé 5Ód<îk]‚;¥­Ï!<ƒŒÔºwÊR÷òGë àW.—å®{yb¡Åârð@š“\qqoË’-x¼ÍâPK¹—|L¶U9¯òìõá>ŸËªãª.mŸ»N­niGÌT»·ï²xnóqþåä+/ÜK>GÊJ½ò±XÝí8µº›¶­*óŸôÝiq:ÝmX­î¥Ã}¾j_íã26§ƒvN»Ãæåe#oËRη²Ý•ôͪη…²Q•…kåã¡ ‘ì]cs†G×;6›3¾"‹û“ê,°¯áî—\0yd@ýj&(?€œAHí8Ï!}›_Jħ !®YyÓhÎá~JÍ•¤¹¹ìÄ­Í^ieÄÇè=V¨$ÌîÙDFÅGE.kò_8Û9Ú1 “®Edz<1½戃y.„N~ï·/kE±h@?ƒ¤K2»ôû-Šˆo“Ì0g·&xªÿ'©Ää÷ä™2Úœ{”$z§Öt˜ËzlÞv×ÒTWsiµÃ°)D´ÿ=·YQ]žKuFÓ.U¢yÒ™xST(%†¾7ùš'WS6u©Æ*ÐIj¾‚·=ç-ø¬Æ¦=ör›pÕ•¹[«¢}¼"ȹ¨S¬0mÜ}XÝŦxF€)Â(eî¨Á`›ÇÉL“ˆä‡OÖl×_XŒáÇU!ÿIᤸ*.wßǦfcQy‘ÚÚdú'£Ê?ûÖ* žôAÊÞÀ&„¢Äi"¡Æ! æ'0£âiÊ’Ì ­Ú’«¾Hò¬‘œr"¡¶ºâü ê#þŠ2Ó$‘ëdv[Dü÷sdÏóÆž=Ïš„1J!'·6*KbÞû¯~¦9ÒVòégäžA:žnY²Ô-ØÑ(€ô|2j«)K’¬6„•1{Ñ!‚‹5Úÿ}y‰¶I³'×N¤§$¨–£ü’¢E¾ÒêÒOheE|!4eI ‰b7fh¦*-/;_lÊ¥ª¦—#IÆ+};é¥ yûElÉ=ÚJkÁµÛaw)ç *¸ùžGŸp;•×%ôêÞòPÑà^   /fÆbާ%Óþp•{–IN:j šy6V#&¾K×íR»dVú©‡nR3à\bˆÊ”×´-Ï!yEÒ.HP-ŠÞ:Ž”§ùHI9I¢Û[žAF.f^‡²d滃¾€@ b '#ºb¶"‘QƒÝû—&/#ýÒ’8 ·;œùZÎÛ$Ññ$ø‚„èEg·!7Š„9o‹\6{¼GòÈ¿¿»’ÆqN•hŽŠµqÏÕ7­ž…3ƪÕT~Y1ŠŒ&‹ÏV}½¶î;FÒ7£8›§n»M¿ît9ÃT']ÔŸçùžG@m4eIžEšx˜‘ýCøyCMÊ’ü¬ÿ¼øsš;%[™ÚINµ¯·ÐNWËA^ŠÇÁ¦ ì™”=€¶ì=¦öˆ¿ã“/|H£e¨Y*Iã IVßDÍ9˜ÄßÒ(=õd¼F3¯CY2óÝAß@@ ˆ h3H’™Þ(’‡É—ÜyùlúÍ¿>VAD±Z¶~úx—Ååê9½wûÜ–ÈVó¦fÓŠ{Õñú»>Ûe‹-9î¼óÜIBïo]ušíÂ3¨-TW¦ÙÛ6pm¢%^BÀ[1’äã‡ðyõ2³ô_w\¢Jú,À;åeæáÛ/as˜èÖŠ´ØÓÅÓéŽ+fé‰'¤?ÕêÆ‹¦wÓ]WΦlù5Šä•šÂ/^?ÿÖ•ÔŸóLi²fû*«ôŒx¥Ã@ { ˆo’”1ÊtöejM¾yåùtå…[¶)ŠÌ ¦ÑU\¦­",Ýs'Úöì‡v¾DØü9ûKÊàŽ&xi$º‰™¥î¿è€t9ùc/ŸŽŠ˜®øŠe¬ï·ß¿Ù¸és]¨#9L¹ |ÒȼOóº‡Ya’¬÷EeTR^ÍA-¨Áæ\"Uy‹„ÿýÛ#wyïöØ–d’ÓùšN—S>G¹rpœCYïÜ-r¢¼ID,Y.(*¥ø˜(Â}“Èx"?¼}¡Zzÿ7nX¿söÅûlƒ@O$ÐÙg0yüþëÏŠæ† ŸsÉX6½Ýwì”^lúØÖ•%@¹œÍwðlsÁ™r*©¨Rfqqü çPfŸŽìÙòõù—‹®Öë÷µ"Ï~ã*çhxùgJé4ç_JMˆáçP*ÅD…·8Ï HºmGË»Ým]Aà  =€8.kŠ’\ÛÌ ÃÚt‰-Ê1ªM'£(:Y}“ÔçEõÃâC €@ð8œ×ì4œgwÄ ï\"~”ƒ2SÔç\eÛs\fºä3ª'áÔFP~,eÉpQ5€ôVëvâY¡*†pHG /(Þæ0úA¬€€@d´’¯M|ƒ¶4¥êgqÎ4´‡”¥öÐBY6XòõòÎGtùùãÛt. €@g œ`%éÍÏ7xT#¾Cðí!€í¡…²  " þOçÒ¡sq€t–€øÝ~ÙLŸ>Š­ç÷l˜YêÙ÷W ÝBàúyS©ª¶Ž—#¨/'†”äÁå›ÕRÇJXî~}“Ï\&PýB;ÁGÊRðÝ3ôÚL€3K‚I0cåüzV‹ËbSK ¹—äRK9N|L--.'.·ºx[-›¶=Ë\|Sû½Î÷,ËmHYH¯"0vXV¯º^\,€€¹ˆ‚4cÜPsu ½ JP–‚ò¶¡Ó Ðf<ÿÄc?isi?ÌÉYÉÏœ3Öò¸<›Íá°ÖW‡sæ†vYíau6ÖÐ¬ŽÆ¥¤9m‹Ëêäfr¼‘²P‡Õ&Ë›Ãf…Ž·å˜RêœN·rgkRò´m«» _š•œV·rçrº—V·‚èV›Î·XlN.Ë}᥵iÉŠ¦´ÇÇšÊ^ÂõÍö3.T   ÐÍ ,uó @ó ЛääÌe'øeÑ£¿ŠçÙ2(KÁ+{õX-ÖF:Nd9Ü«aåÅ»ó ÿ ì:: ADÊRÝ,t@@ºŠ€-ÄRÁu¥ðléÏ?ñ¨if »êúzz=€˜„@C£òO—QQY•WSTx¨ ÌйJ †@ˆÝî êÚz½©˜è²Yáò¦Á €€@@ @Y (n4 æ#àtºèË-¹ôñêmTUÓ¬¨h= aϲó' KfŽ¡ø˜(m·_–;8‰äóo¯ÔëþÅ·¯¤ÄÉrX@IDATþéÉú¶?V¶ì=F¯|òµ^õ/]E ±Ñú6V@üG€Mšéþ_ÿë¬ p°"Š ãße”AWpζèÈ𳞃ƒ ÐU ,uIÔ AJà¯o.£]ó[í½Ýá •÷Ц=Gèçß¾‚’8ÊTwÉS/~DeU5ªùó'ç—¦ îŠÝé šºf%Q^Þ æ! ¿ÉÚúõ9YTF[sѽ×Ï¡AÜ ²‘Ÿo-Û¨wåñû®C˜rFð¯À¶!øï!®@:L`Ùú=-¥ÔÄ8šœ=²ú&z$p¬¬®¥¿.^Nöî‹ÓQ^]Cå•îO]=ÇF„€ô:e•Õô÷w¾ 16ƒ444êÏ%y>GÞ€ô˜Yê9÷W í&°j‹¤árKFj"=tó|JŠÑvQ ûý…¤Cy§Õ¾¼ÂÚw¬Æ ÉÔËtåʸ¡Yôô÷nÔ«ŒŽÔ×± Ðó ŒšI·.<ÏãB+kê(ït)½·r › שc%ÕôõŽƒ4gòH²Ø®&e©«‰¢>23sº¤Rïí¼iÙŠ’ˆb¿€;¯˜Mÿý·wôr‡Yqe©²ºŽçŸÑ÷göI ”„X}ûÄ©’ Ò /AFÙqài&oɬ ÉLV5›Ã;Y¢> /E†‡qЉJª«·“ÃÑmåw/}¢Vö­Ë•’'28%ÏDéŸ/¹vÞ’¾e¼ymÉ:µKfÙ½ûJúý+Ÿêƒ\#¤AY2óÓ:|–üÕ‚€€Ù ÄrXncÔ·O¿Þ©¢Âí;z’dôÒ(SG¦óÆQŸaýÓÔ!9×8J{8ßmª'e€öø)OeãHA‘^¥±¬ìËæw]!o³“õ«Ÿ®õ©(Iý§xdø¯o® 3¥’bÈ·üåå-%))~Ïüg‰2Mô}&ö‚ø“Àža6ÊpVŒòÁ[è¥×´ª(IÙÏÖî¢?¿±Ìxšß×E©û-+P­)JÒwWlbejÃYúâ¢?X­+Jg)ˆC]L3K] Õ€@0˜8r€Št§õyÍÖý$ŸÐŽ4•BÃxf¿ ëß×g¾£ÑlÖ&æ$"ùl'ÁäÜSÅe-œ¯œ¡ñÃû©²G Š“Ìöx›Ñ©B†ÿ®™;™ªØê¥WëáÍ'ŒèO³& gÓœURÌd–sÔ>M$7ÔÌqCIêß¾ÿ8N¨Câ‡õñêít×UçkE=–åmO\L5H]Ã’¯vðõ¸Íod´Zü$Líq6@º†€‹GZ$ßšQ*8¸Lt,^êže‘c6›•²¥ëÅd¶Z!M"#ÂèòÙãIòĉ’õÙ×»ôA’=‡óióÞ£êw®•ïÈ2“ý<¼yíåúVllöÿ¼çº9l~B}“âUµ+7æRÁ÷sRvÈÌölŽæ)³Ø_mÛ¯qd¿D7,‹¯+C6=DÌž÷UŸÔ÷Iètä ÓqøuÊ’_ñ¢r07LQ~=û½LÜDé‘}jÿší$/SYy¸ò‚ d º0šMà–®s¿ Èèéqö!’Õ‡ÍSšg•â97Šøµ6³4vȹg•´Á!!Íöÿâeœ‘’k}¢(‰9­qfüÃ/·°i°ÛŒN~ÿÿõK•i¯T4‚ÒiÒˆôسïèI¯%X„ ŠtFħSžAMé ´ºÄ48<,TmÊ3IžMšÈ³ìGw^ªm*¿£Ÿüa±® ®ãÁ_Ê’œ ]÷„áýÕ3Y¯+~%3<¿âEå  `n22ùý[/V/ýÓZOþZ[×@â ôøßß§2ƒ3³ŒÊª&ZÀ‡ãMJ‰nÓ¤ ‰S³ˆŒË,”&cºÈϨ ‰Ó´¦(iíÜxÑT¿ùˆ•÷èµVn´Wú1IÌ«‰·¯—¶KÀ¸çÚ94ƒg²Ÿ:h"Ïñ4Š(6ðlŽ&bŠk|–iû»zY\^©òCiõŠ?”Q¢"ÂiŠAiÛºïX«fÄ×ò »\· ^AG ù/\àÚìQ-E5ºó|ˆy$°dúZ¤!%ƒœûw“ÕÕÒÑ3°=Bk œd´RLB”Y›zì=’¯-HpÍÄN»21ùÇ{_ê#£rîÈé$‘íDŽ(¿¥ÑúÌ’DŸ’HObj" Waq›ÓÕé#À2ú:”Mü:+â#UTV¥WÓ'±9*Ÿ¶S»³vÐÊeóõEʨÈ0=®U»µÙ+c¬ƒt «Õâ1#¿7ï|J/¾ÿ%›Ñ^@“ØŒXDÖŠù¬&Y}|GˆËðŠw’Í…}E¡ÓêéŠe¡!â¨Ô÷oVóòÇ_yT-¦‡šˆ©oÏÄK¾;oimÆÉ»¶»–”¥NòŒ«qÿq.ör:ìdµ8½ Š›^ŒJ³§Ñ×C&ÒÄ^ è²æ0Æm¨E@¼HЇic†¨’D´k¶¤÷WnÖKŠ%Š6º9jp†AY*âà.:Áù˜Dföaß§TýÜ#ì·¤åI‘òÇ¿+ìƠ lú×Qî!0‘ÍåŒf°Ò Q–6î>ÌJÆ×ªS¢PH —‰ì·ha›»Š*÷à©Öc™Eò%Ñ^32<øão)çÀ0F‘ç£A72Ò×[›½Žiåºô±âP–:‰5¾Þ=’Q\îùcèdµ8½ $ïŠHXXUÆ¥ÒÚÛ¦Iïý’ ·ál“¹-{ê æNÍæ°à1ú¶¬ˆÒ¥3ÇÒiöÓ1FrÊggem–Fü–4)åü&¹MO –™¥ŒÔ5R,ûŽq`qÖÖd¬Wî%m{—âì,~ ÚKˆŒÌB@z1õ•`.vÖý eÐEò‰ïbrB´Çï¿¢•ßyÓì°FEò)ib!~€œE*½Î=KQCÆ$ßràÖKÎc_«³æ$Å5÷ËX™ÌºAOÊR'™'VWñÏËÅ?ØJe¢¡åédµ8ýÄÎØìR¦ëÝy[#¢ióõ÷Ó´7ÿDñ§ŽŸ£Ÿeëwë Âù…äÊ 'êÛÆI®h”ø¦|I²OÌE䣅ãþbS®**~>ýÒÕÈïÀôÚü” ò`t†Öü™Œuwd]ÌåC^žD 92ž·|ÄNÖ[Ùy\D^:~ö­+ºdVË»lƒø‡€<‡ŒÁYªÙB”%yÖ$ÇǪäÕÒ²ÑÉØITk”´dw´:ÙÁÉ­«jÜQøJšž#ƲEeÆÍ6¯÷5´!'ÅEGqTÐþm>»ŸªLê¼D`®­€@÷ðè°jë>¥Ðx÷(MêVs8qMdÇÛž^¢?i¢ù/‰¢¤FÐLñŽŸ*jè •LQ"åuTêÜ>£ÚùÙ7J‰z·ë`¾¶©‚9ˆ'>Xò±YmP”t:Xà  E˜Óz«%Ê–mÍIÖå÷oœ —}Gò‹hý®C²ªDòÅɳLI5 ÉQŽæi4Þu(Ÿ¼-­¬¯eáÙ”Í>Íí|¶v‡Gp‰à'‰müo¨1ò¦¯º±/ðšß2ßviqdaå%¥(s–9“Gö˜ë2ó…læLÚ" l~ç- 1´ñ†iÆkÏPdesÄ-ïrØÞN "IäE7]4þÅ eE$ÇœD•ðßb:¼qÏQ2ÎdÍŸ6J«K“ðüÖ™¤SÁÖá§ò•)žä#ÑrxÛ5Så¡%¾âÔÓ<’l¼†ú˜ÚxãÔåÛî×Xë Л \5g2ñò’™$Q:¶æ'Y7*8SG¦ùÓ²[ “— 1…3ŠwÐdPF³â¤íëHÈpã –änúðË­ô¿ ‰döI¤yìw¥I}ƒÛ)ü‹Í¹Š’¼ŒL 3– ,bYñ0Šæ»,û$÷7.ɦùÍaþ%µ˜ß®g_'c˜+Ο@Æ™h9>'šŽ‰jžÿËu;©\s¢d>K>¸ìêDê‘`ï®ØÌJÛwR”&ÐÑDhK~:I¢kT”ä¹´pÆ­–&!àù—Í$ ¶nÄÖÕÒØ<· žŒ&@üKàÍÏ7ª’Ò)$ÄôÍW‹Õ }iãµ÷“@@@|‘͇n¾HEŸJç@ ­I_NÌzïõóèîk.ðYD°edÖ(ƒ fqbng Ñ+f)Æ™'ãyg[—üH’4¶5¹éâétdz<^˜´²òuÛ¥3èFå…€L¯gÔ_nó¸QH~þí+ÕÀ‰Ç¦ XóÐ-Ñœ\Û[Äé{œsÎèÇ$eä¹uÿó9¿ÑïSômQ²®ã™*™­oM]7—îäg“1©·VVžŸòl’g¬ CÌE ÙžÁ\ý ºÞœ`íMïÇ6­§Ôˆlg³B€uXlrèa›5„ú¦6Ò´Ö|Eß~´åê{hʻϒÕÞ<]ßZyìÞJ@žYò‘‘Z Ë/KñhQ©œ³Hœ¨ÏõGü·/<+¾§¿{ÓYËAñ;øÛ#wµZN|¥¾ã%ɵ&!v£yDÙ(³Æ£é<vŠ#øåŸ)cÓ àé) *°ƒ±¬¬Oã²ò9›üöû7Ÿí0Žt€ègûÍ«Á9ÐÎUV~çÝsµÊ»TÀ¿ ö’Í T‚zŽëó^?Μû®U©òN—¨` )‰1ºã”Qƒ¼OÑ·/♩ùSG)¾êºzå™­—•™òlâÔ îgS©Š¿'3âÂW … ÙµC>î%e©‹øÇÔÕÑy‡siõ°Ñ*áXzJ<‡ËõÌÝEMõÚjÄÄñµ%kÕõ÷IíÏŽã­à!÷FÛ.û&MüðE²hq…°  ¥H>#È31«^À$+âèííìmìZHˆMù^‰ÿ@ ÷_!ùt$‘«ä“퉲)Q‘ÏYÌ•EA’$8À ¯ ïÓy‡öÑÀ¢B5âùç7–+gé.¬¾WW%¡ÂÿºxçnqP\l2¥$fµ‹GáÐñ”;÷ºvƒÂ    ½›”¥.¼ÿVŽuͶu”\U©ö~ûÒ't²ƒqù»°[A_•8˜ÿö¥Õ”zDx õÏÉæ@í¿¬£.¤#“ç¶ÿDœ    Ð+ @YêâÛÞØH7l^C‰5Õ*A£ÄË—h,Žˆ2¢(IèÎðð(Ôo4û4Gºio­û.¼†N kéØÙÞzP@@@@ ç€²ä‡{œÀŠÒ_/§Å§•IÞ³o® ?½¾ŒCð"çO[qK‡g^YB/¼·Êmz“LCL¤PN:Ûq±;åöËî ÒôÖ4;S?Îè9àÁO÷2¢±nÚ¸šV C £Ý‡òÔgܰ~œßcI2Fc¶h?u#¨ª­¨ª¥NÐVN"'¼D¬+¥&÷kSä»¶^¬“Cl¹ö:ïÕg(ºìL[OC9^FÊ’o¸D^»pßNšpü0­>ŠödôWÊ€(âs“ž’H œ{$ž#§ÄpΑs…åõcW»¥j'ó‘,Ùå¬$•qrIOÿ. %qž$ Ò¹Ù$_×C›®¿Ÿf¼ú¿V[å«ö€€€ôrP–ðˆ¯­¦+¶o¤‡riß,:Ð7NÆ'QÁ™Rõ @‚¢ Q ##â8äf2ÅÇ¥pXàH¿ö»&>…s0ÝMSßü ÙÈÁäWب@@@‚”¥Þ4‰’7£j/+M{©*"‚Š£ã¨*<’?áTFâOÓ›ÄÂWÕÐ@1õuátÒ¡Ëî"{BŸ€"(ÍB{çßHc–¾ÐvÑ€€€€€ù @Yê¦{$Ilåi&üÑK´þÖï“ÃÖ¼3k'ÆÌ ˜¢“4pËh M€€€€ Dà –;Õ úúM|ÿdu9~µûæ\CgŽ x»h@@@@À¼ ,™÷ÞôÊž¥Í¥±Ÿ¼Ì‰®€^¿“¬´ýŠoQUb`Íz‘h @@@@ ] ,µ  ‚@ƾ-4jù›hÊ£F(±åÚEÔÈ~d€²„ï€) ôß¾††}õqÀûVÍ&¶ñ “KBóA@@@@z5(K½úö›û⇮ÿŒlý"à,0’öϺ<àí¢As€²d®ûÞxµòÊÜ»Ák¯ÿ7O»˜ ‡ŒñCh@@@@À´ ,™öÖ c1Ÿ½F)'öi›[î¼ôªIH X{h@@@@À\ ,™ë~ 7>Xšðþ [Tàã¨ÿv©€WÝMöPÿ5‚šA@@@LKÊ’io :f$ÚPGSÞýET—wû}½2%“ö,¸Ùïí ó€²d¾{‚µB ¢²Œ¦¼ó,…4ÖµRÂ?»óGM£ãfù§rÔ     `ZP–L{kÐ1_bÏЄ_$‹Ëéë°ßöí™{=•÷Éò[ý¨8ÈX,Òc›ˆB@ X 468µìß Áz ½¹ß|óÔB—K»½™®= èC›þ¦šõ ,™õΠ_­H=šKc>½Õãþ8à´…Ðö+î"Gh˜?ªGAF€_Mò¥Ë¥•5AÖstš —©„r‹uó^¬  Y+¥¯õö`é2ú ŠË«ÝÛ.×q&Û€²d²‚î´@Ö®u4hóжî¢R’°vÏü»¨6TÌl.«z°—”CY æûØÛû~º¨U%¥Ñ™A£Ú|Ng æšNÉ<³•‘»¹³Uáü &` áQ0~?)*¯”½âËA×{!ÊêZª©o`eÉârØ’ööBAÉ.—…•%U×Öýµàz'[Ÿ)­PCmVë3ÀÌRWÞK;ß™Ú[¾+ûÚ…uÅ<ú0%¾öœúXû´//‘%6†"o¾†BÆfSHö°võÊÂJÙ„OþEÑ%…í:¯³…wst¼šøäÎVƒóƒ˜€#=e/«HeUÕu–#ùg‚øJÐõÞJ`ÓýÝdýó9÷bŠ4¿V«m‡tûpþi’O}ÇNR}ƒÝj!ËÉ>ô3SÏ,AYêÂo–«º†V­óø8Ïë-8Žåy«_½N?Ö›V¬ ñ2r(…_µâŸý-Yb¢;|ù!õRü½ç(´>pëía´ýò»ÈeÅϧÃ7.ÈO|þÞ{ùÿ¡\ÆÖÜcA~5è~o$°nÇ!·£‹•^î×ß®ùÙǶÍb±Õ±Þ‘ Úô„{Ú›®aóÞ£Ú従cqjf\ ¯ ¸¤…YÌϾGa©îYˆVŽj_}Çw‹üâmËL#[ÿ,’z‡‘«ÁË™g¢,ÑÊÄœˆMø\uî©÷aƒÉCŽ£yê\Ÿ „„-#¬é}ÈYx†Dqóe.g‰'â²"¢üI™ý 5Œ\¥dç~IÛ‘ˆ›®¤ˆk.ëLçF•ÑÄ^¤7<@.K`˜²´t`Öå4|µz_öè6z +ÿ˜wlâþ5s'“•ÇÇ  ò KèØ©âžm‹´¾ }F[`EɵèÑ_-ã#·ìØ‚†dõiY{@À„8(ÉÖÜãÊÿr¾iÂ.zt Ê’ŽîÙ3‚b~€¬i©Íp8¨néJªùëKü>æO,&n ÿüƒ*Ó°f=5nÙI‘wÜ@ÖÄ÷y¬ØÔ¶’ªÿô‚‡"qé|м礡¦\5µd߈jŸÿ7ÙYÉÒ$êoQø‚ ÔfÅO§ÈÛ®§ÐqÙììVBœ§NSÕïŸ%ûî}:y<ÅþÏõcrR ϫ²ŠJo½O«²ÅÒUR¦·i±±’Ø/£E™öîH>±ŸF­|›vÏ \†ÃSPÊ‘=””w¨½ÝEù@ #$}IAcþÉ’òªôU[÷ÑœÉ#{ÀUáz·–mT/)ì¯ôæ_~ñ‹fó‡Þpñ=îm¬ì:nùrK.-œ9†¢dÀ&'°tí.öµ«c=ɲ?=ô‘5DšºÇ†75‚îí\èøÑ÷›_z*JÒ%›".]@1=¬Ïô{j>„¢¿{w³¢$yæ)ü’yqõ¥zѰ9³(ŠË%U”ƒ2„NC±O=Öâ˜vrÌÃ÷s™ÑÊ5­Åüì»|N„4çqL'JÕ9ÌÓjßúˆ*ø‰úTþôq­¹N/ûo[MY{Öwºž¶VÀsn´sá7N¼­ÀzX¹œœoÕY-®ÿ–ËúxÍvW½÷Lp»^\NÏ °…ÍFsžäI ª uÑÏ{ÆUõÞ«xîW?ŸÿøoS¼eëÔ°÷~‚çÊË9åÆÒõ»•“ÕF?1» ž…²Ôß/Vˆ¢îÿ¦Rr¤µ‹? ²o}Ÿ*ÿçÿ‘ãDêYØ´‰)nb‹^Úx–©qëNªøþ#T~ÏÃÔ¸®9:[ØôæòáÎÔÏ­þã?¨ôÆïPÙÝ“#ÿ”Úo‰%ÛÐzãŠ5%‘ªŸyŽÊîúU=ùÄoƒê°59IÓ°m7Ï ÝKö]ûôÓÊ¿÷ *û+xÝ$ÙËSÜ•' =@¹^¶ÐˆùÌ7âE6ŽÝSYUkYüùFóu=²Êjz}ézõ’ÂþŸþ듞0ÆjS<²XՠͲ{]'Ï”áU Ë½…@Ñ+Ÿ|M 2`óås?ÊʾùÊR7Þ#ñ5%Ç‘cTû¯×•?Qãú-Tûú{zÏÂ矯¯ë+N'U=õG²8¢ŸšWßÖYÒûêë¹û©ö?oSõß^¢ú%+”’³ð4¹*8âh“XûÌÿ´¼¬{ÿ3ªÿüKrž.¢†¯6}Û.ý¨-=Íí7U^I.ƒ“‹ÿ‹^wIˆ½‘ý—þA¡ µëÂñq³©¾ï€€µ‡†ÌCছnrðìÒƒüзµm?­Ü„Ìæ¹;艑€øüeñrª¨¬Ó—!¿3Çzðøû¿ø€ïégòúì[+Hf™ `Fï®ØD;Šë‡¥Úb}ÈŒ}ôÕ'(K¾¨hŸ•:èJÑ?ý®þ Ÿ=M?dË®¯k+Ž‚Bw†¦ÎÂ"íYšrÉŽº7Þ§º>“¬uͳX±OüŒÿóWÓݲN½‚¦ÇAÏHŽBKSmÛL˨òb÷I`<•N»˜M'CÍ„} ¿=ñØl“ú_ÒÜ›ŸotmgGk˜‰@#hýý/èÄ©1Ÿ>ÅOª+ÄŒÔL}D_:G &4ê6žå>zº¤‚þñÞ‡ÉíëܹZq6t/6çÒÒu2ènqY-Ö;ÿ–óHó|×5ã—š ,ùkÛ*µ¦¸£äIi r~á ý:cŠ^‰·¿‘p–7Ï ©‚2·éCÄ'*á¥?QôQø• )tÒ8"ö72*>>NS»œežm´ÒDk§wëþ>‡wÑÐõ¬$HQ±>´Ùü1@Í¢“xþWþ‘ßB_p:–¿½µÂµ|f˜Lrkz}7Äôîÿý{‰Íå—éZ—%ä*˜ßõ¼¯Å39—P¨åzv¥­Ûu0ŸžùÏgTUSßó.WtäÝñíeéõ%ëTßYñøåsOü¢•ÐÐæ¼qÝuå, BŠwãMéåM—TTÓ«ì£$Ï"™Qbà_<÷ä£OÌ,uãsäè­[9OR#LÐ>ö}‡T$¥(q˜ïŽHȈ¡º¢Ô°a+Õþóu²ç$bÛu[FߎTTçXÞøOÿMá5S`j2ÆqS‚ê{ÒUÿ¥çŸxä!‹ÕòÿÙ;ø¸ªcÿÏö]uÉ–Ü{/SLï$@¼—ä½Û¡%᪥ bÙHÂKHÀ6!!„zÇØØà‚qýK®êmûýÏœÕ]­¤•¼Ú¦]éwü¹Þ[N™ó=W»wîÌ™s+ÿ(ø–~µ•ŠIûhÅFòûÃ[~ãÕ6êP‡«éϾG|î=E‰ïÇ5d¶Ìxb^ñg¡ù°ßû,šW¼Ä`°žÊÚñöÃUµ†‡ÿö&ýå•ÅTSÇë&"@’¸Ü^zíãÕtßc/k-ŠR#Ç4»†ïÏ´S”,KIºqÂ5ãÛ±K¹Ã’¸ÝÙ¯û&¹9 ‚¡ ß¿Ž¬'«Š9ßü€9´?®¾öçBÝ÷ Y-‹ÙòÝj=ùø¶öø•c¼’eâ8rs€YÇ)’µ©AÍ_Zñ훓"Žßb§¦AÔŸê“ÒI=ì’÷ÇÙ%ó¿d—¼?45»Žÿ×»_Ð[K×ùO˜<Ê8cÊ(=$|@•Ôë $J' MNZõõ.Z¹qmÛ{PãÄ^wää7¢hæÂ²E¥³ð´œNƒ¬ ËîÜú“˜áoð•ù‰f}±a‡‰ï mò˜Á†SFÓôñÃÉ2·9†¦P‚ä¥à×;ËÕwЗ›wû9ЈdXo7¼k&ËÏûÕ]iëŸe)8ÌÉß‘PÜ š;çן2~pÉšü‡+©ùéBOE¼/V$ÍéRë(Y&O ¼gþÄÁ,dÈÎâà¤i08ì×.£DòSë1ñÅÌ»n¡ Y”öº™á²öȹþ{6Óèï“,"›ŒTaêG5𛲓ÑÚHI æÞ»´´T›qÀ7ÿûüRVר;}L{Þ)ÚEsï^Ç¢]8³xîõDt=¿À™\YÛ`å6ì¹$H ± ©Éñ¬ •óÔ¤çfÃs J‹¿ˆ©Ö*Üá•æÍ¥óq{|?»è”©t5ONNVb—òð\šf§“êÙ2QÃÑÞ>X±™¾ÞWE'íØLçn–¿÷ÞŒ¼>’iøåÂæÛµ7nëI ÓØQj]$™ Å1ER2Cv¦ªß$õY@véßE^kl–´pð*³²©&#‹²ꟛEvq8>³¦çxÔ~²­/}°R…èd£ÅËÊJî '3Î%Ÿ@iéGæC´|¤ÇïgôûDz‹Ìþ–š¾4ú.Ïê)f¶—ßI<iþ´Ë§iÍ`ü&O\å/îØ?¼üÛ6 ç{ÞvB£Å†²×”ž]úpù<㈴q~Í?š;ÿÅ^C éŠ{ù»{·ÁhܦM[Þw×^~Þéu“taYêê.Hò5ÿþƒ$[¼“ÖØDÞ¯6Ä»Úõ‰kŸl©šdý¥©ï=Gk¾ñ䈸ÁCcýÕ”Ÿ”ÖÐH:(-=WÞTlkÙÒAä¤ÈÈo½O"-re‰¥½ý%ÀÌ… 綠‡Ÿæ‰m}³»9"i×8Õåßùàƒßzè®»0™²›üzcö¥wâ~ɶ´7ö}ê9‹Jïî¹ÆØ2Þh&.ªN=ƒ6¯¦Á_''r®—çW¯hÈI=@ å ,š5Ë3Ø<ç#‹YXM;¯¦Îóá­óç#ºHÌ0Q€@_#e©¯8úK“?|‘ìµI!Qî±Ñ^—råMJ{h@ ÷¹F¼&ÉÍ}ÿ™5§,¦h{}‡“C¨WÄÔ=¬¼Pò?o,)»)¦zP@z1(K½xpѵÈŒ_òzR¢ãÉÌì/ê³" ¹@@  š{÷ÖtNc—¼­]d;ê%ú`$¿ö§ç”ýò¨™‘@ú (KQ ºãÛWPÁ›ÿT[þ+#ãÀ¢µ˜ÇæÉºÿÎ×#=aÈp!7[mÄ¿Š©œLÇRþ‹Oª-ëÿý$n¢Ú¯ùåkÚõ´÷ðk3K""MÌ0Q€@o e)N£˜9óûü;Õ ï£‘LÓR´X1 UÄÉbf…"Gmí qÙÓ¯…Ög°Ûƒç v[è%µ/V+ËIÇ‘í’óÈEÿãY¿ÍϾLÍÿ|‰ÈïW§E¹Ë}ìAµï|ã}ò­ÿš7þ·Rhêï™GþÚº`ñбe?P¬%9ßø »ä¤UÕw×>U…ÁP*CëKäþð/SùäTW4,‘ÍP-[—¶;m4ΑxKVB;‚ÊAR†Àµ×^ëca~<³¤¬RókÑO–åJø'àTo£÷Ó[J¼è¥wÅu/eAè>Ö'ñî—E &à|ëCòï($Ž\G†‹K8@–c§p°‚_¶U”$#[mì—^@Y%·‰FŒT!Š’ªKŽyómßMþšÚÀ©Â~Aë‹(l¡Ö-Ó„±*ügš0Fík.7yÖnTû†ÜÊùãüŽŠ’\e7\MYwÞ¢ò¶ÿÏ4reülV°íö×õc ~‘}ß/‚ŠRó_ŸëRQ’rÍ/¾Nu7Ý©¶ú»~¥W•”O£æ§ÉI ö Ö%¬KIW4}‰À¢¹Åwñ—8¿m3Äö £Ñ·Ç³tVñC‰›0Ú—}HKP–b6¯‡ýCÕ"Vǵßì¼FVˆ2~òƒ BÓüükTó??¥úûC¾½wVv…“ͽfU_?‹¼ë7뫽í^ªak‘$蝹Áóæ‰ß1‰ÀšÌã ’X¾D6I¢(‰Â$)ㆫ‚ç½›¶QíÍ÷Põwn¤†~OþÆ&•ÇzÖ)açcY¦°«»z6n&×;“¿: ¼©B-ÿ‰‹`ö¯îâH~9êŒóå7©ù…×B³¤ä~~ÅN¼yUÂekði+¯½„ ñ&ðļâߘŒ†ÿå±6E8èÃH\Kf–Î;>êJP@Ò˜@J*K¶jÈ–.Éóù*ò¬Y¯Äµ]u„¸×…tB” ¯-É·s75?õù&Ïç«©ù¹W‚9mçŸÉÚ—´ÚzÒøSOZ}£rÓcÏʯôÓdb77Iæq£Ô§·Årdn±&‰œž<+îwÄŠŽíÒóõÓÔ´àoJ&­±‘ÜŸ~Nî÷> ^³ó’à~èNãâú_ÜO¿_D¾=·9ýºÁb¡ì_þ?2 ¤N¹?\BMO<£_NùÏñ‹_#“/ Tv[ØnܺkäíÝo·ÅC¾A`ÁÜ9O‘Áx5/^댥Çì’WD^ÿG³‹çžK=(  éH e”¥pÊ‘ÅlRLÝüàê©yÑßÕÜJü¯+®qÈÀÖóf eÞukp³qRðš‰&-¹×p˜kþ“diq·3 (KÎ×ÞQçÍcF*—>sˆ;žgÅ—k" »ûIÒÉ»u‡Ú×ÿó|PþäØ4¢ãü­©™\Ÿ,Ó³wø”`橃篾ÜO‡ ö0ê‹"ÕÏaÓ%…»»ª¤Éo¤MM°.uÅ×@¢'ðDÙ½¯5ÃÅü ²£ù¿ÕòÏMŽ_3¼=³xþ·ºQ YA@ í ¤Œ²ޤÝP–­©ÿ0) \o®­g²ëZˆ¢ ÷ÍØ¿Ÿ¾«"áÙÎ>•ôÍrê‰Áká¢Õ/¶ìˆÕÉ»e»:2±ÅJ¢×™†&Íé$÷òUl•ªc-ÊLæQÃTnÉ(®~þ‡UcaëšM¾»ƒŠ—º(y·îÒwÉØ¿ ¸¯ïøöe¾o;Ë`‚H·4jÅûdg¥)’ä3þ”LíúIÙMDÆ‹ò€DE`á¼âÅf‹á~s0ª Z ±KžMÓü/ÞX2ïc©eA@ L )*±Ý/”%AØôÌ d=ç4µ.ƒçµOåMO2ÏÇýÉRý°Íg‹Á¨Í¹pžÕëH¬F¢\YÏ?CÌäÛÂ"Ž^çÙ¼]Í}2OžÀ‘ú§  Wæã(~z2ŠJ‚G´D½“ó¦ñ£õË$÷Ú'QÊŽ–Äòd=þ’…e% õÜ3ÈýÑ’£K™ëfž6qñ«´æ?<ªL>c@±7¶X˜ŽZ $ƒX—vpd¼±ˆŒB» ñ$ðXéœ57•>p†Çë{—½? Q5 ™øíΓ3ç”õ_4¯ø¡¨ª@!H#ü„œ:IÍUbçjý3ËnUÂÉÎ!ýÍ}êHÛQ­®šÿñ¢ºn"ß¾VkŒ1'‹ç9mn^VnDIRŠ»¸E’Bç-Ù¿q¡*âá@ ’|›ŸÖ‹Ï ®á¤»à©ëR¼e>”13ƒÌ!Ê‘\·NŸ**µŸ¤ŸïêÓ»i;Éœ&z¼%cÆn8j´À®êì‰kƒ6¯¦Üƒ{ºlZc¶PIòŠ;^Hò|ÉùB"ëÉ%YtVw±“5šr~w¿’'óöÙʵOòH;ï†Í²]â6›žüg°¬ý›»àÉß)ܵ‰ò+\ÉÚh Ü£VH°&¬£áòvunGÆCDø¿ÒŸ×dd^È_XÝÖA8í‡ï¯ÝüRié__„®ã€¤7”R–¥(JbU (LFR¡¯<‚ä ~:$Ϫ¯È½¢%Dw;%¬wÝ¿RaºÛ]"÷g+¨öÖ{ÁZ.ºÞþ$xDgIÚÒ“Ÿç!ù«ó¢Äâjáò„‘G«oPíIè󉕜濿5B[kŽÄíùØ*ãbëKSs3ÕñüŸ*~𯪪¦÷Öì¥z——NÚ¹…ÎÝ´6q$¹fã ¼îÒ’0ܾ]{ƒk(…Cæ@²3•uȤ*\–˜Îɲ&vÙ3ä³’µŸ|ûÊy1'oLuö¶Â+®¹‰ŽŒ˜Ø¦[•YÙT“‘E&~íP˜ë ‡¹W[™L¼±®ü·)ÔÅÁ(»‹ÎÎmè"Gd—^ú`%½»|½(l/*+¹3²RÈ=C`fñÜùûìH[çûú¾¯Ï‰4?òuN@æÍ.){įÑO;ÏéÚŒ Ã%¿Ÿ3ç`¤%@R@JY–B-J&~ú4ó§™8Ç `%Óªc¨šL{Kòï?¨¤«‹XyºJb)ò8D‰P”¤]µÖÏir/^°zAQê0ã—´µ.yùÞ¬ÍX>3mfå‚gä§8Ùô %ÊÔÔ¡¦ÎOìâ¨x¾”ú³ì\X\H{ü]¥-,+ùût”ÄÞmzS“¶ä–â¹£b¯ 5€€@jH©§2]Y’7òfv×Roè­V˜ŸIyIxæ—N85y,5BоB@¢âm X6%þÓþÜ|Òˆ•zöœ]û†„Fyìá÷Þ¬âù—-+®ƒ€@*H)eI@)Ë’¸á™ÌlQ (Jv‡¼õËqÐè ¿½×HB‰?}ê¹<‡);•ùB¶^D &#“žá{î` ¿àw§VI¸p¥(ñ»V7¼@DÇîÎW Eµ¹Ézˆ}H'ÊJÞ× æóXaâó¢O´#C#ß«³ç”}/úZP@z–@Ê)K·óÜA·,‰¢äp8(ƒd”ç ñù<Ÿ‰%—¼'Ϻ>˜t,5YñpÙ³·RïmÝÉî OœF>ób:”G†ÚÔë©!»ñŽ•{±†JP’€ž(ý-Z*û=VjB ‡hñ¡€@Œž˜{Ï ~ky&¿ÀÜKU¬0™}DOßXO@64²r^Ïó‘ª2³Û̉³ý4À¡Q¿,«RÞ3´:òð?ŸÉ¦,Jʪ$ó–T€‡Ø¬Jú@lã¨xÇf6ë‡ø¤xl^ñÞ›çÏ?ÃÓä{‹zgÄ$€_+æ ý‡XæÜ\Zjˆ1ê^L’ 0€DL e•%yK/®MÏo…ÉG~~Ý®+J¢Péy²mnªuz©ÖePkÔ¸y¾Ó댱0ñܸ,“Ÿr¬A@ ‘RRY’ëÖ%Mã‡T~%JR{EI¹@ÉzLf[¡\”k÷*¥ªÙ£‘˧‘—ß[ÉÆþÒH pT&Vr̬ ™Yé±q˜z‡¥52£çÄIdF™7§ÜAYY*²6Óa/JÂJ½¸Þ¶ø(Jº°[]6(K: |‚ôÇJon`çž-Ïð¯ñwbDÊWx·ä³v•ÔK](  ‰&²Ê’t\W˜ˆ,í¥ÀC¬©eáÚæf'9Vr»ÝäñzÉ®\öîz¡n{¡0uÅ+ôöûpÖ9'[Àb°lZd½/^Y%=ЈX–2YiÊÌtÐSm÷ç«2bP’-ži7Ï[òf7°ÏZQ€tŸ€X‚JKµï–{æUrDÐÙݯ¡µ„X¨¼ÞªÙÅïR±\µ^Á€¤”W–äÁU’<°ê³Ècì¢×²`­¬Ãät¹Èåbe‰ç'y< “(Jºë”£ÔºñRMýÞ’5’t÷N±\Ê}'îwré!ì%àH@q²Óx£›v6‰U) ÜÇ»_l ¥}.+´Ã[%ÞlQ€@÷ ´Ì5ú k8‘hŠ»_Ck ™åiô/¹iNÙE27ªõ ö@@ u¤´²$˜ô7ýd–ౚÏ$Ê?Ì6ó[Q”dóвÄÖ%/o>Ÿ(K ”¥Ô¹éRQ’VeI,JhŒåÎÒ¢,&Y$™ç-ɱ̧Ë2i€×C‡½‰[Dv”¥T¼e ôiOÌ-.ápàGØíã¶2Emûf—¼‰^ƒaéÌÒùIô½> HI)¯, 5]ajó@Ë'‹%à"%®nwÀ¢äö°Âä DÎÓƒBè SJŽ„J ú=¦[–$t½X–,¬‰R°bÚ÷Ëy}m¥q®Oœ²Tî¶’„B@ü”¸U €@ 'Êæüžœ=â7ÐSl%Šúy‚_f3x}ŸÞXòÀej}'"õ—[²û +J‡ÚW)Ž|'²âz'Ö$Ý¢¤‡ç/p5߉ß^ç=%[v´—ÚÔ½EùJâN'J¸âéDÔš_¬˜›ù^ÓÏé¡Â¥ì:}$*@IDAThv‘[Ñ ‘'ú—«]òð“HGÅЍx]rÂEäX0¯ø³ŠçW“Á÷ÿÞfD+—ío ï‡7Ͻꉲ’÷£­å@@ ÞÒFYÒ;P–dŽˆ(L½Œ->+L¶&©5™$ĸ„ïà®x:E|†{IRû{KŸgj cP¢ŠºäÕ˱'²»hK³=´Ú¸îïâ@P–⊕ĉÀ²{ßœ]2ÿ"þ~ßKæE[-+LYüÞê™sÊþkѼ⢭å@@ žÒNY’Î몇[MÍ1ñûÍÊrê:W)€K,KH ÐV}Ô%Ñ›÷/0ËJRë¾nyjU’BëÏ®x‰T–öò¼%^fŒ½ mû  ̽wéÌ’_ŸEš÷þ½µTq4'í¹™Ås ••,Œº8HKeIï»®4ɱÑP†ÄzÔºr¢¤ÃgWä~’¤+L¡÷—~­³òý-^Ê7û¨ÚË 4% ¹ØÅï ‡¨µbÚàE• q °hîÝën)ž{:_½Ëðc£­’ÆeŠæ‚™Åóú/*›3/ÚzP@âA ­•¥Púìþz ÊR( ìwF ܽÓYÞpçG°+^uCÔ.ûáªlsn7¯%e© €¤?–•ìüé¼yg45ÑÛ¬0MEÚ^ÌB ¾FÊR_qô7¡úñz_Ù&ÂÚˆxH  Nxî’¶pnñfß7ïJÍGWòz&—ø<þÁR6`f ¸o'èg#R‘Òœÿ©?"Mý]Ux÷5ÝX<÷C~${Ól.xú±Ò›¢é ”¥h¨¡ tAÀÌ•…f‰()Qõ&BVÔ  á,(+ùxfé¼s ^ÿ[¬0…ËÑ9ìDþ—f—Ìûñ‚¹sžŠ¨Ldšý˲«x½¨ûeΕjžw¬3 ز2ì”é°Q†Í¢Ö„ìñÐ$ô n¯›œÔÐ좕µT]טÁ»œõ§Ë=žêûgÏ{ÀV”ýØ£·ÝÖ­7ÎP–zÅíN¤l]J”RÓÀó–\~ÙŒx™jãy@"'°¨tÎêYÅAä~—­D##/Ù>§fòùé/7Î)ëÿÄ¼âØ£îµ¯>†ã™¥¿®y¼ÿòù´S¤š »Î˜>ަŒJc‡‘ɯ»ð¢(tI âp5­ß^NŸ­Ù*ÊS![wç:Tû3þ®¸ž¿+>ë²pÈEü•†ÀÀ.Ä‹À V–™ª¼xÏ‘H¾¨@ 9–ݹÕj±œNÚ[‹ìGÚÃìróëØê‰_iv5<¼žU,×)›•.?k:Í¿åÛtõù'Ò„¡(Å5j°æÓE§L¥ûf}‹¾ùéT“IlÔÎóš>šU<—ƒÍD– ,EÆ ¹@ [ -^2gù©ö˜º%2ƒ€@ªøcé]æLó™-‹YF¸ìÏÏ?ÿ|~IÎ*™÷}Ÿfx—Ìú\H÷Íü]~æt²³«€@r ðòtÚ±ã¨töÕt¤‘2¯Éê×èÏ3‹ç–E" ”¥H(!t“€x(2{»Y*òì°,EÎ 9ARŸÀã÷ÜSMæ¢ ø¡æí˜¥Õ´½÷Õ–nýÃbˆ¶½lQ:Gókæ'2ÓéÓÇÓ/¾ ååÈÔ $ž$`µ˜èƫϡožs<‹a`¯<šÃóx4™ ,®ƒ@” èŠWíëÑ—¦QA1èœÀ¢ÒYM4¬P"Å=Ûy®H¯hW9Õ½}k颎¶iK¡ùn.?Þ¯^â9X–“§Ž¦ÿþÆid6áû:”öA § \zú4v‹=–_kKäbÿBžÃtZW2AYꊮ@ ú™c[w±«¦kxΛ‘@@ WX4k–gÑÜ9ß3á1wLÓÎqyê>š]úpôÑöº)„Çë’¥ü±ÃÐó $Ô$ n±ºK{éý}æÂ…úÈBYJÍ1„T½€@AÝðd §:X—zÁ]‚.€´'À®xÚ¢yÅ·²›Ì}í¯u÷˜—ãý^ç’›ç”ènÙîæ—ðàì×s†ÕjÑn¼úlX”º ùA É~pÅ”›ÁA´1´ïð¬Îš‡²Ôœ d™ýdM`‡*/\;b"HaÚ÷Wd4ÜÌo}+¶F)+ÏKÇñI—Þ\òë)QVqÔbòVÚï×”Œ2Å›…9JG…† ÐÃd­³+ØÂ¤’¦•ÜTú§¬p"AY Gç@ Ni]ªFøð8ªHUOÌ-~Ì n`·¼˜Öc` Ó·æY<«dþ©‰è«qoåÅ¢”åd9T¨âD´:AâO@¢ääHHñ"¿¯æÛáZ€²Ž Î@œä[7o©‘§EÞN`á¼âqàªËÙ-¯1¦¾jT i¾÷y}•Kbª'Laù¿%§gLEò¶ @ =:å˜1JX¿æ¿"œÔxÚ Gç@ NiYj‚²§QB5 ©N`QYÉ»£ñ|^¼¶*Yùíq†¦^›5§ìúXê -[ZªyÝ–+åܱ†‡^Â>€@˜6n˜.åEá–€²¤ãÁ'$€@~#â5ú1g)C†*AR”À¢¹÷~n4›Îd…i_,"JXožõ™%e·ÄR^öÍŸÆk*f8l4Ž£à!¤¡ (/;S\ñ²<‡Nl/=”¥öDp q$eJœ^“¾q*T –Þ»Ñ`¶œÎA6Ç&®fà…cY<·4¶zˆü^ÃP©c@A6/¥–n‰µJ”H2¢ülÕ"+LƒÚ7§­öDp q$`7jdJÐo§„wúTy *ˆ'E¥wï±e˜Îä +c­—Œîc—¼?)Wº(+cµk°ÍÍÊŒ²èiÙ™v%‚F¾æaÌBìéÑAû½ž@¦Ñ—°5‘šØUÞN‰³^õúÁAAÒ’À£÷Þ{øÎ<¯ºÞó ûΜK'ü¤ÝTá×Ãÿ·,ŠÛݺ4ͯ”¥<^¯%ÕÓÁÊ:ºoÁKA1mV3Ý?ëjÊËé(»Ëí¥Ÿ>üL0ï5Ì  ONXôõ`;é¶óËÇ_¢†&—û¢S§Ò%§sž|e1­Ø°#XÏù'M¢ï\xrð8tç¥VÒ»Ë׫S6«…~Ç÷B/'lýörZð‡ÁúoÿïKhôÂàqºíäd:”Èlî°ˆ5,Ké6š7ídÙ;>A©k-%ˆ,ªHuÝuW½½(ç2¶0ý;VYyQÊëhïá×ñðÃј‡”¦a5§þ€œ ‰!PZjð?QV<“—Ky ÖøeùÉž¦Oo-}Pmˆµ¾t)ÿü»xOõ±Z¿­œ6î(Ou1{¥|˜³Ô+‡J% tÃóBYJ¥¡†, =H`aYɽ3‹çf ÑoÙbõ›$vU›ìòº—þ¤øÁ‹/»+ƨ{=¤Mï(?¬æÈ̘2º¥Y÷쯤]ûPù¡jrØ,4bP!Ü¿Í<(·ÇK‡ªê©º®©Mýû¸ŒÍb¡¢‚Úsà56»Õu“ÉHSÇ æu{|ôõΊàñàÂ\*ÌÏ Ë\¬P7µ)£“9Ä-R<7íª i¯‚7‘gHQ¾Ú&ŽL2w«}ª¬m }«Õi‹ÙH“GQ.gŸ¯ÛÎõÔÐéÇŽ% 9ÝUjvº9oU0‹DK3´(ꨉ/¼·‚JnL²jwR4ý×ëí{Ñnc#Ë?nøÛù‘Üz[©úÙñîHUI!¤)+”¥49ˆ  n•ÍydVɼJV˜žäĨŸq¸ìp/y>ýIÉ—>>÷žUéÆ!RyæSÅá€Bðò‡«h:/ªk1G†ÍéòпÞýœ–­Ý¶¹«Î;‘.怒äAû·O¿Ý!ßÃ{S»û¾AËY Yòå–`žßüü»”•ˆP¶£ü=þÂÁkg?n¸ôÔàñŸ®¡/Z"HT³‡öÝൺ†fzòÕÅ´y×þà9ÙYõõ.u\Ä!ßg^}nÅgý¶}ôìÛËUžlžü_ü£+è·Ï¼¥”>99aÄÀeTæ–ÿdþңϽG¢ˆêI˜tGÑr¹YJIklvÑþ#5´xõ&:çÄIz•GýŒ¶ÿR±È¾ðŨ¶¡­’;žû.cÐUêÎýÑU=©p nx©0 ¡W°$°w^÷Þ.%PT  )A`áÜ97’é*üЛ@Z¡×ïûhvñÜób«'uKŸòd*È Ä´¨ªkä¨j"Öç÷ÓC¬èt¦(I%/¸’•©ÈÝû¦Œnëù¸³üHP–Ýl½ M;Ûͱ UH¦Ži­§†-YsÿüZE)´.±xýú¯ot™GæVýåµOƒŠRhùÎöÿÊùC庀# êÊcge·ÛÌtÅÙÇ/ýgñL"x¢‹Xú¿}ß!Vpßê (Is[v*’ášOÄý®dƒ²”,Òh§Ï07ÕÛg©¢ã  Ð9e÷¾ÎW/b…)|±Î‹¶»¢eû5Û³Šç_ÝîB¯8´ZÌtõù'ûòÎgëTð…à‰Nv>Z±)h‘’,b-ùá•g’„Ø?7X꣕ûܶ`Ý|ÝtÞŒ¶‘¯>GPKG lã^¶“­IzÚÖ©Ð$.oà°¡ÉIGjꃗ§ŽmU–^ýd5Õ7¶êÌÇŽF3¯9—n¹îB:û„‰Á2Õíù÷¾à(ôÁSmvêm”©L‡LÆÎ¡ÅJ§[®¤¢SŽCßf6Ѧ³ÙŠ#V@IbazãÓ¯"ª*–þ¿òÑjòùZTIXðËÏšN'L©\EŽÎRwïÎêI•ótªH9@ Í ˜yaÚD%_ônù‰ õ‚€@JX4¯x‰Éb8‹§YˆE žÃdókþçÙÂtc,õ¤jÙ' º†É\ž—?êÚëP"³‰Û›žFñCô/¾©Rd¦;pY›ùBËÙM/ƒ•‹cX‰‘yB¡Iæ%Éy‡ÝÊó¬¼NOë7;+Z$ݲ”Û²–•Ȱç@`.P¨JæòL¥–½¢GjÙµo[°¹ã' Ÿ|ç|:~âš:v]É)êá_Ï ˜îʧŸ ý”yT?¸â zäÿÝ@¿½ýz:f\«RšoñêÍôβuÁSÒÖ÷/?#xÍŽÌuúÎ…­ÊÖÇJüPU]—UÅÒÿm{ò<¥Ö?£;x\/?s:‰‚;‡]ͦð¡ò£¹?ºìH \„²”ƒz7s-KP–z÷½ƒÞÄFàñÒ9kMfóélaÚ[MšÉ§Ñ¢‹çÝ[=©YúÚ O &s‡v…(*Á -;•µõ$k é©ýÜ• »Nd냞¾Ü¼;hÒÏuö9%$¨ƒ¸Ú‰¥G è–£Pk®$…ºä‰²%Š—¤­üÀj)ºäÔiš½à¤)m;q/ë,]uî tê´±ÁúÃåÛÄó¢ôyNr}$º˜yõym,fáÊErn+b“$nn/%”x,ýo?þ'³eL6= êŸGçž8Q?ló™Èû£MCI<ˆl_BS ÐÛ˜gX"(K½ínA@âMàñÒ{vÜTúÐé^¯ëm~x>6¦ú5ÿüKÊú/úÕœ_ðÃc¿Ýc’²Û…‡êÇÑÝÆÑÒ¯¶ª²/°KÚm×_¶žƒ<Ç'4ýýõ¥ôôKCO±’ÒŠF"ÙÕðÚJ¡ÑëÚd9eéÕW«3 à4¨ .pÂÄ‘ôɪMÊUpw˼¥!B]ðVÖ„ÔL¬ZrÑÎÑûúåf’DÓ“$íu–DY9Z‹Lh’è~]xë…fh_\ùÖo/Wîqë¶îk!°}±ô¿²¦¡MuÌ¢}’{&\Jäý®½dœƒe)”ÑFŸ&H7_ðš«‘¸áI’¹Qúçví×<±äè.y9m¬Gýó³U~ùÏåöª-ÜzJ5Z\Oýó:—3Òµ&ŽÄ!¾kƒQäÞZº–N=fl›u§ôö¢ù”1ZÆs±$è„„}¯çáR,ýÏÊhUŽš\$‘õ¨‰z[¡Jª~N>}„¶•¬ýðjv²ZG; Ðx[½âÞ[¼íˆ;RT Ћ 0I&æ {¾‚óOšDýóJFh¹PÉôkv'çs23x.Íð.7qw‹4µ™·ÄÊÒîý@²Ð­$ýSö?Zñµ|¨tLHÈp9Ñ^Î-! %ˆ*Y᪱¢ l×7=_¤Ÿ¢ÐÝtítåÙÓƒE$hÆÑæ3G°#,Å ¨§HÇ©;ý—µ§BS¸¹\_ïh]$84o{îñ¾?BÛJÖ>”¥d‘F;}–€••– ÐÞñÿR›¬aÔYrº=.M=$xN,'¢ÐH5$ ,ÔŸô`£½?:T”"' ,¥È@@ŒÞK ‘s–Â/ ×{Y¢g  o ÊJžÐŒ†ky-¦@$x7†õÉâ±g7¾KÉg^}./¶zzØHiV‹™n¸ôTšuÍym”#yø¾úüUÈî®*Ÿ²Þ’äÓ#àéeBç-ɹö.xz>‘ãçß»D->kê$†÷IüÐ_üã+" m®×éç5çÏhÓÿÿ,þ’3´”ˆ´žÎò K3[É:K±ô_æoÝû£+••/´~ tqÙéÓ芳Zçe…^×÷£¹?ô²©öɆ¼¶éæÒùpLüŸ‰6*ƒ€ ]N+}\Ûv²dl5¶–žàpÒ©9#1½Ä~Øò–ß–>¼¨¬äÎÖØÔ#0³xîÇìnsv¤’ñ}ý ß×çDšù@ ³‹çžÇž¯iQ}a÷Õç&ŸÏO*k©œçÉC¥(Z2É¿3åDÆBÜçÄ«‘#Ý™Œ&µÖQg¡½#»Hòx}>^G©Ž#ÈU‘›çX )ÌWî|z¸ôHêHç<±ô_æI¨övm”…vE ‹4EsDZw<óuõÜyoã)ê>DÀéOœ× 7¼>t'¡« ‰$À¦RòÀ¹^¿ï-V˜qªÙ`/©[”Qd‹4‰uBB•Ë–¬$Á+d~”l}1ÅÒÿ~!Q¶hR4÷G4í$²Lâžâ)5ê4"ÐèKÜŸ”¥4º *€@Êx|î=«ÌË™l½Ü“òÂB@¤HÜS\RÄG# úš´Äý™e‘€RŸ$ô ðxÙ]›mfëé<‡iczH )AI qOq‰”uƒ@hô&îÏ,ÓÔº¶E!¨  Ò-½k_¦%C,LŸ§´ @ á÷—pÑѤ¦ÎYÊ4!ÚmzÜ@ Ý_Ižes±‹–$µºµ%£c•;ߢìÂc)»ÿ1d¶å’×U«šÍo±4ÕT,#Ÿ»®(õGÖR?¶$e‹KÞ†§Ôµì¢é곎•¯þ|MOãÏþ-eõ›J5–ÓŽÏîS§']°€9£¨öà´}i‰:7ö¬)§ÿtªÚ÷!íúâA–g<éÊÈO“™šª¶Pý¡Õ´÷Ë?×pY<öÊ—XʤíË~IýG}ƒú¸€6¾7“ެ'[Ö`ur åœAF³škwÑÁÍÿ¢ý_?­‹–”O}<Ý*tx«¢$ãß—”¥¾4ÚèkT¾âPáþöo{¢ªªËB“Íd„å¿KF¸  ÐóteI毈²ÔSÏ•ìŠ7ò¤»•E&oðit„•'q±Ëx’‚tdç›`¹›“X² YYjI2—I-Wýný”úl®ÝNýF^Lùæ³Õ±X‰r 3å[Z­YùCÏ&³%‹çM}­”¶)—þ]呹Q®úrÊì7‘·IdÉ(¤-ß®ê²f á2™4‚åÏÌlW”¾i—¿ ê!ÒÈã¬V.…#Oº‹,ŽÚ³ú÷Á¼ÉÚ‘ñ X•´>iYJü`²Fí€@Ô°ëÝÆfGjn[¥'KŽwÜÚ^Á€€¤1,´*L~”À.=ÜM‡¨þàjÕ²>o)gàÉd4YÉïsSõÞÃJUwxR<ì9ÃÉdÉ Œ‚ lùYêI[‹I;%ª¼ÕÑŸ¬™ƒØR4A)A>O3YìýÔ9Gîh¥(IÆšŠ%Ôoø…*«a­ü×™ôåË—QùÚEªžœ¢Õgè™yc©|Ý´éÃ[¨¹n7 6K)Jîæ#´êÅ‹¸Ž³hïšÇT‘AÔÂdÍ-ž”}_q¿Ó]ðú˜a‰`YJÊm†FÒ•Àçu™äkûÝ™®Ld«’¯.•‚€ÄŸ€®0>ã_¤5á2ÿ(oðªHÁ€Å§¦üSòyšÂVSÏÊRÿQ—²+Þñäi<Èy <ÿi²J…¨?²NYœÄÚ“]8•˜W®y£O½/¬<þ–€úE·«R)~2ÿjÿƿ駃Ÿ®ÆÁý¤íðø¶ï¤5œ Áñ'5ÆR¤•¬(5'!¨C6GÀ›«RŠ>ÄH'âŠ'©£ÊIEÉïíÜcCó{9Ãzœ0šr8ÐC“RžÂç¯åEm%eLV.u -áÊÅE/«ÿTñNÜïÄ Oåãs’³²S¹ëò:kØ2u™:ÉÍÕÛT6 XÑT³MÕëåˆ~Eã¿­6£„GJ*X–’Š¥ —…¶8íIu:[•Lˆ€—Öh@@ w¨Úó>yNp¢Ê]£àµïytÈp‚ Í-ó•:KâB'–$ .©C럢ÐH’Àb{‘T[¾T…—PàbõÊê7YY½äšÁxtE§|í*~ ò0튫¶óŸ®QȺRb CJ.X–’Ë­¥8¯ÚýY}À¤žhQûY¼4Æu•Íõƒ€Ä@*ÎWËNíª“²ŽQM¹(/]§PI"áu•ªCꓵ˜$Õ(M²¯ÏW’ýC[^¤†ÃëT´¼aÓof·Àá´m +rœ F =ÞÑÕO]lù¯©f;mþà&rÖíáõœFðúK*EIú´ù£Ÿ†fíÑýT¼–¥D‘E½iIà v¿kð%ç lUBt% xHù7¾û£.ÅXÿæµ¹^½÷#Zö·€Ëœ~aï—ä…cÿ¨?÷¬úÉšjö-îP^®‹ågݛ׫¹Sê˜×Y’t˜#è…¦ÿì\iªÙ¿ŒCŽƒ•¥ádæ°åÎÚj½¥ÐòÉÞO¥±Nvß¡,%›8ÚKYåì~·µ99îwC¬lKîJç) ‚€€@/# ‹ÑÆ–4µîñÚKH=K 9¯Ð{¶hŽJ@¢ß-­KŽûLQ:1»ñ¨2!€€€€@Ï€²Ô³üÑzŠE©) Ñ碌ãìNÊ7ûR¤ç@@@@ 3P–:#ƒó}†À–fívY“Ò_;¯§tB6æ*%6 @YŠ Ч7¯‰¾¨¬¾ŒžœÕH6,@› Ôh@@@b&e)f„¨ ] øxI„k³ÉËá“‘p¨ðqž Ôh@@@âBÊR\0¢’t$ %±,%#‰:vJNC2šB    q"€Ðáq‰jÒ‹ÀN§•6')L¸™œÑŒ éu‹@Z4 `²d‘#wYx="7¯qÔ\»üÞÞ»à»ô—W·%Íç$¿Ï#”þ"BYJÿ1DºI@¬IÉ .¢eý4= Aº9LÈ  ЇL¹ôiÊ):Ž4¿—¾zí*Vzv{ßÌ4rðù3ÇγÂ0ü¸[iФï“Ñl æõ:khßÚ´ÿëgÔ¹ì¢ãiê¥ã}ÕT,¥¯ß›Ì;tÚlvÜ-êxÛ’{yáØ×‚×¢Ù9þš·É–5”v¯x˜*6J›ñOÇ~ó²e¤=«¡òuOÆ¿ÔØÜð: Á‰ÞL@ÖSú°&yó”„åiì~gIδ¨Þ+Uy­E4æô2vE<§$Ùïi5k0»6T®§-Ÿˆ’G4é‚ÇÉj/¤Š OÒÍÏSf¿É4þ¬ß¨k[—ÜÅ®‰?£œ'¨¹[•»ÞcŸËPÀ€²&dJw{8 Ãš†Œ¤vã´lv¿ÃšJIeŽÆ@@Ò›À‘]oQÎÀ“(#o ˜p-Øôl‡ù< JYc8[Š$ÀC-[”ê}I ¬<ÉÖ>Ùþ*ó-Êt2 šü}u¹©z3U¬ª}Ö6Ç¢pÙ²‡¨Ïÿ™Ìg [Ö`}ê/ÙHe ƒ›ÿÕ¦Œ~ — ·:s™mªNkM?uÉh²ð¼ªgx¾Óà–¢ ™ú¿äi®âv ÈÕ|P¯’¬Õœ%“5»¥¬ì9ÃÕþÄó#ƒÉÊ")#‚Ú4¿›Ê×ÿ%X;Ý'Ð:rÝ/‹ ª8 Ã§õ=&‰i‚ÃIƒá~—Dâh @@ 7KÒ ›~KЂԾo»¾øµ:ef+“(?/X@3nXNS/{†ú¾œ­3–öEhÇòûU ýÂŽes#¶ºH‰ê½+¸MïÏfëÕUÍðé·ògë\*½îî|½*¨(mýônúâŸ'±Åé>V”ò»S Uíý€V<{*­yõ[AkY6[Áb#e)6~(âš}ú :‡<Ø!YIÜïNÌnLVsh@@z™w$Š‰Ù–Kæÿ$lß*w¿Ë®mßV–'g}@q¥%»p:;ó×4õ’¿wP˜l™ƒø\«SUö€ãÂÖÝÙÉ]+R.}ÕåK‚ÑîÄeК5°³"Ïp¢Ê×pxÙñ:»ë5ÓÁ­ÿk!ëªÂƒ›žcD?»àí ºƒ+UV ˇ(K±ñCé&àõ}P›CþäÝæ¢’•[èw)|_@4øv*Š]ËûH-F K¨T2ÇHˆœp=e°›]¸ÔTµ‰v~>¾|é2Zùü9´ý³ª?²VeÍ*<†ú8?XÌÈîi£O½/x,;ÃŽ½9hÑis!Ì×ÓÈnqG‚Wy‘žl<‡(–dc×:IΆ¶s§ZúiÝ®¦Á¬ñXƒ)8ž<¾Á±¶Ðwv’÷Ùw˜¢§)Bà“ºl:âi}ƒ” ±d=¥"‹7M¡ H:=ÄwhÃvKà·Ö¥e†žŽi_ÜÜd­$±ÉZJ¡Iæ3M<ïOjsäŒP—D‘‘᲎’®(ز†‹ =ö'dÏÌíÙºä•Gæ:å—Á<]í˜ÍŽ6–*GÞ¸`vWóáà~莟•==L­Ï#¶<4¹%Ì–5$ô4eõ?¦ÍñQ´@ ‹¶ùZ4Ù¶'#:rS`9­¯ï$A$‚]hj<°ReNckØEª?ýF^ÄÊÒÔÐlIßwú³T›[Ge)éÂô`ƒ}¢÷ZXM»©£é„øºÉNë9Lx2“£Þ™ÛG„dJ¶@@@ {Ø/X æàôÂy/ĸ#Qïö~ù‡îs¢ T°²3xò¨pÌ•¼&Ó춇,eΞ°&É"µ5å‹Y—¿_Y¨d.О•¿QRIHí¢±×¨hs#gÜMÕåKYñ©ïRb±B ˜ð]ž£4˜$°„¤òõUó„ÂtÖïSáÂM– wÖì°ý/Ë7"¨Ìéeð\£¤B,NãÏù»z”Ëçm FßÓó&óSW~-ʰ´:îÉ”¥'Ûê5Ê’®ÉgUm¨¬¥ºF'ÕóÖÐä$?OxCêýêíýé`¾˜ÆkºÝY#¯k`6ñf6‘•7»MÂoFVÍéÙõ”Á@@@ ·Ð]°Ô<~|ÎË´«ßËz!9µ,²â†àà–iàÄïª0Ø¡•î^ù[’5™Où¡Rx$Äv i*HÂnVŠü> à²Ù…ǪKåë‘«)†[Ö<Ú÷Õ•ž…•”'ÜN]-N+ó§¤ÎaÓoæÅlEQÒÔ°XYê,ñ³ç–ΊÒC*XEf¿I>'[©¾,%JѺ7®çõœ~©ÖHòºk©jÏû¬TÙhÀøkÉÐ/ýÝZÕû‹”ŒyY<¾ò¯å¡Hÿ v —綠²$Ê‘ßï§å‡é«-{iÃŽ}Tq¸¶—ºŽ€)Ù™ÊzR|"Ñ™XqÊá€ìL‡úTïSÂ(O“8Lø0»'œH8  ½’€<7ËC³Ñh qÓÊ˰Ru£›z'Ð˪n÷yý›ÿÕI¾z횎×XëPņ§x.Ò²e "¯§Žšëö*×8½€DˆSQâô!Ÿ²¨«l‘&™C%ë>9ø¥¬‹#ðé‹ÌêåW¿t±¾ü¬©XªÂÛ²†rþzMO.ê‹Ñʾ¸z•¬Xݦ<9'iÒ…‹Ô§ÛY¥>å¿Õ/^Ü—úC«iÙßøÙ§]Ú¹|.Ém:Àã¨i¬gXH,‡J1æñnÑ—¢­6-Ë¥¥²$ ’®$í`%é?‹×ÐÖ=72 FþêßPG™Neºäð¸ÉÀ Rï%à.H•Ç]DþæÀêØÑôÔÏß>¶.y&òðdF/^]פ6+› äPN–¸÷µ~Yr0„ †6Ê€€¤+y`n}x6¼\kWÊR…gJTÊR´,Äe­¹v—Ú¢­£;åd(‰Â×ÝäjØ×i‘®£Q'ß«®ËúQµV°5lå ¬‘Tpu§eua¿g’ªz@®C¯(Å­cž¨VS³Þ´S–t%©®±™þùÖrZ·-pó™ü>št œÆÚO£Ž ›oúSó–‹¿T ùEôùù×S¾ÓÉ•Ë{â—)ä´X©Ñj§»Ün¢=ªÈa·Ò°¢|åÉaÒè\n cmŠ]Ô  ©K@·*™L&~˜6ÑÐ~Y´©¢Ž-Kc©Ú7„òMå©+|ŠIvxûkjî•tyÒÝm¤«Üõ¯¹ôB›s‰>¨õ ¤ýމʊ4¤_&[jŒu…)Ñí§Zýi£,éÖ$ŸÏÏ­•ôä«‹ynR£²"MÛ·‹NÛö5e;‰,Õn°DËÓœS@+¿s¹í‰–ñjÏÀÑ7¬!ÉVÐTO5ŽLªÉÈ¢f§›¶—b…©€.ÔÌ~Ù>e¦– $è ŠRàZW–r3ì48×Fµ.ÚຈÎÈèb.OA’ˆz{¿ü#ÿÖ³»I‚’²X÷ÏËâpäÙOàu¥Æ«¡œ-X›©fÿ²µÚyµ]ª‹EÙVÊç©fö¶‘q¥I¶¾ö̓ʒnMEé«-{èé7>#·×Gù tõêeìr‡9Jßò½÷JSn?úâº[©9KŸÔ™˜¾ýð½–Ã.~róÉEVÚ¶ô}Ú>9ŸŠ¦m1O÷½/ÄÐF­  ©N@W–#Yx%«ÕBãdÓþ:ñŽ¢rÏTb „ÄNõþt%Ÿ¸øí[» «,q»ÖT³•dëÉ´ß;‰­ƒã”Ui<§Œ­lâj) “Œ=”¥ž¡0m‡*J;ùþÓo,eEɯÜí®XûÜíÂ0ë §’¥(…²4ó¼·!5•T_{ˆv•o¡•(—#ÄL5„ߺð\¹>ø¶%”öA@úýÙÄs|- YÌ¥mæ€H6–g£=Õ.úÒyeP®é@ß€Ò z)Ñ W5‚i ɵªg +Áj|źÄãÝŸuRzQÚPEéHu-ýùÕO•¢4–ç%]³ê36LïT@IDAT(J½à3š.ˆ¢ôùµ·%Ü¢N¶œÃåôí§¢©å»9 £F}8RC^¶tJTF¹g‘@@@ 7X–Ôrü@m³ÙxÉ ;-Ê¢<KÒ,ôyó÷8”x|ªíÍ\{²o.-“–7}¼ì=“Ãã'V%›Õ¦ÆU¬†ºu©/Î[JyeI\ï<¬áo}®ÖL*¬¯¥+Ö|ÎñÈðPÚ“T=ÕvS^Z~ÝOÉ™Ÿt¬ÍõtÂ+OÉ릋ׯ¢ÁÕUÔärÓ3o.S÷¨Ü«P–’>,h@@ \ð–%¯Kh·óƒ5o{\ Ì$»‰¨ÉŸKŸ4̦ÿ MFJ Î7€>iœMþ²qÔªIÁñ”qe)tÞR¤õö–|)«,ÉC§<|z½^Z»m/Oª?LñîšUKÉêóöþèG74æÒr¶(¹²òºQ*>Y¼°(JŽºÀZâ’wÕšÏÈÊ÷çÞCÕ´jÓ® Â$&$èÍĺ$sX̼Éô-K¢(98‚lf†&Ù•ÂÔÌ–¥%?¦=žÀ°½™I:öMæ–-n¼Q)¶6Žò;±¿U­/™ápPF+M<®V«•]ñAúÚ|%Ó”T–ZÝï|är{è­¥ëÔýwâ®­”Ã::éxCæQ”¾¸®g%‘à˜·ÿAyûwÉn0eq¨ò“vnVÇï.ßÈ÷ª[)÷pÇ "€€@/% »â‰ÅA¦Å¡°9zZffåñº„“ ­”eÑ”KÞêækØz1“ª|Ãz)‘ôê–„wÿ´éÇ´¢ùZåz—eáEpy¼òs2Ôø98º¡(¿¡–¥¾è‚'£š’Ñðt«’¸ß­ß¾U7–=uû–ôº!m\4ò:J_\{+93sãR_w+ÿÙ4xsøáfìÜJ«GŒ¥JÈøÕ–½ÿŸ½ï€£ºÖ?Û›z³š-Yî½blL1ئ÷JÈKPRø'’¼PÒÞKH z3`ª+¸÷^d,¹Éê}W[ÿçÜÙ»;»^õÕjW{Žãiwî½óÝ‘4ßœs¿³'Ž_Ú´Z_Ò©ÅôW.Ï0Œ#ØBñ0ÑKµËå—Û%æñz0³;}<‹Ó&Ž6¢B^‡ó/ /F¶¾ õ;¡Ð° ,š–Ä!zïð¥% >îžŒŠ…%¢çZÌ•’c(Î0@ ’\"ºV«RRl‚üRˆ¥E<¤txÝnÔºwdIz•(üÎ)È’’ÔlÒ±J0á\‘¡b ÖT¨IMÃħ°LàÀ¯2ɼEóÎ,ø‹Õìê+’áœFrðjl¢T´g=ŒZû¡ºK!Û:ó|­/»¿<SÇû¡3]‡\À;Œ#À0ŒÀA Ä»„‰Ü-N¡PŸ=Rnt„Vƒ9 ;àD›š\:¨sËÇÅ`Ñ6MÛVM˜4mC™ø¹°Ý“m¾L°ûÒEnHêIR†Ý¤h Ã&É+H)•–T†RZ…g‰DÈ{˜¬^%Â*NɺlQ]̉!xÔP?aLõq±Näÿ*³ra~Ê+€LrÊv*™ö6(CµÃq'ŽB&F‰ŠÐ;kÚ©cp$óx9Lúè?ݶDêŒD–­ŽW@b“½KÝBÇF€`Å»äjifŸIx“”ptåÆ(g;y%HvÚdè;þDG´¸5àðjÀŽ"´”&8ñß}"HRô^È0kÀªšk–’bE¢”)©¸ØÐ«Dó•Ð[TÁ‹Ë™;1=.É}•pãû£'ë ÍááÅ µ1d 9š™ «ÆN"KÒHj³$?Ù¼R,ÈèÍÆ¤ Û"/b;Žo«½•íPYU/Hä& k£%Õœ Ãð—¬U‚õ­þ$Ì$廈‰cî%3zAíØ¿#Uu0º%6=J·v™›bF€`bŽ€â]Ò ïý]÷Y‚jÅD”4˜ƒPAà{ÁNó›œ`1¹ —"ˆpéð8qq!qòÐõ1¿ƒ¡Û Â:’$#ªÜ™ñ­ß §±ÂÐ:$J&Œj¢yIDŒ(ôŽ–ÂH‚láßTµÑ1¶ž!®TGûrQƉ’N/¼EŠÜ;%R0D²D‰Bïˆ8Q""E ‡u:­HDÛ³ ÍRqE–bú¡ð 1¢9Käm ³u8Ä:‘þ«KI…7fžõ¶Tñ 5},\4*¤§†Ÿ$1#ò8çüÐréYÓaÉê­ðŶÐØT¿\Û ¤x"þðZôf ØÎioüÌ-½jÇêTžÑf|féÙ¥g˜~ùã7^ÕÃ…F€`DD œ0ÑKºâQ—süxLIk&;t8̨Û!È BÐßLú{©, 1b‚Ôÿ'@'škDcC y‰(¬Nñ*óbÑ|%"H´õ;$IL”‚cWd‰~8è‡E„ááOk»òjŪD²c™9ðÊì3Á‰evz |ëÚó`x~V"Ý ÷5¥+o¹ø ˜?m,<õú2hliƒƒ‡·@Ùˆ©˜Ã!e@úGÉfg¿ù$¤ÔìuýÖåmÅ ¬‚,¡w”žgZä/¬^WÊ0Œ#À0 „€š0Ñß>­†Bð´*O Z9:p~o¸Ð³$È’ð.‘(„’Ø]þíL ÛŽË®þr!ÂDDIz• ‚‡äUx—pMI!IäMÂrX–=JÁac²ä‡SI>kÄù ‰b V¼1kž JãJ àŽ«€ ç$±õ ‘E9ðàm—“¯-ƒò£ÕpøÈN]:˜£‹©Öç™ï>‹¹”*úÔQ#J¥’9(¬ÅIˆðñ§_úlŒ#À0Œ@² “FC¡è Y’ z9§¤µNÌKèÂâ”"FþÍd²Ý'D%Zñ‘óÆh,h.…D’‡IY”¤³j’$Ç1º½JÌÚâŽ,Ñ˥׫x˜¼¾Äš§D2௡G©_äGå½7,BvÎaXýýÑHÅwß½é|øÍóï£èG=>ºF•LÇ_ÂÑÖ’Îæ|¹§¿]UÂHQRža&Ký†”+`F€H0èE[¾¬+„I‡^ xAW’YDa‰}`T¢1¤Äÿýìû ödè[ã@ž%¡Hˆc@ï¥DŠhM¡v4. IÒá{•²'¯ï{†Ö•qF– ð5ž¾Ì'ÚÊòqSÅ% ½»ëº…âAZËàÝÝÂ÷ܰûÇ{Є!yU5‡¡pب¨thò'/CážQ©‹òJH=¿ìXŠ ¬\ #À0Œ@‚! x4èÜ'^Â…gßíH슼H¯¡Dbø‰’ø»Éó–ú;Ô’ìÐZ.’0 òŠ›¥·‰Æ…IR׈ÇY¢®ÒË¥ò²©ˆ=tÝýø9[“šÛ‡—ŠÝ~õ9j3ÇOç†HO2PÒòWœ ü×R¨k89™…ø•ª‚–½÷5„èùU?ÃQ«˜+bF€`5i¢t¯W‡‹BŽè}/ü£ô,%à­Æ]—Ï% •&ÅãG„IMè<[çÄY’]U¾Ê'Î|O'LÃÜ8}r”ó)ÉûáutW’ÓÇ•ÀÖ}p¼ú”OêsÅãW¾¥[VôùúHÒ/yµGT½©<cF€`’õ »|Ç“kºåccâ¼÷%ʘ)¸+½•c ׉rƒÝϸ"Kâ‡Æÿ²)¾,$ÈÏLmjTdçaخɉý| ŒÜøYô±"Ï’úŽ~ \##À0Œ#Ðtö²Î£?¬„5[ÿˆ+²Ô¿[¼«÷æO=2Ólƒ×‘$i973 &”À΃Ǡ©¹òrü{zû£×.…Që>êiq.Ç0Œ#À01@€_ìc27ÑkdÊå^_ÈØ[P$vfN( ä­E`Ö„‘¢þ¦–š^µ3jÃÇ0æ‹÷{u fF€`F€HNâ†,%ªëµÙl:[J/ja z–ØbƒÀô±#ÄäD»£Üg%ÒØUïö¨l4 %ê³M ¸.F€`F€`¸!KjðÄÜ%šé—ÖhM½ÌÉLŹ3Õ«!³˜@ídNG·ÍŽ]ýnÌVžëƒGªûÙ8'¨®±%°¯ÞøhíN¨8^ ÇkáDM£úÔ n§Ÿ¬€iïýl¡a„ƒÚ)nœ`F€`F !`²çÃ6zx>¤§X¡©µ]ô4œÉîïÀ­ÒЇeÁ°ì4¹²6a¸ Íc:VÝ#x–Ê*Þ¦ôT+±êŒ,mCoRùÑ ± i$Æ;ô„åTì…ÒMË!÷ðî·ÎÍ1Œ#À0Œ#À U˜,ÅùÈb$ÌšPŸmØ#zZ×Ô Uµ8×('8רº¾jšw2{¢’°5p@µAsFd©º¾hî‰=H#oÙ¨â\hní\8æD©Ãõj]²žX®K¶¬€Óöl‹e“Ü#À0Œ#À0Œ@ Àd)y’I–¨»;ˆ¥§„à•vzW>ðÁÈ¢\X¹yŸ(CawÓÇÛ$øÐnïÛeEy@s‘:³[.9#äÔ¿ÞÿVoÙr,V;äYbcF€`ÄE ;!©Ä½³øè9̓gëL–ú†[L¯UœiVhlVBñhÞÒ¢Ó'ú° É“´ÒÂ\ ¹F]y–¤ÂP:I–Ô‚ÔfWdI^ÏkF€`F€`zŠ‘"ZèãìœP‡‘).Tâm³»ÁÞáí)”Ý—C‚d6éÀj1€ •”S,&JËÙé) É“\w_Yò–`²” c?{B)|²N™£–'¼ý˜IÚ쉥r³Óu^V*†Þ™0l®#dÞ’ ÁÓcž¤ùÙ^Ï'F€`F€è)D޼H‚qþõŠ {1Bæ8œ¨kbS=­ƒËE¬´˜0²œ6 qZ…V« §èµ2tjb²” c9kÂÈY’⤊·÷ðq }i³±\OŒBñ(mEUÈ­¤×ëÄ©¤ t:V•ï Ž\†`F€`"# Iy>Z» Öì8O0t^ãµÆ‡ æ}:о°ãB×°E b®ñ€O㟶 ê›[áómà‹í0º¨.:c åe ÂDĉ-&K¡xÄí‘›,t›Ö£À©âY¢W‰wêðýÅôbM‹•íÞ\¶ ¶‹iZ0tLF/‹FÅÕ`…uF ‹L·i;”c´ÑË6yã…sA¯WhB²Ïcb²öÀÄû.©Ý-ýb‡è&y—¤u•ˆV– _—áÜ$"K$ßYî÷,ñ|¥p”xŸè;w<òHÏîý®^«¿ÒírŒ¥éµ8“ìN/.ÁŸß¾·´WRþƒ›’öî#ݸ˜ï¾Ïwþô‘Uø =“¯ûÉk?L3»ÙE@†ÞQú…VlVò.:&0QXè£V»Ã#õÎIàB´~÷aÌç™çΞˆQHz‰”Ì!yL–¢ö˜Å¦"RÅ“dI¶¨Õj`æø¹ÛãuYQ,ßZœŽ±1Œ@ÿ¸ëÁ_ GÊùì¾o"72º=ʇþâa™èÑͪOôZÐiuýkˆ¯NjèÕè].#SA´¶;Ì8C{1³ø8<²ÿŽõh¡áÁ˜4%õ£2 7¯&JM( ¾dÕvÑÍOÒz8¬@Árå4—Iï nãXºf7L;ÅÅRYJfÑ&KQ~кºáùY@y’ªë•Pœ§«>\³®]8[%ƒg5%©àCÒÇÊì#70µ‡çRêI"ÚH=¡ùI8q"pÊdÔ}ùfcÞ#ðÊ+¯èîüé¯~ëóyÞ$¢D#~pëEpßW/„ãG0Qê=¤|E(À“Ÿ9 ½ç:¸fÑi@¿×1ôs±£Ý³½Lgö¡J¾„èI”Ü( ^‡ó©×î8,ÊêÐ;Á–¸w‰l# ‰UÕ5â<4·§ñNFcÏRŽúå f-=µo^uÐÉ~û½"ÇèE¯§vËÅg-lŒ@²"ðɶýObf‘obæ?ßó&i® ¦ŽO½¾ Ž×4bÿ>ºó§.|꿲&žúÊ}I\HÐTÓ(‡ÒÖýGÀƒû$®õf%îMqÏ…j¡Ö“ h€mûŽ@nfš§i$)žlÆž¥dq¾_F€ˆ:·?ôȉ(aL·ïŽkh®:O [ˆzC\!#ÐK†e§Á¾~)L;£B}¯Ï³äއßËj¸8#p Ê\%J<ë§Ó »¿¾˜`’ÛzôrœPÿá€R«š¾ˆ­$;†|ÿ‹€Ñ ƒÛ¯Zbžª²Àåýàþ_ÿ:u`[åÚ‡: Y¹JøÝÜÒGkÄ-k=,5Æ^’¥c8²±¹Åd<‚'c(“¥¡ðDó=0ŒÀ  ððÃÿ0k|Þ?ãœÍùs'ÃY3h>=#èQñîëBnVÎ:ð•6¶8½ä%2<G«Ðë€AÈ^+"›é6¸¯  ñq,­ø-(ŒWx$k¢Z&K<$|˜`î8æ>þ}zñÌNOËÏ™Ñ]q>Ï *VTM½å¢y˜‚Iؽßzø‘ɃÚ!n¸ß±G€< dÂÓ€îIú7ØFÞ—ÓqÎP1†Ö1;ÔS”áŸ#D}ù!Hƒû.ÿƒOÛáö¯¦Á™(¬ ¶¶¶`ß^ÚUÕA""ËÙÑÓ#u*è˜NÅ]R{)KîT¢ædÕÐâ'>äA¢9SU(,AFs¡ˆ(‘Qaˆ»rxPvT} ÷AéLìå0¼ØcÎ-2Œ@‚#€ïÄïNí`é¹&8~Üýø@@§÷?À>Ÿêµ0>úƽ`ú‚ÍAÚ¾»ã”:R¦ÃtPÂJŠ BýŽð²PHaÖ4#*ÎPíÍuõA¯Ï(TÆ##¯TA~ç?"Ú¸ŽU‘§Ia3§²å3POÚ‘£þʼŽz0ÄqÓWî#À0Œ#À0ŒÀ " C±± ]6ýÑrLc !LDT–}n‡Å¨<7m’ÒS5”k JPö›>,ýT‘ö&REêz”·‰äÁÏD;"T†oËv»B~F`ØßW®´Á’”:"uŽÂíÖovÂÜÙ&˜;Ëœu €ƒuØ_c‰E–è9 “Áس” £Ì÷È0Œ#À0Œ@”s–üóX¢\uŸ«kÆ·Uª¼E²¢Õ(¦ðÙ*;z˜|0²Ä ”ó¨ÿŸâ±5›¡º•—ßj»] ‰+¦ƒ}°íÂ^©"ÚߎDŒÈy›RS´àó’1Åó´Íê×ß6¨¶ÁG€ÉÒà÷€`F€`„@@£R7 mZ´¤z ,è‰Ì›ùç,5@FšJŠõbY0ß»à½Ú¡®Á ™^G6I-áf±Ê{RΜ¬F6¤²Ã•nhlöŠ6Æ”éÁçS"ŽWy ¦N!]ýmCÕÜ m*ãIã+ÇZvDýÈcCuÍdi¨Ž,ß#À0Œ#À01@ÀbT<*>mPÙˆŠXArwù93 +}p»B:˜Œ;8ÝGä|\^rƒHšžºê0/¡H´{VÈ’œ¯¤Ãj#˜‘(™p]i…¢4%§‘Ǹ¼ºúD»½¤ì¯W[ÞÀ½âÞ SõP˜i³ ÇÔD„I ÅSH7ŒÎC¢DäWòÒ8Óx'›W‰F…=KQx6‡55@QC-ÎYrû+·F¡F®¢+Þ^±(챤®Z„áuU–Ï1Œ@ìxõã bROZvt¸ º>¨&zÎ{ ÷øX-&¸õÒ3Cª;ä'Nò`~N:\0oŠÜa{o-ß"¼I¯²!pœþÐõâùIù?Bœo a‹Ë£˜™£‰‡éÅéx)d Ãððš^¦éåÚjEA .¸Ÿk…,¡É‚‚†ýà—qŸÆ§w“¬Ýr‹±! ;d"Q?Ì&¯GW_g.ùBðèÉ`ÏR”~>ÎÛ·^˜{žˆ‰?ï´‰@¸Ù¢À‘ªzX»ã ¨øÜ½Û£ß×È0}B€Dh^QzNÔ6¢Â^ôìLè¶.—[ÉU' Ö5‰“÷æ÷aMw;iºFd‰æ¯ˆ9K~ï½`;.üxì.£‘ñV68  ³¬xõÇÁ¥«{$hÑË„Á\IƒUüݨOÌ'SÄ”æ<‹J³,BuÔf³‚‹Ù,H°†§OÊùJ4vìYŠÒ\ØPãpîÅÄ?óÖ ¡Œ¥ª¹?ôúÙ·WˆLÜ“UÀ°æà ƒÄ0ƒ‹€Ù¤‡Ëp4ò²‡‹,ÈsêuªÍ ©AâóÁ;à_ïûŸäK]ö´Ie0wÊ(±¨ç(É2¤ÖtÓE¡¢ jQ‡ìŒT¸ôì`åu¼Ž8¥ãrY~¤i.?¾ú (ŽÿžíJï <‚BðèE›–”›XFdbX^šÌZô]hÈ“q\–Õ£áÕ*6†6Jñsw>]³Àßiùç'‘§Ï &—‘©>(A7 79†ä!¤Kßdõ*Ñè±g)ŠÏð¢ÝÛàXFP<ýóﮂۯ^ÅÚ“»*š—ý,’ЪÚ& Bö*%÷óÀwŸœƒÒÞ+7íRž#Ó{«¶¡”øœn;;c|‰Pº“W£j-ôzdQŒ> Ɣ䣈Ã0 5¦®lÂÈB˜3yW)ÜnF"ÅéÂQI¼}üs0{Maz¿BÒô n?g(‚·^½X5ÆÃ©ö.½^A–hÞ ·IðE•³PÛæ†ê-xÀªkÇÄB&Ï ¯´>ƒ±ÇIâÖß5¦Æä²N¬¦ ×´¨R{Ùf/ KA¢‹BDRñÃUJJŠ ¹6«U™·$Ä’׫D€1Y <6ýßHé°ÃU[¿€ÏY›ö†ôÖ‰œ#ø;‚­Ð/Ü1?ÊŽƒGÁàqÃ5¿«3T¸Õó¥Œ#%è…èºÅ§ÁŸ_úHÔ¸¥ÄÏ™5^$•íª‰kÍÆD´õ°¿¢*¤)ÞÑ1q|õ6!c{ÚÄ‘pÙÙÓ»Ò¹ó,mÛ_ $mʘâ^‰NÈëx¿à«8‡éÅÁð¼KŠG¯½Vô áN©>©*ƒÐ¢H@da”H³Ã M. ´z´@œŠòú`[$Ql‰€Vã›Î ér0kÑkDÊ\3Å«”¢&›MÌ;#qEØ!9ç*ɱ`²$‘ˆÒšÂñ.ع Þ›zSf ¿íÊsøkfñµw8áé7Vu- þò½dûÈkáð»>ÂÉ—1Žyv¦ŽdåÎ/ò«(%~÷õ‹ºl—¼Eß½ñ|X³ý H[YU±¼Ýáç·ì«€¿q9d¤“Óª/8YßB”èÜÁ#ÕÐÜj‡´V+Uc5T¶ñ}›Õôi02¤E2äB$ &JD’t(m(æ6ÙQUMßåœó].81g¢Y’n•7Šn'蛤›KÐfÕßèuøˤ\|¸a¥±@Qƒ"÷.B'Ñ‹dE%g:‰D‰ˆ“v ñKÆÜJê¡g²¤F#JÛ4Ÿ†< K¦Î_7ýܸùâ3 ¬(7J-$G5ô5ù%ô(Q’K#~a¾bëZ(« ýòœHð]2‰…ÀµèÙ!¹n ÇÙqà(ìùòx·7@Ïœ1V,-m¼æJ…× É9‰^§†ëé<Í ýÁ­…§j“æ<…­—ÑÛÏáÑáÈ ½}|Áæ0½«â]Â7raFñD)D‰Õhþ‹Ýî€üJBn|Wò 0 ýÜJo”$Zrí¯”W½@€°'£µ\iEE;½NI ,äÞIÁE9,87IÌS"Ò„Û‚(¡§HU²Ê…«áf²¤F#ŠÛã0ÿOšc¼6s¾øCÿ›çÞ“’¯\0«Ó¯¡Ql>¡«ªkl7>Û$BéFÒv¸vãjÈmiJèûâÎ3É‚%”]ˆª ­Ý)n™¤Ä'Œ,èñí“èÍ;¢…Œò×­ÞvÞ^¶)P‘("@”a^m|±]Ìm”Çèæ<’Qxô<Ì5y4ç{QÚÿã°s˜^ ‡X’#œjˆfjw”Pš^Òiþ!‘%£ÈÇdBϯS&RÌs£‡‰ÍsRý̲O©¿C§$…0‘gˆH‘ «Rï~Gy”ˆ‘êy˜h1ýRáX6Ù=Jr ˜,I$`]ÐX·­þVÛ†DÉërX·óLÀ‰Ês§ÆP•|(yz ·Û²·Öá Ñ>| "Óú¼0³¢Î<°Lnš ÈÆ0‰‚ÀÅgNƒ5(óO^ |hiwDìú¡c5°IŒ´sO›€²à)rW¬)Ñ÷EgLjô2S¨ž´cXïhv²®–~¾Cî "u&«}öÍcÿ^º~~çU@$96ðÕ›Ãôb0Ôê—k…<)‰k)ä§i~Œ_ÈɳD‹›È%7FHQ…0)d‰ISïp'SÈ’âY"ÂJc@¹’ô’,!Q"bD^$ZÈÓdÄsrŽ{”‚Øó›z‹Ù"!‚ vn†iGÁòqS¡";v>!€U…r»ùwŸ‡_buø&™Œæ3œÄ|(UÍvhèPtþéþI½e$†Û»w''M¦‚ïuH!`6àŠ3á_ï)!qäŠdôUù“u»§L†P òÀ ÜÈËJSïBzØü£1üŽ^¼¤]ŽB$±?T‘@ Y]S+,Y¹®^8[ãu’!€¯á¦7€cN/éòE[nSè—¶…GÃiÆ<'€‹ô,E"KL”ú>Hj¢DÞ="±‚,ù=KЧÉyûhÁùKäu¢2D¬Ô¤·ï½:W2YŠÑXæ75ÂWÖ¯„&‹ vŽ€E# Þ– õHêkša7.Én9-Í@ó½&ž¨„T ½ccÄF`þ´±°bÓ^ dÒÙˆüìS+·ìƒq²7vD~ÈqJɠΙd³˜€Âý¤}¾u?ì¯ Îi,ÊË $Žf¡2‡J¾|}²~†ø•Añ°äNNëòØ€vã–k®üL’XÆjÁ¡ƒ«†ýí iâ0½Â\’$Z_ÖQ—‰BïÌ›D$‰>˜W‰Bñ(úNx–TÒòçv€º:¤ª%¼¥ IvܧCD~d­É{$É‘’C‰æ&I’ºY_2¯™,ÅxôÓím0¯|Xø°Ö¦¤á’8©.Ùr Ð_©ŒöV ’”ÓÖ &tdz1ŒÀÐA€þH_y–~ÿÂÒNoŠÔé6…ûÑÂÆô|µç0½Þ‚ÖEyõK{x1½ G¤ïûç¾×ÀWL–ø9`F€ˆ+ˆÑ2zž›)®n€;3¤@â¦çi¨?¡õ$·XH4œ_ð£‰&× ÈÅÌÆ0Œ#À0Œ#ÐC0¬©§„<¡ÑÁý:oVu/ãbŒ#€0YJÀAã.3Œ#À0Œ@l@‚äÂå-»2ÏEo}¾ÿæw`[l{Á­1Œ@¬à0¼X#Îí1Œ#À0Œ@ €i+z‘ž³˜áÅß µ Óqî(#ÀD&KQ‘+aF€`¡‚€³xQ£…çØ{4TF•ïƒèL–ú†_Å0Œ#À$4ZÐ;ñŒZ¯í’ƒ½±¾5ÎG±Ã™±n·³öÊŒþ®ŸË5Ãû»SìwVœ3Œ@’ Àd)Išo“`F€P#`ÐYšq?Çâœñú ß{ã~õ¹Xl_ý\ x5muÕ’#³ë >Ç$9L–’üàÛgF€`’ $H5xÏf—lÏ÷Ëô&K}/aF€`ÄB€Ãìk¼¸·Œ@¼ Àd)^F‚ûÁ0Œ@?hln‡ãµ f³@ANètœ¢ŸòåC³ƒÈ·À "L–|nš`þ"ÐÒf‡W>Þ»ÊA»£#P“ÁäfÀ¨â<¸òÜ™`1çx`û#ð×W> @ñ³;®€ÂÜL±ßÕ¹À¼×p˜]\O§óù|žã}C@ƒ? lýC€ÉRÿð ¹Z7¼ÒŸúmȱ.wÜn¨¿üÖ.‹$ÂIÕ`P%_s+@/Ù™¯¹Œsf‚·¡ Z"n™ûÈÄw ÿ^ºÚìA’$;æñzáèÉz±ì=|î¾~äe¥ÉÓƒ¾~ìïK ±µ]ôã¬cáÒ³¦zŸ¸‰‡Ù%Îø)¢¥¥ÍUuÐÜjÇßv±öú¼‰s# ÐS«Éé©VÈHµ@NFªø; ”\'Àm j™,EþÞ²÷Þ–f_£XWÊC÷aú$Qcã׿ Þ“4o¶g¦IMË W‚&ÅÞšºž]Ä¥FÊVÃ3o®8 “QÔcv»=s'ëšá±,{nX$^ÛÛï«1íóPl,73¦ÓÇŽ€‘E¹ Å(&M]4“¥®ñéÕYþñw®\r~ÂÐæf‹cžŠ£@‹4Ÿ7ø2#%ÃZ›‘Úü\Жë¯D)î›ï‘ˆ^$C/}°&¤º±#òáúóç@Q…’iàÎ]zùÃu°¯¢J”³;œðâûkà§·ãÏGe„`Ç;‰‡>ìf— ÃF‰ˆRu} ,Yµ6í©é¹Ý §A z¸tüË)œ(ìèÐQgÂgF·,.7Ô4´À'ëv‹eôp%L»´ '@š˜8 :“¥S1éóo]ý)ad)?úýdɹj-Ø_z#rýÈìuEù Q TçPøœa_[ñ¯ƒÆfU®Ç>Ÿ~‚~LhÒRÀsø¨¸6bz=è óA['Ô`5B}Š êRŒHXˆ&VêÃ_öm.Ènu@v‹©†ßýs)Ì_7,>Rl¦iŠUŸ¡Sÿ&B¯‡Xõ“ÇAÊ}w oKàÖðÅÀñÑ2hÿëó€oâ°6ÿÿãÏbÛ¹z¸6ïËW¯mf†rþt|¸ ÚþçÙyCæ‹‚åö[@!¾v;¸÷—ƒýoÿ7’,iÖ»¾¦Eg‹Ýæû –›®ÃÔ €?=☷ªZÿàÞµ ³¦Aê/~8G2žý#øZZ¡áÆo‰ò‘þóÕ7ÚÔà/IÝðÂHÅø#Àt‚ÀšmCÎ\~ÎŒ¢$O’éÂ3¦Âóï®–‡à†ï©ÉÍxöí•°ïð‰@ÚØ´ç°ØÏËJ…;®>÷ròÏ%«Å—b*Dó´ø³üÎòÍâõãK à®ë‚Ñ?¯ñ…%Ÿ¼]²ÜZ$A´LU÷|e1ì]ép°~ã&áq1ΙJq3ÀùņžêÐËäÚ¼ìÏ¿ ä%²Þv3æÎeŒ§Ï%Ó9g®k{â P@MZ¤þò~ö§IOÝèRpïÜ('7´9™ÐöǧÀµmP¨Ÿíw凵ÙYâçÖ]èAºR¼È;FÖôŸ`˜_­¬‚׌#eêQqR-ÞŸí÷,÷¡·Wl!J4é÷ô)£Áˆ¡¸;›öŠZ)î•×ÃCߌ<߉BèÈtάqPˆ¿7`ˆÞ2 ”¶½F’,]yî,hEõ¾çß]­íŠŠßôq#`þô±j5ËKkRËÚ×ôzÙ,& Yt2"rD„¤YÌF¸äÌi0Côjðÿ‡_ìÄø|…Xî>tLxËfM(•Åû´^¶aoQ=|œ‰j~-øBøùÖýPUÛ$ê¥ûŸ:¦(D2ܺº§ð²É´‰ÃìxÀ%Q¢ßOµøs÷·7–ãÏE‡ð"í)Lƒœ›Ä—oÛˆL˜pwÙ]ðì;«á»7.Âðe%Ô™ ÎN‰¿aKž¡9Jdž/+ÀþÜÄ6©ÉÙ­VHùá]bß´ð¬SÈú·¡õ±'”yEXªý¥×!ÝO–4ÃÄuôŸkï~p<^ ëXú™8î³;À׌/8GŠL›—‹ÿŸJ–o¯eœÕµ"<ÏpúL±¯+ÈG‚µ|M-àSÍaòµ´‰0ýÖÊQÚ1"(ü‹-~ Ï߮⠘x ÿ¾´;á™·VÁ÷oZ iø;›L§š¿w1p=c²4pØv[³ÖOVDA½lܸFk4¶uƶå†çøÉQ¢cjoŽÆ?7€Ž;^~[4çÎz±´E U šôîó­xd©M- ®ñ‹@¨Ïó6#À <ÎÐy9r.Po[&ï¾ÛìÂySÛrcÑœI°ô‹—ÿý¨¬‰, ËJ%ym H²DÇÈ‹Dd©/vz£"…îGAi“Q´B%yÌŠ^¨³ÑëóÞy™(ô0’ƒ¼¦«u~²w8EΞ©xÔå+ äÌFÏÍ¿"Û²¯nÁI†¿/;»'YW2¬ñýk+>‚ÓßülK†ûÊ÷Hó“(ôÎÏûkŸmB5Î&!ܰ§(‰R‚ ¼ùì^ •œVÙ Dvþƒó2¿Ž!y(³ƒ£4I=‡‰ÉÒ >ÄÚERœº@" ¨…dw½MJh‰Üyë ÄGçD¥üç‘Êß|øÇžˆvy”:7ochê«Î¯â3Œ#0PŽ µ‘ \_ì$&‚T[$Å83*¼e§ÛjzUµ¡×Èë rÓåf`mSy€è`~DòÎi!o•´bȈܗëÂ0Õ¼xß}%K'1ìOmÿD±ŠÞû\}HÄùË$8As—H)0Ü"ÝSx™¡¾ÿêÝtmõ›Â÷G%"KD”hž‰Qê@5K¶ÄA€´4ãü¡Hf1Cã"ïï±ìo¸5£ô­Úèž"Y8iëì"]~¬ ÌզÌÑP9uÛ¥J¬>éžÔçy›H¢äA5J¼·z‡ø0Rf†3¿b&ʪûIs˜Nâøå79PüÁ¬Ìîúç"žï‰ûp$Û†’âP’ïû+NàÜË¢€2^2JŠ3YÄŸ Ï¡Ãp8üÙ`¾á p¢ ‚&+,·ÞÆÙÓDïï î¡ó‡zÒmuèFÎÀ q#Š4Â^Àè#Jf?œ(&A }lŒ#00ÌžXŸ®ß#*'ÏÑ’UÛàV$AáFžÊÔ.^Ч!v‡e‡†Îí¯¬:Ed¼V ¨¾'-?ìy|0Ö”€7ÕHýé}¢:óçAÇ’{SuHY’$×Ϙ úÒâã¼Ã0ƒ½°_Œù„^ûd} R`#b@y—ܨJ%ó Éé.vóEÁ¼k”iñÜÉið#nñ«gÞž#Êzï:t4$Ñ,I{+QÒ È:û³6©T;·ã—ËŒÕÛ ûN²ã=µ çOÕH%yyÉáÖ}•Jž%T¾Û´§B(tÉú®Y8[nöy}ýâ9ð&É%;|¼~ùôÛÂ[Gú »‹HYùÂ9å&¯!‡…à‰ùJèYªªm€z …¥<&KCc¨)°Ç“B±IÜgD¡? )ó[“mî“¥~¦[€–.Œò5ÝýcÐb~$ÝÌšŒ!lžÃGNÉWD võß±&_k[ÄsΕkÁµi;&‘ (Uî}ø•×d‘êjûÓ@K$kêy %ÜÜ?)ôw:Ç¡çsÄüqÚSÇìÁW/€L-ª¿è³5Ђ/ïÒ¦e¦Ã_æ(‰µï^¿¶54ÉS0×ü?¼nT F¾ Q•øòÿêáãðÖÑâýw^~ürÚ„À¾Ü¨w:a?öçïå°»›T òšx[>züeGÖáýôÔ\~òÛ‚IÅ)‰yPèe²Í[b²ÔÓ'†Ë1Œ#ÇX0 ìèáÃÄÒ—n’º\ñ°L±ôåú¾^Cd¬3BÖÛ:IR–X%|¥ÜTD"%ôímß¹<#HȾ<»ñCG{‡"ÒâÁ¤¦ý1º:EjrsHrfÀ#;öÁÊêº^UI€¤_?›:Nî‚ _ô ø1£•,0i4”á\Î? )#ëL´ óÈÍÍ1¬¬t¸mÍV8„ÓÍ›>Ò0ŸÞ¯Ï¥Ç!ŽžÜƒÇŸ>Æ‘QÎS£ñO&c²”L£Í÷Ê0Œ#À0Œ@? —e5a¢—h²¾¾D_³r8ÑKeD"3{¿7® 0tø'SÆÂÆåë¡IYo- Õ¿;¾L\v½×?Û¾ö¡`ÖL$=_)-B”W(„wUÁô©í[ë¶ ¯– û37;K+"Y7âuìܯ.qÛ„$£ÉEWF÷êôãÖU93~È"’çéAÑk´àöuÝnWm…Ÿ“ãéÅ:Õ^%y<¼üPÝg²4TG–ï‹`F€`B@ñ2ÐÜ%%,«?Í4bh˜$'Qš¿÷ÿvútHAõµó óà­#Áp¹ž¶smI¤ûçZ>ŒDiO“"B±±®Ql¿~ÎQÿÅEùðçfÅ»$ënBÑŠ¿·ŒBõ.(ʃ)iÂ#%Ë„¯‰œ}oBÌÉ΄ l—îc{S3†ûƒÝþ¶éšy¹Ypϸ‘¢®F Cþ9öHX‰Õ ÿ8T ïSæ®Ïψo)…ô–YÚDïÏ{îc&¼þ…?lð—;öÂXv†Fõºå'ká…CG #þËØ Jxä7F—ÀœœLøåö}áÝïr_cš¯$‰q— ±“L–†Ø€òí0Œ#À0Œ#0H'‡ò­x™¢ÙÍ jFÂBd|Í; %K4ßÉ¡òÜ£K¸É9J•(²KEV¨\†^øéšðK:ݧÐ@2;z¿:³Ÿc¸ßéHDÈŽ`›DTççÂ|$GW.['¼cÓÑ«õ›™“ÄG*Gdîw3' “ ç{Yýs¾Ôó§Zq®y¡&#Y{böø›vÂvT6Qè´_¸ç73&‰ðB-Ö<çkÒBkyU¿Óúc-ZØðºÞZø8Ëñïm=‰ZžÉR¢Ž÷›`F€`AB€^˜é%Z.ÑîFzvˆ,å˜OUâüöØ‘Ý67=5dÇU"Ý^ä/°`XÔcûNw’RÑ##ïN$#Ò3óđݳa;l­o‚ÏÏŸ ¤Â7½@$:ñõ²‚(Ä„âß߸C¨ò}½L4·Jmwc"ÙÇU5ð8†ý0¼î÷³'äôT¸yd1lß²[]\ÌëúÝîƒ0Iã¯gN„"‹¦gfÀ‹_…Vn„÷Ï'HÓ_öêÕœ%Ùˆce-&ÏšÉRòŒ5ß)#À0Œ#À0ýF@¼4£®œ\÷»Â¤ùWG’ù^†afä9‘F" ’¬ÈcF¿Ø„;XLžêvM!máFau/`²îHÖ„átç|´ȃ3>=Èóu.iVT•#Ý Ræ#{­â8*ò)JO8B–†!1†9äÈ*P½ŽæV‘‘°‘¥¹HވĩíMÌwéEâJjwDÔŠŠ,赊â+>cÕxÓ¸'“EÉd‚ï•`F€`F@ åwçT$JÙþзj”·ßì:pŠtx8Y:ŠræÅVK€x„×Aó¡ô˜@×…ópÚ0ÔMm4¯ÉŽ¢t[uØ~yK|püd`^•º¬Ü¾ =>·àBÞ02’@W:‰ £¾I#Fòätž,ßO”hû›8Ç(ÜtHÈlºÐ×÷jT«“æÄûa‹.¡hG·î![›åÚËÀòÅýùðkBÓ·~ÞªP)Fý˜2Hûó¯D熭Ðúóßô  þ ƒÿëŠÜ¿qÌæu#ð—Ä~!îÓµf#´þþÿútÏâ"ü…b<{&Œí¨R̈ÖîŠ#Ðñ·àÅX]6F€`F€š\€¢Ò6Ô6ÊÍ^­+Ðs3;£ÓlBL¡s+I#2öÆÙsÀ‚s„(Tíÿö)O‰õï÷óŽBv±CyŒîò‡’°«F§Þ?onàªFœƒEÞò>úÃúè$y’$Q¢}·öø®ýHÔ‚ý–Ç[ÂÈy}ÂM¥ ~Š÷{‰@¨¯—sqÔåÇ8Uëm7u …b]×…"ŸMyè>Èü÷SbÑæ]º‘KòQüBCäN<ÿ¡>õñJ¹ï[rÿÝ`ºì0L†y³Áò•« íé?€áôY}ª–/bF€`þ!0!Xè!9,¸¶¤î[&:yÜî€uµõ}êð«DŽ׈HåZfQBÛ(„í>LRKD‰ì“°Ýâ`/ÿ›€áqd¤nGaub·†M#¤¿ ‡Šìº’"ÑòBÝZV,†[Ç1¬”§yG¤âGKbs†÷™—‚rq7;äI‹– Äs­¾E»ö,EQãü9 Ÿ2Ü;öD¡6®Â|ýå`<ïL„¯­ \øeE?vhQ F‹‰äR~ð-ôæ=Þº¾ýe„F€`F ÿˆ9KQˆx!ïp«CË7íŠà3 /y¿ ÃÛžG)n e#™í×Ñ“D YHÊd8yÂs,E®­ë£ëÅûÈHAø¾?ßWð£ï"T“&sõ>Dêw³&£`„Iô‡æ]QXɦËyH”'éï+á[cKq.S.䡪~ŠRÒ#ªó?(Þ[kFO%¥ýjÙp!þ@„®·­±îm»ñPž=KQÛ·Rúçž×†afºá… ˆÖøÑÂCÕã‹Ñ},ˆ’MŠ-ä2 ~ÝçÔ'4fsà¸F+Ë×Ê0g˜.<ô“ÇÆZ¯(‡Š.²nÊ…¬Ÿ0V—uE\cl°½MbÁ_T]™qÖ4å´Ë Íßû)´>ü[h¼õpnÜ&ŽSߌ 2ÕU=|Ž`F€` CvFy•¾³~¨CçúrÏ•WÂ϶í` #á"J”ä–HÇÓ+úRí)׈>c>&"=×`¢[R·{jÿáÀÜ$)Ò°¶¶@%»rk ¢DDí¿1çQ›Ç-ê”Ó^:|žÁ¾‘‡ifVÌ@¢äAõô xµòø)íwwàáeó ¢47W‘7ïî>D€=KA,ú´å­®"ºQ%`Z|6t|´¢ÛzˆŒ¤ÜwhU_0528>Zí}hÛ€„!õ?ðO¤J3žý#øZZ¡ù¡Ç ýíxkê ñk÷Šm"D™/= mMß¼<Ç«Ä9 i3ÌUÂ×hþÍ£"Ó¢‹×öC<7}’Øü‡_Šìÿ~ì/½˜L&r—þ×_‹mÇ{Ÿ€gç°ÜþUÐârË/&_‹dz$ƒ©=$ˆoûÓSã„„L7z¤¨Æƒs”<˜Y[~q}º Œ³"¥+®çÿF€`F áxçhÐÒS#ÕYrZ’å>óÃU«ú¬ªh)À0¶œ*@+ ““IpåEŸž¨Zúj¿Ûuþº÷K(²™Å<#šŸD mÕfÀ0¸ H˜ÖÖÔ‹°<:gÃÉ?÷‹;Pr^2º–ˆÞ0¡íHLJK3’¾D‚¥îóöNîù÷(!N‹ÚÞ@‚õ.bM ƒ.$]l½C€ÉRïð:¥´ãƒÏÀ|þÙ -ÈË×nçªuàÃÛÎÌ0m¤>ú“¡ ”C’`¾hh³³¡õWTœT*¢$ÊÑ>.žò 9hBoN:hs³YñÖ7 ¦önéÆ%ݸQ¢ ª³¸¶ïÛšô4HûßGa ôCn —ÌrÓÕ +.„ÖÇŸGk"+¦EgPàD؆6?Rþƒ@9û?þÓ9QÂkÉëÕþÜD->?Ñ Téÿe"ö1¡#À0Œ#À0=AàD»hH#UWa}¤²$A!‚Ø{êPÅŽØ_}'¼XȾÛ%Å;ZdØ(€m™¯¾8PÖ½¿<°o õüÆxÜF€`F`ˆ"@¡yD¨hÎÒofL‚gçÍ€‹‹† ! òLìâcû…$!n+nDdmFs¤ÓquÌþ·‚áW¤Ä1“ãµ%§ôO[”<¦7€íe®ÔªDtHTº3çÖ`#ôÀ0܎қɹ>ôʤYU @I×ð¼4׆-bSO}Ásd>Œƒu8$¶å®m;dIW2\¬}˜PMM”'ü$¡ t¼½4¼Hö‰è¥üè;Â;FPŽ¥Ö‡”ß*ž¬ÍhÝ‘Ïl<õûÂ0Œ#À0ñ…ÖmÆ©´°%ñM–Pæ¬Ý¤¼ˆÆ3¤nT.éxÿS0]ºŒgÍ k 7mNvà‰%ÐÉ"©Õ…—#¯…¢Ò¡ÇŠâh~‘ÏáçÚMè•jáoú‘ÃAz–(ÔÏ[¥L^Ôæõÿ=‡*NIvë9p8Ф6'+°-7<Ý©±„…QZQ¢ùþ_ÈË{´6->l÷Þ u„ië£os.ô¨Ñ>j7)„Þ„;6F€`F€`¡@ّܽ–<­-~²ToU’}Å;äíÿzŒ ÎrÞœn>ÕWšçã\ñyx±O£ž˜kóA–ˆ\ )m$(žýè!ÂI|®}åbêF)áy<¬Üƒ¡lÒ´ä"ñ¿ê×ùÂÑ6)î…‘²îŒ¿tò™îì^ù8#À0Œ#À0Œ@ü"Wd‰`¢è-åe“¢¯´—ª„7ÌS„âJ¥gŽ%˜ȯlÞWÏ¡Ã@bd†y³Á|ÃBö[7ºl?þ.¤=ö XŒœ~©r ªÞi0G€47"_[›Ø•!}î½ľ Ô—‹}’3w©”õ¾:–|,«ÛÝ·a¸ÞÑ’7ž¿ pŽTìzkÞZô\¡§JäcòK€S>&ËM×t[•å¦keˆ¨N›ÖÛo Y(Ü1^L>›9)˜ìŸYõ3/}ä~0Œ#À0Œ#Àô¸ Ó/™Z £E‡*xÃ2,p¢É»ŠFÀ¼ò} ÷dZ&Öþ· õ¿8e4H²»õOAÚã‰sVÌËD‹Ú(äÍþzQüFªy2a¬í{ÀŠjx 7Ü¡œ¥p; Å£9RÒ<è¡"sS8žÊ\[vàA·êˆ¤³ÆùsD®&Jª›ö‡_„œ§R±sï Ê—ŸR »ØæÿoïNä(ë¼?=ÓsONr“@8Â}ˆ÷âµ.² âëëî¾â*"º¬"„°;J‚÷É*wÙõ`ÝE@P\ÑåÊŠ(Ë ‰& BrL&sÏôûûu÷“T&ÓÉLæÈL÷÷Ñ?U]U]ǧ:=õïç©§šÿåߨËs]žWÿÕÛôœ¥» &”î¥/öâçU;‹]£'7Õ©ÞùÚÉ^rÞpŽgô¡]6ãàì&§M¨É~nãç7&ýù?lkxR™TcFOÅhá™ÃÎV†D`KcK®Ñw&µiH6ÀJ@"Q5K¾¸Ì]h¦²‰’“¥ƒ&ׇ u!¾Y÷-=>3wQ:ÒÝ;|4´ßŸï¢»Çκ[ïÆ‹¿°£›îälw¾õ“—f;gˆÓÛÔ ¹;(T¼­XÜ¥v÷æ\ï*®qJÖpuô²?î†ÜÛs×ç»%9-×þ44}í{»Íê?<œÜd‹zà«ýØ ®¢bÎaªvQ9|Á}õŒe3 /ÕÑg4fMÒ½Y:>nËÔ|ÔŸešáí‘oôÎ,K­öÎojl½ÇÀž—¼ÀÚ[³_¶©ò°¬ä1@ Œ¨«ÒÜ/ñ¹‹L_p¦Óå¡JÏë9tRuX±¾9üþð£Â1kV‡òîý{“Ë ¿ Ž=•¦üJÁÙK—g[6}ªž»t`ð}9]«žÛñ ¥äð4žq¶·»Ô˜ºÝj‡Ú~»D55K’oÙ1¾õ£ŸÙ1^hÄ÷Ymûü׳R”«É^ÙÄ J²Ö†®ç×èaN»ÖDy7½ãý…V•=†Bó?wEÁ÷%gø!·…Ö‘\n$Œw¥ÊÂ=sŽÊîÊìjÔ!Ieö3›K–r‰ÉÒH8Sƒ¿©Lw.Yêñ´õÁßkD`è6mmÊöLSVVN²4t̬F¹ÀˆJ–l›ß9QªP¢”V!l­© •ªU:DŸQV³ŸY}vcÂT‚,%qÈÝ銧CGGذy›Z×v¦ªò=w–ÄÁsE!ðìÚ¡³««B :š¦†KV…piQ ¶Àˆl†W®æKnÎT¡¨ªª 5Õ•aÞ”šìó8;]êjÜjWÿKœ÷áߣý·ÅW³dŠdíRewwèÒ/ö]]*Ãõ©™ü?D/W[Ù64© ß¶TèåÁ½”9ÊBwÓÚ&nßêÛZöŸ0[5nι¹®>4VÕ*ÙÞy¿Q…¾1&Uu‡)cTÓ©æxNŽÆè™WuuuÖ‡ºÚ\s¼JýòâQzÁ5§|Pv´6]wQsÇöWë™5ó®þÙᢿ}{¶)æ ¬œ• 0H+ž]~|ë½¾ŠõŸÎE‹¯˜¿dVÍjJ\À¨ jiQSYZtq=®¹#l©«,q•Ñøc[Úõ\ÓŒžwZ&Ž©Í~qŒþ£Ú·#‘É’Åÿø²µKºu‚”‹î‰RîyLj®×ìûC”©™^ckgØÚ®‡Ft–‡.¥K[«k³±o4¼«”Êuðué®0¾2ÆVûþ9wà &¡ùZÎúúúlÂT¯„ÉÓª4ßMð¨U*½OÍ·þnËy ®8=•IÝ÷ôš \õÓÿçuª~‰«*= ŽxD <¼|uø·[—éêîòOÃ×]sÅe—/^xùˆÜWvjtø÷D‡¯ÅÊÔ¨Ó1uluXµq{8 ©ditœÆ=îåĦöìüi㪳ç7žoK­Œèd)—e²=$OLüÇY®ûIâ½M­­mZ®=ŒÓ}M®jÓMi­Ý©Ð¦¿ ±J8“+cü¿|ÿc¨R–T¥‘êt¢ ûŠ\öNŠ\‹T[—kêDɵL¾WÉ‘8QògÓ‰>¥´táùÔÇ/¿òÝ]™®[õ þØ+ÿå—áãgŸfNPZíˆðŸ¼_Üý`øõ½{¿ôÕ”ºmFzî‡4¤Ôˆ:S£sgâµXîZ­Mç5žc_ßä®sFçqíë^ØdÉä“â.˜“Åסٓ¦DÉ¿äûyL¾W¤¥¥5´µµ«#ˆvuѪÕÑØ|“=7Û‹M÷â0¹NÆKWÀŸ1cø2î|ÿQ|ÖW¶£uâàΜ Ŧwžî瀥Ӯ᤻ðÒý$…põów^Õ¯ Ý·¼´µi5óºç¤ÞñºãÂ8ÝoIA`8Yñl¸E9¬Ý°%»Y}Å}õÍÇͽô½ï}oî&„áܶUt;ÿ^–eÿöùïßô‰jqQ½)lkí ³^jÏL®/ºã.•:ps³:LË„:5­œ1¡~Ç-¾>Šç¾T,|œ#:Yòú¤$&_Ûæ2\÷˜—ë^¼²¹2ÛDªµ­-ßk^G6aÊÝã”kº—K˜¼F » ø3ÿñÇ_OüÙr2ží¾^Íïü¥Ø…½‡N’Ü,/×û]yþ—–\âµëÚyUJ‹æ/?ïK_zEhê¸VMžÎ\òàòð‡ÇWf^uÌa©cçÌ ófOË&á¥d±ŸÀ‹›Ãã+Ÿ÷-}*¬^ûRné°IMD?¶x႟-¾]aKE.à¿™þ{™¼óŒs§Ô…W7†é[Zúñ5¡E÷»PF—@UGW˜¡§]æL«Óß,·âÊ…¯Ç}Î}þK©ŒødÉ'#™0Å‹ZŸ°ÜŬšHé—ý6Õ(¹)ž»ïìP7ãêbÜÉ’ÃM¨]*¥uߎ5þcŸ)ÿÛ÷ç*ûÙÒ—~:Ÿ,¹æÒ‰Quu.IråÎüùsÓ;/×Õ·-³T1 ,¾ä’­:¾³Î»láë2©Ì•íí¯ÿŸ‡V‡/&f©iÞ7嬭uÕÜ]ÌŸ…¡>¶Ný}ÛÖÜšš[ÆÍÛÂÆ-Û›LmSk»oT¥Ç}㪆 3E`Àþ›·óÇÅtî‡CýMœªZˆñšÃ––Î0oMcxì ñzˆ{i]Xw?® L=¿ùBc(Ó…ó8ݯíZ¥ ]cç~vo¿$Kûñôì}Óþ‡/Jã¸ïYŠÍðÜü®½¦#´;QR¸)^L–’=èÑ oïÖ¥´DLrr_ü¹Ï˜?g;j–ò “›ã9IªHçš~zîQ*¥OJÿuñ¢÷è]o8ïº2gfRá/õ#Î!ê¢ÿ+ãôA@ dÚÔ»ïů*jÊ®ûîüùùê¥>¼™Eè§@¶Éºj|æýâã¨iõáþÕ[ôŒÌÎ0wmcøó =ç…|©Ÿºûgñ¹ëC][gH+Á=jz}ö|ƈÝG@y Ö*ùLŒŠš¥ø‘‰IR¼°õ¯Ù‹Z]Àúh|h­\»³«ñî]º×ÝKqu ðS² ÉÏTL„üÅë@DC=ËËŸµ8ÏŸ=½ ,þÂewiÇ'uOÓ¼Œ®ÊB×äL&5YvNPµ7—{Cd~¯z&œš”}§6–eº×Õ«ºïkŸý¬×NVw˜á&,%Tü73^“ùï¤/¨c+Œ±õÕáÈ)5aéº=¾¥=[SñÄô1Ô0àχk”æ¬oÌödè?Hó&Wë^Ûš|«š\'VNŠ} ï[Á‡3°]륜Q•,Å£÷EjîâÖM œ0¥•ù— Õ&ù¹LjšÐ­a.r;$›áÅõ0DÀñK?cQ61Òg-— å¾ âgÏËR诀ïiÒ{(î­Ákji+–#êÓqøï¡ÿ>ºyq•šª×¨©zkM[¨ÑýãŒi‡¶u„§6u*aj Ç­îR ÓØÐª(#K ª³;¹fk¶FÉW6‡NH‡ÉãÔ¡•îÏöýÚ¾ Á­kü£±Ïw±^ÿ4·æºJ×ï—¹^q§iT&KÞÿxaëaYY&›4uw§³ ’£˜,å’¤ÜS«”8óŒîØY»”û\ù@LŠrã¹_ÐâgnÇA(yÕ)­5–mÍ%e‘»þJe°vÍ’;>òv®gâŽ0}¼šs…íaå–Îl“¼“žÝ^_ÖL¬ åü฿?,~àìÌMÍaÆ–æàš%Ÿ’Ã&”‡)J”ü¨”õì¡Ï«Ï¯[×s­Rãö\§jp´¾ç¹µÉR<䬓&î9ì’(•XyôaØ7Ž\<ˆŸ©ÓøRï›"K!€¥'PVžz¡[Ý,om*­dÉgºgíR­.°}KDW¾“-ÿp]^ÖV)aÚÞÂêemÚÖÖð¢`»¹¾2l­© z$&e˜ü„µ±-Ùæv“ô%wîR›Î„CƧøºêP__êýˆ…ϧ;·ò=inÅH¦ÝÖÍ4nÏ=Wª<“z±ç†G}²”< =]Ü:q¢ PH ~v Íg: €½ T–¥W´vµ‡µ·fš[ÛRµú%¾TŠÿvºéº{K«RbÔÙYè\+÷è[Ì-o ›¶w„uͩЦZŒé[Z²ážòZÔ4¯]Í»Úõ0xzÎüON¹.+ÕéY…šÛÕ¶w×(ÅR¥*¥)5™0I‰«›Q楺P§„©.[»¤¦xêÀ‰’Ïs±^+¹í5/nβèýdô‰ÃÝ’¥ŽÎ®lߣzFH\¦(†Åz‚‹âäpƒ. ¶óþ6L…Ljã ¯œ"€ì¸ªásÏŸ{ÙÂGT‹rÂã+ׄWsèŽyÅ>âk«X»äÚ‡n5ÃKÞ/îãß¹Lk[ÕžíV¼±=šºÔI’§úVU9e8Òêè¬6ÝÆVfÂøê´jŽr‰’k’êëT«4Æ5KJ–ô:Û¯j•þüÌÚì#‡tÓÅÓW/üÜŠžça·dIWMz*QhUöIAÑ)°n£Ú<„P‘J•­GÀ^#€£G@ ÁÍjÁrÂ#+V•T²ä3ädȵ™Œ:zP+žÜ-¹Ú‹‰’É¡ç1¥ÛT ÕÆ»©ž:ãjîÈ_nvª¶£SƒvÑ hÐ?ôîR#­¼´\µxUÖTèµÏ…z·«ªô3$«²÷&e›Þe›à)Yª¯ÍuðàŽмVÉà=ù\t¿5Ž$‡»%K™²ÐäL¿Mw¥ €ÀèX¯Tº”¥3$K£ó²× 0ŠÊÒáÆ®ŽÐðèÏgÖ¿Ô˜šzÀØQ´÷ßÕ˜0é7ºl²ä5zšþ¯DJÝз; hii ­­•ÁÏÆtÓ§j%L~Ô‹k£’IVr¸"©±çq›÷,¹ókwö`èîb§¾/ÉMðÜôÎ5Jî¨Ã½à¹ye17¿³•ï5üßeOg[ãdÊÊnêéç×»%K©î°ÁïðÓÀ) 0ú6ln Û›[+ôgª£+L"Y}§=FQ&ðý†Ë–ž·àŠ›tÑæÏïz0|ì=§Ž²#Øîú‚ÜÍñ\|ž¼@ßQ‹¡{_|Þª®Ås=æµë''Kñq/»ÖJ lx·âyØ‘´*qu¯v>GN^}Ÿ’»w¯wîÌ!›$éuUv~e6Q*æNlô«ß=ªä½S—Láw?¸bþOëYvK–jjSÿÓÜ2«×mLéFÅPJ7*öÄá5£Qàþ?­Êîv&•ùÍ>ZzÝ3Æ“Æ>#€À¨¨H—_ªû¾Ïxdųéå«Ö†y³§úcêÏ$&×0Å×;’%]€·êB܉’£CµKJ”:³=çí¹v©?ûÁ²;b²”»¯lg­RÚr8yÕùp›:qÒ´Øók”Š=Qr§÷<òd¼Çû’r»Ží^_§ù½üŠ¥ª=ú¼³N 'Í;x×wð F´ÀçßœY»aKJŽû›ï/Zp݈ÞYv("ó.[x•êG.¨¯«Î\zÎ_¦?¦ˆŽ®o‡›Ó¹yk:::²MîbmR»nóh÷4…k–œ,ÅN!bs¼¾m‰¥ö&ÖX³äÄÕ5Kn^ç¤ÈµK±–)&IÉ{”üþb-Ûõé+¯½5¼´e›*•R7.^´à=…Žu·š%/¨ûënÓàè{}‚d©ÓËžz>8QÒ÷[sYzÂ-#pÙ%@ hª¦ŽýLëúÆ“›¶·¾ê»×ß.þÀ;ô˽î¨/¡k3rêñ^5ÿ*W‡ª¹ðs˜œ å"×ÏI•ûvÈ&ZêdŒû”þ‰‰NYÊ]~ï|&–“¥\Ò”ëäÁç%NóCg]›ä(æâD~ñwe%5V|¢¶¢î#{:Þ^SÆ \qH{&µB׊Ïsz8äÀI{Zó@`t«7¡/üó-™uJ–ô}÷Åk^>컀””Àù _™ÖÙÑþ€®¡œ>y|øÄ{O “J°†É'=Ö2Åš#'EÙPó¥8î¿]^.Y«D²4ð21YŠÉk. rÏ…êpá„(ŽÇ$).;ð­Ü545·†ïßpWXùÜz'‘Êâ_¹¸aþò=íq¯É’ßpî‚…‹õé=×mn?ý×oÛÓ:˜‡#@àöû–…n¿ßÕÉkÓæ~¯áz @`¸λüKdžLç¯tÑ?«®¶:ó‘w½!uä!3†{7FÌöbÒ”KŠv&F¹$*ö„—Û]¥N#f¿‹aGÜsk–œÅZ£ãž·3Šáx÷t «×½®Q¢ôÒÖ&«lVïwï.Ô©Cr=“¥O\¶ðàöTf¹>³ÕuêËÂ_¼æØäûG$°B7ç§·gô0é”~-úà÷¯¸ìßFÐî±+ €@É |jÑ¢©ÍÍ™›U¿ò*üñsg…w¿éä0í€q%g‘<àXk”L âã•â¼äòŒLÀÉKL˜br”›V0 Ⱦ§Xþ³¥±9ܲä¡ðÇÇŸÒGLôRÓ»²Tåé×,¼øÉ¾ã•>vù¢sºº»ÿU°™ þÏi©£›Ù—u²  £Àº—¶†/ÿðW™––výSMýpñÂç ãæÙ €@O~ç;Um뿤ٟð­ zPxæ¸9¦N8âàpüœYꮹªÀ;Ks2ÉÒàŸw'G¥XÚuoÜÒ•kÂÃ+ž <±:£{åœ$e”Ò\W›®ÿä·þnK_]ö*øÑË~W¤çWV¦3çœþúÔIGÒ;^_qY¡p›ÛknZ’ÙÖÔìïÃ%3ÒG¼µ¡á½íC½]Ö Ðwó¾x¸ÂúEµÖÙÑã–šDe¦L“š8¶>LSêHœúÊ’ô"Т.é77n›·5‡õ›3];óœTêÎT:õÙÅ —=ÔË[÷8içJ ,vÞ5×T„ç^¼AU¤g8#;ã'¤þâµÇg«ó ¼…É 0 K\®ÿíýÙ¦wj|û`}ºî­ßl¸hÓ0lšM €ìƒÀù ¾|¤ú;S÷„Ÿ©š¦“öa¼ú( û’–«&éç!ºéš†ôñm»-¶×dÉïhhÈ”­íZôeõXò¿>pÊ„pæ›^h–g Ã+ðijëÂÏïz <³fcvêQúIÝĺó¾yÑE-û'l @`_.høòŒ¶®®9åÝÝ3»C˜¥ß£'îëºxX l«~ˆx>Užz¾¢»ûéZxù3ƒáÒ§d)nH[û@&•ù†ª‘³ÿ ›9%œ|ÔìpÜ܃Âãêãb @`\­üè“Ï…‡þ¼*8YrQ’Ô” e ~°ð²oòæX € €€ú•,YìÓ ßßܵýRÝ„w¡’¦ê¨8eâØ0ql][WÆÔUgûoó"€@ÿº3ÝaÛöÖШØÔØÖ¿Ô¸s©Ð®ÇÅ]]Y[¾èªùó7ìœÁ € €À` ô;YŠÏVwvž­.1ÏH…ÌtOS:Îcˆƒ+ Z¤N­ÑiìLùIDATñ÷ªbþe:“¹þ{‹<7¸[`m € €=ö9YJ®ÈµM­M't¥Ê¦©¶iªn¦š¢6ƒÉeG¾ èf§šØmHe2ëôïi}ª.ýðÕ—^º¹ïk`I@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ÈREv<ý>œ‹—e.Ì”…7ôû¼¡dRÝá꯺£d8p@@ DÒ%rœ{:ÌŠ U{Z€y$”\—'_3Ž € PœeÅyX € € 00’¥ùñn@@(R’¥"=±Ã(àãzl³Ðô‹ ÊËj­Åû@A@M€diÐ(YÃ.p„¶¸µ±nöð­³I±Jñ!E,½M¿[3½¿Nl«ÔiE¿TlR<–_é»5ôv.οf€ €ì“<ìoB`Dtj/ÖôØ“ ½>\Ñ¥x¢Ç¼–¯úÒß_Rx›ÿ©¸_áRhz½æÍ.1xÿy¿Vuºb¥Âûàâýñv3)óz) € Pb$K%vÂ9Ü¢xJGsT#š­×Ï(\³Òsž& j™§µ¹fç׊÷%Ö\húZÆ=O¶%–èè_æWð. — te¼@H Ð /©Á8¥)0Q‡ÝÛ'Nl<¯Pñ|—-¹ÁŽÿšþ‚–p"—Ù±äΑJ:¹›´sRŸÆb7îNûZüž¹Š=[_×År € PÄ$KE|r94z˜¦inº÷mÅqŠŸ+^T«pñw§·)6+6*\csâ$E,‹5rcþ…kw–+> (4݋ޠðr1™ò´© ßs´MáílP,U¼]±§â}÷ºNÉ/t·†÷çÇ ÐŒŸ)+/)žVœ¥ˆÅ ›}þ+NȽ=O^Ñ3ɺUÓV)ê@("’¥":™ }p­Ê …›Ê¹ùÜ;N:Z.W*¾¥p³Pq¹Â ŹŠ;Ó.+ÊŽ…°VÃ% '…¦{ÑÙŠ#)…ËŠG¾çènÅ÷*ŽV8‰;AQ¨lÓ osS~û4ü}~¼·Á4M|Xñ…¡Ï(þIQ©pw©ÂåÏ ½E‘L~NÕk»yŸß ˆeŒFÞ¦X¥Ø®  € €@ ,ÑÉäPè‡À[µ¬•ÉŠcN\>¨X¯x¥âJÅ"Å;N^Æ+NQ¸\¥ð<—{U8™*4]³v+NŽœÄ|Uá„ã¯U|RQ­¸@Q¨¸9Ÿ·ùX~ÏiøéüxoƒÏkâ,…—;[ñu…·ãí½ ð¶ç(2ŠÛiÅk±œ¢‘¦ü ÇòFxÙž5Qq>C@Å$K£øä±ë PÀ]k»IZ,®eqô!E{œ˜ºÇeRn0àÿº)Û9ŠÅ|E²\«w)ª’0>Aïý°¢Kquõ<«×?Rx[ïÉÏ‹‰!—”µI7+V+NQÄrZ~$¾'Ngˆ €E à_D) Pzn.ç{„’Å Òwòfkø2Å!Š¹Š¿U fq-N™âO wž,Ízñ¦ä„Ž{ÿÝ´îŶ^Ö›‘Ÿ÷[ XÅævÇjÜÉÝ }0ÿÚM½Ÿ«K@(2_¬P@ ôÖ8ä·hºkIžRø^ž¯(\ÃòGÅ`–Có+sÒ6Ôå°üÜ\°·âdÇeJníÝï^¿Báæ€±†Éµ]w(bM“›0:‘²@ŠP€š¥"<©û(ð*½ï׊í wxðß ÷׬ø¨âÅ`•xÿOLPk½½­'65<¨·™švb~zLšüÒ Ðë¯T8YZ•V ]NQT)œ8‘, ‚ €@1 ”ãAqL °OîøÀÍÕ>«p’{s¢äâ¦lƒYžÈ¯Ì ‰·™,nò¶Lá{„£<™_É©öö÷êüü¸œ_:itq¢äp­’Ë:…›Ü¢8MѦpm@ŠP · ‡"¬˜¤ˆÉ’F³ÉÑq¾Sq·"&”¥ € €@1 ,ÓÙäX˜Àoòo¿DCwÛýVEƒâ±ùÙk4îç ´d´‚Ï(º¾7ʵYg+®W\ªØ ø¶b0J·Vâm¹¸¶ÊÉØë«ø­ÂÉà7î2=Y\»›îõL–Üünš‚&xI1Æ@(2îY*²Êá 0?KÉÍïœX\•_O“†~Ò7w+Þ§pïuN4Zœ€¼MqÂÛŽ֯pÍÎ`•Û´¢3ÿªøVb¥>–‹>¾žÅ‰ÐG+Ï'f.ѸßçïO’¥ £ €›€-érñ²ÌßgRÙûJÚƒï‡@*\õÕ#S®‘(Ö2[v¼â9…›£¹ö'–4âÄacœ0C¡˜¥x:®yŠâÞí܃ÝኧK4£@vð/£@ )°J/½•Gz›8ÀiNŒ–çc€«ÚëÛ[µÄýùØëÂ,€ €¥-@²B‡:ÿuVú$êÞ¥¦¥Oïa!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@F¬@jÄîÙ0ìØ¹—_ùÆén†M±‰Q*N—øê†KŸá»£öïÍöq£¦ÿIñuÅÝŠXæiÄÓýÞ³ãÄa^«í¥8Eñ°b¨Êt­x¹âÅ™Cµ‘Zïp·"åP@Fª@z¤îØpìWª»{J&dNŽm±Ñ*Ð]? ö¼Nû8V±R±%±¿µ?\q¨âtÅŠï*bñ%ûóïŸ÷»<îÐ ËòÛ±¥ïoÑ¢×+®VÌO¼m¸Î[b“Œ"€ °|AAâ¸H‡ñòD­q'*2Н*¦)FJñ~9‘{l¤ìû±‹@…^WÔì25÷yâ¼õ@á% €@q ,çy娈¹Jñ€Â½'+ ×L(4s¦Wë=®Ùò°·ò¢&>£hïm¦¦¹ùÜ\EeùÉÉSõb°¿Ï¼Ý£“’*0n7ïkjɼ޾î³÷ʼnK_Š—ë‹™×Õã¸í½·¡¶ˆûÁ@!èëê!ß6€C*ðT~í½Õ,¦yw+6+6)žT¼SË75²Fñá8!1t"äæ'[.¾ê6E³ÂëÚ®x\ñZE²|C/|/Ñ1ɉÿ€ÂÛ{A±B±Mñ=E½"YŽÓ O÷6Ö)¼ÿ¿V|F1ï6'^¿Tx»ËKoWô,gk‚ïi³›÷µIñ…¡dñ~ú˜<ý Åó ¯w«â&EòØœ`zÙE ÛÜ£ð¾øø|¬ïQô,>ÞPx_¼œ›cÞ¥ø EoeoÆ?×›~”ã¹zÞœ]è¼ …E~“ @Ø?¹ Ø?{ÌV@ ¿®í8!ÿ&_l'‹§ßª8Bqâ?‡*nPœ¨pùb†âoü¢Gqq˜â!…“#×*üVñz…/¶?¥ðÅõLÅÝŠ“±xÞnuœ áÿ–íãûŠ&ÅÇ?PÄ2K#w(>¢¸]áíü»bžÂÍ ¯TìK9PozDqºân…÷ç^ÅÑ 'ÑQ£á2ÅõQùŠâÅŠ¿V¸iáDE,÷ñþLñYÅ+®R8az·âÇŠXÒñ²oSxÛ®ü'…·oëŸ*NVÄâóësôyÅF…Ýçñ(…“¾óÉÒco÷þü›œ€ù3òbþuoçm¨,ò›d€ €Àþðe ‡@…£2q(ã4þF…/ÞD¸ÇÁÉâ‹o_<_¡èÌÏp É?*NU<¬ð…ýzÅSñ¢Y£;zÒû‰_¨¸FʉŒ/Ø}‹kO¼í÷+œXõV¼/N8|ÁÿjÅj…˧O)Þ§¸XñœÂÛ™¤ð¼o+b9^#Þg' ^W‹-¦)¾ªð¶\lqÂɇNÐf+.W<£ð¾ÚÇåËŠ… û×R$Ë\½pÂø§üÄE>¡x³Âmd±¼L#_W|NÑ•Ÿèõ{¿NQ< p9Oá÷ÿK~¼[C—ÙŠ{ßTü§â%E_}ü®U{»âÅß) •Ùš1Ô…¶Ít@†T lH×ÎÊ@`8nÔÆÚá¤ægŠw)Ö)>¨èP$‹—I&Jžç l'.¾PwM…¿/œ„ÄR¥'-›nþæ297¯ÉãàyâGqB/C'"Nø¾¥X˜ïcú‚â÷ŠÃóÓ—jø)ÅOò¯ãàq8)p"ÕßâÚŸs-ŠùŠd¹V/îRø˜].TxüEL”4š-Nmö6KÏ‹‰’§û½(ê³Éò‚^x?¼®XnË'hèDÅûìÄ1&J «?TT*œhºôÇ8÷޽ÿw8,ö¾, €À¤‡`¬öÀµÙµ=6½A¯}q~bcy~éZX£g?Ÿq K,NJœœø¾”kò]ë0Fñ}ELÂÜ\Ë51§(¼n'kN2~§p²³§2/?óÑ^Z¬iŽXþG#'W§*\c2[á}r¢ä®¿eŽÞàäÆ^=Mš5íMŠXŽÌ,‰C/»Bq”â Å*E,Å‘Äpu~ÜÞÏ$¦/×x{âµG“Ëúµuºb•â-Šž%“Ÿpb~Øãžë*ôz8, m›é € ©ÉÒò²r†UÀµ¾G¥?%Ù¤nOï{@3œ¢ðº¯÷(\œHÅâeÞ¨ð}9®…šŸ'7(ܤl¢·rh~¢k‡öV\ ääí\…“—mŠ»sûRú³}'g.¿Ï vû¯“'KS«±l‰#}öeÙÃóë™­áM{X§“Z—þcî{ÿïpXì}/X@!(‚u²J=±æ¡/{|*W¼KQ¥8Cáš{Éò°^¼_1UñWŠï)ÿOq‹¢PiÊÏp‚±·òc-ð oë,ÅLÅ8…÷i»b_J¶ïãq™•ìößX“ã¤)YúãÝ—ecBå$Õ‰S¡¸0¿ý9Æä~ïi|8,ö´}æ!€ 0d$KCFËŠ(:'K.®Qz«b¬ÂÓ’õž÷qEJá‹è_(>¡pó/×b½BQ(zBó\ܬ®gq Õ“ŠÓÞî;Ï+œݤX£ð~ŒWZ¿fí±Äí¿^K9)L–‰z±Lqs~¢÷ÅÅûÓ³¢ N]ÓU¨­ç{öõõSzc—µiÍ ¿NF¥^¿]kßâ1îÍXoés)}ÞaD@ ¯é¾.XŒËeÊÊ^ ™î»‹ñØ8¦Á(‹¿ÄÖ GózžÖλÉ B¼§çG=ȵN­xTáeciÕˆk||a_¨æç?4ï}ŠÓ_St+\œ¹ù^­âüPƒìýJþó:c¹@#ûú#‰‡')œLÜ®ˆåSq³º_å'8iò¾:Yû¶¢g¨I»tuî×CQ:´Ò[®Á³ýÕŠXìðÏŠ×(NÎO쫱ÇT‘o¡ÁH±(´LG@(aÛtì¾°}g? æåßsC/ï‰ó~ÓË<×y[Ž{z™ÿ¦ü¼U:i:NáýòEºßs½"–8-^È{ú /ç)'#—*\sáiW*bq2æi^ÇÛTܨتxAá$ÂÛu‚u ÂËöv<š¼Kq’äDp‹â³Š³ÞçvÅ‹ ¯+'ƒ^¯“'*>ö… 'oÓ±Äc=&NH ¬q¯çÕùióò¯{;7‡åç%åMs­R«bÂûñ^Å…×û¯Šdé«qtóqÿ½ÂÛq‰Ç’Ø A1ØvƒjpÔzÐN‚6p\WÀ p €G@ †ÁK0Þi‚ð¢Aª¤™BÖZyCAP8ÅC‰’@ùÐ&¨*ƒª¡CP=ô#tº]ƒú Ð 4ý}„˜Óa ض€Ù°;GÂËàDxœÀÛáJ¸>·Âáð,…_“@ÈÑFXñDBX$!k‘"¤©Eš¤¹H‘q䇡a˜Æã‡YŒábVaÖbJ0Õ˜c˜VLæ6f3ù‚¥bÕ±¦X'¬?v 6›-ÄV``[°—±Øaì;ÇÀâp~¸\2n5®·׌»€ëà á&ñx¼*Þï‚Ásðb|!¾ ߯¿' Zk‚!– $l$Tçý„Â4Q¨Ot"†yÄ\b)±ŽØA¼I&N“I†$R$)™´TIj"]&=&½!“É:dGrY@^O®$Ÿ _%’?P”(&OJEBÙN9J¹@y@yC¥R ¨nÔXª˜ºZO½D}J}/G“3—ó—ãÉ­“«‘k•ë—{%O”×—w—_.Ÿ'_!Jþ¦ü¸QÁ@ÁS£°V¡Fá´Â=…IEš¢•bˆbšb‰bƒâ5ÅQ%¼’’·O©@é°Ò%¥!BÓ¥yÒ¸´M´:ÚeÚ0G7¤ûÓ“éÅôè½ô e%e[å(ååå³ÊRÂ0`ø3R¥Œ“Œ»Œó4æ¹ÏãÏÛ6¯i^ÿ¼)•ù*n*|•"•f••ªLUoÕÕªmªOÔ0j&jajÙjûÕ.«Ï§ÏwžÏ_4ÿäü‡ê°º‰z¸újõÃê=ꓚ¾U—4Æ5šnšÉšåšç4Ç´hZ µZåZçµ^0•™îÌTf%³‹9¡­®í§-Ñ>¤Ý«=­c¨³Xg£N³Î]’.[7A·\·SwBOK/X/_¯Qï¡>QŸ­Ÿ¤¿G¿[ÊÀÐ Ú`‹A›Á¨¡Š¡¿aža£ác#ª‘«Ñ*£Z£;Æ8c¶qŠñ>ã[&°‰I’IÉMSØÔÞT`ºÏ´Ï kæh&4«5»Ç¢°ÜYY¬FÖ 9Ã<È|£y›ù+ =‹X‹Ý_,í,S-ë,Y)YXm´ê°úÃÚÄšk]c}džjãc³Î¦Ýæµ­©-ßv¿í};š]°Ý»N»Ïöö"û&û1=‡x‡½÷Øtv(»„}Õëèá¸ÎñŒã'{'±ÓI§ßYÎ)ΠΣ ðÔ-rÑqá¸r‘.d.Œ_xp¡ÔUÛ•ãZëúÌM×çvÄmÄÝØ=Ùý¸û+K‘G‹Ç”§“çÏ ^ˆ—¯W‘W¯·’÷bïjï§>:>‰>>¾v¾«}/øaýývúÝó×ðçú×ûO8¬ è ¤FV> 2 uÃÁÁ»‚/Ò_$\ÔBüCv…< 5 ]ús.,4¬&ìy¸Ux~xw-bEDCÄ»HÈÒÈG‹KwFÉGÅEÕGME{E—EK—X,Y³äFŒZŒ ¦={$vr©÷ÒÝK‡ãìâ ãî.3\–³ìÚrµå©ËÏ®_ÁYq*ßÿ‰©åL®ô_¹wåד»‡û’çÆ+çñ]øeü‘—„²„ÑD—Ä]‰cI®IIãOAµàu²_òä©””£)3©Ñ©Íi„´ø´ÓB%aа+]3='½/Ã4£0CºÊiÕîU¢@Ñ‘L(sYf»˜ŽþLõHŒ$›%ƒY ³j²ÞgGeŸÊQÌæôäšänËÉóÉû~5f5wug¾vþ†üÁ5îk­…Ö®\Û¹Nw]Áºáõ¾ëm mHÙðËFËeßnŠÞÔQ Q°¾`h³ïæÆB¹BQá½-Î[lÅllíÝf³­jÛ—"^ÑõbËâŠâO%Ü’ëßY}WùÝÌö„í½¥ö¥ûwàvwÜÝéºóX™bY^ÙЮà]­åÌò¢ò·»Wì¾Va[q`id´2¨²½J¯jGÕ§ê¤êšæ½ê{·íÚÇÛ׿ßmÓÅ>¼È÷Pk­AmÅaÜá¬ÃÏë¢êº¿g_DíHñ‘ÏG…G¥ÇÂuÕ;Ô×7¨7”6’ƱãqÇoýàõC{«éP3£¹ø8!9ñâÇøïž <ÙyŠ}ªé'ýŸö¶ÐZŠZ¡ÖÜÖ‰¶¤6i{L{ßé€ÓÎ-?›ÿ|ôŒö™š³ÊgKϑΜ›9Ÿw~òBÆ…ñ‹‰‡:Wt>º´äÒ®°®ÞË—¯^ñ¹r©Û½ûüU—«g®9];}}½í†ýÖ»ž–_ì~iéµïm½ép³ý–ã­Ž¾}çú]û/Þöº}åŽÿ‹úî.¾{ÿ^Ü=é}ÞýÑ©^?Ìz8ýhýcìã¢' O*žª?­ýÕø×f©½ôì ×`ϳˆg†¸C/ÿ•ù¯OÃÏ©Ï+F´FêG­GÏŒùŒÝz±ôÅðËŒ—Óã…¿)þ¶÷•Ñ«Ÿ~wû½gbÉÄðkÑë™?JÞ¨¾9úÖömçdèäÓwi獵ŠÞ«¾?öý¡ûcôÇ‘éìOøO•Ÿ?w| üòx&mfæß÷„óû2:Y~ pHYsgŸÒRÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì`Õµ÷ÏJZõ.K–%Ù–-ɽc6¶©¦×I †4^B òü„$¼—/=„¼itˆÁ4ƒmlÜq—›lË*VïuÛwÎ]ÍhvµªÞ]íJÿëiwnùÍjvΜr‰                          à=&oTõÍ5¿Šo³7\DGŽÉaŠŠŠ4'GD„%qÝ!Þ¨u€Àp$`³SKsS[¥Åfk 59êì&ó§xâáƒ&“É1y`Ì    àoV–¾þã§Xšß1™®³;ì3#_=´7섆„ÔØö <ð?üñÉÇ×;0€€€ø‘@¿•¥û×<“f·¶~—Èô5Vb´¾¦§$Иô 7‡‘Y>¡Ð46X‚À@زXmÔn±R}S+8[Fmí½*“‰¶SHÈž{âÑ·õXð~)K«ûÑe¬$ý“•¤déAvF*]:?—r³3(>6ÊkBE ] Øí:SZE»œ¢M{Ž*%J•2™þžqß?ôPS׳°@@@@` ú¤,­Yã)¶<õ‡Jü'¿ì3*…®_:‡¦ŒÏh»8@à<46·ÑŸ õÛ8lv›‰Lt$$,ôæ?¬ùÁá󨧂€€€„Ö»]´Øü "Ç#\À´t^­¾q)d·;€Ààw×¼qüÂ"Ãt¸ „ZÛ,©lñýܼKWþ{÷G*§Wh@@@†^-K÷>öÔ·Èaÿ…dàúâ5KLLÏZ0rbeúýËèdQ9¿Î01E™=÷裥A>,t@@@@YîÿáS—›ÈþséåÍ+æCQôË…€@W±Ñôõ[—Sú¶ö:cÍŽukÖ¼Þµ$ö€€€€ô‡@·Ê’Œ@@@üMÀ£²ô½Ÿþ4Înr<.¹~Ùž;©Oy üÝw´ àF`Þ”q”;.ƒs=P´Ãjý‰Ûal‚€€€ôƒ€Ge©®Þ²Š”(Í"¡C?h¢([8¾ÐÙ ÇM®ù¹š-º….€€€€@Ðð¨,qÐÃM2yK .™iI46=ÅÂ/<›lÍwWïÑ[]”¥ÕkþM&Ç•ÒÅÙ¹c§§è €@Ÿ ,š9Á¬ ;_èóI(    àB ‹²DT™+ñ ±Ñ$o¨! ÁG`nžnž«^€ßÐct]”%“Ý1Zz5"1vÐ;‡€ Œ@\L$EG„7IêU ïÿÀ0â,aN ‹²Dv‡J7œ3¨hjë›épA1•U“ÍfÔ¾ qF©IñÒo“Í4-û>ƒ€€€À`sï€ÃdÊ ~å~ÈçÛ M-ôÒ;éÐÉbjnmÓÛ ¡Q©‰”“•¦R™GE„ëǰ8ʪêé?Ÿ}MïPDxý×}7Rb|´¾O[ik·Ò·žù›¶I7­˜O+NÕ·}¹òÌóëèLi•j"w\:}ã¶•¾l®KÝüçEϾò!¿—°“94”Vß´¬Koìà¿ë™s•üçlŸäúP€€€ 7]”%~²R“*…˜º| g×áSôÏw?¥¦–N%IkÏf·+ “X™òO—Ð×o]AiÉñÚa,=ØÉ<_Y¿S?òÄý7’(/¾§_3Š(D¯oÜE÷\w±q·Çu™È_beK¥ÕfSÍÙlþkWßÞü3´ïX¡Úôåffs¨ÿãÛ ¯ Kbü«uïdQ9ýïëwQ”äá>,ÌuB\±^<ý—·HÎtO ½ÝBu Íú‡Ü™îÏôî‘í ètI…w+ âÚ>;ZÈÖÓA<t@@@†Aãl·;èïls!>iL:ÝzÙ‚Žl|&*­¬¡ßÛNGÏœSåZZÛéïë¶Ñã÷^G¦Žé7]*ÀF@xéýô½»¯¨>ù³3ïzΔTRIe-•VÔú³i´    p]YÚ°ó0—×èCÈ—A_¿m9…q,‡&©Iôí;¯ 5xIJ$RRQCOÑô YZ1µo.qÕ+â:KøÓn±*¥KÒ çfgxtE«®kd…ÌùÊíæfRu‰Eä ÇOÅÇDÑ”ñœ!0No«¢¦ž­[ªTv œ>!“ã¼bôã²R^Ý ×+V2©·Ýb£]‡ è\eÅDGPö¨4il:+}]µ¾Ç‹8®Åé&&Ù ÝS¹—W×sýuz›3&Ž&‹ÕªÚ­áFf³r_twý*äøÓ¥•ê:DE˜iì¨T—1Âc¬‘±Î¾®WÐÎC4êø¾ž¢—«®o¢ã…eŠsYu%1ã ¾–ã3S»ðÐOêX9{®šNñ5,«ªås’)ù''ô-Ë£(äGÏ”²ûg Õ²….sd_«TžäòÝtoÓÓö>¶&Áê‰ ö€€€@`teiÛ¾.„®½d¶Ç‡QÑ%®¸p=¿v‹^¾€]ñŒÊR}c ýéÍMtôt©^FVv9­¶Ó’ãhõË(kd²ÚÖþÙ{ô,½üÁvµÉÊÂ#÷¬¢ÿ÷÷÷” ›VF–+8ÁÍœˆ`ýöÃäêJA_¾þbVª2õSv矦77îVÛœ™Œî¿yýêŸP]£«"#‰+¾Ä禸=È?ûê‡z&À‹çL¦;®\¤×-+—´öã½ú¾_?|'.¨¤ÿyá]}Ÿ¶"I D¹çjÊÎHUë­mzñýí´m¿ë5PùŸ.G—/x"5QrE©yýÃÝ4kò2‡õý+'ýúç»Û”‚©*qûGúvÝÒ¹Âɱ"ÙÿõÞ§´yï1ãn ¡{o¸ÄeŸ§C¬„ÿßÛ[»\)›ž’@|neŸ•.9gÔˆD’¸;M*k©±¹UÛÄ@@@@ @ ôýÉÕ£I9[h4Éâ·÷ãØbÐ,š1äãI$ÕøS^K’Q¯;KÏOþò6}óö4¹Ãzä^VþûâÊë·RqUž” ILñì+é'ÜBÑ‘îÕRK[;ýú_ë»(JRP¬?á8¬'¾z#ù+ÓŸ<¼ÿŒ(M™éÒaÞñú‡»”Uå6v‰ˆ,_8…ÞÞô‰uH>ïzˆ®^2³OUýû£=´î“ý=–}oÛA:Ë–Q^ŒòûW6ÐÁÅÆ]j]”¨ç^ûˆÂÍÝíÅö§76u9WÛq®ªŽ~Êܸ}e¯–-휻®¾P[UË¿­ÛJ[Ü9—؃šà¡º¾‘¬VgV2¡‘ž’8`(o~¼ÇEQš9i´JÉ,i¡/™›«×+YÐ$À¾»äkâÆVÓÐD—ÎÏSé¬'rü”QDQ’ ?W]4‹­L \²ò‰ËßÖ}'Åõu±$Ôr½âJwù¢é$–¢ØèHýxCS+­Û¼OßèJ&[s¾~Û Õc÷Þ¸T활 voÜ™ï¢(M=’î¾ö"5æôÎ2Rp#»I9Ub¬ªÏ뢔ܸ|ž^þ½­<*¡zŽQjEÒ$*2\Yô¼órÅC¬tšÈ\\šåPöI\›QQ÷Ga}+i#SâIbäÄ¢æI${ß+vé‡Dé]¾` [/¥Órt‹§$Îp³ÓO €€€€À!Ðý+v? ±ŒßÒ%y€áJüϧ:]ÉæäUîvZÝÓ8žHœ·ØÊ!"1R;Ø‚°pšçšÛ.[HÍvNM³l^.ýà7¯¸øirÏ‹#Ê…ÈÔœ ú¯?¼¡¢Jƒ¥LßÙ±"±IòÀ¯Å'‰[ßÙ¦=¼¸ë]é\’y¥*Ñì(®‰õn®~Ór29^ˬª…áíÍN²C¬yßý•z“‹gN ïýò%]‘ý”D‰%ˆÌ›2Ž>Ú•O'Ζ©ø±×Ù-Q”²žd-+¾šÛš¸_~ç®+Ùu2I2™FÑœÉcéñß¿¦gO|cã𛗭ޝÛâªp~ëŽËU|“Ï¿þ{3W<+´ïÉwQæ¾rC§[¥¸&ÅE³w@µ#Ö@‰[3*nêþ2þTîmíV%©®'÷¨žš;ÎâFKÑ‹ft)¾bÁT—4äÇ:2ëu)È;r²:]%ÑÄ(ŽSÑDbš4EIöI<Š1aBs7V ){ëʺ¢$Û2W”–LB¶ÅM¬ª¶IV}*Uu Ê-PkD,/F‹Ê¼åCöï=zF%Ž0–éϺŒ[QTNsf¸žä'tÐd+~š¢¤í…ðâeVö‰Ò"n˜"ƺ%¾HAE²,ºÇ8iÇ%™ƒ&’ÌÃ&û—Ìž¨VKI‹ºÕ²”šÔ™]NK\Ë@D²Å=kœ%'%!FϦw®#ûñ¡ÞþĸËe’ÁO2 Ԋ2fT -ž9‘>Ùw\µñ2»AºÇiKl—1Fg±ó$nI:Jù;À“°RÏ/¥‰XòÜEAQ„$“ »÷UÖ6Ðמ~Þ½ˆË¶¸ B@@@@†.Aµ,¹+Uü€Ú“ˆkV§ùÖ>ÍœTA¤®¡ÓENbT$ë™'‰1$^¨ç!_ˆk^¶Î؅˘];2Ò`µ’}Ƈ}­Œ·—u;eQðÄ5Ïø1Z꤬Å[f<·¯ë×-£»Š Û.ÎäçIê]¯‹X‘Ú«Uí²LIŒaWE±Ð9w׳E˓Թ]®§»E°çYrQ eþ%O’ÂcÔÜù$±Ç Îæ×“$ð˜    C—@WÂÏc7e,mØqDµ*–£·8#ÜX ryÈ•ù4‘¸“Y“ƨMwë̱Âs.ó/I!±ZÕpö=M$žÅ'â® u4"YÿJ+kHæ2J¾ÛœPF%.2"Œçãqº÷‰é.%=¸º—5n»óЉ¦™,å¼½¾|A‰Â+.nÝ¥xë[JBœ*#íã—Œý‘‰j"×S溒ø1qñ—&䊫Ÿ'.Úä±Í­m~aâ©Ø    <û«ù±oW/™å2/ÑVŽk‘ŒeÆZI«ýžûH²5™1q´z8–í©ã3\,Iÿæljò°«‰(a¯rJhÍZ!ûgåŽÕ{ui¢“ˆ‡Zÿ¾n›>ɬ–÷S%–%Iø`Œ2®Ÿ.­r™ÈôàÉbrW<4©ïj5ÄòHÖA£{Û{Ûöë™ïäá%Û>ô‹ÕGæ¦ò†ˆ"tóŠÎTâÝÕ9Çpmd’]÷y­NWÒöƒ'õÓÅ $Š’ÈøÌ4}¿|_$µ¸&¢pKf¾îÄ8Y±dLÜsäŒKÑ=ùgt&ÂÆ˜²Ü¥ 6@@@@†A·,ÉC»ÌóÊú:Pɘ&Ã2ï’•3ÄI¶3£HüÏWvNôE+/˜¦§7«'ÿw­² „‡…Ò¡‚"2f:“ÔÚ’XÁß"V‹ïþâ_4='‹ÚxN¦Ã%.Ê“¤¶6ÊDNO.iÑEdž¦§þ´Võ[aH]òðß„w¤ ׎¿²~—Ê*wÁôåú(êD)‘ rOüñMe©ÃØÎç•;¤v®Ì5ä-™Åi¿eBà£n5cýW,žN[XiÖbÒž_»E%ÁÍIä»°›•ɨÉM†¹œ®_6GM<¬ûíKØÊ8Z¥ý>~öœË÷@+£-%ÃÞ&N®1ÿó››ø{˜I9£Óè8gOT׋Iq+œ2ÀtêZ{X‚€€€6A·, y¿n™ëüB¢”²›™»¢$ÙÌV߸TÍ›dD{+KÆìgâ¶&ªÊ¼8FEI2ºIúh_‰6‡’{ý2w’X.ZZÛÕOûŽº¤ã–4Õ2'‘Q–ó÷“L’ûåë/!YB@@@@†.€P–Äšqå…Óé{w_EžÒ~kø/äôÓO|õ»’,âÁ;¯ UÏêvR×SÇÓc_¹fÀ)°µ~ diâ«oÞ¾BÅRÉC¹&{u]޹‹Äá<ð¹ËØÂæ_•M_½e9-š‘ã~о-JÖlq‘”éÝÉê—©ø0±Ì¹‹ð¼ãÊEtßM—º(]îå²-×X›ô·»óE‘ûþ—®éöû }þÆí+Õõv¯ã¶ËÒ-+vùHÊðÿ¸ërމê>Y…(^ÿuÿõdt4Ö/ ùï½nP,“Æ~`@@@@À÷XMq•ÕýèglÕyH,5ò°="֗⊶T«u™øUÒ@cxzê—XÎUÖSIE5µsb…LNª çG¸¹¦õT‡7޽³õ½¹q·ªJ¤ß<üyµ. Ä:®\ã4KFOm “¢òj’d #’b»(Ý+)¯Å…¯‰c¸BC$yBL—Ôêb}9WU§˜ËB”Iv Ö°@É–XRQKelýJŠ‹áþ%ª¹’zë›p.I$52I}r¾¯Ï÷²i2ûÕŒ0‘Dý±\I||º™—J¤ž¬zÝëý’^^>yýŒÎýɸzãæ±£ Á#¦ƒÁ?Z ,yÄ‚     Ô¥áþ ÀøA@@@@<Ș%= ‹¦åÐDž£G¤»”âA8,t@@@@†(K>¼Ì’â[>à#7¼à»fè1€€€€€€@Yòd4     | ,ß5CA@@@@ü@Ê’ £ à#e)ø®z      àP–üM€€€€€(KÁwÍÐc?€²äÈh@@@@@ ø@Y ¾k†ƒ€€€€ø”%?@F     ÁGÊRð]3ô@@@@@À ,ù2š>P–‚ïš¡Ç     ~ eÉÑ€€€€€@ð€²|× =ð(K~€Œ&@@@@@‚”¥à»fè1€€€€€€„ù¡ 4  À¾½æÿ%¶Ø›.rØcˆB¢#Â͉æ°"^\á2 míÖZ‹ÅVo29šL¦²Pó¦ß­ùÞ¹U†“†<þó¨æÚ–%v‡c"Ù):,,$.2œl" òƒÇ}BÀn··µ´Xª&S™¨ÁD¡;Ÿ}âáý&¾)ù¤ÁA¨ÊÒ @G“ ÇÀýO>™io£o„™B®n¶6Nu8¨C1²Sk[† ŒÔwø{Å:·lüe»ï‡Oæ³BþN8ѯóäã§|×*j_[óÛX«µö>“‰®kªn¾Àáp˜µ~[­6jäΛ€Ü„Ôÿvºÿ‡OÕÞûØÖ›BBþúܾ}ÞurP–ù y¡Iàþ5OgÛ­öGì­Ž/ÉÉ…œ$£F$RfZE˜Ã(œ?æ0¼ÐšßÿŒÊf·Q›ÅFííV*¯©§3¥Ud·9r¹õÜv2}cõ£O>b}êÙ5ß?ퟡ•@!ð½Ÿþ4®¶ÞòM«µæ;|JV 5w.%1޲G¥[¶Õ}÷ @¹bÁÙþnQ;+Ümíjln¥EåüÐ’È£¹Ùa³ßÌJÓîÓþðÄco牠,ë•C¿A–À½?õ%»Õö;þ‰NNÎEÏ™L“ƤS\LdÀö ~-­ít¬ðmÞ{”ž( ÂWlVë]¬4ÝÿÜS=ü#ÄúBàÞÇŸž_ÛÐþ ¿è#oûÓG$ÐòSiÊø JIˆíK(" ÊÓ™ÒJÚ›†>Ú}T”¨¹üçÕýè Š5ßýÜ#Ô ¨âA< ÊÒ ÂGÓ C‹Àš5‰,±ÿÖa·‰ŸO(o\]sÉlŸ™:´ŠÑ,¨Èpš9iŒúœ.© 7?ÚKGN•D:Èñ×û}rcLê·Ÿ»ï>KÀ;oüPz›É÷ ˆ´ä8ºö’947o±|N€c•(;#U}._4Öï8Lïo?ä°Z¬×›­»ï{ü'7ýáGìóyG¼Ø‚нU _kÖl +±¿É¥_â@{Ç —Î¥oÝq¥áû•ô‘Ë‹|¯[:‡”MÑäøš£°âE~ó‹ÇæA¿:¾éÀ}=õ0¿Ø–ïCsó²éÑ/_Gó¦@Qò mÔÚè¨VÖgÓÃ_¸Ê4‚Ý?ùÞ“c·[7ßÿøÓ³{;7ŽCY ¤«¾€-Ë'¿å‚Ë$àÁ;/3É5+Ï oܶÜfccƒã†Õ?ùt ô }ð.ûêVÎr§®í —Σ{o\ÊqIp ò.eÔ6£Ó“Yq¿†rÙ%ïAqv‡uÄõ¤®Á8ÊÒ`PG› CŠÀ½=õ-~“»Z,Jò€2ilúü¦ædÑ=×,a‹§óuÐÃ÷=þÔ]Á?*Œ@# 1J6‡cÒêEÍ勦i‡°€ .Â_½e9‰âÄÖÏt»ÅöÎwŸy&& :×K' ,õ‡A@ 'ßzê©‘&²?)en½l¾iZNfOÅq €¸e­ºx¦rÁ³Û?ÿæš_ÅZgа׈[¥Éaý-+Á‘srÇ’¸C@  ˆ¥ó·­ ä„XÖ—¹ u­b?ÝûeɶA@ ZZè¿ø®›“•FËæåõãLÿ¸zÉL•*Y}©m¶ú‡ýß´èm÷ÿðÇŸç{Ðü¨¨pÇW]èíêQx•@Bl4ÝqÅ"çK¢ïÜ÷ØÏ&zµT6l”%~ó"eø€Aßü¡Ê!LàkkžžÀ÷•¯ÈoZ1C*$SÕÏ¢ÄNŠet¨Œm8Žcõþ`æ ˆ,c¿zñ,S ÔC@ Ð L›I3&Žæw6nw´?èýR‘š2d³Û©¬ªž*k¨ª¶‘j›ÉÎû Ðyˆˆ‰fóp ¥ÄÇÐÈ”xŠŒ—,RêÓÓ¹86< X-¶[ùn*®wH><¿Á8êé²h§³?U\ÅsHÞÄcø]0Ž}æ´³U+9ËafbBŒcÙü\õ¶\@ ܼbí?^$³l¬ZýÔS£ž{ôÑÒ@íwÐ+Kº‚d³Ë\´çÈiOÍmíÊý ¡¡!”;vÍfð³(6:ŠS\;¿uÓä¸V&|œÍ± &2ï+KDvºžû e)˜.ž¡¯v‡ýÙœ—;Ö2lœ… °¬d°IcÒÇ ËB©Åtãu,A«,iJ’XŒö9Co|¼—ªëuΑÖvJlj¢„–&Šã ‚ža=` RcD$ÕGÅP]T45DFÑ¡‚bõ1‡…Ò²ùytÙÂii¦þQ‹dø÷¥æfÇ&à˜1! _†áûUʑϚ<š^Y¿ƒø‹»tõO~’ðÜ#Ôå@†q§Å +®“WósXù…€@°X0='„•%î¶ýóü”%o^@QäS\^C/}°“N•«êcÚ[)·´˜?…”USåÍ&Q×0$PGù£2éhz•Å'ÒûÛÒÖ}'èš‹fÒ…3'’Xžà¢7 ¿Cni±ÏäUSfZÅÅD _yP "S“â©¢¦Þlj°Ê¤`[‚r øӦҪ‰ì‚—Ä:Ø /l†ñw!X‡.Ùÿöö6¶8fJvÎ_¯y >ÇT–%Íšdc—»C'‹è¯k·Pk»•Ì6]PO N£0^‡€€7$57Т“ùês2mmÌN¢‚ÿó½ít¦´ŠnY9ŸÂÍa°2yvÖÁs*¥9Øb-™} Œ’ã£EYâ{˜)#û?ìûl¥lašEiØ‚@tdÅÆD´46µF·Ù%ìö@IÐ(K¢(‰5I¥-Ÿ£W6ìâm©ª UûwP\kK òEŸ†œòR_qŽvdO¤'O§­ûOPyu}éú‹9)D”R˜Ä52ŒØi2Úø˜Èa4h u(Ð}›ƒF ¥q —±8È6VÆšÂI‰ ¬Ò“ÚO°²BŽ€U–‚âéNS”¬VíÍ?­\ïDQšV|†nݵŠR°þ…Y¿M¬°/dëå-»·P„ÕB'Š*èߨD­œLD”xQæ!Ç€#„Re´±ÑHÕ;|®úÐi|¬SÑ!;Ò‡á¥esç^&Jâì­Vé#•.Â.¥;ßRÀ+KFE©´¢š] v¨ïÃÜ3'èêý;)¨Áú÷´ýWQFwmûH)L¬0½¼~Y­V(LA{EØq‡#TÎ 1ümt€ÄiC€öÝåAãe2Ô¯IƧ]7dÁë5” 4œ@ËéFêp˜­oZþW^s½kã·÷Yû Ç(YhtM%]zd¿6,AÀïF4ÖÑu{?%±6m?X@[öW “|_EÁ‡€€€€?€V–äÁSÜí, }zð$•U7Pt{›zHE*ðàÿòûÆU–Ñ%G¨a¼»õ54·°ÂdSîxP˜‚ýê¢ÿ    À$ ¡ÓýÎJ-mmôáÎ|ÕÕEœõ.¦­5P»~ 3 N§õÔÔÒNvVнÄ/AYf_ @@@`HheI:-+í:|šj[(¶µ•f É A'qÃ[Úa]Úºÿ$Õ54ëñKP˜‚óš¢×     È Nͪdã9“Ú-ít¸ DõwváÉ!5RsDUÆÄSSD$5FD‘l·|j¡„|ÝkØ—Pæ³Ù9a+ýDHò0¨_&4   $Ê’(LòV^>mìŠ'$þÒךè8¶(-¡VŽ[Ÿ™J÷ݼŒc’¢å¤ŸÂÍaôÅk–PÖÈzuÃåÎÉVÆeùûûY“ï‹Çð„É"Ímüݵ«ï¯(K°,ùž=Z_¸y–:-KN…ÉùfžÔ§/x»Î¶°pzyÞb¥(M=’þãóW@Qòäå òèË×_¢j’¦ÏÆŒ÷B­Þ­Â¤¬HÄ%grùîjÊ’w[Bm    þ €Ê’S1Ò4;@ýÃm¼3}ÕÄÄRJB,ÝÏ%I! ñ‰ùº~Ù\UÙûSf«TÝÞ©Ù»µhß]»Ý¡”|y>§, By¶”Lç2x '¦ÐÑô,Î~f¢¯Ýºœb£‘éÎÛWïŠ §Óü)ãTfÁõœe0 …¿¸šÂ¤}²Ÿè€€€€@NYÒÞÂ;2Ya À@þîˆnÌ®-™5‰2Ó’º+†ýçIà–• (‚c™Š“FÐáŒ1çY›÷O%¿ÓªäýúQ#€€€€€œ²$ÃÖ$µ ¦ÂäTõð. V] ÿ|§|ÞJ'Õt æÎý_XeIKðà«{³Þ:vÉHM"sXÀÍóëÍ¡T]ǤSgl5‡S}d`MúLß߀º¨è €€€€U–ˆQ¯]iŒˆPeâc«Ô+,/0™ˆÒ:Ò³WÆÆy±fT    D0ƒxá[ÐáœOɉ~þ»t¬ðœÞˇ¾xåd¥éÛÆ•3¥•ôôŸßÒwÍÉK«o\¦¶ÿôÆ&Úy¨@?&+2Ô?x»Ë>mã§}›NWh›j™7.ƒ¾uÇe.ûŒRÿŽŽ6.œ1‰fçú>~+}D<•4¶Pu\<¯,3vë     p^`Y:/|Γ[ÃÌj%:"Ü µ¹V! Q(2nº¬p;67oœ~ÜSÌLcs+«ªÓËh+V« K«´M}é©í dÉ~wë:p¼H}*jëµC>]&'8-J á˜Ø§ Q9€€€ CP–ü¢ÏÉÍ&q7ÓäàÉbmµËòáXDxMŸ0ºK÷'ÏvµÆœf •Ínw/ÚívCS ½ðö'T\^ÓmŸ0°ñY¨@@@@`X€^€_vqí›Ä‰ Žžqºâž«¢zv;swùkln£Ó%ns3&ŽQÙù< /9>†jšx²T¢“E´xÖ$—b'ΖëÛ)‰qTUÛ ok+eUõlIÚOõM­tâì9jk·j‡°!A–¥ ¸Œs§tºÓIwtµ..(RÊ6œyS²µÕ.ˈp³ÊÜ'Nx°,,rZ›â¢I+OR]ßHÛöŸ C'‹ (y„}    AOÊR\Â9¹cÙ¯Óßì [l’ áà‰N*Šc§¦æ8'Éõ4<‰=Ò’D”WדÄ.E¬M"9Y©¬€±ùɃDE˜);c„þÉ™ä¡v€€È}¬®¡Y}š[ÚüÓ(Zï­ÝƒdÙŸp@ lpà ìë£z'Yër³GÑ‘S%jûÈ©R¥Äh ”è3FkÓÌI£),4´Û‘9ÈAã2SiÓž£ªŒ¸ÝÍšìÌ\' ´ñ™iôÙÑBõdg¤Ò#÷¬ÒÉáá_½¤oc@`p È}á«?þk·÷/ñ±Ñ”–O“³Óiå©$Vg£HvÍŠ§îÂé9tÛe Œ‡jýç{J*œq“Ó'fÑ×o]1hýûñŸ×êIrŒYI­Ch‰À[›>£·6ÖmëáæP‘O’ÙöÒùShÂè‘.e÷9C[·Uß÷ÃÕ×Rbœg½Ð ­ìçÙϽºQoý_º†ÆŒJÑ·ý¹²ÿøYúÝKô&¸ú:Ý£H߉•>€e©Ï¨·à\ƒ[]skÒzŸâX¥&ÛÔySÇ÷ÚÙœ¬ÎRAQgŒ’1áƒf}êµ2:¢LÉKŽã<5<Ð<ö»×xº€J—q´´µ‘ÜoäÓÞnq9†0[]ŒëúN¬€Àð ÐwŠ6úv‹M½ä¥è¿ÿïz~ííZZí6ý$÷¡nœ]\ÎÁx›”%oõQ}³'¥NW^áæ0’©4‘õq™#ØAÏ<¿NÛM¥q?ú·™O-ÿô9*«ª¥´äxÿè~¿8Í@u<Å&¹å%Óœ4¸g¥%ºôOÜwö䟡n¯¢¶‘"8¶!‘³sNáþÊ‹w)­¬¥–6§«`Bl$ÅDu-#.<ÅåTTVMç*ëHÕ$'ÆÒôœ,’ìŸÝI%OŸp†'ê–˜Nq]Œç‡ ±ÎË ‘\@ oùïÌxJå˜É:Ðü÷õ¸ìºÆfåzéô~q¯¹Ýb%ya|º#!‹•¯)ã2uo)ßÊ÷mÊÙNŒ‹¢±£FȪ‹È=HîE"a¡&NŽ•¥ïï=¨‰ÝÏ”VëçO;’ï1áú¶¶"÷vy..¯V!I 1”•–DSÆwŸ˜KžÓÄ HîAåÕu*éWÇnMæ©e+.JÏpXBY ¢«,Iþ¾n›žaŘÔA†!o1&Mïóˆr ’ä¡©¥]?w=p‚^Y¿Sßõƒ/_Cšãï~yóüÚOô„ z!^Yûñ^•…óë·.W÷<íØ¿ÞÛ®—÷”ࡆ­g~s³ŠÑÒÎÑ–bE_ÜwÓRýEŠ™ÿ6ÿºv3µ´:ŸkFðË¡'¿~“V\-åïû7/®×Ë̘8ZW–r’0†ÞÔ­Œq)–«_þó Œ±œ¶.ŠÒþøoŠ’”‘·¶òóÊúÚ)j¹î“ýôïötÛŽ¸/ÿ›•·xü8iHZ%µHè'ù틺(JR®¶¡‰þßßßÕ³ùÊ ¹¹Ùzbk²Q$Q–¦LÉþùÓÆ«Ã¾¸IůòË¡¼³Í£¢$Ç¥¿{ùCÎ@êôð‘}Cú“¿¼Ý­¢$eÄÚÿ?œCîG߀eÉ7\}V«LP{ cž%-…¸ÖX\ðäqË=2Y¹ñÉ‘æË’˜Èãcák«qÅÒ7^r8B9Ö˜Lvß´€Z{#àT\ƒ©Å½Ì“Ô³[Lcpå♜n<Ž]\JiýöCzÑÍ{ÓuKçªD4Iì.7‘ÝC$ÓžÈႲÛz’y«z¼Ð9ùµŸoÈà¹q×vák–ÝêuÿÍËØ-x$?4XI”—;ù[Ã"®tò‚GÚéMÞd…G2ii"î.b©oæ·Îïo;¨Û°ãˆJ_¬¹ íǦÄò=q¼ÁcHU€¼BÊ’W0ú¯m%«Í™NSkYLÐîóhÇzZŽÏJSÊ’üjñîñ=cC—À‡#„St»bBÌmqPlˆ—üÒ_ÚÅðW0†Ób&Š&.C!ÎSx™ùÆëf¾¯›ÙËA:Ì*b6u¬Ë6™(lç'»‡óQ™üËĵÈ2„l¬>©m>_msIñ˜°³)¼÷´p›ÍüÃÑÂu6Û;–²mãcìvެʄP³ü™pbÇÚèxª]“ar>;›vÿžcWŽßï´œÔóC‡¸òŠo¿Q&ºÍsb<¶úÆ¥4©ÃÕW\ݤN‰e¤¡¹…xî&‘ù?©)K-míÊß^;÷è™2’{˜&³ ‰X4‘Ø ÉcG)÷¾ˆð0ºñÒ9ªMÍ%®º¾÷KZ^Ý ’VhuÊœu|n¥¶IâÂò[vÅÑ$ÿt)?Ä©·µÚC˜»›“ch1r¯=zº„öæªÓΖuÆjõ`  ЕÀ~Á²³ã!7u‰Ã‘F‘˜Dcö_ã±Iürä^¾i’ÎVi±¼hRÖ¡8ɶ”•XD‰9È1Ú—]0M­«mƒu{æ¤1ür&Tóö=H*}›çš’F"¢ô沈 °Ìi'®"ÆX«]‡O«}òÏ4ާ7=MRù¥ÕÁú³¿eI£ãÝ%”%ïòôym,85'C ¡587o¬¶Ú¯åxðþh—ë)²24 ¬9äˆå°ûdV&’L¡”d•%XQIàe+!ñ<ò>×t„¢x?‘ü~tØ~X1Q»äžobÍEÝúå)§6:×µMÞ£;ÿ‘-bRGC;ê ã~8ÅP¯¾ÚqŒgõö¥-Ù–C¿GªÏ’mßÁ}oª%úÞaG;©åqÖò8j¹ûU|N—)sب"4ŒÊœKÕìš*Õ 9‘7šU¦»ÁI\ÑdV&<‰$hp!3Ž_¶hÊ’œc1¸ÈÛß9ŽHSlÄOS–³KŸ&œ,‰"4‰d¥HqùᳯÓ,žh{☑ªýoÝq™v¸OËÂs•.1V+ KRÁ´œLºqù<½Lr‡ß¿(QÏ>z·Kò«ªk`E©”­Ic°Z;þ@\Jc@ÀÀnCÖ;÷c²-q€F…À½ÌTÎ8g”Ñ<ʼnLu¢½|1ÞƒD)—`Í~‚à Äí.*2\%V8SÚé‚l|aãí{ôWÄh2vTª~/Ôöݲr>¿°îì–¡X,ëî"–¦³¬`®Û²ßå•݉!¾!Ðù«ä›úQ«ÈCˆf²ÕªŸ7¥óͬ¶¯/KOV$ㄵ}©e“›fÔk²°Q9 :âx–žM&r:‚‹VÑ¡\hJŒ® Èpäø®¤%Jãq¦u _Sý¼0)VY¡"++U¬ps™³üc{–ídElN;»fª©qˆ!ч#.(âFvõEÝ'*ÌNîo{ãØ•Ä(vÃoµÄLá;’qSD\`nZ1_­“Ó]ðä ¸×Hysº€•º‹gOîÒíã²¢¦Á¸É.„Š™¸MãÛf—¼!Ö±í 8Åq…Š)Д?÷rØ8?G}ò¹.®rî5湽̗½è¨p7aÍz£'J¦,ɱƒlMêð©ýItǽJ;ÇÛ÷ yÉRÉ=57fw‘Qî/£´2bµßºï„rg+¶ÄTAüKÊ’y{¥51ËÛ-˜O2 H àˆøæK=™RD\]²F& ¤*œ`l!æDé’)"&“£ú¡¨y9»òïÚ(~~óÊ<^g-Š-S¬9±»`-/N±Òy’÷Ÿh|ë÷Ñֆη…^ï‹—+”¿õùS²õZCxâ¢"û%#ž6Iµ^Àm%>ºÿqŒ¢iÊRYU½ \á‡Y×d~Þ8mU-åePg¥{sãNÞîrL6$([>ÛøááÛw^æ15¯ñ$Iî`I]ÜWy—3á½±qw—âòp©”§.±@ [b½6þ F°·ÌN-ÖeÍ͵ۓù@ÿÝõG4˵–åó [¸•²dpÁ“ ãü”Þ¾Éó•fù’¾ÇßÛXjÙÕøþö®KÒíyÙ­…Ohû°ô (K¾áêÓZÅæ7¾_m|å†KH>žä™oßæi·Ú÷Ý/\Ùí1ãyèrwY1Ç: ‰¬$Íæ˜ªÙ2–¨%7æÙê*¨(!Œö5EÑH³…Ò­|80e$?Œ\Ïomý)³T@˜ Ä–v÷/&sX'!‰)HŒïª¼ˆ•K‚Ÿ%™LX™ÏÁßÚ›c­ÿâßÿ+T’î»'‘@j£ÔrüBwI,Œå¤m£¢$rL¯ÜGH¢×?ÜeÉ ë Ðóß¶¿ãjĺôÇ ‰ˆÛ°X˜ŒÖm£ ž6oÞƒÄÊÎï¦tK–܃ú*’&]ËŽ'/É—p‚‡iìŠ8ž­pa<ùö?{¡¯U¡Üy€²tðp*€Àð&`2GPSDímt>ðsr J ³PzXyÉÊï¶¢2L±Â´óPbp]ñÄz­‰{OÉðtÚG É&ä-¯ˆÌ5"ñë¶ìÓ]mŒéÇU!ÿH´QÊxBG£²$.-¿øÇ{zÉx'Jù§:§%»ßC_¼RÅFh«ê:Ýj´}X‚œOS–š8™Õ¦=ùúËyÑk̨é‹{¤1OŽåxGç=£Œ3㹋ôoo‡û±¸;?rÏ*•OKú åof7f¹7iâž ]Û¥÷ tþjy¿nÔ ½àŒH¬OH†‡Éáê%¿…“ýÄûœK‚vW…ùûs*)ã<_Žsj×"çñŠœ+õñþŽó¹UŸ¼å3¬«òÒ¯pC|¾œ¥ÕÏ{Õ1Þï<¢7Ù-mÊÍQÚÒ„³ñÑ9‹Y}dŸ¤ÌŠj§Ñáí”n¡ð.0ÌDÞÚjÊ’LVÆ"âö¢)B¾^ôóÞÕ6iÉìIz°·¤î^uÑ,:Í.xZR ­.ý+aI5,I´9æ>=xRe¾ÓÊÈä•"%ÎtIJ. ž‘k"\Ç ©ÏµýÁºäoåm÷>ö¤3 ,X1 ûÍ÷¼+3Vââ'îx…IÖò$Óšˆk²ú=èØá‹{T-÷!MYH²ØM›©Z•ŠvåS#O¸ë,›ªî¥nóB¹ÇˆîÉ?­Êã߀²ä{Æh…ÿšUÃv[ªµ²$B)üœêT"D¡èP"”r 4gNºN…B)êì˜`°­Ýª[Ü´–/˜B³sï-âc£ø¥R&gÁsºËË"£å}$Ï1UÏnÄ’]¤¡## ±>¬{‡@g¤­wêC-  ½°²±íTk­¯§—Ê“è3NÑjº*“»b$“?Μ4Ö#%±,}çóW¨ %=à29÷Üyg•Šé®ˆË~y¨xüÞkirÇ„º.yC²J}ŸcäFÙ'ó½ÈÜvF‘ì£_½åR? í…Ê}Îí– ƒO@b“$¡ŒQr,“'ñÅ=HÚ¹õ²…ôùU‹õÉomË=ñŽ+Ñ-+wÓÝ×,¡<¶‚E\‚ç±ò÷ý{®¡1<Ï”&[ö§Ú×ìŸÚ1,Ï,KçÇgƒ€Àyhq„Ðgœ b?Ƴ¥iJL %‡¿ã¡øáŸo†Ê'¾zSc»˜“!ȧ7‘jQ:´Tàsr³UPwç‰{ݰÂT^ÝÀó“ÔSu]O‚lâ igŠaQXÜ凫¯sßå²MÞuÕq&ªâŠ*çù—RcùÍrj·iÓsæ©ÙœVX,H캓Ãý’TëštÇ÷_¾F+‚% k×\2›ä3P÷9OÙêŒõýì[Ýgô5–›ÎnoGÏœÓw-äì–ÝÉ@îA’‚¼»{‚ÖÎâ™i!é\U߇jÙ-Ù¦¬[â.ì>œ#Ö%™ˆ»¨¬†J*kHîcr’Ìx"Þy¹Zºÿ3câè^ûâ~¶»'e©{68 ~# Q/'ØÚ$ŸLN1?®™½ 4ùm=4T\^£+JRìÂY{(ÝyH²Ö3×uøš¼a–Ï”>VÉ“ëvÄ1õñ@Eñ‰“ØÊ,nx½‰/îA¢èdLVŸÞÚ׎K<%æÀÔhø eÉÿÌÑ"€ôH ¸=œJªÂULÓœØfŠU*øäÓ'Ù*ÔÈÈ'õÎËŠ»;Œ~+  àE2­Ì™&1Š{òÏè5‹Õ}%e©¯¤P@üH@ò ˆ•ét[8åE·Òôè– K=þîÖý]&n½ú¢™~¤ˆ¦@†3³¬$½üÁÃ(  ÐWHðÐWR( ƒ@@’Aà¯V&QA«k²AèÎy5)±p–9€ ‰º“·xŠŒþ Íà ËRp\'ô@`˜hc¥iS]µµÑqMAaeºéÒùœ¡•(DÒHžR&‡„€€€¿Œçd_äŒr2µL0zdJÉeüÕ/´\ ,×õBoA†9vÍ+·˜iI|¥‡[šÆô‰YÝ?t@`hiÑŒ C{Ï À ÏçˆÑ€x—@£-„Þ«I ] Ñdï˜4Õ»- 6!˾  „DG:ØEµ¶PZšÐ@aCwNÛ ¼:AÖe™ ‚¬×è.9Æ“ƒÆ€€o @Yò-_ÔƒF€'%•çiÿg1…†µ+ïãgj^59œyÝd’UuXÖy…˨}Ò÷Žã¼[ÀûU&‡]âm‡É!m9+4®kõHR³YJ"l9“Oìh‹+ï8Þ±ì<.û¥)+cêè‡ê§óœŽÃ\®£ª­Ž~vœÀç†p{êl®›ûÔqÜ9tÙî¨ß C•ú8… —‘楈½¶r’½¹~гq¶¼ØÊ´<±>(☜øðo à{‹Ï=ùØ÷©OèKïV?ö£Ÿñýè¡ÞK¢€Àù€²t>ôp.0SH+w/Ê^6jÜowÕ9øÀjõ§ÿ¯ÑàÒݺŒ¢»cî}5–Øè½V™%ŒÖ±Ât+Lç;'S»ÅJÅåµTY[O•uMaV‰Fó$Š’¨ÁÒØÜÊ3Û;ç–2óŽÑQþhm€€€@P–º Á>µÖPZW@—'ÕQ\˜SÑèÏ(ìüôñž|z{ógÔØÜÖåÔ°ÐPºhÎdºâÂi”Ýå¸7wüüoïQIEªR’D|ýÖÞ¬Þc]Oÿù-ª¨iPÇrzóÛ.[à±v‚xŸ€˜ì¿úã¿öX1{Pdx8%ÆESî¸ ZÅs¶ÅàEJÌpÐ; ,y‡#jè;ËYØοVþí7ÁÞÂÛjì¢öñ6ÿ€ªõ¾URöÚPOW±Â.Dû!¿{y=ì†ÖÄ?RͬØ4±ÓÄÅK;ï7Q3otب™"¨)ÄB +B<¥…… –ˆF²p–Ø0²üçŽêˆ1ê®Éžö¯Þå0g$PTS;E;B)ŠÛŒ µózE±JÅ W4÷'Š•­(îk´ÝD ¼/‘ûœ(K>沞á‡cbaú˜çc’¦ÑY¿ýpE)5)žÆ¤'SYu[yjI,O" M-ô»—6ÐÃw_Eæ0ÿÿ„ˆ"S×ÐlVÿ”BÉXbµ MôÇ×>¢5÷ß@áfÿß‹ÜÑáÞäNdèlþ·kè°ÄH@  „:¬âSÄʃ¥Ý윩@,7V^¯åõ*>Ve ¡jV"jx»&ÄFµ!!TkµR}[4Õýz¢©«/ÖyŒpÍyœ+§>7Ï$¯ûåS/Ûý•5‘Mi”ÈcLåñ¦±’•ÆéÒXŸHåÇíQ\_Zë ÔòÅífÚÙC yòھȦ=!m©IôÛ–SrB¬~jsKý–¤“Eåj_QY5=SFÓr2õ2Þ\yðÎËÉf·©*C!óæXP€@ÿ L›IŸ»ü—8–±¨¼†Þظ‡]…%$—¨º¾‰¶î?AKçæº”Åx“”%oÒD] @8[»tÇRx䃟æÒÃçcÕ  a ¸+k–™ä×õ\ǧK=ß<îˆH°QF›²ØR•ÅÖ(IɛӡHu)è;Ž4GRr˜•&Fõ¬ó¶¶Y¨¼Ú«#cºtAž‹¢$û$ÁÂV-¡ÿ|ö5ÙTRÀŠ“(K'ΖQS‹úªQ(› T»ÅFGN•h§PFj‰ÅJ“²ªz:WU§mÒÔñÆ ÄŦ…û%’Éq ©$‰'¤Ÿ5õF«©‡§³3 E¸Ù5íˆX  Š+9aE5÷±’b(+-‰¦Œï›’WÌfù§ÏQYU­Jr!î>9YCF§Ö¹c€üM§$ƹtK¶³3R).:Š~ÿòýXѹj}ݸ"ŠÔñÂ2*á¿c±'ÅÅPÿýç¿ãL^º‹Ü_äÞ£ÉÔ¾±‹„Qö?+ÙW•¤%ÇѨ‰ý¾7µ´¶ó˦R**«¡Z¾GeŽL¢ìQ©4:=©K{ÒÐá‚b²Xq¨£¹¬¼È’VŸ+TýXuÑ,µÄ?¾#eÉwlQ3 á®(õåBtXÒNqYù貦ÄÝXKãÙ"5ž]ü&óOÖT¶F¥èxe[},¥›-=&|°XmUI´mm92%ž¾|ýÅlñq>)¤°â!òé“´eï1­ý÷ƒ·ëYó ŠË]j.æw\¹H/+É$v*PÛq1‘ôÌ·oWëÿzo{—gJ+é^xW?W[yæùujõ‘{®VRÚþM{ŽÒËìä‡ «¶K_¦H »®º&Œ©ïs_ÙòÙ1úûº­úƒ‘vü~ƒ}ëÊJ1Ôöa  à?Ù£\o¿6»S‘0ö`[›þùî6Vd\ïoZ™ËM£ë–Î¥ƒ¯òîüÓôæÆÝZ¾ÝFq1Qú¶¬ˆ’¦)K—/šN7\:—úso:t²ˆþïí­n®ÄÎ&ÒSèÏ­ìò²ê/ÿÞÌîÏNKšÜ?%nëõýÌbå Ê’Ë%òÉ”%Ÿ`E¥ C…Àš “˜2v|Ô°~pÀ1²éØÎ_†„šsÂâóv¨ò±ÝñV$vZŽÜ;+JJ"¿qÿ‘w¶ ¶ÂÌŸ2ŽrF§¹¼éœ?u¼ûél ÊrQ–N±%G2؉œ)oÏN9URѹÁkÅÛÓrœç¸àÆ«œâƒí‡º=û\eýîåéû¬`-]Ú øæ“}ǵM—åÇ»ó9#W?$ÍsÙ ÿ¨¨u½ŸM›îÒð¿?ÚCë>Ùï²Ï}ã½mé,[vD9ñ—ìäCzcS·Í‰•ý§üòçÛWz´|ɉÇΜSIvº­|BÊ’O°¢R¡LàÇÓMeŸoÊØ¬ƒÀp" ˆüz²(JâN+–qMÖ~¼‡]…nyò÷ÿ»®$qU™Ìy|æLKÿþ5Ã(û$YÄù*K}¹7½ÿéA×»¯Üp±;9kòާŠf%ï€tI%Ò…ΓÕ[ŽËýìŽ+ÑHvÛ“1B|O »ßNß·Œ@@`ˆæ|æÓbZèÚ”Zºž?¹Ñ­dælƒ) ¬Àíotõ½7öG¬EßúÜeêÁcLºk<€±œ&K,Ð|“j ‰¦²â‰«XÜD¡Ð,G磉¦$]òDÙEÌ¢)hR×XšÖ%­î[VÎW.tâF·t^^—·×RN”/÷x¦qn‰Üc½´ú±ð{oXÊÖ™ .ㄚˆµZS”´}¢Ø\<{’¶©¬LÆ{™~ÀË+’ÌA“œ¨Â=ÉÌ’ÙµÃj¹ý 3–Óe'oˆÛô7o_¡,åP”Üéøn–%ß±EÍ Ü@"[œ.àôÝsbšéXKå·DQ#+.ƒ!ÒötVâB»y)oi—ðC„|$˜øÈ©b~ÃY¡,Fâ>g9þ¿o|Lßý•j·(Korœ€ˆd×+L­Á477›$ÖG¬Kg:â–Nã•zqÁS÷áQÒ*kõ’âBç.¢¹+Bîe&éø-ÇãØÏ(bʇ± pÄÚlœ;I¬Å’Ó(~ócv£½˜æ°±ˆ$>¨3Üw²Ò’Åõõ vÉ3J)g»LŒ6îòúº¸k"/‘¾öôóڦǥ1C©±@NÖÈA™ßÎØ‡á¸ei8^uŒ@À¯ÂClmj¥<¶2å”Þû›£©•gÍõ§H{…¿4.ҙ滧¶åíå‚i9ê#åd"Ú-ûN¸dŠ’”ábi‹X£ä-cS+DÍΔåâ›/™ôÆqÊßÏŽžÑÝôNÜõĽÎ"ý¿MÙµe Ï©‰! ƒG`6»ËI<¢QDY’$ /p69Ét'ñ=³Ù3¾²‹®3cœvŽX‘âË>ku½qOè°iïQ:VèšOú#3Æo6L>+Vc¶&cÀr [œª:Ò‚Kêq‘±£Fð[_ç»Û½laÒ¤·”áZ¹î–­í®)ѳ;”3)/Yïžpf°“mQ ?Ú•Ï™ÿjÔ'4$Š’€€@0f¿”nke˺¿$ëò÷o´„Ë>I0³ýàIYU"V¹—‰¸O@«e¿”c6›ÞÚô™¬öYŒ÷¦,Cœ”Üöé¼J…’ýï¡_¼¨$¥9$p¸ªÑÓ/ô@†6nТø&J µÑž@¶ÃƒÌgc—y—Ī¥Id„Y¹ØÉd´"wôóÞå,RÉ4’ßàÚvÎÕH¢ýì¯Z2K«B-e>#QˆäÅ誢¹áEð[ÛŒÔD¥¤hõˆ%(7»ç”á.ðF¸!M¸{eý.•ñê‚é9*•÷U<§ŠñAã¯o¤ÇÄ)ìä9Jä-´&—/š®­b  $Ì|ß0ŠÅøáŠÅÓ9¾ò8É&àúóqc¨@@À3<ŽeZÈ™ó|-µ<÷“ÅM#»vé\š6!Ó¥i±$‰Ò±7¿PY•4G ÍŸ:ž–/Ès)/SÇ»*>’ØAb{` , ŒÎ¯9™$½·/Eô$÷)ñŽûÆm+Uö©QlýéNDù¹ï¦KùÇübE¦æ[’’Ï(Zü’¶o .x’ êF~,±îäÖËÒçW-vy`ÒÊÊCÔW.¢[V.Ðva  D2ÝîQÿþø3—ÞÏÉKßÿÒ5Ý&Žw»oܾ’VñäÚîrÛå i)Ï ç~‘ùœdªc:s÷s{»7‰bö_÷_ïâ*h¬Cæ„ûá½×Ñd^B‹@ÿ^éVßÑræ°‹\I»¹‹BãÍ–s¢‡ôp×9K¤~™É^>ò¦¶Šç+’¥ÄH¶¨Tž³H‚¨V÷>ÉüEÏ>z·ûn}[2Yɧ7ùáêëz,²ráTZ> §'o¥&––Ø£”×7¾‹gN¤…l;ÇüŠ+jÙõƦÜåí°$vp—'¾z“û.—í‹çL&ù@@¼O@^Øôtï0¶89{T¯eåïüqV÷õÛ¯¸€n»ü5_\3gí”Z\Óšûop/î²ÝÛ½)™3®¾i§9oáû’Äv6ª{«XÄÅÝÏ“<óíÛ=íÆ>?€²äGØh @z# ñÆ5ÒÚêî-<½ÕÑÛñÊ^²ï‰R$ŸÉ4ª·ªí¸(<âÛoÌVåÞq}‘Ø+cpµ{lƒ ] ì²'Ÿ<žÚ ?"Ê[OVöžêê˽©·{WOõã˜ÿ À ÏÿÌÑ"€ôH Ål£t³k–·OèçAw7¼~žŽâ   0l@YòÂ¥Ö JÖ(ˆ Xxo‘Pcú-ÿv­€OLâø%_I›£«š¯ÚB½   Ì´çü`à÷=ºÍ™¢RRîBüK ¬²F5˜Ô䜄ҿ­£5ðŒpßY–¬¬,¹%ÄóÝ@P3€€1(K^¸x±mÎìUõP–¼@³U”Õ:S-§4×÷ïD”'â ¸PßY«Ûí°.øWÝP–¼pbÚ¥šzßÏ‘â…î™*ZZÛ©¦Íùö=¥±aÈŒ @t”%–   0 ,yzZ}™8fFRÔBaòÐ>V±'ÿ´*™ÖPKß¹,õ±;(^'fò²d%X–¼~ÁP!€€À#eÉ —4ÒÒNYµUª¦ýÇÏz¡FTÑÛœTŦö¥8Ê€@ðBcA’‡ ü> Ë  þ&eÉKÄsÊJTM{ŽœöR¨¦'5õt¬°Œß;hJ)ÔžXáXðh²ûîfBЇàýf ç  þ"à»_b @ÚÉ;WÄé«ítôÌ9~ˆ? ½ºÝxuÃ.5¸œòs×êL°1tG‹‘ W6ßݢÑo¸~­0n~ðÝ/q?:1ŠÆ·4Ó¬ÂSj(o|¸{( )`Ǻ”>;ZH¡v;-Ëß°ýDÇ@à|´q¶:Iñí+ çl{ž @Yê™O¿Ž^xâ0…Û¬TP\A›öí×¹(Ü7Í-mô÷u[Uá9…')¹ YðúF¥‚@­5Ôg]<(K>ËŠA@†(K^¼˜Ñím´äØaUã‹ïo§“Eå^¬UÙØ’ôì«©¢¦››h1+§ªŠÛÃ}6´XÎßä³N£bP–¼ }þéc”Ç l6;ýìË«1Yª7Ûíú¿µ[èÇ„EXÛéæ]Ÿ ]¸7À¢Ž€%PÒnöYßbCm>«ƒ€€ÀP"eÉWóª»hd}-Õ7¶ÐOþú>œ'cq½ûå?ß§í („“h\¿çSJi‚zžXqzhµ™¨Êæ³&BYò[T   0´@YòÁõ ³Ùè¶›(«¦Šš[Úé—ÿx6ì8Bbô€¸2>ý—·è('uˆ°Zè¦Ý[)» îý£ˆÒÁF@\ð|y·a¶ô@@…€ï^]Êp§Ñ¨övº}ûÇôÎŒ¹t(c,½üÁvÚ²÷(ݲr>MŸ8 ОTÕ6ÐkœUpwǼUIÍtÓ®­°(èõB·¼Kàt›ï╤§# ,y÷Š¡6¡JÊ’¯¬Ì»´jßNSUAOšN¥•µô«~@™i‰4ojÍŸ’M#ã|؃઺¥­]¥ßuø49U¬,qâv')Ù/:~"-–àz  Ðh ¡"*K‘œ /6Ì>€žá~ ,ùášÏ(:M¹KË #£éLÊHªç˜¦úâJ:Á©Äy¥E”[ZH <Ñ/†¿+8ÑáÓ!g„·û´~T  C‰”%?_MQš¦•ª%4”j¢ã¨&&†jyÙlö]ª`?³ÏÍ™9F"Ç#%ñ¼I—$sUA@ H´J?-œ[I8‰€å6h¿o>èx]C‡¢o"(K>àëë*í¬,IUµºÒëë&Q?x@yMƒ Ø7‡„özå^ªÊ’—@¢aG@=`VÔœÿ[]±PímŠö9Àñ‘P–|9H\:å5ÎɽC¡EAÒmtÓ@ÀlsÈfE]ƒ£Ýât©4Æ*<òêjk³D˜ÈdI „úÛ§;c2ÑQ«Åf:xúÃf(;ÔÆúþš5·lö!(KÚeÂ@úA`Íš{Z&Ç9eÿy<¨ˆUi_“oc•¤£T> Y…€í?V¨(pìݺ[o½f‰`ýN˜L¯J×÷æŸÖ ßØÀ§Nª{߇þÈ ,òÕAß@š@ˆÉô¶tpû“÷UÒ„kQSû|Î@ f„[(ÕŒI+Ân(žóÂÛŸPcS+eM‡/>ñ…¡8Æá6¦ß¯yìSNÔñŠÃa7ýï2ã ·o@p·°´ŠÞظ»ã…ãÑ_¯yÀ™–3€‡e)€/º ø”û@HÈWø ™ãÃGˆV{í4{ жÿÏÞuÀWQeý33¯¥wJB¤v]vÕEW×ÝOW°ëŠ”$ø$ ¡XqUwu-ë*öÅŠ¢"M¤ƒôÞ!¤·Wæ;g^æe’¼$¯%yIÎýý&ïÎ{Ͻ÷?“÷îN¹…! Ú 4ØÀ‡ ƒBØÁøZUÓ¯~ÞwÂuµP.êáìÐzn¯Qgxoì‰c§/À¿—þÐz&Æ3iUä–ÀË|‡¡î­ôÂfiG]ê-a‚L–ZÂ]â12Œ@@#3'…=˜iÿùj¼}ßqÊÖ™v£Fé|#o@K·CRGÔ,qb~Þ²þ÷ý¯ ?—EáþÅæÔõŒJëAàæé'Aº…ˆð¯»ÃûËÖ{t¦õ Â3 $ò ‹aÑ{ß&t³ƒm:}Ôf³à™íz3MˆÉR3ÏÝ2Œ@ëB '#%>¶à³——|'ÿðën—,µ ð+ú*5EÐ>³M÷üoÕ&xëóÕ¸x–émî¢×ÒS^g`Z9é³Ö!žŒ7Ù¾rÃNXüáwh’Ç&¸­ïN·¼‘é]Ö_ÈÇÏ\ ÍöI½,ÜüŠù¢–2&K-åNñ8F  ÀWer¼nÔíbì5òx÷«5ðö?;6þÔŒ|ua(XdôkmäƒZ¥NFÖ*52Ì->7¿^ýà;øò§-Ê8A4çd¦>ЃæÁù„Àk™©oc8ø?â‚´tËž£°àÍ/åýÇÎø$“3Þ"@A–¯Û ϼý•4Jh®¾U0‚6TöVfs´Ã1sbF€ðfó8z›495ó ¾ÆÏ\½y°i×!ûïÆ¯ÖöWÁ±rƒ?ºjPÆ `öUj¤VZ¡¼Â ß®Ûßü¼Ín±ÚD|•[‘ï&g§§¼ÕJ§ÌÓÒ 1ëãÉiYcÀn[zìtnû…o~%_6¨‡0qìpˆ ÑÔä,#Ðxlß>üv½ýÔù|RÌÐ÷Ðçz]ÔŸZ’FIE‡É’Š„Ÿ>¥Î ‘½Ð}iV+äþî/î×Äš’Bh¥Y†¥•ËÊ<¥éÖÁpép°_ȇ¢y‹}WÛAƒgúZúÌ Ì;ÀR"gØ'càaÝörb÷xûˆA=¥¡}:ƒÑ g ¿"p&·Öo??oÝgAÍ6=`ø§ðeÍÌWçÌz“,0üÚa c²äo ñ©ð(yZß#áMSY—Ø Â<¥tV¾ü(~n±G a¡tûï‘p…€ýìyÚreF P p¾f³|éIÛÜûl2¤Ê=†w>WP tà6Ðë$0êq‹Ÿ>%úñ‹öGÈzvgñ»e¹-ÊYA¶ÛMèSã68_› Šž¿™ñ ì¦kl·ÛCm6{ƒÎmçÑÔî †­/(,»ì\‡(?DˆÑû9©w4ݨ¹§@BàåY³èuÊ”´¬l;Øàã1~çãX,Èa!¦¢v1áe£#dQb¯Œ@ºw-i,%¥åpò\žt6¯07G6VŽ]ËÛümZªÏX0}záâô”–4­jce²T ßOäâ¨øam5AºÄÞ ÆÅ(e¶ÃÇ€5Év›šmSŸbdˆâ@êÓ‚&MTˆR›€'Û& H?I™™KÏ–Âc«æŒkZ‹Å¦U…^çhaìTUÙrOÀÑm?«?X^ m! Ã[È8›e˜H>oOJÍ<¯›õpK‰:Õ,@µòN§ÏÜ„S¼ú”Œ®Aü3šÿY¹_aqiûœnåðôš ô•#GÙeø£ôvG]Âgfó=­â…“%??Aöó¹µÌÈBg< †J²TñãZ(}÷c—½’fEêÑ„`°í?ö3çjÕL¸Ò9n3Š Jš]ÿÞ ãÛEëÃhûã2a;)¾ˆÛýôYi«zéh‚¯¨%¡¹Ì±¹»®wÂCÁvèÐ}M¦I7ƒé÷7ø*†Û3À”ŒŒÐGÿ¤ïÔ;¡1*HRCNà“¢R»½lëoßâ¿qqcôר2e¸'ÑÙÝ>pÎGQC÷ž»õ[\=AÐc [@ºø2v”ñÀ kFLRvö_r’“9â‡/`¶ð¶•Nõh–™S³²¢ä¹¿]¶ Äÿ»îøÿĪ¥~›oøb±(À. ¤–NQ{Zã÷ “¥æ{ºœ= ¾ç0Þ|­³Œ26$KÅs_랪M.ƒï¿ŒF+õ žœAwÞ úÁ‰ø5çøž³Ÿ:EϾ ÖÕÛ®A“ÿ ÙªìE.)Ud—æ¼V$A”Äv±ùÆ‹J¾â§u`ùuÝ}ˆQ‘J‘³òoVBñKÿRˆZøsOƒ®o/Ç5üKc3^5 г߆ò¥ß8ËkfäÜ3mZË#Ò®&Åe>!ðêÌ™PÀêÊÃ'Yܘhí𛄸ÃaO?Y‹(Ѱ$$.aóRA?lËQ†>>ôC8‰U;´ƒÐ!)29ÛÆŽ„à‡þV(ÑE!8Û„°¬´Z×è:™È…`;'QR `¼î*0M¼žÎP¾¢C›ˆ¸Õ(Ò^¦|釟CÁýO*Gáô95/ó9#Т˜bÎêë*}ï‹zA`)f0ž—b;¯Ó%,× KZ4p<øj¼ôðÃå÷„úþV»àÕ‰|m~^ùòÇÌÏE{Õœ1Œ#ÐF`²ÔÌ7Þ8æ Ð BÍ&òe*|úÈ»÷…LP™¢uš|ek%16 ŠŸÏ†¼ÿ{Š2_¹ÜýJŒ‰©W7g}êCMÅ‹þ þxäýíq°?¥ aÕê«u‰¬Y6mƒ‚GR òã`Y»Q½†Ô|aê<sUô?ò׺ð§d¨øz…³.g¶„jHzÚ­¶¤øžÝtíºú}ê‚ÁtNGDI°i…K¼ñ|gã…kAiùI“&Ùpo¤Éx¿çù>ù²bKÉŠy¨ïÂX#À0m&KÍ|› W;Lêh%o-˺_Á~ê,”¾þ.ØO:Ȍԭ HÝk/ºÊ>ûÊ¿]¥ø6U¬^ÖÍÛ³‘:vpæ-¿íÒÿ|ŋ߄r$1äëd?}ä‚g±]œ3ïÌ`(Ú¢¬E`Ý{P!V%ï~ä¼$tl¯äI–â;UyE®¨9¿ä 6w‚Å™6ƒÀÔÔù}ÑÇd•ÕÙØËñBÁŸ“G¢tV×i=jsíZ¹¨ÈÝ5/Qø^[ÆùÖ…@NzêLܨäïø ]L¼OèØßßV«0Ïíã½nÉ0Œ@ÛA€}–šù^Kñ#0Ž †Ñ—;ϵ à`;xX[¶}ªSÐ5 •A è¼ìýÏ” †Ë.†©1¡#èzv!¢~ó Û‰ÓÕˆýtUÀ Csb*îOŸh•-ß^ßÑ4µ¹‚ßE Æ 3ºØ„_j%ôó±ËvÈ® çZ+Ù™©Ï%¥dœG3碒÷_²ܵÂbÿijZÖõ¯¦Ï¬2h­Àñ¼F€ðï¿l}è”›V! ÆU™F¨ºP#§ Ì ^²çUi†¨ <]&ýúÔÕü’ÈdÈ•ØÞ…F©RŠ=¿ºü:;pÙ+2mÒæ °È+dÚû]‚1į“GÒi æ°±QÂ^ðßþƒg UQ`üÚ3 4Ð$ïÍäÙ¹²]^‚7¿Ê9ÕãÊqV»meÒì̉9sRVzÜœ0Œ#ÐF`²ÔÌ7Úvþ‚ÈÂ}Î^€£qÍxHËãM¢0à¡æi¸xslÃRúÁÿÀ²æ°î?„Qî&AÐm7y#–Û0Œ@%Éió†XdËr|Y«ï6tÑUÚb€$M§¤ØNµ›Î:åÊp0ôL+Ÿíœ(g´dÏI]:5mî56Ù¾Íê"´×<ËËa`ƒ¯’SçÞ™1Ëõžž äÚŒ#À´:ük'Òêàiü ÙŸtt‚fsvôõ±lÞá<ìyèûƒÜI9ʼÛ׋Âz«D©bý&(}ã=°þ¶wÄ´âžK¿£ÆŸ%÷À´N’Ì™Ãí²e%]LG0tÅè”~L¢)øD]D ý”¬uÿ9ó8Á:Ü]³¨GàÕôY? ¢n #u8·z9^$[F´ã\’”–yŸ—"¸#À0­&KÍ|{+~ÞàAè´© ØWñ/2^3"^Ì€ð¬Ϙ‚Á¡rVv3£5ßB+7›•$0\q .Õ8 × ÿí¦üšÕ¤NAŒŽrnœ[ó:Ÿ3­ÉiY—ÈVûw¨ ŽB"ÀПü ˆ™ïÁä… àãhz÷«KÊ‘Exƒ:ò@$Wmed§ÏØ‚S…¡Å«;°zOZ†+¡È²ê‘ÌLê3˜,€`Z*L–Zêãq3Œ@“ €›vÞh·ÉŸa0% ŠÆÄ˱oôòCCÂ÷KÑíwÖ' ƒUÚEÌë*\¨¯_c"Ô¸yícH˜R|E_ )-•WO5gõðU·gF %"Àd©%Þ53#À4 ɳ3&¢ÒÇh’d¤1ø˜ÆÐ•þq÷TˆRTû] MF°Ãæ' õú25$ƒ¯·=0ÍE1™È¶/³G¦ž6‹í§©æLÿ©S}·eF  `²Ô„`sWŒ#Ðr˜<;ãVÙ FIÙäŒB„ ‚Þû0ûÚÙ‹!{$wˆ’ÑOém[Î3î"€&y9²(LBEh…»m\ÕíVû*Šéê:—1Œ#ÐZ`²ÔZï,Ï‹`¼F 9%ãvÁ.¿‡ DÇæI¸‘³qÐhÜ,Ìk™Ú†hÊ÷›Õn¶ÌU5gƒuðû)¹B‡ËÜEàµ9©¡†é|žŠÜmã²î+†åËÈ4Õåu.dF "Àd©ÞTž#ÀxÀ””Œ»Ðfé?è«á°µÃ¦±ÿH¢½ªi)†Eí’"c÷iŠêÊÚ$ ,0÷ êªÀ匀»dÏIùNÐ ãðq>çnWõðBFjýtrJÆÝ®®s#À0­ &K­íŽò|FÀk¦¤eþŸ]ßÂðà’*ÄÐ÷RÀHuê©OŸbxô)"7:k8á~·¯g ~k¸&×`ÜC Ûœú‹†QH˜Ž¸×Âu­Ê o&¥e<꺗2Œ#Ðz`²Ôzî%Ï„`|@ )-ó>›]~‚ÎïE}÷Á oßÍ©UMQ3µ] 9XURwÇðó‚~ï§T7D|ÅK^͘¾Û¨3ŒD¦‹Ôß…,Èvùù¤ÔôŒúëñUF€`Z6ÎEAËžFÓ>êã7 úËw•#äáû\ ì©iÎ:º¾½\Öq§P!"L90&¬;Mš­ŽÔ¥D}ø/åýûT¿ŒC…ðùiÊa¸xˆ_d²F@‹.ø¦âÂ/5JÎ0}Bo0tIÔVó.aœQ›´UŒˆ9äŽA†C¶pð}w:ã:m—ÌÓ‚¥+1¾ãz_@bŸ‚A²Íf™×¾‚ÉíF à/7oo‹†´¯R÷®µ$U-»j]ò¨ 4õqˆúo¶rˆíb=jÛä•EÜÙƒÈ`T‚ˆù<ÓÍׂnP¢rÑä_̉ðÉ©éá‚ï-QÒµï †^Ã}³ý”ޏ%L€|»ÒŸï,”ºUŸ+1^"ðò¬Yç#"W!aúÖKÎfèÇ”tÜšñ¾Ù¼Ä?_úNÉœaF ù`²ä{€Ä)$éÏþÄ2ÄSêÖô †Ð”G!è®[F QÀ¨wÛeX¤®‹ëÆ~#´EÞåt| Ù"…EuK€ VÁ ™Ï θUŸ+1>"ðÌ´iÅ ú>7¡IÞEỸí„eÏ—÷›_õY `F €`²ä§›¡2ô—_ì‘4!4tƒû+í<Õ‘iš® sí¢ÎÔæ¨å‚IÙGÓ9&gyD¸³LÍP]]Ÿž`¼z,F^ RB…´¨×ÕO!$Ø)_5 Pžn`_µJÝŸ8NŸr™]U‚LñÊ<KŸ¡ŒÅU.c|E 95sºägµr¤¨Ž`L¼‹œÖxÚËnçQ«Œ¦wq›Q£tÌÝF2ÀK  >ú‘¸Û×c˜Í“*t©ñU_1A Óx«åÂÊ$ó3náëL¹=#À´%ªVÙmiÖþœ«Å ö¢"£"!ä¾;!oÃf«µÞˆ˜ßsѼL›lgÎAñÜÁºÇ,KÑ{z€XÅi#ÿõ<È…E`ým/èG\¤4/~!Ê—­Rò&ÔÂÝv³’/ÿ~5/xYÉK=ºAÄ?æ*y’_ðhš’§?Æ £!øþ{ &¹²=E8Ûáªõ^ÈcÉ`¸â¥mÞ½@È÷‚~Ø °ìÚ …Óžvʬ™ÑõëaY© Q¢kÚ1׬‹aiÁz¨ªO)&„0~YY '.ðô±˜a—íYZbd;0 ‰<©êÿM{Ýí¼ Øu‘q›pÓÙ“î¶ìðá wës=FÀŸ˜Íøܾ{çÐ$µêÇÁ‹N0],XKJ2Ï»&Ç<ãˆ"¸ #À0…€«‚€šK³ F¶Ù ôÍ÷•¾ÅŽÀ4±:r5¨°§Ÿ¬E”¨ž„þHaóRòAçŠ[”†(Q™Bœ°¬â—-Ê)ý!"¢&]ïjô}{jòUu,Dè*SЀǧÔ"JtYêœá/d€®wwµzµÏÿ†¦rp¡æ«ÕÂËÚAØSO8‰Réï9É]ªÊ©\V÷?é<ÊX몗1^!œ–ñ4.誥ðHþîΈá^ÉÆZ»÷«GD `íüð–wr+FÀäd¤ÍF·Ó‡ñÞ'$\}e«eõý©óý!ÅûqpKF€`ü“%? Xþí`ÛçˆlBòAfiu%ã˜+”@t46…O?¤¡)ýðs¥‰¢uš|—’¯Ø¼.ü)¬Ûw+çô'ÿáYwßã`Ù¨%K½×u½º9óDÞTŒÔ¯Š8©dIŒÓíõQVüê›Jy{*~Þ ”“É\ðßãq ®Ìè‡{î¨øiX×ýZó²£=š†Í™îĤì“/¡ôŽˆì,.lt0˜Ã\»]ž­íH ‚ Ácðí€N[ìy‰’Ùn£qÊÝÆ¸¨<„±Pž0„»m¸#Иdg¤½$ˆÂŸ1Tŧ~dèd•-?N1g\ê“nÌ0Œ@3#°d ø’|Í¥$EÍÒÌhÕÕ=®xг/†Eôé úËmuÕÃÕ£×JÞZ$öSg¡ôõwÁ~ұƢàJt=$0r~!ȳ>¹°X1ã6¶ã•õ1\·`2ر=!!H`òœ×t}z(ý©Ú'{^ÓÌÏxýx§¶§bÓv(_úÒ£øÅ×Ða×±~#¿*SÍDæ|ù“ÿ®˜ê•~¸´æeôz›ýw:96ô¬Xñ”¼öN­z­¥À^©a£gV}~Õ¹Ñ9§æE5J 1˜ÃLí(„0)D©n:mý:ó‚hC3¾_ÄðÓuÖ©}!0ò¹»PVû—0͇@NzÊ»²$Lį­_FÜ›V$ÏμÚ9Ü–`æD  È’«¥Nr ±\çãb¦‘Q¶îØ •æb¦k¯BrÑÙeR¼ƒ8ÐE㸑2ý!ç¡m ëß[{ê2oQMñp‘.!)RÍåȟɶ{ŸÒ†‚6PP2©£dÕh¤¤.Ž2¥É’6‘_”mÿ!g‘®k'g^ÍT¬ZriÝë<ý¥Ã0ðC?µ:”}öµ3ß3…& —Ž)Øà£†¢5‚ÓÌsBÒ ¨QzB; !(5Jc‘ԵŞçÁ&E·Û …„»ÅN€rtÊàÈwžÃÍ-š׿¤|%Ò|õsÁ·å»Ýþù”´ÌI¾ÉáÖŒ#À4E–\A`Щd)ð %¨’Ë+žàä¿T:UŸ•í,0ŒÆ1—;2›SSÍ` j¹öÓ¾Ø-½@IDATº±Ê÷ˆ4GR¯ÊeÛoûÀâ$K½”(wj»Š ›Ô,ˆq1μu¿ÃŒÐY€k¥i!•iëªulGŽ«Yן5´)Á“ïv]¯•”˜‚•™„øi©VK³NC–eÿŒ¥G´MH”†ŒÁ`Ò{žG¢¤‹î°^ ;çncü·°£;ý‚ý„*ûZws=F  ÈNŸµFÔ £18ä Ÿº•Á€UþK›?û$‡3Œ#Ð <1Tš6•||ûÛàÚ1š]ÙÇ_@Пþz %.×êÕvþ‚È"æÎ^€×]»*ØN4lÍcÙþF㽃9ôrh6¢„A(IäAG×(a”9ËÆmŽ<âx5O”È\Ϻ­zÔbÕ„®ÛÏÖ^ Êåuk•¨ ¥rÔ>†R|§(¼¸aÜ(¨Xù“ãb+û›ªÌ($C¤W†žV?[ÙT[Ät¢”š¹M’´&’‘ˆ’ÑAnµ×<Ê‹‚UB¢$˜Bs=i‡ÿñ// ¬÷¤ ×eš Åæ”íSÌY#íVë2´ÌnØä¡Žb[zóùJRjflNFJzÕ¸˜`€C 5Kê“>£ÂàDd4Ø| éÛð—aðûyÇÚ‰ü‡j&ûñ“Ž"ÜsÈŽþH â ö<ôO•”r”5LD(jœe»ƒàH¨YÒõî¦"ÛÞ`=xä ‹²'’~ôåJŸÖ{«8mHpýÐAÕ†*D„…W“ƒQxš¬¿í‡â…/Cé»;›ÿíN }”Z[: ¥èÑl4"ÄTÍgÉ•yik› ÍÇl–Åä´ÌÕ$J¨‚ ¡Wè+Q«.:~j¨<#JvøÏÂDaY áÅãaêC`±yæ!Qg…ZÑ*Ó„úÔsM–ís0tÿ‹ô2£žj|‰`€A àÈ’º°¤O:ˆ,™ÐÏ‚‘ªŽGW™ ‚5B¦äõ÷j”VªQæ¨$tÚTe3WŠXg¼f D¼˜áY)ž1̓ U49}¿Þ `ø,5©~K´Ï‘3Û!$I¤UBÍ•­Ò´N×ÍáoTñKõß¹ò¯V8ê¢0ý°ÊþLdnGÁBþ~¿Ú’¹í(÷¨óÜÝŒýj£P›UöÅr°8¥4£#!èÎ[ÝÑbêíiïÀ¸]„ ÈÏ]Œ¨îøl1“h%]²d‰tšùo\ŒÝ£’€DÉ85J†ªÿíu·ó¢`‘bã× ¦à n·ÁŠøH|õÌ@¡î/O„q]F ‰Xlžvƨ‹‹ßm«|í_b<ŒWÞIÊÎlgd_'ÊíF U pd‰P¥…¦ƒ,9ò±áMÄžvU }23³¢ß«TŽÄ4I”ˆ”„/x ¢ÞÏG“s:*/yû°Ÿ«zam;x˜Š•2ýAˆ|ãEõÍê¶:ó”± 6GMÖJ¿%õܲ¾ÊljÊ(ìwÙ»9.ãÂ>èÞ?Aä›/AÄ+óÀpñ¥œ8”¼öGoÿ"q+ù×»ÎÖ´•Ô9ÞyÞÒ36ÄnWYJˆÆ`’TI”„ŸžgNMƒ¥å[w¿ƒD©šƒF©sh”|$J‚(Vè¢Ö¢f*Ï“¡Âxmp",ö¤ ×e —ÌÄë:]‡ßiŸú:6´ ¸Žœý,Éœí£=¬¯#áöŒ#ÀÔ@À‘%•(Ñ[y:háÙ9Æaζµs7(1¾ïA^RJÜüEsžuì5¤ Nõ(]ÑÜ¡lIõ}ˆÊ¿^ÖC®ÍàlGŽ¡?Ñyg7j<*Ð6"_¤uª™h§¢¬«™ç©uh拓‡f–¬©×<ý´¬ù¥Ê' Mƒ§üÕS[sçP Œ¤cdˆòÌ:“à$M;øV40³y¥îÛ­»ßÃEØÚi‰Áá`òC0"Jb i”‚òµòÝÈï •a¡÷ar£.Wa³ùž² ƒûÞ†¯‚^÷u ¨aº¬g¿š•å«,nÏ0Œ@c!Pë7F«Y€‹i×\6nqcõ[§\›ÍåQ®¸¤ ŠàB^>ä¢dù–£PPf…Kí«vUצÔ),À/ïŽÔµ3išíèqÇÞHh¶VW"3;! ‰#’,­æ©®ú•1oîÙDÚ$2»³_ðèå¹Gݵ–ÊÒ>gÌuP‚Húw …Ý;@tLDFD@x8F\Ãý¯ŒˆS§ÿ|µæØ¿îî„/ æd¤=ÙÔý7efóà ëî÷ñ»ë÷Ú~EÚGiÈX îÐNk¯y”¥r%£©Ð“vø{$X†éæB‘'íÚb]üíùïßwçŽÏõ*|®Çº[Ÿëùu½à«d$^Û zýµÿ0O÷-ꞯáöŒ#À¸@ éWp.¡-"ó;U£¤ÓI ¨]êÙΡ©ßÔ¥œŽh/¡ˆ”ОHäÇd;Š¿õ%ˆ¢ëÙOñ?Q"áØ·íØIe,–MÛ˜(&n¤å‰C¢¬¡K\èp#^=ôÜjÍñÜÅU¼Dà¡E‹ŒÇ­»?rM”ÐGÉg¢$–ëbã×xJ”0Ðå¹` žb¢äååf½„Ñç1¨aXa±¬¾ßœÕ+à'Íd6‡@€“% ‰M‹O vÐ1*bpåaÅ7ô¿\‰<ÖæîO8 ØÐ­lïÔMñ±ëƒdÞhЃ=šÒs+á³Ú¥€©‘c6¿a*?Sð)“›´]‰¡Q`¢`>n8+HR™ÛégÜÉ3Í y‚)æ¾Bí˜ûÚržhá`ð…¢÷¢‡±Í—© aêfµZWOIËæ‹nË0Œ€¿8²D$í-4u¸èDÕ¼bÆD¦Lý;†ad<Úü󳡗a„<Éßx°6¼çE=óGíp;»lý~rÚ\·Í1ëÇ—F€ð G–fxH–éq³UzSOD‰¢!Á&è ø Ç´ƒwGŒƒ"ô áÄ4%ë{ô…O‡_2’ Ø ºb’ ô?3"Q¢ç”žY"ú¤Ub¢Ô8w扅 CŠÏ—|Di‚¶)ªj”p%ù‘X’J1<8%“GD ¿šJ$fgökÇÅyF µ#='õ3$Š”WàË\‘0…ƒlû:yvÆD_äp[F€`ü…@À‘%š˜¢Y"²„ N#.>ÉIÞ„‹Ñ`Ü_(:<úÅâbG~*"Þ¾|¼BœüËaêB€‚8|>äXÙwZ}!QBžÞ;.H!JÁAAÊóiÂgU5Å“”ý–X«TžÞ–ßo~94?¿ì+4Û§•¡‹MÓ +ñûÕϾ$IW"ÅvþMøJ<#C9èÀv9ä;BŒûÁ8° "¬ïÞ£Þ];â»âb\†|ÔzÆAHÁÁ„Ï'&Ò‚ôîàDÏ¿™‡Ì‹Â­ÖÜeÈV¯ÔJ–Úw】ø¦Å·¯44½+ÖÅuúYÔëKµòÝÈ[DÒôv¹Q—«0­Åé37áo÷H4@>äÛ$eÉ.ÃëI©™Ó|“íF€ð ßV¾õ]ok5"i—²„ QZ˜†bøì`ˆ ‚D$L1F;¾ãØÛ!rF_K.¹¶'t é̉ð£Q±°lÀpxyüMèŸ4Ÿ)|%;ô !Ò¤<‡¡¡ø<†+yÅÉ™²VÉä]·}Ôü|d™¥à[4Ó¹\[Cß LýF`‘Z<®HŠë´FÐé=õ»°¡"EÝómÜš`ÚM–èF¨oè‰0Q"¥’(ò ‘ÐDþI¥Æ2(++‡ŠŠ °X¬`B³;­É‘-:´©æ¹öç[7ô ÕLµž-ÔZªÌ’‰ê;GÁÚ$Š€G%G<"í¤ét%»f_|Þ0÷›t°XˈŒT©0€ƒ1qèâº4, è›”>JkqãYψ~•àãóÒ‚D´]œF r’“-ø{{wrjæyü÷y¸êõ^ÆŸñ1åÖüïñEÊu‹ÍÓ|‹ºWoO|‘`O–h˜´ø$bPE˜Z%Ú‡ÉñÆßXj„r$KåD˜, arø8ÙZ) «C‰ ’ÿ©@Q‚®˜w*˪}¾h% â@QïèS è€Ïy&J5õñôAóüø kù T÷UE¡™Ž)²½Zäõ§ ×çéâ:¯QòÌ™‘ˆ é]µ]œFÀ]ð;–~„Áèvç0hÃwÛ¹ª‡„i˜ÍR¾zŠ9ëêÅæ™‡\Õá2F€`ü…@‹!KDŽª´U&xŠù.Vé­yyå`©$KVE»dC?'‡V‰ˆ“%=:­KŽöÙ"bNaëuä‡Ï™ß©#Ó>Jô¬Ñ¹ªuR4œ¬QòÛqJFçr‹…4JN;;A¸ bH”ÏýzÃÔ(­ó†(ájï……ØôÎç›ÀÚ,9)éÉigñ·øeüI¦· ^&¹—ÍjE”yíbsŠÏQ÷¼7c6€@‹ KtÔŬ#¯sh›”풩¨h”­’µºf‰ÌñT¢Äd© <Õ^LQ}¾ˆø¨ZK‡/œ÷MÂ÷û"-¦cÃY½â#GÚ'µ>µçä;¤dt­`%þÃvW¥ A¡4h Ч¯I0sÑGiÞ/"jáíµãÖoÏÏK¾÷u Üžhëd§§.ž’–™‹¯2ßFí±Ák¡’$„ãp| ‘Í4î– ìV«½#b‚(áŽ"ƒ#ƒ~zþñÇ=Ý;±ÚlZ YRG­Õ09« OR±V ê@þJôII5Ãsj–ð[™É’Š$ª¨DGDâ<ôŒ©fxІ‰´Ld’‡¾qª¹J’¨.'ÿ 0՜գÂjCÓ;¹«*Q ÓàÑ@š%Ÿ“Þx^ŠMXï QBÒsH”Vù<À0 ‹ÓS–$Ïμ Ûí£†Éë7!ø«…ßÛßNžyÛksR¾ tx2ÏïTn­˜"ŠÒõy…–¡¸&¡À«Žd'K˜@Ÿ|”©J÷›âܒФ”ŒÕ¸¼û\ÒGå¼b~ ÈÓ´8²D¤Å--fÁŽ [ZÀêœÄÈf@’„_8D”¡Æ‰5Kž>m§¾J–è“"@"ªT2.U’':§r-ao;(5îLï7gõ²X­¨Q‚NjORT{0 ÅE-òú5JçÐGi½ò¥á‰ ‰*¨gŸí/üàI3®Ë0 #='åÛ)æŒñv |‰¿Ô1 ·p]ÉV°`“?KJËü¿œô”w]×jÞRô¯êf·Úg”[-÷âxõ´F¡Ô>&â¢ÀˆÖ FÜÐÜ€'F€ð îËZ†î8eðíÈ© p>¯´×ã@ë…YhfaX„áÏL›Vìn/-ú¿R}«/↡´ˆ¥·þ¤EÒîÇD$‰Ê(á—±»¸p½6ˆR%\K;ȸƒ09üäTò¤’)•\µAˆeÊSSç÷µX+(˜C¼ÚÔ®+˜/ÅSß5w‚1èŒ.6áoˆö¾paá'u\üÉ0þE`±9uýý©ó¯´@Å2íËO{Áßw=ØáäÔô˜ìŒ´— C”ÇÆëf=h6£½~3$³ù n »¿}.—DI¾uÂÅÂU—Tm× Câ.6½¿t@Ò»3ü{é°é·#&íÿÂҹ°>p|·q©Oz]SI:ür‡Ú8ÐãÚê‡r¿9s(ØÑG©’( ¨Æ1ö¿ÜŸDé$i”<&J”¢EïSL”Zý#È @rÌOœÓ颯Âog|‰â[½œ¦·fü×l^¢˜¾ù&Í³ÖørV@¢ô’¶ËC‚ŒðøÝ×2Qò B®Í4ä˜|ëUð»1 üŸœ:÷–ú:l3d©>ø#Рì@œW²9ãb«E^átèÖá>VƒÇ‚.®‹†k :DéW|!à‘F Á*BU{*šÞmóÏ@X #ÀxŠEªŠ×÷¹]H?ô´m­ú2L:nÙóù †ÔºÖˆÉisg#QºÝä)·ƒžÚ5bo,š`¼Aà†QC`ˆØTÆHãöw’Ì™Ãë’S‹,a¨„ T¹¸¬¢®6\Î0- ùE•dI<(ÃMJ›;B¶Àr$JÊî²¢ ÷P>¤§%žOCL!Çô±<&Jh*“§˜‘•(ìñiܘ`|FÀlžT‘ K½5L9> ùêü¼òÌëu´=OÆ@[  ót µ¹ûÆ+„Þ]:xÒœë2Œ@"pëøK`HŸÎ¸ƒd‹üj]/™k‘%4A9Jã¼PPÜ„Ãå®FÀßœ8—¯lN„n±»=nyÈ6ràŽ ¹Šá1`ºhˆAa~™ºrT¿ÙSa¨:+H0=³¿pØÓ¶\Ÿ`ò5ÊÉLMDa®ï=È—V”Ø~¤=Ž|—U¿Œz—‰ßqú½à²A=ë¯ÌWF Y ?¦»oA&#Z¢È—&Ïžû'WªE–pé#T17ßã=›\Éç2F€hl¸³a~a©BJt¢®ÙÍÊ’S2F£UÜ7hšB»Ó£É]':Ñ/èA!G¤˜ø- “á„d€é ú '#À4H˜^ÀÞþŠo1º¸÷ ÿç»Ø â'ò›ô^JÝ-­6ßJËÂE‰Ý„„vŠ¥qÝ•ù #À ©2"<­ð ‹|üÜ„š«E–§¦Ç/¤M6»MزW±È«Ù†ÏF ÀX»uŸ.ßp.iΡ&Ïμlòø,£Ç¶±÷E`è1ÔoCƒÃê¢;xnf(ÀoÈÜfš‡ y~ bFCàµÌÔ·q÷ùߣS©/àwQ¬l•WâwÓx_ä¸j‹+­©|X¿®®.s#À(:Ü/äâ~]í’`“o«9ÌZd‰*²ð1}núMø NŒ@KB ¼Â wVLVdA|³¹Æ>yvæõv»ýä8 ’‚] ºø^~j”HÑvx*y¶„˜ Í<@(ò´-×gæC gNÊ¢(^ï]|zÉ„)¿›¾ÄpÁµEÞÎ®è%>@DÇïþ=â½ÃíF ™Ö¯›ÚóD³yeµ}h]’%IÐD-¶ï;.ŸÉ-Tó'#À´¾[¿,6«„o`w¾–>sCs 9yvÆÍ`·Š‹“`‚ aWÝÑoCCÂ÷¡Fi§Ì+…§ÍÝ…2/ÚrF€hf§ÏZ­Ó‰cðûí¤OC‘Á ƒíýä´Ì$ŸäT6¶â0Êvé AFƒ?D² F€hBzvjÁAF|—"Çœµ­«æÛè’,½’1}šâ}J¦x¯ø¥ ‡Ê]1Œ€/äb˯ÞªîXoöE–·m§ÌÎøƒlƒ(ÄÐ(ºèjCüg¿/…Fì•¢ÚÿæéøP£´tA",̹X°xÚ–ë3Œ@à ðª9e«AG¢™±âcííÈpU$¢†);)%c–·2Ôvè—MùˆP%©ZÌŸŒ#ÐB Èxq‘aŠ)^…`«¶ñ£K²DóÒë¤éäL¹y÷aØsøT ™*“hÛ,Y¶*,6a†Ýý ©ÑÀEÇívy …Î¥ˆw¦áWi–ü•„ÐÈÝbd»Ý˓᭄ŠRåq[nÀ0‡À?2Ò H˜`‹¯ƒÃï«Lüîz¾®=VÜ‘ß,Ê>NÁ&ÿDøt§O®Ã0þE :"X(Êr5ÇÃj6yÚ._6ÏÚƒ_‹1²Ëƒ¯}úƒ<óž…èðmÎ3Œ@!ðù›aóî#hÎ/XøPS Ãÿ UZo£FI2tíúnƒü:14ò7)2nŸGBe°ãk¢EH”¾ó¨Wf€GàÅ””ÓIóæЬÿC¢3Ú—#azMòbÐWá^³yœÇQ÷°GÑ›˜ À7Áû×§?À†œp¿4þxõç¹6óñw¿À²µŽ:Fƒ^œv—ö2çÕ[öÂGË«¬°žû»Ë­z<Âêôùxj±>@ig4èàéä[ 2ܱ˜× #?åG¾ã,ºuÂ%põˆÎóÆÌlß°ÂÙÅãw_=ü³É¼Shf¢Â<¿OªíÉV§fI›>n:®¼6•¯,YtC81Œ@à!°vÛ~øü‡ÍÊÀPy25Û<˯'†‹Œ¿ Ê; ’1ñ2ÿ¥°¨%Ê1:y:%¯o+7dœ3òãu ×âK¢¥¾HwŸ°¬þô±çžóXŽDIyù, õ/«|£?Úã<«‰YùËopê|~µ2õD[W›W¯{ó¹açA˜¾h‰óhékK«Õ%eåÎÃLj¶Aò^­ˆ0úde!«v±Æ‰¿îS ±.O©/«Íæ<š²o—ò±¢âU¦jo=êý¯Î1'—H&a"¦ÇN_€gßù ò JTAüÉ0€Àòu;àÍ¥«•oV4IÉÌÎHûWSkrZæ½²lô&Ñ4t<èÚuõk÷bxô)"¶ê5¨{Ò uHÅÍfÝûuqO&×bDÀl¾§,^?ò4³õ9ú'.Ro,Î-Yö¨ùùÈœj£ M§áÃåëE¶+¡Ü4½Äy “«j\VuÛÀ¡gk”òiS P/Y¢(û.éÄ›qvæÈÉóùúRØwôtSŒû`z¨°XÌ)>\¾¬ µ™°8'#-µž&~¿””šžŒ»ßþS‰Mï)LñqöO?¨"Ãc¶Iá1=ˆßUgñE×ô¬Á‚ÇA <é‡ë2Œ@à @¦sÙé)÷àÿÿ³¾Ž ߎ*¶¯JÊÌô_O_ÕÈí1ú1ìümøëki?7åÌ“Ò2•íòóú.ýÀÐ}°»Æ/)"f †?æ¡à!2¤›ûóf³âÆÕV…F—”–y¿£(H•Ó ÁÓI"áê²°:9mÞµÙé3|ŽºçiÿMQ?"4Xñ7).-‡“çòà‡_ƒ±'ºÝõŽýÇà­/~VLêj6êÿéj…`Ð5Zh?ûö×5«ÁÂ7¿Tʸ}<¼úAU,Ç×Vžx£½j-›&޽¨Q|ã?Baq™ÒæÎë/WHÏ'+6*ç$¿!²DA—²?ZQEâ$=ù×ë•öžü‰‹‚g/(M¨ÿ¡¸îÖëÜ^ƒ·ó§‰ e¸ò‹ª»áôéÚF¯¶Q­)yò|Ôj@î#ƒ~þñÇKñ㾩æÌE6‹<÷aºIÐA,Ÿ˜y8î1Œa8|fÝgJ DÀ‚N£ùÅ%¨=*÷`ø…Mÿ~Ž„ÎÌÇQ›”¯ù/o"7©r¼ùLJÍœ†mµIþöO’‘(é"â6cä;ìAð=èOÁ2¡‡ï¸ZYÖUO- 6)õŽ£æ‡ÒãgÔK Ù8rªºÁÀA|8¤OçZu©`P¯ª`fÿûþWørõV§,W™oÖl‡£¨#rWW¢mt~Áàî&ò/"|TmWXˆ ½ójÜ‹«vD»†dŽÑ¾À{CÚ!:–­Ý7ŽÒP3åº/óßì <÷Î×`³9´kÚ õ^iËÕ¼¿ŸUns|zD–ÔÒ†p˜¿)Ùœq1n>y+ØáwhԿ惬ÖçOF€ð$Hùhs÷µˆ›«Å\ùòÂwÁH@ó–Y‚Á˜i0Ê¿þI8"JRDÜ&$J'<rFøx~ø7ï¡ä j\—hý,ž“úIÒìÌëÁŸá7Œ×>H¶"¬²mYòìŒIÙsR}ŽºhÈAíÀw+š Ò0}ñã %~i½Ã¤èlbXq5¹º|pO4Ïê¿þv~ÝuHÑXQ ‡w¿ZÓþz$ väÛ'À.4m[±a—Ú&ß2 z´ŽP¬”Ôøq$/«UÑ œ:Ÿ§˜ñ9aæ0’•,‘å…š:ÄF8ÍÜHãDDHMdêG$£sûh8‹ßü¼Î^(P.“ÉÝF÷E‰ÝÔêÕ>µDÉ —Àd¨0­ZÝóùEð2jºhü”Èòêá;®¸¨ðjõÜ=!|n1üó“UJ“o~Þ£†ô†ˆ°ú‰—¯óÿtå¯Õˆ…ïß3NžÍƒ-héBÏ‹«äÍóáJN ”yE–ÔÁg›Sé?…Ž™¦¦w·ôE†ö¸hé ìf'j]þdÏÀÿ£Rt4>%€tÿ“ŽÇK—olj-’vÄ“S2f 1O›ŒAïç]êÁ®‹D¢qRÛg½yÜC ¯¿Š¡Á¿^PoE¾È0mœ9)+§¦e³Úm_!aò~LhÖ÷1¾0ºÍüÞlMxâo ’£KàÅw—)ÓúC‰¹¨´‹®{a¿ Íõˆ©é¾?Œ†þèŽA‰LÄ¢pÿÍšmÊ9i'ˆQ OA s®¸ø¦=œ( À¼º¯ùçA?¥žÚ¡ _•V‰õMš%5iµPƒzVi•–®ÂÅ>Ê¡„Ó„¿ÿùz§y^_èÃûv…´W?v.ú‰ÔE–H™žÝyÝåÐM Iž«Dþ=/½÷­Ó|ü¶¦þq<úpùéâþÝî ™"’ÿ×'+7ÂÿýîJWCp–ù2êgï‘SNYtïîŸ4çí˜8™mfþs©BŠ•*3Þ>5åʹOdI; ÚMÏÝ×Ojsž`trÎÐuè‘bè3Ç)úw¬H”¤ÈvÅp÷Ãl P* 0^¢à0÷ïˆX#À´"^MŸ¹ñóÜQVû2´‰êêíÔМŠÖLoàÜ1Ù™©Ïy+'Û%vW´4[öUÈÅJüIê*sPúR‰’Z6jXo'Y¢2 {ÝÕ£ ¤E!2@‰üeˆ,A'J¸¡Žuõæ=p¨²Œö9"-”šjLðö`@5Q¹Ö‰ÊƒÑedô°>ðjj(©£-r\mþJftÝ1¡A_¡Åèßsê\¾"þÜ;q4ôE’å4 5~s1*5%Ú_‘üº:ÆFÖ)Ú—ùkµuÔÁˆA=D‰Î©ßq÷ƒoqû’š©±žšý4Õ¹ßÈRS ˜ûa¦E`òSóºyBßÓÿ ¢MŠŠÛ(‡W§7ÜK®QsF?_Î4Œ×`Dàeó¬=S22FÚÊàÜÖg€÷ Èê)žÅ 7q9é©3½—x-o›p lß\1»Ú¶÷ì:X·Eôô£UÓ94g»?«~e™ƒ¹“$I„~Ý:*‘í¨þAÅoi€S³Df`Ýãc²DQÖNŸ/€¢Ò2§öˆ4T½0R%Š@§ JЩkÍN<šäiÓI4ùsE–zvjß Q"9äË£M¥þs¥íÒ1F¢ùÝê-{•.>øv}~V¾Îÿ|^‘v. ÇUj¬çÃU_MQæçWÄM1dîƒ`š )Y/¾l8º‘ˆ’€D©Ý/ž%|³{H'ÀãL”šê à~Öƒíª Yk}šäÍ@“¼×–,Yâu´=_Çàïöd&7þ’þN±JÜ®F'p–:2yEŽÞj1ml[óP¯Ñ§µÒwG[VW¾x祃ÇÏ¡2PÆà ¹JY·„vÐ “š¢ßÒAÔ>©‰4dpŒGÒ&Ò"¹J!èǤM•Ñï´e” vݾf½šçŸ¢¹;¥‰c‡;ÍÉÄQëK¥íÃ×ùçT‘%š{Xˆ#ò ¶º|¦óùÐößTyÖ,5ÒÜ#иÿ…¿­ï5ìÏ‚¡ö¤ÏSˆ(µß ‡U7 ­¤7…”Â<óÅB•¡|mø2#À0Zž7?žûÄÂ…òóÊ?B¦kµ×<Ícà‡û–oÝýТEw–Ÿ©2»òTN Õ¿ƒ¬Ù¶Oñ·¡PÕ…%Õ ‡:Ö˜ðPÅ\Î)¨ÃŒÖV_Ѝ ñ]_õù-©éF~ûíÐI§Yi–âã"¦z‡Ño©@)vf菱ÈůHå{+U>}æã~…Úª=uæi!widžö颋 úz|é›`Ò5#ÜiÞ` —N÷é“¿(u?ý~“Ë6¾Î?4¸ê·¿¨¤\‰ÂG{DiSMS=õZc>jMùÉd©)Ñæ¾€¾Å|ãóÏtñ½o*ßÐùuØ¢`•¢:lƒB«n…Pw§ïñ—&¿îVæV×=òuþí¢Ãª …Ì /C¿%mÚuÀµ©fc>Úþ›*ÏfxM…4÷ôÌ+eÝÃþüƒ¾S߯$Jë=!Jè_ðF¼û¥ðñ‚@Nr²%^—z— ˆ/û:dÔ0C¢äéòu þl?rHŸ#·uÒøùP¨ï_w®6 >í…÷…ä®+•U`,å‰"ä©i+†¨¦DD‰%ÕïÈ©sŠÆƒÊhL5͆÷«ŠçA›âÒæ¬ÚDf~ë¶ïw‘–Ì×}B‡õë¤]ºåª‹rÉ”ðýoÖ9Ï}Í·M¨’_—<_æO5´i úIQ0 5ÑvA{1bž«äÏçÕü¦.cÍRS#Îý1ŠÀŒ娼‚í«p¯£A2DQ°ˆÑÖ‹¦Ðª°Eõt„øûòÂÂÂõTãKŒ#Àx…€ÙL±àAÜhû¬,ÛÍ^ ©l„ßU|iHmÉÚŒ¢®=ûö×u‹"Èý€áÃÕ¨o¯ö’ŽèÙ¹ìE ÄNÔ8¨!»Él¬?ú©ÉP&\=ÿpù/J”:ÒZ¨¶Ò^>+Ù¥T!¢A‰LðÔÔ=!VÉ’Ÿ”š´Ѫe×?á"¿¤r? 7—þ›wq쳄‘ï6"ÉÓn¸z+îeä¯4<±«ÉOÕÀ‘&Œ6j½d@¿t1Þ÷Å`»ÑL±®äËü)2á@4kܾϱGün¼¯éÿü pÄCšgnÛw¬yÒŽÁ—çC+'Pò¬Y ”;Áã`š™¿–÷/¯8ø èF”¤˜øu’›D þ.`hð™L”šñ¡à®6‚@NFÊÓHăÈ'D€4,õí7¤ÓIð7_éŒgµÙ`Ëž#ð1nTK‹h•(Ñ&°ûý O5%âb[Ý«‡Êˆ@|²b#äæW¹£R¨mÒÎhwPS÷ø*⤖iC†«e´Y¿}œ)*§q~þãf%œ9[MÚ¼FÚóg¢ý«´é£ï6mØê¯D¤V‹eM¹¾Îÿ¶ —V lAÑWmüMñÇ¢ðîu?_žšs„óêOb ŒˆÇÀ0MŠÀ´mòMå§—É6«^wÕ=ú=Uè¢ÖŠÆà¼—\ŸÊ°_/ÀcY‰Â׸”`ÿ"“‘ö²,ˆw Ô¶ óoW-FÚ-¨e¡Eo]‰ˆÅÓS~ZS/m]ÚÀuö䉵BNSd5’MþQu%Úk©7j6´©‡†È¹6¼7EºÓjž´íHÃ3óÞ›ëô¢(oÞq5Ü4z¨¶™_òÝÔi E^a1|ùÓf¿È&! í¢àJÔòÕ—|™?ùoÍúÛï +úˆi“( pÃÈÁps=˜yû|hû ”¼{¡=e´<F€ðí•Aö-çOü].+íè7ÁAèP!ÆÆ#Q ªÚ”Cs½f­-~=Ï›Ç e5¯ñyëF7>þïÿwg‰Z€U¸Àën}®Ç¸ƒÀ”Ù™×ÚlòG)¯zØ/wck.¨7«·šjQî8FÏ£½yâ¢Â”E|h°©Þù‘ ™sc€I¤` !µ´Iõ ðâ"í»tâlœÆ}¢¢ÂBpœ‘@›ê¶•äËüÏc  ¨Žä’ˆZw“7χ»²ýY4£ËÖnGm,Äß—'UÙîÏTmÁŸŒ#Ð☶Cî TØS¬çNÜ&——6Ž­½(–K1 k£É­Ýq¡üþÂþðaƾãÄ0Œ@3 °xNÊ7SÍp[ /0¸LõÝJ›a<-¥KòK¢Ã“DÚ oÚyÒGͺäEíÇÔ“/óARI‡7©©ï³7c¬¯ ›áÕ‡_cZ!3vÉaä¦E–sÇÿho$¢$HR™.6a›DÉ<óÌá´½f¢Ô Ÿ9ž#Ð’xÕœºVÔIW¢IžÃ³½% žÇÊ0~G€É’ß!eŒ@`"`–eñÉò]húð´õܱ±ryYuƒp? ›ˆ’ƒDÉ`*jP¤ y:f,LV5X—+0Œ#ÐDd›gíDš‘ì;ÙDs7Œ@À"Àd)`o ŒðÓ÷ËÅ»àiÜäNËÙc#䊲ڡ„üÑ$•J± ?‹cqCâÐ&ø@ˆŽ94„_gæAàåÌÔæqj¾76ϸWF€˜,Â]à10ˆÀ“{äD{9,’íòE–3G‰(Uk㯾%]‰>¶óÏ‚ÞXÿµ.Ù2¬ >ÓÍ}…suUárF€`š—fÍ:f‡&y+›{,Ü?#À4~ ð0ÕœÕC¶Êýe°ÅÉ Æ‚ G£_DÝ1'›g¾Ü+#Ðb@ÿRü:gijh r2Aê¸Îl¾Ç£(qOî”/[àÔ(ém玎Ky£8, ’®˜¢Þ^_ÚÀ¢ïÏëˉ¯3Œ@` °`úô‡-º¾üLþ»ˆæ–À‚`š ŸÈRRJª§eúâ¸Áj±ö­4îëÆnÚUppŽðu×rû$ž°/œš¾cZ~!„èþ“3cF~]bÍ'äàâ ð(þ^.Ë6Lï,QuÕ÷©‰’×i Ó×Oäd(Çï…çç÷VÏ÷©CnÌ0Œ@Ó"ðÒ×/Y²dÒò-{ã˧ûš¶wî`š¯È†Õ¼Ìf ü¯’"Úi¹sûŒ¿nÂÝ~MŒ»5‹è”À‰`¼CÀbµ){Pâ>gpOˆÜ‚â ü»µM7@¡5crjæÂˆâg¦M«æ„›Ìö,Ƀéhgß7šEÒq"J‘Þ¢V:]šÞ­Á Ë뫉1îN£F)c^¢p¨¾z|`@E`Ò¤I6Û䤴ŒshÖ<#PÇÉãbÿ"àYzȼ(¼ÜRðO«Eþ# ƒvv1°' îÝ úu‹£Á#qþ KcZ9'ÏåÁ¶}Ç`ݶýpüÌ…($Ms òËMšyoΜ”/húÓv"‘’a2j”t Q:{ì2Ùj‰h h®PŒí´¶!¢„}o Ìë'¸µßRcŒ•e2Œ#à/rÒSg&§dœEÿ3hFÃo…ý,Ëa·ÙÍÔÔù}‘(}ŠÚ¤~¢(Ê#‡ôn5"Ãtj<,F u!Ð16è â7ì8KØ„§Âv`“—&™³æFüq…ê‰%@¢d°"Q«%¼1P@“»4½[‹¾JõÉÇ¡|zp;üûƒI½‘åÄ0Œ@«@ ;3õ9tE8ß·ÿD?&·×R­bò< F !àV4¼©is¯´AÅz"JÑ¡0󞛄»n¸‚‰R{XxºƒÀ%zÀSÉ€«.IÄAÉ‚l±¦-ûaÕJD”lg^ÞˆD)_ñQª‡(!Iªmðì‚þ¿˜(ÎsÃ#aÿ!“™ú¦ · aªß_Ó]²$F€h$KÉ© z[eÛ§øæ$¼o·Ž0ëÞ›¡s‡F ¨Õ Óç.–‹€„N@“®÷N¼ƒÐédë™Ã‹~øh¨õÌÑ+d«5¬1f&è y•%K]òÑUñ¬(Ãôg ß×U‡ËF€h dÏI]ª¤kðQ½~›­a®<F ­"P/Yšš•e—+>Ç—ÕÑ=;µƒ‡î˜€ÁŒm+ž7#\Š~ƒI·ŒÅHã¢l9ú['ËÁ­¡1P$J"Jø]±]0Àcóûc ,“`@CàÕôY?Ê ü7ÐÆÅãaÿ P/Y²[_@Ÿ>1‘a0õWNâm“ü;Kaü‹À ^àO×À—›hÿvpØ ÏûµÁ`ÌE¢´N%k]‚¾¸¤?¤Îï)ÔÒ¼®¶\Î0Œ@KF@dÜ#À´Fê$KIæÌáè q7¾®–“o«„oðœÖ‚Àèá}aäÞÊtÊ÷üŠï9ü43ƒñ¼Û¹>¢dA–¶hA?añ$9ø uÃ0Œ#À0€@dI¶ÊÏ’ãøeƒz ]:ÄÀPyŒ#иêb2Á^” ÖSûªÞàuÁ`:§‹MXNÌ.£ÙáþIg%žÄ@ß6(Œ+0Œ#À0Œ#ÐÂpI–¦¦e]„{¸Œ5tòıÃZØ”x¸Œ@ÛE€| 'Žª`9²Ë' (ÕÅuB¢$¹&J›‚ ð(û'ù37fF€`FÀ%Y²íó^…Ȱ>`j"0jX0‘v©¬lùÞ™Ñ Æ 3H”6€ ྋµšÝ- N³¹·PPû*—0Œ#À0Œ#Ð:pI–P«t+MoX¿n­c–< F  !@X.ÐM™±õÔAgŽ¥ÓR¬k¢„$©D'CšÝ½m®ƒHyÜ!7`F€`F @¨E–LMïŽ{*õÕé%y`¯„6‹`êCà²Á½”˶³Çê«Vëš` :‰Qï~Á½’\…‡8 "<–5@XW«!0Œ#À0Œ#Ð ¨E–0.pwšg»ÈpÁ ×µÂ)ó”Ö@÷øX%d›E1ÇsgÆ¢)ø„“ð+EÀ¬YK¾9 O`Ä»5¯ñ9#À0Œ#À0­ZlÈ. )æptxp³Î9¯ Nœ»á!AÐ16$\øqb÷@ÂQQ‘pþ\.ÈÅèVdªß÷P >ŽDiS-é2Xñ?/gþá«Z׸€`F€`VŽ@-²„Ý]Ðg ¢#B›|ê…Å¥°äÛ °cÿq()+wö/‰"tŒ‹„žÚÁïÇ ÇÐÈç5ΧÏÀS‹?vÈhÐÁÓÉ·@¤ â]^a…G¾ã¬{ë„Kàêœç™Yøæ—pø¤cÓÖ~Ý;Àƒ·_ݘÝ9eŸ<›{ž†ƒÇÏB^a ÄE…)Ïõh2g4èõü•iã KöâPÙyþûõZ(.­"Ij}›ÝÇNç*Ço‡NÀ“&@»èpõ2º@`âùáò Î+s¦Ü‚ r·ÛYÃ÷Œ\ÃÍ…Ñ'+{&ŽnP¸Œ½©’Õf«Í Ûfkš~WoÙ o¾ºÚwtœ~½zÜ~͞صÚu_OBQ+KI¶VÔ)J =¢‹é¸µfDe}ˆ Ï›E5¯ñ9#À0Œ#À0m€°mÛì üó“Uµˆ-îu:©Ú½ íEÖŸµáT7ÈGí…zies¤uÛÀ¡Þ…¯nŽñ6FŸë·ï‡w¾¨N”´ýä•ÀkŸ|¯hœ´åƒC×$Jh½gþ½02˜(5ö`ùŒ#À0Œ#è4;Y²Ûex÷«5ÕpêÓ¥¤Þ÷;xቻà¥'ï†ÙI¡o×Î:¥eðŸ/×µ §€À’eë[À(gˆô|“i©ú¬F„+š¶Çï¾Fíæ›iÖÞøß3R…àÐCRtÇm5.åâpS& ¹ òP£.Ÿ2Œ#À0Œ#Ðêh\»,7àûnÃN8~悳fb÷xxàöñÎE$]ˆ‹‚GïºÌÙi–(8{¶ï?ƒzuRÎÕ?´(%S½c(ó+$´‹RŽ~Ýâ]š¢åæÁÉsyŠ ¯ýº9ü;H#²ý§(ÈDÿñ©X(*õÎ^(@íÖY¥84 „aÖknà{&·Ð)—´d$·Âbƒ_v€Sçò!$ØÝ:ÆB$‚ä_3mÛ{ ì•«ìØÈPeÚ:gr P~¾³hpïÎ`±Zú½€2´‰ð0êõŠù¢A_][wýw<§Ü‡ £ºvŒЦæÊ×H+ÓÝüôÑÙ°ã\2 ‡»Mœõr Šaï‘Ó Î§só! 7IŽÇûÙ#!®ÎF•™£§rá ÞÃÓçó°M4$"þîúâ!ß}ø$š~^Pü‹ÚGὊƒÎ¢ª=›5û¬y¾}”ŠJʜœo½:·WÎé¥ÝõŸ6ïQÎé~^ÀùF…׌Á)ÌËŒ~@Ý~§¶9Žc“`„gç÷ª(mÎ3Œ#À0Œ#Ðhv²´f˾j°ÿnÌ0—‹Qâ×]1Þ\ú“³þ4ÅÓ’¥‚¢Rø×g?ÀîC'u1éÒf@IDAT(³q×!å¼]t$Ý2:µVÎÕ?›v…¾ulcB²0ãž›àùÿ|£˜°©uès ¸ ,_·ýªkKB‚Œð·ßFRUµ7ÕÆßÁg+7*"â¢ÂaÊmã`Ñ¿2»Ò& \q/¶©TcñG+À†þ5”Fï w^¹¶_ÒÒUUÌ^šþg \pž}ûëjõè„‚PšqÏÐ->NÉ—•[àýeë`ÍÖê÷@¹ˆþpÕÅpíåÕS?‰ä©¥ôÉŠ0´oÐëÜäh\ÿýzB0]uNc›8ö"ÅêD“0{ðã& QÛRDÅÉ£žÖù¹Iø[_ü\ëþSƒ1ððŸ®v›tF¤&"©*QRˆöíê$KTF¤¶1É’¾_Šn¿KíŸÌð&³**üÉ0Œ#À0Œ€÷W®€)MΠ†FMðí}wÔÔ•.Ǩat¸Jj<óõ¥@õêJ¤q™÷ÆðРo¥ö¨f]røùýï\.”—¯Û¡øU¹"˜bñ‡+aÞÄ`“±¦X(-¯€—Þ[^‹(QEò¿š‡~Xs¦ÞÒd‘þ(hÆ$P*™©5`,ødÅ/ŠVåök.uu¹Á²ñ#úÃ?lÒѱlí¸qÔÛQ…ÿ}ÿ+|¹zk½u¿ùöÞ>Ž«êÿ>Ûwµê]²Š-Û’,רŽK\Ûé½A*$„‡„^þ<ÀÄÐIÞ‡À ”@éqzq÷Þ{‘%K²dõ®íûžsGww´êòj‹öÆ3;åÎï]I÷7çÜs·‚*ôüxQÛï^øªQïÛ$¢þðÒzjþ0ò€=õʆ~×ÊuMmð(rûòW ëÙ¢k²Ðëxmï3“4»ÓÙg—~Säkâ“Nê’3Ëâ_“ÖÿóØLÍa¹×L€ 0&ÀÂE ”ÉžÂõŒáºï@LáªK´Ý7¬b©¹½\.%+ËÆTÇcµW?ÚÓG(Í-·ų§=OUÁG»‰¢) Ú¿ßÛc¢nÂзþw£0¶–Ž.X}ñ HAOÏUVç;‘„R‚Õ —Î/3¦0߰熽)‚Bþ¶ì?¨2ßùrC†bQH଩yB<í9Vé Ñêè²Á›÷¥Ð¾›„Þœ/Üq9-¯u;}øÌ­— ‘•š$Šÿpç±>B‰<Ë/*† ÛŒaa&Hö!†IΙž9Z#Qrëš…"y]ûΖƒ°|îtHJˆ²(µ$„¤YÌF!²òÑ#ØÐŠÇ¶ ƒ$;‚ÏIžÃ3&‹ÏÇ+ëú% \“7ï??y m [6w|óWÿö Ùm(Ç"–¨°…eS`ý®c@cwèáË–xÿ+” òÿZ¾äù"#Aûõ{¯ÁÐÉñ¹r`>†®}ïw/ù²'¾òáŸXzsÓ~qžüï+w_%Æ7Ñg ñ|“(l;xZî³þ…/ !iÿq‹?¬’BSPä½³Uɉ@Þ@l^9V{ C(ž9络¾·Ã IßÉ£ØÐètMºJ^ð Ïý ‡ç+Öø•ô(ÊâS™`L€ Œ…‰#þmohéÀÍÇñ¥nÔáËQ¯WvëÇR*_3Ä2‡dÌž–+æ‹—ÿ,œ'V±dwø½JTš£}ö¾„§]½tN¿Ó/_4ÞFφôd@Ä`bijž?²•åà8)–(”J=î$=4…7uâµ c¿b‘O(Ñgš+Š’>ì;~–>ŠñIM­]¸ßŸHBòMm³%‹¥ñPj#ÊBôÔHQ±÷x%Ü‹·ÑŒ7R—GÏM¢ŒÊ¼lá nƒÙ |Ë$m ?)”ä>„+Q̾…íIF¢…Â0)!EŹFyš_D‰ Ôöq )$¡L‚1Ð(™ƒ4Jæ¡Fû—_4Ý'–è3¥E¿~Å<Ú•‘h|³9RJqiñ˜ìãV#Ts»DqžÎ¶-˜IâÿÓvþGr‡ 2æÂ˜`L`HR$QB*Š0Ù…Qþ>›tõ¨­Æ :p‚ת†,›Ž€¾%u{õà#¸¼FhuO‚nO2œÂ$e´¼»ý¬¹¸ .]@S®íÓWÁ&ü)aK)}Ek‹Q¶3µQ¨[ Ñ -Éê˦W×›ý.ð<úœ€![j£.iɘú9Ð(ƒôŠ%ú¥0‘‡$;] S§”èR,Ñ~Ã5Þbé<¾ÉQÛ_q²Ô¿Ì¤~‚ä"«¥ ' –aøMÌJö<†AŽ3’õ¡±]êy˜Ån Ë HÒQ‹ßŠV;Î/%m ð7‚$„dè¤<—Öê}î÷ùŸ=£>Üo›ÂGk4Fì/®û$¼I_ÂÐIu¶EyìBÖ—Tg–»©ú¦ÿèBÊâk™`L€ Œ†€Iôr’¢2ÞØt5ÒGÊÒ‡Bã^HÓU€IãèMù|îØ XªwO‡Óö¥ÐÑ“¯aäeæýôÍ—bFä4!˜H4±)Â*–EAvP‡2 Í¢N»4‹Ñäehëð‡ÈÑÊz6YU‰ÚqŒÐxØ`ÎdêË9uÔ÷ÍB¯•ÚÔ}õþ`n·á˜,µÑ/¯A4žï4§jl™oç(6nºl>¾Mªb†BØva&¿¬½³o»PûdVǤ6jÏæ¶¾Ï•›1°÷*ÛB-Œd9­}¯Èû$Ï¥µ ½m£1J:A“ϪۘÄò`†¾LOlÃ,~åì¹<&À˜`ƒ p;Z:»íbRöCåJÈy®á”×C’®n+yw(Äi[a²v'êwAk6µ¯ÁD\¿üÇ;ð1k¾tÎtìK{ÙËÔÛaKvG“tJO©ªzìðvš–y#Ƹþóí¾ïÑ'®_&<é* Ò§…DS µªÆѼE¡4ú…A‚„ÛÉ~ýÜ»pÃït6×¼ ЛÄ94/ä@6zúv÷Ü µ®ðÜ;;pL¸Ö,*ší…Ãò‡z‡Ù–Â;”±æä9z3Â}EP Q'—æ7’FsëÌ+.½3'0{zþ%:‰¼V-˜}OÍ—3.¨†zoBc¥j[Ä»êû ˜J-âÌ&=¾•QÆB‘ˆ ´sC„ž«þÈ+Ñs{YªÏ ööšE3€/…¸ –â¼oiI ⺿zü’º>”=GmÔž4וzüK 4š —Bý2âB^/²n›=hLhrcµP¢D&Þv™o¾«êÂû˜`L€ D)”œ8-Æ_0¡ %³¦–Æý•½IܘzÇ=Çì—á²Öb°T~QédLØnÇ«…°A¯[>¯Ï¼D[p\ e,Swh)­ö8÷u²¥Í™ž/:ÇôyfQnOÒk˜M:»ÒH„½ˆ)¡¥·‚öÏ+-”‡ƒºÖ ‘èòï8¨_N2K7¥ŽûìHK£„êñQêíŠÚ&_šq:ÿÐé‘nS^;ÜÚ¦ËCuuxÛ;[ø_P9Ä‹&¶ýÆãÿ ÍM #!tûåÃ'1˜¯jšd7p^«35˜\ÁŸ¼@$”ÈŠ&eúªJßJ-.7eæÌÔ“SÆÄ=G+ûœJéÃ%ZËÉŽûœ4À‡gÖnöy”,˜nþs[ƒó/%AÍÑoQ?(Šw1&À˜ˆHR(QˆúK8Wâ1|aIÉ–Ä=ËB)"[¬¥JMëaª‘¢`þñövLšÕ ú‡Ô¶Ô‡ŠU »g‰:í4iç ïïðµeL£Î0Í»äBW®œSGž@ãî¹æùQŒ÷¸bÉ,_jp ³úñŸÖ Ï€Q¯ƒÃåÕ ÎtF^'+j#¯Å>þO˜ó,Ù1#ÚŒáUwŽ)µµÚ¦ãÜGr¾#š§é'O­õ¦DTÖP_\#ŽçRÛ ïïYå–Ìž*B)C‰R2Ê ÷Ã?¾*z¼l°É½hŒ y.È›@©«÷Ÿ8 4®4JSMs©m ÎÃD)¥¥µ H"!Iáe$f¢èÌfLÎÅ/7*Ÿ^Û‰÷|¿ôÍmʘM4¯4Jxðî¶CÈëP¡tQiŽ«™%O Ê:0…z`¡ÔÆ÷¢¦:iÄëut SºnšV¥îžœ›.?ŠÐ¶U˜š\qÚ‡©Ï?Ä9¬è{‚^µ©y~ï“<ÖzÖ÷߰—"îC÷} ½’OUûæ~¢Ir)c ­‡³“8 0&À˜ÀD& ½JN|ü:ÎwH}¸I†C£ç)ý¢­ÝiÓ|Ë+ètý¦( «¤¤WÔαh!–¨OÍ%³á›÷_ ¥ý– s ¦Ÿþáçn°³KÉ"¾vÏÕpýÊyƒNêºhf|÷?ns lY±¬58ÆêKw^.ÆRQ§\½ºŸŽÃùò]WŠù‚ÔÇh>! åZ:gªzwŸm1oz\(eú`F÷Òø°2±Oòr ò‡?sÓˆ=“§q2^6&À˜`•u ©#MêSÁqâl=Žñð@™éý‰úÈþ¹Lš.(6nÏùöÖÃ`èòR[Õ4QÁ„= O ¶0'¾‡Qò¾Ôà|4Õç›EG&0¥4Ðê1<êëä6õéÉÓ@⣮±Î54ƒ+LÊH×›BÓäu”x€–Áì3èÉúÌ`qÿ/¿~÷Gý‡(óßp>š·ˆ¼CV‹Q„Æ 5ÙkAv<òÙ[“êúf d é)ñ>!èòß à ôLÑDcÂ×…c¸tZJž`UŸ$@Ïš*æý!æ$‹HÌP²ò†ÆHÜ=ùûGt 1Z†2jwú>P¶Äs ­p½_) V¬_ò°sQ{’ ;[×-˜Qo2Î ç‰zàæ•@Ë`FÙ¼m•˜ˆ˜˜4µvâµ ‚ËpßÁÀ2ï¹ö … 0&À˜ÀD$@gêHS´ÌÞãgÅ#æ÷ƒU;pæÙ‰È`">Ó4ã(w,쉡³¦åc:qfÇóý%z¤ó‹(±$aQxÓ4¯CËXŒ äe¥ˆe,×÷5^6kÚ¤Q݆˜P"ƒÑy®h| -ƒÍKEi(¯Þ`׆b?‰LZfŒ`Œº>Äy¬ß!*g8nê{ñ6`L€ 0X#@B‰ÂÝI,90‘” =çð»èÿ&h5.È5FÁ´öãP„’ÉÙ w‘X‰mÐ;쨣H&6:×A$? × 0&À˜`L d”<K8Æ·Ç™·âÄòZpC¦ÞŸ±6d•á‰%2šÆÅ.BñÜ"ä2ÖBñX,ý«Å2&À˜`L`b ³âYr‹ñJ4åY²®'¡uN쇑§KÓV‚ç`ê±»p¸F+ŠbzÝ17n)"Ãðbä;Èɘ`L€ 0¨%@‰È«äÂñá]8>šŒ’„Êf]÷HHŸöÎjØóâÕ}n—Rso|Qì+ßò}h©Ù >ö¡ïœÆ3oÁÉ ßð}NȘ ³®ý»ïsÕÞ' úÀ“¾Ï±¸A™ñ¬šfhõæ@SK'×HÃLÅä]ÒÇÔØ%Kãøí_Љ¦ã=d±Û9ŽH¹h&À˜`L WɆ‡c–º09™QÓ²Ú5y[ˆ%S|X’&CO[…ïÞ)yJ"'¯ÇMgß­®ït ™}ç¶ üì+(Æ7,Z ¯ôä@sG—›æŽÁŒx†7Ž?”â[&ªlnŸq¼=͘`L€ 0q! Å…e‘g©<鵊‡i\nPhs廾=ɹ+|Û´‘2IùÜZ» \ö¶>ǵ`²fƒÑšãÛŸ9·½`ïªóí Ö†V7ø4.ê{ ::¦Õ›ñ…»Êõù¡ØŽC±DÖ†ãÑ(‘µw¬¥gÏR(¾i|&À˜`L€ L rÌuœ©M ™Öº‰KIØt4샄Œyœ·jþMÔAgL€„Ìyb»éÌ›b­þ¯£ñ¤Åç@"z—ÏÔŠCTFwËIÐhýÂÆ`NÙ×þS¯Øõ4ŸýÈ‹5óʧľÊÝ¿€¦Ê÷@gL„¹×?/öÞú}hCFá“}âRŠA£ÓCwó è¨ßU{ .g$d͇éË~nWœÜø_0ùâoaÙ¹°÷¥kD9)ù—Aá‚ÿ‡³)àq;¡«ù(Tìøt6ÇCõ%ì #ALK,Š%ö,…êÛÆ÷aL€ 0&À˜À" „á)áx$žÂa4öˆ,1ëbá…¡íä\œßPƒSã¢Èh>»Žvõ±Žú}â³ ½3'€Á’ í½ûåÉN[ Š˜&ARö"嚌Yâ3íKÌZ¨ìKŸ-ö­YÐÕxô¦$˜yÍ_!>c¶8nï¨kZ)dϸŠ–ýHìÓéÌâsB”¬ú$å,­Fña¤M¾JW?B©H+º€ÆT•]ù!ðD!þσ"X¶·Ê!®BØnÇb)lèùÆL€ 0&À˜ˆN¤”…Ä’’/O¢„âyqL’Aªƒ¯Ôvn¸8«j€uµC!åð „ eüR'z©­Ë ‹Ï˜#ÖÖ´Ybívö@µ hÌSWëIgKEØ^ÑTñ¸l­>åZqÞpÿÙ:ªÅx*:HPö;ZÌ ùY|;¤¢óz”4íÕÅǃC€=KÁáÈ¥0&À˜`L &Db…â%æ(ëÈSÓª/5 ,{gµHNÇÛ19Ã`ÖV³I¢ñF$TºšˆL{4 .M†K¦¾_[Íf1¾(­ðrpö4B|ZŽ]ºXœ§ÑêÄz°ÿhÝêý¿‚ù_COÖõ`ŠÃñK ^O‰!4P±ó±Á.åýãD€=Kã–‹eL€ 0&À˜ÀD' Æ,…)¹ƒšmóÙ÷0NL™ã©¥jÏ;£>'p[&t ÒÕt8ð°ïsO{%Púo²®fʤ§xv:ï”Õ«Ýî;¿þÄ ÐÙpPŒÊŸ÷ ,€S›¾#ŽÓö¡x†®¦#pàõ÷yžÒÕO@JþeÐxz-œÜômqÌŸ SRòVô9—®?³ý'ÐѰ_ìŸ¼è¿ gƽbûô–‡¡þäK½çk`î ÏC\j)ð  i=}ÊÍkZ̹þßâ’=/\ö®ºÑ\>¢sãÓgÂìkŸSîñâÕ`ï¬Ñu|ÒØ pÞØÙñ•L€ 0&À˜`A&@¢#cÚÍC–ªÕ tõÿù„‰†ž¶3⺾ôŠßƒ%i²ø\µ÷×àè®Ûùó¾Z½YlgN¿E%úP¹û—$”Düß„$ÀbiB6+?`L€ 0&À¢—@Áü¯‚N7è$d.€¸”qüèûŸ…=èe!Ï-nG;è1T-«äqÜíìFOÓOÅ6y°rÊîe\ôe±¯½~œ?¡x„ÄŽü§ÑèFpÖØNÑꌃ^ØÙxvþs™Xì5ƒž§ÑrðØ pFy€IŽŸÎ˜`L€ 0&0~\¶V0ZÒaÒœÿ€³{~=àÌñyb¿Çí„®æc¾sÈ»T¹ëÑcT ¶ˆZóÙ÷¡¥j½å›4ëÓ@㜠x¯Ç å[ñ]?ÐFrî%P´äûà´7£ê±^BRÖÅÂ[UsðOC - LÍ[íõ»áÔ¦ÿö?ÿ¶·q¸‘Îìü9ÖK ?LÎ[ “~=bSÀik“þrg~ ,‰S êÀï áÔ+(‹¡ô²_‰r¿sŸõ›4ç!Èœz´Öl„žöJÈ.½LñùÐV»MÔM–ï»9oŒŠ‹¥Qáâ“™`L€ 0&ÀÆ“@ÕþßâX¤o£è~¨;ñ<8:kûÝ®»õ”ØGáxeW> §_öºÐÙtΟ|¡ßù´£Ç1ÍË^Œ â «øc✚ƒÄð½òÏ—;)lÏ”0Iˆ«Ò5¿ñy¼hÌTÑÒ‡48ü_òô>kƒ9U¹¶ólŸý¦^±§3XÅþÄì…0ËÆÂÄgƒ9J×< ½dV¬³r=/ÕE˜FéÆñfLp‘f¼ æáY£óh,WrîRØÿêMBD)ñÿ£%Àax£%Æç3&À˜`L€ Œîf ésú]ç誅ê¿õí§qNÕþèû<܆VoÞ) ƒ;†¡¶vEà8()r†+c°ãys>'Ê ¤û^¹v?¿š+ßBi°k÷“P:öÁç`Çs— Wê[â0…ãÅg^x*K£€Å§2&À˜`L€ Œ?ª½¿—³ Ò§\ ‰ƒtöIœøèëÐTùžÈfGµ¢qNS®ƒY×ý&ÍþL¿ŠRxš4ƒ% ÌÒK#w³®Øù¸ìmÐR³ ÎyFœ­G/1>{˜+‡:¬Ä¬ù℺£ÏŠÔ產ŒÆlUÐR½Q\Òxæ p»ºÅ¶Á”<šbøÜ,–€ðG&À˜`L€ 0‘Ð`-Z\ȼœ®¥³§j<)Ê,¼½$8¿PóBSÅ;pbý×ЛBáf·@Õ¾ßâxŸ&qjþ¼/ wÊ໌DWv‰?%9+Zòˆïøp$ÞœªÔâ]M‡|—˜Ì¾íÑn,iøxJ=m8O”4 ?”Ï"÷ µ WôºC>¢cnPê¥ÕjE;S[KSoË}q=Ð7o">'?`L€ 0&À˜@ ÔI6è•.¥,A¸ƒRDí‘g1IÃYˆOŸIÙ‹ú”KcŽJWÿ¦.û‘owëI¨ÆñN»~!öQšÑšãÛž²ô±M¢¢²÷+”9í±¸ÿôz‹OÔй–äé¾Kì= ¾mõ% ÓöŽ/¢m —S› “9`¦ ±KŽe¢Twƒ9M}jÈ·{{Üœ´õœØÎÅ x4¶‰ìÌÎG¡öÈß|ó2.üÏ~Fœøz·²pn&212Š®Û”ªÜÑ}^lþ×ÓV!vY’‹€Ò–“¥^!Öò?¯×íËèG“çãs@oJ‚<̺nëö*!|qfÅÃîú„ãþ1!½^o8Øò=£À@oË¢ð1¸ÊL€ 0&ÀBJÀhTº”voBPïKi¯)vRÎ’>å6–¿Ù(,H•¬úµÈöæ²·â¤<Ÿ7†2Ýy=.L½]ˆÂã³âz*‹Òˆ“Uîþ…ðN‘0™Œ¡~'7þ—Ø?ÔS–<Œó7݉‚&WÌåDçÖú‹Ï3xmWËq±‹ß§xsmç¶ÀÌfG¢ŽŒÙô•‰m§­Y¬;ö»Ç™?Zmç¿–îÕç—Ò9ÉÑÿ¡>’èu«?rTeFãÉQ+–H ‘PªmhƒµöÂþ“U>þz<–ÕÖ V‡,˜\øÞ%Þ˜ý·ëõ`Ó¡=LuIÉè•Ø}´B,olÞ×-›‹¢©°÷—{›&É{™`L`¢ Î²²PZY2ÍÐÙÐ 5Ι£KÂËíè@¡2:qv!÷#1ÓÝ|lTExëi¯ôš²+þ Y Ä«3;~‚c  §ôîÞó½ÐÑp`ÐkÇãy”je¢èÜÔ8Ñï‘ñhKubIz’</¼†"éýíG„hÒ šY[ŵÕ0¹é<ÜŠ*¥Æäg ./þ8—œ§3²a_~œoj‡?¿¶Öí<Ü´R“¬¾_Á½3—Ƙ`L ò P§Yzt:ä§[áŒK³¡Ô´ⵑÿRó{‡Ò+~/Æ,Q–?i$²*vühBÝPÚIÇ px-gĤØßÑãËdjc® ñPÖ+÷Š*±DÞ$ZzpÒ3¯o†C§k³éu5péÉCÖÉáwáøMÔ{’ŸÔÒ(–%åÇ`çäé°sJ1TÔ6ÁcϼŸºqö¦$EaÅcš&ê7Ÿ‹ 0&À Èð;LÔ‰¦Îtz"No€†N'³¯‚…–¾¡feDËçž¶r¨Úû„/ßäË|·âäá þbpL XRº;à¢ÊS¢¸×7‡ð.)ã—‚u.‡ 0&À˜@$PX®!`L€ D<ú[GYÒ(Ú‚Ä PÂÒݾH‹bSÛê€z»š\ù°¾ó³¢«Ály`’á ˜5ÿœ¡‚”D ®éèᛵ®RpcÆ;²x½òtOBÉññV°Ò§„âQˆ%µq,z•ˆODŠ%µW‰BðNU7P]'Tæ;º¬«qÂÓ¶8+8tp¢kÓ1¿7K¦Ã_®· Œ.'˜0„-§­9êæË2;@s}BÁtðT5LÍËôýR¡70lL€ 0&À&*ú;Gý6·äÁ,±n‹Ç’®Jz¤Ãó’qŒï¹.t¸4Ðâž$'cÒtã¶M¸ÔƒAc›¨¨Âò\^H]žtèÀ¥Û›ˆm…q‘½fÔz!Û+Õj¡“$”â…X"Áׂכ /½J„*"Å’ô*9±óÜÔÚMmÝ@„NnTD“läh[ÛõFØWPå™9p.9\16@n¤í•à°CaCÎiTÍÑÑæ¥ç±t¬²HàÓ\4è•þ€ÄÚ@È‘¶3ŸÇ˜`ƒu¢u:Lz0cx—Ì KÉÈ#!ÎÁ4ã&ƒløw²ÕÐîÔ@[ voØÝqÐäΟ@"ø)ôèåKл!Ùˆ%³Ç"™…P¢ðIH´Ád%±¤dÃ#!,Û0‚mܪ‘b‰~¸( ¹q+k•ôàÙm-èypŒˆñ,Øf0ÂÎ)Óa7.v­yZR<äg§Ùˆ¿\0Ö—fHŽ5óx=øKÓv»Úºz ¼¦:À‡&Š%¿¥–<…Mõ¦°ñ¼Ahï²A]c+äÊÉÛ”™Í#ºò\9&À˜`@@†S¨õá¼\TBI#ÄeÍÃp½ì ;`1¹ ûyN·ì.@±¤‡ÀC×_@]øR?ŠUa’(L:H¶aÒSؤ’¬Ád4‰ÐIJâ Æ(õŠ¥x!”üYðt:­»þ’ck+âzçôÃ%ܸ8 -Žvv+îØ{OT¶LyF¬·lø „,?;.]P 3&ç@ZrBT>ÓxVšÚüTu=ì=V ›÷„ª”tøç¢•0 '½òв7ž÷kÙ4÷Wrw'4$$¡7´r3S„à÷z#îGl¬È×1&À˜”yŽSÆÁжQJzqIè¡ ¬y6›MDaP$† Cñ­ø7”ú~©Þy5FÄ_.Ô>ä¢ôߔՎ’6ÐX$%ÛŠ%LÈAc”(OìëM.…•«q=9E,yÀí¡ù•\ÐmS¼I&ÓMFc6O/ƒÍÓfˆj“HºaåE0g:»˜‡jGú.E!IË5ËfÃ;[ÁG{ŽÁ¡Ü8Bä–=Û€2ÐE¢%öt ±ÔÜÑ…^QL!nR~ùÓw:–ÉDb[q˜`L øýí£}"z3H(±n·™ÁŽ!÷$–ÄßKŒ&¢ˆ"E,âêõL¿–±Q¢ìw(m Ì‡E¢U™@Û²b[ÐLäŠ%ü¡!/ƒ ]µd&üŠ&{sÎBFFu^}ñ ¸íò‹7²œ@r‚î¸j1,(› xi=Ð西^² îÝöaD&HB±DÖÖÙÓ;¸Uþâg±$ÀðL€ 0&0á ¨“è¨kH,)téɰÙì~og¯X"ï‰%7FÑ F¹LxXãø€R,I %ááÃH'= !ß$Â4?f14£X"EÞ?JìQò7NDŠ%Ÿw pœ(˜È´8¶%ZlëÔB(‘8ºÿÆåpñÌ¢h©zDÖsZ~|÷Ó7Âï^øÎÔ4Âó —Ã'¶~V{deÌÑã/{2ŽÁ¢_øô†L†D$X®`L€ 0q “FCé¦iì®.&!˜°cîÀ—á”ȋƧÓËqéY¢p<KÞ(‰%òð‘p5/yúz½}¸¦ÄTÊqJP¥x“d^›è.!âÄáTÄ’?v5šË΃ Å3E•ï¾f ¥ 5^"æþÿÒWÀ£Ï¼8G1¼°`9ܳmÐX¡H3JZ!éó/üHk®`L€ „‚‰$êlÓ¢&ô.¡wƒBñh˜…ßѺ¯Pê _ïMñ@CÙFO@ФßËßßR¸R² !ŽzÇ0ѶÌx'Åîèï<1¯ˆ8±D?²ƒ)ß.D z~áÞ+»HT÷ª¥³`Ù¼âh©zTÔ3Îb‚/ßyüü/oBÖxç”XzúhÄÕ]ûþñJWA®`L€ 0 N;…~iq>ꈋ1346 üÜFô&á O%üŽÂÖý%)’ä:Up·ð ¦^±ªB"$Oxü”ÐåØ|ì >Rĉ¥~O%áª4Vɉ3SO™”s‹ ú=ï.;®\?üëp<+ºqRµ8LA1F¡¤*ÁOؘ`L€ 0?Ùq÷ïQ¶øof ‘ û,…Ñ…•ÛWG¾XŠ’ö9™™+jº`g¾ E“åf¤@Qn:”Ÿk„£9y° òt(nË÷`L€ 0&ÀÆ‘wîÇ.=&5ñO´¾M°ã@Ū´LÑóJxÒÙ1}ÇpÑ¢9ÓÄU‡'Žáj¾„ 0&À˜`L€ M ¢Ä’ºª2–U½/R·Ï'$ƒƒA3R!°…†ÀÂSÄj“R2F²Eë‹€HfÊucL€ 0&À˜ÀxˆX±4ÞÌò»pÌ Yj’5˜ÅrYÈ3AN¤FÖY"5šÄþ0˜ù0`L€ 0&Àb–‹¥ 4}—Ù"JI²*ë ÉEŒ@v²"P­‘%–FX}> 0&À˜`L ‚ DvìRƒSW²±‘%XÍêÝAÙþß¿½ 'ÎÒ¬Š}ã¾kajž2>Jî“ëÊÚFøÙŸ_—Rl?xë*ñù©W6ÀÎÃå¾c´g†_|íÎ>ûä‡GŸ~ÎÔ4Èb=cJ.|åî+ûì³;\¢~tn&[0›ô8ÏT2”å´ü¬>çŽÇ‡¬ôd8^ßqž¥ñxV.“ 0&À˜`L ´X,7W"ÓáD_Á6ûTŸo8·ÞÜ´..›w\µE™"&N ÊG£Qù »5Ág” r!L€ 0&À˜`QK€{˜ÞtóK'‹‰Äd5®‘›ýÖ‡UÇL("fO>3ßéªóýÊ©@•Ûãé·_½Ãîp¯þñÞBÉÖÎ#gà™µý;x‹ 0&À˜`L€ DKÞX‰ñ(.ÈöÕòl]´wöø>ËÎn;†ÁùÃææL/£A'÷Y§&Z}ìtµÿyÒ©ªz¹ iƒd÷Û¼ÿPØŸ´åóŠáÿ}âj¸û𥓑,wy»Ãÿ|yƒ 0&À˜`L€ D0KÜ8²j 0œMm‡Ëû{—Ž”Wc¨ÿ¬…e“ý¶L˜AŽ&u%;5€gétµâmJJˆVÙ¾c•¾Ý¥“sàÞë.¢nåüøòWøŽÑƱ3µ}>ó&À˜`L€ 0& X,EA+Í/-DO2.Šª{(`l’²Ï/ ,&#Ìœ:iÐ'£±G2ID}s;ÐØ%µIoÓÔ¼ŒÇ:ѹçñ:i30™ƒÚRP`MÎM÷íª®oñmó`L€ 0&À# §ÞàµWôÁ‚Áa0Ö¼d8ÁÃÈ8…õ,ÊZGÞ›£gΉzEO ýðHE%µ·inq>èu‡àQøãS&eÀ†=ÇEyv7¯¤@lS‡î»Ø.š” ûŽŸÛÿ­¸¨<½®,Ê’h6Ó$M¯cM.Yðš 0&À˜POQ_¦£«Î7µC[g7´â0jàñ=nšùœõ­q’`z™žqf“èCÊ~äÈK‹Í3Y,EI»/À°:)–( ]9¦ê–Þ¡38V©«WàÐã,œY4ìSMÍó§õ.¯ö‹%uÂ*0±týÊyƒÞƒBûêÛ|ÇsUc˜|;yƒ 0&À˜ˆ)$Ž<˜@ª¶¡ö8‹ãškúŒŽ)azXÊÜ<½ èÅú¼’|HЧqìß ø0U+¢oËb)¢›Ç_¹‹J áomÅ_2ÊÀ$u quXžÕb‚Srü²•™š RzSbõ¸%‚Gž©‚ì´A®|wCK;üùU<мláŒÁ/à#L€ 0&À˜À„&@‰„ÒÙÚfX»q)ï;–Yãµ€ÆkLj èvò=˜Ð`BñpˆÒ«qàìàÑÚ1Ûq«¨Ë‹ëvÁJŒºzé\°ÆA‹BнMý…ÅR&¹‡DPéä\ü£ŒM¢â7^6_ÔU-–HTt¾' Å;x²*1ÃÍ­¤×ë|©0' t£ Ÿ#Ï×S¯|„c ”0>ªÜšE3aR¦’L""Ár¥˜`L€ 0q! =Iíj÷Â{;awor(ôc€ÆZw:èp ^Il¡ àÕ`˜£§ˆÑ6€ Ú`ÝÎc°í`9\·|¬œ_*ú~ìiêÛ,–úòˆèO”áNŠ%u qÚ–¶pæ¹9ìºCñH,¹Ý ¹•²Ó’>É¿a ÁhèÒÚ {á­ÍûŶ¼†/H1Nû†2JïM®VúÅFs+õØTb 3á g”rüWϽ Uu;SiœÓ§o¾²Ò}ûxƒ 0?Ï?òX¶Çe/uk´Ù/ä`"O_é¿tboyaøÌ4jxþƒßýÑcê]±¸ím‹Æë=§ÑiÎa§æôïùvy,ràgŽ j¡t²²þúæfô.á·Cí ö2¬$w;#£¥úÖBçÊÃ?Eñ(˜a¤Qüñå ðÅ­F“낉¿µ}¿/ÿ‰&¨•bI¦—•M]c4è!?+(Œ;t÷z–Ò’ 1~x÷ø__ßÜG(­º¸ n_³pÔcdýyÍ&*‡¾÷ó¹ë&g{ƒÓéX€Ï‰o)pÀ3=°’³e¢>ú¸=bËGvß·DMÁÊ÷È‹R¾Éð;?>æÕx_Õé5/=ùÈwwDÍcpE£ž€Z(oj…?¯•B)…Ǫ̀¾‰þZw2l ÀiÞ-Âñþùî¸çš%øØŠTˆeÁÄb)ʾýr%—Û1¦þD É V˜–ïO>ÒÇ*Âôà$–hn¥Ó˜Bœl$ã•v­€'ý¡w+ç—À˜NœÂòÀ?Å’(¼WfŒec±FàsüdŽÛéý©Ç㼎žtþv‡Ks3¥î?Q »Wz]N÷|yµ ÃÿnÒ'}áÿù²’Á'|Uä;OP”œ„’Ó鄇ˡº¾úô(”fã¯@óMÍ®u§`Ž©à2œ‚··†¹8^ž&µ%¡DÞ¥XLü ަopo]Ì˜Ü¯Ö ËFî/ È‹¤ž°Vž¸¦„lL€ ô'ðà#?-E¡´_²ÞK/áÍš ?øÜÍšo]KfOó›õ¿Š÷0 '`2êa>¾8ûÔM+áGŸ½×Rd@IDATUCIvè;HßE›«mЦ©~. ô% Ãï\˜ÄÁîp»ۊtÎô,ñ‹¡¾´¢ã“ÎIc˜¬"ñ×û(˜H“¦¶ŽEc±…­>·¸@̉$«ž–SFà ’ç«×4>‰Â€¤ÑÛ¼¬¡çEjëè†ÆÖy ¯™è%ðÐÃ?¹\nJ%ôsùíO]¯y঎?glL ”R­pß Ëá¿>u&·14ovsv`hÞòPÖƒï5ñ (bÉ‹jì:rZ° Áy“´®ü‰ÿðö 5ާ¼[ÙŒs0µuvã|œ®˜L†…_t“ÑO|룪ùÜr)Ð2ýÏW‘ûÏO^Óï’¤„8xò;÷÷ÛÏ;˜@,xð{?Ÿíõ8_B¡O)ü?ƒž$·‹åoDd<{aN:|ûàÉÖѸÔTJþðùG~¶ø·|ûTdÔkÍÔ^%§Ë ”xŠLëÊÅ,6ü>>šÛ–2jqÌ™:àÀ‰³° Ç.QžV«ÁD^±•À•¿ÉÑüMæº3&>ûÈÿdz½®µ$”ÊŠ&ÁW…RD´ W‚$XÍðµ{¯R’÷x!ç¼yíKüšçwà¯Ç^%—Ë v»NŸkeê|òæ_µ ;ö™Î¬„iÀ3~³vÆ4a~øpX¿Å&²ÜQæ9µQÖ92+N K^$ÊNw®Îe˜­Ž1¤§*]âÆÞ°¼ôt’‡²â¡Côt~½£¾eŸí³çRjŠ&(¾ ¦/ŸYª„瑘jk÷{’ú\ P ûSxC‰„=K¡¤Í÷bL€ 0&À˜7vôŽºñê¾ ŽžtB5 –7Ƨ’w* =N4“ ãS—¹u— “ExE9ÅECû\¨ÁžúGTöŽY¢ÌxT§Ó ¯½Ý {ÚÕEGÅv¤‚qè– Ö]¸&À˜`L€ 0 G@&xǃýéÙŽoKÂè·iïwŒ„Ñs/u ¯QZš::½(’Üý<:4ñì{ë»ÅœI‡Ó Á®ýýÅÌŽ=vؽß4tÕÕA÷Ó_¶ö»¯ÜAe=…uŽÇPÀ L-N¬±‰æ-òø:‹“á>üh‹¼Ä·~ÓÓN g[‡ó¹Y,…“>ß› 0&À˜`L ¤:Q¤Ð2”‘'¨ Cð†3ò,ut wVßã4!.-lÑA€Ã𢣸–L€ 0&À˜`L€ „˜‹¥çÛ1&À˜`L€ 0&X,EG;q-™`L€ 0&À˜1K!ηcL€ 0&À˜`L :°XŠŽvâZ2&À˜`L€ 0&b,–B œoǘ`L€ 0&À˜@t`±íĵdL€ 0&À˜`L ÄX,…8ߎ 0&À˜`L€ 0è Àb):Ú‰kɘ`L€ 0&À˜@ˆ °X 1p¾`L€ 0&À˜@hh4ËðÝ]ƒAÚáO•ÖëÇV÷PÜcl5㫆"0ÆæªH>Ƙ`L€ 0&ÀÂG >N ׬±@Y‰t: ´¶y ¢Ê[vÚ¡®Þå«XÉ4\y™ÒSuàvÔâ±7ßïšZåœÂ<=ÜzN/¼°¶K”™œ¤…£'œPVl»Ã¿ýK‡¯¼UËÍ0o¦ Ú»<ðÔ³Êþ±Þãñß·ûÊåð¡†_ùÎL€ 0&À˜`L`4î¸Å ³ËŒÐÝã…SN°Æi`Þ,#ÜwG<Ñ‹D6«Ô÷Üi:=^±/?WŸº3 ')þƒ^)ÉZHÅå®[­PTˆâ ÝUgκÄþìL=äfëĵôßìF±¿º•Ú…ÜCÀÿ…{–ÂÞ\&À˜`L€ 0`HJÐy„È^|½ Ê+]`µjá~J:Ù™:8[ã‚«VʼnsuÀ«ouƒ]Ÿøxäçê`ù3T¾Ø)ŽÓBGªµïtCC³ÎV» »Û q(¦à\RÐãD*²ý‡íb}!÷ða'Àb)ìMÀ`L€ 0&À˜N1=èQ²X4pç­ñpºÜ (nžþgtá1²¤D­Xh»±Ñ Ó§(]âúF—K$€H ©míÛ]p¢ÜÂwè˜Í7Á4<÷£-6˜>Õ N?ßàÆP?wP?o‡‡‹¥ðpç»2&À˜`L ê §†–H2·Û ïoìkqÌ’Ù¨™¥F±\y©vpÀ[t;’¶z…EnúÖ”ìÁ –ê<¾ã´AÞ#Kºg6i EÙ#±Æ=DAò_$¶u(аX e¾`L€ 0&À&0Ű‘iÏò!|ÿïÜkòü”N7ÂÔB=L›l!sK˜Ä¦æf¿ðy=FuõþϲÖ=ö¾Ï"Ç5ÉãUçÜÐÜêÆñL: $S ôàÅKVÄRW—ÿú±ÞCÞ+lkâI3èýã²d]"M$Ëz{í—ÕÁ.™ËcL€ 0&À˜˜pê$õJ—Ò Î°?ïŒéøâ§1QCÆñH”Åî±ß´B fÄ##áÔÜêWoD]ažAd¿£ x©)ZX0×¥(~ÈC5œíïF«—[DØ^ÅY'´u(÷ Ö=†«Ãx÷j”±Wñ¦Øõ¯Äî“ç7‹ËfL€ 0&À˜@ 0JÏC¯'"œNã…ÈÛCó!}áD8yÆ)2àQ24>ÜÜW`hÞÜ™FHJP óõVðö=#zò"­Zfðè‚ý½!x´¬{PYá2¯Vá`µ˜|U@R¾íXØ`± ­ÌÏȘ`L€ 0 Pwš-F¥KéÕŒLd¹*}Š#Òók;áÚËㄈYt‘ÒÑwâ\I›¶ÛáΑD¶i»MdÀ[™ï¦`Jp28ë6Ù`ën›ø<ÜM-¨>ç‚<·Džª#ÇúzÖ‚qáê0nÇQøÊöLˆ3¢DR%Ýo ïâ¸Õ#̳X sðí™`L€ 0&mdg™Öô/3Ù ZÜöh»±'‚ÉÛ?iB(Ÿ‘&=qºÒ0¬.Áªn›ZPØØzçS¢ºÐø¢õ˜Ån3NT›™®ŸšÜ@¢JÍÑôð£-òã€ë?üÍ?)mà ÁºG`¹¡øìÖ5ˆÛ$Åáx/“A$ÑÞ±åXK¡ø¶ñ=Æ€ÿ÷ÙøÝƒKfL€ 0&Àú p5êûå‡I™)pÃÊy0¯¤Pîë×7ìƒ×7îóíKJˆƒ}îVCÑ÷O\†í>òä+àì ¦?øß¼ïZ˜2)Ãw-o„–ÀM¿üW>ÕØ<5´è/ønR,‘—„’Á`ꢬx¨n±ËÓnÃIÐ;‹/ø^\@èx5ØvÆ#â†9‰FHŽ7ƒÁˆm«7`;ë1{ .æµ^ÌxàDUJäb†"àöx`×!%žvÖ¹³CÊǘ!íø£âÜèÆ*ôØðÔˉùMdU)³ÖŒ)¹0kê$ˆSËcom9;+/KhŸÃBî¾æyX¬ßC±ÔÑåGJblב3¾s’¬póeó}Ÿy#|P6aÀ¼(¿éqX‡Â铟ø+XÃW#¾óH(¡Xøó'¼JzìLÁŒBÉŒ뜔8ÈKP<»Nã pëöä>|NhGÉaÞ‹B© : ”eÇAœÅ &lS†W’Xž%ü}KÞÄX²ØzÚqlÙéçkDéÔQè¶qf¶qD-ŠÞ¸÷tڜ£WÐ4ºŽÙx×Ëg±Nàßï§«¡¹½ËwÍW.‚_~ýøÊÝWÂï¼~ñµ;áS7­ÄðÿŸ¬võO¥“s`ñ¬"ß>òr­Åðâ0½ÈkY#ŠGhòh^Ix9 §Ëí;¥Ûf/â°Óh×®˜ 3§åùv»0¡Ž:‡…ëݶzüí-âº(^xoÖ©Õwß~ßyåßgÞˆ|¦‡âéøfìô6­Ç·¼Oǧ‹û$øÝ‘‘ÿ²†ŠXÒoƒ†ç‚8‡œN¸ðçÚívC†Í›´=PÓ"º³ä£`ª­+ tÌ–çéŸfBŠ˜‡rc¦»fœ8¸¼(\¥·/Ùè…))ˆG‘DBÉjµ*‚ÉŒÞBü},½J±‚GÍÆb)ˆ_Þ) ç¡°©*Ó2á·Ï¯ƒoÝ]¿lLA¼]LµaÏqØ„!xd—ÙË^¥˜üðCG*5‹Ëà }£:ZÞÝv®[>wØêªS‚;œnøù_Þ€5‹Ê`6 £Ô¤xßõ9éÉ@ËP¶l^1l=pNU§:­„HËknA1EÙòØ¢€Ž·Ê° Ãô~ƒÂéy §¿9›^øÚSfÃóx”P<òD¸P$‘Pr{Ü “¹ÄmP×é†f»çaê·8 /ŽIôZ@ë¡°X#nsÔeÐ[SãB¬\¼È^ $ºUçlôþ%[Â#o…\hmCïz Ef;ŒE¯1b±D‚h×ïß Ï,[Y›þüêFøìí«‚XzluÇ5<÷ö6áÒã!¯ÅºÛdøé™@d TÝ·®YÂd dï`"†ås§+N(‘ƒ Cxì§¸ŽæX¢Ÿõçðyè‹ ²`Z~&Ì(š©‰Ãû¿çÚ¥ðã?¾†5ìV«lj^&¬œ_¢ÚÛÑJ@„é†éá‚azå¸<ƒûþúÊ× "ZŸ)Zë-Cñĸ%×âÁŸ;òêÒZ %yŽAoƒtÌÜÜãN—zã—ä¹rMÞ§Ëq§w¶ö?zÇ•‹å)¼ž@8L/üIbˆ:Ó4× %I´Ð~qL¦ï¦ãv0¦çò$w»0<§¹ð|7z•ú¾âÿ³E{ ÈO§£öÑxEò3ºcÍšKInB+Æ™¡G0žBïz=KbÌz•è8 aò R[Æ¢±X‡VÏim†÷í€×æ-‚éѧÛáó[Íc˜ÆÀš&ù}ñƒ°n§’ùªçTºòðÞ1”Ä—0&*ÇD ?ýóZq»mOÃe g >7sj<üàMðîÖC°§`,«hem#üáÅášesà¦!RŸÅ¶ûè(ÈI ÜÍŸ' Ó [KR'ZÉŒç‰dEüûiÒZJ¸PÖ<»Ýô$»Ü.°Š=Å %E–¼^®¥‡J~æõÀ3rµ²hpº¥-dºw‘Å’Õª&ò(‘€’B)–½JDšÅÒÀß· Þ[Œ©ÄïÙö¼´`©Éûþï_†Õ ËÄä‰ \¾àN°ðl?tZ¤þ¥7Ëd+NÊ~ÇÆ˜@d A² Ãï6ï?)*ú<&Yøò]W [iòÝwÃrñFºâ\#«¬ƒòêó襪š‹Imom>%˜.œR† ´£gúO„ùÞöðhÖT˜”™x ž`ðo vã0½¶+uÊ)­´Ú±DsQ4i­§W±ÙíB,‘`c›P0)a{8d Å‘šâ…mSÈv ±D^"š‹„ WšpVz–dCJè …K×Ų±XÇÖÏik†û6¯ƒ7æ,„Šô,1£üFLe»xfÌ)·’Âá¶Ç*D]Ñ5õ-°ÿdìÄùªj• V ¶¸êðÿFÝCq…™@  ¯Ï®£bRà„°Ãá ?ìS&eˆ`¶è88{^^· HDI;ˆ¿+ÅRN- žS ‹Âë•+(<èÙ7·À7ï»;²^Ot¦º¦Ÿ])˜d]ÎÃD!z&£ …’ÍçYröz—Á4´w)tO1±î¤´y•”¶¡ö Ìv†^±$'©W<±­HT‘G‰®uc±4ÎßÃtÇÎpÅÒG%³á<$ÃúÝÇÄBšéMjR¼E,fükFsÚ:{ ½Ë ­íÐÚÞíC`v:`Ééc°°ò4¦õ§öÀL€ D,Dü½v-fÂ#CöÊúÁÃg_Ç z¶Þ䙩‰ý’0Ðë’Âlxà¦Ká襗‰èåJ ½ðþNèìöO ~Í%s€„–ÌŽwç€úhÏ1¸lAià¥üy¢à0½´0ý¼R‡œÖ´WBtÐ{Cð,3z•0 ÏéÄã4vIÉœ'C°g)¸Í$Û@Š%éY2à˜%š;‰ò.Qˆ¤IÒ›ë%Ù,–$‰q^OÁIki!Ñt2kœÊÌ .«8×0ÎwŽ®â (ž¦4Ö /R †2šð)`ÑI`Í¢°Óý7b(mGWÏ qÃíNœ­Çдô2)ÐR“âðm§Üe8½hRÛñŠZ_"ÚŸ‚™ó®^6=ù-˜Žüuß©¯`Ò‰‹pRZN!îCs¦7¾M.;èÊZOJ(žQ$§Ó$<ôJJ=™ÖšCñ.¼ˆ¿ø'D«_¸Êd$ŠÈãGm£x’t›D"‰®eS°X ñ7AЦ+4&$B›Å &3tÍ`C—g¬™Îë+Æ.Ç£[ÞŠáv98Ñ,{‘bí[ÀÏ;Q ÐàÛ/_(²‚õŒ…8ÆIŠ%ÊŽõïwwÀW-î“E”¼I¯¡wÊîÀÔY½697]nŠ·Ó”1Sm·_~1–¡:o†?ï8\.Û0uñ¿ÞÝÞ¶J}zÌn÷÷Þvóã03P´ ?×8WŒÃôưè¨Sg½w¡¸Ž)Ìm¤9˜<ªñJþ zR$ÉõøÔ.6J•‚‡ÖÄŸ¼KÊZñöÑïhòú‹ :#ÊØëœÍ¸Ÿ™ÞÑ´°1&À&*y%…"y}³5‹fÂ6LèÒá¸d”‚Æ.æa"†dœ@¶'¸­mjƒv Ù•Fó/-ž=U~„77í‡úf% í¤°½3&ûŽß¼j>ìÁéèM6Ùžc•pðd5Ìžžç;'V7<Ú®"-E±úü¾çæ0=Š`nPG]é“ ¢N:έä¥ãäIRzGã ¥g)˜uˆå²È³D†Í D‘_8)âI~¦5ÛÀX, Ì…÷2&À˜@P*ñÿé5‘¨a "“ãà3·^¿þÇ{˜JX3Ý686ˆÀ¢Éoºí2™EÏ5´àœJ‡|EÓÛSòL©-çrZsñŒ>ç=÷öVr·ôñ`©¯áíØ%ÀazÁo{êŒË޹"ŽH4)%º›Jô[Ð H!DzH¶ƒ‹¤á€³XŽgL€ 0 "@©ºW\T püÒ`V\ ?þümðÖ–° ³†ÒkF"hù¼b¸~Å< Òž}c‹/éí£ä ¹ýÓƒ_ƒã—6ï?… V3z¬^ûh† .’Eñš ô#Àazý\ðÙa,ˆCï‰ç³FÁ)-öJa±{mÎO̘ ì´$xò;÷¨¬»¯Y ´ eäaºëê%p˪PßÒŽ‰!:¡µ£­fHǰ»,Ì”g1ûñÍû¯ë·o f“~ñµ;:Äû˜Àð†Ó{è{Ã_Îg O€;õÃ3â3BO€ÅRè™ó™`L`$j ²ÓÄ2Äi|ˆ „@Ÿ0½_Á‹Zg5¸ûgp [ùÆL€ @lOÉ4Œ\`L€ 0&+p”G=Žÿx\¯¹¯|n×x ŽXyv~N&kسk-ÎÏ˘`L€ ŒšŠ#'Ž_z]ðôœ¹ðæ#«ÀŸÇ~Ô¥ñL€ D KÑÒR\O&À˜`L ô4°Ãpž6[àÏ=T—B_ ¾#`a"Àb)Làù¶L€ 0&À˜@d 0;¯þޤ§_ü*ˆÌZr­˜K¡ Ì÷`L€ 0&¡´kùÿÏÞyÀÙQVýÿÜí5ÙôN ! zWºXàU@±wÅî+Ò%@D^¬èŸ®¢¨¥+¢4©RRIï}³½Ýÿï̽³™½{7ÙÝl6»w¿'Ÿsgæ™™gžç;woæÌ9Ïyb6gw4O‰¦ëº£vǵS¯I˜]*¶!'€±Ä÷€ Ї Öpïßùëv‚üÒîÁô™Ýqíækf׌‚@ 5Œ¥ÖL( @ ƒ f—Á7—®A ‹ `,u1Pªƒ @ ç Ì®çÝZÞ@c©7Ü%Ú@ måºÍVQ]c£‡´~%…í8‹C á³ËðL÷ °k `,u1ßì1£¬ÿM?i­ ¶ñ>×þã{â‘ÙÙ+)J´¬¶Þâ55neÁ²¼C´¦M[¬âê_uø|N€@_&ðÒ[ïÚÃÏ̲5Ê-® ¡”ÚÃÙ{˜dûOÞ#,f™$ðÆüevý]ÿnæñó?l#‡ ¶··¯ùVz,Âìzì­¡aèu0–ºú–ÉÏß!éèñª¼{ÎÙ{¢õ»æ²àbµÿzÚ*~c‡.+-±ÂDW±5­ÛСs9}™@EUýá‘çíõ¹KÒbØZYmo-\è)ïÝßNyÏþiÛ…/½½Èîù×KÍ—¾â«§[~ÿ%5a¥Ã³ë0²qÂò5›lýæryÄk­²ºNZÓâ¥Ohd/nDLÅ…VR˜g%E6bp™ د÷¨û›ÎÿL]Ì<^YeuO¿Ð¢Öœ½÷²¬!ƒ‚²Æ%ËÍ5”xSc¸Ú§–Yeý-køËž´§žùáÀPêSè,º€Àïîÿ½ýîŠV5æë£¶EùCO¿n«×o±¯œvL‹òݵQWWo[¶VE.¿Í#)d;&@˜ÝŽõ #›ìÕwÛì…+l¶~¿*õÒé^C”Ú¾{޶¦ŒµIc‡wïÅ{áÕ0–ºø¦5mØØ*Œ¬ä‚oY^ÒXªûÏ V}gú¹¿Ý³’=a¬ÅŠ‹¬qábkZLÞ¢…±‚|%|OÜ67ÌôúÅÜ3“3u/‹o*·†wõ†Y¡}iEçenY#†ZÓšu £-²œ£7~ý@TO¼&ñÀ•³×‹õ+±ÆÅËÍû¸³Rpæ©Vð‘îl5œ>Kà=lD ¥…Ã~üäÃì€Écõö0ߪkë쿳ߵ{ÿý²ÕÕ'~^–7Çÿs}zÛò?Ú*oºÃj|´¹q¢M7¢¹¬0?ÏŽ9hŠæçš{ Bùçó³ÓKõð2é[¹v“­Ù¸Å”ÛÈ¡ôè¥e:Y°ly[\<¬cÈ€~¶Bç¿>o©•WT[Yi‘M=Ô&GÞ\ºá¶vãV=,E½JfËu^~nnPO^n¶mØRažã’›“eS'Œ2ØzñÍ…:v³5}¢60ØïþÞgÎâ•A=Þ¿Ž·Ûuʸ‘»,Äoéª ¶xÕú ßÎzìˆ!6~¤~Gû%_:5·Ðã¶¾AO-’1ÃØÀþ%êãÆ€——õ¤0IoOOÂìzÚi_{|å»+Ö¿C6W'ÕêozmÿÛ\”g[ r-Î3zû`vÁQYú *S¸ãÀŠZ²µÆæ-]m?¹ýïúa¬}æƒGY~Ç0šZ‚ÆXjÉc·l•^þË™&ƒ'E²e¸”^}‰U\ùs«íÍ”½f%ßûZÊÝ‘5|¨•\ðMÛrÖ÷›-ä{”}óËÑÂõXQ¡ååüøRÛüÅo5{‘Â=D.ï¨CM5aQ°žÿþã­qéJ«¹ï‘ľè~?Ò ·È)ÛNÞ¶V}ÏCæê’UÖÏÊîìØ8§m5±¾Gà­wW¶asC"j(E‰²Ï{ðéYz“X/[³Áêå5ÎMz¨½ðù7ØŸþñ¼ ŒôaÁ'±¯}øØƒô§Ýòû÷=>^‡?ègéÅÇO¾ê›-ÄÛvî™'X^nŽ-‘añ³;þÑb¿oüävýžH.øâ‡lÜÈ!6{Árµ)Òì‰*.ùò©ö³?ü½ùzn€…Æ’f¿½ÿi›»xUPGøáÞ7—¡KíìÓk>>(ÜÉ%³ùË?_ Ø¥«ê´ã6ç•[øm­L„}êGÞ¿¿=þJpÈhOKQZ‘uÂì"0zϪI®O¿:Çîþ×Ëæáwu2’–,²Õ2”â©Ï½§k½º¥MzDÛXœè²AÅ6zC• +¯¶WÞYb+”Mõ«ggÃõÇ`ŠÜeŒ¥ŒÝ±š̑͆’eªºíÏ [fù<É ?zJà *:ëÓ¶åÜ Z5/kð«üÅMV?ë-ó0¹âïŸk1½MÎ4в'޳†Ùs‚sü¡Tþê7æ¡€±~ý¬ôŠXö¨áë_š<¾¥7ʵúWß°êÛÿbî…*úò§-÷ðƒ‚ªò; 0–¶^rµåì;ÙJ/;/(÷ñZ•7Üj¦š °k¬Ù˜0|ÂÚß{àäpµÕÒß^yîé­ÊÃ7nyöp3íòQy£–ÉËó­Ož”v¿¾>oI³'(õ 92bþªpÀO¼ÿðÔ]íÜŽÛïdh¸G*U6ËCõ£ß=(#¤:uWó¶Ÿwõ­Û7å}›ñ¾5ÐÁ•Ʀ&»FÆÝÊu ÏWºÓÿöøË¶Yc²>þ>½pJ#ó–¬6‹DÒY¾VÞ†?ê!å¶{¿cÛÿ‚¦¯‚ÒÝHÀ¤&ýÜõÏÿÚS¯&ž-Ö—æÛ‚á¥Öˆ‘´ïLËK»‡oá°[#ãuÊÊ-Á¸Ökn{ľþñãm¼Âò²’QK-Ïê{[K»ùžç”©ófTýþ.«1ñV¶úwwZþQk|ÑpË·‡ek‹–´hmÍýZícOeu Ùó¸Üà ¶³u^h,ÕÏ™g Þµ¦­VûǃýqÏ6S^n&cÉ%kè}¶4–ôKg?þ•c£´·êÎ{­ÒXŠæ§ûÂýÁv]Å·´~  æèk7liQÏðÁý[l·wÃ7„B),ȳ=]!bmÝfí{nv³GÊÇG¹§¦­ñN2ç cšl#õ"g¾Bôžxéí°j{^ásn,Rjî¯üD{Gõ=þÒ;ÍûÏ:ýØÀó4l`뾸7fnå6¯‘'°ÈNþ'~ÿS¯¶0”¦Oc‡M›hyòœ½¹`™=õJ⥑‡ðyèâ%_QB™–²æ6´w剗æ´0”&ŽfG+=ûV TöõyÁ‡×åýßo¯Ñ¶÷øÖaÆQCÉà òòÚ{ùŒ=N÷¥^Ñ”eË@Úoº=2ã8kcnÆ"ÈˆŽ¹¡Ô¨¿·¿øv`(éžÚ’!%¶bó¾õÔ\QcoŒ`“Wê¹P!z7Üó¤÷¹(´º4ð0õõ°<Œ¥ÝüÍ͹mŒAþqGYÞ{HÛ"Oàj,5ÊŠŠ'm% ±©ùËýAˆ¼Ã¶â¯}Þ²F°œ=ÇÉ£Ô/<<í²qåšfCÉhZ³-áDLá4 °{¬N1–||QgäAî%qqâ?ó…ª%Æ'M¶v ’E\zÃ_›3ëÝ÷Ä«mK¢wîÇNÐÛÈÁA}*þݽ=¡Qàc›¶TTYÍÉ6mâhiª Ž ?öÝs”Æ军­–Ù ñûÌ´ý'íanÔ¹xv¿Þ\Ð|¬_ÓÃíBÙwâ(pæ™]|<Õ5'ÕaûNéð²©)nÿ'QŸŸ<^cº¾¯‡ŠP|,Õ®½Kyv!/(Ä1±äÇ{ªO½ÿˆdÈKXC\f—17=4”fi³ûôûâ²xh‰­,ÃPêé7¹N¿±o*³iËå1׋Ÿïyܾ÷é“­H/§ÜÃÔ— &žxwó·7kÈÀæä}XózêJ41C¸¯IóDE/sÒJîô}¬ä²ï·Lî LYn\e sRziÚÒ²þ`uúC)…º‘@˜ÝÎ/éFNnŽÞÅwBæ)¡C(ûÊ€ ¥°Ìÿ“|¯<&.1fÒÇ=yØ[ºäî ¥ðü±JtK^VQUKáþŽ,O;î ;b¿‰-NqïUôwïýGì×b¿oœxè>öµ?4^<ümgŒ¥ òœ{¦ÁPRC ‹”°ç`e|Až4—×4ÖgRƈy¹q;æå}MrKìÛwÅ6öµ~gbCC©Bžà?jþ7ÿÛ\-# C©÷ÜmÏôÎÈþ6}éf[¥—Q÷=ùZJì†ÆRï¹×ÒÆ ›ÌÇyºï­?¼FýKoñ¸—§3âiÀKfœŒeòó«ï~ÀêŸÙ.V–»3ƒqQ©—s ÝGÀC#ÜKââ$›å¥دcÞ%àwOO(£‡n{q–ùrd$ãœo¯Ú°9­±4bHëð¹â¤ÈÏs‰6‰’ö¦óάQ[¢’.kŸgvÔ¿ØÖlH¼üY½¾å9ÑóÛ³¾&eÜÔïzÖîxøÙ§úCc(ž4ÃÇ.y¦À¨ì9zXŸ7”œ†Rô[Ñ{×ý;ïc”<‰Ãcÿ}Kމ:«Ê˱w~‡ô.ž„cîˆR›¶l³Â§ØÑûï©i‰ˆ¾:†I6$²; 4­HÆá+l®Io,ë_«Y›4fÀÿÏ ´¦s <­·'}p©ûïkV}럭aŽÂV”V7{dbÜÑîì?׆:N`hʃ÷ýVlO*R᩸Cõß”òŠ–¿)îEJ'©Oy2›[ê±…»xÌMIšömÙZÝÜŒ|=˜y¨^:)öùé’ÒVûÃý;ZnÙºm~?6ñèŠÛÔùF¥>’-ó¹°d „W©É6é9æéWçÝZ2¤ˆ”à½ô—æš'äðßµûŸ|]ïóc8ú"¨—v­SÍ& ¯Sغç^²Ü¦–œ÷5«¼þVeÃ[ayGdÅ_Wºo¤’›¿øí6|NÛoK4|/¦±hòÊ<%‚È;ô€m'{,OHöè–5p€5•ëáM\ Ðõ†hN£¨øä³{ibÁtâoz¯üÍÍ©ÆÝuÕ7?fƒÊŠƒ¾ðÁ¾\Þt²Ei¹£2¸l÷¼)NM[îm,[(µu šó©!í|J›#}ØÙöûÜHQù¤’Vø|RÛ“šÐ;U²ºè77µ^¶!ÐÝB¯’?P{"9às'm,æ…@wß‹®¼Þ’ÁÅ6Hs1ÍQè²G2øT Š—­gȾ&K»ùŽ×>ü/Íet˜æ;Ú'Èz×ïšËZµ¨êŽ»­i}çBºÝ‹¯© Æ+åNle¸Îbšø1VZ¢ä •&‚ˆ´ºn{ }B[tú ²œ){ר¼ñv«}àÑöVÁq€@LSâÏ ΋ôܬvÒáÓ‚ù„R«ñ„["†ÐSƇäèïu¦ XŸôJEÇ/Eëð‰j£2\óoôñ¹@¢â“+zòˆ¨¸×mSyEsÑζ?õšýê<]I'ôU~çc}þ¶9‹W|ÂY¤w¨Ñÿ1›4ÓÀŠ:{}îb>¨_`(ÅbM}.¥xú˜…Þ}{]ë+®øY0–(ÕWªïŠ«®µš»ètŸš6n²Ê_ÞF^‰{}LÆRͽ[ÅÏoj®·àäã›×;º¯¨´jo#ž¤Ž¢ãxtŠ@™²ß½OÆQ(‰y¶w­ ‹‚åK2”þüè ÍeîÌ8lÚžÍÛ& '/ðÉb}rÚ¨,Z±Þ^œHTàåî½ò´Ý»Bj”-¯£²Ï„‘-¡×ÈÇ%]{ç?ƒ´ÚžÂxºî¨œz̶ÇðAÍEï?jš=3k¾UU' ŒÛ|Fo—&æYRæ;ŸÑÝÃøB9㄃ÃÕ^楤 ¿ç_/Ùø—1çéÅÛ#>¯ÓI‡ïÛœ|Ùê6ó7žžú ²RËÕK|ÿÝîK©Ä ÃËœï4=úsÁNÑù´` Sºî{ØØW?zœ}ù´cÒí6ŸÈõÂ/jéÒnû î½ùÆ'N²SÞ»Úó;[èÙàN—§ÊS{ïŒ$ŒÆ÷íËÖĉéäÐ}&Ø%_9µUúîtǶ·Ì'¿ýÜ)G|RÏñ6}êGØ9gß§(R9°Ù^¥x†Z¯ »•É؆l¹X{¨ Vvà=ô›Ös[ØóÀÕÊSè²aKe`{È¥É}Ið,õ¥»M_!Œ#àé²O;þ ûˆ&mõIc—¯Ùd땾w°’7ŒÜ߆ú Ü6ŒˆƈÁevéYæ]Z¹n³B.Êm€ÆEZfƒõ&±-¹âkg´µ+(?rú^æÚ–œtØ>vÂ!S5YmMð •åI'óEsÐsmxø›{¾Þä4[½¾ÜV®Ûhup>jÈ9¤LãšÒdûí5Æn¼ø i/±½}á Þ·ÃöÝÓVëëŠu›‚07:= D:æ?ùÎ'ÂSYB ×pcɵ±)áYªL†óÖïà÷&Úñ_<ÍT-²z=ˆ¯R]Ï+±Õói{[.þ_2ÍÔP…—5ÇÚw^~³ÅyßÝ{O;c‘¶±®Îþ牛÷¹aô‰q£ì³ã÷°~šbÀ¥Füs•øåço/´…bÊ•ûïmÇ%ç ËÕÇÕ.ñ‚þÆo]¸Ô6§„7‡Çõôåôý-G?–‹5¿Þ1j¯Ôȳä²QÆ’ÅyMAJñ¾ä]ÂXjï·…ã ô`n0 UJq×ΊrM7lgëÜÑyž¼_Ia ;:vGû=ÃßèaÝѱ]µßU7ÚòÌuÕu¨=@³äÎÉlxzv‰ï¤Û&WÆ–{ö(e(³ 4ÿ䚤!Kú„ü÷®-‰¥4દÚ{†n«Ù Uú­pãá†Ã§Ûe³Þ±çe¹¤«6[UX(C¬Ðö-ëgg¿ðº¹ÕÛäÇûO ŒÅ™oε¬\Ûîæ{(žKµ ,·tãØï9ÆR»r  @€@_#K–„fí„ñ’-v|Ö‹¯¡~îaûÒž{؇F³ê¿‡ÿýwÃ&ûé[ ¬J¼]ÜàûÀ¨a Ü`ËÎ|Ú˜6Jýñ0Åû—¯´g”Ê}¸ Â+§ïmÅÉ …ÎÄ ¾+Þ˜ÔÑÞ7–Âûz–Ú{no?c©·ßAÚ@€º™€Û¡‡!\ve^X¿)0–< n¢:vd°î¾™i2Ôþoÿ}ÃÉ ½¿.?œ6ÉŽ>$X÷1Fƒd0¹wk¼B¥¿öâÁ¸«9º¨@ž²¡V¦u7Ý£u„Æk¢1«Ÿ}öU…ÌY~—•¬·PcC‹†ØQIÆÛÆ+uôüÞ|<ÆRo¾{´€ ì&¡ÁÔäIüÉ¿ ecí6ãh°Œ¨ø˜&Oè°=%#"4Vjj–ŽHINŽ}hÔðà”¡º¶)¡Ì«Ø6ZXæËc‡ %¿Öd¤¸÷笽ÆÞŸýËú‡R6¾IÔûÏUkígJ0á^¨ó÷™Øl,ù>6*4”.—è1ë!‰¿=üÛ«´ÄŽ6ÈŒ;rCé¼Wgc¯NR[/ÛorÐ7ÄY±Æ>þôËöÈqGFÓuóÞíИ¥ áú½ˆ¡a܇¢ð c)ü°„ @hhv že)ù¿®”R,¡TižŸ¨¸§å…š<ü¯@IDATEeï~¥6&éIòr÷°„Rߨ±¶ “'éÂ}[gò|xù{[“³¦ísõñEÓ•hg|I±½İàÐÂdF¹© Ós—ËMó—(4¯Aaz[ì!÷õÉ‚rÿp#Ç¥Bû=;à1É }K*«ÏÒ±ÚŽK+ª«CÉÏqÃê<_Eòõ×4]&BØ %ç×…»ìvôŠŠüõV‹%ݲµÿxÜ*õ›Ví.½ì<Ë=쀠¼ü»?´†¹ ZÓž‚˜âO-ù…+Õ¥¾­í9m·“½Çhë÷ó˃k×?ÿ²Uüì†nGL.ìÒ‹¿ÔSs÷V÷ò¬®“ @Ø9- ¤.~4qc#”µÉ9œÂmO:æÆ=MQciEå6o’'rH7¥ú'§¨Rh]4Ñ‚'SxucÂó$ ktý—èc†Ú7N~8}²’S ¼:~ÜúˆwÌ·‡ø"HÀ°&âíz[“iGÅǹ¸‡k¦ÆU¥J%ÁˆJ˜-0,ó1`Ö‰P»ðütË„9¬Ïˆ‘œî¸L,ÃXêì]MÆ~úéù'g5>f‹–´¨-%{e‹}Ù(¹ä{–«xV—Í_ü¶5­Yבӻ÷X½É Œ;¿ªÜÍ]!§žl9Ó?±?ÝUR @=”€2S^×`ó¶¶4&ÚÓlƒs#ËÇ?|°Ýñî²§y¸Ú®æòåç_ æ] ðy•.|ííp³]Ës&³£‡ Æ ýäí¹öÊÆ-Ahž{yBÙX[¬ú¸¦!zFZ—4¦Üã•Mɹœ<ÙÅù¯½ÕÊgWLð=g—¯GŒáFò.¿ðî¿@¸ûÛÑ»[ éøìÏôî>ô¤Ö‹gö¸=,÷ ý¬äâïXá§·?ñeOj:m d:лÐUýÌÕ|e>îÆÕÇë\´ï¤`$¯ÿ¡«w˜‚»­vܹxE°ËÇù|[ž§0n¬2Ú}Eã‰\–+‹œOP»³2%iðükõ:{|õzÛ"ƒÇS”Ge^ye³áã×÷ùž&x¸Þ¨mc¢üxÏèâÙòÜûõŽ&Ÿ#õð»SG·1qÔIñ,}HÇàYê¯6Ι¾åq°yèY{%¦?ì c-¦?ÚÆ…‹­iíúöžjšKƽ6ù•âZ]bzSKºyãz£âJV2Ö=¨ñ-åaq°ôB¡Ë;ÆâUUÖ¸x©5®\Ó*äÏÛK†6é×CcýûY¶ÒU6ÌÞAJ¹“cúA DçÅ“oNZ4DÞþþ×_ZÌ6 @HàAeöÐq‡·êÙsšÏè–-£vZ´‚û–®RŠî¡AFºí1Ò>2z„mÒx§0» çwÚN5íÚõ_…éyêpO¾àÉ)&÷/±&;„î–WUÛ#÷äéÀ=„'Žp.L^è?k7ØÛzÆr“'kðTàžàÁ'Çõ9”îXÔÒKž·½e¹ž=cßg'Œ ê¹iþâíÞ®}n4Ç"‘Ví:©„±´³7Mi›”%K³LåS¶ù¥×› —¶ªvä苟°|…—E¥QÆRåU×Zü…AqîAÓ­ôòóÌ’F‘–ýö—;ºaÎ|‡:(8®ò—7Yí?Ÿ Ö ä…)üè©Ází“ÏZå5×ëÙÆYÿÿwU°îõ—çÒ`Ý?òO|¯ûÅæ1XáŽÆe+¬Bíi\²<,²âïžcyGloþÒ·­øë_²Ü¦Yý;ómëy—7—º’3e¢•þø’À˜ó}Ñ6§ëy.o»f¶~Dcz+„@€ г„ƒþ»ªUžvÛ …ׯvÇ¢¥ö*y{ܰøê‹³ì\…È&cÉ“>„†’{o~òöü66t´?*IÃaƒØÔ²Rû²æ2òú4{ž],/™DéYÆ“Rüäí¶©¾Î< °T/Ÿ_aäó6ý`ŸDB ŸãÉ烺D©Æ}–Oªë%+œùæ<[%oXGå®%+웓džÒáJ-Þc©«ïuGû°»ŽÇXÚIòqÅVßþ+þÎ9–5b¸|ød«¹÷áíÖZzùšÇàDÌVÚÉÒ«/±Š+nõ¯½)k]{#†Rp¬oK=ÉAh,¹!K9{Mh®2wò¶´š¹“'6—×»A—”ÂOœf…ŸûX¸Ùb™=f”õûåLÛúƒË­aþ¢û|£ø_¶Ü÷kUžZ5\?—}¿ÙPª¾õÏÍíM=Ö·ÝV~îšwÉ +øÐ‰ÍÛ¬@€ Ðû |÷å7;Ô‰o¾ôF›Çÿâ…æš*ž¸á—sÞµëæ-Ò„²Å6H¡mË”üa¥2ÈE†á§¹ÒYqCælM(;B)Ë]BƒæïJÝJðX§ß-Xj7Í[Ü|ýS“iʽ=>FËÅëóqS>!­'®Ø oÕ y¦¢mþ圅ê[ë>艂:¢]ºÒÜ ë'­^F$Ò~ŒYj?«6¬}ìik\0& d|xXZ[’̑͆’{l¶^þSsMõ=§^§³>¬×½þ–múä9-ÂÛ¶|ë"Ûü•ïYý+Û2ÂåLÙ–Þ2gâ¸æK»ñzd²5 Z(¡±”5x |üÉb¹g+o¸=¸Þæ/Ïêž{)(÷°¾¢/'Úž.ÝPjÚ¸ÉêžyÑ^|5,n±ôPÃÒ+ÎofRó·G¬Zí@€ Ð]<«Ý|Eæød·žj;jtteÜH ¥ÔzÝðyü¤£½bú” lï eöÉñ£ƒCgo.·Fy•¢²Qaƒ³”^ÜCøZî‰Õ¾u;t£+4ÈÚwGõXcÉc {M¤¾Ø•7ý>ø6eiLOáç>Úæ7+ï¤÷6ï«úý]V/#£Iƒ«w§5­ZìóäÙãÇá|q¹¢ãÉñH¾3¾UƒõÇîç4jУ‹5Šhò5åóé­I“Ò]†ûr&%^†Çci/‘=2a8øÎüãŽÒñG¤=.gê^­R‘§X¯P¼lwÝ*,/[FQ˜ÀÁÇ3™Ü·¾/gÒžß´@ F§7DD©{êy‹Gæ ˆîóõÜC5ÇTdà_ÍýÿH=$£¶«ôÆÈ%?™#Ú¹¨!-g€ ¾Càf%V¸W!qÓô 2ÿùœNK*«‚Ie}’Z¤çèQÆRºÊ\Í„ìR›Û5söìÊ[P%ïPîaÞš¢s>—6Û[ÖÍMÈ;ú°æõÔ•pÂÛÔòèvÃ+{¤1R.î9Š•$’ 4Ê8jR.ÿ¼ã–±4QºíMEÝK¯5W‘¥ùBiX¸(\m^6(´04–¢Ç†4.M¤ä ·[-#†’ï+:ë³V®ñO™*åEA׊ò{ÔŸU¦â¦_€ ^IÀCá<½¸+Òó ôø§:ϽïR«´Ó=]<õwÍ_¶ÂOžf¹J%¯¬lÕäFÍþì‰<Õ÷Ö^£ýé#Pƒ´Ý­ÎnYP?{Ž™²ñ™<A¥”t©Ÿ+ãHž%—l%yÈ “;(Vµþ•mƒ)ƒTåò<¹x¸^Û-6†!|¾¿i]ë?èx펳±ÔÊû”wà´`ìTξ“-︣­î‰g¼ÊŒ“ò$ÿâ‚m†½‚I3®Ÿt€ ô=rÌRô3|K¿¾¤í¤ =éfÕ(yAÓ†A“|üPª4­X•(’ñçóÕ+‰C¨M›5>I¶S š=zGâYãêg' œly–rö'«¦Éç¿k J·éóyh^n2Ô¯A)2£\4%xîþÓZ\.¦Üþžn<”†Húð°lGËeh©üÉuV}ç_›-úò§šçj.Ì€•ª¼|Û\˜ðìõ+Ö\Wþ/éY —ÐMº@€úg,…–¾ôCˊ͹©¨Ä¶&œzòr¦êwn³‰a–9? ä¼¯™{[áòíÆEK|Hñùß°²[¯ 7V×rÎúH¾ýÇ‹Hý·Í¯äÅžö»æÎ{G(IDá—>ie·ÿÚú_µå<=(÷U·ü1qLg?e¸UýöÎæ³}.ªì1#›·3aeî°„A:b@‘yRÿîb0eÂ¥èb±ôñà} ët€ÀŽô8c)4”ÂMðÚ/1çµ1vÔŸ³¿*™J<]ƒ*®øYb®¡”¬'ž®âªk­æ®–óÕþãqkX¼<]UÖ¸t¹ÆmhÞ×1¢›_îuJŸß©âÇ×¶Ï ñ¹£Ê¿yQ c-Ü×Ñeýó/o¥Ä¢¯~¾£UôØãוö·ÅC†í=°XÆRv ¡ÁÔcNÃvŽ@<¶Ù+¨Ò@]½‘@eublk,Ûöv®7v„6CØ…®Œ]xÎTz•‡ÎñÃûÙüUå¶¶_™Í>Ú&¯No8tæZ=gÓi_Øî©îÕÙøÁO¥=&®ñHÕ·þÙjþ|ŸecšÖ¸lEbn$…­¥J㲕V~î‚0»X©ÆA¥Y›?ÿÍÔS‚mOe¾Qº#©ûÏ‹V÷ìK–=rX0g“{“<ì®iSð,ØâôŠ™¿h±ºáçµÕïòó¯L=¼]ÛU×ýÎ\{ª<1e?¥éˆÙ°~ùÖO3w‡ß[_fÉcçßg$ dÅÖš&9ܪ™àôF[*ªÍŽYr0moìm† °k ô8c)a(iÞ äÛù…áiüΘ¶hCµýgÒTÛsíJËIcTìZT]_»%ÁœHí¬Ú“3D4´ó´öæ‰!–¯ ´}'p”X0t„-¬É€eM^jy¯”«pÊD(^ÂPÂXÊÌïJ<.cI²µ²:3;H¯2žÀfŒ¥Œ¿ÇtØy=0 ÏÇ*%Œ%7”rsŸ†–Z®Æl(îgîsàÎ÷œ °“6—ÚCÓ jÝ?Ïúç[Ž ¥ÜÿÎæZvž¥DÜ£OÏ‹e-ô._»É{t[iR T+|tÕºò`¬RNc|^ê~¶!@ A çK2˜r4¶%×Uoë‹‹òmʰD6¼Ù£ÇÙ+cóq#!°;Ôh’ä{>RóåZi^Ì&+µ|¥ÏÏϼKnèG=ìŽ6rÍ]Kàº+/xKÅ¥ušëlîb¢˜v-mjïj³*ô»©Q_áØìë~tɶ,B]}!êƒ ÐË ôHcÉCš< ÏÙüá³ @¡ Å®Lc{”%"ÿ5õ{aÂä^ŽŸæ÷F›ŠJ퇧tö »Ë6›:¼ÈŠ ,_ßSÿ®&BñäYJŽYê}¤Íí# ñù‘oÎßýã(Û×bŽ‚@‚À¬¹É„?±øý0@—ð‡7 ¡ç]B³çU’¼¿=¯a»¾E=ÎXò.‡axÊäžùV ‡Q7¨Ø†'þ Ÿš<ÍÞï«ÏîqC¯výã »…À»J_ûQÇÛ†……ÊPš28ßJ‹ ­° ÀŠ4ÿUŒûüü<ËI޹ã?Ýr›ºï¢Ù±ûübÏÏ^¯¨"ÑC÷çJ;C`ݦr{mÎÒ /+–“œ?bgjäܾLÀÿŸsÍÍN<›e)ñ ’9ò‰Ç õv8¼×™Ó»öõ¤GK~3Ü`òÎÀX’‘ä¢ÅEzƒ¯åÄÁE6ºÄÿ8ã6{ÔX»ù˜“í …æù¤ võ%ýíÞƒŽ²»>:½+’}>upŽ ì—øN* ^aÒ w#ßCHI¾+îDϪó¦+.~L?;/ÖÖÖÇþþlb‚èžÕBZÖîòµ OI<ºñÊ [ÎVÞúpJ Њ€?§¥ŠÏ‹é’ÓØ:«oê±l÷Ic©¸ ·U£Ó}Z”=Ò%“0–bÁgžÂï ¬¾¾ÐêêêµlPæì­7¹YU¶¢Â¬"¿Ðþ>í`{QaySW-³½Ö¬´¡å­Ó^gÀý¢ ÝH Zß½…CFØüa#õôàþßÀ‚¸-ÓX¥âb+..’&–E……WÉÇÙ‚×7jw_*+ëB=y>þä+óâï9pJlø þ»»E\mX°l½üöb·ÏÊÑw@ÈMK¹x–ºi¨&¿>‘À¨XQ3}Uz¤±ä7#Š'ƒHž~רlSžqÊ—Mñ&‹Çã6LÇ•æ×Øš­õ¶¾6Ë<;Ù3§ZR[meUUVRSeŵµ–ÝD¶ª¾ú%ow¿õôP¥Ä •2¾+V·¾¤_ oeiN“ix’õSÖ;7ŒJJŠ­TêK÷zºgÉ“<¸g ¯R»©÷úo¾ââ'ξxæÃúmúÐõwýÛ.üâ) Ëì»ÿ©ôúšÁØ\^e7ýõ)=ÉÆ—ûÍ3.žÁÝ¥kÝL ´0ñ»WT[¯¯˜.ÞÚùÔÍ-âr;K °®Ñ< OÁ^šG2/ùÊØomߺ¹=ÖXjÅ“±”çsS“5iÝ %7¨ü¡4';ËÖÔÙæš&ÛZgVÏ ¼Mîq2Ë:C ?«É㛬¬ f¥rA{¢‘ $T¥ÀP*- –îar£>:ÇRg®Ç9½”@nÁbõ5/­ÝX>î7÷=m_?ó„à·©—ö†fg ÏÚxýÝÿ¶­Uz-{Õr†|'»I—º™@øÐìËA¥zÓC´UÉhjÐ9õ mˆÇäÂ-ìܶ ß7íÎÉòϘäÄ-_?99y‰ÌŒ2”Ü{”0–%7˜ÜPòò¼æ±J‰ÉhÛ¾{2ÀÍ3¾¿þk3~ôá†z{î­…Ë‹õ§ÚÙgkE2 ìn›Ê+í:y=—¯Ù¨¦ÄÖè?þ\?㜪ÝÝ.®ß» „˾tõç²!2˜Vm®¶uK½ûö­\‘H\4r@að0ŒšÑíîSÒã%ÿã“sI†’L‰{þQº¡äóÙx×ZM²çcšêõÄRX p=k =Q¡7*zwÓ•E÷³ž¹ÂùhÃümß/MŽ,ƒ<ñýò±s‰ð;OæŒURè]h(¹ÇÉúÄÜJ=2oJ´«¬ï7̸ø³/¹òtU}÷œÅ«ú]}ëCöÕo#‡ ØW£J´Àü¥«íæ¿=Oz”Vé7íÔ민pYûÎæ(lŸ€?4'þïô—Ø1Þ?a, ßRc+™Ïä…ôNƒ*j­¨¶Ñ²t‡—¹±äÑ\ ïyïìYÇ[Ý£%ïNøàêFQb;Ï´-[^žyÊæÚºº¤ÁT¯$ÉñMAØž[ ÏTP H!àß3—Ä÷Í=šI¯¥æúrc)˜pVÆRèYò1K¾î”{”0”R€öÑÍ›g^úϯ_zõ‘uÖðàÚ[Ç_y˃ñ÷°Wì”÷ìoýJ<,@÷ðôà{â{õ%~AýÂÅ^ÍÏÍýð¯gœ¿¼{ZÀU2€ÿ·>£ùC´ÿ¿9j`±½³r‹yXψն\ÒûhP£[_4|ÌÍ!©ç Ïòë÷Øb¿ßásSïë]Ç[Üã%ï’ß¿AázøÇé7γåùƒlMaM`(¹w©¡^Yó”1/H±ïRP)}ž€§Bõÿ¾åhþ.ƒ”ëÆRrþ$ŸOÉ $ŸK)‘Ì!'ù’xÛÒçAÀ®»ò‚·ÎžñÓC­¾æúx¼écO¿:×^˜ýnüÀÉ{ÄöŸ<ÖöÙsd¸TèjÕ»ûæ‚eöº&œ5oy¼±©Ñg=»Ñ-–;äMè]W#ïÓõ…Ïbáÿ™þÿ¦ÿŸ9~P¡½³ºÒFm®²uò4ÕæmÑÛ¾(£6VY’;ø|’ãÿg¹Ó"a,% %¿ÿ}Ez…±ä7ÃoŠß¤m´‰·þ‡é®µùJ-®0<Iõ2–BÏRh0áYê+_éÎõ3ü^ù2üáwc<0–raža8ž/=KcÔ›äoY„| “ÖÏüÚŒ™‡74ĪiŽzáÍ…æªïT|ØÀÒØ€Òb++Õx·>œŽ5äŲó*e ù˜¤M[+m톭I)¨ÏÎÐ/Õ×Ï<ÿÎ_3!ž@øÿ¦?›¹úôÏ9²¬À–éa»¢.nSäezcLY‹Ì²ék£´§XYgc7&¼Jcû{ä–¼JÉšTƒ©§´yW·£×K"ñ ›°h›hõ‡é­n0…s0µ4”<ƒžBð‚<–‰p¼] •ú{àGß“a&¥0¤ øñOMñ¤õÄ ‰Igûš+º÷ÝÙÝÛâf\ò‚Zpô9—^u„YÓéŠ þˆ~§&._³Iƒí7íÞÆqõL$à?a>;ò}˽÷æ+/x3;IŸzð™,4”üY,WÑ>ñ3iH¡½±Ê§oi°‰k*lþðÒžÑhZ±]%µ 6iUyú}pa–yb‡Ä‹âÄKâ /9ni»eØÎ^e,9ûà¡Vÿlû#ÕØ%Íݨ‡Ø¼\Sò4ã>“–é<Ô!þÁ]Ôè÷*ñÝJÄäoÌ‚¬‹ O¦o»¡~ÿºèòT“ánºò¢çÕE×óÎñã‰ú¯¨ÑzÝ:J3ÞôËðîÓ½]I Û¨á+bÙ±å9ñ×ϼ„ä »’wuë(÷nß{!¼XÔóJ>~7ã«—×>çÒ„²Z›¿©Ñ†–×h®Ë&›7¼¿5„ÑÆ7h÷»GÉ ¥l9JrÍ& ÔpÜJCp#x›g)ñ´û[ݵ-pÂE† Ñš{±6>úp›ðdóFÒ¶ù˜áwn$'Sé…°„@ ýé%²Å›/úw+¡Û $ÿî!è,ëg\¸@çº"€@†ˆÅc›ý9£Ê'dí#¾X =Kþ0íczýáÚ—‹ëlL]•-«ŒÛ ¥Ÿ¶|“ÍÞÏjòcÐû¦ßMOæàc”‚Ð;­eÇeè&‡¸øíàžjz‚ ƒØïw¦>mÞš˜Q!‹­ŒÞ¸^k,…& MF‰¥ï ¥p=<‡%Ú"þ¸=~·Âe[çP@}›@,K>½‘¯¬NLâÙWhøÿþ𜫱½¡¢@™b kkƒ¤[CJë-Ëjd0e!y.Ùh+•†z¹²æ5ø¶Èn%0PFì¸uVXïy`Ìúç6Ùý²‚9}NÉ™=#^]“¹™ð6–Wôµ^¬$?z½±íÌöj ¿‹’b½-þB @" pH?~keu‡Nëíûÿ™šîáYîy(PÆØÂæi\”l«ÁÊ4T"[Óªª,«jRzñMÕ6L¡y‹óm}i¾m)ʳ&þëí¶¯B¡²ÜùJƒ·ÖZ±Æ(¹äÈ+:(¿Áe^ÁÀP’±T˜4˜‚iR‚0<·¹ÆÒú-rƒz^Ïs.÷IK;î‰ä]ò‡êúú áV£ %7–|x„KnVmQ‡õõÓ˜ŒeòñLM2¶ªÅËSŒ×j‰áÔõ_œ¼Æ¸åë{Y ù¯ò4i(ŠÅ²þ9 2”š4J®¹‘T\Td%%EV\\,UÆVy ÝkèY€3ÙPZºjƒm­¨–ÙÛš7¤hNÈÈ—}ÆXŠvšu@€ ÐUn˜yþܳ.™¹HÙxÇÏ[²Úö8ª«ªîñõ„Þ¥`*—¦<%Ù* mE§nñNxàF,&oFNUÖÇ­¢!ÛªL†“l)÷p÷­ÆÝv_å6±BM¿V”Õ`%y2¢¦Å ¦P½À«„{åZuuÕÔäYAÔkL“‡ë5Ê`rc)4šÂóÃehx…Û,Ó £è^/ Õïß‹œì„GɽJfd½óñJ£ä¡xn<¹¡”ŸLêɆ’1|àéYîÐtP—ß<ãœVP+ÏR<–µÌâ¶~ËÖĉQâ¬C½‚ÀÆòJ«©k(Ôß~cê@Å^Ñ @ ÈŠe]Òo|îÙY âGNß+6~Ô^Ø‹Î59ay8Þ¶óý!Û5ð()ä+L/^“L-^__ßìaJÌ“™ðHamc¸3k¡‘äãÅü>†’2ºAd/ô ge5Lžö=HèßaéTëW¢÷V}D¢S¸ÞÂX’§¢Fç>nɵÁ¥HÖ¼ÐıÔ5_˜mÆRÂëçÆ’gµËIK ãµ ð"ž$…äyhžSžÞï]&JϾ>Ïžxém¿SmÖ7o<眴³J·2–nÖg_2óAÙöŸ}mÎŒ¥®ù¾R º•Às³æ×é‚yJ úûn½0ƒ ÐÇ ôë_pvù–ši[¶VM»IÓ·>yR–¹¯` ¤ðAݶ=¡@-OžŒy•êꥺzLJôÅó„m‡âõ~]ÙÏm÷ÀçÃJxøÜ Ç+åÉxuSBéÁ}_èMòó3UÞY´ÒîüÇ Aè\tã?ÚV_[K~ xþM!¤Ÿ}åÅö‘ãÒvæÂj åè­._kk6”çé7®ªiÞ=½µ´€@o$ðÓóΫ”wé4 €xI¿Ç®¹ýûÆÇO´ýŠ{cw:Õæð!Ý—þ L‘±2>ÿR}}b&÷&%ÒŒ{¢‡džrâá]êúæ“BCGßÃæ{LáØ27œ/’îM0®,X&9„ç7W˜A+ÿymžýI†’<À>Né7ͼøÿ¶×½´ÆRaYÑ?*7T­Ú°¥bÄӯ͵cš²½:Øô w?ö_ŸPÆYw\sþù[{PÓh  >Aàæ™—.üê¥?>¡©©ñÁk7ºúÖGâ_øŸ£b{Ù'úv2a,%¾Ýhr¯Ec£«¼Iš54’Âð»h‚Œ¥bç–¡±ãËð>D ×0}¸InD…ûÂó:wÕž}VMm½Ý§1JO¾$ –'(vÛ¨œIçì¨ÕmºŒ¾zÉ•giÂß›KK ã3¿vzÌgöE žMàÙYó펇žõœ.› в'ýú¢‹ÖõìÓ:@™Kà«3gŽj¬¶‡Ìâû{/§Ocgœp¨ Xš¹n£gnü„zI¢©Ã'ëÈ6j¡¸£ܳäÑt-¦ÄX$/wC)Ü×Ѻ{Ëñþ½soÒƒO¿ßZYíÝUúÅØ…7ÿè’kÚÓ‡6¥»îº+û±7澩ïëÞGí?É>û¡¾•³=ð8=‰€Ïðã[nªª®UB¦Ø·o¾ò’_õ¤öÑ@}‘À¹3®+ilÜtYS<þ-=Såéá4>e܈ØÁSÇÙ“ÇZaA^ŸÃzBã)±L`÷õ9(»°Ã c(a0ùeBãÈ—™,‹W®³—ß^¬ gÇ7—W•é8;+ÿö3/}¼½}ß.%y—ŽU<Ïc2Èr>þ¾Cí¸C¦¶·^Žƒº‘@UM­]}Û#ñµ¶è·/öÜÈœ£Ž™1㸆nl—‚ í8wÆ'j¬ÎOôLõ‘ð°X,+>°_Qã°Aý³—•Ä ˆâa¬RøåèÂe¦E!*½°Mš:eí¦ò¦u›+â55uÙá>™ˆkb±øe'î7ù7gžyfã¶ò¯m×XòÓϹäÊoj¼Ý¯üMÈ9g›>i×Ê€@·ðÜëîú·Í_ºÚ_-*(Ê:Œð»nÃÏ… tˆÀ×/ž9Vù‰?®“>®p³;t2Cí& /Ò–x,~¿ò1þ%>fÐcžñ»Ý'GÜ¡±äÇjFêßÈÔÿ²,Óø';ñ0b¿‰öѱâÂüh]¬CÝHÀSûÿñïÏÇ5FÉGo.ÉŠå|ø¦+/˜ÕMàR€ @ £ ´ËX hÞ€¯Çã±kõ$»¨ ?~Ê{÷yZqO7ˆ@ÝCÀ,ÞýØKæó)¹ÈãûL~QÖé„Þu®@€@ß!Ða+笋g©“®UœíÁŽ©´¨ éà©ã³Þg¼í9zhß!GO!Ð6o­TF—%öÊÛ‹â‹V®KþÝÆ¶jåꑹ“~:cÆ™uÝØ.@€úKNEicç\ò£ÏiÐÔL¥Á’ÊËÍi2 ´qøà²œ¥EÙ}%ûFØ–èJUµuñÕë·Ô¯Ù¸%¶µ²¦y¢3eúlÐü¿+,ŠýðÚ‹/^ӕפ.@€ m:e,…§Ï˜ÏZÕtÕq”øÉXÜN—·i@¸% Ðu|µ¸ÅžÌŠÅþ’S»÷º‹.ÚÐuµS @€@:;e,E+ô$O¾½p|}SÓ^±x|/í%T—Õ½ëè²¶èoiA<76¿¬0gþ5矿µoô›^B€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ ô1±>Öß´Ý=ïíø³Â´;)„@±˜}ñš)±­ivQ@€ !r2¤;טå[\Š@ Šrd^#€ @M +£{Gç @€ t’ÆR'Áq @€ Ù0–2ûþÒ;t~ºÐ„4k«<Í¡;]4J5”ìt-T@€’0–ø*@ w¸MÍßÒývw× ”’×-Rwºò““Ç͈׫ŸV%s¥Ë¥g%+ü¯–›’ë, @€@§à¡SØ8 =†ÀFµdEJkFj»¿tµ4Õ`èê ~ŸÓ5“.þEJºò\ítOSAxP,³Uǥå~ý—¤.îar @è4Œ¥N£ãDôßS+\£r›6>/½Júké®”ƒ’•»wǽ9¡¤+\;=TosxP,§©Ž1Ò{¤Ÿè‚ú¨€ 4 ¯+è³üw`p½¦òíýN„)÷S  tåUªk‘4ÕÛ^:O+S¥mµ%<.ºtÏ’‹‡"vDèàIÒðüŽœË±€ ôÛ{ê#è&úkÔcÝ+=_êÌo¥¡ì§•ë¥ó¥a(ßßµþ}iø›áÇÌ‘+uñý×V¹s¬ÔÏù_iTÜ {PºUú–tt¶ôýÒíÉÍÚyoò€jéu»Gm{ò1í|WºQêãœ*¤–†â,œÏ‰aAry›–^~er;\Ÿ,¿ ,` @€@f|2£7ôh÷ªø¸¦Ë¤WK},‘L.ÒöoéW¤ÿ’~[z§tŠô'RísqÃæ)©./HŸ•¶UîÇø8¢ÉÒ!¾‘Oñºôé“Ò+¤ÏI÷‘þMº¿´-yS;^Mî\­¥·gyr;ÝâbÞ•Üá£7è!„oHJ]^–:Ÿ|#)> ñÿH½üÔdY¸89Yî @€2ˆ@Nõ…®@#ð)îFÊÑÓÜp/Ëw¤×Fʧký5éiR72ܸ:Gú t©{¨BC¥­rÒJÜ8.uCìɽnÄ}CúëäÒ ·tâûݰò6=#õë¶%ã´ãR©·ûé©ËÿIgJÝú©ôKR÷’¹¼7±>÷Ó§™î‰òu7¬6J]ܨ*—º±ˆ@€ Að,eÐͤ+è 7t¢†’Ÿ>[êÞ¤?øFDÞÔúi4\-²»S«np|AZ-½H•ßiã i~´p'Ö¿•¬ë-CC)¬Î½eÒOJý7q±ôéÁÒB©Ë1‰E`@º—)4¤Ü€:@ú˜´^Š@€ A0–2èfÒtÀŸÒÿ´Ê~%uOÉqR÷ê¸çå¿Ò®4”Tí%õß ·¥ Ò¨x2ˆã¥ŸîÄúÞÉs=T/UüZ>~©@ê^2—G¤yR÷B¹+]*½Yêrlð™Xfi=ÕèLîf@€@o&àÿÉ#€@ß$°*M·ÝÛs¹t‰ôqé-R÷ʬ”n•v¥LHVæ^«]-{&/ÐV¨ÜÒäþ¡É¥K.îAŠ%—îéòãæK•ºx^\†îy@€@†ÈÊ~Ð @ kÜ¡j~(õñIgHGKûKÿGZ)íJ©HV(]Ywj]î)s“X´úôP:—ÐhzFënzø'›$ucÉåßÒý¤nXº±ä¬<Á@€@†ÀXʰJw °úéÜJ—KÝ8ú«t…Ô='eÒ®6jæ©N—÷H³ƒµmnˆ¼%½o[ÑN­¹7ÈÅ›T¯‚aR7ŽB£§Nën&}ŸÔ%4–­sݘøxR½ªW¤‡J–þZú€t¢t¡tgŽ4'Kÿ(½&RÙËZ?Wº"R¶³«Pî=rcìÑÊÜ»uº4 Á w-×ÊR7üpŸ‡â¹±ô¨Ô½L @H 5%»¸ã.÷Nü.Êðpí"PœkŸž±W¬¼]÷¾ƒòÕäC¤eÒ¥îá Å=K>‰ì[Ò®4ü·h²tŒôݤºçiWˆ_cÿdų´ Ãëvŵ¨€ ^LÏR/¾y4»ˆ@­ê}¦ºÝpŠOmÖáb7Œæ$µÃ'wð„e:Þ @Û%€±äxâV«!ß]=x}»àÙÙ» T5âzw'h= @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ ^HàµyKº^å/J¯’–J£ò'm4I÷vãú^º–·û®n¸æ¯’×:´®•é—èÎû–é,é ôp9=¼}»¼yg_ò£‹ã?q—_ˆ ôV«n™yɧzxã³Õ¾~Ò*éÛ‘¶Æ´>Zê‚ë™Ò¥åRßﺻ$lwQ74 P×pF~M¤ýNÒ¡nÌÞ ½(yZwÞ·ä%Y@€v>o,éÅúT‹Û±»?Wíñb±E=¾Û8G«îeJ•±*x@ºŸt†ô{Òž Õˆ ÒªžÐÚ–@®Jˤnl†Â} I°„ Œ'•ñ=¤ƒ€À!¸0‰á½;À1Bû»òwa¸êÕÆ5ëUîÆèš6öç©|ªtpû£ÅùÚ-è¢uç1IêmÙž¸·ÅëH¼N7DÚ+Ρ½÷ÆëÝQ›Ãë¶·áñ;ºoaáuw¤a[XB€v)öþÇ»KAå€À.'àÞ7^RÅ _KݨZ) Ç…Þ„1*[.}Sšî7Ã=U+¤ß—ºxhßw¥ë¤«¤~î&éuR¿V(ãµâÞ°Âär˜–J·Jß’z=³¥ï—FÅÛòmé?¤^¿Ïòão’(Ýù¼Nö>9¹RoËõÒiTiãni¹ÔÛ }Wz†4*µáõýHêcÄž‘zÞîùÒJ£â×òãÝ€¸Rê ƒß›¿JSÛ¡¢àÞüPK¿¾×»Yú„ôÒt²£>þM'ý>yâYZz{N”¶ußÚË«ìLÿü<€ ЭÒ=øtk¸ Ð-J^ÅÌSåvœ+'ý¹Ô „I½Üe™ÔÖý!ÿ=ÒTùœ FJIîpãÉëY*!=_ú’Ô¯ñ'i(n8M–ú¸ªPFiåué)Ò'¥WHŸ“î#õ‡÷ý¥¡\¥•_Jݸš)½Tꆂ?Øÿ[ê“Έ_ó¶ä‰þPïÆ\…ôkÒ[¤¡ ×ÊkR7t¼ïß—þ?ižôé…ÒPr´âŒN–zÜõc½O{JËÁÒPjÅwCì<écÒ_KÝ`:Mz‡4*ÙÚxTz¹t½ÔÙüYêž¹¥Î>*í飷Ós}HºVšî¾u„…ªRàÒ¯’Ûã´tÍxé$ég¥•Ðk2+M7«ÌÕå0i¶ÔÑ­ÒTy5Y09eÇm{Û£²4¹á¬SåÔmGw¦nŽ.–ž$M•x²à€ä²½}L­§­mgÞ^_{ú×Öu)‡ ìrK»1€@·X¨+Þ‰«ù8”öÈuKî-qci¢Ô½Yn­”†r®VæI¿$uO”«‹‡t¹'áNßH#’eo¦Ù—®È ƒïJO–fIÝ(pÃðé±ÒÎH{Û°g²òÐhL½ÖÒdÁД›S¶w´Ùžãý>¸Œ“þÕWÚÒdy{ûØF5­Š;ËÂ+jOÿZ]@€@wÈê® q@ Ç=;jàý: BêÆOL†˜ýAëQq¯ÐO¥S¥ûI¿#}Rº·Ôý 4xÝ.©F¢´åçáÚü»Ô½\J”zhš{rܳÕYioʓأ …^œÐh k/ëŽsu’Nmé·’•¶·av´ì, ¯·£“¾,uÁ·Ò÷J£âenPUKC^Ñý]½^¯ ’ƤI©Üûð©ß“Ðmo½ªÐó“ëmHObÑF)† tŽ@NçNˤ³²Þ¶XüÉLê}éR«º´¶Þ_Ùãê‚3 ÊoKéÒm?%õñD7HýÁÜ©£¤—H뤞Y/øƒù÷¥IÁµXê^$¿Þ:©{©\•ž#½@ê^7¢Ž”ž% 7ßööºQÓ^¹_>!ucí>éÒñÒ/IûI,Ý"uñ¶þ]êÇ]&}EêÇz¸Û(©·ß³×u‡|WyŸôÒAÒ礃¥îås·I½}.éã‰SìL-Iݳ˜*M*èI,RÛÇ6 @€@&àãwÜІۋ ?oß4'„ûÂp²è!?KžçϱèŽäú8-Ÿ–zÝQ]­mèeŠV|ÿCaAré^?6zîKÚ>$¹?\üŸVÜP s#é‡ÒR©³ðò;¤.·H};]|TŠ´ñ{©aÝÞv•4ôÎh5Sõ¹^çËz©/Q ûêF`ªì©?Ï ÀPþ¬/Kwo¼O¾/µ/ÓU6;¹Ï÷»n’^(-F¥#}|D'†õ¢õ°/©÷­½,¼韟‡@€º•@ºnmƒzϫŷIÝSäÞ“¶dí˜ -‘.’¾&­¶Gü·i²tŒôݤú{ªŒS ˤ³¤n<…â×_.]tpÙ_Ç uCiŽ4Ld Õâ†È4éDéB©,~Îî7æÜ˜q#ËÛû‚Ô ¦¶¤½}­ r¥K¤nD¶%=‰E[m¤€ @»ŒÀª¹V:l—]Š!@€ ôn¹—å\©{x~#E @€ ôy>VÅ$Wyóð8€ d4Æ,eôí¥sè2žÝíLéjé?¥¤ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€Àÿo‡HýÝŽ@'hÀ€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€ 0`À€Î­†5–}IEND®B`‚neutron-12.1.1/doc/source/admin/archives/figures/vmware_nsx_ex2.graffle0000664000175000017500000000701213553660046026224 0ustar zuulzuul00000000000000‹í]kSÛFþ\~…Þ|-ˆ½_RšÒ$%@1IÚ 3ï{1j„äÈršÉÏJ¶u± 6…„¼]’ñE{ö~Îóœ½zë—O—‘÷ѤÃ0‰~„}ôÈ3q7é…qÿçG¯Ožm¨G¿$¶ü^Ö{٩׊O{a7{²öÃÖ{sõd»›…Í~peÒqÏ|ÚÚ´O!0Œ3Ó7é´µ9ý8y‡Ý ƒ$ß)Ï"iØ?l ³ªüJè'—qØO“ÑÀ?„O¿¦ÁùydÄÖæD¤"¹ð±->A‚*VÙÚœ&]a”%Û½¿FÃl–u–ŽÌæ4üiÐ}o³Œ{Ýà"ìÎÄ&uŸˆY‰á4lVŒÏŸÑº‡¾¬{Ÿë§âË—Zq­üN ç£v’(,ólDy±[ÊOÛ”T›·ëdW‘)%§%.!Éä}Z .ÂwÓ`\«,ØÁaµ8О³†˜~*?-84ï’är }Ø âÁð0 ûa© Ó¦Ì[²Ú‘yŒ$]ÆÛQØçÒÇóéçÒAÐ…$fò© ¢'Ô(ÿ0…/V/wƒÌ4‹Bfo`äaù˜’ÇŒ{?"ø›+M$I›ñ_YæýŒƒ4hFÚ ‡ƒ(¸êtƒh.gÐhøóº—ÞÏÞìs3gadN®s‘Põ¦l®`»Iwtiâ¬iƒ³†œoɉb-L´Øl]ÓÆQ±L©Oˆ¢Œ …Œ„p°ZɹÒXj /ÔÚã>åŒRļrùºÇˆÏ±ŠIø*8â¼n]mæUÚ×E00m6iÄ$Î^ÄçÉbûe*»¶^Œ+³lQ-«ŠõTòm1ºÃ‰;A<Üè˜Ëð,‰zdrkÿ6Õ¨¹:«™Z×2k¢HÙÙ”ˆLÒ¶­5߈Ǧ›qß*j³ëØ3×zça]×xÍÖ­I"gÕà²}%õö™Äè/ˆ¡¹ÔŒµFIEQšÑf”Zo6{vxô’ñuu~jbÀ‹‹6µùs¯ÓÌ« £tKiè§=À¨â+vCK¹RPÂ1®é‚i=ÒØ¤ÇA/Õ›jÎ>jÒ°•ó)[¬ÍõÐàNÓ쟂݆ùKwÐÇ„“ÓnÒMD/¾ Ggð`´öùô ;‹NÏÑéùp‡§çÝ‹ š yÏMôÑdà3ýô$»¶‹@ô§ÓÔôç§ýÔ˜Ø~8‹FÞóE‹ç±ü9ÆbŸ‚´wš}âÁ+<Ë߆á–¿)dß(ÍE¨ÎEËE8ÊE ¶}8DlºaÙ( ¢Óݵ5[“3¨ aÞi÷œxo^á/ |ˆ²9¢ k,+µ`ª»:¥r[Þ‘rÝÃHù9ÃÎ0c„+Ç;_“w°t¼ãxÇñŽãïw ÿ~›ï÷gý½,Þcr à?7AoqŪtpÝ8d¹Ñ ja‘£U{d6R­Ø æÜ×ÀåizÈ$Ÿ€h_!Š™ÒTÂúSF&ˆù°\ ²rŒÁŸ˜{´(n3c!²Ä>·Žæ’KɈjjnY…ehïN`7XˆíðÙÒx%tV+3»“­"n§i²Œ©1öM?è^ÕÄσh˜O–B`ÙÕ—š"ãºBN¡&£ÕJò6ì58º0<¼,“@Ž+áœS6?Û·‚ש ­ˆÀàlS-4(:c¾@‚h©$c!œÏ–ÌGª)§\ôuOjŸJ„¤"Rr%Á"¾×¿²×ÑêÓ=¼0_ýØÜˆ˜ÍÙ6XúŽüw Ã>.WÂ'Pcp W)…5‘ßÎwù¿×!'”Mˆ,ý69…G~º6ý·fýtä˜8ˆ3{&'é{|¹iµéöüSÌú˜aN,Ñ Aÿ|UþÁÂñãÇ?Ž$ÿà;ä-€A¤ækF0K\÷±Ê|¨¤D1bùcáË@X *Àʹ]—>tšúøJ=,þ9kÈú:’«¶ñlA¤a4Ø!mÀjfðW7¸[Ýp«ŽÌÜêÆ}­nx;Éå`”ï é™o¼¾äc¬€¸$ÓTgÍ´X>Ðâ>‚A–Ð@g˜Xn[÷ñ©°ì-¤†‡nyý9räÈÐ}ÐéÚÑÅ´Fyq˜u/¾5 ͯÉ2¤} ɸ¤vdÂò=^Ú®CÝ%É„YC¯˜{!UᣄàaŽûß Ýo!ýxë òüpŸh›´WÃÈÚÙ‹ÅöûгgÎ³à °n¢öeöh­È… ‹uÈ›!]7‰cк0‰¯G»·ó [ï·¡hÙ E  BÝzï’>RB`Hƒ(Åe¾Ëå[í6aÊHoà!Òç0¤aÉ„fJÞû6ðTlá¹°;q5Á+»ŠÑÝ@Îô=lèy™„qk;¡ÖvzðûÄžîݸN Ú¾¶R#¬±àÒºN\9×éáºNÒ¹NÎuzh®Ó‰=¶v½s0^ç3}sŸIÜÚg¢@ÒÚn‰AÜËT ŸÚ6ˆC(BB/t™Z®›b.“Û!½ÀCúšÛoáï0{W¡kÁ±4¦àÖì ̰„IÁsGøB úÉ9ÃZ‚ÊŸqÁ‰”HkÝÆ°&2ÉÕÉíKž‚‡UÀÕö…Q@PIVo1© sÃÜR†ÛF¼3õ‹½¡I?šô÷€µ1 EØ'À%µ(‰Š“çËP bŽjšT#V§v‡TóKCœpPÜ• “KðîW[€ÇLHÍ 9r4D¼Ýç;G^Ð7qö×Ò[Hû9AA碥n„ôUGHÔ €ÍdŠ©ÕpTHŽWã&Áì<—ã&ÇMŽ›ìm*ã 5ÞAçoúa|‡£$*µ¯ªaSLùº‡5ò%”šÚS1Ênâj=(c73æs$¤rrBR¹ýÃw°˜HÜþa·Ø1ŠÛ?|oX¦g.è"˜ÕT@J ÉL*âN°|UÂŽ9r t'Xìhfª™&Q´xÝÇÝù 6*ÝúbÇ–Á$ÑÜDk¬±{´[´ˆCòí!©(…$4§7Çþ~÷v¯¶ºƒ ðJÈH8Òê_º©û8fZ¯¥÷;ñÛowrÀ² °€ßXˆ–¾°7ºPÎæÌ^'F™o¯r¬Á‚*Lî€$˜ø\.QpòaÂÎq5¯¥gîЈ;4òMü“;cÑÞK5kœØ]xgŸˆ8ÑÊ ÄÝ@Ü ÄÝ@ü+\%qœŒ27ÿŽ~·a1ý_Ë)ò¶^8˜xñS ˆ!)edÞ d¡¾ºï¼pç…?x/|ÞIsw5a îp 9—¨ð²HÅ5@•P>’H N˜”rá ÊAÕ÷6a0·°ë êA»jôö®š„‘4‘Šb¬´ý¥+A}û [š p¸ÖÔ¹jÿþ]øGnÄ¿R‘'?¿öªP6ËhÚø‘ûI5ˆ¯ê1ÊjMNü•¤­ÑgíÜKSÉxhc•âí“R…t †'ÉòeíóZžî'Ý÷¦×üiûÚdVEúM8 ÏJƒ_M®âGAßÌð«]­Š>¼ÁdOÓ%Ä3fp’tºA¥3Õ.Ô:¸2i™ó °Œg+~Óe(…äApÙ2›gãáyñ#øºì…KoB3¾Qt‘ÒB ’Q¶Xm·ãð2ÈÌœR4.|±¡Ý0í&¯Âx7f Ǫ¡Ó¹lÇ ‚4°×ô5Ä‘òQ^̽¸p3W–^’ÍÆNÉ&œöMܯ€Ç4‚Š?’!B‰=YË0¿B³q2—’¯ñý"‹˜7 ÉA¢ä‡` S9}T(óLÓ_%½ð[êÝJOO%ó D60òz ÿñ~´-7—WžPÅþ&Á»Ÿ‰½‹ nF9H2³|eÓÐÄY­yÛ™~* ʲtâižB½.!ks±Étž&Y–\¾ Ò~…p+Î×4éó( ²ºAO>3\7Þ€Šž'iøw”;Œ*7ŸQ7é™^kFO·³¿{Ï_F¾ÅÈüñ4z÷ây²Í~úû‹ÎÞÅöÅákÄñÙÛ—Q·¿ß{‡¯ñ»‹³ço¢mßÙã''äå‡woÐö‹·{Û óìS4Þùõúâï›óìŸ4Vקߢ*¡Y~½¡YŽ‚Xku˜ÑRèù{ô¦¾¸²³¬\ó/7äbÕ÷Ø|‚3´¡nÒŸã°q¯ t’ î#ù:öçq‹= ücpòãèêfP:NƵ«ë®q@´áJ,EKÏsâBoœ„Ù<~îñÇ`Xs\Š—AšåŰ‹>…»·ÝÍÂËx{ydë<q׬÷u~™Ò•¿¦ö¯‡f/ÎÂôºf³¬ãù6Œ{Éx1LЉ‚³çÆêlÃ/¤éìïŒ,[fyÓÏ;ú-~þÞ§Aƒuþ·è“²Ô¢ŸLy¤U´Üͱ cëÞ†ùá=B` ¯%ªoʘ)j“5[Y:2›µðãQTúsЩgA%¸PVÃëã½ëj?á÷cÓ¯!جnê•oDHú´ú•䌴Tò]’\6ÝWŸ7ºÉ ½ ¢Ñ\s/œgi1™ZÓŸ=¬M,•5&åû ëy²ö?•¹¦neutron-12.1.1/doc/source/admin/archives/index.rst0000664000175000017500000000071013553660046022120 0ustar zuulzuul00000000000000================= Archived Contents ================= .. note:: Contents here have been moved from the unified version of Administration Guide. They will be merged into the Networking Guide gradually. .. toctree:: :maxdepth: 2 introduction.rst arch.rst config-plugins.rst config-agents.rst config-identity.rst adv-config.rst multi-dhcp-agents.rst use.rst adv-features.rst adv-operational-features.rst auth.rst neutron-12.1.1/doc/source/admin/archives/config-agents.rst0000664000175000017500000004027113553660047023544 0ustar zuulzuul00000000000000======================== Configure neutron agents ======================== Plug-ins typically have requirements for particular software that must be run on each node that handles data packets. This includes any node that runs nova-compute and nodes that run dedicated OpenStack Networking service agents such as ``neutron-dhcp-agent``, ``neutron-l3-agent``, ``neutron-metering-agent`` or ``neutron-lbaasv2-agent``. A data-forwarding node typically has a network interface with an IP address on the management network and another interface on the data network. This section shows you how to install and configure a subset of the available plug-ins, which might include the installation of switching software (for example, ``Open vSwitch``) and as agents used to communicate with the ``neutron-server`` process running elsewhere in the data center. Configure data-forwarding nodes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Node set up: NSX plug-in ------------------------ If you use the NSX plug-in, you must also install Open vSwitch on each data-forwarding node. However, you do not need to install an additional agent on each node. .. warning:: It is critical that you run an Open vSwitch version that is compatible with the current version of the NSX Controller software. Do not use the Open vSwitch version that is installed by default on Ubuntu. Instead, use the Open vSwitch version that is provided on the VMware support portal for your NSX Controller version. **To set up each node for the NSX plug-in** #. Ensure that each data-forwarding node has an IP address on the management network, and an IP address on the data network that is used for tunneling data traffic. For full details on configuring your forwarding node, see the `NSX Administration Guide `__. #. Use the NSX Administrator Guide to add the node as a Hypervisor by using the NSX Manager GUI. Even if your forwarding node has no VMs and is only used for services agents like ``neutron-dhcp-agent`` or ``neutron-lbaas-agent``, it should still be added to NSX as a Hypervisor. #. After following the NSX Administrator Guide, use the page for this Hypervisor in the NSX Manager GUI to confirm that the node is properly connected to the NSX Controller Cluster and that the NSX Controller Cluster can see the ``br-int`` integration bridge. Configure DHCP agent ~~~~~~~~~~~~~~~~~~~~ The DHCP service agent is compatible with all existing plug-ins and is required for all deployments where VMs should automatically receive IP addresses through DHCP. **To install and configure the DHCP agent** #. You must configure the host running the neutron-dhcp-agent as a data forwarding node according to the requirements for your plug-in. #. Install the DHCP agent: .. code-block:: console # apt-get install neutron-dhcp-agent #. Update any options in the ``/etc/neutron/dhcp_agent.ini`` file that depend on the plug-in in use. See the sub-sections. .. important:: If you reboot a node that runs the DHCP agent, you must run the :command:`neutron-ovs-cleanup` command before the ``neutron-dhcp-agent`` service starts. On Red Hat, SUSE, and Ubuntu based systems, the ``neutron-ovs-cleanup`` service runs the :command:`neutron-ovs-cleanup` command automatically. However, on Debian-based systems, you must manually run this command or write your own system script that runs on boot before the ``neutron-dhcp-agent`` service starts. Networking dhcp-agent can use `dnsmasq `__ driver which supports stateful and stateless DHCPv6 for subnets created with ``--ipv6_address_mode`` set to ``dhcpv6-stateful`` or ``dhcpv6-stateless``. For example: .. code-block:: console $ openstack subnet create --ip-version 6 --ipv6-ra-mode dhcpv6-stateful \ --ipv6-address-mode dhcpv6-stateful --network NETWORK --subnet-range \ CIDR SUBNET_NAME .. code-block:: console $ openstack subnet create --ip-version 6 --ipv6-ra-mode dhcpv6-stateless \ --ipv6-address-mode dhcpv6-stateless --network NETWORK --subnet-range \ CIDR SUBNET_NAME If no dnsmasq process for subnet's network is launched, Networking will launch a new one on subnet's dhcp port in ``qdhcp-XXX`` namespace. If previous dnsmasq process is already launched, restart dnsmasq with a new configuration. Networking will update dnsmasq process and restart it when subnet gets updated. .. note:: For dhcp-agent to operate in IPv6 mode use at least dnsmasq v2.63. After a certain, configured timeframe, networks uncouple from DHCP agents when the agents are no longer in use. You can configure the DHCP agent to automatically detach from a network when the agent is out of service, or no longer needed. This feature applies to all plug-ins that support DHCP scaling. For more information, see the `DHCP agent configuration options `__ listed in the OpenStack Configuration Reference. DHCP agent setup: OVS plug-in ----------------------------- These DHCP agent options are required in the ``/etc/neutron/dhcp_agent.ini`` file for the OVS plug-in: .. code-block:: bash [DEFAULT] enable_isolated_metadata = True interface_driver = openvswitch DHCP agent setup: NSX plug-in ----------------------------- These DHCP agent options are required in the ``/etc/neutron/dhcp_agent.ini`` file for the NSX plug-in: .. code-block:: bash [DEFAULT] enable_metadata_network = True enable_isolated_metadata = True interface_driver = openvswitch DHCP agent setup: Linux-bridge plug-in -------------------------------------- These DHCP agent options are required in the ``/etc/neutron/dhcp_agent.ini`` file for the Linux-bridge plug-in: .. code-block:: bash [DEFAULT] enabled_isolated_metadata = True interface_driver = linuxbridge Configure L3 agent ~~~~~~~~~~~~~~~~~~ The OpenStack Networking service has a widely used API extension to allow administrators and projects to create routers to interconnect L2 networks, and floating IPs to make ports on private networks publicly accessible. Many plug-ins rely on the L3 service agent to implement the L3 functionality. However, the following plug-ins already have built-in L3 capabilities: - Big Switch/Floodlight plug-in, which supports both the open source `Floodlight `__ controller and the proprietary Big Switch controller. .. note:: Only the proprietary BigSwitch controller implements L3 functionality. When using Floodlight as your OpenFlow controller, L3 functionality is not available. - IBM SDN-VE plug-in - MidoNet plug-in - NSX plug-in - PLUMgrid plug-in .. warning:: Do not configure or use ``neutron-l3-agent`` if you use one of these plug-ins. **To install the L3 agent for all other plug-ins** #. Install the ``neutron-l3-agent`` binary on the network node: .. code-block:: console # apt-get install neutron-l3-agent #. To uplink the node that runs ``neutron-l3-agent`` to the external network, create a bridge named ``br-ex`` and attach the NIC for the external network to this bridge. For example, with Open vSwitch and NIC eth1 connected to the external network, run: .. code-block:: console # ovs-vsctl add-br br-ex # ovs-vsctl add-port br-ex eth1 When the ``br-ex`` port is added to the ``eth1`` interface, external communication is interrupted. To avoid this, edit the ``/etc/network/interfaces`` file to contain the following information: .. code-block:: shell ## External bridge auto br-ex iface br-ex inet static address 192.27.117.101 netmask 255.255.240.0 gateway 192.27.127.254 dns-nameservers 8.8.8.8 ## External network interface auto eth1 iface eth1 inet manual up ifconfig $IFACE 0.0.0.0 up up ip link set $IFACE promisc on down ip link set $IFACE promisc off down ifconfig $IFACE down .. note:: The external bridge configuration address is the external IP address. This address and gateway should be configured in ``/etc/network/interfaces``. After editing the configuration, restart ``br-ex``: .. code-block:: console # ifdown br-ex && ifup br-ex Do not manually configure an IP address on the NIC connected to the external network for the node running ``neutron-l3-agent``. Rather, you must have a range of IP addresses from the external network that can be used by OpenStack Networking for routers that uplink to the external network. This range must be large enough to have an IP address for each router in the deployment, as well as each floating IP. #. The ``neutron-l3-agent`` uses the Linux IP stack and iptables to perform L3 forwarding and NAT. In order to support multiple routers with potentially overlapping IP addresses, ``neutron-l3-agent`` defaults to using Linux network namespaces to provide isolated forwarding contexts. As a result, the IP addresses of routers are not visible simply by running the :command:`ip addr list` or :command:`ifconfig` command on the node. Similarly, you cannot directly :command:`ping` fixed IPs. To do either of these things, you must run the command within a particular network namespace for the router. The namespace has the name ``qrouter-ROUTER_UUID``. These example commands run in the router namespace with UUID 47af3868-0fa8-4447-85f6-1304de32153b: .. code-block:: console # ip netns exec qrouter-47af3868-0fa8-4447-85f6-1304de32153b ip addr list .. code-block:: console # ip netns exec qrouter-47af3868-0fa8-4447-85f6-1304de32153b ping FIXED_IP .. important:: If you reboot a node that runs the L3 agent, you must run the :command:`neutron-ovs-cleanup` command before the ``neutron-l3-agent`` service starts. On Red Hat, SUSE and Ubuntu based systems, the neutron-ovs-cleanup service runs the :command:`neutron-ovs-cleanup` command automatically. However, on Debian-based systems, you must manually run this command or write your own system script that runs on boot before the neutron-l3-agent service starts. **How routers are assigned to L3 agents** By default, a router is assigned to the L3 agent with the least number of routers (LeastRoutersScheduler). This can be changed by altering the ``router_scheduler_driver`` setting in the configuration file. Configure metering agent ~~~~~~~~~~~~~~~~~~~~~~~~ The Neutron Metering agent resides beside neutron-l3-agent. **To install the metering agent and configure the node** #. Install the agent by running: .. code-block:: console # apt-get install neutron-metering-agent #. If you use one of the following plug-ins, you need to configure the metering agent with these lines as well: - An OVS-based plug-in such as OVS, NSX, NEC, BigSwitch/Floodlight: .. code-block:: ini interface_driver = openvswitch - A plug-in that uses LinuxBridge: .. code-block:: ini interface_driver = linuxbridge #. To use the reference implementation, you must set: .. code-block:: ini driver = iptables #. Set the ``service_plugins`` option in the ``/etc/neutron/neutron.conf`` file on the host that runs ``neutron-server``: .. code-block:: ini service_plugins = metering If this option is already defined, add ``metering`` to the list, using a comma as separator. For example: .. code-block:: ini service_plugins = router,metering Configure Load-Balancer-as-a-Service (LBaaS v2) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For the back end, use either ``Octavia`` or ``HAProxy``. This example uses Octavia. **To configure LBaaS V2** #. Install Octavia using your distribution's package manager. #. Edit the ``/etc/neutron/neutron_lbaas.conf`` file and change the ``service_provider`` parameter to enable Octavia: .. code-block:: ini service_provider = LOADBALANCERV2:Octavia:neutron_lbaas.drivers.octavia.driver.OctaviaDriver:default #. Edit the ``/etc/neutron/neutron.conf`` file and add the ``service_plugins`` parameter to enable the load-balancing plug-in: .. code-block:: ini service_plugins = neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPluginv2 If this option is already defined, add the load-balancing plug-in to the list using a comma as a separator. For example: .. code-block:: ini service_plugins = [already defined plugins],neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPluginv2 #. Create the required tables in the database: .. code-block:: console # neutron-db-manage --subproject neutron-lbaas upgrade head #. Restart the ``neutron-server`` service. #. Enable load balancing in the Project section of the dashboard. .. warning:: Horizon panels are enabled only for LBaaSV1. LBaaSV2 panels are still being developed. By default, the ``enable_lb`` option is ``True`` in the `local_settings.py` file. .. code-block:: python OPENSTACK_NEUTRON_NETWORK = { 'enable_lb': True, ... } Apply the settings by restarting the web server. You can now view the Load Balancer management options in the Project view in the dashboard. Configure Hyper-V L2 agent ~~~~~~~~~~~~~~~~~~~~~~~~~~ Before you install the OpenStack Networking Hyper-V L2 agent on a Hyper-V compute node, ensure the compute node has been configured correctly using these `instructions `__. **To install the OpenStack Networking Hyper-V agent and configure the node** #. Download the OpenStack Networking code from the repository: .. code-block:: console > cd C:\OpenStack\ > git clone https://git.openstack.org/openstack/neutron #. Install the OpenStack Networking Hyper-V Agent: .. code-block:: console > cd C:\OpenStack\neutron\ > python setup.py install #. Copy the ``policy.json`` file: .. code-block:: console > xcopy C:\OpenStack\neutron\etc\policy.json C:\etc\ #. Create the ``C:\etc\neutron-hyperv-agent.conf`` file and add the proper configuration options and the `Hyper-V related options `__. Here is a sample config file: .. code-block:: ini [DEFAULT] control_exchange = neutron policy_file = C:\etc\policy.json rpc_backend = neutron.openstack.common.rpc.impl_kombu rabbit_host = IP_ADDRESS rabbit_port = 5672 rabbit_userid = guest rabbit_password = logdir = C:\OpenStack\Log logfile = neutron-hyperv-agent.log [AGENT] polling_interval = 2 physical_network_vswitch_mappings = *:YOUR_BRIDGE_NAME enable_metrics_collection = true [SECURITYGROUP] firewall_driver = hyperv.neutron.security_groups_driver.HyperVSecurityGroupsDriver enable_security_group = true #. Start the OpenStack Networking Hyper-V agent: .. code-block:: console > C:\Python27\Scripts\neutron-hyperv-agent.exe --config-file C:\etc\neutron-hyperv-agent.conf Basic operations on agents ~~~~~~~~~~~~~~~~~~~~~~~~~~ This table shows examples of Networking commands that enable you to complete basic operations on agents. .. list-table:: :widths: 50 50 :header-rows: 1 * - Operation - Command * - List all available agents. - ``$ openstack network agent list`` * - Show information of a given agent. - ``$ openstack network agent show AGENT_ID`` * - Update the admin status and description for a specified agent. The command can be used to enable and disable agents by using ``--admin-state-up`` parameter set to ``False`` or ``True``. - ``$ neutron agent-update --admin-state-up False AGENT_ID`` * - Delete a given agent. Consider disabling the agent before deletion. - ``$ openstack network agent delete AGENT_ID`` **Basic operations on Networking agents** See the `OpenStack Command-Line Interface Reference `__ for more information on Networking commands. neutron-12.1.1/doc/source/admin/archives/use.rst0000664000175000017500000004220513553660047021613 0ustar zuulzuul00000000000000============== Use Networking ============== You can manage OpenStack Networking services by using the service command. For example: .. code-block:: console # service neutron-server stop # service neutron-server status # service neutron-server start # service neutron-server restart Log files are in the ``/var/log/neutron`` directory. Configuration files are in the ``/etc/neutron`` directory. Administrators and projects can use OpenStack Networking to build rich network topologies. Administrators can create network connectivity on behalf of projects. Core Networking API features ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After installing and configuring Networking (neutron), projects and administrators can perform create-read-update-delete (CRUD) API networking operations. This is performed using the Networking API directly with either the :command:`neutron` command-line interface (CLI) or the :command:`openstack` CLI. The :command:`neutron` CLI is a wrapper around the Networking API. Every Networking API call has a corresponding :command:`neutron` command. The :command:`openstack` CLI is a common interface for all OpenStack projects, however, not every API operation has been implemented. For the list of available commands, see `Command List `__. The :command:`neutron` CLI includes a number of options. For details, see `Create and manage networks `__. Basic Networking operations --------------------------- To learn about advanced capabilities available through the :command:`neutron` command-line interface (CLI), read the networking section `Create and manage networks `__ in the OpenStack End User Guide. This table shows example :command:`openstack` commands that enable you to complete basic network operations: +-------------------------+-------------------------------------------------+ | Operation | Command | +=========================+=================================================+ |Creates a network. | | | | | | | ``$ openstack network create net1`` | +-------------------------+-------------------------------------------------+ |Creates a subnet that is | | |associated with net1. | | | | | | | ``$ openstack subnet create subnet1`` | | | ``--subnet-range 10.0.0.0/24`` | | | ``--network net1`` | +-------------------------+-------------------------------------------------+ |Lists ports for a | | |specified project. | | | | | | | ``$ openstack port list`` | +-------------------------+-------------------------------------------------+ |Lists ports for a | | |specified project | | |and displays the ``ID``, | | |``Fixed IP Addresses`` | | | | | | | ``$ openstack port list -c ID`` | | | ``-c "Fixed IP Addresses`` | +-------------------------+-------------------------------------------------+ |Shows information for a | | |specified port. | | | | ``$ openstack port show PORT_ID`` | +-------------------------+-------------------------------------------------+ **Basic Networking operations** .. note:: The ``device_owner`` field describes who owns the port. A port whose ``device_owner`` begins with: - ``network`` is created by Networking. - ``compute`` is created by Compute. Administrative operations ------------------------- The administrator can run any :command:`openstack` command on behalf of projects by specifying an Identity ``project`` in the command, as follows: .. code-block:: console $ openstack network create --project PROJECT_ID NETWORK_NAME For example: .. code-block:: console $ openstack network create --project 5e4bbe24b67a4410bc4d9fae29ec394e net1 .. note:: To view all project IDs in Identity, run the following command as an Identity service admin user: .. code-block:: console $ openstack project list Advanced Networking operations ------------------------------ This table shows example CLI commands that enable you to complete advanced network operations: +-------------------------------+--------------------------------------------+ | Operation | Command | +===============================+============================================+ |Creates a network that | | |all projects can use. | | | | | | | ``$ openstack network create`` | | | ``--share public-net`` | +-------------------------------+--------------------------------------------+ |Creates a subnet with a | | |specified gateway IP address. | | | | | | | ``$ openstack subnet create subnet1`` | | | ``--gateway 10.0.0.254 --network net1`` | +-------------------------------+--------------------------------------------+ |Creates a subnet that has | | |no gateway IP address. | | | | | | | ``$ openstack subnet create subnet1`` | | | ``--no-gateway --network net1`` | +-------------------------------+--------------------------------------------+ |Creates a subnet with DHCP | | |disabled. | | | | | | | ``$ openstack subnet create subnet1`` | | | ``--network net1 --no-dhcp`` | +-------------------------------+--------------------------------------------+ |Specifies a set of host routes | | | | | | | ``$ openstack subnet create subnet1`` | | | ``--network net1 --host-route`` | | | ``destination=40.0.1.0/24,`` | | | ``gateway=40.0.0.2`` | +-------------------------------+--------------------------------------------+ |Creates a subnet with a | | |specified set of dns name | | |servers. | | | | | | | ``$ openstack subnet create subnet1`` | | | ``--network net1 --dns-nameserver`` | | | ``8.8.4.4`` | +-------------------------------+--------------------------------------------+ |Displays all ports and | | |IPs allocated on a network. | | | | | | | ``$ openstack port list --network NET_ID`` | +-------------------------------+--------------------------------------------+ **Advanced Networking operations** .. note:: During port creation and update, specific extra-dhcp-options can be left blank. For example, ``router`` and ``classless-static-route``. This causes dnsmasq to have an empty option in the ``opts`` file related to the network. For example: .. code-block:: console tag:tag0,option:classless-static-route, tag:tag0,option:router, Use Compute with Networking ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Basic Compute and Networking operations --------------------------------------- This table shows example :command:`openstack` commands that enable you to complete basic VM networking operations: +----------------------------------+-----------------------------------------+ | Action | Command | +==================================+=========================================+ |Checks available networks. | | | | | | | ``$ openstack network list`` | +----------------------------------+-----------------------------------------+ |Boots a VM with a single NIC on | | |a selected Networking network. | | | | | | | ``$ openstack server create --image`` | | | ``IMAGE --flavor FLAVOR --nic`` | | | ``net-id=NET_ID VM_NAME`` | +----------------------------------+-----------------------------------------+ |Searches for ports with a | | |``device_id`` that matches the | | |Compute instance UUID. See :ref: | | |`Create and delete VMs` | | | | | | |``$ openstack port list --server VM_ID`` | +----------------------------------+-----------------------------------------+ |Searches for ports, but shows | | |only the ``mac_address`` of | | |the port. | | | | | | | ``$ openstack port list -c`` | | | ``"MAC Address" --server VM_ID`` | +----------------------------------+-----------------------------------------+ |Temporarily disables a port from | | |sending traffic. | | | | | | | ``$ openstack port set PORT_ID`` | | | ``--disable`` | +----------------------------------+-----------------------------------------+ **Basic Compute and Networking operations** .. note:: The ``device_id`` can also be a logical router ID. .. note:: - When you boot a Compute VM, a port on the network that corresponds to the VM NIC is automatically created and associated with the default security group. You can configure `security group rules <#enable-ping-and-ssh-on-vms-security-groups>`__ to enable users to access the VM. .. _Create and delete VMs: - When you delete a Compute VM, the underlying Networking port is automatically deleted. Advanced VM creation operations ------------------------------- This table shows example :command:`openstack` commands that enable you to complete advanced VM creation operations: +-------------------------------------+--------------------------------------+ | Operation | Command | +=====================================+======================================+ |Boots a VM with multiple | | |NICs. | | | | ``$ openstack server create --image``| | | ``IMAGE --flavor FLAVOR --nic`` | | | ``net-id=NET_ID VM_NAME`` | | | ``net-id=NET2-ID VM_NAME`` | +-------------------------------------+--------------------------------------+ |Boots a VM with a specific IP | | |address. Note that you cannot | | |use the ``--max`` or ``--min`` | | |parameters in this case. | | | | | | | ``$ openstack server create --image``| | | ``IMAGE --flavor FLAVOR --nic`` | | | ``net-id=NET_ID VM_NAME`` | | | ``v4-fixed-ip=IP-ADDR VM_NAME`` | +-------------------------------------+--------------------------------------+ |Boots a VM that connects to all | | |networks that are accessible to the | | |project who submits the request | | |(without the ``--nic`` option). | | | | | | | ``$ openstack server create --image``| | | ``IMAGE --flavor FLAVOR`` | +-------------------------------------+--------------------------------------+ **Advanced VM creation operations** .. note:: Cloud images that distribution vendors offer usually have only one active NIC configured. When you boot with multiple NICs, you must configure additional interfaces on the image or the NICs are not reachable. The following Debian/Ubuntu-based example shows how to set up the interfaces within the instance in the ``/etc/network/interfaces`` file. You must apply this configuration to the image. .. code-block:: bash # The loopback network interface auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp auto eth1 iface eth1 inet dhcp Enable ping and SSH on VMs (security groups) -------------------------------------------- You must configure security group rules depending on the type of plug-in you are using. If you are using a plug-in that: - Implements Networking security groups, you can configure security group rules directly by using the :command:`openstack security group rule create` command. This example enables ``ping`` and ``ssh`` access to your VMs. .. code-block:: console $ openstack security group rule create --protocol icmp \ --ingress SECURITY_GROUP .. code-block:: console $ openstack security group rule create --protocol tcp \ --egress --description "Sample Security Group" SECURITY_GROUP - Does not implement Networking security groups, you can configure security group rules by using the :command:`openstack security group rule create` or :command:`euca-authorize` command. These :command:`openstack` commands enable ``ping`` and ``ssh`` access to your VMs. .. code-block:: console $ openstack security group rule create --protocol icmp default $ openstack security group rule create --protocol tcp --dst-port 22:22 default .. note:: If your plug-in implements Networking security groups, you can also leverage Compute security groups by setting ``security_group_api = neutron`` in the ``nova.conf`` file. After you set this option, all Compute security group commands are proxied to Networking. neutron-12.1.1/doc/source/admin/archives/config-plugins.rst0000664000175000017500000001712113553660046023741 0ustar zuulzuul00000000000000====================== Plug-in configurations ====================== For configurations options, see `Networking configuration options `__ in Configuration Reference. These sections explain how to configure specific plug-ins. Configure Big Switch (Floodlight REST Proxy) plug-in ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #. Edit the ``/etc/neutron/neutron.conf`` file and add this line: .. code-block:: ini core_plugin = bigswitch #. In the ``/etc/neutron/neutron.conf`` file, set the ``service_plugins`` option: .. code-block:: ini service_plugins = neutron.plugins.bigswitch.l3_router_plugin.L3RestProxy #. Edit the ``/etc/neutron/plugins/bigswitch/restproxy.ini`` file for the plug-in and specify a comma-separated list of controller\_ip:port pairs: .. code-block:: ini server = CONTROLLER_IP:PORT For database configuration, see `Install Networking Services `__ in the Installation Tutorials and Guides. (The link defaults to the Ubuntu version.) #. Restart the ``neutron-server`` to apply the settings: .. code-block:: console # service neutron-server restart Configure Brocade plug-in ~~~~~~~~~~~~~~~~~~~~~~~~~ #. Install the Brocade-modified Python netconf client (ncclient) library, which is available at https://github.com/brocade/ncclient: .. code-block:: console $ git clone https://github.com/brocade/ncclient #. As root, run this command: .. code-block:: console # cd ncclient;python setup.py install #. Edit the ``/etc/neutron/neutron.conf`` file and set the following option: .. code-block:: ini core_plugin = brocade #. Edit the ``/etc/neutron/plugins/brocade/brocade.ini`` file for the Brocade plug-in and specify the admin user name, password, and IP address of the Brocade switch: .. code-block:: ini [SWITCH] username = ADMIN password = PASSWORD address = SWITCH_MGMT_IP_ADDRESS ostype = NOS For database configuration, see `Install Networking Services `__ in any of the Installation Tutorials and Guides in the `OpenStack Documentation index `__. (The link defaults to the Ubuntu version.) #. Restart the ``neutron-server`` service to apply the settings: .. code-block:: console # service neutron-server restart Configure NSX-mh plug-in ~~~~~~~~~~~~~~~~~~~~~~~~ The instructions in this section refer to the VMware NSX-mh platform, formerly known as Nicira NVP. #. Install the NSX plug-in: .. code-block:: console # apt-get install python-vmware-nsx #. Edit the ``/etc/neutron/neutron.conf`` file and set this line: .. code-block:: ini core_plugin = vmware Example ``neutron.conf`` file for NSX-mh integration: .. code-block:: ini core_plugin = vmware rabbit_host = 192.168.203.10 allow_overlapping_ips = True #. To configure the NSX-mh controller cluster for OpenStack Networking, locate the ``[default]`` section in the ``/etc/neutron/plugins/vmware/nsx.ini`` file and add the following entries: - To establish and configure the connection with the controller cluster you must set some parameters, including NSX-mh API endpoints, access credentials, and optionally specify settings for HTTP timeouts, redirects and retries in case of connection failures: .. code-block:: ini nsx_user = ADMIN_USER_NAME nsx_password = NSX_USER_PASSWORD http_timeout = HTTP_REQUEST_TIMEOUT # (seconds) default 75 seconds retries = HTTP_REQUEST_RETRIES # default 2 redirects = HTTP_REQUEST_MAX_REDIRECTS # default 2 nsx_controllers = API_ENDPOINT_LIST # comma-separated list To ensure correct operations, the ``nsx_user`` user must have administrator credentials on the NSX-mh platform. A controller API endpoint consists of the IP address and port for the controller; if you omit the port, port 443 is used. If multiple API endpoints are specified, it is up to the user to ensure that all these endpoints belong to the same controller cluster. The OpenStack Networking VMware NSX-mh plug-in does not perform this check, and results might be unpredictable. When you specify multiple API endpoints, the plug-in takes care of load balancing requests on the various API endpoints. - The UUID of the NSX-mh transport zone that should be used by default when a project creates a network. You can get this value from the Transport Zones page for the NSX-mh manager: Alternatively the transport zone identifier can be retrieved by query the NSX-mh API: ``/ws.v1/transport-zone`` .. code-block:: ini default_tz_uuid = TRANSPORT_ZONE_UUID - .. code-block:: ini default_l3_gw_service_uuid = GATEWAY_SERVICE_UUID .. warning:: Ubuntu packaging currently does not update the neutron init script to point to the NSX-mh configuration file. Instead, you must manually update ``/etc/default/neutron-server`` to add this line: .. code-block:: ini NEUTRON_PLUGIN_CONFIG = /etc/neutron/plugins/vmware/nsx.ini For database configuration, see `Install Networking Services `__ in the Installation Tutorials and Guides. #. Restart ``neutron-server`` to apply settings: .. code-block:: console # service neutron-server restart .. warning:: The neutron NSX-mh plug-in does not implement initial re-synchronization of Neutron resources. Therefore resources that might already exist in the database when Neutron is switched to the NSX-mh plug-in will not be created on the NSX-mh backend upon restart. Example ``nsx.ini`` file: .. code-block:: ini [DEFAULT] default_tz_uuid = d3afb164-b263-4aaa-a3e4-48e0e09bb33c default_l3_gw_service_uuid=5c8622cc-240a-40a1-9693-e6a5fca4e3cf nsx_user=admin nsx_password=changeme nsx_controllers=10.127.0.100,10.127.0.200:8888 .. note:: To debug :file:`nsx.ini` configuration issues, run this command from the host that runs neutron-server: .. code-block:: console # neutron-check-nsx-config PATH_TO_NSX.INI This command tests whether ``neutron-server`` can log into all of the NSX-mh controllers and the SQL server, and whether all UUID values are correct. Configure PLUMgrid plug-in ~~~~~~~~~~~~~~~~~~~~~~~~~~ #. Edit the ``/etc/neutron/neutron.conf`` file and set this line: .. code-block:: ini core_plugin = plumgrid #. Edit the [PLUMgridDirector] section in the ``/etc/neutron/plugins/plumgrid/plumgrid.ini`` file and specify the IP address, port, admin user name, and password of the PLUMgrid Director: .. code-block:: ini [PLUMgridDirector] director_server = "PLUMgrid-director-ip-address" director_server_port = "PLUMgrid-director-port" username = "PLUMgrid-director-admin-username" password = "PLUMgrid-director-admin-password" For database configuration, see `Install Networking Services `__ in the Installation Tutorials and Guides. #. Restart the ``neutron-server`` service to apply the settings: .. code-block:: console # service neutron-server restart neutron-12.1.1/doc/source/admin/deploy-ovs-ha-dvr.rst0000664000175000017500000005317213553660047022500 0ustar zuulzuul00000000000000.. _deploy-ovs-ha-dvr: ========================================= Open vSwitch: High availability using DVR ========================================= This architecture example augments the self-service deployment example with the Distributed Virtual Router (DVR) high-availability mechanism that provides connectivity between self-service and provider networks on compute nodes rather than network nodes for specific scenarios. For instances with a floating IPv4 address, routing between self-service and provider networks resides completely on the compute nodes to eliminate single point of failure and performance issues with network nodes. Routing also resides completely on the compute nodes for instances with a fixed or floating IPv4 address using self-service networks on the same distributed virtual router. However, instances with a fixed IP address still rely on the network node for routing and SNAT services between self-service and provider networks. Consider the following attributes of this high-availability mechanism to determine practicality in your environment: * Only provides connectivity to an instance via the compute node on which the instance resides if the instance resides on a self-service network with a floating IPv4 address. Instances on self-service networks with only an IPv6 address or both IPv4 and IPv6 addresses rely on the network node for IPv6 connectivity. * The instance of a router on each compute node consumes an IPv4 address on the provider network on which it contains a gateway. Prerequisites ~~~~~~~~~~~~~ Modify the compute nodes with the following components: * Install the OpenStack Networking layer-3 agent. .. note:: Consider adding at least one additional network node to provide high-availability for instances with a fixed IP address. See See :ref:`config-dvr-snat-ha-ovs` for more information. Architecture ~~~~~~~~~~~~ .. image:: figures/deploy-ovs-ha-dvr-overview.png :alt: High-availability using Open vSwitch with DVR - overview The following figure shows components and connectivity for one self-service network and one untagged (flat) network. In this particular case, the instance resides on the same compute node as the DHCP agent for the network. If the DHCP agent resides on another compute node, the latter only contains a DHCP namespace with a port on the OVS integration bridge. .. image:: figures/deploy-ovs-ha-dvr-compconn1.png :alt: High-availability using Open vSwitch with DVR - components and connectivity - one network Example configuration ~~~~~~~~~~~~~~~~~~~~~ Use the following example configuration as a template to add support for high-availability using DVR to an existing operational environment that supports self-service networks. Controller node --------------- #. In the ``neutron.conf`` file: * Enable distributed routing by default for all routers. .. code-block:: ini [DEFAULT] router_distributed = True #. Restart the following services: * Server Network node ------------ #. In the ``openswitch_agent.ini`` file, enable distributed routing. .. code-block:: ini [DEFAULT] enable_distributed_routing = True #. In the ``l3_agent.ini`` file, configure the layer-3 agent to provide SNAT services. .. code-block:: ini [DEFAULT] agent_mode = dvr_snat .. note:: The ``external_network_bridge`` option intentionally contains no value. #. Restart the following services: * Open vSwitch agent * Layer-3 agent Compute nodes ------------- #. Install the Networking service layer-3 agent. #. In the ``openswitch_agent.ini`` file, enable distributed routing. .. code-block:: ini [DEFAULT] enable_distributed_routing = True #. In the ``l3_agent.ini`` file, configure the layer-3 agent. .. code-block:: ini [DEFAULT] interface_driver = openvswitch external_network_bridge = agent_mode = dvr .. note:: The ``external_network_bridge`` option intentionally contains no value. #. Restart the following services: * Open vSwitch agent * Layer-3 agent Verify service operation ------------------------ #. Source the administrative project credentials. #. Verify presence and operation of the agents. .. code-block:: console $ openstack network agent list +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | 05d980f2-a4fc-4815-91e7-a7f7e118c0db | L3 agent | compute1 | nova | True | UP | neutron-l3-agent | | 1236bbcb-e0ba-48a9-80fc-81202ca4fa51 | Metadata agent | compute2 | | True | UP | neutron-metadata-agent | | 2a2e9a90-51b8-4163-a7d6-3e199ba2374b | L3 agent | compute2 | nova | True | UP | neutron-l3-agent | | 457d6898-b373-4bb3-b41f-59345dcfb5c5 | Open vSwitch agent | compute2 | | True | UP | neutron-openvswitch-agent | | 513caa68-0391-4e53-a530-082e2c23e819 | Linux bridge agent | compute1 | | True | UP | neutron-linuxbridge-agent | | 71f15e84-bc47-4c2a-b9fb-317840b2d753 | DHCP agent | compute2 | nova | True | UP | neutron-dhcp-agent | | 8805b962-de95-4e40-bdc2-7a0add7521e8 | L3 agent | network1 | nova | True | UP | neutron-l3-agent | | a33cac5a-0266-48f6-9cac-4cef4f8b0358 | Open vSwitch agent | network1 | | True | UP | neutron-openvswitch-agent | | a6c69690-e7f7-4e56-9831-1282753e5007 | Metadata agent | compute1 | | True | UP | neutron-metadata-agent | | af11f22f-a9f4-404f-9fd8-cd7ad55c0f68 | DHCP agent | compute1 | nova | True | UP | neutron-dhcp-agent | | bcfc977b-ec0e-4ba9-be62-9489b4b0e6f1 | Open vSwitch agent | compute1 | | True | UP | neutron-openvswitch-agent | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ Create initial networks ----------------------- Similar to the self-service deployment example, this configuration supports multiple VXLAN self-service networks. After enabling high-availability, all additional routers use distributed routing. The following procedure creates an additional self-service network and router. The Networking service also supports adding distributed routing to existing routers. #. Source a regular (non-administrative) project credentials. #. Create a self-service network. .. code-block:: console $ openstack network create selfservice2 +-------------------------+--------------+ | Field | Value | +-------------------------+--------------+ | admin_state_up | UP | | mtu | 1450 | | name | selfservice2 | | port_security_enabled | True | | revision_number | 1 | | router:external | Internal | | shared | False | | status | ACTIVE | | tags | [] | +-------------------------+--------------+ #. Create a IPv4 subnet on the self-service network. .. code-block:: console $ openstack subnet create --subnet-range 192.0.2.0/24 \ --network selfservice2 --dns-nameserver 8.8.4.4 selfservice2-v4 +-------------------+---------------------------+ | Field | Value | +-------------------+---------------------------+ | allocation_pools | 192.0.2.2-192.0.2.254 | | cidr | 192.0.2.0/24 | | dns_nameservers | 8.8.4.4 | | enable_dhcp | True | | gateway_ip | 192.0.2.1 | | ip_version | 4 | | name | selfservice2-v4 | | revision_number | 1 | | tags | [] | +-------------------+---------------------------+ #. Create a IPv6 subnet on the self-service network. .. code-block:: console $ openstack subnet create --subnet-range fd00:192:0:2::/64 --ip-version 6 \ --ipv6-ra-mode slaac --ipv6-address-mode slaac --network selfservice2 \ --dns-nameserver 2001:4860:4860::8844 selfservice2-v6 +-------------------+------------------------------------------------------+ | Field | Value | +-------------------+------------------------------------------------------+ | allocation_pools | fd00:192:0:2::2-fd00:192:0:2:ffff:ffff:ffff:ffff | | cidr | fd00:192:0:2::/64 | | dns_nameservers | 2001:4860:4860::8844 | | enable_dhcp | True | | gateway_ip | fd00:192:0:2::1 | | ip_version | 6 | | ipv6_address_mode | slaac | | ipv6_ra_mode | slaac | | name | selfservice2-v6 | | revision_number | 1 | | tags | [] | +-------------------+------------------------------------------------------+ #. Create a router. .. code-block:: console $ openstack router create router2 +-----------------------+---------+ | Field | Value | +-----------------------+---------+ | admin_state_up | UP | | name | router2 | | revision_number | 1 | | status | ACTIVE | | tags | [] | +-----------------------+---------+ #. Add the IPv4 and IPv6 subnets as interfaces on the router. .. code-block:: console $ openstack router add subnet router2 selfservice2-v4 $ openstack router add subnet router2 selfservice2-v6 .. note:: These commands provide no output. #. Add the provider network as a gateway on the router. .. code-block:: console $ openstack router set router2 --external-gateway provider1 Verify network operation ------------------------ #. Source the administrative project credentials. #. Verify distributed routing on the router. .. code-block:: console $ openstack router show router2 +-------------------------+---------+ | Field | Value | +-------------------------+---------+ | admin_state_up | UP | | distributed | True | | ha | False | | name | router2 | | revision_number | 1 | | status | ACTIVE | +-------------------------+---------+ #. On each compute node, verify creation of a ``qrouter`` namespace with the same ID. Compute node 1: .. code-block:: console # ip netns qrouter-78d2f628-137c-4f26-a257-25fc20f203c1 Compute node 2: .. code-block:: console # ip netns qrouter-78d2f628-137c-4f26-a257-25fc20f203c1 #. On the network node, verify creation of the ``snat`` and ``qrouter`` namespaces with the same ID. .. code-block:: console # ip netns snat-78d2f628-137c-4f26-a257-25fc20f203c1 qrouter-78d2f628-137c-4f26-a257-25fc20f203c1 .. note:: The namespace for router 1 from :ref:`deploy-ovs-selfservice` should also appear on network node 1 because of creation prior to enabling distributed routing. #. Launch an instance with an interface on the additional self-service network. For example, a CirrOS image using flavor ID 1. .. code-block:: console $ openstack server create --flavor 1 --image cirros --nic net-id=NETWORK_ID selfservice-instance2 Replace ``NETWORK_ID`` with the ID of the additional self-service network. #. Determine the IPv4 and IPv6 addresses of the instance. .. code-block:: console $ openstack server list +--------------------------------------+-----------------------+--------+---------------------------------------------------------------------------+ | ID | Name | Status | Networks | +--------------------------------------+-----------------------+--------+---------------------------------------------------------------------------+ | bde64b00-77ae-41b9-b19a-cd8e378d9f8b | selfservice-instance2 | ACTIVE | selfservice2=fd00:192:0:2:f816:3eff:fe71:e93e, 192.0.2.4 | +--------------------------------------+-----------------------+--------+---------------------------------------------------------------------------+ #. Create a floating IPv4 address on the provider network. .. code-block:: console $ openstack floating ip create provider1 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | fixed_ip | None | | id | 0174056a-fa56-4403-b1ea-b5151a31191f | | instance_id | None | | ip | 203.0.113.17 | | pool | provider1 | | revision_number | 1 | | tags | [] | +-------------------+--------------------------------------+ #. Associate the floating IPv4 address with the instance. .. code-block:: console $ openstack server add floating ip selfservice-instance2 203.0.113.17 .. note:: This command provides no output. #. On the compute node containing the instance, verify creation of the ``fip`` namespace with the same ID as the provider network. .. code-block:: console # ip netns fip-4bfa3075-b4b2-4f7d-b88e-df1113942d43 Network traffic flow ~~~~~~~~~~~~~~~~~~~~ .. include:: shared/deploy-selfservice-networktrafficflow.txt This section only contains flow scenarios that benefit from distributed virtual routing or that differ from conventional operation. For other flow scenarios, see :ref:`deploy-ovs-selfservice-networktrafficflow`. North-south scenario 1: Instance with a fixed IP address -------------------------------------------------------- Similar to :ref:`deploy-ovs-selfservice-networktrafficflow-ns1`, except the router namespace on the network node becomes the SNAT namespace. The network node still contains the router namespace, but it serves no purpose in this case. .. image:: figures/deploy-ovs-ha-dvr-flowns1.png :alt: High-availability using Open vSwitch with DVR - network traffic flow - north/south scenario 1 North-south scenario 2: Instance with a floating IPv4 address ------------------------------------------------------------- For instances with a floating IPv4 address using a self-service network on a distributed router, the compute node containing the instance performs SNAT on north-south traffic passing from the instance to external networks such as the Internet and DNAT on north-south traffic passing from external networks to the instance. Floating IP addresses and NAT do not apply to IPv6. Thus, the network node routes IPv6 traffic in this scenario. north-south traffic passing between the instance and external networks such as the Internet. * Instance 1 resides on compute node 1 and uses self-service network 1. * A host on the Internet sends a packet to the instance. The following steps involve the compute node: #. The physical network infrastructure (1) forwards the packet to the provider physical network interface (2). #. The provider physical network interface forwards the packet to the OVS provider bridge provider network port (3). #. The OVS provider bridge swaps actual VLAN tag 101 with the internal VLAN tag. #. The OVS provider bridge ``phy-br-provider`` port (4) forwards the packet to the OVS integration bridge ``int-br-provider`` port (5). #. The OVS integration bridge port for the provider network (6) removes the internal VLAN tag and forwards the packet to the provider network interface (7) in the floating IP namespace. This interface responds to any ARP requests for the instance floating IPv4 address. #. The floating IP namespace routes the packet (8) to the distributed router namespace (9) using a pair of IP addresses on the DVR internal network. This namespace contains the instance floating IPv4 address. #. The router performs DNAT on the packet which changes the destination IP address to the instance IP address on the self-service network via the self-service network interface (10). #. The router forwards the packet to the OVS integration bridge port for the self-service network (11). #. The OVS integration bridge adds an internal VLAN tag to the packet. #. The OVS integration bridge removes the internal VLAN tag from the packet. #. The OVS integration bridge security group port (12) forwards the packet to the security group bridge OVS port (13) via ``veth`` pair. #. Security group rules (14) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge instance port (15) forwards the packet to the instance interface (16) via ``veth`` pair. .. image:: figures/deploy-ovs-ha-dvr-flowns2.png :alt: High-availability using Open vSwitch with DVR - network traffic flow - north/south scenario 2 .. note:: Egress traffic follows similar steps in reverse, except SNAT changes the source IPv4 address of the packet to the floating IPv4 address. East-west scenario 1: Instances on different networks on the same router ------------------------------------------------------------------------ Instances with fixed IPv4/IPv6 address or floating IPv4 address on the same compute node communicate via router on the compute node. Instances on different compute nodes communicate via an instance of the router on each compute node. .. note:: This scenario places the instances on different compute nodes to show the most complex situation. The following steps involve compute node 1: #. The instance interface (1) forwards the packet to the security group bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge OVS port (4) forwards the packet to the OVS integration bridge security group port (5) via ``veth`` pair. #. The OVS integration bridge adds an internal VLAN tag to the packet. #. The OVS integration bridge port for self-service network 1 (6) removes the internal VLAN tag and forwards the packet to the self-service network 1 interface in the distributed router namespace (6). #. The distributed router namespace routes the packet to self-service network 2. #. The self-service network 2 interface in the distributed router namespace (8) forwards the packet to the OVS integration bridge port for self-service network 2 (9). #. The OVS integration bridge adds an internal VLAN tag to the packet. #. The OVS integration bridge exchanges the internal VLAN tag for an internal tunnel ID. #. The OVS integration bridge ``patch-tun`` port (10) forwards the packet to the OVS tunnel bridge ``patch-int`` port (11). #. The OVS tunnel bridge (12) wraps the packet using VNI 101. #. The underlying physical interface (13) for overlay networks forwards the packet to compute node 2 via the overlay network (14). The following steps involve compute node 2: #. The underlying physical interface (15) for overlay networks forwards the packet to the OVS tunnel bridge (16). #. The OVS tunnel bridge unwraps the packet and adds an internal tunnel ID to it. #. The OVS tunnel bridge exchanges the internal tunnel ID for an internal VLAN tag. #. The OVS tunnel bridge ``patch-int`` patch port (17) forwards the packet to the OVS integration bridge ``patch-tun`` patch port (18). #. The OVS integration bridge removes the internal VLAN tag from the packet. #. The OVS integration bridge security group port (19) forwards the packet to the security group bridge OVS port (20) via ``veth`` pair. #. Security group rules (21) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge instance port (22) forwards the packet to the instance 2 interface (23) via ``veth`` pair. .. note:: Routing between self-service networks occurs on the compute node containing the instance sending the packet. In this scenario, routing occurs on compute node 1 for packets from instance 1 to instance 2 and on compute node 2 for packets from instance 2 to instance 1. .. image:: figures/deploy-ovs-ha-dvr-flowew1.png :alt: High-availability using Open vSwitch with DVR - network traffic flow - east/west scenario 2 neutron-12.1.1/doc/source/admin/index.rst0000664000175000017500000000075013553660046020320 0ustar zuulzuul00000000000000.. meta:: :description: This guide targets OpenStack administrators seeking to deploy and manage OpenStack Networking (neutron). :keywords: neutron, networking, OpenStack ========================== OpenStack Networking Guide ========================== This guide targets OpenStack administrators seeking to deploy and manage OpenStack Networking (neutron). .. toctree:: :maxdepth: 2 intro config deploy ops migration misc archives/index neutron-12.1.1/doc/source/admin/config-ml2.rst0000664000175000017500000003653213553660047021156 0ustar zuulzuul00000000000000.. _config-plugin-ml2: =========== ML2 plug-in =========== Architecture ~~~~~~~~~~~~ The Modular Layer 2 (ML2) neutron plug-in is a framework allowing OpenStack Networking to simultaneously use the variety of layer 2 networking technologies found in complex real-world data centers. The ML2 framework distinguishes between the two kinds of drivers that can be configured: * Type drivers Define how an OpenStack network is technically realized. Example: VXLAN Each available network type is managed by an ML2 type driver. Type drivers maintain any needed type-specific network state. They validate the type specific information for provider networks and are responsible for the allocation of a free segment in project networks. * Mechanism drivers Define the mechanism to access an OpenStack network of a certain type. Example: Open vSwitch mechanism driver. The mechanism driver is responsible for taking the information established by the type driver and ensuring that it is properly applied given the specific networking mechanisms that have been enabled. Mechanism drivers can utilize L2 agents (via RPC) and/or interact directly with external devices or controllers. Multiple mechanism and type drivers can be used simultaneously to access different ports of the same virtual network. .. todo:: Picture showing relationships ML2 driver support matrix ------------------------- .. list-table:: Mechanism drivers and L2 agents :header-rows: 1 * - type driver / mech driver - Flat - VLAN - VXLAN - GRE * - Open vSwitch - yes - yes - yes - yes * - Linux bridge - yes - yes - yes - no * - SRIOV - yes - yes - no - no * - MacVTap - yes - yes - no - no * - L2 population - no - no - yes - yes .. note:: L2 population is a special mechanism driver that optimizes BUM (Broadcast, unknown destination address, multicast) traffic in the overlay networks VXLAN and GRE. It needs to be used in conjunction with either the Linux bridge or the Open vSwitch mechanism driver and cannot be used as standalone mechanism driver. For more information, see the *Mechanism drivers* section below. Configuration ~~~~~~~~~~~~~ Network type drivers -------------------- To enable type drivers in the ML2 plug-in. Edit the ``/etc/neutron/plugins/ml2/ml2_conf.ini`` file: .. code-block:: ini [ml2] type_drivers = flat,vlan,vxlan,gre .. note:: For more details,see the `Bug 1567792 `__. For more details, see the `Networking configuration options <../configuration/ml2-conf.html>`__ of Configuration Reference. The following type drivers are available * Flat * VLAN * GRE * VXLAN Provider network types ^^^^^^^^^^^^^^^^^^^^^^ Provider networks provide connectivity like project networks. But only administrative (privileged) users can manage those networks because they interface with the physical network infrastructure. More information about provider networks see :doc:`intro-os-networking`. * Flat The administrator needs to configure a list of physical network names that can be used for provider networks. For more details, see the related section in the `Configuration Reference <../configuration/ml2-conf.html#ml2-type-flat>`__. * VLAN The administrator needs to configure a list of physical network names that can be used for provider networks. For more details, see the related section in the `Configuration Reference <../configuration/ml2-conf.html#ml2-type-vlan>`__. * GRE No additional configuration required. * VXLAN The administrator can configure the VXLAN multicast group that should be used. .. note:: VXLAN multicast group configuration is not applicable for the Open vSwitch agent. As of today it is not used in the Linux bridge agent. The Linux bridge agent has its own agent specific configuration option. For more details, see the `Bug 1523614 `__. Project network types ^^^^^^^^^^^^^^^^^^^^^ Project networks provide connectivity to instances for a particular project. Regular (non-privileged) users can manage project networks within the allocation that an administrator or operator defines for them. More information about project and provider networks see :doc:`intro-os-networking`. Project network configurations are made in the ``/etc/neutron/plugins/ml2/ml2_conf.ini`` configuration file on the neutron server: * VLAN The administrator needs to configure the range of VLAN IDs that can be used for project network allocation. For more details, see the related section in the `Configuration Reference <../configuration/ml2-conf.html#ml2-type-vlan>`__. * GRE The administrator needs to configure the range of tunnel IDs that can be used for project network allocation. For more details, see the related section in the `Configuration Reference <../configuration/ml2-conf.html#ml2-type-gre>`__. * VXLAN The administrator needs to configure the range of VXLAN IDs that can be used for project network allocation. For more details, see the related section in the `Configuration Reference <../configuration/ml2-conf.html#ml2-type-vxlan>`__. .. note:: Flat networks for project allocation are not supported. They only can exist as a provider network. Mechanism drivers ----------------- To enable mechanism drivers in the ML2 plug-in, edit the ``/etc/neutron/plugins/ml2/ml2_conf.ini`` file on the neutron server: .. code-block:: ini [ml2] mechanism_drivers = ovs,l2pop .. note:: For more details, see the `Bug 1567792 `__. For more details, see the `Configuration Reference <../configuration/ml2-conf.html#ml2>`__. * Linux bridge No additional configurations required for the mechanism driver. Additional agent configuration is required. For details, see the related *L2 agent* section below. * Open vSwitch No additional configurations required for the mechanism driver. Additional agent configuration is required. For details, see the related *L2 agent* section below. * SRIOV The SRIOV driver accepts all PCI vendor devices. * MacVTap No additional configurations required for the mechanism driver. Additional agent configuration is required. Please see the related section. * L2 population The administrator can configure some optional configuration options. For more details, see the related section in the `Configuration Reference <../configuration/ml2-conf.html#l2pop>`__. * Specialized * Open source External open source mechanism drivers exist as well as the neutron integrated reference implementations. Configuration of those drivers is not part of this document. For example: * OpenDaylight * OpenContrail * Proprietary (vendor) External mechanism drivers from various vendors exist as well as the neutron integrated reference implementations. Configuration of those drivers is not part of this document. Extension Drivers ----------------- The ML2 plug-in also supports extension drivers that allows other pluggable drivers to extend the core resources implemented in the ML2 plug-in (``networks``, ``ports``, etc.). Examples of extension drivers include support for QoS, port security, etc. For more details see the ``extension_drivers`` configuration option in the `Configuration Reference <../configuration/ml2-conf.html#ml2.extension_drivers>`__. Agents ------ L2 agent ^^^^^^^^ An L2 agent serves layer 2 (Ethernet) network connectivity to OpenStack resources. It typically runs on each Network Node and on each Compute Node. * Open vSwitch agent The Open vSwitch agent configures the Open vSwitch to realize L2 networks for OpenStack resources. Configuration for the Open vSwitch agent is typically done in the ``openvswitch_agent.ini`` configuration file. Make sure that on agent start you pass this configuration file as argument. For a detailed list of configuration options, see the related section in the `Configuration Reference <../configuration/openvswitch-agent.html>`__. * Linux bridge agent The Linux bridge agent configures Linux bridges to realize L2 networks for OpenStack resources. Configuration for the Linux bridge agent is typically done in the ``linuxbridge_agent.ini`` configuration file. Make sure that on agent start you pass this configuration file as argument. For a detailed list of configuration options, see the related section in the `Configuration Reference <../configuration/linuxbridge-agent.html>`__. * SRIOV Nic Switch agent The sriov nic switch agent configures PCI virtual functions to realize L2 networks for OpenStack instances. Network attachments for other resources like routers, DHCP, and so on are not supported. Configuration for the SRIOV nic switch agent is typically done in the ``sriov_agent.ini`` configuration file. Make sure that on agent start you pass this configuration file as argument. For a detailed list of configuration options, see the related section in the `Configuration Reference <../configuration/sriov-agent.html>`__. * MacVTap agent The MacVTap agent uses kernel MacVTap devices for realizing L2 networks for OpenStack instances. Network attachments for other resources like routers, DHCP, and so on are not supported. Configuration for the MacVTap agent is typically done in the ``macvtap_agent.ini`` configuration file. Make sure that on agent start you pass this configuration file as argument. For a detailed list of configuration options, see the related section in the `Configuration Reference <../configuration/macvtap-agent.html>`__. L3 agent ^^^^^^^^ The L3 agent offers advanced layer 3 services, like virtual Routers and Floating IPs. It requires an L2 agent running in parallel. Configuration for the L3 agent is typically done in the ``l3_agent.ini`` configuration file. Make sure that on agent start you pass this configuration file as argument. For a detailed list of configuration options, see the related section in the `Configuration Reference <../configuration/l3-agent.html>`__. DHCP agent ^^^^^^^^^^ The DHCP agent is responsible for DHCP (Dynamic Host Configuration Protocol) and RADVD (Router Advertisement Daemon) services. It requires a running L2 agent on the same node. Configuration for the DHCP agent is typically done in the ``dhcp_agent.ini`` configuration file. Make sure that on agent start you pass this configuration file as argument. For a detailed list of configuration options, see the related section in the `Configuration Reference <../configuration/dhcp-agent.html>`__. Metadata agent ^^^^^^^^^^^^^^ The Metadata agent allows instances to access cloud-init meta data and user data via the network. It requires a running L2 agent on the same node. Configuration for the Metadata agent is typically done in the ``metadata_agent.ini`` configuration file. Make sure that on agent start you pass this configuration file as argument. For a detailed list of configuration options, see the related section in the `Configuration Reference <../configuration/metadata-agent.html>`__. L3 metering agent ^^^^^^^^^^^^^^^^^ The L3 metering agent enables layer3 traffic metering. It requires a running L3 agent on the same node. Configuration for the L3 metering agent is typically done in the ``metering_agent.ini`` configuration file. Make sure that on agent start you pass this configuration file as argument. For a detailed list of configuration options, see the related section in the `Configuration Reference <../configuration/metering-agent.html>`__. Security -------- L2 agents support some important security configurations. * Security Groups For more details, see the related section in the `Configuration Reference <../configuration/ml2-conf.html#securitygroup>`__. * Arp Spoofing Prevention Configured in the *L2 agent* configuration. Reference implementations ~~~~~~~~~~~~~~~~~~~~~~~~~ Overview -------- In this section, the combination of a mechanism driver and an L2 agent is called 'reference implementation'. The following table lists these implementations: .. list-table:: Mechanism drivers and L2 agents :header-rows: 1 * - Mechanism Driver - L2 agent * - Open vSwitch - Open vSwitch agent * - Linux bridge - Linux bridge agent * - SRIOV - SRIOV nic switch agent * - MacVTap - MacVTap agent * - L2 population - Open vSwitch agent, Linux bridge agent The following tables shows which reference implementations support which non-L2 neutron agents: .. list-table:: Reference implementations and other agents :header-rows: 1 * - Reference Implementation - L3 agent - DHCP agent - Metadata agent - L3 Metering agent * - Open vSwitch & Open vSwitch agent - yes - yes - yes - yes * - Linux bridge & Linux bridge agent - yes - yes - yes - yes * - SRIOV & SRIOV nic switch agent - no - no - no - no * - MacVTap & MacVTap agent - no - no - no - no .. note:: L2 population is not listed here, as it is not a standalone mechanism. If other agents are supported depends on the conjunctive mechanism driver that is used for binding a port. More information about L2 population see the `OpenStack Manuals `__. Buying guide ------------ This guide characterizes the L2 reference implementations that currently exist. * Open vSwitch mechanism and Open vSwitch agent Can be used for instance network attachments as well as for attachments of other network resources like routers, DHCP, and so on. * Linux bridge mechanism and Linux bridge agent Can be used for instance network attachments as well as for attachments of other network resources like routers, DHCP, and so on. * SRIOV mechanism driver and SRIOV NIC switch agent Can only be used for instance network attachments (device_owner = compute). Is deployed besides an other mechanism driver and L2 agent such as OVS or Linux bridge. It offers instances direct access to the network adapter through a PCI Virtual Function (VF). This gives an instance direct access to hardware capabilities and high performance networking. The cloud consumer can decide via the neutron APIs VNIC_TYPE attribute, if an instance gets a normal OVS port or an SRIOV port. Due to direct connection, some features are not available when using SRIOV. For example, DVR, security groups, migration. For more information see the :ref:`config-sriov`. * MacVTap mechanism driver and MacVTap agent Can only be used for instance network attachments (device_owner = compute) and not for attachment of other resources like routers, DHCP, and so on. It is positioned as alternative to Open vSwitch or Linux bridge support on the compute node for internal deployments. MacVTap offers a direct connection with very little overhead between instances and down to the adapter. You can use MacVTap agent on the compute node when you require a network connection that is performance critical. It does not require specific hardware (like with SRIOV). Due to the direct connection, some features are not available when using it on the compute node. For example, DVR, security groups and arp-spoofing protection. neutron-12.1.1/doc/source/admin/migration-database.rst0000664000175000017500000001113213553660046022740 0ustar zuulzuul00000000000000.. _migration-database: ======== Database ======== The upgrade of the Networking service database is implemented with Alembic migration chains. The migrations in the ``alembic/versions`` contain the changes needed to migrate from older Networking service releases to newer ones. Since Liberty, Networking maintains two parallel Alembic migration branches. The first branch is called expand and is used to store expansion-only migration rules. These rules are strictly additive and can be applied while the Neutron server is running. The second branch is called contract and is used to store those migration rules that are not safe to apply while Neutron server is running. The intent of separate branches is to allow invoking those safe migrations from the expand branch while the Neutron server is running and therefore reducing downtime needed to upgrade the service. A database management command-line tool uses the Alembic library to manage the migration. Database management command-line tool ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The database management command-line tool is called :command:`neutron-db-manage`. Pass the ``--help`` option to the tool for usage information. The tool takes some options followed by some commands: .. code-block:: console $ neutron-db-manage The tool needs to access the database connection string, which is provided in the ``neutron.conf`` configuration file in an installation. The tool automatically reads from ``/etc/neutron/neutron.conf`` if it is present. If the configuration is in a different location, use the following command: .. code-block:: console $ neutron-db-manage --config-file /path/to/neutron.conf Multiple ``--config-file`` options can be passed if needed. Instead of reading the DB connection from the configuration file(s), you can use the ``--database-connection`` option: .. code-block:: console $ neutron-db-manage --database-connection mysql+pymysql://root:secret@127.0.0.1/neutron?charset=utf8 The `branches`, `current`, and `history` commands all accept a ``--verbose`` option, which, when passed, will instruct :command:`neutron-db-manage` to display more verbose output for the specified command: .. code-block:: console $ neutron-db-manage current --verbose .. note:: The tool usage examples below do not show the options. It is assumed that you use the options that you need for your environment. In new deployments, you start with an empty database and then upgrade to the latest database version using the following command: .. code-block:: console $ neutron-db-manage upgrade heads After installing a new version of the Neutron server, upgrade the database using the following command: .. code-block:: console $ neutron-db-manage upgrade heads In existing deployments, check the current database version using the following command: .. code-block:: console $ neutron-db-manage current To apply the expansion migration rules, use the following command: .. code-block:: console $ neutron-db-manage upgrade --expand To apply the non-expansive migration rules, use the following command: .. code-block:: console $ neutron-db-manage upgrade --contract To check if any contract migrations are pending and therefore if offline migration is required, use the following command: .. code-block:: console $ neutron-db-manage has_offline_migrations .. note:: Offline migration requires all Neutron server instances in the cluster to be shutdown before you apply any contract scripts. To generate a script of the command instead of operating immediately on the database, use the following command: .. code-block:: console $ neutron-db-manage upgrade heads --sql .. note:: The `--sql` option causes the command to generate a script. The script can be run later (online or offline), perhaps after verifying and/or modifying it. To migrate between specific migration versions, use the following command: .. code-block:: console $ neutron-db-manage upgrade : To upgrade the database incrementally, use the following command: .. code-block:: console $ neutron-db-manage upgrade --delta <# of revs> .. note:: Database downgrade is not supported. To look for differences between the schema generated by the upgrade command and the schema defined by the models, use the :command:`revision --autogenerate` command: .. code-block:: console neutron-db-manage revision -m REVISION_DESCRIPTION --autogenerate .. note:: This generates a prepopulated template with the changes needed to match the database state with the models. neutron-12.1.1/doc/source/admin/config-rbac.rst0000664000175000017500000005533613553660047021376 0ustar zuulzuul00000000000000.. _config-rbac: ================================ Role-Based Access Control (RBAC) ================================ The Role-Based Access Control (RBAC) policy framework enables both operators and users to grant access to resources for specific projects. Supported objects for sharing with specific projects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Currently, the access that can be granted using this feature is supported by: * Regular port creation permissions on networks (since Liberty). * Binding QoS policies permissions to networks or ports (since Mitaka). * Attaching router gateways to networks (since Mitaka). Sharing an object with specific projects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sharing an object with a specific project is accomplished by creating a policy entry that permits the target project the ``access_as_shared`` action on that object. Sharing a network with specific projects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Create a network to share: .. code-block:: console $ openstack network create secret_network +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2017-01-25T20:16:40Z | | description | | | dns_domain | None | | id | f55961b9-3eb8-42eb-ac96-b97038b568de | | ipv4_address_scope | None | | ipv6_address_scope | None | | is_default | None | | mtu | 1450 | | name | secret_network | | port_security_enabled | True | | project_id | 61b7eba037fd41f29cfba757c010faff | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 9 | | qos_policy_id | None | | revision_number | 3 | | router:external | Internal | | segments | None | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2017-01-25T20:16:40Z | +---------------------------+--------------------------------------+ Create the policy entry using the :command:`openstack network rbac create` command (in this example, the ID of the project we want to share with is ``b87b2fc13e0248a4a031d38e06dc191d``): .. code-block:: console $ openstack network rbac create --target-project \ b87b2fc13e0248a4a031d38e06dc191d --action access_as_shared \ --type network f55961b9-3eb8-42eb-ac96-b97038b568de +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | action | access_as_shared | | id | f93efdbf-f1e0-41d2-b093-8328959d469e | | name | None | | object_id | f55961b9-3eb8-42eb-ac96-b97038b568de | | object_type | network | | project_id | 61b7eba037fd41f29cfba757c010faff | | target_project_id | b87b2fc13e0248a4a031d38e06dc191d | +-------------------+--------------------------------------+ The ``target-project`` parameter specifies the project that requires access to the network. The ``action`` parameter specifies what the project is allowed to do. The ``type`` parameter says that the target object is a network. The final parameter is the ID of the network we are granting access to. Project ``b87b2fc13e0248a4a031d38e06dc191d`` will now be able to see the network when running :command:`openstack network list` and :command:`openstack network show` and will also be able to create ports on that network. No other users (other than admins and the owner) will be able to see the network. To remove access for that project, delete the policy that allows it using the :command:`openstack network rbac delete` command: .. code-block:: console $ openstack network rbac delete f93efdbf-f1e0-41d2-b093-8328959d469e If that project has ports on the network, the server will prevent the policy from being deleted until the ports have been deleted: .. code-block:: console $ openstack network rbac delete f93efdbf-f1e0-41d2-b093-8328959d469e RBAC policy on object f93efdbf-f1e0-41d2-b093-8328959d469e cannot be removed because other objects depend on it. This process can be repeated any number of times to share a network with an arbitrary number of projects. Sharing a QoS policy with specific projects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Create a QoS policy to share: .. code-block:: console $ openstack network qos policy create secret_policy +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | description | | | id | 1f730d69-1c45-4ade-a8f2-89070ac4f046 | | name | secret_policy | | project_id | 61b7eba037fd41f29cfba757c010faff | | revision_number | 1 | | rules | [] | | shared | False | | tags | [] | +-------------------+--------------------------------------+ Create the RBAC policy entry using the :command:`openstack network rbac create` command (in this example, the ID of the project we want to share with is ``be98b82f8fdf46b696e9e01cebc33fd9``): .. code-block:: console $ openstack network rbac create --target-project \ be98b82f8fdf46b696e9e01cebc33fd9 --action access_as_shared \ --type qos_policy 1f730d69-1c45-4ade-a8f2-89070ac4f046 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | action | access_as_shared | | id | 8828e38d-a0df-4c78-963b-e5f215d3d550 | | name | None | | object_id | 1f730d69-1c45-4ade-a8f2-89070ac4f046 | | object_type | qos_policy | | project_id | 61b7eba037fd41f29cfba757c010faff | | target_project_id | be98b82f8fdf46b696e9e01cebc33fd9 | +-------------------+--------------------------------------+ The ``target-project`` parameter specifies the project that requires access to the QoS policy. The ``action`` parameter specifies what the project is allowed to do. The ``type`` parameter says that the target object is a QoS policy. The final parameter is the ID of the QoS policy we are granting access to. Project ``be98b82f8fdf46b696e9e01cebc33fd9`` will now be able to see the QoS policy when running :command:`openstack network qos policy list` and :command:`openstack network qos policy show` and will also be able to bind it to its ports or networks. No other users (other than admins and the owner) will be able to see the QoS policy. To remove access for that project, delete the RBAC policy that allows it using the :command:`openstack network rbac delete` command: .. code-block:: console $ openstack network rbac delete 8828e38d-a0df-4c78-963b-e5f215d3d550 If that project has ports or networks with the QoS policy applied to them, the server will not delete the RBAC policy until the QoS policy is no longer in use: .. code-block:: console $ openstack network rbac delete 8828e38d-a0df-4c78-963b-e5f215d3d550 RBAC policy on object 8828e38d-a0df-4c78-963b-e5f215d3d550 cannot be removed because other objects depend on it. This process can be repeated any number of times to share a qos-policy with an arbitrary number of projects. How the 'shared' flag relates to these entries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As introduced in other guide entries, neutron provides a means of making an object (``network``, ``qos-policy``) available to every project. This is accomplished using the ``shared`` flag on the supported object: .. code-block:: console $ openstack network create global_network --share +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2017-01-25T20:32:06Z | | description | | | dns_domain | None | | id | 84a7e627-573b-49da-af66-c9a65244f3ce | | ipv4_address_scope | None | | ipv6_address_scope | None | | is_default | None | | mtu | 1450 | | name | global_network | | port_security_enabled | True | | project_id | 61b7eba037fd41f29cfba757c010faff | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 7 | | qos_policy_id | None | | revision_number | 3 | | router:external | Internal | | segments | None | | shared | True | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2017-01-25T20:32:07Z | +---------------------------+--------------------------------------+ This is the equivalent of creating a policy on the network that permits every project to perform the action ``access_as_shared`` on that network. Neutron treats them as the same thing, so the policy entry for that network should be visible using the :command:`openstack network rbac list` command: .. code-block:: console $ openstack network rbac list +-------------------------------+-------------+--------------------------------+ | ID | Object Type | Object ID | +-------------------------------+-------------+--------------------------------+ | 58a5ee31-2ad6-467d- | qos_policy | 1f730d69-1c45-4ade- | | 8bb8-8c2ae3dd1382 | | a8f2-89070ac4f046 | | 27efbd79-f384-4d89-9dfc- | network | 84a7e627-573b-49da- | | 6c4a606ceec6 | | af66-c9a65244f3ce | +-------------------------------+-------------+--------------------------------+ Use the :command:`neutron rbac-show` command to see the details: .. code-block:: console $ openstack network rbac show 27efbd79-f384-4d89-9dfc-6c4a606ceec6 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | action | access_as_shared | | id | 27efbd79-f384-4d89-9dfc-6c4a606ceec6 | | name | None | | object_id | 84a7e627-573b-49da-af66-c9a65244f3ce | | object_type | network | | project_id | 61b7eba037fd41f29cfba757c010faff | | target_project_id | * | +-------------------+--------------------------------------+ The output shows that the entry allows the action ``access_as_shared`` on object ``84a7e627-573b-49da-af66-c9a65244f3ce`` of type ``network`` to target_tenant ``*``, which is a wildcard that represents all projects. Currently, the ``shared`` flag is just a mapping to the underlying RBAC policies for a network. Setting the flag to ``True`` on a network creates a wildcard RBAC entry. Setting it to ``False`` removes the wildcard entry. When you run :command:`openstack network list` or :command:`openstack network show`, the ``shared`` flag is calculated by the server based on the calling project and the RBAC entries for each network. For QoS objects use :command:`openstack network qos policy list` or :command:`openstack network qos policy show` respectively. If there is a wildcard entry, the ``shared`` flag is always set to ``True``. If there are only entries that share with specific projects, only the projects the object is shared to will see the flag as ``True`` and the rest will see the flag as ``False``. Allowing a network to be used as an external network ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To make a network available as an external network for specific projects rather than all projects, use the ``access_as_external`` action. #. Create a network that you want to be available as an external network: .. code-block:: console $ openstack network create secret_external_network +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2017-01-25T20:36:59Z | | description | | | dns_domain | None | | id | 802d4e9e-4649-43e6-9ee2-8d052a880cfb | | ipv4_address_scope | None | | ipv6_address_scope | None | | is_default | None | | mtu | 1450 | | name | secret_external_network | | port_security_enabled | True | | project_id | 61b7eba037fd41f29cfba757c010faff | | proider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 21 | | qos_policy_id | None | | revision_number | 3 | | router:external | Internal | | segments | None | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2017-01-25T20:36:59Z | +---------------------------+--------------------------------------+ #. Create a policy entry using the :command:`openstack network rbac create` command (in this example, the ID of the project we want to share with is ``838030a7bf3c4d04b4b054c0f0b2b17c``): .. code-block:: console $ openstack network rbac create --target-project \ 838030a7bf3c4d04b4b054c0f0b2b17c --action access_as_external \ --type network 802d4e9e-4649-43e6-9ee2-8d052a880cfb +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | action | access_as_external | | id | afdd5b8d-b6f5-4a15-9817-5231434057be | | name | None | | object_id | 802d4e9e-4649-43e6-9ee2-8d052a880cfb | | object_type | network | | project_id | 61b7eba037fd41f29cfba757c010faff | | target_project_id | 838030a7bf3c4d04b4b054c0f0b2b17c | +-------------------+--------------------------------------+ The ``target-project`` parameter specifies the project that requires access to the network. The ``action`` parameter specifies what the project is allowed to do. The ``type`` parameter indicates that the target object is a network. The final parameter is the ID of the network we are granting external access to. Now project ``838030a7bf3c4d04b4b054c0f0b2b17c`` is able to see the network when running :command:`openstack network list` and :command:`openstack network show` and can attach router gateway ports to that network. No other users (other than admins and the owner) are able to see the network. To remove access for that project, delete the policy that allows it using the :command:`openstack network rbac delete` command: .. code-block:: console $ openstack network rbac delete afdd5b8d-b6f5-4a15-9817-5231434057be If that project has router gateway ports attached to that network, the server prevents the policy from being deleted until the ports have been deleted: .. code-block:: console $ openstack network rbac delete afdd5b8d-b6f5-4a15-9817-5231434057be RBAC policy on object afdd5b8d-b6f5-4a15-9817-5231434057be cannot be removed because other objects depend on it. This process can be repeated any number of times to make a network available as external to an arbitrary number of projects. If a network is marked as external during creation, it now implicitly creates a wildcard RBAC policy granting everyone access to preserve previous behavior before this feature was added. .. code-block:: console $ openstack network create global_external_network --external +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2017-01-25T20:41:44Z | | description | | | dns_domain | None | | id | 72a257a2-a56e-4ac7-880f-94a4233abec6 | | ipv4_address_scope | None | | ipv6_address_scope | None | | is_default | None | | mtu | 1450 | | name | global_external_network | | port_security_enabled | True | | project_id | 61b7eba037fd41f29cfba757c010faff | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 69 | | qos_policy_id | None | | revision_number | 4 | | router:external | External | | segments | None | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2017-01-25T20:41:44Z | +---------------------------+--------------------------------------+ In the output above the standard ``router:external`` attribute is ``External`` as expected. Now a wildcard policy is visible in the RBAC policy listings: .. code-block:: console $ openstack network rbac list --long -c ID -c Action +--------------------------------------+--------------------+ | ID | Action | +--------------------------------------+--------------------+ | b694e541-bdca-480d-94ec-eda59ab7d71a | access_as_external | +--------------------------------------+--------------------+ You can modify or delete this policy with the same constraints as any other RBAC ``access_as_external`` policy. Preventing regular users from sharing objects with each other ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The default ``policy.json`` file will not allow regular users to share objects with every other project using a wildcard; however, it will allow them to share objects with specific project IDs. If an operator wants to prevent normal users from doing this, the ``"create_rbac_policy":`` entry in ``policy.json`` can be adjusted from ``""`` to ``"rule:admin_only"``. neutron-12.1.1/doc/source/admin/misc-libvirt.rst0000664000175000017500000001141013553660046021610 0ustar zuulzuul00000000000000.. _misc-disable-libvirt-networking: ========================== Disable libvirt networking ========================== Most OpenStack deployments use the `libvirt `__ toolkit for interacting with the hypervisor. Specifically, OpenStack Compute uses libvirt for tasks such as booting and terminating virtual machine instances. When OpenStack Compute boots a new instance, libvirt provides OpenStack with the VIF associated with the instance, and OpenStack Compute plugs the VIF into a virtual device provided by OpenStack Network. The libvirt toolkit itself does not provide any networking functionality in OpenStack deployments. However, libvirt is capable of providing networking services to the virtual machines that it manages. In particular, libvirt can be configured to provide networking functionality akin to a simplified, single-node version of OpenStack. Users can use libvirt to create layer 2 networks that are similar to OpenStack Networking's networks, confined to a single node. libvirt network implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default, libvirt's networking functionality is enabled, and libvirt creates a network when the system boots. To implement this network, libvirt leverages some of the same technologies that OpenStack Network does. In particular, libvirt uses: * Linux bridging for implementing a layer 2 network * dnsmasq for providing IP addresses to virtual machines using DHCP * iptables to implement SNAT so instances can connect out to the public internet, and to ensure that virtual machines are permitted to communicate with dnsmasq using DHCP By default, libvirt creates a network named *default*. The details of this network may vary by distribution; on Ubuntu this network involves: * a Linux bridge named ``virbr0`` with an IP address of ``192.0.2.1/24`` * a dnsmasq process that listens on the ``virbr0`` interface and hands out IP addresses in the range ``192.0.2.2-192.0.2.254`` * a set of iptables rules When libvirt boots a virtual machine, it places the machine's VIF in the bridge ``virbr0`` unless explicitly told not to. On Ubuntu, the iptables ruleset that libvirt creates includes the following rules:: *nat -A POSTROUTING -s 192.0.2.0/24 -d 224.0.0.0/24 -j RETURN -A POSTROUTING -s 192.0.2.0/24 -d 255.255.255.255/32 -j RETURN -A POSTROUTING -s 192.0.2.0/24 ! -d 192.0.2.0/24 -p tcp -j MASQUERADE --to-ports 1024-65535 -A POSTROUTING -s 192.0.2.0/24 ! -d 192.0.2.0/24 -p udp -j MASQUERADE --to-ports 1024-65535 -A POSTROUTING -s 192.0.2.0/24 ! -d 192.0.2.0/24 -j MASQUERADE *mangle -A POSTROUTING -o virbr0 -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill *filter -A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT -A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT -A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT -A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT -A FORWARD -d 192.0.2.0/24 -o virbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A FORWARD -s 192.0.2.0/24 -i virbr0 -j ACCEPT -A FORWARD -i virbr0 -o virbr0 -j ACCEPT -A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable -A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable -A OUTPUT -o virbr0 -p udp -m udp --dport 68 -j ACCEPT The following shows the dnsmasq process that libvirt manages as it appears in the output of :command:`ps`:: 2881 ? S 0:00 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf How to disable libvirt networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Although OpenStack does not make use of libvirt's networking, this networking will not interfere with OpenStack's behavior, and can be safely left enabled. However, libvirt's networking can be a nuisance when debugging OpenStack networking issues. Because libvirt creates an additional bridge, dnsmasq process, and iptables ruleset, these may distract an operator engaged in network troubleshooting. Unless you need to start up virtual machines using libvirt directly, you can safely disable libvirt's network. To view the defined libvirt networks and their state: .. code-block:: console # virsh net-list Name State Autostart Persistent ---------------------------------------------------------- default active yes yes To deactivate the libvirt network named ``default``: .. code-block:: console # virsh net-destroy default Deactivating the network will remove the ``virbr0`` bridge, terminate the dnsmasq process, and remove the iptables rules. To prevent the network from automatically starting on boot: .. code-block:: console # virsh net-autostart --network default --disable To activate the network after it has been deactivated: .. code-block:: console # virsh net-start default neutron-12.1.1/doc/source/admin/config-sriov.rst0000664000175000017500000004135213553660047021622 0ustar zuulzuul00000000000000.. _config-sriov: ====== SR-IOV ====== The purpose of this page is to describe how to enable SR-IOV functionality available in OpenStack (using OpenStack Networking). This functionality was first introduced in the OpenStack Juno release. This page intends to serve as a guide for how to configure OpenStack Networking and OpenStack Compute to create SR-IOV ports. The basics ~~~~~~~~~~ PCI-SIG Single Root I/O Virtualization and Sharing (SR-IOV) functionality is available in OpenStack since the Juno release. The SR-IOV specification defines a standardized mechanism to virtualize PCIe devices. This mechanism can virtualize a single PCIe Ethernet controller to appear as multiple PCIe devices. Each device can be directly assigned to an instance, bypassing the hypervisor and virtual switch layer. As a result, users are able to achieve low latency and near-line wire speed. The following terms are used throughout this document: .. list-table:: :header-rows: 1 :widths: 10 90 * - Term - Definition * - PF - Physical Function. The physical Ethernet controller that supports SR-IOV. * - VF - Virtual Function. The virtual PCIe device created from a physical Ethernet controller. SR-IOV agent ------------ The SR-IOV agent allows you to set the admin state of ports, configure port security (enable and disable spoof checking), and configure QoS rate limiting and minimum bandwidth. You must include the SR-IOV agent on each compute node using SR-IOV ports. .. note:: The SR-IOV agent was optional before Mitaka, and was not enabled by default before Liberty. .. note:: The ability to control port security and QoS rate limit settings was added in Liberty. Supported Ethernet controllers ------------------------------ The following manufacturers are known to work: - Intel - Mellanox - QLogic For information on **Mellanox SR-IOV Ethernet ConnectX-3/ConnectX-3 Pro cards**, see `Mellanox: How To Configure SR-IOV VFs `_. For information on **QLogic SR-IOV Ethernet cards**, see `User's Guide OpenStack Deployment with SR-IOV Configuration `_. Using SR-IOV interfaces ~~~~~~~~~~~~~~~~~~~~~~~ In order to enable SR-IOV, the following steps are required: #. Create Virtual Functions (Compute) #. Whitelist PCI devices in nova-compute (Compute) #. Configure neutron-server (Controller) #. Configure nova-scheduler (Controller) #. Enable neutron sriov-agent (Compute) We recommend using VLAN provider networks for segregation. This way you can combine instances without SR-IOV ports and instances with SR-IOV ports on a single network. .. note:: Throughout this guide, ``eth3`` is used as the PF and ``physnet2`` is used as the provider network configured as a VLAN range. These ports may vary in different environments. Create Virtual Functions (Compute) ---------------------------------- Create the VFs for the network interface that will be used for SR-IOV. We use ``eth3`` as PF, which is also used as the interface for the VLAN provider network and has access to the private networks of all machines. .. note:: The steps detail how to create VFs using Mellanox ConnectX-4 and newer/Intel SR-IOV Ethernet cards on an Intel system. Steps may differ for different hardware configurations. #. Ensure SR-IOV and VT-d are enabled in BIOS. #. Enable IOMMU in Linux by adding ``intel_iommu=on`` to the kernel parameters, for example, using GRUB. #. On each compute node, create the VFs via the PCI SYS interface: .. code-block:: console # echo '8' > /sys/class/net/eth3/device/sriov_numvfs .. note:: On some PCI devices, observe that when changing the amount of VFs you receive the error ``Device or resource busy``. In this case, you must first set ``sriov_numvfs`` to ``0``, then set it to your new value. .. note:: A network interface could be used both for PCI passthrough, using the PF, and SR-IOV, using the VFs. If the PF is used, the VF number stored in the ``sriov_numvfs`` file is lost. If the PF is attached again to the operating system, the number of VFs assigned to this interface will be zero. To keep the number of VFs always assigned to this interface, modify the interfaces configuration file adding an ``ifup`` script command. In Ubuntu, modifying the ``/etc/network/interfaces`` file: .. code-block:: ini auto eth3 iface eth3 inet dhcp pre-up echo '4' > /sys/class/net/eth3/device/sriov_numvfs In Red Hat, modifying the ``/sbin/ifup-local`` file: .. code-block:: bash #!/bin/sh if [[ "$1" == "eth3" ]] then echo '4' > /sys/class/net/eth3/device/sriov_numvfs fi .. warning:: Alternatively, you can create VFs by passing the ``max_vfs`` to the kernel module of your network interface. However, the ``max_vfs`` parameter has been deprecated, so the PCI SYS interface is the preferred method. You can determine the maximum number of VFs a PF can support: .. code-block:: console # cat /sys/class/net/eth3/device/sriov_totalvfs 63 #. Verify that the VFs have been created and are in ``up`` state: .. code-block:: console # lspci | grep Ethernet 82:00.0 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01) 82:00.1 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01) 82:10.0 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01) 82:10.2 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01) 82:10.4 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01) 82:10.6 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01) 82:11.0 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01) 82:11.2 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01) 82:11.4 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01) 82:11.6 Ethernet controller: Intel Corporation 82599 Ethernet Controller Virtual Function (rev 01) .. code-block:: console # ip link show eth3 8: eth3: mtu 1500 qdisc mq state UP mode DEFAULT qlen 1000 link/ether a0:36:9f:8f:3f:b8 brd ff:ff:ff:ff:ff:ff vf 0 MAC 00:00:00:00:00:00, spoof checking on, link-state auto vf 1 MAC 00:00:00:00:00:00, spoof checking on, link-state auto vf 2 MAC 00:00:00:00:00:00, spoof checking on, link-state auto vf 3 MAC 00:00:00:00:00:00, spoof checking on, link-state auto vf 4 MAC 00:00:00:00:00:00, spoof checking on, link-state auto vf 5 MAC 00:00:00:00:00:00, spoof checking on, link-state auto vf 6 MAC 00:00:00:00:00:00, spoof checking on, link-state auto vf 7 MAC 00:00:00:00:00:00, spoof checking on, link-state auto If the interfaces are down, set them to ``up`` before launching a guest, otherwise the instance will fail to spawn: .. code-block:: console # ip link set eth3 up #. Persist created VFs on reboot: .. code-block:: console # echo "echo '7' > /sys/class/net/eth3/device/sriov_numvfs" >> /etc/rc.local .. note:: The suggested way of making PCI SYS settings persistent is through the ``sysfsutils`` tool. However, this is not available by default on many major distributions. Whitelist PCI devices nova-compute (Compute) -------------------------------------------- #. Configure which PCI devices the ``nova-compute`` service may use. Edit the ``nova.conf`` file: .. code-block:: ini [default] pci_passthrough_whitelist = { "devname": "eth3", "physical_network": "physnet2"} This tells the Compute service that all VFs belonging to ``eth3`` are allowed to be passed through to instances and belong to the provider network ``physnet2``. Alternatively the ``pci_passthrough_whitelist`` parameter also supports whitelisting by: - PCI address: The address uses the same syntax as in ``lspci`` and an asterisk (*) can be used to match anything. .. code-block:: ini pci_passthrough_whitelist = { "address": "[[[[]:]]:][][.[]]", "physical_network": "physnet2" } For example, to match any domain, bus 0a, slot 00, and all functions: .. code-block:: ini pci_passthrough_whitelist = { "address": "*:0a:00.*", "physical_network": "physnet2" } - PCI ``vendor_id`` and ``product_id`` as displayed by the Linux utility ``lspci``. .. code-block:: ini pci_passthrough_whitelist = { "vendor_id": "", "product_id": "", "physical_network": "physnet2" } If the device defined by the PCI address or ``devname`` corresponds to an SR-IOV PF, all VFs under the PF will match the entry. Multiple ``pci_passthrough_whitelist`` entries per host are supported. #. Restart the ``nova-compute`` service for the changes to go into effect. .. _configure_sriov_neutron_server: Configure neutron-server (Controller) ------------------------------------- #. Add ``sriovnicswitch`` as mechanism driver. Edit the ``ml2_conf.ini`` file on each controller: .. code-block:: ini mechanism_drivers = openvswitch,sriovnicswitch #. Add the ``plugin.ini`` file as a parameter to the ``neutron-server`` service. Edit the appropriate initialization script to configure the ``neutron-server`` service to load the plugin configuration file: .. code-block:: bash --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/plugin.ini #. Restart the ``neutron-server`` service. Configure nova-scheduler (Controller) ------------------------------------- #. On every controller node running the ``nova-scheduler`` service, add ``PciPassthroughFilter`` to ``scheduler_default_filters`` to enable ``PciPassthroughFilter`` by default. Also ensure ``scheduler_available_filters`` parameter under the ``[DEFAULT]`` section in ``nova.conf`` is set to ``all_filters`` to enable all filters provided by the Compute service. .. code-block:: ini [DEFAULT] scheduler_default_filters = RetryFilter, AvailabilityZoneFilter, RamFilter, ComputeFilter, ComputeCapabilitiesFilter, ImagePropertiesFilter, ServerGroupAntiAffinityFilter, ServerGroupAffinityFilter, PciPassthroughFilter scheduler_available_filters = nova.scheduler.filters.all_filters #. Restart the ``nova-scheduler`` service. Enable neutron sriov-agent (Compute) ------------------------------------- #. Install the SR-IOV agent. #. Edit the ``sriov_agent.ini`` file on each compute node. For example: .. code-block:: ini [securitygroup] firewall_driver = neutron.agent.firewall.NoopFirewallDriver [sriov_nic] physical_device_mappings = physnet2:eth3 exclude_devices = .. note:: The ``physical_device_mappings`` parameter is not limited to be a 1-1 mapping between physical networks and NICs. This enables you to map the same physical network to more than one NIC. For example, if ``physnet2`` is connected to ``eth3`` and ``eth4``, then ``physnet2:eth3,physnet2:eth4`` is a valid option. The ``exclude_devices`` parameter is empty, therefore, all the VFs associated with eth3 may be configured by the agent. To exclude specific VFs, add them to the ``exclude_devices`` parameter as follows: .. code-block:: ini exclude_devices = eth1:0000:07:00.2;0000:07:00.3,eth2:0000:05:00.1;0000:05:00.2 #. Ensure the neutron sriov-agent runs successfully: .. code-block:: console # neutron-sriov-nic-agent \ --config-file /etc/neutron/neutron.conf \ --config-file /etc/neutron/plugins/ml2/sriov_agent.ini #. Enable the neutron sriov-agent service. If installing from source, you must configure a daemon file for the init system manually. (Optional) FDB L2 agent extension ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Forwarding DataBase (FDB) population is an L2 agent extension to OVS agent or Linux bridge. Its objective is to update the FDB table for existing instance using normal port. This enables communication between SR-IOV instances and normal instances. The use cases of the FDB population extension are: * Direct port and normal port instances reside on the same compute node. * Direct port instance that uses floating IP address and network node are located on the same host. For additional information describing the problem, refer to: `Virtual switching technologies and Linux bridge. `_ #. Edit the ``ovs_agent.ini`` or ``linuxbridge_agent.ini`` file on each compute node. For example: .. code-block:: console [agent] extensions = fdb #. Add the FDB section and the ``shared_physical_device_mappings`` parameter. This parameter maps each physical port to its physical network name. Each physical network can be mapped to several ports: .. code-block:: console [FDB] shared_physical_device_mappings = physnet1:p1p1, physnet1:p1p2 Launching instances with SR-IOV ports ------------------------------------- Once configuration is complete, you can launch instances with SR-IOV ports. #. Get the ``id`` of the network where you want the SR-IOV port to be created: .. code-block:: console $ net_id=`neutron net-show net04 | grep "\ id\ " | awk '{ print $4 }'` #. Create the SR-IOV port. ``vnic_type=direct`` is used here, but other options include ``normal``, ``direct-physical``, and ``macvtap``: .. code-block:: console $ port_id=`neutron port-create $net_id --name sriov_port --binding:vnic_type direct | grep "\ id\ " | awk '{ print $4 }'` #. Create the instance. Specify the SR-IOV port created in step two for the NIC: .. code-block:: console $ openstack server create --flavor m1.large --image ubuntu_14.04 --nic port-id=$port_id test-sriov .. note:: There are two ways to attach VFs to an instance. You can create an SR-IOV port or use the ``pci_alias`` in the Compute service. For more information about using ``pci_alias``, refer to `nova-api configuration `__. SR-IOV with InfiniBand ~~~~~~~~~~~~~~~~~~~~~~ The support for SR-IOV with InfiniBand allows a Virtual PCI device (VF) to be directly mapped to the guest, allowing higher performance and advanced features such as RDMA (remote direct memory access). To use this feature, you must: #. Use InfiniBand enabled network adapters. #. Run InfiniBand subnet managers to enable InfiniBand fabric. All InfiniBand networks must have a subnet manager running for the network to function. This is true even when doing a simple network of two machines with no switch and the cards are plugged in back-to-back. A subnet manager is required for the link on the cards to come up. It is possible to have more than one subnet manager. In this case, one of them will act as the master, and any other will act as a slave that will take over when the master subnet manager fails. #. Install the ``ebrctl`` utility on the compute nodes. Check that ``ebrctl`` is listed somewhere in ``/etc/nova/rootwrap.d/*``: .. code-block:: console $ grep 'ebrctl' /etc/nova/rootwrap.d/* If ``ebrctl`` does not appear in any of the rootwrap files, add this to the ``/etc/nova/rootwrap.d/compute.filters`` file in the ``[Filters]`` section. .. code-block:: none [Filters] ebrctl: CommandFilter, ebrctl, root Known limitations ~~~~~~~~~~~~~~~~~ * When using Quality of Service (QoS), ``max_burst_kbps`` (burst over ``max_kbps``) is not supported. In addition, ``max_kbps`` is rounded to Mbps. * Security groups are not supported when using SR-IOV, thus, the firewall driver must be disabled. This can be done in the ``neutron.conf`` file. .. code-block:: ini [securitygroup] firewall_driver = neutron.agent.firewall.NoopFirewallDriver * SR-IOV is not integrated into the OpenStack Dashboard (horizon). Users must use the CLI or API to configure SR-IOV interfaces. * Live migration is not supported for instances with SR-IOV ports. .. note:: SR-IOV features may require a specific NIC driver version, depending on the vendor. Intel NICs, for example, require ixgbe version 4.4.6 or greater, and ixgbevf version 3.2.2 or greater. * Attaching SR-IOV ports to existing servers is not currently supported, see `bug 1708433 `_ for details. neutron-12.1.1/doc/source/admin/config-ipv6.rst0000664000175000017500000006676313553660047021361 0ustar zuulzuul00000000000000.. _config-ipv6: ==== IPv6 ==== This section describes the following items: * How to enable dual-stack (IPv4 and IPv6 enabled) instances. * How those instances receive an IPv6 address. * How those instances communicate across a router to other subnets or the internet. * How those instances interact with other OpenStack services. Enabling a dual-stack network in OpenStack Networking simply requires creating a subnet with the ``ip_version`` field set to ``6``, then the IPv6 attributes (``ipv6_ra_mode`` and ``ipv6_address_mode``) set. The ``ipv6_ra_mode`` and ``ipv6_address_mode`` will be described in detail in the next section. Finally, the subnets ``cidr`` needs to be provided. This section does not include the following items: * Single stack IPv6 project networking * OpenStack control communication between servers and services over an IPv6 network. * Connection to the OpenStack APIs via an IPv6 transport network * IPv6 multicast * IPv6 support in conjunction with any out of tree routers, switches, services or agents whether in physical or virtual form factors. Neutron subnets and the IPv6 API attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As of Juno, the OpenStack Networking service (neutron) provides two new attributes to the subnet object, which allows users of the API to configure IPv6 subnets. There are two IPv6 attributes: * ``ipv6_ra_mode`` * ``ipv6_address_mode`` These attributes can be set to the following values: * ``slaac`` * ``dhcpv6-stateful`` * ``dhcpv6-stateless`` The attributes can also be left unset. IPv6 addressing --------------- The ``ipv6_address_mode`` attribute is used to control how addressing is handled by OpenStack. There are a number of different ways that guest instances can obtain an IPv6 address, and this attribute exposes these choices to users of the Networking API. Router advertisements --------------------- The ``ipv6_ra_mode`` attribute is used to control router advertisements for a subnet. The IPv6 Protocol uses Internet Control Message Protocol packets (ICMPv6) as a way to distribute information about networking. ICMPv6 packets with the type flag set to 134 are called "Router Advertisement" packets, which contain information about the router and the route that can be used by guest instances to send network traffic. The ``ipv6_ra_mode`` is used to specify if the Networking service should generate Router Advertisement packets for a subnet. ipv6_ra_mode and ipv6_address_mode combinations ----------------------------------------------- .. list-table:: :header-rows: 1 :widths: 10 10 10 10 60 * - ipv6 ra mode - ipv6 address mode - radvd A,M,O - External Router A,M,O - Description * - *N/S* - *N/S* - Off - Not Defined - Backwards compatibility with pre-Juno IPv6 behavior. * - *N/S* - slaac - Off - 1,0,0 - Guest instance obtains IPv6 address from non-OpenStack router using SLAAC. * - *N/S* - dhcpv6-stateful - Off - 0,1,1 - Not currently implemented in the reference implementation. * - *N/S* - dhcpv6-stateless - Off - 1,0,1 - Not currently implemented in the reference implementation. * - slaac - *N/S* - 1,0,0 - Off - Not currently implemented in the reference implementation. * - dhcpv6-stateful - *N/S* - 0,1,1 - Off - Not currently implemented in the reference implementation. * - dhcpv6-stateless - *N/S* - 1,0,1 - Off - Not currently implemented in the reference implementation. * - slaac - slaac - 1,0,0 - Off - Guest instance obtains IPv6 address from OpenStack managed radvd using SLAAC. * - dhcpv6-stateful - dhcpv6-stateful - 0,1,1 - Off - Guest instance obtains IPv6 address from dnsmasq using DHCPv6 stateful and optional info from dnsmasq using DHCPv6. * - dhcpv6-stateless - dhcpv6-stateless - 1,0,1 - Off - Guest instance obtains IPv6 address from OpenStack managed radvd using SLAAC and optional info from dnsmasq using DHCPv6. * - slaac - dhcpv6-stateful - - - *Invalid combination.* * - slaac - dhcpv6-stateless - - - *Invalid combination.* * - dhcpv6-stateful - slaac - - - *Invalid combination.* * - dhcpv6-stateful - dhcpv6-stateless - - - *Invalid combination.* * - dhcpv6-stateless - slaac - - - *Invalid combination.* * - dhcpv6-stateless - dhcpv6-stateful - - - *Invalid combination.* Project network considerations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Dataplane --------- Both the Linux bridge and the Open vSwitch dataplane modules support forwarding IPv6 packets amongst the guests and router ports. Similar to IPv4, there is no special configuration or setup required to enable the dataplane to properly forward packets from the source to the destination using IPv6. Note that these dataplanes will forward Link-local Address (LLA) packets between hosts on the same network just fine without any participation or setup by OpenStack components after the ports are all connected and MAC addresses learned. Addresses for subnets --------------------- There are three methods currently implemented for a subnet to get its ``cidr`` in OpenStack: #. Direct assignment during subnet creation via command line or Horizon #. Referencing a subnet pool during subnet creation #. Using a Prefix Delegation (PD) client to request a prefix for a subnet from a PD server In the future, additional techniques could be used to allocate subnets to projects, for example, use of an external IPAM module. Address modes for ports ----------------------- .. note:: An external DHCPv6 server in theory could override the full address OpenStack assigns based on the EUI-64 address, but that would not be wise as it would not be consistent through the system. IPv6 supports three different addressing schemes for address configuration and for providing optional network information. Stateless Address Auto Configuration (SLAAC) Address configuration using Router Advertisement (RA). DHCPv6-stateless Address configuration using RA and optional information using DHCPv6. DHCPv6-stateful Address configuration and optional information using DHCPv6. OpenStack can be setup such that OpenStack Networking directly provides RA, DHCP relay and DHCPv6 address and optional information for their networks or this can be delegated to external routers and services based on the drivers that are in use. There are two neutron subnet attributes - ``ipv6_ra_mode`` and ``ipv6_address_mode`` – that determine how IPv6 addressing and network information is provided to project instances: * ``ipv6_ra_mode``: Determines who sends RA. * ``ipv6_address_mode``: Determines how instances obtain IPv6 address, default gateway, or optional information. For the above two attributes to be effective, ``enable_dhcp`` of the subnet object must be set to True. Using SLAAC for addressing -------------------------- When using SLAAC, the currently supported combinations for ``ipv6_ra_mode`` and ``ipv6_address_mode`` are as follows. .. list-table:: :header-rows: 1 :widths: 10 10 50 * - ipv6_ra_mode - ipv6_address_mode - Result * - Not specified. - SLAAC - Addresses are assigned using EUI-64, and an external router will be used for routing. * - SLAAC - SLAAC - Address are assigned using EUI-64, and OpenStack Networking provides routing. Setting ``ipv6_ra_mode`` to ``slaac`` will result in OpenStack Networking routers being configured to send RA packets, when they are created. This results in the following values set for the address configuration flags in the RA messages: * Auto Configuration Flag = 1 * Managed Configuration Flag = 0 * Other Configuration Flag = 0 New or existing neutron networks that contain a SLAAC enabled IPv6 subnet will result in all neutron ports attached to the network receiving IPv6 addresses. This is because when RA broadcast messages are sent out on a neutron network, they are received by all IPv6 capable ports on the network, and each port will then configure an IPv6 address based on the information contained in the RA packet. In some cases, an IPv6 SLAAC address will be added to a port, in addition to other IPv4 and IPv6 addresses that the port already has been assigned. DHCPv6 ------ For DHCPv6, the currently supported combinations are as follows: .. list-table:: :header-rows: 1 :widths: 10 10 50 * - ipv6_ra_mode - ipv6_address_mode - Result * - DHCPv6-stateless - DHCPv6-stateless - Addresses are assigned through RAs (see SLAAC above) and optional information is delivered through DHCPv6. * - DHCPv6-stateful - DHCPv6-stateful - Addresses and optional information are assigned using DHCPv6. Setting DHCPv6-stateless for ``ipv6_ra_mode`` configures the neutron router with radvd agent to send RAs. The list below captures the values set for the address configuration flags in the RA packet in this scenario. Similarly, setting DHCPv6-stateless for ``ipv6_address_mode`` configures neutron DHCP implementation to provide the additional network information. * Auto Configuration Flag = 1 * Managed Configuration Flag = 0 * Other Configuration Flag = 1 Setting DHCPv6-stateful for ``ipv6_ra_mode`` configures the neutron router with radvd agent to send RAs. The list below captures the values set for the address configuration flags in the RA packet in this scenario. Similarly, setting DHCPv6-stateful for ``ipv6_address_mode`` configures neutron DHCP implementation to provide addresses and additional network information through DHCPv6. * Auto Configuration Flag = 0 * Managed Configuration Flag = 1 * Other Configuration Flag = 1 Router support ~~~~~~~~~~~~~~ The behavior of the neutron router for IPv6 is different than for IPv4 in a few ways. Internal router ports, that act as default gateway ports for a network, will share a common port for all IPv6 subnets associated with the network. This implies that there will be an IPv6 internal router interface with multiple IPv6 addresses from each of the IPv6 subnets associated with the network and a separate IPv4 internal router interface for the IPv4 subnet. On the other hand, external router ports are allowed to have a dual-stack configuration with both an IPv4 and an IPv6 address assigned to them. Neutron project networks that are assigned Global Unicast Address (GUA) prefixes and addresses don't require NAT on the neutron router external gateway port to access the outside world. As a consequence of the lack of NAT the external router port doesn't require a GUA to send and receive to the external networks. This implies a GUA IPv6 subnet prefix is not necessarily needed for the neutron external network. By default, a IPv6 LLA associated with the external gateway port can be used for routing purposes. To handle this scenario, the implementation of router-gateway-set API in neutron has been modified so that an IPv6 subnet is not required for the external network that is associated with the neutron router. The LLA address of the upstream router can be learned in two ways. #. In the absence of an upstream RA support, ``ipv6_gateway`` flag can be set with the external router gateway LLA in the neutron L3 agent configuration file. This also requires that no subnet is associated with that port. #. The upstream router can send an RA and the neutron router will automatically learn the next-hop LLA, provided again that no subnet is assigned and the ``ipv6_gateway`` flag is not set. Effectively the ``ipv6_gateway`` flag takes precedence over an RA that is received from the upstream router. If it is desired to use a GUA next hop that is accomplished by allocating a subnet to the external router port and assigning the upstream routers GUA address as the gateway for the subnet. .. note:: It should be possible for projects to communicate with each other on an isolated network (a network without a router port) using LLA with little to no participation on the part of OpenStack. The authors of this section have not proven that to be true for all scenarios. .. note:: When using the neutron L3 agent in a configuration where it is auto-configuring an IPv6 address via SLAAC, and the agent is learning its default IPv6 route from the ICMPv6 Router Advertisement, it may be necessary to set the ``net.ipv6.conf..accept_ra`` sysctl to the value ``2`` in order for routing to function correctly. For a more detailed description, please see the `bug `__. Neutron's Distributed Router feature and IPv6 --------------------------------------------- IPv6 does work when the Distributed Virtual Router functionality is enabled, but all ingress/egress traffic is via the centralized router (hence, not distributed). More work is required to fully enable this functionality. Advanced services ~~~~~~~~~~~~~~~~~ VPNaaS ------ VPNaaS supports IPv6, but support in Kilo and prior releases will have some bugs that may limit how it can be used. More thorough and complete testing and bug fixing is being done as part of the Liberty release. IPv6-based VPN-as-a-Service is configured similar to the IPv4 configuration. Either or both the ``peer_address`` and the ``peer_cidr`` can specified as an IPv6 address. The choice of addressing modes and router modes described above should not impact support. LBaaS ----- TODO FWaaS ----- FWaaS allows creation of IPv6 based rules. NAT & Floating IPs ------------------ At the current time OpenStack Networking does not provide any facility to support any flavor of NAT with IPv6. Unlike IPv4 there is no current embedded support for floating IPs with IPv6. It is assumed that the IPv6 addressing amongst the projects is using GUAs with no overlap across the projects. Security considerations ~~~~~~~~~~~~~~~~~~~~~~~ .. todo:: Initially this is probably just stating the security group rules relative to IPv6 that are applied. Need some help for these Configuring interfaces of the guest ----------------------------------- OpenStack currently doesn't support the privacy extensions defined by RFC 4941. The interface identifier and DUID used must be directly derived from the MAC as described in RFC 2373. The compute hosts must not be setup to utilize the privacy extensions when generating their interface identifier. There is no provisions for an IPv6-based metadata service similar to what is provided for IPv4. In the case of dual stacked guests though it is always possible to use the IPv4 metadata service instead. Unlike IPv4 the MTU of a given network can be conveyed in the RA messages sent by the router as well as in the DHCP messages. OpenStack control & management network considerations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As of the Kilo release, considerable effort has gone in to ensuring the project network can handle dual stack IPv6 and IPv4 transport across the variety of configurations described above. OpenStack control network can be run in a dual stack configuration and OpenStack API endpoints can be accessed via an IPv6 network. At this time, Open vSwitch (OVS) tunnel types - STT, VXLAN, GRE, support both IPv4 and IPv6 endpoints. Prefix delegation ~~~~~~~~~~~~~~~~~ From the Liberty release onwards, OpenStack Networking supports IPv6 prefix delegation. This section describes the configuration and workflow steps necessary to use IPv6 prefix delegation to provide automatic allocation of subnet CIDRs. This allows you as the OpenStack administrator to rely on an external (to the OpenStack Networking service) DHCPv6 server to manage your project network prefixes. .. note:: Prefix delegation became available in the Liberty release, it is not available in the Kilo release. HA and DVR routers are not currently supported by this feature. Configuring OpenStack Networking for prefix delegation ------------------------------------------------------ To enable prefix delegation, edit the ``/etc/neutron/neutron.conf`` file. .. code-block:: console ipv6_pd_enabled = True .. note:: If you are not using the default dibbler-based driver for prefix delegation, then you also need to set the driver in ``/etc/neutron/neutron.conf``: .. code-block:: console pd_dhcp_driver = Drivers other than the default one may require extra configuration, please refer to :ref:`extra-driver-conf` This tells OpenStack Networking to use the prefix delegation mechanism for subnet allocation when the user does not provide a CIDR or subnet pool id when creating a subnet. Requirements ------------ To use this feature, you need a prefix delegation capable DHCPv6 server that is reachable from your OpenStack Networking node(s). This could be software running on the OpenStack Networking node(s) or elsewhere, or a physical router. For the purposes of this guide we are using the open-source DHCPv6 server, Dibbler. Dibbler is available in many Linux package managers, or from source at `tomaszmrugalski/dibbler `_. When using the reference implementation of the OpenStack Networking prefix delegation driver, Dibbler must also be installed on your OpenStack Networking node(s) to serve as a DHCPv6 client. Version 1.0.1 or higher is required. This guide assumes that you are running a Dibbler server on the network node where the external network bridge exists. If you already have a prefix delegation capable DHCPv6 server in place, then you can skip the following section. Configuring the Dibbler server ------------------------------ After installing Dibbler, edit the ``/etc/dibbler/server.conf`` file: .. code-block:: none script "/var/lib/dibbler/pd-server.sh" iface "br-ex" { pd-class { pd-pool 2001:db8:2222::/48 pd-length 64 } } The options used in the configuration file above are: - ``script`` Points to a script to be run when a prefix is delegated or released. This is only needed if you want instances on your subnets to have external network access. More on this below. - ``iface`` The name of the network interface on which to listen for prefix delegation messages. - ``pd-pool`` The larger prefix from which you want your delegated prefixes to come. The example given is sufficient if you do not need external network access, otherwise a unique globally routable prefix is necessary. - ``pd-length`` The length that delegated prefixes will be. This must be 64 to work with the current OpenStack Networking reference implementation. To provide external network access to your instances, your Dibbler server also needs to create new routes for each delegated prefix. This is done using the script file named in the config file above. Edit the ``/var/lib/dibbler/pd-server.sh`` file: .. code-block:: bash if [ "$PREFIX1" != "" ]; then if [ "$1" == "add" ]; then sudo ip -6 route add ${PREFIX1}/64 via $REMOTE_ADDR dev $IFACE fi if [ "$1" == "delete" ]; then sudo ip -6 route del ${PREFIX1}/64 via $REMOTE_ADDR dev $IFACE fi fi The variables used in the script file above are: - ``$PREFIX1`` The prefix being added/deleted by the Dibbler server. - ``$1`` The operation being performed. - ``$REMOTE_ADDR`` The IP address of the requesting Dibbler client. - ``$IFACE`` The network interface upon which the request was received. The above is all you need in this scenario, but more information on installing, configuring, and running Dibbler is available in the Dibbler user guide, at `Dibbler – a portable DHCPv6 `_. To start your Dibbler server, run: .. code-block:: console # dibbler-server run Or to run in headless mode: .. code-block:: console # dibbler-server start When using DevStack, it is important to start your server after the ``stack.sh`` script has finished to ensure that the required network interfaces have been created. User workflow ------------- First, create a network and IPv6 subnet: .. code-block:: console $ openstack network create ipv6-pd +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2017-01-25T19:26:01Z | | description | | | headers | | | id | 4b782725-6abe-4a2d-b061-763def1bb029 | | ipv4_address_scope | None | | ipv6_address_scope | None | | mtu | 1450 | | name | ipv6-pd | | port_security_enabled | True | | project_id | 61b7eba037fd41f29cfba757c010faff | | provider:network_type | vxlan | | provider:physical_network | None | | provider:segmentation_id | 46 | | revision_number | 3 | | router:external | Internal | | shared | False | | status | ACTIVE | | subnets | | | tags | [] | | updated_at | 2017-01-25T19:26:01Z | +---------------------------+--------------------------------------+ $ openstack subnet create --ip-version 6 --ipv6-ra-mode slaac \ --ipv6-address-mode slaac --use-default-subnet-pool \ --network ipv6-pd ipv6-pd-1 +------------------------+--------------------------------------+ | Field | Value | +------------------------+--------------------------------------+ | allocation_pools | ::2-::ffff:ffff:ffff:ffff | | cidr | ::/64 | | created_at | 2017-01-25T19:31:53Z | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | ::1 | | headers | | | host_routes | | | id | 1319510d-c92c-4532-bf5d-8bcf3da761a1 | | ip_version | 6 | | ipv6_address_mode | slaac | | ipv6_ra_mode | slaac | | name | ipv6-pd-1 | | network_id | 4b782725-6abe-4a2d-b061-763def1bb029 | | project_id | 61b7eba037fd41f29cfba757c010faff | | revision_number | 2 | | service_types | | | subnetpool_id | prefix_delegation | | tags | [] | | updated_at | 2017-01-25T19:31:53Z | | use_default_subnetpool | True | +------------------------+--------------------------------------+ The subnet is initially created with a temporary CIDR before one can be assigned by prefix delegation. Any number of subnets with this temporary CIDR can exist without raising an overlap error. The subnetpool_id is automatically set to ``prefix_delegation``. To trigger the prefix delegation process, create a router interface between this subnet and a router with an active interface on the external network: .. code-block:: console $ openstack router add subnet router1 ipv6-pd-1 The prefix delegation mechanism then sends a request via the external network to your prefix delegation server, which replies with the delegated prefix. The subnet is then updated with the new prefix, including issuing new IP addresses to all ports: .. code-block:: console $ openstack subnet show ipv6-pd-1 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | allocation_pools | 2001:db8:2222:6977::2-2001:db8:2222: | | | 6977:ffff:ffff:ffff:ffff | | cidr | 2001:db8:2222:6977::/64 | | created_at | 2017-01-25T19:31:53Z | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 2001:db8:2222:6977::1 | | host_routes | | | id | 1319510d-c92c-4532-bf5d-8bcf3da761a1 | | ip_version | 6 | | ipv6_address_mode | slaac | | ipv6_ra_mode | slaac | | name | ipv6-pd-1 | | network_id | 4b782725-6abe-4a2d-b061-763def1bb029 | | project_id | 61b7eba037fd41f29cfba757c010faff | | revision_number | 4 | | service_types | | | subnetpool_id | prefix_delegation | | tags | [] | | updated_at | 2017-01-25T19:35:26Z | +-------------------+--------------------------------------+ If the prefix delegation server is configured to delegate globally routable prefixes and setup routes, then any instance with a port on this subnet should now have external network access. Deleting the router interface causes the subnet to be reverted to the temporary CIDR, and all ports have their IPs updated. Prefix leases are released and renewed automatically as necessary. References ---------- The following link provides a great step by step tutorial on setting up IPv6 with OpenStack: `Tenant IPV6 deployment in OpenStack Kilo release `_. .. _extra-driver-conf: Extra configuration ------------------- Neutron dhcpv6_pd_agent ^^^^^^^^^^^^^^^^^^^^^^^ To enable the driver for the dhcpv6_pd_agent, set pd_dhcp_driver to this in ``/etc/neutron/neutron.conf``: .. code-block:: console pd_dhcp_driver = neutron_pd_agent To allow the neutron-pd-agent to communicate with prefix delegation servers, you must set which network interface to use for external communication. In DevStack the default for this is ``br-ex``: .. code-block:: console pd_interface = br-ex Once you have stacked run the command below to start the neutron-pd-agent: .. code-block:: console neutron-pd-agent --config-file /etc/neutron/neutron.conf neutron-12.1.1/doc/source/admin/config-subnet-pools.rst0000664000175000017500000003063013553660046023106 0ustar zuulzuul00000000000000.. _config-subnet-pools: ============ Subnet pools ============ Subnet pools have been made available since the Kilo release. It is a simple feature that has the potential to improve your workflow considerably. It also provides a building block from which other new features will be built in to OpenStack Networking. To see if your cloud has this feature available, you can check that it is listed in the supported aliases. You can do this with the OpenStack client. .. code-block:: console $ openstack extension list | grep subnet_allocation | Subnet Allocation | subnet_allocation | Enables allocation of subnets from a subnet pool | Why you need them ~~~~~~~~~~~~~~~~~ Before Kilo, Networking had no automation around the addresses used to create a subnet. To create one, you had to come up with the addresses on your own without any help from the system. There are valid use cases for this but if you are interested in the following capabilities, then subnet pools might be for you. First, would not it be nice if you could turn your pool of addresses over to Neutron to take care of? When you need to create a subnet, you just ask for addresses to be allocated from the pool. You do not have to worry about what you have already used and what addresses are in your pool. Subnet pools can do this. Second, subnet pools can manage addresses across projects. The addresses are guaranteed not to overlap. If the addresses come from an externally routable pool then you know that all of the projects have addresses which are *routable* and unique. This can be useful in the following scenarios. #. IPv6 since OpenStack Networking has no IPv6 floating IPs. #. Routing directly to a project network from an external network. How they work ~~~~~~~~~~~~~ A subnet pool manages a pool of addresses from which subnets can be allocated. It ensures that there is no overlap between any two subnets allocated from the same pool. As a regular project in an OpenStack cloud, you can create a subnet pool of your own and use it to manage your own pool of addresses. This does not require any admin privileges. Your pool will not be visible to any other project. If you are an admin, you can create a pool which can be accessed by any regular project. Being a shared resource, there is a quota mechanism to arbitrate access. Quotas ~~~~~~ Subnet pools have a quota system which is a little bit different than other quotas in Neutron. Other quotas in Neutron count discrete instances of an object against a quota. Each time you create something like a router, network, or a port, it uses one from your total quota. With subnets, the resource is the IP address space. Some subnets take more of it than others. For example, 203.0.113.0/24 uses 256 addresses in one subnet but 198.51.100.224/28 uses only 16. If address space is limited, the quota system can encourage efficient use of the space. With IPv4, the default_quota can be set to the number of absolute addresses any given project is allowed to consume from the pool. For example, with a quota of 128, I might get 203.0.113.128/26, 203.0.113.224/28, and still have room to allocate 48 more addresses in the future. With IPv6 it is a little different. It is not practical to count individual addresses. To avoid ridiculously large numbers, the quota is expressed in the number of /64 subnets which can be allocated. For example, with a default_quota of 3, I might get 2001:db8:c18e:c05a::/64, 2001:db8:221c:8ef3::/64, and still have room to allocate one more prefix in the future. Default subnet pools ~~~~~~~~~~~~~~~~~~~~ Beginning with Mitaka, a subnet pool can be marked as the default. This is handled with a new extension. .. code-block:: console $ openstack extension list | grep default-subnetpools | Default Subnetpools | default-subnetpools | Provides ability to mark and use a subnetpool as the default | An administrator can mark a pool as default. Only one pool from each address family can be marked default. .. code-block:: console $ openstack subnet pool set --default 74348864-f8bf-4fc0-ab03-81229d189467 If there is a default, it can be requested by passing ``--use-default-subnetpool`` instead of ``--subnet-pool SUBNETPOOL``. Demo ---- If you have access to an OpenStack Kilo or later based neutron, you can play with this feature now. Give it a try. All of the following commands work equally as well with IPv6 addresses. First, as admin, create a shared subnet pool: .. code-block:: console $ openstack subnet pool create --share --pool-prefix 203.0.113.0/24 \ --default-prefix-length 26 demo-subnetpool4 +-------------------+--------------------------------+ | Field | Value | +-------------------+--------------------------------+ | address_scope_id | None | | created_at | 2016-12-14T07:21:26Z | | default_prefixlen | 26 | | default_quota | None | | description | | | headers | | | id | d3aefb76-2527-43d4-bc21-0ec253 | | | 908545 | | ip_version | 4 | | is_default | False | | max_prefixlen | 32 | | min_prefixlen | 8 | | name | demo-subnetpool4 | | prefixes | 203.0.113.0/24 | | project_id | cfd1889ac7d64ad891d4f20aef9f8d | | | 7c | | revision_number | 1 | | shared | True | | tags | [] | | updated_at | 2016-12-14T07:21:26Z | +-------------------+--------------------------------+ The ``default_prefix_length`` defines the subnet size you will get if you do not specify ``--prefix-length`` when creating a subnet. Do essentially the same thing for IPv6 and there are now two subnet pools. Regular projects can see them. (the output is trimmed a bit for display) .. code-block:: console $ openstack subnet pool list +------------------+------------------+--------------------+ | ID | Name | Prefixes | +------------------+------------------+--------------------+ | 2b7cc19f-0114-4e | demo-subnetpool | 2001:db8:a583::/48 | | f4-ad86-c1bb91fc | | | | d1f9 | | | | d3aefb76-2527-43 | demo-subnetpool4 | 203.0.113.0/24 | | d4-bc21-0ec25390 | | | | 8545 | | | +------------------+------------------+--------------------+ Now, use them. It is easy to create a subnet from a pool: .. code-block:: console $ openstack subnet create --ip-version 4 --subnet-pool \ demo-subnetpool4 --network demo-network1 demo-subnet1 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | allocation_pools | 203.0.113.194-203.0.113.254 | | cidr | 203.0.113.192/26 | | created_at | 2016-12-14T07:33:13Z | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 203.0.113.193 | | headers | | | host_routes | | | id | 8d4fbae3-076c-4c08-b2dd-2d6175115a5e | | ip_version | 4 | | ipv6_address_mode | None | | ipv6_ra_mode | None | | name | demo-subnet1 | | network_id | 6b377f77-ce00-4ff6-8676-82343817470d | | project_id | cfd1889ac7d64ad891d4f20aef9f8d7c | | revision_number | 2 | | service_types | | | subnetpool_id | d3aefb76-2527-43d4-bc21-0ec253908545 | | tags | [] | | updated_at | 2016-12-14T07:33:13Z | +-------------------+--------------------------------------+ You can request a specific subnet from the pool. You need to specify a subnet that falls within the pool's prefixes. If the subnet is not already allocated, the request succeeds. You can leave off the IP version because it is deduced from the subnet pool. .. code-block:: console $ openstack subnet create --subnet-pool demo-subnetpool4 \ --network demo-network1 --subnet-range 203.0.113.128/26 subnet2 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | allocation_pools | 203.0.113.130-203.0.113.190 | | cidr | 203.0.113.128/26 | | created_at | 2016-12-14T07:27:40Z | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 203.0.113.129 | | headers | | | host_routes | | | id | d32814e3-cf46-4371-80dd-498a80badfba | | ip_version | 4 | | ipv6_address_mode | None | | ipv6_ra_mode | None | | name | subnet2 | | network_id | 6b377f77-ce00-4ff6-8676-82343817470d | | project_id | cfd1889ac7d64ad891d4f20aef9f8d7c | | revision_number | 2 | | service_types | | | subnetpool_id | d3aefb76-2527-43d4-bc21-0ec253908545 | | tags | [] | | updated_at | 2016-12-14T07:27:40Z | +-------------------+--------------------------------------+ If the pool becomes exhausted, load some more prefixes: .. code-block:: console $ openstack subnet pool set --pool-prefix \ 198.51.100.0/24 demo-subnetpool4 $ openstack subnet pool show demo-subnetpool4 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | address_scope_id | None | | created_at | 2016-12-14T07:21:26Z | | default_prefixlen | 26 | | default_quota | None | | description | | | id | d3aefb76-2527-43d4-bc21-0ec253908545 | | ip_version | 4 | | is_default | False | | max_prefixlen | 32 | | min_prefixlen | 8 | | name | demo-subnetpool4 | | prefixes | 198.51.100.0/24, 203.0.113.0/24 | | project_id | cfd1889ac7d64ad891d4f20aef9f8d7c | | revision_number | 2 | | shared | True | | tags | [] | | updated_at | 2016-12-14T07:30:32Z | +-------------------+--------------------------------------+ neutron-12.1.1/doc/source/admin/config-lbaas.rst0000664000175000017500000004327313553660047021546 0ustar zuulzuul00000000000000.. _config-lbaas: ================================== Load Balancer as a Service (LBaaS) ================================== The Networking service offers a load balancer feature called "LBaaS v2" through the ``neutron-lbaas`` service plug-in. LBaaS v2 adds the concept of listeners to the LBaaS v1 load balancers. LBaaS v2 allows you to configure multiple listener ports on a single load balancer IP address. There are two reference implementations of LBaaS v2. The one is an agent based implementation with HAProxy. The agents handle the HAProxy configuration and manage the HAProxy daemon. Another LBaaS v2 implementation, `Octavia `_, has a separate API and separate worker processes that build load balancers within virtual machines on hypervisors that are managed by the Compute service. You do not need an agent for Octavia. .. warning:: Currently, no migration path exists between v1 and v2 load balancers. If you choose to switch from v1 to v2, you must recreate all load balancers, pools, and health monitors. .. TODO(amotoki): Data mirgation from v1 to v2 is provided in Newton release, but its usage is not documented enough. It should be added here. LBaaS v2 Concepts ~~~~~~~~~~~~~~~~~ LBaaS v2 has several new concepts to understand: .. image:: figures/lbaasv2-diagram.png :alt: LBaaS v2 layout Load balancer The load balancer occupies a neutron network port and has an IP address assigned from a subnet. Listener Load balancers can listen for requests on multiple ports. Each one of those ports is specified by a listener. Pool A pool holds a list of members that serve content through the load balancer. Member Members are servers that serve traffic behind a load balancer. Each member is specified by the IP address and port that it uses to serve traffic. Health monitor Members may go offline from time to time and health monitors divert traffic away from members that are not responding properly. Health monitors are associated with pools. LBaaS v2 has multiple implementations via different service plug-ins. The two most common implementations use either an agent or the Octavia services. Both implementations use the `LBaaS v2 API `_. Configurations ~~~~~~~~~~~~~~ Configuring LBaaS v2 with an agent ---------------------------------- #. Add the LBaaS v2 service plug-in to the ``service_plugins`` configuration directive in ``/etc/neutron/neutron.conf``. The plug-in list is comma-separated: .. code-block:: console service_plugins = [existing service plugins],neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPluginv2 #. Add the LBaaS v2 service provider to the ``service_provider`` configuration directive within the ``[service_providers]`` section in ``/etc/neutron/neutron_lbaas.conf``: .. code-block:: console service_provider = LOADBALANCERV2:Haproxy:neutron_lbaas.drivers.haproxy.plugin_driver.HaproxyOnHostPluginDriver:default If you have existing service providers for other networking service plug-ins, such as VPNaaS or FWaaS, add the ``service_provider`` line shown above in the ``[service_providers]`` section as a separate line. These configuration directives are repeatable and are not comma-separated. #. Select the driver that manages virtual interfaces in ``/etc/neutron/lbaas_agent.ini``: .. code-block:: console [DEFAULT] device_driver = neutron_lbaas.drivers.haproxy.namespace_driver.HaproxyNSDriver interface_driver = INTERFACE_DRIVER [haproxy] user_group = haproxy Replace ``INTERFACE_DRIVER`` with the interface driver that the layer-2 agent in your environment uses. For example, ``openvswitch`` for Open vSwitch or ``linuxbridge`` for Linux bridge. #. Run the ``neutron-lbaas`` database migration: .. code-block:: console neutron-db-manage --subproject neutron-lbaas upgrade head #. If you have deployed LBaaS v1, **stop the LBaaS v1 agent now**. The v1 and v2 agents **cannot** run simultaneously. #. Start the LBaaS v2 agent: .. code-block:: console neutron-lbaasv2-agent \ --config-file /etc/neutron/neutron.conf \ --config-file /etc/neutron/lbaas_agent.ini #. Restart the Network service to activate the new configuration. You are now ready to create load balancers with the LBaaS v2 agent. Configuring LBaaS v2 with Octavia --------------------------------- Octavia provides additional capabilities for load balancers, including using a compute driver to build instances that operate as load balancers. The `Hands on Lab - Install and Configure OpenStack Octavia `_ session at the OpenStack Summit in Tokyo provides an overview of Octavia. The DevStack documentation offers a `simple method to deploy Octavia `_ and test the service with redundant load balancer instances. If you already have Octavia installed and configured within your environment, you can configure the Network service to use Octavia: #. Add the LBaaS v2 service plug-in to the ``service_plugins`` configuration directive in ``/etc/neutron/neutron.conf``. The plug-in list is comma-separated: .. code-block:: console service_plugins = [existing service plugins],neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPluginv2 #. Add the Octavia service provider to the ``service_provider`` configuration directive within the ``[service_providers]`` section in ``/etc/neutron/neutron_lbaas.conf``: .. code-block:: console service_provider = LOADBALANCERV2:Octavia:neutron_lbaas.drivers.octavia.driver.OctaviaDriver:default Ensure that the LBaaS v1 and v2 service providers are removed from the ``[service_providers]`` section. They are not used with Octavia. **Verify that all LBaaS agents are stopped.** #. Restart the Network service to activate the new configuration. You are now ready to create and manage load balancers with Octavia. Add LBaaS panels to Dashboard ----------------------------- The Dashboard panels for managing LBaaS v2 are available starting with the Mitaka release. #. Clone the `neutron-lbaas-dashboard repository `__ and check out the release branch that matches the installed version of Dashboard: .. code-block:: console $ git clone https://git.openstack.org/openstack/neutron-lbaas-dashboard $ cd neutron-lbaas-dashboard $ git checkout OPENSTACK_RELEASE #. Install the Dashboard panel plug-in: .. code-block:: console $ python setup.py install #. Copy the ``_1481_project_ng_loadbalancersv2_panel.py`` file from the ``neutron-lbaas-dashboard/enabled`` directory into the Dashboard ``openstack_dashboard/local/enabled`` directory. This step ensures that Dashboard can find the plug-in when it enumerates all of its available panels. #. Enable the plug-in in Dashboard by editing the ``local_settings.py`` file and setting ``enable_lb`` to ``True`` in the ``OPENSTACK_NEUTRON_NETWORK`` dictionary. #. If Dashboard is configured to compress static files for better performance (usually set through ``COMPRESS_OFFLINE`` in ``local_settings.py``), optimize the static files again: .. code-block:: console $ ./manage.py collectstatic $ ./manage.py compress #. Restart Apache to activate the new panel: .. code-block:: console $ sudo service apache2 restart To find the panel, click on :guilabel:`Project` in Dashboard, then click the :guilabel:`Network` drop-down menu and select :guilabel:`Load Balancers`. LBaaS v2 operations ~~~~~~~~~~~~~~~~~~~ The same neutron commands are used for LBaaS v2 with an agent or with Octavia. Building an LBaaS v2 load balancer ---------------------------------- #. Start by creating a load balancer on a network. In this example, the ``private`` network is an isolated network with two web server instances: .. code-block:: console $ neutron lbaas-loadbalancer-create --name test-lb private-subnet #. You can view the load balancer status and IP address with the :command:`neutron lbaas-loadbalancer-show` command: .. code-block:: console $ neutron lbaas-loadbalancer-show test-lb +---------------------+------------------------------------------------+ | Field | Value | +---------------------+------------------------------------------------+ | admin_state_up | True | | description | | | id | 7780f9dd-e5dd-43a9-af81-0d2d1bd9c386 | | listeners | {"id": "23442d6a-4d82-40ee-8d08-243750dbc191"} | | | {"id": "7e0d084d-6d67-47e6-9f77-0115e6cf9ba8"} | | name | test-lb | | operating_status | ONLINE | | provider | octavia | | provisioning_status | ACTIVE | | tenant_id | fbfce4cb346c4f9097a977c54904cafd | | vip_address | 192.0.2.22 | | vip_port_id | 9f8f8a75-a731-4a34-b622-864907e1d556 | | vip_subnet_id | f1e7827d-1bfe-40b6-b8f0-2d9fd946f59b | +---------------------+------------------------------------------------+ #. Update the security group to allow traffic to reach the new load balancer. Create a new security group along with ingress rules to allow traffic into the new load balancer. The neutron port for the load balancer is shown as ``vip_port_id`` above. Create a security group and rules to allow TCP port 80, TCP port 443, and all ICMP traffic: .. code-block:: console $ neutron security-group-create lbaas $ neutron security-group-rule-create \ --direction ingress \ --protocol tcp \ --port-range-min 80 \ --port-range-max 80 \ --remote-ip-prefix 0.0.0.0/0 \ lbaas $ neutron security-group-rule-create \ --direction ingress \ --protocol tcp \ --port-range-min 443 \ --port-range-max 443 \ --remote-ip-prefix 0.0.0.0/0 \ lbaas $ neutron security-group-rule-create \ --direction ingress \ --protocol icmp \ lbaas Apply the security group to the load balancer's network port using ``vip_port_id`` from the :command:`neutron lbaas-loadbalancer-show` command: .. code-block:: console $ neutron port-update \ --security-group lbaas \ 9f8f8a75-a731-4a34-b622-864907e1d556 Adding an HTTP listener ----------------------- #. With the load balancer online, you can add a listener for plaintext HTTP traffic on port 80: .. code-block:: console $ neutron lbaas-listener-create \ --name test-lb-http \ --loadbalancer test-lb \ --protocol HTTP \ --protocol-port 80 This load balancer is active and ready to serve traffic on ``192.0.2.22``. #. Verify that the load balancer is responding to pings before moving further: .. code-block:: console $ ping -c 4 192.0.2.22 PING 192.0.2.22 (192.0.2.22) 56(84) bytes of data. 64 bytes from 192.0.2.22: icmp_seq=1 ttl=62 time=0.410 ms 64 bytes from 192.0.2.22: icmp_seq=2 ttl=62 time=0.407 ms 64 bytes from 192.0.2.22: icmp_seq=3 ttl=62 time=0.396 ms 64 bytes from 192.0.2.22: icmp_seq=4 ttl=62 time=0.397 ms --- 192.0.2.22 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 2997ms rtt min/avg/max/mdev = 0.396/0.402/0.410/0.020 ms #. You can begin building a pool and adding members to the pool to serve HTTP content on port 80. For this example, the web servers are ``192.0.2.16`` and ``192.0.2.17``: .. code-block:: console $ neutron lbaas-pool-create \ --name test-lb-pool-http \ --lb-algorithm ROUND_ROBIN \ --listener test-lb-http \ --protocol HTTP $ neutron lbaas-member-create \ --name test-lb-http-member-1 \ --subnet private-subnet \ --address 192.0.2.16 \ --protocol-port 80 \ test-lb-pool-http $ neutron lbaas-member-create \ --name test-lb-http-member-2 \ --subnet private-subnet \ --address 192.0.2.17 \ --protocol-port 80 \ test-lb-pool-http #. You can use ``curl`` to verify connectivity through the load balancers to your web servers: .. code-block:: console $ curl 192.0.2.22 web2 $ curl 192.0.2.22 web1 $ curl 192.0.2.22 web2 $ curl 192.0.2.22 web1 In this example, the load balancer uses the round robin algorithm and the traffic alternates between the web servers on the backend. #. You can add a health monitor so that unresponsive servers are removed from the pool: .. code-block:: console $ neutron lbaas-healthmonitor-create \ --name test-lb-http-monitor \ --delay 5 \ --max-retries 2 \ --timeout 10 \ --type HTTP \ --pool test-lb-pool-http In this example, the health monitor removes the server from the pool if it fails a health check at two five-second intervals. When the server recovers and begins responding to health checks again, it is added to the pool once again. Adding an HTTPS listener ------------------------ You can add another listener on port 443 for HTTPS traffic. LBaaS v2 offers SSL/TLS termination at the load balancer, but this example takes a simpler approach and allows encrypted connections to terminate at each member server. #. Start by creating a listener, attaching a pool, and then adding members: .. code-block:: console $ neutron lbaas-listener-create \ --name test-lb-https \ --loadbalancer test-lb \ --protocol HTTPS \ --protocol-port 443 $ neutron lbaas-pool-create \ --name test-lb-pool-https \ --lb-algorithm LEAST_CONNECTIONS \ --listener test-lb-https \ --protocol HTTPS $ neutron lbaas-member-create \ --name test-lb-https-member-1 \ --subnet private-subnet \ --address 192.0.2.16 \ --protocol-port 443 \ test-lb-pool-https $ neutron lbaas-member-create \ --name test-lb-https-member-2 \ --subnet private-subnet \ --address 192.0.2.17 \ --protocol-port 443 \ test-lb-pool-https #. You can also add a health monitor for the HTTPS pool: .. code-block:: console $ neutron lbaas-healthmonitor-create \ --name test-lb-https-monitor \ --delay 5 \ --max-retries 2 \ --timeout 10 \ --type HTTPS \ --pool test-lb-pool-https The load balancer now handles traffic on ports 80 and 443. Associating a floating IP address --------------------------------- Load balancers that are deployed on a public or provider network that are accessible to external clients do not need a floating IP address assigned. External clients can directly access the virtual IP address (VIP) of those load balancers. However, load balancers deployed onto private or isolated networks need a floating IP address assigned if they must be accessible to external clients. To complete this step, you must have a router between the private and public networks and an available floating IP address. You can use the :command:`neutron lbaas-loadbalancer-show` command from the beginning of this section to locate the ``vip_port_id``. The ``vip_port_id`` is the ID of the network port that is assigned to the load balancer. You can associate a free floating IP address to the load balancer using :command:`neutron floatingip-associate`: .. code-block:: console $ neutron floatingip-associate FLOATINGIP_ID LOAD_BALANCER_PORT_ID Setting quotas for LBaaS v2 --------------------------- Quotas are available for limiting the number of load balancers and load balancer pools. By default, both quotas are set to 10. You can adjust quotas using the :command:`neutron quota-update` command: .. code-block:: console $ neutron quota-update --tenant-id TENANT_UUID --loadbalancer 25 $ neutron quota-update --tenant-id TENANT_UUID --pool 50 A setting of ``-1`` disables the quota for a tenant. Retrieving load balancer statistics ----------------------------------- The LBaaS v2 agent collects four types of statistics for each load balancer every six seconds. Users can query these statistics with the :command:`neutron lbaas-loadbalancer-stats` command: .. code-block:: console $ neutron lbaas-loadbalancer-stats test-lb +--------------------+----------+ | Field | Value | +--------------------+----------+ | active_connections | 0 | | bytes_in | 40264557 | | bytes_out | 71701666 | | total_connections | 384601 | +--------------------+----------+ The ``active_connections`` count is the total number of connections that were active at the time the agent polled the load balancer. The other three statistics are cumulative since the load balancer was last started. For example, if the load balancer restarts due to a system error or a configuration change, these statistics will be reset. neutron-12.1.1/doc/source/admin/config-ovsfwdriver.rst0000664000175000017500000000424413553660047023037 0ustar zuulzuul00000000000000.. _config-ovsfwdriver: =================================== Native Open vSwitch firewall driver =================================== .. note:: Experimental feature or incomplete documentation. Historically, Open vSwitch (OVS) could not interact directly with *iptables* to implement security groups. Thus, the OVS agent and Compute service use a Linux bridge between each instance (VM) and the OVS integration bridge ``br-int`` to implement security groups. The Linux bridge device contains the *iptables* rules pertaining to the instance. In general, additional components between instances and physical network infrastructure cause scalability and performance problems. To alleviate such problems, the OVS agent includes an optional firewall driver that natively implements security groups as flows in OVS rather than the Linux bridge device and *iptables*. This increases scalability and performance. Configuring heterogeneous firewall drivers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ L2 agents can be configured to use differing firewall drivers. There is no requirement that they all be the same. If an agent lacks a firewall driver configuration, it will default to what is configured on its server. This also means there is no requirement that the server has any firewall driver configured at all, as long as the agents are configured correctly. Prerequisites ~~~~~~~~~~~~~ The native OVS firewall implementation requires kernel and user space support for *conntrack*, thus requiring minimum versions of the Linux kernel and Open vSwitch. All cases require Open vSwitch version 2.5 or newer. * Kernel version 4.3 or newer includes *conntrack* support. * Kernel version 3.3, but less than 4.3, does not include *conntrack* support and requires building the OVS modules. Enable the native OVS firewall driver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * On nodes running the Open vSwitch agent, edit the ``openvswitch_agent.ini`` file and enable the firewall driver. .. code-block:: ini [securitygroup] firewall_driver = openvswitch For more information, see the :doc:`/contributor/internals/openvswitch_firewall` and the `video `_. neutron-12.1.1/doc/source/admin/shared/0000775000175000017500000000000013553660156017725 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/admin/shared/deploy-secgrouprules.txt0000664000175000017500000000276013553660046024665 0ustar zuulzuul00000000000000.. code-block:: console $ openstack security group rule create --proto icmp default +------------------+-----------+ | Field | Value | +------------------+-----------+ | direction | ingress | | ethertype | IPv4 | | protocol | icmp | | remote_ip_prefix | 0.0.0.0/0 | +------------------+-----------+ $ openstack security group rule create --ethertype IPv6 --proto ipv6-icmp default +-----------+-----------+ | Field | Value | +-----------+-----------+ | direction | ingress | | ethertype | IPv6 | | protocol | ipv6-icmp | +-----------+-----------+ $ openstack security group rule create --proto tcp --dst-port 22 default +------------------+-----------+ | Field | Value | +------------------+-----------+ | direction | ingress | | ethertype | IPv4 | | port_range_max | 22 | | port_range_min | 22 | | protocol | tcp | | remote_ip_prefix | 0.0.0.0/0 | +------------------+-----------+ $ openstack security group rule create --ethertype IPv6 --proto tcp --dst-port 22 default +------------------+-----------+ | Field | Value | +------------------+-----------+ | direction | ingress | | ethertype | IPv6 | | port_range_max | 22 | | port_range_min | 22 | | protocol | tcp | +------------------+-----------+ neutron-12.1.1/doc/source/admin/shared/keepalived-vrrp-healthcheck.txt0000664000175000017500000000136613553660046026033 0ustar zuulzuul00000000000000The health of your ``keepalived`` instances can be automatically monitored via a bash script that verifies connectivity to all available and configured gateway addresses. In the event that connectivity is lost, the master router is rescheduled to another node. If all routers lose connectivity simultaneously, the process of selecting a new master router will be repeated in a round-robin fashion until one or more routers have their connectivity restored. To enable this feature, edit the ``l3_agent.ini`` file: .. code-block:: ini ha_vrrp_health_check_interval = 30 Where ``ha_vrrp_health_check_interval`` indicates how often in seconds the health check should run. The default value is ``0``, which indicates that the check should not run at all. neutron-12.1.1/doc/source/admin/shared/deploy-ha-vrrp-initialnetworks.txt0000664000175000017500000001040513553660047026562 0ustar zuulzuul00000000000000Similar to the self-service deployment example, this configuration supports multiple VXLAN self-service networks. After enabling high-availability, all additional routers use VRRP. The following procedure creates an additional self-service network and router. The Networking service also supports adding high-availability to existing routers. However, the procedure requires administratively disabling and enabling each router which temporarily interrupts network connectivity for self-service networks with interfaces on that router. #. Source a regular (non-administrative) project credentials. #. Create a self-service network. .. code-block:: console $ openstack network create selfservice2 +-------------------------+--------------+ | Field | Value | +-------------------------+--------------+ | admin_state_up | UP | | mtu | 1450 | | name | selfservice2 | | port_security_enabled | True | | router:external | Internal | | shared | False | | status | ACTIVE | +-------------------------+--------------+ #. Create a IPv4 subnet on the self-service network. .. code-block:: console $ openstack subnet create --subnet-range 198.51.100.0/24 \ --network selfservice2 --dns-nameserver 8.8.4.4 selfservice2-v4 +-------------------+------------------------------+ | Field | Value | +-------------------+------------------------------+ | allocation_pools | 198.51.100.2-198.51.100.254 | | cidr | 198.51.100.0/24 | | dns_nameservers | 8.8.4.4 | | enable_dhcp | True | | gateway_ip | 198.51.100.1 | | ip_version | 4 | | name | selfservice2-v4 | +-------------------+------------------------------+ #. Create a IPv6 subnet on the self-service network. .. code-block:: console $ openstack subnet create --subnet-range fd00:198:51:100::/64 --ip-version 6 \ --ipv6-ra-mode slaac --ipv6-address-mode slaac --network selfservice2 \ --dns-nameserver 2001:4860:4860::8844 selfservice2-v6 +-------------------+--------------------------------------------------------+ | Field | Value | +-------------------+--------------------------------------------------------+ | allocation_pools | fd00:198:51:100::2-fd00:198:51:100:ffff:ffff:ffff:ffff | | cidr | fd00:198:51:100::/64 | | dns_nameservers | 2001:4860:4860::8844 | | enable_dhcp | True | | gateway_ip | fd00:198:51:100::1 | | ip_version | 6 | | ipv6_address_mode | slaac | | ipv6_ra_mode | slaac | | name | selfservice2-v6 | +-------------------+--------------------------------------------------------+ #. Create a router. .. code-block:: console $ openstack router create router2 +-----------------------+---------+ | Field | Value | +-----------------------+---------+ | admin_state_up | UP | | name | router2 | | status | ACTIVE | +-----------------------+---------+ #. Add the IPv4 and IPv6 subnets as interfaces on the router. .. code-block:: console $ openstack router add subnet router2 selfservice2-v4 $ openstack router add subnet router2 selfservice2-v6 .. note:: These commands provide no output. #. Add the provider network as a gateway on the router. .. code-block:: console $ neutron router-gateway-set router2 provider1 Set gateway for router router2 neutron-12.1.1/doc/source/admin/shared/deploy-provider-networktrafficflow.txt0000664000175000017500000000200413553660046027522 0ustar zuulzuul00000000000000The following sections describe the flow of network traffic in several common scenarios. *North-south* network traffic travels between an instance and external network such as the Internet. *East-west* network traffic travels between instances on the same or different networks. In all scenarios, the physical network infrastructure handles switching and routing among provider networks and external networks such as the Internet. Each case references one or more of the following components: * Provider network 1 (VLAN) * VLAN ID 101 (tagged) * IP address ranges 203.0.113.0/24 and fd00:203:0:113::/64 * Gateway (via physical network infrastructure) * IP addresses 203.0.113.1 and fd00:203:0:113:0::1 * Provider network 2 (VLAN) * VLAN ID 102 (tagged) * IP address range 192.0.2.0/24 and fd00:192:0:2::/64 * Gateway * IP addresses 192.0.2.1 and fd00:192:0:2::1 * Instance 1 * IP addresses 203.0.113.101 and fd00:203:0:113:0::101 * Instance 2 * IP addresses 192.0.2.101 and fd00:192:0:2:0::101 neutron-12.1.1/doc/source/admin/shared/deploy-ha-vrrp-verifynetworkoperation.txt0000664000175000017500000001675613553660047030212 0ustar zuulzuul00000000000000#. Source the administrative project credentials. #. Verify creation of the internal high-availability network that handles VRRP *heartbeat* traffic. .. code-block:: console $ openstack network list +--------------------------------------+----------------------------------------------------+--------------------------------------+ | ID | Name | Subnets | +--------------------------------------+----------------------------------------------------+--------------------------------------+ | 1b8519c1-59c4-415c-9da2-a67d53c68455 | HA network tenant f986edf55ae945e2bef3cb4bfd589928 | 6843314a-1e76-4cc9-94f5-c64b7a39364a | +--------------------------------------+----------------------------------------------------+--------------------------------------+ #. On each network node, verify creation of a ``qrouter`` namespace with the same ID. Network node 1: .. code-block:: console # ip netns qrouter-b6206312-878e-497c-8ef7-eb384f8add96 Network node 2: .. code-block:: console # ip netns qrouter-b6206312-878e-497c-8ef7-eb384f8add96 .. note:: The namespace for router 1 from :ref:`deploy-lb-selfservice` should only appear on network node 1 because of creation prior to enabling VRRP. #. On each network node, show the IP address of interfaces in the ``qrouter`` namespace. With the exception of the VRRP interface, only one namespace belonging to the master router instance contains IP addresses on the interfaces. Network node 1: .. code-block:: console # ip netns exec qrouter-b6206312-878e-497c-8ef7-eb384f8add96 ip addr show 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ha-eb820380-40@if21: mtu 1450 qdisc noqueue state UP group default qlen 1000 link/ether fa:16:3e:78:ba:99 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 169.254.192.1/18 brd 169.254.255.255 scope global ha-eb820380-40 valid_lft forever preferred_lft forever inet 169.254.0.1/24 scope global ha-eb820380-40 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe78:ba99/64 scope link valid_lft forever preferred_lft forever 3: qr-da3504ad-ba@if24: mtu 1450 qdisc noqueue state UP group default qlen 1000 link/ether fa:16:3e:dc:8e:a8 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 198.51.100.1/24 scope global qr-da3504ad-ba valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fedc:8ea8/64 scope link valid_lft forever preferred_lft forever 4: qr-442e36eb-fc@if27: mtu 1450 qdisc noqueue state UP group default qlen 1000 link/ether fa:16:3e:ee:c8:41 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fd00:198:51:100::1/64 scope global nodad valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:feee:c841/64 scope link valid_lft forever preferred_lft forever 5: qg-33fedbc5-43@if28: mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether fa:16:3e:03:1a:f6 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 203.0.113.21/24 scope global qg-33fedbc5-43 valid_lft forever preferred_lft forever inet6 fd00:203:0:113::21/64 scope global nodad valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe03:1af6/64 scope link valid_lft forever preferred_lft forever Network node 2: .. code-block:: console # ip netns exec qrouter-b6206312-878e-497c-8ef7-eb384f8add96 ip addr show 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ha-7a7ce184-36@if8: mtu 1450 qdisc noqueue state UP group default qlen 1000 link/ether fa:16:3e:16:59:84 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 169.254.192.2/18 brd 169.254.255.255 scope global ha-7a7ce184-36 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe16:5984/64 scope link valid_lft forever preferred_lft forever 3: qr-da3504ad-ba@if11: mtu 1450 qdisc noqueue state UP group default qlen 1000 link/ether fa:16:3e:dc:8e:a8 brd ff:ff:ff:ff:ff:ff link-netnsid 0 4: qr-442e36eb-fc@if14: mtu 1450 qdisc noqueue state UP group default qlen 1000 5: qg-33fedbc5-43@if15: mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether fa:16:3e:03:1a:f6 brd ff:ff:ff:ff:ff:ff link-netnsid 0 .. note:: The master router may reside on network node 2. #. Launch an instance with an interface on the additional self-service network. For example, a CirrOS image using flavor ID 1. .. code-block:: console $ openstack server create --flavor 1 --image cirros --nic net-id=NETWORK_ID selfservice-instance2 Replace ``NETWORK_ID`` with the ID of the additional self-service network. #. Determine the IPv4 and IPv6 addresses of the instance. .. code-block:: console $ openstack server list +--------------------------------------+-----------------------+--------+---------------------------------------------------------------------------+ | ID | Name | Status | Networks | +--------------------------------------+-----------------------+--------+---------------------------------------------------------------------------+ | bde64b00-77ae-41b9-b19a-cd8e378d9f8b | selfservice-instance2 | ACTIVE | selfservice2=fd00:198:51:100:f816:3eff:fe71:e93e, 198.51.100.4 | +--------------------------------------+-----------------------+--------+---------------------------------------------------------------------------+ #. Create a floating IPv4 address on the provider network. .. code-block:: console $ openstack floating ip create provider1 +-------------+--------------------------------------+ | Field | Value | +-------------+--------------------------------------+ | fixed_ip | None | | id | 0174056a-fa56-4403-b1ea-b5151a31191f | | instance_id | None | | ip | 203.0.113.17 | | pool | provider1 | +-------------+--------------------------------------+ #. Associate the floating IPv4 address with the instance. .. code-block:: console $ openstack server add floating ip selfservice-instance2 203.0.113.17 .. note:: This command provides no output. neutron-12.1.1/doc/source/admin/shared/deploy-selfservice-verifynetworkoperation.txt0000664000175000017500000001262713553660047031136 0ustar zuulzuul00000000000000#. On each compute node, verify creation of a second ``qdhcp`` namespace. .. code-block:: console # ip netns qdhcp-8b868082-e312-4110-8627-298109d4401c qdhcp-8fbc13ca-cfe0-4b8a-993b-e33f37ba66d1 #. On the network node, verify creation of the ``qrouter`` namespace. .. code-block:: console # ip netns qrouter-17db2a15-e024-46d0-9250-4cd4d336a2cc #. Source a regular (non-administrative) project credentials. #. Create the appropriate security group rules to allow ``ping`` and SSH access instances using the network. .. include:: shared/deploy-secgrouprules.txt #. Launch an instance with an interface on the self-service network. For example, a CirrOS image using flavor ID 1. .. code-block:: console $ openstack server create --flavor 1 --image cirros --nic net-id=NETWORK_ID selfservice-instance1 Replace ``NETWORK_ID`` with the ID of the self-service network. #. Determine the IPv4 and IPv6 addresses of the instance. .. code-block:: console $ openstack server list +--------------------------------------+-----------------------+--------+--------------------------------------------------------------+ | ID | Name | Status | Networks | +--------------------------------------+-----------------------+--------+--------------------------------------------------------------+ | c055cdb0-ebb4-4d65-957c-35cbdbd59306 | selfservice-instance1 | ACTIVE | selfservice1=192.0.2.4, fd00:192:0:2:f816:3eff:fe30:9cb0 | +--------------------------------------+-----------------------+--------+--------------------------------------------------------------+ .. warning:: The IPv4 address resides in a private IP address range (RFC1918). Thus, the Networking service performs source network address translation (SNAT) for the instance to access external networks such as the Internet. Access from external networks such as the Internet to the instance requires a floating IPv4 address. The Networking service performs destination network address translation (DNAT) from the floating IPv4 address to the instance IPv4 address on the self-service network. On the other hand, the Networking service architecture for IPv6 lacks support for NAT due to the significantly larger address space and complexity of NAT. Thus, floating IP addresses do not exist for IPv6 and the Networking service only performs routing for IPv6 subnets on self-service networks. In other words, you cannot rely on NAT to "hide" instances with IPv4 and IPv6 addresses or only IPv6 addresses and must properly implement security groups to restrict access. #. On the controller node or any host with access to the provider network, ``ping`` the IPv6 address of the instance. .. code-block:: console $ ping6 -c 4 fd00:192:0:2:f816:3eff:fe30:9cb0 PING fd00:192:0:2:f816:3eff:fe30:9cb0(fd00:192:0:2:f816:3eff:fe30:9cb0) 56 data bytes 64 bytes from fd00:192:0:2:f816:3eff:fe30:9cb0: icmp_seq=1 ttl=63 time=2.08 ms 64 bytes from fd00:192:0:2:f816:3eff:fe30:9cb0: icmp_seq=2 ttl=63 time=1.88 ms 64 bytes from fd00:192:0:2:f816:3eff:fe30:9cb0: icmp_seq=3 ttl=63 time=1.55 ms 64 bytes from fd00:192:0:2:f816:3eff:fe30:9cb0: icmp_seq=4 ttl=63 time=1.62 ms --- fd00:192:0:2:f816:3eff:fe30:9cb0 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3004ms rtt min/avg/max/mdev = 1.557/1.788/2.085/0.217 ms #. Optionally, enable IPv4 access from external networks such as the Internet to the instance. #. Create a floating IPv4 address on the provider network. .. code-block:: console $ openstack floating ip create provider1 +-------------+--------------------------------------+ | Field | Value | +-------------+--------------------------------------+ | fixed_ip | None | | id | 22a1b088-5c9b-43b4-97f3-970ce5df77f2 | | instance_id | None | | ip | 203.0.113.16 | | pool | provider1 | +-------------+--------------------------------------+ #. Associate the floating IPv4 address with the instance. .. code-block:: console $ openstack server add floating ip selfservice-instance1 203.0.113.16 .. note:: This command provides no output. #. On the controller node or any host with access to the provider network, ``ping`` the floating IPv4 address of the instance. .. code-block:: console $ ping -c 4 203.0.113.16 PING 203.0.113.16 (203.0.113.16) 56(84) bytes of data. 64 bytes from 203.0.113.16: icmp_seq=1 ttl=63 time=3.41 ms 64 bytes from 203.0.113.16: icmp_seq=2 ttl=63 time=1.67 ms 64 bytes from 203.0.113.16: icmp_seq=3 ttl=63 time=1.47 ms 64 bytes from 203.0.113.16: icmp_seq=4 ttl=63 time=1.59 ms --- 203.0.113.16 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3005ms rtt min/avg/max/mdev = 1.473/2.040/3.414/0.798 ms #. Obtain access to the instance. #. Test IPv4 and IPv6 connectivity to the Internet or other external network. neutron-12.1.1/doc/source/admin/shared/deploy-config-neutron-common.txt0000664000175000017500000000076213553660046026206 0ustar zuulzuul00000000000000.. code-block:: ini [DEFAULT] core_plugin = ml2 auth_strategy = keystone [database] # ... [keystone_authtoken] # ... [nova] # ... [agent] # ... See the `Installation Tutorials and Guides `_ and `Configuration Reference `_ for your OpenStack release to obtain the appropriate additional configuration for the ``[DEFAULT]``, ``[database]``, ``[keystone_authtoken]``, ``[nova]``, and ``[agent]`` sections. neutron-12.1.1/doc/source/admin/shared/deploy-provider-initialnetworks.txt0000664000175000017500000001044013553660047027034 0ustar zuulzuul00000000000000The configuration supports one flat or multiple VLAN provider networks. For simplicity, the following procedure creates one flat provider network. #. Source the administrative project credentials. #. Create a flat network. .. code-block:: console $ openstack network create --share --provider-physical-network provider \ --provider-network-type flat provider1 +---------------------------+-----------+- | Field | Value | +---------------------------+-----------+ | admin_state_up | UP | | mtu | 1500 | | name | provider1 | | port_security_enabled | True | | provider:network_type | flat | | provider:physical_network | provider | | provider:segmentation_id | None | | router:external | Internal | | shared | True | | status | ACTIVE | +---------------------------+-----------+ .. note:: The ``share`` option allows any project to use this network. To limit access to provider networks, see :ref:`config-rbac`. .. note:: To create a VLAN network instead of a flat network, change ``--provider:network_type flat`` to ``--provider-network-type vlan`` and add ``--provider-segment`` with a value referencing the VLAN ID. #. Create a IPv4 subnet on the provider network. .. code-block:: console $ openstack subnet create --subnet-range 203.0.113.0/24 --gateway 203.0.113.1 \ --network provider1 --allocation-pool start=203.0.113.11,end=203.0.113.250 \ --dns-nameserver 8.8.4.4 provider1-v4 +-------------------+----------------------------+ | Field | Value | +-------------------+----------------------------+ | allocation_pools | 203.0.113.11-203.0.113.250 | | cidr | 203.0.113.0/24 | | dns_nameservers | 8.8.4.4 | | enable_dhcp | True | | gateway_ip | 203.0.113.1 | | ip_version | 4 | | name | provider1-v4 | +-------------------+----------------------------+ .. important:: Enabling DHCP causes the Networking service to provide DHCP which can interfere with existing DHCP services on the physical network infrastructure. Use the ``--no-dhcp`` option to have the subnet managed by existing DHCP services. #. Create a IPv6 subnet on the provider network. .. code-block:: console $ openstack subnet create --subnet-range fd00:203:0:113::/64 --gateway fd00:203:0:113::1 \ --ip-version 6 --ipv6-address-mode slaac --network provider1 \ --dns-nameserver 2001:4860:4860::8844 provider1-v6 +-------------------+------------------------------------------------------+ | Field | Value | +-------------------+------------------------------------------------------+ | allocation_pools | fd00:203:0:113::2-fd00:203:0:113:ffff:ffff:ffff:ffff | | cidr | fd00:203:0:113::/64 | | dns_nameservers | 2001:4860:4860::8844 | | enable_dhcp | True | | gateway_ip | fd00:203:0:113::1 | | ip_version | 6 | | ipv6_address_mode | slaac | | ipv6_ra_mode | None | | name | provider1-v6 | +-------------------+------------------------------------------------------+ .. note:: The Networking service uses the layer-3 agent to provide router advertisement. Provider networks rely on physical network infrastructure for layer-3 services rather than the layer-3 agent. Thus, the physical network infrastructure must provide router advertisement on provider networks for proper operation of IPv6. neutron-12.1.1/doc/source/admin/shared/deploy-selfservice-networktrafficflow.txt0000664000175000017500000000147613553660046030216 0ustar zuulzuul00000000000000The following sections describe the flow of network traffic in several common scenarios. *North-south* network traffic travels between an instance and external network such as the Internet. *East-west* network traffic travels between instances on the same or different networks. In all scenarios, the physical network infrastructure handles switching and routing among provider networks and external networks such as the Internet. Each case references one or more of the following components: * Provider network (VLAN) * VLAN ID 101 (tagged) * Self-service network 1 (VXLAN) * VXLAN ID (VNI) 101 * Self-service network 2 (VXLAN) * VXLAN ID (VNI) 102 * Self-service router * Gateway on the provider network * Interface on self-service network 1 * Interface on self-service network 2 * Instance 1 * Instance 2 neutron-12.1.1/doc/source/admin/shared/deploy-provider-verifynetworkoperation.txt0000664000175000017500000000577413553660047030463 0ustar zuulzuul00000000000000#. On each compute node, verify creation of the ``qdhcp`` namespace. .. code-block:: console # ip netns qdhcp-8b868082-e312-4110-8627-298109d4401c #. Source a regular (non-administrative) project credentials. #. Create the appropriate security group rules to allow ``ping`` and SSH access instances using the network. .. include:: shared/deploy-secgrouprules.txt #. Launch an instance with an interface on the provider network. For example, a CirrOS image using flavor ID 1. .. code-block:: console $ openstack server create --flavor 1 --image cirros \ --nic net-id=NETWORK_ID provider-instance1 Replace ``NETWORK_ID`` with the ID of the provider network. #. Determine the IPv4 and IPv6 addresses of the instance. .. code-block:: console $ openstack server list +--------------------------------------+--------------------+--------+------------------------------------------------------------+------------+ | ID | Name | Status | Networks | Image Name | +--------------------------------------+--------------------+--------+------------------------------------------------------------+------------+ | 018e0ae2-b43c-4271-a78d-62653dd03285 | provider-instance1 | ACTIVE | provider1=203.0.113.13, fd00:203:0:113:f816:3eff:fe58:be4e | cirros | +--------------------------------------+--------------------+--------+------------------------------------------------------------+------------+ #. On the controller node or any host with access to the provider network, ``ping`` the IPv4 and IPv6 addresses of the instance. .. code-block:: console $ ping -c 4 203.0.113.13 PING 203.0.113.13 (203.0.113.13) 56(84) bytes of data. 64 bytes from 203.0.113.13: icmp_req=1 ttl=63 time=3.18 ms 64 bytes from 203.0.113.13: icmp_req=2 ttl=63 time=0.981 ms 64 bytes from 203.0.113.13: icmp_req=3 ttl=63 time=1.06 ms 64 bytes from 203.0.113.13: icmp_req=4 ttl=63 time=0.929 ms --- 203.0.113.13 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3002ms rtt min/avg/max/mdev = 0.929/1.539/3.183/0.951 ms $ ping6 -c 4 fd00:203:0:113:f816:3eff:fe58:be4e PING fd00:203:0:113:f816:3eff:fe58:be4e(fd00:203:0:113:f816:3eff:fe58:be4e) 56 data bytes 64 bytes from fd00:203:0:113:f816:3eff:fe58:be4e icmp_seq=1 ttl=64 time=1.25 ms 64 bytes from fd00:203:0:113:f816:3eff:fe58:be4e icmp_seq=2 ttl=64 time=0.683 ms 64 bytes from fd00:203:0:113:f816:3eff:fe58:be4e icmp_seq=3 ttl=64 time=0.762 ms 64 bytes from fd00:203:0:113:f816:3eff:fe58:be4e icmp_seq=4 ttl=64 time=0.486 ms --- fd00:203:0:113:f816:3eff:fe58:be4e ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 2999ms rtt min/avg/max/mdev = 0.486/0.796/1.253/0.282 ms #. Obtain access to the instance. #. Test IPv4 and IPv6 connectivity to the Internet or other external network. neutron-12.1.1/doc/source/admin/shared/deploy-ha-vrrp-verifyfailoveroperation.txt0000664000175000017500000000125113553660046030307 0ustar zuulzuul00000000000000#. Begin a continuous ``ping`` of both the floating IPv4 address and IPv6 address of the instance. While performing the next three steps, you should see a minimal, if any, interruption of connectivity to the instance. #. On the network node with the master router, administratively disable the overlay network interface. #. On the other network node, verify promotion of the backup router to master router by noting addition of IP addresses to the interfaces in the ``qrouter`` namespace. #. On the original network node in step 2, administratively enable the overlay network interface. Note that the master router remains on the network node in step 3. neutron-12.1.1/doc/source/admin/shared/deploy-selfservice-initialnetworks.txt0000664000175000017500000001056713553660047027526 0ustar zuulzuul00000000000000The configuration supports multiple VXLAN self-service networks. For simplicity, the following procedure creates one self-service network and a router with a gateway on the flat provider network. The router uses NAT for IPv4 network traffic and directly routes IPv6 network traffic. .. note:: IPv6 connectivity with self-service networks often requires addition of static routes to nodes and physical network infrastructure. #. Source the administrative project credentials. #. Update the provider network to support external connectivity for self-service networks. .. code-block:: console $ openstack network set --external provider1 .. note:: This command provides no output. #. Source a regular (non-administrative) project credentials. #. Create a self-service network. .. code-block:: console $ openstack network create selfservice1 +-------------------------+--------------+ | Field | Value | +-------------------------+--------------+ | admin_state_up | UP | | mtu | 1450 | | name | selfservice1 | | port_security_enabled | True | | router:external | Internal | | shared | False | | status | ACTIVE | +-------------------------+--------------+ #. Create a IPv4 subnet on the self-service network. .. code-block:: console $ openstack subnet create --subnet-range 192.0.2.0/24 \ --network selfservice1 --dns-nameserver 8.8.4.4 selfservice1-v4 +-------------------+---------------------------+ | Field | Value | +-------------------+---------------------------+ | allocation_pools | 192.0.2.2-192.0.2.254 | | cidr | 192.0.2.0/24 | | dns_nameservers | 8.8.4.4 | | enable_dhcp | True | | gateway_ip | 192.0.2.1 | | ip_version | 4 | | name | selfservice1-v4 | +-------------------+---------------------------+ #. Create a IPv6 subnet on the self-service network. .. code-block:: console $ openstack subnet create --subnet-range fd00:192:0:2::/64 --ip-version 6 \ --ipv6-ra-mode slaac --ipv6-address-mode slaac --network selfservice1 \ --dns-nameserver 2001:4860:4860::8844 selfservice1-v6 +-------------------+------------------------------------------------------+ | Field | Value | +-------------------+------------------------------------------------------+ | allocation_pools | fd00:192:0:2::2-fd00:192:0:2:ffff:ffff:ffff:ffff | | cidr | fd00:192:0:2::/64 | | dns_nameservers | 2001:4860:4860::8844 | | enable_dhcp | True | | gateway_ip | fd00:192:0:2::1 | | ip_version | 6 | | ipv6_address_mode | slaac | | ipv6_ra_mode | slaac | | name | selfservice1-v6 | +-------------------+------------------------------------------------------+ #. Create a router. .. code-block:: console $ openstack router create router1 +-----------------------+---------+ | Field | Value | +-----------------------+---------+ | admin_state_up | UP | | name | router1 | | status | ACTIVE | +-----------------------+---------+ #. Add the IPv4 and IPv6 subnets as interfaces on the router. .. code-block:: console $ openstack router add subnet router1 selfservice1-v4 $ openstack router add subnet router1 selfservice1-v6 .. note:: These commands provide no output. #. Add the provider network as the gateway on the router. .. code-block:: console $ neutron router-gateway-set router1 provider1 Set gateway for router router1 neutron-12.1.1/doc/source/admin/shared/deploy-ha-vrrp.txt0000664000175000017500000000645713553660046023351 0ustar zuulzuul00000000000000This architecture example augments the self-service deployment example with a high-availability mechanism using the Virtual Router Redundancy Protocol (VRRP) via ``keepalived`` and provides failover of routing for self-service networks. It requires a minimum of two network nodes because VRRP creates one master (active) instance and at least one backup instance of each router. During normal operation, ``keepalived`` on the master router periodically transmits *heartbeat* packets over a hidden network that connects all VRRP routers for a particular project. Each project with VRRP routers uses a separate hidden network. By default this network uses the first value in the ``tenant_network_types`` option in the ``ml2_conf.ini`` file. For additional control, you can specify the self-service network type and physical network name for the hidden network using the ``l3_ha_network_type`` and ``l3_ha_network_name`` options in the ``neutron.conf`` file. If ``keepalived`` on the backup router stops receiving *heartbeat* packets, it assumes failure of the master router and promotes the backup router to master router by configuring IP addresses on the interfaces in the ``qrouter`` namespace. In environments with more than one backup router, ``keepalived`` on the backup router with the next highest priority promotes that backup router to master router. .. note:: This high-availability mechanism configures VRRP using the same priority for all routers. Therefore, VRRP promotes the backup router with the highest IP address to the master router. .. warning:: There is a known bug with ``keepalived`` v1.2.15 and earlier which can cause packet loss when ``max_l3_agents_per_router`` is set to 3 or more. Therefore, we recommend that you upgrade to ``keepalived`` v1.2.16 or greater when using this feature. Interruption of VRRP *heartbeat* traffic between network nodes, typically due to a network interface or physical network infrastructure failure, triggers a failover. Restarting the layer-3 agent, or failure of it, does not trigger a failover providing ``keepalived`` continues to operate. Consider the following attributes of this high-availability mechanism to determine practicality in your environment: * Instance network traffic on self-service networks using a particular router only traverses the master instance of that router. Thus, resource limitations of a particular network node can impact all master instances of routers on that network node without triggering failover to another network node. However, you can configure the scheduler to distribute the master instance of each router uniformly across a pool of network nodes to reduce the chance of resource contention on any particular network node. * Only supports self-service networks using a router. Provider networks operate at layer-2 and rely on physical network infrastructure for redundancy. * For instances with a floating IPv4 address, maintains state of network connections during failover as a side effect of 1:1 static NAT. The mechanism does not actually implement connection tracking. For production deployments, we recommend at least three network nodes with sufficient resources to handle network traffic for the entire environment if one network node fails. Also, the remaining two nodes can continue to provide redundancy. neutron-12.1.1/doc/source/admin/config-trunking.rst0000664000175000017500000004064013553660047022320 0ustar zuulzuul00000000000000.. _config-trunking: ======== Trunking ======== The network trunk service allows multiple networks to be connected to an instance using a single virtual NIC (vNIC). Multiple networks can be presented to an instance by connecting it to a single port. Operation ~~~~~~~~~ Network trunking consists of a service plug-in and a set of drivers that manage trunks on different layer-2 mechanism drivers. Users can create a port, associate it with a trunk, and launch an instance on that port. Users can dynamically attach and detach additional networks without disrupting operation of the instance. Every trunk has a parent port and can have any number of subports. The parent port is the port that the trunk is associated with. Users create instances and specify the parent port of the trunk when launching instances attached to a trunk. The network presented by the subport is the network of the associated port. When creating a subport, a ``segmentation-id`` may be required by the driver. ``segmentation-id`` defines the segmentation ID on which the subport network is presented to the instance. ``segmentation-type`` may be required by certain drivers like OVS. At this time the following ``segmentation-type`` values are supported: * ``vlan`` uses VLAN for segmentation. * ``inherit`` uses the ``segmentation-type`` from the network the subport is connected to if no ``segmentation-type`` is specified for the subport. Note that using the ``inherit`` type requires the ``provider`` extension to be enabled and only works when the connected network's ``segmentation-type`` is ``vlan``. .. note:: The ``segmentation-type`` and ``segmentation-id`` parameters are optional in the Networking API. However, all drivers as of the Newton release require both to be provided when adding a subport to a trunk. Future drivers may be implemented without this requirement. The ``segmentation-type`` and ``segmentation-id`` specified by the user on the subports is intentionally decoupled from the ``segmentation-type`` and ID of the networks. For example, it is possible to configure the Networking service with ``tenant_network_types = vxlan`` and still create subports with ``segmentation_type = vlan``. The Networking service performs remapping as necessary. Example configuration ~~~~~~~~~~~~~~~~~~~~~ The ML2 plug-in supports trunking with the following mechanism drivers: * Open vSwitch (OVS) * Linux bridge * Open Virtual Network (OVN) When using a ``segmentation-type`` of ``vlan``, the OVS and Linux bridge drivers present the network of the parent port as the untagged VLAN and all subports as tagged VLANs. Controller node --------------- * In the ``neutron.conf`` file, enable the trunk service plug-in: .. code-block:: ini [DEFAULT] service_plugins = trunk Verify service operation ------------------------ #. Source the administrative project credentials and list the enabled extensions. #. Use the command :command:`openstack extension list --network` to verify that the ``Trunk Extension`` and ``Trunk port details`` extensions are enabled. Workflow -------- At a high level, the basic steps to launching an instance on a trunk are the following: #. Create networks and subnets for the trunk and subports #. Create the trunk #. Add subports to the trunk #. Launch an instance on the trunk Create networks and subnets for the trunk and subports ------------------------------------------------------ Create the appropriate networks for the trunk and subports that will be added to the trunk. Create subnets on these networks to ensure the desired layer-3 connectivity over the trunk. Create the trunk ---------------- * Create a parent port for the trunk. .. code-block:: console $ openstack port create --network project-net-A trunk-parent +-------------------+-------------------------------------------------------------------------+ | Field | Value | +-------------------+-------------------------------------------------------------------------+ | admin_state_up | UP | | binding_vif_type | unbound | | binding_vnic_type | normal | | fixed_ips | ip_address='192.0.2.7',subnet_id='8b957198-d3cf-4953-8449-ad4e4dd712cc' | | id | 73fb9d54-43a7-4bb1-a8dc-569e0e0a0a38 | | mac_address | fa:16:3e:dd:c4:d1 | | name | trunk-parent | | network_id | 1b47d3e7-cda5-48e4-b0c8-d20bd7e35f55 | | revision_number | 1 | | tags | [] | +-------------------+-------------------------------------------------------------------------+ * Create the trunk using ``--parent-port`` to reference the port from the previous step: .. code-block:: console $ openstack network trunk create --parent-port trunk-parent trunk1 +-----------------+--------------------------------------+ | Field | Value | +-----------------+--------------------------------------+ | admin_state_up | UP | | id | fdf02fcb-1844-45f1-9d9b-e4c2f522c164 | | name | trunk1 | | port_id | 73fb9d54-43a7-4bb1-a8dc-569e0e0a0a38 | | revision_number | 1 | | sub_ports | | +-----------------+--------------------------------------+ Add subports to the trunk ------------------------- Subports can be added to a trunk in two ways: creating the trunk with subports or adding subports to an existing trunk. * Create trunk with subports: This method entails creating the trunk with subports specified at trunk creation. .. code-block:: console $ openstack port create --network project-net-A trunk-parent +-------------------+-------------------------------------------------------------------------+ | Field | Value | +-------------------+-------------------------------------------------------------------------+ | admin_state_up | UP | | binding_vif_type | unbound | | binding_vnic_type | normal | | fixed_ips | ip_address='192.0.2.7',subnet_id='8b957198-d3cf-4953-8449-ad4e4dd712cc' | | id | 73fb9d54-43a7-4bb1-a8dc-569e0e0a0a38 | | mac_address | fa:16:3e:dd:c4:d1 | | name | trunk-parent | | network_id | 1b47d3e7-cda5-48e4-b0c8-d20bd7e35f55 | | revision_number | 1 | | tags | [] | +-------------------+-------------------------------------------------------------------------+ $ openstack port create --network trunked-net subport1 +-------------------+----------------------------------------------------------------------------+ | Field | Value | +-------------------+----------------------------------------------------------------------------+ | admin_state_up | UP | | binding_vif_type | unbound | | binding_vnic_type | normal | | fixed_ips | ip_address='198.51.100.8',subnet_id='2a860e2c-922b-437b-a149-b269a8c9b120' | | id | 91f9dde8-80a4-4506-b5da-c287feb8f5d8 | | mac_address | fa:16:3e:ba:f0:4d | | name | subport1 | | network_id | aef78ec5-16e3-4445-b82d-b2b98c6a86d9 | | revision_number | 1 | | tags | [] | +-------------------+----------------------------------------------------------------------------+ $ openstack network trunk create \ --parent-port trunk-parent \ --subport port=subport1,segmentation-type=vlan,segmentation-id=100 \ trunk1 +----------------+-------------------------------------------------------------------------------------------------+ | Field | Value | +----------------+-------------------------------------------------------------------------------------------------+ | admin_state_up | UP | | id | 61d8e620-fe3a-4d8f-b9e6-e1b0dea6d9e3 | | name | trunk1 | | port_id | 73fb9d54-43a7-4bb1-a8dc-569e0e0a0a38 | | revision_number| 1 | | sub_ports | port_id='73fb9d54-43a7-4bb1-a8dc-569e0e0a0a38', segmentation_id='100', segmentation_type='vlan' | | tags | [] | +----------------+-------------------------------------------------------------------------------------------------+ * Add subports to an existing trunk: This method entails creating a trunk, then adding subports to the trunk after it has already been created. .. code-block:: console $ openstack network trunk set --subport \ port=subport1,segmentation-type=vlan,segmentation-id=100 \ trunk1 .. note:: The command provides no output. .. code-block:: console $ openstack network trunk show trunk1 +----------------+-------------------------------------------------------------------------------------------------+ | Field | Value | +----------------+-------------------------------------------------------------------------------------------------+ | admin_state_up | UP | | id | 61d8e620-fe3a-4d8f-b9e6-e1b0dea6d9e3 | | name | trunk1 | | port_id | 73fb9d54-43a7-4bb1-a8dc-569e0e0a0a38 | | revision_number| 1 | | sub_ports | port_id='73fb9d54-43a7-4bb1-a8dc-569e0e0a0a38', segmentation_id='100', segmentation_type='vlan' | | tags | [] | +----------------+-------------------------------------------------------------------------------------------------+ Launch an instance on the trunk ------------------------------- * Show trunk details to get the ``port_id`` of the trunk. .. code-block:: console $ openstack network trunk show trunk1 +----------------+--------------------------------------+ | Field | Value | +----------------+--------------------------------------+ | admin_state_up | UP | | id | 61d8e620-fe3a-4d8f-b9e6-e1b0dea6d9e3 | | name | trunk | | port_id | 73fb9d54-43a7-4bb1-a8dc-569e0e0a0a38 | | revision_number| 1 | | sub_ports | | | tags | [] | +----------------+--------------------------------------+ * Launch the instance by specifying ``port-id`` using the value of ``port_id`` from the trunk details. Launching an instance on a subport is not supported. Using trunks and subports inside an instance ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When configuring instances to use a subport, ensure that the interface on the instance is set to use the MAC address assigned to the port by the Networking service. Instances are not made aware of changes made to the trunk after they are active. For example, when a subport with a ``segmentation-type`` of ``vlan`` is added to a trunk, any operations specific to the instance operating system that allow the instance to send and receive traffic on the new VLAN must be handled outside of the Networking service. When creating subports, the MAC address of the trunk parent port can be set on the subport. This will allow VLAN subinterfaces inside an instance launched on a trunk to be configured without explicitly setting a MAC address. Although unique MAC addresses can be used for subports, this can present issues with ARP spoof protections and the native OVS firewall driver. If the native OVS firewall driver is to be used, we recommend that the MAC address of the parent port be re-used on all subports. Trunk states ~~~~~~~~~~~~ * ``ACTIVE`` The trunk is ``ACTIVE`` when both the logical and physical resources have been created. This means that all operations within the Networking and Compute services have completed and the trunk is ready for use. * ``DOWN`` A trunk is ``DOWN`` when it is first created without an instance launched on it, or when the instance associated with the trunk has been deleted. * ``DEGRADED`` A trunk can be in a ``DEGRADED`` state when a temporary failure during the provisioning process is encountered. This includes situations where a subport add or remove operation fails. When in a degraded state, the trunk is still usable and some subports may be usable as well. Operations that cause the trunk to go into a ``DEGRADED`` state can be retried to fix temporary failures and move the trunk into an ``ACTIVE`` state. * ``ERROR`` A trunk is in ``ERROR`` state if the request leads to a conflict or an error that cannot be fixed by retrying the request. The ``ERROR`` status can be encountered if the network is not compatible with the trunk configuration or the binding process leads to a persistent failure. When a trunk is in ``ERROR`` state, it must be brought to a sane state (``ACTIVE``), or else requests to add subports will be rejected. * ``BUILD`` A trunk is in ``BUILD`` state while the resources associated with the trunk are in the process of being provisioned. Once the trunk and all of the subports have been provisioned successfully, the trunk transitions to ``ACTIVE``. If there was a partial failure, the trunk transitions to ``DEGRADED``. When ``admin_state`` is set to ``DOWN``, the user is blocked from performing operations on the trunk. ``admin_state`` is set by the user and should not be used to monitor the health of the trunk. Limitations and issues ~~~~~~~~~~~~~~~~~~~~~~ * See `bugs `__ for more information. neutron-12.1.1/doc/source/admin/deploy-ovs-provider.rst0000664000175000017500000003714613553660047023154 0ustar zuulzuul00000000000000.. _deploy-ovs-provider: =============================== Open vSwitch: Provider networks =============================== This architecture example provides layer-2 connectivity between instances and the physical network infrastructure using VLAN (802.1q) tagging. It supports one untagged (flat) network and up to 4095 tagged (VLAN) networks. The actual quantity of VLAN networks depends on the physical network infrastructure. For more information on provider networks, see :ref:`intro-os-networking-provider`. .. warning:: Linux distributions often package older releases of Open vSwitch that can introduce issues during operation with the Networking service. We recommend using at least the latest long-term stable (LTS) release of Open vSwitch for the best experience and support from Open vSwitch. See ``__ for available releases and the `installation instructions `__ for Prerequisites ~~~~~~~~~~~~~ One controller node with the following components: * Two network interfaces: management and provider. * OpenStack Networking server service and ML2 plug-in. Two compute nodes with the following components: * Two network interfaces: management and provider. * OpenStack Networking Open vSwitch (OVS) layer-2 agent, DHCP agent, metadata agent, and any dependencies including OVS. .. note:: Larger deployments typically deploy the DHCP and metadata agents on a subset of compute nodes to increase performance and redundancy. However, too many agents can overwhelm the message bus. Also, to further simplify any deployment, you can omit the metadata agent and use a configuration drive to provide metadata to instances. Architecture ~~~~~~~~~~~~ .. image:: figures/deploy-ovs-provider-overview.png :alt: Provider networks using OVS - overview The following figure shows components and connectivity for one untagged (flat) network. In this particular case, the instance resides on the same compute node as the DHCP agent for the network. If the DHCP agent resides on another compute node, the latter only contains a DHCP namespace with a port on the OVS integration bridge. .. image:: figures/deploy-ovs-provider-compconn1.png :alt: Provider networks using OVS - components and connectivity - one network The following figure describes virtual connectivity among components for two tagged (VLAN) networks. Essentially, all networks use a single OVS integration bridge with different internal VLAN tags. The internal VLAN tags almost always differ from the network VLAN assignment in the Networking service. Similar to the untagged network case, the DHCP agent may reside on a different compute node. .. image:: figures/deploy-ovs-provider-compconn2.png :alt: Provider networks using OVS - components and connectivity - multiple networks .. note:: These figures omit the controller node because it does not handle instance network traffic. Example configuration ~~~~~~~~~~~~~~~~~~~~~ Use the following example configuration as a template to deploy provider networks in your environment. Controller node --------------- #. Install the Networking service components that provide the ``neutron-server`` service and ML2 plug-in. #. In the ``neutron.conf`` file: * Configure common options: .. include:: shared/deploy-config-neutron-common.txt * Disable service plug-ins because provider networks do not require any. However, this breaks portions of the dashboard that manage the Networking service. See the `Queens Install Tutorials and Guides <../install/>`__ for more information. .. code-block:: ini [DEFAULT] service_plugins = * Enable two DHCP agents per network so both compute nodes can provide DHCP service provider networks. .. code-block:: ini [DEFAULT] dhcp_agents_per_network = 2 * If necessary, :ref:`configure MTU `. #. In the ``ml2_conf.ini`` file: * Configure drivers and network types: .. code-block:: ini [ml2] type_drivers = flat,vlan tenant_network_types = mechanism_drivers = openvswitch extension_drivers = port_security * Configure network mappings: .. code-block:: ini [ml2_type_flat] flat_networks = provider [ml2_type_vlan] network_vlan_ranges = provider .. note:: The ``tenant_network_types`` option contains no value because the architecture does not support self-service networks. .. note:: The ``provider`` value in the ``network_vlan_ranges`` option lacks VLAN ID ranges to support use of arbitrary VLAN IDs. #. Populate the database. .. code-block:: console # su -s /bin/sh -c "neutron-db-manage --config-file /etc/neutron/neutron.conf \ --config-file /etc/neutron/plugins/ml2/ml2_conf.ini upgrade head" neutron #. Start the following services: * Server Compute nodes ------------- #. Install the Networking service OVS layer-2 agent, DHCP agent, and metadata agent. #. Install OVS. #. In the ``neutron.conf`` file, configure common options: .. include:: shared/deploy-config-neutron-common.txt #. In the ``openvswitch_agent.ini`` file, configure the OVS agent: .. code-block:: ini [ovs] bridge_mappings = provider:br-provider [securitygroup] firewall_driver = iptables_hybrid #. In the ``dhcp_agent.ini`` file, configure the DHCP agent: .. code-block:: ini [DEFAULT] interface_driver = openvswitch enable_isolated_metadata = True force_metadata = True .. note:: The ``force_metadata`` option forces the DHCP agent to provide a host route to the metadata service on ``169.254.169.254`` regardless of whether the subnet contains an interface on a router, thus maintaining similar and predictable metadata behavior among subnets. #. In the ``metadata_agent.ini`` file, configure the metadata agent: .. code-block:: ini [DEFAULT] nova_metadata_host = controller metadata_proxy_shared_secret = METADATA_SECRET The value of ``METADATA_SECRET`` must match the value of the same option in the ``[neutron]`` section of the ``nova.conf`` file. #. Start the following services: * OVS #. Create the OVS provider bridge ``br-provider``: .. code-block:: console $ ovs-vsctl add-br br-provider #. Add the provider network interface as a port on the OVS provider bridge ``br-provider``: .. code-block:: console $ ovs-vsctl add-port br-provider PROVIDER_INTERFACE Replace ``PROVIDER_INTERFACE`` with the name of the underlying interface that handles provider networks. For example, ``eth1``. #. Start the following services: * OVS agent * DHCP agent * Metadata agent Verify service operation ------------------------ #. Source the administrative project credentials. #. Verify presence and operation of the agents: .. code-block:: console $ openstack network agent list +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | 1236bbcb-e0ba-48a9-80fc-81202ca4fa51 | Metadata agent | compute2 | | True | UP | neutron-metadata-agent | | 457d6898-b373-4bb3-b41f-59345dcfb5c5 | Open vSwitch agent | compute2 | | True | UP | neutron-openvswitch-agent | | 71f15e84-bc47-4c2a-b9fb-317840b2d753 | DHCP agent | compute2 | nova | True | UP | neutron-dhcp-agent | | a6c69690-e7f7-4e56-9831-1282753e5007 | Metadata agent | compute1 | | True | UP | neutron-metadata-agent | | af11f22f-a9f4-404f-9fd8-cd7ad55c0f68 | DHCP agent | compute1 | nova | True | UP | neutron-dhcp-agent | | bcfc977b-ec0e-4ba9-be62-9489b4b0e6f1 | Open vSwitch agent | compute1 | | True | UP | neutron-openvswitch-agent | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ Create initial networks ----------------------- .. include:: shared/deploy-provider-initialnetworks.txt Verify network operation ------------------------ .. include:: shared/deploy-provider-verifynetworkoperation.txt Network traffic flow ~~~~~~~~~~~~~~~~~~~~ .. include:: shared/deploy-provider-networktrafficflow.txt North-south ----------- * The instance resides on compute node 1 and uses provider network 1. * The instance sends a packet to a host on the Internet. The following steps involve compute node 1. #. The instance interface (1) forwards the packet to the security group bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge OVS port (4) forwards the packet to the OVS integration bridge security group port (5) via ``veth`` pair. #. The OVS integration bridge adds an internal VLAN tag to the packet. #. The OVS integration bridge ``int-br-provider`` patch port (6) forwards the packet to the OVS provider bridge ``phy-br-provider`` patch port (7). #. The OVS provider bridge swaps the internal VLAN tag with actual VLAN tag 101. #. The OVS provider bridge provider network port (8) forwards the packet to the physical network interface (9). #. The physical network interface forwards the packet to the physical network infrastructure switch (10). The following steps involve the physical network infrastructure: #. The switch removes VLAN tag 101 from the packet and forwards it to the router (11). #. The router routes the packet from the provider network (12) to the external network (13) and forwards the packet to the switch (14). #. The switch forwards the packet to the external network (15). #. The external network (16) receives the packet. .. image:: figures/deploy-ovs-provider-flowns1.png :alt: Provider networks using Open vSwitch - network traffic flow - north/south .. note:: Return traffic follows similar steps in reverse. East-west scenario 1: Instances on the same network --------------------------------------------------- Instances on the same network communicate directly between compute nodes containing those instances. * Instance 1 resides on compute node 1 and uses provider network 1. * Instance 2 resides on compute node 2 and uses provider network 1. * Instance 1 sends a packet to instance 2. The following steps involve compute node 1: #. The instance 1 interface (1) forwards the packet to the security group bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge OVS port (4) forwards the packet to the OVS integration bridge security group port (5) via ``veth`` pair. #. The OVS integration bridge adds an internal VLAN tag to the packet. #. The OVS integration bridge ``int-br-provider`` patch port (6) forwards the packet to the OVS provider bridge ``phy-br-provider`` patch port (7). #. The OVS provider bridge swaps the internal VLAN tag with actual VLAN tag 101. #. The OVS provider bridge provider network port (8) forwards the packet to the physical network interface (9). #. The physical network interface forwards the packet to the physical network infrastructure switch (10). The following steps involve the physical network infrastructure: #. The switch forwards the packet from compute node 1 to compute node 2 (11). The following steps involve compute node 2: #. The physical network interface (12) forwards the packet to the OVS provider bridge provider network port (13). #. The OVS provider bridge ``phy-br-provider`` patch port (14) forwards the packet to the OVS integration bridge ``int-br-provider`` patch port (15). #. The OVS integration bridge swaps the actual VLAN tag 101 with the internal VLAN tag. #. The OVS integration bridge security group port (16) forwards the packet to the security group bridge OVS port (17). #. Security group rules (18) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge instance port (19) forwards the packet to the instance 2 interface (20) via ``veth`` pair. .. image:: figures/deploy-ovs-provider-flowew1.png :alt: Provider networks using Open vSwitch - network traffic flow - east/west scenario 1 .. note:: Return traffic follows similar steps in reverse. East-west scenario 2: Instances on different networks ----------------------------------------------------- Instances communicate via router on the physical network infrastructure. * Instance 1 resides on compute node 1 and uses provider network 1. * Instance 2 resides on compute node 1 and uses provider network 2. * Instance 1 sends a packet to instance 2. .. note:: Both instances reside on the same compute node to illustrate how VLAN tagging enables multiple logical layer-2 networks to use the same physical layer-2 network. The following steps involve the compute node: #. The instance 1 interface (1) forwards the packet to the security group bridge instance port (2) via ``veth`` pair. #. Security group rules (3) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge OVS port (4) forwards the packet to the OVS integration bridge security group port (5) via ``veth`` pair. #. The OVS integration bridge adds an internal VLAN tag to the packet. #. The OVS integration bridge ``int-br-provider`` patch port (6) forwards the packet to the OVS provider bridge ``phy-br-provider`` patch port (7). #. The OVS provider bridge swaps the internal VLAN tag with actual VLAN tag 101. #. The OVS provider bridge provider network port (8) forwards the packet to the physical network interface (9). #. The physical network interface forwards the packet to the physical network infrastructure switch (10). The following steps involve the physical network infrastructure: #. The switch removes VLAN tag 101 from the packet and forwards it to the router (11). #. The router routes the packet from provider network 1 (12) to provider network 2 (13). #. The router forwards the packet to the switch (14). #. The switch adds VLAN tag 102 to the packet and forwards it to compute node 1 (15). The following steps involve the compute node: #. The physical network interface (16) forwards the packet to the OVS provider bridge provider network port (17). #. The OVS provider bridge ``phy-br-provider`` patch port (18) forwards the packet to the OVS integration bridge ``int-br-provider`` patch port (19). #. The OVS integration bridge swaps the actual VLAN tag 102 with the internal VLAN tag. #. The OVS integration bridge security group port (20) removes the internal VLAN tag and forwards the packet to the security group bridge OVS port (21). #. Security group rules (22) on the security group bridge handle firewalling and connection tracking for the packet. #. The security group bridge instance port (23) forwards the packet to the instance 2 interface (24) via ``veth`` pair. .. image:: figures/deploy-ovs-provider-flowew2.png :alt: Provider networks using Open vSwitch - network traffic flow - east/west scenario 2 .. note:: Return traffic follows similar steps in reverse. neutron-12.1.1/doc/source/admin/config-macvtap.rst0000664000175000017500000001466113553660046022115 0ustar zuulzuul00000000000000.. _config-macvtap: ======================== Macvtap mechanism driver ======================== The Macvtap mechanism driver for the ML2 plug-in generally increases network performance of instances. Consider the following attributes of this mechanism driver to determine practicality in your environment: * Supports only instance ports. Ports for DHCP and layer-3 (routing) services must use another mechanism driver such as Linux bridge or Open vSwitch (OVS). * Supports only untagged (flat) and tagged (VLAN) networks. * Lacks support for security groups including basic (sanity) and anti-spoofing rules. * Lacks support for layer-3 high-availability mechanisms such as Virtual Router Redundancy Protocol (VRRP) and Distributed Virtual Routing (DVR). * Only compute resources can be attached via macvtap. Attaching other resources like DHCP, Routers and others is not supported. Therefore run either OVS or linux bridge in VLAN or flat mode on the controller node. * Instance migration requires the same values for the ``physical_interface_mapping`` configuration option on each compute node. For more information, see ``_. Prerequisites ~~~~~~~~~~~~~ You can add this mechanism driver to an existing environment using either the Linux bridge or OVS mechanism drivers with only provider networks or provider and self-service networks. You can change the configuration of existing compute nodes or add compute nodes with the Macvtap mechanism driver. The example configuration assumes addition of compute nodes with the Macvtap mechanism driver to the :ref:`deploy-lb-selfservice` or :ref:`deploy-ovs-selfservice` deployment examples. Add one or more compute nodes with the following components: * Three network interfaces: management, provider, and overlay. * OpenStack Networking Macvtap layer-2 agent and any dependencies. .. note:: To support integration with the deployment examples, this content configures the Macvtap mechanism driver to use the overlay network for untagged (flat) or tagged (VLAN) networks in addition to overlay networks such as VXLAN. Your physical network infrastructure must support VLAN (802.1q) tagging on the overlay network. Architecture ~~~~~~~~~~~~ The Macvtap mechanism driver only applies to compute nodes. Otherwise, the environment resembles the prerequisite deployment example. .. image:: figures/config-macvtap-compute1.png :alt: Macvtap mechanism driver - compute node components .. image:: figures/config-macvtap-compute2.png :alt: Macvtap mechanism driver - compute node connectivity Example configuration ~~~~~~~~~~~~~~~~~~~~~ Use the following example configuration as a template to add support for the Macvtap mechanism driver to an existing operational environment. Controller node --------------- #. In the ``ml2_conf.ini`` file: * Add ``macvtap`` to mechanism drivers. .. code-block:: ini [ml2] mechanism_drivers = macvtap * Configure network mappings. .. code-block:: ini [ml2_type_flat] flat_networks = provider,macvtap [ml2_type_vlan] network_vlan_ranges = provider,macvtap:VLAN_ID_START:VLAN_ID_END .. note:: Use of ``macvtap`` is arbitrary. Only the self-service deployment examples require VLAN ID ranges. Replace ``VLAN_ID_START`` and ``VLAN_ID_END`` with appropriate numerical values. #. Restart the following services: * Server Network nodes ------------- No changes. Compute nodes ------------- #. Install the Networking service Macvtap layer-2 agent. #. In the ``neutron.conf`` file, configure common options: .. include:: shared/deploy-config-neutron-common.txt #. In the ``macvtap_agent.ini`` file, configure the layer-2 agent. .. code-block:: ini [macvtap] physical_interface_mappings = macvtap:MACVTAP_INTERFACE [securitygroup] firewall_driver = noop Replace ``MACVTAP_INTERFACE`` with the name of the underlying interface that handles Macvtap mechanism driver interfaces. If using a prerequisite deployment example, replace ``MACVTAP_INTERFACE`` with the name of the underlying interface that handles overlay networks. For example, ``eth1``. #. Start the following services: * Macvtap agent Verify service operation ------------------------ #. Source the administrative project credentials. #. Verify presence and operation of the agents: .. code-block:: console $ openstack network agent list +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ | 31e1bc1b-c872-4429-8fc3-2c8eba52634e | Metadata agent | compute1 | None | True | UP | neutron-metadata-agent | | 378f5550-feee-42aa-a1cb-e548b7c2601f | Open vSwitch agent | compute1 | None | True | UP | neutron-openvswitch-agent | | 7d2577d0-e640-42a3-b303-cb1eb077f2b6 | L3 agent | compute1 | nova | True | UP | neutron-l3-agent | | d5d7522c-ad14-4c63-ab45-f6420d6a81dd | Metering agent | compute1 | None | True | UP | neutron-metering-agent | | e838ef5c-75b1-4b12-84da-7bdbd62f1040 | DHCP agent | compute1 | nova | True | UP | neutron-dhcp-agent | +--------------------------------------+--------------------+----------+-------------------+-------+-------+---------------------------+ Create initial networks ----------------------- This mechanism driver simply changes the virtual network interface driver for instances. Thus, you can reference the ``Create initial networks`` content for the prerequisite deployment example. Verify network operation ------------------------ This mechanism driver simply changes the virtual network interface driver for instances. Thus, you can reference the ``Verify network operation`` content for the prerequisite deployment example. Network traffic flow ~~~~~~~~~~~~~~~~~~~~ This mechanism driver simply removes the Linux bridge handling security groups on the compute nodes. Thus, you can reference the network traffic flow scenarios for the prerequisite deployment example. neutron-12.1.1/doc/source/admin/intro-overlay-protocols.rst0000664000175000017500000000274013553660047024047 0ustar zuulzuul00000000000000.. _intro-overlay-protocols: ========================== Overlay (tunnel) protocols ========================== Tunneling is a mechanism that makes transfer of payloads feasible over an incompatible delivery network. It allows the network user to gain access to denied or insecure networks. Data encryption may be employed to transport the payload, ensuring that the encapsulated user network data appears as public even though it is private and can easily pass the conflicting network. Generic routing encapsulation (GRE) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Generic routing encapsulation (GRE) is a protocol that runs over IP and is employed when delivery and payload protocols are compatible but payload addresses are incompatible. For instance, a payload might think it is running on a datalink layer but it is actually running over a transport layer using datagram protocol over IP. GRE creates a private point-to-point connection and works by encapsulating a payload. GRE is a foundation protocol for other tunnel protocols but the GRE tunnels provide only weak authentication. .. _VXLAN: Virtual extensible local area network (VXLAN) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The purpose of VXLAN is to provide scalable network isolation. VXLAN is a Layer 2 overlay scheme on a Layer 3 network. It allows an overlay layer-2 network to spread across multiple underlay layer-3 network domains. Each overlay is termed a VXLAN segment. Only VMs within the same VXLAN segment can communicate. neutron-12.1.1/doc/source/admin/config-auto-allocation.rst0000664000175000017500000002445713553660046023561 0ustar zuulzuul00000000000000.. _config-auto-allocation: ========================================== Automatic allocation of network topologies ========================================== The auto-allocation feature introduced in Mitaka simplifies the procedure of setting up an external connectivity for end-users, and is also known as **Get Me A Network**. Previously, a user had to configure a range of networking resources to boot a server and get access to the Internet. For example, the following steps are required: * Create a network * Create a subnet * Create a router * Uplink the router on an external network * Downlink the router on the previously created subnet These steps need to be performed on each logical segment that a VM needs to be connected to, and may require networking knowledge the user might not have. This feature is designed to automate the basic networking provisioning for projects. The steps to provision a basic network are run during instance boot, making the networking setup hands-free. To make this possible, provide a default external network and default subnetpools (one for IPv4, or one for IPv6, or one of each) so that the Networking service can choose what to do in lieu of input. Once these are in place, users can boot their VMs without specifying any networking details. The Compute service will then use this feature automatically to wire user VMs. Enabling the deployment for auto-allocation ------------------------------------------- To use this feature, the neutron service must have the following extensions enabled: * ``auto-allocated-topology`` * ``subnet_allocation`` * ``external-net`` * ``router`` Before the end-user can use the auto-allocation feature, the operator must create the resources that will be used for the auto-allocated network topology creation. To perform this task, proceed with the following steps: #. Set up a default external network Setting up an external network is described in `OpenStack Networking Guide <./archives/adv-features.html>`_. Assuming the external network to be used for the auto-allocation feature is named ``public``, make it the ``default`` external network with the following command: .. code-block:: console $ openstack network set public --default .. note:: The flag ``--default`` (and ``--no-default`` flag) is only effective with external networks and has no effects on regular (or internal) networks. #. Create default subnetpools The auto-allocation feature requires at least one default subnetpool. One for IPv4, or one for IPv6, or one of each. .. code-block:: console $ openstack subnet pool create --share --default \ --pool-prefix 192.0.2.0/24 --default-prefix-length 26 \ shared-default +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | address_scope_id | None | | created_at | 2017-01-12T15:10:34Z | | default_prefixlen | 26 | | default_quota | None | | description | | | headers | | | id | b41b7b9c-de57-4c19-b1c5-731985bceb7f | | ip_version | 4 | | is_default | True | | max_prefixlen | 32 | | min_prefixlen | 8 | | name | shared-default | | prefixes | 192.0.2.0/24 | | project_id | 86acdbd1d72745fd8e8320edd7543400 | | revision_number | 1 | | shared | True | | tags | [] | | updated_at | 2017-01-12T15:10:34Z | +-------------------+--------------------------------------+ $ openstack subnet pool create --share --default \ --pool-prefix 2001:db8:8000::/48 --default-prefix-length 64 \ default-v6 +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | address_scope_id | None | | created_at | 2017-01-12T15:14:35Z | | default_prefixlen | 64 | | default_quota | None | | description | | | headers | | | id | 6f387016-17f0-4564-96ad-e34775b6ea14 | | ip_version | 6 | | is_default | True | | max_prefixlen | 128 | | min_prefixlen | 64 | | name | default-v6 | | prefixes | 2001:db8:8000::/48 | | project_id | 86acdbd1d72745fd8e8320edd7543400 | | revision_number | 1 | | shared | True | | tags | [] | | updated_at | 2017-01-12T15:14:35Z | +-------------------+--------------------------------------+ Get Me A Network ---------------- In a deployment where the operator has set up the resources as described above, they can get their auto-allocated network topology as follows: .. code-block:: console $ openstack network auto allocated topology create --or-show +------------+--------------------------------------+ | Field | Value | +------------+--------------------------------------+ | id | a380c780-d6cd-4510-a4c0-1a6ec9b85a29 | | name | None | | project_id | cfd1889ac7d64ad891d4f20aef9f8d7c | +------------+--------------------------------------+ .. note:: When the ``--or-show`` option is used the command returns the topology information if it already exists. Operators (and users with admin role) can get the auto-allocated topology for a project by specifying the project ID: .. code-block:: console $ openstack network auto allocated topology create --project \ cfd1889ac7d64ad891d4f20aef9f8d7c --or-show +------------+--------------------------------------+ | Field | Value | +------------+--------------------------------------+ | id | a380c780-d6cd-4510-a4c0-1a6ec9b85a29 | | name | None | | project_id | cfd1889ac7d64ad891d4f20aef9f8d7c | +------------+--------------------------------------+ The ID returned by this command is a network which can be used for booting a VM. .. code-block:: console $ openstack server create --flavor m1.small --image \ cirros-0.3.5-x86_64-uec --nic \ net-id=8b835bfb-cae2-4acc-b53f-c16bb5f9a7d0 vm1 The auto-allocated topology for a user never changes. In practice, when a user boots a server omitting the ``--nic`` option, and there is more than one network available, the Compute service will invoke the API behind ``auto allocated topology create``, fetch the network UUID, and pass it on during the boot process. Validating the requirements for auto-allocation ----------------------------------------------- To validate that the required resources are correctly set up for auto-allocation, without actually provisioning anything, use the ``--check-resources`` option: .. code-block:: console $ openstack network auto allocated topology create --check-resources Deployment error: No default router:external network. $ openstack network set public --default $ openstack network auto allocated topology create --check-resources Deployment error: No default subnetpools defined. $ openstack subnet pool set shared-default --default $ openstack network auto allocated topology create --check-resources +---------+-------+ | Field | Value | +---------+-------+ | dry-run | pass | +---------+-------+ The validation option behaves identically for all users. However, it is considered primarily an admin or service utility since it is the operator who must set up the requirements. Project resources created by auto-allocation -------------------------------------------- The auto-allocation feature creates one network topology in every project where it is used. The auto-allocated network topology for a project contains the following resources: +--------------------+------------------------------+ |Resource |Name | +====================+==============================+ |network |``auto_allocated_network`` | +--------------------+------------------------------+ |subnet (IPv4) |``auto_allocated_subnet_v4`` | +--------------------+------------------------------+ |subnet (IPv6) |``auto_allocated_subnet_v6`` | +--------------------+------------------------------+ |router |``auto_allocated_router`` | +--------------------+------------------------------+ Compatibility notes ------------------- Nova uses the ``auto allocated topology`` feature with API micro version 2.37 or later. This is because, unlike the neutron feature which was implemented in the Mitaka release, the integration for nova was completed during the Newton release cycle. Note that the CLI option ``--nic`` can be omitted regardless of the microversion used as long as there is no more than one network available to the project, in which case nova fails with a 400 error because it does not know which network to use. Furthermore, nova does not start using the feature, regardless of whether or not a user requests micro version 2.37 or later, unless all of the ``nova-compute`` services are running Newton-level code. neutron-12.1.1/doc/source/admin/misc.rst0000664000175000017500000000025613553660047020146 0ustar zuulzuul00000000000000.. _miscellaneous: ============= Miscellaneous ============= .. toctree:: :maxdepth: 2 fwaas-v2-scenario fwaas-v1-scenario misc-libvirt neutron_linuxbridge neutron-12.1.1/doc/source/admin/config-qos.rst0000664000175000017500000010213613553660047021260 0ustar zuulzuul00000000000000.. _config-qos: ======================== Quality of Service (QoS) ======================== QoS is defined as the ability to guarantee certain network requirements like bandwidth, latency, jitter, and reliability in order to satisfy a Service Level Agreement (SLA) between an application provider and end users. Network devices such as switches and routers can mark traffic so that it is handled with a higher priority to fulfill the QoS conditions agreed under the SLA. In other cases, certain network traffic such as Voice over IP (VoIP) and video streaming needs to be transmitted with minimal bandwidth constraints. On a system without network QoS management, all traffic will be transmitted in a "best-effort" manner making it impossible to guarantee service delivery to customers. QoS is an advanced service plug-in. QoS is decoupled from the rest of the OpenStack Networking code on multiple levels and it is available through the ml2 extension driver. Details about the DB models, API extension, and use cases are out of the scope of this guide but can be found in the `Neutron QoS specification `_. Supported QoS rule types ~~~~~~~~~~~~~~~~~~~~~~~~ QoS supported rule types are now available as ``VALID_RULE_TYPES`` in `QoS rule types `_: * bandwidth_limit: Bandwidth limitations on networks, ports or floating IPs. * dscp_marking: Marking network traffic with a DSCP value. * minimum_bandwidth: Minimum bandwidth constraints on certain types of traffic. Any QoS driver can claim support for some QoS rule types by providing a driver property called ``supported_rules``, the QoS driver manager will recalculate rule types dynamically that the QoS driver supports. The following table shows the Networking back ends, QoS supported rules, and traffic directions (from the VM point of view). .. table:: **Networking back ends, supported rules, and traffic direction** ==================== =================== ================ =================== Rule \\ back end Open vSwitch SR-IOV Linux bridge ==================== =================== ================ =================== Bandwidth limit Egress \\ Ingress Egress (1) Egress \\ Ingress Minimum bandwidth - Egress - DSCP marking Egress - Egress ==================== =================== ================ =================== .. note:: (1) Max burst parameter is skipped because it is not supported by the IP tool. In the most simple case, the property can be represented by a simple Python list defined on the class. For an ml2 plug-in, the list of supported QoS rule types and parameters is defined as a common subset of rules supported by all active mechanism drivers. A QoS rule is always attached to a QoS policy. When a rule is created or updated: * The QoS plug-in will check if this rule and parameters are supported by any active mechanism driver if the QoS policy is not attached to any port or network. * The QoS plug-in will check if this rule and parameters are supported by the mechanism drivers managing those ports if the QoS policy is attached to any port or network. Valid DSCP Marks ---------------- Valid DSCP mark values are even numbers between 0 and 56, except 2-6, 42, 44, and 50-54. The full list of valid DSCP marks is: 0, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 46, 48, 56 Configuration ~~~~~~~~~~~~~ To enable the service on a cloud with the architecture described in `Networking architecture `_, follow the steps below: On the controller nodes: #. Add the QoS service to the ``service_plugins`` setting in ``/etc/neutron/neutron.conf``. For example: .. code-block:: none service_plugins = \ neutron.services.l3_router.l3_router_plugin.L3RouterPlugin, neutron.services.metering.metering_plugin.MeteringPlugin, neutron.services.qos.qos_plugin.QoSPlugin #. Optionally, set the needed ``notification_drivers`` in the ``[qos]`` section in ``/etc/neutron/neutron.conf`` (``message_queue`` is the default). #. Optionally, in order to enable the floating IP QoS extension ``qos-fip``, set the ``service_plugins`` option in ``/etc/neutron/neutron.conf`` to include both ``router`` and ``qos``. For example: .. code-block:: none service_plugins = router, qos #. In ``/etc/neutron/plugins/ml2/ml2_conf.ini``, add ``qos`` to ``extension_drivers`` in the ``[ml2]`` section. For example: .. code-block:: ini [ml2] extension_drivers = port_security, qos #. Edit the configuration file for the agent you are using and set the ``extensions`` to include ``qos`` in the ``[agent]`` section of the configuration file. The agent configuration file will reside in ``/etc/neutron/plugins/ml2/_agent.ini`` where ``agent_name`` is the name of the agent being used (for example ``openvswitch``). For example: .. code-block:: ini [agent] extensions = qos On the network and compute nodes: #. Edit the configuration file for the agent you are using and set the ``extensions`` to include ``qos`` in the ``[agent]`` section of the configuration file. The agent configuration file will reside in ``/etc/neutron/plugins/ml2/_agent.ini`` where ``agent_name`` is the name of the agent being used (for example ``openvswitch``). For example: .. code-block:: ini [agent] extensions = qos #. Optionally, in order to enable QoS for floating IPs, set the ``extensions`` option in the ``[agent]`` section of ``/etc/neutron/l3_agent.ini`` to include ``fip_qos``. If ``dvr`` is enabled, this has to be done for all the L3 agents. For example: .. code-block:: ini [agent] extensions = fip_qos #. As rate limit doesn't work on Open vSwitch's ``internal`` ports, optionally, as a workaround, to make QoS bandwidth limit work on router's gateway ports, set ``ovs_use_veth`` to ``True`` in ``DEFAULT`` section in ``/etc/neutron/l3_agent.ini`` .. code-block:: ini [DEFAULT] ovs_use_veth = True .. note:: QoS currently works with ml2 only (SR-IOV, Open vSwitch, and linuxbridge are drivers enabled for QoS). DSCP marking on outer header for overlay networks ------------------------------------------------- When using overlay networks (e.g., VxLAN), the DSCP marking rule only applies to the inner header, and during encapsulation, the DSCP mark is not automatically copied to the outer header. #. In order to set the DSCP value of the outer header, modify the ``dscp`` configuration option in ``/etc/neutron/plugins/ml2/_agent.ini`` where ```` is the name of the agent being used (e.g., ``openvswitch``): .. code-block:: ini [agent] dscp = 8 #. In order to copy the DSCP field of the inner header to the outer header, change the ``dscp_inherit`` configuration option to true in ``/etc/neutron/plugins/ml2/_agent.ini`` where ```` is the name of the agent being used (e.g., ``openvswitch``): .. code-block:: ini [agent] dscp_inherit = true If the ``dscp_inherit`` option is set to true, the previous ``dscp`` option is overwritten. Trusted projects policy.json configuration ------------------------------------------ If projects are trusted to administrate their own QoS policies in your cloud, neutron's file ``policy.json`` can be modified to allow this. Modify ``/etc/neutron/policy.json`` policy entries as follows: .. code-block:: none "get_policy": "rule:regular_user", "create_policy": "rule:regular_user", "update_policy": "rule:regular_user", "delete_policy": "rule:regular_user", "get_rule_type": "rule:regular_user", To enable bandwidth limit rule: .. code-block:: none "get_policy_bandwidth_limit_rule": "rule:regular_user", "create_policy_bandwidth_limit_rule": "rule:regular_user", "delete_policy_bandwidth_limit_rule": "rule:regular_user", "update_policy_bandwidth_limit_rule": "rule:regular_user", To enable DSCP marking rule: .. code-block:: none "get_policy_dscp_marking_rule": "rule:regular_user", "create_dscp_marking_rule": "rule:regular_user", "delete_dscp_marking_rule": "rule:regular_user", "update_dscp_marking_rule": "rule:regular_user", To enable minimum bandwidth rule: .. code-block:: none "get_policy_minimum_bandwidth_rule": "rule:regular_user", "create_policy_minimum_bandwidth_rule": "rule:regular_user", "delete_policy_minimum_bandwidth_rule": "rule:regular_user", "update_policy_minimum_bandwidth_rule": "rule:regular_user", User workflow ~~~~~~~~~~~~~ QoS policies are only created by admins with the default ``policy.json``. Therefore, you should have the cloud operator set them up on behalf of the cloud projects. If projects are trusted to create their own policies, check the trusted projects ``policy.json`` configuration section. First, create a QoS policy and its bandwidth limit rule: .. code-block:: console $ openstack network qos policy create bw-limiter +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | description | | | id | 5df855e9-a833-49a3-9c82-c0839a5f103f | | is_default | False | | name | bw-limiter | | project_id | 4db7c1ed114a4a7fb0f077148155c500 | | rules | [] | | shared | False | +-------------------+--------------------------------------+ $ openstack network qos rule create --type bandwidth-limit --max-kbps 3000 \ --max-burst-kbits 2400 --egress bw-limiter +----------------+--------------------------------------+ | Field | Value | +----------------+--------------------------------------+ | direction | egress | | id | 92ceb52f-170f-49d0-9528-976e2fee2d6f | | max_burst_kbps | 2400 | | max_kbps | 3000 | | name | None | | project_id | | +----------------+--------------------------------------+ .. note:: The QoS implementation requires a burst value to ensure proper behavior of bandwidth limit rules in the Open vSwitch and Linux bridge agents. If you do not provide a value, it defaults to 80% of the bandwidth limit which works for typical TCP traffic. Second, associate the created policy with an existing neutron port. In order to do this, user extracts the port id to be associated to the already created policy. In the next example, we will assign the ``bw-limiter`` policy to the VM with IP address ``192.0.2.1``. .. code-block:: console $ openstack port list +--------------------------------------+-----------------------------------+ | ID | Fixed IP Addresses | +--------------------------------------+-----------------------------------+ | 0271d1d9-1b16-4410-bd74-82cdf6dcb5b3 | { ... , "ip_address": "192.0.2.1"}| | 88101e57-76fa-4d12-b0e0-4fc7634b874a | { ... , "ip_address": "192.0.2.3"}| | e04aab6a-5c6c-4bd9-a600-33333551a668 | { ... , "ip_address": "192.0.2.2"}| +--------------------------------------+-----------------------------------+ $ openstack port set --qos-policy bw-limiter \ 88101e57-76fa-4d12-b0e0-4fc7634b874a In order to detach a port from the QoS policy, simply update again the port configuration. .. code-block:: console $ openstack port unset --qos-policy 88101e57-76fa-4d12-b0e0-4fc7634b874a Ports can be created with a policy attached to them too. .. code-block:: console $ openstack port create --qos-policy bw-limiter --network private port1 +-----------------------+--------------------------------------------------+ | Field | Value | +-----------------------+--------------------------------------------------+ | admin_state_up | UP | | allowed_address_pairs | | | binding_host_id | | | binding_profile | | | binding_vif_details | | | binding_vif_type | unbound | | binding_vnic_type | normal | | created_at | 2017-05-15T08:43:00Z | | data_plane_status | None | | description | | | device_id | | | device_owner | | | dns_assignment | None | | dns_name | None | | extra_dhcp_opts | | | fixed_ips | ip_address='10.0.10.4', subnet_id='292f8c1e-...' | | id | f51562ee-da8d-42de-9578-f6f5cb248226 | | ip_address | None | | mac_address | fa:16:3e:d9:f2:ba | | name | port1 | | network_id | 55dc2f70-0f92-4002-b343-ca34277b0234 | | option_name | None | | option_value | None | | port_security_enabled | False | | project_id | 4db7c1ed114a4a7fb0f077148155c500 | | qos_policy_id | 5df855e9-a833-49a3-9c82-c0839a5f103f | | revision_number | 6 | | security_group_ids | 0531cc1a-19d1-4cc7-ada5-49f8b08245be | | status | DOWN | | subnet_id | None | | tags | [] | | trunk_details | None | | updated_at | 2017-05-15T08:43:00Z | +-----------------------+--------------------------------------------------+ You can attach networks to a QoS policy. The meaning of this is that any compute port connected to the network will use the network policy by default unless the port has a specific policy attached to it. Internal network owned ports like DHCP and internal router ports are excluded from network policy application. In order to attach a QoS policy to a network, update an existing network, or initially create the network attached to the policy. .. code-block:: console $ openstack network set --qos-policy bw-limiter private .. note:: Configuring the proper burst value is very important. If the burst value is set too low, bandwidth usage will be throttled even with a proper bandwidth limit setting. This issue is discussed in various documentation sources, for example in `Juniper's documentation `_. Burst value for TCP traffic can be set as 80% of desired bandwidth limit value. For example, if the bandwidth limit is set to 1000kbps then enough burst value will be 800kbit. If the configured burst value is too low, achieved bandwidth limit will be lower than expected. If the configured burst value is too high, too few packets could be limited and achieved bandwidth limit would be higher than expected. The created policy can be associated with an existing floating IP. In order to do this, user extracts the floating IP id to be associated to the already created policy. In the next example, we will assign the ``bw-limiter`` policy to the floating IP address ``172.16.100.18``. .. code-block:: console $ openstack floating ip list +--------------------------------------+---------------------+------------------+------+-----+ | ID | Floating IP Address | Fixed IP Address | Port | ... | +--------------------------------------+---------------------+------------------+------+-----+ | 1163d127-6df3-44bb-b69c-c0e916303eb3 | 172.16.100.9 | None | None | ... | | d0ed7491-3eb7-4c4f-a0f0-df04f10a067c | 172.16.100.18 | None | None | ... | | f5a9ed48-2e9f-411c-8787-2b6ecd640090 | 172.16.100.2 | None | None | ... | +--------------------------------------+---------------------+------------------+------+-----+ .. code-block:: console $ openstack floating ip set --qos-policy bw-limiter d0ed7491-3eb7-4c4f-a0f0-df04f10a067c In order to detach a floating IP from the QoS policy, simply update the floating IP configuration. .. code-block:: console $ openstack floating ip set --no-qos-policy d0ed7491-3eb7-4c4f-a0f0-df04f10a067c Or use the ``unset`` action. .. code-block:: console $ openstack floating ip unset --qos-policy d0ed7491-3eb7-4c4f-a0f0-df04f10a067c Floating IPs can be created with a policy attached to them too. .. code-block:: console $ openstack floating ip create --qos-policy bw-limiter public +---------------------+--------------------------------------+ | Field | Value | +---------------------+--------------------------------------+ | created_at | 2017-12-06T02:12:09Z | | description | | | fixed_ip_address | None | | floating_ip_address | 172.16.100.12 | | floating_network_id | 4065eb05-cccb-4048-988c-e8c5480a746f | | id | 6a0efeef-462b-4312-b4ad-627cde8a20e6 | | name | 172.16.100.12 | | port_id | None | | project_id | 916e39e8be52433ba040da3a3a6d0847 | | qos_policy_id | 5df855e9-a833-49a3-9c82-c0839a5f103f | | revision_number | 1 | | router_id | None | | status | DOWN | | updated_at | 2017-12-06T02:12:09Z | +---------------------+--------------------------------------+ The QoS bandwidth limit rules attached to a floating IP will become active when you associate the latter with a port. For example, to associate the previously created floating IP ``172.16.100.12`` to the instance port with fixed IP ``192.168.222.5``: .. code-block:: console $ openstack port show a7f25e73-4288-4a16-93b9-b71e6fd00862 +-----------------------+--------------------------------------------------+ | Field | Value | +-----------------------+--------------------------------------------------+ | admin_state_up | UP | | ... | ... | | device_id | 69c03d70-53e8-4030-9c02-675c47f0b06b | | device_owner | compute:nova | | dns_assignment | None | | dns_name | None | | extra_dhcp_opts | | | fixed_ips | ip_address='192.168.222.5', subnet_id='...' | | id | a7f25e73-4288-4a16-93b9-b71e6fd00862 | | ip_address | None | | mac_address | fa:16:3e:b5:1a:cc | | name | | | network_id | ea602456-3ea8-4989-8981-add6182b4ceb | | option_name | None | | option_value | None | | port_security_enabled | False | | project_id | 916e39e8be52433ba040da3a3a6d0847 | | qos_policy_id | None | | revision_number | 6 | | security_group_ids | 77436c73-3a29-42a7-b544-d47f4ea96d54 | | status | ACTIVE | | subnet_id | None | | tags | | | trunk_details | None | | updated_at | 2017-12-05T15:48:54Z | +-----------------------+--------------------------------------------------+ .. code-block:: console $ openstack floating ip set --port a7f25e73-4288-4a16-93b9-b71e6fd00862 \ 0eeb1f8a-de96-4cd9-a0f6-3f535c409558 .. note:: For now, the L3 agent floating IP QoS extension only uses ``bandwidth_limit`` rules. Other rule types (like DSCP marking) will be silently ignored for floating IPs. A QoS policy that does not contain any ``bandwidth_limit`` rules will have no effect when attached to a floating IP. If floating IP is bound to a port, and both have binding QoS bandwidth rules, the L3 agent floating IP QoS extension ignores the behavior of the port QoS, and installs the rules on the appropriate device in the router namespace. Each project can have at most one default QoS policy, although it is not mandatory. If a default QoS policy is defined, all new networks created within this project will have this policy assigned, as long as no other QoS policy is explicitly attached during the creation process. If the default QoS policy is unset, no change to existing networks will be made. In order to set a QoS policy as default, the parameter ``--default`` must be used. To unset this QoS policy as default, the parameter ``--no-default`` must be used. .. code-block:: console $ openstack network qos policy create --default bw-limiter +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | description | | | id | 5df855e9-a833-49a3-9c82-c0839a5f103f | | is_default | True | | name | bw-limiter | | project_id | 4db7c1ed114a4a7fb0f077148155c500 | | rules | [] | | shared | False | +-------------------+--------------------------------------+ $ openstack network qos policy set --no-default bw-limiter +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | description | | | id | 5df855e9-a833-49a3-9c82-c0839a5f103f | | is_default | False | | name | bw-limiter | | project_id | 4db7c1ed114a4a7fb0f077148155c500 | | rules | [] | | shared | False | +-------------------+--------------------------------------+ Administrator enforcement ------------------------- Administrators are able to enforce policies on project ports or networks. As long as the policy is not shared, the project is not be able to detach any policy attached to a network or port. If the policy is shared, the project is able to attach or detach such policy from its own ports and networks. Rule modification ----------------- You can modify rules at runtime. Rule modifications will be propagated to any attached port. .. code-block:: console $ openstack network qos rule set --max-kbps 2000 --max-burst-kbits 1600 \ --ingress bw-limiter 92ceb52f-170f-49d0-9528-976e2fee2d6f $ openstack network qos rule show \ bw-limiter 92ceb52f-170f-49d0-9528-976e2fee2d6f +----------------+--------------------------------------+ | Field | Value | +----------------+--------------------------------------+ | direction | ingress | | id | 92ceb52f-170f-49d0-9528-976e2fee2d6f | | max_burst_kbps | 1600 | | max_kbps | 2000 | | name | None | | project_id | | +----------------+--------------------------------------+ Just like with bandwidth limiting, create a policy for DSCP marking rule: .. code-block:: console $ openstack network qos policy create dscp-marking +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | description | | | id | d1f90c76-fbe8-4d6f-bb87-a9aea997ed1e | | is_default | False | | name | dscp-marking | | project_id | 4db7c1ed114a4a7fb0f077148155c500 | | rules | [] | | shared | False | +-------------------+--------------------------------------+ You can create, update, list, delete, and show DSCP markings with the neutron client: .. code-block:: console $ openstack network qos rule create --type dscp-marking --dscp-mark 26 \ dscp-marking +----------------+--------------------------------------+ | Field | Value | +----------------+--------------------------------------+ | dscp_mark | 26 | | id | 115e4f70-8034-4176-8fe9-2c47f8878a7d | | name | None | | project_id | | +----------------+--------------------------------------+ .. code-block:: console $ openstack network qos rule set --dscp-mark 22 \ dscp-marking 115e4f70-8034-4176-8fe9-2c47f8878a7d $ openstack network qos rule list dscp-marking +--------------------------------------+----------------------------------+ | ID | DSCP Mark | +--------------------------------------+----------------------------------+ | 115e4f70-8034-4176-8fe9-2c47f8878a7d | 22 | +--------------------------------------+----------------------------------+ $ openstack network qos rule show \ dscp-marking 115e4f70-8034-4176-8fe9-2c47f8878a7d +----------------+--------------------------------------+ | Field | Value | +----------------+--------------------------------------+ | dscp_mark | 22 | | id | 115e4f70-8034-4176-8fe9-2c47f8878a7d | | name | None | | project_id | | +----------------+--------------------------------------+ $ openstack network qos rule delete \ dscp-marking 115e4f70-8034-4176-8fe9-2c47f8878a7d You can also include minimum bandwidth rules in your policy: .. code-block:: console $ openstack network qos policy create bandwidth-control +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | description | | | id | 8491547e-add1-4c6c-a50e-42121237256c | | is_default | False | | name | bandwidth-control | | project_id | 7cc5a84e415d48e69d2b06aa67b317d8 | | revision_number | 1 | | rules | [] | | shared | False | +-------------------+--------------------------------------+ $ openstack network qos rule create \ --type minimum-bandwidth --min-kbps 1000 --egress bandwidth-control +------------+--------------------------------------+ | Field | Value | +------------+--------------------------------------+ | direction | egress | | id | da858b32-44bc-43c9-b92b-cf6e2fa836ab | | min_kbps | 1000 | | name | None | | project_id | | +------------+--------------------------------------+ A policy with a minimum bandwidth ensures best efforts are made to provide no less than the specified bandwidth to each port on which the rule is applied. However, as this feature is not yet integrated with the Compute scheduler, minimum bandwidth cannot be guaranteed. It is also possible to combine several rules in one policy: .. code-block:: console $ openstack network qos rule create --type bandwidth-limit \ --max-kbps 50000 --max-burst-kbits 50000 bandwidth-control +----------------+--------------------------------------+ | Field | Value | +----------------+--------------------------------------+ | id | 0db48906-a762-4d32-8694-3f65214c34a6 | | max_burst_kbps | 50000 | | max_kbps | 50000 | | name | None | | project_id | | +----------------+--------------------------------------+ $ openstack network qos policy show bandwidth-control +-------------------+-------------------------------------------------------------------+ | Field | Value | +-------------------+-------------------------------------------------------------------+ | description | | | id | 8491547e-add1-4c6c-a50e-42121237256c | | is_default | False | | name | bandwidth-control | | project_id | 7cc5a84e415d48e69d2b06aa67b317d8 | | revision_number | 4 | | rules | [{u'max_kbps': 50000, u'type': u'bandwidth_limit', | | | u'id': u'0db48906-a762-4d32-8694-3f65214c34a6', | | | u'max_burst_kbps': 50000, | | | u'qos_policy_id': u'8491547e-add1-4c6c-a50e-42121237256c'}, | | | {u'direction': | | | u'egress', u'min_kbps': 1000, u'type': u'minimum_bandwidth', | | | u'id': u'da858b32-44bc-43c9-b92b-cf6e2fa836ab', | | | u'qos_policy_id': u'8491547e-add1-4c6c-a50e-42121237256c'}] | | shared | False | +-------------------+-------------------------------------------------------------------+ neutron-12.1.1/doc/source/admin/intro-os-networking.rst0000664000175000017500000003543313553660047023157 0ustar zuulzuul00000000000000.. _intro-os-networking: ==================== OpenStack Networking ==================== OpenStack Networking allows you to create and manage network objects, such as networks, subnets, and ports, which other OpenStack services can use. Plug-ins can be implemented to accommodate different networking equipment and software, providing flexibility to OpenStack architecture and deployment. The Networking service, code-named neutron, provides an API that lets you define network connectivity and addressing in the cloud. The Networking service enables operators to leverage different networking technologies to power their cloud networking. The Networking service also provides an API to configure and manage a variety of network services ranging from L3 forwarding and Network Address Translation (NAT) to load balancing, perimeter firewalls, and virtual private networks. It includes the following components: API server The OpenStack Networking API includes support for Layer 2 networking and IP Address Management (IPAM), as well as an extension for a Layer 3 router construct that enables routing between Layer 2 networks and gateways to external networks. OpenStack Networking includes a growing list of plug-ins that enable interoperability with various commercial and open source network technologies, including routers, switches, virtual switches and software-defined networking (SDN) controllers. OpenStack Networking plug-in and agents Plugs and unplugs ports, creates networks or subnets, and provides IP addressing. The chosen plug-in and agents differ depending on the vendor and technologies used in the particular cloud. It is important to mention that only one plug-in can be used at a time. Messaging queue Accepts and routes RPC requests between agents to complete API operations. Message queue is used in the ML2 plug-in for RPC between the neutron server and neutron agents that run on each hypervisor, in the ML2 mechanism drivers for Open vSwitch and Linux bridge. Concepts ~~~~~~~~ To configure rich network topologies, you can create and configure networks and subnets and instruct other OpenStack services like Compute to attach virtual devices to ports on these networks. OpenStack Compute is a prominent consumer of OpenStack Networking to provide connectivity for its instances. In particular, OpenStack Networking supports each project having multiple private networks and enables projects to choose their own IP addressing scheme, even if those IP addresses overlap with those that other projects use. There are two types of network, project and provider networks. It is possible to share any of these types of networks among projects as part of the network creation process. .. _intro-os-networking-provider: Provider networks ----------------- Provider networks offer layer-2 connectivity to instances with optional support for DHCP and metadata services. These networks connect, or map, to existing layer-2 networks in the data center, typically using VLAN (802.1q) tagging to identify and separate them. Provider networks generally offer simplicity, performance, and reliability at the cost of flexibility. By default only administrators can create or update provider networks because they require configuration of physical network infrastructure. It is possible to change the user who is allowed to create or update provider networks with the following parameters of ``policy.json``: * ``create_network:provider:physical_network`` * ``update_network:provider:physical_network`` .. warning:: The creation and modification of provider networks enables use of physical network resources, such as VLAN-s. Enable these changes only for trusted projects. Also, provider networks only handle layer-2 connectivity for instances, thus lacking support for features such as routers and floating IP addresses. In many cases, operators who are already familiar with virtual networking architectures that rely on physical network infrastructure for layer-2, layer-3, or other services can seamlessly deploy the OpenStack Networking service. In particular, provider networks appeal to operators looking to migrate from the Compute networking service (nova-network) to the OpenStack Networking service. Over time, operators can build on this minimal architecture to enable more cloud networking features. In general, the OpenStack Networking software components that handle layer-3 operations impact performance and reliability the most. To improve performance and reliability, provider networks move layer-3 operations to the physical network infrastructure. In one particular use case, the OpenStack deployment resides in a mixed environment with conventional virtualization and bare-metal hosts that use a sizable physical network infrastructure. Applications that run inside the OpenStack deployment might require direct layer-2 access, typically using VLANs, to applications outside of the deployment. Routed provider networks ------------------------ Routed provider networks offer layer-3 connectivity to instances. These networks map to existing layer-3 networks in the data center. More specifically, the network maps to multiple layer-2 segments, each of which is essentially a provider network. Each has a router gateway attached to it which routes traffic between them and externally. The Networking service does not provide the routing. Routed provider networks offer performance at scale that is difficult to achieve with a plain provider network at the expense of guaranteed layer-2 connectivity. See :ref:`config-routed-provider-networks` for more information. .. _intro-os-networking-selfservice: Self-service networks --------------------- Self-service networks primarily enable general (non-privileged) projects to manage networks without involving administrators. These networks are entirely virtual and require virtual routers to interact with provider and external networks such as the Internet. Self-service networks also usually provide DHCP and metadata services to instances. In most cases, self-service networks use overlay protocols such as VXLAN or GRE because they can support many more networks than layer-2 segmentation using VLAN tagging (802.1q). Furthermore, VLANs typically require additional configuration of physical network infrastructure. IPv4 self-service networks typically use private IP address ranges (RFC1918) and interact with provider networks via source NAT on virtual routers. Floating IP addresses enable access to instances from provider networks via destination NAT on virtual routers. IPv6 self-service networks always use public IP address ranges and interact with provider networks via virtual routers with static routes. The Networking service implements routers using a layer-3 agent that typically resides at least one network node. Contrary to provider networks that connect instances to the physical network infrastructure at layer-2, self-service networks must traverse a layer-3 agent. Thus, oversubscription or failure of a layer-3 agent or network node can impact a significant quantity of self-service networks and instances using them. Consider implementing one or more high-availability features to increase redundancy and performance of self-service networks. Users create project networks for connectivity within projects. By default, they are fully isolated and are not shared with other projects. OpenStack Networking supports the following types of network isolation and overlay technologies. Flat All instances reside on the same network, which can also be shared with the hosts. No VLAN tagging or other network segregation takes place. VLAN Networking allows users to create multiple provider or project networks using VLAN IDs (802.1Q tagged) that correspond to VLANs present in the physical network. This allows instances to communicate with each other across the environment. They can also communicate with dedicated servers, firewalls, load balancers, and other networking infrastructure on the same layer 2 VLAN. GRE and VXLAN VXLAN and GRE are encapsulation protocols that create overlay networks to activate and control communication between compute instances. A Networking router is required to allow traffic to flow outside of the GRE or VXLAN project network. A router is also required to connect directly-connected project networks with external networks, including the Internet. The router provides the ability to connect to instances directly from an external network using floating IP addresses. .. image:: figures/NetworkTypes.png :width: 100% :alt: Project and provider networks Subnets ------- A block of IP addresses and associated configuration state. This is also known as the native IPAM (IP Address Management) provided by the networking service for both project and provider networks. Subnets are used to allocate IP addresses when new ports are created on a network. Subnet pools ------------ End users normally can create subnets with any valid IP addresses without other restrictions. However, in some cases, it is nice for the admin or the project to pre-define a pool of addresses from which to create subnets with automatic allocation. Using subnet pools constrains what addresses can be used by requiring that every subnet be within the defined pool. It also prevents address reuse or overlap by two subnets from the same pool. See :ref:`config-subnet-pools` for more information. Ports ----- A port is a connection point for attaching a single device, such as the NIC of a virtual server, to a virtual network. The port also describes the associated network configuration, such as the MAC and IP addresses to be used on that port. Routers ------- Routers provide virtual layer-3 services such as routing and NAT between self-service and provider networks or among self-service networks belonging to a project. The Networking service uses a layer-3 agent to manage routers via namespaces. Security groups --------------- Security groups provide a container for virtual firewall rules that control ingress (inbound to instances) and egress (outbound from instances) network traffic at the port level. Security groups use a default deny policy and only contain rules that allow specific traffic. Each port can reference one or more security groups in an additive fashion. The firewall driver translates security group rules to a configuration for the underlying packet filtering technology such as ``iptables``. Each project contains a ``default`` security group that allows all egress traffic and denies all ingress traffic. You can change the rules in the ``default`` security group. If you launch an instance without specifying a security group, the ``default`` security group automatically applies to it. Similarly, if you create a port without specifying a security group, the ``default`` security group automatically applies to it. .. note:: If you use the metadata service, removing the default egress rules denies access to TCP port 80 on 169.254.169.254, thus preventing instances from retrieving metadata. Security group rules are stateful. Thus, allowing ingress TCP port 22 for secure shell automatically creates rules that allow return egress traffic and ICMP error messages involving those TCP connections. By default, all security groups contain a series of basic (sanity) and anti-spoofing rules that perform the following actions: * Allow egress traffic only if it uses the source MAC and IP addresses of the port for the instance, source MAC and IP combination in ``allowed-address-pairs``, or valid MAC address (port or ``allowed-address-pairs``) and associated EUI64 link-local IPv6 address. * Allow egress DHCP discovery and request messages that use the source MAC address of the port for the instance and the unspecified IPv4 address (0.0.0.0). * Allow ingress DHCP and DHCPv6 responses from the DHCP server on the subnet so instances can acquire IP addresses. * Deny egress DHCP and DHCPv6 responses to prevent instances from acting as DHCP(v6) servers. * Allow ingress/egress ICMPv6 MLD, neighbor solicitation, and neighbor discovery messages so instances can discover neighbors and join multicast groups. * Deny egress ICMPv6 router advertisements to prevent instances from acting as IPv6 routers and forwarding IPv6 traffic for other instances. * Allow egress ICMPv6 MLD reports (v1 and v2) and neighbor solicitation messages that use the source MAC address of a particular instance and the unspecified IPv6 address (::). Duplicate address detection (DAD) relies on these messages. * Allow egress non-IP traffic from the MAC address of the port for the instance and any additional MAC addresses in ``allowed-address-pairs`` on the port for the instance. Although non-IP traffic, security groups do not implicitly allow all ARP traffic. Separate ARP filtering rules prevent instances from using ARP to intercept traffic for another instance. You cannot disable or remove these rules. You can disable security groups including basic and anti-spoofing rules by setting the port attribute ``port_security_enabled`` to ``False``. Extensions ---------- The OpenStack Networking service is extensible. Extensions serve two purposes: they allow the introduction of new features in the API without requiring a version change and they allow the introduction of vendor specific niche functionality. Applications can programmatically list available extensions by performing a GET on the :code:`/extensions` URI. Note that this is a versioned request; that is, an extension available in one API version might not be available in another. DHCP ---- The optional DHCP service manages IP addresses for instances on provider and self-service networks. The Networking service implements the DHCP service using an agent that manages ``qdhcp`` namespaces and the ``dnsmasq`` service. Metadata -------- The optional metadata service provides an API for instances to obtain metadata such as SSH keys. Service and component hierarchy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Server ------ * Provides API, manages database, etc. Plug-ins -------- * Manages agents Agents ------ * Provides layer 2/3 connectivity to instances * Handles physical-virtual network transition * Handles metadata, etc. Layer 2 (Ethernet and Switching) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Linux Bridge * OVS Layer 3 (IP and Routing) ^^^^^^^^^^^^^^^^^^^^^^^^ * L3 * DHCP Miscellaneous ^^^^^^^^^^^^^ * Metadata Services -------- Routing services ^^^^^^^^^^^^^^^^ VPNaaS ^^^^^^ The Virtual Private Network-as-a-Service (VPNaaS) is a neutron extension that introduces the VPN feature set. LBaaS ^^^^^ The Load-Balancer-as-a-Service (LBaaS) API provisions and configures load balancers. The reference implementation is based on the HAProxy software load balancer. FWaaS ^^^^^ The Firewall-as-a-Service (FWaaS) API is an experimental API that enables early adopters and vendors to test their networking implementations. neutron-12.1.1/doc/source/admin/neutron_linuxbridge.rst0000664000175000017500000000204713553660046023300 0ustar zuulzuul00000000000000==================================== neutron-linuxbridge-cleanup utility ==================================== Description ~~~~~~~~~~~ Automated removal of empty bridges has been disabled to fix a race condition between the Compute (nova) and Networking (neutron) services. Previously, it was possible for a bridge to be deleted during the time when the only instance using it was rebooted. Usage ~~~~~ Use this script to remove empty bridges on compute nodes by running the following command: .. code-block:: console $ neutron-linuxbridge-cleanup .. important:: Do not use this tool when creating or migrating an instance as it throws an error when the bridge does not exist. .. note:: Using this script can still trigger the original race condition. Only run this script if you have evacuated all instances off a compute node and you want to clean up the bridges. In addition to evacuating all instances, you should fence off the compute node where you are going to run this script so new instances do not get scheduled on it. neutron-12.1.1/doc/source/install/0000775000175000017500000000000013553660156017035 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/install/compute-install-option1-rdo.rst0000664000175000017500000000401013553660046025051 0ustar zuulzuul00000000000000Networking Option 1: Provider networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Configure the Networking components on a *compute* node. Configure the Linux bridge agent -------------------------------- The Linux bridge agent builds layer-2 (bridging and switching) virtual networking infrastructure for instances and handles security groups. * Edit the ``/etc/neutron/plugins/ml2/linuxbridge_agent.ini`` file and complete the following actions: * In the ``[linux_bridge]`` section, map the provider virtual network to the provider physical network interface: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE_NAME .. end Replace ``PROVIDER_INTERFACE_NAME`` with the name of the underlying provider physical network interface. See :doc:`environment-networking-rdo` for more information. * In the ``[vxlan]`` section, disable VXLAN overlay networks: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [vxlan] enable_vxlan = false .. end * In the ``[securitygroup]`` section, enable security groups and configure the Linux bridge iptables firewall driver: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [securitygroup] # ... enable_security_group = true firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver .. end * Ensure your Linux operating system kernel supports network bridge filters by verifying all the following ``sysctl`` values are set to ``1``: .. code-block:: ini net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables .. end To enable networking bridge support, typically the ``br_netfilter`` kernel module needs to be loaded. Check your operating system's documentation for additional details on enabling this module. Return to *Networking compute node configuration* neutron-12.1.1/doc/source/install/compute-install-option2-rdo.rst0000664000175000017500000000517613553660046025070 0ustar zuulzuul00000000000000Networking Option 2: Self-service networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Configure the Networking components on a *compute* node. Configure the Linux bridge agent -------------------------------- The Linux bridge agent builds layer-2 (bridging and switching) virtual networking infrastructure for instances and handles security groups. * Edit the ``/etc/neutron/plugins/ml2/linuxbridge_agent.ini`` file and complete the following actions: * In the ``[linux_bridge]`` section, map the provider virtual network to the provider physical network interface: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE_NAME .. end Replace ``PROVIDER_INTERFACE_NAME`` with the name of the underlying provider physical network interface. See :doc:`environment-networking-rdo` for more information. * In the ``[vxlan]`` section, enable VXLAN overlay networks, configure the IP address of the physical network interface that handles overlay networks, and enable layer-2 population: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [vxlan] enable_vxlan = true local_ip = OVERLAY_INTERFACE_IP_ADDRESS l2_population = true .. end Replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the IP address of the underlying physical network interface that handles overlay networks. The example architecture uses the management interface to tunnel traffic to the other nodes. Therefore, replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the management IP address of the compute node. See :doc:`environment-networking-rdo` for more information. * In the ``[securitygroup]`` section, enable security groups and configure the Linux bridge iptables firewall driver: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [securitygroup] # ... enable_security_group = true firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver .. end * Ensure your Linux operating system kernel supports network bridge filters by verifying all the following ``sysctl`` values are set to ``1``: .. code-block:: ini net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables .. end To enable networking bridge support, typically the ``br_netfilter`` kernel module needs to be loaded. Check your operating system's documentation for additional details on enabling this module. Return to *Networking compute node configuration*. neutron-12.1.1/doc/source/install/compute-install-rdo.rst0000664000175000017500000000731113553660047023472 0ustar zuulzuul00000000000000Install and configure compute node ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The compute node handles connectivity and security groups for instances. Install the components ---------------------- .. todo: https://bugzilla.redhat.com/show_bug.cgi?id=1334626 .. code-block:: console # yum install openstack-neutron-linuxbridge ebtables ipset .. end Configure the common component ------------------------------ The Networking common component configuration includes the authentication mechanism, message queue, and plug-in. .. include:: shared/note_configuration_vary_by_distribution.rst * Edit the ``/etc/neutron/neutron.conf`` file and complete the following actions: * In the ``[database]`` section, comment out any ``connection`` options because compute nodes do not directly access the database. * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... transport_url = rabbit://openstack:RABBIT_PASS@controller .. end Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` account in RabbitMQ. * In the ``[DEFAULT]`` and ``[keystone_authtoken]`` sections, configure Identity service access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... auth_strategy = keystone [keystone_authtoken] # ... auth_uri = http://controller:5000 auth_url = http://controller:35357 memcached_servers = controller:11211 auth_type = password project_domain_name = default user_domain_name = default project_name = service username = neutron password = NEUTRON_PASS .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. .. note:: Comment out or remove any other options in the ``[keystone_authtoken]`` section. * In the ``[oslo_concurrency]`` section, configure the lock path: .. path /etc/neutron/neutron.conf .. code-block:: ini [oslo_concurrency] # ... lock_path = /var/lib/neutron/tmp .. end Configure networking options ---------------------------- Choose the same networking option that you chose for the controller node to configure services specific to it. Afterwards, return here and proceed to :ref:`neutron-compute-compute-rdo`. .. toctree:: :maxdepth: 1 compute-install-option1-rdo.rst compute-install-option2-rdo.rst .. _neutron-compute-compute-rdo: Configure the Compute service to use the Networking service ----------------------------------------------------------- * Edit the ``/etc/nova/nova.conf`` file and complete the following actions: * In the ``[neutron]`` section, configure access parameters: .. path /etc/nova/nova.conf .. code-block:: ini [neutron] # ... url = http://controller:9696 auth_url = http://controller:35357 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = neutron password = NEUTRON_PASS .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. Finalize installation --------------------- #. Restart the Compute service: .. code-block:: console # systemctl restart openstack-nova-compute.service .. end #. Start the Linux bridge agent and configure it to start when the system boots: .. code-block:: console # systemctl enable neutron-linuxbridge-agent.service # systemctl start neutron-linuxbridge-agent.service .. end neutron-12.1.1/doc/source/install/environment-networking-storage-cinder.rst0000664000175000017500000000105313553660046027221 0ustar zuulzuul00000000000000Block storage node (Optional) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you want to deploy the Block Storage service, configure one additional storage node. Configure network interfaces ---------------------------- * Configure the management interface: * IP address: ``10.0.0.41`` * Network mask: ``255.255.255.0`` (or ``/24``) * Default gateway: ``10.0.0.1`` Configure name resolution ------------------------- #. Set the hostname of the node to ``block1``. #. .. include:: shared/edit_hosts_file.txt #. Reboot the system to activate the changes. neutron-12.1.1/doc/source/install/controller-install-option2-obs.rst0000664000175000017500000002237713553660047025601 0ustar zuulzuul00000000000000Networking Option 2: Self-service networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Install and configure the Networking components on the *controller* node. Install the components ---------------------- .. code-block:: console # zypper install --no-recommends openstack-neutron \ openstack-neutron-server openstack-neutron-linuxbridge-agent \ openstack-neutron-l3-agent openstack-neutron-dhcp-agent \ openstack-neutron-metadata-agent bridge-utils .. end Configure the server component ------------------------------ * Edit the ``/etc/neutron/neutron.conf`` file and complete the following actions: * In the ``[database]`` section, configure database access: .. path /etc/neutron/neutron.conf .. code-block:: ini [database] # ... connection = mysql+pymysql://neutron:NEUTRON_DBPASS@controller/neutron .. end Replace ``NEUTRON_DBPASS`` with the password you chose for the database. .. note:: Comment out or remove any other ``connection`` options in the ``[database]`` section. * In the ``[DEFAULT]`` section, enable the Modular Layer 2 (ML2) plug-in, router service, and overlapping IP addresses: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... core_plugin = ml2 service_plugins = router allow_overlapping_ips = true .. end * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... transport_url = rabbit://openstack:RABBIT_PASS@controller .. end Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` account in RabbitMQ. * In the ``[DEFAULT]`` and ``[keystone_authtoken]`` sections, configure Identity service access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... auth_strategy = keystone [keystone_authtoken] # ... auth_uri = http://controller:5000 auth_url = http://controller:35357 memcached_servers = controller:11211 auth_type = password project_domain_name = default user_domain_name = default project_name = service username = neutron password = NEUTRON_PASS .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. .. note:: Comment out or remove any other options in the ``[keystone_authtoken]`` section. * In the ``[DEFAULT]`` and ``[nova]`` sections, configure Networking to notify Compute of network topology changes: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... notify_nova_on_port_status_changes = true notify_nova_on_port_data_changes = true [nova] # ... auth_url = http://controller:35357 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = nova password = NOVA_PASS .. end Replace ``NOVA_PASS`` with the password you chose for the ``nova`` user in the Identity service. * In the ``[oslo_concurrency]`` section, configure the lock path: .. path /etc/neutron/neutron.conf .. code-block:: ini [oslo_concurrency] # ... lock_path = /var/lib/neutron/tmp .. end Configure the Modular Layer 2 (ML2) plug-in ------------------------------------------- The ML2 plug-in uses the Linux bridge mechanism to build layer-2 (bridging and switching) virtual networking infrastructure for instances. * Edit the ``/etc/neutron/plugins/ml2/ml2_conf.ini`` file and complete the following actions: * In the ``[ml2]`` section, enable flat, VLAN, and VXLAN networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... type_drivers = flat,vlan,vxlan .. end * In the ``[ml2]`` section, enable VXLAN self-service networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... tenant_network_types = vxlan .. end * In the ``[ml2]`` section, enable the Linux bridge and layer-2 population mechanisms: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... mechanism_drivers = linuxbridge,l2population .. end .. warning:: After you configure the ML2 plug-in, removing values in the ``type_drivers`` option can lead to database inconsistency. .. note:: The Linux bridge agent only supports VXLAN overlay networks. * In the ``[ml2]`` section, enable the port security extension driver: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... extension_drivers = port_security .. end * In the ``[ml2_type_flat]`` section, configure the provider virtual network as a flat network: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2_type_flat] # ... flat_networks = provider .. end * In the ``[ml2_type_vxlan]`` section, configure the VXLAN network identifier range for self-service networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2_type_vxlan] # ... vni_ranges = 1:1000 .. end * In the ``[securitygroup]`` section, enable ipset to increase efficiency of security group rules: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [securitygroup] # ... enable_ipset = true .. end Configure the Linux bridge agent -------------------------------- The Linux bridge agent builds layer-2 (bridging and switching) virtual networking infrastructure for instances and handles security groups. * Edit the ``/etc/neutron/plugins/ml2/linuxbridge_agent.ini`` file and complete the following actions: * In the ``[linux_bridge]`` section, map the provider virtual network to the provider physical network interface: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE_NAME .. end Replace ``PROVIDER_INTERFACE_NAME`` with the name of the underlying provider physical network interface. See :doc:`environment-networking-obs` for more information. * In the ``[vxlan]`` section, enable VXLAN overlay networks, configure the IP address of the physical network interface that handles overlay networks, and enable layer-2 population: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [vxlan] enable_vxlan = true local_ip = OVERLAY_INTERFACE_IP_ADDRESS l2_population = true .. end Replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the IP address of the underlying physical network interface that handles overlay networks. The example architecture uses the management interface to tunnel traffic to the other nodes. Therefore, replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the management IP address of the controller node. See :doc:`environment-networking-obs` for more information. * In the ``[securitygroup]`` section, enable security groups and configure the Linux bridge iptables firewall driver: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [securitygroup] # ... enable_security_group = true firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver .. end * Ensure your Linux operating system kernel supports network bridge filters by verifying all the following ``sysctl`` values are set to ``1``: .. code-block:: ini net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables .. end To enable networking bridge support, typically the ``br_netfilter`` kernel module needs to be loaded. Check your operating system's documentation for additional details on enabling this module. Configure the layer-3 agent --------------------------- The Layer-3 (L3) agent provides routing and NAT services for self-service virtual networks. * Edit the ``/etc/neutron/l3_agent.ini`` file and complete the following actions: * In the ``[DEFAULT]`` section, configure the Linux bridge interface driver and external network bridge: .. path /etc/neutron/l3_agent.ini .. code-block:: ini [DEFAULT] # ... interface_driver = linuxbridge .. end Configure the DHCP agent ------------------------ The DHCP agent provides DHCP services for virtual networks. * Edit the ``/etc/neutron/dhcp_agent.ini`` file and complete the following actions: * In the ``[DEFAULT]`` section, configure the Linux bridge interface driver, Dnsmasq DHCP driver, and enable isolated metadata so instances on provider networks can access metadata over the network: .. path /etc/neutron/dhcp_agent.ini .. code-block:: ini [DEFAULT] # ... interface_driver = linuxbridge dhcp_driver = neutron.agent.linux.dhcp.Dnsmasq enable_isolated_metadata = true .. end Return to *Networking controller node configuration*. neutron-12.1.1/doc/source/install/overview.rst0000664000175000017500000001525413553660047021443 0ustar zuulzuul00000000000000======== Overview ======== The OpenStack project is an open source cloud computing platform that supports all types of cloud environments. The project aims for simple implementation, massive scalability, and a rich set of features. Cloud computing experts from around the world contribute to the project. OpenStack provides an Infrastructure-as-a-Service (IaaS) solution through a variety of complementary services. Each service offers an Application Programming Interface (API) that facilitates this integration. This guide covers step-by-step deployment of the major OpenStack services using a functional example architecture suitable for new users of OpenStack with sufficient Linux experience. This guide is not intended to be used for production system installations, but to create a minimum proof-of-concept for the purpose of learning about OpenStack. After becoming familiar with basic installation, configuration, operation, and troubleshooting of these OpenStack services, you should consider the following steps toward deployment using a production architecture: * Determine and implement the necessary core and optional services to meet performance and redundancy requirements. * Increase security using methods such as firewalls, encryption, and service policies. * Implement a deployment tool such as Ansible, Chef, Puppet, or Salt to automate deployment and management of the production environment. .. _overview-example-architectures: Example architecture ~~~~~~~~~~~~~~~~~~~~ The example architecture requires at least two nodes (hosts) to launch a basic virtual machine (VM) or instance. Optional services such as Block Storage and Object Storage require additional nodes. .. important:: The example architecture used in this guide is a minimum configuration, and is not intended for production system installations. It is designed to provide a minimum proof-of-concept for the purpose of learning about OpenStack. For information on creating architectures for specific use cases, or how to determine which architecture is required, see the `Architecture Design Guide `_. This example architecture differs from a minimal production architecture as follows: * Networking agents reside on the controller node instead of one or more dedicated network nodes. * Overlay (tunnel) traffic for self-service networks traverses the management network instead of a dedicated network. For more information on production architectures, see the `Architecture Design Guide `_, `OpenStack Operations Guide `_, and :doc:`OpenStack Networking Guide `. .. _figure-hwreqs: .. figure:: figures/hwreqs.png :alt: Hardware requirements **Hardware requirements** Controller ---------- The controller node runs the Identity service, Image service, management portions of Compute, management portion of Networking, various Networking agents, and the Dashboard. It also includes supporting services such as an SQL database, message queue, and Network Time Protocol (NTP). Optionally, the controller node runs portions of the Block Storage, Object Storage, Orchestration, and Telemetry services. The controller node requires a minimum of two network interfaces. Compute ------- The compute node runs the hypervisor portion of Compute that operates instances. By default, Compute uses the kernel-based VM (KVM) hypervisor. The compute node also runs a Networking service agent that connects instances to virtual networks and provides firewalling services to instances via security groups. You can deploy more than one compute node. Each node requires a minimum of two network interfaces. Block Storage ------------- The optional Block Storage node contains the disks that the Block Storage and Shared File System services provision for instances. For simplicity, service traffic between compute nodes and this node uses the management network. Production environments should implement a separate storage network to increase performance and security. You can deploy more than one block storage node. Each node requires a minimum of one network interface. Object Storage -------------- The optional Object Storage node contain the disks that the Object Storage service uses for storing accounts, containers, and objects. For simplicity, service traffic between compute nodes and this node uses the management network. Production environments should implement a separate storage network to increase performance and security. This service requires two nodes. Each node requires a minimum of one network interface. You can deploy more than two object storage nodes. Networking ~~~~~~~~~~ Choose one of the following virtual networking options. .. _network1: Networking Option 1: Provider networks -------------------------------------- The provider networks option deploys the OpenStack Networking service in the simplest way possible with primarily layer-2 (bridging/switching) services and VLAN segmentation of networks. Essentially, it bridges virtual networks to physical networks and relies on physical network infrastructure for layer-3 (routing) services. Additionally, a DHCP`_. All nodes require Internet access for administrative purposes such as package installation, security updates, Domain Name System (DNS), and Network Time Protocol (NTP). In most cases, nodes should obtain Internet access through the management network interface. To highlight the importance of network separation, the example architectures use `private address space `__ for the management network and assume that the physical network infrastructure provides Internet access via Network Address Translation (NAT) or other methods. The example architectures use routable IP address space for the provider (external) network and assume that the physical network infrastructure provides direct Internet access. In the provider networks architecture, all instances attach directly to the provider network. In the self-service (private) networks architecture, instances can attach to a self-service or provider network. Self-service networks can reside entirely within OpenStack or provide some level of external network access using Network Address Translation (NAT) through the provider network. .. _figure-networklayout: .. figure:: figures/networklayout.png :alt: Network layout The example architectures assume use of the following networks: * Management on 10.0.0.0/24 with gateway 10.0.0.1 This network requires a gateway to provide Internet access to all nodes for administrative purposes such as package installation, security updates, Domain Name System (DNS), and Network Time Protocol (NTP). * Provider on 203.0.113.0/24 with gateway 203.0.113.1 This network requires a gateway to provide Internet access to instances in your OpenStack environment. You can modify these ranges and gateways to work with your particular network infrastructure. Network interface names vary by distribution. Traditionally, interfaces use ``eth`` followed by a sequential number. To cover all variations, this guide refers to the first interface as the interface with the lowest number and the second interface as the interface with the highest number. Unless you intend to use the exact configuration provided in this example architecture, you must modify the networks in this procedure to match your environment. Each node must resolve the other nodes by name in addition to IP address. For example, the ``controller`` name must resolve to ``10.0.0.11``, the IP address of the management interface on the controller node. .. warning:: Reconfiguring network interfaces will interrupt network connectivity. We recommend using a local terminal session for these procedures. .. note:: Your distribution does not enable a restrictive firewall by default. For more information about securing your environment, refer to the `OpenStack Security Guide `_. .. toctree:: :maxdepth: 1 environment-networking-controller-ubuntu.rst environment-networking-compute-ubuntu.rst environment-networking-storage-cinder.rst environment-networking-verify-ubuntu.rst neutron-12.1.1/doc/source/install/controller-install-option2-rdo.rst0000664000175000017500000002217013553660047025571 0ustar zuulzuul00000000000000Networking Option 2: Self-service networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Install and configure the Networking components on the *controller* node. Install the components ---------------------- .. code-block:: console # yum install openstack-neutron openstack-neutron-ml2 \ openstack-neutron-linuxbridge ebtables .. end Configure the server component ------------------------------ * Edit the ``/etc/neutron/neutron.conf`` file and complete the following actions: * In the ``[database]`` section, configure database access: .. path /etc/neutron/neutron.conf .. code-block:: ini [database] # ... connection = mysql+pymysql://neutron:NEUTRON_DBPASS@controller/neutron .. end Replace ``NEUTRON_DBPASS`` with the password you chose for the database. .. note:: Comment out or remove any other ``connection`` options in the ``[database]`` section. * In the ``[DEFAULT]`` section, enable the Modular Layer 2 (ML2) plug-in, router service, and overlapping IP addresses: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... core_plugin = ml2 service_plugins = router allow_overlapping_ips = true .. end * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... transport_url = rabbit://openstack:RABBIT_PASS@controller .. end Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` account in RabbitMQ. * In the ``[DEFAULT]`` and ``[keystone_authtoken]`` sections, configure Identity service access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... auth_strategy = keystone [keystone_authtoken] # ... auth_uri = http://controller:5000 auth_url = http://controller:35357 memcached_servers = controller:11211 auth_type = password project_domain_name = default user_domain_name = default project_name = service username = neutron password = NEUTRON_PASS .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. .. note:: Comment out or remove any other options in the ``[keystone_authtoken]`` section. * In the ``[DEFAULT]`` and ``[nova]`` sections, configure Networking to notify Compute of network topology changes: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... notify_nova_on_port_status_changes = true notify_nova_on_port_data_changes = true [nova] # ... auth_url = http://controller:35357 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = nova password = NOVA_PASS .. end Replace ``NOVA_PASS`` with the password you chose for the ``nova`` user in the Identity service. * In the ``[oslo_concurrency]`` section, configure the lock path: .. path /etc/neutron/neutron.conf .. code-block:: ini [oslo_concurrency] # ... lock_path = /var/lib/neutron/tmp .. end Configure the Modular Layer 2 (ML2) plug-in ------------------------------------------- The ML2 plug-in uses the Linux bridge mechanism to build layer-2 (bridging and switching) virtual networking infrastructure for instances. * Edit the ``/etc/neutron/plugins/ml2/ml2_conf.ini`` file and complete the following actions: * In the ``[ml2]`` section, enable flat, VLAN, and VXLAN networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... type_drivers = flat,vlan,vxlan .. end * In the ``[ml2]`` section, enable VXLAN self-service networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... tenant_network_types = vxlan .. end * In the ``[ml2]`` section, enable the Linux bridge and layer-2 population mechanisms: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... mechanism_drivers = linuxbridge,l2population .. end .. warning:: After you configure the ML2 plug-in, removing values in the ``type_drivers`` option can lead to database inconsistency. .. note:: The Linux bridge agent only supports VXLAN overlay networks. * In the ``[ml2]`` section, enable the port security extension driver: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... extension_drivers = port_security .. end * In the ``[ml2_type_flat]`` section, configure the provider virtual network as a flat network: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2_type_flat] # ... flat_networks = provider .. end * In the ``[ml2_type_vxlan]`` section, configure the VXLAN network identifier range for self-service networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2_type_vxlan] # ... vni_ranges = 1:1000 .. end * In the ``[securitygroup]`` section, enable ipset to increase efficiency of security group rules: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [securitygroup] # ... enable_ipset = true .. end Configure the Linux bridge agent -------------------------------- The Linux bridge agent builds layer-2 (bridging and switching) virtual networking infrastructure for instances and handles security groups. * Edit the ``/etc/neutron/plugins/ml2/linuxbridge_agent.ini`` file and complete the following actions: * In the ``[linux_bridge]`` section, map the provider virtual network to the provider physical network interface: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE_NAME .. end Replace ``PROVIDER_INTERFACE_NAME`` with the name of the underlying provider physical network interface. See :doc:`environment-networking-rdo` for more information. * In the ``[vxlan]`` section, enable VXLAN overlay networks, configure the IP address of the physical network interface that handles overlay networks, and enable layer-2 population: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [vxlan] enable_vxlan = true local_ip = OVERLAY_INTERFACE_IP_ADDRESS l2_population = true .. end Replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the IP address of the underlying physical network interface that handles overlay networks. The example architecture uses the management interface to tunnel traffic to the other nodes. Therefore, replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the management IP address of the controller node. See :doc:`environment-networking-rdo` for more information. * In the ``[securitygroup]`` section, enable security groups and configure the Linux bridge iptables firewall driver: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [securitygroup] # ... enable_security_group = true firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver .. end * Ensure your Linux operating system kernel supports network bridge filters by verifying all the following ``sysctl`` values are set to ``1``: .. code-block:: ini net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables .. end To enable networking bridge support, typically the ``br_netfilter`` kernel module needs to be loaded. Check your operating system's documentation for additional details on enabling this module. Configure the layer-3 agent --------------------------- The Layer-3 (L3) agent provides routing and NAT services for self-service virtual networks. * Edit the ``/etc/neutron/l3_agent.ini`` file and complete the following actions: * In the ``[DEFAULT]`` section, configure the Linux bridge interface driver and external network bridge: .. path /etc/neutron/l3_agent.ini .. code-block:: ini [DEFAULT] # ... interface_driver = linuxbridge .. end Configure the DHCP agent ------------------------ The DHCP agent provides DHCP services for virtual networks. * Edit the ``/etc/neutron/dhcp_agent.ini`` file and complete the following actions: * In the ``[DEFAULT]`` section, configure the Linux bridge interface driver, Dnsmasq DHCP driver, and enable isolated metadata so instances on provider networks can access metadata over the network: .. path /etc/neutron/dhcp_agent.ini .. code-block:: ini [DEFAULT] # ... interface_driver = linuxbridge dhcp_driver = neutron.agent.linux.dhcp.Dnsmasq enable_isolated_metadata = true .. end Return to *Networking controller node configuration*. neutron-12.1.1/doc/source/install/environment-networking-verify-obs.rst0000664000175000017500000000611113553660046026400 0ustar zuulzuul00000000000000Verify connectivity ------------------- We recommend that you verify network connectivity to the Internet and among the nodes before proceeding further. #. From the *controller* node, test access to the Internet: .. code-block:: console # ping -c 4 openstack.org PING openstack.org (174.143.194.225) 56(84) bytes of data. 64 bytes from 174.143.194.225: icmp_seq=1 ttl=54 time=18.3 ms 64 bytes from 174.143.194.225: icmp_seq=2 ttl=54 time=17.5 ms 64 bytes from 174.143.194.225: icmp_seq=3 ttl=54 time=17.5 ms 64 bytes from 174.143.194.225: icmp_seq=4 ttl=54 time=17.4 ms --- openstack.org ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3022ms rtt min/avg/max/mdev = 17.489/17.715/18.346/0.364 ms .. end #. From the *controller* node, test access to the management interface on the *compute* node: .. code-block:: console # ping -c 4 compute1 PING compute1 (10.0.0.31) 56(84) bytes of data. 64 bytes from compute1 (10.0.0.31): icmp_seq=1 ttl=64 time=0.263 ms 64 bytes from compute1 (10.0.0.31): icmp_seq=2 ttl=64 time=0.202 ms 64 bytes from compute1 (10.0.0.31): icmp_seq=3 ttl=64 time=0.203 ms 64 bytes from compute1 (10.0.0.31): icmp_seq=4 ttl=64 time=0.202 ms --- compute1 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3000ms rtt min/avg/max/mdev = 0.202/0.217/0.263/0.030 ms .. end #. From the *compute* node, test access to the Internet: .. code-block:: console # ping -c 4 openstack.org PING openstack.org (174.143.194.225) 56(84) bytes of data. 64 bytes from 174.143.194.225: icmp_seq=1 ttl=54 time=18.3 ms 64 bytes from 174.143.194.225: icmp_seq=2 ttl=54 time=17.5 ms 64 bytes from 174.143.194.225: icmp_seq=3 ttl=54 time=17.5 ms 64 bytes from 174.143.194.225: icmp_seq=4 ttl=54 time=17.4 ms --- openstack.org ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3022ms rtt min/avg/max/mdev = 17.489/17.715/18.346/0.364 ms .. end #. From the *compute* node, test access to the management interface on the *controller* node: .. code-block:: console # ping -c 4 controller PING controller (10.0.0.11) 56(84) bytes of data. 64 bytes from controller (10.0.0.11): icmp_seq=1 ttl=64 time=0.263 ms 64 bytes from controller (10.0.0.11): icmp_seq=2 ttl=64 time=0.202 ms 64 bytes from controller (10.0.0.11): icmp_seq=3 ttl=64 time=0.203 ms 64 bytes from controller (10.0.0.11): icmp_seq=4 ttl=64 time=0.202 ms --- controller ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3000ms rtt min/avg/max/mdev = 0.202/0.217/0.263/0.030 ms .. end .. note:: Your distribution enables a restrictive firewall by default. During the installation process, certain steps will fail unless you alter or disable the firewall. For more information about securing your environment, refer to the `OpenStack Security Guide `_. neutron-12.1.1/doc/source/install/controller-install-option1-ubuntu.rst0000664000175000017500000001745513553660047026340 0ustar zuulzuul00000000000000Networking Option 1: Provider networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Install and configure the Networking components on the *controller* node. Install the components ---------------------- .. code-block:: console # apt install neutron-server neutron-plugin-ml2 \ neutron-linuxbridge-agent neutron-dhcp-agent \ neutron-metadata-agent .. end Configure the server component ------------------------------ The Networking server component configuration includes the database, authentication mechanism, message queue, topology change notifications, and plug-in. .. include:: shared/note_configuration_vary_by_distribution.rst * Edit the ``/etc/neutron/neutron.conf`` file and complete the following actions: * In the ``[database]`` section, configure database access: .. path /etc/neutron/neutron.conf .. code-block:: ini [database] # ... connection = mysql+pymysql://neutron:NEUTRON_DBPASS@controller/neutron .. end Replace ``NEUTRON_DBPASS`` with the password you chose for the database. .. note:: Comment out or remove any other ``connection`` options in the ``[database]`` section. * In the ``[DEFAULT]`` section, enable the Modular Layer 2 (ML2) plug-in and disable additional plug-ins: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... core_plugin = ml2 service_plugins = .. end * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... transport_url = rabbit://openstack:RABBIT_PASS@controller .. end Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` account in RabbitMQ. * In the ``[DEFAULT]`` and ``[keystone_authtoken]`` sections, configure Identity service access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... auth_strategy = keystone [keystone_authtoken] # ... auth_uri = http://controller:5000 auth_url = http://controller:5000 memcached_servers = controller:11211 auth_type = password project_domain_name = default user_domain_name = default project_name = service username = neutron password = NEUTRON_PASS .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. .. note:: Comment out or remove any other options in the ``[keystone_authtoken]`` section. * In the ``[DEFAULT]`` and ``[nova]`` sections, configure Networking to notify Compute of network topology changes: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... notify_nova_on_port_status_changes = true notify_nova_on_port_data_changes = true [nova] # ... auth_url = http://controller:5000 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = nova password = NOVA_PASS .. end Replace ``NOVA_PASS`` with the password you chose for the ``nova`` user in the Identity service. * In the ``[oslo_concurrency]`` section, configure the lock path: .. path /etc/neutron/neutron.conf .. code-block:: ini [oslo_concurrency] # ... lock_path = /var/lib/neutron/tmp .. end Configure the Modular Layer 2 (ML2) plug-in ------------------------------------------- The ML2 plug-in uses the Linux bridge mechanism to build layer-2 (bridging and switching) virtual networking infrastructure for instances. * Edit the ``/etc/neutron/plugins/ml2/ml2_conf.ini`` file and complete the following actions: * In the ``[ml2]`` section, enable flat and VLAN networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... type_drivers = flat,vlan .. end * In the ``[ml2]`` section, disable self-service networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... tenant_network_types = .. end * In the ``[ml2]`` section, enable the Linux bridge mechanism: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... mechanism_drivers = linuxbridge .. end .. warning:: After you configure the ML2 plug-in, removing values in the ``type_drivers`` option can lead to database inconsistency. * In the ``[ml2]`` section, enable the port security extension driver: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... extension_drivers = port_security .. end * In the ``[ml2_type_flat]`` section, configure the provider virtual network as a flat network: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2_type_flat] # ... flat_networks = provider .. end * In the ``[securitygroup]`` section, enable ipset to increase efficiency of security group rules: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [securitygroup] # ... enable_ipset = true .. end Configure the Linux bridge agent -------------------------------- The Linux bridge agent builds layer-2 (bridging and switching) virtual networking infrastructure for instances and handles security groups. * Edit the ``/etc/neutron/plugins/ml2/linuxbridge_agent.ini`` file and complete the following actions: * In the ``[linux_bridge]`` section, map the provider virtual network to the provider physical network interface: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE_NAME .. end Replace ``PROVIDER_INTERFACE_NAME`` with the name of the underlying provider physical network interface. See :doc:`environment-networking-ubuntu` for more information. * In the ``[vxlan]`` section, disable VXLAN overlay networks: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [vxlan] enable_vxlan = false .. end * In the ``[securitygroup]`` section, enable security groups and configure the Linux bridge iptables firewall driver: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [securitygroup] # ... enable_security_group = true firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver .. end * Ensure your Linux operating system kernel supports network bridge filters by verifying all the following ``sysctl`` values are set to ``1``: .. code-block:: ini net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables .. end To enable networking bridge support, typically the ``br_netfilter`` kernel module needs to be loaded. Check your operating system's documentation for additional details on enabling this module. Configure the DHCP agent ------------------------ The DHCP agent provides DHCP services for virtual networks. * Edit the ``/etc/neutron/dhcp_agent.ini`` file and complete the following actions: * In the ``[DEFAULT]`` section, configure the Linux bridge interface driver, Dnsmasq DHCP driver, and enable isolated metadata so instances on provider networks can access metadata over the network: .. path /etc/neutron/dhcp_agent.ini .. code-block:: ini [DEFAULT] # ... interface_driver = linuxbridge dhcp_driver = neutron.agent.linux.dhcp.Dnsmasq enable_isolated_metadata = true .. end Return to *Networking controller node configuration*. neutron-12.1.1/doc/source/install/compute-install-ubuntu.rst0000664000175000017500000000676613553660047024245 0ustar zuulzuul00000000000000Install and configure compute node ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The compute node handles connectivity and security groups for instances. Install the components ---------------------- .. code-block:: console # apt install neutron-linuxbridge-agent .. end Configure the common component ------------------------------ The Networking common component configuration includes the authentication mechanism, message queue, and plug-in. .. include:: shared/note_configuration_vary_by_distribution.rst * Edit the ``/etc/neutron/neutron.conf`` file and complete the following actions: * In the ``[database]`` section, comment out any ``connection`` options because compute nodes do not directly access the database. * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... transport_url = rabbit://openstack:RABBIT_PASS@controller .. end Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` account in RabbitMQ. * In the ``[DEFAULT]`` and ``[keystone_authtoken]`` sections, configure Identity service access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... auth_strategy = keystone [keystone_authtoken] # ... auth_uri = http://controller:5000 auth_url = http://controller:5000 memcached_servers = controller:11211 auth_type = password project_domain_name = default user_domain_name = default project_name = service username = neutron password = NEUTRON_PASS .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. .. note:: Comment out or remove any other options in the ``[keystone_authtoken]`` section. * In the ``[oslo_concurrency]`` section, configure the lock path: .. path /etc/neutron/neutron.conf .. code-block:: ini [oslo_concurrency] # ... lock_path = /var/lib/neutron/tmp .. end Configure networking options ---------------------------- Choose the same networking option that you chose for the controller node to configure services specific to it. Afterwards, return here and proceed to :ref:`neutron-compute-compute-ubuntu`. .. toctree:: :maxdepth: 1 compute-install-option1-ubuntu.rst compute-install-option2-ubuntu.rst .. _neutron-compute-compute-ubuntu: Configure the Compute service to use the Networking service ----------------------------------------------------------- * Edit the ``/etc/nova/nova.conf`` file and complete the following actions: * In the ``[neutron]`` section, configure access parameters: .. path /etc/nova/nova.conf .. code-block:: ini [neutron] # ... url = http://controller:9696 auth_url = http://controller:5000 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = neutron password = NEUTRON_PASS .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. Finalize installation --------------------- #. Restart the Compute service: .. code-block:: console # service nova-compute restart .. end #. Restart the Linux bridge agent: .. code-block:: console # service neutron-linuxbridge-agent restart .. end neutron-12.1.1/doc/source/install/compute-install-option1-obs.rst0000664000175000017500000000401013553660046025050 0ustar zuulzuul00000000000000Networking Option 1: Provider networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Configure the Networking components on a *compute* node. Configure the Linux bridge agent -------------------------------- The Linux bridge agent builds layer-2 (bridging and switching) virtual networking infrastructure for instances and handles security groups. * Edit the ``/etc/neutron/plugins/ml2/linuxbridge_agent.ini`` file and complete the following actions: * In the ``[linux_bridge]`` section, map the provider virtual network to the provider physical network interface: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE_NAME .. end Replace ``PROVIDER_INTERFACE_NAME`` with the name of the underlying provider physical network interface. See :doc:`environment-networking-obs` for more information. * In the ``[vxlan]`` section, disable VXLAN overlay networks: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [vxlan] enable_vxlan = false .. end * In the ``[securitygroup]`` section, enable security groups and configure the Linux bridge iptables firewall driver: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [securitygroup] # ... enable_security_group = true firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver .. end * Ensure your Linux operating system kernel supports network bridge filters by verifying all the following ``sysctl`` values are set to ``1``: .. code-block:: ini net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables .. end To enable networking bridge support, typically the ``br_netfilter`` kernel module needs to be loaded. Check your operating system's documentation for additional details on enabling this module. Return to *Networking compute node configuration* neutron-12.1.1/doc/source/install/environment-networking-controller-obs.rst0000664000175000017500000000164713553660046027270 0ustar zuulzuul00000000000000Controller node ~~~~~~~~~~~~~~~ Configure network interfaces ---------------------------- #. Configure the first interface as the management interface: IP address: 10.0.0.11 Network mask: 255.255.255.0 (or /24) Default gateway: 10.0.0.1 #. The provider interface uses a special configuration without an IP address assigned to it. Configure the second interface as the provider interface: Replace ``INTERFACE_NAME`` with the actual interface name. For example, *eth1* or *ens224*. * Edit the ``/etc/sysconfig/network/ifcfg-INTERFACE_NAME`` file to contain the following: .. path /etc/sysconfig/network/ifcfg-INTERFACE_NAME .. code-block:: ini STARTMODE='auto' BOOTPROTO='static' .. end #. Reboot the system to activate the changes. Configure name resolution ------------------------- #. Set the hostname of the node to ``controller``. #. .. include:: shared/edit_hosts_file.txt neutron-12.1.1/doc/source/install/compute-install-option2-obs.rst0000664000175000017500000000517613553660046025067 0ustar zuulzuul00000000000000Networking Option 2: Self-service networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Configure the Networking components on a *compute* node. Configure the Linux bridge agent -------------------------------- The Linux bridge agent builds layer-2 (bridging and switching) virtual networking infrastructure for instances and handles security groups. * Edit the ``/etc/neutron/plugins/ml2/linuxbridge_agent.ini`` file and complete the following actions: * In the ``[linux_bridge]`` section, map the provider virtual network to the provider physical network interface: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE_NAME .. end Replace ``PROVIDER_INTERFACE_NAME`` with the name of the underlying provider physical network interface. See :doc:`environment-networking-obs` for more information. * In the ``[vxlan]`` section, enable VXLAN overlay networks, configure the IP address of the physical network interface that handles overlay networks, and enable layer-2 population: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [vxlan] enable_vxlan = true local_ip = OVERLAY_INTERFACE_IP_ADDRESS l2_population = true .. end Replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the IP address of the underlying physical network interface that handles overlay networks. The example architecture uses the management interface to tunnel traffic to the other nodes. Therefore, replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the management IP address of the compute node. See :doc:`environment-networking-obs` for more information. * In the ``[securitygroup]`` section, enable security groups and configure the Linux bridge iptables firewall driver: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [securitygroup] # ... enable_security_group = true firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver .. end * Ensure your Linux operating system kernel supports network bridge filters by verifying all the following ``sysctl`` values are set to ``1``: .. code-block:: ini net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables .. end To enable networking bridge support, typically the ``br_netfilter`` kernel module needs to be loaded. Check your operating system's documentation for additional details on enabling this module. Return to *Networking compute node configuration*. neutron-12.1.1/doc/source/install/verify-option2.rst0000664000175000017500000000313213553660046022460 0ustar zuulzuul00000000000000Networking Option 2: Self-service networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * List agents to verify successful launch of the neutron agents: .. code-block:: console $ openstack network agent list +--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+ | f49a4b81-afd6-4b3d-b923-66c8f0517099 | Metadata agent | controller | None | True | UP | neutron-metadata-agent | | 27eee952-a748-467b-bf71-941e89846a92 | Linux bridge agent | controller | None | True | UP | neutron-linuxbridge-agent | | 08905043-5010-4b87-bba5-aedb1956e27a | Linux bridge agent | compute1 | None | True | UP | neutron-linuxbridge-agent | | 830344ff-dc36-4956-84f4-067af667a0dc | L3 agent | controller | nova | True | UP | neutron-l3-agent | | dd3644c9-1a3a-435a-9282-eb306b4b0391 | DHCP agent | controller | nova | True | UP | neutron-dhcp-agent | +--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+ .. end The output should indicate four agents on the controller node and one agent on each compute node. neutron-12.1.1/doc/source/install/compute-install-option1-ubuntu.rst0000664000175000017500000000401313553660046025612 0ustar zuulzuul00000000000000Networking Option 1: Provider networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Configure the Networking components on a *compute* node. Configure the Linux bridge agent -------------------------------- The Linux bridge agent builds layer-2 (bridging and switching) virtual networking infrastructure for instances and handles security groups. * Edit the ``/etc/neutron/plugins/ml2/linuxbridge_agent.ini`` file and complete the following actions: * In the ``[linux_bridge]`` section, map the provider virtual network to the provider physical network interface: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE_NAME .. end Replace ``PROVIDER_INTERFACE_NAME`` with the name of the underlying provider physical network interface. See :doc:`environment-networking-ubuntu` for more information. * In the ``[vxlan]`` section, disable VXLAN overlay networks: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [vxlan] enable_vxlan = false .. end * In the ``[securitygroup]`` section, enable security groups and configure the Linux bridge iptables firewall driver: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [securitygroup] # ... enable_security_group = true firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver .. end * Ensure your Linux operating system kernel supports network bridge filters by verifying all the following ``sysctl`` values are set to ``1``: .. code-block:: ini net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables .. end To enable networking bridge support, typically the ``br_netfilter`` kernel module needs to be loaded. Check your operating system's documentation for additional details on enabling this module. Return to *Networking compute node configuration* neutron-12.1.1/doc/source/install/controller-install-option2-ubuntu.rst0000664000175000017500000002225313553660047026331 0ustar zuulzuul00000000000000Networking Option 2: Self-service networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Install and configure the Networking components on the *controller* node. Install the components ---------------------- .. code-block:: console # apt install neutron-server neutron-plugin-ml2 \ neutron-linuxbridge-agent neutron-l3-agent neutron-dhcp-agent \ neutron-metadata-agent .. end Configure the server component ------------------------------ * Edit the ``/etc/neutron/neutron.conf`` file and complete the following actions: * In the ``[database]`` section, configure database access: .. path /etc/neutron/neutron.conf .. code-block:: ini [database] # ... connection = mysql+pymysql://neutron:NEUTRON_DBPASS@controller/neutron .. end Replace ``NEUTRON_DBPASS`` with the password you chose for the database. .. note:: Comment out or remove any other ``connection`` options in the ``[database]`` section. * In the ``[DEFAULT]`` section, enable the Modular Layer 2 (ML2) plug-in, router service, and overlapping IP addresses: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... core_plugin = ml2 service_plugins = router allow_overlapping_ips = true .. end * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... transport_url = rabbit://openstack:RABBIT_PASS@controller .. end Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` account in RabbitMQ. * In the ``[DEFAULT]`` and ``[keystone_authtoken]`` sections, configure Identity service access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... auth_strategy = keystone [keystone_authtoken] # ... auth_uri = http://controller:5000 auth_url = http://controller:5000 memcached_servers = controller:11211 auth_type = password project_domain_name = default user_domain_name = default project_name = service username = neutron password = NEUTRON_PASS .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. .. note:: Comment out or remove any other options in the ``[keystone_authtoken]`` section. * In the ``[DEFAULT]`` and ``[nova]`` sections, configure Networking to notify Compute of network topology changes: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... notify_nova_on_port_status_changes = true notify_nova_on_port_data_changes = true [nova] # ... auth_url = http://controller:5000 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = nova password = NOVA_PASS .. end Replace ``NOVA_PASS`` with the password you chose for the ``nova`` user in the Identity service. * In the ``[oslo_concurrency]`` section, configure the lock path: .. path /etc/neutron/neutron.conf .. code-block:: ini [oslo_concurrency] # ... lock_path = /var/lib/neutron/tmp .. end Configure the Modular Layer 2 (ML2) plug-in ------------------------------------------- The ML2 plug-in uses the Linux bridge mechanism to build layer-2 (bridging and switching) virtual networking infrastructure for instances. * Edit the ``/etc/neutron/plugins/ml2/ml2_conf.ini`` file and complete the following actions: * In the ``[ml2]`` section, enable flat, VLAN, and VXLAN networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... type_drivers = flat,vlan,vxlan .. end * In the ``[ml2]`` section, enable VXLAN self-service networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... tenant_network_types = vxlan .. end * In the ``[ml2]`` section, enable the Linux bridge and layer-2 population mechanisms: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... mechanism_drivers = linuxbridge,l2population .. end .. warning:: After you configure the ML2 plug-in, removing values in the ``type_drivers`` option can lead to database inconsistency. .. note:: The Linux bridge agent only supports VXLAN overlay networks. * In the ``[ml2]`` section, enable the port security extension driver: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... extension_drivers = port_security .. end * In the ``[ml2_type_flat]`` section, configure the provider virtual network as a flat network: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2_type_flat] # ... flat_networks = provider .. end * In the ``[ml2_type_vxlan]`` section, configure the VXLAN network identifier range for self-service networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2_type_vxlan] # ... vni_ranges = 1:1000 .. end * In the ``[securitygroup]`` section, enable ipset to increase efficiency of security group rules: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [securitygroup] # ... enable_ipset = true .. end Configure the Linux bridge agent -------------------------------- The Linux bridge agent builds layer-2 (bridging and switching) virtual networking infrastructure for instances and handles security groups. * Edit the ``/etc/neutron/plugins/ml2/linuxbridge_agent.ini`` file and complete the following actions: * In the ``[linux_bridge]`` section, map the provider virtual network to the provider physical network interface: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE_NAME .. end Replace ``PROVIDER_INTERFACE_NAME`` with the name of the underlying provider physical network interface. See :doc:`environment-networking-ubuntu` for more information. * In the ``[vxlan]`` section, enable VXLAN overlay networks, configure the IP address of the physical network interface that handles overlay networks, and enable layer-2 population: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [vxlan] enable_vxlan = true local_ip = OVERLAY_INTERFACE_IP_ADDRESS l2_population = true .. end Replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the IP address of the underlying physical network interface that handles overlay networks. The example architecture uses the management interface to tunnel traffic to the other nodes. Therefore, replace ``OVERLAY_INTERFACE_IP_ADDRESS`` with the management IP address of the controller node. See :doc:`environment-networking-ubuntu` for more information. * In the ``[securitygroup]`` section, enable security groups and configure the Linux bridge iptables firewall driver: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [securitygroup] # ... enable_security_group = true firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver .. end * Ensure your Linux operating system kernel supports network bridge filters by verifying all the following ``sysctl`` values are set to ``1``: .. code-block:: ini net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables .. end To enable networking bridge support, typically the ``br_netfilter`` kernel module needs to be loaded. Check your operating system's documentation for additional details on enabling this module. Configure the layer-3 agent --------------------------- The Layer-3 (L3) agent provides routing and NAT services for self-service virtual networks. * Edit the ``/etc/neutron/l3_agent.ini`` file and complete the following actions: * In the ``[DEFAULT]`` section, configure the Linux bridge interface driver and external network bridge: .. path /etc/neutron/l3_agent.ini .. code-block:: ini [DEFAULT] # ... interface_driver = linuxbridge .. end Configure the DHCP agent ------------------------ The DHCP agent provides DHCP services for virtual networks. * Edit the ``/etc/neutron/dhcp_agent.ini`` file and complete the following actions: * In the ``[DEFAULT]`` section, configure the Linux bridge interface driver, Dnsmasq DHCP driver, and enable isolated metadata so instances on provider networks can access metadata over the network: .. path /etc/neutron/dhcp_agent.ini .. code-block:: ini [DEFAULT] # ... interface_driver = linuxbridge dhcp_driver = neutron.agent.linux.dhcp.Dnsmasq enable_isolated_metadata = true .. end Return to *Networking controller node configuration*. neutron-12.1.1/doc/source/install/verify-option1.rst0000664000175000017500000000270313553660046022462 0ustar zuulzuul00000000000000Networking Option 1: Provider networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * List agents to verify successful launch of the neutron agents: .. code-block:: console $ openstack network agent list +--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+ | 0400c2f6-4d3b-44bc-89fa-99093432f3bf | Metadata agent | controller | None | True | UP | neutron-metadata-agent | | 83cf853d-a2f2-450a-99d7-e9c6fc08f4c3 | DHCP agent | controller | nova | True | UP | neutron-dhcp-agent | | ec302e51-6101-43cf-9f19-88a78613cbee | Linux bridge agent | compute | None | True | UP | neutron-linuxbridge-agent | | fcb9bc6e-22b1-43bc-9054-272dd517d025 | Linux bridge agent | controller | None | True | UP | neutron-linuxbridge-agent | +--------------------------------------+--------------------+------------+-------------------+-------+-------+---------------------------+ .. end The output should indicate three agents on the controller node and one agent on each compute node. neutron-12.1.1/doc/source/install/install-rdo.rst0000664000175000017500000000052713553660046022021 0ustar zuulzuul00000000000000.. _networking-rdo: ============================================================= Install and configure for Red Hat Enterprise Linux and CentOS ============================================================= .. toctree:: :maxdepth: 2 environment-networking-rdo.rst controller-install-rdo.rst compute-install-rdo.rst verify.rst neutron-12.1.1/doc/source/install/figures/0000775000175000017500000000000013553660156020501 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/install/figures/network2-services.png0000664000175000017500000052376313553660046024621 0ustar zuulzuul00000000000000‰PNG  IHDR‚¹ÀÄösRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|EûÇgöî])Ò‹‚_»¯½a{mô}mð* E±@r\É](úbAJ}_}}E}íl¯Ø;vÀ RTZ’ÛÝùÿžM6l.—ä.¹ Ixö“ËîÎNýÎìÌ<3ÏÌ Á`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 4+²Yņ#Ø@\®Õg÷*‡y…øyA(°&.Çl™ ´@N™>ï/ùú´_¢%a²~Gç’pñAÒ#öÔ,ß·§ ¸zäÈ‘f4»lÆZ qzðejžE9Yo·”8s<™@…&дtýuïzñ^? Õì¹ÇšüŒŒp]1˜˜›ÛÍ(—zˆ½Št}ôŽºìînÏÊ c„êNCj¤=Kú3²C—+euÈf/ŒÅ~s°“®ßÞU3JJöV>ñ]ZÇ´¯î˜2¥tWÆ­¥q¯ÏÙ˲‡X–lë“ÚªpŸ.+ë{÷Â7]ÏKÓÌß÷5¥9H³4%¤X'=¾ïé7mhˆ‘nœ2¯™Æ <»%òyzvèšâpñx/Ú)ˆ–(ËV|—{Ó#íÖv?ÁìoJµ·R²‡ðÈMRëN=`ð7Í]`iŽeÒ©¿•ðn¯¯ 8mƒé³JóýþuµåÏîjnêE)ÌöH¿oweÀén™´–mŽuK&°A¼7ÀßÓO¬ÙP£³™¶ÅÖ²[.<"òßÇG`âÝw§*Kݯ”X õŒÏuÓÚÖu¥¥ûƒ7ñUáÒ¦¯›B=d…Շſo‡ùçã²sOhÚXU„ÖR8*ô–ѽ2=ü%.[gšÖKJ™O–[á/Å/WŽ™¼8QüˆI†?ákMËøD˜êqK™OX–ùŽ.û5=óÞØé¡3^4Æë¡?¡|çÛϤ6OóÈ %qHy8šýH³ =xĘ@ðÕr¡~By{Õêß–i½b†­/–}¾r=8Þ¿dÉO¤»æpß\ËäŽëª¿McÇ7×…B=êbµ1üvo»](QÿªË?cL e`a£eåWkŒíTê 4eÂh¤:ª4zÙ”á6‡°æMšT&¤z£Ío÷ g­oqЇ±úÌEFèM%Ôl)U)åKR“s4)&¡ó8_ùÜl*óôì`ˆFD£ùÓ³ô@({L gV4?ZG[XË-³,ëA—)RÈ;…¦]­ ù70\ˆrЇ‚ôé¡¿DKc¼f;Öo]„ÎyŽ¢#òë)Å­BjYoòðKÄáhaYeñúý°¡®ÅŒ’''ý“òfžÎúC ƒS¿«ÏŸ Ù³T†z](u*âþ+Òð/x4~ÍÂýÁ+~j®³;L*±GI©uO}ùèçu½Ç‰‹ýcL :„7ÐуaS&P“ðMè€t5 ë~tÒõSŒš¶oRVlƒNÃPáó ó°ûPŸº "ÕÙÍ2ñöh| ôà T¬ðßÈhE»“l t¤UV‘õö$fN"„ðG#ü÷jó³¹sÔuiA¥è¡Ä—¢ƒwzþ´iÄÈ9Á,Ä[Hã¿!pPðyçACÎÙ¹Æ Æ(¼Ó4¯çèEzæO‘þŒ×gîÓ]LûAˆä•;,B aG@õiydøõ݇•1õQ{jófø§Aذ½rÜéúâ6Eb}wç¾9ž›s™¬¬ï‡gr/Ê fý·©øÕ÷7U<8&°;à™Ý9÷wuÚ•| Чhà-4Þ¹)Ñ¡A½þHu~½v’h!¦8Ö>éÃ×ò()ÆÔñ'õŒx<'7‰'F"/'A#ñ?õöõ>:š AñÉŸá^jÞÓ P§0‹ÖuÄORƒ©o&$={ÖÁዱØiLž7Æm~ŽÿÞüPàºAÃŽr^(ðØmë4Û™Ž ¹¹{FšÕv¯„u´ýLÉ%Ñ z¶@Ï\MPm~9åK}yS—{ "تšðý^·½êO©Ì‚ÃQ¶©§Ím‘‚™Óz±Ú¤»}kh~Q¹Œ÷]s‡ïõ·ÝÖ.V7 M“ÛÌÝ@÷J˜óÇÍœÙÅý¬!×±”•D¿Ç ‹g mR-7¦æåͳgw ²í›1dàB—LºìwT4«lõ Ís¯¥Œ ù` Ÿö§|=ëÛHGPyZŒ&j:Ÿ' ãTcÄ2]ÏÝO&GË#Q‘ö…½BøñžæóÜèîøŒ 䜊õ¢3ÐÚGaÀÞè§Ú‹Ó±ŠµÐçM¹Ì0ÊÞÀ“ßòƒÛÙs蜟ë»)®;úŽ™3uê6çÇé¡¡¦a-AÏ÷‹‚`öçUîÓCî%øŒ0QáËÕˆÀ‡>%2£í"EzöXÄ¥yµá´0úâ$Œ] ¿‡»Ÿ ‚Aäÿ˜@è:¡¬;ÑH]Ä Ó~–=óH¡ŒÅˆo±ôyG8%¤ã<ØßÝ©²Í•üŒÇ`­Ä„sÝS²â\(•|Aó¥fG[äIi$!þNÁ€ðiàûÜmrÇ ~<„NnÐm澦ײ+„}¥¦¡Î²ûy´kÄõqÄób¨Xݕ̞ì¶S™¦O‘¦Œ ¦j ·¨øhV÷ !å’¼œ¬Ûvãnl@‘”öN†·ÂÞ:ÇOM“™‹fž¤ûÚ8Ò3âÑÐ<—ÒwIš'eMq¸d&¼: ÷ƒw¥ˆó7šGä:áS8=Àî¤s?éóìï~÷¶ ø½åí]”åãë yÊëíÈó|b]Ÿ}÷sR÷* ‡nD:/Bx‡T>û é]Œ‘úùn»tí”yäÅ ”á[®Ÿ;·-ÖðŒÂ{{>òvècM…\rˆ×‡¦Ý@‚©}]Ç?¤yÜ·—iZ¯x'ÇZ9ÁW”í¼³cÇeÏ<ÜPæõvÎA^tÃQ}F"ý{{¤÷²…9™;îÜg§¾AZF}u&=«¯LŽÍ^‹ºn$ÂÁf ¢3Ê÷v¸_!…Âlà nÿãM“Û­ûk` qY›vžî˜U^ˆ°/Æûö â<Êm®Iµ•ÖË€Á«(G§G>µ¬Äò[–:þ‡ªß Ô3ÿŽ ïÆ3(“CPܸԘ5Eºò¥P'*éÉ(ÈÉzÓqߘw¿þú>甋öx/|Nxt®ä’‡øžà®cɼÐÌ…6b<ÜíOõ2êu –¿àþ%¡yóós¦}áö‹¯™@2ðÌF2¨²Ÿ1ÀgÚ¢œÌOÑᛃ(UÖ}T9Æä¸ÒMÉCÐø1R úî<ŒÛ¬¢Í ›ŸAÀ8ÙñÏ’òTÄEhÈþ°Í¤X‰Jwý°gη ô›ŵűã³÷uÜ9g„ñWTÖûÂï¶n7Îv̳VgÐs4¤P©8ÒgÍꄎØËX´: ýЉxûÃè•R ½±Ï£éÌ[Jt!¿,Kô_úùÊ{æL4]Ð9x„7ÿk;ÕCèL/¡AèÕ´Aƒì£C7˜üŽtk›KuÙ,\?Š0÷„ûçЀ=nܧ[á²w£r¢s|þ'ì÷Eïî_$o@|¿3<–üšôišVgÜ_ÿúûþàOã–^žcˆŒcÔ{´;¤–’§D>·Ó$Ä©c²ƒ9è /F=`ø8â"j|­9XóñLz^žÝx·õmê€2Ðùôe…_r‹SFì³¥*ÊÚ~GáØØâY”[«¢ÃBå'Æ&ò„FZ á¶ÙHgwäûcˆÏ“(o½þ=è>0{Ó2—"îV”9¨íiÚ·èü¿G«7‘gÃTW‘СÁû¨­LÒVÀÐRÔ+,Á€…’+Q¦žÚÕ(ÉÇpµz·!irâPÛÙ*¶ÒÚ¦É Tÿ"O¯Šw³€xÊJ,ï1ÒþñCGü¼È8“Ѓ2y^_5*ò9 Š@иæ{·ñ´ÿÔyÞØw¿¡õ=„uâ®Aš¾îé;®jÌïÂ:©û(žx¯P§ÈûðnQÝ6ö¯Å«Ól°“>>3† Ñ=>˜À®! •­”ڭ㌲õ[/Btl‘ÂO4ˆ1Ô©QFébXÕ¼>qüB=P¥_Ær8vÂyÌT2Ƀh‹ÏüœÀ=°{:Óâ|„)“òB7¯r…Æúܵd˜:®U‹Jiʾ(ü6%ù%*éƒ,e]€çKÜnÑxŸ†Æ õ¹$?ìC؆Îí ¢½ïo.½ù1Ù¡Kv†‚Îü¿®×çîs‡>¥¦ê‡eå qî§y´³Íð¿ìø[×™fŽ ÃX ;´Eây s²ÞªË~µgJôA4SžsÝ#ÔÐÿ±­ü}äÑþëÂ…ãáf®ãŽFX!hMÂý6áëÖ/_Ï(©|6¬o’ÙèÐl(ÈÉÎvÜÔvÆbf4Š8¤ú!Ö5<©šwÕ³œ\U¸¥«j‡ÚGZr"f>N[Ì~Ãy4AÏ[/"Mçjk6Œ‚yÁ]~ÿzœG’¨Lë9Äû­üPöeŽ›XÎÍs”!?<);xG»ÕŸhKWeY({3Q±Î©qkœŠÌUÖóÿÚ‹‹]‰SšöotRNÆhûS±l1Œ†¥ÙAýâHÞñÖØìÜkbùÀ–må™öL¼4´éÞq´û;Fñ³È› ÑI¾vtE¯Úå¢@ #É»E­FöÞ¶Wçë7V›U«æ(Ê Ê|®)Ìgñ¾ÎÎätîÔ!efäìe¤³xë ·{ƒ QþÒþJ§N©Wß~ÓMÅÎót}Ö:iX:Ò)òzJd^Û3€Ÿ¯ü+4x£pÜÕv.1¶£§‘-ÚÊK \ÛÊŽÕoë¾—8¢ªîiLšj ŸÌ1Ž“v—êϨ®Çíx×óQ¯TcÇÏxÊJ,ï1mÚ óÕ¤kâÿFÃP§òt0Õóõ¬•N\–}±ú˜ÓFËçé“höÓ>ûîã‹»¾Ç{wfi0!_îí‚òRQ/dy±y-ÚßÚyÓ†¸ÛR¡Û¶¹ìp¬MúŸw>3d¨6š‘Ì€Øo&I£iöú ê` !¼;úQ"—v"Š´õÞ(Í¢ •ìcnAƒì¢£L£×P PC´_6Ú‘¨~Dz<¶°Á.qªûѯÆòÑ ¡‘þGáï7h9ÿ⌆“=ºF\N@JRºvXNf4:f)…Ý“Ävo;ïåîŽ#=/Èñÿ}¨ª¨.P™‰Ú‡Ÿ‡jR« 1QŸÝ'l˜Ëà}W)<—`º½Jð¡0c:¤\à4ÈÝ!ò º¶¤8ŒÎΡ Ò×Ǽ…TϺ ûq fE*íáØ¯ëŒÎ4FÚíãçºì¹Ÿí)n.cL©vµnç+Õ=nAƒÜSçl¯£kÌŠdÅ;«Fî"Dä9Ê3ÔÞÚ¤G–—ŽSAÙÛ‘é=hûèȰ㹷ÕL”¨45¯„0Xý(˜áêS}¡>2±ú“èw”ùZùȇ÷Qž÷ÃhýrêI@Š6F¾Pgï=øKåõzŽ AÏîԯߌɴ)t ;:'ûXÌzñ‡º‰ c ó­áB¤!ÏÖû¯-ðÆÔAÈGx»>/ÇÿW· AAÑL$:Á¯"õÝ~5Þ=+2ø×¾\u:8÷öÿáÿ>ò¹û‚K?¼žÅ;ÒÚj#"UÄH-Òé Úî“&wÀ×–´ìúe ‚£|u[¿-[Ã(ƒõÉ(+öL6TÈÀ¸G†ž{€;ò.B9(ÅLù2Ç.g4¸Tuà¹Ý>@¯JÄ»o}YólPAoI_·‹t}¤=êBUåªoe„ßs dFå ê¬,hTâSò °°‘|ÆB  BwÐ bæAµ³ ³ 'då\úç‘Ú}tŽ< Šð™áã\G>«íÞJó¾F0ôNAƒ٢Ⰴv]a°ë5Œ%¾Š†¾“,ÜtrÅSüÿe#u¸Û¡cô†Ói24yzJ)ðï±…™™TÙu]x¼Þt w縌«.Ñà}D *ƒZ.àÞÂìÈeFù2Lï÷Õ”•Ìzªëu{„gI4 ^OÅŽE€2ÐýõÊÅžZÕÈŸó\vïDjkP“bëÌkÝm·JÆ<*]¹èØ™õ†E'ìjg¯ößj÷•7Ї uòsÀ†p¨w4;ñ˜%"ÏQö^6*owH•|“âcZVµ<ˆ'Žv¹oü'ÎÔ™±H|ûÚìÒ C/ï¾'bé0¥µ GÐLL‘±v uŠhvÐíÖ0ç¢sE:ä¯/Ô3p?£ëÞžiãužöŽM•+Ò‡øïÑq_蕾C <-F>¤" éÊ ¯€Ð±,]ÅÇÆÕAšv+ jQü%½¦ÅdŽÙ–+"Ÿ›V¥ª—B<ë9´°qÒ‘Š÷öñÊÿz\$¾^ kÈÒ!ØmG}:.–oå$«¬hZåà’iÙÂÅ“¾VÇ!S–§´•&(Ô…î4`6é4º·4O•°‘ˆw?–ú‘6{}ΨÎÇÛx;9ÐÓ]û%üÛ‚èŸ UÑI4Fîø`»‚@µÊWD€ÃdŽS³¶n.Ã"Ou:Ô®ÆbߨBÙ¯T#€vÚ·ÆC·{¼ãÏÎs…J„g´|ç£Z®h4ªPÇŽ¿5—„”Ô9ƒ*Ç…èøü>lè÷_]±º3>Œv-ÔŸ¨ZJ^¡°+tª4•áÊïÉN´£‡˜öS‘…ñl ¥)rˆ;ŸFsi†Æ±|»Qò"²¯ÐdpQ0ðp¤XïSÚXk£ÙµŒ”ß„(£ÄB€ÚyHŸx­b™½õW˜Þºó F7m¥E¯mÁîãúv""w˜Üú,éèYqªÿ¿=ôfCW²™*ÚþÝEjTsêèá[”?½ :4ç5ÑÝÇfšˆ<‡`•?ÅÑý e“Ê[µ<ˆ-v¶°Mí<ä 銧N:$«dK•#«÷á¹\vð¢cÛ‹©ST~ûœ‰úÝgV©ºØþïï#‘þAô“¤‰¸¦”n-%»_FÚ‰õžÞáô[Bö€ÛWi"Õ §~;‡zÑ4|ŒðJ0Ÿ÷§É°xëe®ÁLÄCäG"ê Í[ëûÝÓ×ûÉBcífu>­pfºHfËæ2|¬PlïØ)õqŠK]‡Y¥^Xs0 Ò]"Òég´{š¹AžOCæßƒúû>“‡Ô©®—¬²b«¼ª+g²1Ø…E?;ÄxÇ0é)^˜—•µñüÆÇBÂí¶cÖãP6ëE±È´“˜ˆw?Æú¾›O CÃwêý-¾4íìyY;U¹ìÈàÍVaÍâ…LžÇšÁ»°ÉÈõš °næ¾…NÇ+>3FÐí{ÀD€Fn¥G¦“w¨ o¿VŸÝ‹®Ñ¨ÚýO¶Ÿ¡{:ÞúúÇžè„Ø‹zÑ9;³–ß>ÙùJÍhoã9*Ö\†8…\eLŸyÂê0ž!a ¥{ûW©¡·°….ubÈîO£³ðh¯Øgüƒ>uûZ Zõ 8âGÛú(M‘–ô_#Í¢ÝcëÑÉÐŽ/Η‘Î{4{±˜u gmŒÅžc§§ðÎ_£Þ—ÔNhQ>©3á<#°9dYw«c¿Î³ªÌú×iÏõ0e͆>vçy©.àXë%§N[Ô† ôyPƒTu&$ÏQê¢QÐqšƒNєإ©Ý;])Ü6Ês—câ ¶öTfFBMëÏï€:¦ÌØ¢;ÖÀÛÉã½kyÏ„Ýô£>h°pEá=öØc}À0ò‡¢[œøDžI½3®·‹¾Ý©ÉG|SÁn¡S/%¢ê*°)E-mµ .ÿAÁl#·‡‡;Ö¶m-ÿ?°l‡gK"Õ¯;î3*¨ Îrç®jîçîëD¤Éí_]×Xç³£ oáÝŒÝÄìú!ìó*rƒZÑÞy$«¬ôÔz½…v@ª=É™yE¾Bø,ñdE ´§©~)Ý!Σû²MÛŽ¥<»e¼û1Ô÷“¢Lõ(¢‚}MT§p©uYEì:hij+ì‡ðaEzÁjU9­é ›0Fð6Úö€ $¬-E‡õ~Tø/ ‡IÅèBTôe‘Ax;ú~¿…Ñ0©thöˆ|Þ˜{„M³:FÙi¶â.ÌbØÔµž IM £á/¡Á>~ú¬Ã0*÷õöß‹Äñ³{ûP´—ß¡r‡ä¤ìQwry°‚Y“=ÑØY{Šv§×m§6 ·ºF‡yOlø9:cçbÅã¨âpñý0®1’é.Úý-·¥ëÑžD7£ûæo´sP:†¢ÓE‰õPQÂTW _Ÿîºº©l+¾¥äN ¢ÜX:S†ðHÊ&p³ººo;ïŠ|ïtÃݺ&®+‰ü¡.޲jðwÙŠé²)ó<¦¹,a¶0knB™úŸðv»pÞ¤Œï•ËzÂ.IM aBØ/¢IïÒòúF¼Ûtèx‡o·¯’ôoĈÖÒÏC7Ez!8ꌗÛm.û lÁ=åû˜òpxîLDTß»æñ‰ÅfXŒ¦+æ}/èè_Ng‹+Nõü×Ä&[ñÆRõÖ“‰HS=±©zLu^_Sn˜Ÿ£œ’¡—´ñ™kJˆvÄ‘¬²Bòõ-¼üÃ~5g‚¤Ä6ƒÒVŽDFq®S-k íÂëý)ŠËøŒš0Ïã‰f4‚´èeñ5gGêwÇãWCì¦x;½Cî0‹gÏVÒ5ôÞWÒef?ûœÄÔ±¥YŠÈߢÿ’Xƒ…ï’]Ôv’Y9q"AÞgTC'ÒŒ ©òàÙéè|¯‚€¶Ü±W×î¿·Ÿ×Q9î›"MNXt¶7išªe¨ûµpÛ(¢FÒËŠ]o›Ê:‚ƨ1c^1°dÇ1gÚWˆÁj)ÄɶʦR§¹l³s×AºÇ"›&ªï¥ÙÛwÜ9ø>лÐõ¢ï7¥HË\R߇i“Ú‘±—wÈxçónåØñÌŽ;ÿcI&ÀÂF’³÷ñ hð¡¥ŠŽ¦¥îF-Þ&º/Ònü±(:=úóZLiÚ‡&½¢Ù Õ¯¡ß#ý–Ü³Ðø€ŽÆóÎÂorÓÆ×îy4âaTÚ§Q#dû£íÜ™„îSµÐçÅâB¥†×úµÞŒ0Ñ!•ݳ¯ð‘rrFHŸæ‰ÎH F3ñ ‰Ü£à]ÜNÊp.˜ØÎÛþ|úè > tÿgnf±xJBÍ#o$»ôn¢m9ërgïÕ¯ÔÉHï/½½½î¨Õ®aí©|A°é÷öǬuì Oí2Õˆ¨eıynÊ< »¶ûŠoŒ?ؾÒ~´ Iks—Hór³d ù®è¸U^Ïûà )uq<_+wœï‚³¤bgDë 8"÷j1É2ÃQZjA s˜J>«šÐPÑaˆí‹ÔÉOSE|*þŸ6tðí(ã=<ß—™d×™n tÝÀ²Ë{ìõU ~O@HgÙÁ oµ %°ü)Ø¢MA0øtêþ/+·\&ëöÑTï>ªHÌ¡Ì%´KUcü‹Å-ôE>øŠÍ’³h›JêÔÐÏуŽÅÇT®^“(ïMcǧ$8ÏÜgú’4tͤ–i븻-¸®!Œ§¨¹Œup±`æfÚ§§{¨(Øe¢â ñ¬ÙUyîN—û33 Æ@çèùÔîϯs®Ë!Ô2Ž„zÉ+ØEÊ.ÿ®GQ/iÁ*å{´‡öwTx!=ìÀóŽ<=ëks÷A¨ï\^b>md–F’鎛div”vE¢ÒÑÂÀÂðËQ†.Ç?¤7¥j` uP´`¢š¥¥ÉA(3ÐǼ¼." óˆFµž@7ÔYÆw‰6ÞmλìÑ;ê®#›"M®à îhÒwµ=xƒ-¨ÝϜ놖•XÞã…ºøüŠu"‡Ùu¹_- NýÎ ›ÎØÙÐžÉÆBë©^ŧg_q?§ë]õîwêÔf<"ˆ¨b#@Èžå¥øPýfo{L7‡aYv]æ˜çƒ $Ÿ€7ùApL a<Þ6ñÅêÓ¨³ÍÚ.©)è°ã«¸æÓ¸~½™OÑl—–è7*ê0Ÿ±ý\Så‡&…qìNÆÚ‹þýz ÝËîùA¿½˜™ìaT½ReJ C…\Ú¡“ïÅ*÷Uê)t”NÇ-¾¯¡Þµªg½½Y“±«Noø—^d,?ªMK±óÉF4nC eŒ :FœoBتîz‹oxÜE€µWWl7ŠÄù|š5h¨õ¹ƒþ2}úh(”?¶h)ÀÛ í´5&\‹¥ýOxÛj³ægeýVŸ_ô èÈ-[Êî@FŒÁ>į¡ã» ùô9¢aÝeb(ÖXôÂõflpEê0ØE )_¨}&¢Qþ*=ÃÅÖEðgòâSáíjw†¸-¸eÚh¸?Àó£JŒâìuDR{„õ-|vìE;ïª<Œ Þú¢}v¥ùÀ² [Þ…Y¤5ç~+¶}=Ù¹–‰™E|ŒR‰ñŒ…u rä°@Ø,?œ>…ªÔÅ*´ÍiqrxÇ™(xmñ½ÞËsÜ­Uaà"%MË —˜G!žçÛÍoPVhëfRYé 7ý±¥ô)åÒÖ¿Äí.Ñ×e–1Ûô¾¶lÅw›PÖ>Fø…(#›þnHÝøÝÁ4 €ºæR÷¶Ä ®ƒâHí„|ƒð-ζß`ÌPEŽª×ç×ë½)lGà}ºZ­Ùp,Êö¨Š¡Zw¾à~8ê†[à‡=3ØiŠŒo^δÏ17›ãŠgȈ£!e%ö÷Xâ+îÛ #ä/É^žÌw ­ÐzÔ£gP´ øU[¯áDuW¼û´®m¬i…Õ˜½š…ÝæÞ!+«Ü, óm¼“oBÍöcKˆÏž˜K<ë~NB½W‚OzÞéÄÏL ™xf#™tÙïF Ý`¤&ë\w@ûâkÒ{ËåèœÎádj°pþ;:=èHÊÅørGµ_ògøñm- ÏMt&F¢£³•´­ºãDþ~ÎÑö=:;Ñ*§zSžvìCX‰ÚøÐâé^Þ>—!¼yݲ›Ñy¹ á^ ·_¡£|ÚÕv÷¶Å‡4|‹°ÎÅ"ô›âö Fö̉’g£ö¡ó_„ß ô{|Y}„Îc7úðTy±õ\ä¨jmAï‚` *Uçï×Ñ1BçS°óW¨3Ð!(R>ìSrh‚€]ïé‡ 9—(Ôy6K‘ºviÉCþ¹†qWøÞ•pý:{#ÜzÔwâ{+‡ÔgÇ|Wæ¹:+MvpîÿPέí¡w¨c—Î`¶Ê¾— k‰°+R}‡Ù—ç0”·áèÄ^G?„w!ÞËBŒ0gôò 9Ë={D^ÚÂgßîGb¡ö äÞGuÜÜRáV^³oê‹BcžÓ8Þ•'ðŽ¦‚„#ÔxW‘÷£ÿÁè˜ý[óiCQ/ÔÑnH\=‹'ý4úHœÇ=s5TFÆþj@½°Øyœý18|ñŒ?CgôK·—M“&wˆô]•!ôõìZGÚRVb}Qæ*—Pö¥ï‰ê1£md%¾e*ìM.ÇRÔÿ‹´C÷»êÝ_¤û¿Äû‡-š…yû(Íjx„> ImÔ±v^[j~…0§h÷Æ7=^yZ¾îÿ$Z:ØŒ $šÞ>˜@ë @*o~½š:†išðn8ñ€ëëÚÚ“öK·6–ìcyLO›ŽmWŪbÒPZº¾$eñSé §î%¯lÌ‚ð†Æ!ÑîˆùÒßý„Ф3¶8=má-þ©w‡Sù]Ìܨ^Ísbäw Üv뺞˜›Û­¼Të¥<]VE µ¹ÃÌÕhX;A ó8ñ"Õ:åóøæß2í{Ǭ6÷dNêA¦(ïƒv|CåÎ.uY¯ö¬¥æ9©Ö…CǦú:®¨ú.Fµ”E¿ÁŽ7J¶•w5¥w¬¾)1¼±C[FItÛ5MmUª1ÈRž­:¨_í/××´–4*Ïo¯ZÕuGXb6ÕJS>Y˜—•õk,å„"o”´„Ôãñx°¯éÓ:ô™ßR¹.ëÍ5Mñ–•ƼÇuñ©íYsy÷ívnýÖ½ ŸèæÚÖ°Øsm<ïdmécs&6â¡Åv™¨F #{Ž¥LZ,ŸÝq2ª=tÝ`*ÿ“¥¦]–Ÿãÿ·ëQR/+„ Ñ9?À€pu!(©³çL€ 0&À˜€M€Õ¨¸ 0&Ð`˜²?Èv¬‰/êò‹/ÿd?÷Hž¶¯ ?cL€ 0&Ðʰ°ÑÊ2”“Ã𔀦½i‡g‰ëi›ÝÈ5ãõ™û`váAè⟠eè×ònɬ¶ËK“Æ•cL€ 0&ÀšœïFÕäÈ9@&Ðzäçd½©©Xd:SYæ{b͆Ø9‡¾ Í*Ñ#6úcÉ%&6ij^o—¿±*SëÉ{N `L€ 0X°° %¶Ã˜@­°VcÎX}æ˰®Â¢Ú}° »',oÇ®DŸ@âø9EªGï fÿX«I|€ð߃Ðù•$†È^3&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜@ à[[|0&À˜@" èúâ6EÆÚÞ½¼} u}ôŽDú_ôUwáIù>_¿qS<îØ.`L€ 0D`a#Q$Ù&Àv{9ƒ-Q66ü‹À?|GÝÀ×Ô¿ÆùãÎ|×Í™:u[SAÒõ×½Eá··ãî òƒÙSš"Üô@p©*µ ˜}bS„—Ì0t]iº.­d†Á~3&ÀvÚîHN#`L Ùnž=»ƒ%Ê_F8‚”1Õë“Ö¤v™” pѦ) J«®Ÿb(ML‘Ò÷`²ÓÞý/4‚?OÔg÷iiã41&Àš’€·)ã°˜`­•À–mæ B©š¦]•—ãÿge:ßÃyÉ®JsAN`Á® »%‡;6{桦e° Ñ’3‘ãΘ@³!ÀÂF³É Ž`-š€²¶Sü¥G}[_:Òõ¼4Þ83g@—µ‡âsÔ s²Þr»Mä¼m¬5Ÿ(·Âb:쟌™’Ç¡šu¨Ç'/[¤û¿¬f_ÏÝO…­Gá—Q0ë9re®º… [™s¤RÃà÷>Jªß¤ÐžÍúÇ;~Å?Ç~=tJ„»Ñ> ~>{«5MNñHÏ}¸ÞÇ4¬Ë#ì a˜—Ãþ«½æ-¢’ïáØ³ãÞø>k`ö:øé4AˆYãØ‰3~޳˜Ï$ ¼ñJ‰bÄ5U³©èÌ{”)ž˜½¥GH¡F×çìåö˜Þcöh,xÙB–®/IËw…% ¼ÈÀçJ ¦J©[ áÇÝn!hô„v@u³Š;ð<W6'SzV">³QRÝø•ÔîŒæŽÍ˜`L ~<³Q?#¶Á˜¨—u¢Çdg 3<¯lÃÖU-¿»¯cþ<}ÒV·cµvã,ÿ“×§º@÷Vùì¾1àO²fãþ¨jö•ð{<ò¬E3´Ä>°û tÆÿŠNu&FñÑw¯8”ƒùSùÓ¦mqÌÜç²[ÇÁòé=|QNæ§îgÎu¼ñsÜÅs.ŽvÛ‡ñVX•mÈð™0ÿ¦mš|°´D #|îÿáØ]g¬;BJGŸOÚëPŠŒUHPðŠ”ƒ§~SiïɱÜMe>›È='/˜õ‚ã>–sANÖ›c²C=…¥&¥zSž§O]‹;¶Ã˜`Ñ ðÌFt.lʘˆ›€­®¤äIè â¶á­k¡†3çú¹sÛ:žA4ø+f@>s ö#)äK¸8FzúWÝ»/¤êG;S¹Œ,øžâº·/)l.õí 9Òß3&À˜@cTMY7Æv˘`Ñ ”š¥çÓË#~¦3:µ¯Aà6ÁŒÞI&K1óõ,lñ*ÞU¦n™j$®?ÌÓ³ª©fEzƒ°—c6áL»sù°ò>Qñ‹æýÆðÊžR¡Kfoq[eÇÜq)T¢j ~ù„XŒøîùêŠÕ@˜:³AV¹¡ %ßG|ψL©iÁ¿C ”õZ•})×"ìNô *3ò"¼ãl÷}ŵ\GçrÓ$•6>˜`L jTîð‹2&Àv[PšŒÄã›r¹TžµR…÷4¥<ÅTÖß±FãËŽRi8V'øBP º",Å«Ø-išÞÏ•(ˆåX×ayð­ݶÛ¿ÑŸQiÕ9×êÒëK FÙ(a”¾’1=4U³¼¿(i‚½h{ã;wØ?tþ»§g‡h-IµÃç‘u™«¡µÂÆUø–Ærôm4TøÜ_ F¿E*2-Ö`mËKBY·A…j»¯[‡§Ýžvêœú­›ËÎSFéKðo2ҷڲ‡X¦±þ}ÛÓsܽŽ})¼¯ L/YÊœ…´Ï‘)ê[³LÒ‡o…jªeÃÞgùÒßý®,ë…7h>ŸÉ»R9$ù̘ˆÏlÄÇ‹m3&À¢À·+¶`‘ö•Ê´žC‡÷3S‰WÑ‘½s/jmÅY·ßt“½ö‚¶¥ÕÚÈ#0¦þ•RÖC–*[‰Qø—±sÒ”¨×a˜æmÿ(:èñëÜÎÛî‘:¬ÚH ÛÄ…8•b6äEC•‹ž öŸ·Ò²/:éGþ Ã<“T£ B6ñýá½Fáã|™&=#0åó‚÷Ë4?ZT¯=:oÒ¤2÷3bŠmrÏ…Ù˜-z:.[gZÖSðëC_;íx]?êeG^δϥ԰õ¯:Ê2­ef©Xƒ/~dK¯¤º>wìÑÙ^÷"µ›àçåBý„û¹Õ±ÜVùš 0&Àê!€:–&À˜Hq3gv‘ebOËk–.ôû‹°>!rÀ¾*(¬Eð,[ñÝÑV+9}ðà õ,î®r—ˆ ú@ÞÆpa{‚þ_j‹c2ã7Y¿£sYê¹03óºÒƒ…ðg0¦ižcór²Þ­Í.}™|c8Ô÷¤C†¬­‹#ÙÛõNñµÛv§~ýæÚüsÌÇê3h¢ã¦ú„펟™`L€ 0&À˜h FõJnEY-Rþ"½âå|ÝÿÉΔ$çÊ›o…ÐõÅm Ã…× ©®3,£”ÂJ®GxÔˆlOV¸ì/`L€ ´h=ûÃÑvô@ÛÈY‹¶ã®Þ¾Þ÷èúèÍ9eÜî5çÜi’¸µˆ²«ë¯{‹Ì·¯UJQ \Þ$d8æD 7"s”Pª» «Ütð'%µë ‚YÏ$+’I™ÙH䜠 ÷“R¾„ßÃZšöüÂÌÌ?’•ö— 0&ÀZq3gv±J¬¿(¥.Ãï, Xý‚ÔÉf¿ÒSÉí^sÌ•]§æ\v3¦‡NS¦º[ uúfæ—îJê®&0Y¿£s©Yr¾¥¬ó„Ôž(Èñÿ'YqJ¸°‘žœ¬,q»”êKL…OÊ þ—¬È³¿L€ 0&Ðú døƒ'B½cRz ÔÄù9;›Sª¹ÝkN¹Ñ¼âÒœÊnzvèe©|Z%œˆ±L€ 0&Àê#`·9h{ì6¨>Ë ~Îí^‚îfÞíʲ»›¡æä6†€&ÿC3Èézî~ñ†Ü6ZØ /ƒÓûè; »gL€ 0& js¨í¡6(û‰´Ãí^"iî~~5UÙ¯‡þ”Ⱦûæ'‚€ÏRKmL‹¾×¨£ÑÂBïO_çö5*Ø1`L€ ÄAÀnsÐöÀIÿ8œ%Ê*·{‰"¹úÓTe×0¬”0ƒ»!bNr,Ö`@ç]|i|Æz×èUæJª^ˆDQc#Âî™`L€ ÄCíµ=Ô5éÁí^“ân•5IÙUrOèÜoi•9QMB /'pÔþPŒw4ZØÀw5: Û v͘`L >h¨í¡6¨In÷šw« ¬IÊ®Ti§´UäD5 DÑF M’Zd·!pýܹm‹/îÕnvEwL™Òj*I|ük2ñj%´ ‚þ»(C£™µÖŒÎ˜¦,k6–‰}ôm­éät1æD ]ŸÕ¯­Ï,»3+kC¢: Í)}—æC )Ú3' !åýù9{šútýö®^îœÖλîö›n*n¨?ì.v,lÄΊm&‰@z(ÔS–Z•'mÿ½øìn–²ý·bꌡ¤vgAŽÿþ$]åmźF•ÓþÒU†‰¼²—²Ô¡RZ˪¼fVõ0ñÓƒçY¦zÆöY“s r7D†’ž !žYš¦eäåøó#Ÿ7ô^™ª3æa•BýÞP?Ø`õ ÁÐÓÿ?¬¥ívÒgÍê¤JÔK–™Øbí)ä{¨Ðþ‹ßw¸?XX_˜TÔ1Ù3, ¯-]_ŸÝÖò\*5)={ÖÁ­%=œ&À„È䌶”ùêο¡C¸õé¨KßD9c/£÷o­‘S¡±üé¢ðÊ—ZcÚšCšP޾FyzqYƒoÛôSJ%,둌@hjsˆ_,q€ 1ïÅ=žz"=_"=/àüî÷Ãèn?¸<í¤1VE}¤& »â™†qcW 0ñî»SË6ly^ Åÿ;^oÊÅ ô›u¼¦mûRDÚOÎ}²ÎR#½ú)Y‘K‚¿¨`· ±è$Dx!Î'°zE ³—L ‰ Œ äžk*«Ár)µ ù9þ{(ÜxÛmí¶l5ÐõÑ;³Örž¬ßѹ$\< éYÞZÒ”Àt|¿<òæ‚ûó7ÏžÝaóÖðbtÒ/¶„¢¯KC=¶EK´SóC9t­ëJ[gÎ>X¦›èž.OœÿVXäJaÒíéŽYCÎ,l4„»I² ÛÏÆHÃIxý‹SÓ´ çeݼÑíñÝÿ™û¾²’ ¢1=E)9$ü€©Ü¥ÒÓ-;_Ï(qìBhF*®ñˆ”–(?ÞRò<£Qüupóp/ï¾¹4ÝNªSEÆÚ¦…¯ÐÝ‚À±£¶þf/oàOB¼¡Ë?»—Dßî~¹vc®²Äp„_–̶?rkœœ¸År¶g{¶³°°ïd„Õñ[‰ó½Øb‘#Ð4o}q«-,¥‰û„%/£ãÆfGÁÞâÚì:æñ¤sBö¬Ãʘ  G‚éF0V í5¥L$u½ëˆ%­.ë|ɘ@-¬Š-N=¨#þá^£»@IDAT4Èz¥^ú‡n§±¾Óõéx§Gß½Qo¯Ð41wÑŒÀ“˜Q¹ÚUÏJM,̛ΎF}¥äû©í´¬²kê‡QðkYª·ãäyú¤­¿ëB¡¥¥}¹¸¼ ˜}™9GFvðV üŸ&µ¹‹rüddçþ¹8\|'êoâsêï•vóPGϧëݹŽƒ›v‰:Ï™:uÖà-„jìÅàŽA«ºXËù2Q¿»cYxkíÝIv/ÄwPqúPx=¹ùú´_¢…Dma¡ñöPÑŠ5‘`MäÌhöPžö$s¯O¾â<¯T›þܹ¥<Åšç½Iâ‚°’}1µ˜…¶ƒ{¶ŠòCc³gŠïõ¤W¶óýlv!?QÒ3½ '³ê]]²d‰gÙ+'`nr ¸ ‚=iYw4[I êkyÁìÉt”².UBäV£¢âc×–=5‡éyYYÕÈÑK„Ñ”Ï L³P ø•Ñ^P³š¢Œ ÑÈËM1¢üa¨gÝ‹|^Hø¯öù-EÆwYd·H¬ïŽ— Lqàßvü6ÓÏ~Þs%=9á Sk6>hYêFÚò÷Ðó8ãDNê=ÈO±Í€€£Æ"^áà5üD< ¡ý‡ãAQ=qsìE=+Ö4y#=CZç\¯ÏÝ#ª½JÃxÒ9AÏRnï¢C@Úöeˆû –²ìEñîpbM«Û _3&P“m¬ÓéIj[íöš6ª›ÄóNÃ¥]Ÿš¢üÔ§wcÄ ê ïøñ–)>Dõ,ê«~¨g!0¨!X÷u:SèŒVN}…»e%æ[¨x.‚?Ãnø3Ì×i”™l[a¯ê]©äA®wþÇ`Oz†ðº‘)0ÎD˜égê„Ùõ7®í®cˆLË:¯ÂWùb]¾ÇSÎÆÍœÙe‡±uÚx þáÛ9R}‹üÜåãoBtÙ-<“$h  `ðL®•}»ÖZö1àõ,ù6,ÅÍš>ÆRžâ퇔C8G¿áy”Ö3‘Í’â7 Y)ƒÔ¶3ð¬çgÿ¤ç,i™Kǃ½É¯~±r:Þ'j?½°s+üx å\hU[‰ã9²×ÜË: ”K|ìh8*… õe}ÅF•Ð Œr¼ÑËÛ§~0p’ôu@÷h¸öߺeGMÝQ%ÃBç«0“Ñ7/è$4´ÃQò*:ÓH FÎNÄËû#Ýãe˜H÷ô«¾P\ý‰* éÑÎíí=¾’Kö'rXÇ!·‡§R:!L=ÕË{|_ŒL]àõ¥ À„:írRF`ÎàêΣǭºÈ;- ‹¿"v¨°ºn7JfEÚpßÇ“Îò°• V @þ/µGLjÿPOíöϹŽ?­ŽK>3&à&°ý·’Cñ.cR®¯oà†ÜÅóN;á þ*CPIö¯rÖæÍ~Æ ÇdÏ͆&ôà:&Q8Õ0t‚ÿŽÖ7ªh¡ŠÑ ¾ìÝ£]a]!ÄSÎÐ!N~Áÿ[ò§MÛB×Ô¢#þ=]»¨K•Ž „2a³ rE;oûskHwÔaðñ4Ø' ‰žXoòBcíª±Ù¡QŽúÎñ¤Çñ‹o;Ï„»üþõdæôèŠL˜.%;–”蜞—çÃ{ i¶é’ú5™Ù‡&¾¢³ûkîe=!ºX©çÿL NRmÀÛÒ/I·º\V.$ïcÛñÈån»š–ò•iî —®¿®/I‰Øúp•ÛnÛŽžŸÊ7£ošê­_¥²oºŸ×uíñj¹Ÿ7"Nnoª]SÅ¢~Ùh7ÐØ¢ö èÛB YB娆‰¿Õá&2n‘Ïk»_œú ôJÿ…ˆiFX.„v¦lI¡êˆ'BkØÕ«9Ö¼ÚÛUžÐ½«Ü°šV·Ÿ|͘@Ô¡è “:ëR²Ï;]½>•‘¾5äŽ+Nÿ±.Ë6G\ ®^óh'Úþä6…®*©ËPÜû»Í{ÍuŒc9§Gƒ¤ŽÛàæQ©>ß«–0Ó S•â+1»q76x¹žbæ¿æO9#×PoîKå ­­öEMß"MÔ±X£A뇄ôÈÉwê×oŽ´í~QNÖÛËÈžyšÿiè Mh­&4*ÖøDsGfñ¤ÇýÞ Ž/,ÌÌ´U¯Ý~Óg2 ãZ¼»†1³ ¢µWÊCvIxÈyF'`ö’„±‘Ê$®ÏªðËó[BYga£"Çøÿ®  äx±Å/B5¨zdöü½ƒ,[,˜jžTi86Ú ±Ý¾Qž"ñ•g¨HVRÙ£#ÎmcΦèºÚí¾Áqr{qí]g¤B²+´¼KPù;VÐÛ‡å‘;fÎ92nŽyLgO·È|Cåö§¥+V]‹9{6ÂqO:SEÙÎúÄ«"*W³Äñ“Î M«Û¾fL ‚€Ç«~0IDªž´~­®•ÅóNÃÇõ©°°£æµ¥5Mk7)V¤FE{²NÙÎú£6çR´#‹±\ÇШdÕ(aês›æéS×Vúµk ^·Jåh7ÉÐsÈÓ³vŽºWZЧœun»Ù£¶’&3Šp8­ZTé]µÂEg[Ò:ÌnØ‚÷.tÀŽõ#À•3 OÁ§Ó§‡n·×}*™æW $â&žôÀiÕ{A¢Zß¼%ffØ|E¹ž¿£áú \_ãBÄÑiò…×+o6 õ,âûOS!hôCº; ðx^NæÓùÁ¬ä¶§J~GqnìQÿËÝØØ=¨€”ïC„¿¿QézÞ­µMƒ’ÚFh´ãP|RhÎUîQB÷¨wDQmîíç þ—Œ8-Ð'l‡>ì:šâÅ\ë2¬ ùO‚£]Ã;b†ýM‚N.Y3À1ÏݘǛNÄ âßÉ*´®å1'@SÉÞvw¢Ò`W¤Õ‰ Ÿ™@k#°—赺Hþ†Éž[6—Cúj](ï;hVš¦ÙÛMŽ¿èdQ]ª‡Œ Qæ3LQ!8ùhw"g—*z†ƒÜÆtpL²bÑ}LÀâ°dÝ0˜´‘:a~(Î5„xËÚŽßÐvìYfîØþUµñÑ¢…™–W½¾.ÆïÏ¡sŽ—Ðêôhvk3#¡c¼>ç¶°U6mS(e­šÓxÓSÍqäa¦“  úëO«T˜!LœKVñãqÅÑ¡­ï+,J'A¤TÍÞHö¤GjËÍð¿Œõ¶¥d–u¬ÉpâÒ˜säCcüb·L >Þ®ó!¿ÿŠ×ª»06ÈÒÜ÷‰ºÎ›x©Òîˆ)Ýñ‘þÆ“NÔïUºÇÈSÅQ™Ž+œûªó.HkUØ|ÁZê¡Ë¥$áÀÂy‘ÉK×oïê˜ÅóN;nu†ÚÊhÇ/»~Ç—Îí{ZŒãôÁƒ7 >£>ÒÊŒí§:v+Ö•©ªŽ™cŽN£]cWÚ¹Éw\Ǹ`$æ’Ú!Þ8…•ÈGÍ#WÔæs<å mÇròÇTÆw>ŽÕoëN}wèx@ìToÊå(ð¿£w>fLvèR·÷5 c§‡Î¬ø2øÎ'¦QöºCy[çµu•§xÒ³3”šWHk…º±Tv¿†lÐn\ðÿºFªÊñ–mÆx°>ë²n*Ò rÙ$h½jG3/ë<³Q-·ø¦) ШúØéÁñÐÿ| Ë…b{ø'l£ø Þ2LŠÞÐá*¼ñlT–…ãq'´¯w¢6OC¥Ö.ÒZ<éÄ öY¦ØâOý£4ÝQy!ÃQub«Ìê>華Vß1ÖA ¥GÇù;6l=uÅÉÊ”Oc¤ø¤ì+üÒðêí+Â;Hµã@Jm<ï4ÙOÔAõ:†cÇøƒCPχ[èÝã‹ÎR®IíÖá_ÎÈ‘#MÔ/£Ž?W óÌjÿu|#¬N„{š©šÈþ¢œÌOÑnü ûgd‡^Bºñµkµštð¹Ž!B?”e݆v7 y•*‡"}ä+:éùÑT¨œã)gRóf þ Ú‰¿a›÷ÁÈÇÏ‘Gšá²¡›|?öŸEŽ¿Î™T»Ò¹W£œ<‰&¼>ó£zf µ¥2QÒY™ÖKEÖòMð÷ ø q(Öìoû¥Uêt]WyŠ'=N£ž%O•ºqò q­ vã)ƒÖ±ö­æF©_èÛL? »«!O“šÙxg–z„÷¹…Á©¶šSs/ë<³Q-Wù¦© С|^Ï¡xq^BÇu™:å%˜H‚5 ï ú‰âD À´¶â,ÔnãÙ~°8öÁËùšÏç9Ì¥OwÚxÒ B$ïÅ‹Ÿ"”5á^ÜÖWV±v¢ß’'Ú¥#‡ Q¥]Ò ÃÛÓpMÓ«Ë„8™Ö®$üX¤gþOƒÑ<Ž'XˆøXŽDü©Ò¤8F§byŠ7åäuvªŽ]•ÖªðhEh÷·Þ^ÿiØ öf$k ¨'G Z†w¾yS5ûÏ;HDˆ“¸œŽº`â6‰âˆ.ë{¨xOrv¯£ð°%îxªÛQ‡´¡Î'ìÕ¤˜©4íʨñÑÄ ¨sÞ§Aj`‡TqìüvE}5Ž-Ùr°=ùq8òl3X¿ëѴѧ2¤ÆL¸;™ñ”³üœi<ÇÃïOáÂQ× ïѪÏÊ󯧢p{]u5 Oa’èt0Âæ£‘¶Å6b Ú¥ûq½ïÃqT¦r%ÌGåçî©ò.j)Oñ¤§š‘7}º-ÄLÅoF\®G¿g®ÿ…AS¨Vìvé8Áøßa†XÕŽÞ¼?‡âúLô n7Uø‹t=tÙmîí)âݸ’ÖäFN¦3L 1®Õg÷R)fÙü¬¬ßjó•ž¼6êî×­¨®}µks_›9MÕÒŽ©Ý;­u7|µÙw›'#Nô‘«MF°¿ô¦„÷7¸w¸p‡Ý”×ñ¤s¢>»·£ï·Xî5Ç´6%W«avUû³«Â‡}C`“±¶gWïñkêš çŽ'|·ÝŠÝr6؃ í÷l—Fu‚ý¥pg‡³Í©Û¾s=^ŸßÞ[».¼eÚÏB_«îÃþöÙX¤ß´!Òfs«cš¢ 5E‘œk»§œÑŽK%¿}Ò¼ÞßîЧ ³¸ƒÊ¢X³¡_ÇNm~­k# ±®òOzj‹=ù‘qëì¾½ÄÔµÕ¿íUá3uWZ–õ Êþb"ÓiP•ÜL„ú„¥š i8>Ð;#/'p‹;ŒD–õ±zð(Óô(÷WÍÝaÅzÍjT±’b{MBà}j©ÒÈ€+Ÿ#Í{_Ù™·õãõ+qª¬|~Œ7.É´O:ã™mjŽiM&Gö› $›­ã@õÖñ¼Ó‰ˆsñÖvö §óݺü$½|<ߎ™×º¬U=[VÝD\ì¦uÌvÙm<å¬r€jU2"[9@S;_WyŠ'=µ¥£Ò¨I‘òBÍÃR´C4…ÈÈ¥+eýí£ë¹ øèzÅn9º.­hyÀfÉ$ :”åw „K’ û½ëä³oÞu¡'*d.§‰"Ù ¤JˆœO’]_ܦ0\x­¦É)†eô¤/ŒvHkcuî˜&Ûø|¼–¤!P›ÈÍŽpØÚ¼µDm+Ù¡!ï‚è°­3MëŽ>)}æéú艈—DPÜ5~4Eù¯Ïo66_íóˆ±aÃØ¯o®¿[%kJßúte¾ü!j½vÔAƒNZà_SíÆË?])`?*(¶/D2øH)Ê…-¥¾Ý» gѦÍÓ³C·Š>]ÌÏÈGÍ 6L(äË1Ë02czèÞ¼þ¥ €=c Àå4›‰QådÇmLÖ­gþjÝ'„ê}À Þêȉƒ÷é-ÓÚ¤z’6ûŸ¶0X²£L|±ºP|øÕ{}¹zíäéä«§éWß7K¡4x¤’ËGBòhWz’Üò™ ËÚ¼D(k@ÿž=ÔŸÙGyÀÀ=hªâøC‡Ø¿XÏöë&• >èঠÔÞ`ê}ù_þ,_ö‘Z½f}¶vÓ-c¦é£ fé¯â¹=óTwìøi#hJhATӹʲæO¼ûîƒçMšTVŸ˜9#s>¨xŠî)¸í‚òü¾øµ÷çûýëZMîÄ‘¦žA¨¬®Òó|Ïnöºáгĵ—œ.>h€ G´Ùjs @yFyGyHy¹W×N=4÷¹k²n½ñ#Á±ZƒCœ¹|Ä©¥XIBùéÙÁk5e¾Ó¹CZ¿›®:GÜxåÙò¸C‹ß.7i)YÑlã9 W7;¯ûÛ¢g×N=5¯÷E XŒF„R4Ût6·ˆAàÓºtðÞƒú\Þ±~ëÔúâˆwo쾌žÿÉ¨Ø a?u*F•^Eçêj·û¢ph!žÝ {ƒ!4àp¤¯AÐèï¶G×$hˆmÆ'°?ö:Ãè5üDX 2²ƒÿ ;tì(¶ž1æƒ À¼ª”ìÈqÊR‡WØàÿ­òºÅ•Ó1ÓCgCÐxeÿ*”ÑŸ h|r}ûi©áÅ­-bMOS rŒÆõ(©il¿¥Ê§ÏT[8ãõ9{a^ˆžkR‹…ã‡c=ÀaRxΣ!ŽÛ'äæîIÏ1 ¸7[øPšv>ì•êíÔ®¹z“÷!·‡§Â|Tfžêå=¾/ì_àõ¥ Àõ2tÖ&eæ 7sf#ÇZìà= á/{ÚÊ>iÞösÝþñõNc9§Òo§IË»jIå”èJS…²Šn˜2zÊêé½¼ûv‘"õ°yú¤­-/ã¦6$F«°\›sÄþÕ5$S lÍGë"@yJyKy¬ižÙWNÉÄKÓ%—ÖU¢¦¦åƒüÓʶþx>öÿ¼êÜãd›T_Ô0ذåHMñ‰ñ#N“ç©ÖAJ(“©¡`# Ùá6ù3ü¯£ÿfÚ˜aóžÚ‚1²³!T´‡P˜Ì~ɱ—ÌzjNÿƒûÎåÅÖ9dŽûaèlyàï¯3ü/’u² HèpÿÝ»ö†|èú)´–D,Ðoþ§e¶?ªü¨…™™À¿ï/fAn¿.êAö…wê×o¦k>jÀ{€~5Ÿ´“–TN‰ªÒä»öÙ—¥OýE¡€ëúÈò¼àÍ«ZuWL•ü²Ów.“]6…°!¯¸âÆ4ŸÏw_ŸÅ•çÏ Gƒ²ªå8¢<îݽ‹j›Ö.ÿä“/l˜×ÕaàòÑr²6!1·| PmÕoDòOTÜMQg%$ìIüH•ÿü“•éÃOûû3?°7| ý8ÎóøQÆì¢m[íF[0»pvF ÷¢¼ôt»Ãïöã´ûØ÷R,w›W\˯ì³&H@¤%ªRJ¾]ñ¼â? Ømò'·™½Í®}ÉÌ2Õ˜Ùâü`B‹íß@:kR›nÏvuMI©õKz x?Í¢Ð3>j! Q_Ò¯-¥œJO×g0<ò6Þ§žÊ´žƒ*à7ØÆ÷*]½Eêúbf&ƒ~-BMQ5_¯öMËêuÉG{xF£±YÖüÝS_zæÑì6Ó³ÿá^‹×ÕaàòÑü³4¡1Œ³|Ðà„·oÿž³ hð–Ø Í‰fíYû“Oþs1tf7š¢­jÖ@’¹»üþõèúÉ%¬;'ܺ :õawx *6üPb½Û¼Â½³M?ÑÂx¢œ˜Ø÷Õÿ•¸o½ë ØU4EóWØôA>àü°.câq·å‘öÖê‹rüK4Ÿg?è§ÌGXêƒÑpõmz ÷BÛ=ÿkÕZJ9Í×3J† Ý÷$¡iÅlܧ˜Û‚ÇEÆÛ¯Ó.›­:“êH\²%-j <¾ïu´ëô¶©ãÀÇn@€tôÚ§úêûB6îÂ+´YÕv©âò »ãcù‡~¸gØÈK§Z–¼hwä´;§¹}»vç?¼xþÞ—ž°ì¯JíÎ<’™ö^ÞÀÂB#8øÃMó[ |”¢Ó¿3H¥áëÁ&ŒÔ †Wø>Ë ªØ5¥VÛ&Rþƒ.µï]ÿ @ôFÇ«Êd>a;F}×Ñ(0ô¯–åýÿ©zåb‘žùŒ¯½^Ÿ;½Ø(™‹ø^%¥9fOE±ÎF­Œ@K)§#GޤúŠÊòþŸ½ë€ªØú³I€4B'„z—NèAšQñÙ°¶gyOͬOŸõ}*¶gTD¥wé½z$@(!=ÙýþÿIf¹ÙìnÚ–»É=¿ßÝÛæÎÌ=svî9sÆE–åÐjجX~Çf–°a)Ðë¸{µÈt÷Ï!L¥¥lp53Wúfòq pÌñ yÿ„(¼ [[0èÃÇǸ8Ý/}øµì=¨Ç¥k©¯;ï8¯b<ë `îð«QëÙÎ}WFõÎL1ÝÐzéªR&U4<Š•X(¤-OCȵ[& ™¨MY~½™ÂΣSÞjŠãx.Ó"Ê­âu±‘{ÔÑüñ—¦Gò˜@Gey„|*òå„àUZ^ã£? –{d5—Q«äU!Þ‹~6! ÀŸš/4d2"Eä।ïôN§Äÿøèwªâ?dåu¤ßRƒXK-­ºS³Adû— ÄDd²0†'þéYƒ™­•µmÉ¿AGD_€?×îm›Ôðð…îæé#Ɯ΂– aqs+6Ú+ ‡Wè#O'}è¶'do;4¯—§×q—®a1ÑÂðÃyîéõBAè¡P{“ ‰¬QÅ#ó‡#\»pEl?xR éÞš4í¨˜ÓëÎÆÏÞƒœ¯À¹µ={uúÚµßÖî½§iûvÇ6­ZøúÎù<¬îÀÀçÓ&oyä¥é3€â‰¶õÓa£Y1Ôa^ÿÈ”és‘ýÂ’eɸ »2øG¼§`?‰ž²÷WaBê“!Lë™òÚ\“°´3 K7˜E]Fù\´€€²/df¤ßŠùk`jüÕØñS^ûù«8odɸ0¶îcÏmŠ™Ç™2mÚZ^îþ·²ŸÐ¶,¶í¯q^r1 g:•2S߆ŸÆphìVcîÞ‚ «>Ìo§i¢¿¿i…¯ÌÄèéQYYþâ½8}w§°Á•Hrýõ˜Ü ûÈdý°`ƒØuø´¸žœ"jV«$†öh+Ú7Ï^TÙì¬øyÙqþ"ò aä«V,/Fõë ÓvêÜ%ñúWóÅ‹ ukÈè}vqúëŠmbñ†=ò^P¹²"¼JÑ»CSѵu#»åm/Æ'$ 2øNóWï}-HyÛ¶ôpÎ< û”¤¤ºèíyÓ±Q½ÈÍcô¶ÂKÿýE\IL¯Œ%ªWÎv î}ýÁØÿ Éé 2³²Äã甲¶³b.¹·eßqY¢[m¥ó×ìYæ,1á¶~Ú˺>.}€±hV9,8+8¨œ;ç*¡æ"ŒNÊ僃D‹5ŽC»Ëó3ñ òÿ8¨k+)±ÎÆO[_ì…ËLt'XÄœ8'êET¥%Wp`YKzJJ=à‡ã¯„ Ì⸡þ/f%eŽÆ˜g‰û>E_¼‡ñ¿|ƒ€0ç" Õ3È@þBÖZ»â?:ëzÖ7(3É7Ÿ„öâ<¢ôÜa²ˆöXnzÑZd{iz›L“øå2w†)M"·!Hô1‡ˆד,™ Ñn[+ÿÈ‘9ÓÑö7"4à)m}Æq. @#Uò@¯tJLƒ&7â?Ò´:Ìl1ß–}Môó“?‰~i·¯†9C¼n’,›Pœ¾»ó\ãá'jV &cé6ØsJlÜ{aû‰ ¡Áâðé8+C&.A|8k©€É†xxdoQ&À_¬Ù#>Ÿ»JŽ Z6,“ˆwOÞy³HBöìÇbÅ÷®q—®Š‘};äû~þµCT«fí[¾ V„ RL·e8ö"ÐA ¤7åxè1ú(r~ZºÉ eð“‚·–Ùw4VÌ[µÍ#†µQ;Œè™óýµsSÇ—ò¡†Ï¬äÖùC‹ž‡Gõ†€*Žž¹(~^ºI4ƒ°ÉÄ•ž„?ÿÚ%Ò32Å䇉¤”4qâÜE)hP°ýtÎ ñ¸a¢F9ßÑ`w•+„˜®^½V upþàÜÁ… ßžñÞD–qø±bˆYôM†•µ×GfôÆõOŸŽ~¯bVÙ”2½øâ–›1mJ®â9õÜúÌþ”’œ\™!js ü‚ýK¹ ãäÿ^›r»Ì<~1sz¤) lFQ?ž!C…u'àþ0>G3ÈÁøgƪP¹¼n@^ @œ”÷ªo\ñQ:ŸM{i0<ƒÑ§b3×F"Ì%ŸÏl²¸DNpI%vH—Œ$?áˆÜAvʸìÒ‰Ø ¢´pB—uj5‹ÖïÆÝÚÉæYÆ ˆGN_ ×í)´°ñTÕßNÐþxÃ…kw‹>šËv¸BúÓÒÍâ4„œpÝÚ4}:6 `µeÿqiµa×Dkê"nj\[¬Ú#VlÞ/®^OM#kˆÁ0Ó¨_«š7Ô†¼öåïââ•ë¢C³zâ®[ºÈ6µÃ÷>#–lÜ+¸['¼²¬“+ôç/^ß/X¾]Â*~êê*hÚ²6êÂŽ=bïQe *Ãð}ä÷:½Û7+¶{Žœ±+4PõÓ’ÍbÌh`',úuj!vi†4^Ì\¸Ú´TñÒÇs@KµÄõ”TQ%,TÜ6 “4kzõóß@ÄÄ1}e7>ž½T4@2:äç!¤ÎÂó'@3•°j=¬W[ѱE}YŽõÖÆØ]¸ÿµãxüÎþ¹^ƒm~üã2Ió*~\¼IšÞ3´›Øuè´X ºj úZ²a/VÁË‚›‰^xOÂéó bÑúÝb?„efÞ ×FOõ1WCn>q@œ/¸a]ßR6 ûëæ¾°zþ/¨Ýdfëå›÷‰ãc{†³±£)çœe›ÅÎC§¤ T'ŒéЃlÇO{ïxìEÑ ÚRë<ƒyÂÇG³—‰Ô´ ñÞÌÅ"sÙkOŒ‘mÚ{ óËNhèRDýšUÅÝCºŠj•Â$ýMænhoîò­">ášxõ±ÑâÛùëÄhH¨ÁiÓ¸Žݯ£Ô”°ßWo\¼INKÁ ¼€˜tß`TÖî@Ûow•- :0Q²ÒÎ4Å4À‹((óô޳Ϧ ›JÐÈ·ÇÒ&_ˆãΠΈþçEg÷{¼I§9‚ðQÕ—Ò¾wçGœu@åI¦Á­ÐºIq&R_þºÌYb®¶ÎÆ_Í"#¬‚†ºÙ¼~„dÆÕyQ÷4iAX_qŒ=!,$H´ƒPð>ÐmšÖÅêè‘ -Hk|ÔC‚ʉvM#¥àS¿g•ÃBĨ¾Å?î¹&VYâÏ¿vÊëêgÍŽƒ`›‰þQ-ĆÝG¤©ï9j‡>)3m€©EñÔ]Ñn]Q¥B¶yÐw®Ae`4HÔ‚©Ù,”ó@%O `ËMÑœÇè#¿w$£MÍW²³²òò1k¶† rVŒÞK ïÕN2n._^CD£:Õ!d†È1íÛ©¹,v>%›<{I®Lï9rZ2‹d÷Á¯U½²¤™Á<¦àÚ½(€|ú¥I¸–”*hµïØ1´gQ&{ (üü÷§e¢…¡Z,ŸBBzF‰³bãžc`r£D$háÇ%›$£Êû¿AC!ã'o—¦B¼öÈèÞÜyÐiÆís‡í „éÒV, üá25=CDÝÔжH¾cGünÞwL.2Œéß4’{¡ØÞøii‹ùl1„D.°, %ZÖ—Ç£ Eåâ áØÙ âëßÖˆ†µÃ%%§¦K¡„óáÒÕëâ¿?.—ÂÂhô…> uAnëzî!hÚµ=æ„,Ë÷Þvघ4nˆ@âDùìƒÐ˜Ñ|ËÑ@>èæ˜5r®PšQ{œ.ÜüŠFõ (ÑàÄíPŒ‚Þnÿ0ðcNûúC0ŸzùÓ_%3H»{µ¤œ+À…¼(™/š&hÛM¸z=Yî©EéÕ+‹åEëFu$csü¼\¥æ‡f‚a?Cƒey !íšÕ…ùWh‚ÌýÇc劤¼‰ŸÍ닞횈a=ÛJ&áV$ ŽÚáj%á$˜V2£:·¾4Ç cÒ£mSØ£—]°rÊn[áL>ìò)p*AƒT ƒGè#¿×É„€qVw¯$&‹eÐ2ÙÂ^ Ô„W)/šÔ —NØÛcNŠŠåCD% HÀ1¨ZQšÝPEz8pü¬hReh:Šƒ6-^VM /B0¦„ÚŒûÀÜчa=J©i™Z‡HA“‚*Áãû¿ßׂv3¡‰è'µ\ª¼í~DŸvÒoiDïöRˆ"#M8x2NÒíþã}È *­Ÿmž9ÏCjÎ0¥¥¥ì®‡ÕzOÁbh|f/Þ(VnÙ/n‚¦ª:4¶ßØm‚I'ÿ·t&§Û‹ 2~·ß%uk%æ­Ü&^þäW©©¢•ZJ}6Ho„M{ŽÊùôC:¢/5GÏÄÉûü¡¦tüè¾V­Ù€Î-!œT—æbÔž <µ,K‘Ú8§)jóúÙZâ4\GÿyÓÍ?†°áfÕ00``ÀÍp—»M†¡·Ý¯Ù`c4zuâ(i®ÄUÁÐ@iêR«r×’ÒX$Бœ&ŠdP U*„Ê=M˜®Û%âðÁWÇ™YÙ«“²€Í ®&š¡!CÁInJhˆÐD"c@ ‚³v&ÜÖWü°p½xõóy¢S‹Ò¬âà‰óRˆùfþZkÈÜž‰¿"ªÀÍC’ÐŽp=Jù½…·[ºµ–æ(]46úÔZ:u^šLmÝÂZ £?Ùƒ†µ«Á/(@…™^ Ç;¡.8Œ‰ƒ"9¾4s!“¯è¦Ž&ÒXðJ"+Ñ ¨5±œFó—dŽdbƒ¹Øë¨Ñ „†d *iÐxZ4ˆ+·ýZ³ý <å /ý8 Ù›ïßûÍo^?=ÞS]û;´4£J¸–$Þýv¡Ôþ=2ºO®æ]&´“דÓPG¶`ëAœdüH/\\ Ù$ŸsW"€ÁHÛªäùeô“š2u³ÇüÒÕ$hT²¯¶BT8‚g.Ü(…Η¯%K+ï7‡êLÌ4Á£F˜Â4çÕÂþT[®Ø‡W [S’Ï .~«8Y«ùÃÕu(Ñ`¸a¾ œûW”è5^N÷p—°a]™ô$Ș 5Wr¹‹+uNÄÊ•]2{ öb5Œ_qaÃîÃre›y*Sè¯A­çî"aÚ2éƒ6qŽŸô)QQ¸šJó ­.è„<®Y­b¾íp%øÅo…9È1øhl•Ö‹¦õè£-ÄÓÐь @ä+ºP¡Î½Ð¼MÒcÝÎÃbR`` ®øS íÓ¡™\5ÎûTî+,K!áøÙx©IºwX7i²´>!A2Tt+j¾ç1¦Êo&‘VXMW`/„sÄ»w…ÉÕ*éó¡ÃÐTÊP¨úŽÆ\ɾï|“âJíöÜ5-}x.hÞÈq¡_Ž-8;úôpÜhfg 3~ø¯?8¢—xþߤ“8ýÒl [÷Ÿ´^ŽK¸*ðآ€Â¯‚å›öC£yILlŒzß@¥ =4¬ á[ƒù²aí0§ÊóK/ýÂüT}®Øß=¤›¸oÌï£.Ò‡¢¯Ó‡+ÞͨÃÀ€»1`¶˜¦ä´aîFvI­ßb:èŠWãäí.àÁ#®ÒÀÊ1W#)DÐ1ZiÈX%”êsÚÃã#˰’t¾%3ÐæIZàjÞñ³åÆc{‰ð¢4?Ú þàl½f7C± IS-j0®%%cE0XÚè/Ù¸'W´»§3;m÷ižp5[+BSš3YMh²-Àä³´ÿ§ùÓ.8›dzœµCçT:¢Ó•þdšÊÁѳA­êÒtk´.l‹åˆ+À S:-Mh=Òü!£H:ÜjµQí`³ißQqZ®ò’63É1%>¹ÂLS5B3›[œt@GÝfõjHº#ýñZ š«Ð›fWtPg^ pÔnÑOhP×›$Ó&¿°À¶ÁŒ¦g»¦òÂm^ûôá•.Ññ›‹\Ýg(íº0Y²…üÆŽåvh+9'‘VøßUßøqn`~Ò Íì€@:£&–Zjڨ݂FH Ý,ß|@ÒäÒûä\Äÿ»= †îü DÓ¯GknEs*FÙë‹4¤`¤ÀÙ@•qã^Íùž¸ñ=Œª x&ø;q3ÀÀ@1€¨`¸ñqëc7–¼¬—\}à~3ªDìY„ÈR4 IP×Ö)Ûq–«ƒOß} "©lo —µÌ‘ñ{çYáך1WÇËãGäAƘ0:Ty8‚“ Ý¼²Éæ â@04ãZ€U½;4AT›ÌJo¬ŽÏZ´Q<ýÎÒFŸ«ÒôÓø×gó`¯_VŒèÓ^2ÚF£Z6DèÞ%R@¡ ÑΣÎÚa®…I§Q2‘d:ú MwèúBõ2·ÌKˉ£ÁÀh[tÛ±ƒVÜO…y#úÏÏ,0’ëߟ¹D2x4_â¸Sàx¬…6dòG?ËÈfO"œ2™MædQÑŸ(TÐÌŽ‚b9†,ûõïÁ.ÿ¹Ngï6HâXŽTûÀ,~5oø'‚ÈðÒ|Q«(<ш‘¨l™ S§ ÊjéƒÇjó臒ø$GýàðÏÀ ¶ßØÝ1°³ Vñè…ÐNæ¶ãj;~"ÜíCT´ÅÐx2`Dvt©nÖü?Ô°12Í«Þ}æ.)lÜ~sgÌ9»e€ÒÙãúÈÿ;…[è¨dôùyêßß#p@U1 ª¥ˆ¿œ(‹q> /Ðû?,–tN„B-£9ûضá†sõ"jï†&Œ* 00``À]Èþ£öñS¦­âãˆç܇ûàRi¶°ž›ò[‹F‘Ÿ½÷uÏmû„k¤ áhO½v×a1 ÿ¸[{J&ÀQÙâ\g;d´f[ª>~ÿS°zˆ$eê’ÔrØÚæ[oâ€ÏPb›ÈËY;Œb㇠³öú@<œáJpÁϾ[$öÄÛýí{o܃ê.a£} ;Àò(} ½b#Uw .`+ q¥™Bˆb ÓW³9þ¶u¦Ž‚–å*ûÐì½0n(‚„Jºúì—U2@ÁýˆNäip@LæÅ…Ð!cïüÎËO~«Ì=Ý¿üÚs6vié™Ò+?ß{mð?ÏP´åÊæ]âuÊb¶fv)pô/h[ì·v>á|1éýŸÄ]ƒ»H?/ *«‘ˆBéûÿ¼ÇJ›ÎþöÞÃ×üƒëôE=qØò’éÃŒM8øþð–[Á[íºõ¥ŒÊ=ŠOÐ'Úð(ÒŒÆ|y¿d>û*ù3Ï4“áÊ 5!ßþ±VÚÐÛ®8ºâõÙŽ# C©4XΙ Áû|FËðÁY;\}už2µíË×)¸©(b¶ïa;¦¶÷Ûå‹{Á.#°Jæƒ9  ¯«#à"7¿ð:õg޽ ÎÆÎž PPó?ïèyG¾84ØÛ~« tn¿ ³,špí†æ«6œÏµB°³ÿ@Aß­0åhvÚ©ß Æ[V,ŽÅs0 †•0MxizüA¿ ŠÀ°çuÌ)1¯ëÛ/ÂK4YL yÊß?`Ñ'Ó&oóí7*pï :-0ª¼_P¯tê˜#õ>ÎÜÖÆ›Þ»->¢äg 00Pz0@áú<"i1Kõ¥«‰2âÃ¥2¼²Î€æSrã*¿îÅ<ÙKlÜ}95bD`Ù²Òoc`×Vîm8ŸÚç­Ü.Z´ëð„•(JaÃ×ÓøñÑAYUüžÀbÑÓü#L~&KXp ñLHréQ³Á|ÐoÜÖ` 5#à 3jKbrª_¦9sú„©¯ƒ¦ï½Úekýµm% :õÑÑt5NŒž••åoù|Úä-ÅAI©6ˆ0g+ÿÅA¨ñ¬½c`Hn:µ  ö:îjÉéZ „¾å¦' Æ‹BTk… _?¥à<î¹)ýË|n1›k¶hPÓÂÖ75ªeBrÇêp= Héî‹™¤—I^·ì;Vcï‘3oŸÏŠ}ú¡¢úòÍh:i‘NõoY(0è´PèÒea—Ò©9C¼n2Ý€â¼m©6Šƒ4ãY ð8(døš A™ÿ_xù)ÿ2oFT«`¹A˜Ñ¦=NBÅoù©:#6ÓaD†›½xSøÙø+<ü⿞ÿâõW¦Y+¿ÁbÔ_ÚI|Ü Ó"ÊWйŒNM—È Ròäý40`` T`@1”j_*^ÚxI§ 3¤ÝœÖÉMö—‹2ã&My‚ÆÛ›×7Mº˜¿Ê¯“~Ý("8ŽÏæß±y=?“Éï‡&G?ƒª8æ{_ƒN}e¤ŠØO=Щ!l8ÒÉ#¾Å Ôç/f'à*ðCFA¥¾¸Š]zFǃo Ÿ2CüVqó&ÎÊÀ}ü™ÁeÊ”{‚†åáѽM¶QÄ<ˆJ£)7`€ãÉqåøúùù¿uß³“zÓWƒNÝ@z¬ÒÛtjv¨‚‰ÿ>œµT<óÎ,™ƒá9¯"z+aþšâ·Õ¥%˜…+1Wôº˜ûbâkÿCVõõy*™‚Ü#¼·ïè™<÷JÓ…äaXT/ƒ4Ìii©»´¹jÜÙ¯Sç.Iøç{³²6·RåWd•'}|÷Ç:wvA÷u{‡>d.2oJØÐ»ÀaeàZ¶ìT¡B¥ÊÿW;¼’å¾á=ôÞoÝÓŸž;ÈñE~KPpÈŒ>}F†¢¯z8 :Õ3A¹©oÞ¢SCذ3 ŒÔÃXö“&¿c€h…¬ÎÜ•ðÀˆžâá‘}\Y¥QW0‚ü&;žÌ¥±bè×dä((확%>³™Ì]+X¯´{6Ïüðßo1zœ§€ê² h"³ƒ_ONEä–šâîÁ]%ÂlÌëwBÑfâGd+o ålüeQ¿V5™œÏÓlê£ÙKŃ1¹iÏQ¹zzÏÐn²jÖ»hýndAOuëˆ ÈèLÆØQ{ª?ƾphT§º8t2ÌäyÑ ™Â ÛögD±iï1ked¬¾¿NÀJ¿ŸŸI´i\GŒî×Qæ'àX¯Þ#Çx ²Å"‘ddfVÃWáÞŠÍûåX6í îÞZÒ+_¤’kwgâD¨LhCz´ÝÛ6q8Öª=F Zºq¯Ì¯2²o{ÁD‚‹i:4¸œ¸¹K+ÁŒôÒµ7§ã.É,ñwÝҙ˫I‹œ1¢ÄÂõ»ÄåkI¢C³zbêòG¶èf/“Z÷f.8í‰1²>/ü(µ÷îB³IŽõö'd&x¾ûñ³E ’j6¨UÕŠŠâÐ5(?-Ý,sœ„#kw·6@?ÍeÝž š‡2 ùÝÌÕÓ¯S 1ôÃw×}Ü=¤›¸oÌtºå*¬v³Ž…ÎÈÀqE» ¶ òå'¶jTÛb8ƒël”ÜÔÚÆs¼÷=KaãlÊYÜ£ó˜z=³Å4%çx…º–³7èÔ!¥é´Ptj1tnHpØ` m“:b1˜È%`ìøaV@Sªoæ¯=Ú5oï'…‰5` éâÀñX1sázÑ¡y¤`HƵ«‹õ"l‹9!ÒQ_ýšÕĵ¤T‘˜œ"o=/ë­[£ª7¼§Ô¤PÐpÖžªÓØXum0¾d&lÅqÛ¦õÔ©Üøû‹ºúnëƒ1é0‡ÇÅvŒc½ÿØY±qÏ1ɸG¢Le~T9,DŒêÛQüãž[@?YÈi±S>Gá€Ù»™»`Úc·É°:m›Ö•‚†³±VíQ#3vPÁú¿š·‚Ëq;‡JåCÄÏKo„ÀþîÏuÈ$]F<õ·A¢VµJbÖ¢ ²}þ\ºz}X‡H* ňÞíÅ_ _ 36:µ¬/ËêÛAŒ»µ‡õ/PÐàæq¸ ÂÆŽƒ§¬Ú¯mމöM#sõ¦8ô$ÚAÈ{î¾Á¢ ÆŸcÇpšž¢5ÛËÐã†÷Ã{µs—o._“xÖ)}(!Ãã´P„ÙW.âugöÕð-à5J 8ÞÜÃGÞ?ãOZðÞø›,~‚[^0è4/NJÕ•‚ÒéŒ×¦LàV\äØ#ÂâÖéóÏß~s”Ô­•˜·r›xù“_%3Æ—ÚsäŒdôÚ7‹Ä>˜+bGÌIëû¦gd &H fvæoj( 2¸ZÔ—+åÖ‡p°+êÌþûàˆ^`zëÊUFÞϯ=mÆqÁ0™i–ŒWu¹’˱¡@иnxž tn)ƪC¥– «U¹ÊŒèÓN´‡`I¦ɤ¶„ZƒYm׬®¨$µbû!„²­#h‹å¨¡¨ A ¢HMÏf 2Ö·ön'ëíÕ¡©¬¶? KK‘˜”"5Ih޽ z´m*ÊCãѺÓçÄ%d„VÐŒsÿ¨š›Hê'f†®^Y©Q•a9Uqoìù!ÔníCÐiâ´_ £ZÔ“æUÚŽ•>¼ øo.ªU*/Z7ªƒzÍ"æøyÐû¿÷Èi©• ¯R^&î«Qµéó˜éC ž^>µcžÏ1iVi5+Wï³< µ¦” p¼9î!Ââ­©áқè”V)i¹Í˜ó ΓM³=Èï9{Ï”Ökž¦S·›QeYÌIÈhÈ Ü'€vÛÃz¶•&&³o3殯Œ)54szã«?o¼‡Í[QQP&:vÄœã$5!÷í®n[÷ ×®Ãtª2&(ë%y@MI~íå~Bg©é–,sV’£žyš>Èô·„ùÃC§âà¿qJPxô‡©”®^O†–j#„‘'D]@IDAT8dÙ†ÉQ²4kÓ–¡FƒRNîÓ ñ PHÝvà$ÌäÌ’‰§³17j´‚`r5gÙQã}øtœxrìÍò™‚ŒµB¡õ"Ô©‘-¨sú%l¨1ŽÀÊ3µ Ïø“8qͯ–éߦŠäÙ— ´:µlûþý`XCíEÖ¡íþóç´Écúä×^ž‡tváʵ$KFzzºEÈÞ Ú©·Æœ¾4!¢-,ß´Kbúcc¤ŸÆ;ß.´-"Ê–Éû÷9 :¡ÅS(mþWnÙ+$q»Q>$‡ÆRÈ)‡gÿ~×@©9aÅë2p@Ô‚¿)¯b2°\vŸžFû4Ó-‚½~kËyòØ}9“ÂF•ê5†Í^²ÉaÓݒ£ lrá¡=Lž¸×BQéƒC@æõ#Äs÷F*ULúàGYµ'èƒær4ëÓ¡™ÔÂjßIô±eï±ä¦­ÛU=¸{G,úªm·õtL"áP þá+„ä&=õÖè‹Û0í¥ß…„:’Hö—âÝÖ§ë†N¹˜óŸgï²~“´Áy¨¥'ßEAƒsÓ¿®'b/‰»né"ÚÎ_¼b}IGÏY ØÐZᅆɫó×ì„Y{2üo³ýgkT©`SÚ=§ûŽÆŠy«¶yMØà[y’Nór+®Ã+9KFZZlbJš‰6ɾ´»ÿsí.q ŒæE˜žÐ¦@¤­?¯-‡óoÌ_®AËq«ÓÎ ªe=ØC_›÷Q°“·Íê×D $±tÓ>‘›úp§_”öìÕï­ksŽ}zJ²B’¤‰œþx•>Úy]·ó0$ LIòš Ñy: ¦N~`ÎèŸA GA ájv$' ‰4]²úìàm9a®Ü#º¶n(:Â?¢JX¶¦õºj¬Ôª‡ñ@±+çlŸœ½6æ_ŽÞƒš82Õ\ù¦ÿ€b>•/îõ|èƒDÓõÄ«{ñ?óçÿ“вa-üÇ3¤3?M¨l¡¨ôA ?lÔ–QµdãkÕž 6Ö¦š›0Dðj:ÈBùlX;cçÀÓôAAìëùkË·èйº£V†õ*p^ÕŠ1Mg!Ø…–)cvh©¤_â¸ÃY‚jKeFEÚÐ-èŠN9çýë³yÒ€4Áà<Ô"#„°4Afà 5±\Þ«­45¯­FˆµåÆûŽžã={ÀG.ür£l`Ù²Öó€?éùÜû?Ê…æ™ð³Tþ˜ìÃ'?/‡)êY1õÿæÂßn³œÇç,Û,^úxŽxæÝ™rOm ý0ÉÇý€`- §þÂG?I?`ö‡ïÃzã¡Qæs<ö„N'FOzdêŠÛ¿¼K³Å­1ûy2’KÜ™SkêTyj‡*/½0÷Á7c1V§i·]ÄȈ(ukT‘Uv¿¯Þ!‰ŒŒÿ êäµ÷WïYΠ´Ñgþ†ñ£û¨Ë¹öŒ"tvö4¿ù&6dÛÁ®ž¶ÿ…m/WÅ^>á˜c219vKû¹V%É0¼F­0QQMÛ¡yý<ækìXoD—úßïkÅSÿþ^š ˆj)â/'ò–S $£—q †ÇOM$T+grÒM«¨}x~ÜP—u`¹2Ò©ý»?× Nx2‰-'ŽvÚoÞä¤ËoF*¢ùà»ÏÜ%}‰ò}°ˆœÐ‡š?DÜ™3;ª„GÈÉY™±¹B=V& @®8ÑϦq¼ÂhQéƒõìz“ @±`ínÑ»C«¶ÓôA$ŒÐI0ˆÀû3—H’þbÇôU+f›t9B”§écç!é¤o:yôàzôÉÖ”ÊQ7½y %¿©ÜÊ`îã¹¥à£ÉØ+zð-ðÛk º¢S¥P¦¼*8OÙ²¢/¾Áœ/ §àwH`P{àè9{e󻦂Ð7’<àŒ_VÉ9º[›Æ,²Ñ2 ü,ýÛ¶ÂB‚fÓ/<0TòqŸÎY)µÖÔÈ,ß|ÀŒã*™˜§‰‹=U*„€o¬ÍÍ% é.y¾üúå®ûùÑ©9C¼Ž€ðl~@qú=’Å©Áñ³d(Í«þ˜»·Që¶—¶ì;V†${Ç=ÆreˈI`þ0Rb-¢×r¹qÕH˜FŸâfž›wŒ&"´­ 8ˆÏ-î€c:U‡6x਽ì»úþŘ[,fó•­k–Ç §’röì¸ÇéƒÎû kúÃI÷ªSÉTúÒ8ë9HšËqEƒ ¼lÇš4 }öYD¡âª8i‰ Bâ~õÛ_˜dB¥ùéŠZ²W>ýš“XÉØ:kÛöšC¦m¯&"NiÏéÜý꣣­NwôÉS®r¼öØýsîeïîfãÉÕvn³­\]p’}Pà0oY±òP‹/®Ù~¨ ðàÖùƒ‚ŸØ,Ð4NAqèch6b)(pÒŒ@§èƒóË£·÷—¾(tÌ ‘!˜³{¡}^ñ&} ø†Åœ•ynûš•‡Ñ%l¨…Šìëç—´ÉM1r6t±’­ •ºžÈd”d HŠ>¬ )ž@ÇŒéS'Ù´£ú¡K:epž`,zqáuå–2ú"µþñ—®Ê *Žœ=góî>eðj]ø¦–…Áa(ld  ¡¬üÞª­Dm”aЗæõkÉ2Ê]Œƒ9T0ŽAXp¢õC,LÁ¼Œý͇NM–ÜL°|ÃÂÿ¸¤;Íò%™ì3/ž‹µ×dzâ0Ì3¼X;½µs‰Ì¾­ ¡-FûjWó98ª×ÑuW÷ÁUõq¬÷9c:{âøï¨“öªtô$£ èBíuM¶‚ú_ P‚†ma:‘3iË g øòhÁUc}CÈÐÖžÿ±'|: Jéé‰q§N~Žoõdš³QK¨'(*}Po\p;ºS³Af’ˆŒË—ã¯ÅìÜúoSû¨×¿¿ÖòðèÞŠàu€c£ ®ÆÇ8ö±{ÓºO¯Ð³7Å06J}0ß;ãÕK7‚>(œ¦''_½¾kËúkUì=+Xw”T[xƒ>²ÿö6_ºrÝ´míê“’®¦àªZ¤àœAí†^ß5õmS{½öµÄô‹yê" xQ5¹nCÄ 3:-]¸­¹BT¬ínè”&Ë˽‘¡`éï¸q÷QùJ ÎÃ°ïŒæ‰o‡‹hT4[º‚= /_»ze‡Ï'Ö¢ö‚´n\×zßö€&VI†Ó·S ‡k‚ÐÇ T0úš4Bþ&.0Ò<™ Œ|¯5ÛcV?IFã¢OœWÀCtê.aƒ8ãGA2 ا®]8ÿ¯°J•¿ÆñCb®°Ü7¼‡ÉÝ6áhËb€+’d$·8n:qpÿìÍ+ªA†g¢°ÁXã¤Å,ôd”&($}±$½0Œ]òæåK¶a;ѶQÔ[‡º¯¤ ¥‰ì½+„Ió±ûÿܶfÅëVïÎ5wè]ØP¯ärÎ6WÀJ؉Ï]¾MŒ»µ»ø|îjk¨mÕëv‹¥È óÎ3ce@“ÅöÈ$ž÷ ؉¦ ˆíÒéWÈè>ZøuóÈêýLÜe1ý‹ßdP‹úµªi‹{åØ6OƒW:á¼Q—Ó‚óærß8eZ?^ùtúÔ¹ïÈ3¯öͶ?4]wœ‡eŸúÛ DvÚ ¶i†Lsã[ºµ‘‚ˆ³çlÛÉï¼°Á?hª•š–)Þÿa±ôó`Kúd0 ¯£`6苲1'ô³üïÙóíͯ¯.¼ïvZp§°ÁW²ù¡ ³™´`æ7³n{Û¼÷ܥ˖±ƒºøùŠúl€ П¦SÔhÙ÷ó²_fÓ|ŠZ &ôS†Òl°&ƒ>ˆ…RE  £jþàê6i©ìÈ1ýüî[¯$<ñè¸Ç ïpPJð틯™‘‘‘öæ;ÿý.zÚ»›Ðÿ+Ø8wpìµÂNK70dñœ¥[ hôÑôþ¨ºK†¥¸‚1'%3£pÕt'|ÄîÜUšx°Ü „uf¯êÒóÞ6Oƒžûê¾™-¦)9íÚ6¼Ñ¥\mÒ×B”ÂYpj~Éc¡I?aR:Ξ{ÍI^4>ÿàˆžª¹/Lð†¶ý÷7 Žú!q`Ù·ÕÐVÐÁ}x¯vÒ'ÎQ0F*e+†™gD@Ý‚ÅtÐ}s§°¡˜šÏHaûr‹fÿc§>Ι;wŸøîw‹*ÂÑÆÂ(3 ª%W¼œQ‡{1À< _ʨBt‡uâöu«¿Ù±v5™Ɖ妄 eF¥Õl™4èH(‰àú @JíçÎU ïUöÏÿ믥+×^lÜuÀ¿êÕ¬Ðù¦†¦¶MëêÏŒ5À1ø‘ÝGp¬N›ËZRw?óW~ýkݦX<Áyƒ)ÍÕüAíç Òƒš?p¨kpËJ!µŸÍ]…ðÉ­¬Ñp:4¯'óA1Ù¡D™'ŠÌކÙדÓÄeäJãÿs7rqдKÑ*ÛuŒƒÑ·ô 3^›2Áýs§°ÁþñàeØ^À–UËÖîß²)¦Ûàaò²2‚Q YÊ•³0㪑‰¨Ó/¤fdX˜ž ûÉc˜xò`ÌÊ ‹ÿ\’œ|+’Ws62 JØ Ø2 })% \HJØà‘+ÜJà(³hÑòCæð¹II ûÁi°Ú7ó?@æ*CL•ÃBM\­ÒFcNæ–`2N[઒QÞsøAL}Ë•ëÉŒ‰œ?23Ò.~ýöôÏrƘóç Î!6h†IaƒGz(µ3ÕO~^F¸˜ìöVŠAP Ç•Œ‚ÒjØ|p' úP˜({WÓ±¢hDi*xM+€áœvœÏ”ÀÀ½L8ŸhÖ§êd=œð£¼ëñ£ÆBÑçµHÁ¹Cm0”¡æŽŸ+Õ “ŸVð±è MÁ/`ðyÌ~Bûæ‘Ò”jõ¶ƒÒ”CkB¥F߈ÖÐ@ÐWcìÊ„ùGqZ‚—l’¦Vô±vÓ*ñ ×DMþ9ùúth&Fõë nçÚÛFªŒ‚óí¯+¶JŸŒ3q â[{æ*¯NêÕ¬*^|ðV±uÿ1hq6ˆJkƒE›¦‘òöy´OÍ!.!Q´‚I—ÛöÔuco` ´c`bôô¨¬,ËçÓ&o).uQ±;胈P4BCµA£P«æ2´JØPÇ8Ïq¯®©çI›j՜ה€b”¿ñw5~îÕ¼Á¹‚c¨ ޝš;8Ï‚ :]ïElDÿŒtC¨Y­’ŒÛ¿jÛ<&TÚgÛÁ¬éKø+„—ƒmy ˜ÝEjÚÃ<ë¿vÉöé¡…]‡O ¶yíz²ÔjtÏ1©r–@û¼:®,û;ÿ¯‚&UÜlZ¶× æUÌ‹P9l·(W®,Å+Áÿ#X,A(àÑý:wg íH75®m[EIHOd~U¤;ζ JàPƒÒTpN!ðyEoŠNy]=g”w~ˆ_‚G޵jÞà¹n 3wà±ÒŒ’s̃>‚Óóh):åDU¢£øŸkwÉp¸Ž°Ñ ¹4üýM²Œ6ZŽ£ò¹Þþ÷·ö¹á´®žkP«ºøpÖ5Ò1½Mã:ò–³üêYÛ}L©¾ûcCmH–9K,€Ðóõok3<³í>í›â8@†Lýú÷¿ÄËŸü"“ª …sy›&um›(qç3¦OTâ^JóB0Ý¥•¢¯.“EjÞß#‡&‹Kä—TRˆVxÅ,ðâöE­>*AC}œ Ñ„QÔÍPŒ™ndÖ8Žj¯®s_X0裰Ó_yOч¢72ªjÞP±¢Ž¹×Þç=éSKoò"~8÷¨M;ÿå³1¤p£æh…·üð£ÆƒåÞ9†JÀà1ëPõàÐ…Û\LÀ§ÍOÀrÑDŒ›-hM•Ș}8é^kFb³­GÝäsÚgy½6´¶åáð/é  [ 9×ýÃ{" \†Ð†¥“»£üÇôµ­Fžw‡PÃM ¶y^™0R†Õõ3ùårg(Ý©ŒBµ1ZAËQ{ÚvŒcý`€þ@Lî·ëði„œM‘Z½¡=ÚJSBWõÒ’EºêU=V§… õbê£Âg*õáR2í‡I=cìõƒ[†’çjLy\\PuôQ\LzçyOÐߌtBAWÍÜ+Ð^³=VýÓî=§-§Žò7ð®p¢Ý;¯«r?%d¨kê9c¯s $\K’‰™i¼/òdT©X>O-øk’±× ÚBÎòhË昚 G਎Ê×õ‡†vÞ¸÷¨xìö~Ò<îðé8»fuÅé9éò?ÏÞ­‰cZ*Ný¥ñYoc’˜¬G¦¾ÑÞ"2Ì_L{y×#/½ú0¦§»ì †%+kÖçoF‘sÏ ”«cñùÒ2²ˆQÞ­ø!£ ¢£å0`o’çñ®û‘ôñèÔ7Ú"—Ç-“ˆŸRô‘÷«æº6šŠ‰‹° ™)V˜-§üüü}öÚ”­Å¬ÒÑ㤠æN&sÖŒè—vŽŸ2í\Ë=äÌ 6ó¤W£<1˜=wz?rÜØ2æ9ocþà5|Ç>w̶è1L“@P½3>—ƒ&Å®ÄÀ‰Ø ¢[nVë¿ã®d‘4ך³l³Øy蔌¶FÓEš2·Ì÷ Ö‹Óq—CAßuKWkW¾sI¨ËkÂÆcÑÿ Íȼò梉 øtUK0'ŸNùkÇ¡ðÍ{ÙíWT«½>}ùô'ZÄ3ƒ)Êk/YòB†Ht5~°R•åEíí‹ÿ‹ˆ½x¥âø©¯ýKÔ®úÍŒ hQlˆŽþ:ðlÆÙ';ýÙLsf„ÉÏd 4ÃQI©3@§@R?ó•kÉ–ÄäT?ÐÈ´ S_;µô{µËÖþ(:úúXlç:áUùëzÿ<1ؠ§%^x¢7{@ó¦×Ÿ¼ÝÞ-ãš10qÊ´~lîÓéSW8hÖçè´5rÈ,Ù¸W:Ù·}.š;’Eo¿­Ú&#® DHéjC¥¯3QlpPYñÔ߉5ÈÞ0 I%_zèVÞ2Àv™z›2.?åJ¤Ù|å'a1׋Œ·tE¬nHŠ•IõŒ®ž_ãFyçr~`2É0£µðA©uÏЮø·ZŽœŽûÜïÌÅWy!úhŸ–ã~‘'±G^ü× óY±_¢ŠZX½°tjÙI£j™!ÚX&s>Üz¹+…ÁäÔ4™%x˾c5ö9ó6Æôé‡^ˆ~èË7£— £EÖ„óÇavÇÿûFíî™Ý=hûïÍc$·L…àÍy0ÖÝ›ý2Úv?RÓ3,pXgx¯‚Ùbš’ÓGÂEú2‹üS(þ7­xùÓ_E˜ðÑ·ˆ®;’E›`¶Õ¹l†tomϤ”4Áä˜ÇôåÝ­KëFâý‹KH iϤÐú Ž D§ÓAWtÙãÂÆø©ÓŸ0Y²Þ«â÷àˆ^Ì@jLÄ®I/ÕQ¯f5ñÏû›…б%âÜ…+ ÁPNCù ºD†²0B‡ f-O#ô¿#ªUwìŒDV5 úðÒØ·Y‡¢s«ÜL‡O³o ?å‡_ü×ó_¼þÊûE aÌÅ}=ïâùC//'ç¼ôÔ´ËW®&†^FŃý¸r-É’‘žž€& óýs}MgV>K§ÌRÿêÄQÒŒo1B‡†ŠþQ-°ò<’ZÆ#ÏÊ +.ã.]³Û&o,H²ÈÌÌ,8¢§ÉÈTÖŠppðÄy$•´ˆo ô(à7ïLüŸ6 B§3^›2A½_qöž6L5*W_¯V5ÿ;oŽ2ÎZÅ:}=Ë?ÿ¤û‡øþëj V±¿|ðù—ƒ¾zëÕOÑË‚ &øß<ƒÿ׳Ü7¼‡É]!íô…¹ÒÑâùqÃü¿¿Ö²õÀñwšmúòh t.èÙ˜?J(¹¸`þÐfHÇŠ–-©ÉISÒLÔð‘ 1 t`€ãÍqOOIŽËyc-]è Úþø,2ªØ(äM9r:^Å6°K+àï/\,’É0É‹\¸|Chá –ËfŸŸ†–Å6¿Œ9¿>xšNI½ùõµ°÷ýÒ®=qTËû‡u7ÂbÏÊóÏÿØíýMCz´¡éStYå?ÈO;a‚6d‚’½Ý±y}Ëã{‚†Œwa»È ›cË1öóóë¾g'ß‚:h—}°)cþ J0cþÐV¬B:Ãó©ÃwaåÓ´çÈY=ôÏ胇0Àñ渟9v” õ´‹mŠF<Իͨ>pï“tºrëAK FCcrË3ñ "¢jù²*YäÁçD–Ù,ÎÆ_Î#$h±Rd‘,ÏÅíNÊvYç.8Š3 C8/X·KšNщœýñð4zBØ #áwxϺúeÊ”ùBO´é+ã]âú ‡nqkïv¦ñcú?øûœÿ5Ä Rüw6æ¦{ïýg0hãËÚá5%)Æ åÂÇD,AÁ!3úôŠ›ÎcþÈ…½’}R„ùCO‘Ì:Ä}ÖÞ-Ž˜³²®ÂgI1xzê«Ñ7a€ãm1›/o]³<MhiÂM-ºZmŸ|ŽN®\ÿû}­xñ£Ÿá½QtmÝX îÞF"¢"kTïÏ\"ã[ñÎw !”0g´c`²ÈSç. ÔpXè˜tW,,>@½Ó>ÿMìˆ9)Ã9O¸­ˆ½pE¼ôß_ÄßßþNü´dM‡ÕèꆧéÔfTdêDF¼ AÃYª+rskgBûôé:-܇“›ZI±mÔ¯LÍÐ'± Q“>†é”-zJÞ9Çxì Îþï~·("²CË'ĪyïäÐMªlÁ˜?l1R:Î :èœßÔÇÜ/2Yá©#‡–øùûßNŸ%šP²1Àq†±éì‰ã¿+À^}ÿôÀ†ú<Þ‚[JZº*ÇX57À]É"«T“Æ ErÊLa†Æ„É0 üO¿úèhÙžÛö‡×ô…¡Ó‰ÑÓ£²²ü-ŸO›¼¥8ïâlŹ8õªgM:tðá­?o6›F«‹Æ¾t` 4$äÖ¾þ¯ÒnØÓXþüË” xªU£Úãc\:è‚oɱ昗 z§ÊÜÎv>2æÒCyÞ´óGžg¼|L% %l¤¯[0‰93ó V`Í4³0 äb€ãËqF¾šËkÿ˜7o*NìI¤ oM¹¸i¡DЩ3Æ^%‹D€*—B¹²VAC[1ûâ¬?Ú²Þ>.,š3Äë&sÖÅí·íǽ¸õÙ>ïײ÷ —®¥¾zìl¼»Û²mÛ8÷2h2W1¢Ö³û ®Œ®Ø3•1ÝýÄsƒl©O ^~y£ù|1À1„¼B SËjKÆü‘/KnÌzzy¥Õ DA&“ùdÒ’“¯o_»ê;šZ08‚ž:lôŵàørœwoZ÷Ib"l}²i€´@šPÚ ×6šOm3¦OÄMSÌ S 2Jãa¡éÔd ÜŠ îÈ8øûôa¡€j0G@[;ÆNöØvàB¼×uw[»÷ž¦íÛ=ŒNÚ ’>ÊD|l òhèú=ôÐ9Ž77{Àð~ÌfêKÀ1çØ‡Tˆ~+í†8t9ø~KB_Ìz|=2rüÀ¤c£°Acñ”ëVï8º÷oˆÂfúbîj‹¡áVJp<9®ß÷ÿ¸yåRj8ö¤Òi‚´¡0èT/#áÁ~x›N‹-­8Áp Í*‡gåÄ sò€£[¿®Ø&oØ#F÷ï(Û©rŸÎY)“©Ü=¤›ºäp¿~×a±|ó~ bùѶi]ÚËòÈv,f-Þ(ö‹LwO{¼nHÐÂì”e²QD N-êáZ‡mÐÉèõ¯æËûTãU«T^´lPS ëÙήêÍ^E1ˆ¢P/¢ªt>²w_{m˾ãpF2‹Ž-êk/ëê88°¬%=%¥:EDªI—“¤ìë•Gfpo%ìã¸^IL¯Œ%ªWÎv)Šªxôõodâ F¡p™YYˆPqIk;+æ’{o‚½¬½ó×ì@ô,1á¶~.iË•0(Ç>%)©.Ú£,?Ì\äæ²ùuà£p2èñ¸zMÚ%“ÑdR· låVüúó"°› o=wé²eì .~†Ù(°áã@ÛwšNQ£qž#Z6¬%®§¤Š*a¡2ZÙW1öá•+ˆ‰cúÊWûxöRÑ vu\$âü¥«j7ˆЈUBX½a½ÚZµT¬·vxeÄï¾.þÚqH<~gÿ\¨a›ÿ¸L´Àÿ¡†Å‹7!J†EÜ3´b€Ÿ«·ÅˆÖk‹%È®çµ>›‰^xOÂéó bÑúÝR“W¶L!ù‰: WÕÇ\ ¹ù„cï'LT)iéƒôÀ šQ×Ìn~ £z7aÀÁüÁ•Y½ç6jp)lðûª6ù ܵaíöC;wî:hHŸ¬¬Ì^`VÃhJX>¨œ¥"f¼=¢¿8À@jF†…á™°s» ã—xò`ÌÊ ‹ÿ\’œ|ý £+7fSÂ†Òæã’çaâ”iýØê§Ó§®°iÝ S„””S½Ò©»… —1‘dÜnÐ+ÿ+%ÓT¿Vµ\´qììñõokÀŒ7VS2YdÞ£'ŒÓ_ 1’CÄgsW¢ŽN` o˜¼Ä^¸,k%h¨J[À|f®e«ˆ½x¹Ð†ªƒûšÕ*A¨(NÆ^”—™á’Ù&{uh æ>KÌøe•H:CcÓ©e}Au×(˜jU©"ˇ…‰vÍêI“¯]‡OK!ñ ÕJØn\й¥{Kñûêâ7¬²?2ºÌ¤i¯⇠è…+‰âɱ7‹80¼eÈç ±fûa¹b3nx/q5)Y|÷Ç:Á$9ØŠ;~hµ+×dx-À",.£‘¢ö‘Œ6ÑŸ—nw ~Zp„ŽQ£:Õ¥°0nxw™àg3ÞmŽKaãDì%é´çÈi‘žÁy©åêÙ¾™L8ô1èf"â^4áûâ×Õ¢:pÍq»–” ­ØQZ¼¡=Ûˆи) ðóߟ–‰r†Bh!°ëüµ‰ð\LÎy-÷@x£sF›^Ç€ùƒtAfIÀ?¡Òn¤à˜ó7ïe¦¤$e@˱ÇK›ÜÔ¶^d“fÍ‚BÊW.à@Mˆ:Ä@fVfZZJÊÕ”ë×°qÿÁÝÛ¡›JƒE <æØ+­FöÄŒ ž³Å4%§Í6m³OÚ ¥$œºœN-¦ƒ®À‹;… öÏ„çn¬îw)ngiæÔ¶i¤hV/B2H/<0,W•›ö³WNÜ—cöTN&`9z&N†ÙœüÀPñ¿ùk¥ö€vø\fìäk×SDhP`®ºx¢®q¹¸†,“dP8 ðt ?’›‰}È:I! V² ôÙ¨‘““Z•þQÍß9C´nTĠ@sü¼ÔF°,MʘȆ˜”"}[ä ~µCf:áêu¬š'!‘ #ÓfÃ^0¥\ ¯R^nìÃv$¯Ôõ&U¤H{fA}|É0øåØ1©^W=DÚº Ú¥—?ýE,ƒ_ÏÀ.-sUí /•ÂB ^±ÆÐoÚZ¸n·¸z=¾g¥ðrôÌqèTœ•=jY¦zñrüŸ¹çÁsúÝì„–mýî#RØ`RÓ2ÅôdžfVV@ó?&4⽿èÔ4oDŸv2óiæPïAmšÔÁ>Nümp)x4®.Ûõ‡&Í{ NÎEì„VÈpÙüá½w3Z..ìÌz6øºd丢Íh#ì+A]£?¯a+whÏÎýØø1Wó"­ÏðØïc@ ¶C-“Îq¤PA ÔhpOçpÞ# °¼÷Àä4‰²¢IöÕ Sï’«Zv ÎxmÊWtÐ݆øþý·ßüæõÓã‹ÛY2ç&~›öÅob¾•߯_FêúZÕ³™už× ¯Â¸t5I4ƾÌTžúÛ@½‰&DŸÏ]%(°ðº="1GȨYíF²Â"ü\†ÙR“Èù$™Ð™ð¡ÄŒ”—áœNÁÈì9|Ìë.—pÍêÀ ÉÕZ¼&´& (¸$B¨¹A‚¦1ŽÚ‚l›)`Vg@ÓS;¼’¸gHw˜ÑTC|ýŠŽN¨*¡ùàüYt¯&¶¦$ŸA ¤5F¤R\­ü¸b\Õ$WôF\ð$…º[ºµ–fx]4™D‘l°PxiX»š *pôô…çD'Ô3 qðx¬ hÞ—mÊÆïèc¦ Æ‚B jM´‚¯Óü.Ó¨‰Q‰…TyÛ}$4$„ÐlúJƒÆƒÐ¢A„X¹õ€ìךíECð&äЀb¶}È.¹jþðæûmNæ¢Wêþ'ù±âª±žk;2¨´Íå*W´s£.æCôÉ€ÜPcÈqåXùåp"§É”Ú8¶dÞ•V#›qÁ‚A§:”bvI·tÊIΧ ¢ZEÑ»Cs1¾ jU·ö~[÷Ÿ´žÇ%P£‰•ÿÜZ ® _½ž*Mf²°¢] ýŠZ{ž‘Ù¬kU¿ÁÌ«{…Ù“yOHƒÁà–oÚ›ùKX±#™Îw¾]è°:ÊWô×h^?BA šOÝŒw¾ 3iBуÚë [F¼‰ó‡/Ї–‘SÇJÛAUj6°§°ÁM-ĨwS{Ü2À‹àظ×j5(LP¨ pA³µñ:Ç™B‰z‡ºE›ì :6èT·Ãå°cŠÖ¸×%ÚçF¾>n ïÕFlÞ{TÐ_¡G[ê-¸Z[K,#¿|ó˜ÕK7î“fUHB¸ ‡o2u̳±ûÐIé‡ÀèP4+¡Fà[ø'üí–®ÒÉšæ&4…¡ƒ4W¥ÐiüøÙlß ^ãJ4©mŽ¿´Ñ??kwÃ'£šèrS#YŒ˜°Ð`øøI»zj8Z7®+ïѱœíQÃ@!‰u_ƒï5 ¬oÉÆ=¶M‰Ô·nç!)xÑT¦ 23³gíP[Rµb(üI*„'~gd½ôÏØ´ï¨ôyióæm Ã[\Ÿ TN$ªM½ƒí¹ºîµ=ñM‡Ï~YÞè†3¼Ð—bÍö‰o2÷ÔR5ƒp¸jkŒ7âŽ×X°Œ?Ã[ËŠI;@ôŸa€ ·tܧá H« }{úüMÒ9½!œÍiX 3|#<׳]ShNüe¿ ó¼ËËfûl°Z]Уx–-›Çw§0ïÍ|(ÔÆê9,5߇s]]h5užýVÑ…æ_Y˜ÑðZYżi—‡­ ¡¸½Öi£a»PchËÈq<¹i… –ay_ƒN}e¤òï§néÔ]†öã V±óGSKÐ9zDŸö’ÙRPظýæÎðYØ-µdÀÕG®ôgdšeô:CÓ,…šæìPðÔ]‘gcÌŠV€©ÏÖ~€“öàn¹}6Â/„›‚艣r9îªëŒÅ´öd4©=Ô­••yí¨@´¹êßßËhU¢ZŠøË‰òQ2ª\Agä£ÙÈûñî3w‰ð—X †”BKïM¤3¹j‡û.75”&1ßý¹^2¯·ß%o;k‡Û ¬e/ôËf—@ßj:ÞŸ¹D2J4Ó™8¦Ÿ+„ 5ñª½l/ûGfTªCíšÕM¡á¢€ªÀ^(¬®…6dòG?ËÀOŽ µÔ,©èO*H4oSÚ8æoaÙ¯ÿK¼üÉ/R¨£³w›&Ù‚§jÛÑ~8"Píƒ8i‘Ô X¨dÔ*š‰…§ˆ‘¨lƒ$¦N”Õ2’n?ìõ•Îû ¡‹(šUr!‚ø ‰‹…wäÀ)L{ý¥†•Á8þjv?Bt4F÷ë¤ã\=誚7Ô^ößG~ØgµÊM&”«ÆdL¹Îï¯Òh(ACû?Àmt‚Ž#7Ž!7Ž©Ò`p¯®sï‹`Щ/ŽZÞ>»œN'FOÊÊò·|>mò–¼ÍüJ±'¶ñS¦­bs3¦OíÃ}°^Ї{ïàw^~ò[2pž‚Dá±gÏNAÂÁ49ÞAˆÙ÷z–ŒÆ„ÛúJ-;úÍ4y²Ôbp‘W™Ñd"jµÎòt8zggí8ºG&$%-«ôV!É^? zÍ?¸N_”Ö€’?¶¤°ž›ò[‹F‘Ÿ½÷œêœá…~¤9­&¬ oÃq ¬Õ¦ôÙ–Ûuè”øau_7TT†ÄÀŸ!"£^9 )]Ø6 Sþ?ß-{bŽíþö½7îÁs—°Ñ.P1b›?ɉCÇ`¢5¾«A¡?áÚu˜<Þ&µS…y/FÍct°Goï_˜Ç–¥©ç?ß›ƒ''x„ÃÂn0Z_5N s­€s#s;UÆÛ{;óéC2u¾?nïr1Úå·‘‚….´ÇÅþ»ýÅKw¶ŒÏ•ÁãBA1h¨Àí ·YüÙ¤?”]РÓB"LGÅ]K§/M_Æwƒ£ø€â¼£»5~áuê=¾`í.¹Z\œŽæY{‚ŸWÌ»³º˜Û€«ùïý°XšV¹‹s$hd÷3÷°Ø3Õ²}Gïì¬G÷(ÔpµÛp"ö‚èÔoPã-+Ç¢>NÌ…ž”]ÑWÔá /ôƒ)*8‡¢Ö—ßsfh3hVH3æo¡¹Ãëêø¡“;OÌ\í_¶i4QÍD_˜N²5£½ÄÔÿûEj®¨¡´—û„(æ,Û,vBˆ£ I ŠgB‘ÿAsµ9ph¶ÆÜ*Ì»C É#µ•Ô¢0T75(¤!{9pèKd/½þ”Ͻ¼<œƒ·ì?.…Ú »Ž ”vÑ ÉäþõÙ¦D¦V«ácâgññ §um…Ü)­ôÔ%Û¾¨yCímï»ôœ¹vö´y4%d‚K¶¹OèûÔ¡y}1¤{¶ã¿*Ç=µ÷ á"rÚ™çÂËh:1®}³Hy¯U£ÚbGÌI)l8Êc/¶íc9ÊËÃ9­2Q6F 5[Zذû¨j˜Ÿ‡À@Ìû¢|ºxÍQîÞs” ùC Ïãž›2Ð/°Ìç³¹&4Z†°¾©Q-üŽ;ȵuJA$ö9+¶ì;Vàß>ŸûôC/D?ôå›ÑKпs¾ úòèe÷]—tê.aÃ÷‡Ëxw`€ÜMnÇ­u(*–‘¥™£Æ®'§!<ö\(ÚÜ'ô¥â}Fs²‘ÈL¯€ Gi®Eÿr®Ð'ä¯þT·­Ë2örà(Ó«…oiûëùåå¹ñdî#ú„$ÁïH9ó.œµÐ¸ðžG¹[Ô}7ï}qþ ÀÌ¿ÿƒ/¼ü”¿™7#ªU°0_„>Ón“RW=ÔtFþ$l¦Ãˆ9{ñ¦ð³ñWþxøÅ=ÿÅë¯0Ì»vaÍWðcЩ¯ŒTû©7:•Pû^˜bŠ¡Tû³5â³™Y™Nóò0' ®ýŒta ­œëà)™{ƒ y¬rÙ–õä¹ÎVnìãÏ .S¦Üë4,îmÒ ˆžÄ£Ñ–{0Àñä¸r|ýüüߺïÙÉ ­¨ Ãl1MáæäÍ :u‚œ’t«Ètj1ÜŠ ö—ÞŠY)WŒ‚9--uÌ:¿JÏÕ{á2"Ìl•–*7²FUñÐÈ^¢’ë¹ æ¯ÙY¯ÛÏUU긙Kƒ“¯6t#p0CžÒ‘WÙ×+DNùï/’)d>Œ–`ÄX–³­ká’¡JÌÏÕl:áÞ §`ÛUàSç.‰×¿š/åªx58 ‚S0s¥Ü‘›aß±Xñå¼Õâýþ-—ó²lP¿?Ÿ?èsÁD›L:wùV©UhŒÄ–ŒLç,Ü0‡ùÚ‡›†À±Ì/W µ™'ˆáv†&‚Q¬qŠ)åÀ±—ƒÇvøèœî,/Ooäð™µh£xúÄ}úÃQ¼Q®*Æâ]¾Bx\æÞÈ‚àܼ>r ¸‘(WažØ?H#z+ײe§ *Uþ¿šÕ+YîÞƒóŸ%_Ó¹KWÌç.˜fôé3²ÅªUó®ãUiƒè=Z5YœÑœA§%”½Vaé!o'8«¯ ÷Ü%l°}.ñšg~øï·¾{óôÃíÊ1R£ÓL~p˜´c>qî¢K ¾ã#zzs òšÃ«„‰­É)gÐ '=%lx¬ý‚4DFŽ‘ƒ~”Œá`“‘¯¥ pîböJô=0ÊôÃYKE“È#<=<ª·Ì4¾Ég-Ú ˜¡œy6ÜtBnõŸ4<:pܙēsÖ0±¢Õ!&>´†È„œ%i陂!…UjÛ²B”PÉ:C¸áF?‘ À2R¸áõ¿¹ÙËsç Î2‚•ƒ\¥²mƒÏ31ä .ì»m^†·¥ ”WªùÓ—Æñ1 ªÆ#·5tÔœh#kÑ‘™›jƒ´Ïªë®ÞûÂüaç9ÏñÛZ®Mÿ¾!FF úh ;˜*A—8¾cuö÷»E‘Z>!VÍ{¯GAã†Ó“¾Þ× S}‡Gzã-:u—°¡$yînÌ@¼@IDAT‹nWâÔçmä8ÍnXõSÎ’d8ŠÏ8ôëwƒÙLü¸dÌj šUÔ¯UM®bòyšMqÕðAhI6!9#­A%°ÞEÈ`LÇѺˆÝ?Œ ™`Gíɇ|àçnDã¹oÌtš#¯ÝtÓ{:Ù:'ÍGšå$ŸÜs&D‰A¦õcùö3#S•úÈ¥pf8ŽÂÉÒ$¦.œ‡kW¯,ͦ¶#‘í*ófD9š¿f—˜öØhÙ># ¼öÅ|I/4ÝY¿ë°tÞ=Ÿ BeòE“ YÚ½m˟@¯>:+÷&Am͘QÈ–½K\¾–$:4«'Fôm/‚àG #ðêü¡†ÂàÑF~u”±ŸÏÆQî[m™½úib(K+AÃÞ³¼¦õÑpTÆS×}aþ°Á8jq¹zR¾üDD³Îà6X*¡§´çxï;zö ¼âØÈÿè‘2è´„Ò`A^ËtJ‚sð¦˜wµá–zÛ¦zñ†½Ò~›~ T|ü틉·÷“Âã㇞Qff.\P˜‘r°!y1t¤‚m1'D:ê«ØúŒ‘Ÿ˜œr“æ7Œ»_æZã†÷­ ¨PÐpÖžªÓGöJÈÐew3+¾u`{ÂÚ¿­8nÛ´žõ¼  NÁ¬ªÆ>?8yž ³…,›—ÑOÅJ9ó`(@qÎ|Y‚6ûÌ>ák§!»5ÿd\µ¶4ø,é’Ï!!•¬ŠÇ?,\‡H* ňÞíÅ_ _ $:v8»Ó:ìœÑ%b@×ó‡ &ØW.âugöÕ°Ák” p¼¡ yÿŽ?iAãoÐi)¡GG¯éi:u—°ABÖnŽÞW—×™QwP·VbÞÊmâåO~µ2cÚøø•¹EÅÇW/AF‰ºFöe²®ª¢3L'(0P˜ ™e†a®0ka3VϹšúàˆ^’i¤ý6!¿ö´uèüX1Žºd33Í2Ú™9Ù"Ç+aMi³_XøîÏup*=l4 Úzè¯óïoˆO~Z.êFT75®¥½ïñÐÑ[4¨%*„‹ZÕ* '…¶M#‘‹¡…ôSaûŽ-裞*§;rÿY<Õ£=a@×ó‡Q¤U¥Õ¬\=¼?ÌÙ,ÔPz0Àñ渇Tˆ·¦†‹4¡§yL—tÊ$¢´)*0°ëàÂ\A<[i…‚ÒéÄèéQL}£Ø~×î2£âø‘ ý†Þý@0Ù™í›×sg[.¥Ú;ëÙVš˜Ì^¼QF¥yeüH§ññU(€( ¹ …Ž1'Dùà ùGºwhwuÛºO¸v¦S•1AY/Égñøs—ÔïVxÈó­ÔÉŠqβ˜“ñ’×lÞ^ÞöÈíÓ[6¨)µ‡NÅÁã”L¸æo#æ×:;{¡‡æ±—×>Ëöh®uÇÀ(QU{Ëá±6Ê5c4{š³l‹ sz‘Œž{³|ö¹÷gK?ž¼õÔvëÓ¶Y>8КAÚna7_LMϰ PB’f|vþ°ó.Æ¥"b ¿ù£ˆÕºë1ešBUe _@@ÝòAå,ˆwïµ¹Í]/jÔëÌo€yÕœ’”T¥H tþã7Мíf´k ^¡Ó—`Â{ QøÄóÑæêü&ÎGÀŒªš¢Ì×m;ß9MŠYÇàn­óøÛiŸe[?,Ø v> ËDþ«_·¶¢=,Rè¯vàø9˜Pßàã´Ï–¤ã‚Ò©9C¼nÊ&ÝÅyw œ\%³P¥za³—l2AØ(N?½òlVŒ©mxþßÄU||ÛhDÚΕ“¥:µl(VoÛÕîPAgG{ ºhsà|Þð¤iOÛ–·ì=–Ü´u»ªwïˆEÿ”Àaíª9#+þÊÕ$¯ ì íÜ[c‚¡¯ÍŠèWS`ÈÔuð£øÇ½·ÈìÐΞ½æKôÙpt8)2qûƉTíü{´m,(•+ þ~×@A“=£')Á„te b÷oï9w\»r-É’‘žÎ—# ((ó‡zc_t ä7½f·u’f¼F§ #Þ·S „¿&~…åÈï«·‹Goïo§‹î»´#æ”ØŸÈÇ` OË.Ö)gßÑX1oÕ¶R!lâS“Å%r‚K*q@’a¸žxuo¹àà1 °¯ŒÈ-zFY¾e¿$¶`˜6mÜ}Tv™1ê1hbñ3>>Íd«> ½²ÒDµ¬‡èÍbó¾£" vòö ~é¾tÓ>hS"%I¿ú¶={õ{ë#í|=mù:÷€°±ýÈ£áÈHK‹MLI3Ñ×’¶7¡b†1 A¶ç&pôKMË*æKÇ¡½PP),HÀaq·Š;`~—úQfsJPe ³o\7Bªƒ×B€iÕ°Öa®)„¬ÜGïŽ2A…!™‹Ófaú犲sŽ}zJrv’‰lC >9¸/FÙÈgþКH¯jئ3þ~~!eÊ𺥠w?a*×VfT¤ Ò‚šß¼…¯Ò)Êj‡W’sþh}Zµ¡¯ì/H?°]š6 ˜Ÿ×£ûw´¯°PGû<Ž>fYßÙ?W´G¶Ë°ó4C&(Aƒßí™ð…dDÉ—>žƒp÷µddÀóHt: ×ÿŸ½ë€ªÊúw’!¡÷Þ{ïED@@+bA…uum»*+hT°ìêê®ë'‹m] (ˆ(Ò;„Þ{ï%tÒ If¾ÿÿ&gxf&mf2ïä÷òÞ¼wß}÷{Þ½çÜÓŽ!t}9˜HéÕFu€9<å™DõRl¢ö䳊!R“«ž„ïfeX·tkÝ@õéÐT—wØ¥(‚ù’N½)lhäž;ujkùÊU5ÂM™v:¥¬Ù©h·ÏÈBŒˆÂ•hn®âãë—uò¯txIMÔÌNüÔÝ}œ”PªBJ=}Aûˆü Ó˜R0mi »zwñøVäg'·!ÁcËñÃû× iFAƒ‚íÜ©ÑåË?·óÐi8-×Ë:[Dÿ™ÔŒyUÚ7­{I›4)6!I½ÿßß姺£w[6ü?¢åÔX&4h¥ÈÒªW»Æš&f,Ú¨úvlªÌñÁÔ\Ô†¯£žÑäQÍè{¡Í·+"P€}Nú8uä0gÔzÙ'ä@?÷ÐN7ã‡>üì=øñqNåV ´8£Ÿ!òFhæ2öBþD EF§g‘̘aæÏA³±~÷0áMœvõ¯Ë·`ö¨Ž°ÈôÅ ÕȰ¾4/É€:ŒÙ „\ðe@#ÌEùõÐ^¼üÈ ‚Ë´Â".-¸°x"1–‡àA(_& Ç•´PñèÝ5–‰pæÿž¶ ¡Åuî¬=È[õî«T%1ç2Ð}0Ë”*©÷l­¸(?¶ˆòˆ0ÈÚTk:æïNäm0j1°Ë“}Ï(¥ï}=G‡½g` DŸß:0“¦pÃàCŠÝÛà+:õ¦°Á Áºqé²ÍÚw¸¸rËò•ƒù7ŸÌ#sm8†´tŸ„ÏÍ0)œ#cäSÀ!are< ‘†(lˆÿ†«ç9Öç¿·î;n³ffÄlY¹ì Ú'†Ü[—ÿ>sWƒVm.mÜ}$ÂgŸ?nÆŸþ×ËËOí´oXÖX^ 2·A^€ƒ“±>Ç{Œ4ÁkÌ·@¿ þ˜3+;„¯~]…±”6Ÿ"}2õ“QmMéÒñùOßë[¶~ìès›ÍjÝ´rÉ>œÒô½g‰€?Øp<ƒ\ÆÏ<Äsµpìâ&Œ„ ýÛsO0k 0 èd”ä±HB׊¤EB§{Œ„ «ñI)Š jÔÚ;ƒµ°(騼®ê×¹¹¾Líµ 6Œu„O’:ø{˾cjnôõì·©ªÊÊ%ûž–(Ï=Ø_ýËë˜7o…Yçõ²¥ÃU¹ˆpuy³–°ÿøY=¿¾0r jŒÔhЧ“má|NHMËPŸ65%°4Õ–/­à—~$ûŽž…9t=° M™]ŒÁ€X§ò…°©–4áu:õ–°ÁJ3 W¯&¤Ÿ;qüs|kãHh¢º"BýH´Ž‚†±½®âãËä÷˜Qª\Õëê|~Ÿá«òtnG[.Äœ™‡gŠ !t!{2˜cÎLÝe±PÇ´ß„B` —ñ£5{õV2p²åtÞóêcÍÊýÔÚètás;þí¾|è䉖.4ʽOé”zFë¤ :Wü?üvúÇK÷ÛÍ£ØFÎi\tó&ž« S¥h¬ôóš«€:,ÇyðÛ9kôB!Ð]sU½5v„’í:ÍA)˜wIPã=œc 5 uՄݨ 1&åÝy𔚷z»ÖÞP "ddf€·©î2°KQòzóÐŒ$ð|uñÏ3fB¤;¿hÝn2&Üà`8¹ïæ®±Z3Òc–Ìøé'¼.û]y{¡Œ³fNà ÷¥©ó×YoæPt‚˜ÜöZõ ÍÆW¿®Ô“E¾„•—ŠPí°Ù×¶ÌÌ+ѿϚ…6‹°!_CèÃ?¡S=ØÆ<Ž|¢Gª"cI00˜šÙÌ:kþ¿Y1@ËH>ŃÕfÏÍðPc[²…ßÓ)™óLß Çp·\`ƒã²:m†Ê™è”×øäÜ嬨Vr]ö4)~dH0õ!:VËi§{¶šú9>yÞi™Šå²„…³x¾ŸóxZDÐ"†þôãøà…ûÕ†_³à‚1»‡ïÇÔÇÀ.âï+Á€þñÒ¾²·j}±wM§6Ë~(?®9°5ÞÔlØ™…ää¸Äí×üµzÙÞý yÞkÚ²°·ä¶µ;Zá0eÙ½âßIIqÌ\(L$i¨ՠ’~åÊùø}Û6ýÝÒ®Ó;ÿ›m{âîÞ2 Èû¶™‘PõÞÛ¿³oêÁ§±Ï &úŽõ«?KH€g4ÑØ(pˆ@J:1Ç áf„<ŒþŠŽ[2vÉÞ_ÛjoW™+³ö æAá0pÍŒÎH…«3¿w[œú ÛãS:¥Ãɳ—aš”ÂNMw•#Ë\÷VôŸÜ s¥VjéJ7¬¥Ë¹ ¨#•0Ámd™0õ7ä²bÒÚžmÉ%½_¶i¯ö­¨ ÿ ÎCŒôxK¶7}.VnÙ§®Ä')š:Q‹ÁˆU ‘äùn&»à{K¿Í–H¼ì ¨ÁˆOJÖ¹¶h†¿pÝN{1ð½.»i0 \ètʤñcì/Qˆo l’0 i8NÞ°dáflÇÚ4¨’:tð€Q¦ÀQˆ^óã[!LZìÝ3góÊ¥{·­^±Meÿ3¬“6Üó¯¥FÏ›½*¢\ä×8­f*Û¨;zXŒ+8oB€c€+Ö46í=j9¶Ï´ ˰„±… ¡¡süðþ.Hóó1~¤z_ÝãS® /Åål ©+ÌR’t¤Hè`$ç?Ó‚ÏÛÆhLܸÊObx¿ŽÚ䉌¸îÇb5÷ŸL[£˜LÕ´nu5Ñ ®êï§“õ øRN_´^G–dÚËtæ¯Þ©â“µsx×V Õíݳü.ÉôGo;¨Æ}2Ѩj U?½}ýÛ*$xþH0rr´F¤Ð,ÁGê”}±Õ¿kKmšE¿‘ÞíÙÓ¸ ìâÁ€¼N Þ6H=\©$ÁÕm®`¿køèé¾ÿÆågþðèÓ!!!Π  ‡ôôô´÷>øôÛ¨·?\ÖÇbc²6ö½0’d x,ôAf3iîßLxÿHÒäÃ1—®ØîÐ%Èôátöž>4âJÒÑ}»§/þyͧ8&FDØÍ_Ö?ˆ…›ò9~ÜD˜ñì«’±cDcg.©vQ5«Dª³pŠõ7¸Ùrøþ=ÝžI¼Çe•ôµ0/an©§î¹Eûh^ÉÄ ÐŸÖY@Ç`(LÌÌÍî€Ã-¡í™×ŒúêcƒU2|FJ"&Qž¼ŽàY@>_À1¨ ÏSÐ¥…öa`Ü»r0 yGwûk=è®TÁ®‘YÕI2|cNé•7W-Z}±a×~oB éܲ¾…ª/ÇŽGYüü ÷À»µ¸-uÇ /½ñ˪ÕëÏ É4¨¤‘#÷d&©Ý ™ éA–0„™äy-l`_bþ´ï~ìØ§_Œµs÷±~;¿,¢2Ø0ˆXZ¾³¨óp }&äÌ£Áð¶Œ:Egpøð$lY½â›­Ñ+(Œ’6„>ØÿbFe¤süȾ­h!Ç]Kâ_Me>&.‰îœw Œn'à.Çï[±yŸô²áCÉÒ‡Œ}9áâK!GÑm`´¸úL`‚²ÈMÀ§;‘±¹J…õØÐÞ:Ñ„&½r»zûé»uYŽ“¾˜­Fî¦MXœæ<€ü‘Ãàꈀ°ÔUÕCQ Œ¿¥S¾‘«)¼æ. ¯çîøÍ0‡Pº¬‹>#y~Î /]-³÷tvΛŸ'Ì š+Ü"p›?Ékxå™IIõûî;Sñ›Ù:…½µ|ÙpKdD)ËãwöÌaGŸ˜¢~Ò®Òò&'P5Ëû?ié¶ØÄdúÄ‚UKFzÚůÿ6ñ?èö1… ôî"3I3 bF…C;±äyFI!—/ŽÞ³qý¾n·’™™ÑŒjV>l¥K–°1#¯™(ËŽ?¿Ð‡ Ç÷ï[¶vÁœ…Éɉ\B%mp#ˆfƒt ‚5˜ãG6"n¤ñσãG6vÌ]~0p6ó„fõª9½Í]Žƒ«ˆ‚GÓ+Ú£Có¬–"îW³VêT#úuRëvÒQ†DØ þÊ-ûu¤Ÿû¡EaÝ¿"Có“w÷Q©˜Ë/ÅqŠÈZÒ\B”š[V­P溜,E§ß°’źt€Z ¡gêüµêµÑC³k0w&üÊC@§yM–7»ŒšØ)33ØöùÛã6»Þ6Ø62b.Cf”â!Ÿ´ðç¦c?¯IÛŽkÔkÐ4,,¼|LxX9ð'¶qúÓ qq4¿¡ b©P¥ZDß»F ÎÊÞæç5d¤§§d—'Sk–'V<ŸÒÀÿâ6”iWÓRã“Ο8tp×Á0rÌ*È8rã BF’f2bFEÆÑÞg8&ðiƒ«Ú,—•$%X͘Jú˜Û¤uûFµ6nQ2¼Tùâ%CË„›æwÀ‹ßœäÒÒRRâR/Ÿ8¸Ïþ[Ž ±¢½"mPРÐÁcö»h5HŽ`Ž7ÆøçñÑVÌßyÀÀyDù¡Ó««$»îrHõC‘Ì´òdX3M$iB-I©ðêß·YV´YŽ¿Ì_ 9‚˜_a!ƒsg9õr4ŒÞW•†¥K«êãï {s‚=1[nõÞ„×ég‚` P»XÓÕ;ͦ©~…A£/… 2d8ì %޵²oëÆmØöà7uOZX0ìuù‹gÏ$ý4ùŸÿÎ>O„™nò[î 2Ë{ ?@·Æ9W¢Å‡ #5"lÈ1ûÛ•V—4°ÿX—Š<)çXھ훷cÛ‡c ª¤# ±ÏMð ð;$Èw)‚$û–4BáSQîÝi½pYë”zÌñ#k|”ñNð,¿aüc§¿oÒEaÆÖgB>0ÀœÉ©Z¯‡ÜrÈÌ{@(•mnB¿ãoj>¨¥`îƒh;¨Áp:кƒýÇÎ"Ù®M1{´MkO5… AˆÃ~ÊÄ /;œ2šÈ,6È ©$-†A4¼ÅÈ@0h1½qØ™0¹¦’ ¦Q‘É•up3ŽR¼Ç,M£ä)üH_÷Ä7…Ga"É4ÊF†P„ ãŠ5ïsBrMꄌH6­˜¦ !XóŸ½ôéŽ}(‹ ¤F¹g¿’~„F\ÑŠhÁ™õ™ãGNaƒ8ñÔ÷ííñÕ[ãéÄ|` zÅHßæËªIª9îÌ-Ç.fÈ-ÀsÁv¯s0æK8‡ü´•ç*o0’ØRxÈÈÈÔ¾ AêB‘;ð<²?KöfwåÍk&L ø|%lð…©$à  24"#ÉK„ 9æ$ÈvL¹_2¿<'ŠY>K ð~÷ì;2‹,؇"`°yŒ&ûEú‡.u²­9aƒL*…Q¡£vC„ Ùó~Šì7‚Ðû“t t"4"¨ÐËɽ8t Bæø‘Çò?0þ }]xbüpI4æ…ë1ЦqMÅP C­èGAmC,|"­V«N2æ.ÇÁõµå~†¡uWo;GîJ:ìi£Ú•u¤ž†µªBØP*¡P[Ô¯Çóý9*sÌyÀûK……ª¹ÈÎ<Ñ„J‡‡©0áâ½&˜01àßðµ°AÆ““£qÂádãÈ@rÒ”B 6p¨ïgdT¸±^‚Üc–Ï‘§ñCü¤…aCH^çÆ>Ê+H m°í¬—ôAƒ4b¤FqÚ?€ôûŸ}Hšàƾ:á ßm^iDê5ÇÀÿ¼9~€¤LÈ f”ÖßÏ]«¦ü¼\;«2ÎÀn­µ°á.ÇA^êw,Ã,ÉL¤öíœ5ÚW„Žå„J‘¥U¯vÕ¬e›ÕŒEUߎM¹*Ü~»³œcîé£ëyíÓŸ³ëˆPÍÇÞ áÅ~›yÀˆAhô-û«Að"%0*5}ÞhÇ9Dg£VOüšŠò=}õl_ |'2 d2dOÆ ˆ¤.¡09æÞx?53+õÉy2¡²I]¼f–ÏÂàÆ‘YÏ ?Ò¬EúO˜I#ó(õd=-ÿ¥^îYë%£Jè­’¶ûELð °ï¤ÿ؇¤¡îyN6æ Œô!õšãG¾‘ý¾å›’zØGF|Ëù‚Öï‹ñCÚhîsÁ͘˜°Œ ó Ê»Ëqà˜Ë iÝj9ò#T«X.Ço6#Ñ£þ<êv•?É] Í{ðö®êÞþ0é[sˆ#9¯;ËyÀ¼KoýánÝf–q¾”×ov;þí¾ÄÁ䉖ú ¿¯Ü¦–oÞ«þþüý9õ‰_ü -Z¤zthO—M¡)Ýì[Õ€®-܆½uY.œ¹pEÍX¼I8~4iQµ«TP£ïê¥Ê „³§`öÊ­*ÁÆÜ£»ÇSÕúu=¾6œÈÜ“‘‰ÈÈ<Ï9 Ccܳ>‚cYþ6–“c]Ø,¯ñ%81î]á‡ç¥ûL÷r‡…ÖÇ}Çú…FGú—LðC -H?ò·ñ¸°Mf]îÍñ#ëÛÓÁ?ù6Œ{é㾨Êó¹ÒöŸ7Æy7sŸ ¸jk4ŒÅÝå80–Ëë±£ !÷åög9L!C°ç~oµYÆg—X꾤箶oZGý¾j¢”ÓËXó…+ñêÔ¹+êÎÞí=÷ /Õ4gÕvÚyÜãC# ‹¹èQAƒÍ~ ©ô(è¥wðhµ6KNûÆV^T†4WO:˜ðn{Hyp²6¢ÞVÃ,YZ po³²ûÆÆ TVBôI œÌÁ,OŽÃ{øâ‚l Éû,'‚‚‚æÿgÒøMŽ}à¡ßìãLÐGäjˆÞ¯ *}XrЇ‡žeVã! à;E¤)Ëeµù‚>lONx·£%Øš9%êµmOûI¼Æ9^ŦpeËÌœúù{Q_d_Ó‹YØð=~ø]ë-**kLŠºÊíôí¼÷“i‹ÔãÐ’¬ßy>R6À’×î8„¤˜;T|§jU.¯Æ ¿ÁúüÜè*19Uãî!h ßÒ>oì§L?Æõ™°õuèéôÓÏ ä‹ÖŒªXa±• µ–cÒ6Ÿ ž@æÍR’¶Ycã“m É©AV›íí1&Å láG5Š×ø$*ê1š<®£ ‹-¤BãÕø‚>žŽú´TzFìhÅ‹I¤IÍÊ.[“O¦¬Úz ò†]GœŽkZÔë5ùõ“Ÿq½õ€Byã)û±Yλ^ÀÓ¯BÔ¸€±cϤ/þ¯ê™‹±eŸš0éMU£Â7SÆŒ¡Ùä&‹^d–rü^¡ÓMëÂñÿ€º&r„­ûŽ+úàÐTî˜(mØ}T êÑJ_›½]ó?¬oý[þÑÄO’<ʹøÄd%ëËùM0¹rËAµóÐiõè½T\R²úö÷ÕªmãZªb¹©^ïÛ  ÖîRÅ‹‡¨[:4AÛ®M'®’J²-{žQ'‘0órÎÔV¡ÅOêà"llÞwL]…ÀS·ZEµhÝnáz½K>u^‡s¦‰"µBç¡¢ —¬ÏëÛ^ÕÁ=Sç­U+1ìÖ2G{åÇ5,ú°ÅOþõÍg3Ï|‰Å­êÍêU·É-T·@Kß üZLNMÓïÆÝGª Ë÷ßЧÏ~5jô—ïE-Ä+d}Ix“> €4ÿºÅ»ôM†ÕûTžujW­lëÚºêØ¬n$gÍmé-¯è0˻ǔ7ð‚aΫc©úÈÁ]im;tòÜçA§.¾ñä«QAû´×½Â¹[Ï^ErËTÞ|ëÕïž}”Y›Ÿa õjº Ö ñí÷à :m×´¶6¥:væ‚*§ÿ£HÆ8¨{k‹¼$ÌÒòš`rס“ZãP¹|iDc+­µ/[  èš“yq['­=`à‚e÷*4  ä.©$ÛËÌ÷÷Wäg a¡Å) õkTR[öã<¥°À®¯Ë?.vÑÄðñ;{åt°óÐ)"º]“Úºh‹5´ÐæIa×têkaó…ç‘!üïU+–ÔÛYÁá+'æ¥̽ßc€¶¾!Åc³0‹ì´ë+Ÿ>ûû}ó•/Þyãc¼Žü0 &}ø}¯ç½^ õÔ„‰ÏXl™•âàŒAÜ?òÞ%~W’+vp¶pUpÆâUf,Æ`Áâ46¿ã‡¿¼Ÿ󮦦]‰K2… é¶#6>É–~õ*‡ägþóa õ£¼F§ ÀÐÅÛa>UÁÄ„*¯I#]!"3Û¤Þx=/ &3Ö™a’ÉøoÚsÌ~;£B9ý˜†ôl£Ú7©žfš2s™zã©»´Ï µ-Î’JJ*EF@#QÂ1d¼/©ÍªÜ].Û÷—ãa:™CÐàEމ4«z÷«9ö²ž^¶ð%úRذ<ùÚ[/ ¯>ìдŽmÔ=,Å]èyh`”WŒØí¶M{~0z\”åËw£(pÐ4/®IÖéùh¯èƒO³T‰,óTêƒï»­“%´#!›p#`€Q^~dPð翬°AKúå㯼^ò«÷ßšŒw $ƒãœŒu¶Ôä¤ )ij€]9aß}g¾CN °¿ÙïWS’Ïe_1ÒEÎÂEóËØ¯Ñ)M‚¶<¡Ê—)m7¡âëÂT^=žG¢GŽåŽàª±C%úA”/s-<2Ïç%Ád0êbý}Ú7¹VÞœÔé{­Wþõ“v/Ó&‚³¤’°îÐ×hf„ŽÍë#Ì„t.¥sÛPøp„R%CÕÞ³1ާ“⪵‰¼çºkž8‘W:5±Sff°íó·Çm,Ìs³z³05äí^ V«àô7ØòÙž¸»·)hä oUŠÂ#û–}üþ¨Ç Ä ðëËmõÙ¤€êé‚5¶ôÁ¥Å½3jì°æ én 뿾«Dñbêé·ZõhMÓÚFh¬äÕÉmüð‡÷² h $뉃û·c%ÔB;qn °¿Ùï§ŽÞ@:Àf¤ _#‚mà&`l‹Wé´„ ŒJÕ>’4r?BË2¼,Ç[5¬%—í{ 5*—S›a~Dÿ…Åë÷ØCÛ åã€þëwVûÅ ì¬U·Q²Œš–®MŸNœ½¤.Æ&(øêËÔÒ“J^Â5šMí:ìþÛîÔ¼"q%ÂGå0|9êe?n‚…–Ø„$µhýnu9.Q­ƒó8ÙiºÅ6,Ù°G¥]ÍPñÐrÀÜÔ~_aòJ§ÖtõŽÅšùnaŸç ͆åá‡ÿV¬X±/i:EFamÞïß`Ç\еÆ\°LéÓç®fË—ÏJD‹]i8LúðïîôxëòKh€åàÎÕu1†ü“¸¯H<þÞf…¹c€ö̰Áæñxõ°ÿN:üÑÃ8&ƒÄñÃßA3oh$÷™»6®=ÔùÖqXõŒ ©©¿7ÞlŸg0€þ†ï¯5vÓÊ%ûP£‘&<ó€|Ô2eâ„—7¶ÉktÊhN4%ŠOÌŠ¤$íÈOÒÈÝZ©ß–oÕÚ&‡ì”í!uåg?Yçéàýñ u~úIŒÞ7‡ƒ8,¨ÔnøJ,X³ &œ¹+ V7U«Jyý(WI%]µƒ¹bà—¬v>¥žº»Ób|'ú´ÐGäçÅU©°P8®×†VSG²ú yG¦/Ú s–ôïÒB'ÞtZQ>Oæ™N-6È ©$—w *V­Ô³$«ÑGÃ4Ê[7Àeöñý:øíüªµÛ7F-Ÿõ^ËÃ`ÒÇ Ðçùy…|Ò™´šµ«¾Aà yœDvÙR}útˆW…ÌÇîýØ>i#s¿Ðö#ãÄ¡ ƒ‚ƒGЧ¦„&ÜØ`?à ÐrúØÑß𦚰ú%}5ø”NßDvwGÈOÒH:TsK»š®¨ù4±Å›çsK0I&þ#nUˆœ©5$á0_¢pa>ãåGëpÍWÓ3ðÌœ,2¿agI%Ûb¬“É3a,BÛ ° à—Ô½pL§:Û)íº™Ô¹%$¥Â‰üzS3©#¿û¢ So¯²þàbÅCžƒãŒÍlóK[ž}Í>/Q²ä3x 1‡p¤7“>·‹ Õò<Ò‡¥}ûöÁ¯¾ÿ÷W¬VËõ³V¡Z`Þìï(>ôû¯?¥íg|¶À/[O¦’‚†WWϽК‘;uþ:+Í.L¸q1Àþe?#ŸÏ•èßgÍÂ›Š°Azð'AÙ/è”>ư²î(ÃQÐpW6·k ¿kdè•'³ï(hËÑáÝÓ‰%©Õ-~MÐ0>ç}ZŒ×ós\TtêÈüå§Íy)ky虿 L¥­ $¿@˜,òòNf™‚BBzcQÛV;ÛfÖ[ ¢CUõ΀±Òéèè :‘™a½ l˜oo?Æïêÿ5z×ÈÆíÚ>†ù»°Aܱƒ¸ŠÍ…Œf2¶”­«Wl=¼gǯˆÒgùbæ ›©áVn `²_Ù¿ÇöïùqÃ2Øgõ=i€´@š(’wìø·ûrÃó`Ò©7ÉqQÓ)W›½d8AÔafp3aŸ·Ðì¿õ2Ü#û>%)©ZÉD^x¹ÊÃÍëôñËÒÍ:’…cè8Úl~2m‘zäŽÚmñ:°- ÖîTwßÚAÑÉK`òŒeÀQ í„&ç\í÷!ŠFªt˜@WeqWoØ«•[ö©Ïg.W¡pêk^¿†{ “ÝÙ³Pk–¹õ+é…ŽŠã¢Å\Ô‚6gÕVUÑ^nda#²L¸%..žjqŽ;ȸù«°Áv±}l'M2œ ίǾ¥³¦/¸t.æ\‡^}ïGðŒ2ôi£)!5|f`)@€ù 6”Ñ|è nÍHOزzÅ7[£W¬Ç+$do"l-ÍZœFï3é4@h­0ÍôN™4~LaÚ!÷z‹Û"#©W®ƒTP©ÐbÅøÛ„›ìû ea!1£"]ÈöÛ÷ôÁÕê7ÿ3K@ô‡6k©æ­U‘eJ©30•Ù…•îúÕ+ªÁ=[#ûgE•’zUMüâ7hºêvìÂ/g­TeJ…ªáý:©ÿûi‰ZžÖ›—Ô§?.ãP\=†d@ŽÐ!ðNc세£VŬÝqHÍÞ¡“³Â>t{WÅèŸL[¬ü£¨$(bû—ûõ'ïÒ‘+}™J#’Æ ´-L-Û´WmØu‰«³—âÔT¼ã±˜KªVćôj£: Âï^ÙK/ÁĘ1Åÿxß­×5 ™á5nxÍ“Žz| úàxÁ kù–ðâ!!^6æ#¼!L=:´‡bÒ'ûôÐÉ jÞê9„ j&}ùâž'ê̲ ì¢(<þÜÀ'MÜMßï¬ÿ •±_›¿f‡Î [«ryÐHOõÕ¯«rôë¤g†ë²òï虋ª[«v¢Qí*úÒ\˜vmÜsT÷ÇÚí‡ͤ‹jÙ°†Z>Ÿ½ qÛS²Ã6vÕ¡Ióã?ýYA”™K6A+¯Þyv„Z¾yŸZŠ8îÌTÛu3òI]Ð?aÍöƒ*4AMOQË5¨G+Õ½M#åî=õÍúW²xqÐ…Òµqüàʬ¿9j`(lp~•MØöµÑ[lÛz°ë€A}233zY ©ié’%le!X™s%0æ§šžncFx&ìÃ÷dAÿ%ß¿oÙÚs&''Æ¢Ù´§äM„ ÒiÂßÀ¤SëµÇ_éÔ[ÂÑÆÁ5ĦldL¸‰1>‡4cÒÅï"¥KHž“†cB<Âʭܲ_õlÛHÓ8{å6í³ðÇ{ûi3–5ÚYÇ'&+ŽÐ@æëýÿÎÑI‹’SSÕÞ£1jü“C³.:ü'Ã7¢_h(–©^íÛ™:)ÆÄEßÌŽÖf<t( ¬³w[çæX}¯‹äHgÕ°[Úë ª%K”Pô­8 F–½Ç`ÎcÓ ‹˜<ˆ±Â+–‹ÐÉ‹þ A…ÐÃx·=GΨ/~Y¡*áZ­ªåõ»Ó¨ 4-`˜¼ˆ‰…ÈÔ®ßu˜Ù=.hÈ3\Ð1ìÕ±ƒ~MjWµ Òž¦u«jaM~s¿rë~uG¯¶`Ê“Õ<ƒÍêWWíšÔÖøCP}]ÔUÿ ìÖR>u^÷-V²A+uÔy$“Š@ vÇ~5>“ÇmØiÁÚ]ª8B0ÞÒ¡‰=z c°S lÙ ¦êÚª¾ªZ¡¬:‚Xí_ÿºRukÝ‚q5µ÷QH3L ¤¤ãO\¢ê׬¨ÍùX$2Ô»¥ƒ¦) ÁsVmSÏÜ›JFÆïAOBˆ¦ðñÞ×sû½–4ܽ'ëô4à»áXaÔŒ’.ü‘ã«‹‰ W´S°±íܼ–‘’’”-Ç\/jÔ²MÚš4)^:²DXɈà,É”¥Mð+ ddf¤¥¥¤Ä¥$&^FÂÆ=ûwl9‚Š‹ :x̾­FÖ~&úQgx²)þJ§Þ6„Q”àU†Á“dÖå- h“´ÆI—ôpmï'ôA3&ï!\ŒMR¿.ߢmøõ 7ÿ(ôíØLý¸hJ‡£àí>Ȱ;ƒ øŠ´i\[5©SUý¸p½zõ±!9Ší„€@æL,fZ[Afµ& }6ªTȪŸáû ;+ÍÁÁÞÒ¡©Úwì¬6 :æ–ym<¯³¾0r ^¹¦Fc2¶®…Â!5-CM|:§0AW-ûŽi-˳ܦ™Y]Ø+ÿ®£3,˜ÝwÔ©V¡‹7Ÿ”‚ˆ×óvŒ¿N-²%Ü 3àR%ìFÖØðŸ‘~’¶¹ë?j™˜Dêqh¼Œn0ÎúUêãžÚ7jF˜ðiÙÆ½ZƒÁ̲0Ù6˜Ùó7PHæûŒÒ]WŽûþ‰V‡O³'‚¢àðÔÝ·ØA¡…@‚Z žŠI?ômbB*ÒWõŠeU*²Øܽ§.àᆌþ*lðíµP}6¡e9Gž×¾vnÛƒ6Ñ|/n¹'ë—ù¿¨1 ´Æ>42éìG \¡€A÷ Àkü`XÞ_Ah’mš“s&úk¯¹n—_Ó©·„ ¢Cž˜(„ˆ]£È¼rCc ›d2• Õ¯è£V•,fžA†Í]ôjŒ@Ó“¥`ióß¿Ksã¥ÇdâÞþâWµf*Á¢"Áù½GÏhs–w¿š£Ëén¾ -‡OžÓZ–z`"‚Yüaîu¾d–›Ö©¦öCB¨ipn®Y¹\íEƒš•®ÓZÐdæÛ9k4ƒ\Ãp¯®ÌÃÿ\Ї~Êwÿí½oÞ9ù”‡©«+ “²ø$γ9!19E¿7cÀKŸUÍðX’>4orwýw9>QÕ‚€`4ïwö›ý0¤gmº5mÁ:5eæ2õÆSwé̼Žå¯Ä'¡m×è¸&Lµ—â’ ld•náBÌæ½Ça fÕfa4 ãVB4ãÈÓ<‚ÍAÐÙ³ÐxܽgÖS<÷¿rùµ)%ùjä\Å€#2~xî!ž¯‰:}7ø[˜8aPÃp.ÍÃŒïææ‹GIŠ Ò‡ìW ÔZP«AÁ‚&S²Qø`‹V#kÐÇ ?“Ný°S Ù$Ó騍‰23ƒmŸ¿=ncaÚæ-aCLÙ¦æ½7È$=à ¿‹ü ]%ð ʶ忚ι# h~S+Ê `ûO?‰«X _¼~¶{—kÎöU±JÜ»}S½Z]¯z%{‘P0wå ÙpŒže/àpÐæ> ÖìÒg›Ô­¦Ö¬¬…j.ȤÑ¡b¹¬¤Ûga£O„pþ-`$ÀŒÞŽ@†ó‘!=Ô¬å› t¬Vc‡÷u,âéßFúð ]PˆÚ{ìŒ^Á§ÿ…À.h.(áð'ÀãjèCGp×ôyØ{6Æñ–<ÿ&ÍP+òÊ¿~‚ïÍE§ÂF¥ÈÒjÓžãö:Ï]¦U‡‚¹yÚ,0&Ñ:Žzè·ò܃ýuöÝe÷@ㆨÀ>“HõhÓPkÎJ@³ñ§ú«ú5²hÕÝ{Ês<µ§¶qÔðû>F}ì ¡ŸÐG!ßÁÈÈÉ1™T2¢dPµf{ Fy7Ùã² EˆöÁÈÄq2à&‚#…ÙxžýL¡DîÅa‘Ãðº¡M–‘c“NÝaÌ?¯ ­qÏ… ÒŸGèÔš®Þ±d­ô+Ì«{KØ`›8Xšf>{‡+êdô<툛Ïfx¶ø5S)#M=û¢>‡ }oB p,ß´O1JÕ  ÈQÝöƒ'T[øZ$Àôêè™ ª+ü"Á]ÿQ¤S5úÛÃLŽæoí›ÖvÚ¯ìgµSK ´„9}nÖí8¬Ï‹™÷ÇЖã-4y¢°»dÃ^ÕªauµhÝn­¥3 ´º‚ì—ãÈó*-à^ŠMЦuú¦+jt–ÃácDÓ«2á\ˆÏwï)e<¼—±" 1®Ü‹Õ ófdhž"+⎂†S^l’Yu1 }èÈȱ?¹… –aù"‡)'¼œ‡F˜tš$HÏÓ©Åæ9Á#•¸ïÏšQ½†ˆ*œe5Nž}‘vÞùj¶fÒÿù—‡ätÀìs‹÷0/⺡.ÏÒ‡ããÉX¿ÿßßí§ïèÝV ‚ÙS~`˜Òß–oÕ«Êdü;ÁÉ—@›öÿý­{úbÖï<¬Ï½üÈ`-è“NþÑGäÎ>ít4(¹Lûyžû y¦#/™Næäà3iß§}õVžiNóá ¨òÈýÀü©W¯Úý/hZõ;œ|©õ p%ûY„qýú·UêõÏ~†ý}°Ü£µ]‘g»ÚÓ'eÊO_´^5ªUEkL\•-äy#}™K2`^j žh pº^½‹±ƒVntª§§1ðh„NÍë#LîBÍØónGpפ™£pà¦ÙÒÏ0M¢¿M[ÐŒ³~¥ I AGjÎÒ32³£KuSµ²ö= Æ¿zþƒïµŸÃGÜÖå;t•G„µ'†õÑyYÄÌØf†\¥Ÿ#³Q˜1Ò#i»6|zè[D:ä}|ÝÌÝ{ë÷à1'P‚ì³~ƶYV¹É„Mp8ÿŠi˜Æï 0Þðæh%û‘ûPÑ`p/ç¹D0é4{íú6û-ú@ظ…=à zóÞcZõ/umBH:Rr’ 4ÈO¼ÿ@{·¢lï°¾íud'gm˜üÚ£öÓcÔ]ZÖWÜ:©š5 ާ7ÆÜ%ÅôþE8b;¶Å‘Š›èÿÁ-²J†³;(³Ì}:g¿E <çhrŰ¶ÜŒ@›û OÞ©e¶ŸL¬€ã»ó<#&q Ï7[4èþ‡»í‡3¶#óï©¶0Ü+q“fž¾8Žætd´…V¤j»²˜økuÕÄùýè¿{áðg~ ÒÎú•õ³¯^sO!ˆ¹6ÛFÍ5X)ˆû/áuoíÔTqcÈfZX—ñ=ø›ÀʤW#] =2$/…šOñ¹±ÀóÆä_t43jZ\½gVÍÿ/ŒœL¤ÜÈ;X(|ˆpÁ½_#¤@{»›£½BÒ—üm<¾° ïcÒiàö¦_Òi@ œìh—ΜD › |´„ú¶'í$röbœúN;Ì^Òæ  ìj·[g´!FÛa$žú5*ª‘° ¦ÍýôEÕþã10U Sí`æ@&‹‰·þ7{5ì»cTz[cš&)d:Èü¶b ¢PÉiWUìîÉE¼<êv0ÅõJôÖýÇõÄΨEÆìÑÒмÄû§ÉÍì•ÛÕÛOß­oc–I_ÌÖñý›ÂDƒæW\õvö¬¯‚NÏ÷Â1™Àº~G²°·þU—;<éÌŽ‚†7ÑB›ygP˜D„F&ÙYÝ~tŽ5· Ê5ëü‘áw½%lÈ;“ñÎm0äP#8dxàg/!Üìy0÷×pÕ?œ]sׯ|®£ !íæ54ä÷FAÃxÞÙ±+ºNƒŸR,¢Tqüã¸Â±¶ |Œàì]Œ× {L3±Ž}4ܸtÁÔEFˆi ÛOaC £_ŽêÔúÝÚ@oUä•Ér² ä7¼AÛŽ0þˆ4e9£¬¶ðå›ÿŸIã7Ý ¯ÊײÓiTTTÈéÔ ÖÐÁµÇÂG{ 9Ì SåºwgæÓLëÔ)ï¾ñ®¡X<1îÍ'¯ûå·}o–רð~dœä^Ží¨/ŠƒÜæ×¢hS®Ï¤Yɶ' œÕaD_ž«s aæa6èØJ†ÿ¹¨•H^5uþZõÚè¡zŽ‘;Àì6 è8L°8aÿ¾p›º€Ù–œdµÙÞ3aR ,)>ªQ¼Æ'QQÑÇï`ìø·û²Q“'NXšÏÆYnöÙâÕ#*>rÖüš ¶Öâý¥ÂC3ëU«h¿å”N;µ¨×û³ ''ŸÅÄ  ýí ÌòJ'Nõ$~&¿~ò_/.€F÷ ûäÒ ŸÍ _o©UñŸSÆŒ¡oQ‘@@ ÄT«†µt"3Ú¨oÞs亘÷4U BûëÒXÕï‚,¼¿@û{0[0˜Cp¶€Ô ¸êÈËKȳÀÄhFè‡Äj4™ 9C_2Ö>m¤@àaT:á6­[]ßBÁ‡°ëÐI´³ìÜKëù¶ì;®yuì¹ÅûçÊbn×g9ÖãOô 0ÁÄ@`@VÅd_MpÿHFó2Fôr_:ð®2ß‹hB‹²õô3N¿ "dȾ(›–ßgkáùÑ¿ŒïZìs›ÕZ ‰m4W„ïŒ>\|GüšÁ¦U7î>Ràÿv6óÌó£_ýå{Q Ñ|ҩ߀ÕfŸÝ˜¥ylÇÜ ‡Ÿùs­°²åVSFn'Û-›ªzˆF‡q!ßtÚù‰¸åÌòî1•~À¦Òé¯:ÆÍêY;v²Þ¦ˆóÚ±šj(Ê–×ÂK&âÖ3r3oB#s—®…Дsîâý3 ‘$“òÜ}Sòó,Þ˘úîðd ‚%sobÀÄ€`€C  šyC»ƒõõ炃‹½Wµbsí@ï·Â´ôµß6|Dçõ¸YbŽŸ¶`}åÓçcâ¯o¾òÅ;o|Œ†s‚õ:µØœj \ W ĸVì¹Gߺûèù ›×¥ «I§.§‡÷ëhiŠ€1 Óúà‘—‚NŸRû”':2iüO¼g@ |q®*Ò_bÞê:R £ª…ÐY¯öÄë¯_£ :[iÛæ`àRH`!gÎÁö pà.Þ]˜k˜üÂYi_~êüe}>ëZÛgQ“Cûk \¹áIÊ™{>Ä€LÔ²÷á£ÍGù)Èð·@  ¶—+Á!¾<^ šÖµº£G³Ü6~Šw³Yn0@ÓíWü¿ÙѶM{~0z\”åËw£(pÐ!hTÞŽ‚iµØÅÓ;žŒŒ|§m³ùT¤sï‡h^¿†zcLÕ /YiÛºÿÄ'¿•þÕ{Q_¡©>£Ó€%&2Ü-‘w^ôÄ«¯£|c3¶<#¾Ì]½]›N]MÏDŽÓºÈ©sW#ÜÐ)S˜ÒIrçÁS*æB¬NØÕ9„gfÞ˜I1ÁCÔRÃ!@sª$¨U©iT !J±ê!жq-µ~÷aý,j¨ù¸€¼ ŽÀxÿŒßÏxÿt:=s኉ʲ¢~lX«ª6ýŠFÖišr­ØœS³åîY ЮC'Ïëw£ŸËî#§ìMp‡'{!ó P 6‹NøÒ×§YQŒ”ö;JA`WÀþrwÝÕ}7ÀyNÔ4Y{ åið×òiÓ[Ï(l½Xc!Ó.«®<A£Øý|áöbÅJ¼CA㉻{[LA#º/ïmd²_Ù¿AAÁïzqC’q8Z=¸;z ÌOËæ½çn®’\ü&¶jTƒ ÖŸ=üÜ8†Èd?û„NV³A2iפL”Žj QcÆÜÓŠ5й94Ij>önͬOE®2ø¥ÃJ‡¢…ΰÉÙÖ#RSF†UÑ¿b"Nzwh¢þû[´zîïß!ö|ÕÑgÎg›l±NF´¢?H $L°6ù ¾Ò’X¨¯1B }H,Åû/‰¨V/¼Ý®•av`jc§"fQûB“-wÏêUè¾#gÔ[ŸÏ‚VÙ««ù«wê[ÝቑnL¸† šÿšºHMzf¸ *W^ýä'Õ­UCÝç<·‚,ì\í‘‹f¯ÜŠ ™ Ç¾Z#5ŽÅ¤9DžQŸL[¤¹£‡­Kßœ½Gct7ÖCS9ãuž»I@ kZZêvcèè‹÷–\=|3*Âg‰>]Æ0Ⱦh‡½JܼyÇ2eÊEþ_µJå´Fóx1kó' @ce‰¹k¹`™Ò§Ï]Í–/Ÿ•ˆöùl帀¸Ð´Ú¾}¿°÷õ¿¯^ÝÚš‚F1·ÑÊå©»ûXVm=˜vöÄ)ë·˜êÐlš½x}< 8acÌššVbàóœ£ã Uœ ï*+Ãdà œ´¹ñ¼œãyúqpcF^ qÉóŒ~Es*Çó 7û÷o橇‡t³XO +¶ìÓÁ½ÚjÍÊFܪ™F>+¼äµØú¬×ÆxÿÔ^LC²®3cíÂ˲m÷ö酪Ô`d;¾ûÖ,aˆ×¨Åqõ,&~{ñaÆÓ¿«ß˜7ÁžX¯ ùÆÉ3–ªW¢ª”(£o~ìΞN?eöá?^|@kØXp÷á3jÖòÍvaÃñzþZð¥iOjýá_ÿÛ÷N>áË·ybXo퓵zÛAÁŽZCWáa½Õ.™ø ‰ûvhª…Ô“ç.iSJO?Ïmzú9­¯2BínJN9…ûI³Á¶rn-ÑúÖ[ž†{úh˜‚RA`ÜÇþÅb@ð‡ßίZ»}ógÔòY ådâ(pø#Ø…â½;=s>.m"ßé“b‹j8néÐ$4¹yíqxËQØÄwéÀ16jb§ÌÌ`ÛçoÛX¬œ°Q—5 Æû]7 ÆòŽç)…¬“ÓÔdª¦yÓäù¨Q)f]×î$ÓHa /ÀUúy\ŽMÔ™¨KBCÃÄ]ΜÅå÷îžå*V¿Üï rÝÜ»Ç}ˆ>™¶X ¥ý°@…`Z¬×~E#ádj˜½y½1_ÁóÖ‚–RÕkÿž¡šÃ'é]ì×)»Ë¥BÀ…UJ_žšHâǤkeÍøÌ9–A{„>j+™¥›ß15§Œ GíÆxhHÇ äÌ%›ÔùËñêgG¨³‘=ýv á³Ë!x*v@È•[ö+DÑáb{ù¦½j9‚H<2¸‡úbÖ õР®ª‚K¬Ý‰È6G–û»9h ¸¸ÁhEÔjr3ÂÚ‡Ô\˜’^ÕHçbøîíÖl? z¶m¢³7@ªxü®^ð)«¤« lÂóÆ=~Çu´Éz<.1EÕª\^¿/“¨ºzž±MÞ8~ùF ¿6ðQ›7ç‰:)hh­ö%ÃK—Û¢A ›é î Ôú\¸cüú´öŸØ„‘óù8–­ .°f4p=44<ôqøœÚ h8wš§o „‡…Ý9ã‡Ïë ðɃx!Ò§S¡Øš®Þ±d]êW˜çÀhB1ÀÕÎјÄ!Ä.²Y3zÑ·€5^»Z‹þå!Ít^;kù3¨¢dô°[Ú«G‡öÐÇñÈžœ3׉¾€Ì•’!¥|™phÐ*©²9Êûj ×ylÌ¥BÍ™^úõPèý9dê ÌsÈøÌ°ÐåËädNy€ :ФéÇá?E-ž¥e_|ú㻇ß'ý°þ á’ykX‚¡!T‚æXÝ{#+ÓoDb¼$D¾Û¢º"üv]$efx ëvÖfwU(§`®YQ›Iþgæ2h¼¸° âhQöz´m¨Æb¬¡™ÕJIJ'\E ˆ½GÏèyí¡ù¥ h1,`l†Ð$°nž‡À|œ‘69õÖªR¹zªÛMAÃÝó¤N/kÈñòà Y=ÛÉE¼Ð~Ãîë„}EŒÒöB¾ºy;1ÀþÆBRå»Ãþ'-YÿO™8áenl—ˆP\|ÐCu·Z-õ0fY;Úfþôh.×­K»{ñ(Ò(éÁyÿ[l!Š[!Á6 ‰@®NrÕ0jÌ0ØLH^µ¹=ºXHˆÓ°·¹Ýg^/ P«D­>\åÊ+Ðÿ¦ ªây“¨9‚1— ƒHÞj×LNYAúunvÝJ¸c]~þ›/dÜ|Ú\ú1üý›¹ê³Ÿ–h3Æ– ³4lDÝêaóz‹Ö^0èµ™÷À7‹ÚŒQCºk„5ÐPIM5#ÿþq±îÛ:·ÐïѵU#””>9Ô0h@§–õr¼#qHZtAmÙ?á÷Ea‡°óÐ)Æ»×á¿##¸’ª¶Bû"À`Œ€w^  •Ù‚ à>(LKŠÛ÷L,E¿²Çïì¥ÍLûwÉjonϳWà½<‹TøÌÃë‘fE«Y©ò­L؇<y¸Õ3EÁáß3oZ°Zœƒß›7ýÍ~/ÑÏaf`ÒiÃ_€m!ïÇUhß"º³am üíƒV8>qw¯§¯Ñ¯‰šóÆÀ+›÷Óãt~î7–åx¨p0&öÎÁ#k‰ö‹ÀáµW)´´’[Ë2mÖ$dÞä$áO[nÍ6¯{©WÓmp޾–®Ü¡Þ@¤cž¾Ž Hown¹TÆÜs‹Î;à ô¢™Ž¯ý º6O?ÝЇž?ôX-{e´kZÇëc•4˜æmLJÿ(„ ð¡…qŒ…†PI=j"?ƒèëÅz0€Š…ŒÑ?sÉFµˆ3`|(d6r"˜Òüê9„î¦À2A!>Ÿ¹ C´æ‚fNï~5G{Rè mÛoË·¨£ÐÀ²-̟ö9ÂåøD˜Nå4ejJr{žc]žú ¼3gs1GÁÃSðd=dàÈX’‰ ©Uºd ›¦H ƒ¿ÂìŽæ~FðµÃ¿´¥nõ ko ÞAÛºN}ôÒƒös®Î_NÐ&¢L–ëMà7Ìà•Ê•V Êâ- ÏCid„OIJª…gn4ì/\)é”ã)ÛR'¬Dq+xÞ-¸ ZAó] |’›©·Ûàâ¢õ»`6Úøº1MhÓX%ÍaÜ=Gàj™m˜Ä¹`”W -}?w­Ú~ð$LYSÁ´œÜ£¢qAÀ1¨LAê(È= ×ílR©Zpïhlkeœ-HunïñúnMÏ<—d n»áƽŸdK¿z•IA²¸5‡W $úà*3áÈé‹öˆb—°²Í¤tdõ&ÐD‹ƒ¸«¼-\ÁþëãCÁ˜Qßa,f¦æÍv¶nôAAC å+U2máz „Â>*Ï÷íÝî:&Nn¦¶Q "˜ÂYh êAãA8ÆI2‹s¿ÿxŒžˆè—Ñ9yh*U*¬„bøÁ˜¨Nž»ŒðÛî'9N‚q‰©8Öë`¡tA…A+\M§(\Ð_cžÇIÂŽ3(…{ÏÆ\w)/Ï»î&ظëHrãVm+ìß±õ ªôÚDè¡æ’f…‰  ©\¶L8Ïù ŠÂáŸZ8jUé×CSÁüœU[UEøIy[ØÈo» S¾lDXÐ…Ë—«¢2ô¤‰ôÂÔçá{I“"—Öˆ,[*W:ÍKÐ FXô7`À*峂µÐ¯ÍW¶î;¡Ö!réÓ0c-ƒ´¡Y)(ý: *ã+†…†!²x%後Á(,€}Þ ôH¥ÐØ>< ¡NøjÖJ-øÞ‹HY„ `ž~_µ]Gptà॑Ì¢¤./ÿ¨ô냨i¿ ,{ÛÆµsDr”2«¾›»Âõ%=F>0°«¦í¹0kÚ͵°k·RwBÐÿuÅm’ˆÌÕ:GÕÿ~_­nïÑJ‘Ö¹¸óïèÀUžþçÅ›´‰ ÛÐIziÆÌEÇ@ ÄÃÃaסÓȪ¼NQ@“à Æë…9f¿) W#ÄŒŠ´AZà¼ç3;þí¾|Øä‰–f?TèÔ.''%œmV·Z®tš[Ð Î{kw"phˆó^oºZ£–Ñõ¸¸Rs$}ÚH3F©ê%ÅYß³?Þw+{"ÕOH¶¼æŸ šÓyÌĬ“&P?Ì[£ótoÝP×vÍsö;æØU)_õ_ÓšQ+!YxÅò ãØ™ :4ºùAƒ&]Ñx‡ñO ÕB ëFt2Ðzí‡I 5 "Âô„ïä,¨Œ»`0ÄçÂg/ª½Gb´95}ìæ¬Ú¦v!ED´pnGÁ¬æÿyŽuä÷¶'ôøqüðþ5¸ß(hØé£ õzñΧdâ¸ÃØç­ùÕå+þ,€¹Öí<F›&åu„2F9#Ä!Ï#) 0ÐÁå¸,‹WW ¹w òѼ” 3øvÎjṨê w ÚÒÇL ) ŒÑ¬~50i­ãõ]ðOJ‡àN- ŒfbJª^Eþ¦`C†y‹Ö"ÐMyŽÕÍLž;Á˜Œ×Ó‚†<<-x¡ŸÓÛaµYÆs“6eïÙ.j64Îüâÿ¾§–¸ ZÁ{ÉÄs„>l4ïûqázûüµm“:ê/£nW­Áà“éæb4Kó?&ܳµŸ\Caùï;­ ¦àü+c*¯åŒWŒ÷ä5F+ÌÕ¾¾D@ À-a¾Êó4‘%pÌ=ˆ…G Îø/ ì³÷ߦvkï áÌ•q÷þÄÇhF?|ðö.Š~ZoNùE/P޼½"#^¸.é³´ÏÙ&d¢å"=\O§6Ë~Å­Àʽd(­ËŸ¹«A«6—`à õ}®Ò³·cÖë{  ÏAÇÖØM+—ì#-66& éƒ+´MæÊä(­YàËá>$^£ƒ.²¨y‹p©œôŒŸ1O 'òW᜜’¦W†¸Rd¼î.—ÊcîÒm ÂzW¿r¡2”ÖK—hÖ¾ÃÅ•[”g”o¾W-0cF|ŸåØr+rž¼SOª\¥I áN8gs /+y<Ç$\¥ÆÁÐ7¤Eƒa`š25óÅIÉ mÌ«ÿŒn%vÒŽô%÷¡ûtÜ(ùißi“mgÒ@ HI AÒ›¼«çÙ+òœÞmÖÌŒ˜-+—Dõ"lpÜðG`ÏscGi&.›Ù,ò¶r•“a°©%ãª-v.l¸ÒpAåì¥ØœÝW ãÜ}ÚëüP=`?o'&-Ѥ mKİ L­˜—L¿>+Ñø$ FÓºÕÁ Ó!ÓÆòÅÐ ’™åù†Ðêò{X‹¨jŒþׯss]–L'ƒ3H2^(ap{ûxËû ‰hÏtmè‰hn·vÊZ]×<þO'£Nè÷‚²Å©À+m!­’Ñä–+p,`ЊÿBcËÕxÒi¤¼AãLTž¯_½RZãbqM-l«5µP¸ïèY»#O|zÝÏÐ ¥rùÒö`(\aÆç ýášÖ­ªC“»kü»_ÍÖÖ,C?¼žm¹,n †ÁB|c ’»>o\d$.^Ÿü‹ºB+ç|Z6Px…Ÿ¡Î—ÅÀ¥JjœPð§†þRlt\÷Aeô…àêý™0šÀ±yÄmYÉ-ûŽ!bái-|qAô4'ðç²Ç)—¸)“ÆÉk]îÊyKØàÅCÆÅ˜3SwY,ÏP“Å]£Ìkö5V¥,§ý oÃ¥4£zŽtÐôa´ÝwÖ[4ƒ*(ЮÔP˜pñ,·6¹«Ó××òJW¯&¤Ÿ;qüsp/ã8X‹ÚÚ×íÍíyŽ9x\•g*ªÑ™00ÂIŸöî™ «î€ùY< œ°\Õëê¼§Û@§tô·åBÌ™y¨[ ™W<ý8OÕGæM6÷ç©'æ¡j4¥Â³Æ•4h<œcà WœÝËs4s¢ÙW´y,°ÿØY-(0¬²¨OÕÁ äœì¹ Ìïã2ÌI9íyhChºz<æ"¢×ÂÍ)Ĉù ï%ÓF^0Js³¡u䂎·y7"| MHüe/BGžÛã*h…TàŠÖ˜jÞêíði‹×‚+Ëgd’%Ȇ}—€&î‚¡Ph%0tDŸ4w@MK…²Y&¸FzqvO~‚aP¨zkì0ä;ÚŽ\I»ð}…j-µ‚3–~$ÆúmˆHŸ<.V êÞaÒ3´Ff]#u×ÑÛáîý¥¬(üÍdÑU+”³ áÂÃéçìB1Ç,Ò„W àQîÍÑ‚Še¬˜5sÚ=cÿøÀÔùëʽúØA¹M ¹Wm–ðg pE }mµefÆEÿ>kÚ*†l¾Iþ܉^l[>éãêâŸgÌ|èO/Œ^´nwy~ÃÄELüHÛeš3Ñ$Ï ¯Ç"éöýVkFú¹%3~ú %È=ŠÀqý þqF&iƒ©'ñ"o«…jŒ‚™#¸ p`,c<͵!ÔT„–Èb3žÇJ0µ‡¹A“:U´æ/‰1©ù €Û¸v0—«Ú\ͦ6„«åç²ÍY'Ã:Sè-Ï%ð7&[% ÄÓ‡„¡¬éwåE  a¤ />*ßUË‚_¾ot Z!8£5*”8æ±ßþòÈ ­}ùŸ?Ê-zoI—ìCgÁP(|.¿£˜j.ÞÄ#2Ÿ; ƒ£Ï†«òù †A‰š4 Á‡±©.JGµ¢À½Ùh®7‰I \D¤^_äТY!s29 òáîý¥ÝF|ñœD:”ëyÝ3°Í¦ä”S(O•-¯·ç«?o€1'ˆô+WÎÇïÛ¶éï ó§I^3áÆû˜}½cýêÏ0*(•Ї0 4¸™ô$Ülú pz599.qûÆ5¿xv:VˆH7 tœdN&5N€ûB^høÚ­pµlŽ^ñ襤8r²HÁ¾÷çùÃ8Y ƒé eUÉdŽGó 2A&‰9hZE›yc€ƒ?=Ð+Òq:À\wµ§6„Œ(5õ`RCMì\¬lÓtŠÂä.˜}00néKB3®V3±$“]ÂqYk\§šþMSSYnQùeöŸÐm§Ù #7 0À¬ë¿¿EçV¼à׳|6x¿‘. ^ŸçîþŒß“µó-·5g@ŠÜ€eØ'êNž½œ#h…»{©ÁˆOJÖ¾„\lA¸UwÅõ5 †Âgr¥ŸôMA“þm>#ˆ5_‹áM“fOÍ éBGoæ  ã,‡Ç²M{uHp¶´Ìп X@ Ò®Il´-6µ;üÖè§×Á\D7•¡Ææ~®ÞßSï)õ<AèÇÏ>úH~{sŸµäà' 5³€}jô¼Ù«"ÊE~ãÑj¦²º£‡ÅQ:óN3ÌZ}…N"d$7í=j9¶Ï´ ˰œ¡“PØ`¬qÒƒ0 &}7ä“>8’^èE˜¼aÉÂÍØŽµiP%uèà£0 {k¡äfê¿{W “GöyåÒ½ÛV¯Ø‘Ýÿ2vø»°!øôº ÁÍ’È„½þÔòü<íiÞ±ïÈÅÜ<¥ÂJÂ<©±š¿:‹48 sÇ3ù‰Ì˜„uõ{àl¼Ññ¨msO¬æ®Q¯}ú³>M«ùØ»µk}2  WÏð½ŽBÕ †(Ð4EÌ&¹*þV‹i³/p?¢h}õëJ3!¦SôõÑ/wGgÞÏG£‘¼òݯ׌«ø}HÝÜû„òÑ^λ9¶†­Û §)Í×Ü­ 6‰ 5û$/Žå4ïí_öçÜè ³FN͇ŒÏf0®þ3 ™o.ÒÐç§b¹ãÇÅìˆfê=ig †1ì ÍŸ€À1/Ñe¬¡ò[aØ{šqumÕ>t­í¯Ð4LÁl(¾‡Nªõˆø–àôÍß"cPFGûð…tä4Wï/õyp/ãªÐEŽªÇFMì”™lûüíqs\ÈçBO{9Ÿ9eâ„>Ü€uÓé¤$6&! ‘]…÷|¸fýÆW¯TΈ]‚ây‡<Ä„¢Á@zzzÚ{|úmÔÛ®G hÓc?ð[´?4¸™¤ˆWöNžKZ•¹Ž¶BUF>÷ò?[7kØôŇz¥ žª”+¸%ÊÛ’F¹ˆrÍMS›D'ÊÍ_ÍY©…!CêO>kÿ@¸ÓûŽìøßGïŽD›IÇ2ÿQ›¯Á É%íñŒ¿±2ðg/*•1•að*>üü«_´jZ¿E^锋FŒFsŸü@FF¦fÞóÀ„fR¤ú%ÐLOÀjµA£‘éUºu ¾!Ï6îÙ6GZæuúþkê"5 ¹}J¨ÁsæãG­Ù"ã"¼«÷7¶¡°ÇÁa5{¡ŽsØ®`£% ø(€hx굉‹yGñ~Yg öß›# 'm&ƒ=W¶S¯ÄüißýرO¿kçîc¸,¸lŒ2ÓÑW¨>5!p0ÀÐu _ʨBt‡u–Õ+¾Ù½‚Ì™nìw£•0 &}172x€>8àq&ýp¬ââEñ—^ysÕ¢eÑví÷fjCÑÉÂxéÎ}”7ÁO1@“F„j-nKÝñÂKoü²jõú3h.Ç 2g2~pòã"…¬ÀáÐïÁÀùw[}%h ®¾Q2’4X§ÑGƒ¿M¸†!ƒH—[þ|‹ŒÌðµ'å~DmR~ÁU0ú(x›nó ÑžiVuìÌEõËÒÍ:¬²£ Á÷w&hð¼3WïÏòα[e-ë›GäTâæ¥Ùh#³Àç…l\¾8zÏÆõûºÝ>dHffF0ªPWÙJ—,acÆU_'BrÓ~ó’ ¤¦§Û˜ž û°¢dA&ß¿oÙÚs&''rE’ɸ‘aaƒt“ˆ³~›ôÄÜHàAúaƒ“#WµEà(6þ’ÖðÊ3“’ê÷EVìŠßÌÖ‘I¬åˆ["#JYG¢.‰nBÜÒö–ji®â:Uôfyßá‘Rl±‰É6ô‰?2ÒÓ.~ý·‰ÿA¿°9^pÜàøAaƒf˜6Øq¤L ˜(8žR-Á-ÿ@áž}Cß}Ѳh’FŸ‹!½Úúý»ÒWªËmƒ¯[4—š áÏdïÑö{[Øàä Ú N$Bà*))ÁºhÆÔé87·Iëöj5lÜ¢dx©òÅK†– 6Í#<ÚË® _ii))q)‰‰—OÜ¿gÿŽ-GðÑ^‘A £@¡ƒÇìw£s8~ÚÁ¤;*nœÒ‘Âcí/HKÔnpÜ Zøó?æ5iÛ±qz š†……— +ù×6îOú!!.Ž÷èU¼ UªEô½kÄðààà,õ)7pMCFzzJvy2µfybÅóø) ü þa_Ÿv5-5>91áü‰CwÜ¡½‰)TPÐàÆqƒUúìsö#Ç {ŸáØ&ò=¾á64B`Æs&xLžÊ-P`Ö²-ªq«6OCØX‰6s|õÚëmaƒ8ç$ÁU)™Èç8‰¤íÛ¾y;¶}8&#AIÛ.”àØü€?!Dö©QP`ß’) s Œ÷yY•4鈺AÀôÁ:eÁ‚­qlÐBȾ­·aÛƒk;d2•½.ñ왤Ÿ&ÿóßÙ×Y'7¡cùm¿Ç,ï5üíö9AHŽFaCŽÙߦVƒ3ÁÄ€ç0 c(÷¦fÃsx ¸šèû«"gó&çD‚/„ 6šL€Lò"€p¢a k:{ʪ¥¦ !XóŸ½ôû”}H­™adu’ýJFA´®ˆ×¤ éOÓQ#4"š ž3 tÌ¥Ó#Ç3¸—I•ã‰q@e}R'ë¡Ð! °Y> ÇžÂô…ÐÇY¤àØ!Ç2dì`ÿð>L ˜ÈÆŽ»/‹Ož8aiömÂSÉ8”wåpjšå£Z³è „lÍ–ŒÏBC_l´LìÆc68ÉY y… £vC^\ö¸lBa@&{a„Ù32 Â(pÏóìc–“{qèLúpŠ–€:)}ì ú "„F(pÈ3Hcjeìà€iŸD³)4pœã^®Ëý¤MÒ¨0³¼—eÌòY8ñ~÷2np¬`Š€Áþ•±ƒý"}ƒCL ˜È¬6Ëøìò"lo×|Õ¯ÿ2mæÿM¸ÃxÁ<¾™0 çGÒÇé,°YöËaaö¾6ØF6^ON <æÄÂI†‡£ !Œ.™àGfAúŒ7ö¥‘Q`ÿæ‡Q0é»À›ôAz¢p Ï Ýqüp\ C„¤2˜âPß/ô&tÊórŸYþÚxíIü¿éGöµ2nð·ù;p› ¾ÆðEä&ÙùæÀ1¶M£Z:éÞ¹Kñ:-ó 8ƒÜ®;»ÇÊ<Î?8öÉ–Ñù–Ûší‡æ©q¶9\þ«óíÌ Q´fõªë‹ðËé É jëþãŠIûvlf×Öý0o­ªQ9R]ŠMT«¶P[ÔS‡OWã¿Ãžü£ïæ«z5*©¡½Ûª7ÿ3K¸­“bÂXškÍX¼Am;p‚Ñ›´¶cx¿ŽêìÅ8õÝÜ5êä¹KªñvUõªWô-B ñ´‡uS£†ß÷ª1¹µ¹¿µÈ„¨¨¯CO§Ÿ~ƒÔ‹ÖŒª– ‹-",ÔZ6"ŒIý¨á0ÁO1€¤mÖØød[Brj&œ·ÇL˜µãG5Š×ø$*ê1šÄœÓG¸I…Ƭ÷+ð}<õi©ôŒØÑÅ‚ÕXL0MjV®pÙš|2“Hå »Ž8×:µ¨×kòë'?3b “ÊOÙÍòJy?˜«¯bj»€±cϤ/þ¯ê™‹±eŸš0éMU£Â7SÆŒ¡YÕœ¼ohÕ°¦Z¶i¯jÙ ¦êÚª¾ªZ¡¬Ú‹ìï—âÑ•|ÕœÂF|R*|6¬*ÂEÇæuÕÁgÕ°[Ú«òeÂ5Nä:Ä%$«ofG«a}Û«:Õ*ª©` Wâ»Ø­¥Z¸v—º› ž½ÿ6uîR´(tÏ2¡ºä^  [·Na2P„Vjª…ëv©/Y¡îº¥2fé^¹å LÿN«Gïè¥â’’Õ·¿¯Ö ö*–‹P¤9šï•)UR îÙ´VAÓôáSçTƒš•ÕYÐý4hNH m#©>þuùfµa÷Õ¿kK:¥´6˜(6¬dqõ܃ÔJdo˜:­zmôP}OýsÔ. èW5±Sff°íó·Çm,Ì;9” Sa^î}ò¯o8›yæKSÕ!Ú:6¯‡A¬º%,´m«Mð ha095MØw©‚,ðCŸ>?úÕ¨Ñ_¾µ¯À¬@`ÒGÐæO7y—> É°ZcR6kÚU+Ûº¶nÀ•¦HŽ=Ú6Ò[^‘a–w)oà â S\+„ÕGîŠÃM¶C'Ï}têâO¾õ´OKp=DŽ羕þyÉ-S!xó=rrâþÙ\·­âŠpHp°Š„°Ð°V·eiŽR÷è³áÌa|ç¡S*2"\Ñ,‹Ð¢A µußq-lPcrLߥØ$Õ©E}}=þ¥^M·Ág…aà‹68iiÒþ}Y33SSÒ‡N)<÷`õ_¨¯OþEÝ íUjvÁǨUêrùÒz#½m- €@HMËPŸ¤5tüM­iunÙ{L•-®Õ®Š+vô°˜Z³­öMëªAÝ[éßü—”’¦Ž cøØá}Ué°ªK«êãï€Vr@öüòÀÆÅ F#GK­éêK–¥]¿òùÃ׆f ÏÃùýïU+–Q÷õïÌ+àâ|âü†)áP«"¡Ž´påjÚ‚õ•OŸýý‰¿¾ùÊï¼ñ1^”GίÖýÛ›ôá?uÕ ô¡žš0ñ‹-ó£²áAßÙKÕ¯QÉ?Š*r6–+Ùu»…«äpH®s!v,Æ`Áâ”Ìïø‘³ò¢û¥Ç¼«©iWbã’x4š ³'#X­V4ÜûMgßÇ%¦¨w¿šsíñÙÔ½µJƒ8eæ2˜À”S#u×+Ò× úçQl|’-ýêÕËh]N¤ú¸¹S&NxÙá‘Ĭl¼d¹š–vñrÑ)Ý´n5õÖØa0½Û®@ûU*„¦Q›öc1 Œ|&Рf%» Ás[ÖWË6îÕæRJ:A ™Z9¹>G™0ïKÓ‘©®Ujÿ±³:âµrœóN a#55È‘?ZӅ߆·Ç/‹Í#r‚G*Dç²·<ùÚ[/ ?ìдŽmÔ=,Þ Y–K;ÌË^ÀW»^ytHðÿfGÛ6í=úÁèqQ–/ߢÀA‰9'ñ:¾IÎñrCœõ}–*‘ežªS½bð}·u²„–0M*nâÀKyxù‘AÁŸÿ²Â-é—¿òzɯÞk2.’ÀÁqNÆ:[jrÒ…„”4 5ÀdBüÊ–SGÁ¤áð© XŽ0žòÊqh‰âª4ÿxÏuõ‡•,¡†_GߎMµÉÊ0qVðû›ý~5%ù\v3ŒtQ„-Ëñh Ô@[R’“.'¢½)©WUÉP*J/Fuèäyu[ÿ.-´ö­Oû&ZÓáì-yÎNÍê©_–nR;žR§Î]V íyÝmôýà}®\ZX(´Dûü<´,µª–¿î>?Á sVmKªXµj± 11FACÆ0¾‚¯|#,X­Úþ[‡¦umOÜÝÛ4<ÚþQ?Hö-û8((øýQ/Žˆ–Ñ4Ž›;0éÃvnk… b (-þèQc‡5dHwSиAhÂødžq«6Ó4­m„k’?%·ñÃXMQËͽž¸OÜ¿1ìiCîïÐ>{Æ`•w:}þŠš·z‡:s&M5íM¯R¾Œ¢s.½™c#¯@ÇYš¸pÕ9¦'Ž‘«ZÃÿ"LO–l؃z3T<´0«ÓÕ“ „¶ áM˪Æ0o/¯Ï.Šrìoöû©#‡iÂd–…FŠ¢YÆgò{’-èäýÛ!#[èü@ß!jÃ.Ç'©]‡OC“p>DYQÎÚ6®¥Öï> ­C B/[5-; Æw„°j„…ÒÙˆ´F“*£³¹±C¶ì=®ŸËïc;pU¯z%U*,TÍ]½]›Nщœí ذû¨Z´a_­†-ÚÕB›…N½F£¾ÐlX~øÏaÅŠû’¦SÔhJg˜í,ØÇ1—b­1,Súô¹«Ùòå³Q“+ ‡ICsÀÞ•_úÀ‹Zî\]cÈÿ0‰ûj$`ñÈ §>"ÁpŽx¼zا þèasäøáï … 4’ûÌ]×ê|ë€8ø´EÐÔÔŸ?¸g[Ø _U?/Ù¬CÕÒvýÎ>íTOø@ ôÆŠñÔùësrŒÒýÿÙ»ø*ª¬_J Bg.½WEÅÆbuõÛUY@QŠ®ëîZWÄŠAEz‡%@€PC =$„4’¼÷ýÿ7¹ÉË{/íµÀœü&3oæÎ;çž¹÷œ{œd çbéíW›%hÁêâß/ç Gçs>ëäéX´v§L¸JM;zæù ½|V–Qú{p%ÛÓý ßxcâî-ëcÐV-MxdÓ£v†ïÔ¯ÿÕ¨#§+wkÓÈ£é”L@4©Uá`z—*Í•ºµi,†ÂÜŽÀQtÚþà§5R¨¥¦†>t·aJEGrú}Ø‚`òO‡ôQ/¡+îi‹–ïïƒçmS?ýMž§`ÝyihŠåé°y÷aÐifü¶µË£ÑVµHâ´f»BØð*S#àH™5è£a©ÆrÚ›é» ìãqƒ»xÿûûU!uC[N›–¾ÆØbtúp[O¹çÁE¤Û>µë†¼ ACyìž.sÇSúôé6  ›Ó'þ$Û§ÚHh.ýg>vd—·÷Xú´űº„m)òíü&†–¡0Ó2¬›}1(¬4š…A€ 0¸‹‚¹S'¨C0x}ÍÇ\ “¦ÍìÇÎ5}ƒæÁŠNÙ. ñÙ1{#uk]8ŽkkÊyäáý(¸¥eÀì &xZ ¦áÙ±ý“òñº¿oy3ãoIsê¾»Û6Ü´@Mœ–žƒƒÄ«†KÍ}™”¹¿i†~æ³–íÑÖéIÇôks»áÜɸ%hé€Ái4êìUBÖï]¦¬ÏKˆ0aòäÁV¢Yÿç0 °¯Ùçå|}'£ReaIo:}8 㥫¢BÒ‡!44Ôûõþë5£Ñ0ºt½¡ÞÚ’b Àßÿž¿ù”!ˆ¸(V Ö å„MAC 7ÃW,[cÌÊJ„FÀH3 O®ÈÚó/‘×s¢¾KÙ2>.6V€£/£^YBi4Ø¿ìgäó¹öçÒ¥x%l3gùjNým4¦q³xˆ6f†¯þsÛüÿÍý"333â¬Çþ´ÇØ3 GkÊ•õ1 Zİ-öÚ£-ëîc Fßþ±Õh2f'ìX·BEU4‘¿y&C,¬ìbó_(ÚKæ¯hw\ÚðÈä¿#L¥©:V@JÃdQðé% ö9V£«úËÄθÉàÓG¡±yû,}xµì=¸ÇÕëéoŸ8wÉÙcÕí‡àRþF4™«Ró•.}†VÆ«Æ÷Ëo¬&kJd2Ó±e¤¦&߈ Ûô}<ü<à ԟí\ °ÙÏQáŸ%'ÃÖ'‡H ¤ ®ïÌN¹Ý-r9žtšÆýÖðˆ ï¼÷ÉXÕw‹`t«‰ú‘31ðݲpÓåkɦ›Ö¾íÊ$h6 Ť|4:oö´‰ÜJÚ&-–´.Ëû)\x—õ-?ÄkB Ëëwäï=ˆåÌ­$ÀÄ3JmW’zœ}/ûœ}ï8ÏRÚ %têôáìððú C^>>½±¨mª[½hÑ>,W‘ùÍíFdW’_ŠåÏȘì…:߯ÀÁQï§Sbq€uÁ´çV¸ç÷°ƒ6íÐþ)4ÆÓ… ⋈æj±bâRqœ¶7|óÞãÑQ¿#JŸáËÅ›M–´‰2:”b °?Ù¯ì߸Øè_vn„ã‰ì{2ò¤Ò„'}„Z:¥ AÁˆ[Ê[³ÿ³û»ÑL8€„Û ®§¤‘]Ýp2æÐOQÛÃâý´4J¡ØitÊÕfgNõ* 38T³ùu£z2s“SÄ›Ï܇”ñ9fÝœ`Ÿ3_&a${•-#q´F2!gÃ.D „6¯'÷Öþý¹uŸøs˾|—¨J¦sÝÇ ÖŠ¿ŒìÁDfùÊxÒ ªãÙ÷i))uÐ.Wrà%Ass}àY·(!Õí0–8iÞZÒ,O}ùÂÐ$Òf•ý²a#^àXÅ÷ÿqÅv±ÿèÄEOC›Jbxv¢Cóº‚ß3wtñ7“€qi_Ùgt”ÕÂ’ {#þ€ö”xþ"ãÑWK7‹þö°4)áýU*V°)%O¹?â/_c²"cµ!)é:¹Ž;ȸ9mBDÝ%¶‹íc;ÉhRØ „)Ǿ K­¾zñüÅŽ½úCðŒ ú´Ñ”>{~¸_Âóh0¼-£NÑܘ•™¾yþÞ°Íh&ÍR¸±ïIžH³Z:¥°Á…`ng9/Â×­[Ú³ïß,X[¥Eƒš Óú†fõBd>\ס`€!¤ ÷{£îÿë+o.©Ü9œ"}&a»ŽMÑ)rœ:¶:‹c%Á’pÁ{”/S†¿ ½;4 òÀ±³V…š0ôTH‚IZú!Å=ÃëÑ+ÿ§•Û±š.¦~ò«h )þFZº ¸Zúö¿‹j•ƒÌ6ðŠµîˆ°|'År4+i¢~*ˆæÑM†Yã}Ó q™ˆ¨‹×ï—®‹9/ŒÍƒ>ó“_Ö |Ì ÷h¾F¦@1ÌàZ)Èßœ5•õ¾õùR™ñ’ÑAØöʈ”púÂqøÄy¹²=adO&kOœ“1¤ïíÓq¨+Êú/\I?¬Ø&Î\¼ -P xhH7Ä‹®j~¶£Ø÷0¥ºI™Q‘.Ôæ2ú(è½J» úø½==—³ƒ|ô!'<܆5yƒYÒKÀUÁ¬ó{"X P"ãDƈF¹ßo=|³„M{bÄÄùg&ã¦u«ËÈ8õñ]ì?rFlÛ!@›‰_ÖDˆ6kÉoo{Ô1|÷QrÜhÑ †xsT„j%Y{vh"ë·õ¯V«]nŸ¹Ïb”.0hÁÖ8¦-Ãã“ñWD÷6Ì8h‚w"0tc „ ;°‹ü̓H?ié™bÍŽƒ2n}m´‰Ñ¸¨a9>2:­qD{£ûw”Ù}¯%§ŠÐfuEïÐæâ»åa" ãdÇõ¯{{Ü­¾eËÑ׎ž´J,ñ¨ùGFŽ7Mίj“/»{Xä‘}{v<¬OvvV/0«45­à[ÎT‚•+æJ´I‡b` =3ÓÄŒðLØ:7 ÿ’OÅÆlܾzùšÔÔ‰¨’ œ5&Î…c~C¤S®j«ùX»†ý;ÂöDïÙu¤ûaýðž¢Oœ“±cÁeÿ}ÂS€¯¯™w$SËp³Ì‘b ŒÚôÆæÐQ —W˜€ÚÓ øII¿iº„„™™Y/¯ŒC—½³%lÇ<•¦}ÔfPÈÐÒ)i€gu\4cVçìloÓ3§ìB™bƒ™`Š]ƒíIÀ>&¸±Û.â¸+µaÓU"ÄéŽ-`6e9Io‰<*W#&Œì%’RRe\e&€ #Ïöq篊 #ï– vB€Øsø¤6ââ¯Ê•AÆûæJ!œÝÅ¡ñ`"š1t˜øæ÷-¢;¦‘áX³ý LœÖ‰ï“Q®&ÝŸþ²^4¬]UN®Ú·%ÓðéÂu¢&óáZЬ7m!\G¨À-‘±¢çæØ*ÝñÖ¼%`4‹G‡v ×EˆÍ{bŠV–ç àç[æeƒÅ0D?¯Ú.¦>y¼æ¬Hy&]üv)}Ø{7w ª®&‰Ÿ!,’ö*AÀÑ«Ù̇ ™Ð«ð3ܺ÷ˆxþÁ[«ß|KAõ—Õ’6¾’Ìêfô-cÒ$3ôöéØLô‚@N8s!A¬Ú%¢AÇd&} òñû±PÞäÄ6èCM|…~r$#³Ìxè£úv1ص7ïÃBCä -ƒ@°¾J«@ Ã}};Êñ`ÉÆ=RXŸ?øqµd‚±"ì<'i/éáÖÞEôaïÝÜ!¨2»é'La“.óKÆÿK0Éw!ùP`)DÒ4*(ÀW ïÙV0{¯k‚*…Nú$Ȭb%J¤¦ßD’£Î21Wå;C§‰Îï0¬àï+Þ¦çi×ÿôèÞªz7ìóч3 ˜Ý£ }èZ˜F1 ØKßBxcîÑÚK&jÂʱ¼«H"áJb ð œ q™‰Å$È÷\¥'Ðô¾á¡æœa,(œtÀŠ=æ0b/Æ t즆C=‡ ?B“h .%$‹Éï~o¾ü­ØÇ´åÇì,Û±cÔFh|á#€ µE·Ö<-RD#/ÍFÙîŽÍë›´‚àËÅ“]Z˜««„wÕŽöÆjY Ôf0!\VV¶X»ãÆÐšÐ&7—×ÖBØa‹b jìÈéY£Çýã‡HfŒ+†Š–Õ9NìÏ ŽÆP/qÏzÙwò” A:åF!ƒçxeìÓ©Áä9Á!• ±Ö@žT7Z»èŒsL=?¤{iÖÔ« ÈØ#M¦vGǩӂNµÖ a­ªr%ðø™Ë"æäyÑ u‘a‰ÅMŸ šK‘i»v=f ·L jW –Õ]MJ°‘Ss++Q¸ƒ!IãOMŒJc­…=G“LbR¥’Y³CHiAbã.HŠ«² h+|öRb¾U`uÝû\P“)÷—ÓGÎcóÿw‡ Ê•ã+Xõ}ùÑ!Òl‡ŽËûbO‹m0Ï¡°A 0:ë¹ayTÐETi>Çà4“£Ù<2œ±§.J-i¸qjò¹…1iÉ9Çœ±A²ò>xïÝùsÎ¥n’äq Ê|Aýæž“-Œ/—l­¡máŠsIÀ2+»¥ ™ª»|n@Ÿ¿bõW1´êš öêCà£xLp9}ä<6ÿW ª‰XA'Ô¾ëV YLP-™½¢ªus…–ÿí6Íe-„ˆ»K&›&xÔpyhé£DtAœÝ-'ºãØD®^„ ™¬Á)8OÓÔ‡Z~ÛwECãëM+Ê€iV@³4®öÏzþ~uʼ§oÔöÇÍ¿¹ aÊ!s³ÒªØ+ÇkEÇxOHÕŠ°™î%^ûh!Ìõ®Ha£[ÛFbÁª*Kÿ2å³Eÿ•)p%¦#Ÿÿ¶AšG¨÷ FÄ¡ãÒ·¢VséàH&¶Î4QÙ#5œ$© hV?DlÚ#*bå‘exžæd:†õh#«¥#꺈h±~ça0 5¥¹WLÔ¼K=Öꞎ« …yæBké|ÙΪÁ¹æVopÐI¶‹&W+Â÷‹±:Á”ÆOj|œÚî–©”–&´ÇzÃ’UãJAµj¥³µ  ˜½‹0©Ñö…¥É·+Š jÉð)ìPû÷Ù¯¤ùÔ@ç­•NpõÞ:}¹ ø-WÃÆP”g/%À_¡yõ$$q,R€ µKl ÁÔ…ï;‚ï>Zô@ä8æÅ C͸šÔ«.è³±mÿQ©5¢É•#ÁÞ8¦ž“ž‘)ÖC`¢™ÍôvDå?ʯ}“œÀß”˜¦bûžÍê†`,«3±(QBÁr|tÃ8¢Æ —Ï' §ÅÜ“‚Ó© -`)hh…b>R¿ÍI`_²U?*ƒýÉM+d°ŒÑW<lÑ*™T­0ÌÖ³¬z?õmRXV³¶ éŸqBÐËçàAáБøQãë&¾I“¤M ŠN)d¨1Iõ!N9œ)lä¶ÞufT ]í›ÕM¦-V“k ˜k:5~Ÿ ®¾Ñ|‰¾ $È<„A2åãEˆFUK¼0n€\ád|åTËPšèü¨„ ô•X½=J:¦óúS÷õ‘&Vj…OµÉÚ~$(Á¶þë¥[Ä߯•+ÍÖÊ9êÍf&ÞßxØ&ò•ÀˆT-'Î#˜9êyõØ`\Oí2ÿt¥ J-iŠÜ£± ðøY™+†š.{àA•QÛ©gû¦Ðœx›íõí=××´ô¡&$î9€ àL¿*ü€ôµ¢R7IzwÁÁz”~ŒòFæüÞ>ä"€­‡RÁ2 ÿÅ"äï¢ÉU \8`^ïþ —‹ÃaÊÄoÜQ`oSÏ@sÄ!DÜZ mM&|&r¢äuur#’ΩMe.­ÊÛ6yÂå2àT8ÆõÉ (`m|´5ލ68x¯&EµwpõN­ŽmV“;Nöd¸Ê¨] VŒ‚ö;@<ìGnìCnìSŸq¯Îsï10iÚÌ~lÌÜYÓ7¢Ql»ö=I§dT•¡h“eÜ«1šô«h˜×ª>Eÿ9gsÊéå‡öûˆ{nŠN’>­ª>Á)ׂ „ ç¿Ðl+æ /?28σ¹¢ÿìØþ"Žä̾MßNÊ:˾þøpéG¡|(81Ï:A^WÿÞœ8Jš÷ŒÅ- θê^^$by?Ïk#þÐ,¢ HPŸNÏÛÌ`Y¯¶>RŽ©ê 4ÜÐ)“!5Ur†çÔá\%¨Ò¼ŽBí7ÅúÆg¿å0¦ˆJÖ«Î…KAµ0÷¨2Xè£À¨Uôà·A:"³ìAÀ¯ÓkظǺsÑ€‹PÉr¤mKº¶üNº‚ÉæFð‚‰Ï+ð¡†B™­©EúUq³†…å– Ç|ßòe¤™&Ëð›f@†$ÓN“F¡°t^çf –ÏÔŽ#öÆ1UßáÕ Ã±¨‚å,øFX3£ã6#iq±@|q|£‹BÓ7MµñÑö8’Üû/r†ha|jŠœ Õ$ɉ”[iõ¤k2Š1Ó2]¹3Ri{µ;¦½ŠöT_ò·öØãa4¦å6jC!Çw"}ò½¸§À¡hUѧÂ.™\^ÓnªŒvÏòm9u¬-§ŽsJëå‰#…íÞ~´eT_òû´è`2Äý¦üwÜÂFþײ}†“('lk âä[»VÐ9­ QPYO¸nÉŒyB›ÜÑw ª m;ýé{ážÃä*á—x°dŽ-ÏY ªÚò–Ì*}úÿÈi䣉s&‘ùYÈ`þÛ&éçäAÂ†š€¼ªÕ®÷k>fª>kãcqq­ê,hÏÜ)ú n¼kÃêx”ådÉI³4ÛO€›×“¯Îèèåí5Èh2Ö…”‚.sfXÀÒŒ7··—ìe CÃi//¯UŸÏž¶Ûí*¨6º RÖ®+:3fÌð9ŸéÓÎd0…b#4q¡ºµ›°zõó¼wÞü×8†Ëoõ™i3ŸÂñCzy`À5øùBƒköì͹"Λ=mb‘n°QøŽ6làA?­c @ Øcð¬1bV˜[€æm®F\b²5šú¹˜ âŽáu= ÔŠ¯Wèup h:u~BãGô(tèYÇ<¹xµ,Ý)Z´}ÂÆFÔ@a£´ †gž™á›ì5ælÅ·mµ©¢ŸŸA*˜Ôt¯ƒbIýŒ‰×SMÉ©é^F“iæÄé³ÏÃR⿵ÊÖúxÆŒÇiw;aè /”­Xõ/ŒÞSÂX‡,«¿_ùì5ª°He•N‘‹¨÷gÓÏÌÕ"‚~kÌ]f ôòB8?sß8óúê2h4Ù'7Lÿl©ßåÄäë†:U?œ7q"5Un]Øp Úõ‡êph¦u!Ÿ—Ã>ÿjR2 ™Ly˜Áƒ€«bµÏù¥ÿ/1qw‰ëpe ? fœöÈZ!£4ŠŸRxžð÷iƒ¼Ê—ùÂd4Ö@"Xµð2 9ßQÏÆ€d°SÓ3d‚à]‡NTGø÷.dÇÿõÉ×g<ùÕ»3Ö ù¤ÓÒ s½›ü·:~+…S®RÅÔ9rÀ ù…ŠL§LpÊ­° —·©‚ðƒÅ¹²¨¡&ÆÍš<¶mZÛ´.âÁëì•gžøÇ[¿žóæf\w9ê†ý~Õ¯ê¸í00¸[+øä䞸í^N¡Û2J› !™7´Ûû‰×ßxÉۻ̻!UƒLêBÍ’.L—Bjenª.Ƚ…Ípù»¬Ž¨vîRâŸOýã­×¾œó&Ã4kãÒô†R FƒË¼4axÿC'/UéÔ²>MXu:-M½hÑV14GtUÐiÃË×’7€N'ƒN©}r)ZU…Y´Uÿ©c@Ç€ŽWb@1”jïÊgëÏòL áÑnžÙʼ­b{¹\f«Ó^† ñ²µ^ýËïÂfOÏ[þËÓ0À~|mÂïŽÍëy ^ï?9eÆËh#û¼´1èäÙî²WÎE½Ø¡eãÏÞ½LQ|åp¯ŠFYE¯öMëš (þø hâÐÔBÑ餳:?=ýN%}5]ذƒA•aØN‘b_böræúÐAÇ€Ž«(«ØV_D?Y2 À,€Œ›Zu--LœYÐ÷üËCË”)7‚†é©Ñ½ Öòç” CúÝîÄû“ýÊþEd»ŽeÊ´§PŒœ Ú줂›=0ÓêÑCa÷U®\yüùtÞÐÆJá5Š!¶iRKÀ_ì³Ç^šÂPˆìg»cª1SÌ1³ß)é+ëfTV0ùšøuÝn™ôÌÛÛ êV¯"žÕK!ÁŸ£`Ù–½L¼ýUå]Ïv$bcOæ9Ppød¼tLûËÈê”Ã÷L§2F3š µDþkYÜ“SÒļśÅää‹ôǦHé¸ûôè>%n#Ã;3±\3dÃnŠü¥” aÌÈHßÌÖ%^U) ©iÎ×Ë­®œxï¥qyúpɆÝȧsPÜݶ±(m~…y÷–‰AbØpË\B…½§äåd.2ojbääèÉZ/3óÖ²e§  J•ÿWã®J¦ñ#{è \ɉÁck@ÿÎ_M4ž¿l˜×§Ï¨›6-½Æ2ê˜Ûhuެ鯀0I«¡¡ü†<8èÁõëþ[4 ÀX)¾œš½aëÞ£NŸ5~Ÿ“ë‡Nã¶iÔ`rˆœ ~V‡Î³ŒO?å‰âùˆVH´æHAƒ|üÞžâ©Q}¬<]?U „#)ã±3óÜÊLÒ…Í[‘çÆ"ü¸‚Dr Íʼñ—®‰o‘7cmÄ!«50<2¢o §Fþo‘# |ÚþÞ­Vhã$óglBÔ! 0Î 33¿øÝUkë¤=©ñ§þõÏÑý;jÏ;õ˜ylر§ÎçyÎîè8Q’ˆcy*+¥?²²³Å\džOD$3WAµà@‘žšvÏã\¥„ W=¾¸Ïa;ù‘—kÛ¿ïs §êðÑðÖ5ÅEgé¸ý;npoŒå!uC[NÎ¥Oæ±ÌBq«Þ'_JÊøÎï¥Ûz+‹‹j8úvlVþÞ¡wOA*;‹wU1तeˆ‘½Ú‰Ã'ÏIçB Ô ­ÝqH&‚+£@®@[²52VÄa¥Ý •œt( ô÷QGÏ@[s‘Aª!Cvd¼.—§-ü±ýÀQ1¤{Ðé>q)!ŸœÐý?¸/6Nf•æóÖª.R"œ£Ø²7V2‘ÝÛ6’Éý¨…Y¶eh¼&èú¨©RQôéØ Ñ«üdvmÒò¼ÅAçuÌÉñò5¤ø'”ÔÅ=…—÷E†ðÈÃq¢94C„“箈4ä#i éw2Þß- ‡±ÒO-ïa&x†1æXÀ³˜!¾<æw*Q 5( ‘yœa‡«AGœ÷]ˆk†B<{)AÈÄ£1¬Gq7èÑÖX žÇ¾Z»ã ¤éQ};È$¥«EœššÈpάᚇ.\³Sì=%ËöëÔBf@ç»Oûô71f@g±rÛ~qízŠmVOÜ‹º¸"öñ‚u2'ÌZ-|ð{6rµ8Ö]Œó n9j7g?º¸õ“¹¤†“¸¯… “Z5ªeÒÁ‹‹ÎÒu}8Øß‡ŽŸ£°ñ!6å„ëÒq¬Xã·¤è´|yÿòO`>0a>áyî øûùÝûëO_4óðÓGñº¤Oj᜞,u;í¥ ª¸]“ÚÒdb &nNÌ ’°¢7Y¸5“Æö“ÂÄ0„›™™`$ãÁˆm¡ÍëʬÃ4éÙ!BÁž˜8qõÕGæÞëÈ<œœš&/?{IÖ[æZFö”š öž§êÔ÷¶1@¦ýp\¼,ÀãEk#$N»Á†Lþ~0î„g/‹ó 8zB_*BRÖ;ÀØ“¹_‚ŒÛ!:7ÚÖpu¨Nîïƒo¶ú龜Ä÷MàX}âpB2îuQŽ LÞH`Ÿ·ÿ÷ñCE[ÐÅ¢µ»Ãi¦B ¥@;‘Âf>w¿|醂†½±@=o„‡qƒ»2<¥øzé.ÇdWBxc>CÁ–È£2tç„‘½ ·‹×ï—¯]——¯&Ý@Â!7÷öî ûŸÂ … F¦!ÜÁzÂ=Î3M”ÉûO yÏzæ/¶•‹xåÜ÷`gì«"¼­ÎÀyf_9¥UìoîÕFýe"ûŸ´à‰ý¯„â²Ãyün£ÑЋžØN§ô‘^)¦3øåtïÚá\%=8µÿuaà ÕØY îÞ ÌÜñÆgKäJ%‹8vVNäšÕÅÞ+boÌ)s d ™³€«Ü°3]Z7”L… WK;µ¨ŸÇœç™ð†&6OÜÛK2¥ƒ° I(èy²þ¯Ð`vd®Ú²ÿš× GO]°{/¾Ç†ß-~^!" pØòý  Ô™ W±R}U 43R@ÚøsÕY Gðlfw&½Ð UÃ[שñ€·Ô¶1e;ûÁLU§/\ƒpPN 6\Öµ!U‚Dfv´UÅŽ\a7þÊ5¹R^9(@ÔBÝÔlP«sZjØ /‰K×’%#¼‘&û£)Z7húØfšÕ„° ºRR¥¢SsÐÓnª9.Ù7©SMjø¾„ÝønC[ÔËgæ6 KKé#T¥b€Ô‚b53OûîíÓ^tÀ™vÒDln}HÞ&5JU+UmÕF½Fh¿.ˆc'XŽ´Bá’xN‡FP˜±àžÞí!ÄÔ½B›JAièÝmäótm)(„ª€‘Z—jÁßµ:è%R3޵ƒ`Õ¿s ŸÕaNLhYï ôÙpq4%¹Úd<ôiÖ¼Z\ù®jýñ½˜GÃC›«7Ë`³ßýƒ¡~—™©X{—IÓföãfqtJÞù } ¼›×Ûaœ/­ÀE¾ÝÑ'KkóÝÖî£çïþèã­Ñ ÅN•œnF•m2¦ ó&' x©2T#z¶“& Vïæ"o>3Jj.hæôÎ×Ëo½‡Å[QQ@‡e {câD?_© !ój ×oÀtª2¨¼W¨))èyyïð¼_X!7Á>ÅVË\Idî”ã6Í–Èä) x.5ýfÎüç*q&ü1ÀôÓ¤É p•ûa˜ÓöÆœ_ÿ¾EšáñwÝÇZ V«‚_yó)š'1Ùá0§ó—mëáûA3jÌú]í!T „yM;0•ø°4tCS›„¤ñÏosh4%í¦4!k -džÑbÆÜ%0«)/”ëT8a½½C›çisuØÌ,ñ%O:àŸúà[y äñÒ³:4¯çô±J½kšEqã1€fNi¥€Z®ŸVîÇÏ^”¦e×`Ц„BU† BCB4jÎV†ï—”‰[„ÃÆuj _˜\Q³U ãÁQ˜¾0n ¼§0cr…ÓÚÕshOý¦éûrp„~( OA] H£¸Ï]aš§zŸ+?SµáÐ〓5… å½||êTð-§›¦x\79·A4mÅwcLKI!÷NZà„Bvª™ êÏF“aZîÉ š‹¤Sާl›Ÿ·O=¿reXðt*³©y¾ÍCš!ÿ ó]K yê{Äò´ù75Ë&“QtÄb®«€&Ê´|Qæö®z®#Ÿ³fÇfwÕ¨õ2ê|ÇZ5ÎÞzŒÉ{ëGñœ>3³/%&¥p‚аDÅo°+ï ÁÊ"µ ¯}´öóW¤í5mûg=¿Íf”Áj±:µlûíh0$‚ÎŽ>,¶Ù‡/äuHeÚzô<˺<íwâõSæÍ› hi 8’>²ÀfÀü‡@³KÐ2åÚkÔ>Ðä‰+üû`>£LçÈd~óÇÁÕýç.ÁÎ~4ݵ·Êc^6üôË OHj ì™Wšä¹ ¸2NÓ'jlK¯ͤÀËWªÝ±ºN­GO˜ñQ3A†W+(œ…Àù+‰âŸ/>(Í¢X×W0§¡Â‘š‹©zÀ`dªX¾M jW“ˆeâg°òÙRX!ãJ³ N%ôÁ±B ÁwU±`M„ÂFISä{©‘øÂ:Àä‰{-¬ˆ–Ú¬YÏ‘8xÿ»•ÚËò˜}d é¯Á$KÿË0hÒÅ«þ"‹Ñ”­LÖŽ@ÛT÷¾øÐ stµÂŒeà ªoCþo€ßû¾Lý¨UÓ‚Ò’Yk·¶œ+w<‘Ú´Mû*±Q{ãñÜü¡+Sð³H$Љ+ïíãS­b^Â)¸Ž;®ÄyøÒEBk;ìî¶æ±N!ãyŽ)iˆ:U*öÐ^z]NH`£ÉГ&r&&W·Þ`5„-i’ƒÛæ :­U¹b€GÐiO˜Œ¶BNÂ{ó—‹[B;[/Ï<'/zÀ¿å[÷ŠªX ,͆_ùò^ˆ,N[hÒi”cl¡xÞìiq®Ä6,q•æ ä TfFF|rZ†6ÉÖœYÍ¥=ä€ Ôz8öÒ^ß+Ù;¢ŽË–‘!-Æ/|ß±+Ät æ@x ¶ÎdÒlAç–õ¤­üÎClj¨¡Õb QJ†QŒBaâCfƒ~má;RÔçY}€›N²ÏÙ÷7ÓR/æ6AÒ„öØ‘ô±ló^ÁÀ ¯ZÇþÜgZÝu…¹ÛÁãgÅ_ßÿQ ƒuªç¬H3<±¶ÁÝZÃÇ&U¼5ïw˜ŸÔ–ý¢­ˆ‘¨&¿û½ÈD>4¤«ö²Õc ‘#aöòœmÉ$6‡_FZ®M?™yšðmRØ cxoŸr¿ +?Ó?[l6·yQÍl: &¶¦YÐàyÚÞÓ‘{TŸŽ`2½Ä´ÿ-ÆYÈÊâéûúH˜A ¸šN-¬ˆvÛBñCšýã…0k$Æ좚P¤}ôÁÉÏp#9é`9?¿1 ð%PÂX‘RÌÂ-aÖÆï›ÎÚ/=2(_-tž„©ÍÉèŸA }d j0HKÔdQ`ÃÊ’ù2ûwÇÀÔ¯£ ,„€ 9´oZGD`Š[·“î#CÉ9•[Œ}üí6  øwÅ‚Í@‹*ÌñëFˆê傊r«CËÒ¿®ÆÝÒ$h(€÷$¯èÁ­´ Ú”»g»(I:]üåÿ~\ôÑ燖³hDQj[$aÁæû?ÃÁ'ÕœWµ ‚iPs[Áx~FÐ òá\§‚ip¡‡ôLƒ¼ƒ§0  ƒi¨È}'ý|ËŠ—,¶ ÂàÏ«¶‹©OÞ#Çã»cލ Ɔ2Z#M]¹ÑZ!îéûÆß 44º_'i’Ë A4§e”RO˜Ÿ‘(lœF§¬ÜY@†Ò¸éÏŵiwÑv*ƒYñéÙÞ SR}“;ÍHtâÕ.¹qÕÍ·|³9 "PÈTÚ²êø…qÔ¡y? ¡mPÀA|nñÓiŠCaƒç¶ž—sÕ³ÿ£ÏAÇÆÄÝ[ÖÇ ¥’r÷l¸Çч;âàk "…«åi7WMxf]Ô¢iÍ¥ä=Š•à ~^j=yè8"pYj`ÈtJjBU}¡4îÚ°ñH‹ÐŽW¶D vvT2öZË2ÁâK2£‡ 4§´fJf9°¿´õJ!‹(È*øú÷­ ͧ8î0‚ڛ𱡉[cåó²Wû¼U+åùÍñåÙ±ý¥‹´æ/Cìæ´B{Ï<÷@Õ<¹ãTŽù•!`ž§ƒ~ ø†É˜u>rËÆ£¨R 7<8bsc‡rÒ€°!ãÐ=Àä¢ ÷ÍHz¶‚[X[¦ ¨e˜cš’RóþÆÓ£äÜDM(ƒ£¼:a˜ f@Æ‹N(sú7$Å¥içÑN0ÿ¤†…ßµñ–¦Å VGÈãÏ?Ø_ÄÄ]€p!Þ~v´m…f¦¯çë?6GJ¿¹TÐ5üÐ#ñ*¢Ôj¹k{B&£$-&}p\s7¨¶°]œIŠ8›¸§ùÚÀ n¡[Ðò@ Ú`<¯ùPØPÀ`´|ÈBbå£ÐÔrleä?ú×ýçûURÛK³ÖXæB µ]$åƒW &ò¥Oé¹24/Ú`2HÐðP3/Xó9i2BÇp~3È4oÕÌ[µÍÕûÜqÊ鸼œ´ãÞ’”d°Ïºr>þçƒÃdvª¶c÷8Ç×DfßRÐÐ>…„èh Ci«^[çÝGÕǾ>xì¬á\ÜÉ?P'mµê92 ¥š>…'ËzèN[~FB¢YÍh¬ÎY,)0 ënÞLμxúÔ˜«§Ð\É2ªWIñRÒû‹+pÙòé᪓æqe™š' $ÊI_µÕQc EKZQï/Ly®¢¿ —ÏÇÓF j^)Lî(CæMmngà8~Œ…IM§˜ãÅr5ÕÖ ð@DYc˜c~£ s †Ê·\9As0y"£ÅÈŠ2ù%òÌÐ<˜Öj…ùw$Ý ‡]æ‡!,GÒSú2Þ‡<4*D¼võW…]fY&Heä¿W1rá厙~qä×Uhæ¡ðï _ÓÏ«vˆzX%¦),£íA$½×.-øÞKÝ#h`‚ƒVVôÀ½§:<­]ùÚS˜Àê&ŽÖ)L0Xºü†è[©€®g/%Š`;QµA‚y¦Í{@›ŒÉÜk…1aTÏsÍÞ,sÌ"MäI3fuÎÎö6}1sÊ®<ŠøÃY›A†’ fÖæ¥‹Ü?éù‡00Tzýñ‘^îXAfƒtp (Ý£¯¦ì줰?—.ÅS•°¡6D§ÝAÿ nŽ~s4©âæn("}Ü\÷Û¯‹yñå'‘%†ۙ8gá‰øÖAåÎhf6˜Hñÿbfâœõ\O«—ô;i£1+óâú_.Dû(l(ÃÓš«Ú£&i ƒ)'quÝå{Ì )ƒQ0ÏËëÈÓ{+ÀŠc˜c® (˜ÒŸÂæ hà™à”šjÖhnÈÄ£„íðs¤°ÂÐÐ&Ge¾) ËÕ_ \Ü‹Œ‰“¦…/ÀgŒ´o Thf^GL†f¦°ÁņõfØh2x0DþpÏ? ZÚpu+hÂl Ô‚Ÿ­ësÞ^` ËF:*˜Fùr9ìñ_¡Ñ¦Ö»°`$¨ü@(® !9}><´hwjÚY´‡4ª¶<Í3fŠ99üŠy.ñ‡³¤mEÄœ 2¯]»t=fßîÑYæ»ea¼¦ÃmŒö1û:*"ü³ääÄxÕtl8Ã@Aƒ›N@ÂE  §7SS“nìßµíׯ\X„•~ÒÍm Ôj=†m "†Ñ‡è©ûzç[¾-_Ü⥶8j¼šxð'ló'))Ii¸¬)Ø÷ž<h'kÅ`Z¼ë~rU–@!à4ò‘1‡;¨¹Ú`†sgÈË0±ZŽ#35Œd˜à %«Ègæ„ rA!™¦ÀÚ=\hvÌk ´«¿¨É¥”Åò²¤}{Bm{ø~Òy|‚tÎMŒZÔ¶8«<µ,¿|ößÿÚ­ß`òÜJ%®ÀÎóIÐ’YÀ>=lå²­•*ƒã'Åba?²‡A×pØÁ^)¼Ä‘ŒäîÃ' q±Ñ vn„½©Ø(lÜÄFzÈ™sö:}!w ‘>8’>!#uçú5{°ÅµkT=ýžáƒÇƒ‘rÖBÉÒùž&OŽ^¾gˆÃûÂ7Gåö¿;<]ØP8½ÅÑ«3nÜ3óç0 (…YVe¹o†H{«·”§A±1ü–h®B¿2®Òd„ÀýEh3\J¸.OH_³Ü“–«¿ †ð—=ÄÒM»eðÚÉÛ[f|Ðr%̰bââ2º:üIòG·²U§Ï{-à=9ÿæÙ·í0†æk vâÉ`+°…2ßSmwT`šÈN¼¿‚§~ú›¬ž©ZN-5q½,ƒf|Œb9‰{-iZµ‡&³t8§ÌDÏj\Utá”&:SØà p)ƒ™Í”?ÍÿyȸGùÌÇÎ_½fB4¯ÒâÃ6ë`´ï¥é5'c-Z÷ÛšOQ«Á„~JØPš Ö¤Ó±p‡@1胟?¸ºMZ*;jÌ“‹þýÏ7&?;á9Ÿ¢;Ü!ø.¯™™™™ñîûŸ~?cæ¿#ÐþDl;Ø÷Za?u(*Föj îã"êè™Ë…÷Û §l-Ìqs0£ÌÉ“‰ðÍtÊ¥O#ý@vÀDŠ‘û0OÂ^ mpŽœ¶<.DXh:èVòC~…b+¢õlßDUY¨ý!¬:§ ÜzßN-BÏ" kN2ÍBÝ|BˆYD?õ<|:e|žFÙ l¡ ¸Ã›Lƒü)ƒ0^I÷iLë›H•&…Ö€áñ‹ß•çYÅ›9œ)l(fæ3RØÀ¾Üª?üÒ©Ï€óÆ.wOú÷÷«*BjBZÖ¼4äáð@Bq[“˜'áKUˆÎà°±NŽ ß<oØf2 ÔÇsS†2£RĬӇÛzÎ5v}P ¥vƒãÇ*FÌ(û¯½µuíÆ°+» x ¸V‹ øµÊéàáH…¹ £´ 䯱¬)=êåÿ{sÉÖðˆx4›ãSš«ñƒÚ-ޤ5~àУÁ£V´9·2ŒúOªÀÞ °µ0ÇtŠe.–ô›7Í6ì4­bÖgj=ŒƒÙýŽ>^°}›-ý'è¨^ C6 !Á­MÀèÈÈ¥gdɈA4!c EŒ<¤ƒ¤K‹Í½¾Eæ–ò (-LÃÖÜB_#%hXkþéóW¥ŸÓv„Ôe¸xα[Õæ”1Ö™ÂñÊFk™>ÏgצuaÑ»"bº1";;kÕ@¨ÏM|Ë™˜qÕ]‰Ø` Æ@zf¦‰Yá™°ƒº}˜|*6fãöÕËפ¦ÞàŠ$uèÜÈ0(aƒt`IÄ:})·8>”°ÁÉ‘+ÜJà(³jÕú#Fÿj‹SRöCò±ªó—aÐ/_Î\Ñ߀0¿&;d4´ÛeÌtÚðZíÄõò®ÃOFf–)ñFª }"Ǭ̌+ß¼7ësô û˜ãÇ Ž6h†IaCMˆ8Ô¡ Xf‡gyF¤â¦[+À,c-Ì1Ã=kaD¯v‚›h6òÌý}¥™~mŽ Ë0ͼÏò܈ž¨æYÚÕb[¡™n÷_óWŠÇFtG¨Ý2ŠÐæÈi:6²W{iö"+¼ÃþMš6SÚ£Í5}ƒæÕ9žÒ •ÛmpCó¾n;¤éÔE˜އ‰ 'Zñ0¯W×ÚîX»‚N-Š?S{‡âÍÙ™ªg¸*ʼnD¸HII6®ýõçE8·¢YÛÐ&u7måë\Ö·|·n¼x, rFFZZRÚ §ÆFÇFEž@c•öŠ  ˆ|Zs*J·¼ÖüöÇ•ÍÚwjZ«A£æ~~þÁçýý*Aþ5MyñÅŸ’“’¨û–«xUª×ì7jìooïÛ ,nàš„¬ÌÌ´ÜòdjõòÄŠãñSøKücµ;ãfFúõÔÉ—N;zðhÔ¾£x"… Ü8nPà éÇö#Ç sŸáXbÀÖ °-ÿˆÂlÑø­â„« •{YþÊ…ø”…s?ü¿yŽurSt¬~«û¼ôòNÃÐnž8þsÜà¦6Ô1û›c‹®Õt°j1ŸÕ þ#ÇÅh4Ê—- 3¬jbP·V¶oº®¬ÒPc(÷ºfãN ïH!VEÖæMΉæÍž6Q—dï aƒ&³ @MòJáDÃ0töT«–|y~œüuð, ¨þcŸ²©µ S µ:É~%£ ´fâÅ9-èô¡ÅFé?v4}#ŠF”¦‚ç´ˆ/~Ós㙸W“*Çí€ÊúT¬‡B‡5ëåspì(ü¨¾PôÁqA-RpìPÇ%d¨±ƒýÃûtÐ1`LøéiI?m6Ö}O¥ÆI¯ä¤kǑŽ…ûš¤?ÙÈÕl©ñYчÛä aƒV»öX œdÈ,м†V»¡^\íqY7a@MöŠYPÌž–iPŒ÷<Ï>f9u/­‚NVÑRªNª>v}ŠF(p¨gÆ(Ôª±ƒ¦yÍ=¦ÐÀqŽ{u]ÝOÚ$*f–÷²Œ^>'ÎÀ½78V°•€ÁþUc‡Vó„Ó:èÐ1à@ H¾ê÷oç-Xü¿é#X¯^U©Â€  I§ ®6Øh6^1žœDx̉…“ KAC1¸¤ƒa@1 ªÉpc_jö¯bâpX èôQ ŠJEgÒé‰ÂzéŽã‡å…8”ðÀT ¦8”÷+zStÊóê>½ü­ñÚ‘ø!~ ªÙÔn¨qƒ¿•ðW”±·ÝžÀ¬Øp¦—qúµo8ï·b`×VNMúÈ(Sú^TFîŒÖkÛŒø¶rÛá‹P¸}U\ Lä÷Ò{?Š<92O¢?m™¢o‚µuo/qw»¢…Å-Ê3nó²÷Ük7y²4ÿc0D¶ P Cá1À ?(M~Û©<·+… ¾½šà¹×N8d´«‰ê¥ÕäÄ{u𠨊ýÇ̈Ò`h^+*èôQTŒy^yWч¢;2ªJP±¢Ž¹×^ç5éSKoò$þ©AWAê¼^>ÅÅêÖ¢ðÎ>T†jžsžv‡ÿg¶äSç¯ä6:4¯‡0´NÃø6ñç–}¢+’1ùß6d8xR¼0n€ÕgÒ?¢ „kÀlÏŽF”û­&ü…6¯/:¢^móo#{¶L„xƒvŒVß]©ݯßÊ0üâ•G‡ˆ&Ènïl@DQ/¤ŠSèÐÙm·R¿ŸÕÞ\dÒŒY³³½M_Ìœ²Ë|²®6T9úpã‹‘ØÕÄ®&²|/Œ2:x´ƒ•bTŸòZIAÕ¥ÓGI1éžû]A|3Ò \5^p¯@{ÎòXµO»·wŸ¶œ:ÖËß»‰vo ?<¯Ê±ÿ³£Î©ûô½ PãAa£‚¿¯Ì á# ‰ð¸ªËð¶L¶wàØYqâì%™_ƒU­ßyá¡ËˆöMëÊ{úunDcUd²¿3Äý:å{bŸŽÍd2²#HÚúŸïW!tt¦¸™™%V„E‰N­ˆ5ÛÈ\§!ÑI»NH° y»mÿ1<ÿ ÂÐÖ§ª¹Þˆƒ'q‰ä„9Ú+‰Éb+îCþcÍÑàÁA]¤“øä7 ®(œñ½±j#bIT ôG¦qh’SEÇõ°Õ‡cy¬Ø{IÚ2EêÁÂZø`󋔞ƒMUß™úöøýq+ÕÀæûœµªU=Ÿpº°AÝÜ_7ˆ×'ŒÕË•ZÜ1´ô®´´³x[©ÅÛ3ÅC‰X_i°(oë§»„ Õ~ö³Óßi‡\ CL&c]£AÔÀ[WPô½ça–ìeñ ÏÓHœ´êóÙÓv;©•:}8 ±Î¬ÖÅôazzú; ÞÆìy3¦î{fÚ̧ñnißÏ„ˆ²2|ñÞÌ/s¯q²zypþ®Ç;DncÇ.ôjÙr¬˜1ãV(âÜþÑwv0F^9Có8öÔyä»h/¨ ™ÿg˜˜1ñ>q ñýù[cêú—ÝÚ4’+÷,÷ì˜~âÇ•;ÄsôWÅòì÷!øñ³—! œ–B Šë)i’1?qî²è ƒBϦ݇E‘ÈÃqb$’éqOóÂÙ‹×įëvŠ)€àpR„¹âž^òü‡?¯ã8þÊ5ñÉ/ëÄ[“FË{´ÿ˜©|0¢Kedf˨SJØøuÝ.Q³j%1ad™}œ¹C‚ü$3È,â½!0-\³a`MRø‰‹¿"–nŠ£ä°z• ñÕÒÍ2`ꕵ0ZÖµ«k]jçÍšþª•ÆsìS[V—¾[N(È•VØwä4„×22!$³Ü?4¤«`ò?éUÛ¢Dô‰x)àúà|m“Ì:~áJ’øaÅ6qæâU™Ýû¡!ÝDê¤ÙiŸþ&Æ è,VnÛ/®]O¡Íê‰{ûvÞHùñ‚uH ™)þûÓjáƒß³')•¨{dXw1~̃ÿEãÕ˜œÿ= &‡È ©$ë >3cÆ7åÏež›Œ•†W²ŒY!/ƒ)ÐÏßX1ÐIýr¨¤àjônÀ’¶¯§š’SÓ½0€Ïœ8}öyØJþ·VÙZϘñ8MâJ :}”…n«ÀôñÜŒO2³Ÿ,ã-&!¡W³ÚÕª$SϤmÝ{¤V¶¬Žk`ŒzÏñÌgZÄp5˜+aÖ@//äj¹£ñƒ…Ú.c숞ýå²ø+s*>3}ö[¢V•ùó&N¤YÕí9¶‹Þ¤k›Æ’‘®[½Šì³©ö‡á!Ý[‹¨£gÄ;ßü AcÑÔ†ÙÉ…«×åê2Œ0-R‚™r®ü3ƒ¸%DŸ8'z‡6…©S=˜™‹=8$|Ë‹ÊAÈQ)‡ñƒ&f_ì) …Éϼ ^âòµd)€ÔªVIÞǧ/\ç¯$¢ •¡9É‚fæ"Ê]—øË×dâ?j+¡éaöó›H»´³yMT¬à+öDŸ”8b} kU};å`Z%Žž¹(Ú5©ÃKÐjTÁ ù­gË“·Ï?E—ÜK£qÛcV„í/ÕÂǨ¶ð)jÕ°–¤jõÚ5ÍéÏß7í‘Àw^+~Y!5]Oî-{”‰^ý|ËŠ—,¶ÀèçUÛÅÔ'ï‘×®&Ý€ .†ÞÝV”ƒîçU;D="ÔÖQ = mß}}CEpi§Kí²¢‡¾—ÕIÙ¡O°RÙÓÿxkð…ìø¯ LÕÄꌉB[7ªi@`ëFŸVêÐO¹RLMÏ€ªüí$«# ü{èÓ¿>ùúŒ'¿zwÆ´ŽY±@§b¡Í“nr.}@“a4&.Ä’|½º!ÕLÝÚ6âP™#döMäVXdèåícÊøÁ¢!l]DM˜×Ô|tx7¬vï6;sñ ¯³WÞ|úõñîŒõ¸î” ÏþÛ:ö*˜âtÞ|›& ŽzbÌ!esc•Ÿ„‰¨Ù д)L?µö|?†to%ͨ(¼üýƒÒä¨bFª¢.V‡­}+hÞE ÈÙó¸mã:bÃÎh1cîhAÊ‹Ñý;ò4’ð¥bµ8El×ýý; _˜{iZ ®HðÓ*yÉ!¥vƒÚ“{zwó—më#‰j•¥¹ÛÀ¬âмËòÍë×B[rÞ'ªçâ‹Çe}|„ÑA~%¬~3Ó”mÌfxO’…™4ŒÙÙéi®¡Sg #9%]>yN<;¶?Xohüj‰‡Ž›…ØSÅÃC»JߊÆð-¢–ŽÚ‰”´ AíÜ$h÷h¾×¿~\-®Â,1¡]Óº0óËJÃá·t‚4ý˜jÃ\‹@Ÿ jÇJ/˜¸¸CaC+p8åu\-l`¶ðW8¿ÿ+¤0PJ@IDATj´«D w§ÄNÁœ^)ìˉ.P£c3PÊ_°:¢Ú¹K‰>õ·^ûrΛE8̃Z!P¦ÓG!TZŠ8>Ä3ÓgM6˜²ÿ[1Ðßë‰{{a…ò.}ü(-a¥õjT?Ôpød<„Ž]!ç/'®Ä‚ÅD,XÌGñ¢ŽVžà–SrÌ»™žq-1)…Ç%¢Ñl˜¹ÑW‚@fÜzVïü¨$µôqHcu‚EMhÔ6ÐãùŠ/o-±"|uKÈBtÞ¿f*dÜ«U‚“}åSsä» CÇ•å=‡O™«$ó Z‹©OŒAXT†qúL|÷ç9ѱy]y> Ñ·h¥€‚ÓNøkð{'óG€SP#AaƒfQ½:4#z¶“~#@‘€…ÇiJÓ¯SN„,­¶‡e,ïç gøë) ŽY2ÍFâõSæÍ› ¨¨(óŸe“œñ›o®6Öo¸™‘q%ÁtêŒÆ¦Núï09¬MÐäé:„cš9•/WÂGˆØ3?~;ôË¡PAˆ…Ï5uó—…™Ã9ëì¥D³°Q„‚ 3à¯t;AzzÆu¼_J Ns])lžžúöËèÛwl^Ï4~d¥Pn @h¯Máýݲ0>þ÷Ÿœ2ÃðÕ;3(pˆ 3àêôq{‚Õ·p}°^CõÊAÏ@•íýàÀÎN$:Üh^¿†xõ/ü¿X²Ù-éWO¼ö†ï×ÿ|{.ÞÎi“Ÿ0ÇqNu¦ôÔ”ËÉij€ÉÄ(ˆ½ô¯åíd¢ß}áBUÕ&Nõ!̽þÑB¬ÜúŠ–dæ\¹]<6ìn˜Ô½áPÎH>À¿t ÖVþþw+%“FÆýá¡Ý¤¦âò5û™û…0Wyåß?KÓ%8p5™Ú‰iÿ[ŒG˜ øTOß×Gšbuø/<‹Â µ³žƒçæ´äÀѳij­Õ27-‚É‚Õ;à«rAjI–nÜ#6Àñ›>%}B›I§ø1px§@òÚG¿Èzé£Açy[Àçõ…Çç¿m!U*Š×n«¨ÝóìoöûÍ´Ô‹¹µta÷^G_œ4mf?Ö9wÖô u»TûÒRSn ½ié7¡Q¢Ò±tM¨è€cÄj.ˆŒ‰ÝÛ6Cº·ŸÁ™›.ºu£Ú²\ùr9ìï_$È“¹ÿ”¹ uÁ^[²ôó—oÝ—R5$¤Ìåóç9Ϊ±Va}9W ¬V m¿GAã©Ñ½s‡‡¾‹^™›1@áQöíbAãŸã_™óÝÞ¡Þ» C§7÷+_ú`ó¼2®Ÿ¼§L™2-1Hêã‡+:ÌÅÏ £øÜØþäw0]¸œÐäëœü)jÕÍ) _Qµ{9qŸ>»¿fý†ÒÔ”àâWî¹Y‚VàÐS™;u‚¹øK`¤è¿`¹°÷ŸWnÅPPæLæ›p@Æ[[ö#EY^{ZÔž¼Œð£Ôd”‰’‚O~Y+um-ÍR²ÍçK8fï…¿ýGÔ{Zcx©!i×ôaUÜSxûôõñÒ Œ~Wï¿óÓ¨Éëˆ÷Ê1htDM¶ë0<öØßüÀ$|U«ZEA†í¢ú•ÛìcL8&_?ÿy}úŒ¢ñ#¿n[ý®ÓÇíÐéEx‡¢Òªö:z ¼>Æï0‰»bÌ*ÂÛèE‰†&½§w{Ã3cú?ñǯß6DÝäVKKŸK!íå>ûà®íÇ`ŸŸ¶ü8 ¸ , W=W+hð™•àT{^šD­ÂÊ3£\ukÃ.¾E]Y„¹ͼvÁù›¦ULDÈ(U¤#ålP ꜽ=ñU”ò–u±¿MFãµÝ[ÖÇàš–&,‹:ÿ·ã%7;µ3ü¸1;ójÔ‘Ón¥S;M´y‰ 3)è®:pÇÄÅK-…ßȘSâýïVˆ·çý.}à#&M¬&ÞßGÄ_NSyêÅ÷¾—9,ø B@ £œý ¾L4Ù*m°y÷aÐiæ¹mk—G£í|kÒj>˜7{ÚDnù.ñÄ­e‡"ÞX„â^ej¼»ËŒ}í®¯íÕ‹–ìãqƒ»xÿûûU!uC[N›–¾*IÌÔpX‚N–¹Í‘>È5øÔ®ò. =$ömNš× èÓ§Û,ü“ ÍÉPs;Ù>ÕFjcÈ}d>vd—·÷Xú´Ñ”ðN†¥)Ë¹Ë ‚¶ðoMlŽH@Å7/#.+#ÕßaþÅz1>ˆÑˆUapÝìg˜ÎÅüm4€½¢_Ò‡'€¢S¶‹srvÌÞÈEÝZ×§ÝXŽ‘'´²mÞ£­àf 8¹Ñ׈æusš–Nßô}¢™\ø¾£¢Qíjò›|ûÙÑR`eôÙÉC>íexèwÉÍÅB?ªmIîÍ¿;Žü;pŽ7œ;·%p#8FíJ¼9Ï.ÑÖï]¦¬ÏK°·4Ýéƒm‰0YÊnf_³ÏËùúNFÓi\ϯђÞtú(eýê¨æ’> ¡¡¡Þ¯ÿó_¯†[¶Žj„^Gc Àßÿž¿ùTi7n-U{n«9aSÐPÂÆÍðËÖ³²6ÓHs¦;ȵjTS&l ÿ‹’  —ÕáO_ n\Åv—5 û—ýlÊξöçÒ¥hŸ6HŠ™SÍvç^ ŠN3ÃWÿ¹mþÿæ~‘™™™áΆ9ãÙŒ4ƈgŒ2Eÿ'`ò?-Pȸ%hh¯Ø?¦OGi4¨ üö­F“1;aǺ*r¨¢ û/\‚«–Ì_ ª²z«á‘ÉG˜JSu„·- “…Õ—ÐOìs¬6Uõ—‰Qµh–4 ÓGñP{[ÜUúðjÙ{p«×Óß>qǪÛ§·ÓKÐd®bHÍWºôJ΀‹–ã‡'½®š¬)QÉLÇ–‘šš|#2lÓ÷4Õ`ð Oj°ÞÇb€ýË~ŽŠÿ,99ñj' H6<¡ÿµtzmbÓ¸ßqá÷>ù¾ž$¡iҶÈ2~BË·î-X‹¨T1R¡ÂÙ¿æÒ{çwËÂMÈecÚ¹iíû×®\IÆ›(¡˜´á4u¦'ï²¾åxMÌ£Qz»Goyq0€>§Ó”É?(pîßM­ð uú("RUr,&˲„‹H¾E3‚Òó»0ôáåãÓ¤bBLÿ;züXޤ[L:Æ•Û; ~;øhÓíODlZùO¼7ǧM†À+ÛÆ6*&Ž1TÓö†oÞ[é®»~Çñ}Á3ôHŒÀÄmÔhäFa4ÄÅF/عq-ÃÙ÷däI n¤[4ùÌOµtJAƒ‚“”ykövׯ_»òc¹¿o‹f禃@Xç4#‚2xŒ9ôSÔöðƒÀ‰–F)ç_Ÿ>§§Qx]ÿ|æëûK‚Cg ÒD«‡øÄFD(]Fm%Áª~¯Ä#†°ïÓRRêà!9ð’ ¹y}Ð1,19E¼ùÌ}ˆ3Ÿã@¦ýÙ9ó‘Ut9œÚk²UåðÉó§WËêuGždt ‚5acÙ–½I¤ÄÄûû9ò‘N­«0ô £Yå@¿l˜\8s¬*ò{ž>UÌùz™¼Î¤U+U@hшõß¾Pá#‹B7¤Çe›÷"ùZ…;NØð+_Öt3-­ÍþWL[¾ Qv„ûÿqE˜c›6˜ÔYíÊmX²h¦q Ì÷œ¿zÍ4npW/ݬØ(å@ šNQ£nѺßÐ|ŠÌ;û^1r¤ ·h `R».CM<_ Z:UÂçg~cÞžzyíθ¤ŽU‚ƒkÜ? “Wa¢3i+×=4‘ûèãyß-þòûƒW/^ŒG+ÒtŽÚ ÒE¾±5Û”ýƒI×âÚSØŠ ΜÀ9°ú€¥¬p|·…„\l,ßÁ7²ï/'$„j0#Q<Š>®Ý!&#ÁUQáÐñx±tÓ—öÚöø½=­ öîðŒkЇ7&Ë*ÈBì±ãÇS÷õ–‚ƹK×Ä2$•:t"^LêÞ£Ù‡n ùçÏèD'¶¢r¿!)é:™%Ž;ȸå›q΀íbûØN2šd8é-Ǿ K­¾zñüÅŽ½úCðŒ ú´Ñ”> Þ:” 0ÃÛ2êÁY™É‘á›çï Û7 Y 7%l¸•f?Ÿ9íM+XÕÒ)… .sã8ËyÙ¾nÝâО}þdÁÚ*-ÔÖ74CÞ–JpÈסt`àú v­`¢LãÞ¨ƒûÿúÊ›K`*w§HŸIØ®cStÊ…œ|cëóSgÕ½i2ÕAŠZ¦”œ%l`I¸Xóó (_¦ ëpb€}Ø{ThÄICLjÅXÜ޵*40^ó“_Ö!;jM† ¿¬ŽYTÞ8ΈÍ{bD›ÆµÄší⯬èƒdU*¡Õ™ bÕ¶( ƘnŒA_»z°PmTÏpÅÞ}p¼àæmÿ²>>¤Æ[¯Ü1ºKÍDoÙ‹ä`͵ aQqæbh 1ß¡š[¥&L³U^½ø¥„d1û«?ÄÐDh³zâ¡!]¥PC- ìpÅá¸ó2ägÛÆµ•§£ íH­Ý¢µ»üì<âÁû‰H†ÆL˶hZ=ËSö¾eË‚ AhvüpË*q!qBFŽ7… ίj“4¼{Xä‘}{v<¬OvvV/0«45­à[ÎT‚•>Wc é™™&f„gÂ>h è¿äS±1·¯^¾&5õF"šMÎGšð4à7D:媶šÕ¸kØ¿#lOôž]GºÖï9 úÄ9™^ä³ÿ>aˆ)À××Ì;’©ý~y¸Ìgbù’ eüžV^^ajO'à'%ý¦éòdff|¼¼2m\öΖ°ðTšöQ›A!CK§¤ÒB¾qÒGœÇ 法û’€™`JR‰{IÀ>&¡'ಟ;æ4¬@8ˆå™tñÛc背6VÁ”EˆÈBªÑÎNÚyT®dMÙK$¥¤Šïÿ í›ÖÁAþŸw—&Œ¼[&b6Ó=‡OJa#.þªHIã*Ø$ÖÊ’ÂW¾{vh“'£ødÁ:¬h–A@ ãÿå’Íâ.$̪ ÛÊt¬”ïA¾bx϶‚WQütá:Q‚‚ ûÇòš-‹ÜÌÌD}çD*2Â2Ü$Ÿÿ 2úvÆ;2ëöïÐÄTð÷ï¼0VžgØÈ§G÷VÕ»|oƒ>ÔÄçòö÷ ¹Y™‡O!Ú !8n¡a Åþ£g$ÓßÉݬѽò*J …&+cè•aQ¢„ÛÍêBXô–4Ó+´)„ˆl1¡)ø0¡…ÍˈÂò¸â"„Û2>9“¾-šfÂ6O0v+´šQÒ†'2pD?B¥ÝàÒ¢bäÔµ¬´´”Lh9VàÄÚ&­ÛÕ«Û¤Y3_ÿ •Ëùùúxûè*bÊ!+;+##--)íÆ$lŒŽŠàíí—v!Dgef¦MyñÅŸ““’ˆ' zù\D8?>>eÁÀ[ÀbêfJrbbüéc1QGöEžÂ• AÚTµ¤SeÞžoL5LOãúÙOÞ~-úÓ™¯ã°øà,aC1 ^ GëpGc@ œ¤5N¼¤‡[{ ¬l£¸+ÁoÌýM¬Û ­EË<½uÌ:µÕ‚+ÈNØL4¸[k©VŽ¿’hŽ¡ß ÂÊÊð(É>yN /L^uäôE3{Ô´nuqìÌ%¬P'ËL»üMƾØÓb[Ô1É8²éYbÖsÃò¬ 1³í·„Ék/Žd×\çÞ>í¥ÏIdÒÝŽzc!T´mRû‹âá¡]¥àѸN5ù\oèIÝùèCÌîQ`œ»º¯mE{r _y)(ò.fsîß¹¹ŒíÞ¦Qméwsò‚Ô.ÐAK7öÊ·oVG6"´y}dÁm"?'Ž@“Aaƒ0 KK©I#Mщœ×)lPpNHº YŠèܪ¡,ËöhÚ\ÈC,„ 5vä›=¤¹l™Kµj¬hYãÄΕD_låŽØ-Ç|/õª{pJÀ€¢5ö!7%L²ɬqµ˜ WŒ¹'cgsµ×<ø^Jà ÍiéŽ×Hä×òqG¢S°Ï-Ãk '†+â“ÎýðCœSß&ëQeÎÔo½|ž> ^âžõ*ß1%hN•ŸÏQdö3ËçIoÌœmìíåå5‘Ú×<‹ñÃY›"Oª‹Ñ.ý–Û¹4 &S5¡z}1Ò½X¶OtÅê³j ŽÀ &S»£ãÔi¬s^É kUÅê±8~沈ãx'Ô…UÄ"¾7‰ëcřڅDÄý&ÔÖDª]­’d U­ÔšhÕÏd¿²•_r®«}KACi5J̤¡®ÃÄi³_Ã8;اL¥{þ7ãy2œ ­’é$ð·ö߇‚1U¢œ³µ‚~Jày~›Üx][†øâ¦efõòyñì(ühû‡4Ê>eÿ)Úä^+d(eßä£S¤¬˜†®<âÝýk\/1ps¨Síñ ½ÎÒ…õ!°Õ<&x}Ѓ™EoØ-6+þ4SéÚ,7[(ÏÚ–¥pòœÌÐ)Ñ]úEl„Oˆ/„ŒæÐ|½ˆpŒ(5„‹°É×Fþ°–,ÈޤÀ¾ÿË%›Dkh[he èa (T}öë™uw Þ¹5VÝ=´ôáQtQXÜP ¥_ÅP0ø0 “þÍë‡È ÇÉ0s{õÃ_lVU˜ò—A+ x\£jEùs}D´8sá*´`c¤ ûþw+U1™8fzý:5—vÕ4Õ›õüýE¢isen8xdXw1~̃àѤE#¥>ÃF¬©c%lp—š ìÉÄqS‚”z7µÇ%܈Å„qO¦ŒŒ!™4nJ³Ábµñ<û™åÔ½8t3ÐÁ7[ÌÌʺ¶hÆŒ#gÌèË6*PôɶÔo¾i•+JØÀ¡Äƒz7Ò)¿KK™×¾ø,ÅÐêåo cŽÆ#‰{âœÂé’}H¡ƒÇJÖö‰êK\Ö‚išº×‚V´Štl)R6 “¨n«s=Ll¸ZÌ4÷Ž„Œ›™r•’ÑnK¸e*¥¥ í±G¼6¿éãðùo°ê«Iôψ8t¢ÌŽ.\I’YCißN_Š-‘1XMN‘BW”›ÁÜ„äA\ef2ý?®ØÆŽámdÅÔbøI›z:ó<~V†à¥a¸¢Íзg.´–Îé ál`ï–|×è ß÷õlßšoÙ®|…\yÂ:}¸²Å~üéì´+àGQ¯FUѵu#A Æuø÷x}ÍŽyžaI7,í•çÍûžíÑ÷É)i/.‹n0“"öAKPwK_j8Ú4®#¯Q[R¥b€LšBãrèÄYyÞMËžõOš¯Ò³h£5œÄÓ© -`)h(aÊFuúi7b@1Ϫ•ÀÁþä¦2XÆçž7øüí©ëŸ™:ëih7¾Ï ÿṟ>eCáޓtË÷"“ª†ù,£ÞO}›J³A&¨2¬‡qBÐËçàÁøQãë&¾I“ìC ŠN)d¨1Iõ!Nå‡y³¦šÿlñÏ8SØÈmUé1£Z²aœ{ãäÊŸ5”®8(´’ \ý¤“¯JÐE§á¯–nüía¹¢^”üÖÚé¡çl0 žE´o ¡/æJ Æ ²£m|ðÓiOó¥IcúIA‚>aІLùx¢QÕ‚# -©èO*(\…í|ƒšwÉjijŲßü±U¼ñÙoRx¡³73žF"Õ!8½t‹øÛø¡…¹Å\†t·t㵊fbð3`$ª’ÒµùÅ;ÐÒ‡š¸W“Wñjuò]Ĺ²>’¨=ܽ•TÙ¿ƒàÓ³ÚBz‡6‘NÛª9ÖèÆ^yÞ×¹eCñÑÏk௓)i”uz#Òýx^ú×2*Ö€Î-Å¥k428@ÄÁã"+Ë(>ÞÁ–`¦eÏú§&Eµ÷¬ÖÙo Û¬&w2œìÉp•Q»¬íw€":xØÜØ‡ÜØ§Šqã^çÞ#aÞìió'Nl4‰÷²2¯u™4mæãsgMߤi,Û®}OÒ)UK­Ë¸Wc4éWÑ0¯T}ŠþsÎæ”ÓË;?ìK6â^Ñ(éSѪêÕ.Û»@ØpÙ»”š-ߺWTE(L%lÐɳÕÿå|‰âÄà/5/ïa “Kxù‘ÁyN‘vlg)ؿ߄=|y³æƒQ^|¸ô£P> …:wê„Gô"š¹YØPÍU{9‰ ÷Xw ~= 1LáÔV»(<†™}v诣ktc¯¼zÖp„­eŸÑïGû¦QB´çy!u¹Y^³GÓª^Ús‚T“¤b†<¨y…jŠzÒ5™Å˜i™.^ÓÁs1 hOõ%k=·å¹-û|ö´ÿ<3}N8B#~›m2lÀq·y3ÿ¡i8߉ôÉ÷➇¢UEŸ ¸dÖTðšvSe´{–'hË©cm9uœSZ/O)œh÷¶ð£-£ú’çØ§yà™wß òJɾ/ÛhzÓ”aÞ¬iópà]ذƒLFïùiåÿ³w€Q_ö’F½w^Qi*6»|6Š ìŠ=% vÿVŠEÄ.‚4A1ôzï$tBê•ý~¿I&lŽK#wÉ]²O—ÝÛòæeö½ym9ò(¤‰+a²€èrg[½Â?ܵdΚØÐ–™Ž¥y ¹X$úÚ‚ÝçÆ°ÉgøRšXÌŽk¶í—Ž¿+bö ,æ ƒíYMúÚ#ƒFõ”|ߘ»á$v)kU­ †ôí*›ŽC¶RæX`"7cjJä™ #™4wÀy¿Tpe/µžü¾ç„6ã,Ô¡R—9 hDúô!P Kõº ãߌ¯ ùÅMórWºÉ«<Òœè%§ûl?§g¹Ñtný.ªg`.Ö¹wÿ¦kÏE›üXò£éÏÀþ“pX­ÖÀ£©–vØ“ì„?ÅN˜ZæÜ¨qÑà˜ÍÑáü~êë¯|†güÛðð˜W‡ÁtîNõ;ël–÷&~ýñ¬®³Pï.¬Ö/;sÄÞY¾lÀ6cŸµ¾YÃnKÿLhúE¶¹ˆõdú¡ÕJµ©ûð—&ÔDÙi».ËS°|€3ièÔ7ÞPÎfºYþ‚%‡·ð™AÎ 4X aÁÎû0_ ÿÂ0¿µ0‡uõD[.FXVvaJ?—^úÇ6rAì·s–K§Þ›®ë(ÖA@ }¶‚0k_ÏŽ–NÃ"˜œm)’¯ èÞF†¾d”†© çLfž†é±ÛûÀ–º®ŒD§Ünm‹šˆË¿‘Šhbƒ?T·1øuÁÊ-HÔÕYî®Ûq@0²PÃLÇbÕ'ólb / ÐLë"iÍù/4—Ÿ‚pIÃ*¤j^ïásµãk¡ó´ ¥¿-Y/Zvè4 ÂÆ`À¯v‘s˜1íº'ž(S»|Õÿ;æ +œõX®lxˆ£Q­ª´V¤û‹ùYz~:îðdãƒh|o˜×ǘå…ð4~&¿|ø|¢Obsp²ú-÷éoaÈasN«WõSGŒ ÀoÀj} ýª  åjœµ"– B.t¡!Ä6A1º¼3­W¬X9ÛYþBYoãÇü‡"Y(ç †Âj9¦í0²áý=ŹÖMÞ2…ÐI§Ë“ÈÂø$Ljè¸Í¨2Ì­ €Ù¦+#V¾ŠsߺI±Ï)lÂ5‘T§ãþ÷Öã/Ãt†f-•‘®i½‹7±Èü¹ÆàG]ñÇ¿ëÅ6%m2Û¹1÷‹77‚šùb ·ÖÈÒÚ—{®voÕÙ—ûjöÍ‹@øEš¡Q=ddZrú€z±'…®š´l¹ïñgë…U¬´ ÚÅš jVѯŸO#kÀ·$w˜›æ{`3‹G~Á,Ÿ;¦ò¾ßePCmÐcmeßvÍëê‹VmÕ,GN ð¥WG|1ñ•ñœtê·ð‰õùcèüÀüÀ,Ÿ;¦ü?¹®`OMa#|í;zR>©»z…‡ˆŠáJù›Úˆ„ó)âõ/æðgX£zˆe¯ lX°t§Ù í· ÌqYÃÚR»ÂXûGaž¥™‚Öe–71`bÀÄ€Ÿb€B†? JK4úþ®ÝºÿD•έÒ4°à?¸’ØmYи 9sþªÆ0u^üðK¯>þÙÄW¨}2 Æ%qèæ˜L ¦°‘ÊT­SñçE=ØãÓ9ø„ !Áe¤‚Ž™î€j< ÝÚ53ç­µ!ü0œªÊÍàÉ6̺L øC©Î>Ò-³ň2æÆÃ_hƒ‚µA§Žnz¢råÊ;´lâÖTªqk6}‰`ÂWFÔ´|þëR}ÃÎC>ø¢ÕöÅ$먎¦ðþB£—8zó5ùÇ€¹è倫ZU+‰rá!jRq&U‹cƒáj0ì䩸DÁÜi鈩AdÏáãêq®gÆØ§ã##Ä0²Œ+ðùiÔMS®$d‹&t€­=É™¿¡ vÅLð 0BU~€š-»=Ã+?åKy~¨Íu)'Ÿ aœ”v€×þì«4vo¾•‚Öó›ë/³—Ï~Ò,úáA=µ¶Íêxøô¾Ñc:áUγ?Ñj>Gk31pi0¾\ðvs¯Ž»bìG?ŠCˆeô± £7Ÿÿñïßþ…f &ÐÊôD6êÃÇΊ'ßþNæõp}‡‚L­ª•eî†/~ÿO>f”f‹¦ÒµMc×WÌß^ÂÀ¡¸Ób䄯Ä!dh6mɘ¿JÎáSoÏ‘ÿ,þZ¾ÙXä¢kÎåãoL—™»/zèáÌÕB¿"?%h8ÓÒRcÔªR$ÃPsýì{ß#2dv9çWd•'LÿsY‘ôÅWÙ0ÄÜ$)Z\È´+Θ8)htêÔ'lì›oÞÛ¨aýɦ Q´TS”­Ñrø ^Úíý®Hïß¹ͨhÚàtZ”h2Û*Å𬭟#òÖÞdd5Œ훉îm›B£á‰»Ô}u¾îʶ‚GbRª Üá sð÷x(hß¼žLÞ–’š&Cåò¾1/‚»ü,Ã]qF¾ª†&/,ã@_æß¨]µ¢LìÇ Ô:¹ËèMíWÌ®ƒ2·#È4«qpOލäjáÛ9ヷޘ>éðÞÄMnuѧ 1ÇÅ΃q29£*»vÛ¬¿Uu¯´)ÀNþi±xñþ¢Fp…"~õˆòbmr ¥f JØ(’¶ ш4ð~Pëž]?‘•̵~&”\ ðûÍå-B’[ÕƒQÅÁ5Lmœ”Ü›#31 ˜ÂFH"S™—ÿÍ­ ôw±ïZ‡zÎ×]‡Ž‹›÷ׄs®ï˜¿‹ý‘!ZA«FµeVîœv}7"™^äi¹‰ÞRù®Wd%z;|쌘·|“؆Lòe‚!´ZD]$¤ÐzìT‚øvîräÃ8-…Ì»t“þ:1»‹×í@(å:b²TÓ‡¨2I3kùÞ#'.ÊÕÂÄn~J­À3?ØE4uk­åúí²„ýGO‰˜;6ª}AÃBÆû›ÙËÄvìôsà;ƒ›ù,r›„ϳ®–9Nªcã ;ü±z]~™ãò˜Ý2dç‘gDY™ª½– Á…‘M-è-×t”‰&çAÛÆ}‘\šQÍAg-X ­íAY¶wç–¢žsìÔÐ îÓZºiÆÙ©Eq3êâÎí‡3I­Æ{3æ‹@üž€ÄÞÂ:øŽ÷Ñxãáí¦/µþ,A„„„‡<ظN ‚ï›P 0vóO3¦5|÷°Ý.×/Óv¶Ì»9ÄÜ1À"|d*7‚1:°G6S.ïv©èÞ~D-cN•ó–Kí”Ê ï:xj3ȶ†C¡ÝaÏfâôû?ë¤ñúCdrH¾;lPOYÅô9ˤÖlôÝý¡A©$¾Ÿ·BÞOGž•mH¹ró>ÉÖGFë’ÂNÂ*7©[ š—pqÿM=Ãkú!ÛŽ`ÌÍ'É|Ömß':6¯ŸÍƒ„»˜Ì">â¶^âþ{ˆ5[÷#4öY>·¹aòÈ:ßLüsC¯í åüqáÁïdøg}‡|=ý–xü£·É-QjA)h¨¼>=:4#‡ô–é˜×‡ ÚãÁ$¡ ÉýÅoKÅr$ Á¡Âi³ K×ïýE¿¯7^ÝAüò÷Zæ›Ï™ï绿–‰®­‹›{vÿ¡ 36A‰pë5$]ÉEó2ЦµÂµÂo*UÜe®¿ç+N­„ISÐ(NýêmšËu¿¢ãíè47sýEçW86;ë0… ?˜³û^)žcb4Åòƒn—Š.’9œ#1) óÓÈí˜if·}ÿQѾE}(êˆÕ[÷f•Ýyð¸4«â®xÓzÕÁx¦KæŽÁ‚¹Gûæ¢v§¯hÛ¾>g¤ßŽzùæ^DÇËêKÆÓ˜õ^åja[ô3bâH?Å\ªs‘v¿怦“»€KÂZh9:µl Í«ŒéÓµ•hŒ U*–2·î=j| Ÿ®‹ç†h~wm—ËU®œh‹äždtß±ÿ˜ØçŠ eäy^*ÌïƼ>•ñ¾Êë#fþsSÏbꉫ;5—‚M$ãvÃ=ô€I“½ãe ¼¾V)¼‘±¦ŠŒ¯)0±'ÍÕ$œO†¹&kÇ!Ü…aN“E8Bd!§¹a ‰¿–ňãgÎÁ4.#wµ]MëÕ¡0‡ûiÑéÛ³Ñힸ³¯¬2¯¼>,¤„²™ý¨›™ëGýN·Ù¥`³ëÐ1ijG?Ç‘U^A} Ê……ˆ4¼W\€]b6N3£ý»¯ 4H§6„—q††”)öM½?ÿÛ(¸þ¸ן÷Ÿ½ÇõvÖojìtÝ).oÙ0ëž·/NœI”¦~9i‰½Ý¾'ê_°rs‹jµê<…ºÂAVôë‰êÍ:L ø¼þwÚ'â’LaÃïHÃ3Ž?—¤ÛÒÓÏ 6· BI úÖ\Õ¡¹øæ(;ìñ²†µ²!&TŒ¨¤îÓ]ZAëÕ@ú{ç\:0ç¿ ¢*|™üYØ ± bs5 ‚'w€H»¦ï`BéÄ€792—º---61%M3£q”>ãœsîÓS’gŽ^Ò„ñÚ_èƒYÛŽ y‰æIÜñÝq –Y€¥µ ޽Ü9®“ij¢fûäÙs‚~tö¦yŠ‚ý±§¤?…Åk¶‹&0Ç¡À\&@ÁÁ—Шv58÷†ˆ¹ØÒ§3ò¾„=¦¡¶ª&Ç3sµ,]¿CjQÈ<ºîºçøb>ȃ>¤Àq>1aKpXØà3ð%¨\¡l‘õ®pÌ8ÑðY}O¿‹Úeœò0u²€y§ï 5m›Ö»¨œë j0Î%%Kmé; YEÈì/AÁ}. kW0ß {ÙÆ]2¯£ä±o'@[MêVWEò}îмžX3¾–j‰&0£bÒt•ŠZ–œ*b< ]¤ÁòŒ°c‘–Ó»—rŸæ‡_ÎŽ.ײS×66 £†ãRªôæ;R8Fü¦’Á INJ<Ö²a-Ÿ6*@ûƃ á¿JðëR!¥s  »üã© 9¾˜#¨%.$VÀçˆ>HÌ/µhÊôÆWÊãs8ß:aZ°dívDïùE¼øá°ƒ?‚â˳…0æWãȧÚUä=m)¬Ð‡;x´™û›¹âµ©¿‹çÞŸ)DÒÄȱ'ãÅXD õætDZœfÔ‘Û¿îrµäV¾8žåBY#<~ä™LiöS”} „†©ÌÓBDÓº‡)î‰È_ça 7ú­o‘kg£Ü)ÎOÿXo?D1›“¬gÞ)ÍäÓGs-åèÿÆWs@W³ÄÄ/fKÓ§Âäõqí×`®õíìý Äc¯#Þžþ—&»»è7VjDÉêYÐhN‘×.zño0‚׃{w.GFA#‹>.±jo½FÁ‚;8‚~ùì“ï]{¹·ÚòX½¹ 06âÉ 9¾ “Î:`c†Á-" ÐŒpÆ_ËE'h™§Š¡æ)\(X¹y¯ /‘«R=/Ž3ÌÏ”–‹ôà-^«8†f¶ib Àð¦fƒ ¥óŸ?ÙÒ¤mûÓ0û¨Üµu#ŸØå)0–Ì. ˜s¬·ÎøµKÿÞAZ0¬Ï/èƒæK“ÇÞÏþ^/ ßw£Ómîó°ð…ê–‡+pwšG ªÅk¶‰‰#Ê{îXOùùìbï–»Ötð~í‘ABe)§M?yYŒ¹YhcìgN¹Z\ûQœ¿ó 2”Î5‹—ìjÙéòSK×ïŠÀx½º~¸Îõ°A½²¡‡þ8 ¨Qˆzì¶,¿uŸç¼æ†ôÐærdŒè¢€I™£…æS ·} <_™ü« ‰LÁ'§¼>®íÑ\ÏH µÅÌø›Ú2懡pCº —!v3za,Ç;Þ~­êž<ßÑ¿k¦ù•&µ!ÙzøÇ†u§Ã·~é’ݨZ j£Âíy¤:Ò'N*M>Æ€  C{sÆ ü­‚ è~!8ƒ0‡Ý黡#Í2¢}ÊÞ>OjÑh.ÈÀ#÷Î |ñþwóeà j†¹–U†æÅ˜D—kì­7tÊZóºµm&þ„¹Õ½ aFP‹Ã† ÉûàÓ@ZÂA: Mð·¯ Êèš &¼‡o üƒ’ÌÎöSq±ßoѴǹøï ˬ¹¸1À¹Æn¿vôÀþ?ÐÚ¬ÕÈdJ}aÌ+K^sáD4¢³‰ÉRp9|üŒ8«®Ê. (!#¯º\Ÿ«\-®÷‹ûw~é#==ÑvüÐÁiøN¡¹#5ùPût)À\î€þñ æñ` ÒW $¶3Â¥äõ1¾¯®)èPð((…Ow²1ßÚɸX:Ã(AC}W Úå¢.¯„Ž¢n·Àí1Z^T¥ž R[à‹ˆ\Lù(Ü(ÀÆ%B6¯Û@'ÔüVB¸çfؘñ-ÐIä±øÇÎkL ”Z xKØ BÉP’Á´ÿûÛ/3oùØ]ßÏ[YéÅn´Ð>Ø„’‹îBa®ºÃ‘ýço¿a¤JØPoÒÐa1!Мÿb°C—(ÃÖÒq÷ª͈£ ¤ôE?ÿôË=£žzháÊ­6JìâÁD|‹”ï‹ß—Jaƒ!‹Ÿ¹wBäf6J$QEú@"K§Ón;þ÷O³fá… %pJúì¥ÚHñÙªŽå0@•QgO)È-ð…jËÝYù²ñU¶mVGlÞ}X&Çì’™ÆÝ{ÅqÏù^ ž¦°Qa¶é3¸ Ã÷l—ÔbË„íìÙçvl\ûw ¾™Íg&”` pŽ9×›V-û411þ<†šŠƒ‡b(hð0éHè$n£îê+^9Hfˆ/É‚†+×€|Ò…Óôää„ó1k–¿tîÔ±±ÓOº)‘Àd|·÷ë*Æ<8PŒv³xøÖžÒQ¼D6—A­Ø¼Ûy:þ¼¶.úß’’RPTmRpî}ùû¡¾{r}ëzMßV;‘aÞ×A `_©é`0 ¶púÑ´ïïÕÛ`î‰@çS¤o™k¹¼~_œF}.·rÔ0¸ÅØ“ÒD‘f¥9Aw˜RÑWŽšà®mçT¬Xî3óýŸ¾÷^±4n6jbÀ1àMÍ^É,àœý×ìÿÊWªü%®¿}è=4SÃáƒQˆ.ñ£AAy%´;·Í\½1>… ç)…t¤Å,˜ôd”&( }a#½0’Kò꿬Ãq }“©7ÝÐ(|e¼µQRš¦ÄçÆJarßömsÖ-]¼}ã²7eοZ;|YØàz–íhÚ®ã`FW’ë|Ó:Ä€tØfÀú 1Êý)\5jÆ ?bi§‰_?ø4"š |1}Îrø‚=a¤³VØlA•ÒšqñäÛ߉¡HhkÔh\赌g‡pT«jE•Œ|åZÑ«¢ _é—ÙEŽo üCãÎ5?d6“æÎøúûwÞË6ï‹;}VGX;‹éÃl”  >M§¸c½ÇÖý<“æSÔj0¡Ÿ6”fƒ#6éƒX(%p ôÁ´Z?¸»MZ*sËà‡~|çWÎ<þÈýÜá ”àÛ‡i³ÙÒ&½ýñtëøwV¡ÿñ8¸vpîÂ~ú Ä,¢Ÿú^_?34[§r ÀÝFðT‚œ_°-:”Ó¼4¡Ó•Ï™1†ê’ÅB8>§ÕPýÙë—úæù %zj^šð2¼)l(fzP)làæ0ñàÎKVÌŸ³ 9ù%6AqcŸÉ0gÁŒ«nõä!ºÆë!˜/e¼ô9¹¢ïõÍW.œ{ï«ïž:_J•æ;&üÞ6È,PÈ])~HÔB,’’ úþGܛۢ]§fõš6o^6¢LhH…ÀÓ<xñY@ä´´””„”óçÏÚ½sÛÎMë÷¡³J{EŒ…^sÞ•Vƒô`“>ŒØ(!פb„h£9µ\·, ~žÁõã¯:7¯Ó¨Éeaaáqáa• ÿêcFš‘˜@ó2€Z•µÊ÷¾eÈà€€€ õ)67p_‚ÝfKÉ,O¦Ö,O¬x?å€ÿ!Ä¿ÃáHKOK=—|>ñÄ¡=»·ìÞ„¤2B \7(pÐtŽëç‘kEÖœáÚ×@Ò :¥ÀÌ< ¾ÖO¿ï7¬#nõéqü¶d½hÞ¶ý£6–¢£¤[_¦]ŸÆ¥Ù¹’.…‚á‘ãÿaS£ÆõâÙ ° îòI uŽr8Ï‘ç²8Bq `–ÍJp]è>¢<ƒµ`òÃoȤ‘) s ž•s¸6Ôûx”&}d¡Âï/Ôüz’>ˆÒ ®\?¸v¨ƒk×f;äÚÁ²ÆC­%<«ûì'ÕOõ[=Wï¨ßê¹Y>‡…ÁÐ.ñN…®<¸V(aC]sƒk ×âžó òñýÉVÞS?Ü´KœI•qÔ¸wô ¶kÙ¤åÓ÷ ÀOJ˜Àpëîë¿z{Âí÷Y¤mÒ{ »¡!<6ÁÄ@ÉÄ€·5Äÿ¸¸3©€¿ùáàÇF1©d (ˆ¨]K%pðco‚oa@Íç”sHF€LbÃ@B1 ,›µÈâÚ&}±áÿמ¦bDшÒTðiJÑž6¸ž)gÅs=1 "¬OÕÉz¸)à;fyÏãGÍ…¢ÎZÿ¹v¨ƒk‰QÈPóÃ÷|Ô·JÑŸ%1áìÞµª´ôåN›}ó25[Šî}x¯A³f>Œ¢68|õa7^+aƒ£fèÝP êÌ÷M( ¨½b³gd£À3ïsŽYN½‹K·`Ò‡[´øÕM5ÇÞ "BшÚ¤€@£P«´¢ü°g1{™×¸Îñ¬žûHU»æ|—eÌò8áß®§ñ£p¯Ö ®œC%`p~ÕÚÁyQsƒK¿ù½úý«©3ùdÜ~Ók³£Æ€ `\wH¤L ”: •°AÄ*fA}lø!㇅ 2Œ‚†b pÛ€š?2Fƒsidø¬ Œ‚I>4É…èŠ7éƒôDæWµA¦”ë‡qÝP†:sQš ^ø¾¢7E§¼¯Þ1Ë{?Ä/AÍ£8ÔºáÏB鉠hK3îúñ¿Ì½ÁPü³0!¿`ð”åšcò2ùEšY®Äb (… "Q}ày6~pÈ0wÕ§ú8ñ]|ê#ÊùãAfMi0Ô.¨zVЛôQPŒù^ù¢¢EwdT]w‰%8ðl|ÎgÒ¨‘ÞäMü£˜µ©ûfù L\*~Ô|°…wΡ0”ЧðœÑšýk¤}5ÿ›Þ~ñû o.ž¾w€hV¿†›ž½…èr¢AÍ*‚ÉÿJ(ºWç0$s& Ž¢6T3Ê?@.Êêî>dæ¦Â”ožUŨ9å³Â‚ªË¤Âb²xÞ/ úàÈH'pÕzÁ³ã=×kÕ?ã9·÷ŒåÔµYþÞNŒçœðÃûªçO1åêžzÏßΪÿjL¿†ŠŽÙuPÔ©^Y¬Þ²Ïë†Ýá“Z,^¼ ¨\ÁoqW=¢¼X“’r0®I~;³ã& ‹â6T¿¹@;÷z{äj¥c}|‡j!ó*#z˜à£Ð…ŽHSZ¬pê‡,˼)"×z©«&}x ±Þ¬¶ˆéC6îõÎZ€Ó1Õ:v#"¼ ÃØîÊ6>=ワ;ßO›dý,ó™Ba–Š?Š1×­ÖŒÄwVë…PÄ™óã'Ò”:ì]¯éÛr'vé›7¨éc‘}Þ¸ëòX‰ë{´ßÎY.îp…`ò?Âácgļå›Ä¶}±((q¿nÁ¬ãÇN%ˆoç.‡ŸÕ*—Ç{ÝD£ÚU‘ñ[‘ÿ,÷é"þZ#ΞKZ47_ÓQX,âÙ‹DjšM¼7c¾Äï –mùÛ?÷\ß] |Ç{è·¢uŸ¬Y³–lÞÛÞ)œt§ÞIh:£ü¹%XÌúw“£ÆM3dĸ ÃuÝy·ñžº6Ë á³øÑµs Ì£q~H óñí\¯æÍ[çb6¬Ö/CŽÚŽ>n±hOÛöššTâ¬X>ŒIý2V3oÚ¬·P@Ò6gü¹d=19ÕâÔõñøƒŠƒMï{uÊÔùÐj}€&q…“> Âb« (èãQëÇemöø‡‚ÄH›ÝÞ¢nõ*gœÉ‡SþÛ°«:v`Ý®k]Z7ºzòˇ?5"&zÃ.¹ck¼§®ÍòBx?Èì$ÖŽm>û¤fì©øŠÃÇMxUÔ©òõÔ#hVå@¦’À³8š¶ë8xntŒ_ Ôf´kZW´n\G ŽØ¼çˆhß¼žèïÿ¬åÂCÅëO ?,X%v<&† ê)Ÿ1fXh1úîþbéºâûy+ÄØ‡n’ÏN'œßýµL\we; !åûy+E"[6[5»·^ÓIDT—åýøW­¢‘b’Õ:«L¬c÷ÐE1»Æ`s¨QF‡´“š®­œ™©N@IDAT„À‘ïþiPò_[”fù\q[„ø©Žtô_M·é‡: k–ÑÓ¢^ú#×âa¡U|—+zØK¯öüÜátÖnݤŽÞ¹U#­M“ÚÌ\ˆ¡˜¯5’SÓðñ9J{^Yà5ìLÅÚl¶‡>Ÿd]€¾ðƒ{I`ÒÇ%¡Íç^ò}@“ 9gAÀmФnu½[»&à_`¦åŸ­Õ÷>®AKzÄi³=íÓßE¾x˜KùþxC9´Ë\/ä+á¨z÷Ï~xY³F—¿ôÐ…þÎz¢Ï­#1)U¼ðÁâ‘!׊6MêÀ¼i‰€²A t¬ê‰7¾w_w…èÖ¶‰X³[ü¸pxïÙ»ERJšxö½™ÐpôµªV§’ÄûßÍ»MT®PV<2ñkÑ£C3q/vÿ o|5GT«TNÆÂ…5_LCæ¸Múð'È£¯^ 1|\Ôãšîx¯bùp˃`Nשf®ỹ/?nP«ªxvèuÚöý±:ÖÔŒ;ÿ×C/ZG`Ãâkô» ëGq•t¨öCKOK;u&!‰ëŸ_ÒèÚíû…Ó©‹?—nó–m’&Oç’S¥™·[6ª)–¬ÝK M,]¿S\¡ƒ°óÀ1i.õõìhù›ÿp-8r"^ ü]à Ê……ˆ4]¯J¤¦¦Ãh8(¥ÝðZ.ãHvmn€¦Ï‚YÔâ’ƒus$ÅÀûÖ§âñÎ7™GA_/Pù¢6´ac_{ :·w.¿¬>ôÆZØ@˜P20¡Q¼pÿÀ€ofGëøH½ýЫöùëV \ló#p˜ôQ2HÁí(<@¬W«Q¹Âp˜\ÜÑ·‹VB¢Õ¸ÅWi»yYÃZâùÿ»>`Ú¯ÿRKúùƒ/¼úůM|‡IËߤP°Àþ¿ÐR’“ΜOIÓRRÓEh•þ4¡BRBÁ¹!ÐßbþŠÍbýŽ¢{»¦b@÷¶âS8sÓ|ªï­¡ý¨+Ë…g°OÞÝOÔ«!ï©X¡$k!9Æ9ÿmLªZ³fÐɸ8Ò¯¢áü|%~¼ùχ/½tõôffÝ&\1ÀE±(@ÃnU¬Áo^~YCýáA=MA£(°^ÄmPxäÜrŽ-–€7†>=fº@‰2¯=“>Šx®Š£¹BлkI;·ÿfëÈ[[ýßÀ+MA£8&ÐËm— ¹Vƒ32Mk›¡9•?%¯õÃË=Ëwõì§:,‡w팋®ÑÉÚßàäÙsÒ¤i@÷6âæ^åq ü(šÕ¯ ³Ùýr8‹×lMêTWuh.jÂä)(3G£ÚÕDYh+æ.‹§ãEºÍ!¶ì=š/Йœš’]Д'ÃK 'ùzÙG ­~®ÞQ¯iëŽõÐ%Ÿ4|Ef7|Ã'Mª?޽#^ž@S@@QÚ}÷=ôyê5é¹Y‰Ïb€s\»Z%=4,|j¯^·”EGs8LúðÙ™ôNÇ Jè…e÷æe ±†|æ£(Ö,ï ܬ5O `ˆ¸©gmøàkü㧯ãn“ûåœoZ½l¯Óa;½i×!ŸØÑÎù†d˜)üÑ1ÜtàÞq V$œOXã¡å8(Þþf®xmêïâ¹÷g øÞÈü#në%bOÆ‹±ˆ<5êÍébÈ3•Æê.º ½:µ@ùÕâYÔÇÈTþÿ®ÝŽM¶£ËÎÙ†¾sî)pÐLcßbë€Ù°ßa Vê ˆ8*ÒN'‚þ,ñˆ”G*É“– ZeŸ€3x-úh˜¦Sy`«<æßÙ¿kÀ;ÓçլߩÕãâŸßÞÆ°¸èÒ¤ÊLúpÅH ÿ]@úàæD`Ýú5'AÐ0Cb—pÚ0 ¯l¯^Ý¢ð{(µ;\¬L›¡o9]*Æ’ýäZçØ±aýÝÚ4¼×6F9½éc÷o@¨[®Ð£}3Á#ÚšÅk¶‰‰MK§ï´t›˜òó?bÙÆÝAè‹)^{dHIK—U„+32ML{¶j½=ûæéý»Š[{wBÍïø…½GNˆ}GOjG÷ø (š-63ζëq§Ðßyäõ×+:f ÕM01+ŠÜ9ìå O ‡>7Îý ˜ë ùxèí#ÖT&p4£Nq2¡t`€sÍ9 }#Væ®ôfÒGé ‡‹F™OúÐ:uêðâo½àtjƒ.ªÄ¼Q¢1P6<ü¦ï¾üXi7üA#®„ :sKÞ¶lþŸË¿þdò4Déc$¢Øõg“¥©üÄž’ÉÿŒƒ¤qAÐ0>Éýš>þ¶1IÁê«?þC`Ç™•‹æªˆŒŠ&r°Ÿ:5ýA˜¦E›‚†‘\«žöÚØ¿ ð/?ë‰á¹2ž¨ÓX‡vÏãÏuFD‹ ok|`^—| pα]ý–ÿÑ£¥Í•Lú(ùdãóA–V=û÷8}.õµ}GOx{­Ê±ŸæƒâÁMæ*Ö¬ýt×^×UFr3Å,žfoU1•Ôhp;Ÿù†RxþoÙªc¯¿ùÑw`øÔ.7nû7´kVNámà#>˜¹Q©vHmĵ]Zú÷À Ñûof/ÓOžMÔWÿ³ðí³§NÑ …'OÒ"‡áã&vEË­,šöy‘7n6èÿ°h3aþXo¸ub‹ÂÆ›fTd,Ê„†ôÃ"«#†+£Yؾ›ïû8˜;…s^¡|?t•YƹðòƒË…פ ¡ °nûY¼Óe .zíøésÒ™ÒŸbÓç‡>,=A*zý~½~ÌAr·ö`ÐhçnBþ1ð{ô–{›wì°oÕ?½·ã–ÿ ¼T ™I×LË\רG 4˜[!GЫÞ]Û°aÝÊ÷Ý=˜™íJ„àÜ¿[kÁÃ!Î%¥87ìýʯ0•£†‚Ú®LèGaƒÚ ·š Ò#É^‘‘¢Œ×Y•{µ ¯uÞ¬Ø'00e|ä•´P)lg¼%lpaå`–²!AAümB)ÄçÞ"¤ºØè$NÚðúèÙ±9"«l›÷q+4ØìvŠjrˆXò½;·ýÄŠ‘GfüµBœGVݱý$Za§ò|Jªˆ(_V`çRš5½6íwQ½r1rð5röÁdˆFˆM\âØéñ=Þ?wZT*&^Ý^\Þ²¡,ÇzëT¯ çËóâ¿ »Äcw\›zØæG?,BßÚ *~˜¿JfûåîzÌ®Ãâßu;DÛ¦uÄ‚[°³]Fôº¼…¸ã$>vFÌ[¾IlÛ 'Ì@ˆ¿Òº5"²ú˜­!/ÿÈ>¸^ðÀ®\x™À@ÒŠO@­ªfP ýI¨[=B(AãØ©ñíÜåâðñÓÐ’•w è&Õ®zQ¿s¢'ä¼3ºÏ¡c§Äö}q‚fq÷ßx“„‰-ûŽŠf¼ps¯ÈkPQÖ»bÓ17z“¤Á–j‰{®ë&iôÙ‹dØÐ÷fÌ‹˜€¨A®4uEÛF²È‡o "È¡ÏU`¹ü]Üÿ„–):Ð(-×î+XæÛö Îd'#M}kè ûF“»ÚjSô¬Å¬Œ^·mÝš]Ý\ßÛá°÷Ù¶ïhy¾‹5ÂñÜýô²¡¡Yßd2µÓç,C¤'V—˜$ðAl00<­³¼Â„I=Ÿ¤ÔtýòØlv Si[—Ì~}iôÊch•‹µ2ŒÂi€“w½’n ð´‚ŽðÜkq*ça‚‰K€' 6œµ°]R/r‰ m Ñ=÷bæÓ’Ž,ª¤ÒÅ8ú }ц³²øqá*ÑÙrëßK×ï† rLßÕ"!)YLÿs™èмžˆ¨Ž0Õ¤°pÿWÊDV̺»nû~)lˆ=-’˜jóžÃØ ·Ka`+ü«:¶€É“S|†f"â>dü?ûõ_Q­Ry™u÷\Rª iT…²¡â†«Ú‰v§É¬~¤&®2Ìo:¶¨/Ë"ԴغdÖçºÐŠè³a àJS8Ö#ð…õÐÜÁd-O?%Yqþã"l¨µƒ yñ‚®ý›iouõ” ‘K aß”ÀAZVôÌ"|F‚ŒhÈ]Û’pìÅ5Ëð™—vêXlâ¬Éÿûî©1Ëpg‡zÇ,Ÿ‡žÆP-qÍz9wò” A­†òÓà=>c·Z Ò‹S× »Ï¡Œ ÄÀ|l?‘Ò@ ,{ôëcÄûEÀDt±öèÕ|P+°Nw«õ,¡ü¢Â¸1"rüB³M?vª»çæ½ÂcÀ[Â{ÆÒ‚…q¡-|Íü™4 é4‘9Ÿ¢ø€Ik “”âŠÖ²pL íói2µvÛ¬ûŒþäש*‚ÅÞÃ'Å8ŽwF]PCŠØ=¦©JÃZ»ñØ•&Ô5D'ª[½’8“paí¤ÖÄh&ÁòÛáô› Æ–ššRäõ3…–²á£iÐxZ6ª‰P•Ûe¿¸‹NƸ8!ú]úöý7'}=ñððâì_~ÚÞyà˜4›£¦Aàœˆ—fQê^~èI |'<4&S•²´m̰œ-¹ `jóúsäoùO«­+Muo×Düß“{!po„ ÒÒ«¯@õˆòbmJòô‡ß*åÿàÝ«Tû£Xû‘'œºøp¸uJ·©ÖÔ½£@ƒL'A êw¾CqЊk¡QÀO ¼¯ü>øÜX†õð ó«À,ŸÏžÂq~” Èù£CF!Cù)aPÍ„9m'?ÄŸØ¡Zµ?Êz`^äŠ~#FŒ‹‰Ó“©ÇÏ5Æ4ÛY}XdÔ!àò»ZÍ^µZoWk"¶æ.M?$:°ÒX‘ÎùˆOšTÁ™hûBÀ2à‰‰ýð¥—NæX¸22qŸ´ûYˆêŠìÕ‘Ö¨.G€>mü˜5…i” ¸7@}­ÔÙm˜uúÔ‚Í^«?~Ÿ¢úa0î/‹×JåŽ`@€èÕ©EfV[ÞÍX– Ýþ£2“¬¸o`wé±>!¡Ð$¨èVU+eD¾:væ\–]ÿñ3‰Â¡Æ]R«p0°´Ëÿì×Dh[h•ÐÃP¨úÎÃ4Ÿê‹1·iR×]±¢¾g¤Ÿ¢‹ü "$3XÖ“ˆ`¦4Sê=|(Õe¾èÉuÞ]ÍúTeôÅ¡cqÔc·©[yž]ë¦F¦t«·îƒŸÈiqïõÝó¬£( ܃þ |Çûh“ô¡hÄ'胻¥ˆÔ2 ýš+l'¿Â<ßáb߬Ž )?ƒ&L[~2§ÜPÂ.³4¼æ9^£Å{¬“L,ëQ;ç¼g–¿ Œy?Šöˆg%l(Í…^sN9ÏÆ9aù, s䄯pƒªóëóÚmG€2éÃ#'ÌÅe_XÈqn7ãØŽ?…ú°Òï¼¾«ï¼a¸õí>S­ÏžÂ³ÁÔ_L>6j,vY‚¼%h°CGíÑ¿#Š7z¨ƒÅ\Øiµ EmŸÂtÅ=7R˜/¼ËŇ ™H·9¼’5-Ý&ö#{+£šø$\0•2Ò„ñÚ'ºMçoú8Lùy1Ö ]¢ƪ­{¡¨%šÀ숶ídÚhcO_Š¥ëwˆ³ç’¤PÁç kŠäª"œ¾Y†LÿwpBý×÷h++¦£BÙ0éÀ=&\[ö‘!x)@äÌÓÀ燵‘¿4ƒ‰p1ÓÉí}>£3|¼wU‡æÐœÈ~åõŽWŸ»§¯6ééÊÕ®&ÍŸæ.‹C \x˜Ôˆ…GÕfnô¤ÊäçL“¸ew‰¿aúÇл\èÀÚ¤nué N µrå)+'-5g[4"9iÙUPÊO?Š ŒZ+ •EÐj>š€Mü‚á㢞Õú{#"'pçúþ4F!;°dR)hµ5,£T5f¥Ù ÃKPe(lð óE0ËgàÁø!î‰_ÖM|S¨àRÀà™… ·j—€ ÐÇW0»¢Y´§¦Ž·àÂSï]Œ/ÛÉQã{¯ïÖ s¨h¡/‚…$èí®ŒÌÖm>6nR+›ÓþŽvžö2îŽríMxù$ÎI®0uBäÄ\ òá“Ö÷*&Û’ú¢šèBVUô¯kºGäT’ûè½oFuÑ|&~1[¼ôТœ}} ðGFs…ˆÙ}cR£ÙÜУ½èxY}u“NÇŸÿö¯xÿÙ»³vä=V¹ç*ÊQð>}dZÔÍ!´í„¹’‚Á`éüûþŒÒT†Œmó)H዆6ḋ?"UñÄ}¤öâ×Åë²¢?Q¨ @p&RdJ 4µbÙ/ÿøO¼üéÏRx¡³73óænDª­pÿâ·¥âÙ¡×å畬2X~[²NF­¢Y}-‹Lj1‚‘>ãijb²Š±ky7Mÿ—·õ,ÌÝB`DªV#]ôrnôtQá\n´mZ‘©:Š?ÃãÇ…«¥ùµsœG ½ÔÆÍZ°ZÌœ¿R¼óÔ]9ÖÔ­]S±ró^qêòQPÌ‚:ûT7§Ž|­0ÞÖì'šÁ&”‹Tö¯É¬’QU‚†¢}5>ží“þ³‹K ª>Åܪûª¬Y>×…Åç€sÄ35¬—Â…:ø[Í .³ƒôÑ€éî¶Ê4"©©+pêZdfC‹‹¤A7Bÿ ˜M½"Q¯iŒ‚›úxü‹[G¾<áQ‡CŸ§éúÈÇÆF½óñ„ȃÆn¤³W@0‡¯0Wcù··(8°ü“ZGI[èLÿŽõ|þ]Œ§‘‘:„ó~m§k:MæV>7Ù:æË+xÂúAù4Û¹ ‡Ý¦^ÐÖN,Èk`1‘j'Ù’0ç:6´.ІnÊ|o 6+>Vu”ôs%…¹oÃŽCbå–½âÑ!½åNöîÃÇ=žØ‹¢­ŸñiA#w$ãÓ nLPžº§¶‘dȵ»ö”´tiG¯4Œêôâ7H? µ{LwòØû³Õñʈ[²ý憶7ìf¢”;̪N>S¡ry­Àxæ]ÆHGÆgôçà¡€¦]ª?1»A³±MLD(T:.s7|ÊÏÿHó±b6TwÕ™wËõwÞׂ@_ … §ª_M–öµGIá½P˜9e€–­lnôdœG¾ëjÖD!“‡‚ë®l‹èQmE"¢X…†I“?õìD¬ºµw'üÔ¤0ëZ·*ç@NBWøkø(‘ãAfO>ÕU)pDŽß†NMs ýßá‘Qó YúÎf™“™‡ƒýVL)Ïdz”P@Z'ÇÆ{îUÆx–/›å³#nx­ °ø4Ö«æ’÷H›Ù€y42ÂÛê÷Ðkû!¸¾¨4YÑd$·¬Ÿþv‘~æ|cüYTÅŸ‚XnŠ»þO~mì|ønÄÆn}»Ðe^(§9Aì?à¿„&P„ ›þƒvV«ÞÙjÕœ™þ2ŸÑ¿NãO8tÇl”õ7ˆ§.öŽ;íö~ŽjýÉ„ÈÃl‡sštnê­2çP|Újɦš¨ô´®ŸxZ™ò$<·ãÏ÷ðÇOó»R%RØ(h¬ú¦r1LèlÉÈ@üx7ÌŒ¿þMHã"ob—\ÝÊ:›å¹px?ª^"ÛîySvùµ˜°Ë+RL‰#(ýÁ;æ,kB>0àtBØpBi"rxk/î×Ç ¨ò™ÅàH.´­"Ôrõ´±cã $Ôµkb5¾}ãì¯B¡ŸÜÕÿ*º=%J>³tšé ä}üI޶i:ŸýŸc®Çbâëƒ7\.Ê^? > ™Ú’ú™&•Ö‘·èºãG_?-j\o¾WÚ€;+%Ožf"L”ÆXõŒÄXõüð2Vý¸“2V½8ÃGÞzÍåâ„!µÙ2Ÿ1êÏwH²Õ¯[k1þÑÛä¶Sûæõ¤ ¡bÜ÷èÐTŒ„Öâ艳‚1î]¡-ÌlÈüŽ §êÔÆxû7^ÝA 'asM ÉÍÇ?ü-ãöºöré/@νGŽËçLÇP¦ôÓ`^–ÇIî¾3Ãùä48_!zth†h69h ÄÝOæ¨ s.æ0¡ta€;ò}¯h Áàø–p÷ûZ³>\›ä¶ ^ÀÀ~„êe²@ú0‡/ÂoKÖ‹–: Cß”vç2”Ó&D¾ †¢n %ðrÈãÐ÷Ù8.ØeÑdjyX,]êž»jÔ3U–guÏ,Ÿ!$\*>yÎ8ϳ9ïœÒéÁ4rÁXntMª´ñ“¹Ó„~‚Ï!DÔr-§Y,¯O… ÁûÔFàsò5¯ÂÙg·`KeÒÅò ²Ea Ô¾•åu­»zjÁ¼ÿõ Íymµ^cÇæ 21XR1ÁèüÄªçø¹ëO Ñ š &Sã®ï„;¥é µTjW­(R3³¹æã^V–ùMSF#BÍW‰ùòä_ŵØA$sG INñöiwM veø k2kÊЊ0Ž>ëdlüŠåÂÑßšɺ/« ûLÁcÔ]}³´)|˜[_ÜÑÌyáq ô‡ðÌÇA}ÍÕÙ‡»ê¿]c ¾ Nl#b­Äî¤6”ةξÜu‘™°mOwÒ윉Æ´DÔhr£¦T9V 8CʵB–7– $<ÀÖNîXàãRßX.Ûµ …q9|,¤Áç: 2 !OVë¬2±öuy7,Ô²9ãQ ûW×vzbD%VØÈo¬z"‘ZuÛ"óSš6ðÇ£!Ì“hñÓ¢5Ò¾žþOÜÉ€‹qÏp§¯¼9bÄü[DYD‡áNr^ùZ7É0½’ ⟮m †PÒ·‹L*—_=Í83*¡>’y!·<¦°aÄ”ymbÀÄ€b€ßô¬¯½öÑì–‰€Eìud„]­F'ì\L©¤ù”Ó¢ïqm:-½L¶5C×´pì&s!Éÿ c=vh®²ØX§q,9t°~Njàáz–f,WR®©k„'Æ’²=Q±ÕáO>§XõãN‰yË7Kí…‚%pžý\(öÒºGû¦ Ž‹`h6FÝÕO0Ô(¡ 1îé|+Bœ2sô^Ô^䔿Z#¡KËF°¯^+6ï>"ŽÀÖþ›®2>–׌BC8nÈßÀß¹åàsL øÔÇA}¨kfWŠ üÆ“6Ši"Ìfý `füª6Ü ÍÁ1ðD5Rmç†c$o»Žš‡~x^«CzC\4^‹ÃFÓ•SYïéz7y­K§ý¬ÛÆ M³ìÒ™ÒCÓƒ`5ÚøÌxýæ /$Â'ë4D—ˆ4G*S“iDáºDúlÆ—ç噄dY†É±èS±|S¦`ŒO™~Ú´wkÛX\ŽH-åËfÕǧ§Pž1îÓ`ZuÎå{ ùpfjf¦_:cnÙ{T:š+ oŸÑv‚”~Êgõþf–ëfˆx3û¿ ÒÑÜèl®Ê7‚éUp™@1ù&é«Â¶)˜ópœÌùÁþ˜àY ¤#»s¦¬xÉÓtôPÊ̤ÉP–r"àðñ÷D!CùððÚ&ò0ÊÏóÈGQŸ,ÂlÛ`ü_‘Óô±pÐ`ìèHëØkŸð†U„(c‡î|@ý~ö­·ÂñQ¹š¿4}¹ºÑYZo)ÖžæÈUÒËõ9ó¦¨{¨/š×ÝŽÈS9Œ´¾UfV|SPé¿@Íeø¬´@ö­óÒ2jÃ8ÛÀT‰~¯NùM„!ãÖ3šL_ýš2Ã.},hZŤW/܃ôóÈ)ƽ¡zq&þ¼˜·l3"]%Ëh/ÝÚ6E´«ŒÈ?9ÅÛ¯’KT˜.0¥šþç²Ì–Æ–2®©Ù¸ÿƫŌyËÅ+“‘‹‡ ê)ÍÀrÊ€¡•J`>å´OAަw7÷ì(q¥Â2[6@D¯ ´ü³^¬@^‚ן¢Š!±ßNµŒÑ(ìÑoøm½äÜÏ_‘aʆìߤ§ÛûuÓ~ŬwŒPöì{?È:Þu»ôí1>÷ôõ d-g›îWO·U€ú(dðp¦¥¥Æ4¨U…á ½*_OÙ°`ñæè;á”{áƒEš@^‰œ÷ ¼Òë}ñÕv`c¤L4‹6‚šü€Óoƒ'…éÃ&J8®mÓôóE›vÒaû&§ÐæB“°CÞŽ ¾Ãîl• ½¶: <`¼+*ÀÛp÷oä°±QÍ,I{.>vðu°‚ ªVá×òê÷”¨çw#wÎ;ð{Ñ=gäéX†Eg– ا;¯ö ¬Ìa¢YÇ ÝÆþÝ=b\TSô/‘Î[ZÛSAûë \ìäñc60éðÃÞınn*8°©°ó`vܯÝv€&ŠÆò¥éÚŽ|“Z,^¼ ¨œáÎÛã¯Q^¬MN9‚vL͆·‘mÖobÀ1pûí·;Э›¡ÕxæJ£!d4‡€A†Q¿öba˜^3èÊIÖ1רÝtÿOä¤z×®;¿Æ²ÞÏá²!V!1ßÝŽ•«rçŒA›{ X}Úˆ+Ñ.v™ îhb7"’­QmMÿâæáã&öÀ—ãSÜë„>væVˆ¦éÒCd"ÀŒ¢ñŒæÏ¡®~¸ÑÂË{ª_>´Fuq8ôlQ¹.¡Ã%Bظ„q_ôŠ4\¤!¬l<¢TñC»açA¹Ó\@#äãÞXÆUÐ0>ód¼}c½Ü¡u¹õÅ]ù’|sGF›GkQŸý.~þ{xæ¾lÚÚ\Qð+ 013€+ ¬ ÐÕÃ)ÿ¸p[á“åW#dëÆµñÜ&óª…KÉù¢rÎÄÂDo 2Ž7†™sj07 ¬Ù¶_ Ù+böˆ;û_!Ú4åFL±–j <©=>¢¢Ó1Ú›Âýþ£§D 6#Õ¾tëÁ7³—‰í©á;ƒà“Åÿ˜]‡Z{´ŸuÄhCèßÕëòY›Ô ÌBÆojÁªCÐëÞ® ž_&|)s¬ÚcÔ¼…+·ÈÍæÖ!­Ñë@_øˆuÉLô˜W~ŸÁ}ºˆ¿–Ç›èÔ¢¸u1‰ä‡© º|oÆ|ˆßÒÛpÏõÝÅÐÁw¼v”FC½Ý´Y¿‰>„)Qã>Gw>g‹SöèºUkÇåRxêˆ6”åZ¡ Ñè ª§¤ªð´êAngÕæðI“*hç-Õ,áŽS™I:³½6uüK«p£ãSレ|Æ^',0ðô{Ö§Ï M{-ògüþydTTma¶M¶>wÂøÜW¯61‚»×§0}4…\°Çö"|¼¿ø}©6húÂ\U+e6r©Â|äGÀŽ4BõÁ f˜=©®ÇžŒ«·îS?¡¡ˆÏº¦yýuútm•u/· &ZäîypP†ÍX–Ìá–=GÅ£·÷–!–¿¹óÄ0P€Êù2ìÖž2$ò¤/çÈüL.©r¾0¤2…ˆïaÈœ/º·Ì9C¯«oeè ÝÖy£˜!ã±ÛûHS@úµiRWú%ådÚeìc^SÐPBG6+ pÕ•šª»¯ë&çjÝö}¢cóúØtHÊêƒ;P ¼ºSs™›gêÏÿ÷U 84•yo¶A°KNMdÜ7ï9,M1ÉìS)¬óÀÄ߉ ß1»Ká³KëFr´ÌëSÐ9fž¶G! ŒÌÿÅoK3ÙAû+7ïÉh#SØ0æ÷IHJ–f™ô£ù&Ãf÷×2iêÉ ÔØ5€€Ú¹eCd¥o(v:&srDTÏÂE\˜F Ùl¢äaþ½9ªÉQã—”Ñ1‡Ʋ¿ ãùߨ±Çݽw¦®T‰h"¾OËJiSÝÊP²Bàº)"o½÷ôÓ)¸ØÓsÞŸy4·ç>÷LÓ="'x¤ŸCއ:Äd·÷ëê¡ÚÌjü埅ÎóF³·mp²g1éðq`9FT”2ùÃåŸÄäTÉü1™$}$^Õ>›?€*¾»ée`öÖ¦Sv{†“8ͪ˜ ¡09_è+Âb©ø$ñ;|N°/}SÈ4WãH;Å\ªs‘v­Y½êR€Ø…¤™Í¡±â¼üßÀRKaìLjBh^IÍØV\ °¡àæ^¤v„AhjÇ$œ ,A³ºk»\&M7ÛBЛýï±cÿ1h$,…ÊësSÏÒÿÌîtH¡€&žLBZ6„€Œ!þɼGa£09_êÕ¨œÕMjhFC&™–â†\胳ÜpÏ=`ÒdïxY¯¯U 4k¤YÔ:¼f šÉÑ$J5Z3þZ)ö9Žäša09J–¾6ê9Ï @ ³O I&¡«ÿZ#ÃSW«\NÞ³;ìøj*¯Êfú–ÔÍœwõ›šF9Ë+¿1G…ê4¼W\:eãÔáÓœÎ:Šk"ÌvýšN_'Ü`@w:¯Ãgp4áS¸A 2ª•›¢æ-`Àëp§Íq">!É6<0YþXEü¹$Ý–žNÛE·»’¾Dd*WmÝ/šdæRɾ#*†Ëà4•ÉÉß¡\X¨ø¿{äZC#3t2w†™P¾lˆ fƒ&T…ÉùÂྠ9Ð )lDT«1pæ‚U„"BÇËêÜh¥È:ÂäÉU0û{Õ6qøØiõè`iõö7]Ô¿20Crj”è¯qYÚâ¹ÿ»^$ÂÌíùÿý ‹fŽYAPPF Õf€v1ŸAß‹¼òû¸ë·ª³¨Ïk¶ìKnÞ¶C•›6Ä¢m%pu7ÌöL ˜(aÐC£4gÚïAíèÇÖ—v•°áùÜp.þy®‹rÊ––›˜’¦%§æêøï¹VÍš|œsÎ}zJ²²•”4‘ÙAŸ ú4ˆ=%¥ßúf®ô¸íÚÎùÆ!ý)zù7zã.•fRq§âå®x¾+AÁ5r¤€‘ÓR™ÇݺKMǺŒ˜Wøœ/îúS#¢ÆR:þ2"[QBô!Žó‰ [àp¾E ­à¤O|DÃ÷¥SË5MçéòeÃ`g‘þÔpä¨Á8? jC¨iX°ò‚·æØµ_Íï£Þ¯gv ]ÔŒPf½ ľœ]®e§®”Ô©Ý0j8¼Ù´Y·‰%S­ÏžšúÚØ%¦ Q4}ñÖ›gÚU_!ýø‘CÑå#"Fo†ãkW:AšPj0À9C¢Ù·—Y=»’¼&;}0úh#*”Cþ€ñÐ-W8À@ØÊSË|ÌÇÀìõMêÖæOÃÌû_F¡¢éŽ SÌ7¸ÓMóšRuƒÆ£°9_Üõ¢g§Ò øÉ·¿C‘?Âès஼'ïåBjýÇÙQ½&Ì•NÀ·äBRMOöÃ]]"Þš¾2M1—®ÐÑ¥¾ú#ZŒ~ë[ÌKѧK+qâl¢k±‹~³Þ~ÝÚHÁtnô&ѳS3éX΂žÈësQƒnn\J~VÃ̽@/³¬3ç¯ï¸ã ‡lO»C0ç 8/éèmÌù2rpoÉ—¸$MeÔ½¼ÎÜ Nª¨óHäƒ>Ê–)S.âÿž{ý„pÄ>·~0ì¥$¸ƒS¢ÔÐ'D7çXµaŸôê@´s £Á$ã`$äðýQͳ‰Róï£TO¿O þ—γÝâÎî`ÛOÅÅ~¿eÏaM(à\sÎØÏL›.È (í†ñ\¢èƒ »§ E1*çËQäÌX‰ æ4iq—ó…6ùö·¨üÒGzz¢íø¡ƒÓP^cxW_ƒK48j°Œ‚†—·æXÕo<³}&-(½Ò§ÃÛ‚ÆvD€Ã|k'ãbé cj5Œg^›È?hUÀÃÅŠo ”4p¶ÿûÛ/3áù1Û +jBÉÆç˜s­;g£ÿüí7ŒV Jà Lú(0çK̈˜ó…ØúkΗÒGú¢Ÿú6F'®ÜZ¢’4Ç 틊’>¾»Üé´ÛâþþiÖ,à¼+ã¢òæ &Üc'ÏópÿÔ¼kb è0àMŸ 2“ü@ØÎž=qnÇÆµoi»Lüfv´þð ž>gQt(/ù-qŽ™oÓªeŸ&&ÆÓ³7•t€C1 ¤ “>€„üBIÊùRú pšžœœp>fÍò—jWìÙÚœÛáäÍM’üN‰ÇË•¤9. rVlÞí<^[ýïGII L’¥6)¸fPcn‚‰&L ø¼%lü(HfçÔè¿fÿW¾Rå/qýøEèCoì¡y[¶L(B pG’ŒäÚíûµ;·Í\½1>3l¬)l0éA1 &}¥ Hd,I/ c—¼úïëphߤFêM7ôZRŽÒDîÆ aÒ¹oû¶9ë–.Þ¾qÙ¿›2ç_­¦°áiæ=ÆÀpëÛU…­bXx`ÜÛÏ=—cž,7kVç#^žÐ–Ao íÔê©QcGº)âÕ[#­Q]Ž}Úø1k Ó7… µs͙ͤ¹3¾þ~À÷²ÍûâNŸÕïì…ÅÇ2—¥ú]ÚàÓtŠý;¶þ¸èç™4Ÿ¢Vƒ •6”fƒ¸2éƒX(%p ôAa”ôÂõƒ»Û¤¥2· ~èÇwÞxåÌãÜÿh```î™PB0`³ÙÒ&½ýñtëøwVaHñ8¸vpîÂ~š`bÀÄ€70‡òÇ„®Õm)5iŠoÃÇFí€c×g`tßQmZ­KÀÇ´X­·óo³ÄÈ—£nt:túŸ2Ûî»ÓÆG>ã:èáã¢&èNý%„C1eüØ©®Ï/õ·îÐ+âcØA:ì98mb¢&?Å¢Oa÷¦°¡˜Ò¬6pž7óÛ:÷êçìzåÈw¦Ï«ØºI½s«FZ›&µE¢ø˜à?`ž†/]³uŸNgpØX'®_öï×¢ÿ%³À¨S<”°¡Ì¨H“>2ðPbÿõ}(í×®UŒ`Væ™^ýoá’èSM»õyµA­ª]Û4ÖÚ7¯—-ë;Ê™àã`ä¶mpGT,g=uÓSϼòëËV1y×s™g®Ônqý05@‚ &ò‹‘‘ã{³ìä¨q‹óz‚Æ#ˆLø?͚жèš~HÓµnºÐ[hºh`|ÿ¨=úw< Å=Y¿ñYI¿Öt}Ôðq“¾š:þÅÍ%}¬r|šî9Á#•ä‚p2”4…PÌÛ \óÏ¢èmkVíè~ÝÀ‡½ÕòPŸëåBƒõе  Ó§#¤÷£T›MgVx&ìc ÌaâÁ;–¬˜?gAròyîH2D%2 JØ0šPá¶“>&JÐÙƒô¡„ ®ÜáVGмyïr†Wÿ%)©qïâª~=µB‚Èè®U._V{ðæ«d’D…V$Óç,C¢>’av )#ÌòE‡Ÿ4›]?Ÿ¬cNäúa·¥úòͨ)˜Î1× ®\?(t0Ô-… NéÁ&ò‰§®EfÍSØ@¹, Aã…©"ßäµÕª[âo´Ñ‚í =-áIë{“mI}ñ#:óV©97 ày*`ïãSœ¯"ßZj_Èz[ØàÇA:‰ãÌ ;¥sgRR¢sáOßÿˆßs[´ëÔ¬^Óæ­CÃËF” ©`šG/> È‚œ––’’rþü™C»wnÛ¹iý>tVi¯È Q ÐÁkλÒj¸2 &}9% áÚ&ò€–ÿ`øãŠ`uAÚU­Õªñï.Fý1nb·$[ÒûøSÄ:¬u6dSæ³)ˆzõ1¯ŸãrñçlQBÓ¯ÁZÜKÉ>]ÓjUÇMµŽàß¹˜BËq³M×ꢡ—P ¼4Cúvä¸×;8œŽáX{¡®zx ë¶^×^6úÌš5+`Ñæ]a…†rPÎŽ±Æ{ì;ôÅS¢Æ=Éëá“&UÐí“2묓±]xç³)ã#'çGp6?NíôûÊ‘ã¢îG•_²ÞÜ ¿ø`›ÔʦÛßÅJ×c8 |ÌÖ„e±®;ÐUùmÊÖÔÈȉÂù Vëvøìqí\øÜd똪àˆÈñ tÞŒº®Á½J¨hÖ÷U"T|1uìØ8UÎÛgo ì?'œ»RêCn¼ÇHÚŽ˜u18vàšŒ“d %¸]˜à#ýKàœòP‚$ç–L™Å(ðœŸ]I“>€¨Þ Ö©èŒ­qm>;6¬ÙˆcžqíàzaŒùæ§S¢žßͲ9ƒ% Îßß‹ŒzýìuÞž< e‡çT¾ øH·9Ç Ïå aY\­|¿GJ“‘ lâ;×ú-L·§DÉû–€®S3#EAyß©‹Ñ6Mç³ÿÓúà 2—öñÔñ‘ϱ¼Õ:«Lœý@ý­£¨=.2( aƒƒQvãµÒvð#Cfæ 6x¸ ŠÁÀ#Š êc¯˜Åì™Å(ðÌûœc–SïâÒ-˜ôá-~uSͱ7胈P4BCµA£P«Ö2´FAƒ×\K\70Ôû¤MÒ¨bfYžu˜å3pâ ü(Ü«uƒkçP œ_µvp^ÔÜàÒ&¼…ÉÖÈÕ#ÇM¼ñ>ƒÚ¨ïµyjä¸ ã&ûU~Ú~;ËaþN )4þò„×U©øÞ{ðøec]ØÅO xìcÆœÍ|—k„ÈÔ€ìÍVVÓB0hëÔ´¼?|Ê” qè$¢5iŽJÁÛxO‚ElÅÊq*ª«nA†¹‹ŸÍbu ×}ñ‰õùcLáþ šžÞ·v«òîϺŒ€$‚µ [ úòð#Ö¨/>µF®tW¾€ø¸.£} ^sN†GNøõ¼›­~[*ÇF_çåJÐϵo…M wÖ!sqoî§™‚†ñG¬¯7rØíCHè“¢` + ?`ö! é:×h1uÄÛ°Èñ+qëªó§“ãÖô§Þ}7×2ê ØÈ3…ýÐI)xÀ¬ègŒ'kmAm!ìþo˜ñNÞÿ~õÂvøœ¼FþE»Mû~#—Ü‹feYCøûñ@IDATP|ÀUÍrÔv¤:_¶Z–eUÂßBÛÍT6ÐôÆ}Ö/ÇXè#+A¿Ð9- Ê0_ãœ^)úŸ0Û áêõZAWB̸Ի9,`DNÏ r¿(… ö‹sA¼ñlüàa0î&*AC}œðØÁçNÍçó©„ žyO¸,˜ôQ tùdᢢEwdTùáq]3¸v¨Ãø\!4j¤7uŸõ¨Ã¸þ˜å30¤p£ð­ð–~Ô\°¼Â;çP ¼V럛`bÀÄ@á1Àĺì|óïï70¨¿c7üm0Ôð·ÐÆážtÏ©²ˆ3å´X‘À¿aK@°vÜX.TØéЉ?ý€Xq–¼×m hnºVçÖ‰-6Çrt¤ž¯Â}úHlÆõ · Yks` ö¼Ã®ÏF¿àñzh§VÿŸ¦ŒóûÔ¨—D`œ=؆¶3³ ¸ÐÜ;ógÆÙ I¬ÌÛyŸªŽ×ôð ÑÛ/Ü´ûq`Mj#Ô‹ÁG°H»À‹êRãê‹ÅÍxW ëÜ/´]8-6>ÌB nR«A¨çÂÍ»‡hºóyà®póU¬}ÙÃðËé«´OÆ÷½u}a€ÞjÁ}½ê£B¼ð#£>\êCfü0¹¯Á¼[œàbÄCÍ£ëuaû¦ê5飰˜,ž÷‹‚>82Ò \µ^ð¬ÀxÏõZõÏxÎí=c9um–¿€w…ã9'üð¾*ÇùSB†º§Þ3Ï&L ÐF<©UPèøö®¾Šb럽é•@!$„Þ›ô""‚XÁß'‚½=lDŸ¾‡í=)곋 QDªôÞ; ½„@HϽûÿ$6777íÞä&Ìá·ìÞÝÙ)ÿ™ÌÎ™ÓØ¦áÝKNë¡OEhOÏ_ØgDMÙÃåuf]•¦|.X¼§S:~cÆH0z¤÷lý—k~”'„ZüGÍŽê%“03q+®yùBP€Ç>6J#ÁJRëy5ù«›fZ;ãÍI‹Ù¦D¤ùoôWxGÿ K1ÂXÇk(œ“ÿz¹Ohû{šÕÃæ±mțܶ™bfËϱ¬xpýR¸~µ,Ô‡³€MŒ ³®…çM™ò£¢™ê¬Å܆÷ñ3WŸ½ºçž{0Ç¢½s`0ÎŽ/¾¾ æ„»øÞwEßpΪb6dk0`ÌMžvÇjÊ^1¨ X‡/@&Pg×C€ÿ ØÓobXô“ì™aÑÌ©Q[TK5>œ¬3³­äñ¡›<­»æf1ÏŠž´“EÊã¸m÷ÛÇžJò~ææÌ™ýÏ)Ÿæ?ÃB—Tzžq+tˆ8FúÑÔ®Ý(6Z»©ù]£N …@e"F"Çr¹w}SŸ¿Œê5ìå~8êïH’Ñ`&äþzÙëS+^´BÈ?Ábí«màù¿3{IÂxŒlƒžKOçç³TÞ³wæ óTŠ4½3åÑcÓ¦Õ6_1ßÎùÃ&¤`c)årîã|ïF“I»cÖ›Q¿ÉôEÎ-áWÿÆÁ}f§_¥]ÒÁˆ½TLU¦3¹¼LÈæÝyž;öOýt~XÂ…·‚<õ Ѝû%t° :@](•‚@¥éfË¢ËÚ ¼Ã¾‡¹–PgÞ‡h#*`¢·dEfLye/XO°x¡ûqúýœþ0l:|<ü&qÀ¿~¼ýhTLþ;ßÊLIWþ}/Æ/{¹{DÉ|ìž56lÖõû¸ó†o ™¼Íi¹£˜ËHä<zW×Oâ·ÅLßrÚÃÌÿ@é/°—º‘ûŸÄ¼‡çîîžl_‘}×epfbJ^ÿÀõF¾æzÎyöœµ"ÈÈháÒǵxÊ’c¾‰?ëôeÁÃMÓß6ë슗ô¿3S•®çHž/yª,œ3¼fåÙŒÐËüÎ N¿Ž¹¯eŒS]F¢å&þÎoDQnæ?ÙNc÷Ñ*Æc gÓ„]éŽbæ1ÇÍM[^8WÛ¿„G,³›nŒmb;¥ý»6?Êö_©øÓq¯¾1ä¬9á3æ¬ÂÛ6 ×»·kJš‡k8O§®âE¨œ‹€`Ó3³hÏáÓ´eßÑúþŸÜ§ÏþßËÑÿ÷ÙÛÑK¸xž ÊGj|”7z˹ãƒ%K2ëÝZ7 Õ{wjNÝÛ6©ƒù¸oç–â(-*½}¤œ8=¹Ôpþø…?4¼7ý¼l«~øÔ¹Ù¦ø ¯{9úáÙoGÿÅÏ­>¯ö멞*@À›R´ísþ³ëÌÌÁõüׇ¿Q&í /bßb·©_æýÎÿßD/p¸¿‰X¸óÁœf:ž¼ý\2ÇÀʶàü7€ÿˆûò¢™cöÑrw·ñE¿ŸŸƒýSDÈ'ZüùpÁpèôœFz‹Wf0³òoÞÌÌ…Ì@sÓ.²2æy./„ËkÅuqã»×mˆ™r¦==µK½·Ãóǟ蔫{Û¢Á¬†ô˜˜e4ádd1Ñ€r­Y@ó1\Ÿ©²Nò\< à|îaèã–•›}Læ)ÏÜ'¯°«ÛÃ,çyÛz=·éz,»˜99Ä›÷[ŽçØÌ©tä|nµè–»óîQœÉ^ù$zÒn™—½³%‡ÞÒ`¦AöÒ•ôŒëP1bNr%r`®vÎ%ÆéŸeÎó݈РºwpO·‘õKxE=®:y–æ,Þd>˜Ìóƒù¥Oßz'Ápðß@©IRCU½:h|ïæÀozíZ~¦¿ßÞÏÔ,¢R%ÁÕ ôjTÛǘéØb>s>YÏÍÍÏ_rõññ·;”ñûSQUUTV±ˆ\x!»¼´¹ w²§#kyŸ5à³ù:3áĶ×3¢'&Z'à°ödÔÔȜȄòJ,‘Çø7ÞiØ€^ŠÏf^¨–¬Œ±X,_ò¢ú̈¼fï<55‚cM ÷HÖžy“#„¿n|1:Z7]Èi¤¹{æS“D£w,c:G^—§¢ß‰pôHšþüóp ^"!x vÅTÏäg¾`˳KmÜr×6òu÷OTb††Žšc+“ÙÐÆMzó9æµþÕ­M}̈¾š'ë@(ª9dç˜é«ùkõ­Ži‹yâgÓ¢Áp€%¶»`ÈG@š3l¶¤‚ãyj¯MÿhWãðö÷ÞÜCóöò°YŽºY=ÈÊΡٿ®ÒYJª™ssžüü7gpKì2ŽúVOÄT­öàrÁd°ûRÁtØO]ýž²ú ¤ašÉ£ã¬)/Ã8½€xcêY¶G›Î5ž3郂ê¢L8jŽ­,5*ÕkØðÅôÏnmëÜÕ¿ÂLN™ÐR‰+0¢oçŽwÆ<ÿJìWÿž¶ˆ /‰áPã£Rz¨j ©Àø@ÅMY©Çnóððh‡¢ªm‰*ÝxyzÐã£nÒþX³S?{þbËÏóâ§ÀÛXIó‡3ª£òTT4]¨´Vÿ†ØnF¦clƒÀ[¹ïLxmêîËA³æ^‡U†Y,úd¶MHâ¸ÅÛÎVÝu•1µÑ£ÿáË‹„Ï :‰†Ú¡²t!ÐÇáõjë>¾~³ ¸ÃŸ«Vqý®Æ‡ õ]eT¥¬ãƒëd:´g]žC¾bF£2æ¬Ê€A•aVy ÛúwÖyÓßÿù‹fœbªÏm`¥n)®u¼Ü^e†b«N e'5‹²t:ʇ¶2òÛ.¬q÷ÐÁ¦âZÇÉÚ_“¸É£ÿSf‹¥l4”ê”+t»së€>¾oHO76L kԵݓ\š½ƒÎí—˽ŒãLª{ÃFao3£¡\b»\o:­BþôŽáÜ¡+‡ÍŠÊøV9­1*c…€BÀñÀ/«ˆÝêæC 9ÒvÍÍt«ÉCëîåëVŸãsÜò_v‡îøRUŽåAÀÙjTø@¸yxº?¯Sl ^Üîvyê®Þqa`øß¾y„¾ïÈi0ЗdY§°ÝÀY’‰kì\Êñ¡uíÚÕíæ{î{ÉbÑ€HÑ5„€¿Ÿßmßþï?Í|ø DÉîP®¡æ«¦*¥D`FTÔiNŠC‘£Ð5á:¸¢Ù:{·H{ðɉì¦R¯Ïîm£QÑÞªfï£Ïy7:ôŽ¿ïÁUck=Ôø¨f}êÈê–b|˜ÚõÒ7)5óÍ£§=W9²i*/ •¹ °ðç{¸¥ggOÓ¥©, …€BÀ–Ça}¿¬¿ùÇÂÒÍÓÇ{0»%Ó9ŽFYëV©éùÃF¬Rˬ酡ÏÑ÷~µá‹[ªCH†£ZWè«mŽ[t.)•Î^H±õÈeï•f|˜ÜÝû3ª7ªì²í(MŬÝE§/•&©Jc@à·µ{jÕ¥ó#|K1\Ô¥B ”læt8)ªgªQ n]㎠îªûâÏ]¤¹Ë·Ñ‘øDÁlDÔ«CwÜØ…Xí«J;ÆXxìñ3Ô8¬.U7WŸÜ焾ÏHK‹äö HP6P‡ÀáãcÒ~¡äËiôú£wR½:y&`<{ëKzæÁÔ¦I®jñ”k6Ócg8(eDñ‰ôd˾c"§®mÉqþêdæªãïX䙫Þ(Íø`Ž´u@_³¯—3çªRCôƬyı Фï{]KzhxŸ"÷qãiþªT7(€Øq‚Í4θY]ç #¾ÞžzvFFc¾‡þÇnÜhãP¤P”€Ç?{±„$ê±B Rpæ;×lèëL J¹JJ¾LÎYJ`0žhùxyÑÁg¨AHP¹ótô‹XÌÎøy9½<öVªïUËÑÙ;=?ôýù‹ø 0o9ù…ºÔøøqéFzòÞ›ó«VúÓ¾# 4oå¶Ja6ìÕêáÛo¨–K°Ƈ{©èïã2óǸ;ûâ…ä –sÿÚJOß3 ¦ÉÏÛ^÷Tú³ê>oHÀêÔòÓRRR±óƒùs6*³Á (RTøVãÉsÞ/õ¿«" çH—Ùœq³)v®Mdò÷öðpɺl󺜖AÜÑxçT ¹»g“Rèû…èø™$ªàK·ö»Žºµm"Ò}Ç÷ëÔò§“g/Уg¨~ÝZ4vÄ ´`ÍNÚ{ô4µdéÛt¦°ºA´ëà)úkó>bu~¾‹Ø»#Ý6  õêÏŽDŸÏ[Mþ¾^tÏàžâ÷æ½GèNõÈmôÑœe”™•CÓ¿[Lî&M}r¤H³a÷aúsínº’žÉR˜ôà-½ Ú ¸Èè{ö‘T£Â¸‡›+Œþ]ZÑò-hÏáx›LÔë~\²™vÄ ww7ؽ- îÕ^HÃ0Г>þ™Ú5 §+™èOwê.v´ßœý…Ö©EFÞ(zäcfn›rÔëa};•8¾"BëPRòZ³ã =qïM…ze~üÃ2!cW¡ôÃâMĶQbwãmÕ¶XêØ"‚–lØË1OЭ5õãv‚N½H‹Öï¦ýGÈÓÃܹ²š’¬c¡‚œü£˜ñù‡»5ôótwwf£AHžd"åJº@¦ah0ä3PcûæÏõtê\KÉéþ¡½©ixH‹OHX–yémÍ£¶æ ämS½:6sæ7 }½ˆë\—îØMü®êÿ|<=yhØe1ÎF'U]EU¾B &"€ùW~§µ1ϾÒÕÝÓc°f2Eò&Œ¿épi¯È°~…,úÞ’9a1›ÿï½)Û¹š˜3qH&¤Ô5ŸÓÃlvÓgOyeK©_²‘ÐYÌŠÂ@u×ÉupŒÙ¸Ì5k‘`âÂXÃ!Ž.3>°Ð#øÓÒMÔ–Õ¦àëßH«·bFä43“ý¸¯Òéë?ÖQçV‘¢?š7¬'˜Ñ±#®g†Ñ›6ï=ÊvdzqðMÔ¥W>ú‰Žž>/$ Æ4Ækì66äÝml6 =a¾N uiÝHüf³´#ö„Ë2¼.ÀŒ®1Qå]`|äš-t7ïä¾6ãZ¶y?K-Ú \å{™Y€” 48@è‡íŒ÷Þ¨6÷CÂ…d‚+WPkfV®ÛMØù>pì´`^ŽÄŸgµ›sË#Œ§ÒŒ¯Ì¬\Šy|!²²$7f„¾ø}-K»rééûìHËçÆ3$k°9Áî:vÀãx䜡eeeìnÜ n¯ª«[éJS‰¿ç #²’õêØœÞÿv1K¦. ¨1{ã éJ;¯Ø›lÍÈÛzLa>ÚÎN`s´%w¬²V¢ò©L²b6äÜ¡˜ÊìUVµD`BÔ”¨øŒ˜ÉËKh€ü.ÃûýO¾080¨ö+ a °Ç=4Wµ½-¡m×Úcñ1OÏÄFçiÚ²ïhèÞÃño…ulúÔèf/ÿzú;‹¨£–NÒ¡éá’I1=‰›øC!Å$«ºÛØÝMË€ÍrQJf騡Á ³ahm–8\)H,?è¸áçãÍ*Sµ vÅýX-+‹w³$mA°@õaÕ–cÅ0Þõ¶Gx'2åJMû|ÁÕd.‹2¯±óÆ€\añ‡v™ñÁ»5̬udÕ´ÔËÀ€Bý|8غÿxÞðþd‹šE„‡»;9užbÙp¼;çÅ;BÇ}æŒ&ù¥_š ”w€¤ó“¿KÝìQ#Éûå©f±ÄÔ¶i­Øz@Ô Ò9,Œ«’Š¢Jß¼ÿÏ·¿|ëÔ£UY¿Ò”wü¬P›ƒÄQl9â“ 1¥O¥WÊ3X©>šÓÏl{ò3Ü;™îÁ’TŒWW¡Ðà@Úš‘ÏõÁ· !9¸JU=.‹€E×¢ò+gÙÀ¼`2øì1úù—Ÿööñ{³A½ 1c#Íu&—EÚõ*†ï6ÓùÐ ±ž³xS½Óº>oì‹Q¯~ñϘ¸ÆX Vš &pgœòìŒ2*œgë_ÓŒ)¡Îd¤Úyž‰Î^L-л>wñ2µg½|IÖÑЭÕod:y>Ç6 P{‚¾}FV6…‡äI-°«,‚H‹Š=‚>–˜'î¶—ÌÕža‘ ǃ\0Èß.QWØa¬Ûyˆ½“m *…¾ÁØеµP[+©¢H‹ݱӉb§{ô­}„]Ä ¶ ña&Cz·*ÏøBÙ~<À>çÓ_WR–¶@5ª8‚=†-Sõ ;€äífns‡æ m%«ì{ÆñáRã¢4@xç;Ëz–=˜Iɧ|™)yYªñTÚy¥<ó€uÞ°Aƒ*Ýæ}GÙþ,‰fÛ£VA*ùâA®Ï˜‘÷¾ÏÅb|È1RíÆG%æŠSä!  D{hào ‹ˆÏ½|f"7ºµi¢Ñ×d=_ØËH=s] yñÒØ[ݾš¿VßzàØÛc'NÒ¾xw*P¥0˜¼EÄ.ýQ¸¹w{¡î2{îJ:ÁöPyØs(ž  )F-_a`›ÈL ˆá" ¼òô£Ï_JåÝó]bÑPs>C­.5¡ê²ï(6òò†¦ØiÄî:v´±p* T¼þb•Ÿ¬ì\Je)ÇáS¬¦ãªtUUÊ8&Œ×.QsÖÂÆaÛ]䚯J¥`Ÿ±iߊc©v¦/ý‚-ÆÍ¥Ô4aŸ{­›„ÑVVM b§ÐÃoݸ>%œ¿$sx*ïø‚ëT¸¾…  ~“ ’6‘q)þÃXnÎê37tnÅÒ¸ZÌXf´K‘…c“ØŽ-Ãɹ5 ¯'ÔŸþ\·KŒx¬ÚËj•¶ÈÞx²•¾¸{öæ[ó†­| 9ëÒº±pH±lÍ(Ùz§ îɹ¥¿'U€‹*R!Pð÷$ ï;‹¯àë`4¹«¿¦Š@ëzï¢?ѯè_Oï©÷>þì-\KéxÃés«í­O‡âäºjT‘l w§?²að´ÏÿàE=±/ü@z|Ô@ªÅ ŧîDÿû} ½öÉ/¼;í&Œq;µŒ,:z`§ùµO~ªVÃÙ³•—güP]ˆeÃá7gÏã‹õïÚŠ­Û#Ê:vÕá iÎâô¯çîgû†ìéª ýξûZºY0#Ø•oÞ0y)WÿR1ƒÙµÆGçÖ‘Ôªq˜`,$$#Ù³Œßÿn‰`ö ¾Ý|0Xð­eilpÚ5‹cÒ‹_9v‹ôþ¦5˜=—1À¢U«ŠŒ¯ìjÓÙ?Æ`Î(=a™·b›ð’æ 6ðDUÅãÇ8>p-gnˆ”´RB5nüÝxœ¬'ÄnaÁßnÂ]EÞ´7žŠ$¶sÃÞ<`kÞ(.«ÞZÐÆ=G„‡¼âÒTñ})’ç*®Ž*^!P#ÀÜŠEˆWddËÚõDL­-$5¢uª6`‰•–pá’ÎKÒÿ´oßóº½{7¥pBÌ­p8ŒøròhÔ”•x‘ƒÇ À9ŸÀ-ûðøðĨßÚ6oÔíùÑCå3—<Ã[‰¹ ìn[\Ïb°¼ªÌl CŸñ¢ð?/áq¥šMÃ^H)$b]Ôë/ë݆ËìaÆÇÛ£ˆ˜õûUõûßìJsOìÑÝ_MŸö×!‰ˆ`(ƒƒj3>¸®df½v¨¿Á>Çz,@ê&¤<úî_¨[ii×Á“ô-KD^;\ØdeçÐÌ_V ¯WÑ·´Ù8,]1ã#“ ÀGÐØ}£oyïµ§¾X]c»,{do<Ù{ÏÖ³âæâæ cð\öá÷Ki*«esàAW#7߆7rÎñq‘Ë|`|öb¾?üX‘B@!`çÑ`”üï™ðì³µ‚ƒ_|×iÒÙ‰B¯æ"¸9O¿råÍo?xç=niXd1~tRÌL 1kjÔxœËK• Ù(oÕ*÷½âôÛQ ìZ:‚Ø pfÁ˜oqŒÒW?éãߘºv0ð†ÀÙrŸl+­{Ž_¶ò¶u.±ª TòN»HÇ.÷º¶ÒVѽ©FhÃÆO@í°:1%1S{ãI¦)í¹¸y ¸yù^dÕ?ô=¤p7rìWd4Ž'œ§î‡´Ø²|qWB%Ý@ç)RT ̱XÿáƒæW+¸ÎßÙ«¥®ŒÁ+juy %ú{ß‘ø \çù€Tæd£¢L†Ä¤Z¨(ÈÊV׳§‡‡pUËʪkT½kP¼¹W\òC0¸bk¬0|¿©G[Wk!æ&qì«]­ŽÕº>ð†‡ ¤°!AW¤y+¶SÛÎ]ÇqÝÀhÈC1®ØYªN®ˆÀf®#a!R`«qÃÐÛ8‚§V—=ªŠ¥~þæok½¡÷ŒîÎMuºí†’lT€‚^?E W@`;FÀáÂ$?zòìÂU­¾Uƒ£®LL ª‰X¥ŠÙpåNSusX½ýE•Á&þ¦ çé1€ÿÆtÄѰ‘Öa·÷ÁÛÓ³ <€Ìx;S”žñ¬ j¾ÇXúÚºÔháÔÃZMݺ>5é7÷7æV½vHÈMÜ®-|@ÿ·X GEÛ®˜Š"¨ÞW( Ê@L†b4*iUFMFLÖ~‚Ùp÷ôŒðñÒ9.ƒÃ™ l,\¿›àú.ÿ¡: G$pß -ûŽ1³a±ÉlìcÇ9ŸÍ[Eïÿãwôâ¥2üo¢9¹¹Ç(Ö&çÛ?7ЮC§¸~Ô€Ã! ï{uiÓH„D@h<­‰„8è÷´T÷HnÆÆD^0.'4X1NUe©PT¹ ”ç e¦^®`!d<ÔØ¨ݪQÉàoHJ6 :ãíîáTËÏáŒÚõ«i.Þ°‡FÞÔ:² /‚óæ{_/¤˜Çï.Ö´nDí_(?£<¬ÙA!ÌØØb6vÄž¤{äy e¯‘‡8„€L·ïHÍ[¹­Æ2Àýž˜tnL¥ÆÆBÁü:!:¦‡Ùì¦Ïžò ¤å&Ål”:õ¢BÀ6ðîs!ùJë[Û©s;3ðjdË‹šcJ¨²\0ÙLxUV‹2\ƒû£ŒH8.9q¹@’BÇe®rR\{àïk?¸ÖÏÛÃÃá̤ Ë6íãï`ká€0Ãü#wö£ÉÿýE¸Œؽ n³dƒ„÷=‡ã©~Ý@zø¶þ,ùîÙä £o>v—Šœ½Bßü¹ž›$ ÉÈýC{]>s!™¾_´‘c¦%‰r ų”-û o¢v¦û†ô*+ (BØ_Û¦yÁš%£q$>Qı‚4fÒÇ?³[ûpz€¥1g90ó÷ìÍñ8—Q›Ã#ÜÊ! ¤úâ^E„Öá8KWhÍŽƒôĽ71¸nZa³pÄÊ OŸNÍi@·¼6Ÿ:{‘±Ôg?KoàÈÃ¥> 9ÜÐÛk§ÌAÿ¡ßMšÉŸ³“ãc£Yrè--Ïv|P¡eüQ$ã2¾ïRÉáUeÂÔ/Ä`´®Xû¾Ç3¶¾·~tMýŽåÀtpµZé$OèãLÿž ¾5Ò¯Ͼþcñv±×Á ÌÆJ6¼. U¤äÿùokèÉw¾ÑÀKS^EÓT´¾¥,_2–¬¬Ì]Ô-åkUŸ¬2û#×l&|œ­é“ŸþbS[­oü^ËÂ)³ø‚›.}!bñ@Ç\2_¹tóUååD`BÔ”8 ¯ãoG2ïX`zä3ó†$޹Ć\o·Éd+s­Ë‹ûÚ”ÀÁq%íf5¦@ºoh/JMË¢ßX¢ÊÎÉñ©àÉ„xWpõÿÌC(œUž¾_´AÜdžÛÇs–±*T«hõ¢¾[Š€µˆE„˜C[5¢±·õ¥&á…¿%Ù–LÊg¿®ÁXEfü_p-?V÷ªGA~â½™)BL*”‘Áë§ÑÃûPSŒû)¿‡u(•ÃÌ_½Ce~C'ð7Ðχ:·nL9V'vÄñÓÒ-ÛÚ&cÚS£˜Ùi ¿8×NñÐáÿ‰ùcÁöüªéî„£‚T£˜ `µ3î„08’ØÀ½czfžï{yïZpÞ{y—ºYxˆˆCиAˆhäÊm±´|ó~¡ØŠË-×wdŽ9ïÙú]‡;‡ñ‰É_ÓhXߎtýu-iÊ?×î†Ràda$…𮃧hç QÞÒ{…J D€·hýþñ‹‡횊ò‹±A…œ‘ƒz°QÖ.ºÄ~ô»2W};ç…AüsåjLÿn1¹óï©OŽtNs`-€A'ÞÍØÎ.ÉtìôÊ`¯M­v5lõÔ‘ládo¼`7å‹ßW‹ ¯QX]ªÇ‹$,пš¿Ž°D QäQ·»v×e-Gæ)Ï;9@Ü*ëÛ‰¾áÈÕ÷ó® ð@åÑºÐø‘b)œÁtT ªÌþH8ŸlSÌ¿hÝnêÃaùQýmåvÚ{œÿîs©YD=4¬˜snä!ÒöêÐÌ¥±}ë;fä½ïs%厬<»t½Uå.€¦ç}®V? ÃS,6óî:ð,ØA8jMøfF ¶ðb^T™ð˜#Å?+/ç¯-¸Å~|b²¶‹›øþ–•°Nxs¼6ÛÅö%{ÉßÏ›ÛØ¬‘›´ õnZ›.ê‰õ'@KÚs(ž®ÛEç.¦ µ0ÜÏ5#vñz/Œ]Ï€WoÌîÛk§c!aœÇªâP²ˆͼ*2³`±É¢1,6%måëëZ5–?ÅÙžx â°y+¶‰hÞcX\†àW x JªÃï7v£Ê^ÌÂW=ž9@tæÁìVt ?a¥t‹ÎÀh¤°4$};·  £ÒifdV3S‚¨ª7È@§ùÎÇ׳Žá(fjó-Äo’ì‰ØðúíÂu¼CÐŒnïßEè‚™³Ñ½]‘|êC¤XS©/èwÄ,nm;p”º°5_+š]\‡“½ñò׿}t„w\îÐEìTlÜsUÊ€]•Ȱ`÷˺áy‹¿ò”cÝ_f€yiß,BL`F•šòŠh]hüàÏGµ¡Êì[b~uŠ79"êטæÝ:躹§ø{ä±”îE²nð¹‹)”ËóW5 |凰TWUQ!àÒÿžœöwä'ví­U×yá}þRª:H”pO®èë #y{åí?Ë’ˆ¿pÁõ¤ ,êËC`îäMÀf¬u„%¶Ò ÐYCçØÓÖ’Œ®sy)*6´!]yï¹û豑ð0{•†öé(´LòÆ8$Ñ2Ö‘½v^}ÛWy6qÈÐ8.X@^V5ŽÙÈ͵¹ùªTP+C1•‘쉷3vÕ®ãEjïŽ-¢/ ô;·Ž¤Z,úƒlÿ±±°…(ºƒPÔbÏá!A”ɪ. ,ñÇï uX´ÆÑiGì ñLþw[ÿÎ"ß~][‰ü 1 ¶A½Ú1§Ÿ!Œ†J#bC!ƃÞ"º˜‘ÁŽwCô Æ¼è€h°¦RKîkô9þˆA`6»¶m,Ô«d›‹ëâp²7^b'Pw«Âðk8K°»a¤A=Û‰I¬n¿ðt±ïHñýa¯cžØù9pì4]Çã \Û¦´yßUµ˜Šˆh]`üÈIOžMwÉëÊî[b~d&§¦¸”C‹9ë0«$ÀîehŸØ…²Šž¿t¹àž _`敇 WSUM!P­Àüê4ÂFÑkw¤%¬±´Pš5waþº¾S‹‚²±¡»ŽÓa‰-4Zòú*ÑFj^OHlÿdIT§c/KAMY³ÄËÓóæÊ¹¤Tþ6&ãp<«\‹`­Äí0$ H‹ò‘4RÂêÖIðʆÖ]b`]·„¥hËrvç›|9­Á¹1oH0RYU,ˆ Éa»²dããcñ~sfnnèÜJ”éÁZ {í,”cÛ±y‹Üœ®FeÖ-i™99øP8½1hTAÚ±ŠâÁ“çXZpR,ðÝX…ÅHöÄ[H™¿;ˆk¨1áC޼ñ©Ç¶'ØÙ"ñ0FÆÑ„Õ¬|¼<éçe[×7jOÝw3²nߦ}¾@üÿ®R3àŸÏ¥7̯ƒükOÄÕ/Q”àëMYü^UQfvŽn¶˜ÓŠ+ßã vü,×`“„$L0%õ‡L‹sqãýï`H%a×XJR®¤³ºËF:NL:—x1(wadz㹸rŒip½õÀ11æþX½S¨Ã`BLeϘP±+TmUŒ;ã¦á>Ü—¥”¹]Ú4vú\eqY~»B`läŽ T;áAåWž¯Vo?H#úu*ð S‹?€ø–_L½Ba¼)âªÄs.&/ˆ_ N'œ).ŽÀ0Þ05ñ¦ÇÒû„=lüN9°E¨sbáÿ5«cA?êæEZ†ï4f«‹ƒ«£Ý„»Ä7uìˆ~ôÝ¢õôúŒ¹BsÆÖ*ôïÚZx©zö½o…E#“ÃFì‹Öíáµ@:AE ßñ[®ï$ò†ÄdíÎCôÊG?±7ª^Ë Çÿ~_C¯}ò‹ØäÃæb'vék‹<ÜÝYË¥ƒPÍ‚ú|ÿ®-ŦL u1¬%¡yãs¨¾ÂìHŠk§ÿ%³rîY×âQ€Ó?à–sbrJZ¥1z÷Yr[ täßïèW+ìèÁ^^&þm˜Ð|ñƒ ¥—l‹Nœ¹ ì(`T]¿[öÓì7¬Tëúõ½®…`r¼ØËÀÓ÷;ÚÈÇ›™Å ú–ù`»°ÝŠõ.IiúCbho¼ÀÜÝI2êpþµi?ÛO$±Oñ‘‚ xï«…2Y‘³½r¬Ce»ÕÒ.}æPÑÂd í'ì @Šh;°qÈÕÆO1㌆`6‚ëÕ¿uÎ’M3¨¾Ë’+ô>Ò èÃã ¨_—VlÌØ˜½¤ìóT³ˆP!íLå+ƛԫ‰]ð¿-{¦·êعnÜî \=Ép¸`MU•Õ̳N#|woa‰*8¥Ã`ý-Æ[Òøx#¾\É5–¿W ¸ÁÍÈÊsøƒÍ]IÐ6Á{o^¿IwðPeCÁ^ ¤©|ÆÞ8Ÿ1/<`õ¨—.ÔãeÀ¼Lw»ØÔƒê•qñol‡ÌÌÈ–î ÍØø”´‹í-—óÚñ-¶›Å&$.3YÉÒC‚Ù°×N™GegM®¶Þ¹Î‹K='++árF–&Ý}Nâ¼_yq‚ŽƒË4ˆäŒT’x˘ÖúúbJÞî!ˆØ <pkñ‡±‚]žöîØŒº±}Dp`ž¤y€K†'ƒ¿Ø°^kà©:Õe¥ŠˆØ°(Æ ‡(ö%ò¹¬u(mzô9ú>;#]6TŒ‰ü÷:>àÀ0Ö‡ •5Ùëkœ Õ²'mݸ/òOxpLcpI8ò"»;°Ë„CRYË‘ïAßõÛˆ@-æv¶ÁqÛá´l&lB"^G‹h­ë[ÑñSÂø Ç•Ë){ùoÅÍÈÀI\å\Uýa-æÇ $çØ<(þÜ%–„ž›Ò¹„4^<Çc_ö®JPMûßüµm»ö„¤F ‡«V[ÕK!àJðN(á¨r‚ݚѰ®”\ÔãÛí€Íû޲–Ip¡…:Þc`Íȼ ‰" yL5£!Ÿá\\^x†÷¬ëÇ4áiÉ„º ù4c.ñÆ֑д€×T03F²×Ncºêpí¬-p,$Aú¹ø“kƒƒŸÙsø´pk–wÛùÿÃhÖÍM£®mšð@)\^Iâ­Â© ÿ‚W+Øi¼1sÛuxŠ…¼W ®Óˆ¥ ?p N¨Ø@úðÒØáÂ΋ÂßWí`cïÍâ9<@dV²'J,)¨ï `‘""xÎY¼‘þõÜý…vJz¿¬ÏÑçîÀÒ-ØÑ´hX˜ÙDÁ°»)®?ládO La‡ñÖçóÅâ "aêõg7~_ü¾–žy÷¡Þ6¨G;JÌד/k9"Cþo3™cш1n$8ø–A$\­1/[×¶ê+?¶Ò—tÏÎøó‹ßÆLZ¢Ø*)Ϫx^UýaK̽bx"asã{þ;‡½W€¯ aÇp% ‚w5\ãïÄU Þ½0œ8·žëhd4 Ƈ«Ö]ÕK!à ÌŠ™ü¢+Ô£¬u€ZœœÀÞ.qkAÒr–mK¬aû“”ËBÂ|çÀ®l¿Ñ²¦5µ =VËð‚û¥¾x4jÊJ$æA=ç|‚Äî°mVç‘Wß\Ë‚êþa™¾çàÉäO§½~'¿‹-}¬‚`‘ +->\b|×Ö8•4^¬EÀܾ’¶7 e-ÇðªÍKˆháíefr"Z84ø{Ä’TÕã§ãÃßÓ3 øo'®iÁ\û £‡ºÔü!q,éììþ€”RîÂÍóôo±ÚÞÝ"Hêf­"wÌQÁ÷nö¾Ò=ßvIm¨Šç~¿Tß{èÄ™ÏÞ~ãV.ÿÙ@´Œ LP1ßùX Â`ÑáÃv ‡öÐ3/¾ß©m‹6ÏÊ?]KüûëE´'öèO{ˆÛÄDނ͇’³Ô¨°ó„;عÎ$|¿÷p¼vÈF ‡¶¦Œ™'Þ*M6ÖŒ†|'‹ÝØBg®m7î9"˜™úù»‰2 l;*Êh ¯òŠØ`ÓálF}>?}üØï\U0X Hé†ñ\å㣸þ°Æ©¤ñbo—©â¨¬å—ñ¾3E´Öõ5–[ÚëÒŽììË9çNž˜Íé5¨¡UWrfÅüˆ •©µïG’µŠœ[4¨[Û¥ ¨pkçÏ$ÀÈII5dgª³B@! ¨D&DÇô7yZ…ÅKÎb6…`4øœ»jÞÜ9ºÅ’ôý¢x‰ª©„@|Álìóùo«Åà{ˆÅH–סÑ׺Ù|iíóæqÛ%³!ÀqÍÊÑÞÜ«ƒÑ~8g©°#‚ˆî]Ê8>²—ýòó\ÖQLd&Õrò¨ìþxˆcÁ Lq„(½Oßsq«ü>ÆÇ7®·XrsÎüõó?r…Ðï’á¨òú© ( kK½¥YÌÓ*Ú^g)ìJ©>9—.%¦ÆîÜú®Ö¥Ç[_Í_«?rWÿj©QØp9yÏàž%%«ñÏÑLjp¼{ÓºO._nšB ‡\0€ÑÀqMno¥ôóq¸"•a|€9ÍNOO¹²kËúWÃúbµÇ{Xß™›$NÌ•ûÃ) ®@¦ö²°w7mÛÚU§¥¥@¤/7)0gàÛ¢H! (¢¦ D²1“——"¹C“@í*êÖÆÕ-¤2ƒCŸâ¼“V´x8”A˜késEóuèûšî>¡Â™ðÌü0ÃGA,øœ¹váü5µëü¯ÿæ’>fD_ÍÙª<6ê¤n9ìHb!É1´ãqûçl^Á–ðy:Ö`6à«ãA.Ôø`0®%*ãøÀÂã%‹ôÍ-ÙÆÇñëš×ϼmø1ՑḖúº¼må…‰åèý ¶­^~`çºU»óû_ÎE™ ]ƒÝWõÕ¯+/Pê=…@)°èZT~²Je6vrPå?¯iîÚ«5­ü$ÌO“p¦b‹Ö³**¼‡B%.ÂáB÷¾!ŽÛL†C¡Xóv” \Ó©Â̆¦kp»ÓÕPø0`ç ,6ÓþüîËï‡Þ÷Ê}&é’~ß^¦šÉšÛyÍtð¡:‰Æ±Ø}?-ûeÔ§|ý$³!%ÀE pP9ƘQ9`wcÉóŽ‘ÿ÷Ó¿Þyýâ“}ÜÝÝÝûïšhfNNNÖÛïýçëè)ÿÚÄ NæsúÞÈlðÏB„®­…î¨ …@ZÕHwn\ìÚˆGåj´`Í áp¶˜ 8B,¶ÝÚp€½tê\ tl õù÷ó÷ ×䮆3êSafƒ+u‚4=ô±iÓjòÊ+— •”‹¨ÏfƒÏ^‹æ|óC÷ƒÎXz^?á__/ jßÃÑφ]‡YbÑ‹:´ˆ(@ïÌ…d¸Þú‹ÈÒ„ØÁu‚Ó˜ÝÛ€P&˜,ù]Ã1¾îg©¢„#°t³ˆz"Í’{i+»¯ùá[EDKÇ÷ ’ÿŸ—m&¸ü†ê¤pûk¯<‘a5ù¯Â̆›Émq®%wª%Ý2œÛüU»ña0.Pžû–•ËÖîß²)¶Ï-·Þj6çæ…j ƒ«øxéAµü4oDzV•R?+†@fNŽŽ¨ðØ?øÜ‡—OÄŮذxÁ’ôô+Ø‘„‹JX0Hfã `¡À× 5>òp¨Qÿ;p|Hfóv¸%Ãá±hÑ_-~¡sÓÒš Œ=~&äËù|ÉÛËä§Õ ô×þ~û …\S#ˆæ× Ö‰€šÖ`˘J_yødåäêÉWÒuî1äæd]øß?cfæ÷1æ ̘?ÀlÀÕ-˜ Ì„o/@Lîü *¸©. *E`¸õäø=m›6L¤’Ù@¼¤/篞ðºràåD. F£¸ûX|#ŽÑ°¾y/0“àÁ ü;Ùmw&ÛR F…$0#I)WÄ¢÷R9ÄÀêíq"vÅf@æ¯ÞIxÿ‰{‰8[+¶àz5A˜ÃêÉlĹQXˆPšÉÌÒ(^ð·3Ä´Z½ýØd;¢¥¤¥Ó׬£ÎÌ,ÀÊœ¿zÁÍ<`°`ÍNÚÆŒd66î>ÂñÕê1C‘W_žE™ˆ+‚ †ˆéä_É^y…*íâ?*Ìl|2å•m㢦Äó¢óAn«5³ƒÜ½Æb†Â¸3-í²eéÏßÿÄ¿ÿlÝ©kËÈ­Úûøù{úx×rwSꌋËG`ÏÊÊÈHɸråâÉCqûãvo?Ê••Ò üõc¡¦×èw)Õ(´Xàûj|058> R£:¤˜·LK~ùóÇÂÖ»·ŠhÚ¼¯¯_ð?ßÚÌÿê¯<ýôw—SR ~FE«[¿AàÀ;FtssËŸòæß”›““‘Ÿ‹Z•¨8ŸÆð7›ÍYÙY™©éW.'ž<|hï¡Ý;q‰`*ÀhàÀ¼†ªsR sEAŸñ5þ=Ƚo~+R(ÊŒ€Îóu&6ˆøMÌ}&08 Õ©ëZ7‹qÄ’‚Ët<ËÛÜé'Û²°âîoà…9‚ÕêÙN$=›”BëY2f£4m™‡õI/$§q ÀíBBéÂÔ©åG¶TùÁ¼òðpú‚£æ,’HLàmtïáS̬DpPÔqÔ¯[‹¶3ƒ5„™PfV.Ç9V°ÙÕƒƒü®Þ+ÔÉ/¦RÂùKôÀ-½DZã›XRƒ ÔÖžK*ϘGY¯3³st³ÅŒ9·xÒµ¸â–þI…™ Q”®} “þîøI1ýfNZmU<>`Ýä‡å=,²bwmÛÅG,_c! 3L _;ä€óQTqäÇý‡C2’è[, °8 œ‹Ý•äg’äXPãC"R}ÏÎÈSŽ30´Æ¹A0!±;¶ìäc??ÃÜ!˜ÃY¤¿p6!íÇ|œyâãXþ–ïšTz§áðšÿ1oà02òýy¡ˆTß‹®s2m"?W¤P”ì̬KÐTà×+¼ÖJácq'Îð">€ÞýòOÊf•#00ïѾ]L½ÂªSu 1¨¶­û>šÆª–F›Š†üîZVOÂ3k‚Áµ5ÁÓ“$_/¡’Ä›\~ÉM­ÅÑËŸy`0me©ÄOK·Ðì¹+iâ߆ÑA¶O…$fëþã2k:ÇÑÀ%AjaŒÃÖ“™¥ß™É9vú<±ž‚ƒX²ÁÚŸÅä“ Î’1Á|bdDŸÌù¿RxG¥w<>²/äø@ßÉM ÌòÀ\"™ 9w ðž þÆøZrÎÄë…“ ÜÃÁ@*R(l#Å‘ø›ÊLO;•hØtJ0ˆsdM¨?Þ;ÏjeFr3™J,Ϙ¾,×èoô{vFú9~¯`~-KeIëf#:úáÌG£¦Œã‚ÿ¤œó_ð¸6†ŠàZ2òZJ;ð‘Ábê `6ŒÒ ÉlÈ3?VTEÈþĹØ3.äBgÜG#|—/m’x(¯Õø° •ËÞ”}Œ³£Ç-ÇYƘZ9w`A‹¹‡¼ã€ygyO¾±‰q&³ò=•þj:‰½œ70W %ƒþ•súEö _rdzŽÜø¨©_ð%ô*†áÛÃgE …€ fÅL~ÑÆmü âé'Æî oÒì^8{±µÎKVºÿasбE$Ý> KÁ µ9þØœÅyç>“Z³á6 Á—nÚG]YÅêàÉs¬:Ô¨ØûíÙVbGÜIêÈj19‹kÎÔ"2Œç¢µì¢¶}³pZµ-NÜ/íõƒkÑñ„ó‚£4"Ž%—XJÓ”Êgc÷ÁÂ&¤`Ÿ±ißaÑ<2”¼Œ‚½àÍìÄ„ëK0<‡«[[£öíNû@?º|™ÕÑ"ËUž­ü­ï¡¿1Ÿž:rXzó3Ž ëäþífµàA½äÑÉ1ÿÐ-útþï>-FÂá‚=>,øX€á°f4ä€)r!dÿÉ>ÄÂúÒ¸P@ÿZ(ðo{„|%c"óVãÃb®ùÌ™ãã‹_YÆæë |—äyDJ*p Âûr¼ÉqŠûò•Þ9ø_ìGô¤rÞÀoÉÜ™; Ñ£Á*»£4“öܬ)“—pzE …@éÀ¼’s¨eïÖGzšÂ^%™Ù£y©Êð?ì)NžI¢á};z«KëÆ‚Ù€Ôc@×6Bš~Y¶…ü}½y1݈%!MmÞ¿opOúü·Õ"Ûz±íD8lçÙkÀCT¿.­„·§ŸYÍi`÷6¨»Páv~ôgé»ë§gßû–ÆÜz=õéÔ¢ uN®ExÌ‚TLH›&a ÀCœ¼ÿÝ,Ö… Ê„‘í20†ÿañ&jÈÞ·ÀäØ"¸þì×Uôç êÅ ˜ò”g+ë{ð"ª[,ÉÛÖ,—fœoå±~¥B¿Ë=°Š+•Žgu ½Ç‚½ƒãi6x}yÈ]Dy–÷凿¸¢ÔýªA±`¢âk,Ö°@g Zyðe¹HŽœå¸gùLrAëô—*k| ÿ%S`”ZÈ9MŽ™ãF>rr• .îäøRé¯2ÆOyñ‘ýŒ‘pÇ! \ËyÃXß&6Dñe;ÍDÿ˜5%ê}ñ@ý§P”üýBŒ`˜0hå£Þ »ï¿·Ië¶w¿0z¨MƒiNãP²°Î=ì1ÀlÍ'Š» ,ì=ØË•5á™Æÿ u(+A2’õ1öˆe‹àŽÖÍófu&k‚Üüú±j˜± ÖéÊú’ ÛjÀÞHŽ,q¯8ô:rèëEs¾úŒË*ôÐ`k‹ ÌÅ‚&DÇô0›ÝôÙS^Ù"ï•çlüø–ç}›ï°JÕ`~0›;2’Õ©ññ­É×´À*ÞEùòãe}픺¡PEF‹ÆÅñºÂäg`=&ŒcEG¡ìø|*s|`Ùã=ëkY?ãÙÞ{ÆtòZ¥¿Š»ÄÄx.Ü—é0gH&CÞ“ï‰8yîmõy¡16üp¤è‰Ô…B@!P0beíÏü½Öã#Ô××?ôþ§ž;",$àå‡G˜¬í8¢‚¨·ÿ7ßršEQs>ž>áÊ•”xnZ"ð '?`60 ztRÌ2\°¡ø ¼;åû¿(›X¾| ½…AtôÿZÎ9ý$‹¼ŸaÕª¯õ4‹…+}޽R&ðhGƒŠ§÷øüA(rax'At«3TZ-Õø¨4¨+\PUŒâ*mkÜØ«ŸJ.¢ø¿ïŠâc/oô!O,þ,o€€}¼aeâÊðÇP›cpe£QÜ(W÷E˜5e îΈ™¼ÜðL>´p@²˜Íñ±2¶­Yù­éÆ›'|5­þÈ]ý+ïÃÎPTy Î'ÓÎ k>eF}¤tãc£0iºCø‡dR¸fy¿ò? ïñ¯÷›<­+ûòÂ×øhÀDxEH.\åǨHuãšF@kºûËÝø²Ž•Þ>ÔÎÆ‡K?ÃÇ6DGÀ>GÃ~¨§ â°èZTþ3Él`“ J©Â›7ìdgí\¿zoízðõm4—ô1#újJÂÁhÔ‚DŒÆÖÇ´c±{Þºê¯íÜ4ô=Æ£”™:–œÆl«™ÿ±P—Œ ¨k…€B@! P(ÎB@Ó¡zlM`8°‹ÍšpÐp>+~ûy9«,âag’.é÷ ée²ôŽŸ+ªFÀFƒá-hÙ¿{Þò_ZÈÕGŸ£ï100&06œB•Âl8¥æ*S…€B@! P( ² %’Ù€*MAø•¿ÿ²"éÜ™Äîd#âÀöÍ#ôîíšjš‡W8GY*©ÒV ÄÑ€{[xÚ{8^3çæ\Ù¶zù÷»6¬…D}sœ%³!%|Ëñ¤˜ ÇcªrT( …€B@!àŠ`÷Ú¨F…'ŒÆe`TÓžMë÷ÚµóX¯ÁîçEêõ¼X @ì´/=¨–Ÿæíá¡l:\±g¹N™99:"Â#`K©“q,öÀšKþ\•‘‘†¨`2àu ‡d6¤•’l0(Š …€B@! P(*†€T¥Â®66å!Õ®,™™éæ•¿ÿ¼|åï´ªy»Ž µlÝÂ×? ¶—¯o€»›;˜E®†3„¹¹9ÙYé©éWR“OŒ‹;¼o×I®&¤XP›£ì8ÀxHf£x*]‹ãt&%Ù¨0„*…€B@! P(Õ©J…m,BÁd0|Å'žyóáqxßî#|ãk¤QR Á… Œ$ú‡ìG0•`, Í£7·¸FßK©Ò!vy;¾ÈÍrÜPÌF9@S¯( …€B@! pq6Û©Ÿ\ŒÂ#‘d ä=ì„ã~-_Ëà©’)‘ïð#E.€€TBâªrèG)Õƒ‰Î0Ç30$HïTRƒÅ©ðªÌ …€B@! P¸$X‚‰ðàªQ`.ü ‡/_ 韱9-µvd0\¤TŒ†QªÆ’ y€ù£!¥’Qá[Î!%Ùp®*W…€B@! P(®Œ™X˜‚ä5©Xˆbj”l€!±f6ÓÁ ¸Ifg£TÌ„”l@•J¸~FßËwùÒy¤Šó°U9+ …€B@! pu°„z˜ lBƒ±€w*¸62ÊnƒqQ’̆5õ8F&iJd4&DÇô0›ÝôÙS^ÙR‘6+ÉFEÐSï* …€B@! ¨Þ`Ñ)w¹±5ªàÕ§$£¡6ª]³¿ÑÖ ‡”`àŒ¾•G©Z`É¡·´<á× R½PL"ÅlŒº­P( …€B º"0!jÊ@Ô}FÌäå¥lƒ\ˆ‚™óæ‡TŸ’ÌßRä¢X3²OqƳ²‘¦;„OpH&e«¹J­P( …€B@!àL,º•Ÿi™ ›Õyðɉ½|}n&Í©™(̤iþ6ª›UŽ€Eׯ0K‘ [,'-¹æÅ_ü{ê¶*¯W@1®Ð ª …€B@! P(‰€¦CQ’’ Sß¾Ãýšõêü¸·÷“‹^_D÷õ¶ú"‚xYó-KTÚ ÀÄ-É©éúåôL“î¡GŸs6;+ûÃ¤Øøÿýsx¡’’Ž ”RöW³QvÌÔ …€B@! P(j … Äïò…ÁAµÿË»äam›†ëÝÛ5¥ÍÃ5_o/¨S)rm#˜ž™E{Ÿ¦-ûކî=ÿVXǦOnöÒø¯§¿³˜«¹ò©U•³íŠÙ('pê5…€B@! P(Õ0^¨F?ÿòÓÞ>~o6¨¤ß;¸'µˆ¬¯ŒÁ«a3cH=Û7Å¡:y–æ,ÞTï´®ÏûbÔ«_ü3æn’4/»G9ðP¢°r€¦^Q( …€B@!PÍŒ\Ûz?øÌÄç˜ÑˆéÖ¦‰öâßnucF£š7OU _{«úÕÃÃëí±'=Ë·¥;cṳ̂®ÅŽ ’ýB*˜¹z]! P( …€B òx4jÊJ”:+fòœ­¨£qÇÃ㇆4ˆø ÒGîê¯Ö†V`Õ”ŸŸÎ]¥o=pLO½”4ê‡ÿ¾¿ÛU)QÄ•d£¦Œ Õ…€B@! P(WØÌ—8lÖP¥÷ŠŒlY»^ƒˆé¡µõ1#ú*FÃZ5äú·AHÕ þOûö=óÇ€ÓyeìSCj†B@! P( ‰À¶Õ+–â¿ gÉh B¸ßûG?éåë;dÜ]Lõê`ý©¨¦"àæf"f6´õ»ûG4Hß³iÝ&n«4wšý†Ó¹™šÚaª] …€B@! P(ª!^@ªá͇_­à:oßppÀ"el;pœ¶î?Vä>ndeçPìñ36Ÿ9úfvBO\;„þF¿× ¹‰[±€1Q„'°äÐ[šÅ<­¢È¨8EP½¯P( …€BÀŘ5e ª4#fòrCÕ°{µŸ`6Ü==#|¼tŽËPdWÛðN¹.Á\,\¿›Vl9@WÒ3 ö͆҃·ô&i²eß1Òu ukÛ¤HûŽ&ÐgóVÑûÿx€<ÜË·\M¼x™rrs)¼^í"ùë¼µÿíŸhסS\¿ ¶e¨MÃû^G]Ú4¢\³™;ÃÁ #мWn ú=-Õ=’Ûƒ±€á™ª0izù€/œ‹ÈÜê–ú©P( …€B@!P°èZT~ý%³†BJ6„•»‡GHP-?‡3(÷5;iñ†=4ò¦nÔ±e$¥\É —l¦÷¾^H1ßMžö×±]Z7¢ö/”ŸÑ@¬ÙA!lôn‹ÙØ{’6î=BHµü}éЩséöI y+·ÕXfØ ß“.†ò¥Œ¹±±PTÅ7+Bö{º"9;ùÝÇ&Oëj¶˜‡p1tMoÀè8¹H—ËžGÃeM׸b'ÜLn‹?™òÊ6—«¤ªB@! P(•€¦Q‹áJàÖ~8<ÜL&?o‡3&,Û´úuiM7voËE×ò§GîìG“ÿû ­Ýyˆvo#î³€>Ÿ·šöާúuéáÛú³ä#€v<ÅÌÉ&z󱻄Täì…úæÏõtê\’ŒÜ?´75 yœ¹Lß/ÚH'Î$‰r ÅŸ»H[XEËËÓƒ6ì:L÷ éEZ\•TO8O!AÔ¶iž ™dHŽÄ'Òw 7i̤¦vÍÂé–ÆœMJ¡ïùþq.£v€/ÝÚﺉ ÒG„Ö¡¤ä+´fÇAzâÞ›\7úqéf®ïE e†§O§æ4 [^›O½H‹X곟¥7`ºÜYêÓ°~0My#Ùk§h¬ƒþC¿›4“?g'ǃ­ñâÒœ–±Cjg•Itôÿ¼ÇMŠùǸ¨)§r-¹[IÓ§p’|„Y%½V~¢Ý#€ð.À8]+¨v* …€B@!P*ÀTà ‡/ôÎh &xÑ“Kmš^žÕåÅ}HíJH¼„d‚v³S ¿Ý7´¥¦eÑo,QeçäPRÊÞfÏÛhÿzÁ:òñö gBá¬òôý¢ "Ùl¡ç,cæ ‹U´zQßÎ-)¬n-êØ¢!ùùxQçVhìm}©Ix]‘^þ×±eC“òÙ¯«˜I¸,o3³âÇê^õ((ÐO¼w#3EfK^Y94zxjQ>å÷N2ãJMˤù«wо£ñ4ü†NT?¸úùPçÖiâ˜[¨S«Húié‚í m“1í©QÌì4÷ÆÝÕ_œ‹k§xèðÿtô?˜ Œ 9>^ ¨Ä‘0'äÆÏæÊFj¤-bv쓯iÁ'¯¼ruÄV‹–8¾’M›VÛ’nÎú‡ò廌ÓSŒ×8ŽºÄñ¥© …€B@! ¨¦`Q);…RYe äï[tïÓÏÇ›RÒÒ Ê£1rPžÃ£ËiBõªàaþEZF=}žwþR€¯õêØœÞÿv±`.¤¤ ¦äéûo.RÈ÷ÝYºP‡™[n}a?ò̃é‹ùk鵿ÒM,¹s`W ð£ÚÌh$0#"ß‹;q–¨ËôÜCC©U£úB¢±3î$­ß}˜"ÂEq™Y¹¬6LHRÄ ¢›z´†î›7¤ù«vPì±³Â&$îÄ9––ô"o/.#”K™È^;ƒ™Qs4ñºQ2ŸrL8º‘_µ`6ó,۽džó{M¤žµÚ)hTÓLó®o¸úߌŸÓm²>âë?·Ìšõ~5m–ª¶B@! P(ŽC@J1 L±Øt\ ù9aÁºÌ;þÖ D„Á`»AÝ ‚$PeÂ;h)îøY6$×éKf $ÁÈ9>1™2²²Å­Fa…%2½s›& èÍ wÒŸkw1“³—üý¼ip¯öE^I¾œÇ54Ô»ahmºh¨'¤!PÙ’´çP<-\·‹Î]Lja¸ŸkÎÛ6 £[À#­Þ'˜'<°×Ng0¢2W¥ø)ÇHÞ#]‹ËOS¡“Ë3yŒ†>¥?‘{½±3£Ç_e‡+ÔôšùòÌ©Q«žÙ›rΡ[ôéŒ)†£föµj•B@! P(ʈ“rA)ÏeÌ¢ääÁA~b×~ß‘xêÄêJ’yá}þR* ê™gÇû¸' רí¯Ã’…£ò&Ÿ½½ò–«Ï²$BJäcØz€°¨—6òYiÎ`îØŸJ¤#|P¯¢oAõ tÖPÆ9ötÕží9$yz\ÁöP#›ø·a‚zñƒdRÚ§#}òór:È“›™¹éÀ’½v¼ìÈ‹«jtÆqQP¬©Qã ~Tàb—%¨N ‰33c&Ý;K1¥ê+à¼À ?àXªU"…€B@! P(j ›¹!8l‘Ó õ%,¢×î™6°ZRKŽp#5 ¯'T²þdIì+cï‘Ó"  /OwZÌ×ç’RÙem‚0ÇCØNÀ<“m-·ÃH, -ÊG^ñ‰…­‡|å\âgPm‚«–°ômYÎî|“/§287æ F*«Š±!9lW–lÜc|,ÞoÎv7tn%ÊôpÏcTìµ³PŽýQlÇæ-r»Ê†9!óŠd #çË–Ô%<ÞN’G½·ÝØ­ð(©Hæ×À»o¼ñu½éÞdI¿1¼gÄÀ13V®ü-O~w ´_5Q! P(×2ÛV¯XŠÃ€6˜¡çãŇ/þ{]KýàÞì)ÉÑÔ‚m"ܘéøkÓ~Á ¦F«LÁã’´åØºÿ85nP—`$>õNV•"ºwHO!ÙH8‰vÄž a}; cjx¯c/VH·pÝnf".ïN0´®D«wÄ {­\$ õ¹,o–Z¬Ûy˜ ²· Vðø$i;—½`ín¡Bu$þ°ê{è™ßïÔ¶E›çGåŸÎ£ŒÌl¡e-±0–ˆ4>Þˆ/—G›9Æç¿­¡ÿ¼û4cõ4‹%?âú6…‰B@! P(5 QS¢…3b&/¯N-…qyÔ#·W§*—¹®Cz·'.Ošî>Á%% ~#Òµs*`Ÿc†¡À‘ñ¸:&K•‹B@! P(.Œ€E×¢p¸pUÕ®±8+]Ó»=ÁÑù^Ëù1¦À¸*R( …€B ¦# é®º¡\Ó‘Wí³BÀ%"ÛòðQ8|¤UÅÕϲ!Øw4>›·ŠÞÿÇäáîÜ%ízޱñ׿ýwã’Lx]«Hºc‚8ŠÌf }ÄãpÛ½mGeëù8·g\¢‰ª …€B@! P(ŽBîi?ýu»lM¢û‡ö¢†õëÐÙ ÉÉ~ÁšR'°TÌF—Ö¨ý Îg4 AùqéfØ­ ¿{ :—Dn&Öq ÁE¿_0t`¶ËJ×â*–AÞÛŠÙpŠ*…€B@! P(×Gâi×ÁSôа>„~P0K6$MJ¡ï9pÝñ3IT;À—níwuËß­ÿŽïC ’À‚½,iÆQµ‡ßЉ#‰‡ˆhÞ[öAó6ì:Ì’ƒ^Ô¦i}5`u)/ð;µhHw ì&â] ?.ÙDo>v—xõŸ_hä ´pý.º”šF][7¦ÛoìR—Q³ÿä¨áWÒ3©mÓôà-½E ä³~×Aº¡skúóëØ"‚FÝÜC6‡Î0#…`~ˆ^¯N€8 òEqù¢­ˆŸ‘”|…Öì8(³y+¶ÑßïèGÍ"ê‰,–lÜKˆzþò÷Ò3ç‰rivŽ™~^¶™vr@Äú€´®€!õùqÉfÚwBÄñؽ- îåÏV³¦F7¶³¼×.i³QÞÆ¨÷ …€B@! P(›ù§“g/Š<±`·&ÄÉøxÎ2¶=È¡ÑÃûPS^TC r’PjZ&aÁíÅ‘¾Çðó‹Ì,X»K<ëÈŒ„Òëܪ½­/5 ¯Kp…Ì…4–UŒ¶ðÂ|{ìq‘>;'‡’R®ph6΄ëo®cu®ft{ÿ.bF”ÂAô¾œ¿–úvnAF êP«™!×ûnázêÚ¦‘`*ăüÿ……Õ©™sWо#ñÆGvóE[ç¯ÞAûŽÆ † xyp ¿mÌPIÚ¸û3ZuE @Ô?+'Ï&±FØpKSFÞÔš7 ¯¬Þ~ˆƒžf,úш~iî_[éü%ÿv]R’ ×í›*«Ytô ÷„Ü ·rÄú~º®]ÏÑÇÙñ“ý93:jk•UL¬P( …@©˜3ùÅê¹æÜÌÌœ¬Ôˤ”È’‹Zþ¾BBa]ÆáS‰t#c?÷ÐPjÕ¨¾hìŒ;IëYª¦äëíE²Tt!9ƒøm'ÖÌR0ujùQ‹Èúâ9þÔ³?×E¾°åØwä4õéÔ¢à¹ñâ:fTd€¼ulgq€¥'½:4QÉëúT¯@í›GÐŽØ4´Oñ’„;‡w-Âhà!"†¿òðpú‚™•˜‘jÓ¤=ÄŒ¤9ˆvn/ß̬\Šy|˜Ö ¯\—ÕÛcéžÁ=)ñb*%œ¿DÜÒ Ñ¦½G˜ñiBîïXèþÞç„ä%48€pÔ¯[‹™¯·#¯…Ûù‘™£süµ4;IöH1ƒ²ædt:wÝ5îá©çCM3qäH½ i––\·5§•5³%FÅ,usÍlj•B@! P8ì̬KÉ)ief6üý¼)=3ÛfU’Y‚jh0ðnZ›.ò®½¤H¶ñäïë%TƒÀL@]ÈšR®¤³Äa#‰?ÇÒ_VJÒëtòw£°ºò’|½ $\¤\É iŸâE[¤8ªÅe?óÀ`ÚÊR‰Ÿ–n¡ÙsW Õ§’òmÞ°^£¼{¶kB¿3suìôyáI+8(@H-Ð~I¹¹fVõÊ"#Nx©ÑÁ“g‹D®Çq™œÎ%•]²‘œš¦çdg牨 rrÎ…b6œƒkµÍõѨ©‰,·»iž=?‰yÉh4½Ú6ê©øcQï´Ê¥ìй¹F°RÍT(×8be›™žvþrF––ž™%¤ ¥Å$<¤Ž`àv¶uã°B¯…ÔÎó²–w훲=è{˜jß,¼ T¨JKmÚO§Î&±t`¤°Óxï«…v_õô°··—'ÕfÉFÌwû>TœJ"Øž¤\Éd†cÁƒTIùzzÎÌì5v:%˜ xð²&w®Þ³Vr3™„ZÙ€®­éÎ]­_+õoô7ú=;#Ÿ¯r9V9LˆŽéa6»é³§¼²ÅêQ™~Úî‘2e¡×$t²¼Ì[ “?™RˆÑ(ÒÄ QSÆ™uíI~ÐŒ7Žqè OfO‰ú¯L8.*æS2i¿hºÞŠ,ô«b¥°HW,„ÇOŽ™ ç݃UÙMs{aæ”W7Èw­Ï&Oël±˜ßåûxßÄ ÏŽ™|/Ò==õºÜý=<ãû,Ôû¹ûýãýèç„[ŒñQoÝeÑ-Øé_ÏuxIÓ5(˜n4yxµ˜³‘EÿÊræ“ÉýýYS^ÞÃÏ ï±è@®ÿ:‹¦Ob5²HÞpÙ£knÏÿ螊þ003'õ–ÝÂÒ  ]£&wš(ÕÍžû÷¿}®$¥ïÔöFÁRŒd–Xx罫JAÅjɆ½Â{/Û8$_N£lt]ª\‹½\ÙtÀÐ;ó3ñBnv!áèØ"Ï(½4ùÉ4Z6¤u; ÷µ}¯kIð0•ȶÒB¦³uŽc¦êKlÀB…¬SËH¶g‰¤MûŽ÷æ‘¡ì,E0&¨Oi ýÍ’íÔ‘ÃR5Þ8. ²±äÐ[‰eÊ ‚›å¸Pâå­¦¾òDô[-y ªãë­ýb¯NŽy’)Ì”|îíáÑšL4“Gí¤<©ˆ|S#³ÍùÝbÒôÿ3yk#ðD0:½ÊêY¯{º{¶b‘éRžœ–>ýŽÍYhBô´Æf=—Šg›‘¡žšÞ™ã /øn M™ý›°/A:x¤jT?˜ÞçûOLûŠÞûz!Ùç©®ÙÊÇÖ=ô·n±\Ú¶f94X0dÿËsÞkÚÿ·wðUëߟ=%=@€„Òl`¹ˆ¨¨×†^õêUÑ+ ØP¯ÔœxÏkŠÜ¿(¢`»–«ÊiÒ›ôÞBIB %½=»ïïÙd“=''1åN’g>ŸÉîÎÎÌÎ|g“̳Ïǫ́Ìq,žê¨JZ+¨ÊÃ8¯op8Õëñ÷#÷ÝèhR«• •°AÀ˜;76Z›ô#ã¬([|0&¶yóæ½­­Ç ÚÍ?¢qË÷^~¹@¯L++I3â&}Ki˜tÛ!¤DåËò“¸„ˆkpÊÊk’Rà‡0Âp'‰Î‡“´'çÆE¿X|oß‹¶Øª8 mÈ]H[HéÐx˜ÃBýžzküø,º?ש¨ŸHfiÄœ7m+´4[ìLPË^š9Ó_o/Ì'ƒ,Vi<´;)OdBÂÓâäÙ Š£àY\N3ùYq²XM}gÛ£µ¥_jGàŒí'çóq#îÑ4)¤1 ðÓò…Àeô_nÓL¸h·ñàÀ¾.•z¸ qÞ}8YJN:ò?ܦw@èX+Á•V­<‚+­+ð5fH"ÀnŸWôF{hx¤ý_ð¼R±þši™ñ64Kð÷¦ÑнG:”¦KßéwJ{ÕþvS˜&ÁhS¹“è)BÓ1ÏÅŸ!µô/Mi8S{¡Þ¥.I¥½H3Rz ©'.& ³òÃÙéËqNé‚%ài'¡Ý(lmêºVÏ¡ ÿ“tGÎÉ ÒÓPOÚÿœ¤™UQZbT-Ù± e»ÑµSuöÆß†³Æ<”Ž_ª%èáù¨É"ÁtÊ%$A ÒˆRÐÿ« ÉÈSdù] L·ÆÓ=¨»Ð±8¤ê‚]ÿkìØbiª"Èê¹øÈ˜`L@‚3ÅÒ€aÚlšTʈ:®ûeÁ2Evd|µhƒB+2U5Ð$Ù(hË“p`4Œ÷*:§2º ¡çs4ôôêCáàN«^U5…» a¬£ºõëp?'ÿ]Ð0Þ#.<ªÂ—Æ—ÆYu:/®ùß$lhïŽôNèï‡ñ1^9g͆W0ÖJ,ªåp¡ê0‡z¢GWž f9§è¯—Ë2ªY- WÕ$JÕmØ{'ÅH¦@Èš3¢ÈôÒpo–U5§® §ª?~‘H*ð[!KBqi‡–IR ¿ÞmµŒ~Ç íöGÊ–5<ÏÍÇQúå+ ªä M %à/8Ö‹…îyÐ?(WJ9P^³ÓâÒ…Jj–$h6ò$“i奀öQxG1ùo+:Õþ ¸Ô¡§ó‘ 0&À˜@% 肆.lææfçý¾zå—¦[o õ¹á*þTÉ9[!@㛚–.¶¯_ýavvÍ)taƒÞz7j%&dµR?WZ‡„›ÇïKQâ·Ë²ƒf?ä©éd¢ƒ¯ï‡¡Ò#§ë§nø)ÜŽ¯ìŽVÍ’<•£´ìãNÃÄ(³ìˆ„7mº VyÙµtÔyÓ졞2¡žMPFÜo¼÷¢}ZëGa/Å,öÓ«u®Š–中kE`"f^¶ãà`h!Þ¦úàǽ ¶\m#í“{$Ú'íןs²Û!-”\ëéå¡‚Þ‹ï Í-&‘ö=Ú£W^Yc:Ç!| þ6Bás&À˜Ð ÐÇ3šP’ú‚&™ùˆdæ\°}ݪÝaáô¥û>ñ½PŸºw ä¾Šîq¨£H£A‚Æ–}IRÒþÝßnùí×­è =½ô.Ð;Aï†ëV$x#ðÄÄëIv»¤ÀW` &ÓK ü×l1Å*­šð?¾‰œ'z¨fë9ö±g¡³›‰¯òÿ€Ànµ]³"åÜX‚b4&Ø3ÉÔ¨"Ð8Lû<& ‹ÙÒôYä¨JA??§º÷ýxÛq÷²fÉü>ü"6¯#Æ?Hšc. tˆœÚ*Kó¡½­F d±~JuÝvU§Ý>V  oEÆLö7/³þÖþpfHy*ÚXlËSñ[²úpæ‹mi±­:%Oï,IŽkæÄFÏ«LZžëF‘%¢ŽÁsâbVVºgdL€ 0†B€&“ô›4û4Ñ$ÍbàŠ¿]Ž_dvu÷©óÕÇî¼ÑT”ãàƒÈGƒL§H£qdïÎùËøæ4“ƜƞÞzè(+h¨Ò¤×8mùj\WP÷ $ÄÛVa Ø~Ð(´TМ<›_£¤À?a®" ÛRcm³`”ˆ/é3ĉ´ 4!^gpÒˆT†öí†%lM³ ÐÌpÈÎ«Ž¼4IQæ8M榞 ÎŽø;–{} ø(jGŽœ}F–±-B±6á>,O;áå%Gxk£`UæÀJVk ™§¡Õx þ+ͱÛ6¹×WÞ5 yX¥ê>üšgÁ¡oIŠ—§¨…œª:º¼2žÒ'LÈÀ¿‰i¨ãûÈè¸äÈ7âïñ”Ó˜`L ÁÐ5º°A¦4ÙˆtÌ[ùÓw+6,ûåÓ©iÙÿ÷ù"1ë¿ËÔ»b?úΡ® ñ¢q£ñ£qù‘íâPD€ü.h ãH¦R¤½ ¹5 éÅç4ö4ÎM¨îµà“âp"¦eP¯óZ/¹""@ÂÛFQ9Øôï É$}S¹Üœ‹ 0&À˜@" OFé«¶.@èiô%œÒI»AZ +"™^…½ ’9ø2(è‚ $HÐ8êZ 6ÈŒŠŽäN÷*ÔhŒ²Çõw:ÍêÜØ‰›‘·ÚÁ'… ôæ8œn[Œž2%̸‰Yµ{ÙÀ GgŽÜkµoà(*Ýý¹ñ¶uÈL‘`L€ 0úF€&§F}ý‹8M>õ jÎ5íŽ4_Ô4Ã>†4®ºvŠ´$XfC$|ÐëZ ]PA’kPb²Tôš u½Sµ+Ÿ6Ì&óbY‘ã•\…VÓù¢j]âÜîˆ#vá6YÀÕý_3&À˜`õ–AB½ÂRèËËéQàÐÏuaƒ&¨F͆Q»¡ ú±œê9ùÐ…:’fC×j5$tè‘Òiœ)Ÿ^§‚äºA±‡•JòIaƒ–;Å>Éð¤½`a£RCY~&£$’‰kù¹ø`L€ 0&P_`t[q_Ê6è¶.d'ªú²¸$pa4Øoƒ¨ùfÐÇÐ]à ñ¤h2(OÅ‚†û蓆Ö?Uz›ªMÇ&sƒ´½¼Øé†TñSTuL2Ç6¤~s_™`L€ 4h’¶A_e¸ Fš'êæSº ÁÊP½ôyhÝ]ƒAG0ôxI[ç³ÂFk›Y©ròKŠ*Þ‹´'Ü”h" ›C€[âH{›Óhmi3« E9+`L€ 0&аèQ&ÈÄF.訟³ áÛï„»Àa>èü²Ÿ6ìögò±7ÄHPY(iŸÂèQÚTæ²Pªƒ¥Í\¢lñŸ¢é½ï&ž8r`L€ 0&À˜@Eh®E†&pü}œýz“Ùt‡*‰øxÙJÚRú•ç{—‰,‚°ÒVtUÔ&“i,ƒ|bRŸ—P#câ^Qu^îo„5|k8þø &†&  õ/’Iz51ÖöΗâL€ 0&À˜@}!PƒÍ|¥ÈH{ ³™éE‹ÅüŠ¢(­´ă”&‚hqÒrpðAØA\IÏÌU³róMôÑÇ)§S™ÑÖ¯í{Õùè—@ÝLŒ·EÕ¤»>/lPçŠñ/(6vcŽ—Ù‡£ü!×|4„x9zI&ñ: å³â;L€ 0&Àê+jš¹Ôˆ±¶;ç:¥uï.mÕ~½:IWui#‚xãðºò®äæˆ]‡SÄæ=GÕ݇“%³É”êp8þþÑTûôÌå.i¨ÂÁ/Í8ÌÅ®í!a/BüÒdú™÷á‚öÑ(ZÞV}’ì0òÑ«‘‰q1ôRq`L€ 0&À̛ޢ.c.0îºNsA4ÌÏNxcŒÙlÚ¶E˜úè7˜»¶oùEù¶¯8tâ´øzñFgÊÙt¡ªÎñNþ'Y»ÀqÉ\ꌰAƒi·âHyþ¢¶˜T+بîŒ*©©èH6åiHoI4=­iDÚG{€&ƒÇ»ä\_uYCbÇ}eL€ 0&À´ÝÃiµ)ˈq¶1V«ÿÔë¯ì¨>uï@ÉÏJÉêB‡S|¶`ºe_’¤(αM±“ÀñÇûlx©óuJØ0öyt̔뜊óN¤u€°Ñ 5Þoç6² l¤¢¯Çi#DÞG£!Œ:÷‘ 0&À˜€WÐ$ ëc/¼:,´IÓo!hHÏ ÿSz…J=®äÃï#C-È˽ÿ³·§,BW/‰ÀÁ/T=~©¸kL€ 0&À˜ð@ DÐèÕ«_ãÞ·­uDXø¸§ÿlf†Zõ$‰4Ó>ýŸóTZúÙk¶ô\¹r>Y•+pŒ²Çõw:ÍêÜØ‰›k‚€W¨ =.˘`L€ 0ºG€æ´ýßÛn}{šµ$ 4êÞ@V¥Å4¾Ýyƒ›=·êp]¯‹ßreÅ!&KŠsJUžá)o¹ð”™Ó˜`L€ 0&À|ŸÀ([ìŠZJs?Í| ÇÀàÐÐQ´ê;ƒ{ U“hœi¼ýIذ"Ò»àYT ü‚k¼'ŸçÊë!\î`L€ 0&À E•l=ô—L¨h0ôÁGûãNËÛzÈÇIõ”7V/mñÀÓQ4þô.Ôêø³°QO_$î`L€ 0&Ѐ H*V©Dt %¾HhÑâ6Ú°öѨÍ@û>(°Õr¿ï;&¶ìMrOÖ® bÿ±Sïy;‘|R ñ¦qn܈¶•еµ&pÔX5Ò‡ûʘ`L€ 0:L€„2›ñC 0Y,íCýUlØçõ‰& ¿¬Û)VlÞ'²só…Ùl]ÚµOÜu“ˆhÚHC¸yOö~PÄõ=;jׯ{ަпÿ&ÞyýqaµToºzöB–pȲhf¬Z;Ç—}ñåÂõbÇ¡“h_žh&îxµ¸öÊBv:ž¤Sâª.mË”« ´Ac(v„ÏËÉiþлPˆHW­H]Õ=´†`L€ 0&À˜@"@BÍý4aÃl±´hÒ8Øë‚ùßêíbñú]âáÛ®}ºµÙybÞ’Mâ_Ÿÿ"âžHøY+ž‚^Û£ƒèýê Ô†ŸWoál< ÛöŸvÏÿeˆh$Ÿ<}A,‚Ög/´7$tY õiײ™õð­¢¢~jõÒwØÙÑuº½ô.”Ú»©Òo<ÎÝ–ÏurL€ 0&À˜`¾G€æ}ô¡™¢¦Dµ2<‡Iw¡CWv¤祡9&÷áa¡"õìÅ’Ä0cj(v£ÈÌ)?B£@¡Ðáç3²1ó-šû~þóZ`c¿S´ÉÓW‹ÖkùœNEÌúz„ƒ˜hÝ(^ÓM´jÞXôéÚNú‹kºw#î(:¶i®å×ôéÖNòÑ¿AHÈÒ“!¬ÃÜ+_þƒµr·B(r*EÏÈ+pˆ'ï¹Ytj!>D¹<(dæä‹«¶‰=G“Å=·ô-›5‚Å5=®cŸºKôíÞ^|³t³ ß ÔG2¦¼ô;­µ´‘Ãÿ¤Ëë§vÓË? è‘p¡¿eÞ…Äx[Åš>¶LÅ5­Ë3&À˜`L€ \v›ÐŠz ‰%E]à€°Q;«eÂdŠBHP€v4þ 9¹%I$h<<´Ÿ¸¡w'qëõ=ÄÁ§Kîé'9yâhJšxuwøø‹ût¤ !á04$”<<ôzÑ¿wgMcÖHÓ4X ]h á–{uo ùŒyüqæSoÌùA|ÿëø¨¢Ih°ƒ A{RP¹VÍ›ˆÃ'ÏŠsxÖCh'ù—<õç‚üÖí<¬7QäÈ,î·õï© 90SÂù•špÕ§K;M`ÙŸTÔ·ÇψnZŠ+žÑBH¡€–ITÔÏ’yõDÕ… z'ô÷ëO ÊØŒÊëH¹B&À˜`L€ \^‰q1ã<´€&•z$GñZ 4a§…/þî4m Û­1™×™2QŒáÀ±Óš ðïkJ’i²Ÿ|6]äo³Z¹j.J2VpreÇÖâÍQŠ…kvÀ¿d· wÜØ»L‰ô¬"ᨡÝíZ„‰ †v’6„L¶ô°ëP²øeíqæB¦fFé²SÖn÷ìÔJ¬Ø²V„«¶Є'ºQQ?›A+äíáJ>õwÂÛÐêca£V°r¥L€ 0&À˜ð)4±¤`˜`j“Í¢T/þlÖ$Xûj¿çH²è s%=œÅÄ;íb¦zC‘¥Sš蜾ö7…f᨞ˆc€Ñtõh"Ú·jf¸#4_J I½îÃá’á.H@xpÈõšöâ4âÆ²Èô‹ÂiÃ3Î`¥«ÞðçЃq÷uòC! 2#ûôÝš5îÝÿêYŰ›ûˆÙß.Ÿ·C¸¹ š õ³¤°÷Ot­Õ¬¿#^} =€`L€ 0&À˜@ý'@“I}B©½Þk2_¢IôšíÅ’ »!PdiæQ‰ß¯ÐÌ”ôíZòÌ ™9b-òJKëa–Ô ¦KôÅß:µ‰ÐÌ BS@¦S´/Æî#)Z0üý,b1®ÏœÏÄ’µ©šs8Ý$ß rχ¯íÛa ¤Y ¼ô|ª+ùN§¢ %?IDATìÍ×C/GϹˆ{dÚDZ Z±j ´Ô—åXÎ7=+ÇÅáÜX7i02a*ÖŽä仲dÃ.ãm­|ø}ÜrMwí™VK‘’©¢~ºTà­‹R3:ã{QRû({\ÿ‘1Sú•$Tó¤ÖThÕlcL€ 0&À˜ð>úÀLv>þˆAˆ!}npWËðfá7a¥$o‡®ð‰0Cèøuã^M =5ÈÿV\Òý'¶ì=&®hÝ\“ø‚UÛa*%Ä£wÞ i6RÓ.Šmû‹»öÕœ©ÉÁ{-V±¢|¿¬Ý !✶º9Z·lÖD¬Úv@[jw žC…–xV´k·†CöVm+ZñI[ñìŸ×ìÔL¨Ž$§‰ë®ì(ît¶2Tü-öÃtë»_7‹˜jݪsíŽCâ§ß¶ŠCð+!íÄ ëzhÕQ?h'ìÌ­]“ÿ…‚Îp²hÝ.˜x5ÓúÖ‚Q›ˆ¦š6‡„ ®VnÙ/~û}?œÎõ6–×O7ùK{NM~`wöÜÅ3;6¬ùõ“ y¯“W‘G>N®0äߨj|èï«—†ËjWѱÚÕpA&À˜`L€ 0_!0Ê;„Ú2'.fyq›ès "í¨G³î–3îݾ=»^ùÚ“ÃpY{!Ðdå®±0>‘òЖEaöÀøøÇÕâý OiKßêéºF i^ýùƒ@Ëa)ÖP: 0yX*+Sy TŸ§º(o.´Ô&c»IKB¦W•™ü˲S+KêaÇÁâK,•;aÄ=p^Ñ4. ß­„æ$P<}ï@=[‰/Jym+ÉXÍ“·?_$ví?ºó³Sþ†*hY-²g#¡£dc¿H[ìJ\ øÿ ¦cuûlT——cL€ 0&À˜€PTÉVÜ4]Øpoé%ûàl"Ü¡_ëyhE(2kÚ´ç¨hM„q¢Ny+š|‡`¥*÷@BAy‚ÆÕç© M• F¡ÇXFÁRºátN}¥}8HKCKæCEý4æ« ç,lÔ…Qâ62&À˜`L *¤ÚÙC£*M¨N^ÚÓ‚ö¡ Z·¾…¾ØMý4|K~^ ÿ“Œ,͇åÁ!×Á£[}ëjIXØ(AÁ'L€ 0&À˜`—“9—Ûž»ÿr6¡ÖŸ}çM½ņJÈJ¹ŸL€ 0&À˜`L€ \¬Ù¸$˜ù!L€ 0&À˜`L P¥Þhm6FÇL¹Î©8¤¶†ÿÇ­¡ºù+1Ì5 #!ž0¦éçu*¿*eb‘…,upBÂÓ‰öè­z?øÈ˜`L€ 0_!àe¡(ªË.ÛÔ6Ú_cë,q; ËªOz»iï r¤nÙ¼±žT+Gj™pWžòÖƒ.U¼Õ^½žÄx[”~^“c6"c&ß TåY‘Ûa…E¨ÒHEtÝÛ¾&TêNYÚº²?~ #T‡:92:î˜*™ÆÌ›ôSÝé·” 0&À˜¨›j¡Î*W¹÷hŠøfÙfqú\º¶ mó&¡Ø±û:ìkq…Vm¦·à·mšMöÝÂUÛ>.‹¨‡†¸ßªÔµìtbó¾SØ¥»­Çü´ŸÇ·Ë¶h»y›Í’èв¹øûƒDc8¨{+Ô´ÞjÇ媧Π’0å«BÁ2nÒ¯æ`óÿfOœx±ªðŠ5.ÚŽŠêðõü¯Øg4ÉsæÞ§¨Ê½BR½÷ÛQ¾Ç˜`L€ ø,ì0îr7.˺Îüj©¶ÙÝsüIÐNÙ«¶îs¿_)*zu.¤’Í][ýÌý·¶™s½W™«=GRÅ|¬nUž°A«BÑ.ߟý³¶[ø±Sç¼*hPkÚ‡ÊôÓ—óÔ9a#!vÂáËP/uÛÞ±¿šŽg~V/õãùyL€ 0&À˜(C€vÏn‚ݸGÜ7PЮÚÚ_>™†]Àw¹k¶ÔÒL°‹¿oðµâÆ«:kùÿ»x£f~õ·{nÖ®içë…Øù;;7_ôìÔZ…'4›ÄvlšG¦Pýzv×ôè þƒ ô(oô¬oñ¼6âqä7†$ìqq3vo¦%wëвäöésâ‹…ë°Æymwï¿»ItÂ.à;žëvÄrµ=Ä—l}º¶ÅNãí~?ÚNʾ÷õRñ,´$w)Ó‡EëvŠŒì<ѾE3…]Õƒ±é`y}+iP=)yn¼Ý^7׉öa¤Ü4&À˜`L€ Ô:š€÷èЪDÐÐxeÇV‚̧Œá÷½ÇÄ#wô×&ý_.\/Èü‰BfN¾ÈÊ¥­…ÈÀFxÿ^°F ¼¦«õ—!ÚÕ¶ƒÚ=Ú›ƒ6|ý•âáÛ®]ÚµÍã'Xxníw¥–×øãêníÄâõ»Å’ »ùmÃç?¯ÅâV1æñ;E›ð0ñÕ¢õÚíB‡¦Y©dÖÁ¬ƒ¦¹éÜ6B¬ƒ ¤‡ß÷…¨¯cëp—>I>«õ¡=̵FÜ{‹è A…Šú¦×y©£ìqýGÆL©ñf'>-lD½[Š#>ó…踗p}x^äÔ©áÇq„8Ö‡þp˜`L€ 0¯À3µ2sò„§]½ƒD~ÃerßàkĵÐD+UÄ*Šòxö±ÛouÙ«XggL€ 0&Àꬼ9„Z ¿Sø¹z²SÎÏw8Hèp{Î^åTÚ<3§ L¹lh*hÂmµ”NC[ dvèï'’0ÑïÁH›@¦GS>þ¹4-—e'L¥ Dû–MKÓ+yF&W¾åjq]+Ä׋7ˆÄïWˆF> ’Ï\ÔVÁ"MŠ‚ü¡‘!Ëõ¢@ÂŽ"š6Ò„ŽmÐh„jZ—'ï ß.9^È̆éTS˜z•$i'åõÍ5—÷®ò *ïs*¬QRK¨ÂŒßôJ%?¢zwSk_T…Úá] QQŽêÕÒ°KÙí’2òø×„S]xJ^34ÞkØD¸÷L€ 0&ÀE•lÅ=õ$lhZÂü‚‹é9µ&l´…ľc©ÂéT„Ù\jL³ûHŠ6á6ŽÄÙ‹™âŠÀpMhÈ+(„ÙRYÁ!Bi*â^xÈXT;'íEê¨nhÞDÓ6ŒŸ9O“xL›(¼òø¢}«f.Õn†¹rx7†~½:‹ß~ß JˆhѬHø0Þ§óhuö>åž,*ê[™Ì^HHÏÌQ……d˦½ ^¨²Ü*JG¾Ü,—é†"Æ@Ú\’³è2µ ^¹TóssÒ²ò ¤Üü²Ú×bÕ»vs‘Sª¹?ü&Nœ>/h™YrئUªîØ×¥Òå›öiÂÂ/kw2=êÚ¾…Ë}ºè ÿŠs0cúuÓ^QP(‹Lh9Ÿ¤„¸²ck˜.×|)ÈWdÅ)´lÖX3}º˜™£™Fi‰Å?È”ëç5;´¶Q½«‹ý?¨L§60  Ñ2"³)’* ý{]>dÃwäˆèß»ÈÁÝ=´3=+G,ݸG\ÈÈàiÕ+àL€ 0&À˜@' O&é¨P^ í³ÖHŒzxˆè~E«’çE4 …_…Eó™ Aã˜5ùû•¢öéÚN[íé'ìËñÍÒMÚªSäó@ÎàÜqƒøBÍ»ÿY¢ÕK«YõíÖ^›Ä¯Ù~HL|ïmõ«—°ä®È”iü@cÕ,L±:Â÷≻o†9V‘&#ê¡ÁâóŸ×‰è÷¿ÓŠ©T¯QÃõâeŽ¡ÁX!«Øs$YD\æ>%ô‡? ™ˆ‘ÈwØ„škºwÀŠVå÷ÍcE5H¤ñÆF‰RòÑ#›P ½ Æw£5{.Zv$=ç»´©Ne=P X«áòfË’|¥P`Wœ;P ^`ÊU0&À˜¨ƒ4!í¦£s÷æõ‡o¸íÎ ˜5‚°áæEàÞuÇR²1#ï×ü*œÚâ®SOræ¦H3„ £Éínt)¹ ;SÌÂ*U´R”¾`³Æ!b܈{4|U5Ÿª“€ ÏÜ#ráðM~"ÆàïgÕÊÐ#h¯ w‡œÊß=\Yò%¡`l³–`øafôäQXÚV$àä î)PrEm#!C4<•¯Nip¨žšT^ߪó÷24Î4Þ)Ç’~Â=íÀ‘Þ ýý(-¢JÅÏÔkX)÷=¤.C ‰`L€ 0&PÏ DÚbWR±“ø`:"Мϊ„HkÀ†#F…¶úëK¯MoÛªy£ ÏÜk¢ ¿/òøî×Í¢+L¤îtµ¦¡ð…vÕå6ßÉÔO()§Ïgü÷ýwžÍÊJ§ú´Æ0-±•‹H‡×犮º,7üO>ñ!º7vû¦ÈÁ{h|SÓÒÅÎkgCÐÈFÍôл@;(êÚ œz7ø†øêÝ>qmL€ 0&À˜hÐ~_µb)E7$HP¤ù}p&'¿Ó'g4iÞ\Í—ü¯<}.CíÝ¥¤›3á>‡:N€4Ÿü¸Zݲ/I:v`ï׫þH^ôˆ´V0íµAB ^×j Níe£#&À˜`L€ 0úO€„ òÙÕ:,Iû÷&7iÖÜ £ûÎC'ÔÖáM$rºæP· Æœo—+ŽŸIû÷|³ì»¯@HР˜…HæSäOÂF­ŸÔlЮ—×Òq˪IµÒëXé({\ÿkn¹½õÖU¿¦6Àîs—™`L€ 0RºÀ¡ 44“Àᔩ!M[tÛ°;)›ÛÑBMRSìêmÜí»´>óE´ÆÖý'hy]õÇ•[¥ŒÌ¬ÌßW¯˜»nñÏ¿¢½$d†Q«A¦TdFUæÍ›gëÞ'òÖ{ïÞ¿néRº_íà“>°ëeµ;Û *1Y*Z‡6dvû ËiyÍ3R°åÛÙ'^lÈ,¸ïL€ 0&Ð © ­FE¦3šV£ø¨­Pºcýš­·o;tÓwv:åAX¹¨ù}†ú«MKV+ *|@¾Ã¡ÒŽð´a- „ñË:~`ÿŠõ‹^’››M†®Ñ0 ô.”1Ÿúuס¿BÒœ““)oÁýßkÒ]Ÿ6<ìxY“>rY" ©UëQoÄ=èTÄCxýzaY¶l¬cµSH~ï$Ä;ä  ‘¶¸¯%!mKˆ‹žæú*[GvðgºHô˕֡ •Çù˜`L ÎˆŠ‰ *tÁŠTootÏC: š #Ý“óòrËç³çK»]uõºõèÚÔ?(°‘ÅlñG:$ ;å‚‚¼¼Œ¼ìì Ø°qï[¢™$T’_™LéZ :§±'…îŽÓÒ€÷æEx÷ì™;±F‚ÕX¥ hiø¬>ˆŒŽûHqŠ'  ¼#IæoñA#DʽB-Üi›ü·Ä¸Ió«Òÿ({ÜõBV‡'ÄÅLÒËAxùQ2I'ôk>ÖM—&ÆÙn¯›­çV3&Àê7|™GŒõFüÚ9oF/6ôV*p]€¨k*ô4²ß§ô@Dÿƒ»¶ïE¤½ŒB‰^É|€€®™ 1¤H 4Ž$TÐÊS$`FC÷Ó {¤Õ ü.adtÜÍЌ܀½@F»Ü¨æ ÕW_‹|#þ.¡(#Ì’yðìØI« ýükvÇáýiOX’h"‡¢Ê‡¦)̘9!Þö•ñšÏëѶiÝeQxmÝk9·˜ 0&Ð@´k>S:‘ö¬¢¨ïÛíŸô¶ÛŸ¡¯Ührjt¦k]ØÐ'¨´G"íÍAóEòë ƒ @ðÁ !«¾i#7Í×H³¡G>hŒu­†.¨ IˆÈ©S‹lù3œžmäÿ¹–XÃ,lÔ`}+.9Õ±ø32ÏMÐ(ê¦%b²êH‹”ä³O#aö«o¿˜s!g›Eø=èŽY’P¯‡‰àiÉ$ÞOŒµÍ¤B‘1qñª"þíH$å¡8^„:·ÿH[܇0Ïک磼X`¤S•^Äigü%KRMböÜXÛt•ÁŸ¹_$E  i8’ñå±ÛUSª3>2ïÃeÄ-xæWЪ|D÷+FÅL¹FQœÓ‘—&Ò&URÏ‹y”Ê>o¿Zv¨ÿ¢{HÇ/®´8Øüú;öWÉRDÙ&WT…¾ô¯ƒfh¼¤J­q¾Ádõ¡8 ‡ Eý~«Û¡ „ÉòNbì„]z9U¨C`d¹V‘ÔhìØÙ|v©’ù•¹±7S /Ùg6ÊwdNÃÝíPUÛL16Án#›JAc’}>w»d5Ý/deþÜ€¼{ÐÎùCût}ç‘G)ùç7 cóÚÙEwA‹õ„ØI멽&Éô«*œ“ÐæH^f6™>¦¯cQoÄßæt:þ66Á¸¢2«xr¶Ý¶Î90&À˜Àå'å3y”¬(¿¥:R¾ˆLHø+¥· ÚKýœ&©4¥ ª¦ÙÀ‘„ Šî €âÆŽI`¤ÿó4ÆuÁ‘„=R:3åÓËâ´(¨Yò'˜´3[¤[þ5v, (5$¡r`%ðÖõÆ_e% †M›!©!Pô¡d³Ói‚z¶»¬:¾’ô¥¿Å¯Ê~"T5~TLüÊc1‡MÁKû&»ß[ƒûù[¥t¼ß­·Yѹ&”¼A#õ| ç³*POt¤-~¬žG+£¨Ÿ"OÉbº[²H£Þ~N‡3QÏc·Kô‹–‚g¾j1wÄyú4ç¥É“Ãõ<GÙ§\áTe "ÙdÃü$õLüߥ2t‚îIû-’u~yFBØé’+g/"G,ÊÁ¦NžÆq¸I24Y¥A¨«±"ço† ñœIRÇâÞMhS3U‘¿ 2¨þF<ƒZžÂ_ó—ýLÊs\(ΣìÓ#Šrᯆœ¹}» &h/«éFò¥Qduõ öÉÝ( 8uSeç÷8ÎAR/U˜¦£}ÿoÙ®CÏèõh‚†*&I’éŸ~¿îpþ[ª(ÊÒ—ìÓÚR½ªPþŽþL2hcžäT”ùÏÛßjé× HÕ1'øÇ»Í}SHÊR­à«š ¯u°ZTêc!U:ðGÝz}úôàŒôüpa2(/¯$L'1Ùíl¼ Ä·sc£?.N› s«^PÙ¾„ëO?°¿/ß…8/пþËêçøÂnƒ€1õh{¤ÏвÅc²kÃòko—~‘—Ö@2Q/72&þ}I( ú5q–áú´'¦ O½iÒ=ž:eå58®§Àa„!C+'i%N΋&í …}/Úb¨â(´!wáz!%Bãa õ{ê­ñãÉ.R@ðšëTÔO$³4bΛ¶Zš-v&„«e/ÍœéÿÞË/Ó/?d+d±Jã¡ØI×øõ´8yö‚â(x—SñuêYq²XM}gÛ£µA…ŸÀ`1ê¿ç·$ÍHˆ›ô-e€0b‡`•/ËOâr ¥aœ­æ`Ëãúª]x?b32òÇ(Š£ï/[ íGŽ"œjEãªÕÃ?˜`Lಠ+([lþ‡}˜"§¾ŠÆý/j’K¾rÓ;‘¾~Ó—pš'ê ükÁç³¢ˆ#@ãH‘Æ"i.h,õ£žNGa–}|ê “'7Ò$R¼|RØ€™Í8¯õ+Ò$ÆÛ¢þÅô×_Ï…ÙS>Œ“—ŸW¡{Œ÷ͪú«ñšLoàPþ€1­¢óHû¿š«Ž¼ø{¶Ì˜_Ý—àkûÔ{t@úQº³Ý.y„”„I4´¥at̔렸¿q`Ɔ_=HêÂÅg¤4·û™Ú õ-uO-¾ÆÊ\’˽Yq1I¦cÕ†>È£ hå)]РrÐV@@…­M]×׃&ùŸ„ü%É99d« (—öÁ?'í‚ ¡e#U7‰U賦µpªÎÞ8?kÌCñ× úz§V¨ø´>{Œ×8O‚¤=¥½j»i¶#§ ´2÷BDiÐt[dFA2U4(©Sឦ*R(]s`L€ 0ºC€>@EÆLÞk5[Ž—Ójš„R$‚&¨ºpAGýœîqð]ø7î"pе>®t^ÈJ.>°#ÍHIð¶ Aû¤°QÒc>¹¤0‘V±ÕQ¼—=Ê}°*uÔôã}E2“³QiPT|‘èKH¥B0Ë9¨ÉHRT³Z@¿"&á²lonI'ІŒ—UyþNÂ,‘Õj:ç_ýíôP L’êI7©Ëü–ÊТ¸´QË5†ß%µLÑžB»ý‘²e OÁsói I$©8HSBià#㯡{˜IA¹âÂæT—>¨(¤×\ d?ªå~0™Ä^:/³¬ª9U¿@C\êÐÓùȘ`u“@bì¤î-´Om/di¹iRJÚŒ’ átIÎz:qÚ4ÒÔk‚ÆS¯F·ô ðOĿ풌Å'”ß™wáéOßyGÓìS2ç/¥t)ø˜•œ§ŠÇ‹æ* ²£ðC}¼ÐúøÚÙá(Æ’¹UDi kçŒ$ULÀHàK8(v³éÔ</ëÕpÄø±€ª(Ú—w= ^Û·áüˆ~?ONDr.óÈ 3áÃÐbÜnÌ_„Ûñ±½ÀѪY’1½¢shT^ÆÄ:´c³çÆÛÖšO!¯ŠÊïáyq=Ô˜¦Ÿ£›0ùwi£f߈½H³Ø§ç«öQ-ÇM›V¢5 Ý;ñ—}0ž©±T%+ž/ÚFÚ'»ƒ05»㲿²ÏÕ¾bH‚˜G$¼½Ôßà®)·Z8ŽCøâåâL€ 0:@Àì¯B0`5 yÜ{ªIÓ¦ú—qÒv8C7všL&µ¼Ѫ•–òrþ²œj›a¼\?^¿‹ø°™Š9Ù|!™&IViXqr­ _ckõ9\y!ÐÚÚæm¬Xñdªœ¼rdÌ”—Ú˜;ïH 8(e+·Ã9ø8&¿•hŸä:±•Ô‰X2÷\ӤũÎi½„"߇Ïðv½Ëø¢¾/v -§–8aB†žîr”¤™È÷øìVÛ5[ RÎÝ%xG£ÜLÃÊ.E<]@ÐØ³¥N´Sø9±)âb´˜¥£žJ,ùû>ü"6Â!Æ?Hšc. tˆœÚ*Kó¡ž{U³TŸÔl`ò2„bͺƥFÙãúCxègLótNkp[¬aý fÛ&©òÿRs± Úy,çÍ]^ƒóu‘C¡0M\±mBªwQU›ð•ýÓ¡Wu›­gÁªP?Áâ¨Èvœ€ÂI=Ýx$§n˜ú$âkù ¬–‘Acî/ôhcÌ÷Gçã'Ché›*¯9_(‚ÙÓ´gæ•ÓïÓN™ð9y ø¨‚%%GÎ>#ËXŠ¡XȺËÓÏ‘sN¨i&ì-Í˰ªDú³ÜZ6ÃØé°êP¶À‡"m˜(™LÃíNP^r’÷·6zýq`%«µŽÌÓÐj¼ߖǪºjÄоݦÃ0kÆy†C¾p>3i’¢ÌqšÌMÝÛUÞ5 ް}›¦¨Ò÷0¿KŽ|#þžòòr:`L€ 0&À|†&.Ë)úLƒêACÀsŪv…œ·±‰91— ´zÕÈèXõ…˜©š™-ÑJ…2‹FO™f4*/ߘøø´_Fy÷+“Ní&3¤Êä-/-—K«EyºÿŠ}FÚÓÂÓ½ê¤ÑRÁÍ„‰ÚM *ª+UY©å©ì=2ÃêÍ*›ß=ŸÝ>Ï/2>¾•{:_3&À˜`L ’Æ%—VÈYIO„Ýý`:r¨9Ú`Z¼Tn¶ŸÉÚ»*¶þ5ïMý«„ §ªŒ›Ó³þõŽ{Ę`L€ 4T5úz\[Ð` C«àxüš^[Ϭ÷õªR0³ë}?¹ƒL€ 0&À˜`>CÀ'… LŠÏªBªõ¥¸|f.EC$Af9g¼ù¨œlÎ0›Õy\Á›ϪïuaÓ¿ƒ&Iú¦¾÷“ûǘ`L€ 4,¾iF7 N°±Á–àðö×\6kXÃãÞÒ&nphNƒƒÍý&{§V®… 0&À˜`L€ TLÀ'5f«XŽÕ‹L9rþ Š›Ïw+C Ï™7”xbyÔ*;ˆW¦~ÎØ`L€ 0&À<ðIaãÖžÝ6csµ±áÈOæ´*ÀR©Ø¬gCK½¥Š%9;`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&P=>¹•{WF¾—¤ªƒ,æ°øì/ð^î€Ü®i³½ÌŒÂ×Ì’iáìØ‰¿»ÝæK&À˜`L€ 0&pIø¤ƒ¸{ÏMªW1Vv\Ü5Ê;Øý>_—Ÿ!»„PìªP:—Þá3&À˜`L€ 0&pi Ô Í!eë¯8Ä¿±»xw!‰ßÌBýlùvöĉ ~C¹&On&ç©)ªú¨PÕ!à³O’ÌÏ$ÆNÚxi_'~`L€ 0&À˜(%Pg„ j²ÝþI@ª#åeœ>© µ7–sÍ °Z{¼gŸ¬wéyû[-eGá‡Ø0DOÓª*¥‡5²>ùÖøñYzZ]Ï3µ¯ªÈÁÃBÆ“0}îúÎ{/¿\ ÷‘L€ 0&À˜`Làr°\އV÷™vû3ù(ûÅÑöø>ª¬ÜpKÏŽ§Þ«n…õ \h#óᬠÇX“Å´bŽ=zw=èw 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0Ú&ðÿ ™~üÊGËIEND®B`‚neutron-12.1.1/doc/source/install/figures/network2-services.graffle0000664000175000017500000000770113553660046025430 0ustar zuulzuul00000000000000‹í[sÛ6€Ÿã_ÁÍÓîÔ¦ ÁÔMÇ·6n|Kä$ÛŒfv` ’ÙP¤JRqÜLþûRŠÛrk9²|ü`IÄÁ… Îùp@\¶~þÒŒÏ:Nü(üé95ÉsC‡^ÔñÃÞOÏßý²!Ÿÿürmë_{'»g¿ŸîƒÀORãôÝÎáÁ®ñ|css{0ôææÞÙžqzxÐ:3 ÍÍýãçÆó‹4¼ØÜ¼¼¼4U&ezQ?L6Oãh ãôêÛ€f'í<‡lF©—ŠW;¾—¾\{¶õI_½ÜöRÿ³>TW:>;úËÖfvý0Õ=¿$[›“¯“(·ï©’|?JyIűʾ<ÛJÒnù%”ÐŒú¡ß‹£áÀ¥ÌªnÆÄ¹e#7=R½P§³ÏkÚ¨ž]o>žÍ Ñ\Öž^ׂ›^õé–$žï¸ˆ)mNlRü‰™zi|ÐÓ˜®p\΋˜¬!fÜSºÜš‰ÉK1K¾Ú ’ Õ‰.oª‰‚»hjj¿ï·nieU.ÝcEô¨¸i:wEêæ˜Ík–p‹¿k+zrsq¨ã·ªã“Ò=ÖTfáÈ ã°$Ûˆ³Š–UîLI¯oûåÐB«ÚqÚ¥mP~?ÿç z” Öö"/RY'|ô#žÃon“µ¯í.Ø‘ôlš‹Øˆdéúq¨Òa¬‚öŸ^v!õ{©»Ê‚“µµìÏÛÝ„ÚFÛë2ãLº¯Óøª½v¤BÕÓA¿ÝÖšÔ5%gÐçN¯åÚ’ñ(\$BPêpj[’JVc£†C ;骲:{DH°¾Â¦wd£åB£g4Ü‘wd#hŠ Ë%–D6"ÎFè=e6G­7‡¸Îê\%º½ÖÒñgßÓ÷HGneæÄ¦p ¨˜Á±æKZè8®& ÂáˆpD8>vÇq»w¿>cà3JÇ$9*‰dR ÇSW‹ÒE,"‹ˆÅG‰EÔÈ^^­«$Õý ¬ZÔ1á,jQJ,A%0ÒvLÆ9qá§Å¥tldäj2R"#‘‘ÈH$ÞïЇ_Œc^Fñ§öÚ»ÔüÔ×É}¾D¤ÄÌÞJÂ…$¶”`',“8à Æ]Û²Ð!\QØeEDØ!ìv»' ;ý|Œ°sîîÙ9;„Âa· ó_F˜ƒškï‚%”Ïx'ö;=mÜ÷‹Ç&ô2éšÔ½²EfK(Ñ»’ƒªèg"z½ˆÞeCï‘NUG¥êÞqW}jî\×dð`\Gòl•²´qúéŠâN îwˆ;ÄÝàÎoí¶Œ3÷túë,2ÒÕfËp$Ýj’Ž#étH:œQº23Jˆ–e2&- Ÿ$̲%r5i! HÄÝàn'ˆ¼OF UO·×Þg{Ýø<á®ФLÛv™„ûs8Xœf³¢ÄcH<$‰·Ä;9ÿC{i¼Éï‡AžEM[X‚SËá ̇‹È[Mäá^lˆÌ‘CŒš4{F@ë©åà[±UEžãލCÔ!ê– u‡Ö½ƒ®i™^í wÝŠ‚ß‘!èt¸a56n9£/W ض¥q);:ƒO„‘¸'52‰KÙ'#÷TªÎUtá„’ i÷ùžÍO+-çµI³f`"úV'¹±G<–ñ8/)¹3¹}ÿ`/rèEîøüwÒ^jëùÏfGwSufnl?³“K7غnÂÈxÕ [\¥Ëúƒ(¼ám̨†>€}©†òø;†‘›NÍ0rfº.£pQ æ’QÏßE³ˆf‘qfÍââÌ"tÕõw6‰¥ñ J b³¾"3Á6‚SH¡K,AE3›X?NdéGC.›Ú+ý{ææ×(½PýÃìÎkrì»[œ•Ÿ s»É³~nö7ÿxÁ#Kptc‹qî˨rbRápGZV6úÌ4ªeTU4ªhT6düwÞÂj fÙÔµÍÞAˆuƒ›2[#àpÂl.Á,fÒÊCÜUñÅïâÌÎgÝ,»C'uNªÅª‹†ÆÃx{a´tÐÝÏ;›H$í»d1=Úl’Ê¡ºŠ†ßÇ æL˜`ˆ$w\*lîÚ š9vÃé™Ð—[7(禰¡'³Ž#ò‰žiáé™xz&öšp_ˆU:o³¼§üR»ÙD©Êø-;*SŠSê¥iæ½k›2.&3¤(»;¥R )…”BJ­¥J»-ÇÑЕÉÕsîud!8¹ú‘O®¦¸r'W#Mqrõ2L®n½9œ9ôdKd¹I‹1ÐQh·<s[õÆH¾FXffΘc¹±m m›<4¤Pܱœ§5fètÆÐC| >–Óµ"Æd‚f 6tœ{W q®ŠŽnžÊ¯±ß™µÞÓÇ; m ”e˜æ0j<Ì”eß©hLã~øQÜ}êÜW ˜Ûä‹è2Ébâͦa$ªArÍ!_Ü}~WC\×Cpfõ4b£I™‘~ï'þyÁ‹k²É_‚w 5 2 1 °ã2Ý@IDATxì|EûÇgöî])Ò{Eñ}í½·W}íü•$€ ¢Rr\É](úÚ’DEÞWß÷•W_{ï¯Ø{W° @P¤¥ÞîÎÿ÷ìeÃær¹Ü%—ʳŸ\vwvæ™™ïÌÎÌ3m…àƒ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&ÀZÙªBÃaL Ù \«ÏéWõ ñó‚P`U³€=dLÀ&p½~ûN%áÒ}|>mÝ|=g9ci?ÆêÁ?)Só,ÊËy«ýÄŠcÂ#€öL íÐõ×¼¿‰wQÈ;;­*ÌÊ Ç‹Å„üü^F¥ìÒGìR¬ë£ËãÙÝÑžUÆ…B¨; ©÷P"ñÏÊ ]¦”Õ¥0˜»0û­ÁN¦~[OÍ(ÛO(Ù_ùÄw]3¾ºcòä²– [[äH¼&ès˜á°Lµrê¼§Nšx CIo· óô‰[³¶pnhº–åG(¡žª4¬‡ÏËZ:®Nz(áݶH¿i]¼ð8e²é³Ê ýþµñìîˆÏ•Ÿ“Â쌸ûvÄøsœwlÚŽ}Ž}[%°N¼;Ä?ÐO¬Zws}ñ(/±æ’ÝâðšCë³ËÏã˜p÷ÝéÊR÷+%d†B}ãÛnÙ§º®´LpÊðW.[o*ñš)ÔƒVX}P²±dÌ?››tK„²-qtødø²yw–‡+WR-sÌSu./µîpÞk:W(ñcyxóæÌ@ý>Æ/K×wH•M!§-¦k]œô0òo® …úÔeÌׇßêo—Ç¥êñìñ3&Àv<¬lìxiÞc‡Öu$Î¥K—zê É̽¿Pj×Dä%b§1iÞ·öôS=ƒ¡Œ4GžY˜¸;‹$4•i|~þΉÄ-;yÓ>CZ]Lv¥PçÒ¹úÜ%’‡æ>ÿë{žì{á–×ÐpSÞMÖ_Œ2Ý@~+aÎ;kVw8rMïNs¿? gçì%’÷’ Ó”9sºP¹˜Œ¶ËZ θ­%%8I§ÏÚ=6VhB.šç^Kïcá¯)|ÚA…zηÑÂ0åi1ªÊ«Ðø<º0¨5×Ö5ÂúÂy¦XÝUÌä¶S§O§¬S5†”[ž42ü…riA^Îm»‘a¬C‘÷nP†iqsõ‚YM“ÓÍ (NYAü5á ‰xý‚üÁu¹Gܲðl2Ç;ÈËGÕeÏm>&|¿ThÚ_£G6{˜–¶ïÀNi>ϞΎM‰æu’‘ Wøõ ˜íæ‘ÞKæMÿÈ ƒû켳ș?ã?•žÅKWšVal‹p«{à·éóæ•·(C턼õØþl/%Yî#ÑrÊq ‡ö1Þëì±¹³†ʼoëÈ—=ðn]…<½Ä±ëì¤G‡NžÞÍ]HïòùÄóªhû4¥´R¨•û äžýœòMq8t#âz>òÄUÏ¿ßň+¦5FŽDÞËRÃa{¤ÚL¼ßÿtÜ:g¼ÏOŸaxïGXjV"^…PXQÒ“U”—ó†ã.™¼á¸I4ï!/ý¸vF\}Ž[:Wq)@xv—mö{fæ_…²yÜíMå!ÊS –¿àþy¡y ó¦}á–Å×L µ¨·g¨µœÃň€%DÆ¢¼éŸ Á7aº0¬û¨N†M €¢ñc45è4º д‚*V+l~ ã8Gž%å>¨ŠQ¡þa›I±…ÿçôÓ”øv>åW\ûPi160gOÇs†E¥±'dï³e›qºc:…ž£BÿÑ1Ëœ=»á/`Qö<4•öRR½…†ÍCh—Qà óÆ>³§9ªÎ˜[߃dY–üÒgËPYõ@Cæ4ÖÞˆ²^ë6[aç&ãyTl»z5íFGÑ ‹h,ìA²£ÙæR†Ê>×ÃÏáþiT¤ÿ7î3­pÅ;±ze³sƒw@òßa Ú? HÞ€ð~f¤xÀ/ù3¦×üGÓ´¸aíëƒ?)Œ›ûyŽx :Œ1ï=ÒnìXJýÜŽ“'ŒÉ æAy[Œ0zÀð„íM"ëØ!©z‡ªDóz²\¥ÐÞ¥¼g(ëʺ‚h˜êJ²ƒ2ƒpû¨+]iQyx Ê+ ù'ï0½Ç¯!Ïï‰.‹ÿÁq­Fz•H‘L9帡p Œ>.0goÓ2_´ó"ùSû4­V§Œã.úl•X3äx*÷ðn]™=#d+UÑö꺧^ù5áгÈsozCÎð>?†´ìv÷@)z r!±÷V¿#æhˆŸí')=ðçìHš¨«¢ŸSg‹`¾[OçOœçÉæ Ç]¢yϱ}†FJÜ5ˆÓ×}}GV¯ï‚ù]²îƒýÝÀ ï²¼¼¨LÙö¯ÒLh6Ú?¾g-A€zæø`m—€Tö” ô^]gVü¶å|TXG›¡k¡»‰M›QFÙbØÕ¼>qÔB=P=¿•ûHK™ÿ1•,@Cr?Ú^SHîÝ{ИþçC¥H›XraQÁ¿ˆ«lK†©áZ½N€¦ ‡ßBå(¿De±Ÿ¥¬sñ|iÄUä?„'¢’D½"I†}È#û'£qò¬è커hÚ4Z[`crC ì %LõìÑ¿ûúäγê³eå¡r¤y´ÓÍô¿Pmç‚FŽ Ãx Vh«Æ³æå¼ÇzÍGJ @;tº&N\Ì}Ýy4^Ï[Ï!Ngi«Ö]ó¢»üþßpE  2­§î7 Cµ{©±ÎMsä!?<&»xGºò F(®Q–U„¼7 ù뜚n“Ò´¢±tT’ÇSµÅ0MMSV˜F‹~µûhxõäõ¤¹z=K¤aéHß‹Ákr4/{í³å%ÅK*õ@tp¢ï-سax—éлëeöbrPÉZ#DÃuz´ºO¶œrË@À†¢òy*/vë–~õm7ÝTâ~žÈ5úO2îòOýïâõ°ÿÞ±B¼ÏûEÌÖ%kóÖJŠ×©ÿïÑN¼'éwt/5Jžßó°Æê ØY’Èû3NŸ»K8\=ªvç@X¡^(ä‘/ðìOï¨3Fá{ù‹ï…yW”±ËÜ[*'7H˜û¨'ï¹­:×Y¹Á[1JÅ_¾Ðß7 y,ò>ÒôÃÊóZ”Û:y3†¹Ëõo½µÓÖMà fúI9åƒ ´ Iõ·‰q w(è ³çŽSå…éÕ¨`ÐŽùÔƒ˜£,‡*öÿq+ä eê½FcY Ó~Y?*!y°äñØÊ:ÝÄ n7¿ËŽÒ@=ýCî7èñ=Óé '{t°8”¦õ첌̨—ÎR »'‰mÞNÞËÜ GznO5‘ SUTL™‰Ù‡Ìƒ5©]•¨¢AßPæËßS ÏEö¯V|ÈÏ„)¸ rc7L¤|”®-)¡³s(Ã:œQÿ«§\Іý8 £"UöNqìÇ;£1½{ÕóŸãÙs?ÛYL)c ©Nunç+Õ=nEƒÜS#l¯£kŒŠä$;ªFî¢T¤9ò3¦½uÈŒÎ/]»¦ý y¯=Ü;ÑöÑÑ~§ò¾h¦ÿ9LŸˆi,R!7SŸ=HXÆ?«dÄ’/¯7„+æ¡Aû òE¯_wN‹öóÕ/Wœ„w¯/òíÿðžüýÜ}_5Ò9ŠÞåÞnW; n²£ëÒêë nß»ÝT_7¦œBZCÎoyþ¿6DÑ 0XÒ²ËÙÈ´+ù8Ú¼%<»:|q.H1@ÙŒwD*¯×pÇûNýúM¨šLÎañOìˆ(šQªO–ž¿Û—óÑH/Ãõ\2Š:uª<·Ëe(ÁÕ/ ÉÕ«.âå½h»tO;mAÑÀ´2ñ¦ôõ:_×GÙ½ôLUªtÆñ®[Ñ JCL#eEƒ`ðÑf°²Ñf’ŠZ¢Pàm´W1ò :Y†YTŸýªçgÑÙ#µûbÙÇ4Š·ÉÜ”jÿXÏc™YÞWQ±š¨[GÅÝ"rXB;Ÿ®Ðéö*úA_A#¥›\óûq‘§øÿËzjpwB¥ûºS!š<µpäýgáôéTÛu]x¼Þt wg¸Œ«/Qñ~¸(˜ƒöøÜ[ôã £òeL3¨)Ìéæ<ßUì§áYë‰×#ž!s@ÙÕý õªE§Úr·¹m·w7š¶†)c²[by­·-CÉk=¢åºï©±‡ûdæ ‹Øßðjÿu»q®1—ÿD¨é9d]8Ôß1oè9i޼÷Z¡~c­øÛM%ß °™–U# Þ¦p'•u&c7àçGoú¼Ì@ða„ãýžéç ÌŒåo¼¼ÞP®¨$“_¦0/öÓ´TÄLIÛNôs÷½%*#ÓÊ„|ÔÝ£îØ¡<¥åaç>êܸrJÓn!M>Jfƒn±v+“&¼0cùFa†O@CœÖ¼¶PŸþc´§ý=Ó>Â3jhï–Ìt;M«êÔ1­™´™Ê–#Ñei%” г:ÏyNgŒ@HgKóT+ Í$Ç9âå=ÇCå ­é©t3Áñ(žgEw°ôG| y›ØS1Es" 92øÌÚ"V6Úbªq˜ë$е{z é•h䟄]]§E<° p%‡ À·ð­…q¨þýCÝ`÷ŠA9pzË㉴ŸQo2dbºè9î–|[I±•L@¥ºñ䇽‡^·çlËæöŠ‘]i¢QP] ¢BªòWþP—Ç}Ä´•ˆs•ì®±*%¨;ŸÔåÖm÷•ÛŒÒÿ ܘ.󅹟'sÖÁZ˾e¤m°Ímj» é¯Fn7\…ßB Z;Ò\ó*¥ ÚJ{$ç/U}k<ˆsS5ÂÔ“¬¤‹ŽŽû(é1Í«qvú`êÐ(GIߦ"Í¡˜ÅäOA›ÓNä7(±­ó@¤/Ô߆_i-î©ø‘&=ýgוâåõ†ríëëÿÞÛMðÿš×ï£é,ÐðÏ£†7ÊGóºÎÐ$†DžÉuÙA^ª•n©(§Ò4oBe@]ár›W­Ýš†F¼D¹y_½ ‚ªššˆò0º|¥ûb#øÊW“ä•m)ÛÍíWÜ목¦îäòrq.ò ųórrÖ#¯¹r>HhpQª#Q^n°•œ*š7Üá‹—÷\öJ±éÃÉ胺‹” _†çôØŠçñÐ캡kõîÂæßCéÎqâá’Ç—L MÐÚD(9L AÔs+=2“¬£ é6ûCd¸Fƒm+Œ``+ :Óñæ×?õECÆ^Ô‹zîÔ:~»£Rø ]T†É‘5†!Ž'WY3f€_ýáÇ“£F2Ózw~…)¶ÐuF?pO * dõ´%Ì`›IAëbÔðBøÖS\(NÑ–õ_£ÍbÝ+aM%;¼8_Jó©cÙKĬG8g}"ö;}…ÿcpþšTª´(Ÿ¦3á<½ÇydIw‹c?îYU+fƒãÚs=L[µn€ÝHAZGO[p¬õéÁó@ci=@ÔâÓAÔ¤9òC/Zý#¼ !¬â=yâhÏ»kß‘0Mi}ã!^}¼¼ÞP®º>ÓÎä¿‘¸ä¶ðHÞÖ-•AŠCáK™ž„>öȨWœwñ®õž§¢œê)°qE ìB· Ø›xgö(ÙXb¿—aŸ×.cÑè–n¯ðN8ïánu”±´Ø¼œÊY”Ù +À}µ~o§rôþëŒx¢,Ešá³Äc‘0hOÐ{]V.ΦûŠß·Aéw/»Ö†æˆ‘ÿñòžc`Ò”i^a?Õ-\f]ê<‹>ÛS6½>ìö§-D{Â~Vü£|Ô9Õ3Zß3VBÀÛJÂÁÁ`)#€…s/¡Áz?*žÿ«‡iŠÑy¨p*¢=ðvõm¨ U)æ"Ó¼æ”ðû%ÓÑË~Îw)eÚ• ¦k=JžÐ4)l…ø<*¾‘ãfÌ>½ƒ_oÛXò'hE?»·îEü*hNÊîu'·Ñ)+X\¹3*$kg±«ÝèuÛ‰×8«alSq!挅•—W•„KîÇs{ê—Û^"×7ß,”®'b3b‡*~ìß”YbЮ4™¦™¢ÔÂÔ˜¢„¨.Gº>•ˆDÙQü ÊÈJ½Ï‰4 áÙ—&ÊÀMì9óWì{»Nkc†J&,Ô©Å?¦ý8†Í™æq‚Ñ¢”Ô¾\”ç¯íJ<(ñòzc¸z|b±ÙHâËšû(D˜oYUÈWë9E¶pFOuJ£N?Š­Æ‰(/>š üLR}Òó8†‚¹8·÷¢l¡òVÔ tvŽÆäj LS‡ Ø åÙͨ^FxnÅöâïåMÿÀ‘ã>W"Ãbü©›¶…¯D3 n®¥â ð?ʽðÝ펯™@k#À#­-E8<)!áët*4 Õ¹´cêZ;°Ð90_…  3-ŠN‰ÇUBì©R&G{*x#ѳ&·úzw&%Ä>NØ’òd(G¢"¡^½êQ Û’&0 ‡”CèëÈÊÏÇLlû‹ý×Ý‹ cÙg†©·Í <ŠÅŠ´Åå×`÷ÌžÏM*Ÿy*44´°X=¦øxÜùXH3ÒçÓîлÛP,n}0Q¿úýÅö)Uì[6WŽKÄ)¬È:A«zDc¸ ‹x£»Ú.¼Þ•1\&gÔŒiž\ÀÚ¸íFp]¤Þ§w¯ò14ZZ5å$4¤W ½,2(V’=L©»¬©ý@cà‘£)Ë)džœíÍ„¦ã­ñàû ÷káŽ1T ¼‰JEÖaI±WCü©Ç]^šÊ:ŠÆ)(1RéÐ!wóó¦}…|˜ÇÙS%#ß6²ÃöÝþlùÈõ„/ê±41Jw¾ËóæzÑw“Ò¤e.­ïC‰´¹í„ØÏ;l”˜²§zUb—´(á|ËZ-V6ZmÒpÀC€v9ÁŸ" MKÝ ¨Clyò2Ç¢èÌØÏë0¥á{š4v‰eƒ¦JAÑx•ÉN™7矆JphÏ8 ¿ÉM_'|0L†Qy`ºˆ8Ζ£mß!…îÓµ.Qu‘†‘öqÛRÔ¿RôtÑ!ÕÛQO’ºECª’ÐbEŸæ¥]sJ1 ßÈÇÂõ¦?*p>˜íÛÉÛùúè)> tÿ§nf‰„‚z]5¼‘ìBÑ»)[¿5²`¼Çö7”:ñý¥¿·ßuXø†Ë9±žÑ”/(6ƒà~Ý.âO«;HS;`ŠFÌ<âØ‹>7gšGûÝžïËys1r”¬0Œ ËÊ,L§Â8 ’$ÌÌŸ’]ÈùK¬µU¶œªÆpm™ ,§j J©É‰ìqʯÿ÷Çw]&ÚeU´^֨ѬVuA2_”Oäýñú"Jš£áíiäµWxkl䀅äS‡mÆNŸ(s¿\¬q³±yÃ-+Þ5Š&ŒøF¶·ÅÈÝ`÷wîC0¢û@ùú¢û9]76oDËKô¾[·ãPNáÃ¥Ø|»¯9î¨\±·zv \gòì2ˆFÚ\Æ|ÉZ5 —VRh·Ã|±úDj¬ÇrN[1bG”Éh°ßFõ¸~ ÞOP!mÃf¨ýàæ J¡ñÛöÁõ7Õ24ù0”ˆËawÖ^ F/Ü«h^ö. úíÅÌd½êUS¦ÔɨʺtóEv ªbÛz•ðI¸Â÷5Ô;¶Rã¹ý½9“°cKÈË,6–†©M/aì´"0”q!5ÐÑ›üþ!Êi£nñ û±½öêòmFɜω7'¾QžÁ1¦ŽÐ×…Çdø…­ ˜¬³E‹PxL ¸KûõvÔfÏÏÉÙˆ_¨ÈGmÞ\qb ö!~ ßH§Ïàv <Û y⬱è‡ëMØ8àrô4B1­ãÀ.bP>ÇÇÑ>“‡Ñ8ø߯î.±Î‡œ!H‹O„·çB·ë7Oû ˆ÷ñ|>\ö¡½ŽHʰ¾ÀâOä—º–JóºCÔ€'–9oÁÉÈßÇà‹ñ ã)r Þ 'áJ›CùVœŽ¼€ +_Œî!¯/P^¯œbê|;'ùñ¼Oo@y¡}—Á %¬+‹À,;ZNƒË©hAMp_7í³1¹Á9XWˆˆÇ[u¤ehÓÃ¥æ¼7gÛÌoð>ÓöÚ4½©;PƶßÇWJ{ÅEŽÓÄ߉/£G¶ †¼êòב³‹gú;k¬Ðo(¿N!3ð~Áyæ>7&o¸å$sMëɲõÐ(+¬ÞÇ* ÙY¹ùoÓ+«ÒÚCæ[(3ÞÀôÖ?¨ã¢/ƇÃZ¡cQÞ” ¯¼3¿Ø.hI<²Ñ’ôÙï&'°H¿iÔdÜuØåf¡&½#Pñ/CÅw8‡“¨âÄùÿ°Ë’r1¾Üñ«;°…3ýø¶‚–cïR¥Ä(T¢h$XöÔÇäþ%ãGûi¬…ÊéÞ´'ûPVbV‚´xºŸwÀ¥ðoÚ%»£‡n $·¢w1Ü~…†ò±P4êžþ³Ýƒ¤¯ºvK‹8| ¿ÎÂ"ô›’ {äDÉÓÑð¡ñ_Œß³ô{|ùX}ˆ†I/úVe‰õtÕ6µõJ&ÞEÁ@&¦TY¯A™CÃF]h§¯P§ aR‚ãC>%ˆ«hÀ'Øõžtà0Ú5 …Âhø[Š”Ô°[LÒçˆè½òv啾+àú}4tvƒ¿!(7wâ{+Öø–Lóú–ès0[aÛ• k‰°£S+8ÏÕ³é裟†r!Ù(чC¥H?”Êdãik_4$ñ½ú~ˆç"|Eú:ä3–܆”S±ä4…Yϰ¼x=ívÁÀÞ‡aG¹™xgL(—¢Ì¼_L¿eÚ¹0ûÖÙ<à _¢ïäTuꀜôÕ=&Y”æÈ‹öæÔéƒwõŽîsãó†[Zâ׋tÿ—Hÿ ˆ‡ùáaÕð}L’ê†#ìÿYj~D™S´kâ¯<±P÷œ¸/l“ ´,¼ƒ|0&à ¹Ôo|ý=5 34á]wÌ>»þFSœçÑgÚ·ÝZ_º»å1=ºv\A‹9£í¤ò^×—¦­5V–Þpú.båYžÊp5F1éóïV¢0ꮡ]x³ÿjh¸eÒz•Í›*0r£úx5Ï1 ór° MòÇ„üü^•eZ?åé±"Z9¨KF®þ@ß Ç M­S>oþÍÓ~pÌêrOæ4-Ì•ОXWµÃL<ë5žµÕ4§éoÅáÐé¾®ŸÛÓTjĪåoZš+•ÆÆ­{¦‹Œ_bhÆ#”l9OVK<³§R•Š¡–òléÖEýJ  ã…£1ïO<¹u=ké¼á„Ë®_~Û²‹á½¼BÛ;¯N´Ürdð™ ´¬l´†Tà00˜@V ÿ K™´X¾;ûdÕ…S î€8IjÚ¥…yþÖe/ÕæeCt/ Й]S Jµ_, 0&À˜@{#ÀÓ¨Ú[Šr|˜@#€©ûÙAÖÄñ‚ŽE ÙÏ=’§ÄÅϘ`L€ ´"¬l´¢Äà 0’€¦½aÇÛ×Ó6»Ñk2Æé³vÇèÂÌó>“²_-¸yzÝfvHfi&À˜`m„ïFÕFŠƒÉÚ+¼œ÷0Ej*‰ÎR–ù®Xµ®»þЗ‰1³Jô ‡ÁXúI_9{Êëíq Oej¯9ãŘ`í‘+í1U9NL ÀZ¹Ùú¬¥–a]‰]½vǂ쾈Â6ì‚õ14ŽŸÓ¤zøž`îO--øÿ.”žèהּDPØO&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜ÀŽNßÉ⃠0&ÀRI@×w(6V÷ïç°F×G—§Rv2²è‹ì“öC¡~ãïɸc»L€ 0&ÀRE€•T‘d9L€ ìð²s÷°DÅíø(á™ÿð t_Bÿçºwñ]7wêÔ­ÍI×_ó‡ßÚ†¯/( æNn3Á—”PéEÁÜcšÃ¿¦ôCו¦ëÒjJ?X6`L`G  í‘ä82&ÀššÀ”9sºX¢òøs´Œ©^Ÿü³&µK¥Th€‹Í©hP\uýxCib²”¾%M÷ö(üy‚>g@{Œlj 0&М¼ÍéûŘh¯6o5JíªiÚ•yþ¿WÅó]œ—¶Tœ‹ò ZÊï¶ìovMË`E£-'"‡ 0VC€•V“&ÀÚ4em£ðKú¶¾xdê"¼~FBŠÏO*u2dï®¤Ú …öTaÐ?Α•høûÉœizRq8t9â|™Pboøß ¾“š Ì ^ÿž_*4ñ4¹7 ñœaXÖk™3BÇG¹Û èÓ óqØû^Óädô܇ëÝMú,Ê®†yì³:kŽÒ²/ ù>Ž=;\áõïAѸf£Ÿ Ec”˜UŽ$Ãç8KøL ü§”(AXC˜j6y2Å£ãsö®ô¾jô8}î.nÁ´ð£GÙàe+Yº¾4 ,ß–€ò"Ÿ+$˜*¥n^=âv E£/”´}jšEîÀs?\ÙœLéYŽðDF£¤ºá *©ÝË›1&À˜@ýxd£~Flƒ 0&P/jDÉ ÎDcx^ź-+Ð[~w_×ÂyúÄ-nÇjõú1X<~×§¼@÷Zõì¾1àJaYsp?¢†}%üæßtÌKÊJEÐ0—âþoŽÝµÆÚ³ ¤tõù¤½¥ØX1ž¯HÛoApê7UöËä`*ó©¬@þÁœg÷‰œ‹òrÞ“ê+,51Ý›öÔ<}êêDܱ&À˜ˆM€G6bsaS&À˜@ÒìéJJ‹ñ×P(n-oYi8s¯¿ýöŽŽ0¨Åȧ.EÃ~$…|ãGE`þÊ¢™þjEƒ,cDb ü”m;Æ¿¬Üü?ÓšLI²⎹û åd$ž¿^—¢Av“ Ÿ[~C¯#Š‚ü aëO2îòûC@žB|®rË´”u Bøùvvj$F+^q)¶ušB%ì%¬‹Ýîùš 0&ÀšŸl4?sö‘ 0vLsû—!z§eë¡ý¬°ºáK6–ROþ±ml ; DÇ1<($Û(;áN37– Äùûê'R9£ÕF}µœ×Š­Ð*É€áÿèÜ_ŠuÅ'°çKÕ6k^H%‡bºÑ“5MkÞ%¾šÎºË̽?v¾BYbO(´»3b°³5ÊÅ£Èê¹,=xhø0söìnb«qÂïwì@áÚ JÅû¨ó ÈÜ=ÊŒo™`L ™ °²ÑÌÀÙ;&Àv U‹·¯ÆÔª”¥ægbñv¡žó-ü |€EÜÕ Ö5zí…ÑA®w›£1]â¾§kš²…“¿C1‹u …èe­ ¿5 #‹GeFÛwÝgHa•»îk]&¾Zê1Àï‰Ê ß…¸>Š)P M[-5µëRžEô«¾ÞÀ‹ÅFðWÂðC¹-|>{¼Þ´šÜlÄ:”€¹¬síL 'JúhH‡&À˜H=V6RÏ”%2&Àª tödü{›U2_†­3`ø-v_ú ú²EAÿÕ–páózþ^6ýÅÖŠ“5±Â@/~/éñ,‰' ÍéŸ0Ò2(®…/–´ »8¼|.¨·±›ÔHÇN¦~[O!˺‘²åöð>Ü_»téÒI/¾üb(W/.ЧüêØ‚ðâ3¸úÞ}!Õ Ú™ÊedAzšëÞ¾$¿U¸Ì·Ýçh|Ϙ`!P=dÝ!ì– 0&Àb(3ËΡ'–GüLg4j_…Âqòx0v#™,%pÌ×s°Å«xG™j¤eªQ¸þ @Ï©15+Z ü^†Ñ„SíÆ}ôêûT…/–øõáå}¡0¤c.™½Åmµ³übL‰ªÕùåb1»ó+Ÿ.”©0´¤Ú ](ùÂ{Jt|hšäˆIY¯VÛ—r5üîFßШ6#áòÓÝ÷‘k¹–Ε¦ISÚø`L€ 0F¨U¸7B;eL€ ì°0=h"ofÈeRyVKÞÙ”òxSYÿ‡5_ví’N À±:Á” ËÃR¼‚Ý’¦ áýL‰Š]±\ë:,¾õ Ûöû·ñ™UVs.½¾´aT\%Œ²³f„¦j–÷%±m|gãÛa#ÇÆïÌÜ­%©qø<òÃ^bú÷˜µÊÆ•ø–Æ2ô­7Tø"Ü_F¢'2-VamËóBY·b Õ6_¯.O¸…vëžþ·-›*ÎVFÙó7 ñûÞ²ÂZ¦±ò¾íë9ò^ǾÞ׆—,eÎFÜçÊ4õ­Y!éCŒ·ÀN©e'ï¿û²—>ÿn£²¬ Þ ù|&ïJåä3`L 9<²‘/¶Í˜ˆI߮،EÚW(Óz ÞOM%^ACöFŒe<§u§ÝvÓMöÚ Ú–Vë EŸúWJYZªb9zá_ÀÎI“c Žc˜áíü0è]ñëÞÉÛé_q¬Úh ¶‰0•a4ä9CU~‹žŠà÷AŽÛƆqÙô‡¢†ažJS£0…ìB„÷gø÷*ùó¥šô\ˆ!Ÿg0¸ÏX¦ðÑ¢zíáy'V¸ŸSl“{Ì~ÂhÑápÅZÓ²‡¬|´£týxL/‹yÓ>“RÃÖ¿j„eZ/›eb¾ø‘+½’vèú̱Gg{Ý‹Ôn‚Ì}*…Z‰û¹§c¹­ò5`L€ ÔCe,L€ 0&*cgÍê!+ÄΖ×,[è÷c}Bt‡}µWX‹àyùóŽZéI{ì±®žÅÝÕîRqAÈ[^Óçž ÿ—ºÂؔᛤßѽ"½\.œ>ýxñÁBøSHÓ4Ïy9ïÔe—¾L¾>xìÃVÇãHöÖ…CýÓ|¶Þ©_¿©.yŽy¶>kˆ&ºþ¾@¿Í1ã3`L€ 0&À˜`퀦Q=ƒ)R_´ƒ¨p˜`;$^³±C&;Gš 0&к ÐÈËÚðšq–Rg`þâÖZ`L€ ÔE€§QÕE†Í™`L EŒñ±¾Âþ ­¹˜‡Eó {ʘ`&À#FȘ`L •<šç*%­ÒzvY½(<•þ°,&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0¦! ›FlM©csg 7-óT˜VRõƒ§]jÚà;&À˜`B(!¶J%‹ÁâgæyaaÞôÚ"®÷Úbª5.Ìm=ïfòÆXB\‹‚&ÄC‹‚¹Eîgl;6ÅGÉ-È«k„”¿H¯x¡P÷¼=&Msåm±BèúâkÂk®R]gXÆ)…%”ü þQ%²­©üe¹L€ 0&Ц ôE臣î胺#4&·uÇ]ý}ýïÑõÑå­9f\ïµæÔi–°µ‰¼«ë¯y‹Í·®UJZ \Ö,dØ“ÖD ?3B(Õ[…U~¦?¸RIíº¢`ΓMÈ&ÙÈ ä‚!#’R>ßCZ†öÌÂéÓÿhªˆ°\&À˜h?ÆÎšÕÃ*µÎTJ]Šßiè°ú±SÌ}±5Æ’ë½Ö˜*-¦Öœw³f„NT¦º[ µÚf· %öµ¥ LÒïè^f–žc)ël!µG‹òüÿnª0¥\ÙÈÌ NR–¸MJõ%†Â'„ÿkªÀ³\&À˜hÿ²üÁc0½cbº¯ÔÄ…y;[S¬¹ÞkM©ÑºÂÒšònfnèe©BZ!˜`L€ ¤†¦Sɬ@èaT.磊9£¥§Tq½—štݤ´dÞÍû›eb8?)õº°0++¼#0ç86Œ@æìÙÝÄVãcé‘™3ý¯4LJMWX@ßøƒÅAJM¢ V4Ï”%0&À˜@MvÝ‚:¦_áWTU÷Ô´ÔLw\ï5èvâMKæÝEÀÍ#Oîï;r$+í$C5a4ú•OÝ ñ•–eÍ£ÍRáUJ” ÚuÊ^ Ž5þ5‰xµÚ’¢ ÿ.JÐXfí5¡³f„NV–5ËÄÞ/ ú³Ûk<9^L 5ÈÔgêè3+îÌÉY—ªFCkЇ¥õhŽúÌñCHya^àž†Æ>S¿­§W„»gtò®½í¦›J*‡Ý%N€•ÄY±Í&" õ•eÖ%ä±Û6–ŠÝÍÒ¶m(¡ÆøJjwåùïo"¯«ÅF>ÐuU%í/]m˜Ê )û)K,¥õrµØXfÕS‘5#x¶eª'mÉš¼½(/pC´/™¹Á™£iZVAž¿0úyCï•©ºcö`)ÔÆ†Ê`wL€ ÔO€cžþ_°–òX÷/Å·¢3¡”§OhäúÀ[ýRږȇÇÖkº>ª²m…¼u‡6ÓüH uˆJÌß/E¾Z'¤øVI¹uó¿gPj×qÕStQå‡&ìéûI µ•%ý*\Ö—>¡¾y“!Çoö{Ñ ö7G ç'‡DêΩX žºÐ°¤Ž@æìÙÝT©zÞRb:¶X;P ù. ´ÿâ÷î÷Vã&ÕuLî¬ÃÖ„W—­ ‡ŽªÏn{y.•š˜™;{ÿöŽ`BdòF[ÊüÊÎKÐ ,Gyú(ÊÒ7À†:QNÙÅè¿¡=rZc,{¢8¼üùö·Ö'䣯‘ŸÞBXVáÛ6ƒ”R§ ËúWV 4µ5„/‘0@Ñ‹÷â(O}Ÿ/Ÿgqþ÷{aô· ÎOÛidëÁÔFÚnÒ°+Ùh7v•î¾;½bÝæ' ê ñ¿íõ¦]°@Ÿò«#š¶íK+û¦:Ke\ØèÕOM¸&‹v3*‹nB„â|4O¯hÈ,’ 43ì@þY¦²ŠÐ¬”R_˜ç¿× ·ÞÚióc]]—ó$ýŽî¥á’“Ÿeí%N)ŒÇû)‘å‘SŠfìÏL™3§Ë¦-áÅh¤_` E_—ÆôØ6qdQ(QÿM- æÒµ®+m­9g™nüN÷tp~Šppþ[a‘/…I·'9f 9³²Ñjì&%*Öm;= Çâõ/IÏÐΛ—3e½[ðÝÿ©û¾ª ¢2=^)9$üˆ¡Ü—¤§Wn¡žUêØÅT ‰è©¸Æ#Ò.´DåQ–’×àõ⯅›‡úy÷̧ávš:Ul¬`XøZ¹[P8£÷Þ¿ÙÏ8Hˆ×µbcÙÇp÷¼ØÛ/W¯ÏW– ÿ+ ƒ¹öGn “¶DÎöhÏVc6ö¿ |Ëq¾»B,ræ­/luù¥4qŸ°ä¥`tdvnð*Ø[\—]Ç<™xŽÏ½oX·èa`ºÌŸ’B{U)QAYï:‰«Ë:_2&P+²Å©eÄßÜŠY¯š—þÛi¢ïtTyz ÞéѳÊíÏ5Mܾhfà1Œ¨\í*g×HM,,˜é¿«Vy¥ä{é´œŠRkʇcPõrº·ë¤yúÄ-¾ëB¡>ee}¹¸²(˜{(™9GVnðtüE“Úí‹òüdåæÿ¹$\r'ÊoÂ3å÷çUv PFϧ빌ƒ)»TçNºkðbjìàŽN«øG¢ùŒ¤LÐïîZÞB}w¬]Ç ñ¦8} ¼žüB}Ú/±|¢ºpñÖ˜¢{ÖDþ k"gŲ‡ü´3™{}òEçyÕ´éÏœûDòS¢ñqÞ›4!Î +9C‹9¨ ѹgOQ~0;wÖÁø^OfU=?aÀfòc%=3Šò¦W¿«K—.õ¼üÅòñ›.CaÏ@\VÁVR…újA0w]7I^—*%zO£¢â£eHËšC…ôÀ¼œœŠFt€è%BoʧP &âYL ø…Ñ.˜f5Yë>¤ž;—›>Pbö7DåC˜žu/^ð¡x!!_íó›‹ïrÈn±ø­7^ZTx0ÅÛðÛD?ûyßåôdøs²Zµ~‰e©iËKÜÿAÏ“ 9©÷ ™b«Ge#\ÝáàUüöE8 ¢ý›# ¸ž°9öbž• kš¼‘ž!®s¯×oß)¦½*Ãdâ9^ÏViï A@*û „ýKYö¢x·?‰ÆÕ톯™¨M€6Ö€é¾ô$½£v[m5M’y§áÒ.OMQù/”§w£Ç Ê ïøQ–)>Hå,Ê«A(g¡0¨aX÷uShŒF§¼ÂÝ…¥æ›(x·ŒŸa·äŒFó5êe&ÛVØë£rW*¹_Äõöÿèì@Ïà_/2EÆ©ð³+]ãL0»üƵ=‚Ãe ‘i‚òΎH•ÏÅ“žL>;kVrcËç¨ãÑù‡oçHõ-ÒsOäK„èñ{,ðL’¢Œ€Î3¹ZìYgÞG‡×S$#lXþÈÍÚÉOɶC*¡œ£Ýð r멈fI±|VÊ iÛYxV‚ó“)âsš´Ì—²ƒÁþd‡ŽW¾X>ïÕŸ^ع2Gþê@Õ–Aãxšìµö¼ÎÊ¥-BG•²¡¾¬/²Ä Š^Ž×ûy . Ž•¾^Cè×Þ[6—מ;ªä!Xè|%F2ýC…&ƒ¶?J^Igê)AÏÙ1xy¢{¼ èž~5Š«ƒ¨ í¬þÞ£:a!Ùd¿Aa"‡q¹-<•â eêñ~Þ£¢gê\¯/mÐh—³s÷¨é’dAþÍ…Ó¦m¦kªÑÿ®Ý¦K• „¦Ã.FAäç¼ÏªK!qÜQ=„ÎÇaŸfHôÅz“¿­1V¯ÈÎ ]娩ïœL|Y¤àx;yÆßå÷ÿFfNûÞ¡è¸éKdÇ’r3 |xÏ0ÃAšz¤Mfö¡‰¯èì~ÇZ{^OÉ\¬Hìù?H’€Tëð¶ôÇKÒ+žËª…äl;¹ÌmWÓÒ¾2ÍrzéëúÒ´¨­W¸ívìêYY¹ mk”Toþú+å}Óý<޵ǫ=è~Þˆ0¹ÅÔ¸¦‚Eý²Þ® ±Eí£˜l+5d …cŠ$þv­á7Ña‹~^×ý‚àÔo0¯ôo˜1ÍË…PÀÅ-ME¨>’‰§%võêCŽ5¯öVµºr…vCãê–É×L€ D  ]GWh˜Ä-KÉN2ïtÍòTF7úV‘<?EN‘ÿX—e›#,˜®^ûè$:®t›b®*M—¡°v›7öšË!²y'G(ƒ4·ÁÌW¥û|¯XÂÌ0L5SНÀèÆÝØàåjÅÈí#™|F®1½y 僌ŽÚµ¥E›¨#°FƒÖ é‘“îÔ¯ßm#Öý¢¼œ· 0’•;ë\TÿÓÐ8›*ÐZMÌXˆ¬ñ‰åŽÌ’‰û½AŸ]8}º=õÚ-{¬>k¨i×âÝý3”Œ!­è†ð #µ½R²KÊû˜@Þ»0:£—¤Œýƒ¦Lâú´ˆ,ϧtn y•HŠñÿ–  äx±Æ/jjPÍÀì¼±‹,›-˜jžti÷86: ±Í¾QžbñågL‘¬:¤²{GœÛÆœMÑó{·û‡É-$êÚ»ÖH‡*d2¨y—¢ð)q¬ "¶Ë#?rÌœstØó„Ξ^yÐù.AávÐKŸ¯¸c6öh„ã6™x¦‹Šíå‰WE®f©#“Î «[_3&!àñªMš@"U_Z¿ïCeɼӸ½<ÖcÔ>°¶¢¬¶iÝ&%ŠžQÑ™l£Q¶½ü¨Ë¹Èb"—1´°FªX5JÙÀô¹ßçéSWWÉZŽ5¯Yeò'Ôféùûè9Û{Ý«,%“ϺwÜäQ[h&3²p8£FT%®Æ þ¢±-if/lÁ{à‡'úઑ…Ç!ã‰Ì¡ÛìuŸJæÂƒù5<‰ºI&>pZýÞ@‘¨Ñv ±ÄÌ ›o#+wÃó÷`ô\ëó¡pœ‡0:U¾ðzåÓPO!¼‡â1ŠÆ Ä» ÚäM¢0˜Ó´õ©’ßQ˜{Ôÿr7ÖvÏê" å{Pá/ÀïªL½à–º†AiÚz¨·ã`|Rh(ÎÕ îRQJ÷(wDq]îíç)þ×aZ ß†ù°kiˆc­/cmÈ¿SìZâˆ>ö7sr±èÌš ŽîÊ<Ùx"ü›þnV™ u-ÿq<4•ìo7'ª Z"®NXøÌÚ]D¿ï‹Åš hì¼ySÅXį΅²É¾Ó©f¥™a½ýÝ‘‹F•(zhÁ¸>ÃÅÉG»9»TÑ3ä6¡ƒË`’‘E÷ KÂ’eôBgÒzê@ê†Qøp®¥l$›ÏPwl@ݱs…Y¾äU×ñ±‚…‘–W¼¾çÆÆ§Ñ8ÇÇ€Kiuf,»u™‘Ò1NŸ{kت˜Œº©OŒ¼VÃi²ñ©á8úÆ03IÑ@½õ§ÕS˜¡LœEVñãqäèÒÑ÷¥“"2SÍÞ†Jö˜GjËÍô¿€õ¶¥¦ÌëX'’å„¥1çè†ÆÈb·L 9Þžó¡¿ÿŠ×ª·0Ö=dï¦à’@Ó¢h›92BïÙ;t6•yCb"]£ðyÉ1KöŒJîÛ&÷JÆm“„IŠ) ˜Z0š†mÝáB–á¾OÕuÁÌÀH…vW 鎋–›L´s¢ï:¸ŒqÁHÍ%ÕC2¼~2u*‘DÍ#?¯Kr2ù uÇ2’c*c²;³õ[{S[ÀíÞïS;Ý›v2üF´ÎÇŒÉ ]ì¶ã¾&E"{FèÔÈ—Á·?1Š3éùm­£ÔÆËOÉÄg»/µ¯×Ètc©ìv ٠ݸ ÿ\ºFªóñæ­Æ8°>ë²n* 2‹ò¹¤h½G+Ïë<²Q#µø¦9 P¯zöŒà8Ìÿ|Ëyb[x%¶Qüo†GEÌá¤õé:¿|Ý–³QV§LùzŠ¿A̾Â/¯Þž"\NS;ö¥Ø&óN“ýTT a˜=Ɔrþ;ôØbÞ=¾è,åªô^]þAþŒ5ÊDÙñÊø³”0ÿ…Qíÿ¢Œ`„Õ1pO#"ÕJÙ_”7ýÔ?Cîà¬ÜÐóˆ7¾v­¾§9ø\Æ¡ÆʲnE½›ƒ´Jáõ }$ôÂXS¨“ÉgRóæ >õÄ%Øæ}¤ãgHÇÃÌpÅ¿û~™ÅŽ\çLS»2ùW#Ÿ<†¦pœ>ëÃúôZÓ–*DiweZÏ[Ë~‡Ü/ 3ÄÁXw²·-K‹(êt/?%'Œ1ϧJ]Œ0ŒFþÆq­vãºZ­cXÃR¿Ð=¶™~v¿‡>MÓÌþÀ;ó’GxŸ^œjOsjíyG6j¤*ß47ú ”Ïë9/Îóh˜¢,S' ²¼‰Ã°fá=áQ+)L´Lë(NCéöží‹“`ÿOx9_õù<‡¸æ“&…ž L!’÷âÅOÊš/è諈¬ˆ#­)ÂD»´ à@Tª´£K ±Øc{®ixõe!Ž£µ+)?éÓWBh0–àd≅ˆ¯ƒå(„Ÿ M óh4*–¥yÓDÚQc§úh©¸V€/˜@;"@»¿õ÷úOÄV°S­¤` œ¼ÅêÉxé›7Õ£¿É¼Ó©D„0ËI( †!l)Œh²¾‹‚÷Xg÷:ò[⎣²eHj|Âîš³”¦]3<š¸eÎ{ÔICuìÐT{'¿–(Oc†±-¢Q¶G =†#Í6õ;M}ÒÃj„»£™L>+Ì›%ÀsdðG]ƒ´G]¨>­ì`‹Â-ºúkÇ$ Ð.èb„͇£gØ;ˆÍ¨—îÇõ*¼GRžB\ hÈå0¿ª0/pOµ@º¨#?%Ÿò¢oôZˆ‘й0Þ„°\vÏU¸þ:M1¥0²Û¥ã à7 °ª½/xÆõ©hÜfªð™zè²ÛÚëS„»q4­×IzŽ£3L 1®ÕçôSifÅüœœ uÉA¡'¯ „…õ*Ž·¯v]îë2§¡ZÚ#½w·Õ.ûnó¦}äêw#8XzÓÂ;‹]×¹w¸pûÝœ×ÉÄs‚>g€·«oC" ÷Zc\›“+ûÕ0-Uÿ´”¿ÉP¢oün¬îÛÓ{Ôªx£¡É¼ÓÉøï¶Ù-gÝÙÐyçNT&Ø_ ÷xÊmNÝöëqúüΖØÒsáÍÓ~F‡ÚZñûchØgc‘~Óºh›­­ŒiŽ<Ô~Ds®ë>™|F;.•n4dx½îÐ'£±ºƒò¢XµnP×n~·‘ù/?%ŸºBO2²n™3°Ÿ˜ººæ·½".0Rw…eYK÷C™Aªäf| 4 ,ÕíP˜Fâ½3 ò7»ýHe^ÏÖƒ#LÓ£Ü_5wû•è5O£J”Ûk÷èSk •F{\UéümÞØûªÆ¼=ÿ7YYM¦ªÂç§dÃÒ”ö“‰g2£M­1®MÉ‘e3¦&@ë8àG½åG2ït*Â\²¥“ÝÉé|w žLš—çÛ0òÏZõ³EÀšê›¨‹´Œy? C‹Ý&“Ϫ:¨V4E`«:(ªçãå§dâSW<ªd`šM^¨}XÊÂ,Ò{‡SŸV¹Y…mëiß‘Øq¬–2–ʼn…E¾&…â$ú×Ѓ•†’cwL€ 0&À˜h¥0ㄦÔñÑF H¡ý¤°ÌIXÆ,p¿ËkYËMéÝ JÈIXëš‹)`ðM«'š4zR¥DOH‰&( gL€ 0&À˜`;to—œŠð–!˜?x†iZ§Ûã Ø‚ëZð‘qù"¾Á1 âW¶$¬l´…Tâ02&À˜h£ 23ìÜYPðOܧGEa› 4'ªíxÏ¢µ#ªLî¦<²‹ÔÔoiiÚÏórrh×Î6s°²Ñf’ŠÊ˜`L í¨šgŽ-l¾Ê`ɨZ;Rçz¤ddµ”]Þú¶¥È³¿L€ 0&À˜h"Ù¼è×DâY,H˜l$ŒŠ-2&À˜`L mÀNEª¾Ú6BÌ¡lu”´?ØØp±²ÑX‚ìž 0&À˜`­€T<{¥µ¥I Oa(•Š sFLE–Á˜`L€ 0&À˜@-¬lÔBÂL€ 0&À˜`L€ ¤‚+© È2˜`L€ 0&À˜¨E€×lÔBÂL€ 0&À˜@ª dúƒ©†jÒ{õ¢¼éŸ¤Z>Ëc© Àùt;Ål=8Â4=ª(oúÛM“¿be#yfì‚ 0&À˜@›$€†ÔGJ¨C„[Ó}¾½îѧ»#’YPàS¿¬«”B®ÅâÐ~îg)¸Þ[)±¥ÌN)Å"ê'ð~ýVZ§ Χ­#]¬°È—ÂþvùI Q«P6ÆæÎnZ橈È`%U?)D—ÆDŠÝ6-%ÄV©d±PêÇûü¼é5¥œ?š’nêe7wþ“;ë0é±ÌBÝÿif o bô×X±’Jý³ 4£¨ê‚)DVn(S)붦g ðÑõÈn9º.­XiÀfMI@u©0*ï€5¥/,»ås§´œï©ò™óiªH6HŽT)ÑR"¤!ÐõÅÖ„×\«ir²a}é £]2:XÝ»fÈ>¯%iÔfrS[›¶”ª­¥åÒ.ˆÛZÓ´î6`ž®.OE08¤‚bËÈhŽü1NŸß9llºÚçÙaÃØk`Ÿž­ÒUeo~²¼Ïû_þ³\±ßÐcøWÕø€ñ²O– Ø Ší Ñ|¤•B‰õ–R_‡î]зø÷MÝ3sC·ˆ=—fe…c&¦”ÒÀ€ÀJŒ2ŒÊšº·`¦ÿ¥”zÀ˜@ p>MÄV""f¥ÜÔa“sË©¿šÅ÷ ¡úï3´¿:lß¡bÿÝûËŒ鞦ö›å§„€­ ––Wˆ/¾_#>øêÇ]¾ü~õ\¤é¤«§éWß7[¾4¸§’óGJÒ¨%…4mþÀH†emZ*”5dpß>êÏî.Ûg×h¨â¨ƒ‡Ù¿D#Ïöã“j >hà¦Á×þè`êÙ™¼ü¡ú~ÕoEÚêßo3M]4[Ïí‘§ø¡ã§  )¡QLç+Ëš?áî»÷Ÿ7qbE}ò0rxŠPr g?ôb‚ü\*´(˜ûºÛ-X­ ‡.»Öh*ÖÇŸÈ1 ±‰RÖƒ^F·ýÌÙ³»©­Æl7àÇrœï-È ,¢ŽH²;!?¿WE‰5ZHq<¦ý vB{Wjê…‚™'ÜòøºÝhsù”ÈgòNÃÏEÖ=·=Ÿß¿ŽâþB¿m»I$"ÒÜ#…ÕõBzžéÛ«Û.7\~š¸ö¢“äáû P4’6[m (Í(í( )-wéÙ­æñ>}MÎ-×#|¤8Ö¨P3ç µ+M?DfnðZM™owï’1è¦+Ï7^qº<òÀ=Dš¯EúMÚJR´Úpé×ËNÃë.9EôíÙ­¯æõ>‡‹ÑpCÊVÏÖ0(|Z.Þ{PBƒë=ÊÛ2µ¾0âÝ›»/ å ö5°Ÿ:½J¯ quµÛ}q8´Ïn½= 4âp˜¯BÑì¶Gפhˆ­ÆÇ°Ÿ {Ýaô*~û¯Y¹Á¿‘:ÊK¬'¡dÌùP(0¯(%»` äXe©áü¿½@Z·¹|:fFèt(Ï"ï_‰<ºŠÆÈ×'B±Ÿ–îRÒÞÒ(Ñø4§²!Çøg^Ìsû¡{Ѧ\y–gA»$N¶×Ê PZN½ê,¥­”ÚmWOדU88´ò4nLðR?È{¹ËNÝ2?`wOî5çj» èݘ ±ÛVD`ï]û‰)WžáÙg·þÍã¹ïÿ¦ÎÈFð¨~BÍGS—ù|RÓÆÛ²¥š>VŸ5´.ÆéswÁ½=פ: LJc=À!RxΦ!ŽÛÆççïLÏÑ¡¸[ùPšvìH÷vë×íPú“÷!·…§Â|(¦Ì<ÞÏ{Ô@Ø?×ëK‚êh¬MÌ ÌÝcì¬Y= Œü‰¦ÕÈ.ÞðdQ(°‡§£áí|»[_o'È;~ÛMÚÞU[ʧDWšê4äU4ƒÄ|äÑÓWOêçݳ‡é‡ÌÓ'ni{)š7—²!Ñ[…àÚÜC÷ÞU]sþ±2 “­ùh_(M)m)5Í3çŠÉÓñÒ%ÔCÉù£}e…˜±iDþ yZÅ–ŸÎÕ³ÿ²ï•g);¤ûbúÁ†m—@zšOŒ»ðDyÆQÒÔÚaˆ %2U¬p4A²†E¸CáLÿkhÀ?ˆ‡fؼ§.oL£ât(¡¬)æ>ïØ+æ<‹iNÿƒûî•%ÖdŽû“ÑØò@î¯E3ýÏ‘5² Hèp¿îÝ {C(> týxZK"èS~Åée[Žª±púô? ï;„Á‹QÛ® …ú½EÀš;õë7Ñ5µ  ‡=@¿ÚOÚŽI[ʧDUiòûl‰K3g„ÎTÈàº>ª² 8eEÛ¡î ©’ßAwúÎeÒ ËæP6äå—ߘáóùîЧ»¸â죸âhPRµG”Æý{÷P3:wÜyòx Îm'iSÒdó<ÕV|ñÖ®(CþŽ‚»9ʬ”Ä“…$O€¦òŸsìÁ2sä‰ÿ÷ä#ì 4?ŽÓk‚DZ’™*¥ä[‘ç‘ÿ¤H`·É•n3{›]!’™eªG1*²ÙùÁ.”[Þ®tÖ¤6ÃíêšÒ2ë—Ì@ð~E¡g|ÔA@¢¼¤_;8ÚJ>•žžO¢{ä-¼O}•i=©€ß`ß+uýµ69×#3YôkljŽL¨ùúuž`ZV¿‹N9ÜÃ#M²ÖïžÒøâS÷`·™¾ƒ‡ï{-B¯ÁÀù£õ'iJC˜dþ Î ïÀÁ}gCÑà-±Sš­ZXçãŽûs!tF7š£®jÕ@š*pwùý¿¡Aê'ùJXwŽ¿eA'4êÃnÿ @D6üPâ7·yĽ³ ?ÑÂQÎÌ?ìûšÿJÝ·Þµì*ꌢñ+lú p~X—±á¸ÛòH{kõEyþ¥šÏ³æ§Ì‡_ʃÑpõmf ÿ<Û=ÿk×ÚJ>-Ô³JO>`Ïc…¦ý£qŸ`4nO(o½F»l¶ëDй¦Ö´¨‚ðøÒ¼×Ñ®S˜·M >v4G¿Ý¨¯~XCÊÆ]øQe…:«Æ.Uœ?dG<Ìrøðáž“G]<Õ²äù;"§9Î;u:ç¡Åów»tôøåà`UjGæÑ”qïç ,\cG£?Ü4ÿ¸ÊGýÛ½T¾lÂH Ýn¹Â÷Y†RÁ®)õ½m"å/P8èòûÞõ D4¼ªMèã·¡×w-õcþÕË…Aÿ¿«ƸX¤O_ ãk¯×oŸQb”ÞŽð^)¥9fǰÎFíŒ@[ɧ£F¢òŠòò¿iÁ¸0ÕäÕ£ŠÍbªÇþÙÎ’%¡è4uo‘¼ôÚ›°M¥Úsp]%WBacKmœ¥9^°>ç]™5Q!Å6:pþhãiܘà'?´}=õ¨ [Êgþ¸f]S—U‰ »m(;´î}ûO>ü¸Ów‚øxS1›À÷K¤ýQE鋞X H«IPjôÀú¼^|‰ZšPNÍÔó÷rèŒ ÌÙ×§À¡Dúëdîñˆwé {÷Ó5´PJE-eµm•ýFƼΨ‰L½ Ã>ã«æö®U¶©wè“7z½ù‚G’wЍâÒÞO­=ŸÿLý¶žx‡ªÛ:öº¥ª<ŽÌºÃæÕ¦Ù Øž´ŽPIEßÑhŽ¡2lbÌȨl´øø—€â#h!b[8žYö™8hØ õm!¸µÂˆ4§Å‚ªS·®§àá‡øÑ\`g„£EòG­@¶!ƒ¾Yi‡vøÞCj…ú· [Й¨hûáZÏZ«A"ù[¡KÍÁ»ìÜ,åG]¬Ö®ß$>þîgqÆ‘Pž®ËZ\óxéË!•W^´Üê_,™mÍì‰e_^¶ç!ÿøÞëÏÍAØ©ü@–¦ P”7ýƒ1þ`!gG˧ÛPÆšJʰõö˜@ð¿øú…2Uøœ|x#îpÀ.Ôïâùë(Ž ùÖ˜@è¿R¨ƒ-¡ŽÀ´¨?`¿F…æõ¦M3•ç ü:¥|ÝæâÌ@èa¼ò›q¿» ¯?sÝ»¯}¯¯0Â? ä}¿^G[n Þs(œmy!:¼|ß~ ´æ|jop`”ÏÅ:³1b÷ÊîP`íŠé‰ÒÔDG¾ÚÖR&[Ž0M"î {S*ÔI­þ!ôeð¦ü`5²zöñÙŠUb[i™è׫‡8ó¨ƒÄ!{G:U¾þqøÏˈ_Ç÷„ò=»w9a¸pm¿¬Ý òïJä\}¶´‹½{_L¦½ú‘xá/ìgÓÓDŸ»‰c‡ï)þ|Àî1íG®Û¸UP"åâôÔŸØaMÄ~´_­áž¾³@i_VR2á¡ù¼•øÑð"ýš-À¯:ÿüGŦ­%âæÌ¿ˆÞ;E–û±ùKíýO[rÆ; Óßü´¥ÏZJž}ðÕO¶'ߺ…>õ¿O„i™"ë‚ÜÆ­ú:‘ü†Å^;uÍ03:¦7eY%œ2€€Ñ"å.Å>Cû‰ËÏ<Ò¾_½n£ý>žúçýl !`㥟[^ñú?èCwbùÏ¿¢—XŠÁ»ôWŸwŒèÖ%C|»r­Ò·§ØQvãÊè¦*Ëʆ€¥¿£l ç£)x;{rÌã|µz`ñá¾"4¢h-Þ5x/G#°Í¹X‡Õõøù]ز¶:HÞNžóÍmæØ9ßœ€Ñ‹_±KÏ(©Ä!ènÊ©¶ˆ RdÆùƒRÜû§Ð·3l•RŠr<†"qœÕI|²­DÏÁ߃ ¬ÜP¥sVÂï%¢³÷:·<¾®A#Ríïh­ù”H#O¾‹wääÕÿgï:࣪²þ$@¡¡…Þ¥Št¤I;VXËZvEYAYÁ²®®u?ÛÚEéÝÐ{Oè%B „dæûÿor‡—É̤My“¼“ßËk÷Ý{ß¹gî;çž6Ìl1ßžsMÄùù‹IŸL}y·¯†9S¼a’,›P’¾»ó\ãá'jU &cé6Ø{RlÜ{aû‰J¡ÁâЩD+CŸ˜$>œµLÀdC<:²·(à/ÖnŸÏ[-Ç­IÄ»ˆ§ïºE¤"{ö£ âû?Ö‹Ä‹—ÅȾ |¿?þÜ!ªW ³ö­ÀP+B…)¦Û2ûóII‘è … Ò›r<ô}9s–mDrBü¤0Å­eöIóWoóˆ°amÔÎÁC#zæ~íÜÔñ¥èƒá3«……¹uþТçÑQ½!à‡Š#ñÄOË6‰æ6™¸Ò“ðÇŸ»ÄõÌ,1éáa"5=C?sA l?»R¼4n˜¨YÁw4X%Á]ÕJ!¦Ë—¯ÔFœ?8wp¡Â·'E¼€7‘e~¬b}“aeíõ‘½qýÓg§¾W9»|z¹þñó,7cÚä<Åsë¹í¹ÿü'(=-­*CÔæøû—óÆÉÿ½>ùv#˜yüBÖô(S@ùÌpÑàC† !ëNÂýa|Žf*;ƒñËLP¡ryÝ€ü€81ÿU߸â£t*>›öò `x£O%dÅD!ÌEŸÏl²¸DNpI%vH—Œ$?áˆÜAvʸìÒñ„ó¢:´pB—uj5‹×ïÆÝÖÉáYÆ ˆ‡O‹Öí)²°ñTÕßNÐþxÃE1»EŸŽ-d;\!³l³8!'‚E÷¶EŸN-ÄB˜DmÙLšpmØuÑšºŠ›šÔ«·ÅŠ•›÷‹ËWÓE³¨šb0Ì4Ô®nÅ µ!¯ù›¸|Utl^_Ü}kWÙ¦£vøàžCñb鯽‚«±u#ªÊ:¹BöÂeñýÂõèÛE¬â‡¡®n¢¡¦-k£.<àØ#öUÚ¨2ÑGA¯Ó»C3±r˱çp¼]¡Ú¨9K7‹0£°è×¹¥ص5Òsbæ¢ Ð¦]/<´T[\M¿&ÂÃBÅí:K³¦×>ÿtPILÓWvããÙËDC$£C.qBê,<4S«ÖÃzµZ6åXoŒÝEŒûŸ;Š'ïêŸç5ØæÇ?.—4P¡âÇ%›¤‰à}C»‹]O‰5 «6 ¯¥öb¼%™E2ŠûÀà×®QUÒÌÇ`Óqí~@¾ýRˆ$\I½&hµïh¼Ú³­¨ “=~þ;g¹¨Aa(„˧À„p=3‚Äi±qÏQ0¹Ñ" ´ðãÒM’Qåý_¡‰¡ñæÓwHS!^{ltoî¼èƒ4ãö¹Ãö…ã`º´‹?B¸¼v=SDßÔȶHcGünÞwT.2Œéß 4’w¡ØÞøii‡ùl „D.°, %[5Ç£ Eåâ áèéóâë_׊Fu"$¥]».…΄‹—¯Šÿþ¸B £Ñú€Ô=Œ¿½蹇 i×öØã²,ß{Ûbâ¸!‰å³CcFó-G¿ù ›ÿÁ¬‘s…ÒŒòØãtáæW4ª70``ÀÀ@©Æ'nw€bü ðvû‡sÚׄùÔ+Ÿþ"™AÚÝ®¤¦‹Ðà<.äõ @É|Ñ4¡$@ÛnÂå«irO-JÿèXY¬(Ú4®+“Øcgå*5?ô0  û(ËSi߼̿‚DS¬`î?– W$åMüëØ¢èÙ¾©Ö³dbE’ਮVN€i%3: KKé‹@s 2&=Ú5ƒ=zÑ+§\á¶ÎäÃ.ÿ'N%h°ƒŠað}ô:Y0nÇênrJšX-“-ì…°@-ADxEÑ´^„tÂÞ{BT®"ª@Ð`@ŽidµÊÒì†Ú(ÒÃc§EKh”ªBÓqðd"´içdÕÔ`ñøcj@¨ÍxÌ}ÖC Tp-# Bë)hRP%øc|ÿ÷[ h7 šˆ~RË¥ÊÛîGôi/ý–Fôî …(2Ò„¸‰’Öh÷ßïCUiýlëðÌy>úPs†)##}w}¬Ö{ –@ã3{ÉF±jË~q4U5 !°…‚ÆnL:ù»¥39…ØvXtPP˜ñ»ã–h1¨{k1Õ6ñÊ'¿HM5¨ÔRè³Az#lÚsDÎo¤Ò}ѨÁ8Ÿ(ïó5¥îkÕš èÒ ÂI i.Fí ÂS˲ô©ƒsš¢¶h£%΀ÀEpô7ÝüÏ6ÜŒ`£z ¸î2£b·ÉP"ô¶û5lŒfB¯M%Í•¸*(M]*cUîJj‹ä:’Ód‚@I€ *!¼R¨ÜÓ„iѺ]"|åpœ•³:) Øü#CÁÕD3´#d(¸"ÉM ‘šèBd hbApÖÎøÛûŠ­¯}>_tnÙPšUÄ?+…˜oÄX{@æ6þ\²‡ š;!—$= %àz”> z? o·vo#ÍQºjlô©µ:xò¬4™Úºÿ¸µF²êT‡_P€83½X8ŽwF]pq"9¾4s!“¯è¦®&ÒX݈*" +Ñ ¨5±œFó—4Ždbƒ¹Øë¨Ñ „†ä*ÐxZ6Œ«¶ýZ»=N žò†—þ9 Ù›ïßû­oÞ8õ¸§ºöWhiF•t%U¼ûí"©ý{ltŸ<Í;»,h'¯¦e ŽÁ σ8)Ìø‘^¸¸@³I >3æ­Bƒ‘¶UÉóKè'5e êFäŒùÅ˩Шä\m¨p (Ï\´Q #œ/]I“WÞo3Ô™˜7h‚G0…iΫEý ¨¶\±[ÓÓâQ¿Uœ¬ÕüáŠê: ”j 0Ü0_Îý+Kõ‹/§{ ¸Kذ®LzdÌFa…š+G¸ŠÜUÈ•ºÇäÊ.™={±šGƯ¤°a÷!¹²Í<•)ô× ‰Ö )0m™øÁ›8ÇOú”¨¨G\M¥ù†VtB‹ת^¹Àv¸ü‡oƒ9ÈQøhlUb‚E³úôÑâYh€hFá ò]¨Pç^èNþ&釱nç!1oåV)0°Wü)öéØ\®ç*ï–¥pìô9©IºXwi²´ >!A2Tt+j¾g1¦Êo&‘ÖXMW`/„sÄ{wƒÉÕjéó ¡ÃÐTÊP¨úŽÆ\ɾï|“âJíöÜ5-}x.hÞÈq¡_Ž-8;úôpÜhfgŠ2~‘ø­?<¢—xñÃ9ÒIœ~i¶À­ûOX/'&]–ÇaXlQ@áWÁŠMû¡Ñ¼(¦?1F ½ï@ RÐÖEð-‹Å|Ù¨NM˜Så„ù¥—~Q~ª>WìïÒ]<0æ®÷QéCш×éÃïfÔa`ÀÝ0[L“sÛ0„ w#»´Öo1ŹâÕ8y» øAðÈG«´°rÌÕH tŒV2V)0¥úœöðøÈ2¬$oÉ ô€y’¸šwìô¹ñØd!¼(Íö†ÿ8[¯€ÙÍP¬BÒT‹Œ+©iX –6úK7îÉSíîéÌNÛ}š'$]ÎъЇæLVš 0ù,íÿiþ´ ΦÇð,…gíÐ9•Žèt@¥¿™¦ pôlX»†4ÝZ­ Ûb9âÊ#pÔNKÚct£ FÈ(ÒÇ·ZmT{˜ÁlÚwDÄA«ÀU^Ò†b&9¦Ä'W˜iªFhasëã’è¨Û¼~MIw¤?Þ#P‹As:pÓìŠê ÁË ΀Ú-ú êv“¤cÚäØVc˜ÑôlßLþN¡Í«`Ÿ>¼Ò%:~s€«û ¥]&K¶PÐØQ Üm%ç$Ò » ?Π̯CZ¡™ƒHgÔÄRëAMµ[Ðɤ››Hš\¶qŸœ‹ø{·¤Ó0ЄhúõhÍ­hNÅ({}悌8û ¨2nÜ«¹Â#ß7¾‡QµÏbÀ'n(&l<·b>n}ìÆ’—õ’«ÜoF•„ˆ=‹YŠ&4 êÖ¦ "0å8ÎruðÙ{oE$•MâMäÒ ö92þvßà|+üZ#æêxåñù‘ ³F‡ªGp2´›W6Ù\A&f\ ¡ªwǦˆjsƒYéÕñY‹7ŠgßùAÚèsUš~ÿül>ìõË‹}:HRÛht«FÝ»T (4ÚÂyÔY;̵°¡3é4J&’LGD!¢éB¿C¨^æ– yi5a4m‹n;vЊûé£(oDÿ♂…F¢sýû3—JæKw hC&}ô“Œlö4Â)“ÙdNý‰BÍì((&cȲ_ÿö'ìò–«átön‹$Ž…áˆ@µÌâWó׊¿#AQ€ /Í÷µŠÂý‡‰ÊÖ‘¹(uº ¬–>x¬6~(‰OqÔÿ Ì` Ý»È`€^]ádn;®¶ãG!‚ÀÝ>DE['FäD—ênÍÿC #£Ñ¼êÝçî–ÂÆ·tÁœ³[8 =:ªü½S±…ÞˆJFŸŸgþý=T¢[‰s—Rd1Îôzÿ‡%’Î)P¨e´3g¿Û6Üp®^DíÝЄQ¥ ¸ 9_¸Ôþøäi«ù8â9÷á>¸T„-ì¡&ÿÚ²qT§çï¿UÝsÛ>á)H8ÚSÇì:$fƒáw[OÉ8*[’ël‡ÌƒÖlKÕÇï:V‘¤L]’Z[Û|ëMðjBly9k‡QlütÖ^ˆ'‚3\É.ø÷Ÿï‹=±GwûÞ›÷¡º‹Øh_Âp ûyµ Pð ¢yГyq!$tÈØû¿óÊÓß*3@O÷¯ öœ]Æõ,é‹Uo½6ø›g(Ú åó¯ñ:e1[3»t8ú¶-ö[;Ÿp¾˜øþq÷à®ÒÏ‹‚Êä#¢Púþßï³Ò¦³ß€½÷pÅ5ÿàº}QO"6†ü£dDú0c¾?¼åVðV»n})£rbÀ4ä‰6<Š4£1ŸÅ@þ/™Ï¾JÁÌ3Íd¸2HMÈ·¿ÇHzÛGW¼>Ûqd(µ‚Ë94xŸÏh^#8k‡«¯ŽÀB†£¶}ù:7EÌö=lÇÔö¾³s{cë¬|Iï1Á%6 CÉ|0Çt€áuu\áæQ·þ“ÌQ£WaÃÙØÙ ‹cþæ=ïȧ°‚û`Ûo”‚Îí—`–E®ÝÐ|Õó¹Vvö(컥ÍN;÷ÔdËÊ% xŽİR¦ñ/Oïˆßá èw£@ñ‘öüŽ9¥æu}ûE@x)&‹‰á"Oúû,þdÚ¤m¾ýF…î½A§…F•÷ ê•Ns¤ÞÇ™ÛzÀxóÃ{·ÃG”üŒÊ(\ŸE$-f©¾x9EFb¸T†WÖÐ|Jn\å7À½à‚Ç##{‰» §F¬,_^úm ìÖÚ½ PûüUÛEËöƒ°± E)løºÀazüñ©AÙá~Oa±èYþ‘&?“%,8ÐŒˆx&$¹ô¨Ù`è7nk0p-3Ó 3jKJÚ5¿,sÖôñS^?Mß{uÊ×ùhêÔ‡¨m+M`Ð©Ž¦«étÂÔéÑÙÙþ–ϧMÚR””Iaƒs¶ò_„ÏÐ;ä¦cP«j¯ã®–ž®µDè[nzj\°(DU±VÈðEñS Îã^˜<Ð/°Üç³¹Vˆµ, a}SãÚ&„!w¬×Ó€”í¾HAIz™äu˾£5÷Žûlv³¼4õ‘/ßšJ'-Ò©žá-‹ ]º,ìR:5gŠ7LBæ£P’·-³ÂFIfùÜàrå*¼AÃòèèÞ&Û(bD¥Ñ”0Àñä¸r|ýüüÿõÀó“zÓWƒNÝ@z¬ÒÛtjv¨‚‰ÿ>œµL<÷Î,™ƒá9/#z+aÁÚâ×5e%˜…+1Wüº˜ûbÂëÿCVõõù*™ŒÜ#¼·ïH|¾{eéB,ò‹0,ª—A 挌k»´¹jÜÙ¯“g.Jøû{³²6¯Råd•'}|÷û:wvA÷u{‡>d.2oJØÐ»ÀaeàZµê\©R•ªÿW'¢Šåá=ôÞoÝÓŸž;ÈñE~KPpÈŒ>}F†¢¯z8 :Õ3A¹©oÞ¢SCذ3 ŒÔÃXö“&ž¼s€h¬Î•Ü•ðЈžâÑ‘}\Y¥QW!0‚ü&;ãNäÑX1ôkr”uÈÊΟÎ]‰L殬‹‰WÚ=›g~øï1zœ§€ê²!hĸ‘Ô‘moÝ<_ÈjOõI/íxƒ>"‘h0-+üV)aC/(qÔö“vÎÚöïûÈ©&|4ü †#t•Žëß±ƒºø›-–È¨Ž­žÊ¥¯òX&OëÇ͆ :u€˜Ò|Ù[têÇÒ60ÇÀ|voÓXfæ»1Ë·‚ »ËìàWÓ®!rK-qïàn’ a6æõ»"„hsñ#²•·€rúÜ%Ñ vu™œÏÓlê£ÙËÄÃ1¹iϹzzßÐî²jÖ»xýndAOõ"ÂÅxdt&cì¨=Õc_4 4®[C<‘fò¬hŽLá„mû1"ŒØ´÷¨µ22Vß.X'`¥ßÏÏ$Ú6©+F÷ë$óp¬×l‹•c¼Ùâ‘H²23«Œá«qoåæýr,›vßÜFÒ+_¤’1;ŠøsI"T&4‰!=Úˆ›Û5u8Öª=F Z¶q¯Ì¯2²oÁD‚‹‘i:4¸‚¸¥kkÁŒôÒµ7§/Ê,ñwßÚ ™Ë«K‹œ1¢Å¢õ»Ä¥+©¢cóúbêòG¶èf/—Z÷f.8ý©1²>/üSjî=Ý…f“ëíŽËLð|÷c§/ˆt$ÕlX»š%¡jPæ,Û,sœD kw÷¶A?-dÝž š‡2 ùÝÌÕÓ¯sK1ôÃw×}Ü;¤»x`Ì]tºå*¬v³Ž…ÎÈÀqE»¶ Š'´n\Çb8ƒël”ÜÔÚÆs¼÷9MaãlÊYÜ£ó˜z=³Å49÷x¥º–»7èÔ!eé´Htj1Ź7$8l0Юi]±LäR0vü0+ )Õ7 bDöMÄ„;úIab-GÂõÌLqàX‚˜¹h½èØ"J0¤a£:5Äz ¶Å×Q_ƒZÕÅ•Ôk"%-]Þ:NÖ[¯f51nxO©I¡ á¬=U§±/°ê$Úb|ÉL*ØŠãvÍê«S¹ð÷õ"!ôÝÞcÒa‰í?ÇzÿÑÓb㞣’qB9 ˜Êü¨jXˆÕ·“øÛ}·‚~²‘Ób§|ŽÂ³w3wÁ´'n—auÚ5«' gc­Ú£Ffì ®‚õ5-—ÃâU*†ˆŸ–ÝýÝëIºœxæžA¢võ*bÖâ ²}þ»xù*ú°‘T‰½;ˆ?A¿f(ltnÕ@–Õ·£w[ë3^:  ÁÍãp„q'­Ú¯mŽŠÍ¢òô¦$ô$ÚCÈ{áÁ¢-ÆŸcÇpšž¢µÛÉÐã†÷Ã{µóVlç/]‘xÖ)}(!Ãã´PŒÙW.âuW4öÕñ-à5Ê8ÞÜ#F>8žãOZðÞø›,~‚[~0è4?NÊÔ•ÂÒéŒ×'çVRäØ#Â’ÖéóÏßqK´Ô½µ˜¿j›xå“_$3Æ—Ús8^2zšGaÌ ±#ö„õ}¯gf &H fvæ]nj$ 2¸[6+åÖ‡p°+êÌþûðˆ^`zëÉUFÞ/¨=mÆqá0•e–ŒWu¹’˱¡@Ф^D¾ ti%Æj•C¥– «UyÊŒèÓ^t€`I¦ɤ¶„Ú€Ymß¼ž¨$µbû!„²­Ãh‹å¨¡¨ A ²¸v=G˜-ÌXßÖ»½¬·WÇf²>jLØþ€®­DJjºÔh¤B 9zú¼èÑ®™¨GWhèNM‘ZA;0Îý£[Bhn*ªœ˜ºnDUY¤~d5†åTޱç‡P»y´MA ¤‰ƒÐ~(ŒvlY_šWi;R\ú@ò6à¿…¨^¥¢hÓ¸.ê5‹Øcg=BìÿÞç¤V."¼¢LÜW³Z%Ò7æ1Ò‡<½"|jǼ€cÒ¬ÒjV­ÑfyjM (;àxsÜC*… Ä[SÃ¥7ß ]Ñ)­ Ò3òš1œ'šf{PÐsöž)«×S˜±VÂ@(´^„º5s„uN?£¸ãg¥ B œÆÛŽ?—,ªV¢Ï¢Q&T xÎ[à„>8 ~Cï}¨„ô¬-ê»}®R8 àESªm2xL‘&w4™SPúØs(^,Z·K$&]™[EYeVv¼Ún§ 6‘€B6ýP0:ž=Ñ~¯$Nf”Ræ(JðPÝÕÓ^™¦”G§ýêU ª`Áï/ï䢧}q98ßb^5§§¦ÖCå¤rÒ¤a™ {oƒnè”s+MË„õbœgîò­r¡Çßß$¢`íñÌÎé3KAâÇ%›¥UAL€ù=c¾“}:8}îe˜wÆbÐÔxêg¿¨Ó<û¿Þ}‹G’›Ò÷À±30á¾Á3æéˆ›O&é PP{ùÒÙ…ä+©–ÌëדÐ-Ò@>ð}ÐN½ ~àôÕ  }hlaŦýÐ\ÓŸ#ý4Þùv‘mQ¾\þŸÏ Ð ý(ž¹g ´ù_µe?L¬ÄìFÅ@hšH!§žýëÝ¥æ„f¬ËÁQ þ¦üŠÉÀ 9}zíÓ L ´öú­-çÉcôAæL á5j›½t“ †'»%5F?@ØäÂC˜(ˆ}½ ¦bËŽ]z ;jeX¯éU­Ót&‚]H`¹r†°a‡–Jû%Ž;ËÆ•À@IDATœ%¨¶TfT¤ =Ђ®è”sÞ??›/- H ÎC-2BKd® PË…Áá½ÚISóph5Z5ª#7ÞwôïÙ.8rá—ýcË—·žøIŸÈÞÿQ.4Ï„Ÿ¥òÇd>ùiLQO‹)ÿ7þv›å<>wùfñòÇsÅsïΔ{jSè‡I>îka8õ—>š#ý€Ù¾ë=2Ÿã±7 0t:aêôèǦ¼Ù¹¤ýË¿4[Òsž'#I°$ÆŸŒ f‡*/½0÷Á7c V§i·ÝÄȈ(õj†Ë*»ßÖìDFÆŒ?†ÆuóÛû«÷¬gPÚè3Ãã£û¨ËyöŒ"t vö4¿ù&6dÛîž¶ÿEm/OÅ^>á˜c21Å=‚¥ý<«’d^£Ö˜¨¨¦íØ¢A>ó5v¬7¢Kýï·ñÌ¿¿—fG¢[‰s—RxË)Ð’ÑË8Çã§&ªŒ•39i‡¦UÔ>¼8n¨ËÆ:°B9éÔþÝë'<™ÄVF;í7orÒåŠ7#Ñ|ðÝçî–¾D>XÌNèCÍ"1>~GxD¤œœ•X1›+ÒcåäŠýlšÔÍ/Œ—>XïÀn7É cv‹Þ›Zµž "à΂AÞŸ¹T ”ô›0¦Ÿ¨V9ǤË¢·¸ŽéTRØà5‚£örîêû?ÆÜb1›“·®]‹žJzÈݳã§:ï+`¬é'Þ¯N%SýéËã¬ç i.Ç 2ðZ°kÒ€öÙç…Š«â¤%‚ ‰ûÕ¯b’ •æS¤+jÉ^ýôhN$cëh¬mÛkM˜¶½Zˆ8¥=§s÷kmuº£ŸH˜ò”ãµ'îìŸ{/gwè0O&¨¶óšmå)肓胇yËÊU[vìtaíöƒáÀƒ[ç ~Z<>f³8@Ó8%¡¡=ÚŠAX¤ ÀI3ž¢Î/¹£¿ôE¡cfˆ ÁœÓ íûóŠ7éÁ7,æì¬3Û×®:„®(aC-TätX?ÿI›Ü#aC+ÙúÁP™ë‰LFI‚4¡èúâ t̘>e¢M;ªº¤Sç Æ¢^Wm9 £/RëîâeTÅÑ‚“£çl޽Ч þA­ ¿ÑÔ²08 … ‚ 4´£•ß[½õ€¨ƒ2 úÒ¢AmYF9°kƒq0 ‡ Æ1 N´~H€)˜—ƒ± ¿Щɒ— –oXô.©ÄN³üAIfû¬ gfí5™ž:ó ï#ÖNoí\"³o+hh‹Ñ¾ÚÕÀ|ŽêutÝÕ}pU}뽇ãM§û uÒ^•Žžd]¨½®éÃVÐ@ÿ Jа-L'r&Í£s#b‘ᬠ_-¸j¬oÚÚ >ö„OGaéãúõ”ÌÄ“'>Ç·zÍÙ¨%Ô—>¨Æ·ž¤ :<Š ž LÀx›ÎŸI ³”4Ôw¥¨]öTy2pj³?Àžê‰ÑŽ×1@­¾†HzE£ÜëŠNù=´œ'ü–³Ä»Žž£V¿¨P˜àÚ@@-ðMbÊæÞ¢Ije„¢ç‚`a‚qµoî(ï):u瀌%̬5óçÍÆ ÷ÅY‹7š)Pº1À1æX[²³/Åü>>ÞV Jà Ê}05_ýºVnŒˆÁ\Õ«}Bôe *"}\_þóÜyX†?·lã¾R=yô‘CÕ¤$¥4›³2Ϭ˜;g®rÜ•À¡WÒ'cIÐ0˜’Ù̹jü/« ¥¥ oãAÛ—\¡Ctª‚óд”N⵫W•þ±ð3s¶Ï9+ëèž6øÇäGGˆÈj•óÕê€0÷Ìu{<&Ë•`3$GkŒ£/¶þçow[·†Ýœ¯>\p;ºS³Af’ˆÌK—Î]‰Ý¹õߦÑo|» ÆòèèÞŠàu€c£ ®ÆÇ8á|²Ø½iÝ'))Éô콆‡bHeŽ>˜ŒïÎ]ðêeŠ@N¯§¥]¾ºkËúÔ®Ü{V°î,­¶ð}äü.6ì9d¾˜|Õ´-fÍÇ©©—ÓqU-RpΠvC¯Àïšú¶©½^ûZjúÅ< õ¼¸š\·!↖.ÜÖ\*ÖöG7tJ“åˆÞÈP°ôwܸûˆ|%çaØwFóÄ·CŒE4*š-%#@ÃËשQÕásEÀ‰µ¨½àmšÔ³Þ·= ‰U*‚áôíÜRÀáZ„ ô±Œƒ¾&‘¿‰ Œ4Oæ#ßkíöX„ÕO•ѸèçðºKØ ÎøQÌö×b-ø3¬JÕ¯qüˆ˜', ïar·M8Ú2ÀƒàŠ$É­Ž™ŽÇ퟽yB5ÁðL6kœô ˜ƒ>€Œ²E¤2–¤†±KÛ¼bé6lÇÛ5®yí¶¡ƒ(­GY¢{ï aÒ|ôÀþ?¶­]y`çº5»sÇ_Íz6Ô+¹œ³Í° vâóVlãn»Y|>o5Ô¶êÀÂu»Å2ä…yç¹±2 É’ {dÏûìD “D‚véô+dt-ü²’ù‚Žç õŸxILÿâWÔ¢AíêÚâ^9¶ÍÓà•N8oÔå´à¼¹¼w'LžÖW>>eeÞ;òÌ«}³íM×çaÙgî„ÈN¤Ã6Íin|k÷¶Rqöœm;5øMµ®ed‰÷X"ý<Á’>LÂë(… ú¢Ä "æ¤~’¿={¾½õÕ…÷ÝN î6øaàJ6?d6SÎüfÖ­cïc›÷Ÿ¹xÉ2vPW?_ñá@Ÿ p‚ÚàÓtŠc±û~ZþólšOQ«Á„~JØPš ÖdбPF ôAaTÍ\Ý&-•9摟Þý׫IOýeÜEw8(#øöÅ×ÌÌÌÌxëÿ~7uÚ»›Ðÿdlœ;8öZa§e²xî²-4zÈhz¿WÛ%C‡ÒN\ÁÎØ’™Q¸jº>b÷î&M¼W{éç(#•2‚ÃÌ3" nÁbŠsEßÜ)l(fæ3RØÀ¾ÂâÙßÿعπ3æ.7Ox÷»Å•áhca”† Õ+^Ψý`ž†/eT!:ƒÃÆ:eûº5ßìˆYCfqb¹)aC™Qi5d& úJ#¸€>(R»ÁùƒsÃ{•ÿÛ‹ÿüsÙª˜ Mº øgýZÕºÜÔÈÔ®Y=ý™Q ³8Æ?²ûáŽÕisy˵ÝÏýíÕ_þ\·)OpÞ`Js5P»Åy‚ô æêܲRH-ÄgóV#|rkk4œŽ-êË|PLvF`(Qæ‰"³£€a6žH49b“šò»»iïQU¬H{曊AªÉÞfª†oºhX»šÌÖL­Éíý;#ÉénqýîØ¼¾LÈFLCl3Hƒ%ôƒ ŠÊ©ÀPáu`6S:ù^ ™Ï¼“A)§‹ï}vêÔ«Ç 3ÎhDœöŠ—¹ âÏ%ÉoFø£vÑŒf¡^†­‚¤Ã¯¡2VlÛ{ò®þÖd«9­ùðSá—ÝB§%ÁÇØYp (öxÅ‚ž+jŸ üƒáëØçjZ†¸„\iü}îF.šv)Ze»Î‚q0ú–žaÆë“Ç»¢î6Ø?~´ÌÛ Ø²zyÌþ-›b»6,;;k Õ0¥bP 3®‰ˆ:ýµÌL ³Â3a#` SNÄŮڰä¥iiW¹"y9w#à„ Ò-£`ÐRÚÀ…ô¡„ ~¹Â­Žr‹¯8h‰˜—šÚ¨œ«³@ðd¯bªjâj•6"p2·“qÚW•ŒòžÃbê[’¯¦Y0&rþÈÊ̸ðõÛÓ?ËcΜ78‡PØ &… é¡ÌBÌT?ùi%áê`²;Xñ@aã÷?wŠ£ÈÕÔ¦M;bOJÁ[›“ÀŒÕ`šml‡Y”6¶âøÎ[º86mó¾ÂÈÅËž m=“šQ`ä1íÑ©Á|óM²…ž—mÃ{¶“ø˜h´%’evh[õCvóмä ÂÊ/X»¡DƒÄОmeŽš1±*W)8Ph‚Ò•§ŒßÌÅD'àãöþp÷rT”Q>ž½\úÜ?´» 5ÎlÔ5ÐÃ]Û¶ç© Ò²óÆ?ŸÆ¿1Œì%ýKèÁä€Má›ÁÅòbÀÝÂ?jõšÌBnôp ©)æesgý„k ›·íØ´^“f­ƒBBÃËV ð7Ì#ò“¾Î9##=ýrúÕ«I'ÅíÛ½_#¥½"ƒ@FB9îJ«aË,ôä”6p!}5HµæTÔnpÞò[úóL΋š·ïܬNÃÆ-‚ƒCÂÏ„Wük™ô׿ÎL¹|™æ7TLÕjÖ ë7òŽ1þþþ9ËHXÜÀu Y™™é¹åÉÔå‰×ã§"ðñŸq=ãÚ•´«)çN>´÷Ðn.çäd¹qÞ ÀAÓ9eFŹÂ:f8.sðçö82[›‡‰H`Æï§é… šKQ°P&T,“•eí¡Yø~á:©] `„eMÀ9ƒ«i×ÄìÅTTç€Ùrc §­z#h(ÀPØØŽvà hÍ¹Èø÷hßT>¼ïh¼80 Îò°0mà§?1ĺ`À\È»"… ¶Çð¢M£¨¡¹A\a&œ€ö‚9ti)Ï)˜pÅù9Dþk†„«ÔhìŒ;)Öï>,… ²mO>hü30P 0»ÞB²¢Û/ânaƒ/ÄÙ‰«RêC®½Ff #v×¶]ØbqLF‚qŸ­B Žsfàu ¨™cÊM ’[2d£À}aV% ú¢J ¸ƒ>X§¢3 ´Ú¹A !±;¶ìĶ÷8wHaA³—å/œMHóéç^gÜ«sõ¬ŸQÞmøÚ­ßÎÿœ7¸q®P†:æxsn)óZ à@ ý£[‰\&Í—Ï{Z4Š[º¶’:¨jvî­‡AžL”Ì6™~ÿ\][V{LÍ“›*Pâê¼{ÛÆbâ>„(¥°ݪô a{ B ¾ôüD “€f_ZÍ$L%e’7šKm·¶ƒqN3ª 1þö¾âäS÷J0á|¢DXŸª“õp>RÀgŒò®Ç EœÔ"çµQÀPB†š;8>|®LCL~ZÃÇ¢'4?ƒÁç1Wø ZDISª5Ûâ¤)‡Ö„J!¾m  ¯Æ.Ø•? ó’µ?.Ý$M­è'bí*¦U Î%]µ |øçæèÓ±¹Õ¯£ºgo©2η¿¬Ü*}2â“ÄC·õÌS^Ô¯UMüãáÛÄÖýG¡ÅÙ ªÄ‹¶Í¢äí³hŸšBbRŠh “.¶í©ëÆÞÀ@YÇÀ„©Ó£³³ý-ŸO›´¥$¸ð„°Áþ©»öXi;ø‘!³@ó Zí†6Ô· ðÔÇž{~ü³§e£À=¯sŒYN=‹C»`Ї]´øÔE5Æî "BÑÕiŒB­š;ÈÐ*aCSpà<ǽº¦ž'mªUs^SŠQþÆoÜÕøQ¸Wóç Ž¡08¾jîàDt¨}èó¬ßÿÜ%Û§„v:)Øæ•«iR«qs®I•³üÚçÕqå°`Ùßî4©âf ÔÚ°½æ0¯b^„ªa»E… åá(^þÁb)Bî× ¸‹‡¶#UÜÔ¤Žm¥ñœáç 00Pl ˜3Ŧ}Á€bW‚=%l°üÈ(ÆS1«œéø‘¡Àa+h(Æ· г ÆŒ7Ž¥–QàX…Q0è+àNú =‘ùUmî8Ø.P(C JSÁ9…Àç½):åuõœQÞ=ø!~ j9~Ôn¨yƒçJ¸)ÊÜÇÊ0JÎ}0úNÏ[ ¥èœU‰ŽâÄì’ápa£5riøû›dm´Gå s½ü#6î9"nësÃi]=×°v ñᬥj2¥czÛ&uå-gùÔ³¶ûh˜R}÷û:‡Úls¶X¡çë_× fxfÛ}:4Ãq€ ™úõoŠW>ùY&U çò¶MëÙ6QêÎgLŸ2±Ô½”æ…(`ºK+E_]&‹Ô¼¿GM—È .©¤/¬>ðŠYà‡E1 ì‹Z}T‚†ú8¡ £¨›1 52ÜȬqÕ^]羨`ÐGQ1¦¿òž¢EodTÕ¼¡„bEs¯½Ï{Ò§–ÞäEüãÜ£6íüc”ÏÁš£Þ –Wxç*ƒÇ¬CÕƒCls0Ÿ6?Ë G1n¶ 5U"cöáÄû­E‰Í¶u“ÏiŸåõ:ÐØ–‡Ã¿|¤ ü5læ\ï‰(p™BJ”NîŽòLÓ×¶y~3„nZ°ÍÓðêø‘2¬®ŸÉ/ƒ,ÒU¯ê±z<-l¨S~€8S©—úi?Lêc¯ Ø2”øf¤ ºj¾à^öší±êŸvïì9m9ul”¿w…íÞ~x]•ãø)!C]SÏ{c éJªL ÈLã}‘'#¼rÅ|=¶à§IÆ^+hh 9Ë? -W”cj2£~8*o\×ÚyãÞ#â‰;úIó¸C§íšÕ•¤ç¤Ëÿ<7´&Ži©$õ—Åg½I~`²›òf‹È41í•]½üÚ£˜žî¶7–ììYŸ¿5õ‹Ü{|V ücX݋ϗ–ÑEŒònÅ1uªìMò\"Þuÿ$}üeÊ›íËãV‹IDO©úÈÿUs]›FM%Ä€EX‰Ì” Ì–“~~~‹?{}òÖVéèqÒ‡óGg“¿9{ÆÔ—w>>yÚc¸–wþÈlæI¯Fyb0gîô ~丱eÌrÞÆüÁkøŽ!|îB˜mÑÿb˜& zf|®MŠ\‰ã çEu¶*ܬÖÇ]É"i®5wùf±óàIm¦‹4dn™ï®§/ †‚¾ûÖnÖ ®|çÒP—ׄ'¦þ743+ùÌE@ ð骞dN;•þ玃›÷µÛ¯èÖ {}úÊ©O´ˆgS”×^²å… ‘èjü`¥ê:”óHµÿõ/þ/2áBråǧ¼þOQ§Ú73ƧYD‰aêÔ¯Ogž~ ±ÓŸÏ2gEšüL–°à@3™ô‘0tŠ$õ3'_I³¤¤]óL?åõ3PK¿W§|¦N}ˆ>%Ûù£nD5cþ°Áª^ç?OÌ6¨ðÆi©žè#ÂÍмé§ï°w˸æA L˜<­›ûtú”•šõ9:mƒ2K7î•FöíG£æŽd‘ÄÛ¯«·ÉˆkRºzåPéÄëLT^—üû£ÿøç‹_¼ñêûÅ aÌ%}=ïâùC//'ç¼ë×2.%_N5„ ½ŒŠû‘|%Õ’yýzš,Ê÷Ïõ=4YœYø,2KýkFI3¾%i(úG·DÂʳHjyyVŽ[q™xñŠõØ6yca’EfeeÃ=CF¦²V„ƒ¸ãg‘TÒ"¾Ð£€ß¼øsÉ>#l†Ng¼>y¼z¿’ì=-l˜jV­ôxýÚÕýïº%Úd8k•dèôõ,üâÿù/k,XÅþòá_ úê_¯}Š^Và0Áÿæ9üvßíÔ¢¾åá=Lî i§/Ì•Þ@h/Žæÿí‚ËÖÇÞydÒTÓ—oN¥ÀAáÂ~ù£”’‹ æ=`†t¬hÙr--õ|Jz†‰>2!” p¼9î×ÓÓsßXKz@‚¶?>K§Œ*6 ySŸ:'Ž`صµð÷®NÉd˜äEÎ_º!´p+ä°ÏÏBËb›_Fƒ\Ppï“tºjëAK FCcrËøsI"²Z%ù²*YdÜñ3"Ûl§Ï]Ê'$h±R˜d‘,ÏÅíNÈvYç.8Š3 C8/\·KšNщœýñð4zBØ #áwhϺåÊ•û?BO´é+ã]êú ‡nq[ïö¦ÇÇôø·¹ÿk„¤øïlÌM÷ßÿ÷`ÐÆ—u"* j4JRŒʃŽ1"ˆX‚‚Cfôé327 Æü‘{¥û¤ó‡ž"™7tˆûì½[66gg_†Ï’bðôÔW£/nÂÇÛb6_ÚºvE,šÐÒ„›Z,rµÚ>ù&%_ÿû-Fü㣟à½QtkÓD ¾¹­D#DEÕ ïÏ\*ž|ó[ñÎw‹ ”0g´c`²È“g. ÔpXèN˜tW®,>@½Ó>ÿUìˆ=!Ã9¿½H8Ÿ,^þïÏâ¯o'æ,ÝAÓa5ººái:õ„™…€ºQ‘oAÐ0B–êŠÜÜÚ™Ð>}ºMG `ãä¦VRlõ+W+ôi¬BÔ¢†a:e‹žÒwÎ1;¨‹ÿ»ß-ŽŒêØê)±zþ;¹ôA“*[0æ[Œ”óÂÎzÁç75Ç1÷‹LVxòðÁ¥~þþwÐg‰¦„”n pœaFl:}üØoаWß?=°¡>O§·C à–žq]U`¬šà®d‘á•BÅÄqC‘œ2K˜¡1a2LÓ¯ýe´ì ÏmûÃkz„¢Ðé„©Ó£³³ý-ŸO›´¥$ïâlŹ$õªgM;vôé_ÿ~Ñl6V}ÙÀ@hHÈm?|ý_¥Ý°§± ýù—+ðLëÆu,ÆÇ¸lÐß’cÍ1¯ôN•¹í|dÌe‡$ò½i!æ|Ïxù™J Jظ¾nႥ欬d¬Àšifa@éÅÇ—ãŒ|5—b~Ÿ?o*NìI¤ oM¹¸i¡TЩ3Æ^%‹D€*—B…òVAC[1ûâ¬?Ú²Þ>.*š3Å&sö›%í·íǽ¤õÙ>ïת÷ ¯\{íèésîn˶mãÜË É\åÈÚÏwé3¸*ºbÏTÆtïS/ ²¥&|<\<-xùåæ ÄÇ41òÁñÑ(L-«- óGX,½ 1èéå•Vƒ™Læ“ÉHKK¹º=fõw4µ`p=uØè‹k1Àñå8ïÞ´î“”ØúäÐi4¡´®m´€ÚfLŸ2‘›¦˜A§d”ÅÃ"Ó©É ¸•Ü)qð÷ è ÂB;:=Õ`Ž€¶vŒì+°íÀq„x;¦ëîþ³÷¾fÚ?ŠNÚ ’>ÊD|l òhèú=ôÐ9Ž77{Àð~ÌfêKÀ1çØ‡T ˆ~+í†8t9ø~KC_Ìz|=2rüÀ\ÇFaƒÆâé;Ö­Ùqdÿî_…Íôż5Cì”"àxr\9¾Çãöÿ¸yÕ2j8ö¤Òi‚´¡0èT/#áÁ~x›NK,­8Áp Í«†gåÆ sò€£[¿¬Ü&–lØ#F÷ï$Û©rŸÎ]%“©Ü;¤»ºäp¿~×!±bó~ rÅÑ®Y=ÚËòÈv,f-Ù(öMLwO{¼îHÐÂì”årPD Î-ëãZG‡mÐÉè¯ÈûTãU¯RQ´jXK ëÙÞ®êÍ^E±ˆ¢P?²št>²w_{m˾cpF2‹N-h/ëê88°¼åzzz}tŠˆT“.';IØ×¯ˆÌàÞJØÇqMNI¯>>JÔ¨šãR„Uñ—7¾‘‰ƒ…Âdeg#BÅ$¬ã¬˜Kîq¼ ö²ö.X»Ñ7²ÅøÛû¹¤-OTÂp ûôÔÔzhF°ü0s›ËæÔe€bÀÉü¡Ç7âê5i—tLF“IÝ‚°UXùËO‹ÁnR€¾íÌÅK–±ƒºúf£À†mßi:EƱØ}?-ÿy6ͧ¨ÕàØ+aCi6ôò¶êe$<Ô=Щ;… ¹2 ›¹ja¡AdJdÞÿøs—èÚº‘@}Eª+ãz¦˜ƒÅ†~ZHfìTâE¨\Ø=,7€±ü`ÖRdÑ4‹'îì'"Â+!ns¢˜µh£¸–‘)îvs‘ÚbáGGõ–‚C¤-X»Sìƒ3åÑ‚ïà ȸ~:w¥xiÜ0Q³BN(7gå}á^ÕJ!¦Ë—¯PmAfR©”)lpÀRÖBø9çˆAAwÜeÅSwÝRäföIóWoóˆ°á¬s詯µ3gÕÜãØŸOJŠÄ%Òç#ÒÁ¥óGN•Æ_À“ùC¯ÂyŒ%iX L°!i{åüŸ–\L<“Ø©W¿±ŽP‰>K4%¤†ÏÈÃ,ù0?Æ2šÁÍY™)Û×­ùfGÌšMx…”ÜM Úož^ÞРS½Œ„û¡7:u¿°!L!åJÌL¶jX[œÆêÁ/«¶‰‡÷°;D›öEFÉX©N jU÷é¦?Lœ¹,LìrõZ­`³’q'å}dÁëFÈz£Q.9%MP£2¼W{ÁXÌEUÃm2\DACQý`¨´µ;âD_;(¾]°N€ƒaÛ6©+F#1 ?š½\öó½™KD€ŸŸxý©12$¥S‰I"õvoÛXôA= húõÕüµ˜üâEÍjaâ¡ÛzËwtÔ)r%ÿ§e[D܉3",$XthíK;©Õ™³t3prB0‘M¿Î-óh’T›EÙ•/¦ÑDÉI™É8ÙqñË/4°\¹É¼½;4+·8´§¡ ¶Ë^˜©tæ¢ È.zM¼üñ\ѪQmq5ýš •Ñ2(Ⱦ†±¨ZILÓW¾Údz—‰†ujä"g/^†P»A‡F¬ Âê ëÕΪ¥b½u"ª"~÷Uñ玃âÉ»úçA ÛüøÇå¢%~5,~\² Q2,⾡Ýü”X³-V´iRG,EvÕ@8¯õéÔ\ôÂ{NM‹×ï–š¼òåÉOÔ½ª>æiÈÍ'{?a¢JIK¤nÐŒºfþpókÕ» æ®Ìê8·QƒKaƒßWµÉoà® 1ÛîÜq¨Û !}²³³zY £)aÅ  –ÊX˜ñö\ˆþà×23-ÌÏ„}˜ÛM¿”q±«6,ùciZÚÕdj 9®ü+¿Nä4™RÇ–Ì»Òjä0.¸ C0èT‡ƒRÂ.é–N9ÉùDV¯,zwl!æÃw£aíÖ¾ÓcëþÖóÄ$j4±ò’WkÁäËW¯I“™l¬h×€BÿF¢ÖDÁÞ#ñ2›uí7˜yu¯({2ïç’RÄ`0ø„›öÃfþ"V¬ÇH¦óo9¬Žòý5Z4ˆ/<8D ?ø1OùsBð˜ŒlU¬¶ÓßÄQ;ˆ &Mwúun!¾ûc4ß™þäíÒôªOÇæbT¿ŽªÊï)ì1w½Š3É©b(uõaصµX·ó˜·r«5 WüiúVX¼°,…„c§Ï šöÝ?¬;è(@¬‚OHÆFE·b¤2ÂYŒ5„DÐIkø}(°—M=œîÜ c¶ZÜ-M£Ûµª>A šOÝ‚w¾ 3iBуÚë [F¼‰ó‡/Ї–‘SÇJÛAUj6°§°ÁM-ĨwS{Ü2À‹àظ×j5(LP¨ pA³µñ:Ç™B‰z‡ºE›ì :6èT·Ãå°cŠÖ¸×%ÚçF¾>n ïÕVlÞ{DÐ_¡G;ê-¸Z[[,#¿bó˜ÕË6î“fUHâ H\‚Ã7™:æÙØ}ð„ôC`t(š•P#ð-üî¹µ›t²¦¹ Maè ÍUit?v:Ç÷‚׸MGj[ ã/môÃOcaÌnødT]oj,‹Q ¿?iWO G›&õä=:–³=j($±î+ð „õ-ݸǶ)‘„úÖí<(/šÊ4EffÖá¬jKªU…?Ie˜ðD ^ÖKÿŒMûŽHŸ—Æ0¯aÞ2¼%õÙ@åD¢ÚÔ;Øž«ë^Ûßôqøìç•Àán8à })Ön•ø&sO-Us‡«·ÆÊq#îxý‡…ë!ÀøÃ1¼¬˜´S t@ÿ pKÇ} ΀´Êз§ÎÞ$ÓÁÙœæ€E:Ã7Æs=Û7ƒæÄ_ö«(Ï»¼lŽÏ«ÕM0ŠG`ùòù|wŠòÞ̇Bm¬žÃRó}8×ÕƒVSçÙo]h~•E ¯•UÌ›–Paq)pØ ZÛk6¶‹5†¶ŒÇ“›VÈ`–÷0èÔWFªà~ê–NÝ%lh?j»`4²£Gôé ™-õ…;n韅ÝRkAìÑQ}äJf–YFß¡34ÍR¨)`ÎÏÜ=y66À¬h%˜úí÷8iîž×Wa#üB¸)˜:aTÇ]uÑ¡˜ÖžŒ&µƒº·¶2¯½ˆ6÷Ïüû{­j@t+qîRŠ|”Œ*WÐùh6ò~¼ûÜÝb ü%–€!¥ÐÒ»cSéL®Úá¾ëM¤IÌw¬—Ìë·DËÛÎÚ¡Ãð&kYÀ ý2FÙ%Ð÷ƒšŽ÷g.•ŒÍt&Œéç aCM¼j/ÛËù§3*Õ¡öÍë‰fÐpQ@Uà /Vc  ™ôÑO2ðÀÓcHí5K*ú… Ò#ÍÛ”6Žù[Xöëßþ¯|ò³êèìݶiŽà©Úv´ŽTûàNZc$µ¢jµŠfb¡ð)b$*Û E©ÓeµŒ¤[ç{}¥óþ"D袊f•\ˆ >¨Eâ"@QÁ9pŠ’ƒÇ^©aepŽ?šÝÑý:ë8Wºªæ µ—ý÷‘ì³Zå&ÊUc2¦\ ç÷Wi4” ¡ýà¶:ÁÇ‘ÇÇTi0¸W×¹÷E0èÔG-Ÿ]N§¦NÎÎö·|>mÒ–üÍþJ‰'¶Ç'O[ÍæfLŸÒ‡û\`½42öþÁï¼òô·dà<éˆÂcÏž‚„?‚i:s†GˆÙ÷z–ŒÆøÛûJ-;úÍ4y²Ôbp‘W™Ñd!jµÎòt8zggí8ºG&$=ã:Vé­B’½~öšpݾ(›ˆ- %+~lIa½0ù×–£:=ÿ­8Õ78à ý(HsZMXa߆ã@!X«M)ì³E-·ëàIñÂê¾4n¨¨ ˆ>CD4F½rRº¨m¥ü¾[,öÄÝýí{oÞ‡ç.b£] bÄ<60’‡ŽÁ"D|—8‚BÒ•«0y¼]j§Šò^ŒšÇè`¹£QsX–¦žovNžÜà ;¸Áh}Õ!81̵Î5ŽÌíToïí̤ÉÔ9øþ¸½Ë%h—ßF J¸Ð—ø{ìö/Û Ø2r¯˜wgu1·Wóßûa‰4­ræHÐÈégÞa±gªeûŽÞÙY;ŽîQ¨áj·+àxÂyѹߠ&[V.I@}œ˜‹<)»¢®¨Ã^èS\p4Å­¯ çÌÐfЬf>ÌßBs?†×ÕðC'?vž˜?¸Ú¿|Ó>h¢š‹¾0$ähF{‰)ÿ÷³Ô\QCi/÷ QÌ]¾Yì„GA“΄ "ÿƒæj7ràÐl¹U˜w‡@“Gj+©Ea¨njPHCöràЗÈ^{ý)Ÿ!{yy8oÙL µvF(í®¢5’Éýó³ùÐG#Ò_=©õøyùV±yvè/Ö¹e}©¦RA¹[äK¹á_iš?rÑÃù«âÜü™8µ“Ÿ¿ß@èw£@>‘È+“ãÌ•[ØØéÓŽ…2S‚0[N zñg¯OÞªŸÞÙïI1„ U‘]:5[ÌQÂÏ:*LélKñ³ð[&×Щɒ—!-æûº¤m«?~¸| ¨Õøà…{%3æKýÖc_ç¯Ú.Z¶ïø„Uè_±Wôøn¾Ø'šiE„±?þܳ®™†z¶oª·×ñØüq‰¹ÂOóJ-T«\&„E¹Kò²½Ü'ÌY²yßQiîX~P4S°L{àõ„Ù^°v'„‹]â‰;û‹Ëô¾Anâþ\L츉o…Ù¦½8ÔÄÚËÁc¯?™ÐÞÚËËÓÉCŒÁºµi$"s£ØÑ´/CjRò¯lÇ»³úýþÒψf–ånQïìê})œ?L?>5(;Üï),"= Á?BªáÑ͈ˆÇ¤~¤{tˆ$K3'_I³ R¥ŸÙb™6~Êëg í~¯Nù:Múµm¥ ìÒiåà`ƒNu>Êz¥Ó_F×"«’µÏ9ó¡ÿ\Í3 ä -<>¦D¦V«ácâgÉñ §ukÜ)­õÔ%Û¾¨yCímï»ôœ¹vö´y4%d‚K¶¹OèûÔ±E1äæÇUŽ{jïC46Âä´!3Ï…—=Ðt0b\‡æQò^ëÆuÄŽØRØp”Ç^>lÛÆ8r”—‡sZU$¢l‚ j¶´°a÷)Ô0?.˜÷Eùtñš£Ü-¼ç(Eó‡žÇ½0y _`¹Ï-fs-h´, a}SãÚ&ø!wk딂 Hì9|ZlÙw´&2À¿}6;áÙG^šúÈ—oM]Šæøóe0èÔ—G/§ïº¤Sw ¾?\Ƹänòr8îhŨÓÀ@0P¹bˆ,Í5¶p5-á±oäBÑæ>¡/ï3š“=ˆBfzL8Js-2øs…>!o~õ‡ºm]–±—G™^Ý(|ãHÛ^-(/Ï'óÑ'$~GÊyœw)àÄ@ãÂ{ ånQ÷ݼ÷Åùƒ3?þþ¿ôÊ3þþåÞŠ¬^ÉÂ|Qú<"L»yLÊ\õ PÓù“°™!räì%›"NŸKþýÑüóÅ/Þx•aÞµ k¾‚ƒN}e¤ ÙO½Ñ©”€ Ù÷¢S ¥ÚåY£léÄ'3íV:ßÒx+W`@ÍjïŠ:Ö^9Dk؇ðÃZ`Þšó—® ñå aBë÷E_*ž³Œ=`D:{X¡¼`æùÿüíîÛówË¢*ΔÇFÈü._ü²Æ^ÖkÚþPQA ö÷çÆŠ¿Œ)¼s:µLlɈ} TÞ­–׋ÎäÚ¹ƒÇ¾ì'5åÆMœü·;µh`šøà0¥]ò…—0úèÇÇ óïÔ¢¾|´ÞydÒÔçPšc® 0yZ?nŽß@Þ1è´ùúm=Щ»„ Ž 0 î"µéŽú™™›y,Ê€ âdFZ㦋IØxç 0#V˜›a+œx] Œ6Å©>›?ÈL3¹a òÕ,ݸW&ád®›óVIŸ–›Ûæäò±‡C&iÜ~à„ÔVœ†o£} —|!99ö˼?4ã:|ŠÁÚr4gÎ'[sàVÈX´9xùÌÖŠÏfeg9ÍËÜ0t¸fô3Ò…-´ns®¸“2÷>òXå²-ëÉs?¬ ÜØ'Ÿ\®\…7 hXÝÛ¤=‰G£-÷`€ãÉqåøúùùÿëç'1´¢.³Å4™›“77èÔ rJÓ­bÓ©Å'¸•ì/½•°R<®sFƵ]0è\ò*=WCÂùKˆ0³UfXf¨Ü¨šÕÄ##{‰JH®ç*X°vrd#¼n?WU©ãzd. N¾JØÐÀÁ| yJG^e_¯9ù¿?K¦ù0ZcY ̶®†K†*]0[@nDz \)tÖfž †Ûý šF±bÄ)¦p”Ç^Ûá£sº³¼<½‘ÃgÖââÙw~ »ŽâóT1ïòÂã2÷F6ç »hÀ|Dy {ôÄîüAÑ+X¸V­:WªT¥êÿÕªQÅòÀðœÿ (¥ÀøšÎ\L6Ÿ9ošÑ§ÏÈ–«WÏ¿ŠW¥ ¢÷hÕdqFs–RZtöZE¥S„¼מּÂÞs—°Áö¹Äkžùá¿ÿõÝ[§-l‡ôPŽ‘zfÒÃäóñ3\*hðÑÓ›SÇÐ&¶¦¥Ç£ANzJØðXû…iˆŒ#1ü(?ÃÁ¦!_KaàÌ…œ•èû `0”釳–‰¦Q5FxztTo™i|’ÎZ¼A0C9ól¸è„Üúo>%h(4xtþà¸3‰'7æ¬a8bE ªCL|h ‘;9K2®g †V!¨mËRQB%ë á†ýD‚ËIá†×ï r³—ç®A]d+*¹JeÛŸgbÈA\ØwÛ¼< oKA(®*Tó§/ãc(T=ŽÜBÔÐQs¢¬EGfn ¨ Ò>«®»zï ó‡wæ<Çok…¶ýû>5é£ah4ì`ª]âøŽÔÅÿÝïGFulõ”X=ÿ¼NOúz_ƒNõ5é·èÔ]†’ä¹/¾]‰GPŸ¿‘c`4»cÕO9K’qTà(>>ãЯßu fsñãÒM0?¨#hVÑ vu¹ŠÉçi6ÅUÇ¡%Ù„L䌴B•Àz#ƒ1Gë!vÿx06d‚µ'ò÷"Ïcî¢Ó¹xí¦›ÞÓÉöà‰Di>Ò<7ùä6˜3!J 2­-°Ÿy1˜ª4@.…Ã0ÃqN–&1õà<\§FUi6µ‘ˆlW™7#ÊÑ‚µ»Ä´'FËöåõ/Hz¡éÎú]‡¤ónü¹$*“/šdÈÒ›Ûå aKºœz|í/£±roÔÖŒlٻĥ+©¢cóúbDß"~:¯ÎJ`( >ùgTGÅûùlå^±Õ–Ù«ß™&†²´4ì=ËkZ Ge>qèefæ¢õ…%W!‘CG*Ø{\\G} [Ÿ1òSÒrBnÒü†q÷ëÁ\kÜðž¢5 ÎÚSuúÈ^ ºì.b¦cÅ·.lï[û·ÇíšÕ·žö€BÁI˜U5ÄØ'Î2a¶åó3ú×°RÎ< ÐEœ3D¶ Í>³€DøÚiÈnÍW­m >KºäsHH%«âñ‹Ö!’J#1¢wñ'è—‰Îé´;gtÉ£Ðõüaƒ ö•‹xFÝ}u,Fðšeoh#F>8žãOZÐãøtZFèÑÑkzšNÝ%lµ›£÷ÕåufÔÔ½µ˜¿j›xå“_¬Ì˜6>~UDnQññÕKd¢®‘}™¬«šèÓ &df™a˜+ÌZØŒÕs®¦><¢—di¿M(¨=m:?VŒ£.™Ç¬,³L„¶CfN¶ÈñÊDXSÚì¾ûcœŠƒE ƒ¶úëüû›…â“9+D½ÈpqS“ÚÚÛ=ѽeÃÚ¢Rh°¨]½² pRXh×, ¹ZJ?¶àèéÂ>ê©rÚ¹#ïÅS=0ÚÑt=hEZUZÀª5"úÜÍB ©eoŽ{H¥°xkj¸HzšÇtI§L"Jkâk°.Ìȳ•U(,N˜:=ú±)o–ØïÚ]fT?´ßÐ{ê&;«C‹úîlË¥ôB{ça=ÛI“ÙK6ʨ4¯>>Òi||Õ h.C¡cGìqQ18Hþîz³ºmÝ']¹ Ó©ª˜ ¬—ä³xüyKê÷ +<ä‚ù‹VêdÅ8ÈNg[Ì©ÈxÉk6o/o{äíÓ[5¬%µO&Âã¤L¸æo#Ô:=}¡‡æ³—×>Ëöh®uçÀhYM{Ëá±6Ê5c4{š»|‹ sz‘Œž{‹|ö…÷gK?žüë™;íÖ§m³bp 5ƒ´ÝÂn¾xíz¦Rí4ã³ól â@IDAT‡w1.ÍŬÖ])Óª*ýêU ª`A¼{¯ÍmîzQ£^Ç`~Ì«æôÔÔz(EZ ó¿Þàl7£][ð ¾ Þ‹ˆÂG Ž˜¿ˆ~h4Wç7qfTCÐe¾nÛé‚ÎiRÌ:wo“ÏßNû,Ûúaá±ëÐ)øX¦#ò_øºµ`‘BµÇÎÀ„ú§}¶4–NÍ™â Sé(Éû»Kàä*™…ð5‡Í^ºÉa£$ýôʳ‘X1¦¶áÅç:‰«øø¶Ñˆ´+'K-tnÕH¬Ù¶«Ý¡‚ÎŽötÑæþÀÙüáI Óž¶-=oÙ{4­Y›öÕâvïH@ÿ”Àaíª93û\òåT¯ ì íÜÛ`‚¡¯ÍŠèWS`ÈÔuð£øÛý·ÊìÐΞ½ æKôÙpt8)2qûƉTíü{´k"(U( þz÷@A“=£')Á„te c÷oï9w\K¾’jɼ~/GPP*æõ2ƾø(hþ(~Íny’tËoª6ü"*W á5ʳÆï|RR$^›´@šÈkÚ8™1}ÊD;ÍxNF¼oç–/~EüË‘ßÖl¹£¿.ºïÒŽØ“b#|"Ÿ€)<-¸X§œ}GÄüÕÛÊ„°A ŠNM—È .©ÄYH†ájÊ彂ƒÇ$ÁV¼*"·èfÅ–ý’Ø‚aÚ´q÷ÙeƨDÄ uˆÅÏøø4“a¬úsHèÅ•Ž ºU}¬@o›÷Ѱ“·ÍáðKGðe›öA›%™Hú}Р¨íÙ«ß[×içë1[vìÒÂÆô#Ÿ†#3##!%=ÃD_JÚÞ„öˆÆ4Ùž›ÂÑïZFþhTY0_:í…‚*aA΀‹»UÜ ó»tÐ2›S€*[”}“z‘R¦u£ÚXo„¹¦²jk,½;É•Bn„d.I›EéŸ+ÊrÌ9ö×ÓÓr’LäJèðÉùÃx1êÈÁ@ó‡ÞÐDzU+Æ4 ô÷ó ,WŽ× (cà¸û SE¼¶2£"mÔüæ-Œx•N¹PV'¢ŠÜ˜óGëÓªE}eFúíÒ´YÀü¼¾Ý¿“5x…½€:ÚçyÌpô 0Ëzò®þy¢=²]†§2A ünÏ„/$#J¾üñ\„»¯-#žE¢ÓY¸~¡ë«ÀDzX¯v¢Ìá ,Ï$ª“¯JÿG¶U‘ú˜\õ|7#`ÝÒ½mcѧS YÞY`oò$ºSØÈMŒß)0_6hÊ´ïp¼X²~ Ý># 1" W¢¹9Š/_ÖοŠ!A’¨™øñÑ}ì”"!%>/}D~†iL(L[ÚîÞY<~»éìâN$8cl:q$n=º¦4¨á XãOÆ„…‡?³çði8-7̹ê¥ÿLjƼ*[4ÈgÒ¦º”œ’*þõ¿ßÕ©Þ»½ › þQÈòj¬K´FÕŠ¢W‡f’&æ.Û"úun!̱aj.¢àkÁ¨g4ùcT3ú^Hó-äŠðà˜“>â!â´Z/ëÙ׿_Á½/ôÓÉüa¥½|ü¦r+Úö£ÎYºƒo{Ez¢¯ÑéY$3f˜ùDh66í; &¼¹Ý¡þuõv,ГY€¾´Õ¯“ä%P‡Q ;b ¾ ¨£……(¿ Ú‹‰É#h°L,âÒ ‹#‰1‚!¼RŽkH¡bÜð›%–pæÏ^Ž…Ðò2wÖ~ä­úÏÕ¨‚ˆ’øæ2Ð}0+…‰¡=Û .Jg¤=¢<" ²4Õú ßïhò6˜µØå1„¾g”Ò·¾þC†½g` ˆ>¿õa&Má†Á‡nE(vwƒ§èÔÂ?æ-+WlÙ±Ó…µÛ†ûBTæ;`Ü|2̵aÒÒQ||>7{À¤p¶ ‘O‡„É•ñTD¢°¡ü7µg[ŸÏwÄž°˜³³Îl_»êú§„ %hpo^ýû¼½Û´»¸eßѪ689{øãVÀøÓN¼_J§}­ÀÀ²Úòª s89ië³}FK¼Ç| ôë0á9¸²Cøê×?11†Jó)Ò'³Q¿úé/‚¡­­©-]Ú¶ÿÄžUaËÈý‡1·XÌæä­kWÄâ’¤‡Ü=KøäüÁŽà 0¸¦×Õ¹‹›bä lÈs×µ`ÔäcÉ(Éc‘&}p^ó&¨~x…N÷# V¯¤¦ .¨Qko6À¢¤s«b@—Vò6µ Ô‚PØÐÔQ|’ªƒçÛc‹…1»ÅÓwß""«UV·¬{Z¢gbc Òƒœ7ÔÞWçô߀` €ù£5»õQ2pjËë¼çÖfÊõˆjm5ô@ºð8L˜<­ýtú”•šÆrïQ:¥†žÑ:i‚Îÿw¿["þó·±Vó(ö‘ß4.º*ó&^« S¥¬ô󞣀:,Çïàw¬— …t@wÌUõÚ„QJvÉ4¡0ïRQ@µÏðK¨«©«.ÌÀè €ÚmRÞ=‡âÅ¢u»¤ö†!+; ¼Mm‡]¼ ÈStê΀d$çëËž;"ݹe÷‘é4 ”c€áä¾_¸ÞlÎÊ<³bîœ9ÿÏÞuÀGUlýÙ$@ 5ôÞ{èDº+"‚ úðÙ¾§¢ QÁîSŸÏ'QŸ P,(Ò;$ôZBï¡“^H²ûýÿ“Ìr³ìnv“ÝÍ.ÜóûÝÜ›{çÎ9svæœ9 Ýå¸+Cõ^ÑGöº¿ÍÃ÷•¹K7oçPt 1¥êš¯ÿX/&‹|;/• Úõàs¬M99×¢þZ°mV†8Ø Eúüá ƒêÂ6:8¸ð‹.©ŠŒ%AÃ`Jf3÷®þ÷vÅy,-mxF“a*ÍGµmÉ:BG´ºmõÌ6PSB?Ç£§/Z-S©|®°pßWÀo‡Â<^-"Ð"†þôãøð¹ûÅ“£nXpØ]NÂ÷ãêc`åï«‚ýó… |åÏ? ªõÄÙ6š qP~Üp-dkÜ©Ù03 ii‰){¶m|¥z¹^ý yÞ«Û²r´|äµM{á0eصîß©©‰Ì\¨˜HÒ„V«A$ëÚµ‹I±»·`h×ñíïF™»»—š}¤ÇžmfT½÷öïäÙºðkãsˆ‰³%ú‹ädxÖAƒ‡HI'úü$ÜŽàÀüá­há¼¥æ.uöÖ¶šÛÅ]fíάù~Q4 Ü0£ÓÒEÑêtömƒU¿!m{>Þ…i¾óGaª÷Ô;eà Ó)î(ÿ´l+M]a–’*#E2@ x Ü"9¼™<Þ6FcâÁ]~ £úu&Odĵp?6Ó¨¹ÿlÞ Å䈦u«‹Ñˆ¾H°PGû>¬×rþŠ-2²$Ó(¸ Agiô^‘˜’&û´j(uËõ»$Óµû°˜òÙ|D£ªüUýäñÍŸàùW þÈÉÑ‘BsU§:—ý»´”¦YôéÑÈœîÀ^`/ävZp§°AêáN%™îns³äˆQæôÞëW'?9î©€€ë‰PPßÃ@VVVæ»~þ}ä[mAëp0YÇ^1’d ðZÑ™ÍÔÅs¾;ðþ1¤É‡ã¯\3Ý? ³ŸîáÐåÛgúhÐtŠ;IÇc÷Ï_ùë<šOqN (aCi6ØY}þ n#prþ¸0ãÚ®’±cD箈v5«†‰ópŠõ6¸Ýrxþ]Ýž»Çf•ôµÐ/an©'îé#}4H¯dâПÖZ@Ë`(LÌÌÃî€Ã#¡í™W Œúò£CD|F‚Š@“¨i‡#x®ßW`Ô…÷)Œ èÜBú0°‹‚‚»ør0 ÕG{ç#h¯Táž‘YP»“d&ø-Æœ.ùÂKolX±&êrÃ.ýÞ€Щe}U_–²:x1øƒ<GpHìÆ’¦Œ˜ç^xý÷ Ñ[ΡÉ4¨¤‘#Ïd&©Ý ™ éAma(f’÷¥°s©¥ó~ø©Cï~ñÆNÝ&}ôýÒrˆÊ`Â$bh ûÎâÎÃöéà˜GƒámuŠÎàðáIÞ½îÛ]Që(Œ’6}pü••–>ôùà |ûZÑ"ÎÞÞ] Kâ]Me>&.ƒî\w Œn§À^޾·nG¬ ô²áCÉÒ‡Œc¹áâK#GÑ`´¸ûL`‚rÈMÀ§{‘±¹jÅPñè°^2ÑV„&]¸~xë©»eYÎ3f/c†t•&,VsÀ? þ€Èapu„BXê"ê!Œ¨…Æ€×Ò){d+@ ŸÙ ¨Ãç=~3Ø"”.ë¢Ïˆ£Àß…5p$°‹¯²ÖOk÷Ü)lð{ŠY As‡[ %–.]uÈRå·ÔÔú}cOÄWúv¡Lao¬P.ÄZÚ0~x|v¤I)éÒá'ó:-oò%P½¼çð“™•mJHI3aL Øu0dge^þæýéÿŨpŒ)\PРw™IšÉPØPfT¸4KÞWÂ(é#`ÛÚ•Q¶m‰í:hèМœìþ`TC±óa*TÊÄŒ¼z¢,3þ¼ò"#+ËĬðLØGúÀ&ŸŒ‹]³iÙ¢åii)ÜB%mð (Íé@ ¸” Ïyˆ¸•æ?ÎyØÑOÎ`àlæ ÍêU³úš½×¦W´G‡æY¬Fܯ¬—9¨F÷ë(6ï="£ )aƒ6úëwÆÉH?÷C‹Âºÿ@†æÇïî-2°–_Iä‘ ´¤¹‚(?4· ¯Xö¦œ,E§ßà ’]:@¬‡Ð3wé&ñê„ay5è'Þ‹ å+! Ó<Œ&‚ Ëë ]&ENãoúò­)ÛŠ‚]w le.Cf”â!¿ë·ü×9óq^Ò¤m‡Æ5ê5hR!>$¸<øÓ”¿ÿ}Nrb"Ío(¨*V­ÚwÄèQþþþ¹Ù[À|⾄쬬ô¼òdjõòÄŠëñSøMüÆ2ózfFRZJòÅSGï;#Ç\¡‚Œ#® d$i&£Ì¨È8šÇ ×Þ#mpW›åò¢c ’ÔdãŠ_æ’>7iѨVÃÆ-‚BJW(X6À_7¿^¼à$—™™žž˜ž’rõÔá¸q1;¡±J{EÚ  A¡ƒ×w¥Õ =X‚>ÜóŸ;æKZÑÿwå‡N¯¶’ìÚËq ª†d¦w Û˜#h"IjIJ‡”ÿü~)̲¡ÅÈuüeþ•#ˆù–Ã1¸ °–ó€áP!GäQ}EhP:·j >ùq²7'›³Tïmøœ~q:x|5°‹1K¼mlšèW4zRØ CA†ÃÌPâZ !±»¶íÆqÿS÷$…ÍY–¿|þ\êÏ3?ýwÞ}2 <ÈœðPÿ«wýôònÃÐ-qÎhåC†‘ %l¨kŽ·-­Iàø±.%(ò¦ºÇú3c÷ìØƒ#×TI#Zâ˜ëààï ~—J䨒F(|*A”g{Z/<–À:U=úü‘;?ªùNáYýï óUûû&]eþ`}:8æHËàÔz3”ã@½Á¼„Òyæ&ôûÐþO͇j)0÷A2´Ô`XhíA܉óH¶kÌ­€¦µg.&è†BˆÅyÖôi/ZÜÒÿÕ1à &—È .©Ä–+†Ai*øŠ–`Ðbzã°=jÁäY1•d0µ‚ˆZ\Yí,Åwôò74J®Â âžø¦ð¨˜H2ê C¨„ íŽ5ß³Š6Ô3U¿@Ȉã`0¥S‡.h(¬yÏYéŽc¨6HJå™ãJúQ4b‹>PD άOŸ?ò ĉ«~ßîž_Ý5>tpÕ+…Éü0_Mê„ç{³ ªp MnÞó7px­ƒ6_Âä ­8ž¤E'ŠF”0ªh„åÔ»¸´ Š.ôù#?ŽÕïŒøñ…ùOч¢ WÌ6‰Fp3Ú4®) a¨ý(¨mH€O¤Ñh”IÆìå8¸¹¶‚ï0´nôîCpä®,Þ6ª]EFêiX+†Q…Ú¢~u8žÇå«Ì2çß/(#;óhD*,Á„‹ïê c@Ç€wcÀÓÂO.ŽÚ‡‹%ÉES:(h(a—ò}ÖAF…ë%¨wôò¹8r5~ˆ_‚GÅ0(C1|΃cä(¨ñT´Á¶³^ÒÒˆ–N”0ŠÛ:xÔøqü9†¤ KE'œÔïÖQQõêó‡ïÎîœ?@R:8‚†¥ƒõ‹7‰Y¿®•ΪŒ€3°kk)lØËqàHý–e˜%™‰Ô¾_´QúŠÐ±œP9¬ŒèÙ®±X°f‡øeÅ6Ñ·CSD® 1¿n-çÁÄ{zËz^ýü×¼:BEóIwCx1¿¦_ø0â}gÜI1>@¤ÓâFe£¦Ïí¸€èlÔê)¿¦âì§§¾íIaƒ}"Ã@&CÉ8Q‚©KQ˜ºæYûÿJP̬ªOÝ'ªUŸéås1¤pcɬ„5¬EŸb&µÌ£ª'÷kÎýUõòÌzX/U Zm•j»v|QD/ÀÇNÇ4¢èƒgÞS.-}¨zõù#ß ‘…ý}«ß”ª‡c¤Å·º_Øú=1¨6êç0@3&&,#ÃÃ|Ú°âörXæ2hZ·Z¾üÕ*•Ï÷?›‚èQÿ7vH‡ŸˆÊ] š÷à .âÞþ±ès(Gr>·–ó€y—Þ|ònÙf–±¾”Ïow˜4õ­¾ÄÁÌéÓV{ ­ß-Öî8(>xöþ|ŒúôÙ@‹&Æ ëa³)4¥[¸n—Ð¥…ݰ·6+pÁƒs—®‰_Vn‡NžMDíªÅ„=EY„pv,\¿Kä ¸ÂÄ{äð¸ªZ¯®ÇÓ†B2Ïd@ÔB¤eµ÷,¯C£=³>‚eYþ¯-§®ea½¼Ä—‰öl ?¼¯ÊqüÈôñ¬îá²ÈÀúxpìX¿b„££ÆtðB (ZPãÈÿµ×Em2ë"ð¬Ï¹¿=‰üQ¿ íY‡ö\\åù]ÕŽŸ;æÕ7ý\¸k«4´Åíå8ЖsôÚRÐPïôk9t!CaÏþÙh2LÍ+±Ú~I×=hZGüµa7¢”]ËXó¥kIâÌ…kbx¯×}ÈM5-Ú°G†vž2~¨`´ñ—]*h°Ù"µƒœÝÔ—Vk2ä·o,dåÅ%l¨æÊEçÉiïD@Ê€›µõ¶VÉ2ªÏ&#‹qlL\@%`'DÞ4ÀÉÌôòä8܇ >ÙÏ$¼ÏpÊÏÏoégLÝn9.úŸcœúhƒ\ 1úµA C>úpÑ·ôj\„üNiÊpNMž ÓãÓÞé`ð7æÌŠ|u÷SßzÝx _WL’¦œœ¹_¾9;ï™XôòÀ†çñÃßµ<"#sçôÈÈ¡ÌóþOc€Iþè—¡C1aÀ`âG!¼R9i´+î”YØà5Dæv±—4RÛPj¦ÏþS<4¸‹L"Ég_!§KÙÒbrº8›`’ßýyùV± fZÔÞõíÐLæÑ~“×ÇÏ]]V¹:|™ ñ¬€!­%•d[6î9$z´m"~Z¾E´jXCœ½xMÔEÂÉá½ÛÉ×ùîgóVˆñÐ’lÙ{>R&™À’7ÅAȒߩZU*ˆ‰£ú@#XJÞ_#RÒ2$î‚&Кð­ÚçŽó¬S'º¢Þb6"#¿ <›uv2²@>ŸmÌÇ‹©Lp ±\h0“¶yüâ dÞ.u i›1!)Í”œ–ág4™Þš8mF<Â~\£dÏ"#¥ÉS‘á&úð3˜Buú(2^=Q'èã©ÈÏKge'L@PœIXDšÔ¬Rñª1ítú†]‡ªlÝwÌê¼Ö±E½ž3_;ý…Q» ”×Þ2_ëåá¼ëü`3ý:DK˜;̘ýŸðs—Ê=1mÆ¢FÅogMœH³É[t£È,å:x5ÜB§í›Ö…ãÿ!qLä»bO úàÐTîw˜(mÝ\ îÞJ>[µG”ó?²o{ù¿úC?•äQÝKJIjÙÙ“ëw{œãîê)SÓÄ÷E‹¶k‰JåCUõòÜí\¶iŸ(Y2@ôißm»±œØJ*ɶ<~NœFÂL¬È9S[–<-ƒ(acGì qOÝj•ÄŠÍû!„Ëý.qôÌEΙ&ŠÔ ]„ˆ‚Fbrš¼?²o„¨ƒwæ.Ù$Öc>صe¾öúÊ?7°èÁ?þÊÎçœû ›[Õ›Õ«n"’[6¨n€:—¾:x?¤0˜–‘)¼Ûö«Š,ßïcLŸðr䄯Þ\Ž.äþ’ Ñ> 4ïzŽôM†Ñ˜ð3Tžuj‡W1uiÝ@thV7Œ«f÷¶äá(:ôòö1åü€‡`˜óêØ`ª>fHÚG›Žœ¾ð¥ß™Ë¯?þrä£Ð>­Âs·0Aö{ëÚ§Hn™Á›ý¸YýîÚOéµy2®g™`­Áß^î ÓvMkKSªç.‰òpú?ŽdŒƒ»µ–¸p$i¤3Hs4Áä¾#§¥Æ¡J…2ˆÆVFj_vBÐ%?ó>úÎŽR{ÀÀk¶ h@AÉ^RI¶—™ïG‰äg Á%) õkT;žà:%°Á.Ÿ«?Ü좉áøá=ó:Ø{äŒ Ý®ImY´EƒRhs¥°áI:õ´°a€Ù³ÈþAx¥²z; 8|åǼýìõ ­o'Hñ8 Ì";oÙ–*g/&üõØ+o¼4ûí×?A(p8Ã4èôáõ£îxÝ@â‰iÓ'L9— ñãäŒI\Ÿ?¯+É;8¸+øËÊmáˆF³±añ-ëìüá-ý“sÞõŒÌk ‰©º°á-£âÁv$$¥š²®_gâgÖ?¶P~ÊmtÊ@ ]¼æSeŒ@™P9š4Ò"ròLêµÏI0™ƒ°Î “LÆûæ×ÊèÇ4´GѤxšÍbÖokÄëOŒ>'Ô¶XK*©ê @  rX(4! œ@Æû iVõðnê±ù|5)¦Saù >äœH³ªw¾^d.ëêm OÒ©'… Ã㯾ùÆê£öMë˜ÆÞÕÝPÒ"1Ð ŒêW¾†F yiÜPÄn7m?xüà S" _½Iƒ ŽL¸:}øÚ ;Ñ^пf¨Vö‰:Õ+ùßwgGC`)FBÖáVÀ£½øÈ`ÿ/_g‚–ô«ñ/½ôõ{oÎDß|Iàà<§æ:SFZê¥äôL5À¶œ°o…±ÓûoŽûõô´ yO´t‘¿pñü§mÛè”&A{ŸÊ–1›P±»0•ÚD‘è‘s¹e øƒJìÐDIý *”½™÷I0éºXïˆ&0×rÌI¾'ÜÐzé_?K'ñÒ0m"XK* ëùŒæ`Zèм>òÇ@HçÒ2· …K((ž·¼ œ”å!¨MÿÛ=7=sÅ GétRäôŽ99þ¦/ßš²­(ßÍÍ¢ÔàØ»ìVÁÜï}Øò™»»—.h8†7Ÿ*Eá‘cË1öóóoìóS¢üõ´û¬Ó‡Otá[úàý2“Žœ4²ù#C»é‚Fá†À«ß*U²„xjô†ÁÝ[Ó´¶«òê4xC¿ÌBCÉxêpÜì„h'®Ã탎7Çý̱£[I8´´áiD° <hÛâV:maƒÂ£REÀ‡CJ‡Ð² /KçñV k©Çæ3UÊ‹0?¢ÿÂÊ-Ì!Í…œ¸ Æ–ýGE܉x„5ʶ1J–22³¤éÓ©óWÄå„d@ù˜ZmRÉ+xF³©}Gíÿ¶;6¯ƒH\)ðQ9 _ŽúÚO™¯›`£%!9U¬Ø²_\ML›áѧ…>"¿®Ü&JÂq½6°š2’ÕŸÈ;2ÅV™³¤ç2ñ¦ÕŠœ¼é0L.‘\RI}ô+Q­ôÓ$«ÑGC7*[·ÀcŽñý:ùôýÒðÚÍ'‹µ >D·l1 :}ÜcîLœ¤2i5k‡¿ ACyì ¢}»léÞ½»LGÆâ sÄùƒgo¶Oµ‘¹_hû‘}êÈ¡å~þþ£éÓFSBnm pœah8{âøŸè©¤œý’>е@GlåŒÍû%˜$ÿäè;"gJ IÌ—(\hßxqÜ®ùzV6¾™ŸEæoØZRI˶hëdòLK˜„ж ØðKâ^8¦Ó íTí„Lê<’S3àD~³©™ªÃÙsqЩ»w Y¿‰’ÏÀqƤO¶Î’„ï–çXsÌKMF/”9„%½éôá»C\¤–;H†ˆˆÿ—ßûà%£ÑpóªU¤è/{;J‡„ ûñ›Ïi{Àß‚-ðÊÖ“©¤ ¡„ëÑ‹.7fg'Ì]ºÙH³ n] p|9ÎÈçs-ê¯ ÐS%l¼IPö :¥…6¬¬=ʰ4ì•-èÃïjzkåÉì[ ÚrtxwubIjuË„Ü4´ßã}KŸísg®‹‹N-™?gÚìHYÃC“ÿ0•¦ªü|a±p¤Oz1À1Çnt•Ld°mk ƒNâòV,æ}ø5ï5 û•¤Œ7½èî¹êVD±O÷‰&så«?ß©÷ 0tÄ߯âì¯ÒjP¢ “É|C™iiÉ);£Ö~îR‚`ðŒâl þm÷b€ãËqŽÙýErrM‡I¤Ò„Ònà²XA§ÓbEñ¼¸èÔ 8… ÿ’Aý™°y4ŠËz <ŠŽ9Ç>¤lh|Xi7”ÐY¬ôAç´ôÌëŇ¯| áG¥“"ǹ¡¿€€^ØÔ6Õγ™uWƒèGU½5`¬t::Z†NdfXwÛæÛÝŸñºúÿˆÚ7¦q»¶¡aÞ.lw îbsb!£™†#}Wôº]GÄü(}†Ù¿­3é`åŽ'Ç•ã{"îÀO[×ÀÀ>wìI¤ÒD±üx'M}«/|_ :j±q›\7r·Ù]@A† Df×ö¹ ÍÞ[/Ã=rìÓSSk¡•Läʼn—»<<ÜN¿¯Þ!#YX†Ž£ÍægóVˆGîê.íAÑ·Û²lÓ^q÷í¼Ìüe pTJ:¡©{¶Î±ˆ¢Q'¼¢ h«Œ+xU,„SÚ 8ç1渻ÀúÀ×›„…ç•rË\uàØY1Nyç/'H;ÝŠåÊÈЈ ÛH ƒO\ð>-aáú]ˆl’#&Þc¹ž[–´ý¿½q=wé“Þɨ-þþQ»jE1aDOQ¶L°¸x5YF-±Ö.Û_ó­'HŒeºžž^­æø+¦­X70ÇÝkÎmJØ`R· ¥Vÿ>)ØMþ˜†Å_¹fB¢0?ݬØðq í;M§¨Ñ8»þÊ_çÑ|ŠZ ޽6”fÃã½5š Só>ºZóqN5ȸ.‹D§&Cœ+pä–<¯aœXÀRVC\e2–:܆àØ_ºz5]§°AzSÛÇÅF´Ùüçó X;ÉÿæÁæwmØ#:#^hiò Ž“!ÍüeµxyÜPQµTYÇ_ôò’Ї?dŠÀ•[æ3®ŠÍ]! Ñ öÃþbýÎXñåokE œúš×¯aÆžÁÆÆä£Ã{iϲ q%½ÐQqÊø¡ÒyðDüe)h°a‹6ì•íåV6Âʆ“¨çüÁ¹ƒŒ›· lÛÇv’Ñ$ÃÉàürî[½`þ²+â/´ïÙ÷~Ï(KŸ6šRççá–|˜Ÿ€aC͇ÎàÆì¬äÑë¾Ýµn ºœw(a£xiÖ`5zŸN§>BkEi¦«ètÖŒ©‹Òõ®»¸-2’rçÚOø•,Q‚ÿëpb€cï' Œ"¤Ì¨Hêûíyúànõÿ] F#úC›ÆµÄœ%›DXÙÒâLeöa§»~õJbHÖÈþYI¤g\Ógÿ ÍCÂŽCøÕ‚õ¢lé@1ª_GñŸŸWI¡å±‘½øH|þÓJ0%Å£Hd Íï,vÀ~Gˆ;jU¬Á¦˜#bqTŒHIË øÐ .‚Ñ;>›·R0øÇs–‰$(bû—ûµÇGÈÈȾŒL¥'Ic0Ú,Öl?(¶î;†D‹CÄù+‰b.úx"þŠ(ñ¡=ÛˆöˆðA`ßk {é˜3¦øßî»ã¦f!3¼Ä Ÿ¹ÒQ²Aœ/x`/ßR2 À-ÂÆR„7d‚©qú &}"pLœ¾$–DïÍ'lP‹0ã«?÷ibÌ®ò}kã­ŒùÙÒ12+l­*@#=Ä×lÈ7®3&’eÕŸãç.‹®­˜ŠFµ«ÊG‹aÚµíÀq9›öA4“΢eÃb Æ|qÔnÄmOÏ ÛØE†v$ÍOýüW1QP~[µZ‘$ñöÓ£ÅÚ±b5â¸3SmcÔÍÈ'uAÿ„{‹(Ð5]L»fÓ²EËÓÒRÐlÚSòH¡„ ÒiÂÛ@§SoµÇ[éÔ]ÂÑÆÉ5À$Ldt¸1>‡4oÑÅÿÅJW<';Æ„$„•[¿3NôhÛHŒÓ¸pýné³ð·{ûI3–ÕÚY'¥¤ Ž@æë½ÿ-’I‹Ò22ÄÁãñbêãÃrZü%Ã7º_{h(Öˆží›™:UŒ‰‹¾]%Íx(èP@XfïÎNͱû^ɑ΋‘}"dÕ R¥}+΂¤°°óà ˜ó˜dÂ"&b¬ðJåCeò¢CP¡ô0úvàØ91û÷u¢2žÕ ¯ ûNS ²Ð´P€aò"&R@¦v˾£ÌîìrAC}Ã}Ãn;è‡Ñ¤v¸YÐPíiZ7\ kêž×ïŠwõl ¦í›ŠØç¥YÐ0·ÌksäôE™…ô¹1åÎ55»‘±u#4(6™ÙbúSù… ºjìŒ=!µ,O?p§dfea·ü¹‰>ÔœaÀêS§ZÅÎîølRj:B ÞÌÛ1þ:µHtÊVá™—Â(a?²Æ‚ÿŒ'Õ6{ãG-“H‡ÆKëcm\U}­ÙvPj0˜Y–&Û3#sþ ÉìÏØ¡Ýd!xïS$°:zæ‚9‡'îîcþ…jM(xR(&ýз‰ ©H_Õ+•ÈbK°×OYÀÅ,„ 5wx«°ÁÞK¡çLŠ–Õ=úsð¾ôå8´w÷´‰f¿xÔ;¹ÿ鋊Ö8†Z&ãH¡‚»30¨Ñà™øŒ?–÷VP4ɶ*šS÷t:õÖQ³Ý.¯¦Sw D‡œ<±P("¶"ýÉ-<P‹©ZP½Š>jUÍeæ9dØìEÿ¡A 4=Y F6ÿý;7×>ÊwM&Ž@!à­Ùˆh˜©ø+ î<~Nš³¼óõ"YNþ±óë¡Ðrôô©e©&²!˜Å9‹7ŠÓðE ³Ü´N5m¡¦Æ¹¹f•òù´ jV¾IkA“™ïm” r Í»²2ÿ±Aò+?|òþ»ß¾}ú RVW&eI©\góCJZºì7cÀ«1 ÏðX’>4o²{ãw5)EÔ‚€ 4,ß·ö?Çah6ÒtkÞ²ÍbÖokÄëOŒ™y-Ë_KJEÛnÐqM˜j®$¦BØÈ-ÝÂ"* …˜OÂÌ(ÍÂhÆ£.„hÆ‘§y›Ã ³§¡ñ ØëgîW\÷·J…P±==í jäZÅ€#jþpÝG\_èôÝPÀÿ§Ô`Ü ÄAó0mßìüâQR‡â€CŽ+…j-¨Õ `A“)uPøà+­Fî¤^:zá ±I.§ÓI‘Ó;æäø›¾|kʶ¢´Í]†š0Õ¹(mÔß½50@&AуbÔÿÅÞC[ |üòlù¯gqíÈšßTÀ޲‚e°ý§ŸÄu섯Ür@Ú½«gÖÎáØ%îÑTîV׫^Ù\$Ì]yh6,£g™ X\4¹Ï²ûäÝ&u«‰†5«Ha…š 2iôG¨T>7éöyØèS !\€ÿA ˜)`FoK ÃùÈÐîbÁÚí:¢Å¤Q}-‹¸ú-}x„.(Dh.(háð§€×Õ0†–`oüèópð|¼å+ÿOš¡Vä¥ý ß›ËV…ÊaeÄö'Íu^¸J«s-ò´¹ M¢uõÐoå™ûËì»k¶€Æ Q;}&‘êÞ¦¡Ôœ•‚fãïôõkäÒª½~ªï¸êLmãØQ÷}‚ú8@ŠF4H š"v/K•ÀÎnyB·1ìû·8‘/?ë^¹u?BŸö©p&Ÿ»d³t:§ ½=¸«gk8p1‡OK¦Žei½ûX‡ÝîpÂeÞÚö7€QQ‡¸Ë}š 24Éi ÍÆÐ>dåd‹]Z‚f¤}ýf˜HµnTK~žZ B´ß¿»o{±ïè8§Jgb{íã3:χ• ï»X:+3¢‚Þsú¹uúpºg_ˆÐ¾[÷_‡…¾78ÖnŒRõÜCòU·çð)ѾÉ0½:~î’è¿K°7~éTM§þ˜ÉÑü-¢im«ãÊq&P;µ @K˜ÓÑçfsÌQy_™éñ|ma9¾B“' »«¶­V+6ï—Z:­@++Èûs5‘<¯î•„diZ'o`¹¢Fg p1 >F4½*Âø\°×OUÆÅg5Wä"ÆÅ•»±:żiš§¨qKAC Snl’^u!1 ÆÐ’‘ãxòÐ ,Ãòų¦O{ÑFètê’|¤ˆëéÔ`r‰œà’Jì‚kͨ^ED.Œj7N}û"í¼ýõBɤú‡ÔmŸ9ïßg:b»¡6×Ò‡åçÉX¿÷¿¿Ì·ïêÕV †Ù“30LéŸkwÉ]e2þáäK MûwEIÆž¾„-{Ê{/>2D ò¦•?ôÞ»Œ¥Ó~ž÷þD^‡ùÈ E¦“99øMÚß÷Žh"~ÆÎ3Íi>zîQ¹˜ÿ!ãúu³ÿM«þ‚“/µîd?0®ßü¹A¼öů°¿÷Cº·6 #êÛ¶ÎôIŒòóWljU•[e‹x_KZæ’ ˜[€Ú‚gœnï`î •ê©Åi Ͼ:úâ¨ÝÜf¯¥S7c£¨w¸@ï8xBªþU]Û’Ž”\$} œ‰÷ïk}+ÎöŽì!#;YkÃÌWÇ™oOB8P-tnY_ðPÐNÕ<¨i°Ô8½>q„*&ÏÏÃÛ°-–ÀˆT<´@ÿɈXÂì Ì2÷ è”׃xÏÒäŠamyh6÷Ó.e¶ŸL¬˾ó>#&ñP@Ÿ¶Òoðýwƒ3¶%ó襁0Ü+q“ fž¾8–ætd´­ HÔv)Èeâo ÔÖøç÷cüî…Ãw*œù)l¨q°6®¬Ÿcõ"˜{ A̵aÙ6jž¨ÁJGÜ^÷ŽŽM†lVB ëÒöƒÿR™ôª¥kE ÉKa…æSüîelð¼>ówÍŒš[ýÌ­Ùå#§Rž} TH,>”pÁ³º¾AH¾Ö»Û£½ŠþÔXòíõ­€ÕN}w4½’N}RØàbG»tæ à"JØá£%ìÐw:m&‘ó—ÅÒaöŠ4Wx``³Ý:£ 1Ú#ñÔ¯QIŒm0mîç¯Ø&âNÆÃT%X´ƒ™™,&Þúna4ì»ã…z[cš&)d:Èü¹n'¢i™×E0ìîÉE¼8v€’r'zWÜI¹°3j‘6{´j¨#ñþir³pýñÖSwËט°eÆì…2¾S˜hÐüŠ»ÞÖ¾õ5òBÐéù^8&X×_Hö擹uÙÓ|Aÿ#1`)h¸-´™·EID¨e’­ÕíE÷ø£æáW¥f¿1ü®»„ Õg2ÞM†œjøÔù+7{ÌýÍAlçkÏì+¿k)h¨vó™4Ô=žµ‚†ö¾µk[t ?¥D©âüÇy…smUøiÁZ_´Ï‹zM3±}4ܶzÙ9ÔEFˆ ©/ÛOaC ^Œlïçï×úÝÚ@o8òÊä:YùroѶ#Œ?"MÎ £é|ù–þwÆÔí·hWÙ-3FFFœÍðk \6>"0å07LÕ›úÎ̧9ƹ³Þy}6ž¡X.<6åǯûÕÿæ³^^¢ÂMøQó$ÏêÚŒúâ¸(h}-Ž6øMš•ì>t BÁyF”ñå¹;×fZaƒŽ­døŸyp€XäUs—n¯N†p àƒfwÊh@'a‚Åû¯å»Å%ìà1êÊ$B+»ƒÉ“4!èÑXš3Ìúu-¾U”ñì©Qa4——"Í$˜CáÈIP‘nh;ÍL£ãîê)SÓÄ÷EËXõ4ÕЂ#ñþvòJ"æº<àn§6ÿÃú‡m~+væZH‡÷UD¨Q` Oê¹~Ö1P P;¾~¤uoƒsØÈøæn4…êÖº·5¯ÈíѧX¹y’®—Â}^€Ärî*ò‡ ¨`Áš¢YÛˆÇ!l¬AQµëê…Q@Gn<6<ñDdPN¿ÉpŸE°p0n¦Ðà@#:0©é^/Ä’¥’ÒLÉi~F“é­‰ÓfÄÃ’âã%k|ù(ýp¼&M}«/5sú´ÕN6Î0èé§KV­ôÈy£ÿ«ÂßX‹ï— Ì©W­’ü–U:íØ¢^¯/¦ž©ýƒ2ô·5ÐË ™8Õ•ø™ùÚéA¼¸=€ì“«§}± |m’¡V¥OgMœHߢbŸ6ˆ©V kÉDf´QßqàØM1ïiª@!„ö×e°«ßYx?ùq™ô÷`¶`20Gàl;9 ¨ pב;–Wg‰Ñ´Ð‰Õh2As†¾d¬}ÚH‚Àè6tÂmZ·º|…‚aß‘Óhg ع—‘ó#ìŒ=)ye¼?ÅûçÎbAàè·,뱇'úè c 0 vÅÔ¹š`ÿ“Œæ¥èe¿´ï=e¾¥ -ÎÖÓWÌ8ý(h(!C‹³iÎ~[ Ïãþ1µ¿_`‰/MFc5$^4Ñ\¾3øp±:x7$ƒM«n"nÛ¬*2À¿>çܳ^ŽœðÕ»‘ËÑ|Ò©×€Ñd˜š×˜Õ6Šs®ßÓÿ¯Vp¹òц‘ÛÉÔ§CSQÑè0/8M§Ý‘Ÿˆ‡£ —·©‚ð6•NÕ1oVÇäygëÆ5M+·ì7ø¹üÄøWÞ˜øõÛ¯¯ÃsÇéÔdˆ³ß"Çžú¤°Á•† µ~\²QÚ±o‡faü°âÒµ;ÿqHnFမÐ)÷ÌÅéÜù L°˜1wýÎCÈ ÜZô‰Óàn­i([Ʋg¢1ƒ»I ³ÏA”!&ÇblþkIiÒ±“õ6E˜9hÇ:hN¨¡(W&D /9ˆ[ÏÈA̼Œ\¸r#„¦ºg/Þ?£©Äbª<ÏZßg¾ÅwS_=<醒~Ö1 cÀ 0À©ß× ɼ¡Ýþã_~íÿï†W*kb®hâ½V˜ö‚±öÚ&èÔ¢Ãa¬ñó–m©röbÂ_½òÆK³ß~ý4œ ¬wЩÁdUa¹R ƳÏŒrÇþã+vh^—&¬:Ú@˜/ÜÕ¯ƒ¡)Æ€NëƒG^ : :¥öÉ!:5cêDWôÓ'… vœ»Šô—X##¥0ªŠVØ,•ÛµgC^eJÖ"ŒŽí›Õ‘~Œ-_¿FYîá!]‘º©Ì-0!1逻 á$OÃ>{úS£¤Íö‡ß-1WÕ®I-±6屈×_¿FU8t¶’¶Íþ0À¥ÀBÖœƒÍàÂ^¼ÿº0×"0ù…':²Ò¾üÌÅ«ò~î3?»ß¢&‡ö× (p)(Oªœ~Ö1àA ¨…Z=øiýS^Š2<ÚÃhƒíåNpÀ¸§JA£}Óº¦±wu÷³–ÛÆKñ®7ËhºýÒ¸¡þß-Œ2m?xüà S" _½Iƒæ¾@£ªw4H«%.Ÿy:,,ìí¶Í8#¨¨zô³b yýâõ‰á~_ý¾Þ´+îÔgã_ŽÌúúÝȯÑTÑ©Ïî–H‹»$*ñêëH_;ÆŒ-ψ/‹£÷HÓ©ëY9È1pV9sáš`„:eª0¦t’Ü{øŒˆ¿” v5FNň33o(̤˜à!j©áP@sªT¨U©iT !J±ë¡ mãZbËþ£ò[Ô>Póq y,ñþ¿ŸñþétzîÒ5•e•ú±a­piú…¬Ó4åZ·#¿fËÞ· ]GN_”}£ŸËþcgÌM°‡'s!ý¢H 6‹NøÒ×"§¹QŒ„ô;JG`[Àñ²÷ÜÖ{·À}.Ô¾´X» å™ð×rhiÓ]ß(j½Øc!Ó®v]yí  ÷ÿí¹A%J”z›‚Æcw÷2肆/ Ÿãmäxr\9¾~~þï}~ C’q÷9Z=¼?j$ ¬O>Ë:>r·WIn~“N[5ªÁ ë/~f Cdrœ=B§>«Ù ™´kR&JÇ¥†‚ÿkQc&ÞÓŠ‚¹94Ij>énɬÏE®2øe‚ƒàCÑBæX…äl[©);Û(è_1§½Ú7ÿû3J<óÁˆ=_QôCô™‹y&[¬“­èB’ ¬ C>¨¯¤†ä“9Ëå3Fˆ¡‰¥“¥µxÿAˆjõ˜Af­ ³SÃ8ý¿ bµ/4ÙR`ï[¡ =vN¼ùå`AÈ^ÝX,Þ+_µ‡'FºÑá(hþkî 1cò(T=yù³ŸE×V å˜ó^,Yعš#-\¿ A:r@}¥Fj!‹Isˆ<#>›·Bו ¥MWÖëÚºd.2ojaäÌäÍ‚(Û'w‰›7ïP¶lù°ÿT«\^j4\‹½6oÂ4V†ø+ ÆøK†Y½{h¶ví‚´Ïc;Ç…Ä…¤Õˆˆ~Áïë_½ºµ?ÒBbÒ^£•Ëw÷6lØu8óü©3Æï±Ô¡Ù4{qû|êsÂÆ ˜5)`hZŸ÷,g¨âdxWµ3LžÀE›ï«{¼O?ÌÈ«B\ò>£_ÑœÊò>ÃÍ~ðíñðЮ`ëIbÝÎX)ÜÕ³­Ô¬<9úÉ4ò[!A7bë³^-hãýS{1ɺÎ]N0 ,˶ÝÛ¿£dRý‘íøî;r…!>£ÇÖ·˜øíù‡OÿF¬~mÞ[xb½:8‡ 3Y-^7TT-UV¾üèðVÊÃ>ÿ€Ô°±àþ£çÄ‚µ;̆åsçZâó¥iOjœó¯ÞûþÝÓy²7ì%}²¢w–ì¨5´Ö]íb‰Ÿ‘رoû¦RH=}áŠ4¥tõ÷lѦ«¿SØúª Ôîö´ô3xß—4l+×ÖR­ïèó|Ü«ÒGC×h– |ã=Ž/6ü?ú~ixíˆæ“ÅÚ¢ådâ(px#˜…â½:N¾˜˜9ÎïôIÑáÖÅ5}Ú7 Lk^{ z9‡òݰ*pLŠœÞ1'Çßôå[S¶+>'l¦³ZBû¾­ûZAC[Þò>#¥0‚UJZ¦¸†LÕ4oŠAž•Ã`ÖuãM2î¨ÒÏãjBŠÌD w)°æ,®žñlï[¶bõ«÷máC=×Ïö1@¢Ïæ­”BéÇs–‰ì"P òÓ²-Ò¯h ü´@M³7F¢7æ+˜³dh)C¼úï_Dsø$=0°³ù9…c{¹Th¸¡JéËSIü˜tMEYÓ~ÓG®Õ¤Ç3'Bµ•ÌÒÍß15§Œ GíÆThH'"äo«¶‹‹W“ÄÛOç"{.ÆíÂg—Gð&TlëwÆ D‘áb{íöƒb-‚H<2¤»˜½`xhpÑ Á%Ë6íEd›ãË}—Ù4 ÜÜ`´"j5yhaS̱&¤¤D5’9‡¾{æŸ{‰m›Èìß A†*Æè Ÿ²Ê² ÒÉv|oÊø»n¢MÖ˰à‰)é¢V• ²¿L¢jë{Ú6¹ãú!ä?;ê>ÚÀsFÕîøœ+ꤠ!µ8…”)3©Eƒ&Ýܨõþ:¸qÇñ†yõd´öSŠ‘óø<–‡­­6°f4ð<00$p<|NM44œ‹7õÛ·B‚ƒ‡ÿ2çËz£|ü0:Dú´*³ÄÛ†ÜGýŠÒqNŒ:Ü휀EüBìþkîr0!;$£÷äè¾…¬ñÆkÔZ|ú‡$Óyã®~åÍ Š’Ñ;#ûDˆqúËë$dONËŸëD>ÀæJÉ„R¡l4h•E9„å{ 5HPÏy­Í¥BÍ™^úõPèƒ2u æd|fXè eó3§|ßÇ€‚†:Š¥é'á?E,™«åX|þÓ*™»‡¿OúaýÂ%óÖ0°CC2¨ͱº"÷†væ¯Ü†Äx©ˆ|·StAøíºH ÊÌð.lÞ{TšÝ!T¡ºsÍJÒLò¿¿­Æ‹û7 ‰öe¯{Û†bæšY­G,{Âu‚8xüœŒÍ/M@K`c„&›cð=žàç´´ÉÈy¬·VÕŠÈ ÔC´@Øn ö¾§êt󙈹7¬ˆÕ³ÜÄ ì7ò¾Ž8W‚Àè+m/b×õ׉Ž76’ªŒxd"ÇŸ´Plã?kú´y°] „â’ƒz´›Ñh¨‡9«ØÚiÑ6ý_`€ær];·»Ÿ"’¬¿Á xta£ˆäî$w #'Ž”‰ýÈ„8ªÅ(èÓ%¬†½-è=ýyñ`€Z%jôÙà.—£@ÿ›ò4¨Šç{L¢f Ú\* F ò¶P»F`rÊRzЯS³›vÂ-ëòòÿÙ!íáÑæÒáƒo‹/~^%Í[6ÌÕ@°u«W‚Ík©½`Ðj3ïoµc‡v“&¡ ’š,jFþýÓJ9wvj!ûÑ¥U#™””>9Ô0h@Ç–õòõ‘‚À$ -º ¶ìSø}QØ!ì=r†ñîeøï°Ð`]о(`0 FÀ—BEGhev"8ßc $ÓRÅÍg&–¢_Ùøá=¥™iÿιí-è{æ Üw¡Ïb>èiVi5Ã*W¹ƒ ûGÃW]SÄþ]ÓÓÂÕÂàü½¹8Þ÷²¡ýñf&M6¼Øò~ÜEö-´ÖFcAÁÿíeÐ ËÀ'öÞuõ3úµ1Q³3  ¼²ãà 9O;ó¾¶,ç[_…Ãñ ÇŒy´%Ú¯·u¥ÈÒJA-Ë1S‘y“‹„7ýØ j¶þÜEȸže‚sôtåõú"}hó”°;&( ÝMÜåR™xO™w†Aè?D3OûX ­CÿÚ¡¹ yèÑî;žÈn×´ŽÛç*Õ`š·1Y(ý£‚À{„Æ1BM$õTPùy˜”@_/ÖÀP”PÈý¿­Ú&@qŒ…ÌFVSš_=ƒÐÝXæ#(Ä—¿­Å†ÆP©¹ ™Ó;_/RŸ½‰)€(èmÛŸkwŠãÐÀ²-̟öYÂÕ¤˜Nå7ejJ úže]®úxg(.®æÊE ®ú„+ë!GÆ’L\ _@@­2A¥\n𢼳;šûiÁÓÿª-u«WdXsSðÚÖÍâã4ß³uqñj²4e²\wà ÎQ¹|Á ,îú<”AFøôÔÔZøiáIÃÞ•’N9Ÿ²mÁþu‚K•4b£÷í‚­ 4ßUO 2õ¶ûB<\±eÌFß4§)ÚÔVIs؇‡tËx…Zfqn9 ¤¥o{Ÿ†)k:"˜–Cº·ô#. X•)L…ygùæ½M*W«ñÞ€ƒs­šg SÝwܾ€³r.&$¦ê†Ýa¸u&$¥š²®_gR\nÍ¢«¾DÜe&;{ÙQì v¶™ô‘ެîšhq·•·…;دŒÆô˜ø“`ù¨`s45w¶«¨uÛ  RبP¹êÐyË· lõS¿?¬W»›˜8õ2µ *i!œ‡¶ 4„ `œTfñc`îãNÆË…ˆ~m‘“‡¦R¥ƒK †Ü‹…êô…«¿m‘ã"˜˜’c‹ 6ˆ@P´ÂÐtJ… úkÄà{\Ô(ìXƒÒ`qð|üMùÞM/¹èƶ}ÇÒ·j[1.f×9Té¶…ÐEÍ%Í*&.Ð?  J¹²!¼ç1(‡já¨U¥_M…Ev‰Jð“r·°ál»ŠR¾\h°ß¥«WÃQzÒDVQêsñ»¤I%Nk„•+] :´‚½ ð£j…Ü`-ôksEà•]±§ÄfD.} f¬e‘á04+…¥_kAe<…ÃàÀ@?D¯Œï‘NI£œcÝ"ßX9ñƒÜÊÊÌ<—œžiУ¸»>PÇœc==Mé8%Mä5Ýçè£ &,:¯€ƒm`Éi.·|Ó>Mªò øœ %rCáôM³GâzD3c^æ€ ¶xWåR¡C0s§0wͮʖÁË)ѹaè¯#JåE^sôÛÅQ®úàªeHINÜW*8xµaeKG3m~“Z .4¤»&{|+èŸÑ¾\@è »eõÄ=wDˆHX¾y¿ yÌ »Â”j.ül®A;BK` >£C“…˜C'¥€Ë…²u£š"z÷!·{›Fxž…PÜI7íìië섽upP§ã9C-[ƒ&u«I†qÛ#šÔ _@£Ú…úžµú½— _§oF•iÑ©;„]x_«áp¶:w—'½*ÍMg±AX¢„G¹/m0  à˜·=’F)4ö†C¨¾^°^ ¾÷"Ra+˜§¿6ì‘m8xaÌ@³’åÕ*ýz#jÚïËÞ¶qí|‘UÎU?,ÞáúŠœ#ØEÒöb˜5mƒæŽZØM{ŽˆáôÿX·Sš$"sµÌQõÝ_ÑbP÷V‚´ÎÍþ°L.Gxú_Wn—&‚lC$é¥37e,% ¯…}GÎ"«òfAMOÐ>/Ê5ÇÝO¸¡Ì¨H¤®{ƒISßêËÍœ>muÞGš…â´ÔäóÍêV+N ZÁuoÓ^® qÝë…@7ŠÖ¨e`t=n®TÁIŸ6Ò Qj@£zAq6À÷ìo÷ÝÍž0ñ3’-ï‚ù'ƒæôE3eÖI¨9K6ÊÀ<ÝZ7ÀµYóœ×Ç|§ªÊ¡þZ3j%T`^±G‚aœ8wI†FWA>” A“®(ôaêcäPú ´^QúaRCÍ ¨Ð`©aŸ¬•± †øâZxêüeqðX¼4§¦Ý¢ »Å>„ç§–|xï¶VM±-ûZ2é¢M&þ É¨dâp›vƒ?w€úA™.œ9…Á5ìÅ[‡Û sŽý™cGC»+Ék‚OÑÍ`h®T"ÀO|1µ`&yÚìÓÔ;Y²Ct¨¥öåÿ}2OF’øC&²Z¥01å³ùâë?6ÜôËÚ0›`Þ–¿½óøðû%âjRšÌáÁ þ¥ý$^üt‹ƒEï<†â¦J¼è†úPó‡¸pæ ™L$Ò¼èE-Ïm µOßßO:é¿öůy½ªôÖ2zÜ¢õ»¥óõ½ˆ4Fœ½ÛI3F¯"0ÚX6Ì«Áœƒ*xKÈB®F…z}æo⣖H§ôÇFö”ÅZ5¬‰Å¤ø&!ÌýóÒ¿~– I-ëÐþÏħdøjV©`ÞåÓ>ç5œ’`.ŸWí8·£‰`VÿžeÝ…ù÷¡Srþ8y4n#Þ× fú(L½n|‡ë)™8%0÷¹k}µÙ­Ã?ƒ07Ðæ½ÇÀhÓ$°‚ŒPÆ(g„Dä™b$E tp51×âÕV€KAC½;yƒh^J†Ç|¿(ZFxæÁ¢:è‚6´Å9“B c4«_ LšAjàø|ü“² ¸S H £™’ž!w‘ÿ€iàV˜ÀacÞ¢M´@S‚e FuÓ“çÎBð&ãuµ ¡¾ž– ¼¢ÓÛa4¦òPmÊ;³]ÔlH:ýmö~¤VØ ZÁwÉÄs„>l4ïûiùóú µm“:âc‰Ö`ðÉts³‰@š¥ù“ éÑZÎO¶‚¡°üÈV SpýUs*Ÿ9 ÚÀ+Úw †Ñ k5…¯¯„°‚–0_å}šÈ8çÆÆ#g ü—Pöéûï»¶Àï áÌ­•±×â‹s4£>8¨³ ŸÖ³~—”cuEdÄK7%}Ví³v† ™Òr‘n¦S“!Nð("°rwJãÚ¿~Û× U›+°á ƒú¾@éÙ]Ñëõ<0æ ccÂöõ«bI šƒñIúàm“¹ó9JjØ-܇ÄktÐåF5“.U=í§ÍÃ…üe8§¥gÊ!îiŸÛË¥òúIJM~ØOãî·/@ôA†Ò¸mõšCÍ"Ú_^¿óPFyqg¿jÓâ[û-˱Pϸ#7íñárQåî,MJÃáœÍC™xµ“Ç{LòÈÝXj¬}CZ4 ¦)G2_\”´ÀÐÆ<¸ûÏèVÊNÚ’¾Ô;dè>Ÿ2Výk>ki“mgÒ@ H© AÒ›ê­ï™+rÜÞMÆœìøë×FõJØà¼áÀ‘çÁ’L\³Yìmå.'Ã`SKÆ][2ìÜØ°¤'8à†Êù+ ùX{¯æ¹á½#d~¨î°Ÿ7 “–hRÈ„¶e`BئVL‚K&¿~+ ÑøT@¦u«ƒA» ?C¦;ä+¡$3Ëû ¡Õåïa¢ª1ú_¿NÍeY2 Π’ñÊ@ C"Ìó-ß'¤ =ó´¡¢¹ÝÑ1ww]>pù™ŒR1pŠ><+(¬ ¼ª-¤U2š< Î Zñ?hl¹Oš"TÐhœi‚Êûõ«WÎGkÜŒ#®©…mÕ ¦ cŸ7û801òô§›ý µÁPªT(c†Â F`|ö¡Ò®iÝpšÜ^ãßùz¡´6`úáõhÛÈfqm0 RÁ7"¹«èóÆMFââµ™¿‹; ´rͧe…WøÊ|Y ÌZ:Hâ„‚?5ôWR ãƼ¯ *à/[ýgÂhçæÑwæj$wÆž@ijRøâ†è!hNá7î(äÍS65p³fLèh]öʹKØàІìËñçæî3&SÂSнFéÏ|kìJΞ8þ'zí4­zŽtáÓô¡µÝ·6Z4ƒ*,ЮÔP˜°ñ¬ 6Ù«ÓÓÏ¥ëד³.œ:ù%¸—)œ¬•ÚÚÓí-è{–9xl•g*ªÑ™00ÂIïûÌ…U{Àü,®.X¶êµußÕm S:ÆÛp)þÜÔ­ µ®¸ús®ªÌ›:윫¾è@=ÔhJ‡äÎ+™ÐxXËÀ¶X{—÷hæD³-îhóZA܉óRP`Xet¢>s1A+P÷Ô™»Àü}\…9)#§= mMWOÆ_FÔ¡‹Rø ™"…e¾ÂwÉ´Ñ„…Ïh%¨{ ¡u䆎»y7JøT4¡šà-g%t8Ü[A+T¶hy –DïO[’\Y>;‡,A.0ì» hb/ …VYHGôI³Ô´T,—k‚«¥kï8 ƒBÕ›“F"ßÑäJÚ‡ßW Ô²Q+ø ÂÒÁ\¿Qé“ÇͪÁÝZ#Lz¶Ô¨Ñ¬kÌàn2Z e;ìõ_•U ÿg²èðŠåÍH¸±ÄpúŽƒY(æœEšp ž#*¸9RÐ@±ìu ~›wϤ¿=0wéæò/?z—_A hÁUë%¼ÜQÂXM99‰Q-X€¶*aC l¾NÞ<ˆnl›“ôq}寿üöÐߟ›°bóþ 6¼†‰+ Š˜ø‘¶Ë4g¢Iž>ÞŒEÒìûÆì¬ «~ùùg” ÷¨Ž›_ðŽ;j‘Ö0˜r/öÖÙÚø v@+xùׂ­Ú2Úk¥£6„š ¥rÙŒg±LíaAФNU©ù‹BbLj>(à6®]U&Ìå®6w³© ánù…<³DÖɰÎú•–÷´ø?&[¥ ÄÓ‡„¡¬éwåF  ¡¥ 7~ÊéªÕ†ŸÓ/Z­PX£5*”8çqÜþñÈ`©}ñÓŸÔ+ò¬I—CkÁP(|.ÿ£˜jnÞ$!2Ÿ= ƒ¥Ï†­òÎà€DM…à£8Dg!£ZQàÞŠˆl4׃Ĥn"Ò?¯/rhѬ9™¬ù°×Õn-¾xOE:TÏ=3°Íö´ô3(OU‡£¯;UŽ?w€"b.Y×®]LŠÝ½ý†y„Ó$Ÿép c€c̱ŽÙýEr2f!2pPàP  :} ·8AN¯§¥%¦ìÙ¶ñ•¤Ëççc‡ˆtã³@ÇIæäa2Píè³rCÃ7í=l„³¨aGÔº§¦&’“P›{o^?´‹µb0Ý€¡Ü*™Ìñ8"ãñ൳À d’˜#¦U´™W  pð÷úaG:Q8PÏm© !#JÍ„‚z0©¡&v1v¶i:EarÌ>00néKB3îV3±$“]ÂqYk\§šüŸ¦¦jwºE}ä—‰;%ÛN³^3@FA@a†X×ÿþŒ*¨xáŸçúlð}-]¾>×½©ø3þžŒúÜÙœ) –á˜P¨;}þj¾ öÞ¥#)5Múr³áVí—ÏT0~“;ý¤o šôo£ð¹A,¨ùZ ‡lš4» h^H?:z30k9<Öl?(C‚³ ¤e†þeÀvMêÈ` ¤m%`S»Ãßýô#˜‹µAe¨q£¹Ÿ­þ»ªŸªž‡ ýôÅÇ«ÿÝyÎÝrpÏHÐ’YÀ9#jÉ ¡åþÁõñ›0½«»ÁR:sO3ôZ=…."d$·Ý.hðCZ³$2a¯=1\}ß¡3Í;bÌÍS:8æIÅÒè\FPà€Ìü‚ÈOdÆTQ[¹ÎÆ1ˆŽ§€Ú†‰÷ôÆnîFñêç¿ÊÛd°šOº[ºvÐ'špñì‡?Ê(T]aˆMS”Ù$wÅÆn1möÜ(Z_ÿ±^æLÈé}=F÷+ØÑ™ï3ÂÑ$¯|盿$ãªü>TÝ.<{„œh/×Ý|GÃÖíFшækö@­ 6‰ 5ÇÄÇrš÷ö‡¯ÇsqT 謑Uó!í· …»ÿ †Bæ››4ôù©T>TÇøiÙVD1;&™zWšÃiƒaÌûBó' °ÌKt{¨ü­0ì=͸º´jºÖæ.t S0†ß‚½GN‹-ˆø–àôÍP¾EÚ 2ŒŽöÑsÈÈi¶ú¯êsáYÍ«Š.òU=)rzǜӗoMÙ–ï“ÿù‡ðÄÔ·Öò›³¦OëͳX7N‚p0 ì*¼ÌÃ5ë7~¸zårpFììçJBAý:hƒOÓ)j4ŽÇîÿeå¯ó¸ª\Áqól$áà.%5$j>€„Û I4™*‰ƒ6Ü6’sÈGï½~Çä'Ç=àz‡|D‡âÁ@VVVæ»~þ}ä[mA hÓ£?’ñ¿Òrpþ`gýQEÜr¶ò]ÒªZëh+TuÌ3/~ÚºYæÏ?<Ð-mpU¥ÜÁ-…PÞž€ôLÊ@”EhnšÚ¤#:QAþjÖÚH- RoòYû'Âî=óÝÇïŒA›IÇjý£6_‚R\vÆ7ÞgeàÏ^ÔTªæT†Á«ôð³/ÏnÕ´~ Gé”›FŒFsg ;;G2ïÎ0¡™i†~ 4ÓS`4š ÑÈq+ÝZßPßÖžÙ6KZæsúþkî 1¹,}J¨Á³æãG­Ù"í&¼­þkÛPÔkÿàš=QÇ×pÐ…|@$<ñêô•¼€£x¿Ü;…ûëΆ ‚4“Á™;ÛŒ©Wjé¼~êл_¼±S·Iˆ?\\&F™i‰è+TŸêà;`è:†/eT!:ƒÃÆ:ygôºowE­#³@æ€Ç]kF¥>€˜[\@œð8“~8Wqó¢ä /½±aÅš¨Ë »ô{£NµJˆèd`¼tk“>Êëॠɣ aA5–4eÄ<÷Âë¿oˆÞrÍå¼AæLÍ\ü¸I¡vàpéõ a¼»­ž4ˆ[¿Q2’…4X§ÖGƒÿëpB.-ç|‹´Ìð/|Em’³`+ }ÜM·Žð¤gšU8wYü¾z‡ «l)h°ÿÖ Þ·æïb«ÿ,ïBà˹UŠGËý„Áä9Á%•Øé4­eø½€mkWFض%¶ë ¡Csr²ûƒQ …ºÊT&¨”‰W=ÉNûõGV0‘•ebVx&ìÃŽ’c˜|2.vͦe‹–§¥¥pG’ÉxaPÂé ?çþ¯Ós+ éC \¹«­ŽK—®:d ©ò[jjý¾ÈŠ]éÛ…22‰±B¹CXhiÃx$êRÑMˆ[ÚÞR-Í]\K Š^/ï9ü RŠ)!%Í„1‘óGvVæåoÞŸþ_Œ ǘóç Î6h†IaƒGzÐAÇ€Ž¢a€ó)Õ<œ—Šöí[úí㈖E“4ú\ íÙÖëûJ_©Îwn¼yÅbj6¦Î.m¿»… .J»Á…D¸HMM6®øeî|Ü[ܤuD£Z · )]¡dP`ÙÝ<Â¥£ìâÊàð•™™žž˜ž’rõÔá¸q1;áJ{EŒ…^sܵÎáø× :}˜Qqë\¸>ˆN|œChAZ¢vƒó–ßò_çpþXÒ¤m‡Æ5ê5hR!>$¸<ä_Ó”¿ÿ}Nrb"ß‘»x«V í;bô(ÿ\õ)67ðLBvVVz^y2µzybÅõø)ü&þa_Ÿy=3#)-%ùâ©#‡÷Ž‘ÞÄ*(hðà¼Aƒ*}Ž9Ç‘s…yÌp­ƒŽÎc@ÎoxM °@ã=\€&Oåá+°`ÍNѸU›§ l¬G›9¿ºmŽu·°Aœs‘à®”Zȵ÷¸ˆdÆîÙ±G,®ÉHPÒ6 %¸Ö@‚—€"DŽ©VPàØ’) s žÙ•Ô鈺EÀôÁ:Õ†ZíÜ …Ø]Ûvã8€gœ;ÔbªÎ²üåóçRžùé¿óž³NŠŽÕÿæwôònÃÐn^”ɹC+l¨kŽ·®Õ ÆtÐ1à: ¨9”g]³á:¼ú\Mô}U‘µu“k¢KÁÂM¦@Zä•Â…†®éì©v-•À¡  kÞsVãÇ1åRkA¦@1jw’ãJFAi5l¯N@Ò-®¦¢FшÒTðžV¡c.9Ÿ)gµ¨r>ÑN¨¬OÕÉz(t(P °^>Ǯ EœÔ&çup.QB†š;8>|O:œÀÀ¤©oõeñ™Ó§­Î{MñTjžôKN¼v´NµŠÍœ¨V/z a O³¥ægE.ï¡'„ 6Z-ìÚk%lp‘!³@ó Zí†ê¸:ã±Å„µØ+fA1{Z¦A1 <ó>ǘåÔ»¸´ :}XE‹OÝTcìú "PàPß Q¨Us'Ló"šwM¡óÏê¹zŸ´IUÌ,ße½|.NÜ…{5op®à*ƒã«æŽ‹\ê c@Ç€30š SóÊ+aCûºä«þøß¬y¿ýgÚ]Úúõí„ @»>’.8Oç‚ɧ.‹rö”°Á6²ñŠñäÂk.,\d(pX Š1À#¼ŠYPcHÆ€ÇRË(p|atúÂnp'}ž(¨oî8XnP(C œHÕdŠKù¾¢7E§¼¯ÞÓËߘ¯]‰â— Æ‘ãG톚7ø¿þœ™;ðšžÆðGä&yùÁ1¶M£Z2éÞ…+I2-ó Xƒ‚ž[{G¿W nÊGÄyÀ³ö7}íÃð6:VA}e¢ËZUÃlFP+è}_yÎ ?h+ùm«<7BÞNtE_<)l°½jçY»àaÐî&ªN«Å‰ïêàPÇ™2ê¬îóì,èôá,Ƽ¯¼§èCÑU%H(aXQ×ûËÊmm~Z¼ä{„‹W“e¶f•Á{?~µ`øäÿ”ág®Û%*"ÁŸ/\¿ ™Ÿs̯¯|ãDðŠæ°Ÿ–Ïe!ý§0 £ÕïÎSß.òw˜SåÇśĞçEJZºÌ*>¤{Ñ®ií"×­*`~‹Ïæ­ÜÕ]thVWݾ•Ïj~Vg—÷ÕÓ†ê€ZTØ1»ZØÕBæ¶«èç"a@;Y)&L©+U—NE¦b{ÙôÁΑN(èªù‚gÚ{–ת}Ú³½÷´åÔµ^þÞN´g[øá}UŽã§˜uO½§Ÿ½ ‹6ì•ù[ íšÔ-^xP&Ó#¨…GzZŽ2n2éÞÌ_V‹—Ç UKåj:´Ïµïé×nÇ€ú©ß<|vÅž›‘‰û©Ñ}EÙÒÁâðé fštU'˜ßâŸÏ?`5÷…«¾á õT©*¶¥§ŸA[´k§[šV\†ê ?çÉiï´A®†PæÔÆ UÍ eTýì}0 "MÎ £é”ŸŸßÒÿΘºÝM­ÌO&cm£A€>„NnB¸+ªÅ %û™’³y·`’5-ü´l‹`¤›{ûwÄñJÁ¬ÉÏY&uzÆäQB=3¤«|mSÌä'ˆÁNu†hV¯šxhP™ä/!9UÌ_±MÄŒ¡!Ár÷zh6ÚOé×Îc€sŸ:²;õ¹³Y4OóÌᜯγo07D%hКի.?¬„_þ“• šÜ*vÅL"Ø·C3³¶nÎ’M¢F•0q%!ElØuHtjQO=sQL—9;ùÇ?,õjTÃzµoüw}gGÁ„±4×úeåV±ûÐ)Fo’ÚŽQý:ˆó—Å‹7ŠÓ®ˆÊÄØEÔ«^ɳ)Â×ÜUŒußǨBÍÉE¨Íþ«Å&lDF~x6ëìdLRÏg³Ã ~Shp ±\h0“úQთbIÛŒ Ii¦ä´ ?,8oMœ6#jÇk”¬ñYdä£4‰+2X§>ŠŒY÷Wà úx*òóÒYÙ Jø‹IX`šÔ¬Rñª1ít:‘*[÷³:¯ulQ¯çÌ×N¡Å@”×Þ2_ëå…p~°V_ÇÒv sdzÿ~îrB¹'¦ÍxCÔ¨øí¬‰iVu+ï[Z5¬)Öl?(Z6¨)º´ª/Â+–‘ýýJb †’]Í/l$¥fÀgÃ(ü!\th^W>u^Œì!*” ‘8QÏùObršøva”Ù7BÔ©VIÌS¸¿Ë][Šå›ö‰K Éâéûï®$B‹B÷,Š€E—ñÕïëĈ>í„6K÷ú‡aúwVŒ»«§HLMßÿ-ìU**Hs4ß+[:H éÑ´VQÒôÑ3DƒšUÄyÐý4hNH m#©¼þcí±uÿ1Ñ¿KK:¥¥6˜(68¨¤xæÁb=²7Ì]ºI¼:a˜|LJþXj—È.LŠœÞ1'Çßôå[S¶¥OVå¢TèÈ»¿òÆ€ó9羂0UÒ©©Cóz˜Äª‚KѶZïÇ€Ó22å{ÛþcU‘þ}Œé³^ŽœðÕ»‘ËÑNd…> …6ozɽôM†Ñ˜ð³0ëÔ¯bêÒºwšÂ8CvoÛHŽ"C/oSîÀ6Ħ¸:v«Ò;†ÛMGN_øÒïÌå×9òQhŸVáy¾Ï~+½ó)’[f@ðf?òsâÞÙ\»­âŽp€¿¿ƒ°Ð°VU»eµiŽRïè³aÍa|ï‘3",4DÐ,‹Ð¢A ±+ö¤6¨1¹ ¦ïJBªèØ¢¾|î 2®g™à³Â0ðÅ [­4€4iþ}sr2Ò3}‡N)<ó`ñ?¨¯Íü]ÜíUjöÁǨUâJ…2ò ½í- €@ÈÈÌÓŸ,5tüŸZÒëÜyð„(W&D4ªŽ'fô°˜Ø³­ˆ¦uÅàn­äÿü“šž)Ž!cø¤Q}E™àR¢s«â“—V“ó @æ¼òÂÄÍ Z#_KYâmC®¥]¿|œüÇÓ†f ÏÂùýƒðJeÅ}ý;qâòù‰ØIœß2Å!JU$Ô‘î\Í[¶¥ÊÙ‹ =öÊ/Í~ûõOÐQ ùµö{¯Ó‡}üøÔS7ЇxbÚôÉSÎÇåBCüÆï)êר¬Ï>EùËìÿ;ÈÀ]r8$‡Ç_JX‚ ‹‰Ø°ø%?òW^|ÿÉ9ïzF浄ÄT^ûæÀìI F£ wÓ9ö‰)éâ¯Ýø|Þgwk-ÒÁ Îúm L`Ê‹1ƒ»Éé½ó*!)Õ”uýúU´.?R=ÜÜYÓ§½hñIbV|d¸ž™yùªÑ)Ý´n5ñ椑0½Û#–AûU:$PÜѱ™8>„¦QÛœ`1 Œ|¦ AÍÊfAƒ÷:µ¬/Öl;(Í¥(”t„2 ´rê øeçÀ¼/SF¦ºqWˆ¸çeÄ5jåpÍ;s1Ág„ŒŒL"Gø‘š.ü¯é=þ3˜\"'¸¤…èΆÇ_}ó9 âGí›Ö1½«»Á]!Ë h‡þØ àn×Kã†ú·0Ê´ýàñ'L‰4|õN$JÌù‰×ú÷uú°Ž—[â® èƒx0T +ûDê•üﻳ£!°”nRqK:AæáÅGûùû:´¤_éµ ¯ß{s&ù’ÀÁyNÍu¦Œ´ÔKÉé™j€É„x3”+,ŽƒIÓÂÑ3—°;ª½å–ëÀR%Eyh6¦ÿíž›ê*%†_GßM¥ÉÊl˜ÎX+wÓ‹ÅxƒãÍq¿žžv!¯Zº(Æ–åû4j  éi©WSÐÞôŒë"(JG߀R%K@£Ñ^9}QÅÑ¿s ©}ëÑDj:¬õÂ’çìØ¬žø}õv±÷ðqæÂUñè°7½Fß¾wéÚ ¡……Kå²ÏÏBËR+¼ÂMïyû uX´awj¥ðð—â㵂†šÃ\ÚOùF°[5´ý~û¦uMÝÝK4\:ŒÞQ[ޱŸŸÿ{cŸŸ2-£i'6{ Ó‡=ìÜ"ÏŠ@Ä€_fÒñá‘“F6dh7]иEhBÛ 2O¾Ã›išÖ6Â3•?¥ ùC[Mq]«šg¹pŸ:·‡1ìiCîíÐ>Çc—÷€8{ñšX#NÆ_†ISMsÓ«V(+èœKgoæØpè8Kî:§ÁôÄ2rUkØà_†éɪ­Po¶H‚–fu²z2Ðv!¼i9Ñæ-ŠÁsôÛÅQŽãÍq?sì(M˜´Â²¢‘âh–ö›ü=©Ãïô¡¸=‘ t~ö ïµaW“Rž£g¡I¸ ¢Ü(gm×[ö…Ö!¡—’–-…má#,a£t!"­Ñ¤Jël®-ÇÍOÊïò÷±¸ªW½²((GSt"g{|¶î?.Vl­Õ°E»Zh³¢S·Ñ¨'4†‡þ¿à%J|EÓ)j4|e0ôvãø+ ÆøK†Y½{h¶ví‚ÔdKáÓGáÐì³o9Kè¨áðÞ躘C¾Ã"î© ŸÅ¯/7œ6úˆÃ5b|õàÿÍ6jÜQ\säüáí … 4’çœ}Û6étÇ€Dø´…ÒÔÔ›?¤G[Ø _¿®Ú!CÕÒv}xïv¢| ôÂŽñÜ¥›srŒÚ N²Ž¹X†@;ÁÝfF š·l³øè¹üÁâè|Îoý‰<óWl•‚ w©iGÏ<´—ÏÎ6Jîd{;`¼áoLؾ~U,Úª¥ ¯lzÌÖè£úÞq%æÐ©°.­x5WMjiô^˜Þ¥Is¥.­ŠA0·#0B¶?™³\ µÔÔЧ‚â¶ #L©èHN¿[p/Lþéþ)ê%tÆ;­‘Àrâ=½ñ½âÕÏ•÷)X7G^šby;¬Û~tšunãŠEÐVµIâ¶f{BØð+Q­ôÓ2«ÑGÃRå¶žé8Æ÷èäÿÑ÷KÃkG4Ÿ,Ö.ø±Å0èôQl#U<v’>8mÔ¬þ. =äqñ Yq|µtïÞ]¦ãÃcqasûbXÄN²}ª´æÖö©#‡–ûùû¦O›3ŽÕEl‹Ó¯ó7É0´ …™žiÝì‹!@É`¥Ó, Á]Ì|uœºƒ×Ç|Í‹ûtÊcæ ’°|>Ž·<’1((°„4‡á{".jS|Ál’ã 3@ÃÙÇÿDó% à¬è—ôáQ˜4õ­¾üàÌéÓVk>¬è”í¢Ÿ»kçü.-ëÁuMM9¯¼¼ôL˜}ÁO Ô4<9úÁ¤||hfü-iN½×­uCÁC ÔÄié¹BÙÒâÅqC¤æ¾LÊÜŒ¿i†~æ·–íÑÖéM×ôks»áìñ¿£]¤·Ñ¨»w Y¿‰’Ï Â„É›'[‰fýË0À±æ˜— šŒJ•9„%½éôá2ŒûVEÒ‡!""Âÿå÷>xÉh4Üí[=Ô[[T ” öã7Ÿ37Å|`¯P.Ø4”°q=zñÂåÆììhŒ4³ðvàŽ¬=ÿùüš÷ÐýJRÆ›ÇÎ^t÷\uë!ØÇ{D“¹ráÕŸïÔ{PºâˆïWqöX-Ö”(ÈdfàÈLKKNÙµöûsð;`ðŒâl þm÷b€ãËqŽÙýEr2l}ri€´@š °áùñ7Àì”Ç ÐÒ)·ãI§é¼vù24›…bÒÆM4:kÆÔ‰<ŠÚ&-µ.Ë÷)\ø— ìâ5!†åóÛòÿˆåÌ£(ÀÄ3JmW”zÜý.ÇœcR6´?¾¥´JèÔéÃÝàåõ;B~½°©mª]Õ¹h–»ÈüÍmGdO’_ŠEÏȘìŽocá਀ïÓ)±0ÀŒº`Ú óªW¼óGÔ¾1Ûµ} ñvaƒø"¢¹[¬˜¸4\§ïŠ^·ëè˜?¥Ï0û·u&KÚD|OŽ+Ç÷DÜŸ¶®ã‰{2ò¤Ò„7ýµtJAƒ‚Ô7füsûw?Χ ˜.p ·$¥¦‘]Ýp‚Xžï=ë±bET@‘ŽÔPB  $ô–zBHOv¿ÿÿ&w™lv7mw³9ùMfvæÎ;çž¹÷œ{Z%d‡j6¿nÔAO¦sNBR²xýñ»2>Ǭ›ìsæËä/Œ$`²²³e$ŽÖH&äl؉„ŽÍëɽµmÞ+þÚ´7ß%ª’é\÷ñ‚5â£z2‘Y¾2ît‚êxö}jrrÚEãJ¼$hn.£<ë¦%¤Z£Æ'Í[Kšå®/_ú€DÚ,Ðß'6âŽU|ÿ—oûŽœA\ôTD°©"Fôl':4¯+øÝ1£q'3W0.-…ã+ûŒŽ²ZX¼n7bÄïמOÝ3Pdb<újÉFñÁ‹÷K“Þ_µr%›‘RòTû#îâU&ˇ±0áéiukTŽî-â´¨àÊñѲm>Ë›2RSëá<û_1mN›-Ÿ_ÄßdÐ8¶)aƒIݼ±UX·ø×•˜Æ¹ÀrǹËWMã‡tóÐÍŠ2ôÑ é5`à~]ûû‚%x%ɸc¯9Ò„;1ïZ:UÂçg~cž{~ÍŽ“‰ªÕ„o„G«†úB1ðRæ&r}<ï»E_~àòùóqx!E§\ £Žtá´±µÀ /.p`õKYáÅœ©A1·oášíb꽃̿ {pðXœX‚tô®6 Ó¦^횈V sŸ÷æ/»´ÃTO0R mÿóÂ}°}ufצ•…+þ¿xåJ0J«ÁŒDMp9}ä<6ïÿ›EP]ºiBýe#:Fÿ¼/èæ¿  OØÜVõ÷ó.Ôø±'ú´ØŽÈ5OŽë/ü|ĄϴÊÐ]ÐÂÌÉOÞ3ÀÜ \Thõ÷#ÚW±µË6ï™YbÚ##¥Vå$˜GÐ`ÃJs|DÆjCbâ5r;?8vqsÚ„ˆºKlÛÇv’Ѥ°A SŽ}ë–üºêòùsç;õî?Á3èÓFSBjøìùIà~ÜÌ£Áð¶Œ:EgpcVfRÄ–ó÷„m G3i–Â}OpGšÕÒ)… .sã8ËyÙ°eíÚE{õ»ÿ“kª¶hP tZßЬ^°Ì‡‚ë:” 0„4ã¾qOä}Ͻðúb˜ÊÅâé3Û5lŠN¹ãÔ±ÕY+ –„ ÖØÃ¯b¹rüíTèÓ¡©X‡Lûžµ*4М€¡÷ BLÒÒ)î^^ù?­Ø†•Ð41ý“ßDKHñ×SÓD¿ŸŒxÀÕÒ7¿øCT 0GØÀ(Ô¾M <ÂòG˽Ь¤Šú5«"šGwf÷Í€Æe2¢r,ú{—¸p嚘óô¸<8à3?ùe­ÀÇÌpækd cÀ ®U|ÍYSYïŸ/‘/„mD¤„Óñ—Ä¡ãçäÊöÄQ½˜¬E8+cHßÙ·=âPW–õÇ_J?,ß*Μ¿ -¿¸ohwÄ‹®f~¶£Ø÷0¥ºI™Q‘.Ôæ2ú(è½Êº úð½Ü—³ƒ|ô!'<܆5yƒoy//ÒKÀÕÀ¬ó{"X P"ãD$ƈF¹ßo=|³„ »£Å:Äùg&ã¦ukÈÈ8õñ]ì;|FlÝw!@›‰_V‡‹6kËoo[äQ|÷‘rÜhÑ ¦xsT„j%Y{uh"ë·õ¯V»:]nŸ¹Ïb”.0hÁÖ8¦-Ããq—D6Ì8h‚w"0tcM„ 7¨«ü̓H?©i™bõö2n}´‰Ñ¸¨a9>2:­qD{ct’Ù}¯&¥ˆŽÍêŠ>›‹ï–…‰DŒ“ZÔ¼îéQp·z—/D_;~¸Ó*±Ä£æ9NÜd49¿ªM¾ì¾ma‡÷î9Ò}Èð¾ÙÙY½Á¬úÓÔ´’wSeV®˜+Ñ&Š´ÌL3Â3aèÜ€þK:½~Ûªe«SR®' J2pÖ˜8wŽù ‘N¹ª­æc5îömÛµ{çáC‡÷Ç{Œ:+cÇ‚ÊþçÄ¡&?oo3ïH¦–áf™#ŵéŒ-Ì¡£@/¯0µ§𓜖aº€„™™Y/ôƒë—¾½)l{<žJÓ>j3(dhé”4Àγ:®N Ý%;ÛÓôŬi;Q¦Ø`&˜b×`ûF°— nì¶‹8îJØtc•qºÃE ˜MYNÒ›"ŽÈÕˆ‰£z‹ÄäW™ `‚ÀÈ3…ýÉs—ÅÄQ·Ë; @ì>tB 'ã.Ë•AÆûæJ!œÝÅÁãq`"š1t˜øæM¢¦‘áX½íLœÖŠÐÉwÉ(—¯‹Où[4¬SMN®Ú·%Óðéµ¢&óZЬ7m!\C¨ÀM1b@—æØ *ÝpñƼÅ`4‹‡õ ׆‹»cŠV–ç àã]æeCÄ&0D?¯Ü&¦?z‡¼æ¬Hy&]üv)}Ø{·ÒTã/'ŠŸ!,’öª@ÀÙ»Ù̇ ™ÐËð3ܼç°xêÞ«ß|KAõ—Uá’6¾’ÌêFô-cÒ$3ôöíÔLô†@N8E¬Ü)¢@Çd&½ òñû±PÞäÄ6èCM|…~r$#³Ìxè£ûu1ص7ïÅBCOä ˜-…@°¾J«@ Ã]ý:Éñ`ñúÝRXŸ:~¾ùL™È‰8ëÒªh×´.æ1i˜ åY¯f5Ù‡›ÐGC{´–YhiÉçÐLfžQTà3ùcìÄ­y‡O[ã˜eùvÀÅ*ô}yDO釾§–„@¡ƒ8Ó¿³Ô’îŽ>‰±$SÔ ®*^ûl‘è êݘ•7( èÃ#ßøÈzì#lûÐ#P.#YÛÊ­ûå"Ïp˜´Ñ$‹‹/\()¬;ŽZÍ(‘⎠Qà [i7¸´¨9u-+559ZŽå8±¦Iëvõê6iÖÌÛ·R`o/O¯¼¶v¼K·À@VvVzzjjbêõëW°1*&2â8¦4Xjµ˜BÙ÷J«a•‰ÃuWýG¬¿Ò©5à5¬n²7.]¼ Û†zMZÔ¬Û´i“s•ƒªÕ™öL£É€°œ¡jšþýG»ÇÓÓ3/íBˆÎÊÌLöÌ3?'%&Oôò¹ˆp4~¼¼ÊCn@š XLe$'%$Ä>yxoÄ) 2«X‰)iHrÔE&Æâª|ã4Ñù&ƒ•|½ÅÛдñ<íú'飪/…}>úPc†³{$´Ý Ó(&{öþÁâ[¯Í],@{ÉDMX9–·séAä \JH"€3$.3±‚râž«ô:€Þ5¢£9§@ '°bO€9ŒØƒ±„»©áPÏáÅÐ$Ú‚ W’ÄÔw¾7_~ç™{ÌÇÖìcÚòãu‘íX‚1j=4¾ð@Ž„:¢{ëFHž!¢—f£lw§æõÍ ÚNAðåâÉÀ®-ÌÕUÁ»jÇG{㵬j3˜.++[¬Ù~ch-h“›Ëkk ì0ÈE1… 5vätެÑíþñC$3ÆCEËê'vž—¾‡÷ïƒßZ¡D݃Ó:¸­±¹)a’ýHf«Ådà¸bÌ=;^³¹ZŒk.y³g¾dçA|/%pæ´tÇkl?éµâÉÃQÉØŽå–á5…Ã¥ø¸¤…s?üçÔ·ÉzT…3õ[/Ÿƒg…G㨗¸g½ì; yJÐ r£Ás¼Æ2öéÔ`rˆœàJÐXk Oª­]tÆ9¦žÚ£4kê†UHdìÑ‹&S»¢NªÓ‚NµÖ aíjr%ðØ™‹"úÄ9Ñu‘a‰ÁMŸ šK‘i»z-f 7L êT’Õ]NL†°‘Ss++Q¸!IãOMŒJc­…=G“Lb\³k©@IDATµŠY³CHiAbNÆKŠ«² h+|öBB¾U`uÝû\P“)÷—ÓGÎcóÿ/ A•+Ç—°êûüƒC¥Ù—÷Æœ[ažCaƒ@atö“Ãó¨ ‹"¨Ò|ŽÁh&G³2xd8cN—Z0Òpãêò¹…1iÉ9Çœ±A²ò>xïùsÎ<^Ø'ñ}ßœr—ÔZpeßqûi.IÐF´â"…6 ü¶É”ï>t Ú!£üÆ©Á䦀…‚Chnõö×ËÔ)ó4}¼ö{ ©qãÛ¼QøÆÍ%i‚¥À}b Š2ŽñFöj¦¿žÌÚŒI§œ³µ‚~Jày~›Üx][†øâFúW —Ï‹gGáGÛ?¤Qö)ûOÑ&÷Z!CÑ(ûÆétÊAΠLµwÆ3¬ÖIÆbËÞ#bѺ]fÓ2RLÔ·c3»)éU…,KÓª±2Ë¢xhdinÂB2*º#_íŠ:¥nç¯$Êc08 ”ù‚úÍ='[2_.Þ ZCÛÂç’€eVvK2UwÅÜ€>ÏaõW1´êš öêCà£xLp9}ä<6ÿW ª XA'Ô¹íV Ù˜ ( Z2{ETëæ -~¾9ÚmšËZ4ëw’L6Mð¨árÐÒG‰è‚8» ZN tǰ‰\½2YƒSpž¦©µ"ü¶×ÆÖšV”Ó¬€fi\íŸýÔÝê”yOߨmû™sAÂT@æf¥U±WŽ×Š:Žñžàj•a3Ý[¼üÑB˜ë]’ÂF÷¶Ä‚•Û!Jÿ2å³Eÿ•W¹cÚqøumUÂ|$Yì#Þ–,<=nàO[WAÇ„&Œ½÷”#}(ÑôLA5”Úu۱êX üø¥f{2q܈ õ~8ÔRêPJPL÷dÊÈr å¦G®«çÙÏ,§îÅ¡[ƒ¢Ïœ â½òýH«œ@”°ÁÑ2§üI·–³_ćºG/CSôä(ü¨1’¸g6H—ìC ÿ}4PïAÿŒðƒÇ¤oE#¬æÒÁ‘L:mi¢²)"Zj*8IRЬ~°Ø°+ZTÆÊ#Ëð<Í!Èt ïÙFVKGÔµáQâï‡À0Ô’æ\1mPë6õX«{:®2æ™øÖÒù²!œUƒrͬÞà “lM®–oÙ'Æ ì S©ñqzh»¦RZšÐ;è KV+ÕjUrÌÖâ8@1{çaR£í K!’oWAÕ’áSØ¡öï³ßÖIó©AÎ[+5œ*àê½uú(r+(@ñ[®Ž¡(Ï^¸…æÖs%‘c±} ¨]’`c¦vhËÞÃøî£DODŽc^ :äÑŒ«I½‚>[÷‘Z#š\9ìcê9ié™âoL4“¢™ÞöÈáG™âµo’X‚ã›ÒÓTlߑӢYÝ`Œe!0‹ T,ÇÇRGÔXáòùDá´˜{Rb:c¡e, ­°QÌGê·9 ìKö¡êG%p°?¹i… –±1zàŠ{‚-Z%“ª†Ùz–Uï§¾M ËJ`Ö–!ýs#Nzù<(:?jü`ÝÄ7i’´ICÑ)… 5&©>Ä)çƒ3…ÜֻΌJ¡«}³ÑaÚb4‰±Æ‚¹¦Sãð©àêÍ—è»@A‚ÌC´!Ó>þѨj‹§Ç”+œŒƒ¯œjJ“•0Aaƒ¾«¶EJÇt^쮾ÒÄJ­ð©6YÛ‚åAØÖ½d“xqÂ0¹Òl­œ£ÎÑlfòÝ}‡­‚!_ ŒHÕrʘ<‚™£žgQ FÁõôaÑ.óOW ªÔb¦èÀ=«ðŽ•¹b¨é²ŽTµÑ˜zµo ͉§Ù^ßÞs]pMKjBâžh¡à œéWnÙ/}­èƒÔA†Ý^pð†¥Ÿ£¼‘9¿³o¹`ë¡ÔD°ÌŸÈñ+òwÑd‰‚*… .0¯Çwm‘‹#`ÊÄoÜQ`oSÏ@sÄADÜZmM&|&r¢äõ!¹‰IçÔ¦2ˆ@×Ö åm ›¼árð‚ *ãúæ°6>ÚGT¼W“¢Ú;¸z§VÇ6«É '{2\eÔ®+FAû ˆn‚ö#7ö!7ö©bܸWç¹w˜2cV6fîì™ë Ñ(¶]ûž¤S2ªJˆP´É2îÕMúU4ÌkUŸ¢ÿœ³9åôòŽÃû€}Ä=7E§ŠFIŸŠVUŸà”kÁ†ó_è-+æ Ï?0$σ¹¢ÿĸ"Žä̾MßNÊ:˾òðéG¡|(81Ï>Q^Wÿ^ŸRŽ©ê 4ÜÐ)“!5Ur†çÔá\%¨Ò¼ŽBí7ÅúÚg¿ç0¦ˆJÖ«Î…KAµ0÷¨2Xè£À¨Uôà·A:"³ìFÀ¯Ócøø‡zpÑ€‹PÉr¤mKº¶üNºÉæFð€‰Ï ð¡†B™­©EúUq³†…å–Ç|ïŠå¤™&Ëð›f@†$ÓN“F¡°t^çf –ÏÔŽ#öÆ1Uß᥉#°¨‚å,øFX3£ã6#iq±@|q|£‹BÓ7MµñÑö8’Üû/r†h!>5ENj’äDÊ­¬zÒ5™Å˜i™®Ü©¬½Ú-Ó^E{ª/ù[{ìvˆ0š 3rµ®ã;‘>ù^ÜSàP´ªèSá—Ì .¯i7UF»gy‚¶œ:Ö–SÇ9¥õòđ‰vo ?Ú2ª/yŽ}Zt0bŠ~Sþ;n a#ÿkÙ>ÃI”¶5Pqò­]+èœVÐ(¨¬;\·dÆÜ¡M¥Ñ†ÒTÚvæ¤;ážÃä*á—x°dŽ-ÏY ªÚò–Ì*}úïði䣉s¦Ž•ùYÈ`þûéçäF†š€<ª×©÷CÔFØPôS\ÚV‚†ª§0ûJÿ,myk ¾özIícªnÒ“e;N#Ú£êmÛTX.Èð>k>fª>kãcqq­ê,hÏÜ)ûi¼sݪ8”ådÉI³,ÛO€›Ç£/…vòðôl4ëBÊ F—93,`YÆ[©·—äa CÃi•Ÿ¿5cW©7ª dØè‚JY»®èT„††zËôjgÆNx÷ Qˆ 5´7™rƒi²³~ž÷î›_âÇpù­Nšþæ$,’Ž×Ë)®ÁÏ\³d?hÎépÞ[3&é…o9aÃôÓ: Ä€=Ï#V`…¹hÞæJ`Ä%&[£©ß™óWÄI$€cx]7µâë‘ësìfÍ+›Í¡éÔyø MٳСgKóM—¬-Úwœac=ÚAa£¬ †ÇõÎò˜ s¶çð-ƒ3Uöñ1"H“ú‘îupC ©Ÿ1áZŠ))%ÍÃh2Íš<ó­s°”øoíòµ? }˜fq7†=ýtùZþÕþoôœA#„òƒ¯OÅì5«°He•N‘‹¨Ïg¯Ÿ™«EýÖ˜»Ìèå…p4~憞ùâÅEÐh²O®›ùÙŸ‹ I× !Õ>œ7y25U¥º°Q*hתc t0@3­x„|^ûüˉI€€dv0åaN7®ŠÔ>ç—þ¿Äxhäí%®Ã•0ü0˜qÚ#k…Œ­Ô¹²ýšgIáyâ?g ö¨Xî “ÑX‰`MÔBÂgÈ€ä|GÜ’ÁNIK— ‚w<^àߋώ{îÑWBýêÐÕh>é´,Ç\‡¦¾âS¹Ê Ãõ‚«šú!GNø£!¿P‘é” N¹ôòö1U~°8W5Ô¸Y ƒç ¶Më˜Ö†4xœ½ôø#¯¾1ùë9¯oÄu—Ó©.lØïWýªŽ›Cº·‚ANîåôº™1Pb“€R@ŽdÞð\ÏG^yíYOÏrïW 0Ý;¸+5Kº0] RÒG27UWäÞÂf8‚ü] V…W½ð×c¯¾ñò—s^g˜f­`\Òǹò~)ãåž8bÀÁªvnYŸ&¬:º²ü,14GtUÐiËW“ÖN§‚N©}r)ZU…9ø]õêt èÐ1P ¨•kµ/ʽzÙ›dx´[YxK¶—+Áå&¾4ãyï![»á¥Œô,löô²ð’·rÙ/OéÙ©y=ƒÁãýG§…>ŸÛçeA'/HZ-)6ò™-þàˆåŠâ+w+Ó»¿;£¬"ˆGû¦uMPü4q¹ý] N ÝeÒÌ·;—ôuaÃU†a;EŠ}‰ÙË™ëC:¬b ,®b[}ýdÉ0³NˆjÕµÀɱdOsØÝl§4Æ?õü°rå*Ì azlLƒµü9{ª^‘Ë1Àþd¿²ÙîÝ /LšÛ÷î@«ÈN*¸Ù3­9vW``àøó鼡=Œ•Ák C:mÓ¤¶€¿Øg=;¡ÙÏvéÔ˜)æŒÙo—ô•u3*+Œ»xUü¶v—LzæéiukTŽî-àÏQ°tÓ„ÍFÞ‹þŽªò–®g±1Œ'ó(8t"N:¦ýcTOuÊá{&S£͈ ÐZ"ÿе,îIÉ©bÞ¢bê½óE úsC„tÜ4¦o‰ÛÈðÎL,× Ù°›"D%hÓÓÓö!³u‰WU ƒFjšóõRD«« Þ{v|ž>\¼nòé··m,ÊšßCaÞ½°e¢†6Ü2—Paï)y9™‹‡Œ»š99º³ÖË̼µlÙ9  JàÿjÞVÅ4aTO+91¸m è_Ã¹Ë Æs óúöÝbÆ%×ÑXF+5Z7{æK LÒjÇŽ}†Þ;øÞõëþ[4 ÀX¾œš½¯aóž#éñ§Ï¿ÏÉõC§qÛ4j09DNÐ?+„CçYƧŸöÈHñÔ=E+$Zs¤ ÁG>|g/ñØè¾Vž®Ÿ*¶ )ãÑ3çóÜÊLÒ…Í[‘çÆ"ü¸„Dr Íʼq®Šo‘7cMøA«50<2¢o §Fþo‘# |ÚþÞ­Vhã$ógl@Ô! 0Î 3³¾øÃUkë¤=©ñ§þõ¸è"€S쇘Sçòöïø!]=1–×íØrj. ¸3eŠ[õé2õBbúçp~÷+ØÖ[Y\ PÃѯS³Šw»}êPÙáI N…ü\SW6*?P =Ú4L~F`va\A_)®§¤ D ë.™2x[÷FTŸfâ—Õᢠ”X0ŸõkU“Ù†y?ͦ>^°F<-Iøþc‚‘V`)«f½+·F"ûqª©$&#Éš¯waëyª=úÞ6."Äç1 횆Èlò±dÒ²£È3Ð}Ú¿Ssª±b½_Tñ÷•Y•YÛË·Šžíš jS¢ Èì2IÛ+¶á#mŽ>­šç¡žH 7º_Nr6jv:!°×#ŸEÍÛE44,É©ébTïvâЉXé\Hƒ´5ÛÊDpå4È%hK6GĈ“Xi÷…€B%'Jý}}Dä‘3ÐÖCdêÈݯ+äi lÛD íÑtºW\¸’ONèþp„ÜsRf•æóÖ®!R"œ£Ø´'F2‘=Ú6’Éý¨…Yºi/h¼èúˆ®ZYôíÔ Ñ«|dvmÒò¼EëAç!æäxùRüJêâžB‡Ë€á€Û"CxÄ¡“¢94C„±—D*ò‘4Ðô;ïï–n‡°ÒO-ïa&x†1æXÀ³˜!¾"æw*Q 5( ‘yœa‡«CGœ÷]ˆk†B<{áŠð“‰G bxÏ6âvУ­±@=}µfûIÓ£ûuIJW"‹855ƒáœYà 4]¸z‡ØsJ–íß¹…Ì€ÎwŸñéïbìÀ.bÅÖ}âêµdѱY=q'êâŠØÇ ÖÊœ0ÿýi•ðÂï·«ÅÙðÀðbÂØ{étËÉP»9ûÑÅ­ŸÌ¥4ŸÂÞÛ·R¥)­Õ6éÎàÅEgÙº>ìïƒÇb)l|ˆM9áºt+Öø-):­XÑ·â#˜L˜Ox^‡[¾>>wþöÓ ÆÞ?é^—ôI-œÓÀ¥n§½tA·kRGšL¬ÆÄ͉YA"Vôæ/ C·ÆbʸþR˜ØÆ€‘™ F2ŒØVѱy]™u˜&=[!D(Ø}Rd ¾úÈÜ{ ™‡“RRå¥cg/ÈzC`®5qT/©I¡ aïyªN}odÚŒ“xüëšp‰Óî0…!“¿Œ;áøÙ‹âCûœ…З‚”µÅv0ödî#ã6ÏY ¼‡y+6ƒVl‰„ qRôéØLVÅü_,Ú ®A€¤À“†5âÐ)™Å>#3[Ìùê/Þ¢~íjaÕãa·Sf¤žó/ ¾ÍêÕ~>HÆwé>ɸžB²3æ ° QÇãDW0–¶#yáìyšîãÀH6¬SCœŠ»,úth"smÌúòÁätpŸü²Vâˆm݇ïu{»ÆRkDÆ•P£Ze0©‚Lj£:7ÌÖäEÇý£ ¡„ÇÕZˆšZCpØsZâ†Åw:.:7ÚÖpu($‹w÷Å7ÛýwBDàû&p,ˆ: Ü—Œ{]”ã“7ØçíÁÄÿsÂ0Ñtñëš‚á4S R ŒHa³ž¼[>ŽtCAÃÞX ž·ÂÃø!ÝžR|½d—£²¿« ¼1Ÿ¡`Sĺsâ¨Þ€Û‹Eï¯^“—/'^G¶@ n(îìÓAö?… ŒLC¸ ‚õÄ;œgš(’÷Ÿ2òžuÏ_l+ñ*¼ëÞ.ØWCx[sϾrJ«ØßÜ«þÇdö?iÁû_ Åå‡?ððíF£¡=ܱNé#½RLgðËéÑ­Ã=¹4Jzpjÿ놪7¨‹Ò£˜¹ÝâµÏË•JÛô¬œÈ;4«‹½W0ÄžèSæÈ@2gW¹ag.º¶n(™ ®–vnQ?-8Ï3á Ml¹³·dJc’PÐód!ý_¡1ÀìÈ\µeÿ5¯,ŽœŠ·{/¾‡FÜ.~^.Â!pØòý  Ô™øËX©¾, š) mü¹ê¬…Ãx6³;“^èЪáëÔxÀÆ[j[Ș²„}`&ƒ*‰ÓñW!T‚ W£µ@íEpÕ‘™íG5±=WØ»tU®”ø‰Ú¨›š juŽ@ËC ä%qáj’d„wA RÀd4EëMÛL3£Z6AWj@ªVvJÂczÚM5Ç%û&!Õ¥ö‡ïKØ…ï¶c‹zùÌÜvm)}„ªVö“ZP¬fæiß}Û‹Xx ÓNšˆÉ­ÉÛ¤F©Z•J¢M£:¨×íW¼8Šq‚åH+.‰ç4hÔ… îèÓBLˆèݱ©”†ÝÞF>`·–‚B¨ Hqàè©u©TIð]k€^"4ãX;Vº´±ñ)P‚àÄ„–uñž@Ÿ GS"‘«M¶ÁMÿ‘fͫŷU€ïÅ„<nÚ\½YÎÀû›ýîà?õ»ÌLÅÚ»L™1«?7‹k¤Sò~ÌÇí›ÿí¼Þã|Y.òíŠ:QV›_jí>r.áÎ>Ü  PìTyÀéfTÙ&c22or¢ — C5²W;iB°`Õvi.òú㣥æ‚fNo½ìÆ{X¼tX¦Ð±'ú¤¨äã-5!d^-áʵë0 Ä•÷ 5%=/ïî÷ +ä&8Â'Ûj™+éƒÌrܦÙ™<$PÏ¥¤eäüÀ®gÂÃßL?Mš¬W¹ï‡9aOôiñõ›¤× ÎaÐx¬jµ*ùT4Ÿ¢y“íîs:éfñ7|?hfCY¿+£=„À˜×´S‰KC74µ¹’˜,Þý6‡F“S3¤ Y[h9Ö툡s즢P~¬Sá„õöéØ@™¸eA8lRKxÃ䊚­ÚŽÀðéñƒä=… ”0à—+œÖ©‘C{ê7ýÐH߇‘€ ôCQÀèx êB˜P@MÇ}¥¦ùpª÷ù±ò3UÝ8YSØà@QÑÃË+¤’wÝ4ÅíºÉ¹ ¢i+¾cjr2¹wÒ'Ò°SÍTP>0š 3rO®Ó\$r²À¦Ãü‡@³KÐ2åÚkÔ>Ðä‰+ü{a>£LçÈd~óç&ÁÕýã±`g¿šŽÚ[å± ¯F~úeÐ'ƒ $µö€Ì+MòÎÄ_\§éµ¶¥w‡fRàe„+ÕîNX]§Ö£Ìø¨™ ëÎÂàÜ¥ñî3÷J³(ÖõÌi(€p@¤æb:‚0Ø$™ê–m•F£:Õe`âA™ø¬|¶VȸÒ,ˆ“@IÀ}p¬ÂFÐm5F.Xn€°Q’Çù^j$~„0Á…‡0yâ^ ‡GImÖì'ÇJ¼ÿÝ íeyÌ>² ‡ô×`’¥þc84i⥑ÅhÊÖ&k‡¡mª€{Ÿ¹o°9ºZaÆ‚rpPÕ‚§!ÿ7Àï‚}ߦ~ÔªiAiɬµ[[ΕÇ;OiÚ¦}Õ˜È=qxnþ‰Ð•)øY$ÅÄUôôòª^9À7/á\Ç-Wâ|é" µ~{[óX§Àñ<Ç”4X*{h/=.^¹ÂF“¡'MäLL®n½Áj[Ò$ ¶ÍtZ;°²Ÿ[Ði/˜Œ¶BNÂ{ó—‰]ZB;[/Ï<'/ºÁ¿e›÷ˆjX ,ˆOÅŠˆ,N[hÒi”cl¡xÞ[3&ã\‰!ÿlXâ*ÍȨÌôô¸¤Ôtm’­9³šK»É¨¿áØK{}¬do<&[F†´"¿-{‹¿±BLb„`ëL&ÍtiYOÚÊï8x ‘ˆZ-Æ¥dŨ#L|ÈlÐï£-|GŠú<«(¥“ìsö}FjÊùÜ&HšÐ;’>–nÜ#¸˜áUëØŸûL«»n0w;pì¬xîý¥0R#gEšá‰=À° éÞ>6)âyÀü¤ŽìmEŒD5õï¥@&ò¾¡Ý´—­Sˆ³—àlK&±9ü2RsmúÉÌÓ„oèÂÃ;ûvû¥Xù™ùÙ"³¹Í#ˆj¦`[ä0±µÌ‚ÏÓöžŽÜ£ûv“é!füoΚ0@ŠIwõ•0ƒp5Z XÏÜ7+ì¶…â‡4ÿÊÇ aÖHŒÔU5¡Hû胓ŸázRâ >>c¯À—@ cEzH1 ·„Y¿o:k?ûÀà|µÐyÚ¦N4'£5ô‘)¨Á -Q“E +Kæ[Èì¯ß S¿N2°D(päXоiˆÇxÄ`FEó*FÒ)È$ŽšZ ]ÔŒøC0¢é§¥¦Úëˆ=±o–†UjѱkOü°9 º«ÀAz¥tG&ŽOEv¾Ë•+&nñºÝ2ð5˜Ê,mB4±õXu¯ èx_¸*Ì1ƒ!p܆ –ôt‹0_-ÙˆUíûÍ`Ë °ß=„6¦¤Òiƒ´`uÑ ç]ŠNÍBqJrR|‹ú5K…N-_š a*ò'ºªøšhm¶°¬ƒ¿Lƒc#ÆÐDšãß}C»#PH5r'̶8?oƒo}åhNm$(‚tÍjæ9’‚5ý"Ôßhíœq®<˜8y£n 騬k7ðpg}½êƒ2?{:Ì?(èÙýGs¢ð8 ÍN­‚ ˜á›± ΰ4Ÿ©Mf2XÜÈôý‰ñW¬Nr`ä@nh*Áæž6ØÁÌ>>¦¯Õ¶3J̉X:üé#³IÓ2¶E}žÕ”ÒIö9(ÃÙãÇv  Z&LJÑÇ‹p¶µt®%p¯Žù[«¡ kæ¤;ÁüeK¦‹× ôUPQ¦*ÃÉö¿ÿwÎÍfX;}¢æÌÇ¡ÓBµ*þyÊŽèÙVF­RZ–= Ço2¸ï??^Ò#!}ðÓJéÈËAøAÐ"µ(Ô>X®@[cú[Á~ø£—Ä·´ÚZÚâgãþ/13ÑЭ¥`Fá,æcÔT¨ _û^¼ÎMÁó )±fÃ}¨ñCœ?{vOPõ`iöãJaƒŒ èGÑõ–ÐÑ¥¾ý3L<û¯ ª*Wà¨9*Xï`¯ÌÙÁ¨v}:61O¦ô”39û€}LŸ‰—'ŽpèX0v`gñý²- «ÕÒ·ƒBÔ±ý 6h&FÁ—‘¬h^úïçï“GAï\Üë{K'}éc1[Q‡VÐ0ÓGqëvÒ}d(9§r+‡±¿K hÈ0îݰà@3Т€ süÊÄ‘¢F…€¢Üêвô¯k…q·,  à=ÉÀ+z(UZPmÊݳ]€$.úò?þúÑ4燖³hDQj[$bÁæû¿¶€O œWµ ‚iPs[Áx~FÐ òá\§‚ip¡‡ôLƒ¼ƒ§0  ƒi¨È}'}¼Ë‹gï"6!ÂàÏ+·‰éÞ!Çãõ»aލƆ2Z#M]¹ÑZ!îéûÆß 44¦gi’Ë A4§e”Rw˜Ÿ‘(lœF§¬ÜY@†Ò¸á¯EµiwÑvÁ¬¸…ôlï…)©¾„Éæ$H:ñjN—ܸêæ]±œÙ(d*mYuüôøêмŸ‚ж (à >·¸Žé4Å¡°Ás[Ï˹êÞÿÑç cc®MG£¥’r÷l¸ÛÑGiÄÁ× D WËSÓ3WMxf]Ô¢iÍ¥ä=Šþ~p?'µŒÈPw®[¸EÇN—6ErvT2öZË2Áâ³÷6£‡ 4§´fJf9°¿´õJ!‹(È*øúÍà'ͧ8î0‚Úë𱡉[cåó²Wû¼šÕªäùÍñå‰q¤‹´æ+Cìæ´B{Ïu¤ç@h^´Á2d ͼ`eÌç¤É(ÃùÍ Ó¼U3oÕ6WïsÇ)§kàòrÒŽ{K~P’YÀ>ëÒ¹¸Ÿ SÙ©ÚŽqÜã_™}KACû¢£ ¥­zmwtUûúÀѳ†Ø“'þD´ÔªçÈ0”iúpž,ë¡C:mù ‰f5c°:Cf±¤À(\¬3öâ¹ÿÆä楚¤®°ô‘‘‘”yþô©/0WO£¹’eT¯’⥤÷Wà²åÓÃU/&ÍãÊ25OH”“¾j«£Æ :<Š –µ¢Þ_˜ò\D.ž‹£3Œ4Ô¼R˜*J£ ™7µ•:ÇñcLòh:Å/–«©¶V€!ÊÃóe˜ã 0TÞ*š€ÄÂ䉌#+2 i òÌÐ<˜Öj…ù$݇]æ‡!,CÒSú2Þ…<4*D¼võW…]fY&Heä¿—0rá厙~qä×Uhæaðï _ÓÏ+·‹zX%¦),£íF$½W!-øÞKKGÐÀ­>­è{w%t¸[»òµ§0-ÔM; ¬S˜`1tù Ñ·R]Î^HAv¢0jƒ1ò M›wƒ6%’¹× c¨žçš½Y(æ˜EšÈSBgwÉÎö4}1kÚÎ<ŠøÃY›A†’ fÖÆ%‹Ü=å©û00TyåáQ¥±‚Ìéà PºG_MÙÙ‰a-Y‚§*aC lˆN6ºƒþAÜ üæhRÅ­´¡ˆô‘±ö÷ß=ðÌó"7J„Rg✅?&â[ •;£™QØ`"Åÿ{p¨™‰sÖsÝ­^Òì¤Æ¬Ìóÿ¶p!ÚGaC îÖ\Õ5IkL9‰«ë.ß3`M9Œ‚y^^yxdž6Ø[VÌÃs™@Á”þ„6¯CϧÔf_G†où,))á:^5 Å0PÐà¦Óp«AèƒÂiFJJâõ};·¾zíRü¯Xé'ÝÜ”@­Ö=`ئ!b}ˆ»«O¾Õè›òÅ-^jÛþ#ÆË × »Ã6~’œœ˜ŠËj‘‚}ïÎó‡v²V ¦ÅÛ¹î'We N#s¸ƒš ]f8w&¼[@¡åÂ1SÃH†©1ÌP¢±Š|QðhNÈ ’i ¬ÐCÁ…fǼ¦@»úËsÔà}¨x4å¤À`l…fn³1/ØÛúßÿÞ‰ ¾:jګʹ×n˜Ñié¹ϴ¨}Þì™/q³8­ø39wí7¨%5Tî*°ÛJMµZ­˜j?ƒiЕ|Ò‘§ˆIu‚n‹ jÝ&ìåSNÓ) É4¹”r¢X^”´oO¨mßO:o@ð.¹‰Q‹Úg•§–å—Ïþû_»õL^‚[ ¡ÄØy> Z2 ا…­XºÙ¿Jà78~T,¦ £zt ‡ì•ÁKüÉHî:tÂp2&jÁŽõð¢"… l¤‡œ0g¯Ór«@éƒ!éƒ2Rvü½z7¶“íÕH»cÄ `¤œµPr«t‡[¾'…É㇢–íÞ´îÐÞ-#sû_î.l(œÞàèÕ™RÜ3óç0 (…YVe¹o†H{«¶§A±1ü–h®B¿2®Òd„Àýyh3\¸rMž¾f¹'-W á#{Š%vÉà´“·¶Ìø: ¡å ˜aEŸŒCÈèð'ÉÝÊVN<ïV´€÷äü›gkܶÃXš¯1؉;ƒ­ÀÊ|OµÝQuh";ùî¾Ržþéï²zF¤j9eŒÔÄõA° šñ1Šå$iÕšÌÒáœB0=»!¨qUÑ…SšèLaƒ/À¥ Nd6“—ÿ4ÿç¡ãä3:wùª Ñ<ÊŠÚ¬ƒ о—¦SÔhœˆ>øëÚßÐ|ŠZ &ôS†Òl°&>ˆ…[ŠAøÔøÁÕmÒRùÑcýõßï¾~eêŸôòò*ºÃÁ-‚ï²øš™™™éï¼ÿé÷¡³þŽö'`ãØÁ¾× ø©CQ10ªw[8p‘GÎÈ\.¼ß^8ekaŽ›ƒeNžL„o¦S.}é²&RŒÜ§€yö@iƒsä´åq!ÂBÓA70Àù–‹ÍˆÔ«}Ue¡ö±êœŒpëý:·=‹$¬9É4 uó-\!fýÔýðé´ ye/°…6àorT0 ò§ NÀ@"P@z%ݧ2­Cn"UšZ†Çgd,~Wnœgoæ4Jp¦°¡˜šÏHaû +üðKç¾Ï»Þ>åß߯¬ uª dh [ò²‡Ã ¥ÔšÄ< _ʨBt‡uRÄ–ó÷„m$³@}<7%l(3*EÌ:}”ZϹæÁ  ¤ÔnpüàXňåÿïå76¯Yv©q÷o`÷Âj‘¿v"@9Ü)0·a”„ü5–7¥E>ÿ¯/Þ¼%<Íæ¸Á”æjü v‹ãéA8tkp«mέ £þ€*°·l-Ì1b™‹%-#ÃlÃNÓ*f}¦ÖCÁx˜mÑïèãkзÙÒ‚Žê…2dÃü×5ᢠ½¢¹´ô,1ˆ&dÌC¡ˆ‘‡t0c€ti±•®o‘¹e…<(J` GÓ°5·Ð×H ÖšúÜeéç´ !u.ÞMs,ÇVµ9eŒu¦°A¼²ÑZfÏóÚ¹amXÔÎðèÃFŽÌÎÎ FÕêsS%ï &f\-­DHl°c -3ÓĬðL؇AÝ€>L:½~Ûªe«SR®sE’:tnd”°A:°$b>€”› HJØàäÈn%p”[¹òïÃFßê‹’“öGò±jó—bЯXÁTÙ×€0¿&;d4´ÛeÌtÚðZíÄõò®ÃOzf–)ázŠ }"ǬÌôKß¼7ûsô û˜ãÇ Ž6h†IaCMˆ8Ô¡ Xf‡gyF¤â¦[+À,c-Ì1Ã=kadïv‚›h6òøÝý¤™~mŽ Ë0ͼÏòÜÈ^¨æYÚÕb[¡™n÷_óWˆ‡Fö@¨Ý2ŠÐƈhi:6ªw{iö"+¼ÅþM™1KÚ£Í=sæÕ9žÒ •ÛMpCó¾¥vHÓ©ó0#œAw´âa^¯nƒ†7ݾf9Z¦öÅ›³… 2 TÏpUЉ"p‘œœd\óÛÏ¿âÜòfm;6 iÜ´•·¯_PyïŠ^žºyðâ¶€,Èéé©©‰©×¯_9}$&*&2â8«´WdÈ(Pèà1û]i5HZÐéC‹›äØôAŒpàÓšSQ‚à¸å±ú÷Ÿ8~¬hÖ¾sÓÚ 5÷ññ :çëSò¯iÚ3Ïü””˜HÝ·\Å«Z£¦ÿÑãÆzzzæØV`q×$def¦æ–'S«—'VŸJÀÿ8â«Ýééi×R®']8}ôÈ#‘{à‰*(hpã¸Aƒ¦s?Ø+Ì}†cˆ[+À¶ü# óh­FaÊ—¤ }>:þzJº¸š”,„#Ÿ³yà-+hŸF“aF.^×åîåø†c%hxAä9œ€‡àËáΰd}„hڦݓ66¡_ó±&CŒ#ÞÁÙÂÛÈI‚«Rj"מã$’½o÷>lÑ8Ö&áÇ@Ð?„<¸ÃEˆìSnJdß’) s î ³*©Óu“€3èƒu*:£@k^°È=Ÿ½gç^lQø­â„« •{YþR|\ò¹~‚ß<Ç:¹):V¿Õ}zy§áh7Ï ÿ9npÓ ê˜ýͱE×j :ØÆµ˜Žî ÿ‘cb4Ë—‡Vu1¸{+Û7Ý W Vi¨1”{]³q+Ðw¤«"kó&çD óÞš1Y—dï aƒ&³ @MòJáDÃ0töT«–|y~œüup/ ¨þcŸ²©µ S µ:É~%£ ´fâÅ9-èô¡ÅFÙ?v4}#ŠF”¦‚ç´ˆ7~Ós㙸W“*Çí€ÊúT¬‡B‡5ëåspì(ü¨¾PôÁqA-RpìPÇ%d¨±ƒýÃûtÐ1`LøénI?m6¶ô.(žJ“I‰W!‹{‹Òk’þäÒÄ@®fKÏŠ>Þ$Wl´šØµÇJØà$Cfæ 6´Ú õâjË:”Ôd¯˜Åìi™Å(pÏóìc–S÷âÐ*èôa-eê¤êcgСh„‡ziŒB­;8`š'ÑÜc ç¸W×Õý¤MÒ¨bfy/Ëèåspâ ü(Ü«qƒcûP ì_5vh5O8­ƒŽÄ€ä«þøvÞ‚Eÿ›9ÊõêU•) ÈÚù‘tÁqÚ¡à*aƒfããÉI„ÇœX8ÉPà°4c€K:¸³ úŒ7ö¥–Q`ÿ*&‡‚N¢¨Lp&}ž(¨gî8~X.P(C HÕ`ŠCy¿¢7E§<¯îÓË߯‰â— ú‘ýGí†7ø[ E;pÛÍ ÌŠ gz§_û†ó~_/ukåÔ¤Œ2E ïE rg´n\ÇfÄ·[÷ o„Â틬âZ`"¿gßûQ¼úè¨<‰þ´eŠr¼ÖÖå<=Äí튷(ϸÉËrÜ#p¯ÝäɲüÁÙN0@…ǃü 4ùm§òÜ®6øöj‚ç^;áaЮ&ª—V“ïÕÁ=0 (ö72#Jƒ¡ex­¨ ÓGQ1æ~å]EŠîȨ*AB ÄŠ:æ^{פO-½É“ø§]5©ózùL?ª?X‹Â;ûP J¨QxÎyÚ-þŸÙ’O»”OØèмÂÐú9 ;àÛÄ_›öŠnHBÆä[‘}<üÀ ñôøVŸIÿˆrJ¬³=;Qî8´šð×±y}™(Ðõj똷h½Õ«½`"Ä›´c´úîÊüë~ýÇf†á/<8T4Av{g¢ ŠzÁUB‡În»•úÕø¬öæ"SBgwÉÎö4}1kÚNóÉb¸ZØPMäèÃ/FbW»šÈò½0Êèà>ÐVŠ9P}Êk%U—N%ÅdéÜï úà›‘N(àªñ‚{Ús–Ǫ}Ú½½û´åÔ±^þÞN´{[øáyUŽý§˜uNݧïí`€ •|½e>«º oËd{ûžÇÏ^ù5XÕß;!PÍõ†8ŽŒã'E&’œ1Ò3}èÒ\0ÁƈQɧ´…eVhVF’Y®™`mסHN˜£Ý¸”$6ãÞ“¨Ãa¬9Ü;¸«tߎüÄ…3¾7#V­G,‰*þ¾È4­QRŠèÔ¢¶úp,ûbÎ I[¦©$¬…6¿HÙ9ØaÑTõ©oß·2 i¾ïð)Q»z èù¸Ó… jìæþ¶N¼2q¤¨Q! Ì⎡¥w¦¦žÅ ÜøH-ÞÆ˜)ærHÄúJƒEy[?KKØPí!ág?1óívÈÕ0Ôd2Ö5DM¼u%U@ß»ÐiI&ú<ÄI+?kÆ.'µR§'!Ö™Õº˜>L“f¾ÝÙàiÌž:}ïã3fM»ݗçýrC;š²³þâÐ/s¯q²zy Áõøáw-·ÐМ„b¡¡7B³_t°H0òÊšÇ1§Î!ßE{AMÈü¿ÂDèä»ÄÄ÷çoŒ©ïï[QtoÓH®Ü³Ücû‹WlOÞ3@˳ߋLàÇÎ^„€pZ 1(®%§JÆüxìEÑ…ž » ?  "‡NŠQH¦Ç=Í[gÏ_¿­Ý!¦=<‚à æþ‘;zËóþ¼Z<Œã¸KWÅ'¿¬oL#ïÑþc¦ò!ˆ.•ž™-£N)aã·µ;E­jUÄÄQ=eöqæ ðó‘Ì ³ˆ÷À´põ„5IáçdÜ%±dC„$‡5ªˆ¯–l” Cj ja:·¬/êTÒ>ºÌÏ›=ó%+çØ§¶¬®ýµ P+«°÷ði¯ådBHf¹¿oh7Áä Ò+·FЍãqRÀõÂù:&™u<þR¢øaùVqæüe™Ýû¾¡ÝEê¤ÙŸþ.Æì"VlÝ'®^K›Õwöë <‘,òãk‘@2Sü÷§U ¿ßš:¶L¢îá=Ä„±÷þWcrþ÷0˜"'8¤’ü­+øLhè7c3c§b¥á…,cV°ÁÃ`ò÷ñ5Vö÷aR¿*)¸½D)`IÛŒ ×RLI)iÀgMžùÖ9ØJþ·vùÚ‡†>L“¸ƒN%Fa©Uà úx2ôS¿Ì¬„GËyŠ)HèÕ¬NõªWŒ)gR7ï9\+[VÇ50F½ç¾væ3-b¸Ì•0k —rµÜÑøÁBw¦¶‹;¢ÞúòÁq—*?>ó­7DíªóçMžL³ª›r8l½I·6%#]·FUÙg×SìÃC{´‘GΈ·¿ù ‚Æ¢© ³“øË×äꌚ0-R‚™r®ü3ƒ¸%D}:6…©S=˜™‰Ý8$ü¼+ŠÀ?俨’ÃøA³7攂ÀÂägqñj’@jW¯"ïã¿Óñ—ŹK hC 4'YÐÌœG¹kRwñªLüGm…?4=Ì~žäà±81¨k 0›WEåJÞbwÔ ‰#Ö×°vuѯsN¦["Å‘3çE»&!¼­FU¬ßx¶ É0 “±^Ýàê¦îmqäÙ³}¹zyû˜r~°h[Q æ5µÑ«Ý»LGÏœÿÂãì¥×'½ú0´OãºS&<ûoëØ«`ŠÓ xó=lš(8ê‰Á0‡ ”ÏuŽU~Z$R` fƒ@Ó¦ 0ýÔvØóýÚ£•4£¢ðòÏH“£Ê•©^ˆºX¶ô­ yÁß/gÏã¶CĺQ"tîbhA*Š1:ñ4’ð¥`µ8El×ݺo˜{iZ ®HðÓJyÉ!¥vƒÚ“;útó—n‡Õý¥¹ÛÀ¬âмËòÍë×F[rÞ'jäâ‹Çå½¼„ÑA~%¬–‘iÊ6f3 ¼;ÉÂLÆìì´Ôt×Щ3‘”œ&ˆOŒÖ¿ÚbÇÁcfa#æÔyqÿ°nÒ·¢1|‹¨¥£v"95]P;7Ú=šïuƒÆïƒW‰Ë0Ë£@Lh×´.Ìür„Ò-ð[:Aš~Lu`®E ÏµceL\Ü¡°¡8œò:®6 0[xÎïÿ ® í*‘ÂÝé±S0§W ûß ¢+ÔèØ ”ò¬ ¯{!á¯Ç^}ãå/ç¼þPDÃ<¨e:}Ie¥ˆèC<>söTƒ)û¿•ý}=¹³7V(oÓDzBVÚY¯f5ñâ„a†C'â tì >w1a,&cÁb>Šuü°ò„R9%ǼŒ´ô« ‰É<.f#ñ}%dÆ­gõÎÿˆêAR@‡0V' XÔ‚F@mý0žºwøbÑÑ+·Q·„,D÷áýû`¦Bƽz`̘ì+Ÿš#ß:®,ï>tÊ\%™ÿ ÐZLd¤€À¢2ŒÓg⻿bE§æuåùDDߢ” N;à¯ÁïÌLA… šEõîÐLŒìÕNúE¤)MÿÎ9²´Ú–±¾Ÿ7œá¯%38fÉ4 ×’M™WPQQæ?Ë&9ã7ß\m¬ß‘ž~éŠèÔ-Lôßar:4X š ÉÓ5Ç4sªX¡„`±f~üvè—C¡‚Ÿjêæ/ 3?†sÖÙ fa£.„ • §Ã_éf‚´´ôkx¾”6œ6æºRØ0LšþæóèÛwj^Ï4aTO¥Pn @h/OéùÝÒ0>þ÷jøêíP $â ¸:}ܤ`õ-@¬×P#0àq¨²=ïÔÅÀ‰D‡›Íë×/ýc¸ç‹7š %ýê‘—_óþúÝ7çâíœ6ù9sçÔXgJKI¾˜”šn ˜LLq‚سÿúQÞN&ú§ï)TUÍaâTÂÜ+-ÄÊ­·h !€@fþÇÛÄCÃo‡ÙI-ÑåŒäüKjmåï·B2idÜïÖ]j*.^µ/l¹_s•þý³4mQ‚W“©˜ñ¿Ex„ ‚O ˜tW_iŠÕ à¿ð, 7ÔrÌ~j,ž›Ó’ýGÎ"¤©I´jTÛÜ´ŽL¬Ú_•x©%Y²~·XÇoú”ôíØL:Å…Ã;’—?úEÖK :ÏÛ>¯ü;>ÿ}ƒ®ZY¼òð[Eížg³ß3RSÎçÔÒ…Ý{}qÊŒYýYçÜÙ3×YÔMìRícHMI¾ríMMË€F‰JDz4¡b Ž! «¸ "ú¤èѶ±Ú£ø ÎÜ ÀpÑ­Õ‘å*VÈaŸ»° l OæþSæ‚Ö{mɲ{Ìw\¶yorµààrÏã8«ÆZ5†9ôå\%l°Z5´ýÇÆôÉFú.ze¥Œ ²o ïNxaZôwÿy›zï‚>J¹ï\ñøÐ›ç‘~íÄåÊ•k‰AR?\Ña.~Å'Ç 0 ¿ƒ)þâ•&_çäOQ«nN™øŠª}Üˉûô‘˜}µê7¼—¦¦Ô¸rÏÍ´‡ö˜ÂÈÜéÍÅŸ#EÿË…½ÿ¼p#†‚2g2ß„2ÞÚz´×)ÊòÚÃÐ:( öäy„¥&£L”|òË1¸[ki–’hl‚Ú*@IDAT>_Â1{ü5è?¢ÞÓÃK I»¦÷«jäžÂÛ§¯Lf`ô»zÿùñRè8{IšZ‚³<#rÑñ‘Š(ä(‡am[YÙ¬'o8£ßd(lô•YWž‡òû›cÔÙãÇvàÅÀñnE#…¬©äÅŒ&ÃŒÜZÖijãø©63‡cöÕ¬[ÿ:Y3x@Yú휦PýÌZ/¶ÿ´];„€Âƺ‡D#˽Ú7…0ê šÌYänPë6I3Ë·ìã ˜Vòõ‘Ú> ീԔP;HÓD iü]–`ð³fGtHãVB.ž[v m'}æ§Q“!Æï•cÐ舚l×axè¡}À$|U»zeA†í¢ú•›ìcL8&oßy}ûަñ#¿n[ý®ÓÇÍÐéEx‡¢Òªö8²K}Œ!ßawŘU„·Ñ‹: MzGŸö†ÇÇxäÏß¾mˆºÉ­–•>—BÚË}öÛŽÂ>>mù'ppX ®z®VÐà3ýýàT{NšD­ÄÊ3£\uoÃ.¾E]Y÷‡¹ͼvÂù›¦ULDÈ(U¤#lP ꜽ=ñU”ò–u±¿MFãÕ]›þŽÆ5-MXuþoÆKnv rÇ–cÆìÌË‘‡O—*Úi¢ÍKd˜¹HAÇp-Ð;údœÔâQøˆ>%Þÿn¹xsÞÒ÷>bÒÄjòÝ}EÜÅ1‘§žyï{©‘ÂÀ Ô 1ÊÙ‹ðe¢ÉVYƒ»N3c·®Y…¶ó­I«ù`Þ[3&sËw¡ˆ'n,;ñÆ"÷(WÓïiØ]ÖdìëÒøŠÐ^½h 1À>?¤«ç¿¿_\·cË©bÃ’÷Q%‰™KÐéÃ#7ùï"Ò¹¯:uƒß ¡‡Ä¾ÉiCóz~}ûvŸß°q´9jî)ÍC¶Oµ‘ÚrY§^íáé9Ž>m4%¼•aDiÊ{ñŠ -ü“›#PñÍˈË@D ú'Ì¿X/Æ1²j" niûf€†Ø“'þD$ `¯è—ôá è”í✜½'â×î­ëÓn,ÇÆÈZYˆ6ŒèÙVp³ œÜèkDóº9MK§oú>ÑLnËÞ#¢Qêò›|ó‰1R`eôÙÉC>íexè{‡tÍÍÅB?ªmIîÍn¿;†ü;pŽ7Äž8¹%p#8FíJ¼9Ï.ÑÖïY®¼×³°·4Ýêƒm‰0YÆnf_³Ï+x{OEÓi\ϯђÞtú(cýê¨æ’> ;vô|åݽl4nØ:8ªz=n?_ß;~üæS¥Ý¸±Tí¾­æ„MAC [–/]mÌÊJ@ØL#Í™ne CÖªQ-™°5ü/J*h(\Ö€#<}1¸q»´¬YØ¿ìgäó¹ö×’%hŸ6HŠ™SÍ.ͽ6fnYõ×Öùÿ›ûEfffzi6ÌÏf¤1FÇjSõÑÿ˜Ü5P‹fI:}µ7Å]… –}†ô¼|-íÍ㱜=VÝ8½™^‚&s•ƒk½Ðµï0r\¬°?ÜéuÕdM‰‚Lf¶ô””¤ëa¾§©ƒg¸Sƒõ¶8ì_ösdø–Ï’’®£vÒi4AaÃú_K§hÛ˜Êýæ-áño¿÷Éð=p'ÁM+>´Eî”AðZ¶yŸøhÁD¥Š–Úζø5—Ý;¿[ºÅ„\6¦Ö¼õÒ¥$¼‰ŠIN£QgšQqbð,ï]q0ˆ×Ä<e·{ô–ès:M™|üãþ]ØÔ  Z§""U%Çb²,K8ä[4#(K1¿ C^^}@*&Äô¿¥ÇeHºÅ¤c\¹½•à°6íÐþxø†ïâ½9~8m2t^Ù6¶Q1qŒ¡šºgËÆ=Un»íß%óS-RР`Ä$$åÞxë?»êׯøÐýcïÀï›b‡Yç¹é Ö9Õˆ  '¢þ¹mËàDK£Šó¯OÌœÓË(<®}>ë•}%Á¡3… i"ƒÆÕC|b#¢G”-£¶’`U¿Wb€CØ÷©ÉÉ!8AcH¼$hnnAt KHJ¯?~âÌç¸ibÎ|dl§‡öZ…,DU9tâÂéÕ¶zÝ‘']ƒ`MØXºi@)1ùîþŽ|¤Së* }@Âhèï“ “ gŽUE~ÏÓç.‹9_/•÷Ñ™´Z•J-Z±þÛ*|dQè†ô¸tã$_«tË >Ë›2RSëÑìÅ´å›eG”þ?®slS“º1«]…u‹]‰iœóç._5ÒÍC7+6Ê8ÐGƒ¦SÔh€ûuíï h>Eæ}¯9ÒD©h `R».C-<_ Z:UÂçg~cž{~ÍŽ“‰ªÕ¼{`gÂDgÒV®»'h"÷ÑÇó¾[ôå÷.Ÿ?‡V*:¥éµ¤‹|ck¶)û“0®ÁµÇ°œ9s`õKYáøn ¹ØX¾…odß_¼r%(Pƒ‰šàVô±pÍv1 ®Š ʼn%v»Dذ׶‡ïìee˜°w‡{\+€><1YVEb·?»«4b/\K‘Têàñ81ó±; ŒfSº1äŸÜ£ØŠÀ_Cbâ52K?8vqË7!âœ;ÛÅö±d4ÉpÒZŽ}ë–üºêòùsç;õî?Á3èÓFSBjø(xëP60À< o˨St7fe&ElÙ8OØÆp¼ÍR¸)a£TiöóY3^·‚U-RØàB07޳œ— [Ö®]Ô±W¿û?Y°¦j‹µ@§õ Í·¥ òu(¸v]+˜(Ó¸'òÀ¾ç^x}1LåbqŠô™ˆí6E§\ÈÉ7¶>5}vÝ “))rh™R"p–°A‚%ábÍÏïb¹rü­Ã-ˆö=bïQe u'm¸ }0©cqï?zÖªÐÀxñ qǘð^ˆÑÝ¿s Ä‹o…ÐÄOHŽÅÌ´Ó?ù Yxk‰ë©i"!±"$ÍšÞüâ™uwÊØ~²÷1x‹ˆù=4â/'ŠŸqÿI¬’WAFÝ‘½Û‰N-êËr¬—Nl—aúËŽOÝ;@žWÿøÌO~Y‹ì¨µ*Tü²*\fQ}pDDà8#6îŽm׫·@ˆ¿ò¢/’U©„Vg⯈•[#Ec:¸1}ABµQ=Ã{ôÁñ‚›§A|Ë{y‘VÜo=¸ct&–š…þÞ´'ÉÁš j?¢âÌù+ Ä|o„~hn•n˜0ÍVyõâ®$‰·¾úS\MtlVOÜ7´›j¨%®8tòœ ùÙ¶qDåé$C;Rk÷ëšH~vñà}D$Cc¦e[4­žå.{ïòåA†´G;~”Ê*q!qBFŽ7… ίj“4¼o[XÄá½{Žt2¼ovvVo0«þ45­ä]ÁT‚•>Wcn i™™&f„gÂ>h 迤S1Ñë·­Z¶:%åzšMÎGšp7à7D:媶šÕ¸kØ·=lwÔî‡{ Þï90êx¬L/ òÙÿœ8ÔäçímæÉÔ~¿l‹Ìgbù’ eüžV^^ajO'à'9-ÃtùG23³ ^é×/}{SØöx<•¦}ÔfPÈÐÒ)i€´o\…ôÑç1ˆymæ¾$`&˜’Tbã^°—Iè ¸làç–9 +by&]üvú £F0eᢲjc´³“6E‘+YGõ‰É)âû¿¶ˆöMCDP€/ÂçÝ&……‰£n— ‚˜Ít÷¡RØ8wY$§rì keIa€+ß½:4ƒÉ“Q|²`-V4Ë‹‡ ñÿrñFqf…Á¶2 +å{D€Ÿ·Ñ«­`Äd?]¸VT€  Âþ±<‚fË"™™¨/V¤ #,ÃMòù¿ £o¼#³nÿML%_oñöÓãäy†œ4¦ªÞå{ô¡&>—·§¸dÈÍÈ<| ÑNþÀq{-ö9#™þ.Hîfnì•WQR(Ä0Y3@¯‹- ÜvhV¢§¤™Þ›BˆÈóÚ‘‚ZQؼˆ(,O$ÎC¸-ç•3éÛ¢i&ls7cDZB«%m¸#GÔñ#TÚ .-*FN]ËJMM΄–c9N¬iÒº]½ºMš5óö­XÁÇÛßËÓKWqSnYÙYéé©©‰©×¯_AÂÆ¨˜Èˆãh¦Ò`©Õb &×JøýN­¯™`u“½qéâUØ6ÔkÒ¢fݦM›œ«T­FÈ´¿`Mæ”å UkÔôï?zÜ=žžžyiBtVffê´gžù9)1‘x’ —ÏE„£ñãåUL€¼,¦2’“âNŽ<¼7➨ Ò¦Ú¨Õ *óö|cªÑ`š„ëg?yóå¨Og½‚Ã⃳„ Å(x€y¬Ã-)p’Ö8ñ’nìÝ€>²²ân¬¿6÷w±vG´-óôÖ0ëÔTª$7:a3IÐî­¥Z9îR‚9†~3++¶DJ†ðЉX)¼0yÕáÓçÍìQÓº5ÄÑ3°B$3íò75{cN‹­‘G%ãȤ¥g‰ÙOϳ*Ä̶ßþ&¯=3~°]s;û¶—>' IwêPѶIìÏ‹û‡u“‚Gãêò¹žÐ“–ä£5f0»G‚qîVzm+Ú“ý}*JA‘w1›ó€.Íel÷6êH¿‹èñR»@s-ÝØ+ß¾YˆlDÇæõ‘·‰<>x,V†&ƒÂa`×–R“Fš¢9¯SØ à|%ñ:4dÉ¢K«†²,ÿÙ£is!79°6ÔØ‘obt“æ²d.Õª±¢euŽ;W½±U8¼o¶ó½ÔG¨îÁ)ÜŠÖ؇ܔ0É~$³ÆÕb2p\1枌ÍÕb\sà{)ƒ4§¥;^# “^+ž<•ŒíXn^S81\ŠKZ8÷ÃqN}›¬G•Q8S¿õò9xVøp4~€z‰{Ö«|Ç” A:U~Vœ©]H@ÜoBMt¡:Õ«HÆPÕJ­‰VýÌó4•I¶„š˜‚²íÖ…†„à盳à”¡Eƒ`„<$Ûµ)"FtkÓHž/­6èC6ç‡Þ{gþœ3—VÛŠúÜ«0[jR7XÞ¶ÿÈYžûÄù+×ÌÁ°:j³Ê‚ÊCÈU@âD®…šŽŸVlž•aŽwõZŠ`†[ÂðÛÛ"aU–˜·h=Ìòªˆ‡ß“¹À"Ñ´zfiì«ù‹]©)gñlÎUÊ®¼4šR”grbVLïãoNæJ!ƒêƒMFÂ^ûnú| „¸!¨>d¿²•_r®«}KACi5J̤¡®Ãäo½ŒqvˆW¹*wü/ô)2œ ­’é$ð·ö߇‚1U¢œ³µ‚~Jày~›Üx][†øâ¦efõòyñì(ühû‡4Ê>eÿ)Úä^+d(eßä£S¤¬˜®<ìÙãk\/1ps¨Síñ ½Î²…õ!°Õ<&¸}Ѓ™E­Û%6+þ4SéÛ±Yn¶Pžµ ,K!áD¬ÌÐ)ÙCúE¬‡Oˆ7„ŒæÐ|½ˆF”šÂyØäk#XKä GÒ`ßÿåâ ¢5´-4²ôǰª>ûm̺;ïÜ«înZúp+º(,n(Ò¯b|˜†Iæõƒe†ã$˜¹½ôá/6«*Lù‹ <®Y­²üùwx”8Z°±R}ÿ»ª˜LœF3½þ›K»jšêÍ~êî"Ñ´¹²R8x`x1aì½àѤE#e>ÃF¬©c%lp—š ìÉÄqS‚”z7µÇ%JŠ ãžLC2iÜ”fƒ+Äjãyö3Ë©{qXÊ@ßl1++ëꯡ¡ëG…†öc(údÛ ê7ß´Ê• %làPâA½é”ߥ¥ÀÌë _|–bhõò7„1GãG‘Ä=qNaƒtÉ>¤ÐÁc%kûDõ%.kÁ´MÝcA+ÚE:¶Î© ›…IT7Õ€ù7Ll¸ZÌ4÷Ž„ôŒL¹JÉh7%Ü0•ÒÒ„öØ-^›Îßôqøü÷uXõ¿Ñ$úg„<@MÑfGñ—eÖPÚ·Ó—bSD4V““¥PÁåf`07 yW™Y†LÿË·B€ñ„cxY1µ~>Ò¦žÎ¼Ž•!x)@Ø®h3ôí™øÖÒ9½!œÍƒüìÝ’ïáá¾^í›Bsâ)Û•¯+OX§W¶ ØÏ¢“?ýq˜•v9ü(êÕ¬&ºµn$¨Á¸ÿÒ¯¯Þ¾?Ï3,醂¥½ò¼yߑӢ=ú>)9ãÅEÑfRÒž?h êné«C G›Æ!òµ%U+ûIÁ¤)4.Ÿ•çíÑ´,à^ÿÔX¡ù*Ý«6ZÃI\1бÐ2–‚†¦lT§Ÿ.E (æYõ£8ØŸÜ´BËØ`àJç >súßOŸ= Úoã²¶üðdè§ÙÐp¨÷$Ýò½È¤j…a¾Ë¨÷SߦÒl† ª ëáFœôò9xp~ÔøÁº‰oÒ$û†¢S jLR}ˆSùaÞ왟æ?[ü3Î6r[Uv̨¯Û çÞ“råÏJׄ ZI… ®~ÒÉW%è¢ÓðWK6Š^¼_®¨%¿µvºé9Œ‚{Ñíã›B苹’‚±ˆ,Åhü´ZÚÅÓ|iÊØþR D´!Ó>þѨjÃw Ô^–Tô' .Ãv¾A­Ûdµ4µbÙoþÜ,^ûìw)¼ÐÙ›O £ê œÀ¿^²I¼8aXan1—!Ý-Y¿[F­¢™˜ü ‰ª¤tm~@ñ´ô¡&$îÕäU¼Z|ñ_¡¼—¤j†ôh%Uöï`øô¬‚ƒ6…>›H§mÕktc¯<ïëÒ²¡øèçÕð×É”4Ê:}iŒ~<Ïþëk`—–âÂUšŽ  üÀ1‘•e” ï‚`K°GÓ²€{ýS“¢Ú»Wëì·†mV“;Nöd¸Ê¨] VŒ‚ö;@ÜìGnìCnìSŸq¯Îsï–0ï­ó'OŸd4‰÷²2¯v2cÖÃsgÏÜ i,Û®}OÒ)UK­Ë¸Wc4éWÑ0¯T}ŠþsÎæ”ÓË;?ìK6â^Ñ(éSѪêÕ.Û»@ØpÙ»”™-Û¼GTC(L%lÐɳÕÿå|‰âÄà/3/ïf } &%–ðüCòœ"3þĸÎR°Ï€=|E³æƒQ^yx„ô£P> …:wúÄytžßüÁж3'Ý)™Gúfhµ)ÖÂÐjÏѼkú£LòšÚkôçঀ¦]ª=ûŸF˜ß(1gêXˆZµÏ½ˆæc¥,l¨æª½œÄ†¨? €îŒ¦pj«]‡ÀL>;ô×Ñ‚5º±W^=kÂÖ²Ïè÷£€ýFÓ( !Úó¼ÎºÜ,¯Ù£iU¯í9AªIR1CnÔ¼B5E½éšL€bÌ´L¯éà¾P´§ú’¿µÇîÛòÜ–}þÖŒÿ<>s΄Fü6ÛdX‡ãîóf½®i8߉ôÉ÷➇¢UEŸ ¸dÖTðšvSe´{–'hË©cm9uœSZ/O)œh÷¶ð£-£ú’çØ§yàñwÞ ðHξ+ÛhºÓ”aÞìƒòpà]ذƒLFïùiÅVäQH·Ãd1Ðåʶº…~¸jÉœ4±¡-=2Kó2rqHôu«Ï a“Ïð¥4±X¶OìŒ:!·í;а˜Ý„Û šôÍ'Æ Œê%y¿6wÃE¬RÖ¬ Æ ê*}ÙJ™c‰ÜÜŒAT¨¹)÷dɤYö{qÁ’A,n=…½ÏmÆU8¨C¥.s@ЈôéF & êuê=ÅoÆÝ„Â⊦yöÀ’n *OÔ½Ø:ÏçÛºf¦íµÛU×NÂ\¬sÿ!w®[‡gr²ä¤Y–í'ê›æÑk’ñ)vD×2çF|/ÇlŽÙÆŸç½ýú—¸ÆoCÂcÓÞ˜Ó¹ñê·y¯—w&~ýq¯ŽÍ¨/ .BC¿iŸ7ÞßÏ3JÛæ'Cß«‘•™ñ¥0˜òÙæ"ÖOBÅrY> ¥ÚT¾û㯾Œ²_ ìº,¯MuÀòžÆä óÞ}W9›™ôò7,9œ…È ²o ÁJô©`|ý¥ð/4ý[}XÇ””Y•ƒ†•ÃèÒä¡“þé†Äþ°l«tê½cX±í³$‚Y›¿4L: Sˆ`r¶MH¾6´Gkú’Q~¦rœ3™Yx¦§î[ê:2r»·i(‚—ÿ"ÑĪÕü ‰ºzû$êê,WHwGŸŒ,T?×±XµIßë(4ÓŠG$­e›÷æ’àSà+iX…T-è~^W+¾tžÖáÖÄÀ’õ¢EûŽ“ l¬ÊÔ*²3 {úéòµü«ý#Þè9]xCXÎÏ·bvƒšÕ ÐZ‘îóò³ôùlæ™¹Ú a˜o˜×Çèå…p4~æ¾væ#Lѱ8…¬~ëf~¶Ä9l®Bª}8oòdjÊ „†>œ†Æ~[Ô{{{s46È^åË!—F†0 Ä6A1º<3mª\90Ï^þFYgãÇø÷F²Pö…8…ÕVtÛdÃûûó묔qè)]ذN:]^DÆç`RCÇmF•anÌ6ˆXù*Î}«FµÅ\§°AðaÔ DR!\BŒû?6DàãË1¡YK Â5É¿ˆEæÏ2?²èŠ?7Fˆ(%­sŸÓ 1÷K77‚|5ý_ÄÀî­#¤•;·\­Þª½;·Uo›1€ð‹4C£zHË´Øš@Ø’WMZöxhê‹!>•«lv1¸^pUS?øù4@°Ì%öU`Vß‹YÜ zyû˜*?˜¿Ë£†Z ÇZ ÊAm›Ö1­ ?hð8{éñG^}cò×s^߈ë¤Ó2 ÿ })YØÐËÛÇTYÇý·+ÚU]ذ¯ã±å•ØÕ(<UÎ WÊßÔF$^Oo½Œ?s@Ã… –½?Ÿ Ò!œf+´ß.*0Dóúµ¤v…±öcaž¥™¢Ö¥—×1 c@Ç@Å…Œ²(h(-]¹g'ŽpðÄ…ª[Ö§i`Ñ'ƒ2Úq7c³dÁÀEÈ«ÂÂÔyÝc¯¾1õË9¯Sû¤ŒoÆW×ßIÇ@‘1  6P¦h]J¸.B`Oçàk.T¬P^j è˜i ¡Æ‘нm#±`åvQ ÂéªÜ Ž|†^—Ž7Á€b(ÕÞMš¥7£1@Æ\»•Ú  A­E¹K±‘OÎiߢ‘US©RÄ­þèbb€_ŸìñÕâM¦=1§?~ä•ÐÌ¯ß ýö®0Š¢ Ï^BzïMz/RE)ÒTE°w¥Ø°LÐSB±û‹…bG±‹t¤†Þ{ïÐ!„Ô+ûß$6Çår!¹äöé²{»³S޼̾7¯}ƒêh _Phô Go¾fbÀ{ ˜‹^&¸ªR¾´(VT@M*Τj>rl0\­†<}>N0÷Fr bêCÙ{ä„zìñÌût|d„F–q>?ƒºiÊlÑ„V°µ§#9ó7´Ã®˜ þ‰æT ¹\N€‚-CÒ^å@,æ“W9 Éð©ÆP”v€×ØW)hìÙu h·ÍonA™=/ûI³è'úwњׯ&àáËŸÞ¯rž ­z9Z³˜‰+Ã@în¿_Yüö­~][‹_æ¬Fô¨ýðͨ•ÁÇ‚ŽÞ|þÏâ â×y«¥y3P{ª ²Qÿ -Åóü$ê{ÝeIÕÜÅàg”f‹^¼n§hßì¿ÅYAêXøç¿K¡Ž}¦©Zuhúui-CÒªq°LÛÆµÄíÝøýH…à³bË>1æÙêûí’‰GNœ•yj#hÀà;»ŠÙ˶ ×ÂYަxL yW¯v20@úˆ ´/ü‹¬ãÝaw]‘Ù¡º,/]s¾dùBÞP‚†399iS­*åÚæE³‡cΈÑßLGı`ñÞs÷ IÞ%^áOd•gÎ F¥{³W+ìDbødÝÊ7x‘\È´+Žã4Ú´éÚçî^wש]óCSÐð uøC­ôŸÜ¿«¶tÞäã‡:'§æPáN¢¿Ó©? ÏìÃU€SØ0LòÝÛÈÈ<êVç–õE§æõ ÑpHæOÝWç›®k.xÄÅ'‰¢A‚;cÎþîဇ‚– jÈäm‰IÉ2T.ïó"¸‹ÁÏ2vôƒ‘¯* G‡ ¹ƒ2ÝÛ5gcãÅ"r£¾ž.^{äf‰go[`d }oh)ž¸£+4V)2ÃsLídž½»§8vê¢@mDt² ø0us[ýfd|fræó cר6#Ôù\s¾ø®¥l×LÕŽsʧï¿;yì‘'²ýö¾@Ÿ*Ä»eÄýÚíÓÿV¯°êÿ“Žÿmxý‘¾¢RpÉ<OŲ%ÄÚ„Ä£h¬ i6¤ >5íÒ±É‘ \ë±Ù`BáÅ¿ÿÝ®mX4¡IÍáåC8¸†©“Â;psd&¼À€)ld$înfåAs«ìýÄ]cì»Ö¡žsÇu÷áØMß+\ι¾cþÎ8wL®ÈƒÅ"¿ú[üþß:ñÒƒ}¼®èOdãfÄ2&eSÀ¤o -ém$À,î×yk¤0¡„UŽçÕÈöÜôšªxn“a-ÂÆòM{¤öäèɳ¢˜L,¨‰›;7×A(έœ/ÍêU3v'¿®Õn ÏyjOÆ - µ\¿ã`º wàØi‘sÇ:U˥ヌ÷Ó—‰¹FðþÈÎÍ|›v‘ÈæÀå\hCèßÕY¾UFyþ=Oƒ6”Z°ŠØ8謮×6’u_É«öש*æ!D6µ ·wk-MÎ^¾EjjzBëJÍ(Ú³isW‹ »ɲÝÛ6ÔÊrìÐä èÑNÌZ¾Išq¶F·êâÎí¸©ó¥éçÇSæˆ@ü…ľÂxhÀÝŸ 2ðÆÃ×M_iýé‚*(Z4¬èc×T«¤CÐà}® „…†öûmʤ:î´Ãåú•šÊà*»9D™a€»E&ø9h:µŒÁC};g0åòón¸îQl…lî‡bR#‘©D#‰âêmûÓcø­ öb‚ô×i„¤ŽÞÀ!0šÜ=F‚HW  ²uï1h½ª j¿jYù ñÙOÈåÒ !kG>u§Ü.c *çKçVõÄÐÝe´2æ|!\€Öí/C˜™ó…þGÌùB ) µ(­ÔÜÖy[.1Ó²@þþ“o;‚Í€8{Jæ›(X·f”À‘DzN¸‹IrLå¹µ³X³íæë |š‚8Û‘Ìså–ý’q§éÜ/HÚI-B do&þ•‡n-0‡>¹ó}¥s¬ÚãÁ$¡ ÉýÍ_KÄr$ Á¡4Âi³ KÖï[@gÜzƒ¸õ†VâÿÖÊ0ß|Î|??ÍZ&Ú7½Fš.Q˜¡°ÁJ„;`RHzÉC £^P˜u~S©â.róý^çtju L”¾çá”Þ¦h.שCë»0Bnæ’Ìù/¼ÓmŽÌK ˜š /•ŸÅ®fñ¼Æ{qdO±92h˜ßdï‘“é]IÁÎ0ËN AáÄÊÏ âažÌßA1ô‘è{}Ë þê½µØM/¡ !L§ìöT'qšUµiTKì=zRFDãîu‘ @Qµ|)‘„À_å|Qýʇ³b.Õ9O»P¿FEi:¹ûÐq™¹œóò0}j)ŒÐ£})0P5cÛöƒ–¢^z‘~][Ií#ÇQó´ õQ¤YÝíÉàÍ‘Üs:ü¾v8-ƒåŠç˜ÞÖ¥•¨_³’°;bÏáãÒÄ“i±°`ñÑäÙâøéXQ ¡³·î=A³š¨X¶¸Múlð<%xòìÏ@z%sIûɰâ%¤sOK÷(¨°´O×µkÔ!äK¿÷ÄœïwËΜñã·ÐS»‘/³`6êOð¹°áÐñI6?\ˆM¸Ê0”bÓNG|fÃö7ú – ¤hdŽOõ·`¿{€ñrç ÎgÜ¥&$$¦È³ût™ù½Gû¦’ÉcâFwÀlÀLbFÆ@ÿdÞ£°A‡söé·ùk¤ûD>{öžž²\^æ|‘ æâ?èC2n·Üÿhg˜4Ù[7ªåóµJ + À"Í¢ÈhñšÑÁh&g6¨Ñš2k¥Øwô²°‡Âä(Aj‰TyùþËî«ÔØéº3O… ? Z¡PâÕyîÊ- +T©ö ?Žƒ4¬è׫÷ÍB& |þwÚ'ÏÇÆ›ÂFa£/ÇsþB¼nKI9‹ânw%ý‰>ÈT®Âǵ®-…ë°Ë– “&J[°[œ™¿CñÐñ0Lm<Í›:™;ÈÌ O(Q¬¨ fƒæ5ô-éܲžôÝ †fcؽ½Òµ)yóÅÓ8²û,ú  !…²*õ:w•a#»Uç¨|ëF5aN´Rš¼1k2ÎÿVmGŽŸ‘O ~ü0ë²ö¨rlK &{åá›ep‰Wÿ÷‹,–“9fAA©*T›Úå<.Í¡hÖñÃÜÁ&¸ë·±\^^¯Ùº?¡AóVåvmÞvýa#ÍJ*œC«•)UŒ÷ò®‡¹eSä„ ¼÷ý l 4ü›2F\Ë÷N¦uÀƒVx¢Ð¢E-ˆØLu7Oî2˜Ú ¯±g,Œ¸ük˜{£ä—K·%'GÇ%&kf4ŽÜClA©‰sιOIL8‘ÖgIÆëü¦ú4Œ>-N1g rªP³ñìÝ=¼FqP` èçÞ9pÌe†yú?@“#èçA„·ÀÄà"Aâ8ÄÒÁ—À(gwý"ÖÁÄ…‚ÆBäXÐãZøV”%ÃBÓ«¦iβ»eÎFPcî–“ç.x†Ù˜ó…>+ìC^AôA&M»»584tÀYø”)Y,¯º&šÀIŸxŒ‚ÏÂs÷÷º¬]æÀ)Q,ÌšEúgPÃѼ^ËʹޠãB|‚Ô†PÓ€Ðô"dö}1Çé ¤]´jPBõ>Ñ~Fua2Fóª"TÊ~=£àQè¢f¤„_j]…0Oïg÷éÿÛéQÅ·ißÂÍQŒŽìVçëò¤W£f£hB|ÜñƵ«ø…°QÚ7 ÿ•.†ˆ{©¬ÌÈÂ.ÿdˆ"»A Hs?Î\Ž gdtÅ{ût”‰jgŸlÍöÔµp|ŽèƒÄüRË7í×·j(}ŸêV¯ hâxÿÍAÃUe^œkhøã·ú”&]БéÏ"Mשö¦°ÁdY¦v#Sl™® \¾õ•;£NÝ"ƒ°qâèá(|D5:$špua€sι?ºßjŒÜ¸+ÉkB¾Ó£ÿp§ïŸEDy0[o é—m{ô¾°•ïݱ™Ì§ñæ—¿‹1ÈÕ°lãžÔzù/£P1¢‘4øwºi¾CS*j]”£ñ»ßͯ›&sBÐ,Ƙóå¹÷¯}:MšêxÓ4s¾9~Næ|¡ÙP^‚úPë‡8qô(™Liö“—}£Ù ÑÉŠaêU¯tYÓ]]ê"|qˆïo”;Å—rsƒõö’´²U¼ôÑTi¯˜>_ͱk7ôh+jV*+>™2W<=æñÁäYàZì²ß &@#Y½üÉÔt‡÷Ë æÒ»¥“¾vhß®å¨Ò(h¤ÓG.5•[ÕP°àN7ñ‚þøê‹ŸúßxmnÕí³z< 06ê)EvƒLž±L†‹î¾Þð?+Ðá+dSî‚V°nšŠN™µ85×,†š§p¡`%òїȗ¯jËÛ3ÌÏ”–‹ôà+^ËÛî˜åL ä+|©Ù Cé\ôï[ë6oyfͶýeÚ7­ã»<ùŠñ«¨qÌ9Ö[çùµKþÛIZ0ÄB¾ÓǨ§ïd?<‚»2·!™#4Ma‚G4y A<}j ®¹[Rï^þïˆAý.¿‰;ÏÝ—º«þÍßKEYììÓ|Š‘¥è”üÖø?±«-™âÜÌùâ¶#>¸™}¡t®Y°pwã6מ^²~wYä¢ñéúÁèRãÃIé þ]Ó¯y¡æ‚×LÞ úa„)Ú½9sŒysh¶d¬—!’{ÛŒ}B\é»¶ÇpÉÆöª€™3þ.?Œ'Þ(Xæs “a”S{a,Ç;OÝu£êž<ßÝ»}šù•&µ!æò ;éN‡=fý’…”Ü•°¡6*r¹µ\©ŽôɃ“JF“‡ßCf¸b„¬Q°¬7A ¸‰²ÿØ)ä£êŽ@Á¢Cóºâ“ŸæÈ«Õ„€/e y©Wã’Ï wÜÒ&ýïªcóúâߥÄØ„aµh’ïíÓÁØÝ|¿†B†´ÀÅt@šào”Ñ5L ø¾6ø%™œí§c¢ÞªiÏ0BŠqñݰ̚óœë­{jÇø}¡ÍªQL†¡PÒ‡Êâ üÓÁø<̼˜ã9ȬVBÒ3#ðCž] `äË~»ë·ô‘’g;qøÐ$|§‡3œ¬2›pWg~Üs4¼íƒQƒe|ÇWsllC]SСà‘]È Ÿîdc¾µS1Ñt†Q‚†ú®d·Ëy]žL%¿o¨Adˆ‚å¼ R°ëàq©Ñû~z”ªZ&<²ALÒ6~!ßü½D •Ë•/=ÐG”/QØÈF•ùV4›ô‘2ÿ÷ßþ¸Ø Ï[¹­,„B»x¦9Î q‘>`ÏïtÚm'þûmÚ4ÔEaC 9©:¯ÞU)yÕÞ·ã)`€k¥Y¢`yo‚ Ne;ž‡ÖÖ˜üÔµ=×ßAi¾l¼_ ‘æõ«Á\ôˆLŽÙ.-Œë;ùõÛù^ ž¦°‘_“a¶ëð•°¡[~ lçμ°sãÚ÷µÖíFÿ0=J¢óÏ/¦ß7às·ióªe_ÆÅ¿ˆV’H8Ã@Aƒ‡I@‚7ÀDmwõjïMQ¿/“ ú pš’{qÓšåoT-Õ¥´9wÁè’í‘ßÖû¦9ö~Ô——\±eóÌù‹Úº¨ÅŸÅÇÇ&¢„Ú¤àšÁo‹¿‚úîÉõ­}·žMv!Ã|ø]ù3d0Àu##'(Œã¯Sµ‚Ô¨ÍDøçð*Ž` :ÐA®A+Œï¯;Á”оç í} Ký Ò2ßìO}2ûbb ?1à+aƒcâÂ+™œ“¢fM_Z¢t™oqý¸øCèÝÚY35ÀF!îH’‘\»ã€vp×ö©«Σc8=O)l0éA1 &}Wd“>Ȱ‘^É%aõs×á8زn¥¤ÛnéýPa8®&zp7V“Îý;¶ÏX·dÁŽËoN›µvø³°Áõ,ÃQ¯E댮äïÂÐa›ðw%£ŒÑŸÂUØ0¢øK;Í8™’þKÙš¹³+Ú\.Â?ÿ]¾ÊHgM†ö—¾nt‡f\­x¨ïu¨Ñ0¶ÃˆqvȨ‚§ôKòCPôªè»hvÉÄ@Þ`À—ÂÿиsÍ™Íø™S¾ÿ¹Ï=°ÍcΜÓÖÎbúp…hƒOÓ)j4ìÜöëüß§Ò|ŠZ &ôS†ÒlpÄ&} W \}ð­Öîn“–ŠÜ>àñ_?|÷­³Ï<ùÈSÙw8¸Jð]‡i³Ù’Ç~ðùdëÈW¡ÿçqpíàÜ… ü,€³ˆ~ê}ý|øC:å)`ÀÐÝ2”Í,Evƒð»ÿΓýŒIT™õ¾²®'"tºò%3\Pbˆqâ·=rù)pýRß )]¨aå}P ¥vƒë×*Fv)òÒko/·0êt½Ž=Þ®U¥| ˜ Š‘aA9üŒÜF_„üuÑ“6¿ðÒ[.]¶*ÝæÚq!íÌõƒÚ-®¤µ~àÒ¯Àh›Ÿv-„ýª“ž:“€WˆÂ]Û™ýÍz ZÁÑ»ÅÈðâE‘¨k›Fîªö‡{\»H³êðWÚõ\™}(äð¥°AÔñËÈ,°½À5‹æGm_³jg§›úöu8ì½À¨–€JV/¬—*¦ 2}:ˆ=?…$›MgVx&ìc ÌaÜ¡];®˜3cnBÂEîHƦd”°A:p]lMúR ä"}(aƒëw¸•À4{ö»aÿˆ¿¦û΃1å¿ŸŽˆZEƒÈ讕)QL{¬ßõ^¸˜(ME’SH†ÉéÌòy‡Ÿd›]?1AÇœÈõÃnK>ýí{‘0+œc®\7¸†Pè &… ŸáÒotJ"úÖo;Z;Æd˜Ó`ÆU½bY˜cuóyæ+ÁÕÁèS¢CÏ›¬œ7óÞWß=u¾’*ÍwL h øZØ ³@5"w¥ø!Q ±ˆsÎûíç_qofÃmêרנiHX±²EBŠ– 0Í#€¿dANNNLŒM¼xñìá=»¶ïÚ¼~?:«´WdÈ(Pèà5ç]i5HF0éÈBr‹ôAŒðm4§¢vƒë–eîïS¸~Ìjتmƒjuê6 +Zò¯>|ذ)q±±´Ñ»Ìå*U)Ñýö‚qwµô¿ÝfKL+O¦Ö,ïüþÿ‡#9%9éBŸ“‡÷îÙºg³Ì€I¡‚‚®8h:Çõƒóȵ"}Îpío éR‚F`Zžëgï7¬Cîðëqüµp½hмåS6– £¤[¦]¿Æ¥Ù¹Â.9‚Á#±‚‰‘#ºòìØÃU’I u(Žâ8³“çb8Bp `–MJpã>¢rjÁä‡ß((I#S@æ@1 <+çp%l¨÷ñ(LúHGE¿Pó››ôA¤F(`pàúÁµC\C¸~ÁÁµƒe‡ZKxV÷ÙOªŸê·z®ÞQ¿Õs³|*s‚ ]âš \7x… uÍ ®-™šPyñýÁë¹nÚ%NH‡¤Ç28*=ðÜkãZ4®ÛøÅûà§ W>š<[lÛspýwŒº ã>‡ƒB4ék‰74¤™g…¾ÖlaüãâΤþæG›Ť’  ¢v-•ÀÁ½ þ…5œSÎ!2ŠAP»“d £À²é‹,®`Ò‡ÿ:·éƒQ4¢4¼GšR´§„ ®gJ@àY1Å\OŒ‚ëSu²®G øŽY>÷ñ£æBÑçN­ÿ\;ÔÁµÄ(d¨ùá{þ ê[¥èÏ{n_­*åûs§Í¾ùiš-E÷Š>|× Y³‰?Æ@^¾ú°¯•°ÁŒQ³aÔn¨?Puæû&äÔÇ^1 ŠÙ32 ŠQà™÷9Ç,§ÞÅ¥[0éÃ-Z ÔM5Ǿ "BшÚ¤€@£P«´¢ü°§3{i׸Îñ¬žûHe]¼ÇwYÆ,ŸŠþíæ6~îÕºÁµ‚s¨ ίZ;8/jnpY`@~¯þþnâÔ?¾qkéµÙÑ\Æ€ `\wH¤L \uÈ+aƒˆå™b<ùá5?,üÈPà a4c€Û&ø³ æŒÎ¥‘Qàüf‡Q0é+àKú =‘ùUmî¸~× %l¨3×¥©à5ï+zStÊûê³¼oðCüÔ¢œ?dÖÈ ¨³ºÏsvÁ¤ìbÌÿÊç}(z#£êº{H¬(Ágãs>#>ô&oâŨ5HÝ7˧bâJñ£æƒµ(¼s•€ÁkµvðyA#í«ñÄqdèó7/exsñâ}Dýš•2<óÅD—µ*—LþW@ѽ:‚!™C01} äµ°¡z¨>*ü䢬>ìêCfþa*LùçÙøQUL˜šÓÜ`T]&}øçügÕ«¼ ötBAW­<+0Þs½Vý3ž=½g,§®Íò—ð®pboÜ}y,‚ÄÍ[ˆg,÷öé ˜üpäøY1{ùf±}4ÊŠ@ܯ^©¬`Öñã§cÅ3—‹#'Έ eJཎ¢NÕòÈø­‹ˆÏz´³–oç.Ä‹6 k‰~ÝZ‹‹EŒ›:_$%ÛÄÇSæˆ@üõÌÙVAûçþ›;‰‡Üý1ú­hÝ/‡0mÚ´€…[öµt g¬m„¦3ÊŸ[‚Ŭÿ4>rÄ$ã@†Œ5X×÷ï©k³¼~‹]»Â<†ð‡µ@1ßÎõjÞ|uÎ7aÃjý¶è1Û±g,íE»Ó^Y³€ÊC‹:K•eR¿ÔÕÌW£6ëÍ´ÍyþB‚—dqêúHüAÅÀ¦÷ãjEª³Z¥I\ŽÁ¤£0ß*È úxÊúy1›ýüãAb¨ÍnoX½b¹³Î„#‰K7쮈X·ëZ»¦uºŒ·þÒˆ˜¨ »åŽ­ñžº6Ë á ü ;t X°SX;¶újzåèÓ£K 1êmQ­Ü÷‡ ¡YUA2•ž¥ÐQ¯Eë3£6haƒÚŒõª‹¦×TÈŸ#¶ì=*Z6¨!ú÷¢u¢xXˆóì@ñËÜUb÷¡ãbPÿ.òÙäËDhHñÜ}½Å’u;ÅϳWˆðÇo“ÏÎÄ^?ÍZ&nº®…†òóì•¢‘¶k‹¶Mj‹=‡‹;ºµeK†ÉòøW­¢‘|’Õ:­H´cÏCó7íŽÍ¡:©ÒNiº¶‡×ýÓ ¡x_[”fy¸ÍCüTEGÚAú¯ ÛôуÃ#êšå¹I‘oü㱃9x˜cߕĊôÆÛ½¿v8U›Ö­¦·mRGkV·*3ç`(æ«y„¤d||ŽÑžWGx ;SÑ6›íñ¯ÇZç¢/üà^˜ôqEhó»—|FÐdhÎipkÕ­^QïØ¢®FF…»«&, 0Óòoó×ê{œÐ %=ê´Ù4ÖúFás%ߟÜÀP&í2× 9äÒ8Êß÷ìËãÕ¯síßšãïlnô9»uÄÅ'‰×>ýE<9ðFѬn5˜7-P6ˆÁý»Éªž}÷GqßMDÇæuÅòM{įóÖˆ_¾OÄ'&‹—?ž GwQ¥|Iq&6^|òÓ1êé;E™’ÅÄ“£¿[Õ`÷Ÿðîw3D…ÒÅÅ£ýnŽ’¿™´¯R¹‚kFòµ„Ö¸Ã;ƒy6˜ƒŠÁÒ¿‹™ÐŠøžµ¾[-Ùn[-Suþ+ `üO \1Ñúúaß¶lÖîoxÞúq©DGÂmNÝy«Ð,¿O>ÕW}Ì믳†?°ç¤áýÊX„îîÕ^Ô«Q©@.ľš‚T/…ÃöMëðи5uΪŠÇNžÿ÷‰7Þ~í«Ño}‚±paõŠiH·I‰²è«èC ùŒ¦;>.U"Ìò˜“kªU0×,æÁŸתR^¼üÐMÚŽÑ:ÖTŽ9u~Öã¯[‡`Ãâ{ô;»ëG~•t¨öCKIN>}66žë_¤Ñµ;'Lìþ]²QÌ^¶Yš<]HH’fNtÞn\§²X¸v,14±dý.ÑBa×ÁãÒ\êûéQò7ÿáZpôäy)lðwM8€+(ZT$ÛèzUx ))ùF£§Žý‡–‹8’…]› éÓ`µ ð`ÝIv1ð‰õ…óx燴#»¯g«|^ Ú ðw^€ÎíÃkÕÒºµ³V6& @h¯=Ò7à‡éQ:>R<>ܪ}=ÆJƒªdo“> )¸E.ÐëÕ*•)9&w÷l§’h5nñuµÝlT»Šxõá›&ý¹˜ZÒ¯{íÍoÞ}g<ðà?Lšw“BÁûÿBKLˆ?{11YKLJ!E©ô(X@*$%œý-æ¬Ø"Öï<(:µ¨'útj.¾„37ͧzvh íGuY®hp*[ñü}½DÊeå=õë f-$Ç8c鯸ò•+Љ1 Þ|ª|v÷ƧPùPŸ5`VlbÀ ¸(æhØ­ê5ø½kÕÖŸèßÅ4òëyÜ…GÎ-çØb x÷¡‡÷A(Qfµ³gÒGÏU~4—ú`w-Éô³½£ÉÃ}¯3ü˜@·\$H<5ðF ÎÈ4­­æTþ”¬Ö÷ÌëêÙOuXŽìÞµ ,ºF'ë‚§Î]&M}:5ýº¶–Çíð£¨_³2ÌfÈá,X³CÔ­VA\ߪ¨ “§ ´<uªVÅ ­˜¹l“8s>N¤ØÐ-­@IDATbë¾c^¡€ÎäԔ솦<æXJ8ñêe?)´ø™·zgzM[×@—”°ì‚†Ÿ È솟c`ðر%áDZoÈ›£h ˜+†öàƒ/‡}]­b)AF®ôܬÄo1À9®Z¡´6±k×Û‹¡£ž“>üv&}Ó±ìÒzaÙ³eYm¬!?€ùÈ‹5Ë77kÍ"nëÒJ<àÆÇþùí»kð·É äœo^½lŸÓa;³y÷áÇh’a¦ðGÇp#Ð{çÁh{1A`‡–ãøà‡™â‰‹W>™*à{#óc ¹³«ˆ>u^„#òÔ°÷&‹ip OSj«»ì:,$XtmÓåW‹—Q#S4X¼v4ÙŽ-Ÿ7c;úι§À‘o@gp0=ó­fÃU’^CÄQ‘ât:ôga®X@åJ%Y`ÒT¥Ø³p¯B Ót* l‚Çœã{z·øpòìÊ5Û4yF,úë ‹‹.Mª\Á¤WŒòßÙ¤nNV¯Yy, 3$v!§ ÃðŠuíÚ1¿¡v‡ó•i3ô-³KÅX²Ÿ\ë;7¬ÿµc³Ú·à:ÕÆ(³7ýìþ-uËÃ:·¬/xl‚¶fÁšíb4BÓÒé;9Å&&ü¾H,Û¸G h}1Å;Oö‰É)²Š`eF¦‰ñád¨ö©»2nžÞÝ»½¸£{”Ñ ¿°ïèI±ÿØ)íØƒb¤E³ù&pÆØv?ãú‡OŽSæËáÃé¬n‚‰@(rç 7G½(úÌ{Ô“(<Îã ^<ôõŽë*ø£Nq2áêÀçšsò F¬Ì!\éͤ«ƒ.¥—ô¡µiÓ&àõwßÍéÔú_V‰y£Pc XXØm?}û¹Òn¸6èÌ-yÛ²9ÿ.ÿþ‹ñ“¥‘ˆ `×Sœ‹K¦Rtð?}Z&ÿ3’BÆ%AÃøÄó5}: ÚÆ$«ïþYŠÀ>޳+çÏTMx°Ÿ:5ý1˜¦E™‚†‘\«žôNø,üó@À/çÆð\™¿Ü¨ÓX‡vÿ3¯´ED‹J ok|`^~ pα]ñö‡‡´Ãh©Es¥“> ?d:B/èÃÒ¤KïÎg.$½³ÿØI_¯U™öÓ|? É\©ÊU_lßõ¦2è'SÌüé`ÆVSI·ó™o(‘ç¥ËVóÞg?áS»Ü¸]°¡Eýp oGèMâÓ©ó•j§ÔFÜØ®qÁXzÿÃôeú©sqúêEó>8wú4ÍP(pRð$mðÈsRØ  ÁÜ Eq½=ꣵµkW/óà}˜Ù®Pν;6Úïzw{gî ûÑÝ,è#Q0Ë•(â7ëÇ ;ºÈPž ÍùÇkŰ{{Ê„eÅøí?@!x„2>dMTîÓ6|Öy³b¿ÀÀ„‘×ÑB%§ñ•°Á…• l€EXŠ âo®B pî-Bª‹N⤠¿¡.­ ²Ê±eïQ·BƒÍn—¡¡&ˆ%ß½mcÑ I¬ydʬâ"²ê†ö›h‚Ê‹‰I¢l‰b;—Ò¬éI‹ŠeJŠ¡ºÉÙ“!ê 6=r ˆãgbÅÏxÿ`ÌQºx¨è{CKqmãÚ²ë­V± œ//Š¥v‹§ï¾1õ°ÍÏ~™,¾U*Tü2g•ÌöËÝõM»ˆÅëvŠæõª‰¹+¶bg»ˆèzmCqÆI8rü¬˜½|³Ø¾?N˜"¥Õ+•Mïc††|ü#úàzÁ#»raEI+~UʧšA1ô'¡zŲB ÇOÇŠg.GNœ–¬„¸·OGQ§jùËú=± çÑ}?-vì4‹{äÖë™$LlÝLÔGà…~][!¯A)YïŠÍ{Ą̊͒ש"î¿©£¤ÑqSç˰¡O™#-1 Qƒ\iªCó:²ˆ'n "È¡Ïå`­üßÿ„):Ð(-×îç+XæØöQÎ'#Mýhè ûF“»ÚjSô¬mZµnûº5»;õ¹¹»Ãaï±}ÿ±|k„ã•GúèÅBBÒ¿Édj'ÏX†HO¬.#0IàcØ``xZfy… !“(æ6~â“Rô“È?b³Ù5üM%o[8}Ì’¨•ÇÑ*j3(d… Ò'ï2z%Ý@ái á¹Ï<ânTÎÃW„Ü4ØpúÂvE½ðüÚ@¢»çbæÓÂŽ,ª¤ÒÅ8ú }ц³²øuÞ*ÑÙrëßKÖï r Lß "6>ALþw™hÕ †([2 a+Haá‘[¯“‰¬˜uwÝŽRØ8}FÄ#1Õ–½G°n—ÂÀ60ø×·n“'§ø !ÌDăÈøõçbQ¡t ™u÷B|’ iTÉb!â–ë[ˆJe/íN“Yý|Ú| AA…§dyw—ÝN±ÙPß1‘€ÌÅz´“íÿ‚8÷í0FšÔü MLñ°1æÙ‚÷™xPÿ.Æ!çéu&ô¡´<íKN#cRDƒ‰‘#^õYåfÅ&²_ ŠQ°`Ùäµ W5¤À© Ò™‡Ô³ЇÝáwb'÷Íñ¿‹ù«·CkÑ$Ãlm…°@-AŲÅåÁÝf&³êݱ™4ˆ>}^Æ•çK !¬ÌZ¶Y&½Úqà˜^ö=…Œ¸'ÒÙ£5+!ùÕIqÙu_x àoj46î:,–c§ºFå²²ý¤d»ˆ|êæ »—„¾û' »Öv1ìž^é;Ò:œöƒ;àô9áî:wÀwA¨hQ¿:Î'$#KÁ£^вÝì~ç\FjÍÐ’“7תR®CþõÍ»–)T"¾>´CÝá§,:4¯+>ùiŽ JM…<ÑË1ûòÀžíå+ëwÛ}ù>h,(ï>#…C>¤&® ÌoZ7¬)Ë"ԴغdÖçêЊè³a àJS8Ö#ð…õÐÜÁd-K?%Yqþã"l¨µƒ yþ‚®ý›iï ¼a¨ˆ%†Î°oJà -+zf>£@AF´èÁÝÛãqìÃ5Ëð™—vúxtÜ´ñÿûî©1Ëpg‡zÇ,Ÿ‡¹ Zâšõrî(ä)AƒZ å§Á{|Æ2nµ¤§®÷Aw_A²‰g?ý4Ø~2±–XìØÖ§‰÷Ë€‰è¢íQ«ù J`µNVë£éBùe…qcHÄÈG…f š02|¢»ç潜cÀWÂ{ÆÒ‚…q¡ÍyÍ Òh@Òi"m~Eð“Ö&)E‡¦uÒqL íói2µvûÁôûŒþ䮩V^Š}GN‰po‹º †»°{LS•ÚURvÏcWšPݨzÅÒâl쥵“Z£™Ëï€Óo[jbhJá j¦ -ÅÂR7F“¡ñ 4®S¡*wÈ~qŒq~B&ô!»ôã'ïý~ô‘ÁùÙ?oÚÞuð¸4›£¦AàžøÜX†õð ó«À,ŸϹ…ãü(A‘óG †:ŒB†ò-R š: uÚNߨá*U?K`^xÄ¿CFDÅéù¤®Á4Û9}PDäaàò§*õß¶ZïRk"ºònM?,Z±Òh‘ÂùË[ÒgûFÀ2àÙÑ£ÿ÷Ƨ2-œƒ©™¸OYŒýÌAuyöêPkd;‡#@Ÿ4røšœ4ÊÜ ¾Vêì‹6Ì: Ô‚Í^«?~¿¢úa0î ÖJåŽ`@€èÚ¦aZV[ÞÍX– Ýc2“¬x°o'é±>!!Ð$¨èVåK§F¾:~öBº]ÿ‰³q¡Æ]R«00°´ËÿêÏE¢´-4Ê èá(T} çašOõƛխî®X^ß3Ò‡_Ñ…7ˆ(š,ëyD0Sš)õ>”êÒ+zrwW³>U}qèXùôêV–g׺©Q£)Ýêmûá'rFvÚÄh-UQÛ#']qÏ䤯KïrñãaBRlŸdEMN±‰ÈÞʨ&~ —L¥Œ4a¼ö‹nÓù›>~_€uçR—蟱jÛ>hªˆº0;¢m;™6ÚØÓ—bÉúâÜ…x)Tpç¹aíÊb’\•‚Ó7Ëéÿ ŽÃA¨ÿæÎÍeÅÔb”,*¸ûÄkë¾£2/OÀ< }{äx3éðK3˜².f:žÞç3:Ã×Å{×·jÍI€ìWVïøô¹{úði“¹]yª¤ùÓÌe›Ä@(*5bFáQµé‰žToÎ4‰[¶q·ø¦ ½Ëu€¬u«W”êÔPP+W‘²2Ó‚QsÖºa-ñ/’³‘–]%oú‘eÔZaø«ÌƒV½h6ñsˆ|Ywê‰ÅëG2Ñp…îÀ’I¥ aÔÖ°ŒbPÕ˜•fƒ /A•¡°ÁƒÌÁ,ŸŠ_à‡¸'~Y7ñM¡‚sHƒg28ª}\^j4@ßÁìn fÑ^˜8rÄÜKO}w54b¤dlÇGŽXà»V|[3Ì¡† …ž«[´{S3[§¶ùôˆ±MlNû?8Z{ò›¸;̵7a%â9'a⨈Ñ äðáóÖK%Øâ{¢š¨V•÷¯kz®È ¹R‰çÑûÞŒê0¢ùŒþfºxãñ[E 8ûúàŒæ ±iÏDŒIŒfsKç–¢u£š¹ÖM:ý×bñÉË÷¥ïÈçZå¹WQ&Œ‚ïé#;ChÕ°†h¡mÌ• óHçßO¦Ì•¦2dÜh›OA‚ _´!ÃÇýŠhTÕij÷ôÚ‹?¬KþD¡‚Á˜H‘)%ÐÔŠe¿ýg©xóËߥðBgofæõnEªmpÿæ¯%âå‡nòæ•ô2XþZ¸NF­¢Y}-‹Lj>‚‘>ãijb²ò±kY7Mÿ—!wv,ÌÝB`Dª&Cû_ö²'zº¬°‡ÍëUGdªÖâäðøuÞjiþDíç‘B/µqÓæ®S笾po¦5ulQO¬Ü²O܆ºü³ Î~Õ͉##>ÀA+Œ4ûÉú°ÉæâÃA•}çÁk2«dT• ¡h_gEû¤ÅìâR‚ªO1·ê¾*k–OÅuNñÃ9àñÌCÍë¥p¡þVs‚ËŒ }4`:…»MRjêòœº‘ÖЂòõmCßõ”áÏÖt}èÓá‘~>*â±Éì%Ìá;Là ˜FþíÍ,ñü8ë0i æß±žïÀ¿£Qã44bt_‡p¾„À¯-tM§ÉÜÊ€ÀÀWÆ[‡dyÏZ?-‘l»0 á°»ÀÔ«ÚÚ…y Ì"F#²@Õx[<æ\Çæ‚ÖÚÐÍiïMÀfÅ窎Â~Îa£°£Ðóø6ì<,VnÝ'žØ]îdï9r"×{ÑA´éK~-hxFR>>åÆå…û{gè™ñ'Þ(°kOLN‘vôJóÁ¨N¯?z‹ô£P»ÇxLJ?’¡Ž·†Üžá70´íˆAýdˆRî0«:ùL…Êåµã=šw#ŸÑŸƒ‡šv©þlÚ}šíb4B¡Òq™»á~_$ÍÇòYØPÝUg~Ü-7ßó`' ~ý (*œª~ÕCXÚwžì/i„÷B`æ” Z†²žèÉ8|×Õ¬‰B&7]×Ñ£š‹8D± )$MþÔ³»±êŽîmðS“¬kݪœ99íá¯á§@FŽ™=uøUW¥À1r;:5É)ôŃ#"gC³ô“%Ô2#-û­˜RžÉô(¡€´N0Ž÷ܪŒñ,_6˧ FÜðZANñi¬WÍ%ï‘63óh¤†·Õï§38ÖöÃ(ps^i4Ò;£ÉHné? ÚEÊÙ‹×àÏ¢<þôàÀâÜõü;ásà»q»5íZ@[”9t©œæ@±¥À-L 6ýQ-¬V½­Õª9Óü;d>£œÆŸuèŽOÙ6(ë?OuìpÚí½ž lúŨˆ#l‡saê­‰2P|'ÚjˆÉzš(ý¢®Ÿ| Z™$<·ã|žïáŸæwW Ja#»±ê!Lå˜"ÐÙ’‘øñ®ù¦=" á$ž<+ŠÁY“¦ 4…¹f îbÜ#Óqâ9}J”Gæ`æC pWYAfñö© ‰Àîèì63qØIØõwo×X0¬êðÇnMgJ?þ1ñað—Ó`wMF‡NÈ1ˆŽôóì•â4>ÜQ¿½[kDSª.ͼɠúgž3b€¸%£è\çÝ]™Ìîåu†g'´çà N:;râ¬8<†×õ#Àš,KÅ굞žµÉï„ O¸º$dx*…-SôäùÍËŸª\®O2óÛa¹³0ýãÜS × ¹cÊbò7àúÙ¶{ïzk̉Fß”ÀáoÝ”ý¡I•Õúmƒc¶cÏÀ\æ9˜VMÖãN؃ŸÀ®h4ˆúRô/G€:dIÔÇ¿ `\äMì’«[ég³<ßàGÕKd»Ã=ïcbŠÁ.¿ öayEŠ)q¥_¡3¸qÇœeMðN'„ '•&"“·öá~MÌ€*ŸV ŽäBÛ&B,7L PÝ®‰ÕøöµŽ±éB¿¹«þåt{b¤|f h?1Í9È'ø“|Φé|ö0Ÿc®Ã1ñ5Á.Åož4mIÍ4“JëˆÑ[uÝñ+НŸ9¢;߻ڀ;+…Ožf"L”ÆXõŒÄXõüð2VýÁ˜S2V½8ÃGÞÑíZñÂÚì™@‹Ïõç'$ÙêÕ±©ùÔrÛ©eƒRÐP1î;·ª'†Bkqìä9Á÷®Ðf6dþ¿F…3ujc¼ý[oh%‹S°¹&Ðäæó_þ“qûûßx­ô ç¾£'äs&„c(Súi0¯Ëãƒ$wß™¿ábB2‰;ˆÎ­ê#šMjŽšq÷“yªÂœ‹yL¸º0ÀùžšÆ7‰O‘`p!|K¸û}#„Y?®MòÀGÛ`àBõ2Y }H˜ÃᯅëEãVm¡oJ»á×ÊI£">CQ=Ðx-d„èût—ì2³h2µ<,‹®uÏ]5ê™*˳ºg–O®Ÿ <{ÎótÎ;çŸt@z0 óôHפJ?婘&ô“|!¢Šk9Íb3‚ïSÏÉ÷¼v g/žÝ‚-‰IKP€È…)PûQ–×µNê=¨=ðü×[t6çµÕÚÍŽÍ @&¤a °°b‚;ÐÞÄªçø¹ëO Qš &Sã®ï^„;¥é µTª–/%’Ò²¹zŠq/+Kû‡¦)Ï!BÍw‰ùæø?ÅØA$sG IfñöiwM vepÿni5¥jEGŸu26~©âaèoe„dÝŸ^†}¦à1ìÞžéÚ>ô”Àw4Ód^ä:zCxæáÇ ¾æêìÇ]-¸]c þ Nl#b­Äî¤6”Ø©ÎþÜu‘–°m_wÒ윉?Æ´DÔhr£¦T™V 8CʵB–7– —$<ÀÖ.îXàãRÓX.õ …q-|,¤Áç: R¡6OVë´"Ñö]Õy74IJ%õQ!ûW×våÆˆ ­°ám¬z"‘Zu;!óSš6ðÇ£6Ì“hñÛü5Ò¾žþÏÞÀÙ‹qÏp§ï ½96‰9+¶ŠbˆÃä¬ò74­›jz%Ä?í›]#Bu`Ïv2©\jL|õ4ǫ̃H„šHæeOyLaÈ)óÚÄ€‰?Å¿éé_{?í£Ù-&r °Ï‘vµ°=˜RIó)§EßëÚtrJ‘ k†®iaØMæBâ‰ÿ e=vh®²ÀX§q,!ä@°~Ajàázšl,WX®©kHnŒÅ²s£þ|«Ã5ž|f±êÅœ³—o‘Ú  á<û "¸Pì¥tç–õ œÁÐl »·—`¨QBvcÜÓø„8eæè}8¨½È,µ*F,2B»Æu`_½VlÙsT…­ý£·]o|,¯…†p¿¿=åàsL øÔÇAý¨kfWò üÆ“6òi"Ìf ÀÌ\¨X{4ÇÁUJ²]Œ‘|à:hzáyM¬)AqÙx-MWN§¿§ëåµ.öÓo/4Ͳ[gJM‚9ÔsÆgÆë÷^{->Yg º”Mv$Qà15™F® ¥Ï†a|Y^žMe˜‹>Ë7§ Æø”‘é§M{Çæ×ˆk©¥l‰béõ1äéi”gŒûd˜V]€sù^h>\™š™é—Θ[÷“ŽæÊ‡BÅÛg´† ¥ß‡òÙp­‡¿™åº>"ÞL_ºA:šÍUù:0½ .(æ,ß,}UØ6cŽ“9?ØrÛ’–™¹óH£vøB 3i2”…pb³;$8… åÃÃkL ˜ð`”_åáEQ¿,ÂlÛ`üß’Óôp8h÷1vt¨uìµ/x Ãç*B”±ŒCw>ª~¿üþûaø¨ÜÀßš¾\Ý¿ì¬-„·” kOä*éêúœySÔ=ÔÅk‡nGä©K†Z߯@3+>ƒ)è>ž(¢± ï]-qëüjµaœÍ`ªD?·'ü%B‘?qëÍŠ@š•ËÊ »ô± i“^½öÈ-ÒÏ#³÷†êÅÙóÅìe[é*AF{騼¢]¥FþÉ,Þ~9QaÚÁ”jò¿ËÒBZ[J½¦fã‘[oSf/oÿCf,Ô¿‹4Ë,†V(@å[),Þ{î8U^5B4a»9ì{]–ãÝ °LÑ®$R…íûcÒólxj,'í°Þoþ^*Ö ûó‹n@:ö5ä´¿^öBgrrÒ¦ZUÊ1œ¡Ï!7éÇçͧòhþ]F'?àôÛ ÀÁ?j¤L ˜(丱Y½¯çoÞE‡íÛœB› MÂ6 y€š»³%V0ôÚꀰ€‘®¨ocÇ®ñÐAá‘õ-I{á|2íà«a9T¡ä®åÕï ‘¯îAîœá)öº{ÎÈÓ± ‹Î|,A°OwÞ ì'ÿAY™ÃD³Žºý»oȈÈzèß&hDÚ:lÉÍO¨†rÑãGßÀð¼èKÍ!#FÍF™í(³‚àçªÍÂ~.ÂcÌ{×xòžbÕ3k45æ ©á†Ö 䙌CÇÒ|ŠÚj2Þ‚“÷v$ÑkV·š ‘›YŒ{Yþ¹ áx0?ƒkHLOñöãQuñLf™‡\ó*01F¤*Š~sŒ„Ìók+Ø×  wŠ‘ÉõÔhÖn?®T3u/³³yÆÿ¶@¼þH_Q)85’Wfesr?§íP£¶i÷!)H2,²¯…œö7›¸¢Ÿsʧï¿;yì‘'²ùîÏ ú¹âÆýüÅ<ž‰ŠeKˆµ ‰GñÃÔlø9}˜Ý31à Üu×]TÝ÷ƒVãq˜+=!£ j4PìÃÂ0¹rÐuc­Ã»ÙÝ´ÿ/rR}d×߃-èçpÙ«˜ï¾qÆyô±@îœáhs/«oAqÚÅ.%ÄMìAD²5ª­‰#_ß2xÄèÎà<¾Ä½6èc[n…hš¾!¥¨L˜ZÔ"^ÒœâÔÕ 7zAxùXÕáÏç¡ÖÈvG€ž!*×t¸PW0îË^Q‚†ëƒd„•=(UüÐnØuˆê0Q @#dãÞXÆUÐ0>ËÍxûÆz¹Ãï<õÅ]ù‚tÈ¢¢‹1Z}pމ&ëTÍè4ï.O ³qœ”lO™#‘‚ÐÚ;¡ý¬&æB›Fÿ®®×6LßÄðDKW’×GµÇ¨yóVn•› Ì­ÃÐÝôGã:Ð>bíÒ=f•ßÇæ?}¾ï¿¹“xhÀÝŸà†Òh¨szóÂÄ€‰Â ‘#¾Æ(¿f‹Óö¨ê嫯dRxâ!6”åZ¡ Îs£FUL HRáiÕOgÕæà±cKj-,aŽÓiI:3¼6qä«p£õ }’pÖ^-40ðÌÇÖÏ Mz'âwüþ}hddUa¶·¾rÒøÜ_¯61‚»×#'}4… Øã{>>Þßü½D •Ë•’¹8Ê—Î(lx¨Â|”h†žI ﻩ£×íØ/Z7¨ ¡15R»¤ò¤0 1“"þ Ó9æIéÙ¾ ²o×{—¹Ê– “#(Lá­Àx߃¬Ì›ö¿Î[#Ú5­Ó»`øílû³àv˜àÅ'¥H³¹òiƒPóvC›2‡ËÄß¡½r¢}Ók²ÝŽ+*©Í ðÒôšj O»`8fæ!ü½h„ˆ1Ï”f€»‘“…ætæ[ )"ó­,SÌ|+*9Ã&ÿ4k™4õcPâ±ü€Ú6®í¶¿²BßüCAC ¾i!“Z½¡ŸÌæµ´ŽÌ{CSºÐ÷-{È9 ³Oa$3Zâh™×gÐ]dØë±ßÎ90˜@43zíÓ©Yz{)6;賃ÌÿÍ_K3ÙDû+·ìM¥×4aØß'6>AšeÒŒæ›~4ÿÆÙ1 #6Ìk^bþÝYt|äˆ^¾â÷ŘÃ<ÝŽþ/<ü„»wbÎV—*MœïѸtòD7…Ò”ô¸nŠÈ[¿øb".ödöœ÷ÇGD,gYMÏ9ªi2Á“ýÝÕ«=²v÷#õO¤1™7oû êר({2Ø„µÐr´i\KšW©.ó¤”ã}S˜Å1‡ µL*l2}6hzF sþí ÍëV—ý;¤Ö¿ó`´dÆ©é¸Z†ºÕS#–©¶z@€a³r¥ŠIÇþmpÌ¿’vT}<Ç!qåŽÇDˆ5¡Ý@N•jbõ6éƒ&‹í:tBšU‘¹­|ñ¥vBå[éܲ(ŽïÍëJ-ˆ1ádKf ÍÌ„”v€qά¿Æ>åâµb.Õ9«Îº*o臵¸›Wcíýº¶­Õýº´–þ_LÂIÈŒ–Œy}J Í4¯‘^íÝÖ¥•4Ÿ¤`K M<Ù~M@/‰R£ÅòÆü>k%$ý\ÚWà󯺢ÎJðÌáSuÂ<›(hpêZ‚Öï¼èïÐ7Gõ†_Æ3°B Ô ÚŸi&[yÑüU×F®H,ž°†HñI6?dL¸Ê0”bÓNÇ%•‚Ëø}AdŒ¹ã¿B¯éèÏLë4iQÀ(]±ˆ 6æ›ê–G e¸áYË6ÉÂʤšIQ›À ‡`bEÇÜQ¦0C``€)³VÊÌï¥ÑþÜ…¡Â«òÆsfíËðz펲í—l„&e³4yº$Í¿(`4®S‘ÔvHÍ#cQ¨ xÊ·RþIcŽ–âHŽ™Œs_úàza¹åþG;Ã$ÎÞºQ-Ÿ¯UjŒÞÐ7óÊà„ba©æŒ4É$d6ÇõjTÍQ^%$KóMª^©ŒlOý¦æƒQï²Êï“—ó/;èáM$>êðiNg pe>21p4ÝÜP¾ )©7t§ó&ìÉȰ¶°Žß  Õ*“âæíbÀçp§Íqò|l¼)läp¢ êëç/Äë¶”Ú.ºÝ•ô}pG÷'0ùô±i ó'ž<)X¤¿F£Ú•Å+ß,µ ¯þïY}/*À‡ã ¢Ž)8 S$ÿ­ÚÍÁùÔiBóÁ³Ô£ËΞÚq-L*šc)¿îdÏY±;ÔáOROôéÔ\| 'wjwh¯ß Ú‚§|+¬ƒ@¼‚L胓%…²*õ:w•a#¯º$ÛÉŠ~¼™Wwxô4Ç9Íë —´Ëù j·²Êïã®ßÆzóòzÍÖý š·*·kó†h´«޼ì‚Ù–‰…z`H¤æLþ;È¢ûÜú“÷™àC \þ5ʽÆÈ¹è¶ääè¸Äd-!É£ãîµjÖä7àœsîS”­¤¤‰´ú”>š\SUF‹‚M¨\ÁSž N¸L'[:Ô^€m;5Üž»rK†êÖª"ÍPèøKS:ƒ+ “u ˜ÄXÀäÑŽßQ… !…”ì´£êd.–ð¡½>Ã/ó¸½[˜MUFܲØdš¯ Ó­ë[5Ì딑,'ùV\ñ¢„կ잳 )p\Œ‹ÝŠ6F.»í\Iù¬èÇÓ¼zjÚ°Ìh‰øÌi^Om«gÙÍï£ÞËíùWõfv¦©à·Ó£Š7nÓ¾3ÊP»aÔpdöšyßÄ€‰Yb`¢õåÓß _h Y¢*W øJØ #IÐO=…¨¶eoÁò‰Ií¾ùoN0À9çÜÝ¿Y=»’¼&øŒ>˜}ቋ! |½ê—çŸh3+•'å¹÷¯}:Mš·°S4sêÚ¦¡˜†Lò/2UšaõêØLæéx飩ÒžZ,KýÍtñÓÌe¢r¡(è‚(DaÞÄ6þY¼Qôh×D=Êv;êÅÕ(=ŽáF c;ýGhâÄ´Ãÿà‡™â‰‹W0&¤‰ó­DŸ:/ÂyjØ{“1ÎU“±&÷×®xaÄ®œ€úHï͉£G7°}Gó6pGVôãi^=á„õfFKƼ>ï~7C¼>nš¤)š>y¢WOí¹{Æü>5aê÷É”¹âé1?ˆ&ÏBÒÑwE3ÜËíùÏP¹›Œ¶Æõãо]ËñØ(h¤Ó‡›×Ì[&L ˜01[е]œã,ÅmK® sH¿ˆ¯!9IWžÓ€B •iÜ^æ‰7Þ‰B„—2ÏÜÝ#Çí¥Õož >ûe¾¾e÷áó_yët—[ú4§ŠÃA.5‡_ÐwPCŠIóô)¨Å 5° ÌÔMMmúÝA"œ°CÒ1'£ï²ÛŽ»:Œ÷6IcT£×‘|’~Ì!3Q°J ßÊMâT`îBvC!»ö7µ¶ìÿë}+R¤xÙ‡_yei=xª¿ô`¿[?<Í«'Œ¸£%æõ¡ÔÇp¯Æ¼>Ct—‚³ª/3zUϽ=S¸! „…]{û–Ú=ãß…÷of¯ä§?ÏÓ·î9óõØ·ûâÍÓ8 †Rÿ0%dòýQͳ‰«æßÇU=ý~5x÷\SλÈ'ÜÁ¶ŸŽ‰þyëޣɚpu`€sÍ9?vð3mR¸ ƒ ´Æs¾ÓmåiÇî ´]W‚Ÿ1Gf‚Ÿg&hðYf‚Ÿe·¾“8±~ùaeŠÎð£OËäÆ÷(ddWÐàû®ý5Öéíµ·ô‘’g;qøÐ$”×h†æoài^=õ53ZRy}Ž<‡µû¤pë.¯;zõÔž»g¤e&ÍŽ ÁzrcþÝõÇx4‹ùÖNÅDÓÉÉÔj‘c^›ð´*àa‚‰|Å€¯„ J 8ÛÿõÇTxþŸAÌ~gŠß  38ÇœkÝá8õï_a¬JØP‡oÒ‡ˆ Eýp o&f,Ý$>:Oú0ŸÃÙúd“>RæÿþÛ°e;9oå¶B½x0¯OYh¢˜×‡“/¾ô@„[¾ºòú>tÒé´ÛbþûmÚ4Ð,ç] þ@ÂfL  ÀâäU¢³f' 5|vFi5ø°;wòÂÎkß×Z·ýÃô(ý‰þ]²¡´/Ôø/”ƒãÓ'`óªe_ÆÅÉ0MI¤Ša  Áä ÁлcSÁÃ!ôAá4%!!öâ¦5ËߨZªK˜±Ý;~_n’äÊT^Ÿ|뀟4¼bË'¢»ië¢Ë$Yj“‚k¿-&˜01`bÀÄ@€¯„ ¢€É,àœ5kúÒ¥Ë|‹ëÇÅBèÖΚÑD÷M(ààŽ$Iä€ÐîÚ>uõÂyTßÒÆšÂHŠY0éȸš ›ô¡4_ c—°ú¿¹ëplY·RÒm·ô~¨° W=¸+„IçþÛg¬[²`ÇÆe‹7£ ç_­¦°áiæ=¹ŒÁÖÊ [©Ð°À˜^y%ÓÅ„\¨”°¡4ì°IÄÂUW@FI/\?¸»MZ*rû€ÇýðÝ·Î>óä#OÅ= l6[òØ>Ÿlùá* é<®œ{£°Ÿ&˜01à À¡üiD ×m‰•iŠ{Þ.‡Gî„c×W`t?TmZ­ ÁDzX­wño³ÐÃ7#ou:túŸ‚ç×>š42â%×A#ù(Ý©¿0÷C&Œ ŸèúüJë½>†­4¡3ÀNžƒÓ&FkòS,zä¤q_ ŠY ÍJaçàÙSü¥m×1Îö× ýpòìRMëVÓÛ6©£5«[U„MÍ´›“™ïæ˜'áK×lÛ¯Ó6Öqë—-þ~CÔb2 Œ:ÅC ÊŒŠtA0é#…öß\ ¥ÝàúÁµŠá¼Š¼ôÚÛKç-Œ:]¯c·kU)ؾÙ5ZË5®ÈÙõ™O`›ípGT,g=ió /½õçÒe«˜¼ëÆ…´3×j7¸~˜š ÁÞb`hÄÈî,;>rÄ‚¬Þ ñ$‚á}ÆO³&´­º¦Öt­£.ô†š.jß?fúÏBpOÖo|Vد5]6xÄØï&Ž|}Ka«Ÿ¦çŠœ+•x@8JšÎ(fí®Y4?jûšU;;ÝÔ·¯ÃaïFµÔçzñ`½TÉ0­hPéÓá©ùý(ÉfÓ™ž ûswh×Î…+æÌ˜›p‘;’ QɃ ƒ6Œ&T¸-Á¤…‰BtÎEúPÂ×îp+#höìÿv;Ã*þM÷cÊ?]p³ÂY¶T˜V¦D1í±~×Ë\$ ­H (&ÏX†0À$ÃŒÀ(bfù¼ÃO²Í®Ÿ¿˜ cNäúa·%Ÿþö½È ˜Î1× ®\?(tÐ “Â'Žô`‚‰^bÀ©kiE³6PnËBÐxm⨈÷xmµê–ǻʹ`;COKxÞúq©[|OüˆJ»uÕœ€›Xð<%±÷ñ%Îדo½jŸÃúZØàÇA:ãÌ ;¥sg||œsÞo?ÿŠß3¶hS¿F½MCŠ•-R´d`€i¼ø- rrrbblâÅ‹gïÙµ}׿õûÑY¥½"ƒ@FB¯9ïJ«áÊ,˜ôä6ÈEú j”L™SQ»ÁuË2÷÷)\?f5lÕ¶Aµ:u…††• - ùW>lØ”¸ØX¾CAE+W©J‰î·ª>5|$ì6[bZy2µfy féÑ\ÂOqà ñïp8’S’“.$\Œ;yxïž­{6o܃)TPÐàÁuƒMç¸~p¹V¤÷ ×&˜014ïƒià«,« Òæªj­Vw›Ôï!#FwŒ·Å‚?E¬ÃZ;hC6§=›€¨WŸóúÕwß-~þ‚-Rhz7¬Åu°”ì×5mžP~ÄDëþK€ÙÑ0äz™©úÙt­:zeÁÀK3¤‡ŽÓÊát Æ‚ÜuÕÀKX´õºð¦Ñ`Ú´ió·ì~+Ä ”«ƒrvŒåÞcß¹ /˜9ây^;¶¤g›Vg5˜ŒíÆ;_M1ÞÁØüZ8µûÑï놎ˆ|U~Ëz=·ø`OÛĦÛ?ÂJ×c8|Lׄe®;ÐUùmÊÐÔЈÑ}ÂùVëøìqí\øÊxëðƒªàˆ‘} töC]Ýp¯4*Z…õ}•ßL Qå|}öµ°Áþs¹+¥>äÆ{üˆ$ïÜ´nޏ&#Á„éB ®A&ø @ÿ8§<” ɹ%S@æ@1 <{³+iÒUHÀôÁ:Q 5® Ò§cç†5qlÇ3®\/Œ‡,úxtü´ñÿƒ‰€|Æ:y(:V¿Õ{³¼Ï𴧸þsÝàa6Ô5çÛÔj &˜ð5ÀÔN×…óI›Ýnµ~û Õú(ÿþ2˜ÞÞàyKpÁÄbI¦ž›Š\”eY2ó4ÖÃôªžŸDŸu89Q×OÞôòûï·58œW„ÙV³M< †ÿ¬8ÅP_˜ê3¬S×íÑÊ0ÑëQ×?`’; Þ>šîè842²Éøˆˆc,÷ß–ÝoÂWâMôk'ʼòMPþ>ÔÁåK§pþËr웈³¯GÍuÐy&}[€£7Ê}1dDd=\¿ˆÃ3èº >/;údôõ½¬ýý±õÅL})²ƒ§­£ë#aî ô¯8Æá }{ sÒÏ]§ D<ëПG´{ûc®Žòœv{¯§Â#›~1*âÈ 7GÝ_“x–„yX‚óQ¿mt ¶•ç®^_ÝË aƒtI¦@L~hBqÐÙ“Âû¤àÆ?Àš?Î)…Hj-¸Ð(AíNr^É((­ßs&}¸ÃJÁ½—ÛôAL(!sªÖ£BÛa¦nçÚÁçêP‚ ×£ ÂúT¬‡ë‘¾c–Ï}ü¨¹PôÁuAmRpíP×ƵƒóÃ÷L01`bÀG°êß9mÚc0m;vÝðÈ*UýÂ(t@{a1z+„j•×OŠÑÝØ-Þá”̼¶¨J`Õ›øî`ë„PÝ~j¤‡®b“^Cù7ï€AöŸCCµþ~‚¦Ù“Þ ÅKýåg¢Í}ªü ðÈM8šc…¸÷&ñ>ò×>És€%à/G_Çk8µCK£ß¢ ç:¼?Ÿ÷´‹¶×°@Ó"þªØy ÕÚÍþ”õ½Jv[ÊAôa؈÷¾œùê–Í,¡pþþqPDäãèg׋ö„±(;8³òÙÁGŠÍ9}. Ë’à %z6,YF‚²‰Ÿ\ëg´0Ýž)ï[ÚOL‹ä§.ž³i:Ÿ=¬9ô>Àd.íó‰##^ay«uZ‘ûÁšã¬Ã¨=Î3àÇ9/@}ØÙ–ºVÚ~dÈ,мÂWaC1xdB>a@}ìyæÇ_1{F¦A1 <ó>ç˜åÔ»¸t Š&øP]›ôáU~{SÍ1ϹM´¢ ª Ò…Zµv¡5 ¼æZÂu覨÷I›¤3ö—÷Xžu˜å/ÍanãGá^­\+8‡JÀàüªµƒó¢æ—&˜01à+ Œ·F¬:bôˆ×ðÔ–øCýð˜ýè CGŒ1~døwÞ´ ü.–Ãü“Rh:5øÍQï ªRWð½÷ãñ›Æº°‹Ÿðôÿ†?—ö.ב¦Ù—¡¬¦Ïƒ`ÐÜ©iµxð„ Aâð)DkÒEKoç= ± +Ç-¨¨ººæ^.óšÅª×5ñ…õÕãLæãþ-šžÒ·ö¨òîϺŒ€$‚ž² Û&ôå‰'­‘ß|iXé®|6ñqSjú ¼æœ Žõêù(Cý¶$޾ÎË• !Ÿj? ›þð܉¿u‹¶B8õaºSÜ9X4áí7fâ®±YŒSÖ–ú®í2üºâ˼6ØA?î<óÂkš ®‚†bðÈ?€š?5‡d xp.yðšÈœ_–ayoÀ¤o°äÿe|I¤'Ò–jƒ´ÆõÃuƒB JxPš ®)¾¯èMÑ)ï«÷Ìò¾ÁñKPóÈùãGU­jíàóì¬(n‚‰9ÅÀø‘o,cÛzȈ10Ýq¼fº½Cw~ ߌ0å“‘YÏ~úipòÉØjòy€e,g±Ùæp$qÑ­ÉucÈ\Ü›ùeš a|çIë˜:»ý aRT tIôÌ>D!]ç-&b1r%n]ñLÂÜšüÂG…àºOj]y¦P¢>U×0+úãáú/µeÇðmu/«ó‘¯í€Ïɇ`ä_·Û´/á7r-̹hV–ÙÁ\Õ,ÇlG+òeK eYz%ü-´=üHeM¿&µÏúµ }d%è—z Ç¢”ûækœÓë„Cÿæb» \©t„ÁTK½›ÙÁ†dö,;÷óRØ`¿8ÄÏÆãn¢4ÔÇ Mð pîÔüq9ŸJ¸à™÷ÔËlIÙB—_Î+úPtGFÕ¨µPkÏê0>WH#éMÝçÚ£UŸ™åS1¤p£ÖèÔ»YãGÍË+¼s•€Ákµnð¹ &L äL¬›-ÀÎ7ÿþþƒú7vÃ?C  mîIðÌ*+{¶¸-bù7l ÖNË…ˆ;:ñ§-Α×ãº-ÍíU×ê<Ä:º±ÃæXŽŽ”ÄóU¸O‰-¸îãvô1}m Ô^uØõéèï<^ƒ QíÇêÿÛ„‘Ãÿžù†Œ±ÛÐvjƒbš{§ýL=;4i‚•v;ëS@ù‘š~¾!zËy›÷<¬Im„z1;øÉ—xñ@]jxT=`±¸ï t=€û…¶§ƇéˆÁMj• u™·eÏ@Mw¾ ܵn¾‹¶/{~9=•öÉø¾¯®/ ÐW-¸¯W}Tˆ~dÔ‡K}ÈŒ&÷5˜wó\Œx¨yt½ÎißT½&}ä“ùó~^ÐGF:¡€«Ö žï¹^«þÏžÞ3–S×fùKxW81ž3Ãï«rœ?%d¨{ê=ólbÀÄ@1mÄ«WZ…ø4¼os&ÃqZ¯ø¬õÓžìü¥FÄÈ-h¯lUêàœÎ¼'ˆþæŠmŒH%ï¹ûÇ$^@Ö숪„‰¾¼ƒÏõBBñ mpJ§ R FRËÁMþ Y¢Æ¿>>%²ÌÖ§/bG?ZŒÊ°ñš…SÓ^¿âÇdÃ`ö|CÞÁØ&È•-­Æìâý‹EÿJ:E'TAŸ ]«šºdª;ÀŠfÙ­Ã6 ÒFæø¹KO.¿ºë®»¸Ær¼Sé0 Ç÷@_çhGtÜ›rù¾¹“_† Æñäˆ1-‘«¡¢ÔR«À†¯¸*`žýøƒ@¤)lb8õÈÌ0{¨ˆµ>ꥤÔÍ?Å0²)#SÉß&øÔÇ€guÛ½”u1¦­àtL´†o„Jy¹7CCŒÕÐŽŸ'µ~•öŒŒ®0Ë1yŽE:âøË¹AˆM_ÑHÚt›'&2à ›3®c%K§ÿŒæ5ˆr ßat$%h@ÙÇQŸ€i¥’!ýo;í+À´B”$2À©öt»–VÏŒä>ÂÄéOåÁrtb÷Jr©tÚƒô/DAÚS®Ÿºìàc^‰ê{ã  ˜6hŠ•tâƒ.Í¢™ …šH±ÿ ¸±+’6.2–Qã¡#ù„·^:£æŽ÷³àÜN<ÜJ*ßñõu¾ Pá=f;ö äE»Ó^Þòz‰Ð¢ÎR%B™ÔüHÚæ<!AKH²8u}ä£bøãjEªó¡ZŽ Zú¢æ§¨1»•GxÊúy1›ýüãAb¨ÍnoX½b¹³Î„#‰K7쮸zë~·ëZ»¦unÿæ‘/]ŒÚ°[ ¼ñVúµYY»|€° )øK>…µcû¨¯¾¨}ú|©Á#F½-ª•ûž6Øé`^˜01'H ¥t‡sv´3ê4vØ·`ÇZÑ ‘ÉXÄhÕ‘ñ#‡oÃzê…šøöÏFùí(¿—>!AaáHøw˜àGDÖÆßùZ%mðûz0ãqÁAªg ŽÍº~úð(6„°1d)ꈷ„”quVÏð®®æo§Cü„²{!ÿМéìy"ðß/#_ÛÅçEà_‘rúÒ+édl4¯A¿™¤¯®n;…ÈY K-¾ã ¯Å³N›ãF·[uë=9×(ÆäF“? Ÿ²WæUÔÞj¦75¡\B‹Jw­{\†²s’«‘sµý°¿iœþ9æ<ß ­Y•îÔÝ­YýZ×xDÝ.>uŽ~Zº%ûL|"Ù/öæk<؆ƒŠŽA€Ws`ˆ7#¨ŠŸéŸwö15 ½®’`ÇTBåRƒÇã˜éØ–}öB¢n±XÆó‚Åלÿ"Ç~ ¼W]PTdXŦ?êÇÙ•Å­§p'{:¾~`ïs†|vgÆ¢.±íõÌðIñ¶ x¬=6­¾¹~H\i%–Ècüëo׫C/ÇæF3Ï÷–¬Œ±Z­_ó¤úKfDþ fÏ<6-”cM ÷(Öžyƒ#„¿f|Õ7M-‘ 4wOs05Š7zÇ2¦säqIðx&üíP÷@„/¼·à×$ÔRL5L~Ùíyöb©{œe}_wÿ0@×ÌÐÀQcìõ”lh㦼ñfDoÍ“u U À4¾<öv·o¬×·<þîÿM×>Ÿ†#›·"' U‹ë€€V«Z•ÇÖ q»ÿ¶nš·—Çux¥zÅõ@ U£:ôÒ?†¹Íù}ÎRÒÏÿùò¿}¾xû™üîk2×£|ê òˆ€UפÚR±™\Æàhqê+£xÛKË ¾û'íÝ+îµÜjÀ?ÿœûUÎ bªÍK¦zF!PÁðrx•Š…,BÂNj–dêtŒmgäM¶]Xçî¡ „ME‡¡\Tïz â&:þÏd[­u`£¡T§ÊE¿(S!ÑÆ îîÆ†Iµtnó4g¦& eB´R? fý^ƒÚo1£¡\bWž®à߯_ÏH®.tå°Xq=¾U•]US…@@îx9Âõín>T#m÷ÕÜL·›<´®^¾nµ8>ÇÐOØz¨f…¨‚³Õ¨ðpóðtŸ¯S¬×¯V·+D·¹v%`ÃѶi¨¾ÿè0ðƲNa»½"…@qÐ:wîìvÛ}¼lµj@¤¨!àïçwÇ÷_þ¯ÉÃ>…(¹P¥R¤P( k;r¦À u¡ìèšp\ÖŒœ½Z¤=üô$vS©×b÷¶ŠÑ(kk•³çÑæ¼]ó®ŒïÆEc«ú@9kÃ\\S›¾ƒ{'$g¼qìL¼³Çª\Uõz[ 2Wµvݺ÷Zï)UL[€Ô¹B@! p2,9­¬¯qæK7OïA°Âç8e-«SŸçqp0§¾£²eŽ6GÛûU „/n©¡ŽÊÖJW_1~˜ÜÝû2ª7¨\º\\ä©…ë÷ЙøË.RšòSŒ?ÖG=Ò¢SÇǸĊÙ(?ͦJê:lå¢`S¤¸¡8SJ¨PqípdpW Ø{þÍ[¹ƒŽÆÆ f#´F5ºëÖNÄj_7´aŒ/>q–Ö®NåÍÕ'·9¡íÓSSës}$(‹7¨C(•AQ‘ˆñƒ9Ž–Õ}³}}¼œ9VYãÍ×gÏ'Ža¼$Ž{ßԜޫÀu\ÀBÆ‚5»¨zÕbÇ vÓ8ãby7ŒXøz{êYéé ùÚ«Ap§‰M‘B@!p 8þÙK×H¢n+® Îü€ceÒÍúêT ôu¦¥Ô@%$^¡ZN`0^xd0ùxyÑ¡“g©NHÕRçéè-ÙÙ4sîJzeìíTË«Š£³wz~hû —.ÕæÙ@3;ý¥ê!Ù`"Õý}\füww_Ê2gÓ!b9ïïíô샷‘`ªý¼] óò>nH0«UñÓ’’’±òƒñc‡rƒ+ÁQ{…ÀõAc16Ü眩ÿ®Š€\q™Åg1èbeÒD&o—ì +¶¤+©éôØ]}ˆWNE§©QíªÃ›s IôãâMtâløÒí}n¢.­‰t?ðõjUüéÔ¹‹tðØYªU½ q -\·›¢Ž¡æl }g¿ŽT»zUÚsè4ý½u?± ßßCìÝ‘îè׉z´ƒgG¢/æ¯%_/ºoPwq¾5ê(ýÅ黃>úiedšiÆKÉÝd¢iOi6í=B‹Ö凜´ –ÂÔ¡‡‡öÌ«ƒHà"ÿÐöì% J5*ô ôùcp‘’ªb¸è#nìÖÐÏÓÝÝe˜:!9’‰¤”4W½šÁËhœ»˜Dß-ÚH§Ï'PjôàžÔ¸nHX¡®ù˲­´+æ$¹»»Qÿ®­iP¶"]IÆ<`o@žöÆ äZ³%$¦Ðº]‡¨GûÆbìÂ8ãæ–ñ¿]Âe®Nw÷ïR Ü7â‚§'÷ «,ÆñC9™¸¡ÞY™Àø‹AA|¯Ç<7¹³»§Ç ÍdªÏ˜ÚüM‡K{E.ˆ€•ô²êgy†uÒš½ôËw#vr11fb+ñ¼kBxd·ìl7}NÄäme©®³˜ ” Ô]'× ÀuêìEjÀêI’Ñ0É®zécžè³ŸF³zÄcqôÙïk¨FP Õ¯LÉ©´vg èÖŠÚƒ~\²…^Ÿý;õjߌÚ‹~Y±…Öìˆ X^ 5³Ä䜘aß7¨mÜsˆ¾_´‰—†äîæFIÌð)™‹KI©äÆÌE×6è0¯¢Þ}kg ®â'’%]I£¯¬ç AgjX'D0Dkyò0¤W;c6.sÌZ$¸Ð×°åÌj\¦tª .Šú ¶rCß.ÜÀc‰'M|h0­ÝÍcÂ&šòw(ÿÚ‡iß‘3¼8чûiôí_¨c‹úÂcKIÆ•ÂÆÛº·±;n ïkwQ~KjZƒVò‚ËãqÔ®i(YÂX3ôf×G`(Î %£è%þhhuA! °E¿5±õî=ܯIŽOzûx? '?°¿„Z4k+h¼ˆ¨¾ã¶È¹Èy†ÙlMLNÓ¯¤e˜Lîn¯Ÿy.+3ëÄèØÿüó L6%ãQ¬[Íô¦–£ù>°X’ÈY̆œ(˜ø“ಆ󗒨{ÛÆv¡9r:ž.²šÕó ¡ j ‰Æî˜S´‘% `6@þ¾Þtïm9ÒˆÑ'ˆÝ¼2ãÑ“˜ êX`0Œi>ðêT§ÉýJÇÎ\cã1VëñJ$6ž€ö‰¥j~Ô©eqÎ.fiWôI—e6x^€>  Ù7ÔdA´žúWZffúÞ†uª÷("KÜJMÏ¿ç £ú³’K šÒûß/e)Â!52êÈijß,”jˆ ¿ëüûÜ3g’_Üq¥¨qÀÞ¸2ddZ(òÉa„hÝ ŒG;žÌÆN–´°ÊµjTGÜs•6Ì&9CÔøá* ¤Êá²L‹èÂÍŒœºò…Äo ¿-8bpðéV ú±²¶ZìðEsUÛÛkÔ­²ÝF;RZF¦XÔÚ¶ÿXͨ#±oÖnßø™ÑM^ÿ팷—òm¨£OÒ¡éá’ *f‡Pa(Љ]’ü|(56Ë)‘¥ zƒÎz5ƒXâ’—X~ÐqÁÏÇ›U¦‚£‘sîE™æüÞ­¤-H3 >^žt¼fÃÊR•¢è ¯D&¥¤Óô/^Mæ²(ó¬ §ˆþÀ?„«WG Âøîýÿ¼õõ›§/<…k܉9qN‚Câ( ¶±ñ‰ù˜ HLaï‡ÛœIé|BrÞqqǕҌMëÕÈc4ðÂ^šÒ\¶=y$ÛJ»™á鯒T^ÁÌ+Ë>¨HÛÓÓb¹øVa"¤ÆÝ(êýå«®…å¶(f?xÁdðÞcô ¯<ëíã÷FUuÄL*>Z¹iî|Å÷‹é¼iXÿ´tK3º>ìKa¯~õŸHÄ>ÃõºÙÀ9‹Ù_+¹Ï‚«œ„²þõÁãg Æ”Pg2RHÌ ˆÎ]JÎÓ»>é µmrÕK•m4tH4Š¢ólµ'ØY¤gfQݩԥ2YÕJ&(E‘73*`X"Ÿº§¨d®v“ Ü»ZUy¥BÀ;×YÖs Ê“|ÊŒ˜Ù–‡B5cM¿Î-…dÞ ÃAqǕҌ¶yÃíçe[hëþcl–@ ³ïQËP¼ëzø0—g̨ûßç—ÊÅ Œjü¸®­ ^VnЄ bQÅÇo “ˆ:½ž8 ŒÆë]Z5ÒÇŒèm²/ŠÊHÝs]dùå±·»}³`½¾ýàñ·ÆNš¢}õÎ40 ëÂp8s•Èå? ·õlË‚4š3o5dû ¨<ì;KÐ…†£Š¿/-ÛEñÌd¬Üv¯¤R;V(-A?úÂåd6ìÞ#&Íê×Y5å=Ô¶àR3†U¯öÃB^ÁÐ+X McU L\:4¯'T¼þÞz€2³,”ÌRŽ#§ÏËG\oU•Îåû„ë§JTh\·†P«\´aGà±*ŠÕ*íì3¶ì?J1ìÒ’ÄßÀ¸PR*j°7nØËêTZ6)jW)À(Ù{æ\“ãöŠ Ç €ß“d4¼ïztüP_ÿÀ×Àh<6²¯¦ Ç€ì*¹ =Ñ®h_Oïi÷?ùÜP.›t¼áô±ÕY’ ¾®«FUŸ…=}ÿmôËò-¬’ôOê‰}áÒ“÷ö§*ì}ê™Ò—®£ú¡¡†÷îÀýú†ºÿROwÎëw¡j5œ=[yyæÀÕ…h6@cÎ|ž°øPßÎ-hɆ}"s?ö’…UPx¯ùiéfúïó²¾w=ötÕ‰þdßý¿.ß*˜x³iZ/‡y)~©®kJ§wæëZõ2g"€¾"7g.ˆ8¬ˆ3þž~ôíÂ4忉|1áo3adwŒØ•Óm ÷X&|Ø l=` ^*j°7n–wÏÍhó¾£ÂC^ainðu)’û\õz…@…@c+&!^õë7ªQ'tFÝšAB¢Q!j§*a–Xiq/ë<%ý_Û¶ÝoŠŠÚ’Ä 1¶BÂá4º̆ÓÊîŒÛr”ë¶MG²Ç( {tÓ„+J™1ÜDNw§p=‹@£*ó„Q·Êdbo«~0¢oGÂ&‰½9°+̽­·²t5‰ûöõÂè!BJ!Ûo¹I>J÷³G+xžÂüK®6 ½¹={iÏ®{3ÈÇÛ£€XÞÃê@!P>³aöÀè^´hˆP-®CXt˜9el¾ATýÆ#…Š$nÀ.+‡´|iaþĽ(›í$ N {/9¶”t\)j°7lóÎ-—#çÓ=\”`Ć D¹¹hQU±å0RªáÓcذñüê  9Ç(µP…,1h_ž‹šØÍy­ú=ÉÌÆ»œ‰_±ÏOº“ÿBéÎ*=³!aƒÔ¡0rTänv\äY2öÊQXù¤{Ϩk rŠ@žT£f½†OAíÐÕ˜¢p½Êd•Š¿ôìmŒ‡#¨°q °qï¼”œJ'â.Òï+wЭë#˜=\NÄ] ®ý7Û¶ri—M~]­˜ª< ò†ÆXLz0ùU ®öOöj©+cðòÖŒ¥+/ÆÐÞûÆNà>æ +Nv%³§…/Ý[ò?U.Tò¹üyzxWµ ¬ü^•X!pcÀØ$6ƒ}õ)I}+¼á!)lHÇÇiþªÔºcçq\60rƒtC‘B@!pm¶rlFÂDDJ5¼orGðÔª³{[5A1¢TÁÑÞüm­1ä¾Ñ]¹ªN·Ý(|9¿‚}=«CNlŠ b! ?zr_¬‡T¢’!йUCÂæÊõSv‰‘Qª¡˜ Wn4U6—A`väÔ—ìFªPAÏÓ;¤nh?ìC ;iv q¼==óÂÈŒwpœ8¾g<[ÊÌ2Óq–¾¶¼j´pêQ™Tȸ½1¶êA!!wDGˆB%¶mSÒsÅl”1•^! P(nÊ^ãF ®ÞYÑS¹Ÿ`6Ü==C|¼tŽËàpf‹‹7î¥UìÍ.ÿ¡: G6sðc8ÐmÛœ™ «]fc?;Îù|þzÿ_‘‡{馫ð&j¶X¨®!fšx1ÿ“óý¢M´çði._:ÕápÃ{ßDZ5!¡M®ˆ„8h÷Ôd÷ú\?ô|5ƒƒ+]ºÖsp!Tv …€BÀ€€\¹–{Ã-uXIÀDȸ©¾QI;‚ªv™ÀoHJ6 :ãíîáRµŠŸÃ ”ò/VÓ\ºiÐ…Ú³S #†gÍw¿]L‘OÞÃ’„¢§ Z6 ¶/–žÑ@®ÛE!ÌØØc6vEŸ¢ÍQGsAHFÒ3/èòÙ‹‰ôã’Í3-A¼ç®[;QìùK´íÀq3àA›ö¡÷È+ (BØ1FëÆ9Áš%£q46ž~X¼IHc¦|<—Úp0ç‡XsŽ3ÿÈ×Oð;‚8<ÂíÂ@ª!=<˜&$¦Ðº]‡è©û0¸nZa+—÷Õd†§W‡¦Ô¯KNOŸ»DKXês€¥7`ºÜ¹~õ8¼UO˜ƒþ¡ÝMšÉŸ³“ý}#YÍô¦–c;>0ßžȸ„Ï»TrxU™0í+Ñm ƾïq­ïmoUªóhv#š‘é4IY¥ÂRUÖiHFÚ™™±§aêN{‘£3þâuôôÛßÒ!Îél²°ËZ|œméÓ_ÿfSÛm/ç¯çaÄœ?Ä>ï¢KˆXMàÞ$³áð‰‘KW_N!PJ&„EôÇfx¿ɼc‚é‘ËÌ’8æð"OºR U£ünËáê?$(€â8˜©¤½¬ÆèïCpx€äÔLúƒ%  ,³™’RxÕ)gÝ ñ‰àêâCƒ©.«<ý¸d“H‡žZÁÌA&«hõ Þ›SíêUDL2ÄêØ¢½£75ª›ÿ[ÒžmiÁ¤|þûŒUdÆÿ‚«ø±ºW ªè'ž»•™"`Å;Òyþ4zx/jZƒ>ãçN1ãJæ0 ÖîA™‡ßÒ$5Ðχ:rÐÔIc†RvÄñëòmÛê&cú3÷2³SG\7²¯ØVOqÓáÿĸо`|ÕtwÂVFªP̰@ÇÚsRèâIlàÞ1-¶/•›09™9w%GBO«Ü@¨Ú—„ç¡>|çí‘,‚/”™eᕸ“bukkÔ1§bþy6LVä g|ù,ŒzòêZ:‡[X}ÀÕ©fp e¤¥ƒ£Â·J~ ]½Øª| —@ÀªkaØl #KØ;M›Ì*S {î½[()õê<Œ‚voÛ˜níÒ’*¸X“šžIÇØƒ^ï›ZP€¯õhß” HH¼BGxÜS2j`êÖ¶‰˜ H*$ î,]¨ÆÌܽږö#D‡X}êß3§yosǪ~ÄŒ Æñ\íêUéÈéxºÈïº‡Ë iƘÛo&Ø=lÜ{DÔÿ22-ÌX £ÝZ‹¹hÕ@_>n%˜«öMë †%úxNÝbNž§æ jB+4«_SÌQYÊDEÕ3ïE<`i¼d>e¿p`îW³*3·r5+×87zˆ1†W¥ƒ,FƒåýÃÜhaâ-ˆÃª±¸œwÔ±3Ô¤nSmX'DTrõŽhZ¹õ€Ð?lÁµqÐÆ=‡ +‡±ñ—È_ëÒhXïötóMÍiwÊEë÷ Ñ8YIù2sQá΢¼å›£„ D€iüãZ²qÿ@¼Ää¡[›Æâ…‰Ø Â Î¨ÝØ(k]f?ú™«¾“óB'þˆ¹rH5fü°”Üù|ÚÓ£D~êŸBÀÅÈYÆÊQ£ÓQ.h÷¡Süqòàß{úŽ#‰#ˆ§ ÞY‘yQ¿ç¸ ‰vÅüK6ì¥^ü–Õ?Vï¤Ñ'ÄG°Ih!ø(Æœ[ùcˆ´=Ú5qilæòŽuÿû\H¹"+÷.]nU8…€K  é˜@ ¿a‚)&›9WøvÛ$¡ƒí:<™—U&<æÁH1'Î Fàëëó.c²Ÿ˜HµAíü’‹¼„E´jT‡Þ˜p7ÏÍö°}IùûyÓ ;‹5r‘¶ž¡ÜõjÑ%C91ÿ„Ê–¤}‡ciñ†=tþR²P ÃuK¶EÜnݸ6­Ú~¡híÎÁ<áFQõtb,$ôcßetä?ÛŽèȼoH^VžpÃÍìNv§&i;ßÔ¢¡<û¢Ä[‡Í_µƒ;;aq‚_-äŽ(©s¼wßÚ…^|d{9ȾêqÌÁ÷̨ êÙ–"Øø 3¦›XtF#‰¥ ø‘ôîØŒ&ÜÛŸÎ0#³–™D…˜©D:…Èú‹YÇð^f‚øG ñ›¤¢Dlø~¿x¯4¡;ûvºƒ`fÀltÍ Ÿú)*R¸0øùH¦Ã…‹yµhfthVÚ6 £ŠSiEæ…ýží‰ùQ’Ó¼ÈZ«š(Ô^­ƒNð½·u¿÷úü!£ªÏºÁç/%‘…ǯr@ŠÁ(¤ŠXn0þžäÓá…®ê'VímU×ãyâ}ár²:È—âš$cµó #y{å¬?Ç’ˆ÷^|0oÃ|- LêKC`îîß…š°jÔQ–`Ø#¨~ÎÞqž=]a.)Éè:v(XІtåÝç 'F ÉÄ~H¯öBË*·DËXGEÕ3_Ž:ɱ‰CnÆ~á¨Üóò©p̆Åb:r»rU© V†b*#%ÞB:pÌXU»‰uýz¶oF}¡ó€Úó„¢cËúT…Eƒ8'8nˆò ; EölP7¤*e°jüxà]¡‹Ö8z#íŠ>)îÉwôí(òíÓ¹…ȸ`Ø£ súéÂh¨8"6”b<è-Ö¯L™‘Á k=îô †<é€hP‘BÀEƒžÜ»h1¯ +qŸ¡›ø÷NëÆ¡´uÿU5¥²ˆÌíýží‰ùa™˜œ–çR ³Ž°Jì^†ôj—Wàš¬b€{._É»æÂyåæÂÅTES”+0¾: ¾„Iôú݇hklÀ-Ô£fÏ[E¿nîÐ,ïÝXÐÝÀéβÄ Íy~‚#5®[CHl±¤ªSˆ‹uôŒHÒ˜5K°8¼”WÎ'$óX'ŒÃq¶0‡Vâv ’¤Åû‘4R`ëÂsx4D0ï‚óºe,ý@]V²;ßÄ+©ù ÎyC‚‘̪bUÙ¶+Ë6ï3ÞÏ7eææ–Ž-Ä;=rŠUÏ|8ö$?ØŽÍ[äæt5ªlÝšša6ãCáôÊ FP=hÃ*J與NgiÁ)1Áw3å}Qâ-äS?wuÇPc‡yã©ÇŽƒ'ÉÊC˜Ä×4¶F¬fåãåIsWlÜ,ܨ=óÀmÈBth¸}›þÅBq.þå/R3àŸË¥×Ë-ƒ ¢„…å¡7wyCbY“?ú•½Q…ò\n Ø¾üsýûÓßÄ¢ÒpV™íÀ.}íâ‚ êÙN¨fA}¾oçæbÑG¦…ºæ’Ð<ñ9T_በv$…ÕÓ†ÿ’Y9w¯k1ŽxÓ?àVsv|bRêuc6 ÜN¶gÉl5Ðÿ¼«O>¬°¢ñ¼$LúÇ0¡øÒ?çK.Ù<{QØQÀ¨º~«¶ ŸÙo4X©Öõë}S3Áäx±—g$ÄrÈÇ›™E>u½lÅ5^5’›VPðd±Aja$0C kù®6>ãìãÄäTÝœ•u‰ß£&λâäFC0Á5jÝþÓ²-3.];¨PAz€1„ß"üËÃ^7ˆÌ?eç RdÞŽAŽü=ã# ‚n1<¾€útjÁÆŒ ÙKÊ1N5 ­)¤ÉüqÅp!õªEbü·-êXZ‹ö«ÇìÝÇÅ“ ‡ –TI!P.À8ë4ÂâìP–¨bƒS 0 ¶ L°%!7âËåœÓh¹kÕÐÈx㉑y6XÜ•ml°ñæù›t?Uv0éìJª‘Êg`ì-=“ßkÈ ÷X=ê•G‡ õxY&0/SÇÝ)‘ zeœüë!ó32˜¥;¨³´ßý=lß·’çŽo²Ý,• q™õÛj–îÌFQõ”y_¯ýìiaãñ®‚³YGäš“&—º933îJz¦&Ý}9.û¢sêÈ“4\¦A$g¤k‰·Œim/%嬂q€ˆ-Ï×?ŒUÛ£™;nB]Ø>"80GÒ€<À%ÓÁßlX¯5ðÔê’RYDlX@§‡(ö%ò‡\Ò27=ÚmŸ•ž&+*úDqŸWé*5‚áH¹’Å¿7£ž«¡ýããì%jJwöë$¶»Ø.ªyƒÚ":.Ê ‘»£Eæ¶b~,@bqž}ÁƒbÏ_fIèY±ø KHãÅó\f|Ù»*A5íËëZwî3H7ŒW-¶*—BÀ•à•PÂvà v[FöPrR¹ TÏ·î?&ìËŒu<ÆÀ–9yAE2ò˜[FCÞþ°¼pÏÙ–;‡iÂÝkÊb[~<͘˼0„y$T¹à5ÌŒ‘Šª§1]y8¶¿|_ö’cR ÒÏÇžZ6vWpÍÚÛ+©VäjlÝŸ4 cŽ‘àá{@}iDæÆ¼ìÛóC¯ž¯@XÜø‘ç°÷ ðõ¡Á츮dAðƇcŒ…®Jðî…ñãäј\F#£¡ÆWm4U.—B`väÔ—\ª@Å, ÔŠàTöp‰[Ñ’–sl[²pÛŸ$]æ»ûwfûæ­ªyõ±™†ç]/öÁãa«‘˜;u?ìs ¸À²YµÇ^}c}»fõª=}ÿÀ2¿/7‡ìà‰L=®óZ/€ØK®Ê´èÅ_GaXŽUF|ì_cßÍÐOlÇj]’°b‡À4Ð_, Aô*Š+·—?l? ¥bôœ`/]Y¯}üó }ß¡S‰ŸMínÎ Ò Ì‚`‘Ѝ6`>)ì! ÇOÏ€àLš´®sí/ŽâRㇽ‚Û»‘9¼Ô½Â‹F‘9Lücís¨´¿gH)åªáñ3iÆ÷K(’½áÁw=ÈVEñvÂ8‚ï=ì}¥k®;휸Öÿ\®G>yöó·^¿Kv‘7ˆl ZÆ&¨ï¼­ö …@~0éÀÀ€Uèa×~dâKïwhÝ¬Õ £‡ð©¢Ê„À{ß.¡}ÑÇö~3cú#\ïÞ òF€,î8”œ¥F…•'l˜PZ.žû1êH¬vØN ‡Ö¦„™&Þ*N6¶Œ†|&“ÝØBg®m7ï;*˜™Z¹«‰2 l;ÊÊh ¯ÒŠØ`ÓálFm6?sâøŸ\T¸€ÀAJ7ÔÊ$ƒ¡¨PòƬ¬+æó§NÎáþ¤Á=ty%gŠÌb~DÈ…ÊÔzŽ÷#Év1Î-êTriFjÜÞÚ…³q‹¹Jª!Sí …ÀuD`Bxd·qS§—Y¼ä,fPFƒ÷–5óçý¤[­ ?.Ùl…—¨ŠJÄÌÆ>_ü±Vl¾‡XˆdY™mŒ¶Ö³³/¯ÿkþ|®»d6$ÃQ™àPu-rüÈZñÛÜy¬£ÏMÊåà‘ùm=Ú ‘ù‡?-v]™Ã=µ3èŽ /0…¢ô>û`Ž—¼ÂÒÜÈë?¾[´Ñjµ˜Ïþ=÷—_¸,hwÉpÜÈ¢©w+ J…€ÕLojÖìée­´³v¥TóåËñÉÑ»·¿£uêöæ7 Öëì[.Õ!®6\NÞ7¨ûµ’UøûhcD8Þ»eçW®°o9"„Ã!' J²Qá{A™*(Ç0§YiiI){¶m|µnÕ¾Yíñ>Öãwæ"I™ ^ØÃ°—À¦èÚlÚwØš˜¢íX¿æãÔÔ$ˆôå"…”Œ^;•B!   aýÃÌÈ©+¯7PS‡Šº­qõõ.GY߇>…y'-kÞ1Ðal¥ÏeÍסÏkºCø„2g³èà×¶S9LÄd÷ë/XTíK>þ?šGú˜½5g«òØ)“ºäD°" Fƒch'bü´u[ÂçèXƒÙ€ úú…"…Àµ’ Ø÷¤mý{ÙÞNÜÔ´VÆÃ) ǵ*¬î³%™¦Y<°pÇÚ•woX³—1Aû˱£ ³¡k° ,¿úuªÑNDÀªka¹Ù_Wfc7Už9w•pHóÀN¬aé³F`>ÄOƒó{´‘UQá=*ñpº vÜb2 }Ä’nØíumÝÈ^*Ôµ23š®Å1"í ‚V²ñ¡Àd3uÑ_ÿ8äGðÎÑg.ë îaR‘¬ @°Ñ€ê$Ç£÷ÿºâ·Ÿ >©úIfCJ6*@UœŒ˜R9~`u}Éó®Qÿ÷ëß~íÒÓOŒ}ÒÝÝÝÛÉePÙ_GÌfsæ[ïþïÛðˆÿná×&ò†±mod6ø4a¡k{¾+êD! ÈA@»1R`Ä‚W;…¶‡É&¨²+4ÏÂu»(„ÃØc6à±ØúwiÅöúÓéó (б 9ú¼÷ƒÂ5¹+àáì2”™Ùàž$M¯ùÄôéAŸNž|ÙP`9Y€úŒ`6xïµä§ï~îÚoàYk÷›'ü÷Û%UÛ6 ÕÙ#Š—²¾Þp`¥¨¼ €8po»mÿ1Æà¬c}eç†5_ïZ¿“H¼°IfCªQ)ÉFyiàô)ÝÀø±Êƒ7Ï_~}ÝòUë/6ë9ðõ†uBÜ»·k¢aÕÉ¥EÑ\pEù€­lÎ+|VO=cïó/¾öûº [°x…q^QäøéÆ|’ |s²S-5‰½ãò=E …€ ïw{9FÆž¤Ïž·Šö³c£7ÎM{p`ä½ì<ê× ¦ñÔnùí]‡öËo+¶ÓN–” iWP:r@á`gkÔQ¬4âÉ‘¢Ö˜LûlÁf ÁU’ÞÿâX2ÅehR7„†ßÒƒ¯†Ð¢õ{h}†£ŸM{ް̵kš‡ÞÙ‹‰"p¼õ!‘1¤!°‹Ë'Cý»¶&„1á`²X ”Öí:D²TQÂXºIh ‘fÙæ(ÚÎîÒ_yôvBÑÒñý‚fÈÜ[ .¿¡ziÜþõ>‘a9ùWffÃÍä¶ÔbµL³¦Y‡s¿³©7& P‘“¼Ï}ÛêëlÛÝkèí·gg[ñD5ÁÕ|¼ôªUü4oDz6…R§eC ÃlÖûàŸÛðÊɘèU›–.\–––‚I¸¨Ä† ƒd6” ƒ¡¨DHfãV¸%Ãá±dÉ߇¬~5祦6é}âlÈ× 8ø’·—5¸ªŸV-Ð_ûç·äsM šß.Ü jÚ–ncUúë‡O¦Ù¢'¦¤éÜ&bü°˜3/~ùŸÈY¹mŒñãÆ0pu fãúCá›Ã“;ƒò.ª…€Bà†"°+ú$yrüžÖë&RÉl ^Ò× Ö Ox9ðr<£QØuL¾ÇhXï‡ ™Iðà þÝì¶;ƒm)£B˜‘„¤1iǵd1°vgŒˆ]1†kwžê¾"îÙªí¹\õDæÚÕ«ÊlľAí¡:5‹™¥{yÂ߯CiíÎÃb‘uìˆ>””šFßþµ:2³G@x炵»nÍÁ؃…ëvÓfl$³±yïQޝVƒŠœòòx(Þ‰¸"b8¨g; ©êŸ©¨÷å+´‹Ÿ”™Ùø4bòŽqa±<é|˜ëjËlàãU¬Ja²ÃNaÜ™šzź|î¿òù¢–:7¯ß¬E[?ÿ`Oï*înJ=‚qqYâì™™ééIé))—NŽ9³wç1.¬”^á׉˜£Ý¥T#ßd¯+R\ ,XÕ© ÝÀ¸eZöÛ?·ìصEh㦭|}ý‚Ïúù1ÿ«O~öÙ®$%AýŒŠV½VÀþwÝ;ÊÍÍ-G|Ê‹|]ÅlNÏMI­JTOã/ðÏÎÎÎÌÊÌHNK¹êÈá¨Ã{wæ7‚©£ ã¨ÎI5*ŒymÆÇø÷0·V,¾A8W¤P”¿çX@ä'1ö•™À\ à(T§njÙ@LÆÛ .÷q/gq§˜lË—v}OÌu`÷6"鹄$ÚÈ’0Å!hË<<¬—Hz11•îH~ Z?²§ÊF`ò£Ãé+fŒ>úi…”@bo£QGN3³ÊAQÄV«zÚÉ Ö`f@™Žs4,o±«[»&ÌôD u²øKÉwá2=4´‡Hkü·…%5BmëIðZï3æQÒãŒ,³žmÍÆ˜[8éZLá7‹§Ì̆x•®} “þÎø)‘}fM [kóz|$ÀºÉ9nËk˜ dFïÙ±‡·h>ÆDAgò˜>vÈ€óQTväÇí‡M2’h[L 09ì ]•ä{ŠÅA}Nö30´Æ±A0!Ñ»¶íæíßÃØ!˜Ã^¤¿x..õ—™|œ{yb“ýXžËgM*½ÓðaØóÿ7°™ yŒöÆØR@ªoU×9 ™6‰ï+R(J‰@VFæeh*ðãežk%qŒ±˜“gy@ï|½ˆ²XåŒ Æ»µmB—’SXuªZ>FŶwÁGSYÕÒhSQŸ]ÏêI¸gK0¸¶%xz’äïë%T’x‘‚ßíªVáèåDÛY*ñëòm4gÞjšôatˆíS!‰Ù~à„ÌšÎs4pIZã°ugféOfrŽŸ¹@,…§àª,Ù`íOˆbr ž»RÒ2…g*y {DR¿ÖûŒéKzœ˜œª›³².õÜìia㋺_Ü{a6êzÔý8ÎûŒU§ŸÕsvøx|,$QcÏÀ9z…d@ð¡ñå ÆžrÕR2×îü¢ëŠ€l?´)ÚR L äA®N¢]1QR«¿,¾¨H!Pä"%xýOö=DÄõä ã™d°—Œ Æ##‚üdžÈÇø•Â3*½ãñ‘mÜ7ÚN.R`ìÆÉdȱéñœ þÆøZÍ>âù©:îuÁ@*R(ì#…‘øMe¤¥^€J4lÊj7 ;H0ºñ[’™'Ò\€Ùð÷ñ¦ƒçÎÊ[y{{×!y¨èËy(Jä$Þ^B*ƒmLØ1Q‡íDl|Á9³#\ÖvaÛ‰¤” f8Ø•C¹úunÉҕβXùö¶^VÁ\@…jïáÓ‚Ù@œ#[BùñÜV+3’›ÉtÍ÷Ó—äívÏJO;ÏÏå¯%É£$iÂl„‡?šñxXÄ8~ñ"2_øŠ;Àý°Á0Ç’áÇ’ÙÀG“¨7€Ù0J7$³!÷|[Ñ B@¶'öøøËÉžqÒ ' Øã:Úéä³|¨H!P*丆CöAô10µrìÀ„c6y Æãöòš|}}TNfås*ýÕ߸£ñ‘ØËqcÚP2h_9v ]dÛð!7<ëÈ›öB¯b¾=¼W¤PØA`väÔ—ì\ÆoH?u(z_ÝFM{áœdÅû›ƒöÍêÓý:å=ÄñÇ~Zº™Wî3¨%nÃ|ù–ýÔ™U¬:ϪC ½Þ–m%vÅœ¢öƒ³8æüAÍê׿ñ€h=»¨mÛ¤.­Ù#®÷_­à*t"î‚0‡ Ã(ˆa Äe–Ò4f£rÄÙØ{褰ɀ)ØglÙTØc4­_“¼ŒBQÁ›Ù‰ —/š`xW·öFí;žö!~>t1ñ «£Õ/Õûìåo{ íñôôÑ#Ò›Ÿ±_Ø&/ó¹C˜ ”‚;õ²Ç§FþK·ê3øc@¼ú4¶ *„>ôø°àc†Ã–Ѿ¥È…í'ÛlhKãDí›o¢À犥E@ö;L~å1úÆÛ |—ä†qDJ*p ÂóØÐGe?åügTzçàƒ6a\@;¢ý ÝãÎ%sS`ì€DŒ«ìÞ«™´çgGL]Æé)ÅGãHޡ֍í›v8$‰½J2³!£9©Jðö§Î&ÐðÞò=Õ©eCÁl@êѯs+¡NM¿­ØFþ¾Þ<™nÀ’Æv¯?0¨;}ñÇZ‚m½Øv¢.lçØkÀCTŸN-„·§¹¬æÔ¿k+aÔïåEœôeé»ë§çÞýžÆÜ~3õêÐ,/µÙb³ UÒªQmá à! ÎFÞÿa&ëÂe¨þE20†ÿyéªÇÞ·ÀäØ#¸þü÷5ôç êÁ ˜Ò¼Ï^þ¶×àET·Zw¬[ .Í8ÞÊ>bûH™ÎKݱ {+3ÏéVz—QƒãY;6x}¹ÉUD¹—×åd¡°W©ë7tļŠ1YÃAîÑi寇ŠGcÆÉ¥rL“ã‡L'Ÿ‘…‘ƒ+ú­qp•ãJŸƒ‹£ð‘íü7òÅ& ËqÃØ|™HØh}ćm4ýkvDØûâ†ú§P”Œk#& jòVcà=Þߨeë{^=Ä®Á4§q(YYçö`6Œæ…]‡&öìåÊ–pOã?HJJŒ¤C}Œ=bÙ#¸£usã¼YÉ–`#7¿~¬f¬ƒmº’žC’be[ ÒÉ‘ïC\4=A§þvÉOß|Îï*ôÐ`k‹ ŒÅ‚&„GvËÎvÓçDLÞ&¯•f/?Ì¥y¶ÐgX¥jߜà YŸÕ©–ðö½É×´Ð&žÇûÑŠØlR6~¢²#€É6ãäÀx\ö7¨×Fc„í&Ÿ²½n<—ý׸/ê9c:y¬Ò_Å^bb܆®Ët3$“!¯Éçq4rÜÛêóDcl4øæ8HÑó©…€B $`ÄÌÚŸ7ø{­Á[M__ÿš>óÂ[¡µC^yt„ÉÖî€Ó(ª €zëËÖ3,ŠúéãRR’b¹jñ¼Áƒ(œü€ÙÀx,èñ)‘+pÀ†âs®”îA6±tùä{ ƒðð/[œ1ŸyšEÞYµê[=ÕjåBŸg¯”qÜÛQ¡ÄéÄ5~?E.Œ¯$ˆÆbu.¥*šB€GM;ãJQýW¥Qøï»¬ø•7ú+,þ,¯ƒ€}¼`eâÂðÇP›cpe£¡~Ñ â#0!,¢?RÏŒœºÒð˜|h#`ƒd1‹ãc¥ïX·ú{Ó­·MøfÁzý±‘}Õ‡©ˆ„ö»H»7­ûŒ 8ô‘Òeôôü¤éá’Iþ’åœå~Þå³wŸ˜:½3ûòÌÇ x«ÃDxHN\åǨ@uA! P”’Ž+*}Ñ;~ûYÞv 28ö©8E·‡º«( «®…åÞ“Ì 1¡”*Œ°yÃJvæîk£‚BjüÅÇwÐ<ÒÇŒè­) £QA 0Û׎GGÍݾæï\5´=ú£”™OKNc6ŒÅÌýX¨ÀKFPÔ±B@! P( g! éPQ·%0XÅÎâ M8èG8ŸUÌ]É*‹xfØÙ„Ëúƒ{˜ì½ãûŠÊ°Ñ`cx+$Gì¿ò÷_sñÑæh{ôôô ô §Ðua6œRr•©B@! P( …@I’ Él@•&/üÀê?[•pþl|×~G±q`Û¦¡z×6µvMë–9GI ©Ò– ÄÑ€{[xŠ:«e[Ì);Ö®üqϦõh Ía΀½d6¤dƒ/9ž³áxLUŽ …€B@! P(\¬^Õ¨0á„Ѹ ŒjÚ·eãþÃ{vï1hØÍ6²¯šÚ€UQN?›·Fß~ð¸ž|9áÞŸ?y1×ëºDW’ŠÒƒT= …€B@! P\E`+b³G˜ÿA•Þ«~ýæA5ê„έ¤Ñ[1öЪ ×оuBªRÕjÁÿkÛ¶{`np:/ Œ}*HRÕP( …€B@! رvÕrlòܰ—Œ"„û ~pôÓ^¾¾ƒÇìgªQ óOE771³¡mÜsÄ?8´NÚ¾-¶p]¥±¸Óì7œÎÍTÔSõR( …€B@!P€ôR oÞüªWûgÛ¦¡º²Ñ(‡-YŠ"£ÑÞ~þrûú‚S%ZŠÙ(EC©G …€B@! P”C0©„V‹0 ¿eÈ]xžYÝÛ:u²YqªÐEF{ë:Õrß讹}}¢@˜ÙmÜÔéHS&RÌF™àS+ …€B@! (7`Þ‡‰%T¨¼Cê†öCÀ>Ž£áÔ î,ðŽOÐöÇ \Ç…Ì,3EŸ8k÷ž£/f™z¢òÚí2€k¾€>Q€'°šéMÍš=½¬È¨8eEP=¯P( …€BÀŘÑEš9u¥¡hX½ÆÜO0îžž¡>^:Çe(°ªmx¦T‡`.oÜK«¶¤”´ ‚½@Óz5éá¡=IÚ†lÛœtÝJ]Z7*ðŽýÇâèóùkèý=D®Æ_ºBf‹…êÖ*¿ÎKûß/ÚD{Ÿæò¥³-C ï}ujÕ€,ÙÙtðøYfZ๊pq8Ðî©Éîõ¹>è ž©ò“¦—øü¹ˆÌm.©S…€B@! P( òŒ€U×ÂrË/™ 0R²!Ô¨Ü=}>/Ýþ£q4õŽ Ël´{|Â¥š|(cn o /AñŲPÑ-]–œüìS§wζfæ×4Ð5½£àäWº\öÜ®hºÇ;éfr[úiÄä.WHU …€B@! P(®?š^@-† k˜ûaóp3™ü¼=<Îl@š°bË~êÓ©%ÝÚµ5¿Š(¸Š?=vwšúÉo´~÷aêßµ•¸ÎúbþZÚw$–jU¤GïèË’Úsè43'[è'F ©È¹‹IôÝ¢tú|‚Œ<8¤'5®"ò8{1‘~\²™NžMï¹ëÖN{þmc-/OÚ´ç=0¸µkvURq"î…T  ÖsTÈ$Cr46ž~X¼IHc¦|<—Ú4©K±4æ\BýÈ×Oð;‚|éö>7åId>´f5JHL¡u»ÑS÷`pÝè—å[¹¼—¨&3<½:4¥~]rê|úÜ%ZÂRŸ,½ÓåÎRŸzµ‚i¨[©¨zŠÊ:èÚݤ™ü9;Ùìõ‡¼Íi;¤t6™„‡é=nJä¿Æ…Eœ¶X-ÛIÓ#8ÉÞjÛ$­,§¨÷à<€ ðN•UO…€B@! P(Š…˜ l’áðà‰¾Ã ”ä"Oº³ÌjÕ(ÿô¬:OîC‚(.þ2’ ÚËjLþ>ôÀ”œšI°D”e6SBR /³ç,´»pùx{ÐćS]VyúqÉ&‘.;ÛJÿ´‚™ƒLVÑêA½;6§ÚÕ«PûfõÈÏÇ‹:¶h@cïèMêVéå¿öÍ똔Ï_ÃLÂy™™?V÷ªAUýÄs·2S”mÍyGz¦™FïECkÐgüÜ)f<@É©´`í.Ú,–†ßÒjW¡@?êØ²!M3”:´¨O¿.ßF°]¡Ž`2¦?s/3;uĵq#ûŠ}aõ7þOGûƒÙ@ŸýÃáoÁ Êq$ÌAq–Ø9\ØúiK˜›lò5-ütòä«=¶\ÔÄñ…|búô kšu8ë>Ì?Êw§g¯q5t™ãߦrT( …€B œ"€I¥Ü`ìJf•)¿oÁµO?oJJMË{/Qs]IMªWy7sRÓ3騙 ¼òߟ|½¨Gû¦ôþ÷K“p1)U0%Ï>x[ž”B>ïÎÒ…jÌ<Øsë û‘‰ ¢¯¬§Ïü°æîþ©j€1£ÇŒˆ|.æä9f ®Ðó ¡ j ‰Æî˜S´qïª_;X¼.#ÓÂêaÄ$E\ð!Э•0toß´-X³‹¢Ÿ6!1'ϳ´¤y{yð;jòb)UÏ`fÔM’sK×brÓ”içòÌF£¡Ï`iƯä^cì¬ðñWÙá2U½b>ƒñ#ÅpT̶VµR( …@ ÀdRN(径Y\;ypU?±j¿ÿh,u`u%Iñ<ñ¾p9™vϱãÀu\“„c¬öWcÉÂ1y‘÷Þ^9ÓÕçX!% ò6l=@˜ÔKy¯8{0w÷ïBGNÇÓQÞ¨GÁ§ ú:gxÇyötÕ–í9$yz\ÁöP#›ôa‚z郟eRÒ«=}:w%b‰ÉmÌÜ´cɨ¨zæ=ìȃ«jtÆ~‘÷†ÙÓÂÆç”áb—%¨N ‰3³"§Ü?[1Åj+à¼À ?àX¬U"…€B@! P(* [¹"Øì‘Ó ¼ êK˜D¯ß}ˆ–mŽb†â :uŽfÏ[%Ô”nîÐ,¯L—’Si§;{!‘6±ZRsŽp#5®[C¨d-bIì+#êè‘ †—§;-eƒëó Éì²6N‡ã&l'`žÁ¶ˆÛa$HïG^±ñ—„­‡|ï¹Ì÷ Ú)ô½e²<[”ÂÊ–—°”ï}»„öEÛûÍŒépp«}60§~µöœ»K‘¦OäÆ]¢²µ ðŽìwbÙrRO+ …€B@! (9¶v{9HF†à°ÃغÿÕgI„q¢Žç0ù.lîÏžªŒŒÒƒ)(ŒÑùaoðœ‘Ñ@Ø”‡Ñ@Z”Ŷü¸neWº—ÙèªZPå‚”q:ŒTT=éœy®÷ÃâÔ¹UÁTˆ¹ÿÔªS³æ­¢ýGc·ŠÌu]°ví?+*àåÁþv0C%ióÞ£ÌhUQþLsŽMb °a?–¦ŒÐ…šÖ«)Y»ó0=<ÃXô¡}:Ò¼¿·Ó…Ëþíº¤$®Û67¬dáá«Üã,›nçˆõ}t]»™£Ç³+âm&Z4+9LHkW7.ËÚÑtß î)™â.\¦‡†öÀ­|´%ê(3>hØÍíó]:rZH^j¶ZÕ«0óu’ãväÔ#_â"N2²Ì:Ç_K-"‰Ãn)fÃaPVœŒÎX6|¢ét=jš‰#GêH³ö³ZÜvWœZVÌš<¹|vdØm³vªV …€B@!àH²22/'&¥–˜Ùð÷ó¦´Œ,»EId ¨žÁÀ»^Í ºÄ«ö’곇$_/¡fêB¶””’ƇÍt4ö¡ä’ÄäTÝœ••#¢ÊËÉ9ŠÙp®å6×ÇæM"²Þé¦yvÿ4òe£aÐŒr[©JRð'ÂÞna¡¬²¹©$X©j* JŽ€˜Ùf¤¥^¸’ž©¥ed iCq1©RM0p;Û²aí|…åxÙ?Ç«öÙtž=LµmR7/T¨ŠKo9@§Ï%°t`”°Óx÷›ÅE>êéa?oo/O bÉFäS÷úVìs´7Ú=+= Ÿ¯r969Lì–í¦Ï‰˜¼ÍæV‰Ní·H‰²P‰+:Y_᥅©ŸFäc4 TqBXĸl]{šo4á…ã:èÓ9aŸÈ„ãÂ"?#“ö›¦ë-ÈJϰ*V‹tÅDxüÔÈ zÎ5X•íÓ4·gE¼ºI>k»Ÿ0uzG«5û¾ŽçM péœÈ©÷#Ý“áÓn²˜õwq¯³8P[êçî÷¯÷ßn1Ƈ½9Òª[±Ò¿‘Ëð²¦kP0ÝlòðkÍÎHVýEþ•Õã:, “ûû³#^ÙÇ÷ ϱh.ÿ«¦Oa5²ú¼à²O×Üž3þèž ÿ00Üü6K†²4¨ª®Ñ.“;M’êfÏ¿÷žOJBÚnÍÃt'Y¬3ø'ÝÓîçrÎؾÙû÷Ýw_6Þ÷ÒÛo\¾bf†NÊïòиŽ¾nÏýïÕW…’«¬‡I3ý­Sö«\æüØ x¾˜ùÆ”¥ãÿ=m@v¶ùk.cÕqS"#OwýixØf+R( …#€‰%6~êPô¾ºšÜ{“ÞœdÿßÔ¢«ð²áözz€½Q…²#‘¥V^yoÀªRP±Z¶)JrG±Câ•TjÇF×Å¡ZÁUØËÕa A ½9?O´áfŽöÍrŒÒ‹“ŸLÓ¡y=Ú°ûp_Ûû¦æSñlë m!d:{ûfª.³ÄÌâlì=t’B‚ ¤J“owV¥Z³#ZžÃÕ­=‚ªÖ΃'©M“P ôó*dš×g{–ú´eÿQaàÞ´~Mö–$”§¸„öfIŠvúè©oìyÙXÍô¦Fbš20ïb)”x)@«¨<þfs‚ªùzk¿UÇǧF>ÍŒF3%_x{x´$Íâ^;%G*"ŸÔkS¶Îù 5iúÿ™¼µ¸# ^eõ¬×<Ý=[°Èt9NËŸ Ûî(4!|zÃlÝÂŒŲÍÈOMïÈ“ñî1£Á÷´hwÍãfîÌã4Ò›¦YR–àG„4ùjmt2½Ãå{}žÃâ] f4æóýÚš»i„‡‡6ˆ‹_Óœfý)çîÕz03ø\ŸWݼµö|ï8‹Tç?þŸZžÕÖóùóŒM’Ÿ‡_Wl5éf9ˆÈlÔ^! P(* ¼(Ø›¡ºyL_³b‹Ú¾ù¨5;;iÛþcòž!yá‡PwšøÐ`ªD³[M¯~<—>ýõo:yö’ˆwñÌŪü¿?ýÁ·ÓðÞxR^<¡/¯ÚŸ>w™ž{÷{ÚqðõíÒRxšøÎwôçšÝ4°[›Â VÄŸ#ÈŸkvòzùÃ_hßáØ"ž¸zËl±Ò’{éµ™óè¿ß-fVžôØÝ}D‚ÒäÛ¹UCÁ$Ô«L`®ìl: 6öÁË(bξéà‘ªA­`zŸ¯?5ýz÷ÛÅldŸ£ºf/{×ÐÞºÕzyǺ•Ð`A_í/÷9iº;ÏqÜíåQ’keΠ$/Si]s¶Þ…Ç´¦LX­Pb©D3sæDL“~Nøñø°i~<ûå—_Þ“«õÜA›{Õ¨Rë£gŸÍ”™‰g5mƬÈWçâOºÃ™IŸa±ŒæS¶ÉOÙë igØa¬áÎq[ÍÙJœž9åéÜ{Ÿ‹ˆÉÔéKC†òµE¸Î· Ï1ÿyùå+8Ÿ0uÚœl«þ¥æ¦ùFØ*q-,âCf V<óá‡^²¼¬>éëÌÒ½Hóø¬Yÿ Óñ—¬æÌòé[OL}ó‹5»»‡©Ã§áSD¾>q\XÄÍYfëd>Îc&˜ãxgVäÔ/ùèÏÇÃ"–rþ°ûŒ%½˜Iêgrwo23|ò $?%2Òªëk8]–Å5ÆÓÃÍÏý¡O'O¾ŒÓ½óNDRRÆD«ÕÜá“g_ZÊÒT+sxRªƒ4Š …€B r"`Õµ°Üš¯4 €‰¥Ü°d}òpÌ “›Û=‡ÙÀh”mxÆî!Œ¢ÁTðw\D½†Ñ·$¸{:îN!ðòôà[ÿ›|óejçíu$V‹=~ô/¾> ûöN¡üh9å*Óò‚¯æ³y‡‡ÿ’Ó£íüñðwÙòJgÿk¦ÆÛ,©XÆãMàªG\½®ý&'î¸ö|ø{ÕX5‰•6­#xý6–tÌç÷ò0¤_i®fÀGzÎwy¾KWOÚ@2rõ”¹žÈ©ÇyV~„Yv¬üç’vV2¸Ào;ÍÒ¬:¦fd ¼NãŽ%5ÕW^ã|.|òÚ«B­ ×f—kùÙæ8ÏÖ³ÛòØoLƒëü£ZÆ2¼Ÿsr×Xu*g)H ®pýÛ±$#Ýj±| qaÕ­—qÅÆM±Ï¥8ÉhàüÝI“R™» [µY™Jí …€B@! ±‚3¶«ÄŸ0±‚I¥…73ö/Xaµ˜“~\²Ù L%%L’Œ†ñy0FFÃx¯¨c<# ™Î–Ñ×K³`wx½*)ÁŽÂ–Ñ0æQÚ|yØþE2Æ{PáãQ|Ѿhg=;ûòú¿þ³!úïÑ'dÿ0¾Æ!ÇJ²á+F&îºû‘,ÝlŠ§Ã­¹Fv=Où’›%5gôÊç†BwÓ3ÑUMtUÜÆ±wΑÉ$‹`bXèw¨^î}ì¡»ÅÎ ‡ºÿÀ þUX4²æ+‡H¤é™Ì¿ú¶^@¾ÈãXVxø}Ÿ5¼…ß›Áƒ(~|WI×Ì”ààì/‚²lÓpýX¸r¤uËvÏWÊËYÓ¼X²‘®™L#-ˆËzßjòÚ•s(F|yÈëj¯P( b"  Éld¥¥¥¤ïX·ú{Ó­·M` ý±‘}s?AÅÌQ%+7 }ã.$ÒîMë>KIIœB2èèN!ÄÌ)ù«LË!n/f5 Ù¼’>ƒN]HbFâEl ‰H‘4°Csvakú˜šf˥ݜ~A³Zgf›ÜªÙ{ðÓˆÉ;ØÝë<r¤ZRÎ[,슖)Wšp»§™jI=ÍJMl_¡×rswr-¦ÇÞ»l¯1Ó²•Žèfëv¶-Iä2LfU§‘³Ã_9…´0„÷ò¼‹µÊÌìÉjC¦9ùK5^`û•f†‡mµÍ¯°s0yì¥êþ™_aƒ¾eg,‘éV=+&[ן(ì{×g¿òJ&Þæ<æ=>%2öñOn/º¦P(•)ÙÌTiRxÃ>}õŸ¿­Ú¼bñW§â.¤ü÷Û%ôñÏ+ô-QÇ8€Á•Ð^h7´ÚñôÙ )[W.ÌƮƒ±Í%³!%ùª8{ZØxlù.–âÄ%õòXMg5êÂ^xúa¯¨ì”ÓðpÝtÎ}ZíZ–›Ï‡‡ß ®·Mœ6­fùÕ bÂ\ànÑ ò¤{fgÊXE§&zæÍ7CÈß?Ùht.Ÿy.|FU-PÏœñ àÖËLì±j¬ÕªOâY¨Om8|¸zQ^ºØS•ý{ç_E•ýñ3¯¤ @èE‚…¦ ‹ˆˆº6tÕÕUÑ]’ *ê IÜ÷7 E÷c¶µ¬®••)*Ho‚ôÞCèÞÞ{3ÿs&™dòRHy—äw?Ÿ›™¹sï{¿w’Ü3çœ{O¤5Hp¼x¶*Ï«çB‹úÁÖsâ^™ºÄÁ?ɾ¯qBTԉʔGšO€ÿï¿*½à¹Ôވߡ,óÌQÖ]•}9†r”…K‚‚‚ ¿}`‡nÝZmöñ2 ô×6VxÉ{Ÿœ;r»ë|Èv:5Ù^6ìc-•âv9ÓíÞµbíâ¿fee¤2 ‰²¢¥ìáužc G™kˆ4)GµŸ|a*31®:µ¨R0­Ø`аáÖÔ ¼y`÷Š•DnðibÕ"fô"pÔãh"lȹ¤r”E]¬{ôjÓ®s×NAõBBýƒ‚BlV›”Cð5,º\Îܜ̬ÔÌôÔä#{÷ìÙ¿c‹XbˆK>ÄŠK„ 6 ACÒDÐÊuËAœÆÒ¸Ó-8"x‹€¦/Zdu(oUz@@@j ÔJ|,e*‡D rO&žr/€£}ÿŽ­8âsÉã“©¹]yÄïBÆÐ<Žb*%Ú ™[‹°‘œ.c/ã\¢ §{-ø¤ƒ8;Ë2¨×y­—¨Hˆð¶(ÊG€7ýÛk±(_•/7r€€@"`LFå«¶!@iò%\ÒE»!Z ;G1½2 %FNFð"dH0  Dq4´"lˆ•Å9\шtÄõs»­ÚÜØI8o¥ƒO Ü›#ìtÛlÌÔ©¡æMÌ*ÝË:^P8º3\Íx­Ö#uE¹»?7>z5g–ˆ  µ€LNÍ6úÆq™|Ô >×µ|”ù¢!p@Ð`>Œ1”q5´S¢ÕÁB4FáCÆØÐj‚ ' ª“¦(y¯É°¢w*v哆Õb]äR]ñj¦*«é|Z±.!·'áÈ»p[lÌÕó®A@@j^}¨ôŠ—Bÿ¥”Þ™ãÜ6d‚jÖl˜µ†°aK©É—ˆ€!,ÈQ4†Vì١È’.ã,ùŒ²|ZBPŠnP\BŽr%ù¤°!Ëò>‰ìIÿ÷ÂF¹†²ôL:G……ké¹p@@@ ¶àeУóûRš°!· !Ã/¡†ÇÄ=§©ÚürEö°QÐp\ü †. ‘ö'Å¢<ŸýæÅK!€€€@m!P…Í|•ðpG »±åi›Íúœªª-ôăÔ†õƒdqÑr ø ÞA\MNÍÔÒ2³-òÑ™Žn·úFk¿Ö3+óÑ9<*nŽt3!>:¢*ÝõyaC:—'pÐ?Y±±÷àx>¥¹î£A4“sôP,ô"ÒYကÔV•6ts©Q㣇Îu«jËž[k}{tP®êØŠ‚°qxMyW2³shÛþã´aÇAmûþDÅj±$9ο¾?ͱ˜û ær—4ÔaCˆð/Íp>Ìå%\Û²„½ãg– Ë؇ƒHöÑÈ[ÞV{„%Ùâ£Á¬F'ÄÅÈK…   PÇð¼éUé2Ï&\¤ë2AÃúäK/³ZíÓZ7 ÕÞßÚ©mó‹Åm_'°ïèIúbÑ:÷ñÓɤiî‰ïMù‡X»ˆÀqÉ\jŒ°!ƒép|pÜyüiÞðo#jÍ“j•7ª;¥)Zw$]òÔ¥ÀoI=Öô´” e Þ4‘y¼%Îõ•Q—Õ%vè+€€€€¾{¸¬6e5!zœÝî?­O·öÚcwRüì’ŒPä:Ýôñü•ÚÆ]‡UuªCŽ‹ï³á¥Î×(aÃÜç11S¯s«î[9­ -¹#!æûuᜅ46’¸¯Gd#Dì£QF}¯9 Hö‡Æ>?"¤a£¯YÐPþ6ò5vnè*µ¸’÷¾ýU-'+óî_Ÿº»zI¼Pµø¥B×@@@@ ‚F} º÷®Í-›††MxüVh4J UK’DÃ1ý£ÿ¹OœI>½gåÆîË–Í« RŽHG\?·ÛªÍ´¡*°¢@Uè¡,€€€Ô<2ÿ“íü{ß|ÓS¼§YsñÑ€ Qó²"-–ñ}èÖþVÞì¹E»ëz<ÿ”* ¨Nš¢¨î©yFIyK}@I™‘    àû"£c‡J,¡¥2÷Óͧø)«NÁ¼Rµ0IÆYÆÛ?0P„ ;GyJ–ÍÆ~ÁUÞ“¯äÊk!\t @@@ê US¢%–Ð_1¡’ dÀ°{ìÇÇ0YÞ¶„|Hª¥d¼yõÒf÷<!ã/ïBµŽ?„Zú"¡[   u˜€¢ñ*•‹†_ NhÔ´ÙͲaŸì£QAö}PÙVË3ü¶ë0mÜyÈ3Y¿ÎÉuÒîÃ'J¼çíDñe¨KAÆ[Æ=¸A}ÙVÂÐnT›ÀQeÕH]ô@@@j0>ÄlÆc€Åfkè¯ñ†}^ŸhŠpñãê­´tÃ.JÏÌ&«ÕBÛ4£Gn»žš6ª¯#ܰãïý RŸîíõkó“èýy¿Ò›/>Lv[妫§Ï§‘Óå¢VMCÍUëçüeŸ>[°†¶ì;ÆíË¢–a¡tÇ «éÚníÈåvÓ®C'誎­‹•« ²Ac‘Ñ–û#ïB.G‘¸ªEêªÜèqk@@@@j*dî§ V›­YÃÁ^4„ÈÿVüN‹Öl£ûoîC½:·¥”ô,úrñzúç'?RÜS÷‘Ÿ½ì)èµ]ÛQÏ¿W^Ð6ü°b3…±`S’°±y÷QZ»ý=õ§¡Ô ^í;vª ߎI4oÙoµVØ6 ëYΜ?ß‚Oå]Ápr¬–PöHWË#Q)€€€€À%& B…¡ÙÓ™«Å`·{]ØmÂOëvÐàk»ÒM}»ëÝlÜ ýíÞÁóî7´ò÷}4´o7= ôÁ¼å´m"5oRŸž¸ë¬ù¡-{±p²Ž^3R׊œ<›BŸ.XMÇNÓ5#q=uh¦×qâl2}¾p-9qŽä9÷Üt-%ž:OØDËßÏNk¶ìçU˜ÐU 5‡“ÎPXÃêÞ!Ï„ÌH$ž¦ÿ¸FׯD½ý5õ¸²=ÌÚ˜“çRèsN?ÌÏ ¢?¾º@##ù[7kDç’ÓiÅæ½4öÁ›ÉnµÒ—KÖs{ÏS3xnèÝ‘†ôÉëó±“çi!k}v²öF„.k}Ú4oL‘÷ßDeõSﬗ~ȸ³ìQg˜QÉ»!ïB¡½›¦ìñÆã,4„’N_(HÜÊfLõëÒC#PjFý—5 rN:—’Î3ß¼¹ï'?¬¢À;{øVjÅ&OŸ/\£çs»Uzû‹ŸX8Èa­4èšÎÔ¢IêÕ© úÓ5]ÚѨ»QûVMôüÆ^Û)ï÷+ iF2 +ÁlîÕ”¿üëånb¡È­æ=#+ÇIÞquhÝ”ÞãrGYðš‘Mó—o¦éŽ{SóÆ ¨~p ]Óõ ÿØmÔ»K[újÉß ÒG2¦>ó'vZêi£GþA?–ÖOý¦—° 'Â…ñ>{â£#$Võ±Å*®j…(    pÙ ¬çH4‚L,% Õ³ Q*›LI¨ Í?‚(%#³ Iû‡õ¥þ=;ÐM}ºÒÞ£' î'Y9tðøtuö5ð§½:’hDHØÏšJîÖ‡úõ¼Rט„…Ö×5 6Ö.4báA–{õl‹øŒ{x8íeó©—gGßþ¼‘ýG4jL¡,hÈžR®E“†´ÿØi:ËϺÛ)þ%ýq ‰ßÃê­û&RvŽ‹‹Ûéæ~Ýu!‡Í”ø¼›.\õêØFXvÊëÛž#§¨s»æàoçg4c!$—XËDeõ³àA^=Ñ aCÞ ãýðê¤2˜Qy)*ËK !.fB -I¥ÅQ¼Z‚LØ%¤ñÏ ˆÖ&‡í–<™7‚˜2IÌaÏᓺ ð¯ù+ ’e²Ÿx:™²rÄ·™¨]‹¢š‹‚Œeœtkß’^‰¼—¬ÜÂþ%Û©^p гX‰ä´<ᨩÝmš…ÒyS;E"&[Fض/‘~\µ…NOÕÍÂ$Ýåvé·»whAK7î’¡hù¦=ºð$7ÊêgcÖ y;°peŸÆ;áíGèõAب¬¨@@@|Š€L,%˜&˜úd3/Õ‹?7 Ö¿Úï8H½Ù\ɧyâ}æB* ëŸçÇ!é’f9—¯ýX³pÐHäc€Þtõ9ÖD´mÑØt‡t_II½áÃQ$ÃE.D@¸wh]{q€54 x1ý’pÒôŒS¼ÒUOöç0‚y÷uñC 1#ÿøíº5á­ÿYiÄ ½hÖ׿ÐÞ#'én®b͇„²úYPØû'†VCj6Þ¯>E€    Pû ÈdÒ˜PG¯÷ZÌ—d½ò÷½´xív(Òtó¨„o—êfJ{w*xæùÔ ZÅùNœI¦5l–Ô™M—ä‹¿9thÕT7ƒZÀš1’}1¶8®gÃßÏF‹ØáúÔ¹T^²6Iw—›â;!ŽàÙìk!ûv˜ƒh$¯<_êJ<}^÷õ0ÊÉs.ð=1m-†¬Xµ˜µÒ—_x9ßä´Œ"çæºEƒ‘ʦb Ù‘\|W¯Ýf¾­—ïÈ~7^ÓE¦Ý–§d*«ŸE*ðÖE¡ù½(¨=Ò×otÌÔ¾ •<©6Z%Ûƒb    Þ' ˜ÅÎÇŸcÇz½ ¼­yXã°ëy¥$o‡Nìae¡ãçu;uA@öÔÿYqÉðŸØ¸ó0]Ѳ ‰“øü忳©у·ö×5Ig.ÐæÝGèöA½ugjqð^Å«XI¾Wme!⬾º“8Z7oÜ–oÞ£/µ»‘Ÿ#…æü¬ÖZ¬ú}?;doÒW°’ŸŒ°‰ŸýÃÊ­º ÕÄ3t]·ötçàkô•¡BÙßb7›n}óó:ΦZ׳ˆÔ¹jË>úþ×M´ýJD;1øº®zuÒYĉwæÖ¯ÅÿBåΈp²põ66ñj¬÷­= F­š6Òµ9"ì¬eájÙÆÝôëo»Ùéì·¿|Ì—•EEÇJWƒ‚    ¾B 2:v¨´ev\Ì/ùm’ÌeG=™u7ÿ˸ oõîÞ©Û ŽàËê Yì-æQž ó%O`€lùÖóüw½óÒcúÒ·Fºá£è_˜×¸'þ ¬å°åk $]˜,^*ˆW¦*)H}%Õ%y3Y«!m2·[´$bzUžÉ¿ËåÖËʆ†Fز÷(}ÆKå¾4êv^¯§k\æ|³Œ5'ôøƒŒl¾(¥µ­ c%O^ÿd!mÛ}pëÇoLý W!Ëj‰=›û…GÇ.ãkbÿŸ!r¬l€ÏFeÉ¡€€€ø(US¢ó›fž-½dœÍB„g#Œk#¬%fMëw¤¶¬‰0OÔ%oY“ïz¼R•g¡ 4Aãbõ•TN„¦ò³Ðc.£òRºØé\ú*ûpˆ–F–Ì5‡²úiÎWÎ!lÔ„QBA@@@ "”êÙC£"M¨L^ÙÓBö¡Y·¶…Þ¼›úIö-ùaûŸ¤¤é>,÷½Žý7:×¶®ôÂF œ€€€€\Nâ\ý·»/gªýÙ·^ß“$Ö•PhDVWzŒ~‚€€€€\Ðl\Ìx€€€€Ô š²Ç­­±ÂƘ˜©×¹U÷­ ¡¦h-Ùÿ§Ä­Yuó¯Ä0× ‹Wh­=bN3ÎkT~MIåEŽóRG^b:ÁµÉèŽ    à+œ.©ªVd—mi›ì¯±i/q;°W‘UŸŒvËÞâHݼI#©ZŽÒ>1á2¯<å­]ª>x«½F= ñÑÆyUŽ5Nؙҟ4õ+—êjÃ+ ¨¤)§@Ç¢{ÛW…JÍ)+[WöãߦšS›wXS,ãæÆMþ¾æt-¨ë«¡Î W¹óàqúê§ tòl²¾ m“†!¼c÷u¼¯Åz]²™Þü_7ë> 2Ù÷ ó—o&þ¸L÷ õ¼U®k—ÛÍ›÷à]º[—˜_öóøú§únÞV«Bíš7¡¿Þ3˜°ƒº·BUûà­v\®zjœ°¡%[#•—qS~¶[ÿ7kÒ¤ …—¯é(¢í(«_Ïÿœã†YîÌ»TM½“Í{¿eAÁ=Ÿ%À{#L¸ÜKäe]g|¾Dßìîo÷üd§ìå›vÓÜo—QÀCèǕ…€R°±yÑV?q÷¦mæŠÞ+ÏÕŽI4W·*MØU¡d—ïIOþQß-üð‰³^4¤UíCyúéËyjœ°1'ö¥- t”/C½Ôm{Óñ|2?óãüx©ç€€€@1²{vCÞ{Ô]ƒHvÕ–pÿ°~´ÿØÞ|[acåï{õ4 ÛÅß5äZpÕ•zþÿ,Z§›_ýåŽôkÙùzïüž™MÝ;´¤Gn»^ßG#×éf Åzú7ÍS¨¾ÝÛÓ5]ÛÑ¿y=Éõö×ü¼Vô0ç7‡C¼ÇÅ ¼Cx«¦¡zrçvÍ nŸ<›BŸ.XÍ{aœÓw÷þóˆë©ï¾eï1Z½e//WÛ•þ³xõêÔšw¿@²CøÝÜv RvæKèIÖ’¬Ûv X®ÞJ)éYÔ¶YcŠà]ÕƒyÓÁÒúVРz’7ò>Üx‡£f®íÃHÑ4¨v2ïÚ®E a<°[û$æSæðÛÎÃôÀð~ú¤ÿ³kHÌŸ$¤fdSZ¦llM”ÂáýkþJtM'ŠüÓP}‚¿|ó^ýžìÍ!éÓulÓŒ7æcSx‚u禾Ýô¼æWwnC‹Öl§Åk·“øm˜Ã'?¬âÄí4îá[©UX(}¾p~;×édÓ¬$dV³9X;]sse릴š!#ü¶û0år}í[†éÃÄÓzÚ²¹Ö¨;o¤ž,¨ˆ QVߌ:/õ1Ò×otÌÔ*ovâÓÂFÄËñ7wÆ§ŽŠkw©׆ç…O›Ö€ý8ÇÚÐô@@@Àkx™ê ©YTÒ®ÞÁ”ã,2¹¿kÈ5t-k"¹í=ýàñ3Å·m"5bÁAò5bIOöÃØ¼ûˆžoÝö<ño¯;š÷íÑ®îÒVß0/”óûÙ­Ô©msjѤa±:ÿtK?ºõ†ž4oéoôò¬ït­…dÊÈÊ!ià«»PïL>€µÇNž§sÉiz¢Iß“{nºŽ®hÙ„ú³&F&$lÚuX×®XDUc ë·dÆž¼{°ÞÆáòöÛ(«o¦â—ôTuÒEuO­êC}ÖŒÊáXjKr®šÁ<îl&à$Ð2{bZÅ窪:“yör8n**²W°>d¨xåÍ¡ÒRö;e?×’ƒËíÊÎv:Eè(:#.9{…SeðÔŒœbåÒYS!n»­pÚ2_³«@?:ÄýÎ, ˜ƒhÄôhê?&sË].7›JåPÛæ ÓËy&&W¼ñjº®ëôÅ¢µ”ðíRúGø=”xꂾ –hRŒàϱ\Ï "ì¡i£úºÐ±™5!AºÖåÑ;· ŽçSÓÙtª›z$é'¥õ­h.ï]eç:5v¼Ï(³FE+ 23–}Ó+•”ýˆÊÝMr¯zZ#­; „·Í‰ˆpV®–º]ÊáPÔÑ/Ç¿@nmÁ ×Ê1LcfÝ&‚Þƒ€€@Ý  jJt~OK6t­Fnv΅䔌j6Z³Ä®ÃIäv«dµÓl?p\Ÿp›Gâô…Tº"0L²rrÙl©¸àÀBˆh*âÆÞg.ªŸ‹öâ ×QÙÐ"¬¡®m˜8ãK'ñzlÚ$ṇ‡SÛ‹T»Íµ$ˆÃ»9ôíq%ýúÛNÖ¨Ô£fó„ó}9¯ÇZ]'Ox&SY}+–Ù É©š37WlÙôwÁ U–ZEáÈ—šå2ÝPiK›‹çÄÅ,¼L-¨ûJÔüÁb ¿I/ÖŠ¡   ' °KÏ‘tIDATϫĢA&–ÆäRËÎÌ8“–•£df×>-V¹«7ô¢46¥šûݯtôä9’efÅa[V©º}Pï"•þ²~—.,ü¸j ‰éQ§¶ÍŠÜ—‹Þì_q–͘~^¿“rr]”ÊZŽýÇd¢ní[²éÒÝ—B|E¶°£¸„æè¦OR3tÓ(=1ÿ‡˜rý°r‹Þ6©wE¾ÿ‡”éЪ)›€Ðn˜N‰Ù”Ie…~=®à>¤³ïÈê×3ÏÁÝ3WngrZ-Y·ƒÎ§¤ÓZv_‘²úæYGU¯e¼eÜs³2óà彯{QÕê‹•÷IÍF¸cJWÍ龂C⊵ '`Q¾ÐTí}ášà˜¼»â €€€@ '`L&å¨J<ºoÏ–Ví¯|pÛþãÔ¿g¯wO´Ï=2‚¾ä›¦~0_ßg#,´>EÞ?”º\Ñ¢àyM…°_…M÷™Aã6kò÷+>EíÕ©¾ÚÓ÷¼/ÇWKÖë«N‰Ïƒ8ƒ?0¼?½ÏBÍ[ÿ^¬×+«YõîÜVŸÄ¯ü}Mšù•¾úÕ3¼ä®Ä”iû,âU³œlŠÕž}/¹ý6ÇÊÓdDÜ7„>ùa5E½ó^DL¥zDŽ4Š;†ò Y­hÇD 9¤Ø}IèÇþ$b"&>"ßðþ#"Ð\Ó¥¯hUzßJ¬¨ ‰2Þ¼Q¢’xðÀz®FÞó»Q…šK.Z|$KÎwiSÝêy  @«áòVÛâl5—xWœá\„ /0E   P èB·[ŽîíÖìïó­)lTŸ… /ïô® /%3únݯ­ï ^tê)ÎÜ%ˆÀ`eaÃlr%»‡›]JnãÆ%¦ñ*U²R”±`ãõh¨;tûªê>!R§/=qe²Ã·ø‰˜ƒ¿Ÿ]/#½6<q*eÌH³. âK"ÁÜf=ÁôÃ,ÌÉ‘¼´­DÀyèÖþô;¦‹º†ÿFi}3ÊzëÈã­iªš¼qùÏ2'4¿ÞzD‘zXyàƒAÓœ ~Þìèè²õU>Øt_lÒLÇÄDæ¹†ÅØâ¾Ø`´ @@@ÀÛdÖ.Q&—²`ŒøÃºŽîß»xûþDeßÑ“|Y}ÁÆþ ž“yϧ‰ß…!hˆ‰Ñï{ŽðŠP§õý+<ó†æ{ò O¡B8ƒ—$¹¬¶‰a%•¯Lšhp¤%5©´¾Uæ9žedœe¼>ô=ßÓß>Ê;a¼…E4eI¬b(™z+Eqß# ê2ò"!€€€Ôráѱˤ‹¼“ø9r9ŸcGY6ŒcÓ  ~æ…×Z·hRÿ¥'î´È„ß‚øG|óóêÄ&Rw¾Z×PøB»jrÄïdÚ‡óÕã'Ï¥üç7ŸLKK–ú²Æ°,±•ÉQ„¯Ï‹ê²ø µ“Ú9®è€€”B@ìñÍA&‘ºùeR™Í1'33-}ÓÊeŸX† ûñü•ÚßFþÁ'>D÷äݾ%"x€ŒoÒ™dÚºnÕ,4Ò¹fyä] íŸz7ø†øêÝ>¡6¨Ó~[¾t‰D"HH”ùŸ|p'¿“ÇŽ¤4lÒDËVü»<›¢õìØF1Ì™ø>B ' ÿ»BÛ¸ërxÏÎ/V,ø¯xѧp”µ‚e¯ :DàðºVƒëÔ_69"€€€€Ô~"lˆÏ®!pˆÐa;´{gbÃÆMÜ,ptÙºï¨Ö2¬¡"N×5›€øhÌþúuÏ‘“th÷ޝ~úæ‹ï¸G"hHLã(æSâ/ÂFµŸÔlÈ®—}m¿qùÒCÕÒë:Xi¤#®ß57ÞÒrÓòŸ“ê`÷ÑeB†Àa2´ŠÀáv9“ê5jÖyíöC¼¹,Ô¤4â]½Í»}Vƒ3_$ ûhlÚ}T–×Õþ»l“’’š–úÛŠ¥sW/úágn¯â£aÖjˆ)•˜Q„/¿üÒÚ¥WøMwÞ¾{õ’%r¿ÒÁ'}6.²ëe¥;[— ªNš¢ä ­Ãê2‡c©í¤kåJ°íëY“&]¨Ë,Ðw:I@Led5*1ѵùG}…Ò-kVnÚûûæ}×ßzû·Û5˜W.ª/~Ÿ!þZÃÁJ€Ý.‚ ‚Èv:5Ù^6ì“…xüÒŽìÙ½tÍ¢gf¦‹€ah4Ì‚†¼ Å̧~Þ¶ïÏ,iÎÎHumäû¿U¥»>)l”°ãeUúˆ²B@Ñ*4Ö‘/ÇÝëVé>~ýzð²l鼎ÕVRüÞœ7aŸ7€†GÇ}¡²yN\ÔtoÔWÞ:Òƒ7ú»“)Á/SYÍe l”ò€Ô81ññ† yEª—M7;‰gqºº ÁG¹çÊÊÊpþ2ï«|¾¤óUW_Ñ®s×®Á!üƒëÛ¬6NGðA.·+''++%+=ýìépYlo&ľ´Í(§‘6”,W©ŠÅ;v¶e>Û4ÅúÜÜØI$„g3êg;S§óÝÆÚ¡†šB›-6?Ç-6•$c’~.ówÅn¹›\êüï¤?çÝÁíœ7¬W§7xà‚.1q‘<6Ïp;ÛrÑm¬ÅúûœØÉk¤£Åò³FîÉÜævœü“Õbù@¾ŽE¼³Ûíü·±!ë>)c³Ó£³Ñkå@@àòHˆˆpމ™éRÕ_“œÇ? Ÿ3çÏ’–ß2þÓ^ pç2I•‰¨LPuÍEØè)l@è`(>dì$ÈQFù?/c,ÑEè0¢¤Ë8K>£,Ÿæ-Íõ!Ï ÚXmÊÿ?^”*‘P@ €¿u=ù¯ÇO ¦]›¡hëX è%ÉV·ÛÂêÙ..Íù-)Êgþ6¿\öCÒ´øÈ˜øQ’Çf Ê/í7<Ùý&ØÜ×ßÖ`˜¤óûÝ‚ó6Î;×…’§YЈåz>`ç³®,TÌáz¢Â£ãÇyô2ªöçɱØ,·+6å~®·¯ÛéN0ò8Šü¢çg>o­gmÏçs¸O³Ÿ™2%ÌÈSÖ1Ò1õ ·æbA-vá§h×ðÄÿ-)#÷XÐà{Ên›bÈ¿<£YØé˜éJ_(ŽX’‡›ú|ò8GZëh‹]Ìu5P]ÙXÐø›EÑÆó½ë¹M5Õõ©”‘ åøoÄ\Ëcü×üY?‹Mò!Õ½4ÒñZÓ¼\üWÕ:Ÿûv› %»e€øÒ¨.mÅXǔΒGÆ„9uÖ\îoù8‚”Y^ãöýßOÛö=aÔ£ MVË?ül~]Øùo‰ªªKžqLo-yŒ~h¤þ•û3Ù ù!·ªÎ{Êñjs¿&!+9ÛóÌ&EÆUb3¨ <Æ3p¸üô‡zšÿ§Œ¤£g¾c ‡h+ŒÀÉ“S™˜Êä2•£|@;ÇQv—–xŠãIx‚¯/?Ïq‘±2ÆMÆPÆRÆTƶTߣ§Óù©v/Ï þ>Ûí¹)¤d©TðU͆×:X)*µ±¦ì¹X·^|íµà”äì0²XŽ––W!Ë1žì^i¾Ïˆ¯çÆF}Ÿ6Í­z°Êö¾þè]ÇØtþòËç9Æ×sY㜿°G³€1—ëÑ'öœþvDt|0Ov£yùµ× ¿È++Y2É(7:&þ…Ô9Ƶùþۦ미=19YÚÝœöž)½ÄS·K}׳Â(S†Cr®:Ý¢•867.J´/v=»'G£ƒ¬ ¹¯H"k<¬¡!~½:q¢ØE ^sݪö¡bUFÍ~%z©ž;ƒ…«Ÿž™1Ãæ³ÏÊ/?ËVd³+Y;°U®ù ÔãtìôyÕ™ó$_Nã¯S7ºT÷`›ÝÒ{–#JÏÃéãFGÇÌuªÂ¤@˜`‰ã5Öæ|Èi¾g‹¸þ|®3Ðy+Êsâ&-Xq°`‘ír=Ê—S%ÇÙn ¶=l¬ÚÅïGlJJö8Uuö~÷Ù ‹Xû‘¡’[+k\õzð@@à²+ˆèØ,þöÞqWÒóܘ¼¿óy­â䂯ÜòÁÎÅQ¾~Ë—p™' þןÏò"|Œ€Œ£DC‰¢¹±4ŽFºK o;&&2¥É;“'‹âµà“›ÙLðZQ‘N !>:âb(^{ñÅL6{Êf㤥çUåÞyó}«¦ýl¾Óv(¿ÇœVÖy¸ãŸM4gV3þ{ö“9u_Ì_Û§-Ýy §”{l¶³½HRñ$šµ…aLÌÔëX;q7ÿƵc3¦PþÕcIŠøŒæö<Ózp}K?mÎ#ù¯ÿbîë­z¡ü¬õÙa¾æóC¬i+iÏ;^o”îÌhÅZ™;Y,i¬é¶œ…Ì‚d’!hHQ§²CøMUBä@@ æPá1SvÚ­¶#¥´Z&¡E  ª!\ÈÑ8—{¾K€ÿ8äÚW9/b¥ ï:&ˆf¤ x[Њ}RØ(è1N.)žHk¼ÕA~/»–ú`MéÌSÒÍ÷UÅ*ÎF…AÕø‹ˆ"_BÊ‚ÈêÊàJù/™h@ ‚fÕräWÄBE–íÍ,ÈP kC&º4×Kü×p2ÏòÚí–³N—ûÓ¼¿%(–¤ùó¤[TÅÿ–ºX‹R¤z&öGa¡Æô»¤k#·'×áx xYÓSø¹Ù2¦$‘Tœ¢)‘4æãâ¿þ¹žyØLŠ•+E±9•­H4.dÔœC.?©Ë}g±ÐN9ÏoÛ5k’qÁ )R‡‘Ž#€€@Í$;ygËÃÓÚ’Ë)Zn™”Š6£ °…p²âJ{cå16úýȘ)ùe½š1þm. ©ªþåÝHc¯í›ùü€q͞܏¬Ä f8<ÞÏZŒ[ÌØáþØžãlÑø9½¬sÖ¨<Ëÿ×X;6kn|ôêà@ë Îߣ¬2æ{ü¼½|=Ìœfœs×óä¿HuûFÞ‹DµÒ.#_¥5Ÿ0}zÖ@vïä¿ìCø™:KM±óó©u¸cJaMÍnáqÙ]Þçê_1æMç¼µÄ߉}ÉS#Rjµì8ÎÂ>X” 7@@ °úk,(l5 ”¸÷TÃFŒ/ã¢íp×kÐÀm±X´ÒbÓ-ô|’ù‹sªn>¦ñ*úñ2ÿ]ä›I<'›GŠe²bWFä'WëÁô5¶ZŸƒÊk–öV¯óŠ&¹—Ž™úL+ë•[’*éê-ìü.;&¿šà˜\tb«h“xÉܳ­,“%¹§÷ Õu†w]æ/ê+øÅŽ‘åÔ^z)ÅH/rT”œïïì°]kÓx>?;œ—àÃåf˜VÎ(R¤¤ 46³ÙRÙ)ü,­oz!µe­F b³$Ån¾ªÃöŸ¶ì]Êmx5'ñ>"'C=žáJ?årñR´ò…¬»xyÚ‘®ŒclÔ´'ìÍ­6ÛˆŠDƳ<,´l`c§ýšSÝÈ>É܆IŠÅ22ÁñÒQÉ+Nòþöú÷pœ¼’ÕªgêIÖj¼À¾-UtÕˆa½;¿Æ†Yoó8¿át?Ç>3gUí¶Xy¶«´kÙömºª)ß²ù]bøËñw”–é    à3xâò‹DŸiP-hóüIbE»"ÎÛ¼‰Ÿ81 ²zÕè¨XmlÌ4ÝLI–hB±Œù c¦N 5› •–o\||3Ù/£´ûåI—v‹Ryò––G–˕բJºÿœã†²§EI÷*“&Kóøè&LÒnaPV=¼R•]úXVžòÞS0^}¢qyó{æs8¾ô oᙎk(§qÉ¥Å+ä,“'²Ýý9"T@u0Í_*7ÝÏbïY[ÿª÷¦öÕ Â†[S'Ì‹é^ûz‡€€ÔUUúz\]ÐØFVÁ)ñkzu=³Ö׫)Áìhœ^ëû‰‚€€€ø Ÿ6xR|Z#¥Ú—âò™Q¸ QHÌrNyóQõ2Ú9ys†¹¼Y]‰+XxóYµ½.Þôo¯EQ¾ªíýDÿ@@@êß4£ŠŠ›ÌN°±Á¶à°7/Ù@®n wz+›¸±Cóâbxs¿)Þ©µ€€€€€@Ù|R³aµÓ/¼z‘%Õ=¸ìæãnyd¹³† O^µÂâå©y@@@@@ $>)lÜÔ½óÞ\íÞpä@IFZ ðR©¼YÏÚæµ±‚%‘@@@@@@@@@@@@@@@@@@@@@@@ r|r5*Ï®Œ~9þ6EÓÛ¬¡ñï:Æb¯O@ײÙ^jJî VŲ`Vì¤ß»ÅúDBìäu—öuÂÓ@@@@@ @6¤ÉLJIÎãÏòé£i=y9׌»½ëLÇÄD£KO9^mîræ¾Ç›Ö3ÒŒ£¦)É¡õí¾:qbš‘VÓóGÄLë­©®uÌß…Œ²|â×4äÍ™Ï>›côG¸l—ã¡•}¦ÃñD6—}UâG|/Í¥ö¿±{û3+[a-(Rߺ?-Å9Þb³,íˆÚ^ º„.€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€T7ÿÑ"*œ;}IEND®B`‚neutron-12.1.1/doc/source/install/figures/network1-services.svg0000664000175000017500000014032313553660046024616 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.5.2 2016-04-26 14:56:09 +0000Canvas 1Layer 1 Controller NodeSQL DatabaseServiceBlock Storage Nodes Object Storage NodesNetworking Option 1: Provider NetworksService LayoutCore componentOptional componentMessage QueueIdentityImage ServiceComputeManagementNetworkingManagementBlock StorageManagementNetwork Time ServiceOrchestrationTelemetryManagementObject StorageProxy ServiceNetworkingDHCP Agent Compute NodesKVM HypervisorComputeNetworkingLinux Bridge AgentTelemetryAgentTelemetryAgent(s)NetworkingML2 Plug-inObject StorageAccount ServiceObject StorageContainer ServiceObject StorageObject ServiceBlock StorageVolume ServiceTelemetryAgentiSCSI TargetServiceNetworkingLinux Bridge AgentLinux NetworkUtilitiesLinux NetworkUtilitiesShared File SystemServiceShared File SystemManagementNoSQL DatabaseServiceNetworkingMetadata AgentDatabaseManagement neutron-12.1.1/doc/source/install/figures/networklayout.svg0000664000175000017500000005461613553660046024163 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.5.2 2016-04-26 15:08:44 +0000Canvas 1Layer 1 Controller Node 1 Compute Node 1Network LayoutManagement network10.0.0.0/24Provider network203.0.113.0/24 Block Storage Node 1 Object Storage Node 2 Object Storage Node 1Interface 2(unnumbered)Interface 2(unnumbered)InternetInterface 110.0.0.11/24Interface 110.0.0.31/24Interface 110.0.0.41/24Interface 110.0.0.52/24Interface 110.0.0.51/24NATCore componentOptional component neutron-12.1.1/doc/source/install/figures/hwreqs.svg0000664000175000017500000012130313553660046022531 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.5.2 2016-04-26 14:57:28 +0000Canvas 1Layer 1Controller NodeCompute Node 11-2CPUBlock Storage Node 1Object Storage Node 1Object Storage Node 2Hardware RequirementsCore componentOptional component8 GBRAM100 GBStorage2-4+CPU8+ GBRAM100+ GBStorage1-2CPU4 GBRAM2NIC2NIC1NIC1NIC4+ GBRAM1-2CPU1NIC100+ GBStorage100+ GBStorage/dev/sdb/dev/sdb/dev/sdc/dev/sdb/dev/sdc1-2CPU4+ GBRAM100+ GBStorage/dev/sdc neutron-12.1.1/doc/source/install/figures/network1-services.graffle0000664000175000017500000000770413553660046025432 0ustar zuulzuul00000000000000‹íësÛ¶²À?ÇO>Ý;µi@0ÇͿڸõ+‘“Üf4s‡¦ ™ Eª$Çíä? êÁ§¹µY^}°db‚ v» îüçó04>©$ âèÇçÄ´ž*òã^ ~|þöâ§-ùü?/7vþup¶ñÛù¡1 ƒ43ÎßîíÏ·¶·wG£Pmo\çÇG ÊØÞ><}n<¿Ê²Ñ‹ííëëkÓÓR¦µ`º}žÄ#•d7ÇPØd0{Yï9œfRz¥:p´øÙËg;ÕÍË]? >©cïF%GQO}ÞÙÖG!1ˆ25PÉKkg{ös–Îø^E¾›”<Ïä%‰§<ÛI³.ù%ÔÐŒ‡Q0HâñÈ<ƒ_?'^¿*±³=)IášÄ‚êS.\ÉJ";Û³¢'Ugñnï÷qšÍO%cµ=Kßóüú”QN7º ü¹ØôÚ§bZ"¥Í«ñ×_Ö¦a}Ù4þ’ÒÞ48q¿|©TWËï‡^ÚÌډà8g-ËÑA!?kSZnÞ‰X'» U!9«ñ$ŠŒ?©¥äIúAâ]Õ**vzV®´ç¼!f¿Š“LÕ‡8.Ðö½è“—ž%Á (z¬)ó–,ßÈ"G'øS5äç-Þȇãa´ƒ¨Q'Ò¬S.Ýy>1—O”¾´¡ëå?f¢ðîË^Ö¨µÛ"d‹Xq^ØôãÆ|µÓ…ÄI=ÿ‰—eƯÐQ¼k/ñꙂtz7ß gU…áùïz?¡º¸52{ uÙ¼SÄþx¨¢¬®·ó†¤–œvæT›–=¯öΚB•4 Z´”šØL8Ìrà6SJ@Ý©-,—b;Bæ:ÇMËvmáriêæ‚J²1.8uËu©jd›J:yåT›RN1޲£¨ß®SЙŠ[[MŸ\–ç§µLfKnU Ý,Ãà– –C§-CÒžÁ¦Ìµ3ª]ºÀ‹p6RQNjҭŽ—qØ«“›¢’Šêc¹ÞȹþTNV7q%çV­ÜDôÄD*+߯y¯zv»ýxVBA«½lܽ~†wݼúÝ­H´Üßi+X¦ÌVñá¥vi½Ñóœ.w\ÆŠœ´%gÒžSºÌ.åd•œ•[_ïé•׋¯ïj‰=»jëj¿v¾ÒËê`zÀ†žèQqÑdᆞ(ÔÝ9Û:×,îŸ[zvqI¤’7^/§•kl¨Ì4Ã9 A%QE¶•g5-«)Ü…úœÝÞ÷«©…Vu“¬Oº üAþÇ å´ëÇ~ìé$f±É?éøþgÂÚø«Û;’]†Ý¾ÕíG|ûW^’ªÌ2¦äß_@Þ×÷2ü»›¨å¼;H”ŠôËp¬à;Oöä8Sêã„PÈÝyI¯›}æÂ‚¿p,ÿRQÊò/ié/ÛÎEl7a,áV.¹õ— ¹ˆp@D—$‘—/ìþáëY0¸ÊTÔ÷|@¸µ±¡¯ð²ÛO‰0º~Ÿàx—àu7N¼È(MÐ/_s§G#——sB¤Å¸´„” €Ç¹ Gˆ%æÚÒu‘ŒkIF)Ö•ŒàëY\‚íå‚Ü“Œ6¸ƒ6µ@¿yO2It.ײ%’ñÑ‘ñ{s<«§Ì¹S•]ÇÉGh9 ʼpÏØ<0í\S2 >®í ióMƒÀAK#Ða ù”¸;¼dޏ{ì AÜ!îw5|ꀌ;¯Ká`G%Ÿ_= Û†I‰tLK  ”TJîp דŽÒéˆtD:>J:@ ¢†~(itnÒL —4`ÊlmU€‡.¥:F’Â1)c–ks@§”Ž@H®çˆ©‹DH"$’kɇ#mâ˜P{›Ø„X@D‰€|:€”H$q·¸;¢ñçÙƒÅîÆÛ,ƒ,Pé’§Ïn›–cI@s…m;6Ân-a'pþ Âa‡°{ʰ«=Ù=Ø9÷ì„Âa‡°[±É¢ß¹b)aeï%Ao |âjý©£ôº®I¡“¸ŽdzÁ²g®é *Gô"z½ˆÞ@oÐÙï^2PÙ2ž¶Í¯iNLEÒ­'é’I‡¤Ãù5’*TC•%7ÝoÛ6)•6àNêlp›5¥¢TD*"‘q+À¸½0ö?Hôª»ñNïu§ŒåÇÜÝ4 K™B¸TÂõ9 ¬.F\SâQ$‰‡Ä[â]þ®ü¬@Þìÿoƒ<›˜‚ÛœÛaÌnƶ¦ÈÃÝiyˆ=ûš©~PüãÚ 8« :ø›EÆiÜ»ý¥E‹Ÿ‡žŸ¿ú¶ÅÜU×\=ÐòJ‰Iô¡®%±Œ§Öõ­G¸|ã)Ä"bq՞ѼÚ?ðgbm«/øÃ§bkŠ::DÔ!êp’æz¬g?OâÏ7KXÍÞºÂsDbI‡¹ ò·ê\SFâVÈHd$®ð{ô N¼09\rüØÜèERDãz¢·ôD4"1||¤ácâ_)h:/ â‡Üð'åÄåµ]áæCªµ×È·Ž¨ºN‰C$>&$â~ŸˆDD"><\¡‡‡ÆE°”7µ ‹6£<ŒýÖtgÉ ètû­Å[þ–24Ú6‹´FBÇÅ©5kÊG‰|D>"‘v«¶ öRPWòlÙÛY”t8äù¸H‡[`#étHºÚZå[a'‚>!ÎáŽÖÈ9ärn8w4º-ãÅ}m„«¯|Ç1Ë5%îN„CÂ!áV‚p=ˆÝ‚ìf‰pãbѵîm33n n¸4 á†p[¸¨4ÕÜë±?døÜ¢s(°Ë²¹+àt\3Ÿ…"Ý8·ó)™D¯Ô£ù¤-Èj=çµH‡e30}£ÒÜØ#«x\”ŠÄº7™xøA—¹yæ2·Âü;e¯´õüçF³§ú™w fnj?õŸÙ¡;l]?¥Öt‘ÕH¯®òBÇ£8ºãy̤…Þƒ}i†ê ø;†‘™NÃ02jº.%pPrêZ“7.šE4‹”q4‹h—gÁUWßÙ$VÆ3ˆ «}Ej‚m„ €K,AEµMlî³¾ò£!×mý•ü=sósœ]yíc}C59âþg-Ç'¬…Ãärœ«?‹GX¸—;Žn,c9ÎCUf™„;Ì‘¶­GŸ™ƒFõ[UŠF*Õ¥ ÿ­eq¶©g“Wp¢ŸAðMƒ™R¯p˜E“`µ…RÜu‰ÅïÌ.fÝl»Ã u AªM눦Ãxyaœ'ñ§ §’YjÚ½Oñó÷½L§®ÇÞM<þ>0£Ü#$™ã.˜+Xs6·#Z^)~ܦA3¹Nj'ŽZò‰½RÌÆWŠá+ÅÐcÂ]!Öé%dÕåWâ]dm”ªHNš”b„è½xiæžµ ”q‡Jû1SŠÐûcÊBL!¦Sˆ©õÁTeó¢ÕxafmfuËVGm[Ú`È8ά~ä3« .™ÕHSœY½ 3«;¯/ó.½Tu7–°@–™Ä±)…~Ë\ÚŽYù"`™©£1Çv¹#N“éQC-ÁÛyZƒ†Ç` ƒ1Äâc5C+˘ÍÎŒ Ã†¡~ÊÑÕR‚«ÂÑÍKù9 zeë=¿½óÔÎÈó¡ó3L:5e5v*:ÓÔÿ=NZ³ÏƒûZs›|_§:W!Þn&Ò‘7J/â䋫ϯj@èz Á¬šgl5)%éwA\¼¸å4:ñÕ9DÇsu+•òÕNP¥÷ÒØâg*iˆ“†ø¯J.âŽï•jÑ÷ÂTmÏ$޽•gž45'C_ø×kN$O½¡jJæ§1HSüþ]t–û@]Uô¶N;yx{{·Ý‚¡—©F§¨=ºÕ©~øñIiVëâDÖút.ÛQ Îù«{j╹K¹|˜Wó0Q³.½8kTFƒ1>éc Jˆœ€ÎÀ¾gÕN˜ç¿G³ëx,$_U ¨âÇÛ4¢©@Óüò)hDçJ•âøIgž÷ô“¸ô?¯õAéNÏ ¥[ۢ ì/,×øA·\ã\yA%ý›&ŸxYv¥®_Á#ö®¡}êOãL-~IgIFºÒÈívz& ]fáµ½Ùƒûô1]@V«èíŠsÚÙ‹³,žx Ԡ襱‘iyý0öª(g)ŒTU¸0C“3¼Š“àO½4 êDµŽ×<‘ 쵞ho7û³÷ê—ð·÷ÄRÿ·~8zï²×{¯:‡W»Wgo-N.ßÿúƒ}ø¿wö–|¸º|õ.Ü…ôýC~qAùãÃûSk÷èýánçùésx½ÿóÝÕ?VýìŸ4¶*w•ÁVR1¼-ÅO¾ZËÒxËc œ£Zµ£1ßlæÚq—›Ôá\8gƒptÓŒ˜Rºà pË‘àê}ùJtG|£>°T‹v<†žðFûtËì ñhÅWmy~#Î"¥­È-~Ååõ΢ðæëæåM|½ƒhwDk®á„:váIN]BàÀE5-á¾}òÒŠ#2É1;¯†öŒ'îÛ®ŸŸñÞòÌÚð"_Ý3ïÛ(øc¬Šˆ;®þmª£,HîjöwíŽd³°÷A1÷íV}œ$ÐyK6ýð7üðóÈ‹@ÙþÒÄE%Jží$K5®²6 –/÷'®Ø4„ãTÇ›æÎ~eàh'KÆj»’þfpnÀݹôJÉóö«¤¿ze_êŽë…ë„Þå+5YP xxÝ{šúT W¹ $1íòg2Á˜;Òå½i@ŒÙÒDâxX÷JM^«‡zçA\›¶Ú‰–ç-šó¬µüùÁʈ[a§6¥ø… D/7þ .ß><'neutron-12.1.1/doc/source/install/figures/hwreqs.graffle0000664000175000017500000000767213553660046023354 0ustar zuulzuul00000000000000‹í[WÛ¸ǟ˧ÈéëG’%Yî0Åm 3´0„¶gºXë,ã(ÁSÇNÊÌêw?ÛÎÅ×@RHIÃÎ ñ–-ËÒÿ·µ£ËÖ¯_z~ãJG/ ~yN ò¼¡7l{A÷—çïÎ~ÛTÏ}µ±õŸ½ãݳ¿Nö}ßÄ“w;G‡»ç›Íæv¿ïëfsïl¯qrtØ:kÀ9šÍý·ÏÏ/ã¸ÿ²Ù¼¾¾6œÄÊpÃ^b8hžDa_GñÍœlí¸ý.3:{!;ðmÛsãW϶>é›WÛnì]é#çFG‡A[Ùj&ßÂA/ˆuWG¯ÈVsòq’®í¹N §|?:ó4‘ENòáÙÖ Žà–_A°xÝ(öcøô:r:_Ë­æØ$gM¥mPÙgBÚŠçL¶š“S²0ŒÃíößÃA<½t usr|Çq?%— Úp¹þ¥çNÍÆ÷>6K,“cÓlüû/yÑ __4þµ‰õ¢!¨ýõk!»‰ý®ï ªI[¡ïe×,%9ÜËì'eÊòÅ;2kÅ7¾Î,'9„S†Ÿ²£¹Ã£ã{‘se+ËØÛã|v <§1ù”}•à@ ÃÞõa× ®œÁqäu½¬&LŠ2-ÉüƒÌR´¼tÅ~Zâ•4¡?ìÛ¾× *y¢Õ<¥Ö­¾ãÂ)¦ö‘vüW&T½ôÃÄþIêòžW²Ãå›”nRÒ ÖK“½ä¢ñW%wÉI¨œþÇ? ¢8×Nä”íyƒ¾ïÜ´\ǯ\š*¼n¯ñKcú¹|‚ß<_ŸÝô+‰heÛ´Rî…î°§ƒ¸Ün§É*%9®ÌƒDZjÚy±v–T®E™ŠÒ²¨4)ãDq™ aØDJl_rËNÚœ†´™MM8©m3–Ü –ɘbÒT‚ÛªØ ëZdÖ$/¾®k“ã2 ƒø0脳›Ô¥ìÉ .r§—%‡¬’‚ÎMtg$ µ¬ºQ}“qS–®µìÜ ÖÂq_-'l¶tÏ»ývé4©åZhò]Úl²æS¸XYárg›‘™¾qºŽóÏkZ©žÍ–g9ÔÊeåéu<ß¿íᕟnÁ¢æùŽKJr"Iö¹r©}ÐÓ”¶°l㔬&eTŸRÙÜ̥䅔…G_®ƒK§^ßV;:»¬«jí·î¨ee.=`AÚQvÓtî‚5¨ÛSÖtÚ²„½fô俢@G§NÛ ÷˜¦RùÔù6Sj>gúK<»&fmä<Š;ôš²—þqû]Ê;wC7t’C ¬£à øŸK²ñïyT!¾ðÏ;ä¼xðî^:Ñ@ǤQ†Ÿ¿‚½›<=Hðóy¤ÛLˆón¤u|¸ð‡ÞÓÊ}Oá’É÷”2H}Þw¢öyüEHá»ôMªäð'oŠ$o¦™š˜vjÂyj"Hj©“7ISiIr^/ œx9þùg7ù"öº—±:Ž <&É^œwT6ÎÝk4Ûúª9h»_ïò‡`É’6ЉH*ì Û6<ÛRxí{Ó[eó¢.¸pÓ.;fcã‚PͼÓ{¹é”X4Ra‚Ä žyÒÈ7˜€¦gq“P ¾úê¦c ê60ì`ûØXþõN·ß“ˆD"gM‘C•m¤^”e )¤´¡;UþeLâèŵìOY6 á†pC¸­ÃdÝy‡è3Âhr !·¦#9„B!‡+Rüx+RTF¡Ï7-‹|늌*ƒ%1XÁ$Ô)8´ªƒ"\ ‘ø³Ö”˜oºÄ¬ÄB”*J”ÅmX‘¤¶‰!MÖ§H´°L¤À)ÖbAŠJðů"~惬Ùqëïö·*¥d†™ºjBŸÈV¬ª”³ÅnÎ!Š3R—Wù“fU*™…R‰R¹RI–»vÏ“ÓUþMR©Vé´jF¼Is!¯Ô×" š¬2mi“£T¢T¢WùDÔ-ß«´VÈ«¬^Ä«,I%Ã8J%z•¨«5ºJ—¯«r5VÚ10u‘•v¹iPnK"ÀóµMpŒQWQWq¥Ýû-iQru„…ƒùžÎä(ç€ãpœŽsX‰qçow—~G?^^¼÷·áøî¾8;c¿þøá-Ù>ü°¿Ýú 6¿}ñ¯w_ßžý#݉ïS<‰ªÜvþègEá­9ýè­öüÞ Ütø×(ôªê®Q™k3qê„-Œt“]‹nqè^¾h(èoBÏ|A,NÞ×;²TÄS}ÀÒ5­ãG¨ §‰7·Ìªpö—qú¢–§â8ЉŠÌð+NµÓ>ü›»åå4¼Þö½n0‡;¦%×pD3ó$Ç.!pàÌ‹«J¸ëWÎ àˆŒRôÀÓN³‘xÆ#÷mÛ½«y¼·4qâ 8«Lû.ð>u{¸åîß ô~{ÑmÅþ¾Þ‘¬žìƒ@o{¶ªï£J"-ɪ^ã†ïé;4¶ÿŠ8ËDŽÃã¨NTPªi Š¼hð4 Å|”V)Ô4uö 1£­8êfáøéÐÏà\1€§sáäO˯püƒ×ÎûR·Ü74¸–ï\èÑÔèb‡G”½§1 Ou· \Yfé•L·LGq ›2ÿJg[ÊšBú†½²_jˆRN£÷ôiµJQóÛzMÛyV{þé—…h[¦TUÉÞû>4£WÿHÜganeutron-12.1.1/doc/source/install/figures/networklayout.png0000664000175000017500000031160313553660046024140 0ustar zuulzuul00000000000000‰PNG  IHDRµ7…Ø£’sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì`ÅþÇgöîÒéÒ{S‘Š *öî³"v,$û’ài°=;-ØþúTìõÙ *XèØé ¤—ôÜîÎÿûÛdËqI.¹KHr¿ÍîNŸÏîíüö·¿™‚`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ G`¬;ãø1iÓ†›c1&À˜@Eœr`LÀŸÀmÓ¦µÔ‹e#%œ¹³Ývø‡ûž»Ýß8·‹Ÿ:.³ +%e›o ¡ëê)Œ°p…Êþ.ññÎíN˜j~œž 0&Pßhõ­Â\_&À/Â|óqÝ£¯3ôÂ?ïÈÌl]Qmvz¶§¸"_½\Q< €}]ö(¾0ôÜ8&À˜@ý#ÀšÚúw͸ÆL nP¢y~ù *sEmV()53M 3~nFÚ¤Ú,—˪?Hk]˜g~%ÄE32S7ÕŸšsM™…kjC¡Çi™@RìJ\žœ:íÒÚÄ „ºQÙ©6Ëä²ê¢<ó?i.Ç=³Ý“7Rž–0¨ïx@˜ú-t.…:?)5ý/:&§ir²iªD^.¤ö@VzÊ«V€Ïäû„#…’Ïge¦>ìdŽNÍÈB¾'+éHž›>å[;í“ÉS3ÇCz¿[Ÿl}K£Ñ)kQ‰Å.%&ÏÌLÝlǵ÷Éi™×+eNÑœÚå§ÓóÏ/W®ž€°Ë”gg"ÊÙ87#µ›7ÐÞíVZ¶'sê{’Tò¿s2S3Å Õo¬{z7C×oB>çK7õ÷Ž&àºC ±Ä)Sg¥O^j—6¿#¥:8nš›™úƒíï»çžÞC×õ~GVzê) (¼Z S2R•T×JáH“1å-ßrìcÔõ5÷w:ÌtO^;&5ý4Cˆ”ÖŒù âx(¾r+¸Ÿn§å=` kjÞ5å1Z!`æ™q±qr¼r/„–ÆLÍ<»ª[¦ º±‚Ò¿öOhç()ÖÀÄà2Óc¬€2œòŒuíj¡«„œßèbí~œÿâÝL…:ÈUfŽÊCýèe uéù 0D€5µ èbrS˜@mPÒ÷dʽ› ½ å¾hšfÖć:öá{ïÍ ¦IîGPzÁ ˆ«9]bØ,wêOv:»—›ÊxÓPr>‰ûdròv„ RæG0¿ÏÊL»ÆŽOûqî‡Ûxƒ{r1ö¥ˆº}NæÄ5A%æHL€ Ô{¬©­÷—ÀSš–ý+>åÿ `v:í?à ^«¨L!Aißôh©5Öçf$…:Rû{çˆ`Z8Ó=ñäõ Ò´NvO;Æ7 4É—B¸,€g™xtEša¯Cøit‚å^Á“´»¦R·CÎuÆ;¯Íòh)îÜô”yH0Bo³Âùišøœü¡Íµ„T:¦¹t¥Cñ9{ì€!´’§º˜Âl'•²l-MÍájuMž©Ÿµ!tOž\ò9ÛNPºw83éžçdB\2;c 4Ëå;¤6Ø §¦‡îà¸8ÆÙä‚,wr~ù©j6¤½Öî›ÒÚú–}Žu®Ì›|ýéØÐIK 'å‹ÖÂÁÐ΋÷L€ 0ʰP[!gL RYîI#æUÒPÆsw=öXlE‰È†Q»@€3 øñß ëYÚTØHö¨(¯2aR–µ¦ð µ……â_ИB?=eÊNØÃâS»:Þ^8â¶§žŠ†=éP »Û;&yFAÀ,-W®+S†ÏIk1i#Òy 4wµÚäF‡°^îçè4Ñ΄ÙÄ“Èk¿+Îq®¯ D áô£9]“S2®Â'ûIÐÏÄ@¶W·z¶¾Je€=švÐÍNŸ²ZäßÁó„q©m‡”Ìš`^‰°bW¬fiy), í2xϘ¨Œ µ•âp&À‚"0'=u&}6‡ÀÓ3oOÞý”ÈãrBi we„£ïÿØÐŸ¶]Éïìr¶òöBà4¬xAüi«µûu(„4v Í `e¯Ô%´w™âÝ’,´÷IÈ-(Ö€²¢]9'BÕƒt_ºÝÒÒšZé„ì`Å—‚ìy:Šúí¤¶P›ü#aðL"*v¥ õ:ba,“jâ)0ËØ Wœºú¡·¹ŸjŒ‰W ó-¦P$ÄNÄ‹Ä0è±Û@”-y)Të_쎳ÈÏž›ì0móî³ÁôDþhÆ”)»mÌ82C;¯@{¼R¿@ñØ 0È ÀÅ"ã:s+™@ Ñî€uK±n¬„°qw²;ã—±9ßšP©lñÎÆ®Ýb·¯Ê‡Mnó²¡Õ?#{PL=õ=ò=óãÁ¾y¿^äè§C`]:#£de)—t¼W¬Ì hIü¬×žV(¯éÕõ*™óÔåÕierjf Ñf Ñu‡<{ßsƒq8_¨iÚ}J_"ÏGF§MÿqnúdìT#Κ2LÏüeŸŒ–¾+5ç}Yé“~µ »ç‘Gâ÷ï+ÌňõRbûÓÞçxÙÈ5ÄÌ×A;=iĈ†) Kׄã߸á`蛟ÿ1$Ú¦TA§¡±pë‡Ï™@`Mm^tn2¨)4£€&47„J‡ÒÕóš'6€H+ÄãwßA[j3„ª„ÛÜ•hóÂW)ËÁPæÉhÏ*ÑkoÛÙÏHŸô;¹µ‚†[ŸÍ•:•ÂdL‰é‚Oh¢dF)»xýü’§Mkƒü£!üý]ýA]Òhïz Ú‚͇%Mãp-háWeët»˜v,„ç“aÿºeðh)BAž§½1€¦–ì‹!ëÒ€±Ö_þ²êt€÷"èLÿiã<áS+ý'†¸?, =”±”`'U¸ïÀå°/`õ‚ µõâ2q%™@ý!púq=%Í(Ö>yzÞíÑö•´FþHû"½8)ØÖA-¤¸ø¬Ý¦¼4NW‰pŠÁ_'!Î9Ï)œïøÆÇ€²÷H –[w ‡Ü6Åßf§¦nõ­5‚}-ê®Ô奂›opÉq¾ºÁ:ê‡Cƒóð¦ìi»f§§¼v/¡Ð.Fžþbp9T=–éQ½­TJ¬¶ËöÍE× bW¾ÃTk%rdξ¢ À2ZéÿúçC¼(Xe˜âŒ@IN›v]Ã@a`hÝ'šÔ˽O¦cO&Àê5jëõåãÊ3ºG€>GkÒu3E4˜SÊ«¡Ãé˜b JÞ3:uÚEâÑ”PãÜ3¼‚ >Ño x5OºÓýxÓ@if¹S~!­!¬A@š†6Rü>+ãÞU¾q1³Â{tŽEî!M.¬o-í®ok°–”´pB‹ûŠs»¿)c®56mz"*òoä_ŒtS}Ó†rܤIÌ8äù'¶‹0pëß¡äU^ZÍ%J8B[=~Ú´¾ñ’Sî Cáé¾~þÇ´¢½€à¹¦T—Záíÿx¡04òÏÒüÎ û_ß¼éÚÃ|äa_?¿c«}„hÙMû…ñ)` ”@™‡tm#7‹ 0Z&@ó”ŽNËxö¨©%ECÇêçf¹'¯Ç@¥»!>…9¸ÞÇñ·‚—CÀÌÅp­vˆÞ¯X¨.=÷[ÎÌû&m€ ·‚æà|=o Fì?ÏÓyaþ:;#íëƒEÈ/ a½ŽÎQrúAÿ’£6ŽÉ?n53·ãüYVUÖžÖŽßÞ9åÎl=£=â%eë %¥e|¡LÄqXqë Ô× 儬Œ”uvšP÷N˜7Æ9ÚÔE0è}ÉÈ4!Ø|1tm:XbÐס&À» žÕZ ]’-,G»ú{òÍŸ±àûxØŠÉÅú(U| Ú„ÅĹZÉÞ7 ƒ†9 +§=—áxñ÷”?E¬.ìû§|Šký®õñEž+iPÆ÷í„b»Gžž{1®ën\Ü…¨çÐCÊÕäëXã:\Ÿ;G§¦wF\ÜZ+\§Cî…CÒ²`õ–kjëí¥ãŠ3ºM ½ãHZM+  c×<+#m–&ƒ!D-€P5Ì$cì61å—|AÅ›Þp®œÒu=©EȺ#^&Ô­O˜BëkçI{h9¿°Ï¥tyíim?𵂷tŽ:´svøÎóÝS<„]#¤ö4ÂÐìBXT@‰x¿;4íJû¦ ÇñlwÊohêmh‡É×ýµ©•&]®  yö¡´d& 9—‚ç§Ä‘|@ÛÇ58/ ÷¢M·âe㧊ʉu&¼LìÀÃx/”·º éZG»\—Áî÷Ô¥Úƒå|Í'p_\‹r6mu®áü@åf=ò?\³)ˆ‹tŤ„{Åe?&À<Ø1&À?šçõÛ?Ö’ § 玓éºLÊ«-‹kˆâûv”Γ[^Ô°ø»ÝoDmÓ7v–NOtѶ¨5»ÚWX*D&ô)¿Ð(ì*έ³Ýv‘ÄŠR²œî–m8‰ÁŠkíÊ[ Â7¿ê2¤tÛõ ]5§¡ž¹oòx}ó-ï˜æ 6wæ÷0†#¦qì X^\ögL þ`¡¶þ_Cn`L Ö ŒIɸjÐÿÂc4îWÕz¸@&À˜€6?ð§L€ 0&P1¼gÈ[e%U›C™`µC€…ÚÚáÌ¥0&À ØêîÑ÷<Ãå®0Hþ¨&‰hÀ¸L€ Ôžý ÖPsAL€ 0úKÂì˜M¶TÉ B®ŽŠÓFÕßqÍ™hhX¨mhW”ÛØ¨Úg˜é`lhÆL?JWË™3¦$ç×HQœ)`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0Ú! k£˜±iÓ Ó8euVRµC¡j£\.ƒ (!r¤’Ù ±É¡9>›•>y)SaL€ ”G`LjúhSˆk…kB¼2;#m®oÇ?HƒùÁ÷ÃÁûŽÝec„÷ÌÞìææv¿³Õ³õV!Õº©wR˜BÉíˆAFîÁ˜|Äj”@[䞈û°5îÃÌÑ©é[p>ÙÞÕþ·ûÆÂ-™3gL Îp»¿qf oUJ œ›‘zm­(WŒ 4 ·¹ê¯í}t„¼šhVhj“RÓÏBeçâaÑIJù)¶W´8í³&OÞ[à<™@eÆNŸÞÌÌ7ÏWJ]ƒí¼dý4£³2Ò>¯,-‡3&а$OÍ<]ê)%Ô1èŸ^ÏÊHÙ°ZÈ­au“Kàëi´Ò#ç¦O^îZ†]¨MJ˸S™âQ)Õo¨øís2S¿ w¥9?& 䔌“ñyñiäÑ[j➬ôÔ'BÉÓ2&P$¥eÞ¢L•…¯yçÜR>©?µçš2úM`tÚôAB¯K%:À6ab¸û_G8ñ”´êq)äÛÂÕê¬ô»×†3΋ „ƒÀÒï¿Þ4ðô+_’fþQÐÚÞ=pøéû—~÷õOáÈ›ó`L î°ì•È‚6ç£ö®#O}ìþ±«ênm¹fL áXöÝWÙCO»øÃ,:’úßA§œ¶eéwß,WKæ©%“¥äÇÈð9)Wâ“Æè°cu—~P295óuܨ—âv=Mêîµâš1P ŒÉÈhoˆÍÈçÙ©åYÉÉžPóäôL€ T€Û­´lOæ˜<àL‡tœ6;}ÊÂêåT6¦…îhPr™K&ÂÕr ´¡3åjž€uŸâ~EI¿c›[z×|Á\`µN`vjêVÍ!Ïlïz9 ´µŽŸ de¸ÝÒŒv5¾žëLeÌ(ÂIX„ZšåÀÚ,wr~õá¤L V ÐýŠÁmtÿZ³uÔjé\`µI`Î)_¹Ý§êµY&—Ř@`O»o?àpÆ Ní¦À1“/F³mNJÍ`cûÃÄŸ‹ Ý¿t‡žçÀ˜`L€ !kjiaQlø”ûÊáh—ÉÂAÀºq[÷s82ä<˜`L€ 0Z%²PK+…Ñ 4m­Öœ ca$@÷/ÝÇ¥+ß…1gΊ 0ÃI`œ;³_rê´Ëg¸l&Àj‡@ÈB-ªÙ™V ã…jç‚q)5CÀºKV¼ë\3%p®L€ ºnþ[ #ãp”Íe2&1iÓ†&»3»üX!/“‹éÚ!{Zú–¨×p/Ó}L÷3;&À %[`Ú¾ý ¥9Ü&Ð Ê̺(FÛÎ¥}! µ˜—¶*J%8-¨ p/Ó}L÷3;&À ©âðÛ.h(Íáv0†H@â“?ÚjÛÂa~j8=`L€ 0&À˜‰@ÈšÚJ¯ q’ûÁN±.£è‰)Sv4¤Å°òÚx4ûf%´ÿ››‘ò$!äWšz”<5óLeš ¡-ÊÊHS¯ÕgL€ „@môvBÊç³ÒSŸ©nµ“ÜឦqñÎmN˜WÝ|8]è¸?=”aji„* ú/úåñ´ÏÇ"†I©™yø1¾¯Åȉ´"Ì¡M¯OɪR£Šiµ‹ðæ\š›”픩úKi~éÍ?Ÿ70üI)K•P„9Ñ.W¯gÜ÷–±‰Nš3Ç¥þÞQ,…Ü–•™VSe¨¦°oëO {ÂÙ2Zò÷Jòtc[773µg8ó缘`ÁHžšq¡i¨ìø4³ Q»ñ}u»ÔħNS<3#3u“AóÐ~Á¦ƒÒ24aƒ©r¦–P¬dŠò´¥õ…÷ïÓú’¿P÷g¡ ø¡Ûý 䊚Û=‚ì#¼ãþ´n]â:c~œš~#–J{+;]-¤(„@õ6ß —gµÑÛï®it£Ó¦ÚêÙR°Í“9¬¦Ëªù«FEzñãu£.Õ¯Åmî§C }Wu?6ôì˜`^‹pD[­;ôa¹èÏhMûÅx0EA™p¬iª{a<‹å#¿¨ ›úÐQEžïA˜íÍóûØÿ«>¶ƒë̘@ÍÈÊH›X39‘«’á«‘¥$¡¯Ic§N?Ê›ïhYnaŸ‡^ "—º%™*Aö^|½{˜ŽÝn¥m3ê#£õ]tNîN÷ãMó=ygâpå!¸? Ã…VrUruB¨5KætÀvö?¾-5°Ôfg±oc'>ôP£}<‚OÅs¢›”j½’ò éh™–åNηã&¥eÜŽ·È["ê S3•¼a}°mCšWÚ9šFŸHÈä [ß’ %ß­ŠðO¼€7SËV¨3µŸóµl}Á2¤ûTtl•"·ìœ¦Lq9Ê/”ÁÖ‰âë’|°‰ÊÑÄþp”Õõ[ý³sÒSgÛvÆv£„ø—GÉŽPkOAÛNÂËAòœô”ÿVP–»Þ (ÂÑsÆmO=ÕçéÛo/ª ¾T•vŽO{°·Géè 0݉z}(…öµRéSËhTƒik º)MïsŽnøäp»’ÎùÊô°Pû1&pX ”>³bIîÐÏ 3…jZY…ªò¼¥/VxÁÏDqŠÕ/ ± ¦‹…Ó1-Ë=éï@e‘©ÀV}á‹0;ýÁkç1=P<<¿[¿Ó%?·ÃKMôVÚçÉiÓNÈóä=¶¹ þFúKiØô“3è8ØöTÔ¯µu´{k›±u”©Ä•¨74Ų ÚŒöi¯D·jô°o?6.õ¡£1KTê¼T²%âmC5¼Ó»‘l0+ã^K˜²”kÂü·T¢/¦w¤þÿ'‡Ó9a¶{òFWæ¸?­ŒP%áxY²^œ*‰Vi0dÃëîzì±XÔ 7Õ":V{´²ÚðvDÏÛ·5Þ–âÜKw+}ÇÒìúäÑoÄ}pS¿‚À³ªºA’Ú‰]øß—­¯šBq³ÅöV¸éOÆ ‰µ"Û>Ú¬ð¶«)¤Ê9SmÞùôùˆæçÅù^ ¯b(I¥Žò9:i5õ¢‡ßרz£3“Ó2¼öKð³Úã¥Q§ÿ!þÙ¨·fJQ¡¹òÑš5r>ÑòO÷,Ü~àÞÊ*U•vŽwO;²ØÔ„vâ,ä[öE(çߦ2­Áq¾eU¡­¾É¬ãÙ¤|&®žs2Òž–=úìÁ˜¨#îÈÌl¾k0UÇåÐ>®¨ZUyÞŽ>½Y¡~àä Å D’ê/ôGáù{µͼšTßòHsL-?×@0Ü";Qnÿ eć”Ö£›)%ãN|s*9†²âlô¯é {ÝîCql}e­J{EýZ<º1uo”± …}‹úwSÊ| xç(jJ±ö(…]Ÿ/±¥b+@?ÔéœhþKÑΨíæ·Êøyž‚ü–¢Îy8¾ÜÔõ•ãR2:RœŠ÷§ѩݰÃ.ÔæîÎÁö§§LÀY±“yz*~¸Ýð:¿³C笌ÔS¤«e:ÇMxôý…‡ gJÐ4íhf;ÎÉHé&4Yrã+y•Fo±s3ÒNƾÎå6:§­ì€1Õ„FéÐ.hïãø)~µêD +p2×s/µBû{íœÃ:âM÷_NWT(8!ÊÛ“Sîé› Ó”ÆÅimPïØ¬û§TjÓä)p¹¤¦·ò‘jòX÷ôn¾yúW¥Ås2X5‚vâ»èÖ»¢þÇ9\rˆžt^Õ¶úçak!LÃgò+}9ñÇçL€ 0Z% U $zÚÙç±}_¯6@c˜ƒ>hü ÷”ÕÕ¥*Ï[#OOpÚÏÛD‚«ú€í]C›âYØß÷ë¥oyÉ©™OY-ÙüºZ^ž•œLã¿:Í©^¤¾‡½lÏÖõÉ)wû ·xÆ»ñ…ë’[f÷¡P8FãAˆ/qù…ê2äÐÊžÏï2›Nymip#˜…<ú„û®}4›úþ™@s A~g¢-½4)žDùaÿì”í¼í¹? D¥öý»P+5yLi³é³@¥o—#(~¼¯Øv¶ô£E>X‰•¼Æ?H:óñ)þ%ú1Ð' (áœgÅ‘ªÓo¼áð_Ñ9òz.ë”ÿ¹Ý§ê”Å­N**£$Oyí!tΤ²èx¦{â?ØáS9`~l½í“?9zà8ããŸLI±Þ<íº•„þëž´åÎÿÅÃ*ÆðNóRÅvž[RªzÕþ4Ûºõšé_éUj«z>gL€ ”G`Ljúi´•^“þŠ 0©›ðì¼Û0Çâa½\ ç÷••[•ç-—S~Èÿ¾¬I“¬ÏëÔo@8[ç_>ÒŒNÍœŒ¸ÐêÊ_â ”'øÚiéÙ ÅÐéˆO_IÛÂtâ?[õ-kƤe޲ãT¶¯J{ì¼ÊëײÜSþòÂÎ&Ÿ[i”hçv¿k<ÔT©VÖ^JÛ B4Šþ“B ½m[2Kbx Ï'á}ÓsÓ'/¦4–sÊÿZ{%-åU©o¹;îOËES«ÎZ--@ahv7„Å–‚ËxÁî3ºhÇþ–§C.ð Ô´¨ß £?jÑ™nj¿éDÖøÆmìØX¼ÏCoÚ÷ÿüC ßðŠŽN­äF/BÊ-¦dJ­)¦…y¶I–ðLçø5ÆP#ñ¿+ÛçÏš<Ù2‡°ý‚ÝÇÆj÷@ƒp!ò>Óª]:;)é]hÊ$¯J;ñåFÃ,­)Í©-ôÍHr/ìê´Õ7?>fL€ TDc)RKÃÉ„«–ü%Æå:Ÿ -öxºá9= Oïeê+i Ë9SÞ T¡ª=oaB§¯êˆ¼E\¬ök üÊú©a‹zÅ—y'i+ˆ>›>e!ÓÉiÓ1nÁ˜„þsxÓø|µ,±™ œŸ>«Ùwê×HõÕÊÕ#F ¬îQÛéúž¸ƒeçYÊ:üùÎêk”ºf‰Óh|Nî¾¢‹Áò«øÍV!}÷’>U D[¼ö¶Š (J\™¾Öö,oÏýiyd*öãÎlUæÅ¢â$CI ;¬ÎáTë úè!U[²‡­h2ç{Él±c¡„或–FÒ®|¬pè¹Ö‰rd‹½Ô®b; y{oT¯_5 qÄZߤծ“o&~ÇÎmz4Dî ²oà7HFë–ƒðo9Ó!—–Z;(ËÔË7¬²cÒîâÇŒÏW4e‹ùÄøûg~Ž7dº*^W•vF‹¢ƒ÷•Sù ÚF¾7ST§­¾éù˜ 0&P!©,!§Â85ˆçuñÓî{·”fOûïðé¾=ã`JŸÌ µUyÞ6ÝçPÈjݨ'®ˆö9¦ç &LýTKašOb\ËÇï¾» ¢4vXéÀ÷ÇûIS3µÆ²(™†ðvœ@ûª´é½}w ~íË•«gƒß-è£vCö%>ÛÎÂñð|™ÊÞßx¯ÕM¶qûn«gየë(˜%þ3¢|,Úžs…±>õ´b˜…¬†_™»¿õ‰[é!÷§•" Áôˆi²D¿xFÀAz>‚LîhmD»µÙbënÜx-öï+¢­\cu27€ðEo¢ýE¾è†½W°ËùtŽûUdWö)ÅŠ¦?5Q§™îñ¹øn÷-~°_Âv·Ä\"Lu” fy˜µUÏ Od‰†±÷>¼@˜öF­j;Qÿý¨³@Ч›7íŒ %ÛãajŸÂ¤¢öÛê-œ˜`µLO¿­V‘RW^ÑÕxÞîÆó¶E‘QØyzûÅ@ùCüÊéjv1´›AÛzJîž|¼›(ny~$ÜŽs?üˆÇ,ºÏóÖ4óÂÓîÛ”¿ªí)/§çê·P8Œð®›û@Ê'tL³`†#:M4³:ÒÄ&¥N{ÂîuðøuÞ ísþŠïø¸«²ƒrAã|‡•Iˆ¸?­@©Â"¶7X»Ét³CtšFçØ§ÒJ,v˜½'CnûoS?Ò1F*–¹ù”.n'ü`¿ }uÊ_g¥Ód¯ª¤¯‘:Iñ9ÕvA7Ò§ßú$¹çø|jñ ©þ±5 N:ÇâG[{u'1þ¹U¥HÿSiúsì|JÛq}îÝ×r[½åò`L  Ð_è ,ÏÈ•]ÅçíÊËPúÝx~{µ´Àƒmcj—yt)0òÿZt˜{`‡7ztZ¦5ÐÊŽã»'uÌÔ̳½6¨¥†^d™U ÏÝf ´­>ƒ³hæo=(IUÚã[~™ã<­ÔNV˜ ZìÏv˜.=£ìc[SK¦m òðÿunFêÕPÝŠåŸðh­4Êõ Úà€l¯‡ÛùØûêô·ÜŸÚôjÉ8ÔjGµn<£pÇ ñ#® ù>´|0æ¿c‹Ãÿ(rÓ+Xo*'ÖŸ‚ÉOFÜë`÷ÙJ¿%ø%âü$ülr¢.Û~Š¢WÉiR{6Bá­-†ôÇB¾k&œ±w!“ Íj¢NNgÔ$ÝSŒº¨³ wìÏÆŠY¯ã©°ç=”gç¹xÀ4õÚU©•åG&[–Ñ)Yx(VUÚ‰‘lJ`z1uÓèÔôVx e£——Íes?m-[>cL€ „Ÿú¯£ÑOYã ð,ìdª¢%¥Èœ(§w¦€€Wåy+5gšPðt5¦|ì‰>t%ú‹A†§è¸]® Tf¶!dMæÍJïB•‘5Î=}ÉL÷äCÌØŠD~Se˜Ÿf› v!ß_‘/¾"Šþ˜&óh+O­D)EdzÓ'/š± ‚rçä´ÌOÿÄ_K6·Ui]ís£cÓõróŽhg«\=ï=ô‹PbÁ>Ø}!˜Ò gÍì¸s’’t°ÀtÕuÚYa;z8¹\sˆOãšÆ}Nfs2&®Á¼¸ÿÁÚ¥“Ðg}ƒþj!¤ñ/aåešy²Ðw|€öKU&ãˆ{?ŒgQ~”Pæmøü~Y¬«¨Ä¶µ‚Üj¢N4ÓÞ8úB¤Tí±Xd`Ž/À†Üp²-»s&8°xƒ°ïùg^•vÎÎH›–#¬¼”Uçq-D9£úâÚ•|'*-àpµÕ¿}|Θ/)êDÚÐOa0´\ mæ èÓúW6¥WUž·Yé“ l:†áy»õOD¿x ?ôjEqŒµ@ÀfeeLyƒwg¢/m¤{Œ×ý¿ Z‰bÄ~<ËŸÇñfôÉCIp†€mr´ŸÏ”É\ÿF=~F›1M݉02‰UiO™ü|NhÆp¼å~‚þäô‹ î-5Íyd€7|¢Š»ï¼XëË.æ;háú8œFü1€ý^Þîü—íøhÃdLáu ®Íf´Úxòð̬´ØŽWÕ=÷§U%z|Ü¡9ظΧð&6œöáp4ÿÝ.}KÛ#œÃ6W¤ÄFÞššÙÉÓ©e¶ïô¡Ö>×ÐhÒèVM¶ØÓQ›gMÔ‰–#Ü¥gt–Î(O Ñu‡ßÌÁV-¬ñªÒÎÛÜup6víf0ÂálkMÜËa…Ι1&Peø]?L‰ÐG‘Ò¤^ºªH˜:æEïàe‹0tdœíYø´ÎÚ# ÅÇYv‡›LUæPB].·NØÔn˜\>`L€ 0&Ð0¸ŽÍºÒi¦Ù  ¬HÒõƒCÉbØ÷ÌÖÞ³ˆÐèý‡Úºs½Cfí–Ô ›Z»2¼gL€ 0&À˜@(hœ†’Úu³ö±³•éùEWÅaÖ¤a3Ûãw¦bÚ­zkŽ ›†ž–5µ ý sû˜`L€ D¹é)ó`ü¶Ø¶«ÔeLìŸ/EÔ¶ÖÎÁëJÆê¤D‘Èh. µ‘q¹•L€ 0ˆ$€¹GO£†cF–2«EE$ŒktéòeÖìˆn. µ}ù¹ñL€ 0†MÀTÒžc”…Ú†}©¹uõ˜ÀwÆ`Ãp(šß7”f°P =N˘`u›€TÍrsÚéø_Rjú]¨qdǘ¨SƤ¦ŸF[ªTý¨ ÷A^'îƒU Ñ"]¨µ´®QSïr8\½m‹¼îÇo÷îªüõ·âsý_;ŽÞ¶YSJ<6:ålëïåäš3KÀT2•¶ÛÀši÷ÕäÊ}cõÀqg 6}PõRLÉB­÷G;rü]çº\ÑÓ Ðª‹Vü$]†~•K€8/âˇožä>‘‰+;&À˜@Ý •&hc,îƒ%UN<îËS·éÓ¤iL¯ JPA‘úC÷þh{÷Ô¤I£&3[æìWçþº„² n›²‘ˆÛ9û…KÊç®»îž8„2DzˆøŒ 0&Pp߯«Ä}c`JåÄËgÈã¼"U¨¥v¼è¾§Ÿ:Ît8Úœùûrkh«púD%ngý¾ÌahZ;W»„Û©÷•>dL€ Ô;Ü7†ñ’qßF˜Af‰Âµ™†¹°ÅÆÅÅé†Y؆6È;¦œhÄ8:5y¢ßH¼·Ê¡ÃÞL€ 0:O€ûƸDÜ7ÖÔ ²ŒDÁƒ>¯–6æŒK®,Ž–˜¶‹?—Wp“D•¦µ¹æÖ dìÍLƒÇñ˜`‡Ÿ÷5t ¸o¬!°²4¡–~´¶–6¦y«Ö§ÃCaÚhØ«ªˆ#ñŒŠ9«”3ñfǘ8œ¡pÚØ•O€ûÆòÙ„Â}cȃΠd£Ü KªíÏ+Q¨NŒætvŠ+*R˜c®î _‡põé%œÇ%ŒÍ[Eñ÷?× ’jAsõ§¹_Ê.¦—£tÃŽ`L ö de¤M¬ýRë]‰Ü7Öà%ã¾1¸J® "V¥Q"QSK‚¼%Ô:œÎÖ …ùUh›>ÿ¤ˆ5²R°V—S¸÷.n9±¢N9A$Ü7AhM›­QB9±Âç­5o*ÿÇ-š¾2³Z™‚§kÚvHLœ«Ä¶Zr"&À˜•=«¹o¬€"÷À CPVfj2m¡fIšZúÑÚo£4H,Æ¡iñÑÅz ^®}Eܨ+ÅþEË«}¢ž_þy3_¨vÁ&töê)R±†BQQ°I‰mèR-Ißö`1â‹ylÙ1&À˜@$À}c%…ûÆJÕ¡àHj ; µÔfÚ\J…6!·kH¢ˆ¹àLQ¼h™ˆ¹ì¡ò EñGŸ‰Â¿.˜ Äßv³Ð'ˆ¦/<)Š—þ"òŸyN8:¶‡ÿ-ÂÑ£³0·ny3žú_kP3M4}îq‘“ù¸ˆ»ù¡µm-Š?ûÆ«é¥k"]‰Ç‰èÓ†‰‚g_Æšu"nÌ BÆÅ cûN¡ÿö—0wí¹ÿ™Aò3ëJÅÝq‹Pyù"gÊtalÚ,âÇß佂Zë–¢‘{¢P9¹¢àùW-a™ìhõUk­<Œ­Û„¹w¿ð,øY˜ð€(þq±ˆ}­ ñV±×ÑÃO…|. æ¾"<(Ÿ\ô9§ × þ"ï±Y¢à¿oŠØ›®¶„f+ÐçOá'_•µN‚S^Î6ó2ã¤L€ 0ꓚ~mÕÏ¡Á§´ŸÓ¶` …÷¾WûF_uû8’„ZºÔ^{£Ïãaq/½)Š!h¼ü&ô“NáìsŒ0wï±6…Où$ÜoZ[>ñ~úµ0÷E_|+Ý; ­MKo=HkKÙâï~ƺMBåæ sß+•_!y(|ÿSanûÇz©€Ü~yˆ´û+Ü(9?&ÀB$`*™J[ˆÙ4ôäv¿H{îkèjsßX>Ø1îŒÁ£Ó¦Ót !¹Ur!•]Û‰IÐ"ç#pYÂW‰oõ5ë­Ô$¨’“±1ÖÞÿë¸Þ–™A¿ÇxƒHhutî$Ì»-?ÏâÞ°@®!ýEì• G»¶ÂÈÞ^Åå2 [“FB_[RoZzû-œG)¢O>Þë­ulë=®¡_Þ5Tg˘¨„fÏ®$F¤û>«K…[îkð¦ðå]ƒÅÔ¯¬M˜&­ “Ä¡Ô<’„ZâD7SØo(ÒÆãTA-'ešÐ×l(›6µäT±§¬¿ï™”"ö´ÅËî¹_hM{g) tª°H8Ú·ñM!„®ÃŽV…ÿû& ¯• «Ù3_Ö5[çΘ`¡ð}^Û}d(ùYi¹o ˆÐ—uÀé)UXäÑH}ƒ Û¶¢›ÏØœ-ÈNVkÙÂ2=Ðÿ\#´¹±#/±Lh`WÔÀ¾eQ&ÌÒÆ6k"LhðWÌ” ×Wþ&\'®þ}„£KGá:ÆàÈ !zøP˜)@S Í-…imý„ß29…zž·üPkÁé™`L J¸oä¾±J7L]‹©Bm­\ÏOK­Á`Mÿïi‘0a´ö¯½+ ßþH(S¿Â´À×™˜qáÀÝ÷aj/Øôb%2•—g“v8çþÿXZZ*Wȱ¦üòMë{\øúû‚6vL€ 0&q¸o,ç’sßX˜:äÉBm­]š¢Ëßy…Yÿ€ Î+´»EzUX8²¯-Ì8û2&ÀE ®E ¤AÜ76 Y‡šÁBmº\&À˜/¬Œ´‰áÍ‘scL ì”\Ž^$Ü?QĽA¸Ž;ÆöÆŒ ý)ýÈão»Y˜¹ù"!åN¤ƒ0Œ)Ââï'¢Ï,+œÇÝxæÒ½Xhþ 9xKâ&À˜@ý 0&5ý4ÚêGmëq-ë@ßhÓóïøo´É4ü}`ubÃowµ[HšNG玢`Þ»ÂÕ‹”º¨3OEß,yO=g-¬ÐôÙÇE>Íë3Û@0q¢¡}ÍŸý¢ø«ï…±a³ˆ¾ä\QøÉWv1Öž„PKózL#(Šä:Xè6 sÛ?"çG±"Cét`x¹†EŸÍ·òr 8N8ºv:¸¯å˘`õ“€©djiÍ¿®Ÿ-¨µ® }#‘ Ô‡•×gZñ¹o¬7ØwÆ`Ãp¨¹é“‡R!ÖÔV‘-vóÀ°œmq™”Ž6­…¹åËÏüg§†)m[U#NKanͶÒ›·G»CWþ*|ïQðß·„Ââ ¾®<Š}úÉ¢øËïK¢Û-ΜÇ%ŒUk-™íï(‘÷D–å˜O”dÀ™`õ„€Tš ]¨ }cy}÷5zéÃ’¹éÓ¤i”˜¿9ó½Ð&Á²µÊãñѱŒŽöž[•ÅÁ¶´Œ­*.ÑÀZyD¹¬…ÊfTµ3­ÕÂÙ«‡(þþ§2 ÉŒBƒ K‹9‹“(|ÿhi!œ¹ÊY™ ù„ 0&À˜€/Êú=Š[Yœ ûƪöaÜ7ú^¨Ã|,•/Ÿ![„œÁaÆJñJ7ôÂ"‡Ks‰j­ â[¸Úw@hM•xAË)ãb…écz@•ơŰPƒÖ8AX«5nl £¬™AI!Áÿ>í$QüóRïªf”2öò „sH¢80)‹5 4¶ÑC ¼) W¯žB6n$b/>O˜»÷ cíÆ  +r¸”iªCW›:ŽÈ˜`‡‘@½ì«Ó‡qßxï²*:¢5µÅ…E{scâH¨ ÙéK® zó‹ÅìæÞ}ÂØœ-´#š g©ím0qŠ—ý*¢Ï9MHÈÑçž&<+·êæ<²»ptl_­zFŸ ¡öËï¼ic.;_DuªÈMÌ2“ ¥s ,±› ³ƒâ¥¿ˆâe¿XƒÈôõ›,AÜ›0ˆƒÜ˜X¥{<»‚ˆÊQ˜`L ¨}cuú0îëàÍb•"U¨µÙÂü¼ùÑѲЅOü!º¢¯¾Æ–m¢é³‰˜ÿ²Œ‘†5ê´a"áö›­Üƒ‰Sðâ<¡uê š½2«d@Ú‹¯[ic¯»BÄ\z^•kIo¯fËä$>óÄÝ|5]WÓçÍÞ|ÖÚT^ž(þzwSEEÂóËïÂܵ'è2‰#ñÔ‹‹·ˆ#2&À˜@]!PoûFs÷oÿE}Ye}÷uå– o=Bþìž”š>Ÿª„¥‡Ó¾;ꃭ1¶ØÚ;ðø!'œ}~Æ+‹Þٛຣ©³ès¾×Á6Õò+(<èTœä㓆lŽ0øLÀD¡®ºßÛuõ$V-_zåw¿G£Ò`£F„f?QK ®G÷r-áb˜@ý'€ßõÃÔ ^.·ÜkyHߘ˜8ä¤ç\0•ûÆr™U) ¾÷Ujl5#‡«ÿ$›ZÛÌ€ö…ùÛ’ŸÖ >õŒý´ïÔBmÈ>Ñm·B@IDAT]Ë2­å¡„òhƒsP µÒ`NÙºîÀQ™ºgÚßPW_Þu½ê\?&À(f+½°Ö³ºÓ¦Q#›4-ť枆®ÉÏ¿·ï6…÷Ü7Vx'*¹ªÂð #I¨%$–0[º'í¡ñ÷úu_:zsÙæfGˆŽ{Ù” UÇ¿õG´–;·l~é}9W';NØ`5L@ î\{`ïéù¦ººÀ4.Ù¯vë±Ó>&üù«Øx¼à¾1´‹À}cpü²2S“ƒ‹Yq¬H²©¥·Q[KKßðiþ-ý‡O>üÒ,*Üÿyï¦Çi2~Å7G°¡Äíócú‹è‚<óÔ¿n~[»v–[›y°Yq<&À˜¨AÊíÖÖpÒºÄ~3Öåì£ÉÕ?EÏw=l9ñ½Ò “9¶è¿~Ë–……Ü7VÿbX}#d å)ÞýÝûï‡dî«´Ò”‘$Ô º™è¦²…ÚâüüÜ‚¥?|ÿÊ®FÅ'}ZŸa("»à |Üg  ~]~þ^ë¬É+/kÞä³Å}}s逾ýÞ»wè£ð‚¯ ÇdL€ 0Ö î?p]bßÿ¬ûà½Í¦0¾SJJÑø¡A˜%AC‰7n÷è/¼¶{ïØŒ›¦,úqÁóÜ7€¤ÉÄïKŸØ·oW’ÙòÉ"ìj€@$©&m--™––ŒViDWÑŠ¾û­YËV‰cû^$ÄñêÜ_—H—A÷»ŠÐ[(ýhÿjÛA±|Ñ–;¶5Žvº„ Ç(!‡Ç(5ÜãÜŽéÍ%_èúÓŠåÇaL€ 0pHNv)å9'cÊ;áλ®ç·vàÀc…©”R]­t³kI}Ëên4)ÿÆÈw¶¾wÑ_k×"Nsl­±Á}c ±ªþµûÆ?Ñ7þ½vÕË?|þ1­|D2É$ƒØÚZ² 'Hj‰ýšIZ¥QWtƒåc+ÀûÍûo}7WMôéwÞ®„FêÌß—klc 2å8²¢ÏRôºqÕóžÿìã7Æ·iÕíŒÆÎó\švŠC ëÞRÊz8N5‹UêÚÄ~:4mv—ó/ú\ºÝü¦Z[öfL |”0nÀšâcDµë>RšÅW˜¦¸F˜ž£‰$žÃeôÛ¡•}Gjr^×E˾—ÒZs= ‘°qßX†VÕNüûÆ/Þzî;’5ˆ+É$ƒø]øD¸›6í$Shæ¤OZ ŠHjI¢·$[¨¥Ï±Øh=[×üÞþf÷öm; ;õòWÞ¸Û®í꘭Ëî;³EŒÏ¸ˆ‘Žæ¡]ײ YhPl‘s–ûÕ‹ËÌÿ@ržÚºí+lo>ÔµkÜyMâ¯×4ízüt©3¡_°†'ë¿ Ãø×úßûÚÛ™šõ]/&›.vL€ 0š! äzd|JÍd^7r]—˜Ø Ê¿ËñŒ½JéEíè`\°ËŸ¿¯)m^—nÝ¿”o¾I}!d[2;°4‡Ü7‰j¸JúFZa“d [¨µ5µÕ(©á&1”ñ_%Ì/ÐÂ[Bie¤ µôvD7”m~@7 ´ô†J,´_þá÷5+Wl8þ¬ó†êG5t}ßAð“WqEE*¡0_Fºõ@܈q´”0­¼F + Ô‹Šr6ýþë·?~öñç¹{"§t³~¸÷nذû^!¦ª+®˜º~ÃÚ‹…©Æáé”– A#ްxÐ0=é¬ð´³»-^~pÉ3ŠÄŽ 0&øô¾ÞT¢ÉøiÓZ̘2ew²¬Y¬2¤5ØËðD)”>Ì~¾úUŽªÿ94ùZ—®=? [27ä²e~Ñ,Í!÷þT*9¯j߈ìló’EØ•Ÿ’ѹr¦‰%¡B‰4¡–xÑÍDêzk¢öÛ›=hÎ,,Ì7æðÖ×ó…ø¶Gïã:v>²Wϸ„FÍ¢ãâ9N‚#á7 †bÙÛ¢¢ýÿääåìÙ´zÕ_k~[±Þd¶AÌý¥-´`¿ZŸWð%ÍøÛ´m<¸«nÇgÀëq Z¤[ ¸Jê*hnWK¡=ãˆÿoç öZáü‡ 0&*éX‚•k„^ ÎGV/…šÝáL¿iذfzQr$ÚÓ¡  …Ê8(^ •ŸA;/Úó~ǤgµKV”‰à„ûÆPz…Ø7Ì3‚=ñ65ŒšïNZ´)$‰B- Zö)ýØI˜õ ´8&ŒÞ¦b°¹ÖþþË:l$ÄQœˆÓÒ¢Íô #f´Ùlè…€XÒÐ’P»¯ô˜xÚo¢ßëº,ZD ïövʆ k¯4K´·'Ú`óu$>=<¥ r†€ûŠph³»/Zò[›?LsÒ§ü˜”’ñž9cA Þ µ;†O8»ÿ(Fz rÏÃ3ÊÐcÙÇIIŸ¿šš×T‹y»ÅÏ?“’¡ªŽûƪ KßXµ"flSªÑhÙ–g¸÷é“Bjd$ µÌÐhö[Pµýèó ù{mmqLoþ¯Þ ÖÙOMââû°#6¶––„ZzxÒž á)Œ_ŠЕ~þz/oÒÿ]W·âøjhšP·ô2q³ÐÍ›aš°žIHhòz«ùóI+ÌŽ 0&PuR͆øL’{Z¯,÷”¿ªžAí¦Ø0|xŒÈÝw¶aª«rrö]„#õGe:!½×Btaó¢Q¯wX´(¦v?È}cÚeNj¤o,SBŒ™šy¶a˜§` N2ú{›mµ DªPKàH[k;:÷ý1“ЇÍÒÖbOœlÁ6Z4×r6bå«¥%–4µöF¼è!hkiƒº1;ÿ¼ü¤—˜xO¾¦_'•á¶ü,GÇØž=³÷ñµ‰}_ÔœŽg»ý¼ì;œ÷L€ 0`8ÍÿÏ0öv‰1;‚‰8âÐê^8Ý”æH#gߥdS=ð ,S)䘼æŠó:-\ž]&0ô*ŒûÆÊ9ÖhßXyñ '¾ ¤âÅlm[ljχ£U‘*Ô;߯}L‚ g$´ùjji¡¶! ·Äƒœï—VÚˆ ±d‚`oäOìèah§Åap®ÝÒ¥Ä{m4A¸4Ô­Èä <ÌéÅ‚rl„¿·™ã¶uú}‡™ÂgwkÙæùÉ'TvL€ 0 Ìt§/=*Œtiu¯ ÿ{˜2ÕH¬î5»ž °“ý Z¬y˜1æÕRS®š¬-=ÃmÁÖ>æ¾±„¸Ý¿ÑžaÄ©ÆúÆ’"ú_5Bír·ûTºÇBv‘,Ô<ûë{ƒÚÓ}‘ E¬¯@ivµ6ÿ/1¢ÍW˜¥8?$WjG; SÔÜ!5ý&eÊ[í1v¦˜$üd̦pòºÛv¯KìÿlT”|¶ãËÖÚá¼gL€ Ôuëõ„çØHLox^ÞÛª/ÙÕJÉ7QÚ+]~Zög 85èÇ}cÅpk½o¬¸:õ74+#mF8kéB-±ôÿñÒÛ k¤…$>¶†Öh²†Í-ãˆÿ—øÐÛ)íIµ7†Ïu_º” =NÛÚN“Ò‹‡ÿE8§é×P+Õî½ÅEbâÚ}?ƒ=ά® M>–óçS½Ø1&À*$0:%ã:,³~vúØ¥Ö¼³V÷úU’æ’5T×’éñzÐAû7noa ìku` ,÷/ÿÑaëý+Âçe °P{‡-œ‘ÐJB›-ÄÒÞ>Ž$Ö&ãÿã¥s›׸ë±lÙ×(äëm'õoYP ’ ÜÞ 33«S@èšœcšæ9ëìˆövN´3êù?ÿ¼¥Æ+Æ0&Po `LJša=“RÓVR{"Úáüî÷½aµQ¥Õ½”Q4S^í]ÝË4²Ûñ{[bQ„®K–.Ç`¿"B=µŸ÷Ü7–%yØûƲթ»gI>ØDË3.ÁÀÇ«q¿Ë¬ŒÔ3kª¶,ÔJ–nTj-Áöæ‰îšC;ËTfgØr¶Å¯šì;#ÊHަD6VRÜäp8?•>yéáÐöûå;Qn&„ÚiëÏÐÞâülœ“6NµÃñýEzÑÔuú¾mǬn‹–}U;‰’êò_&ÀvÎÇm3²¯Wʼ[˜ækEf±ÀÔ_XuK,Žv6¾âi÷íÞi±Æ¹n£{ŠŸRÑ2²eLö5kìºîá{ï¥Y`­î•}S±C»Cåå4-‰LÝÊA‡~sqË÷ XÏëÚíȯÆ÷ÔÒÊ?-#BöÁˆ8òÏŸ«RŸšˆãÒ¯Úí¶g¤‘×ß•Ò&*&:«<>FÁž^|â ‹Õ§¡Ä‡€OíÁB´³\]¼^¾÷'U²ïŸv(®£ÊñAH­Æý”uXCX¨=¬LJrÇ-´[¥C»ŸŠÈÞIŽ(³Ø%¥Q²Fö¡©°Oý(VJ0 M7õŒ[R3¶aâÇÚ»Ú?ãvßHfµêJ…ÔPèG[ êXhzH¸Ö²M+r/†yéºýÖ¯Oì?36N¼T*×j]¹0&Àê&ÒgWžsÇL~¼RÆq0¶:fM­bc ÊJ¡•4á¬E‹Z&'ö»]ÅUXÝkh|¡.-iÇ'îp››·Ü½ºuû 2>úÜq³f•,2³t¥×{O̺K|Hƒk¹„&M åXóÁoª„BÈS-Ú¶µE -~ þ[ÍøÐýdþÍšÔ¾¬ …ØÁ׋òªT‰?Þ*çSû§}=v–™Á¨ ©g9££çBhk·7!ZíLˆ–{\BÇúm‘)šåzDËÜ"Õ,·äVa7Ïvßg‡› M‡³áÀÞKM)Çâ¹z ž¸þ÷6 l{Ýé³»,^ñC ú6 {9PóØ 00°V÷*Ì»RÛHd{ZéËt™ É³V÷‚íîkNgôÞÕ½ÊÄâ&ÀÂE€5µ%ÂI¬Ž›&M½Ãáp=˜íTëŽHâ\þ‚Q¸¸×Ë|H°ßÙ8š6Ù8ß#ºïÌkW$þap^jž@£ª¤Ý'„Òbo Ï7Ö é×Sêb<ªsôÍKË¡f×醺ní€~@Ûû Þ _-Ϊp^L€ 4P´ºWNîþ i .¬îu.Zš§¬Ãê^xö|;ÚyMÑïTsu¯²yò`Aˆtõ# ­dé51õ.´ïn#Wvhæ€@ÀHD|VvlêØÝ(ZƒàøØè”î‹:ñÐýçkº/]q§Ö¨Y{é7A€]Tö:©c`Q1fÓÙëôŸ½aP?ï‚eãñ`‘N€V÷ÚØïb̲2«{íP¦ù*„VZåËÛIàÁgâÓÕw°NãˆjÓcéÊsz,[ñ" ´‘~÷pûk›@$kj½íÈñwãrEOƒ@«þjÛ¨Nfµ}#T§<¯DÄ«žð-rоy’ûçt“)ÂaÓØú¶£ëüùdïûmë‡ 8ÎÔM,É‹O…%‹9 O¢ÅT²aˆd,ê°ä« k~騕£±cL ‚ Ø«{)i^eäìÅê^%„ñÌ(C‚ìb˜aÍsíµ.K—n+È'L€ Ô:¸zj‡èh{÷Ôäø‹/\^ãjIZÔØU€†á}7ï3b‹=Û‹6í?òå—¥…+Ê>ý«–eŦχ¹öÝ€ÊÆ€¾þ;\z´¡?íˆÒæ† Ïý«ÃçL€ ÔZÝkãÿÞ? ӎĈ§+ðÖÛ"PѰ“ý Kz¿¦9£^«…Õ½Uý˜(‡@¤jjIt¥¶G÷=ýÔqšmȆ–Úrî’J¼‰Ûº–ñŽ>[öµsµK¸ ÑÁV2ƒG%ik;¸Õüù4 ­`2Sï C5Ç@þ¾ ˜ªK”áqBà½K/6îZ—Ø÷khbfwíÖó}ùæ›4ÐŒ` ŒÀú}cåBZÝk$4±Ö *þïädWK¡½¡¹ä«ü²ÛÀnnNƒ"‰B- ´–-ö±1 ch–Ú}M6¶Ä±I^ÑÈé1l¤©õNùZî5“ƒÄ ç›O8áö¢¢‚›sbbÜ `’Pâ èžmîië×­ÞÁesΨçX3cÓá=¨¿ðBÛGIc¤4ÅÕ¦©ºX+¿oK°Åÿ¿ÿ71µã¼:°ºWý…Í5gµH …Z2= vÇœqÉ•ƒR¶Ä´]µˆ¼áEÓŸaª¯6×Ü:aÐ+Ï<òS}i)¦ÙÙƒº>Sšó{mÛÜì_+­Cÿv!>?Z¿·B§—bèE“¡½ýãáfwëÖóShoë¤6º¾pçz2Ú$P²º—çJ)Ì«•Ò{Ñk·Ÿ óØ’Õ½ ÷x­Û’% KçÄ®ÍjrYL€ „@ Ò„Z¯--˜Å4oÕútìæ¡ Ù¶8„kÐ`’Ò|¾Ä3*6æ,ìc#M­¿¯ºëþjÛqïÝÏe]º11³…I˜7üf´ #Õ Ñðç]°nýêÍëô›­IÇ <@¤î^O®YdXbÿ΢H]a*1RéE‰DÃÿ„‡?-‚ð.V…š×µkϯ½/«pÙ1&P¿DšPk›М¥1šÓÙÉãÐæ_å§Wî[šÇÖãp`jÑÙ‘‰G™UdÂPD­eQ*¨Þ¯®¸"cãú5B: Íí–`Kµ€ k •i ãþµ‰ýÞqH1«Ëâåß²f§Ö.ÄØ0hPex.à éHUhÅo6Ðó=¿Õ±Í몴OäÒ¥+³%+æÉžL€ Ô‘&ÔÒÃÚl µ§³u‘«n ´0ýš7Ç5m,6å勯ÿÙUãwVœÃ!ò1ÏUu\‘Kj1E‚Ö{&Î%Eu2ª#iJµ6ï¡:ïm8¾_,%yÕ(t”0I€+1Qa(1Ëñ®ÁÔ`3£¢c_*5i°¢ð&Àj–ìâ›{ /¦ºÊ4Ša o}U)S(¥´º×§XÝk¯îU Ÿ0C ’„ZhmM-}'ÁÃ-^Ç«zU®æ›' _þ³SÌY³±Òd$/lÑLü¸“L6«çÎhÛRL<¦§øxëv±·¸feÄóÛ·7õè$ZÇD‹ì‚Bñö¦lñú¦­Uª8–©•šÐˆ4µÄ›ø¢©ÿ®ëO+6¢÷B{›¶~ýšËѬ±L†J•ôÄÑãÅÅùÓ×%ö›'¥šÕmÉJ¿…ìØ¼gL Öê^9ûh„‘EÅç`oÙ?•ÉÓ^ÝKÈ×°º×»¼B:|ÂHjéâ‘Em¦Í…é[è¼ÆÜÍEò‘]BjO<¢¹X¶g¿øÏŸkk¬žvƤ ~iÝfñÓî½âœ¶­ÄÔý‹m;ÅžâªÍfáÎÖˆ×(_»Þµ½/Þ « ‰W7?àhC7ÇÃzø´»)Õ¥dz0hs•ÍíJÀ˜Ñ(6~^Ë… sj»®\hHhu/ãÀÞs1¢k$V÷ºÏðX«}ø±Ù¿7˜ÐÊïÉ´ Úáz³Ã¢E»í0Þ3&а D’PK‚m¶` ¡6 ­UÐW|XËâ’NmÅBhb¯îÒ^äë¦xgs¶xoó6Ë\`Bï¢I”S¼uò`ñÓ®=âÑ?ÖŠ. q–æµGãx±5¯ÐVÛw@hP¿-ð”ˆñGuíccÄGÐΞز¹UÊ#u埘”¸½W7ѳq‚Ø’W`Åyçïl+N ÌnEÚ“Z· ÁJ|¹m‡xfÕíЬ4Ã[!ŠLS¼…ø¯nØrH;§ÿ¾Úë÷Þ–mbtÏÎâ˜&Ä‚UìJ¸g›ùÁÇ[BÃ8(³òÖìÄĉ…¸v¶°½ìÖAÐí‹Ægí/È}lÝÀ~ÿ‡Á(s»-Z¾Òç=`P‰‰®š:ËÓŽ´V÷XÝ 8ÿ‡ „ØEBªyåœÇƒ7+fÊ¡L ¡ˆ$¡–®! ZöFŸÇCr±‡ÑL4r9Å '@½ç&·áóýʽÄÑ>3~[-xJL&öî)r‹uq÷’ßÄ%ÛŠÝCÜøã2«mb£Å#z‹_ ä~°e£ø6´Ãgì[Ÿ_û·ØŒóx§Ṡ]í®C[5‡°ÚÕ*/Ç£‹$¡gBÃúʆÍb[~¡(„Kî"˜œ<ã·U¢yt”˜ÜûHñí?»ÅÖ‚+<ПDØñzþ¯UW.Bð¶_ˆuD¸vK—Ò*jÏÒ¶vÀ€DL4:ÛèxãK$(S§ ¦ Ñ#ÏÒ7}»t)߈`ÄdÁð®îeˆ«Ö ãra”»º×¯XÝkžŒ¯–š[ÇcL ˆ$¡–-r>—%|•ø†ðw.ìkïÞ'~ߟ#ÎkßZôoÖÔÒnî(,]¡™]¹w¿•{chm…æsÊò?ÅÞ"e'ûä >¢m\ŒØ^PdÅ¡<ÒVüé­MŽnˆý°¥µóÈÃù›Ð´Ò`®…РÞÒ£³ؼ©øfû.q6Úo ð¾´~³7=œØª…X¸c·ø;·ÐÚ6ææ‹ámZ@ø=T[KñɦöŽ^ÝÅ<ØÓî*ªšé¥/u¾¼m¿ˆØ÷X¶l)z&x¿K cÛÑÐ,õ¶¡(އš9{ŸZ?°ßsÊ!ævÿyÅ;œ÷L R lHLbbQ¬îu%~'%«{ù©dñQ «{‰×¥p¾ÚuéÒ¿"•·› 0C D’PK­'A+ìÂÖ_håULvXj{ë ¬N„°Kf)}Ž´âÓÒ°v÷ µ?î¨xPÙ‰ÐÎ^ßµ£è'¶ä—hZ]˜J+ [“(×!šU'†mõkÖØ¦OoÓÒ[n§øS4¯GéA<êþpboñ+´ÅY«7úWåÜ—uUÒ5˜¸X±ŒÞfž¤mÃÀ~à SaI^q 6š}ƒ¾ 6GÇ=«Ýíí8žÝ½q³åüù:…³c‘@`ýÇ)݉¯WJïrˆ] À´«{‰7„Âê^K­—ÆH@ÃmdL Š"M¨µñØ‚­}Ò¾0Èé¯ìi²î\ò«XU*Û“°KŽl^ËsƒÌïÚ'Æ-úE4s¹Äû§±¢#Õ££Ÿ°ª+Sx =½ ;ßY«7”—µåO‚ñÃ0 -òý¿ü¨o©0ýÁÀðhÀæWÿº.Y1­˜¿í¤þ-óóÕ-°¼€»QËÐYKH¸gáð¬õ9û¶Áö6+Fºžk¿xqY•;EfÇõe*c­îezŒ^š„Gâ?fßÊ1W÷ Dˆý˜ð'16þ ¯ó¿1«-ìdés>™Ѐ°}0%Õ½“er@»Ž‡Mn°. 6¼Í¢¢ÄÎÂb¬¡‰«»u(“tÉž}☠jÑTtKˆàÕ%÷ýöݰµm)áOš[ ëWVSKóáfô=Z´ƒ?ÙäöjÜHôÁlåitËÌ'AhûýòÝ—®˜Þ}ÉŠ4v>^Thxï¤ÀôÉv·÷šž XÔáÝ œMö…AÀ™@%@«{­ÐoÂÚľKMÓƒ·fó¼o—hñ{Ø‹íyéÐÎìÖíÈøÜŠ/ ðñ3B¨£äj1&pX Dª¦– “â³Fݘ\Сxû”’ÙîYú»H½ì$ £ùnÉm†àûó‚%AÕ£È0­Y ®0K‚1ÍPð'lpm÷ÄŸë„û¸^âñ},¯O³wªÃÓ«Ö‹IÇö'k™?äêºe×k›/Pä611°½-‚íôäÿ æäM[ÉfkÄ"œ®´“þy~¼eÈÅzq²©ÔMÐÛÒÂPÜÂÒVˆ‹±TïÅë?|wÃÚýf‹¨¸{üðà gÇêku/U|9ÆŽÒê^'¢Îø*qHÍiu¯$EèjÊO½«{-^~HDö`L€ TD dÁ.)5}>•‘6œöuØ‘@êÉÆØZ`k{íŸð´l~ô¯­éEáU3®1ÌH„Ðâ-€f1 —ÿªºÿgï:£(ºðÌÞ]z!ôE"%±â¯bÁ®`AÁF*úÛ=%lØ! 6,X°üö† ¤„–Ðk€ôäÊîÎÿ½½ÛË%\ KØÍÎNŸ·s³ß¼yó‰ PI¤À—£Cd$Î@uz;’½%™Y:xVÖïµ÷ÇîÎc!ùE+ß}!ù”V€‹=œÈÚ×P%œì±,† 3o/Ê¿ª‹Hö–¬"Uü}:ðñÿ” )5fÕª?ëFÉjN²îåtڮÎÃÍXžÑ8>f§Ó“ Ãû;.ÄG–À¯aÏu@ æÕ9 0( !0ÈPÏÐÕyyWS0«ç'ùÙã9]v·bÁyŽãç­˜Çxn8 ¸ˆ}Š?Ý=t@w‡C™•¿cÁ¶¥E¹ppoЇ‹mÜ%IÌâa|~×Åkò\ÑÆ_ƒ'‡9çœ^`+¾ öX÷ºcÖ—u/¬¦Å/“ý("(ôsÃÉÉyWF­š2Neñƒ¦ü^¾5r tZºjºðˆ¸üòÉÛsܤ •Œ:œUÖ-qÖ(¯óBödo?03Ól(œw)<.Kdø ÔȺ—Z\pƒQÚ+±àr ê—ía«A³îÅ$ñað©aÝ«Þ^‡Q°Aƒ €ja`PÀ)À¿ûŽÏ£ zo!,-ß È07œš ‚Û=2“ï÷6‡ÏfCí‘Û%1œA:£Y÷ÊâêÅxdÝë:}V¬âË`-û#7}lX÷ªHãÙ €Aú¢€j닲F¹ê˜8¾EN84lØc……ù·Ò&Ôö+«FÄAŽñíR.ffÇõk2KiÑËVo,‹7|ªOͺ×ÿ¾8_UùÍÙLÉTæ:QZ¡(Îø:H$Y؇†u¯ Ä1 h  ¶šdîÊHýUE=³Õ,¦VÉéôÐûçı±KVÁzdÙV_­ ug&U_ôêΦ Õ*Šý e†ó/ ´^¼˜¬}¤Òµcpÿ¡²¬NÀ2œ0wmÿâ‰Qñ€êT÷öw“IšÝ¥K·Eü“Ojlο(`´¦!( Y÷bÊ-°î5 c Ö½0ª*L74b>ZhX÷jˆ7bÔaPÀ À‰(`€ÚQÈ+^³Öç46iͯГãíó»àŒ ò _™Z6Ç"qÖ18ˆÑa³k;µc{Kl'Àײ;M>{—å«—¢“Kwž{îƒriñaŒÇ:ç´²Ž‹ E¹ +kk¬–½e–,oE¯X‘]oø ”Q {pÿ¾B!ë^üfͺWY”LJyg'Ô]/”`ÁãöÅð0(à0@m5^ÂÙà\î,.aJíìîîѬ&n?Þ¹— …+;´Ñô¹ÎЋå”:ØÐVÍÙþR{:^É|îܳãØö¢bÖ»Y8Ë€‘„é™[]ù Ç6 ºkI ׂó²ë/c·‘\X#ã¤Çö¯œ#lTtèÅ,eý¶Õmll·NlX›–Ð%{˜½“µSëÉ]Ý;³³Z4gívöƦlÖÆF´oËZãNí µñ=º°î0À°:nI·­ª¾&žÖæt›±åGÊÌô~¼s¶<µößjPÉHz2(ý×_¹¨w&]Yƒú_Äu‚àüê²Sè¢Àî$Yq<™5 ß÷Pn?»k—nß‚{Û(Ô­ šž*u’u/!”›° ºE•U·1„ò‹epd!}Š_ŬX½Ä­gùT!‘ÑOƒ ŽÑØHÚ}RšI–¸²LÉ…@ß+Y#gÛ" ºhÉE˜-9ìÆ?–³<§“߯¥)&<„->x˜Ýüg¬ˆ5gÑ¡!Z¾‹k]¬…¹üA° Nì˜%ìÀé ÑíÙÝÿ¬fÃÂ¥í[kõП ÙøekÙ5ÚjVËzE†³¾Í"Yü²5,3·€è؆‘nZºÏÏÞÍž]·™]Ѿ ;P>îŸ5àí×<’ ðÜ׸e«ÙX+ÓݶÂ"Öí0\ã¢@·«é¶jí&ÉÒ™qé)]zU`šI\®*ÊWYÙ[vÀºSâ®súkô4ƽéSÀ˺×*²î%„ú =å­{qvóÒÛšu¯«®ëÐ}Õê‰ÝW®ùÛ´M|=4(ÐX)p*sj…¬È6 Âbxy´BG\ϵGÉž@y`*Ÿ}Åa—ÚP²ØÕ*(À“xù‘\& •í·e`Y8% ðéíÖçæÃ¸‚жÿI쬉‘(ÀÀ–e†"Ö¸’¾Ûm…Åì Z’‡íÏÔŠÊB8¹­Åw˜üýbÛÀzØ@˜Ì¥rÛ‡±0½KõQhãM]‡ÌñRŸ«ãL@L%fSóà˜öæÒì}ÕÉj¤­c t]±âŠœŠƒ>)Ûÿ÷å•8D£âR x×`¬#Å;lâiˆ&|‘³|Õ¯h©ãá'ÅiÖ½Ty$ŒÀu¯¡hV¥Ö½˜>Œaüú—Ÿ¼<£ T‰§2¨e›=7‚cAí!ˆ´ r¼<»ÓøˆCêíìªï]»BU•¹|pr P’#N©·sjXÛ¢hMôŽuù{„‡±õyŒäk·@$á¨Ý¡™º½où:-Aí¯6¯ö¬6³´­;²ÿϰîÕÐoɨϠ€Aº¢À© jµ9ÝVRœÓBQ¹Zìå œR_&Îç%íZiQ‹fÏõï­i!Ø ŽlM\&i.LÖ~xî@¶\Øb§o0ì«ì<ä»´}+öxïì¯CG4î/q€·@NöÝ¡4Žð·û²]Éõvßí=È<£›sV?;ü܆­l ¸´WA¶÷½³°Gò˜Š jr=ÂÂXvQ‰wöãú‰Ž\6@$ØqïÁ[µü©÷;³g)=“10ÁyÜÌFdƒP fÉê¨hô>•%©7@5>qo/(«\tzÉ&Û§Á¨ÃÇfOuF+Kbøüššu¯Òâ«!hróñ¬{ágþ3ô²È°îåׯÔhœAƒU¤@ù}ó*fòNŸ8u1=§'' £»;€ ÂE,QtmÛgàYC†^:"yKÛ–qâmvÚ® `pòšÚa1”`È51ò×ÔÕ¦ R/VQ­—2¾h;ž9Ý@ÈíÊà{ç­XÖkƒbÙ›·k\àªô­Uõ™×TE+¼¬{Ý‚÷7 ·u¯²·‰)AÅáÁ?v? ’ ë^e¤aã’¦â&UI·NY5>qê8lmŒöŠöx1°¤&'ÍñÀc¤/£†Ac<”—Ïט¨˜¦.ŸO%N­‹ýèÒEÛ±jæÊ²^4<¿U‘= ö„²hlZæ Fȹ¤4oÿÔ¦ oPª7€äv±x\Gò³wY¤á‹Ýû« h©,ÐQȪz4û§ŸFµ¹nÄ3:vÞÑ1@(ÎŒÞóÒ&gÞÿJSéÙXž»fdlB[ÄVõ“§íâÞB4zÞ~ {X2Sgç—½×w¾$ñ´®+Ö¬ÑãëûŽÅF7¨˜À¹èTÖKbj®¯í±-0B œš—8u#`ÀY*‡Q&nBÙm}€nmFøÇ3èæ¾ûJvʇÅ'%ß½‹3g{L\º=uêä¿Oy¢0(Ð@ДBù3aGèáy<}jâ+uYµ>Û׸ÌF¨¯ÄŠ%Å«jéÄÛ–ýÎ>ð  /$<ˆoj^kš ÌSʾ¿P´(´‰ +–>½äÇo@çsp¨¥Sfθ•ifÛFþ>È“ ëçRî‹|à_êÅžð—Cdh,ƒz ï² 8Opu<Þá ¨ý!tüp~ÁÁÔ®BúÒ£ ­Ö…‡œ[Ú‹É"1hZëTæÂ ôX¬ñ#iOqÚµBÈ;¦Á;·™zÚ.,áD÷O’©Û‘yRq(Ñ}§(J©Åix¢ôÁ*q¹3çÏ9KQ•À1zÉR“$ZwÛ8âîžT×ÈÆòI£Õ¾¸¸–%\¹ (.€«›Þ; ìŒb{š·.ÌjÝvïѰpH˜Úäá·Vîôôew®HBÍW9 k[T`¾tý*)@³nêF©4Y•èîôIL³tב­ž‡Ð®î÷`ÜJòCÜ}Ùwˆz8,ܵoƒ™·uqhU/5+)bQ¥ÅÚ $$êPYý*8Á%¬ØÈòBBÙ‘°pv(,B9Á‰“vk¨ò3T…,Ò·¾5ë^ª|3ä!nA_Á >Ö¡yÐÇOÌÐ\ 3É´ 86•bPÀ €Aÿ¥€Õ*¤}Δ¯—˜¸é?uµc¢ÏÇ5î9ä#¾Fævs’“Ö¸†ËH\BR «‹ ·–.¸î¾áâ±ý®( 4±¬Vá’!c ªTâH†¶Û¡"5Ä!³›7~üÓ§~Œ¤Ä¡Õ¹´Eð“y2Ï9øYÜ´Hh{èäZzÖç‡ÁqºmØxâòž4?%9•ïMOI¼ú¤5¢U .)ÿyĈ[vFµœ´£yËÞ‡"š]áMè5+)aá¶R™×P\$ûj‚º7”w ©œX¢; çZj²0Ä @`Ü®,-‹ûJÄðæÄ¢lä÷æÎzsi]¤£)MÏSóü#š±Í[³¶…¹}ÈǘNÕMýüe"›±ÝQ-Ø®mä€@³EQí½öïÌ’½¹uT1ýtÊ;ôû(zõ9©àŠqÍonÝÃåO hD Ž­MÎÿ³¶´úÕEÓé P+ †®h ¦6©¿$‚àÍ­%N-©øj†+,vÈÙ½œÑ¹a"',ç†Yª¤Çù›´#=´QENMËAT‘;ŽüuKþ|oÕ_‹— ãGpÆåÍ¥%$à“‹Ôk^Ú#?3ÀÒ5p`÷—§Œ¼-Þjå<à†wËûPë×Ë _{ãªq|Ò´sU}L‹ð¥–Å…j§£‡¥ÖE¬eq3+XËЛ÷ža*>S— £ î!S1®âsÉŸÉ6µiÏv6oÅTý3wï`çoÍ$p_ý¬{}ý¬{Q¿ gPÀ €A&@ñÖZ«ÌÑ‘ÔéÕEw¼?95*@`2¶Ê¦šBÍ-gOš”[£B6qkI0W$.âÒ¨%õ@¸B‚‚BBÎ~Å9ѧŸqN€%€´%§Iv(”ÅVj­i†ò•#SÂÐ;nõ;dgÁîÍ›~_ú÷?––ˆ¥÷N –üù¸HU‡W9.-žË¹Þóß:‡)òÇÀ*Ê"ø/a,è¦åcÇRy æ&LŸ¥ˇ±KžP;­Á*nd%X§õbNå¬:®r:•Óî5Å=ÄB ‰ÀÅ£uRíg/vîµêqt§ÿ·Š×¹­zÊp*æw@Cƶp;YÎ}tÖ ëlˆø]siGaBRò=sädž ­ß½£Aê”:—¬Æ…š$Ó²*§¨%ê2¿Æ5\FúÊ’œ'©!¢þë—®÷@µÙJ”Å_}ú+ ‹þÞ½÷™¢{žÞ#$,<*0$$Ül2s@e4Mç–ÕsÊN{QIiaiqáÑ[6oÚš¹f;:Lò²´OJ –.’¡%0Kt%úºÐ <•¹ ·Ýówÿ…ïôwØ$g{‰+¸¨˜ÛVôý ýªµ·Æo¨,o]‡Óø%Ž£ã¹®Ën*åá€ÍíÂ)§(й×=¬GΓDY€TÕdÖ*õ•V> %DEÝ U#€êZœ+%[ÊPО¢ù¡¾Œ¹oëvä_Ñ©[Àˆf¯KL>³ƒ¹çýVë(å1\ (0á©égÃByºP w ûO5(ÂÈbPÀ @#¡@­Aíì©“2 W»§Ñèscµ´µMDÚ'`F`Öhá'@Fq¤[Õ²mú,\Ûá§4ôe>ÕPà¸.6\ Àâ"@›çö=‰vDß*‰¬uWŽUˆËÎOO‚Òÿ§Ã]N¶´ÏÜô[3ÇÆÿaõî´ñ ?4žë½²FXvd^ûð¿­ ÄY»¶ñ@§•Nûë? &ôópײŸŠ®ß_‘D³åÒ–Å»ÖCúOM×ïM?h{^ö&¾±mGpn;ݳOÞLâR·7ÂaãM†Q™ó1L¿m¿x#F# Ôô/G­jçQ|ˆ^Àá |év¬Uáõ“™úMb$_Kà•Ô|…ã"ñºÓA2úW–ÒPZoð['tC™þìEÓ­¾ ±KK –8´t'õ]vuP«çGPÕ@ì•*`,Ñ; %üÉ̱ /¸ëço”äóQïïøð=6'%ñÅú©¥ñ–Ÿ˜ò˜êó=`}öï­ìÕÒÏq äJÀÕ%¬ã?! Œüé·¥u{[¨s”øè¹M=! ÇPL—¹Pö~>ÄŠºi0(à s¤ÚÓ¬‰+kÓ Zsj©ò–oì“÷LT{=Þš64Ýš@ÇŸ}v ¤éŽž ¼'Rmt5n-îD'ØÒ—÷Tq:]ˆVÞ\Zz¿Ä©Õ/¹D·ZäeÄ•…ØÁPhsúš¸µ‚Ö0ñ|ïy©}:·ìÿÝWPuê0^CTgÎë_»Ú›;¼Q§…7žJVuFÇü£ìŒƒ{°Ê€¦Y/P«Ëú8³šE¯Ýàë@+Ë¡œU¹$¦t.gäwÿ2šíÊè×ýèAv0Ve_®Ú´°þòRût`«ûuPK Í›SëÍ­¥/9ýîzjZõwGwû:—–@«ú‰+«_N´£tz^x«ïHŽ6Á6™Šqt•†ß˜]‡w÷èóÁœë2ow°ú¥úÎòyBbÊ{ˆíë ǸΛŠúBˆÓ&b÷ï"ÃÞ1.?½m‡yÇêáz=ëB>zÝõtMz¸žöÎß±à(?Þi‚5åÌÙÖ)ët’÷*R’Hé*vÆHfP iRË{ú ÔúÌR€Z"1tŒýóƒ U¼  ÀÀ»£qlé“©ƒ7:Aà†&A³Þ€V Dð)átºè´!ðJш.o0Ki(}­]Æ­ ‡a…ìÛFö&--–ˆË7§îWô^~õ†Ññkj[ qh Ђë8šJŸšôcmËljù‰K«*jß®¹‡™Óâž*è`9 V»ÓL¤…RœW¸–NÓ(¡‘_#…ND?§äZL`¬ÒΑá&¾öZ |¨´ 7‡íe½¯ÈW“­ÖßÌûä¿–S\{sdzO´¨NHœz'N_ZÒ¦NI÷UÞÉ #‹ûîd6·f‚NFúî$Qç>ÅÎDŒÄÍw§NtÊY¬3PKïløØ2¡²¹|¨'dh2¶ô•Õ9ŒôcôÞf'úèb: ­ÈSB’&ëˆ6t]è":éYºëát¯Sç6ßg^Z&0 Ö„{'&‹?bç¦_¿~lüÏ5­P“¡…Èò÷vÚºµ=]Óvù[>üŽÏÆ`-J‹¸ ±ƒr_ ÑAŽžÉ¯ƒ5 ƒóÒ³húƒ‘_#Çñè·/"J`òÉ ŽYïJlü­&4àXÍ<ÕN®íü$%ÃŒ4ÿ¯í`A7Œ~sæ h°Ø…ŸÅ‚ö¦žÏxk±Ø×n »XªhsøÚÇð´!~ÆŒHµÐù}\'N›¶èõÉ“ëEœ‚€6ìçHÞíô4¢‚»³ÝÐ×—h¯ÂZUr:ílO.Aø÷&0yvò›õ,Vë»XÝá8U/€e¡0!Z,§¿a}‚t {\|ZšEì:äÀ±×ýõ ãÿ ¼^0ïê©°<4¾q¾" EYqeáüI:(¶Î‹¨SPK­Ó€mâÔðΡÃ7ñ‰ÉßCa"}ãçzlu€FŸ[o:ˆ¥»î×?Å:eÁºtúTô×+!2Ç$¼Ög~Ú&¦pè³0!ÂÑo{½Ÿ~çÆÛãTµrÒCëRÛ%F«Ð¶€Ì.ä½ÂàЂjÏU&íÄfF¢˜)=2°ôK ‘€_&fâ¤{Ä 8ñ®\ñž_ ±yD¼õŋӭ’‘šj¹ô'ŸÌPš‚d©/@K Ú+ÿõ%dIôî?ÇkàãÏ=ž[àüýëŽq #!ìüâ;á'‹þ_,ß«ç—4}Ð^çžåK¹aèá§Æ]„ÛeÇËèëM¹¿dù ã{Þ÷5®~x&v¿ëVƒZê!‰"`evÚ^çÞûñq{" ï‹bUÅò ìüî9|nÇøuÐ ´]kúá¿o±ž ‡ªFÒ;zRܦC¬(вõß‘±v»°3½?üå—Ÿír¤h÷ñÚ„†‡a’nà m0á»q`Øct(ìDÛ}Ç+÷‰Û­o‰di ´XÕ ú]{¤xà¿'-Å»'Œžõ|úAx§F~/úÙ¡;«Yk‘B€öpsæºIiÜü#H@³.Á»Ê¿eγS¾Ó›y_ÒŒÞNUþ ¿¾L¶?…ðô8ýQìúÐè>îõmæ¿Ö—›•8‹/AÕù¨¾\P^¡r~µÝ18GDvyñ±ÇèÐ0#æ\¤œñê”)ž³\È#Oعr¥7Іv3˜:GA”ë­´g§üÔ{6!ñ¹Óì΂/ð¾OÇäý%în`럽©PK]uRôℤéqŠªÐ‰¶h\íqé*›àõo§9Øùwk›nëÂìÎ’Ø=y«3ÛGÆÚ,RßÓ<8Æa‘{,ÚFÛr•¸ýÏÀšd'V0ôÐVB%_Á\Z¥ U:Ê"í¥¥dÚŠþè wÝ«Eº±oÅ") ¹“•_ÂWF@—¡^Ck„Þh?j©9€í bGÃhm31>.5%±Ê;. ’$? qƒ§µAÅyŠ7 ¥v¼9õÉ ãŸJ¹WQÄ÷\ˆñ÷MI~éÍ”ÄÞm´È‘P=öÞùùø5ÑY…ŸÍÿÕ- ¹åoWQÈßö^Oœv¥ÂÔGÀzè ¦ËLfóc©ÖI;(½îˆÛp’Ř>cP×fp~W0³iWÔÅÎâWÐ ê ý@b˜Toêeèwh¨nA¿c”ó·h)ν»„ü$r­H‰˜{ï'Ú ý»(W¿í͉ýtQ„ GšÇß\^4‹¯Ã^OJjrÒb*‡œWÿ¿gZOá{r¦aÊ¿}±£}§S]û•½w€uöNGy‘ˆÛ…í£­ÃŸý<šsîM|î ²OF}g¹5cÐ7‚ô­k¢#uщªÒVÏ[á.’ŒMN´U}ã$Ö»Òz«BJlµ i¯3%ý¼™7Ze²°ÉŠ ½ñèœ z=…ÂC",¢PžÀaÈÓ4Ú‚û[¤*ðxû…$ws/Óú‚› ÕY? Vp¸Šw»«ë¯7PëÝ7Èð3üjB¸…i‘¼”-Ây!å?Ø!'"èŸÎ-;®•_5icSÉÓÞtöÂýêß)»"[v>ýÈ~ɵ«ŠOŽÞA€Dr..,Îà<Àô*¼ ê]‹;nâ?`Qü}‹OKNz›Ò•õŸ+lwNGÐñ¤„®}X½Çú›=…¤fÀâ %p1¿H0õYGN韌Êy0%¥MI‰ówôµhð!‚6ຠ-ŒEyÿ"Ï;–Ë\UÚR¹¾Ê”¢"Ìoä:n‡ÿ ,|ž@ºg}¥ÕêJJ¿Ï™2´Š§÷…¾àݲAŠ“ÿŠûQŠ÷vhY¡ Z‰ôóâ(Ý¥h׬„¤äð?ìÞÛOc7Þ:£GšõÉ] Öä•ó¼sU߈„êç:6Ƭá 4 dŒJÈgÁQ‰åé­ÆwÃÎû~Š]° J3îµ§€Õz¡ÌL<Þf P¶Dµ³YSë%ÊÓ.|=Ht¨Ö¦õÿÅ>™–Â>™>©0÷»ê‡ï5ÿ"€á†j¿§Í }ÕcòòÖ%ù,jÔ$U½Æ'%ï ÍœI²Ž†«&Æ'Ný]ÕÌVõäª P«¹C:gµ’ÌY®p¡§w'Ã2WÁRg¾ Vio å@‚öËÓ¯¯¤,hz±%€"8p’iHzrâ%ĵîUŒ›'®8DCüj l4æÍ%,ÌÒyNrÒÀ–sšpö'-EÈg….i:DnâϧK•®à²¿éS¦ì(þ )t1 0}oMP–bˆATÚ¸Â6&êåP¿×ú|[´)E‹ãâJ€š8´cg&íàÊ}ñ¾iÓZx—‰úúẔ›¤+;˜Ï MOžr6ÅkÛ,ÒxçVPÇÐârôçfŠò¾•îäJlâäoŽöH7u>ÏÍü6W, 'ã;¯XÊ«mÝy}Þœ¥ —¤û´H.&M°Nñ™Õ¡¸¹Ý@×»©,!IWƒnƒÍ‘ngbÜt¨X/r>ðŒ«/Ú›Ïí„ôט-]îíx$$>OÀ¶RGï’"UÅ„Y +Z¨ø©Ã83œAÆE £F92o¿“ÅLOË;OU ÿ:sþœŽž0ÃSk 7ñ‘Å–@ySTåpP(À¢@–€)U7€Õüb&ã˪Å#­îW<þºËOì©õþÁºÄƲŽ}ûº@,ê'§hmpÕÝ¿?sÚlùwß°˜˜ `……lßžÝlÿî]¹ /íß³‹Ø»×Ófêk}¶¿"ý‚U'ke+d søi¹ûL °0{ ¸oêsÅGŠÖÄ'Íиuµ~±§Pªà‰tÕ[—oGeãk\ØÇ8Dé°§­ârsÙtŠHÜU †¹äW™:œî>Ó6‚À+Õ9S'­ð¤1óùš_p ð‘¿“]wñ4:#?-ZnÜ@›Bªç:XzÞ ô€¶öÅ5Â)Vl}®^窔¤ÈöËÑþ0«½Ï8tæriÉ“¿EŸþ@£›9ŠÕ+ôpý:¿þì”o¨ýHçWéÖÉ›ÒHtCs&säšG°ö¤vŒüÀb­µ;çë´8ü  ü—bÀ¥mçÒþ€ÀjÐV/Ç×ÝÉœAhëo Ó|ô'Hq*oøJGaÕ¡ú} hn¢Å.îB *V+èy¤b®·PÆÙ,Yÿ,ëãıýY+G8S|Sp "~ÐeôÁ¿(àžÌé=7u/6g_Ä—‚ÖŽ½dEù3vÁ;­}W¶µ¸ñ¶&íÙÄ/!‡ÖW–é{ÃZœ{PU•H¹Äê°a––ÁÁ&9>-˜`ñS¤î§.Ó'§B˜¯ßµdzž iO”Ÿò0ýwå vþ£Ø\ˆ@·.kÖÏpSý=aÙ™ëYÖ¦Ùp³…uŠ=“m[»ÖÕf-5Ò£ĽÛu¢úµ>#¿'O5Û¢ü¡Š…–Úy”£˜íiÖ _ìå8¸ÑO—ùs7ݸœ4¢ÕŸƒ<£¦® C"•;K*üå®ôÞ)ƒY¹ù ÞL£Ã)Ú;]9?Ç• ˜Ô€*Å p¹®tÓõÈRhH°TgjáÜj¿½ßúÜL§â|`êÌØÍ\vÆAÜaȉ´aÞîîúáø:˜Æ! ÎÇäÒÓÕ•²¿&³äíeAláÂ…¦_Ön…>ŽFp7`Ôö²|4¤,I±6ðç……¸êÑ^˜FòÀEyök€×Â2uÀ‡üU¢mYùÇ÷K––ˆ«ð®.OHœv}j|ü"h†*—©:ô@?£µÌiö.„ÚÙì·K}XN'J§*â3Œ šC{‚\cˆicEoÌ÷zý±7fÂmoØ0vüL¬LoÅŒäp·¸‹";ÿì3oΣ£•³’Ÿø7=%é<‰›F¨&ó¢£aŽ]a-Ù–Èv"+¼r48LA('r qr]âšØ=ãÒEô»'Œ¶àÁeõ×E*˯+ `¿}õ% kÖŒõ¿èb-?QUÁWŠòDD°èÓNg›Ö­e6ˆlÿw#;}ÐàrõhéÁÙ­ný.n´»O5hUó[ ÈÖ¡$×$ aQ„ƒd ç'˜Içv¶¦ÃXÇi–&v Jb[Å4vG€hPN¨vw™h¯˜\Ö@@õdxÏûœøñ›”°$x{ À¦ö­çÎpUëÖ‘V6zÌ"xÔ›‹ºz¨%ÊM'ªv­>óhJÐó N&?ú qXõpº+¬å1ôûyí–T,­?@ôYÈD«Õ$ˆÜ©çËÈEQŒµ5ŸûJ}mŒ-È·€F¦õ ÉPKÆMôô¸W‰¶^éëÕ´Ap1…AÎ÷•ûž™ Zy¸Ê®ðjÐCˆ@w…¹î»÷­ÄûÁ¼_FZMÄ…º°Ü8<•ÆŠjâ'ýÌÓxkò`RÿæÝöšøÍ5Édä1(àOØ0&þ£ÞóÒs±µ“äE{l×þÞwî[—®{ ЮŽ(@[ƒ(êÛøÄi×âàØ;XéG‘XH„àÖ`âÄ4‰ q4ƒ»}R4Jóªë@nZ¼–ùô{µò£¬ò Ø’_~bC‡_ÆÖ‚kKíp‰H¬Ç€8MëÁ9—`g ¿”…ü††…3 r¶›M«— Ù4æD»«U¿Ö]WjÔþjåg¬™³Øt$ ¼ÿxëô.O·S ×ðhmîš0æÚÚœñhiý)çÀŽøh W‡EaËËEâARœÄI;ì ' Šä„¦OÛìíhÛ‚yãUX Fð wœ·ÿù'ž(x;‚ßc »b#`]/†4:@ö I$ ¯ÇpX½Û¤ù…‘ @JX¿ª‡m·š:°ˆ;ÀVLëR#&ßCáØ¿]ߎ'-ªfø’±È‚(m&"N&æ®/A·Ûð5~³Gñ“;0XZà­ÿ·ª´­Ø–ã=CãÃì½rò Mœ¢ä>÷VªÍ‰z¦êЃsÈLi]òˆ˜èÅ`‘ÓA›{ÝdÑï?ÂÚanûrÈžó(z¸Còj¾"Ô”‹kÓƒS[êyý†¶?[]†¾…p+™)¿ÆÎMu}ü¦¥»!÷Y§õ7eêOB(‹p) ‹µ³5…ª7–8­0¤D\O»:÷Róãw-Ž8²ðWäĺ8¥UÏOÓºêæ°þýûbVR\̆]}-Ôv9=ÅzÄöíÚŲ¶nf;²³ØúÕ«`KÂÄzöë著ފ̲ýÕí¿IgæÈòé{$5ÖÓ6<@ÐÓZÀéyÌCen¼5¥æ¥Y‚%Ó›ºF‚²{B½Sƶx(Æõùôlâb‰~Ì]X~#n°Íi87¬b<™×ÃPÞ_äW„ M´ºt¹ñÖZ{äM9×8Ι§y§ÑÓzßIãÁDës½Ã´r„€¬'úÉ¥Mz*sq²%h[ðr³àž+a—Æ[§yâH/*’ Ø„ Eàb¯,¾½ÅÄ:¨N¦†IÁËôD2wÞ¡ûuN-mŃ s¾~Nrâ­x÷“Á(o@«å©mõ:Nt×T˜qój'èû_¼“ ï<Õ¡¦¯(/Ê8*â¢õrè@$¾}Ç,0ð4ùb,î$tzzº{ïð÷saЯ5£µÖ4xÇ TBõ£ÇÿÑ{~úEXì}vs\Í 'ùSïys®Þ0fܯ•d3‚«H¨›¹ßáT^‘ S(˜ AÂa“>]³4®¬çsYÅB½“a†¦¯~u¶i™ÀÙfÁT†üß|û5»uôSëÀa¶6mYÇè.lÁûsÙÆ™ž¢Û!¬W\[¶ZnÜŽD´rô€êÞkÐþrUT!¿ã› ‡¿›Ëå5N*.ŠíñöÏë6ÓÁ­«UÆ¿g 2¡ì_¼ÒhEVûal`p/7…š¦Vl(7 ØñÐ Ðzó6äÙ vÄoa¥u伊éõç´äÇ·â7ùÖ9O*‚ýYÊ¿ñóùüÍ–XêÏäC_!m"¥ç’9‰ 'µïVRá„öa‹^@”ýÌÖíN÷¥N´šÔŽ¡-Ñ I)ß#ÍF¤Ù.°&Æ@åè°ø›Óy8Ðtà* ý„NÚÍ¤é µm 6…,ÔÓJ\ZÐ~5¸£SÇ%¦@DAbæà‡fY=6mOçNu êþœŽk)Âynôåeê£^New¥S³l¾ûÐ!ô­u‘\ü¸Å?a2&v_€þ\”åÑŠ“/£ÿûßõíÀ{9H'ùóWcû>¤YÈd¹¯:´­¬]¾Âé@Þs:ê_1žnU•³­‰ÿ ý‹ñ®†9ÿtýûEý!‚ÓæåûLõ˜ÍOÊNÞn;”¿4úï6ÏÝ…3çrkæ‘'®Ø°FöŒO’á 4 l¸-~&§aØVÒä´ð£ ÅdúMŸ¹éW6^6|O0 ¾}’¯0ÅÁŠL |p’‘ ôÈ…ê¼äf)Ž8ŸzÏ]ϧß)] ò%4ÙYwþu—Í7–tÕRxlÿÌn·³Û¶”kÃêukXLLw©…S9*òèíó–ë­ÏöW—~¥R gÛõFrÒvj³áªDÚî§«ÞܨQ£R“„Mƒ{’2Áíì 05Æa‹yL&A…Ö9•žúå_ÛÆ0<ïx™Ùl¾àDÊúÁeœäª“ï@;ÀîiÌw÷¡Þ òhDHŸúäzÆMç¢\ÇŠCÛîAÚ¾hëGf°ÁE‰=BuøAšÿ"ÄŽqXLþÐù=úÖå\º¯€ß‚>Ìz¿˜Ôb陂L!_h½…~ÀH@ó‚-vm%  s`#`è• Pv'µ `ˉ¶?9ÝGô2Žw'àöŽDß¡MCÑÿg[I’ù<°o=àšÊxø™—£`—’º=þœ‰çÁ2qˆê‹â#%ïS¹ªÒÖ•ºêÍa¦É ñ!_9ªCs¨ézôùkЪÑ@W2Y,ñQž†þÚòŽ3–ýxߌ:! ÑÌçOÂOßE,„†©ås4Þ'ŒÃhzè;7½§ÌÔŸñƒ'Y5̵œ&ÊÛ  ¬Ü$×ôz^÷=gáQL˜/B>-@ÂÙÕèªqEõgï8=àÛâ•ÎÈ_#ú9pÖÃFçC$é–9SýS.Îõο¤ê°üW§–æŽû½­ˆ2d Ôd²éj·ôô®쇴ðÜÒ:”tËêqúìó"©µª®æ|y§¥6CÙ©yö”)ûð;§_·O§kal¹ÇX'3½J@©åQŸ¥ù|È:³yqC©H?J  ÎÁçW´7ïµSë? Óö;W¨ý¸¶íͧ…º5;x*©*m=êÀSUzÐû,-)ižš˜¸·*Õ ‡tXNŽææg ÖõPžV¥ŒúH®ÿb*‹Ãat¯©3@mM)gäó{ ô_ðV´CV~Áê_ã6`ÂU°w׆ÛÇÍóûÆûI݇0ö™¸`²ƒ„.pêºS#õoÁ[zÒ«õê‹‘¿fôS •Ê©°ÎGrØÍ+þØ2gb¥ Ó¿ë%!e‚;¶©}FÆ1@Ç‹ô†·S€”öËN9 ?²¼KÎ<­%q†qwNJÓµ“õª¼SØçN7x7‚!ûœǘƒCÛó”>±y'3üõHºµ†Lm=¾$£è“KÕ£ïÙ÷~Úy6Á~³°Vê8ž¯¼Ûûý9̶U{7j±¸)ƒ˜YÅÖJÕÃÁÅ?z*seq®0_SNY#úÁv.Îã‰p{iÁ˜¥¿í·•tŽ-wjºŒÖ>|b76ív› ‹Bàîš3£—-Ûa|¼}ÐËÏ‚ Iá’³¥fam¸&]ãÓ¬‰+ý¬™¦9ã’Rn†îW°2lãÝhÌ;ûAàÙíÍ“S4-Þ‘†¿QQÀצQuÀh¬AªP ÷·š³RMÁ¶U!˜; ÙW…c³°¨\ ÀY⺷(‰X†šCqe jaž4ë=Å ½'ÎÈ<úq n—Pb‘ÅÒb é›#Gšvdg÷\íƒEZ| û€Ä}@X€]·‚uí¥ÿ -Æ»À }±w2¡;*SbæÌ®+V8~ÎÆKjލÕ8„ókck}¼õÅ–LµÇZ$¾÷Mëä-­ýþØ^M­×þñ\æm¡x°„³€ým̃³šÊé¤yC¶Éû‹Óõuhp °6ùg`±þîÊq’\º:nßkðÆ4¢ ¡:&4»GÆ‚.)_Nµ„¿à¼ýzZ_az\Å<¾Òú kŠù‰N¢»DpÖçÄ´”Ä™Þ}õå—_¸ãðþ3è …Çº€.À®¨H_y|…¡Î£8O³R%aàtн%(lcô_åúJß šJ³é)‰¸m m6ÚhPÀ @Í(PåÉ®fŹ ø `[ý÷¡q6vå|>íä 0ƒhÚ .mÙ]÷Q¸·AYx²¢œFþ ôã`6ÀÆdÀ6\~ÁÌ–Ó­OîÒÈUÃ?9çœ^ì,é£ÊÄÑàe¬@k,èߪZEBg*^­v8 ùx¥ÌÎÿm ‡ÓêêJµèe$6(`Pà¤P€¾9†3(pJQÀl•ŸÀÅàî88¶üö c>8¥QÎjVgœ9ÓŠ&ÂN¸j æ&G {ú |FÎ`‰a«éb îpW"Äè ÜÙ]‰OÁüè²ÅI\Y•8ì…Bº6¡³túì©“ÿtÑ«~þî‹‹kY"Ä™tñ¶ÀÑe}ðZè0edUkÄë#y¿?œf€Úª¾Q#A“GñÖäÁŠbd ¢6­Ð?+µ)ÃÈkP ÑQ vÁ‚(E)‚(‚ ØÔBÝ¿iÃíñŸ5ºÎ4`ƒÇ%M»€ åuàÕX˜§UJƒ$“#ˆŒ¸+è’ºp+5‹ÀmÅIÆxÝØVǸîÀ&}4Ã|’™€,Œ›8…« hx.ú½Ðdf³f[§…¦“æ²ââ:Üqrt¹[^WœŽwZ½ÃiLÀL*duaŽôdN3@íINFŪLˆ ÁôÔ¦$^\åL>VüÞøHbhš(PW¿Ó*OH>Ú`hÔX>vì‘3çÍ»Xa¥¿wœNØ ä“ÞóÒ¯Áá±uçê©ñp²³·^ƾûl͹yÁ¡lkÛölsÛŽlods&Š Êqa„-@’ìf€\ è*¾ätN®ç®jQå$¡ asÛ,«,Щ°@€¬SU-² Ëóœï@Ií" d~ y¤[ʧ—« ôx¢0‚¿K·¥­X¦æ[ä:E€p–ó-Š ñŽ*š àç„Ö^M¹hð6Á¥l Ü-pìpç;Mf¾æÍ§ŸÌ¢]ý²Ò]¾xë´Ó…¬LGy×P¤-Ð$ ƒÍ’-ÐÌJÁ!v€S\ÑéÜX=¼â3…£.0!û XȾ²@pŒƒí q(JC& %²<’s$Sà«©ÖÇéåwß Ãi&ÇÊLrËìÖìp–?Ù|Ы °+Ôõ†å4ßô6B 4& Ô•ø¾#†3(`P€(pæ¼9]¦þ0CfHlX.7›†eÞ:î¤Þ¡¶4¤#ðQ*”ÛXï²ëV±nÐå@ä<ÆMovËȨ•Ê©Še×ôù>ë´žNYŒ„ÑØkÐ6hµppp‡!!9I ø–”¿â€4R¹¦>•%XȾ‚ «UͲP-2ô< 0½MHWŠ|K¶Tâ¦ïÚš&-5¬éÔ©ù];œÆMÝ“À‚‚@o§á=Af—gš`9­óòå$»L¯ÙpuKú]è¿ ý^·54ŽÒô±EwÝß8Zîg­lT vBÒô8EU. £±áØ¿€p?£§Ñœ&JÌ2…Øã×í4I¦fO”q¼®ö›ÞSæêŸÀ8ÚV)>ˆ-¦€óÖŒ¾sëñò5…¸íqqCphlÍߌþ@ £¼ü“qivŒ>å~{°‰Ô™XΙªÂúas(<è1’Ë%ÍÁ² }«jæ ê˜Ñ„ZU|žKá)‚6‚<ÒÝo–•œs²7ÝÔ*ïhûÅ…,ÂVšmgq]¯É+Oã©®) ¬Viûÿþ×Ãs8M³šFzvyw¼´*‹Íá]s8M¦Ì.F¦¹®i[‹ò@Nñs]|Ì'Å™,áݢ3íÀ9«EÙ2+æŒB,ÄöÎîTdùÇw_œº¡+àVóBûAeöƒ„jf-—œi½8«õÝ ½Î½÷ããð ^oGb€éqß}¨´¨^*5 5(P˜YÂ[Úc¶!fÀ @ µƒ¥ÃVë¶ ÉµÇÞï¿Ý—©òbˆw6£Û]Üb97ó–»vûJߘÎ ‘ë´ß fÖDЇ}Ê;üVsA¶’dz#fåJ:(Ôè\¯¹©žÌƱã«5çe é×CÈl%€Tucᛘ•«¯2¸'gT÷pZe­Ä÷è(æÒÂà2lN«ŒT®YéÜsG„v;«ÿ½ËýB’H”H„Øíj¸½”Èt.òÔr³E- %´Ñƒ}!õÖί8ö¾ñþû/–‚åD²N-ꜜÞVk‚¯jÁFŽ´sð‘ìŒÉÿ{\ ¤é›Ù“&5ZS‹Uí»‘Î?)0aúô(µDÎÝh\—᣶ -—žœô£¯ÇÎMŠsö?!m¨Ïù3 >–ùJßØÂ°å‹-^e<ÀÚXLÆ®>–ëÏÀ}ª% xA§¥Kirn´®6 –:½m@ßëpûÜCÎ’ºg¬Mö<ž“NCÆ…ô\%- ±ÓšA Ü}N«¼Á|/Àîƒit@MZßX,§UÞ§Ç>  J²ôæ[îdxDxÄ,U’ÚÅ>(zíÝÅ»åìƒV¿Ý´©qÇ«›Ñf±°¬VíÙÆEvË6bLûl²sÜ{/$ÿ€² Îmu Z‹ôujã“’ÿ‹S¬/‚ó“ Ù°[þG-Úgd5(PçH˜’|>f™×Qpoظ4}jâ+¾*‰›u_êÿ§mÅãÐÒKDÀ…k®»³Qn?ï:4Ø)—Ü T®¬¦Z©b·‹0!,”Lüõ®+Ö¬©ÙXŸk j©ßYú=ÎýãäH¯Øå1k}.ˆ(áüƒ$þAÿÁ?¶.Ì={ëÆÅhYNëñ_-ËiÞ‡Ó [‡Ô”ÌΪy‹?‹áÔò 6ÐÀ,î–Û~ò àÐg[æ‹K6¬6uÊ=\Ëâ›nöÝQ-Ù½(‡Ã#À¸Užx{º•¾/¤k,ÃÕ7êÔº­xÿO˜¥ÕéÖ„’úî€Q¾AšPÀmöõ=•‘øH=T°í3?ý:¡ŠO𤠞ÍßÁÍ/Y:jT£á^fxš*ä{¸wcV%å>òë!bðVdPÈ»­þþ»°\dx¨ PKúy³²·’iå 5’p~D äq1KVïl$jÒ]ðu¥âá4ü°ûà·}vËTЈ*œCc‡ËrZ;œRh€Ö‚{àè{ $,â™3öï—¯_É- ””î¸pÂ<àw±Å¿í:B‡¡óñwf<û22Àö¸T«›H¼uâHä‡.¾EŸ§%O¹É9«²…Ô#TyBbÊÇz×cgáŠÊDz¿?gŒê{4Úï ð­:vÝâ /ôÛÙ]ÄÅY²%qSÕñèç%>ÈhGgI&)µëŠÕ¿ûˆo2Auj‰ÛÎ>»5³•¬Ã¥=cŽ[ÌLç´ÏÈ0ïD?u¾@­¯¦Òá´ìo¿íÎTg,ia  ‹w]ýÃiœ—€'·u@fW¬“'³N£9Žðhƒ®½3á²Ví;ΠåW¯ùG›ÿn¸*Rà«~g°öÒ’kæÍœþ=²À¶Ú·&†s1gꤕ$©Rp•O‘¯4:¶OÞ3‡D˜¹õ =µŒ8¡Splï`ΜÓЦ9ǧù:<¶áöqóú¼Ÿ­´lË®ÌÙ³å-€Å;ým¬gŸÝ?ZØÕ»²™œ ÖæXZómhóÛ&ËœŽË—96Þ©ŒÝ—,9´cpÿ‘²,#MÞ¿&¿?i‹0\#§€[Ïòtƒ®Ïôî€K°}ç¶3„L@ƒÊ1íPe%–Ó„AÞAÚ9'ìA¨R†K_œ)á0&°K&‚qPÍ’ég–ÓH†–pA`çÎ=£Z·i÷rKˆ€CKᆫ&ˆ³¡ K¿øâ‘güüó'E(‚€­á*P@u²iÜEš‹+DUë±N@­¦å€±Î8Z~{š!rP­`$>¹ ÈØNTûÝ=Ž_ôÕ¢ÌÛã_í57 ‡NÄdЇHÂØÞóÒrà}ÌWú† #ÓŽo¾®WÖ¦^Îsùç2¾«ßBíNj·«èà¦!ÛUÃÔeùê¥ÙqýîÃgŽ»ˆ›²ô[³jÍ´idós ÀL°M\ë¾<­õ>œ†¦¹¸»XôúXL¸%Ñq><çÓýUÝrš×á4næ™ÁŠ´ñ$ìм¡siƒÏºâŠa2µ -t7ûí¦”ç}ø£‡è6|Ã*Óg kסßi÷³ŸÙ h§öêý±½'µM¼êªúŽ×Î:µ¤¶ [,ß§%‡ÂŽGl#Î?)@‡ã“¿ÇgæA´Ð'¨¥–o›0¥÷¼ÔVøhs÷äQppðVšÇ®^nÙC†´²íŽì¯¾˜€¶GS%4[zÔ—á+õ®9ˆ§vþ{5éê¥=sO´á©b2Ö¼µm@ÿ¾€$÷S  ýÔ¬Öu[µŠî¡@ëÅ‹‰ëFËYÔ-§©LêƒÅOÄŸàpÄY#‘–KµÑ$ÃÊ䆶ÅõÛŽ°õ`e6Ðá4š\qhDdä]¤å‡ÂŒI©©£CuDÇìæ­&¢ ]¶¶¦ÅùN@ZƒZ2¬ «rGüè& .#Ú €ßRÜË྾Oãùxz5Ÿ°±ôhK€GRóÑ;ñ|ï¹i9Æ&Ìm¨ÎepTMPeûøBö­ ÊâëC¿2 Fºvÿ\&c««^L·ˆÈ‡² óˆ;7 îPñŠ®›ê¡:£ÈÚQ`yí²W/78¬‡‘ã7÷åɼwРN¥BÈ™]0ƒ`9M{¹=Ú˜rYóëï×Ò®,ý³¸â̊뻿r è’8CYN#àªsiƒÎ»ìêÜln µ]›f<×€¤þ ª¾ÚŒ7qÐ's^_Š" ˜â5q× L#‹o ÔÔ’¥00TÒCë» #Ô €ÿS€Æ¯(†—廌ÊZüɨQÊåß~{ˮû¾Çv˜vxŒ³·bçÍÉY?fÜ·•å«møÎsÏRJ‹oƒt¶¾•ÓŽyvL晥€YÑ+Vdkõ­\]Ûjü•P€/^,ƒ#7²”)+À©í‚q¡2å«íÃú 6,ŽUB´“Œ Ÿ¤ªËUÛaÅŠÝ ë;=Âs8À.NÃÁ4\г[‰å4!,¼HÃú`ŽÉë:XöÀþ%P;ç:œÆÁÙej¦9HÊôìÐèV~×EÈÒ^P«‡Ñzh ì®– }¾DÏðp:´K‹,Z£ ‡ZÒÕWöZƒZM– à ¾Èk„5 Ðø…™¾ƒÚx>A£¿»â ûùó¯)RŠ )VfV¹º°Ï¼Ôa™cƯ¡[vÛm}>˜s“Õ¥ø tÅŠOÍ7± Þº~4”ÔÂ><´øhÎMP#v?J|£à´?Z©•ÈÇó‡f‰½½b qg3¸²úO̲Uë²ö -ëŸà Am)»4ëë/žC;Nú†¦…Q_ÝQ §íÃk&àïz0|7äuÓK»÷ïøüðaM•—Ùbif+© möΫÌþÇRVúÞG'&„ÅÌ,ýc™syÍw“.ÊB'ÞÃ?ýÎD¦ÄzvRóf,lʙԶ5Ë}oµk=¥¼ÐvÈHbÄ'ú–Mìx8¥à›ë¢ÿµµx+áh ÌΠ@£¦Æ2cÏUr™·Ž;ØwnúeN.–`jjðÙZ‘ßÇ}vvÆ­ ‡«TˆW"2]+˜<®èÈ¡±´í¥y!·VHbvhóÖóÛþøcqÅxãùäP ÛÊÕŸeÇõMÆûOÒZ Ä£Ø ^ÓmÕš'§EF­M•Ç;œfç têŠ>ª •c'>œÖã•ðà Y5ƒ îÉíÛˆ„6-wï´Ùw®–]az°¾èhЗ…Üq˯ ¨<€9×mdųޭ¯fzÊ5ŸÞƒ…%>ĘÝî «®'P‘qôˆ‡!á.µ†ó¢@zJb‚×c½µµ5®ÙÈhP  P`íØø-±sS¯Âôÿ >tà£G©S|=táÂÿTÅ꘸üòÀ¬œ×qULB>ßE’²Å;¸²%xúæ|ßè¶bM­”R7rûmº®\ó48¶gb1rÖH.ÞÊÜs·å«ëTÅo àÇ Ÿ8õ?Ô¼Ôä¤_ý¸™µjšûpÚbB—Çy§y“Àâ–ÓʧÑlC ð¤†Îa&©sZÌÌ5‡–!q,èÊK˜cù*tÕL”ؘã?0Û·¿0 ÄB'Þͤˆ0ÖìÝW™#c+yãmfêÔA㼚ºG3uïAVüæ;LÞ´ðObÍÞ~™¦¼ÌBîͤvm˜ã‡ß˜ep­ŸTFÑ´Wµ^ÇaæÊ€Ù~ZÌì_ÿ¨¥áA,øžÛXÀÐ8tV0ÇïKYÉœùŒ°„Û>˜ ˆØ¿úž•~z¬s×ÎÌöù7o5èúZ™5úaiäÓ¹´ä/›ìkT ‘©"j>j+–TÏÏñÖƒ-Šý•É“z6ë™ØuX|ÂS)—UÅv¬´<=yÊø:,ÚoŠZ?vüÒØ÷ÓnU„ø ¢ ë¬|ÛÑG.\x,óÕÐÝCtw8”»³í‡I¶Å±3ÿãü-)\¼c<òEAÿ £9)çœsnÏ·/ÅûìïfSÔEÛ ÔuÅŠþÕÚS«5ªà‰î7YP[Ù­âá´XE0:œÖ àÖ ­‘–i‰;“ñ°VúÖfÔ…ŒËì‹—0å`“371sÏn¬hæl& \½!ÞÃDa +œ<]Ä¡÷ÝÅò'º”*ImZ±pëãLÞ¸™•~÷ Söîg–³â–KYé‚Ï´gÌœ-c%³ç²€!ýYè¸Û˜ã·¿™(*fÁcF±Àag³ÒO¾fâÀ!¦:Úá/ûÚÖŸ£<*’…þ7Ùÿ^ÁÔýˑԆ:ÉÝäZ³–‹¬Æ:¨58µÕ [u’ú5¨MHœv#Ö×A@þ¬¢:”@´:>1¥f¿”‚øã©‰‰{«ÓÙÆ–¬³1v‡Ãjåj]µ7á©ä«TE|¥•'ñ™s¦&>R±ìø¤äœ¦,IRBÚÔ)éãkú,Ñ sd°ŽÖ´ _ùhrÀXHBœWÖœ”ľÒ5TØúÛ¾€1†Ю7´:»f£-÷uø=ÂWbØ0óö¢ü+@çñ»rè‚ ®œu}å2’°Ú5‹6TŒzjMVÿ]¸+.î'“¡Šöë»ïŸáÐÏ…Æ!¾Z“·æpa/êù8œF2žA½£¢ZœdÈø »Ùr榖^Ùjä-÷ s®^¯q\/¹€™c{1ç² ¦9Ê„½£n©`®­[ü…S_fj~³CN6|úȯ¶bê¡#ZÝĵuqd]M!°ª@&G×öå÷ŒiâàÛG2K¿>Ì xá¹ÚA2ÛÇ_¸2»ÿ·×±l5“÷ìg 8 XþñѪ×nV¯ðñÖäÁŠbs¦Nª•˜_‚Úñ‰Ó®T„: œ+é¾ô©SÞÒÉóè /„æȽ¬Ö;mzXS¹s!,Ç·«ãŽ<åãC m"³q?¯1ŠqLH|î4»³Ëmq:æ…/q¯Ý~PÓ8s̸IEèˆÅ£ûîÞîX·!g ’¡k»·´'±„¸$¥vqõ7† ¨:~'±¸˜•k~ÊØ÷1Û™Ô ,ÄïÎØwMÌʵ..þIl›QµAP NÁ–¨âÁ*QZª5«pÊ4&oÝ^¾‰©%'ÇÑ‚ÎW(äi«3YÁ£Ï0)2‚5[@kIW>a³3S‡¶Ú³ç ëáN™Ù¾ù‰•¾ó¡'¸~=šøAýVÑHKWlw©î½¸6]ðKP %æÌ„ÿKÞ€–:úâcÑ©ïrHþñçž Ï+p&_ˆ]鈷e Îâ¦VIéÖ„ÊG[ìà s‰Œ„Âêáw"¸ÀÑ:ünf¦>›¸â»!ƒuÂéé^Йöì”Wu¨—!qóX¨ê‡Ð¸užŽ:7âhãô´g´\bVø®Ç,ñjZrÒÛî`v¯õù¶²lÿùJ±m>„DöÉ{!fq?~‚øÇÞE^ít{{sb?]AûL}„ Öœ=ŠÿÇd6?–j´C/ûxwl½ÍT>48g|RòHûîñÒS\UiKiïKšÑÛ)ä™èÀ ô!4ùš3éW!t­üÊ4~ÆŒHQ(Ïm††‘` îo¥MMLÕiMeVtB’»`û>ÓÜ‚› ÕéW vÿÀ->5ñuA×7/*¦ƒcô‘ Ksèö¹Ø{A’e¶[Þ £y•;Ö¸5 t[¹öåì¸~}aqn,õ sÊËààn&ÀÛTúhôà@M) ìÞÇ/¿ˆI­Z@ ÁÎä·2nnðÍ×±â9ïCEW³ô99V®­R72±âðQíðWÐÈ+Ëå“×f2ËÙƒ™eÅZ¦ææij¹œÿd0çÒ•µ=‡É«Ö1gæffêØíqB¦ö@¹üÆCP€‹:Á£å¸G ÐìVñÐÌ™°!î`éÅe p@»Pð¤mƒ[Àb[|Eò¡•ÄÙõ*£ ¸&± s|NÚkR‘_øîˆsU…}<.1y>Âß¸ê ‚ýÑêåñIÓo¨X†*ä4Ôù„ë["í^”;eO»^O‹¸ŽTÚÓJÓîN‡…ÂúúÐó>v°5Úq>êÅ`-+•G=“Øž¨Rîì@¤ ÔY ÿª,¯½wJr'Wªü EýR*”ýüC֙͗£:´½Ï:­§C•Igëp”iCßìèÍ#ªPéXj9Gå²BHNŒGŸ›!òW\t¸fVBRòKåWxH}vÊÜléEÂëÜ$Vˆ>i;õ;{Û€¾óJ„¼×,;ŸsZO{þíÐι¹S»‡c›uî–±f²ÐzRž¦F®­Û%`>Z¦õK3æ–Owê«ÍmM­¯Fš<‹ðúêJeçnÖlîë,ì±û4yØÂ”W˜Ô¹##}·QŸ¿Ë‚Æ‚Pµ¦»ƒÙp,häU,ê# 7JÞ’åi~qê<œêÈeá)“Xä¬ç4¹YŠ,†%k; OžÄš5E¼ð“Z·ðä3<u‚Œë²ÛEGJú£<´‹|}ò䜕͋åDØÆŽÁà_ÜÞÜárKˆ·¦…9çp$‡äÛH¾ñ)ïr2Ï„úãÛKS…¾œŸoû` G)Ùhgñj§Öïiéwå| àu”àÂó§Þe÷“¸4"uê¤ïˆ³8.qê Ô÷Ò>ƒ²ÛX®<¤[ŸÜ…Ûù(#÷ÞXiLhûÕ.‰Å[_l)äÒdíY2 Iw˜辂åƒÐ“Jq˜N䤛ð~7Ú8¬H.™ñ•åªmNÇTE8úýG`ëˆá¯?ð€]“‘q²ctuò"ç*cਲ਼/Ú›Ïiµ^(kÜk§c€í ‰ÏÏNK~º\|;7½˜ªàm1¯ÞµðºþB ‘';FcqsŸ¬ˆcÁ ç¹ß÷éeùb@ß°}Í"- ÍÄÄ Ë|¶xñ ÇuýµÚ(¹¡(À¿ûξ#.î:™ËË0·tÂü!+ü›ì!C†Ä,[v°¡ÚaÔ£™%5ÈPÏÈ»ëAO öß—0º<ÛüG¯¸Õó¨ÂXBÁCO1ÆD±k3•|å{˜ñP2ކ¯‰;¿›ryõB §L×½Ú½ôÃEÌöÙÿQø,ÈåâTh\(xøiƃ‚°lBÙÚF¨Æ.||È×R½¢ #%Îöñ—Œ.Ãù/üŽS‹-ü^nrÑ6í ÀN¾@—³%‘”ó¬–Y`»½‚˜úmγ‰ŸÑ¶>‰3}¯%áltÎIOHpÒe’pCÜ'ôû´äÉßêà•‡Y¦c‡Ý €Ógâ3Ï×­¤¹Ó6¿³Ôµ¤œµ™Ï×Ú-øÙÚý„~ÑŒY˜å^j+:vÏkr™„|…üÕ¤íå®ìâ´äOµ&.G›gU(s¿… O:‹-ùgY§ýžŸAmD©S˜¿ºìÁýûf 蟚ç´íÃ"f:TЂ¶+¸‰Ýe oÖ~öðaçíŠ,¢¾€ž]6ç—Ã~# †;(Ð%#c?gæØ]Á×’æ˜9uÚ¿Þ=thð©ÐèczrÒãtùC[Œ6”§€(ÄÔ¨‚ÅáåÌz­WxU¼šÜm@ëOØl@ëN ˜ÄŽhË¥7ü–àˆú—àÑNh–ß²÷Ñ̉¯½h?”ßQ‹2ñ¿¼“HRÀEÁf,Új]`µŽr”Åó²} WànwÜö²4ßœiáhK€w8ù!³»Ã;,ýÉ'ó!»vv*J4âöyÇ×ÊÏE7ê>ˆQG¾^–èr]uOUî³’Ÿø²Á/AðâIÙÉg/\¸pà/ë·”•†BªC[lâüŸ½ë€¢xÛ³{w¹tzï„¢H K@Aå³+¨H» %è) *ü[€©*XPTDQ@)RÒhJIèEZ ¤]ÙÝïy÷²—K#—äBÊÍü~{3;;õÙ¹ÝgßyçVQÝ¢^ÜìÜ‘ %§ˆˆØXƒrìœú‘3cß ?ŽN Þö~²RõÇ©ø  1[²‡BŸù)Ù&÷,¢¢ |Y}!ê„ÛnOLv\ß°!©ó’ØGpþ=H­GŸ³ÇÌ‹à?¢}9Òò@D (>~÷áî݇Ȃü“:˜ÒÓbÉ^¦˜Lñ‚5ò–óNq8•„@•#µ:½’*Ñ"GAiBú°¹ Ê„§ÞÅá»LŸy¢Î(ä›Îóa:›*ƒäïK£~:‘ZŸd…̆ٗ`¾T(D·°”[üˆzâÂUqµD?fu¦z…Š/¡ÎÇ }páç‹hGÙœ®ÁLA9; /ÙÐßvœ²¦JWµÂJƒ­‘™óú«WÒ´2ì¾”å|®?m3BD SGp[¯ûˆ"mÎBúäi[¢Â–©ëfÍ`š›Ê1`-OqÉõ" ¥$L°)Ã3Øa•Àä×S,II5WZl#¦G_†¤µ–œÍú¢Œ•Z9’"4#¢¯¹¹¦§2ö4Ò6dsvûR»VU|%,ÌpXïæOÚ,Ò€‚í‚úJnÆ·PΚ)ܦ‚׋:ß7jü‡ǵǹ§é:Ô¾¦vZ2ÿà¾ã•žÇÕ<‚âç i‡¿Ã‹jïàêr°ÝÎäØš×[Þ#ŽG€#à:q1Q‘®§.>%„ŠUË™‚äkµ ~í†U°…´pJ‹ƒôr+…a ÙSlŒ¬!  á7òÝîaàøèh6»“˜í. ð»­S[ûNg‚p‚â Rq+ùäÈ|¶o?Ëÿ‹þ’ô2`áº|WÃzÒ…^mGìcžïNha\Á8WÎUócû‘ôuÑÆ ó”[ÐÖ¿sóß¡•C* ÐξÀ~¥0ÖwÎMã¸TÖ¾8 (G ~æã =‰­SÛ Y–W—|„÷à Z=ÙËË·,<æ*¡Õšt½wíç1>~ÔÎYˆ ^¼ _Ú5î×L‚î¾2ÔN¾sôN>‚jÂíŽsp;xf Ãíó9*‡@•“ÔB^?Î9›~7¤µáŠ$|ÉÞ?ˆÞ‹Ãä©#³æXVçøü¦gY3oFÚQÑm Ù ®a8¿ äŠQoˆBZ·;Ô!È9lç¸éÑ«°ú¤Py8·’7‡*QXÄ_Àh_C(}Ø ÝÍ'm'DVZ¶ou–`RzXSø›NÜ áÌqQ1!A­Ãô>ÏÇš^:h×eS ú]+ ›‘‹ªD{ùff;K ÚÊÔOع}Z¶J·Br Õ‰ü®f@@IDAT4Øbu×›hÛí(g Ú×}<•söòC q0™–¿\½Þ º¼ôS„4§°;ØW`¸´1D;Åz,Ö×ÖåÏéþ3è5Š©?®º÷éIeãÚÞ¨v''6œý(bÚ¸ÍÎÄßÊ£»ã"xÅŠG…ì´¿€S(ƒ ÈßtYÛg÷°ÈjåÁŠéО žÍ¤ ¸ÿ=1îô’ ¯H »‘tokh·+µ[°¬=ó©nUj£jp冰&Âf¬yÝŸŽ^ê;wd^7ô`ò¹ ,çg¼º` ¡ s%¶»Õ·o»¶Ômo •Ü‘œ¬æY’±E¯]VDi½a×vâ³í&J‘ç¼`«V>IÛäžb^oýܺ°]»›YwíË·Mð62ï‡îf– ›™D[ërWå«\‹Ð Z=ßL?ýVL»ÓŠÕƒDdñ"9dŽl :¤¯ï›ž¿$ú°; ’…É-å:¤{„êˆLÿ0tÝ?4½¬JKÝÝO´ãmØ¡ý þh¶ahS&µêó´ºÈR⟂„¦æ; L‹ôGƒØÔ vZó;o/T„OÐ_/Ø%y¬øAƒYÕ?›5UØ UÇAÜû¡ÎWA~ŸB Þ°ô°#I®ŸånÜ]TŽÒ` «Ðö¡ ±gÑ>’ZF_7yé½Bpoè#ÄáÈÒ¾¦B–ȸ/Móà Ë„)žxá¤']¡îpÏžSÂB§¦®^uucZ7šœë°H¸¾¦gº–íâ“îoŸüky­VìÞ¡C3ô>Þw¡"UšqQ[¶)?u[ñY‰ #µ2¸_½hŸ%ê ÷àÿrTí L})ÌöÌ5©Þ=«¢­°í ÜU,x ûŽÊ^y‘o»ÙQ—þº 8k:|½™÷=ƒ˜ß …'*]Iã}ÿ`æ;áqµ\¿ç"U‚ê¨$7`èÝnìÅÔƒyÑd!$~~ÌÿµÉÌ÷ÉQÌеSnj»GDÕïé±LÎÈbþÓŸÃõëƒÝ[¿—&0ãÀ¼~PjßÑbƒˆû˜Xpw²|%ò“ÊD ï%^ÆV`õúÊ 2N~E8š²?o;Ѥ¾þÆãW“àAÒ'LŒŠiimÙà™äªˆ¶@b4 „g6ˆæË´šßpì\Ó¢§+Žô˜LŠxнÕÜÈ|/}hz¦ÈEjÎm%k §lû[Ö:¡™Çr¾N›bCÑO:?oêÔ ³œSº/\lŸ6½Õ\h¸0ç…²KjasÞÝJÐ{Yë±6gó[©()wé¯î.É´éƒð¤d†%(§ëÔOk’vþ‰  ?+Wª÷iÜr¼,.”Iò_ ÓþT þˆ›™OÝ ½ù>ÜR™‡‚Ezá;t›ËýÌsœ‡zôèÌÛfŒÃ@{™B¼¯ »™H¯»êàå@=ë¼£ª9Î$8ñÁAãÖ…4þìä÷;Zr®tÛFœºæô‚TÂg;˜ªJKÓ§Øe%¾OaìÀfΉÃN^ XíOæ°´Ç&ØMhåíJšZqï²ì¯V1Ëïl†3ãýw²ô /çk‘]%-e-Z‘/Þû¾;™àïÇôA­˜íЖ½ìÇu¯[oÂîfØl¯Ë ·äJ‘}†?ÄtèSÆ+o©i Ý»ªýj×bYŸ.eÖm‰Ž2\ |Ñû–ê¸sáÛÑ÷"=ñz?VØ;Æ•6U¥4ª]{I§ä3[Z†âV}—köpI-Í%•GKJç–ë ÐTN.y>úqŒ6ÃU¸ôÜmn¾RtL.±Ë›3)ŒÌ‡!ÊaÚ«Àå 9- ¶¥‘ŽçbSâ½-O§`Ž«.L(€$mµ,°É<'œÃ6ÉŸÒïö›Ïøçàí;†i«Št{‹Hê¼(îQ…Éߣ­é'ä\¤C£+²^^vÕA ÝÎ{°uîC2~&5ŒÏ°,&-‡JÌÜÔWÕ¹O¼%®!@;x]yý]æýàÿåË k܈ْH{3ùgÎÆÉLפ!³‘]Ø\çZšP°[Ê”ŽŸ`º¦µì_¬åÏ„V͘ýzÌŠ-v-Ûâ™’cf9«Ö¨iüM“iµ€ñÖ›™eÝ_öS'µ}§ŽÌ¶×®F„Ø÷ÉÇYÆ«³Õ]É´¼Üw²•Íìÿ¶ò”ʧdʃÏ[¥HíÒ+¥{ègK¶1VÞµÚÜVc¶l¦€‡µoÞvgÒd"´×²C{FEüˆ…—´:ѾÇ;/Š-üÔÕp¿Æ!€q÷¾hòi*ʽ©?¬z§Æu”w¨:!àüÅ_þv¡MgÍ›8¥°`´«8 /) $½´ë—b± 6Õ2¼0цÝÁœ]ο2óOë˜íð1æUïGïw¾\(,6¬Ïô×µƒ~®¶ÎÙž„Ô(DYÚ¡ŒœïScXÎ÷k˜tê ¦ÕTy–=!ÿu>ìé(§+w嬟g縳ááþé—…}‚,+¡êb<§G4HäeÌõ/Õ넹­¶%b\B’[ÛPšÂöŽŒœ¼x~0íXʇ-Ÿß„÷"¼¥)‡§­¾´MHZ€¯vЯ¦5PIWž‡¾÷!Xؘ[}{Å[^°I¶3ŒÆ£åfpÊ¥t»~+‚é}ÁׇÉNRZŠ.1 íø…ÝÇÄ@U–'ªçLÊ?{oMÌ[k©de3Ÿ!w±ì…_RE:〛ìÒ\mK^¤òyè.¦ïÆÒ§ÌT¥¼Hlýz2A–˜áºöL `>÷ fò…4&A•ÁUgÖ`øHÉp5=OW6¸¤¶”¸˜~¹(èÄÎY‹K™•'¯@R{wïšÒ#ô£ô+i§@ ã ™¡urKÀj‘lÊÑ4hgÒD¡uJRiAïºðêÈÿb"Ú¿¼ëâO:WZ{xÅ×¶ñ‰SÀòý°QˆmþyÜkÞªSávô„îJF@XrÌiÞdl¨üΣ†>=IE}Éi—T+býºLŸk©À•4X#0Þ1€ XF:°Öd»JéòêZ4SJ–Èq6të „]êyq?Æ µNVHuÂkP–1ó=UMBðóeÒçXÆûqÌ¿‹YP‘ÙRªD¼¸r‹ŠÏðöQ$› úÜU$\R[Jt?ž9…þIöS)óòäîEàpx¸·œ~éALYŒ‡Y² –©,6g_1½07h{â΂׫Ê9- [û@ŽmÇSY ˜ôâzÅ‹<_UÚÉÛQqÎ:t¿G@U¦9¤ö½1tˆ[q¤W·ÛZoOÜZq5×ü’¡#¯JÀk~OËÕC"°‰Ur²2Ïe5j,ä ÌÛIu ,5˜ßÈôa]°@ì=Uò™ñî|F‰y ¸‘yßÎ.}¹’&ûó/™ÿ«“Xeó˜tò ËxÍ®¥ã3b“Ï_`™ÿ[À¼‡a†ëÛ«„Ô’´›e÷s±M& ,ƒ„d™œß±©áÚŸÎQ}úI{`4³ü±Éqî;r(L}íEq%Ç,£Q°fdR°3Ö%eå×K‰@¹§øÊÒR"Γ—”Þ¡í‰Ã®\c°À†VëpÂ>¨=ÅAáêsØþtyA]eååqÁV«£¨‚ôVð®{·ˆPàö–ò´ªZ?(ª‡úömÈÌ™[ñÚkK×1Ž/êõâMUjf¡¨†ó¸êŒÍØ’õzîÔÅѸsÏ>½û <ë®ä,øÔQD•ß‘é,Z´åpÜj\vÞR×Òx£§<¤‹Åg * ª#=[”í8wTXy½M[±Cz²ƒÉ‰C6üø-1ä+8ÈúA…›¯¬¼^—®fw½¹úAépç©+ %<\¨{Èý˜’]ˬÊ~è&M*@hÉÖW:Qèß.!)úˆÿ+ ¡­¤nå«6yXÄ^Xax‘öÂnRrÒæåKÄOj4í¶l9kôÒÝŽWòYê(¤¶u­6ù—“={¶¨Ñç«l虣Òž[Ióå}ÍZjÒÛr·/¡¥Ò0¸'B«F9“ÞbÓäZ5 ÔòXÒ³Õ.%¨Žp”mÖ ´{Ð ç*в*ÔEØÛüûËÛ"NjË‹ Ï_¡œèÝ»9teM)é—H\ð-¦eá)‹w¾Ýáƒü0¦i§ùù‰ÍÛ%$?ÒfgÒíZuô÷ŒˆüIÅ—mW”1Á‹æ¿à8ç@‹­ ‡t‚†5UiMV¶È–¬kÉ4]ï<ï`e  M‡Ù"q'™*°?œúkjýFÂñ:õ+£M5¦NÂp¼pöì„+ÂYüÆô³¼‰‹‰Š¤£¼åpR[^y~·#â*¤†… :úÙf>‚Í.^Å3 ©VH¬2û#6Àø?˜D ‚Tö&%Öü=#"ÞAÿ>×ú‹§ßÛ]/¬s¿æ#Ð:>>AE²GD3pÊõfsöØb××~Î]E`|ÔÌt¸šÞCÓi„V#µ–Í?¯þU6›/ýÜ]¶êøò›²Œ ÂðS¬– W}Kf_Ø–¥Hž§8©- ~ùÚ!pú¦n õ ƒô)З]‹é©ûhÁŒÖÙÓ¢ F{‹†6AñÉwÊÁψ£/Þšç¼ëF¢oÚê ǾèºäS¬làÎSÚ‘ø»(Ã1-A„Né“ͤ•¤Šc?ç¿® +B®¤õÐ4ô ¥1Fö±HJKóû欬+ [ÿZr> ­éÒ£f>gÑÑŠt„á÷ïÓÒΦ£.—pÖ¤µY½G–ÍI­GÞöªÕé”°°¡+»,3S>Ádå-¨ZµÑZˆº©å˜’Ò6 vK˜>šÑlÇŽãÚõšêÓâ0/oÃèßµ ”ë÷a+bkÕÔ>ó~F m|ÒJ,§yF»‚¼Á)W.}J³Z÷KD€lƒòÿÍÕa"ÒJDš R›…#;qóÆÄ”}»¾ÿ§Isá‡Ð.±*.8‰ð"ÜN>´pÓšÕd²‘p%| gþ‘*Âñ/þŠ@•—Y" ²µEŽÏÕ ŠbëT(ƒ \€ v13°yAÛ’ºî‰CÇœ ^òé}L¶mÁ’ _ýŽÙ9Êr“¢Üm„\éáá]Äæ §„…4ÄýE…BQF¦ôèF Éøîs.Œ °‡“HÖË…¤žœ„ž'$AÔHm&ÂdÁøÇw+Xp×{Îû(÷&Š-Ò¸¥A`S¤#ZR9 íñ”ý‹ùr)éÒž©Õ$µEæ÷ÔÈÈW¢ïÕK,é㘨£åÁ€Kj˃Ï[jŽ„…u‡®lžŸ´IÂGø`ÍOhö7̱<® ¨Ý<(!éO%´°{GŒM†îðhí/—Á+—ÄÅ8ÎyÀ#€ºÍ«X‰ÿM®S”—RÃB_ÒN¹á®6|Î4§öURyú%’:« #³Stdü±jåÚí¬]ðŸ—ñÊòÂÙÊž7)d¦Šì¯r¶ Â…ð9ëeLOÚ¼q6íW¹j¤VS?à’Z§3ÞôvClØö DØ:E—)È%µW‡Mˆœ†©¾Û1Ù× ßªM0çg·!zõ|5ê*þ}WE £ÑGu¢ní¼™SãKÓAZÜ’-Úf’0Á¦ØzPÞ|ÿh¬òq¹`æ¶ÝF[¶p猎­è´(6¨M£xŒÇ)ÍOÚ3j<=0¹ó‚î¹ïÉÔ¾«ÿ©¥Ð8˜Ú£Ûmw&.ñÊÔM½ þŠçά,kæ½(`Q™ ñŒLôX¦©q’(7ÐUø•¼uS¤ă}nnk×ñæÔFx*¾f³âŸ“%%N=ËÑV´óm¬ð›Ù|åÄþ}¿nYóã™™WHœMvÒI—V#µ\õ ˆ!¢H–û0”tÌ û¥ˆË¥Šâ¤¶0\BD„ÉGª'NÔ ì9H›@§Sñ3›ås¶àe³zœtÛ¢7ÈWŒ>ôÇm²-&bÚë§A|¯™¡ÙG&ÓhzéŽöîÖÉfS"°¸åqE"¶|TßB2¤²±µ–4ܰôÞ¸+¡##f@B"£nŠÙÀÏ‚—ÅíßûXDR1Yxt C@0™dåÎ;‡¥üwz-þK·ÐKãá3˜¼»@‹&kXwÝÖú˜ ´ðâä¤ö*Àj*$M¤è]§½ïèš-;;Ó ©-µß:t mݪÃu×ùøÔ5úúêuz#â=ÊÙl¶˹³—³33.;¸Ï¿Éñ€&å&B{ ‡¶Ñ‚&¥%,¹sBϱ! ÿÄš¦ísŠ.S“Ú<ØÔ?ðã“¢‰za" MÛœÿOétò :wJÀVŽUøyY<&¤>ØhŠ%¥ASCÒawïí3–ãÏ›öÚØ³^ÅKÖî”!C¼ŽI¹_’åñV«®Åk>>r ¸R¯æ·Þ‘´E‹çþÕ Z,{ él#ÝZÒ±Ådá*l¥Ûƒo¥{uìjÒUaÍ3ôÑïÅJ1B ®Å3\Xy¸{÷[Û$$ü]“úêξ@„8 +N¹³ÌZ–J^Ñ73MòªÅYrãU]Û»“öá cùÎäW˃èë4é áB‡¦¶A˜ÑÇ hˆÈ’„–ü,t¤´”ž;'"_‰(Kòm°l4Å)ºÌANjí\úSêÆLyåYÎðfÝ+—IžAÞþ .Úœ¶LÄ¡ä†2|ãóµ~Âöv“^ZûÃ÷:ÙúDjʱøÇ7,X(Èì‰q/ï…-¶nu}Óì‚yðyüÐÈË0ëu¯Äl ¶J-šV96¶2|ýúú÷§&w€í”w¸gÏ;dÙ²8m0|eAúéÈ ÝolýwÂ?A©»ÅUu\C‘4ÍѹFj5ÒF¶’½qB-qøÐ;Ô“Þ—.„={I K³–D`IR«Dr 7MJKù¸ËE Âë«XÏÍǬí¾&úösÜŒ§“ZúÒRÿøä(•Ð^ú„rçî¢AâájŒV¿ŽÜú‡ŽìðÁlÉ{ñ-[½ÓãðA1ßSMl˜ýžéÄùm·'ü^cmÊ^ (7_Û5bì~lÄ0 vkW£h¤&üܱô0xÚÍUñâª0mvì8s¼O÷A³´oI²ŒP×f•Ö’y<ÞcU¸é¼iU"^±ÕÂôB$rF¤M•ÔÂ'RKGAR›ï5€ë5Éi¤”|g)-‘Vô¹ÕŠ'ìO-/‚ÜŠtîq˜šo …7™LCi& ÜΓI­Fh <õüƒqÚ{’þ®ÉÈrçˆø^ »­ïØU¬›~™µ½p–Á`ü1è|~¢õ è嫿¨–;÷ °{丟;-Ž‹bŠ<‹JÄ4ôÄ΋$í9îS÷ÔÀK©Ðvº°&r§Ù6`àh¡0éwÄÝŒÉNW‡>TVW¬X¡:t¨FÜ*«U¹^Ì:“7"š4² ¡õTImAbKÑáLf) '´¡ 3è¼¾µÚÌÄD¹MÑSI­ƒÐ÷¬U+ ÖÜzP9 mAÐùyÉ Þ½“]€ýŸC{›ûkݨ#-Z|3tåJþÂ(º2§Ø72âN‹æ‡¢€¡TˆÂä¹]ÇîÙ52r[™ å«´nJÏn÷+’ü…:J;›"ý~¨oßðv[¶-[îŠ@à·ä?añX3E'L^ðúô5E$áQv"¦I5"kDlÕ4BëI’ "©á׈­&‘%Ÿâ´AîŠB`®i2 ½ì‚¯¢”!ÎSI-ý ©ïÆ[ûOuºÆ¤CËUÊ0‚…p~ìóyϿ厶Ÿ½e*[At/Õ 4Œ˜ýòË´Ê]u’žVø«DväóÓ›xyãŠÃGʾ8êó÷ßwàƒôkJz,pRÉ+6ÈQï½ÇýÍ7þÁ£¤Q¦-³€Þ  *”ÔÁó4G£È¼÷m÷?ÜKÐéÀl—}Dznî/ᨈbãÇ&Nꉢ9¦nÆ·¨â’}DбGq¤)Ô±ðrÒÞ**-«Ù`†¯ñ)9 Ã`^7õ®Ò‘pž$%jC3óR¥Û\S·{xÄ 0ßkýÁ&xŸ/ý¬“vÎ}ÏAÛO±Sßļ+·X,Ù«°™Çíø”‡q8ž€€§‘Z’\êJjuz}#Ú³º47ºögÿc>?âZƒžzus-m1©¼néÃü_ÄÄÚµ˜Pñ³ˆbÝÚ,ð]«½ln1-ºz4ð¡9Ó©çRa{õ’ùÕ«!@6€Dÿ‘HsÒA Á_‘­ßõ^º4ðjùøµš‰@P|Ò\ìÐó’Ö;èÙJ={êk%,Œfª¸ãp85O"µD°4I-=ؽu¢èg”lF¼ ÝC˜ïhRw,»óêÕYwíc™s²œŸ/{A.äÔ_מ~0K%Ð.$/2 á VKì[“ÔV¾E6Àƒ#· žn0ˆ÷ƒàÒn6Äl;\‘3Ðð{àã¢m|⻢(Di]LJÎ]©Ší leMÿMî8Ž@CÀÓ¬©¥>ÓaÀ˾\¤ÞÐ;Œyß5Y¶'0ïïbJV³ü¸V%Ÿ†N™ßÓc™èÏj/ü³ÄïbY}Êt-š!þ ¦k׊É'ÿc™ÆlÿB¸&Ьö§sØ•˜9ÌwìcLlÒˆYÖ®wHz©ŒŒYÿC³æ1’éÛRþ3,ç· Ì¼úWÄC,êmd>O g^}ÂTQeãV–µ`)Œ^Ì7râ{1*æ~aÙ_ÿ¨æqþÑ·iÉr¾…¹KP ïþÏùR©ÂXML$Š0.¾¥ª”'VH±·ó¢ù´Uñ—j„¢Ü¼dÁ„ßàymw&Åꊠ”éÔ{Œ‹SS.RL¦‘‚É”» Ìópá=æpj&žD:ˆhÑ¡[ÚòM‹ ‘†°®Ì8àF–ýÉ2&La¾ãG1ÁׇIÿc¶=ÿ2ù|ËxwˆäZuù>ûS2³Ø•io0éèqæ÷ÔÇÈ5`¦ÉL¹’Á²?[®’eéøIfÛH-C:yšÉi—™uÓ6–>éufÙºƒùÎ?µ Ÿ‘C™1¼/ËùáW–½`³¢~rÆ;0CÏn,ó½y,{éJæ3f˜JšÕ‹N?9k~·“Z}9¿uì¸ÎæNµð`E#°gÔø¯Q$³jv§ÈÑ—,¨rß³h—…•ýŽñ€ùÇRøn—à{Ö8à½åxžDjé~RµÃmSpÙ‹W2 ˆfö,6!ÔwéÄä ÕC1›Ur+;ÁHm ˜âÏùå&_Ngæß62]P+&6nàk$µ%‰¬åÏ¿™”r”)™L¾”®–¡deƒ$_d9ßÿÂäÓgTÒKõB;«ùýod–¿¶±œ¯V1óÆ-̺u§Oz½–m‰Ìvâ4³îþ—Q6öëå¨ÓÝÜ—¥†³»‹çå¹€@ƒæí_†ÂÆÜ¤°+/}ÑmÙ'­\ÈÊ“Ô@‚â“_Äxp(ÊCb;V>ª]å]âp<rŠäªr$5$§IkAºÜ£kh;˜ªLDU­ÀÇ[õ þºƒR‹ÌÿÅñŽKDZu­Z2ù¬ÝÖ·uG’ãZQCïnÌçáû˜®i&úÏž¦´/µ˜í½-޼Dz»\Ïô×w`Æ›opD‹-š8ÂpÆ»‚ªàŇÀ†þým—/x˜Y%lÌÀšaÞ¹žÅ&}¾~áúÎ).¯¹À*ÂÄÔ°Po"´ÔK||N8’Ý.>Ù± ¬æöž÷Œ#Àð<‰ÔÒý$¢åv²EÒXWœ’m7yeú,f;x8]rŠ…L»ã0‡è}ZKâ–þÒkL¬è°R@ù”3Ó5kœ?³Í=ZËùé7¨4|‘ÿZÅž9c]±5ñÒ‹D`ϰqÿu]ºà!I–7‚ÀxA×9ììqËÇH<¶È <²F#@2 K;.uõ*#ÆÃcjgöbJXˆ’Ü©5ºó¼sŽ€G `gRÑÕ|Ôˆm¾HwŸHÇO1Ò“ÔSUlÿd ¤¹>ܯªÐÂ.¯!.W«JcëÔb Thñ—÷»òåµ%ïa†¾½˜¡[¦kÝ‚nÀ‚18RC0†÷SíÝ’º]› ¿ùJ*ï‰{$àåmÏÏØ®áãþÆ·Ðs,eL§%±‘Žsð(hqXÛ¶íGáøÖq¬-˜’Ú#t¶vÎ}ŽG€#P]ðTR{Mî—õïxu1XíE2ÿIOÁ:B6¬¼ÏÄ–ÍÙ»­óíBæ9 ²c×8¶b¶°œ•«Afïfu¾ŒC>‘Ù¤8ú’91S.¦±€˜©¬ÖÜ·z³™°€ ¥fÑSYݳÀ·_abÃzŽ|ÊôѯÃ}ŽG€#PpR[¨ó:9•€@Ò££`Ê™¦Ô/,Hæúï˾S MáUV„ l ¶ÃÀ`í›u ] ´RºÍçĶŠÜ$Þ ŽGÀe8©u*ž#PýØ;2b-T¸_Õz¢ÊäÎKbÿO;ç¾ç! Û Ã!±]–×{%‹ÇÀZGäÂCŽ@G€?°ªø âÍã¸,ƒtVX£–‹ifEf‹ùÆ îF¹z•§.»û¾‘¢· j÷cSV¯Z¨ ÂUTª×íä­åx,œÔ–ñÖÂB˜ñ¶›óåÖwîÈ|ŸxŒyß{c†¢×à¹’Æ ;ƒùFŒ`^7õÎW¾vB Áèšï¨‡í&Á`¦ËÙyßy+v5»Þ9J {Á¬—¾}[u!™ñîÛ±]î£öÝÈrmäjÈÔ˜Ïð‡˜®y…oРUÉýkˆÙ+õgÞ#0å|œª…B]‹Í¶"xÅ ¯kØ ^UC€Ì}µ¹û¾1Ÿ9š¦(#SS~ bËdžàpª*œÔ–öÎ`îÖwÔPðÊ‹ùH­þº 8k:|½™÷=ƒ˜ß y»†iU¸’ÆûþÁÌwÂãj¿ç"Ô‚Îÿå‰Lßé:&c³ßgÇ1ãýÕ$‚Ÿóm2ó}r3tí”/U¿§Ç29#‹ùO×Aza"Ìï¥ Ì80?9÷ý(léÞÇÄ‚9ä+‘ŸTg¶uA§Ó ÅÂ1u·ØÂè¥ä¤½]ûÄÛ^~T;¶ñIOLŒÕJƒníC©)WïÓÇG‹ã>G€#À¨ŠpR[Ê»B’N]«,ûËïòåôx 3¯ßÄ2?ø”¥Ã —ñ¦°mm`©ÓAb³æ/bYqKà/f^wÊW\yí–»f·V1ë–Ìp}{5 ‘S [öZã“ å1ôëÅl‡2ùôvåõwXìåf/û†™ùƒúåI„ Ý»2]›–y[ð*‰GÔhcì¯7ÙÑEy&;9ÎyÀ# I~PBâx|¿;ìÖBš§Ùœ½æ\¿~ ï4G€#P-श”·‰6;¸òú»ØÎÖ’/§®q#&Ÿ8£ÆÉg`úS’™®IÃ2¤iÀä“§Ô|ÒñL״迼@RIý@w];–³ê5}Ϊ5,{é×LÁ¦ ñÖ›™eÝ_öhØÓÕœ¾SG&í?¤ž þ~ò>Î2ßcB1êZ>î× öŽŒ|$æ[­7Š,}¼ä“vÚ9÷=lû"6íx-å–ôœÌuØÖÍ‹ã!ŽG€#Pu(Ì~ªNÛªWKŒ^L±ª3¹j»),ùûPRÒÅ¡X¤¼2¼ êF ù ‚ÊnH0Ó‡@Å*‚ïÕgņõ™ä×ò×ßùŠ!5 D–6s çûÔ–óýHiAÎ]Üå,_ü¤Z"mèQ ö­éÈÛ×áëzWËÎðF» I&A_Ô …*B/‹%kCjïÞ´8îs8ª‚€'“ZÅ&ÙrÌ:}ñ[k•â.)—Ò™X+wfRN"š2mtàäJLC›#`£1Ð_Í%‚_ÐÆ ’ä:Å2ç.Tw³ìLrèà:_wÜÄ,ÛâómöàóÐ]Lß;Œ¥GÍb ts ØûõTUü¡g+0Ÿû3]»ÖÎE•6ë ¶ÜD£¹«.ļ¬gº!ø’É¡6cÕ{ȹc–«Kûy;+ øD¨!‘X@f·o¬°.ŠÕüWJXXËŠ­™—ÎàpJ‡€'“ZfÉ1§exûº…ÔÚâw3CŸŒ¤¢>°~ §]bÒñSL¬_Õ`õ®¸’Æ’° ¿@7€ïÀ¬É{Õ¼úAL×¢™*ÉÕ·n®Æ üêGõ\͂ԮûÓ‘ÄûÁÿc^ƒú³Œ™xWAM‚¶Î•°ÅnÔ,ñ»˜%a—*¶¥eDÄKã2¼}›Õz¾4yxÚÊG yÔ‰ÐZqì Ê'‚—,Yù-ã-¨ ´KH‚N’0‡ª»„ñE~é¯Ô=:V…öñ6p8BÀSI­Jds²2ÏeBŽSüåtæß72éÄiVû“÷˜÷Ð{Õc$aõp#óf¬Zº+i²?ÿ’‰-›³:ËæÙ¤}þ•š×gÄæýÀ`&èuÌïŧX/æ³Ú‹?b UÒ£-Α–A  ²LŽˆ°ïØÇTs]µ?Ãê¬üD=”ÌLfùc“ãPÌffݵ—Éç/Wt¡x‘ð´Y,' ]äU½#"ã0B–:*ËóB–ÇÙ¿È‘<à©Å'-× ì!ôßL@¡¥¬Ø6¥öé婘ð~s8U Ì(•ÏEDÍÜ@%ÄEÏ'¿ ;ÚFz‚8êáhÜ¹Ç ½ûÜþÑw%ï`Á§Ž"ªüŽLgÑt¾ÃA7UËVgvÕh×Òx£§<¤ ©*ƒŠ‚êHÿ–œvn?«Ôß½M[±Cz²ý‰ñÿùó*Z•Fb^êDaý‰JmiÑ•W£±\tÜÛuñb?‰eïaÍ7P\AøGÇ|zî92Ó ÅWJÍwÌÆì5¾ÜϼJéDª4¥g·Ûð,"ó/ªžÍ™îÁ6 k«P3yS8DÀ“$µÚ‹|Ò “÷ìü;E²˜/ïkÖR»Vî!ÐRiPPTœ­åLz‹M“GhÕ$X–À™­B„–ÚH8Ê6ëÚ=8Õ0Õ|JÂ]G@%¯¢á!Y•ĹɅ—;Ž€ ‰ë)AU3ÂÜOäÕ)a¡Ã8FŽG 2ð$RK8«d6×'é¡t,5e]jýFÂñ:õé:weD€ð#Ï9³E8ã\Æy¶ÊB`ïð1ûQ¯Õb;¬Ó’ØHíœû I;W?ÌCÙ§¸Å€ø¥ ¶½lŽG€#À¸Öx©%‰!D¸hŸìoÙ¶¬Y½N6ç\þ5¸»lÕåNéãw®#@¸~ŠÕráÏï¿%RKøÎæ®ÆSV ö ¿š3 ‘•ÿu]ÛÝqÎ@Ðöí >B_Œš™¡?»€ ÷Algy<8ŽG Rð$RKk„V#µ–¬¬Œìø--;ÈÖtéA$Œ»R"@¸~û’âß¿téz ±œ~%mã‘°°&&ŽG€#p-ð4RK’C’ j¤–¦Ê3pŸ½á‡oÖÿ½nÍçg ÆŒå7„³•=oRÈL•;ìØ¢üjïƒp!|Îzy]‰ßøû‡¿}ýÅ*tNÃQ#µš¤¶Ú÷ÛÓ;°wDÄ7ÂýOÃde6ׯÕÐ྆€°r¥”4FÄh-ŸµÝ%&ý}´gî4Ž <ÀàpÜ€§­Œ"É¡³ú‘Y#/„…¸{Û–½““ß0hp?[‡ŽýRCzà…®øšÍŠN–`”l8õ,G[ ÓÎk´±lfó•£{woܺöç_³³3hw†+¹áé¬~@xsWð©;YÈIëBÛ‡—Ma_öûþÓ°Í÷Ž¥{ÏGÀ@ÛøÄ©=º‘e>tVô/-m’°éHXÈ­ã“×;VÑÀø¨™ã ýx¬¨æA ´l~ôŒ¼”HÄÓç!Åñáã!o4ØCE‰‚iÜyîi¤–°#¢ESãD¾¨ÿÚ¡I­åœœ,iÃ_ÿ±±í‚»¶hÕáºö¾þuŒ¾¾zžH°g88,À²ZÍæËg®dg^¹xôÀþîI:ŒhRÛ éìå܃6ZÐH-W=5Éí:ÔÒùËØG KÀ?ˆ60iù²uüá5©Ÿ¼/îA íÎÄOSÃBŽËûã%ÖºkãCèÝ'pm‰{j)_)&Ózý)ióDèÿöXÅÇqùàä¹9.!ð´é­æF?1íI“ˆ/¸Ý•[êXMwa¢U¹´7.í0懃Vt×Îõrãè¥Q%¸ð‰ô–/”QÝ[RÛ ƒÈª¶ÈŽ$IéˆÔ^Êõ5R«-CtõqÕt,_S€ƒÇ=¢(òŽJE6f߈ñ çU0Àw«¼›’Ú«[ˆlS~‚,¡™£›Ñ.>9Úq^ ÈWbnU$åíNØh䫸è¨G*¡¼JŽ€Ç!0.jæ>AŒŠ¨{dÁÌ©;Ü €&tw¹U½< ‘õZÜDÄŒ¦Ñ/äNÃAñ$Ô¦×I2©éޫɇÖW­ï„áA¸ÄŠâ G“ˆ/áË] D`ïȈ/±è­k‚,|ÔeyìuÚ9÷9δݞ˜ìm0Þ€1³Ë¯°™©a¡ŸÃä©}]s1#æ YR~Ã׺žéÄÁœÐ^ó[À+ôdý(A–6G̈~ÎÝPx*©%écAÝZØÒÖçp_ä¡#©$ù5ýpî§3‰uƇ⠷‚Z—»Š€w]ö,ì’î£îAÒå+ÛØŠðõ ifƒ;Ž@!šoÛv¢¶Á[¶ÂoÚEèÛŽ‚ɯ?NßÔ­w-|ÒUd%Sn?63tè²àõék®E½¼ŽGÀŽIg½õ¡8[…ÿâœÈ¨™c݉ M­{ªÓˆ-õ_ “”Q“Þú Lú³¤‚@©,8« ÔdU”’ORWí€Ô Õ |ÒK¦ƒâ ;J§åE»šˆ@üÝ‘Y]ò°$ضCÑ ºœ=nƒ¾>YûËûT~êmÛ–®„‡N½’6ŸlØR‰7ý²2Ùö”°°{‚âãw—¿–«—0>:º™”Íb‘êÖ²ÁSäPznqÇà\c>4=“n2)œ²ÆøÊL™;~ƬçÏœ¶ÙÍðdRKøidÖ™¼iæ¾h:½ ¡u&µîÀ¿ª—¡áRØFt8“YJà mU¿£njß®‘Oì~í³øæ‰S‹TäñÁKü¾wø¸¯ÝT/¦†! lØ@¾O¤„uÛÇùm<,DHú[3Ŷ%µg÷amw$¬®È.ÏŠ: ]ÚMľM‘ý©-Üq8•„€É$ÈO›>–c»ü·¬H£$½-·ótRKÓ$ŒDÌèaGd$„&¡ÕmM–Т»ùaCáBá¤IdÉ×âÉçÎÀ~í,º ݪv]’„~±pgÒ££x¼»¥@ (>ñ½”¡ÿâÉòĵÈê¯HÒªCÝC§´KHz»E•:iìëÓ/u&ž#À¨Hb;Þôv¸Ì,ÍÝU'µyHjH+‘7Ä’¯…=‰ÐjÈ$¶Î$—ÂÜy0:ÿqrlײ¶d¶Ébµ|¶3ö¦ø‘|j׃ÇEI]Ú™ô36dèk“ÙjŒ6xà«ÌN íÔ¶mûHlä@3AÜq85ù¦IgÑE:Üâ8©- #5"µ*±;ÙÔCÔ‰ƒdEnÅD¡ X-™üò(@®`žð¶K=ªÓé™7sj¼GÀ;[,Û†O^÷ˆ )›Aj ¼õÎÙ'D#ÃËÅfâ8@ ÕŽä½§ÂÂze3錛 èÙ>ž’r°¶Ö}¨u|üiG€#À( œÔFKˆˆ0ùHõĉ‚N|NP”&H¢H:/Ùb Ì®p–š£ƒaR/«¢$I´É¶è'¢¢Oc[†÷šš}d2&5 î<½Ã#vtZ;ÓÉï øš}Û? ž°Öƒaá]w¦ññç•°°ÛR™mž¶€ #¨/¶Ö?Ò3ô¡Ö;’¶¸PÌU“L0Å„b#Èv±ÑÓ¸¾÷U‘â9ÕNjófðø¤¨A²Q·’ɦi~^Ê9#Kó76Q$ÝZOu*‘×Ë2«“ae 2Ìëd˜ß>i;ùܸi¯]0ëUN^9êyÎ0ûB€·Ü¼Ž.Ý—¬yqW„Or‹Úº Fæ½qÓ_çĶ8°<$>~XäyQCwUkV.‰æ!ÝçÝt­ã“× L†#‡Þ>ômG§þwzÓÉž=[”© AñÅ7ÙÖæŽ#À¨¢˜¦8¤ì¯¹ýòdRë ´<õüƒq­òo“AödTJ1à '‹pÃâåÙc§˜nGv.±-†5-éžã6ˆL˜™×/Á¼8öƼsâ\lÄpLXûFHÿ?×R‚ØöÈ–¬ñGÂBúkqÜçp8ðTZ¡ îY˯v¹YÞzå@£Ng ŽΛb‘˜EùtĈ—:,Yòm\¢Ë'"p½O™{³Ó@>”›I?¶å]–- ÙýØc´µ4wh³a-@ _;ð yú,˜:SHLø-µ{è+mâß •— ª¤OðÑv6»µ ÷?9×ôTFQÍ0™ÖëOÙ6m§kMõÍû–´èÛ‰Žf‚hˆ9ݾáIQ…VBœÉ´Âë”m ½Þ˜ÙM:KFõ+¡¼Ê\"¦G5 mEA?vþÌ©‰îæ©Y³êY-¢_Söò‰ªzŸ=•Ôy¥¾Cní?’¥Æ)õý—Жmøn) üt]N\jjhêÿ4J!êd;D`åСRÈŠEì9ÙÉø´©æÑB–2Ї<Þår Ÿ4÷PÐd|} u–Æê"2Æb°yÃÍ06œ¬'”£x·fEÛ„ÈÑãá=—ó_zH¸ŽYÓ”qQÑÇ-kªëðšÉ4Ôa÷T“‚rŒu£Fœb–« T"Þ|³–|ÅúvbcOÏšõ݇Ӧskãs #¢ÍØ9ѹÅÕƒµAèë» ´wãÞˆV«v£³ÿ‹ŽyM›ýò~-¯É´Ð›±Ç-U•iít—bم¼b4®ûÈô²ºÖ@+?"6Ö ;kÁŠÚÓq1QMµx7ù×ã~tÂ.]~î(/Âë«ØÎ¾‚ÿà(K¦Ô˜^í¸Ï¸Ïsšêo|ÝdªZ»ó]õä@ª`ÔgZFJ³>ÞþþãÓü ס-ß"üGÁ ÃÖ©Ž]ØÊW(Ï]mH:ê$‚£µà…ÿ ¬#<¡sŸ#à*ív&mÖ1}w€?yv{–"%¥„…¹¢ÚBÒPU"êÈïæ‘Áˆ¨˜µ s!@n‡cˆì—ðÿ†<¹±"+Ó@þ¶G˜Þ©_–ªã¦L¹ŒþO‡EISEZj×IÛ¦ïOYüRR'¿õV-Ò)÷ÂÏDÛ~Û…°òÞãåc»¤•1nÆ=OZOdŸ¶Æ¸r¯´l5ÄWÌ6ËœêÚõ#Çzn„dw\Ę^…’M¸Ï~ë30ÓðJUë›'’Z¼kU)­÷m÷?ÜK' `¶‹â¸+'„#L¡5~lâ$2ŸÃ1-'žÕ=ûÞ‘ãWãH{zÛÂÞY×A;å>GÀUh#†¶Aí`<½K®ÚÒLaÒú”°—IJZ\YqÑ3&ÓQÜuwÄãå‰f ¹»¬ˆâ]¨/Ç£8úD}âSAB›¹Hà˜Y¢*$z³â¢§¿æŽöUÆs¦9µEXÔµ‚q—®H·¢¿ ïì|­ÚÆ&hÛ] b¢Bôþ:²4qËÿ¦OÿOË#(¶!ZØ“|`cÑ…Ñ:4ò•—p­jøR¯¡ï2}Ã6Ï÷ÇEGÝ„ýÿ&ªmU÷ *a?ÖŠ9¤ûeÅ‚¦à=ÉуO“Òz×mØL¦ZO¡¢ú ©hÅËÇ{ü8H·ªÄ‡5ÒpWCô®3érvZ8†A0ˆ‡Ÿ úµØF·ßF·†Þð ì¶Î%•¦i‡{vÿS’å%г­CÌ›©=ºÝ|¢W¯‘Í·o¿PM(²hÒŸ…ºÁ«ê£Nb¼>}sÂgNÙ;þ•˜ ’¤ü‚Í|Æ?5=úÝc¢Ž:§1§Ûj‹šù9^D7C@v{×õÏ}hz&Òåêß&Pú·½œõoÇGͺKbò‹‡(‚’‰$ëôúIóMSPzÍ=mú ÐlM¾å- žmQ×~˜OÛÁôºY‚$7Ë´f¾>à!.ôÂÔò®Ü|± 2y¦¹‘“H­ð ›ß™4‰êTݼ©SIo~ ÊÁ)Û‰( LÆ3¶åªi›ê£B5UÄ BšÉ¸Þ’m4KØ%2%f~ôŒ T9§þÿÂZ4œ.œ87 š¡/f´ï:ªë´tòqYacýu(¯®ƒpq™±aÀìŸyÆl/‰± Qo]oc–hÔwªk€t§qÍaò ªC4Õ W±ÕÊ.àCÆ#Fã5ˆ¶ÊcœtqnG´ŽSWð Ä&“"ž´ÆD¡ý ¤Â 3°i’]"°u¢,ó©°(Wlo"2yš£ð?‰5‹ÈQ´ËÝ´äkç«‚·ð’¥|ŒL W¬X¡ •3çëe ãÆåwž&©¥þ©õÂá-êõ-­:QávhË?¨ÂѪÓ‘mƒpö´ñ….sçŒÀÖ¡C³Eƒ8 qêK¯¶°œÊo‹Ð¹ö,ÚìHøÅ襄Bz´Ië9>˜çHæÄÔžÝ@ ¯­³\Ìik2¦õ±EÕ>ÿõékA bül‚Ø3A²Z-l<’xÝÚèÏhÐõD\(­ª«°.¸ÖÅYÿ‹Çž–i5HÌ-˜‹G™?$ÛlɦG;L =ùÆurlé»P?IØZ¡©ÿ‚tD=øoÖ9¯(ÒíÈHuÁ·áÚ%:¦{…œâ#þ üéY[äôè"1?Åþkˆ2nF]ðˆÖ² *“­ÀˆÑÏàêZÄ…£®“ˆÇ¡ @Á¿£ocµtyý*ÇÏ-’eå%ø¦À‹H4œv²g¯ ’`”•€Nlik ;ǯ[Î¥ƒXÚݳ11¬Š•®Ý˜u8¢pd®È§Gy‹z/UÊì*¶ö’ ÿ¢L±N€þ#´å„ÛãǦð¯ê\Ń 9e™¬^ní1Ôž’•ý Z¬„-»bÃG‘2éjãú8 h`s¡þnÁô%ƒÐ¶¡4¨{¿;mIõ•溧‘ÜFÒi•Ôâk¶‘Ùÿk¦4à]‹´P`aõj³ÑA-Ù€ÆeRÇ*u3}uÄG”}Wg@IDATËæÌl#,2új$œ oî<=ÃÆí‚2–cúÒ—º.«‡Ã»_ZlM>Ù6 NL¾…‡ øœÂZ(’ u„ÐYØzW6*G®g•eZÕÕ$«ÅdN±Ç+ZúÜdXPÆØ^æ#¶Ä~4<ý3  ÝOÛÞx ˜²éç¢ãvÂ&êzcZx I-ñFûþcVAq9)Ó6bÕVÈmaþ†– ¢gôhfèWºnq¦È,ä3˹u%àúÍtÄFÏø´¨úã¦O? ø=@÷Ý0nzôªS Få¹8Ó”cTÚx˜bA6žÖÊ%)íÓìÆhSŒzMP. í€î´N]x†rß¡Õöy%RH Åq;¶°¿«™þF?¨cô¥Xua›A ´lÐ uü°¸ýy„®É*q§cY9ʃÈß„x-ÒB³½0Ü~•ÿwÞ7=©4Øææ-Ò³fðFŸR/ ÊÔ'Mo´-2!"Kƒ¤¹AÀu,•u—{€[/£¾V3Ö÷0nš¬CȰ¾Œø¶W«°¸«Òß«7xµ†ZŒ÷ñ™È¨Ùí æ¹Ú9þsô1€7¼ðÉÕÒUÆ5O"µD°¨¿ô¡ž7Äó~2þå»ìVÞÔ“E¶oíRzâË}Ôu)mq‰nkÒ€ÍîÌêzy±@5»âÜÿ5k̾¹¥ûõ¶¾lÅÍ=Ùí ý7J¬œð«õGB™ð.¾%VÀTKvˆøCÁ>-‹§¨M÷Z´¨À «Zv7º’6l°a Ý)X¾r^®ªÚ^ôØ F™šªH[Rz‡ª/êñQ3ÐQaÍT„&T6tWµH€iñ³”í£þ|Äç "Š97&ê8ú±ˆÂ2“I•«hgÍù?"¯DTÌœJê^v§–ªEP …&¨–G@l^¥EgGú’ 7¹D›bJçš:<‚·ç» R˜…Á‚1«²dë‹Ó›-])I²™ïDûýA¬N‚<;§aºR`, „ÄÚ’).Xpþ4îõé?Ùõ=ó¦ÍãLÓþ‹Œ$Õ Õéôµ~UØòžÌŽQBㆪ/šz ð3þCW ½m¢.Œ¢¥À–’ç¬Ìê¶®NKÑoÉ*}T\ÚÒà|s’yÑÔ]èƒ „Õ< ©àà‘û(Õ‹q6W³V0×4ù ¢Ö©å(–^ŵ«`<>`^Xw¢´]Mõ}ñ\¯ZN_µšSá­!’E}¦[0Vìž½ëÕe‘Z³­ç.–¹c}ë×e /³wÿ9Tæ2\ÍØµv [œrœý}!ÝѤ!¶ÿvú»h±¸Z„šŽžÆ„7wpAé¼|ÁhÅ&a…4ËEiš!d“è>G <íLúùHXXIᙎ—=F6k€fjbjXèso*°ÜòiÊÕíúŒ§¨PQ¨ ï@,iÜÓAMïœÒèÃRÏñàÜI%åVÎñùÂ$¾¨”ú 2éÐ UHyÀîÚ§Ù‘¥X_q·ýRùsÍ~½4ÑôÖ{VÉú<°‡93öˆ`³†AÝ¡w®~m±)‚Ð Ÿ:¹©p"a/zv3Þ  ^ÓéÅ¥ãH¯ó÷äCQÚc¸ŽÚÔf»è›—.S}áçO‰"åî—Þ~{ég\2ã„ÖlFøß%lóÊ¿zÈÇG|);K¹›È`dÔ¬æGD|/Sið@?[©™¡Óì\µºÙG0(»ù°s-(,)ß`¬ »Ý¡=Þö1ÄÔ±¢ÅçÃTÙ(džM"Ì ¿ÛWqJ?ÞÝK’tJ¾´Rä×’êµ€øD´è ÁMý©UÏ,›»±A=vË&l3Hë°ÖÍX–Mfß?ÅV?͈ N nÇjyéÙ×7÷bŸ¿ÈÞÙwˆµö÷e“;µgíýØÉÌ•¬î¹”®ÚÊX)ð´¤}쩎mX3oöãÉÿXß\I/••ü}å³g®kËÚú³™ÙjšoÙŸ‘ÞP˜ˆ¼75ª§>+Ö>Ë>Ú˜u¢š'¼a}f–eö5Ò/?|¢P§ßØ{À·êÄi6®}+Ö©VÛt®Ð‡Ÿ#]‘;®„³†¹ãOTdzé@ á¿.Kã—$ùgµÃ »·óâùã÷Œ?ß#à¬0È:ž·§öèr%ÏBEFÉ–Ôs5ŠÙ(Œ~xiqš/±ú…ð[—|`>ÐzB•R > Â<„/b,,¡|—ÓTHëoüó¤uóçhããé—s΀¤‘˜Ò5]Áã“Zð]ÂÖ)ýUƒd d* ì#…Éï?õÚÜ_Ñ>‡T™2— űl®Nq¾ê³œÏô§mF+ÙL&'°àï™ÚumœÈº¼m©µk}èN?Œö)òœÒtás¡^R0MyÎe+›…ˆTÄmå)çj˜ò”[UóÑÒûM.GK}@{ׯà zö1È#©< ÂùÈäéì–œ–ήùŒÞs€¥[íãwrp{–a±±vîa÷·hÂ^¼¾½U]ÔÊûÙÛ݃Ù.ÜNaÇ2³Ø(3S’Øg‡Ž±ã8÷ÓëÙ†3çÙûÿ¤²~ 났¶Që»bµ±а.;|œÎÊa9 °äîZA_ðè=ûY]£›ÜmjXaŰ m *ŠjÏ:òÌ~ùå+ o@úꙥœ \Ž/Ä-§dѶzAš”HôµC‰…*"T&@)±ûUÁ´PhK7º$…lÁ´´Ζa{‚â1!?B›Ž'+2³¨Ék¥×¡âT•‹ˆ¨Yß·ˆ “ƒ!¡ýÔè#.s¶ÿë*¶já.þÀâü“¶èÑÀ&L’Ò^Å}˶OjæP<á}ôÀõ-X=>r`êN½¦^¢ípÿO#® Èþ:è!Y0OIçã^‰~¶–—âÅ~iÌ5MM))O©¯ Š[ø(OqÚG‰áRÉW¹û¿àà¶þ¿óì“CG™ºÕ©ÍΙ-ìlŽX&§]f‡3²X ¤¶!ùüáÄ–f¶²Ÿ!‰m‰m_l¶’ëö^¾Âf$ýÃ~Ç´ÿÁôL,X”Ô´TF&ÂgsÌl%$­'³²!!¾ÀôÐ`íQ—32v;ízÞÅ©ÇÙogα¿ÎÚ%¬}Öc›>–‘Ã’.¤³#hKxcÇìDnÍy^#o#ÈyûòèIvý(£sÆ»ŒEðl5Võ[LÆËDÅsÙ/˜åÁ+ì:o5±¿¼O×¶Û“uµÃ1ÏF²Ål”$iMJ÷Ðχ‡Ú˜njMýª'(Ó±jþç¢Ç›bH8—âðPü˜tf¯SXRäÑZ¦Åý@In¦s lÑâ ùŠa=Iûðêáð‚×i'(-åm¢°¤Ø^©ÒžÍl¼éí†}SAP‰ H&YEp¤ÑÊpöÉâÁÓ¦·š;Ç©å䪵k(ÈN€Dá:-Ž|ƒ^¿ˆH a·G˜f9®=õVG\² E ãJ{U—)B­%Áƒ¿è³MKk¬kaMRKSñ@aâw/ˆŽ‚71nfÔû΄VÍS lµ:JòUf‚þIj'ð}÷$ïÅÌ¥Á“²S}(ãzHÈ[iu“î8Èk¡ ¼_) >F“ :-=ùÎãÄ9^ G¾}¯ ±/@ÀOÀbÔ-åÑÃÖʬHß-̸"èæ²éJ9Í·Ÿ•ã÷ßô 5wZ®î©¾h!pÈ.mÉ1½KÞG,IXƒüüØÙöY¡­g¯®ÛÒÙ‘mZ°–¾¾ìˆ-9"Ò^8jy IV‰ô†Ö TÉô­óT¾Zúù¨y þø¡í³Ã‚ÙnH‹ã)x¹4çÎX—&O[ÃX3x°9dyÜ£˜¼Ø‰Ç²7¡BNÚ›èö 5¼ë¼{×66ä ª‰ØN÷çþ;£ÕêEUc¬–Ò…ÛSºw JHøÑ]͹µKûO×íÚO ·î‘™ð3$cÐ …)'è>J69£~a»ÎO7³`DÞ u8D˜ÚJ¿d&½àæxC04¬µ¸`zí<6zòA˜€z“øS$…­‡.åfŒ¾Â½½ŒóvŠõÜX W»(ýXÜß ˜O£{iÀxô‚©¸W°éó0F挬¬sO’Ô:cì6BK…æ@=À—•›î¹»ÙluÎ:«¤óZœ£F“zá+™ì® ³©‰ûI-ÈGíhQ€¬ÚðϦOùo¡çë\ç{ ?+ˆφúI‘_Ûõ/}–Ñ]ý+¿Œ…òl5äa{ñð}QëÆÚs—ÆÒιÏp´ˆìÓ›îØq¸Aã³yåÁ¾)“VCj»äxŸ>uóâË"[x±ß ƒ7O€$í´³ÈÔ” ב 3`B«_1‹§~„%ž[A:`ó€gñ_¸Dw›R±’ŒõCÊ8Õ^§pì~xÔ¿ òH&¤¼!©vXDˆ›9e7t7¢ÜDjÚöÒ† ­IouÃ{çEö"ÕMäižCdýBþ_èüB'”ý‚¾5D9 îÁñY ö~™ÅÒrxë|Wá¿þ úåÅùi¤{ÐÇ`ÖÑul°°{< ¼RAÊFS»@¶¬hûóØÀñ|ÐÊ*Ê'‹öAkЦ>èÿë DQÄ·rMy_xmNØ­nŠŸ®8ïE$‹¨Ve^ÈZB×È¹Š­=µë¿Øum0vyyKƒ‡ÞO÷ú¼¸Â4pÅoŠ( …â_!‚I„LÜoF:¾ÀèI¨LAø.ø /’x(‚¾.î­úAví¶’}äo„£Ê¸r“;°ö Ôü¡ÃɯŽþD$ž ÄAsï‡?;ùÖu¯ßÝÂõÙ(2éµSû±°|¾rëÿÛ&èŸ*˜ lýÀA÷«z®÷4o¬.ÐzlS<Ë–±”i¾B~’‚~°?U%$Eýû|š*ÁýsÐìuäý:¹š›Ó£ »l±2H&-øZÝÿ¶,õ[ Õ€±Ð¡%³[Zž7»wbíüýØ[{² ¦M}l$¿3ºtdÝ #ÝÞä‹éŒ¤´D‚5I/ÕE¶AÞíÄÚ×òg³vïA¶ñ˧Â4W]—ã—˜ïåŒ ßŽ¾yÒqPf×X¿«•TPºj4–+k_lðâù«ñ€¤+އ쌗W×Ä¡cι³%a I®Û7j|¹ŸyZYܯà=›Z:uÍ7[ Éš‡ñÖXk9{Ð…eÚ%$§Å¹Ã‡äKÞ¶©E}}óÓλ•T6m­Óåhf·´ôöìgU]0ÁÐÐlËj×4Ÿ ì bCÑO:_ yV“>ÿÞ{>YmÍ}õú sL/958>:ºÃú¢ù¦Iy/#­¢>µÆNõó¦O?•«×\ …ýT³Â`lXëDQd¶é•¼² …TŠ,­èÈçMïÕÍô¶Hñ£Ô ê$Ðæ( Í»WnÑð/"Ä4ý.Èçû€Ô®%©mS}G¿\ËŽJ\ÅÖ‘Á Wñ û™•Uw~Té»–èhCó¶èV‚ÞËZµ9[°¯%PA Üõþõ4õçÛQá/7"”wØ’íW²~ðRü^6ú²S°XŒÈ1¹ã ŒÛ6aÖgÑ$«µmÎÇf d¡àèàjîýR˜©ëuŒˆ0¹_NUIí‡ ÐS:·gsÂ:«ä9ÃfcÓÿÉGj{{3Rm §å§ðzøÉÿR;Ž€Ûðòöcɶ`§#Ö˜KŽå3Tr·Û+âz,¸L¦ÎcΗA2û§Ùœõ>‚#(.—à~{¨{ÈW¾‚~bÓøøó_^—;•{¸´åÐ*ù¢òèN_la£ »t[§:fêKA—K䦽 ^×ÎizáÂSuZø®$Ê¢µyœJ(Ì%O)…¯Øcœ¥»Å¥))¾8’Nù"^y³nx>d¾…Dô­,ú@ÀGÈÆSÒfÚíLxõÕ!V“I»j÷]Å6®ò¹ŠGîýt‰ÐR‹r·'.õØ,_o\È­û]HUb’r;w±ë[Zþ%µM ©}¿´’Ú²4ƒ6M " ípGv²b@.ñ¥u¤&@%‘u‚¢íFº»T§³#Ý[Ò™%Éo^KœS”?Ì%µåÇÐÓJ µ eLeÚÕV 홸wdd!=¾²âÂ%µeE®ææÃ®c¤_ ZÛ,¯—Â9¼ÀßÁB²`íœûw#Ÿô“ è‚Anhf ×) &ø „÷§caaAZleúŠ,ß ÝÇí„–% z6·2ÛSë&½REG@§7 ϘùŠlÝeS,ÿJŠ´RlM,¾³[ªT¿:÷ÓÓÛÎI­§ÞŽ@U@ÀX{*=$Ù›¢xÛ¬Êò;þ9Ÿé™ªÐLÞ†šƒ@P|üå „ä±:¦»UÇ´žAb7ØÊl{RÂB^;žÏì’–æZùŠÞ'ZЉ¼ ºŽ v5E¹¦«v­XÍêY0sú—BË-ƒ& ºÿÓ ¬¿^𺮩¡_K˜÷š™;5_Ízś대'ëÔ:ãÀÃŽ@%"°wèPK—å±Ê6–)ŠVAw=~þx šÄ§+ñ¾Ô„ªÉv'õÃYÒ¹_mÖžëׯszN曈ŽÇô4lþ«6D_‘¯¤€ù¯gÜiþ˹î’Âq¦—HÇw}Iéøu× ÅaHàzž²:!À%µÕénñ¶rj0»‡Eþ ‰™ÃŒ¦‚_^¼@%$5¸Û¼kŒ€¬Qt\­š›7_JÂS ÓõÂÜ®¥¹mc7ÿòÑ^½ÚhñÜçpÜ‹ÀxSt¯q3Þ°¯ /GќԖ¼v~¬#¶¾­LG«û–ö SÍp¹»Í}}Të´Øl@cØð.ƒ3ôÍZM{áþޝÏig2ù•¡žÅCØ3"rVxÿ¬v_]8&/ê²l™Ãxº‡Â»]fbq¸àÚ%$Ä·Ý™x2DÀÆ©}KFäƒôöîÿgï:ࣨ¶÷™ÝìfSHèéHoŠì½àß§bAPÑg’àjÀò, @Š ê볋"¨X(‘Ò{èR·ÌÌýg’ ›„”ݰIîýe2íÖogîýæÜsÏÑ4÷ÆíýúLäW]%ÔbÊ¥ˆ"¨º—M’tmrEÒ”W¨”„J)× o`Ý;³ñ«7”£ú.ŸîÀØOôÝ­ÙÚmj7›ÝتÜñºØæiå-Æ«HÍl ›¼)ÁLXˆ-„ÃéN¤…wi”ô72EÞhu([Vßt_¡îòæ-âÕ~d‹r?÷êë ‚Ðú-55k&Z}{ío¹ha0 I-uª³`þëS·;o2œ<€ * úóÛ Oec‡`¨¯¨ƒ@ V q¿ðQ¿dR+-G#.hÍvç䲃pi;ªC– ·Á ÂÀ†ÑìÚM {®äáHž‡ß¨>;çbÏÁÆ+¹Ï{A?¶3;‡u‹Š`iÇO0òèe¤ƒÛØ®%Éèûõg7-YÆî"7¸pÐ#*Ò°cKÇFÀÉ‚›¼n ÛZ@2ïiߊ iÒ¶d²w¶ï6Zp‡ÖlPƒúìÛÍÞügkjc×4oÊcOõ ›µ1Ï1œ4l„[²mk©¯qÛÂn[~ì”-îv§³;Q—‰k6•R¢@Ú†¿v¸Ûúj×$\Õ™'SeÝæ¥ìC“@v±1]Æ71»²qÈNU¢”lÅåÚ‹Àú>Ôí½Ô`2ä­Ñ}^ê×ëGÆzû©½­- Zýù'õC±ÛÏë=[ÒØt|dõ7ê[§PKøÉ~RdËÓç¬\I^ºD‚rMÉA=ƒ¢ íà­k;ˆ)ì½ÚAD)X%™…Ã-…H‹®¸9»õ×åìLy]Ü„œ—ÑE8Ø’CGÙÿý–Æ5¬ÏÚ„9Œt‘Öüï ØÇcÑ!ùÇvxkIìÈ?ÒØaÓ[Ú4g£þZÅ~‡3‡+š76ò£›2³Ø˜ekØ ­š²&vëZ/‚õŠªÇb–­fë32Ù5-›ÀË™lìçïØkx»ºyv¤|ô_« ÂÛžÆz‚CGW "Dx»‚윋A$_q¹XüÒN™ŠÅk›A”ó /IwiãÀÍGß’á—²<Ë¥5­V^²x±åȞͿãSÅøhªÉºÖ [÷ÝÕW»N.Ÿ`¯Gæ2w¤…ñü¦2”¸ ]«WÏÓ¬¶¼ßÛ„Òè…@ØxϘ*÷yµdѨ*!°³_ï¡ZÉ-ëR4#ÌI,¹]»ŽIì—~°hþgíLŠKê‡~ø |h¶à ^¬¢S–g­jÕW0ú¤,Ì ¦£ÄÝŠ¬ü0#q|Zõ•.J* *wð5ˆÔÒ@Š^¼úØšbk|ï3gfG…GnlY.,ÈN-‘[šî¯Í¬1 ‚µ‡ŸaY¡¼¡kz&ÈÌ;>gòs# -¨ÈÂF¢â€ê@˜;·A®ìéé º¤³‹='ÂküÆ(¾ÜÛT=Ïm•ÄÃÉ®¼‰È®n­·¯j÷^nˆª?âØÉ“£rëµ'C•Ùv¯bÃŒ†Ã«fX½‰-—d ¼Ì‹9«Ê×ãp?¶­ ¾[1(o´[ê-Ÿæ|„>ÀDT>dˆe{vÆ(LtA»ªh߃³[ÑMj5_Z²„T²jbbbœ¡Zùa,#ù7—$"²Üávëî<)Dõ]R[XÁ:{,V=ËÊsm6¿/ŒÁñ\’_mamñ¦ÓyŸP?© žþˆŽg²j¡†‘ZÇš¤–VÓ7vË··íÒõ–u-£X¦#ß‚AÕ©›©#s½¬Ç¾ìо½3¾œ›:(˜¤Ö〒ÚÒ8~dÏíi®ArI‹~‡tx[A‡·¢Ïþ.¤ÝßF²µ+ƒìrɱyÝwf”V¾¸^yÆ:'·S½Ú]ø­®ÃÕ¿ñ{©Mó`1ÄcÁâIX ¡s ªC†S ²ø–ÉßSÉ ˜&C—Ý LÉY0WªêšÍ£Ãð¨n˜/A2§6*ÿŠKÖSŸ]Wùš‹”uƒ—_–}ìÈãxŸD?Sd±Èí..ó)íÛvšÉí)S3Á ‘UùÞ§â/·[¬³ð¾5owôïºÔþH:³cmH].X?ÚÞ¨9ÛØ¢5ßѰ‰„¾g?LpŽš5é¹ê:6ÕÝþŠì§Õ¯‘Zj+‘ZZ„D&ckâp„7ñðcS¼aŽˆ5­£e½Î}k…*”µ×ž =$ÏñùÌ7o=qâè>dI& HºI¤–¸FЄ~_¥8\'¥nн0Hn¡dW2léVðÀÔ“ag—m”¡Î )lcˆ$mLûWlùEÜAƒÌÙ¯Èçäs4¯÷U¨Ü@MVh˵ș¡V–g™…´Õ_Hn˜[eáØêåzydžF•IuAZ,Ë–ÇRŸ]S¼¬Ç^}5Ô™×YóòΈÜSÑè3¢a!õU!«ñ‚###)„:]’¥}WöÙûÎ×Ñ;!B5#0&>ñR*rfRÂÏÕYôÎ!½£ôLö(º¿Gñl] ±}P{ÉbŸ k E>Tg%Ë.‹úBzá”ûŸø¨¢X§4Ê:ɇoX¥´ÊÝ[iÐínÈv뫨G¢Û§ Îö:âÕXZÝkÃõ à§7¹†‘ZÅ:°©¥_Dlö¾àâç >æX„]ú§YD•1Ažu*t9Åd¹ø†>÷ÇÂoéËô6Às±Ñg|x¡ˆOú%Ûnu¬kSÿU²Æ€jc#^c뼨 6\,+@"s†ù18•àúF7bPØ´êŽQ¤Uç‚Ó9Ç~ŒeÁ³jQC\ŠælÞˆßiÿ؉ɗAdú…&I¡û¢C•CQvCe ºÀR Ím”åf-çª6Øû³HÊåŒ+‡5É;ÏÃeøv닾% }ëR«c¡©ñ=L’a¬´Ä· –7‹¢»s”ËAØÓp¼¤¹2~…Ó)Ì“ùbˆã˜¸$ƒÌ¦&Çä6e”•ç‘Áƒ#2]¹¡!émþJ゘%8„¾â•z[jƒeË‚I†ÆAšÉ°Üûtü£V«mʹöñ«Ö­”¬ZMÕž(ëWòï=¯baßõèÏ75k ù³’'¾†jÄ8è_$ÊŸÛgÒMSø¬Äñ+ÊŸêô˜U&p5ˆÔRëÉÚéÕÒ”P}l¤‚Ð[ôÐn½²C÷^׃Øò-M"$c„ JG€$´„VÚ³mó¼>šÿbÂFªÔA“¢Æô€¥=ËÆB¥ý;;a½[W¦évva™Aâd~Œž§ò‰ebÛ€þm#&Ç7)²´ û6þý¯Q{jº­Ý (K6ní¡i|øa_´± ÈÛ9~6)¤’’‚Å‚“èí)6Ë ±86´¨'{,ExcIévÔzíÎÐC=hX-ä±h™v‹’¢°\l¤á… YM)Q‘)C ê`l^¨¼g‡oFósùMK{¯RX™ÂÆ­6ncñ›?‚Û¬HTÉXð®¤ØÞl¿|ù–"÷ªÿÄ$´Öÿ{è±+#¢êB+]¿ú¯*ó…êoÊÙ-ñËÞƒ@l[麦^ûö' |±-å'ÁÇçOt ŸÃJ‰R®ËU6é…_ˆ}AËUôY‰D‘,Òe"%n’$ñ ]ü¿O~FG#wìÑûê^/ßÞ(B:¶@¦”@:´ígë Ö»6oüïŸ|ø¢ž„+áK8׬˜K¤o}š9²%C‡R[6lØå'ž—ç¿Ý¢9ÃÎ.®’]p¢Îh:åuz€èøžØ`}úžTŽómíæ›Ã=¸–6Ze릎ְÁlkwœsj¤Û›y~è;~\³åb´É ùš"« ¨ŠÛ"K^Tj4$b3 ˆ¨dpF˜€S çZ^èê“”sgãpèËž=BK?$Ålg£p¹Q–‹‘Úà GsY ÜR„ò´“Úa´Å0¬R(è· ­Pwð°èOÃ9î»M»wáÚ-°š|ºÝ!Íz#.Ž> E¨eÀÆ-õ¯í2dÏ<1 Ÿ5OcÌim4“k=Æ1Õý0œ8|/+òÔs–§ýp>té-¥9k·nçÕ«Qoz¨@B{v_J¤š÷$ÛGÂ#YFXøÛwßýd§÷Þ{…žš5.Vì¯2¥ªV9“sXÈQ#d‹†y)“Ôæàœav¬K¾ütñ±C÷½ø²[{¸µÈŒp?n“2­å²c‹ÚF’Ì£aVê,Ënan+æßjpȃ./¶@/·°Û6a¹eMO¸šbÿBnOˆIHz5*Ü:é¥gž!µ § oñ~ýRwÊúHóÇ0“Ñš ¶CŸ}WišvÕŽþ½7ïèÛ{ZxdÔÜÆK–T—õ"¯ôîÚz]6ôA]QšB‡Ÿô]/BE Ü.ßð·òÁ !Í­ÍÃÇ!ýËØj“i·ŠBðøþxvc¶ ™Ý™1~|°¯§/$z Hϓȑ0"´4zòºelغfõÎA—_=¸M—sƒ¼‘ÄÇ©' êrô%[§¹Æ”)'Ç h¸äQ½YÛ7ÿóËŸ?|»0//›T hð¥Í$µ„/á\c¾HéùÕrÔ&˜ÜzW9¬ºóʇ¶ï|3ë÷^J3/LŽ©©/ÈäT‚ìì’½ÝF¾ñÌc@OÏfOÄéiÀit‡“\ÈîÖ²kHv±œS¤5jóŸ#FÐìC@ƒqI­T‰%ÂÇôÝd?à@”M9igÙ6h ‹Paè9òŒM …©²ÇrCšdºž9žå} 6~ÒØ”¤ ŸT8S‘ F ¥¥Q?ù6mÛÏës×ø#è®Å3aHEiÆý›YY'’·÷ï=òÓéí—­ÞÀÆQ¹†”ûP‡Ã1†¬`Qõû"TZTG8î¬ß‹Ù«Øèµ¯iŸJ¶¾ú“U™Ô’ÁaUW“õ\ýT~õ7¡Â%ÒEŸDj©ýæfN¯è.W®¶äËO~†ìí—Ýz¶jÓ©KGGxD´Íሰ(c2éjÀÒMj¤Wõº³só²òr²ŽïÞ²ùŸ­ëWïÄe"N$=8Y°ebo’ZÂ×H‹}ôüb‘-xžYá´»c Úù–ÓmÁìú²WíŽïƒäâži†¬¹o<ód—Ÿ.@ÆÞùÍf7yRËà »;òÉ.3Ü3˜‹ŠPþùý†QU’ü‰Ÿt­—ëóu&…§G‡Êû뇊 óñÃ>Ϫ°mM#ØèP©Ã¡¬úá.õãØøÄÉÍ,ññbA™â,Ú¯XEýÁ¢]´Õ4÷Ãø¸¾„–4Cˆ ÷àœý4èßÛûõþ ó],,[€…eD^i<´»éö’¢4‚Ù.ª‚UD€ÌŸÁÔWÓ;~ê¼÷ß|ù¯*f'’—=ÀU äAct|â>¼xw"£ùUʬz›*ô•LÄŒÈl!¡Å12ºGÞǬÛ6¬ÝŽHÅ©‹_¬DN 3ÚLlL)7‘$"µd逎 OÂŽä‰5êKÔx~afçly„Ù0â’xÿZ°a—ÿï투,½›NæÇ á…×"“ô¶F ó¹5£ÓH_"í1¶ÇÅëŒÐáÌ8¡³nsgî=%Ù%½]eæ(6”}*Ž ï¯qý…›…ÿÓgùrkž€­Û‰9ÇŽŒD¿„)k¼óçƒñ~>¡º§‚à~ŽyÃwÛ^{ã"Éé¬jKc›)¥µ×oÜä2\à°C[Ç<n¿íÉž/áj¿™Òê~ú½hlÁÏT™ÔõáÒøª|ßÅ)Éñ40{ Šš›ùÒš×HO‘®êÚâ˜^v"&‰0ÓàR­ æ G¸ÐFD•+acJi‰È’„–ö¤O÷WŠ_c=·Ðk»ÂSÁVéé*}Ùùº?Á‚ÐlïI¸ ÖÎÚ »ù’] rm!q.ñ½Æ §Ø¿"¿;ÅO…_d÷`Ù…dn=¡·+sÛÆµ#G&ß‚ûž$Yæ¶[¹rs%ÛJcs¤îd—-–ÖðÆáX!xßv¬Ý´öèÂ,];3mï~æùmY%›ødä ‚<¯”¤sPáLã)m"˜Àu½yX•}‰ƒ_E3$—péê¾qXÌ<-Æ™r~ª3–N0"l¾3‰.‘6Z¸cHk±'œLb¼/9*éç`âBX6DjIJK¿/uºæFx¡5¥´”®F<¯Ý{dÈàžæ–oÖˆJ£’z³i8¤­0t[° Dñdt†~^W#èêæ«3 }0?V²­]üXMAr›‚ì^JÂŽ7AÇOÜmÞÌŒèlÏ^=ýdwÒûÜÖ8âÔ'`a‰â ìjÎêå©z˜K}åRH°ƒ0ïë Qµ…0 Ö0OÒîÄñ½x—{V—Ãv2ãã9÷ŽßÞ¯×2LÖ| ¯­Ÿ·úóïm…qÎ|@ãs©U,–&á®Ü uQï¼ÁÜ¿þÉòÞýï™KƒIáD/!mD^‰Ä¹57ºNØQ<3-ƒ;à9•bã“ßE-iåñÕµÁWwE„uhm…á6ØýGÍèÈIgWá-ÔÙe0 ÒÜiR h8"ˆ&w´[ša¼6?ñ§µ>H.óƒõìrWV²8‘✰1Hj&ªQÍÀ$ØQùm;öíɽú½ènïĻܨ¬ ÞÙ¸6ÐíÖ^Á¥>àsY–>o»bõj3N {z³MI-uvE–Ãl, P°öíÅ÷ÞÎNV…Ôè˼k7²œésTËSÙZºtdáñaô£á¯rÁ¦©¼È…#µ/—¨1ãeåZ]ý©üBj©Ú˜âYS4Or¿¢À »·IléÁ2É©Y#bK/¸/¡¥?`/:ò¶`âbbc[ˆ6_2KqjÌ JZ"´ t·aºý±ÔÄ„…Á¾?ëS`ëöäIÛçfÞDì{~0§-WUHv /jùÖ$Ö¶ˆ##ò¼l[“¦Éué±7Ñ Ž½^€=—‘n­ÖnÙßkÃã|ȧwæd^ÅaëﵸF’V#€àöÀ9C™‚»Çxï-Ÿ·»îº?JÐÁ¥±ømVô t^é`ØÙ¯Î<Ëÿfö[®e<×Å<_ÿÀ\ß.bV¨ „ÅäÈp5ç æI[Ërß|›)­Z’W¥C¦ï?ÄrÞz‡©ÿlÝ–YÔÛ¯±¬äטcÔLnÖ„y~X̬úõ£<²'ׇ'œ˜‘ÌÒŽÒd®—0÷Wùݺd·±Ðîb!ç÷C4˜KüåO–;k>“l!Ì{7®€‰#/sù=ËûäëÓÚmiÛš¹>ûÆýí7ÓšøÊŒ7Ô‘ÆU·r¥×T~#µWjbüë ¶4}ùФî]ÅGj€Ž-1SÂHÄÌwšð1¿ªLB[—Fw†6ÂÅ$¶¦D–öæuÚט`èÐBåî–Ohã_¯1•÷sE fTv [ÚŠôæ÷:'¿„^üÉ#píçbEv@ !œ@àS:[bÑë+LD-@`L|â¥t83)áçÚŠ´d õÃ_Ѷû £½yÙ×ãø&¼°—ƒ˜Òl£ÀãÚâàq oïøòóÃÐÁ]„w‘dá‹ÚþµzîÑ;Nc\©­šðF‰´öë ;)–7û}f=¯7sŒ¹‡¹aÒ\;t„©ëÿa–NíYö«3ÏÌF±Ð÷{ôƳrYÖ„É!{è~vrÜxãžÜ¤‹p>ÍÔ›YÞw‹˜¶ÿ³‚YïÜ<–÷þ§Æ¹äeÞ¥ËX,d`6ú.æYü;ãÙ9,täfrËûø+Æfº‡d2°çy奨[–ƒzHÑõXØ¿c™û÷L?pȸoþs¡L öÛo0/Un}0$ôå4¾ŠàGüJj©^±O¤)²YX©ýKL|Ò÷xyÞ—ò7AnÇ–ˆmôÐÉ5<Ú›Çt¯®…âÄÖ—äÖ˜’ìÐæ›íâwÒ¢0Ò¡Åyum—ÐVåaµªZsEÒà2ÚïýDUêU—Òb‘«Ÿíƒ9öìtçCù£]ÀmÕ¹_M­#µ¾ð´Yº4çsi3\ò*XüªëdîïH£Ì¸è´CZyHï4ß¶µ_¯™šþË—kYÊ¡£~ÊÈ AŽ_BÞ¼™wÕ:Câj~ ³ôèʼËÒ˜~ì8ãî–¹¥‚$Hm­˜âÏJ|é'3™z²“ã ¿Úˆé‡u!©m¾D6¿jDVub"Ȉàºþ÷=“B톄8ôîÛ˜µwwæѵ ½ÐXHæúè‹üÄÿIÚëY¶Š©û`e-8³ i-}'ø?ÐÌr59…ÿ ¨Á9Žq& Ð4…ÏJOÖ!*2X‘*‚Ó9§ó~ïþ‡ñ2= •„÷xŽ®Ã·ï!Lq’Ó9£î¸Ó•º&'„“zH5k\3Pñpx½kNŽ ­ñɲ]èS´(¬6èÐò!øÃVÓôë×¼Ÿ>Ð5æP’äÕ’®Ëõò<줣PU¯ÆÔ¿&VÔ¦ê¬ùñ\Ö,3O—0Õ¯sO¤&ÅAaPÚšø{C <—‘réBH Ü5 O/,½ ‚šKq~1®‡Ów+ì%“ÚÚU,—€…‚ª^Î…U<ÖÃ6dÜ$¦nÝY´X] ÜCK8J øò ƒ>­gÕz–ùäóL®É¢ÞŸnD¦tÜåfJ‹"\š*ôhUæúæG–÷·¥dËDf¡-­Ä«þÀ!O¿dR¼nÅÏ ˆDZñëâ\ ^BE|ä:œ9©íÑœfkZYeŽ#¡ký†ìãkoa·~ý)Ûtœz—ZGD2›ba[O/;bîZ1О߬%ûu?i±ø'È:c}vfpŒ9b^Dšú7ÿä.r€Iå{‰$‘'mÿ!)îZæäÒøÎAtVRa¡X€ÁÒö¦3ÛU—1¹Q¨!¸™ºi+ãæ†þßM,gÖ{0ѕɬݻ0ÏÊ5媉!…N,?zÜXüe¿ÖÌ êšõÌzÁf]±†é' ³\Þ¿Ò˜÷ϕеÌT¬»ó®ßÌ”–ÍP/tjžJì×#CýÀ¯9ŠÌŠ"ÿ Tôš8lÚ#¸ÁÝÆÂF*ɉtSx°W?6¼uÛ³R¥ ›·bOôƒõ$?è.ÃMn¸^ ^ÅtÀ= f›ÒHHúîrïs:W‹ÂMY9$Åíõ÷Ú?®]?eÈúM·ŒÛ½wðâ“Y\V뙿*«Ð6"”Úî½,jî4þÔC†>lVòëLnÝ’‘½ÛèÏæ°ÐØ{ˆ…—«îö0Ùo»ŽEÿ7éd¦nÙ^˜6gæ<Æg°ˆäñ¬Þô ½Yº™ Úö,"i<«ÿå<ùòD&7nP˜NÔ<Ê÷ÄÔ¼v‰ ~BÖ"nÇLøûî ÛÕÀ¡°ù)ç¢Ù—ÔNtKÏÎb£ê³ [´bkŽb3Ö¤±uÇŽ°Ø}‘Ú\L“fa%sòò¥ì—}{Ø í;÷êÛíìô}ìù¿~c'=n6´evsÇ.lÁ–lüyƒw7{9í/öýMw°—VþÉbzôaMáláîìõUËY¶+hJʯ]½(öÚ%ÃYÊ8œ›Ë–BZû²¥Eã‡3úü¤SÛ4Ó­Gäz )g+,ˈ™Îñ»ü}É6Ô—Pc±ÖcíE8 ZF–Èd1ºfw=úôë=®sïXö N¤ˆpÆs`=“>á ‚–o6Û¸n^,çž$¶Æ:¨”$¼³ žÈxù ò Я¥ryfô#§þpà%lGxäÊ9/'‘ R2& Z”.ð×{*$µâqÊDfù>’ek¿¯þw癬ÿ® ­ÍÑáò²@JoØCÙc}Äuâ¿°faálLOše)=‰iËŸöìdã—.fkfBlÒà¡ì“­›Ø#‹²ŽÑõÙmÎ5â‡bÐ:¿Y 6qàEqýfç6ãz‹ðæyþjÇV6uõrvk§.lh«6ƽÒòÛ¢vø ;„‘Ê~ÿŸÀXÚ"ÛÀ‡#ílmËzòÆõ˜×¢ôUUuuŒsJk£‚â_yXŽˆ´‰dð,¬÷!´T="³•!´FZÒ»-…Ð÷]®Ó -Ý ýZZÌ BKEˆP=ˆé¬êÁY”"¨Ñ¤$>»‹JÆ&L¾ÁæUïk™¡]oc䘄ç…Xôôh»r° {üˆ¸’´•ÐGû 0VXü“qŒytÍ䦦µ€ŒÝ I,IvܽÓ8ÿ’[RO˜½>Õt¨ÅÊ&þý ûvWþ´¤\0µùÓž]ì½MëŒ47wèÂ7oiÜKZ¶.5¿ƒ9Ù¬CT4Èm~ÙFâþË€›b[¥÷žá’ê¢.`qµ*kHhŸ®U ÔF¸´ÙͤÖ(Š<u‚E&_ ©_ÄNLºkÒ¬ Ž¶{µ£oþaÓñ#…™f¸]̉+‘Q­‰ÊX¸ÕÈá`]ssaÔ¯ð˜~Ý¿·È9l€:ƒŽ»ò˜ä—Byò3ÓUÇÞmUØ^¨´=œ}Ñ8ç‹-§9ŸÙWåŠ2@ HMŽõG‚ÔúE‘‡@ Ž ½§öà‰oê¿Ò£ÈZzý0v¨žMQ±¢,!·Œ©ÄâåeÃÍåA¨\ùù‡Åož»µÓuíòJ)£<ùf\MY¶ü.Û««0ìI6—EÀŒDfîb/Ôb’c`8uƒ&ÉÃw6 gim(û£CY 홀ÛyòëÙ° ƒu’®þ¼wk3_wŸÛçÖ:¹}³My¦L}î—•ßÎ̆:ök¡@IDAT:DSG«g³ù¤ ìa(¤â¸nÝØ’DîÊ!`í׋نÕŽ±tïÌÜÉì7\YªýÛòÄ g0GÌÝ,䢒-Xºuf¡ÿº¹p³tio4‚ˆÙ®»‚…Þ‡áeŒ=ø†˜õ²tlÇÎO‚ûßлn5Lù¦ÇÁƒ@Ñ_6xê%j"£’þ£ëzÊ Gˆ5­]´’2K&¨Îføpóvnýlù÷³+Ú´cK`Ñàõ¿—³q½û×~¾õ.6V*ÊÊoñÞݰ›Á¡Œ/¼´²ET8]ã,{E‡f&>•¿Ò­Â9Ô½câ/¥­îµ¼š[ µ Ç=#XÄÄ'ŠZ"–‘“â˜ä°3ûõ—³°ÇOy 3kXž8ö›®fŽï5’„ý;–Ùaç¶x°ìˬ„ó…cc!ùœáqÿfÖžX4 Ó_aO>ÈlÃO‘n"ªaãF1=;—•ÊrÜwléÞÈä⎊WDœŸ5„úÁYƒ^,¨Ä$$=39“TvW£ð€QÙp¸Ðm^J!(.!'H§Y( Í $I]Œ-™í¢0ký*c#“^dêË[°²š‡™ ÄÌô:ôr}Ë£ë/þÁ¼mìKËïtoïøösVeg˜ÿ*’0' ³Ü¬^Ž[–d9¾@¿9¥Ô¾,u.Å´êçÚ׺àiI:•6­XÞ?gÖ^¤“B†_ÂÜ‹—²œ©oÃéA#5û5–Kvc \çR¬òÄ!g ¹3ç2Ϣߘ¶s/³Ýts}·¨ ”üÉÔei,wî‚"׳^€sSx3È·uð@æþa‰qj<€©;wʌ׷'SÚ¶>å‚7?7ñßOŒq& Ð4…ÏJ¿¢*Yl€ªJ¥DZ€@ 8ˆ™2¥×Ù”ãá6B•ò©-3 ­Ïev¦{LBë{½²Ç¥åGe9tw«¬ÃÁl¬Èc+š+Þ tyµ*‰ËXÉ(ƺÿ¨äì ë…ÿÀm¾g³8¥i¦ïË÷ХĢLMgJ³Ææmc_¾8˜¾?݈¯íÝǔ槫Éõ™¥wwþÄXf»ä¨¨™„©-];3mó©‰Ûe3ÏOùV ‰o±xRxsŒ½—弞Ê$?¸6!þA@÷²I’®M.r±'âE¯h"‰@ ® ÀsÕaÐà ÝW?T8j9K?zm·½'5™é{ìå§S‚ ‚‚€-„q,â4KÅõÐÏ:ò ÷è”Sp´@Ž|ƒëË…ÌýÍO¼îa¡P…°ßq“ïmC=BAu}úµq]nÜYºt`žßþ*3žã¡û™ëßAJ r^` °HqRu$تªœAËÉ F@ÒX8É!±8,ˆkY{«Ö(ÓÍ:ÊÒÑÙ°qiè´  êA P¸ª©.·b¡W¿Ê³üDf¾~+URNÉ}|Õº|Æ8äŽäÈpÃ©Ž´|’KyPð®Ê·7MÇ<7…Þv-Ë›ó_:e¡·^Ë,û±Ìgwå«+Ù.½ˆy ®àëì¡x<+$»¶Áç1H™µKG&EF°Ð¯fú± ¦mÛeä]žnÅÊuÃÛ„D@ŒTDWä-¨áX$«á‰©Q–«†·¤fUß]à‡²X§ƒ™`ü·ÐP¥ï[Éñ»kV+Dmk*—;#Ûîð‹^š¶ŽYÏïÏH* ëzÆ ¦íMgrÃúÌR {[ž8ž¿×1Û•—2 ‹ÀlW]ʼk6ðZ:µgJ«ùÇÛ{"ÎÖ>Ý™wåZãÜ~Ë5,äò¡,;ñUCýÁtÅkRûÓ¯FúWR<íЖ µOÚZæùùa±™ºc·AÄ –ã ÛÊU¯÷h9¢Š(U@@Hj«žH*¨íLOzfÓñ‰´8žwG¶Ý* ©íM>»íh’ébçÉшØÂSîK—õìœ0bĈ¢"©³[KQzíEÀ ²®Üœ#¹MšJ.˜Ë³û¨T¦ÙîE¿0K¿X öª!!ÍþÏLK…\z!³_1„õ8+Oœ¼wÿËŸ{ŠE¿?ƒiû²ìç±ø !ôîÛ˜~ôËyc³ßu³žÛÑ ®žÕëXÞçßBÕ!„9FÝiÄzû5cOÿ²&Lb j^e ¥Å˸ù>æùy©‡þ9FŽ`ÞµPæñÂkg: sm6IÍÎÞ¦¸â~Õ¨òÔBÕŠ©`GÀéœcß§î[ŒzØÓ LÞí`\ô~ýÙ$,6#2Û"#Oµ{4ÌÑJK‹4v¦3n½_ ªƒ™ÁaÈKÔlá.·ÔŸSíØ"±5ÀÖ´{ÿAϿ⚤k׬`ÝÒý3A@‹¶Ìi”aè¦×òNÍ•/Žùø¤!}\,>c¦Ò³%½WóÜ(ììþÛм ûº×ylóª´Ûýö Z•–‰!>V ~¼§KèïéÚW6Ime‘éu§ó>¬ \ɲÕ7Ú͹§Q¶[ÛÕ LÉRÛª>./k½ÙÆYnÕ¢é˜êZ-IÊ䙉ã?f»ªŠn~zAfψ£!E,ÚÓ"D}ýÊ¿¶:ì䯭#Ajýò [„ÐR•ð!Ç}­q©@וŽPbœS„–âp¨ Åôl‹Ü;K'À‘ëª÷8-}¤úâ}–j„Åri³?jå—‡ÕyÁ@ìĤë4ÎßÄúûÖ®Eß*´5¯bÁXÑÚà,2Ï‹MeQ¹Õî5¤²n‰³¯e‰OŸ™”ðsÑâL phm Ù¾ŠÀV[l‡ÝrÇím»t½å_-a­2„*(0©TØÝ}0h;´oïŒ/ç¦NE&¤·…V« K&ÁŸAZ¢)òÔ,X ü´fËÍèŸÆ"¦þpË3Vv<f›0é"È02SŸ]c^«Ì^X?¨ j"@@ pF ôDW "m ©‰–!ã Ç:“i^~{t®÷¦ú9ž6fa‹¬z, ô\eÙ Ý[’äÒfHˆÍHØ+$)Ö8#u‚èÀbË—«D^Mfb°M²$ý.)ì;G”caIDÛ'[q piªqI0T%ˆë€Ï7ƒdÙ"óôqI’[Òµµ.ùòÓÅÇ8|Þ…CoÅ¢§ÈvGñ®û÷Hí¤WÙŽ-ò¯ñìÐnoÔœ‘•ƒ ›HºÛ•µê—Eïþ½tÉh‘X“p5É,á-‚׿s¦ÿˆKø\®ð¡ÔV2‘@ fÆ$%µ`.¹çzo’£®­ÀY[ã8]H¢8 ÖE:B÷s¹$gC¥áô`÷#ÍA¨:ì•$y $›­Jä¦é·h€¡!Ÿ8ß*SC”†oM˜p¬U½º«JÒZ"±aØêa#)-Ù­ÆFj »ÝátùÕƒÛvê<Øb³Gà½á·›‡»r%›¦Ö9>A®„Éó9VÀ¯¤ºÝ™»·mùåϾ]˜——Mª¤ûOÏŸÄFä–ˆ )-@0ÃCqIm<Œï’eilJbüLózeöBR[ÔD€@ h˜O^{hûº¤J:\>Vš•î58Þ”§7Ëੱ±´hC„Úˆ€¤¬„u~¦æñkмyµ±‰~jx™¡DEâæfêµë.W®¶äËO~^ÂØ/ºõlÕ¦S—ŽŽðˆh›ÃaQ,DˆëFÀ×.5noÝòr²ŽïÞ²ùŸ­ëWïÄeR“¢_"±´‘­)©%5+#-ö" †!Z˜å·ª‚R羬ª ˜H/5 ˜¸¤u`Ù³’ãϯY5¯öÚ’´–>øÈØ)±%Ýw’Ü’[ºF÷(‘^"¼´ÕE.Aä”Ôh#²JÆôA@–ìС%K¦„–îQ!¥¾¼‰Ìö©‰ñ­«jFHj}‘Ç€@@ PûøLÈÇÞŒqNê’êœðOík ßZd4š"7‰ªyôAéz¡®-މ›Ä‡…i踶SÒjZ"©DV SJK¤–$´´'ó]tˆ/¥Á1“¯Ð4ýY–c«Jh)[Aj}À‡€@@ Pû°XêÏÕ´ŒsÌ~¸öµÎ¯-"Âæ+I4¥‘DÈLÒFöl}¥µ&±5I°_+¤™™¸V¾RZ"°$©57"¹„›)¡5 1.‰@è:ǷжfÊïø‘ºôú/‘‡@@ j;Ä ˆ¬’àËTG "K›¯¤–î'µµ™W˜¤Ô—Ôaõ•Ô’š¹Ñu"½D~Í´8ÁDªÁ¼ã*¨‘¥ˆ*‡ÚüðU‘@@ ê(ÄHµÀ—Ü’-[ÚˆÌúÚº¦Wk’ZR'0ÕˆÀ’Šm¾d–âB ª#R[(‹2€@ ¨—t·"Ë;f&Nø=¨*|•1ufiO’[“äšZ“ÐÖ%>A$µ8±5%²´'"kn8¡º¨Kaua*Ê GÓž[`‹¸#¼×-ƒ}â×mŠå×7ϤyµÏfõLÉ­IbM²K×ë"—(Nl}I.‹P€@Ì”)õäí&MçÿÂû&¥&Å8b¡X ù @Ð"ÐÜÒ²ç-}$œt<ŽÕ*ºuƒé¯ г6KämñŒú?è|©©êõÌf'7²EôODGZï~é™gh¥»j{|»U9Íé4‘H#‹kb·¥–†–wüžw_½ŸÚLœ1 ^ ÍPŸ‡2žÿæÀ¥Ïò6Ì_}(m€{ª‰U ö‚ÔU‘§@@ 5Nç}´˜'•s>kÌÄɃ8×zbB¹;g¼qhhž´•ñë…††šRI#Vx½zH?©= 7<Þ Y3â4ù¼µ(~(‰[@`/Ô²ÿçß+KòO3ǧ T€@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ €@@ ÊF€s.u[° ¤ìXâ®@@ NG@:ý’¸"œ ØøÄ+%ÉrhfâøUgŠ+î—Ž@Ÿï4ò¸¼#g—3ÆûpÆZ ¶,1ÉËß…ƒå\R¾±GéÿK».6·ôœjþgZ›â÷ÅyÙÄ:“úCÚ4©ìXþ¹ ’â—tr¹T'=æÍº:Oe›¸Î,‰Ð–XΠ_˜¼çèž¿{ΛݽÄ8e\¤w}ÊÕ²$ÿO’Ù·\ba ‘ϧ{÷¯ˆq¦TºO)£ÈÂ[ ظlœÌRÃyzáÅx Þ¹ø£‰* êgMR{xÈðÌÌÿ‘tïžž™\sÞ à¹’þÔÎþ½n»rõÿ*ó[¡¬L¤[*1÷}ØOðÍcÑú-Ws‰ǵB/Ùr{Ù•:“¢Ë»ªÑnV$þkU3é«®ï¥Äjº6¥žù/¡jœ³®ª¤þÞc^êõëFÆüRB”2/)JÔÓÉœù`ü‹çz¹g£¤¾ç)e&¬ÂM§ŸÊŒM¬BÁ’T¼sÁòKˆz%"pVHíîóÎk—••ñjÔ $³b³–ç_ìèßûå¶+V=#IR…²€6”+ì]çS!™M1bMEAר(I’߆$—¹Â@S—˜´LÆ… 0¨† È¹d™:+qü Š?éfëÃ!Z ïT¨ .ÿ¤Èò;3_ˆûâ8\N×’“™Î®Ç)é ®D>¦$% ¼ü;1y8×õû9“ÎG«,È'q4´ñõ”Äø™+6!i $\ã0uÚ§ë$Iy"%qŸƽ‚zàøÜmmŽã¿d«í^]ó c:y¶aÿŠÉ–×SŸ]Gé(<èLî­ªúë8ì Öa”9ù¾Nø>öê«¡ÙÇrWKVù¦ê¯Aâ5Il0ùbXÏŽ¯†1 Iɨ׈î†uö©I ŒÌ}þ•+Š^Z}y~Z„K=ù-~‡ ÐóPVž,³0M L¥í³’â&Sz’ºÿ´fËràð êñ8]£—ô3~ãéP-ù„¦…]ÞÌÑ–«€w¤w«d {*Å¿2?6c£ã“fCÃóS‰óÎøí÷“ȯ¯yßÜS»0³0žY-·¤:ŸÝc^ûSt›—ú¶ÎÀ•ª}3r©1þU·÷Þ¾hÃÝ£Öœ*¡bG¤ €w›ÔGŒY›Âç<Â2€g«oá#ú¼C/§&ÇO2žG/qûâÃ7Mø!ÌöäëÎÇNà™úV–øç3“f™5 ÐýêÒ¿¦8CëÛáýY+K!W§$=½•⌙˜|…®ëô¬vÄ;¹Y–åñx÷Ý ¡ž~ñňŒ,ïk¨ÇU¸nÅ;õƒÕ¡üû­ ŽQœò>›ùñØw’Σo¹Iu‰ñ/d‹eêLçø]”…`xçJû P½7ŒJü£…±ñÉÔ'Z†õêãÛûÆÇ@ÝB RÒ’ª@´kÀ€¶ªî] RÒ©*ù€”>bKŸŠ‰ÛZȾÂвhí¶+ÌÄ:_jŠAc¸Í!ÍÇb3¯ÓÞb±Ñ ¶ ƒ®“R܇ÀX3ÈÝG:Rk¸‡3}ö»Ô—wjºþåKq ¤5ûAŠS•¶¸”‚Álæ¸I“åß_‚AíI–>HMŒk Òyâ¶ä²üXBËÙ²çB,!A8ÄÀøã8ç‹-)³Øß,KÊhÙ*] úPOW]+@hÀÀûîAµS¹º:ŸÒPx0.©íïÀ’íRW¦°g1h<3fâ¤Ñt_Ñ4<'¼WµÏ°ÿ‚9¤nœÉ/#Îó?­Ûzű(Ñ“QßOQ‹OìaçÙ,õ†ÑõâÁ¬cYX•UŸ©ÏË ³„_+I,—KòƒT–#Ú1 ‹ «¨ßb–÷óÚÍḠÚz ºNùBe¨…ëÆÇˆ[Íü uîÌbVyˆíZ]å¿=äœäólòfLãNù«€ß(àsY†¹™˜| m*“凡5Q)ºï57˜òÙÐWÇ+âÀ#¸îý¸ç¼ya•Ííaç‹Íñ.“*ÃvÊ£ð9Ïöâ¹`‡ ;ïêŒ1ÎÉç¨^ŽEé‹dŒ—a4Ha‡\5û{"W\’~Ó¸UŠS!]ÿã 4´£niðsA¾,’B1Æ8“»kÿùÌ`áÖ–Ðߟ‚w?Y…žÊ1Ú/€Y3É"_gµJ—ã~o®þßSqÎülæÇE:õwËùjÉ"ÝŠ÷ô<Í«¥šyË;WÚo`Ö“ö…„ïnT¤õß‚Ðú¢#Žuj•Ô’>l®êù†:jÀb<ú¸ÿ´O[]ä+¾¬¼10HNç¤+ó±^dâ‹ÁT¯g$:úï¦M˜p÷Šd1ÝùôA\øyqlü‹¯ªÜ»aìäÉÑ3ÆÏ0®Kܪ„Yþež?ùòˉ'OºÕuo/ܧô,51þM#nþ¿!%Jpçñp:û€¾å*ìsS^ˆÿ ÅXŸô+ÓõÁ8&©6Öбx L¯‘”Ñ8çÜŸëRU’,$C¬2ò¥gžÉ¢8c’gi:Ÿ#)Ò½3_ˆ_l\‹OœŠAø§qS§Ú¦=òˆ[eìqà²)³áŸ"ß¡ jc¿pàà ü2$Ës(„/Qÿð ÂñlšÒ…ÔÔƒc7I¯(B©á X•UyªÏ ”ÅIË6ËQøAWÙK9_­ÿšóñã:“oe’þ.~Ó»è\‚4‹¼²4Áÿ¼•¿{l¤‹T]»Øb•{ÍpÆ­-¨ë££ã{¼¸2fuãºÄ;Ù×kJXÄ+Ü!þ0Þ¹´Ý03q²Ââ ª¤¿‰g¥Ò´Hf§N:ª’ÝOF'LÞ͸ºiŒ"ð|_â<ÒÔö¦456.)Içüš9ÂlAÄ‘O©Ï¦Yÿü½´ý=×Fü–Äôó<èÞ9ÆŠü&†Šªð˜„Io¥¾ø fösf;Ä^ ¨ÛT+©ÍcÚ$P³sý 9¾Ú_Ú>`Àwí—/ßR¡|eË;LWÓHRJDƒÃý$-)˜”«´÷Ø º¤Ÿ1AKy£Ð…»Y=ÄÏ'µŒ¥›„–òxå©§r@Jp]Š0ó›0¹ŸÆÕ0@¶x'Ò¿æ¸M÷eY‚d—·ëœÜn†süš†Ë9–ÛÓ…óé>‘µloN t×aP»˜®A5g êføvô‹íÅ8éi.wüÝŒÁ™m/¸§¤æäÐ"j= AF¾ÿ+ŒÃykäÝÎ<§=$<|Ïq¼…“DEÙ°*W}| éŒ[òq [Ï UÏÓ½É7+²r;TUêƒtÒ”ë"üvÀ–ñ q­;ÚwxúsÖˆfâ¾xJðóoHŸ–Dh!mîIßSøž¡-Ä·03q` ÐmþìÁ\S‡ü†v[0û?F<@ºðg ªÊ>À3 µ¶ïÖB¡LŸöì#™¾ ¡6ôï9Ž»Ñ¬ˆïµ7“v‚xnÇ_ÏÔ¸øoñì}«D–=A»Z¶v½leÆ{ê›Î8–xgX`Yâ{=<Ú¶>û¨eAÀûØšyºª¾÷Ò¸ª3nAŸ¾Bî€ ù¤3#%=›f>æí]oÓÝÆN|dDú\ ªw®„ßÀ¨ªÆ½ôa1V¶*f Ó^>?Ÿ8j#µ{úõkïaÚC€=„iž)È—ˆK¹é“b ZãÎåwa¿ÓÒa-ä ?ú8 BÒ÷nYŒû’Ì•W0¸í”ͦzɮ橀±* ¥è=£rõYH& «ï­Vù¨WÕ@Xó%3$átôUU7Aú·"ûxŽºœ3Z(q"ŃyªÆÔ%¦ý?‡éFŸ’Þ´r%ýÔ9dFÅJðtºØåS§#u‹u’,z»tƒj…)KŽÍ h–"m„Ðc¯y·üû3a…ÂËUŸÓJ”ØBI—.;È'y•š(ã? O²CUå]ÝqéjÒeHCRmZ¥¤¢qžâ-¾À<øØJÝïszêK4Å<jOIJúïÌøø’ãJQ7¸6:P ÇGm˜äÖþ…ü}gAJ-Îj‰îY°P¬Ô8 WöùÞÄóªBªyúûýq›f„Sû"åº\pdï\ñß °¾ë‚ãß :ñ*öêó ó@­E ÈàÈVz˜ú(:ìÀ”ÇùDš+lËVâï@â:›ž¶ø«”‹À°ß»¥?â\bU:¿åœ°…nbêîÿp­H¼3èL -:™Aqiȉ,­?>•–·Eœ™!Vë‹o:Ÿ) ªñÆmRÙ=Ùê åV·8•wéGhÉ& ˜CR^ˆ+"*=E)w$F$vÕByê0ƒ\ôy’%é{Øçu6hëçô{‚d,N×~=èM¾4=¢¹µÅ¯T;¬»YÎtoËç¤.©Î ÿ˜5†ôu8Huá¹y½¤=ñ[°wœ‰|ÍÅ?‚Dhjl¬·¤¸uõé?vŸ—zš²_ñàüzäWHþüš72ó¶ © ÒÇu{=Ýt…m¢‹ÍäÁߤëKùAýË "p“äÙ…‘O?ØX.ñ½üÓÚÍç@r Žš€ÛFt1 -2;2Ý·Ú7n ŽkÊ;Ç,ò8‡•gÀžï*HÊŸœ•ÿJ ðy 5ÀÌbXÐÀ¶£_ïÛÑq$ _IåIG&U¤‡%üÃ5ûU $]E! Ài!Üêø'[Íqiª±°m tßΓ¸úHEÛ²º ªíhÑÒQ¶¼qF¤¶`V‚Z£\H/Ã4á\¯êé Kç:äÐU¤jV ñ_{¶`·)–ú«,ÏÎu÷y!ßHz¢f¼Šî­Ìút„ïižiŠÔ¸^ºûHvo‹éf$Nø­¼ùAjûÚ“3eJ½ªxý)W}¸DõºØŸÌ‘P%ô§žó>®ß(3C˜9CUί!K1YbÚ¿¬G»õ°Ž°˜yõ—b¦ÄÙÇn·š5 * 0m|ayÛLñ¢"¬£32½+ÙÞÃø}ØãI[Ûãöšÿ,œ@$€ÝË fO« çã9ypt|òøpKhJs5ö¨^'ÞÕ­¬E£/©lzÎðþÌÕ5þ(Þ~!Ž¢$Ø·~xOfc&aÔ ÆÂùÜÚ¡ÎX¸™ˆ8…ÝJ KüÏP¡YéUùÆÄ'&6µÄÿz@}¹½$yûÌLŒ[à›Ÿ?ŽkÒ;G×F'LºCÒõ ¿¬"}”?°yÁ‹favÀÁzëÆ, ç¥ÍŸÐ~²ùé±–”žH%ôß&jLKÁ •Å_€\>‹mOIñK»†öOÂ`Ö+]]zÌ£z¶b:ó8¶©f|Z=·ßA¼Æè ¯X =dúÊĺüÓ5/C‰7Òk^õø1îÍ;‚Ž}¦&+U" dÚ É®7º¹´Ý}(3 å/‚>ê³ìòì±RüKH›v°lïÔ{oyÒ”§<õA}IZ= *‡ >b,x)øH>Q—öìüË©¼åÏP/X‹È×§¥ë´bÚf¼¿„âw·7ó ¤´Ã²Äÿ•>m|*Gß#Òa–dË­¸§“h/BXŒÙ.ð`ðr·¨r $ù×ìÛÍ9jÎ^(®ÀoªX,WúJæC,Ê;臮B=¾4Ín•T§”äø_qýlSÒÕ}Çðü-Ƴù9Þ¿ f|ãCÍ!]š›…þ`á~5)OçžÍx'Çšqü¹¯iïÜ¬Ä ¿@ýéyX˜ùèÑää&þÄBä%Ô\Ð>lïßç2WÈ’@2µO[Ó4Pe´ù‘É“‹ÊªPHŒó•†Ãº¶Î(n†SißÃÓQÚ°&š÷t¾®z3v1Eº-õ…¸Å¾ÅÒô'ÑÜe ž¾ñË{LRÖ—Ö 8LÒ§ò¦óG e"dYõ]°æ{¿"ÇgªáP•ºR]h! ;U/ÕùäÑŠÔMÄ=3ù¶iõϳj1d›ÔaýÿÅn¯Z.gNýoçkQR$w¿öøãygŽ]v êSzþå&o=÷Ô¡âºÝ¾)óŸÏŒf‘aÊ1Z€ê{/Çâ ª"O€@ :¨&RÛë1˜£"Åþ€4„·K[­”58¬ð*fŒ)tS°»Ê¯•”ð“™µÐòØfE‘n18˜÷Ä^ Pè>o&“°O]W9$¤õú;î¯ôì@ ë'òÀ#P=:µ:­.T H«»Ä†Uy¥¤‹©T93¥©FL×bþ&lQÎç²tpõÖò8-ñÅz‹F?—3+M TȲ’®k´~0 AçJø¡€– 2 G ZH­ cùX©ÐB˜#-YRã­ †Üw@{±cދᎵ–\n£áì€ôïDÔH""ä 'Nbù‡sê¨ý³aD&ëT®ÈV ‚ ê!µ߆)Èi[€ hö+ø× ÚDÔ ~¿aTV·y)d¯õü@5&ù UvU†ÈW ‚Ñ+ y ¬Ub¾%¢€@ Âpi~…ÓT ¤(dÆM€@@ ¨ãT ©m¼dI6ÌYUͰÿ~(xßùâ QÄm€@à,  Hö¹0Ww$0EK¿n¸óR†£ƒoSããÌDã&MjäÎÕ>ƹ[‰½•ÉÒk³ãþKéK{§É9ÜUO‚5’‘ŽÔ'è0g¥&%ì|Ë.-ß8æqi}Ý/éÞƒqI­¼Ÿ‰²›!Š õwÁƒÝc)‰Ïž¶¨õ½Œ¼bnì¼›ÙævÔ·ˆS 8zÙj·Z‡ºUÏ5¥abÖ•ö†WC.¹g%Çßì{] `CàŒIV¸íŠÕ«%…ùÑÍ£´Í!Yî.oá/üUU?CG¿´xwŽ>.h¿Á0ˆ…[b@sŽq¾|škßêŽG+¨%ä¿—s%‹<ƒËù©Iq}u‰u…_ø!¾mç|±¥¦óÿƒTl·ïuq,8Û‚d‹ÒßV¥.²$ýgÝȘQÌc§Å}>œ›\¨„²›%®¿hxêB&¥½ÓäÈEQØÿš[÷@º‹-a–á Ï•$µ--âu,«*í^ck‹#L²8Ñ7õÅvùl¸ÖM(ž7ITu¿-1Û5©IñƒYÆG{Qµ¯±Î¤AhÖþiÎgö!}©˜˜yÇÆ'Þ‡ãló\ì@0#P­¤–€h¿bÍY’‘ªè O¶ÊviXó´´r»6Uë&Ùbý¾ÍÅHbn eóèzê³ÏBšÃ¾ãšçƳoL|òE¨Ë¶éΧ’öç„T'p¡v±:Ý|ëéË[²ly çü) ÷-B *ìÉfCáZÓGAn3+”>Ôd&_·~dì“ôüW$-ÈàÂé· r63>~?Ì€iòÌhÊ£¬w†3þ/s¶CrGzׯ\kñ²ËÊÃ7nY}Pi÷œÎû\³ǯ0óÁ›ïv§©C,Z·mZ³'%éé­×ðBÈY·Çœ¯Ö7Ój^v':}ÃZDY˜P|ú@È÷H²ÀõfÍÄ^ Tj'µTåvi«¦AÚp-M‘U® Ò—!¶ÐAíþXU!iäLçS‡i“ éç©’I ’]ÿ¸¸S^‰$iÎx«S±0\œ…xTîDÝN3YdHp9¿ÂÂB~6ë8:!ù~´d-MK¢ªeêÜšiÄ^ PÝ!1*Ù-mAnãñ…¶¥Ô:y•¤ePŠiÓ°Uçõ÷Ä|]jÜrÞˆqNê‚Å,ê ÊóNuNn;1é:Õ›ñ.ÞÅ·SOù.OfÕJëƒè~Y÷œÎ!1 Szgúè^6ó4÷º¤7GŸºÇ<7ößçby-éØé\l!ò-…Y>)'¾˜˜÷\ªgÃB<ô%~Y¯`æ+ö€@ PX•ñ™òm¿rõ·»/¼°³š—5 ;SHg×ÊXÖ6mµ_œ<ôüô0ˆkܾå£óÏAWÞÚ÷ZuÇ£,Ý»å›5ò)ßzÐql|òS0)±”¤·tnèÝé|´½Iä:A ìlñÀqÔ1™¶ÞÎ9GÕ¼}tMo }Y;fS²A~w…È|EÚ¿b ¹Á ¢OîÙ)ŠÄ£ìÊóNk^íôOCà²Ú+Ëòiú´åÉ£ªU?hÙÒˆ©|ú©îPHúÓ0†4Ö7_Yç‘  9¾×ðEÃ4Éêî×ÿR¾rÆøñ¾qŠcB÷ v0 ÈëSq“Z„o|q,‚³"©5Áh³tiFû´5ODYíÍ1XŒ„Df²ÕèxƒÀžÀ¶×Cª;I‘,ƒÚÿ½º—¿ -Õ…¦%1hÑâ«Â s)ƒX‘οºãíW·]‰ ý1ÍùH‘iÚ1“n龞µjh ÌTix¶ Áö‰©ãÆy ).NÒ›m”"‚ÕwÜ·ký]1Ÿo¼gÌ4,{yýݱ3ÖÝóIhýÕ÷ᓳñýÍ̤c†£<ïtjr|,ôØ;[-¶a\ãŽq&w÷­Oyòð_™cR™€ºÀÔ¥¡Œú{¸tº^²,á-fÝGp¥ Ó9f}NwTQú@FË”F‰†Êü˜c‚‹ôûEÁ‹ÀY%µ&, –-Ël·rÕ{ÒVßÓ>muŸö¯ií\»´ÃÊÕqmÓÒ–™ñ±G§N¹™7TÚœ½æ¹¹¯ÞxÚP"(¢z—t±¦1§#T¾%56ÖKõzhÒ¤ à¡n?Ä&$eÒ†Ë ÙÞÃÇ1]y¹Yw±Ôeð^¼ I§ Äð%_ÊûN“^;tY¿æª>Ô7=—7âé*sÞÌ÷Hg‹/5-’ž[vA—¾yL‚…¶‘lòþ||h3K³"ê%a¢ÊÒÈã\¦>dô%ÿ|öº˜øäfÞb/‚  µÁ :ÿOTIºŸêã|¥!4ù®–-¶¯Œó¸¤ÛÈ6,û#­@ÿbç²ò#Û‘Ž n&wúŽâQ“0¹ä%oJVËu¾ú¿oM˜p Rœß Ñ6·\M‹AŒÄâŸ@ #—ô,$-›[ãb‹ÃPÚ;ítα“©.3>-¸‚9½«ßЄ@žî̼*²'òjZj tÔÉÿßÞ¹€GQ}üœÙÝTPÁJ«-Ú¢µÔ¶´~VÑj[µµ­íÓ>Vl¿$\D¼€@²«#»›€ð "·$Zm+_[¼ÔÏÖ ˆ`½ßm«Õ‚Š’¨ æB’™=çûŸ “l²ìFv³ÿy;s®ïùÍœ3ï¼çf&°6 Ó·˜ë„»—ødÆÞ÷TôB ?xÍ 7ÎÖ ¡Ð¯6“ÎŒŸ9:cR.»ÕÛ–hË:Áº­O…ÿ‘ @ö8`cj³A«$þsšÝOþ„5_NÓ` y°eÆ(³õÛÿа½i,B>—n8“[ÌÕ³„h1câ¦u–^8—ÂÿÛ¾¸ÅÄ1‡R±yЪ?'\ç9X`ãn°ØÔ`¹¡1ñ þG$°£ŒÖ; Pê¶Öº‘šDÝÁ~˱4Þ ÕAÓâ4¨ÇPÍjM n#Œ˜:l†.ìo»°pÝppTËXñÞÖyh›6C¡ El£Ïòýж¥j/‡¾K…rW¢ŒuXÞ«¯ôû.6Y øzs&²ëŠI"I€H€r˜€±¶ßQ,޽߮Lé„k»²ÍÞŽÒk—%/I€z@guÐì6É^2 }Öé´ íÓêêÚ´IÆjÜÑN‚ÉÑÑZº]åA                                                                                                                             ¼$ ?RO UŒŽ©Øw×-õ0dzЧ‘o6å¡…¨“ZÖB¦M>Ë·jYxÖKÙ$e!LX¹r¥oÝ«oE 5Z+=ZH}0ò8²£|,!V,„ª½~%¡h±Öêç^·ÄyN…×òÔÿ!å»Ò/VUÙe/'ÊÁ_  Ìðg.©¶)Ùöí}kœšËñ"›ê*÷()…Z~ˆPF±«o:/®ÌËܼØhQ0¼Œ¿V¡õÀ²Y³¶w)Q/0±¢b°jTßÓZÿçBÙE.ªŠ„V÷ò¢³xy@`Š=÷¨f×yÏögðl?Eöfð?SeÏ4Ïy^WÚ íŠ5^ ´:_HëžêpÙó O$@&q¥¶8¹R+1_Jý^hWTFƒgXæ^“\IYät%Ä-(Ð(i‰iUáàÂ^S8$/ L)/ÒܨÖP+1œ`m^B`¡I€H€Œ*µ­ ­^ …¼K†Œ¯²KH©r(Ób»²P8[ï@7íO¤%¯¢b›C7¢’ d Œ)µfÈÖòA$xoe¤ì§rÊp¸¬r AW­, Fÿ`Ûw9á@ÜæI†@ñœ9Eû²ôÉâÊÙe)˜+ ä>+E0“ÂNµr`,´ThS£çnˆõ/Ãq7ÏÔah8̤°’ë¢g€¬{M–ÚfÔ¡0-J©[l{]MÞí5ÀX è„@F”Z³ÊA|RÆÐrÈA'¤»p6Üp3¦ŽñU#ºOoÈï;.W1µÚL€ÌyrQÛ–JûäÕB‹/¾ï>91Ë@™I€H dD©5ËvÁÚø0'…¥wK ?ÃÑðL/%Æ&O‡€’úWxfŸäÊ&éñ®ž]ö%{C¦¥—c“ @þH[©5+À€uhåŠüŘ¹’Ç9‚gœkæ’eJ$qÅ¡òÓP÷GYRÞ–ñÄó1AKþÑôÔÛåÇçcñYf H—@ÚJ­Ù)Ìl¬`Ö¡MWÆÂp4¼6e­€9(Xe88C€– @ªÒVj±.íAÈ´>ÕŒ¾s`jx®ìÈ©E¡ŠS²O2JD$@ÙO€Ã²ÿQB ƒçÚ¹sÚ^çàÃAŸ‡¼°Œ¯ ú®\RZºÍä“(‡%­Gµˆ•Bæp^㳬ß,Ÿ]¶ªäºèY±˜ó[È8Ö“x7±? .YfŸ5ñy@WdLOdz¹²BÛÍ?´\;[‹¥»åR8,3ÏuÃÇ ¯øEÁá,–BÁP¥`]R.2‘ŠC‘(êù5x–›ñLŽÃïv´§¶Ö-UT ž×š¸ÝkOÄCRé±ZÈ‹O!Ïû,¿Ñr{ÖL:¶­­ÚX4Šöæ\Çß‹Èó•‘ÐmÆ¿;G²¶¦ÛuÝòÿX(÷f´7£Q—ß‚Å;äóË Ž[ ·Sàöº"n¯Œa¢;,M¸nçßE[cÒJÖwÕÖÄåpw>¨µ8,¿Ã½ÝeYâ±sÝõ&m$ÐÛ°ë0Gïèd»|$”®C ûÊ{’/«Ë¡Ð†¡üþ¦o p¼°D%ö–/kµò&bê#ELÛHïÏÖšŽPÅ­³R?‡çÒ$&Ùo²¶ÆÄëv]G;‚JÚ¼/áCû­ÕÝ®û3>òWÊ~Ö Pü„ +&ç~ѤkÚˆ®X¦”mMWmpWmÍ¢ë§Ôõ÷ø>> µ´&™{[8¸pŽ‘‘ ôF´Ôæè]ubzŒi¨n.+û0Y`} B‘­®—Å•K„]\Œö‡Â\¹råM ë#^.#û xÄ-W\ÑœH/WÊ•‘Ò»”;ÊpI“ë^‚ËŠD¸ÄoÌUWK!k`ÕŸpÃïFs®œ˜±²¾W)3cs¼qy0¼¾Y‹w`q1V #^,¾ÁüòÆ3êÌ5^ºÕ1¥o—>9~ùìຸ[0¼Šúš)‹õIÈ‹M¡? gÀÚùO¦¸²òRñÞ–•Óü+\Ι*ÿ–«b§ûÖIËì²x¸O- †Ç¶8jÎ/Ã_üÀKn,F·ï¾¼ݹ«þ×p}«—eü XŽMXJÊ"¥õßîXX·ÞŽÇ“:àëïÿybæö´yóÂ;w6MUÊ9ié×®‚…¥AáK"a¥Þòœ@U4XÒï·¡¡0á†t¶.½¾4>Á¸U•”8ZˆÇw¤¹ŽéØ øØâ cÜQV£@žü‘’_bÈA›c#¬º”ÿDXÀv)×½9ÁCf?˲>o~wµ …Ö\ÏŸ>½ ÿV­äA‰½à÷y”Áüñø Ì›6­Ïu:õvž­‚ŸþØëïÓúQïµc>f½nÉÎSiOP×^ó¦…g£is¼nC£Q‡fãÃò·˜ôv?ªô0ôµÓë ßö¼ó¶&•º.,ß¿éVÍœ¹2Ôáë}]ÂmEY«6õ6ËTòOÖÖ¤ÐçC[“¸%ü%¤h©MŠ'{=ýÚÿV‹v¬-âÍ/AÊ¿w$i¡ð¹ xkAq3˜=‡öéfŒeƒB·wMLK¢+Ðs4 7®,£ûýσõºÇkq@ûj=מSÝ/3£lïs@Áta©i#G<ÆðAOö<‡º±}d(-¶}ñ¾q=‘o”fdã9´tŒå׸€C:íà|0ïå`Âúbþ6eЈ´'e)ûÀR»KZÖ+ä3ÇBeõy¥õê„è˜C¿7üÂ*}mo(G®•Á>«ßï»S8jRQ0:k€¿_eƒhÚâ:6,¬oŠáCîoŸVª×°¶vÔµ,‡Ò®¼~Öz¬00VÚ~Ҹäu։Ǽ%wd¸±84§¬¯pS³[÷kt5žŠImß4aºs ÷×Ö:ÑWÿf‡ðßw¬”ÎÉËÃe+»“F<Ì€Às¢Îí‡4ÎÀŽ\u;öjìÈ©±˜O{Wíè¨ÀÃÃoªuj.©u7?Vª˜2Üwì?jû¾ÓOÖ«³1!q)&CÞèýx‹§!õ,,öÑp«tUmlî(Ìú¿]v"}´O ]aléÀxW|ÂÃû›F{Ò&-_ÁТcÌæ1‰ç‡n¯ƒÕÚ òïÖ‘¬­ÉT]O*H–™Ì?Õ6¸S™µ|~çcŒðšÝC*: JÈeè)á‘«°ÌÌãXÚêX-ŽP,¡ïmijnP5ÓU­DËQ¦\f2ºÏ«°´ÔñîÖPhâõ &… oÒcÜI#±4—µŠóÇýx›vvm•J-Y¾C:Џ,<ë%ŒÓûÅ#Gƒ[ÿ¡ëb‰-»_°`Ù­‹܆÷0ã_õ>¿ÿÜ®”ëŽòjïåøt˾¥õ"Æþî€ ³0Dà¢*{æ»&¬™×'pðÐ%ë`å„§šO>€•öjŒ/þÙr;Øíq¡ñB¡¼®:쨵ºÆìÂb÷ëcZOl/S²k£4`\È\¤q/º’7_ý^²ðôËÊåRÅö™„Ù¾ôÆ:ë >uó©Ý¿Ö8u» KâE-K^z¯wÞxFAª•µnd»ÖÎó¨ wŒ;qdÜRjÂa‚û1îõL–zÊ·¾ï‘N{âM ]æåP¢OªuŸÜÖâ¶¼‰¡ICžEÞ0ÉΓµ5™ªëÉòOÆ2“ù§Úw&3&ÚšÞ¶q¸÷¢½i3a¯³8t'¼$€îíÇÌ_^¾‡ ½?Lͺ"‘áÆòÑ™XS£ÑÃM¸Îü“¹›¡“ËËMÆëg–æ1«xÝçWÚ ™õ×éþše‰ÐPÇ'w™a¦œÉÒÄÊÖI/ÉBuíךΜ£Ílô®CwÂLô+ŽFìØ7»]ae>Óüe·”¹%ÝþÔ}SBó« x‚ñÔC :|—æÇ§H ­™L4ÔŒ‘æNEéƒ7cTÕLz̨R»{çâô%d dÉg€²›@Ú–ZŸå3[ˆZªQqövîµáhx®HŽI@ðcg:³p£ÓÐfýá˰—'¬¤Ä?‰ÿž5¥{y‘Y< È>fù—â`ä¡ì“,÷$2;[N'÷JC‰{;L{“›þÜÛËÉò‘ d?´-µñ"jy3vZ:›œžýEÎ^ ?ÃƯníà•½%¡dùB˧”b¼æº|)/ËI$@$½Ò^ÒË [5öÅÎ6ë1kw‡ ùz•]²Ï~ÚÙ‹ ;$3kLjgë3˜(2h˜ÿ¨ãÚo}™RR    ÈN±ÔîVÀŠPÄQÂÙz¬Q–³Y楊ó·8?!ЍÐfž1S$l$`ê~QYäén@e£L$@$ðiȨòYŠ\©•^€m[ï!ãi±íúvÆw2Bÿûµ_…m(v‹!H ; åÌlšÒe—TÅsæ Ä–´¿ÃÄÐ dÀ]e—½œ]R È-imOؾ¨/=¾öÙ1gœµôT©Îó­³^鉵›Ú‡ãu+øZÕx®þ í5Thùdä:A#OzhÌégÎøê·ÏúÏË[ûV®—§§äŸ*«[ܰ,Ú,á5¹jvð¯=•Ó% |!QKmfCŸƒój(·Gc‰š‡ñ·Â*´à:¶B˜uh[—íÒ¿0“ÂðB{¬Š°Í0–GâA¹M`B0|¦Òr zŽG½_ƒM1VXJ?º44kÚæý1!½Xiu5ÚÆÓУUã ˆ/³ƒÏæ=  è¥ÖÈe&Õ85—cc†©B‹£ ¼)ÌêÿPK]‹LÍöšyuÀ"3 ¾ó6ªÀ ÍRl6«  _Ì1´yõ(ôúÂÚö:­ûd žù™¦î›C{-Ðß:cIié¶€IöG¸NË­h#öÙfÛL:|pà’g̨ë-á±üÙ7¡ì?oXÒZX8¸ßï\}õ®DùøK$@$Lì(Ö¡»µùðœ?1T1:¦bßÁùü™-uÂ_¾fÛÛ— Ènòcc…eáY/å–7?Øö·]”t‰ù›œûEW´œ©…<® oß¼^¥2RöÔÄÊO\v}é¿`ņÎσH€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H€H þ¹°À™R}®»IEND®B`‚neutron-12.1.1/doc/source/install/figures/hwreqs.png0000664000175000017500000026132213553660046022524 0ustar zuulzuul00000000000000‰PNG  IHDRÊvFž¤NsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì]€EÖ®ž°‘œsT’L¨¨ g¾óôôTs8쬎ì,˜Î¬Àb8õü½Ã|FTQ ˜HJÜ%g6ÎL÷ÿ½ž­¡gvfvRÏÌÙî®®ðê{õª^U¿ª‚#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ@ƒ( †àŒ#À„AàçÃí*Ü•CìvË–§œ“W„ ÆÞŒ#À0Œ@£DÀÖ(©f¢&€€Óù™m³XÔË&,µO8ïÜ©H2¬âöºŸ.q¬6•ïª<ÕÇhB{§Ö£¾Œ|ÿšÊ¼31¯¦Níè©UZi³ei{Ÿ4i›¢(šÑ¿±Þß༿G§v¢"²_˜éºcec-ÓÍ0Œ@4°¢ J†0-bQÛ³Ò«ˆåH~P¤,6‰E=¼nÏïP¶~G¸"…åwéC ºR}DhÚ%F ñˆóÖÆ0é¾Öë¾ZÓD¡PÜ@÷ÄÆD;ÓÊ0Œ@¬XbÀáF€`@@Qæ EyÖ÷¯ E,[oMSûÜ•ËhV¶2÷µ¢ÌG¹Öb@07s‰Ì\Êè«M?^Wèê¹T2eŒ# àe‰_F€HŠ2sVqáŒÉ]_[­Ûýo˜ªœXív?‡wcïËýÌ)…¤ ÷i,ôf5êéBh‡ »5;ÓhczF ><£\öa=fδÇSˆÙ³g[oxüñ°x¤wñäo³ÊG¶àô‹—®HñžtÞYnÉ—RÌ.v:ggE ït>Ÿé}¨w™Â#mœ3óŒÏÁ÷‰`n†ñàNeÒ4M‰ E;+ƒHÏwÜKJ;R~Ç0æ!ÀÂg¶œ2#kÓpÃFYr©«(¢òDç´>°Q^M6Ê¥.G=åkœÓúy=ž¿#ÃÓ…¦ôìek¤»«Ç¾µ)¶»§OZLÌGñb!,ß•º '^S4m¸GóÞ‚åf§#n[E(——–8^ 878oUãÙû:ëñx ~[„¢}c±)÷j­ìUç ¯ÿCtÛ\¤ëB˜ó…b™RZ\ø¸p ]ÿCü ó9äñ@ÀK<\íp•*B;^S¬³Š'NïÍ,ŸÓ©YÊÝ%·¦s×auôü‚Æñy”驺ç¨. ýߺ²Åòçàe™ðÙ̺dYì?U|×/ÒŸ®œS ·ªôàÝ|(ƒ÷"‹ÝzÛ ç¤5&Ø‘"µsû¬<úÔн eùڢئÌ(žô=òûõfîÌbÇí2îÍÎGÚTzö-Âó&”q´ô7^'M=VÕ¼Ï"½gºŠn–ï ŠJ.ƒ Édø?ÿ'‚ý-6Ëù' 9ð·O~\AùL‡cd°f–ËÑO†sžŠý"%Û³M­ÖîÓ„rêIOÔ£_a ²0ÛÖòŽ'œ7î©›¹¿yBÞƒÅnES¾Rì–Ûf:'ÿ*i0^cÅ]—Mùõ÷jÔç P¾« 7#AKkäW†ü޷س‹f8oß"ó™è(>É+Ä¡‰cɼýaaÁ%|>'Ó=aSæz¹ÐÔkñ`Ô•<®EÚëˆÂb+--¾ë' ËŽ`ÌG€g”Íǘs`LGÀëñ¾¤/°Ò”\MÑtÅ™æ >Ý£y¾Ô * D¥nøµŽû{UïÇ{âþŒ.|‘°X–QpRЫÝ{¾bT€Î:ÊØËø}†{ ºøù2&(Y$©,-ÑÑŸüŽì2¡PœIï‘×åÁïiFÊÏEðïŸcmñ½|oVùHÉ,s—¼šî‡²Ò ÊË«(ÿ›PtºÆ'¡½™ÌÙ*öÿw’pòT(É“qÿ_¤ß2ñ.°uÇŠç ª»æ+ãì´ª(Cð®ul§ž»"V KégÑ„.oäAÛcBUŸÅ-x£|Iv}‘îõBñàNqØ1Œ€y˜ò‰Ñò[=½°°|†Ã¡'7ÁùPÍSõ<,6»5Ýé Ù^ÝA‘;Jë«^M™ ““ƒK ôYHzY«ˆizP0hÀ1÷§÷©ª6×J ÿªj1”Á^«åÔS ççœæTß >#zu¼Vâ@ƒ9ÔÓÏÁ+ ÚjŸåÛò/zÔy‹>¹nêÔöµ•êl¼?I©Ri–ûVIS¼¸ëñ5ÑŠñ$‹°ŽŸéšü¾LSŸáß[û5ò¼Ñ]v-ü¦w¥ÅŽ'qyÊõ7¸¡ˆ¬g–n¯§ÓZá½ømÏ·å 0îŽrÛƒæïÝU36â4@eÇ0)B€g”S4gÄC3R}1ó·,Ò3ŠsÃÅ'ÿ™Å“¿2*Éäçt^Q-,ÊËt™¨!t­ç4Ñ~›gþ9XI¾Æqÿ@¼»Jí¾[ë+¥Ii8ŠÚÕFZž²Šžîiç›à¿¹v.pN ÈÊùPª ø>@qÜfOýŽ”zÀla€‚eFù®u>ÐØßD¹ÙlV‡±|¤dabPW¨ƧÍú©Œý†ׂB×E˜|‘òCùîÄ`I×9OÕd àÝ«F%™ÞB £]3 ˆi,ë¶^(£Ð ƒfßCñç /ôvµÞI ¹ Ÿª+Ê1Ô¢X.¥$'Š9êÎî6­ì·J%™ÊD&)@ô]º èÓ´k¥’L~OMž¼X¿B÷˜½=T¿Ê?qà.£êWEyÚ¨$“ßwÞ¹¸¿N÷ª"†Ñ5Z§Õ”Äç•dò"ùœYâ˜_÷ž/Œ#"Ш°ct"€2:øÎ ü:ÆCcwK·Ïêâu ßb¹7@i« ¨ŠÚQPzÐF(¯gwe:¤,C×û¯|6^-ñ=k^õ$éSIIg”õX”sAV®E˜Eíùž®°KÐí4U‹5@Q6†1Þ'R>×}ÊGöŸŸMwNúØ.Ýw·ÞµïjqÛÿ–‡Î ~éYQµ‡®vÿJ¿ ×:˜"Ô¨Bû hÄ%°m…‰G€OOVÅBŸÜë9EX¾$O¯¢"_z<ÞãâyÊð©ºB™ýv†k²®¸ç™0æ0ûÑÑ „«øD÷R´°¹þ=èµÀì¶|ß+è]̸ã[…u¶ñYÞÛ¬â=ºG}ï+ý¢¹vÇüLƒÂ8|}¸‘XÑÄã0Œ#`lza¶œ2#-+gE¹˜/R‚´?km…:ÊXoÌhõÂ,r›2wYÅÁŒúìÐ.Ëbû>ÔhÁ}ðY¯”•¡Þ“ì ˜®ÿ¦P‚oÓTAŠ2}rÕÕâlRì‹xÿ‰É“·bÌDŽ"ú±ÂÂÍ´c@õ–ÝÇ‚ÖíÝHI ²êHvù0ýP8Mt-¾GÝÃ÷§Üã"ܼÀ1«jO…ýÙð:ò­¢e#®O¹†öÀVp`5ß:xú¤I>ÕºH·>t¨^Í{-h¹¶~âªN+Ò<@¾‹†?Èx½ Ÿª+ìªCÖ'=ÿ1GùËC•C Ì,`±,”åÍ9»*Ä> áßi$^Üùg娌Ïò^õdm¢†Hòç'ßEº:'z`‹~¬“ÞÓTí1Ô‹[`ª1+7Oy–d$R\~Ç0æ Àв9¸rªŒ@ÊðíJ±{zu¥÷|dšeŠ˜ºJÅ6h5>]7üöR™JÜY^Ì÷v×U`E„í ¡ü…|×ÕÒí‹2uC54…Èž™fŸ¡PÓ+aWÅ›¾Ì,ocÖˆªjq&žŸ©Ù¶÷(9 ùm ï #wÝH~ù ÷®Ë£?>ÉËOÞ2[y­žÕ &&…GS,7ÌríßGÊÎû(ÿiÞ õF$|¯Lœ®_üºº+h±c$ƒIgeœññïwÂfÃ+ý¢áÅ¢lòBÕÇYÙëõ³!ê$`‚úÚ%Š9Ê4’Ó*#½5¾‹wcmÝ“· R˜L7ÃU4o‚ó¾ÁŠÇ{ªÄ¥ß’Ê*í|™xEä*“J 7&3?N‹`"#ÀŠrd|ø-#ÑèÛlyJ°èLùÝ7‹íãÖQ´h÷®jL¥ìaƒJtÏ=Bs:ƒ<õGm]0³Õ&Ô[òSõYS¨?˜{5†!ûhœ>öbŸ²É{ßaî»ï±×s2”à%O¹k)¬]±¾U«©.(Ëgãñ¿}²Ðüff–JþV}“k˜BnÌç‘[oÅâFm=”ö±m-ùüÂ͆c1™Ò‡ò æ½6WÎLëŠ4… vH?<߃GùœJÌ")^ÜJ7™ïÉ›vËèf0L| RÙ±Ö£:“™§Å0‘`E92>ü–ÈhT·vN &V}c0±´à+Ø/êgUü@a1Õù§°‹Šê”ÛPiÚì>…ÊÑp*…± ÛưXÙö)ˆJÙ¶ÑÐåGÂÌágl™F‡lèÎÔòÙ¬_c†¶Úy´-—ÌÓ¬k¶­õä‡C`´3 îvÑ,ºÁ)_ÑC§v‚Á3â-Œ}§Pğž&†?¾]!”½`n» Î’0;3hc#ÏËcÞ0‰±ãÞpš „PL’hêéÒ@Hÿk§óÂÚ,›µÎƇõ°c”!ÀŠrÊ æŒä#`±‹Õ”*ÍÊ+{tøL#¦Å›ë¬;_@‘í5÷Ç“‚Ó™è˜:JßiÁþòyº³p)ÃM˜ke³Þâ}fUÀ»9¼E0ï¸fœµºÝ2d3ËG§´Á$áY(‹mj+½ÿºfÚ´¶2_y¥™nÚ†M>'r¥C`»¬ãˆC93ílµY'ŒjLÌßvµcêY¡ò¡ÃE®u>ÕB¾£mä€ïçø¢Ð³Ì½p²ô—× Ž©çÓ“åsˆëoºŸW;/øÝÕw»ÎCºIW”Syp¹‚ŸãÁ=88žu™ÅÂM²Íp$ðOîàY÷àQÕÁt‹Á䯡޳#À˜ƒÛ(›ƒ+§Ê¤ÎâØoË•ßC©ê®T¿.(r½ ¥´ ó¤‡hZí%°éÄ#ʬö{$Bm6å¯G›ƒ•xŰ9>é}ŽbZi7L^ÌŠ*3öÄði(8ñO»”ÞC™/×Å:é«2µd³Tʶß>™Âš]¾¬<Ë$w¥w$ã=û¼¿áêO`¦@æ m€co±n뉵8é¤Ði »Ò)“ŸÇ&× ¼GÏRn”(Ù,c·‹[}æÞ·qÿ9”æï1pذ»!Èá8Ãx˜Ý³oî} .npŒøª[|…ø^ðg”‘?°ý> JÕsx÷w­ï¬ŠSxµ÷±3É$,ë+óÅ0·h ;ê#°ôG™7ÄÛúIFã“jÌ#Ñ/î‘ÒlðEù/p¿ü½ÛöF]ûœì„£ä‹ÕZõ@œÎ¸‹??Çî0K g¤Tw…<Àá1'``T)lÊ£ æÁF iðŒrÒ ä„Ô#@æ›í\(HBùêÎ[²i@É;JÏè|qä­¶(^ÊôYK‘}Ò_;Ú¡|¹ÐyÄ@;XÖ‹ºÛÜXÚ»ÖË èÇÒSQì~ûdé§ïÅ,Ä;ô ¥®ª›­Ç|ùŽ®f—£=;ÀS€“Jý% ùœw“~”´¢-ÃÞÆõè6ÒË=pĤ²;_`H ÄíÆÙjìÿ;Ý¢ØFÖxw$Ü›±ZÒAŠ.Ôaû8åy-_ÝdÌo†Ó±Øf·ðñgˆÅ*΃}ùKÆðÆûYS ?-L Úâï"l¹ñO,ϼaúó»Û»ß‚w`wr]ª1oˆúxpo(ÍHïK§¾'ËdðõM\ˆŠ€Á¦zű û:Ô»·q{ É2øÿÕ FÇA±þÜjSN.uúLn"eÂïF i`0ËŽ`štìrµ·º¯°ÚÊf8oß’ì2Ñ>Çž{f‹¼uƓϒO¸ôÌ.å«›_TŠ~ªfÝÓº¥¶)ÔááèK¦?Ù„þëªþP’ó,¶åø!}7Ói{‘ò #Ë7‹òÞëÚÒ»îÚMaÉ<ŸøßÂûP/ Ÿö¨vW)=Zµ°¬Ju™3sÂ&ÜÃaÚ?É“ºµòÕêµæ´Ê]I‹ eýÝæ=]éŧÜãzýè° E%%¢G‡J Ü‘)Šï­)3ÊÅcAÎ,Hd/lvÿ!~/[ò,ïMŸ4ig|dr,F i pÍ´imÕJõ MÓ.ÁïTtxëP²«K]E¥º„,§©Fœók d’Œñ*(ºï0MóÜ‚~u®­…õ]îOèð}sD`¢Ó5RõˆGЗ£eEÑ®ÄW”O“EÒå E®›5U<¤(ÚÏøÔ|ãÌÇüdÍé1M‚B×ñø^ôÊrb·•;MU¹XNS…4çÓ˜H§Œ6fܘvF •à í(³cvyò½º´ÄñB2ó×í;’• ¯óÕÁo[§cXIN²œNSD€äC±w<šä_D!ùIE9YNS2çÑH—ŒvÒþ²)àÈe`ÌD`–«è“6-³ŽŠ˜« í_øZZÌüÐG'ÇÑg\MSÞ§N¦«ð"˜[Äb •"8F "€ÏFJ£ä¿˜s!6§›i†ÁrÚ+“œvR)£TØ‚»KNV½ÚÛYøÚôT‰cmÚ`F€€Óù™­Ü³À©)–Ÿgþ'Y$'eF™ Ydn!ì/g%9Yìátšº¼@nPÖ_ð›U'OI/:ËiÒ!å› ©’Q‚“:{Í«=ŽÛ2w¯Ž´ø#ÀD€Óy¢MŽd*É”mReÚÝB_¸›äRgAeåá Œ#`@€äÂxÉ‘¾[Œá]²nYN“…$§ÓH…Œ®åÞ…×ãóñ,LºÉ¬UüÍ‘\fF ^’¢(ÓpqÈ6Éñ²ã1Bèö#’'Sð`95VN´ù `ºŒ”ª þô£™®¢›²\RF sHXQ¦ÍÏ…&°O²òræ“)cºAžt¹J"É,§I““jÖ˜%£êçÔA˜Mî£mv³™ Ï$Úµæçã­M*aE™NÜ£ÍÏiŸäD‰áøŒ@sG€äˆä©î$ˤÁÁrš4(9¡fŽ€Y2ªÃêUO¥«’ƒ/KìF !pôÜ÷ž‰ %‚È +ÊH£7ö®ÛÌ›Ÿ'Ê ŽÏ¡Ë‘ïËÞIƃå4É€rrÍeTMsc üÖ ‡£¬y¢Ë¥f’‡6”Ú‰ØîK>Â{7‹Wæ&·œR3G2EòDr•4Çrš4(9!FsCÉ—Q‚+öŸÂ…~ìF A°Ûl»Ú#ÁdDŠ2´õ– b_¢„p|F€ð!™"y"¹Jšc9M”œ#€s ’/£ +#À$=H±u¢©&¬('J@ªãOpÞ×+×î­ytòä-X”ÁFÓp8Hâ:”äJMX^˜å*|ŒJʯi”¶~)°Aÿ)šªÞk¢Å¥®Â„m’êçÀ>f"pËÃçVì¨è–ß.¿ü‘[o­23/N;¹L(t½†ZúYÛ•3Š'}ŸÜÔ95³¨;œa1¥ßÍÖã§óŠêdçeÌCôìtd¼ÛÝ9³³Ê=Ë{ÚlÙÄí[œNEM6­œ^ô47™oŠrcêùšðþ ßËNÐÜîî•nR"K* H¾mÉQîH…=˜ï°‡ËkMpEé†c‡*Šú‰¿º‡òó¿LþMÁÝ®3qšÔÿô”-ÊóŠÿÎÇ'—€ÎÉ‹¥`fqaiðûxŸ±AŒz†bµøŽxÓàx©E`BIIW¥J½AÊ ûvTÝs²öm¯ ÞO8YéQlÿ\j)2?7R„Øjq:/¬Mfn踖`·„aX¶7Ûnô¤óÎs¸ 3gÚµu[j¡l,-q$Õ¬åŒý¿‡¨š7?™eºÆqÿ@¯æ~ ådÊ_g”8xg¥Þ_|aï\‰~o ¦À—iŠò‚ñ †ò®+mJ!ËEm2Ö+գΘG÷m1›C[Ðv¦ÿ„’|&ê˜Åí®劫þZEÖäé®;—ËMïceFrÝÏk–y³YbŠp˜Mt,é8Н@þ*„ì/h,ªÑQ¼ŽÆãs¤A#Ò±]<Ý·Ç’^7•ëÍÎGÚTº+NAy˜Q& 2Mˆ.«rǬ)(–B¹kûy :ÎS…v ¼`¢–ùn×^ïÉP@ØÖªuvŸ‡n¿½‚¨¾fÚ´¶ž}ÞÁn–¥hn}¬¿Ü,óŠW|=+ù"FÏ&­(«ÂëVtÆÿ4*É„QÐ}cÄ«®AqA±>íu?˜0ÿÏU+ÖŽEÆ£¹a>p#¶ñ¹ Ÿ~.PEí(4nW!š=Eî½ÜÍ6p*}Z¥OAåž Œh®‡ÀãŸxŸŒtaïfs.Ä“-MÆr4t¯Ïæíõ܇ï`£‘Wз×gf;fH¥¶Î¶,"máòÑ,âY¡*—£c'¹.G¸çÃ…•þ±”óº¢ûrkž‡è`º˜¿£˧šæEQн\4e5çÛ P³eßiPæNÀìPEvžåœ'&ß±Õ˜íÓÎÂŒÏt¹ æÞ~ ~ƒÇÊR‹ÐJf¸ŠæÉ°²Îâå§šÍþ°ð¸ƒ‹ç½ˆ7ßno{ƒ-<žšGÖq¨óÙ¸~m³[o~Ú9i¥#Ó ÿì|ËäšJõ Ô³ãQ«H ý$ÛÖêæ'œ7ÒÝ.'nÎEá$µg}¾B\ë|  òùùWÍ*q åïè wÅ£hì }$â-­ ;³n§ýQÜ õX=qvV ¶b‘Õf»}†sÒšºð‘.|F›§¢QŸÂ€ä'n¼±&Rz ¶ÎéÔ,eîp», óï¬v1Ù뻨q³Z,€a¿‹§,÷Þ;Ï úO„ŸmË:³ã´ÞbÄþT›ø‚Ö3Éî;ïÜ‹Ót˜§‡úÕ¦hë‘Н¶§¢žß†zq®`ö ñ–Q‚‹„YóÑ^My ¡÷Ù4qñÓ%ŽõÁaámj…nDY(•d S·í—tßP+M£)_öÃôÇ”×FoÙåø vLü¡h²®Ãº˜—³;µ|@ÊÚìÙ³­Ÿü´â:|³¾ïûLʱÂA_±©ƒúØÜL÷IèŸXæ H“]Ò…Òdz£Nž!ðA!;×òPC©ÂbÔýÔÙ¶3x ´ >ߪy¶|K3І4:Óça¨}Bó :î~¨üèìµàì©&SØr±¹,|áðg~»è§¿'1¤ƒ|NÑÖo}AUµÛh/<ï¤÷1ÒDQt”¦Øë¬M]mf.ÐÀ‰§ Š\ÿ” è¶eh“áB^±(úÌÛèÊúÀ-·#~F¥œ×9§¨U=_aæi,’¯ö5 ýª¦ê ôD[Vc¾OŠª+>”ýë‰É“”äP¹ÓÀ<žƒº4rV†0øi'¡×™‹NúJÇ_g…¸Dx< Бú± 2ÎJ»ÜíÙñ¾Û]ûÂ_ˆº¿ r†ŽX;Ããñ¼AJ ¥#ÓÀí5•Þ/PÏE¾k¿-êÜؼþ3–ÂþºG w¤g¿s×ÚÉùL~Äí­èWê8õv÷þT(Ë ^Íû=–à]îÏW=ž¯-tõ¤¸‘ò³´mi{qÃýÕ›÷4ø™=Zl)ßrwÉt”ä^Ðu ð£Áõ¯í‡VŸÒxËâtžè¶ÜóóÛçzÂyçð0;R™ù]”¨ê™¾Ê ň¥Nàä³[Ñ~€ú|äiêûÔ‡ñ¨Šaíàé”P(ÉÿC=¨X”»C)ÉD£–kyý0)—c脵Pt7ÔÇRœhË#e?\,D>ºq7ÊwÊûäìsÔÏ~š¦N©Ýº‡&åt7÷§wcõG6¼¿aߢ²R{¥z ô.LFÿÄ2¯CnúŸ&«(ïÛ^9•Ȇnis4±Ráq誢Ìà àÞ¥.Ç Š½cgÆà=»«ëw:š2 ‹Òþ†äž3]…ý„Eñ ‹¦ü8Wê¼kÝ,WÑñ”Õô °o gúÉ‘.ù£I8¿qŠÕ2¾»mT>vm8†|㢉"FpÊ>÷TN4@ou³ê‰Ù¬³mö¬>˜€Â©ÜXàxàÀÀè¡i üdÉÃB½vàA‡}žÊû‚CŸc)g­[¬ZBÉšŸÝ¹U_ШծiLOÞÇ^V“¯f"€Î¦NQÖ~n(š™…–YBá,Š6ŸÒ†ƒçÃaÕ÷ Óy躩SÛÓ¡:™ý‹ÚúQý°åbý½&`¤mµÙlá¸Å.F’?²QL °……ŒÐÌÛ/^˜>Ú®)˜É[à?l£gÚ¹zz1üA~N˜ÕEùN¶rÖm‚ó¡h'|í‡Åz$ÚŸSgæh1+.Z¹ÍßGÊÖ]e·+ËuzE›ts ¡],ØbF®?èÓ%šÅrh™mkÝÊòÃÀ¤»1‡DËRê¼m›ÜùÐ10æóàûÈ`öø&,öz¿Ùø­†2{Úø÷l6«4í™@,u‚ÂBþ¦PB`ÒE’«yy–.¢…õñPÐÝ^ÍC6úy‡¿ÀMVQÆHuHX£ ³ER8(`/K»e2·@:zC€Š~Ip:hæA!|‘ˆL²„í?z,ž Ï/Áá#=#­gK§¾G3*”…‡¦HyøÒTþLWt¦Oë³7¸¯³…ðjV˜sëÊ…‘.mò]è«–Cþva¿³JËU×8]G…s9Oó¥£ýŸüÔ5ÃéX ÌžNlÌe NƒŸ“ê“O6aƒÚPê^O ™i´@=*ƒRI¬îfº&c¶I™ºÕ¦¶B=]úË+°%úŒ$<4{G˜æ ÂAÁ~XšYÌt:¾…À¯ÕýU­þŒ­ÍR"m§Ÿ*q¬EØQXU¨ô5#¹Î]})Ä(Ó—³Š'}ãOܦü[¿×}ðì÷sãî´#ŸA©ÿ7°Éñº½O† *bÁt¢·ŠØ4kJ¡>+I&(l;íö€<’T–€4ù!F´S0€ù;~X£õAͯ@·2ÇÖ._c»Xê„êuŸŠ:‘¡Z…ðU™*Ù—Þu×nùLWšÞi_ÙVxT˜#‰÷@†ß0† ußÝ>àbÄý'd5ÈëlÍ­}‹AÛ+´Ík¨ðÁ~±”Ç7\ŸWꜼ̸ÅÕÖú#=ž&ºÑv´» ââK­âÍi›ý«?M‹ø…îѱûÛ™dõO,ó~”ëÝLtºFÒBÏz/bô°Å¾ÑG%ÜBÄ¢Ò~ Q‚ºÅE=ôWVe1ˆÅ’õ‹×[M¼7 BжN+as[Y×Ôî‚^ˆïN_lÚDØzï#Ý[m_‡X(šÂfãÛ"j«.¨X,ó:ÿ, Ó*$þ÷ N ˜¶à÷សvÝù>{ýæ+wyÜÊt ŽÀg)úììw±”_¢a#¹¡3E¶Ø, ý‰Ð³PVÁŽ·¬Æ4ùÞ$m *ZwÈhƒ² “ÇH  Èré£LAç£æ¦Ôbµý.ýhÀ‹º¾yv±(ÊjéOW´ë‘zoÔÿ,£?Ýç‹Ü5F?ØI-£º‹8½þI¹W´þ$| ãÐêW2´ýÒRO.#å››k¹­ªR;é†í1Ï1a›®€(±`Ké‘a/jL„ÛW;Š×€ðý³úI.‹1?¾ ^.Ç•¹ªðæy¼ÚH˜õ]†YåDZˆ–¾ à fhKÀŠþz*šòSèÔ|+«ª´Ù˜üéIÜ™.dzoÃ<Ôõ··]ï¼ÿa·×} âODsp±âqÇ¢¾#ëì•ÃĆ<ÅÙ~„êóhòkî+.„,ФY(ïÝ<žyû3¯°@‰®†<,‚<‡¯ÚçãÝKdŠ{ *ÈY ¿fôO,ó„l SÝb*lÝÉsLà›Øžl±o<¡­6íØÏ¡WÓº’}±q1@p)Úïh©”‹Ýøº#,ÖlÅ¿’–Âå «gŸA³–‹„W­þHÍß¡ùýâ¼ñŠ«ŒQã¦É˜Hнm£'j¼o¦[³ÑŠTÈ èüu§Z•%ÒO^ƒi“þQ]­‹¡ý Üá/]y=æÊ13°ßÅRÎlQ³¿¾Ú4ÝŽ{JÞÊý÷0‹³¬Æ4øÞ$4åtaCñ;°¡Ð)‘\"¨KŸ—¾ÀŽ×zJ®G´Þ£Ç ú£Zµª ¯°øàô²=ƒ¦ýõ0(€|ôØ-ùÂmºÉ7a¯z‡ Kƒñ©1””M£_C÷4«…»Ä>© õÑëî}ú#9«.ãÆ„­†…>$wºg€ìÁ'©e©Ë·¹\'¥ Ve›ü¢‚ôVLt¹>S«”Õh‡+pN2Ó9yÿl§!ØêDŸ1;Ц’óßB¹mícPw‚†ã ŠJþª›çùCD¾©Ûüöë ]O¢[ÿi¨Vz/B¬‘bÆXR¡ú¼O~\1Ï«ô/(ŠøÓäÓq¿6Ê/QÄÝ­vê¢j³)wx=Ú; ñE(ÍwBIî…V£%ª×fOz»Ô5Ù”þ‰eÞϾý7ŠÖ`[½?pø»¤$>ùô½é"º­*eÛQYÛïÞUC6Caô‘©:Åöìýpõ+‹•¢’ži©Ü¸ó…îgâ3hzÚyÝ>Ø«m„°w…[hŸ©ˆ‰å ̰'같Âu pœIš†t±–ôïý­Õ*AŸ¢ýŸû°8¤»®ÂÔ%œŽ²Ê2ñµåkÌŸ‡ßåœ3ï(Wšf´›‰¾z< aÌðö£ªdÑ´€Af@ ,^wODß&“@^TçH1_+ý0ðóêÕYÑ:H?= L9 ÕÜø*ä=lW Ã¥Á·vž7… £'vÖ™^æq]6p¸×»Û­aà3ùõ¥ ¶Š²NŸÙ¯ÃÀH ”ûîI¿—eñ'ÞÄoÀû;Ì(¢êéˆÎVдÆ×ÄCq ©(‹êä`5qµÖ7³Ü0ág(0îÃæDsP×§caöâ§œ“i`µ#(†I­õºÞ—¤z ÅPžzq ¾-é·_œZ,åDK´¨.~ݧ,¬ô•ãÒàt‘gÊËZö¨€­ÃS°;Ü„úÐIx¶¼¬¯þ6„"'Ú.¼ì6fØ/êÈ8Z$ƒÑ©m¸‹t`œ=Oú'óŠ=دééôàdOývÄÒÎ tz}²ô£m¤°=ÛDù,¯øä¬·Pú±Þ¨±RöÏÐÆ¸É@Ú:KÆ‘×xdS_0¬Ø®FXk¤Ý ÙÉ‘éÑ5l­VŸÜ!Á˜Õë-Ó¡Ã1 $û&¤§ e‘Ió5v¨î(î­·ÒŶX•¥áR‰¥NX„U7ÃAÿvêDg‰¾» ¥K& ú"º L°è}íí­Xă¨ç-Üïl’• `þGÚéâçý>“È:_j l§Ð#dËd`в¥<2­× K'ò'YjaÉýZ†ñ(îË彜QÞ½×s-°>ë›nŸårLÀ µE¤$Ëpþ« ý˼ݤÞ4ÙeB)«s«§ª·ìÁêxm´æUÞÆläoð†]£ÈCƒ?P¸«i(x~"מ_ˆÃŽGØKaË×$ߢCŽçã {³mv…‹ÇYË›ètÏÂ(ºøjGÉÁµ¶Øé¤Ñtà šp¸ì…kA‹6¶zËîrŒÎÿ ­y7žÐÜ[OÃ^’mä"¿xÊ.ö‚½Au{OF’&–rbBâ>LáC:Çg­NP ÊQŽó}Ê@`Êé*k üŒÍ O¼Ûu-ì&ÿó±Ï½æj‡ë;tv[¶;¶WfÙg¹÷³i1”±kÀóRÅ­~‰po`(ÜçáB g™éºcep‰>S}B‡<+í å˜!‹üºBÙ]ŸÝ±¥þ©•òÀQÛ"ܽ¸;íË/P–y6œ‡ziE<|%FKSçfOúô¯EøÞøôü!Âãô4mfŸ¢2øìùÅ](ëg¨Û ÿ¨5Ì«Çc@ñ?$sD Q†RÐQOqÛéNÇ"Ð>´v e!Ú±7˜Ï$ßçtlŸ'ËiVYdú|m ÔD?6u6[¸· >Ù)c¥áÌ.è},uÂWŸ‹aw,.T=*ê„‹¶6Ìûxéò‘Ó«Hn¥YÏõèèPÖo9ñF”{ÊÆûz“'²tyµÛ}9¾ö’bÿ;$ ç,§]¤`¥¬ÊµæÍ¦päÂõ±O;oKJûáíÙæм4wÚç©x ý&&δ±øD§ŠîÎþúYG4©^ñ2h_…11™¦ìDÛñ±UØÞ•Çn›Õ?±ÌúÉuMvF™`¢]ºÛ OÆÈ“>g­$åú(À§ ¢=xý³Ä:oÙeɧ¢%y 0ánFgw†ŸÚíÖa{¯˜9c̓Ùò òÇhX½-Ày¹ökC ™A5„E€:Þ<(ï×`ÏÇ»p??tÌ£ñ%-ù®îÀW¨”c)'0,/ý´ Œh¾¼\eË: ¼£ß¥«¬~ø&,3¦8Þ´Û¬CÑy|9ƒ¸i؇UƒÍ!>§â°aÕÖÈÈàù,ìË} ‚ýåì ¾ ¦36·àœÈpɼBV=¨WcP§ ¿©í@ç¼|‚Üm…ò£N íÈu¨“d4ô]‰xk»åÐø]=š,⨻_Ó@áoÆ{ÿgëÒbÇ$lw)ã¨ÛÇ¢S¾òyÂä ¬ßÔK+J[ ëd]^B„[[¾«(ï lÙ‡vŒö·(âÓòôà¤Í*Kp>üliŠ:v êÑpðkøÿ„¹bÌaB*¥ÆTb©9Z_†´ÔãkÚ%¨Bý·AnôEkÆtå}Ý®†<í¥þÆ ä;ã ñ`Wø4¥dá\”åtÜÛ‘ö‹YMê7døH}l,å‘é_‰f´S þšŽÝSPÖŽ‹ )~…âaˆ¼·â}>µ?÷ãç! ð’[Ü™Ù?±Ìs0±gð01‡ÓólólèÚÁ6j}¤YS40ÊõŽ’^î^Ë[Á$Z6úl„qÏìN­7;ÛhÒ5ƒ&'YØæqõVlYîö¢ï– =¢!+éab)'}–³µ²o—{®F"&ËŽ^3dÊŒ4ÃÑ?VµwÓ²¼5OMž¼=R|:Ú›UefoôHé„{ç[¾EpµhŸŸGu릒’ÎUVkuð–WÆ4¨~•‹û{d‹¼]Æ“ûŒaŒ÷X\Õ]`mí çí[ŒþòžÌQ”}–N–|ﶆVöË8‰^£Å–VñWUV¶›áp”E“g:Ê ]‰„1KžÈ”…è‚r÷i"ô%+n´u‚ò£:­zlÖn¢ß¦d÷%$ƒØèß6½°°I蟡]C}l,å ƒt€VEN­7T{€/E—©ªúh|_¡ï¦ 6êÓ®s”ô€¦M§ÉžAÿ ðï1¦Ÿ®þ)Z<«Ìc6“pNô(ëf¥(+&ß3™Š€°if*~‰ÐeT”{§üˆ Ɉã6jÌ’'t캂ŒŽ]W˜5HÍx˜L½G3ߊÅ~hiñ]ÛæÁ´êf|½}‡Ü<ËUøX3„§Ñ¹IÛ(7Z®0áŒ#À0ÍÅw”zó+xÓ(±",«±# ”=÷O¼»ä1›ª®ð*¶v8ûz ÖdÁtc;Îx»i”¶ù”‚åæÃk.)#À0Œ#À˜„@¶­åä÷ž>° 9ÝëUOóR>Ø: vÜ*Ì1>ÂË“p2蓲çdMB€e“€ådF ñ!0sÂÏÄ¢içå'i[ƒí"Ø1Œ#uëÆ“½¶V¥ôÇžË-‹¶9+˲֬uQÆB€å„àãÈŒ#Д¨[(ô•I_Ò” Çea” P·Ð5ªÅ®)!¨™f2ÑééõZ5Ú(XQN=ŽË0Œ#À0Œ#q¨n1UÁÉ®pc!ŽåDÐ㸌#À0Œ@òXœ¼¤8%F ™# hIÑq“’H3gŸ`F€HœG@‡c±c B IŸÌ—A83)Œ#À0Œ#À0 V”Ø\F€`F€`Ô ÀŠrjpæ\F€`F€`l£ÜÈÆä2Œ#À4M&:Šõ£«g¸Šô£¬›f)¹TŒ@ŠДåÉÈ)Se…¡9yõ=5¯¿8ÐGwt•÷u^}aþùØ#yÖØøKåb^7>^3Ï ÏTMqÔ c¢Š2ó¼‘ð¼Žßtaž%™g¥%޾qߦSQ¦JA¦ôS.»yÒp[–}¬b±ô¥«E(-â.U#¨)b¯PµP‘×z=žž¨ø[Çg¤Ò¬óïŠÛŠŽ°Úlc!⽉oŠ&Z6Røã&[Ú>⛦ªë<µî^|tÚ’:žï2•±”—y]‡V#â5·¯A5¼Q´¯Š–ˆ9$Ë)ËiP­o|™(§éP”¥rl5êŒüþG ½6'7çzUպЩX-órÔ6­ò”»=‘£ñÕP\ív«»öTj{+«-ŠÕ2¥ ÈµÉãö>Z[¶÷É—^z¨ AHéJ·³\zém¹YÝ[^o³[of¾òÍj·;‰oÕUÕOþ¾èû§,x¯ “ s&ð/–úüBË(£Êkn_ƒx&¼ËàöU’Ë•å4-#¯YNƒÀÉðG#ï2ENS©(Ë+ødûóõÿÛªMÛ§UMë:¤_wmÄAýÄ!tWòr²é}suúà ²ºFü´ªL|óË^µá¾ü>mo¼üvÇÕÿzÐ5À¤k†Rçè—““;Ë«ªÝ˜oþjŠo®ƒF}]ÏÃ]ûÊ“ÿü!=øÑAé⟟Ø(n˜×áAÊT^sûžgòM(ÞeJû*iŒåÊr­P¼Î„6™å4<Ïä›P¼K«œ&¬”?þ¤Ë©tKæö/º†qT9(/;~Y—Þz×Íy-ZÍèÞ¹mþÕ玶œvì¡JNm…Ý–J½= ¥àM8#î§ ìÓE¬Ù¸-¯¢Úý—¡£NØ÷ý‚ytr“´…Mµ:ÿ®œä¼Åf³?×½Sæ[äƒù¶vãö¼¯¸è GV/ýj™cH‘QÊ”L+ªk i2¯£@4Y¼Ž"«h‚pû Jua‚yOûƒ<Å@™1¤Ër²Á¼Ž·MŽ"«h‚°œFƒR]˜`ÞÅ#§®‘C;¥Ûwóç–Çu½ ºæ^Ï7¹ÆÊ‘sÉM·ß’“›ï:bp_县·Ø«Krskb©>w^>ÞJxY,Öÿ~×Ý· ˆ4è \SátþQ¾”?ó-:È|£úNõ1sð£Áb*ùÁ¾PÌëXЪ ›f^sûÏd#ïÒÔ¾JRb¹²œÆ‚V]X#¯ÓÐ&³œÆÁ3ÅÈ»XäTu‹©Šê&Ó‰÷j¶¢P9ι¢à4Ì$ßeK»êÜ”,{ÂÚñ–»QÅ#œ/ÂÍjµßwÙ­“NER¡léü£ü(_æ[lÕÆÈ7ª÷Tÿ‘B¦*ËÌëØØ:M¼æö5€ ñ=y—âö5ÁôÅ~áËi8d¢ð7ò:…m2Ëi¼i(ˆ‘wQË©¢Ùýtf+Ê”>™Ý«×€¶ºõx¤Gç¶ÚegŽ¢ŠÃ.F·îÚj¹yù¥cÆ\è¦óò¡ü˜o12ËœøÖ­cAõŸä¯²ñ#¹0›*¼µ0¯ĨÁ)æ5·¯ r$úih_ëWê*ºƒ~õ^ì÷`9ÝEÜw,§qC—öˆéS3;jJ›f=éSsîQ§Ÿ^ÃÌÎ=ÒJ#v±#@¸]<îH+-€ì~øÀë‘iuþQ>”ó-v~ÉÄ·?Ÿz”…ê?Éüsñ“&fñOfÍ•y JQ„I!¯už$n_£àK4ARܾFCRp–Ó`Dâ|f9¸ ˆ–95³“¦Ycš5£OÍù­Û·ûûÁôÐÈÖ„]ü~„cvNÎ H…ð5kv^çåÃ|‹Ÿ_2¦äÉüèkÉ…™ü“YGse^GƒR”aRÄkn_£äG,Á$ïRоÆB– Ër*‘HÂUòÚä6™å4 ¼ NBò.Urj–¢L•CÎ&çwêYG@Ÿë€-àÈŸ]‚Žš¦u¾àêF )Â9Ù¸êü£ô)æ[‚ «‹îÃQéà“‡Œ±Uf^'‡½©˜ÌkgÈf“¹} @>ñ‡´¯ñÉrj Äa9m  ~=tÎ@IDATJ95KQ¦tIËÂ/§c÷£é0쓜Á°‡&Íãõ ¯7³Î‰  ϼ–-OÕf˜_èü£ôÓÍ7‡¶ŽÎaogQSKÛg¦“|#y…4£LòaÿlÔ.cxMÇ*oìYpèMÔ…MU@“yöö•pOV»XU]›*¶D•ä‰íkX:&:ŠO¢_ˆ%§!è‹Ê+VùŽ*ÑI^›Ô&§]NCACí%ÉoC®¦Ö‘m«¤[ò.¢œjÊrA¿}ú5ÃÑè—ÒÖe[VV–¹Ù!ÿ´»_ÿ(ϼ5_ypqÑØ‘!éÙ°y‡øÏœ¯Åﶈì,Cé).=ã‘e7 ²d„ôŽ‚N0¬©®îD;dÀø=}ü³XzS>é8æËWŠý,¶ìØ#zvn/Î=Tà€“%ú~Ù:ñÎüïŦí»tÁîÔ®•säAâøaźÛÅÔçÞÑãY, pËE:ÝÀËc=§Òé|ƒì‚< _©(›Á¿XŠ•v^±ñÈuº®gþ'¬‹(ºúìXÊlzX“yíãYÚWê`_zo¡Xºrp{T1°wqÙøQ¢E­OÞmÙ±W¼úÉ×bÅÚMúà¶e~޳2쫯'RøÔëbû®½ú=aIûÊ_4îHÅÌÑggHw涯a)S5ÅQ÷òÓ @!§A4é ×Û󖈿üIÜvÙi„ƒƒèÏñÈwÈ„’ìÙTå4LkÊ·Šûÿõ¾8óøÃÅ飫¬Öí,øA|¹ôw±{_¥~¶EÎmÄegŒ]± =ãúÓä´´ÄAk‚vfh}$Ðr$¥´ÙíÛ´Î'ÿ´»9_ý,>Xøcƒ‡›lܶKÐN…r¼¯²Z<þÊÇb:…ã†H{ˆómÙ¾kwWÜÊIÂ7Šq"ÅŽèüüS,JWÊ'bh^VÕÔŠW?þFœuÂábð~cîýùž‚úŠòWKW‰ÞY +Å—Ÿ5JØ­V±rýfÑ·[ÇÊ®úÓ ¢C›ølCZ_‹A}»a Ô/ L*H6n¶q¤(“|$›±#í¼–ÄÆ#oïÍÿA쩨m[æÉdô+ÕŸ.: _it&ñÚÏ3-åíë÷ËÖŠo]-W%r³³Äƒ/¾/æ}û›ŽWº¹‹õï&º´o-½®ÔO}î¢cÛ–bâù'é×u›vè_Œ=ì@qâˆ!ú`ùÍÏ–ˆÿ}þ¸æ‚“AL»7±}L³¢…joý«Öoç¢íÜ·«Øµ·RüQ¶Mtjß* BÆô§æéAå %”â| tI §Ÿ3>ù9v; {Ú]ù–â–KƉö­[D¤ö/â/§­7ö4JîÛ­ƒXµnSÄ8©|IxZ… A'›:ÿ(ýtðš”ÛéÒªšú“æ°Ÿ¯ÏýVôêÒ^ç]iÔK3É=»´ `Í2÷ò|òÈÁ¢x¿_ ÒáO’äM­•”‘dó/–¢¥•×’ÐXåmݦíâËŸ~Çz€L­…ùͼ% mó§ÖyºR޾y¹Y¢=4 ܺEž.«FŒ¾ûmµ6Øè/ïßŧò<³Çƒ1`íЦ¥6¨·‰6×è(}lK)† î-†ì…AîVãkSïMn_ã¡=#äÔH8™ÌäBéºæ‚Þ!ïc•äÙå4Tï-ø_gÛbbºŸúnÙšâçUeâÔcÕûÐŽm[ Z47îèƒõ¯vƙԟš¨ù‹L m²)Är¬7äÐg2BI¦‚^qöñzy½jô¶¯4r^SŒaƒûèq3æWj@%æÉšQöñ/M|k‘—£+´³Þü\‰ÎsÌ0°½Z=Ø·áÓ,ÍöŸ4bp½wÁËñ‰—Ì8¨³­†íÕÈCúIÙsïŽ:<3Ì.$Í訕8Kïd\õ4ëÒOFz1§q,°^ðÃJñé7¿éêÌ<»{*u¯}ÈEˆï~[+^™³H¿§Ùå.£ßÓŸ9_.Õï÷UÖè&è¤ýÍ»‘&fð/ªÓÎë`b’·`VÑ®U¾ Yª·ñI^:ª+dV#ÝuÓ^Ôo¯¿øqpÿúf;2\ ®É浬3tMiûšŸ›-ŽÄ óÍO wMŒy>#¼ óÏöCùÔìOôûc`>qÙxŸ"-_îÜS!Žª¨’ù¤'^“¯DáßÏ$³2ý™Ö’¬.Û óš*Ñ©]Ë”óÐÄöÕ_Þn2NNc = hCò8µMFN°Ñâ½Þ]sŠáúW ]m¼Rø]Ë6˜mî\gfñ¯ÿ}!~ù£\Ojüq‡‰†ï¨fRIN':]#½^«6«xÒ7FLb½7CQ–Ó%NWèb¥-¥áÿ‹…{ŸÁÖŽÜ…côÏR¾Oûd£sçåg«•ÚªŒsF¼“A\²Ó‹™&êHiÁÁ8|: 35¬Óž{WL¹öÜ€ÙÁÖ-èÜ!*ëVÍ÷ìÒVo Án¹¢ª& ßÿ𙔭͘}¤õ#†ôÑ«^x_¸®=OG¦ésI絑gu sêÚט•šS·p‹0}_}p ‘8ÌHñÀMé0?5{.ÓÁB¿®Ââp)štòI³ýÔoÚ¶[Ì_UÃä}):çÄáXìçÖ×(üó¥9âá\Üàú’$óÚˆw’“Ž:¹L !jb)àíþGìÅúr÷ßt¡n¢C÷ÁòM~™áš–œ1ýPŸ$ÒDßî±à}7v«ñŠŠÊ*}±™NI× ýieµgäwÌÚhê?˜|ªqî&•Yý©^‚2¢ºÅTEèÖûgÍdc¸š¡(SöDtHÂc -¥AiÑÈPØÉ‘£Ù r´ëÂB|öÿÇ¥§ê3Xºgæý1b,êÌH3jÚh&‰%§{¨çØŠÓlÍ.WUÓâ$¥•ׯ‡’7²S¿õ¯§êÁˆ×dGG+³û¿ŒQEñ¬·Å“w]Š]ir±ß§1cB †8ÉßdamLG¦’¢~Y£v’-äÆy0Ü¥â‚SFúñ¶Avór²üÏÁ„õOi¦ŠøH§l™>å’¢ÊegÙqÈRw|aZ!ʶìÔׄ g’Ÿk“²HvqÀÓþ‡TÓ±?ç8îh‘¦Ü>°EnŽžB(ùŽ#i3£HYJÖÆtdÚfÒ2í_~/îB;…sÆ›þ÷´à–¾®JSTzArI2ùËï°·‡T÷¥öµOÂOÆfXZcEKŠŽ›”Düè׿©Oxý0áCʱT‰ …h”ßüô[q!:ZH&+ÙAdÑØ Ð\:ÌN?4õý{t•˜)¤…ƒúvKÐ9ç¢ã%s £³a‡‹S°Ðଆo øˆƒúbÆYÁH¹ÊL¿§‘4)P¤Dÿ¸r}Ø­æêE4×ÃdþÅB|zx-)Œ$o´ÛŒt];¶†bÕ_>Š÷±@…”®;.;ÝïG³˜G éëÎ3xmFšáêß³“XüóïbÇî}Príâ·ÕÄ ÌÝ`l¿HŸpÃ9Z,ôõψé¯ÎÅÎ6CõÅE›°ËP°£MȶrÛ®}ºM sçv¡wÒŽ›œçÔËD©«èŽÈ´§ž¦Èô„~ÜOF’ïÐ)¤Í× ™2#ͨºñϧø×kP¤©Ï¾‹…³}Å©Çnwè=õEð/¼»Ûæ©O(Ñ×Z¹öÀ˜afõ§æË„ÙŠ²ÛFu? V W‰ÿ~8ÀŸQxy£*Gc$¶K‡Öbüq‡‹Ù/Ò;IšU¼xÜQè˜ëW×ÓÐé’reÛ˜ß3Œ@rh™Ÿ1k<’[°8RcE9Ð8 #À0Œ#l&:ŠO¢4g¸Š>MvÚœ#ÐìДåÉ(3+ÊÉ@‘Ó`F€`D@ÕG]¬('ˆ%GgJKÉ@åd Èi0Œ#À0‰" h¹Y¢ÅâøŒ@cF€…²1sigF€`F€0 f§(Ó–'´í»¦€ÇãÕWà7Òp)$t»¦ƒËiÓᥱ$,§F4÷=mÓHrÊ®>ÍÆôbþwË¿º~ó}»±¾Ý:Š ç[wìSŸ{GG†¶:êØ¦%ŽN>Ä¿ïͽ,hŸÐ±GìGNªùiÕzqïÄðÛ¬øóM\>õºØ¾k/NX8á+OßGõÏ㎠؎ê‡åkÅŒ×>£qýŧåÏgNå#ž¶ÈËÆ±º öY–Ž‘™óÕÏâØÃ—Ž?Vzó5p{<â¿s :™‘Žo׺…8òà~âìÑÃpøÏÿÉm´»IolûwáØ‘¢k‡6zøÇ_ùX”\¾h8ÒÝõÄlqÌ¡ê‡ZH?¾&–ÓäâÙRc9m \ÚO£ìɇtœh#O9ê qìáöÂÝso!¾ùåý$Tã!O²í5t€øëéûÓ;ÐOoC?}ÃÅcôÓüÌ€‡‰N×H¯×ªÍ*žþxÁ(èlŠòWKW‰W>\¤ïË{ÕŸF‹êšZ±º|«~ü±Äèª? òs³q"ßJ„ýJ ØKßCP¾çkê8Ê, ']ýÚ'‹Å½:û0DÍbœîE‡‘|ûÛj(MG(Ä´}•WÕp4îF1¸o7?ñßþº{¶îßgÙÿ‚oÒŠÍf<óæç89q»ø3==»´Á§µµi•'n¸èQ¶u§x‡Ë¼3ÿ{}Oß´Ι –ÓæS XN/¯¯<çx]ÇY´ôw*²X?Š^n©Jçü¸b­ÞŸR¿jT”©Ä¤ÑÄÔ%§É+ßÄrK'÷e²SÝb*4"qL"t6 Ó‹7?[¢Ÿ[~f†é˜jÚ4ûÌB]§v­t…ŠŽn¤JóݲµÆ×|ŸZaóó~Ý;ê§èÑßþú‡ŸŠ* v–®Ú Î=Üw>=f!ŽôÃp$çw¿­ñ{¯.Û&ªðI¿_÷~?¾É èˆøW¬gâ™Ã1H¥™áƒpý¤³Y¬úÉŠ#ê'N]a¢±Fümü(ñl”3À¥U.2 ü$lÙ¾[´†-:Ù%‡s{+«ÅsoÍk6n[°¶`üq‡˜Û„‹—þfòÚÌ´£‚®‰ËiT4—@,§qs:ír:ëyè‹jÌ%þtÒXçáû²º·¢Zü¶ºL\sÁɘU¶b²O¿ü®Ù“¥õxT˜jôÿ~¡n÷Ì!©o%sÈæàR¡(Ó¥ºÚí¦Š²UUŠÐmUw #)Á‘}ªeŠÉ $Ó­«Ñ©ªŠ‚¤¼(FDu­[SUm_€g’¼šZ‘.¾QQˆôþ ,À£OFÒ¶x÷ÞJÝö¸]ë–âÁÞµøŒD»™ ÕȃûûQ°Z-ú(y dº§ÏIÄãt*ÊÄ7ÇSå'2CnÒÍë0³iØÞMÓù8æÈƒÅ¡ömZæ GritÓ)¥©âu:ÛW»©Ê©Ùí«±®F{Ïr-Rчk.r: £I¹·d™6¨— Zヺ.ÞÿƒøpáR± ©÷`R¢ºÆ-r²}_ìH?:_ûjݘxZ·}í:¤Ñ[X åý ¦ð&zÇlEY×0k«kvîÚ]‘E¹}›|}— Ú¥ât®áÜY' ½º´¯÷ºMË<±ö“F÷û†­¢sûVF¯”ßïÚS¡y=ž­ff¬º½[ÒÅ7*W/,è:~Ø@`áÖwâˆ!½õYÇo`E3Ç#êë/>54ËlT”éå°Á½ÅË,Ò?ÓÈX.DðGLñ ñ­¶¦fW]¶#°ÓbÌ.ݼ&³²›[¶f£>˜1Ò&ï[æåŠ¿9J>ú¯mëæ?`ƒÞ±­O.iÇ2»J§œ¦€×io_‰ MUNÍn_ý8†–ÓÀŠ2hs‘Ó¡PlIÇYµ~‹øïG_‹ë±0šõ›}ºuð/z'¥xÎW?ÁVy¾PWÂH;f 3G²M&ûæ¿cq`º]*ô *£Y6ÊÔ€K%@«®¬Øº·ªF©¬®I9®vرž„…? ~X¡o FŸl7nÛ%h–1Gv;¿­Þ(>ûæWQ¶e§ø#®µ·‰ƒèMtSÂŽ„§»¦¦±NV~zš”~ºøf,ÈÉ#†ˆ¹9Øùâ[Ý{1¶°9ôÀ^ú¶a´uýNB˜_W—‹} ÝAý»cq¦[ßpø>ÆW)¿—|#y@æ~ù0ܧœ¦º¼µtóúð=u¥öÅwˆ¥°¡#{Wú¼·jýæ1¡E*´÷ãE?£_'hÁÑì¿ÑSzum0¾LæµQæÓÚ¾±kjrjbûj„-à~¢£ø$úxú2¢Mf9 Á™ð^)§œ2Bü¼ªLŸÞºs¾«Ô©ØWö¥çœ8»^tÕ̓‹6tp}g0 Ý×,°O§“mlD9Õ”å‚~ :3e£@ßBÕu+—ÿˆQŠò˜“7þ„¡°o=D%Ý=ýu1 {ìÒ6pѸ3ŽªïåúúÜ%¢xÖÛâó%Ëõ u¶-K—# ÏÍÖ- F¼“A’?=J?|“…!³‰óN¡ïñøÕO¿ Úòˆ ¥—f‹ÉÑl³ÑÑ@‰ûѧý{¦W°%ßH@£.¸úñ6Ò¢{Þéæ5Íôßô—q¢kǶ¢ôõybò“¯‰é¯ÎÅ Ô·È/d§^pÞ‰Ân³ Χâ¡?Ð÷ö¼é/cm)—g"¯ý|ðO¹£a±*ÉD$ʼn§ãNv ?ÂqÛÆòW6áK8KÌ“‘LKçå“ |KFÁÒ™†äÉèðËî“Í¿XŠ™‘¼¦Ùåxe¾ ÐÊít:“yÀ3”3£Ú×t➌¼%ïLl_ã!3€ç™Ò&³œFdeÏ’å4"\±½Lµœš¡(S‰¥’,‚Ú…ï¿ó‘êñì y*­œd;„á‡í.¶þÖÿA „¯T–cO0| ?ÿ(Êù¬†ÞH¾Qý'9@xÚ‚Eo8q5ƒ ‘d|ϼ6¢‘à}ŠxíçÈ¥zÄík‚|£è’w)h_ã¡ÖÏsn“ã/0ŽäµÉm²ŸgÈå4q?IÞ¥RNÍP”i$E„´aª´ºª¦²rï¾ïÌ{©|ë.;†]Œn„ß²¾}pçÎ-´A4áK8ÞÉÂ4€”åÇ|Âq:É7ªÿ$H†ÌdH.Ìà_,T2¯cA+а)àuÏ@·¯Qð%š ’w&·¯Ñ&€çÜ&Ãû³äµ‰mrÏ@!Ëiìl Cò.9èt¼ºhÚˆ ÅàiÖöpTIh¦ŒfΨ‚TâWõýÂÏ¿oÛ©ÓÛ¸ÿ“xCh—9JI÷gRÐ’ñŽFPT9°ß¡R¶zÕs >xç M¸¾„3áLÀ?ʯUÛvÏ#ƒ+™oÑÃläÛï¿.}‹ê?bÓÊ$fò/z"}uÇ/«ÌëX Û6żOPÁíë~VÄ|gä]ŠÚטiD„ž³œÆ¡ï«ìKSÐ&𠳜ÆÇ6=Vùp!‚ÒÎ$»ñk ÈTP®Ñ=F‘&ó::(õPÉâu Y† À3àö5JA~õy{û…<åÝ#úÑé!tϹMŽ€^Õçu|mrä\|À3„f9m²P¼KŸœš©•ÊÏ4óIùÈŸnýãW ¾[ñÃ÷+wúh¯×s ×ÎÍoÑ>+7§5ø–“tJ2‰Ìp}& ó< 0AÞ,§A€4‚ÇäóLS–'£Üf+ÊD£Q°å½“"¨ÏxàJвqVR*ÊòŠ×MÎ䌄”`úÉú|/äOØ‘B-ãâÖT'yF™È{æŸrɺg«2‰>J£û+ùK¡å=óÚ‡]¦òZò‰yæã“ño¦òÌHcÀ½ª)Ž:pŠ2½fž ð©žé§JQ¦Ì‚+ ÍT‘HʲÑäB*ÉÍMQlÂGšbr,¸M‹cþ…‡°Étþ…§¾þæu}L¤O¦òšy&9Tÿš©<«Oil>Ìóðxe*Ï™ggº‚žlsÞH…aR¥bLWyßœ”d‰r°`ӳĊî3ÅIš˜i,ü ¤:òó:4>™ÌkæYããYhŠ£÷ež‡ÆŠå44.™ì›‘¼óº>6™ÎkæYããY}Šcóaž×Ç‹å´>&™î“4žMtºFz½VmVñ¤o)t:e#ÝŒÇèÏ÷æ_ãàS2¨d^'ÅÔ¦Á§Ì]v½Å¢Ü :»B9ÔZ2ßt¾©šæ"¾y<ÞG­ÛÕ'KKUÀ¾Ñ*ÍÌë@É1Êh†óšÛ×@Ö #ï2¸}]DvT,§0yÍrˆM¦?y—°œ*ZRtܤ$#ð¤[.¿Ý1Ö’cŸ¥©j·!ýºi#ê'9 »’—“m1½¦\TV׈ŸV•‰oþŸ½ëŒ¢ÚÚw¶¤½é E¥‰ì>»>Ÿ]š½< $¼·J¢þöáÙÞSŸ¢Ï†¢"""é½÷Ð!R·Ìÿ}“Ìf“ìn¶ÌnÚ½0™Ù;·~gÎsÏœ{îúÍÖmÛ÷ÒAgæc#Æ=wß´‰ÿü¹²;‹v G{Þƒìײ{û–’nEñF·É¦¦ÖGñœøð¥´YHæ*>*›„×/iíªªNk9¾z%›évUj|ÍHÿ´ïæ{¿#ùÔ+.Þh]•Ædɧ^ɦEz£]¥òiØBiŸACîf×–ÏûíCžý|•¬Ïrï³ÿxÌby¿e“äÄ×6]~~/¥U“úÂj© ¹ÝO‹+éq ý{´Wº´k&v8šx*·ðö¾]| 8/©¤f)#S'<Ž9· [I·òTðB·„œ|û_Ï:ÿœ•ó_V>‡÷˜ xÊ{^bƒ,SÒÚ †žQFÑÚ³Ì0¯åø €^hôø$?ز “I>­2/´iL® š`nK> -/´«4>Õ$÷ÛN2ýá°Þýtêãf³õžÝÎPž¾ë*s§6ÍÂ)·Æç%>ÏÜ}•¹o·v&U¯ŒHyÂ*¬¸£ÖËúÙI·ŠÁ/¡Û Ÿw>÷ÈeÅÁÉb´éWqƒKRHZ—`ÐU µ_¢TùD%´«Ôñµ|Ã*Ž‘|Z1F¥R”ÐºÒÆdɧ¥(øÚUŸFCPv?·>øøåVkìDÉêý×_¤ÄXÃVhŽt5NIœˆqƒF÷Åûžµqñ\´„-¥¨>'7’nA@|,òE¤é§7!³IÒ:˜ü§‰2­åøêŸAÝ­„ñµ\û\ª’ʣܒɧ%X„|%ù4dè*=c0|š‘ž:ŠG¸Žä‹šeS"¦ö,>1)itŽ­TÚšÈ:Ä8Zc,¢â)jôc=’n¡ÓKÏ©Ó|€¸xºV9RôÓ«ä,iJ¦‰­5š¡Ir| .$Ói…ñÕ{sÕ$xx’O½ãR¬NëÉ’OC¢ŽÿL:í¢Å§¾Ò+»ËςԚнî–þ87† ¸H› Ö²jžŠ8¿r³ÛzŠ;ÎD S…å³I7c˜bóCJ%DŠ~Á4ZÒ:´HZËñ5:„’$Jãk(M“| j~òH>õN¿M>” ÌA\×&Ç5hÒônJ?É•½Óé…vmw–rõçØ¼9òò N©„Ä‘xÆÄÇ CÄÙhaK£˯Lºƒ_A¡#˜ä•’V§ù   ¬k•¦_0ý«TZûã'÷‚é`A¡]`ÂL–°ÓF˜ÖÍÐH>?•>¾VuâÓޝÁ@¦§­’|êïݪ7<гäÓ@‘’鈀>ÆFƒO©ÑŠDÐ?7Ä ð8“ÅÒ&)>VÅf"(è~;w¹øiáZñ÷;/[7ÕúÄøé¿,óVlÒ^”çtm+î¹æBEÖ©1å˹bWæc7\ÒW :§‹W,?%¾˜½DlÙ}Pp OJŒa_ÂZú”·ÿ'ŽeÒ®ÑGÍò-È–ð…‰À:¸Þñ‚‚v(Ÿ‚2gÞg¸BÐé׎õTÕM`ò Å·¿­ ×lv‡CthÕT\:¶oÙ8„.G>‹F7ðA6øµQPæn‹œyM?pˆ(­7ìØ/þõÍ<1 GqË0~X* k¶îÿþ~¾8[ š4¨+ÆÜ4D4o”¬ÝôwOÏ_Ñ™ãç¯Â³±]œ<«ùhפ^yhŽ…•_ÏY.~^´V+†6o êÖ—ž{¦8ÿ¬ÎÐýÓZ§YTÆ×€:ì'QµäÓÈŽ¯~ÐòyK§yDÆä`ùÔß»Õg¼Ü|ê”JŒò%KUb“|Vˆ4Ú–Ößé4«Ó&Œ xo’ù"(S×r³ÅÒ4¹^bD„dîb÷æg¿ˆåw—ëÇš­{ÄïË7Š›†ö£o"6î: æü¹QK÷Õœ"'¯@¬/‚€üé‹Ä±“§Ë•Á—ìÄ÷¿ƒ`«•‘öà X˜5PôîBY§$œß»“H½ÿZñ·+Î'ö»ßW”ÜŒÀ¶ù6Á:¹ŠŽÄçû"ú¡|­ž´ßˆ"©8zò”xè–¡â¹Ñ7³Ù$fü¾Òˆ¢#Vù€ü€ ((“?"A¿`Ú1Zÿ¼hÈøj®0›J3œÔ|ðí¢sÛfbÜ}W‹:ñ±â£óµ6û»§wjËžƒbÙ†úO¯çŒ¯æˆß–oW^Ø[LxàzñÈmCÅY]Ú‰& ëºÓãÙãG\+¸y¨hÝ´ø|ÖR‘‹1Á¨AZÑ, ã«XTK>ìø ¬UŠOý½[õÎI>Õ¾ôFEÒ1çìO– §ÜHæ­HrÙÅDÅåœnJ¿ÁÂ-­(?ZŸýjŸñ¢LŒ³ZoxàçÙøX+4R—+{Õæ=Ð.6ƒûv½;·ý±MöŠ»´t«6ïCúuÕ´ÏÔBRK¼¼øžgAßÏ[%hž1Úãng´’“5Ó,Ë30?\߉sºµgCˆÞ¾ïˆçmï‰'$å:(˜eâm¾nú±üHÑÍ@’“!$_ª \MÑeëÞCF±2ˆ'ùxš^I¿`ÚQZg>.¿}¸hXiIØ´ë  –ñÖaçŠ6ÍŠ¿ éƒ/;Gµ‰ª¿{z {›vdê?Ë7aB¼[À_v^/í+Qãúu Ø£”Ðn1™µ¯>]Û5— èó,‡ØwäD¹òBˆ­Ý4C»">¾†ÚwÏ|Õ•O#4¾zBãíz)"yx7Í#1&‡Â§þÞ­zÃ%ŸFOÒ1çìO– §ÜHæ­PRT Çr²V»µóÅϲyXUÕç*^ÙnÁ•&GNd—ËD-p£ä’—4…Üåw ΜøÉ§Qr‰v‰÷ŽŸÌ)WÆN¼ÀiBÁ—­¿pðX¶Xµy·8t<[,Y¿ÂyWÉ ¹§ÂA "ÆÄÛȠѯ¸|#ËhY›wª¨Ù…gÇ‹ùBŽÎ#FÓϳºŠ®#Fë{®¤Õít•¶ "_Òü©n:ÿ¢I1Ç÷êcb„gR¸ð}ÜjùËj¬É³ gwi«}ý)€`Lžå—¥%kwˆ¶Íáùiâ+yHñ¢µF34(âãkH® SµáÓȯ>ÊHÿ´›UŽO½½[Ùv®|ªQ±Zñ©?YÊÇ3Y%¢‹å”HÈAîþ±p£…7}\<¦í ª­¹Ð67®ŸäÎcÁ§ÕBh³ìZ\ ~ë!ÖÊ{å?»žÈÎçöì %s8bì›_êYDʽW ~¾e ×ÎýGDvNl.“DQX¸¨ºqÖ1‡UvØA/˄ю×Õ",Åädõ–½â)بWõ¸ê‚2Ss#èL÷õz#Nk“8Q%¿é!&†E&°…Ú$Ö×½ÿûÏøRsXÏ&Á6=å¼ñôßÜq¼ÈÏR‹Ù´ØÌâÃïþë‹5ÐWÁã¢>E“ØÓ¹ùâ¿?-ù……Âáp‰[/;W˜LÆ>ò u ÍÜ‚²äÓR€Q?"3¾†ÒºšGxLœO½¿[Ù9ɧ‰KhV ù4”‡´ÒòDOKÞVÆö’/ý(z [~@¥%Âö‘ŸSõ@[¹„ø‘GÓP¬¢òð”@íRÒ— ÉI šÆ‰ñÔ\]ÏÄžÔ¹<^þCúu¹¸ûÙÅ¿,Ã`ñ³xåÉ[µEDeË4ê7´UdFg£Še9Z™ÅåYnDÊÚs‹¿@Üuõ¢]‹ª¹ÏKÇu“™HÐÏKu>£¢Nkò%ùMº'„xð¥¿{Þ|‰pÀƒÍ¼•›Å!ðàM—ö‡÷½”’35Õ¹ù\'YÎíÕQtyÅg?/.U/MB¸æÏ¹6Éšò呈…²4Ÿ28Mký™á¹ÒÆ×`1ªn|Áñ5X蘾Êñ©·w+*ù”(h¡Zò©ÞøêrŽŸ’FýÕå!ÄiÑõTX^ƒz‰0É(òFÁļ®_76Í1‚/åÃæú½²…r‘µQ4Õ€«41/Ýî~´Åô ÑîÛ¸(iÿaãì˶Ëã·'ÞÑ!_]^È $ã¾C'ÄÛÓŦo9»ñ@òWNšRœÊÄ»Rꦀêp8ÅI˜`0.æÑàM÷8‘¥Lþ¥&™×I‰Eæžt$Ï’_×oß§EÓ™|k1{—)É×=;¶B™f|*ÑX{–úµá´ö¤Yñ‹¸rÆ×`0©ž|ªõÐï`ºldÚJiƒ?^ôõne§%Ÿj¤÷¤YµáS#Ú(—剷áUGBPf#Ùèˆ6<$úv?Cì„û7.ìá@MûdÝn±o·vbþª-š[·¹ð„Áϰú=ϲ¹ ˆ÷ÞýâWÍ´"+;å,Ï$ÚuvN¾àâ…•›öhnê(07mP¯\ºDxbmTñ‘(Ó¨¶¹Ë¡]úëÿ%º¶kûÒ†Úgy~š§Íi5žü¡_WF³£Në.m›k‹géžü4{É:,„m -”õwO§L*Îðc‹Þ«SkѺYñ¾2p.ͧ8iÕmšõr°&Ïruþç³–hÂu›ôÛFžuú…µg9zÙF¶×вjŸF ãÑ©†ððBOš{¹m|”?^ô÷nÕ["ù´jÈA:=jøÙ;¨Êf,5Ønß#ez¡·+jŒ^¡ç™š¤á{Š)ÿ›#èž/.éß]KB3‰©_þ&è™¶Ë7]:@³-öÌÏëfê‰'þ6\{‘¾øÑLí3-5P\­c)oáê­‚½_ôèÐJÜ—tÔZG6DZ“éòÃC‡.OÁ&œ“zàâÎ>˜UƒP©üQŸèÑšvÀ÷_7X¼ÿÊs–mÔÊŽºa°Ö÷ôöž þª(Õ‘p+ ÝòÂH|óDFzê(#j*‘ôŒ(­Ë WŠ))w—kÁµƒÏW : ‚²ª}ZÕÐò‰;.ÓlŠc¬V¯¶ŽzZº–¢d.æËË/²sö\iŸ;G¢Àeçõ„ °žÑ¯XÖ)÷]S.}¦“½YsGS Ïàïžg:ל°êq2Å/<4×ÐÃuXkÀC†È# ù4Œ•Èyˆª¨Áò©¯wkEõxÞ—|ê‰FÕ¸ö%KUÖUN+JÞ‘«¿Òµj±…×Àh 6ŽI‰Þí-C¦“#Pé|Q(QVHöl“¿{žé*ºöfÇ\QƒïG’Ö‘,Û`dqÕ_¼èïÝl_%Ÿ‹˜L-|ˆÑª^Ö#H$‰€D@" ¨šHA¹jÒE¶J" H$‰€D DFÛÒú?©_ˆÙÝÙ¢azá®L^H$‰€D@" H"€Ë.&*BÛvh8uIA9ôd^‰€D@" ‡ÀR㊒%Ij9ŠjˆŒkH!•M n.ÀÍdxC€îÁ¬påçm7oée\d|\kJ©’O…ÈHÿteÓSòieS j×_ù´Ú ÊÜvö“™‹Äê­{±!HžhѸ¾¸ò‚³´ígéÆmãÎÚn[Uû‘“­ úºÎ:•#þ9ò:·¯kÒÌÄÄ£&ºÑB|=g¹¶¹·$Öü›Åü•[ÄÞCÇ5_Ùg`{ë‘ð×ëk·žOžC@ò©qXVõ’$ŸVu ùnŸ?>=|ü”¶ÓlË&õ} ïT ö8&&¾?65ÏÜ}•»ÍËÖïŸþ´X¼úä_µ8ù>uC£]T[A™L,^·]­6¤r7ÔŸþðÇJѸA]÷ûÕI^TKøeu7f¾#öêPjûû´Ú Ê»°5uãä$ѽ}KкÌ-Œ?ýq‘¶ítÊ[_Š3;´½| 8xì¤ø/âw᩟”  P܆“鹅Óâh ¼åa…Ïäé¿,Õ´‘M1Hœ×»£Ü·›–žÛÞþ´pذ#SÛÄÀ'Í­±Sßè/ÆöÖ'ÅÇ3"ß1h?ëb·¯¢½ŸívµåŸ ¸èœ.Ú.nk·í hôõo˱µus|eèí.» ¶¹–!ºH>.Þ•]›äÓʦ@hõûâÓ™óW‹evjö,Z½MÜ:ü\ѳS« ß§üª7géqòtžà†B—Ÿß˽ =w´å—¿}‡‹:ñq0‘SÄôçŸÕYæfÎ_£½Ï»·o!nÇ»<›…É:ø«É2|GžÝ¥­ˆ‹­x/‰Úþ>­¶îáza;êG³Ä{_ÿ÷”û©iX/QtlÝD$c»Ú»¯¹@\ܯ›pº\â­Ïfc0»¸ãÊóDûVMÄ¿Ÿ!²sòÅŒy+ÅúûÄ•öÍÖuãÅÙ]Û‰§î¼\ôîÒF|ñË2‘›_ ¥ÿÚjîò5éá› ¨·ÐâF\‘vþÏ °uµ&ÃEK˜ƒü÷§EZ¼üœˆô;³=è°DÛŽÜ_i'OçŠl ÈÝŠiã/­¼Y$ŸFߪVºäÓð(2:uÂá•|n_|Ú«SkÁ]l)Pñ}ÊOö¡¼Oà}|ÝÅ}Å“» fNñë´FææˆO ¨6°‡˜ðÀ 2œ8 ï[ É'Oå nAÁÙÄh|9Þø„˜Z†ð¸ x» étðWbµ~ŸªÊfÁ#Ì AYu8ùùv;yÀ°Ð±uSÍ>u L.þ1åkñÕ¯ ÚY%'%Šú`J.îãÖÓÍ%‹m{‹£¦oÚOP‹|çUç‹„¸X±Ÿô_à€P|…¸¤wm`H®›€ën¢qý$Ñ«ckmpØ´ó –|óîC¢3fÅœ‰ujÓt!vþ3‰0ýŽýGÄguI ±â\|Ö öÙS×ë ÷œ_hW].õt¸åøËïT]9FÓÍ_}þî9œ.qþ°Uγ¡™ðËÖnwÀ„¨ªÒÍápä¡]†òC¸ýŒ­k3ŸF‰Ö_C}žjŸFz|õ†±KURyx»Ç¸hó)¿´rGÚP@ñ}Z'!.¤÷)î³»¶™d¼öîܰ3S{_oÃ`'Æv~¦ eËÆÉ"¿Ð¡uŸ_)`ŸÓµ-Î ¢GÇVbå¦Ý¾  9¾6ò©òѵƒûh_iµ÷¥Ÿ•îUý}êO3ÒSGñùá(ÎÓ‹Âü‚Y's(ºÝ*q=?ú:ÁÏC?/Z'ê$Ɖaçö(‡ …+†Ö‹Z7­/ŽŸ,‘3©…öÜÎzíÖ}âǫšãÙîdøµrº·o.~ûs£ö‰ˆ Æ(3lÞuPc~΂õ@|ßá,Ñf"F†¬ìÕa·5²Ì²e9 G"A·²õú›“—ËÎëz¯çöhï3¿0äæúLSY7H·Â‚‚¬ÊªßW½‘¤umåÓhÑ:R㫯g¥¢øšÂ§‘_½â¨¨~•WÕ•O¿ÁgþåwkZL3L!ÜhXsÝÈ—³—iæ\kôð­EëP6B˜¦©Æ¤÷(ÊP ¢¨ØÚʧ@kÿûòMâóYK Üë\‚q™«ªþ>ŸFZPÖ´fù¹9GNå(4] àhd p{4ÔoÇ!Î-_:µÂ !ôêö‡°’·ì—õàé^Ž6<´OîvFsñÔ]WˆS0ÍxúõÏõ¤š°öî—sÄ–ÝÅ¥Ì{BãÌ[çcðÄI{XâH<……ûÝŠÀËÝBm.'B Vm_ÍùSsùæ­œ†É‰š—‹µÛöjötÞÒTFœN7òCqýUF«iZ×6>­#>¾†Ê'5O#=¾†‚muäÓÝŽbMÏZ·‡¢ß–m€pwÑz“ Üºà¬NbËžC"æŒÜ6Lè_ã @óë°§'£P0ó—§6ó)•È· ^þ÷Ú_8Uõ÷i4øÔïìÕpÄs×…uÏ–Mka¡¬Ýfœ\G.gœÇ³sĺíûµ…ÍÕÓšFcš;œÀ=šCP{ÌÏ:³ u¦«›9Ë6jîÆ¸(Á[ æ8;'f ‚>g-^[*ówÄgý ÏîÓŽzØŠ|8·oÙDû45šhÖO”l›Ñ8Ï{v-DÙžXU•V&Ë7šná6ЬoÚ_,Y·Cèþ²eÒgò,¼œ¿j‹ö¥4§=ûò»Ê&êonäTìæ먶§¸²ˆÒº¶òi„iíÉó_Ã}kŸFp| ÞJåS.öËÇŸ‚B{ÐïÓã'‹¾èRèå{ÑmòˆÑ\ò·?7ÁûBÑ÷Ì3DúuÜØôÆ:$šLþ S»˜cpÝÉ6hœ µOiGsT*Ÿ|…ªþ>ŸFTî>@IDATB£L†fàÙÅcÝŸ‹·zÙIøê«; G{C>ž‡‡ŠŸ¬Å§™\ͬa`¯NXI[äဠ6„ûæðzÑ Ÿr†jÇßý!þñîÿ4ûezCèݹ ÛY.ðÁ6°§&dqÅíE}:‹v-¹ÓÑÃ?%­Þ²W³]¦Ý=^ð¡?½ÿùa¡ OQz¾8sôõ†nv1¾¨ÇçÍüfªðÄ[«3Ì?îòX~ç³Î9†úE·0Û¦e§­[x´Ø¼ë€Ï⮺èlh,Ðp-|,ÿ©Ñ¼cëf¢O·v>óDúéær:³É¨Kã œÝxGº~/å»ëŽ­k+ŸFÖnšéÏP$ÆW/ÏJÐQÕ™O#8¾#2¸i^|zQŸ®š«ÍÇ^þD[ßs^ïNA½O{vl©Ù%?7õ|QŽmì9š§)Aûä¶ðFÄÏÿôvA“ ~}æî+íš™ö»ßWb÷Rí>¿Tð=kT|*Ä —ôk¶îñ iU}ŸVħ£miýN³:mÂØe~;XÁÍHʬRxæFÛÎÝ[7Ï6™Í7lÝsP[ÀDá.Ìã‘ÿ¸e}ã&ÁFõÙ{®„jðøZ”Þî»im>¥ *•òh:Yìš-˜÷)¿.<oÔFëë€ÁÝ'Ãûßþ!â=J“‹Ø‹¦Aþ'çÓõjO,Þ£94¤7).,4*ÔF>å$dJÊÝ¥ ¤¦ÿ§ï(W]Þ§ñ©Ë.&*ÚëV -ÕÁ ”H€Afô“œ³_dl®~³ó¼ðdz]ûIlá¢I‚QÁŸ J¦¦ ëè©¢L”çíR×dpO!Y¿I·*'°@Ÿ‘hþ±+ó¨¶A¿Ï3Ûå¯mži½&nÄî.ŽÍûö+ ÊÄ—8ë˜Z”¿tzYýXë3šnþ`ô½¢çÀèR/O§Ÿòrºù×FÓ/ð†•<7§µ?^¨I|Z—âO;¢ãk0S¸i« ŸFx|­&ï ž¡Í#9&ûâS¾3IŸ²!˜÷©.$—-£Àn×¼ÑýÛâµÛµwv³†uK%£-³‘B²äÓRðõ£Zñ©¢Z0C$e6I{ñ⬠…¹¹§ó–ÿ1÷“Ì#Yâß3æ“ñ«eàÌúÒs{ÂÿàjñÆg¿höUœ}Ñ­\¤q#~–/y-+ëhê#¾<ˆ·‘ÁM?ÖÃúª;ÝŒ'زtºñù' ?ÝqhΑ _0M¬‘´®,>­Ý4+~ŽjÌø̃ktZvQ_}6=#müÓ<¼$pÓ¼&ɹøM£üþ·ó ]ž§mØE_Ëë—”½àV”NëÉnš¡±ï%Ÿ†Eµ¢Ì:í¢É§aKÚ^úM!˜ÕÆ|8òqp§Ž‚U 筫߸É÷¸¾F|%Ô;¯¾@ñô6øj†ÃY7hÎ~ùpü¹q§²gÛæÿ,œ5s1ê&®Ä—8o£&åèÇú’ê7øuÜYé†öG5xÒmÛºÕ3øü£/à)úÓÇMëhòii]Žfú3USÆ×``#ÒzÒ. ãk(M.Góš2&ÓOòÍÄ‚IHyvmÞ˜i/,ȬӰYÇÅëvÆî:p”®‹î¾Cµ=зãŠM{èYCývî ådö©ìÌÍXðÓ÷¿›“Å'(¸(,gôŠC9úíØ°nüùH¨ß¸;è'é¦CUt.K·¬ìS§–Ιõ)Ú’V¤Y…ôë3hˆ é–ƒ§øõÅà‡OY¾¤u(Eë «õL^Žf¸©±r|õ„©üuYÚ…2¾F‚GË·´\L9šË1¹F¥"ÊÒ:Ô1¹T¡Áý(G3d—|†ei"ŸÞͪ–ÏûíCžC FH¥»a,Ýt̤Iõß;öDqC8“ò4½ =-WÐë4­]²pýÖÕ«vž;ìŠóûùðä„…wjR|¬š\/Q‰³Zù€ÕªÀí¢¹7ù€Ö]q:§vmÚøû¢ŸgÎÊË;}`PÐâA<©ÔM/ˆ·‘Á'ýVÌŸ»tãò?· ~Å0´ï"Э®¤[º9ì§wlܰ`ñ¬™ òós) ó8£Bú‘œ9Ž¦ØŸ~·‘EYÞø”UHZt9 ƒÖAT[6©Oš!¡_Ë¢Uü»íB_#È£btê„!lî”´ñüå|Ò\ŽÉž0]—£µäÓò Uјr´ ‘Oµî©Êf#º¶ l6™v¸é®\וhÐÇ"có“.:Ö£úB„çÜワ3÷;ñ{Ç3{µnÛ¹k§„:Iõc’,fKù%¶(¤FLØ/‡Ã^P›w*/çÔñÝ[6oÚºnÕNDóS …,]™k]PŽ„ÙŠ×‚OúAhs¾™Nmç¯zœuFÛÎ]ºÆ'&5ˆMˆO²X¬Etƒ _\N?aãÐ-÷TîéS'voÙ´uÛú5{ÑiN©íŠ~ä#|^1YÀWFç‡OY¤u€`Ië«ô•Ì'ÍŠ3ÈñUGÎàñ5R<ÊæºT%µ¸ÙeeFû¤y•“… ”Å}á8Xä§ï.xÑw&-¾müIò©ñ˜F¼Dƒù”íÍHOeD» fF¤NØ ó‹ui©——iÍ.hׇ#·ÎK.>Ó“q¼Ç4¤)Dó0¤](§:‚\”ǃ°¾’B±þÉ> טuAY×(#*"AÒ/pX ¡ßÈÔ´U¡ö˜–6¾uàU–ÒŸ²IëÀ`d*Chxu>SJšù„¦Ü ÃhI™:a.[σyöªÍ[·ïÔ«y»3þÊöã£ÚÖ¸„„ޏä{]ݶvõÇ:À{†Ñ<ÌvV š…ÙG£²Wš•ê…Óðƒª¼Ž—ûK£RÒMMOçQ .ôqµ§.üêqœe2>µ–ù@éÂ2.Ýyx]S ]HÖMVˆ®¤ LM2ÏüŒÏ{¦™'’A§•¤Ÿo” £ùÇ¥ª—á±ÊwuaÜñͧ,TÒºbh £uÅU”BÒ¬b˜ ¥YÄy´âþT š;\Î#k‘9&Îi·ÒÕñu’ÎB÷”¶]º^ AyZÅ] 9…¡4¹%«ÍJš[)WUf¥@0DPnimùV¦cßÃØ}òÍ‘¶©3l£(Ì1°óüôÀßž ÁžZe]XÖk=oM>ë¸+Om2q¤öX?ˆ…V]“¬?\ˆŠHô Ö°é¾IpÙ¼ Çþ{ZXZ¾XµÁ¥òç,HÒ:08æu`Õ”JÒ, ˜Ü_Â_£Á£t§ZÐÜYèÜÇ×:>[,u¶­]5­ËÙ}{ã§‚ø^m:vn´gÛ–­Z‚Èü‘|\#YjU¢Y©~"(Ûl÷äã“Ñ”í1Õ¥¾ {å/„µñÝšeÖ˺hVA1t Åü6ÃמBrm³SæƒBA«¬°ÅA„‡§€Ì4úƒ…˨I?ßP‡L?j©´É¥PoRLÊãR_ó]1w*àSV"iíêií»HCîHšù†1,šE“G°Qöìe•¦ù #¢p¢-îžûÝ7ç6lR?©ç¹ƒ¾ÆðR‡È9uòß>ûÏ+ž2ð:,šØŽ²EUiš•ml”N³‘)i³Ù,êN_ Ñ(ë àK/a¡ºÄËŠãpgØs=âa³LtM(…=jª(R{Ævè&ºÌª¶bSö!!>ú§B]ˆæ¹²‚¤ŸoäC¢Ÿfïs {f´„dv¡>eIk¢à=„DkïE+iæÎiV ÜÉ߇€inp½ÁW¥hLÃ#”ÖPšù…Æg—H¤ˆ›íƒ¸ýöýa3’G¡ŸjÀ…M©Šš‰Jé¸T€É†ööLkS© «à̾5 ]¬‚­óß$I?J“¾éÂÖQT¥’4¥Ÿd$Þ‡Gýu.°‹”M²ŠÝ –O™KÒÚ?­Á=Úi$Í*¦YUåÑŸ¾Dô‰~±¨Ð²Ý?îù1Zü¹—:fÚ ¶i¼¾ïÉ”VJ\ìbìòÚŒ¿!-oÎÉ?yÁÿïÿô Ë´hƒþ E_ÉôkƒŠ6¼˜J§™á= ­@N<ë×!•TåeÏ^?©Óåޏ¶”[ài e$µpû)Ê™èðnnþñ˫Zç%ŸV5ŠÈöDêÀ£ÑÄ#ܺF¤¦Å¬z"Ëá5؉>®—9bü¤~ŠËù;c\ÌYY™k©{å›¶GèU‰€!T+AÙËB$‰€D@" ¨ŒLøUubñƒòó´ôTøˆ/ £R'^ïR]_Bi¨i¼ñÕye|¼éò×SR•¤’WÐ0JP¦M° ‰€D@" T2£S' áQÉÍ0¤z³0oÔ ‚Ü]¿ÖÏSÓÆ}e¢if±ÉLÒÎÎÍUç±Mj¯§ñ8ëf ”Yxpñm?t,´‰†VòRG@U6c¶YÿêY*r2ŸD@" H Dî¬æ°8˜) 1°ØJ)ÊfûͲß1?V¦p«¨VkýºïØ,·>iÔøô;UÕõeÍCÌ4N(Ši$éÿ¡ánùük®‰ëйçP«Åz! ¶D¹M €SX®­Ás¡#ÀnοŸÊ?ù˯¾Êýð3<ÛÞÚ ¨¯~KAÙ22^" H$QDÀ¨OÅQl²ßª ø¯‡²¦M6Y•~Sm©zË0:uâU0Ø®Û,Ã#Öú©íÚ´ýI§%ÏÚ¾}÷§¬1–Ñ.—H°Z̮争jr.{¯µ2 ¤a5ët®++;G±;œ&“Iä:®·Oæe½9 8W¦;Yod®¶q†ø˜«¶½— —H$‰€D RÐüB”]vÑ ×^å)iã¾Ç.jƒå3Õå<ƒ^Z\.×]­;¶ËÅÿ ýºŸ¡œÛ«£èܶ™É ©Pm’`vbö°e÷A±xͶ„¥ëwþ½AbÃûï}*õÖ÷_JûQX¦†Y†0O[àɬ‰€D@" ø@@Q7èw`LAÙg˜bK]f?}ôhE?u:Ð(»DLlL³æ ë%¦ÞrϵƒD·3Z)$—†xâCœš7J®k‰›yß³¶{’2^­Õº—F*ô_RP;™S" H$À%¦{AŸª*=|$c4…9Ó‡¯½–ÿÁäç(ÈÍ}-&6NíÖ¾¥xæž«DË&õýd•·tˆÓÓw_iîÞ¾¥Él±L¹çÉqC‰+ŽZ),ã+Eº"Ôñ õ,åP‘“ù$‰€D@" ð‰€ÅjòôìŽ} l”Eh Û¥oßfIÉõïiÑ(Ù5êú‹ElŒ´õ °—Äkäõƒà'bâþ{ù=÷4@²Z)ëÁÜg"üuOòSPQµ¼ ’‰%‰€D@"–¢5"¼m·ž)³3Xž×pôs“¼™_P¡÷ xÇ }λø ì@R÷Þ¿ 2K!ˆ„ˆñƒqrró&mžEÄ·öÉ{ŠjÕ°gZµ¸:™E" H$‘F #müÓ<"]O”ËŸ¯×§:œê×ÅgÍä×fâš5kS¿Nݤ;ûŸy†is‹ ;ö‹y+Âv±[¦;ýô¬ûÀ‘,ñÃüÕôòXæS?âhµZÆœ5xp²QÞó¥Ñ°ÔÚ™L ʵ“î²×‰€D@" ˆ<Šò‡G%x\ó’‚›®MŽï}Ñ !°Îˆ£w #ÂŽýGÄ“>§ròËGauÊÅá¯_eyÖ½ïðq1ã÷•‚Þ+ŒÄDâ»õê; eg)(‡²”CMf‘H$‰€D b°CŸ[PV…âM£¬ ÊqÉ ¤Ÿd¸€«¸àR,]·!ZФďR‡–Ĩ:°ÑJh ð“‹8ÏkÌ $“‚²¬üÝ ÛvÃ_áòžD@" H$µ‹{vX5{ÍæÓ°,¨û‚¶¤¤µ~'=uo1"TÖQ¡}r¼Ébižœ”`ˆ 8T©nØ)nº´¿VÕéÜñáwóĶ}‡EÛæD“útÏ\­Ù&fÎ_#Nçæ‹îí[ˆÛ/("Ž‚ðØ{¯†ÙtQÚW?þI´oÕD\;øøz.]ÇÚ­ûĬÅëµÄ­›6—ŸßKsÝVQÝ,yþª-âÇkwQ¹eŸÛ³CIãB¼¢ë8â™›—×Eg;gˆÅÕÚlR£\kI/;.H$U Ñ©†ð¨Jm ·-7ß|33e¡^ŽÃäÖ*Sô¤ BM§G¬ÙdÁ¦{ñ†¨V7ìÌ…v»8«s-įK׋í0Åø „PjZ¯Ý¡ÅóÏÉS¹â£óÅgw£o"ö>!æ­Ü¢ Ì{Û÷ÒÒÑ n¢8§k[íwŽ­ÄÊM»ÅeçõÔW^wlÝT¬Ø¸ ÚDÚE‚rÙ:˜y÷câ¼ÞÄÐݵ²øÇ_Ýz¢kŸ­ÕѾEcñì›ÓmŸ;· ß…xº\NniMA9l\õöV‡sFzê(#Ú)5ÊF (ËH$‰@¸(ª î¬jÜ{ÙlU¾÷€æš‡ßx#¿)´ñÐµÊæÂü¼YÐ°­Ðî«6ïýz´×ª¥¶—l+˜Cè¡M3*Y‹ÂFhŸ)4OzÿíX¸z›8’uJ»9&+ (3ð OšFÙ:xÔ ‹¼ÂBñü´oÄûßÌ…vÍ<Ã_ÝÌÇ@¿Ç ÉuD|lŒØ A9Ü@‰§½°€.úˆ³Žy¸Eתü5Ž!kõdg%‰€D@"PÅxÇ–² 2Ú66¶Êu fi—K7M`>¸oÏ»ÃiÚó†pª-{¸ˆMœÙ¦¹aøÛ¤A]q,ë´»Øã'K®ã ˜Ö‡Fù•'o+9ž¸MKÛ¿{{›9‚öÇûœÊÖÁ¸v-‰q÷^#îÅvÒ«·î…Íóê ëf>†Ã'²µ3í™ó EËÆ%B½v#„?Ä‘xf?¶ Ù¥†Ì"å“Ù$‰€D@"  Å$¦ë)U—¸©øšfúáZµà÷5B¸ c]8aéºí¢O·v¥vm×BÓïf™vÆwpWÑ»skqä_—n€Ø!²¡]Þ¶·È.™^š@Ìøc¥f†¡ûw.[G¡Ý)–añ Ò>™¦±ÀüÕ­7bÎÒâ„å ×Ú4Õo…|&ŽŠ¢æ/úeæB¢ãryµ5£”k+åe¿%‰€D@"%,få wUª¸Æf›N»d o.\¹æÌËÉ)8°{7Ö¾íT¹ .”@솙¢±Ù…^Æà>]µË‰ïÏŸÌ\PÊ«D¯N­5/ßÁŸñ£/},žycº¦AÖóö‡™¶^¦·:œ.§˜ùÇjäý\<ýúgš·‰ÁçtÑŠðW74i$b¬ñw¿s—oW^x–Û¶ZoC°gâGO=úʼnC‡èHš8×*ay´-­ÿˆñ“ú‹]ÙôTÅË H$‰@%#02uÂ\6»ó 湦…)i[!«ud¿ÌŠùê)iã~Á%w£ncMë5lØê¦‘¿Ü²Iøgî¹Ê¤/ÆÃ=CB^~¡ˆ+Òôz+›“ÄÇY…Å̵oÁ»Ão&a6—×CVT7µÒf˜‰xËLK¨ŸüÁ÷®Ì#'rf|òÞµwïÞ…ü\З‹£æF¦¤Íf'±¨oh8-OÉpJ“y%‰€D@" ¥ÈÈ£F“¢ºµÊ.áºÔµÉôï[€#ÿä±cYÎ=íÀ±,‘ñÕ\•BŸ‘ÁŸÌz¸9I¨B2ó[-Ÿ‚nEuÇX±=‹›åˆq#~þ\<Bòqä%¾ÔÚëZå@‹«ÞéՂűürVmÊV•2³D@" H$eX>ï·_x”¯)¿ z (FkýQD—^.ÊXµp.}´Q¡@CUo õŒO¬“+â“z¬Þ¼GíØ¦©À¸%ƒ?hnñæg¹vf;7®kî÷_ÿ„ô'qäà ùgµB›Œ~Š>ƒ†ÜÍ3xêCžC RP9™O" H$€X6wÎÁ¾ ¹Zâ°X,¦<1¿ãš_·ua™³e϶-‡ìûê6iÑãU[-GŽg+q±VÑ ^"L¤Õ(0Ò]ÀmÆÂÄïç­ÿýy‰š}:7‹"'ÿñã N¸($ÓÏnrA­²”B0!l•t0•É´‰€D@" Ô^`û¢*œE&ªòÀ5÷>ýÒwï¿H»Yj–é³ZeÊ&¦5K®Þ¹eãóç_võpUuž¿dݬy3»¸-36þ ¸\k%fH»*ÜÖဟd¸€ƒUEaæîÝsüýº5oÓ®clBB½˜˜Ø:Ð,ëé¥vàç,,,8]—{ü྽›×/[´:?7— ö( ó\Öä¢vÙ'ø8ÔÚ٘ʢ$‰€D@"6£S' a!SÒÆÏ »°*\¼{ŒÁÆ#ï°‰ŠPvˆ#{ºfddPèш# G=uqÔÁAeîægÅ¡ Ò”_j£ CÓ eBêy ʺɅ§m25ʵ*Àë'`ôz1*œŽKr8èɼ‰€D@" 0—ª¤U£åĉæÏyÂr#h•Û››¶½ýþz¾`  HÁN7É  9 Ñn³ \KA¹hq1¢6žvÈŒinÁ3O.à£P]ëB¸²˜”u$äY" H$•‰€¢Ö S‚WŸx"odjú[ßl„ž0&´Mý>Ã6Š‚®-¥€§ Ê’©QÖej•=åÚ Y&. <ë #âFa™Ze¼&vº¬çE” Á" å`“é%‰€D@"  DKü›9Žœ‡ŠµÊíÇa |…;]¤ÿ_ }iv!M/JeOÓ âDÁX?ˆ!5òL#…d€N¨ ³°pð‘y%‰€D@"júÎ|eA5>ýN—Ëõãá¡(Ö¾S'<»?©YçAÍ1*õx”µO®2 _ýÐ…e ƺp¬ ȵÒÜ8¤FÙpHe‰€D@" T„ÀÔ )ÿÆ‚«;a§| 4ËUudØlê@›MÑ5¡ÖC-©§p\5ÉÄÁ3OœŠbä_ —]LT´GG ’ˆ ÊÓ§O7ÿ¶vûYØ×½êRû`ßmº{iî­Ñ˜.~·8Ó<ïáÓÌHUuýÕ3N¿–é…øèOƒ¦n¨òÏσ¶‰Â•<Å–º´¤åUãjÌøI}œ.çp´¦­ª¨-ð&¢‹¦rAòä;χ¢&>Ðj¢[Bk6=»éë*7.DºÃïØÆnƒVy´Êé¬KU•É#ÆOZ\F ¨<üÆ1ù‡O^Šqi$ÃÖŠª4†<q&Òý¹|Uq¨Š8¬uŸ0™~m”4ûÍGá¢>t z>"öœ:Õ*ö½köê-céþ¥¨ÝÊ<Ü‹ñpëݨð¬@º<5¦œ2½_L%>~á‰øóƒ”žÃ}+l×bÀŸf¶4øàÛƒtçSiaäø‰„êúÂár´† K¨Ê!4&GÀí’Ï•òI|ª>™Ž´/À猟ž.Z5ú(cÔ(~úxÈHÿtÄ+©Š´iü’ØsäˆÉ½ /Ä+ªã»‘ÏØdL¶íûë³ÏÖK4'<[xääCÐ&X¸3_ÝD5¹N‚ c(þ×ιHÍ:ëÒvæ³;ž>¹#ǧ½!-/d<û,ý(Ë`yȶMnUà°/ÄÎ1|ñ.‚püºjµ,ʰ=»Ç vËb$ÕÇ_y%>÷xέäF@`¨(ÊvU1ßVF{Õ¾ÿBoØ>Žöüj©cþþݱcODµ²2‰@C€¶.‡xï°ó°!Æ.“¢ÞWÓ7©lŒ±Mjït8–`jÜ!ºÖ8 ÿŒ{¿’ûŸy†rn¯Ž¢sÛfÂlÒþ•Ýä*Q¿Óå[v‹×lK×ïT!sNçÍS'þó×*ÑÀJl„Q‹c##(OœØ¸ W`®érp©Ä§DV]¥‘’vô!ãK+EQŸœš6žvy2H$U©†â%ù ´Ë]ѤØÀà£*Ò´ÙŒQ)iƒœÂù‹êTéN˜¡=nѨ¾zï_™[6©_#ûld§ö>!Þÿfž3óh^-ê¼S>Dù˜gÔÎP¥åÚIÙk‰@ðí/+Áö2ø†Ê*€ÀÓ“''e²O‡–ó2hëFƒO§VfÕÄ&Pqgºëï©w›MÊ¿,11¢{ûVêÈë+±1³­q8:DÆWsÕ ;ö»T‡ã²i/بY®•²Q‚²ü~QãØDv¨:!ð¦í‘ìii©‹–<êé—ì·§g?˜’Ö¶:á$Û*¨,^|æ™S-,ç_ !9]ULµý:aÊêk%Ö« ɨߺaÍÒY–˜ØœfÐ$K!9xŠpRAÜZ4J&‹e:l¼“Q ñ­}AU6ãkÐæp;nÈ4Íf›sÀµõ¢©Ï§ünƒd~‰€D 2Øl¿Y2í Þ@éûíms±ž ‰@ØlÓ—ojIÃJâR½Ž9aTý2SiGy$¶[>\Иpÿ_IMrˆt¤°Ls•´}—œ`Š‹bxÐÅ^­ 0•eD‡ Ñ(°oyÈåtÍ3i’4"2‚*² ‰@Èt.x+Ê»caÒ£ÑZÅnÈ"%5néxÔ®Àþrǽ˜víºÖOH¸ ÷¢a“üëÒ bÛ^:ù‰~ˆtÝÄ8šÍ¦ßtS§rò.ʨºýUHá#¡u›ŽÃŽò^í4ÁðR÷”‹|°Š3MŠò^õÉ$‰€àh¿ŸÝîZ©yÄð“.Ø[#m»B›Ü‹§›W¦—HJ#@ï \ˆ[:Vþ  nš6çø:u“/°ÂÓ]ÀèenÙú]¢_öF糌¥ëvˆng´I‰Ø\° âH<-fËEhq–‚rô ßF~&Yoœ9á›ê—Y$, b›ã4Æ) ¶…·Â»tº.cJœ"µÉá!)sK°>HýµÀž(^”p„…€§ ;ÉI †ùIÞ´+Sääˆsº­]¦öøÓŠÓ¹âüÞDœÕŠ!M#`m1}ÖR±rónMMÄ~ÝŰs{hÚè3Z6×.rJtðèIñæg¿ÐXthÕ[Uü¹a§¸éÒþZ9k·î³¯û­›6—Ÿß Bt Msí«nf\?È?-\#NžÎmš6£n¼X$ÆÇje†ú‡þ¦‰g^~A+”AA™¶öµ&кÓiVÃݧ l2ž2h©”¯ÙϪ5èËŽJ"„€f¾¤ˆ]Ø ¯»¡U¨ª«ö¿™’šºßÐreaZˆ¶Y> ¯Húª…ý7°Ë”A¨°£ßäx³É\¿nb¼aZOjz{uj%âcY¼ÿ°PÄB8þëåÅѬSâà±'&óVlk·íw_=H\=èlñÕ¯Š#'²5ax!„X=,ß´KB¨>£Ec-jÃÎLQh·‹³:·Ñ„æOZ$Ú5o(½mên#ÖKÒÒù«{û¾Ãâ£óE›fPÿ…¢Ú®¬·—x"4Äoâ¾Ì§\ Î.»˜¨¸œ“ÂmjØeÌÅ8Xì·!2¿D@"PŒ€*Ž@÷ËͰ÷so£02H$a"€ ‚öáÝG-Ña©ÑVáò(Sp£¦ÓŠ#Ön/ÈÉÎÉ´á›8NM;|×U¢8!Ndçh‚ïc·]Û5‡–·¹X±©DtY·m¯&T7m˜$x4kTO»? gñÃüÕ‚Â,5È+6”£ûÂÄ ´(Œ÷†@ìéëy÷câÃÈ^¨ç:e_¨.ñðĉ_¿9n\Ñ4ÒgŽÐnpâ¶¾¹°¢ @t€MÛÿAH¾ëLè³ÈTÒrÿ“YÄŒ{7í÷Î16ÛX6{wam¢ñ\o X%¡œ‚íX×·lÏdzâ9rêT«ºçp!4à<<¼‘Ó³à¢ën Gw—êL,+ô,Qð‰ƒåS|3l¡ºÔ³Å5ÛG‘îèèñiàü$ùÔMžr5‰OéQš»—ö;æ÷…¸s"»P n±Åd~¨ì»§2ÂhtaY˜W-ø}m~ ¯ÙŽ œ›_{ã}â‘[/u··EãúšWŠÙKÖ‹¿@^¶~§¶€OOpv—6b „ëîí[ˆŽmš .Ú‹±šEãúuḠ›9 ßý{ ½Ì·tÝvѧ[;·ö»Ðî«·î]Û6×ì“Ô]#ba]QÝ]1)àb¾_ж>Xx¸eÏ!”ÛBxx"pݬæ/šõÃ"4WÇZï²<ˆ€_¡3À2‚J6òéWBH^ ådtvpp¨| bàu¡Su,Æ kLP…z$ÎxöÙ“ªRÅd‹”Ìê0È~›ißR¡§'ON‚Ü„têµ8ç m? ¿kpMà×ÄÄ;Ü‹ él¿}_Þ{ú]ª%—jR£ðÕêÞYºŒ‚ü5èýLAî&dÔŽBzåÁn[ø4~’|ª?)«7ŸÂ­Ûmèá|¼‚9WB`þ cò1ðê¹—óG~9©í¾A[ãTWÍNDͦ~¸òrrì™»wÏYº~§J‡PÊ»E„8Ñ«g çŠ•›÷ˆ”·¾{Ú”ûÆ¡ýDÛf ÅkŸÎNú·xù??Â$"WËž”º%òŠ]ÍÑsƆ™œÛ»«pºœbæ«Å3o|.ž~ý3ÍãÄàsºh÷ýÕÝÿÌöbpßnâ›ß–‹qo} ¡|ý‡ˆq<~äÈÿN>L…žŽsXåÖÆÌáMW‚DìñW^‰Ï9žû.©: ¾²XêßõŽíÁÓ,ÆfSMiið´2×ÿ=l›<ãMÛ3ûÊV‘X7§ÂO2&–ÍgäïÇl¯&çÚs8U_Q¹Y§œ—àùìˆþ­[/¶ÝËO=•Ã<ÜÅÐqÚÙíõ”ÚiAQ7UØ9=q :ºSˆçâæQÿHÿWuÝ }Lêä.p7‰jWL†¾ÅùÚªB¦`¶Æ ›O}˜P•Å"Ò| ?I>-Kò¿kŸ6·¶üz¿cÿg0ò{fƒÚø­™ÄÙ·ìÀ—­–N§ œŒò½/côDV/Z^OI_¶°Ö7Š„Ü^Y;þøñÛoùð ÷¾þ=î™{®2y.’#6„ Îî,x” œÕYœ×«ÍJ-¾c: ÖcnºD3ÉÈ+(„׉¸R ë¾uh©âê$ÄŠwÆÞU*ŽÞ5þ9ê/š¦Ú¤˜vÅsß÷W7xKÜ:|€¸.æèÎŽma\¨¡ Ð!€ŸKu9sçÍüæß(‡ØgóP‹®^ù ú:k ·×Á0vî±Ü1ŒZCˆ8",ïxÇ6ªhº†F›ŒÃ'°‹‘æÜ»}<¢G•m_A¶£L4>Ä34åØqv¬¥îcoÚáâ ”ó›%Ó1¯aÏÜßÓžytêÄ«œÂõ$ÉÞðƒIu±ÙbyjŠmì.¦×µ‚xÒa¿z4¿íQ×f<µË`Ä4QqºZæØs^Ãó†UºJh¿×益·œWE8jO¦ª,Ð…d¦/ÞÅp!¯in‘騗 äC|Ž‘þ”« Ô-,©géfˆ†4Oã~È"h–²Æ$Ôt ªsYƒGÿ­›¤(ûŽL„ÊèKÚוupî¿’[°ùDW”W÷öÀ|铨&I/¾ùÈ#E% ñ@êänQ˜†úÎEu‘ŽFT'õû0¹I7 [=o™³ )iàc´ÕõöÃo¼ÑÓ³eÒº‚s¶ßžžŠöߊ~Ð|c…Ù*Æ9"‹`Ã>­Ôj_T“£LÚch|DU,sU—½ÊÊè{ɈíFÐûE¸| 0ói ÞyðvÁ³ºA1)“¦>ŸŠÉCQðxNCæSÔqêø;úv&Θ_«ËÐÉoš[Z}?émṺòéôéÓͳ×ny¯Àè{tÅÌ÷‚|)ò[ëìÄø¯ir¼€¸ÁHÛ ¼¿çM:Å×úæóÕžO‹ßõèƇ› aZòâFb²^Zé™0 ×ÁLh£ÐœhTÇUâ¨,áûÜž}ìXö²9¿|`ºô²1_ÍUG^?X EXöÕx÷þÊ£pKA5œàËd¢¢ºy?ÜMK($·DzÄÚÅ ^8¼w/¿Z_ ËĻ֣&´¿<}!KÆö`n_É´xPè\íÂ$þ“á!${f2›”©ü Aö|Ïø¢kÅi·þþz¼waPK†=è=j£0Ä4š=³*zâ^OO{f¼TvªÎxL.ÂÛc9ÊÈÁõ0Yý@J„÷¢@Mo¾#{ ÄU­p}§¨›Ðî.¨ƒký£ªêŽ¼ÚŽL8ó¥”Å×ùzžg5Þ4¯p¾´†r7'Ï{úu¦8ÔeÀ/ Z€?§Y&=ÍÈñiàîψŒºö#‡:ÿŠ¾Ý§§+é¿r©º÷ÈGp†þw`ÙxÇJÄ,Sü•œ‰²V‹ßv{Uu=_x$ÂjQx4=½©]µóÞÕˆ™#GqE> Êûw¬%FÓ†ŠmQÉåÿ¢LSý$Ë[hËF\wÊ?”ýLùT¥cѹ2íéï«ç€['`Lá ŸÓ.惶¥KÅÌ,Àç¤l>þžò|ÊÏŠÅÚ ‚É›ŠÙë-Muˆ}ÃâSðÊ-˜ˆ½©\C`½4€/’_Jx½Þÿ’ç44>/=:~D[‡àY̆0ž z^…G©E ü¤·C?WW>ýuí–`rö:úa¯><¾ÁsŽñJôÄäw>ƇïÙG Éâ” u4h’Œ¨980ÁöLSQ@ÚÁ§Þú ÜÎÐâUuƒ·ûQŒãÖzQ¬¯2«ìÚ»‘…d*iø-X½dÁ†õË–|±aÇ>uòß»Â1Ã@yµ&§ÉÌp·mkWg,žƒTŠp%¾ÄCE ‚A lAƒ®I;«U³‚G¥[}%‡Zh;ïAóá­¬§jÆ­õ"ÞÔ ¤B¥{&„ЃjÏ9à˜ä~ —-{¤íåFx2Š„@“y@FZê¥Ô®bòö:ÿºvEu ˆÎG 6Qi ËBQÇÚfZÚø¾-­ç'ãE|6…{ä³Áþy|q+pGï•­—¿3RR ü Ÿˆ't.4æßŒ´¥Ÿã™6Ãöì–6îd<ðyX/—ÚdÍnNUÓµ{ŠzfI}ÐŽsa֢ܗœ8±Œß]õ,Ô9\1›®ji¹ 1#-å<æ×ZMC”6[¢Ž+ÅåèÏ­¼çÒ&¼"7_½ùCÈþéîB/*åoEwEð™›Ìƒmq^¯'{žÕª˜Lôš@âc›ÔÞkBDƒ´Î€«6‘PM¦k€[ÿXK½–¨ã<7-=ë0¢/¤%Ët9ÍWЕâɯ ÜMˆöµ¦@º0ùTt0™•kZZR:âÙém2L¡ð$©®çÊótIëſȇxž9ò-x»€®LÍDóñSI%WÕ•OaǨ­çÀF #’WõÏEظ–ã7'¹B9mÏ{{Œ—ß´°\Ðx]k±Æ´Ãx[€ñé‘Q©/v*AÃ÷UMáSÏޱ¥qb8S¦ÅZÿGÏ{Q¿Vúg.5.E½ Ñ­‚›G!º àWfžóüüý¢E?ÿðÞþCÇ ÒþõúÁ·óÄFlîA—g2” @<ˆ ñ!NûÏÿsîì—ç|û%ÇOâKœ%x%Ð|e 8¥1 ›³— |Õ|˜ êñ¼ð5ŚТô¦&e¤Œ£ €x'=u/Ì0>Âå3.á†ó—Œ/ìùWbð©Ká7cÂØeîûåcaWÅ‹E"½ƒoä_üûç4, dœÍv10M€çï`CKkç[áñb"Ê|_«ÚÅ5à>ë3º`å¯L§£àrôuðRÛ?5-õ'=íÔ´q3G¦¦Íƒð1¨0Çuâÿ£ßãBÄ{ϧð“"úÀ¿E!Ã6n“~ͳÙRo–Ë~œÝnA{= Ój¨å† §è¦%ø$»ñdVÁiÎOç.A`ëYgÙk»°Ç¡­¿—Ñ׿9íη†}*‚Áí¿ø˜9¡BùÚKf:h¿-Ó¾`4:S2Á0¨/åd„¶›6ùÇË»rB˜|ªÌ†™ÅŒ¢B$×±¾•m X÷xø¹Yv¦×nˆ¿Ëi¿ k"ð˜è}¡—åiï¯Çs®n|šÙü„*öIƘ匫[¢ 5A™àóZëý‡0|›6º™LïgâÛÓarAZ½RQ û#­O†^NMãSxFjœŸëú(fÅô¸¾fFﯯ³Q[ã–-4Û‹¸&\SKv»åkƉƒ‚\>šr¡{ ëÚe‹7îܺiòùï{ÛKÖí°Â›‹Û2''%bXçk®v§fÊÁ‘«¹€ƒdU¸oçŽ?ü4cFöñ㔑øu‚xWâKœ‰· A"UA:¥L Y0tk쳊³ I È}çÙg³`Ë[*il¼ØáíëfN‘À-m=ãK]+j–‰u_bšðËû* 'ŠÂ<é~Ž›oZ[t+ü¿Å.äþþmò+v§ýqn£! Þª8ì}`ê1 Ø^ÙgE0¿ì-7;9¿|"e=z2jèÎeï™-¦ËÆÑ¦ñ×Õ[nFi·ã^hV[8ÇJÒåRM«=\…z¯þûK/M¤}õ鬂¿ ²§X§¿l‘? lYT !>Þô÷¼\õjÐêr~ªŸ2räט ”Ê èg[-3lÄ= aû1ÉÚ…‡¢DP6¸/žõu­³›PPuz$—O± ÒG-øî°ncw:Iï‚r€øc­i¼ñ(Æ£ì~uãÓŒQ£òñ /®ž>–‹ ¾øbâ ó*þ-r±xDš]Nõ Ø¢(€ÏâŸÿµ1P¯è\ø”æ(§í?£óØfMI›2!ezEýÖïGj2kQL³ªc"‹s}ÃGz}5øÌgQ”©I¦pG!Ù‚C{ÎÊrýüùÇßÇÅ%ÌîÚ§oÇæmÚµKH¬ IY1#]í øLÉc=«°°àt~nÎɃ{woÛ°|éú‚¼<â—íqð7q­u‚2ƹXPy!øúCô?¬À‡1ša;*ëA±£¯J1pë/Á`íðL[PS*BS"…HDúë‹&œc ÊšãYlwÈß KG9{Bû†AÅ>‚Ÿz0%í-;\‡N®\ç-(~Š¿*Ð7Îø6s{ÈÐÓãµÝœ(pp)œ¢Ñ¶Rø1{õ–)@ë~h§!ÓlÌHÞÅõq|ÿÓž¬{Bƒ¤™å‚yûí >DïÎ>™OÍÓ.(=PÓ)˜jhŸ{‹ËÛâ´ž¨Äóñ–*\¯=øÜ;³Ð>ÀU‚ÂCUu[áR_&ŠKãg)Ï`h_< ®×KƒhCX| ž‰Ê [0WØ|Š’‹ÊÂÕrµQøÔbQžv:ÔàÓCh~Br Iàí/§NûmFÚ8a9àˆµ ÍlÅtL0¨eÒ‚>þ¹ÌÊr=.suçSºÌ:Uø#^g“ÉÓ&¤Ž¤ßî4šÌÒ3ÆÚP@@)Qke¾ßø£æ“ã…_í=Œ³~/.??·pÕ‚ykp¬C<]ýñÅe­ cyèøðIütA9 ×T êeâË´ÞÆfD×Ä ¼5Z­Ð³Ãí¿—V¸e—ËÁwô›ñðNh>Æ¿úÄœé” Xi6’ æüR7Š˜œöÖ¸<ê¾§ªµk|„tÇ•¹€`·‚ ØJµÂ6ïÑ2·Ý?5ÍWJÚ1’ œù؃zy¸ ªàâíôÔÝ𵋖: xt® 9À0Á\bjѪöRÉa"ÑžO¾IUË Å¥âG‘K:ÇýŒ‡1ÂÓŠMèÝÂ¥M8±Š$»¾ÆHÔ¸ŽLø-p»3@»ãÐ$¿oúÄÓ?u Ø–m‹¿ßðôñî~GÚ=À¦Óy⟠[:_’%<e'Rç•Pt…‰SKmŠU|#})[geýÆsÿt u‡Ï§.ò¨;ÐþÕ¥4b„Éå ›Oñ$ì$EÁÑE“jwMÆ^T>MŠ·®‡YËZÌMZÁc!Ä‹¯aB0Ÿ KaŸ¬Bs_ð¬7ÇÄx6Ö+|fRÕ•Oñ…,1ëdÁL i?Ÿ1!5Å<Œ*Ï÷8L6›U^5(‡œµžT^èƒ=ã)à1žæTzXqè‚4ÓéiqYk‡¿â!PÃB2•”§tA™Bò)Ä“ø_âY+å_†5WƒL&Ó(#:¬ÏÚÂ)‹šª€´U±ëN…vphÜ8çXî°½]G¯˜^+й‰¼ðìç( Êóú=ϳSuÝ£ÿ怇§e›u¡_î¬Z£Vew;»Áeï´MÕ4TŒGyš€ŽOž€ æfÂѶ—šÐ4ƒi Hl×Ò**½a¸Ó0®l § ø„æ¬Æ´rTõRF@8Û¤ß@AZ¹ø ØUã®f€¯â„`7„§A@IDAT|¤m¢ûýöâö0°0"‰Ë´~CŽ© ï#½«Ž)~‰žÖ¡ØïÖ¯u2?ׂ·>BüÚii©ÅËõ!¼P^ó’µï}ô=ì€wIt6”¸¦5ЦŠdÌCSHWmüdpü–h T“òÀ´ )1æÆ\<ó ݶᅌ<ÝQξ¸&õ:Ò÷®ž–½R¬Mu7tp'6 n”žÕz‹E@¨s6ô[Ðr¹!î;hÜRyoäøz Õ¾•X0.C}«ñv釺za‹åVúË#RÓvASÙ/ÿYèˤن2ÞÖÊ÷øûÚ÷QÖÝÐø¬Aôv }¨{â0IP¶%Zûé‹6FO¿+X?@½‡á‰ésȉõ…%þñ ÛßB°áTEð9Ñü+|ÜFQâœê#ý«¨û Vë«ÿú=±÷0Ýu5fp³gú"ä0üî6AØVë×i˜˜@M?'pEwm] áìFm‡ÐF¯4™ÅO É ³ô/bË6” Ðti[XÇZcZêØêià!ä]´o4£ß¥¶°æ­~­£ŒýÐÔ|ÿÑg£Üó@Û“ì3¶®ôî„q0m8}a~=h^+\Ž¥ ÍVЦâ¯zFœaz2—?‘o0Ï•BâÓñiéà±qè3µÙxV¾á’€÷-xŽÈOé<âë9 ˜|Žgòf”™ M*Ý>&€/Æ“òFbæŸÊbZ]ùþm.¡~Š~ç€?·s¾O`\úÅ,,ßë~Î9pØ 7ðyMŽƒ÷?^'ÁÐXÿ \NÏ>îueÁÁïšÂ§èÇ{Àà^àå^¼t•|×Ë[¼g\UàQÏöÔk¼†4“ ÎTJQABM2^s‚ÇxÞ§V™éy0èç¢_5ó/ßù <ó –˜B05Æä{ÊQÔ*óà5ãu!YÏ‹¨šø^{|x†¢—ðê5^S„Ûc#4ÊAµ»®ÅYbzâ±þ4>¨v¯†üKÃüÙÂdéç)$—)ü{lq òv†ÕGAùnxQ.±X,U´Afècáî~jµðˆ÷?aVð ʃ6¦ E!c³k…b¦¶a%bú m÷k‚¤¢®*ŒÓ6))JhO²n¼h†!ÍcˆìPt£ô_f°±?¡¯MPÎõ¨û \[чƒã‡êB2sÅ™àUùúKý‡‘î†xkîÔ4 ÎÈǧ<îa»ð¢³£ícÀ'™¦¢€Å?ð„§Ü„:~D›¢ÿÐÚ«M&Ë…†§{æâ¹Wë㥻ˆqÊ[àO/üîô£± è~ÐÓŠ­ž>г¥Ž™B×aoéƒÁÃ’h¾}ž\›i¸b§DLÆnÆ A¼tˆT_J×RÁ¯*°=nX|ª˜¡­W~À3v&(·£·¹Àÿ]Hö×û@ñÇÄøN</ieiu¨×¡> øJ[ÀÆxüT¶ Õ•OáªÚ#là/ x¾»N‡ƒ·_vªöµº+Jz¸€tјá%"pL±âà*ü†Ò`pȧêħ?›Ñ^5c<Öüí—=—}6*û7¿”Tv¢T?aMóIáÎS³Lí1µ¦xε÷Á!œ–9àwM?Êö™8ðýH\ˆqÊÆQV“L\kEÀÞoƒŸ‡Cvºß(!™Àa\­Ü@ÿ¿J¡9¶¬6±¢VqCŒ<³9?£Ø…›žÞ—¦J¿Ï3W;+§MML‰Î£þ1c­ÌÛS^:Ï l3œ–ZÞMIÉ„ÀîóáÕ½oÄ6©·ÏÛ€®ƒœ1yÖrfž•Upý¸í•9q…βø1ÕÆ£Æ§oÅUS̤®uµnò…l~¶T\GBPþ™Úå–.‰ÅžܵŠ­;ƒâAzæåæ6˜’šº?j+£/´+”445a¾P·Æ …OÉ‹Ö=GZ¼•–²§ìón$Ÿ’]‹¹…h°ìóÈ>WÄOL㪠Ÿ‚GïÄ×¹€í±ë?Þ´=ƒ¯Eªò`jz+̈_Á¤üFL®ŸÇDúŸžý£™ÛQGZ[Åco(Î8ì 3ÏôF]×$>…fz*q1jç/ãËÚOxQ·Äº’§õu%þÒ×{Tâñà$µÈº&™ñ”]ô—µ*PvÐNpuÍ2'¼æÁø'¿È[-¾ô|‚‡b17ý2²|ÐjTàF»c;X(ëÒ^]Ý|óÍ|hd³¯0+»Á3«¶õ°}ÁAhfhÿ•€<ÓÊ몃^ìsؼØ5¹²[&ù4| Àüä~¡RLÖ^Ú—0"aÂò4Ư ÅôØ´´”×=nÉËj†'¹Øýöm|EìŠñv¶IQ>1¹Ô_¹@5ëJ°ÍÕažuÁ¹¶ É:†º Ì³.ó쯧•ç0à -¬®¦*¬Ê=2þGúp¬üêäp85ÁŸz¿–B²@A\ZÍæ½ðå ›eqfh£`ì½ÐŒYªpvÊt,€É‹Ú#×ÿI!9P+;)wЬAò©qD€ñN¸PÄkÒ1¸¾nq¹¶8Kx ·Hã13YLßW£,©2àW ((zf:æÂØû¬Ó¥~@í&¿ë¬‰¦ÁoÇÏîZ(¶EÿLå°¦tÀdž¬úu­wл“~§º¥W§îʘ<™í×”|w>žÒ,&.6ÃWyÇïúðµ×Üý­IéÁߊ1Ö À—àêOß@žOtšÏvsôÜÞÒÒ²«Íví±#”1ÓM-n¡¦±ŠhkýçÛܽîQ&ÞJÅ"Þñ“\ÞòƒíGŒO¿>'^ÃÀ<‹µÌæ<Oÿha—.Äx?¥È[òH>-I¨1±–¤qöìvxq\átº.×>©Ž.LbgÁÇòØwlcw…Z¾ÌWu(^hÉÅâoÓ§Cr@—˜¸8.œ­5!¹AjMyh¡N½zN,Rxû— Ն͛“-4Öàíšœ¾L÷kòOØ¢++p¬†L ‹¦"a@ZUVÿŽ´½ÜH¸ zZMÊþ·mã¶„Ñ%™µmé£=‡ÒLN,ÈŠ9ÐÔÒ»¿Õñ¼ðˆÄö¸’OçKU-6Úð4Þ6¬IŠI=cÚÎú…ªÚOÙ.‰€D@"P„­Qþö¾>ŠòüÿÙ+' 9 á>å¾A.¹Åz+ÚjÕ X«µ­ÕŠ€~‚ÒzÔÖÚñ¨J=ê­ (("·Ü÷M¸Â¹³IvwþÏ3›w3Iv7{ÌìNvŸ÷ó™Ù™wÞãûçgžyæ}ÂÑhwuÒj¸ÿwÇx_`Ð|x&>¹q %Z…Ç e<ÕÅrê ™À÷WœêÓª×Âg*ÐâaVY>o3Œ€~ˆEY?rK Ð(‹*ò »Í¶ü?/ÎÝ‚R¸N¡€iZ¿Ÿ…ËüÝ÷ÇÙMfóD|ÌiK¼aôù$?ËiôÙ •ožù¤­²jù»›¿;Eœ‰p«áçÏ ‘¬š˜ëjä×ζòøZçjodãkÖó_F€4˜•¦Íœ·’ À'ì±>$”cãˆ×%vÚø¸‡©¥Á`’â)É †8‹%˜»MÑW6kU•£ ¨L*.³% To4ÎÙªì«<]üê¢E/–ck]1îÃØrãÝwÿ1>&+éa³Åô;æ ÀoÖrë«G7nÿ×Úµ_—Vó&”æ°P`kæº[€k_ëp&þºãN‡ã«h.¯F@'¯(Ïš»Šú‚³_Œ¡µ—$,&Ìc¾óáÇ&&§¤þË!I™=;µ–]ÑÁЫS$ÄÅz)":•Y+`÷‘Ó°yoŽ´çH®Ád4ž±Z˧¾ý¼oR¸Âa¡”ù»÷ñYWÇÅÅ¿nw8Z1oµ¯Çº¼ †³E—úàÕ—–cN.v\äO‹ð¸~*ÊÌumjëýS‹ëz¾ƒÇW±«ËNÆW[ÏÙF ÔÒT0jì½TÀÖÕ?¼Mk‰qªË‚KÌÝxòw M’dµHMœzóã¤+{Zg¤‚Å´'ˆ‡ê×nÂðܳƒ¡k»–püìÅ„RkÕ]ýFŒ.Ù¾vÍ[jEYæïW3æüÞl¶¼••‘¼¹¹¤êòvâl~B…î¸bÐë® kÉC$¯üõ¿rì;I¿uÍÊwÅ Á®}”Sª†¹ölµ¸ö¡*_²ðøê JÕyêrÈøJ³ýFNhµmõ÷gü¨š³2Œ@#D î ÊA<îç>þû¸øÄy»·7<ñËɦÎÙ-!l¡k2áó§{'›/£ÑôÂýO>ý{¬:×P$™?ª—êgÞ|ƒ\É]ïtÝã™q¸Ðâwþ(•` Z+ʵñï›> -Éÿ•-é›Gb,A´ ÌÁ5“p"¼7“Éòç{þ0ã,Ñ»²\•âl™?ªêeÞ,¾­•¼ÑuO×?žé›²ì[þä¢7Þ¢h2×þ Y'o˜¸æñµüUrç󸾇Ù@ºÈç0Œ@h­(SùäO›Ý%5£Uë—[·H•î¹~ ðœüD€pËÊH•âŽ["ž®9TÕǼùI–";ñÖªy ÐõOr€‡ÈŸäBkþ\­ š DÑ42×.¸Þ1×<¾ÌTýÃ0¾ÖoïaÝ! ÆÚ“¥ŠÊ&«'½jŽzíµÓÑ1³Å‡˜è ž“ÿnS®b¢ ³úv}K ÕàÐ]cdþ¨ªys‘oûˆ·;¯j¤ëŸäÏŠÇE¸`hÅŸosæb®ýAËKÞr-s†MáñÕ þ ñøêOÓ8/#À„ oÒ^,Ud5&«½jNlšžv?Í’@>aœG€ð#cãâÁR_­¬ó2Tó8_âLÁÉ$Zò'ªöeÍ\û‚’yBÄ5¯>òáO6Á]ÆWšÅyF Œ­({h; âš7òšŸ D}®Mç!?ïöÂçYnqÛÔGUã¬6®2T>ÕüùAŽ—¬N ÍœòàÅW™Âã†.D.sí…³@ùÌu`Èœá©dMæñ50 =ž‚ñÕcÝ|€`ô‡Y´´Hâµ` ×<«õ &‚ó$«­ÐiÑöZeÚìv4ÙÀdÒꙢVu>ý¡ù¦ Ï„¤¤ x¹¾Ðü¼´¨•dþ¨üpñ†ó4ÎoT**«ä)1pKPå¨u²àäˤé⬸TâR‹?-Âãz‰ v®±ÿA'½É©¯\Øq™3<7ìã«rZn­„ø8êŠ>’àÎëøºY}€Â­`¢­eÒL¨ly 7ÇÄ´NŠ•0˜ˆ.4–}9§á/VÞᎉƒÝÒŸ{þ|øíOp4÷ÄÆ˜¡W§6p÷uÃ!Æ¢dn›áv'e¡†Vk[Ì@ ªr›1ðNþŒÆ¶TÖR§òÏWno7ìg~}3d¤%ûÕ‹Ê*;,[»Öï: …%e²¢ÜºE ÜsÝÈÄê>_¹ËÞ-—I>‰iÉM`ÂÐ+àʾ]üª'Ì2o((x>¹^|hÁ_½æ9$ìê+ë +×uÚRëoc–S¹ÖÅøŒœ^¸T ÷:q**m”]Э çÕ—¯™ÿüò ŠåmÂ’æ•¿ãê!€3׺F´ø#s×ÀøªÅì}á2F x‚ÖúÜXªhùÕ ÙbižÒ4‘ö‡=‘¶lÝ΃›œ½X4SÁ/P9.)³Â+¬€.m[ÂÈ~Ú+T¾€„a¾ù…™˜W|ÐGøz dáK¹˜ÇÅŸÁhȤz| ŠË çôEÈH¯Q¸±OðÈC…úí‡àË7A¿®m!!^û¨$gÏ[šcS•ÓÄÆjñG0ÔOÉaåº~#köD‚œjĵ‹3D+lã«79ý~Ó~¸¢c+h™Þ´†PÅ=À>÷ÖWÐ<5 ¼u¬¼>yîÐ[eº²Og¸jP¸p©>ÿa+|õã6øõmã”Y4ÛÖp|Õ¬Í\0#Àhƒ€»›§_5‘¥Ja­çR¹¤„ÓbÁWè‰q ðaOg.\‚ßÿüjHoÚÄk[ÐO îš4Lì;µií[5ƒ#¨¨é%ž"™:AÍc~ÉüQù¡äÍnwÀ;‹×Áø!=ë4Çù÷ÞLäœq{Œv8~ö`èïk†÷†Qý»â 8è㜫‡õ¬åÆa6šdËT·v™0nH¨¬²AnÞeåªy€ð$yÀ2IÉ2¢6þ49,\7ÔÀHS ¹–9«¾~B>¾6$§ÛösYƒÝñ¼dõ°VTÁ4´woß š¥$Aÿnma0޹ÊDVfœ–úwo‹²Ùøv/OyXÓmÇWMÛÎ…3Œ€ºƒ&KUmk)ÄÂê!+Ê’$ÿW·å–vß £ mf3°;j[/¼Wf­€“èŠÑ¡u†·l¡?æÄ•8˜«ÑQ–íÓ´²´lÝ.ÙÕâŠYµêt8$äË8M¼Ð6-uÓ±3å]dö–*P1Þqð¬Ûq>û~«|=tÌ ·Õò åzüQxÜ©³çÓ‡šZ§°qÝPÇ"ENâº!ܯá¬ÚêñÕ“œ ¹Ä€Ä“þÓvÝDrJ.ô ë-Ë/’å”\¥~Ú›cvó–]ýcÚŒ¯ê·“KdM EV‹D7±„Ô¿ÕŸÎ|ôTXôõ:HIJ€!ðcõ§ýx¢›¦ÀÙŸSÊ+—Y]~CyU9ž{þ2¬Úzf?ð3Ù%BYèK‹–ɾâbßtÍ _ñWžø…Ø%¯ ŠJ‘§DhQífñöWk`oµzòÈ>0z€óFK®4|óX++ÁfsÀ”k†B>ø.3õø“Ãã:¿Í_«ƒÚü 9×þv£±Ë)ö×#×þbQ_\3´éøêIN ÑÅéO¯|äêÎ??úNÞŽî÷L¾ÒµŸ6.£œíÕQÞGî3þñ‰ëøÌû¯r¢D>êÇNçAQi9>@'AÏŽµ ]'i´ám|¥‡Y»Ý$½>wÆfªçbF@'h¡( +¤B‰“:tÙ}3þ‡îý°e¿|ðö‰C`ì îòögßo‘}\ÿtïuºšùBÑ %ÞŠÝoª]^ƒ !«Ó»KÖù$–ãL«?âÉ»\M›$Àon‡þ‹X½ý œ¿X·MŒ³~Ô/6¹I<”Yi" gÚ»tE÷Š¿ÝdE‰ÜnæýæÙÚµóÐ)XðÉJHĆèohR­Ñ±Æ÷’ré®k¢N±v—GWûÿÛ‡P\êäø/Þ._Ô@ýÊi\û®àŠÖÕ shÆWorÚ±uKxþÑ;äÞüó£ïÑ}ªtm› 7Á¥ÈèPZ^!祙mn;Ρ\“åmÐ.Dh ¾ñªø±_|¼b3¼´è[øëcSü¾ÄU€:J¼]%†øaÖU/o0Œ@èÐê†Lƒ‹Û&ô]ô­Fúh¤úÉQ"ë¥å÷Àº‡á±»¯ÁÙÈ­T—I‰µZ Ô¢Lm+ÂôNžË—1#eþLJßÉïôéÒF>7>6F¶$“Bì.µi‘†þÆvØ{4?&j äƒLé“ïÜ}pê;œÍ¤5–iBËÕ…*Êr³”ò!¶åýÐ4‚îRH¹v×öÑÇ_ä#K©I<}Ù(äTð«ÖÊrDÙ2Zþø*§fœJ3§{ó&§ô¦‡d•do>ÐÄÙ/”²¯ìGlŒƒ,eÁZt•:}á2´kE߆,)±®©4t³5uò#À„­eÑ™ â¢Â@פ ™Ê ÿUšþèv´^–ã‡'4M¥ŽºòSÖÚ’¤uù2¤Ð-L}ìNçü¥›áK‹¾'ï› mZ¦¹ö·D—ŠX/ÓóõîÜFÎÿÎ’u8íßèÕ-Ì•õü™mèŸN–£ëÅ–½Çäv6~¬†2ù šÞû®½·¡á£uå¯qÈ©Ü/-¸Ö¢L$ø*§Ý;´’] <DÛþ´'þýñ÷ð³Ñý Ý¥Îá,CuS¾9 9½XP"+Ѥ0·Hs?“FÝsÕùß8dB¾r)Œ#à 5eO–*Ou6Šýä/Kß¡ÐÔaÊ´`æ½Ê¿¼­4w©Hd9¦DV)eв7”~;e¢ìjñÎâµòl”?ß4O©ùp¨ ¨ ž}ó+0›Lò”w¢²·)窓‡–ÓÐbNÙ×k£Z6k øÅÕ8¶þÏ¿³Tv¢·;Ù-Ó!Æ\sKZoóh¡Ù/z¢ìß:~®“xí$dˆA fT °K [ª\.Ö Íi3õ3¯7tÜëÉ|0(Z£ E $tSA Šñ# ²B)ƒÄÜ„þ´è …Ô¨ƒþÔ„†ä°¡ãUªþIZr­eÙ^‘FNiÚÆYÜ Ï\n­Â9Ìk??‹ßpbF@­(ë¡ÜFÀI‰î}™ÝåÕÕ>«+:¸1Ú!@ou’C:q‡vá’F "`E9"iåN5f´ë&‚fc†ˆÛ΄~˜ /þ\;#BXQ!Ø\#.Ñ3W†« \/#)hñ0)Øp?HC hE™-U‘vIp"ÚÑ3#²‹Ü)F€`F@m‚V”ÙR¥6%\#À0Œ#À0Œ€ ÈNÁ%²T±µ*8 ùlF@…Ç:{þ Å.ÞdF€`0 ¼¢†FSe%†2¦ù‘9E6›]ž‡52zãì…×aŸI}ò·/¶˜Sä irʳ‘smrO†Úõ¢¡ ôr|õ¶ƒ°vû!8uþέk†öuÚ-c ïR1<÷Öb¹™f³ S$ÁÕÃ{ÁÐ^å}¿{ñ=¸vD˜8´§«+y÷‘SðÞìÚÇê"0óŸŸB~A1` …› ¸óê!@‰´ãà XðÉ0f@7˜‚ACD:y6_æ´IB,<ÿè0k¦š¥h‹ßnØWöé wO¾Rœ¢¯u”†Ç­²Ùàßn‚}9§áRQ)¤5mCzv€ÆôÇ(™[]!Ž)èEÛÌt¸}â`Èl–"çåƒðì÷B:ž#Ò“ÿø†÷î,Gûx­.Ñ*§òÃ,Ø Ìñê"Ê¥1Œ€Þˆ Eyî#ðÁ7aò¨¾ðÀMcÀZQ ÇÎäˆG¤B.–À©s— «yš«Öíø c6CäµW§Ö²uÙu°z£*ÊÛžtù0oÝŸý»¶ëéj¥>ZájNx7N"¿”ˆO_Ò t¯¡·±‹/ÙÃGK®µ,Û/Ü"TNý€33Œ@ä!´ë…/V*›ÝfµVUÑ€^ã,",ÏçÉ5uláµFz[V^‰¯~/Êç^³¼æ÷Ake•äpH%Z¶Ã.9JÃÅõk ú”oÞ{ òQQ&ËâÕÃjüÄÉí¢O—6²»EßnmaÑ’uhY¶AŒ¥æ’î‚®Uø±ß¡ç +¾QØ‚Öå_NËÑG9\‰x³ÙlåáªßS½áæúB~!4E_tòKö”ŠË¬ðÖ«áøÙ<¸€ßLÙ·–»§óµ?T\‡s|%l#UNµ_Ãu]r½Œ#à5Z…çùš[¶vTZ+.–†EQNNŒ—ÛJJ°·D¯êÉ=ƒ>j›YûÕ<ùº*“Ãá@?ä:¿² PPT*Ùm¶¼Z;Uþ㨲_oÔâ^Æàõèåò-.,.“}Óš&Á ï,…JüŒ”dú¸opOçG˜t¾Éd”Ý/¶¢‚LÛv»Cæ8œŠ2ñVYQQ@íÃTûÂrîý†ƒb3TëpsÝÝlʬÞe”à"Çé ½;·†”¤ÄZð\*“„Ã)¥>q­l°ÿÛa_©É‘*§Z¯þÓÍg0Œ@8ÐJQ¦\(’µ¬4¯¸¼ÂPf­úb=”)=%Qžå‚f©è…7WOég£ûCvËôz‡S’àúO*ÓÑÜÛr‹´ë%‚¦.¸&·šõâÀñ³òÃŒ;j’âá—רw(µZaÎAôæ©N¹¤SÈí*\rê+×õ:ãۥ̇u|¥æFªœz_Ãð0ëÛ¥Á¹F@mÔðQ®Û&¥@&ÇÉÃw¢ÅÖ°ûHí®êž¨Å ú±ŽÅÖî8$O F¯lÏ^,²2ú’胰ýÇΛ÷Áé —aÙº]pâìEèÙ©/§k’‡p$<Ïçž\‹(ñV£>WyT~¸xSvdÜ Ð$>g¾Ø"ïÞ´7-ŠÙò´a4u-c1Ͼcg _Ï+Ó³ðãÌ*yjÀ=Ú)…|[ðFò€•˲kÞZ6ˆ"h*¢hŠª\u‡›ë¾]ÛÈJí»‹×®ç€ü]sp¶“#§Î‹¶z\·HoŠé&Ê{ðƒÀ“@~´b³ü0Õ%;ÓãyZÐkgØþ°¯J #MN½¯ô0«Å­OÞf} ´¢L–*a­RtI(´¶ïÙ¼áˆÃn/ܼ7G òЬÚoNÝý[{Éó°>ýïOa>ΛLÓÀù’®ÙOžËõÓï·ÂÜ׿„·”³‘8mY¸áˆ)Õ’ÏÈÙV`­fsä2©|ª'\¼‰Ñëö[Æ Båذû(Ð<Éë(½d-¦DÖfe¢%úØ^íwnÓRy(äÛ„#ÉÉVN“° îh­mòAS\Ó‡yÞu5d6O……Ÿ®‚§^ýþýñ÷øPêüÈÏ84Oöô[®‹Ùˆç¬„ß]Ñ¢üè]¦” GÒ˜kåuöñUàIrªñø* ã5#À4‚vá›6sÞJê'>]­î/•IþôUN .ô]‹ ·Þug»®Ýo{ìîk䩾p_XRYyÄ£û²ð'Q4¿òŠÐ»ŽÔmãá“çà¥Eß@Þ™Ó¯~ñŸÇãäïJõUà¢ÆƒH-þn¼ïÁG›·Êz8ܼaßu¼?¸ÿ㟼ÿv†L¥äÓ£6nqš6kî*:€ߎ¡uuÒ%ר¤ÈÓ¾â¦Eîø²­É5iDgCµÖ˜ëZœaŸt5¾† c­êÜi8¾jÕt.—`4B h‹2¸·T‘ÅÃV½P,ÚÊuK/wØløÃAóÞ†+Q° •dj+È[Í~n„Nw‘ÿãŸ}ˆe Œ o5“‹?ª‡ê 7ojv.Ôe Þèú'9Àúé«5’ ·ü…8<®î¸&ër ²Fo©$ûËu€×¢‹³êëH7ãk€ýÑÅi‚»Œ¯ºè/7‚`|C xE¹~=dÕ¤œ´aRÈi´¢¬¬¸dÛÚU‹Îäú!ªaùÄb£+n„ß[^¸|ùÍ{GøŠWøjaZ‹?ª‡êcÞ¿Öotý“`Idý'¹pËŸ×aŸx>ŸÉ\û •oýåÚ·RkåªÅáñµ<ÿÜù2¾†øa6ðNñ™Œ#4ZÎzAÖ2²œÑ@^†Kùöu?nOÍÈø·o‚Ï@ºçú†pZ°"‘¥ƒq Õl8}ìÈ[k—-^ƒ '\ _ÂY-%‹’•çâêKNMûîûóæÈ—_%oG÷íú‚®<æP&yðÌ_hÃã2×¾Ù@ž€¹n \‡kq†yx|õ”/»•Üù:¾Ê³²ÆûRçaÆ‹€Vв°( E¹!ŠÇ%våçƒjùÙýìlþeiÊÕCž˜“{ÈgŽÜÈ¢{êèÁw¿ùð¿äßJx EKX”ÝØÞzü-}ÿ®™ò º^îfÞUÉÛѽ»¾\ù^÷NÞÈ¢¬5îè)z&sí-?ö…ëzœasy|õƒ3‘UÉ_ãkhfEsyÍ0a@ è/^Œ{/µ{ëêÞ¦uD 1¹wÐBuÉ˱ûrí¶ª3MÒZtÙ¸çXÜñ³é[9C~¡N>†ÑžhÖmNÂ?l•¾\µÍPXTR¸sÚ¿¯^òÅRĆ>þ"· ¡lÑë{ºqj‘jñwdÏ®“ɔߴY¯{r˜·:ˆ×ç­¸hóªïÞÝøÝ7ë0k1.…¸4È_2U§Vßþ¢|® ÅKnæÚ 8u©ÅuÝrýü_‹3<—ÇW¬Ïÿã«2êCÓ9 #À„5´RO–*ñz,ŸTXd¿èÖn;´cûáaW_;Æn·Ús$7?â‘’âc¥”¦‰†8‹…nQ•(\4E£ øå¿q)>uäÐòõË–,)--¾ˆ`¢¥T’µp»˜»å¿µû6ÿt`ø¤É“±}™74í»áíø}«7|»tUyy))ÈdE&îBÉŸàÑ—5sí J˜GG\»å ›Èã«.ÝqÆñÕC+y7#Àè ­•Q²pXp‰Ã%—¦¸¤T¯“ª÷Ñ1K—^}ÛµíÒ­[|bRZlB|²Ùd¦)æ¢*Ùì6ke¹µ°¼´$ƒRì9°së!€,ÇJk$)[Jk²–Sˆ4È_·>ºdwîÚ3>±IzL|\SäøŒª„¼UT”•¡N|éÄ¡íÞq K?=$úÍŸ‡©Ü°(Msí¼jsíC•Þ²4ÈžÌãk5‚jޝa’Qo×cPâì­iä@VOúÊ_(åbù/Ó~Ù·•‹}¸ÄÿÂU7]çÐv¤&² Q"\h³…6ôáY#IQ&K2­éÃ:F¸R~-“àŠêsË*ó;q9€ÇéˆnÜÌŸSIœ¿ð„Çe®ñâm éMVä ûÃã«“TŠ=¯Î–ñ/#Àè­eº±(-žô_9¸“"H¡³d«®©=BÙŠîŠø$p!¬HÖHRŠÉ")‹0:NyÅ75IÌŸo°ªÊŸ¡qEôÌóf¯ôÐ%æÚ0uv«Êu²ýýËœù†˜úœ…çaÖ·Þr.F€P­ej¬r0ÛÂÊLŠ lñÀ5Y$•VI¡(‹5ޏDxPRä¤Ó",’ôú^,´Ÿ° …’ŒÕÈIpFÄ6óWƒÀEi­ÒrK’a–³ÉàIQý¶Ìu5`Õ+ƒ­õĵàI´MÊ0ºA hEÙGKu¸î`NVIRÉZªt¹Jr´)ÊuoÀÂrLkRŽÅ‚›aIÌŸgØ ½óç¹õõ0×õ1{ôÊ5s&ª¿Ö+gõ[Ê{F@w­(ûa© )ÂôšP(Æ´ÛѤ$ \êæô_`EÛzI¢MÌ_mFTãÂãÚí&éõ¹36×®"äÿ˜k÷«ÆµûâƒÚËœ¹‡OÏœ¹o1ïe] ¼¢x7hà"E™Rº„r,Ö¸+êaBI êÎúüeþêó¢ : Ë\kÄuýbUÛÜՇRù¤buô0[¿—¼‡`TE œŠ²²#4€‰AL¹Ÿ·ÌŸš<é;<.s­&ס)‹9Sg>̪ÜC.Ž`zQ”E{xÍ0Ú à)‚¦6µq©Œ@$# ï‡ÙHFžûÆ„V”C9WÈ„…óf?úZ¹FF€`F q# †¢Ì–ªÆ} pëF€`F€`Ü ´¢Ì–*7¨ò.F€`F€`F@Њr£G€;Àè «7F¸=Œ#À0QŠ+ÊQJÞ´%Þ $¹ÉÑ» •o’ÃqÒVYµüÝ¿Íߊ&Έ;ÿùÓ_x\æºú Vëêr5XñøZT5ÇW?ÌÖé9ÿe`‡¢,”cãˆ×%vÚø¸‡©¥Á`’â)É †8‹%x·` ù֪*GAQ™T\f5LÆg¦ÏžwÎVeÿ[åéâW-z±›DJW¸“ñî»ÿ“•ô°Ùbúó äÍd±Ì!Þ¬åÖWnÜþ¯µk¿.E„ÂNþ‰ É\ב¶FÀ5¯u8•Ü=¾êïaVt“׌# 2du*M›5w€úÆÐÚKæ1ßùðc“SRÿå¤ÌžZKƒ®è`èÕ) âb½‡Ê¬°ûÈiؼ7GÚs$Çt㫵|êÛ/ÌûðßB©l2÷>>ë길ø×íG+æ­6°uy3 g‹ .?ôÁ«/-Çœ6\ì¸4ÈŸ2U»êýc®ÀR-®¨ÆŸÃ<¾úˆV]î_u £>ö–³1Œ@°ÒT0jl,àôÖÕ?¬ðR âT——˜»ÿðäïš$/Èj‘š8õæ1ÆIWö6´ÎH‹9n/­Ó!Âðܳƒ¡k»–püìÅ„RkÕ]ýFŒ.Ù¾vYCíŠ!ó÷«s~o6[ÞÊÊHaÞÜ\uy;q6?¡Âw\1hˆu׆µäŽ!’WþP¦î¥Œ(SoÓ:ĉ¹öpµ¸ö¡*_²ðøê JÕyêrÈøfõ£·œ•`‚E hE™d?”丟?úøïâ“æìÞº}¼1#-9Ø>DôùéM›À°Þy—Šál~áľWŽDeùÇŸ°Ó^•-A‘oÂ÷?ùôïM&ó Ì›oÈ*y»PP:®{ÿÖÝ?­¾ËÄGþÂxf®}£·V®`¸®UP`”J2¯~b¨äΟñ5Œ2êg9;#À‹@Њr ¨5ˆßxßôI)éÍ_!eë›GL¦¨sCn.÷‡ §þÝÛÎ],”Îåï9hè–Öæ`nÊ–û’üÞ+ówÏf\c‰‰{“yó?%oyE壳;uÙ}`ÇÖãXŠP”Ýò7`ä¸aø}kþÖ5+—øWcP¹™ë à ”ë ª¤Sy| @:]ɯã++Ê*ÏE0­5U*Ÿü)b³³»¤f´jýrë©Ò=× ž“ŸnY©R|BâÂñãoKÄÓ5çê¡ú˜7?ÉRd'ÞZ5OºþIð9â“\¸åÂãª"—"hŠ(šŠ¦)7̵ŽÀ¶ýå:°Z\gñøê‚"ø ¿ÆWÉpháÄ0€ÛµJ½¦²…_rüÐk¯Žæ³wLbбhmÈV©:+†p›rõ}™Õ·ëÃÕøjÅ¡ÌÕCõ1o_ ÄÛ× 5ÒõOr€%ÅãBþú$Zñ‡E×$Š ©ˆ¢YsÀ¹Å\×E$Àÿ!äZæ ›Iׯò¥<ÍŸñU‹‡Ye[x›`ôƒ@Ð7i/–*²“Õ,—Ħéi÷Ó, ³[ê§÷°%„á÷6ŸðÕÊ:/óGõ0oÁ_(‚7’,Þ\hÉ_íF{ É\×F+¨!âšÇ× Xr²à.ã«ûð^F€ÐA+Ê,U4ˆ krÜÈk~6õ¹f4œîh„ "%IjqÛÔG(Ì1á¬6®2T>Õü©s‘8q44sʃ¬( «²ÚüùÓ`æÚ´|Ì«1×2gغ~x|õ‘_³…`|õµ)œ`t€@Њ2¸·TQ¹¤ÀÅà×<«õ &Bó$‡*•[+=Våí˜Ç“ܨ¨¬ ¸á戶»GÂ3!)iÖ¤Åë{™?*?Ô¼i‹\xK¼‘<`KÈ¢LòQ? ;uö|z E +×ÞdÑÛ1€ ‡œúʵ?ýPä ûøªhKÄm î4_#3î#ÉЫ_-Y<¨lYQ6ÇÄ´NŠ•0˜ˆj–³Â’2Xøé*È+(†ç½ÃÕ‡]‡OÁ»KÖBIYÐÔs¿¾m,d6K‘{;æ* Ê*;,[»Öï: ÔyNÎ)pÏu# ?Ø:y6ž{k±\ŠÑh€¤„xèÑ¡Ü}Ý•@ÿÕH”…"VX­m±<¹Jre8ù3ÛR=X)sºMûrNÃ_¬†!=;Âë¶2o((ØH¡(×ã/Äáq5åÚ7ÞdÑÛ1_ÉmHN?_¹¾Ý°[.Ž|SÓ’›À„¡WÀ•}»øZ…×|¾ríµÏœi8¾z®:ð#ž®…ÀKÔæL_ÆWz˜µÛMÒësglÖ¦\*#Àè²L¨hùÕ ÙbižÒ4‘ö«’rNçÁso.¢Rk­òªl6øÏ—k KÛ–ðÔ¯®‡&ñ±ðÎâµroÇD!å•ðùÊ-â¯ÛõÂÏVÂ[Àu#ûÀ܇n†ßÞ9úvméµçƒ~à¦ÑðÄ/¯…‰ÃzÁ†]G`ó¾cnË t'†ù6Œ†L<_X$ÕÂ×Å•OõÚÆPœ÷í†=°ð³U€ÑµBQ]Ðu<`A¤( × j| 74!r5åÚ7ÞdÑÛ1þ¡“ç”'_ä¯m˜=õšÏÚ´Hƒÿ-ße墚 ×>qí-.ÎðTMÆWÿ›Ôðž®…†Ï OކÆWùaÖaŸžÖq­Œ#J´Ò.¨\²”ÑbA%&1Îb©Q‚ìáé —a^AQ¡é7óß•7ž2zv¬ù± ¨RðfÝ¢ÚÍâí¯ÖÀÞjËÖdtÅ= ›(¾]¿KÞ&_é!=;@ZŸÕN8#Ý4Îj/—Y]¾šåjZ–•¨F”„ËŒüùCH¸VrãM½{iÑ2|ð¬y3C.M1(ï¯<ñ‹ZýõUNKʬðÁ7?áƒl%Øl˜rÍPÕ¾#¨Õ ÷(µ¸åÐ:dãk>üWy-\HNÔp| Aë¹ F€P ­2ÀÝXª„R¡ÄÉ ]€5ø~Z"ú$“•H¤ t¥ ÞŽì—ƒz´CŸçrxþ¥0ï¡[äóâcäµø!Ku™µÆ/zhïNÐ_Û~øíÆZõRþßÞ9Qvÿ¸„ÊõKï.Ãò˜zóQ”šk%Þj”«vyj´)Âʨõ€N¼ÃR·7Yôvì7·›Ý«·„ó á¶ ƒqö—ú—†¯rJî:ó~s  B;‚Ÿ¬„DüP¶÷†ßÕ¯ÕÓÕ¹=¦uµÂšñÕS#|¿ïï*w`Ü!@­‰—0tã³ÙìPˆ.”.\.–×iɉ²«§côÕ;Ý\q†švMÞ¦ÿfSmc }ðC_Óï=š+—K¾ÃPY®›O>XýCuwÈjÇðD’kµªÐ¢LµÚ)å(åCl;ûÚð¸!ç:P9MÀa’ËøØÙ’,Ël¢ÓÍJyQø+§$ó½:µÆ2M(§5ke™An ~ÕÂZYŽ(;È&òéPbí! ïfHF h‹rà„tïÚ6’ãäiŸ&íßý´Z£rÛ,%IžþÉÓ1Ñ Þ(öh/þÖ[÷îÜÚ´Lƒw–¬ÃiÈ†È 0¹u_Iå çò eK}L¸§¬ëѡƅC™/¸m­-IZ—\ï#àl·òAáqÕîEФ2Ì›½Ò}Ù¡ã:X9m‰®OJ«ºýñUNmè×N–£ëÅ–½Çä‡àìVÍê§Ö·\Y¸eÙ¤H:Ý‹LÐÃ,'F€ˆ ´V”C "ÍSüÀMcà-œS—üŽiVŠé·Œ‘Ûàí˜h$ÍŸy÷uÃÅ_·ëßN™(»Zдsô•<¥T´7¯þpPœDm D_ÕÔg¿è!ñš9A³ºRŠrèšäM½-¼¢ck±éq틜•Á³o~%¿¢)%ïDåA^”=VÆ¢-f£Dî0#ÐHZQnØRårÁP’IÃ{-ÊÔoxyôvyš8zE«LÞŽ)óyÛ&«´ð5.FŸæØ‹üXœ“™ fÞ+þò:DÌüÕÏBT“jÕ„ÖH4ÔÜqãM½óµ ÉéM8u$-!JZr­eÙªÃãîZP½.`ZQÖ“¥J‰K]%Ù×cÊ| m'¹ñlè>Î05°œÖ`Á[Œ#À0úC x+YªÂh­Ò¤Ü"F 8(<îÔÙóW ŸÍ0Œ#À0Á"´E9ØðùŒ#P9<.ÈTÆ×>ÂÿF@ÐìÝn’^Ÿ;c³ÚÃm`í`EY;l¹dF 0(<.'F€Ð-ü0«[j¸aŒ€êð YuH¹@F@—¸‹ ©Ë†r£Ý#À³º§ˆÈ¨…@D(Ê„‚pbÜ!@ÓøYÌf·QÜÜåÄ}n"h†¼›,§!‡¼QUÈrÚ¨èâÆ2Qƒ€ŠrX,Uvö½¥ä`%eåЪy*\7¢¯~Öf·Ãþcgåh[QÃdwtæ??…‚âRøÓn‚Œ´$¹§Äÿ¯Ÿ{½k"toß >_¹¶î?.‡$P¬ÞvÖn?§Î_ÂiüÌоUs˜†ój{›iAœËku`9UÇÆP Ëic`‰ÛÈ0þ"´¢.KÕö'a㞣ðÐmc¡i“8|ê¼܃Ø{ô |±j++Êþ^ :ÏÿÑŠðð|jå†]Gàƒo6ÂäQ}å 4ÖŠJ8v&•dŸÐS/Ë©zX6–’XN SÜNF€ð e_*Ñ"ÏqTzšchjš"àQ:š{Þ_¶Jʬ0óÕOàŠŽYpפa@!¥?ÀýÇÏæCjR‚¬@‰pÕ”ŸB]ç”À´@þæŽq`1™à£›dkd‹´dÞ§ŒØ]®ƒÂÞ~³~ìË9#1›ŒÚ:¼õ*8w±þ»t=ž—ÖÏdŒö5Lu-ŸÈ?#0ºW9Úâî#¹>=}þÃV¼çc–@IDATèÖ.ß2ôqÕIÁ`EŠ ð¸,§âŠS­‘Q%§ª¡Æ1Œ€ž~å0õ®w—6pöb¼ùù¨à»Z‘Þ4:µÉ€ +}ïÏFÀU>ÚîpÀ«~‡ÑúªäÕZgÀxÞITš)•Zañêí°7'®ÙZ¦7…d &Ò¯[;xüžIЧk6|¼b3”Y+äü_¢µ:Æb†ù܆Šz+yßÔ›GËëE_¯ƒø8 º\ YèòÁ7äýüô 2èŠÈÃO`·;¼VXRE%åнš¯™uxÂãª"—"hŠ(š¡ì2Ëi(Ñ]Q#§ô0A´á¿r¸Œ€~…¢,iÑýNmZÈþ©‡ÐåâéŸÃgßoò‡LIJ„TT’éã¾ÎÙ-!³Y 9u.¢2}ËøA@Vä{&_ q±°_Ï‹d­°¡R|-ŒÜãcQÑNÀíîÐ<5 zwj#+ÛŽ“³+yQ¯ýCÀ†Êñ-c¢¯r|·iŸ×“ÏçÉÇ;⑎“&rá©¿ASEÓS6Õ÷³œÊjɵ–eû}=D œºÅ@‹‡Y·ñNF€;A»^+Õ‚y³WzêÍn³Z«ªh@7xÊÈ~úˆë™o‚¥kw·ö@“Ä8˜8´g½¢H¹¢Ô¦Ú=CÞn‘ — KäýôCVèØ‹ëÿîù°lÝN8©ÈõöC>Þ£C&ü°e?΢`ú`ŒbJŸ“•õw¯•ÿÓ)ä¹ ÝDÔLÖÊ*Éáj: fáÕeÙ%G©¼ÚTzx¹fxoä{ íÙÁc1ô6€RYy¥Ç<á:@¼Ùl¶ò×ß@ôL-¹ŽV9 ×Z¯^£‘"§Z¯âËç1Œ@hZQVX©Ü)ʲµ£ÒZq¹ °TuE™ "åö&´4’Õø(.0´>€d¦t•ÞYÍåíó—Š¡'ú/‹¤œ^ Ó²r÷ö™ðø/¯…btÍxâïÿYeeíߟ¬„C'ÎÁTÌ{¡Å™R\¬ÎßáL ZûÕJv›-ÏÕ( 6Uö ZñhséAhÝŽÃðÙÊ-ò”oîÊIOI”g¹Ø}äôêÜÚ]–°í#Þ*+* ª ËGØ£¨Xk®£QNCÀµæã«âñk3äTëñÕ/@93#À„ à]/ÈRUßZE¸P$kYi^qy…AøøªÑ[²èî?v.•ž£§Ñj{ Ý,šÊE“1¹;\ÆcäÑ­Ç43Ær´:_@yåæýòtcž”(²•–¡GÐÜžË7î®Õd:¿¾ÖÙ¯«\§ÅìœÃ¹CV4Iˆƒ¥h‰¦úiÞXj›Ú‰p$<«**Î`ÙJ¬ÕªJ.“ÊW›·`hF¬o?~Ú“ÂÂ_·Lš3y,~x¹vÇ!ùMqNþì4}\8“àäÛá’ŶÜ< ;uöüA!j«¦\G«œúÊu€+e^“ñ5Àv¹N‹9Õp|uáÄŒ# ‚¶(»é¢R ¯®'ܙվ㻜†!^^™»)Ëã®K8CÅ7ëv}¸En Ãzw†IW:g8èƒú­E«ãŒ|Œ³^´†G¦Œ——ÿ|µžþ÷§²ÿ2͆ЧK¶ÛòIÑš8¬—¬d-]» FèíZ5så¥6¾ÀYv:%û.“rL3^?ætœ§wÑ×ëæ¥D3_\ñàͪ» ÑÛp>÷$ùx(ñ–ë òÇU•Ÿœžþ¨š¼Ù6ùô~ݲ¡+ÎhqðøYÅMÝ} Èánœcy‹Ìy§6-a@÷vÏÑú€àäë’e×.¼Eý! ëª[+®£UN}åZpîÇÚÅž£ÙøêG{wÆfðF€ˆ‚öž6kî*BçSCkLd¥ŽÅ…üÒpiKƯžœózï®m“¾c|Ðuby®TŽóãz Q†Öäø¸Ù—Xœ`Å™/è50êP &›Í.ŸkÂéßDÚyè$¼‡ÓÉ=yïuÖ´ TTVÁkŸ®B‹u<üòú"ΰáôõÔ6WÆ6^ýßwÒîç.½ñÜÓTá%\hÚš’ƒnœÁ¦Zü=ðÔ3k{un“¦6oÁ6ÒŸó×A¬OœûS®¿y‰·]O½ùç9Sñ\ô‚ó¸ÔãÏLù[U½üÊ ×Ñ&§¾r]¨†wÔâ ³k:¾6ÜõrèIN_§Íœ÷õ?ê¯\#Àè P½Ö‘ŃRÚl¸TÑúä‘CË÷É5>éœ9÷©’¼)¢ 8{}p§L4SE]Êõ¶éõ¡RI8ÝÜeü@Ü+Èýãø™‹ò<Ìâ8­©]ÞÚ¦ÌëÏ6áG8^<{æ<ð%œæþå)¯(KæêÑ‚7O•k±ßyhQ²ïe ÞHð,—\à¶ÚüyjEЬE3d\{“…H“S¹®ÅrªéøêébÒb¿žä´ÁñÕ ™ÑåP‹7²Z@Ëe2Œ@h¡(Ss„’,‚ÊuK/wØl-ÍA¾»5‘»Æ„¡½àë5;á•WàìðcÂò´rZ÷‰p#üpº‹ü¿øìC¬ðʲšÕ»ø£z¨¾ÆÎ›šàø[–à®’<Ÿ^7È ®µà¯^)‚¦‡(šÉu¸ä4D\»8«¾Ž"f|­wá†p‡à.ãk{ÅU1Œ@°8¿B ¢”£ÆöÀÓOo]ýà E1dÆ¥²i¾5Z⪪*ééûrRF«ÁyøqUÿîíj›zñ`cI4•Ü^á*ü`lN ×6Dßþóåé δq`û湇vo߃x•âbÅ…”.ºyª•\üY­¥Æ„¤¤¼¸¦éã;ojão9‚·­«W¾q*çÐQ<Ÿ\e qqËÊÔ½x P¦Þ¦µÆ)b¹‡œúËu€Üº8Ãó#n| “ OÜù2¾†XFƒîÀ0#´¢L r%™ZC¹ÌIAŽ¡åÜ©…)ÍšIVClw õ,õìÔÆàεórR @–Ä·ì?f8}ìÈ[ßþÑ—x˜¢jE‹LôôJV­T‹?t¸Ø²M¶¡Äfìϼù±’·£ûv}±aùÒñl¡${ä/Ä7aæÚwJ=æ ”kz?P‹3ÌÊã«w¼¼UrçëøbõÚ~>È0Ú"´¢ì¥y4˜“kÕAʲ¼;°/7%½™•å®»Ÿ”Z5O1¤ãGqœÜ#@þŽ >Yé Kò©£ß]öÁ¢÷0'Y#Iá¢H*ô_ _–ZüÞ½3'#+K*µ›z3oˆxIÉÛѽ»¾\ùÅÇßà)BIöÊ߀‘ã†ásfþÖ5+—4PZ‡™ë †ë ª­Å–Ããk`*¹óg|eE9°ùF ‘" ¥¢LˆÁ\(ÌTŸ‰”e»­êL“´]6î9wüìEŠñaHÃÈk45[´'šƒuÛ“4ôåªm†Â¢’ÂÖü}õ’/–"6¬BiTÛíB -þŽìÙ•c2™rã›6ëµqOó¦D ·ëóV\´yÕwïnüî›uxX(É òG ²ÚJ2EÐ8jlû-«8V§Ùâ/s-ða­×>Tå-K-Î0#¯ÞЪ>VŸ;ÿÇ×0<ÌúÐ3ÎÂ0Z @­V‰”cÒziª82S4š..µz»I||bÒ°«¯Ó®[Q&“9g¨’âc¥”¦‰†8‹E˶aô—(\4E£ 4O²Ýn+:…³$¬_¶dIiiñElñe\h:1²HRøjšN«Á<ò—˜˜Ôlø¤É“Ûtê2‘yC'q7¼?°oõ†o—®*//%™¸"ÎBÉVW“p:«•ô§³[³×µÅ\» ð¾¡#®=r†= ±–Ç×:Tºã.ŒãkÖñ_F€Ð+A+£d©¢Î-˜7[¾×é(Y8äMpˆ à)Õkšg™öÅábéÒ«o»¶]ºuCÝ9-6!>Ùl2“‚Êd2m«+´£¢jDÅ]Ƨƒ;‡äæ iÂHsÖÊrkayiI>¥Øs`çÖCX!ù!+­‘¤p ßd²&kávÅÊ©AþºõÐ%»sמñ‰MÒcâãš"oÄgT%ä­¢¢¬¼uâK'8ph÷ŽãqCZ†“?¬Þ™<Ì£,Óºqpm0Ä¡X¶¢ã[¨ ”K1NH8[A.î&Ü5K:ãºAνŒ¯šqâkÁ:_}m:çc0"´ŸƒC2̪n¿;E™fb ‹'Y>…R.ö‘o-íÇ%•‹}¸Äm²”ÐBIœãü§á¯Ñh4 píß° ¹þ¢Ëù«“SÓGS•UÖŠÊÍ?®xY£êñ~/'Â…R|éfOØ”ãBÖHR”É݂֤°Ó1­,ÉX´+ ®¨>Á…Ø'ó‡ÊüN\àqz ¢wXøÃzÕôÌŸ?˜^uÍuZFËŒnýΦŽÙm¶âªÊòCq I½ð¯¡¼¤øÂöu?¾MÇ4JzãºAÎ]Œ¯ñáK±zãÌ—6sF€ÐA+Ê8éºPjÝu‹)¥Å“þ+wRp‘­¸¦öeK(f¸KûDAD,11Ũ0“ÕÛpùü¹åÍ3[ Ã͘ظ¸l´xw@E~«F-¸V¤ k$)Åd‘ áEŠ §¼â&€›š¤Fß&½÷½PUù SxÜFÁµµ¼ÔŠò(3ƒo}’rsÿ­Kï~¤(îïß*»Ý'gN?å;u~çT•k¿k¯}B£à¬v“ÃòOOœ…®”`G xE¹áº•ƒ¹ØVfRe‹®É"©´J EY¬®)È1qqyf³…eˆKN.²UV}Õ$%õVúß¾{ÏŸŸ=uüÇâ‚RVÕJ„%å@NJ°Ò¢L¯ïÅBû »P(ÉXœgôGlë’?gsCú« Ž*xÎà|¾ÒÞÔðKÕê’ë²âbˆ‹O¬À÷är[t1Œcâ‡âÆÞwáLî£6›ÈÕLšp­BOT”Øfùt«gaz˜Uárá"FÀ_B¡(S›ÄNkº‘¢G¯îI$e¹®’LVê)ÈX—œðãÂs–ØØNô§U«¶püÀîW»vƼ¦3F]wÓ/¾~ï?ÏÈ™Õý¸l„²LÑ¢T)¸¨Û Ï¥5 þ<7_ó#êòÞиºç:.11ßü´&V[wéb;wæäsèŽñ1J*ÏI]ÆÝtû„o?~ÿmXW—ku©{ÎÔéfÀ¥¨ÎYfOdÀ•¢L­«;˜“Õƒ@R–©ÂåB(É¡W”O›-4w?NÏÑ*3nÍ·_íWQù§¤ÔÔ…´/.>áÖkîºûÓoÞ_´šþ«˜›ºƒ9á#\1H9‹ŠÕúU”îùó«7êfn ümò£ËºæåðœKQîÐ9fÅÇïýԵπ—⟢>âÇÀ¿¹òê¾X÷í—Gý賯Yõʵ®9ó\ò©ÏYxf5‚‰‹ew„RQõ …aR…bLk±r%™‡®¹&3·’Miä‚QôÕ¢×?øåfN2˜Œ7ÑþÌÖm_½îî_ùzÑ›Ç鿊©î`NÿV´­—$Ú¤;þ ®ù[8oöà£K®cbcr FÓ@êOó™4ådÑŽ•Ëžuó]×àÛŸþø?¶S¯ÞoYMW­úì3š·Zí¤g®uÉ™ÚPžž9  ;| #À„ 5e,UÊ~ÑÀEŠ2-¤t åX¬qWhSlLÌQ0‘aÍÛfs[\ÉþÈSåCfKòpÔ[[à¾ôVmÚ~|ÏofŒx÷ŸóÕ¾ &”Ä îü§Ï_Ýñ§˜þÀ¥+®Í11‡ªgnD9m’)ÏÉɱ1æû ãüÈ>îÔ©kßEpoò¤Uo¿Mo®ÔNzçZWœ© ~€å鳻ŧ1Œ€–7¨D–ª­UÊzi–¡<‡|m2ÇìÅ-¥Œ¨/»a›ä6¼ý çÌ1†Ÿ ¦r:†ºÅ¦&~4gÎÈü¬f;b@Wb¤çm]ð§2ðÚXùóçÚ ;×øÖçpœš:ÞßxnÎ.”Û_ &I>n4ŒéœÕéߨ8/ðéíœÆÄuØ9ÓoÜx:Ö˜8óG&9/#Àhˆ@Њ²†m yÑ1‰Æý¢R ŒGв+-˜3kÞ|ïA-Ù©ÄJÒØ3¶Üo™óJ²+o0j -œÜ"`”G:*¶aÁ3³>ÇWROÖì“î6ûÙ÷¦½öšÓ§ªæo1Œ#À0 "Àв¢<õTž ùÎ]RÒ#sþ"Y/²¼6ï©O”7aI‚ÑÖª¢}öYrÉp—È„™|:¢}!&¸ÉÉfz:-"hŠ(š–¡—ó,æ—¢Œ´ê¶ ±{ßú¼îÚ/IwJ'óÿñ…(¨»$®I–Óšª&îðâ}ü0Ë×#5Ð`ÈIÀ´YóÖà«Ú´Ëh2N|í™™+‡åÍé3çýßὈ®ÄNü †c&3L!«3f }B!4Þùè»ÅÇ5¹ƒaw£!;šÔ-/Zþ#ZÅàÎâÆa›d[òÎ_æÀ¾ÓëP§•>Z€C?§Íœ·’ªE%rlªWµJ”OôÙóÊðª‘#ÄYš6ýÇœßRäJW¢<Óg=û7 ¤ßºv`«Ålžò¯93HÑf9uS{ƒå´6ü`¢ ea¥Z0o¶|#nìpN5o!HÒTê‡ÁhxtáÜY¯¸ëÓÔ™óî6¤·Pé“?ˆÄmŒq =³òÃ^8~|•ážß?uSlBÜÿÃRºà"%'Æ9R’ q )ÑQ™¬UUŽ‚¢2©¨ÔŠ ©ÚªìO¿õ—9Ÿ" ¬0kxUL›5wߌ¡ucOSgÍ݃ŠòÔ“Å0¤ú!µ^·ð¡v–¤¹5 ¥’Ýö‡7þ<çm:•å´±År*à5#À0Îù‹ƒÂÁ!fUŠ2º^ìG+”Ü%ü¨»'p^v֢鳞C7 ÇûÉÑC`›ñ›¡gÆÜ:âzKÌUÔ²ûf5O±ØútichÚ$Ü.¢=É …%e°óÐ)øqËþN§ó þ7mæÜµÖ²KSÞ}ùå³ï$ ÚÑâþ{DŸðwãE"+Ê›Ô3º}çµggÍÇùó þá¤XI²'¢¼¾vïgÞ‚!ëSPN³œÖƒ™å´$¼ƒ`¢à­› ½ p‰d‡ëƒ>´{T”©»è³¼¬¼ÂÚ•äµ»h‰‰‹„7à>wO¾fM½Ñ4ªW@%9BÐQ§„áBøNf‹yX|“ôÍ÷>þTo¬®¥ ßt¨ÓÒð”Báq§Îž?(<µ7’Z%Ãöš–IQö˜ðmר •޽5r7åtË©GØäq‹åÔ3>|„`¢ˆQpÕ¢Ë,\Š2º9öh \ã¢çþüµw'W”—¼/%%ÆÃ“÷^g¸²Ogüž¨³£ü0áC8!^¦&‰ñ±q KþÈ#™KT_—rx\‡}~”_^»o0)eG_¯™ñzzý¹9û¶}ûýU•ÖÒÿ²œ6€VÃ,§uÁ¿ü0[ÞÃD*Q­¸#õÕy3O¢ûE¡ó˜ÔüáYsۻˇû;r§ˆ½|9'..!i˜Ål”½s"de¤z8…w»C€ðztʦŒ„¤æbÂ5z¯M Ë!rÝ]*®}’1¶Æ¢,zôÑGž\›\rº}ûcl|“®³‰åÔ…¤ï,§5XñÃl ¼ÅD:Ñ«Œx`¿0#ÿØõâp•Á8Rl+Öd+¦3ÍÍwï¦ß‚AM¹f¨‘•dJ~lnS®bB3ü•¿|læ-x*áË6y?0l +ùðºõãmà<]^8çñêÈ¥Æá7ñßï;ÒÕMC=Èé–S7`ù²‹å´%~˜õårá<Œ@D Àвq>†5b·äÜ)Ê„Ív‹Kb³ŒÌ?ÒAÃ{w§ñ:?Âý¼çàé„/_Ÿàèî•"hº+:lû J?e›}˜›†°œº%Ø],§Á"Èç3Œ@cB@ E$¢,UDžA’\Š2þ•çTVJV*¬ÉñCÇNêe4šÚãì&­}’÷圆ÕÛ° §uƒ¯×î„Òò  Ú>}á²¼­ÖáG8"]n|à×äN8³UY-€#¯œµ¢KøhŒØ®^G¥œÖ#XNë\ü—`?ZQŽDKUl‹äÍè§,k„øZ·Û#Ï=×\+Ý€ÉÚƒK\f»öãIµÆ)àYßÌ9ÍŠK­õ ¡›àª-£#ø4ãû¥t_*.…Å?n‡â2+N3-ÉÛ¹*+ÊÔ+'Ž)))å:üK8³¢LÀpª‡€d4ý vzP”£NN•cË©¸:xÍ0Œ@à­(^µ~ÏüÇo[!$—?g•Õ¡´*fhõ”åø˜ØØÉ ±’ZSÀmÚ“ÝÛgAR¢tLŸ<E%å0°{» Ê7h0Ý1áHÁYŒ&ù±ÎÑwrx\Ÿ®Ë ½:n÷Έ|´Æ *'²œVƒÁrª¸*x“`? ‹ '7àG}ÊZöOv8ý”?Çlâu.áF®±&‹9#¥i¢*VO¬¶ì;·M,·¨¤¬Þþj5ɽm3›AFj’¼_üœ»Xÿ]ºNχŒ´d¸óša ¯|°î¿qtl!g]¾qlÙ{ fܽ²ŒŠ„a¦§«ÝÑH‹ IøÜ~ûív ͽ/ zû€±£qu]Éé†]G`éÚ]P‚oczth?Ÿ4 Öã>’C!“Ôþ—ÿû t@™½aLú+oHN#¨–SBAåD³œF *^«‰P˜ðƒ¾µŠ®Ulfdé$EÝ/$U”d*ß±3PYU}»dÓ_ø~Ó^8Š®7â³KÛ–°qw޼_ü,úzÄÇYàÑ»®ÆàRáƒo6@‹ôdÀé¯`+*Ü"mÜuÚµj&+Év»¶î?ƒ{v/ß°ò Šá‘)àšá=±œ4yîùK°dÍèÛ5&ê ëwŹ֫·ÄÀ!Ý`Üà@ÊÀÎç\Ç‚Ú •'±Æ_™¯Ñ ÀtžL4Q4U(QEॲJ´Ä 9Ɖm\ëBN ‹ËàÅkaD¿Îðàmce¿þÕÛÉ óÉsùp4÷¼Üäsù…ø°zºµ£çC_å´¡1‚Êb9%ÔMô0«Å­º­äÒF@ ‚VBÈR%¬Uj4H/eXŒ)kÐ.%; £KnŸ‡æÌï„m#åMX«de¹²ÜZPPXJª]Љ,L}:gCl R?ƒz´‡1ûº} S§…˜ŽÑGuäÏ<¢oWHJˆ…¡½;Á©s— •ÞÁ½:¶ƒ'(Z“ŠàLÞe—b¼çh.Øöj_`Ô&Œ¸TX‚ç•bžŽÐ¯›SI?pü¬lqþŵÃa*Õã†ÈÑ‚å2ÅÏ€îíad¿.²:;3á9j¤‚â2‡½Ê–‡eÑõ)0W£èè-#Â"h "MFÓ÷bŸY¯›öÚkô+®—².9Ý}$Ò’¡·¶¸N€žZÃö' >ØÒTk´MiÛþã’”ˆÄNEÙw9õÖ‚³j´r壛ôÙ¼YÉ튯}…Þ¥]K¹Üõ;CÞå"X‹~•uÓÎÃ'e÷¨ä;“]«ý+ëæóõ?áG8ÚíU'6._FÙk_‹ˆˆ|×Oð©8gx¸a̘9$DâÚ¡uØä”\/¢;Ô÷›ö¡l“g›9rÊé—Œ­B—ì–°xÍvùŒˆêéœz#&,§ õÖü0«–\# wj´2½·4 í{þO*Æ÷¶.÷ ‹%öVl†|ãŵ—*\lwlÿüÌÅBãú]õ?xÃã &úr}_ΗB+N3 ›¼ùÜ[‹á½¥ë`hµÅ‰vÆ¡Â;ý–1è\3ÿù)üöùEðÑòŸÐÃy6)Ý4+F›éÐ2Ýé"A¯s¢ gôpfÂßÝGNÁŸß^ÿy*Ú¹pãUåcd‘¦0Þ]²æ¾þ%tÆ §Éy_ÑQžaã߯„ö¨¸;—4áG8æ=º+ |¸…G®3*~8<®_4/œ7sº_ÈòxýgtnY}ÝÐõV9íݹ<‹ÅW8ù£/üþôÊG@§"Ñ÷ô ¬|Ëãœz#\u°œ (T[óìjPrAŒ€î¨Ñ˜l*Î]ºŠNÅÀ#chiiÚìgï’Ž÷¨_x3Þ†_:ÃÍ\Hûl† 9ÿ6ûù#œÝ4-­Ã“÷M6 ËîW%•[+qv œ`ÃC*¯¨”Ð4n&kE•¬|×=Ÿ¬`4‹½æu—H1§WŤ¸“(Êߟÿ³ÄQR\¼ïݿοË"géB\Èù›Hªp+“2…e>O°Q€ H„oÚ¬g_•$Ço¨oø@÷>öó>ÜÔ•œR!š¥†\œMžä´¡1‚å4PÄÝŸ§…Œº¯‰÷2Œ@¸`‹r Ôr¿©ÿ¯žœ}žbÃ…”· \È5úâÓ÷_«¨°ÿýƒåµC;{S’±nyžã`”d*Ó¢K3pxR’é<²2{:—Žû’/­²ª²àÇÅŸÌÁsWÙZkaUÆMN"‰4•X Æ7Å|¢ºõ¶‡§¯Ou%§D(%™úçIÖ#XNÅÕÁkF€`üC@ E™"ØÑ‘©Úýâ Ñ9ƒÁünÓë\2ã–ãB_¹•]8sæÂª¯>ûç墲Œ®Cß^áÇ9¹A€ð!œ/„­pÊe³O–ßK®„/áLŠ2'FÀ+4û>¶m”3I“’’üKÜf9õŠšoYN}És1Œ@d"ø;Àj<¶®þa-‘ ³WÇŽ;!9`ý3 Ý{ôðö®Mëi ÂO,æËyÊsÚ•Õ®S‡ý'óR·ï?æ0 96%)âb‚sM º#%Ñpä‡ùîâ5Žu;J ‹/ÿô½§söí¥/ÉÝ¢¡,G¢<`ÔØ{±ÿ€rõ6­9ù†À 1ãª0šæM”Û`4´·æŸ5//|†„ŒÒšå”ò!±œz‰eÔ36|„ˆ4œsŒEZ¯TîÏksfmÁP¹?à<­W¡Ô“Ôäa¬b.¤Ì‘L aiÌ;{Þõ¥¿3a`Å€“ðã´Œ÷—m”’b% ug±Ð;*“µªJ¢à,4ï4ºp¶ ‡vnÿtÊ¥«˸Ð|sôBî,äzn©÷?ešZ}tZÊ}½ÙÓPaî8ôgS®Ý»wö·XË©p²œúgeˆG j•6™úô³“0¤ÝRçy†âsy';,^¸nÀôÁP2.4‰0-ô‘_\âq‰ÉîÒ­U§½z6INi›Ÿl6™cqT&›ÝfÅi…%Å…çîÛ³ýø½ÇRŒÉŠ\P½²Lð‘ëù˜â³ §`Ñ3Ì›½2زô|>}´ˆ®SÑ/wçºÏ?¼wï^ze9õ‘8–Ó†BÃÉk”‹ÃX7Œç`;¬(ûÁ Þ„wáM¸—|ŠO¾þÜì—q›_º “rL 3-I¸Ð¾8\h* zåKþàÂ'<špŠ.Y‡i~£d5&…˜Ü,H9ÖdÚGó‘’Lù9©€ÞØeoìcU(N·E<8ç… {UE>_%R#1Ҕמõ9n²œzgåÔ;>|”`¢ ]/¢ÅREׇÁ`|§ zG¾V †Çî|ì±7?xé%Rð(ц”@r %nÔ¤(Ó špÊr4)ÉØm96BI&˜aˆ>„¤…¬Ê¤ “…žŽñG|‚ª‰"hFAZ0çñ ÓgÍ}#ÀÏ îâ…7gÀ€iŸmݺ®+J,§NÜý²œºC…÷1Œ@T#´¢ì ä«KI¶X97#óWjÝì8•÷ NgÑï·Í›Ä§þ{:º “"HJ ¹ ÂG )ÉBQ–}˜ñ¿P”ÅwEl¢/%q&|„¢L˜N´ÒLÿé!ƒ•dSàÍ/H%ö‡ð›‚¦¸të7©Í=[·ÂÛX"Ë©{XYNÝãÂ{F€-ÁÁ%–*iáôéUÓŸž÷ˆÃ_É IpÿÔÏ,z}þÓkð¿RÊr­ý0Yõ¢AA–áQülÄÃ)Ä´N´Ð6)Ф$S^qãÆÍèK×n7I¯Ï±9úz|ÿ=cÆåé3罈Ñ\* îùiO=»tás3)v´¸•µ,§NØ6,§N<ø—`å(ñµgf-ž:kî'x»½ï¹èa\0gÎG}æÌ¹”=ºÁ(Ý/„Ë…Òí"Zeq&|hбøOØQž¨Orx\"õ`@RJìËE÷¡E¹^Ui`t¼ŠEÝ‚‹ò:$™¥·,§&ÂF‰Ë© Ký~˜­ ïa"ºApòC¼ñ·P&M¯vÏØÏÀ"þº±e™¬Çt#&%™”cZSŠVE™úNøÐX(Çâ¦LkNƒÄr)°pýâã—N{úÙðJû/9~„{3~Ðx~Ìø1ÉrêW!‡,§îñqíå‡Y¼ÁD<|C€â…3gž>{Þ“„äßòé’4- ËÌ™E¯Ê]ŠðI“b»ôè;ÞbŽi0³ ’¡¹!Š• I2Ø øAd.Þ‘,¶®øøå—Éo”’¸I;ÿñ¯ÚlR»@½—·ð™™?L›õìBœS™¾#  ìŸÓæüù§…sž<…YN=Èrêåî(Ç•0ð6# °¢ Ë ž™ùÚôÙÏÞ7ááhY޵UÂW¿||Ö•ï¼0/wÌm·¥vèÐãqKŒùA‡,f“#%9QJi’`Ä»³ë`Õö4TT¤‚’2GAQ©¡ÊfÿCZ“´²žú¿–üfšG™¬ñœ4@`á¼ÙOhP¬î‹LI2?^P\9 -ÊÙxù5—ª*—Üõë_~ÿßÿ.c9uOË©{\x/#ÀD'j(ÊQg©¢KÅ€¦á_Ï™·­Ê¾Q’ìÍ%‡ÔÂÆ%wÿæ÷ÿŸš†–fCÓA=Ú†öî]Ú¶4šŒQ1;WCRD &;>=:q6î:’°iï±?¦%¦?pÿ㳦¼õÂ<|M.+Ël]nI>îÏÿéOÅSgλÓÒJ‡äˆE9íÓ$ýý;¦ÿ~AÓæio³œº…‘åÔ-,¼“`¢¨µnªD¶áW3žA‹²ã;‡ÍFs&ƒ%6VÊj–ê¸ÿÆQ¦¬ŒT•ª‰ÜbN_¸ o}±Ú~æbØm¶ßüóœÿ`o£úÃ> l³ŠG+ðZs Ús·Ã ½ã°“û-FŠu´j–*±œú†-ËimœXFkãÁÿHF€Íœ³KÆ7ç?½µ¤àÒƒx–ðæ =:dž¸÷:V’}Ä•&/ÄÍh2›Ü÷ØSã W\¢÷!N2ÄïÏú!góŽ€,§oüeÎGåå/¢,ËiÇÖ,§Þq«u”å´ü‡`¢1CuYµ®väºg{A§n=§¶l–jþÝ]WClŒ-ªµS÷™MFèÛ%Û°ëÐ)©¼Ê~}FǶoÙ±ƒ¦íŠJŒ­kV.¡EMâ(‚æÀQcÛoYýÃ15Ëme¹ä´Âf=Ò¹{¯©-›§š½s¢åÔ?öXNkð0rÜ0|–ÏW[Nkjà-F€Ð lQŒ ÂnÀ1¸$ ~ÕÀ`Œ}à¦Ñ¬$†§Œ½GÍ8%3#ûÉj|ùú Ϻ§QMEͺ‡#õ-9xåØßãÌç1Ü8Е䧇 –St‹zvÖtZ„‘OcF„@ЊYªhiD}¶©ò«\,D¶&·l™Ú$9éžÁW´7°OrpÐ~„£Åbþuß1c°4º>£×#88kŸM4£(ŠfõuC×Ëií+!è,§ACÈ0Œ@#B hE9 -U¤¸ kr|ŸÑ£Æâ¼£q4»E(Ò¾œÓ°z[xÜWCQ7áè ¾{ï«qfE9VäÕV9ý~Ó>8rŠ"f‡>±œ†s®‘`" eÙJ}–*¡(Ç¥¤¥£y’q 8Õ®’œÓyðÐüw ¸”Ütk'º ®Úr öÎþy«ÃSqjÕí©|ÚO8ž±1±£ñ/áuŠ2…Ç:{þ ƒSÀ(eMäô“ï6ÁßÞûÖmWü´âˆÁ&–Ó`äóF€àåàêoŒg‹×¹äŸo4›3S’@Íy’7íÉîí³ )QžqNŒBQG ' O0[áùôÚ<ê®Q9<®Ã>?üøšÊ)0Í{àž\j±ÁrªªÁ—ɳÁcÈ%0RD8ùŽY©èL–N .±&£ƒîÅ«fõt ßÁ–}Çà¶ ƒ±x€’² xû«Õp$÷´Íl©Iò~ñ³a×Xºvæ³âÔt­à瓆ÁzÜG7Ø÷_Qœ9_þï7СuÜ0¦?Ô­c÷á\X¾qä^¸mZ¤Á¤+{£¢Þʧº¿Y¿ KÊ!»E:L¿õ*HŒM xMxžC:@8ÞÔ TM¢$qxÜ`‰Ö\N?¥åп[[¹­d=~ÙzYf®ìÓâ,À¨®~°œº ˆˆ ùaä9¹ÇGD‡¸Œ#àRB8ù‡Ý„I£‡ KUUeIQiyÍÑ¿²êåÞwì TVUÑtiò±ï7í…£èŠq#*¸ä–°qwŽëœÂâ2xgñZѯ3NÚÔÕ'3Ý3+û³›¥»Å¡9 ’µ.ꨞë?ût¡¼ u"~vtéåò:}Cº*Yùï”/ ÈÔGo¸ÓI¯ŠºšÃÙÕEŠë4ÃÂ? €@‡hq‹ržµT雬-›áüùù?½ñÅ¡gÔ¿ôÆÒí×Û’éÏï­ÐÌàAÛ¿€=Ïš>‡ö4Ÿ®ß¼3ÛÏ6ìZï"öí--ʉ+¿±s¸rúÉŸ3ÿ7ïU£ý?üø33âÒí­_{–¡éí¸÷ß—Jßè̃O¼hz/(2ÿ9ì´FËîÞµ‹yç£5aq9[Š£ßnÛ‹O=ñ‚d:ç,2Êð³£Ëœ^§¯,þkæ¯8шö 2¦ßá½2ËOä:(}ôÓéÀl”°áÄuJ°D:ž@.Z”;ÞY7¿Æá—o Yèé­µµuk–/—î¼ËœönÉôÊ[5CN:v·.'Û?Ó2¼BZ–õ†¡wþ¶+8ýÒñGgúC꘭uõ©Ì—s8vë!=‹ÌñûšÇŸ{=Ó #|ÊžeÔ'Óf¡Ü<¨7jÿdíÊQ¸£ßeceŸ(? Öoª5O½¼ØhðþÒ›•–àTKN?ÓÇZ×}òɯ×}ü±Ž§Î¡y‹òîP;»ÄèÌÔ\ð3“óët˶ºL—§Ó³F»èDïÌ5sõZnsåz̾¸N›û6r pð¶7‰üzt”èŸqÃùô.¿­ëÇ«V|tò©C‡-]ù÷ˆ<0ÃF#þûCG·Ð›ê¾yþPsh¯î;=—?á.\¼ÌÌ~ñMóÜœ7ä¤ã¤?ä&óÏCN4GÖËD¤¬?,ø‹ùýsÎ|A÷(ê’±B3ä®ûçäá$çŸ18ÓW¹¡2ê%¸}àw ÌcÏ,2ó¾-­c‡˜o bäéx¦±²Hp wýÏ‘zi€ çn† =Ù4çܵ®èOyø©`SíÖÚ§}xÌæ >“ÍÚ,—”9s{¹,óbZôܼßëœË“Õ§gžvΰã^}vþ²\æÛNójµëôe¹™öok>1ß¾ðŸvëvT$ýõç¾ü¶ÌoI׋Ó]®ÃÃéž¹—ë´~JZP­!gŸ÷éöi®¯ÓT‰C@ •ô …éÀtX¸®2÷”Y; ÷‘ùðüÊWÏ8ý¼¯>ùsGÙ’áçÚðf<Ù—“ië¶úÌÍsûÊLNÒµKLÕæýöÑ0Ïz™À{Ï2+[ûQkÀ¬A8ÝžÇïïµÉ5>ãÞþàC·ø•Ç>?ç‰9ŗ2o”9 –÷— û()OÌÓÝ5Uña$ëL»Úü:ÕkAú×ï¼·!L®Ó†T؆´_æEUYçSzsÕ§}þ©‹ž}úí¬Íy5ssœ`vËrÜ,´¡k·î[L×_üË’îó´2pδïrc“æž<¬£¹“ŽØ¡}¢š+ÛJt¬e77HÖî*Sž,[½Ö,{gñ½Ïüþÿž”:èØZµ2k÷ íÏ¡JgjÀs†iGö#¤¬¦Ùt¤CÛü:ÕkaQá:íH!êŠlöZä0ä«ÃÊ F¿öìü;[”QÇ9X¿€÷üÖ›"£+–¾÷q²®îÞ}úñ¹?¿]ûÙFÛ¥0&])ºIkmÃAhÇ9íÜÕT‡€["}­ÿìëæáÙ/»›·l•›"'=÷ÇÇŸ’R4HÖ¡;¶È\/³v» P„–LCþù¼³ñŒ×ž›wGKòé@Çr¶ðÍâ:m! ‡#€@§hqôV¯,•®°Ó Œ=vjU|y§Piü$ÔL›wµr7™õ–wJçCdîÙ£wïÞg]tÉ׎þÜ ³ddªi‘ ô±Ì2–±†Ë-6—2:ä$š“e–q’e8éE]¿zùòyÏÏ~|Öúµkõ.Eí—¬sØ¢œwò¬Y³"sßXò£ž½ºü‹ßtíÚí'zR¿Ç~¾°¨¨WAAawiYÓÉ!ù5É#}Óõõu›ë¶nùì£W.Y¼ðÅ¿lÛ²Eƒ6틬Ë0@»\„£^ä ÔÓo¾ÿÿô‡gíÆÔ«rÒ‹ruâQëÍI¹Ôø-ÉÚ—<ž«|Ûq>\§Í|s¸N‡“Æ¡Ar?ÇÙÓ+Ëh<%{@ 3´8Pî³xµ«Z7J@î (M8 à´K€¶xj°•8  u{×­[k·¼¾à™—_7Fƒ 5]˜VVófÒ % \ÔMG²¨“YoÔÓ¡5Pn¨Ë…zæÕä\p•|BO«,ËY¬€šŸÜÐ÷ÊzÚ\§ªÐô‰ë´IVö~¹qó(Iú@“’“:´@‹eß· =U™v¦F~i_( ™Ó¡Eš^ù0(Ö€O'ý’ ƒg ‹dÖáã´‹†:(ïj‰×Új¬?2´{˺Ô×ê©­õy$—'Δּ3ä¦JýÑ™óI~¡sÖ™óŒÛw†\§M²e½¹N÷°+W^$Ãnž#7N—— ÐIZ(«KßèWg®N>?Vþd|½¼Ì—@9l!ÕÖÑp=ürÑ@YƒäB™Ã@Y»gd·(çC˲ºè~ë‰ÐHb –ÕJg]×/æ0H•M*™8±—Ùœú…œéŠ= ÿ§5θº*þ«ÖÈ·ç^›\§û~£ÂkëtßFæ†Ûoï¶qöÛäãKûEμ¿‘¤ìBN$“@Ù÷ÿ%Uê'¾mSîðNdÓ”S ¿„5¸Óõ°[} ëX®:‡}˜³eÙœ7Sø¬­{¡‘.‡³ê>M£éójr›R3¥[ÄÑ‘¨=;—7ñåâ¾O–ëtß6Ù{¸N³5²ÖKª«cW®ýl:ÉZ÷uýÎËÚÍ*tb|hÕl«·O»Vè¬A±Îú#Dç=ƒä|4¿€Ã€%lYƒã0@Ö 9嶺üIýë’õ«¬gG×TÆïm+aã²Ë.Sû|š¸N÷ýnsîæ8ž¨‘Ÿï?’ÿy‡öü|ÉØŒP ƒ¶Ö|Õ3œÃ/d]†ÛòÙ{Ï/a ŠÃÖãp_k¾7í:ï+Ç?lê¸qú4Â6›¤Oô“òà"ö¦·–ÿ±Í >øe_\§»¿ᵨËðÍûëTî¿yH>4/U'*¦ìÎÅ+èì­¸Ý4iRõ›ê—gËÍI¿ºEþ¯fìXÙ Ÿ¦ð YϹլ;¨~ùê~o•Gÿ^áßÖWO÷>ÿ¦öiËM¸Ãg§:ãN”§ÊÍ•¡ ò÷ô}Uñ•»nm\>×éîàyîÎÁ+ÈgV ÞJË×Ëÿq‹õ‹X‘­±ŸJ?ÌÎÙ½{ƾwÛ˜1ú¶Ì¤D*Yÿ3Ùß=Ü.%ýzÒãÓ‘?òî-õ$¿ä!5v팪xŸð|æÒ÷çGW§”Êu:V~¾¥u‘ëô­X7ïÜìn®Ï]ïÿ?2¦3~äÐïž~r$Dœèû#ô^&ÈsœÜÌ·/C¹Ë^k}gqÅ„¡ÖçÊ“ÁÊk™È+y$ãjù"~Sº±/¶Q3»½œüŽ›’¦J}¦^ŸtRÊÔsÆžPÐ¥‹ÕÇ„@> ÈÓAík2/&HΧ·sE@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ž€mË¢GUL’Ò_“2qÖõ—Â{´eùí¡,gÌ&ëìj©Ëòˆ™=­²lQ{¨u@@Ø] ÕeߟÙeUrÕUƺkŒ3GYkãìÇ(¯–Â7ï^ÎÿJåî(÷#3ž±æCñ¸g@lÀ½¾?b[çà @@Ž!ЪrI¼òBa˜!á@kí“2?äy˜VV¶®cð´^-GM˜Ð;Ø|Ý9÷]™/’+¤´âšDÅœÖ+•œ@@š*ÐjrIEâZ˜;¬uoI êÕÕUñg›Z©|KWZž8'0fŠœ÷`ë™j*ãwç›ç‹ €´7HkTh{ìî²Æ>bb}.©©¼~ik”ÓYò\ôܼå§÷í_Ø`Ë Òº|ý×· ZIDATiçž·aѳó^ê,çÇy € €Q ç-ÊÚÝÂ9û„dühu¢üÛÒÝBºå25E@‚d[¯ú•€ ¶¥FSÔHƒ €´Ž€—ËlõÆ=Éo†v·0±#.'H>0ÝŒ—¸ÉQ‹Õq‡çeBj@@œä4PÖÑ-27îIŸä¿tKNj˜g™¨›¼)£Õ13ZHž?§‹ €´œÊ:œ´Š>É{-{{ÕO3Cêµ,+ŽF@h¦@Îe}˜Èöq’íCͬ ‡e H üzf\³¶³Š € Ð69 ”õ‰{ú0'¹mªÞ¹KQGõÜñ$ÃÎ}²œ € Ðr(˹£OÜãa"¹y—3Žâ™qÍM–ä‚ € pÑHÛhRy$uI°ºÑDì< }Ì· ®L € €m,³@YÆMî!ußÜÆõïÔʼn©zª+ € €@ ä²ëEWâ@@h=åÖ³%g@@,@ Üß<ªŽ € ÐzÊ­gKÎ € €X€@¹¿yT@@ õr6êEëU‘œ›"p…[ß UwI`Ì92žõÉÆšÅ2¿RØÕûÕ”qãÖ6%Ò € €ì Ey—E‡]»rüøÃ’ÉúçgG:ã½j¬W)Aò*çÌšúîuöÄò¤â%ñÄSyrªœ& €J€åõví]YߟU°jË’ßJ`ünÿèQßòýÛv¤zlïÔlio£â“NH™úSÛ[½¨ €C ÜÁ?k‚÷Ï–Ssöø¬ ¹Á³¯,N;{•ì$3Yæ<3mFeü¾0qq<ñ3ãÙG¬s'˜ÀŒ6Öm¨ITœzÓ¤I=ÖmJÞeŒ»XºuĬu³cE‘k§Ž÷ixìžË°,)ç ò„Áuò§‹xu¢b¦¦»Â¯úÇTÒÝ!«§Ê¾ZcììnÑn7Üí_·^÷—ÆÇ\p¬¾ ucÕ§¾äÅ /Òõç›ÀýØs´äý¸ñ¢w×TŽ}3<Î7Lêÿ|`]¹Ôu µæMg#×Ψ,[¨itjjùžõžv&=NÊ:F›ñ¼û§ßZ>;“‰ü³?—ížæ6pg9c‡Ë!5î1/<Ý/û[éÍUç¥ÓÉŸK).O¼¯ùFcæ{ÓüøKºÎ„ €W€®׿ť»Àü“d²ð¾ªøÊÆ2+©H\%A²tɰ÷w‰ÅN4ž©6Ε—Ä«nÜuœëgÒÎ7Î\ìY÷C¯‹½D÷I,­Ó®Ÿz—ÄböBçì‘É-Á/w·ûZi¼r´t™`­3æ¤ÌqÆ<£©FúŽ• Y`ûnÔÆÎ’`±Ÿß’Úü¤“Œ5â=eå²îÙH±³çH‹y¯ µm¡É?’ºÝ(û¾"ìa.H=¨Çè¤ÇI,:Brù~Ę« ¼¨¦Yn‚ôü‘þí}4Í”ïLðC©Ç¸Hûrè²t<¦}Á5öï"f{@*VçE½µQûM©åÐt2]£ÇÞc,®“€~C·X·¡:iÎzU÷1!€ €ÀÁ Eùà¿-¬Apš5öƒýe"u\‚ã3*ËïÙ‘öÞÒxU7 ã³fͺó²Ë.Kg¶[w|aŸ^}§\}u¦o³´tž)ì¹Ò :H[A5Miy"8÷§’xå iqþk測$8§eÕT–WíØ¼<Ü$ÓÚ¼rF¢\[¶uzçªxå’:g>Y1ábyý„n”–æHïß¿m̘MúzdEÕŒtàfÚˆ½|ú­ñù™mñÊÉüÏ=yraX_é—]Ù1Ò*û†¦)©®þYù÷Ï‚dÝËˉM-_Z²c‘nÑïL++[§ùÜpûí•6l»&’_’—5ÝÅ.¨©Œ—i:WTMµ&¨Öu­³´ž×òë$lM×íL € Ð>hQnïC³k!Ò—® ®Wc”øw.iŽ4Æ››ÎZoŽ–=ç¿ý×cvm·„A§n“VÚS¤¡wkJÝ#ñou–n ctŸçyŸ×eötç¡’g_Ϲ}Ý 6XZšwÛwo¢b™”´Ô¹@[nwLvM$ëi%^)­Êõý½/<¦p¦PZÑMÕÖ…Û$Ÿµ÷Ý2.ÓC·Õ”–&%0VŽ=~Gš&–oV‡A²wÇ7ÖÊ’µ.°=ôuS]¤[Å[š>œ$ej¾f‰ €íW€åöûÞ4©fÒÚ»T~G»-È$1áÞS‘‰¤j¥‰T~ÕgïuW'}‘¥ñ×íüx2ZFv‰ ¥Œ­Öóî ·gúGswà¾n —µ±­…&)¯¢vs¸-{)LI‹ênõÈì·®Nú"﬇À[²Óu)·Þ÷/Ûûج„˜nÛËÁÙ¤¶Pk²¦–/éäH#SÓ]ö:Fre €´#¬À¤ÕŠª4Y fc¿®Rñ‘ñªËä _5t þY_Z‚—Aæ¹Ã4AÊ] -œuÉ~‡- ·í¹”üm‰.zfí}~ùŸ÷Ü¿çëšòò5RÖæ e/}¯ì¹_bùW¤øß³·_åOê_—¬DÌ;ÙÛ›µ.­Ùz“]Ø-ÝJ"sÿòÞ¹ò;âNÍ/Wå¨Ë¾ÎEn”ÜT»/¶#€ p0”¦~ÊžZ9v±¦wKÃðrÃ^¿ÂHì7gŸ|ÜíN‘J¥>7#Q±½»…µ“¥½ùÇÒ'ö-wôa›UŸ\h‚`”4ÓNÖî ûªÊ€h|ÞêdÕ«É”û©ŒdQÙ7vMêöAÖ&¿<½²|VCÇIà7%0®Xú2/í;ë‘OR ŽN›‚‚i‰1KL4ò IWǫʺG»Vךm}êSI_Z‚ß7Žø]CùÈ6õ"¶~Sýô|eõ-eKdd‰+¥5¹«ÆÈä“£ò›ãÒàyt½l6¥ºŠí¹ÓÏ4˜† € pPè£|PØs[¨ÜPwƒ¼‘WÈ {WÖ¥ê—Ï}cI}:•z]º2Œ K’Êî•Î5Ò‚y—Y±vƒÉä>!7îU„iZú¾ L‘½TZ•7ÉHsV¥[W¿$íܨ†Òë¶‚>½~"Ý9“î 3W¥ÔÊ]¯¥Mý0ÝWã{W—ÊnÃkSµ+M*-ý‰]ßH4zQc»Û”Iî…Òõb©K¯–V$ÖËÈeÒmdx?v…Ÿ«ò›ãÒPýkÆŽÝ }b&‰í£%å‰Kn®úzC騆 €X@Z5ŸÑ¹ŸB»«zsL¯»óή×TUÉ{ûžt¿ïk—å›d‰X‰?q ŒÑ­)Gj%UUý´ÿtCé¯õï:DëÛоæl“‘1.—`s±«].öç«òÔ¥¡sÓǨUCû؆ €\ 8^ù¸ÌŒ›Ã÷Q‚¾E2·¸;B«Ô®³Ò@Y>ƒo·ëJR9@è0ܪ¸¯3“?ó¯–¹ÿ¾ö³½YÚ¸¦YGr € €@‹r(K-–ËdGŽš0¡w‹jÄÁŒ£xf\1i’€<ä=ÏÚ_7)1‰@@ýälÔ‹ˆ™ RUÁ–@oFzp?å²{?ê(¦'Gfï')»wÌ¨Š¿ «:3!€ €-ÈY‹ò´Ê²E2ÔØ‡rãÖw[\+2ÁÄQ<3®x € €´¹@ÎåLͽG¼‹düÜsÚüL:Qê§Žò`Ž{:Ñiq* € €@‡Èi < 6à^Çv…<übJ‰_]Ô¡$ÚIeÕMýÔQ=ÛIµ¨ € w9 ”}Ä6,–y°I®}@ZE?7ï”›xÂ/qËø‰ãÏ&M2@@\ ´J +R¾Öî.kd‚Ø—×ø¥[rYéΘW¦^\÷-ëÙëäIzwwÆóäœ@@Ž"iŠ.zvÞK§{Þµáëj/9íìóÞ^ôܼå­QVgÈ3Ó'9ØòˆœË?Küc‚äÎð®r € ÐÑZ¥E9D‘G0_(ë3$`h­}R懼"ïÓÊÊÖ…iòu©ã$oÎ}WoÜÓ>ÉbQ\“¨˜“¯&œ7 € ОZ5PÖõý™]V%W]%#¹Æ8s”„Œæð±³nµ¾¹=a´E]œ1Ý3O0”‡‰è8É:œŽn¡7îÑ'¹-ÞÊ@@š&Ðêrv5FUL’Ò_“mÇH Ü_ ?Ö%PÞ¤û–s]®iaœä|x×9G@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@°’¸¥iGUL’Ò_“|ŽqÖõ—Â{4”§gÌCÓ3²÷ŒWÆ|7{[¸Þ¡Ò;»Ñ³ÊX»ÂFÍì¿üµð.鯗”'þæ¬w͌ĸߵ³zR@@ ¯Ú¤E¹´bâ—œK]çœy:Ú=òûieeëòZ]NþZÿ®C¶¦·\¸àc½GfT–ÿ2ßM8@@@@ÈGßwÒm˜ @@Ž+ó®¥7W¤Ýo Œ<µ*¾¼ãÒœš—LœØËlJ½f#¶¤úÖò§N-(@@ §-¿¾??êÒn²°®JÎ:ó[ÉAö6Ö;ž€üàyªãÕš#€ Ðö´(·½yÎK¬K%o•Vät¤{ä¬ieeëÂnš4éu›êç[í­²í‡áö&.‡G¬{6;muUüáì׬wy´?¹ç¶Ô†'œ3§ÆþBZ•·zžùßêÊø-ÅñÄÏä±Úoh:Í?“6¹q’±æbim>Dž#øº57VûñWu¿NzŒñÌmàÎrÆ—M”ù˜Nžî—ýMÓHëõ cl•¬ž)åHkøSÎF'Ϩ,[¨û›2Ɉ+Åig¯’G~AZÒ×ÉŸJâՉЙzì~Õ?¦’îY=UöÕJY³»E»Ýp·ÝzÝ/FÃ\ «/HŸð1ò££¿¬¿äÅ /Òõç›ÀýXZë–¼7^ôîšÊ±o†Ç9ã†Y瞬+7Ο7\›]÷¦–ïYïigÒ㤬c$ÿ¹Ï»ú­å³µ,ä‡Ou›’wã.–²búWƒXQäÚ©ãÆ}ªû÷g]zsÕyétòçRÇCä}}_‰ÆÌ÷¦ùñ—t @vÈe׋W$k™r%àì ˆ–4–>'IàéÙ-6·¡tE‡v™¯ûƒTÝɺ?’N{ŒžrÉGµF K8Ó8W5²¢êòÉ·ŒÞÔ-Úýß$˜Úâ¬wE·X·¡E½‹&nÏÛõ“´‡…åÔ¥6>.å)Ö³Wš˜÷O,¿¤Üszó]˜FÊî'AòRVõþÕFí7%PšN¦kÂ4Ñh¡¯ ­^b‹ì—åœ·Ø =!Ü¿¿ei¼r´Œº2Aº…ÌŠs’z—Èò=n¤?áX ’%¶ïFmì,ùÀK þù-©ÍO†—€³§œ×d9ܳ‘b/fÏ‘à¿WÚ¶P‚äyÖÝ(û¾"ìa.H=ÖG“¸„ü@ø~Ę« ¼¨¦Yn‚ô|ýs å;üPê1.ÒÅþƒ»,Éœ¾ay$?–ñÔó‹Ù åΑÉ-Á/Ãýû³.8¼‡v›¹N|7èûªó‘æ¬?jvåà €¨@ÎZ”kz³Sjªâ¥ûË.bÜqÒò»A[J{×õ×o•#>•–Ïã²÷Ëø$¿™QY~ÿŽm¥Uwp¸Ñl> ÛÖK‹£tuNo[]³ÕõQãÏNés¢1ïKÓüò7v쿦8^yV}2(“×#vl“…] ­Ðº-3WTMµ&¨_kK·¬ÿ4|-Ýî”@~ñ¨ zïÙJ¦É^Jp:NZ­gÔT–k«´NË·/$ŒM¦µ5xåŒDùU;¶½sU¼rI3Œ¬˜p±l{B·KKs¤w‚ò£aF:p3mÄ^>ýÖø|M#­Ö“¥ÕzîèÉ“ §\}u]æ8gŠ¢1;FZe3%ÕÕ?0+ÿþY¬ûoÙ?±©åËŽX¤[ô;áùÞpûí•6l»&’_’|>ÒþâàŸ+-ñƒÂ–øÒòD"pîOÚ"/×ß_µ>Yk¥õ¼60i·¯÷u{ü‹ €*ËeD‚€nŸIÕmÖ¬YÒ¨¹÷´£Õ´»´%ï컬©"Î=Zÿì/ùdµgïÝ{=íÒ_”Vç¿ßw˸L7„0…| æH«¨¶ˆîœ$Ý[;_ÈŠŒœ±LZµ¥5vû$ÁeL¸oW$~*ö¯Ò&)];\ÄÕ™^aš}-¯óïÿn¯:JÀ_ïû—í}lVf˜nÛÓ@N%©-Ôš¬©åK:é~ÒÈdm¡´(oµžwo˜Jê§ÓÝWøúöÕÌ¿{GÖ>V@@àhQ>¬ö˜´Ÿ){W‚µ×“)w}Cõ“òiÝ\T}ë˜ÝZu]„-ž™Ãä»ódeÇŸï3-˜é ‘®9r/Ù+Uâ?1»\ég{´L¿›½­±õUÉ÷äfBwf,¹¼:1îÑé•e¯§ÒöØÆŽÉÞWS^¾FÎs²do×%Ž—zî¾ï*R©ûà bÞ Ó5{)­Ùz“]x¼¶ìK{®”™±ÌUùbô¶´œñÌÚê[ËŸÊžkü> ËßßRn”¹ërµ¿òØ €@GÈjQkÙiHÿÍašÃôD°nåΣGú‰ÓÓéˆËAaçÎ+¾oéƒ|µ³ÁS²ÜêÅ"÷L»eìòÒŸL:Zb¢QA`~$I/Ü«ÕÓº²â›«>à›½:=i° R—JGgþÎ>'ë—ø¾›«eìܾcå¼S>÷ÖÜ¿¼7ß$ƒÛJ*&–FŠ–×¥6ýPn <Ý‹™¯î™~_¯»ÇŠÞÝœªÝ–Neº*¼W\1a¨u©«wk¦Þ×Á;¶Kà7%0®Xúì.í;ë‘OR ŽN›‚‚i‰1KL4ò ÔñŠâxUY÷h×êZ³­O}*éKpý¾pÄïö“õ~wKKxlý¦úéòƒ¡²ú–²%2²Ä•ÒšÜÕFcdÎQù¢ñy«“U¯Ê¢ŸÊµVÙ7vMêöAÖ&¿<½²|Ö~+&è{ÙlJu•<Εkõ™p3K@Ø[ g-Ê2ê@\罋`Ks¤KÅø¦Œþ 7ý-ˆD½¡Ò’ûe¹yìíÒŠD½I¦Þ59#fcCgTÅ_سÒí`’ŒFQ½:•Xç\ò9öóO9~Z˜Nnb»GÖÏ—ýK¾óÆ»pÿe—]–.Œõü9.)#A<_—Üø‘´&_o­÷_Óýx“G?¹Ë¿þ3k¼›Ó&]-ý“×—zLê6VÙaYû[ôéõO†œ“àzæªÔ‚Z¹Ëîµ´©Ïüp«ñÇiëö¥2„ÛðÚTíJé0"}ª]ßH4z‘ö'Þ_ÞûÛ/õ”Ñ:¤¿s2xUÜ×Ë…2é1¼Æ›©®ÊÏüX)²—JKø&¹Îæ¬J%¶®~IÚ¹Qû«cöþš±c7Èœ“$Gå}ý°ä檯gïg@ZA@n.zFçVÈ:o³lŽ©´{Úµ@— ÁÉh ÝŠË+Ý•ë~ÆÌ÷çïó/ ÛóÚ÷~ÍCoÆ+ñï8\×›;éM‡£Ç?¢¹ÇëqzÎ%UUý4¯†ò¹Ö¿ëGº¡}ÍÙ¦ÃéI°¹XÕ.×TUÙX>¹*»÷Äú^6V^cû´o¸Z5–†} €ä»@ƒEsP¤5ð’Ù2LÕéÍ9žcö L¸ñ±´ÿÛÞ{›·Eƒ« ë·m.ðb_œZ96ä5/'ŽÒ@9í‚›f$*2cT#‚ €K ÁVÇf¢³—'¯õiÖ±Ô°€5ÚBùqÃ;ÙŠ € К9 ”¥5ù}¾êh×¶5+œ/yowtä|wŽD‘‹sï^{LRÛ!}wW9yç[ò@’÷=îžrõÕuá9²D@8¸9{àȾNÃ÷Gl“}·é<ʯú— Î8ûäãÖLÙ×y°½GÏÈÒM’7zQoþt¿ü­<8eN@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ “ ü„ [.eTÁIEND®B`‚neutron-12.1.1/doc/source/install/figures/network2-services.svg0000664000175000017500000014221013553660046024614 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.5.2 2016-04-26 14:55:33 +0000Canvas 1Layer 1 Controller NodeSQL DatabaseServiceBlock Storage Nodes Object Storage NodesNetworking Option 2: Self-Service NetworksService LayoutCore componentOptional componentMessage QueueIdentityImage ServiceComputeManagementNetworkingManagementBlock StorageManagementNetwork Time ServiceOrchestrationDatabaseManagementObject StorageProxy ServiceNetworkingL3 AgentNetworkingDHCP Agent Compute NodesKVM HypervisorComputeNetworkingLinux Bridge AgentTelemetryAgentTelemetryAgent(s)NetworkingML2 Plug-inObject StorageAccount ServiceObject StorageContainer ServiceObject StorageObject ServiceBlock StorageVolume ServiceShared File SystemServiceiSCSI TargetServiceNetworkingMetadata AgentNetworkingLinux Bridge AgentLinux NetworkUtilitiesLinux NetworkUtilitiesShared File SystemManagementTelemetryAgentNoSQL DatabaseServiceTelemetryManagement neutron-12.1.1/doc/source/install/figures/networklayout.graffle0000664000175000017500000000766413553660046024773 0ustar zuulzuul00000000000000‹í][WÛ¸~n…OŸÎYSŒn¶¥ÓYÜÚ2C!´=ÓÅZg G ;uœRfVÿûÙ²sñ- Ðâ$ÖÖŲö÷mí-Ék¿~ë…ÖW• ‚8úå¶Ñ KE~Ü¢î//>½Yá/~}ý|í_[û›Gl[ý0¤ÖÁ‡ÝMëÅÊêêz¿ªÕÕ­£-ë`w§udA««Û{/¬§iÚµºz~~nK-eûqO V’¸¯’ôb [ v;m¿€jòÒKÍ«íÀO_?¶v¦.^¯ûiðUíÊ •ìDmõmmU_…Ä JUW%¯ÑÚêøë8 Ôø2…"?æ%O2É$‘ú˳µAšÀ-¿†Úq/ ºI<ìÛûðím";P¹k«#‘‚4v…4Ÿ8®à¬ ²¶:.:oÂ0×Û é¤ê4ªÕqú†ôÏt•QªëŸþDltï#1-1§MšñÏ?襅¾¿´þᔾ´×ýþ½Ô\-¿ÊA=k+ƒi•,;[SùqŸ’b÷æb­ô"TSÉq‹óD(2>›¦’óô­DžO›5mØÞ~±9П“Ž›~É{p >ÇqoŽñ°)£¯r°ŸÝ`:Æ]™õdñANs´‚¿UM~Ò㵯‹ªA6ZkÒq”îDx¶BÂHœŽ‹rz.pRHœÔ‡lF¹ƒJ(9ÎБyØóš2$Í(aÔ­Ô0Å…Â 6ÀÉ~_E- *¹3ø*¨µ¾–éF¥+URÅÅ02·MØXëìN†—õugÁc½§²] -»‚é‹)»Ú˨´¡œ:UÜã$íàcxÒAöÏïw1qȱû±ÔI ±üÇ`x¿)CÏÿ9îÀ IOÂã:îD|ú§2¨Yãqóówóµj€ÜÏljj@w¥"ýå$*øÌ8ͯc¨I_ǘ@îã¶ê¤òÄ#èùq_&íìßøÒñ_ÿNƒîiª¢ŽôšÑóçY{YÇ~‡@S4êËÐ¥G€ŠßgPdÞCŸ_êÀP~Z9³=Ä<À@Ѝ#࣌Nf‹‹ s ,X¼;XÜŒõƒ! n3â`³@*\á9‘y0AÐIXhLt\ÛD`Їa‘Y•„Û`f2Çuˆç!!\¼@̼=®7W|3¸y§§²·²«è¼ã^qXâ¼—ÝH¥ÅœÌ žÍžý=+Lä]Õþ.I40ɨ]DÞF ™H:¢üÇr&sÔQ|ªw»*‚)éiSéU‚¿¤¾ñcÊùMþðÜ-'þËs6?¶Ì(>õ+#@f¤’CÙ†åþËr•‡K–á@¦©J¢’l£/cuÝTë°XUæP½/P%T ¨P]¨. "1¶žu;Ü£€t®À‡¶=J'.t×éQ˜´#ƒmJsbˆÄsþh!r<]Yi©^p‡íyq’_'=~§8©»½þ46ÃxØ®?……ª0™ãÍ­£qPÓ¹“cA œ+ó Üf1èP²^*¬ölCh0›\º¡ª+³@uiœåú6qä8ÄA„V@¹±ã¦Y<Ä kÌ’ÌÈ‚™ë § ýEEZ.Ÿ‰!‡åš`윾¡ï#ÇOúÍqüǘd.ׄ°ìƒ#ýAi&BE&ÂX&â Lrëg"®vérƒ$’é0‘á%Τ“ãλڄ­½õ£ïWEžçghê[…©‰Ë=&€¡=° = l]—8Â3“˜[0´w}†~”î¡eáÜ;ž¤1Ò\ ”ˆ—' º\ÌB¬ÀœR•…t˘åÊV¯‰W »cÞÓ+j0æ€<ð“rá:†÷î•÷ˆá=Ã{†÷î›÷˜kxoiyÜ=ïqÇFTPW8œb"8§†÷î“÷\axÏðžá=3ß3¼7æ=v£ùÞ æÙ "u Þ™ÁïTøU¥/ç^›J.¡†wJ¶g7¡H—ÄòFÂ¥»™¹f>râMÎȃ.]ÅMT`fÓà†*<æ€á [ÛÔC…¹ô{¥÷&¹1¶uf‚9‚!&˜«kSÛó„C\L9Ø%®7+7!.Hb°SÁ®'\íÖ†é½c#`@³jîë2ç:-WìSûõ$‰M7Ô´þcWu¥QïÈpmUœ ÆÏ ©âÆê‘ ÂëµäSЮ Ù­’y9*¼¾Æ“…h¼AÐ{DPºHÅœÙze¡ë1Š0fžc0Ð`àÓÂ@f0ð¡a Y$ 0"±^˜À™>Հ϶"  |”Xõ.g{a;Äsî!xÈH`Tªº¿Œò}z 7dãA6dãA6ä±™.xÅPïU"§‚˜C÷Ê{Ø1¼gxÏðžá=Ã{cÞà 潦=œÍ+eÍÎûÚÉ\³‡3ߌ8f2ÿNùÑžÄ9q<ß8‹ÌÆDÖfc¢Þ˜˜‘" Yµs…ï¶ŠÞwoÁ7·Ü$bryîêš=ÉxËò• ½Ü(üÌMøù¡…ŸÑMá{6Ìqv… â\/bÄÄvg„ ÇÍô\~6áç'…Ä`àCÃ@O,+Kp2h0ðia`5øôƒ—à`akÂÃ.嘛ãœoá’½þñ£®÷C‘ÃÉj¢’ÆÏú¤¢’d ¢’$oÔ¿‡Q4ì(èÃÿÜñjB‰ÍŒB›1xŸH„¡@C†  \šàèâ=aîB£ Ì0 ‘ÀÈ3±Q}Âqaâ E¢!@™Môk‰àYB]ÎgÁaõ= 2»Ø—ä PU×`©ÁÒ'»Å×ûQñ…¦³—jêš¿Z¼È5t‰M]P`˜c!ábΖʵ2ã%¸L ~—à:pošìæ~ .c¨ZÁ2¾ˆ.þ]¸7F2î²¢k À¼ É„ôÁ.ÇÀfP\0ZÈÉ̻ь‹ç.^8ùHœB3^š¼ >!2ÚeíŸü¥üÔjA²ì*k/n+ _þfÝó¡ôUO!yòˆËë D±U£6_T)–3ñ›c±áXñ†c Ç>rŽ%KƱ˜¼´ª«oàX‡Ž}Øërñ†c ÇŽ}D»ÆþÙrMca¢jcçvu1õ\ÓXÁlŽ(fçsJ³× üÒ¢Î94H+ƒn¤äÒa¢Æ¢‡jÑÍÂ"†ãxwÄÆî`òx—Ø—H~“²¯\ëaК¢-¨)4sÏ0w{tk«N*OF@§ÿ/ÍB$À#”ãÑA Ú*±"•žÇÉÙñÜ¥L–kD³‹àÿ%‡Bp€u.?ê› b6#‹»À®zû À´Š"qƙ˄K³÷_cÏFÅ·bk¨«^sîu’p/ËfÀPÓ†êYç‰*spÐC:8èšVäíø&Ëÿ±­­‚=&¸çq1Ëìl1vÇíí̘±;ŒÝ±vÇ{ÁüGÏhnayŒKüaf«;€¥ÙÁù™ÝÁJ Ø(3vG†èõ;ð±¾OÛØh:c›RîpâbxPTð\W "`xÒ7~ÕT>—Ü“½WqV…ëâðs^ïÛÇ@_):kÐæ‹ÙÃv= z2UµAQqWèT?Hüø}mƒ´2Ä1¯ŒéL¶¥@—eÄQEÙ¨"fÍÜŽºAToK;NkѬë-Ú»*êøq\³¾ÐÇbt}šÿ LÏã~0—|Y ‰g³4¢®@£CÍ®!?hªÂIhù`žŒô÷q;è~Öê­Â“JvW[!®…Wˆ¿bÌúI÷\­®¬ ‚þ’ßË4=UçÖï`ÌËsèŸjƽ8UóßÒ~H—:¹§ÇÒ0dæ.\ãÍ<§³Á²ZEg+Î^k#NÓ¸÷^&Ђéx(œ1*¯Æ2-«õè;ÃežÂP^û8 þŽ#B»ƒ¨2ðêù@…íÆŠ6ÖÓ¿Ûï~ ÿü„‘úïFøyç]¼ÎþØøc§µ}º~ºÿ9øäÓo¡ßÝ„ßíýøóéÉ»á:¤on;GGä·/Ÿ?í¡õOÛë­/ óæ[x¾ùöòæïªNz›îÁüòò†Ê2tËÛ+ºå¦·I‰/4¶:Ä+’ÍQGi:ØTG-|39…a›s抃<¦(Lþ…cëƒ1]¸Æ<Ær|»¡ ZÕWàYµ¤Oêª|¨íлÁGqÿ.Š/SPö ö#¥Áo†9t¨d{? /®FÅÃø¼´ˆä+ D+mN–tj,Y ¯£ ­ø¦Œ¾ÊAÉ~Êsô`Ž5Cô¹Õ¹î§Á×yŒÎ,³¶adä«kæý_†jê5¹äî? Ôv”ÉeÝþ±Ùþ­ö)ˆÚñùl2Ú& ôDÖ“õéCÃìaû[_F lÿË»xÚˆ‚ù0rG%%¤š8ÑK+Mc½ÅÜ#••"“9JÉU·–&CµZJ?†S›¢&OçD’§ÇôÓ˧è\vß p­Pž¼SZ³«ó4§jôìŠCÕ-W±h¶ÏÏaŽM‹zÕ땯á†úǽª-m;•fh¡¦âƒF˜h8B«Aqž5–?¹XrNaª)ÓÏ~:ôúùÿñ;÷£HÉneutron-12.1.1/doc/source/install/environment-networking-controller-rdo.rst0000664000175000017500000000202213553660046027255 0ustar zuulzuul00000000000000Controller node ~~~~~~~~~~~~~~~ Configure network interfaces ---------------------------- #. Configure the first interface as the management interface: IP address: 10.0.0.11 Network mask: 255.255.255.0 (or /24) Default gateway: 10.0.0.1 #. The provider interface uses a special configuration without an IP address assigned to it. Configure the second interface as the provider interface: Replace ``INTERFACE_NAME`` with the actual interface name. For example, *eth1* or *ens224*. * Edit the ``/etc/sysconfig/network-scripts/ifcfg-INTERFACE_NAME`` file to contain the following: Do not change the ``HWADDR`` and ``UUID`` keys. .. path /etc/sysconfig/network-scripts/ifcfg-INTERFACE_NAME .. code-block:: ini DEVICE=INTERFACE_NAME TYPE=Ethernet ONBOOT="yes" BOOTPROTO="none" .. end #. Reboot the system to activate the changes. Configure name resolution ------------------------- #. Set the hostname of the node to ``controller``. #. .. include:: shared/edit_hosts_file.txt neutron-12.1.1/doc/source/install/controller-install-option1-rdo.rst0000664000175000017500000001741613553660047025577 0ustar zuulzuul00000000000000Networking Option 1: Provider networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Install and configure the Networking components on the *controller* node. Install the components ---------------------- .. code-block:: console # yum install openstack-neutron openstack-neutron-ml2 \ openstack-neutron-linuxbridge ebtables .. end Configure the server component ------------------------------ The Networking server component configuration includes the database, authentication mechanism, message queue, topology change notifications, and plug-in. .. include:: shared/note_configuration_vary_by_distribution.rst * Edit the ``/etc/neutron/neutron.conf`` file and complete the following actions: * In the ``[database]`` section, configure database access: .. path /etc/neutron/neutron.conf .. code-block:: ini [database] # ... connection = mysql+pymysql://neutron:NEUTRON_DBPASS@controller/neutron .. end Replace ``NEUTRON_DBPASS`` with the password you chose for the database. .. note:: Comment out or remove any other ``connection`` options in the ``[database]`` section. * In the ``[DEFAULT]`` section, enable the Modular Layer 2 (ML2) plug-in and disable additional plug-ins: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... core_plugin = ml2 service_plugins = .. end * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... transport_url = rabbit://openstack:RABBIT_PASS@controller .. end Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` account in RabbitMQ. * In the ``[DEFAULT]`` and ``[keystone_authtoken]`` sections, configure Identity service access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... auth_strategy = keystone [keystone_authtoken] # ... auth_uri = http://controller:5000 auth_url = http://controller:35357 memcached_servers = controller:11211 auth_type = password project_domain_name = default user_domain_name = default project_name = service username = neutron password = NEUTRON_PASS .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. .. note:: Comment out or remove any other options in the ``[keystone_authtoken]`` section. * In the ``[DEFAULT]`` and ``[nova]`` sections, configure Networking to notify Compute of network topology changes: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... notify_nova_on_port_status_changes = true notify_nova_on_port_data_changes = true [nova] # ... auth_url = http://controller:35357 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = nova password = NOVA_PASS .. end Replace ``NOVA_PASS`` with the password you chose for the ``nova`` user in the Identity service. * In the ``[oslo_concurrency]`` section, configure the lock path: .. path /etc/neutron/neutron.conf .. code-block:: ini [oslo_concurrency] # ... lock_path = /var/lib/neutron/tmp .. end Configure the Modular Layer 2 (ML2) plug-in ------------------------------------------- The ML2 plug-in uses the Linux bridge mechanism to build layer-2 (bridging and switching) virtual networking infrastructure for instances. * Edit the ``/etc/neutron/plugins/ml2/ml2_conf.ini`` file and complete the following actions: * In the ``[ml2]`` section, enable flat and VLAN networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... type_drivers = flat,vlan .. end * In the ``[ml2]`` section, disable self-service networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... tenant_network_types = .. end * In the ``[ml2]`` section, enable the Linux bridge mechanism: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... mechanism_drivers = linuxbridge .. end .. warning:: After you configure the ML2 plug-in, removing values in the ``type_drivers`` option can lead to database inconsistency. * In the ``[ml2]`` section, enable the port security extension driver: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... extension_drivers = port_security .. end * In the ``[ml2_type_flat]`` section, configure the provider virtual network as a flat network: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2_type_flat] # ... flat_networks = provider .. end * In the ``[securitygroup]`` section, enable ipset to increase efficiency of security group rules: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [securitygroup] # ... enable_ipset = true .. end Configure the Linux bridge agent -------------------------------- The Linux bridge agent builds layer-2 (bridging and switching) virtual networking infrastructure for instances and handles security groups. * Edit the ``/etc/neutron/plugins/ml2/linuxbridge_agent.ini`` file and complete the following actions: * In the ``[linux_bridge]`` section, map the provider virtual network to the provider physical network interface: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE_NAME .. end Replace ``PROVIDER_INTERFACE_NAME`` with the name of the underlying provider physical network interface. See :doc:`environment-networking-rdo` for more information. * In the ``[vxlan]`` section, disable VXLAN overlay networks: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [vxlan] enable_vxlan = false .. end * In the ``[securitygroup]`` section, enable security groups and configure the Linux bridge iptables firewall driver: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [securitygroup] # ... enable_security_group = true firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver .. end * Ensure your Linux operating system kernel supports network bridge filters by verifying all the following ``sysctl`` values are set to ``1``: .. code-block:: ini net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables .. end To enable networking bridge support, typically the ``br_netfilter`` kernel module needs to be loaded. Check your operating system's documentation for additional details on enabling this module. Configure the DHCP agent ------------------------ The DHCP agent provides DHCP services for virtual networks. * Edit the ``/etc/neutron/dhcp_agent.ini`` file and complete the following actions: * In the ``[DEFAULT]`` section, configure the Linux bridge interface driver, Dnsmasq DHCP driver, and enable isolated metadata so instances on provider networks can access metadata over the network: .. path /etc/neutron/dhcp_agent.ini .. code-block:: ini [DEFAULT] # ... interface_driver = linuxbridge dhcp_driver = neutron.agent.linux.dhcp.Dnsmasq enable_isolated_metadata = true .. end Return to *Networking controller node configuration*. neutron-12.1.1/doc/source/install/index.rst0000664000175000017500000000112113553660047020670 0ustar zuulzuul00000000000000.. _networking: ================== Networking service ================== .. toctree:: :maxdepth: 1 overview.rst common/get-started-networking.rst concepts.rst install-obs.rst install-rdo.rst install-ubuntu.rst This chapter explains how to install and configure the Networking service (neutron) using the :ref:`provider networks ` or :ref:`self-service networks ` option. For more information about the Networking service including virtual networking components, layout, and traffic flows, see the :doc:`OpenStack Networking Guide `. neutron-12.1.1/doc/source/install/environment-networking-obs.rst0000664000175000017500000000734013553660047025104 0ustar zuulzuul00000000000000Host networking ~~~~~~~~~~~~~~~ After installing the operating system on each node for the architecture that you choose to deploy, you must configure the network interfaces. We recommend that you disable any automated network management tools and manually edit the appropriate configuration files for your distribution. For more information on how to configure networking on your distribution, see the `SLES 12 `__ or `openSUSE `__ documentation. All nodes require Internet access for administrative purposes such as package installation, security updates, Domain Name System (DNS), and Network Time Protocol (NTP). In most cases, nodes should obtain Internet access through the management network interface. To highlight the importance of network separation, the example architectures use `private address space `__ for the management network and assume that the physical network infrastructure provides Internet access via Network Address Translation (NAT) or other methods. The example architectures use routable IP address space for the provider (external) network and assume that the physical network infrastructure provides direct Internet access. In the provider networks architecture, all instances attach directly to the provider network. In the self-service (private) networks architecture, instances can attach to a self-service or provider network. Self-service networks can reside entirely within OpenStack or provide some level of external network access using Network Address Translation (NAT) through the provider network. .. _figure-networklayout: .. figure:: figures/networklayout.png :alt: Network layout The example architectures assume use of the following networks: * Management on 10.0.0.0/24 with gateway 10.0.0.1 This network requires a gateway to provide Internet access to all nodes for administrative purposes such as package installation, security updates, Domain Name System (DNS), and Network Time Protocol (NTP). * Provider on 203.0.113.0/24 with gateway 203.0.113.1 This network requires a gateway to provide Internet access to instances in your OpenStack environment. You can modify these ranges and gateways to work with your particular network infrastructure. Network interface names vary by distribution. Traditionally, interfaces use ``eth`` followed by a sequential number. To cover all variations, this guide refers to the first interface as the interface with the lowest number and the second interface as the interface with the highest number. Unless you intend to use the exact configuration provided in this example architecture, you must modify the networks in this procedure to match your environment. Each node must resolve the other nodes by name in addition to IP address. For example, the ``controller`` name must resolve to ``10.0.0.11``, the IP address of the management interface on the controller node. .. warning:: Reconfiguring network interfaces will interrupt network connectivity. We recommend using a local terminal session for these procedures. .. note:: Your distribution enables a restrictive firewall by default. During the installation process, certain steps will fail unless you alter or disable the firewall. For more information about securing your environment, refer to the `OpenStack Security Guide `_. .. toctree:: :maxdepth: 1 environment-networking-controller-obs.rst environment-networking-compute-obs.rst environment-networking-storage-cinder.rst environment-networking-verify-obs.rst neutron-12.1.1/doc/source/install/install-ubuntu.rst0000664000175000017500000000041413553660046022552 0ustar zuulzuul00000000000000.. _networking-ubuntu: ================================ Install and configure for Ubuntu ================================ .. toctree:: :maxdepth: 2 environment-networking-ubuntu.rst controller-install-ubuntu.rst compute-install-ubuntu.rst verify.rst neutron-12.1.1/doc/source/install/controller-install-option1-obs.rst0000664000175000017500000001757213553660047025601 0ustar zuulzuul00000000000000Networking Option 1: Provider networks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Install and configure the Networking components on the *controller* node. Install the components ---------------------- .. code-block:: console # zypper install --no-recommends openstack-neutron \ openstack-neutron-server openstack-neutron-linuxbridge-agent \ openstack-neutron-dhcp-agent openstack-neutron-metadata-agent \ bridge-utils .. end Configure the server component ------------------------------ The Networking server component configuration includes the database, authentication mechanism, message queue, topology change notifications, and plug-in. .. include:: shared/note_configuration_vary_by_distribution.rst * Edit the ``/etc/neutron/neutron.conf`` file and complete the following actions: * In the ``[database]`` section, configure database access: .. path /etc/neutron/neutron.conf .. code-block:: ini [database] # ... connection = mysql+pymysql://neutron:NEUTRON_DBPASS@controller/neutron .. end Replace ``NEUTRON_DBPASS`` with the password you chose for the database. .. note:: Comment out or remove any other ``connection`` options in the ``[database]`` section. * In the ``[DEFAULT]`` section, enable the Modular Layer 2 (ML2) plug-in and disable additional plug-ins: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... core_plugin = ml2 service_plugins = .. end * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... transport_url = rabbit://openstack:RABBIT_PASS@controller .. end Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` account in RabbitMQ. * In the ``[DEFAULT]`` and ``[keystone_authtoken]`` sections, configure Identity service access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... auth_strategy = keystone [keystone_authtoken] # ... auth_uri = http://controller:5000 auth_url = http://controller:35357 memcached_servers = controller:11211 auth_type = password project_domain_name = default user_domain_name = default project_name = service username = neutron password = NEUTRON_PASS .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. .. note:: Comment out or remove any other options in the ``[keystone_authtoken]`` section. * In the ``[DEFAULT]`` and ``[nova]`` sections, configure Networking to notify Compute of network topology changes: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... notify_nova_on_port_status_changes = true notify_nova_on_port_data_changes = true [nova] # ... auth_url = http://controller:35357 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = nova password = NOVA_PASS .. end Replace ``NOVA_PASS`` with the password you chose for the ``nova`` user in the Identity service. * In the ``[oslo_concurrency]`` section, configure the lock path: .. path /etc/neutron/neutron.conf .. code-block:: ini [oslo_concurrency] # ... lock_path = /var/lib/neutron/tmp .. end Configure the Modular Layer 2 (ML2) plug-in ------------------------------------------- The ML2 plug-in uses the Linux bridge mechanism to build layer-2 (bridging and switching) virtual networking infrastructure for instances. * Edit the ``/etc/neutron/plugins/ml2/ml2_conf.ini`` file and complete the following actions: * In the ``[ml2]`` section, enable flat and VLAN networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... type_drivers = flat,vlan .. end * In the ``[ml2]`` section, disable self-service networks: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... tenant_network_types = .. end * In the ``[ml2]`` section, enable the Linux bridge mechanism: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... mechanism_drivers = linuxbridge .. end .. warning:: After you configure the ML2 plug-in, removing values in the ``type_drivers`` option can lead to database inconsistency. * In the ``[ml2]`` section, enable the port security extension driver: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2] # ... extension_drivers = port_security .. end * In the ``[ml2_type_flat]`` section, configure the provider virtual network as a flat network: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [ml2_type_flat] # ... flat_networks = provider .. end * In the ``[securitygroup]`` section, enable ipset to increase efficiency of security group rules: .. path /etc/neutron/plugins/ml2/ml2_conf.ini .. code-block:: ini [securitygroup] # ... enable_ipset = true .. end Configure the Linux bridge agent -------------------------------- The Linux bridge agent builds layer-2 (bridging and switching) virtual networking infrastructure for instances and handles security groups. * Edit the ``/etc/neutron/plugins/ml2/linuxbridge_agent.ini`` file and complete the following actions: * In the ``[linux_bridge]`` section, map the provider virtual network to the provider physical network interface: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [linux_bridge] physical_interface_mappings = provider:PROVIDER_INTERFACE_NAME .. end Replace ``PROVIDER_INTERFACE_NAME`` with the name of the underlying provider physical network interface. See :doc:`environment-networking-obs` for more information. * In the ``[vxlan]`` section, disable VXLAN overlay networks: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [vxlan] enable_vxlan = false .. end * In the ``[securitygroup]`` section, enable security groups and configure the Linux bridge iptables firewall driver: .. path /etc/neutron/plugins/ml2/linuxbridge_agent.ini .. code-block:: ini [securitygroup] # ... enable_security_group = true firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver .. end * Ensure your Linux operating system kernel supports network bridge filters by verifying all the following ``sysctl`` values are set to ``1``: .. code-block:: ini net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables .. end To enable networking bridge support, typically the ``br_netfilter`` kernel module needs to be loaded. Check your operating system's documentation for additional details on enabling this module. Configure the DHCP agent ------------------------ The DHCP agent provides DHCP services for virtual networks. * Edit the ``/etc/neutron/dhcp_agent.ini`` file and complete the following actions: * In the ``[DEFAULT]`` section, configure the Linux bridge interface driver, Dnsmasq DHCP driver, and enable isolated metadata so instances on provider networks can access metadata over the network: .. path /etc/neutron/dhcp_agent.ini .. code-block:: ini [DEFAULT] # ... interface_driver = linuxbridge dhcp_driver = neutron.agent.linux.dhcp.Dnsmasq enable_isolated_metadata = true .. end Return to *Networking controller node configuration*. neutron-12.1.1/doc/source/install/common/0000775000175000017500000000000013553660156020325 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/install/common/get-started-networking.rst0000664000175000017500000000251513553660046025470 0ustar zuulzuul00000000000000=========================== Networking service overview =========================== OpenStack Networking (neutron) allows you to create and attach interface devices managed by other OpenStack services to networks. Plug-ins can be implemented to accommodate different networking equipment and software, providing flexibility to OpenStack architecture and deployment. It includes the following components: neutron-server Accepts and routes API requests to the appropriate OpenStack Networking plug-in for action. OpenStack Networking plug-ins and agents Plug and unplug ports, create networks or subnets, and provide IP addressing. These plug-ins and agents differ depending on the vendor and technologies used in the particular cloud. OpenStack Networking ships with plug-ins and agents for Cisco virtual and physical switches, NEC OpenFlow products, Open vSwitch, Linux bridging, and the VMware NSX product. The common agents are L3 (layer 3), DHCP (dynamic host IP addressing), and a plug-in agent. Messaging queue Used by most OpenStack Networking installations to route information between the neutron-server and various agents. Also acts as a database to store networking state for particular plug-ins. OpenStack Networking mainly interacts with OpenStack Compute to provide networks and connectivity for its instances. neutron-12.1.1/doc/source/install/environment-networking-verify-rdo.rst0000664000175000017500000000611113553660046026401 0ustar zuulzuul00000000000000Verify connectivity ------------------- We recommend that you verify network connectivity to the Internet and among the nodes before proceeding further. #. From the *controller* node, test access to the Internet: .. code-block:: console # ping -c 4 openstack.org PING openstack.org (174.143.194.225) 56(84) bytes of data. 64 bytes from 174.143.194.225: icmp_seq=1 ttl=54 time=18.3 ms 64 bytes from 174.143.194.225: icmp_seq=2 ttl=54 time=17.5 ms 64 bytes from 174.143.194.225: icmp_seq=3 ttl=54 time=17.5 ms 64 bytes from 174.143.194.225: icmp_seq=4 ttl=54 time=17.4 ms --- openstack.org ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3022ms rtt min/avg/max/mdev = 17.489/17.715/18.346/0.364 ms .. end #. From the *controller* node, test access to the management interface on the *compute* node: .. code-block:: console # ping -c 4 compute1 PING compute1 (10.0.0.31) 56(84) bytes of data. 64 bytes from compute1 (10.0.0.31): icmp_seq=1 ttl=64 time=0.263 ms 64 bytes from compute1 (10.0.0.31): icmp_seq=2 ttl=64 time=0.202 ms 64 bytes from compute1 (10.0.0.31): icmp_seq=3 ttl=64 time=0.203 ms 64 bytes from compute1 (10.0.0.31): icmp_seq=4 ttl=64 time=0.202 ms --- compute1 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3000ms rtt min/avg/max/mdev = 0.202/0.217/0.263/0.030 ms .. end #. From the *compute* node, test access to the Internet: .. code-block:: console # ping -c 4 openstack.org PING openstack.org (174.143.194.225) 56(84) bytes of data. 64 bytes from 174.143.194.225: icmp_seq=1 ttl=54 time=18.3 ms 64 bytes from 174.143.194.225: icmp_seq=2 ttl=54 time=17.5 ms 64 bytes from 174.143.194.225: icmp_seq=3 ttl=54 time=17.5 ms 64 bytes from 174.143.194.225: icmp_seq=4 ttl=54 time=17.4 ms --- openstack.org ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3022ms rtt min/avg/max/mdev = 17.489/17.715/18.346/0.364 ms .. end #. From the *compute* node, test access to the management interface on the *controller* node: .. code-block:: console # ping -c 4 controller PING controller (10.0.0.11) 56(84) bytes of data. 64 bytes from controller (10.0.0.11): icmp_seq=1 ttl=64 time=0.263 ms 64 bytes from controller (10.0.0.11): icmp_seq=2 ttl=64 time=0.202 ms 64 bytes from controller (10.0.0.11): icmp_seq=3 ttl=64 time=0.203 ms 64 bytes from controller (10.0.0.11): icmp_seq=4 ttl=64 time=0.202 ms --- controller ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3000ms rtt min/avg/max/mdev = 0.202/0.217/0.263/0.030 ms .. end .. note:: Your distribution enables a restrictive firewall by default. During the installation process, certain steps will fail unless you alter or disable the firewall. For more information about securing your environment, refer to the `OpenStack Security Guide `_. neutron-12.1.1/doc/source/install/verify.rst0000664000175000017500000002130613553660047021074 0ustar zuulzuul00000000000000Verify operation ~~~~~~~~~~~~~~~~ .. note:: Perform these commands on the controller node. #. Source the ``admin`` credentials to gain access to admin-only CLI commands: .. code-block:: console $ . admin-openrc .. end #. List loaded extensions to verify successful launch of the ``neutron-server`` process: .. code-block:: console $ openstack extension list --network +---------------------------+---------------------------+----------------------------+ | Name | Alias | Description | +---------------------------+---------------------------+----------------------------+ | Default Subnetpools | default-subnetpools | Provides ability to mark | | | | and use a subnetpool as | | | | the default | | Availability Zone | availability_zone | The availability zone | | | | extension. | | Network Availability Zone | network_availability_zone | Availability zone support | | | | for network. | | Port Binding | binding | Expose port bindings of a | | | | virtual port to external | | | | application | | agent | agent | The agent management | | | | extension. | | Subnet Allocation | subnet_allocation | Enables allocation of | | | | subnets from a subnet pool | | DHCP Agent Scheduler | dhcp_agent_scheduler | Schedule networks among | | | | dhcp agents | | Tag support | tag | Enables to set tag on | | | | resources. | | Neutron external network | external-net | Adds external network | | | | attribute to network | | | | resource. | | Neutron Service Flavors | flavors | Flavor specification for | | | | Neutron advanced services | | Network MTU | net-mtu | Provides MTU attribute for | | | | a network resource. | | Network IP Availability | network-ip-availability | Provides IP availability | | | | data for each network and | | | | subnet. | | Quota management support | quotas | Expose functions for | | | | quotas management per | | | | tenant | | Provider Network | provider | Expose mapping of virtual | | | | networks to physical | | | | networks | | Multi Provider Network | multi-provider | Expose mapping of virtual | | | | networks to multiple | | | | physical networks | | Address scope | address-scope | Address scopes extension. | | Subnet service types | subnet-service-types | Provides ability to set | | | | the subnet service_types | | | | field | | Resource timestamps | standard-attr-timestamp | Adds created_at and | | | | updated_at fields to all | | | | Neutron resources that | | | | have Neutron standard | | | | attributes. | | Neutron Service Type | service-type | API for retrieving service | | Management | | providers for Neutron | | | | advanced services | | Tag support for | tag-ext | Extends tag support to | | resources: subnet, | | more L2 and L3 resources. | | subnetpool, port, router | | | | Neutron Extra DHCP opts | extra_dhcp_opt | Extra options | | | | configuration for DHCP. | | | | For example PXE boot | | | | options to DHCP clients | | | | can be specified (e.g. | | | | tftp-server, server-ip- | | | | address, bootfile-name) | | Resource revision numbers | standard-attr-revisions | This extension will | | | | display the revision | | | | number of neutron | | | | resources. | | Pagination support | pagination | Extension that indicates | | | | that pagination is | | | | enabled. | | Sorting support | sorting | Extension that indicates | | | | that sorting is enabled. | | security-group | security-group | The security groups | | | | extension. | | RBAC Policies | rbac-policies | Allows creation and | | | | modification of policies | | | | that control tenant access | | | | to resources. | | standard-attr-description | standard-attr-description | Extension to add | | | | descriptions to standard | | | | attributes | | Port Security | port-security | Provides port security | | Allowed Address Pairs | allowed-address-pairs | Provides allowed address | | | | pairs | | project_id field enabled | project-id | Extension that indicates | | | | that project_id field is | | | | enabled. | +---------------------------+---------------------------+----------------------------+ .. end .. note:: Actual output may differ slightly from this example. You can perform further testing of your networking using the `neutron-sanity-check command line client `_. Use the verification section for the networking option that you chose to deploy. .. toctree:: verify-option1.rst verify-option2.rst neutron-12.1.1/doc/source/install/controller-install-ubuntu.rst0000775000175000017500000002426413553660047024750 0ustar zuulzuul00000000000000Install and configure controller node ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prerequisites ------------- Before you configure the OpenStack Networking (neutron) service, you must create a database, service credentials, and API endpoints. #. To create the database, complete these steps: * Use the database access client to connect to the database server as the ``root`` user: .. code-block:: console $ mysql -u root -p .. end * Create the ``neutron`` database: .. code-block:: console MariaDB [(none)] CREATE DATABASE neutron; .. end * Grant proper access to the ``neutron`` database, replacing ``NEUTRON_DBPASS`` with a suitable password: .. code-block:: console MariaDB [(none)]> GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'localhost' \ IDENTIFIED BY 'NEUTRON_DBPASS'; MariaDB [(none)]> GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%' \ IDENTIFIED BY 'NEUTRON_DBPASS'; .. end * Exit the database access client. #. Source the ``admin`` credentials to gain access to admin-only CLI commands: .. code-block:: console $ . admin-openrc .. end #. To create the service credentials, complete these steps: * Create the ``neutron`` user: .. code-block:: console $ openstack user create --domain default --password-prompt neutron User Password: Repeat User Password: +---------------------+----------------------------------+ | Field | Value | +---------------------+----------------------------------+ | domain_id | default | | enabled | True | | id | fdb0f541e28141719b6a43c8944bf1fb | | name | neutron | | options | {} | | password_expires_at | None | +---------------------+----------------------------------+ .. end * Add the ``admin`` role to the ``neutron`` user: .. code-block:: console $ openstack role add --project service --user neutron admin .. end .. note:: This command provides no output. * Create the ``neutron`` service entity: .. code-block:: console $ openstack service create --name neutron \ --description "OpenStack Networking" network +-------------+----------------------------------+ | Field | Value | +-------------+----------------------------------+ | description | OpenStack Networking | | enabled | True | | id | f71529314dab4a4d8eca427e701d209e | | name | neutron | | type | network | +-------------+----------------------------------+ .. end #. Create the Networking service API endpoints: .. code-block:: console $ openstack endpoint create --region RegionOne \ network public http://controller:9696 +--------------+----------------------------------+ | Field | Value | +--------------+----------------------------------+ | enabled | True | | id | 85d80a6d02fc4b7683f611d7fc1493a3 | | interface | public | | region | RegionOne | | region_id | RegionOne | | service_id | f71529314dab4a4d8eca427e701d209e | | service_name | neutron | | service_type | network | | url | http://controller:9696 | +--------------+----------------------------------+ $ openstack endpoint create --region RegionOne \ network internal http://controller:9696 +--------------+----------------------------------+ | Field | Value | +--------------+----------------------------------+ | enabled | True | | id | 09753b537ac74422a68d2d791cf3714f | | interface | internal | | region | RegionOne | | region_id | RegionOne | | service_id | f71529314dab4a4d8eca427e701d209e | | service_name | neutron | | service_type | network | | url | http://controller:9696 | +--------------+----------------------------------+ $ openstack endpoint create --region RegionOne \ network admin http://controller:9696 +--------------+----------------------------------+ | Field | Value | +--------------+----------------------------------+ | enabled | True | | id | 1ee14289c9374dffb5db92a5c112fc4e | | interface | admin | | region | RegionOne | | region_id | RegionOne | | service_id | f71529314dab4a4d8eca427e701d209e | | service_name | neutron | | service_type | network | | url | http://controller:9696 | +--------------+----------------------------------+ .. end Configure networking options ---------------------------- You can deploy the Networking service using one of two architectures represented by options 1 and 2. Option 1 deploys the simplest possible architecture that only supports attaching instances to provider (external) networks. No self-service (private) networks, routers, or floating IP addresses. Only the ``admin`` or other privileged user can manage provider networks. Option 2 augments option 1 with layer-3 services that support attaching instances to self-service networks. The ``demo`` or other unprivileged user can manage self-service networks including routers that provide connectivity between self-service and provider networks. Additionally, floating IP addresses provide connectivity to instances using self-service networks from external networks such as the Internet. Self-service networks typically use overlay networks. Overlay network protocols such as VXLAN include additional headers that increase overhead and decrease space available for the payload or user data. Without knowledge of the virtual network infrastructure, instances attempt to send packets using the default Ethernet maximum transmission unit (MTU) of 1500 bytes. The Networking service automatically provides the correct MTU value to instances via DHCP. However, some cloud images do not use DHCP or ignore the DHCP MTU option and require configuration using metadata or a script. .. note:: Option 2 also supports attaching instances to provider networks. Choose one of the following networking options to configure services specific to it. Afterwards, return here and proceed to :ref:`neutron-controller-metadata-agent-ubuntu`. .. toctree:: :maxdepth: 1 controller-install-option1-ubuntu.rst controller-install-option2-ubuntu.rst .. _neutron-controller-metadata-agent-ubuntu: Configure the metadata agent ---------------------------- The metadata agent provides configuration information such as credentials to instances. * Edit the ``/etc/neutron/metadata_agent.ini`` file and complete the following actions: * In the ``[DEFAULT]`` section, configure the metadata host and shared secret: .. path /etc/neutron/metadata_agent.ini .. code-block:: ini [DEFAULT] # ... nova_metadata_host = controller metadata_proxy_shared_secret = METADATA_SECRET .. end Replace ``METADATA_SECRET`` with a suitable secret for the metadata proxy. Configure the Compute service to use the Networking service ----------------------------------------------------------- .. note:: The Nova compute service must be installed to complete this step. For more details see the compute install guide found under the `Installation Guides` section of the `docs website `_. * Edit the ``/etc/nova/nova.conf`` file and perform the following actions: * In the ``[neutron]`` section, configure access parameters, enable the metadata proxy, and configure the secret: .. path /etc/nova/nova.conf .. code-block:: ini [neutron] # ... url = http://controller:9696 auth_url = http://controller:5000 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = neutron password = NEUTRON_PASS service_metadata_proxy = true metadata_proxy_shared_secret = METADATA_SECRET .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. Replace ``METADATA_SECRET`` with the secret you chose for the metadata proxy. Finalize installation --------------------- #. Populate the database: .. code-block:: console # su -s /bin/sh -c "neutron-db-manage --config-file /etc/neutron/neutron.conf \ --config-file /etc/neutron/plugins/ml2/ml2_conf.ini upgrade head" neutron .. end .. note:: Database population occurs later for Networking because the script requires complete server and plug-in configuration files. #. Restart the Compute API service: .. code-block:: console # service nova-api restart .. end #. Restart the Networking services. For both networking options: .. code-block:: console # service neutron-server restart # service neutron-linuxbridge-agent restart # service neutron-dhcp-agent restart # service neutron-metadata-agent restart .. end For networking option 2, also restart the layer-3 service: .. code-block:: console # service neutron-l3-agent restart .. end neutron-12.1.1/doc/source/install/compute-install-obs.rst0000664000175000017500000001011013553660047023460 0ustar zuulzuul00000000000000Install and configure compute node ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The compute node handles connectivity and security groups for instances. Install the components ---------------------- .. code-block:: console # zypper install --no-recommends \ openstack-neutron-linuxbridge-agent bridge-utils .. end Configure the common component ------------------------------ The Networking common component configuration includes the authentication mechanism, message queue, and plug-in. .. include:: shared/note_configuration_vary_by_distribution.rst * Edit the ``/etc/neutron/neutron.conf`` file and complete the following actions: * In the ``[database]`` section, comment out any ``connection`` options because compute nodes do not directly access the database. * In the ``[DEFAULT]`` section, configure ``RabbitMQ`` message queue access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... transport_url = rabbit://openstack:RABBIT_PASS@controller .. end Replace ``RABBIT_PASS`` with the password you chose for the ``openstack`` account in RabbitMQ. * In the ``[DEFAULT]`` and ``[keystone_authtoken]`` sections, configure Identity service access: .. path /etc/neutron/neutron.conf .. code-block:: ini [DEFAULT] # ... auth_strategy = keystone [keystone_authtoken] # ... auth_uri = http://controller:5000 auth_url = http://controller:35357 memcached_servers = controller:11211 auth_type = password project_domain_name = default user_domain_name = default project_name = service username = neutron password = NEUTRON_PASS .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. .. note:: Comment out or remove any other options in the ``[keystone_authtoken]`` section. * In the ``[oslo_concurrency]`` section, configure the lock path: .. path /etc/neutron/neutron.conf .. code-block:: ini [oslo_concurrency] # ... lock_path = /var/lib/neutron/tmp .. end Configure networking options ---------------------------- Choose the same networking option that you chose for the controller node to configure services specific to it. Afterwards, return here and proceed to :ref:`neutron-compute-compute-obs`. .. toctree:: :maxdepth: 1 compute-install-option1-obs.rst compute-install-option2-obs.rst .. _neutron-compute-compute-obs: Configure the Compute service to use the Networking service ----------------------------------------------------------- * Edit the ``/etc/nova/nova.conf`` file and complete the following actions: * In the ``[neutron]`` section, configure access parameters: .. path /etc/nova/nova.conf .. code-block:: ini [neutron] # ... url = http://controller:9696 auth_url = http://controller:35357 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = neutron password = NEUTRON_PASS .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. Finalize installation --------------------- #. The Networking service initialization scripts expect the variable ``NEUTRON_PLUGIN_CONF`` in the ``/etc/sysconfig/neutron`` file to reference the ML2 plug-in configuration file. Ensure that the ``/etc/sysconfig/neutron`` file contains the following: .. path /etc/sysconfig/neutron .. code-block:: ini NEUTRON_PLUGIN_CONF="/etc/neutron/plugins/ml2/ml2_conf.ini" .. end #. Restart the Compute service: .. code-block:: console # systemctl restart openstack-nova-compute.service .. end #. Start the Linux Bridge agent and configure it to start when the system boots: .. code-block:: console # systemctl enable openstack-neutron-linuxbridge-agent.service # systemctl start openstack-neutron-linuxbridge-agent.service .. end neutron-12.1.1/doc/source/install/environment-networking-verify-ubuntu.rst0000664000175000017500000000575213553660046027151 0ustar zuulzuul00000000000000Verify connectivity ------------------- We recommend that you verify network connectivity to the Internet and among the nodes before proceeding further. #. From the *controller* node, test access to the Internet: .. code-block:: console # ping -c 4 openstack.org PING openstack.org (174.143.194.225) 56(84) bytes of data. 64 bytes from 174.143.194.225: icmp_seq=1 ttl=54 time=18.3 ms 64 bytes from 174.143.194.225: icmp_seq=2 ttl=54 time=17.5 ms 64 bytes from 174.143.194.225: icmp_seq=3 ttl=54 time=17.5 ms 64 bytes from 174.143.194.225: icmp_seq=4 ttl=54 time=17.4 ms --- openstack.org ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3022ms rtt min/avg/max/mdev = 17.489/17.715/18.346/0.364 ms .. end #. From the *controller* node, test access to the management interface on the *compute* node: .. code-block:: console # ping -c 4 compute1 PING compute1 (10.0.0.31) 56(84) bytes of data. 64 bytes from compute1 (10.0.0.31): icmp_seq=1 ttl=64 time=0.263 ms 64 bytes from compute1 (10.0.0.31): icmp_seq=2 ttl=64 time=0.202 ms 64 bytes from compute1 (10.0.0.31): icmp_seq=3 ttl=64 time=0.203 ms 64 bytes from compute1 (10.0.0.31): icmp_seq=4 ttl=64 time=0.202 ms --- compute1 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3000ms rtt min/avg/max/mdev = 0.202/0.217/0.263/0.030 ms .. end #. From the *compute* node, test access to the Internet: .. code-block:: console # ping -c 4 openstack.org PING openstack.org (174.143.194.225) 56(84) bytes of data. 64 bytes from 174.143.194.225: icmp_seq=1 ttl=54 time=18.3 ms 64 bytes from 174.143.194.225: icmp_seq=2 ttl=54 time=17.5 ms 64 bytes from 174.143.194.225: icmp_seq=3 ttl=54 time=17.5 ms 64 bytes from 174.143.194.225: icmp_seq=4 ttl=54 time=17.4 ms --- openstack.org ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3022ms rtt min/avg/max/mdev = 17.489/17.715/18.346/0.364 ms .. end #. From the *compute* node, test access to the management interface on the *controller* node: .. code-block:: console # ping -c 4 controller PING controller (10.0.0.11) 56(84) bytes of data. 64 bytes from controller (10.0.0.11): icmp_seq=1 ttl=64 time=0.263 ms 64 bytes from controller (10.0.0.11): icmp_seq=2 ttl=64 time=0.202 ms 64 bytes from controller (10.0.0.11): icmp_seq=3 ttl=64 time=0.203 ms 64 bytes from controller (10.0.0.11): icmp_seq=4 ttl=64 time=0.202 ms --- controller ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3000ms rtt min/avg/max/mdev = 0.202/0.217/0.263/0.030 ms .. end .. note:: Your distribution does not enable a restrictive firewall by default. For more information about securing your environment, refer to the `OpenStack Security Guide `_. neutron-12.1.1/doc/source/install/controller-install-obs.rst0000775000175000017500000002473213553660047024211 0ustar zuulzuul00000000000000Install and configure controller node ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prerequisites ------------- Before you configure the OpenStack Networking (neutron) service, you must create a database, service credentials, and API endpoints. #. To create the database, complete these steps: * Use the database access client to connect to the database server as the ``root`` user: .. code-block:: console $ mysql -u root -p .. end * Create the ``neutron`` database: .. code-block:: console MariaDB [(none)] CREATE DATABASE neutron; .. end * Grant proper access to the ``neutron`` database, replacing ``NEUTRON_DBPASS`` with a suitable password: .. code-block:: console MariaDB [(none)]> GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'localhost' \ IDENTIFIED BY 'NEUTRON_DBPASS'; MariaDB [(none)]> GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%' \ IDENTIFIED BY 'NEUTRON_DBPASS'; .. end * Exit the database access client. #. Source the ``admin`` credentials to gain access to admin-only CLI commands: .. code-block:: console $ . admin-openrc .. end #. To create the service credentials, complete these steps: * Create the ``neutron`` user: .. code-block:: console $ openstack user create --domain default --password-prompt neutron User Password: Repeat User Password: +---------------------+----------------------------------+ | Field | Value | +---------------------+----------------------------------+ | domain_id | default | | enabled | True | | id | fdb0f541e28141719b6a43c8944bf1fb | | name | neutron | | options | {} | | password_expires_at | None | +---------------------+----------------------------------+ .. end * Add the ``admin`` role to the ``neutron`` user: .. code-block:: console $ openstack role add --project service --user neutron admin .. end .. note:: This command provides no output. * Create the ``neutron`` service entity: .. code-block:: console $ openstack service create --name neutron \ --description "OpenStack Networking" network +-------------+----------------------------------+ | Field | Value | +-------------+----------------------------------+ | description | OpenStack Networking | | enabled | True | | id | f71529314dab4a4d8eca427e701d209e | | name | neutron | | type | network | +-------------+----------------------------------+ .. end #. Create the Networking service API endpoints: .. code-block:: console $ openstack endpoint create --region RegionOne \ network public http://controller:9696 +--------------+----------------------------------+ | Field | Value | +--------------+----------------------------------+ | enabled | True | | id | 85d80a6d02fc4b7683f611d7fc1493a3 | | interface | public | | region | RegionOne | | region_id | RegionOne | | service_id | f71529314dab4a4d8eca427e701d209e | | service_name | neutron | | service_type | network | | url | http://controller:9696 | +--------------+----------------------------------+ $ openstack endpoint create --region RegionOne \ network internal http://controller:9696 +--------------+----------------------------------+ | Field | Value | +--------------+----------------------------------+ | enabled | True | | id | 09753b537ac74422a68d2d791cf3714f | | interface | internal | | region | RegionOne | | region_id | RegionOne | | service_id | f71529314dab4a4d8eca427e701d209e | | service_name | neutron | | service_type | network | | url | http://controller:9696 | +--------------+----------------------------------+ $ openstack endpoint create --region RegionOne \ network admin http://controller:9696 +--------------+----------------------------------+ | Field | Value | +--------------+----------------------------------+ | enabled | True | | id | 1ee14289c9374dffb5db92a5c112fc4e | | interface | admin | | region | RegionOne | | region_id | RegionOne | | service_id | f71529314dab4a4d8eca427e701d209e | | service_name | neutron | | service_type | network | | url | http://controller:9696 | +--------------+----------------------------------+ .. end Configure networking options ---------------------------- You can deploy the Networking service using one of two architectures represented by options 1 and 2. Option 1 deploys the simplest possible architecture that only supports attaching instances to provider (external) networks. No self-service (private) networks, routers, or floating IP addresses. Only the ``admin`` or other privileged user can manage provider networks. Option 2 augments option 1 with layer-3 services that support attaching instances to self-service networks. The ``demo`` or other unprivileged user can manage self-service networks including routers that provide connectivity between self-service and provider networks. Additionally, floating IP addresses provide connectivity to instances using self-service networks from external networks such as the Internet. Self-service networks typically use overlay networks. Overlay network protocols such as VXLAN include additional headers that increase overhead and decrease space available for the payload or user data. Without knowledge of the virtual network infrastructure, instances attempt to send packets using the default Ethernet maximum transmission unit (MTU) of 1500 bytes. The Networking service automatically provides the correct MTU value to instances via DHCP. However, some cloud images do not use DHCP or ignore the DHCP MTU option and require configuration using metadata or a script. .. note:: Option 2 also supports attaching instances to provider networks. Choose one of the following networking options to configure services specific to it. Afterwards, return here and proceed to :ref:`neutron-controller-metadata-agent-obs`. .. toctree:: :maxdepth: 1 controller-install-option1-obs.rst controller-install-option2-obs.rst .. _neutron-controller-metadata-agent-obs: Configure the metadata agent ---------------------------- The metadata agent provides configuration information such as credentials to instances. * Edit the ``/etc/neutron/metadata_agent.ini`` file and complete the following actions: * In the ``[DEFAULT]`` section, configure the metadata host and shared secret: .. path /etc/neutron/metadata_agent.ini .. code-block:: ini [DEFAULT] # ... nova_metadata_host = controller metadata_proxy_shared_secret = METADATA_SECRET .. end Replace ``METADATA_SECRET`` with a suitable secret for the metadata proxy. Configure the Compute service to use the Networking service ----------------------------------------------------------- .. note:: The Nova compute service must be installed to complete this step. For more details see the compute install guide found under the `Installation Guides` section of the `docs website `_. * Edit the ``/etc/nova/nova.conf`` file and perform the following actions: * In the ``[neutron]`` section, configure access parameters, enable the metadata proxy, and configure the secret: .. path /etc/nova/nova.conf .. code-block:: ini [neutron] # ... url = http://controller:9696 auth_url = http://controller:35357 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = neutron password = NEUTRON_PASS service_metadata_proxy = true metadata_proxy_shared_secret = METADATA_SECRET .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. Replace ``METADATA_SECRET`` with the secret you chose for the metadata proxy. Finalize installation --------------------- .. note:: SLES enables apparmor by default and restricts dnsmasq. You need to either completely disable apparmor or disable only the dnsmasq profile: .. code-block:: console # ln -s /etc/apparmor.d/usr.sbin.dnsmasq /etc/apparmor.d/disable/ # systemctl restart apparmor .. end #. Restart the Compute API service: .. code-block:: console # systemctl restart openstack-nova-api.service .. end #. Start the Networking services and configure them to start when the system boots. For both networking options: .. code-block:: console # systemctl enable openstack-neutron.service \ openstack-neutron-linuxbridge-agent.service \ openstack-neutron-dhcp-agent.service \ openstack-neutron-metadata-agent.service # systemctl start openstack-neutron.service \ openstack-neutron-linuxbridge-agent.service \ openstack-neutron-dhcp-agent.service \ openstack-neutron-metadata-agent.service .. end For networking option 2, also enable and start the layer-3 service: .. code-block:: console # systemctl enable openstack-neutron-l3-agent.service # systemctl start openstack-neutron-l3-agent.service .. end neutron-12.1.1/doc/source/install/environment-networking-compute-obs.rst0000664000175000017500000000177213553660046026560 0ustar zuulzuul00000000000000Compute node ~~~~~~~~~~~~ Configure network interfaces ---------------------------- #. Configure the first interface as the management interface: IP address: 10.0.0.31 Network mask: 255.255.255.0 (or /24) Default gateway: 10.0.0.1 .. note:: Additional compute nodes should use 10.0.0.32, 10.0.0.33, and so on. #. The provider interface uses a special configuration without an IP address assigned to it. Configure the second interface as the provider interface: Replace ``INTERFACE_NAME`` with the actual interface name. For example, *eth1* or *ens224*. * Edit the ``/etc/sysconfig/network/ifcfg-INTERFACE_NAME`` file to contain the following: .. path /etc/sysconfig/network/ifcfg-INTERFACE_NAME .. code-block:: bash STARTMODE='auto' BOOTPROTO='static' .. end #. Reboot the system to activate the changes. Configure name resolution ------------------------- #. Set the hostname of the node to ``compute1``. #. .. include:: shared/edit_hosts_file.txt neutron-12.1.1/doc/source/install/shared/0000775000175000017500000000000013553660156020303 5ustar zuulzuul00000000000000neutron-12.1.1/doc/source/install/shared/edit_hosts_file.txt0000664000175000017500000000136613553660046024214 0ustar zuulzuul00000000000000Edit the ``/etc/hosts`` file to contain the following: .. path /etc/hosts .. code-block:: none # controller 10.0.0.11 controller # compute1 10.0.0.31 compute1 # block1 10.0.0.41 block1 # object1 10.0.0.51 object1 # object2 10.0.0.52 object2 .. end .. warning:: Some distributions add an extraneous entry in the ``/etc/hosts`` file that resolves the actual hostname to another loopback IP address such as ``127.0.1.1``. You must comment out or remove this entry to prevent name resolution problems. **Do not remove the 127.0.0.1 entry.** .. note:: This guide includes host entries for optional services in order to reduce complexity should you choose to deploy them. neutron-12.1.1/doc/source/install/shared/note_configuration_vary_by_distribution.rst0000664000175000017500000000046613553660046031267 0ustar zuulzuul00000000000000.. note:: Default configuration files vary by distribution. You might need to add these sections and options rather than modifying existing sections and options. Also, an ellipsis (``...``) in the configuration snippets indicates potential default configuration options that you should retain. neutron-12.1.1/doc/source/install/concepts.rst0000664000175000017500000000540513553660047021410 0ustar zuulzuul00000000000000Networking (neutron) concepts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OpenStack Networking (neutron) manages all networking facets for the Virtual Networking Infrastructure (VNI) and the access layer aspects of the Physical Networking Infrastructure (PNI) in your OpenStack environment. OpenStack Networking enables projects to create advanced virtual network topologies which may include services such as a firewall, a load balancer, and a virtual private network (VPN). Networking provides networks, subnets, and routers as object abstractions. Each abstraction has functionality that mimics its physical counterpart: networks contain subnets, and routers route traffic between different subnets and networks. Any given Networking set up has at least one external network. Unlike the other networks, the external network is not merely a virtually defined network. Instead, it represents a view into a slice of the physical, external network accessible outside the OpenStack installation. IP addresses on the external network are accessible by anybody physically on the outside network. In addition to external networks, any Networking set up has one or more internal networks. These software-defined networks connect directly to the VMs. Only the VMs on any given internal network, or those on subnets connected through interfaces to a similar router, can access VMs connected to that network directly. For the outside network to access VMs, and vice versa, routers between the networks are needed. Each router has one gateway that is connected to an external network and one or more interfaces connected to internal networks. Like a physical router, subnets can access machines on other subnets that are connected to the same router, and machines can access the outside network through the gateway for the router. Additionally, you can allocate IP addresses on external networks to ports on the internal network. Whenever something is connected to a subnet, that connection is called a port. You can associate external network IP addresses with ports to VMs. This way, entities on the outside network can access VMs. Networking also supports *security groups*. Security groups enable administrators to define firewall rules in groups. A VM can belong to one or more security groups, and Networking applies the rules in those security groups to block or unblock ports, port ranges, or traffic types for that VM. Each plug-in that Networking uses has its own concepts. While not vital to operating the VNI and OpenStack environment, understanding these concepts can help you set up Networking. All Networking installations use a core plug-in and a security group plug-in (or just the No-Op security group plug-in). Additionally, Firewall-as-a-Service (FWaaS) and Load-Balancer-as-a-Service (LBaaS) plug-ins are available. neutron-12.1.1/doc/source/install/environment-networking-compute-rdo.rst0000664000175000017500000000214513553660046026554 0ustar zuulzuul00000000000000Compute node ~~~~~~~~~~~~ Configure network interfaces ---------------------------- #. Configure the first interface as the management interface: IP address: 10.0.0.31 Network mask: 255.255.255.0 (or /24) Default gateway: 10.0.0.1 .. note:: Additional compute nodes should use 10.0.0.32, 10.0.0.33, and so on. #. The provider interface uses a special configuration without an IP address assigned to it. Configure the second interface as the provider interface: Replace ``INTERFACE_NAME`` with the actual interface name. For example, *eth1* or *ens224*. * Edit the ``/etc/sysconfig/network-scripts/ifcfg-INTERFACE_NAME`` file to contain the following: Do not change the ``HWADDR`` and ``UUID`` keys. .. path /etc/sysconfig/network-scripts/ifcfg-INTERFACE_NAME .. code-block:: bash DEVICE=INTERFACE_NAME TYPE=Ethernet ONBOOT="yes" BOOTPROTO="none" .. end #. Reboot the system to activate the changes. Configure name resolution ------------------------- #. Set the hostname of the node to ``compute1``. #. .. include:: shared/edit_hosts_file.txt neutron-12.1.1/doc/source/install/controller-install-rdo.rst0000775000175000017500000002551113553660047024206 0ustar zuulzuul00000000000000Install and configure controller node ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prerequisites ------------- Before you configure the OpenStack Networking (neutron) service, you must create a database, service credentials, and API endpoints. #. To create the database, complete these steps: * Use the database access client to connect to the database server as the ``root`` user: .. code-block:: console $ mysql -u root -p .. end * Create the ``neutron`` database: .. code-block:: console MariaDB [(none)] CREATE DATABASE neutron; .. end * Grant proper access to the ``neutron`` database, replacing ``NEUTRON_DBPASS`` with a suitable password: .. code-block:: console MariaDB [(none)]> GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'localhost' \ IDENTIFIED BY 'NEUTRON_DBPASS'; MariaDB [(none)]> GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%' \ IDENTIFIED BY 'NEUTRON_DBPASS'; .. end * Exit the database access client. #. Source the ``admin`` credentials to gain access to admin-only CLI commands: .. code-block:: console $ . admin-openrc .. end #. To create the service credentials, complete these steps: * Create the ``neutron`` user: .. code-block:: console $ openstack user create --domain default --password-prompt neutron User Password: Repeat User Password: +---------------------+----------------------------------+ | Field | Value | +---------------------+----------------------------------+ | domain_id | default | | enabled | True | | id | fdb0f541e28141719b6a43c8944bf1fb | | name | neutron | | options | {} | | password_expires_at | None | +---------------------+----------------------------------+ .. end * Add the ``admin`` role to the ``neutron`` user: .. code-block:: console $ openstack role add --project service --user neutron admin .. end .. note:: This command provides no output. * Create the ``neutron`` service entity: .. code-block:: console $ openstack service create --name neutron \ --description "OpenStack Networking" network +-------------+----------------------------------+ | Field | Value | +-------------+----------------------------------+ | description | OpenStack Networking | | enabled | True | | id | f71529314dab4a4d8eca427e701d209e | | name | neutron | | type | network | +-------------+----------------------------------+ .. end #. Create the Networking service API endpoints: .. code-block:: console $ openstack endpoint create --region RegionOne \ network public http://controller:9696 +--------------+----------------------------------+ | Field | Value | +--------------+----------------------------------+ | enabled | True | | id | 85d80a6d02fc4b7683f611d7fc1493a3 | | interface | public | | region | RegionOne | | region_id | RegionOne | | service_id | f71529314dab4a4d8eca427e701d209e | | service_name | neutron | | service_type | network | | url | http://controller:9696 | +--------------+----------------------------------+ $ openstack endpoint create --region RegionOne \ network internal http://controller:9696 +--------------+----------------------------------+ | Field | Value | +--------------+----------------------------------+ | enabled | True | | id | 09753b537ac74422a68d2d791cf3714f | | interface | internal | | region | RegionOne | | region_id | RegionOne | | service_id | f71529314dab4a4d8eca427e701d209e | | service_name | neutron | | service_type | network | | url | http://controller:9696 | +--------------+----------------------------------+ $ openstack endpoint create --region RegionOne \ network admin http://controller:9696 +--------------+----------------------------------+ | Field | Value | +--------------+----------------------------------+ | enabled | True | | id | 1ee14289c9374dffb5db92a5c112fc4e | | interface | admin | | region | RegionOne | | region_id | RegionOne | | service_id | f71529314dab4a4d8eca427e701d209e | | service_name | neutron | | service_type | network | | url | http://controller:9696 | +--------------+----------------------------------+ .. end Configure networking options ---------------------------- You can deploy the Networking service using one of two architectures represented by options 1 and 2. Option 1 deploys the simplest possible architecture that only supports attaching instances to provider (external) networks. No self-service (private) networks, routers, or floating IP addresses. Only the ``admin`` or other privileged user can manage provider networks. Option 2 augments option 1 with layer-3 services that support attaching instances to self-service networks. The ``demo`` or other unprivileged user can manage self-service networks including routers that provide connectivity between self-service and provider networks. Additionally, floating IP addresses provide connectivity to instances using self-service networks from external networks such as the Internet. Self-service networks typically use overlay networks. Overlay network protocols such as VXLAN include additional headers that increase overhead and decrease space available for the payload or user data. Without knowledge of the virtual network infrastructure, instances attempt to send packets using the default Ethernet maximum transmission unit (MTU) of 1500 bytes. The Networking service automatically provides the correct MTU value to instances via DHCP. However, some cloud images do not use DHCP or ignore the DHCP MTU option and require configuration using metadata or a script. .. note:: Option 2 also supports attaching instances to provider networks. Choose one of the following networking options to configure services specific to it. Afterwards, return here and proceed to :ref:`neutron-controller-metadata-agent-rdo`. .. toctree:: :maxdepth: 1 controller-install-option1-rdo.rst controller-install-option2-rdo.rst .. _neutron-controller-metadata-agent-rdo: Configure the metadata agent ---------------------------- The metadata agent provides configuration information such as credentials to instances. * Edit the ``/etc/neutron/metadata_agent.ini`` file and complete the following actions: * In the ``[DEFAULT]`` section, configure the metadata host and shared secret: .. path /etc/neutron/metadata_agent.ini .. code-block:: ini [DEFAULT] # ... nova_metadata_host = controller metadata_proxy_shared_secret = METADATA_SECRET .. end Replace ``METADATA_SECRET`` with a suitable secret for the metadata proxy. Configure the Compute service to use the Networking service ----------------------------------------------------------- .. note:: The Nova compute service must be installed to complete this step. For more details see the compute install guide found under the `Installation Guides` section of the `docs website `_. * Edit the ``/etc/nova/nova.conf`` file and perform the following actions: * In the ``[neutron]`` section, configure access parameters, enable the metadata proxy, and configure the secret: .. path /etc/nova/nova.conf .. code-block:: ini [neutron] # ... url = http://controller:9696 auth_url = http://controller:35357 auth_type = password project_domain_name = default user_domain_name = default region_name = RegionOne project_name = service username = neutron password = NEUTRON_PASS service_metadata_proxy = true metadata_proxy_shared_secret = METADATA_SECRET .. end Replace ``NEUTRON_PASS`` with the password you chose for the ``neutron`` user in the Identity service. Replace ``METADATA_SECRET`` with the secret you chose for the metadata proxy. Finalize installation --------------------- #. The Networking service initialization scripts expect a symbolic link ``/etc/neutron/plugin.ini`` pointing to the ML2 plug-in configuration file, ``/etc/neutron/plugins/ml2/ml2_conf.ini``. If this symbolic link does not exist, create it using the following command: .. code-block:: console # ln -s /etc/neutron/plugins/ml2/ml2_conf.ini /etc/neutron/plugin.ini .. end #. Populate the database: .. code-block:: console # su -s /bin/sh -c "neutron-db-manage --config-file /etc/neutron/neutron.conf \ --config-file /etc/neutron/plugins/ml2/ml2_conf.ini upgrade head" neutron .. end .. note:: Database population occurs later for Networking because the script requires complete server and plug-in configuration files. #. Restart the Compute API service: .. code-block:: console # systemctl restart openstack-nova-api.service .. end #. Start the Networking services and configure them to start when the system boots. For both networking options: .. code-block:: console # systemctl enable neutron-server.service \ neutron-linuxbridge-agent.service neutron-dhcp-agent.service \ neutron-metadata-agent.service # systemctl start neutron-server.service \ neutron-linuxbridge-agent.service neutron-dhcp-agent.service \ neutron-metadata-agent.service .. end For networking option 2, also enable and start the layer-3 service: .. code-block:: console # systemctl enable neutron-l3-agent.service # systemctl start neutron-l3-agent.service .. end neutron-12.1.1/doc/source/install/environment-networking-compute-ubuntu.rst0000664000175000017500000000212013553660046027303 0ustar zuulzuul00000000000000Compute node ~~~~~~~~~~~~ Configure network interfaces ---------------------------- #. Configure the first interface as the management interface: IP address: 10.0.0.31 Network mask: 255.255.255.0 (or /24) Default gateway: 10.0.0.1 .. note:: Additional compute nodes should use 10.0.0.32, 10.0.0.33, and so on. #. The provider interface uses a special configuration without an IP address assigned to it. Configure the second interface as the provider interface: Replace ``INTERFACE_NAME`` with the actual interface name. For example, *eth1* or *ens224*. * Edit the ``/etc/network/interfaces`` file to contain the following: .. path /etc/network/interfaces .. code-block:: bash # The provider network interface auto INTERFACE_NAME iface INTERFACE_NAME inet manual up ip link set dev $IFACE up down ip link set dev $IFACE down .. end #. Reboot the system to activate the changes. Configure name resolution ------------------------- #. Set the hostname of the node to ``compute1``. #. .. include:: shared/edit_hosts_file.txt neutron-12.1.1/doc/source/install/environment-networking-rdo.rst0000664000175000017500000000722213553660047025104 0ustar zuulzuul00000000000000Host networking ~~~~~~~~~~~~~~~ After installing the operating system on each node for the architecture that you choose to deploy, you must configure the network interfaces. We recommend that you disable any automated network management tools and manually edit the appropriate configuration files for your distribution. For more information on how to configure networking on your distribution, see the `documentation `__ . All nodes require Internet access for administrative purposes such as package installation, security updates, Domain Name System (DNS), and Network Time Protocol (NTP). In most cases, nodes should obtain Internet access through the management network interface. To highlight the importance of network separation, the example architectures use `private address space `__ for the management network and assume that the physical network infrastructure provides Internet access via Network Address Translation (NAT) or other methods. The example architectures use routable IP address space for the provider (external) network and assume that the physical network infrastructure provides direct Internet access. In the provider networks architecture, all instances attach directly to the provider network. In the self-service (private) networks architecture, instances can attach to a self-service or provider network. Self-service networks can reside entirely within OpenStack or provide some level of external network access using Network Address Translation (NAT) through the provider network. .. _figure-networklayout: .. figure:: figures/networklayout.png :alt: Network layout The example architectures assume use of the following networks: * Management on 10.0.0.0/24 with gateway 10.0.0.1 This network requires a gateway to provide Internet access to all nodes for administrative purposes such as package installation, security updates, Domain Name System (DNS), and Network Time Protocol (NTP). * Provider on 203.0.113.0/24 with gateway 203.0.113.1 This network requires a gateway to provide Internet access to instances in your OpenStack environment. You can modify these ranges and gateways to work with your particular network infrastructure. Network interface names vary by distribution. Traditionally, interfaces use ``eth`` followed by a sequential number. To cover all variations, this guide refers to the first interface as the interface with the lowest number and the second interface as the interface with the highest number. Unless you intend to use the exact configuration provided in this example architecture, you must modify the networks in this procedure to match your environment. Each node must resolve the other nodes by name in addition to IP address. For example, the ``controller`` name must resolve to ``10.0.0.11``, the IP address of the management interface on the controller node. .. warning:: Reconfiguring network interfaces will interrupt network connectivity. We recommend using a local terminal session for these procedures. .. note:: Your distribution enables a restrictive firewall by default. During the installation process, certain steps will fail unless you alter or disable the firewall. For more information about securing your environment, refer to the `OpenStack Security Guide `_. .. toctree:: :maxdepth: 1 environment-networking-controller-rdo.rst environment-networking-compute-rdo.rst environment-networking-storage-cinder.rst environment-networking-verify-rdo.rst neutron-12.1.1/doc/Makefile0000664000175000017500000000633713553660046015536 0ustar zuulzuul00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXSOURCE = source PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SPHINXSOURCE) .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest .DEFAULT_GOAL = html help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* if [ -f .autogenerated ] ; then \ cat .autogenerated | xargs rm ; \ rm .autogenerated ; \ fi html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nova.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nova.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." neutron-12.1.1/setup.cfg0000664000175000017500000002117413553660157015151 0ustar zuulzuul00000000000000[metadata] name = neutron summary = OpenStack Networking description-file = README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = https://docs.openstack.org/neutron/latest/ classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 [files] packages = neutron data_files = etc/neutron = etc/api-paste.ini etc/policy.json etc/rootwrap.conf etc/neutron/rootwrap.d = etc/neutron/rootwrap.d/* scripts = bin/neutron-rootwrap-xen-dom0 [entry_points] wsgi_scripts = neutron-api = neutron.server:get_application console_scripts = neutron-db-manage = neutron.db.migration.cli:main neutron-debug = neutron.debug.shell:main neutron-dhcp-agent = neutron.cmd.eventlet.agents.dhcp:main neutron-keepalived-state-change = neutron.cmd.keepalived_state_change:main neutron-ipset-cleanup = neutron.cmd.ipset_cleanup:main neutron-l3-agent = neutron.cmd.eventlet.agents.l3:main neutron-linuxbridge-agent = neutron.cmd.eventlet.plugins.linuxbridge_neutron_agent:main neutron-linuxbridge-cleanup = neutron.cmd.linuxbridge_cleanup:main neutron-macvtap-agent = neutron.cmd.eventlet.plugins.macvtap_neutron_agent:main neutron-metadata-agent = neutron.cmd.eventlet.agents.metadata:main neutron-netns-cleanup = neutron.cmd.netns_cleanup:main neutron-openvswitch-agent = neutron.cmd.eventlet.plugins.ovs_neutron_agent:main neutron-ovs-cleanup = neutron.cmd.ovs_cleanup:main neutron-pd-notify = neutron.cmd.pd_notify:main neutron-server = neutron.cmd.eventlet.server:main neutron-rpc-server = neutron.cmd.eventlet.server:main_rpc_eventlet neutron-rootwrap = oslo_rootwrap.cmd:main neutron-rootwrap-daemon = oslo_rootwrap.cmd:daemon neutron-usage-audit = neutron.cmd.eventlet.usage_audit:main neutron-metering-agent = neutron.cmd.eventlet.services.metering_agent:main neutron-sriov-nic-agent = neutron.cmd.eventlet.plugins.sriov_nic_neutron_agent:main neutron-sanity-check = neutron.cmd.sanity_check:main neutron.core_plugins = ml2 = neutron.plugins.ml2.plugin:Ml2Plugin neutron.service_plugins = dummy = neutron.tests.unit.dummy_plugin:DummyServicePlugin router = neutron.services.l3_router.l3_router_plugin:L3RouterPlugin metering = neutron.services.metering.metering_plugin:MeteringPlugin qos = neutron.services.qos.qos_plugin:QoSPlugin tag = neutron.services.tag.tag_plugin:TagPlugin flavors = neutron.services.flavors.flavors_plugin:FlavorsPlugin auto_allocate = neutron.services.auto_allocate.plugin:Plugin segments = neutron.services.segments.plugin:Plugin network_ip_availability = neutron.services.network_ip_availability.plugin:NetworkIPAvailabilityPlugin revisions = neutron.services.revisions.revision_plugin:RevisionPlugin timestamp = neutron.services.timestamp.timestamp_plugin:TimeStampPlugin trunk = neutron.services.trunk.plugin:TrunkPlugin loki = neutron.services.loki.loki_plugin:LokiPlugin log = neutron.services.logapi.logging_plugin:LoggingPlugin neutron.ml2.type_drivers = flat = neutron.plugins.ml2.drivers.type_flat:FlatTypeDriver local = neutron.plugins.ml2.drivers.type_local:LocalTypeDriver vlan = neutron.plugins.ml2.drivers.type_vlan:VlanTypeDriver geneve = neutron.plugins.ml2.drivers.type_geneve:GeneveTypeDriver gre = neutron.plugins.ml2.drivers.type_gre:GreTypeDriver vxlan = neutron.plugins.ml2.drivers.type_vxlan:VxlanTypeDriver neutron.ml2.mechanism_drivers = logger = neutron.tests.unit.plugins.ml2.drivers.mechanism_logger:LoggerMechanismDriver test = neutron.tests.unit.plugins.ml2.drivers.mechanism_test:TestMechanismDriver linuxbridge = neutron.plugins.ml2.drivers.linuxbridge.mech_driver.mech_linuxbridge:LinuxbridgeMechanismDriver macvtap = neutron.plugins.ml2.drivers.macvtap.mech_driver.mech_macvtap:MacvtapMechanismDriver openvswitch = neutron.plugins.ml2.drivers.openvswitch.mech_driver.mech_openvswitch:OpenvswitchMechanismDriver l2population = neutron.plugins.ml2.drivers.l2pop.mech_driver:L2populationMechanismDriver sriovnicswitch = neutron.plugins.ml2.drivers.mech_sriov.mech_driver.mech_driver:SriovNicSwitchMechanismDriver fake_agent = neutron.tests.unit.plugins.ml2.drivers.mech_fake_agent:FakeAgentMechanismDriver faulty_agent = neutron.tests.unit.plugins.ml2.drivers.mech_faulty_agent:FaultyAgentMechanismDriver neutron.ml2.extension_drivers = test = neutron.tests.unit.plugins.ml2.drivers.ext_test:TestExtensionDriver testdb = neutron.tests.unit.plugins.ml2.drivers.ext_test:TestDBExtensionDriver port_security = neutron.plugins.ml2.extensions.port_security:PortSecurityExtensionDriver qos = neutron.plugins.ml2.extensions.qos:QosExtensionDriver dns = neutron.plugins.ml2.extensions.dns_integration:DNSExtensionDriverML2 data_plane_status = neutron.plugins.ml2.extensions.data_plane_status:DataPlaneStatusExtensionDriver dns_domain_ports = neutron.plugins.ml2.extensions.dns_integration:DNSDomainPortsExtensionDriver neutron.ipam_drivers = fake = neutron.tests.unit.ipam.fake_driver:FakeDriver internal = neutron.ipam.drivers.neutrondb_ipam.driver:NeutronDbPool neutron.agent.l2.extensions = qos = neutron.agent.l2.extensions.qos:QosAgentExtension fdb = neutron.agent.l2.extensions.fdb_population:FdbPopulationAgentExtension log = neutron.services.logapi.agent.log_extension:LoggingExtension neutron.agent.l3.extensions = fip_qos = neutron.agent.l3.extensions.fip_qos:FipQosAgentExtension neutron.services.logapi.drivers = ovs = neutron.services.logapi.drivers.openvswitch.ovs_firewall_log:OVSFirewallLoggingDriver neutron.qos.agent_drivers = ovs = neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers.qos_driver:QosOVSAgentDriver sriov = neutron.plugins.ml2.drivers.mech_sriov.agent.extension_drivers.qos_driver:QosSRIOVAgentDriver linuxbridge = neutron.plugins.ml2.drivers.linuxbridge.agent.extension_drivers.qos_driver:QosLinuxbridgeAgentDriver neutron.agent.linux.pd_drivers = dibbler = neutron.agent.linux.dibbler:PDDibbler neutron.services.external_dns_drivers = designate = neutron.services.externaldns.drivers.designate.driver:Designate oslo.config.opts = neutron = neutron.opts:list_opts neutron.agent = neutron.opts:list_agent_opts neutron.az.agent = neutron.opts:list_az_agent_opts neutron.base.agent = neutron.opts:list_base_agent_opts neutron.db = neutron.opts:list_db_opts neutron.dhcp.agent = neutron.opts:list_dhcp_agent_opts neutron.extensions = neutron.opts:list_extension_opts neutron.l3.agent = neutron.opts:list_l3_agent_opts neutron.metadata.agent = neutron.opts:list_metadata_agent_opts neutron.metering.agent = neutron.opts:list_metering_agent_opts neutron.ml2 = neutron.opts:list_ml2_conf_opts neutron.ml2.linuxbridge.agent = neutron.opts:list_linux_bridge_opts neutron.ml2.macvtap.agent = neutron.opts:list_macvtap_opts neutron.ml2.ovs.agent = neutron.opts:list_ovs_opts neutron.ml2.sriov.agent = neutron.opts:list_sriov_agent_opts neutron.ml2.xenapi = neutron.opts:list_xenapi_opts nova.auth = neutron.opts:list_auth_opts oslo.config.opts.defaults = neutron = neutron.common.config:set_cors_middleware_defaults oslo.policy.enforcer = neutron = neutron.policy:get_enforcer neutron.db.alembic_migrations = neutron = neutron.db.migration:alembic_migrations neutron.interface_drivers = ivs = neutron.agent.linux.interface:IVSInterfaceDriver linuxbridge = neutron.agent.linux.interface:BridgeInterfaceDriver null = neutron.agent.linux.interface:NullDriver openvswitch = neutron.agent.linux.interface:OVSInterfaceDriver neutron.agent.firewall_drivers = noop = neutron.agent.firewall:NoopFirewallDriver iptables = neutron.agent.linux.iptables_firewall:IptablesFirewallDriver iptables_hybrid = neutron.agent.linux.iptables_firewall:OVSHybridIptablesFirewallDriver openvswitch = neutron.agent.linux.openvswitch_firewall:OVSFirewallDriver neutron.services.metering_drivers = noop = neutron.services.metering.drivers.noop.noop_driver:NoopMeteringDriver iptables = neutron.services.metering.drivers.iptables.iptables_driver:IptablesMeteringDriver [build_sphinx] all_files = 1 build-dir = doc/build source-dir = doc/source warning-is-error = 1 [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = neutron/locale/neutron.pot [compile_catalog] directory = neutron/locale domain = neutron neutron-log-error neutron-log-info neutron-log-warning [update_catalog] domain = neutron output_dir = neutron/locale input_file = neutron/locale/neutron.pot [wheel] universal = 1 [egg_info] tag_build = tag_date = 0 neutron-12.1.1/neutron.egg-info/0000775000175000017500000000000013553660156016506 5ustar zuulzuul00000000000000neutron-12.1.1/neutron.egg-info/PKG-INFO0000664000175000017500000000302713553660156017605 0ustar zuulzuul00000000000000Metadata-Version: 1.1 Name: neutron Version: 12.1.1 Summary: OpenStack Networking Home-page: https://docs.openstack.org/neutron/latest/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/neutron.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on Welcome! ======== To learn more about neutron: * Documentation: https://docs.openstack.org * Features: https://specs.openstack.org/openstack/neutron-specs * Defects: https://launchpad.net/neutron Get in touch via `email `_. Use [Neutron] in your subject. To learn how to contribute: CONTRIBUTING.rst Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 neutron-12.1.1/neutron.egg-info/SOURCES.txt0000664000175000017500000032720613553660156020404 0ustar zuulzuul00000000000000.coveragerc .mailmap .pylintrc .stestr.conf .testr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst TESTING.rst babel.cfg bindep.txt requirements.txt setup.cfg setup.py test-requirements.txt tox.ini api-ref/README.rst bin/neutron-rootwrap-xen-dom0 devstack/plugin.sh devstack/settings devstack/lib/dns devstack/lib/flavors devstack/lib/l2_agent devstack/lib/l2_agent_sriovnicswitch devstack/lib/l3_agent devstack/lib/log devstack/lib/macvtap_agent devstack/lib/ml2 devstack/lib/ovs devstack/lib/qos devstack/lib/segments devstack/lib/trunk devstack/lib/ml2_drivers/sriovnicswitch doc/Makefile doc/source/conf.py doc/source/index.rst doc/source/_static/support_matrix.css doc/source/admin/config-address-scopes.rst doc/source/admin/config-auto-allocation.rst doc/source/admin/config-az.rst doc/source/admin/config-bgp-dynamic-routing.rst doc/source/admin/config-dhcp-ha.rst doc/source/admin/config-dns-int-ext-serv.rst doc/source/admin/config-dns-int.rst doc/source/admin/config-dns-res.rst doc/source/admin/config-dvr-ha-snat.rst doc/source/admin/config-ipam.rst doc/source/admin/config-ipv6.rst doc/source/admin/config-lbaas.rst doc/source/admin/config-logging.rst doc/source/admin/config-macvtap.rst doc/source/admin/config-ml2.rst doc/source/admin/config-mtu.rst doc/source/admin/config-ovs-dpdk.rst doc/source/admin/config-ovs-offload.rst doc/source/admin/config-ovsfwdriver.rst doc/source/admin/config-qos.rst doc/source/admin/config-rbac.rst doc/source/admin/config-routed-networks.rst doc/source/admin/config-service-subnets.rst doc/source/admin/config-services-agent.rst doc/source/admin/config-sfc.rst doc/source/admin/config-sriov.rst doc/source/admin/config-subnet-pools.rst doc/source/admin/config-trunking.rst doc/source/admin/config.rst doc/source/admin/deploy-lb-ha-vrrp.rst doc/source/admin/deploy-lb-provider.rst doc/source/admin/deploy-lb-selfservice.rst doc/source/admin/deploy-lb.rst doc/source/admin/deploy-ovs-ha-dvr.rst doc/source/admin/deploy-ovs-ha-vrrp.rst doc/source/admin/deploy-ovs-provider.rst doc/source/admin/deploy-ovs-selfservice.rst doc/source/admin/deploy-ovs.rst doc/source/admin/deploy.rst doc/source/admin/fwaas-v1-scenario.rst doc/source/admin/fwaas-v2-scenario.rst doc/source/admin/fwaas.rst doc/source/admin/index.rst doc/source/admin/intro-basic-networking.rst doc/source/admin/intro-nat.rst doc/source/admin/intro-network-components.rst doc/source/admin/intro-network-namespaces.rst doc/source/admin/intro-os-networking.rst doc/source/admin/intro-overlay-protocols.rst doc/source/admin/intro.rst doc/source/admin/migration-classic-to-l3ha.rst doc/source/admin/migration-database.rst doc/source/admin/migration-nova-network-to-neutron.rst doc/source/admin/migration.rst doc/source/admin/misc-libvirt.rst doc/source/admin/misc.rst doc/source/admin/neutron_linuxbridge.rst doc/source/admin/ops-ip-availability.rst doc/source/admin/ops-resource-purge.rst doc/source/admin/ops-resource-tags.rst doc/source/admin/ops.rst doc/source/admin/archives/adv-config.rst doc/source/admin/archives/adv-features.rst doc/source/admin/archives/adv-operational-features.rst doc/source/admin/archives/arch.rst doc/source/admin/archives/auth.rst doc/source/admin/archives/config-agents.rst doc/source/admin/archives/config-identity.rst doc/source/admin/archives/config-plugins.rst doc/source/admin/archives/index.rst doc/source/admin/archives/introduction.rst doc/source/admin/archives/multi-dhcp-agents.rst doc/source/admin/archives/use.rst doc/source/admin/archives/figures/vmware_nsx_ex1.graffle doc/source/admin/archives/figures/vmware_nsx_ex1.png doc/source/admin/archives/figures/vmware_nsx_ex1.svg doc/source/admin/archives/figures/vmware_nsx_ex2.graffle doc/source/admin/archives/figures/vmware_nsx_ex2.png doc/source/admin/archives/figures/vmware_nsx_ex2.svg doc/source/admin/figures/NetworkTypes.png doc/source/admin/figures/NetworkTypes.svg doc/source/admin/figures/bgp-dynamic-routing-example1.graffle doc/source/admin/figures/bgp-dynamic-routing-example1.png doc/source/admin/figures/bgp-dynamic-routing-example1.svg doc/source/admin/figures/bgp-dynamic-routing-example2.graffle doc/source/admin/figures/bgp-dynamic-routing-example2.png doc/source/admin/figures/bgp-dynamic-routing-example2.svg doc/source/admin/figures/bgp-dynamic-routing-overview.graffle doc/source/admin/figures/bgp-dynamic-routing-overview.png doc/source/admin/figures/bgp-dynamic-routing-overview.svg doc/source/admin/figures/config-macvtap-compute1.png doc/source/admin/figures/config-macvtap-compute2.png doc/source/admin/figures/demo_multiple_dhcp_agents.png doc/source/admin/figures/deploy-lb-ha-vrrp-compconn1.graffle doc/source/admin/figures/deploy-lb-ha-vrrp-compconn1.png doc/source/admin/figures/deploy-lb-ha-vrrp-compconn1.svg doc/source/admin/figures/deploy-lb-ha-vrrp-overview.graffle doc/source/admin/figures/deploy-lb-ha-vrrp-overview.png doc/source/admin/figures/deploy-lb-ha-vrrp-overview.svg doc/source/admin/figures/deploy-lb-provider-compconn1.graffle doc/source/admin/figures/deploy-lb-provider-compconn1.png doc/source/admin/figures/deploy-lb-provider-compconn1.svg doc/source/admin/figures/deploy-lb-provider-compconn2.graffle doc/source/admin/figures/deploy-lb-provider-compconn2.png doc/source/admin/figures/deploy-lb-provider-compconn2.svg doc/source/admin/figures/deploy-lb-provider-flowew1.graffle doc/source/admin/figures/deploy-lb-provider-flowew1.png doc/source/admin/figures/deploy-lb-provider-flowew1.svg doc/source/admin/figures/deploy-lb-provider-flowew2.graffle doc/source/admin/figures/deploy-lb-provider-flowew2.png doc/source/admin/figures/deploy-lb-provider-flowew2.svg doc/source/admin/figures/deploy-lb-provider-flowns1.graffle doc/source/admin/figures/deploy-lb-provider-flowns1.png doc/source/admin/figures/deploy-lb-provider-flowns1.svg doc/source/admin/figures/deploy-lb-provider-overview.graffle doc/source/admin/figures/deploy-lb-provider-overview.png doc/source/admin/figures/deploy-lb-provider-overview.svg doc/source/admin/figures/deploy-lb-selfservice-compconn1.graffle doc/source/admin/figures/deploy-lb-selfservice-compconn1.png doc/source/admin/figures/deploy-lb-selfservice-compconn1.svg doc/source/admin/figures/deploy-lb-selfservice-flowew1.graffle doc/source/admin/figures/deploy-lb-selfservice-flowew1.png doc/source/admin/figures/deploy-lb-selfservice-flowew1.svg doc/source/admin/figures/deploy-lb-selfservice-flowew2.graffle doc/source/admin/figures/deploy-lb-selfservice-flowew2.png doc/source/admin/figures/deploy-lb-selfservice-flowew2.svg doc/source/admin/figures/deploy-lb-selfservice-flowns1.graffle doc/source/admin/figures/deploy-lb-selfservice-flowns1.png doc/source/admin/figures/deploy-lb-selfservice-flowns1.svg doc/source/admin/figures/deploy-lb-selfservice-flowns2.graffle doc/source/admin/figures/deploy-lb-selfservice-flowns2.png doc/source/admin/figures/deploy-lb-selfservice-flowns2.svg doc/source/admin/figures/deploy-lb-selfservice-overview.graffle doc/source/admin/figures/deploy-lb-selfservice-overview.png doc/source/admin/figures/deploy-lb-selfservice-overview.svg doc/source/admin/figures/deploy-ovs-ha-dvr-compconn1.graffle doc/source/admin/figures/deploy-ovs-ha-dvr-compconn1.png doc/source/admin/figures/deploy-ovs-ha-dvr-compconn1.svg doc/source/admin/figures/deploy-ovs-ha-dvr-flowew1.graffle doc/source/admin/figures/deploy-ovs-ha-dvr-flowew1.png doc/source/admin/figures/deploy-ovs-ha-dvr-flowew1.svg doc/source/admin/figures/deploy-ovs-ha-dvr-flowns1.graffle doc/source/admin/figures/deploy-ovs-ha-dvr-flowns1.png doc/source/admin/figures/deploy-ovs-ha-dvr-flowns1.svg doc/source/admin/figures/deploy-ovs-ha-dvr-flowns2.graffle doc/source/admin/figures/deploy-ovs-ha-dvr-flowns2.png doc/source/admin/figures/deploy-ovs-ha-dvr-flowns2.svg doc/source/admin/figures/deploy-ovs-ha-dvr-overview.graffle doc/source/admin/figures/deploy-ovs-ha-dvr-overview.png doc/source/admin/figures/deploy-ovs-ha-dvr-overview.svg doc/source/admin/figures/deploy-ovs-ha-vrrp-compconn1.graffle doc/source/admin/figures/deploy-ovs-ha-vrrp-compconn1.png doc/source/admin/figures/deploy-ovs-ha-vrrp-compconn1.svg doc/source/admin/figures/deploy-ovs-ha-vrrp-overview.graffle doc/source/admin/figures/deploy-ovs-ha-vrrp-overview.png doc/source/admin/figures/deploy-ovs-ha-vrrp-overview.svg doc/source/admin/figures/deploy-ovs-provider-compconn1.graffle doc/source/admin/figures/deploy-ovs-provider-compconn1.png doc/source/admin/figures/deploy-ovs-provider-compconn1.svg doc/source/admin/figures/deploy-ovs-provider-compconn2.graffle doc/source/admin/figures/deploy-ovs-provider-compconn2.png doc/source/admin/figures/deploy-ovs-provider-compconn2.svg doc/source/admin/figures/deploy-ovs-provider-flowew1.graffle doc/source/admin/figures/deploy-ovs-provider-flowew1.png doc/source/admin/figures/deploy-ovs-provider-flowew1.svg doc/source/admin/figures/deploy-ovs-provider-flowew2.graffle doc/source/admin/figures/deploy-ovs-provider-flowew2.png doc/source/admin/figures/deploy-ovs-provider-flowew2.svg doc/source/admin/figures/deploy-ovs-provider-flowns1.graffle doc/source/admin/figures/deploy-ovs-provider-flowns1.png doc/source/admin/figures/deploy-ovs-provider-flowns1.svg doc/source/admin/figures/deploy-ovs-provider-overview.graffle doc/source/admin/figures/deploy-ovs-provider-overview.png doc/source/admin/figures/deploy-ovs-provider-overview.svg doc/source/admin/figures/deploy-ovs-selfservice-compconn1.graffle doc/source/admin/figures/deploy-ovs-selfservice-compconn1.png doc/source/admin/figures/deploy-ovs-selfservice-compconn1.svg doc/source/admin/figures/deploy-ovs-selfservice-flowew1.graffle doc/source/admin/figures/deploy-ovs-selfservice-flowew1.png doc/source/admin/figures/deploy-ovs-selfservice-flowew1.svg doc/source/admin/figures/deploy-ovs-selfservice-flowew2.graffle doc/source/admin/figures/deploy-ovs-selfservice-flowew2.png doc/source/admin/figures/deploy-ovs-selfservice-flowew2.svg doc/source/admin/figures/deploy-ovs-selfservice-flowns1.graffle doc/source/admin/figures/deploy-ovs-selfservice-flowns1.png doc/source/admin/figures/deploy-ovs-selfservice-flowns1.svg doc/source/admin/figures/deploy-ovs-selfservice-flowns2.graffle doc/source/admin/figures/deploy-ovs-selfservice-flowns2.png doc/source/admin/figures/deploy-ovs-selfservice-flowns2.svg doc/source/admin/figures/deploy-ovs-selfservice-overview.graffle doc/source/admin/figures/deploy-ovs-selfservice-overview.png doc/source/admin/figures/deploy-ovs-selfservice-overview.svg doc/source/admin/figures/fwaas.png doc/source/admin/figures/fwaas.svg doc/source/admin/figures/lbaasv2-diagram.png doc/source/admin/figures/lbaasv2-diagram.svg doc/source/admin/figures/port-chain-architecture-diagram.png doc/source/admin/figures/port-chain-diagram.png doc/source/admin/figures/scenario-classic-mt-compute1.svg doc/source/admin/figures/scenario-classic-mt-compute2.svg doc/source/admin/figures/scenario-classic-mt-flowew1.png doc/source/admin/figures/scenario-classic-mt-flowew1.svg doc/source/admin/figures/scenario-classic-mt-flowew2.png doc/source/admin/figures/scenario-classic-mt-flowew2.svg doc/source/admin/figures/scenario-classic-mt-flowns1.png doc/source/admin/figures/scenario-classic-mt-flowns1.svg doc/source/admin/figures/scenario-classic-mt-networks.png doc/source/admin/figures/scenario-classic-mt-networks.svg doc/source/admin/figures/scenario-classic-mt-services.png doc/source/admin/figures/scenario-classic-mt-services.svg doc/source/admin/figures/scenario-classic-mt.png doc/source/admin/figures/scenario-classic-mt.svg doc/source/admin/shared/deploy-config-neutron-common.txt doc/source/admin/shared/deploy-ha-vrrp-initialnetworks.txt doc/source/admin/shared/deploy-ha-vrrp-verifyfailoveroperation.txt doc/source/admin/shared/deploy-ha-vrrp-verifynetworkoperation.txt doc/source/admin/shared/deploy-ha-vrrp.txt doc/source/admin/shared/deploy-provider-initialnetworks.txt doc/source/admin/shared/deploy-provider-networktrafficflow.txt doc/source/admin/shared/deploy-provider-verifynetworkoperation.txt doc/source/admin/shared/deploy-secgrouprules.txt doc/source/admin/shared/deploy-selfservice-initialnetworks.txt doc/source/admin/shared/deploy-selfservice-networktrafficflow.txt doc/source/admin/shared/deploy-selfservice-verifynetworkoperation.txt doc/source/admin/shared/keepalived-vrrp-healthcheck.txt doc/source/cli/index.rst doc/source/cli/neutron-debug.rst doc/source/cli/neutron-sanity-check.rst doc/source/configuration/dhcp-agent.rst doc/source/configuration/index.rst doc/source/configuration/l3-agent.rst doc/source/configuration/linuxbridge-agent.rst doc/source/configuration/macvtap-agent.rst doc/source/configuration/metadata-agent.rst doc/source/configuration/metering-agent.rst doc/source/configuration/ml2-conf.rst doc/source/configuration/neutron.rst doc/source/configuration/openvswitch-agent.rst doc/source/configuration/sriov-agent.rst doc/source/configuration/samples/dhcp-agent.rst doc/source/configuration/samples/l3-agent.rst doc/source/configuration/samples/linuxbridge-agent.rst doc/source/configuration/samples/macvtap-agent.rst doc/source/configuration/samples/metadata-agent.rst doc/source/configuration/samples/metering-agent.rst doc/source/configuration/samples/ml2-conf.rst doc/source/configuration/samples/neutron.rst doc/source/configuration/samples/openvswitch-agent.rst doc/source/configuration/samples/sriov-agent.rst doc/source/contributor/alembic_migrations.rst doc/source/contributor/client_command_extensions.rst doc/source/contributor/contribute.rst doc/source/contributor/development_environment.rst doc/source/contributor/effective_neutron.rst doc/source/contributor/index.rst doc/source/contributor/modules.rst doc/source/contributor/neutron_api.rst doc/source/contributor/dashboards/index.rst doc/source/contributor/internals/address_scopes.rst doc/source/contributor/internals/agent_extensions.rst doc/source/contributor/internals/api_extensions.rst doc/source/contributor/internals/api_layer.rst doc/source/contributor/internals/calling_ml2_plugin.rst doc/source/contributor/internals/db_layer.rst doc/source/contributor/internals/db_models.rst doc/source/contributor/internals/dns_order.rst doc/source/contributor/internals/external_dns_integration.rst doc/source/contributor/internals/i18n.rst doc/source/contributor/internals/index.rst doc/source/contributor/internals/l2_agent_extensions.rst doc/source/contributor/internals/l2_agents.rst doc/source/contributor/internals/l3_agent_extensions.rst doc/source/contributor/internals/layer3.rst doc/source/contributor/internals/linuxbridge_agent.rst doc/source/contributor/internals/live_migration.rst doc/source/contributor/internals/ml2_ext_manager.rst doc/source/contributor/internals/network_ip_availability.rst doc/source/contributor/internals/objects_usage.rst doc/source/contributor/internals/openvswitch_agent.rst doc/source/contributor/internals/openvswitch_firewall.rst doc/source/contributor/internals/ovs_vhostuser.rst doc/source/contributor/internals/plugin-api.rst doc/source/contributor/internals/policy.rst doc/source/contributor/internals/provisioning_blocks.rst doc/source/contributor/internals/quality_of_service.rst doc/source/contributor/internals/quota.rst doc/source/contributor/internals/retries.rst doc/source/contributor/internals/rpc_api.rst doc/source/contributor/internals/rpc_callbacks.rst doc/source/contributor/internals/security_group_api.rst doc/source/contributor/internals/segments.rst doc/source/contributor/internals/service_extensions.rst doc/source/contributor/internals/services_and_agents.rst doc/source/contributor/internals/sriov_nic_agent.rst doc/source/contributor/internals/tag.rst doc/source/contributor/internals/upgrade.rst doc/source/contributor/internals/images/live-mig-ovs-hybrid.png doc/source/contributor/internals/images/live-mig-ovs-hybrid.txt doc/source/contributor/internals/images/live-mig.png doc/source/contributor/internals/images/live-mig.txt doc/source/contributor/internals/images/under-the-hood-scenario-1-ovs-compute.png doc/source/contributor/internals/images/under-the-hood-scenario-1-ovs-netns.png doc/source/contributor/internals/images/under-the-hood-scenario-1-ovs-network.png doc/source/contributor/policies/blueprints.rst doc/source/contributor/policies/bugs.rst doc/source/contributor/policies/code-reviews.rst doc/source/contributor/policies/contributor-onboarding.rst doc/source/contributor/policies/gate-failure-triage.rst doc/source/contributor/policies/gerrit-recheck.rst doc/source/contributor/policies/index.rst doc/source/contributor/policies/neutron-teams.rst doc/source/contributor/policies/release-checklist.rst doc/source/contributor/policies/thirdparty-ci.rst doc/source/contributor/stadium/governance.rst doc/source/contributor/stadium/guidelines.rst doc/source/contributor/stadium/index.rst doc/source/contributor/testing/coverage.rst doc/source/contributor/testing/db_transient_failure_injection.rst doc/source/contributor/testing/fullstack.rst doc/source/contributor/testing/index.rst doc/source/contributor/testing/template_model_sync_test.rst doc/source/contributor/testing/testing.rst doc/source/contributor/testing/images/fullstack_multinode_simulation.png doc/source/ext/support_matrix.py doc/source/feature_classification/feature_classification_introduction.rst doc/source/feature_classification/general_feature_support_matrix.ini doc/source/feature_classification/general_feature_support_matrix.rst doc/source/feature_classification/index.rst doc/source/feature_classification/provider_network_support_matrix.ini doc/source/feature_classification/provider_network_support_matrix.rst doc/source/install/compute-install-obs.rst doc/source/install/compute-install-option1-obs.rst doc/source/install/compute-install-option1-rdo.rst doc/source/install/compute-install-option1-ubuntu.rst doc/source/install/compute-install-option2-obs.rst doc/source/install/compute-install-option2-rdo.rst doc/source/install/compute-install-option2-ubuntu.rst doc/source/install/compute-install-rdo.rst doc/source/install/compute-install-ubuntu.rst doc/source/install/concepts.rst doc/source/install/controller-install-obs.rst doc/source/install/controller-install-option1-obs.rst doc/source/install/controller-install-option1-rdo.rst doc/source/install/controller-install-option1-ubuntu.rst doc/source/install/controller-install-option2-obs.rst doc/source/install/controller-install-option2-rdo.rst doc/source/install/controller-install-option2-ubuntu.rst doc/source/install/controller-install-rdo.rst doc/source/install/controller-install-ubuntu.rst doc/source/install/environment-networking-compute-obs.rst doc/source/install/environment-networking-compute-rdo.rst doc/source/install/environment-networking-compute-ubuntu.rst doc/source/install/environment-networking-controller-obs.rst doc/source/install/environment-networking-controller-rdo.rst doc/source/install/environment-networking-controller-ubuntu.rst doc/source/install/environment-networking-obs.rst doc/source/install/environment-networking-rdo.rst doc/source/install/environment-networking-storage-cinder.rst doc/source/install/environment-networking-ubuntu.rst doc/source/install/environment-networking-verify-obs.rst doc/source/install/environment-networking-verify-rdo.rst doc/source/install/environment-networking-verify-ubuntu.rst doc/source/install/index.rst doc/source/install/install-obs.rst doc/source/install/install-rdo.rst doc/source/install/install-ubuntu.rst doc/source/install/overview.rst doc/source/install/verify-option1.rst doc/source/install/verify-option2.rst doc/source/install/verify.rst doc/source/install/common/get-started-networking.rst doc/source/install/figures/hwreqs.graffle doc/source/install/figures/hwreqs.png doc/source/install/figures/hwreqs.svg doc/source/install/figures/network1-services.graffle doc/source/install/figures/network1-services.png doc/source/install/figures/network1-services.svg doc/source/install/figures/network2-services.graffle doc/source/install/figures/network2-services.png doc/source/install/figures/network2-services.svg doc/source/install/figures/networklayout.graffle doc/source/install/figures/networklayout.png doc/source/install/figures/networklayout.svg doc/source/install/shared/edit_hosts_file.txt doc/source/install/shared/note_configuration_vary_by_distribution.rst etc/README.txt etc/api-paste.ini etc/policy.json etc/rootwrap.conf etc/neutron/plugins/ml2/.placeholder etc/neutron/rootwrap.d/debug.filters etc/neutron/rootwrap.d/dhcp.filters etc/neutron/rootwrap.d/dibbler.filters etc/neutron/rootwrap.d/ebtables.filters etc/neutron/rootwrap.d/ipset-firewall.filters etc/neutron/rootwrap.d/iptables-firewall.filters etc/neutron/rootwrap.d/l3.filters etc/neutron/rootwrap.d/linuxbridge-plugin.filters etc/neutron/rootwrap.d/netns-cleanup.filters etc/neutron/rootwrap.d/openvswitch-plugin.filters etc/neutron/rootwrap.d/privsep.filters etc/oslo-config-generator/dhcp_agent.ini etc/oslo-config-generator/l3_agent.ini etc/oslo-config-generator/linuxbridge_agent.ini etc/oslo-config-generator/macvtap_agent.ini etc/oslo-config-generator/metadata_agent.ini etc/oslo-config-generator/metering_agent.ini etc/oslo-config-generator/ml2_conf.ini etc/oslo-config-generator/neutron.conf etc/oslo-config-generator/openvswitch_agent.ini etc/oslo-config-generator/sriov_agent.ini neutron/__init__.py neutron/_i18n.py neutron/auth.py neutron/manager.py neutron/neutron_plugin_base_v2.py neutron/opts.py neutron/policy.py neutron/service.py neutron/version.py neutron/worker.py neutron/wsgi.py neutron.egg-info/PKG-INFO neutron.egg-info/SOURCES.txt neutron.egg-info/dependency_links.txt neutron.egg-info/entry_points.txt neutron.egg-info/not-zip-safe neutron.egg-info/pbr.json neutron.egg-info/requires.txt neutron.egg-info/top_level.txt neutron/agent/__init__.py neutron/agent/agent_extension.py neutron/agent/agent_extensions_manager.py neutron/agent/dhcp_agent.py neutron/agent/firewall.py neutron/agent/l3_agent.py neutron/agent/metadata_agent.py neutron/agent/resource_cache.py neutron/agent/rpc.py neutron/agent/securitygroups_rpc.py neutron/agent/common/__init__.py neutron/agent/common/base_polling.py neutron/agent/common/ip_lib.py neutron/agent/common/ovs_lib.py neutron/agent/common/polling.py neutron/agent/common/resource_processing_queue.py neutron/agent/common/utils.py neutron/agent/dhcp/__init__.py neutron/agent/dhcp/agent.py neutron/agent/l2/__init__.py neutron/agent/l2/agent_extension.py neutron/agent/l2/l2_agent_extension.py neutron/agent/l2/l2_agent_extensions_manager.py neutron/agent/l2/extensions/__init__.py neutron/agent/l2/extensions/fdb_population.py neutron/agent/l2/extensions/qos.py neutron/agent/l2/extensions/qos_linux.py neutron/agent/l3/__init__.py neutron/agent/l3/agent.py neutron/agent/l3/dvr.py neutron/agent/l3/dvr_edge_ha_router.py neutron/agent/l3/dvr_edge_router.py neutron/agent/l3/dvr_fip_ns.py neutron/agent/l3/dvr_local_router.py neutron/agent/l3/dvr_router_base.py neutron/agent/l3/dvr_snat_ns.py neutron/agent/l3/fip_rule_priority_allocator.py neutron/agent/l3/ha.py neutron/agent/l3/ha_router.py neutron/agent/l3/item_allocator.py neutron/agent/l3/keepalived_state_change.py neutron/agent/l3/l3_agent_extension.py neutron/agent/l3/l3_agent_extension_api.py neutron/agent/l3/l3_agent_extensions_manager.py neutron/agent/l3/legacy_router.py neutron/agent/l3/link_local_allocator.py neutron/agent/l3/namespace_manager.py neutron/agent/l3/namespaces.py neutron/agent/l3/router_info.py neutron/agent/l3/extensions/__init__.py neutron/agent/l3/extensions/fip_qos.py neutron/agent/linux/__init__.py neutron/agent/linux/async_process.py neutron/agent/linux/bridge_lib.py neutron/agent/linux/daemon.py neutron/agent/linux/dhcp.py neutron/agent/linux/dibbler.py neutron/agent/linux/external_process.py neutron/agent/linux/interface.py neutron/agent/linux/ip_conntrack.py neutron/agent/linux/ip_lib.py neutron/agent/linux/ip_link_support.py neutron/agent/linux/ip_monitor.py neutron/agent/linux/ipset_manager.py neutron/agent/linux/iptables_comments.py neutron/agent/linux/iptables_firewall.py neutron/agent/linux/iptables_manager.py neutron/agent/linux/keepalived.py neutron/agent/linux/l3_tc_lib.py neutron/agent/linux/ovsdb_monitor.py neutron/agent/linux/pd.py neutron/agent/linux/pd_driver.py neutron/agent/linux/polling.py neutron/agent/linux/ra.py neutron/agent/linux/tc_lib.py neutron/agent/linux/utils.py neutron/agent/linux/xenapi_root_helper.py neutron/agent/linux/openvswitch_firewall/__init__.py neutron/agent/linux/openvswitch_firewall/constants.py neutron/agent/linux/openvswitch_firewall/exceptions.py neutron/agent/linux/openvswitch_firewall/firewall.py neutron/agent/linux/openvswitch_firewall/iptables.py neutron/agent/linux/openvswitch_firewall/rules.py neutron/agent/metadata/__init__.py neutron/agent/metadata/agent.py neutron/agent/metadata/driver.py neutron/agent/ovsdb/__init__.py neutron/agent/ovsdb/api.py neutron/agent/ovsdb/impl_idl.py neutron/agent/ovsdb/impl_vsctl.py neutron/agent/ovsdb/native/__init__.py neutron/agent/ovsdb/native/commands.py neutron/agent/ovsdb/native/connection.py neutron/agent/ovsdb/native/exceptions.py neutron/agent/ovsdb/native/helpers.py neutron/agent/ovsdb/native/vlog.py neutron/agent/windows/__init__.py neutron/agent/windows/ip_lib.py neutron/agent/windows/polling.py neutron/agent/windows/utils.py neutron/api/__init__.py neutron/api/api_common.py neutron/api/extensions.py neutron/api/versions.py neutron/api/rpc/__init__.py neutron/api/rpc/agentnotifiers/__init__.py neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py neutron/api/rpc/agentnotifiers/l3_rpc_agent_api.py neutron/api/rpc/agentnotifiers/metering_rpc_agent_api.py neutron/api/rpc/agentnotifiers/utils.py neutron/api/rpc/callbacks/__init__.py neutron/api/rpc/callbacks/events.py neutron/api/rpc/callbacks/exceptions.py neutron/api/rpc/callbacks/resource_manager.py neutron/api/rpc/callbacks/resources.py neutron/api/rpc/callbacks/version_manager.py neutron/api/rpc/callbacks/consumer/__init__.py neutron/api/rpc/callbacks/consumer/registry.py neutron/api/rpc/callbacks/producer/__init__.py neutron/api/rpc/callbacks/producer/registry.py neutron/api/rpc/handlers/__init__.py neutron/api/rpc/handlers/dhcp_rpc.py neutron/api/rpc/handlers/dvr_rpc.py neutron/api/rpc/handlers/l3_rpc.py neutron/api/rpc/handlers/metadata_rpc.py neutron/api/rpc/handlers/resources_rpc.py neutron/api/rpc/handlers/securitygroups_rpc.py neutron/api/v2/__init__.py neutron/api/v2/attributes.py neutron/api/v2/base.py neutron/api/v2/resource.py neutron/api/v2/resource_helper.py neutron/api/v2/router.py neutron/api/views/__init__.py neutron/api/views/versions.py neutron/cmd/__init__.py neutron/cmd/ipset_cleanup.py neutron/cmd/keepalived_state_change.py neutron/cmd/linuxbridge_cleanup.py neutron/cmd/netns_cleanup.py neutron/cmd/ovs_cleanup.py neutron/cmd/pd_notify.py neutron/cmd/runtime_checks.py neutron/cmd/sanity_check.py neutron/cmd/eventlet/__init__.py neutron/cmd/eventlet/usage_audit.py neutron/cmd/eventlet/agents/__init__.py neutron/cmd/eventlet/agents/dhcp.py neutron/cmd/eventlet/agents/l3.py neutron/cmd/eventlet/agents/metadata.py neutron/cmd/eventlet/plugins/__init__.py neutron/cmd/eventlet/plugins/linuxbridge_neutron_agent.py neutron/cmd/eventlet/plugins/macvtap_neutron_agent.py neutron/cmd/eventlet/plugins/ovs_neutron_agent.py neutron/cmd/eventlet/plugins/sriov_nic_neutron_agent.py neutron/cmd/eventlet/server/__init__.py neutron/cmd/eventlet/services/__init__.py neutron/cmd/eventlet/services/metering_agent.py neutron/cmd/sanity/__init__.py neutron/cmd/sanity/checks.py neutron/common/__init__.py neutron/common/_deprecate.py neutron/common/cache_utils.py neutron/common/config.py neutron/common/constants.py neutron/common/eventlet_utils.py neutron/common/exceptions.py neutron/common/ipv6_utils.py neutron/common/profiler.py neutron/common/rpc.py neutron/common/test_lib.py neutron/common/topics.py neutron/common/utils.py neutron/conf/__init__.py neutron/conf/common.py neutron/conf/quota.py neutron/conf/service.py neutron/conf/wsgi.py neutron/conf/agent/__init__.py neutron/conf/agent/agent_extensions_manager.py neutron/conf/agent/cmd.py neutron/conf/agent/common.py neutron/conf/agent/dhcp.py neutron/conf/agent/l2_ext_fdb_population.py neutron/conf/agent/linux.py neutron/conf/agent/ovs_conf.py neutron/conf/agent/ovsdb_api.py neutron/conf/agent/securitygroups_rpc.py neutron/conf/agent/windows.py neutron/conf/agent/xenapi_conf.py neutron/conf/agent/database/__init__.py neutron/conf/agent/database/agents_db.py neutron/conf/agent/database/agentschedulers_db.py neutron/conf/agent/l3/__init__.py neutron/conf/agent/l3/config.py neutron/conf/agent/l3/ha.py neutron/conf/agent/l3/keepalived.py neutron/conf/agent/metadata/__init__.py neutron/conf/agent/metadata/config.py neutron/conf/db/__init__.py neutron/conf/db/dvr_mac_db.py neutron/conf/db/extraroute_db.py neutron/conf/db/l3_agentschedulers_db.py neutron/conf/db/l3_dvr_db.py neutron/conf/db/l3_gwmode_db.py neutron/conf/db/l3_hamode_db.py neutron/conf/db/migration_cli.py neutron/conf/extensions/__init__.py neutron/conf/extensions/allowedaddresspairs.py neutron/conf/plugins/__init__.py neutron/conf/plugins/ml2/__init__.py neutron/conf/plugins/ml2/config.py neutron/conf/plugins/ml2/drivers/__init__.py neutron/conf/plugins/ml2/drivers/agent.py neutron/conf/plugins/ml2/drivers/driver_type.py neutron/conf/plugins/ml2/drivers/l2pop.py neutron/conf/plugins/ml2/drivers/linuxbridge.py neutron/conf/plugins/ml2/drivers/macvtap.py neutron/conf/plugins/ml2/drivers/ovs_conf.py neutron/conf/plugins/ml2/drivers/mech_sriov/__init__.py neutron/conf/plugins/ml2/drivers/mech_sriov/agent_common.py neutron/conf/services/__init__.py neutron/conf/services/extdns_designate_driver.py neutron/conf/services/logging.py neutron/conf/services/metering_agent.py neutron/conf/services/provider_configuration.py neutron/core_extensions/__init__.py neutron/core_extensions/base.py neutron/core_extensions/qos.py neutron/db/__init__.py neutron/db/_model_query.py neutron/db/_resource_extend.py neutron/db/_utils.py neutron/db/address_scope_db.py neutron/db/agents_db.py neutron/db/agentschedulers_db.py neutron/db/allowedaddresspairs_db.py neutron/db/api.py neutron/db/common_db_mixin.py neutron/db/data_plane_status_db.py neutron/db/db_base_plugin_common.py neutron/db/db_base_plugin_v2.py neutron/db/dns_db.py neutron/db/dvr_mac_db.py neutron/db/external_net_db.py neutron/db/extradhcpopt_db.py neutron/db/extraroute_db.py neutron/db/flavors_db.py neutron/db/ipam_backend_mixin.py neutron/db/ipam_pluggable_backend.py neutron/db/l3_agentschedulers_db.py neutron/db/l3_attrs_db.py neutron/db/l3_db.py neutron/db/l3_dvr_db.py neutron/db/l3_dvr_ha_scheduler_db.py neutron/db/l3_dvrscheduler_db.py neutron/db/l3_fip_qos.py neutron/db/l3_gwmode_db.py neutron/db/l3_hamode_db.py neutron/db/l3_hascheduler_db.py neutron/db/models_v2.py neutron/db/network_ip_availability_db.py neutron/db/portbindings_base.py neutron/db/portbindings_db.py neutron/db/portsecurity_db.py neutron/db/portsecurity_db_common.py neutron/db/provisioning_blocks.py neutron/db/quota_db.py neutron/db/rbac_db_mixin.py neutron/db/rbac_db_models.py neutron/db/securitygroups_db.py neutron/db/securitygroups_rpc_base.py neutron/db/segments_db.py neutron/db/servicetype_db.py neutron/db/sqlalchemytypes.py neutron/db/standard_attr.py neutron/db/standardattrdescription_db.py neutron/db/subnet_service_type_db_models.py neutron/db/tag_db.py neutron/db/vlantransparent_db.py neutron/db/allowed_address_pairs/__init__.py neutron/db/availability_zone/__init__.py neutron/db/availability_zone/network.py neutron/db/availability_zone/router.py neutron/db/extra_dhcp_opt/__init__.py neutron/db/extra_dhcp_opt/models.py neutron/db/metering/__init__.py neutron/db/metering/metering_db.py neutron/db/metering/metering_rpc.py neutron/db/migration/README neutron/db/migration/__init__.py neutron/db/migration/alembic.ini neutron/db/migration/autogen.py neutron/db/migration/cli.py neutron/db/migration/connection.py neutron/db/migration/alembic_migrations/__init__.py neutron/db/migration/alembic_migrations/agent_init_ops.py neutron/db/migration/alembic_migrations/brocade_init_ops.py neutron/db/migration/alembic_migrations/cisco_init_ops.py neutron/db/migration/alembic_migrations/core_init_ops.py neutron/db/migration/alembic_migrations/dvr_init_opts.py neutron/db/migration/alembic_migrations/env.py neutron/db/migration/alembic_migrations/external.py neutron/db/migration/alembic_migrations/firewall_init_ops.py neutron/db/migration/alembic_migrations/l3_init_ops.py neutron/db/migration/alembic_migrations/lb_init_ops.py neutron/db/migration/alembic_migrations/loadbalancer_init_ops.py neutron/db/migration/alembic_migrations/metering_init_ops.py neutron/db/migration/alembic_migrations/ml2_init_ops.py neutron/db/migration/alembic_migrations/nec_init_ops.py neutron/db/migration/alembic_migrations/nsxv_initial_opts.py neutron/db/migration/alembic_migrations/nuage_init_opts.py neutron/db/migration/alembic_migrations/other_extensions_init_ops.py neutron/db/migration/alembic_migrations/other_plugins_init_ops.py neutron/db/migration/alembic_migrations/ovs_init_ops.py neutron/db/migration/alembic_migrations/portsec_init_ops.py neutron/db/migration/alembic_migrations/script.py.mako neutron/db/migration/alembic_migrations/secgroup_init_ops.py neutron/db/migration/alembic_migrations/vmware_init_ops.py neutron/db/migration/alembic_migrations/vpn_init_ops.py neutron/db/migration/alembic_migrations/versions/CONTRACT_HEAD neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD neutron/db/migration/alembic_migrations/versions/README neutron/db/migration/alembic_migrations/versions/kilo_initial.py neutron/db/migration/alembic_migrations/versions/liberty/contract/11926bcfe72d_add_geneve_ml2_type_driver.py neutron/db/migration/alembic_migrations/versions/liberty/contract/2a16083502f3_metaplugin_removal.py neutron/db/migration/alembic_migrations/versions/liberty/contract/2e5352a0ad4d_add_missing_foreign_keys.py neutron/db/migration/alembic_migrations/versions/liberty/contract/30018084ec99_initial.py neutron/db/migration/alembic_migrations/versions/liberty/contract/4af11ca47297_drop_cisco_monolithic_tables.py neutron/db/migration/alembic_migrations/versions/liberty/contract/4ffceebfada_rbac_network.py neutron/db/migration/alembic_migrations/versions/liberty/contract/5498d17be016_drop_legacy_ovs_and_lb.py neutron/db/migration/alembic_migrations/versions/liberty/expand/1b4c6e320f79_address_scope_support_in_subnetpool.py neutron/db/migration/alembic_migrations/versions/liberty/expand/1c844d1677f7_dns_nameservers_order.py neutron/db/migration/alembic_migrations/versions/liberty/expand/26c371498592_subnetpool_hash.py neutron/db/migration/alembic_migrations/versions/liberty/expand/31337ec0ffee_flavors.py neutron/db/migration/alembic_migrations/versions/liberty/expand/34af2b5c5a59_add_dns_name_to_port.py neutron/db/migration/alembic_migrations/versions/liberty/expand/354db87e3225_nsxv_vdr_metadata.py neutron/db/migration/alembic_migrations/versions/liberty/expand/45f955889773_quota_usage.py neutron/db/migration/alembic_migrations/versions/liberty/expand/48153cb5f051_qos_db_changes.py neutron/db/migration/alembic_migrations/versions/liberty/expand/52c5312f6baf_address_scopes.py neutron/db/migration/alembic_migrations/versions/liberty/expand/599c6a226151_neutrodb_ipam.py neutron/db/migration/alembic_migrations/versions/liberty/expand/8675309a5c4f_rbac_network.py neutron/db/migration/alembic_migrations/versions/liberty/expand/9859ac9c136_quota_reservations.py neutron/db/migration/alembic_migrations/versions/mitaka/contract/1b294093239c_remove_embrane_plugin.py neutron/db/migration/alembic_migrations/versions/mitaka/contract/2b4c2465d44b_dvr_sheduling_refactoring.py neutron/db/migration/alembic_migrations/versions/mitaka/contract/4ffceebfcdc_standard_desc.py neutron/db/migration/alembic_migrations/versions/mitaka/contract/5ffceebfada_rbac_network_external.py neutron/db/migration/alembic_migrations/versions/mitaka/contract/8a6d8bdae39_migrate_neutron_resources_table.py neutron/db/migration/alembic_migrations/versions/mitaka/contract/c6c112992c9_rbac_qos_policy.py neutron/db/migration/alembic_migrations/versions/mitaka/contract/e3278ee65050_drop_nec_plugin_tables.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/0e66c5227a8a_add_desc_to_standard_attr.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/13cfb89f881a_add_is_default_to_subnetpool.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/15be73214821_add_bgp_model_data.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/15e43b934f81_rbac_qos_policy.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/19f26505c74f_auto_allocated_topology.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/1df244e556f5_add_unique_ha_router_agent_port_bindings.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/2f9e956e7532_tag_support.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/31ed664953e6_add_resource_versions_row_to_agent_table.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/32e5974ada25_add_neutron_resources_table.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/3894bccad37f_add_timestamp_to_base_resources.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/59cb5b6cf4d_availability_zone.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/659bf3d90664_add_attributes_to_support_external_dns_integration.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/b4caf27aae4_add_bgp_dragent_model_data.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/c3a73f615e4_add_ip_version_to_address_scope.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/dce3ec7a25c9_router_az.py neutron/db/migration/alembic_migrations/versions/mitaka/expand/ec7fcfbf72ee_network_az.py neutron/db/migration/alembic_migrations/versions/newton/contract/2e0d7a8a1586_add_binding_index_to_routerl3agentbinding.py neutron/db/migration/alembic_migrations/versions/newton/contract/3b935b28e7a0_migrate_to_pluggable_ipam.py neutron/db/migration/alembic_migrations/versions/newton/contract/4bcd4df1f426_rename_ml2_dvr_port_bindings.py neutron/db/migration/alembic_migrations/versions/newton/contract/5c85685d616d_remove_availability_ranges.py neutron/db/migration/alembic_migrations/versions/newton/contract/7bbb25278f53_device_owner_ha_replicate_int.py neutron/db/migration/alembic_migrations/versions/newton/contract/7d9d8eeec6ad_rename_tenant_to_project.py neutron/db/migration/alembic_migrations/versions/newton/contract/89ab9a816d70_rename_ml2_network_segments.py neutron/db/migration/alembic_migrations/versions/newton/contract/8fd3918ef6f4_add_segment_host_mapping.py neutron/db/migration/alembic_migrations/versions/newton/contract/97c25b0d2353_add_name_desc.py neutron/db/migration/alembic_migrations/versions/newton/contract/a84ccf28f06a_migrate_dns_name_from_port.py neutron/db/migration/alembic_migrations/versions/newton/contract/a8b517cff8ab_add_routerport_bindings_for_ha.py neutron/db/migration/alembic_migrations/versions/newton/contract/b12a3ef66e62_add_standardattr_to_qos_policies.py neutron/db/migration/alembic_migrations/versions/newton/contract/b67e765a3524_remove_mtu_column_from_networks.py neutron/db/migration/alembic_migrations/versions/newton/contract/c879c5e1ee90_add_segment_id_to_subnet.py neutron/db/migration/alembic_migrations/versions/newton/expand/030a959ceafa_uniq_routerports0port_id.py neutron/db/migration/alembic_migrations/versions/newton/expand/0f5bef0f87d4_add_qos_minimum_bandwidth_rules.py neutron/db/migration/alembic_migrations/versions/newton/expand/30107ab6a3ee_provisioning_blocks.py neutron/db/migration/alembic_migrations/versions/newton/expand/3d0e74aa7d37_add_flavor_id_to_routers.py neutron/db/migration/alembic_migrations/versions/newton/expand/45f8dd33480b_qos_dscp_db_addition.py neutron/db/migration/alembic_migrations/versions/newton/expand/5abc0278ca73_add_support_for_vlan_trunking.py neutron/db/migration/alembic_migrations/versions/newton/expand/5cd92597d11d_add_ip_allocation_to_port.py neutron/db/migration/alembic_migrations/versions/newton/expand/67daae611b6e_add_standard_attr_to_qos_policies.py neutron/db/migration/alembic_migrations/versions/newton/expand/6b461a21bcfc_uniq_floatingips0floating_network_.py neutron/db/migration/alembic_migrations/versions/newton/expand/a5648cfeeadf_add_subnet_service_types.py neutron/db/migration/alembic_migrations/versions/newton/expand/a963b38d82f4_add_dns_name_to_portdnses.py neutron/db/migration/alembic_migrations/versions/newton/expand/c415aab1c048_add_revisions_column.py neutron/db/migration/alembic_migrations/versions/newton/expand/d3435b514502_add_device_id_index_to_port.py neutron/db/migration/alembic_migrations/versions/ocata/expand/929c968efe70_add_pk_version_table.py neutron/db/migration/alembic_migrations/versions/ocata/expand/a9c43481023c_extend_ml2_port_bindings.py neutron/db/migration/alembic_migrations/versions/pike/expand/2b42d90729da_qos_add_direction_to_bw_limit_rule_table.py neutron/db/migration/alembic_migrations/versions/pike/expand/349b6fd605a6_add_dns_domain_to_portdnses.py neutron/db/migration/alembic_migrations/versions/pike/expand/62c781cb6192_add_qos_policies_default_table.py neutron/db/migration/alembic_migrations/versions/pike/expand/7d32f979895f_add_mtu_for_networks.py neutron/db/migration/alembic_migrations/versions/pike/expand/804a3c76314c_add_data_plane_status_to_port.py neutron/db/migration/alembic_migrations/versions/pike/expand/c8c222d42aa9_logging_api.py neutron/db/migration/alembic_migrations/versions/queens/expand/594422d373ee_fip_qos.py neutron/db/migration/models/__init__.py neutron/db/migration/models/head.py neutron/db/models/README neutron/db/models/__init__.py neutron/db/models/address_scope.py neutron/db/models/agent.py neutron/db/models/allowed_address_pair.py neutron/db/models/data_plane_status.py neutron/db/models/dns.py neutron/db/models/dvr.py neutron/db/models/external_net.py neutron/db/models/flavor.py neutron/db/models/l3.py neutron/db/models/l3_attrs.py neutron/db/models/l3agent.py neutron/db/models/l3ha.py neutron/db/models/loggingapi.py neutron/db/models/metering.py neutron/db/models/portbinding.py neutron/db/models/provisioning_block.py neutron/db/models/securitygroup.py neutron/db/models/segment.py neutron/db/models/servicetype.py neutron/db/models/subnet_service_type.py neutron/db/models/tag.py neutron/db/models/plugins/__init__.py neutron/db/models/plugins/ml2/__init__.py neutron/db/models/plugins/ml2/flatallocation.py neutron/db/models/plugins/ml2/geneveallocation.py neutron/db/models/plugins/ml2/gre_allocation_endpoints.py neutron/db/models/plugins/ml2/vlanallocation.py neutron/db/models/plugins/ml2/vxlanallocation.py neutron/db/network_dhcp_agent_binding/__init__.py neutron/db/network_dhcp_agent_binding/models.py neutron/db/port_security/__init__.py neutron/db/port_security/models.py neutron/db/qos/__init__.py neutron/db/qos/models.py neutron/db/quota/__init__.py neutron/db/quota/api.py neutron/db/quota/driver.py neutron/db/quota/models.py neutron/debug/README neutron/debug/__init__.py neutron/debug/commands.py neutron/debug/debug_agent.py neutron/debug/shell.py neutron/extensions/__init__.py neutron/extensions/address_scope.py neutron/extensions/agent.py neutron/extensions/allowedaddresspairs.py neutron/extensions/auto_allocated_topology.py neutron/extensions/availability_zone.py neutron/extensions/data_plane_status.py neutron/extensions/default_subnetpools.py neutron/extensions/dhcpagentscheduler.py neutron/extensions/dns.py neutron/extensions/dns_domain_ports.py neutron/extensions/dvr.py neutron/extensions/external_net.py neutron/extensions/extra_dhcp_opt.py neutron/extensions/extraroute.py neutron/extensions/flavors.py neutron/extensions/ip_allocation.py neutron/extensions/ip_substring_port_filtering.py neutron/extensions/ip_substring_port_filtering_lib.py neutron/extensions/l2_adjacency.py neutron/extensions/l3.py neutron/extensions/l3_ext_gw_mode.py neutron/extensions/l3_ext_ha_mode.py neutron/extensions/l3_flavors.py neutron/extensions/l3agentscheduler.py neutron/extensions/logging.py neutron/extensions/metering.py neutron/extensions/multiprovidernet.py neutron/extensions/netmtu.py neutron/extensions/netmtu_writable.py neutron/extensions/network_availability_zone.py neutron/extensions/network_ip_availability.py neutron/extensions/pagination.py neutron/extensions/portbindings.py neutron/extensions/portsecurity.py neutron/extensions/project_id.py neutron/extensions/providernet.py neutron/extensions/qos.py neutron/extensions/qos_bw_limit_direction.py neutron/extensions/qos_default.py neutron/extensions/qos_fip.py neutron/extensions/qos_rule_type_details.py neutron/extensions/quotasv2.py neutron/extensions/quotasv2_detail.py neutron/extensions/rbac.py neutron/extensions/revisionifmatch.py neutron/extensions/revisions.py neutron/extensions/router_availability_zone.py neutron/extensions/routerservicetype.py neutron/extensions/securitygroup.py neutron/extensions/segment.py neutron/extensions/servicetype.py neutron/extensions/sorting.py neutron/extensions/standardattrdescription.py neutron/extensions/subnet_service_types.py neutron/extensions/subnetallocation.py neutron/extensions/tag.py neutron/extensions/tag_ext.py neutron/extensions/tagging.py neutron/extensions/timestamp.py neutron/extensions/trunk.py neutron/extensions/trunk_details.py neutron/extensions/vlantransparent.py neutron/hacking/__init__.py neutron/hacking/checks.py neutron/ipam/__init__.py neutron/ipam/driver.py neutron/ipam/exceptions.py neutron/ipam/requests.py neutron/ipam/subnet_alloc.py neutron/ipam/utils.py neutron/ipam/drivers/__init__.py neutron/ipam/drivers/neutrondb_ipam/__init__.py neutron/ipam/drivers/neutrondb_ipam/db_api.py neutron/ipam/drivers/neutrondb_ipam/db_models.py neutron/ipam/drivers/neutrondb_ipam/driver.py neutron/locale/de/LC_MESSAGES/neutron.po neutron/locale/es/LC_MESSAGES/neutron.po neutron/locale/fr/LC_MESSAGES/neutron.po neutron/locale/it/LC_MESSAGES/neutron.po neutron/locale/ja/LC_MESSAGES/neutron.po neutron/locale/ko_KR/LC_MESSAGES/neutron.po neutron/locale/pt_BR/LC_MESSAGES/neutron.po neutron/locale/ru/LC_MESSAGES/neutron.po neutron/locale/tr_TR/LC_MESSAGES/neutron.po neutron/locale/zh_CN/LC_MESSAGES/neutron.po neutron/locale/zh_TW/LC_MESSAGES/neutron.po neutron/notifiers/__init__.py neutron/notifiers/batch_notifier.py neutron/notifiers/nova.py neutron/objects/README.rst neutron/objects/__init__.py neutron/objects/address_scope.py neutron/objects/agent.py neutron/objects/auto_allocate.py neutron/objects/base.py neutron/objects/common_types.py neutron/objects/flavor.py neutron/objects/floatingip.py neutron/objects/ipam.py neutron/objects/l3_hamode.py neutron/objects/l3agent.py neutron/objects/metering.py neutron/objects/network.py neutron/objects/ports.py neutron/objects/provisioning_blocks.py neutron/objects/quota.py neutron/objects/rbac_db.py neutron/objects/router.py neutron/objects/securitygroup.py neutron/objects/servicetype.py neutron/objects/stdattrs.py neutron/objects/subnet.py neutron/objects/subnetpool.py neutron/objects/tag.py neutron/objects/trunk.py neutron/objects/utils.py neutron/objects/db/__init__.py neutron/objects/db/api.py neutron/objects/extensions/__init__.py neutron/objects/extensions/port_security.py neutron/objects/extensions/standardattributes.py neutron/objects/logapi/__init__.py neutron/objects/logapi/event_types.py neutron/objects/logapi/logging_resource.py neutron/objects/plugins/__init__.py neutron/objects/plugins/ml2/__init__.py neutron/objects/plugins/ml2/base.py neutron/objects/plugins/ml2/flatallocation.py neutron/objects/plugins/ml2/geneveallocation.py neutron/objects/plugins/ml2/greallocation.py neutron/objects/plugins/ml2/vlanallocation.py neutron/objects/plugins/ml2/vxlanallocation.py neutron/objects/port/__init__.py neutron/objects/port/extensions/__init__.py neutron/objects/port/extensions/allowedaddresspairs.py neutron/objects/port/extensions/data_plane_status.py neutron/objects/port/extensions/extra_dhcp_opt.py neutron/objects/port/extensions/port_security.py neutron/objects/qos/__init__.py neutron/objects/qos/binding.py neutron/objects/qos/policy.py neutron/objects/qos/qos_policy_validator.py neutron/objects/qos/rule.py neutron/objects/qos/rule_type.py neutron/pecan_wsgi/__init__.py neutron/pecan_wsgi/app.py neutron/pecan_wsgi/constants.py neutron/pecan_wsgi/startup.py neutron/pecan_wsgi/controllers/__init__.py neutron/pecan_wsgi/controllers/extensions.py neutron/pecan_wsgi/controllers/quota.py neutron/pecan_wsgi/controllers/resource.py neutron/pecan_wsgi/controllers/root.py neutron/pecan_wsgi/controllers/utils.py neutron/pecan_wsgi/hooks/__init__.py neutron/pecan_wsgi/hooks/body_validation.py neutron/pecan_wsgi/hooks/context.py neutron/pecan_wsgi/hooks/notifier.py neutron/pecan_wsgi/hooks/ownership_validation.py neutron/pecan_wsgi/hooks/policy_enforcement.py neutron/pecan_wsgi/hooks/query_parameters.py neutron/pecan_wsgi/hooks/quota_enforcement.py neutron/pecan_wsgi/hooks/translation.py neutron/pecan_wsgi/hooks/userfilters.py neutron/pecan_wsgi/hooks/utils.py neutron/plugins/__init__.py neutron/plugins/common/__init__.py neutron/plugins/common/constants.py neutron/plugins/common/utils.py neutron/plugins/ml2/README neutron/plugins/ml2/__init__.py neutron/plugins/ml2/db.py neutron/plugins/ml2/driver_context.py neutron/plugins/ml2/managers.py neutron/plugins/ml2/models.py neutron/plugins/ml2/ovo_rpc.py neutron/plugins/ml2/plugin.py neutron/plugins/ml2/rpc.py neutron/plugins/ml2/common/__init__.py neutron/plugins/ml2/common/exceptions.py neutron/plugins/ml2/drivers/__init__.py neutron/plugins/ml2/drivers/helpers.py neutron/plugins/ml2/drivers/mech_agent.py neutron/plugins/ml2/drivers/type_flat.py neutron/plugins/ml2/drivers/type_geneve.py neutron/plugins/ml2/drivers/type_gre.py neutron/plugins/ml2/drivers/type_local.py neutron/plugins/ml2/drivers/type_tunnel.py neutron/plugins/ml2/drivers/type_vlan.py neutron/plugins/ml2/drivers/type_vxlan.py neutron/plugins/ml2/drivers/agent/__init__.py neutron/plugins/ml2/drivers/agent/_agent_manager_base.py neutron/plugins/ml2/drivers/agent/_common_agent.py neutron/plugins/ml2/drivers/agent/capabilities.py neutron/plugins/ml2/drivers/agent/config.py neutron/plugins/ml2/drivers/l2pop/README neutron/plugins/ml2/drivers/l2pop/__init__.py neutron/plugins/ml2/drivers/l2pop/db.py neutron/plugins/ml2/drivers/l2pop/mech_driver.py neutron/plugins/ml2/drivers/l2pop/rpc.py neutron/plugins/ml2/drivers/l2pop/rpc_manager/__init__.py neutron/plugins/ml2/drivers/l2pop/rpc_manager/l2population_rpc.py neutron/plugins/ml2/drivers/linuxbridge/__init__.py neutron/plugins/ml2/drivers/linuxbridge/agent/__init__.py neutron/plugins/ml2/drivers/linuxbridge/agent/arp_protect.py neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_agent_extension_api.py neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_capabilities.py neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py neutron/plugins/ml2/drivers/linuxbridge/agent/common/__init__.py neutron/plugins/ml2/drivers/linuxbridge/agent/common/config.py neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py neutron/plugins/ml2/drivers/linuxbridge/agent/common/utils.py neutron/plugins/ml2/drivers/linuxbridge/agent/extension_drivers/__init__.py neutron/plugins/ml2/drivers/linuxbridge/agent/extension_drivers/qos_driver.py neutron/plugins/ml2/drivers/linuxbridge/mech_driver/__init__.py neutron/plugins/ml2/drivers/linuxbridge/mech_driver/mech_linuxbridge.py neutron/plugins/ml2/drivers/macvtap/__init__.py neutron/plugins/ml2/drivers/macvtap/macvtap_common.py neutron/plugins/ml2/drivers/macvtap/agent/__init__.py neutron/plugins/ml2/drivers/macvtap/agent/macvtap_neutron_agent.py neutron/plugins/ml2/drivers/macvtap/mech_driver/__init__.py neutron/plugins/ml2/drivers/macvtap/mech_driver/mech_macvtap.py neutron/plugins/ml2/drivers/mech_sriov/__init__.py neutron/plugins/ml2/drivers/mech_sriov/agent/__init__.py neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py neutron/plugins/ml2/drivers/mech_sriov/agent/common/__init__.py neutron/plugins/ml2/drivers/mech_sriov/agent/common/config.py neutron/plugins/ml2/drivers/mech_sriov/agent/common/exceptions.py neutron/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/__init__.py neutron/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/qos_driver.py neutron/plugins/ml2/drivers/mech_sriov/mech_driver/__init__.py neutron/plugins/ml2/drivers/mech_sriov/mech_driver/exceptions.py neutron/plugins/ml2/drivers/mech_sriov/mech_driver/mech_driver.py neutron/plugins/ml2/drivers/openvswitch/__init__.py neutron/plugins/ml2/drivers/openvswitch/agent/__init__.py neutron/plugins/ml2/drivers/openvswitch/agent/main.py neutron/plugins/ml2/drivers/openvswitch/agent/ovs_agent_extension_api.py neutron/plugins/ml2/drivers/openvswitch/agent/ovs_capabilities.py neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py neutron/plugins/ml2/drivers/openvswitch/agent/vlanmanager.py neutron/plugins/ml2/drivers/openvswitch/agent/common/__init__.py neutron/plugins/ml2/drivers/openvswitch/agent/common/config.py neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/__init__.py neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/qos_driver.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/__init__.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/br_cookie.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/__init__.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_dvr_process.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_tun.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/main.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/ofswitch.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/ovs_bridge.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/ovs_ryuapp.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/__init__.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_dvr_process.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_int.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_phys.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_tun.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/main.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ofswitch.py neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ovs_bridge.py neutron/plugins/ml2/drivers/openvswitch/mech_driver/__init__.py neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py neutron/plugins/ml2/extensions/__init__.py neutron/plugins/ml2/extensions/data_plane_status.py neutron/plugins/ml2/extensions/dns_integration.py neutron/plugins/ml2/extensions/port_security.py neutron/plugins/ml2/extensions/qos.py neutron/privileged/__init__.py neutron/privileged/agent/__init__.py neutron/privileged/agent/linux/__init__.py neutron/privileged/agent/linux/ip_lib.py neutron/privileged/agent/linux/netlink_constants.py neutron/privileged/agent/linux/netlink_lib.py neutron/quota/__init__.py neutron/quota/resource.py neutron/quota/resource_registry.py neutron/scheduler/__init__.py neutron/scheduler/base_resource_filter.py neutron/scheduler/base_scheduler.py neutron/scheduler/dhcp_agent_scheduler.py neutron/scheduler/l3_agent_scheduler.py neutron/server/__init__.py neutron/server/rpc_eventlet.py neutron/server/wsgi_eventlet.py neutron/services/__init__.py neutron/services/provider_configuration.py neutron/services/service_base.py neutron/services/auto_allocate/__init__.py neutron/services/auto_allocate/db.py neutron/services/auto_allocate/exceptions.py neutron/services/auto_allocate/models.py neutron/services/auto_allocate/plugin.py neutron/services/externaldns/__init__.py neutron/services/externaldns/driver.py neutron/services/externaldns/drivers/__init__.py neutron/services/externaldns/drivers/designate/__init__.py neutron/services/externaldns/drivers/designate/driver.py neutron/services/flavors/__init__.py neutron/services/flavors/flavors_plugin.py neutron/services/l3_router/README neutron/services/l3_router/__init__.py neutron/services/l3_router/l3_router_plugin.py neutron/services/l3_router/service_providers/__init__.py neutron/services/l3_router/service_providers/base.py neutron/services/l3_router/service_providers/driver_controller.py neutron/services/l3_router/service_providers/dvr.py neutron/services/l3_router/service_providers/dvrha.py neutron/services/l3_router/service_providers/ha.py neutron/services/l3_router/service_providers/single_node.py neutron/services/logapi/__init__.py neutron/services/logapi/logging_plugin.py neutron/services/logapi/agent/__init__.py neutron/services/logapi/agent/log_extension.py neutron/services/logapi/common/__init__.py neutron/services/logapi/common/constants.py neutron/services/logapi/common/db_api.py neutron/services/logapi/common/exceptions.py neutron/services/logapi/common/validators.py neutron/services/logapi/drivers/__init__.py neutron/services/logapi/drivers/base.py neutron/services/logapi/drivers/manager.py neutron/services/logapi/drivers/openvswitch/__init__.py neutron/services/logapi/drivers/openvswitch/driver.py neutron/services/logapi/drivers/openvswitch/log_ryuapp.py neutron/services/logapi/drivers/openvswitch/ovs_firewall_log.py neutron/services/logapi/rpc/__init__.py neutron/services/logapi/rpc/agent.py neutron/services/logapi/rpc/server.py neutron/services/loki/__init__.py neutron/services/loki/loki_plugin.py neutron/services/metering/__init__.py neutron/services/metering/metering_plugin.py neutron/services/metering/agents/__init__.py neutron/services/metering/agents/metering_agent.py neutron/services/metering/drivers/__init__.py neutron/services/metering/drivers/abstract_driver.py neutron/services/metering/drivers/utils.py neutron/services/metering/drivers/iptables/__init__.py neutron/services/metering/drivers/iptables/iptables_driver.py neutron/services/metering/drivers/noop/__init__.py neutron/services/metering/drivers/noop/noop_driver.py neutron/services/network_ip_availability/__init__.py neutron/services/network_ip_availability/plugin.py neutron/services/qos/__init__.py neutron/services/qos/qos_plugin.py neutron/services/qos/drivers/__init__.py neutron/services/qos/drivers/manager.py neutron/services/qos/drivers/linuxbridge/__init__.py neutron/services/qos/drivers/linuxbridge/driver.py neutron/services/qos/drivers/openvswitch/__init__.py neutron/services/qos/drivers/openvswitch/driver.py neutron/services/qos/drivers/sriov/__init__.py neutron/services/qos/drivers/sriov/driver.py neutron/services/rbac/__init__.py neutron/services/revisions/__init__.py neutron/services/revisions/revision_plugin.py neutron/services/segments/__init__.py neutron/services/segments/db.py neutron/services/segments/exceptions.py neutron/services/segments/placement_client.py neutron/services/segments/plugin.py neutron/services/tag/__init__.py neutron/services/tag/tag_plugin.py neutron/services/timestamp/__init__.py neutron/services/timestamp/timestamp_db.py neutron/services/timestamp/timestamp_plugin.py neutron/services/trunk/__init__.py neutron/services/trunk/callbacks.py neutron/services/trunk/constants.py neutron/services/trunk/exceptions.py neutron/services/trunk/models.py neutron/services/trunk/plugin.py neutron/services/trunk/rules.py neutron/services/trunk/utils.py neutron/services/trunk/drivers/__init__.py neutron/services/trunk/drivers/base.py neutron/services/trunk/drivers/linuxbridge/__init__.py neutron/services/trunk/drivers/linuxbridge/driver.py neutron/services/trunk/drivers/linuxbridge/agent/__init__.py neutron/services/trunk/drivers/linuxbridge/agent/driver.py neutron/services/trunk/drivers/linuxbridge/agent/trunk_plumber.py neutron/services/trunk/drivers/openvswitch/__init__.py neutron/services/trunk/drivers/openvswitch/constants.py neutron/services/trunk/drivers/openvswitch/driver.py neutron/services/trunk/drivers/openvswitch/utils.py neutron/services/trunk/drivers/openvswitch/agent/__init__.py neutron/services/trunk/drivers/openvswitch/agent/driver.py neutron/services/trunk/drivers/openvswitch/agent/exceptions.py neutron/services/trunk/drivers/openvswitch/agent/ovsdb_handler.py neutron/services/trunk/drivers/openvswitch/agent/trunk_manager.py neutron/services/trunk/rpc/__init__.py neutron/services/trunk/rpc/agent.py neutron/services/trunk/rpc/backend.py neutron/services/trunk/rpc/constants.py neutron/services/trunk/rpc/server.py neutron/services/trunk/seg_types/__init__.py neutron/services/trunk/seg_types/validators.py neutron/tests/__init__.py neutron/tests/base.py neutron/tests/fake_notifier.py neutron/tests/post_mortem_debug.py neutron/tests/tools.py neutron/tests/common/__init__.py neutron/tests/common/base.py neutron/tests/common/config_fixtures.py neutron/tests/common/conn_testers.py neutron/tests/common/helpers.py neutron/tests/common/l3_test_common.py neutron/tests/common/machine_fixtures.py neutron/tests/common/net_helpers.py neutron/tests/common/agents/__init__.py neutron/tests/common/agents/l2_extensions.py neutron/tests/common/agents/l3_agent.py neutron/tests/common/agents/ovs_agent.py neutron/tests/common/exclusive_resources/__init__.py neutron/tests/common/exclusive_resources/ip_address.py neutron/tests/common/exclusive_resources/ip_network.py neutron/tests/common/exclusive_resources/port.py neutron/tests/common/exclusive_resources/resource_allocator.py neutron/tests/contrib/README neutron/tests/contrib/gate_hook.sh neutron/tests/contrib/post_test_hook.sh neutron/tests/contrib/testing.filters neutron/tests/contrib/hooks/api_all_extensions neutron/tests/contrib/hooks/availability_zone neutron/tests/contrib/hooks/disable_dvr neutron/tests/contrib/hooks/disable_dvr_tests neutron/tests/contrib/hooks/dns neutron/tests/contrib/hooks/dstat neutron/tests/contrib/hooks/iptables_verify neutron/tests/contrib/hooks/linuxbridge_type_drivers neutron/tests/contrib/hooks/log neutron/tests/contrib/hooks/openvswitch_type_drivers neutron/tests/contrib/hooks/osprofiler neutron/tests/contrib/hooks/ovsfw neutron/tests/contrib/hooks/qos neutron/tests/contrib/hooks/quotas neutron/tests/contrib/hooks/segments neutron/tests/contrib/hooks/stack_base neutron/tests/contrib/hooks/trunk neutron/tests/contrib/hooks/tunnel_types neutron/tests/contrib/hooks/ubuntu_image neutron/tests/contrib/hooks/vlan_provider neutron/tests/etc/api-paste.ini neutron/tests/etc/api-paste.ini.test neutron/tests/etc/neutron.conf neutron/tests/etc/neutron_test.conf neutron/tests/etc/neutron_test2.conf.example neutron/tests/etc/policy.json neutron/tests/fullstack/README neutron/tests/fullstack/__init__.py neutron/tests/fullstack/base.py neutron/tests/fullstack/test_connectivity.py neutron/tests/fullstack/test_dhcp_agent.py neutron/tests/fullstack/test_l3_agent.py neutron/tests/fullstack/test_mtu.py neutron/tests/fullstack/test_port_shut_down.py neutron/tests/fullstack/test_ports_rebind.py neutron/tests/fullstack/test_qos.py neutron/tests/fullstack/test_securitygroup.py neutron/tests/fullstack/test_trunk.py neutron/tests/fullstack/utils.py neutron/tests/fullstack/cmd/__init__.py neutron/tests/fullstack/cmd/dhcp_agent.py neutron/tests/fullstack/cmd/l3_agent.py neutron/tests/fullstack/cmd/ovs_agent.py neutron/tests/fullstack/resources/__init__.py neutron/tests/fullstack/resources/client.py neutron/tests/fullstack/resources/config.py neutron/tests/fullstack/resources/environment.py neutron/tests/fullstack/resources/machine.py neutron/tests/fullstack/resources/process.py neutron/tests/functional/__init__.py neutron/tests/functional/base.py neutron/tests/functional/constants.py neutron/tests/functional/requirements.txt neutron/tests/functional/test_server.py neutron/tests/functional/test_service.py neutron/tests/functional/agent/__init__.py neutron/tests/functional/agent/test_dhcp_agent.py neutron/tests/functional/agent/test_firewall.py neutron/tests/functional/agent/test_l2_lb_agent.py neutron/tests/functional/agent/test_l2_ovs_agent.py neutron/tests/functional/agent/test_ovs_flows.py neutron/tests/functional/agent/test_ovs_lib.py neutron/tests/functional/agent/l2/__init__.py neutron/tests/functional/agent/l2/base.py neutron/tests/functional/agent/l2/extensions/__init__.py neutron/tests/functional/agent/l2/extensions/test_ovs_agent_qos_extension.py neutron/tests/functional/agent/l3/__init__.py neutron/tests/functional/agent/l3/framework.py neutron/tests/functional/agent/l3/test_dvr_router.py neutron/tests/functional/agent/l3/test_ha_router.py neutron/tests/functional/agent/l3/test_keepalived_state_change.py neutron/tests/functional/agent/l3/test_legacy_router.py neutron/tests/functional/agent/l3/test_metadata_proxy.py neutron/tests/functional/agent/l3/test_namespace_manager.py neutron/tests/functional/agent/l3/extensions/__init__.py neutron/tests/functional/agent/l3/extensions/test_fip_qos_extension.py neutron/tests/functional/agent/linux/__init__.py neutron/tests/functional/agent/linux/base.py neutron/tests/functional/agent/linux/helpers.py neutron/tests/functional/agent/linux/simple_daemon.py neutron/tests/functional/agent/linux/test_async_process.py neutron/tests/functional/agent/linux/test_bridge_lib.py neutron/tests/functional/agent/linux/test_dhcp.py neutron/tests/functional/agent/linux/test_interface.py neutron/tests/functional/agent/linux/test_ip_lib.py neutron/tests/functional/agent/linux/test_ip_monitor.py neutron/tests/functional/agent/linux/test_ipset.py neutron/tests/functional/agent/linux/test_iptables.py neutron/tests/functional/agent/linux/test_keepalived.py neutron/tests/functional/agent/linux/test_l3_tc_lib.py neutron/tests/functional/agent/linux/test_linuxbridge_arp_protect.py neutron/tests/functional/agent/linux/test_netlink_lib.py neutron/tests/functional/agent/linux/test_ovsdb_monitor.py neutron/tests/functional/agent/linux/test_process_monitor.py neutron/tests/functional/agent/linux/test_tc_lib.py neutron/tests/functional/agent/linux/test_utils.py neutron/tests/functional/agent/linux/bin/__init__.py neutron/tests/functional/agent/linux/bin/ipt_binname.py neutron/tests/functional/agent/linux/openvswitch_firewall/__init__.py neutron/tests/functional/agent/linux/openvswitch_firewall/test_firewall.py neutron/tests/functional/agent/linux/openvswitch_firewall/test_iptables.py neutron/tests/functional/agent/windows/__init__.py neutron/tests/functional/agent/windows/test_ip_lib.py neutron/tests/functional/api/__init__.py neutron/tests/functional/api/test_policies.py neutron/tests/functional/cmd/__init__.py neutron/tests/functional/cmd/process_spawn.py neutron/tests/functional/cmd/test_ipset_cleanup.py neutron/tests/functional/cmd/test_linuxbridge_cleanup.py neutron/tests/functional/cmd/test_netns_cleanup.py neutron/tests/functional/cmd/test_ovs_cleanup.py neutron/tests/functional/common/__init__.py neutron/tests/functional/common/test_utils.py neutron/tests/functional/db/__init__.py neutron/tests/functional/db/test_ipam.py neutron/tests/functional/db/test_migrations.py neutron/tests/functional/db/test_models.py neutron/tests/functional/db/migrations/__init__.py neutron/tests/functional/db/migrations/test_2e0d7a8a1586_add_binding_index_to_routerl3agentbinding.py neutron/tests/functional/db/migrations/test_3b935b28e7a0_migrate_to_pluggable_ipam.py neutron/tests/functional/db/migrations/test_97c25b0d2353_add_name_desc.py neutron/tests/functional/db/migrations/test_a8b517cff8ab_add_routerport_bindings_for_ha.py neutron/tests/functional/db/migrations/test_b12a3ef66e62_add_standardattr_to_qos_policies.py neutron/tests/functional/pecan_wsgi/__init__.py neutron/tests/functional/pecan_wsgi/config.py neutron/tests/functional/pecan_wsgi/test_controllers.py neutron/tests/functional/pecan_wsgi/test_functional.py neutron/tests/functional/pecan_wsgi/test_hooks.py neutron/tests/functional/pecan_wsgi/utils.py neutron/tests/functional/plugins/__init__.py neutron/tests/functional/plugins/ml2/__init__.py neutron/tests/functional/plugins/ml2/test_plugin.py neutron/tests/functional/plugins/ml2/drivers/__init__.py neutron/tests/functional/plugins/ml2/drivers/macvtap/__init__.py neutron/tests/functional/plugins/ml2/drivers/macvtap/agent/__init__.py neutron/tests/functional/plugins/ml2/drivers/macvtap/agent/test_macvtap_neutron_agent.py neutron/tests/functional/sanity/__init__.py neutron/tests/functional/sanity/test_sanity.py neutron/tests/functional/scheduler/__init__.py neutron/tests/functional/scheduler/test_dhcp_agent_scheduler.py neutron/tests/functional/scheduler/test_l3_agent_scheduler.py neutron/tests/functional/services/__init__.py neutron/tests/functional/services/l3_router/__init__.py neutron/tests/functional/services/l3_router/test_l3_dvr_ha_router_plugin.py neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py neutron/tests/functional/services/logapi/__init__.py neutron/tests/functional/services/logapi/test_logging.py neutron/tests/functional/services/trunk/__init__.py neutron/tests/functional/services/trunk/test_plugin.py neutron/tests/functional/services/trunk/drivers/__init__.py neutron/tests/functional/services/trunk/drivers/openvswitch/__init__.py neutron/tests/functional/services/trunk/drivers/openvswitch/agent/__init__.py neutron/tests/functional/services/trunk/drivers/openvswitch/agent/test_ovsdb_handler.py neutron/tests/functional/services/trunk/drivers/openvswitch/agent/test_trunk_manager.py neutron/tests/functional/services/trunk/rpc/__init__.py neutron/tests/functional/services/trunk/rpc/test_server.py neutron/tests/functional/tests/__init__.py neutron/tests/functional/tests/common/__init__.py neutron/tests/functional/tests/common/exclusive_resources/__init__.py neutron/tests/functional/tests/common/exclusive_resources/test_ip_address.py neutron/tests/functional/tests/common/exclusive_resources/test_ip_network.py neutron/tests/functional/tests/common/exclusive_resources/test_port.py neutron/tests/functional/tests/common/exclusive_resources/test_resource_allocator.py neutron/tests/tempest/__init__.py neutron/tests/tempest/config.py neutron/tests/tempest/exceptions.py neutron/tests/tempest/api/__init__.py neutron/tests/tempest/api/base.py neutron/tests/tempest/api/clients.py neutron/tests/tempest/common/__init__.py neutron/tests/tempest/common/ssh.py neutron/tests/tempest/common/tempest_fixtures.py neutron/tests/tempest/scenario/__init__.py neutron/tests/tempest/scenario/base.py neutron/tests/tempest/scenario/constants.py neutron/tests/tempest/scenario/exceptions.py neutron/tests/tempest/services/__init__.py neutron/tests/tempest/services/network/__init__.py neutron/tests/tempest/services/network/json/__init__.py neutron/tests/tempest/services/network/json/network_client.py neutron/tests/unit/__init__.py neutron/tests/unit/_test_extension_portbindings.py neutron/tests/unit/dummy_plugin.py neutron/tests/unit/extension_stubs.py neutron/tests/unit/test_auth.py neutron/tests/unit/test_manager.py neutron/tests/unit/test_neutron_plugin_base_v2.py neutron/tests/unit/test_opts.py neutron/tests/unit/test_policy.py neutron/tests/unit/test_service.py neutron/tests/unit/test_worker.py neutron/tests/unit/test_wsgi.py neutron/tests/unit/testlib_api.py neutron/tests/unit/agent/__init__.py neutron/tests/unit/agent/test_agent_extensions_manager.py neutron/tests/unit/agent/test_resource_cache.py neutron/tests/unit/agent/test_rpc.py neutron/tests/unit/agent/test_securitygroups_rpc.py neutron/tests/unit/agent/common/__init__.py neutron/tests/unit/agent/common/test_ovs_lib.py neutron/tests/unit/agent/common/test_polling.py neutron/tests/unit/agent/common/test_resource_processing_queue.py neutron/tests/unit/agent/common/test_utils.py neutron/tests/unit/agent/dhcp/__init__.py neutron/tests/unit/agent/dhcp/test_agent.py neutron/tests/unit/agent/l2/__init__.py neutron/tests/unit/agent/l2/test_l2_agent_extensions_manager.py neutron/tests/unit/agent/l2/extensions/__init__.py neutron/tests/unit/agent/l2/extensions/test_fdb_population.py neutron/tests/unit/agent/l2/extensions/test_qos.py neutron/tests/unit/agent/l3/__init__.py neutron/tests/unit/agent/l3/test_agent.py neutron/tests/unit/agent/l3/test_dvr_fip_ns.py neutron/tests/unit/agent/l3/test_dvr_local_router.py neutron/tests/unit/agent/l3/test_dvr_snat_ns.py neutron/tests/unit/agent/l3/test_fip_rule_priority_allocator.py neutron/tests/unit/agent/l3/test_ha_router.py neutron/tests/unit/agent/l3/test_item_allocator.py neutron/tests/unit/agent/l3/test_l3_agent_extension_api.py neutron/tests/unit/agent/l3/test_legacy_router.py neutron/tests/unit/agent/l3/test_link_local_allocator.py neutron/tests/unit/agent/l3/test_namespace_manager.py neutron/tests/unit/agent/l3/test_router_info.py neutron/tests/unit/agent/l3/extensions/__init__.py neutron/tests/unit/agent/l3/extensions/test_fip_qos.py neutron/tests/unit/agent/linux/__init__.py neutron/tests/unit/agent/linux/failing_process.py neutron/tests/unit/agent/linux/test_async_process.py neutron/tests/unit/agent/linux/test_bridge_lib.py neutron/tests/unit/agent/linux/test_daemon.py neutron/tests/unit/agent/linux/test_dhcp.py neutron/tests/unit/agent/linux/test_external_process.py neutron/tests/unit/agent/linux/test_interface.py neutron/tests/unit/agent/linux/test_ip_conntrack.py neutron/tests/unit/agent/linux/test_ip_lib.py neutron/tests/unit/agent/linux/test_ip_link_support.py neutron/tests/unit/agent/linux/test_ip_monitor.py neutron/tests/unit/agent/linux/test_ipset_manager.py neutron/tests/unit/agent/linux/test_iptables_firewall.py neutron/tests/unit/agent/linux/test_iptables_manager.py neutron/tests/unit/agent/linux/test_keepalived.py neutron/tests/unit/agent/linux/test_l3_tc_lib.py neutron/tests/unit/agent/linux/test_ovsdb_monitor.py neutron/tests/unit/agent/linux/test_pd.py neutron/tests/unit/agent/linux/test_polling.py neutron/tests/unit/agent/linux/test_tc_lib.py neutron/tests/unit/agent/linux/test_utils.py neutron/tests/unit/agent/linux/test_xenapi_root_helper.py neutron/tests/unit/agent/linux/openvswitch_firewall/__init__.py neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py neutron/tests/unit/agent/linux/openvswitch_firewall/test_iptables.py neutron/tests/unit/agent/linux/openvswitch_firewall/test_rules.py neutron/tests/unit/agent/metadata/__init__.py neutron/tests/unit/agent/metadata/test_agent.py neutron/tests/unit/agent/metadata/test_driver.py neutron/tests/unit/agent/ovsdb/__init__.py neutron/tests/unit/agent/ovsdb/test_impl_idl.py neutron/tests/unit/agent/ovsdb/native/__init__.py neutron/tests/unit/agent/ovsdb/native/test_connection.py neutron/tests/unit/agent/ovsdb/native/test_helpers.py neutron/tests/unit/agent/windows/__init__.py neutron/tests/unit/agent/windows/test_ip_lib.py neutron/tests/unit/agent/windows/test_utils.py neutron/tests/unit/api/__init__.py neutron/tests/unit/api/test_api_common.py neutron/tests/unit/api/test_extensions.py neutron/tests/unit/api/test_versions.py neutron/tests/unit/api/rpc/__init__.py neutron/tests/unit/api/rpc/agentnotifiers/__init__.py neutron/tests/unit/api/rpc/agentnotifiers/test_dhcp_rpc_agent_api.py neutron/tests/unit/api/rpc/agentnotifiers/test_l3_rpc_agent_api.py neutron/tests/unit/api/rpc/callbacks/__init__.py neutron/tests/unit/api/rpc/callbacks/test_resource_manager.py neutron/tests/unit/api/rpc/callbacks/test_resources.py neutron/tests/unit/api/rpc/callbacks/test_version_manager.py neutron/tests/unit/api/rpc/callbacks/consumer/__init__.py neutron/tests/unit/api/rpc/callbacks/consumer/test_registry.py neutron/tests/unit/api/rpc/callbacks/producer/__init__.py neutron/tests/unit/api/rpc/callbacks/producer/test_registry.py neutron/tests/unit/api/rpc/handlers/__init__.py neutron/tests/unit/api/rpc/handlers/test_dhcp_rpc.py neutron/tests/unit/api/rpc/handlers/test_dvr_rpc.py neutron/tests/unit/api/rpc/handlers/test_l3_rpc.py neutron/tests/unit/api/rpc/handlers/test_resources_rpc.py neutron/tests/unit/api/rpc/handlers/test_securitygroups_rpc.py neutron/tests/unit/api/v2/__init__.py neutron/tests/unit/api/v2/test_attributes.py neutron/tests/unit/api/v2/test_base.py neutron/tests/unit/api/v2/test_resource.py neutron/tests/unit/api/v2/test_router.py neutron/tests/unit/cmd/__init__.py neutron/tests/unit/cmd/test_netns_cleanup.py neutron/tests/unit/cmd/test_ovs_cleanup.py neutron/tests/unit/cmd/test_sanity_check.py neutron/tests/unit/common/__init__.py neutron/tests/unit/common/moved_globals_code1.py neutron/tests/unit/common/moved_globals_code2.py neutron/tests/unit/common/moved_globals_target.py neutron/tests/unit/common/test__deprecate.py neutron/tests/unit/common/test_cache_utils.py neutron/tests/unit/common/test_ipv6_utils.py neutron/tests/unit/common/test_rpc.py neutron/tests/unit/common/test_utils.py neutron/tests/unit/conf/agent/__init__.py neutron/tests/unit/conf/agent/test_common.py neutron/tests/unit/core_extensions/__init__.py neutron/tests/unit/core_extensions/test_qos.py neutron/tests/unit/db/__init__.py neutron/tests/unit/db/test_agents_db.py neutron/tests/unit/db/test_agentschedulers_db.py neutron/tests/unit/db/test_allowedaddresspairs_db.py neutron/tests/unit/db/test_api.py neutron/tests/unit/db/test_common_db_mixin.py neutron/tests/unit/db/test_db_base_plugin_common.py neutron/tests/unit/db/test_db_base_plugin_v2.py neutron/tests/unit/db/test_dvr_mac_db.py neutron/tests/unit/db/test_extraroute_db.py neutron/tests/unit/db/test_ipam_backend_mixin.py neutron/tests/unit/db/test_ipam_pluggable_backend.py neutron/tests/unit/db/test_l3_db.py neutron/tests/unit/db/test_l3_dvr_db.py neutron/tests/unit/db/test_l3_hamode_db.py neutron/tests/unit/db/test_migration.py neutron/tests/unit/db/test_portsecurity_db.py neutron/tests/unit/db/test_portsecurity_db_common.py neutron/tests/unit/db/test_provisioning_blocks.py neutron/tests/unit/db/test_rbac_db_mixin.py neutron/tests/unit/db/test_securitygroups_db.py neutron/tests/unit/db/test_segments_db.py neutron/tests/unit/db/test_sqlalchemytypes.py neutron/tests/unit/db/test_standard_attr.py neutron/tests/unit/db/metering/__init__.py neutron/tests/unit/db/metering/test_metering_db.py neutron/tests/unit/db/quota/__init__.py neutron/tests/unit/db/quota/test_api.py neutron/tests/unit/db/quota/test_driver.py neutron/tests/unit/debug/__init__.py neutron/tests/unit/debug/test_commands.py neutron/tests/unit/extensions/__init__.py neutron/tests/unit/extensions/base.py neutron/tests/unit/extensions/extendedattribute.py neutron/tests/unit/extensions/extensionattribute.py neutron/tests/unit/extensions/foxinsocks.py neutron/tests/unit/extensions/test_address_scope.py neutron/tests/unit/extensions/test_agent.py neutron/tests/unit/extensions/test_availability_zone.py neutron/tests/unit/extensions/test_data_plane_status.py neutron/tests/unit/extensions/test_default_subnetpools.py neutron/tests/unit/extensions/test_dns.py neutron/tests/unit/extensions/test_external_net.py neutron/tests/unit/extensions/test_extra_dhcp_opt.py neutron/tests/unit/extensions/test_extraroute.py neutron/tests/unit/extensions/test_flavors.py neutron/tests/unit/extensions/test_l3.py neutron/tests/unit/extensions/test_l3_ext_gw_mode.py neutron/tests/unit/extensions/test_network_ip_availability.py neutron/tests/unit/extensions/test_portsecurity.py neutron/tests/unit/extensions/test_providernet.py neutron/tests/unit/extensions/test_qos_fip.py neutron/tests/unit/extensions/test_quotasv2.py neutron/tests/unit/extensions/test_quotasv2_detail.py neutron/tests/unit/extensions/test_router_availability_zone.py neutron/tests/unit/extensions/test_securitygroup.py neutron/tests/unit/extensions/test_segment.py neutron/tests/unit/extensions/test_servicetype.py neutron/tests/unit/extensions/test_subnet_service_types.py neutron/tests/unit/extensions/test_tag.py neutron/tests/unit/extensions/test_timestamp.py neutron/tests/unit/extensions/test_vlantransparent.py neutron/tests/unit/extensions/v2attributes.py neutron/tests/unit/hacking/__init__.py neutron/tests/unit/hacking/test_checks.py neutron/tests/unit/ipam/__init__.py neutron/tests/unit/ipam/fake_driver.py neutron/tests/unit/ipam/test_requests.py neutron/tests/unit/ipam/test_subnet_alloc.py neutron/tests/unit/ipam/test_utils.py neutron/tests/unit/ipam/drivers/__init__.py neutron/tests/unit/ipam/drivers/neutrondb_ipam/__init__.py neutron/tests/unit/ipam/drivers/neutrondb_ipam/test_db_api.py neutron/tests/unit/ipam/drivers/neutrondb_ipam/test_driver.py neutron/tests/unit/notifiers/__init__.py neutron/tests/unit/notifiers/test_batch_notifier.py neutron/tests/unit/notifiers/test_nova.py neutron/tests/unit/objects/__init__.py neutron/tests/unit/objects/test_address_scope.py neutron/tests/unit/objects/test_agent.py neutron/tests/unit/objects/test_auto_allocate.py neutron/tests/unit/objects/test_base.py neutron/tests/unit/objects/test_common_types.py neutron/tests/unit/objects/test_flavor.py neutron/tests/unit/objects/test_floatingip.py neutron/tests/unit/objects/test_ipam.py neutron/tests/unit/objects/test_l3_hamode.py neutron/tests/unit/objects/test_l3agent.py neutron/tests/unit/objects/test_metering.py neutron/tests/unit/objects/test_network.py neutron/tests/unit/objects/test_objects.py neutron/tests/unit/objects/test_ports.py neutron/tests/unit/objects/test_provisioning_blocks.py neutron/tests/unit/objects/test_quota.py neutron/tests/unit/objects/test_rbac_db.py neutron/tests/unit/objects/test_router.py neutron/tests/unit/objects/test_securitygroup.py neutron/tests/unit/objects/test_servicetype.py neutron/tests/unit/objects/test_subnet.py neutron/tests/unit/objects/test_subnetpool.py neutron/tests/unit/objects/test_tag.py neutron/tests/unit/objects/test_trunk.py neutron/tests/unit/objects/test_utils.py neutron/tests/unit/objects/db/__init__.py neutron/tests/unit/objects/db/test_api.py neutron/tests/unit/objects/extensions/__init__.py neutron/tests/unit/objects/extensions/test_standardattributes.py neutron/tests/unit/objects/logapi/__init__.py neutron/tests/unit/objects/logapi/test_logging_resource.py neutron/tests/unit/objects/plugins/__init__.py neutron/tests/unit/objects/plugins/ml2/__init__.py neutron/tests/unit/objects/plugins/ml2/test_flatallocation.py neutron/tests/unit/objects/plugins/ml2/test_geneveallocation.py neutron/tests/unit/objects/plugins/ml2/test_greallocation.py neutron/tests/unit/objects/plugins/ml2/test_vlanallocation.py neutron/tests/unit/objects/plugins/ml2/test_vxlanallocation.py neutron/tests/unit/objects/port/__init__.py neutron/tests/unit/objects/port/extensions/__init__.py neutron/tests/unit/objects/port/extensions/test_allowedaddresspairs.py neutron/tests/unit/objects/port/extensions/test_data_plane_status.py neutron/tests/unit/objects/port/extensions/test_extra_dhcp_opt.py neutron/tests/unit/objects/port/extensions/test_port_security.py neutron/tests/unit/objects/qos/__init__.py neutron/tests/unit/objects/qos/test_binding.py neutron/tests/unit/objects/qos/test_policy.py neutron/tests/unit/objects/qos/test_rule.py neutron/tests/unit/objects/qos/test_rule_type.py neutron/tests/unit/plugins/__init__.py neutron/tests/unit/plugins/common/__init__.py neutron/tests/unit/plugins/common/test_utils.py neutron/tests/unit/plugins/ml2/__init__.py neutron/tests/unit/plugins/ml2/_test_mech_agent.py neutron/tests/unit/plugins/ml2/base.py neutron/tests/unit/plugins/ml2/test_agent_scheduler.py neutron/tests/unit/plugins/ml2/test_db.py neutron/tests/unit/plugins/ml2/test_driver_context.py neutron/tests/unit/plugins/ml2/test_ext_portsecurity.py neutron/tests/unit/plugins/ml2/test_extension_driver_api.py neutron/tests/unit/plugins/ml2/test_managers.py neutron/tests/unit/plugins/ml2/test_ovo_rpc.py neutron/tests/unit/plugins/ml2/test_plugin.py neutron/tests/unit/plugins/ml2/test_port_binding.py neutron/tests/unit/plugins/ml2/test_rpc.py neutron/tests/unit/plugins/ml2/test_security_group.py neutron/tests/unit/plugins/ml2/test_tracked_resources.py neutron/tests/unit/plugins/ml2/drivers/__init__.py neutron/tests/unit/plugins/ml2/drivers/base_type_tunnel.py neutron/tests/unit/plugins/ml2/drivers/ext_test.py neutron/tests/unit/plugins/ml2/drivers/mech_fake_agent.py neutron/tests/unit/plugins/ml2/drivers/mech_faulty_agent.py neutron/tests/unit/plugins/ml2/drivers/mechanism_logger.py neutron/tests/unit/plugins/ml2/drivers/mechanism_test.py neutron/tests/unit/plugins/ml2/drivers/test_helpers.py neutron/tests/unit/plugins/ml2/drivers/test_type_flat.py neutron/tests/unit/plugins/ml2/drivers/test_type_geneve.py neutron/tests/unit/plugins/ml2/drivers/test_type_gre.py neutron/tests/unit/plugins/ml2/drivers/test_type_local.py neutron/tests/unit/plugins/ml2/drivers/test_type_vlan.py neutron/tests/unit/plugins/ml2/drivers/test_type_vxlan.py neutron/tests/unit/plugins/ml2/drivers/agent/__init__.py neutron/tests/unit/plugins/ml2/drivers/agent/test__agent_manager_base.py neutron/tests/unit/plugins/ml2/drivers/agent/test__common_agent.py neutron/tests/unit/plugins/ml2/drivers/agent/test_capabilities.py neutron/tests/unit/plugins/ml2/drivers/l2pop/__init__.py neutron/tests/unit/plugins/ml2/drivers/l2pop/test_db.py neutron/tests/unit/plugins/ml2/drivers/l2pop/test_mech_driver.py neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/__init__.py neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/l2population_rpc_base.py neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/test_l2population_rpc.py neutron/tests/unit/plugins/ml2/drivers/linuxbridge/__init__.py neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/__init__.py neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_arp_protect.py neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_agent_extension_api.py neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/extension_drivers/__init__.py neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/extension_drivers/test_qos_driver.py neutron/tests/unit/plugins/ml2/drivers/linuxbridge/mech_driver/__init__.py neutron/tests/unit/plugins/ml2/drivers/linuxbridge/mech_driver/test_mech_linuxbridge.py neutron/tests/unit/plugins/ml2/drivers/macvtap/__init__.py neutron/tests/unit/plugins/ml2/drivers/macvtap/test_macvtap_common.py neutron/tests/unit/plugins/ml2/drivers/macvtap/agent/__init__.py neutron/tests/unit/plugins/ml2/drivers/macvtap/agent/test_macvtap_neutron_agent.py neutron/tests/unit/plugins/ml2/drivers/macvtap/mech_driver/__init__.py neutron/tests/unit/plugins/ml2/drivers/macvtap/mech_driver/test_mech_macvtap.py neutron/tests/unit/plugins/ml2/drivers/mech_sriov/__init__.py neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/__init__.py neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/common/__init__.py neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/common/test_config.py neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/__init__.py neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/test_qos_driver.py neutron/tests/unit/plugins/ml2/drivers/mech_sriov/mech_driver/__init__.py neutron/tests/unit/plugins/ml2/drivers/mech_sriov/mech_driver/test_mech_sriov_nic_switch.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/__init__.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/__init__.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/fake_oflib.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/ovs_test_base.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_agent_extension_api.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_capabilities.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_vlanmanager.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/__init__.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_driver.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/__init__.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/test_br_cookie.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/__init__.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/ovs_bridge_test_base.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_tun.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_ovs_bridge.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/__init__.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ovs_bridge_test_base.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_int.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_phys.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_tun.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/mech_driver/__init__.py neutron/tests/unit/plugins/ml2/drivers/openvswitch/mech_driver/test_mech_openvswitch.py neutron/tests/unit/plugins/ml2/extensions/__init__.py neutron/tests/unit/plugins/ml2/extensions/fake_extension.py neutron/tests/unit/plugins/ml2/extensions/test_data_plane_status.py neutron/tests/unit/plugins/ml2/extensions/test_dns_integration.py neutron/tests/unit/plugins/ml2/extensions/test_port_security.py neutron/tests/unit/privileged/__init__.py neutron/tests/unit/privileged/agent/__init__.py neutron/tests/unit/privileged/agent/linux/__init__.py neutron/tests/unit/privileged/agent/linux/test_netlink_lib.py neutron/tests/unit/quota/__init__.py neutron/tests/unit/quota/test_resource.py neutron/tests/unit/quota/test_resource_registry.py neutron/tests/unit/scheduler/__init__.py neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py neutron/tests/unit/scheduler/test_l3_agent_scheduler.py neutron/tests/unit/services/__init__.py neutron/tests/unit/services/test_provider_configuration.py neutron/tests/unit/services/auto_allocate/__init__.py neutron/tests/unit/services/auto_allocate/test_db.py neutron/tests/unit/services/l3_router/__init__.py neutron/tests/unit/services/l3_router/test_l3_router_plugin.py neutron/tests/unit/services/l3_router/service_providers/__init__.py neutron/tests/unit/services/l3_router/service_providers/test_driver_controller.py neutron/tests/unit/services/logapi/__init__.py neutron/tests/unit/services/logapi/base.py neutron/tests/unit/services/logapi/test_logging_plugin.py neutron/tests/unit/services/logapi/agent/__init__.py neutron/tests/unit/services/logapi/agent/test_log_extension.py neutron/tests/unit/services/logapi/common/__init__.py neutron/tests/unit/services/logapi/common/test_db_api.py neutron/tests/unit/services/logapi/common/test_validators.py neutron/tests/unit/services/logapi/drivers/__init__.py neutron/tests/unit/services/logapi/drivers/test_base.py neutron/tests/unit/services/logapi/drivers/test_manager.py neutron/tests/unit/services/logapi/drivers/openvswitch/__init__.py neutron/tests/unit/services/logapi/drivers/openvswitch/test_ovs_firewall_log.py neutron/tests/unit/services/logapi/rpc/__init__.py neutron/tests/unit/services/logapi/rpc/test_server.py neutron/tests/unit/services/metering/__init__.py neutron/tests/unit/services/metering/test_metering_plugin.py neutron/tests/unit/services/metering/agents/__init__.py neutron/tests/unit/services/metering/agents/test_metering_agent.py neutron/tests/unit/services/metering/drivers/__init__.py neutron/tests/unit/services/metering/drivers/test_iptables.py neutron/tests/unit/services/qos/__init__.py neutron/tests/unit/services/qos/base.py neutron/tests/unit/services/qos/test_qos_plugin.py neutron/tests/unit/services/qos/drivers/__init__.py neutron/tests/unit/services/qos/drivers/test_manager.py neutron/tests/unit/services/revisions/__init__.py neutron/tests/unit/services/revisions/test_revision_plugin.py neutron/tests/unit/services/trunk/__init__.py neutron/tests/unit/services/trunk/fakes.py neutron/tests/unit/services/trunk/test_plugin.py neutron/tests/unit/services/trunk/test_rules.py neutron/tests/unit/services/trunk/test_utils.py neutron/tests/unit/services/trunk/drivers/__init__.py neutron/tests/unit/services/trunk/drivers/linuxbridge/__init__.py neutron/tests/unit/services/trunk/drivers/linuxbridge/test_driver.py neutron/tests/unit/services/trunk/drivers/linuxbridge/agent/__init__.py neutron/tests/unit/services/trunk/drivers/linuxbridge/agent/test_driver.py neutron/tests/unit/services/trunk/drivers/linuxbridge/agent/test_trunk_plumber.py neutron/tests/unit/services/trunk/drivers/openvswitch/__init__.py neutron/tests/unit/services/trunk/drivers/openvswitch/test_driver.py neutron/tests/unit/services/trunk/drivers/openvswitch/agent/__init__.py neutron/tests/unit/services/trunk/drivers/openvswitch/agent/test_driver.py neutron/tests/unit/services/trunk/drivers/openvswitch/agent/test_ovsdb_handler.py neutron/tests/unit/services/trunk/drivers/openvswitch/agent/test_trunk_manager.py neutron/tests/unit/services/trunk/rpc/__init__.py neutron/tests/unit/services/trunk/rpc/test_agent.py neutron/tests/unit/services/trunk/rpc/test_backend.py neutron/tests/unit/services/trunk/rpc/test_server.py neutron/tests/unit/services/trunk/seg_types/__init__.py neutron/tests/unit/services/trunk/seg_types/test_validators.py neutron/tests/unit/tests/__init__.py neutron/tests/unit/tests/test_base.py neutron/tests/unit/tests/test_post_mortem_debug.py neutron/tests/unit/tests/common/__init__.py neutron/tests/unit/tests/common/test_net_helpers.py neutron/tests/unit/tests/example/README neutron/tests/unit/tests/example/__init__.py neutron/tests/unit/tests/example/dir/__init__.py neutron/tests/unit/tests/example/dir/example_module.py neutron/tests/unit/tests/example/dir/subdir/__init__.py neutron/tests/unit/tests/example/dir/subdir/example_module.py neutron/tests/unit/tests/functional/__init__.py neutron/tests/unit/tests/functional/test_base.py neutron/tests/var/ca.crt neutron/tests/var/certandkey.pem neutron/tests/var/certificate.crt neutron/tests/var/privatekey.key playbooks/legacy/neutron-fullstack/post.yaml playbooks/legacy/neutron-fullstack/run.yaml playbooks/legacy/neutron-functional/post.yaml playbooks/legacy/neutron-functional/run.yaml playbooks/legacy/neutron-grenade/post.yaml playbooks/legacy/neutron-grenade/run.yaml playbooks/legacy/neutron-grenade-dvr-multinode/post.yaml playbooks/legacy/neutron-grenade-dvr-multinode/run.yaml playbooks/legacy/neutron-grenade-multinode/post.yaml playbooks/legacy/neutron-grenade-multinode/run.yaml playbooks/legacy/neutron-rally-neutron/post.yaml playbooks/legacy/neutron-rally-neutron/run.yaml playbooks/legacy/neutron-tempest-dvr/post.yaml playbooks/legacy/neutron-tempest-dvr/run.yaml playbooks/legacy/neutron-tempest-dvr-ha-multinode-full/post.yaml playbooks/legacy/neutron-tempest-dvr-ha-multinode-full/run.yaml playbooks/legacy/neutron-tempest-linuxbridge/post.yaml playbooks/legacy/neutron-tempest-linuxbridge/run.yaml playbooks/legacy/neutron-tempest-multinode-full/post.yaml playbooks/legacy/neutron-tempest-multinode-full/run.yaml playbooks/legacy/neutron-tempest-ovsfw/post.yaml playbooks/legacy/neutron-tempest-ovsfw/run.yaml rally-jobs/README.rst rally-jobs/neutron-neutron.yaml rally-jobs/extra/README.rst rally-jobs/extra/trunk_scenario.setup rally-jobs/plugins/README.rst rally-jobs/plugins/__init__.py rally-jobs/plugins/trunk_scenario.py releasenotes/notes/.placeholder releasenotes/notes/1500-default-mtu-b0d6e4ab193b62a4.yaml releasenotes/notes/1500-default-segment-mtu-54e2cf6aea9602d5.yaml releasenotes/notes/404-for-quota-tenant-2c09c16759269b21.yaml releasenotes/notes/Adds-http_proxy_to_wsgi-middleware-24e8271cbd94ffdf.yaml releasenotes/notes/Dscp-marking-for-linuxbridge-agent-e765d0d934fa4017.yaml releasenotes/notes/Ingress-bandwidth-limit-in-openvswitch-agent-51cda9bb6b511885.yaml releasenotes/notes/QoS-for-linuxbridge-agent-bdb13515aac4e555.yaml releasenotes/notes/QoS-ingress-bandwidth-limit-54cea12dbea71172.yaml releasenotes/notes/access_as_external_rbac-455dc74b9fa22761.yaml releasenotes/notes/add-availability-zone-4440cf00be7c54ba.yaml releasenotes/notes/add-conntrack-workers-89d303e9ec3b4963.yaml releasenotes/notes/add-designate-driver-ssl-options-169c299c96f2aff0.yaml releasenotes/notes/add-dhcp_release6-ff1b8d62fd7fe76d.yaml releasenotes/notes/add-dns-domain-to-ports-f71359d75909a2d5.yaml releasenotes/notes/add-dscp-for-tunneling-03e28fe7c2f34e86.yaml releasenotes/notes/add-enable-dvr-knob-636268f775bb4569.yaml releasenotes/notes/add-get-me-a-network-56321aeef5389001.yaml releasenotes/notes/add-integration-with-external-dns-f56ec8a4993b1fc4.yaml releasenotes/notes/add-ip-protocols-in-sg-60467a073e771aee.yaml releasenotes/notes/add-keepalived-vrrp-healt-check-f23ed7c853151484.yaml releasenotes/notes/add-minimum-bandwidth-support-sriov-63664b89f4dd1c1b.yaml releasenotes/notes/add-net-mtu-writable-api-extension-f7038f85f3494a74.yaml releasenotes/notes/add-osprofiler-support-7fc2de3001187075.yaml releasenotes/notes/add-placement-api-configuration-options-f1611d0909bf6166.yaml releasenotes/notes/add-port-data-plane-status-12726c964210b374.yaml releasenotes/notes/add-port-ip-allocation-attr-294a580641998240.yaml releasenotes/notes/add-port-rebinding-chance-33178b9abacf5804.yaml releasenotes/notes/add-rbac-qos-8b1154ee756c66df.yaml releasenotes/notes/add-standard-attr-descriptions-1ba0d7a454c3fd8f.yaml releasenotes/notes/add-subnet-service-types-bc81f6df9834f96e.yaml releasenotes/notes/add-tag-all-standardattr-resources-6f757cb39cc1dcfe.yaml releasenotes/notes/add-tags-to-core-resources-b05330a129900609.yaml releasenotes/notes/add-timestamp-fields-f9ab949fc88f05f6.yaml releasenotes/notes/add-wsgi-script-support-e611fa5b5c2043a5.yaml releasenotes/notes/add_dhcp_dnsmasq_t1t2_options-3cef427d8109c165.yaml releasenotes/notes/add_is_default_to_qos_policies-f7c6bbac08d474d5.yaml releasenotes/notes/advanced_image-8abff2ca91de7f6c.yaml releasenotes/notes/advertise_mtu_by_default-d8b0b056a74517b8.yaml releasenotes/notes/advertisement-intervals-for-radvd-configurable-6d85b5fdd97a2742.yaml releasenotes/notes/allow-non-admins-to-define-external-extra-routes-0d541fc356a5c546.yaml releasenotes/notes/allow_port_create_update_shared_owners-2a57b1c72d91ace2.yaml releasenotes/notes/bgp-support-ef361825ca63f28b.yaml releasenotes/notes/bug-1311040-dhcp-no-dns-09291c23e2ce800a.yaml releasenotes/notes/bump-default-quotas-810570badb378c50.yaml releasenotes/notes/change-of-default-timeout-b09d11683526e27d.yaml releasenotes/notes/change-oslo-db-defaults-f94df09c30767f95.yaml releasenotes/notes/change_external_network_bridge_default-5de3a0c19182eb70.yaml releasenotes/notes/clear-allowed-address-pairs-with-none-4757bcca78076c9e.yaml releasenotes/notes/common-agent-extension-api-3fd06ff67329200a.yaml releasenotes/notes/conditional_updates-10b9aa66fd144217.yaml releasenotes/notes/config-file-generation-2eafc6602d57178e.yaml releasenotes/notes/config-wsgi-pool-size-a4c06753b79fee6d.yaml releasenotes/notes/correlate-address-scope-with-network-ea16e16b0154ac21.yaml releasenotes/notes/custom_ethertypes-eae3fcab3293e3a1.yaml releasenotes/notes/default-local-dns-a1c3fa1451f228fa.yaml releasenotes/notes/default-subnetpool-semantics-1cdc5cdde2be88c2.yaml releasenotes/notes/deprecate-advertise-mtu-51e3f78475a14efc.yaml releasenotes/notes/deprecate-allow-sorting-allow-pagination-4549c92a74cfe15d.yaml releasenotes/notes/deprecate-force_gateway_on_subnet-376855c4e66f4e11.yaml releasenotes/notes/deprecate-gateway_external_network_id-f5c4071cd06714b0.yaml releasenotes/notes/deprecate-implicit-service-providers-loading-703f984b90351bf0.yaml releasenotes/notes/deprecate-ivs-interface-driver-b68e06a470c65ccb.yaml releasenotes/notes/deprecate-min-l3-agents-per-router-15ddaa4c178b23df.yaml releasenotes/notes/deprecate-network-device-mtu-59b78264c9974808.yaml releasenotes/notes/deprecate-neutron-rootwrap-xen-dom0-124ee3647beecc17.yaml releasenotes/notes/deprecate-of_interface-driver-option-1968f8bf6fcd1a38.yaml releasenotes/notes/deprecate-ovsdb-interface-b7e7cc5b036e9ef9.yaml releasenotes/notes/deprecate-router_id-34aca9ea5ee9e789.yaml releasenotes/notes/deprecate-send_arp_for_ha-0281853632f58e8d.yaml releasenotes/notes/deprecate-supported_pci_vendor_devs-12279b70a1f1fe8e.yaml releasenotes/notes/deprecate_max_fixed_ips_per_port-5e80518cbf25cfd6.yaml releasenotes/notes/deprecate_neutron_debug-a578e0adfc9cff4c.yaml releasenotes/notes/deprecate_prevent_arp_spoofing_option-a09e673fc8f9fee4.yaml releasenotes/notes/deprecated-driver-e368e0befc9bee4c.yaml releasenotes/notes/designate-driver-keystonev3-8e70d152e84388e0.yaml releasenotes/notes/dhcp-domain-removed-cc5bc6e2129fdf7f.yaml releasenotes/notes/dhcp-ipv6-address-update-ff18d1eb0c196bce.yaml releasenotes/notes/dhcp-lease-time-5c504c3730a4f9ea.yaml releasenotes/notes/direct-physical-vnic-878d15bdb758b70e.yaml releasenotes/notes/dnsmasq-local-service-c8eaa91894a7d6d4.yaml releasenotes/notes/dnsmasq_dns_servers-d729c04887ce67b4.yaml releasenotes/notes/dscp-qos-77ea9b27d3762e48.yaml releasenotes/notes/dvr-configure-centralized-floatingip-with-new-agent-type-05361f1f78853cf7.yaml releasenotes/notes/dvr-fip-namespace-on-all-nodes-c4da7ccd60ee62f5.yaml releasenotes/notes/dvr-ha-support-cc67e84d9380cd0b.yaml releasenotes/notes/dvr-ovs-agent-6052a8d60fddde22.yaml releasenotes/notes/dvr-support-live-migration-b818b12bd9cbb518.yaml releasenotes/notes/dvr_handle_unbound_floatingip_port-f12ae806b8be2065.yaml releasenotes/notes/enable-bridge-command-openvswitch-agent-d07c0b59ea9f864f.yaml releasenotes/notes/enable-sorting-pagination-754390289d3311fa.yaml releasenotes/notes/end-to-end-mtu-00345fc4282cb8fb.yaml releasenotes/notes/enhance-tags-1f8915fe3e074069.yaml releasenotes/notes/extend-quota-api-2df3b84309664234.yaml releasenotes/notes/fail-on-missing-extensions-bc332124b780875b.yaml releasenotes/notes/fdb_population-70d751c8c2e4395f.yaml releasenotes/notes/fip-janitor-53f0d42a7471c5ed.yaml releasenotes/notes/fip-qos-52926bce81c3f8bb.yaml releasenotes/notes/firewall_driver_not_needed_on_server-4159669ad834dea6.yaml releasenotes/notes/fix-deferred-alloction-when-new-mac-in-same-request-as-binding-data-2a01c1ed1a8eff66.yaml releasenotes/notes/fix-mtu-for-existing-networks-5a476cde9bc46a53.yaml releasenotes/notes/fix-ovsdb-ssl-connection-4058caf4fdcb33ab.yaml releasenotes/notes/fix-security-group-protocol-by-numbers-48afb97ede961716.yaml releasenotes/notes/fix-update-port-fixed-ips-on-routed-provider-networks-c54a54844d9a3926.yaml releasenotes/notes/hyperv-neutron-agent-decomposition-ae6a052aeb48c6ac.yaml releasenotes/notes/hyperv-security-group-driver-fdbe0c0c292a1505.yaml releasenotes/notes/ib-dhcp-allocation-fix-a4ebe8b55bb2c065.yaml releasenotes/notes/ingress-bandwidth-limit-in-linuxbridge-agent-50a2dad610401474.yaml releasenotes/notes/ip-substring-port-filtering-f5c3d89c4a91e867.yaml releasenotes/notes/iptables-fail-on-missing-sysctl-bridge-firewalling-912f157b5671363f.yaml releasenotes/notes/ipv6_first_ip_address_valid-cd94b47bdcc642cf.yaml releasenotes/notes/keepalived-state-change-server-threads-9ed775e7533dd1a0.yaml releasenotes/notes/l2_adjacency-e6e54e5ff9aad9b7.yaml releasenotes/notes/l3-agent-api-get-router-info-93c316a792a9d87f.yaml releasenotes/notes/l3-agent-extensions-b348ff26aec0fe88.yaml releasenotes/notes/l3-agent-extensions-ha-state-change-f50ae363a53b0f18.yaml releasenotes/notes/linuxbridge-agent-extensions-66bdf9feee25ef99.yaml releasenotes/notes/linuxbridge-vxlan-udp-ports-73b260efefa15a46.yaml releasenotes/notes/linuxbridge_vxlan_arp_responder-e9ea91552e1b62a7.yaml releasenotes/notes/macvtap-l2-agent-2b551d8ec341196d.yaml releasenotes/notes/macvtap_assigned_vf_check-f4d07660ffd82a24.yaml releasenotes/notes/metering-driver-stevedore-alias-2c4fdb0556205a3a.yaml releasenotes/notes/metering-iptables-driver-load-interface-driver-ca397f1db40ec643.yaml releasenotes/notes/mtu-selection-and-advertisement-ab29f9ec43140224.yaml releasenotes/notes/netns_cleanup_kill_procs-af88d8c47c07dd9c.yaml releasenotes/notes/network_ip_availability-d64bd7032b3c15ee.yaml releasenotes/notes/network_link_prefix-e3fe37e37ea275b7.yaml releasenotes/notes/new-vif-type-for-pf-passthrough-33ec560b9b5d246f.yaml releasenotes/notes/noneutronversions-fbbdb98f350767d8.yaml releasenotes/notes/of_interface-native-by-default-0c07bdbd7365230a.yaml releasenotes/notes/oslo-cache-cache-url-deprecated-16cd3d335c5962eb.yaml releasenotes/notes/oslo-messaging-notifier-queue-d94677076a1db261.yaml releasenotes/notes/oslo-reports-166a169037bf64f2.yaml releasenotes/notes/oslo.messaging.notify.drivers-abb0d17b9e1bd470.yaml releasenotes/notes/overlay_ip_version-ml2-e6438b570844ef5c.yaml releasenotes/notes/ovs-ct-firewall-driver-52a70a6a16d06f59.yaml releasenotes/notes/ovs-ipv6-tunnel-endpoints-f41b4954a04c43f6.yaml releasenotes/notes/ovs-mac-table-size-config-option-d255d5208650f34b.yaml releasenotes/notes/ovs-make-inactivity-probe-configurable-39d669014d961c5c.yaml releasenotes/notes/ovs_hardware_offload_support-798d3896ab2c4b1d.yaml releasenotes/notes/ovsdb-native-by-default-38835d6963592396.yaml releasenotes/notes/ovsdb_timeout_override_for_ovs_cleanup_tool-e6ed6db258d0819e.yaml releasenotes/notes/path-mtu-back-to-zero-e4f9e8bdd8317ad4.yaml releasenotes/notes/physical_network-aware-dhcp-scheduling-94e9fadc7c7c5fec.yaml releasenotes/notes/pluggable-ipam-is-default-15c2ee15dc5b4a7b.yaml releasenotes/notes/precise-agent-state-transfer-67c771cb1ee04dd0.yaml releasenotes/notes/project_id-d5ea7a42be428230.yaml releasenotes/notes/qos-drivers-refactor-16ece9984958f8a4.yaml releasenotes/notes/qos-for-router-gateway-02340f7aa8be3b0d.yaml releasenotes/notes/qos-min-egress-bw-rule-b1c80f5675a4c1c3.yaml releasenotes/notes/qos-rule-type-details-api-call-27d792980235aec4.yaml releasenotes/notes/remove-advertise_mtu-28933264714453c4.yaml releasenotes/notes/remove-allow-pagination-allow-sorting-ff23ca5ccb3007b9.yaml releasenotes/notes/remove-driver-60eb7e26d95f7322.yaml releasenotes/notes/remove-force_gateway_on_subnet-77cb79f0b35d0c6d.yaml releasenotes/notes/remove-min-l3-agents-per-router-27aef7d91dec0348.yaml releasenotes/notes/remove-network_device_mtu-option-a1a96e99dc7f0a02.yaml releasenotes/notes/remove-quota_items-d50b4672dd31ea3e.yaml releasenotes/notes/remove-router_id-b3732089f8f1faa1.yaml releasenotes/notes/remove-send-arp-for-ha-c1b4a926b8e52b8e.yaml releasenotes/notes/remove-subnetpool-config-b15dbe59237aee7e.yaml releasenotes/notes/remove_max_fixed_ips_per_port-64f1fb36748d5756.yaml releasenotes/notes/removed_prevent_arp_spoofing-b49e91a92a93e3e1.yaml releasenotes/notes/rename-ovs-vsctl-timeout-9df1967c47f394c0.yaml releasenotes/notes/rename-tenant-to-project-b19a4068f8625969.yaml releasenotes/notes/rename-to-nova-metadata-ip-685fd81618c16d9d.yaml releasenotes/notes/rm-notify-entry-points-aa442134a780469a.yaml releasenotes/notes/security-group-port-range-check-73114bdcde459e53.yaml releasenotes/notes/segment_mtu_to_global_physnet_mtu-9cee5ff09557edeb.yaml releasenotes/notes/sending-garp-for-l3-ha-c118871833ad8743.yaml releasenotes/notes/set-of-default-qos-burst-value-0790773703fa08fc.yaml releasenotes/notes/sorting-pagination-extensions-e66e99e2a8f5e563.yaml releasenotes/notes/sriov-agent-num-vf-0-0c06424247e7efe0.yaml releasenotes/notes/sriov_allow_use_many_nics_for_one_physnet-3570aa67a60ce6c4.yaml releasenotes/notes/sriov_show_l2_agent_extensions-ca852e155a529e99.yaml releasenotes/notes/switching-to-haproxy-for-metadata-proxy-9d8f7549fadf9182.yaml releasenotes/notes/terminate-macvtap-agt-when-interface_mapping-not-present-3109faf3b44d366a.yaml releasenotes/notes/timestamp_format_change-73eda78566b4690b.yaml releasenotes/notes/trunk_inherit-455dc74b9fa22dad.yaml releasenotes/notes/use-keystoneauth-24f309566001a16b.yaml releasenotes/notes/use-pyroute2-in-ip-lib-558bfea8f14d1fea.yaml releasenotes/notes/vhost-user-reconnect-7650134520022e7d.yaml releasenotes/notes/vlan-aware-vms-aka-trunk-3341cc75ba1bf5b4.yaml releasenotes/notes/vxlan-multicast-groups-distribution-linuxbridge-9337019c961c01a7.yaml releasenotes/notes/web_framework_deprecation-f984b83a1366c5b1.yaml releasenotes/notes/web_framework_removed-6e4c5c7ca506523a.yaml releasenotes/source/README.rst releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/liberty.rst releasenotes/source/mitaka.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder tools/abandon_old_reviews.sh tools/check_unit_test_structure.sh tools/coding-checks.sh tools/configure_for_func_testing.sh tools/deploy_rootwrap.sh tools/generate_config_file_samples.sh tools/generate_dhclient_script_for_fullstack.sh tools/install_venv.py tools/install_venv_common.py tools/list_moved_globals.py tools/misc-sanity-checks.sh tools/ostestr_compat_shim.sh tools/split.sh tools/unassign_bug.py tools/with_venv.shneutron-12.1.1/neutron.egg-info/top_level.txt0000664000175000017500000000001013553660156021227 0ustar zuulzuul00000000000000neutron neutron-12.1.1/neutron.egg-info/pbr.json0000664000175000017500000000006113553660156020161 0ustar zuulzuul00000000000000{"git_version": "f07b62c378", "is_release": true}neutron-12.1.1/neutron.egg-info/not-zip-safe0000664000175000017500000000000113553660156020734 0ustar zuulzuul00000000000000 neutron-12.1.1/neutron.egg-info/entry_points.txt0000664000175000017500000001632213553660156022010 0ustar zuulzuul00000000000000[console_scripts] neutron-db-manage = neutron.db.migration.cli:main neutron-debug = neutron.debug.shell:main neutron-dhcp-agent = neutron.cmd.eventlet.agents.dhcp:main neutron-ipset-cleanup = neutron.cmd.ipset_cleanup:main neutron-keepalived-state-change = neutron.cmd.keepalived_state_change:main neutron-l3-agent = neutron.cmd.eventlet.agents.l3:main neutron-linuxbridge-agent = neutron.cmd.eventlet.plugins.linuxbridge_neutron_agent:main neutron-linuxbridge-cleanup = neutron.cmd.linuxbridge_cleanup:main neutron-macvtap-agent = neutron.cmd.eventlet.plugins.macvtap_neutron_agent:main neutron-metadata-agent = neutron.cmd.eventlet.agents.metadata:main neutron-metering-agent = neutron.cmd.eventlet.services.metering_agent:main neutron-netns-cleanup = neutron.cmd.netns_cleanup:main neutron-openvswitch-agent = neutron.cmd.eventlet.plugins.ovs_neutron_agent:main neutron-ovs-cleanup = neutron.cmd.ovs_cleanup:main neutron-pd-notify = neutron.cmd.pd_notify:main neutron-rootwrap = oslo_rootwrap.cmd:main neutron-rootwrap-daemon = oslo_rootwrap.cmd:daemon neutron-rpc-server = neutron.cmd.eventlet.server:main_rpc_eventlet neutron-sanity-check = neutron.cmd.sanity_check:main neutron-server = neutron.cmd.eventlet.server:main neutron-sriov-nic-agent = neutron.cmd.eventlet.plugins.sriov_nic_neutron_agent:main neutron-usage-audit = neutron.cmd.eventlet.usage_audit:main [neutron.agent.firewall_drivers] iptables = neutron.agent.linux.iptables_firewall:IptablesFirewallDriver iptables_hybrid = neutron.agent.linux.iptables_firewall:OVSHybridIptablesFirewallDriver noop = neutron.agent.firewall:NoopFirewallDriver openvswitch = neutron.agent.linux.openvswitch_firewall:OVSFirewallDriver [neutron.agent.l2.extensions] fdb = neutron.agent.l2.extensions.fdb_population:FdbPopulationAgentExtension log = neutron.services.logapi.agent.log_extension:LoggingExtension qos = neutron.agent.l2.extensions.qos:QosAgentExtension [neutron.agent.l3.extensions] fip_qos = neutron.agent.l3.extensions.fip_qos:FipQosAgentExtension [neutron.agent.linux.pd_drivers] dibbler = neutron.agent.linux.dibbler:PDDibbler [neutron.core_plugins] ml2 = neutron.plugins.ml2.plugin:Ml2Plugin [neutron.db.alembic_migrations] neutron = neutron.db.migration:alembic_migrations [neutron.interface_drivers] ivs = neutron.agent.linux.interface:IVSInterfaceDriver linuxbridge = neutron.agent.linux.interface:BridgeInterfaceDriver null = neutron.agent.linux.interface:NullDriver openvswitch = neutron.agent.linux.interface:OVSInterfaceDriver [neutron.ipam_drivers] fake = neutron.tests.unit.ipam.fake_driver:FakeDriver internal = neutron.ipam.drivers.neutrondb_ipam.driver:NeutronDbPool [neutron.ml2.extension_drivers] data_plane_status = neutron.plugins.ml2.extensions.data_plane_status:DataPlaneStatusExtensionDriver dns = neutron.plugins.ml2.extensions.dns_integration:DNSExtensionDriverML2 dns_domain_ports = neutron.plugins.ml2.extensions.dns_integration:DNSDomainPortsExtensionDriver port_security = neutron.plugins.ml2.extensions.port_security:PortSecurityExtensionDriver qos = neutron.plugins.ml2.extensions.qos:QosExtensionDriver test = neutron.tests.unit.plugins.ml2.drivers.ext_test:TestExtensionDriver testdb = neutron.tests.unit.plugins.ml2.drivers.ext_test:TestDBExtensionDriver [neutron.ml2.mechanism_drivers] fake_agent = neutron.tests.unit.plugins.ml2.drivers.mech_fake_agent:FakeAgentMechanismDriver faulty_agent = neutron.tests.unit.plugins.ml2.drivers.mech_faulty_agent:FaultyAgentMechanismDriver l2population = neutron.plugins.ml2.drivers.l2pop.mech_driver:L2populationMechanismDriver linuxbridge = neutron.plugins.ml2.drivers.linuxbridge.mech_driver.mech_linuxbridge:LinuxbridgeMechanismDriver logger = neutron.tests.unit.plugins.ml2.drivers.mechanism_logger:LoggerMechanismDriver macvtap = neutron.plugins.ml2.drivers.macvtap.mech_driver.mech_macvtap:MacvtapMechanismDriver openvswitch = neutron.plugins.ml2.drivers.openvswitch.mech_driver.mech_openvswitch:OpenvswitchMechanismDriver sriovnicswitch = neutron.plugins.ml2.drivers.mech_sriov.mech_driver.mech_driver:SriovNicSwitchMechanismDriver test = neutron.tests.unit.plugins.ml2.drivers.mechanism_test:TestMechanismDriver [neutron.ml2.type_drivers] flat = neutron.plugins.ml2.drivers.type_flat:FlatTypeDriver geneve = neutron.plugins.ml2.drivers.type_geneve:GeneveTypeDriver gre = neutron.plugins.ml2.drivers.type_gre:GreTypeDriver local = neutron.plugins.ml2.drivers.type_local:LocalTypeDriver vlan = neutron.plugins.ml2.drivers.type_vlan:VlanTypeDriver vxlan = neutron.plugins.ml2.drivers.type_vxlan:VxlanTypeDriver [neutron.qos.agent_drivers] linuxbridge = neutron.plugins.ml2.drivers.linuxbridge.agent.extension_drivers.qos_driver:QosLinuxbridgeAgentDriver ovs = neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers.qos_driver:QosOVSAgentDriver sriov = neutron.plugins.ml2.drivers.mech_sriov.agent.extension_drivers.qos_driver:QosSRIOVAgentDriver [neutron.service_plugins] auto_allocate = neutron.services.auto_allocate.plugin:Plugin dummy = neutron.tests.unit.dummy_plugin:DummyServicePlugin flavors = neutron.services.flavors.flavors_plugin:FlavorsPlugin log = neutron.services.logapi.logging_plugin:LoggingPlugin loki = neutron.services.loki.loki_plugin:LokiPlugin metering = neutron.services.metering.metering_plugin:MeteringPlugin network_ip_availability = neutron.services.network_ip_availability.plugin:NetworkIPAvailabilityPlugin qos = neutron.services.qos.qos_plugin:QoSPlugin revisions = neutron.services.revisions.revision_plugin:RevisionPlugin router = neutron.services.l3_router.l3_router_plugin:L3RouterPlugin segments = neutron.services.segments.plugin:Plugin tag = neutron.services.tag.tag_plugin:TagPlugin timestamp = neutron.services.timestamp.timestamp_plugin:TimeStampPlugin trunk = neutron.services.trunk.plugin:TrunkPlugin [neutron.services.external_dns_drivers] designate = neutron.services.externaldns.drivers.designate.driver:Designate [neutron.services.logapi.drivers] ovs = neutron.services.logapi.drivers.openvswitch.ovs_firewall_log:OVSFirewallLoggingDriver [neutron.services.metering_drivers] iptables = neutron.services.metering.drivers.iptables.iptables_driver:IptablesMeteringDriver noop = neutron.services.metering.drivers.noop.noop_driver:NoopMeteringDriver [oslo.config.opts] neutron = neutron.opts:list_opts neutron.agent = neutron.opts:list_agent_opts neutron.az.agent = neutron.opts:list_az_agent_opts neutron.base.agent = neutron.opts:list_base_agent_opts neutron.db = neutron.opts:list_db_opts neutron.dhcp.agent = neutron.opts:list_dhcp_agent_opts neutron.extensions = neutron.opts:list_extension_opts neutron.l3.agent = neutron.opts:list_l3_agent_opts neutron.metadata.agent = neutron.opts:list_metadata_agent_opts neutron.metering.agent = neutron.opts:list_metering_agent_opts neutron.ml2 = neutron.opts:list_ml2_conf_opts neutron.ml2.linuxbridge.agent = neutron.opts:list_linux_bridge_opts neutron.ml2.macvtap.agent = neutron.opts:list_macvtap_opts neutron.ml2.ovs.agent = neutron.opts:list_ovs_opts neutron.ml2.sriov.agent = neutron.opts:list_sriov_agent_opts neutron.ml2.xenapi = neutron.opts:list_xenapi_opts nova.auth = neutron.opts:list_auth_opts [oslo.config.opts.defaults] neutron = neutron.common.config:set_cors_middleware_defaults [oslo.policy.enforcer] neutron = neutron.policy:get_enforcer [wsgi_scripts] neutron-api = neutron.server:get_application neutron-12.1.1/neutron.egg-info/requires.txt0000664000175000017500000000216113553660156021106 0ustar zuulzuul00000000000000pbr!=2.1.0,>=2.0.0 Paste>=2.0.2 PasteDeploy>=1.5.0 Routes>=2.3.1 debtcollector>=1.2.0 eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 pecan!=1.0.2,!=1.0.3,!=1.0.4,!=1.2,>=1.0.0 httplib2>=0.9.1 requests!=2.20.0,>=2.14.2 Jinja2!=2.9.0,!=2.9.1,!=2.9.2,!=2.9.3,!=2.9.4,>=2.8 keystonemiddleware>=4.17.0 netaddr>=0.7.18 netifaces>=0.10.4 neutron-lib>=1.13.0 python-neutronclient>=6.3.0 tenacity>=3.2.1 ryu>=4.14 SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 WebOb>=1.7.1 keystoneauth1>=3.3.0 alembic>=0.8.10 six>=1.10.0 stevedore>=1.20.0 oslo.cache>=1.26.0 oslo.concurrency>=3.25.0 oslo.config>=5.1.0 oslo.context>=2.19.2 oslo.db>=4.27.0 oslo.i18n>=3.15.3 oslo.log>=3.36.0 oslo.messaging>=5.29.0 oslo.middleware>=3.31.0 oslo.policy>=1.30.0 oslo.privsep>=1.23.0 oslo.reports>=1.18.0 oslo.rootwrap>=5.8.0 oslo.serialization!=2.19.1,>=2.18.0 oslo.service!=1.28.1,>=1.24.0 oslo.utils>=3.33.0 oslo.versionedobjects>=1.31.2 osprofiler>=1.4.0 ovs>=2.8.0 ovsdbapp>=0.8.0 psutil>=3.2.2 python-novaclient>=9.1.0 python-designateclient>=2.7.0 os-xenapi>=0.3.1 [:(python_version=='2.7')] weakrefmethod>=1.0.2 [:(sys_platform!='win32')] pyroute2>=0.4.21 neutron-12.1.1/neutron.egg-info/dependency_links.txt0000664000175000017500000000000113553660156022554 0ustar zuulzuul00000000000000 neutron-12.1.1/requirements.txt0000664000175000017500000000356713553660047016620 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 Paste>=2.0.2 # MIT PasteDeploy>=1.5.0 # MIT Routes>=2.3.1 # MIT debtcollector>=1.2.0 # Apache-2.0 eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 # MIT pecan!=1.0.2,!=1.0.3,!=1.0.4,!=1.2,>=1.0.0 # BSD httplib2>=0.9.1 # MIT requests!=2.20.0,>=2.14.2 # Apache-2.0 Jinja2!=2.9.0,!=2.9.1,!=2.9.2,!=2.9.3,!=2.9.4,>=2.8 # BSD License (3 clause) keystonemiddleware>=4.17.0 # Apache-2.0 netaddr>=0.7.18 # BSD netifaces>=0.10.4 # MIT neutron-lib>=1.13.0 # Apache-2.0 python-neutronclient>=6.3.0 # Apache-2.0 tenacity>=3.2.1 # Apache-2.0 ryu>=4.14 # Apache-2.0 SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT WebOb>=1.7.1 # MIT keystoneauth1>=3.3.0 # Apache-2.0 alembic>=0.8.10 # MIT six>=1.10.0 # MIT stevedore>=1.20.0 # Apache-2.0 oslo.cache>=1.26.0 # Apache-2.0 oslo.concurrency>=3.25.0 # Apache-2.0 oslo.config>=5.1.0 # Apache-2.0 oslo.context>=2.19.2 # Apache-2.0 oslo.db>=4.27.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.messaging>=5.29.0 # Apache-2.0 oslo.middleware>=3.31.0 # Apache-2.0 oslo.policy>=1.30.0 # Apache-2.0 oslo.privsep>=1.23.0 # Apache-2.0 oslo.reports>=1.18.0 # Apache-2.0 oslo.rootwrap>=5.8.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.service!=1.28.1,>=1.24.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 oslo.versionedobjects>=1.31.2 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 ovs>=2.8.0 # Apache-2.0 ovsdbapp>=0.8.0 # Apache-2.0 psutil>=3.2.2 # BSD pyroute2>=0.4.21;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2) weakrefmethod>=1.0.2;python_version=='2.7' # PSF python-novaclient>=9.1.0 # Apache-2.0 python-designateclient>=2.7.0 # Apache-2.0 os-xenapi>=0.3.1 # Apache-2.0 neutron-12.1.1/api-ref/0000775000175000017500000000000013553660156014645 5ustar zuulzuul00000000000000neutron-12.1.1/api-ref/README.rst0000664000175000017500000000015313553660046016331 0ustar zuulzuul00000000000000Networking API reference is maintained in the neutron-lib repo. See api-ref in the neutron-lib repository. neutron-12.1.1/TESTING.rst0000664000175000017500000006521213553660047015176 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) .. _testing_neutron: Testing Neutron =============== Why Should You Care ------------------- There's two ways to approach testing: 1) Write unit tests because they're required to get your patch merged. This typically involves mock heavy tests that assert that your code is as written. 2) Putting as much thought into your testing strategy as you do to the rest of your code. Use different layers of testing as appropriate to provide high *quality* coverage. Are you touching an agent? Test it against an actual system! Are you adding a new API? Test it for race conditions against a real database! Are you adding a new cross-cutting feature? Test that it does what it's supposed to do when run on a real cloud! Do you feel the need to verify your change manually? If so, the next few sections attempt to guide you through Neutron's different test infrastructures to help you make intelligent decisions and best exploit Neutron's test offerings. Definitions ----------- We will talk about three classes of tests: unit, functional and integration. Each respective category typically targets a larger scope of code. Other than that broad categorization, here are a few more characteristic: * Unit tests - Should be able to run on your laptop, directly following a 'git clone' of the project. The underlying system must not be mutated, mocks can be used to achieve this. A unit test typically targets a function or class. * Functional tests - Run against a pre-configured environment (tools/configure_for_func_testing.sh). Typically test a component such as an agent using no mocks. * Integration tests - Run against a running cloud, often target the API level, but also 'scenarios' or 'user stories'. You may find such tests under tests/tempest/api, tests/tempest/scenario, tests/fullstack, and in the Tempest and Rally projects. Tests in the Neutron tree are typically organized by the testing infrastructure used, and not by the scope of the test. For example, many tests under the 'unit' directory invoke an API call and assert that the expected output was received. The scope of such a test is the entire Neutron server stack, and clearly not a specific function such as in a typical unit test. Testing Frameworks ------------------ The different frameworks are listed below. The intent is to list the capabilities of each testing framework as to help the reader understand when should each tool be used. Remember that when adding code that touches many areas of Neutron, each area should be tested with the appropriate framework. Overlap between different test layers is often desirable and encouraged. Unit Tests ~~~~~~~~~~ Unit tests (neutron/tests/unit/) are meant to cover as much code as possible. They are designed to test the various pieces of the Neutron tree to make sure any new changes don't break existing functionality. Unit tests have no requirements nor make changes to the system they are running on. They use an in-memory sqlite database to test DB interaction. At the start of each test run: * RPC listeners are mocked away. * The fake Oslo messaging driver is used. At the end of each test run: * Mocks are automatically reverted. * The in-memory database is cleared of content, but its schema is maintained. * The global Oslo configuration object is reset. The unit testing framework can be used to effectively test database interaction, for example, distributed routers allocate a MAC address for every host running an OVS agent. One of DVR's DB mixins implements a method that lists all host MAC addresses. Its test looks like this: .. code-block:: python def test_get_dvr_mac_address_list(self): self._create_dvr_mac_entry('host_1', 'mac_1') self._create_dvr_mac_entry('host_2', 'mac_2') mac_list = self.mixin.get_dvr_mac_address_list(self.ctx) self.assertEqual(2, len(mac_list)) It inserts two new host MAC address, invokes the method under test and asserts its output. The test has many things going for it: * It targets the method under test correctly, not taking on a larger scope than is necessary. * It does not use mocks to assert that methods were called, it simply invokes the method and asserts its output (In this case, that the list method returns two records). This is allowed by the fact that the method was built to be testable - The method has clear input and output with no side effects. You can get oslo.db to generate a file-based sqlite database by setting OS_TEST_DBAPI_ADMIN_CONNECTION to a file based URL as described in `this mailing list post`__. This file will be created but (confusingly) won't be the actual file used for the database. To find the actual file, set a break point in your test method and inspect self.engine.url. __ file-based-sqlite_ .. code-block:: shell $ OS_TEST_DBAPI_ADMIN_CONNECTION=sqlite:///sqlite.db .tox/py27/bin/python -m \ testtools.run neutron.tests.unit... ... (Pdb) self.engine.url sqlite:////tmp/iwbgvhbshp.db Now, you can inspect this file using sqlite3. .. code-block:: shell $ sqlite3 /tmp/iwbgvhbshp.db Functional Tests ~~~~~~~~~~~~~~~~ Functional tests (neutron/tests/functional/) are intended to validate actual system interaction. Mocks should be used sparingly, if at all. Care should be taken to ensure that existing system resources are not modified and that resources created in tests are properly cleaned up both on test success and failure. Let's examine the benefits of the functional testing framework. Neutron offers a library called 'ip_lib' that wraps around the 'ip' binary. One of its methods is called 'device_exists' which accepts a device name and a namespace and returns True if the device exists in the given namespace. It's easy building a test that targets the method directly, and such a test would be considered a 'unit' test. However, what framework should such a test use? A test using the unit tests framework could not mutate state on the system, and so could not actually create a device and assert that it now exists. Such a test would look roughly like this: * It would mock 'execute', a method that executes shell commands against the system to return an IP device named 'foo'. * It would then assert that when 'device_exists' is called with 'foo', it returns True, but when called with a different device name it returns False. * It would most likely assert that 'execute' was called using something like: 'ip link show foo'. The value of such a test is arguable. Remember that new tests are not free, they need to be maintained. Code is often refactored, reimplemented and optimized. * There are other ways to find out if a device exists (Such as by looking at '/sys/class/net'), and in such a case the test would have to be updated. * Methods are mocked using their name. When methods are renamed, moved or removed, their mocks must be updated. This slows down development for avoidable reasons. * Most importantly, the test does not assert the behavior of the method. It merely asserts that the code is as written. When adding a functional test for 'device_exists', several framework level methods were added. These methods may now be used by other tests as well. One such method creates a virtual device in a namespace, and ensures that both the namespace and the device are cleaned up at the end of the test run regardless of success or failure using the 'addCleanup' method. The test generates details for a temporary device, asserts that a device by that name does not exist, creates that device, asserts that it now exists, deletes it, and asserts that it no longer exists. Such a test avoids all three issues mentioned above if it were written using the unit testing framework. Functional tests are also used to target larger scope, such as agents. Many good examples exist: See the OVS, L3 and DHCP agents functional tests. Such tests target a top level agent method and assert that the system interaction that was supposed to be performed was indeed performed. For example, to test the DHCP agent's top level method that accepts network attributes and configures dnsmasq for that network, the test: * Instantiates an instance of the DHCP agent class (But does not start its process). * Calls its top level function with prepared data. * Creates a temporary namespace and device, and calls 'dhclient' from that namespace. * Assert that the device successfully obtained the expected IP address. Test exceptions +++++++++++++++ Test neutron.tests.functional.agent.test_ovs_flows.OVSFlowTestCase.\ test_install_flood_to_tun is currently skipped if openvswitch version is less than 2.5.1. This version contains bug where appctl command prints wrong output for Final flow. It's been fixed in openvswitch 2.5.1 in `this commit `_. If openvswitch version meets the test requirement then the test is triggered normally. Fullstack Tests ~~~~~~~~~~~~~~~ Why? ++++ The idea behind "fullstack" testing is to fill a gap between unit + functional tests and Tempest. Tempest tests are expensive to run, and target black box API tests exclusively. Tempest requires an OpenStack deployment to be run against, which can be difficult to configure and setup. Full stack testing addresses these issues by taking care of the deployment itself, according to the topology that the test requires. Developers further benefit from full stack testing as it can sufficiently simulate a real environment and provide a rapidly reproducible way to verify code while you're still writing it. How? ++++ Full stack tests set up their own Neutron processes (Server & agents). They assume a working Rabbit and MySQL server before the run starts. Instructions on how to run fullstack tests on a VM are available below. Each test defines its own topology (What and how many servers and agents should be running). Since the test runs on the machine itself, full stack testing enables "white box" testing. This means that you can, for example, create a router through the API and then assert that a namespace was created for it. Full stack tests run in the Neutron tree with Neutron resources alone. You may use the Neutron API (The Neutron server is set to NOAUTH so that Keystone is out of the picture). VMs may be simulated with a container-like class: neutron.tests.fullstack.resources.machine.FakeFullstackMachine. An example of its usage may be found at: neutron/tests/fullstack/test_connectivity.py. Full stack testing can simulate multi node testing by starting an agent multiple times. Specifically, each node would have its own copy of the OVS/LinuxBridge/DHCP/L3 agents, all configured with the same "host" value. Each OVS agent is connected to its own pair of br-int/br-ex, and those bridges are then interconnected. For LinuxBridge agent each agent is started in its own namespace, called "host-". Such namespaces are connected with OVS "central" bridge to each other. .. image:: images/fullstack_multinode_simulation.png Segmentation at the database layer is guaranteed by creating a database per test. The messaging layer achieves segmentation by utilizing a RabbitMQ feature called 'vhosts'. In short, just like a MySQL server serve multiple databases, so can a RabbitMQ server serve multiple messaging domains. Exchanges and queues in one 'vhost' are segmented from those in another 'vhost'. Please note that if the change you would like to test using fullstack tests involves a change to python-neutronclient as well as neutron, then you should make sure your fullstack tests are in a separate third change that depends on the python-neutronclient change using the 'Depends-On' tag in the commit message. You will need to wait for the next release of python-neutronclient, and a minimum version bump for python-neutronclient in the global requirements, before your fullstack tests will work in the gate. This is because tox uses the version of python-neutronclient listed in the upper-constraints.txt file in the openstack/requirements repository. When? +++++ 1) You'd like to test the interaction between Neutron components (Server and agents) and have already tested each component in isolation via unit or functional tests. You should have many unit tests, fewer tests to test a component and even fewer to test their interaction. Edge cases should not be tested with full stack testing. 2) You'd like to increase coverage by testing features that require multi node testing such as l2pop, L3 HA and DVR. 3) You'd like to test agent restarts. We've found bugs in the OVS, DHCP and L3 agents and haven't found an effective way to test these scenarios. Full stack testing can help here as the full stack infrastructure can restart an agent during the test. Example +++++++ Neutron offers a Quality of Service API, initially offering bandwidth capping at the port level. In the reference implementation, it does this by utilizing an OVS feature. neutron.tests.fullstack.test_qos.TestQoSWithOvsAgent.test_qos_policy_rule_lifecycle is a positive example of how the fullstack testing infrastructure should be used. It creates a network, subnet, QoS policy & rule and a port utilizing that policy. It then asserts that the expected bandwidth limitation is present on the OVS bridge connected to that port. The test is a true integration test, in the sense that it invokes the API and then asserts that Neutron interacted with the hypervisor appropriately. Gate exceptions +++++++++++++++ Currently we compile openvswitch kernel module from source for fullstack job on the gate. The reason is to fix bug related to local VXLAN tunneling which is present in current Ubuntu Xenial 16.04 kernel. Kernel was fixed with this `commit `_ and backported with this `openvswitch commit `_. Currently we compile openvswitch userland and kernel module from source for the ovsfw tempest job on the gate. This is to avoid ovs-vswitchd core dumps. See the gate_hook.sh comments for details. API Tests ~~~~~~~~~ API tests (neutron/tests/tempest/api/) are intended to ensure the function and stability of the Neutron API. As much as possible, changes to this path should not be made at the same time as changes to the code to limit the potential for introducing backwards-incompatible changes, although the same patch that introduces a new API should include an API test. Since API tests target a deployed Neutron daemon that is not test-managed, they should not depend on controlling the runtime configuration of the target daemon. API tests should be black-box - no assumptions should be made about implementation. Only the contract defined by Neutron's REST API should be validated, and all interaction with the daemon should be via a REST client. The neutron/tests/tempest/api directory was copied from the Tempest project around the Kilo timeframe. At the time, there was an overlap of tests between the Tempest and Neutron repositories. This overlap was then eliminated by carving out a subset of resources that belong to Tempest, with the rest in Neutron. API tests that belong to Tempest deal with a subset of Neutron's resources: * Port * Network * Subnet * Security Group * Router * Floating IP These resources were chosen for their ubiquity. They are found in most Neutron deployments regardless of plugin, and are directly involved in the networking and security of an instance. Together, they form the bare minimum needed by Neutron. This is excluding extensions to these resources (For example: Extra DHCP options to subnets, or snat_gateway mode to routers) that are not mandatory in the majority of cases. Tests for other resources should be contributed to the Neutron repository. Scenario tests should be similarly split up between Tempest and Neutron according to the API they're targeting. To create an API test, the testing class must at least inherit from neutron.tests.tempest.api.base.BaseNetworkTest base class. As some of tests may require certain extensions to be enabled, the base class provides ``required_extensions`` class attribute which can be used by subclasses to define a list of required extensions for particular test class. Scenario Tests ~~~~~~~~~~~~~~ Scenario tests (neutron/tests/tempest/scenario), like API tests, use the Tempest test infrastructure and have the same requirements. Guidelines for writing a good scenario test may be found at the Tempest developer guide: https://docs.openstack.org/tempest/latest/field_guide/scenario.html Scenario tests, like API tests, are split between the Tempest and Neutron repositories according to the Neutron API the test is targeting. Some scenario tests require advanced ``Glance`` images (for example, ``Ubuntu`` or ``CentOS``) in order to pass. Those tests are skipped by default. To enable them, include the following in ``tempest.conf``: .. code-block:: ini [compute] image_ref = [neutron_plugin_options] image_is_advanced = True Specific test requirements for advanced images are: #. ``test_trunk`` requires ``802.11q`` kernel module loaded. Rally Tests ~~~~~~~~~~~ Rally tests (rally-jobs/plugins) use the `rally `_ infrastructure to exercise a neutron deployment. Guidelines for writing a good rally test can be found in the `rally plugin documentation `_. There are also some examples in tree; the process for adding rally plugins to neutron requires three steps: 1) write a plugin and place it under rally-jobs/plugins/. This is your rally scenario; 2) (optional) add a setup file under rally-jobs/extra/. This is any devstack configuration required to make sure your environment can successfully process your scenario requests; 3) edit neutron-neutron.yaml. This is your scenario 'contract' or SLA. Development Process ------------------- It is expected that any new changes that are proposed for merge come with tests for that feature or code area. Any bugs fixes that are submitted must also have tests to prove that they stay fixed! In addition, before proposing for merge, all of the current tests should be passing. Structure of the Unit Test Tree ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The structure of the unit test tree should match the structure of the code tree, e.g. :: - target module: neutron.agent.utils - test module: neutron.tests.unit.agent.test_utils Unit test modules should have the same path under neutron/tests/unit/ as the module they target has under neutron/, and their name should be the name of the target module prefixed by `test_`. This requirement is intended to make it easier for developers to find the unit tests for a given module. Similarly, when a test module targets a package, that module's name should be the name of the package prefixed by `test_` with the same path as when a test targets a module, e.g. :: - target package: neutron.ipam - test module: neutron.tests.unit.test_ipam The following command can be used to validate whether the unit test tree is structured according to the above requirements: :: ./tools/check_unit_test_structure.sh Where appropriate, exceptions can be added to the above script. If code is not part of the Neutron namespace, for example, it's probably reasonable to exclude their unit tests from the check. .. note :: At no time should the production code import anything from testing subtree (neutron.tests). There are distributions that split out neutron.tests modules in a separate package that is not installed by default, making any code that relies on presence of the modules to fail. For example, RDO is one of those distributions. Running Tests ------------- Before submitting a patch for review you should always ensure all tests pass; a tox run is triggered by the jenkins gate executed on gerrit for each patch pushed for review. Neutron, like other OpenStack projects, uses `tox`_ for managing the virtual environments for running test cases. It uses `Testr`_ for managing the running of the test cases. Tox handles the creation of a series of `virtualenvs`_ that target specific versions of Python. Testr handles the parallel execution of series of test cases as well as the tracking of long-running tests and other things. For more information on the standard Tox-based test infrastructure used by OpenStack and how to do some common test/debugging procedures with Testr, see this wiki page: https://wiki.openstack.org/wiki/Testr .. _Testr: https://wiki.openstack.org/wiki/Testr .. _tox: http://tox.readthedocs.org/en/latest/ .. _virtualenvs: https://pypi.python.org/pypi/virtualenv PEP8 and Unit Tests ~~~~~~~~~~~~~~~~~~~ Running pep8 and unit tests is as easy as executing this in the root directory of the Neutron source code:: tox To run only pep8:: tox -e pep8 Since pep8 includes running pylint on all files, it can take quite some time to run. To restrict the pylint check to only the files altered by the latest patch changes:: tox -e pep8 HEAD~1 To run only the unit tests:: tox -e py27 Functional Tests ~~~~~~~~~~~~~~~~ To run functional tests that do not require sudo privileges or specific-system dependencies:: tox -e functional To run all the functional tests, including those requiring sudo privileges and system-specific dependencies, the procedure defined by tools/configure_for_func_testing.sh should be followed. IMPORTANT: configure_for_func_testing.sh relies on DevStack to perform extensive modification to the underlying host. Execution of the script requires sudo privileges and it is recommended that the following commands be invoked only on a clean and disposeable VM. A VM that has had DevStack previously installed on it is also fine. :: git clone https://git.openstack.org/openstack-dev/devstack ../devstack ./tools/configure_for_func_testing.sh ../devstack -i tox -e dsvm-functional The '-i' option is optional and instructs the script to use DevStack to install and configure all of Neutron's package dependencies. It is not necessary to provide this option if DevStack has already been used to deploy Neutron to the target host. Fullstack Tests ~~~~~~~~~~~~~~~ To run all the full-stack tests, you may use: :: tox -e dsvm-fullstack Since full-stack tests often require the same resources and dependencies as the functional tests, using the configuration script tools/configure_for_func_testing.sh is advised (As described above). When running full-stack tests on a clean VM for the first time, we advise to run ./stack.sh successfully to make sure all Neutron's dependencies are met. Full-stack based Neutron daemons produce logs to a sub-folder in /opt/stack/logs/dsvm-fullstack-logs (for example, a test named "test_example" will produce logs to /opt/stack/logs/dsvm-fullstack-logs/test_example.log), so that will be a good place to look if your test is failing. Fullstack test suite assumes 240.0.0.0/4 (Class E) range in root namespace of the test machine is available for its usage. API & Scenario Tests ~~~~~~~~~~~~~~~~~~~~ To run the api or scenario tests, deploy Tempest and Neutron with DevStack and then run the following command, from the tempest directory: :: tox -e all-plugin If you want to limit the amount of tests that you would like to run, you can do, for instance: :: export DEVSTACK_GATE_TEMPEST_REGEX="" # e.g. "neutron" tox -e all-plugin $DEVSTACK_GATE_TEMPEST_REGEX Running Individual Tests ~~~~~~~~~~~~~~~~~~~~~~~~ For running individual test modules, cases or tests, you just need to pass the dot-separated path you want as an argument to it. For example, the following would run only a single test or test case:: $ tox -e py27 neutron.tests.unit.test_manager $ tox -e py27 neutron.tests.unit.test_manager.NeutronManagerTestCase $ tox -e py27 neutron.tests.unit.test_manager.NeutronManagerTestCase.test_service_plugin_is_loaded If you want to pass other arguments to ostestr, you can do the following:: $ tox -e py27 -- --regex neutron.tests.unit.test_manager --serial Coverage -------- Neutron has a fast growing code base and there are plenty of areas that need better coverage. To get a grasp of the areas where tests are needed, you can check current unit tests coverage by running:: $ tox -ecover Since the coverage command can only show unit test coverage, a coverage document is maintained that shows test coverage per area of code in: doc/source/devref/testing_coverage.rst. You could also rely on Zuul logs, that are generated post-merge (not every project builds coverage results). To access them, do the following: * Check out the latest `merge commit `_ * Go to: http://logs.openstack.org///post/neutron-coverage/. * `Spec `_ is a work in progress to provide a better landing page. Debugging --------- By default, calls to pdb.set_trace() will be ignored when tests are run. For pdb statements to work, invoke tox as follows:: $ tox -e venv -- python -m testtools.run [test module path] Tox-created virtual environments (venv's) can also be activated after a tox run and reused for debugging:: $ tox -e venv $ . .tox/venv/bin/activate $ python -m testtools.run [test module path] Tox packages and installs the Neutron source tree in a given venv on every invocation, but if modifications need to be made between invocation (e.g. adding more pdb statements), it is recommended that the source tree be installed in the venv in editable mode:: # run this only after activating the venv $ pip install --editable . Editable mode ensures that changes made to the source tree are automatically reflected in the venv, and that such changes are not overwritten during the next tox run. Post-mortem Debugging ~~~~~~~~~~~~~~~~~~~~~ TBD: how to do this with tox. References ~~~~~~~~~~ .. _file-based-sqlite: http://lists.openstack.org/pipermail/openstack-dev/2016-July/099861.html neutron-12.1.1/.zuul.yaml0000664000175000017500000002470613553660047015273 0ustar zuulzuul00000000000000- project: templates: - neutron-tempest-plugin-jobs-queens - openstack-python-jobs - openstack-python35-jobs - publish-openstack-sphinx-docs - periodic-stable-jobs - check-requirements - integrated-gate - integrated-gate-py35 - release-notes-jobs check: jobs: - neutron-functional - neutron-fullstack - neutron-rally-neutron - neutron-tempest-dvr - neutron-tempest-linuxbridge - neutron-grenade-multinode - neutron-grenade-dvr-multinode - neutron-tempest-multinode-full - neutron-tempest-dvr-ha-multinode-full - neutron-tempest-ovsfw - neutron-grenade - openstack-tox-cover - tempest-full: irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ gate: jobs: - neutron-functional - neutron-tempest-dvr - neutron-tempest-linuxbridge - neutron-grenade-multinode - neutron-grenade-dvr-multinode - neutron-grenade - openstack-tox-cover post: jobs: - openstack-tox-cover experimental: jobs: - legacy-grenade-dsvm-neutron-linuxbridge-multinode: irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ - legacy-grenade-dsvm-neutron-forward: irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ - legacy-neutron-dsvm-api-pecan - legacy-tempest-dsvm-neutron-pg-full: irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ - legacy-tempest-dsvm-neutron-pecan: irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ - legacy-tempest-dsvm-neutron-full-centos-7: irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ - legacy-tempest-dsvm-neutron-dvr-multinode-full: irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ - legacy-tempest-dsvm-neutron-with-ryu-master: irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ - job: name: neutron-functional parent: legacy-dsvm-base run: playbooks/legacy/neutron-functional/run.yaml post-run: playbooks/legacy/neutron-functional/post.yaml timeout: 7800 required-projects: - openstack/devstack-gate - openstack/neutron irrelevant-files: - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^releasenotes/.*$ - job: name: neutron-fullstack parent: legacy-dsvm-base run: playbooks/legacy/neutron-fullstack/run.yaml post-run: playbooks/legacy/neutron-fullstack/post.yaml timeout: 7800 required-projects: - openstack/devstack-gate - openstack/neutron irrelevant-files: - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^releasenotes/.*$ voting: false - job: name: neutron-rally-neutron parent: legacy-dsvm-base run: playbooks/legacy/neutron-rally-neutron/run.yaml post-run: playbooks/legacy/neutron-rally-neutron/post.yaml timeout: 7800 required-projects: - openstack/devstack-gate - openstack/aodh - openstack/ceilometer - openstack/diskimage-builder - openstack/ironic - openstack/magnum - openstack/networking-bgpvpn - openstack/neutron - openstack/neutron-fwaas - openstack/neutron-lbaas - openstack/neutron-vpnaas - openstack/octavia - openstack/panko - openstack/python-magnumclient - openstack/python-senlinclient - openstack/python-watcherclient - openstack/python-zaqarclient - {name: openstack/rally, override-checkout: 0.12.1} - openstack/senlin - openstack/tripleo-image-elements - openstack/watcher - openstack/zaqar-ui irrelevant-files: - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^tools/.*$ - ^tox.ini$ - job: name: neutron-tempest-dvr parent: legacy-dsvm-base run: playbooks/legacy/neutron-tempest-dvr/run.yaml post-run: playbooks/legacy/neutron-tempest-dvr/post.yaml timeout: 7800 required-projects: - openstack/devstack-gate - openstack/neutron - openstack/tempest irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ - job: name: neutron-tempest-linuxbridge parent: legacy-dsvm-base run: playbooks/legacy/neutron-tempest-linuxbridge/run.yaml post-run: playbooks/legacy/neutron-tempest-linuxbridge/post.yaml timeout: 8400 required-projects: - openstack/devstack-gate - openstack/neutron - openstack/tempest irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ - job: name: neutron-grenade-multinode parent: legacy-dsvm-base-multinode run: playbooks/legacy/neutron-grenade-multinode/run.yaml post-run: playbooks/legacy/neutron-grenade-multinode/post.yaml timeout: 10800 required-projects: - openstack/grenade - openstack/devstack-gate - openstack/neutron nodeset: ubuntu-xenial-2-node irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ - job: name: neutron-grenade-dvr-multinode parent: legacy-dsvm-base-multinode run: playbooks/legacy/neutron-grenade-dvr-multinode/run.yaml post-run: playbooks/legacy/neutron-grenade-dvr-multinode/post.yaml timeout: 7500 required-projects: - openstack/grenade - openstack/devstack-gate - openstack/neutron nodeset: ubuntu-xenial-2-node irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ - job: name: neutron-tempest-multinode-full parent: legacy-dsvm-base-multinode run: playbooks/legacy/neutron-tempest-multinode-full/run.yaml post-run: playbooks/legacy/neutron-tempest-multinode-full/post.yaml timeout: 10800 required-projects: - openstack/devstack-gate - openstack/neutron - openstack/tempest nodeset: ubuntu-xenial-2-node irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ voting: false - job: name: neutron-tempest-dvr-ha-multinode-full parent: legacy-dsvm-base-multinode run: playbooks/legacy/neutron-tempest-dvr-ha-multinode-full/run.yaml post-run: playbooks/legacy/neutron-tempest-dvr-ha-multinode-full/post.yaml timeout: 10800 required-projects: - openstack/devstack-gate - openstack/neutron - openstack/tempest nodeset: ubuntu-xenial-3-node irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ voting: false - job: name: neutron-tempest-ovsfw parent: legacy-dsvm-base run: playbooks/legacy/neutron-tempest-ovsfw/run.yaml post-run: playbooks/legacy/neutron-tempest-ovsfw/post.yaml timeout: 7800 required-projects: - openstack/devstack-gate - openstack/neutron - openstack/tempest irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ voting: false - job: name: neutron-grenade parent: legacy-dsvm-base run: playbooks/legacy/neutron-grenade/run.yaml post-run: playbooks/legacy/neutron-grenade/post.yaml timeout: 10800 required-projects: - openstack/grenade - openstack/devstack-gate - openstack/neutron irrelevant-files: - ^(test-|)requirements.txt$ - ^.*\.rst$ - ^doc/.*$ - ^neutron/locale/.*$ - ^neutron/tests/unit/.*$ - ^releasenotes/.*$ - ^setup.cfg$ - ^tools/.*$ - ^tox.ini$ neutron-12.1.1/HACKING.rst0000664000175000017500000000473613553660046015130 0ustar zuulzuul00000000000000Neutron Style Commandments ========================== - Step 1: Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ - Step 2: Read on Neutron Specific Commandments ----------------------------- Some rules are enforced by `neutron-lib hacking factory `_ while other rules are specific to Neutron repository. Below you can find a list of checks specific to this repository. - [N322] Detect common errors with assert_called_once_with - [N328] Detect wrong usage with assertEqual - [N330] Use assertEqual(*empty*, observed) instead of assertEqual(observed, *empty*) - [N331] Detect wrong usage with assertTrue(isinstance()). - [N332] Use assertEqual(expected_http_code, observed_http_code) instead of assertEqual(observed_http_code, expected_http_code). - [N334] Use unittest2 uniformly across Neutron. - [N340] Check usage of .i18n (and neutron.i18n) - [N341] Check usage of _ from python builtins - [N343] Production code must not import from neutron.tests.* - [N344] Python 3: Do not use filter(lambda obj: test(obj), data). Replace it with [obj for obj in data if test(obj)]. .. note:: When adding a new hacking check to this repository or ``neutron-lib``, make sure its number (Nxxx) doesn't clash with any other check. .. note:: As you may have noticed, the numbering for Neutron checks has gaps. This is because some checks were removed or moved to ``neutron-lib``. Creating Unit Tests ------------------- For every new feature, unit tests should be created that both test and (implicitly) document the usage of said feature. If submitting a patch for a bug that had no unit test, a new passing unit test should be added. If a submitted bug fix does have a unit test, be sure to add a new one that fails without the patch and passes with the patch. All unittest classes must ultimately inherit from testtools.TestCase. In the Neutron test suite, this should be done by inheriting from neutron.tests.base.BaseTestCase. If the third party unittest library has to be used directly then it is recommended to use unittest2 as it contains bug fixes to unittest for all versions of Python prior to version 3.5. All setUp and tearDown methods must upcall using the super() method. tearDown methods should be avoided and addCleanup calls should be preferred. Never manually create tempfiles. Always use the tempfile fixtures from the fixture library to ensure that they are cleaned up. neutron-12.1.1/devstack/0000775000175000017500000000000013553660156015126 5ustar zuulzuul00000000000000neutron-12.1.1/devstack/lib/0000775000175000017500000000000013553660156015674 5ustar zuulzuul00000000000000neutron-12.1.1/devstack/lib/segments0000664000175000017500000000025613553660046017445 0ustar zuulzuul00000000000000function configure_segments_service_plugin { neutron_service_plugin_class_add segments } function configure_segments_extension { configure_segments_service_plugin } neutron-12.1.1/devstack/lib/flavors0000664000175000017500000000023713553660046017273 0ustar zuulzuul00000000000000# Neutron flavors plugin # ---------------------- FLAVORS_PLUGIN=flavors function configure_flavors { neutron_service_plugin_class_add $FLAVORS_PLUGIN } neutron-12.1.1/devstack/lib/macvtap_agent0000664000175000017500000000165513553660046020435 0ustar zuulzuul00000000000000# Neutron Macvtap L2 agent # ------------------------ # Save trace setting _XTRACE_NEUTRON_MACVTAP=$(set +o | grep xtrace) set +o xtrace function is_neutron_ovs_base_plugin { # macvtap doesn't use OVS return 1 } function neutron_plugin_create_nova_conf { : } function neutron_plugin_install_agent_packages { : } function neutron_plugin_configure_debug_command { : } function neutron_plugin_configure_dhcp_agent { : } function neutron_plugin_configure_l3_agent { : } function neutron_plugin_configure_plugin_agent { # Only the NooPFirewallDriver is supported. If not set, the agent will # terminate. iniset /$NEUTRON_CORE_PLUGIN_CONF securitygroup firewall_driver noop AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-macvtap-agent" } function neutron_plugin_setup_interface_driver { : } function neutron_plugin_check_adv_test_requirements { : } # Restore xtrace $_XTRACE_NEUTRON_MACVTAP neutron-12.1.1/devstack/lib/l2_agent_sriovnicswitch0000775000175000017500000000223113553660047022450 0ustar zuulzuul00000000000000SRIOV_AGENT_CONF="${NEUTRON_CORE_PLUGIN_CONF_PATH}/sriov_agent.ini" SRIOV_AGENT_BINARY="${NEUTRON_BIN_DIR}/neutron-sriov-nic-agent" function configure_l2_agent_sriovnicswitch { if [[ -n "$PHYSICAL_NETWORK" ]] && [[ -n "$PHYSICAL_INTERFACE" ]]; then PHYSICAL_DEVICE_MAPPINGS=$PHYSICAL_NETWORK:$PHYSICAL_INTERFACE fi if [[ -n "$PHYSICAL_DEVICE_MAPPINGS" ]]; then iniset /$SRIOV_AGENT_CONF sriov_nic physical_device_mappings $PHYSICAL_DEVICE_MAPPINGS fi iniset /$SRIOV_AGENT_CONF securitygroup firewall_driver neutron.agent.firewall.NoopFirewallDriver iniset /$SRIOV_AGENT_CONF agent extensions "$L2_AGENT_EXTENSIONS" } function start_l2_agent_sriov { local SERVICE_NAME if is_neutron_legacy_enabled; then SERVICE_NAME=q-sriov-agt else SERVICE_NAME=neutron-sriov-agent fi run_process $SERVICE_NAME "$SRIOV_AGENT_BINARY --config-file $NEUTRON_CONF --config-file /$SRIOV_AGENT_CONF" } function stop_l2_agent_sriov { local SERVICE_NAME if is_neutron_legacy_enabled; then SERVICE_NAME=q-sriov-agt else SERVICE_NAME=neutron-sriov-agent fi stop_process $SERVICE_NAME } neutron-12.1.1/devstack/lib/l3_agent0000664000175000017500000000063213553660046017312 0ustar zuulzuul00000000000000function plugin_agent_add_l3_agent_extension { local l3_agent_extension=$1 if [[ -z "$L3_AGENT_EXTENSIONS" ]]; then L3_AGENT_EXTENSIONS=$l3_agent_extension elif [[ ! ,${L3_AGENT_EXTENSIONS}, =~ ,${l3_agent_extension}, ]]; then L3_AGENT_EXTENSIONS+=",$l3_agent_extension" fi } function configure_l3_agent { iniset $NEUTRON_L3_CONF agent extensions "$L3_AGENT_EXTENSIONS" } neutron-12.1.1/devstack/lib/dns0000664000175000017500000000100213553660047016373 0ustar zuulzuul00000000000000function configure_dns_extension { neutron_ml2_extension_driver_add "dns_domain_ports" } function configure_dns_integration { iniset $NEUTRON_CONF DEFAULT external_dns_driver designate iniset $NEUTRON_CONF designate url "$DESIGNATE_SERVICE_PROTOCOL://$DESIGNATE_SERVICE_HOST:$DESIGNATE_SERVICE_PORT/v2" configure_auth_token_middleware $NEUTRON_CONF designate $NEUTRON_AUTH_CACHE_DIR designate } function post_config_dns_extension { iniset $NEUTRON_CONF DEFAULT dns_domain openstackgate.local } neutron-12.1.1/devstack/lib/qos0000664000175000017500000000072513553660047016424 0ustar zuulzuul00000000000000function configure_qos_service_plugin { neutron_service_plugin_class_add "qos" } function configure_qos_core_plugin { configure_qos_$NEUTRON_CORE_PLUGIN } function configure_qos_l2_agent { plugin_agent_add_l2_agent_extension "qos" } function configure_qos { configure_qos_service_plugin configure_qos_core_plugin configure_qos_l2_agent } function configure_l3_agent_extension_fip_qos { plugin_agent_add_l3_agent_extension "fip_qos" } neutron-12.1.1/devstack/lib/l2_agent0000664000175000017500000000064413553660046017314 0ustar zuulzuul00000000000000function plugin_agent_add_l2_agent_extension { local l2_agent_extension=$1 if [[ -z "$L2_AGENT_EXTENSIONS" ]]; then L2_AGENT_EXTENSIONS=$l2_agent_extension elif [[ ! ,${L2_AGENT_EXTENSIONS}, =~ ,${l2_agent_extension}, ]]; then L2_AGENT_EXTENSIONS+=",$l2_agent_extension" fi } function configure_l2_agent { iniset /$NEUTRON_CORE_PLUGIN_CONF agent extensions "$L2_AGENT_EXTENSIONS" } neutron-12.1.1/devstack/lib/log0000664000175000017500000000040213553660046016372 0ustar zuulzuul00000000000000function configure_log_service_plugin { neutron_service_plugin_class_add "log" } function configure_log_extension { plugin_agent_add_l2_agent_extension "log" } function configure_log { configure_log_service_plugin configure_log_extension } neutron-12.1.1/devstack/lib/ml20000664000175000017500000000205513553660047016312 0ustar zuulzuul00000000000000source $LIBDIR/ml2_drivers/sriovnicswitch function configure_qos_ml2 { neutron_ml2_extension_driver_add "qos" } function configure_ml2 { OIFS=$IFS; IFS=","; mechanism_drivers_array=($Q_ML2_PLUGIN_MECHANISM_DRIVERS); IFS=$OIFS; for mechanism_driver in "${mechanism_drivers_array[@]}"; do if [ "$(type -t configure_ml2_$mechanism_driver)" = function ]; then configure_ml2_$mechanism_driver fi done } function configure_ml2_extension_drivers { if is_neutron_legacy_enabled; then # NOTE(yamamoto): This overwrites what neutron-legacy set, # with the latest set of drivers. # While we modifies Q_ML2_PLUGIN_EXT_DRIVERS (via # neutron_ml2_extension_driver_add calls) in the post-config phase, # lib/neutron-legcay populates this in "configure_neutron", which is # before post-config. # REVISIT(yamamoto): Probably this ought to be in lib/neutron-legcay iniset /$NEUTRON_CORE_PLUGIN_CONF ml2 extension_drivers ${Q_ML2_PLUGIN_EXT_DRIVERS} fi } neutron-12.1.1/devstack/lib/ml2_drivers/0000775000175000017500000000000013553660156020124 5ustar zuulzuul00000000000000neutron-12.1.1/devstack/lib/ml2_drivers/sriovnicswitch0000775000175000017500000000006013553660047023123 0ustar zuulzuul00000000000000function configure_ml2_sriovnicswitch { : } neutron-12.1.1/devstack/lib/trunk0000664000175000017500000000024413553660046016760 0ustar zuulzuul00000000000000function configure_trunk_service_plugin { neutron_service_plugin_class_add "trunk" } function configure_trunk_extension { configure_trunk_service_plugin } neutron-12.1.1/devstack/lib/ovs0000664000175000017500000001511113553660047016424 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. OVS_REPO=${OVS_REPO:-https://github.com/openvswitch/ovs.git} OVS_REPO_NAME=$(basename ${OVS_REPO} | cut -f1 -d'.') OVS_BRANCH=${OVS_BRANCH:-master} # Functions # load_module() - Load module using modprobe module given by argument and dies # on failure # - fatal argument is optional and says whether function should # exit if module can't be loaded function load_module { local module=$1 local fatal=$2 if [ "$(trueorfalse True fatal)" == "True" ]; then sudo modprobe $module || (dmesg && die $LINENO "FAILED TO LOAD $module") else sudo modprobe $module || (echo "FAILED TO LOAD vport_geneve" && dmesg) fi } # prepare_for_compilation() - Fetch ovs git repository and install packages needed for # compilation. function prepare_for_compilation { local build_modules=${1:-True} OVS_DIR=$DEST/$OVS_REPO_NAME if [ ! -d $OVS_DIR ] ; then # We can't use git_clone here because we want to ignore ERROR_ON_CLONE git_timed clone $OVS_REPO $OVS_DIR cd $OVS_DIR git checkout $OVS_BRANCH else # Even though the directory already exists, call git_clone to update it # if needed based on the RECLONE option git_clone $OVS_REPO $OVS_DIR $OVS_BRANCH cd $OVS_DIR fi # TODO: Can you create package list files like you can inside devstack? install_package autoconf automake libtool gcc patch make # If build_modules is False, we don't need to install the kernel-* # packages. Just return. if [[ "$build_modules" == "False" ]]; then return fi KERNEL_VERSION=`uname -r` if is_fedora ; then # is_fedora covers Fedora, RHEL, CentOS, etc... if [[ ${KERNEL_VERSION:0:2} != "3." ]]; then # dash is illegal character in rpm version so replace # them with underscore like it is done in the kernel # https://github.com/torvalds/linux/blob/master/scripts/package/mkspec#L25 # but only for latest series of the kernel, not 3.x KERNEL_VERSION=`echo $KERNEL_VERSION | tr - _` fi echo NOTE: if kernel-devel-$KERNEL_VERSION or kernel-headers-$KERNEL_VERSION installation echo failed, please, provide a repository with the package, or yum update / reboot echo your machine to get the latest kernel. install_package kernel-devel-$KERNEL_VERSION install_package kernel-headers-$KERNEL_VERSION elif is_ubuntu ; then install_package linux-headers-$KERNEL_VERSION fi } # reload_kernel_modules() - reload openvswitch kernel module function reload_kernel_modules { sudo modprobe -r vport_geneve sudo modprobe -r openvswitch load_module openvswitch load_module vport-geneve False dmesg | tail } # compile_ovs() - Compile OVS from source and load needed modules. # Accepts two parameters: # - first one is True by default and means that modules are built and installed. # - second optional parameter defines prefix for ovs compilation # - third optional parameter defines localstatedir for ovs single machine runtime # Env variables OVS_REPO_NAME, OVS_REPO and OVS_BRANCH must be set function compile_ovs { local _pwd=$PWD local build_modules=${1:-True} local prefix=$2 local localstatedir=$3 if [ -n "$prefix" ]; then prefix="--prefix=$prefix" fi if [ -n "$localstatedir" ]; then localstatedir="--localstatedir=$localstatedir" fi prepare_for_compilation $build_modules if [ ! -f configure ] ; then ./boot.sh fi if [ ! -f config.status ] || [ configure -nt config.status ] ; then if [[ "$build_modules" == "True" ]]; then ./configure $prefix $localstatedir --with-linux=/lib/modules/$(uname -r)/build else ./configure $prefix $localstatedir fi fi make -j$[$(nproc) + 1] sudo make install if [[ "$build_modules" == "True" ]]; then sudo make INSTALL_MOD_DIR=kernel/net/openvswitch modules_install reload_kernel_modules fi cd $_pwd } # action_service - call an action over openvswitch service # Accepts one parameter that can be either # 'start', 'restart' and 'stop'. function action_openvswitch { local action=$1 if is_ubuntu; then ${action}_service openvswitch-switch elif is_fedora; then ${action}_service openvswitch elif is_suse; then if [[ $DISTRO == "sle12" ]] && [[ $os_RELEASE -lt 12.2 ]]; then ${action}_service openvswitch-switch else ${action}_service openvswitch fi fi } # compile_ovs_kernel_module() - Compile openvswitch kernel module and load it function compile_ovs_kernel_module { local _pwd=$PWD prepare_for_compilation [ -f configure ] || ./boot.sh if [ ! -f config.status ] || [ configure -nt config.status ] ; then ./configure --with-linux=/lib/modules/$(uname -r)/build fi action_openvswitch stop make -j$[$(nproc) + 1] sudo make INSTALL_MOD_DIR=kernel/net/openvswitch modules_install if [ $? -eq 0 ]; then reload_kernel_modules else echo "Compiling OVS kernel module failed" fi action_openvswitch start cd $_pwd } # start_new_ovs() - removes old ovs database, creates a new one and starts ovs function start_new_ovs () { sudo rm -f /etc/openvswitch/conf.db /etc/openvswitch/.conf.db~lock~ sudo /usr/share/openvswitch/scripts/ovs-ctl start } # stop_new_ovs() - stops ovs function stop_new_ovs () { local ovs_ctl='/usr/share/openvswitch/scripts/ovs-ctl' if [ -x $ovs_ctl ] ; then sudo $ovs_ctl stop fi } # remove_ovs_packages() - removes old ovs packages from the system function remove_ovs_packages() { for package in openvswitch openvswitch-switch openvswitch-common; do if is_package_installed $package; then uninstall_package $package fi done } neutron-12.1.1/devstack/plugin.sh0000664000175000017500000000607013553660047016762 0ustar zuulzuul00000000000000LIBDIR=$DEST/neutron/devstack/lib source $LIBDIR/dns source $LIBDIR/flavors source $LIBDIR/l2_agent source $LIBDIR/l2_agent_sriovnicswitch source $LIBDIR/l3_agent source $LIBDIR/ml2 source $LIBDIR/qos source $LIBDIR/ovs source $LIBDIR/segments source $LIBDIR/trunk source $LIBDIR/log Q_BUILD_OVS_FROM_GIT=$(trueorfalse False Q_BUILD_OVS_FROM_GIT) if [ -f $LIBDIR/${NEUTRON_AGENT}_agent ]; then source $LIBDIR/${NEUTRON_AGENT}_agent fi if [[ "$1" == "stack" ]]; then case "$2" in install) if [[ "$NEUTRON_AGENT" == "openvswitch" ]] && \ [[ "$Q_BUILD_OVS_FROM_GIT" == "True" ]]; then remove_ovs_packages compile_ovs True /usr /var start_new_ovs fi ;; post-config) if is_service_enabled q-flavors neutron-flavors; then configure_flavors fi if is_service_enabled q-qos neutron-qos; then configure_qos fi if is_service_enabled q-trunk neutron-trunk; then configure_trunk_extension fi if is_service_enabled q-log neutron-log; then configure_log fi if is_service_enabled q-dns neutron-dns; then configure_dns_extension post_config_dns_extension if is_service_enabled designate; then configure_dns_integration fi fi if is_service_enabled neutron-segments; then configure_segments_extension fi if is_service_enabled q-agt neutron-agent; then configure_l2_agent fi #Note: sriov agent should run with OVS or linux bridge agent #because they are the mechanisms that bind the DHCP and router ports. #Currently devstack lacks the option to run two agents on the same node. #Therefore we create new service, q-sriov-agt, and the # q-agt/neutron-agent should be OVS or linux bridge. if is_service_enabled q-sriov-agt neutron-sriov-agent; then configure_$NEUTRON_CORE_PLUGIN configure_l2_agent configure_l2_agent_sriovnicswitch fi if is_service_enabled q-l3 neutron-l3; then if is_service_enabled q-qos neutron-qos; then configure_l3_agent_extension_fip_qos fi configure_l3_agent fi if [ $NEUTRON_CORE_PLUGIN = ml2 ]; then configure_ml2_extension_drivers fi ;; extra) if is_service_enabled q-sriov-agt neutron-sriov-agent; then start_l2_agent_sriov fi ;; esac elif [[ "$1" == "unstack" ]]; then if is_service_enabled q-sriov-agt neutron-sriov-agent; then stop_l2_agent_sriov fi if [[ "$NEUTRON_AGENT" == "openvswitch" ]] && \ [[ "$Q_BUILD_OVS_FROM_GIT" == "True" ]]; then stop_new_ovs fi fi neutron-12.1.1/devstack/settings0000664000175000017500000000067413553660047016717 0ustar zuulzuul00000000000000L2_AGENT_EXTENSIONS=${L2_AGENT_EXTENSIONS:-} L3_AGENT_EXTENSIONS=${L3_AGENT_EXTENSIONS:-} if is_neutron_legacy_enabled; then NEUTRON_CORE_PLUGIN=$Q_PLUGIN NEUTRON_AGENT=$Q_AGENT # NOTE(ihrachys) those values are defined same way as in # lib/neutron_plugins/ml2:neutron_plugin_configure_common NEUTRON_CORE_PLUGIN_CONF_PATH=etc/neutron/plugins/ml2 NEUTRON_CORE_PLUGIN_CONF=$NEUTRON_CORE_PLUGIN_CONF_PATH/ml2_conf.ini fi neutron-12.1.1/LICENSE0000664000175000017500000002363713553660046014340 0ustar zuulzuul00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. neutron-12.1.1/tox.ini0000664000175000017500000001550513553660047014642 0ustar zuulzuul00000000000000[tox] envlist = docs,py35,py27,pep8 minversion = 2.3.2 skipsdist = True [testenv] setenv = VIRTUAL_ENV={envdir} PYTHONWARNINGS=default::DeprecationWarning passenv = TRACE_FAILONLY GENERATE_HASHES http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY usedevelop = True install_command = pip install -U -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/queens} {opts} {packages} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt whitelist_externals = sh commands = {toxinidir}/tools/ostestr_compat_shim.sh {posargs} # there is also secret magic in ostestr which lets you run in a fail only # mode. To do this define the TRACE_FAILONLY environmental variable. [testenv:common] # Fake job to define environment variables shared between dsvm/non-dsvm jobs setenv = OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:180} commands = false [testenv:dsvm] # Fake job to define environment variables shared between dsvm jobs setenv = OS_SUDO_TESTING=1 OS_ROOTWRAP_CMD=sudo {envdir}/bin/neutron-rootwrap {envdir}/etc/neutron/rootwrap.conf OS_ROOTWRAP_DAEMON_CMD=sudo {envdir}/bin/neutron-rootwrap-daemon {envdir}/etc/neutron/rootwrap.conf OS_FAIL_ON_MISSING_DEPS=1 OS_LOG_PATH={env:OS_LOG_PATH:/opt/stack/logs} commands = false [testenv:functional] basepython = python2.7 setenv = {[testenv]setenv} {[testenv:common]setenv} OS_TEST_PATH=./neutron/tests/functional OS_LOG_PATH={env:OS_LOG_PATH:/opt/stack/logs} deps = {[testenv]deps} -r{toxinidir}/neutron/tests/functional/requirements.txt [testenv:functional-python35] basepython = python3.5 setenv = {[testenv:functional]setenv} deps = {[testenv:functional]deps} [testenv:dsvm-functional] basepython = python2.7 setenv = {[testenv:functional]setenv} {[testenv:dsvm]setenv} deps = {[testenv:functional]deps} commands = {toxinidir}/tools/deploy_rootwrap.sh {toxinidir} {envdir}/etc {envdir}/bin {toxinidir}/tools/ostestr_compat_shim.sh {posargs} [testenv:dsvm-functional-python35] basepython = python3.5 setenv = {[testenv:dsvm-functional]setenv} deps = {[testenv:dsvm-functional]deps} commands = {toxinidir}/tools/deploy_rootwrap.sh {toxinidir} {envdir}/etc {envdir}/bin {toxinidir}/tools/ostestr_compat_shim.sh {posargs} [testenv:dsvm-fullstack] setenv = {[testenv]setenv} {[testenv:common]setenv} {[testenv:dsvm]setenv} # workaround for DB teardown lock contention (bug/1541742) OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:600} OS_TEST_PATH=./neutron/tests/fullstack deps = {[testenv:functional]deps} commands = {toxinidir}/tools/generate_dhclient_script_for_fullstack.sh {envdir} {toxinidir}/tools/deploy_rootwrap.sh {toxinidir} {envdir}/etc {envdir}/bin {[testenv]commands} [testenv:dsvm-fullstack-python35] basepython = python3.5 setenv = {[testenv:dsvm-fullstack]setenv} deps = {[testenv:dsvm-fullstack]deps} commands = {[testenv:dsvm-fullstack]commands} [testenv:releasenotes] commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [testenv:pep8] basepython = python2.7 deps = {[testenv]deps} commands= # If it is easier to add a check via a shell script, consider adding it in this file sh ./tools/misc-sanity-checks.sh {toxinidir}/tools/check_unit_test_structure.sh # Checks for coding and style guidelines flake8 sh ./tools/coding-checks.sh --pylint '{posargs}' neutron-db-manage --config-file neutron/tests/etc/neutron.conf check_migration python ./tools/list_moved_globals.py {[testenv:genconfig]commands} {[testenv:bashate]commands} {[testenv:bandit]commands} whitelist_externals = sh bash [testenv:cover] basepython = python2.7 commands = python setup.py testr --coverage --testr-args='{posargs}' coverage report --fail-under=82 --skip-covered [testenv:venv] commands = {posargs} [testenv:docs] commands = sphinx-build -W -b html doc/source doc/build/html [testenv:linkcheck] commands = sphinx-build -W -b linkcheck doc/source doc/build/linkcheck [flake8] # E125 continuation line does not distinguish itself from next logical line # E126 continuation line over-indented for hanging indent # E128 continuation line under-indented for visual indent # E129 visually indented line with same indent as next logical line # E265 block comment should start with '# ' # H404 multi line docstring should start with a summary # H405 multi line docstring summary not separated with an empty line # N530 direct neutron imports not allowed # TODO(ihrachys) figure out what to do with N534 and N536 # N534 Untranslated exception message # N536 Use assertIsNone rather than assertEqual to check for None values # TODO(amotoki) check the following new rules should be fixed or ignored # E402 module level import not at top of file # E731 do not assign a lambda expression, use a def # W503 line break before binary operator ignore = E125,E126,E128,E129,E265,E402,E731,W503,H404,H405,N530,N534,N536 # H106: Don't put vim configuration in source files # H203: Use assertIs(Not)None to check for None # H904: Delay string interpolations at logging calls enable-extensions=H106,H203,H904 show-source = true exclude = ./.*,build,dist,doc import-order-style = pep8 [hacking] import_exceptions = neutron._i18n local-check-factory = neutron.hacking.checks.factory [testenv:bandit] # B101: Use of assert detected # B104: Possible binding to all interfaces # B108: Probable insecure usage of temp file/directory # B111: Execute with run_as_root=True identified, possible security issue # B311: Standard pseudo-random generators are not suitable for security/cryptographic purpose deps = -r{toxinidir}/test-requirements.txt commands = bandit -r neutron -x tests -n5 -s B101,B104,B108,B111,B311 [testenv:bashate] commands = bash -c "find {toxinidir} \ -not \( -type d -name .tox\* -prune \) \ -not \( -type d -name .venv\* -prune \) \ -type f \ -name \*.sh \ # E005 file does not begin with #! or have a .sh prefix # E006 check for lines longer than 79 columns # E042 local declaration hides errors # E043 Arithmetic compound has inconsistent return semantics -print0 | xargs -0 bashate -v -iE006 -eE005,E042,E043" [testenv:genconfig] commands = {toxinidir}/tools/generate_config_file_samples.sh # This environment can be used to quickly validate that all needed system # packages required to successfully execute test targets are installed [testenv:bindep] # Do not install any requirements. We want this to be fast and work even if # system dependencies are missing, since it's used to tell you what system # dependencies are missing! This also means that bindep must be installed # separately, outside of the requirements files. deps = bindep commands = bindep test neutron-12.1.1/babel.cfg0000664000175000017500000000002113553660046015037 0ustar zuulzuul00000000000000[python: **.py] neutron-12.1.1/playbooks/0000775000175000017500000000000013553660156015325 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/0000775000175000017500000000000013553660156016571 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/neutron-tempest-dvr-ha-multinode-full/0000775000175000017500000000000013553660157026060 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/neutron-tempest-dvr-ha-multinode-full/run.yaml0000664000175000017500000000400113553660047027541 0ustar zuulzuul00000000000000- hosts: primary name: Autoconverted job legacy-tempest-dsvm-neutron-dvr-ha-multinode-full from old job gate-tempest-dsvm-neutron-dvr-ha-multinode-full-ubuntu-xenial-nv tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x cat << 'EOF' >>"/tmp/dg-local.conf" [[local|localrc]] NOVA_VNC_ENABLED=true VNCSERVER_LISTEN=0.0.0.0 VNCSERVER_PROXYCLIENT_ADDRESS=$HOST_IP EOF executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=1 export DEVSTACK_GATE_NEUTRON=1 export DEVSTACK_GATE_CONFIGDRIVE=0 export DEVSTACK_GATE_TEMPEST_FULL=1 # Test DVR works multinode export DEVSTACK_GATE_NEUTRON_DVR=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi export DEVSTACK_GATE_TOPOLOGY="multinode" cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' neutron-12.1.1/playbooks/legacy/neutron-tempest-dvr-ha-multinode-full/post.yaml0000664000175000017500000000063313553660047027731 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-12.1.1/playbooks/legacy/neutron-tempest-ovsfw/0000775000175000017500000000000013553660157023105 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/neutron-tempest-ovsfw/run.yaml0000664000175000017500000000307013553660047024573 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-tempest-dsvm-neutron-ovsfw from old job gate-tempest-dsvm-neutron-ovsfw-ubuntu-xenial-nv tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=1 export DEVSTACK_GATE_TEMPEST_FULL=1 export DEVSTACK_GATE_NEUTRON=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi function gate_hook { bash -xe $BASE/new/neutron/neutron/tests/contrib/gate_hook.sh full-ovsfw } export -f gate_hook cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' neutron-12.1.1/playbooks/legacy/neutron-tempest-ovsfw/post.yaml0000664000175000017500000000063313553660047024756 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-12.1.1/playbooks/legacy/neutron-tempest-dvr/0000775000175000017500000000000013553660157022534 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/neutron-tempest-dvr/run.yaml0000664000175000017500000000274613553660047024233 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-tempest-dsvm-neutron-dvr from old job gate-tempest-dsvm-neutron-dvr-ubuntu-xenial tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=1 export DEVSTACK_GATE_NEUTRON=1 export DEVSTACK_GATE_TEMPEST_FULL=1 export DEVSTACK_GATE_NEUTRON_DVR=1 export DEVSTACK_GATE_TLSPROXY=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' neutron-12.1.1/playbooks/legacy/neutron-tempest-dvr/post.yaml0000664000175000017500000000063313553660047024405 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-12.1.1/playbooks/legacy/neutron-functional/0000775000175000017500000000000013553660157022424 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/neutron-functional/run.yaml0000664000175000017500000000343313553660047024115 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-neutron-dsvm-functional from old job gate-neutron-dsvm-functional-ubuntu-xenial-nv tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=0 export DEVSTACK_GATE_EXERCISES=0 export DEVSTACK_GATE_NEUTRON=1 export DEVSTACK_GATE_INSTALL_TESTONLY=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi function gate_hook { bash -xe $BASE/new/neutron/neutron/tests/contrib/gate_hook.sh dsvm-functional } export -f gate_hook function post_test_hook { bash -xe $BASE/new/neutron/neutron/tests/contrib/post_test_hook.sh dsvm-functional } export -f post_test_hook cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' neutron-12.1.1/playbooks/legacy/neutron-functional/post.yaml0000664000175000017500000000455113553660047024300 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*nose_results.html - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testr_results.html.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.testrepository/tmp* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testrepository.subunit.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}/tox' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.tox/*/log/* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-12.1.1/playbooks/legacy/neutron-fullstack/0000775000175000017500000000000013553660157022252 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/neutron-fullstack/run.yaml0000664000175000017500000000342413553660047023743 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-neutron-dsvm-fullstack from old job gate-neutron-dsvm-fullstack-ubuntu-xenial tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=0 export DEVSTACK_GATE_EXERCISES=0 export DEVSTACK_GATE_NEUTRON=1 export DEVSTACK_GATE_INSTALL_TESTONLY=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi function gate_hook { bash -xe $BASE/new/neutron/neutron/tests/contrib/gate_hook.sh dsvm-fullstack } export -f gate_hook function post_test_hook { bash -xe $BASE/new/neutron/neutron/tests/contrib/post_test_hook.sh dsvm-fullstack } export -f post_test_hook cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' neutron-12.1.1/playbooks/legacy/neutron-fullstack/post.yaml0000664000175000017500000000455113553660047024126 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*nose_results.html - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testr_results.html.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.testrepository/tmp* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testrepository.subunit.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}/tox' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.tox/*/log/* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-12.1.1/playbooks/legacy/neutron-tempest-multinode-full/0000775000175000017500000000000013553660157024701 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/neutron-tempest-multinode-full/run.yaml0000664000175000017500000000402713553660047026372 0ustar zuulzuul00000000000000- hosts: primary name: Autoconverted job legacy-tempest-dsvm-neutron-multinode-full from old job gate-tempest-dsvm-neutron-multinode-full-ubuntu-xenial-nv tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x cat << 'EOF' >>"/tmp/dg-local.conf" [[local|localrc]] NOVA_VNC_ENABLED=true VNCSERVER_LISTEN=0.0.0.0 VNCSERVER_PROXYCLIENT_ADDRESS=$HOST_IP EOF executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=1 export DEVSTACK_GATE_NEUTRON=1 export DEVSTACK_GATE_CONFIGDRIVE=0 export DEVSTACK_GATE_TEMPEST_FULL=1 export DEVSTACK_GATE_TLSPROXY=1 # Default to non DVR export DEVSTACK_GATE_NEUTRON_DVR=0 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi export DEVSTACK_GATE_TOPOLOGY="multinode" cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' neutron-12.1.1/playbooks/legacy/neutron-tempest-multinode-full/post.yaml0000664000175000017500000000063313553660047026552 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-12.1.1/playbooks/legacy/neutron-grenade-multinode/0000775000175000017500000000000013553660157023665 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/neutron-grenade-multinode/run.yaml0000664000175000017500000000313413553660047025354 0ustar zuulzuul00000000000000- hosts: primary name: Autoconverted job legacy-grenade-dsvm-neutron-multinode from old job gate-grenade-dsvm-neutron-multinode-ubuntu-xenial tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_GATE_NEUTRON=1 export DEVSTACK_GATE_CONFIGDRIVE=0 export DEVSTACK_GATE_GRENADE=pullup export PROJECTS="openstack/grenade $PROJECTS" # Default to non DVR export DEVSTACK_GATE_NEUTRON_DVR=0 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi export DEVSTACK_GATE_TOPOLOGY="multinode" cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' neutron-12.1.1/playbooks/legacy/neutron-grenade-multinode/post.yaml0000664000175000017500000000063313553660046025535 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-12.1.1/playbooks/legacy/neutron-grenade/0000775000175000017500000000000013553660157021667 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/neutron-grenade/run.yaml0000664000175000017500000000351313553660046023356 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-grenade-dsvm-neutron from old job gate-grenade-dsvm-neutron-ubuntu-xenial tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x cat << 'EOF' >>"/tmp/dg-local.conf" [[local|localrc]] # NOTE(mriedem): cells v2 should be the default after newton-eol NOVA_CONFIGURE_CELLSV2=True EOF executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PROJECTS="openstack/grenade $PROJECTS" export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=1 export DEVSTACK_GATE_GRENADE=pullup export DEVSTACK_GATE_NEUTRON=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' neutron-12.1.1/playbooks/legacy/neutron-grenade/post.yaml0000664000175000017500000000063313553660046023537 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-12.1.1/playbooks/legacy/neutron-tempest-linuxbridge/0000775000175000017500000000000013553660157024255 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/neutron-tempest-linuxbridge/run.yaml0000664000175000017500000000344513553660047025751 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-tempest-dsvm-neutron-linuxbridge from old job gate-tempest-dsvm-neutron-linuxbridge-ubuntu-xenial tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x cat << 'EOF' >>"/tmp/dg-local.conf" [[local|localrc]] Q_AGENT=linuxbridge PHYSICAL_NETWORK=default EOF executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=1 export DEVSTACK_GATE_TEMPEST_FULL=1 export DEVSTACK_GATE_NEUTRON=1 export DEVSTACK_GATE_TLSPROXY=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' neutron-12.1.1/playbooks/legacy/neutron-tempest-linuxbridge/post.yaml0000664000175000017500000000063313553660047026126 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-12.1.1/playbooks/legacy/neutron-rally-neutron/0000775000175000017500000000000013553660157023075 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/neutron-rally-neutron/run.yaml0000664000175000017500000002067713553660047024577 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-rally-dsvm-neutron-neutron from old job gate-rally-dsvm-neutron-neutron-ubuntu-xenial tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x if [ $ZUUL_PROJECT == "openstack/rally" ] && [ $ZUUL_BRANCH != "master" ]; then export DEVSTACK_GATE_FEATURE_MATRIX="/opt/stack/new/rally/devstack/features.yaml" fi export PROJECTS="openstack/rally $PROJECTS" export DEVSTACK_GATE_NEUTRON=1 export DEVSTACK_GATE_HORIZON=1 export DEVSTACK_GATE_NEUTRON_EXTENSIONS=0 export DEVSTACK_GATE_IRONIC=0 export DEVSTACK_GATE_ZAQAR=0 export DEVSTACK_GATE_SENLIN=0 export DEVSTACK_GATE_WATCHER=0 export DEVSTACK_GATE_MAGNUM=0 export DEVSTACK_GATE_HEAT=0 export DEVSTACK_GATE_SWIFT=1 export DEVSTACK_GATE_TELEMETRY=0 export DEVSTACK_GATE_TEMPEST_LARGE_OPS=0 export DEVSTACK_GATE_EXERCISES=0 export DEVSTACK_GATE_PREPOPULATE_USERS=0 export DEVSTACK_GATE_USE_PYTHON3=False export USE_KEYSTONE_V2API=0 export RALLY_SCENARIO=$ZUUL_SHORT_PROJECT_NAME-neutron if [ $USE_KEYSTONE_V2API -eq 1 ]; then export IDENTITY_API_VERSION=2.0 else export IDENTITY_API_VERSION=3 fi DEVSTACK_LOCAL_CONFIG="enable_plugin rally https://opendev.org/openstack/rally" DEVSTACK_LOCAL_CONFIG+=$'\n'"CINDER_ENABLE_V1_API=True" DEVSTACK_LOCAL_CONFIG+=$'\n'"IDENTITY_API_VERSION=$IDENTITY_API_VERSION" ENABLED_SERVICES=key,horizon, ENABLED_SERVICES+=cinder,c-api,c-vol,c-sch,c-bak, ENABLED_SERVICES+=g-api,g-reg, ENABLED_SERVICES+=n-api,n-crt,n-cpu,n-sch,n-cond, ENABLED_SERVICES+=neutron-qos, if [ $DEVSTACK_GATE_SWIFT -eq 1 ]; then ENABLED_SERVICES+=s-proxy,s-account,s-container,s-object, else export DEVSTACK_LOCAL_CONFIG+=$'\n'"disable_service s-account" export DEVSTACK_LOCAL_CONFIG+=$'\n'"disable_service s-container" export DEVSTACK_LOCAL_CONFIG+=$'\n'"disable_service s-object" export DEVSTACK_LOCAL_CONFIG+=$'\n'"disable_service s-proxy" fi if [ $DEVSTACK_GATE_HEAT -ne 0 ]; then export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin heat https://opendev.org/openstack/heat" fi export PROJECTS="openstack/neutron $PROJECTS" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron https://opendev.org/openstack/neutron" if [ $DEVSTACK_GATE_NEUTRON_EXTENSIONS -ne 0 ]; then export PROJECTS="openstack/neutron-lbaas $PROJECTS" export PROJECTS="openstack/octavia $PROJECTS" export PROJECTS="openstack/neutron-fwaas $PROJECTS" export PROJECTS="openstack/diskimage-builder $PROJECTS" export PROJECTS="openstack/tripleo-image-elements $PROJECTS" export PROJECTS="openstack/neutron-vpnaas $PROJECTS" export PROJECTS="openstack/networking-bgpvpn $PROJECTS" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron-lbaas https://opendev.org/openstack/neutron-lbaas" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin octavia https://opendev.org/openstack/octavia" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron-vpnaas https://opendev.org/openstack/neutron-vpnaas" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin networking-bgpvpn https://opendev.org/openstack/networking-bgpvpn.git" export ENABLED_SERVICES+=q-lbaasv2,octavia,o-cw,o-hk,o-hm,o-api,q-fwaas,q-svc,q-agt,q-dhcp,q-l3,q-meta, fi if [ $DEVSTACK_GATE_IRONIC -ne 0 ]; then export PROJECTS="openstack/ironic $PROJECTS" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin ironic https://opendev.org/openstack/ironic" fi if [ $DEVSTACK_GATE_ZAQAR -ne 0 ]; then export PROJECTS="openstack/python-zaqarclient $PROJECTS" export PROJECTS="openstack/zaqar-ui $PROJECTS" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin zaqar https://opendev.org/openstack/zaqar" fi if [ $DEVSTACK_GATE_SENLIN -ne 0 ]; then export PROJECTS="openstack/senlin $PROJECTS" export PROJECTS="openstack/python-senlinclient $PROJECTS" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin senlin https://opendev.org/openstack/senlin" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_service sl-api sl-eng" fi if [ $DEVSTACK_GATE_WATCHER -ne 0 ]; then export PROJECTS="openstack/watcher $PROJECTS" export PROJECTS="openstack/python-watcherclient $PROJECTS" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin watcher https://opendev.org/openstack/watcher" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_service watcher-api watcher-decision-engine watcher-applier" fi if [ $DEVSTACK_GATE_MAGNUM -ne 0 ]||[ $RALLY_SCENARIO = "magnum" ]; then export PROJECTS="openstack/magnum $PROJECTS" export PROJECTS="openstack/python-magnumclient $PROJECTS" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin magnum https://opendev.org/openstack/magnum" fi if [ $DEVSTACK_GATE_TELEMETRY -ne 0 ]; then export PROJECTS="openstack/panko $PROJECTS" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin panko https://opendev.org/openstack/panko" export ENABLED_SERVICES+=panko-api, export PROJECTS="openstack/ceilometer $PROJECTS" export PROJECTS="openstack/aodh $PROJECTS" export PROJECTS="openstack/gnocchi $PROJECTS" export CEILOMETER_NOTIFICATION_TOPICS=notifications,profiler export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin ceilometer https://opendev.org/openstack/ceilometer" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin aodh https://opendev.org/openstack/aodh" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin gnocchi https://opendev.org/openstack/gnocchi" export ENABLED_SERVICES+=ceilometer-acompute,ceilometer-acentral,ceilometer-api, export ENABLED_SERVICES+=ceilometer-anotification,ceilometer-collector, export ENABLED_SERVICES+=aodh-api,aodh-evaluator,aodh-notifier, fi export ENABLED_SERVICES export DEVSTACK_LOCAL_CONFIG if [[ "$ZUUL_PROJECT" = "openstack/neutron" ]]; then function gate_hook { bash -xe $BASE/new/neutron/neutron/tests/contrib/gate_hook.sh rally $ZUUL_SHORT_PROJECT_NAME-neutron } export -f gate_hook fi function post_test_hook { $BASE/new/rally/tests/ci/rally-gate.sh } export -f post_test_hook if [[ "$DEVSTACK_GATE_USE_PYTHON3" = "True" ]]; then # Switch off glance->swift communication as swift fails under py3.x function pre_test_hook { local localconf=$BASE/new/devstack/local.conf echo "[[post-config|\$GLANCE_API_CONF]]" >> $localconf echo "[glance_store]" >> $localconf echo "default_store=file" >> $localconf } export -f pre_test_hook fi cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' neutron-12.1.1/playbooks/legacy/neutron-rally-neutron/post.yaml0000664000175000017500000000226713553660047024753 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/rally-plot/** - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/rally-plot/extra/index.html - --include=*/ - --exclude=* - --prune-empty-dirs neutron-12.1.1/playbooks/legacy/neutron-grenade-dvr-multinode/0000775000175000017500000000000013553660157024456 5ustar zuulzuul00000000000000neutron-12.1.1/playbooks/legacy/neutron-grenade-dvr-multinode/run.yaml0000664000175000017500000000316213553660047026146 0ustar zuulzuul00000000000000- hosts: primary name: Autoconverted job legacy-grenade-dsvm-neutron-dvr-multinode from old job gate-grenade-dsvm-neutron-dvr-multinode-ubuntu-xenial-nv tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_GATE_NEUTRON=1 export DEVSTACK_GATE_CONFIGDRIVE=0 export DEVSTACK_GATE_GRENADE=pullup # Test DVR upgrade on multinode export PROJECTS="openstack/grenade $PROJECTS" export DEVSTACK_GATE_NEUTRON_DVR=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi export DEVSTACK_GATE_TOPOLOGY="multinode" cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' neutron-12.1.1/playbooks/legacy/neutron-grenade-dvr-multinode/post.yaml0000664000175000017500000000063313553660046026326 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-12.1.1/setup.py0000664000175000017500000000200613553660046015030 0ustar zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools # In python < 2.7.4, a lazy loading of package `pbr` will break # setuptools if some other modules registered functions in `atexit`. # solution from: http://bugs.python.org/issue15881#msg170215 try: import multiprocessing # noqa except ImportError: pass setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) neutron-12.1.1/AUTHORS0000664000175000017500000011745513553660156014407 0ustar zuulzuul00000000000000AKamyshnikova Aaron Rosen Aaron Rosen Aaron-Zhang231 Abhishek Chanda Abhishek Raut Abhishek Raut Abhishek Talwar Abishek Subramanian Adam Gandelman Adam Harwell Adelina Tuvenie Adin Scannell Adit Sarfaty Aditya Reddy Nagaram Adolfo Duarte Adolfo Duarte Adrien Cunin Adrien Vergé Ailing Zhang Akash Gangil Akihiro MOTOKI Akihiro Motoki Akihiro Motoki Aleks Chirko Alessandro Pilotti Alessandro Pilotti Alessio Ababilov Alessio Ababilov Alex Holden Alex Oughton Alex Stafeyev AlexSTafeyev Alexander Ignatov Alexander Ignatyev Alexander Maretskiy Alexei Kornienko Alexey I. Froloff Ali Sanhaji Aliaksandr Dziarkach Alin Balutoiu Allain Legacy Allain Legacy Aman Kumar Amir Sadoughi Amit Saha Anand Shanmugam Andre Pech Andreas Jaeger Andreas Jaeger Andreas Karis Andreas Scheuring Andrew Austin Andrew Boik Andrew Boik Andrey Epifanov Andrey Kurilin Andrey Kurilin Andrey Shestakov Andy Hill Angela Smith Angus Lees Anh Tran Anindita Das Ankur Gupta Ann Ann Kamyshnikova Ann Taraday Anna Khmelnitsky Ante Karamatic Anthony Chow Anthony Veiga Anton Frolov Aparupa Aqsa Aradhana Singh Arata Notsu Arie Bregman Arjun Baindur Armando Migliaccio Armando Migliaccio Armando Migliaccio Arnaud Morin Artur Korzeniewski Arun Sriraman Arvind Somy Arvind Somya Assaf Muller Attila Czira Attila Fazekas Aviram Bar-Haim Avishay Balderman Babu Shanmugam Baodong (Robert) Li Baodong Li Baohua Yang Ben Nemec Ben Nemec Bence Romsics Benedikt Trefzer Bernard Cafarelli Bernhard M. Wiedemann Bertrand Lallau Bertrand Lallau Bhagyashri Shewale Bhuvan Arumugam Billy Olsen Bin Yu Bin Zhou Bo Chi Bo Chi Bo Wang Bob Kukura Bob Melander Boden R Bogdan Tabor Boris Pavlovic Brad Hall Brad Hall Bradley Jones Brandon Logan Brandon Palm Brant Knudson Brent Eagles Brian Bowen Brian Haley Brian Haley Brian Haley Brian Waldon Britt Houser Bruce Tan Béla Vancsics Cady_Chen Cao Xuan Hoang Carl Baldwin Carl Baldwin Carl Baldwin Carlos Goncalves Carol Bouchard Cedric Brandily Chandan Dutta Chowdhury Chandan Kumar Chang Bo Guo ChangBo Guo(gcb) Chengli XU Chengqian Liu Chirag Shahani Christian Berendt Christoph Arnold Christoph Thiel Chuck Chuck Carlino Chuck Short ChuckC Clark Boylan Claudiu Belu Clayton O'Neill Clint Byrum Corey Wright Cuong Nguyen Cyril Roelandt Cyril Roelandt Cédric Ollivier Dan Florea Dan Prince Dan Wendlandt Dane LeBlanc Daniel Alvarez Daniel Gollub Daniel Gonzalez Daniel Mellado Daniel Russell Darek Smigiel (dasm) Darek Smigiel Dariusz Smigiel (dasm) Dariusz Smigiel (dasm) Dariusz Smigiel Darragh O'Reilly Darragh O'Reilly Darragh O'Reilly Darragh O'Reilly Darren Birkett Davanum Srinivas Davanum Srinivas Dave Cahill Dave Hill Dave Lapsley Dave Tucker David Edery David Rabel David Ripton David Shaughnessy David Wahlstrom David-gb Dazhao Debo Deepak N Denis Buliga Derek Higgins Dermot Tynan Dhanashree Gosavi Dina Belova Dipa Thakkar Dirk Mueller Divya ChanneGowda Dmitrii Shcherbakov Dmitriy Ukhlov Dmitry Ratushnyy Dmitry Sutyagin Dong Jun Dongcan Ye Doug Hellmann Doug Hellmann Doug Wiegley Doug Wiegley Doug Wiegley DuYaHong Duan Jiong Duarte Nunes Dustin Lundquist Ed Bak Edan David Edgar Magana Edgar Magana Edward Hope-Morley Einst Crazy Elena Ezhova Emilien Macchi Emilien Macchi EmilienM Emma Foley Eoghan Glynn Eran Gampel Eric Brown Eric Larese Eric Windisch Erik Colnick Erik Colnick Ester Niclos Ferreras Eugene Nikanorov Evgeny Fedoruk Eyal Fang Zhen Farhad Sunavala Fawad Khaliq Federico Ressi Fei Long Wang Felipe Monteiro Felipe Reyes Flavio Percoco Francisco Souza Franck Yelles Francois Deppierraz Francois Eleouet Frank Wang Frode Nordahl Gabriel Wainer Gabriele Cerami Gal Sagie Gandharva Gary Kotton Gary Kotton Gauvain Pocentek Genadi Chereshnya Gerard Braad Ghe Rivero Gong Zhang Gordon Chung Gordon Chung Graham Hayes Guilherme Salgado Guoqiang Ding Guoshuai Li Gyorgy Szombathelyi Gábor Antal Ha Van Tu Haim Daniel Haiwei Xu Hamdy Khader Han Zhou Harald Jensas Harald Jensas Harald JensÃ¥s Hareesh Puthalath Harsh Prasad Harshada Mangesh Kakad He Jie Xu He Qing He Yongli Hemanth Ravi Henry Gessau Henry Gessau Henry Gessau HenryGessau HenryVIII Herman Ge Hidekazu Nakamura Hieu LE Hiroaki KAWAI Hirofumi Ichihara Hironori Shiina Hisaharu Ishii Hong Hui Xiao Hong Hui Xiao Hongbin Lu Hongbin Lu Hu Yupeng Huan Xie Hui HX Xiang Hui Xiang Hunt Xu Hynek Mlnarik IWAMOTO Toshihiro Ian Wienand Ignacio Scopetta Ihar Hrachyshka Ihar Hrachyshka Ilya Chukhnakov Ilya Pekelny Ilya Shakhat Ilya Sokolov Inessa Vasilevskaya IonuÈ› ArțăriÈ™i Irena Berezovsky Irena Berezovsky Iryoung Jeong Isaku Yamahata Isaku Yamahata Itsuro Oda Itzik Brown Itzik Brown Ivan Kolodyazhny Ivar Lazzaro Ivar Lazzaro JJ Asghar JUN JIE NAN Jacek Swiderski Jack McCann Jacky Hu Jakub Libosvar James Anziano James Arendt James E. Blair James E. Blair James E. Blair James Page Jamie Lennox Jamie Lennox Jas Jason Dillaman Jason Kölker Jason Zhang Jaume Devesa Jay Pipes Jay S. Bryant Jean-Philippe Evrard Jens Harbott Jens Rosenboom Jeremy Hanmer Jeremy McDermond Jeremy Stanley Jerry Zhao Jesse Jesse Andrews Jesse Pretorius Jiajun Liu Jian Wen Jian Wen Jianghua Wang Jianing Yang JieLee Jiri Kotlin Joe Gordon Joe Harrison Joe Heck Joe Mills Joe Talerico John Belamaric John Davidge John Davidge John Davidge John Dewey John Dunning John Jason Brzozowski John Kasperski John Nielsen John Perkins John Perkins John Schwarz Jon Grimm Jonathan LaCour Jordan Pittier Jordan Tardif Jorge Miramontes JuPing Juan Antonio Osorio Robles Juergen Brendel Julia Varlamova Juliano Martinez Juliano Martinez Julie Pichon Julien Danjou Jun Park Junjie Wang Justin Hammond Justin Lund KAWAI Hiroaki KIYOHIRO ADACHI Kahou Lei Kailun Qin Kailun Qin Kaiwei Fan Kanzhe Jiang Kawaguchi Ken'ichi Ohmichi Kenji Yasui Keshava Bharadwaj Kevin Benton Kevin Benton Kevin Benton Kevin Benton Kevin Fox Kevin L. Mitchell Kiall Mac Innes Kiseok Kim Kobi Samoray Koert van der Veer Koteswara Rao Kelam Koteswara Rao Kelam Kris Lindgren Kui Shi Kumar Acharya Kun Huang Kyle Mestery Kyle Mestery LIU Yulong LIU Yulong LIU Yulong LIU Yulong Lajos Katona Lars Kellogg-Stedman Lenny Verkhovsky Leon Cui Li Ma Li Ma Li Xipeng Li Zhixin Li-zhigang Liang Bo Lianghwa Jou Liping Mao (limao) Liping Mao LipingMao LiuNanke LiuYong Liuyuanfeng Livnat Peer Lizhixin Lorin Hochstein Louis Taylor Lu lei Lubosz Kosnik Lucas Alvares Gomes Lucian Petrut Luis A. Garcia Luiz H Ozaki Lujin Lujin Luo Luke Gorrie Luong Anh Tuan Ly Loi Maciej Józefczyk Madhav Puri Madhu Mohan Nelemane Maho Koshiya Major Hayden Maksim Malchuk Mandeep Dhami Manish Godara Manjeet Singh Bhatia Manjunath Patil Marc Koderer Marga Millet Marga Millet Margaret Frances Mark Doffman Mark McClain Mark McClain Mark McClain Mark McLoughlin Mark T. Voelker Martin Hickey Martin Kletzander Martin Matyáš Martin Roy Martin Roy Martins Jakubovics Maru Newby Maru Newby Maruti Mate Lakat Mathieu GagneÌ Mathieu Rohon Matt Dietz Matt Odden Matt Riedemann Matt Riedemann Matt Thompson Matthew Booth Matthew Edmonds Matthew Thode Matthew Treinish Matthew Treinish Matthew Weeks Maxime Guyot Meenakshi Kaushik Mehdi Abaakouk Michael J Fork Michael Johnson Michael Krotscheck Michael Smith Michael Still Miguel Angel Ajo Miguel Angel Ajo Miguel Lavalle Miguel Lavalle Miguel Lavalle Miguel Lavalle Miguel Ãngel Ajo Mike Bayer Mike Dorman Mike King Mike Kolesnik Ming Shuang Xian Mithil Arun Mitsuhiro SHIGEMATSU Mohammad Banikazemi Mohit Malik Monty Taylor Morgan Fainberg Moshe Levi Motohiro OTSUKA Mr. Bojangles Mr. Bojangles Mukul Murali Birru NGUYEN TUONG THANH Na Nachi Ueno Nachi Ueno Nader Lahouti Nakul Dahiwade Nam Nguyen Hoai Nate Johnston Nate Johnston Nate Johnston Neil Jerram Neil Jerram Ngo Quoc Cuong Nguyen Hung Phuong Nguyen Phuong An Nguyen Tuong Thanh Nguyen Van Trung Nick Nick Bartos Nikita Gerasimov Nikola Dipanov Nikola Dipanov Nikolay Fedotov Nikolay Sobolevskiy Nir Magnezi Numan Siddique Numan Siddique Oleg Bondarev OmarM Omer Anson OndÅ™ej Nový OpenStack Release Bot Pallavi.s Paul Belanger Paul Carver Paul Michali Paul Michali Paul Ward Pavel Bondar Pawel Suder Peng Xiao Peng Yong Pepijn Oomen Perry Zou Peter Feiner Petronio Carlos Bezerra Petrut Lucian Pierre RAMBAUD Pierre Rognant Piotr Siwczak Piotr Siwczak Pradeep Kilambi Praneet Bachheti Prashant Shetty Prasoon Telang Prateek Arora Praveen Kumar SM Praveen Yalagandula Preeti Mirji Prince Nana Pritesh Kothari Przemyslaw Czesnowicz Qiaowei Ren Qin Zhao Quan Tian QunyingRan Rahul Priyadarshi Raildo Mascena Rajaram Mallya Rajeev Grover Rajesh Mohan Rajesh Mohan Ralf Haferkamp Ramanjaneya Ramu Ramamurthy Ravi Kota Ravi Shekhar Jethani Rawlin Peters Rawlin Peters Ray Chen Reedip Reedip Reedip Rich Curran Richard Theis Rick Clark Ritesh Anand Ritesh Anand Robert Collins Robert Collins Robert Kukura Robert Li Robert Mizielski Robert Pothier Robin Cernin Robin Naundorf RobinWang Rodolfo Alonso Hernandez Rodolfo Alonso Hernandez Roee Agiman Roey Chen Roey Chen Rohit Agarwalla Rohit Agarwalla Roman Bogorodskiy Roman Podoliaka Roman Podolyaka Roman Prykhodchenko Roman Sokolkov Romil Gupta Ronald Bradford RongzeZhu Rosario Di Somma Rossella Sblendido Rossella Sblendido RoyKing Rudrajit Tapadar Rui Zang Russell Bryant Ryan Moats Ryan Moe Ryan O'Hara Ryan Petrello Ryan Rossiter Ryan Tidwell Ryan Tidwell Ryota MIBU Ryu Ishimoto Sachi King Sachi King Saggi Mizrahi Sahid Orentino Ferdjaoui Sahid Orentino Ferdjaoui Saisrikiran Mudigonda Saju Madhavan Saksham Varma Salvatore Salvatore Orlando Salvatore Orlando Salvatore Orlando Sam Betts Sam Hague Sam Morrison Samer Deeb Sandhya Dasu Sanjeev Rampal Santhosh Santhosh Kumar SapanaJadhav Sascha Peilicke Sascha Peilicke Sascha Peilicke SauloAislan Saurabh Chordiya Saverio Proto Sayaji Sean Dague Sean Dague Sean M. Collins Sean M. Collins Sean McCully Sean Mooney Sean Mooney Sean Redmond Sebastian Lohff Senhua Huang Serge Maskalik Sergey Belous Sergey Kolekonov Sergey Lukjanov Sergey Nechaev Sergey Skripnick Sergey Vilgelm Sergey Vilgelm Sergio Cazzolato Shane Wang Shang Yong Shaohe Feng Shashank Hegde Shashank Hegde Shashank Kumar Shankar Shih-Hao Li Shiv Haris Shivakumar M Shivakumar M Shuangtai Tian Shweta P Shweta P Shweta Patil Siming Yin Simon Pasquier Sindhu Devale Sitaram Dontu Slawek Kaplonski Slawek Kaplonski Soheil Hassas Yeganeh Somik Behera Somik Behera SongmingYan Sourabh Patwardhan Sphoorti Joglekar Sreekumar S Sridar Kandaswamy Sridhar Ramaswamy Sridhar S Sridhar Venkat Sripriya Stanislav Kudriashev Stefan Nica Stefan Nica Stephen Eilert Stephen Finucane Stephen Gordon Stephen Gran Stephen Ma Stephen Ma Steve Kipp Steven Gonzales Steven Hillman Steven Ren Sudhakar Sudhakar Babu Gariganti Sudheendra Murthy Sudipta Biswas Sukhdev Sukhdev Sukhdev Kapur Sumit Naiksatam Sumit Naiksatam Sushil Kumar Sven Anderson Swaminathan Vasudevan Swaminathan Vasudevan Swaminathan Vasudevan Swapnil Kulkarni (coolsvap) Sylvain Afchain SÅ‚awek KapÅ‚oÅ„ski SÅ‚awek KapÅ‚oÅ„ski Takaaki Suzuki Takashi NATSUME Takuma Watanabe Tan Lin Tang Chen Tatyana Leontovich Terry Wilson Thierry Carrez Thomas Bechtold Thomas Goirand Thomas Herve Thomas Morin Tim Miller Tim Rozet Tim Swanson Tom Cammann Tom Fifield Tom Holtzen Tomasz Paszkowski Tomoaki Sato Tomoe Sugihara Tomoko Inoue Tong Li Tong Liu Toni Ylenius Tony Xu Tracy Jones Trevor McCasland Trinath Somanchi TrinathSomanchi Trygve Vea Tu Hong Jun Tyler Smith Vadim ponomarev Vadivel Poonathan Van Hung Pham Vasiliy Khomenko Vasyl Saienko Victor Laza Victor Morales Victor Stinner Viktor Varga Vincent Legoll Vincent Untz Vishal Agarwal Vishvananda Ishaya Vivekanandan Narasimhan Vlad Gridin Vladimir Eremin Vladislav Belogrudov Vu Cong Tuan Waldemar Znoinski Wanlong Gao Wei Hu Wei Wang WeiHu Weidong Shao Wenxin Wang Wim De Clercq Wlodzimierz Borkowski Wu Wenxiang Xiaolin Zhang XieYingYun Xu Chen Xu Han Peng Xuhan Peng YAMAMOTO Takashi YAMAMOTO Takashi Yaguang Tang Yaguo Zhou Yalei Wang YanXingan Yang JianFeng Yang Li Yang Yu Yang Yu YangLei Yannick Thomas Yaohua Yan Yatin Kumbhare Yi Zhao Ying Liu Yong Sheng Gong Yong Sheng Gong Yoni Shafrir Yoshihiro Kaneko Youcef Laribi Yu Fukuyama Yuanchao Sun Yuji Yuriy Taraday Yushiro FURUKAWA Yusuke Ide Yusuke Muraoka Yuuichi Fujioka Yves-Gwenael Bourhis ZHU ZHU Zachary Zainub Wahid Zang MingJie Zhao Lei ZhaoBo Zhengguang Zhenguo Niu Zhenguo Niu Zhengwei Gao Zhenmei Zhesen ZhiQiang Fan ZhiQiang Fan ZhongShengping Zhongyue Luo Zuo ZongMing aaronorosen aaronzhang231 abhishek.talwar abhishek60014726 abhishekkekane adolfo duarte adreznec ajmiller alexpilotti ankitagrawal aojeagarcia armando-migliaccio armando-migliaccio berlin boden caoyue cedric.brandily changzhi changzhi1990 chen, hao chen-li chen-li chenghuiyu chenxing chnm-kulkarni da52700 david shaughnessy daz deepakmourya dekehn dineshbhor dql dukhlov durga.malleswari e0ne enriquetaso eperdomo eperdomo@cisco.com <> ericxiett fellypefca fpxie fujioka yuuichi fumihiko kakuma gaofei gaozhengwei garyduan garyk gengchc2 gessau gh159m gong yong sheng gongysh gongysh gordon chung guiyanxing hgangwx hobo.kengo houming-wang huangpengtao hujin huzhiling hyunsun imran malik ivan-zhu jasonrad jeckxie jeremy.zhang jingliuqing joe@midokura.com john_a_joyce johndavidge jufeng jun xie jun xie justin Lund karimb kedar kulkarni lawrancejing leegy leejian0612 lei zhang lianghao lijianlj lilintan linb lingyongxu lioplhp liu-sheng liuchengqian90 liudong liuqing liusheng liyingjun lizheming lizhixin3016 llg8212 luqitao lzklibj malos mamtap maoshuai marios mark mcclain mat mathieu-rohon melissaml miaoyuliang mohankumar_n mohit.mohit2atcognizant.com mohit.mohit2atcognizant.com mouad benchchaoui nanaboat ncode nfedotov nick.zhuyj niusmallnan nmagnezi openstack pawnesh.kumar qinchunhua rajat29 rajeev rajeev reedip ricolin rohitagarwalla rohitagarwalla roagarwa@cisco.com <> ronak root root root rossella rtmdk sadasu salvatore <> salvatore sanuptpm sayalilunkad shaofeng_cheng shashi.kant shenjiatong shihanzhang shihanzhang shmcfarl shu,xinxin sindhu devale sindhudevale singhannie siyingchun snaiksat sonu sonu.kumar sridhargaddam sridhargaddam stanzgy stephen-ma sukhdev sushma_korati sysnet tianquan ting.wang tonytan4ever tonytan4ever trinaths venkata anil venkata anil venkatamahesh venkatamahesh vijaychundury vikas vikram.choudhary vinkesh banka wangbo wanghongtaozz watanabe isao watanabe.isao whitekid wlfightup xchenum xiaoli xiexs yan.haifeng yangjianfeng yangxurong yanyaohua yaowei ycx yuhui_inspur yujie yuyangbj zengfagao zhangdebo1987 zhangyanxian zhangyanxian zhhuabj zhiyuan_cai zhouhenglc zhsun zhufl zoukeke@cmss.chinamobile.com Édouard Thuleau Édouard Thuleau neutron-12.1.1/.mailmap0000664000175000017500000000111613553660046014740 0ustar zuulzuul00000000000000# Format is: # # lawrancejing Jiajun Liu Zhongyue Luo Kun Huang Zhenguo Niu Isaku Yamahata Isaku Yamahata Morgan Fainberg neutron-12.1.1/rally-jobs/0000775000175000017500000000000013553660157015401 5ustar zuulzuul00000000000000neutron-12.1.1/rally-jobs/README.rst0000664000175000017500000000204113553660046017062 0ustar zuulzuul00000000000000Rally job related files ======================= This directory contains rally tasks and plugins that are run by OpenStack CI. Structure --------- * plugins - directory where you can add rally plugins. Almost everything in Rally is a plugin. Benchmark context, Benchmark scenario, SLA checks, Generic cleanup resources, .... * extra - all files from this directory will be copy pasted to gates, so you are able to use absolute paths in rally tasks. Files will be located in ~/.rally/extra/* * neutron-neutron.yaml is a task that is run in gates against OpenStack with Neutron Service deployed by DevStack Useful links ------------ * More about Rally: https://rally.readthedocs.io/en/latest/ * Rally release notes: https://rally.readthedocs.io/en/latest/project_info/release_notes/archive.html * How to add rally-gates: http://rally.readthedocs.io/en/latest/quick_start/gates.html * About plugins: https://rally.readthedocs.io/en/latest/plugins/index.html * Plugin samples: https://github.com/openstack/rally/tree/master/samples/plugins neutron-12.1.1/rally-jobs/plugins/0000775000175000017500000000000013553660157017062 5ustar zuulzuul00000000000000neutron-12.1.1/rally-jobs/plugins/README.rst0000664000175000017500000000060613553660046020550 0ustar zuulzuul00000000000000Rally plugins ============= All *.py modules from this directory will be auto-loaded by Rally and all plugins will be discoverable. There is no need of any extra configuration and there is no difference between writing them here and in rally code base. Note that it is better to push all interesting and useful benchmarks to Rally code base, this simplifies administration for Operators. neutron-12.1.1/rally-jobs/plugins/trunk_scenario.py0000664000175000017500000000511513553660047022462 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from rally import consts from rally.plugins.openstack import scenario from rally.plugins.openstack.scenarios.neutron import utils from rally.task import atomic from rally.task import validation """Scenarios for VLAN Aware VMs.""" @validation.required_services(consts.Service.NEUTRON) @validation.required_openstack(users=True) @scenario.configure(context={"cleanup@openstack": ["neutron"]}, name="NeutronTrunks.create_and_list_trunk_subports") class TrunkLifeCycle(utils.NeutronScenario): def run(self, subport_count=50): net = self._create_network({}) self._create_subnet(net, {'cidr': '10.0.0.0/8'}) ports = [self._create_port(net, {}) for i in range(subport_count)] parent, subports = ports[0], ports[1:] subport_payload = [{'port_id': p['port']['id'], 'segmentation_type': 'vlan', 'segmentation_id': seg_id} for seg_id, p in enumerate(subports, start=1)] trunk_payload = {'port_id': parent['port']['id'], 'sub_ports': subport_payload} trunk = self._create_trunk(trunk_payload) self._update_port(parent, {'device_id': 'sometrunk'}) self._list_trunks(id=trunk['trunk']['id']) self._list_ports_by_device_id("sometrunk") self._delete_trunk(trunk['trunk']['id']) @atomic.action_timer("neutron.delete_trunk") def _delete_trunk(self, trunk_id): self.clients("neutron").delete_trunk(trunk_id) @atomic.action_timer("neutron.create_trunk") def _create_trunk(self, trunk_payload): return self.clients("neutron").create_trunk({'trunk': trunk_payload}) @atomic.optional_action_timer("neutron.list_trunks") def _list_trunks(self, **kwargs): return self.clients("neutron").list_trunks(**kwargs)["trunks"] @atomic.optional_action_timer("neutron.list_ports_by_device_id") def _list_ports_by_device_id(self, device_id): return self.clients("neutron").list_ports(device_id=device_id) neutron-12.1.1/rally-jobs/plugins/__init__.py0000664000175000017500000000000013553660046021156 0ustar zuulzuul00000000000000neutron-12.1.1/rally-jobs/extra/0000775000175000017500000000000013553660157016524 5ustar zuulzuul00000000000000neutron-12.1.1/rally-jobs/extra/README.rst0000664000175000017500000000025513553660046020212 0ustar zuulzuul00000000000000Extra files =========== All files from this directory will be copy pasted to gates, so you are able to use absolute path in rally tasks. Files will be in ~/.rally/extra/* neutron-12.1.1/rally-jobs/extra/trunk_scenario.setup0000664000175000017500000000003513553660046022627 0ustar zuulzuul00000000000000enable_service neutron-trunk neutron-12.1.1/rally-jobs/neutron-neutron.yaml0000664000175000017500000002047413553660047021454 0ustar zuulzuul00000000000000--- version: 2 title: Rally Task for OpenStack Neutron CI description: > The task contains various scenarios to prevent concurrency issues subtasks: - title: Network related workloads. workloads: - description: Check performance of list_networks action and ensure > network quotas are not exceeded scenario: NeutronNetworks.create_and_list_networks: {} runner: constant: times: 100 concurrency: 20 contexts: users: tenants: 1 users_per_tenant: 1 quotas: neutron: # worst case is other 19 writers have created # resources, but quota reservation hasn't cleared # yet on any of them. This value could be 100 # without concurrency. see bug/1623390 network: 119 sla: max_avg_duration_per_atomic: neutron.list_networks: 15 # reduce as perf is fixed failure_rate: max: 0 - description: Check network update action scenario: NeutronNetworks.create_and_update_networks: network_create_args: {} network_update_args: admin_state_up: False name: "_updated" runner: constant: times: 40 concurrency: 20 contexts: users: tenants: 1 users_per_tenant: 1 quotas: neutron: network: -1 - scenario: NeutronNetworks.create_and_delete_networks: {} runner: constant: times: 40 concurrency: 20 contexts: users: tenants: 1 users_per_tenant: 1 quotas: neutron: network: -1 subnet: -1 - title: Subnet related workloads. workloads: - scenario: NeutronNetworks.create_and_list_subnets: subnets_per_network: 2 runner: constant: times: 40 concurrency: 20 contexts: users: tenants: 1 users_per_tenant: 1 quotas: neutron: subnet: -1 network: -1 - scenario: NeutronNetworks.create_and_update_subnets: network_create_args: {} subnet_create_args: {} subnet_cidr_start: "1.4.0.0/16" subnets_per_network: 2 subnet_update_args: enable_dhcp: True name: "_subnet_updated" runner: constant: times: 100 concurrency: 20 contexts: users: tenants: 1 users_per_tenant: 5 quotas: neutron: network: -1 subnet: -1 port: -1 - scenario: NeutronNetworks.create_and_delete_subnets: network_create_args: {} subnet_create_args: {} subnet_cidr_start: "1.1.0.0/30" subnets_per_network: 2 runner: constant: times: 40 concurrency: 20 contexts: users: tenants: 1 users_per_tenant: 1 quotas: neutron: network: -1 subnet: -1 - title: Routers related workloads. workloads: - scenario: NeutronNetworks.create_and_list_routers: network_create_args: subnet_create_args: subnet_cidr_start: "1.1.0.0/30" subnets_per_network: 2 router_create_args: runner: constant: times: 40 concurrency: 20 contexts: users: tenants: 1 users_per_tenant: 1 quotas: neutron: network: -1 subnet: -1 router: -1 - scenario: NeutronNetworks.create_and_update_routers: network_create_args: {} subnet_create_args: {} subnet_cidr_start: "1.1.0.0/30" subnets_per_network: 2 router_create_args: {} router_update_args: admin_state_up: False name: "_router_updated" runner: constant: times: 40 concurrency: 20 contexts: users: tenants: 1 users_per_tenant: 1 quotas: neutron: network: -1 subnet: -1 router: -1 - scenario: NeutronNetworks.create_and_delete_routers: network_create_args: {} subnet_create_args: {} subnet_cidr_start: "1.1.0.0/30" subnets_per_network: 2 router_create_args: {} runner: constant: times: 40 concurrency: 20 contexts: users: tenants: 1 users_per_tenant: 1 quotas: neutron: network: -1 subnet: -1 router: -1 - title: Ports related workloads. workloads: - description: Check performance of list ports action and ensure > network quotas are not exceeded scenario: NeutronNetworks.create_and_list_ports: network_create_args: port_create_args: ports_per_network: 50 runner: constant: times: 8 concurrency: 4 contexts: users: tenants: 1 users_per_tenant: 1 quotas: neutron: network: -1 subnet: -1 router: -1 # ((ports per net + 1 dhcp) * times) + (concurrency-1) # see bug/1623390 for concurrency explanation port: 811 sla: max_avg_duration_per_atomic: neutron.list_ports: 15 # reduce as perf is fixed failure_rate: max: 0 - scenario: NeutronNetworks.create_and_update_ports: network_create_args: {} port_create_args: {} ports_per_network: 5 port_update_args: admin_state_up: False device_id: "dummy_id" device_owner: "dummy_owner" name: "_port_updated" runner: constant: times: 40 concurrency: 20 contexts: users: tenants: 1 users_per_tenant: 1 quotas: neutron: network: -1 port: -1 - scenario: NeutronNetworks.create_and_delete_ports: network_create_args: {} port_create_args: {} ports_per_network: 5 runner: constant: times: 40 concurrency: 20 contexts: users: tenants: 1 users_per_tenant: 1 quotas: neutron: network: -1 port: -1 - title: Quotas update check scenario: Quotas.neutron_update: max_quota: 1024 runner: constant: times: 40 concurrency: 20 contexts: users: tenants: 20 users_per_tenant: 1 - title: Trunks related workload scenario: NeutronTrunks.create_and_list_trunk_subports: subport_count: 125 runner: constant: times: 4 concurrency: 4 contexts: users: tenants: 1 users_per_tenant: 1 quotas: neutron: network: -1 port: 1000 neutron-12.1.1/CONTRIBUTING.rst0000664000175000017500000000065113553660046015763 0ustar zuulzuul00000000000000If you would like to contribute to the development of OpenStack Networking, you must follow the steps documented at: https://docs.openstack.org/neutron/latest/contributor/policies/blueprints.html Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/neutron Please: do not register blueprints, they will be marked *obsolete* and ignored. neutron-12.1.1/ChangeLog0000664000175000017500000204555313553660155015111 0ustar zuulzuul00000000000000CHANGES ======= 12.1.1 ------ * raise priority of dead vlan drop * fixed\_configured=True when Add/Remove port IPs * [Unit tests] Skip TestWSGIServer with IPv6 if no IPv6 enabled * OVS flows for custom ethertypes must be on EGRESS * Avoid unnecessary operation of ovsdb and flows * Fix creation of vlan network with segmentation\_id set to 0 * Add info log about ready DHCP config for ports * Check the namespace is ready in test\_mtu\_update tests * Increase timeouts for OVSDB in functional tests * Use created subnet in port generator in "test\_port\_ip\_update\_revises" * Increase TestDhcpAgentHA.agent\_down\_time to 30 seconds * Increase number of retries in \_process\_trunk\_subport\_bindings * Remove experimental openSUSE 42.3 job * Initialize phys bridges before setup\_rpc * Populate binding levels when concurrent ops fail * Clear skb mark on encapsulating packets * Stop OVS agent before starting it again * Make sure the port still in port map when prepare\_port\_filter * [DVR] Add lock during creation of FIP agent gateway port * Fix sort issue in test\_dhcp\_agent\_scheduler.test\_filter\_bindings * Check for agent restarted after checking for DVR port * fix update port bug * Retry trunk status updates failing with StaleDataError * Don't crash ovs agent during reconfigure of phys bridges * Filter placement API endpoint by type too * Use --bind-dynamic with dnsmasq instead of --bind-interfaces * Yield control to other greenthreads while processing trusted ports * Limit max ports per rpc for dhcp\_ready\_on\_ports() * Add custom ethertype processing * Treat networks shared by RBAC in same way as shared with all tenants * Turn CIDR in query filter into proper subnet 12.1.0 ------ * DVR: on new port only send router update on port's host * Use status\_code instead of status in requests * metadata: use requests for comms with nova api * Reset MAC on unbinding direct-physical port * Optimize the code that fixes the race condition of DHCP agent * SRIOV agent: wait VFs initialization on embedded switch create * improve dvr port update under large scale deployment * Packets getting lost during SNAT with too many connections * [DVR] Block ARP to dvr router's port instead of subnet's gateway * Make OVS controller inactivity\_probe configurable * Revert "Pass network's dns\_domain to dnsmasq conf" * Use list instead of six.viewkeys to avoid py2 to py3 problems * Only store segmenthostmapping when enable segment plugin * Wait to ipv6 accept\_ra be really changed by L3 agent * Allow first address in an IPv6 subnet as valid unicast * Show all SG rules belong to SG in group's details * [DHCP] Don't resync network if same port is alredy in cache * Remove rootwrap configuration from neutron-keepalived-state-change * Use six.viewkeys instead of dict.keys to avoid py2 to py3 problems * Ensure dvr ha router gateway port binding host * Async notify neutron-server for HA states * Fix handling of network:shared field in policy module * Fix creating policy rules from subattributes * Add missing policy actions to policy.json file * Keep HA ports info for HA router during entire lifecycle * Get ports query: extract limit and use it only at the end * Use dynamic lazy mode for fetching security group rules * objects: avoid deepcopying models in test\_db\_obj * Revert iptables TCP checksum-fill code * Give some HA router case specific resources * Move subnet postcommit out of transaction for bulk create * OpenDev Migration Patch * Fix attribute mismatch in router provider update * Check master/backup router status in DVR functional tests * Set HA failover bridges veth pair devices UP * neutron.conf needs lock\_path set for router to operate This change is adding required configuration in neutron.conf to set the lock\_path parameter, which was missing in compute-install-ubuntu.rst * Do not call update\_device\_list in large sets * Choose random value for HA routes' vr\_id * DVR: Correctly change MTU in fip namespace * Handle DBConnectionError in skip\_if\_timeout decorator * Improve port dhcp Provisioning * Rename router processing queue code to be more generic * Fix regression with SG read API with empty ruleset * Change ovs version we use in fullstack tests 12.0.6 ------ * Add enforcer logic for neutron policy * Don't pass None arg to neutron-keepalived-state-change * Fix slow SG api calls when limiting fields * OVS agent: always send start flag during initial sync * Change default local ovs connection timeout * Specify tenant\_id in TestRevisionPlugin objects * Divide-and-conquer security group beasts * Try to enable dnsmasq process several times * [OVS] Exception message when retrieving bridge-id and is not present * [Functional tests] Change way how conntrack entries are checked * Remove conntrack rule when FIP is deleted * More accurate agent restart state transfer * Fix QoS rule update * Divide-and-conquer local bridge flows beasts * Fix KeyError in OVS firewall * Check if process' cmdline is "space separarated" * Replace openstack.org git:// URLs with https:// * ovs: survive errors from check\_ovs\_status * ovs: raise RuntimeError in \_get\_dp if id is None * Add rootwrap filters to kill state change monitor * [Functional] Don't assert that HA router don't have IPs configured * Improve invalid port ranges error message * Enable ipv6\_forwarding in HA router's namespace * Set initial ha router state in neutron-keepalived-state-change * Do not release DHCP lease when no client ID is set on port * When converting sg rules to iptables, do not emit dport if not supported * Spawn metadata proxy on dvr ha standby routers * DVR-HA: Unbinding a HA router from agent does not clear HA interface * DVR edge router: avoid accidental centralized floating IP remove * Add new test decorator skip\_if\_timeout * Fix notification about arp entries for dvr routers * Fix port update deferred IP allocation with host\_id + new MAC * Switch isolated metadata proxy to bind to 169.254.169.254 * Fix update of ports cache in router\_info class * Ensure dnsmasq is down before enabling it in restart method * Add lock\_path in installation guide * Change duplicate OVS bridge datapath-ids * Update neutron files for new over-indentation hacking rule (E117) * Fix performance regression adding rules to security groups * Always fill UDP checksums in DHCPv6 replies * Secure dnsmasq process against external abuse * Remove IPv6 addresses in dnsmasq leases file * Clear residual qos rules after l2-agent restarts * protect DHCP agent cache out of sync * Check port VNIC type when associating a floating IP * [DVR] Allow multiple subnets per external network * Enable 'all' IPv6 forwarding knob correctly * Don't modify global variables in unit tests * Add kill\_timeout to AsyncProcess * Do state report after setting start\_flag on OVS restart * Block port update from unbound DHCP agent * Do not delete trunk bridges if service port attached * Fix the bug about DHCP port whose network has multiple subnets * Force all fdb entries update after ovs-vswitchd restart * Reinitialize ovs firewall after ovs-vswitchd restart * Get centralized FIP only on router's snat host * Fix neutron-openvswitch-agent Windows support * Update docs for disabling DNS server announcement * DevStack: OVS: Only install kernel-\* packages when needed * Include all rootwrap filters when building wheels * DVR: Centralized FloatingIPs are not cleared after migration * Fix connection between 2 dvr routers * Wait to ipv6 forwarding be really changed by L3 agent * Add missing step for ovs deploy guides * Pass network's dns\_domain to dnsmasq conf * iptables-restore wait period cannot be zero 12.0.5 ------ * Add capabilities for privsep * DVR: Avoid address scope rules for dvr\_no\_external agents * Add permanent ARP entries for DVR fip/qrouter veth pair * [Functional] Increase test\_timeout for db migration tests * Make port binding attempt after agent is revived * Fix dvr ha router gateway goes wrong host * DVR-HA: Configure extra routes on router namespace in dvr\_snat node * [Functional] Increase test\_timeout for db migration tests * Allow Ipv6 addresses for nova\_metadata\_host * Install centralized floating IP nat rules to all ha nodes * Fix corner case in failure assigning MAC to SR-IOV NIC * doc: add known limitation about attaching SR-IOV ports * Do not install centralized FIP if HA port is down * Fetch specific columns rather than full ORM entities * dhcp: serializing port delete and network rpc calls * Don't uninstall flow tables in setup\_dvr\_flows\_on\_integ\_br * Parse dhcp leases file in a more robust way * Fix IPv6 prefix delegation issue on agent restart * ovsdb monitor: do not die on ovsdb-client stderr output 12.0.4 ------ * ovs fw: apply the NORMAL action on egress traffic in a single table * l2 pop: check for more than 1 first active port on a node * Add iptables metadata marking rule on router init * DVR: Add IPv6 csnat port address correctly * Fix lost connection when create security group log * Fix no packet log data when debug is set False in configuration * Reduce qos rpc times on the ovs-agent side * import zuul job settings from project-config * Revert "DVR: Inter Tenant Traffic between networks not possible with shared net" * Revert "DVR: Add error handling for get\_network\_info\_for\_id rpc call" * Fix iptables metering driver entrypoint * Trivial: avoid KeyError while processing ports * Remove fdb entries for ha router interfaces when going DOWN * get\_subnet\_for\_dvr returns SNAT mac instead of distributed gateway in subnet\_info * Use system protocol assigments for iptables protocol map * cap bandit in test-requirements.txt * Update metering driver to load interface driver * Drop strict-order flag from dnsmasq invocation * Config privsep in the metering agent * Fix no ACCEPT event can get for security group logging * Make dvr router port creation retriable * Update install doc for ubuntu * [QoS] Clear rate limits when default null values are used * Use oslo\_db.sqlalchemy.test\_fixtures * Fix DHCP isolated subnets with routed networks * Disallow router interface out of subnet IP range * Skip MTU check during deletion of Networks * Fix fwaas v1 configuration doc * Add list of all working DSCP marks * Reduce IP address collision during port creating * use callback payloads for PRECOMMIT\_UPDATE events * [Fullstack] HA L3 agent restart only standby agents * Fix UT BridgeLibTest when IPv6 is disabled * Make L3 IP tc filter rate limit more accurate * Mark unit test test\_get\_objects\_queries\_constant as unstable * DVR: Self recover from the loss of 'fg' ports in FIP Namespace * DVR: FIP gateway port is tagged as DEAD port by OVS with external-bridge * Fix an attribute error on listing AZs with filters * Configure privsep helper in neutron-netns-cleanup * Use rally 0.12.1 release for stable/queens branch * Dropping radvd process privileges * Mark test\_ha\_router\_restart\_agents\_no\_packet\_lost as unstable * Revert "Ensure \_get\_changed\_synthetic\_fields() return updatable fields" * DVR: FloatingIP create throws an error if no l3 agent * [OVS] Add mac-table-size to be set on each ovs bridge * Windows: fix exec calls * Don't set administratively disabled ports as ACTIVE * Add/rm centralized fips for DVR+HA from standby node * [Fullstack] Wait for SG to be applied by L2 agent * ovs: removed sinkhole RPC target for old topics * Revert "DVR: Fix allowed\_address\_pair IP, ARP table update by neutron agent" * Fix DVR scheduling checks wrong profile for host * Use neutron-tempest-plugin-jobs-queens job template * [Fullstack] Add block test until IPs will be really configured * Ensure \_get\_changed\_synthetic\_fields() return updatable fields 12.0.3 ------ * [Fullstack] Change time waiting for async ping results * Don't skip DVR port while neutron-openvswitch-agent is restared * Disable IPv6 forwarding by default on HA routers * Fix lack of routes for neighbour IPv4 subnets * Change ovs release used to compile kernel module for tests node * tests: port test\_port\_presence\_prevents\_network\_rbac\_policy\_deletion * Fix wrong release names in docs * Use Param DHCP\_OPT\_CLIENT\_ID\_NUM * Fix delete vlan allocation error for ML2 * Adds egress and ingress forward rules for trusted ports * Avoid agents adding ports as trunk by default * Monitor phys\_bridges to reconfigured it if created again * Only allow SG port ranges for whitelisted protocols * [L3][QoS] Cover mixed dvr\_snat and compute node dvr router * Stop using legacy nodesets * Retry dhcp\_release on failures 12.0.2 ------ * ovs-fw: Apply openflow rules immediately during update * [RBAC] Fix setting network as not shared * Override ovsdb\_timeout default value in ovs\_cleanup tool * Handle AgentNotFoundByTypeHost exception properly * Fix pep8 errors * DVR: Restarting l3 agent loses centralized fip ip on qg-interface * Refresh router objects after port binding * Use cidr during tenant network rule deletion * DVR: Check for item\_allocator key before releasing * DVR: Add error handling for get\_network\_info\_for\_id rpc call * Fix l3-agent crash on routers without ha\_state 12.0.1 ------ * ovs-fw: Fix firewall blink * Add in missing QUEENS tag * Set trusted port only once in iptables firewall driver * Spawn/stop metadata proxies upon adding/deleting router interfaces * Remove race and simplify conntrack state management * Process conntrack updates in worker threads * Iptables firewall driver adds forward rules for trusted ports * Try to enable L3 agent extension \`fip\_qos\` * DVR: Fix allowed\_address\_pair IP, ARP table update by neutron agent * ovs-fw: Clear conntrack information before egress pipeline * Pull in ubuntu cloud archive openvswitch pkg * [Scenario tests] Try longer SSH timeout for ubuntu image * DVR: Inter Tenant Traffic between networks not possible with shared net * DVR: Fix mac format for backward compatibility with vsctl api * Imported Translations from Zanata * Config privsep in the linuxbridge agent * ovsfw: Use bundle when adding security group rules * ovs-fw: Don't modify passed rules to update * Fix creation of port when network has admin's QoS policy set * DVR: verify subnet has gateway\_ip before installing IPv4 flow * [Fullstack] Respawn dhclient process in case of error * Update documentation for DNS integration * Always pass device\_owner to \_ipam\_get\_subnets() * Fixing the filter in get MTU by network list query * DVR: Fix dvr\_no\_external agent restart with fips * Fix error message when duplicate QoS rule is created 12.0.0 ------ * Compile ovs for ovsfw to avoid ovs-vswitchd core dumps * Tag the alembic migration revisions for Queens * ovsfw: Update SG rules even if OVSFW Port is not found * Update UPPER\_CONSTRAINTS\_FILE for stable/queens * Update .gitreview for stable/queens 12.0.0.0rc1 ----------- * Fixes using SSL OVSDB connection * Allow objects to opt in new engine facade * [Fullstack] Mark security group test as unstable * Mock ipv6\_utils.is\_enabled\_and\_bind\_by\_default method * Add notification for floatingip update/delete * Revert "[Fullstack] Mark test\_bw\_limit\_qos\_port\_removed as unstable" * fix same mechanism driver called twice bug * Updated from global requirements * Zuul: Remove project name * Move Linuxbridge ARP spoofing to nat table PREROUTING chain * This patch changes the CT zone allocation range * Revert "Integration of (Distributed) Port Binding OVO" * Fix eventlet imports issue * Minor fix to remove legacy OVSDB IDL connection 12.0.0.0b3 ---------- * Fix race condition with enabling SG on many ports at once * [Fullstack] SG test use not too many fake hosts * More efficiently clean up OVS ports * Fix Port OVO filtering based on security groups * Add log-tag to haproxy config file * Modify link address from http to https * Log warnings when test fails because of them * [Fullstack] Clean DSCP mark rule when port removed * log-api: Don't enable logging for linuxbridge * Add retry decorator update\_segment\_host\_mapping() * Fixes minor typo in neutron * Remove deprecated nova\_metadata\_ip * Adding DSCP mark and inheritance in OVS and LB tunnels outer header * Update the gerrit dashboard * [Doc][L3][QoS] Enable floating IP qos * Fix \_port\_arg for security rules with icmp/ipv6-icmp aliases * l3\_ha: only pass host into update\_port when updating router port bindings * remove neutron.callbacks package * Updated from global requirements * Use Router OVO in metering\_db * Configure log extension for ovs scenario job only * Fix ingress bw limit for OVS DPDK ports * Switch to use directly ovsdbapp.api module * [OVS] Fix for cleaning after skipped\_devices * Support filtering port with IP address substring * ovs-lib: Pass string as udp port to ovsdb * Fix mocking of ovsdb connection in UT * Treat device with no MAC address as not ready * Remove usage of deprecated idlutils module * Remove deprecated method OVSBridge.set\_protocols() * [log] [doc] Include logging for security groups * Speed up trunk MTU enforcement check * Removed neutron-tempest-full * Fix fwaas v2 configuration doc * Add missing iptable rule in snat ns for centralized fips * Revert "Use writer for trunk database operations" * Switch rally job from q-\* to neutron-\* service names * broken HybridIptablesHelper function override * [Fullstack] Mark test\_bw\_limit\_qos\_port\_removed as unstable * [log]: functional test for logging api * [log] ovs fw logging implementation * Use constant 'IS\_DEFAULT' from neutron-lib * [Fullstack] Change how DSCP marking packets are tested * L3: prevent associating a FIP to a DHCP port * Cleaned up get\_session * Log radvd config contents on reload when debug is on * ovsfw: Don't create rules if updated port doesn't exist * Use same instance of iptables\_manager in L2 agent and extensions * [OVS] Shut down the port on changing the admin-state to false * Switch to Subnet OVO in ipam\_backend\_mixin \_save\_subnet method * [doc] Added update regarding URL difference based on deployment * [trivial fix]fix typos in neutron * Moving legacy check and gate jobs * test\_provisioning\_blocks: Network model to OVO * [Fullstack] Shutdown tcpdump process gracefully * Updated from global requirements * Integration of (Distributed) Port Binding OVO * use callback payloads for BEFORE\_READ events * use callback payloads for REQUEST/RESPONSE events * use callback payloads for \_SPAWN events * ipam: don't commit IPAllocation while IpamAllocation is rolled back * Update gerrit dashboards * Fix NeutronPrimaryKeyMissing instantiation * Improve Qos Policy Rule test * ovsfw: Create tables for further consumption * Introduce rfe-confirmed and rfe-triaged tags * [L3][QoS] L3 agent side Floating IP rate limit * Fix 1 doc typo * [log]: Devstack plugin for logging api * [log]: Change entry point name of logging plugin * Updated from global requirements * Switch to use \_get\_subnet\_object() in ipam\_backend\_mixin module * Ensure floating IP create does not break * FIP: update\_floatingip\_status() to return the updated object * use l3 api def from neutron-lib * l3\_agentschedulers\_db: convert from Agent model to OVO * Allow \_\_new\_\_ method to accept extra arguments * Update some l3-agent log messages * Remove \_get\_subnets\_by\_cidr from neutron/db/extraroute\_db.py * Fix URL in SR-IOV internals doc * Update the documentation links * [Fullstack] Additional log of tcpdump stderr output * Honor both floating\_ip\_address and subnet\_id when creating FIP * [Qos] ingress bandwidth limit by ovs is not accurate * Use hexadecimal when adding DSCP rules * Switch to use \_get\_subnet\_object in neutrondb\_ipam driver * Tags: harden validations * test\_dhcp\_rpc\_agent\_api: convert from Agent model to OVO * bugs.rst: Add rfe-postponed to the list of tags * Allow port create/update by shared nw owners * Update network external attribute for RBAC change * Switch to use subnet OVO object in get\_subnet * test\_metering\_plugin: convert from Agent model to OVO * Updated from global requirements * Fix error when using protocol number in security groups * doc: Clarify RFE Triaged state a bit * Prevent LBaaS VRRP ports from populating DVR router ARP table 12.0.0.0b2 ---------- * test\_timestamp: switch to Network OVO * test\_l3\_dvr\_db: switch to OVO for Agent * test\_dhcp\_agent\_scheduler: Network model to OVO * test\_l3\_agent\_scheduler: convert from RouterL3AgentBinding model to OVO * test\_l3\_agent\_scheduler: convert from Agent model to OVO * Move segment deletion back to PRECOMMIT\_DELETE * [Qos] Fix residues of ovs in ingress bw limit * Add CREATE\_PRECOMMIT notification for Floating IPs * burst should be set near max-rate * Build metadata\_agent.ini reproducibly * Remove router\_ids argument to auto\_schedule\_routers() * FakeNotifier class 'topic' argument change to 'topics' * [Fullstack] Log tcpdump output lines in DSCP mark tests * Integration of Floating IP OVO * ovs-fw: catches exception from ovsdb * Fix meter-label-rule creation * Fix missing backslashes in QoS docs * [Fullstack] Fix destroy of VM fixture * Revert "Revert "objects: get, update and delete converted to Subnet OVO usage"" * Fix HA router initialization exception * use log api plugin const from neutron-lib * Correctly configure the IPv6 LLA address * Fix DNS connectivity issues with DVR+HA routers and DHCP-HA * [log]: Add driver api and rpc stuff for logging * [L3][QoS] Neutron server side Floating IP QoS * [QoS] Add info about rate limiting on router's ports * docs: Correct various mistakes in QoS doc * fullstack: disable all test\_connectivity test cases * Disable test\_dscp\_marking\_packets fullstack test case * clarify nova prereq for controller install docs * Call update\_all\_ha\_network\_port\_statuses on agent start * fullstack: Wait at least 3 minute for agents to report * Router to OVO * Remove the ensure\_dir * Remove the bundled intree neutron tempest plugin * Remove unused variables ‘LOG' * [OVO] Switch to use own registry * Add dns-integration setup to devstack plugin * use agent api def from neutron-lib * Added zuulv3 jobs for testing neutron tempest plugin * Remove neutron tempest plugin jobs from neutron project * Skip tempest plugin tests on releasenotes changes * use flavors api def from neutron-lib * Remove Rodolfo Alonso as QoS bug contact * use l3 ext gw mode api def from neutron-lib * Remove setting of version/release from releasenotes * Updated from global requirements * use dns domain ports api def from neutron-lib * Updated from global requirements * ovsfw: Use multiple priorities in RULES\_\*\_TABLE * ovsfw: Merge multiple conjunction flows * Add unit test to validate non DB base core plugin can be loaded * use logging api def from neutron-lib * Raise exception when synthetic field invalid * [L3][QoS] Adding L3 rate limit TC lib * Support that an extension extends a sub-resource * use net mtu api writable def from neutron-lib * use net mtu api def from neutron-lib * use PROVISIONAL\_IPV6\_PD\_PREFIX from neutron-lib * Donot run neutron-tempest-plugin-api job in gate pipeline * Tempest: Add availability-zone for agent * use network az api def from neutron-lib * use addr pairs api def from lib * use l3 flavors api def from neutron-lib * use dvr api def from neutron-lib * use qos api def from neutron-lib * use router az api def from neutron-lib * Added zuulv3 jobs for testing neutron tempest plugin * Revert "objects: get, update and delete converted to Subnet OVO usage" * Renamed ovs\_vsctl\_timeout into ovsdb\_timeout * Support protocol numbers in security group API * Remove DocImpact info from contributor docs * use ml2 driver api from neutron-lib * Remove deprecated cache\_url * use l3 ext ha mode api def from neutron-lib * Skip IPv6 sysctl calls when IPv6 is disabled * Do not load default service plugins if core plugin is not DB based * use ip allocation api def from neutron-lib * tests: delete in-use security group * Change iptables-restore lock interval to 5 per second * Fix typo "extention" -> "extension" * Fix typo: allow\_address\_pair -> allowed\_address\_pair * use project id api def from neutron-lib * Remove neutron.common.ipv6\_utils.is\_enabled() * use core resource api defs from lib * Enable bridge command for openvswitch agent * Move check\_ha\_state\_for\_router() into notification code * test\_security\_groups: Randomize SG names * use pagination api def from neutron-lib * use net ip availability api def from neutron-lib * Update section with links to backport/RC potential bugs * Use Agent OVO in l3\_agentschedulers\_db * RPC callbacks: add hook to register additional resources * Update link to stable branch policy page * objects: get, update and delete converted to Subnet OVO usage * use metering api def from neutron-lib * use l2 adjacency api def from neutron-lib * Fullstack: init trunk agent's driver only when necessary * Use Agent OVO in agents\_db and test\_agents\_db * Add initialization in StringMatchingFilterObj class * Add some missing mocks in l3-agent tests * use extra route api def from lib * use FAULT\_MAP from neutron-lib * Updated from global requirements * [rally] Port input task to the new format * [Tempest] Testing remote\_ip\_prefix for security groups * Add RBAC access\_as\_external unit tests * Replace http with https for doc links * Adding OVS Offload documentation * Integration of L3HARouterAgentPortBinding in ml2/drivers/l2pop/db.py * Reorder checks in apply\_filters in db/\_model\_query * Fix callers of get\_devices\_with\_ip() to pass addresses * tests: Add decorator to mark unstable tests * docs: Update supported QoS rule types * use external net api def from lib * Fix the wrong usage of new style class in metering * Modify the wrong command in config qos doc * Correct link in config-ml2.rst * Change QoS configuration manual * revert base extension unit test param removal * Remove the unused code * use availability zone api def from lib * use qos constants from neutron-lib * tempest: Sprinkle extension checks * shim l3 exceptions with neutron-lib * Revisit the process on how RFEs are handled * cleanup unit test usage of api extension maps * Always call iptables-restore with -w if done once * Security Groups: Test all protocols names and nums * Updated from global requirements * consume load\_class\_by\_alias\_or\_classname from neutron-lib 12.0.0.0b1 ---------- * Do not create fip agent port for dvr\_no\_external node * use dns api def from neutron-lib * use default subnetpool api def from lib * Do not try and iterate [None] in l3-agent network\_update() * Redundant alias in import statement * shim FAULT\_MAP from neutron-lib * Add a new method ha\_state\_change to L3 agent extension * create\_security\_group: Expunge an object selectively * \_test\_security\_group\_precommit\_create\_event: Check the result of create * Add NULL check before passing to in\_() column operator * shim ml2 driver\_api with neutron-lib's api * trunk: Fix init\_handler() agent parameter * Only create one IPWrapper class instance in \_arping() * Stop arping when IP address gets deleted * Add some debug logs to metadata agent * use plugin constants from neutron-lib * Notify port\_update to agent for status change * Don't pass trailing dash (-) to rand\_name * Add additional tests for subnet filtering and count * consume common constants from lib * Remove release notes from reverted patch * Remove argument "watch\_log = " * Fullstack: Add l3\_agent\_mode for testing different agent types * Fullstack: add ability to specify router scheduler * iptables: don't log lock error if we haven't passed -w * Wrong path of rpc\_api.rst in class docstring * Checksum-fill proxied metadata replies * [log]: implement logging agent extension * Add a test csnat port removing in DVR migration * Fix wrong OS ENV type * ovs-fw: Remove iptables rules on hybrid ports * tempest: check router interface exists before ssh * Change OVS agent to update skipped port status to DOWN * clarify deferred fixed IP assignment for ports on routed networks * clarify agent file name in config qos doc * use new payload objects for \*\_INIT callbacks * Update get\_l3\_agents() scheduler tests * Remove ip\_lib SubProcessBase.\_execute() as class method * Switch test\_mtu.py to tempest.common.utils.requires\_ext * Allow to configure DHCP T1 and T2 timers in dnsmasq * Change ip\_lib network namespace code to use pyroute2 * DVR: Fix unbound fip port migration to bound port * DVR: Fix centralized floatingip with DVR and HA * Fullstack: add availability zone to host descriptor * use synchronized lock decorator from neutron-lib * Refactoring db config options * Update the QoS bugs contact * Refactor DVR HA migarations DB operations * Use port object in notifiers/test\_nova.py * br\_int: Make removal of DVR flows more strict * Remove dead code in L3 HA scheduler * Remove unnecessary IPWrapper() creation * Updated from global requirements * Update team and bug ownership info * Change metering code to iterate through all L3 agents * of\_native: Use int for comparing datapath ID * fullstack: Remove ovsdb\_interface config opt * Fix the link to the rally docs in README.rst * ml2: fix update\_device\_up to send lm events with linux bridge * Replace default subnetpool API tests with UT * Linux Bridge, remove unnecessary logic to retrieve bridge name * [Tempest] Scenarios for several sec groups on VM * Revert "Fix for race condition during netns creation" * Fix \_verify\_gateway\_port() in multiple subnets * DVR: handle unbound allowed\_address\_pairs added * ML2: remove method get\_locked\_port\_and\_binding * Update correct reference for tags * Remove translation of help messages from tests * Remove get\_ip\_version from ip\_lib * Remove deprecated get\_random\_mac() * Remove deprecated IpNeighCommand.show() * Updated links to dashboards * Remove security\_groups\_provider\_updated rpc code * rally: switch to new format for context name * ovs: log config options when all of them are registered * Ensure default security group before port update * gate: don't configure linuxbridge jobs for geneve * [Tempest] Check connectivity between VM with different MTU size net * add doc link validation to release checklist and tox * DVR: Always initialize floating IP host * make net\_helpers functions work on OpenSUSE * use neutron-lib address scope apidef * Deprecate ivs interface driver * Fix device\_owner during DVR and HA migrations * Remove dead versions code * Allow fullstack to operate on dhclient-script for OpenSUSE * Fix for race condition during netns creation * Dont log about skipping notification in normal case * Tweak configure\_for\_func\_testing to work on OpenSUSE * fix broken link in config-agents doc * Fix missing content of neutron database creation * Change join relationship between routerport and router * Add API tests for Tag resource with standard attribute * Don't assume RPC push object has an ID * Update config-sfc documentation * Stop using subscribe in l3\_db * Updated from global requirements * DVR: Fix bad arping call in centralized floating IP code * tests: generate unique network ids for L3HARouterVRIdAllocation * Fix documentation for DNS resolver config * Update link for API extensions * Stop using is\_agent\_down in agents\_db * Switch to tempest.common.utils.is\_extension\_enabled * Fix missing super's skip\_checks() * gate\_hook: configure range of GRE ids * Switch to tempest.common.utils.requires\_ext * Delete dead API v2 router code * Switch to Pecan for unit tests * Drop the web\_framework option * Remove run\_tests.sh * gate\_hook: Switched to $NEUTRON\_CORE\_PLUGIN\_CONF * Remove duplicated ICMPv6 RA rule from iptables firewall * Updated from global requirements * Fixing hyperlink issue * Fixing external hyperlink * Deprecate ovsdb\_interface option * Don't trigger DVR port update if status the same * Remove csnat port when DVR migrated to non-DVR * Fix post gate hook to accommodate for new os-testr * Allow OS\_TEST\_TIMEOUT to be configurable from env * ovs mech: bind only if user request switchdev * Treat lack of segment info in port object as unbound * fix missing l2pop config option docs * doc inherit segmentation type for trunking * doc for quota details extension * Cleanup unused params * Remove gw\_port expire call * Pecan: fix logic of hiding authZ failures as 404s * Pecan: add plugin pagination/sorting validation * Refactoring config options for ml2 config opts * Pecan: Add missing body validations * CountableResource: try count/get functions for all plugins * DVR: Multiple csnat ports created when RouterPort table update fails * DVR: get\_router\_cidrs not returning the centralized\_floating\_ip cidrs * OVO for NetworkDhcpAgentBinding * doc br\_netfilter prereq for linux bridge * Update config-dns-res to use openstack CLI * OVO for L3HARouter * Add a new method get\_router\_info to L3 agent extension API * Allow to disable DNS server announcement per subnet * [Tempest] Creating sec group rule with integer * [Tempest] Testing default security group scenarios * Validate security group rules for port ranges * Avoid redundant HA port creation during migration * ovsfw: Fix up port\_range and ICMP type/code handling * ovsfw: Fix port\_ranges handling * use qos DriverBase from neutron-lib * linuxbridge-agent: add missing sysctl rootwrap entry * Fix the incorrect doc for class SecurityGroupAgentRpcCallbackMixin * Fix cleaning QoS rules for not existing port * Fix generation of thousands of DHCP tap interfaces * Match load\_rc\_for\_rally logic to load\_rc\_hook * OVSBridge: use ovs-ofctl with at least OF protocol version x * Document dns\_domain for ports attribute * Fix port deletion when dns\_integration is enabled * Tempest: Fix cleaning of subnets * Pecan: add missing body to delete notify payload * DHCP provisioning block only on port addr update * Remove vestigate HUDSON\_PUBLISH\_DOCS reference * update docs to use nova\_metadata\_host * Pecan: strip duplicate and empty user fields * Pecan: Add missing emulated bulk create method * test\_ha\_router: wait until two agents are scheduled * update static urls for pike * Fix to use . to source script files * Pecan: set tenant\_id field when project\_id set * Pecan: add calls to resync/dirty quota usages * DVR: Fix agent to process only floatingips that have a host match * Pecan: process filters at end of hook pipeline * Make use of -w argument for iptables calls * l3 agent: stop expecting old server when fetching service plugins * fullstack: skip test\_mtu\_update when DHCP agent is not in rootns * complete docs for revision number * Update link for contribution * dvr: Don't raise KeyError in \_get\_floatingips\_bound\_to\_host * Revert "DVR: \_get\_floatingips\_bound\_to\_host throws KeyError" * Prioritize tox environment executables for fullstack/functional tests * update docs for stdattr tag support * Integration of Port OVO in db\_base\_plugin\_common.py * use neutron-lib's OVO exceptions * API test refactoring about create\_project * dhcp agent start md-proxy with vrouter id only when has metadata subnet * Refactoring agent linux&ovsdb config * Tempest: change way how QoS policies are cleaned * DVR: \_get\_floatingips\_bound\_to\_host throws KeyError * update contributor internals index * Tempest: Fix cleaning of networks after API tests * fix formatting in ubuntu controller install guide * Open Queens DB branch * functional: Remove ovsdb tests * Updated from global requirements * functional: Fix reference to ovsdb connection * Fix default qos policy when creating network * Fix test\_keepalived\_ipv6\_support for Keepalived v1.2.20 * Add network ip availability filtered by project\_id * add doc section for ml2 extension drivers * Treat Filter as Dict in get\_policies * Fixes input for netlink-lib functional tests * Stop using v2 identity API * Add stubs for new ovsdbapp API functions * tests: Log spawned processes by RootHelperProcess * Revert "functional: disable netlink tests" * DB migration checklist task for Pike * Fix DefaultSubnetPool API test * Make code follow log translation guideline * Stop logging full object in RPC push code * releasenotes: Move Pike ignore-notes to a proper file * Apply network MTU changes to dhcp ports * Apply network MTU changes to l3 ports * Log policy filters in one line * Stop logging versions on every agent update * Update reno for stable/pike * Add a target to Sinkhole so it's compatible 11.0.0.0rc1 ----------- * Allow unprivileged users to get their quota usage * Deprecate web\_framework option * Allow to set/modify network mtu * DVR: Provide options for DVR North/South routing centralized * Reduce rally sub-port count * Fixing test\_convert\_default\_subnetpool\_to\_non\_default * Remove 'persisted dirty' log message * fullstack: Actually run ovsfw tests * Hook bandit security linter to pep8 target * Add API test for port dns\_domain * Add unit tests for dns\_domain for ports * functional: disable netlink tests * Add documentation for Linux Bridge and OVS ingress QoS * Add port dns\_domain processing logic * Allow extension driver to provide multiple aliases * Fill device\_info with port\_security\_enabled data * DVR: Configure centralized floatingips to snat\_namespace * Don't check full subnet body in test\_filtering\_shared\_subnets * [log]: Add validator to logging api * [log]: add driver manager to LoggingPlugin * tests: apply latest release milestone alembic scripts first * doc: Fix non-existing URLs * Do not use prelude section for individual release notes * Exclude relnote from past releases from Pike relnotes * tests: don't set description in test\_blank\_update\_clears\_association * ovs-fw: Handle only known trusted ports * Remove configuration options max\_fixed\_ips\_per\_port * Sinkhole workaround for old topics * Correct tag link in README * Updated from global requirements * Remove code to debug auto address allocation error * Degrade log message for missing fanout exchange to debug * Bump network rev on RBAC change * devstack: fix ovs install on fedora like OS * docs: clarify wording about how to use advanced glance image * Enable QoS scenario tests * ovsfw: fix allowed\_address\_pairs MAC issue * Change ovsdbapp vlog to INFO * Drop port\_delete and port\_update debug msgs * Don't log 'Exit code: 0' * Check if record is stale after bulk pull * Removed unnecessary setUp calls in tests * Fix some pep8 errors under l3 unit tests * Add auto-generated config reference * Remove compat checks for psutil 1.x * docs: Fix in TESTING.rst * Add bandit target to tox * Use push notification for security groups * Log revision number in resource updated message 11.0.0.0b3 ---------- * Add segments service plug-in devref * use neutron-lib auto allocated topology apidef * Error in docs for configuring dvr router * DVR: Fix router\_update failure when agent restarts * Updated from global requirements * Catch exceptions for all rpc casts * RouterPort OVO integration * DVR: Fix binding info for DVR port not found error * Add netlink-lib to manage conntrack entries * use neutron-lib.callback.events.AFTER\_SPAWN * Fixed FlushError on subnet creation retry * Add project links to feature classification matrix * [Tempest] Running Trunk test with advanced image only * Enforce ethertype with IPv6 integer protocols * Add datapath\_type to vif\_details in OVS driver * FloatingIP to OVO * Add specific values to specific fields in get\_random\_object\_fields() * ml2 plugin: add (PORT, BEFORE\_UPDATE) callback * Tag mechanism supports resources with standard attribute * ovs-fw: Update internal docs with TRANSIENT table * Update the documentation link for doc migration * Only ensure default security group exists if necessary * Ignore duplicate record\_resource\_delete calls * Remove network\_id from network\_map on net delete * Always try to delete bridge for ID on network\_delete * Updated from global requirements * Remove deprecated prevent\_arp\_spoofing option * use APIExtensionDescriptor for plugins with defs in lib * hardware offload support for openvswitch * DistributedVirtualRouter mac address to OVO * neutron-teams.rst: Remove an orphan footnote * ovsfw: Fix overlapping MAC addresses on integration bridge * DVR: Server side patch to schedule an unbound port with Floating IP * Ignore cast exceptions in AgentNotifierApi * hacking: Remove dead code * OVS firewall: do strip\_vlan in TRANSIENT\_TABLE * neutron-teams.rst: Update the list of networking-midonet lieutenants * Log reserved cookies in cleanup\_flows method * Use context interface for constraint * import the admin guide content from openstack-manuals * Add callback BEFORE\_DELETE for delete\_router * Imported Translations from Zanata * Stop using non-ascii characters * Updated from global requirements * Extend Quota API to report usage statistics * Remove neutron-fwaas specific policies * Tempest: Fix DeprecationWarning for Read-only property * Adding option to check VM connectivity with packet of given size * Updated from global requirements * Tempest: Adopt keystone api v3 for tests * Fix typo * Add port dns\_domain to DB and object models * Add dns\_domain attribute to ports in the API * Ensure that fault map translations work correctly * Replace test.attr() with decorators.attr() * SR-IOV: remove ml2\_conf\_sriov.ini from manual * Fixed AttributeError in l2pop.delete\_port\_postcommit * tests: kill a bunch of unneeded mocks in l2pop unit tests * Introduce trusted ports to firewall driver API * [log]: implement logging plugin * ovs-fw: Use TRANSIENT table for traffic classification * TrivialFix: Remove only\_contrib argument * stop ovs that installed from git on unstack * New API call to get details of supported QoS rule type * Remove unused exceptions * import content from cli-reference in openstack-manuals * ovo\_rpc: Avoid flooding logs with semantic violation warning * RouterPort to OVO * Rearrange existing documentation to fit the new standard layout * Updated from global requirements * Do not defer allocation if fixed-ips is in the port create request * Support object string field filtering on "LIKE" statement * Add QoS policy network binding OVO * Allow to set UDP ports for VXLAN in Linuxbridge agent * Enable an update test in UT of logging * releasenote: Specify openstackdocs as html\_theme * Do not respond to ARP on IPv6-only interfaces * Tempest: Add default-subnetpools tests * Use \_is\_dns\_integration\_supported for \_delete\_floatingip * devstack ovs: get correct kernel rpm version on customed kernel * Switch from oslosphinx to openstackdocstheme * import installation guide pages from openstack-manuals * Fix alter\_enum\_add\_value * New RPC to set HA network port status to DOWN * Updated from global requirements * Use flake8-import-order plugin * Modify the execution of alter\_enum\_add\_value SQL * objects: support tenant\_id filter for get\_\* if project\_id is present * use core resource attribute constants from neutron-lib * Add "default" behaviour to QoS policies documentation * Use subqueryload in l2pop DB for binding ports * of\_interface: allow install\_instructions to accept string actions * API compare-and-swap updates based on revision\_number * Change all config options in fullstack tests to be strings * DVR: Fix neutron metering agent to notify hosts hosting DVR * Update after python-neutronclient doc rearrangement * Replace the usage of 'admin\_manager' with 'os\_admin' * Add fullstack-python35 testenv in tox.ini * import the networking guide content from openstack-manuals * use service type constants from neutron\_lib plugins * Remove 'description' attribute * Common Agent loop: Catch delete\_port extension failures * Fix list QoS rule\_types tempest API test * Add missing description of Linuxbridge egress bw limit * test\_floatingip: Add a case for SRC without FIP * Enable some off-by-default checks * Add new ovo field type class * Replaced assertTrue(False) with fail() * Fix tempest router creation * DHCP Agent: Set dhcp-range netmask property * tempest-api: Skip test if deployment has not enough agents * Ingress bandwidth limit rule in Linuxbridge agent * Correct the config group in check\_trunk\_dependencies * use attribute functions/operations from neutron-lib * Fix some tempest deprecation warnings * functional: Replace unicode() with six.u() * Integration of IPAllocation * security group: pass update value to precommit\_update * Using constants in local * Fix bug when checking duplicated subnets for router interface * Add missing info about supported ingress bandwidth limit rule * functional-tests: Make addresses for tunneling unique * Add provider info to network for update * functional: Don't write strings to pipe * python3: use binary mode to open file in test * Make HA deletion attempt on RouterNotFound race * dvr: Move normal/output br-int flows to table TRANSIENT * Fix html\_last\_updated\_fmt for Python3 * Updated from global requirements * Add support for list querying in resource cache * Add revises\_on\_change to Binding DB models * Use objects instead of SQLA deep copies in PortContext * OVO: Allow port queries based on security\_group\_ids * Clean up test cases in test\_iptables\_firewall.py * tempest: Make \_create\_router\_with\_client obey enable\_snat=False * Fix SG callbacks notification * Fix race between create subnet and port requests * use six.u rather than unicode for py3 compat * DHCP Agent: Separate local from non-local subnets * DHCP RPC: Separate local from non-local subnets * Store segmentation\_id during segment create * Stop binding attempts when network has no segments * Pass the complete info in sg/rules db into PRECOMMIT\_XXX callback * OVO: ensure decomposed plugin do not break with OVO * Linuxbridge agent: detect existing IP on bridge * docs: Fix indent level * docs: reorganize developer reference for new theme * docs: switch to openstackdocstheme * Updated from global requirements * Lazy load of resources in resource cache * DVR: Add forwarding routes based on address\_scopes * Add a missing \_LW() * functional: Add support for python35 flavor * net\_helpers: Set process streams to text mode * Trigger port status DOWN on VIF replug * Remove unreachable code in OVS mech driver * Add "default" behaviour to QoS policies documentation * Use super to make \_build\_routers\_list safer * Fix test\_dvr\_gateway\_host\_binding\_is\_set * Manually increment revision numbers in revision plugin * Decompose SG RPC API DB methods * Integration of Allocation/Endpoints OVO * python3: use a list of IPDevice objects in tests * Fix linuxbridge ebtables locking * TC doesn't rise exception if device doesn't exist * Fix usage of registry.receives in Nova notifier * replace WorkerSupportServiceMixin with neutron-lib's WorkerBase * use neutron-lib's callback fixture * Add support for ingress bandwidth limit rules in ovs agent * Don't log ipam driver on every IP allocation * Remove unnecessary debug statement from OVO push * Reduce extension logging on init * python3: Do not pass MagicMock as ConfigOpts * Retry ebtables lock acquisition failures * Move info retrieval methods below notifier * Move db methods to bottom of SG RPC class * Add a dashboard for Infra reviews * Add myself to the list of our Infra liaison * Move retry decorator to DB methods * Integrate Security Groups OVO * Add libffi-dev to bindep.txt * DVR: Fix DVR Router snat ports and gateway ports host binding issue * Stop using nested transactions in OVO get/delete * Switch to start\_all\_workers in RPC server * Don't log about no notification on GET requests * [log]: db models and migration rules * Split allowed ICMPv6 types into two constants * Mask password when logging request body * Remove unused class 'QoSPolicyDefaultNotFound' * Removed Mitaka times compatibility code from RPC callbacks * Use e.exc\_type instead of calling str on exception * Remove redundant code in QosServiceDriverManager * Do not defer allocation if fixed-ips is in the port create request * Set HA network port to DOWN when l3 agent starts * Warn the admin of a potential OVS firewall\_driver misconfiguration * Add QoS policy port binding OVO * Integration of IPAllocationPool * Stop arping when interface gets deleted * Honor the common session options for the placement API * Drop IPv6 Router Advertisements in OVS firewall * Checks if net\_dns is None and returns so that we don't attempt to load None objects * Don't iterate updated\_rule\_sg\_ids or updated\_sg\_members * tests: use devstack-gate to deploy dstat for functional/fullstack * Use rootwrap for fullstack test runner * objects: exclude revision\_number from updatable fields * Revert "Use vif\_type='tap' for LinuxBridge for consistency" * Change allowed directions for QoS min-bw rule in SR-IOV * Update pylint disable list to pass pylint 1.7.1 checks * Updated from global requirements * Fix incorrect comments in ip availability test * Add alter\_enum\_add\_value function * neutron-rpc-server fails with no plugins loaded * Add libssl packages to bindep * remove unused reraise\_as\_retryrequest * Fix html\_last\_updated\_fmt for Python3 11.0.0.0b2 ---------- * use MechanismDriver from neutron-lib + shim * Removed ovsdbapp deprecation warnings * Switch to olso.messaging:get\_rpc\_transport * Extend QoS L2 drivers interface to handle ingress rule types * Update minimum tox version to 2.3.2 * Configure root\_helper and root\_helper\_daemon in fullstack tests * l3\_ha\_mode: call bulk \_populate\_mtu\_and\_subnets\_for\_ports * Fix updating Qos policy to be default/not default * api: work around Routes cutting off suffix from resource id * Add relationship between QosPolicyDefault and QosPolicy * ovs: bubble up failures into main thread in native ofctl mode * Fix file permissions * python3: fix log index for test case messages * Switched to pyroute2.config.asyncio.asyncio\_config * Change supported vif type in Linux Bridge * use extra\_dhcp\_opt api-def from neutron-lib * Provide fallback for disabled port security extension * Fixed docs job failure * api-tests: Common way to define required extensions * Fixes import\_modules\_recursively for Windows * Revert "Change list of available qos rules" * Switch to constant for 'tap' VIF\_TYPE * Update the host\_id for network:router\_gateway interfaces * VXLAN multicast groups in linuxbridge * Add "default" behaviour to QoS policies * [OVO] Integration of RouterL3AgentBinding * Updated from global requirements * Fix security group rules created for dhcpv6 * devstack: Adapt to lib/neutron * Use push-notificates for OVSPluginAPI * Revert "DVR: Add forwarding routes based on address\_scopes" * python3: return str from read\_stdout * python3: convert range object to list before comparing with a list * Drop 'notifies\_port\_ready' check for DHCP agents * Fixed python3 failure in functional tests using net\_helpers * objects: added update\_objects to OVO framework * objects: update fields\_no\_update to reflect models * Renamed tox targets for functional with python3 * use is\_port\_trusted from neutron-lib * Add precommit calls to the QoSDriver class * objects: don't allow to update create\_at and update\_at * Updated from global requirements * Allow port security updates even without security-groups enabled * Fix functional test for ovsdbapp 0.4.0 * Update team members according to latest events * Changing create\_server to be "non class method" * DVR: Do not check HA state on DVR-only routers * Send both gratuitous ARP REQUESTs and REPLYs * Fix nullable data\_plane\_status reference in port OVO * Use vif\_type='tap' for LinuxBridge for consistency * Move get\_vif\_type hook point into mech\_agent * Send port ID in network-changed event to Nova * Allow fip associate to different tenant port if admin * Add IPv6 default route to DHCP namespace * Update to support the ovdsbapp 0.4.0 API * Add "direction" parameter to QosBandwidthLimitRule * Bulk up port status updating in ML2 RPC * Separate port status update from getting details * Updated from global requirements * Add check for Bandwidth Limit Rules * Handle PortNotFound when deleting a network * Wait 2 seconds between gratuitous ARP updates instead of 1 second * Enable segments plugin in gate * Fix tempest test failing with segments extension * use worker from neutron-lib * Notify L2pop driver from update\_device\_(up|down) * Eliminate SUBNET\_GATEWAY resource * Use SUBNET instead of SUBNET\_GATEWAY event in L3 * Add BEFORE\_UPDATE subnet event * Get orig subnet in precommit method * Move subnet event to db\_base\_plugin\_v2 * Add missing unit test for segment db * Change PATH for "ip addr list" command so it could work with cloud-user * Add tempest test for l3-ha extension * Add QoS backend/rule support table to documentation * Adds an option to skip ports on ovs\_cleanup * ovsfw: followup cleanups for the conjunction patch * Updated from global requirements * service: add callback AFTER\_SPAWN * Disable QoS scenario tests differently * test\_dhcp: Use a safer host name * devstack: Add neutron-sriov-agent alias for lib/neutron * Fix: set IPv6 forwarding when there's an IPv6 gw * Trivial fix typos while reading doc * Fix tempest router migration test when HA enabled, v2 * Revert "Fix tempest router migration test when HA enabled" * Remove deprecated eventlet TimeoutError exception * OVSBridge: add --strict to allow priority in delete\_flows * Add the parameter sub-resource-ID to the test show/update resource function * Updated from global requirements * Fix error getting segment\_id in linux DHCP driver * Change list of available qos rules * TrivialFix: Remove dead code in iptables\_firewall * tempest: Obey ssh\_timeout config option * Clean MissingAuthPlugin from unit tests * Move \_get\_marker\_obj() out of CommonDbMixin * Fix errors in PrefixDelegation.remove\_stale\_ri\_ifname * Fix tempest router migration test when HA enabled * tests: removed 'retargetable' framework * tempest: Obey identity\_feature\_enabled.api\_v2\_admin in a few tests * Utils: make delete\_port\_on\_error more informative * use neutron-lib port security api-def * DVR: Add forwarding routes based on address\_scopes * Add QoS bandwidth limit for instance ingress traffic * l3\_db: Fix a regression in the recent CommonDbMixin change * Metering to OVO * Make api\_all\_exntesions hook readable * use neutron-lib callbacks * Stop using CommonDbMixin * Update auto-addresses on MAC change * Only add "on-link" routes for L2 adjacent subnets * Refactor CommonDbMixin for removal * Use unused argument in dns extension * remove and shim callbacks * Remove unused parameter in test\_extension\_driver\_port\_security.py * Add Linuxbridge agent to no dhcp fullstack connectivity test * Creating subnet for tagged network without GW * Tempest: Edited bash commands to work with multiple OSes * Prevent regression of IP loss on MAC update * ProcessManager: honor run\_as\_root when stopping process * Add deperecated reference to impl\_idl.Transaction * [Pecan] Fix custom tenant\_id project\_id matching * Eliminate lookup of "resource extend" funcs by name * Register ovs config options before using them * Fullstack: enable DHCP agent * Monkey patch the os and thread modules on Windows * Replace subprocess.Popen with CreateProcess on Windows * Add network\_id in segment check log * Remove unused functions from devstack/lib/ovs * Use conjunction for security group rules with remote\_group\_id * Use the ovsdbapp library * fullstack: Compile openvswitch module for fullstack test * fullstack: Don't let dhcp agents failover * Use dirname in object recursive import * Use HostAddressOpt for opts that accept IP and hostnames * Stop extension warnings in UTs * Remove EXTERNAL\_NETWORK callbacks * Bulk up port context retrieval * Stop DHCP agent scheduler warnings in UTs * Fixup event transaction semantics for ML2 bulk ops * Set MTU on tap devices in Linux Bridge agent * Updated from global requirements * Revert "Update auto-addresses on MAC change" * docs: Update TESTING.rst about openvswitch requirements * Make QoS policy object compatible with versions 1.2 and higher * Correct param type description of supported\_rules of QoS driver * Eliminate mixin references in DVRResourceOperationHandler * Split out DVR DB into multiple classes * DVR: move snat and csnat functions * DVR: move delete\_floatingip\_agent\_gateway\_port * DVR: move \_get\_device\_owner and floatingip CUD * DVR: Move \_get\_floatingip\_by\_port to l3\_db * Use NETWORK callbacks in get-me-a-network code * sanity check: deprecate all version based checks * Disable new N537 hacking check from next neutron-lib * Ensure behavior of None for device\_id * Remove deprecated send\_arp\_for\_ha option * Add a skip check to make sure that vlan is actually in available\_type\_drivers * Add a new configuration variable for api links * Add devref for supporting use floatingip cross scopes * Replace six.iteritems with dict.items(Part-2) * Update auto-addresses on MAC change * Stop loading OVOServerRpcInterface in ML2 testcase * Agent-side receiver cache for ML2 OVO * Delete segments using OVO code * Don't load rels on detached objects * Add api test to create vxlan network * segments: make sure we pass lists into get\_objects * tests: allow database layer to generate id for standard attributes * Refactoring \_create\_test methods for OVO UTs * Revert "Rally: decrease SLA for avg list of ports and nets" * qos: removed silly tests validating that oslo.versionedobjects works * Remove deprecated support for QoS notification\_drivers * Rename method to better reflect what it does * ml2: Remove no longer necessary subtransaction workaround * Update operation dns notes url * Update release page url * devref: Remove resolved fullstack TODO items * Floating IP association without subnet gateway IP * Replace six.iteritems with dict.items(Part-1) * Expose neutron api application as a wsgi script * Allow to disable DVR api extension loading * Add IPAllocations to the port fixed\_ips * Integration of Router Extra Attributes OVO * Eliminate lookup of model query hooks by name * move make\_port\_dict back out of txn * Integrate NetworkSegment OVO * Add launchpad bug update support to abandon script * Implement '-F' option for 'net-ip-availability-\*' command respond * Exhaust VLAN allocations in physnet order * Check permutations of router migrations * Don't set use\_stderr to False for tests * Reduce rpc calls in SR-IOV agent * Dont try to apply iptables rules in a endless loop * Handle CIDR IP address in allowed address pairs * Allow self-sharing RBAC rules to be deleted without usage check * Remove unused variable * Use registry.receives decorator in neutron.db.l3\_db * Use 'segment' instead of 'network' in log * fix overaggressive 403->404 conversion * Updated from global requirements * ip\_lib: ignore gre and lo devices in get\_devices by default * Ignore gre devices in namespaces when cleaning up devices * Record queries for helpful failure msg in bounds test * Add string validation on security group's name * Pass MTU and AZ into network\_create\_precommit * Don't override default values for oslo.db options * tests: removed support for OS\_CHECK\_PLUGIN\_DEALLOCATION * Stop direct access to CONF.debug * Switch to neutron-lib hacking factory * Add some bulk lookup methods to ML2 for RPC handling * Allow offloading lookups in driver contexts * Don't mock registry in revision\_plugin test * Call expire\_all in revision tests before re-use * Pecan: /v2.0/ views response with resources * Deprecate watch\_log= argument for Daemon * Refactor the usage of save\_and\_reraise\_exception * docs: Update TESTING.rst with gate information about ovs * policies: Add policy for rechecking failed jobs on Gerrit * Adding missing neutron policies to policy.json * fullstack: Test vms are pingable before testing data plane * DVR: Create router to fip namespace connection based on gateway state * Port data plane status extension implementation * DVR: Don't clean snat-ns of DVR HA router when fullsync * Exit on failure to load mechanism drivers 11.0.0.0b1 ---------- * Add sanity check for conntrack * Fix some reST field lists in docstrings * New enginefacade for ports and sg groups * extraroute\_db: Remove \_get\_extra\_routes\_dict\_by\_router\_id * extraroute\_db: Clean up update\_router * Add Apache License Content in index.rst * Correct the mistake in ../conf.py * Fix some grammatical errors in TESTING.rst * DVR: properly track SNAT traffic * Ignore gre devices when fetching devices in test\_cleanup\_stale\_devices * DocFix: sriov\_nic\_agent supports qos * Optimize the link address * Assert contents of returned devices in test * Tempest: Fixing L3 agent hosting router for DVR setup * Flush objects by ourselves before processing before\_commit event * Don't check for enable\_security\_group vs. firewall\_driver compatibility * execute: don't call greenthread.sleep directly * Fixed validation of create\_and\_list\_trunk\_subports rally scenario * Fix SQL fixture to preserve engine facade settings * Quota list API returns project\_id * Log messages for keepalived-state-change in syslog * Enable keepalived debug logs when debug=True * Add sem-ver flag so pbr generates correct version * ovs-agent: Clear in\_port=ofport flow earlier * Print useful error on rootwrap daemon failure * Fix tempest router timestamp test when HA enabled * Fix TypeError in native of\_interface \_get\_dpid * hacking: disable log translations check * Disable dvr tempest tests using DISABLE\_NETWORK\_API\_EXTENSIONS * Add net precommit create callback and req to update * Inherit segmentation details for trunk subports if requested * Removing workaround for bug 1656947 * Updated from global requirements * Update QoS devref * Improve validation of supported QoS rules * deepcopy binding and binding levels avoid expiration * Move notify\_security\_groups\_member\_updated to callback * Handle auto-address subnets on port update * use neutron\_lib's portbindings api-def * Deal with port commonly when hypervisor is XenServer * Egress sg\_rules should get 'prefix' from 'dest\_ip\_prefix' * Move conntrack zones to IPTablesFirewall * Use os-xenapi for neutron when XenServer as hypervisor * TestTrackedResource: register core plugin in directory * Retrieve fresh network DB data before getting it in ml2 * Remove stale floating IP addresses from rfp devices * Use is\_loaded in manager.init * Revert "Skip DHCP agent query in provisioning block setup" * New enginefacade for networks, subnets * Fix relationship event handler for flushes and nested * Make RBAC entry removal enginefacade friendly * Fix call a method of extension class * Handle empty body in add\_router\_interface * Remove unnecessary setUp function in testcase * LOG.exception for mech dict extend failure * Add an example for update in object\_usage * Use new enginefacade for quota and provisioning blocks * Port idlutils.get\_schema\_helper from ovsdbapp to neutron * raise Exception instead of LOG.error on cookie/mask inconsistency * Verify metering label exists before applying rule * Remove a release note for reverted patch * Avoid router ri.process if initialize() fails * Throttle SIGHUPs to keepalived * ExtensionTestCase: register correct core plugin * Use router tenant for interface attach * Load all eager relationships on 'before\_commit' event * Update metering agent to use stevedore alias for driver * Fix Quota error while running tests * Clear QoS rules from ports without a qos policy * Correct file mode * Feature Classification Cleanup * Delete segments on BEFORE\_DELETE instead of PRECOMMIT * OVO for Quotas and Reservation * Remove minimum\_bandwidth\_rule from rules supported by Linuxbridge agent * Refactor OVSCookieBridge: always use bridge cookie * delete\_flows shall only touch flows with the bridge cookie * Log instance interface addrs in subport test * Skip DHCP agent query in provisioning block setup * Optimize pid property in AsyncProcess class * Replaces uuid.uuid4 with uuidutils.generate\_uuid() * Configure tempest.conf via test-config phase * Stabilizing process monitor function test case * Use oslo.context class method to construct context object * Fix some reST field lists in docstrings * Updated from global requirements * Fix Python 3 compatibility in idlutils * Bump default quotas for ports, subnets, and networks * Write vrrp\_script before (re)starting keepalived * Use BEFORE\_CREATE events for \_ensure\_default\_security\_group * Apply QoS policy on network:router\_gateway * Move NEUTRON\_\* definitions from plugin.sh into settings file * Simplify RootHelperProcess.\_read\_stream() * Switch RootHelperProcess from select.poll to select.select * Reconcile quitting\_rpc\_timeout with backoff RPC client * Use unique binding\_index for RouterL3AgentBinding * Add precommit notifications for create/update port/network in ML2 * Rally: decrease SLA for avg list of ports and nets * Revert "Stop skipping compute owner in Linux Bridge loop" * Fix copy-paste error in hacking checks * Update stadium stable dashboard * teach logger mech driver vlan transparency * Fix linuxbridge agent startup issue with IPv6 * Consume ServicePluginBase from neutron-lib * OVO creation for RouterL3AgentBinding * Disable RA and IPv6 forwarding on backup HA routers * Updated from global requirements * Pass parameters when create eventlet.wsgi server * Remove network port special-case in provisioning block * Downgrade callback abortable event log to debug * Remove redundant/stale sections from neutron-teams doc page * pecan: Make admin context if no context was created * TrivialFix: Remove extra space from log * Do not use -1 as an OpenFlow flow cookie * Stop spilling tracebacks during normal operation * Neutron Feature Classification * Don't IPAddressGenerationFailure for DHCP ports * Get rid of custom wrap\_db\_retry call in sync\_allocations * Use correct retry mechanism in tags * Fix link rendering for grafana dashboards * Agent common config * On update\_tags, clean up tags from the requested resource only * Use warn\_on\_missing\_entrypoint from stevedore 1.20.0 * use neutron\_lib's provider\_net api-def * Bump os-log-merger version in post\_test\_hook * Don't add duplicate metadata rules after router update * Render service plugins configurable * Removed Netmtu\_db\_mixin from the tree * Expose register\_objects function to register all known objects * Use @registry.receives in neutron.services.segments.plugin * Add PD support in HA router * Remove dependency on strings for kill\_process * Trim rally scenario sizes * Let setup.py compile\_catalog process all language files * Untangle WaitTimeout class from eventlet.TimeoutError * qos: Pass correctly table\_id to openflow driver * Switched gate to new neutron-\* service names * Check for None in \_get\_agent\_fdb for agent * Fix two spelling errors * Generate index file containing oslo.log messages for all tests * Remove deprecated method get\_interface\_mac * Decouple hook and func registration from CommonDbMixin * TrivialFix: Do not use synchronous\_reader argument * Change the way to distinguish the port type * Replaces yaml.load() with yaml.safe\_load() in neutron * Switch to Pecan by default * Init policy in pecan after hook as well * Set OVS inactivity\_probe to vsctl\_timeout when adding manager * Network OVO in neutron/tests/unit/plugins/ml2 * Change in-tree code to use moved get\_ip\_version() * devref: docs about how to use NeutronDbObject * Switch ns-metadata-proxy to haproxy * Don't return null-byte separated string from ExternalProcess.cmdline() * Guard against os-log-merger failure * Update is\_default field only when specified in the request * OVO for Tag * devstack: use neutron\_service\_plugin\_class\_add for service plugins * devstack: added new neutron-\* aliases for services * devstack: switch to new NEUTRON\_\* variables * Fixes crash when starting Neutron OVS Agent on Windows * Make query in quota api lockless * Lockless segmentation synchronization in ML2 type drivers * OVO External Networks * Decouple tests from default quota size option values * Fix bashate warnings * Added docstring for Pager for OVO * use neutron\_lib's get\_random\_mac * add 4094 to fix the range of available local vlans * devstack: configure ml2 extension drivers using lib/neutron * Get rid of delete\_subnet method in ML2 * Ensure ovsdb\_connection enabled before calling monitor * Remove unnecessary overrides in Subnetpool OVO * Switched rpc access policy to DefaultRPCAccessPolicy * Stop skipping compute owner in Linux Bridge loop * Spin off context module * Clean up dsvm-scenario handler for gate\_hook * Cleaned up the list of lieutenants and stadium points-of-contact * Updated from global requirements * Get rid of ML2 inheritance of delete\_network * Use Sphinx 1.5 warning-is-error * Bump to Nova V2.1 * Prepare trunk rules module to deal with the Ironic use case * Stop killing conntrack state without CT Zone * Fix trunk subport scenario test * Deprecate nova\_metadata\_ip in favor of nova\_metadata\_host option * Include metadata content for debugging * [Fix gate]Update test requirement * Use @registry.receives in neutron.services.trunk * Don't use save\_and\_reraise\_exception when we never reraise * Fix typo in test\_rules.py * Use @registry.receives in neutron.services.qos.drivers.base * Use registry.receives decorator in driver\_controller * Use registry.receives decorator in neutron.db.dvr\_mac\_db * Fix has\_registry\_receivers when super.\_\_new\_\_ is object.\_\_new\_\_ * Provide more useful repr for IPDevice * Scan for mac through all devices * Use registry.receives decorator in neutron.notifiers.nova * Stop making IP-specific provider rules in SG code * LB Trunk: Stop matching MAC of subport to port model * Switch external network create event to precommit * Use registry.receives decorator in neutron.db.db\_base\_plugin\_v2 * Updated from global requirements * Convert gate\_hook to devstack-tools * Remove unused enable\_snat attribute * Fix has\_registry\_receivers for classes w/o \_\_new\_\_ * Use registry.receives decorator in neutron.db.availability\_zone.router * Use registry.receives decorator in neutron.db.l3\_hamode\_db * Use registry.receives decorator in neutron.db.l3\_dvr\_db * Fetch trunk port MTU only on trunk validation * iptables: stop 'fixing' kernel sysctl bridge firewalling knobs * Use registry decorator in ML2 plugin * Prevent double-subscribes with registry decorator * Use registry decorator in external\_net\_db * Switch to use idempotent\_id decorator from tempest.lib * Adding a code coverage threshold * Remove a NOTE that doesn't make sense anymore * Add bashate support * Cleanup \_find\_related\_obj * tiny refine: capitalize mod\_name with string capitalize function * Mark of\_interface option deprecated * functional: Stop compiling OVS from source * Clean up deprecated sqla model import paths * gate\_hook: accept dsvm-scenario-[ovs|linuxbridge] venv name * OpenFlowSwitchMixin: do not override delete\_flows * Fix OVSBridge.delete\_flows when called with no args * Remove obsolete comment * Configure HA as False when creating a router for DVR tests * Deprecate gateway\_external\_network\_id option * gate-hook: Accomodate devstack-gate local.conf changes * tempest: Skip QoS test until fixed * Fix string case issue in rpc\_api documentation * Avoid applying noop lambdas per object in db\_api.get\_objects * Stop handling interface drivers that don't support MTU * Provide hook for networking-sfc OVS agent code * Registry decorator to bring subscribes to methods * Add some debug statements to root-cause 1666493 * Allow no network to be passed into PortContext * Avoid segment DB lookup in \_expand\_segment on port * Make ML2 OVO push notification asynchronous * Avoid loading network and all rels for subnet query * Don't log about retrying failed devices if empty * Allow no network to be passed into subnet context * Enforce port QoS policies for all ports * Pecan: Get loaded by paste deploy * Macvtap: Check for no original port in is\_live\_migration * Skip native DHCP notifications on status change * Move dhcp\_release6\_supported to runtime checks file * Disable process monitor for keepalived test * Flavors: move dict extend out of txn * Remove deprecated methods supported by neutron\_lib * Fix typo in blueprints.rst * Remove logging import unused * Add a tempest scenario for floating-ip * Invert device\_owner to avoid filtering too much * Clean up ovsdb-native's use of verify() * Simple logging de-dup logic to RPC version\_manager * fullstack: Fix race between updating port security and test * Adding cleanup of floating ips * For typo in sqlalchemytypes * Updated from global requirements * Only log IDs of callbacks in debug message * Add helper for driver to check if it owns router * Enable dstat for in-gate functional and fullstack test runs * [TrivialFix]Remove the file encoding which is unnecessary * Don't configure neutron.conf as part of functional/fullstack gate * Remove dependency on memory heavy python-requests * Pass --concurrent flag to ebtables calls * Revert "Linux Bridge: driver support for QoS egress minimum bandwidth" * Switch to use test\_utils.call\_until\_true * Fix typo in .pylintrc file * Update devref for enhanced tag mechanism * Use ipam AllocationOnAutoAddressSubnet class * Simplify the QoS bandwidth test to increase reliability * Raise InvalidInput exception on provider-net creation without seg\_id * Fix error in openvswitch firewall doc * Fix duplicated sg rules check for remote\_ip\_prefix * Typo fix: underlaying => underlying * Skip segment checks during network delete operations * Open Pike DB migration branch * Tag the alembic migration revisions for Ocata * Deprecate get\_locked\_port\_and\_binding * DVR: Look at all SNAT ports for a subnet match * Don't delete\_port on binding deadlock * Simplify notify transaction tests * Remove ORM relationship between ports and networks * Switch to 'subquery' for 1-M relationships * Add DBError to \_is\_nested\_instance * Enhance tag mechanism * Update reno for stable/ocata 10.0.0.0rc1 ----------- * Use same session object for ml2 dict extend functions * Skip reading config files from neutron.conf.d for the namespace proxy * Turn nova notifier into a proper rate limiter * Add Loki service plugin for optional DB havoc * Use bytes for python3 friendly os.write * Updated from global requirements * Use addCleanup instead of tearDown * Remove --omit argument in run\_tests.sh * Integration of Provisioning Block OVO * Pecan: Fix tags handling for pecan * Move ovsdb\_nested transaction to ovs\_lib * Add support for Keepalived VRRP health check * Ensure that subnet update does not creat RPC client * Revert "Don't disable Nagle algorithm in HttpProtocol" * Increase coverage in agent/l3/dvr\_local\_router.py * Use socket.AF\_INET6 in ip\_lib tests * tempest: Pin Ubuntu image to one version * Update bug triaging policy for documentation * Remove duplicated and unused code in iptables\_firewall * Terminate macvtap agent when physical\_interface\_mapping config not present * Add IPv6 Prefix Delegation support for DVR * Fix port update for service subnets * Update in-tree code to use new neighbour functions * tempest: Log server console output of failed SSH * functional: Check for processes only if there are any * Fix link in QoS devref * Clear conntrack entries without zones if CT zones are not used * Fix iptables rules for Prefix Delegated subnets * policies: Replace Joe's graphite graphs with Grafana * Init privsep on l3 agent start * Display deprecation warning in test only once * Kill the metadata proxy process unconditionally * Always acquire network.id lock in dhcp port update * Fix WaitTimeout string representations * Remove baremetal notification from nova notifier * Addressing L3 HA keepalived failures in functional tests * Stop passing datapath as 'may\_exist' argument * DVR: Add static routes to FIP namespace * Refactor L3 scheduler (unify code paths) * Retry on routerport delete race * Do not raise an error deleting neighbour entry * Clean up inconsistent syntax * [Live Migration] Extend ml2\_port\_binding table * Fix some pylint errors in IPAM tests * Use weakrefs for common\_db\_mixin callbacks * Add bulk pull OVO interface * Handle attempt to enable br\_netfilter in namespace 10.0.0.0b3 ---------- * Check arg type for SegmentTypeDriver functions * Add missing port UPDATE event to ML2 * Fixes to allow OVO deserializion of ports/networks * Add missing module-level SUBNET ref * ovsfw: Refresh OFPort when necessary * Fix a bad docstring in provisioning blocks module * Transition qos notification driver into qos driver * OVS: merge the required OpenFlow version rather than replace * Fix typo in test\_l2\_lb\_agent.py * Adjust psutil usage for psutil > 2 * Connectivity tests for OVS agent failures/restarts * Allow all migration of routers * Set access\_policy for messaging's dispatcher * Elminate join for network owner filter * Always add unique sort keys when sorting * Allow the other nic to allocate VMs post PCI-PT VM creation * trunk: Add tempest test validating subport connectivity * Change neighbour commands to use pyroute2 * Routed networks IPv4 inventory in Nova GRP * Correctly set project\_name * Break circular import with use of common\_types * ovsfw: Support protocol numbers instead of just tcp and udp * Add KillFilter for python 3.5 * Register sqlalchemy events through hook for UT cleanup * Fix empty string check for python 3 * Update docstring in validate\_provider\_segment * Update docstring for base OVO * Modify docref commands to reflect OSC transition * Server-side push notifications for ML2 * Allow 0 in port range for securitygrouprule object * Updated from global requirements * OVO for Ipam(Allocation,Subnet,Pool) * Remove get\_router\_cidrs method of dvr\_edge\_ha router * Use plugin directory fixture * openvswitch agent: add OVS\_RESTARTED event * Stop using legacy facade * XenAPI: Support daemon mode for rootwrap * Fix importing old path for exceptions * Pecan: Fix policy checks for lists * Use ubuntu image for tempest scenarios from Neutron * Catch invalid subnet service\_types on input * Fullstack SG test for LinuxBridge agent * Get rid of L3 HA override of \_update\_router\_db * Neutron server was not compatible with member actions * Get rid of create\_router override in l3\_ha mixin * Add a ReST client for placement API * Add OVO for AutoAllocatedTopology * Fix typos * Fix broken Windows compatibility in ovs\_lib * Add IPLink class to Windows ip\_lib implementation * Support ovsdb-client monitor with remote connection * Protect against non-determinstic sort * Remove deprecated min\_l3\_agents\_per\_router * Include port\_security check in fullstack tests * Revert "Setup firewall filters only for required ports" * test\_l3: Enable native pagination and sort * Revert "Protect against non-determinstic sort" * Multiple tweaks for Ocata release notes * L3: Add in missing translation * Fix broken links in devref * Rename a test method * DVR: delete stale devices after router update * Linux Bridge: driver support for QoS egress minimum bandwidth * Use project\_id instead of tenant\_id in objects * Remove references to defunct Stadium docs * Use new enginefacade for dvr mac db * DHCP: enhance DHCPAgent startup procedure * Use writer for trunk database operations * Add check for ha state * DVR: fix csnat port missing after router update * Manually add pk for alembic\_version table * Only migrate ports on DVR migration * Protect against non-determinstic sort * Decompose router extra attr processing * Use unique subnetpools in SubnetPoolPrefixDbObjectTestCase * Fix netns\_cleanup interrupted on rwd I/O * Don't emit SG rule AFTER events until TX closed * Updated from global requirements * neutron-lib: use L3 constant * Use the session loader in keystoneauth1 for designate * Fix delete\_network to delete ports one by one * Cleanup unused helper function from test code * Added UT for floating ips with dns * Centralize creation of security group test obj * Suppress annoying "Could not load" stevedore warnings * adds support for vhost user reconnect * Clean up \_get\_ports\_query * Get rid of additional fixed\_ip filter join * Remove advertise\_mtu config option * Get rid of \_network\_model\_hook for external\_net * Protect against '.delete()' for quota and revisions * Don't use .delete() on a port in unit test * Stop using .delete() in external net handling code * Revert "Add ALLOCATING state to routers" * Get rid of ml2 port model hook join * Add retry to \_create\_ha\_port\_binding * Correct the msg ipv6 enable in system * Change default exception in wait\_until\_true * Checking functionality of DVR * l3scheduler: create ha\_vr\_id more robustly * Remove python 3.4 support * Get rid of l3 ha delete\_router inheritance * Allow to pass suite flavor to gate\_hook to disable dvr tests * Don't create default SG in transaction * Kill neutron-keepalived-state-change gracefully * gate\_hook: Add support for neutron-full with the ovs firewall * Add filter check for quering * Fix dir doc typo error * Flavor and Service Profile to OVO * DHCP: "reserved\_dhcp\_port" not well managed during startup * Raise AssertionError instead of eventlet.timeout.Timeout when failing * Adding debug trace to IPWrapper.get\_devices() * Use gate\_hook to enable q-trunk for rally * Update contacts and team list * Replaces uuid.uuid4 with uuidutils.generate\_uuid() * Reduce IP link show calls for SR-IOV scan loop * Add DictOfMiscValuesField in OVO for dict usage * SR-IOV: Remove physical\_device\_mappings depreciation warning * Fix a typo in iptables\_manager.py * DB: remove deprecated oslo\_db warnings * Fullstack tests for DHCP agent HA * Remove greenlet useless requirement * Fix pylint warning in test\_l3\_hamode\_db.py * Fix typo * Add more protocols to the iptables module map * Replace nc command with ncat * Refactors QosAgentDriver * Change the order of installing flows for br-int * Updated from global requirements * [TrivialFix] Fix comment typo error * Don't create HA resources until needed * Do not try and remove non-existent iptables chains * Fix ext alias in revision API test * Modify error word "procedence" to "precedence" * Updated from global requirements * [TrivialFix] Fix comment typo error * neutron-lib: use CORE from neutron lib constants * Use Port OVO in ml2/db and plugins/ml2/test\_db * gate\_hook: Add a no-op rally case * Remove Duplicate line in privsep.filters * Use DB field sizes instead of \_MAX\_LEN constants * Account for unwire failures during OVS trunk rewiring operations * Change passing session to context in segments db functions * Fix a bug in process\_spawn binding on ports * team update: john-davidge doc liasion * OVO for FlatAllocation * Fix python3 issues with devstack * iptables: don't enable arptables firewall * Pecan: Fix subresource policy check * Adopt privsep and read routing table with pyroute2 * Change passing session to context for TypeDriver * of\_interface: Revert patching ryu * Remove iptables nat and mangle rules for security group * SR-IOV: remove ml2\_conf\_sriov.ini from oslo-config-generator * Sort and Remove duplicate field types * Remove test cases that moved to tempest * Kill processes when cleaning up namespaces * Don't return content when we set HTTP 204 * Set standard\_attr\_id as property in DeclarativeObject * Add agent object in router info * Bump revision of resource on tag add/remove * Use subqueries for rbac\_entries and subnets<->network * Use assertGreater(len(x), y) instead of assertTrue(len(x) > y) * ovsfw: Raise exception if tag cannot be found in other\_config * Proposing tidwellr and njohnston as service layer go-to contacts * Propose mlavalle as neutron core and L3 second in command * Use get\_random\_string from neutron-lib * Moving pyroute and oslo.privsep deps into requirements.txt * devref: suggest bug deputies to send brief reports to ML * Ensure random object unique constraints aren't violated * Update comment about foreign keys * Document how to proceed with new platform features * Update MTU on existing devices * Added log messages while creating neutron objects * Add subprojects database migration * Fullstack test for DHCP agent * get\_random\_object\_fields() for setting object attr * Updated from global requirements * Remove external\_fixed\_ips from Plural\_mappings in l3.py * Clean-up L3 constant TODOs * Unittests improvement 10.0.0.0b2 ---------- * Restore extraroute dict after OVO change * tests: change order of assertion in \_validate/compare\_resource * Refactor ml2\_db to pass context * Propose abhiraut as neutronclient core * OVO for Allocation and Endpoint * Correctly print --limit value passed via API * Fix flake8 error in DHCPOptsTestCase class * objects: add delete\_objects public method * DVR: Fix race condition in creation of fip gateway * Bulk creation of SecurityGroups * Remove model\_base deprecations * DSCP packet marking support in Linuxbridge agent * Remove duplicated revises\_on\_change in qos db model * Fix DHCP Port Creation on Service Subnets * tempest: Fix qos extension check * Unplug external device when delete snat namespace * Revert "Squash revert to breaking changes" * Optimize trunk details extension hook * rally trunk port list * Get rid of DVR override of remove\_router\_interface * Check for unbound ports in L3 RPC handler * devref: don't suggest to manually request stable branches * Revert "lb-agent: ensure tap mtu is the same as physical device" * Fix some slow unit tests * DVR: Fix IPtables driver for metering with DVR routers * Get rid of DVR override of add\_router\_interface * Remove redundant "dvr" in test name * Pass context and resource\_type in RPC callback * Tempest tests uses only supported QoS rule types * Get rid of DVR override of \_port\_has\_ipv6\_address * Add filters support to constant queries test * Remove deprecated dhcp\_domain from dhcp\_agent.ini * SRIOV: don't block report\_state with device count * OVO for VlanAllocation * Correctly configure IPv6 addresses on upgrades * Add janitor to cleanup orphaned fip ports * Make UUIDField actually validate UUIDs * Delete related conntrack when delete vm * Add validation for security\_groups parameter in Port * Pecan: Change Quotas to use original controllers * Use Query.column\_descriptions instead of private property * Remove fallback functions in agent/rpc * Convert filters to empty dict if None in DB's get\_subnetpools() * stadium guidelines: Document stable branch creation * doc: Fix a warning * Allow keystone v3 in the designate driver * QoS: update the database before notifying the backend on delete * Expose [agent] extensions option into l3\_agent.ini * Include ovsdb\_connection option into all interface\_driver agents * Eliminate DVR inheritance of router create/update\_db * Use ExtensionDescriptor from neutron-lib * Support alembic 0.8.9 in test\_autogen\_process\_directives * Use Port OVO object in test\_l3\_ext\_gw\_mode * Refactoring config opts for ml2 plugin openvswitch * Don't compile OVS kernel modules for functional job * Update bug tag contacts due to absconsions * configure\_for\_func\_testing.sh: Remove neutron-legacy inclusion * configure\_for\_func\_testing.sh: Source lib/neutron as well * Functional tests: change assert to wait\_until\_true * revise doc: support\_extension\_aliases to supported\_extension\_aliases * Pecan: Remove get\_resources() from get\_pecan\_resources() * Fix docs headers * Update DB lieutenant to Ann Taraday * Compare port\_rule\_masking() results with different approach * Use to\_policy\_values for policy enforcement * Skip larger than /64 subnets in DHCP agent * Refactoring config options for mech\_sriov opts * Delete conntrack when remote ipset member removed * Migrate device\_owner for router's interface * Remove allow\_pagination and allow\_sorting config options * Integration of RouterRoute OVO * tests: introduce update\_obj\_fields method in base objects test class * Add notify for tag operations * gate\_hook: Remove a stale comment * Renamed all [AGENT] config sections into [agent] * Reuse plugin's update method for changing trunk status * Calculate IPv4 DHCP subnets once for metadata * Lock in DHCP agent based on network\_id * Fix OSprofiler support * Introduce objects\_exist method in neutron/objects/base.py * OVO: add SubnetServiceType object and code integration * Revert "Deprecate SR-IOV 'physical\_device\_mappings' config option" * Removed deprecated method * ml2: Add original port to context on \_bind\_port * Integrate Address Scope OVO into DB * Add missing revises\_on\_change attribute * Redirect ExtensionDescriptor to neutron-lib * Remove PLURALS * Show team and repo badges on README * ovs-agent: Close ryu app on all exceptions * Get rid of DVR inheritance of \_delete\_current\_gw\_port * Solve unexpected NoneType returned by \_get\_routers\_can\_schedule * fullstack: add some more security group tests * Update network dict with segment and mtu info in \_create\_subnet\_db * Capture NotFound Exceptions in subnet postcommit * Revert "DHCP agent: advertise SLAAC prefixes" * Get rid of floating IP bgp next\_hop query * L3: Only send notifications if l3 plugin exists * Pecan: Minor Fix in startup * Squash revert to breaking changes * Remove REVERSED\_PLURALS and get\_resource\_info() * Remove legacy oslo.messaging.notify.drivers * Fix InvalidInput exception output * Revert "Fix file permissions" * Adopt neutron-lib plugin directory * DB: use get\_unique\_keys from oslo\_db * ovsdb: don't erase existing ovsdb managers * OVO for Provisioning Block DB Model * ovs-agent: Catch exceptions in agent\_main\_wrapper * functional: Remove unused l3 config * Updated from global requirements * Improve performance of \_modify\_rules * Refactoring config options for plugin l2pop opts * Refactoring config options for plugin macvtap opts * Refactoring config options for l2 agent ext opts * Refactoring agent metadata config * Metering: sync only active routers hosted on the same host * Fix security\_groups\_provider\_updated for linuxbridge * Using a new security group in scenario tests * Fix file permissions * DB: remove deprecation warnings for BASEV2 * cors: update default configuration * Add unit test in test\_allowedaddresspairs\_db * Adjustment method execution order * functional: Pass string tag to other\_config * functional: Use VLAN tags from range <1; 4094> * Replace create\_test\_segment with NetworkSegment object * Remove unused LOG * Get post-subnet actions out of transaction * ProviderResourceAssociation to OVO * sanity check: Check that ip\_nonlocal\_bind works with namespaces * callbacks: Make the value of FLOATING\_IP match with api resource * Move SR-IOV VIF type constants to the portbindings extension * Use new enginefacade metering db * Move DVR fip agent gw port create out of transaction * Separate floating IP port creation from transaction * Use callbacks to create DVR floating GW port * Disallow specifying too long name and description for qos * Move AgentStatusCheckWorker to PeriodicWorker * Speed-up iptables\_manager remove\_chain() code * Typo on side\_effect rendering the test useless * Fix reset/start methods on AgentStatusCheckWorker * Pecan:Add functional test for QuotasController * Elimination duplication of lots of DVR tests * Have qos object increment port/network revision * Update devref examples about alembic migrations * Trivial Fix - Update code to use Pike as the code name * Stop using deprecated CORS.set\_latent() * L3 scheduler: add retry indicators for bind\_router * Install OVS from git if current version < 2.5.1 * Fix typos in standard\_attr.py & attributes.py * Clean up agent status check debt * Collect OVS Debt * Disable 'accept\_ra' in DHCP agent namespace * Replace assertEqual(None, \*) with assertIsNone in tests * Fix ML2, base db plugin update\_subnet for transactions * DVR: Fix func docstring and comments * Move sysctl out of IPDevice class 10.0.0.0b1 ---------- * Parse the output of ip route more robustly * Add L3 HA test with linux bridge * Fix "failed unplugging ha interface" error when deleting router * Add a trunk rally test * Updated from global requirements * Fix typo in release note filename * Use new enginefacade for servicetype\_db * Remove floatingip address ignores ha\_state * ovsfw: small cleanups to improve readability * Use new enginefacade for tag * Change import statement to not rename the module * Add unit tests for ip\_lib.get\_routing\_table * policy: cache extracted parent fields for OwnerCheck * Changing arping command execute to accept 1 as extra OK code * l3-ha: Send gratuitous ARP when new floating IP is added * Make OVO exception NeutronDbObjectDuplicateEntry retriable * Move OVO exceptions to neutron/objects/exceptions * neutron-lib: complete usage of helpers * Add check to address\_pair that items in list are dict * Add rally hook to neutron devstack plugin * Agent to OVO * Retire deprecations for constants, exceptions, attributes * Remove second -m protocol from iptables rules * Truncate IPDevice's name to interface max size * Change cfg.set\_defaults into cors.set\_defaults * DHCP: Add missing path for marking ports as ready * Stop using osprofiler options as if they are part of public API * Register osprofiler options before setting engine hook * Don't pass config object to send\_ip\_addr\_adv\_notif() * Deprecate SR-IOV 'physical\_device\_mappings' config option * Devref to explain nova-neutron interactions during live-migration * Remove timeutils.clear\_time\_override in neutron * Refactor OVSDB native lib to be more customizable * Deprecate run\_tests.sh * Removed deprecation warning for waiting\_until\_true * Removed deprecated tests function * Removed deprecated function import\_modules\_recursively * Switch to new hacking 0.12 * Avoid UnboundLocalErrors in \_create\_bulk\_ml2 * Introduce context in methods for Router Extra Attributes OVO usage * Pecan: Find subresource controllers by parent * Removed dispose\_pool() from db/api * Removed deprecation warning for converters and validators * Removed get\_engine() from db/api * Removed paginate\_query and sqlalchemyutils module * Removed deprecated methods for AgentSchedulers * Removed deprecated checks * Removed update\_network from plugin.common.utils * Removed network\_segment functions from ml2.db * Removed deprecated class LocalVLANMapping * Deprecate send\_arp\_for\_ha option * Change IPAM DB API to use context instead of session object * Add fullstack test for check DSCP marks outbounds * L3-HA: remove unused deprecated code * neutron-lib: use replace\_file from neutron lib * Remove unused configuration variable * Fix typo in comment * Migrate to neutron-lib released API definition for trunk APIs * Updated from global requirements * Reduce rally executions of create\_and\_list\_ports * Neutron lib adoption * Use ensure\_tree from oslo\_utils.fileutils * Remove last vestiges of oslo-incubator * Updated from global requirements * Updated from global requirements * Pecan: No fields to plugin on GET member actions * IP Conntrack Manager changes for FWaaS v2 * Only mark ports ready on synced networks * Fix modify\_fields\_from\_db for vif\_details empty str * Forbid contract migration scripts for Ocata * Check if namespace exists before getting devices * DHCP agent: advertise SLAAC prefixes * Use new enginefacade for address\_scope\_db * Replace a test with use of network object * Add 'to\_primitive' for MACAddress and IPNetwork * Update Lieutenant table with new entries * Usage of new enginefacade for flavor db * Don't depend on translated strings for error check * Simplify resources module in RPC callbacks * SR-IOV: Remove deprecated supported\_pci\_vendor\_devs option * ipv6\_utils: delete get\_ipv6\_addr\_by\_EUI64 * Fix specs broken link * Make README less verbose and to the point * Make crystal clear how to contribute/approach the neutron community * Refactor/prepare db common utils for neutron-lib * Add OVO for dns Objects * Fix test\_unwatch\_log() to cleanup after itself * Updated from global requirements * TrivialFix: Modify the spelling mistake * Rename ipv6\_utils.is\_enabled() * Handle db\_add in transaction for new objects * [TrivialFix] Replace 'assertTrue(a in b)' with 'assertIn(a, b)' * Correcting a spelling in README * Refactoring config options for cache\_utils opts * Updated from global requirements * OVO for SegmentHostMapping * Updated from global requirements * Pecan: Bulk create with one item returns plural * Log OVS IDL library errors to neutron logs * Replace retrying with tenacity * Removes the superfluous 'that' * Add functional tests for OVSDB Connection * Removed unnecessary file(openstack/common) in run\_stack.sh * objects: add validate\_filters option for count() method * ovsfw: Add a dl\_type match for action=ct flows * Update metadata proxy when subnet add/delete * OVO for Router Extra Attributes * Validate type of allowed\_address\_pairs * Pecan: Fix internal server error on QoS rule PUT * Pecan: Don't prefetch resource fields on DELETE * Generate OVSDB schema helper in a separate method * objects: Remove tenant\_id from to\_dict() when project\_id is not set * Updated from global requirements * Cleanup coverage configuration * Fix spelling errors * Make ovs functional tests mutually isolated * Pecan: rework notifier hook for registry callbacks * set\_db\_attribute differs between vsctl and native * Only send string values to OVSDB other\_config column * test\_routers\_flavors: Skip if requirements are not met * Add http\_proxy\_to\_wsgi to api-paste * Only emit deprecation warning when deprecated value read * Swap the order of arguments to \_check\_equal * Cleanup of SecurityGroup classes * Remove deprecation warnings for agents\_db * Have RouterRoute object increment Router revision * Allow to override Idl class in OVSDB Connection * Drop MANIFEST.in - it's not needed by pbr * Updated from global requirements * fullstack: Add security group tests * neutron-lib: use dict methods from helpers * neutron-lib: use get\_hostname * neutron-lib: use cpu\_count * Make the HA router state change notification more faster * Pecan: add http\_proxy\_to\_wsgi middleware * Use BaseDbObjectTestCase in AddressScope UT * Devref: fix repeated 'testing documentation' text * Removes remaining Hyper-V plugin * Added trailing slash in link to Networking API v2.0 * Add db\_add to OVSDB API * Extend project\_id in object dicts (Newton only) * Integrate SubnetRoute OVO * Fix callers of \_make\_port\_dict() * objects: Removed project\_id/tenant\_id field translation * Remove deprecation warnings for l3\_hamode\_db * Fix IPv6 PD with pluggable IPAM * OVO for Router Route * Improved readibility and fixed indentation * neutron-lib: start using neutron-lib helpers * Cleanup Newton Release Notes * Fix periodic jobs: Add databases to bindep.txt * Relocate DNS db models * Updated from global requirements * ovsfw: Fix warning message when fetching port's other\_config * Relocate Agent DB model * Add api test for create update network and port with dns * Refactor code to deprecate get\_interface\_mac * Objects: Add README for neutron/objects directory tree * Add sample\_default for state change server config * Ignore gre0 and gretap0 devices in netns cleanup script * Get rid of double-join to rbac\_entries without filter * Deterministic ordering of fixed\_ips * Account for Py2/Py3 differences in fcntl.ioctl return value * Enable release notes translation * Handle label\_id's DBReferenceError when creating label-rule * Add required extension to trunk\_details descriptor * Relocate Segment DB Models * Make Jakub Libosvar contact for ovs-fw tag * Relocate Flavor and ServiceProfile DB models * Fix typo in comment * L3 DVR: always notify on ROUTER\_INTERFACE AFTER\_CREATE * Fix \_list\_availability\_zones for PostgreSQL * Relocate dvr model * Fix the 'port\_id' attribute not exist error * Relocate L3HARouter DB model * Replace create\_test\_network with network object * Relocate Tag DB model * DVR: remove misleading error log * Enable OVSDB Connection unit tests for py3 * Handle uuid references within an ovsdb transaction * Remove deprecation warning for l3\_agent\_scheduler * Expose OVS bridge related options into metering agent config file * Refactoring config options for ml2 plugin drivers * Remove recursion from import\_modules\_recursively * Refactoring config options for l3 ha agent opts * Relocate SegmentHostMapping DB model * Relocate Router Extra Attributes DB Model * Remove old oslo.messaging transport aliases * Relocate router route DB model * Disallow specifying too long name for meter-label * Fix misleading error trace in trunk fullstack life cycle test * Relocate Geneve DB models * TestSanityCheck: drop test tables during cleanup * Removed is\_deadlock from db/api * Relocate external network db models * fullstack: VLAN aware VMs test * Introduce ovo objects for networks * Schedulers: use consistent plugin, context args * Fixed functional iptables firewall tests for newer kernels * Relocate ProviderResourceAssociation DB models * Introduce ovo objects for ports * Relocate VlanAllocation DB model * Ignore NULL fixed\_ips for duplicate floating\_ip check * Relocate Metering DB models * Relocate RouterL3AgentBinding DB model * Updated from global requirements * Relocate L3 DB Models * Relocate Provisioning Block DB Model * Process OVS trunk bridges associated to VM deletes * Fix typo overriden => overridden * iptables: fail to start ovs/linuxbridge agents on missing sysctl knobs * Refetch subnet/network from DB in ML2 update ops * Send a callback before attaching a subnet to a router * Don't try to delete non-existent namespace * Added bindep.txt to the project * Don't swallow ImportError from load\_class\_by\_alias\_or\_classname * Remove requests-mock from test-requirements * Correct fix for IPv6 auto address interfaces * Darek (@dasm) is our new release liaison for Ocata * Make HenryG the Magnificent responsible for 'oslo' LP tag * Add missing revision number altercations * Fixes for missing IPs on subnets in DHCP agent * Retry port update on IpAddressAllocationNotFound * New option for num\_threads for state change server * Handle add/remove subports events loss due to agent failures * Fullstack: Add helper FakeFullstackMachinesList * trunk: Log RPC communication * tests: Move testtools to 3rd party libraries section * Fix pep8 E501 line too long * Fix dhcp\_release6 error when not supported * XenAPI: add support for conntrack with XenServer * Fix ML2 revision\_number handling in port updates * Fix ML2 test extension driver API test cases * Relocate PortBindingPort DB Model * Relocate VxlanAllocation/VxlanEndpoints DB model * Support new osprofiler API * Include entity in provisioning block logs * API tests: Check MTU sanity of trunk/subport * Use assertItemsEqual to compare dicts * Fix wrong use of six.moves.queue.get() * Allow more time for DB migration tests * Update trunk metadata during wire/unwire operations * Ensure trunk status is acknowledged during OVS subport operations * Clean-up Tempest test networks with project\_id * Add MAC address to subports in trunk\_details * Updated from global requirements * Install dibbler.filters rootwrap file * tests: catch eventlet.Timeout exception * Add basic status transition trunk scenario test * Remove deprecated class NeutronController * Fix typos in test\_ipam\_pluggable\_backend.py & base.py * Do not retry default security group creation * Fix a release note typo for implicit provider loading deprecation * Revert "Do not retry default security group creation" * Try to reuse existing IPs when a port update specifies subnet * Make DHCP agent use 'revision\_number' * Stop oslo\_messaging from error logging CallbackNotFound * Don't immediately restart in DHCP agent on port change * Always cleanup stale devices on DHCP setup failure * Reduce log level for extensions not being supported * Add admin api tests for project-id * Do not retry default security group creation * Increase subnet count for rally test * Lower concurrency on rally port job * Add to rally quotas to handle worst case quota race * Fix linuxbridge trunk subport RPC event handler * OVS agent: configure both OF10 and OF13 * Improve tunnel\_sync * Disable DHCP on agent port removal * Garbage collect HasStandardAttributes subclasses in StandardAttrTestCase * fullstack: execute qos tests for all ovsdb/of interface permutations * Change the prefix for trunk subports device\_owner * Fix devref typo * Keepalived global\_defs configuration entries required to avoid DNS lookup * DHCP: enhance DHCP release log * Make neutron-db-manage ready for ocata development * Update reno for stable/newton * Fix useless use of \_nxm variant in an OVS field name 9.0.0.0rc1 ---------- * Added missing translation marker for an error * Add metadata proxy router\_update callback handler * Fix events misnomer in callback registry debug trace * Fix metering-agent iptables restore failure * Add fullstack test with OVS arp\_responder * Don't raise RetryRequest for port in delete\_subnet * Retry setting mac address on new OVS port 10 times * Forbid importing neutron.tests.\* from outside tests subtree * Raise level of message to info * ovs agent, native ARP response: set Eth src/dst * Remove unused phys\_net parameter form EmbSwitch class * Expire DB objects in ML2 infinity loops * Stop olso\_messaging from error logging CallbackNotFound * Add API test to ensure IPs can be added by subnet * Revert "Don't allocate IP on port update when existing subnet specified" * Add common way to extend standard attribute models * Fix migration of legacy router to DVR * Catch DBReferenceError in IPAM and convert to SubnetNotFound * Don't warn in VLAN Plumber on tagless children * Skip warnings during DHCP port actions if no error is raised * Include timezone in timestamp fields * LinuxBridge: Use ifindex for logical 'timestamp' * Correct floating IP extra attributes updating issues * Refactor for floating IP updating checks * Prevent use filter(lambda obj: test(obj), data) * Catch RPC errors in trunk skeleton * Make DHCP notifier use core resource events * Disable DHCP on test\_create\_port\_when\_quotas\_is\_full * Capture SubnetNotFound from update\_port call * Add retry decorator to provisioning blocks module * Tag the alembic migration revisions for Newton * Utilize retry\_if\_session\_inactive in dvr\_mac\_db * Handle racey teardowns in DHCP agent * Demote error trace to debug level for auto allocation operations * Fix TypeError in sanity check logging format * Don't allocate IP on port update when existing subnet specified * Prevent duplicate LLA iptables rules * Remove erroneous newton milestone tag * Preventing iptables rule to be thrashed * tests: Don't raise TimeoutError when waiting for ping * Mark quota operations as retriable * Pass not IPDevice but port\_name into OVSBridge's add\_port() * devstack: the last fix of is\_kernel\_supported\_for\_ovs25 condition * Ensure there are fdb\_entries before iterating * Fix Rollback port's device\_owner * Protect security group operations with the retry decorator * Don't work with native DB port objects in DVR code * of\_interface: Use vlan\_tci instead of vlan\_vid * Set device\_owner for trunk subports * Only do deferred ip allocation on deferred ports * Accept and return project\_id for API calls * trunk: Remove ovs constants from trunk utils module * Skip can\_be\_trunked validation for subports * ovsdb-handler: Follow up patch * Make l2/l3 operations retriable at plugin level * Reduce log level for ryu in OVS agent log * Use ConfigParser instead of SafeConfigParser * Replace assertEqual(None, \*) with assertIsNone * Emit router payload only during failures * tests: don't override base object test class attributes * objects: expose database model for NeutronDbObject instances * Defer setting 'ha'/'distributed' flags in L3 code * Updated from global requirements * Add sg\_id in the AFTER\_DELETE event of sg\_rule delete * Fix sudo errors in test\_\_set\_trunk\_metadata\_with\_None\_params * Add release note for blueprint vlan-aware-vms * Make optional the validation of filters * Mark unaddressed ports with none in ip\_allocation field * Fix misnomer in \_set\_trunk\_metadata * stadium: remove neutron-release from release job for stadium projects * LinuxBridge: Pass host into get\_devices\_details\_list * Downgrade binding skip in mech\_agent * Use singleton for Nova notifier * trunk: Handle subports notification in ovs agent side * objects: don't call DeclarativeObject's \_\_init\_\_ twice for rbac objects * Move retry decorator in auto allocate * Add retry decorator to RBAC module * Mark agents db mixin operations retriable * Add sanity pings to restart tests to ensure connection * Implementation of vlan-aware-vms for Linux Bridge * Add Trunk Plumber module for Linux Bridge * Move 'get\_tap\_device\_name' to utils module * devstack: finally fix the is\_kernel\_supported\_for\_ovs25 condition * tests: remove self.db\_obj from objects tests * devref: cleaning up release guidelines for stadium subprojects * Add missing index on inserted\_primary\_key in migration * l2pop fdb flows for HA router ports * OVSDBHandler for trunk ports * Prepare retry decorator to move to plugin level * devstack: fix kernel version matching to detect ovs compilation support * Neutron versions api now has its own middleware pipeline * Use correct exception in L3 driver controller * pylint: remove 'openstack' from ignore list * Fix SR-IOV qos extension calls to clear\_rate functions * Don't use nested transaction in provisioning blocks * Rollback port after failed to add it to router * Quotas: add in missing translation * Ensure UnknownProvisioningError can be printed * DHCP: remove deprecated config dhcp\_lease\_time * Standardize release note page ordering * DHCP: device manager: two new methods - plug and unplug * Detect DBReferenceError when deleting flavor * Add API test for L3 Flavors * Reduce the number of debug traces emitted by registry callbacks * Add a composite unique key to floatingip table in Neutron database * Don't return ext net DB object in auto allocate * Be more clear about DOWN\_STATUS for trunk resources * Include timestamps in trunk API test comparisions * Except if tracked resource registered as countable * Convert OVO fields to primitives for to\_dict() * Add link to review dashboard in the effective guide * Fix wrong HA router state * Config: change deprecation for max\_fixed\_ips\_per\_port to Ocata * Only schedule routers from drivers that need it * Remove FWaaS policy.json entries * Update L3 agent extensions devref * Remove duplicated tool * Remove availability range code and model * tests: refactor objects test cases to use db models instead of dicts * gate\_hook: Compile ovs only with supported kernels * objects: use correct object class name in NeutronPrimaryKeyMissing * l3 ha: don't send routers without '\_ha\_interface' * ovsfw: fix troublesome port\_rule\_masking * Check MTU sanity of trunk port subports * QOS-DB: remove deprecation warning * Skip setup\_port\_filters for skipped\_devices * Disallow specifying too long description for meter-label * Use bridge\_lib's FdbInterface calls instead of execute util * Expose revision\_number to trunk API * Change 'revision' to 'revision\_number' * macvtap: Mech driver detects invalid migration * Remove workaround for bug/1543094 * Deal with unknown exceptions during auto allocation * Revisit support for trunk segmentation types * Set trunk status to DOWN when parent port is unbound * db migration: Alter column before setting a FK on column * Switch order for cleanup in NamespaceFixture * Add binding\_index to RouterL3AgentBinding * Add timestamp fields for neutron ext resources * Make L3 agent use different request-id for each request * Refactor \_ipam\_get\_subnets * Fix AddrFormatError in DHCPv6 lease release code 9.0.0.0b3 --------- * Add name and desc to networksegments table * Fix QoS standardattr migration foreign key step * Ignore extra subnet IPs in DHCP agent * Truncate microseconds before DB insertion * Updated from global requirements * Make milestone-review-dash script pull all Stadium projects * Adding support of releasing DHCPv6 leases * qos: require min\_kbps parameter for minimum bandwidth rule * Mock call to sysctl for test\_get\_free\_namespace\_port * split out l3-ha specific test from TestMl2PortsV2 * Add L3 agent extension API object * Switch to neutron-lib for model\_base * Do not warn under normal network circumstances * Narrow down DBError to DBReferenceError in DHCP action handler * Expose timestamp attributes on trunk API * Remove non-pluggable IPAM implementation * Expose description attribute on trunk API * DVR: SNAT redirect rules should be removed only on Gateway clear * Implement state management for trunks * debtcollector for globals * Retain port info from DSCP rule creation * Use method get\_ipv6\_addr\_by\_EUI64 from oslo.utils * Use plugin obj from neutron manager in the TestMl2PortsV2 unit test * Add new configuration test in sanity check: vf\_extended\_management * IPAM: fix the mode of ipam\_backend\_mixin.py * Pecan: Handle member actions * Get rid of flush() in create\_segment() * Fixes DBDeadlock race condition during driver initialization * Remove FWaaS entries from neutron setup.cfg * fix port address allocation for auto-addr subnet * DVR: Cleanup the stale snat redirect rules in router namespace * objects: remove support for multiple db models in from\_db\_object * Check for ha port to become ACTIVE * Fix ipam\_driver config help * Auto allocation: ensure that networks and subnets are cleaned up * Add the l3 agent 'host' when logging routers * Handle fullstack oslo deprecations * fullstack: Use ovs-2.5 for tests * Relocate Flat Allocation DB model * Relocate subnet\_service\_types db models * Remove unnecessary setUp and resource\_setup * Delete unused pom.xml file from doc * Remove unused config.CONF * Remove unused logging import * Add test cases for Invalid exception type * Allow SR-IOV agent to start when number of vf is 0 * Add standard attributes to qospolicy * Enable create and delete segments in ML2 * Use MultipleExceptions from neutorn-lib * Move standard attr out of model\_base * SR-IOV: add agent QoS driver to support egress minimum bandwidth * Add QoS minimum bandwidth rule for instance egress traffic * Remove vpn service plugin stevedore aliases * Remove lbaas v1 service plugin stevedore aliases * Generate new context for each DHCP RPC call * Fix unbalanced-tuple-unpacking pylint error in conn\_testers * Don't catch DBError in DHCP action handler * Catch InvalidInput in DHCP port actions * Wait for ovsdb\_monitor to be active before use it * Fix deprecation warnings * spelling error * Updated from global requirements * Make addbr safe to bridge add races * Not check subnet with dhcp disabled when get\_isolated\_subnets * Add Unit Test for Foreign Keys Not Found Error * Deprecate allow\_sorting and allow\_pagination options * functional: Make trunk tests more robust * Fix passing error physical network for get\_mtu * Fix indent * Remove useless line for tenant\_id * Implement check\_vlan\_transparency to return True in L2population mech driver * Security group call back need cascading delete the related rules * Clean import in code * Set L3 agent standby if admin\_state\_up=False * Fix internal server error during updating QoS rule * Make OVS and LinuxBridge trunk drivers' is\_loaded() property more robust * IP allocation with Service Subnets * Include [agent] extensions option into ovs/linuxbridge agent files * Using revision\_number to ensure no overlap in \*one\* network * Fix the QoSPluginBase methods signature * Implement check\_vlan\_transparency to return True in SR-IOV mech driver * TrivialFix: Remove logging import unused * TrunkManager for the OVS agent * SR-IOV: remove unused supported\_pci\_vendor\_info variable * Catch SubnetAllocationError during auto-allocated-topology provisioning * Revisit the Stadium section of the developer guide * objects: add support for per parent type foreign keys * Fix bug in L3 agent extension manager * Added the appropriate links in developer guide * L3 DVR: use fanout when sending dvr arp table update * Fix the attribute name: \_flavor\_plugin\_ref * Use more specific asserts in tests * Implement the DELETE method for get-me-a-network * Update README to reflect ML2 Exception in Dir Tree * Revert "Fix NoSuchOptError on identity config option lookup" * Use row.uuid as getattr works for inserted row * fullstack: test for IPv6 north-south traffic * Add mechanism driver error details to MechanismDriverError * Make auto allocate cleanup retry * Updated from global requirements * Increase default packet count to 3 in assert\_ping * L2 Agent Extensions handle unimplemented methods * Relocate GRE Db models * docs: Small Open vSwitch devref tweaks * Filter out external networks in NetworksSearchCriteriaTest * Remove useless deprecation warning for tenant\_id * Fix init method for HasStandardAttributes * Imported Translations from Zanata * TrunkStub.trunk\_deleted is called with NULL trunk object * Fix NoSuchOptError on identity config option lookup * Fix bridge assertion error when times are equal * Avoid KeyError when accessing "dns\_name" as it may not exist * Add tool to list moved globals * Introduce ovo objects for network segments * Add agent-side driver scaffolding for trunk functionality * Revert "Publish segment id in port responses" * Increase rally network/port count and add quotas * Extensions: fix file mode permissions * Update the homepage with developer documentation page * ML2: Lower log level of "Host filtering is disabled" message * Don't create another plugin instance in ML2 tests * Relocate AddressScope DB model * Enable ra on gateway when add gateway to HA router * Remove override of \_compare\_server\_default in \_TestModelsMigrations * Make callback manager Object Oriented friendly * Switch to pluggable IPAM implementation * Update "devref/quality\_of\_service" with QoS DSCP rule reference * Removing unused file neutron/agent/l3/config * Use only\_contrib option for nova\_client calls * Adjust spacing in METADATA\_PROXY\_HANDLER\_OPTS * Refactoring config options for plugin agent opts * Reduce DB hit when checking for trunk-able ports * Add API test for tag * Added tests for checking expand/contract branch upgrade * Don't pass argument sqlite\_db in method set\_defaults * SR-IOV: deprecate supported\_pci\_vendor\_devs * Add error informations for users when value is invalid in database * Implement L3 Agent Extension Manager * Correct floating IP updating with same port\_id issue * Fixed neutron-db-manage without neutron/tests installed * Clean imports in code * Prevent duplicate SG rules in 'concurrent requests' case * Relax bound constraint for trunk parent ports * Fix potential problem in test\_router\_add\_interface\_port * Fix test\_router\_add\_interface\_delete\_port\_after\_failure * Remove stale configuration l3\_agent\_manager * Add RPC layer for Trunk Plugin and driver plumbing * Make auto-allocate plugin handle sneaky DB errors * Broken extensions should not show up in the extension list * Introduce ovo objects for security groups * Add debug option to verify iptables rules * Avoid IPAM driver reusing a session that has been rolled back * Fix neutron\_lib.constants DeprecationWarning from db.models\_v2 * Use dispose\_pool() from oslo.db * Get rid of get\_engine() in db/api.py * models: move AllowedAddressPair model under neutron/db/models * Refactor setting OSprofiler for db calls * Raise 501 instead of 500 when updating meter-lebel and rule * Updated from global requirements * Constrain remaining tox targets * Check content type by completely match instead of partial match * Allow bound ports to be trunked if the driver can support it * Publish segment id in port responses * Fix some spelling errors in net\_helpers.py * Refactoring config options of l3 agent keepalived * Fix check\_asserttruefalse syntax validator * Relocate Security Group DB models * Add floating IP test to ensure backwards compat * Always start transactions in quota cleanup methods * Refactoring config options for services opts * Fix a spelling error * isolate test\_db\_find\_column\_type\_list * Include db\_models document to avoid errors * Adds a default reload callback to ProcessManager * tests: added missing space in a skip test message * Set secure fail mode for physical bridges * Avoid allocating ports from ip\_local\_port\_range * lb-agent: handle exception when bridge slave already removed * Ensure ML2's create/update\_port methods not in transaction * Add flush command to iproute in ip\_lib * Better utilize the L3 Namespace class * Fix typo in l3-agent namespace code * ovs-agent: Seperate VLAN mapping outside of the agent * Updated from global requirements * Check the router gateway IPs prefixlen existence * pep8: fixed F821 violation in a unit test * Add devref for Relocating DB models * Handle deleted ports when creating a list of fdb entries * Remove unused params named "context" in filter\_attributes * Set bridge\_name in OVS trunk port's vif\_details * ml2: allow retry on retriabable db error by precommit * Rollback router intf port update if csnat update fails * Enable DeprecationWarning in test environments * Check target\_tenant when create rbac policy * Delete HA network if last HA router is migrated * Add linux bridge trunk server side driver * Enable CRUD for Subnet Service Types * Make revision bump robust to concurrent removals * Fix duplicate routerport handling * Cleanup DB retry logic in ML2 * Refactoring config options for extension opts * Refactoring security group config options * Don't use versions in \_DeprecateSubset * Add RouterPort bindings for all HA ports * Log full exception before retry in decorator * L3 agent: check router namespace existence before delete * Consider baremetal device\_owner as compute for nova notify * Delete conntrack entry with remote\_ip on the other direction * Do not remove the HA/legacy router gateway secondary IPs * DHCP Auto Scheduling for routed provider networks * Restore old assert\_ping behavior * DVR: Clean stale snat-ns by checking its existence when agent restarts * Remove neutron-lib warnings * Ensure most of ML2's core methods not in transaction * Add scaffolding for trunk plugin/server-side driver integration * Remove neutron lib warnings from ipv6\_utils * Check for l3 agents count before router update * Updated from global requirements * Introduce state management for trunk resources * Refactoring config options for wsgi opts * Add a space after openvswitch error message * Remove local subports validator * objects: introduce count() API to count matching objects * Rename DB columns: tenant -> project * Fix for creation of network environment twice * Use neutron-lib add\_validator for registration * objects: introduce a util function to handle tenant\_id filter * tests: check that trunk sub\_ports field is properly populated * Fix indexerror in delete\_csnat\_port * Add a unique key to port\_id in routerports table * Refactoring cfg opts for ml2 plugin linuxbridge * Port device events for common agent * Fix module import for ovs\_vsctl\_timeout option * Change external\_network\_bridge default to '' * Fix link reference in OVS agent devref * Support callbacks for L3 plugins without an agent * Remove deprecated default subnetpools * Fixes the midonet test\_l3 unit test failures * fixed a typo in src code * Suppresses a warning when no agents are configured * Introduce bulk push to rpc callback mechanism * Enable sorting and pagination by default * Added API extensions to detect sorting/pagination features * stadium: adopt openstack/releases in subproject release process * L2-Adjacency support * corrected the link in README.rst * Neutron-lib: use the L3\_AGENT\* definitions from neutron-lib * Fix a typo in neutron/services/trunk/rules.py * Refactoring config options of agent/common/ovs\_lib * Add a callback registry event for the init process - before spawning * Refactoring config options for cmd * Don't use file() to write object hashes * Fix L3 NAT DB signature mismatch * Add in missing translations for exceptions * Fix that api\_workers=0 doesn't spawn any api workers * Use revision to discard stale DHCP updates * modify the home-page info with the developer documentation * Filter HA router without HA port bindings after race conditions * Fix updating allocation\_pools on subnet update * trunk: avoid redundant refetch of subports on create * tests: enable test\_get\_objects\_queries\_constant for trunk ports * Don't use exponential back-off for report\_state * bug tag: Add 'api-ref' for API reference * Add link in README.rst * Set prefix on floating\_ip\_mangle rules * Remove 'released-neutronclient' tag from official bug tags * Increment revision numbers on object changes * Print out specific filter that failed in object filtering unit test * objects: loading synthetic fields from defined ORM relationships * objects: forbid updates for project\_id field for subnets * tests: stop using ml2 plugin full import paths in tests * Add API tests for router and DHCP port status * Skip DHCP provisioning block for network ports * Wait for vswitchd to add interfaces in native ovsdb * Add flavor/service provider support to routers * Add some negative policy router interface tests * Add notifications for trunk CRUD and standardize payload * Refactoring config options for common config opts * Prevent port update from binding a host where IPs won't work * policies: Add tempest tag for launchpad bugs * Fix for check\_vlan\_transparency on mech drivers not called * Refactor DNS integration out of DB core plugin * Fix typo in method description * Filter out subnets on different segments for dhcp * Add information about using file based sqlite for unit tests * Deprecate implicit loading of service\_providers from neutron\_\*.conf * Remove deprecated network\_device\_mtu option * objects: Add update\_fields method in base class * Remove unused code in neutron/agent/linux/utils.py * Pass timeout in milliseconds to timer\_wait * Prohibit deletion of ports currently in use by a trunk * Mark DBConnectionError as retriable * Add subresources support for PECAN * Refactoring config options for l3 agent config * Improve the segmentation ID validation logic * Revisit (add|remove)\_subports request body * objects: Adjust Subnet fields, add tenant\_id and segment\_id * Use is\_valid\_port from oslo.utils * Validate device to mac instead of port id to mac * Updated from global requirements * Don't interrupt device loop for missing device * Enable passive deletes on trunk deletion * Removed smoke tags from \*SearchCriteriaTest test cases * Calculate MTU on every network fetch instead of on create * Fix wait\_until\_true condition in dhcp test * Add callbacks for networks and subnets in ML2 * Check compatibility when auto schedule ha routers * Remove execute permission which is added by mistake in midonet * Ensure test\_cleanup\_stale\_devices fails gracefully * Add new attributes to trunk model * Generalize agent extension mechanism * fullstack: Add hybrid plug support * Use db\_api.retry\_db\_errors in quota engine * Update ovsdb release notes re: new OVS ports * objects: better apply filters for objects/db/api/get\_object query * Use DEVICE\_OWNER\_COMPUTE\_PREFIX from neutron-lib * Imported Translations from Zanata * Fix misuse of assertTrue in L3 DVR test case * Pecan: Define plugin crud methods in base class * Fix broken URLs in bugs.rst (core-reviewers.html -> neutron-teams.html) * objects: Convert filters to string for list values * fullstack: Log testrunner again * QoSTest: skip if qos extension is not available * Add support for Python 3.5 * Only ports on routed networks are deferred * Fill in trunk\_details on port resource * Fix a pylint error in an L3 agent unit test * DVR: Fix ItemAllocator class to handle exceptions * Add RouterPort binding to ha interface creation * objects: Add RBAC to Subnet OVO * Improve cleanup logic for trunk tests * Updated from global requirements * Add retry decorator to dhcp\_ready\_on\_ports * delete event payload * Add function to return all hosts with mapped segments * Handle non existing network in segment creation 9.0.0.0b2 --------- * Hacking: add unit test for LOG.warn validations * Allow unique keys to be used with get\_object * Add object versioning to QoS DSCP * Replace device owners hard coded strings to neutron\_lib constants * Add function to remove constraints from database * Add dhcp to Fdb extension's permitted device owners * Use context from\_environ to load contexts * Use from\_dict to load context params * Add a hacking rule for string interpolation at logging * Add check that factory started in dispose * Delete gw port on exceptions * Avoid duplicate ipset processing for security groups * DVR: handle floating IP reassociation on the same host * Refactor usage of dict.values()[0] * qos basic scenario * Check for provisioning blocks before updating port up * Rename dvr portbinding functions * Emit registry events on subport addition/removal * Ensure deferred IP fails when host is provided no IP allocated * Extension to tell when deferred binding is in effect * Fix typo in message string causing server exception * Deprecate option min\_l3\_agents\_per\_router * Address outstanding TODO for callback event * Allow tox to be run with python 3 * Incorporate tweaks to subport validator * Allow auto-addressed ips deletion on port update * Delete default route if no gateway in external net * Add information about contract creation exceptions in devref * ML2: don't use IntegrityError for duplicate detection * Grammar error fixed * Fixed Typo in contribute.rst * Refactoring config options for dhcp agent * Revert "Support unique labels for alembic branches" * DVR: Ensure that only one fg device can exist at a time in fip ns * New engine facade from oslo\_db: Step 2 * When deleting floating IP catch PortNotFound * Notify nova with network-vif-plugged in case of live migration * Skip TrunksSearchCriteriaTest if the extension is not available * Don't catch DBDuplicate in default SG creation * Catch missing binding record in provision handler * Pull stadium projects from governance.o.o in utility script * exclusive\_resources: use set remove instead of discard * Add an independent function to map segment to hosts * List only admin\_state\_up auto-allocated resources * Change tunnel MTU calculation to support IPv6 * Fix broken link * ML2 remove extra checks in ovs\_dvr\_neutron\_agent * Updated from global requirements * Fixed typos * Fixes a link error * next() is incompatible in test\_network\_ip\_availability.py * Run 'ip netns list' according to 'AGENT/use\_helper\_for\_ns\_read' * Remove unused LOG * Fix order of arguments in assertEqual * Reuse common code in securitygroups\_rpc module * Release note: fix a typo in add-time-stamp-fields * Imported Translations from Zanata * Update the template for model sync test docs * Add sorting and pagination tests for trunk resources * Enable CRUD for trunk ports * OVS-agent: Switch the default to "native" of\_interface * Use tempest.lib tenants\_client * Stable Branch URL Fixed * Support unique labels for alembic branches * create\_router: Report the original exception * ml2: postpone exception logs to when retry mechanism fails to recover * Fix OVSBridge.set\_protocols arg * Create segment\_host mapping after new network * Fix spelling mistakes in the docs * Adding the appropriate log hints where needed * Lower ML2 message severity * spelling error: modify assocations -> associations in files as follows: neutron/agent/l3/link\_local\_allocator.py:38 * Make create\_object\_with\_dependency cleanup * Restore MySQL and Postgresql functional testing * functional: Use assertItemsEqual for db\_find outputs * Adding FDB population agent extension * pep8: Register checks with their code * sriov: Fix macvtap vf interface regex pattern * Mock threading.Thread to prevent daemon creation by unit tests * Fix some typos * Register the dict extend function when service plugin starts * Remove notification for process event * Add two more callbacks registry events * Do not depend on Python error strings in parse\_network\_vlan\_range() * Fix code that's trying to read from a stale DB object * Remove 'origin/' in OVS\_BRANCH * Only update SegmentHostMapping for the given host * Move Nova notification logic out of API controller * Create segment\_host mapping after new segment * Skip INVALID and UNASSIGNED ofport in vlan restore * objects: introduce NetworkPortSecurity object * objects: Introduce the DNSNameServer OVO in the code * Implementation details to support trunk ports * Move wait\_until\_true to neutron.common.utils * Imported Translations from Zanata * Agent extension: fix comment * enable OVSDB native interface by default * Pecan: Implement pagination * Not auto schedule router when sync routers from agent * Updated from global requirements * Remove the deprecated config "quota\_items" * Fix simple typos * Create auto allocated networks in disabled state * Move DHCP notification logic out of API controller * Pecan: move fields and filters logic to hooks * DHCP Agent scheduling with segments * Fixes port device\_id/device\_owner change in failed operation * Remove the deprecated config 'router\_id' * Separate exception class for retriables in callbacks * Revert "OVS: don't throw KeyError when duplicate VLAN tags exist" * Updated from global requirements * Add revision\_number to standard attr for OVO * Check for RetryRequest in MultipleException types * Remove IP availability range recalculation logic * Rename ml2\_dvr\_port\_bindings to make it generic * Override 'create' for Trunk and SubPort * Retry DBDuplicate errors in retry decorator * Complete removing support for unsplit alembic branches * Revert "move import to top and rename to make more readable" * Make pep8 job great again * Disable warnerrors=True in setup.cfg * Move state reporting initialization to after worker forking * Do not rewrite original exception for IPAM part 2 * Change addCleanup create\_tenant to delete\_tenant, fix gate * Obsolete mac\_generation\_retries and deprecate the option * Remove unnecessary flush for duplicate address detection * Fix minor spelling error in debug log * tests: clean up designate client session mock on test exit * Remove unnecessary import from segment plugin * OVS: UnboundLocalError on switch timeout fixed * ovsfw: Fix variable names in UT * ovs: set device MTU after it's moved into a namespace * cache\_utils: fixed cache misses for the new (oslo.cache) configuration * Syntax fix * ml2 lb: do not program arp responder when unused * Remove deprecated TODO notes in L2 agent extension manager * Fix pep8 violations in fullstack qos test * Don't return marker item when paginating backwards * Fix release name in deprecating message for agent status checks * Typo fix * Fix bug with mac generation * Mark port as ready after enabling dhcp at agent * Add exponential gap to retry decorator * Fix designate dns driver for SSL based endpoints * Update Neutron documentation with \`project\` * Provide DB isolation for WalkMigrations tests * Refactor NetworkDhcpAgentBinding * Tempest: fix a typo * qos: support native sorting/pagination for policies and rules * qos: added api sorting/pagination tests for policies * Check for alembic Add/DropColumn exceptions in migrations * objects: switch base plugin class to using subnetpool object * l3: support native sorting/pagination for routers * Added sorting/pagination tests for routers * Added sorting/pagination tests for subnets * DHCP: delete config option dnsmasq\_dns\_server * Refactor update\_segment\_host\_mapping\_for\_agent for agentless topology * OVS agent: remove unused variables * Updated from global requirements * Remove the plugin import from auto\_alloc extension * Use neutron-lib constants * qos: fix shared filter for policies * objects: Add filter query hook to NeutronDbObject * Extend utils create methods to accept check\_allow\_post * tests: fetch unshared subnetpools only * Cleanup Liberty to Mitaka upgrade corner cases not necessary anymore * Fixes variable name confusion * Use StringField for tenant\_id fields * Remove useless str() * Add router precommit events * Allow self-unsubscribing callbacks * Add data model for standard attr revision numbers * DVR: Fix allowed\_address\_pair port binding with delayed fip * Update networking-ovn project functionality * Revert "Add index on trunk\_id in the subports model" * Update Neutron server to use only keystoneauth * Make segment aware IPAM compatible with ML2 * Fix of ping usage in net\_helpers.async\_ping() * Remove MAC duplicate detection for generated macs * Pecan: handle single fields query parameter * Compute IPAvailabilityRanges in memory during IP allocation * Clean up subports model * After a migration clean up the floating ip on the source host * Check for mysql SAVEPOINT error in retry decorator * Check for StaleData errors in retry decorator * Use session delete for IPs to trigger events * Fix update of shared QoS policy * Fixed variable shadowing in ipam\_backend\_mixin.py * Add type information to ObjectNotFound message * Add README for api-ref * Use next available address for dhcp ports * objects: in get\_object(s), filter by fields, not model attributes * Refactor the rpc callback version discovery mechanism * Add index on trunk\_id in the subports model * Support for MultipleExceptions in db\_api decorators * Convert multiple exception types in the API * Add sanity check for idempotent id's in tempest tests * Remove some options from neutron-db-manage * tests: added sorting/pagination api tests for subnetpools * Drop neutron/i18n.py in favor of neutron/\_i18n.py * QoS DSCP fullstack tests * Hacking: make use of neutron lib * Return fixed\_ips from port update * Deprecate the functions map after segment moving out of ml2 * Use callback to update SegmentHostMapping * Match filter criteria when constructing URI for href based iteration * Make service plugins' get\_plugin\_type classmethods * OVS: don't throw KeyError when duplicate VLAN tags exist * Revert "DVR: Clear SNAT namespace when agent restarts after router move" * objects: Use common plugin \_model\_query in get\_object * Tox: Remove neutron/openstack/common from excludes list * Fix missing availability\_zone in dhcp and l3 conf * qos: Add API test for shared policy * Imported Translations from Zanata * l3\_db: Make gw port in-use check overridable * Fix server\_default comparison for BigInteger * Update ml2 delete\_subnet to deallocate via ipam * Make IPAM segment aware on port update * Make sure AZAwareWeightScheduler base on weight of agent * Provide router-id for ROUTER\_INTERFACE/BEFORE\_DELETE event * L3 DB: only read dns 'dns-integration' once * Force "out-of-band" controller connection mode * Devref: Add inline codeblock to upgrade command * oslo.cache: Remove memory alias for cache * devref: remove references to oslo incubator * Revert "Revert "Remove threading before process forking"" * Use IPv[46]\_BITS instead of IPV[46]\_MAX\_PREFIXLEN * Adding basic connectivity scenario to Neutron * Switch to oslo.cache * [TrivialFix] Deprecate unused NeutronController class * remove unused rpc method get\_active\_networks * Added API tests for page\_reverse and href next/previous links * Correct hyperlinks to sub\_projects documentation * DB base plugin: correct typo id to subnet\_id * Devstack support for macvtap agent * Segments: remove log message indicating segments are not supported * Updated from global requirements * Move dragonflow to affiliated project list * Fix getting dhcp agents for multiple networks * Pass ha\_router\_port flag for \_snat\_router\_interfaces ports * objects: stop using internal \_context attribute * Fix get\_free\_namespace\_port to actually avoid used ports 9.0.0.0b1 --------- * DVRHA interfaces should be created in all cases * Make IPAM segment aware on port create * Updated from global requirements * Guard against config\_dirs not defined on ConfigOpts * Macvtap: Allow noop alias as FW driver * Generalise the logic of resource auto rescheduling * objects: support advanced criteria for get\_objects * Fix wrong description in the class DhcpAgentNotifyAPI docstring * Follow effective Neutron guidelines * DB: remove deprecated warnings * Dont use query if network\_ids is empty * Fix two spelling mistakes * ML2: use neutron-lib for PORT\_STATUS\_ACTIVE * Use IPv6\_LLA\_PREFIX from neutron-lib * RPC unit tests: remove UUID future warnings * Use ICMPV6\_TYPE\_\* constants from neutron-lib * Updated from global requirements * fullstack: test for IPv6 east-west traffic * Allow the service plugin to import the extension * functional: fix OVSFW failure with native OVSDB api * ovsdb: Don't skip ovsdb test cases in py34 suite * ovsdb: Don't let block() wait indefinitely * [qos] section is missing from neutron.conf * oslo: remove usage of oslo.log verbose option * Adopt to config\_dir option being a list and not a string * OVS: compare names when checking devices both added and deleted * Do not inherit test case classes from regular Neutron classes * Fix validation of floating-ip association * tests: guarantee unique networks in get\_list\_of\_random\_networks * Pecan: bind attribute map to controllers * Refactor QoS plugin to avoid code duplication * Avoid shadowing the method's port argument * OVO for VLAN aware VMs * tests: cover port with existing sorting/pagination api tests * Allow min\_l3\_agents\_per\_router to equal one * How to support trunk ports with Open vSwitch Agent * Introduce official lib tag for neutron-lib issues * Pecan: tell the plugin about field selection * Add segments to hosts mappings * Remove using PROTO\_NAME\_IPV6\_ICMP\_LEGACY from neutron.common.constants * Prevent adding duplicated sg rules with diff description * Updated from global requirements * Remove remaining BGP code from neutron * DVR: Pings to floatingip returns with fixed-ip on same network * Migration for qospolicyrbacs has hardcoded InnoDB * Remove the inaccurate help message for min\_l3\_agents\_per\_router * OSProfiler imports break decomposed plugins * tests: validate sorting and pagination for networks * Fix unit test for new list of icmpv6 allowed\_types * Use unittest2 uniformly across Neutron * Call ext\_manager.delete\_port on port removal * devref: remove l2 agent extensions steps literal * Fixed help messages for path\_mtu and global\_physnet\_mtus options * Trivial fix - NETWORK\_TYPE validate * Change log level when L3 agent is disabled * Add quota tests in api tests * Introducing the Subnet OVO * unit: fix ValueError on TransactionQueue init with py34 * Add OSprofiler support * fullstack: test for snat and floatingip * Integrate the port allowed address pairs VersionedObject in Neutron * Deprecate advertise\_mtu option * Pecan: Backwards compatible/cleaner init * fix wrong default value of qos.notification\_drivers * Rename \_find\_router\_port\_by\_network\_and\_device\_owner * DVR: Fix check multiprefix when delete ipv4 router interface * fullstack: test for east west traffic * Update unit tests to use correct method being used * Deprecate neutron-debug * functional: Fix ovsdb-monitor waiting for first events * Fix broken URL in Mitaka Neutron release note * Remove nested transaction from ipam driver * Allow for excepted operations in migrations * functional: Run OVSFW tests with ovsdb native interface * Mock out poller in ovsdb unittests * DVR: Moving router from dvr\_snat node removes the qrouters * Pecan: remove deprecation warning * Update for removing ofagent * Ensure unit tests don't assume an IP address allocation strategy * Updated from global requirements * Segment: remove deprecation warning for converters * Add negative API tests that try to remove the resources in use * Respond negatively to tenant detachment of enforced QoS policies * Removed invalid test due to invalid mocking * Check if pool update is needed in reference driver * Remove cliff requirement in test-requirements.txt * sriov\_nic config options were declared under wrong group * Make deepcopy of update body in API layer * Refactoring config options for service * Pecan: Handle hyphenated collection with shims * Enforce UUID of port/subnet ID for router interfaces * Make exception translation common and add to pecan * Remove unhelpful test of oslo.service ServiceLauncher * Remove a SGs API test following sync to Tempest * Avoid testing oslo.service library internals * Restart dsnmasq on any network subnet change * Add service-type extension to flavors plugin * Add method to get service provider names by resources * Enable flavor plugin as a default service plugin * Add setting default max\_burst value if not given by user * Remove the file i18n.py and other related cleanups * Fix for 'ofport' query retries during neutron agent start * Segment extension: remove deprecated warnings * Add provisioning blocks to status ACTIVE transition * devref: Cleanup OVS vHost User Guide * Refactoring config options for quota * Trivial Fixes for 'sub\_project.rst' devref document * Add retry support to pecan * Remove openstack-common.conf * Enable IPAM drivers to allocate from more than one subnet * Associate subnets to segments through subnet API * Insert validation with request params for HTTP DELETE * OVS Mech: Set hybrid plug based on agent config * Remove deprecated references to attributes * SR-IOV driver and SimpleAgentMechanismDriverBase * Delete a magic number of security\_group\_rule quotas test * Pecan: skip all notifications on failures * Remove tools/pecan\_server.sh * DVR: Ensure fpr and rfp devices are configured correctly * Changing VPNaaS bug contact name * Check for existence of snat port before deleting it * Move address scope specific code out of iptables\_manager * Use correct session in update\_allocation\_pools * Revise ICMPV6\_ALLOWED\_TYPES * Revert "Remove threading before process forking" * Basic Extension and CRUD for Segments * Classes lack metaclass decoration * DVR: Use existing IPDevice to add address on FIP VETH * Fix SR-IOV binding when two NICs mapped to one physnet * Add exponential back-off RPC client * Fix prefix delegation deleting addresses for ports * Fix the sriov agent config group name * DVR: Clear SNAT namespace when agent restarts after router move * Delete post\_test\_hook.generate\_test\_logs * Add logging for some functional tests * Fix functional tests logging * Word about notification\_driver * New engine facade from oslo\_db: Step 1 * OVS: Add support for IPv6 addresses as tunnel endpoints * DVR: Fix TypeError in arp update with allowed\_address\_pairs * Remove threading before process forking * Add API to retrieve default quotas * fullstack: Use noop firewall * Don't load DNS integration in l3\_router\_plugin * Annotate alembic migration for VLAN trunking * Fix update target tenant RBAC external path * Preserve backward compatibility with OVS hybrid plugging * Avoid eventlet monkey patching for the tempest plugin * Policies: additional go-to contacts for IPv6 bugs * Added PecanResourceExtension * Resigning as "go to contact" for DVR bugs * Pecan: Fix Shim status codes * Do not rewrite original exception for IPAM * update networking-odl lieutenant * Mock mechanism manager instead of the test driver * add PROTO\_NUM\_IPV6\_ICMP for \_validate\_port\_range * Resignation from FwaaS duties * Remove old fwaas remnants from neutron * agentnotifiers: retried notifications refactor * Avoid L3 agent termination without server * Use paginate\_query from oslo\_db 8.1.0 ----- * Revert "Improve performance of ensure\_namespace" * IPtables firewall prevent ICMPv6 spoofing * Replaced this with "get-me-a-network" * Remove BGP code from neutron * Add device\_id index to Port * Revert "Improve performance of ensure\_namespace" * Updated devref documents for neutron-dynamic-routing * Data models for vlan-aware-vms * Pass through setattr to deprecated things * Use converters and validators from neutron-lib * Revert "Switch to inheriting hacking checks from neutron-lib" * DVR: Use IPDevice class consistently * Use constants from neutron-lib * Decorate AvailabilityZonePluginBase properly * Remove functional migration script about external tables * add networking-fortinet in affiliated projects * Allow OVSDB Connection to register certain tables * Switch to inheriting hacking checks from neutron-lib * Add test that ensures subnet is usable after update * Use exceptions from neutron-lib * Revert "ML2: Configure path\_mtu to default to 1500 bytes" * Imported Translations from Zanata * Object tests: couple of pep8 and list comparison improvements * Add a missing address-scope extension check * policies: changed owner for release-subproject tag to @ihrachyshka * Address Scope added to OVO * Tests: correcting typo in get\_random\_cidr * Updated from global requirements * Mark remaining negative subnetpool tests with 'negative' tag * Removed smoke tags from all -api job tests * Remove two exceptions already in neutron-lib * Correct the interval between two reports * Don't log warning for missing resource\_versions * Imported Translations from Zanata * tempest: enable hacking rule checks * Return a 404 on quota delete if entry not found * Move class properties to instances for dhcp/test\_agent.py * Updated from global requirements * LinuxBridge agent's QoS driver bw limit for egress traffic * Add check that external gw port exist when metering-agent adds a rule * Port allowed address pairs to OVO * Neutron objects test: correct the db\_obj to obj\_fields where needed * Fetch router port subnets in bulk * QUOTAS: add in missing space to help text * Revert "Deprecate 'ovs\_use\_veth' and 'veth\_mtu' options" * DHCP: remove update\_lease\_expiration * Imported Translations from Zanata * release note to deprecate prevent\_arp\_spoofing option * Modify an option for delete-port as recovery * Don't update DHCP ports in a transaction * Use switch-case instead of if-then-elif * Match job name for scenario tests * Clear DVR MAC on last agent deletion from host * Move class properties to instances for dhcp tests * Optimize get\_ports query by filtering on subnet * Avoid calling \_get\_subnet(s) multiple times in ipam driver * Be explicit about the APIs tested in the gate * Move away from invoking api tests explicitly * Updated from global requirements * Add an option for WSGI pool size * Make gate-hook structure more readable and extensible * Don't disconnect br-int from phys br if connected * Refactor test\_ipam functional testing module * Avoid referencing code from master branch * Support interface drivers that don't support mtu parameter for plug\_new * Use tempest plugin interface * Add 169.254.169.254 when enable force\_metadata * Fix deprecation warning for external\_network\_bridge * Add ALLOCATING state to routers * Change wrong word "propogated" to "propagated" * Delete conntrack entry on the other direction * Skip l2\_ovs\_agent functional tests * Added initial release check list * Skip firewall blink test for ovs-fw * Fix doc build if git is absent * Cleanup stale OVS flows for physical bridges * Updated from global requirements * Revert "Add 169.254.169.254 when enable force\_metadata" * Fix broken Tempest conf options in API tests * Add functional test for device\_exists() with invalid namespace * Add an option for WSGI pool size * Add support for building ovs from git sources on stacking * Make tunnel\_sync logging less verbose * Notify resource\_versions from agents only when needed * ADDRESS\_SCOPE\_MARK\_IDS should not be global for L3 agent * Wrap all update/delete l3\_rpc handlers with retries * Cleanup override of compare\_type from oslo\_db * Notify resource\_versions from agents only when needed * Values for [ml2]/physical\_network\_mtus should not be unique * Use new DB context when checking if agent is online during rescheduling * Remove Openflow Agent(OFAgent) mechanism driver * Add RECLONE support for ovs * ovsfw: Load vlan tag from other\_config * ovsfw: Load vlan tag from other\_config * Reuse to\_utf8() and jsonutils.dump\_as\_bytes() * Postgresql: add do\_drop arg in alter\_enum function * Imported Translations from Zanata * firewall: don't warn about a driver that does not accept bridge * Add uselist=True to subnet rbac\_entries relationship * Add 169.254.169.254 when enable force\_metadata * RPC: remove unused parameter * Fullstack connectivity: test interface scenario added * Only load timestamp service plugin in timestamp tests * Deprecate 'ovs\_use\_veth' and 'veth\_mtu' options * Get rid of unnecessary \_ha\_routers\_present check * Clear DVR MAC on last agent deletion from host * Iptables firewall prevent IP spoofed DHCP requests * ovo: Introduce standard attributes to objects * Tempest 11.0.0 is released * Fix race conditions in IP availability API tests * Switched from fixtures to mock to mock out starting RPC consumers * Imported Translations from Zanata * Imported Translations from Zanata * Use new DB context when checking if agent is online during rescheduling * Add quota tests in unit tests * Refactor IPAM ip allocation method * Fix zuul\_cloner errors during tox job setup * When checking if there is a duplicate rule ignore the id field * Revert "Add instrumentation devref, Part I" * Return oslo\_config Opts to config generator * Refactor and fix dummy process fixture * Switches metering agent to stateless iptables * Remove obsolete keepalived PID files before start * Add IPAllocation object to session info to stop GC * Ensure metadata agent doesn't use SSL for UNIX socket * Fix Windows IPDevice.device\_has\_ip racefulness * Switched from fixtures to mock to mock out starting RPC consumers * Values for [ml2]/physical\_network\_mtus should not be unique * Fix regexp for ss output * Integrate the Extra Dhcp Opt VersionedObject in Neutron * ADDRESS\_SCOPE\_MARK\_IDS should not be global for L3 agent * Move all link-local cidr constants to a central location * DVR: Increase the link-local address pair range * Instantiate Enum() rather than subclassing * Fix keepalived functional tests * Always call ipam driver on subnet update * objects: avoid validation in stringify for custom field types * Remove unused PolicyFileNotFound exception * Add custom OVO field type for MAC address * ovo: Update object with data from db on update() * Add ALLOCATING state to routers * Fix race conditions in IP availability API tests * DVR: Increase the link-local address pair range * Make auto allocate plugin safe for unit/func tests * Define context.roles with base class * Ignore timestamp fields in model update * standard attributes: expose created\_at/updated\_at on models * Updated from global requirements * SG protocol validation to allow numbers or names * Define localstatedir for ovs compilation * L3 agent: match format used by iptables * Partial revert "DVR: Fix issue of SNAT rule for DVR with floating ip" * Add semaphore to ML2 create\_port db operation * OVS: Add mac spoofing filtering to flows * Use right class method in IP availability tests * Don't drop 'protocol' from client supplied security\_group\_rule dict * L3 agent: match format used by iptables * Make L3 HA interface creation concurrency safe * Updated from global requirements * ovo: Make return values from objects db api consistent * ovsfw: Remove vlan tag before injecting packets to port * Fix invalid mock name in test\_ovs\_neutron\_agent * lb: interface name hashing for too long vlan interface names * Imported Translations from Zanata * QoS DSCP use mod\_flow instead of mod\_flows * Change get\_root\_helper\_child\_pid to stop when it finds cmd * API tests: Check extensions with proper functions * Simplify chained comparison * Improve handle port\_update and port\_delete events in ovs qos agent * Cleaned up tox\_install.sh * devref: Update ovs-firewall * ovsfw: Remove vlan tag before injecting packets to port * Updated from global requirements * Use right class method in IP availability tests * Fix zuul\_cloner errors during tox job setup * Increase ports per network and add SLA for rally * test\_network\_ip\_availability: Skip IPv6 tests when configured so * Iptables firewall prevent IP spoofed DHCP requests * objects: SubnetPool, SubnetPoolPrefix * Don't disconnect br-int from phys br if connected * De-dup user-defined SG rules before iptables call * Ensure bridge names are shorter than max device name len * Retry updating agents table in case of deadlock * Improve error when exclusive resource allocation fails * Cleanup stale OVS flows for physical bridges * Avoids logging error on ML2 OVS agent start * Allow to use several nics for physnet with SR-IOV * Log fullstack allocation and release of exclusive resources * Hacking rule to check i18n usage 8.0.0 ----- * Support Routes==2.3 * Rename first argument to extend\_dict hook correctly * port security: gracefully handle resources with no bindings * Switches metering agent to stateless iptables * Wrap all update/delete l3\_rpc handlers with retries * Allow to use several nics for physnet with SR-IOV * Fix error format message * Ignore exception when deleting linux bridge if doesn't exist * Refactor and fix dummy process fixture * Don't delete br-int to br-tun patch on startup * Constraint requirements using mitaka upper-constraints.txt file * functional: Update ref used from ovs branch-2.5 * Imported Translations from Zanata * Don't delete br-int to br-tun patch on startup * Delete Devref Enable Service for Network IP Availability * DVR: rebind port if ofport changes * Support Routes==2.3 * ovs-fw: Mark conntrack entries invalid if no rule is matched * l3: Send notify on router\_create when ext gw is specified * l3: Send notify on router\_create when ext gw is specified * Remove obsolete keepalived PID files before start * Imported Translations from Zanata * SG protocol validation to allow numbers or names * Linux Bridge: Add mac spoofing filtering to ebtables * Remove redundant FLOATINGIPS declaration * Retry updating agents table in case of deadlock * Add fullstack cross-process port/ip address fixtures * Make L3 HA interface creation concurrency safe * Imported Translations from Zanata * Effective Neutron: some notes about deprecation * test\_network\_ip\_availability: Skip IPv6 tests when configured so * api tests: Check correct extensions * DVR: rebind port if ofport changes * Remove deprecated methods from migration CLI file * Fix typos in Neutron documentation * Make network segment table available for standalone plugin 8.0.0.0rc2 ---------- * api tests: Check correct extensions * devref: Remove stale description about network\_ip\_availability * Imported Translations from Zanata * Add db migrations test framework with data * Remove unnecessary executable permissions * Removes redundant "and", "this" and "the" * Tweak process to improve handling of RFEs * port security: gracefully handle resources with no bindings * Fix setting peer to bridge interfaces * Add fullstack connectivity test with ovsdb native interface * firewall: don't warn about a driver that does not accept bridge * Fix fullstack LB agent not connecting to rabbit * Ignore exception when deleting linux bridge if doesn't exist * Fix setting peer to bridge interfaces * Note fullstack depends on released neutronclient * Updated from global requirements * Skip fullstack L3 HA test * Imported Translations from Zanata * Clean imports in code * Add OVS flow tests * Adopt oslo.log default\_log\_levels * Common utils: remove deprecated methods * Fix test failure against latest oslo.\* from master * Fix reference to uninitialized iptables manager * AGENTS: remove deprecated methods * Fix reference to uninitialized iptables manager * DVR: Fix issue of SNAT rule for DVR with floating ip * Remove deprecated method from agent utils file * ovs-fw: Mark conntrack entries invalid if no rule is matched * Add fullstack logging * Fix alembic 'No handlers could be found for logger...' * Move db query to fetch down bindings under try/except * Close XenAPI sessions in neutron-rootwrap-xen-dom0 * Watch for 'new' events in ovsdb monitor for ofport * conn\_testers: Bump timeout for ICMPv6 echo tests * Port Security added to OVO * Adds Enum fields for different types * Removes host file contents from DHCP agent logs * Removing 'force\_gateway\_on\_subnet' option * Imported Translations from Zanata * Fix a couple of problems with random hex string generation * Fix latest doc error * milestone-review-dash detect current milestone * Remove deprecated methods from common file * DB: remove method \_get\_tenant\_id\_for\_create * use separate device owner for HA router interface * QOS: Provide get methods for policy & network/port binding * Fix spelling mistake * Fixes typo * Imported Translations from Zanata * functional: Update ref used from ovs branch-2.5 * neutron-db-manage: revision: fail for --autogenerate and branch * Enforce alembic branch when creating scripts manually * Ovs agent can't start on Windows because of validate\_local\_ip * Fix wrong use of list of dict in \_check\_driver\_to\_bind * Fixed typos in wsgi.py * Imported Translations from Zanata * objects: introduce object for extra dhcp options * Imported Translations from Zanata * Ovs agent can't start on Windows because of validate\_local\_ip * Update reno for stable/mitaka * Skip fullstack L3 HA test * objects: minor cleanup in base class * Close XenAPI sessions in neutron-rootwrap-xen-dom0 * Remove local variable named 'meh' * Move qos\_dscp migration to the Newton alembic branch * Add Newton branch to neutron-db-manage * Accept icmpv6 as protocol of SG rule for backward compatibility * conn\_testers: Bump timeout for ICMPv6 echo tests * DSCP QoS rule implementation * objects: fixed base to\_dict implementation * Updated from global requirements * Watch for 'new' events in ovsdb monitor for ofport * Update devstack plugin for dependent packages * Move db query to fetch down bindings under try/except * Accept icmpv6 as protocol of SG rule for backward compatibility * Outerjoin to networks for port ownership filter * Imported Translations from Zanata * Update devstack plugin for dependent packages * Remove test\_external\_network\_visibility * Outerjoin to networks for port ownership filter * Update .gitreview for stable/mitaka 8.0.0.0rc1 ---------- * tests: register all objects before validating their hash versions * Handle synthetic fields in NeutronDbObject * Prevent all primary keys in Neutron OVOs from being updated * Add uselist=True to subnet rbac\_entries relationship * De-dup conntrack deletions before running them * Imported Translations from Zanata * Fix auto\_allocated\_topology migration with PostgreSQL * Removes host file contents from DHCP agent logs * Fix add\_is\_default\_to\_subnetpool migration * Add custom SQLAlchemy type for MACAddress * DVR: Handle unbound allowed\_address\_pair port with FIP * Improve release notes for dvr fixes * Use context manager in bind NetworkDhcpAgent * Add custom SQLAlchemy type for CIDR * Add custom SQLAlchemy type for IP addresses * Fixes force to set ondelete=CASCADE in create\_foreign\_keys() * Fixes "OVS Agent doesn't start on Windows" * RBAC: Fix port query and deletion for network owner * DVR: Agent side change for live migration with floatingip * DVR:Pro-active router creation with live migration * Return oslo\_config Opts to config generator * Update testing coverage document * devref doc config option separation * Added test cases for DVR L3 schedulers * Update Neutron with temporary registry pattern from VersionedObjectRegistry * Reset RNG seed with current time and pid for each test started * Create a hook in base object to modify the fields before DB operations * Add API test ensure tenant can't delete other ports * Add IPAllocation object to session info to stop GC * SG PRECOMMIT\_CREATE should be triggered only once * fullstack: use SIGTERM when stopping ovs agents * Extend dicts with original model in create/update * Imported Translations from Zanata * Fix dict.keys() incompatibility in py34 in dhcp test * Update network object in DHCP agent with router interface changes * Block delete\_(network|subnet) transactioned calls * Imported Translations from Zanata * ADD API tests for network ip availability * Pecan: Allow unauthenticated version listing * L3HA: Do not wrap create/delete in transaction * Add metrics notifier to Pecan * Fix latest doc errors that crept in * Add remote vs local FAQ for callbacks * Revise deputy instructions to include deprecation warnings * Add deprecation tag * Remove test\_external\_network\_visibility * register the config generator default hook with the right name * Stops update\_network handling updates it shouldn't * Fix PUT tag failure * Remove unused pngmath Sphinx extension * fullstack: increase test timeout * DHCP: Downgrade 'network has been deleted' logs * Fix the context passed to get\_subnets in \_validate\_routes * Add reno for deprecation of max\_fixed\_ips\_per\_port * ML2: Downgrade 'no bound segment' warning * Delete 118~ API tests from Neutron * Using LOG.warning replace LOG.warn * policies: add an official 'l3-bgp' bug tag * linuxbridge: added missing space in option help text * Check tap bridge timestamps to detect local changes * Remove unused Tempest AdminManager * Construct exceptions before passing to retryrequest * Copy tempest.common.tempest\_fixtures in to Neutron * Queries for DVR-aware floating IP next-hop lookups * Adds unit tests for external DNS integration * Fixes external DNS driver failure with Python 3.4 * Updates external DNS service if IP address changes * Add logging statements to help debug L3 sync * Only clear dns\_name when user specifies parameter * Catch DB reference errors in binding DVR ports * Add BGP Callback and agent RPC notifcation implementations * Set DEFAULT\_NETWORK\_MTU to 1500 and use it * Downgrade network not found log in DHCP RPC * Downgrade "device not found" log message * Add global\_physnet\_mtu and deprecate segment\_mtu * Ensures DNS\_DRIVER is loaded before it is checked * Add Nova notifier hook calls to pecan * Add fip nat rules even if router disables shared snat * Add timestamp changed-since for core resources * Security groups: ensure correct ID is passed to exception * Pecan routing for agent schedulers * Use testscenarios for OVS flow tests * Tag the alembic migration revisions for Mitaka * Remove unused -constraints tox targets * constraints: fixed typo in tox\_install.sh * security-groups: Add ipv6 support to ovs firewall * Fix tempest lib import in API tests * Delay description association proxy construction * Release notes: fix broken release notes * Fix API test for external subnet visibility * Release notes: prelude items should not have a - (aka bullet) * Use floating IP to connect different address scopes * Add a description field to all standard resources * Add timestamp for neutron core resources * Skip racey BGP tests * Continue the fwaas decoupling and cleanup * Remove obsolete todo * Nit: Occurances of OpenStack * Make all tox targets constrained * reno: Fix bad yaml in reno that ruins html output * Mock out database access for QoS policy object interface tests * Fix branch order when upgrading to alembic milestone * Fix pecan collection->plugin map for extensions * Autogenerate macvtap agent config file * Updates to Bug Deputy section of Bugs DevRef * hacking: remove oslo.\* import check * devref: added details on the new l2 agent API mechanism * Revert "Revert "Functional test for address scope"" * Correct Pecan extensions test 8.0.0.0b3 --------- * Add Queries For BGP Route Lookups * Fix docs tox target for local runs * Improve logging for port binding * Allow auto-allocate's dry-run API call to work for regular users * Make OVS agent tunnel configuration logging less scary * make/update\_subnetpool\_dict call \_dict\_extend * Check if plugin supports starting rpc listeners * Make run\_ofctl check for socket error * unbreak unit test caused by c5fa665de3173f3ad82cc3e7624b5968bc52c08d * Add filter for resource tag * Add tag mechanism for network resources * Make API framework more flexible for various extensions * Moved CORS middleware configuration into oslo-config-generator * Objects DB api: added composite key to handle multiple primary key * IP Availability: remove unused imports * BGP: remove unnecessary configuration setting * Add support for QoS for LinuxBridge agent * RPC Callback rolling upgrades reporting, and integration * Set veth\_mtu default to 9000 * Provide dry-run flag to validate deployment requirements * Use network RBAC feature for external access * Deprecate network\_device\_mtu * Catch DBDuplicateEntry errors in RBAC code * DVR:Remove unwanted check in \_get\_dvr\_service\_port\_hostid * Make agent interface plugging utilize network MTU * Removed static reference to LinuxBridge in logging * Add API extension for reporting IP availability usage statistics * Updated from global requirements * Filter HA routers without HA interface and state * Translations: use neutron.\_18n instead of neutron.18n * Collect details on ARP spoof functional failures * Revert "Functional test for address scope" * Remove effectively empty directories * Added agent specific API support to L2 extensions * Qos policy RBAC DB setup and migration * macvtap: Macvtap L2 Agent * ML2: Increase segment\_mtu from 0 to 1500 bytes * Switch to using in-tree tempest lib * Catch DBReferenceError in HA router race conditions * Catch PortNotFound after HA router race condition * Change the exception type from ValueError to IpamValueInvalid * Fix test\_get\_device\_id() failure on OSX * Make \_\_table\_args\_\_ declarative in RBACColumns * Fix tox -e docs * Override addOnException to catch exceptions * BGP Dynamic Routing: introduce BgpDriver * Update default gateway in the fip namespace after subnet-update * Update docstring in test/tools.py * Pecan: filter items need type conversion * Pecan: use reservations in quota enforcement hook * Add use\_default\_subnetpool to subnet create requests * Ensure DVR unit tests use '/tmp' directory * API test for get-me-network * ovs-fw: Enhance port ranges with masks * Fix sanity check --no\* BoolOpts * Correlate address scope with network * Fix generate\_records\_for\_existing in migrations * Revert "tests: Collect info on failure of conn\_tester" * Updated from global requirements * Revert the unused code for address scope * Deprecate 'force\_gateway\_on\_subnet' configuration option * Fix 'TypeError: format requires a mapping' in OVS agent * Allow non-admins to define "external" extra-routes * Don't assume simplejson in pecan exception catch * IPAM: add missing translation * Functional test for address scope * deprecated: Raise message * Allow address pairs to be cleared with None * Document the ability to load service plugins at startup * .testr.conf: revert workaround of testtools bug * Add fullstack resources for linuxbridge agent * Pecan: get rid of member action hook * Pecan: replace dashes with underscores on controller lookup * Fix for adding gateway with IP outside subnet * Allow other extensions to extend Securitygroup resources * Adopt Grafana to plot Neutron Failure Rates * BGP Dynamic Routing: introduce BgpDrAgent * Add missing character * stadium: revise the introduction to the document * stadium: Add a guideline about contributor overlap * Security group: use correct logging format * Update devstack hooks to work with pecan jobs * Fix typo error for wrong msg format when CallbackFailure * Stop using non-existent method of Mock * Fix GROUP BY usage for PostgreSQL in migrations * Add bug tag for auto allocated topology * macvtap: ML2 mech driver for macvtap network attachments * Don't disable Nagle algorithm in HttpProtocol * Preserve subnet\_create behavior in presence of subnet pools * Open vSwitch conntrack based firewall driver * Add VLAN tag info to port before applying SG initial setup * QOS: get rid of warnings for unit tests * Remove NEC plugin tables * DHCP: release DHCP port if not enough memory * Cleanup unused conf variables * Make DHCP agent scheduler physical\_network aware * Deprecate ARP spoofing protection option * tests: Use constants for icmp and arp in conn\_testers * Add to the neutron bug deputy directions * L3 agent: log traceback on floating ip setup failure * Add the rebinding chance in \_bind\_port\_if\_needed * Pecan: implement DHCP notifications in NotifierHook * Pecan: Always associate plugins with resource * Remove deprecation warnings * Get rid of UnionModel for RBAC * Add necessary executable permission * Updated from global requirements * Add precommit\_XXX event for security group and rules * Give the qos extension a friendly name * tests: Collect info on failure of conn\_tester * Address masking issue during auto-allocation failure * Fix typo 'indepedent' in alembic\_migration.rst * BGP Dynamic Routing: introduce BgpDrScheduler model * macvtap: Common functions and constants * Fix typo of dnsmasq * add arp\_responder flag to linuxbridge agent * Switch "dsvm-functional:" into same pattern as constraints * Add BGP Dynamic Routing DB Model and Basic CRUD * fullstack: Gracefully stop neutron-server process * Remove VPN installation plumbing * Remove vpnaas tests from the Neutron API tree * Make netns\_cleanup to purge resources of selected agent only * Add extension requirement in port-security api test * ML2: delete\_port on deadlock during binding * Start using neutron-lib for shared constants and exceptions * Remove fwaas tests from the Neutron API tree * Remove office-hours from Polcies docs index * Add the ability to load a set of service plugins on startup * ML2: Configure path\_mtu to default to 1500 bytes * Support MTU advertisement using IPv6 RAs * Pecan: wrap PUT response with resource name * Pecan: Controller and test refactor * stadium: Add a guideline related to project scope * stadium: Propose kuryr as an independent project * stadium: Separate proprietary interface projects * stadium: Add python-neutron-pd-driver * stadium: Group lbaas repos together * Remove PTL office hours * Bring back dvr routers autoscheduling * Fix getting agent id in linuxbridge agent * RPC Callback rolling upgrades logic * OVO common enum class for IPv6 modes * Move check\_dvr\_serviceable\_ports\_on\_host() to dvr scheduler * L3: enable plugin to decide if subnet is mandatory * Implement 'get-me-a-network' API building block * Test helpers to facilitate testing BGP dynamic routing * Fix logging error for Guru Meditation Report * HA for DVR - Neutron Server side code changes * IP\_LIB: fix indentations * idlutils: add in missing translations * sub\_project\_guidelines: Document the procedure to make a branch EOL * sub\_project\_guidelines: Remove "add tarball to launchpad" step * sub\_project\_guidelines: Update after direct-release default * Only restrict gateway\_ip change for router ports * Make add\_tap\_interface resillient to removal * Updated from global requirements * Remove flavors from the list of extensions provided by core plugin * Log warning message if get\_subnet\_for\_dvr fails * devstack: Fix check for blank prefix arg * ML2: Call \_dict\_extend in create\_(net|port) ops * Pecan: add tenant\_id to quota resource * Prevent binding IPv6 addresses to Neutron interfaces * Moving Common Agent into separate module * Add hacking check for assertEqual HTTP code * Pecan: Fix association of plugins with resources * Add missing periods * Postpone heavy policy check for ports to later * LB agent: Downgrade network not available message * Imported Translations from Zanata * Call Ryu's clean up function when ovs\_neutron\_agent.main terminates * Protect 'show' and 'index' with Retry decorator * Update related router when subnetpool change scope * Only ensure admin state on ports that exist * stadium: Update list of Neutron sub-projects * ML2: Update help text for path\_mtu * Correct dev documentation for has\_offline\_migrations command * Reno note regarding OVS DVR agent failure on startup * Fix regression in routers auto scheduling logic * Compile OVS for functional tests * Trigger dhcp port\_update for new auto\_address subnets * Correction of spelling * Get tempest via zuul-cloner if needed and it is available * Fix typo in SecurityGroup HTTP error message * DHCP: fix regression with DNS nameservers * Add address scopes support to the L3 agent * Get rid of marshall\_fdb\_entries * Correct insufficient name for external process in manager log * Fix port relationship for DVRPortBinding * Fix params order in assertEqual * Address i18n related deprecation warnings * nova-notifier: Change warning to debug * Warn about a gotcha in the sub-project process * ML2: update port's status to DOWN if its binding info has changed * Fix remove\_router\_from\_l3\_agent for 'dvr' mode agent * DHCP: add in missing space at the end of the line * Fix bug when enable configuration named dnsmasq\_base\_log\_dir * DVR: avoid race on dvr serviceable port deletion * Remove bindings of DVR routers to L3 agents on compute nodes * Only prevent l3 port deletion if router exists * Unmarshall portinfo on update\_fdb\_entries calls * Remove dead method delete\_dvr\_port\_binding * SR-IOV: Agent remove listen to network delete event * Use a thinner try/except in \_build\_cmdline\_callback * Fail if required extensions are missing * Add UniqueConstraint in L3HARouterAgentPortBinding * Delete Tempest fork, import from tempest and tempest\_lib * Add relationship between port and floating ip * Update translation setup for neutron subprojects * Fix required extensions mix-up * Uniquely identify tunnel interfaces for fullstack tests * DVR: Remove unwanted call to \_get\_routers while arp update * lb: ml2-agt: Separate AgentLoop from LinuxBridge specific impl * item allocator should return same value for same key * Set default value for dnsmasq\_local\_resolv to False * Improve autonested\_transaction * Rename confusing dvr\_deletens\_if\_no\_port * Bump AgentExtRpcCallback version to 1.1 * Raise RetryRequest on policy parent not found * create\_object should not add an ID if not present in the DB model * Add generated port id to port dict * Updated from global requirements * Support api\_workers option when using pecan * Elevate context for router lookups during floating IP association * Update alembic migration documentation * Add separate transport for notifications * Neutron review tool use message instead of topics for bugs * Increase default IPv6 router advertisement interval * Remove stale ofport drop-rule upon port-delete * Updated from global requirements * l3\_db: Check dns-integration extension * Add dns\_db to models/head.py * devref: Fix a typo in i18n.rst * Replace exit() by sys.exit() * Add missing index entry for external dns integration * l2pop rpc: Add a unit test for dualstacked port * Pecan: remove deprecated warning * RPC worker support for pecan server * Don't decide web\_framework before config parse * Remove unwanted NOTE from dvr\_local\_router * DVR: Fix Duplicate IPtables rule detected warning message in l3agent * Make advertisement intervals for radvd configurable * Fix module's import order * neutron-db-manage: add has\_offline\_migrations command * Add popular IP protocols for security group * Decorate methods in ExtensionDescriptor with abstractmethod * Updated from global requirements * Remove obsolete plugin stuff * External DNS driver reference implementation * Move helper methods to create resorces to test\_base * db\_api: handle db objects that don't have 'id' as primary key * Introduce new queries to return DVR routers for a host * Refactor remove\_router\_interface() for DVR * sriov-mech: Introduce a new VIF type for PF vnic type * Ensure that tunnels are fully reset on ovs restart * OVS agent should fail if it can't get DVR mac address * Python3: Fix using dictionary keys() as list * Add network\_update RPC into SR-IOV agent * Add L3 Notifications To Enable BGP Dynamic Routing * Fix check in \_validate\_ip\_address() to fail on OSX * Remove floatingip address only when the address has been configured * Use tools\_path/venv environment variables in install\_venv * fix \_validate\_shared\_update for dvr router ports * Rename new\_network to new\_network\_id * DVR: Add action specific functions for arp\_entry\_update * Fixed qos devstack service name (should be q-qos) for -plus gate hook * bump the min tox version to 2.3.1 * Updated from global requirements 8.0.0.0b2 --------- * Add more log when dhcp agent sync\_state * Imported Translations from Zanata * Fix docstring for check\_dvr\_serviceable\_ports\_on\_host * Fixes typos Openstack -> OpenStack * Add tests for RPC methods/classes * OVS agent set max number of attempts to sync failed devices * Don't sync all devices when some fail * Make Neutron attempt to advertise MTUs by default * Optimize get\_ports\_on\_host\_by\_subnet() dvr rpc handler * Do not remove router from dvr\_snat agents on dvr port deletion * Make object creation methods in l3\_hamode\_db atomic * Remove dead method \_get\_router\_ids * DVR: Optimize getting arp entry info * Add support for neutron-full and api jobs using pecan to gate\_hook * Updated from global requirements * Move L2populationDbMixin to module-level functions * Fix L3 HA with IPv6 * Fix the duplicated references * Add opnfv tag to the list of auto-complete tags * Refactor router delete processing * Revert "Change function call order in ovs\_neutron\_agent." * Remove LinuxBridge manager get\_local\_ip\_device arg * devref: added more details on rolling upgrade for notifications * Filter by device\_owner instead of iterating by all subnet ports * Make security\_groups\_provider\_updated work with Kilo agents * Introduce new query to return all hosts for DVR router * fix get\_ha\_sync\_data\_for\_host for non-dvr agent * dhcp: handle advertise\_mtu=True when plugin does not set mtu values * Retry port create/update on duplicate db records * doc: Update Ryu Ishimoto's IRC nick * Make neutron pecan server an option instead of binary * DVR: when updating port's fixed\_ips, update arp * Fix Linux bridge test\_report\_state\_revived failure on OSX * Prevent PD subnets with incorrect IPv6 modes * Added Keystone and RequestID headers to CORS middleware * Unify exceptions for assign router to dvr agent * Unify using assertIsInstance * HACKING: update HACKING.rst file to include latest changes * devstack: use stevedore entry point for flavor service plugin * Do not prohibit VXLAN over IPv6 * Updated from global requirements * tests: stop validating neutronclient in neutron-debug tests * Remove 'validate' key in 'type:dict\_or\_nodata' type * ML2: verify if required extension drivers are loaded * Add --dry-run mode to code review abandon tool * Fix typo in test path in Testing.rst * Fix floatingip status for an HA router * Fix URLs for pep8, and unit tests jobs * Static routes not added to qrouter namespace for DVR * Pass environment variables of proxy to tox * Pecan: fix quota management * Pecan: Fixes and tests for the policy enforcement hook * gate\_hook: add support for dsvm-plus job type * Scope get\_tenant\_quotas by tenant\_id * Add 'ovs' to requirements.txt * Fix params order in assertEqual * Use admin context when removing DVR router on vm port deletion * eliminate retries inside of \_ensure\_default\_security\_group * Register RA and PD config options in l3-agent * Provide kwargs for callback abort * Pecan controller loads service plugins * Make sure datapath\_type is updated on bridges changed * Log INFO message when setting admin state up flag to False for OVS port * Fix regression with unbound ports and l2pop * L3 agent: paginate sync routers task * Remove duplicate for check\_ports\_exist\_on\_l3agent * ML2: Simplified boolean variable check * Pecan: Streamline request body processing * make floating IP specification test robust to races * Fix get\_subnet\_for\_dvr() to return correct gateway mac * Updated from global requirements * Ensure agent binding modules are loaded * portbindings: use constants for extension keys * Add README with links on how to create release notes * Ensure that decomposed plugins do not break * LBaaS tests code removal * Make neutron-debug command follow cliff command convention * Rename \_get\_vm\_port\_hostid in dvr to reflect the right functionality * DVR: Rename dvr\_vmarp\_table\_update * Remove unnecessary argument in limit manage * remove openstack-common.conf * Move notifications before DB retry decorator * Create a routing table manager * Fix uuid passing in disable\_isolated\_metadata\_proxy * Fix incorrect classmethod declaration * Add unit test cases for linuxbridge agent when prevent\_arp\_spoofing is True * Adopt oslotest BaseTestCase as a base class for DietTestCase * Use oslo.utils.reflection extract the class name * Utils: Add missing translation to exception * Unify assertEqual for empty usages * SR-IOV: Fix macvtap assigned vf check when kernel < 3.13 * Delete metadata\_proxy for network if it is not needed * Remove references to model\_base through models\_v2 * Allow get\_unused\_ip method to skip v6 and fix iter * Revert "Revert "Revert "Remove TEMPEST\_CONFIG\_DIR in the api tox env""" * Fix meter label rule creation * Remove l2pop \_get\_port\_infos method * Remove L2populationDbMixin parent * devstack: don't enable qos service with the plugin * Add test for Neutron object versions * SR-IOV agent: display loaded extensions * Imported Translations from Zanata * Allow tox to pass more arguments to ostestr command * Add systemd notification after reporting initial state * Avoid duplicating tenant check when creating resources * Add extension\_manager and support for extensions in linuxbridge agent * Fix API tests * Rule, member updates are missed with enhanced rpc * radvd prefix configuration for DHCPV6\_Stateful RA * DVR: Rename dvr\_update\_router\_addvm function * Support rootwrap sysctl and conntrack commands for non-l3 nodes * Remove openstack.common.\_i18n from Neutron * Kilo initial migration * Check missed ip6tables utility * Keep py3.X compatibility for urllib * Updated from global requirements * Misspelling in message * Mitigate restriction for fixed ips per dhcp port * dhcp: Default to using local DNS resolution * Fixing the deprecated library function * Remove unused variable use\_call in ovs-agent * Wrong usage of "an" * Wrong usage of "a" * Trival: Remove unused logging import * Allow to control to use constraint env for functional jobs * DVR: optimize check\_ports\_exist\_on\_l3\_agent() * Don't call add\_ha\_port inside a transaction * Call \_allocate\_vr\_id outside of transaction * Change log level from error to warning * Fix Security-rule's port should not set to 0 when Protocol is TCP/UDP * Add constant to L3 extension for floating ips * dibbler: fix import order * Add address scope to floating IPs in RPC response to L3 agent * Add firewall blink + remote SG functional tests * Add test cases to testing firewall drivers * Ignore non rules related qos\_policy changes * Remove check on dhcp enabled subnets while scheduling dvr * Run functional gate jobs in a constrained environment * update docstring for get\_ports\_on\_host\_by\_subnet * Correct state\_path option's help string * Updated from global requirements * Restore \_validate\_subnet/uuid\_list not to break subproject gates * Delete test\_restart\_l3\_agent\_on\_sighup * DVR: Remove get\_port call from dvr\_update\_router\_addvm * DVR:Fix \_notify\_l3\_agent\_new\_port for proper arp update * Add tests that constrain db query count * Don't raise if polling manager is running when stopped * Add abstractmethod to FirewallDriver abstract class * Add a link of availability zone document into releasenote * Corrected wrong ethertype exception message * Misspelling in message * Use the constant HOST\_ID instead of 'binding:host\_id' * Force L3 agent to resync router it could not configure * Provide pointer for ML2 extension manager to effective guide * Add notes on loading strategies for ORM relationships * Enable Guru Meditation Reports for other refarch agents * Updated from global requirements * Catch known exceptions during deleting last HA router * Add new troubleshooting bugs tag * Add to deprecate OFAgent in release note * Refactor the subnetpools API tests * Clean up code for bug1511311 * Kill the vrrp orphan process when (re)spawn keepalived * reject leading '0's in IPv4 addr to avoid ambiguity * Remove duplicated code in attribute.py * QOS: add in missing translation * Separate the command for replace\_port to delete and add * Fix comparison of Variant and other type in test\_model\_sync * Add check that list of agents is not empty in \_get\_enabled\_agents * Remove unused parameter from \_update\_router\_db method * Use a joined relationship for AZ info on routers * Cleanup all the release notes * Improve tox to show coverage report on same window * Tune \_get\_candidates for faster scheduling in dvr * Updating devref for networking-onos project * Use a joined relationship for AZ info on networks * Correct return values for bridge sysctl calls * Batch db segment retrieval * Separate rbac calculation from \_make\_network\_dict * Add explicit address family to AddressScope * DVR: handle dvr serviceable port's host change * Adding a VNIC type for physical functions * Add functional test for availability\_zone support * OVS: Reorder table-id constants * Deprecated tox -downloadcache option removed * API: \_validate\_ip\_address should not raise an exception * Removing adv svcs dependencies on neutron * Return availability\_zone\_hints as list when net-create * Decompose OFAgent mechanism driver from neutron tree completely * Ignore possible suffix in iproute commands * Add option for nova endpoint type * Force service provider relationships to load * Add linuxbridge job to the dashboard * ML2: Add tests to validate quota usage tracking * Updated from global requirements * Add explanations and examples to TESTING.rst * Added CORS support to Neutron * L3 DB: set get\_assoc\_data to be an internal method * ovs\_vhostuser: fix vhostuser\_socket\_dir typo * fix call which is only specific to enhanced\_rpc * select router with subnet's gateway\_ip for floatingip * Refactor OVS-agent tunnel config validate * Make keepalived add\_vip idempotent * Fix timestamp in RBAC extension * Document relationship between ways of documenting new stuff * lb: Correct String formatting to get rid of logged ValueError * Skip keepalived\_respawns test * Add release note covering keystoneauth and v3 * Pull project out of request in addition to tenant * Don't emit confusing error in netns-cleanup * Add address scope to ports in RPC response to L3 agent * Updated from global requirements * Avoid full\_sync in l3\_agent for router updates * move usage\_audit to cmd/eventlet package * Use keystoneauth instead of keystoneclient * Deprecate \_ builtin translation function * Use \_ from neutron.\_i18n * Using substitution for Python String * Tox: Remove fullstack env, keep only dsvm-fullstack * Fix some inconsistency in docstrings * Set timetable for removal of oslo.messaging.notify.drivers * Delete stale neutron-server manual * Final decomposition of the nuage plugin * Final decomposition of Brocade vendor code * Trivial typo fix in LinuxBridge dashboard * Add a script to create review dashboard for a milestone * Remove Neutron core static example configuration files - addition * test\_migrations: Avoid returning a filter object for python3 * Cleanup veth-pairs in default netns for functional tests * Reuse constants defined in attributes * Add availability\_zone support for router * Fix default RBAC policy quota * Moved fullstack test doc content to TESTING.rst * Allow multiple imports for both ways of doing i18n * [policy] Clarify bug deputy does not require core bit * Run NOT NULL alterations before foreign key adds * Do not autoreschedule routers if l3 agent is back online * Add instrumentation devref, Part I * Updated from global requirements * Hyper-V: remove driver from the neutron tree * Fix typo in Docstring * Remove lbaas cruft from neutron gate\_hook * Make port binding message on dead agents clear * Notify about port create/update unconditionally * HACKING: fix edge case with log hints * I18n related guideline for subprojects * Optimize "open" method with context manager * L3: add missing space to log message * Revert "Revert "OVS agent reacts to events instead of polling"" * XenAPI: Fix netwrap to support security group * Move i18n to \_i18n, as per oslo\_i18n guidelines * Clean up FIP namespace in DVR functional tests * devref: Rolling upgrade mechanism for rpc-callbacks * Remove version from setup.cfg * DVR:don't reschedule the l3 agent running on compute node 8.0.0.0b1 --------- * Add native of\_interface fullstack tests * Disallow updating SG rule direction in RESOURCE\_ATTRIBUTE\_MAP * l3\_db: it updates port attribute without L2 plugin * In port\_dead, handle case when port already deleted * Change check\_ports\_exist\_on\_l3agent to pass the subnet\_ids * lb: avoid doing nova VIF work plumbing tap to qbr * Remove Neutron core static example configuration files * Update 'Contributing Extensions' devref for Mitaka * HACKING: align the underline text and header * Imported Translations from Zanata * Remove transparent VLAN support from base plugin * Automatically generate neutron core configuration files * Support Unicode request\_id on Python 3 * Stop using deprecated timeutils.total\_seconds() * Correct unwatch\_log to support python <= 2.7.5 * Move a note to bridge\_lib * Add Guru Meditation Reports support to Neutron services * Fix alignment in message and remove unused module * Update toctree of neutron document * Don't drop ARP table jump during OVS rewiring * Remove useless lb-agent remove\_empty\_bridges * Delete HA network when last HA router is deleted * Change instances of Openstack to OpenStack * force releasenotes warnings to be treated as errors * Add availability\_zone support for network * fix some misspellings * Freescale ML2 driver code complete decomposition * Add Incomplete state to list of acceptable states for RFE bugs * Fix typo for OVSDB * Clarify how we milestones are assigned * Support for IPv6 RDNSS Option in Router Advts * tox: pass TEMPEST\_CONFIG\_DIR envvar into api target environment * Wait for the watch process in test case * Add UnionModel support to filter query generator * Minor doc fix in alembic\_migrations.rst * Some minor misspellings in comment block * Optimize router delete execution * Deprecate l3-agent router\_id option * Make Neutron resources reference standard attr table * devref: add upgrade strategy page * Remove duplicate deprecation messages for quota\_items option * Log error instead of exception trace * Refactor OVS-agent init-method * neutron-db-manage: mark several options as deprecated * ovs: Make interface name hashing algorithm common and extend it * Check gateway ip when update subnet * Use diffs for iptables restore instead of all rules * IPAM: add in missing exception translations * Remove BigSwitch plugin and driver * Add Access Control bug tag * Add index entry to vhost documentation * Make fullstack test\_connectivity tests more forgiving * Fix get\_subnet\_ids\_on\_router in dvr scheduler * Remove misplaced copyright attribution * Fix misspelled word in docstring * neutron-db-manage: expose alembic 'heads' command * Reorganize and improve l3\_agent functional tests * Make sure we return unicode strings for process output * Use compare-and-swap for IpamAvailabilityRange * Replace neutron-specific LengthStrOpt with StrOpt * Fix use of fields argument in get\_rbac\_policies * Updated from global requirements * Fix dashboard graphite URLs * Fix Neutron flavor framework * Keep reading stdout/stderr until after kill * Updated from global requirements * Fix the end point test for client * IPAM: fix 'enable-dhcp' with internal driver * Update HA router state if agent is not active * Send 50% less debug information when executing cmd * Fix alignment in message * Datapath on L2pop only for agents with tunneling-ip * Add hosted agents list to dhcp agent scheduler * Add vhost-user support via ovs capabilities/datapath\_type * Remove deprecated use\_namespaces option * Resync L3, DHCP and OVS/LB agents upon revival * Ensure metadata agent doesn't use SSL for UNIX socket * Updated from global requirements * Add networking-infoblox sub-project * Firewall: fix typo * Fix the indentation issue * Elaborate how priorities are assigned to blueprints * Don't add default route to HA router if there is no gateway ip * Add a better description for notification\_driver * Use DEVICE\_OWNER\_\* for 'network:\*' constants * Add the missing arg of RetryRequest exception in \_lock\_subnetpool * Update networking-powervm sub-project docs * Remove unused delete\_dvr\_mac\_address method * Add fullstack testing doc content * Fix releasenotes/../unreleased.rst * Avoid race condition for reserved DHCP ports * Revert "Move dhcp\_lease\_duration into DHCP agent config options list" * sub\_projects.rst: Update midonet functionalities * Switch to using neutron.common.utils:replace\_file() * Trivial fix in ml2 conf * Remove the useless l3plugin check in l3\_rpc.py * Fix some reST field lists in docstrings * Use DEVICE\_OWNER\_COMPUTE constant everywhere * Fix broken references in doc * Skip bindings with agent\_id=None * Updated from global requirements * Use admin context when requesting floating ip's router info * Cleanup dhcp namespace upon dhcp setup * Use SIGUSR1 to notify l3 agent of changing prefix file * Last sync from oslo-incubator * Remove SysV init script for neutron-server * Refactor test\_server functional tests * Undeprecate force\_gateway\_on\_subnet option * Move dhcp\_lease\_duration into DHCP agent config options list * Add transaction for setting agent\_id in L3HARouterAgentPortBinding * Check missed IPSet utility using neutron-sanity-check * Change the repos from stackforge to OpenStack * Revert "Revert "Remove TEMPEST\_CONFIG\_DIR in the api tox env"" * Require tox >= 2.0 * Use assertFalse(observed) instead of assertEqual(False, observed) * Fix heading markers for better docment toc view * Clarify that RFE bug reports should not have an importance set * Remove TEMPEST\_CONFIG\_DIR in the api tox env * Revert "Remove TEMPEST\_CONFIG\_DIR in the api tox env" * Lower l2pop "isn't bound to any segement" log to debug * DVR: remove redundant check * Disable IPv6 on bridge devices in LinuxBridgeManager * More graceful ovs-agent restart * sriov: add extensions option to configuration file * Fix dvr\_local\_router.floating\_ip\_added\_dist failure after agent restart * Don't use duplicate filter names for functional testing * Replace get\_all\_neutron\_bridges by get\_deletable\_bridges * Revert "OVS agent reacts to events instead of polling" * configure\_for\_func\_testing.sh: Fix arguments for get\_packages * Add call to pluggable IPAM from ml2 delete\_subnet * Add "unreleased" release notes page * Final decomposition of opendaylight driver * Adding security-groups unittests * Don't snat traffic between fixed IPs behind same router * Remove MidonetInterfaceDriver * Update internal snat port prefix for multiple IPv6 subnets * Use get\_interface\_bridge instead of get\_bridge\_for\_tap\_device * Move LinuxBridge related features to bridge\_lib * Reduce duplicated code in test\_linuxbridge\_neutron\_agent * Document the neutron-release team * Updated from global requirements * Trivial fix in l3 agent * IPAM: make max fixed IP validations DRY * Fix misuse of log marker functions in neutron * More instructions for neutron-db-manage revision --autogenerate * Add in missing spaces at end of line * Do not use log hints for exceptions * Fix notification driver package * Adding a function prefix before parenthesis * Make command log in neutron utils.execute() a single line * move import to top and rename to make more readable * Move update\_fip\_statuses to Router class * Replace subnetpool config options with admin-only API * Add new config option for IPv6 Prefix Delegation * Correction and clarification to subproject stable guidelines * Make '\*' the default ml2 flat\_networks configuration * Add PyPI link for networking-calico * Deprecate new= argument from create\_connection * OVS agent reacts to events instead of polling * Remove default=None for configuration bindings * Log hints should only be used for log messages * Add reno for release notes management * Add a note about the Neutron Bugs team in Launchpad * Update deprecated messages * Switch to using neutron.common.utils:replace\_file() * Change function call order in ovs\_neutron\_agent * Ensure to decode bytes or fail * Optimize delete\_csnat\_router\_interface\_ports db query * Make string representation of DictModel generic * Add IRC part for effective neutron * PortOpt cleanups * Fix QoS VALID\_RULE\_TYPES location in devref * Docs: clarify that AnySubnetRequest is optional * Update neutron-debug to use stevedore aliases * Fix incorrect passing port dict in pluggable IPAM * Per-branch HEAD files for conflict management * Replace internal oslo\_policy mock with public fixture * sub\_project\_guidelines.rst: Clarify stable branch creation for subprojects * Use a more pythonic string construction * Add ops tag to bugs policy * IPSet Manager: make code more pythonic * Imported Translations from Zanata * Remove deprecated nova\_\* options * Fixed a bunch of typos throughout Neutron * Decompose ML2 mechanism driver for Mellanox * Add text for deprecated parameter * Clarify with example mentioning gratuitous whitespace changes * Removes the use of mutables as default args * Decompose ML2 mechanism driver for OVSvApp * Fix usage of mutable object as default value * Make the Neutron Stadium documentation toplevel * Add notes about stable merge requirements for sub-projects * Fix incorrect capitilization of PyPI * Updated from global requirements * Log end of router updates for PD and delete branches * Don't update metadata\_proxy if metadata is not enabled * Imported Translations from Zanata * DHCP agent: log when reloading allocations for a new VM port * Update specs backlog directory * Log the exception in linuxbridge\_neutron\_agent as exception * Replace utils.exec for IpNeighComm LinuxBridge drv * Formatting exception messages * Optimize get\_bridge\_for\_tap\_device * Optimize interface\_exists\_on\_bridge * Correct indentation in linuxbridge\_neutron\_agent * Use oslo\_config new type PortOpt for port options * Updated from global requirements * DVR: only notify needed agents on new VM port creation * Ensure l3 agent receives notification about added router * Imported Translations from Zanata * Support migrating of legacy routers to HA and back * Use string formatting instead of string replace * Delete fipnamespace when external net removed on DVR * Better tolerate deleted OVS ports in OVS agent * Remove GBP as a Neutron sub-project * get\_device\_by\_ip: don't fail if device was deleted * Allow to specify branch for creating new migration * Mark for removal deadcode in neutron.common.utils * Adds base in-tree functional testing of the dhcp agent (OVS) * Fix \_restore\_local\_vlan\_map race * DVR: notify specific agent when deleting floating ip * Move test\_extend\_port\_dict\_no\_port\_security to where it belongs to * Fix the latest glitches that broke docs generation * Add effective note on DB exception to be aware of * Minor improvement in port\_bound operation * Introduce an API test for specified floating ip address * Clarify what gerrit repositories can target neutron-specs * Fix error code when L3 HA + DVR router is created or updated * Spawn dedicated rpc workers for state reports queue * Fix l2pop regression * Remove deprecated sriov agent\_required option * Remove deprecated namespace deletion options * Deepcopy port dict in dhcp rpc handler * Don't remove ip addresses if not master * Include alembic versions directory to the package * Fix formatting of hyperlinks provided in the office-hours doc * Remove IBM SDN-VE left-overs * Remove the port-forwarding sub-project from the list * Set security group provider rule for icmpv6 RA in DVR * Properly handle segmentation\_id in OVS agent * ovs: remove several unneeded object attributes from setup\_rpc() * Set ip\_nonlocal\_bind in namespace if it exists * Remove SUPPORTED\_AGENT\_TYPES for l2pop * DVR: Notify specific agent when update floatingip * Move some projects url from cgit/stackforge to cgit/openstack * Remove non-existent enable\_tunneling conf from fullstack * Update notes about the Neutron teams * Validate ethertype for icmp protocols * Refactor \_populate\_ports\_for\_subnets for testability * Split the FIP Namespace delete in L3 agent for DVR * Add stevedore aliases for interface\_driver configuration * Register oslo\_service.wsgi options correctly * ovs\_neutron\_agent: display loaded extensions * Improvements to the blueprint management process * Add a note to ban agents from connecting to the DB * Revert "DVR: Notify specific agent when update floatingip" * Imported Translations from Zanata * Fix DVR downgrade exception / error code * Fix AttributeError on port\_bound for missing ports * The exception type is wrong and makes the except block not work * Fix rendering * DVR: Notify specific agent when update floatingip * Do not try to delete a veth from a nonexistent namespace * Do not accept abbreviated CIDRs * Spelling and grammar corrections * Cross link sub-project release processes * Lower the log level for the message about concurrent port delete * Updated from global requirements * Update RFE documentation to clarify when the tag is not appropriate * Cache the ARP entries in L3 Agent for DVR * Revert "Make OVS interface name hashing algorithm common and extend it" * Enable specific extra\_dhcp\_opt to be left blank * Python 3: skip test\_json\_with\_utf8 on Py3 * test\_create\_network\_segment\_allocation\_fails: Assert the status * The first word of the error message should be capitalized * Create ipset set\_name\_exists() method * Add section for code review in effective neutron * Add -constraints sections for base CI jobs * Python 3: make post\_test\_hook work with more tox targets * Remove useless code in L3 HA unit tests * Move retries out of ML2 plugin * Include external bridge deprecation warning in string * Tweak RFE guidelines * Fix link in devref guide * Add ml2 extension drivers examples * Improve performance of ensure\_namespace * Kill conntrackd state on HA routers FIP disassociation 7.0.0 ----- * Mock oslo policy HTTPCheck instead of urllib * Avoid DuplicateOptError in functional tests * Make test\_server work with older versions of oslo.service * Always send status update for processed floating ips * Fix inconsistency in DHCPv6 hosts and options generation * L3 agent: use run\_immediately parameter to sync after start * test\_db\_base\_plugin\_v2: Skip a few tests on some platforms * Fix error returned when an HA router is updated to DVR * Remove disable\_service from DBs configuration * Replaced deprecated isotime() function * DVR: notify specific agent when creating floating ip * Fix the bug of "Spelling error of a word" * Fix iptables modules references in rule generation * Remove the embrane plugin * Fix functional test\_server tests * Add deadlock warning to 'effective neutron' * Quick optimization to avoid a query if no ports have fixed ips * Add OpenFixture and get rid of 'open' mocks * Use assertTrue(observed) instead of assertEqual(True, observed) * Imported Translations from Zanata * QoS SR-IOV: allow to reset vf rate when VF is assigend to VM * Add track\_quota\_usage conf into neutron.conf * Only lock in set\_members on mutating operations * Add pointers to access Neutron test coverage details * Consume ConfigurableMiddleware from oslo\_middleware * Remove excessive fallback iptables ACCEPT rules * Consume sslutils and wsgi modules from oslo.service * test\_create\_router\_gateway\_fails fixes * Code refactor for generating integer in testcase * Effective: avoid mocking open() if you can * Cleaned up remaining incorrect usage for LOG.exception * Remove usage of WritableLogger from oslo\_log * Fixed multiple py34 gate issues * Removed release\_port\_fixed\_ip dead code * Validate local\_ip for linuxbridge-agent * Removed neutronclient option from metadata agent * Adding headers to the devref docs 7.0.0.0rc2 ---------- * DHCP: protect against case when device name is None * Add testresources used by oslo.db fixture * Add the functional-py34 and dsvm-functional-py34 targets to tox.ini * Improvements to the RFE management process * Mock oslo policy HTTPCheck instead of urllib * Add py34 tags to the list of official tags * Updated from global requirements * Fix rule generation for single and all host rules * Fix iptables comments for bare jump rules * Add another patch scoping bullet point to effective\_neutron * Removed a pile of debtcollector removals from neutron.context * L3 Agent support for routers with HA and DVR * Python 3: add classifiers * Adding Effective tips for plugin development * Add networking-bgpvpn lieutenants * Update gate dashboard URLs * Add some test guidelines to 'effective neutron' * Fix capitalization nit in patch 230218 * DHCP: protect against case when device name is None * Execute ipset command using check\_exit\_code * Add note in database section of 'effective neutron' * Correct MAC representation to match iptables output * Add note about negative feedback to 'effective neutron' * Add a note about agent/server compat to 'effective neutron' * Add a patch scope section to 'effective neutron' * Add a logging guideline to 'effective neutron' * Fix missing parent start() call in RpcWorker * Remove OneConvergence plugin from the source tree * Use assertIsNone(observed) instead of assertEqual(None, observed) * Document self.assertEqual(expected, observed) pattern * Move gateway processing out of init\_router\_port * Use assertIn and assertNotIn * Deprecate max\_fixed\_ips\_per\_port * Don't register agents for QoS l2pop fullstack test * The option force\_metadata=True breaks the dhcp agent * Updated from global requirements * Do not log an error when deleting a linuxbridge does not exist * The option force\_metadata=True breaks the dhcp agent * Decomposition phase2 for MidoNet plugin * Updated from global requirements * Changes in Neutron defect management * Tag the alembic migration revisions for Liberty * /common/utils.py py34 incompatibility * Just call set-manager if connecting fails * Fixes 'ovs-agent cannot start on Windows because root\_helper opt is not found' * Use format to convert ints to strings * Fixes 'ovs-agent fails to start on Windows beacause of SIGHUP' * usage\_audit: Fix usage\_audit to work with ML2 * Pecan: Fix quota enforcement * metadata: don't crash proxy on non-unicode user data * Do not log an error when deleting a linuxbridge does not exist * /common/utils.py py34 incompatibility * Remove debtcollector.removals tagged ensure\_dir * Consume service plugins queues in RPC workers * Imported Translations from Zanata * Add more commit msg tips to 'effective neutron' * Remove local variables from IPDevice.exists * Add availability\_zone support base * Pecan: Fix quota enforcement * metadata: don't crash proxy on non-unicode user data * Add neutron-linuxbridge-cleanup util * Effective Neutron: add link to low-hanging-fruit bugs * Effective Neutron: add link to logging guidelines * Add IPDevice.exists() method * Simplify L3 HA scheduler tests * Python 3: fix invalid operation on dict\_items objects * Use format to convert ints to strings * Add periodic agents health check * Imported Translations from Zanata * Fix db error when running python34 Unit tests * Remove OpenContrail plugin from the source tree * Correct cisco\_ml2\_apic\_contracts.router\_id length * Remove is\_ha property from the router * Remove log decorator deprecated in Liberty * Deprecate branchless migration chains from neutron-db-manage * Support new mitaka directory with revisions * Fix the bug of "Error spelling of 'accomodate'" * Just call set-manager if connecting fails * Check idl.run() return value before blocking * Use separate queue for agent state reports * Remove remaining uses of load\_admin\_roles flag in tests * Make OVS interface name hashing algorithm common and extend it * Simplify extension processing * Fix URL target problem * Add devref for alembic milestone tagging * Add compatibility with iproute2 >= 4.0 * Tag the alembic migration revisions for Liberty * api test: Skip address-scope tests when the extension is not enabled * Check idl.run() return value before blocking * Check supported subnet CIDR * Remove zombie pecan hook * Adding trailing underscores to devref links * Python 3: use "open" instead of "file" * Imported Translations from Zanata * Handle empty bridge case in OVSBridge.get\_ports\_attributes * Devref for authorization policies enforcement * Fixing traces of "Replace prt variable by port" * Kill HEADS file 7.0.0.0rc1 ---------- * Don't write DHCP opts for SLAAC entries * Cleanup of Translations * Cleanup of Translations * Move ConfigDict and ConfigFileFixture to neutron.tests.common * Turn device not found errors in to exceptions * Fix quota usage tracker for security group rules * Update default branch in .gitreview to stable/liberty * SimpleInterfaceMonitor: get rid of self.data\_received flag * Fixes 'ovs-agent fails to start on Windows beacause of SIGHUP' * Forbid more than one branch point in alembic dependency chains * Fix quota usage tracker for security group rules * Fixes 'ovs-agent cannot start on Windows because root\_helper opt is not found' * Imported Translations from Zanata * Fix a few nits with the dashboard pages * Open Mitaka development * Fix the broken link in devref docs * Eliminate autoaddress check for DNS integration * Only get host data for floating ips on DVR routers * Add neutron subproject & stable branch gerrit review links * Link dashboards into generated documentation * Add neutron/master review link to dashboard/index.html * Create dashboard page with gate jobs statistics * ml2: don't consider drivers with no bind\_port for qos supported rule types * Adds configurable agent type * Imported Translations from Zanata * Updated from global requirements * Relax service module check on service providers * Get rid of ConfigParser code in ProviderConfiguration * Rename check pipeline dashboards * tests: don't validate respawn as part of ovsdb monitor functional test * ovsdb monitor: get rid of custom \_read\_stdout/\_read\_stderr methods * Change ignore-errors to ignore\_errors * Change router unbinding logic to be consistent with data model * delete\_port: ensure quota usage is marked as dirty * Fix hostname roaming for ml2 tunnel endpoints * Execute ipset command using check\_exit\_code * Refactoring devstack script * Fix adding tap failure if bridge mapping is not provided * SubnetPoolsTest: Skip IPv6 tests appropriately * Remove an invalid comment * Fixes SNAT port not found for internal port * Don't write DHCP opts for SLAAC entries * Simplify join to rbac\_entries for subnets * Update \_TestModelMigration * Add --verbose to subset of cmds in neutron-db-manage * Use pecan controllers for routing * test\_networks: Stop assuming net-mtu extension * Imported Translations from Zanata * Add skeleton to 'Effective Neutron' devref * Introduce kill\_signal parameter to AsynProcess.stop() * Remove early yields in \_iter\_hosts in dhcp agent * Optimize if statement in dvr\_local\_router.py * Re-adds VIF\_TYPE\_VHOST\_USER to portbindings extension * Introduce a separate RPC server * Fix log statement to log correct variable first\_ip * Remove pecan branch reference from .gitreview file * ipam: Prevent none from being passed to delete * Remove restriction of adding constraints to expand * Delete unused file tests/unit/database\_stubs.py * No network devices on network attached qos policies * Revert "Revert "Pecan WSGI: prevent plugins from opening AMQP connections"" * Use tempest-lib's token\_client * Revert "Pecan WSGI: prevent plugins from opening AMQP connections" * Add constraint target to tox.ini * Fix establishing UDP connection * ovsdb: Fix a few docstring * Remove requirements.txt for the ofagent mechanism driver * Always return iterables in L3 get\_candidates * Remove plural param to QUOTAS.count * Return version info on version controller * Log exception.msg before exception.message * Fix pecan policy enforcement for GET requests * Add missing resource discriminator in update resp * Fix missing check for admin/adv\_service * Clarify and add a TODO in the controller * Set expected HTTP codes for create and delete * Add basic bulk support to collection controller * Prevent full sync in dhcp\_agent when possible * Remove duplicated API server * Add QoS fullstack test * QoS agent extension and driver refactoring * Add IPv6 Address Resolution protection * Revert "AsyncProcess: try to kill tender" * Remove out-of-tree vendor AGENT\_TYPE\_\* constant * func: Don't use private method of AsyncProcess * Remove unused ovs\_lib method reset\_bridge * Fix TypeError caused by delete\_agent\_gateway\_port() * sub\_project\_guidelines: Add richer documentation * Fix typo: Large Ops, not Large Opts * Fix query in get\_l3\_agent\_with\_min\_routers * Do not specify host for l2population topics * Add utility function for checking trusted port * Fix typo in error message in NetcatTester * docstring fix * AsyncProcess: try to kill tender * Enable servicing lbaasV2 vip by DVR * Switch scheduler drivers to load based schedulers * Fix BadRequest error on add\_router\_interface for DVR * Fix missing value types for log message * Tweak test\_keepalived\_respawns test logic * Reservations: Don't count usage if resource is unlimited * Restore reservations in API controller * ovs: don't use ARP responder for IPv6 addresses * Install sriov-agent.ini on 'setup.py install' * Configure gw\_iface for RAs only in Master HA Router * Remove useless log from periodic\_sync\_routers\_task * Replace is\_this\_snat\_host validation with internal function * Revert "Remove address scopes from supported extensions" * Add l2pop support to full stack tests * Add tunneling support to full stack tests * Remove an unused DVR function * Handle ObjectDeletedError when deleting network ports/subnets * OVSAgentTestFramework: Remove \_bind\_ports * Descheduling DVR routers when ports are unbound from VM * Updated from global requirements * Reduce the chance of random check/gate test failures * Allow passing arbitrary ip route parameters to add/delete\_route * Make ip address optional to add\_route and delete\_route * Add list routes * Fix dvr update for subnet attach multi subnets * Make ip rule comparison more robust * Remove hack for discovery novaclients extension * Check ICMP codes in range [0,255] * Remove address scopes from supported extensions * Add test to check that correct functions is used in expand/contract * SR-IOV: devstack support for SR-IOV agent * Fix test\_external\_tables\_not\_changed * Delete gateway conntrack state when remove external gateway * Updated from global requirements * Add non-model index names to autogen exclude filters * Implement expand/contract autogenerate extension * Cleanup the fip agent gateway port delete routines * Add RPC command and delete if last FIP on Agent * Delete FIP agent gateway port with external gw port * Remove ebtables\_driver/manager dead code * Stop device\_owner from being set to 'network:\*' * Add oslo rootwrap daemon logging during functional tests * ovs agent resync may miss port remove event * tests: disable process monitor before managers * Retry metadata request on connection refused error * Add ability to use custom config in DHCP-agent * Improve DB operations for quota reservation * Qos SR-IOV: Refactor extension delete to get mac and pci slot * Adds support to provide the csum option for the OVS tunnels * Delete the useless variable agent\_host * Handle process disappearing before we ask for its PPID * Allow only GET on Root controller * OVS agent: handle deleted ports on each rpc\_loop iteration * Final decomposition of Cisco plugin * Remove Cisco Meta and N1KV monolithic plugins * Workaround test stream corruption issue * Fix RBAC filter query for negative case * Updated from global requirements * Remove \_extract\_roles method from neutron.policy * Fixed functional test that validates graceful ovs agent restart * \_bind\_devices query only existing ports * Stop logging deadlock tracebacks * Don't log exceptions in GW update on router create * Remove an unnecessary extension check for rbac * OVS agent: flush firewall rules for all deleted ports at once * Enable most unit tests for py34 job * Changed filter field to router\_id * Fix a wrong condition for the \_purge\_metering\_info function * Don't log deadlock or retry exceptions in L3 DB * Make sure service providers can be loaded correctly * sriov: update port state even if ip link fails * Retain logs for functional test cases 7.0.0.0b3 --------- * Don't setup ARP protection on OVS for network ports * Don't setup ARP protection on LB for network ports * Add support for PluginWorker and Process creation notification * Implement external physical bridge mapping in linuxbridge * Avoid DB errors when deleting network's ports and subnets * Better message on allowed address pairs error * Add info to debug test\_keepalived\_respawns gate failure * Enable to update external network subnet's gateway-ip * Make Neutron service flavor save service\_type * Add tenant\_id to flavor service profiles attributes * Remove implicit registration of \*-aas service providers * Rename 'newapi' to 'pecan\_wsgi' * Catch errors on 'port not found' while deleting subnet * Process user iptables rules before INVALID * OVS-agent: Introduce Ryu based OpenFlow implementation * Deprecate external\_network\_bridge option in L3 agent * Do not track active reservations * Deprecate --service option for neutron-db-manage * Add constraint target to tox.ini * DHCP agent: allow using gateway IPs instead of uniquely allocated * Resolve issue where router can't be removed from L3-agent in dvr mode * OVS agent add functional tests of OVS status * check\_changed\_vlans doesn't need registered\_ports as param * [rpc] pull: removed a hack to avoid object backport triggered * Enable py34 tests for pluggable ipam backend * test\_migrations: Remove unnecessary midonetclient mocks * Updated from global requirements * Fix import path in neutron-sanity-check for ml2\_sriov opts * Decentralize the managemement of service providers * Remove requirements.txt for decomposed plugins/drivers * Linuxbridge-agent: fix bridge deletion * Correct neutron-ns-metadata-proxy command when watch\_log is False * Split SR-IOV configuration file into driver and agent pieces * Python 3: use a hash to sort dictionaries * Implement TODO for version listing * Fix hooks for dealing with member actions * Fixed filters for functional tests * Fix usage of netaddr '.broadcast' * Add lieutenants contact for networking-calico * Adding networking-calico to sub\_projects document * Fix locale problem in execute() * Remove duplicated codes in two test cases * Fixes wrong neutron Hyper-V Agent name in constants * Updated from global requirements * Improve python code for missing suggestion * Fix misnomer on network attribute * Refactor IpRouteCommand to allow using it without a device * Revert "Add support for unaddressed port" * Improve logging upon failure in iptables functional tests * handle gw\_info outside of the db transaction on router creation * Remove ml2 resource extension success logging * Replace "prt" variable by "port" * Add optional file permission argument to replace\_file() * Fixed the typo in the doc string of the class SubnetPoolReader * Add flows to tunnel bridge with proper cookie * Add lieutenants contact for networking-onos * Adding networking-onos to sub\_projects document * Add policy and policy rule belongs check * Base on SqlTestCase to init db tables correctly * Stops patching an object method which could be gone at cleanup * Add enable\_new\_agents to neutron server * Document prefix delegation testing issues * Fix Prefix delegation router deletion key error * Add Geneve type driver support to ML2 * Fix DVR log strings in agent * devref: Add sub-project release notes * Process update\_network in the openvswitch agent * Removing the SDN-VE monolithic plugin * [neutron-db-manage] Introduce contract and expand commands * Fix DBDuplicateEntry when creating port with fixed\_ips on PD subnet * Update template for ModelMigrationSync test * Fix py34 No sql\_connection parameter is established error * Switch to using os-testr's copy of subunit2html * Add a functional test to validate dvr snat namespace * Add snat ports cache to dvr router * DHCP agent: add 'bridged' property to interface driver * SR-IOV: deprecate agent\_required option * SimpleInterfaceMonitor handle case when ofport is an empty set * Make delete-vlan-bridge and delete-vlan functions clear * Run py34 tests with testr * Use directly neutron.common.constants constants in l3\_dvr\_db * Make a couple of methods private * Add IPv6 Prefix Delegation compatibility to ipam\_pluggable\_backend * Validate router admin\_state\_up on upgrade to distributed * Fix AttributeError in \_clean\_updated\_sg\_member\_conntrack\_entries() * PLUMgrid plugin decomposition part II * Quota enforcement: remove locks on \_dirty\_tenants * L3 agent changes and reference implementation for IPv6 PD * Decomposition phase2 of NEC plugin * Allow py34 to run tests individually * Add dns\_label processing for Ports * Remove out-of-tree vendor VIF\_TYPE\_\* constants * Move in-tree vendor AGENT\_TYPE\_\* constants * devref: added guidelines on how to maintain sub-projects * Stop logging STDOUT and STDERR on every shell out * Defer freeing of conntrack zone ids until allocation fails * Update the URLs to the Cloud Admin Guide * Remove redundant logging statements from RootWrapDaemonHelper * Rationalize neutron logs to help in troubleshooting router issues * Move db agent schedulers test to a more appropriate place * OVS agent don't hard code tunnel bridge name * Make models\_v2 explicitly import rbac\_db\_models * Make NeutronDbObjectDuplicateEntry exception more verbose * Add empty policy rule to get\_rule\_type action * test\_ovs\_neutron\_agent: Fix test\_cleanup\_stale\_flows\_iter\_0 * Support dhcp metadata service for all networks * Move docstring to FakeMachineBase * Update rootwrap.conf to add /usr/local/sbin * Remove the ML2 Nuage driver code * Template for ModelMigrationTest for external repos * Only mark metadata packets on internal interfaces * Python 3: do not do "assertFalse(filter(...))" * ip\_lib: support creating Linux dummy interface * Graceful OVS restart for DVR * DHCP agent: clarify logic of setup\_dhcp\_port * Add config option to specify ovs datapath * Python 3: fix test\_ovs\_tunnel * Python 3: use \_\_code\_\_ instead of func\_code * IPv6 display suitable message when MTU is invalid on iface * Update oslo messaging configuration section for fullstack * Imported Translations from Transifex * QoS: fix get bandwidth limit rules to filter them per policy * Neutron RBAC API and network support * Fixed broken link in neutron-server's documents * Used namedtuple for ReservationInfo * Move in-tree vendor VIF\_TYPE\_\* constants * Remove VIF\_TYPES constant * Added initial devstack plugin * Fix qos api-tests after policy changes * fullstack: use migration scripts to create db schema * Only validate local\_ip if using tunneling * qos: Delete bw limit rule when policy is deleted * Do not query reservations table when counting resources * Add support for unaddressed port * Sync FK constraints in db models with migration scripts * Add EnvironmentDescription, pass it down * Dropped release name from migration branch labels * Split DRIVER\_TABLES in external.py * DVR: make sure snat portion is always scheduled when needed * neutron-db-manage: sync HEADS file with 'current' output * Fix \_ensure\_default\_security\_group logic * Add missing tenant\_id validation in RESOURCE\_ATTRIBUTE\_MAP * Graceful ovs-agent restart * l2pop: check port mac in pre-commit to stop change * Adding Ale Omniswitch to sub\_projects document * Add high-level functional/integration DVR tests * Add a fullstack fake VM, basic connectivity test * Final decomposition of ML2 Cisco UCSM driver * Fix query in get\_reservations\_for\_resources * Move tests for non pluggable ipam backend * fullstack: Skip NotFound in safe\_client cleanup * Fix tenant access to qos policies * Rename args for alembic 0.8.0 * Update sub projects git urls * Stop using quota reservations on base controller * Final decomposition of ML2 Nexus Driver * manual add/remove router for dvr\_snat agent * DVR: fix router rescheduling on agent side * Python 3: fix test\_utils * lb: stop handling Havana device updates * quota: synchronize resync and count with other dirty\_tenants code * Add logging to debug oslo.messaging failure * Setup firewall filters only for required ports * Updated from global requirements * Quota enforcement: python3 compatibility * Devref for quotas * Reservations support * Fix .gitreview to not point at a branch * Don't fatal error during initialization for missing service providers * NSX: Move DB models as part of core vendor decomposition * doc: Improve table rendering using multi-row cells * Rename function '\_update\_port\_down' * Redundant tests removed from ovs-lib unit tests: * Add network to SubnetContext * Unskip firewall test * NSX plugin: Moving away plugin extensions * Get rid of exception converter in db/api.py * Python 3: encode or decode i/o data of Popen.communicate() * Updated from global requirements * Use a conntrack zone per port in OVS * Fix some issues around tempest in fullstack testing doc * Add lieutenants contact for kuryr * Add dashboard folder and graphite dashboard to doc * lieutenants: Add Neutron infra lieutenants * DVR: do not reschedule router for down agents on compute nodes * Replace internal calls of create\_{network, subnet, port} * ml2: Remove a redundant assignment in \_bind\_port\_level * ml2: \_commit\_port\_binding: Don't use None to mean False * Minor typo fix * l3: not use L2 plugin \_get\_subnet unnecessarily * l3\_db: not use L2 plugin \_get\_port unnecessarily * Break down \_bind\_port\_if\_needed in ML2 * Pecan WSGI: prevent plugins from opening AMQP connections * Remove 'action' argument from \_handle\_fip\_nat\_rules() * Remove vmware plugin from neutron (etc part) * Setup reference service providers for API test runs * [neutron-db-manage] check\_migration: validate labels * Python 3: fix neutron.tests.unit.api.test\_extensions * Add configurable options for HA networks * Add test that checks external tables are not changed * [neutron-db-manage] remove old HEAD file when updating for branches * Remove unneeded shebangs * Python 3: hmac requires bytes key/msg * Python 3: encode unicode response bodies * Support for independent alembic branches in sub-projects * Remove bigswitch mech\_driver entry point definition * Updated from global requirements * Python 3: specify a bytes to an argument for a format type 's' of struct.pack() * Preserve DVR FIP rule priority over Agent restarts * Treat sphinx warnings as errors * Distributed router can not add routes * Update fullstack multinode simulation image * Fix docs job * Improve callback registry devref documentation and usability * Final decomposition of the ML2 NCS driver * Fix update\_subnet for prefix delegation * The unnecessary value "sgids" was deleted * Fix DVR interface delete by port when gateway is set * Skip FwaaS test that is failing due to race condition * Destroy ipset when the corresponding rule is removed * Python 3: compare response.body to bytes in namespace\_proxy test * Forbid attaching rules if policy isn't accessible * DVR: fix router rescheduling on server side * Fix the low level OVS driver to really do egress * SR-IOV: Add Agent QoS driver to support bandwidth limit * Pass the extension driver exception to plugin * Update documentation acording to last QoS/OvS changes * OVS agent functional test for policy rule delete * Add Kuryr to sub\_projects.rst * Clean up test\_dvr\_router\_rem\_fips\_on\_restarted\_agent * Fix \_update\_subnet\_allocation\_pools returning empty list * devref: update quality\_of\_service * Replace 'import json' with oslo\_serialization * SR-IOV: Convert max rate from kbps to Mbps * Add testing coverage .rst, missing test infrastructure to-dos * Python 3: encode unicode response bodies * Update port functional tests for qos agent * Neutron-Ironic integration patch * DVR: fix router scheduling * TESTING.rst love * Removed configuration option for qos agent driver selection * Add delete\_port api to agent extension manager * Functional test for QoS policy bandwidth rule update * Support delegation of bind\_port to networking-odl backend driver * Use oslo.log library instead of system logging module * resources\_rpc: fixed singleton behavior for ResourcesPullRpcApi * Add thread locks on port routines for qos ext * Avoid dhcp\_release for ipv6 addresses * SR-IOV: fixed singletion behavior for ESwitchManager * Validate local\_ip for OVS tunnel * Imported Translations from Transifex * db\_base\_plugin\_v2: Avoid creating another session * Consistent layout and headings for devref * Use DeferredOVSBridge in setup\_default\_table * Fix get\_objects to allow filtering * QoS core extension: fixed dict extension when QoS policy is unset * OVS agent QoS extension functional test for bandwidth limit rules * Propagate notifications to agent consumers callbacks * Add rpc agent api and callbacks to resources\_rpc * neutron.api.rpc.callbacks interface rework * Moved l2/agent\_extensions\_manager into l2/extensions/manager.py * Moved extensions/qos\_agent.py into extensions/qos.py * Introduce base interface for core resource extensions * Do not delete fip namespace during l3 dvr agent resync * Introduce ItemAllocator class * Validate updated allocation pool before using it * Remove quotes from subshell call in tools/split.sh * Don't claim Linux Bridge ml2 driver supports bandwidth limit QoS rules * Clean up QoS rules first, then QoS policies * Pass the extension driver exception to plugin * Remove a few obsolete options from midonet.ini example * Rename a test method in test\_policy.py * Revert "Add extension callbacks support for networks" * Updated quality\_of\_service devref doc to reflect reality * Broadcast service port's arp in DVR * usage\_audit: Fix usage\_audit to work with ML2 * Revert "Remove VPN from API tests" * Enable VPN plugin for API test * Validate interface\_mappings on Linux bridge init * Initialize ancillary\_port\_info dict as blank in OVS agent * Enable fullstack multinode tests, add L3 HA test exemplar * SR-IOV: Update eswitch manager to support rate * Follow up with some cleanup for agent qos\_driver * Gracefully handle duplicate rule creation * Fix: Skip rescheduling networks if no DHCP agents available * DB, IPAM & RPC changes for IPv6 Prefix Delegation * Python 3: convert dict\_keys object to list * Python 3: do not compare int and NoneType * Remove VPN from API tests * Fix typos in neutron code * "FakeV4Subnet" class be inherited by following class * Update OVS driver to work with objects * Python 3: fix test\_ovs\_tunnel * \_get\_dvr\_sync\_data: Return a list, rather than dict\_values for python3 * Fixing ICMP type and code validation * Support subnetpool association to an address scope * Add API tests for non-accessible policies * Gracefully handle fetching nonexistent rule * use single transaction to update qos policy associatation * Replaces reduce with six.moves.reduce for py 2/3 compatibility * Add oslo db retry decorator to the RPC handlers * Python 3: Fix test\_security\_groups\_db * Replace to\_dict() calls with a function decorator * Add DNS and DHCP log into dhcp agent * Install arp spoofing protection flow after setting port tag * Move 1c844d1677f7 expand migration to appropriate branch * Fix ipset can't be destroyed when last rule is deleted * Guarantee there is only one bandwidth limit rule per policy * Cleaned up some TODO comments for feature/qos that do not apply anymore * L2 agent extension manager: read extensions list from config file * objects.qos.policy: forbid deletion when attached to a port or a network * Remove handle\_network/handle\_subnet from l2 agent extensions * Move away nested transaction from \_ensure\_default\_security\_group * Moved QOS\_POLICY\_ID into qos\_consts.py * Introduce get\_ports\_attributes in OVSBridge * Added missing [qos] section into neutron.conf * Enable rule delete test * objects: consolidate single transaction checks into test\_base * objects.qos.policy: provide rules field, not type specific * Unite qos\_rules and qos\_\*\_rules tables * Switch controller to actually call the plugins * Add extensions listing to the controller * Add placeholder for notifier hook * Add hook for policy enforcement * Add quota enforcement hook * Add ownership validation hook * Add attribute population hook * Add resource/plugin identification hook * Add hook to create a context from the headers * Add hook to translate exceptions into HTTP codes * Add startup hook after pecan init for plugins * Add keystone middleware wrapper to pecan app * Fix accessing shared policies, add assoc tests * qos: forbid creating rules when there is no access to policy * Initial pecan structure * Remove unnecessary executable permission * NSX: Rename default\_interface\_name option * Arista Drivers decomposition part II * Python 3: pass bytes to base64.encode{string,bytes} * Python3: pass bytes to binascii.crc32 * Fix order of calls in update\_port * Check that VXLAN is not in use in LB VXLAN check * Initialize port\_info dict as blank in OVS agent * Ensure non-overlapping cidrs in subnetpools with galera * SR-IOV: update pci lib to support rate limit * SR-IOV: Fix SR-IOV agent to run ip link commands as root * QosPolicy: made shared field required and with default value = False * Python 3: Use '//' instead of '/' * Prevent update alloc pool over existing gateway ip * Moved out cisco n1kv mech driver and db models * Updated from global requirements 7.0.0.0b2 --------- * sriov: implement spoofchecking configuration * [qos] ovs: removed TODO for getting integration bridge from arguments * Fixes a typo phys\_brs in place of phys\_br * Update dhcp agent cache for network:dhcp ports * Keep dns nameserver order consistency * Extend vxlan\_group option to allow a range of group addresses * Load the QoS notification driver from the configuration file * Add pluggable backend driver for QoS Service notification * Enable resource usage tracking for reference plugins * Add plural names for quota resources * Introduce usage data tracking for Neutron * Create packages for quota modules * Python 3: fix test\_attributes * Add FUJITSU vendor plugin in sub\_projects * Python 3: fix test\_dhcp * test\_db\_base\_plugin\_v2: Improve DBReferenceError generation * Fix a microsecond format of isoformat() * Add update tests for policies and rules * Updated from global requirements * Python 3: fix test\_context * Fix KeyError: 'L3\_ROUTER\_NAT' in l3 scheduler functional test * Introduce mechanism to determine supported qos rule types for a plugin * Cleanup IPAM tests * get\_info: request object backport only if desired version is different * rpc.callbacks.registry: validate that callback provider is registered * rpc.callbacks.registry: validate type of callback result * Add UT for agent\_extensions\_manager * Don't set tenant\_id for rule objects * Fix dhcp autoschedule test assertion logic * Fix inconsistency of if/return logic in attributes.py * Imported Translations from Transifex * [neutron-db-manage] revision: properly bootstrap a new branch * Add DB support for resource usage tracking * QoS: Remove type attribute from QoS rules * Don't enforce qos ml2 extension driver * ml2: added qos\_profile\_id to get\_device\_details payload * Add versioned object serialize/deserialize for resources RPC * policy: made attach\_\* and detach\_\* methods more robust * Decompose Apic ML2 mechanism driver * Remove duplicate DHCP agent registration in unit test * Python 3: do not index dict\_values objects * L2 agent RPC add new RPC calls * Add Cathy Zhang as networking-sfc Lieutenant * Add error message when migrate from distributed router to centralized * Avoid printing log options multiple times * Support qos rules and fields parameters in GET requests * Pass context when deleting bandwidth limit rule * Add Pluggable IPAM Backend Part 2 * Create fip on subnet id * Python 3: fix neutron.tests.unit.agent.dhcp.test\_agent * Updated from global requirements * Update port bindings for master router * [qos] cleanup \_find\_object from neutron.db.api * Revert "Mute neutron.callbacks notification logs." * qos: kill get\_namespace() from service plugin * Base infrastructure for QoS API tests * Metaplugin removal * Remove line number of link and useless link * Disable port creation when invalid MAC address is provided * Fix handling of port-range-min 0 in secgroup RPC and agent * Fix a property comment in metadata\_agent files * Add address scope API tests * Python 3: enable more tests * Add new ovs DB API to inquire interfaces name list in a bridge * Tweak wording for project inclusion process * Define fullstack router/network/subnet management fixture * Fix race condition by using lock on enable\_radvd * Fix note in devref/contribute.rst * ensure\_dir: move under neutron.common.utils * Add conntrack-tool to manage security groups * Adding a cleanup for 'qlbaas-' namespaces in netns\_cleanup * Bug-Fix for unexpected DHCP agent redundant * Remove deprecated OVS and LB plugin DB tables * ovs\_lib: Fix native implementation of db\_list * Stop use of oslo\_utils.timeutils.strtime() * Fix gateway port could not retrieve for subnet * Port help text for dvr\_base\_mac from neutron.conf * Add documentation for SRIOV NIC agent (previously missing) * Python 3: fix neutron.tests.unit.agent.linux.test\_async\_process * Adds garp\_master\_repeat and garp\_master\_refresh to keepalived.conf * Added functional tests for L3 schedulers * Always use BridgeDevice to manage linuxbridges * Update OVS Agent to work with Agent Extension Mgr * Instantiate qos agent driver * objects.rule: enable database tests for QosRule * Handle qos\_policy on network/port create/update * Updated from global requirements * Validate that context exists * neutron-db-manage: fix check\_migration for branch-less migration directories * Use only the lower 16 bits of iptables mark for marking * Python 3: fix test\_provider\_configuration * Add address\_scope\_db to neutron/models/head.py * OVS agent factor our port stats processing * Python3: Do not compare NoneType and integers * Use oslo\_log.helpers.log\_method\_call * Unplug the VIF if dhcp port is deleted * Python 3: Wrap map() in a list call * Devref documentation for client command extension support * Alter unit test to match bug and cleanup ext logic * Allow overriding of the neutron endpoint URL in metadata agent * Allow passing table argument to construct IpRouteCommand * Make external\_gateway\_nat\_rules easier to understand * Remove perform\_snat\_action indirection * Flavor Framework implementation * Add breakages in public API of devref * objects.qos.policy: support per type rule lists as synthetic fields * Network RBAC DB setup and legacy migration * [devref] db\_layer: expand on how new migration scripts look like * Add oslo db retry decorator to non-CRUD actions * QoS Service devref * Implement QoS plugin * Add oslo db retry decorator to non-CRUD actions * Change prefix for namespace fixture * Imported Translations from Transifex * OVS-agent: Fix a docstring typo * Python 3: do not use types.ClassType * Create dvr base class and stop passing around snat\_ports * Add qos section to ovs agent config * Mute neutron.callbacks notification logs * Small fixes in test\_qos\_agent UT * Add unit tests and fixes for OVS Agent QoS Extension Driver * Correct two spelling mistakes in Neutron devrefs * Improve check\_migration command error message * Avoid using logging in signal handler * Galera multi-writers compliant sync\_allocations * Fix SR-IOV mechanism driver tests directory * Switch to the oslo\_utils.fileutils * Fix a regression in a recent IPAM change * Fix update\_port\_postcommit and port not found with DVR * Tighten exception handler for import\_object * Updated from global requirements * bugs: Update info about current bug czar * Add another Lieutenant contact for Dragonflow * [neutron-db-manage] support separate migration branches * Add OVS QoS extension agent driver * Disable python3 tests failing due to Routes < 2.0 * Fix typo of 'receive' in test\_dhcp\_ipv6.py * Fix typo 'adress' * Add sub-project lieutenant for networking-midonet * Lower log level for extending network/subnet/port * Cleanup unused method get\_plugin\_version * Remove db-access semaphore in ML2 * Moving out cisco n1kv extensions * Remove self.snat\_ports, a dvr thing, from router base class * Include comment in DHCP ip6tables rules * Qos Agent Extension * Fixed L3 agent manual scheduling for HA routers * Ensure floating IPs only use IPv4 addresses * Implement QoS policy detach from port and network * Add API stub for QoS support rule\_type resource * Lower log level of errors caused by user requests to INFO * Reject router-interface-add with a port which doesn't have any addresses * Fix bug that resources in attr\_map may point to same object * Updated sub\_projects.rst for networking-vsphere * Imported Translations from Transifex * Enforce specific order for firewall.(un)filtered\_ports and devices * objects.base: fixed object.delete() * objects.qos.policy: fixed get\_\*\_policy and attach\_\* methods * objects.base: reset changes after getting objects from database * BaseObjectTestCase: rename test\_class into \_test\_class * Cleanup rule models and objects * objects.qos: fixed create and update for QosBandwidthLimitRule * Use \_is\_this\_snat\_host and remove \_get\_gw\_port\_host * Move more snat code to dvr class that does snat * Add constants for vhost-user vif * get\_vif\_ports: ignore non-Interface ports * Add Pluggable IPAM Backend Part 1 * Fix duplicate entry catch for allowed address pairs * Fix failures introduced by the new version of mock * Arista ML2 driver should ignore non-vlan networks * Ensure that update\_fip\_statuses gets called * Make IPAM more pythonic * Move DVR related method to proper class * Introduce connection testers module * Allow IPAM backend switch * Correct fcntl.flock use in Pidfile.unlock * Move update\_security\_group\_on\_port to SecurityGroupDbMixin * Python 3: Fix a TypeError in policy.py * In Arista ML2 driver Reconfigure VLAN on VM migration * Add sub-project lieutenant for networking-plumgrid * Fix issues with allocation pool generation for ::/64 cidr * Add extra subnet route to ha router * Remove lingering traces of q\_ * Make sure path\_prefix is set during unit tests * Add IP\_ANY dict to ease choosing between IPv4 and IPv6 "any" address * Python3: cast the result of zip() to list * Track allocation\_pools in SubnetRequest * Add ARP spoofing protection for LinuxBridge agent * COMMON\_PREFIXES cleanup - patch 5/5 * List up necessary files for thirdparty-ci.rst * Refactor init\_l3 to separate router port use case * Devref for out-of-tree plugin/driver contribution * Python3: do not add dict\_values objects * portsecurity\_db\_common: Access db columns in a consistent way * Python 3: do not index dict\_keys objects * Remove unneeded OS\_TEST\_DBAPI\_ADMIN\_CONNECTION * Update DVR agent to use get\_vifs\_by\_id * DVR: cleanup stale floating ip namespaces * COMMON\_PREFIXES cleanup - patch 1/5 * Fall back on empty path if prefix is missing * Refactor IpRuleCommand to take more arguments * objects.qos: added unit tests for QosPolicy neutron object * objects.base: avoid db access if object does not have changes * Start documenting potential API breakages in devref:neutron\_api * QoS extension fixes * Install more-specific ICMPv6 rule in DVR routers * devref: document API status for neutron.openstack.common.\* * Python3: do not use urllib.urlencode * AgentExtensionsManager and AgentCoreResourceExtension * Generic Resources RPC * DVR: remove unused method * Generic rpc callback mechanism which could be reused * Update dhcp host portbinding on failover * OVS native DBListcommand if\_exists support * Introduce the AFTER\_READ callback for ports and networks * Collapse create\_subnet into single method * Downgrade log level for gone port on status update * Add extension callbacks support for networks * [qos] policy: add methods to interact with policy bindings * Support Basic Address Scope CRUD as extensions * First QoS versioned objects, ever * Add bandwidth\_limit rule type constant * Use EXT\_TO\_SERVICE\_MAPPING instead of ALLOWED\_SERVICES * Change the half of the bridge name used for ports * Fix log traces induced by retry decorator * Remove unused linux bridge agent configuration options * Add bandwidth\_limit rules as sub-collection of qos policy * QoS: db models and migration rules * Add Create/Destroy API to OVS QoS BW Limiting * Fixing indentation and typo in comments * docs: link quality of service doc stub to devref index * Update PLUMgrid plugin information * Improve fixture usage * Move pylint dep from tox.ini to test-requirements * Disable pylint job * Remove bridge cleanup call * Move windows requirements to requirements.txt * Adds base in-tree functional testing of the ovs\_neutron\_agent * fix spelling mistakes * Register extraroute extension * Increase ping count on ARP spoof test * Read vif port information in bulk * Do not mock arping in L3AgentTestFramework functional tests * Fix Consolidate sriov agent and driver code * Remove failing SafeFixture tests * QoS service plugin stub * Create the QoS API extension stub * Switch to oslo.service * Revert "Removed test\_lib module" * Don't access mock's attribute directly especially when it's not needed * Fix subnet updating failure on valid allocation pools * Add documentation for Linux Bridge (previously missing) * Add parent\_id to \_item calling from \_handle\_action * Add logging of agent heartbeats * populate port security default into network * Revert "Fix 'router\_gateway' port status can't be updated" * RootHelperProcess: kill can consume signal number * Move NetcatTester to common/net\_helpers * Make '\_create\_router' function handle Boolean kwargs correctly * ip\_lib: Add flush() command to IpNeigh to clean arp cache * Refactor NetcatTester class * Use REST rather than ReST * lb-agent: handle security group updates in main loop * Add a double-mock guard to the base test case * Remove duplicated mock patch of ip\_lib * Consolidate sriov agent and driver code * Restructure agent code in preparation for decomp * Fix ip\_lib get\_gateway for default gateway on an iface * fixing typo in gerrit query link in third party policies doc * Use last address in v6 allocation pool generation * Extend SubnetRequestFactory to access subnet dict * Remove duplicated call to setup\_coreplugin * Remove double mock of dhcp agent periodic check * Remove double fanout mock * Remove double callback manager mocks * Remove ensure\_dirs double-patch * Decompose \_save\_subnet * Fix tenant-id in Arista ML2 driver to support HA router * Log OVS agent configuration mismatch * Avoid env variable duplication in tox.ini * Skip ARP protection if 0.0.0.0/0 in addr pairs * linuxbridge: clean up README file * Fix tox errors in thirdparty-ci docs * Removed test\_lib module * Updated from global requirements * Define SafeFixture base fixture * Remove quantum untracked files from .gitignore * Context class should initialise its own data * Abstract sync\_allocations * ovsdb: attempt to enable connection\_uri for native impl on startup * Just use {0,1,2} rather sys.std\*.fileno() * Make Daemon pidfile arg optional * Different approach to indicate failure on SystemExit * Move third-party CI policy under docs/policies * Remove lbaas API tests, which are now in the lbaas repo 7.0.0.0b1 --------- * Only create one netaddr.IPNetwork object * Provide work around for 0.0.0.0/0 ::/0 for ipset * Fix >80 char lines that pep8 failed to detect * Deprecate "router\_delete\_namespaces" and "dhcp\_delete\_namespaces" * Make DHCPv6 out of bounds API test deterministic * Don't process network\_delete events on OVS agent * dhcp fails if extra\_dhcp\_opts for stateless subnet enabled * Revert "Fix subnet creation failure on IPv6 valid gateway" * Support oslo\_db 1.12 * Python 3: do not use itertools.izip * Override opportunistic database tests to PyMySQL * Extend default setenv instead of replacing it in tox.ini * Fix FloatingIP Namespace creation in DVR for Late Binding * Cleanup get\_plugin\_name() from the tree * Bulk move methods to ipam\_backend\_mixin.py * NSXv: update ini file to support dhcp\_lease\_time * Use sets to calculate added/original/removed ips * Add IPset cleanup script * Optimize ipset usage in IptablesFirewallDriver * Python3: do not set Request.body to a text string * Prepare for full stack CI job * Fix callback registry notification for security group rule * Python3: do not use \_\_builtin\_\_ * Ease debugging alembic by passing proper scripts path in alembic.ini * Use string exception casting everywhere * l3 agent: do router cleanup for unknown routers * Switch to oslo\_utils.uuidutils * Fix subnet creation failure on IPv6 valid gateway * Decompose \_create\_subnet\_from\_pool * Move \_delete\_port * Decompose create\_port and save\_subnet * Retry port status update on StaleDataError * Allow setting Agents description to None * Fix RPC version to be a string * Decompose DVR CSNAT L3 Agent from Compute Node L3 Agent * cleanup openstack-common.conf and sync updated files * Fix l3 agent to not create already deleted router * Python3: do not use '+' on dict\_items objects * Disable keepalived process in keepalived func test * Python3: do not use im\_self/im\_func/func\_closure * Add request factory for pluggable IPAM * Python3: use dict.keys() instead of dict.iterkeys() * NSX QoS ext: RXTX factor can be decimal * Move \_add\_auto\_addrs\_on\_network\_ports * DHCP agent: Set an "ipxe" tag to work with Ironic * Add sanity\_check for keepalived ipv6 support * Remove \_check\_ip\_in\_allocation\_pool * Precision networking-bagpipe-l2 subproject * Don't delete DVR namespace if there are still ports on this node * Updated from global requirements * Fixed the only sphinx warning in docs * Fix SR-IOV mech driver to set port status to down when agent is required * read\_hosts\_file\_leases shouldn't parse stateless IPv6 * Fix 'router\_gateway' port status can't be updated * Update version for Liberty 7.0.0a0 ------- * Add networking-sfc to the list of affiliated Neutron projects * Minor improvements to sub\_projects document * Python 3: do not use cmp(), nor sorted(..., cmp=...) * Move get\_inteface\_by\_ip from LinuxBridge class to ip\_lib * Add policy files specific to NSX plugins * Fix cisco\_csr\_identifier\_map.ipsec\_site\_conn\_id * fix rootwrap debug filter for ping all * Refactor rpc\_loop() in ovs neutron agent * Add deadlock retry to API and ML2 RPC port update * ovsdb: session.rpc never initialized * Remove duplicated debug logging around locking * Refactor scan\_ports() and update\_ancillary\_ports() in OVS Neutron Agent * Python3: do not change the size of a dict while iterating over it * Refactor TestRpcWorker and TestWorkerService * Juno\_initial migration * docs: added job to well known tox envlist * API Extensions: inherit from the ExtensionDescriptor * Remove fossilized remains * Refactor update\_port in db\_base\_plugin\_v2 * Refactor \_update\_ips\_for\_port * Python 3: use dict.values instead of dict.itervalues * Put output of docs job into doc/build/html * Remove get\_namespace from API extensions * Ensure no "db" related functional/fullstack tests are skipped in the gate * Use PyMySQL in MySQL related functional/fullstack tests * Skip rescheduling networks if no DHCP agents available * Reflect project moves from stackforge to openstack * VMWare NSXv: Add distributed URL locking to ini * Revert "Revert "Add VIF\_DELETED notification event to Nova"" * Decompose db\_base\_plugin\_v2.py with changes * Remove duplicate tunnel id check in sync\_allocations * Remove meaningless no\_delete from L3 test * Revert "Revert "Set default of api\_workers to number of CPUs"" * OVSNeutronAgent pass the config as parameter * Refactor \_update\_subnet\_allocation\_pools * Stop sending gratuitous arp when ip version is 6 * Set .gitreview defaultbranch to feature/pecan * Fix Enum usage in 589f9237ca0e\_cisco\_n1kv\_ml2\_driver\_tables * Imported Translations from Transifex * power grab * Change ensure\_dir to not check directory exists first * Document existence of br-tun and br-int in the OVS agent * Correct indentation in neutron.api.v2.attributes * Python3: replace 'unicode' with 'six.text\_type' * Fullstack testing devref follow up * Moving out the cisco n1kv section to stackforge * Ensure no "agent" functional tests are skipped in the gate * Remove useless pass from methods in type\_tunnel.py * Make Vlantransparent extension inherit from ExtensionDescriptor * Actually allow to pass TRACE\_FAILONLY to ostestr * Switch to os-testr to control testr * Introduce functions using arping executable * Revert "Defer segment lookup in NetworkContext object" * Added networking-plumgrid in plugin requirements * Switch from MySQL-python to PyMySQL * Context: Remove logic for read\_deleted and deprecate it * Python 3: use next() instead of iterator.next() * Consume oslo.policy * policy: cleanup deprecation code to handle old extension:xxx rules * Fix a regression in "Separate ovs-ofctl using code as a driver" change * Break Pinger class to functions * Handle SIGHUP: neutron-server (multiprocess) and metadata agent * Allow update\_port\_status to take network param * Make pep8 job succeed when /etc/neutron/neutron.conf is not installed * Add a comment on \_check\_update\_has\_security\_groups * Change defaultbranch in .gitreview * Enable all deprecation warnings for test runs * Remove get\_admin\_roles and associated logic * Add documentations for VXLAN Tunnels * Defer segment lookup in NetworkContext object * Fix typos in docs * Fixes bulk insertion of data to ml2\_port\_binding * Add Neutron PTL Office Hours * Python3: Enable all working tests in tox.ini * Add get\_events to OVSDB monitor * Update ipset members when corresponding sg member is empty * Send 'security\_groups\_member\_updated' when port changes * Remove full stack log noise * ML2: Remove TYPE\_MULTI\_SEGMENT * L3 agent should do report state before full sync at start * Clean only floating-ip related connection states * Refactor awkward logic in setup\_dhcp\_port * Add a "light" base test class for DB tests * Make \_val\_to\_py and \_py\_to\_val not private * Decompose db\_base\_plugin\_v2.py part 2 * Fix typo in test class name * Start linuxbridge neutron agent using a launcher * Handle SIGHUP in ovs neutron agent * test\_ovs\_neutron\_agent: Remove useless ofport=10 arguments * test\_l3: Don't assume the order of subnets * Python 3: do not index a dict\_values object * versionutils: switch from incubator version to oslo.log * Run RootHelperProcess always as root * Changes in rally-jobs/README.rst * Add more API tests for port-security extension: * Decompose the NCS ML2 Mechanism Driver * test\_db\_base\_plugin\_v2: Don't assume the order of fixed\_ips * pylint: enable \`duplicate-key\` check * Remove reference to non-existent fullstack fixture * Enhance utils.ensure\_dir to be resilient to concurrent workers * Use a single method to remove an address with its conntrack state * Decompose db\_base\_plugin\_v2.py * Add sub-project lieutenants * Fix confusing parameters names * Extra indent in test\_ovs\_neutron\_agent * Make MockFixedIntervalLoopingCall class as a helper class * Revert "Add VIF\_DELETED notification event to Nova" * Wrap ML2 delete\_port with db retry decorator * Remove extra indent in testcases * Check for 'removed' in port\_info before reference * Catch broad exception in methods used in FixedIntervalLoopingCall * Add devref that explains fullstack testing and its direction * Remove get\_dhcp\_port RPC method * Refactor type\_tunnel/gre/vxlan to reduce duplicate code * Imported Translations from Transifex * Update rootwrap.conf to add /usr/local/bin * Add route to metadata IP by default * Python3: use six.iteritems() instead of dict.iteritems() * Modify ipset functional tests to pass on older machines * Add a non-mixin function for model queries * Implement IPAM Driver loader * Remove comment about hash seed in tox.ini * Refactor mlnx mechanism driver to support infiniband only * Remove unused \_uuid function alias from test\_iptables.py * test\_ovs\_neutron\_agent: Remove unnecessary mocking * Refactor type\_gre.vxlan tests to reduce duplicate code * Removed duplicate keys in dicts in test * Don't update floating IP status if no change * Don't delete port from bridge on delete\_port event * Enable random hash seeds * Fix formatting of core-reviewers doc * Get completely rid of contextlib.nested * Fix indentation errors in tests * Improve test\_set\_members\_deleting\_less\_than\_5 * Rename test\_periodoc\_resync\_helper to test\_periodic\_resync\_helper * Sort \_get\_new/deleted\_set\_ips responses in unittests * Ensure netfilter is enabled for bridges * Cleanup stale metadata processes on l3 agent sync * Imported Translations from Transifex * Fix ovs agent restore local\_vlan\_map failed * Use correct time delta function * Do not assume order of security group rules * ML2: Incorrect commented cisco mechanism driver name * py34: don't run any tests except unit tests * Move full-stack logs post-tests * Fix PYTHONHASHSEED bugs in test\_security\_groups\_rpc * Addressing follow up comments for OVS\_LIB fail\_mode setting API * Move pool dispose() before os.fork * Add RFE submission guidelines * Switch to dictionary for iptables find * Process port IP requests before subnet requests * Remove time formatting in agent clock error * Persist DHCP leases to a local database * Flesh out the new RFE process and set deadlines for it's use * Do not assume order of dictionary elements in init\_l3 * Introduce the Lieutenant system into Neutron * Isolate use of fixed\_ips[0] to avoid confusion * Use the correct name for the "Repository Creator's Guide" * Do not assume order of convert\_kvp\_list\_to\_dict method responses * Do not assume order of iptables\_firewall method responses * Do not assume order of get\_sync\_data\_metering response elements * OVS-agent: Remove optional flags from br\_tun.deferred() method * OVS\_LIB support API for setting fail mode 'standalone' * Remove hack for sending gratuitous arp from fip ns * Force order of dhcp.needs\_resync\_reasons dictionary elements * Remove use of contextlib.nested (api-tests) * Use os.\_exit after forking * test\_fork\_error: Fix incorrect test mock * Skip external tables for neutron-db-manage --autogenerate * Fix a typo in \_schedule\_network method * Ensure non-overlapping cidrs in subnetpools without galera * Add callback prior to deleting a subnet * OVS-agent: Separate ovs-ofctl using code as a driver * Imported Translations from Transifex * Remove unnecessary brackets * Ensure mac address added to iptables is always in unix format * Remove use of contextlib.nested * Adding loadbalanacerv2 device owner constant to neutron constants * Python 3: use six.string\_types instead of basestring * Fix minor errors in the Vyatta L3 Plugin: * Remove middleware oslo-incubator module * Match order of iptables arguments to iptables-save * fix DHCP port changed when dhcp-agent restart * VMware NSXV: update configuration file * IPAM reference driver * Python 3: Use six.moves.range * ovs-agent: prevent ARP requests with faked IP addresses * Use convenience method from db api to create nested transaction * Remove a unused Context class * Use namespace names in NetcatTester * Optimize IptablesManager.\_find\_last\_entry * Take Daemon stdin/stdout/stderr args as file objects * Support for concurrent full-stack tests * OVS-DVR: Suppress a confusing error log about csnat port * OVS-DVR: Improve an error log about csnat port * Replace ci.o.o links with docs.o.o/infra * Refactor initialize() of sriov mech driver * Centralized register\_OVS\_agent in tests * Don't pass namespace name in disable\_isolated\_metadata\_proxy * Add client id option support to dhcp agent * Remove use of contextlib.nested * Allow updating port 'binding:host\_id' be None * Block subnet create when a network hosts subnets allocated from different pools * Fix neutron tests * Allow unit tests to be run independently * SystemExit is ok for child processes * When disabling dhcp, delete fixed ip properly * Update build hooks * Append @randtoken to L3 agent namespaces in full stack tests * Add VIF\_DELETED notification event to Nova * setup port filters when sg rules change * tests: don't allow oslo.config to autodiscover config files * mlnx MD: mlnx\_direct removal * l2pop UT: Reduce code duplication in migration tests * Add unit tests for ML2 DVR port binding and fix PortContext inconsistencies * Make it clear the rfe tag is lower-case * Remove H305 from tox.ini pep8 ignore list * Allow users to run 'tox -epy34' * Deprecate quota\_items, register resources upon REST initialization * Support BP:ipv6-router in Neutron HA Router * Catch ObjectDeletedError and skip port or subnet removal * Randomize tunnel id query to avoid contention * Remove skip of service-type management API test * Imported Translations from Transifex * Add capability to wait for IPv6 address in ip\_lib * Remove from BridgeDevice homemade execute in namespace * remove router interface on Arista L3 plugin fails * Extenuate register\_dhcp\_agent code duplication in tests * Fix typos related to IPv6 use-cases * Refactor checks for device existence * Updated from global requirements * Check for missing network in \_bind\_devices * Add missed actions into policy.json * Reuse caller's session in ML2 DB methods * ARP spoofing patch: Data structures for rules * Limit router gw ports' stateful fixed IPs to one per address family * VMWare NSXv: Metadata for distributed router * VMware: update supported plugins * Allow to define enable\_snat default value * Update the specs process for Liberty * changes log level to debug for help calls * Remove use of contextlib.nested * Fix fetching prevent\_arp\_spoofing from cfg in neutron-sanity-check * VMware: add in router types for NSXv * Reduce prefix and suffix length in ipsets * Add port-security extension API test cases * Add test for security groups * Use iptables zone to separate different ip\_conntrack * Fix dhcp \_test\_sync\_state\_helper asserting calls wrong * Updated from global requirements * Enhance configure\_for\_func\_testing.sh for \*aaS use * Add IP version support to all ip\_lib code * Imported Translations from Transifex * Get all interfaces for get\_snat\_sync\_interfaces * OVS-agent: Ignore IPv6 addresses for ARP spoofing prevention * Remove un-used keys in keepalived tests * Deprecate config-based Quota Driver * Clarify stackforge/vmware-nsx is for VMware NSX suite * Updated from global requirements * l3 agent: fix grammar in router info not found warning * Finally let L3 and DHCP agents cleanup namespaces by default * Context: is\_admin==True implies is\_advsvc=True * Fix port creation verification of the port-security extension * Add some tests for floating ips * Add notes about official sub-projects * Updated ovsvapp\_agent.ini in neutron * Don't use iterator in search for tunnel type * Remove is\_active property from SimpleInterfaceMonitor * Updated from global requirements * Disembowel register\_l3\_agent code duplication in tests * Ensure mocks for lla allocator \_write in test\_agent * Fix \_device\_to\_port\_id for non-tap devices * Imported Translations from Transifex * Rename delete\_gateway method name * Drop use of 'oslo' namespace package * Remove 'IP' from device exception message * Add icmpv6 to sg\_supported\_protocols * Suppress exception when trying to remove non existing device in SNAT redirect 2015.1.0 -------- * Run radvd as root * Add devices to update in RPC call security\_groups\_provider\_updated * Run radvd as root * Support multiple IPv6 prefixes on internal router ports for an HA Router * Not creating HA router when not enough l3 agents * Eliminate extra queries used to retrieve gw\_ports * Don't update port with host id of None * fix l3-agent restart with last runtime fip for dvr * Refactoring to adhere to coding convention * Replace unnecessary call to get\_sync\_routers * Move test\_get\_user\_allocation\*returns\_none test to a proper class * Replace BaseLinuxTestCase by BaseSudoTestCase * Remove RecursivePermDirFixture useless cleanup * Utilities for building/parsing netns names to facilitate testing * Fix MismatchError to nondeterministic order for list of controllers * Add missing interface to populate subnets method * Don't resync on DHCP agent setup failure * Refactor socket ssl wrapping * Don't resync on DHCP agent setup failure * Replace BaseIPVethTestCase by FakeMachine * Return exception when attempting to add duplicate VIP * Imported Translations from Transifex * Allow plugin to specify router\_id * Neutron to Drop Router Advts from VM ports * Fix L3 agent functional tests random failures * Mock report\_state during L3 agent functional tests * Remove backward compatibility for check\_is\_admin 2015.1.0rc2 ----------- * Add weak reference test for callback manager * Spawn RADVD only in the master HA router * tests: confirm that \_output\_hosts\_file does not log too often * Double functional testing timeout to 180s * Restrict subnet create/update to avoid DHCP resync * Only update MTU in update code for MTU * Restrict subnet create/update to avoid DHCP resync * Make sure OVS restarts when Exception occurred * Updated from global requirements * Remove dependency on weak reference for registry callbacks * Ensure metadata network works with DVR * Change callbacks logging from INFO to DEBUG * Fix DVR functional tests resources leak * Create bridges in ovsdb monitor functional tests * Refactor RESOURCE\_ATTRIBUTE\_MAP cleanup * remove metadata\_proxy\_local filters for rootwrap * Add use\_slave DB api support * Fix incorrect query for user ip allocations * Fix typo acomplished => accomplished * OOP naming cleanup in l3\_dvr\_db * ARP spoofing patch: Low level ebtables integration * Fix test discovery for api and functional paths * Block allowed address pairs on other tenants' net * tests: confirm that \_output\_hosts\_file does not log too often * Fix super cleanUp for fullstack ProcessFixture * Add security groups events * Block subnet create with mismatched IP versions * Remove neutron.tests.common.agents package * L3 DB: Defer port DB subnet lookups * lb-agent: ensure tap mtu is the same as physical device * Only update MTU in update code for MTU * Revive BaseLinuxTestCase.\_create\_namespace * Defer creation of router JSON in get\_routers RPC * ovs\_lib: Fix a race between get\_port\_tag\_dict and port removal * Correct inconsistent enable\_snat management * \_create\_subnet\_from\_implicit\_pool assumes external network extension * Log caught exceptions while deleting a router * Define FakeMachine helper for functional/fullstack tests * Replace custom method call logger with oslo.log helper * ML2: Change port status only when it's bound to the host * Release Import of Translations from Transifex * Simplify keepalived.virtual\_routes * l2pop UT: Simplify migration tests * l2pop UT: Expire cached db objects before reusing a session * Correct typo for matching non-dict ovsdb rows * Fixes race condition and boosts the scheduling performance * Register ibm-db-alembic import for DB2 migrations * Fixes race condition and boosts the scheduling performance * ML2: Change port status only when it's bound to the host * Remove double queries in l3 DB get methods * Strip unnecessary overrides in extraroute\_db mixin * Set loading strategy to joined for Routerport/Port * Avoid double-hopping deletes for security group rules * Set IPset hash type to 'net' instead of 'ip' * Revert "Add ipset element and hashsize tunables" * Set IPset hash type to 'net' instead of 'ip' * Update .gitreview to point to stable/kilo * Add Kilo release milestone * Quota model: use HasTenantId mixin * Clarify the init logic for the ML2 plugin * Deal with TODO related to Security Groups RPC API's classes * Add Kilo release milestone * Add some more comments to models/frozen.py * IPv6 SLAAC subnet create should update ports on net * Two api tests for 'firewall insertion mode' feature * OVS\_LIB API addition - change bridge controller connection-mode * Imported Translations from Transifex * Drop the ovs\_lib compat layer as per TODO note * Removed ml2\_conf\_odl.ini config file * IPv6 SLAAC subnet create should update ports on net * Use 'port' instead of 'ports' to reference port from IPAllocation * Enhance OVSDB Transaction timeout configuration * Added config variable for External Network type in ML2 * Update decomp progress chart * Provide details for configure multiple DHCP agents * Stop running L3 functional tests with both OVSDB interfaces * Fix formatting errors in TESTING.rst * Pass correct port ID back to RPC caller * Fix intermittent ipset\_manager test failure * Fix mock return settings in test\_full\_uuids\_skip\_port\_id\_lookup * Add full-stack test * create\_resource should return maximum length str * Add clock sync error detection on agent registration * Log RPC initialization in L3 service plugin and ML2 * Add block name to switch config options for MLX plug-ins * Fix the ImportErrors in l3 and dhcp scheduler functional tests * Removed jsonrpclib dependency * Additions to TESTING.rst * Handle race condition on subnet-delete * Move values for network\_type to plugins.common.constants.py * allow OVSDB connection schema to be configurable * Add OVSDB connection as a parameter to the transaction * l3\_rpc: Fix a comment typo * Fix native OVSDB db\_get handling for UUID columns * Move iptables and ipset config registration into modules * Kill hostname validation for subnet:dns\_nameservers * Adds DVR functional test for multi-external networks * context: reuse base oslo.context class for to\_dict() * Fix routerid constraint migration * Synced versionutils from oslo-incubator * Removed ml2\_conf\_odl.ini config file * Router is not unscheduled when the last port is deleted * Remove L3 report\_state logging * Double functional testing timeout to 180s * Non-json body on POST 500's * OVSDB python binding should use row.delete() to remove rows * Revert connection option post full-stack tests * Handle SIGHUP in dhcp and l3 agents * Sync service from oslo-incubator * Imported Translations from Transifex 2015.1.0rc1 ----------- * Add logging to dangling port to ml2 delete\_subnet * Avoid synchronizing session when deleting networkdhcpagentbinding * Update L3 Agent Scheduler API tests * Revert "IPv6 SLAAC subnet create should update ports on net" * Add missing config parameters in neutron.conf * Moving VLAN Transparency support from core to extension * Re-use context session in ML2 DB get\_port\_binding\_host * Consider all address scopes in init\_l3 * Improves the description string for the config parameter metadata\_workers * Fix intermittent UT failures in test\_utils * OOP cleanup: start protected method names with underscore * Enhance TESTING.rst * Remove check for bash usage * Return from check\_ports\_exist\_on\_l3agent if no subnet found * Open Liberty development * Remove duplicated l3 router scheduler test cases * Remove tests from HA routers test framework * linuxbridge UT: Fix a regression of the recent ip\_lib change * Fix dynamic arp populate error for dvr routers * Reorganize plugin test modules * Reorganize unit test tree * Add ipset element and hashsize tunables * Allow metadata proxy running with nobody user/group * Skip example retargetable functional test * Prepare for unit test reorg * Remove orphaned nuage unit test module * Add API tests for subnet-create with subnetpool * Refactoring cleanup for L3 agent callbacks * Imported Translations from Transifex * Support multiple IPv6 prefixes on internal router ports * Fix functional test using local timeout value * Add index for port * Always run dnsmasq as root * Move network MTU from core REST API to extension API * Refactoring of L3 agent notifications for router * Fix docstring for l3\_dvr\_db.dvr\_vmarp\_table\_update * Treat all negative quota values as -1 * Router test enhancements * ovs\_neutron\_agent: Remove a redundant assignment of ovs\_status * Move orphaned api test - deux * IPv6 SLAAC subnet create should update ports on net * Add API tests for Neutron DVR extension * Add missing neutron/tests/unit/agent/common/\_\_init\_\_.py * Allow metadata proxy to log with nobody user/group * Move orphaned api test * Implement default subnet pool configuration settings * Define bridge/port fixtures for OVS/LinuxBridge/Veth backends * Update core reviewer responsibilities * Remove "Arguments dropped when creating context" logging * Some cleanup in L3 HA code * Fix reference to non-existent setup\_dvr\_flows\_on\_integ\_tun\_br * Modify a different agent in test\_update\_agent\_description * Move API tests to neutron.test.api * Simple subnetpool allocation quotas * Subnet allocation from a subnet pool * Simplify retargetable test framework * Increase max attempts to 2 for pings on ARP tests * Revert "Add ipset element and hashsize tunables" * Add API tests for subnetpool allocation * Handle no ofport in get\_vif\_port\_to\_ofport\_map * Update .coveragerc after the removal of Cisco Nexus monolithic plugin * Make floatingip reachable from the same network * Fix functional configure script * Enable ARP spoofing prevention by default * Support IPv6 Router * Move final remnants of router processing to router classes * Only call get\_engine().pool.dispose if \_FACADE * Stop using deprecated DEFAULT group for lock\_path * tests: don't rely on configuration files outside tests directory * Set floating IP port status to "N/A" * Add simple ARP spoofing protection * Imported Translations from Transifex * Add tests for the l3 agent namespaces manager * Make L3 agent honor periodic\_interval setting * Handle non-index lookups in native OVSDB backend * Fix error raising in security groups method * Update NEC plugin decomposition status * Auto-update gateway port after subnet-create * Allow update of ext gateway IP's w/out port delete * Support Dual-Stack Gateway Ports on Neutron Routers * Remove auto deletion of routers in unit tests * No allocation needed for specific IPv6 SLAAC addr assignment * Remove neutron.tests.sub\_base * Fix test case for DHCP agent interface restart * Store and log correct exception info * Test to verify shared attribute of network * Enable Process Monitor by default * Reload DHCP interface when its port is updated * Don't eagerly load ranges from IPAllocationPool * Revert "Fix validation of physical network name for flat nets" * Enable services on agents with admin\_state\_up False * Simplify base test cases * Send only one rule in queue on rule create/delete * Add full-stack tests framework * Stop any spawned ProcessMonitor at test cleanup * Add missing DeferredOVSBridge export * Use router state in get\_ha\_device\_name and ha\_network\_added * Added note about removing bridge from mappings * Add language around re-proposing specs for new releases * Follow up patch for Validate when DVR enabled, l2\_pop is also enabled * Fix displaying of devref for TestModelsMigrations * Use 1/0 as booleans for DB2 * Remove allow\_overlap from subnetpools API * If configured, set the MTU for fpr/rfp intefaces * Add L3 router plugin shim for Brocade MLX * Moves ovs\_lib to agent/common * OVS agent support on Hyper-V * No IPv6 SLAAC addrs for create router intf without fixed\_ips * Cisco UCS Manager ML2 Mechanism Driver * Cisco Nexus1000V ML2 Mechanism Driver * Rename/move/remove HaRouter methods * lb-agent: use 'replace' instead of 'add' with 'bridge fdb' * Add some useful notes in devref/db\_layer.rst * Fix a usage error of joinedload + filter in l3 scheduler * Move process\_ha\_router\_added/removed from HA agent to router * Ml2 Mechanism Driver for OVSvApp Solution * Add eventlet monkey\_patch helper * Move create\_dvr\_fip\_interfaces in to DVR * Deprecate use\_namespaces option * Add the default\_ipv6\_subnet\_pool config option * Fix common misspellings * Fix port status not being updated properly * Fix handling of before/after notifications in linuxbridge agent * Move external port processing to router classes * Expose ha\_state per router to agent binding via API * Decouple L3 and service plugins during DVR router migration * Transform BaseLinuxTestCase methods in helpers * Remove downgrade from existing migrations * Fix minor nits in \_notify\_l3\_agent\_new\_port() * Drop support for SQL Schema Downgrades * VMWare NSXv: Metadata default gateway param * Imported Translations from Transifex * Move README.odl into opendaylight directory * Fix missing spaces in strings split across lines * Fix typos in neutron/db/migration * Remove unnecessary 'IN vs ==' sql query branches * Fix intermittent failure in TestNetworksFailover UT * Fixes floating IP regression with multiple routers * Add no\_delete flag to UT router context manager * Updated from global requirements * Send notification to controller about HA router state change * Fix usage of 'default' parameter in 1955efc66455 migration * Move metadata proxy shared options to neutron.conf * Reuse nova batch notifier * Allow plugin to specify security-group rules ids upon creation * Add native OVSDB implementation of OVSDB API * Break coupling between ML2 and L3 during delete operation * Fix validation of physical network name for flat nets * Validate when DVR enabled, l2\_pop is also enabled * Fix create\_security\_group\_rule\_bulk\_native to return all created rules 2015.1.0b3 ---------- * Prepare Base(OVS)LinuxTestCase transformation in helpers * Improve DVR scale performance * Remove redundant unit tests from OVS DVR Agent * Hyper-V Agent decomposition * Enable to apply policies to resources with special plural * Add a missing mock in DHCPAgentWeightSchedulerTestCase * Basic subnetpool CRUD * Enable to specify context on POST requests during unittests * Fix a usage error of joinedload + filter in dhcp scheduler * Allow to request metadata proxy only from internal interfaces * Remove unused L3 HA RPC method * Replace keepalived notifier bash script with Python ip monitor * Add sanity check for OVSDB native support * Fix metering agent failure when chain missing * Fix minor decomp progress chart issues * Adding VLAN Transparency support for ML2 along with REST API changes * DHCP Service LoadBalancing Scheduler * Make DHCP tests cleanup neutron manager reference * Include IPv6 SLAAC addresses implicitly for port update * Api tests to cover network mtu attribute * Run more Rally benchmark on every patch * Fix DBDuplicateError handling in \_ensure\_default\_security\_group * Add ML2 VLAN mechanism driver for Brocade MLX and ICX switches * Include IPv6 SLAAC addresses implicitly for port create * Don't delete HA router primary VIP on agent restarts * Introduce External IPAM Interface * Expose Rest Api access to mtu attributes * Advertise mtu over dhcp * Add MTU selection to ML2 * IBM SDN-VE Plugin decomposition * Brocade Vyatta vrouter shim plugin for vendor decomposition * Fix spelling error in neutron.conf * OVS DVR UT: Remove an inappropriate str() conversion * Handle DBDuplicateError exception properly when creating default sg * Imported Translations from Transifex * Schedule net to a DHCP agt on subnet create * Revert "Set default of api\_workers to number of CPUs" * Add portsecurity extension support * Revert "fix check\_ports\_exist\_on\_l3agent in no subnet case" * Move Unix domain socket helpers to a common place * Move mlnx agent to be under ml2/drivers/mlnx * iptables firewall: add framework for iptables firewall functional test * Adding a cleanup for 'fip-' and 'snat-' namespaces in netns\_cleanup * replaces enumeration method used to get a list of interfaces * Remove unneeded DVRAgentRpcApiMixin from OVSDVRNeutronAgent * Prevent updating mac address of bound port * Update api tests from tempest * Set TEMPEST\_CONFIG\_DIR in the api tox env * Remove vendor entry point * Add a netns-cleanup functional test * Reduce db calls count in get\_devices\_details\_list * Move internal port processing to router classes * Brocade vendor code decomposition from neutron repo * Refactor \_remove\_unused\_security\_group\_info * Add MTU selection & advertisement settings to Neutron config * ML2 cisco\_nexus MD: sync config and models with vendor repo * fix check\_ports\_exist\_on\_l3agent in no subnet case * Fix netns-cleanup broken by ProcessMonitor refactor * Improve validate of remove\_router\_interface * Set default of api\_workers to number of CPUs * Refactor retry mechanism used in some DB operations * Revert "Revert "Remove port from ovsdb after its deletion"" * Add rootwrap daemon mode support * Break coupling between ML2 and L3 during create/update operations * Fix incorrect comments * Start metadata agent without trying to connect db * Remove router binding with router-interface-delete * Remove dead code * Update contribute.rst with Big Switch decomp * Migrate to oslo.log * Fix l3\_agentschedulers\_db for consistency of code * Return 404 when executing net-list-on-dhcp-agent with invalid agent\_id * ofagent: Update after networking-ofagent release * Use common agent.linux.utils.ensure\_dir method * Stop using RPC namespace to unbreak rolling upgrades * Add Mellanox decomposition progress to chart * Arista L3 Service Plugin decomposition * Fix pylint issue with type VS isinstance in event\_observers * Raise QuotaResourceUnknown in the quota engine * utils.execute: Add a debug-level log to record stdin * Imported Translations from Transifex * contribute.rst: Use consistent tags * Add README and requirements.txt for VMware plugins * Fix non-existent self.local\_subnets in DvrRouter class * Added oslo.log dependency * Don't notify dead DHCP agent of removed networks * Prevent calling waitall() inside a GreenPool's greenthread * Added check for emptyness where in\_ is being used * Improve performance of \_get\_security\_group\_member\_ips * NEC plugin code split * Imported Translations from Transifex * Change linux/ip\_lib code to better handle address families * portsecurity\_db: Fix a usage of is\_attr\_set * ofagent: Have a thin driver module * Don't start transaction during floating IP delete * linuxbridge UT: Mock get\_interface\_by\_ip * linuxbridge UT: Do not create the same instance in each cases * In Arista ML2 delete tenant without any resources * Initial copy of api tests from tempest * Fix tempest api testing * Use an existing function in process monitor tests * Fix dhcp config dir removed too soon * FIP debug messages * Add proccess monitor to keepalived * Fix wrong log output in neutron/neutron/agent/linux/dhcp.py * [contribute.rst] Current status of Freescale Codebase * portsecurity\_db: Use is\_attr\_set instead of a home-grown equivalent * Imported Translations from Transifex * Imported Translations from Transifex * Updated from global requirements * Add script to copy neutron api tests from tempest * ofagent: kill the left over after decomposition * Use accessors instead of private attributes for Ml2 plugin * Remove 'free' exclusions from pylint * Refactor the ProcessMonitor API * Networking OVS-DPDK plugin decomposition * Fix DB2 upgrade problem for Remove Hyper-V plugin * Big Switch Networks code split * Reduce code duplication and fix argument order in test\_wsgi * Replace IPv4 and IPv6 default addresses with constants * VMware NSX: Update decomposition progress table * Updated from global requirements * Vendor decomposition to move CSR1000v support to the networking-cisco repo * Move Neutron Policy pages into the tree * Adding DB model changes for Nuage Plugin post decomposition * Add ability to run pylint check on modified files only * Fix test tautology for DVR * Decompose the VMware plugin * Remove references to 0.0.0.0/0 in iptable rules * Updated from global requirements * Change metadata driver unit tests to use monitored spawn * Decouple L3 and VPN service plugins during router operations * Move \_set\_subnet\_arp\_info to dvr\_router * Refactor DVR \_arp\_entry methods * Refactor management of namespaces in the L3 Agent * Raise error upon deleting subnet with router ports * Imported Translations from Transifex * OVS UT: Remove useless return\_value for setup\_integration\_br * Introduce ip address monitor * Add cisco decomposition progress to chart * oslo: sync all modules that depend on incubator log module * test\_metadata\_agent: don't check implementation details * Progress chart for MidoNet * Extend test coverage for iptables\_firewall.py * Default the Linuxbridge agent to enabling VXLAN * Remove HyperVNeutronPlugin * ml2 plugin: use attributes.{NETWORK, SUBNET, PORT} consistently * ml2 extension driver: more tests, fix data argument inconsistency * Use oslo\_config choices support * Metaplugin decomposition * ofagent: Vendor code decomposition * contribute.rst: Fill in the current status of ofagent * Missing entry points for cisco apic topology agents * Prevent direct port-delete of FIP Agent GW and CSNAT * PLUMgrid plugin decomposition * Improve structure of Vendor Decomposition progress chart * Removing a router twice from the same agent shouldn't cause an error * Simplify prepare\_veth\_pairs in functional tests * Add a functional test for iptables\_manager.binary\_name * Add InvalidIpForNetwork and InvalidIpForSubnet exception * ovs\_neutron\_agent should exit gracefully * Ensure tests run under python2.7 * Validate string length at API level * Capture progress chart for vendor decomposition * Fixes formatting errors in devref documentation * Imported Translations from Transifex * Fix retrieval of shared firewall\_policies * Password config options should be marked secret * Check whether sudo is enabled in BaseSudoTestCase.setUpClass * Revert "Remove port from ovsdb after its deletion" * Add filter for provider network attributes in ML2 * tests: initialize policy in BaseTestCase * policy: don't hack around oslo.config path search algorithm * Make listing security groups faster * Allow AsyncProcess to block on process start and stop * Don't mock plugin in L3SchedulerTestBaseMixin * Adds migration script for Hyper-V Plugin tables * Make del\_fdb\_flow() idempotent * Update default tox envlist to match voting gate checks * Added a policy for retrieving the agent hosting a load balancer * Avoid ObjectDeletedError while accessing deleted binding * Correct db functional tests to support oslo.db 1.50 * Avoid DetachedInstanceError after session rollback * Always fill UDP checksums in DHCP replies * remove unused code in metadata agent code * Move pylint checks to pep8 testenv * Change L3 agent AdvancedService class to be non-singleton * Passes the plugin context variable in the ML2 Extension Driver API * devref: added guidelines to maintain service entry points * VMware NSXv: Added router-type to database model * Remove discover from test requirements * Add callbacks-based system to Neutron * Refactor Pinger class * Create/Delete FIP Agent gateway port only if DVR Routers * Move the assignment of existing\_floating\_ips before try block * Fix misspellings words in neutron * Ensure arping always exits * Updated from global requirements * wsgi: remove explicit monkey\_patch() call * If providers exist in neutron.conf, don't look in services conf * test\_ovs\_dvr\_neutron\_agent: Use consistent variable names * Nuage core plugin decomposition * devref: consider sphinx warnings as failures * devref: don't link to nonlocal images * devref: fixed class name for test\_migrations autodocumentation * devref: updated documentation for oslo-incubator * devref: updated documentation for advanced services * Avoid fetching network in \_commit\_port\_binding * VMware: Router Type Extension Support * OVS UT: Change misleading constants in veth tests * test\_l2population: Use a fake mech driver instead of ofagent * l2population\_rpc: Make fdb\_add\_tun/fdb\_remove\_tun more flexible * Make nova notifier work with sessions * Fix parameters in exception handling * adopt namespace-less oslo imports * Do not run neutron-ns-metadata-proxy as root on dhcp agent * Move Floating IP processing to Router classes * Updated from global requirements * Improve exception handling in \_process\_router\_update() * Cisco Nexus ML2 Vendor decomposition * Remove versioning import of novaclient * Remove remaining uses of passing root\_helper * Remove root\_helper arg from sanity checks * Enable pylint unnecessary-pass * Enable pylint no-value-for-parameter * Enable pylint bad-super-call * Enable 'free' pylint checks * Remove reference to self.services\_sync * Fix type of exception in ml2 l2pop * VMware NSXv: Add configuration options to nsx.ini * Mock link local allocator write so UT doesn't write a file * VMWare NSXv: Add configuration params to nsx.ini * Remove error logs for a common situation (non created ipsets) * Default route missing for IPv6 subnets in HA Router * Unify logic that determines liveliness of DHCP agent * fix for \_get\_external\_device\_interface\_name trace * ML2: remove underscore from public method * Fix static strings with labels for DVR * Get rid of rpc to fetch fip agent port on agent * Combining sec groups member update RPC calls * VMWare NSXv: id fields should be nullable * Check if routing rule exists before adding * Remove root\_helper arg from DHCP agent * Remove root\_helper arg from AsyncProcess * Remove root\_helper arg from linuxbridge * Remove root\_helper arg from SecurityGroupAgentRpc * Moved several services into neutron.cmd.eventlet * Monkey patch all the code inside neutron/cmd/eventlet/.. * tests: monkey patch stdlib before importing other modules * Don't monkey patch netns\_cleanup * Remove root\_helper arg from IpsetManager * Revert "Add the rebinding chance in \_bind\_port\_if\_needed" * Remove root\_helper arg from IptablesManager * Remove root\_helper arg from external\_process * Add a functional test that checks HA router is configured on a restarted agent * Update midonet plugin requirements * Stop using passed root\_helper in ip\_lib * OVS UT: Fix some confusions between local vlan id and segmentation id * Un-break tox for unit tests * Fix FIP agent gw port delete based on external net * Skip DBDuplicateEntry exception in security group creation * Hyper-V: Fixes security groups issue * Fix the api job * Setup br-tun in secure fail mode to avoid broadcast storms * Delete qg device during DVR-SNAT router deletion * Automate host configuration for functional testing * ML2: Hierarchical port binding * ML2: DB changes for hierarchical port binding * Remove RPC dependency to create FIP agent gw port * Fix typo in bash tool * Remove remaining root\_helper args from plugins * Fix usage drop\_constraint in 2a1ee2fb59e0 migration * Fix index name in downgrade 26b54cf9024d migration * Remove root\_helper arg from linux interface * Remove root\_helper arg from L3 Agent * OVS DVR: Remove dead code * Updated from global requirements * Fix AttributeError exception for API's test\_network\_lifecycle * Remove root\_helper arg for ovs\_lib * Raise timeout for test\_conntrack\_disassociate\_fip * Cleanup in keepalived tests * Add run\_as\_root option to utils.execute * Revert "monkey patch stdlib before importing other modules" * Remove unused RPC methods from l3\_rpc * Tweak mocking logic for L3 plugin tests * Move NCS mech driver to its new home * Added policy for lbaas v2 agent extension resource * keepalived: use sh instead of bash for notifier scripts * Refactor to facilitate DVR scale performance * hacking: also catch 'import oslo.\*' imports * Moved hacking unit test into proper location * Stale VXLAN and GRE tunnel port/flow deletion * Use ovsdb-api neutral column/value mappings * Prepare to functionally test OVSDB interfaces * NEC: Merge NEC plugin models into single module * Remove remaining do\_delete from unit tests * Typos fixed * Scope state reporting rpc api using a messaging namespace * Remove use of keepalived 'vrrp\_sync\_group' as it is unused * Scope dvr rpc api using a messaging namespace * Updated from global requirements * Remove port from ovsdb after its deletion * Add index on tenant\_id * Remove deprecated DEFAULT.root\_helper * Provide routes for neighbor IPv4 subnets * OVS DVR: Use a right mac address value to compose a flow * Refactor radvd control in the l3-agent * monkey patch stdlib before importing other modules * Don't crash when adding duplicate gre allocation * Fix lack of device ownership enforcement for DVR routers * Search in /sys/class/net for network devices * Adopt rpc\_api devref to new oslo\_messaging namespace * Fix minor nits with the devref's contribute section * Remove VPN specific exception * Correctly mock-out 'ip route...' calls in IPv6 test * Cleanup dead code for dnsmasq * Add mtu attributes to network model * Add the rebinding chance in \_bind\_port\_if\_needed * Add vlan\_transparent attribute to network model * Check conntrack rule cleanup on floating IP disassociate * l2-pop shouldn't notify agent about inactive ports * Drop devstack-gate files from Neutron repo * Use weak ref to avoid deleting fip namespace through agent * Move DVR floating ip methods to dvr\_router * Provide more details about testing strategies * Add section for DevStack Integration Strategies to the DevRef * VMware: consolidate NSX models * Restore and fix vmware unit tests * Move extra routes processing to router classes * oslo: migrate to namespace-less import paths 2015.1.0b2 ---------- * Fix breakage in all service repo unit tests, due to duplicate imports of exts * Log entry when no Floating IP interface present * Refactor logging in loop to only log debug messages once * Nuke a useless lambda wrapper and call to iterkeys (review feedback) * Nuke remaining service config and extensions from main repo * Pass '--dhcp-authoritative' option to dnsmasq * Imported Translations from Transifex * ml2: Simplify \_process\_provider\_create * Fix extra-dhcp-opt on stateless dhcpv6 subnet * Updated from global requirements * ML2: Use same port binding logic for DVR ports as non-DVR ports * Improve robustness of network failover * Decrease rpc timeout after agent receives SIGTERM * Configures RADVD to send managed flag in RA for DHCP\_STATEFUL * Make prevent\_l3\_port\_deletion handle missing port * Backout 152195, which doesn't check the same token that it saves * NSX DB models split, part 3 (and final) * NSX DB models split, part 2 * Discriminate loaded extensions using their alias * Refactor ml2 manager * Extension moving tweaks, exceptions and extension path fix * Log tenant ports if subnet could not be deleted * Fixing several misspellings in neutron * NSX DB models split, part 1 * Imported Translations from Transifex * Enable super-on-old-class pylint check * fixes error logging to use the right exception parent class * Drop bw compact module for OpenDayLight * Don't pass the port down to the floating ip processing * Move agent.linux.utils tests to proper location * Drop deprecated namespace for oslo.rootwrap * Encapsulate DVR Fip namespace * Move ha router functionality from the agent to ha\_router * Remove duplicate logging of attribute validation errors * Add requirements.txt file for OpenDaylight Mech Driver * Mechanisms to move extensions and config into service repos * Fix flake exclude matching of .\* * Hardening unittest, make resilient to address assignment order * Allow to request metadata proxy only with redirection * Remove unused mocks * Thining Arista ML2 driver from neutron tree * Allow port mac\_address to be modified * Removed redundant statement from l3agentscheduler * Implements the ProcessMonitor in the l3\_agent * Add option to remove networks from dead DHCP agents * Thin MLNX ML2 mechanism driver and agent * Fixing a log message in Arista L3 Service Plugin * Not assign dynamic IPv6 address on dhcp interface * Default security group table * Support Extra DHCP Options for IPv4 and IPv6 * Refactor \_convert\_sgr\_to\_iptables\_rules in iptables\_firewall * Do not check twice IP allocations for auto-address subnets * Make the interface driver available to the router classes * Make agent config available to the router classes * Updated from global requirements * Drop bin/neutron-rootwrap * Refactor iptables rule expansion for the non ipset case * Set locale before check dnsmasq version * Freescale FWaaS Plugin: Update to setup.cfg * Allow 'max\_l3\_agents\_per\_router' to be set to '0' * test\_agent\_scheduler: Fix a misleading variable name * Fix AttributeError when using DVRServerRpcApi * Add abandon script from nova * Add missing Connection.close() method * Deleting HA router with attached port causes DB inconsistencies * Refactor the ProcessMonitor \_exit\_handler to ProcessMonitor * TestL2PopulationRpcTestCase: Stop loading linuxbridge mech driver * Return 404 when executing router-list-on-l3-agent with invalid agent\_id * VLAN support for DVR * Fixes Hyper-V agent root\_helper issue * Ensure ofports are converted to string before trying to use join() * Add coverage for extra routes extension * Add address family to 'ip rule' calls * Add OVSDB abstract API * Add functional tests for IptablesManager using tcp/udp * dhcp: move dnsmasq version check to sanity\_check * Use DVRServerRpcApi instead of a mixin * Scope secgroup rpc api using a messaging namespace * Add and use SecurityGroupAgentRpc * hyperv: drop useless messaging.Target on HyperVSecurityAgent * tests: don't spread fixtures.TempDir throughout test cases * Extract l2pop/DVR controller logic to common method * Imported Translations from Transifex * attributes: Additional IP address validation * Mention networking\_odl in README.odl * Updated from global requirements * Overload correctly BASEV2.\_\_table\_args\_\_ * Add notes on how to deal with stable branches * Configure IPv6 LLADDR only on master L3 HA instance * Do not duplicate message consumers * Add index on db "allocated" columns * pep8: cleaned up excludes * Remove check\_i18n tox target * Implements ProcessMonitor in the dhcp\_agent * Functional test IPAM DB operation * If router is HA, get current\_cidrs from keepalived object * Move process monitor settings to neutron.conf AGENT section * Drop SecurityGroupServerRpcApiMixin * sriovnicagent: drop usage of SecurityGroupServerRpcApiMixin * sriovnicagent: untangle SecurityGroupAgentRpcMixin * mlnx: drop usage of SecurityGroupServerRpcApiMixin * mlnx: untangle SecurityGroupAgentRpcMixin * linuxbridge: drop usage of SecurityGroupServerRpcApiMixin * linuxbridge: untangle SecurityGroupAgentRpcMixin * Use db constraint to ensure mac address uniqueness * Ignore 404 error and lower a warning log to info * Reorganize OVSDB API * Use proper capitalization for OVS table names * Move shared metadata driver related config options * Remove useless constant from l3 agent module * Added test\_dvr\_router\_lifecycle to cover dvr * Imported Translations from Transifex * Use constants from networking\_odl project * Initialize dist\_fip\_count after agent restart * Fixes Multiple External Networks issue with DVR * Replace FLOATING\_IP\_CIDR\_SUFFIX constant with utils * tests: drop usage of SecurityGroupServerRpcApiMixin * ovs: drop usage of SecurityGroupServerRpcApiMixin * oneconvergence: drop usage of SecurityGroupServerRpcApiMixin * ofagent: drop usage of SecurityGroupServerRpcApiMixin * nec: drop usage of SecurityGroupServerRpcApiMixin * hyperv: drop usage of SecurityGroupServerRpcApiMixin * bigswitch: drop usage of SecurityGroupServerRpcApiMixin * Create SecurityGroupServerRpcApi and add some docs * Improve agent-based flat/vlan ml2 port binding failure logging * ml2: remove stale \_filter\_nets\_l3 in get\_networks * drop unused test rootwrap filter file * Updated from global requirements * SIGHUP keepalived if L3 agent restarts * Update \_cur names to \_current in iptables\_firewall.py * Added comments, and refactored \_add\_rule\_by\_security\_group * Improve test coverage of dhcp agent scheduling * Imported Translations from Transifex * tools/split.sh: Tweak commit message * Switch to using abc in the retargetable client * common\_db\_mixin.py: simplify CommonDbMixin * Fixes blocking of VRF config in Arista L3 Plugin * Drop \_test\_rootwrap\_exec test * Fix pylint unbalanced-tuple-unpacking warning * Corrected singulars/plurals in iptables\_firewall.py * Create DvrRouter and HaRouter as a sub-class of Router * Remove unused self.sync\_progress attribute * DHCP agent restructuring * Move Monkey patch back to being as early as possible * Fix outstanding failures with Neutron API job * Disable unbalanced-tuple-unpacking * Revert "Change transaction isolation so retry logic could work properly" * Change transaction isolation so retry logic could work properly * Updated from global requirements * Refactor the \_get\_external\_device\_interface\_name method * Refactor of floating ip processing in L3 Agent * ML2: Driver API changes for hierarchical port binding * Fix some assertEqual argument orders * Don't log a warning if an iptables chain doesn't exist * Migrate to oslo.concurrency * Replace missing space in error message * Clarify misleading iptables comment * Fix missing spaces in error messages * make delete\_router send delete\_port to core\_plugin * VMWare-NSXv: VMWare NSXv extensions * Dropped fixture module * base.py: Improve exception handling * Correct \_test\_delete\_ports\_by\_device\_id\_second\_call\_failure * Add ovsdb-related functional tests * VMWare-NSXv: VMWare NSXv configuration file * Imported Translations from Transifex * Create arping helper in ip\_lib * Initial thin ML2 mechanism driver * Enable adding new tag with options * Call on dhcp-agent DhcpLocalProcess.restart() breaks dhcp * Fixs shared networks in Arista ML2 driver * Move agent cleanup scripts to cmd module * Fix IP allocation for multiple slaac subnets * tests: don't restore stopped mock that is set in setUp() * misc-sanity-checks.sh: Some cleanups * Log iptables rules in a readable format * Remove main alias for bw compat with vpn agent * Midonet plugin decomposition * Fix topic for provider security group update * Specify prefix length for IPv6 subnets * Service split: cleaned up setup.cfg * VMWare NSXv DB model bugfix * Speed up initial L3 full sync time * hacking: enable H238 (old style class declaration, use new style) * hacking: enable W292 (no newline at end of file) * Update hacking to 0.10 * Use "if dict.get(key):" instead "if key in dict and dict[key]:" * Rename qexception->nexception * Fix AttributeError on check\_foreign\_keys in functional job * Catch StaleDataError in update\_device\_down * Code improvement in type\_vxlan.py and type\_gre.py files * Ensure config directory created before updating leases * Allow IptablesManager to manage mangle table * Fix IPv6 Subnet Slaac Check * Imported Translations from Transifex * Move non-bridge-related OVSBridge methods to BaseOVS * Move metadata agent entry to its own file * Run only one instance of Nuage sync cycle at a time * Updated from global requirements * Scope metadata rpc api using a messaging namespace * Provide doc string pointers for the dhcp agent rpc api * Remove DBDuplicateEntry columns check * Limit permission change * Break out config and entry point out of l3/agent file * Validate legacy router services before migration * Clarify dnsmasq version check failure message * Update comment about metadata\_proxy\_shared\_secret config * Remove redundant tunnel ids from ovs agent * Add index generation for IPv6 rules for DVR * Correct l3-agent iptables rule for metadata proxy * Fix UT for L2pop test\_get\_agent\_ports\_no\_data() * Move postcommit ops out of transaction for bulk * Reset policies after RESOURCE\_ATTRIBUTE\_MAP is populated * Remove SELECT FOR UPDATE from delete\_network and delete\_subnet * Bump minimal dnsmasq version to 2.67 * Make L3 HA VIPs ordering consistent in keepalived.conf * Add Process class helper to manage processes with namespace * Make lb mechanism driver use enable\_security\_group flag * Catch PortNotFound and SubnetNotFound during network\_delete * HA for DVR - schema migration and change * Revert "Revert "Add metadata proxy L3 agent driver"" * moving vxlan module check to sanity checks and making practical * Drop functional/contrib directory * refactor l3-agent to include dvr.py * Validate IPv6 subnet while associating to Router * VMWare-NSXv: VMWare NSXv database models * Deal with PEP-0476 certificate chaining checking * Reduce duplicate code in test\_iptables\_manager * Add support for retargetable functional api testing * print error when no match mapping found in check\_segment\_for\_agent * Tweak gate hooks scripts to handle both functional and api jobs * Replace mention of nose with nose2 in devref * Skip adding ips from non dhcp enabled subnets to hosts file * Add developer documentation for plugins/drivers contributions * Deletes floating agent gw port on disassociate * Add help text for 'host' parameter in neutron.conf file * Updated keystone\_admin conf section to reflect changes in middleware * Removed spurious check for ip version * Ensure test\_metaplugin handles random hashseeds * Ignore non-existent ports during OVS intf list * [apic ml2] Bind ports regardless of the owner * Improve unit test coverage for Ml2 db.py * Delete the console scripts for lbaas and vpnaas * Confusing message deleting default security group * Enable the "not-callable" pylint check * ovs\_dvr: Use lazy logging interpolation * Add a constant for router interface device owners * Stale VXLAN & GRE tunnel endpoint deletion from DB * Add support for flat networks in SRIOV Mechanism Driver * Retry on unassigned ofport instead of treating it as a failure * VMware: fix security group check on port create * Eventlet green threads not released back to pool * Don't unnecessarily loop through all ports/interfaces * Set type=internal as part of port creation * Fix DVR flow problems for IPv6 subnet * Allow to specify IP address of floating ip * Do not count dvr agents while creating HA ports * csr1kv\_hd\_driver: Improve exception handling * Remove \_delete\_port\_security\_group\_bindings from delete\_port * Remove useless parameter from l3\_dvr\_db.py * Clean-up sanity checks done via shell scripts * Do not run neutron-ns-metadata-proxy as root on L3 agent * Correct invalid indentation in is\_dvr\_serviced * Add validation for the dvr router l3agent binding * Fixes spelling error * get\_binary\_name should returns strings without spaces * validate L3 HA min/max \_l3\_agents\_per\_router * Enable pylint checks for "anomalous" string escapes * Tighten dnsmasq version regex * Remove unnecessary regex grouping * Combine author\_tag and log\_translation\_hint regexes * ML2 UT: Fix incorrect mock return value * ipv6: set OtherConfig flag for DHCPv6 stateless subnets * PLUMgrid plugin: Fix for delete subnet with admin context * brocade: Use lazy logging interpolation * linuxbridge: Use lazy logging interpolation * embrane: Use lazy logging interpolation * bigswitch: Use lazy logging interpolation * Use lazy logging interpolation * Cisco: logging incorrectly called with (fmt, arg) tuple * ml2: remove superfluous %s in LOG.debug() format * Fix typo'd format parameter in midonet\_lib.py * Update L3 agent drivers singletons to look at new agent * Prevent symlinks to be added to the tree * Copy the contrib directory instead of moving it * Revert "Add metadata proxy L3 agent driver" * Scope dhcp rpc api using a messaging namespace * Validate local\_ip for Linuxbridge agent * Allow setting a tenant router's external IP * Remove NSX 'service' plugin * Imported Translations from Transifex * Move DB TestModelsMigrations from unit to functional * tests: drop unit tests that only check default configuration values * Backward compatibility for advanced services * Update heal\_script for alembic 0.7.1 2015.1.0b1 ---------- * Add metadata proxy L3 agent driver * Updated from global requirements * Move contrib directory to base test directory * Add OVS status and fix OVS crash * Option for root\_helper when checking namespace * Cleanup req\_format in test\_api\_v2\_resource * Imported Translations from Transifex * Cisco: unsupported format character in log format * Correct arguments to logging function * Support 'alive' filter for get\_agents() in agents\_db * Minor lbaasv2 things from the feature branch, needed in neutron * Advanced services support in neutron-db-manage * Remove locking from network and subnet delete op * Removed unused iso8601 dependency * Avoid unnecessary explicit str() conversion around exceptions * Add functional test for l3-agent metadata proxy * Remove mlnx plugin * Set timeout for functional job * Enable test\_migration * Fix neutron hang for IPv6 allocation pool update * tests: initialize admin context after super().setUp call * Improve performance of get\_active\_networks\_info * Fixed test test\_update\_port\_security\_off\_address\_pairs * openvswitch/ofagent: Remove OVS.enable\_tunneling option * Imported Translations from Transifex * Remove unused dependencies * Generate testr\_results.html for neutron functional job * L3 Agent restructure - observer hierarchy * Replace non-ovs\_lib calls of run\_vsctl with libary functions * Don't restore stopped mock that is initialized in setUp() * Separate wait\_until to standalone function * Imported Translations from Transifex * Mock up time.sleep to avoid unnecessary wait in test\_ovs\_tunnel * Catch duplicate errors scheduling SNAT service * Fix for KeyError: 'gw\_port\_host' on l3\_agent * Migrate to oslo.context * Have L3 agent catch the correct exception * Not nova but neutron * Remove broad exception catch from periodic\_sync\_routers\_task * Fix race condition in ProcessMonitor * Updated from global requirements * Refactor process\_router method in L3 agent * Switch to using subunit-trace from tempest-lib * Move classes out of l3\_agent.py * Prettify tox output for functional tests * Services split, pass 2 * Fix IPv6 RA security group rule for DVR * Imported Translations from Transifex * ofa\_test\_base: Fix NoSuchOptError in UT * Add lbaasv2 extension to Neutron for REST refactor * Remove TODO for H404 * Update rpc\_api docs with example version update * Auto allocate gateway\_ip even for SLAAC subnets * Updated from global requirements * Split services code out of Neutron, pass 1 * Use comments rather than no-op string statements * Fix AttributeError during startup of ovs agent in DVR mode * Enforce log hints * Disallow log hints in LOG.debug * Reduce code duplication in test\_linux\_dhcp * Print version info at start * Enforce log hints in ofagent and oneconvergence * Make sudo check in ip\_lib.IpNetnsCommand.execute optional * Move set\_override('root\_helper', ...) to base functional class * Imported Translations from Transifex * IpsetManager refactoring * Update i18n translation for NEC plugin log msg's * return the dict of port when no sec-group involved * Imported Translations from Transifex * Update i18n translation for IBM plugin log msg's * Workflow documentation is now in infra-manual * tox.ini: Prevent casual addition of bash dependency * Updated from global requirements * Remove RpcCallback class * Convert several uses of RpcCallback * Fix up an old RpcProxy assumption * Remove RpcProxy class * Cleanup recent generalization in post mortem debugger * radvd: pass -m syslog to avoid thread lock for radvd 2.0+ * Get rid of py26 references: OrderedDict, httplib, xml testing * Imported Translations from Transifex * Fix enable\_metadata\_network flag * Fix program name in --version output * Enforce log hints in opencontrail * Update i18n translation for Metaplugin plugin * Update i18n translation for Brocade plugin log msg's * Update i18n translation for Nuage plugin * Update i18n translation for Embrane plugin * Enforce log hints in neutron.plugins.plumgrid * Remove ovs-vsctl call from OVSInterfaceDriver * Update i18n translation for Midonet plugin * Enforce log hints in neutron.plugins.sriovnicagent * Enforce log hints in neutron.plugins.hyperv * Imported Translations from Transifex * Drop RpcProxy usage from DhcpAgentNotifyAPI * Updated the README.rst * Fix base test class for functional api testing * Use oslo function for parsing bool from env var * Don't block on rpc calls in unit tests * Refactor test\_migration * Strip square brackets from IPv6 addresses * Update i18n translation for BigSwitch plugin log msg's * Imported Translations from Transifex * pretty\_tox.sh: Portablity improvement * iptables\_manager: Fix get\_binary\_name for eventlet * test\_dhcp\_agent: Fix no-op tests * Drop old code from SecurityGroupAgentRpcApiMixin * Drop RpcProxy usage from ml2 AgentNotifierApi * Update i18n translation for Mellanox plugin and agent log msg's * Drop RpcProxy usage from L3AgentNotifyAPI * Simplify L3 HA unit test structure * Update i18n translation for VMware NSX plugin log msg's * Alter execute\_alembic\_command() to not assume all commands * hacking: Check if correct log markers are used * Fix hostname validation for nameservers * Removed python2.6 rootwrap filters * Imported Translations from Transifex * MeteringPluginRpc: Fix crash in periodic\_task * Enable undefined-loop-variable pylint check * Remove unused variables from get\_devices\_details\_list * Change description of default security group * Fix incorrect exception order in \_execute\_request * Migrate to oslo.i18n * Migrate to oslo.middleware * Remove unused xml constants * Check metadata iptables chains during functional test * Drop RpcProxy usage from MeteringAgentNotifyAPI * Drop RpcProxy usage from l2population code * Drop RpcProxy usage from cisco apic ml2 plugin * Drop RpcProxy usage from oneconvergence plugin * Synced processutils and periodic\_task modules * Migrate to oslo.utils * Fix floating-ips in error state in dvr mode * Reject trailing whitespaces in IP address * Imported Translations from Transifex * CSCO:Tenants not to access unshared n/w profiles * Drop sudo requirement from a unit test * Remove Python 2.6 classifier * Update i18n translation for Cisco plugins and cfg agent log msg's * Remove ryu plugin * Imported Translations from Transifex * Drop RpcProxy usage from nec plugin * Drop RpcProxy usage from mlnx plugin * Drop RpcProxy usage from ibm plugin * Drop RpcProxy usage from hyperv plugin * Drop RpcProxy usage from cisco.l3 * Drop RpcProxy usage from cisco.cfg\_agent * Drop RpcProxy usage from brocade plugin * Update rally-jobs files * Test HA router failover * Imported Translations from Transifex * Update i18n translation for linuxbridge log msg's * Update i18n translation for openvswitch log msg's * Update i18n translation for ML2 plugin log msg's * Updated from global requirements * Imported Translations from Transifex * Enforce log hints in neutron.services * Enforce log hints in neutron.services.metering * Fix metadata proxy start problem for v6-v4 network * Fix AttributeError in RPC code for DVR * Drop RpcProxy usage from bigswitch plugin * Drop RpcProxy usage from VPNaaS code * Drop RpcProxy usage from metering\_agent * Fix context.elevated * Tighten up try/except block around rpc call * Implement migration of legacy routers to distributed * run\_tests.sh OS X script fixes * Eliminate unnecessary indirection in L3 agent * Show progress output while running unit tests * Drop RpcProxy usage from LBaaS code * Enforce log hints in neutron.services.loadbalancer * Enforce log hints in neutron.services.firewall * Enforce log hints in neutron.services.l3\_router * enable H401 hacking check * enable H237 check * Updated from global requirements * Check for default sec-group made case insensitive * Update i18n translation for neutron.server/scheduler log msg's * Update i18n translation for neutron.notifiers log msg's * Update i18n translation for neutron.common/debug log msg's * Imported Translations from Transifex * ofagent: Remove obsolete bridge\_mappings (plugin side) * Delete FIP namespace when last VM is deleted * Fix a race condition adding a security group rule * Drop RpcProxy usage from FWaaS code * Drop RpcProxy usage from neutron.agent.rpc.PluginApi * Fix a copy/pasted test mistake * Drop test code copied from nova * Drop several uses of RpcCallback * Add some basic rpc api docs * Drop RpcCallback usage from DhcpRpcCallback * Drop RpcProxy usage from PluginReportStateAPI * Fix hostname regex pattern * Catch NoResultFound in \_get\_policy\_profile\_by\_name * Validate loadbalancing method when updating a pool * Update i18n translation for neutron.api log msg's * Catch DBReferenceError exception during binding a router * Enable default SNAT from networks connected to a router indirectly * Imported Translations from Transifex * BSN: Optimistic locking strategy for consistency * BSN: include missing data in floating IP call * ofagent: Remove obsolete bridge\_mappings (agent side) * NSX: Validate gateway device list against DB * Drop RpcProxy usage from MetadataPluginApi * Drop usage of RpcProxy from L3PluginApi * Prevent an iteration through ports on IPv6 slaac * Use a string multiplier instead of 59 repetitions * Convert all incoming protocol numbers to string * Updated from global requirements * Correct raw table regex in test\_security\_groups\_rpc * BSN: Add network to ext\_gw\_info sent to backend * BSN: Set inconsistency record on delete failure * Fix PYTHONHASHSEED bugs in test\_security\_groups\_rpc * Subnet delete for IPv6 SLAAC should not require prior port disassoc * Fix client side versions in dhcp rpc API * Drop usage of RpcProxy from DhcpPluginApi * linuxbridge-agent: make vxlan unicast check more efficent * Moved out common testcases from test\_type\_vxlan.py * Update i18n translation for neutron.extension log msg's * Update i18n translation for neutron.db log msg's * Update i18n translation for neutron.cmd log msg's * Update i18n translation for neutron.agents log msg's * enable F812 check for flake8 * enable F811 check for flake8 * Decrease policy logging verbosity * Support pudb as a different post mortem debugger * Cleanup and refactor methods in unit/test\_security\_groups\_rpc * switch to oslo.serialization * Add rootwrap filters for ofagent * Updated policy module from oslo-incubator * Resolving some spelling mistakes * Fix for FIPs duplicated across hosts for DVR * Drop neutron.common.rpc.MessagingTimeout * Remove neutron.common.rpc.RemoteError * Remove neutron.common.rpc.RPCException * Remove useless return * Cisco VPNaaS and L3 router plugin integration * Fix missing allowed command in openvswitch xenapi agent * fix event\_send for re-assign floating ip * Remove openvswitch core plugin entry point * rootwrap config files reference deleted quantum binaries * Fix L3 HA network creation to allow user to create router * Update default value for agent\_required attribute * SRIOV: Fix Wrong Product ID for Intel NIC example * Imported Translations from Transifex * Updated from global requirements * Purge use of "PRED and A or B" poor-mans-ternary * Include call to delete\_subnet from delete\_network at DB level * Use correct base class for unit tests for ML2 drivers * Replace "nova" entries in iptables\_manager with "neutron" * Drop and recreate FK if adding new PK to routerl3bindings * Imported Translations from Transifex * Remove duplicate ensure\_remove\_chain method in iptables\_manager * ML2: fix file permissions * Fix sneaky copypaste typo in ovs agent scheduler test * Make L2 DVR Agent start successfully without an active neutron server * Detect if iproute2 support SR-IOV commands * Use stop() method on MessageHandlingServer * Rename constant to a more appropriate name * Big Switch: Fix SSL version on get\_server\_cert * Check for concurrent port binding deletion before binding the port * Imported Translations from Transifex * Batch ports from security groups RPC handler * Fix incorrect int/tuple comparison during binary search * Big Switch: Send notification after port update * Allow to add router interface to IPv6 SLAAC network * ML2 Cisco Nexus MD - not overwriting existing config * Reorder operations in (l3\_dvr) update floating ip * Use RPC instead of neutron client in metadata agent * Add assertion to test\_page\_reverse method * Adds an option to enable broadcast replies to Dnsmasq * Add advsvc role to neutron policy file * NSX: allow multiple networks with same vlan on different phy\_net * NSX: Fix foreign key constraint delete provider network * Imported Translations from Transifex * Fix 'Length too long' error in neutron-dsvm-functional tests * Remove use\_namespaces from RouterInfo Property * Fix handling of CIDR in allowed address pairs * Updated from global requirements * Remove XML support * enable F402 check for flake8 * enable E713 in pep8 tests * NEC plugin: Allow to apply Packet filter on OFC router interface * \_update\_router\_db: don't hold open transactions * Big Switch: Switch to TLSv1 in server manager * Only resync DHCP for a particular network when their is a failure * Validate network config (vlan) * Validate local\_ip for OVS agent is actual ip address * Imported Translations from Transifex * Hyper-V: Remove useless use of "else" clause on for loop * Enable no-name-in-module pylint check * Move disabling of metadata and ipv6\_ra to \_destroy\_router\_namespace * Updated from global requirements * Adds macvtap support * Remove duplicate import of constants module * Switch run-time import to using importutils.import\_module * Enable assignment-from-no-return pylint check * tox.ini: Avoid using bash where unnecessary * l2population\_rpc: docstring improvements * Fix race condition on processing DVR floating IPs * neutron-db-manage finds automatically config file * Ensure test\_agent\_manager handles random hashseeds * Ensure ofagent unit tests handles random hashseeds * Moves the HA resource creations outside of transaction * Modify docstring on send\_delete\_port\_request in N1kv plugin * Empty files should not contain copyright or license * Remove superfluous except/re-raise * Remove single occurrence of lost-exception warning * Schema enhancement to support MultiSegment Network * Remove redundant initialization and check from DVR RPC mixin * Improve performance of security group DB query * Optimize query in \_select\_dhcp\_ips\_for\_network\_ids * Updated cache module and its dependencies * Updated service.py and its dependencies * Updated fileutils and its dependencies * Cisco N1kv: Fix update network profile for add tenants * DB: Only ask for MAC instead of entire port * Only fetch port\_id from SG binding table * NSX: Make conn\_idle\_timeout configurable * nsx plugin: keep old priority when reconnecting bad connection * l3\_agent: avoid name conflict with context * Guard against concurrent port removal in DVR * Refactor l2\_pop code to pass mac/ip info more readably * Fix KeyError in dhcp\_rpc when plugin.port\_update raise exception * Refactor \_make\_subnet\_dict to avoid issuing unnecessary queries * openvswitch: Remove no longer used options * VPNaaS Cisco unit test clean-up * Call DVR VMARP notify outside of transaction 2014.2 ------ * remove E251 exemption from pep8 check * Race for l2pop when ports go up/down on same host * Catch exceptions in router rescheduler * Minor: remove unnecessary intermediate variable * Handle unused set\_context in L3NatTestCaseMixin.floatingip\_with\_assoc * Use EUI64 for IPv6 SLAAC when subnet is specified * Arista L3 Ops is success if it is successful on one peer * Add unique constraints in IPAvailabilityRange * Remove two sets that are not referenced * Update VPN logging to use new i18n functions * mock.assert\_called\_once() is not a valid method * Check for VPN Objects when deleting interfaces * Compare subnet length as well when deleting DHCP entry * Add pylint tox environment and disable all existing warnings * Updated from global requirements * update the relative path of api\_extensions\_path * Reduce security group db calls to neutron server * Ignore top-level hidden dirs/files by default * Remove some duplicate unit tests * NSX: drop support to deprecated dist-router extension * Execute udevadm on other linux installs * Avoid constructing a RouterInfo object to get namespace name * Drop sslutils and versionutils modules * Imported Translations from Transifex * Remove an argument that is never used * Refactor \_process\_routers to handle a single router * Add Juno release milestone * Add database relationship between router and ports * Fix L2 agent does not remove unused ipset set 2014.2.rc2 ---------- * Add Juno release milestone * Add database relationship between router and ports * Disable PUT for IPv6 subnet attributes * Skip IPv6 Tests in the OpenContrail plugin * Remove all\_routers argument from \_process\_routers * update ml2\_migration to reflect optional methods * Disable PUT for IPv6 subnet attributes * Do not assume order of lvm.tun\_ofports set elements * Skip IPv6 Tests in the OpenContrail plugin * Removed kombu from requirements * Updated from global requirements * Imported Translations from Transifex * Imported Translations from Transifex * Remove two sets that are not referenced * Forbid update of HA property of routers * Forbid update of HA property of routers * Teach DHCP Agent about DVR router interfaces * Updated from global requirements * Allow reading a tenant router's external IP * Raise exception if ipv6 prefix is inappropriate for address mode * Retry getting the list of service plugins * Add missing methods to NoopFirewallDriver * Don't fail when trying to unbind a router * Modify the ProcessMonitor class to have one less config parameter * Big Switch: Don't clear hash before sync * Remove sslutils from openstack.common * Divide \_cleanup\_namespaces for easy extensibility * L3 Agent should generate ns\_name in a single place * Add comments to iptables rules to help debugging * nit : missing a "%s" in a log message * L3 agent should always use a unique CONF object * Iterate over same port\_id if more than one exists * Fix setup of Neutron core plugin in VPNaaS UT 2014.2.rc1 ---------- * remove openvswitch plugin * Fix pid file location to avoid I->J changes that break metadata * Don't fail when trying to unbind a router * remove linuxbridge plugin * Allow reading a tenant router's external IP * Fix sleep function call * Add admin tenant name to nova notifier * ML2: move L3 cleanup out of network transaction * Open Kilo development * ML2 Cisco Nexus MD: Fix UT to send one create vlan message * Implement ModelsMigrationsSync test from oslo.db * Imported Translations from Transifex * Update migration scripts to support DB2 * Do not assume order of report list elements * Disallow unsharing used firewall policy * Imported Translations from Transifex * Add missing methods to NoopFirewallDriver * Raise exception if ipv6 prefix is inappropriate for address mode * Fix broken port query in Extraroute test case * Revert "Cleanup floatingips also on router delete" * fix dvr snat bindings for external-gw-clear * Fix quota limit range validator * Remove default dictionary from function def * Fix KeyError when getting secgroup info for ports * Create DHCP port for IPv6 subnet * Deletes floating ip related connection states * Do not lookup l3-agent for floating IP if host=None, dvr issue * Remove RPC notification from transaction in create/update port * Do not assume order of body and tags elements * Remove the translation tag for debug level logs in vmware plugin * Retry getting the list of service plugins * Fix entrypoint of OneConvergencePlugin plugin * Forbid regular users to reset admin-only attrs to default values * Finish small unit test refactor of API v2 tests * Security groups: prevent race for default security group creation * Stop admin using other tenants unshared rules * Eliminate OrderedDict from test\_api\_v2.py * Mock out all RPC calls with a fixture * Add logging for enforced policy rules * Imported Translations from Transifex * Remove unnecessary \_make\_port function in BSN UTs * ofagent: Drop log level of tenant-triggerable events * Set vif\_details to reflect enable\_security\_group * Use dict\_extend\_functions to populate provider network attributes * Fix foreign key constraint error on ml2\_dvr\_port\_bindings * Some clean up of code I'm preparing to modify * Indicate the begin and end of the sync process to EOS * DVR to delete router namespaces for service ports * Do not assume order of device\_ids set elements * Fix 500 error on retrieving metadata by invalid URI * Only setup dhcp interface if dhcp is not active on network * HA routers master state now distributed amongst agents * Rework and enable VPNaaS UT for Cisco CSR REST * Update URL of Ryu official site in ofagent README files * Set dsvm-functional job to use system packages * Delete a broken subnet delete unit test * Fix to delete user and group association in Nuage Plugin * Deletes FIP agent gw port when last VM is deleted * Delete DB records instead of tables to speedup UT * Stop exception log in Big Switch unit tests * Separate Configuration from Freescale SDN ML2 mechanism Driver * NSX plugin: set VNIC\_TYPE port binding attribute * Access correct key for template name * ofagent: Ignore unknown l2pop entry removals * Neutron metering does not check overlap ip range * Rename workers to api\_workers and simplify code * Fix DVR to service DHCP Ports * Tunnel ID range validation for VXLAN/GRE networks * Remove @author(s) from copyright statements * BSN: Add context to backend request for debugging * Don't create unused ipset chain * Imported Translations from Transifex * Avoid an extra database query in schedule\_snat\_router * Add HA support to the l3 agent * Stop ignoring 400 errors returned by ODL * Fix a test\_db\_plugin unit test side\_effect usage * Imported Translations from Transifex * Fix KeyError on missing gw\_port\_host for L3 agent in DVR mode * Stop using intersphinx * Updated from global requirements * Cisco N1kv: Remove vmnetwork delete REST call on last port delete * Remove the Cisco Nexus monolithic plugin * L3 Metering label as shared * Check for ports in subnet before deleting it from Nuage VSD * ofagent: Fix a possible crash in arp responder * Add a new scheduler for the l3 HA * Add functional testing to ipset\_manager * Properly handle empty before/after notifications in l2pop code * Remove logic for conditional migrations * Make Juno migrations config independent * Introduce havana initial state * Adds ipset support for Security Groups * Refactor l3\_agent.process\_router\_floating\_ip\_addresses * Cleanup floatingips also on router delete * use TRUE in SQL for boolean var * Remove faulty .assert\_has\_calls([]) * Fail on None before iteration attempt * Imported Translations from Transifex * ofagent: Remove broken XenAPI support * Passing admin tenant name to EOS * Fix for floating ip association and deletion * BSN: Allow concurrent reads to consistency DB * Remove useless check in \_rpc\_update\_firewall * Use renamed \_fail\_second\_call() in cisco nexus tests * Add L3 VRRP HA base classes * Allow DHCPv6 reply from server to client * Don't allow user to set firewall rule with port and no protocol * Added TAP\_DEVICE\_PREFIX info to common/constants * Fix comments in api.rpc.handlers * ofagent: Clean up logging * UTs: Disable auto deletion of ports/subnets/nets * Remove second call to get\_subnets in delete\_subnet * Changes to support FWaaS in a DVR based environment * Imported Translations from Transifex * Remove hints from schedule\_router * Call unbind\_snat\_servicenode from schedule router * NSX: Correct allowed\_address\_pair return value on create\_port * Add the unit tests for ml2.rpc module * Neutron should not use the neutronclient utils module for import\_class * Add unit-test assert to check dict is superset of dict * Pythonified sanity\_check.all\_tests\_passed * Removed direct access to MessagingServer * Remove subnet\_id from check\_ports\_exist\_on\_l3agent * Add requests\_mock to test-requirements.txt * Removed kombu from requirements * Fix metadata agent's auth info caching * Throw exception instances instead of classes * Add scheduler unit tests to enable bug fixes and refactoring * Fix AttributeError when setting external gateway on DVR router * Stop tracking connections in DVR FIP Namespace * Fixes formatting for debug output in neutron/agent/l3\_agent.py * Avoid testing code duplication which introduced testing bugs * Supply missing cisco\_cfg\_agent.ini file * Reset IPv6 detection flag after IPv6 tests * Remove unused arg to config.setup\_logging() * Updated from global requirements * Revert "Skip functional l3 agent test" 2014.2.b3 --------- * Fix leftover Timeout effecting most eventlet calls * shared policy shouldn't have unshared rules * ofagent: Remove @author tags and update copyright notices * Work toward Python 3.4 support and testing * Cleanup rename of get\_compute\_ports\_on\_host\_by\_subnet * Revert "Cisco DFA ML2 Mechanism Driver" * Refactor security group rpc call * Avoid auto-scheduling for distributed routers * Fix interface IP address for DVR with gateway * BSN: Bind external ports in ML2 driver * Remove SELECT FOR UPDATE use in delete\_firewall * Big Switch: Retry on 503 errors from backend * Remove absolute path in KillFilter for metadata-proxy * Implements sync mechanism between Neutron and Nuage VSD * ofagent: Implement physical\_interface\_mappings * ofagent: Enable local arp responder for TYPE\_LOCAL * ofagent: Enable local arp responder for TYPE\_FLAT * Implements ProcessMonitor to watch over external processes * Skip functional l3 agent test * ofagent: Local arp responder for VLAN * Prevent SystemExits when running tests * Big Switch: Separate L3 functions into L3 service * Apic drivers enhancements (second approach): Topology * Big Switch: Bind IVS ports in ML2 driver * Add functional test for IptablesManager * Clarify message when no probes are cleared * Remove reference to cisco\_cfg\_agent.ini from setup.cfg again * Fix a bug in Mellanox plugin RPC caused by secgroup RPC refactoring * Don't spawn metadata-proxy for non-isolated nets * l2pop: Allow network types overridable * ML2: Fix release of network segments to allocation pools * Fix a recent ipv6 UT regression * Imported Translations from Transifex * Add endpoint\_type parameter to MetaInterfaceDriver * Remove chain for correct router during update\_routers() * ofagent: Enable local arp responder for local VMs * ofagent: merge br-tun into br-int * Apic drivers enhancements (second approach): Sync * Apic drivers enhancements (second approach): L3 refactor * ML2 Type Driver refactor part 2 * Adds router service plugin for CSR1kv * Introduces a keepalived manager for HA * Support for extensions in ML2 * Cisco DFA ML2 Mechanism Driver * Improve some plugins help strings * Provide a quick way to run flake8 * Apic drivers enhancements (second approach): L2 refactor * Make SecurityGroupsRpcCallback a separate callback class * Subnets with prefix length 0 are invalid * Adding mechanism driver in ML2 plugin for Nuage Networks * Fix state\_path in tests * Add functional test for l3\_agent * remove explicit include of the ovs plugin * NSX: log request body to NSX as debug * Datacenter moid should not be tuple * Remove ovs dependency in embrane plugin * Layer 3 service plugin to support hardware based routing * Remove binding:profile update from Mellanox ML2 MD * Remove old policies from policy.json * Apic drivers enhancements (second approach): Backend * Make DvrServerRpcCallback a separate callback class * Make DhcpRpcCallback a separate callback class * Adding support of DNS nameserver and Host routes for the Nuage Plugin * Block downgrade from icehouse to havana * Use lockutils module for tox functional env * Do not use auto\_schedule\_routers to add router to agent * Fix func job hook script permission problems * Check for IPv6 file before reading * Remove SELECT FOR UPDATE use in update\_firewall * Fix l3 agent scheduling logic to avoid unwanted failures * Fix InvalidRequestError in auto\_schedule\_routers * Fix incorrect number of args to string format * Add support for provider-network extension in nuage Plugin * Make L3RpcCallback a separate callback class * Cisco VPN with in-band CSR (interim solution) * Inline "for val in [ref]" statements * Minor refactoring for add\_router\_to\_l3\_agent * Predictable iptables chains output order * Prefer "val !=/== ref" over "val (not) in [ref]" in conditions * Heal script: Drop fks before operating on columns * Fixed template of IPsecSiteConnectionNotFound message * Fix DVR to service LBaaS VIP Ports * Refactor test\_type\_gre/vxlan to reduce duplicate code * Fix heal\_script for MySQL specifics * Make log level in linux.utils.execute configurable * Imported Translations from Transifex * Networks are not scheduled to DHCP agents for Cisco N1KV plugin * ext-gw update on dvr router improperly handled by l3-agent * metering driver default value is different in code and config file * Fix for floatingip-delete not removing fip\_gw port * Increase the default poll duration for Cisco n1kv * Fix IpNetnsCommand to execute without root\_wrapper when no netns * Increase ovsdb\_monitor.SimpleInterfaceMonitor start timeout * Change autogenerate to be unconditional * Remove status initialization from plugin's create\_firewall * Set firewall state to CREATED when dealing with DVR * Add template attr. for subnet, router create in Nuage plugin * Implement ip\_lib.device\_exists\_with\_ip\_mac * Add \_store\_ip\_allocation method * Updated from global requirements * Refactor plugin setup helpers out of test.base * Raise proper exception in case duplicate ipv6 address is allocated * Do not explicitly set mysql\_engine * Fixes Hyper-V agent issue on Hyper-V 2008 R2 * Removing sorted() function from assertEqual() * Add hook scripts for the functional infra job * ML2 Type driver refactor part 1 * Minor refactoring of auto\_schedule\_routers * Add ipv6 forwarding for router namespaces * Refresh rpc\_backend values in unit tests to those from oslo.messaging * Add unit tests covering single operations to ODL * One Convergence: Skip all tests with 'v6' in name * VPNaaS: Enable UT cases with newer oslo.messaging * Do not log WARN messages about lack of L3 agents for DVR routers * Add specific docs build option to tox * Fix policy rules for adding and removing router interfaces * Refactor type\_tunnel/gre/vxlan to reduce duplicate code * Join tables in query for down L3 agents * Rename range to avoid shadowing the builtin * Fixes Hyper-V issue due to ML2 RPC versioning * A10 Networks LBaaS v1 Driver * Assign Cisco nw profile to multi-tenants in single request * Remove unused network parameter from \_allocate\_ips\_for\_port * corrects the typos in l3\_router\_plugin's comments * Support Stateful and Stateless DHCPv6 by dnsmasq * Implements securitygroup extension for nuage plugin * Fix bigswitch setup.cfg lines * Arista Layer 3 Sevice Plugin * Add config for visibility of cisco-policy-profile * Ensure ip6tables are used only if ipv6 is enabled in kernel * Remove invalid or useless initialization in test\_type\_vxlan * Fix migration set\_length\_of\_description\_field\_metering * Set InnoDB engine for all existing tables * Use oslo.db create\_engine instead of SQLAlchemy * Big Switch: Check for 'id' in port before lookup * Reorder operations in create\_vip * Send HTTP exceptions in the format expected by neutronclient * Change nexus\_dict to accept port lists * Update DVR Binding when router\_id changes * Imported Translations from Transifex * Remove auto-generation of db schema from models at startup * Cisco N1kv plugin to send subtype on network profile creation * Implement namespace cleanup for new DVR namespaces * Fix config option names in ml2\_conf\_sriov.ini * NSX: Avoid floating IP status reset * correct getLoggers to use \_\_name\_\_ in code * Skip FWaaS config mismatch check if RPC method is unsupported * NSX: lift restriction on DVR update * Updated from global requirements * Use jsonutils instead of stdlib json * Remove INACTIVE status from FWaaS * Ignore http\_proxy while connecting to test WSGI server * Fix interface add for dvr with gateway * l2pop: get\_agent\_ports: Don't yield (None, {}) * ML2: Make get\_device\_details report mac address as well * Delete DVR namespaces on node after removing last VM * Fix PortNotFound error during update\_device\_up for DVR * Option to remove routers from dead l3 agents * Remove SELECT FOR UPDATE use in ML2 tunnel driver add\_endpoint * Fix KeyError during sync\_routers * Fix PortNotFound exception during sync\_routers * VPNaaS: Cisco fix validation for GW IP * Raise NotImplementedError instead of NotImplemented * Imported Translations from Transifex * Fix duplicate function: test\_getattr\_unallowed\_attr * Preserve link local IP allocations for DVR fip ns across restart * Fix 404 error fetching metadata when using DVR * Raise exception for network delete with subnets presents * SecurityGroupRuleExists should point out rule id inseand of group id * Opencontrail plug-in implementation for core resources * Do not assume order of new\_peers list elements * Make plugin and l3plugin available as mixin's properties * Use call to report state when ovs\_agent starts up * add auth token to context * Fixes an issue with FIP re-association * NSX: unify the two distributed routing extensions * NSX: fix wording for configuration option * MLNX Agent: ensure removed ports get treated on resyncs * Add delete operations for the ODL MechanismDriver * Predictable field and filter ordering * Fixing neutron-db-manage with some options other than upgrade/downgrade * Removes extra indents from TestSubresourcePlugin * ofagent: Upgrade note about firewall\_driver * Return port context from \_bind\_port\_if\_needed * MLNX Agent: Process port\_update notifications in the main agent loop * Fix session's InvalidRequestError because of nested rollback * Remove unneeded device\_owner field from l2pop tuple * ofagent: Remove network\_delete method * Do not assume order of parameters in OVSBridge.add\_flow call * Fix to throw correct error code for bad attribute * Improve external gateway update handling * Do not assume order of pci slot list * DeferredBridge to allow add\_tunnel\_port passthru * Enabled Cisco ML2 driver to use new upstream ncclient * Fix to enable L2pop to serve DVR * Remove duplicated check for router connect to external net * ofagent: Add a missing normalized\_port\_name * Return 403 instead of 404 on attr policy failures * Proper validation for inserting firewall rule * Imported Translations from Transifex * Ensure assertion matches dict iter order in test * Fix 500 error during router-update for dvr routers * Simple refactor to stop passing around an unused parameter * Make \_build\_uri\_path output predictable * Radware: When a pip is needed, reuse the Port * Remove redundant topic from rpc calls * l3\_db: refactor L3\_NAT\_DB\_mixin * OVS flows apply concurrently using a deferred OVSBridge * Do not assume order of network\_uuid's * Big Switch: Only update hash header on success * ofagent: Stop monitoring ovsdb for port changes * ofagent: Desupport ancillary bridges * Add a tox test environment for random hashseed testing * OFAgent: Implement arp responder * Updated from global requirements * Do not assume order of quotas dictionary elements * Move Cisco VPN RESTapi URI strings to constants * Remove ignored do\_request timeout argument * Move from Python logging to Openstack logging * Imported Translations from Transifex * NSX: remove duplicate call to set\_auth\_cookie() * NSX: Correct default timeout params * Remove reference to cisco\_cfg\_agent.ini from setup.cfg * Exit Firewall Agent if config is invalid * Fix spelling mistakes * Fix DB Duplicate error when scheduling distributed routers * Imported Translations from Transifex * Make ML2 ensure\_dvr\_port\_binding more robust * centralized router is incorrectly scheduled * Fix-DVR Gateway clear doesn't delete csnat port * Fix spelling in get\_plugin\_interface docstring * Use storage engine when creating tables in migrations * Removed configobj from test requirements * Implement Midonet Juno Network Api calls * Add missing ml2 plugin to migration 1fcfc149aca4 * Replace nullable from primary keys in tz\_network\_bindings with default * Use correct section for log message if interface\_driver import fails * Make sure that gateway is in CIDR range by default * test\_l3\_plugin: L3AgentDbInteTestCase L3AgentDbSepTestCase fails * Add L3 Scheduler Changes for Distributed Routers * Pass filters in arrays in get\_agent\_gw\_ports\_exist\_for\_network * Do not schedule network when creating reserved DHCP port * Check that router info is set before calling \_update\_arp\_entry * Move ARP responder test to sanity command * neutron.conf does not have the definition of firewall quotas * Fix wrong order of tables in downgrade * Fix deprecated opt in haproxy driver * Race condition of L3-agent to add/remove routers * Replaced the strings with respective constants * Make dvr\_vmarp\_table\_update call conditional to dvr extension * ofagent: Update a comment in port\_bound * Updated from global requirements * Set promote\_secondaries when creating namespaces * Functional tests work fine with random PYTHONHASHSEED * Call config\_parse in base test setup * ML2 additions to support DVR * Make test\_l3\_agent.\_prepare\_router\_data a module function * Remove redundant code in tests/unit/test\_l3\_agent * Fix ML2 Plugin binding:profile update * Set python hash seed to 0 in tox.ini * Add definition for new VIF type * Configuration agent for Cisco devices * Handle bool correctly during \_extend\_extra\_router\_dict * Encapsulate some port properties in the PortContext * Changes to remove the use of mapping tables from Nuage plugin * Updated from global requirements * Log exceptions inside spawned functions * Correct misspelled variable name * Avoid RequestURITooLong exception in metadata agent * Move loadbalancer vip port creation outside of transaction * Define some abstract methods in VpnDriver class * ML2 mechanism driver for SR-IOV capable NIC based switching, Part 2 * Modify L3 Agent for Distributed Routers * Audited attribute for policy update not changing * OFAgent: Share codes of l2-population in OVS agent 2014.2.b2 --------- * This patch changes the name of directory from mech\_arista to arista * ML2 mechanism driver for SR-IOV capable NIC based switching, Part 1 * Add rule for updating network's router:external attribute * L2 Agent-side additions to support DVR * Imported Translations from Transifex * NSX: fix router ports port\_security\_enabled=False * Add partial specs support in ML2 for multiprovider extension * Add partial specs support in ML2 for gre/vxlan provider networks * Set nullable=False on tenant\_id in apic\_contracts table * call security\_groups\_member\_updated in port\_update * The default value of quota\_firewall\_rule should not be -1 * Correct LOG.debug use * Fix incorrect downgrade * Fix spelling mistake in the log message * Imported Translations from Transifex * Support Router Advertisement Daemon (radvd) for IPv6 * Move plugin.delete\_port call out of transaction * Add partial specs support in ML2 for vlan provider networks * ML2: Update a comment after the recent bind\_port change * NSX: fix validation logic on network gateway connect * Initialize RpcProxy objects correctly * Fix DVR regression for ofagent * RPC additions to support DVR * no quota for allowed address pair * Allow to import \_LC, \_LE, \_LI and \_LW functions directly * L2 Model additions to support DVR * Fixed audit notifications for dhcp-agent-network * Make readme reference git.openstack.org not github * Fix enums usage for postgres in migrations * Return a tuple of None's instead of one None * Fix a log typo in ML2 manager.bind\_port() * Big Switch: Remove consistency hash on full sync * VPNaaS: Separate validation for Cisco impl * VPNaaS: separate out validation logic for ref impl * VMWare: don't notify on disassociate\_floatingips() * Add L3 Extension for Distributed Routers * VPNaaS Cisco REST client enhance CSR create * Bump hacking to version 0.9.2 * Log methods using rpc communcation * Fixes port update failure when device ID is not updated * Support Quota extension in MidoNet plugin * NSX: Remove unneed call to \_ensure\_default\_security\_group * Use auth\_token from keystonemiddleware * update vsm credential correctly * Shamelessly removing commented print line * L3 agent prefers RPC messages over full sync * Dnsmasq config files syntax issue when dhcp\_domain is empty * Database healing migration * Fix incorrect default paramater in migration * Use method's logger in log decorator * Fixed audit notifications for l3-agent-router ops * Expand arp\_responder help text * Send network name and uuid to subnet create * Cisco: Fix test cases which make incorrect create requests * ML2: Bind ports outside transactions * Freeze models for healing migration * NSX: Optionally not enforce nat rule match length check * ofagent: Handle device name prefixes other than "tap" * Add -s option for neutron metering rules * Security groups extension for PLUMgrid plugin * Missing max\_routes in neutron.conf * Clear entries in Cisco N1KV specific tables on rollback * Allow unsharing a network used as gateway/floatingip * Change all occurences of no\_delete to do\_delete * Split up metering test case into plugin + test case * Use integer server\_default value for multicast\_ip\_index * Validate expected parameters in add/remove router interfaces * Revert "VPNaaS REST Client UT Broken" * Mock out tunnel\_sync in test to avoid sleeping * Add 'server\_default' parameter * Add BSN plugin to agent migration script * Move \_convert\_to\_nsx\_transport\_zones into nsx\_utils * Extract CommonDBMixin to a separate file * Remove dead helper function from test\_l3\_plugin * Added support for NOS version 4.1.0, 5.0.0 and greater * Remove reference to setuptools\_git * NSX: neutron router-interface-add should clear security-groups * Refactor 'if false do nothing' logic in l3 scheduler db * Imported Translations from Transifex * Add a gate-specific tox env for functional tests * NSX: remove unnecessary checks on network delete * Bump min required version for dnsmasq to 2.63 * Add CONTRIBUTING.rst * Do not mark device as processed if it wasn't * Fix 'server\_default' parameter usage in models * Fix missing migration default value * Add a link to a blog post by RedHat that discusses GRE tunnels in OVS * Updated from global requirements * VPNaaS REST Client UT Broken * Avoid notifying while inside transaction opened in delete\_port() * sync periodic\_task fix from incubator * Omit mode keyword when spawning dnsmasq with some ipv6 subnets * Fixed spelling mistake in securitygroups\_rpc * OVS agent: fix a comment on CANARY\_TABLE * ofagent: Fix an argument mismatch bug in commit 9d13ea88 * Fix UnboundLocalError raised during L3 router sync task * Updated from global requirements * Fix isinstance assertions * Imported Translations from Transifex * Allow setting a rootwrap cmd for functional tests * Fix OVSBridge.get\_port\_ofport to handle empty output * Ignore variable column widths in ovsdb functional tests * Add configurable http\_timeout parameter for Cisco N1K * NSX: fix indentations * BSN: Remove db lock and add missing contexts * NSX: properly handle floating ip status * Updated from global requirements * Fix example for running individual tests * Stop the dhcp-agent process when dnsmasq version is not determined * Switch to using of oslo.db * Replace occurences of 'test\_tenant' with 'test-tenant' in tests * lb-agent: ensure removed devices get treated on resyncs * Imported Translations from Transifex * Add sanity check for nova notification support * changes ovs agent to get bridges via ovs\_lib * Use correct MAX\_LEN constant in agent functional tests * remove unsupported middleware * Fix re-creation of the pool directory * Add config for performance gate job * Use patch ports to interconnect integration/physical bridges * Exit rpc\_loop when SIGTERM is recieved in ovs-agent * LBaaS new object model logging no-op driver * ofagent: Use port desc to monitor ports on br-int * Fixed dhcp & gateway ip conflict in PLUMgrid plugin * Introduce bulk calls for get device details * validate flat networks physical name * Remove \_\_init\_\_ method from TunnelCallback mixin * OVS agent: Correct bridge setup ordering * Revert "Revert "ovs-agent: Ensure integration bridge is created"" * Imported Translations from Transifex * Synced log module and its dependencies from olso-incubator * Pass newly created router to \_update\_router\_gw\_info * don't ignore rules that are already enforced * Updated neutron.conf to reflect new RPC options * Moved rpc\_compat.py code back into rpc.py * Updated from global requirements * Updated from global requirements * ofagent: move main module from ryu repository * Don't convert numeric protocol values to int * Imported Translations from Transifex * Revert "Check NVP router's status before deploying a service" * Remove the useless vim modelines * Imported Translations from Transifex * Changing the poll\_duration parameter type to int * Add test cases for plugins/ml2/plugin.py * Removed local modification in incubator code * Removed 'rpc' and 'notifier' incubator modules * Removed create\_rpc\_dispatcher methods * Use openstack.common.lockutils module for locks in tox functional tests * Pass serializer to oslo.messaging Notifier * Fix auto\_schedule\_networks to resist DBDuplicateEntry * Imported Translations from Transifex * Control active number of REST calls from Cisco N1kv plugin to VSM * Revert "ovs-agent: Ensure integration bridge is created" * ValueError should use '%' instead of ',' * NSX: return 400 if dscp set for trusted queue * NSX sync cache: add a flag to skip item deletion * NSX: propagate network name updates to backend * Renamed argument for create\_consumer[s] * Renamed consume\_in\_thread -> consume\_in\_threads * Renamed start\_rpc\_listener -> start\_rpc\_listeners * Port to oslo.messaging * Imported Translations from Transifex * Pass 'top' to remove\_rule so that rule matching succeeds * Big Switch: Stop watchdog on interval of 0 * Remove old quantum scripts * Move \_filter\_non\_model\_columns method to CommonDbMixin * Updated from global requirements * Ignore emacs checkpoint files * Big Switch: Lock consistency table for REST calls * Check port value when creating firewall rule with icmp protocol * Improve docstring for OVSNeutronAgent constructor * Big Switch ML2: sync detection in port-update * Imported Translations from Transifex * Remove SELECT FOR UPDATE use in ML2 type driver release\_segment * Add vlan type driver unittests * Make sure we call BaseTestCase.setUp() first * Don't explicitly call .stop() on mock.patch objects * Don't instantiate RPC clients on import * Configure agents using neutron.common.config.init (formerly .parse) * linuxbridge-agent: process port updates in the main loop * Notify systemd when starting Neutron server * Ensure entries in dnsmasq belong to a subnet using DHCP * Added missing core\_plugins symbolic names * Trigger provider security group update for RA * NSX: revert queue extension name change * Fix pool statistics for LBaaS Haproxy driver * Don't use root\_helper when it's not needed * Introduced rpc\_compat.create\_connection() * Copy-paste RPC Service class for backwards compatibility * Introduce RpcCallback class * Fix opt helpstring for dhcp\_lease\_duration * Consistently use jsonutils instead of specific implementation * Imported Translations from Transifex * Adding static routes data for members * remove pep8 E122 exemption and correct style * Change default netpartition behavior in nuage plugin * Add 'ip rule ...' support to ip\_lib * Add missing keyword raise to get\_profile\_binding function * Add logging for NSX status sync cache 2014.2.b1 --------- * Big Switch: Remove unnecessary initialization code * Big Switch: Import DB module in unit test * When l2-pop ON, clean stale ports in table0 br-tun * remove E112 hacking exemption and fix errors * Updated from global requirements * Allowed address pair: Removing check for overlap with fixed ips * NeutronManager: Remove explicit check of the existence of an attribute * Fix invalid IPv6 address used in FakeV6 variables * Improve vxlan type driver initialization performance * Floatingip extension support for nuage plugin * ovs-agent: Ensure integration bridge is created * Brocade mechanism driver depends on the brocade plugin templates * Brocade mechanism driver should be derived from ML2 plugin base class * changes ovs agent\_id init to use hostname instead of mac * multiprovidernet: fix a comment * Imported Translations from Transifex * Fix race condition with firewall deletion * extensions: remove 'check\_env' method * Check the validation of 'delay' and 'timeout' * Control update, delete for cisco-network-profile * Ensure routing key is specified in the address for a direct producer * Support Subnets that are configured by external RAs * Refactor code in update\_subnet, splitting into individual methods * Make allocation\_pools attribute of subnet updateable by PUT * Monkey patch threading module as early as possible * Introduced transition RPC exception types * Added RpcProxy class * ofagent: Fix VLAN usage for TYPE\_FLAT and TYPE\_VLAN * Big Switch: Catch exceptions in watchdog thread * Use import from six.moves to import the queue module * Start an unstarted patch in the hyperv unit tests * Imported Translations from Transifex * Fix NVP FWaaS occurs error when deleting a shared rule * Check NVP router's status before deploying a service * Add an option to turn off DF for GRE and VXLAN tunnels * Increase default metadata\_workers, backlog to 4096 * Big Switch: Add missing data to topology sync * Replace XML with JSON for N1kv REST calls * Big Switch: Call correct method in watchdog * Freescale SDN Mechanism Driver for ML2 Plugin * OVS Agent: limit veth names to 15 chars * Added note to neutron.conf * Return no active network if the agent has not been learnt yet * Sync service module from oslo-incubator * ovs, ofagent: Remove dead code * Default to setting secure mode on the integration bridge * Cisco APIC Layer 3 Service plugin * Allow neutron-sanity-check to check OVS patch port support * Remove run-time version checking for openvswitch features * Add flat type driver unittests * Changed DictModel to dict with attribute access * Pass object to policy when finding fields to strip * Allow L3 base to handle extensions on router creation * Refactor some router-related methods * Add local type driver unittests * add engine parameter for offline migrations * Check DB scheme prior to migration to Ml2 * Removes unnecessary Embrane module-level mocks * Improve module-level mocks in midonet tests * Big Switch: fix capabilities retrieval code * Improve iptables\_manager \_modify\_rules() method * NSX: bump http\_timeout to 30 seconds * Log firewall status on delete in case of status inconsistency * BSN: Set hash header to empty instead of False * Neutron does not follow the RFC 3442 spec for DHCP * LBaaS add missing rootwrap filter for route * Radware LBaaS driver is able to flip to a secondary backend node * NSX: fix invalid docstring * NSX: fix tenant\_id passed as security\_profile\_id * NSX: Fix request\_id in api\_client to increment * Improve usage of MagicMocks in ML2 and L3 tests * Improve readability of MagicMock use in RYU test * Remove function replacement with mock patch * Remove unnecessary MagicMocks in cisco unit tests * Handle errors from run\_ofctl() when dumping flows * Sync periodic\_task from oslo-incubator * Added missing plugin .ini files to setup.cfg * Imported Translations from Transifex * Make linux.utils.execute log error on return codes * FWaaS plugin doesn't need to handle firewall rule del ops * Reprogram flows when ovs-vswitchd restarts * Revert "fix openvswitch requirement check" * Updated from global requirements * Fix KeyError exception while updating dhcp port * NSX: fix bug for flat provider network * Disallow regular user to update firewall's shared attribute * Support 'infinite' dhcp\_lease\_duration * l2-pop : removing a TODO for the delete port use case * NEC plugin: Bump L3RPC callback version to 1.1 * Synced jsonutils from oslo-incubator * Imported Translations from Transifex * fix openvswitch requirement check * NSX: replace strong references to the plugin with weakref ones * Fixes bugs for requests sent to SDN-VE controller * Install SNAT rules for ipv4 only * Imported Translations from Transifex * Add NVP advanced service check before deleting a router * Disallow 'timeout' in health\_monitor to be negative * Remove redundant default=None for config options * Fix for multiple misspelled words * Use list copy for events in nova notifier * Extraroute extension support for nuage plugin * OFAgent: Fixing lost vlan ids on interfaces * Set onlink routes for all subnets on an external network * Cisco APIC ML2 mechanism driver, part 2 * Remove all mostly untranslated PO files * remove token from notifier middleware * NSX: get rid of the last Nicira/NVP bits * Metadata agent caches networks for routers * Common decorator for caching methods * Make pid file locking non-blocking * Allowed Addresspairs: Removing check for overlap with fixed ips * Do not defer IPTables apply in firewall path * Metaclass Python 3.x Compatibility * Fix non-existent 'assert' calls to mocks * Log iptables rules when they fail to apply * Remove hard dependency on novaclient * Provide way to reserve dhcp port during failovers * Imported Translations from Transifex * Implement local ARP responder onto OVS agent * Fix typos in ovs\_neutron\_agent.py * Allow vlan type usage for OpenDaylight ml2 * NSX: do not raise on missing router during migration step * NSX: fix error when creating VM ports on subnets without dhcp * NSX: allow net-migration only in combined mode * OFAgent: Avoid processing ports which are not yet ready * Add missing translation support * Reorg table ml2\_port\_bindings when db migration * Remove unused parameter * NSX: Do a single query for all gateway devices * Add mailmap entry * Add 'secret' property for 'connection' option * NSX: Do not extend fault map for network gateway ext * Ensure tenant owns devices when creating a gateway * Corrected the syntax of port\_update call to NVSD agent * Fix some typos in neutron/db and IBM SDN-VE plugin * Fix issubclass() hook behavior in PluginInterface * Imported Translations from Transifex * LBaaS VIP doesn't work after delete and re-add * OVS lib defer apply doesn't handle concurrency * Big Switch: Don't use MagicMocks unnecessarily * Make plugin deallocation check optional * Restore GARP by default for floating IPs * Ensure core plugin deallocation after every test * Updated from global requirements * Big Switch: Check source\_address attribute exists * Revert "Big Switch: Check source\_address attribute exists" * ML2 VxlanTypeDriver: Synchronize of VxlanAllocation table * Start ping listener also for postgresql * ofagent: Add a missing push\_vlan action * NSX: ensure that no LSN is created on external networks * Make VPNaaS 'InUse' exception more clear * Remove explicit dependency on amqplib * Revert "Disable debug messages when running unit tests" * eswitch\_neutron\_agent: Whitespace fixes in comments * Upgrade failure for DB2 at ml2\_binding\_vif\_details * Remove duplicate module-rgx line in .pylintrc * Disable debug messages when running unit tests * Perform policy checks only once on list responses * Allow DHCPv6 solicit from VM * Fix importing module in test\_netscaler\_driver * Record and log reason for dhcp agent resync * Big Switch: Check source\_address attribute exists * L3 RPC loop could delete a router on concurrent update * Adding tenant-id while creating Radware ADC service * Fix H302 violations * Fix H302 violations in plugins package * Fix H302 violations in unit tests * Imported Translations from Transifex * lbaas on a network without gateway * Optimize querying for security groups * NSX: pass the right argument during metadata setup * Improve help strings for radware LbaaS driver * Fix network profile subtype validation in N1kv plugin * Performance improvement of router routes operations * Add support to dynamically upload drivers in PLUMgrid plugin * Imported Translations from Transifex * Reference new get\_engine() method from wsgi.py * Allow test\_l3\_agent unit test to run individually * tests/unit: refactor reading neutron.conf.test * Don't print duplicate messages on SystemExit * Unit test cases for quota\_db.py * Cisco VPN device driver - support IPSec connection updates * OVS and OF Agents: Create updated\_ports attribute before setup\_rpc * Imported Translations from Transifex * Updated from global requirements * Synced jsonutils from oslo-incubator * Imported Translations from Transifex * NSX: fix migration for networks without a subnet * Allow ML2 plugin test cases to be run independently * Removed signing\_dir from neutron.conf * Add physical\_network to binding:vif\_details dictionary * Database exception causes UnboundLocalError in linuxbridge-agent * Wrong key router.interface reported by ceilometer * Imported Translations from Transifex * NSX: fix API payloads for dhcp/metadata setup * Improve ODL ML2 Exception Handling * NSX: change api mapping for Service Cluster to Edge Cluster * Fix protocol value for SG IPV6 RA rule * Cisco APIC ML2 mechanism driver, part 1 * LBaaS: remove orphan haproxy instances on agent start * Fixed floating IP logic in PLUMgrid plugin * Segregate the VSM calls from database calls in N1kv plugin * NSX: add nsx switch lookup to dhcp and metadata operations * Use set\_gateway from ip\_lib * Fix incorrect usage of sa.String() type * Re-submit "ML2 plugin should not delete ports on subnet deletion" * LBaaS: Set correct nullable parameter for agent\_id * Vmware: Set correct nullable for lsn\_id, nsx\_port\_id * IBM: set secret=True on passwd config field * Restore ability to run functional tests with run\_tests.sh * Fix H302 violations in extensions package * Sync db code from oslo-incubator * Imported Translations from Transifex * Remove List events API from Cisco N1kv Neutron * NSX: Fix fake\_api\_client to raise NotFound * Replace loopingcall in notifier with a delayed send * ip-lib : use "ip neigh replace" instead of "ip neigh add" * Add 2-leg configuration to Radware LBaaS Driver * Fix H302 violations in db package and services * Cisco: Set correct nullable for switch\_ip, instance\_id, vlan\_id * Ml2: Set correct nullable for admin\_state\_up * Drop service\* tables only if they exist * Updated from global requirements * Make help texts more descriptive in Metaplugin * ML2 Cisco Nexus MD: Improve Unit Test Coverage * Fix migration that breaks Grenade jobs * Fix incorrect change of Enum type * allow delete\_port to work when there are multiple floating ips * Add nova\_ca\_certificates\_file option to neutron * gw\_port should be set as lazy='join' * netaddr<=0.7.10 raises ValueError instead of AddrFormatError * Imported Translations from Transifex * netaddr<=0.7.10 raises ValueError instead of AddrFormatError * Validate IPv6 modes in API when IP version is 4 * Add 'ip neigh' to ip\_lib * OFAgent: Improve handling of security group updates * OFAgent: Process port\_update notifications in the main agent loop * NSX: sync thread catches wrong exceptions on not found * Notifier: Catch NotFound error from nova * Switch over to FixedIntervalLoopingCall * Check if bridge exists and make sure it's UP in ensure\_bridge * Validate CIDR given as ip-prefix in security-group-rule-create * Support enhancements to Cisco CSR VPN REST APIs * Fix uninitialized variable reference * Nuage Plugin: Delete router requires precommit checks * Delete DHCP port without DHCP server on a net node * Improved quota error message * Remove device\_exists in LinuxBridgeManager * Add support for multiple RPC workers under Metaplugin * Security Group rule validation for ICMP rules * Fix Metering doesn't respect the l3 agent binding * DHCP agent should check interface is UP before adding route * Remove workaround for bug #1219530 * Fix LBaaS Haproxy occurs error if no member is added * Add functional tests to verify ovs\_lib VXLAN detection * Add nova\_api\_insecure flag to neutron * Allow combined certificate/key files for SSL * Verify ML2 type driver exists before calling del * Fix dangling patches in Cisco and Midonet tests * Make default nova\_url use a version * ML2 Cisco Nexus MD: Remove unnecessary Cisco nexus DB * NSX plugin: fix get\_gateway\_devices * Exclude .ropeproject from flake8 checks * Register LBaaS resources to quotas engine * Remove mock.patch.stop from tests that inherit from BaseTestCase * Reschedule router if new external gateway is on other network * Update ensure()/reconnect() to catch MessagingError * Properly apply column default in migration pool\_monitor\_status * Remove "reuse\_existing" from setup method in dhcp.py * Enable flake8 E711 and E712 checking * Fixes Hyper-V agent security groups disabling * Fixes Hyper-V agent security group ICMP rules * Fix typo in ml2 configuration file * Edge firewall: improve exception handling * Edge driver: Improve exception handling * Fix typo in comment * NSX: Fix KeyError in sync if nsx\_router\_id not found * VMware: log backend port creation in the right place * Revert "Hide ipv6 subnet API attributes" * BigSwitch: Create router ports synchronously * NSX: ensure dhcp port is setup on metadata network * Hide ipv6 subnet API attributes * Set correct columns' length * Enforce required config params for ODL driver * Add L2 Agent side handling for non consistent security\_group settings * BSN: Remove module-level ref to httplib method * BigSwitch: Stop HTTP patch before overriding * Typographical correction of Arista ML2 help * Fix wrong section name "security\_group" in sample config files * Set the log level to debug for loading extensions * Updated from global requirements * set api.extensions logging to ERROR in unit tests * Add common base class for agent functional tests * Remove RPC to plugin when dhcp sets default route * Imported Translations from Transifex * Add missing comma in nsx router mappings migration * OFAgent: Avoid re-wiring ports unnecessarily * BigSwitch: Improves server manager UT coverage * BigSwitch: Don't import portbindings\_db until use * lb-agent: fix get\_interfaces\_on\_bridge returning None * Clean out namespaces even if we don't delete namespaces * Call policy.init() once per API request * ofa\_neutron\_agent: Fix \_phys\_br\_block\_untranslated\_traffic * Don't emit log for missing attribute check policy * Sync service and systemd modules from oslo-incubator * Imported Translations from Transifex * Move bash whitelisting to pep8 testenv * Fix test MAC addresses to be valid * ML2: ODL driver sets port status * Add a note that rpc\_workers option is experimental * Fix Jenkins translation jobs * Redundant SG rule create calls in unit tests * Set ns\_name in RouterInfo as attribute * Replace HTTPSConnection in NEC plugin * ignore build directory for pep8 * Imported Translations from Transifex * Delete routers that are requested but not reported as active * Explicitly import state\_path opt in tests.base * fixes tests using called\_once\_ without assert * Remove invalid copyright headers under API module * update doc string - correct typo * Revert changes removing OVSBridge return * fixes broken neutron-netns-cleanup * Remove duplicated tests for check\_ovs\_vxlan\_version * Permit ICMPv6 RAs only from known routers * Return 409 for second firewall creation * OFA agent: use hexadecimal IP address in tunnel port name * Fixing Arista CLI command * use floatingip's ID as key instead of itself * Use a temp dir for CONF.state\_path * Use os.uname() instead of calling uname in subprocess * Enable hacking H301 check * Stop using portbindings\_db in BSN ML2 driver * NSX: Fix pagination support * Removing vim header lines * Fix function parsing the kernel version * Updated from global requirements 2014.1.rc1 ---------- * Restore NOT NULL constraint lost by earlier migrations * BigSwitch: Semaphore on port status update * Remove last parts of Quantum compatibility shim * Imported Translations from Transifex * Fix quota\_health\_monitor opt name in neutron.conf * Add missing DB migrations for BSN ML2 plugin * Only send notifications on uuid device\_id's * Add Icehouse no-op migration * Add support for https requests on nova metadata * Delete disassociated floating ips on external network deletion * Imported Translations from Transifex * Invoke \_process\_l3\_create within plugin session * Invalid ovs-agent test case - test\_fdb\_add\_flows * Add missing parameters for port creation * Move test\_ovs\_lib to tests/unit/agent/linux * Update BigSwitch Name to its correct name * Cancelling thread start while unit tests running * Delete duplicate external devices in router namespace * Deals with fails in update\_\*\_postcommit ops * ML2 Cisco Nexus MD: Support portchannel interfaces * Changed the message line of RouterInUse class * UT: do not hide an original error in test resource ctxtmgr * BigSwitch: Move attr ref after error check * Fix namespace exist() method * Make dnsmasq aware of all names * Open Juno development * Prevent cross plugging router ports from other tenants * Adds OVS\_HYBRID\_PLUG flag to portbindings * Disable XML tests on Py26 * Subnets should be set as lazy='join' * nec plugin: allow to delete resource with ERROR status * Synced rpc and gettextutils modules from oslo-incubator * Import request\_id middleware bug fix from oslo * Add unit test for add\_vxlan in test\_linux\_ip\_lib * Start using oslosphinx theme for docs * Migrate data from cap\_port\_filter to vif\_details * Imported Translations from Transifex * Include cisco plugin in migration plugins with ovs * ML2 Cisco Nexus MD: Remove workaround for bug 1276395 * Fixed TypeError when creating MlnxException * Replace a usage of the deprecated root\_helper option * Cisco VPN driver correct reporting for admin state chg * Add script to migrate ovs or lb db to ml2 db * Correct OVS VXLAN version check * LBaaS: make device driver decide whether to deploy instance * NSX plugin: return 400 for invalid gw certificate * Imported Translations from Transifex * Remove extra space in help string * Add enable\_security\_group to BigSwitch and OneConvergence ini files * Add nec plugin to allowed address pairs migration * Imported Translations from Transifex * Fix segment allocation tables in Cisco N1kv plugin * Updated from global requirements * NEC plugin: Rename quantum\_id column to neutron\_id * Log received pool.status * NEC plugin: Allow to add prefix to OFC REST URL * NEC plugin: Remove a colon from binding:profile key due to XML problem * rename ACTIVE\_PENDING to ACTIVE\_PENDING\_STATUSES * VPNaaS support for VPN service admin state change and reporting * Use save\_and\_reraise\_exception when reraise exception * Return meaningful error message on pool creation error * Don't set priority when calling mod\_flow * Avoid creating FixedIntervalLoopingCall in agent UT * Imported Translations from Transifex * Big Switch Plugin: No REST port delete on net del * Add enable\_security\_group option * Get rid of additional db contention on fetching VIP * Fix typo in lbaas agent exception message * De-duplicate unit tests for ports in Big Switch * ML2: Remove validate\_port\_binding() and unbind\_port() * Imported Translations from Transifex * Fix duplicate name of NVP LBaaS objs not allowed on vShield Edge * tests/unit: clean up notification driver * Use different name for the same constraint * Add a semaphore to some ML2 operations * Log dnsmasq host file generation * add HEAD sentinel file that contains migration revision * Added config value help text in ns metadata proxy * Fix usage of save\_and\_reraise\_exception * Cisco VPN device driver post-merge cleanup * Fixes the Hyper-V agent individual ports metrics * Sync excutils from oslo * BigSwitch ML2: Include bound\_segment in port * NEC plugin: Honor Retry-After response from OFC * Add update binding:profile with physical\_network * return false or true according to binding result * Enable to select an RPC handling plugin under Metaplugin * Ensure to count firewalls in target tenant * Mock agent RPC for FWaaS tests to delete DB objs * Allow CIDRs with non-zero masked portions * Cisco plugin fails with ParseError no elem found * Cisco Nexus: maximum recursion error in ConnectionContext.\_\_del\_\_ * Don't use root to list namespaces * Fixes Hyper-V agent security groups enable issue * ML2 BigSwitch: Don't modify parent context * Advanced Services documentation * LBaaS: small cleanup in agent device driver interface * Change report\_interval from 4 to 30, agent\_down\_time from 9 to 75 * Stop removing ip allocations on port delete * Imported Translations from Transifex * Ignore PortNotFound exceptions on lockless delete * Show neutron API request body with debug enabled * Add session persistence support for NVP advanced LBaaS * Fix misleading error message about failed dhcp notifications * NSX: Fix router-interface-delete returns 404 when router not in nsx * Fix \_validate\_mac\_address method * BigSwitch: Watchdog thread start after servers * Calculate stateless IPv6 address * Create new IPv6 attributes for Subnets * Remove individual cfg.CONF.resets from tests * BigSwitch: Sync workaround for port del deadlock * NSX: Ensure gateway devices are usable after upgrade * Correctly inherit \_\_table\_args\_\_ from parent class * Process ICMP type for iptables firewall * Imported Translations from Transifex * Added missing l3\_update call in update\_network * ML2 plugin involves in agent\_scheduler migration * Imported Translations from Transifex * Avoid long transaction in plugin.delete\_ports() * cisco: Do not change supported\_extension\_aliases directly * Fix KeyError except on router\_info in FW Agent * NSX: remove last of unneed quantum references * NSX: fix intermetting UT failure on vshield test\_router\_create * Bugfix and refactoring for ovs\_lib flow methods * Send fdb remove message when a port is migrated * Imported Translations from Transifex * Send network-changed notifications to nova * Notify nova when ports are ready * Skip radware failing test for now * NSX: Propagate name updates for security profiles * Fix in admin\_state\_up check function * NSX: lower the severity of messages about VIF's on external networks * Kill 'Skipping unknown group key: firewall\_driver' log trace * Imported Translations from Transifex * API layer documentation * BigSwitch: Use eventlet.sleep in watchdog * Embrane LBaaS Driver * BigSwitch: Widen range of HTTPExceptions caught * Fix ml2 & nec plugins for allowedaddresspairs tests * Fix unittest failure in radware lbaas driver * Removes calls to mock.patch.stopall in unit tests * Stop mock patches by default in base test class * Query for port before calling l3plugin.disassociate\_floatingips() * Optimize floating IP status update * NSX: Allow multiple references to same gw device * VPNaaS Device Driver for Cisco CSR * Updated from global requirements * BigSwitch: Fix certificate file helper functions * Create agents table when ML2 core\_plugin is used * Fix usage of sqlalchemy type Integer * Fixing lost vlan ids on interfaces * Fix bug:range() is not same in py3.x and py2.x * Call target plugin out of DB transaction in the Metaplugin * NSX: Sync do not pass around model object * NSX: Make replication mode configurable * Updated from global requirements * Fix ml2 db migration of subnetroutes table * Imported Translations from Transifex * After bulk create send DHCP notification * Fix lack of extended port's attributes in Metaplugin * Add missing ondelete option to Cisco N1kv tables * Migration support for Mellanox Neutron plugin * Imported Translations from Transifex * Imported Translations from Transifex * Updated from global requirements * Add support for tenant-provided NSX gateways devices * NSX: fix nonsensical log trace on update port * BigSwitch: Fix rest call in consistency watchdog * BigSwitch: Fix cfg.Error format in exception * BigSwitch: Fix error for server config check * Fixed Spelling error in Readme * Adds state reporting to SDN-VE agent * Fix unittest failure in radware lbaas driver * Log configuration values for OFA agent * NSX: Add ability to retry on 503's returned by the controller * Cisco Neutron plugin fails DB migration * Floatingip\_status migration not including Embrane's plugin * One Convergence Neutron Plugin l3 ext support * Nuage plugin was missed in floatingip\_status db migration script * ML2 Cisco Nexus MD: VM migration support * Drop old nvp extension file * Makes the Extension loader behavior predictable * One Convergence Neutron Plugin Implementation * NEC plugin: delete old OFC ID mapping tables * Imported Translations from Transifex * Fix typo in migration script * Enhance GET networks performance of metaplugin * Adds the missing migration for gw\_ext\_mode * BigSwitch: Add SSL Certificate Validation * BigSwitch: Auto re-sync on backend inconsistencies * VPNaaS Service Driver for Cisco CSR 2014.1.b3 --------- * Updated from global requirements * Add OpenDaylight ML2 MechanismDriver * Replaces network:\* strings by constants * Check vxlan enablement via modinfo * Do fip\_status migration only for l3-capable plugins * Fix race condition in update\_floatingip\_statuses * Implementaion of Mechanism driver for Brocade VDX cluster of switches * NSX: passing wrong security\_group id mapping to nsx backend * Avoid unnecessarily checking the existence of a device * Refactor netns.execute so that it is not necessary to check namespace * Minor refactoring for Hyper-V utils and tests * Adds Hyper-V Security Groups implementation * Rename migration lb\_stats\_needs\_bigint to match revision number * Imported Translations from Transifex * NVP LBaaS: check for association before deleting health monitor * Different class names for VPNaaS migrations * ML2: database needs to be initalized after drivers loaded * replace rest of q\_exc to n\_exc in code base * Adds multiple RPC worker processes to neutron server * NEC plugin: PFC packet fitler support * Fix NVP/Nicira nits * Remove unused method update\_fixed\_ip\_lease\_expiration * NSX: nicira\_models should import model\_base directly * NSX: make sync backend run more often * Embrane Plugin fails alembic migrations * Implement Mellanox ML2 MechanismDriver * Use database session from the context in N1kv plugin * Delete subnet fails if assoc port has IPs from another subnet * Remove nvplib and move utility methods into nsxlib * BigSwitch: Add address pair support to plugin * Remove unused 'as e' in exception blocks * Remove vim line from db migartion template * Imported Translations from Transifex * Support advanced NVP IPsec VPN Service * Improves Arista's ML2 driver's sync performance * Fix NVP FWaaS errors when creating firewall without policy * Remove call to addCleanup(cfg.CONF.reset) * nec plugin: Avoid long transaction in delete\_ports * Avoid using "raise" to reraise with modified exception * Imported Translations from Transifex * Implement OpenFlow Agent mechanism driver * Finish off rebranding of the Nicira NVP plugin * Log configuration values for OVS agent * BigSwitch: Asynchronous rest calls for port create * Introduce status for floating IPs * BigSwitch: Add agent to support neutron sec groups * N1kv: Fixes fields argument not None * Adds the new IBM SDN-VE plugin * Imported Translations from Transifex * Nuage Networks Plugin * Fixes spelling error Closes-Bug: #1284257 * Openvswitch update\_port should return updated port info * Updated from global requirements * Remove unused variable * Change firewall to DOWN when admin state down * ovs-agent: use hexadecimal IP address in tunnel port name * NSX: add missing space 'routeron' * Imported Translations from Transifex * Fix DetachedInstanceError for Agent instance * Update License Headers to replace Nicira with VMware * Renaming plugin-specific exceptions to match NSX * Imported Translations from Transifex * DB Mappings for NSX security groups * NSX: port status must reflect fabric, not link status * Typo/grammar fixes for the example neutron config file * NSX: Pass NSX uuid when plugging l2 gw attachment * stats table needs columns to be bigint * Remove import extension dep from db migration * Fix get\_vif\_port\_by\_id to only return relevant ports * Developer documentation * Fix NSX migration path * ML2 mechanism driver access to binding details * Add user-supplied arguments in log\_handler * Imported Translations from Transifex * NSX: Fix newly created port's status should be DOWN * BigSwitch: Stop using external locks * Rename/refactoring of NVP api client to NSX * Remove pyudev dependency * Rename DB models and related resources for VMware NSX plugin * Lower log level of errors due to user requests to INFO * Include proper Content-Type in the HTTP response headers * LBaaS: check for associations before deleting health monitor * l2-population/lb/vxlan : ip neigh add command failed * l2-population : send flooding entries when the last port goes down * tests/service: consolidate setUp/tearDown logic * Ensure ovsdb-client is stopped when OVS agent dies * NSX: Fix status sync with correct mappings * Support Port Binding Extension in Cisco N1kv plugin * change Openstack to OpenStack in neutron * ML2 binding:profile port attribute * Rename/remove Nicira NVP references from VMware NSX unit tests * Fix webob.exc.HTTPForbidden parameter miss * Sync oslo cache with oslo-incubator * Change tenant network type usage for IB Fabric * options: consolidate options definitions * Replace binding:capabilities with binding:vif\_details * Make sure dnsmasq can distinguish IPv6 address from MAC address * Rename Neutron core/service plugins for VMware NSX * Make metaplugin be used with a router service plugin * Fix wrap target in iptables\_manager * BigSwitch: Fix tenant\_id for shared net requests * BigSwitch: Use backend floating IP endpoint * Updated from global requirements * Imported Translations from Transifex * Raise max header size to accommodate large tokens * NSX: get\_port\_status passed wrong id for network * Imported Translations from Transifex * Reset API naming scheme for VMware NSX plugin * remove pointless test TestN1kvNonDbTest * Rename Security Groups related methods for VMware NSX plugin * Rename L2 Switch/Gateway related methods for VMware NSX plugin * Rename Router related methods for VMware NSX plugin * Plugins should call \_\_init\_\_ of db\_base\_plugin for db.configure * Fixes Tempest XML test failures for Cisco N1kv plugin * Fixes broken documentation hyperlinks * Use "!=" instead of "is not" when comparing two values * ML2/vxlan/test: remove unnecessary self.addCleanup(cfg.CONF.reset) * Fix test\_db\_plugin.test\_delete\_port * Handle racing condition in OFC port deletion * Imported Translations from Transifex * Adds https support for metadata agent * Fix VPN agent does not handle multiple connections per vpn service * Don't require passing in port\_security=False if security\_groups present * wsgi.run\_server no longer used * Use different context for each API request in unit tests * Sync minimum requirements * Implements an LBaaS driver for NetScaler devices * vshield task manager: abort tasks in stop() on termination * Copy cache package from oslo-incubator * BigSwitch: Move config and REST to diff modules * Implements provider network support in PLUMgrid plugin * Should specify expect\_errors=False for success response * Fix unshortened IPv6 address caused DHCP crash * Add support to request vnic type on port * tests/unit: Initialize core plugin in TestL3GwModeMixin * Revert "Skip a test for nicira service plugin" * Improve unit test coverage for Cisco plugin model code * Imported Translations from Transifex * Fix class name typo in test\_db\_rpc\_base * Embrane Tempest Compliance * ipt\_mgr.ipv6 written in the wrong ipt\_mgr.ipv4 * Update help message of flag 'enable\_isolated\_metadata' * Imported Translations from Transifex * Fix invalid facilities documented in rootwrap.conf * Reset the policy after loading extensions * Fix typo in service\_drivers.ipsec * Validate rule uuids provided for update\_policy * Add update from agent to plugin on device up * Remove dependent module py3kcompat * Delete duplicate internal devices in router namespace * Use six.StringIO/BytesIO instead of StringIO.StringIO * Parse JSON in ovs\_lib.get\_vif\_port\_by\_id * Imported Translations from Transifex * Skip a test for nicira service plugin * Remove DEBUG:....nsx\_cluster:Attribute is empty or null * Fix request timeout errors during calls to NSX controller * remove unused imports * L3 agent fetches the external network id once * Avoid processing ports which are not yet ready * Ensure that session is rolled back on bulk creates * Add DB mappings with NSX logical routers * Use save\_and\_reraise\_exception when reraise exception * nec plugin: Compare OFS datapath\_id as hex int * Use six.moves.urllib.parse instead of urlparse * Rename Queue related methods for VMware NSX plugin * Lowercase OVS sample config section headers * Add DB mappings with NSX logical switches * NSX: Fix possible deadlock in sync code * Raise an error from ovs\_lib list operations * Add additional unit tests for the ML2 plugin * Fix ValueError in ip\_lib.IpRouteCommand.get\_gateway() * Imported Translations from Transifex * Fix log-related tracebacks in nsx plugin * add router\_id to response for CRU on fw/vip objs * Move db migration of ml2 security groups to havana * Sync latest oslo.db code into neutron * Add support for router scheduling in Cisco N1kv Plugin * Imported Translations from Transifex * Add migration support from agent to NSX dhcp/metadata services * Validate multicast ip range in Cisco N1kv Plugin * NSX plugin: fix floatingip re-association * Re-enable lazy translation * Do not append to messages with + * Remove psutil dependency * Remove legacy quantum config path * LBaaS: move agent based driver files into a separate dir * mailmap: update .mailmap * Fix binding:host\_id is set to None when port update * Return request-id in API response * Skip extra logging when devices is empty * Add extraroute\_db support for Cisco N1kv Plugin * Improve handling of security group updates * ML2 plugin cannot raise NoResultFound exception * Fix typo in rootwrap files: neuton -> neutron * Imported Translations from Transifex * Prepare for multiple cisco ML2 mech drivers * ML2 Cisco Nexus MD: Create pre/post DB event handlers * Support building wheels (PEP-427) * NVP plugin:fix delete sec group when backend is out of sync * Use oslo.rootwrap library instead of local copy * Fix misspellings in neutron * Remove unnecessary call to get\_dhcp\_port from DeviceManager * Refactor to remove \_recycle\_ip * Allow multiple DNS forwarders for dnsmasq * Fix passing keystone token to neutronclient instance * Don't document non-existing flag '--hide-elapsed' * Fix race condition in network scheduling to dhcp agent * add quota support for ryu plugin * Imported Translations from Transifex * Enables BigSwitch/Restproxy ML2 VLAN driver * Add and update subnet properties in Cisco N1kv plugin * Fix error message typo * Configure floating IPs addresses after NAT rules * Add an explicit tox job for functional tests * improve UT coverage for nicira\_db operations * Avoid re-wiring ports unnecessarily * Process port\_update notifications in the main agent loop * Base ML2 bulk support on the loaded drivers * Imported Translations from Transifex * Removes an incorrect and unnecessary return * Reassign IP to vlan interface when deleting a VLAN bridge * Imported Translations from Transifex * Change metadata-agent to have a configurable backlog * Sync with commit-id: 9d529dd324d234d7aeaa3e6b4d3ab961f177e2ed * Remove unused RPC calls from n1kv plugin code * Change metadata-agent to spawn multiple workers * Extending quota support for neutron LBaaS entities * Tweak version nvp/nsx version validation logic for router operations * Simplify ip allocation/recycling to relieve db pressure * Remove unused code * Reduce severity of log messages in validation methods * Disallow non-admin users update net's shared attribute * Fix error while connecting to busy NSX L2 Gateway * Remove extra network scheduling from vmware nsx plugin * L3 Agent restart causes network outage * Remove garbage in vim header * Enable hacking H233 rule * Rename nvp\_cluster for VMware NSX plugin * Minimize the cost of checking for api worker exit * Remove and recreate interface if already exists 2014.1.b2 --------- * Use an independent iptables lock per namespace * Report proper error message in PLUMgrid Plugin * Fix interprocess locks for run\_tests.sh * Clean up ML2 Manager * Expunge session contents between plugin requests * Remove release\_lease from the DHCP driver interface * VMware NSX: add sanity checks for NSX cluster backend * Update RPC code from oslo * Fix the migration adding a UC to agents table * Configure plugins by name * Fix negative unit test for sec group rules * NVP: Add LOG.exception to see why router was not created * Add binding:host\_id when creating port for probe * Fix race condition in delete\_port method. Fix update\_port method * Use information from the dnsmasq hosts file to call dhcp\_release * Fix pip install failure due to missing nvp.ini file * Imported Translations from Transifex * Imported Translations from Transifex * Make timeout for ovs-vsctl configurable * Remove extra whitespace * Fix extension description and remove unused exception * Fix mistake in usage drop\_constraint parameters * Fix race condition on ml2 delete and update port methods * Fix Migration 50e86cb2637a and 38335592a0dc * L3 Agent can handle many external networks * Update lockutils and fixture in openstack.common * Add test to port\_security to test with security\_groups * LBaaS: handle NotFound exceptions in update\_status callback * VMware NSX: Fix db integrity error on dhcp port operations * Use base.BaseTestCase in NVP config test * Remove plugin\_name\_v2 and extension\_manager in test\_config * Enables quota extension on BigSwitch plugin * Add security groups tables for ML2 plugin via migration * Rename nicira configuration elements to match new naming structure * Fix race in get\_network(s) in OVS plugin * Imported Translations from Transifex * Fix empty network deletion in db\_base\_plugin for postgresql * Remove unused imports * nicira: fix db integrity error during port deletion * Rename check\_nvp\_config utility tool * Remove redundant codes * Remove dupl. for get\_resources in adv. services * Start of new developer documentation * Fix NoSuchOptError in lbaas agent test * Corrects broken format strings in check\_i18n.py * [ML2] l2-pop MD handle multi create/delete ports * Dnsmasq uses all agent IPs as nameservers * Imported Translations from Transifex * BigSwitch: Fixes floating IP backend updates * neutron-rootwrap-xen-dom0 handles data from stdin * Remove FWaaS Noop driver as default and move to unit tests dir * Send DHCP notifications regardless of agent status * Mock looping\_call in metadata agent tests * Imported Translations from Transifex * Change default eswitchd port to avoid conflict * Midonet plugin: Fix source NAT * Add support for NSX/NVP Metadata services * Update the descriptions for the log cfg opts * Add VXLAN example to ovs\_neutron\_plugin.ini * Imported Translations from Transifex * ml2/type\_gre: Adds missing clear\_db to test\_type\_gre.py * ml2: gre, vxlan type driver can leak segment\_id * NVP: propagate net-gw update to backend * Imported Translations from Transifex * Nicira: Fix core\_plugin path and update default values in README * Include lswitch id in NSX plugin port mappings * Imported Translations from Transifex * Revert "move rpc\_setup to the last step of \_\_init\_\_" * extra\_dhcp\_opt add checks for empty strings * LBaaS: synchronize haproxy deploy/undeploy\_instance methods * NVP plugin: Do backend router delete out from db transaction * NVP plugin: Avoid timeouts if creating routers in parallel * Updates tox.ini to use new features * LBaaS: fix handling pending create/update members and health monitors * Add X-Tenant-ID to metadata request * Do not trigger agent notification if bindings do not change * fix --excluded of meter-label-rule-create is not working * move rpc\_setup to the last step of \_\_init\_\_ * Updated from global requirements * Sync global requirements to pin sphinx to sphinx>=1.1.2,<1.2 * Update common network type consts to same origin * Remove start index 0 in range() * LBaaS: unify haproxy-on-host plugin driver and agent * change variable name from plugin into agent * Imported Translations from Transifex * Add post-mortem debug option for tests * validate if the router has external gateway interface set * Remove root\_helper config from plugin ini * Fix a race condition in agents status update code * Add LeastRouters Scheduler to Neutron L3 Agent * Imported Translations from Transifex * Imported Translations from Transifex * Remove dead code \_arp\_spoofing\_rule() * Add fwaas\_driver.ini to setup.cfg * Switch to using spawn to properly treat errors during sync\_state * Fix a typo in log exception in the metering agent * Sync rpc fix from oslo-incubator * Do not concatenate localized strings * Imported Translations from Transifex * Removed erronus config file comment * Fix str2dict and dict2str's incorrect behavior * Improve unit test coverage for Cisco plugin common code * Change to improve dhcp-agent sync\_state * Fix downgrade in migration * Sync dhcp\_agent.ini with the codes * Imported Translations from Transifex * Handle failures on update\_dhcp\_port * Handle exceptions on create\_dhcp\_port 2014.1.b1 --------- * Imported Translations from Transifex * Add vpnaas and debug filters to setup.cfg * Fix misspells * Fix bad call in port\_update in linuxbridge agent * atomically setup ovs ports * Adds id in update\_floatingip API in PLUMgrid plugin driver * Sync Log Levels from OSLO * update error msg for invalid state to update vpn resources * Add missing quota flags in the config file sample * Imported Translations from Transifex * Fix unable to add allow all IPv4/6 security group rule * Add request timeout handling for Mellanox Neutron Agent * Revert "ML2 plugin should not delete ports on subnet deletion" * Improve OVS agent logging for profiling * l3\_agent: make process\_router more robust * Fixes missing method in Hyper-V Utils (Metering) * Fix metering iptables driver doesn't read root\_helper param * Updates .gitignore * Stop logging unnecessary warning on context create * Avoid loading policy when processing rpc requests * Improve unit test coverage for Cisco plugin base code * Pass in certain ICMPv6 types by default * Ensure NVP API connection port is always an integer * Mocking ryu plugin notifier in ryu plugin test * Rebind security groups only when they're updated * Fix format errors seen in rpc logging * Add test\_handle\_router\_snat\_rules\_add\_rules * Rebind allowed address pairs only if they changed * Enforce unique constraint on neutron pool members * Send only one agent notification on port update * Fix showing nonexistent NetworkGateway throws 500 instead of 404 * Imported Translations from Transifex * Update Zhenguo Niu's mailmap * Improve unit test coverage for Cisco plugin nexus code * Preserve floating ips when initializing l3 gateway interface * Fwaas can't run in operating system without namespace feature * Imported Translations from Transifex * metaplugin: use correct parameter to call neutron client * Replace stubout with fixtures * Imported Translations from Transifex * Imported Translations from Transifex * Mock the udevadm in the TunnelTestWithMTU test * Avoid dhcp agent race condition on subnet and network delete * Sync openstack.common.local from oslo * Imported Translations from Transifex * ML2 plugin should not delete ports on subnet deletion * Add state reporting to the metadata agent * Move MidonetInterfaceDriver and use mm-ctl * Do not add DHCP info to subnet if DHCP is disabled * Handle IPAddressGenerationFailure during get\_dhcp\_port * Add request-id to log messages * Imported Translations from Transifex * Enable polling minimization * Add configurable ovsdb monitor respawn interval * Ensure get\_pid\_to\_kill works with rootwrap script * Adds tests, fixes Radware LBaaS driver as a result * Optionally delete namespaces when they are no longer needed * Call \_destroy\_metadata\_proxy from \_destroy\_router\_namespaces * Added check on plugin.supported\_extension\_aliases * Cisco nexus plugin fails to untrunk vlan if other hosts using vlan * Catch PortNotFound exception during get\_dhcp\_port * Reduce the severity of dhcp related log traces * MidoNet: Added support for the admin\_state\_up flag * Fix OVS agent reclaims local VLAN * Replace mox in unit tests with mock * LBaaS: fix reported binary name of a loadbalancer agent * Apply six for metaclass * NVP plugin:fix connectivity to fip from internal nw * Imported Translations from Transifex * Add support for NSX/NVP DHCP services * Fix downgrade in migration * Imported Translations from Transifex * Add log statements for policy check failures * Lower severity of log trace for DB integrity error * Adds delete of a extra\_dhcp\_opt on a port * Round-robin SVI switch selection fails on Cisco Nexus plugin * Tune up report and downtime intervals for l2 agent * Fix DB integrity issues when using postgres * Move Loadbalancer Noop driver to the unit tests * Removes unused nvp plugin config param * Midonet to support port association at floating IP creation * Arista ML2 mech driver cleanup and integration with portbindings * Fix MeteringLabel model to not clear router's tenant id on deletion * Fix downgrade in migration * Fix sqlalchemy DateTime type usage * Linux device name can have '@' or ':' characters * Remove the warning for Scheduling Network * Do not run "ovs-ofctl add-flow" with an invalid in\_port * Replace a non-existing exception * Fix random unit-test failure for NVP advanced plugin * Updated from global requirements * Cleanup HACKING.rst * Remove confusing comment and code for LBaaS * Don't shadow str * ExtraRoute: fix \_get\_extra\_routes\_by\_router\_id() * remove repeated network type definition in cisco plugin * Refactor configuring of floating ips on a router * Remove database section from plugin.ini * Fix import log\_handler error with publish\_errors set * DHCP agent scheduler support for BigSwitch plugin * Fix segment range in N1KV test to remove overlap * Fix query error on dhcp release port for postgresql * sync log from oslo * Imported Translations from Transifex * Use correct device\_manager member in dhcp driver * LBaaS UT: use constants vs magic numbers for http error codes * Modified configuration group name to lowercase * Avoid dhcp agent race condition on subnet and network delete * Ensure OVS plugin is loaded in OVS plugin test * Remove deprecated fields in keystone auth middleware * Fix error while creating l2 gateway services in nvp * Fix update\_device\_up method of linuxbridge plugin * LBaaS: Fix incorrect pool status change * Imported Translations from Transifex * NVP: Correct NVP router port mac to match neutron * Updated from global requirements * Removing workflows from the Radware driver code * LBaaS: when returning VIP include session\_persistence even if None * Imported Translations from Transifex * change assertEquals to assertEqual * Fix TypeError: kill doesn't make sense * Update latest OSLO * Revert back to 'call' for agent reports * Imported Translations from Transifex * Imported Translations from Transifex * Fixing the syntax error in the XML Serializer * Raise VipExists exception in case Vip is created or updated for a pool that already has a Vip * Imported Translations from Transifex * NVP metadata access - create elevated context once * Fix race condition in dhcp agent * adding parameter to configure QueuePool in SQLAlchemy * Fix issues with db pooling * use the fact that empty sequences are false * Ensure that lockfile are defined in a common place * Imported Translations from Transifex * Fix typo in policy.json and checks in nicira plugin * Fix DB query returning ready devices in LoadBalancerCallbacks * Imported Translations from Transifex * Load all the necessary database tables when running cisco plugin * Fix haproxy cfg unit test * fix mis-placed paren in log statement for l3-scheduler * Imported Translations from Transifex * Add bulking support for Cisco plugin * Validate protocol when creating VIP * Allow tests in TestDhcpAgentEventHandler run independently * Add scheduling support for the Brocade plugin * Imported Translations from Transifex * Synchronize QuantumManager.get\_instance() method * Imported Translations from Transifex * Imported Translations from Transifex * Pin SQLAlchemy to 0.7.x * Improve test coverage for quantum wsgi module * Adds delete-orphan to database deletion * Imported Translations from Transifex * Do not disable propagate on root logger * NVP metadata access - create elevated context once * Registers root\_helper option for test\_iptables\_firewall * Resolves ryu plugin unittest errors * Set fake rpc implementation in test\_lb\_quantum\_agent * Ensure DB pooling code works with newer eventlet versions * Imported Translations from Transifex * Sync latest Oslo components for updated copyright * drop rfc.sh * Replace "OpenStack LLC" with "OpenStack Foundation" * sync Oslo Grizzly stable branch with Quantum * First havana commit * Ensure port get works when NVP mapping not stored in Quantum DB * remove references to netstack in setup.py * Imported Translations from Transifex * port\_security migration does not migrate data * Adds Grizzly migration revision * Switch to final 1.1.0 oslo.config release * Fix detection of deleted networks in DHCP agent * Add l3 db migration for plugins which did not support in folsom * Updates latest OSLO changes * Set fake rpc backend impl for TestLinuxBridgeAgent * Imported Translations from Transifex * Update oslo rpc libraries * Sets default MySql engine to InnoDB * Solve branch in migration path * Fixes Hyper-V agent issue with mixed network types * Imported Translations from Transifex * missing - in --config-file * Fix typo * Log the configuration options for metadata-proxy and agent * Imported Translations from Transifex * NVP plugin: return 409 if wrong router interface info on remove * Imported Translations from Transifex * Ensure metadata access network does not prevent router deletion * Filter out router ports without IPs when gathering router sync data * Do not delete subnets with IPs on router interfaces * Update to Quantum Client 2.2.0 * Add explicit egress rules to nvp security profile * Update tox.ini to support RHEL 6.x * Fix exception typo * Disable secgroup extension when Noop Firewall driver is used * Wrap quota controller with resource.Resource * Allow probe-create to specify device\_owner * Enable handling the report\_state RPC call in Brocade Plugin * Imported Translations from Transifex * Create quantum client for each api request in metadata agent * Lock tables for update on allocation/deletion * NVP plugin: configure metadata network only if overlapping IPs are enabled * Show default configuration Quotas * add ns-metadata-proxy rootwrap filters to dhcp.filters * isolated network metadata does not work with nvp plugin * Imported Translations from Transifex * Load quota resources dynamically * Notify creation or deletion of dhcp port for security group * fix mis-matched kwargs for a few calls to NvpPluginException * Populate default explicit allow rules for egress * Switch to oslo.config * Moved the configuration variables * Make run\_tests.sh pep8 conf match tox * Fix syntax error in credential.py and missing \_\_init\_\_.py * Imported Translations from Transifex * Add common test base class to hold common things * fix incorrect pathname * Prevent DoS through XML entity expansion * Delete DATABASE option checkup testcases * Fixes linuxbridge agent downs with tap device deletion timing issue * Rename source\_(group\_id/ip\_prefix) to remote\_(group\_id/ip\_prefix) * Imported Translations from Transifex * Setup device alias by device flavor information * L3 port delete prevention: do not raise if no IP on port * Pin pep8 to 1.3.3 * Avoid sending names longer than 40 character to NVP * move cisco-specific extensions to Cisco extensions directory * Add UT for LBaaS HAProxy driver * Include health monitors expected codes upper bound into HAProxy config * Allow DHCP and L3 agents to choose if they should report state * Imported Translations from Transifex * Enable HA proxy to work with fedora * Prevent exception with VIP deletion * Change the default l3\_agent\_manager to L3NATAgent * Imported Translations from Transifex * NEC plugin support for dhcp network and router scheduling * enable linuxbridge for agent scheduler * Move network schedule to first port creation * Imported Translations from Transifex * Host route to metadata server with Bigswitch/Floodlight Plugin * Incorrect argument in calling post\_json * fix update\_port to get tenant\_id from db rather than request * Ensure max length of iptables chain name w/o prefix is up to 11 chars * Cisco plugin support for creating ports without instances * mock quantum.agent.common.config.setup\_logging * Imported Translations from Transifex * Add initial testr support * Replace direct tempfile usage with a fixture * Set fake rpc implementation in metaplugin test configuration * Enabled add gateway to refrain from checking exit code * Add stats reporting to HAProxy namespace driver * Add session persistence support to LBaaS HAProxy driver * Remove deprecated assertEquals alias * LBaaS Agent Reference Implementation * Imported Translations from Transifex * create a Quantum port to reserve VIP address * NVP plugin support for dhcp network scheduling * Bump python-quantumclient version to 2.1.2 * Add scheduling feature basing on agent management extension * Remove compat cfg wrapper * NVP Router: Do no perfom SNAT on E-W traffic * Enable multiple L3 GW services on NVP plugin * Fix retrieval of shared networks * Imported Translations from Transifex * Remove network type validation from provider networks extension * Fix NVP plugin not notifying metadata access network to DHCP agent * Limit amount of fixed ips per port * Fetch all pages when listing NVP Nat Rules * Unpin PasteDeploy dependency version * Make sure all db accesses use subtransaction * Use testtools instead of unittest or unittest2 * Port update with existing ip\_address only causes exception * Enables packetfilter ext in NEC plugin based on its driver config * Set default api\_extensions\_path for NEC plugin * Fixes import reorder nits * Imported Translations from Transifex * Latest common updates * Limit chain name to 28 characters * Add midonet to setup.py * Add password secret to brocade plugin * Use db model hook to filter external network * Add default state\_path to quantum.conf * Imported Translations from Transifex * Imported Translations from Transifex * refactor LoadBalancerPluginDbTestCase setUp() * Imported Translations from Transifex * Remove external\_id and security group proxy code * Add pagination parameters for extension extraroute * Imported Translations from Transifex * Provide a default api\_extensions\_path for nvp\_plugin * AttributeError: No such RPC function 'report\_state' * Add pagination support for xml * Sync latest install\_venv\_common.py with olso * Imported Translations from Transifex * Add check-nvp-config utility * Close file descriptors when executing sub-processes * Add support Quantum Security Groups for Ryu plugin * Resolve branches in db migration scripts to G-3 release * Add Quantum support for NVP Layer-2 gateways * Implement MidoNet Quantum Plugin * Routing table configuration support on L3 * Correct permissions on quantum-hyperv-agent * Raising error if invalid attribute passed in * Support Port Binding Extension in BigSwitch plugin * Exit if DHCP agent interface\_driver is not defined * Supporting pagination in api v2.0 * Update latest OSLO files * Modify dhcp agent for agent management extension * Imported Translations from Transifex * Metadata support for NVP plugin * Add routed-service-insertion * plugin/nec: Make sure resources on OFC is globally unique * Fix SG interface to reflect the reality * Add unit test for ryu-agent * Agent management extension * Need to pass port['port'] to \_get\_tenant\_id\_for\_create() * Improve error handling when nvp and quantum are out of sync * Decouple helper functions from L3NatDBTestCase * Imported Translations from Transifex * Add Migration for nvp-qos extension * Use oslo-config-2013.1b3 * Shorten the DHCP default resync\_interval * Add nvp qos extension * Imported Translations from Transifex * Unable to update port as non-admin nvp plugin * Update nvplib to use HTTP constants * Rename admin\_status\_up to admin\_state\_up * Fixed the typo of loadbalancer test case * Allow nicira plugin to handle multiple NVP API versions * Imported Translations from Transifex * L3 API support for BigSwitch-FloodLight Plugin * Add an update option to run\_tests.sh * Avoid extra query when overlapping IPs are disabled * Allow tests from test\_dhcp\_agent run independently * Imported Translations from Transifex * Mark password config options with secret * Adds Brocade Plugin implementation * Add support for extended attributes for extension resources * Imported Translations from Transifex * Support iptables-based security group in NEC plugin * Persist updated expiration time * Support advanced validation of dictionaries in the API * Synchronize code from oslo * Add check for subnet update with conflict gateway and allocation\_pools * Alembic migration script for Loadbalancing service * Fix NVP L3 gateway ports admin\_state\_down on creation * Remove cfg option default value and check if missing * Remove duplicated option state\_path from netns cleanup * only destroy single namespace if router\_id is set * Use AssertEqual instead of AssertTrue * Imported Translations from Transifex * Move auth\_token configurations to quantum.conf * L3 API support for nicira plugin * Unused methods in quantum.wsgi clean up * Add firewall\_driver option to linuxbridge\_conf.ini * Adds API parameters to quantum.api.extension.ResourceExtension * fix grammar in NetworkInUse exception * Imported Translations from Transifex * PLUMgrid quantum plugin * Implements quantum security groups support on OVS plugin * Sync latest cfg from oslo-incubator * Improvements to API validation logic * Imported Translations from Transifex * add non-routed subnet metadata support * Imported Translations from Transifex * Enable OVS and NETNS utilities to perform logging * Add unit tests for Open vSwitch Quantum plugin * Add NVP Security group support * Fix import error in ryu-agent * Imported Translations from Transifex * Bad translation from network types to nvp transport types * Update .coveragerc * Register root\_helper in test\_debug\_commands and test\_dhcp\_agent * Adds xml support for quantum v2 API * Allow tools/install\_venv\_common.py to be run from within the source directory * Cisco plugin cleanup follow up commit * Be smarter when figuring out broadcast address * Use policy\_file parameter in quantum.policy * Imported Translations from Transifex * Define root\_helper variable under the [AGENT] section * Fixes rest of "not in" usage * Updated to latest oslo-version code * Imported Translations from Transifex * Imported Translations from Transifex * Imported Translations from Transifex * Resetting session persisnence for a VIP * Improve data access method of ryu-agent * Fixes 'not in' operator usage * Imported Translations from Transifex * Adds support of TCP protocol for LBaaS VIPs * Sync latest cfg from oslo-incubator * Remove redunant key list generation in Cisco plugin * Fixes if statement inefficiency in quantum.agent.linux.interface * Imported Translations from Transifex * Postgresql ENUM type requires a name exceptions NVP Plugin * correct spelling of Notify in classname * Disable dhcp\_domain distribution when dhcp\_domain is empty * Make protocol and ethertype case insensitive for security groups * Fix branch in db migration scripts * Finish adding help strings to all config options in Quantum code * Add NVP port security implementation * Imported Translations from Transifex * Set default lock\_path in state\_path * Use install\_venv\_common.py from oslo * Make get\_security\_groups() return security group rules * Fix OVSQuantumAgent.port\_update if not admin\_state\_up * Clean up test\_extensions.py imports * Fixes import order errors * OVS cleanup utility removes veth pairs * Revert "Reqd. core\_plugin for plugin agents & show cfg opts loaded." * Reqd. core\_plugin for plugin agents & show cfg opts loaded * Ensure that correct root helper is used * Fix InvalidContentType can't be raised because of error in constructor * OVS: update status according to admin\_state\_up * Cisco plugin cleanup * Improving code reuse with loadbalancer entity deletion * Fix database reconnection * Fixes per tenant quota doesn't work * Adds port security api extension and base class * LinuxBridge: set port status as 'DOWN' on creation * LinuxBridge: update status according to admin\_state\_up * Use babel to generate translation file * LBaaS plugin returns unnecessary information for PING and TCP health monitors * Fix all extension contract classes inherit from extensions.ExtensionDescriptor * get\_security\_group() now returns rules * set allocation\_pool\_id nullable=False * make IPv6 unit test work on systems with eth0 * Support Port Binding Extension in NEC plugin * Enable NEC OpenFlow plugin to use per-tenant quota * Enhance wsgi to listen on ipv6 address * Fix i18n messages * Update Oslo rpc * Enforces generic sqlalchemy types in migrations * Remove redudant code * Removes redundant code in quantum.api.api\_common * Fix i18n messages in quantum.api.api\_common * Completes unittest coverage of quantum.api.api\_common * Enable test\_agent\_ovs\_cleanup to be run alone * Fix i18n messages for cisco plugin * Provide atomic database access for ports in linuxbridge plugin * Add help strings to config file options in Quantum code * Document that code is on github now in README * Config lockutils to use a temp path for tests * Fix downgrade revision to make db migration linear * Send notification on router interface create/delete * More unittests for quantum.api.v2.base * Fixes inefficiency in quantum.api.v2.base.\_filters * Refactor hyperv plugin and agent * Update Oslo rpc module * Provide atomic database access nvp plugin * \_validate\_security\_groups\_on\_port was not validating external\_ids * Update WebOb version to >=1.2 * Ensure that agents also set control\_exchange * Add a common test case for Port Binding Extension * Fix line endings from CRLF to LF * Fixes import order nits * Fix ATTR\_NOT\_SPECIFIED comparison errors * Add migration for network bindings in NVP plugin * NEC OpenFlow plugin supports L3 agent RPC * Update latest OSLO * Catch up RPC context fixes on NEC OpenFlow plugin * ensure all enums in loadbalancer models have names * Adding multi switch support to the Cisco Nexus plugin * Name the securitygrouprules.direction enum * Adds support for deploying Quantum on Windows * Adds a Hyper-V Quantum plugin * Add exception validation for subnet used * Remove accessing cfg.CONF.DATABASE in nec-agent * Inform a client if Quantum provides port filtering feature * Remove unsused imports in the plugins package * DHCP agent unable to access port when restarting * Remove unused imports in unit tests * Use default\_notification\_level when notification * Latest OSLO updates * NvpPluginException mixes err\_msg and err\_desc * Fixes i18n messages in nvp plugin * Optimize if/else logic in quantum.api.v2.base.prepare\_request\_body() * Fixes quantum.api.v2.base.\_filters to be more intuitive * Fix for loadbalancer vips list * rename port attribute variable to SECURITYGROUPS from SECURITYGROUP * Remove relative imports from NVP plugin * Port to argparse based cfg * Fix database configuration of ryu-agent * Pass X-Forwarded-For header to Nova * The change implemented Lbaas CRUD Sqlalchemy operations * Iptables security group implementation for LinuxBridge * Update the migration template's default kwargs * add migration support for lb security groups * Fix import for quantum-db-manage * Allow nvp\_api to load balance requests * API extension and DB support for service types * Add migration support to Quantum * Remove some unused imports * Undo change to require WebOb 1.2.3, instead, require only >=1.0.8 * Add common support for database configuration * Fixup import syntax error in unit test * Enable the user to enforce validity of the gateway IP * Add comment to indicate bridge names' length * refactor QuotaV2 import to match to other exts * change xxx\_metadata\_agent() into xxx\_metadata\_proxy() * Fix the replacement placeholder in string * Ensure that exception prints UUID and not pointer * .gitignore cleanup * Fixes i18n message for nec plugin * Fixes i18n message for ryu plugin * Remove unused imports in debug package * sql\_dbpool\_enabled not passed to configured\_db nvp\_plugin * Enable tenants to set non-owned ext network as router gateway * Upgrade WebOb to 1.2.3 * Logging module cleanup * Remove unused imports in common package * Remove unused imports in rootwrap package * Remove unused imports in db package * Remove unused imports in api package * Provider network implementation for NVP plugin * Remove unused imports in agent package * Set default core\_plugin to None * Ensure that exception prints correct text * Cleans up bulk\_body generation in quantum.api.v2.base.prepare\_request\_body() * Exceptions cleanup * Readjust try/catch block in quantum.api.v2.base.create() * Ensures that the dnsmasq configuration file flag is always set * Ensure allocation pools are deleted from database * Raise InvalidInput directly instead of catch it * Ensure bulk creations have quota validations * Correct exception output for subnet deletion when port is used * Update the configuration help for the OVS cleanup utility * Implementing string representation for model classes * Provide "atomic" database access for networks * Add OVS cleanup utility * Removes redundant code in quantum.api.v2.base.create() * Add eventlet db\_pool use for mysql * Clean up executable modules * Fixes import order nits * Fix log message for unreferenced variable * The patch introduces an API extension for LBaaS service * Fix pep8 issues * Add tox artifacts to .gitignore * Correct i18n messages for bigswitch plugin * dhcp\_agent.ini, l3\_agent.ini: update dhcp/l3\_agent.ini * Make patch-tun and patch-int configurable * Update test\_router\_list to validate the router returned * Fixed the security group port binding should be automatically deleted when delete\_port * Add restproxy.ini to config\_path in setup.py * Replaces assertEquals to assertEqual * Completes coverage of quantum.api.v2.resource * Fixed the unit tests using SQLite do not check foreign keys * dhcp.filters needs ovs\_vsctl permission * Correct i18n message for nicira plugin * Correct i18n message for metaplugin * add parent/sub-resource support into Quantum API framework * plugins/ryu: l3 agent rpc for Ryu plugin is broken * pluins/ryu: Fixes context exception in Ryu plugin * DRY for network() and subnet() in test\_db\_plugin.py * Adds validity checks for ethertype and protocol * Add script for checking i18n message * Update evenlet monkey patch flags * Remove unnecessary port deletion * Support to reset dnsname\_servers and host\_routes to empty * Prevent unnecessary database read by l3 agent * Correct i18n message for linuxbridge plugin * Add router testcases that missing in L3NatDBTestCase * Releasing resources of context manager functions if exceptions occur * Drop duplicated port\_id check in remove\_router\_interface() * Returns more appropriate error when address pool is exhausted * Add VIF binding extensions * Sort router testcases as group for L3NatDBTestCase * Refactor resources listing testcase for test\_db\_plugin.py * l3 agent rpc * Fix rootwrap cfg for src installed metadata proxy * Add metadata\_agent.ini to config\_path in setup.py * add state\_path sample back to l3\_agent.ini file * plugin/ryu: make live-migration work with Ryu plugin * Remove \_\_init\_\_.py from bin/ and tools/ * Removes unused code in quantum.common * Fixes import order nits * update state\_path default to be the same value * Use /usr/bin/ for the metadata proxy in l3.filters * prevent deletion of router interface if it is needed by a floating ip * Completes coverage of quantum.agent.linux.utils * Fixes Rpc related exception in NVP plugin * make the DHCP agent use a unique queue name * Fixes Context exception in BigSwitch/FloodLight Plugin * fix remap of floating-ip within l3-agent polling interval * Completes coverage of quantum.agent.rpc.py * Completes coverage of quantum.agent.netns\_cleanup.py * add metadata proxy support for Quantum Networks * Make signing dir a subdir in /var/lib/quantum * Use openstack.common.logging in NEC OpenFlow plugin * Correct i18n message for api and db module * Fixes update router gateway successful with existed floatingip association * Fixes order of route entries * fix so cisco plugin db model to not override count methods * Use auth\_token middleware in keystoneclient * Fixes pep8 nit * Make sure we can update when there is no gateway port linked to it * Fix syntax error in nvplib * Removes quantum.tests.test\_api\_v2.\_uuid() * Add filters for quantum-debug * Removing unnecessary setUp()/tearDown() in SecurityGroupsTestCase * Fix exception when security group rule already exists * Don't force run\_tests.sh pep8 only to use -N * Correct i18n message for ovs plugin * Replaces uuid.uuid4 with uuidutils.generate\_uuid() * Correct i18n message * Removes \_validate\_boolean() * Removes quantum.common.utils.str\_uuid() * Refactors quantum.api.v2.attributes.py * Updates tearDown() to release instance objects * pass static to argv to quantum-debug config parser * Improve openvswitch and linuxbridge agents' parsing of mappings * Move extension.py into quantum/api * Ensure that the expiration time for leased IP is updated correctly * Fix context problem * bug 1057844: improve floating-ip association checks * fix broken logic of only using hasattr to check for get\_x\_counts * Prevent router being deleted if it is used by a floating IP * Updates clear\_db() to unregister models and close session * The change allows loading several service plugins along with core plugin * fix incorrect kwarg param name for region with l3-agent * All egress traffic allowed by default should be implied * Fix unitest test\_router\_list with wrong fake return value * Delete floating port and floatingip in the same transaction * Completes unittest coverage of quantum.api.v2.attributes.py * Use DB count to get resource counts * plugin/ryu, linux/interface: remove ryu specific interface driver * Allow NVP plugin to use per-tenant quota extension * Revert "Put gw\_port into router dict result." * Ensure that deleted gateway IP address is recycled correctly * Ensure that fixed port IP address is in valid allocation range * RESTProxy Plugin for Floodlight and BigSwitch * Ensure that mac address is set to namespace side veth end * plugin/ryu: update for ryu update * plugin/ryu: add tunnel support * Adds tests for attribute.\_validate\_uuid * Adds tests to attribute.convert\_to\_int * Adds tests for attributes.is\_attr\_set * Adds test scripts for \_validate\_string * Adds test scripts for \_validate\_range * Part of the patch set that enables VM's to use libvirts bridge type * Remove qpid configuration variables no longer supported * Removing unsed code for Cisco Quantum Plugin V1 * Add QUANTUM\_ prefix for env used by quantum-debug * Make tox.ini run pep8 checks on bin * Explicitly include versioninfo in tarball * Adds test scripts for \_validate\_values * Clean up quantum.api.v2.validators * Add indication when quantum server started * Import lockutils and fileutils from openstack-common * Update latest openstack-common code * Clean up executable modules * Remove nova code from Quantum Cisco Plugin * Use isinstance for \_validate\_boolean * Fixes convert\_to\_boolean logic * Updated openstack-common setup and version code * Validate L3 inputs * Treat case when pid is None * Fix openssl zombies * Ensure that the anyjson version is correct * Add eventlet\_backdoor and threadgroup from openstack-common * Add loopingcall from openstack-common * Added service from openstack-common * Sync latest notifier changes from openstack-common * Update KillFilter to handle 'deleted' exe's * Pep8 fixes for quantum master * Use \_validate\_uuid in quantum.plugins.nec.extensions.packetfilter.py * Use is\_uuid\_like in quantum.extensions.securitygroup.py * Removes regex validation of UUIDs in dhcp\_agent * Use uuidutils.is\_uuid\_like in quantum.extentions.l3 * Implements \_validate\_uuid * Use uuidutils for uuid validation * Drop lxml dependency * Testcase of listing collection shouldn't depend on default order of db query * Add uuidutils module * Log loaded extension messages as INFO not WARNING * db\_base\_plugin\_v2.QuantumDbPluginV2.create\_port clean-up * Clean-up comments in quantum/db/l3\_db.py * Import order clean-up * let metaplugin work with plugin which has not l3 extension support * Ensure that HTTP 400 codes are returned for invalid input * Use openstack common log to do logging * Put gw\_port into router dict result * Add check for cidr overrapping for adding external gateway * Fix unnecessary logging messages during tests * support 'send\_arp\_for\_ha' option in l3\_agent * pin sqlalchemy to 0.7 * Remove unused metaplugin agents * Get subnets of router interfaces with an elevated context * Support external network in probe-create * remove unused modules for linuxbridge/ovs plugin agent * Chmod agent/linux/iptables\_manager.py * Quantum Security Groups API * Make create\_floatingip support transaction * Update policies * Notify about router and floating IP usages * Fix exception when port status is updated with linux bridge plugin * Call iptables without absolute path * Delete the child object via setting the parent's attribute to None * Add unit tests for the ovs quantum agent * Add MTU support to Linux bridge * Correct Intended Audience * Add OpenStack trove classifier for PyPI * use object directly instead of the foreigh key to update master db object * Remove database access from agents * Fix database clear when table does not exist * IP subnet validation fixes * Update default base database to be V2 * Update common * add test for create subnet with default gateway and conflict allocation pool * Logging indicates when service starts and terminates * Ensures port is not created when database exception occurs * Improve unit test times * Add control\_exchange option to common/config.py * Treat invalid namespace call * get\_network in nvp plugin didn't return subnet information * tests/unit/ryu/test\_ryu\_db: db failure * correct nvplib to update device\_id * Update rpc and notifier libs from openstack.common * Add quantum-usage-audit * Fix filters default value in get\_networks * l3\_nat\_agent was renamed to l3\_agent and this was missed * Update vif driver of Ryu plugin * Support for several HA RabbitMQ servers * Correct the error message in the Class NoNetworkAvailable * Fix flag name for l3 agent external network id * clean notification options in quantum.conf * Add log setting options into quantum.conf * Warn about use of overlapping ips in config file * Do global CIDR check if overlapping IPs disabled * Fix rootwrap filter for dnsmasq when no namespace is used * Add common popen support to the cisco plugin * Use sqlite db on file for unit tests * Uses a common subprocess popen function * remove default value of local\_ip in OVS agent * Remove a function that is not used * all rootwrap filter for 'route', used by l3-agent * l3-agent: move check if ext-net bridge exists within daemon loop * Add catch-call try/catch within rpc\_loop in ovs plugin agent * Fix OVS and LB plugins' VLAN allocation table synchronization * ZMQ fixes for Quantum from openstack-common * Restore SIGPIPE default action for subprocesses * Fix for flat network creation in Cisco plugin * Removes test desription that is no longer valid * Modified code Pyflakes warning * Fix deadlock of Metaplugin * remove unittest section for nec plugin README file * remove unittest section for ryu plugin README file * Fix for DB error in the Cisco plugin * modify the wrong phy\_brs into phys\_brs * NVP plugin missing dhcp rpc callbacks * make README point to real v2 API spec * README file changes for Cisco plugin * fix for nested rootwrap checks with 'ip netns exec' * always push down metadata rules for router, not just if gateway exists * Removed eval of unchecked strings * Update NVP plugin to Quantum v2 * ovs-lib: make db\_get\_map return empty dict on error * Update l3-agent.ini with missing configuration flags * Sync a change to rpc from openstack-common * Fix for failing network operations in Cisco plugin * add missing files from setup.py * Add quantum-nec-agent to bin directory * remove not need shebang line in quantum debug * make rootwrap filters path consistent with other openstack project * Bump version to 2013.1, open Grizzly * Fix lack of L3 support of NEC OpenFlow plugin * Add a new interface driver OVSVethInterfaceDriver * Ensure that l3 agent does not crash on restart * make subnets attribute of a network read-only * Exclude openstack-common from pep8 test * Ensures that the Linux Bridge Plugin runs with L3 agent * Remove an external port when an error occurs during FIP creation * Remove the exeception handler since it makes no sense * Add enable\_tunneling openvswitch configuration variable * Create .mailmap file * Update default policy for add/remove router interface to admin\_or\_owner * Add periodic check resync check to DHCP agent * Update metaplugin with l3 extension update * Add DHCP RPC API support to NEC OpenFlow plugin * Remove an external interface when router-gateway is removed * openvswitch plugin does not remove inbound unicast flow in br-tun * Remove default name for DHCP port * Added policy checks for add interface and remove interface * allow multiple l3-agents to run, each with one external gateway net * Prevent floating-ip and ex-gateway ports should prevent net deletion * fix generation of exception for mismatched floating ip tenant-ids * Give better error to client on server 500 error * Change 422 error to 400 error * Add IP version check for IP address fields * Policies for external networks * Add IP commands to rootwrap fileter for OVS agent * Modified code Pyflakes warning * Modified code Pyflakes warning * Modified code Pyflakes warning * Modified code Pyflakes warning * Modified code Pyflakes warning * Modified code Pyflakes warning * Modified code Pyflakes warning * Modified code Pyflakes warning * Modified code Pyflakes warning * Fix broken L3 support of Ryu plugin * check subnet overlapping when adding interface to router * add local network type and use by default for tenant networks * Fix data passed to policy engine on update * remove incorrect mock assert\_called in unit tests * Fix dhcp agent rpc exception handling * Add missing include for logging when log\_config is used * Modified code Pyflakes warning * Modified code pyflakes warning * Improve error message when flat network already exists * Lower webob dep from v1.2.0 to v1.0.8 * Allocation pool creation should check if gateway is in subnet * Make sure floating IPs + gateways must be on external nets * restart dnsmasq when subnet cidr set changes * supress dhcp router opt for subnets with null gw * add rootwrap filters to wrap ip netns exec * Implements agent for Quantum Networking testing * Quantum dhcp crashes if no networks exist * Update with latest code from openstack-common (stable/folsom) * Fixes undefined variable 'network\_type' in OVS agent * Create utility to clean-up netns * Fix lack of L3 support of Ryu plugin * Ensure that port update set correct tag in OVS * ovs\_lib unable to parse return when port == -1 * L3: make use of namespaces by agent configurable * Fix error in rule for metadata server dnat * Fix programming error of ryu-plugin * Ensure network delete is handled by OVS agent * Implement L3 support in Metaplugin * Fixes agent problem with RPC * netns commands should always run in the root ns * Add lease expiration management to ip recycling * misc L3 fixes * expose openvswitch GRE tunnel\_id via provider API * Do not transfer ips if there isn't any * prevent invalid deletion of ports using by L3 devices * Modified code PEP8 warning * Implementation of 2nd phase of provider extension for openswitch * Mangle network namespace name used by dhcp\_agent * Update rootwrap; track changes in nova/cinder * remove policy check for host\_routes in update\_port * Ensure proper validation for l3 API attributes * Cisco nexus sub-plugin update\_network fix * Fix dhcp option distribution by dnsmasq * fix bug where network owned resources block delete * Plugin aware extensions should also be reset at each test setup * Ensure network connectivity for linuxbridge flat network * Execute unit tests for Cisco plugin with Quantum tests * prevent OVS + LB plugins from clearing device\_id and device\_owner * updated outdated comments in base v2 plugin class * clear db.\_ENGINE for each plugin init in Metaplugin * Enable tox to run OVS plugin unit tests * Allow tox to run plugin specific unit tests * fixes cisco nexus plugin delete network issue * Fix Metainterface driver with namespace * Add lease expiration script support for dnsmasq * Remove 'verbose' API capability * PEP8 issues fixed * removed some unused global variable * Update TESTING file * Typo fix in quantum: existant => existent * Add DHCP RPC API support to Ryu plugin * Run core unit tests for each plugin * OVS plugin tunnel bridges never learn * Add nosehtmloutput as a test dependency * fix typo in OVS plugin from recent bugfix * enable router deletion logic in l3-agent * Enable users to list subnets on shared networks * Fix IP allocation on shared networks ports * Move metaplugin test for common test directory * Enable DHCP agent to work with plugin when L2 agents use DB polling * fix associating a floating IP during floating IP creation * Ensure that LB agent does not terminate if interface already exists in bridge * Treat exceptions when invoking ovs-vsctl * Remove v1.0 and v1.1 API from version info * Get OVS port details from port ID * Fix undefined variables * Fixing unit test failures in Cisco plugin * fix netns delete so that it works when a ns is set * Linuxbridge support for L3 agent * Fix exception message for bulk create failure * quantum l3 + floating IP support * Add missing conversion specifiers in exception messages * Use a common constant for the port/network 'status' value * Remove unused variable * Log message missing parameter causes exception * Update README for v2 API * Fix flavor extension based on new attribute extension spec * Update the Nicira NVP plugin to support the v2 Quantum API * Enhancements to Cisco v2 meta-plugin * Add model support for DHCP lease expiration * Trivial openvswitch plugin cleanup * Convert DHCP from polling to RPC * Add quota per-tenant * Reset device owner when port on agent is down * Allow extra config files in unit tests * Fix visual indentation for PEP8 conformance * Updates pip requirements * NEC OpenFlow plugin support * Enables Cisco NXOS to configure multiple ports Implements blueprint cisco-nxos-enables-multiple-ports * Implementation of second phase of provider extension * deal with parent\_id not in target * remove old gflags config code * convert query string according to attr map * Add device\_owner attribute to port * implementation for bug 1008180 * Fix bulk create operations and make them atomic * Make sure that there's a way of creating a subnet without a gateway * Update latest openstack files * improve test\_db\_plugin so it can be leveraged by extension tests * Adds the 'public network' concept to Quantum * RPC support for OVS Plugin and Agent * Initial implemention of MetaPlugin * Make dhcp agent configurable for namespace * Linux Agent improvements for L3 * In some cases device check causes an exception * normalize the json output of show a given extension * move the correct veth into the netns for the LB * linux bridge fixes following v1 code removal * fixes typo in ensure\_namespace * Remove v1 code from quantum-server * Add netns to support overlapping address ranges * dhcp-agent: Ryu plugin support for dhcp agent * fix missing deallocation of gateway ip * RPC support for Linux Bridge Plugin and Agent * Implementation of bp per-net-dhcp-enable * Enhance Base MAC validation * Use function registration for policy checks * Exempt openstack-common from pep8 check * Make 4th octet of mac\_range configurable * Replace openvswitch plugin's VlanMap with vlan\_ids DB table * Remove unused properties * Notification for network/subnet/port create/delete/update. blueprint quantum-notifications * Make the plugin for test\_db\_plugin configurable * update DHCP agent to work with linuxbridge plug-in * ryu/plugin, agent: unbreak 610017c460b85e1b7d11327d050972bb03fcc0c3 * Add classmethod decorator to class methods of providervlan ext * Only delete VLAN information after Quantum network is deleted * Make quantum pipeline configurable from quantum.conf * ovs\_quantum\_plugin should use reconnect\_interval in common conf * add name into port and subnet * Update openvswitch tunnel unittest * Enable agents and plugins to use the same configuration file * Fix linuxbridge agent tests * Update openstack-common files * Initial V2 implementation of provider extension * Implements data-driven views and extended attributes * Add v2 API support for the Cisco plugin Blueprint cisco-plugin-v2-api-support * Enhance V2 validations to work better for integers and booleans * Refactor the test cases so that all the test cases are under one test class * Add quota features into quantum. Blueprint quantum-api-quotas * Assume that subclass validates value of UUID * fix bug lp:1025526,update iniparser.py to accept empty value * Ensures policy file is reloaded only if updated * Provide way to specify id in models\_v2 * Add validity checks to Quantum v2 resources * Avoid removal of attributes used by policy engine * Raise proper exception if policy file do not exist * Introduce files from openstack common * Ensures API v2 router does not load plugin twice * ovs-agent exception non-existent ports * Ryu plugin support for v2 Quantum API * Add option sql\_max\_retries for database connection * Enable quantum agents to work with global cfg.CONF * Create DHCP agent tap device from port ID * Fix some syntax errors * fix bug lp:1019230,update rpc from openstack-common * Fix v2 API policy checks when keystone is in use * implement dhcp agent for quantum * Corrects imported modules in Cisco and Ryu according to latest nova packages * Validate that network\_id in port/subnet POST belong to the same tenant * Verify CIDR overlaps among networks' subnets * Address problems with foreign keys with subnet and network deletion * Add 'allocation\_pools' to Quantum v2 API subnets * Delete IP allocation range for subnet when deleting subnet * Fix linux bridge plugin to be consistent with naming rules * v2 support for the linux bridge plugin * OVS plugin support for v2 Quantum API * Check if interface exists in bridge prior to adding * Ensure that subnet\_id is on correct network * Use setuptools git plugin for file inclusion * Cisco's unplug\_iface refers to non existing exception * Implement IP address allocation * Enable user to configure base mac address * Bug #1012418 - quantum agent for OVS does not install properly on Xen XCP * Add simple file loggin to ovs\_quantum\_agent * Fixing pep8 warning messages Bug #1017805 * Network deletion and subnet creation bug fixes bug 1017395 * Remove paste configuration details to a seperate file. blueprint use-common-cfg * Bug 1015953 - linuxbridge\_quantum\_agent device\_exists() is buggy * Reorder imports by full module path * Added iptables\_manager ( based on openstack/linux\_net.py ) This module will be the base library to implement security groups and generic firewall. It is an independent iptables module, made to be easy to package if used by agents and also inside quantum * Unit test and Readme changes related to cisco plugin * Implements the blueprint use-common-cfg for the quantum service. More specifically uses global CONF for the quantum.conf file * Ensure unique mac address allocation. This is the first part of bug 1008029 * Add authZ through incorporation of policy checks * Fix additional pep8 issues on Jenkins bug 1014644 * removed "runthis" and other unused functions from utils.py * Linux bridge agents did not work with common linus utils bug 1014286 * Added vlan range management for OVS plugin * Bug #1013967 - Quantum is breaking on tests with pep 1.3 * Remove wrong base class for l2network\_models after v2.0 API * Cisco cli cannot find argument action\_prefix * Use openstack.common.exception * Remove unused functions in common/utils.py * API v2: mprove validation of post/put, rename few attributes * Bug #1000406 - Return value of shell commands is not checked by plugins * Fix python2.4 incompatibility * Add API v2 support * Binaries should report versions * Fix up test running to match jenkins expectation * Add build\_sphinx options * Remove unused imports * Quantum should use openstack.common.jsonutils * Remove hardcoded version for pep8 from tools/test-requires * AuthN support for Quantum * fix bug lp:1007557,remove unused functions in utils.py * Add common dir for shared agent code, add OVS lib * Bug #1007153 * Register enable\_tunneling as bool opt * Quantum should use openstack.common.importutils * PEP8 fixes * Bug #1002605 * Automatically determine Quantum version from source * Fix linux bridge section name Bug #1006684 * Remove the reference to non existing exception by linuxbridgeplugin * bug #1006281 * Parse linuxbridge plugins using openstack.common.cfg * Bug #1004584 * fix some pylint warnings * fix errors in database test cases * Log the exception so app loading issues can be debuged * remove unneeded import from OVS agent that break 2.4 compat * blueprint man-support and fix documentation build bug 995283 * Fix print error for linux bridge bindings bug 1001941 * Add HACKING.rst to tarball generation bug 1001220 * fall back to \`ip link\` when \`ip tuntap\` unavailable bug 989868 * Cisco plugin CLI call to quantumclient CLI * Calling Super method from QuantumPortAwareScheduler.\_\_init\_\_ * OVS plugin: add tunnel ips to central database * Include AUTHORS in release package * blueprint database-common bug 995438 * bug 996163 * Bug #994758 * Change Resource.\_\_call\_\_() to not leak internal errors * Let OVSQuantumTunnelAgent sync with database * Cleaned up log usage * blueprint agent-db-ha bug 985470 bug 985646 * Update codebase for HACKING compliance * Make sample quantum.conf compliant with docs * Make ovs Interface option set properly * Removed simplejson from pip-requires * Remove dependency on python-quantumclient * Add sphinx to the test build deps * Add HACKING.rst coding style doc * return 404 for invalid api version request * fix issue with OVS plugin VLAN allocation after a quantum-server restart * bug 963152: add a few missing files to sdist tarball * API docs: fix typo for network delete * Open Folsom * Bug #956559 VIF driver and scheduler for UCS plugin are broken since the flag configuration mechanism in nova is changed. Fixing that and also fixing some property names, along changes to how the quantum client code is invoked * plugin/ryu/agent: unbreak a06b316cb47369ef4a2c522f5240fa3f7f529135 * Fix path to python-quantumclient * Split out pip requires and aligned tox file * ryu/nova: catch up d1888a3359345acffd8d0845c137eefd88072112 * Add root\_helper to quantum agents * Fix missing files in sdist package [bug 954906] * Fix for bug 921743 Response codes for create ops in API v1.0 not compliant with spec * bug 954538 Fix for the cisco unit tests * check connection in Listener. refer to Bug #943031 * fixed incorrect duplicate title * Fixed incorrect title for example 3.10 * Downgraded required version of WebOb to 1.0.8 * Bug #949261 Removing nova drivers for Linux Bridge Plugin * Remove outdated content from OVS plugin README, point to website instead * add git commit date / sha1 to sphinx html docs * more files missing in sdist tarball * make sure pip-requires is included in setup.py sdist * Introducing the tenant owenrship checks in the Cisco plugin, changes are almost identical to those in Bug#942713 * Fix some plugins that don't check that nets + ports are owned by tenant * remove pep8 and strict lxml version from setup.py * plugin: introduce ryu plugin * bug 934459: pip no longer supports -E * Fix bug 940732 stack.sh can't match sql\_connection string * Return appropriate error for invalid-port state in create port API * blueprint quantum-ovs-tunnel-agent * Initial commit: nvp plugin * unittests: setup FLAGS.state\_path properly: bug 938637 * Cleanup the source distribution * Fix ovs config file location * blueprint quantum-linux-bridge-plugin * Remove quantum CLI console script * Bug 925372: remove deprecated webob attributes (and also specify stable webob version in pip-requires) * bug 923510: avoid querying all ports for non-detail GET Network call * Make tox config work * Pin versions to standard versions * bp/api-filters This changeset implements filters for core Quantum API and provides unit tests * Split out quantum.client and quantum.common * Quantum was missing depend on lxml * bp/api-error-codes Restructured API error codes for Quantum API v1.1 This changeset provides the following changes: - Only standard HTTP errors for Quantum API v1.1 - Customized fault response body formatting according to API version - Changes to unit tests to deal with version specific status codes * blueprint ovs-portstats * Add support for dealing with 501 errors (notimplemented) * Improved VlanMap * moving batch config out of quantum-server repo * bug 920299: remove duplicate + outdate README * Getting ready for the client split * Removed erroneous print from setup.py * Fixes setup scripts for quantum plugins * Base version.py on glance * fix mysql port in sql\_connection example.. * Make the quantum top-level a namespace package * Add \_\_init\_\_.py from plugin to be copied on setup scripts * Fix lp bug 897882 * PEP8 quantum cleanup * Install a good version of pip in the venv * Rename .quantum-venv to .venv * Updating Cisco README with instructions on installing the patched ncclient library * Remove plugin pip-requires * blueprint refactor-readme-to-manual * Bug #890028 * Implementation of the BP services-insertion-wrapper inside the Cisco Plugin * blueprint operational-status-ovs-plugin * bug 903580: remove invalid extensions path from quantum.conf * Fix for bug 902175 * Readme Fix * blueprint api-framework-essex * Fix for bug 900277 * Fix for bug 900316 * Modified the Readme for Unit Test Execution Instructions * Bug 900093 Remove unused function in db/api.py * bug #891246: Fix paths in agent Makefile * Second round of packaging changes * Bug 891705 Fix to change reference to the Quantum CLI from within the Cisco extensions' CLI module * Correcting the plugins classpath in the Quantum README * The relative path for the "ucs\_inventory.ini" file has been fixed * bug #891267 : for XS, grab iface-id from XAPI directly if needed * Changes to make pip-based tests work with jenkins * Fix for bug 890498 * Fix for bug 888811 * Fixing find\_config\_file after packaging changes * Added timeout flag to ovs-vsctl to avoid infinte waiting * Add quantum.exceptions path to configed ext paths * Fix for Bug #888820 - pip-requires file support for plugins * Fixing Cisco plugin after update\_\* change * Fix for bug 888207 * Fix for bug 877525 * Bug #875995: Quantum README fixes * Change version numbers to be compatible with debian packaging * Make the openvswitch plugin tests work again * Swich over to update\_{net,port} instead of rename\_net and set\_port\_state * Added try import to quantum-server and quantum-cli * Bug 887706 * Blueprint authentication-for-quantum * blueprint quantum-packaging * Moved the initialization of the blade state so that the interfaces which are configured outside of Quantum are also initialized in the blade state * fix minor double-serialization bug in client.py * bug #863635: remove vestigial cheetah import from bin/cli * Change the ovs plugin create\_\*() calls to take the kwargs param * Changing the log messages in order to be always identified by their sub-packages of origin, and they can even be filtered on that basis * Add .gitreview config file for gerrit * New tests are being adding to the Diablo code (Cisco L2-Network plugin), and some fixes in the case where the tests were failing * Add the ability to specify multiple extension directories * Add code-coverage support to run\_tests.sh (lp860160) * Change port/net create calls to take an additional kwargs param * ovs plugin: Remove reference to set\_external\_ids.sh * fix pep8 issues in Cisco plugin * Remove hack for figuring out the vif interface identifier (lp859864) 2011.3 ------ * Update openvswitch plugin README * Update openvswitch plugin README * Get output from run\_tests * Add rfc.sh to help with gerrit workflow * merge tyler's unit tests for cisco plugin changes lp845140 * merge salv's no-cheetah CLI branch lp 842190 * Addressing Dan's comment on output generator * merge sumit's branch for lp837752 * merge salv's branch for bug834013 * merge salv's branch for keystone token on client bug838006 * merge rohit's db test branch: lp838318 * merge salv fix for bug 841982, fix minor pep8 violation * merge salv fix for bug834008 * Changes to address Salvatore's review comments, removed unnecessary imports, and changed a debug message * changing key names to confirm to api specs * Merging latest from lp:quantum * Merging lo:~salvatore-orlando/quantum/quantum-api-auth * Implementing Dan's suggestion concerning fixing the bug in db api rather than FakePlugin * Fixing bad indent * syncing diverged branches * merging from lp:quantum * merging from lp:quantum * Updating CLI for not using Cheetah anymore. Now using a mechanism based on Python built-in templates * Fixing the bug in FakePlugin * made general exception handling messages consistent removed LOG pylint errors cleanup in tests * Create operation now generate response with status code 202 * restoring correct default pipeline * Mergin from lp:quantum * Add information about quantum dependency for nova * merge salv's branch to remove dummy plugin * Changing communication between UCSM driver to UCSM to HTTPS * Adding CLI usage examlpes to the README * Adding client-side support for Keystone integration * Keystone-integrated pipeline should not be default in quantum.conf * Removing class DUmmyDataPlugin * Removed redundant configuration, and added more comments in the configuration files * Updating the README file * Merging Shweta's test cases for mutliport resource * Adding Multinic tests * Typo fix in README * Merging Sumit's changes including fixes for multinic support, and CLI module for working with extensions * More fixes for multi-nic support * Fixed a bug with plug\_interface * Merging from Cisco branch * Changes to incorporate earlier review comments, also for multiport resource * adding quantum database unit test cases * Merging changes from Ying's branch (new mutliport resource) * add multiport and exception handling * add multiport resource * Merging from lp:quantum * Avoiding deserializing body multiple times with several parameters * merge cisco consolidated plugin changes * Test on param\_value changes as follows: * Merging lp:~salvatore-orlando/quantum/bug834449 * Merging Ying's changes (minor) * fix print statements in novatenant and portprofile * merge trunk * Minor refactoring * Changes to l2network\_plugin for create\_ports and pylint fixes to cli.py * Modified CLI to handle both core and extensions CLI * merge trunk * lp835216 client lib was not passing in kwargs when creating exceptions * lp834694 fix integrity error when deleting network with unattached ports. Add unit test * Minor fix in delete\_port * merging changes from cisco consolidated branch * Fixes to support multinic * Merging fixes from Sumit's branch for extension API version number and to UCS inventory to associated VIF-ID with ports * Merging from the Cisco branch * adding new api methods using just port\_id * Fixing the extensions URL to 1.0 and pep8 error * bug fixes to handle multinic * Merging Shweta's fix for extensions' test cases (clean up was not happening completely) * Adding Network and Port clean up functions for portprofile unit tests * Merging from lp:quantum * Merging Shweta's fixes in the tests for key names changes in the Core API * make CLI show\_port command display interface-id, add additional test case * merge salvatore's new cli code * Dictionary key values changes in test\_extension * Merging lp:quantum, resolving conflict * merge two pep8 branch * Merging Ying's pep8 fixes * fix pep8 issues * Merging quantum trunk * fix pep8 warnings * Updating common/extensions.py in order not to instantiate a QuantumManager when retrieving plugin * Cleaning pep8 * Merging lp:~danwent/quantum/lp834491 Fixing Bug #834491: api alignment merge broke ovs plugin (Critical) * Addressing comments from Dan * Merging from quantum * merge cisco extensions branch * lp834491: change plugin to work with API code after the API alignment merge * Merging Shweta's fixes to the test cases for the extensions * Added Extension & ucs driver test changes and fixes * Merging from Sumit's branch, changes to VIF-driver and Scheduler; extension action names have been changed in response to Salvatore's review comments in the extensions branch review * Syncing with Cisco extensions branch * Merging changes from Sumit's branch * Changes qos description to string; changes extension API names for get\_host and get\_instance\_port * Mergin Ying's branch * change get\_host and get\_instance\_port function name * Cleaning (removing) unused code..hooray ! fixes for extension tests * Sorting correctly all imports for the Nexus Driver and Unit Test * Fixed the Unit Test for Nexus Driver * add cisco\_faults under l2network package * move faults/exceptions to l2network package, remove unecessary faults definitions change the portprofile action api's method fix imports order and other comments issues * Merging from Sumit's branch, import ordering related changes * Changing the order of imports (to satisfy convention) * Merging the Cisco branch * Updating README according to Somik's comment * Finishing cli work Fixing bug with XML deserialization * Completing Unit Tests * Merging lp:~salvatore-orlando/quantum/quantum-api-alignment * Configuration of multiple VLANs on the same Nexus Switch Interfaces * Adding unit test for rename\_network * Added logging to syslog or file specified at command line removed plugin direct mode fixed unit tests to reflect changes in cli code fixex pep8 errors * Merging from Sumit's branch * Fixed some bugs with credential and qos resources; also fixed l2network\_single\_blade * Merging Rohit's changes * helper function to get creds based on name * integration with l2network\_plugin.py * fixing relative import in nexus\_db.py * putting in db support for creds and qos * merge latest quantum branch and resolve conflicts * Merging lp:~asomya/quantum/lp833163 Fix for Bug #833163: Pep8 violations in recent packaging changes that were merged into trunk (Critical) * Addressing Somik's comment * Templated output for CLI completed! * PEP8 fixes for setup.py * delete quantum/common/test\_lib.py to prepare for quantum merge * Made changes according to reviewer's comments. Add addtional information on extension test in README * Merging changes from Sumit's branch * Merging lp:~cisco-openstack/quantum/802dot1qbh-vifdriver-scheduler * Merging lp:~cisco-openstack/quantum/l2network-plugin-persistence * Fixed a bug in the initialization of the UCS inventory; fixed another bug in deleting a port * Noticed some pep8 errors, fixed them * Merging lp:quantum * Changes to incorporate reviwer's comments. Also changed client.py to handle extension URLs * Review Changes * remove unnecessary code and sync faults and exception handling * Code changed base on Reviews pep8 passed pylint 9.10 * merging with lp:quantum * merging from lp:quantum * Fixes based on review comments * Addressing comments from Ziad and Somik * merge lp:~bgh/quantum/lp837174 * Fix unit test printing (lp837174) * Fixing issue in view builders concerning attachment identifiers * Code clean up as per reviewr's request; documentation strings, unused code, etc * Rewording of the README file to clarify the use of the SSh port * clean up code and fix some comments * clean code and fix some comments * Merging from Sumit's latest branch - Fixed loading of Nexus DB tables; moved imports to l2nework\_db.py; Refactoring of code to generalize inventory handling (enhancement) * Fixed loading of Nexus DB tables; moved imports to l2nework\_db.py, changes discussed & approved by Rohit * Making Keystone version configurable * Accidentally took quantum.conf out of branch. Now back in * Merging lp:~raxnetworking/quantum/bug827272 * Merging branch: lp:~danwent/quantum/test-refactor * Removing "excess" file * Missed adding a file earlier, fixed a small issue * Refactoring of code to generalize inventory handling (enhancement) * Merging UCS inventory state initialization fix from Sumit's branch * Fixes an issue with loading the UCS inventory when a dynamic nic has been used outside of Quantum * Removed obsolete instructions from README * Changes to reflect the new features (mutli-blade, multi-chassis support) * Changes to support calls from VIF Driver and Scheduler * Pep8, pylint fixes * fixing pep8 error * adding helper function for port binding model * UCS inventore persistence and pep8/pylint fixes * UCS persistence fixes * added new columns to models for ucs plugin multi blade support updated methods in ucs\_db for newly added columns changed column dynamic\_vnic\_id in port binding table to blade\_intf\_dn updated tests to handle new column name * Merging rohit's UCS persistence support * UCS plugin persistence * Persistence support for UCS plugin network * adding utility functions to create dictionaries * Merging changes from Rohit's branch * Merging changes from cisco extensions * added ucs plugin related execptions in cisco\_exceptions.py added ucs plugin persistence related modules - ucs\_models.py and ucs\_db.py added ucs db related unit tests in test\_database.py fixed formatting in l2network\_models.py and test\_database.py * Adding some error checks * Reduced excessive logging * Several fixes to initial version * fixing the the test\_database.py tests * pylint and pep8 fixes * Change profile-id * merged Shweta's branch for ext test. Minor fix for review comments * Review Changes * merged Shweta's ext test branch * Initial commit with lots of changes * Moved the conf file uncer the cisco directory * Moved the conf file uncer the cisco directory * Updated conf file * Adding Entension API unt tests * Syncing with lp:quantum * Code refactored, made changes are per reviwer's suggestions * sync up with l2network exception handling for extension * merged Cisco branch's latest changes * Adding changes from Sumit's latest merge * merge with lp:~cisco-openstack/quantum/l2network-plugin-extensions * replace exception handler by using cisco\_exceptions * Raising exceptions in extension resources handling (where missing). Changing exception name to QosNotFound * Changing exception name to QosNotFound * Mergin from Cisco branch * Raising exceptions in extension resources handling (where missing) * Merging fixes to client side exception handling. Thanks lp:tylesmit ! * Merging fixes and changes batch-config script. Thanks lp:danwent ! * Adding the Nexus support to the Persistence Framwork Modification of the Nexus Unit Case to be running with Persistence Framework pep8 passed pylint 8.81/10 * added nexus exception in cisco\_exceptions.py added log to methods in l2network\_db.py added nexus\_db.py and nexus\_models.py - persistence modules for nexus plugin * add plugins.ini back * add all conf/\*.ini back * merge with ying's branch * merging with Ying's extension branch * remove ying's test ciscoplugin * remove all configuration files * remove cisco\_demo and test\_scripts directory, which were used by our local tests * Removed concatenation per review comments * change the configuration files to the default values * pylint and pep8 fix * merging with ~cisco-openstack/quantum/l2network-plugin-extensions * fix pylint issuses * Making keystone integration optional in quantum configuration * Merging bug fix for Bug 821733. Thanks lp:salvatore-orlando ! * Fixing typo * Making the client raise the appropriate exception if needed. Also increasing the pylint score to above 8 * pep8 error fixed for l2network\_db.py * Mering Sumit's branch with plugin support for Credentials, QoS, NovaTenant resources. Also merging latest from lp:~cisco-openstack/quantum/l2network-plugin-persistence * Merging from Sumit's branch, VIF-driver and Quantum-aware scheduler * Removed extra spaces to satisfy pep8 * VIF driver for 802.1qbh and Quantum aware scheduler * fix some pylint issues * Pylint and pep8 fixes * Changes to support credentials, qos, and novatenant extensions * Removing unused error response codes * Merging lp:~asomya/quantum/lp824145 Fix for Bug#824145 : Adding a setup script for quantum * merge trunk pep8 fixes adapting CLI to API v1.0 Fixing wsgi to avoid failure with extensions * Fixed indentation and changed file comments * add extension change to ying's branch * merge trunk * Pulling in changes from lp:quantum * Merging Cisco's contribution to Quantum. Thanks to various folks at Cisco Systems, Quantum will have plugins to integrate with Cisco UCS blade servers using 802.1Qbh, Cisco Nexus family of switches and the ability for Quantum plugin to have multiple switches/devices within a single Quantum plugin * Merging Shweta's change to fix a function call in the test code * Adding the changed UCS Driver function names in test\_ucs\_driver * Santhosh/Deepak | Fixed an issue where collection actions for PUT and DELETE methods in resource extension were routing to update and delete action of the resource * Merging from Sumit's branch pylint fixes and incorporating review comments * Changes to README file and merging Shweta's changes * Mergin Shweta's test changes, also README file * Changes to test structure. Adding pylint correctons * Fixes to the README file per earlier review comments. Also removed main from one of the modules * Mergin from cisco brach * Merging from lp:quantum * Pulling changes from Cisco branch * Pylint fixes * exit unit tests if tests are invoked specifying a particular test * Merging Nexus pylint changes and other enhancements from Edgar * pep8 passed pylint 8.83 * Merging Rohit's changes * Partial commit * Moved test\_database.py to plugins/cisco/tests/unit/ Edited test\_database.py to be able to run like other tests pylint for cisco/db folder - 8.85/10 pylint for cisco/tests/unit/test\_database.py - 8.42/10 pep8 done * Adding a new file with all the XML snippets to make code easier to read Moving the Nexus SSH server port to the configuration file Removing main functions Making some changes based on Dan and Salvatore reviews * Changes in the README file to incorporate Somik's comments * pylint changes - pylint score for cisco/db folder - 8.27/10 pep8 checks done * Removing extra testing function on Nexus Driver * Merging plugin and tests' changes * Fixes to the tests which were breaking, including fixes to the test cases * Pulling in changes from Rohit's branch * Pulling in changes from Shweta's branch * Removed main from modules as per review comments * updated README file to include persistence framework setup instructions updated db api.py unset\_attachment method to return port moved db\_conn.ini into cisco/conf/ with other configuration files updated l2network\_plugin\_configuration.py to get db config cleaned up l2network\_db.py - removed config parser code as using cisco config parser updated l2network\_db.py to raise specific exceptions in error cases updated create\_vlanid method in l2network\_db.py to not raise exception if vlan rows exist updated portprofile and portprofile\_binding methods to include tenant\_id as an argument added cisco/db/test\_database.py containing unit tests for quantum and l2network\_plugin tables edited get\_pp\_binding method in l2network\_db.py to return empty list when no results found pep8 checks done * Adding Persistence unit test * Fixed bugs while testing * pep8 errors fixed * Merging rohit's changes * Changes to support persistence framework * Merging: lp:~danwent/quantum/client-lib * Merging: lp:~tylesmit/quantum/api-client-fix-serialization Adding automattic serialization to all requests by moving it to do\_request * First, trivial, implementation of authN+authZ * fixes from rohit's branch * from rohit's branch * Adding more templates More tests * - Added new tables VlanID to generate ids and maintain usage of vlans - Added wrapper functions to get next unused vlan, populate vlans, release vlans, getall vlans, isused van and delete van - Added ported instead of networked for portprofile binding table - Changed wrapper methods and test cases for portprofile binding to use portid * Adding missing files to branch * Simplifying condition * FIxing missing 'output' variable @ line 243 (syntax error) * Adding automattic serialization to all requests by moving it to do\_request * added network and port models similar to quantum with following changes - - InnoDB as storage engine to allow foreign key constraints - joinedLoad operation on the queries to make use of relation between Network and Port Moved out the network and port code to make l2network contain vlanbinding, portprofile and portprofile bindings * Authentication with Keystone. auth\_token Middleware tweaked and imported in Quantum tree Developing Authorization middleware * Introducting cheetah Updating list\_nets in CLI Writing unit tests for list\_nets Stubbing out with FakeConnection now * I'm too tired * Stubout work in progress * Merging quantum extenions framework into trunk. Thanks rajaram vinkesh, deepak & santhosh for the great work! * - added network and port models into the l2network plugin instead of using quantum models - added api methods for network and ports - restructured code to use the l2network network and port - added l2network base class for other tables to inherit - added support for l2network plugin model objects to behave like dictionary (gets rid of code to convert objects into dictionaries) - added foreign key constraints to l2network plugin model attributes representing columns - added attributes to represent relation between models in l2network plugin - added joinedload only to network and port (need to to for others) - added InnoDB as the storage medium in base table for imposing foreign keys - updated l2network test cases to handle foreign key constraints * lp Bug#824145 : Adding a setup script for quantum * skeleton for cli unit tests * merge trunk * Removing exceptions as well (previously only API faults were removed) * Merged quantum trunk * adding renamed client-lib tests * Tiny change to the README file, instructions on how to get ncclient * - Adding setup script * Adding db connection and l2network plugin database modules * update CLI to use show instead of list for calls that do not return a list * rename client\_lib unit tests so it is run by ./run\_tests.sh, update tests to handle name changes * force batch\_config.py to use json, as XML has issues (see bug: 798262) * update batch\_config.py to use new client lib, hooray for deleting code * Changed to default plugin class name * Rajaram/Vinkesh | Added examples of scoping extension alias in request and action extension * Added tests directory to list of modules in the README file * Added "tests" directory to the list modules in the README file * Adding the required build for Nexus support * Merging changes addressing Bug # 802772. Thanks lp:danwent ! * Merging bugfix for Bug 822890 - Added License file for Quantum code distribution * Fixed typo in README * README file updates (pointer to Nova Cactus branch), and numerous other edits based on Mark's template * L2 Network Plugin Framework merge * Incorporated changes in response to review comments from Ram * Adding Apache Version 2.0 license file. This is the official license agreement under which Quantum code is available to the Open Source community * Making a check for the presence of UCS/Nexus plugin (earlier it was not in certain cases). With this change, if the UCS/Nexus plugins are not enabled, the core API tests can be run even on Ubuntu (and RHEL without the requirement of any specific network hardware) * Merging test cases from Shwetas' branch, and further modified README file * Merging the test framework from Shweta's branch * decluttering \_parse\_request\_params method for QuantumController * Fixing detail action for port collection Adding PortIsDown exception Adding unit tests for detail actions and PortIsDown PEP8 FIXES * Adding Unit Test Cases Now * Adding Cisco Unit Tests * minor enhancements to quantum client-lib * RHEL limitation updated * Adding support for expressing format through Content-Type header Adding action detail for port resource (Member & Collection) * Changes to enhance L2 network plugin framework * undo unintentional formatting change in run\_tests.sh * remove unneeded \_\_init\_\_ * refactoring testing code to support plugin tests * Added QuantunPluginBase as the base class for the l2network\_plugin * Generalized and put placeholders * another merge * pep8 cleanup, restore defaults * Added info about ssh conf required for nexus switch * merge * remove unneeded tests from ovs\_quantum\_plugin * Nexus plugin classpath was incorrect, fixed it * Edits to reflect conf changes, made it easier to follow * merge heckj's pip-requires fixes * Fixed issue with creating new port profiles (one configuration parameter got left out during the migration to the new configuration scheme). Also fixed a bug in the calculation of the profile id * Fixes the broken call to second level of plugins. Renaming will work now * updates to pip-requires for CI * Loading of device-specific plugins and drivers is done dynamically by setting configuration. All configuration is driven through configuration files place in the conf directory. Each .ini conf file contains info on the configuration. README file updated to reflect all the changes. Fixed issue with delete\_network deleting the network even when attachments were present. Fixed issue with port id generation * Deepak/Vinkesh | Fixed show action in extension controller to return 404, added example to include namespace in a request extension * Merged quantum trunk * Santhosh/Vinkesh | Added extension\_stubs file * Removing extra file in Nexus Driver * Removing extra file in Nexus Driver * Relabelling API version to 1.0! * Cosmetic changes to unit tests for client library. Pep8 fixes * Removed quantum/plugins/cisco/db/ and quantum/cisco\_extensions since these will be merged separately * Adding conf directory for configuration files * Fixed pep8 error * Merging changes * Merging changes from lp:quantum * Fixed an issue selecting the right port interface and also properly switching off the Nexus Interface * Completing API spec alignment Unit tests aligned with changes in the API spec * Applying fix for bug #814518 Merging from lp:~salvatore-orlando/quantum/bug814518 * Adding controller and view builder for attachment resource * Merging the port profile client name fix * Earlier fix resulted in a different issue (profile client name, was also being used as profile name, hence breaking) * Truncated the port profile client name length to 16 characters (ucsm excepts max 17 chars) * Mergin fix for Bug 818321 * Merging approved OVS plugin configuration change branch. Thanks lp:danwent ! * Merging the brand new Quantum-client-library feature * Requests now send the Content-Type in the HTTP request * fix broken flush in db.network\_destroy, pep8 fixes * req/res alignment complete. Status code alignment ALMOST complete (need to sort out 200 vs 202 for create ops) * Vinkesh | Changed import orders according to pep8 recommendations * Including a flag to activate the NX-OS driver Updating the README documentation * merging branch for bug802772, which this branch is stacked on top of * WIP. Still need to align APIs for interface plug/unplug * Fixing pep8 errors * Adding the Nexus OS driver based on the new PlugIn structure * fix incorrect handling of duplicate network name, add exception for duplicate network name, and add unit test to confirm detection * WIP * Merging lp:quantum updates * Fixing syntax issue. I had a 2.7+ style dict comprehension, so I made it 2.6 friendly * Removing a debugging line * pep8 fix * Fixing API behaviour for throwing 400 error on invalid body. Adding unit test for creating a port without request body * make ovs plugin pay attention to port state * persistence of l2network & ucs plugins using mysql - db\_conn.ini - configuration details of making a connection to the database - db\_test\_plugin.py - contains abstraction methods for storing database values in a dict and unit test cases for DB testing - l2network\_db.py - db methods for l2network models - l2network\_models.py - class definitions for the l2 network tables - ucs\_db.py - db methods for ucs models - ucs\_models.py - class definition for the ucs tables dynamic loading of the 2nd layer plugin db's based on passed arguments Create, Delete, Get, Getall, Update database methods at - Quantum, L2Network and Ucs Unit test cases for create, delete, getall and update operations for L2Network and Ucs plugins pep8 checks done branch based off revision 34 plugin-framework * Vinkesh/Santhosh | Moved the stub classes in test\_extensions to a separate file extension\_stubs * Merged from trunk * bug802772 update exception handling in OVS plugin to use API exceptions * merged the latest changes from plugin-framework branch - revision 39 conforming to the new cisco plugin directory structure and moving all db related modules into cisco/db folder updated db\_test\_plugin.py - added import of cisco constants module - added LOG.getLogger for logging component name - updated import module paths for l2network\_models/db and ucs\_models/db to use the new directory structure - updated (rearranged) imports section to obey openstack alphabetical placement convention updated db\_conn.ini - updated database name from cisco\_naas to quantum\_l2network unit test cases ran successfully and pep8 checks done again * removing a few additional lines that aren't needed once we don't calculate port count * Adding a tests directory, this can be used for plugin-specific test cases * also remove line that computes portcount, as it is unneeded now that we don't return it * Including copyright info * merge branch for to fix bug817826 * For the modules to get added, missed in the earlier checkin * remove PortCount attribute of network object, as it is not in the spec and was causing us to hit bug 818321 (note: this commit does not fix the underlyingproblem with xml deserialization, it just makes sure we don't hit it with the existing API code) * Changed the directory structure to a more organized one. Fixed the imports to reflect the new structure * Merging the latest changes from lp:quantum * change default integration bridge from br100 to br-int to reflect new default for OVS vif-plugging in nova Diablo-3 release * fix bug 817826 and similar error in batch\_config.py * persistence of l2network & ucs plugins using mysql - db\_conn.ini - configuration details of making a connection to the database - db\_test\_plugin.py - contains abstraction methods for storing database values in a dict and unit test cases for DB testing - l2network\_db.py - db methods for l2network models - l2network\_models.py - class definitions for the l2 network tables - ucs\_db.py - db methods for ucs models - ucs\_models.py - class definition for the ucs tables dynamic loading of the 2nd layer plugin db's based on passed arguments Create, Delete, Get, Getall, Update database methods at - Quantum, L2Network and Ucs Unit test cases for create, delete, getall and update operations for L2Network and Ucs plugins pep8 checks done branch based off revision 34 plugin-framework * merge Salvatore's api branch with fixes for tests. Tweaking branch to remove unwanted bin/quantum.py as part of merge * Merging in main repo updates * Updating to fix some SSL issues * Removing extra quantum.py file from source control removing unused import from quantum/api/\_\_init\_\_.py * Apply fix for bug #817813 Merging lp:~danwent/quantum/bug817813 * Apply fix for bug #814012 Merging lp:~danwent/quantum/bug814012 * Apply fix for bug #814517 merging lp:~tylesmit/quantum/quantum-bug-814517 * bug 817813: default provider in plugins.ini accidentally changed. Changing it back to FakePlugin * Changed the param name "network-name" to "net-name" since the Quantum service expects the later * Removing some legacy code from the unit tests * Adding unit tests to cover the client library * Changing the CLI to use the new client library * Adding refactored API Client * pep8 fixes * fix bug 814012, add unit tests for it * Resolving Bug 814517 which caused XML to have extra whitespace * Vinkesh/Santhosh | Removed loading extensions from 'contrib' and fixed an indentation bug while loading extensions * Santhosh/Rajaram|modified extensions section in README * Rajaram/Santhosh | Added logging to the PluginAwareExtensionManager failures * Rajaram/Santhosh|Added plugin interface in foxinsox and Updated README * Rajaram/Santhosh|quantum manager loads plugin only once, even though both extension middleware and APIRouter calls it * Santhosh/Rajaram|latest merge from quantum and made extensions use options to load plugin * Apply fix for bug #797419 merging lp:~salvatore-orlando/quantum/bug797419 * Re-fixing issues with XML deserialization (changes got lost in merges with trunk) Adapting assertions in unit tests merged from trunk to reflect changes in the API due to RFE requested by Erik Carlin * Rajaram/Vinkesh | Plugins advertise which extensions it supports * Merging branch lp:~salvatore-orlando/quantum/bug802892 Fixing bug #802892 * Merging branch lp:~netstack/quantum/quantum-unit-tests * Fixing silly pep8 error * doh * Restoring quantum\_plugin\_base to previous state. Will discuss in the future whether allow API layer to pass options to plugins upon initialization * Vinkesh/Santhosh | Added tests to check the member and collection custom actions of ResourceExtensions * Vinkesh/Deepak | Moved plugin related checks in ExtensionManager code to PluginAwareExtensionManager * Deepak/Vinkesh | Added an base abstract class which can be inherited by PluginInterface class which defines the contract expected by extension * Vinkesh/Deepak| Added doc and small refactoring * Unit tests for API completed fixed pep8 errors * Add TESTING document: description and polices for quantum tests * Adding more unit tests * Deepak/Santhosh | ExtensionManager verifies that plugin implements the interface expected by the extension * Santhosh/Deepak | Made supports\_extension method optional for plugin, plugin will be loaded only once * Merged from quantum trunk * Santhosh/deepak| Load extensions supported by plugin * add extension code in.(last push does not include this directory.) * add api extensions (including portprofiles resources and associate/disassociate actions.) * Changes to support port-profile extension. Fixed an error in the README file * Very initial version of the nxos driver .... lets call it ver 0.0.1! * Removing code related to functional tests * Porting shell script get-vif.sh to python module get-vif.py for cisco ucsm module * Required for recognizing the "cisco" package. Missed in the initial checkin * Applying fix for bug #804237 from branch lp:~salvatore-orlando/quantum/bug804237 * minor pep8 fix * Changed some credentials (does not affect functionality) * This file is not required * Initial checkin for the L2-Network Plugin with all the associated modules and artifacts * Rajaram/Santosh|misc readablity improvements to extension tests * Santosh/Rajaram| added extenstion test to show header extensibility * Rajaram/Vinkesh | Added tests to confirm extensions can edit previously uneditable field * removing pep8 errors * Added more unit tests for API Starting work on functional tests, importing code from Glance * Now REALLY using in-memory db * Adapated plugin infrastructure to allow API to pass options to plugins Now using in-memory sqlite db for tests on FakePlugin teardown() now 'resets' the in-memory db Adding unit tests for APIs * Fixing error introduced in find\_config * Removing excess debug line * Fixing syntax errors in db/models.py * Temporary commit * Now loading plugin before setting up routes. Passing same plugin instance to API controllers * Adding unit test Applying pep8 fixes * Starting implementation of unit tests Fixing minor bugs with FakePlugin * Removing static data for FakePlugin * - Unit tests will use FakePlugin - FakePlugin adapted to db API with sqlite - db Models updated to inherit from generic Quantum Base model (provides utility functions and capabilities for treating db objects as dicts - see nova.db.models.NovaBase) - functional tests commented out temporarily. Will un-comment when code for starting actual service is in place * Adding Routes>=1.12.3 to tools/pip-requires * Work in progress - just starting * ...and again! * I hope I get the commit right now * removing "quantum" folder as well from etc * removing api-paste.ini * Addressing comments from Somik * Merging dan wendlandt's bugfixes for Bug #800466 and improvements that enable Quantum to seamlessly run on KVM! * fix pep8 introduced by trunk merge * A small start on unit tests: mostly a proof of concept that contains a test for api/ports.py * Added some more plugin agnostic tests (attachment and negative tests) and some pep8 fixes * merge * more pep8 goodness * Fixing bug #798262 * refactor batch\_config, allow multiple attaches with the empty string * Merge: bzr merge lp:~bgh/quantum/bugfixes * Fix cut and paste error in api\_unplug\_iface * Fixing bug #798261 * no-commit * Santhosh/Vinkesh | Added extensions framework * merge and pep8 cleanup * Merging latest changes from parent repo - lp:network-service , Parent repo had approved merge proposal for merging lp:~santhom/network-service/quantum\_testing\_framework , which has now been merged into lp:network-service * Merging pep8 and functional test related changes lp:~santhom/network-service/quantum\_testing\_framework branch * add example to usage string for batch\_config.py * Bug fixes and clean-up, including supporting libvirt * Fix typo in mysql package check * Fix typo in mysql package check * Adding support for 'detail' action on networks objects * README fixes * Santhosh/Deepak | Fixed the import issue and config.load\_paste\_app issue * Santhosh/Vinkesh | Fixed all the pep8 violations. Modified the 'req' to 'request' across all the services and wsgi so that it's consistent with other projects * Santhosh/Vinkesh | Added the testing framework. Moved the smoketest to tests/functional * merged remote README changes * Fix cli.py from last merge when it got overwritten * Fixing pep8 errors removing excess debug lines * Add dependencies to README and fix whitespace * Fix merge indentation errors * Merged Brad's ovsplugin code * pep8 changes for quantum-framework code pieces * Update Quantum README file with instructions to launch the service and get going * Updated quantum\_plugin\_base with with return type dataformats as well as exceptions * Added a basic README file and updated Quantum plugin base class with appropriate exceptions * Initial commit of exceptions that are raised by a quantum plugin * Make the wording a little clearer * Remove -a option from examples (it no longer exists) * Make the API the default * Address Dan's review comments * Make the manager a little smarter about finding its config file * Fix another TODO: remove main function from manager * Fix detail\_net and list\_ports commands * Remove get\_all\_interfaces and fix detail\_network commands * Initial version of openvswitch plugin * \* Merged changes from Salvatore's branch - quantum-api-workinprogress \* Removed spurious methods from quantum\_base\_plugin class. \* Updated the sample plugins to be compliant with the new QuantumBase class * Update readme with quantum specific instructions * Address some of the remaining TODOs and general cleanup * Add headers * Initial cut of openvswitch plugin * Add database models/functions for ports and networks * Print the command list in the help * Whitespace fixes * Added api functions for the interface commands * Initial rework of cli to use the WS api * Copy over miniclient from testscripts and port tests.py to use unittest * Adding ports.py to source control * pep8 fixes (1st batch) * First working version of Quantum API * Adding views/networks.py to bzr * Adding serialization/deserilization for network resources. Adding fake plugin * networks api with final URL structure. No serialization yet * Implementing interface with plugin * adpating wsgi files * Work in progress on network API * Adding first files for quantum API * Minor fixes: indentation in bin/quantum and fix import in config.py * Adding api paste configuration file * Removing .pydevproject from version control * Branching from quantum-framework * Adding flags.py to infrastructure code * Move plugin configuration to plugins.ini - a config file * 1) Created a DummDataPlugin in SamplePlugin module * merged salvatore's changes to local branch * 1) Added a bare-bones framework for quantum plugins. 2) Created demo quantum plugin that conforms to QuantumPluginBase Abstract class specification. 3) Demonstrated plugin registration and invocation using the demo plugin called "QuantumEchoPlugin" 4) Created the initial file structure for a quantum CLI 5) Seeded the utils module that will contain frequently used Quantum utilities. 6) Modified the manager module to initialize and register the quantum plugin defined in a configuration file. I have hard-coded the path to plugin for now but this will move to a quantum.conf file * Fixing pep8 errors * adding /bzrignore to precent checking in pyc files and that sort of stuff.. * Pushing initial started code based on Glance project and infrstructure work done by the melange team * Merging in Shweta's fixes from the review by Sumit * Minor Fix in ucs tests * Fixing issues discussed in merge prop. The UCS Inventory clears the DB on teardown. The multiblade tests now check to see if a port exists in the db before deleting it. It checks to make sure the UCSInventory is set in the config * Adding UCS inventory tests * Merging in latest changes from lp:quantum * Merging in Shweta's test changes * Ading Ucs db tests * Removing excess imports * Fixing pep8 errors and pushing pylint score up to 8.57 * Fix for bug/893663 Making Cisco CLI usable from installed packages * Bug 903684: functions defined twice in utils.py * blueprint api-operational-status * Adds sqlalchemy support for ovs\_quantum\_plugin * bug 903581: remove etc/quantum.conf.sample as it is invalid * Fixing bug/903829 Making setup\_server.py not try to install quantum.conf.sample * Removing a couple extra lines * Adding some tests, fixing some bugs, and making the tearDown correctly remove PortProfiles * Adding author information * Removing a negative test until I can figure out how to implement it * Removing some negative tests until I can figure out how to implement them * Updating tests * Fixing port-related calls * Adding tests * Tweaking other multiblade tests * Updating multiblade create\_network test * Starting making multi\_blade model return data * Adding initial multi blade test file from Shubhangi neutron-12.1.1/neutron/0000775000175000017500000000000013553660156015014 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/0000775000175000017500000000000013553660156016637 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/segments/0000775000175000017500000000000013553660156020464 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/segments/db.py0000664000175000017500000003462013553660047021427 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development, LP # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from oslo_concurrency import lockutils from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import helpers as log_helpers from oslo_utils import uuidutils from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.db import common_db_mixin from neutron.db import segments_db as db from neutron.extensions import segment as extension from neutron import manager from neutron.objects import base as base_obj from neutron.objects import network from neutron.services.segments import exceptions _USER_CONFIGURED_SEGMENT_PLUGIN = None def check_user_configured_segment_plugin(): global _USER_CONFIGURED_SEGMENT_PLUGIN # _USER_CONFIGURED_SEGMENT_PLUGIN will contain 3 possible values: # 1. None, this just happens during neutron-server startup. # 2. True, this means that users configure the 'segments' # service plugin in neutron config file. # 3. False, this means that can not find 'segments' service # plugin in neutron config file. # This function just load once to store the result # into _USER_CONFIGURED_SEGMENT_PLUGIN during neutron-server startup. if _USER_CONFIGURED_SEGMENT_PLUGIN is None: segment_class = 'neutron.services.segments.plugin.Plugin' _USER_CONFIGURED_SEGMENT_PLUGIN = any( p in cfg.CONF.service_plugins for p in ['segments', segment_class]) return _USER_CONFIGURED_SEGMENT_PLUGIN class SegmentDbMixin(common_db_mixin.CommonDbMixin): """Mixin class to add segment.""" @staticmethod def _make_segment_dict(segment_obj, fields=None): res = {'id': segment_obj['id'], 'network_id': segment_obj['network_id'], 'name': segment_obj['name'], 'description': segment_obj['description'], db.PHYSICAL_NETWORK: segment_obj[db.PHYSICAL_NETWORK], db.NETWORK_TYPE: segment_obj[db.NETWORK_TYPE], db.SEGMENTATION_ID: segment_obj[db.SEGMENTATION_ID], 'hosts': segment_obj['hosts'], 'segment_index': segment_obj['segment_index']} return db_utils.resource_fields(res, fields) def _get_segment(self, context, segment_id): segment = network.NetworkSegment.get_object(context, id=segment_id) if not segment: raise exceptions.SegmentNotFound(segment_id=segment_id) return segment @log_helpers.log_method_call def create_segment(self, context, segment): """Create a segment.""" segment = segment['segment'] segment_id = segment.get('id') or uuidutils.generate_uuid() try: new_segment = self._create_segment_db(context, segment_id, segment) except db_exc.DBReferenceError: raise n_exc.NetworkNotFound(net_id=segment['network_id']) registry.notify(resources.SEGMENT, events.AFTER_CREATE, self, context=context, segment=new_segment) return self._make_segment_dict(new_segment) def _create_segment_db(self, context, segment_id, segment): with db_api.context_manager.writer.using(context): network_id = segment['network_id'] physical_network = segment[extension.PHYSICAL_NETWORK] if physical_network == constants.ATTR_NOT_SPECIFIED: physical_network = None network_type = segment[extension.NETWORK_TYPE] segmentation_id = segment[extension.SEGMENTATION_ID] if segmentation_id == constants.ATTR_NOT_SPECIFIED: segmentation_id = None name = segment['name'] if name == constants.ATTR_NOT_SPECIFIED: name = None description = segment['description'] if description == constants.ATTR_NOT_SPECIFIED: description = None args = {'id': segment_id, 'network_id': network_id, 'name': name, 'description': description, db.PHYSICAL_NETWORK: physical_network, db.NETWORK_TYPE: network_type, db.SEGMENTATION_ID: segmentation_id} # Calculate the index of segment segment_index = 0 segments = self.get_segments( context, filters={'network_id': [network_id]}, fields=['segment_index'], sorts=[('segment_index', True)]) if segments: # NOTE(xiaohhui): The new index is the last index + 1, this # may cause discontinuous segment_index. But segment_index # can functionally work as the order index for segments. segment_index = (segments[-1].get('segment_index') + 1) args['segment_index'] = segment_index new_segment = network.NetworkSegment(context, **args) new_segment.create() # Do some preliminary operations before committing the segment to # db registry.notify( resources.SEGMENT, events.PRECOMMIT_CREATE, self, context=context, segment=new_segment) # The new segment might have been updated by the callbacks # subscribed to the PRECOMMIT_CREATE event. So update it in the DB new_segment.update() return new_segment @log_helpers.log_method_call def update_segment(self, context, uuid, segment): """Update an existing segment.""" segment = segment['segment'] with db_api.context_manager.writer.using(context): curr_segment = self._get_segment(context, uuid) curr_segment.update_fields(segment) curr_segment.update() return self._make_segment_dict(curr_segment) @log_helpers.log_method_call def get_segment(self, context, uuid, fields=None): segment_db = self._get_segment(context, uuid) return self._make_segment_dict(segment_db, fields) @log_helpers.log_method_call def get_segments(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): filters = filters or {} pager = base_obj.Pager(sorts, limit, page_reverse, marker) segment_objs = network.NetworkSegment.get_objects( context, _pager=pager, **filters) return [self._make_segment_dict(obj) for obj in segment_objs] @log_helpers.log_method_call def get_segments_count(self, context, filters=None): filters = filters or {} return network.NetworkSegment.count(context, **filters) @log_helpers.log_method_call def get_segments_by_hosts(self, context, hosts): if not hosts: return [] segment_host_mapping = network.SegmentHostMapping.get_objects( context, host=hosts) return list({mapping.segment_id for mapping in segment_host_mapping}) @log_helpers.log_method_call def delete_segment(self, context, uuid, for_net_delete=False): """Delete an existing segment.""" segment_dict = self.get_segment(context, uuid) # Do some preliminary operations before deleting the segment registry.notify(resources.SEGMENT, events.BEFORE_DELETE, self.delete_segment, context=context, segment=segment_dict, for_net_delete=for_net_delete) # Delete segment in DB with db_api.context_manager.writer.using(context): if not network.NetworkSegment.delete_objects(context, id=uuid): raise exceptions.SegmentNotFound(segment_id=uuid) # Do some preliminary operations before deleting segment in db registry.notify(resources.SEGMENT, events.PRECOMMIT_DELETE, self.delete_segment, context=context, segment=segment_dict) registry.notify(resources.SEGMENT, events.AFTER_DELETE, self.delete_segment, context=context, segment=segment_dict) @db_api.retry_if_session_inactive() @lockutils.synchronized('update_segment_host_mapping') def update_segment_host_mapping(context, host, current_segment_ids): with db_api.context_manager.writer.using(context): segment_host_mapping = network.SegmentHostMapping.get_objects( context, host=host) previous_segment_ids = { seg_host['segment_id'] for seg_host in segment_host_mapping} for segment_id in current_segment_ids - previous_segment_ids: network.SegmentHostMapping( context, segment_id=segment_id, host=host).create() stale_segment_ids = previous_segment_ids - current_segment_ids if stale_segment_ids: for entry in segment_host_mapping: if entry.segment_id in stale_segment_ids: entry.delete() def get_hosts_mapped_with_segments(context): """Get hosts that are mapped with segments. L2 providers can use this method to get an overview of SegmentHostMapping, and then delete the stale SegmentHostMapping. """ segment_host_mapping = network.SegmentHostMapping.get_objects(context) return {row.host for row in segment_host_mapping} def _get_phys_nets(agent): configurations_dict = agent.get('configurations', {}) mappings = configurations_dict.get('bridge_mappings', {}) mappings.update(configurations_dict.get('interface_mappings', {})) mappings.update(configurations_dict.get('device_mappings', {})) return list(mappings.keys()) reported_hosts = set() # NOTE: Module level variable of segments plugin. It should be removed once # segments becomes a default plugin. segments_plugin = None def get_segments_with_phys_nets(context, phys_nets): """Get segments from physical networks. L2 providers usually have information of hostname and physical networks. They could use this method to get related segments and then update SegmentHostMapping. """ phys_nets = list(phys_nets) if not phys_nets: return [] with db_api.context_manager.reader.using(context): return network.NetworkSegment.get_objects( context, physical_network=phys_nets) def map_segment_to_hosts(context, segment_id, hosts): """Map segment to a collection of hosts.""" with db_api.context_manager.writer.using(context): for host in hosts: network.SegmentHostMapping( context, segment_id=segment_id, host=host).create() def _update_segment_host_mapping_for_agent(resource, event, trigger, context, host, plugin, agent): check_segment_for_agent = getattr(plugin, 'check_segment_for_agent', None) if (not check_user_configured_segment_plugin() or not check_segment_for_agent): return phys_nets = _get_phys_nets(agent) if not phys_nets: return start_flag = agent.get('start_flag', None) if host in reported_hosts and not start_flag: return reported_hosts.add(host) segments = get_segments_with_phys_nets(context, phys_nets) current_segment_ids = { segment['id'] for segment in segments if check_segment_for_agent(segment, agent)} update_segment_host_mapping(context, host, current_segment_ids) registry.notify(resources.SEGMENT_HOST_MAPPING, events.AFTER_CREATE, plugin, context=context, host=host, current_segment_ids=current_segment_ids) def _add_segment_host_mapping_for_segment(resource, event, trigger, context, segment): if not context.session.is_active: # The session might be in partial rollback state, due to errors in # peer callback. In that case, there is no need to add the mapping. # Just return here. return if not segment.physical_network: return cp = directory.get_plugin() check_segment_for_agent = getattr(cp, 'check_segment_for_agent', None) if not check_user_configured_segment_plugin() or not hasattr( cp, 'get_agents') or not check_segment_for_agent: # not an agent-supporting plugin registry.unsubscribe(_add_segment_host_mapping_for_segment, resources.SEGMENT, events.PRECOMMIT_CREATE) return hosts = {agent['host'] for agent in cp.get_agents(context) if check_segment_for_agent(segment, agent)} map_segment_to_hosts(context, segment.id, hosts) def _delete_segments_for_network(resource, event, trigger, context, network_id): admin_ctx = context.elevated() global segments_plugin if not segments_plugin: segments_plugin = manager.NeutronManager.load_class_for_provider( 'neutron.service_plugins', 'segments')() segments = segments_plugin.get_segments( admin_ctx, filters={'network_id': [network_id]}) for segment in segments: segments_plugin.delete_segment(admin_ctx, segment['id'], for_net_delete=True) def subscribe(): registry.subscribe(_update_segment_host_mapping_for_agent, resources.AGENT, events.AFTER_CREATE) registry.subscribe(_update_segment_host_mapping_for_agent, resources.AGENT, events.AFTER_UPDATE) registry.subscribe(_add_segment_host_mapping_for_segment, resources.SEGMENT, events.PRECOMMIT_CREATE) registry.subscribe(_delete_segments_for_network, resources.NETWORK, events.PRECOMMIT_DELETE) subscribe() neutron-12.1.1/neutron/services/segments/exceptions.py0000664000175000017500000000435113553660047023221 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development, LP # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron._i18n import _ from neutron_lib import exceptions class SegmentNotFound(exceptions.NotFound): message = _("Segment %(segment_id)s could not be found.") class SubnetsNotAllAssociatedWithSegments(exceptions.BadRequest): message = _("All of the subnets on network '%(network_id)s' must either " "all be associated with segments or all not associated with " "any segment.") class SubnetCantAssociateToDynamicSegment(exceptions.BadRequest): message = _("A subnet cannot be associated with a dynamic segment.") class NetworkIdsDontMatch(exceptions.BadRequest): message = _("The subnet's network id, '%(subnet_network)s', doesn't match " "the network_id of segment '%(segment_id)s'") class HostConnectedToMultipleSegments(exceptions.Conflict): message = _("Host %(host)s is connected to multiple segments on routed " "provider network '%(network_id)s'. It should be connected " "to one.") class HostNotConnectedToAnySegment(exceptions.Conflict): message = _("Host %(host)s is not connected to any segments on routed " "provider network '%(network_id)s'. It should be connected " "to one.") class HostNotCompatibleWithFixedIps(exceptions.Conflict): message = _("Host %(host)s is not connected to a segment where the " "existing fixed_ips on port %(port_id)s will function given " "the routed network topology.") class SegmentInUse(exceptions.InUse): message = _("Segment '%(segment_id)s' cannot be deleted: %(reason)s.") neutron-12.1.1/neutron/services/segments/plugin.py0000664000175000017500000004311313553660047022335 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development, LP # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneauth1 import loading as ks_loading import netaddr from neutron_lib.api.definitions import ip_allocation as ipalloc_apidef from neutron_lib.api.definitions import l2_adjacency as l2adj_apidef from neutron_lib.api.definitions import network as net_def from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import subnet as subnet_def from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib.plugins import directory from novaclient import client as nova_client from novaclient import exceptions as nova_exc from oslo_config import cfg from oslo_log import log from neutron._i18n import _ from neutron.common import exceptions as n_exc from neutron.db import _resource_extend as resource_extend from neutron.db import api as db_api from neutron.db.models import segment as segment_model from neutron.db import models_v2 from neutron.extensions import segment from neutron.notifiers import batch_notifier from neutron.services.segments import db from neutron.services.segments import exceptions from neutron.services.segments import placement_client LOG = log.getLogger(__name__) NOVA_API_VERSION = '2.41' IPV4_RESOURCE_CLASS = 'IPV4_ADDRESS' SEGMENT_NAME_STUB = 'Neutron segment id %s' MAX_INVENTORY_UPDATE_RETRIES = 10 @resource_extend.has_resource_extenders @registry.has_registry_receivers class Plugin(db.SegmentDbMixin, segment.SegmentPluginBase): _instance = None supported_extension_aliases = ["segment", "ip_allocation", l2adj_apidef.ALIAS] def __init__(self): self.nova_updater = NovaSegmentNotifier() @staticmethod @resource_extend.extends([net_def.COLLECTION_NAME]) def _extend_network_dict_binding(network_res, network_db): if not directory.get_plugin('segments'): return # TODO(carl_baldwin) Make this work with service subnets when # it's a thing. is_adjacent = (not network_db.subnets or not network_db.subnets[0].segment_id) network_res[l2adj_apidef.L2_ADJACENCY] = is_adjacent @staticmethod @resource_extend.extends([subnet_def.COLLECTION_NAME]) def _extend_subnet_dict_binding(subnet_res, subnet_db): subnet_res['segment_id'] = subnet_db.get('segment_id') @staticmethod @resource_extend.extends([port_def.COLLECTION_NAME]) def _extend_port_dict_binding(port_res, port_db): if not directory.get_plugin('segments'): return value = ipalloc_apidef.IP_ALLOCATION_IMMEDIATE if port_db.get('ip_allocation'): value = port_db.get('ip_allocation') port_res[ipalloc_apidef.IP_ALLOCATION] = value @classmethod def get_instance(cls): if cls._instance is None: cls._instance = cls() return cls._instance @registry.receives(resources.SEGMENT, [events.BEFORE_DELETE]) def _prevent_segment_delete_with_subnet_associated( self, resource, event, trigger, context, segment, for_net_delete=False): """Raise exception if there are any subnets associated with segment.""" if for_net_delete: # don't check if this is a part of a network delete operation return with db_api.context_manager.reader.using(context): segment_id = segment['id'] query = context.session.query(models_v2.Subnet.id) query = query.filter(models_v2.Subnet.segment_id == segment_id) subnet_ids = [s[0] for s in query] if subnet_ids: reason = _("The segment is still associated with subnet(s) " "%s") % ", ".join(subnet_ids) raise exceptions.SegmentInUse(segment_id=segment_id, reason=reason) class Event(object): def __init__(self, method, segment_ids, total=None, reserved=None, segment_host_mappings=None, host=None): self.method = method if isinstance(segment_ids, set): self.segment_ids = segment_ids else: self.segment_id = segment_ids self.total = total self.reserved = reserved self.segment_host_mappings = segment_host_mappings self.host = host @registry.has_registry_receivers class NovaSegmentNotifier(object): def __init__(self): self.p_client, self.n_client = self._get_clients() self.batch_notifier = batch_notifier.BatchNotifier( cfg.CONF.send_events_interval, self._send_notifications) def _get_clients(self): p_client = placement_client.PlacementAPIClient() n_auth = ks_loading.load_auth_from_conf_options(cfg.CONF, 'nova') n_session = ks_loading.load_session_from_conf_options( cfg.CONF, 'nova', auth=n_auth) extensions = [ ext for ext in nova_client.discover_extensions(NOVA_API_VERSION) if ext.name == "server_external_events"] n_client = nova_client.Client( NOVA_API_VERSION, session=n_session, region_name=cfg.CONF.nova.region_name, endpoint_type=cfg.CONF.nova.endpoint_type, extensions=extensions) return p_client, n_client def _send_notifications(self, batched_events): for event in batched_events: try: event.method(event) except n_exc.PlacementEndpointNotFound: LOG.debug('Placement API was not found when trying to ' 'update routed networks IPv4 inventories') return @registry.receives(resources.SUBNET, [events.AFTER_CREATE]) def _notify_subnet_created(self, resource, event, trigger, context, subnet, **kwargs): segment_id = subnet.get('segment_id') if not segment_id or subnet['ip_version'] != constants.IP_VERSION_4: return total, reserved = self._calculate_inventory_total_and_reserved(subnet) if total: query = ( context.session.query(segment_model.SegmentHostMapping). filter_by(segment_id=segment_id) ) self.batch_notifier.queue_event(Event( self._create_or_update_nova_inventory, segment_id, total=total, reserved=reserved, segment_host_mappings=query.all())) def _create_or_update_nova_inventory(self, event): try: self._update_nova_inventory(event) except n_exc.PlacementResourceProviderNotFound: self._create_nova_inventory(event.segment_id, event.total, event.reserved, event.segment_host_mappings) def _update_nova_inventory(self, event): for count in range(MAX_INVENTORY_UPDATE_RETRIES): ipv4_inventory = self.p_client.get_inventory(event.segment_id, IPV4_RESOURCE_CLASS) if event.total: ipv4_inventory['total'] += event.total if event.reserved: ipv4_inventory['reserved'] += event.reserved try: self.p_client.update_inventory(event.segment_id, ipv4_inventory, IPV4_RESOURCE_CLASS) return except n_exc.PlacementInventoryUpdateConflict: LOG.debug('Re-trying to update Nova IPv4 inventory for ' 'routed network segment: %s', event.segment_id) LOG.error('Failed to update Nova IPv4 inventory for routed ' 'network segment: %s', event.segment_id) def _create_nova_inventory(self, segment_id, total, reserved, segment_host_mappings): name = SEGMENT_NAME_STUB % segment_id resource_provider = {'name': name, 'uuid': segment_id} self.p_client.create_resource_provider(resource_provider) aggregate = self.n_client.aggregates.create(name, None) self.p_client.associate_aggregates(segment_id, [aggregate.uuid]) for mapping in segment_host_mappings: self.n_client.aggregates.add_host(aggregate.id, mapping['host']) ipv4_inventory = {'total': total, 'reserved': reserved, 'min_unit': 1, 'max_unit': 1, 'step_size': 1, 'allocation_ratio': 1.0, 'resource_class': IPV4_RESOURCE_CLASS} self.p_client.create_inventory(segment_id, ipv4_inventory) def _calculate_inventory_total_and_reserved(self, subnet): total = 0 reserved = 0 allocation_pools = subnet.get('allocation_pools') or [] for pool in allocation_pools: total += int(netaddr.IPAddress(pool['end']) - netaddr.IPAddress(pool['start'])) + 1 if total: if subnet['gateway_ip']: total += 1 reserved += 1 if subnet['enable_dhcp']: reserved += 1 return total, reserved @registry.receives(resources.SUBNET, [events.AFTER_UPDATE]) def _notify_subnet_updated(self, resource, event, trigger, context, subnet, original_subnet, **kwargs): segment_id = subnet.get('segment_id') if not segment_id or subnet['ip_version'] != constants.IP_VERSION_4: return filters = {'segment_id': [segment_id], 'ip_version': [constants.IP_VERSION_4]} if not subnet['allocation_pools']: plugin = directory.get_plugin() alloc_pools = [s['allocation_pools'] for s in plugin.get_subnets(context, filters=filters)] if not any(alloc_pools): self.batch_notifier.queue_event(Event( self._delete_nova_inventory, segment_id)) return original_total, original_reserved = ( self._calculate_inventory_total_and_reserved(original_subnet)) updated_total, updated_reserved = ( self._calculate_inventory_total_and_reserved(subnet)) total = updated_total - original_total reserved = updated_reserved - original_reserved if total or reserved: segment_host_mappings = None if not original_subnet['allocation_pools']: segment_host_mappings = context.session.query( segment_model.SegmentHostMapping).filter_by( segment_id=segment_id).all() self.batch_notifier.queue_event(Event( self._create_or_update_nova_inventory, segment_id, total=total, reserved=reserved, segment_host_mappings=segment_host_mappings)) @registry.receives(resources.SUBNET, [events.AFTER_DELETE]) def _notify_subnet_deleted(self, resource, event, trigger, context, subnet, **kwargs): segment_id = subnet.get('segment_id') if not segment_id or subnet['ip_version'] != constants.IP_VERSION_4: return total, reserved = self._calculate_inventory_total_and_reserved(subnet) if total: filters = {'segment_id': [segment_id], 'ip_version': [4]} plugin = directory.get_plugin() if plugin.get_subnets_count(context, filters=filters) > 0: self.batch_notifier.queue_event(Event( self._update_nova_inventory, segment_id, total=-total, reserved=-reserved)) else: self.batch_notifier.queue_event(Event( self._delete_nova_inventory, segment_id)) def _get_aggregate_id(self, segment_id): aggregate_uuid = self.p_client.list_aggregates( segment_id)['aggregates'][0] aggregates = self.n_client.aggregates.list() for aggregate in aggregates: if aggregate.uuid == aggregate_uuid: return aggregate.id def _delete_nova_inventory(self, event): aggregate_id = self._get_aggregate_id(event.segment_id) aggregate = self.n_client.aggregates.get_details( aggregate_id) for host in aggregate.hosts: self.n_client.aggregates.remove_host(aggregate_id, host) self.n_client.aggregates.delete(aggregate_id) self.p_client.delete_resource_provider(event.segment_id) @registry.receives(resources.SEGMENT_HOST_MAPPING, [events.AFTER_CREATE]) def _notify_host_addition_to_aggregate(self, resource, event, trigger, context, host, current_segment_ids, **kwargs): query = context.session.query(models_v2.Subnet).filter( models_v2.Subnet.segment_id.in_(current_segment_ids)) segment_ids = {subnet['segment_id'] for subnet in query} self.batch_notifier.queue_event(Event(self._add_host_to_aggregate, segment_ids, host=host)) def _add_host_to_aggregate(self, event): for segment_id in event.segment_ids: try: aggregate_id = self._get_aggregate_id(segment_id) except n_exc.PlacementAggregateNotFound: LOG.info('When adding host %(host)s, aggregate not found ' 'for routed network segment %(segment_id)s', {'host': event.host, 'segment_id': segment_id}) continue try: self.n_client.aggregates.add_host(aggregate_id, event.host) except nova_exc.Conflict: LOG.info('Host %(host)s already exists in aggregate for ' 'routed network segment %(segment_id)s', {'host': event.host, 'segment_id': segment_id}) @registry.receives(resources.PORT, [events.AFTER_CREATE, events.AFTER_DELETE]) def _notify_port_created_or_deleted(self, resource, event, trigger, context, port, **kwargs): if not self._does_port_require_nova_inventory_update(port): return ipv4_subnets_number, segment_id = ( self._get_ipv4_subnets_number_and_segment_id(port, context)) if segment_id: if event == events.AFTER_DELETE: ipv4_subnets_number = -ipv4_subnets_number self.batch_notifier.queue_event(Event(self._update_nova_inventory, segment_id, reserved=ipv4_subnets_number)) @registry.receives(resources.PORT, [events.AFTER_UPDATE]) def _notify_port_updated(self, resource, event, trigger, context, **kwargs): port = kwargs.get('port') original_port = kwargs.get('original_port') does_original_port_require_nova_inventory_update = ( self._does_port_require_nova_inventory_update(original_port)) does_port_require_nova_inventory_update = ( self._does_port_require_nova_inventory_update(port)) if not (does_original_port_require_nova_inventory_update or does_port_require_nova_inventory_update): return original_port_ipv4_subnets_number, segment_id = ( self._get_ipv4_subnets_number_and_segment_id(original_port, context)) if not segment_id: return port_ipv4_subnets_number = len(self._get_ipv4_subnet_ids(port)) if not does_original_port_require_nova_inventory_update: original_port_ipv4_subnets_number = 0 if not does_port_require_nova_inventory_update: port_ipv4_subnets_number = 0 update = port_ipv4_subnets_number - original_port_ipv4_subnets_number if update: self.batch_notifier.queue_event(Event(self._update_nova_inventory, segment_id, reserved=update)) def _get_ipv4_subnets_number_and_segment_id(self, port, context): ipv4_subnet_ids = self._get_ipv4_subnet_ids(port) if not ipv4_subnet_ids: return 0, None segment_id = context.session.query( models_v2.Subnet).filter_by(id=ipv4_subnet_ids[0]).one()[ 'segment_id'] if not segment_id: return 0, None return len(ipv4_subnet_ids), segment_id def _does_port_require_nova_inventory_update(self, port): device_owner = port.get('device_owner') if (device_owner.startswith(constants.DEVICE_OWNER_COMPUTE_PREFIX) or device_owner == constants.DEVICE_OWNER_DHCP): return False return True def _get_ipv4_subnet_ids(self, port): ipv4_subnet_ids = [] for ip in port.get('fixed_ips', []): if netaddr.IPAddress( ip['ip_address']).version == constants.IP_VERSION_4: ipv4_subnet_ids.append(ip['subnet_id']) return ipv4_subnet_ids neutron-12.1.1/neutron/services/segments/__init__.py0000664000175000017500000000000013553660046022561 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/segments/placement_client.py0000664000175000017500000001563413553660047024354 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools from keystoneauth1 import exceptions as ks_exc from keystoneauth1 import loading as ks_loading from oslo_config import cfg from neutron._i18n import _ from neutron.common import exceptions as n_exc PLACEMENT_API_WITH_AGGREGATES = 'placement 1.1' def check_placement_api_available(f): @functools.wraps(f) def wrapper(self, *a, **k): try: return f(self, *a, **k) except ks_exc.EndpointNotFound: raise n_exc.PlacementEndpointNotFound() return wrapper class PlacementAPIClient(object): """Client class for placement ReST API.""" ks_filter = {'service_type': 'placement', 'interface': cfg.CONF.placement.endpoint_type, 'region_name': cfg.CONF.placement.region_name} def __init__(self): auth_plugin = ks_loading.load_auth_from_conf_options( cfg.CONF, 'placement') self._client = ks_loading.load_session_from_conf_options( cfg.CONF, 'placement', auth=auth_plugin) self._disabled = False def _get(self, url, **kwargs): return self._client.get(url, endpoint_filter=self.ks_filter, **kwargs) def _post(self, url, data, **kwargs): return self._client.post(url, json=data, endpoint_filter=self.ks_filter, **kwargs) def _put(self, url, data, **kwargs): return self._client.put(url, json=data, endpoint_filter=self.ks_filter, **kwargs) def _delete(self, url, **kwargs): return self._client.delete(url, endpoint_filter=self.ks_filter, **kwargs) @check_placement_api_available def create_resource_provider(self, resource_provider): """Create a resource provider. :param resource_provider: The resource provider :type resource_provider: dict: name (required), uuid (required) """ url = '/resource_providers' self._post(url, resource_provider) @check_placement_api_available def delete_resource_provider(self, resource_provider_uuid): """Delete a resource provider. :param resource_provider_uuid: UUID of the resource provider :type resource_provider_uuid: str """ url = '/resource_providers/%s' % resource_provider_uuid self._delete(url) @check_placement_api_available def create_inventory(self, resource_provider_uuid, inventory): """Create an inventory. :param resource_provider_uuid: UUID of the resource provider :type resource_provider_uuid: str :param inventory: The inventory :type inventory: dict: resource_class (required), total (required), reserved (required), min_unit (required), max_unit (required), step_size (required), allocation_ratio (required) """ url = '/resource_providers/%s/inventories' % resource_provider_uuid self._post(url, inventory) @check_placement_api_available def get_inventory(self, resource_provider_uuid, resource_class): """Get resource provider inventory. :param resource_provider_uuid: UUID of the resource provider :type resource_provider_uuid: str :param resource_class: Resource class name of the inventory to be returned :type resource_class: str :raises n_exc.PlacementInventoryNotFound: For failure to find inventory for a resource provider """ url = '/resource_providers/%s/inventories/%s' % ( resource_provider_uuid, resource_class) try: return self._get(url).json() except ks_exc.NotFound as e: if "No resource provider with uuid" in e.details: raise n_exc.PlacementResourceProviderNotFound( resource_provider=resource_provider_uuid) elif _("No inventory of class") in e.details: raise n_exc.PlacementInventoryNotFound( resource_provider=resource_provider_uuid, resource_class=resource_class) else: raise @check_placement_api_available def update_inventory(self, resource_provider_uuid, inventory, resource_class): """Update an inventory. :param resource_provider_uuid: UUID of the resource provider :type resource_provider_uuid: str :param inventory: The inventory :type inventory: dict :param resource_class: The resource class of the inventory to update :type resource_class: str :raises n_exc.PlacementInventoryUpdateConflict: For failure to updste inventory due to outdated resource_provider_generation """ url = '/resource_providers/%s/inventories/%s' % ( resource_provider_uuid, resource_class) try: self._put(url, inventory) except ks_exc.Conflict: raise n_exc.PlacementInventoryUpdateConflict( resource_provider=resource_provider_uuid, resource_class=resource_class) @check_placement_api_available def associate_aggregates(self, resource_provider_uuid, aggregates): """Associate a list of aggregates with a resource provider. :param resource_provider_uuid: UUID of the resource provider :type resource_provider_uuid: str :param aggregates: aggregates to be associated to the resource provider :type aggregates: list of UUIDs """ url = '/resource_providers/%s/aggregates' % resource_provider_uuid self._put(url, aggregates, headers={'openstack-api-version': PLACEMENT_API_WITH_AGGREGATES}) @check_placement_api_available def list_aggregates(self, resource_provider_uuid): """List resource provider aggregates. :param resource_provider_uuid: UUID of the resource provider :type resource_provider_uuid: str """ url = '/resource_providers/%s/aggregates' % resource_provider_uuid try: return self._get( url, headers={'openstack-api-version': PLACEMENT_API_WITH_AGGREGATES}).json() except ks_exc.NotFound: raise n_exc.PlacementAggregateNotFound( resource_provider=resource_provider_uuid) neutron-12.1.1/neutron/services/auto_allocate/0000775000175000017500000000000013553660156021453 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/auto_allocate/db.py0000664000175000017500000004166213553660047022422 0ustar zuulzuul00000000000000# Copyright 2015-2016 Hewlett Packard Enterprise Development Company, LP # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import constants as api_const from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.api.definitions import network as net_def from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import exceptions as n_exc from neutron_lib.objects import exceptions as obj_exc from neutron_lib.plugins import constants from neutron_lib.plugins import directory from oslo_log import log as logging from neutron._i18n import _ from neutron.common import exceptions as c_exc from neutron.db import _resource_extend as resource_extend from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.db import common_db_mixin from neutron.objects import auto_allocate as auto_allocate_obj from neutron.objects import base as base_obj from neutron.objects import network as net_obj from neutron.plugins.common import utils as p_utils from neutron.services.auto_allocate import exceptions LOG = logging.getLogger(__name__) CHECK_REQUIREMENTS = 'dry-run' def _ensure_external_network_default_value_callback( resource, event, trigger, **kwargs): """Ensure the is_default db field matches the create/update request.""" # TODO(boden): remove shim once all callbacks use payloads if 'payload' in kwargs: _request = kwargs['payload'].request_body _context = kwargs['payload'].context _network = kwargs['payload'].desired_state _orig = kwargs['payload'].states[0] else: _request = kwargs['request'] _context = kwargs['context'] _network = kwargs['network'] _orig = kwargs.get('original_network') @db_api.retry_if_session_inactive() def _do_ensure_external_network_default_value_callback( context, request, orig, network): is_default = request.get(api_const.IS_DEFAULT) if is_default is None: return if is_default: # ensure only one default external network at any given time pager = base_obj.Pager(limit=1) objs = net_obj.ExternalNetwork.get_objects(context, _pager=pager, is_default=True) if objs: if objs[0] and network['id'] != objs[0].network_id: raise exceptions.DefaultExternalNetworkExists( net_id=objs[0].network_id) if orig and orig.get(api_const.IS_DEFAULT) == is_default: return network[api_const.IS_DEFAULT] = is_default # Reflect the status of the is_default on the create/update request obj = net_obj.ExternalNetwork.get_object(context, network_id=network['id']) if obj: obj.is_default = is_default obj.update() _do_ensure_external_network_default_value_callback( _context, _request, _orig, _network) @resource_extend.has_resource_extenders class AutoAllocatedTopologyMixin(common_db_mixin.CommonDbMixin): def __new__(cls, *args, **kwargs): # NOTE(kevinbenton): we subscribe on object construction because # the tests blow away the callback manager for each run new = super(AutoAllocatedTopologyMixin, cls).__new__(cls, *args, **kwargs) registry.subscribe(_ensure_external_network_default_value_callback, resources.NETWORK, events.PRECOMMIT_UPDATE) registry.subscribe(_ensure_external_network_default_value_callback, resources.NETWORK, events.PRECOMMIT_CREATE) return new # TODO(armax): if a tenant modifies auto allocated resources under # the hood the behavior of the get_auto_allocated_topology API is # undetermined. Consider adding callbacks to deal with the following # situations: # - insert subnet -> plug router interface # - delete router -> remove the entire topology # - update subnet -> prevent operation # - update router gateway -> prevent operation # - ... @property def core_plugin(self): if not getattr(self, '_core_plugin', None): self._core_plugin = directory.get_plugin() return self._core_plugin @property def l3_plugin(self): if not getattr(self, '_l3_plugin', None): self._l3_plugin = directory.get_plugin(constants.L3) return self._l3_plugin @staticmethod @resource_extend.extends([net_def.COLLECTION_NAME]) def _extend_external_network_default(net_res, net_db): """Add is_default field to 'show' response.""" if net_db.external is not None: net_res[api_const.IS_DEFAULT] = net_db.external.is_default return net_res def get_auto_allocated_topology(self, context, tenant_id, fields=None): """Return tenant's network associated to auto-allocated topology. The topology will be provisioned upon return, if network is missing. """ fields = fields or [] tenant_id = self._validate(context, tenant_id) if CHECK_REQUIREMENTS in fields: # for dry-run requests, simply validates that subsequent # requests can be fulfilled based on a set of requirements # such as existence of default networks, pools, etc. return self._check_requirements(context, tenant_id) elif fields: raise n_exc.BadRequest(resource='auto_allocate', msg=_("Unrecognized field")) # Check for an existent topology network_id = self._get_auto_allocated_network(context, tenant_id) if network_id: return self._response(network_id, tenant_id, fields=fields) # See if we indeed have an external network to connect to, otherwise # we will fail fast default_external_network = self._get_default_external_network( context) # If we reach this point, then we got some work to do! network_id = self._build_topology( context, tenant_id, default_external_network) return self._response(network_id, tenant_id, fields=fields) def delete_auto_allocated_topology(self, context, tenant_id): tenant_id = self._validate(context, tenant_id) topology = self._get_auto_allocated_topology(context, tenant_id) if topology: subnets = self.core_plugin.get_subnets( context, filters={'network_id': [topology['network_id']]}) self._cleanup( context, network_id=topology['network_id'], router_id=topology['router_id'], subnets=subnets) def _build_topology(self, context, tenant_id, default_external_network): """Build the network topology and returns its network UUID.""" try: subnets = self._provision_tenant_private_network( context, tenant_id) network_id = subnets[0]['network_id'] router = self._provision_external_connectivity( context, default_external_network, subnets, tenant_id) network_id = self._save( context, tenant_id, network_id, router['id'], subnets) return network_id except exceptions.UnknownProvisioningError as e: # Clean partially provisioned topologies, and reraise the # error. If it can be retried, so be it. LOG.error("Unknown error while provisioning topology for " "tenant %(tenant_id)s. Reason: %(reason)s", {'tenant_id': tenant_id, 'reason': e}) self._cleanup( context, network_id=e.network_id, router_id=e.router_id, subnets=e.subnets) raise e.error def _check_requirements(self, context, tenant_id): """Raise if requirements are not met.""" self._get_default_external_network(context) try: self._get_supported_subnetpools(context) except n_exc.NotFound: raise exceptions.AutoAllocationFailure( reason=_("No default subnetpools defined")) return {'id': 'dry-run=pass', 'tenant_id': tenant_id} def _validate(self, context, tenant_id): """Validate and return the tenant to be associated to the topology.""" if tenant_id == 'None': # NOTE(HenryG): the client might be sending us astray by # passing no tenant; this is really meant to be the tenant # issuing the request, therefore let's get it from the context tenant_id = context.tenant_id if not context.is_admin and tenant_id != context.tenant_id: raise n_exc.NotAuthorized() return tenant_id def _get_auto_allocated_topology(self, context, tenant_id): """Return the auto allocated topology record if present or None.""" return auto_allocate_obj.AutoAllocatedTopology.get_object( context, project_id=tenant_id) def _get_auto_allocated_network(self, context, tenant_id): """Get the auto allocated network for the tenant.""" network = self._get_auto_allocated_topology(context, tenant_id) if network: return network['network_id'] @staticmethod def _response(network_id, tenant_id, fields=None): """Build response for auto-allocated network.""" res = { 'id': network_id, 'tenant_id': tenant_id } return db_utils.resource_fields(res, fields) def _get_default_external_network(self, context): """Get the default external network for the deployment.""" default_external_networks = net_obj.ExternalNetwork.get_objects( context, is_default=True) if not default_external_networks: LOG.error("Unable to find default external network " "for deployment, please create/assign one to " "allow auto-allocation to work correctly.") raise exceptions.AutoAllocationFailure( reason=_("No default router:external network")) if len(default_external_networks) > 1: LOG.error("Multiple external default networks detected. " "Network %s is true 'default'.", default_external_networks[0]['network_id']) return default_external_networks[0].network_id def _get_supported_subnetpools(self, context): """Return the default subnet pools available.""" default_subnet_pools = [ self.core_plugin.get_default_subnetpool( context, ver) for ver in (4, 6) ] available_pools = [ s for s in default_subnet_pools if s ] if not available_pools: LOG.error("No default pools available") raise n_exc.NotFound() return available_pools def _provision_tenant_private_network(self, context, tenant_id): """Create a tenant private network/subnets.""" network = None try: network_args = { 'name': 'auto_allocated_network', 'admin_state_up': False, 'tenant_id': tenant_id, 'shared': False } network = p_utils.create_network( self.core_plugin, context, {'network': network_args}) subnets = [] for pool in self._get_supported_subnetpools(context): subnet_args = { 'name': 'auto_allocated_subnet_v%s' % pool['ip_version'], 'network_id': network['id'], 'tenant_id': tenant_id, 'ip_version': pool['ip_version'], 'subnetpool_id': pool['id'], } subnets.append(p_utils.create_subnet( self.core_plugin, context, {'subnet': subnet_args})) return subnets except (c_exc.SubnetAllocationError, ValueError, n_exc.BadRequest, n_exc.NotFound) as e: LOG.error("Unable to auto allocate topology for tenant " "%(tenant_id)s due to missing or unmet " "requirements. Reason: %(reason)s", {'tenant_id': tenant_id, 'reason': e}) if network: self._cleanup(context, network['id']) raise exceptions.AutoAllocationFailure( reason=_("Unable to provide tenant private network")) except Exception as e: network_id = network['id'] if network else None raise exceptions.UnknownProvisioningError(e, network_id=network_id) def _provision_external_connectivity( self, context, default_external_network, subnets, tenant_id): """Uplink tenant subnet(s) to external network.""" router_args = { 'name': 'auto_allocated_router', l3_apidef.EXTERNAL_GW_INFO: { 'network_id': default_external_network}, 'tenant_id': tenant_id, 'admin_state_up': True } router = None attached_subnets = [] try: router = self.l3_plugin.create_router( context, {'router': router_args}) for subnet in subnets: self.l3_plugin.add_router_interface( context, router['id'], {'subnet_id': subnet['id']}) attached_subnets.append(subnet) return router except n_exc.BadRequest as e: LOG.error("Unable to auto allocate topology for tenant " "%(tenant_id)s because of router errors. " "Reason: %(reason)s", {'tenant_id': tenant_id, 'reason': e}) router_id = router['id'] if router else None self._cleanup(context, network_id=subnets[0]['network_id'], router_id=router_id, subnets=attached_subnets) raise exceptions.AutoAllocationFailure( reason=_("Unable to provide external connectivity")) except Exception as e: router_id = router['id'] if router else None raise exceptions.UnknownProvisioningError( e, network_id=subnets[0]['network_id'], router_id=router_id, subnets=subnets) def _save(self, context, tenant_id, network_id, router_id, subnets): """Save auto-allocated topology, or revert in case of DB errors.""" try: auto_allocate_obj.AutoAllocatedTopology( context, project_id=tenant_id, network_id=network_id, router_id=router_id).create() self.core_plugin.update_network( context, network_id, {'network': {'admin_state_up': True}}) except obj_exc.NeutronDbObjectDuplicateEntry: LOG.debug("Multiple auto-allocated networks detected for " "tenant %s. Attempting clean up for network %s " "and router %s.", tenant_id, network_id, router_id) self._cleanup( context, network_id=network_id, router_id=router_id, subnets=subnets) network_id = self._get_auto_allocated_network(context, tenant_id) except Exception as e: raise exceptions.UnknownProvisioningError( e, network_id=network_id, router_id=router_id, subnets=subnets) return network_id def _cleanup(self, context, network_id=None, router_id=None, subnets=None): """Clean up auto allocated resources.""" # Concurrent attempts to delete the topology may interleave and # cause some operations to fail with NotFound exceptions. Rather # than fail partially, the exceptions should be ignored and the # cleanup should proceed uninterrupted. if router_id: for subnet in subnets or []: ignore_notfound( self.l3_plugin.remove_router_interface, context, router_id, {'subnet_id': subnet['id']}) ignore_notfound(self.l3_plugin.delete_router, context, router_id) if network_id: ignore_notfound( self.core_plugin.delete_network, context, network_id) def ignore_notfound(func, *args, **kwargs): """Call the given function and pass if a `NotFound` exception is raised.""" try: return func(*args, **kwargs) except n_exc.NotFound: pass neutron-12.1.1/neutron/services/auto_allocate/exceptions.py0000664000175000017500000000250113553660046024202 0ustar zuulzuul00000000000000# Copyright 2015-2016 Hewlett Packard Enterprise Development Company, LP # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions as n_exc from neutron._i18n import _ class AutoAllocationFailure(n_exc.Conflict): message = _("Deployment error: %(reason)s.") class DefaultExternalNetworkExists(n_exc.Conflict): message = _("A default external network already exists: %(net_id)s.") class UnknownProvisioningError(Exception): """To track unknown errors and partial provisioning steps.""" def __init__(self, error, network_id=None, router_id=None, subnets=None): self.error = error self.network_id = network_id self.router_id = router_id self.subnets = subnets def __str__(self): return str(self.error) neutron-12.1.1/neutron/services/auto_allocate/models.py0000664000175000017500000000242213553660046023306 0ustar zuulzuul00000000000000# Copyright (c) 2015-2016 Hewlett Packard Enterprise Development Company LP # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa class AutoAllocatedTopology(model_base.BASEV2, model_base.HasProjectPrimaryKey): __tablename__ = 'auto_allocated_topologies' network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', ondelete='CASCADE'), nullable=False) router_id = sa.Column(sa.String(36), sa.ForeignKey('routers.id', ondelete='SET NULL'), nullable=True) neutron-12.1.1/neutron/services/auto_allocate/plugin.py0000664000175000017500000000224313553660047023323 0ustar zuulzuul00000000000000# Copyright 2015-2016 Hewlett Packard Enterprise Development Company, LP # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.services.auto_allocate import db class Plugin(db.AutoAllocatedTopologyMixin): _instance = None supported_extension_aliases = ["auto-allocated-topology"] @classmethod def get_instance(cls): if cls._instance is None: cls._instance = cls() return cls._instance def get_plugin_description(self): return "Auto Allocated Topology - aka get me a network." @classmethod def get_plugin_type(cls): return "auto-allocated-topology" neutron-12.1.1/neutron/services/auto_allocate/__init__.py0000664000175000017500000000000013553660046023550 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/flavors/0000775000175000017500000000000013553660156020313 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/flavors/__init__.py0000664000175000017500000000000013553660046022410 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/flavors/flavors_plugin.py0000664000175000017500000000226013553660047023716 0ustar zuulzuul00000000000000# Copyright (c) 2015, Hewlett-Packard Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins import constants from neutron_lib.services import base as service_base from neutron.db import flavors_db class FlavorsPlugin(service_base.ServicePluginBase, flavors_db.FlavorsDbMixin): """Implements Neutron Flavors Service plugin.""" supported_extension_aliases = ['flavors', 'service-type'] @classmethod def get_plugin_type(cls): return constants.FLAVORS def get_plugin_description(self): return "Neutron Flavors and Service Profiles manager plugin" neutron-12.1.1/neutron/services/revisions/0000775000175000017500000000000013553660156020660 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/revisions/__init__.py0000664000175000017500000000000013553660046022755 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/revisions/revision_plugin.py0000664000175000017500000002054413553660047024452 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.services import base as service_base from oslo_log import log as logging import sqlalchemy from sqlalchemy.orm import exc from sqlalchemy.orm import session as se import webob.exc from neutron._i18n import _ from neutron.db import _resource_extend as resource_extend from neutron.db import api as db_api from neutron.db import standard_attr LOG = logging.getLogger(__name__) @resource_extend.has_resource_extenders class RevisionPlugin(service_base.ServicePluginBase): """Plugin to populate revision numbers into standard attr resources.""" supported_extension_aliases = ['standard-attr-revisions', 'revision-if-match'] def __init__(self): super(RevisionPlugin, self).__init__() db_api.sqla_listen(se.Session, 'before_flush', self.bump_revisions) db_api.sqla_listen(se.Session, 'after_commit', self._clear_rev_bumped_flags) db_api.sqla_listen(se.Session, 'after_rollback', self._clear_rev_bumped_flags) def bump_revisions(self, session, context, instances): self._enforce_if_match_constraints(session) # bump revision number for any updated objects in the session for obj in session.dirty: if isinstance(obj, standard_attr.HasStandardAttributes): self._bump_obj_revision(session, obj) # see if any created/updated/deleted objects bump the revision # of another object objects_with_related_revisions = [ o for o in session.deleted | session.dirty | session.new if getattr(o, 'revises_on_change', ()) ] for obj in objects_with_related_revisions: self._bump_related_revisions(session, obj) def _bump_related_revisions(self, session, obj): for revises_col in getattr(obj, 'revises_on_change', ()): try: related_obj = self._find_related_obj(session, obj, revises_col) if not related_obj: LOG.warning("Could not find related %(col)s for " "resource %(obj)s to bump revision.", {'obj': obj, 'col': revises_col}) continue # if related object revises others, bump those as well self._bump_related_revisions(session, related_obj) # no need to bump revisions on related objects being deleted if related_obj not in session.deleted: self._bump_obj_revision(session, related_obj) except exc.ObjectDeletedError: # object was in session but another writer deleted it pass def get_plugin_type(self): return "revision_plugin" def get_plugin_description(self): return "Adds revision numbers to resources." @staticmethod @resource_extend.extends( list(standard_attr.get_standard_attr_resource_model_map())) def extend_resource_dict_revision(resource_res, resource_db): resource_res['revision_number'] = resource_db.revision_number def _find_related_obj(self, session, obj, relationship_col): """Gets a related object off of a relationship. Raises a runtime error if the relationship isn't configured correctly for revision bumping. """ # first check to see if it's directly attached to the object already related_obj = getattr(obj, relationship_col) if related_obj: return related_obj for rel in sqlalchemy.inspect(obj).mapper.relationships: if rel.key != relationship_col: continue if not rel.load_on_pending: raise RuntimeError(_("revises_on_change relationships must " "have load_on_pending set to True to " "bump parent revisions on create: %s"), relationship_col) def _clear_rev_bumped_flags(self, session): """This clears all flags on commit/rollback to enable rev bumps.""" for inst in session: setattr(inst, '_rev_bumped', False) def _bump_obj_revision(self, session, obj): """Increment object revision in compare and swap fashion. Before the increment, this checks and enforces any revision number constraints. """ if getattr(obj, '_rev_bumped', False): # we've already bumped the revision of this object in this txn return instance, match = self._get_constrained_instance_match(session) if instance and instance == obj: # one last check before bumping revision self._enforce_if_match_constraints(session) obj.bump_revision() setattr(obj, '_rev_bumped', True) def _find_instance_by_column_value(self, session, model, column, value): """Lookup object in session or from DB based on a column's value.""" for session_obj in session: if not isinstance(session_obj, model): continue if getattr(session_obj, column) == value: return session_obj # object isn't in session so we have to query for it related_obj = (session.query(model).filter_by(**{column: value}). first()) return related_obj def _get_constrained_instance_match(self, session): """Returns instance and constraint of if-match criterion if present. Checks the context associated with the session for compare-and-swap update revision number constraints. If one is found, this returns the instance that is constrained as well as the requested revision number to match. """ context = session.info.get('using_context') criteria = context.get_transaction_constraint() if context else None if not criteria: return None, None match = criteria.if_revision_match mmap = standard_attr.get_standard_attr_resource_model_map() model = mmap.get(criteria.resource) if not model: msg = _("Revision matching not supported for this resource") raise exc.BadRequest(resource=criteria.resource, msg=msg) instance = self._find_instance_by_column_value( session, model, 'id', criteria.resource_id) return instance, match def _enforce_if_match_constraints(self, session): """Check for if-match constraints and raise exception if violated. We determine the collection being modified and look for any objects of the collection type in the dirty/deleted items in the session. If they don't match the revision_number constraint supplied, we throw an exception. We are protected from a concurrent update because if we match revision number here and another update commits to the database first, the compare and swap of revision_number will fail and a StaleDataError (or deadlock in galera multi-writer) will be raised, at which point this will be retried and fail to match. """ instance, match = self._get_constrained_instance_match(session) if not instance or getattr(instance, '_rev_bumped', False): # no constraints present or constrain satisfied in this transaction return if instance.revision_number != match: raise RevisionNumberConstraintFailed(match, instance.revision_number) class RevisionNumberConstraintFailed(webob.exc.HTTPPreconditionFailed): def __init__(self, expected, current): detail = (_("Constrained to %(exp)s, but current revision is %(cur)s") % {'exp': expected, 'cur': current}) super(RevisionNumberConstraintFailed, self).__init__(detail=detail) neutron-12.1.1/neutron/services/timestamp/0000775000175000017500000000000013553660156020642 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/timestamp/timestamp_db.py0000664000175000017500000001005413553660047023663 0ustar zuulzuul00000000000000# Copyright 2015 HuaWei Technologies. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions as n_exc from oslo_utils import timeutils from sqlalchemy.orm import session as se from neutron.db import _model_query as model_query from neutron.db import _resource_extend as resource_extend from neutron.db import api as db_api from neutron.db import standard_attr CHANGED_SINCE = 'changed_since' TIME_FORMAT_WHOLE_SECONDS = '%Y-%m-%dT%H:%M:%S' def _change_since_result_filter_hook(query, filters): # this block is for change_since query # we get the changed_since string from filters. # And translate it from string to datetime type. # Then compare with the timestamp in db which has # datetime type. values = filters and filters.get(CHANGED_SINCE, []) if not values: return query data = filters[CHANGED_SINCE][0] try: changed_since_string = timeutils.parse_isotime(data) except Exception: msg = ("The input %s must be in the " "following format: YYYY-MM-DDTHH:MM:SSZ") % CHANGED_SINCE raise n_exc.InvalidInput(error_message=msg) changed_since = (timeutils. normalize_time(changed_since_string)) target_model_class = query.column_descriptions[0]['type'] query = query.join(standard_attr.StandardAttribute, target_model_class.standard_attr_id == standard_attr.StandardAttribute.id).filter( standard_attr.StandardAttribute.updated_at >= changed_since) return query def _update_timestamp(session, context, instances): objs_list = session.new.union(session.dirty) while objs_list: obj = objs_list.pop() if (isinstance(obj, standard_attr.HasStandardAttributes) and obj.standard_attr_id): obj.updated_at = timeutils.utcnow() def _format_timestamp(resource_db, result): result['created_at'] = (resource_db.created_at. strftime(TIME_FORMAT_WHOLE_SECONDS)) + 'Z' result['updated_at'] = (resource_db.updated_at. strftime(TIME_FORMAT_WHOLE_SECONDS)) + 'Z' def _add_timestamp(mapper, _conn, target): if not target.created_at and not target.updated_at: time = timeutils.utcnow() for field in ['created_at', 'updated_at']: setattr(target, field, time) return target @resource_extend.has_resource_extenders class TimeStamp_db_mixin(object): """Mixin class to add Time Stamp methods.""" def __new__(cls, *args, **kwargs): rs_model_maps = standard_attr.get_standard_attr_resource_model_map() for model in rs_model_maps.values(): model_query.register_hook( model, "change_since_query", query_hook=None, filter_hook=None, result_filters=_change_since_result_filter_hook) return super(TimeStamp_db_mixin, cls).__new__(cls, *args, **kwargs) def register_db_events(self): listen = db_api.sqla_listen listen(standard_attr.StandardAttribute, 'before_insert', _add_timestamp) listen(se.Session, 'before_flush', _update_timestamp) @staticmethod @resource_extend.extends( list(standard_attr.get_standard_attr_resource_model_map())) def _extend_resource_dict_timestamp(resource_res, resource_db): if (resource_db and resource_db.created_at and resource_db.updated_at): _format_timestamp(resource_db, resource_res) neutron-12.1.1/neutron/services/timestamp/timestamp_plugin.py0000664000175000017500000000273213553660047024600 0ustar zuulzuul00000000000000# Copyright 2015 HuaWei Technologies. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.services import base as service_base from neutron.db import models_v2 from neutron.objects import base as base_obj from neutron.services.timestamp import timestamp_db as ts_db class TimeStampPlugin(service_base.ServicePluginBase, ts_db.TimeStamp_db_mixin): """Implements Neutron Timestamp Service plugin.""" supported_extension_aliases = ['standard-attr-timestamp'] def __init__(self): super(TimeStampPlugin, self).__init__() self.register_db_events() # TODO(jlibosva): Move this to register_model_query_hook base_obj.register_filter_hook_on_model( models_v2.SubnetPool, ts_db.CHANGED_SINCE) @classmethod def get_plugin_type(cls): return 'timestamp' def get_plugin_description(self): return "Adds timestamps to Neutron resources with standard attributes" neutron-12.1.1/neutron/services/timestamp/__init__.py0000664000175000017500000000000013553660046022737 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/service_base.py0000664000175000017500000000501513553660046021642 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from oslo_utils import excutils from oslo_utils import importutils from neutron.db import servicetype_db as sdb from neutron.services import provider_configuration as pconf LOG = logging.getLogger(__name__) def load_drivers(service_type, plugin): """Loads drivers for specific service. Passes plugin instance to driver's constructor """ service_type_manager = sdb.ServiceTypeManager.get_instance() providers = (service_type_manager. get_service_providers( None, filters={'service_type': [service_type]}) ) if not providers: msg = ("No providers specified for '%s' service, exiting" % service_type) LOG.error(msg) raise SystemExit(1) drivers = {} for provider in providers: try: drivers[provider['name']] = importutils.import_object( provider['driver'], plugin ) LOG.debug("Loaded '%(provider)s' provider for service " "%(service_type)s", {'provider': provider['driver'], 'service_type': service_type}) except ImportError: with excutils.save_and_reraise_exception(): LOG.exception("Error loading provider '%(provider)s' for " "service %(service_type)s", {'provider': provider['driver'], 'service_type': service_type}) default_provider = None try: provider = service_type_manager.get_default_service_provider( None, service_type) default_provider = provider['name'] except pconf.DefaultServiceProviderNotFound: LOG.info("Default provider is not specified for service type %s", service_type) return drivers, default_provider neutron-12.1.1/neutron/services/externaldns/0000775000175000017500000000000013553660156021166 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/externaldns/drivers/0000775000175000017500000000000013553660156022644 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/externaldns/drivers/__init__.py0000664000175000017500000000000013553660046024741 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/externaldns/drivers/designate/0000775000175000017500000000000013553660156024607 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/externaldns/drivers/designate/__init__.py0000664000175000017500000000000013553660046026704 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/externaldns/drivers/designate/driver.py0000664000175000017500000001610013553660047026451 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from designateclient import exceptions as d_exc from designateclient.v2 import client as d_client from keystoneauth1.identity.generic import password from keystoneauth1 import loading from keystoneauth1 import token_endpoint from neutron_lib import constants from neutron_lib.exceptions import dns as dns_exc from oslo_config import cfg from neutron.conf.services import extdns_designate_driver from neutron.services.externaldns import driver IPV4_PTR_ZONE_PREFIX_MIN_SIZE = 8 IPV4_PTR_ZONE_PREFIX_MAX_SIZE = 24 IPV6_PTR_ZONE_PREFIX_MIN_SIZE = 4 IPV6_PTR_ZONE_PREFIX_MAX_SIZE = 124 _SESSION = None CONF = cfg.CONF extdns_designate_driver.register_designate_opts() def get_clients(context): global _SESSION if not _SESSION: _SESSION = loading.load_session_from_conf_options( CONF, 'designate') auth = token_endpoint.Token(CONF.designate.url, context.auth_token) client = d_client.Client(session=_SESSION, auth=auth) if CONF.designate.auth_type: admin_auth = loading.load_auth_from_conf_options( CONF, 'designate') else: admin_auth = password.Password( auth_url=CONF.designate.admin_auth_url, username=CONF.designate.admin_username, password=CONF.designate.admin_password, tenant_name=CONF.designate.admin_tenant_name, tenant_id=CONF.designate.admin_tenant_id) admin_client = d_client.Client(session=_SESSION, auth=admin_auth) return client, admin_client class Designate(driver.ExternalDNSService): """Driver for Designate.""" def __init__(self): ipv4_ptr_zone_size = CONF.designate.ipv4_ptr_zone_prefix_size ipv6_ptr_zone_size = CONF.designate.ipv6_ptr_zone_prefix_size if (ipv4_ptr_zone_size < IPV4_PTR_ZONE_PREFIX_MIN_SIZE or ipv4_ptr_zone_size > IPV4_PTR_ZONE_PREFIX_MAX_SIZE or (ipv4_ptr_zone_size % 8) != 0): raise dns_exc.InvalidPTRZoneConfiguration( parameter='ipv4_ptr_zone_size', number='8', maximum=str(IPV4_PTR_ZONE_PREFIX_MAX_SIZE), minimum=str(IPV4_PTR_ZONE_PREFIX_MIN_SIZE)) if (ipv6_ptr_zone_size < IPV6_PTR_ZONE_PREFIX_MIN_SIZE or ipv6_ptr_zone_size > IPV6_PTR_ZONE_PREFIX_MAX_SIZE or (ipv6_ptr_zone_size % 4) != 0): raise dns_exc.InvalidPTRZoneConfiguration( parameter='ipv6_ptr_zone_size', number='4', maximum=str(IPV6_PTR_ZONE_PREFIX_MAX_SIZE), minimum=str(IPV6_PTR_ZONE_PREFIX_MIN_SIZE)) def create_record_set(self, context, dns_domain, dns_name, records): designate, designate_admin = get_clients(context) v4, v6 = self._classify_records(records) try: if v4: designate.recordsets.create(dns_domain, dns_name, 'A', v4) if v6: designate.recordsets.create(dns_domain, dns_name, 'AAAA', v6) except d_exc.NotFound: raise dns_exc.DNSDomainNotFound(dns_domain=dns_domain) except d_exc.Conflict: raise dns_exc.DuplicateRecordSet(dns_name=dns_name) if not CONF.designate.allow_reverse_dns_lookup: return # Set up the PTR records recordset_name = '%s.%s' % (dns_name, dns_domain) ptr_zone_email = 'admin@%s' % dns_domain[:-1] if CONF.designate.ptr_zone_email: ptr_zone_email = CONF.designate.ptr_zone_email for record in records: in_addr_name = netaddr.IPAddress(record).reverse_dns in_addr_zone_name = self._get_in_addr_zone_name(in_addr_name) in_addr_zone_description = ( 'An %s zone for reverse lookups set up by Neutron.' % '.'.join(in_addr_name.split('.')[-3:])) try: # Since we don't delete in-addr zones, assume it already # exists. If it doesn't, create it designate_admin.recordsets.create(in_addr_zone_name, in_addr_name, 'PTR', [recordset_name]) except d_exc.NotFound: designate_admin.zones.create( in_addr_zone_name, email=ptr_zone_email, description=in_addr_zone_description) designate_admin.recordsets.create(in_addr_zone_name, in_addr_name, 'PTR', [recordset_name]) def _classify_records(self, records): v4 = [] v6 = [] for record in records: if netaddr.IPAddress(record).version == 4: v4.append(record) else: v6.append(record) return v4, v6 def _get_in_addr_zone_name(self, in_addr_name): units = self._get_bytes_or_nybles_to_skip(in_addr_name) return '.'.join(in_addr_name.split('.')[units:]) def _get_bytes_or_nybles_to_skip(self, in_addr_name): if 'in-addr.arpa' in in_addr_name: return int((constants.IPv4_BITS - CONF.designate.ipv4_ptr_zone_prefix_size) / 8) return int((constants.IPv6_BITS - CONF.designate.ipv6_ptr_zone_prefix_size) / 4) def delete_record_set(self, context, dns_domain, dns_name, records): designate, designate_admin = get_clients(context) ids_to_delete = self._get_ids_ips_to_delete( dns_domain, '%s.%s' % (dns_name, dns_domain), records, designate) for _id in ids_to_delete: designate.recordsets.delete(dns_domain, _id) if not CONF.designate.allow_reverse_dns_lookup: return for record in records: in_addr_name = netaddr.IPAddress(record).reverse_dns in_addr_zone_name = self._get_in_addr_zone_name(in_addr_name) designate_admin.recordsets.delete(in_addr_zone_name, in_addr_name) def _get_ids_ips_to_delete(self, dns_domain, name, records, designate_client): try: recordsets = designate_client.recordsets.list( dns_domain, criterion={"name": "%s" % name}) except d_exc.NotFound: raise dns_exc.DNSDomainNotFound(dns_domain=dns_domain) ids = [rec['id'] for rec in recordsets] ips = [str(ip) for rec in recordsets for ip in rec['records']] if set(ips) != set(records): raise dns_exc.DuplicateRecordSet(dns_name=name) return ids neutron-12.1.1/neutron/services/externaldns/__init__.py0000664000175000017500000000000013553660046023263 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/externaldns/driver.py0000664000175000017500000000524313553660046023035 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from oslo_config import cfg from oslo_log import log import six from neutron import manager LOG = log.getLogger(__name__) @six.add_metaclass(abc.ABCMeta) class ExternalDNSService(object): """Interface definition for an external dns service driver.""" def __init__(self): """Initialize external dns service driver.""" @classmethod def get_instance(cls): """Return an instance of the configured external DNS driver.""" external_dns_driver_name = cfg.CONF.external_dns_driver mgr = manager.NeutronManager LOG.debug("Loading external dns driver: %s", external_dns_driver_name) driver_class = mgr.load_class_for_provider( 'neutron.services.external_dns_drivers', external_dns_driver_name) return driver_class() @abc.abstractmethod def create_record_set(self, context, dns_domain, dns_name, records): """Create a record set in the specified zone. :param context: neutron api request context :type context: neutron_lib.context.Context :param dns_domain: the dns_domain where the record set will be created :type dns_domain: String :param dns_name: the name associated with the record set :type dns_name: String :param records: the records in the set :type records: List of Strings :raises: neutron.extensions.dns.DNSDomainNotFound neutron.extensions.dns.DuplicateRecordSet """ @abc.abstractmethod def delete_record_set(self, context, dns_domain, dns_name, records): """Delete a record set in the specified zone. :param context: neutron api request context :type context: neutron.context.Context :param dns_domain: the dns_domain from which the record set will be deleted :type dns_domain: String :param dns_name: the dns_name associated with the record set to be deleted :type dns_name: String :param records: the records in the set to be deleted :type records: List of Strings """ neutron-12.1.1/neutron/services/qos/0000775000175000017500000000000013553660156017441 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/qos/drivers/0000775000175000017500000000000013553660156021117 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/qos/drivers/sriov/0000775000175000017500000000000013553660156022261 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/qos/drivers/sriov/__init__.py0000664000175000017500000000000013553660046024356 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/qos/drivers/sriov/driver.py0000664000175000017500000000412013553660047024122 0ustar zuulzuul00000000000000# Copyright (c) 2016 Red Hat Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib.db import constants as db_consts from neutron_lib.services.qos import base from neutron_lib.services.qos import constants as qos_consts from oslo_log import log as logging LOG = logging.getLogger(__name__) DRIVER = None SUPPORTED_RULES = { qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: { qos_consts.MAX_KBPS: { 'type:range': [0, db_consts.DB_INTEGER_MAX_VALUE]}, qos_consts.MAX_BURST: { 'type:range': [0, db_consts.DB_INTEGER_MAX_VALUE]}, qos_consts.DIRECTION: { 'type:values': [constants.EGRESS_DIRECTION]} }, qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: { qos_consts.MIN_KBPS: { 'type:range': [0, db_consts.DB_INTEGER_MAX_VALUE]}, qos_consts.DIRECTION: {'type:values': [constants.EGRESS_DIRECTION]} } } class SRIOVNICSwitchDriver(base.DriverBase): @staticmethod def create(): return SRIOVNICSwitchDriver( name='sriovnicswitch', vif_types=[portbindings.VIF_TYPE_HW_VEB], vnic_types=[portbindings.VNIC_DIRECT, portbindings.VNIC_MACVTAP], supported_rules=SUPPORTED_RULES, requires_rpc_notifications=True) def register(): """Register the driver.""" global DRIVER if not DRIVER: DRIVER = SRIOVNICSwitchDriver.create() LOG.debug('SR-IOV NIC Switch QoS driver registered') neutron-12.1.1/neutron/services/qos/drivers/linuxbridge/0000775000175000017500000000000013553660156023433 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/qos/drivers/linuxbridge/__init__.py0000664000175000017500000000000013553660046025530 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/qos/drivers/linuxbridge/driver.py0000664000175000017500000000372513553660046025305 0ustar zuulzuul00000000000000# Copyright (c) 2016 Red Hat Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib.db import constants as db_consts from neutron_lib.services.qos import base from neutron_lib.services.qos import constants as qos_consts from oslo_log import log as logging LOG = logging.getLogger(__name__) DRIVER = None SUPPORTED_RULES = { qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: { qos_consts.MAX_KBPS: { 'type:range': [0, db_consts.DB_INTEGER_MAX_VALUE]}, qos_consts.MAX_BURST: { 'type:range': [0, db_consts.DB_INTEGER_MAX_VALUE]}, qos_consts.DIRECTION: { 'type:values': constants.VALID_DIRECTIONS} }, qos_consts.RULE_TYPE_DSCP_MARKING: { qos_consts.DSCP_MARK: {'type:values': constants.VALID_DSCP_MARKS} } } class LinuxBridgeDriver(base.DriverBase): @staticmethod def create(): return LinuxBridgeDriver( name='linuxbridge', vif_types=[portbindings.VIF_TYPE_BRIDGE, portbindings.VIF_TYPE_TAP], vnic_types=[portbindings.VNIC_NORMAL], supported_rules=SUPPORTED_RULES, requires_rpc_notifications=True) def register(): """Register the driver.""" global DRIVER if not DRIVER: DRIVER = LinuxBridgeDriver.create() LOG.debug('Linuxbridge QoS driver registered') neutron-12.1.1/neutron/services/qos/drivers/manager.py0000664000175000017500000001707613553660047023115 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.services.qos import constants as qos_consts from oslo_log import log as logging from neutron.api.rpc.callbacks import events as rpc_events from neutron.api.rpc.callbacks.producer import registry as rpc_registry from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc from neutron.common import constants from neutron.common import exceptions from neutron.objects.qos import policy as policy_object LOG = logging.getLogger(__name__) SKIPPED_VIF_TYPES = [ portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_BINDING_FAILED ] class QosServiceDriverManager(object): def __init__(self): self._drivers = [] self.rpc_notifications_required = False rpc_registry.provide(self._get_qos_policy_cb, resources.QOS_POLICY) # notify any registered QoS driver that we're ready, those will # call the driver manager back with register_driver if they # are enabled registry.publish(qos_consts.QOS_PLUGIN, events.AFTER_INIT, self) if self.rpc_notifications_required: self.push_api = resources_rpc.ResourcesPushRpcApi() @staticmethod def _get_qos_policy_cb(resource, policy_id, **kwargs): context = kwargs.get('context') if context is None: LOG.warning( 'Received %(resource)s %(policy_id)s without context', {'resource': resource, 'policy_id': policy_id}) return policy = policy_object.QosPolicy.get_object(context, id=policy_id) return policy @staticmethod def _validate_vnic_type(driver, vnic_type, port_id): if driver.is_vnic_compatible(vnic_type): return True LOG.debug("vnic_type %(vnic_type)s of port %(port_id)s " "is not compatible with QoS driver %(driver)s", {'vnic_type': vnic_type, 'port_id': port_id, 'driver': driver.name}) return False @staticmethod def _validate_vif_type(driver, vif_type, port_id): if driver.is_vif_type_compatible(vif_type): return True LOG.debug("vif_type %(vif_type)s of port %(port_id)s " "is not compatible with QoS driver %(driver)s", {'vif_type': vif_type, 'port_id': port_id, 'driver': driver.name}) return False @staticmethod def _parse_parameter_values(parameter_values): validator, possible_values = list(parameter_values.items())[0] if validator == 'type:range': parameter_values = { "start": possible_values[0], "end": possible_values[1] } parameter_type = constants.VALUES_TYPE_RANGE elif validator == 'type:values': parameter_values = possible_values parameter_type = constants.VALUES_TYPE_CHOICES return parameter_values, parameter_type def call(self, method_name, *args, **kwargs): """Helper method for calling a method across all extension drivers.""" exc_list = [] for driver in self._drivers: try: getattr(driver, method_name)(*args, **kwargs) except Exception as exc: exception_msg = ("Extension driver '%(name)s' failed in " "%(method)s") exception_data = {'name': driver.name, 'method': method_name} LOG.exception(exception_msg, exception_data) exc_list.append(exc) if exc_list: raise exceptions.DriverCallError(exc_list=exc_list) if self.rpc_notifications_required: context = kwargs.get('context') or args[0] policy_obj = kwargs.get('policy_obj') or args[1] # we don't push create_policy events since policies are empty # on creation, they only become of any use when rules get # attached to them. if method_name == qos_consts.UPDATE_POLICY: self.push_api.push(context, [policy_obj], rpc_events.UPDATED) elif method_name == qos_consts.DELETE_POLICY: self.push_api.push(context, [policy_obj], rpc_events.DELETED) def register_driver(self, driver): """Register driver with qos plugin. This method is called from drivers on INIT event. """ self._drivers.append(driver) self.rpc_notifications_required |= driver.requires_rpc_notifications def validate_rule_for_port(self, rule, port): for driver in self._drivers: vif_type = port.binding.vif_type if vif_type not in SKIPPED_VIF_TYPES: if not self._validate_vif_type(driver, vif_type, port['id']): continue else: vnic_type = port.binding.vnic_type if not self._validate_vnic_type(driver, vnic_type, port['id']): continue if driver.is_rule_supported(rule): return True return False @property def supported_rule_types(self): if not self._drivers: return [] rule_types = set(qos_consts.VALID_RULE_TYPES) # Recalculate on every call to allow drivers determine supported rule # types dynamically for driver in self._drivers: new_rule_types = rule_types & set(driver.supported_rules) dropped_rule_types = rule_types - new_rule_types if dropped_rule_types: LOG.debug("%(rule_types)s rule types disabled " "because enabled %(driver)s does not support them", {'rule_types': ', '.join(dropped_rule_types), 'driver': driver.name}) rule_types = new_rule_types LOG.debug("Supported QoS rule types " "(common subset for all loaded QoS drivers): %s", rule_types) return rule_types def supported_rule_type_details(self, rule_type_name): if not self._drivers: return [] rule_type_drivers = [] for driver in self._drivers: if rule_type_name in driver.supported_rules: supported_parameters = [] rule_parameters = driver.supported_rules.get(rule_type_name) for name, values in rule_parameters.items(): parameter_values, parameter_type = ( self._parse_parameter_values(values)) supported_parameters.append({ "parameter_name": name, "parameter_values": parameter_values, "parameter_type": parameter_type }) rule_type_drivers.append({ "name": driver.name, "supported_parameters": supported_parameters }) return rule_type_drivers neutron-12.1.1/neutron/services/qos/drivers/openvswitch/0000775000175000017500000000000013553660156023470 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/qos/drivers/openvswitch/__init__.py0000664000175000017500000000000013553660046025565 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/qos/drivers/openvswitch/driver.py0000664000175000017500000000370213553660047025336 0ustar zuulzuul00000000000000# Copyright (c) 2016 Red Hat Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib.db import constants as db_consts from neutron_lib.services.qos import base from neutron_lib.services.qos import constants as qos_consts from oslo_log import log as logging LOG = logging.getLogger(__name__) DRIVER = None SUPPORTED_RULES = { qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: { qos_consts.MAX_KBPS: { 'type:range': [0, db_consts.DB_INTEGER_MAX_VALUE]}, qos_consts.MAX_BURST: { 'type:range': [0, db_consts.DB_INTEGER_MAX_VALUE]}, qos_consts.DIRECTION: { 'type:values': constants.VALID_DIRECTIONS} }, qos_consts.RULE_TYPE_DSCP_MARKING: { qos_consts.DSCP_MARK: {'type:values': constants.VALID_DSCP_MARKS} } } class OVSDriver(base.DriverBase): @staticmethod def create(): return OVSDriver( name='openvswitch', vif_types=[portbindings.VIF_TYPE_OVS, portbindings.VIF_TYPE_VHOST_USER], vnic_types=[portbindings.VNIC_NORMAL], supported_rules=SUPPORTED_RULES, requires_rpc_notifications=True) def register(): """Register the driver.""" global DRIVER if not DRIVER: DRIVER = OVSDriver.create() LOG.debug('Open vSwitch QoS driver registered') neutron-12.1.1/neutron/services/qos/drivers/__init__.py0000664000175000017500000000000013553660046023214 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/qos/qos_plugin.py0000664000175000017500000004340513553660047022200 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import qos as qos_apidef from neutron_lib.callbacks import events as callbacks_events from neutron_lib.callbacks import registry as callbacks_registry from neutron_lib.callbacks import resources as callbacks_resources from neutron_lib import exceptions as lib_exc from neutron_lib.services.qos import constants as qos_consts from neutron.common import exceptions as n_exc from neutron.db import api as db_api from neutron.db import db_base_plugin_common from neutron.extensions import qos from neutron.objects import base as base_obj from neutron.objects import network as network_object from neutron.objects import ports as ports_object from neutron.objects.qos import policy as policy_object from neutron.objects.qos import qos_policy_validator as checker from neutron.objects.qos import rule_type as rule_type_object from neutron.services.qos.drivers import manager class QoSPlugin(qos.QoSPluginBase): """Implementation of the Neutron QoS Service Plugin. This class implements a Quality of Service plugin that provides quality of service parameters over ports and networks. """ supported_extension_aliases = [qos_apidef.ALIAS, 'qos-bw-limit-direction', 'qos-default', 'qos-rule-type-details'] __native_pagination_support = True __native_sorting_support = True def __init__(self): super(QoSPlugin, self).__init__() self.driver_manager = manager.QosServiceDriverManager() callbacks_registry.subscribe( self._validate_create_port_callback, callbacks_resources.PORT, callbacks_events.PRECOMMIT_CREATE) callbacks_registry.subscribe( self._validate_update_port_callback, callbacks_resources.PORT, callbacks_events.PRECOMMIT_UPDATE) callbacks_registry.subscribe( self._validate_update_network_callback, callbacks_resources.NETWORK, callbacks_events.PRECOMMIT_UPDATE) def _get_ports_with_policy(self, context, policy): networks_ids = policy.get_bound_networks() ports_with_net_policy = ports_object.Port.get_objects( context, network_id=networks_ids) # Filter only this ports which don't have overwritten policy ports_with_net_policy = [ port for port in ports_with_net_policy if port.qos_policy_id is None ] ports_ids = policy.get_bound_ports() ports_with_policy = ports_object.Port.get_objects( context, id=ports_ids) return list(set(ports_with_policy + ports_with_net_policy)) def _validate_create_port_callback(self, resource, event, trigger, **kwargs): context = kwargs['context'] port_id = kwargs['port']['id'] port = ports_object.Port.get_object(context, id=port_id) network = network_object.Network.get_object(context, id=port.network_id) policy_id = port.qos_policy_id or network.qos_policy_id if policy_id is None: return policy = policy_object.QosPolicy.get_object( context.elevated(), id=policy_id) self.validate_policy_for_port(policy, port) def _validate_update_port_callback(self, resource, event, trigger, payload=None): context = payload.context original_policy_id = payload.states[0].get( qos_consts.QOS_POLICY_ID) policy_id = payload.desired_state.get(qos_consts.QOS_POLICY_ID) if policy_id is None or policy_id == original_policy_id: return updated_port = ports_object.Port.get_object( context, id=payload.desired_state['id']) policy = policy_object.QosPolicy.get_object( context.elevated(), id=policy_id) self.validate_policy_for_port(policy, updated_port) def _validate_update_network_callback(self, resource, event, trigger, payload=None): context = payload.context original_network = payload.states[0] updated_network = payload.desired_state original_policy_id = original_network.get(qos_consts.QOS_POLICY_ID) policy_id = updated_network.get(qos_consts.QOS_POLICY_ID) if policy_id is None or policy_id == original_policy_id: return policy = policy_object.QosPolicy.get_object( context.elevated(), id=policy_id) ports = ports_object.Port.get_objects( context, network_id=updated_network['id']) # Filter only this ports which don't have overwritten policy ports = [ port for port in ports if port.qos_policy_id is None ] self.validate_policy_for_ports(policy, ports) def validate_policy(self, context, policy): ports = self._get_ports_with_policy(context, policy) self.validate_policy_for_ports(policy, ports) def validate_policy_for_ports(self, policy, ports): for port in ports: self.validate_policy_for_port(policy, port) def validate_policy_for_port(self, policy, port): for rule in policy.rules: if not self.driver_manager.validate_rule_for_port(rule, port): raise n_exc.QosRuleNotSupported(rule_type=rule.rule_type, port_id=port['id']) @db_base_plugin_common.convert_result_to_dict def create_policy(self, context, policy): """Create a QoS policy. :param context: neutron api request context :type context: neutron_lib.context.Context :param policy: policy data to be applied :type policy: dict :returns: a QosPolicy object """ # NOTE(dasm): body 'policy' contains both tenant_id and project_id # but only latter needs to be used to create QosPolicy object. # We need to remove redundant keyword. # This cannot be done in other place of stacktrace, because neutron # needs to be backward compatible. policy['policy'].pop('tenant_id', None) policy_obj = policy_object.QosPolicy(context, **policy['policy']) with db_api.context_manager.writer.using(context): policy_obj.create() self.driver_manager.call(qos_consts.CREATE_POLICY_PRECOMMIT, context, policy_obj) self.driver_manager.call(qos_consts.CREATE_POLICY, context, policy_obj) return policy_obj @db_base_plugin_common.convert_result_to_dict def update_policy(self, context, policy_id, policy): """Update a QoS policy. :param context: neutron api request context :type context: neutron.context.Context :param policy_id: the id of the QosPolicy to update :param policy_id: str uuid :param policy: new policy data to be applied :type policy: dict :returns: a QosPolicy object """ policy_data = policy['policy'] with db_api.context_manager.writer.using(context): policy_obj = self._get_policy_obj(context, policy_id) policy_obj.update_fields(policy_data, reset_changes=True) policy_obj.update() self.driver_manager.call(qos_consts.UPDATE_POLICY_PRECOMMIT, context, policy_obj) self.driver_manager.call(qos_consts.UPDATE_POLICY, context, policy_obj) return policy_obj def delete_policy(self, context, policy_id): """Delete a QoS policy. :param context: neutron api request context :type context: neutron.context.Context :param policy_id: the id of the QosPolicy to delete :type policy_id: str uuid :returns: None """ with db_api.context_manager.writer.using(context): policy = policy_object.QosPolicy(context) policy.id = policy_id policy.delete() self.driver_manager.call(qos_consts.DELETE_POLICY_PRECOMMIT, context, policy) self.driver_manager.call(qos_consts.DELETE_POLICY, context, policy) def _get_policy_obj(self, context, policy_id): """Fetch a QoS policy. :param context: neutron api request context :type context: neutron.context.Context :param policy_id: the id of the QosPolicy to fetch :type policy_id: str uuid :returns: a QosPolicy object :raises: n_exc.QosPolicyNotFound """ obj = policy_object.QosPolicy.get_object(context, id=policy_id) if obj is None: raise n_exc.QosPolicyNotFound(policy_id=policy_id) return obj @db_base_plugin_common.filter_fields @db_base_plugin_common.convert_result_to_dict def get_policy(self, context, policy_id, fields=None): """Get a QoS policy. :param context: neutron api request context :type context: neutron.context.Context :param policy_id: the id of the QosPolicy to update :type policy_id: str uuid :returns: a QosPolicy object """ return self._get_policy_obj(context, policy_id) @db_base_plugin_common.filter_fields @db_base_plugin_common.convert_result_to_dict def get_policies(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Get QoS policies. :param context: neutron api request context :type context: neutron.context.Context :param filters: search criteria :type filters: dict :returns: QosPolicy objects meeting the search criteria """ filters = filters or dict() pager = base_obj.Pager(sorts, limit, page_reverse, marker) return policy_object.QosPolicy.get_objects(context, _pager=pager, **filters) @db_base_plugin_common.filter_fields @db_base_plugin_common.convert_result_to_dict def get_rule_type(self, context, rule_type_name, fields=None): if not context.is_admin: raise lib_exc.NotAuthorized() return rule_type_object.QosRuleType.get_object(rule_type_name) @db_base_plugin_common.filter_fields @db_base_plugin_common.convert_result_to_dict def get_rule_types(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): if not filters: filters = {} return rule_type_object.QosRuleType.get_objects(**filters) def supported_rule_type_details(self, rule_type_name): return self.driver_manager.supported_rule_type_details(rule_type_name) @property def supported_rule_types(self): return self.driver_manager.supported_rule_types @db_base_plugin_common.convert_result_to_dict def create_policy_rule(self, context, rule_cls, policy_id, rule_data): """Create a QoS policy rule. :param context: neutron api request context :type context: neutron.context.Context :param rule_cls: the rule object class :type rule_cls: a class from the rule_object (qos.objects.rule) module :param policy_id: the id of the QosPolicy for which to create the rule :type policy_id: str uuid :param rule_data: the rule data to be applied :type rule_data: dict :returns: a QoS policy rule object """ rule_type = rule_cls.rule_type rule_data = rule_data[rule_type + '_rule'] with db_api.autonested_transaction(context.session): # Ensure that we have access to the policy. policy = self._get_policy_obj(context, policy_id) checker.check_bandwidth_rule_conflict(policy, rule_data) rule = rule_cls(context, qos_policy_id=policy_id, **rule_data) checker.check_rules_conflict(policy, rule) rule.create() policy.obj_load_attr('rules') self.validate_policy(context, policy) self.driver_manager.call(qos_consts.UPDATE_POLICY_PRECOMMIT, context, policy) self.driver_manager.call(qos_consts.UPDATE_POLICY, context, policy) return rule @db_base_plugin_common.convert_result_to_dict def update_policy_rule(self, context, rule_cls, rule_id, policy_id, rule_data): """Update a QoS policy rule. :param context: neutron api request context :type context: neutron.context.Context :param rule_cls: the rule object class :type rule_cls: a class from the rule_object (qos.objects.rule) module :param rule_id: the id of the QoS policy rule to update :type rule_id: str uuid :param policy_id: the id of the rule's policy :type policy_id: str uuid :param rule_data: the new rule data to update :type rule_data: dict :returns: a QoS policy rule object """ rule_type = rule_cls.rule_type rule_data = rule_data[rule_type + '_rule'] with db_api.autonested_transaction(context.session): # Ensure we have access to the policy. policy = self._get_policy_obj(context, policy_id) # Ensure the rule belongs to the policy. checker.check_bandwidth_rule_conflict(policy, rule_data) rule = policy.get_rule_by_id(rule_id) rule.update_fields(rule_data, reset_changes=True) checker.check_rules_conflict(policy, rule) rule.update() policy.obj_load_attr('rules') self.validate_policy(context, policy) self.driver_manager.call(qos_consts.UPDATE_POLICY_PRECOMMIT, context, policy) self.driver_manager.call(qos_consts.UPDATE_POLICY, context, policy) return rule def delete_policy_rule(self, context, rule_cls, rule_id, policy_id): """Delete a QoS policy rule. :param context: neutron api request context :type context: neutron.context.Context :param rule_cls: the rule object class :type rule_cls: a class from the rule_object (qos.objects.rule) module :param rule_id: the id of the QosPolicy Rule to delete :type rule_id: str uuid :param policy_id: the id of the rule's policy :type policy_id: str uuid :returns: None """ with db_api.autonested_transaction(context.session): # Ensure we have access to the policy. policy = self._get_policy_obj(context, policy_id) rule = policy.get_rule_by_id(rule_id) rule.delete() policy.obj_load_attr('rules') self.driver_manager.call(qos_consts.UPDATE_POLICY_PRECOMMIT, context, policy) self.driver_manager.call(qos_consts.UPDATE_POLICY, context, policy) @db_base_plugin_common.filter_fields @db_base_plugin_common.convert_result_to_dict def get_policy_rule(self, context, rule_cls, rule_id, policy_id, fields=None): """Get a QoS policy rule. :param context: neutron api request context :type context: neutron.context.Context :param rule_cls: the rule object class :type rule_cls: a class from the rule_object (qos.objects.rule) module :param rule_id: the id of the QoS policy rule to get :type rule_id: str uuid :param policy_id: the id of the rule's policy :type policy_id: str uuid :returns: a QoS policy rule object :raises: n_exc.QosRuleNotFound """ with db_api.autonested_transaction(context.session): # Ensure we have access to the policy. self._get_policy_obj(context, policy_id) rule = rule_cls.get_object(context, id=rule_id) if not rule: raise n_exc.QosRuleNotFound(policy_id=policy_id, rule_id=rule_id) return rule # TODO(QoS): enforce rule types when accessing rule objects @db_base_plugin_common.filter_fields @db_base_plugin_common.convert_result_to_dict def get_policy_rules(self, context, rule_cls, policy_id, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Get QoS policy rules. :param context: neutron api request context :type context: neutron.context.Context :param rule_cls: the rule object class :type rule_cls: a class from the rule_object (qos.objects.rule) module :param policy_id: the id of the QosPolicy for which to get rules :type policy_id: str uuid :returns: QoS policy rule objects meeting the search criteria """ with db_api.autonested_transaction(context.session): # Ensure we have access to the policy. self._get_policy_obj(context, policy_id) filters = filters or dict() filters[qos_consts.QOS_POLICY_ID] = policy_id pager = base_obj.Pager(sorts, limit, page_reverse, marker) return rule_cls.get_objects(context, _pager=pager, **filters) neutron-12.1.1/neutron/services/qos/__init__.py0000664000175000017500000000000013553660046021536 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/l3_router/0000775000175000017500000000000013553660156020555 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/l3_router/service_providers/0000775000175000017500000000000013553660156024312 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/l3_router/service_providers/driver_controller.py0000664000175000017500000003020413553660047030420 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as lib_const from neutron_lib import exceptions as lib_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_log import log as logging from neutron._i18n import _ from neutron.db import servicetype_db as st_db from neutron.services import provider_configuration from neutron.services import service_base LOG = logging.getLogger(__name__) @registry.has_registry_receivers class DriverController(object): """Driver controller for the L3 service plugin. This component is responsible for dispatching router requests to L3 service providers and for performing the bookkeeping about which driver is associated with a given router. This is not intended to be accessed by the drivers or the l3 plugin. All of the methods are marked as private to reflect this. """ def __init__(self, l3_plugin): self.l3_plugin = l3_plugin self._stm = st_db.ServiceTypeManager.get_instance() self._stm.add_provider_configuration( plugin_constants.L3, _LegacyPlusProviderConfiguration()) self._load_drivers() def _load_drivers(self): self.drivers, self.default_provider = ( service_base.load_drivers(plugin_constants.L3, self.l3_plugin)) # store the provider name on each driver to make finding inverse easy for provider_name, driver in self.drivers.items(): setattr(driver, 'name', provider_name) @property def _flavor_plugin(self): if not hasattr(self, '_flavor_plugin_ref'): self._flavor_plugin_ref = directory.get_plugin( plugin_constants.FLAVORS) return self._flavor_plugin_ref @registry.receives(resources.ROUTER, [events.BEFORE_CREATE]) def _check_router_request(self, resource, event, trigger, context, router, **kwargs): """Validates that API request is sane (flags compat with flavor).""" drv = self._get_provider_for_create(context, router) _ensure_driver_supports_request(drv, router) @registry.receives(resources.ROUTER, [events.PRECOMMIT_CREATE]) def _set_router_provider(self, resource, event, trigger, context, router, router_db, **kwargs): """Associates a router with a service provider. Association is done by flavor_id if it's specified, otherwise it will fallback to determining which loaded driver supports the ha/distributed attributes associated with the router. """ if _flavor_specified(router): router_db.flavor_id = router['flavor_id'] drv = self._get_provider_for_create(context, router) self._stm.add_resource_association(context, plugin_constants.L3, drv.name, router['id']) @registry.receives(resources.ROUTER, [events.PRECOMMIT_DELETE]) def _clear_router_provider(self, resource, event, trigger, context, router_id, **kwargs): """Remove the association between a router and a service provider.""" self._stm.del_resource_associations(context, [router_id]) @registry.receives(resources.ROUTER, [events.PRECOMMIT_UPDATE]) def _update_router_provider(self, resource, event, trigger, payload=None): """Handle transition between providers. The provider can currently be changed only by the caller updating 'ha' and/or 'distributed' attributes. If we allow updates of flavor_id directly in the future those requests will also land here. """ drv = self.get_provider_for_router(payload.context, payload.resource_id) new_drv = None if _flavor_specified(payload.request_body): if (payload.request_body['flavor_id'] != payload.states[0]['flavor_id']): # TODO(kevinbenton): this is currently disallowed by the API # so we shouldn't hit it but this is a placeholder to add # support later. raise NotImplementedError() # the following is to support updating the 'ha' and 'distributed' # attributes via the API. try: _ensure_driver_supports_request(drv, payload.request_body) except lib_exc.InvalidInput: # the current driver does not support this request, we need to # migrate to a new provider. populate the distributed and ha # flags from the previous state if not in the update so we can # determine the target provider appropriately. # NOTE(kevinbenton): if the router is associated with a flavor # we bail because changing the provider without changing # the flavor will make things inconsistent. We can probably # update the flavor automatically in the future. if payload.states[0]['flavor_id']: raise lib_exc.InvalidInput(error_message=_( "Changing the 'ha' and 'distributed' attributes on a " "router associated with a flavor is not supported")) if 'distributed' not in payload.request_body: payload.request_body['distributed'] = (payload.states[0] ['distributed']) if 'ha' not in payload.request_body: payload.request_body['ha'] = payload.states[0]['ha'] LOG.debug("Get a provider driver handle based on the ha flag: " "%(ha_flag)s and distributed flag: %(distributed_flag)s", {'ha_flag': payload.request_body['ha'], 'distributed_flag': payload.request_body['distributed']}) new_drv = self._attrs_to_driver(payload.request_body) if new_drv: LOG.debug("Router %(id)s migrating from %(old)s provider to " "%(new)s provider.", {'id': payload.resource_id, 'old': drv, 'new': new_drv}) _ensure_driver_supports_request(new_drv, payload.request_body) # TODO(kevinbenton): notify old driver explicitly of driver change with payload.context.session.begin(subtransactions=True): self._stm.del_resource_associations( payload.context, [payload.resource_id]) self._stm.add_resource_association( payload.context, plugin_constants.L3, new_drv.name, payload.resource_id) def get_provider_for_router(self, context, router_id): """Return the provider driver handle for a router id.""" driver_name = self._stm.get_provider_names_by_resource_ids( context, [router_id]).get(router_id) if not driver_name: # this is an old router that hasn't been mapped to a provider # yet so we do this now router = self.l3_plugin.get_router(context, router_id) driver = self._attrs_to_driver(router) driver_name = driver.name self._stm.add_resource_association(context, plugin_constants.L3, driver_name, router_id) return self.drivers[driver_name] def _get_provider_for_create(self, context, router): """Get provider based on flavor or ha/distributed flags.""" if not _flavor_specified(router): return self._attrs_to_driver(router) return self._get_l3_driver_by_flavor(context, router['flavor_id']) def _get_l3_driver_by_flavor(self, context, flavor_id): """Get a provider driver handle for a given flavor_id.""" flavor = self._flavor_plugin.get_flavor(context, flavor_id) provider = self._flavor_plugin.get_flavor_next_provider( context, flavor['id'])[0] # TODO(kevinbenton): the callback framework suppresses the nice errors # these generate when they fail to lookup. carry them through driver = self.drivers[provider['provider']] return driver def _attrs_to_driver(self, router): """Get a provider driver handle based on the ha/distributed flags.""" distributed = _is_distributed( router.get('distributed', lib_const.ATTR_NOT_SPECIFIED)) ha = _is_ha(router.get('ha', lib_const.ATTR_NOT_SPECIFIED)) drivers = self.drivers.values() # make sure default is tried before the rest if defined if self.default_provider: drivers.insert(0, self.drivers[self.default_provider]) for driver in drivers: if _is_driver_compatible(distributed, ha, driver): return driver raise NotImplementedError( _("Could not find a service provider that supports " "distributed=%(d)s and ha=%(h)s") % {'d': distributed, 'h': ha} ) def uses_scheduler(self, context, router_id): """Returns True if the integrated L3 scheduler should be used.""" return (self.get_provider_for_router(context, router_id). use_integrated_agent_scheduler) class _LegacyPlusProviderConfiguration( provider_configuration.ProviderConfiguration): def __init__(self): # loads up ha, dvr, and single_node service providers automatically. # If an operator has setup explicit values that conflict with these, # the operator defined values will take priority. super(_LegacyPlusProviderConfiguration, self).__init__() for name, driver in (('dvrha', 'dvrha.DvrHaDriver'), ('dvr', 'dvr.DvrDriver'), ('ha', 'ha.HaDriver'), ('single_node', 'single_node.SingleNodeDriver')): path = 'neutron.services.l3_router.service_providers.%s' % driver try: self.add_provider({'service_type': plugin_constants.L3, 'name': name, 'driver': path, 'default': False}) except lib_exc.Invalid: LOG.debug("Could not add L3 provider '%s', it may have " "already been explicitly defined.", name) def _is_driver_compatible(distributed, ha, driver): if not driver.distributed_support.is_compatible(distributed): return False if not driver.ha_support.is_compatible(ha): return False return True def _is_distributed(distributed_attr): if distributed_attr is False: return False if distributed_attr == lib_const.ATTR_NOT_SPECIFIED: return cfg.CONF.router_distributed return True def _is_ha(ha_attr): if ha_attr is False: return False if ha_attr == lib_const.ATTR_NOT_SPECIFIED: return cfg.CONF.l3_ha return True def _flavor_specified(router): return ('flavor_id' in router and router['flavor_id'] != lib_const.ATTR_NOT_SPECIFIED) def _ensure_driver_supports_request(drv, router_body): r = router_body for key, attr in (('distributed', 'distributed_support'), ('ha', 'ha_support')): flag = r.get(key) if flag not in [True, False]: continue # not specified in body if not getattr(drv, attr).is_compatible(flag): raise lib_exc.InvalidInput(error_message=( _("Provider %(name)s does not support %(key)s=%(flag)s") % dict(name=drv.name, key=key, flag=flag))) neutron-12.1.1/neutron/services/l3_router/service_providers/single_node.py0000664000175000017500000000144013553660046027147 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.services.l3_router.service_providers import base class SingleNodeDriver(base.L3ServiceProvider): """Provider for single L3 agent routers.""" use_integrated_agent_scheduler = True neutron-12.1.1/neutron/services/l3_router/service_providers/dvr.py0000664000175000017500000000142213553660046025454 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.services.l3_router.service_providers import base class DvrDriver(base.L3ServiceProvider): distributed_support = base.MANDATORY use_integrated_agent_scheduler = True neutron-12.1.1/neutron/services/l3_router/service_providers/base.py0000664000175000017500000000466113553660047025604 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron._i18n import _ class _FeatureFlag(object): def is_compatible(self, value): if value == self.requires: return True if value and self.supports: return True return False def __init__(self, supports, requires): self.supports = supports self.requires = requires if requires and not supports: raise RuntimeError(_("A driver can't require a feature and not " "support it.")) UNSUPPORTED = _FeatureFlag(supports=False, requires=False) OPTIONAL = _FeatureFlag(supports=True, requires=False) MANDATORY = _FeatureFlag(supports=True, requires=True) class L3ServiceProvider(object): """Base class for L3 service provider drivers. On __init__ this will be given a handle to the l3 plugin. It is then the responsibility of the driver to subscribe to the events it is interested in (e.g. router_create, router_update, router_delete, etc). The 'ha' and 'distributed' attributes below are used to determine if a router request with the 'ha' or 'distributed' attribute can be supported by this particular driver. These attributes must be present. The 'use_integrated_agent_scheduler' flag indicates whether or not routers which belong to the driver should be automatically scheduled using the L3 agent scheduler integrated into Neutron. """ ha_support = UNSUPPORTED distributed_support = UNSUPPORTED use_integrated_agent_scheduler = False def __init__(self, l3plugin): self.l3plugin = l3plugin def owns_router(self, context, router_id): """Returns True if router is associated with driver, else False.""" if not router_id: return False return self.l3plugin.l3_driver_controller.get_provider_for_router( context, router_id) == self neutron-12.1.1/neutron/services/l3_router/service_providers/dvrha.py0000664000175000017500000000157713553660046026000 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.services.l3_router.service_providers import base from neutron.services.l3_router.service_providers import dvr from neutron.services.l3_router.service_providers import ha class DvrHaDriver(dvr.DvrDriver, ha.HaDriver): ha_support = base.MANDATORY dvr_support = base.MANDATORY neutron-12.1.1/neutron/services/l3_router/service_providers/__init__.py0000664000175000017500000000000013553660046026407 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/l3_router/service_providers/ha.py0000664000175000017500000000141013553660046025246 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.services.l3_router.service_providers import base class HaDriver(base.L3ServiceProvider): ha_support = base.MANDATORY use_integrated_agent_scheduler = True neutron-12.1.1/neutron/services/l3_router/l3_router_plugin.py0000664000175000017500000001426013553660047024425 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib import constants as n_const from neutron_lib.plugins import constants as plugin_constants from neutron_lib.services import base as service_base from oslo_config import cfg from oslo_log import helpers as log_helpers from oslo_log import log as logging from oslo_utils import importutils from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api from neutron.api.rpc.handlers import l3_rpc from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.db import _resource_extend as resource_extend from neutron.db import common_db_mixin from neutron.db import dns_db from neutron.db import extraroute_db from neutron.db import l3_dvr_ha_scheduler_db from neutron.db import l3_dvrscheduler_db from neutron.db import l3_fip_qos from neutron.db import l3_gwmode_db from neutron.db import l3_hamode_db from neutron.db import l3_hascheduler_db from neutron.db.models import l3 as l3_models from neutron.quota import resource_registry from neutron import service from neutron.services.l3_router.service_providers import driver_controller LOG = logging.getLogger(__name__) def disable_dvr_extension_by_config(aliases): if not cfg.CONF.enable_dvr: LOG.info('Disabled DVR extension.') if 'dvr' in aliases: aliases.remove('dvr') def disable_qos_fip_extension_by_plugins(aliases): qos_class = 'neutron.services.qos.qos_plugin.QoSPlugin' if all(p not in cfg.CONF.service_plugins for p in ['qos', qos_class]): if 'qos-fip' in aliases: aliases.remove('qos-fip') @resource_extend.has_resource_extenders class L3RouterPlugin(service_base.ServicePluginBase, common_db_mixin.CommonDbMixin, extraroute_db.ExtraRoute_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin, l3_gwmode_db.L3_NAT_db_mixin, l3_dvr_ha_scheduler_db.L3_DVR_HA_scheduler_db_mixin, dns_db.DNSDbMixin, l3_fip_qos.FloatingQoSDbMixin): """Implementation of the Neutron L3 Router Service Plugin. This class implements a L3 service plugin that provides router and floatingip resources and manages associated request/response. All DB related work is implemented in classes l3_db.L3_NAT_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin, l3_dvr_db.L3_NAT_with_dvr_db_mixin, and extraroute_db.ExtraRoute_db_mixin. """ _supported_extension_aliases = ["dvr", "router", "ext-gw-mode", "extraroute", "l3_agent_scheduler", "l3-ha", "router_availability_zone", "l3-flavors", "qos-fip"] __native_pagination_support = True __native_sorting_support = True @resource_registry.tracked_resources(router=l3_models.Router, floatingip=l3_models.FloatingIP) def __init__(self): self.router_scheduler = importutils.import_object( cfg.CONF.router_scheduler_driver) self.add_periodic_l3_agent_status_check() super(L3RouterPlugin, self).__init__() if 'dvr' in self.supported_extension_aliases: l3_dvrscheduler_db.subscribe() if 'l3-ha' in self.supported_extension_aliases: l3_hascheduler_db.subscribe() self.agent_notifiers.update( {n_const.AGENT_TYPE_L3: l3_rpc_agent_api.L3AgentNotifyAPI()}) rpc_worker = service.RpcWorker([self], worker_process_count=0) self.add_worker(rpc_worker) self.l3_driver_controller = driver_controller.DriverController(self) @property def supported_extension_aliases(self): if not hasattr(self, '_aliases'): aliases = self._supported_extension_aliases[:] disable_dvr_extension_by_config(aliases) disable_qos_fip_extension_by_plugins(aliases) self._aliases = aliases return self._aliases @log_helpers.log_method_call def start_rpc_listeners(self): # RPC support self.topic = topics.L3PLUGIN self.conn = n_rpc.create_connection() self.endpoints = [l3_rpc.L3RpcCallback()] self.conn.create_consumer(self.topic, self.endpoints, fanout=False) return self.conn.consume_in_threads() @classmethod def get_plugin_type(cls): return plugin_constants.L3 def get_plugin_description(self): """returns string description of the plugin.""" return ("L3 Router Service Plugin for basic L3 forwarding" " between (L2) Neutron networks and access to external" " networks via a NAT gateway.") def router_supports_scheduling(self, context, router_id): return self.l3_driver_controller.uses_scheduler(context, router_id) def create_floatingip(self, context, floatingip): """Create floating IP. :param context: Neutron request context :param floatingip: data for the floating IP being created :returns: A floating IP object on success As the l3 router plugin asynchronously creates floating IPs leveraging the l3 agent, the initial status for the floating IP object will be DOWN. """ return super(L3RouterPlugin, self).create_floatingip( context, floatingip, initial_status=n_const.FLOATINGIP_STATUS_DOWN) @staticmethod @resource_extend.extends([l3_apidef.ROUTERS]) def add_flavor_id(router_res, router_db): router_res['flavor_id'] = router_db['flavor_id'] neutron-12.1.1/neutron/services/l3_router/__init__.py0000664000175000017500000000000013553660046022652 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/l3_router/README0000664000175000017500000000242613553660046021437 0ustar zuulzuul00000000000000This service plugin implements the L3 routing functionality (resources router and floatingip) that in earlier releases before Havana was provided by core plugins (openvswitch, linuxbridge, ... etc). Core plugins can now choose not to implement L3 routing functionality and instead delegate that to the L3 routing service plugin. The required changes to a core plugin are in that case: - Do not inherit 'l3_db.L3_NAT_db_mixin' (or its descendants like extraroute) anymore. - Remove "router" from 'supported_extension_aliases'. - Modify any 'self' references to members in L3_NAT_db_mixin to instead use 'directory.get_plugin(constants.L3)' For example, self.prevent_l3_port_deletion(...) becomes something like plugin = directory.get_plugin(constants.L3) if plugin: plugin.prevent_l3_port_deletion(...) If the core plugin has relied on the L3Agent the following must also be changed: - Do not inherit 'l3_rpc_base.L3RpcCallbackMixin' in any '*RpcCallbacks' class. - Do not be a consumer of the topics.L3PLUGIN topic for RPC. To use the L3 routing service plugin, add 'neutron.services.l3_router.l3_router_plugin.L3RouterPlugin' to 'service_plugins' in '/etc/neutron/neutron.conf'. That is, service_plugins = neutron.services.l3_router.l3_router_plugin.L3RouterPlugin neutron-12.1.1/neutron/services/network_ip_availability/0000775000175000017500000000000013553660156023552 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/network_ip_availability/plugin.py0000664000175000017500000000441513553660047025425 0ustar zuulzuul00000000000000# Copyright 2016 GoDaddy. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib import exceptions from neutron.db import _utils as db_utils import neutron.db.db_base_plugin_v2 as db_base_plugin_v2 import neutron.db.network_ip_availability_db as ip_availability_db class NetworkIPAvailabilityPlugin(ip_availability_db.IpAvailabilityMixin, db_base_plugin_v2.NeutronDbPluginV2): """This plugin exposes IP availability data for networks and subnets.""" _instance = None supported_extension_aliases = ["network-ip-availability"] @classmethod def get_instance(cls): if cls._instance is None: cls._instance = cls() return cls._instance def get_plugin_description(self): return "Provides IP availability data for each network and subnet." @classmethod def get_plugin_type(cls): return "network-ip-availability" def get_network_ip_availabilities(self, context, filters=None, fields=None): """Returns ip availability data for a collection of networks.""" net_ip_availabilities = super(NetworkIPAvailabilityPlugin, self).get_network_ip_availabilities(context, filters) return [db_utils.resource_fields(net_ip_availability, fields) for net_ip_availability in net_ip_availabilities] def get_network_ip_availability(self, context, id=None, fields=None): """Return ip availability data for a specific network id.""" filters = {'network_id': [id]} result = self.get_network_ip_availabilities(context, filters) if result: return db_utils.resource_fields(result[0], fields) else: raise exceptions.NetworkNotFound(net_id=id) neutron-12.1.1/neutron/services/network_ip_availability/__init__.py0000664000175000017500000000000013553660046025647 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/loki/0000775000175000017500000000000013553660156017575 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/loki/loki_plugin.py0000664000175000017500000000335413553660047022467 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import random import time from neutron.db import api as db_api from neutron_lib.services import base as service_base from oslo_db import exception as db_exc from oslo_log import log as logging from sqlalchemy.orm import session as se LOG = logging.getLogger(__name__) class LokiPlugin(service_base.ServicePluginBase): """Loki brings us the gift of sporadic database failures and delays.""" def __init__(self): super(LokiPlugin, self).__init__() db_api.sqla_listen(se.Session, 'before_flush', self.random_deadlock) db_api.sqla_listen(se.Session, 'loaded_as_persistent', self.random_delay) def random_deadlock(self, session, flush_context, instances): if random.randrange(0, 51) > 49: # 1/50 probability raise db_exc.DBDeadlock() def random_delay(self, session, instance): if random.randrange(0, 201) > 199: # 1/200 probability LOG.debug("Loki has delayed loading of instance %s", instance) time.sleep(1) def get_plugin_type(self): return "loki" def get_plugin_description(self): return "Injects deadlocks and delays into database operations." neutron-12.1.1/neutron/services/loki/__init__.py0000664000175000017500000000000013553660046021672 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/__init__.py0000664000175000017500000000000013553660046020734 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/metering/0000775000175000017500000000000013553660156020451 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/metering/agents/0000775000175000017500000000000013553660156021732 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/metering/agents/metering_agent.py0000664000175000017500000002644713553660047025310 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from neutron_lib import constants from neutron_lib import context from neutron_lib.utils import runtime from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_service import loopingcall from oslo_service import periodic_task from oslo_service import service from oslo_utils import timeutils from neutron._i18n import _ from neutron.agent import rpc as agent_rpc from neutron.common import config as common_config from neutron.common import constants as n_const from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.conf.agent import common as config from neutron.conf.services import metering_agent from neutron import manager from neutron import service as neutron_service from neutron.services.metering.drivers import utils as driverutils LOG = logging.getLogger(__name__) class MeteringPluginRpc(object): def __init__(self, host): # NOTE(yamamoto): super.__init__() call here is not only for # aesthetics. Because of multiple inheritances in MeteringAgent, # it's actually necessary to initialize parent classes of # manager.Manager correctly. super(MeteringPluginRpc, self).__init__(host) target = oslo_messaging.Target(topic=topics.METERING_PLUGIN, version='1.0') self.client = n_rpc.get_client(target) def _get_sync_data_metering(self, context): try: cctxt = self.client.prepare() return cctxt.call(context, 'get_sync_data_metering', host=self.host) except Exception: LOG.exception("Failed synchronizing routers") class MeteringAgent(MeteringPluginRpc, manager.Manager): def __init__(self, host, conf=None): self.conf = conf or cfg.CONF self._load_drivers() self.context = context.get_admin_context_without_session() self.metering_loop = loopingcall.FixedIntervalLoopingCall( self._metering_loop ) measure_interval = self.conf.measure_interval self.last_report = 0 self.metering_loop.start(interval=measure_interval) self.host = host self.label_tenant_id = {} self.routers = {} self.metering_infos = {} super(MeteringAgent, self).__init__(host=host) def _load_drivers(self): """Loads plugin-driver from configuration.""" LOG.info("Loading Metering driver %s", self.conf.driver) if not self.conf.driver: raise SystemExit(_('A metering driver must be specified')) self.metering_driver = driverutils.load_metering_driver(self, self.conf) def _metering_notification(self): for label_id, info in self.metering_infos.items(): data = {'label_id': label_id, 'tenant_id': self.label_tenant_id.get(label_id), 'pkts': info['pkts'], 'bytes': info['bytes'], 'time': info['time'], 'first_update': info['first_update'], 'last_update': info['last_update'], 'host': self.host} LOG.debug("Send metering report: %s", data) notifier = n_rpc.get_notifier('metering') notifier.info(self.context, 'l3.meter', data) info['pkts'] = 0 info['bytes'] = 0 info['time'] = 0 def _purge_metering_info(self): deadline_timestamp = timeutils.utcnow_ts() - self.conf.report_interval label_ids = [ label_id for label_id, info in self.metering_infos.items() if info['last_update'] < deadline_timestamp] for label_id in label_ids: del self.metering_infos[label_id] def _add_metering_info(self, label_id, pkts, bytes): ts = timeutils.utcnow_ts() info = self.metering_infos.get(label_id, {'bytes': 0, 'pkts': 0, 'time': 0, 'first_update': ts, 'last_update': ts}) info['bytes'] += bytes info['pkts'] += pkts info['time'] += ts - info['last_update'] info['last_update'] = ts self.metering_infos[label_id] = info return info def _add_metering_infos(self): self.label_tenant_id = {} for router in self.routers.values(): tenant_id = router['tenant_id'] labels = router.get(n_const.METERING_LABEL_KEY, []) for label in labels: label_id = label['id'] self.label_tenant_id[label_id] = tenant_id accs = self._get_traffic_counters(self.context, self.routers.values()) if not accs: return for label_id, acc in accs.items(): self._add_metering_info(label_id, acc['pkts'], acc['bytes']) def _metering_loop(self): self._add_metering_infos() ts = timeutils.utcnow_ts() delta = ts - self.last_report report_interval = self.conf.report_interval if delta >= report_interval: self._metering_notification() self._purge_metering_info() self.last_report = ts @runtime.synchronized('metering-agent') def _invoke_driver(self, context, meterings, func_name): try: return getattr(self.metering_driver, func_name)(context, meterings) except AttributeError: LOG.exception("Driver %(driver)s does not implement %(func)s", {'driver': self.conf.driver, 'func': func_name}) except RuntimeError: LOG.exception("Driver %(driver)s:%(func)s runtime error", {'driver': self.conf.driver, 'func': func_name}) @periodic_task.periodic_task(run_immediately=True) def _sync_routers_task(self, context): routers = self._get_sync_data_metering(self.context) routers_on_agent = set(self.routers.keys()) routers_on_server = set( [router['id'] for router in routers] if routers else []) for router_id in routers_on_agent - routers_on_server: del self.routers[router_id] self._invoke_driver(context, router_id, 'remove_router') if not routers: return self._update_routers(context, routers) def router_deleted(self, context, router_id): self._add_metering_infos() if router_id in self.routers: del self.routers[router_id] return self._invoke_driver(context, router_id, 'remove_router') def routers_updated(self, context, routers=None): if not routers: routers = self._get_sync_data_metering(self.context) if not routers: return self._update_routers(context, routers) def _update_routers(self, context, routers): for router in routers: self.routers[router['id']] = router return self._invoke_driver(context, routers, 'update_routers') def _get_traffic_counters(self, context, routers): LOG.debug("Get router traffic counters") return self._invoke_driver(context, routers, 'get_traffic_counters') def add_metering_label_rule(self, context, routers): return self._invoke_driver(context, routers, 'add_metering_label_rule') def remove_metering_label_rule(self, context, routers): return self._invoke_driver(context, routers, 'remove_metering_label_rule') def update_metering_label_rules(self, context, routers): LOG.debug("Update metering rules from agent") return self._invoke_driver(context, routers, 'update_metering_label_rules') def add_metering_label(self, context, routers): LOG.debug("Creating a metering label from agent") return self._invoke_driver(context, routers, 'add_metering_label') def remove_metering_label(self, context, routers): self._add_metering_infos() LOG.debug("Delete a metering label from agent") return self._invoke_driver(context, routers, 'remove_metering_label') class MeteringAgentWithStateReport(MeteringAgent): def __init__(self, host, conf=None): super(MeteringAgentWithStateReport, self).__init__(host=host, conf=conf) self.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS) self.agent_state = { 'binary': 'neutron-metering-agent', 'host': host, 'topic': topics.METERING_AGENT, 'configurations': { 'metering_driver': self.conf.driver, 'measure_interval': self.conf.measure_interval, 'report_interval': self.conf.report_interval }, 'start_flag': True, 'agent_type': constants.AGENT_TYPE_METERING} report_interval = cfg.CONF.AGENT.report_interval self.use_call = True if report_interval: self.heartbeat = loopingcall.FixedIntervalLoopingCall( self._report_state) self.heartbeat.start(interval=report_interval) def _report_state(self): try: self.state_rpc.report_state(self.context, self.agent_state, self.use_call) self.agent_state.pop('start_flag', None) self.use_call = False except AttributeError: # This means the server does not support report_state LOG.warning("Neutron server does not support state report. " "State report for this agent will be disabled.") self.heartbeat.stop() except Exception: LOG.exception("Failed reporting state!") def agent_updated(self, context, payload): LOG.info("agent_updated by server side %s!", payload) def main(): conf = cfg.CONF metering_agent.register_metering_agent_opts() config.register_agent_state_opts_helper(conf) common_config.init(sys.argv[1:]) config.setup_logging() config.setup_privsep() server = neutron_service.Service.create( binary='neutron-metering-agent', topic=topics.METERING_AGENT, report_interval=cfg.CONF.AGENT.report_interval, manager='neutron.services.metering.agents.' 'metering_agent.MeteringAgentWithStateReport') service.launch(cfg.CONF, server).wait() neutron-12.1.1/neutron/services/metering/agents/__init__.py0000664000175000017500000000000013553660046024027 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/metering/drivers/0000775000175000017500000000000013553660156022127 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/metering/drivers/noop/0000775000175000017500000000000013553660156023102 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/metering/drivers/noop/noop_driver.py0000664000175000017500000000307213553660047026003 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import helpers as log_helpers from neutron.services.metering.drivers import abstract_driver class NoopMeteringDriver(abstract_driver.MeteringAbstractDriver): @log_helpers.log_method_call def update_routers(self, context, routers): pass @log_helpers.log_method_call def remove_router(self, context, router_id): pass @log_helpers.log_method_call def update_metering_label_rules(self, context, routers): pass @log_helpers.log_method_call def add_metering_label_rule(self, context, routers): pass @log_helpers.log_method_call def remove_metering_label_rule(self, context, routers): pass @log_helpers.log_method_call def add_metering_label(self, context, routers): pass @log_helpers.log_method_call def remove_metering_label(self, context, routers): pass @log_helpers.log_method_call def get_traffic_counters(self, context, routers): pass neutron-12.1.1/neutron/services/metering/drivers/noop/__init__.py0000664000175000017500000000000013553660046025177 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/metering/drivers/utils.py0000664000175000017500000000242613553660046023643 0ustar zuulzuul00000000000000# Copyright 2017 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.utils import runtime from oslo_log import log as logging LOG = logging.getLogger(__name__) METERING_NAMESPACE = 'neutron.services.metering_drivers' def load_metering_driver(plugin, conf): """Load metering driver :param plugin: the metering plugin :param conf: driver configuration object :raises SystemExit of 1 if driver cannot be loaded """ try: loaded_class = runtime.load_class_by_alias_or_classname( METERING_NAMESPACE, conf.driver) return loaded_class(plugin, conf) except ImportError: LOG.error("Error loading metering driver '%s'", conf.driver) raise SystemExit(1) neutron-12.1.1/neutron/services/metering/drivers/__init__.py0000664000175000017500000000000013553660046024224 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/metering/drivers/iptables/0000775000175000017500000000000013553660156023732 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/metering/drivers/iptables/iptables_driver.py0000664000175000017500000004201713553660047027465 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from oslo_log import helpers as log_helpers from oslo_log import log as logging from neutron.agent.common import utils as common_utils from neutron.agent.l3 import dvr_snat_ns from neutron.agent.l3 import namespaces from neutron.agent.linux import ip_lib from neutron.agent.linux import iptables_manager from neutron.common import constants from neutron.common import ipv6_utils from neutron.conf.agent import common as config from neutron.services.metering.drivers import abstract_driver LOG = logging.getLogger(__name__) NS_PREFIX = 'qrouter-' WRAP_NAME = 'neutron-meter' EXTERNAL_DEV_PREFIX = 'qg-' ROUTER_2_FIP_DEV_PREFIX = namespaces.ROUTER_2_FIP_DEV_PREFIX TOP_CHAIN = WRAP_NAME + "-FORWARD" RULE = '-r-' LABEL = '-l-' config.register_interface_driver_opts_helper(cfg.CONF) config.register_interface_opts() class IptablesManagerTransaction(object): __transactions = {} def __init__(self, im): self.im = im transaction = self.__transactions.get(im, 0) transaction += 1 self.__transactions[im] = transaction def __enter__(self): return self.im def __exit__(self, type, value, traceback): transaction = self.__transactions.get(self.im) if transaction == 1: self.im.apply() del self.__transactions[self.im] else: transaction -= 1 self.__transactions[self.im] = transaction class RouterWithMetering(object): def __init__(self, conf, router): self.conf = conf self.id = router['id'] self.router = router # TODO(cbrandily): deduplicate ns_name generation in metering/l3 self.ns_name = NS_PREFIX + self.id self.iptables_manager = None self.snat_iptables_manager = None if self.router['distributed']: # If distributed routers then we need to apply the # metering agent label rules in the snat namespace as well. snat_ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name( self.id) # Check for namespace existence before we assign the # snat_iptables_manager if ip_lib.network_namespace_exists(snat_ns_name): self.snat_iptables_manager = iptables_manager.IptablesManager( namespace=snat_ns_name, binary_name=WRAP_NAME, state_less=True, use_ipv6=ipv6_utils.is_enabled_and_bind_by_default()) # Check of namespace existence before we assign the iptables_manager # NOTE(Swami): If distributed routers, all external traffic on a # compute node will flow through the rfp interface in the router # namespace. if ip_lib.network_namespace_exists(self.ns_name): self.iptables_manager = iptables_manager.IptablesManager( namespace=self.ns_name, binary_name=WRAP_NAME, state_less=True, use_ipv6=ipv6_utils.is_enabled_and_bind_by_default()) self.metering_labels = {} class IptablesMeteringDriver(abstract_driver.MeteringAbstractDriver): def __init__(self, plugin, conf): self.plugin = plugin self.conf = conf or cfg.CONF self.routers = {} self.driver = common_utils.load_interface_driver(self.conf) def _update_router(self, router): r = self.routers.get(router['id'], RouterWithMetering(self.conf, router)) r.router = router self.routers[r.id] = r return r @log_helpers.log_method_call def update_routers(self, context, routers): # disassociate removed routers router_ids = set(router['id'] for router in routers) for router_id, rm in self.routers.items(): if router_id not in router_ids: self._process_disassociate_metering_label(rm.router) for router in routers: old_gw_port_id = None old_rm = self.routers.get(router['id']) if old_rm: old_gw_port_id = old_rm.router['gw_port_id'] gw_port_id = router['gw_port_id'] if gw_port_id != old_gw_port_id: if old_rm: if router.get('distributed'): old_rm_im = old_rm.snat_iptables_manager else: old_rm_im = old_rm.iptables_manager with IptablesManagerTransaction(old_rm_im): self._process_disassociate_metering_label(router) if gw_port_id: self._process_associate_metering_label(router) elif gw_port_id: self._process_associate_metering_label(router) @log_helpers.log_method_call def remove_router(self, context, router_id): if router_id in self.routers: del self.routers[router_id] def get_external_device_names(self, rm): gw_port_id = rm.router.get('gw_port_id') if not gw_port_id: return None, None # NOTE (Swami): External device 'qg' should be used on the # Router namespace if the router is legacy and should be used on # SNAT namespace if the router is distributed. ext_dev = (EXTERNAL_DEV_PREFIX + gw_port_id)[:self.driver.DEV_NAME_LEN] ext_snat_dev = (ROUTER_2_FIP_DEV_PREFIX + rm.id)[:self.driver.DEV_NAME_LEN] return ext_dev, ext_snat_dev def _process_metering_label_rules(self, rules, label_chain, rules_chain, ext_dev, im): if not ext_dev: return for rule in rules: self._add_rule_to_chain(ext_dev, rule, im, label_chain, rules_chain) def _process_metering_label_rule_add(self, rule, ext_dev, label_chain, rules_chain, im): self._add_rule_to_chain(ext_dev, rule, im, label_chain, rules_chain) def _process_metering_label_rule_delete(self, rule, ext_dev, label_chain, rules_chain, im): self._remove_rule_from_chain(ext_dev, rule, im, label_chain, rules_chain) def _add_rule_to_chain(self, ext_dev, rule, im, label_chain, rules_chain): ipt_rule = self._prepare_rule(ext_dev, rule, label_chain) if rule['excluded']: im.ipv4['filter'].add_rule(rules_chain, ipt_rule, wrap=False, top=True) else: im.ipv4['filter'].add_rule(rules_chain, ipt_rule, wrap=False, top=False) def _remove_rule_from_chain(self, ext_dev, rule, im, label_chain, rules_chain): ipt_rule = self._prepare_rule(ext_dev, rule, label_chain) if rule['excluded']: im.ipv4['filter'].remove_rule(rules_chain, ipt_rule, wrap=False, top=True) else: im.ipv4['filter'].remove_rule(rules_chain, ipt_rule, wrap=False, top=False) def _prepare_rule(self, ext_dev, rule, label_chain): remote_ip = rule['remote_ip_prefix'] if rule['direction'] == 'egress': dir_opt = '-s %s -o %s' % (remote_ip, ext_dev) else: dir_opt = '-d %s -i %s' % (remote_ip, ext_dev) if rule['excluded']: ipt_rule = '%s -j RETURN' % dir_opt else: ipt_rule = '%s -j %s' % (dir_opt, label_chain) return ipt_rule def _process_ns_specific_metering_label(self, router, ext_dev, im): '''Process metering label based on the associated namespaces.''' rm = self.routers.get(router['id']) with IptablesManagerTransaction(im): labels = router.get(constants.METERING_LABEL_KEY, []) for label in labels: label_id = label['id'] label_chain = iptables_manager.get_chain_name( WRAP_NAME + LABEL + label_id, wrap=False) rules_chain = iptables_manager.get_chain_name( WRAP_NAME + RULE + label_id, wrap=False) exists = rm.metering_labels.get(label_id) if not exists: self._create_metering_label_chain(rm, label_chain, rules_chain) rm.metering_labels[label_id] = label rules = label.get('rules') if rules: self._process_metering_label_rules( rules, label_chain, rules_chain, ext_dev, im) def _process_associate_metering_label(self, router): self._update_router(router) rm = self.routers.get(router['id']) ext_dev, ext_snat_dev = self.get_external_device_names(rm) for (im, dev) in [(rm.iptables_manager, ext_dev), (rm.snat_iptables_manager, ext_snat_dev)]: if im: self._process_ns_specific_metering_label(router, dev, im) def _process_ns_specific_disassociate_metering_label(self, router, im): '''Disassociate metering label based on specific namespaces.''' rm = self.routers.get(router['id']) with IptablesManagerTransaction(im): labels = router.get(constants.METERING_LABEL_KEY, []) for label in labels: label_id = label['id'] if label_id not in rm.metering_labels: continue label_chain = iptables_manager.get_chain_name( WRAP_NAME + LABEL + label_id, wrap=False) rules_chain = iptables_manager.get_chain_name( WRAP_NAME + RULE + label_id, wrap=False) im.ipv4['filter'].remove_chain(label_chain, wrap=False) im.ipv4['filter'].remove_chain(rules_chain, wrap=False) def _process_disassociate_metering_label(self, router): rm = self.routers.get(router['id']) if not rm: return for im in [rm.iptables_manager, rm.snat_iptables_manager]: if im: self._process_ns_specific_disassociate_metering_label( router, im) labels = router.get(constants.METERING_LABEL_KEY, []) for label in labels: label_id = label['id'] del rm.metering_labels[label_id] @log_helpers.log_method_call def add_metering_label(self, context, routers): for router in routers: self._process_associate_metering_label(router) @log_helpers.log_method_call def add_metering_label_rule(self, context, routers): for router in routers: self._add_metering_label_rule(router) @log_helpers.log_method_call def remove_metering_label_rule(self, context, routers): for router in routers: self._remove_metering_label_rule(router) @log_helpers.log_method_call def update_metering_label_rules(self, context, routers): for router in routers: self._update_metering_label_rules(router) def _add_metering_label_rule(self, router): self._process_metering_rule_action(router, 'create') def _remove_metering_label_rule(self, router): self._process_metering_rule_action(router, 'delete') def _create_metering_label_chain(self, rm, label_chain, rules_chain): rm.iptables_manager.ipv4['filter'].add_chain(label_chain, wrap=False) rm.iptables_manager.ipv4['filter'].add_chain(rules_chain, wrap=False) rm.iptables_manager.ipv4['filter'].add_rule( TOP_CHAIN, '-j ' + rules_chain, wrap=False) rm.iptables_manager.ipv4['filter'].add_rule( label_chain, '', wrap=False) def _process_metering_rule_action_based_on_ns( self, router, action, ext_dev, im): '''Process metering rule actions based specific namespaces.''' rm = self.routers.get(router['id']) with IptablesManagerTransaction(im): labels = router.get(constants.METERING_LABEL_KEY, []) for label in labels: label_id = label['id'] label_chain = iptables_manager.get_chain_name( WRAP_NAME + LABEL + label_id, wrap=False) rules_chain = iptables_manager.get_chain_name( WRAP_NAME + RULE + label_id, wrap=False) exists = rm.metering_labels.get(label_id) if action == 'create' and not exists: self._create_metering_label_chain(rm, label_chain, rules_chain) rm.metering_labels[label_id] = label rule = label.get('rule') if rule: if action == 'create': self._process_metering_label_rule_add( rule, ext_dev, label_chain, rules_chain, im) elif action == 'delete': self._process_metering_label_rule_delete( rule, ext_dev, label_chain, rules_chain, im) def _process_metering_rule_action(self, router, action): rm = self.routers.get(router['id']) if not rm: return ext_dev, ext_snat_dev = self.get_external_device_names(rm) for (im, dev) in [(rm.iptables_manager, ext_dev), (rm.snat_iptables_manager, ext_snat_dev)]: if im and dev: self._process_metering_rule_action_based_on_ns( router, action, dev, im) def _update_metering_label_rules_based_on_ns(self, router, ext_dev, im): '''Update metering lable rules based on namespace.''' with IptablesManagerTransaction(im): labels = router.get(constants.METERING_LABEL_KEY, []) for label in labels: label_id = label['id'] label_chain = iptables_manager.get_chain_name( WRAP_NAME + LABEL + label_id, wrap=False) rules_chain = iptables_manager.get_chain_name( WRAP_NAME + RULE + label_id, wrap=False) im.ipv4['filter'].empty_chain(rules_chain, wrap=False) rules = label.get('rules') if rules: self._process_metering_label_rules( rules, label_chain, rules_chain, ext_dev, im) def _update_metering_label_rules(self, router): rm = self.routers.get(router['id']) if not rm: return ext_dev, ext_snat_dev = self.get_external_device_names(rm) for (im, dev) in [(rm.iptables_manager, ext_dev), (rm.snat_iptables_manager, ext_snat_dev)]: if im and dev: self._update_metering_label_rules_based_on_ns(router, dev, im) @log_helpers.log_method_call def remove_metering_label(self, context, routers): for router in routers: self._process_disassociate_metering_label(router) @log_helpers.log_method_call def get_traffic_counters(self, context, routers): accs = {} routers_to_reconfigure = set() for router in routers: rm = self.routers.get(router['id']) if not rm: continue for label_id in rm.metering_labels: try: chain = iptables_manager.get_chain_name(WRAP_NAME + LABEL + label_id, wrap=False) chain_acc = rm.iptables_manager.get_traffic_counters( chain, wrap=False, zero=True) except RuntimeError: LOG.exception('Failed to get traffic counters, ' 'router: %s', router) routers_to_reconfigure.add(router['id']) continue if not chain_acc: continue acc = accs.get(label_id, {'pkts': 0, 'bytes': 0}) acc['pkts'] += chain_acc['pkts'] acc['bytes'] += chain_acc['bytes'] accs[label_id] = acc for router_id in routers_to_reconfigure: del self.routers[router_id] return accs neutron-12.1.1/neutron/services/metering/drivers/iptables/__init__.py0000664000175000017500000000000013553660046026027 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/metering/drivers/abstract_driver.py0000664000175000017500000000247113553660047025662 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import six @six.add_metaclass(abc.ABCMeta) class MeteringAbstractDriver(object): """Abstract Metering driver.""" def __init__(self, plugin, conf): pass @abc.abstractmethod def update_routers(self, context, routers): pass @abc.abstractmethod def remove_router(self, context, router_id): pass @abc.abstractmethod def update_metering_label_rules(self, context, routers): pass @abc.abstractmethod def add_metering_label(self, context, routers): pass @abc.abstractmethod def remove_metering_label(self, context, routers): pass @abc.abstractmethod def get_traffic_counters(self, context, routers): pass neutron-12.1.1/neutron/services/metering/metering_plugin.py0000664000175000017500000000555213553660047024221 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import metering as metering_apidef from neutron.api.rpc.agentnotifiers import metering_rpc_agent_api from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.db.metering import metering_db from neutron.db.metering import metering_rpc from neutron import service class MeteringPlugin(metering_db.MeteringDbMixin): """Implementation of the Neutron Metering Service Plugin.""" supported_extension_aliases = [metering_apidef.ALIAS] path_prefix = "/metering" def __init__(self): super(MeteringPlugin, self).__init__() self.meter_rpc = metering_rpc_agent_api.MeteringAgentNotifyAPI() rpc_worker = service.RpcWorker([self], worker_process_count=0) self.add_worker(rpc_worker) def start_rpc_listeners(self): self.endpoints = [metering_rpc.MeteringRpcCallbacks(self)] self.conn = n_rpc.create_connection() self.conn.create_consumer( topics.METERING_PLUGIN, self.endpoints, fanout=False) return self.conn.consume_in_threads() def create_metering_label(self, context, metering_label): label = super(MeteringPlugin, self).create_metering_label( context, metering_label) data = self.get_sync_data_metering(context) self.meter_rpc.add_metering_label(context, data) return label def delete_metering_label(self, context, label_id): data = self.get_sync_data_metering(context, label_id) label = super(MeteringPlugin, self).delete_metering_label( context, label_id) self.meter_rpc.remove_metering_label(context, data) return label def create_metering_label_rule(self, context, metering_label_rule): rule = super(MeteringPlugin, self).create_metering_label_rule( context, metering_label_rule) data = self.get_sync_data_for_rule(context, rule) self.meter_rpc.add_metering_label_rule(context, data) return rule def delete_metering_label_rule(self, context, rule_id): rule = super(MeteringPlugin, self).delete_metering_label_rule( context, rule_id) data = self.get_sync_data_for_rule(context, rule) self.meter_rpc.remove_metering_label_rule(context, data) return rule neutron-12.1.1/neutron/services/metering/__init__.py0000664000175000017500000000000013553660046022546 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/rbac/0000775000175000017500000000000013553660156017546 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/rbac/__init__.py0000664000175000017500000000000013553660046021643 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/0000775000175000017500000000000013553660156020002 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/seg_types/0000775000175000017500000000000013553660156022004 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/seg_types/validators.py0000664000175000017500000000316713553660047024534 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron._i18n import _ from neutron.plugins.common import utils from neutron.services.trunk import constants as trunk_consts # Base map of segmentation types supported with their respective validator # functions. In multi-driver deployments all drivers must support the same # set of segmentation types consistently. Drivers can add their own type # and respective validator, however this is a configuration that may be # supported only in single-driver deployments. _supported = { trunk_consts.VLAN: utils.is_valid_vlan_tag, } def get_validator(segmentation_type): """Get validator for the segmentation type or KeyError if not found.""" return _supported[segmentation_type] def add_validator(segmentation_type, validator_function): """Introduce new entry to the map of supported segmentation types.""" if segmentation_type in _supported: msg = _("Cannot redefine existing %s " "segmentation type") % segmentation_type raise KeyError(msg) _supported[segmentation_type] = validator_function neutron-12.1.1/neutron/services/trunk/seg_types/__init__.py0000664000175000017500000000000013553660046024101 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/rpc/0000775000175000017500000000000013553660156020566 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/rpc/backend.py0000664000175000017500000000510313553660047022525 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from oslo_log import log as logging from neutron.services.trunk import constants as trunk_consts from neutron.services.trunk.rpc import server LOG = logging.getLogger(__name__) @registry.has_registry_receivers class ServerSideRpcBackend(object): """The Neutron Server RPC backend.""" def __init__(self): """Initialize an RPC backend for the Neutron Server.""" self._skeleton = server.TrunkSkeleton() self._stub = server.TrunkStub() LOG.debug("RPC backend initialized for trunk plugin") # Set up listeners to trunk events: they dispatch RPC messages # to agents as needed. These are designed to work with any # agent-based driver that may integrate with the trunk service # plugin, e.g. linux bridge or ovs. @registry.receives(trunk_consts.TRUNK, [events.AFTER_CREATE, events.AFTER_DELETE]) @registry.receives(trunk_consts.SUBPORTS, [events.AFTER_CREATE, events.AFTER_DELETE]) def process_event(self, resource, event, trunk_plugin, payload): """Emit RPC notifications to registered subscribers.""" context = payload.context LOG.debug("RPC notification needed for trunk %s", payload.trunk_id) if resource == trunk_consts.SUBPORTS: payload = payload.subports method = { events.AFTER_CREATE: self._stub.subports_added, events.AFTER_DELETE: self._stub.subports_deleted, } elif resource == trunk_consts.TRUNK: # On AFTER_DELETE event, current_trunk is None payload = payload.current_trunk or payload.original_trunk method = { events.AFTER_CREATE: self._stub.trunk_created, events.AFTER_DELETE: self._stub.trunk_deleted, } LOG.debug("Emitting event %s for resource %s", event, resource) method[event](context, payload) neutron-12.1.1/neutron/services/trunk/rpc/agent.py0000664000175000017500000001054313553660047022240 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from oslo_log import helpers as log_helpers import oslo_messaging from neutron.api.rpc.callbacks.consumer import registry from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc from neutron.common import rpc as n_rpc from neutron.services.trunk.rpc import constants as trunk_consts # This module contains stub (client-side) and skeleton (server-side) # proxy code that executes in the Neutron L2 Agent process space. This # is needed if trunk service plugin drivers have a remote component # (e.g. agent), that needs to communicate with the Neutron Server. # The Agent side exposes the following remote methods: # # - update methods to learn about a trunk and its subports: these # methods are used by the server to tell the agent about trunk # updates; agents may selectively choose to listen to either # trunk or subports updates or both. # # For server-side stub and skeleton proxy code, please look at server.py class TrunkSkeleton(object): """Skeleton proxy code for server->agent communication.""" def __init__(self): registry.register(self.handle_trunks, resources.TRUNK) registry.register(self.handle_subports, resources.SUBPORT) self._connection = n_rpc.create_connection() endpoints = [resources_rpc.ResourcesPushRpcCallback()] topic = resources_rpc.resource_type_versioned_topic(resources.SUBPORT) self._connection.create_consumer(topic, endpoints, fanout=True) topic = resources_rpc.resource_type_versioned_topic(resources.TRUNK) self._connection.create_consumer(topic, endpoints, fanout=True) self._connection.consume_in_threads() @abc.abstractmethod def handle_trunks(self, context, resource_type, trunks, event_type): """Handle trunk events.""" # if common logic may be extracted out, consider making a base # version of this method that can be overridden by the inherited # skeleton. # NOTE: If trunk is not managed by the agent, the notification can # either be ignored or cached for future use. @abc.abstractmethod def handle_subports(self, context, resource_type, subports, event_type): """Handle subports event.""" # if common logic may be extracted out, consider making a base # version of this method that can be overridden by the inherited # skeleton. # NOTE: If the subport belongs to a trunk which the agent does not # manage, the notification should be ignored. class TrunkStub(object): """Stub proxy code for agent->server communication.""" # API HISTORY # 1.0 - initial version VERSION = '1.0' def __init__(self): self.stub = resources_rpc.ResourcesPullRpcApi() target = oslo_messaging.Target( topic=trunk_consts.TRUNK_BASE_TOPIC, version=self.VERSION, namespace=trunk_consts.TRUNK_BASE_NAMESPACE) self.rpc_client = n_rpc.get_client(target) @log_helpers.log_method_call def get_trunk_details(self, context, parent_port_id): """Get information about the trunk for the given parent port.""" return self.stub.pull(context, resources.TRUNK, parent_port_id) @log_helpers.log_method_call def update_trunk_status(self, context, trunk_id, status): """Update the trunk status to reflect outcome of data plane wiring.""" return self.rpc_client.prepare().call( context, 'update_trunk_status', trunk_id=trunk_id, status=status) @log_helpers.log_method_call def update_subport_bindings(self, context, subports): """Update subport bindings to match parent port host binding.""" return self.rpc_client.prepare().call( context, 'update_subport_bindings', subports=subports) neutron-12.1.1/neutron/services/trunk/rpc/server.py0000664000175000017500000002056013553660047022450 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from neutron_lib.api.definitions import portbindings from neutron_lib.plugins import directory from oslo_log import helpers as log_helpers from oslo_log import log as logging import oslo_messaging from sqlalchemy.orm import exc from neutron.api.rpc.callbacks import events from neutron.api.rpc.callbacks.producer import registry from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc from neutron.common import rpc as n_rpc from neutron.db import api as db_api from neutron.objects import trunk as trunk_objects from neutron.services.trunk import constants as trunk_consts from neutron.services.trunk import exceptions as trunk_exc from neutron.services.trunk.rpc import constants LOG = logging.getLogger(__name__) # This module contains stub (client-side) and skeleton (server-side) # proxy code that executes in the Neutron server process space. This # is needed if any of the trunk service plugin drivers has a remote # component (e.g. agent), that needs to communicate with the Neutron # Server. # The Server side exposes the following remote methods: # # - lookup method to retrieve trunk details: used by the agent to learn # about the trunk. # - update methods for trunk and its subports: used by the agent to # inform the server about local trunk status changes. # # For agent-side stub and skeleton proxy code, please look at agent.py def trunk_by_port_provider(resource, port_id, context, **kwargs): """Provider callback to supply trunk information by parent port.""" return trunk_objects.Trunk.get_object(context, port_id=port_id) class TrunkSkeleton(object): """Skeleton proxy code for agent->server communication.""" # API version history: # 1.0 Initial version target = oslo_messaging.Target(version='1.0', namespace=constants.TRUNK_BASE_NAMESPACE) _core_plugin = None def __init__(self): # Used to provide trunk lookups for the agent. registry.provide(trunk_by_port_provider, resources.TRUNK) self._connection = n_rpc.create_connection() self._connection.create_consumer( constants.TRUNK_BASE_TOPIC, [self], fanout=False) self._connection.consume_in_threads() @property def core_plugin(self): if not self._core_plugin: self._core_plugin = directory.get_plugin() return self._core_plugin @log_helpers.log_method_call def update_subport_bindings(self, context, subports): """Update subport bindings to match trunk host binding.""" el = context.elevated() ports_by_trunk_id = collections.defaultdict(list) updated_ports = collections.defaultdict(list) for s in subports: ports_by_trunk_id[s['trunk_id']].append(s['port_id']) for trunk_id, subport_ids in ports_by_trunk_id.items(): trunk = trunk_objects.Trunk.get_object(el, id=trunk_id) if not trunk: LOG.debug("Trunk not found. id: %s", trunk_id) continue trunk_updated_ports = self._process_trunk_subport_bindings( el, trunk, subport_ids) updated_ports[trunk.id].extend(trunk_updated_ports) return updated_ports def update_trunk_status(self, context, trunk_id, status): """Update the trunk status to reflect outcome of data plane wiring.""" with db_api.autonested_transaction(context.session): trunk = trunk_objects.Trunk.get_object(context, id=trunk_id) if trunk: trunk.update(status=status) def _process_trunk_subport_bindings(self, context, trunk, port_ids): """Process port bindings for subports on the given trunk.""" updated_ports = [] trunk_port_id = trunk.port_id trunk_port = self.core_plugin.get_port(context, trunk_port_id) trunk_host = trunk_port.get(portbindings.HOST_ID) for try_cnt in range(db_api.MAX_RETRIES): try: # NOTE(status_police) Set the trunk in BUILD state before # processing subport bindings. The trunk will stay in BUILD # state until an attempt has been made to bind all subports # passed here and the agent acknowledges the operation was # successful. trunk.update(status=trunk_consts.BUILD_STATUS) break except exc.StaleDataError as e: if try_cnt < db_api.MAX_RETRIES - 1: LOG.debug("Got StaleDataError exception: %s", e) continue else: # re-raise when all tries failed raise for port_id in port_ids: try: updated_port = self._handle_port_binding(context, port_id, trunk, trunk_host) # NOTE(fitoduarte): consider trimming down the content # of the port data structure. updated_ports.append(updated_port) except trunk_exc.SubPortBindingError as e: LOG.error("Failed to bind subport: %s", e) # NOTE(status_police) The subport binding has failed in a # manner in which we cannot proceed and the user must take # action to bring the trunk back to a sane state. trunk.update(status=trunk_consts.ERROR_STATUS) return [] except Exception as e: msg = ("Failed to bind subport port %(port)s on trunk " "%(trunk)s: %(exc)s") LOG.error(msg, {'port': port_id, 'trunk': trunk.id, 'exc': e}) if len(port_ids) != len(updated_ports): trunk.update(status=trunk_consts.DEGRADED_STATUS) return updated_ports def _handle_port_binding(self, context, port_id, trunk, trunk_host): """Bind the given port to the given host. :param context: The context to use for the operation :param port_id: The UUID of the port to be bound :param trunk: The trunk that the given port belongs to :param trunk_host: The host to bind the given port to """ port = self.core_plugin.update_port( context, port_id, {'port': {portbindings.HOST_ID: trunk_host, 'device_owner': trunk_consts.TRUNK_SUBPORT_OWNER}}) vif_type = port.get(portbindings.VIF_TYPE) if vif_type == portbindings.VIF_TYPE_BINDING_FAILED: raise trunk_exc.SubPortBindingError(port_id=port_id, trunk_id=trunk.id) return port class TrunkStub(object): """Stub proxy code for server->agent communication.""" def __init__(self): self._resource_rpc = resources_rpc.ResourcesPushRpcApi() @log_helpers.log_method_call def trunk_created(self, context, trunk): """Tell the agent about a trunk being created.""" self._resource_rpc.push(context, [trunk], events.CREATED) @log_helpers.log_method_call def trunk_deleted(self, context, trunk): """Tell the agent about a trunk being deleted.""" self._resource_rpc.push(context, [trunk], events.DELETED) @log_helpers.log_method_call def subports_added(self, context, subports): """Tell the agent about new subports to add.""" self._resource_rpc.push(context, subports, events.CREATED) @log_helpers.log_method_call def subports_deleted(self, context, subports): """Tell the agent about existing subports to remove.""" self._resource_rpc.push(context, subports, events.DELETED) neutron-12.1.1/neutron/services/trunk/rpc/constants.py0000664000175000017500000000122213553660046023147 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. TRUNK_BASE_TOPIC = 'trunk' TRUNK_BASE_NAMESPACE = 'trunk' neutron-12.1.1/neutron/services/trunk/rpc/__init__.py0000664000175000017500000000000013553660046022663 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/exceptions.py0000664000175000017500000000637713553660046022550 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company, LP # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron._i18n import _ from neutron_lib import exceptions as n_exc class TrunkPortInUse(n_exc.InUse): message = _("Port %(port_id)s is in use by another trunk.") class TrunkNotFound(n_exc.NotFound): message = _("Trunk %(trunk_id)s could not be found.") class SubPortNotFound(n_exc.NotFound): message = _("SubPort on trunk %(trunk_id)s with parent port %(port_id)s " "could not be found.") class DuplicateSubPort(n_exc.InUse): message = _("segmentation_type %(segmentation_type)s and segmentation_id " "%(segmentation_id)s already in use on trunk %(trunk_id)s.") class ParentPortInUse(n_exc.InUse): message = _("Port %(port_id)s is currently in use and is not " "eligible for use as a parent port.") class SubPortMtuGreaterThanTrunkPortMtu(n_exc.Conflict): message = _("MTU %(port_mtu)s of subport %(port_id)s cannot be greater " "than MTU %(trunk_mtu)s of trunk %(trunk_id)s.") class PortInUseAsTrunkParent(n_exc.InUse): message = _("Port %(port_id)s is currently a parent port " "for trunk %(trunk_id)s.") class PortInUseAsSubPort(n_exc.InUse): message = _("Port %(port_id)s is currently a subport for " "trunk %(trunk_id)s.") class TrunkInUse(n_exc.InUse): message = _("Trunk %(trunk_id)s is currently in use.") class TrunkDisabled(n_exc.Conflict): message = _("Trunk %(trunk_id)s is currently disabled.") class TrunkInErrorState(n_exc.Conflict): message = _("Trunk %(trunk_id)s is in error state. Attempt " "to resolve the error condition before proceeding.") class IncompatibleTrunkPluginConfiguration(n_exc.NeutronException): message = _("Cannot load trunk plugin: no compatible core plugin " "configuration is found.") class IncompatibleDriverSegmentationTypes(n_exc.NeutronException): message = _("Cannot load trunk plugin: no compatible segmentation " "type configuration can be found amongst list of loaded " "drivers.") class SegmentationTypeValidatorNotFound(n_exc.NotFound): message = _("Validator not found for segmentation type %(seg_type)s. " "It must be registered before the plugin init can " "proceed.") class TrunkPluginDriverConflict(n_exc.Conflict): message = _("A misconfiguration in the environment prevents the " "operation from completing, please, contact the admin.") class SubPortBindingError(n_exc.NeutronException): message = _("Failed to set port binding for port %(port_id)s on trunk " "%(trunk_id)s.") neutron-12.1.1/neutron/services/trunk/utils.py0000664000175000017500000000440313553660047021514 0ustar zuulzuul00000000000000# (c) Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins import directory from neutron.common import utils def get_agent_types_by_host(context, host): """Return the agent types registered on the host.""" agent_types = [] core_plugin = directory.get_plugin() if utils.is_extension_supported(core_plugin, 'agent'): agents = core_plugin.get_agents( context.elevated(), filters={'host': [host]}) agent_types = [a['agent_type'] for a in agents] return agent_types def is_driver_compatible(context, driver, interface, host_agent_types): """True if the driver is compatible with interface and host_agent_types. There may be edge cases where a stale view or the deployment may make the following test fail to detect the right driver in charge of the bound port. """ # NOTE(armax): this logic stems from the fact that the way Neutron is # architected we do not have a univocal mapping between VIF type and the # Driver serving it, in that the same vif type can be supported by # multiple drivers. A practical example of this is OVS and OVN in the # same deployment. In order to uniquely identify the driver, we cannot # simply look at the vif type, and we need to look at whether the host # to which the port is bound is actually managed by one driver or the # other. is_interface_compatible = driver.is_interface_compatible(interface) # For an agentless driver, only interface compatibility is required. if not driver.agent_type: return is_interface_compatible # For an agent-based driver, both interface and agent compat is required. return is_interface_compatible and driver.agent_type in host_agent_types neutron-12.1.1/neutron/services/trunk/drivers/0000775000175000017500000000000013553660156021460 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/drivers/linuxbridge/0000775000175000017500000000000013553660156023774 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/drivers/linuxbridge/agent/0000775000175000017500000000000013553660156025072 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/drivers/linuxbridge/agent/__init__.py0000664000175000017500000000000013553660046027167 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/drivers/linuxbridge/agent/trunk_plumber.py0000664000175000017500000001433613553660047030343 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import re from neutron_lib.utils import runtime from oslo_concurrency import lockutils from oslo_log import log as logging from oslo_utils import excutils from neutron.agent.linux import ip_lib from neutron.plugins.ml2.drivers.linuxbridge.agent.common import utils as lutil LOG = logging.getLogger(__name__) class Plumber(object): """Object responsible for VLAN interface CRUD. This handles the creation/deletion/listing of VLAN interfaces for a trunk within a namespace. """ def __init__(self, namespace=None): self.namespace = namespace def trunk_on_host(self, trunk): """Returns true if trunk device is present else False.""" trunk_dev = self._trunk_device_name(trunk) return ip_lib.device_exists(trunk_dev, namespace=self.namespace) def ensure_trunk_subports(self, trunk): """Idempotent wiring for a trunk's subports. Given a trunk object, delete any vlan subinterfaces belonging to a trunk that aren't on the object. Create any which are on the object which do not exist. """ trunk_dev = self._trunk_device_name(trunk) with self._trunk_lock(trunk_dev): # lock scoped to trunk device so two diffs don't interleave expected = self._get_subport_devs_and_vlans(trunk.sub_ports) existing = self._get_vlan_children(trunk_dev) to_delete = existing - expected to_create = expected - existing for devname, vlan_id in to_delete: LOG.debug("Deleting subport %(name)s with vlan tag %(tag)s", dict(name=devname, tag=vlan_id)) self._safe_delete_device(devname) for devname, vlan_id in to_create: LOG.debug("Creating subport %(name)s with vlan tag %(tag)s", dict(name=devname, tag=vlan_id)) self._create_vlan_subint(trunk_dev, devname, vlan_id) def delete_trunk_subports(self, trunk): return self.delete_subports_by_port_id(trunk.port_id) def delete_subports_by_port_id(self, port_id): device = self._get_tap_device_name(port_id) if not ip_lib.device_exists(device, namespace=self.namespace): LOG.debug("Device %s not present on this host", device) return with self._trunk_lock(device): for subname, vlan_id in self._get_vlan_children(device): LOG.debug("Deleting subport %(name)s with vlan tag %(tag)s", dict(name=subname, tag=vlan_id)) self._safe_delete_device(subname) def _trunk_lock(self, trunk_dev): lock_name = 'trunk-%s' % trunk_dev return lockutils.lock(lock_name, runtime.SYNCHRONIZED_PREFIX) def _create_vlan_subint(self, trunk_name, devname, vlan_id): ip_wrap = ip_lib.IPWrapper(namespace=self.namespace) try: dev = ip_wrap.add_vlan(devname, trunk_name, vlan_id) dev.disable_ipv6() except Exception: with excutils.save_and_reraise_exception() as ectx: ectx.reraise = ip_lib.IPDevice( devname, namespace=self.namespace).exists() def _safe_delete_device(self, devname): dev = ip_lib.IPDevice(devname, namespace=self.namespace) try: dev.link.set_down() dev.link.delete() except Exception: with excutils.save_and_reraise_exception() as ectx: ectx.reraise = dev.exists() def _trunk_device_name(self, trunk): return self._get_tap_device_name(trunk.port_id) def _get_subport_devs_and_vlans(self, subports): return {(self._get_tap_device_name(s.port_id), s.segmentation_id) for s in subports} def _get_tap_device_name(self, devname): return lutil.get_tap_device_name(devname) def _get_vlan_children(self, dev): """Return set of (devname, vlan_id) tuples for children of device.""" # TODO(kevinbenton): move into ip-lib after privsep stuff settles ip_wrapper = ip_lib.IPWrapper(namespace=self.namespace) output = ip_wrapper.netns.execute(["ip", "-d", "link", "list"], check_exit_code=True) return {(i.devname, i.vlan_tag) for i in _iter_output_by_interface(output) if i.parent_devname == dev} def _iter_output_by_interface(output): interface = [] for line in output.splitlines(): if not line.startswith(' '): # no space indicates new interface info interface_str = ' '.join(interface) if interface_str.strip(): yield _InterfaceInfo(interface_str) interface = [] interface.append(line) if interface: yield _InterfaceInfo(' '.join(interface)) class _InterfaceInfo(object): def __init__(self, line): try: name_section = line.split(': ')[1] except IndexError: name_section = None LOG.warning("Bad interface line: %s", line) if not name_section or '@' not in name_section: self.devname = name_section self.parent_devname = self.vlan_tag = None else: self.devname, parent = name_section.split('@') m = re.match(r'.*802\.1Q id (\d+).*', line) self.vlan_tag = int(m.group(1)) if m else None # we only care about parent interfaces if it's a vlan sub-interface self.parent_devname = parent if self.vlan_tag is not None else None def __repr__(self): return ('_InterfaceInfo(devname=%s, parent=%s, vlan=%s)' % (self.devname, self.parent_devname, self.vlan_tag)) neutron-12.1.1/neutron/services/trunk/drivers/linuxbridge/agent/driver.py0000664000175000017500000002100313553660047026732 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events as local_events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources as local_resources from oslo_log import log as logging import oslo_messaging from neutron.api.rpc.callbacks import events from neutron.api.rpc.handlers import resources_rpc from neutron.services.trunk import constants as t_const from neutron.services.trunk.drivers.linuxbridge.agent import trunk_plumber from neutron.services.trunk.rpc import agent as trunk_rpc LOG = logging.getLogger(__name__) def init_handler(resource, event, trigger, payload=None): """Handler for agent init event.""" LinuxBridgeTrunkDriver() @registry.has_registry_receivers class LinuxBridgeTrunkDriver(trunk_rpc.TrunkSkeleton): """Driver responsible for handling trunk/subport/port events. Receives data model events from the server and VIF events from the agent and uses these to drive a Plumber instance to wire up VLAN subinterfaces for any trunks. """ def __init__(self, plumber=None, trunk_api=None): self._plumber = plumber or trunk_plumber.Plumber() self._tapi = trunk_api or _TrunkAPI(trunk_rpc.TrunkStub()) super(LinuxBridgeTrunkDriver, self).__init__() def handle_trunks(self, context, resource_type, trunks, event_type): """Trunk data model change from the server.""" for trunk in trunks: if event_type in (events.UPDATED, events.CREATED): self._tapi.put_trunk(trunk.port_id, trunk) self.wire_trunk(context, trunk) elif event_type == events.DELETED: self._tapi.put_trunk(trunk.port_id, None) self._plumber.delete_trunk_subports(trunk) def handle_subports(self, context, resource_type, subports, event_type): """Subport data model change from the server.""" affected_trunks = set() if event_type == events.DELETED: method = self._tapi.delete_trunk_subport else: method = self._tapi.put_trunk_subport for s in subports: affected_trunks.add(s['trunk_id']) method(s['trunk_id'], s) for trunk_id in affected_trunks: trunk = self._tapi.get_trunk_by_id(context, trunk_id) if not trunk: continue self.wire_trunk(context, trunk) @registry.receives(local_resources.PORT_DEVICE, [local_events.AFTER_DELETE]) def agent_port_delete(self, resource, event, trigger, context, port_id, **kwargs): """Agent informed us a VIF was removed.""" # NOTE(kevinbenton): we don't need to do anything to cleanup VLAN # interfaces if a trunk was removed because the kernel will do that # for us. We also don't update the trunk status to DOWN because we # don't want to race with another agent that the trunk may have been # moved to. @registry.receives(local_resources.PORT_DEVICE, [local_events.AFTER_UPDATE]) def agent_port_change(self, resource, event, trigger, context, device_details, **kwargs): """The agent hath informed us thusly of a port update or create.""" trunk = self._tapi.get_trunk(context, device_details['port_id']) if trunk: # a wild trunk has appeared! make its children self.wire_trunk(context, trunk) return # clear any VLANs in case this was a trunk that changed status while # agent was offline. self._plumber.delete_subports_by_port_id(device_details['port_id']) def wire_trunk(self, context, trunk): """Wire up subports while keeping the server trunk status apprised.""" if not self._plumber.trunk_on_host(trunk): LOG.debug("Trunk %s not present on this host", trunk.port_id) return self._tapi.bind_subports_to_host(context, trunk) try: self._plumber.ensure_trunk_subports(trunk) self._tapi.set_trunk_status(context, trunk, t_const.ACTIVE_STATUS) except Exception: if not self._plumber.trunk_on_host(trunk): LOG.debug("Trunk %s removed during wiring", trunk.port_id) return # something broke LOG.exception("Failure setting up subports for %s", trunk.port_id) self._tapi.set_trunk_status(context, trunk, t_const.DEGRADED_STATUS) class _TrunkAPI(object): """Our secret stash of trunks stored by port ID. Tell no one.""" def __init__(self, trunk_stub): self.server_api = trunk_stub self._trunk_by_port_id = {} self._trunk_by_id = {} self._sub_port_id_to_trunk_port_id = {} def _fetch_trunk(self, context, port_id): try: t = self.server_api.get_trunk_details(context, port_id) LOG.debug("Found trunk %(t)s for port %(p)s", dict(p=port_id, t=t)) return t except resources_rpc.ResourceNotFound: return None except oslo_messaging.RemoteError as e: if e.exc_type != 'CallbackNotFound': raise LOG.debug("Trunk plugin disabled on server. Assuming port %s is " "not a trunk.", port_id) return None def set_trunk_status(self, context, trunk, status): self.server_api.update_trunk_status(context, trunk.id, status) def bind_subports_to_host(self, context, trunk): self.server_api.update_subport_bindings(context, trunk.sub_ports) def put_trunk_subport(self, trunk_id, subport): LOG.debug("Adding subport %(sub)s to trunk %(trunk)s", dict(sub=subport, trunk=trunk_id)) if trunk_id not in self._trunk_by_id: # not on this agent return trunk = self._trunk_by_id[trunk_id] trunk.sub_ports = [s for s in trunk.sub_ports if s.port_id != subport.port_id] + [subport] def delete_trunk_subport(self, trunk_id, subport): LOG.debug("Removing subport %(sub)s from trunk %(trunk)s", dict(sub=subport, trunk=trunk_id)) if trunk_id not in self._trunk_by_id: # not on this agent return trunk = self._trunk_by_id[trunk_id] trunk.sub_ports = [s for s in trunk.sub_ports if s.port_id != subport.port_id] def put_trunk(self, port_id, trunk): if port_id in self._trunk_by_port_id: # already existed. expunge sub_port cross ref self._sub_port_id_to_trunk_port_id = { s: p for s, p in self._sub_port_id_to_trunk_port_id.items() if p != port_id} self._trunk_by_port_id[port_id] = trunk if not trunk: return self._trunk_by_id[trunk.id] = trunk for sub in trunk.sub_ports: self._sub_port_id_to_trunk_port_id[sub.port_id] = trunk.port_id def get_trunk_by_id(self, context, trunk_id): """Gets trunk object based on trunk_id. None if not in cache.""" return self._trunk_by_id.get(trunk_id) def get_trunk(self, context, port_id): """Gets trunk object for port_id. None if not trunk.""" if port_id not in self._trunk_by_port_id: # TODO(kevinbenton): ask the server for *all* trunk port IDs on # start and eliminate asking the server if every port is a trunk # TODO(kevinbenton): clear this on AMQP reconnect LOG.debug("Cache miss for port %s, fetching from server", port_id) self.put_trunk(port_id, self._fetch_trunk(context, port_id)) return self.get_trunk(context, port_id) return self._trunk_by_port_id[port_id] def get_trunk_for_subport(self, context, port_id): """Returns trunk if port_id is a subport, else None.""" trunk_port = self._sub_port_id_to_trunk_port_id.get(port_id) if trunk_port: return self.get_trunk(context, trunk_port) neutron-12.1.1/neutron/services/trunk/drivers/linuxbridge/__init__.py0000664000175000017500000000000013553660046026071 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/drivers/linuxbridge/driver.py0000664000175000017500000000324113553660047025640 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from oslo_log import log as logging from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron.services.trunk import constants as trunk_consts from neutron.services.trunk.drivers import base LOG = logging.getLogger(__name__) NAME = 'linuxbridge' SUPPORTED_INTERFACES = ( portbindings.VIF_TYPE_BRIDGE, ) SUPPORTED_SEGMENTATION_TYPES = ( trunk_consts.VLAN, ) class LinuxBridgeDriver(base.DriverBase): """Server-side Trunk driver for the ML2 Linux Bridge driver.""" @property def is_loaded(self): try: return NAME in cfg.CONF.ml2.mechanism_drivers except cfg.NoSuchOptError: return False @classmethod def create(cls): return cls(NAME, SUPPORTED_INTERFACES, SUPPORTED_SEGMENTATION_TYPES, constants.AGENT_TYPE_LINUXBRIDGE, can_trunk_bound_port=True) def register(): # NOTE(kevinbenton): the thing that is keeping this from being # immediately garbage collected is that it registers callbacks LinuxBridgeDriver.create() LOG.debug("Linux bridge trunk driver initialized.") neutron-12.1.1/neutron/services/trunk/drivers/base.py0000664000175000017500000000677113553660047022756 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company, LP # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron.services.trunk import constants as trunk_consts from neutron.services.trunk.rpc import backend @registry.has_registry_receivers class DriverBase(object): def __init__(self, name, interfaces, segmentation_types, agent_type=None, can_trunk_bound_port=False): """Instantiate a trunk driver. :param name: driver name. :param interfaces: list of interfaces supported. :param segmentation_types: list of segmentation types supported. :param agent_type: agent type for the driver, None if agentless. :param can_trunk_bound_port: True if trunk creation is allowed for a bound parent port (i.e. trunk creation after VM boot). """ self.name = name self.interfaces = interfaces self.segmentation_types = segmentation_types self.agent_type = agent_type self.can_trunk_bound_port = can_trunk_bound_port @abc.abstractproperty def is_loaded(self): """True if the driver is active for the Neutron Server. Implement this property to determine if your driver is actively configured for this Neutron Server deployment, e.g. check if core_plugin or mech_drivers config options (for ML2) is set as required. """ def is_interface_compatible(self, interface): """True if the driver is compatible with the interface.""" return interface in self.interfaces def is_agent_compatible(self, agent_type): """True if the driver is compatible with the agent type.""" return agent_type == self.agent_type @registry.receives(trunk_consts.TRUNK_PLUGIN, [events.AFTER_INIT]) def register(self, resource, event, trigger, payload=None): """Register the trunk driver. This method should be overridden so that the driver can subscribe to the required trunk events. The driver should also advertise itself as supported driver by calling register_driver() on the TrunkPlugin otherwise the trunk plugin may fail to start if no compatible configuration is found. External drivers must subscribe to the AFTER_INIT event for the trunk plugin so that they can integrate without an explicit register() method invocation. :param resource: neutron.services.trunk.constants.TRUNK_PLUGIN :param event: neutron_lib.callbacks.events.AFTER_INIT :param trigger: neutron.service.trunks.plugin.TrunkPlugin """ trigger.register_driver(self) # Set up the server-side RPC backend if the driver is loaded, # it is agent based, and the RPC backend is not already initialized. if self.is_loaded and self.agent_type and not trigger.is_rpc_enabled(): trigger.set_rpc_backend(backend.ServerSideRpcBackend()) neutron-12.1.1/neutron/services/trunk/drivers/openvswitch/0000775000175000017500000000000013553660156024031 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/drivers/openvswitch/utils.py0000664000175000017500000000152513553660046025544 0ustar zuulzuul00000000000000# (c) Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron.services.trunk.drivers.openvswitch import constants as ovs_const def gen_trunk_br_name(trunk_id): return ((ovs_const.TRUNK_BR_PREFIX + trunk_id) [:constants.DEVICE_NAME_MAX_LEN - 1]) neutron-12.1.1/neutron/services/trunk/drivers/openvswitch/agent/0000775000175000017500000000000013553660156025127 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/drivers/openvswitch/agent/exceptions.py0000664000175000017500000000155513553660046027666 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions as n_exc from neutron._i18n import _ class TrunkBridgeNotFound(n_exc.NotFound): message = _("Trunk bridge %(bridge)s could not be found.") class ParentPortNotFound(n_exc.NotFound): message = _("Parent port for trunk bridge %(bridge)s could not be found.") neutron-12.1.1/neutron/services/trunk/drivers/openvswitch/agent/trunk_manager.py0000664000175000017500000002700513553660047030341 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron_lib import exceptions from oslo_log import log as logging from neutron._i18n import _ from neutron.agent.common import ovs_lib from neutron.services.trunk.drivers.openvswitch.agent import exceptions as exc from neutron.services.trunk.drivers.openvswitch import utils LOG = logging.getLogger(__name__) class TrunkManagerError(exceptions.NeutronException): message = _("Error while communicating with OVSDB: %(error)s") def get_br_int_port_name(prefix, port_id): """Return the OVS port name for the given port ID. The port name is the one that plumbs into the integration bridge. """ return ("%si-%s" % (prefix, port_id))[:constants.DEVICE_NAME_MAX_LEN] def get_br_trunk_port_name(prefix, port_id): """Return the OVS port name for the given port ID. The port name is the one that plumbs into the trunk bridge. """ return ("%st-%s" % (prefix, port_id))[:constants.DEVICE_NAME_MAX_LEN] def get_patch_peer_attrs(peer_name, port_mac=None, port_id=None): external_ids = {} if port_mac: external_ids['attached-mac'] = port_mac if port_id: external_ids['iface-id'] = port_id attrs = [('type', 'patch'), ('options', {'peer': peer_name})] if external_ids: attrs.append( ('external_ids', external_ids)) return attrs class TrunkBridge(ovs_lib.OVSBridge): """An OVS trunk bridge. A trunk bridge has a name that follows a specific naming convention. """ def __init__(self, trunk_id): name = utils.gen_trunk_br_name(trunk_id) super(TrunkBridge, self).__init__(name) def exists(self): return self.bridge_exists(self.br_name) class TrunkParentPort(object): """An OVS trunk parent port. A trunk parent port is represented in OVS with two patch ports that connect a trunk bridge and the integration bridge respectively. These patch ports follow strict naming conventions: tpi- for the patch port that goes into the integration bridge, and tpt- for the patch port that goes into the trunk bridge. """ DEV_PREFIX = 'tp' def __init__(self, trunk_id, port_id, port_mac=None): self.trunk_id = trunk_id self.port_id = port_id self.port_mac = port_mac self.bridge = TrunkBridge(self.trunk_id) self.patch_port_int_name = get_br_int_port_name( self.DEV_PREFIX, port_id) self.patch_port_trunk_name = get_br_trunk_port_name( self.DEV_PREFIX, port_id) self._transaction = None def plug(self, br_int): """Plug patch ports between trunk bridge and given bridge. The method plugs one patch port on the given bridge side using port MAC and ID as external IDs. The other endpoint of patch port is attached to the trunk bridge. Everything is done in a single OVSDB transaction so either all operations succeed or fail. :param br_int: an integration bridge where peer endpoint of patch port will be created. """ # NOTE(jlibosva): OVSDB is an api so it doesn't matter whether we # use self.bridge or br_int ovsdb = self.bridge.ovsdb # Once the bridges are connected with the following patch ports, # the ovs agent will recognize the ports for processing and it will # take over the wiring process and everything that entails. # REVISIT(rossella_s): revisit this integration part, should tighter # control over the wiring logic for trunk ports be required. patch_int_attrs = get_patch_peer_attrs( self.patch_port_trunk_name, self.port_mac, self.port_id) patch_trunk_attrs = get_patch_peer_attrs(self.patch_port_int_name, self.port_mac, self.port_id) with ovsdb.transaction() as txn: txn.add(ovsdb.add_port(br_int.br_name, self.patch_port_int_name)) txn.add(ovsdb.db_set('Interface', self.patch_port_int_name, *patch_int_attrs)) txn.add(ovsdb.add_port(self.bridge.br_name, self.patch_port_trunk_name)) txn.add(ovsdb.db_set('Interface', self.patch_port_trunk_name, *patch_trunk_attrs)) def unplug(self, bridge): """Unplug the trunk from bridge. Method unplugs in single OVSDB transaction the trunk bridge and patch port on provided bridge. :param bridge: bridge that has peer side of patch port for this subport. """ ovsdb = self.bridge.ovsdb with ovsdb.transaction() as txn: txn.add(ovsdb.del_br(self.bridge.br_name)) txn.add(ovsdb.del_port(self.patch_port_int_name, bridge.br_name)) class SubPort(TrunkParentPort): """An OVS trunk subport. A subport is represented in OVS with two patch ports that connect a trunk bridge and the integration bridge respectively. These patch ports follow strict naming conventions: spi- for the patch port that goes into the integration bridge, and spt- for the patch port that goes into the trunk bridge. """ DEV_PREFIX = 'sp' def __init__(self, trunk_id, port_id, port_mac=None, segmentation_id=None): super(SubPort, self).__init__(trunk_id, port_id, port_mac) self.segmentation_id = segmentation_id def plug(self, br_int): """Unplug patch ports between trunk bridge and given bridge. The method unplugs one patch port on the given bridge side using port MAC and ID as external IDs. The other endpoint of patch port is attached to the trunk bridge. Then it sets vlan tag represented by segmentation_id. Everything is done in a single OVSDB transaction so either all operations succeed or fail. :param br_int: an integration bridge where peer endpoint of patch port will be created. """ ovsdb = self.bridge.ovsdb with ovsdb.transaction() as txn: super(SubPort, self).plug(br_int) txn.add(ovsdb.db_set( "Port", self.patch_port_trunk_name, ("tag", self.segmentation_id))) def unplug(self, bridge): """Unplug the sub port from the bridge. Method unplugs in single OVSDB transaction both endpoints of patch ports that represents the subport. :param bridge: bridge that has peer side of patch port for this subport. """ ovsdb = self.bridge.ovsdb with ovsdb.transaction() as txn: txn.add(ovsdb.del_port(self.patch_port_trunk_name, self.bridge.br_name)) txn.add(ovsdb.del_port(self.patch_port_int_name, bridge.br_name)) class TrunkManager(object): """It implements the OVS trunk dataplane. It interfaces with the OVSDB server to execute OVS commands. """ def __init__(self, br_int): self.br_int = br_int def create_trunk(self, trunk_id, port_id, port_mac): """Create the trunk. This patches the bridge for trunk_id with the integration bridge by means of parent port identified by port_id. :param trunk_id: ID of the trunk. :param port_id: ID of the parent port. :param port_mac: the MAC address of the parent port. :raises: TrunkBridgeNotFound: in case trunk bridge does not exist. """ trunk = TrunkParentPort(trunk_id, port_id, port_mac) try: if not trunk.bridge.exists(): raise exc.TrunkBridgeNotFound(bridge=trunk.bridge.br_name) trunk.plug(self.br_int) except RuntimeError as e: raise TrunkManagerError(error=e) def remove_trunk(self, trunk_id, port_id): """Remove the trunk bridge.""" trunk = TrunkParentPort(trunk_id, port_id) try: if trunk.bridge.exists(): trunk.unplug(self.br_int) else: LOG.debug("Trunk bridge with ID %s does not exist.", trunk_id) except RuntimeError as e: raise TrunkManagerError(error=e) def dispose_trunk(self, trunk_bridge): """Clean up all the OVS resources associated to trunk_bridge.""" ovsdb = trunk_bridge.ovsdb patch_peers = [] try: patch_peers = trunk_bridge.get_ports_attributes( 'Interface', columns=['options']) with trunk_bridge.ovsdb.transaction() as txn: for patch_peer in patch_peers: peer_name = patch_peer['options'].get('peer') if peer_name: txn.add(ovsdb.del_port(peer_name, self.br_int.br_name)) txn.add(ovsdb.del_br(trunk_bridge.br_name)) LOG.debug("Deleted bridge '%s' and patch peers '%s'.", trunk_bridge.br_name, patch_peers) except RuntimeError as e: LOG.error("Could not delete '%(peers)s' associated to " "trunk bridge %(name)s. Reason: %(reason)s.", {'peers': patch_peers, 'name': trunk_bridge.br_name, 'reason': e}) def add_sub_port(self, trunk_id, port_id, port_mac, segmentation_id): """Create a sub_port. :param trunk_id: ID of the trunk. :param port_id: ID of the subport. :param segmentation_id: segmentation ID associated with this subport. :param port_mac: MAC address of the subport. """ sub_port = SubPort(trunk_id, port_id, port_mac, segmentation_id) # If creating of parent trunk bridge takes longer than API call for # creating subport then bridge doesn't exist yet. try: if not sub_port.bridge.exists(): raise exc.TrunkBridgeNotFound(bridge=sub_port.bridge.br_name) sub_port.plug(self.br_int) except RuntimeError as e: raise TrunkManagerError(error=e) def remove_sub_port(self, trunk_id, port_id): """Remove a sub_port. :param trunk_id: ID of the trunk. :param port_id: ID of the subport. """ sub_port = SubPort(trunk_id, port_id) # Trunk bridge might have been deleted by calling delete_trunk() before # remove_sub_port(). try: if sub_port.bridge.exists(): sub_port.unplug(self.br_int) else: LOG.debug("Trunk bridge with ID %s does not exist.", trunk_id) except RuntimeError as e: raise TrunkManagerError(error=e) def get_port_uuid_from_external_ids(self, port): """Return the port UUID from the port metadata.""" try: return self.br_int.portid_from_external_ids( port['external_ids']) except RuntimeError as e: raise TrunkManagerError(error=e) neutron-12.1.1/neutron/services/trunk/drivers/openvswitch/agent/__init__.py0000664000175000017500000000000013553660046027224 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/drivers/openvswitch/agent/ovsdb_handler.py0000664000175000017500000005357613553660047030332 0ustar zuulzuul00000000000000# Copyright (c) 2016 SUSE Linux Products GmbH # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import eventlet from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import context as n_context from oslo_concurrency import lockutils from oslo_context import context as o_context from oslo_log import log as logging import oslo_messaging from oslo_serialization import jsonutils from neutron._i18n import _ from neutron.agent.common import ovs_lib from neutron.api.rpc.handlers import resources_rpc from neutron.common import utils as common_utils from neutron.plugins.ml2.drivers.openvswitch.agent.common \ import constants as ovs_agent_constants from neutron.services.trunk import constants from neutron.services.trunk.drivers.openvswitch.agent import exceptions from neutron.services.trunk.drivers.openvswitch.agent \ import trunk_manager as tman from neutron.services.trunk.drivers.openvswitch import constants as t_const from neutron.services.trunk.drivers.openvswitch import utils from neutron.services.trunk.rpc import agent LOG = logging.getLogger(__name__) DEFAULT_WAIT_FOR_PORT_TIMEOUT = 60 def lock_on_bridge_name(required_parameter): def func_decor(f): try: br_arg_index = f.__code__.co_varnames.index(required_parameter) except ValueError: raise RuntimeError(_("%s parameter is required for this decorator") % required_parameter) @functools.wraps(f) def inner(*args, **kwargs): try: bridge_name = kwargs[required_parameter] except KeyError: bridge_name = args[br_arg_index] with lockutils.lock(bridge_name): return f(*args, **kwargs) return inner return func_decor def is_trunk_bridge(port_name): return port_name.startswith(t_const.TRUNK_BR_PREFIX) def is_subport(port_name): return port_name.startswith(tman.SubPort.DEV_PREFIX) def is_trunk_service_port(port_name): """True if the port is any of the ports used to realize a trunk.""" return is_trunk_bridge(port_name) or port_name[:2] in ( tman.TrunkParentPort.DEV_PREFIX, tman.SubPort.DEV_PREFIX) def bridge_has_port(bridge, is_port_predicate): """True if there is an OVS port for which is_port_predicate is True. """ try: ifaces = bridge.get_iface_name_list() except RuntimeError as e: LOG.error("Cannot obtain interface list for bridge %(bridge)s: " "%(err)s", {'bridge': bridge.br_name, 'err': e}) return False return any(iface for iface in ifaces if is_port_predicate(iface)) def bridge_has_instance_port(bridge): """True if there is an OVS port that doesn't have bridge or patch ports prefix. """ is_instance_port = lambda p: not is_trunk_service_port(p) return bridge_has_port(bridge, is_instance_port) def bridge_has_service_port(bridge): """True if there is an OVS port that is used to implement a trunk. """ return bridge_has_port(bridge, is_trunk_service_port) @registry.has_registry_receivers class OVSDBHandler(object): """It listens to OVSDB events to create the physical resources associated to a logical trunk in response to OVSDB events (such as VM boot and/or delete). """ def __init__(self, trunk_manager): self.timeout = DEFAULT_WAIT_FOR_PORT_TIMEOUT self._context = n_context.get_admin_context_without_session() self.trunk_manager = trunk_manager self.trunk_rpc = agent.TrunkStub() @property def context(self): self._context.request_id = o_context.generate_request_id() return self._context @registry.receives(ovs_agent_constants.OVSDB_RESOURCE, [events.AFTER_READ]) def process_trunk_port_events( self, resource, event, trigger, ovsdb_events): """Process added and removed port events coming from OVSDB monitor.""" for port_event in ovsdb_events['added']: port_name = port_event['name'] if is_trunk_bridge(port_name): LOG.debug("Processing trunk bridge %s", port_name) # As there is active waiting for port to appear, it's handled # in a separate greenthread. # NOTE: port_name is equal to bridge_name at this point. eventlet.spawn_n(self.handle_trunk_add, port_name) for port_event in ovsdb_events['removed']: bridge_name = port_event['external_ids'].get('bridge_name') if bridge_name and is_trunk_bridge(bridge_name): eventlet.spawn_n( self.handle_trunk_remove, bridge_name, port_event) @lock_on_bridge_name(required_parameter='bridge_name') def handle_trunk_add(self, bridge_name): """Create trunk bridge based on parent port ID. This method is decorated with a lock that prevents processing deletion while creation hasn't been finished yet. It's based on the bridge name so we can keep processing other bridges in parallel. :param bridge_name: Name of the created trunk bridge. """ bridge = ovs_lib.OVSBridge(bridge_name) # Handle condition when there was bridge in both added and removed # events and handle_trunk_remove greenthread was executed before # handle_trunk_add if not bridge.bridge_exists(bridge_name): LOG.debug("The bridge %s was deleted before it was handled.", bridge_name) return # Determine the state of the trunk bridge by looking for the VM's port, # i.e. the trunk parent port and/or patch ports to be present. If the # VM is absent, then we clean the dangling bridge. If the VM is present # the value of 'rewire' tells us whether or not the bridge was dealt # with in a previous added event, and thus it has active patch ports. if not self._is_vm_connected(bridge): LOG.debug("No instance port associated to bridge %s could be " "found. Deleting bridge and its resources.", bridge_name) self.trunk_manager.dispose_trunk(bridge) return # Check if the trunk was provisioned in a previous run. This can happen # at agent startup when existing trunks are notified as added events. rewire = bridge_has_service_port(bridge) # Once we get hold of the trunk parent port, we can provision # the OVS dataplane for the trunk. try: self._wire_trunk(bridge, self._get_parent_port(bridge), rewire) except oslo_messaging.MessagingException as e: LOG.error("Got messaging error while processing trunk bridge " "%(bridge_name)s: %(err)s", {'bridge_name': bridge.br_name, 'err': e}) except exceptions.ParentPortNotFound as e: LOG.error("Failed to get parent port for bridge " "%(bridge_name)s: %(err)s", {'bridge_name': bridge.br_name, 'err': e}) @lock_on_bridge_name(required_parameter='bridge_name') def handle_trunk_remove(self, bridge_name, port): """Remove wiring between trunk bridge and integration bridge. The method calls into trunk manager to remove patch ports on integration bridge side and to delete the trunk bridge. It's decorated with a lock to prevent deletion of bridge while creation is still in process. :param bridge_name: Name of the bridge used for locking purposes. :param port: Parent port dict. """ # TODO(njohnston): In the case of DPDK with trunk ports, if nova # deletes an interface and then re-adds it we can get a race # condition where the port is re-added and then the bridge is # deleted because we did not properly catch the re-addition. To # solve this would require transitioning to ordered event # resolution, like the L3 agent does with the # ResourceProcessingQueue class. Until we can make that happen, we # try to mitigate the issue by checking if there is a port on the # bridge and if so then do not remove it. bridge = ovs_lib.OVSBridge(bridge_name) if bridge_has_instance_port(bridge): LOG.debug("The bridge %s has instances attached so it will not " "be deleted.", bridge_name) return try: # TODO(jlibosva): Investigate how to proceed during removal of # trunk bridge that doesn't have metadata stored. parent_port_id, trunk_id, subport_ids = self._get_trunk_metadata( port) # NOTE(status_police): we do not report changes in trunk status on # removal to avoid potential races between agents in case the event # is due to a live migration or reassociation of a trunk to a new # VM. self.unwire_subports_for_trunk(trunk_id, subport_ids) self.trunk_manager.remove_trunk(trunk_id, parent_port_id) except tman.TrunkManagerError as te: LOG.error("Removing trunk %(trunk_id)s failed: %(err)s", {'trunk_id': port['external_ids']['trunk_id'], 'err': te}) else: LOG.debug("Deleted resources associated to trunk: %s", trunk_id) def manages_this_trunk(self, trunk_id): """True if this OVSDB handler manages trunk based on given ID.""" bridge_name = utils.gen_trunk_br_name(trunk_id) return ovs_lib.BaseOVS().bridge_exists(bridge_name) def get_connected_subports_for_trunk(self, trunk_id): """Return the list of subports present on the trunk bridge.""" bridge = ovs_lib.OVSBridge(utils.gen_trunk_br_name(trunk_id)) if not bridge.bridge_exists(bridge.br_name): return [] try: ports = bridge.get_ports_attributes( 'Interface', columns=['name', 'external_ids']) return [ self.trunk_manager.get_port_uuid_from_external_ids(port) for port in ports if is_subport(port['name']) ] except (RuntimeError, tman.TrunkManagerError) as e: LOG.error("Failed to get subports for bridge %(bridge)s: " "%(err)s", {'bridge': bridge.br_name, 'err': e}) return [] def wire_subports_for_trunk(self, context, trunk_id, subports, trunk_bridge=None, parent_port=None): """Create OVS ports associated to the logical subports.""" # Tell the server that subports must be bound to this host. subport_bindings = self.trunk_rpc.update_subport_bindings( context, subports) # Bindings were successful: create the OVS subports. subport_bindings = subport_bindings.get(trunk_id, []) subports_mac = {p['id']: p['mac_address'] for p in subport_bindings} subport_ids = [] for subport in subports: try: self.trunk_manager.add_sub_port(trunk_id, subport.port_id, subports_mac[subport.port_id], subport.segmentation_id) except tman.TrunkManagerError as te: LOG.error("Failed to add subport with port ID " "%(subport_port_id)s to trunk with ID " "%(trunk_id)s: %(err)s", {'subport_port_id': subport.port_id, 'trunk_id': trunk_id, 'err': te}) else: subport_ids.append(subport.port_id) try: self._update_trunk_metadata( trunk_bridge, parent_port, trunk_id, subport_ids) except (RuntimeError, exceptions.ParentPortNotFound) as e: LOG.error("Failed to store metadata for trunk %(trunk_id)s: " "%(reason)s", {'trunk_id': trunk_id, 'reason': e}) # NOTE(status_police): Trunk bridge has stale metadata now, it # might cause troubles during deletion. Signal a DEGRADED status; # if the user undo/redo the operation things may go back to # normal. return constants.DEGRADED_STATUS LOG.debug("Added trunk: %s", trunk_id) return self._get_current_status(subports, subport_ids) def unwire_subports_for_trunk(self, trunk_id, subport_ids): """Destroy OVS ports associated to the logical subports.""" ids = [] for subport_id in subport_ids: try: self.trunk_manager.remove_sub_port(trunk_id, subport_id) ids.append(subport_id) except tman.TrunkManagerError as te: LOG.error("Removing subport %(subport_id)s from trunk " "%(trunk_id)s failed: %(err)s", {'subport_id': subport_id, 'trunk_id': trunk_id, 'err': te}) try: # OVS bridge and port to be determined by _update_trunk_metadata bridge = None port = None self._update_trunk_metadata( bridge, port, trunk_id, subport_ids, wire=False) except RuntimeError as e: # NOTE(status_police): Trunk bridge has stale metadata now, it # might cause troubles during deletion. Signal a DEGRADED status; # if the user undo/redo the operation things may go back to # normal. LOG.error("Failed to store metadata for trunk %(trunk_id)s: " "%(reason)s", {'trunk_id': trunk_id, 'reason': e}) return constants.DEGRADED_STATUS except exceptions.ParentPortNotFound as e: # If a user deletes/migrates a VM and remove subports from a trunk # in short sequence, there is a chance that we hit this spot in # that the trunk may still be momentarily bound to the agent. We # should not mark the status as DEGRADED in this case. LOG.debug(e) return self._get_current_status(subport_ids, ids) def report_trunk_status(self, context, trunk_id, status): """Report trunk status to the server.""" self.trunk_rpc.update_trunk_status(context, trunk_id, status) def _get_parent_port(self, trunk_bridge): """Return the OVS trunk parent port plugged on trunk_bridge.""" trunk_br_ports = trunk_bridge.get_ports_attributes( 'Interface', columns=['name', 'external_ids'], if_exists=True) for trunk_br_port in trunk_br_ports: if not is_trunk_service_port(trunk_br_port['name']): return trunk_br_port raise exceptions.ParentPortNotFound(bridge=trunk_bridge.br_name) def _wire_trunk(self, trunk_br, port, rewire=False): """Wire trunk bridge with integration bridge. The method calls into trunk manager to create patch ports for trunk and patch ports for all subports associated with this trunk. If rewire is True, a diff is performed between desired state (the one got from the server) and actual state (the patch ports present on the trunk bridge) and subports are wired/unwired accordingly. :param trunk_br: OVSBridge object representing the trunk bridge. :param port: Parent port dict. :param rewire: True if local trunk state must be reconciled with server's state. """ ctx = self.context try: parent_port_id = ( self.trunk_manager.get_port_uuid_from_external_ids(port)) trunk = self.trunk_rpc.get_trunk_details(ctx, parent_port_id) except tman.TrunkManagerError as te: LOG.error("Can't obtain parent port ID from port %s", port['name']) return except resources_rpc.ResourceNotFound: LOG.error("Port %s has no trunk associated.", parent_port_id) return try: registry.notify( constants.TRUNK, events.BEFORE_CREATE, self, context=ctx, trunk=trunk) self.trunk_manager.create_trunk( trunk.id, trunk.port_id, port['external_ids'].get('attached-mac')) except tman.TrunkManagerError as te: LOG.error("Failed to create trunk %(trunk_id)s: %(err)s", {'trunk_id': trunk.id, 'err': te}) # NOTE(status_police): Trunk couldn't be created so it ends in # ERROR status and resync can fix that later. self.report_trunk_status(ctx, trunk.id, constants.ERROR_STATUS) return # We need to remove stale subports unwire_status = constants.ACTIVE_STATUS if rewire: old_subport_ids = self.get_connected_subports_for_trunk(trunk.id) subports = {p['port_id'] for p in trunk.sub_ports} subports_to_delete = set(old_subport_ids) - subports if subports_to_delete: unwire_status = self.unwire_subports_for_trunk( trunk.id, subports_to_delete) # NOTE(status_police): inform the server whether the operation # was a partial or complete success. Do not inline status. # NOTE: in case of rewiring we readd ports that are already present on # the bridge because e.g. the segmentation ID might have changed (e.g. # agent crashed, port was removed and readded with a different seg ID) wire_status = self.wire_subports_for_trunk( ctx, trunk.id, trunk.sub_ports, trunk_bridge=trunk_br, parent_port=port) if (unwire_status == wire_status and wire_status == constants.ACTIVE_STATUS): status = constants.ACTIVE_STATUS else: status = constants.DEGRADED_STATUS self.report_trunk_status(ctx, trunk.id, status) def _set_trunk_metadata(self, trunk_bridge, port, trunk_id, subport_ids): """Set trunk metadata in OVS port for trunk parent port.""" # update the parent port external_ids to store the trunk bridge # name, trunk id and subport ids so we can easily remove the trunk # bridge and service ports once this port is removed trunk_bridge = trunk_bridge or ovs_lib.OVSBridge( utils.gen_trunk_br_name(trunk_id)) port = port or self._get_parent_port(trunk_bridge) port['external_ids']['bridge_name'] = trunk_bridge.br_name port['external_ids']['trunk_id'] = trunk_id port['external_ids']['subport_ids'] = jsonutils.dumps(subport_ids) trunk_bridge.set_db_attribute( 'Interface', port['name'], 'external_ids', port['external_ids']) def _get_trunk_metadata(self, port): """Get trunk metadata from OVS port.""" parent_port_id = ( self.trunk_manager.get_port_uuid_from_external_ids(port)) trunk_id = port['external_ids'].get('trunk_id') subport_ids = jsonutils.loads( port['external_ids'].get('subport_ids', '[]')) return parent_port_id, trunk_id, subport_ids def _update_trunk_metadata(self, trunk_bridge, port, trunk_id, subport_ids, wire=True): """Update trunk metadata. :param trunk_bridge: OVS trunk bridge. :param port: OVS parent port. :param trunk_id: trunk ID. :param subport_ids: subports affecting the metadata. :param wire: if True subport_ids are added, otherwise removed. """ trunk_bridge = trunk_bridge or ovs_lib.OVSBridge( utils.gen_trunk_br_name(trunk_id)) port = port or self._get_parent_port(trunk_bridge) _port_id, _trunk_id, old_subports = self._get_trunk_metadata(port) if wire: new_subports = set(old_subports) | set(subport_ids) else: new_subports = set(old_subports) - set(subport_ids) self._set_trunk_metadata(trunk_bridge, port, trunk_id, new_subports) def _get_current_status(self, expected_subports, actual_subports): """Return the current status of the trunk. If the number of expected subports to be processed does not match the number of subports successfully processed, the status returned is DEGRADED, ACTIVE otherwise. """ # NOTE(status_police): a call to this method should be followed by # a trunk_update_status to report the latest trunk status, but there # can be exceptions (e.g. unwire_subports_for_trunk). if len(expected_subports) != len(actual_subports): return constants.DEGRADED_STATUS else: return constants.ACTIVE_STATUS def _is_vm_connected(self, bridge): """True if an instance is connected to bridge, False otherwise.""" bridge_has_port_predicate = functools.partial( bridge_has_instance_port, bridge) try: common_utils.wait_until_true( bridge_has_port_predicate, timeout=self.timeout) return True except common_utils.WaitTimeout: LOG.error( 'No port present on trunk bridge %(br_name)s ' 'in %(timeout)d seconds.', {'br_name': bridge.br_name, 'timeout': self.timeout}) return False neutron-12.1.1/neutron/services/trunk/drivers/openvswitch/agent/driver.py0000664000175000017500000001121113553660047026767 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events as local_events from neutron_lib.callbacks import registry as local_registry from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from neutron.api.rpc.callbacks.consumer import registry from neutron.api.rpc.callbacks import events from neutron.api.rpc.callbacks import resources from neutron.services.trunk import constants from neutron.services.trunk.drivers.openvswitch.agent import ovsdb_handler from neutron.services.trunk.drivers.openvswitch.agent import trunk_manager from neutron.services.trunk.rpc import agent LOG = logging.getLogger(__name__) TRUNK_SKELETON = None @local_registry.has_registry_receivers class OVSTrunkSkeleton(agent.TrunkSkeleton): """It processes Neutron Server events to create the physical resources associated to a logical trunk in response to user initiated API events (such as trunk subport add/remove). It collaborates with the OVSDBHandler to implement the trunk control plane. """ def __init__(self, ovsdb_handler): super(OVSTrunkSkeleton, self).__init__() self.ovsdb_handler = ovsdb_handler registry.unsubscribe(self.handle_trunks, resources.TRUNK) def handle_trunks(self, context, resource_type, trunk, event_type): """This method is not required by the OVS Agent driver. Trunk notifications are handled via local OVSDB events. """ raise NotImplementedError() def handle_subports(self, context, resource_type, subports, event_type): # Subports are always created with the same trunk_id and there is # always at least one item in subports list trunk_id = subports[0].trunk_id if self.ovsdb_handler.manages_this_trunk(trunk_id): if event_type not in (events.CREATED, events.DELETED): LOG.error("Unknown or unimplemented event %s", event_type) return ctx = self.ovsdb_handler.context try: LOG.debug("Event %s for subports: %s", event_type, subports) if event_type == events.CREATED: status = self.ovsdb_handler.wire_subports_for_trunk( ctx, trunk_id, subports) elif event_type == events.DELETED: subport_ids = [subport.port_id for subport in subports] status = self.ovsdb_handler.unwire_subports_for_trunk( trunk_id, subport_ids) self.ovsdb_handler.report_trunk_status(ctx, trunk_id, status) except oslo_messaging.MessagingException as e: LOG.error( "Error on event %(event)s for subports " "%(subports)s: %(err)s", {'event': event_type, 'subports': subports, 'err': e}) @local_registry.receives(constants.TRUNK, [local_events.BEFORE_CREATE]) def check_trunk_dependencies( self, resource, event, trigger, **kwargs): # The OVS trunk driver does not work with iptables firewall and QoS. # We should validate the environment configuration and signal that # something might be wrong. # NOTE(armax): this check could be made quite sophisticated in that # we could check for incompatibilities and abort the creation request # only if the trunk is indeed associated with ports that have security # groups and QoS rules, though this would be a lot more work. if "iptables_hybrid" in cfg.CONF.SECURITYGROUP.firewall_driver: LOG.warning( "Firewall driver iptables_hybrid is not compatible with " "trunk ports. Trunk %(trunk_id)s may be insecure.", {'trunk_id': kwargs['trunk'].id}) def init_handler(resource, event, trigger, payload=None): """Handler for agent init event.""" # Set up agent-side RPC for receiving trunk events; we may want to # make this setup conditional based on server-side capabilities. global TRUNK_SKELETON manager = trunk_manager.TrunkManager(trigger.int_br) handler = ovsdb_handler.OVSDBHandler(manager) TRUNK_SKELETON = OVSTrunkSkeleton(handler) neutron-12.1.1/neutron/services/trunk/drivers/openvswitch/constants.py0000664000175000017500000000116713553660046026422 0ustar zuulzuul00000000000000# (c) Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. TRUNK_BR_PREFIX = 'tbr-' neutron-12.1.1/neutron/services/trunk/drivers/openvswitch/__init__.py0000664000175000017500000000000013553660046026126 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/trunk/drivers/openvswitch/driver.py0000664000175000017500000000465213553660047025704 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import constants from oslo_config import cfg from oslo_log import log as logging from neutron.plugins.ml2.drivers.openvswitch.agent.common import ( constants as agent_consts) from neutron.services.trunk import constants as trunk_consts from neutron.services.trunk.drivers import base from neutron.services.trunk.drivers.openvswitch import utils LOG = logging.getLogger(__name__) NAME = 'openvswitch' SUPPORTED_INTERFACES = ( portbindings.VIF_TYPE_OVS, portbindings.VIF_TYPE_VHOST_USER, ) SUPPORTED_SEGMENTATION_TYPES = ( trunk_consts.VLAN, ) DRIVER = None class OVSDriver(base.DriverBase): @property def is_loaded(self): try: return NAME in cfg.CONF.ml2.mechanism_drivers except cfg.NoSuchOptError: return False @classmethod def create(cls): return OVSDriver(NAME, SUPPORTED_INTERFACES, SUPPORTED_SEGMENTATION_TYPES, constants.AGENT_TYPE_OVS) def register(): """Register the driver.""" global DRIVER DRIVER = OVSDriver.create() # To set the bridge_name in a parent port's vif_details. registry.subscribe(vif_details_bridge_name_handler, agent_consts.OVS_BRIDGE_NAME, events.BEFORE_READ) LOG.debug('Open vSwitch trunk driver registered') def vif_details_bridge_name_handler(resource, event, set_br_name, payload=None): """If port is a trunk port, generate a bridge_name for its vif_details.""" port = payload.metadata['port'] if 'trunk_details' in port: set_br_name(utils.gen_trunk_br_name(port['trunk_details']['trunk_id'])) neutron-12.1.1/neutron/services/trunk/drivers/__init__.py0000664000175000017500000000244413553660046023573 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.services.trunk.drivers.linuxbridge import driver as lxb_driver from neutron.services.trunk.drivers.openvswitch import driver as ovs_driver def register(): """Load in-tree drivers for the service plugin.""" # Enable the trunk plugin to work with ML2/OVS. Support for other # drivers can be added similarly by executing the registration # code at the time of plugin/mech driver initialization. There should # be at least one compatible driver enabled in the deployment for trunk # setup to be successful. The plugin fails to initialize if no compatible # driver is found in the deployment. lxb_driver.register() ovs_driver.register() neutron-12.1.1/neutron/services/trunk/rules.py0000664000175000017500000003336113553660047021513 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from neutron_lib.api import converters from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import provider_net as provider from neutron_lib.api import validators from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from neutron_lib.plugins.ml2 import api from neutron._i18n import _ from neutron.common import utils as n_utils from neutron.objects import trunk as trunk_objects from neutron.services.trunk import constants from neutron.services.trunk import exceptions as trunk_exc from neutron.services.trunk import utils # This layer is introduced for keeping business logic and # data persistence decoupled. def trunk_can_be_managed(context, trunk): """Validate that the trunk can be managed.""" if not trunk.admin_state_up: raise trunk_exc.TrunkDisabled(trunk_id=trunk.id) def enforce_port_deletion_rules(resource, event, trigger, **kwargs): """Prohibit the deletion of a port that's used in a trunk.""" # NOTE: the ML2 plugin properly catches these exceptions when raised, but # non-ML2 plugins might not. To address this we should move the callback # registry notification emitted in the ML2 plugin's delete_port() higher # up in the plugin hierarchy. context = kwargs['context'] port_id = kwargs['port_id'] subport_obj = trunk_objects.SubPort.get_object(context, port_id=port_id) if subport_obj: raise trunk_exc.PortInUseAsSubPort(port_id=port_id, trunk_id=subport_obj.trunk_id) trunk_obj = trunk_objects.Trunk.get_object(context, port_id=port_id) if trunk_obj: raise trunk_exc.PortInUseAsTrunkParent(port_id=port_id, trunk_id=trunk_obj.id) class TrunkPortValidator(object): def __init__(self, port_id): self.port_id = port_id self._port = None def validate(self, context, parent_port=True): """Validate that the port can be used in a trunk. :param parent_port: True if the port is intended for use as parent in a trunk. """ # TODO(tidwellr): there is a chance of a race between the # time these checks are performed and the time the trunk # creation is executed. To be revisited, if it bites. # Validate that the given port_id is not used by a subport. subports = trunk_objects.SubPort.get_objects( context, port_id=self.port_id) if subports: raise trunk_exc.TrunkPortInUse(port_id=self.port_id) # Validate that the given port_id is not used by a trunk. trunks = trunk_objects.Trunk.get_objects(context, port_id=self.port_id) if trunks: raise trunk_exc.ParentPortInUse(port_id=self.port_id) if parent_port: # if the port is being used as a parent in a trunk, check if # it can be trunked, i.e. if it is already associated to physical # resources (namely it is bound). Bound ports may be used as # trunk parents, but that depends on the underlying driver in # charge. if not self.can_be_trunked(context): raise trunk_exc.ParentPortInUse(port_id=self.port_id) else: # if the port is being used as subport in a trunk, check if it is a # port that is not actively used for other purposes, e.g. a router # port, compute port, DHCP port etc. We have no clue what the side # effects of connecting the port to a trunk would be, and it is # better to err on the side of caution and prevent the operation. self.check_not_in_use(context) return self.port_id def is_bound(self, context): """Return true if the port is bound, false otherwise.""" # Validate that the given port_id does not have a port binding. core_plugin = directory.get_plugin() self._port = core_plugin.get_port(context, self.port_id) return bool(self._port.get(portbindings.HOST_ID)) def can_be_trunked(self, context): """"Return true if a port can be trunked.""" if not self.is_bound(context): # An unbound port can be trunked, always. return True trunk_plugin = directory.get_plugin('trunk') vif_type = self._port.get(portbindings.VIF_TYPE) binding_host = self._port.get(portbindings.HOST_ID) # Determine the driver that will be in charge of the trunk: this # can be determined based on the vif type, whether or not the # driver is agent-based, and whether the host is running the agent # associated to the driver itself. host_agent_types = utils.get_agent_types_by_host(context, binding_host) drivers = [ driver for driver in trunk_plugin.registered_drivers if utils.is_driver_compatible( context, driver, vif_type, host_agent_types) ] if len(drivers) > 1: raise trunk_exc.TrunkPluginDriverConflict() elif len(drivers) == 1: return drivers[0].can_trunk_bound_port else: return False def check_not_in_use(self, context): """Raises PortInUse for ports assigned for device purposes.""" core_plugin = directory.get_plugin() self._port = core_plugin.get_port(context, self.port_id) # NOTE(armax): the trunk extension itself does not make use of the # device_id field, because it has no reason to. If need be, this # check can be altered to accommodate the change in logic. if self._port['device_id']: raise n_exc.PortInUse(net_id=self._port['network_id'], port_id=self._port['id'], device_id=self._port['device_id']) class SubPortsValidator(object): def __init__(self, segmentation_types, subports, trunk_port_id=None): self._segmentation_types = segmentation_types self.subports = subports self.trunk_port_id = trunk_port_id def validate(self, context, basic_validation=False, trunk_validation=True): """Validate that subports can be used in a trunk.""" # Perform basic validation on subports, in case subports # are not automatically screened by the API layer. if basic_validation: msg = validators.validate_subports(self.subports) if msg: raise n_exc.InvalidInput(error_message=msg) if trunk_validation: trunk_port_mtu = self._get_port_mtu(context, self.trunk_port_id) subport_mtus = self._prepare_subports(context) return [self._validate(context, s, trunk_port_mtu, subport_mtus) for s in self.subports] else: return self.subports def _prepare_subports(self, context): """Utility method to parse subports in the request The objective of this method is two-fold: * Update subports segmentation details if INHERIT is requested; * Return the MTU for each of the subport in the request. This method does two things rather than one to allow us to hit the DB once, and thus minimize the number of lookups required to learn about the segmentation type and the MTU of the networks on which subports are plugged. """ InheritIndex = ( collections.namedtuple("InheritIndex", "index has_inherit")) port_ids = {} any_has_inherit = False for i, s in enumerate(self.subports): has_inherit = s.get('segmentation_type') == constants.INHERIT any_has_inherit |= has_inherit port_ids[s['port_id']] = ( InheritIndex(index=i, has_inherit=has_inherit)) core_plugin = directory.get_plugin() if (any_has_inherit and not n_utils.is_extension_supported(core_plugin, provider.ALIAS)): msg = _("Cannot accept segmentation type %s") % constants.INHERIT raise n_exc.InvalidInput(error_message=msg) ports = core_plugin.get_ports(context, filters={'id': port_ids}) network_port_map = collections.defaultdict(list) for p in ports: network_port_map[p['network_id']].append({'port_id': p['id']}) networks = core_plugin.get_networks( context.elevated(), filters={'id': network_port_map}) subport_mtus = {} for net in networks: for port in network_port_map[net['id']]: if port_ids[port['port_id']].has_inherit: port.update( {'segmentation_id': net[provider.SEGMENTATION_ID], 'segmentation_type': net[provider.NETWORK_TYPE]}) self.subports[port_ids[port['port_id']].index] = port # To speed up the request, record the network MTU for each # subport to avoid hitting the DB more than necessary. Do # that only if the extension is available. if n_utils.is_extension_supported(core_plugin, 'net-mtu'): subport_mtus[port['port_id']] = net[api.MTU] return subport_mtus def _get_port_mtu(self, context, port_id): """ Return MTU for the network where the given port belongs to. If the network or port cannot be obtained, or if MTU is not defined, returns None. """ core_plugin = directory.get_plugin() if not n_utils.is_extension_supported(core_plugin, 'net-mtu'): return try: port = core_plugin.get_port(context, port_id) return core_plugin.get_network( context, port['network_id'])[api.MTU] except (n_exc.PortNotFound, n_exc.NetworkNotFound): # A concurrent request might have made the port or network # disappear; though during DB insertion, the subport request # will fail on integrity constraint, it is safer to return # a None MTU here. return def _raise_subport_is_parent_port(self, context, subport): if subport['port_id'] == self.trunk_port_id: raise trunk_exc.ParentPortInUse(port_id=subport['port_id']) def _raise_subport_invalid_mtu( self, context, subport, trunk_port_mtu, subport_mtus): # Check MTU sanity - subport MTU must not exceed trunk MTU. # If for whatever reason trunk_port_mtu is not available, # the MTU sanity check cannot be enforced. if trunk_port_mtu: # missing MTUs for subports is not an error condition: the # subport UUID may be invalid or non existent. subport_mtu = subport_mtus.get(subport['port_id']) if subport_mtu and subport_mtu > trunk_port_mtu: raise trunk_exc.SubPortMtuGreaterThanTrunkPortMtu( port_id=subport['port_id'], port_mtu=subport_mtu, trunk_id=self.trunk_port_id, trunk_mtu=trunk_port_mtu ) def _raise_if_segmentation_details_missing(self, subport): try: segmentation_type = subport["segmentation_type"] segmentation_id = ( converters.convert_to_int(subport["segmentation_id"])) return (segmentation_type, segmentation_id) except KeyError: msg = _("Invalid subport details '%s': missing segmentation " "information. Must specify both segmentation_id and " "segmentation_type") % subport raise n_exc.InvalidInput(error_message=msg) except n_exc.InvalidInput: msg = _("Invalid subport details: segmentation_id '%s' is " "not an integer") % subport["segmentation_id"] raise n_exc.InvalidInput(error_message=msg) def _raise_if_segmentation_details_invalid(self, segmentation_type, segmentation_id): if segmentation_type not in self._segmentation_types: msg = _("Unknown segmentation_type '%s'") % segmentation_type raise n_exc.InvalidInput(error_message=msg) if not self._segmentation_types[segmentation_type](segmentation_id): msg = _("Segmentation ID '%s' is not in range") % segmentation_id raise n_exc.InvalidInput(error_message=msg) def _raise_if_subport_is_used_in_other_trunk(self, context, subport): trunk_validator = TrunkPortValidator(subport['port_id']) trunk_validator.validate(context, parent_port=False) def _validate(self, context, subport, trunk_port_mtu, subport_mtus): self._raise_subport_is_parent_port(context, subport) self._raise_subport_invalid_mtu( context, subport, trunk_port_mtu, subport_mtus) segmentation_type, segmentation_id = ( self._raise_if_segmentation_details_missing(subport)) self._raise_if_segmentation_details_invalid( segmentation_type, segmentation_id) self._raise_if_subport_is_used_in_other_trunk(context, subport) return subport neutron-12.1.1/neutron/services/trunk/constants.py0000664000175000017500000000741213553660047022373 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # Valid trunk statuses # The trunk is happy, yay! # A trunk remains in ACTIVE state when updates like name or admin_status_up # occur. It goes back to ACTIVE state from other states (e.g. BUILD) when # logical and physical resource provisioning has completed successfully. The # attribute ADMIN_STATE_UP is not to be confused with STATUS: the former # indicates whether a trunk can be managed. If a trunk has admin_state_up # equal to false, the trunk plugin will reject any user request to manage # the trunk resources (i.e. adding/removing sub-ports). ACTIVE_STATUS # reflects the provisioning state of logical and physical resources associated # with the trunk. ACTIVE_STATUS = 'ACTIVE' # A trunk is in DOWN state any time the logical and physical resources # associated to a trunk are not in sync. This can happen in the following # cases: # a) A user has asked to create a trunk, or add(remove) subports to a # trunk in ACTIVE state. In this case, the plugin has created/updated the # logical resource, and the request has been passed along to a backend. The # physical resources associated to the trunk are in the process of being # (de)commissioned. While this happens, the logical and physical state are # mismatching, albeit temporarily during subport operations, or until a user # spawns a VM after a trunk creation. # b) A system event, such as instance deletion, has led to the deprovisioning # of the entire set of physical resources associated to the trunk. In this # case, the logical resource exists but it has no physical resources # associated with it, and the logical and physical state of the trunk are # not matching. DOWN_STATUS = 'DOWN' # A driver/backend has acknowledged the server request: once the server # notifies the driver/backend, a trunk is in BUILD state while the # backend provisions the trunk resources. BUILD_STATUS = 'BUILD' # Should any temporary system failure occur during the provisioning process, # a trunk is in DEGRADED state. This means that the trunk was only # partially provisioned, and only a subset of the subports were added # successfully to the trunk. The operation of removing/adding the faulty # subports may be attempted as a recovery measure. DEGRADED_STATUS = 'DEGRADED' # Due to unforeseen circumstances, the user request has led to a conflict, and # the trunk cannot be provisioned correctly for a subset of subports. For # instance, a subport belonging to a network might not be compatible with # the current trunk configuration, or the binding process leads to a persistent # failure. Removing the 'offending' resource may be attempted as a recovery # measure, but readding it to the trunk should lead to the same error # condition. A trunk in ERROR status should be brought back to a sane status # (i.e. any state except ERROR state) before attempting to add more subports, # therefore requests of adding more subports must be rejected to avoid # cascading errors. ERROR_STATUS = 'ERROR' # String literals for identifying trunk resources PARENT_PORT = 'parent_port' SUBPORTS = 'subports' TRUNK = 'trunk' TRUNK_PLUGIN = 'trunk_plugin' TRUNK_SUBPORT_OWNER = 'trunk:subport' # String literals for segmentation types VLAN = 'vlan' INHERIT = 'inherit' neutron-12.1.1/neutron/services/trunk/callbacks.py0000664000175000017500000000242313553660046022272 0ustar zuulzuul00000000000000# (c) Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # TODO(boden): remove this once moved over to neutron-lib payloads class TrunkPayload(object): """Payload for trunk-related callback registry notifications.""" def __init__(self, context, trunk_id, current_trunk=None, original_trunk=None, subports=None): self.context = context self.trunk_id = trunk_id self.current_trunk = current_trunk self.original_trunk = original_trunk self.subports = subports if subports else [] def __eq__(self, other): return (isinstance(other, self.__class__) and self.__dict__ == other.__dict__) def __ne__(self, other): return not self.__eq__(other) neutron-12.1.1/neutron/services/trunk/models.py0000664000175000017500000000625613553660047021647 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import sql from neutron.db import models_v2 from neutron.db import standard_attr from neutron.services.trunk import constants class Trunk(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, model_base.HasProject): admin_state_up = sa.Column( sa.Boolean(), nullable=False, server_default=sql.true()) name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE)) port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete='CASCADE'), nullable=False, unique=True) status = sa.Column( sa.String(16), nullable=False, server_default=constants.ACTIVE_STATUS) port = sa.orm.relationship( models_v2.Port, backref=sa.orm.backref('trunk_port', lazy='joined', uselist=False, cascade='delete')) sub_ports = sa.orm.relationship( 'SubPort', lazy='subquery', uselist=True, cascade="all, delete-orphan") api_collections = ['trunks'] collection_resource_map = {'trunks': 'trunk'} tag_support = True class SubPort(model_base.BASEV2): port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete='CASCADE'), primary_key=True) port = sa.orm.relationship( models_v2.Port, backref=sa.orm.backref('sub_port', lazy='joined', uselist=False, cascade='delete')) trunk_id = sa.Column(sa.String(36), sa.ForeignKey('trunks.id', ondelete='CASCADE'), nullable=False) segmentation_type = sa.Column(sa.String(32), nullable=False) segmentation_id = sa.Column(sa.Integer, nullable=False) __table_args__ = ( sa.UniqueConstraint( 'trunk_id', 'segmentation_type', 'segmentation_id', name='uniq_subport0trunk_id0segmentation_type0segmentation_id'), model_base.BASEV2.__table_args__ ) # NOTE(armax) constraints like the following are implemented via # business logic rules: # # Deletion of a trunk automatically deletes all of its subports; # Deletion of a (child) port referred by a subport is forbidden; # Deletion of a (parent) port referred by a trunk is forbidden; # A port cannot be a subport and a trunk port at the same time (nested). neutron-12.1.1/neutron/services/trunk/plugin.py0000664000175000017500000004600713553660047021660 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import context from neutron_lib.plugins import directory from neutron_lib.services import base as service_base from oslo_log import log as logging from oslo_utils import uuidutils from neutron.db import _resource_extend as resource_extend from neutron.db import api as db_api from neutron.db import common_db_mixin from neutron.db import db_base_plugin_common from neutron.objects import base as objects_base from neutron.objects import trunk as trunk_objects from neutron.services.trunk import callbacks from neutron.services.trunk import constants from neutron.services.trunk import drivers from neutron.services.trunk import exceptions as trunk_exc from neutron.services.trunk import rules from neutron.services.trunk.seg_types import validators LOG = logging.getLogger(__name__) @resource_extend.has_resource_extenders @registry.has_registry_receivers class TrunkPlugin(service_base.ServicePluginBase, common_db_mixin.CommonDbMixin): supported_extension_aliases = ["trunk", "trunk-details"] __native_pagination_support = True __native_sorting_support = True def __init__(self): self._rpc_backend = None self._drivers = [] self._segmentation_types = {} self._interfaces = set() self._agent_types = set() drivers.register() registry.subscribe(rules.enforce_port_deletion_rules, resources.PORT, events.BEFORE_DELETE) registry.publish(constants.TRUNK_PLUGIN, events.AFTER_INIT, self) for driver in self._drivers: LOG.debug('Trunk plugin loaded with driver %s', driver.name) self.check_compatibility() @staticmethod @resource_extend.extends([port_def.COLLECTION_NAME]) def _extend_port_trunk_details(port_res, port_db): """Add trunk details to a port.""" if port_db.trunk_port: subports = { x.port_id: {'segmentation_id': x.segmentation_id, 'segmentation_type': x.segmentation_type, 'port_id': x.port_id} for x in port_db.trunk_port.sub_ports } core_plugin = directory.get_plugin() ports = core_plugin.get_ports( context.get_admin_context(), filters={'id': subports}) for port in ports: subports[port['id']]['mac_address'] = port['mac_address'] trunk_details = {'trunk_id': port_db.trunk_port.id, 'sub_ports': [x for x in subports.values()]} port_res['trunk_details'] = trunk_details return port_res def check_compatibility(self): """Verify the plugin can load correctly and fail otherwise.""" self.check_driver_compatibility() self.check_segmentation_compatibility() def check_driver_compatibility(self): """Fail to load if no compatible driver is found.""" if not any([driver.is_loaded for driver in self._drivers]): raise trunk_exc.IncompatibleTrunkPluginConfiguration() def check_segmentation_compatibility(self): """Fail to load if segmentation type conflicts are found. In multi-driver deployments each loaded driver must support the same set of segmentation types consistently. """ # Get list of segmentation types for the loaded drivers. list_of_driver_seg_types = [ set(driver.segmentation_types) for driver in self._drivers if driver.is_loaded ] # If not empty, check that there is at least one we can use. compat_segmentation_types = set() if list_of_driver_seg_types: compat_segmentation_types = ( set.intersection(*list_of_driver_seg_types)) if not compat_segmentation_types: raise trunk_exc.IncompatibleDriverSegmentationTypes() # If there is at least one, make sure the validator is defined. try: for seg_type in compat_segmentation_types: self.add_segmentation_type( seg_type, validators.get_validator(seg_type)) except KeyError: raise trunk_exc.SegmentationTypeValidatorNotFound( seg_type=seg_type) def set_rpc_backend(self, backend): self._rpc_backend = backend def is_rpc_enabled(self): return self._rpc_backend is not None def register_driver(self, driver): """Register driver with trunk plugin.""" if driver.agent_type: self._agent_types.add(driver.agent_type) self._interfaces = self._interfaces | set(driver.interfaces) self._drivers.append(driver) @property def registered_drivers(self): """The registered drivers.""" return self._drivers @property def supported_interfaces(self): """A set of supported interfaces.""" return self._interfaces @property def supported_agent_types(self): """A set of supported agent types.""" return self._agent_types def add_segmentation_type(self, segmentation_type, id_validator): self._segmentation_types[segmentation_type] = id_validator LOG.debug('Added support for segmentation type %s', segmentation_type) def validate(self, context, trunk): """Return a valid trunk or raises an error if unable to do so.""" trunk_details = trunk trunk_validator = rules.TrunkPortValidator(trunk['port_id']) trunk_details['port_id'] = trunk_validator.validate(context) subports_validator = rules.SubPortsValidator( self._segmentation_types, trunk['sub_ports'], trunk['port_id']) trunk_details['sub_ports'] = subports_validator.validate(context) return trunk_details def get_plugin_description(self): return "Trunk port service plugin" @classmethod def get_plugin_type(cls): return "trunk" @db_base_plugin_common.filter_fields @db_base_plugin_common.convert_result_to_dict def get_trunk(self, context, trunk_id, fields=None): """Return information for the specified trunk.""" return self._get_trunk(context, trunk_id) @db_base_plugin_common.filter_fields @db_base_plugin_common.convert_result_to_dict def get_trunks(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Return information for available trunks.""" filters = filters or {} pager = objects_base.Pager(sorts=sorts, limit=limit, page_reverse=page_reverse, marker=marker) return trunk_objects.Trunk.get_objects(context, _pager=pager, **filters) @db_base_plugin_common.convert_result_to_dict def create_trunk(self, context, trunk): """Create a trunk.""" trunk = self.validate(context, trunk['trunk']) sub_ports = [trunk_objects.SubPort( context=context, port_id=p['port_id'], segmentation_id=p['segmentation_id'], segmentation_type=p['segmentation_type']) for p in trunk['sub_ports']] admin_state_up = trunk.get('admin_state_up', True) # NOTE(status_police): a trunk is created in DOWN status. Depending # on the nature of the create request, a driver may set the status # immediately to ACTIVE if no physical provisioning is required. # Otherwise a transition to BUILD (or ERROR) should be expected # depending on how the driver reacts. PRECOMMIT failures prevent the # trunk from being created altogether. trunk_description = trunk.get('description', "") trunk_obj = trunk_objects.Trunk(context=context, admin_state_up=admin_state_up, id=uuidutils.generate_uuid(), name=trunk.get('name', ""), description=trunk_description, project_id=trunk['tenant_id'], port_id=trunk['port_id'], status=constants.DOWN_STATUS, sub_ports=sub_ports) with db_api.autonested_transaction(context.session): trunk_obj.create() payload = callbacks.TrunkPayload(context, trunk_obj.id, current_trunk=trunk_obj) registry.notify( constants.TRUNK, events.PRECOMMIT_CREATE, self, payload=payload) registry.notify( constants.TRUNK, events.AFTER_CREATE, self, payload=payload) return trunk_obj @db_base_plugin_common.convert_result_to_dict def update_trunk(self, context, trunk_id, trunk): """Update information for the specified trunk.""" trunk_data = trunk['trunk'] with db_api.autonested_transaction(context.session): trunk_obj = self._get_trunk(context, trunk_id) original_trunk = copy.deepcopy(trunk_obj) # NOTE(status_police): a trunk status should not change during an # update_trunk(), even in face of PRECOMMIT failures. This is # because only name and admin_state_up are being affected, and # these are DB properties only. trunk_obj.update_fields(trunk_data, reset_changes=True) trunk_obj.update() payload = events.DBEventPayload( context, resource_id=trunk_id, states=(original_trunk,), desired_state=trunk_obj, request_body=trunk_data) registry.publish(constants.TRUNK, events.PRECOMMIT_UPDATE, self, payload=payload) registry.notify(constants.TRUNK, events.AFTER_UPDATE, self, payload=callbacks.TrunkPayload( context, trunk_id, original_trunk=original_trunk, current_trunk=trunk_obj)) return trunk_obj def delete_trunk(self, context, trunk_id): """Delete the specified trunk.""" with db_api.autonested_transaction(context.session): trunk = self._get_trunk(context, trunk_id) rules.trunk_can_be_managed(context, trunk) trunk_port_validator = rules.TrunkPortValidator(trunk.port_id) if not trunk_port_validator.is_bound(context): # NOTE(status_police): when a trunk is deleted, the logical # object disappears from the datastore, therefore there is no # status transition involved. If PRECOMMIT failures occur, # the trunk remains in the status where it was. trunk.delete() payload = callbacks.TrunkPayload(context, trunk_id, original_trunk=trunk) registry.notify(constants.TRUNK, events.PRECOMMIT_DELETE, self, payload=payload) else: raise trunk_exc.TrunkInUse(trunk_id=trunk_id) registry.notify(constants.TRUNK, events.AFTER_DELETE, self, payload=payload) @db_base_plugin_common.convert_result_to_dict def add_subports(self, context, trunk_id, subports): """Add one or more subports to trunk.""" with db_api.autonested_transaction(context.session): trunk = self._get_trunk(context, trunk_id) # Check for basic validation since the request body here is not # automatically validated by the API layer. subports = subports['sub_ports'] subports_validator = rules.SubPortsValidator( self._segmentation_types, subports, trunk['port_id']) subports = subports_validator.validate( context, basic_validation=True) added_subports = [] rules.trunk_can_be_managed(context, trunk) original_trunk = copy.deepcopy(trunk) # NOTE(status_police): the trunk status should transition to # DOWN (and finally in ACTIVE or ERROR), only if it is not in # ERROR status already. A user should attempt to resolve the ERROR # condition before adding more subports to the trunk. Should a # trunk be in DOWN or BUILD state (e.g. when dealing with # multiple concurrent requests), the status is still forced to # DOWN and thus can potentially overwrite an interleaving state # change to ACTIVE. Eventually the driver should bring the status # back to ACTIVE or ERROR. if trunk.status == constants.ERROR_STATUS: raise trunk_exc.TrunkInErrorState(trunk_id=trunk_id) else: trunk.update(status=constants.DOWN_STATUS) for subport in subports: obj = trunk_objects.SubPort( context=context, trunk_id=trunk_id, port_id=subport['port_id'], segmentation_type=subport['segmentation_type'], segmentation_id=subport['segmentation_id']) obj.create() trunk['sub_ports'].append(obj) added_subports.append(obj) payload = callbacks.TrunkPayload(context, trunk_id, current_trunk=trunk, original_trunk=original_trunk, subports=added_subports) if added_subports: registry.notify(constants.SUBPORTS, events.PRECOMMIT_CREATE, self, payload=payload) if added_subports: registry.notify( constants.SUBPORTS, events.AFTER_CREATE, self, payload=payload) return trunk @db_base_plugin_common.convert_result_to_dict def remove_subports(self, context, trunk_id, subports): """Remove one or more subports from trunk.""" subports = subports['sub_ports'] with db_api.autonested_transaction(context.session): trunk = self._get_trunk(context, trunk_id) original_trunk = copy.deepcopy(trunk) rules.trunk_can_be_managed(context, trunk) subports_validator = rules.SubPortsValidator( self._segmentation_types, subports) # the subports are being removed, therefore we do not need to # enforce any specific trunk rules, other than basic validation # of the request body. subports = subports_validator.validate( context, basic_validation=True, trunk_validation=False) current_subports = {p.port_id: p for p in trunk.sub_ports} removed_subports = [] for subport in subports: subport_obj = current_subports.pop(subport['port_id'], None) if not subport_obj: raise trunk_exc.SubPortNotFound(trunk_id=trunk_id, port_id=subport['port_id']) subport_obj.delete() removed_subports.append(subport_obj) del trunk.sub_ports[:] trunk.sub_ports.extend(current_subports.values()) # NOTE(status_police): the trunk status should transition to # DOWN irrespective of the status in which it is in to allow # the user to resolve potential conflicts due to prior add_subports # operations. # Should a trunk be in DOWN or BUILD state (e.g. when dealing # with multiple concurrent requests), the status is still forced # to DOWN. See add_subports() for more details. trunk.update(status=constants.DOWN_STATUS) payload = callbacks.TrunkPayload(context, trunk_id, current_trunk=trunk, original_trunk=original_trunk, subports=removed_subports) if removed_subports: registry.notify(constants.SUBPORTS, events.PRECOMMIT_DELETE, self, payload=payload) if removed_subports: registry.notify( constants.SUBPORTS, events.AFTER_DELETE, self, payload=payload) return trunk @db_base_plugin_common.filter_fields def get_subports(self, context, trunk_id, fields=None): """Return subports for the specified trunk.""" trunk = self.get_trunk(context, trunk_id) return {'sub_ports': trunk['sub_ports']} def _get_trunk(self, context, trunk_id): """Return the trunk object or raise if not found.""" obj = trunk_objects.Trunk.get_object(context, id=trunk_id) if obj is None: raise trunk_exc.TrunkNotFound(trunk_id=trunk_id) return obj # NOTE(tidwellr) Consider keying off of PRECOMMIT_UPDATE if we find # AFTER_UPDATE to be problematic for setting trunk status when a # a parent port becomes unbound. @registry.receives(resources.PORT, [events.AFTER_UPDATE]) def _trigger_trunk_status_change(self, resource, event, trigger, **kwargs): updated_port = kwargs['port'] trunk_details = updated_port.get('trunk_details') # If no trunk_details, the port is not the parent of a trunk. if not trunk_details: return context = kwargs['context'] original_port = kwargs['original_port'] orig_vif_type = original_port.get(portbindings.VIF_TYPE) new_vif_type = updated_port.get(portbindings.VIF_TYPE) vif_type_changed = orig_vif_type != new_vif_type if vif_type_changed and new_vif_type == portbindings.VIF_TYPE_UNBOUND: trunk_id = trunk_details['trunk_id'] # NOTE(status_police) Trunk status goes to DOWN when the parent # port is unbound. This means there are no more physical resources # associated with the logical resource. self.update_trunk(context, trunk_id, {'trunk': {'status': constants.DOWN_STATUS}}) neutron-12.1.1/neutron/services/trunk/__init__.py0000664000175000017500000000000013553660046022077 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/provider_configuration.py0000664000175000017500000002367613553660047024007 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import importlib import itertools import os from neutron_lib.db import constants as db_const from neutron_lib import exceptions as n_exc from oslo_config import cfg from oslo_log import log as logging from oslo_log import versionutils import stevedore from neutron._i18n import _ from neutron.conf.services import provider_configuration as prov_config from neutron.db import _utils as db_utils LOG = logging.getLogger(__name__) SERVICE_PROVIDERS = 'neutron.service_providers' # TODO(HenryG): use MovedGlobals to deprecate this. serviceprovider_opts = prov_config.serviceprovider_opts prov_config.register_service_provider_opts() class NeutronModule(object): """A Neutron extension module.""" def __init__(self, service_module): self.module_name = service_module self.repo = { 'mod': self._import_or_none(), 'ini': None } def _import_or_none(self): try: return importlib.import_module(self.module_name) except ImportError: return None def installed(self): LOG.debug("NeutronModule installed = %s", self.module_name) return self.module_name def module(self): return self.repo['mod'] # Return an INI parser for the child module def ini(self, neutron_dir=None): if self.repo['ini'] is None: ini_file = cfg.ConfigOpts() prov_config.register_service_provider_opts(ini_file) if neutron_dir is not None: neutron_dirs = [neutron_dir] else: try: neutron_dirs = cfg.CONF.config_dirs except cfg.NoSuchOptError: neutron_dirs = None if not neutron_dirs: neutron_dirs = ['/etc/neutron'] # load configuration from all matching files to reflect oslo.config # behaviour config_files = [] for neutron_dir in neutron_dirs: ini_path = os.path.join(neutron_dir, '%s.conf' % self.module_name) if os.path.exists(ini_path): config_files.append(ini_path) # NOTE(ihrachys): we could pass project=self.module_name instead to # rely on oslo.config to find configuration files for us, but: # 1. that would render neutron_dir argument ineffective; # 2. that would break loading configuration file from under # /etc/neutron in case no --config-dir is passed. # That's why we need to explicitly construct CLI here. ini_file(args=list(itertools.chain.from_iterable( ['--config-file', file_] for file_ in config_files ))) self.repo['ini'] = ini_file return self.repo['ini'] def service_providers(self): """Return the service providers for the extension module.""" providers = [] # Attempt to read the config from cfg.CONF first; when passing # --config-dir, the option is merged from all the definitions # made across all the imported config files try: providers = cfg.CONF.service_providers.service_provider except cfg.NoSuchOptError: pass # Alternatively, if the option is not available, try to load # it from the provider module's config file; this may be # necessary, if modules are loaded on the fly (DevStack may # be an example) if not providers: providers = self.ini().service_providers.service_provider if providers: versionutils.report_deprecated_feature( LOG, 'Implicit loading of service providers from ' 'neutron_*.conf files is deprecated and will be ' 'removed in Ocata release.') return providers # global scope function that should be used in service APIs def normalize_provider_name(name): return name.lower() def get_provider_driver_class(driver, namespace=SERVICE_PROVIDERS): """Return path to provider driver class In order to keep backward compatibility with configs < Kilo, we need to translate driver class paths after advanced services split. This is done by defining old class path as entry point in neutron package. """ try: driver_manager = stevedore.driver.DriverManager( namespace, driver).driver except ImportError: return driver except RuntimeError: return driver new_driver = "%s.%s" % (driver_manager.__module__, driver_manager.__name__) LOG.warning( "The configured driver %(driver)s has been moved, automatically " "using %(new_driver)s instead. Please update your config files, " "as this automatic fixup will be removed in a future release.", {'driver': driver, 'new_driver': new_driver}) return new_driver def parse_service_provider_opt(service_module='neutron'): """Parse service definition opts and returns result.""" def validate_name(name): if len(name) > db_const.NAME_FIELD_SIZE: raise n_exc.Invalid( _("Provider name %(name)s is limited by %(len)s characters") % {'name': name, 'len': db_const.NAME_FIELD_SIZE}) neutron_mod = NeutronModule(service_module) svc_providers_opt = neutron_mod.service_providers() LOG.debug("Service providers = %s", svc_providers_opt) res = [] for prov_def in svc_providers_opt: split = prov_def.split(':') try: svc_type, name, driver = split[:3] except ValueError: raise n_exc.Invalid(_("Invalid service provider format")) validate_name(name) name = normalize_provider_name(name) default = False if len(split) == 4 and split[3]: if split[3] == 'default': default = True else: msg = (_("Invalid provider format. " "Last part should be 'default' or empty: %s") % prov_def) LOG.error(msg) raise n_exc.Invalid(msg) driver = get_provider_driver_class(driver) res.append({'service_type': svc_type, 'name': name, 'driver': driver, 'default': default}) return res class ServiceProviderNotFound(n_exc.InvalidInput): message = _("Service provider '%(provider)s' could not be found " "for service type %(service_type)s") class DefaultServiceProviderNotFound(n_exc.InvalidInput): message = _("Service type %(service_type)s does not have a default " "service provider") class ServiceProviderAlreadyAssociated(n_exc.Conflict): message = _("Resource '%(resource_id)s' is already associated with " "provider '%(provider)s' for service type '%(service_type)s'") class ProviderConfiguration(object): def __init__(self, svc_module='neutron'): self.providers = {} for prov in parse_service_provider_opt(svc_module): self.add_provider(prov) def _ensure_driver_unique(self, driver): for v in self.providers.values(): if v['driver'] == driver: msg = (_("Driver %s is not unique across providers") % driver) LOG.error(msg) raise n_exc.Invalid(msg) def _ensure_default_unique(self, type, default): if not default: return for k, v in self.providers.items(): if k[0] == type and v['default']: msg = _("Multiple default providers " "for service %s") % type LOG.error(msg) raise n_exc.Invalid(msg) def add_provider(self, provider): self._ensure_driver_unique(provider['driver']) self._ensure_default_unique(provider['service_type'], provider['default']) provider_type = (provider['service_type'], provider['name']) if provider_type in self.providers: msg = (_("Multiple providers specified for service " "%s") % provider['service_type']) LOG.error(msg) raise n_exc.Invalid(msg) self.providers[provider_type] = {'driver': provider['driver'], 'default': provider['default']} def _check_entry(self, k, v, filters): # small helper to deal with query filters if not filters: return True for index, key in enumerate(['service_type', 'name']): if key in filters: if k[index] not in filters[key]: return False for key in ['driver', 'default']: if key in filters: if v[key] not in filters[key]: return False return True def get_service_providers(self, filters=None, fields=None): return [db_utils.resource_fields({'service_type': k[0], 'name': k[1], 'driver': v['driver'], 'default': v['default']}, fields) for k, v in self.providers.items() if self._check_entry(k, v, filters)] neutron-12.1.1/neutron/services/logapi/0000775000175000017500000000000013553660156020112 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/logapi/rpc/0000775000175000017500000000000013553660156020676 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/logapi/rpc/agent.py0000664000175000017500000000332713553660047022352 0ustar zuulzuul00000000000000# Copyright (C) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import helpers as log_helpers import oslo_messaging from neutron.common import rpc as n_rpc from neutron.services.logapi.common import constants as log_const class LoggingApiStub(object): """Stub proxy code for agent->server communication.""" def __init__(self): target = oslo_messaging.Target( topic=log_const.LOGGING_PLUGIN, version='1.0', namespace=log_const.RPC_NAMESPACE_LOGGING) self.rpc_client = n_rpc.get_client(target) @log_helpers.log_method_call def get_sg_log_info_for_port(self, context, port_id): """Return list of sg_log info for a port""" cctxt = self.rpc_client.prepare() return cctxt.call(context, 'get_sg_log_info_for_port', port_id=port_id) @log_helpers.log_method_call def get_sg_log_info_for_log_resources(self, context, log_resources): """Return list of sg_log info for list of log_resources""" cctxt = self.rpc_client.prepare() return cctxt.call(context, 'get_sg_log_info_for_log_resources', log_resources=log_resources) neutron-12.1.1/neutron/services/logapi/rpc/server.py0000664000175000017500000000475213553660047022565 0ustar zuulzuul00000000000000# Copyright (C) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import helpers as log_helpers import oslo_messaging from neutron.api.rpc.callbacks import events from neutron.api.rpc.handlers import resources_rpc from neutron.common import rpc as n_rpc from neutron.services.logapi.common import constants as log_const from neutron.services.logapi.common import db_api class LoggingApiSkeleton(object): """Skeleton proxy code for agent->server communication.""" # History # 1.0 Initial version target = oslo_messaging.Target( version='1.0', namespace=log_const.RPC_NAMESPACE_LOGGING) def __init__(self): self.conn = n_rpc.create_connection() self.conn.create_consumer(log_const.LOGGING_PLUGIN, [self], fanout=False) @log_helpers.log_method_call def get_sg_log_info_for_port(self, context, port_id): return db_api.get_sg_log_info_for_port(context, port_id) @log_helpers.log_method_call def get_sg_log_info_for_log_resources(self, context, log_resources): return db_api.get_sg_log_info_for_log_resources(context, log_resources) class LoggingApiNotification(object): def __init__(self): self.notification_api = resources_rpc.ResourcesPushRpcApi() @log_helpers.log_method_call def create_log(self, context, log_obj): self.notification_api.push(context, [log_obj], events.CREATED) @log_helpers.log_method_call def update_log(self, context, log_obj): self.notification_api.push(context, [log_obj], events.UPDATED) @log_helpers.log_method_call def delete_log(self, context, log_obj): self.notification_api.push(context, [log_obj], events.DELETED) @log_helpers.log_method_call def resource_update(self, context, log_objs): """Tell to agent when resources related to log_objects updated""" self.notification_api.push(context, log_objs, events.UPDATED) neutron-12.1.1/neutron/services/logapi/rpc/__init__.py0000664000175000017500000000000013553660046022773 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/logapi/drivers/0000775000175000017500000000000013553660156021570 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/logapi/drivers/manager.py0000664000175000017500000001077013553660047023560 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from oslo_log import log as logging from neutron.common import exceptions from neutron.services.logapi.common import constants as log_const from neutron.services.logapi.common import db_api from neutron.services.logapi.common import exceptions as log_exc from neutron.services.logapi.rpc import server as server_rpc LOG = logging.getLogger(__name__) def _get_param(args, kwargs, name, index): try: return kwargs[name] except KeyError: try: return args[index] except IndexError: msg = "Missing parameter %s" % name raise log_exc.LogapiDriverException(exception_msg=msg) @registry.has_registry_receivers class LoggingServiceDriverManager(object): def __init__(self): self._drivers = set() self.rpc_required = False registry.publish(log_const.LOGGING_PLUGIN, events.AFTER_INIT, self) if self.rpc_required: self._start_rpc_listeners() self.logging_rpc = server_rpc.LoggingApiNotification() @property def drivers(self): return self._drivers def register_driver(self, driver): """Register driver with logging plugin. This method is called from drivers on INIT event. """ self._drivers.add(driver) self.rpc_required |= driver.requires_rpc def _start_rpc_listeners(self): self._skeleton = server_rpc.LoggingApiSkeleton() return self._skeleton.conn.consume_in_threads() @property def supported_logging_types(self): if not self._drivers: return set() log_types = set() for driver in self._drivers: log_types |= set(driver.supported_logging_types) LOG.debug("Supported logging types (logging types supported " "by at least one loaded log_driver): %s", log_types) return log_types def call(self, method_name, *args, **kwargs): """Helper method for calling a method across all extension drivers.""" exc_list = [] for driver in self._drivers: try: getattr(driver, method_name)(*args, **kwargs) except Exception as exc: exception_msg = ("Extension driver '%(name)s' failed in " "%(method)s") exception_data = {'name': driver.name, 'method': method_name} LOG.exception(exception_msg, exception_data) exc_list.append(exc) if exc_list: raise exceptions.DriverCallError(exc_list=exc_list) if self.rpc_required: context = _get_param(args, kwargs, 'context', index=0) log_obj = _get_param(args, kwargs, 'log_obj', index=1) try: rpc_method = getattr(self.logging_rpc, method_name) except AttributeError: LOG.error("Method %s is not implemented in logging RPC", method_name) return rpc_method(context, log_obj) @registry.receives(resources.SECURITY_GROUP_RULE, [events.AFTER_CREATE, events.AFTER_DELETE]) def _handle_sg_rule_callback(self, resource, event, trigger, **kwargs): """Handle sg_rule create/delete events This method handles sg_rule events, if sg_rule bound by log_resources, it should tell to agent to update log_drivers. """ context = kwargs['context'] sg_rules = kwargs.get('security_group_rule') if sg_rules: sg_id = sg_rules.get('security_group_id') else: sg_id = kwargs.get('security_group_id') log_resources = db_api.get_logs_bound_sg(context, sg_id) if log_resources: self.call( log_const.RESOURCE_UPDATE, context, log_resources) neutron-12.1.1/neutron/services/logapi/drivers/base.py0000664000175000017500000001270713553660047023062 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from oslo_log import log as logging from neutron.services.logapi.common import constants as log_const LOG = logging.getLogger(__name__) @registry.has_registry_receivers class DriverBase(object): def __init__(self, name, vif_types, vnic_types, supported_logging_types, requires_rpc=False): """Instantiate a log driver. :param name: driver name. :param vif_types: list of interfaces (VIFs) supported. :param vnic_types: list of vnic types supported. :param supported_logging_types: list of supported logging types. :param requires_rpc: indicates if this driver expects rpc sever to notify or callback """ self.name = name self.vif_types = vif_types self.vnic_types = vnic_types self.supported_logging_types = supported_logging_types self.requires_rpc = requires_rpc # The log driver should advertise itself as supported driver by calling # register_driver() on the LoggingServiceDriverManager. Therefore, # logging plugin can discover which resources types are supported by # the log driver. @registry.receives(log_const.LOGGING_PLUGIN, [events.AFTER_INIT]) def _register(self, resource, event, trigger, payload=None): if self.is_loaded: # trigger is the LoggingServiceDriverManager trigger.register_driver(self) def is_loaded(self): """True if the driver is active for the Neutron Server. Implement this method to determine if your driver is actively configured for this Neutron Server deployment. """ return True def is_vif_type_compatible(self, vif_type): """True if the driver is compatible with the VIF type.""" return vif_type in self.vif_types def is_vnic_compatible(self, vnic_type): """True if the driver is compatible with the specific VNIC type.""" return vnic_type in self.vnic_types def is_logging_type_supported(self, log_type): supported = log_type in self.supported_logging_types if not supported: LOG.debug("logging type %(log_type)s is not supported by " "%(driver_name)s", {'log_type': log_type, 'driver_name': self.name}) return supported def create_log(self, context, log_obj): """Create a log_obj invocation. This method can be implemented by the specific driver subclass to update the backend where necessary with a specific log object. :param context: current running context information :param log_obj: a log objects being created """ def create_log_precommit(self, context, log_obj): """Create a log_obj precommit. This method can be implemented by the specific driver subclass to handle the precommit event of a log_object that is being created. :param context: current running context information :param log_obj: a log object being created """ def update_log(self, context, log_obj): """Update a log_obj invocation. This method can be implemented by the specific driver subclass to update the backend where necessary with a specific log object. :param context: current running context information :param log_obj: a log object being updated """ def update_log_precommit(self, context, log_obj): """Update a log_obj precommit. This method can be implemented by the specific driver subclass to handle update precommit event of a log_object that is being updated. :param context: current running context information :param log_obj: a log_object being updated. """ def delete_log(self, context, log_obj): """Delete a log_obj invocation. This method can be implemented by the specific driver subclass to delete the backend where necessary with a specific log object. :param context: current running context information :param log_obj: a log_object being deleted """ def delete_log_precommit(self, context, log_obj): """Delete a log_obj precommit. This method can be implemented by the specific driver subclass to handle delete precommit event of a log_object that is being deleted. :param context: current running context information :param log_obj: a log_object being deleted """ def resource_update(self, context, log_objs): """Tell the agent when resources related to log_objects are being updated :param context: current running context information :param log_objs: a list of log_objects, whose related resources are being updated. """ neutron-12.1.1/neutron/services/logapi/drivers/openvswitch/0000775000175000017500000000000013553660156024141 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/logapi/drivers/openvswitch/ovs_firewall_log.py0000664000175000017500000004407713553660047030063 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from neutron_lib import constants as lib_const from oslo_config import cfg from oslo_log import formatters from oslo_log import handlers from oslo_log import log as logging from ryu.base import app_manager from ryu.lib.packet import packet from neutron.agent import firewall from neutron.agent.linux.openvswitch_firewall import constants as ovsfw_consts from neutron.agent.linux.openvswitch_firewall import firewall as ovsfw from neutron.agent.linux.openvswitch_firewall import rules from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts from neutron.services.logapi.agent import log_extension as log_ext from neutron.services.logapi.common import constants as log_const from neutron.services.logapi.common import exceptions as log_exc from neutron.services.logapi.drivers.openvswitch import log_ryuapp LOG = logging.getLogger(__name__) OVS_FW_TO_LOG_TABLES = { ovs_consts.RULES_EGRESS_TABLE: ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE, ovs_consts.RULES_INGRESS_TABLE: ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE, } FIELDS_TO_REMOVE = ['priority', 'actions', 'dl_type', 'reg_port', 'reg_remote_group'] REMOTE_RULE_PRIORITY = 70 def setup_logging(): log_file = cfg.CONF.network_log.local_output_log_base if log_file: from logging import handlers as watch_handler log_file_handler = watch_handler.WatchedFileHandler(log_file) log_file_handler.setLevel( logging.DEBUG if cfg.CONF.debug else logging.INFO) LOG.logger.addHandler(log_file_handler) log_file_handler.setFormatter( formatters.ContextFormatter( fmt=cfg.CONF.logging_default_format_string, datefmt=cfg.CONF.log_date_format)) elif cfg.CONF.use_journal: journal_handler = handlers.OSJournalHandler() LOG.logger.addHandler(journal_handler) else: syslog_handler = handlers.OSSysLogHandler() LOG.logger.addHandler(syslog_handler) def find_deleted_sg_rules(old_port, new_ports): del_rules = list() for port in new_ports: if old_port.id == port.id: for rule in old_port.secgroup_rules: if rule not in port.secgroup_rules: del_rules.append(rule) return del_rules return del_rules class Cookie(object): def __init__(self, cookie_id, port, action, project): self.id = cookie_id self.port = port self.action = action self.project = project self.log_object_refs = set() def __eq__(self, other): return (self.id == other.id and self.action == other.action and self.port == other.port) def __hash__(self): return hash(self.id) def add_log_obj_ref(self, log_id): self.log_object_refs.add(log_id) def remove_log_obj_ref(self, log_id): self.log_object_refs.discard(log_id) @property def is_empty(self): return not self.log_object_refs class OFPortLog(object): def __init__(self, port, ovs_port, log_event): self.id = port['port_id'] self.ofport = ovs_port.ofport self.secgroup_rules = [self._update_rule(rule) for rule in port['security_group_rules']] # event can be ALL, DROP and ACCEPT self.event = log_event def _update_rule(self, rule): protocol = rule.get('protocol') if protocol is not None: if not isinstance(protocol, int) and protocol.isdigit(): rule['protocol'] = int(protocol) elif (rule.get('ethertype') == lib_const.IPv6 and protocol == lib_const.PROTO_NAME_ICMP): rule['protocol'] = lib_const.PROTO_NUM_IPV6_ICMP else: rule['protocol'] = lib_const.IP_PROTOCOL_MAP.get( protocol, protocol) return rule class OVSFirewallLoggingDriver(log_ext.LoggingDriver): SUPPORTED_LOGGING_TYPES = ['security_group'] REQUIRED_PROTOCOLS = [ ovs_consts.OPENFLOW13, ovs_consts.OPENFLOW14, ] def __init__(self, integration_bridge): self.int_br = self.initialize_bridge(integration_bridge) self._deferred = False self.log_ports = collections.defaultdict(dict) self.cookies_table = set() self.cookie_ids_to_delete = set() self.conj_id_map = ovsfw.ConjIdMap() def initialize(self, resource_rpc, **kwargs): self.resource_rpc = resource_rpc setup_logging() self.start_logapp() @staticmethod def initialize_bridge(bridge): bridge.add_protocols(*OVSFirewallLoggingDriver.REQUIRED_PROTOCOLS) # set rate limit and burst limit for controller bridge.set_controller_rate_limit(cfg.CONF.network_log.rate_limit) bridge.set_controller_burst_limit(cfg.CONF.network_log.burst_limit) return bridge.deferred(full_ordered=True) def start_logapp(self): app_mgr = app_manager.AppManager.get_instance() self.log_app = app_mgr.instantiate(log_ryuapp.OVSLogRyuApp) self.log_app.start() self.log_app.register_packet_in_handler(self.packet_in_handler) def packet_in_handler(self, ev): msg = ev.msg cookie_id = msg.cookie pkt = packet.Packet(msg.data) try: cookie_entry = self._get_cookie_by_id(cookie_id) LOG.info("action=%s project_id=%s log_resource_ids=%s vm_port=%s " "pkt=%s", cookie_entry.action, cookie_entry.project, list(cookie_entry.log_object_refs), cookie_entry.port, pkt) except log_exc.CookieNotFound: LOG.warning("Unknown cookie=%s packet_in pkt=%s", cookie_id, pkt) def defer_apply_on(self): self._deferred = True def defer_apply_off(self): if self._deferred: self.int_br.apply_flows() self._cleanup_cookies() self._deferred = False def _get_cookie(self, port_id, action): for cookie in self.cookies_table: if cookie.port == port_id and cookie.action == action: return cookie def _get_cookies_by_port(self, port_id): cookies_list = [] for cookie in self.cookies_table: if cookie.port == port_id: cookies_list.append(cookie) return cookies_list def _get_cookie_by_id(self, cookie_id): for cookie in self.cookies_table: if str(cookie.id) == str(cookie_id): return cookie raise log_exc.CookieNotFound(cookie_id=cookie_id) def _cleanup_cookies(self): cookie_ids = self.cookie_ids_to_delete self.cookie_ids_to_delete = set() for cookie_id in cookie_ids: self.int_br.br.unset_cookie(cookie_id) def generate_cookie(self, port_id, action, log_id, project_id): cookie = self._get_cookie(port_id, action) if not cookie: cookie_id = self.int_br.br.request_cookie() cookie = Cookie(cookie_id=cookie_id, port=port_id, action=action, project=project_id) self.cookies_table.add(cookie) cookie.add_log_obj_ref(log_id) return cookie.id def _schedule_cookie_deletion(self, cookie): # discard a cookie object self.cookies_table.remove(cookie) # schedule to cleanup cookie_ids later self.cookie_ids_to_delete.add(cookie.id) def start_logging(self, context, **kwargs): LOG.debug("start logging: %s", str(kwargs)) for resource_type in self.SUPPORTED_LOGGING_TYPES: # handle port updated, agent restarted if 'port_id' in kwargs: self._handle_logging('_create', context, resource_type, **kwargs) else: self._handle_log_resources_by_type( '_create', context, resource_type, **kwargs) def stop_logging(self, context, **kwargs): LOG.debug("stop logging: %s", str(kwargs)) for resource_type in self.SUPPORTED_LOGGING_TYPES: # handle port deleted if 'port_id' in kwargs: self._handle_logging('_delete', context, resource_type, **kwargs) else: self._handle_log_resources_by_type( '_delete', context, resource_type, **kwargs) def _handle_log_resources_by_type( self, action, context, resource_type, **kwargs): log_resources = [] for log_obj in kwargs.get('log_resources', []): if log_obj['resource_type'] == resource_type: log_resources.append(log_obj) if log_resources: self._handle_logging( action, context, resource_type, log_resources=log_resources) def _handle_logging(self, action, context, resource_type, **kwargs): handler_name = "%s_%s_log" % (action, resource_type) handler = getattr(self, handler_name) handler(context, **kwargs) def create_ofport_log(self, port, log_id, log_event): port_id = port['port_id'] ovs_port = self.int_br.br.get_vif_port_by_id(port_id) if ovs_port: of_port_log = OFPortLog(port, ovs_port, log_event) self.log_ports[log_id].add(of_port_log) def _create_security_group_log(self, context, **kwargs): port_id = kwargs.get('port_id') log_resources = kwargs.get('log_resources') logs_info = [] if port_id: # try to clean port flows log for port updated/create event self._cleanup_port_flows_log(port_id) logs_info = self.resource_rpc.get_sg_log_info_for_port( context, port_id=port_id) elif log_resources: logs_info = self.resource_rpc.get_sg_log_info_for_log_resources( context, log_resources=log_resources) for log_info in logs_info: log_id = log_info['id'] old_ofport_logs = self.log_ports.get(log_id, []) ports = log_info.get('ports_log') self.log_ports[log_id] = set() for port in ports: self.create_ofport_log(port, log_id, log_info.get('event')) # try to clean flows log if sg_rules are deleted for port in old_ofport_logs: del_rules = find_deleted_sg_rules( port, self.log_ports[log_id]) if del_rules: self._delete_sg_rules_flow_log(port, del_rules) for port_log in self.log_ports[log_id]: self.add_flows_from_rules(port_log, log_info) def _cleanup_port_flows_log(self, port_id): cookies_list = self._get_cookies_by_port(port_id) for cookie in cookies_list: if cookie.action == log_const.ACCEPT_EVENT: self._delete_flows( table=ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE, cookie=cookie.id) self._delete_flows( table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE, cookie=cookie.id) if cookie.action == log_const.DROP_EVENT: self._delete_flows( table=ovs_consts.DROPPED_TRAFFIC_TABLE, cookie=cookie.id) self._schedule_cookie_deletion(cookie) def _delete_security_group_log(self, context, **kwargs): # port deleted event port_id = kwargs.get('port_id') if port_id: self._cleanup_port_flows_log(port_id) # log resources deleted events for log_resource in kwargs.get('log_resources', []): log_id = log_resource.get('id') of_port_logs = self.log_ports.get(log_id, []) for of_port_log in of_port_logs: self.delete_port_flows_log(of_port_log, log_id) def _log_accept_flow(self, **flow): # log first accepted packet flow['table'] = OVS_FW_TO_LOG_TABLES[flow['table']] flow['actions'] = 'controller' # forward egress accepted packet and log if flow['table'] == ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE: flow['actions'] = 'resubmit(,%d),controller' % ( ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE) self._add_flow(**flow) def _add_flow(self, **kwargs): dl_type = kwargs.get('dl_type') ovsfw.create_reg_numbers(kwargs) if isinstance(dl_type, int): kwargs['dl_type'] = "0x{:04x}".format(dl_type) LOG.debug("Add flow firewall log %s", str(kwargs)) if self._deferred: self.int_br.add_flow(**kwargs) else: self.int_br.br.add_flow(**kwargs) def _delete_flows(self, **kwargs): ovsfw.create_reg_numbers(kwargs) if self._deferred: self.int_br.delete_flows(**kwargs) else: self.int_br.br.delete_flows(**kwargs) def _log_drop_packet(self, port, log_id, project_id): cookie = self.generate_cookie(port.id, log_const.DROP_EVENT, log_id, project_id) self._add_flow( cookie=cookie, table=ovs_consts.DROPPED_TRAFFIC_TABLE, priority=53, reg_port=port.ofport, actions='controller' ) def create_rules_generator_for_port(self, port): for rule in port.secgroup_rules: yield rule def _create_conj_flows_log(self, remote_rule, port): ethertype = remote_rule['ethertype'] direction = remote_rule['direction'] remote_sg_id = remote_rule['remote_group_id'] secgroup_id = remote_rule['security_group_id'] # we only want to log first accept packet, that means a packet with # ct_state=+new-est, reg_remote_group=conj_id + 1 will be logged flow_template = { 'priority': REMOTE_RULE_PRIORITY, 'dl_type': ovsfw_consts.ethertype_to_dl_type_map[ethertype], 'reg_port': port.ofport, 'reg_remote_group': self.conj_id_map.get_conj_id( secgroup_id, remote_sg_id, direction, ethertype) + 1, } if direction == firewall.INGRESS_DIRECTION: flow_template['table'] = ovs_consts.RULES_INGRESS_TABLE elif direction == firewall.EGRESS_DIRECTION: flow_template['table'] = ovs_consts.RULES_EGRESS_TABLE return [flow_template] def _log_accept_packet(self, port, log_id, project_id): cookie = self.generate_cookie(port.id, log_const.ACCEPT_EVENT, log_id, project_id) for rule in self.create_rules_generator_for_port(port): if 'remote_group_id' in rule: flows = self._create_conj_flows_log(rule, port) else: flows = rules.create_flows_from_rule_and_port(rule, port) for flow in flows: flow['cookie'] = cookie self._log_accept_flow(**flow) def add_flows_from_rules(self, port, log_info): # log event can be ACCEPT or DROP or ALL(both ACCEPT and DROP) event = log_info['event'] project_id = log_info['project_id'] log_id = log_info['id'] if event == log_const.ACCEPT_EVENT: self._log_accept_packet(port, log_id, project_id) elif event == log_const.DROP_EVENT: self._log_drop_packet(port, log_id, project_id) else: self._log_drop_packet(port, log_id, project_id) self._log_accept_packet(port, log_id, project_id) def _delete_accept_flows_log(self, port, log_id): cookie = self._get_cookie(port.id, log_const.ACCEPT_EVENT) if cookie: cookie.remove_log_obj_ref(log_id) if cookie.is_empty: self._delete_flows( table=ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE, cookie=cookie.id) self._delete_flows( table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE, cookie=cookie.id) self._schedule_cookie_deletion(cookie) def _delete_drop_flows_log(self, port, log_id): cookie = self._get_cookie(port.id, log_const.DROP_EVENT) if cookie: cookie.remove_log_obj_ref(log_id) if cookie.is_empty: self._delete_flows(table=ovs_consts.DROPPED_TRAFFIC_TABLE, cookie=cookie.id) self._schedule_cookie_deletion(cookie) def delete_port_flows_log(self, port, log_id): """Delete all flows log for given port and log_id""" event = port.event if event == log_const.ACCEPT_EVENT: self._delete_accept_flows_log(port, log_id) elif event == log_const.DROP_EVENT: self._delete_drop_flows_log(port, log_id) else: self._delete_accept_flows_log(port, log_id) self._delete_drop_flows_log(port, log_id) def _delete_sg_rules_flow_log(self, port, del_rules): cookie = self._get_cookie(port.id, log_const.ACCEPT_EVENT) if not cookie: return for rule in del_rules: if 'remote_group_id' in rule: flows = self._create_conj_flows_log(rule, port) else: flows = rules.create_flows_from_rule_and_port(rule, port) for flow in flows: for kw in FIELDS_TO_REMOVE: flow.pop(kw, None) flow['table'] = OVS_FW_TO_LOG_TABLES[flow['table']] flow['cookie'] = cookie.id self._delete_flows(**flow) neutron-12.1.1/neutron/services/logapi/drivers/openvswitch/log_ryuapp.py0000664000175000017500000000250313553660047026673 0ustar zuulzuul00000000000000# Copyright (C) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from ryu.base import app_manager from ryu.controller import handler from ryu.controller import ofp_event from ryu.ofproto import ofproto_v1_3 LOG = logging.getLogger(__name__) class OVSLogRyuApp(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] packet_in_handlers = [] def register_packet_in_handler(self, caller): self.packet_in_handlers.append(caller) def unregister_packet_in_handler(self, caller): self.packet_in_handlers.remove(caller) @handler.set_ev_cls(ofp_event.EventOFPPacketIn, handler.MAIN_DISPATCHER) def packet_in_handler(self, ev): for caller in self.packet_in_handlers: caller(ev) neutron-12.1.1/neutron/services/logapi/drivers/openvswitch/__init__.py0000664000175000017500000000000013553660046026236 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/logapi/drivers/openvswitch/driver.py0000664000175000017500000000262713553660047026014 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from oslo_log import log as logging from neutron.services.logapi.drivers import base LOG = logging.getLogger(__name__) DRIVER = None SUPPORTED_LOGGING_TYPES = ['security_group'] class OVSDriver(base.DriverBase): @staticmethod def create(): return OVSDriver( name='openvswitch', vif_types=[portbindings.VIF_TYPE_OVS, portbindings.VIF_TYPE_VHOST_USER], vnic_types=[portbindings.VNIC_NORMAL], supported_logging_types=SUPPORTED_LOGGING_TYPES, requires_rpc=True) def register(): """Register the driver.""" global DRIVER if not DRIVER: DRIVER = OVSDriver.create() LOG.debug('Open vSwitch logging driver registered') neutron-12.1.1/neutron/services/logapi/drivers/__init__.py0000664000175000017500000000000013553660046023665 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/logapi/agent/0000775000175000017500000000000013553660156021210 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/logapi/agent/log_extension.py0000664000175000017500000001140713553660047024441 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import contextlib from neutron_lib import constants from oslo_concurrency import lockutils import six from neutron.agent import agent_extension from neutron.api.rpc.callbacks.consumer import registry from neutron.api.rpc.callbacks import events from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc from neutron.conf.services import logging as log_cfg from neutron import manager from neutron.services.logapi.rpc import agent as agent_rpc log_cfg.register_log_driver_opts() LOGGING_DRIVERS_NAMESPACE = 'neutron.services.logapi.drivers' @six.add_metaclass(abc.ABCMeta) class LoggingDriver(object): """Defines abstract interface for logging driver""" # specific logging types are supported SUPPORTED_LOGGING_TYPES = None @abc.abstractmethod def initialize(self, resource_rpc, **kwargs): """Perform logging driver initialization. """ @abc.abstractmethod def start_logging(self, context, **kwargs): """Enable logging :param context: rpc context :param kwargs: log_resources data or port_id """ @abc.abstractmethod def stop_logging(self, context, **kwargs): """Disable logging :param context: rpc context :param kwargs: log_resources data or port_id """ def defer_apply_on(self): """Defer application of logging rule.""" pass def defer_apply_off(self): """Turn off deferral of rules and apply the logging rules now.""" pass @contextlib.contextmanager def defer_apply(self): """Defer apply context.""" self.defer_apply_on() try: yield finally: self.defer_apply_off() class LoggingExtension(agent_extension.AgentExtension): SUPPORTED_RESOURCE_TYPES = [resources.LOGGING_RESOURCE] def initialize(self, connection, driver_type): """Initialize agent extension.""" int_br = self.agent_api.request_int_br() self.log_driver = manager.NeutronManager.load_class_for_provider( LOGGING_DRIVERS_NAMESPACE, driver_type)(int_br) self.resource_rpc = agent_rpc.LoggingApiStub() self._register_rpc_consumers(connection) self.log_driver.initialize(self.resource_rpc) def consume_api(self, agent_api): self.agent_api = agent_api def _register_rpc_consumers(self, connection): endpoints = [resources_rpc.ResourcesPushRpcCallback()] for resource_type in self.SUPPORTED_RESOURCE_TYPES: registry.register(self._handle_notification, resource_type) topic = resources_rpc.resource_type_versioned_topic(resource_type) connection.create_consumer(topic, endpoints, fanout=True) @lockutils.synchronized('log-port') def _handle_notification(self, context, resource_type, log_resources, event_type): with self.log_driver.defer_apply(): if event_type == events.UPDATED: self._update_logging(context, log_resources) elif event_type == events.CREATED: self.log_driver.start_logging( context, log_resources=log_resources) elif event_type == events.DELETED: self.log_driver.stop_logging( context, log_resources=log_resources) @lockutils.synchronized('log-port') def handle_port(self, context, port): if port['device_owner'].startswith( constants.DEVICE_OWNER_COMPUTE_PREFIX): self.log_driver.start_logging(context, port_id=port['port_id']) def delete_port(self, context, port): self.log_driver.stop_logging(context, port_id=port['port_id']) def _update_logging(self, context, log_resources): enables = [] disables = [] for log_resource in log_resources: if log_resource.enabled: enables.append(log_resource) else: disables.append(log_resource) if enables: self.log_driver.start_logging(context, log_resources=enables) if disables: self.log_driver.stop_logging(context, log_resources=disables) neutron-12.1.1/neutron/services/logapi/agent/__init__.py0000664000175000017500000000000013553660046023305 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/logapi/__init__.py0000664000175000017500000000000013553660046022207 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/logapi/common/0000775000175000017500000000000013553660156021402 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/logapi/common/exceptions.py0000664000175000017500000000340313553660047024134 0ustar zuulzuul00000000000000# Copyright 2017 Fujitsu Limited. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron._i18n import _ from neutron_lib import exceptions as n_exc class LogResourceNotFound(n_exc.NotFound): message = _("Log resource %(log_id)s could not be found.") class InvalidLogResourceType(n_exc.InvalidInput): message = _("Invalid log resource_type: %(resource_type)s.") class LoggingTypeNotSupported(n_exc.Conflict): message = _("Logging type %(log_type)s is not supported on " "port %(port_id)s.") class TargetResourceNotFound(n_exc.NotFound): message = _("Target resource %(target_id)s could not be found.") class ResourceNotFound(n_exc.NotFound): message = _("Resource %(resource_id)s could not be found.") class InvalidResourceConstraint(n_exc.InvalidInput): message = _("Invalid resource constraint between resource " "(%(resource)s %(resource_id)s) and target resource " "(%(target_resource)s %(target_id)s).") class LogapiDriverException(n_exc.NeutronException): """A log api driver Exception""" message = _("Driver exception: %(exception_msg)s") class CookieNotFound(n_exc.NotFound): message = _("Cookie %(cookie_id)s could not be found.") neutron-12.1.1/neutron/services/logapi/common/constants.py0000664000175000017500000000307313553660047023772 0ustar zuulzuul00000000000000# Copyright 2017 Fujitsu Limited. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. ACCEPT_EVENT = 'ACCEPT' DROP_EVENT = 'DROP' ALL_EVENT = 'ALL' LOG_EVENTS = [ACCEPT_EVENT, DROP_EVENT, ALL_EVENT] LOGGING_PLUGIN = 'logging-plugin' # supported logging types SECURITY_GROUP = 'security_group' RPC_NAMESPACE_LOGGING = 'logging-plugin' # String literal for identifying log resource LOGGING = 'log' # Method names for Logging Driver PRECOMMIT_POSTFIX = '_precommit' CREATE_LOG = 'create_log' CREATE_LOG_PRECOMMIT = CREATE_LOG + PRECOMMIT_POSTFIX UPDATE_LOG = 'update_log' UPDATE_LOG_PRECOMMIT = UPDATE_LOG + PRECOMMIT_POSTFIX DELETE_LOG = 'delete_log' DELETE_LOG_PRECOMMIT = DELETE_LOG + PRECOMMIT_POSTFIX # Tell to agent when resources related log_objects update RESOURCE_UPDATE = 'resource_update' LOG_CALL_METHODS = ( CREATE_LOG, CREATE_LOG_PRECOMMIT, UPDATE_LOG, UPDATE_LOG_PRECOMMIT, DELETE_LOG, DELETE_LOG_PRECOMMIT, RESOURCE_UPDATE ) DIRECTION_IP_PREFIX = {'ingress': 'source_ip_prefix', 'egress': 'dest_ip_prefix'} neutron-12.1.1/neutron/services/logapi/common/validators.py0000664000175000017500000001205013553660047024121 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib.plugins import constants as plugin_const from neutron_lib.plugins import directory from oslo_log import log as logging from sqlalchemy.orm import exc as orm_exc from neutron.db import _utils as db_utils from neutron.db.models import securitygroup as sg_db from neutron.objects import ports from neutron.objects import securitygroup as sg_object from neutron.services.logapi.common import constants as log_const from neutron.services.logapi.common import exceptions as log_exc LOG = logging.getLogger(__name__) SKIPPED_VIF_TYPES = [ portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_BINDING_FAILED, ] def _check_port_bound_sg(context, sg_id, port_id): try: db_utils.model_query(context, sg_db.SecurityGroupPortBinding)\ .filter_by(security_group_id=sg_id, port_id=port_id).one() except orm_exc.NoResultFound: raise log_exc.InvalidResourceConstraint(resource='security_group', resource_id=sg_id, target_resource='port', target_id=port_id) def _check_secgroup_exists(context, sg_id): number_of_matching = sg_object.SecurityGroup.count(context, id=sg_id) if number_of_matching < 1: raise log_exc.ResourceNotFound(resource_id=sg_id) def _get_port(context, port_id): port = ports.Port.get_object(context, id=port_id) if not port: raise log_exc.TargetResourceNotFound(target_id=port_id) return port def _validate_vnic_type(driver, vnic_type, port_id): if driver.is_vnic_compatible(vnic_type): return True LOG.debug("vnic_type %(vnic_type)s of port %(port_id)s " "is not compatible with logging driver %(driver)s", {'vnic_type': vnic_type, 'port_id': port_id, 'driver': driver.name}) return False def _validate_vif_type(driver, vif_type, port_id): if driver.is_vif_type_compatible(vif_type): return True LOG.debug("vif_type %(vif_type)s of port %(port_id)s " "is not compatible with logging driver %(driver)s", {'vif_type': vif_type, 'port_id': port_id, 'driver': driver.name}) return False def validate_log_type_for_port(log_type, port): """Validate a specific logging type on a specific port This method checks whether or not existing a log_driver which supports for the logging type on the port. :param log_type: a logging type (e.g security_group) :param port: a port object """ log_plugin = directory.get_plugin(alias=plugin_const.LOG_API) drivers = log_plugin.driver_manager.drivers for driver in drivers: vif_type = port.binding.vif_type if vif_type not in SKIPPED_VIF_TYPES: if not _validate_vif_type(driver, vif_type, port['id']): continue else: vnic_type = port.binding.vnic_type if not _validate_vnic_type(driver, vnic_type, port['id']): continue if driver.is_logging_type_supported(log_type): return True return False def validate_request(context, log_data): """Validate a log request This method validates log request is satisfied or not. A ResourceNotFound will be raised if resource_id in log_data not exists or a TargetResourceNotFound will be raised if target_id in log_data not exists. This method will also raise a LoggingTypeNotSupported, if there is no log_driver supporting for resource_type in log_data. In addition, if log_data specify both resource_id and target_id. A InvalidResourceConstraint will be raised if there is no constraint between resource_id and target_id. """ resource_id = log_data.get('resource_id') target_id = log_data.get('target_id') resource_type = log_data.get('resource_type') if resource_type == log_const.SECURITY_GROUP: if resource_id: _check_secgroup_exists(context, resource_id) if target_id: port = _get_port(context, target_id) if not validate_log_type_for_port(resource_type, port): raise log_exc.LoggingTypeNotSupported(log_type=resource_type, port_id=target_id) if resource_id and target_id: _check_port_bound_sg(context, resource_id, target_id) neutron-12.1.1/neutron/services/logapi/common/__init__.py0000664000175000017500000000000013553660046023477 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/logapi/common/db_api.py0000664000175000017500000002210113553660047023165 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as const from oslo_log import log as logging from sqlalchemy.orm import exc as orm_exc from neutron.db import api as db_api from neutron.db.models import securitygroup as sg_db from neutron.objects.logapi import logging_resource as log_object from neutron.objects import ports as port_objects from neutron.objects import securitygroup as sg_object from neutron.services.logapi.common import constants from neutron.services.logapi.common import validators LOG = logging.getLogger(__name__) def _get_ports_attached_to_sg(context, sg_id): """Return a list of ports attached to a security group""" with db_api.context_manager.reader.using(context): ports = context.session.query( sg_db.SecurityGroupPortBinding.port_id).filter( sg_db.SecurityGroupPortBinding.security_group_id == sg_id).all() return [port for (port,) in ports] def _get_ports_filter_in_tenant(context, tenant_id): """Return a list of ports filter under a tenant""" try: sg_id = sg_db.SecurityGroupPortBinding.security_group_id with db_api.context_manager.reader.using(context): ports = context.session.query( sg_db.SecurityGroupPortBinding.port_id).join( sg_db.SecurityGroup, sg_db.SecurityGroup.id == sg_id).filter( sg_db.SecurityGroup.tenant_id == tenant_id).all() return list({port for (port,) in ports}) except orm_exc.NoResultFound: return [] def _get_sgs_attached_to_port(context, port_id): """Return a list of security groups are associated to a port""" with db_api.context_manager.reader.using(context): sg_ids = context.session.query( sg_db.SecurityGroupPortBinding.security_group_id).filter( sg_db.SecurityGroupPortBinding.port_id == port_id).all() return [sg_id for (sg_id, ) in sg_ids] def _get_ports_being_logged(context, sg_log): """Return a list of ports being logged for a log_resource""" target_id = sg_log['target_id'] resource_id = sg_log['resource_id'] # if 'target_id' (port_id) is specified in a log_resource if target_id is not None: port_ids = [target_id] # if 'resource_id' (sg_id) is specified in a log_resource elif resource_id is not None: port_ids = _get_ports_attached_to_sg(context, resource_id) # both 'resource_id' and 'target_id' aren't specified in a log_resource else: port_ids = _get_ports_filter_in_tenant(context, sg_log['project_id']) # list of validated ports's being logged validated_port_ids = [] ports = port_objects.Port.get_objects(context, id=port_ids) for port in ports: if port.status != const.PORT_STATUS_ACTIVE: continue if validators.validate_log_type_for_port('security_group', port): validated_port_ids.append(port.id) else: msg = ("Logging type %(log_type)s is not supported on " "port %(port_id)s." % {'log_type': 'security_group', 'port_id': port.id}) LOG.warning(msg) return validated_port_ids def _get_sg_ids_log_for_port(context, sg_log, port_id): """Return a list of security group ids being logged for a port""" sg_ids = _get_sgs_attached_to_port(context, port_id) resource_id = sg_log['resource_id'] # if resource_id is not specified if not resource_id: return sg_ids # if resource_id is specified and belong a set of sgs are # associated to port if resource_id in sg_ids: return [resource_id] return [] def _create_sg_rule_dict(rule_in_db): """Return a dict of a security group rule""" direction = rule_in_db['direction'] rule_dict = { 'direction': direction, 'ethertype': rule_in_db['ethertype']} rule_dict.update({ key: rule_in_db[key] for key in ('protocol', 'port_range_min', 'port_range_max', 'remote_group_id') if rule_in_db[key] is not None}) remote_ip_prefix = rule_in_db['remote_ip_prefix'] if remote_ip_prefix is not None: direction_ip_prefix = constants.DIRECTION_IP_PREFIX[direction] rule_dict[direction_ip_prefix] = remote_ip_prefix rule_dict['security_group_id'] = rule_in_db['security_group_id'] return rule_dict def _get_sg_rules(context, sg_log, port_id): """Return a list of sg_rules log for a port being logged""" sg_ids = _get_sg_ids_log_for_port(context, sg_log, port_id) if not sg_ids: return [] filters = {'security_group_id': sg_ids} rules_in_db = sg_object.SecurityGroupRule.get_objects(context, **filters) return [_create_sg_rule_dict(rule_in_db) for rule_in_db in rules_in_db] def _get_port_log_dict(context, port_id, sg_log): return { 'port_id': port_id, 'security_group_rules': _get_sg_rules(context, sg_log, port_id) } def _make_log_dict(context, sg_log, port_ids_log): return { 'id': sg_log['id'], 'ports_log': [_get_port_log_dict(context, port_id, sg_log) for port_id in port_ids_log], 'event': sg_log['event'], 'project_id': sg_log['project_id'] } def get_logs_bound_port(context, port_id): """Return a list of log_resources bound to a port""" port = port_objects.Port.get_object(context, id=port_id) project_id = port['project_id'] logs = log_object.Log.get_objects( context, project_id=project_id, enabled=True) is_bound = lambda log: (log.resource_id in port.security_group_ids or log.target_id == port.id or (not log.target_id and not log.resource_id)) return [log for log in logs if is_bound(log)] def get_logs_bound_sg(context, sg_id): """Return a list of log_resources bound to a security group""" project_id = context.tenant_id log_objs = log_object.Log.get_objects( context, project_id=project_id, enabled=True) log_resources = [] for log_obj in log_objs: if log_obj.resource_id == sg_id: log_resources.append(log_obj) elif log_obj.target_id: port = port_objects.Port.get_object( context, id=log_obj.target_id) if sg_id in port.security_group_ids: log_resources.append(log_obj) elif not log_obj.resource_id and not log_obj.target_id: log_resources.append(log_obj) return log_resources def get_sg_log_info_for_port(context, port_id): """Return a list of security groups log info for a port This method provides a list of security groups log info for a port. The list has format as below: [ {'id': xxx, 'ports_log': [{'port_id': u'xxx', 'security_group_rules': [{ 'direction': u'egress', 'ethertype': u'IPv6', 'security_group_id': u'xxx'}, {...}] }] 'event': u'ALL', 'project_id': u'xxx' }, ... ] :param context: current running context information :param port_id: port ID which needed to get security groups log info """ sg_logs = get_logs_bound_port(context, port_id) return [_make_log_dict(context, sg_log, [port_id]) for sg_log in sg_logs] def get_sg_log_info_for_log_resources(context, log_resources): """Return a list of security groups log info for list of log_resources This method provides a list of security groups log info for list of log_resources. The list has format as below: [ {'id': xxx, 'ports_log': [{'port_id': u'xxx', 'security_group_rules': [{ 'direction': u'egress', 'ethertype': u'IPv6', 'security_group_id': u'xxx'}, {...}] }, ...] 'event': u'ALL', 'project_id': u'xxx' }, ... ] :param context: current running context information :param log_resources: list of log_resources, which needed to get security groups log info """ logs_info = [] for sg_log in log_resources: port_ids = _get_ports_being_logged(context, sg_log) logs_info.append(_make_log_dict(context, sg_log, port_ids)) return logs_info neutron-12.1.1/neutron/services/logapi/logging_plugin.py0000664000175000017500000001207613553660047023475 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.db import api as db_api from neutron.db import db_base_plugin_common from neutron.extensions import logging as log_ext from neutron.objects import base as base_obj from neutron.objects.logapi import logging_resource as log_object from neutron.services.logapi.common import constants as log_const from neutron.services.logapi.common import exceptions as log_exc from neutron.services.logapi.common import validators from neutron.services.logapi.drivers import manager as driver_mgr class LoggingPlugin(log_ext.LoggingPluginBase): """Implementation of the Neutron logging api plugin.""" supported_extension_aliases = ['logging'] __native_pagination_support = True __native_sorting_support = True def __init__(self): super(LoggingPlugin, self).__init__() self.driver_manager = driver_mgr.LoggingServiceDriverManager() @property def supported_logging_types(self): # supported_logging_types are be dynamically loaded from log_drivers return self.driver_manager.supported_logging_types @db_base_plugin_common.filter_fields @db_base_plugin_common.convert_result_to_dict def get_logs(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Return information for available log objects""" filters = filters or {} pager = base_obj.Pager(sorts, limit, page_reverse, marker) return log_object.Log.get_objects(context, _pager=pager, **filters) def _get_log(self, context, log_id): """Return the log object or raise if not found""" log_obj = log_object.Log.get_object(context, id=log_id) if not log_obj: raise log_exc.LogResourceNotFound(log_id=log_id) return log_obj @db_base_plugin_common.filter_fields @db_base_plugin_common.convert_result_to_dict def get_log(self, context, log_id, fields=None): return self._get_log(context, log_id) @db_base_plugin_common.convert_result_to_dict def create_log(self, context, log): """Create a log object""" log_data = log['log'] resource_type = log_data['resource_type'] if resource_type not in self.supported_logging_types: raise log_exc.InvalidLogResourceType( resource_type=resource_type) validators.validate_request(context, log_data) with db_api.context_manager.writer.using(context): # body 'log' contains both tenant_id and project_id # but only latter needs to be used to create Log object. # We need to remove redundant keyword. log_data.pop('tenant_id', None) log_obj = log_object.Log(context=context, **log_data) log_obj.create() if log_obj.enabled: self.driver_manager.call( log_const.CREATE_LOG_PRECOMMIT, context, log_obj) if log_obj.enabled: self.driver_manager.call( log_const.CREATE_LOG, context, log_obj) return log_obj @db_base_plugin_common.convert_result_to_dict def update_log(self, context, log_id, log): """Update information for the specified log object""" log_data = log['log'] with db_api.context_manager.writer.using(context): log_obj = log_object.Log(context, id=log_id) log_obj.update_fields(log_data, reset_changes=True) log_obj.update() need_notify = 'enabled' in log_data if need_notify: self.driver_manager.call( log_const.UPDATE_LOG_PRECOMMIT, context, log_obj) if need_notify: self.driver_manager.call( log_const.UPDATE_LOG, context, log_obj) return log_obj def delete_log(self, context, log_id): """Delete the specified log object""" with db_api.context_manager.writer.using(context): log_obj = self._get_log(context, log_id) log_obj.delete() self.driver_manager.call( log_const.DELETE_LOG_PRECOMMIT, context, log_obj) self.driver_manager.call( log_const.DELETE_LOG, context, log_obj) def get_loggable_resources(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Get supported logging types""" return [{'type': type_} for type_ in self.supported_logging_types] neutron-12.1.1/neutron/services/tag/0000775000175000017500000000000013553660156017412 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/services/tag/tag_plugin.py0000664000175000017500000001243413553660047022120 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import functools from neutron_lib.objects import exceptions as obj_exc from neutron_lib.plugins import directory from oslo_log import helpers as log_helpers from sqlalchemy.orm import exc from neutron.db import _model_query as model_query from neutron.db import _resource_extend as resource_extend from neutron.db import api as db_api from neutron.db import common_db_mixin from neutron.db import standard_attr from neutron.db import tag_db as tag_methods from neutron.extensions import tagging from neutron.objects import tag as tag_obj # Taggable resources resource_model_map = standard_attr.get_standard_attr_resource_model_map() @resource_extend.has_resource_extenders class TagPlugin(common_db_mixin.CommonDbMixin, tagging.TagPluginBase): """Implementation of the Neutron Tag Service Plugin.""" supported_extension_aliases = ['tag', 'tag-ext', 'standard-attr-tag'] def __new__(cls, *args, **kwargs): inst = super(TagPlugin, cls).__new__(cls, *args, **kwargs) inst._filter_methods = [] # prevent GC of our partial functions for model in resource_model_map.values(): method = functools.partial(tag_methods.apply_tag_filters, model) inst._filter_methods.append(method) model_query.register_hook(model, "tag", query_hook=None, filter_hook=None, result_filters=method) return inst @staticmethod @resource_extend.extends(list(resource_model_map)) def _extend_tags_dict(response_data, db_data): if not directory.get_plugin(tagging.TAG_PLUGIN_TYPE): return tags = [tag_db.tag for tag_db in db_data.standard_attr.tags] response_data['tags'] = tags def _get_resource(self, context, resource, resource_id): model = resource_model_map[resource] try: return model_query.get_by_id(context, model, resource_id) except exc.NoResultFound: raise tagging.TagResourceNotFound(resource=resource, resource_id=resource_id) @log_helpers.log_method_call def get_tags(self, context, resource, resource_id): res = self._get_resource(context, resource, resource_id) tags = [tag_db.tag for tag_db in res.standard_attr.tags] return dict(tags=tags) @log_helpers.log_method_call def get_tag(self, context, resource, resource_id, tag): res = self._get_resource(context, resource, resource_id) if not any(tag == tag_db.tag for tag_db in res.standard_attr.tags): raise tagging.TagNotFound(tag=tag) @log_helpers.log_method_call @db_api.retry_if_session_inactive() def update_tags(self, context, resource, resource_id, body): with db_api.context_manager.writer.using(context): # We get and do all operations with objects in one session res = self._get_resource(context, resource, resource_id) new_tags = set(body['tags']) old_tags = {tag_db.tag for tag_db in res.standard_attr.tags} tags_added = new_tags - old_tags tags_removed = old_tags - new_tags if tags_removed: tag_obj.Tag.delete_objects( context, standard_attr_id=res.standard_attr_id, tag=[ tag_db.tag for tag_db in res.standard_attr.tags if tag_db.tag in tags_removed ] ) for tag in tags_added: tag_obj.Tag(context, standard_attr_id=res.standard_attr_id, tag=tag).create() return body @log_helpers.log_method_call def update_tag(self, context, resource, resource_id, tag): res = self._get_resource(context, resource, resource_id) if any(tag == tag_db.tag for tag_db in res.standard_attr.tags): return try: tag_obj.Tag(context, standard_attr_id=res.standard_attr_id, tag=tag).create() except obj_exc.NeutronDbObjectDuplicateEntry: pass @log_helpers.log_method_call def delete_tags(self, context, resource, resource_id): res = self._get_resource(context, resource, resource_id) tag_obj.Tag.delete_objects(context, standard_attr_id=res.standard_attr_id) @log_helpers.log_method_call def delete_tag(self, context, resource, resource_id, tag): res = self._get_resource(context, resource, resource_id) if not tag_obj.Tag.delete_objects(context, tag=tag, standard_attr_id=res.standard_attr_id): raise tagging.TagNotFound(tag=tag) neutron-12.1.1/neutron/services/tag/__init__.py0000664000175000017500000000000013553660046021507 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/neutron_plugin_base_v2.py0000664000175000017500000004124713553660046022045 0ustar zuulzuul00000000000000# Copyright 2011 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ v2 Neutron Plug-in API specification. :class:`NeutronPluginBaseV2` provides the definition of minimum set of methods that needs to be implemented by a v2 Neutron Plug-in. """ import abc from neutron_lib.services import base as base_services import six @six.add_metaclass(abc.ABCMeta) class NeutronPluginBaseV2(base_services.WorkerBase): @abc.abstractmethod def create_subnet(self, context, subnet): """Create a subnet. Create a subnet, which represents a range of IP addresses that can be allocated to devices :param context: neutron api request context :param subnet: dictionary describing the subnet, with keys as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. All keys will be populated. """ pass @abc.abstractmethod def update_subnet(self, context, id, subnet): """Update values of a subnet. :param context: neutron api request context :param id: UUID representing the subnet to update. :param subnet: dictionary with keys indicating fields to update. valid keys are those that have a value of True for 'allow_put' as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. """ pass @abc.abstractmethod def get_subnet(self, context, id, fields=None): """Retrieve a subnet. :param context: neutron api request context :param id: UUID representing the subnet to fetch. :param fields: a list of strings that are valid keys in a subnet dictionary as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. Only these fields will be returned. """ pass @abc.abstractmethod def get_subnets(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Retrieve a list of subnets. The contents of the list depends on the identity of the user making the request (as indicated by the context) as well as any filters. :param context: neutron api request context :param filters: a dictionary with keys that are valid keys for a subnet as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. Values in this dictionary are an iterable containing values that will be used for an exact match comparison for that value. Each result returned by this function will have matched one of the values for each key in filters. :param fields: a list of strings that are valid keys in a subnet dictionary as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. Only these fields will be returned. """ pass def get_subnets_count(self, context, filters=None): """Return the number of subnets. The result depends on the identity of the user making the request (as indicated by the context) as well as any filters. :param context: neutron api request context :param filters: a dictionary with keys that are valid keys for a network as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. Values in this dictionary are an iterable containing values that will be used for an exact match comparison for that value. Each result returned by this function will have matched one of the values for each key in filters. .. note:: this method is optional, as it was not part of the originally defined plugin API. """ raise NotImplementedError() @abc.abstractmethod def delete_subnet(self, context, id): """Delete a subnet. :param context: neutron api request context :param id: UUID representing the subnet to delete. """ pass def create_subnetpool(self, context, subnetpool): """Create a subnet pool. :param context: neutron api request context :param subnetpool: Dictionary representing the subnetpool to create. """ raise NotImplementedError() def update_subnetpool(self, context, id, subnetpool): """Update a subnet pool. :param context: neutron api request context :param subnetpool: Dictionary representing the subnetpool attributes to update. """ raise NotImplementedError() def get_subnetpool(self, context, id, fields=None): """Show a subnet pool. :param context: neutron api request context :param id: The UUID of the subnetpool to show. """ raise NotImplementedError() def get_subnetpools(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Retrieve list of subnet pools.""" raise NotImplementedError() def delete_subnetpool(self, context, id): """Delete a subnet pool. :param context: neutron api request context :param id: The UUID of the subnet pool to delete. """ raise NotImplementedError() @abc.abstractmethod def create_network(self, context, network): """Create a network. Create a network, which represents an L2 network segment which can have a set of subnets and ports associated with it. :param context: neutron api request context :param network: dictionary describing the network, with keys as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. All keys will be populated. """ pass @abc.abstractmethod def update_network(self, context, id, network): """Update values of a network. :param context: neutron api request context :param id: UUID representing the network to update. :param network: dictionary with keys indicating fields to update. valid keys are those that have a value of True for 'allow_put' as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. """ pass @abc.abstractmethod def get_network(self, context, id, fields=None): """Retrieve a network. :param context: neutron api request context :param id: UUID representing the network to fetch. :param fields: a list of strings that are valid keys in a network dictionary as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. Only these fields will be returned. """ pass @abc.abstractmethod def get_networks(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Retrieve a list of networks. The contents of the list depends on the identity of the user making the request (as indicated by the context) as well as any filters. :param context: neutron api request context :param filters: a dictionary with keys that are valid keys for a network as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. Values in this dictionary are an iterable containing values that will be used for an exact match comparison for that value. Each result returned by this function will have matched one of the values for each key in filters. :param fields: a list of strings that are valid keys in a network dictionary as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. Only these fields will be returned. """ pass def get_networks_count(self, context, filters=None): """Return the number of networks. The result depends on the identity of the user making the request (as indicated by the context) as well as any filters. :param context: neutron api request context :param filters: a dictionary with keys that are valid keys for a network as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. Values in this dictionary are an iterable containing values that will be used for an exact match comparison for that value. Each result returned by this function will have matched one of the values for each key in filters. NOTE: this method is optional, as it was not part of the originally defined plugin API. """ raise NotImplementedError() @abc.abstractmethod def delete_network(self, context, id): """Delete a network. :param context: neutron api request context :param id: UUID representing the network to delete. """ pass @abc.abstractmethod def create_port(self, context, port): """Create a port. Create a port, which is a connection point of a device (e.g., a VM NIC) to attach to a L2 neutron network. :param context: neutron api request context :param port: dictionary describing the port, with keys as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. All keys will be populated. """ pass @abc.abstractmethod def update_port(self, context, id, port): """Update values of a port. :param context: neutron api request context :param id: UUID representing the port to update. :param port: dictionary with keys indicating fields to update. valid keys are those that have a value of True for 'allow_put' as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. """ pass @abc.abstractmethod def get_port(self, context, id, fields=None): """Retrieve a port. :param context: neutron api request context :param id: UUID representing the port to fetch. :param fields: a list of strings that are valid keys in a port dictionary as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. Only these fields will be returned. """ pass @abc.abstractmethod def get_ports(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Retrieve a list of ports. The contents of the list depends on the identity of the user making the request (as indicated by the context) as well as any filters. :param context: neutron api request context :param filters: a dictionary with keys that are valid keys for a port as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. Values in this dictionary are an iterable containing values that will be used for an exact match comparison for that value. Each result returned by this function will have matched one of the values for each key in filters. :param fields: a list of strings that are valid keys in a port dictionary as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. Only these fields will be returned. """ pass def get_ports_count(self, context, filters=None): """Return the number of ports. The result depends on the identity of the user making the request (as indicated by the context) as well as any filters. :param context: neutron api request context :param filters: a dictionary with keys that are valid keys for a network as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/api/v2/attributes.py`. Values in this dictionary are an iterable containing values that will be used for an exact match comparison for that value. Each result returned by this function will have matched one of the values for each key in filters. .. note:: this method is optional, as it was not part of the originally defined plugin API. """ raise NotImplementedError() @abc.abstractmethod def delete_port(self, context, id): """Delete a port. :param context: neutron api request context :param id: UUID representing the port to delete. """ pass def start_rpc_listeners(self): """Start the RPC listeners. Most plugins start RPC listeners implicitly on initialization. In order to support multiple process RPC, the plugin needs to expose control over when this is started. .. note:: this method is optional, as it was not part of the originally defined plugin API. """ raise NotImplementedError() def start_rpc_state_reports_listener(self): """Start the RPC listeners consuming state reports queue. This optional method creates rpc consumer for REPORTS queue only. .. note:: this method is optional, as it was not part of the originally defined plugin API. """ raise NotImplementedError() def rpc_workers_supported(self): """Return whether the plugin supports multiple RPC workers. A plugin that supports multiple RPC workers should override the start_rpc_listeners method to ensure that this method returns True and that start_rpc_listeners is called at the appropriate time. Alternately, a plugin can override this method to customize detection of support for multiple rpc workers .. note:: this method is optional, as it was not part of the originally defined plugin API. """ return (self.__class__.start_rpc_listeners != NeutronPluginBaseV2.start_rpc_listeners) def rpc_state_report_workers_supported(self): """Return whether the plugin supports state report RPC workers. .. note:: this method is optional, as it was not part of the originally defined plugin API. """ return (self.__class__.start_rpc_state_reports_listener != NeutronPluginBaseV2.start_rpc_state_reports_listener) def has_native_datastore(self): """Return True if the plugin uses Neutron's native datastore. .. note:: plugins like ML2 should override this method and return True. """ return False neutron-12.1.1/neutron/objects/0000775000175000017500000000000013553660156016445 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/stdattrs.py0000664000175000017500000000222713553660047020671 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db import standard_attr from neutron.objects import base from neutron.objects.extensions import standardattributes as stdattr_obj # TODO(ihrachys): add unit tests for the object @base.NeutronObjectRegistry.register class StandardAttribute(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = standard_attr.StandardAttribute fields = { 'id': obj_fields.IntegerField(), 'resource_type': obj_fields.StringField(), } fields.update(stdattr_obj.STANDARD_ATTRIBUTES) neutron-12.1.1/neutron/objects/subnet.py0000664000175000017500000002411313553660047020317 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from oslo_versionedobjects import fields as obj_fields from neutron.common import utils from neutron.db.models import subnet_service_type from neutron.db import models_v2 from neutron.objects import base from neutron.objects import common_types from neutron.objects import network from neutron.objects import rbac_db @base.NeutronObjectRegistry.register class DNSNameServer(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models_v2.DNSNameServer primary_keys = ['address', 'subnet_id'] foreign_keys = {'Subnet': {'subnet_id': 'id'}} fields = { 'address': obj_fields.StringField(), 'subnet_id': common_types.UUIDField(), 'order': obj_fields.IntegerField() } @classmethod def get_objects(cls, context, _pager=None, validate_filters=True, **kwargs): """Fetch DNSNameServer objects with default sort by 'order' field. """ if not _pager: _pager = base.Pager() if not _pager.sorts: # (NOTE) True means ASC, False is DESC _pager.sorts = [('order', True)] return super(DNSNameServer, cls).get_objects(context, _pager, validate_filters, **kwargs) @base.NeutronObjectRegistry.register class Route(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models_v2.SubnetRoute primary_keys = ['destination', 'nexthop', 'subnet_id'] foreign_keys = {'Subnet': {'subnet_id': 'id'}} fields = { 'subnet_id': common_types.UUIDField(), 'destination': common_types.IPNetworkField(), 'nexthop': obj_fields.IPAddressField() } @classmethod def modify_fields_from_db(cls, db_obj): # TODO(korzen) remove this method when IP and CIDR decorator ready result = super(Route, cls).modify_fields_from_db(db_obj) if 'destination' in result: result['destination'] = utils.AuthenticIPNetwork( result['destination']) if 'nexthop' in result: result['nexthop'] = netaddr.IPAddress(result['nexthop']) return result @classmethod def modify_fields_to_db(cls, fields): # TODO(korzen) remove this method when IP and CIDR decorator ready result = super(Route, cls).modify_fields_to_db(fields) if 'destination' in result: result['destination'] = cls.filter_to_str(result['destination']) if 'nexthop' in fields: result['nexthop'] = cls.filter_to_str(result['nexthop']) return result @base.NeutronObjectRegistry.register class IPAllocationPool(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models_v2.IPAllocationPool foreign_keys = {'Subnet': {'subnet_id': 'id'}} fields_need_translation = { 'start': 'first_ip', 'end': 'last_ip' } fields = { 'id': common_types.UUIDField(), 'subnet_id': common_types.UUIDField(), 'start': obj_fields.IPAddressField(), 'end': obj_fields.IPAddressField() } fields_no_update = ['subnet_id'] @classmethod def modify_fields_from_db(cls, db_obj): # TODO(korzen) remove this method when IP and CIDR decorator ready result = super(IPAllocationPool, cls).modify_fields_from_db(db_obj) if 'start' in result: result['start'] = netaddr.IPAddress(result['start']) if 'end' in result: result['end'] = netaddr.IPAddress(result['end']) return result @classmethod def modify_fields_to_db(cls, fields): # TODO(korzen) remove this method when IP and CIDR decorator ready result = super(IPAllocationPool, cls).modify_fields_to_db(fields) if 'first_ip' in result: result['first_ip'] = cls.filter_to_str(result['first_ip']) if 'last_ip' in result: result['last_ip'] = cls.filter_to_str(result['last_ip']) return result @base.NeutronObjectRegistry.register class SubnetServiceType(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = subnet_service_type.SubnetServiceType foreign_keys = {'Subnet': {'subnet_id': 'id'}} primary_keys = ['subnet_id', 'service_type'] fields = { 'subnet_id': common_types.UUIDField(), 'service_type': obj_fields.StringField() } # RBAC metaclass is not applied here because 'shared' attribute of Subnet # is dependent on Network 'shared' state, and in Subnet object # it can be read-only. The necessary changes are applied manually: # - defined 'shared' attribute in 'fields' # - added 'shared' to synthetic_fields # - registered extra_filter_name for 'shared' attribute # - added loading shared attribute based on network 'rbac_entries' @base.NeutronObjectRegistry.register class Subnet(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models_v2.Subnet fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(nullable=True), 'network_id': common_types.UUIDField(), 'segment_id': common_types.UUIDField(nullable=True), # NOTE: subnetpool_id can be 'prefix_delegation' string # when the IPv6 Prefix Delegation is enabled 'subnetpool_id': obj_fields.StringField(nullable=True), 'ip_version': common_types.IPVersionEnumField(), 'cidr': common_types.IPNetworkField(), 'gateway_ip': obj_fields.IPAddressField(nullable=True), 'allocation_pools': obj_fields.ListOfObjectsField('IPAllocationPool', nullable=True), 'enable_dhcp': obj_fields.BooleanField(nullable=True), 'shared': obj_fields.BooleanField(nullable=True), 'dns_nameservers': obj_fields.ListOfObjectsField('DNSNameServer', nullable=True), 'host_routes': obj_fields.ListOfObjectsField('Route', nullable=True), 'ipv6_ra_mode': common_types.IPV6ModeEnumField(nullable=True), 'ipv6_address_mode': common_types.IPV6ModeEnumField(nullable=True), 'service_types': obj_fields.ListOfStringsField(nullable=True) } synthetic_fields = ['allocation_pools', 'dns_nameservers', 'host_routes', 'service_types', 'shared'] foreign_keys = {'Network': {'network_id': 'id'}} fields_no_update = ['project_id', 'network_id', 'segment_id'] fields_need_translation = { 'host_routes': 'routes' } def __init__(self, context=None, **kwargs): super(Subnet, self).__init__(context, **kwargs) self.add_extra_filter_name('shared') def obj_load_attr(self, attrname): if attrname == 'shared': return self._load_shared() if attrname == 'service_types': return self._load_service_types() super(Subnet, self).obj_load_attr(attrname) def _load_shared(self, db_obj=None): if db_obj: # NOTE(korzen) db_obj is passed when Subnet object is loaded # from DB rbac_entries = db_obj.get('rbac_entries') or {} shared = (rbac_db.RbacNeutronDbObjectMixin. is_network_shared(self.obj_context, rbac_entries)) else: # NOTE(korzen) this case is used when Subnet object was # instantiated and without DB interaction (get_object(s), update, # create), it should be rare case to load 'shared' by that method shared = (rbac_db.RbacNeutronDbObjectMixin. get_shared_with_tenant(self.obj_context.elevated(), network.NetworkRBAC, self.network_id, self.project_id)) setattr(self, 'shared', shared) self.obj_reset_changes(['shared']) def _load_service_types(self, db_obj=None): if db_obj: service_types = db_obj.get('service_types', []) else: service_types = SubnetServiceType.get_objects(self.obj_context, subnet_id=self.id) self.service_types = [service_type['service_type'] for service_type in service_types] self.obj_reset_changes(['service_types']) def from_db_object(self, db_obj): super(Subnet, self).from_db_object(db_obj) self._load_shared(db_obj) self._load_service_types(db_obj) @classmethod def modify_fields_from_db(cls, db_obj): # TODO(korzen) remove this method when IP and CIDR decorator ready result = super(Subnet, cls).modify_fields_from_db(db_obj) if 'cidr' in result: result['cidr'] = utils.AuthenticIPNetwork(result['cidr']) if 'gateway_ip' in result and result['gateway_ip'] is not None: result['gateway_ip'] = netaddr.IPAddress(result['gateway_ip']) return result @classmethod def modify_fields_to_db(cls, fields): # TODO(korzen) remove this method when IP and CIDR decorator ready result = super(Subnet, cls).modify_fields_to_db(fields) if 'cidr' in result: result['cidr'] = cls.filter_to_str(result['cidr']) if 'gateway_ip' in result and result['gateway_ip'] is not None: result['gateway_ip'] = cls.filter_to_str(result['gateway_ip']) return result neutron-12.1.1/neutron/objects/README.rst0000664000175000017500000000115213553660046020131 0ustar zuulzuul00000000000000=============== Neutron Objects =============== Directory ========= This directory is designed to contain all modules which have objects definitions shipped with core Neutron. The files and directories located inside of this directory should follow the guidelines below. Structure --------- The Neutron objects tree should have the following structure: * The expected directory structure is flat, except for the ML2 plugins. All ML2 plugin objects should fall under the plugins subdirectory (i.e. plugins/ml2/gre_allocation). * Module names should use singular forms for nouns (network.py, not networks.py). neutron-12.1.1/neutron/objects/securitygroup.py0000664000175000017500000001116413553660047021745 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.common import utils from neutron.db.models import securitygroup as sg_models from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class SecurityGroup(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = sg_models.SecurityGroup fields = { 'id': common_types.UUIDField(), 'name': obj_fields.StringField(nullable=True), 'project_id': obj_fields.StringField(nullable=True), 'is_default': obj_fields.BooleanField(default=False), 'rules': common_types.ListOfObjectsField( 'SecurityGroupRule', nullable=True ), # NOTE(ihrachys): we don't include source_rules that is present in the # model until we realize it's actually needed } fields_no_update = ['project_id', 'is_default'] synthetic_fields = ['is_default', 'rules'] extra_filter_names = {'is_default'} lazy_fields = set(['rules']) def create(self): # save is_default before super() resets it to False is_default = self.is_default with self.db_context_writer(self.obj_context): super(SecurityGroup, self).create() if is_default: default_group = DefaultSecurityGroup( self.obj_context, project_id=self.project_id, security_group_id=self.id) default_group.create() self.is_default = True self.obj_reset_changes(['is_default']) def from_db_object(self, db_obj): super(SecurityGroup, self).from_db_object(db_obj) if self._load_synthetic_fields: setattr(self, 'is_default', bool(db_obj.get('default_security_group'))) self.obj_reset_changes(['is_default']) @base.NeutronObjectRegistry.register class DefaultSecurityGroup(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = sg_models.DefaultSecurityGroup fields = { 'project_id': obj_fields.StringField(), 'security_group_id': common_types.UUIDField(), } fields_no_update = ['security_group_id'] primary_keys = ['project_id'] @base.NeutronObjectRegistry.register class SecurityGroupRule(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = sg_models.SecurityGroupRule fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'security_group_id': common_types.UUIDField(), 'remote_group_id': common_types.UUIDField(nullable=True), 'direction': common_types.FlowDirectionEnumField(nullable=True), 'ethertype': common_types.EtherTypeEnumField(nullable=True), 'protocol': common_types.IpProtocolEnumField(nullable=True), 'port_range_min': common_types.PortRangeWith0Field(nullable=True), 'port_range_max': common_types.PortRangeWith0Field(nullable=True), 'remote_ip_prefix': common_types.IPNetworkField(nullable=True), } foreign_keys = {'SecurityGroup': {'security_group_id': 'id'}} fields_no_update = ['project_id', 'security_group_id', 'remote_group_id'] # TODO(sayalilunkad): get rid of it once we switch the db model to using # custom types. @classmethod def modify_fields_to_db(cls, fields): result = super(SecurityGroupRule, cls).modify_fields_to_db(fields) remote_ip_prefix = result.get('remote_ip_prefix') if remote_ip_prefix: result['remote_ip_prefix'] = cls.filter_to_str(remote_ip_prefix) return result # TODO(sayalilunkad): get rid of it once we switch the db model to using # custom types. @classmethod def modify_fields_from_db(cls, db_obj): fields = super(SecurityGroupRule, cls).modify_fields_from_db(db_obj) if 'remote_ip_prefix' in fields: fields['remote_ip_prefix'] = ( utils.AuthenticIPNetwork(fields['remote_ip_prefix'])) return fields neutron-12.1.1/neutron/objects/flavor.py0000664000175000017500000000615313553660046020313 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.models import flavor as models from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class FlavorServiceProfileBinding(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.FlavorServiceProfileBinding primary_keys = ['flavor_id', 'service_profile_id'] fields = { 'flavor_id': common_types.UUIDField(), 'service_profile_id': common_types.UUIDField(), } @base.NeutronObjectRegistry.register class ServiceProfile(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.ServiceProfile synthetic_fields = ['flavor_ids'] fields = { 'id': common_types.UUIDField(), 'description': obj_fields.StringField(nullable=True), 'driver': obj_fields.StringField(), 'enabled': obj_fields.BooleanField(default=True), 'metainfo': obj_fields.StringField(nullable=True), 'flavor_ids': common_types.SetOfUUIDsField(nullable=True, default=None) } def from_db_object(self, db_obj): super(ServiceProfile, self).from_db_object(db_obj) if db_obj.get('flavors', []): self.flavor_ids = { fl.flavor_id for fl in db_obj.flavors } else: self.flavor_ids = set() self.obj_reset_changes(['flavor_ids']) @base.NeutronObjectRegistry.register class Flavor(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.Flavor synthetic_fields = ['service_profile_ids'] fields = { 'id': common_types.UUIDField(), 'name': obj_fields.StringField(nullable=True), 'description': obj_fields.StringField(nullable=True), 'enabled': obj_fields.BooleanField(default=True), 'service_type': obj_fields.StringField(nullable=True), 'service_profile_ids': common_types.SetOfUUIDsField(nullable=True, default=None) } def from_db_object(self, db_obj): super(Flavor, self).from_db_object(db_obj) if db_obj.get('service_profiles', []): self.service_profile_ids = { sp.service_profile_id for sp in db_obj.service_profiles } else: self.service_profile_ids = set() self.obj_reset_changes(['service_profile_ids']) neutron-12.1.1/neutron/objects/plugins/0000775000175000017500000000000013553660156020126 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/plugins/__init__.py0000664000175000017500000000000013553660046022223 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/plugins/ml2/0000775000175000017500000000000013553660156020620 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/plugins/ml2/vxlanallocation.py0000664000175000017500000000305713553660046024373 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.models.plugins.ml2 import vxlanallocation as vxlan_model from neutron.objects import base from neutron.objects import common_types from neutron.objects.plugins.ml2 import base as ml2_base @base.NeutronObjectRegistry.register class VxlanAllocation(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = vxlan_model.VxlanAllocation primary_keys = ['vxlan_vni'] fields = { 'vxlan_vni': obj_fields.IntegerField(), 'allocated': obj_fields.BooleanField(default=False), } @base.NeutronObjectRegistry.register class VxlanEndpoint(ml2_base.EndpointBase): # Version 1.0: Initial version VERSION = '1.0' db_model = vxlan_model.VxlanEndpoints fields = { 'ip_address': obj_fields.IPAddressField(), 'udp_port': common_types.PortRangeField(), 'host': obj_fields.StringField(nullable=True), } neutron-12.1.1/neutron/objects/plugins/ml2/vlanallocation.py0000664000175000017500000000232413553660046024177 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.models.plugins.ml2 import vlanallocation as vlan_alloc_model from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class VlanAllocation(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = vlan_alloc_model.VlanAllocation fields = { 'physical_network': obj_fields.StringField(), 'vlan_id': common_types.VlanIdRangeField(), 'allocated': obj_fields.BooleanField(), } primary_keys = ['physical_network', 'vlan_id'] neutron-12.1.1/neutron/objects/plugins/ml2/flatallocation.py0000664000175000017500000000177413553660046024175 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.models.plugins.ml2 import flatallocation from neutron.objects import base @base.NeutronObjectRegistry.register class FlatAllocation(base.NeutronDbObject): # Version 1.0: Initial Version VERSION = '1.0' db_model = flatallocation.FlatAllocation fields = { 'physical_network': obj_fields.StringField() } primary_keys = ['physical_network'] neutron-12.1.1/neutron/objects/plugins/ml2/base.py0000664000175000017500000000236713553660046022112 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron.objects import base class EndpointBase(base.NeutronDbObject): primary_keys = ['ip_address'] @classmethod def modify_fields_from_db(cls, db_obj): result = super(EndpointBase, cls).modify_fields_from_db(db_obj) if 'ip_address' in result: result['ip_address'] = netaddr.IPAddress(result['ip_address']) return result @classmethod def modify_fields_to_db(cls, fields): result = super(EndpointBase, cls).modify_fields_to_db(fields) if 'ip_address' in fields: result['ip_address'] = cls.filter_to_str(result['ip_address']) return result neutron-12.1.1/neutron/objects/plugins/ml2/geneveallocation.py0000664000175000017500000000272513553660046024515 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.models.plugins.ml2 import geneveallocation from neutron.objects import base from neutron.objects.plugins.ml2 import base as ml2_base @base.NeutronObjectRegistry.register class GeneveAllocation(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = geneveallocation.GeneveAllocation primary_keys = ['geneve_vni'] fields = { 'geneve_vni': obj_fields.IntegerField(), 'allocated': obj_fields.BooleanField(default=False), } @base.NeutronObjectRegistry.register class GeneveEndpoint(ml2_base.EndpointBase): # Version 1.0: Initial version VERSION = '1.0' db_model = geneveallocation.GeneveEndpoints fields = { 'ip_address': obj_fields.IPAddressField(), 'host': obj_fields.StringField(nullable=True), } neutron-12.1.1/neutron/objects/plugins/ml2/greallocation.py0000664000175000017500000000270613553660046024020 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.models.plugins.ml2 import gre_allocation_endpoints as gre_model from neutron.objects import base from neutron.objects.plugins.ml2 import base as ml2_base @base.NeutronObjectRegistry.register class GreAllocation(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = gre_model.GreAllocation primary_keys = ['gre_id'] fields = { 'gre_id': obj_fields.IntegerField(), 'allocated': obj_fields.BooleanField(default=False) } @base.NeutronObjectRegistry.register class GreEndpoint(ml2_base.EndpointBase): # Version 1.0: Initial version VERSION = '1.0' db_model = gre_model.GreEndpoints fields = { 'ip_address': obj_fields.IPAddressField(), 'host': obj_fields.StringField(nullable=True) } neutron-12.1.1/neutron/objects/plugins/ml2/__init__.py0000664000175000017500000000000013553660046022715 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/utils.py0000664000175000017500000000327613553660047020166 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from neutron.common import exceptions def convert_filters(**kwargs): result = copy.deepcopy(kwargs) if 'tenant_id' in result: if 'project_id' in result: raise exceptions.TenantIdProjectIdFilterConflict() result['project_id'] = result.pop('tenant_id') return result class StringMatchingFilterObj(object): @property def is_contains(self): return bool(getattr(self, "contains", False)) @property def is_starts(self): return bool(getattr(self, "starts", False)) @property def is_ends(self): return bool(getattr(self, "ends", False)) class StringContains(StringMatchingFilterObj): def __init__(self, matching_string): super(StringContains, self).__init__() self.contains = matching_string class StringStarts(StringMatchingFilterObj): def __init__(self, matching_string): super(StringStarts, self).__init__() self.starts = matching_string class StringEnds(StringMatchingFilterObj): def __init__(self, matching_string): super(StringEnds, self).__init__() self.ends = matching_string neutron-12.1.1/neutron/objects/ports.py0000664000175000017500000004145013553660047020171 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron_lib import constants from oslo_utils import versionutils from oslo_versionedobjects import fields as obj_fields from neutron.common import utils from neutron.db.models import dns as dns_models from neutron.db.models import l3 from neutron.db.models import securitygroup as sg_models from neutron.db import models_v2 from neutron.objects import base from neutron.objects import common_types from neutron.objects.db import api as obj_db_api from neutron.objects.qos import binding from neutron.plugins.ml2 import models as ml2_models class PortBindingBase(base.NeutronDbObject): foreign_keys = { 'Port': {'port_id': 'id'}, } @classmethod def modify_fields_to_db(cls, fields): result = super(PortBindingBase, cls).modify_fields_to_db(fields) for field in ['profile', 'vif_details']: if field in result: # dump field into string, set '' if empty '{}' or None result[field] = ( cls.filter_to_json_str(result[field], default='')) return result @classmethod def modify_fields_from_db(cls, db_obj): fields = super(PortBindingBase, cls).modify_fields_from_db(db_obj) if 'vif_details' in fields: # load string from DB into dict, set None if vif_details is '' fields['vif_details'] = ( cls.load_json_from_str(fields['vif_details'])) if 'profile' in fields: # load string from DB into dict, set {} if profile is '' fields['profile'] = ( cls.load_json_from_str(fields['profile'], default={})) return fields @base.NeutronObjectRegistry.register class PortBinding(PortBindingBase): # Version 1.0: Initial version VERSION = '1.0' db_model = ml2_models.PortBinding fields = { 'port_id': common_types.UUIDField(), 'host': obj_fields.StringField(), 'profile': common_types.DictOfMiscValuesField(), 'vif_type': obj_fields.StringField(), 'vif_details': common_types.DictOfMiscValuesField(nullable=True), 'vnic_type': obj_fields.StringField(), 'status': common_types.PortBindingStatusEnumField( default=constants.ACTIVE), } primary_keys = ['port_id', 'host'] @base.NeutronObjectRegistry.register class DistributedPortBinding(PortBindingBase): # Version 1.0: Initial version VERSION = '1.0' db_model = ml2_models.DistributedPortBinding fields = { 'port_id': common_types.UUIDField(), 'host': obj_fields.StringField(), 'profile': common_types.DictOfMiscValuesField(), 'vif_type': obj_fields.StringField(), 'vif_details': common_types.DictOfMiscValuesField(nullable=True), 'vnic_type': obj_fields.StringField(), # NOTE(ihrachys): Fields below are specific to this type of binding. In # the future, we could think of converging different types of bindings # into a single field 'status': obj_fields.StringField(), 'router_id': obj_fields.StringField(nullable=True), } primary_keys = ['host', 'port_id'] @base.NeutronObjectRegistry.register class PortBindingLevel(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = ml2_models.PortBindingLevel primary_keys = ['port_id', 'host', 'level'] fields = { 'port_id': common_types.UUIDField(), 'host': obj_fields.StringField(), 'level': obj_fields.IntegerField(), 'driver': obj_fields.StringField(nullable=True), 'segment': obj_fields.ObjectField( 'NetworkSegment', nullable=True ), } synthetic_fields = ['segment'] foreign_keys = { 'Port': {'port_id': 'id'}, } @classmethod def get_objects(cls, context, _pager=None, validate_filters=True, **kwargs): if not _pager: _pager = base.Pager() if not _pager.sorts: # (NOTE) True means ASC, False is DESC _pager.sorts = [('port_id', True), ('level', True)] return super(PortBindingLevel, cls).get_objects( context, _pager, validate_filters, **kwargs) @base.NeutronObjectRegistry.register class IPAllocation(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models_v2.IPAllocation fields = { 'port_id': common_types.UUIDField(nullable=True), 'subnet_id': common_types.UUIDField(), 'network_id': common_types.UUIDField(), 'ip_address': obj_fields.IPAddressField(), } fields_no_update = fields.keys() primary_keys = ['subnet_id', 'network_id', 'ip_address'] foreign_keys = { 'Port': {'port_id': 'id'}, } # TODO(rossella_s): get rid of it once we switch the db model to using # custom types. @classmethod def modify_fields_to_db(cls, fields): result = super(IPAllocation, cls).modify_fields_to_db(fields) if 'ip_address' in result: result['ip_address'] = cls.filter_to_str(result['ip_address']) return result # TODO(rossella_s): get rid of it once we switch the db model to using # custom types. @classmethod def modify_fields_from_db(cls, db_obj): fields = super(IPAllocation, cls).modify_fields_from_db(db_obj) if 'ip_address' in fields: fields['ip_address'] = netaddr.IPAddress(fields['ip_address']) return fields @classmethod def get_alloc_by_subnet_id(cls, context, subnet_id, device_owner, exclude=True): # need to join with ports table as IPAllocation's port # is not joined eagerly and thus producing query which yields # incorrect results if exclude: alloc_db = (context.session.query(models_v2.IPAllocation). filter_by(subnet_id=subnet_id).join(models_v2.Port). filter(~models_v2.Port.device_owner. in_(device_owner)).first()) else: alloc_db = (context.session.query(models_v2.IPAllocation). filter_by(subnet_id=subnet_id).join(models_v2.Port). filter(models_v2.Port.device_owner. in_(device_owner)).first()) if exclude and alloc_db: return super(IPAllocation, cls)._load_object(context, alloc_db) if alloc_db: return True @base.NeutronObjectRegistry.register class PortDNS(base.NeutronDbObject): # Version 1.0: Initial version # Version 1.1: Add dns_domain attribute VERSION = '1.1' db_model = dns_models.PortDNS primary_keys = ['port_id'] foreign_keys = { 'Port': {'port_id': 'id'}, } fields = { 'port_id': common_types.UUIDField(), 'current_dns_name': common_types.DomainNameField(), 'current_dns_domain': common_types.DomainNameField(), 'previous_dns_name': common_types.DomainNameField(), 'previous_dns_domain': common_types.DomainNameField(), 'dns_name': common_types.DomainNameField(), 'dns_domain': common_types.DomainNameField(), } def obj_make_compatible(self, primitive, target_version): _target_version = versionutils.convert_version_to_tuple(target_version) if _target_version < (1, 1): primitive.pop('dns_domain', None) @base.NeutronObjectRegistry.register class SecurityGroupPortBinding(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = sg_models.SecurityGroupPortBinding fields = { 'port_id': common_types.UUIDField(), 'security_group_id': common_types.UUIDField(), } primary_keys = ['port_id', 'security_group_id'] @base.NeutronObjectRegistry.register class Port(base.NeutronDbObject): # Version 1.0: Initial version # Version 1.1: Add data_plane_status field VERSION = '1.1' db_model = models_v2.Port fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(nullable=True), 'network_id': common_types.UUIDField(), 'mac_address': common_types.MACAddressField(), 'admin_state_up': obj_fields.BooleanField(), 'device_id': obj_fields.StringField(), 'device_owner': obj_fields.StringField(), 'status': obj_fields.StringField(), 'allowed_address_pairs': obj_fields.ListOfObjectsField( 'AllowedAddressPair', nullable=True ), 'binding': obj_fields.ObjectField( 'PortBinding', nullable=True ), 'data_plane_status': obj_fields.ObjectField( 'PortDataPlaneStatus', nullable=True ), 'dhcp_options': obj_fields.ListOfObjectsField( 'ExtraDhcpOpt', nullable=True ), 'distributed_binding': obj_fields.ObjectField( 'DistributedPortBinding', nullable=True ), 'dns': obj_fields.ObjectField('PortDNS', nullable=True), 'fixed_ips': obj_fields.ListOfObjectsField( 'IPAllocation', nullable=True ), # TODO(ihrachys): consider converting to boolean 'security': obj_fields.ObjectField( 'PortSecurity', nullable=True ), 'security_group_ids': common_types.SetOfUUIDsField( nullable=True, # TODO(ihrachys): how do we safely pass a mutable default? default=None, ), 'qos_policy_id': common_types.UUIDField(nullable=True, default=None), 'binding_levels': obj_fields.ListOfObjectsField( 'PortBindingLevel', nullable=True ), # TODO(ihrachys): consider adding a 'dns_assignment' fully synthetic # field in later object iterations } extra_filter_names = {'security_group_ids'} fields_no_update = ['project_id', 'network_id'] synthetic_fields = [ 'allowed_address_pairs', 'binding', 'binding_levels', 'data_plane_status', 'dhcp_options', 'distributed_binding', 'dns', 'fixed_ips', 'qos_policy_id', 'security', 'security_group_ids', ] fields_need_translation = { 'binding': 'port_binding', 'dhcp_options': 'dhcp_opts', 'distributed_binding': 'distributed_port_binding', 'security': 'port_security', } def create(self): fields = self.obj_get_changes() with self.db_context_writer(self.obj_context): sg_ids = self.security_group_ids if sg_ids is None: sg_ids = set() qos_policy_id = self.qos_policy_id super(Port, self).create() if 'security_group_ids' in fields: self._attach_security_groups(sg_ids) if 'qos_policy_id' in fields: self._attach_qos_policy(qos_policy_id) def update(self): fields = self.obj_get_changes() with self.db_context_writer(self.obj_context): super(Port, self).update() if 'security_group_ids' in fields: self._attach_security_groups(fields['security_group_ids']) if 'qos_policy_id' in fields: self._attach_qos_policy(fields['qos_policy_id']) def _attach_qos_policy(self, qos_policy_id): binding.QosPolicyPortBinding.delete_objects( self.obj_context, port_id=self.id) if qos_policy_id: port_binding_obj = binding.QosPolicyPortBinding( self.obj_context, policy_id=qos_policy_id, port_id=self.id) port_binding_obj.create() self.qos_policy_id = qos_policy_id self.obj_reset_changes(['qos_policy_id']) def _attach_security_groups(self, sg_ids): # TODO(ihrachys): consider introducing an (internal) object for the # binding to decouple database operations a bit more obj_db_api.delete_objects( SecurityGroupPortBinding, self.obj_context, port_id=self.id) if sg_ids: for sg_id in sg_ids: self._attach_security_group(sg_id) self.security_group_ids = sg_ids self.obj_reset_changes(['security_group_ids']) def _attach_security_group(self, sg_id): obj_db_api.create_object( SecurityGroupPortBinding, self.obj_context, {'port_id': self.id, 'security_group_id': sg_id} ) @classmethod def get_objects(cls, context, _pager=None, validate_filters=True, security_group_ids=None, **kwargs): if security_group_ids: ports_with_sg = cls.get_ports_ids_by_security_groups( context, security_group_ids) port_ids = kwargs.get("id", []) if port_ids: kwargs['id'] = list(set(port_ids) & set(ports_with_sg)) else: kwargs['id'] = ports_with_sg return super(Port, cls).get_objects(context, _pager, validate_filters, **kwargs) @classmethod def modify_fields_to_db(cls, fields): result = super(Port, cls).modify_fields_to_db(fields) # TODO(rossella_s): get rid of it once we switch the db model to using # custom types. if 'mac_address' in result: result['mac_address'] = cls.filter_to_str(result['mac_address']) # convert None to [] if 'distributed_port_binding' in result: result['distributed_port_binding'] = ( result['distributed_port_binding'] or [] ) return result @classmethod def modify_fields_from_db(cls, db_obj): fields = super(Port, cls).modify_fields_from_db(db_obj) # TODO(rossella_s): get rid of it once we switch the db model to using # custom types. if 'mac_address' in fields: fields['mac_address'] = utils.AuthenticEUI(fields['mac_address']) distributed_port_binding = fields.get('distributed_binding') if distributed_port_binding: fields['distributed_binding'] = fields['distributed_binding'][0] else: fields['distributed_binding'] = None return fields def from_db_object(self, db_obj): super(Port, self).from_db_object(db_obj) # extract security group bindings if db_obj.get('security_groups', []): self.security_group_ids = { sg.security_group_id for sg in db_obj.security_groups } else: self.security_group_ids = set() self.obj_reset_changes(['security_group_ids']) # extract qos policy binding if db_obj.get('qos_policy_binding'): self.qos_policy_id = ( db_obj.qos_policy_binding.policy_id ) else: self.qos_policy_id = None self.obj_reset_changes(['qos_policy_id']) def obj_make_compatible(self, primitive, target_version): _target_version = versionutils.convert_version_to_tuple(target_version) if _target_version < (1, 1): primitive.pop('data_plane_status', None) @classmethod def get_ports_by_router(cls, context, router_id, owner, subnet): rport_qry = context.session.query(models_v2.Port).join( l3.RouterPort) ports = rport_qry.filter( l3.RouterPort.router_id == router_id, l3.RouterPort.port_type == owner, models_v2.Port.network_id == subnet['network_id'] ) return [cls._load_object(context, db_obj) for db_obj in ports.all()] @classmethod def get_ports_ids_by_security_groups(cls, context, security_group_ids): query = context.session.query(sg_models.SecurityGroupPortBinding) query = query.filter( sg_models.SecurityGroupPortBinding.security_group_id.in_( security_group_ids)) return [port_binding['port_id'] for port_binding in query.all()] @classmethod def get_ports_by_binding_type_and_host(cls, context, binding_type, host): query = context.session.query(models_v2.Port).join( ml2_models.PortBinding) query = query.filter( ml2_models.PortBinding.vif_type == binding_type, ml2_models.PortBinding.host == host) return [cls._load_object(context, db_obj) for db_obj in query.all()] neutron-12.1.1/neutron/objects/l3_hamode.py0000664000175000017500000000476713553660047020667 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.common import constants from neutron.db.models import agent as agent_model from neutron.db.models import l3ha from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class L3HARouterAgentPortBinding(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = l3ha.L3HARouterAgentPortBinding fields = { 'port_id': common_types.UUIDField(), 'router_id': common_types.UUIDField(), 'l3_agent_id': common_types.UUIDField(nullable=True), 'state': common_types.HARouterEnumField( default=constants.HA_ROUTER_STATE_STANDBY), } primary_keys = ['port_id'] fields_no_update = ['router_id', 'port_id', 'l3_agent_id'] @classmethod def get_l3ha_filter_host_router(cls, context, router_ids, host): query = context.session.query(l3ha.L3HARouterAgentPortBinding) if host: query = query.join(agent_model.Agent).filter( agent_model.Agent.host == host) query = query.filter( l3ha.L3HARouterAgentPortBinding.router_id.in_(router_ids)) return query.all() @base.NeutronObjectRegistry.register class L3HARouterNetwork(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = l3ha.L3HARouterNetwork fields = { 'network_id': common_types.UUIDField(), 'project_id': obj_fields.StringField(), } primary_keys = ['network_id', 'project_id'] @base.NeutronObjectRegistry.register class L3HARouterVRIdAllocation(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = l3ha.L3HARouterVRIdAllocation fields = { 'network_id': common_types.UUIDField(), 'vr_id': obj_fields.IntegerField() } primary_keys = ['network_id', 'vr_id'] neutron-12.1.1/neutron/objects/quota.py0000664000175000017500000001214413553660046020150 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields import sqlalchemy as sa from sqlalchemy import sql from neutron.db.quota import models from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class ResourceDelta(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.ResourceDelta primary_keys = ['resource', 'reservation_id'] foreign_keys = {'Reservation': {'reservation_id': 'id'}} fields = { 'resource': obj_fields.StringField(), 'reservation_id': common_types.UUIDField(), 'amount': obj_fields.IntegerField(nullable=True), } @base.NeutronObjectRegistry.register class Reservation(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.Reservation fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'expiration': obj_fields.DateTimeField(tzinfo_aware=False, nullable=True), 'resource_deltas': obj_fields.ListOfObjectsField( ResourceDelta.__name__, nullable=True), } synthetic_fields = ['resource_deltas'] def create(self): deltas = self.resource_deltas with self.db_context_writer(self.obj_context): super(Reservation, self).create() if deltas: for delta in deltas: delta.reservation_id = self.id delta.create() self.resource_deltas.append(delta) self.obj_reset_changes(['resource_deltas']) @classmethod def delete_expired(cls, context, now, project_id): resv_query = context.session.query(models.Reservation) if project_id: project_expr = (models.Reservation.project_id == project_id) else: project_expr = sql.true() # TODO(manjeets) Fetch and delete objects using # object/db/api.py once comparison operations are # supported resv_query = resv_query.filter(sa.and_( project_expr, models.Reservation.expiration < now)) return resv_query.delete() @classmethod def get_total_reservations_map(cls, context, now, project_id, resources, expired): if not resources: return resv_query = context.session.query( models.ResourceDelta.resource, models.Reservation.expiration, sql.func.sum(models.ResourceDelta.amount)).join( models.Reservation) if expired: exp_expr = (models.Reservation.expiration < now) else: exp_expr = (models.Reservation.expiration >= now) resv_query = resv_query.filter(sa.and_( models.Reservation.project_id == project_id, models.ResourceDelta.resource.in_(resources), exp_expr)).group_by( models.ResourceDelta.resource, models.Reservation.expiration) return dict((resource, total_reserved) for (resource, exp, total_reserved) in resv_query) @base.NeutronObjectRegistry.register class Quota(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.Quota fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'resource': obj_fields.StringField(nullable=True), 'limit': obj_fields.IntegerField(nullable=True), } @base.NeutronObjectRegistry.register class QuotaUsage(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.QuotaUsage primary_keys = ['resource', 'project_id'] fields = { 'resource': obj_fields.StringField(), 'project_id': obj_fields.StringField(), 'dirty': obj_fields.BooleanField(default=False), 'in_use': obj_fields.IntegerField(default=0), 'reserved': obj_fields.IntegerField(default=0), } @classmethod def get_object_dirty_protected(cls, context, **kwargs): query = context.session.query(cls.db_model) query = query.filter_by(**cls.modify_fields_to_db(kwargs)) # NOTE(manjeets) as lock mode was just for protecting dirty bits # an update on dirty will prevent the race. query.filter_by(dirty=True).update({'dirty': True}) res = query.first() if res: return cls._load_object(context, res) neutron-12.1.1/neutron/objects/subnetpool.py0000664000175000017500000001025713553660046021214 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from oslo_versionedobjects import fields as obj_fields from neutron.db import models_v2 as models from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class SubnetPool(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.SubnetPool fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(nullable=True), 'ip_version': common_types.IPVersionEnumField(), 'default_prefixlen': common_types.IPNetworkPrefixLenField(), 'min_prefixlen': common_types.IPNetworkPrefixLenField(), 'max_prefixlen': common_types.IPNetworkPrefixLenField(), 'shared': obj_fields.BooleanField(), 'is_default': obj_fields.BooleanField(), 'default_quota': obj_fields.IntegerField(nullable=True), 'hash': obj_fields.StringField(nullable=True), 'address_scope_id': common_types.UUIDField(nullable=True), 'prefixes': common_types.ListOfIPNetworksField(nullable=True) } fields_no_update = ['id', 'project_id'] synthetic_fields = ['prefixes'] def from_db_object(self, db_obj): super(SubnetPool, self).from_db_object(db_obj) self.prefixes = [] self.prefixes = [ prefix.cidr for prefix in db_obj.get('prefixes', []) ] self.obj_reset_changes(['prefixes']) def _attach_prefixes(self, prefixes): SubnetPoolPrefix.delete_objects(self.obj_context, subnetpool_id=self.id) for prefix in prefixes: SubnetPoolPrefix(self.obj_context, subnetpool_id=self.id, cidr=prefix).create() self.prefixes = prefixes self.obj_reset_changes(['prefixes']) # TODO(ihrachys): Consider extending base to trigger registered methods def create(self): fields = self.obj_get_changes() with self.db_context_writer(self.obj_context): prefixes = self.prefixes super(SubnetPool, self).create() if 'prefixes' in fields: self._attach_prefixes(prefixes) # TODO(ihrachys): Consider extending base to trigger registered methods def update(self): fields = self.obj_get_changes() with self.db_context_writer(self.obj_context): super(SubnetPool, self).update() if 'prefixes' in fields: self._attach_prefixes(fields['prefixes']) @base.NeutronObjectRegistry.register class SubnetPoolPrefix(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.SubnetPoolPrefix fields = { 'subnetpool_id': common_types.UUIDField(), 'cidr': common_types.IPNetworkField(), } primary_keys = ['subnetpool_id', 'cidr'] # TODO(ihrachys): get rid of it once we switch the db model to using CIDR # custom type @classmethod def modify_fields_to_db(cls, fields): result = super(SubnetPoolPrefix, cls).modify_fields_to_db(fields) if 'cidr' in result: result['cidr'] = cls.filter_to_str(result['cidr']) return result # TODO(ihrachys): get rid of it once we switch the db model to using CIDR # custom type @classmethod def modify_fields_from_db(cls, db_obj): fields = super(SubnetPoolPrefix, cls).modify_fields_from_db(db_obj) if 'cidr' in fields: fields['cidr'] = netaddr.IPNetwork(fields['cidr']) return fields neutron-12.1.1/neutron/objects/agent.py0000664000175000017500000001617713553660047020130 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as const from oslo_versionedobjects import fields as obj_fields from sqlalchemy import func from neutron.agent.common import utils from neutron.db.models import agent as agent_model from neutron.db.models import l3agent as rb_model from neutron.db.models import l3ha as l3ha_model from neutron.db import models_v2 from neutron.objects import base from neutron.objects import common_types from neutron.objects import utils as obj_utils @base.NeutronObjectRegistry.register class Agent(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = agent_model.Agent fields = { 'id': common_types.UUIDField(), 'agent_type': obj_fields.StringField(), 'binary': obj_fields.StringField(), 'topic': obj_fields.StringField(), 'host': obj_fields.StringField(), 'availability_zone': obj_fields.StringField(nullable=True), 'admin_state_up': obj_fields.BooleanField(default=True), 'started_at': obj_fields.DateTimeField(tzinfo_aware=False), 'created_at': obj_fields.DateTimeField(tzinfo_aware=False), 'heartbeat_timestamp': obj_fields.DateTimeField(tzinfo_aware=False), 'description': obj_fields.StringField(nullable=True), 'configurations': common_types.DictOfMiscValuesField(), 'resource_versions': common_types.DictOfMiscValuesField(nullable=True), 'load': obj_fields.IntegerField(default=0), } @classmethod def modify_fields_to_db(cls, fields): result = super(Agent, cls).modify_fields_to_db(fields) if ('configurations' in result and not isinstance(result['configurations'], obj_utils.StringMatchingFilterObj)): # dump configuration into string, set '' if empty '{}' result['configurations'] = ( cls.filter_to_json_str(result['configurations'], default='')) if ('resource_versions' in result and not isinstance(result['resource_versions'], obj_utils.StringMatchingFilterObj)): # dump resource version into string, set None if empty '{}' or None result['resource_versions'] = ( cls.filter_to_json_str(result['resource_versions'])) return result @classmethod def modify_fields_from_db(cls, db_obj): fields = super(Agent, cls).modify_fields_from_db(db_obj) if 'configurations' in fields: # load string from DB, set {} if configuration is '' fields['configurations'] = ( cls.load_json_from_str(fields['configurations'], default={})) if 'resource_versions' in fields: # load string from DB, set None if resource_version is None or '' fields['resource_versions'] = ( cls.load_json_from_str(fields['resource_versions'])) return fields @property def is_active(self): return not utils.is_agent_down(self.heartbeat_timestamp) # TODO(ihrachys) reuse query builder from # get_l3_agents_ordered_by_num_routers @classmethod def get_l3_agent_with_min_routers(cls, context, agent_ids): """Return l3 agent with the least number of routers.""" with cls.db_context_reader(context): query = context.session.query( agent_model.Agent, func.count( rb_model.RouterL3AgentBinding.router_id ).label('count')).outerjoin( rb_model.RouterL3AgentBinding).group_by( agent_model.Agent.id, rb_model.RouterL3AgentBinding .l3_agent_id).order_by('count') res = query.filter(agent_model.Agent.id.in_(agent_ids)).first() agent_obj = cls._load_object(context, res[0]) return agent_obj @classmethod def get_l3_agents_ordered_by_num_routers(cls, context, agent_ids): with cls.db_context_reader(context): query = (context.session.query(agent_model.Agent, func.count( rb_model.RouterL3AgentBinding.router_id) .label('count')). outerjoin(rb_model.RouterL3AgentBinding). group_by(agent_model.Agent.id). filter(agent_model.Agent.id.in_(agent_ids)). order_by('count')) agents = [cls._load_object(context, record[0]) for record in query] return agents @classmethod def get_ha_agents(cls, context, network_id=None, router_id=None): if not (network_id or router_id): return [] query = context.session.query(agent_model.Agent.host) query = query.join(l3ha_model.L3HARouterAgentPortBinding, l3ha_model.L3HARouterAgentPortBinding.l3_agent_id == agent_model.Agent.id) if router_id: query = query.filter( l3ha_model.L3HARouterAgentPortBinding.router_id == router_id).all() elif network_id: query = query.join(models_v2.Port, models_v2.Port.device_id == l3ha_model.L3HARouterAgentPortBinding.router_id) query = query.filter(models_v2.Port.network_id == network_id, models_v2.Port.status == const.PORT_STATUS_ACTIVE, models_v2.Port.device_owner.in_( (const.DEVICE_OWNER_HA_REPLICATED_INT, const.DEVICE_OWNER_ROUTER_SNAT))).all() # L3HARouterAgentPortBinding will have l3 agent ids of hosting agents. # But we need l2 agent(for tunneling ip) while creating FDB entries. hosts = [host[0] for host in query] agents = cls.get_objects(context, host=hosts) return agents @classmethod def _get_agents_by_availability_zones_and_agent_type( cls, context, agent_type, availability_zones): query = context.session.query( agent_model.Agent).filter_by( agent_type=agent_type).group_by( agent_model.Agent.availability_zone) query = query.filter( agent_model.Agent.availability_zone.in_(availability_zones)).all() agents = [cls._load_object(context, record) for record in query] return agents @classmethod def get_objects_by_agent_mode(cls, context, agent_mode=None, **kwargs): mode_filter = obj_utils.StringContains(agent_mode) return cls.get_objects(context, configurations=mode_filter, **kwargs) neutron-12.1.1/neutron/objects/trunk.py0000664000175000017500000001223213553660047020161 0ustar zuulzuul00000000000000# Copyright (c) 2016 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions as n_exc from neutron_lib.objects import exceptions as o_exc from oslo_db import exception as o_db_exc from oslo_utils import versionutils from oslo_versionedobjects import fields as obj_fields from neutron.objects import base from neutron.objects import common_types from neutron.services.trunk import exceptions as t_exc from neutron.services.trunk import models @base.NeutronObjectRegistry.register class SubPort(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.SubPort primary_keys = ['port_id'] foreign_keys = {'Trunk': {'trunk_id': 'id'}} fields = { 'port_id': common_types.UUIDField(), 'trunk_id': common_types.UUIDField(), 'segmentation_type': obj_fields.StringField(), 'segmentation_id': obj_fields.IntegerField(), } fields_no_update = ['segmentation_type', 'segmentation_id', 'trunk_id'] def to_dict(self): _dict = super(SubPort, self).to_dict() # trunk_id is redundant in the subport dict. _dict.pop('trunk_id') return _dict def create(self): with self.db_context_writer(self.obj_context): try: super(SubPort, self).create() except o_db_exc.DBReferenceError as ex: if ex.key_table is None: # NOTE(ivc): 'key_table' is provided by 'oslo.db' [1] # only for a limited set of database backends (i.e. # MySQL and PostgreSQL). Other database backends # (including SQLite) would have 'key_table' set to None. # We emulate the 'key_table' support for such database # backends. # # [1] https://github.com/openstack/oslo.db/blob/3fadd5a # /oslo_db/sqlalchemy/exc_filters.py#L190-L203 if not Trunk.get_object(self.obj_context, id=self.trunk_id): ex.key_table = Trunk.db_model.__tablename__ if ex.key_table == Trunk.db_model.__tablename__: raise t_exc.TrunkNotFound(trunk_id=self.trunk_id) raise n_exc.PortNotFound(port_id=self.port_id) except o_exc.NeutronDbObjectDuplicateEntry: raise t_exc.DuplicateSubPort( segmentation_type=self.segmentation_type, segmentation_id=self.segmentation_id, trunk_id=self.trunk_id) @base.NeutronObjectRegistry.register class Trunk(base.NeutronDbObject): # Version 1.0: Initial version # Version 1.1: Changed tenant_id to project_id VERSION = '1.1' db_model = models.Trunk fields = { 'admin_state_up': obj_fields.BooleanField(), 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(), 'name': obj_fields.StringField(), 'port_id': common_types.UUIDField(), 'status': obj_fields.StringField(), 'sub_ports': obj_fields.ListOfObjectsField(SubPort.__name__), } fields_no_update = ['project_id', 'port_id'] synthetic_fields = ['sub_ports'] def create(self): with self.db_context_writer(self.obj_context): sub_ports = [] if self.obj_attr_is_set('sub_ports'): sub_ports = self.sub_ports try: super(Trunk, self).create() except o_db_exc.DBReferenceError: raise n_exc.PortNotFound(port_id=self.port_id) if sub_ports: for sub_port in sub_ports: sub_port.trunk_id = self.id sub_port.create() self.sub_ports.append(sub_port) self.obj_reset_changes(['sub_ports']) def update(self, **kwargs): self.update_fields(kwargs) super(Trunk, self).update() # TODO(hichihara): For tag mechanism. This will be removed in bug/1704137 def to_dict(self): _dict = super(Trunk, self).to_dict() try: _dict['tags'] = [t.tag for t in self.db_obj.standard_attr.tags] except AttributeError: # AttrtibuteError can be raised when accessing self.db_obj # or self.db_obj.standard_attr pass return _dict def obj_make_compatible(self, primitive, target_version): _target_version = versionutils.convert_version_to_tuple(target_version) if _target_version < (1, 1): primitive['tenant_id'] = primitive.pop('project_id') neutron-12.1.1/neutron/objects/base.py0000664000175000017500000010742213553660047017736 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import collections import copy import functools import itertools from neutron_lib import exceptions as n_exc from neutron_lib.objects import exceptions as o_exc from oslo_db import exception as obj_exc from oslo_db.sqlalchemy import utils as db_utils from oslo_log import log as logging from oslo_serialization import jsonutils from oslo_utils import versionutils from oslo_versionedobjects import base as obj_base from oslo_versionedobjects import exception as obj_exception from oslo_versionedobjects import fields as obj_fields import six from sqlalchemy import orm from neutron._i18n import _ from neutron.db import api as db_api from neutron.db import standard_attr from neutron.objects import common_types from neutron.objects.db import api as obj_db_api from neutron.objects.extensions import standardattributes LOG = logging.getLogger(__name__) _NO_DB_MODEL = object() def get_updatable_fields(cls, fields): fields = fields.copy() for field in cls.fields_no_update: if field in fields: del fields[field] return fields def get_object_class_by_model(model): for obj_class in NeutronObjectRegistry.obj_classes().values(): obj_class = obj_class[0] if getattr(obj_class, 'db_model', _NO_DB_MODEL) is model: return obj_class raise o_exc.NeutronDbObjectNotFoundByModel(model=model.__name__) def register_filter_hook_on_model(model, filter_name): obj_class = get_object_class_by_model(model) obj_class.add_extra_filter_name(filter_name) class LazyQueryIterator(six.Iterator): def __init__(self, obj_class, lazy_query): self.obj_class = obj_class self.context = None self.query = lazy_query def __iter__(self): self.results = self.query.all() self.i = 0 return self def __next__(self): if self.i >= len(self.results): raise StopIteration() item = self.obj_class._load_object(self.context, self.results[self.i]) self.i += 1 return item class Pager(object): ''' This class represents a pager object. It is consumed by get_objects to specify sorting and pagination criteria. ''' def __init__(self, sorts=None, limit=None, page_reverse=None, marker=None): ''' :param sorts: A list of (key, direction) tuples. direction: True == ASC, False == DESC :param limit: maximum number of items to return :param page_reverse: True if sort direction is reversed. :param marker: the last item of the previous page; when used, returns next results after the marker resource. ''' self.sorts = sorts self.limit = limit self.page_reverse = page_reverse self.marker = marker def to_kwargs(self, context, obj_cls): res = { attr: getattr(self, attr) for attr in ('sorts', 'limit', 'page_reverse') if getattr(self, attr) is not None } if self.marker and self.limit: res['marker_obj'] = obj_db_api.get_object( obj_cls, context, id=self.marker) return res def __str__(self): return str(self.__dict__) def __eq__(self, other): return self.__dict__ == other.__dict__ class NeutronObjectRegistry(obj_base.VersionedObjectRegistry): _registry = None def __new__(cls, *args, **kwargs): # TODO(slaweq): this should be moved back to oslo.versionedobjects # lib as soon as bug https://bugs.launchpad.net/neutron/+bug/1731948 # will be fixed and OVO's registry class will support defining custom # registries for objects. # NOTE(slaweq): it is overridden method # oslo_versionedobjects.base.VersionedObjectRegistry.__new__ # We need to overwrite it to use separate registry for Neutron's # objects. # This is necessary to avoid clash in naming objects between Neutron # and e.g. os-vif (for example Route or Subnet objects are used in # both) if not NeutronObjectRegistry._registry: NeutronObjectRegistry._registry = object.__new__( NeutronObjectRegistry, *args, **kwargs) NeutronObjectRegistry._registry._obj_classes = \ collections.defaultdict(list) self = object.__new__(cls, *args, **kwargs) self._obj_classes = ( NeutronObjectRegistry._registry._obj_classes) return self @six.add_metaclass(abc.ABCMeta) class NeutronObject(obj_base.VersionedObject, obj_base.VersionedObjectDictCompat, obj_base.ComparableVersionedObject): synthetic_fields = [] extra_filter_names = set() # To use lazy queries for child objects, you must set the ORM # relationship in the db model to 'dynamic'. By default, all # children are eager loaded. lazy_fields = set() def __init__(self, context=None, **kwargs): super(NeutronObject, self).__init__(context, **kwargs) self._load_synthetic_fields = True self.obj_set_defaults() def _synthetic_fields_items(self): for field in self.synthetic_fields: if field in self: yield field, getattr(self, field) def to_dict(self): dict_ = {} # not using obj_to_primitive because it skips extra fields for name, value in self.items(): # we have to check if item is in fields because obj_extra_fields # is included in self.items() if name in self.fields and name not in self.synthetic_fields: value = self.fields[name].to_primitive(self, name, value) if name == 'tenant_id': if ('project_id' in self.fields and not self.obj_attr_is_set('project_id')): continue dict_[name] = value for field_name, value in self._synthetic_fields_items(): field = self.fields[field_name] if (isinstance(field, obj_fields.ListOfObjectsField) or isinstance(field, common_types.ListOfObjectsField)): dict_[field_name] = [obj.to_dict() for obj in value] elif isinstance(field, obj_fields.ObjectField): dict_[field_name] = ( dict_[field_name].to_dict() if value else None) else: dict_[field_name] = field.to_primitive(self, field_name, value) return dict_ @classmethod def is_synthetic(cls, field): return field in cls.synthetic_fields @classmethod def is_object_field(cls, field): return (isinstance(cls.fields[field], obj_fields.ListOfObjectsField) or isinstance(cls.fields[field], common_types.ListOfObjectsField) or isinstance(cls.fields[field], obj_fields.ObjectField)) @classmethod def obj_class_from_name(cls, objname, objver): """Returns a class from the registry based on a name and version.""" # NOTE(slaweq): it is override method # oslo_versionedobjects.base.VersionedObject.obj_class_from_name # We need to override it to use Neutron's objects registry class # (NeutronObjectRegistry) instead of original VersionedObjectRegistry # class from oslo_versionedobjects # This is necessary to avoid clash in naming objects between Neutron # and e.g. os-vif (for example Route or Subnet objects are used in # both) if objname not in NeutronObjectRegistry.obj_classes(): LOG.error('Unable to instantiate unregistered object type ' '%(objtype)s', dict(objtype=objname)) raise obj_exception.UnsupportedObjectError(objtype=objname) # NOTE(comstud): If there's not an exact match, return the highest # compatible version. The objects stored in the class are sorted # such that highest version is first, so only set compatible_match # once below. compatible_match = None for objclass in NeutronObjectRegistry.obj_classes()[objname]: if objclass.VERSION == objver: return objclass if (not compatible_match and versionutils.is_compatible(objver, objclass.VERSION)): compatible_match = objclass if compatible_match: return compatible_match # As mentioned above, latest version is always first in the list. latest_ver = ( NeutronObjectRegistry.obj_classes()[objname][0].VERSION) raise obj_exception.IncompatibleObjectVersion(objname=objname, objver=objver, supported=latest_ver) @classmethod def clean_obj_from_primitive(cls, primitive, context=None): obj = cls.obj_from_primitive(primitive, context) obj.obj_reset_changes() return obj @classmethod def get_object(cls, context, fields=None, **kwargs): raise NotImplementedError() @classmethod def add_extra_filter_name(cls, filter_name): """Register filter passed from API layer. :param filter_name: Name of the filter passed in the URL Filter names are validated in validate_filters() method which by default allows filters based on fields' names. Extensions can create new filter names. Such names must be registered to particular object with this method. """ cls.extra_filter_names.add(filter_name) @classmethod def validate_filters(cls, **kwargs): bad_filters = {key for key in kwargs if key not in cls.fields or cls.is_synthetic(key)} bad_filters.difference_update(cls.extra_filter_names) if bad_filters: bad_filters = ', '.join(bad_filters) msg = _("'%s' is not supported for filtering") % bad_filters raise n_exc.InvalidInput(error_message=msg) @classmethod @abc.abstractmethod def get_objects(cls, context, _pager=None, validate_filters=True, fields=None, **kwargs): raise NotImplementedError() @classmethod def _update_objects(cls, objects, values): if not isinstance(objects, collections.Sequence): objects = (objects, ) for obj in objects: for k, v in values.items(): setattr(obj, k, v) obj.update() return len(objects) @classmethod def update_object(cls, context, values, validate_filters=True, **kwargs): obj = cls.get_object( context, validate_filters=validate_filters, **kwargs) if obj: cls._update_objects(obj, values) return obj @classmethod def update_objects(cls, context, values, validate_filters=True, **kwargs): objs = cls.get_objects( context, validate_filters=validate_filters, **kwargs) return cls._update_objects(objs, values) @classmethod def delete_objects(cls, context, validate_filters=True, **kwargs): objs = cls.get_objects( context, validate_filters=validate_filters, **kwargs) for obj in objs: obj.delete() return len(objs) def create(self): raise NotImplementedError() def update(self): raise NotImplementedError() def delete(self): raise NotImplementedError() @classmethod def count(cls, context, validate_filters=True, **kwargs): '''Count the number of objects matching filtering criteria.''' return len( cls.get_objects( context, validate_filters=validate_filters, **kwargs)) def _detach_db_obj(func): """Decorator to detach db_obj from the session.""" @functools.wraps(func) def decorator(self, *args, **kwargs): synthetic_changed = bool(self._get_changed_synthetic_fields()) with self.db_context_writer(self.obj_context): res = func(self, *args, **kwargs) # some relationship based fields may be changed since we captured # the model, let's refresh it for the latest database state if synthetic_changed: # TODO(ihrachys) consider refreshing just changed attributes self.obj_context.session.refresh(self.db_obj) # detach the model so that consequent fetches don't reuse it self.obj_context.session.expunge(self.db_obj) return res return decorator class DeclarativeObject(abc.ABCMeta): def __init__(cls, name, bases, dct): super(DeclarativeObject, cls).__init__(name, bases, dct) if 'project_id' in cls.fields: obj_extra_fields_set = set(cls.obj_extra_fields) obj_extra_fields_set.add('tenant_id') cls.obj_extra_fields = list(obj_extra_fields_set) setattr(cls, 'tenant_id', property(lambda x: x.get('project_id', None))) fields_no_update_set = set(cls.fields_no_update) for base in itertools.chain([cls], bases): keys_set = set() if hasattr(base, 'primary_keys'): keys_set.update(base.primary_keys) if hasattr(base, 'obj_extra_fields'): keys_set.update(base.obj_extra_fields) for key in keys_set: if key in cls.fields or key in cls.obj_extra_fields: fields_no_update_set.add(key) cls.fields_no_update = list(fields_no_update_set) model = getattr(cls, 'db_model', None) if model: # generate unique_keys from the model if not getattr(cls, 'unique_keys', None): cls.unique_keys = [] obj_field_names = set(cls.fields.keys()) model_to_obj_translation = { v: k for (k, v) in cls.fields_need_translation.items()} keys = db_utils.get_unique_keys(model) or [] for model_unique_key in keys: obj_unique_key = [model_to_obj_translation.get(key, key) for key in model_unique_key] if obj_field_names.issuperset(obj_unique_key): cls.unique_keys.append(obj_unique_key) # detach db_obj right after object is loaded from the model cls.create = _detach_db_obj(cls.create) cls.update = _detach_db_obj(cls.update) if (hasattr(cls, 'has_standard_attributes') and cls.has_standard_attributes()): setattr(cls, 'standard_attr_id', property(lambda x: x.db_obj.standard_attr_id if x.db_obj else None)) standardattributes.add_standard_attributes(cls) standardattributes.add_tag_filter_names(cls) # Instantiate extra filters per class cls.extra_filter_names = set(cls.extra_filter_names) # add tenant_id filter for objects that have project_id if 'project_id' in cls.fields and 'tenant_id' not in cls.fields: cls.extra_filter_names.add('tenant_id') invalid_fields = [f for f in cls.synthetic_fields if f not in cls.fields] if invalid_fields: raise o_exc.NeutronObjectValidatorException(fields=invalid_fields) @six.add_metaclass(DeclarativeObject) class NeutronDbObject(NeutronObject): # should be overridden for all persistent objects db_model = None # should be overridden for all rbac aware objects rbac_db_cls = None # whether to use new engine facade for the object new_facade = False primary_keys = ['id'] # 'unique_keys' is a list of unique keys that can be used with get_object # instead of 'primary_keys' (e.g. [['key1'], ['key2a', 'key2b']]). # By default 'unique_keys' will be inherited from the 'db_model' unique_keys = [] # this is a dict to store the association between the foreign key and the # corresponding key in the main table for a synthetic field of a specific # class, e.g. port extension has 'port_id' as foreign key, that is # associated with the key 'id' of the table Port for the synthetic # field of class Port. So foreign_keys = {'Port': {'port_id': 'id'}}. # The assumption is the association is the same for all object fields. # E.g. all the port extension will use 'port_id' as key. foreign_keys = {} fields_no_update = [] # dict with name mapping: {'field_name_in_object': 'field_name_in_db'} # It can be used also as DB relationship mapping to synthetic fields name. # It is needed to load synthetic fields with one SQL query using side # loaded entities. # Examples: {'synthetic_field_name': 'relationship_name_in_model'} # {'field_name_in_object': 'field_name_in_db'} fields_need_translation = {} # obj_extra_fields defines properties that are not part of the model # but we want to expose them for easier usage of the object. # Handling of obj_extra_fields is in oslo.versionedobjects. # The extra fields can be accessed as read only property and are exposed # in to_dict() # obj_extra_fields = [] def __init__(self, *args, **kwargs): super(NeutronDbObject, self).__init__(*args, **kwargs) self._captured_db_model = None @property def db_obj(self): '''Return a database model that persists object data.''' return self._captured_db_model def _set_lazy_contexts(self, fields, context): for field in self.lazy_fields.intersection(fields): if isinstance(fields[field], LazyQueryIterator): fields[field].context = context def from_db_object(self, db_obj): fields = self.modify_fields_from_db(db_obj) if self.lazy_fields: self._set_lazy_contexts(fields, self.obj_context) for field in self.fields: if field in fields and not self.is_synthetic(field): setattr(self, field, fields[field]) if self._load_synthetic_fields: self.load_synthetic_db_fields(db_obj) self._captured_db_model = db_obj self.obj_reset_changes() @classmethod def has_standard_attributes(cls): return bool(cls.db_model and issubclass(cls.db_model, standard_attr.HasStandardAttributes)) @classmethod def modify_fields_to_db(cls, fields): """ This method enables to modify the fields and its content before data is inserted into DB. It uses the fields_need_translation dict with structure: { 'field_name_in_object': 'field_name_in_db' } :param fields: dict of fields from NeutronDbObject :return: modified dict of fields """ for k, v in fields.items(): if isinstance(v, LazyQueryIterator): fields[k] = list(v) result = copy.deepcopy(dict(fields)) for field, field_db in cls.fields_need_translation.items(): if field in result: result[field_db] = result.pop(field) return result @classmethod def _get_lazy_iterator(cls, field, appender_query): if field not in cls.lazy_fields: raise KeyError(_('Field %s is not a lazy query field') % field) n_obj_classes = NeutronObjectRegistry.obj_classes() n_obj = n_obj_classes.get(cls.fields[field].objname) return LazyQueryIterator(n_obj[0], appender_query) @classmethod def modify_fields_from_db(cls, db_obj): """Modify the fields after data were fetched from DB. It uses the fields_need_translation dict with structure: { 'field_name_in_object': 'field_name_in_db' } :param db_obj: model fetched from database :return: modified dict of DB values """ # db models can have declarative proxies that are not exposed into # db.keys() so we must fetch data based on object fields definition potential_fields = (list(cls.fields.keys()) + list(cls.fields_need_translation.values())) result = {field: db_obj[field] for field in potential_fields if db_obj.get(field) is not None} for field, field_db in cls.fields_need_translation.items(): if field_db in result: result[field] = result.pop(field_db) for k, v in result.items(): # don't allow sqlalchemy lists to propagate outside if isinstance(v, orm.collections.InstrumentedList): result[k] = list(v) if isinstance(v, orm.dynamic.AppenderQuery): result[k] = cls._get_lazy_iterator(k, v) return result @classmethod def _load_object(cls, context, db_obj, fields=None): obj = cls(context) if fields is not None and len(fields) != 0: if len(set(fields).intersection(set(cls.synthetic_fields))) == 0: obj._load_synthetic_fields = False obj.from_db_object(db_obj) # detach the model so that consequent fetches don't reuse it if context is not None: context.session.expunge(obj.db_obj) return obj def obj_load_attr(self, attrname): """Set None for nullable fields that has unknown value. In case model attribute is not present in database, value stored under ``attrname'' field will be unknown. In such cases if the field ``attrname'' is a nullable Field return None """ try: is_attr_nullable = self.fields[attrname].nullable except KeyError: return super(NeutronDbObject, self).obj_load_attr(attrname) if is_attr_nullable: self[attrname] = None @classmethod def db_context_writer(cls, context): """Return read-write session activation decorator.""" if cls.new_facade: return db_api.context_manager.writer.using(context) return db_api.autonested_transaction(context.session) @classmethod def db_context_reader(cls, context): """Return read-only session activation decorator.""" if cls.new_facade: return db_api.context_manager.reader.using(context) return db_api.autonested_transaction(context.session) @classmethod def get_object(cls, context, fields=None, **kwargs): """Fetch a single object Return the first result of given context or None if the result doesn't contain any row. Next, convert it to a versioned object. :param context: :param fields: indicate which fields the caller is interested in using. Note that currently this is limited to avoid loading synthetic fields when possible, and does not affect db queries. Default is None, which is the same as []. Example: ['id', 'name'] :param kwargs: multiple keys defined by key=value pairs :return: single object of NeutronDbObject class or None """ lookup_keys = set(kwargs.keys()) all_keys = itertools.chain([cls.primary_keys], cls.unique_keys) if not any(lookup_keys.issuperset(keys) for keys in all_keys): missing_keys = set(cls.primary_keys).difference(lookup_keys) raise o_exc.NeutronPrimaryKeyMissing(object_class=cls, missing_keys=missing_keys) with cls.db_context_reader(context): db_obj = obj_db_api.get_object( cls, context, **cls.modify_fields_to_db(kwargs)) if db_obj: return cls._load_object(context, db_obj, fields=fields) @classmethod def get_objects(cls, context, _pager=None, validate_filters=True, fields=None, **kwargs): """Fetch a list of objects Fetch all results from DB and convert them to versioned objects. :param context: :param _pager: a Pager object representing advanced sorting/pagination criteria :param validate_filters: Raises an error in case of passing an unknown filter :param fields: indicate which fields the caller is interested in using. Note that currently this is limited to avoid loading synthetic fields when possible, and does not affect db queries. Default is None, which is the same as []. Example: ['id', 'name'] :param kwargs: multiple keys defined by key=value pairs :return: list of objects of NeutronDbObject class or empty list """ if validate_filters: cls.validate_filters(**kwargs) with cls.db_context_reader(context): db_objs = obj_db_api.get_objects( cls, context, _pager=_pager, **cls.modify_fields_to_db(kwargs)) return [cls._load_object(context, db_obj, fields=fields) for db_obj in db_objs] @classmethod def update_object(cls, context, values, validate_filters=True, **kwargs): """ Update an object that match filtering criteria from DB. :param context: :param values: multiple keys to update in matching objects :param validate_filters: Raises an error in case of passing an unknown filter :param kwargs: multiple keys defined by key=value pairs :return: The updated version of the object """ if validate_filters: cls.validate_filters(**kwargs) # if we have standard attributes, we will need to fetch records to # update revision numbers db_obj = None if cls.has_standard_attributes(): return super(NeutronDbObject, cls).update_object( context, values, validate_filters=False, **kwargs) else: with cls.db_context_writer(context): db_obj = obj_db_api.update_object( cls, context, cls.modify_fields_to_db(values), **cls.modify_fields_to_db(kwargs)) return cls._load_object(context, db_obj) @classmethod def update_objects(cls, context, values, validate_filters=True, **kwargs): """ Update objects that match filtering criteria from DB. :param context: :param values: multiple keys to update in matching objects :param validate_filters: Raises an error in case of passing an unknown filter :param kwargs: multiple keys defined by key=value pairs :return: Number of entries updated """ if validate_filters: cls.validate_filters(**kwargs) with cls.db_context_writer(context): # if we have standard attributes, we will need to fetch records to # update revision numbers if cls.has_standard_attributes(): return super(NeutronDbObject, cls).update_objects( context, values, validate_filters=False, **kwargs) return obj_db_api.update_objects( cls, context, cls.modify_fields_to_db(values), **cls.modify_fields_to_db(kwargs)) @classmethod def delete_objects(cls, context, validate_filters=True, **kwargs): """ Delete objects that match filtering criteria from DB. :param context: :param validate_filters: Raises an error in case of passing an unknown filter :param kwargs: multiple keys defined by key=value pairs :return: Number of entries deleted """ if validate_filters: cls.validate_filters(**kwargs) with cls.db_context_writer(context): return obj_db_api.delete_objects( cls, context, **cls.modify_fields_to_db(kwargs)) @classmethod def is_accessible(cls, context, db_obj): return (context.is_admin or context.tenant_id == db_obj.tenant_id) @staticmethod def filter_to_str(value): if isinstance(value, list): return [str(val) for val in value] return str(value) @staticmethod def filter_to_json_str(value, default=None): def _dict_to_json(v): return ( jsonutils.dumps( collections.OrderedDict( sorted(v.items(), key=lambda t: t[0]) ) ) if v else default ) if isinstance(value, list): return [_dict_to_json(val) for val in value] v = _dict_to_json(value) return v @staticmethod def load_json_from_str(field, default=None): value = field or default if value: value = jsonutils.loads(value) return value def _get_changed_persistent_fields(self): fields = self.obj_get_changes() for field in self.synthetic_fields: if field in fields: del fields[field] return fields def _get_changed_synthetic_fields(self): fields = self.obj_get_changes() for field in self._get_changed_persistent_fields(): if field in fields: del fields[field] return fields def _validate_changed_fields(self, fields): fields = fields.copy() forbidden_updates = set(self.fields_no_update) & set(fields.keys()) if forbidden_updates: raise o_exc.NeutronObjectUpdateForbidden(fields=forbidden_updates) return fields def load_synthetic_db_fields(self, db_obj=None): """ Load the synthetic fields that are stored in a different table from the main object. This method doesn't take care of loading synthetic fields that aren't stored in the DB, e.g. 'shared' in RBAC policy. """ clsname = self.__class__.__name__ # TODO(rossella_s) Find a way to handle ObjectFields with # subclasses=True for field in self.synthetic_fields: try: field_def = self.fields[field] objclasses = NeutronObjectRegistry.obj_classes( ).get(field_def.objname) except AttributeError: # NOTE(rossella_s) this is probably because this field is not # an ObjectField continue if not objclasses: # NOTE(rossella_s) some synthetic fields are not handled by # this method, for example the ones that have subclasses, see # QosRule continue objclass = objclasses[0] foreign_keys = objclass.foreign_keys.get(clsname) if not foreign_keys: raise o_exc.NeutronSyntheticFieldsForeignKeysNotFound( parent=clsname, child=objclass.__name__) if len(foreign_keys.keys()) > 1: raise o_exc.NeutronSyntheticFieldMultipleForeignKeys( field=field) synthetic_field_db_name = ( self.fields_need_translation.get(field, field)) synth_db_objs = (db_obj.get(synthetic_field_db_name, None) if db_obj else None) # synth_db_objs can be list, empty list or None, that is why # we need 'is not None', because [] is valid case for 'True' if synth_db_objs is not None: if isinstance(synth_db_objs, orm.dynamic.AppenderQuery): synth_db_objs = list(synth_db_objs) elif not isinstance(synth_db_objs, list): synth_db_objs = [synth_db_objs] synth_objs = [objclass._load_object(self.obj_context, obj) for obj in synth_db_objs] else: synth_objs = objclass.get_objects( self.obj_context, **{ k: getattr(self, v) if v in self else db_obj.get(v) for k, v in foreign_keys.items()}) if isinstance(self.fields[field], obj_fields.ObjectField): setattr(self, field, synth_objs[0] if synth_objs else None) else: setattr(self, field, synth_objs) self.obj_reset_changes([field]) def create(self): fields = self._get_changed_persistent_fields() with self.db_context_writer(self.obj_context): try: db_obj = obj_db_api.create_object( self, self.obj_context, self.modify_fields_to_db(fields)) except obj_exc.DBDuplicateEntry as db_exc: raise o_exc.NeutronDbObjectDuplicateEntry( object_class=self.__class__, db_exception=db_exc) self.from_db_object(db_obj) def _get_composite_keys(self): keys = {} for key in self.primary_keys: keys[key] = getattr(self, key) return keys def update_fields(self, obj_data, reset_changes=False): """Updates fields of an object that are not forbidden to be updated. :param obj_data: the full set of object data :type obj_data: dict :param reset_changes: indicates whether the object's current set of changed fields should be cleared :type reset_changes: boolean :returns: None """ if reset_changes: self.obj_reset_changes() for k, v in obj_data.items(): if k not in self.fields_no_update: setattr(self, k, v) def update(self): updates = self._get_changed_persistent_fields() updates = self._validate_changed_fields(updates) with self.db_context_writer(self.obj_context): db_obj = obj_db_api.update_object( self, self.obj_context, self.modify_fields_to_db(updates), **self.modify_fields_to_db( self._get_composite_keys())) self.from_db_object(db_obj) def delete(self): obj_db_api.delete_object(self, self.obj_context, **self.modify_fields_to_db( self._get_composite_keys())) self._captured_db_model = None @classmethod def count(cls, context, validate_filters=True, **kwargs): """ Count the number of objects matching filtering criteria. :param context: :param validate_filters: Raises an error in case of passing an unknown filter :param kwargs: multiple keys defined by key=value pairs :return: number of matching objects """ if validate_filters: cls.validate_filters(**kwargs) return obj_db_api.count( cls, context, **cls.modify_fields_to_db(kwargs) ) @classmethod def objects_exist(cls, context, validate_filters=True, **kwargs): """ Check if objects are present in DB. :param context: :param validate_filters: Raises an error in case of passing an unknown filter :param kwargs: multiple keys defined by key=value pairs :return: boolean. True if object is present. """ if validate_filters: cls.validate_filters(**kwargs) # Succeed if at least a single object matches; no need to fetch more return bool(obj_db_api.count( cls, context, **cls.modify_fields_to_db(kwargs)) ) neutron-12.1.1/neutron/objects/extensions/0000775000175000017500000000000013553660156020644 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/extensions/standardattributes.py0000664000175000017500000000313113553660047025122 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields STANDARD_ATTRIBUTES = { 'revision_number': obj_fields.IntegerField(), 'description': obj_fields.StringField(nullable=True), 'created_at': obj_fields.DateTimeField(nullable=True, tzinfo_aware=False), 'updated_at': obj_fields.DateTimeField(nullable=True, tzinfo_aware=False), } def add_standard_attributes(cls): # Don't use parent's fields in case child class doesn't create # its own instance of list cls.fields = cls.fields.copy() cls.fields.update(STANDARD_ATTRIBUTES) # those fields are updated by sqlalchemy itself cls.fields_no_update += ('created_at', 'updated_at') # revision numbers are managed by service plugin and are bumped # automatically; consumers should not bump them explicitly cls.fields_no_update.append('revision_number') def add_tag_filter_names(cls): cls.add_extra_filter_name("tags") cls.add_extra_filter_name("not-tags") cls.add_extra_filter_name("tags-any") cls.add_extra_filter_name("not-tags-any") neutron-12.1.1/neutron/objects/extensions/port_security.py0000664000175000017500000000205613553660046024132 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import port_security from oslo_versionedobjects import fields as obj_fields from neutron.objects import base from neutron.objects import common_types class _PortSecurity(base.NeutronDbObject): fields = { 'id': common_types.UUIDField(), 'port_security_enabled': obj_fields.BooleanField( default=port_security.DEFAULT_PORT_SECURITY), } foreign_keys = { 'Port': {'id': 'id'}, 'Network': {'id': 'id'}, } neutron-12.1.1/neutron/objects/extensions/__init__.py0000664000175000017500000000000013553660046022741 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/qos/0000775000175000017500000000000013553660156017247 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/qos/qos_policy_validator.py0000664000175000017500000000545613553660047024060 0ustar zuulzuul00000000000000# Copyright (c) 2017-18 NEC Technologies India Pvt Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.services.qos import constants as qos_consts from neutron.common import exceptions as n_exc def check_bandwidth_rule_conflict(policy, rule_data): """Implementation of the QoS Rule checker. This function checks if the new rule to be associated with the policy doesn't conflict with the existing rules. Raises an exception if conflict is identified. """ for rule in policy.rules: if rule.rule_type == qos_consts.RULE_TYPE_DSCP_MARKING: # Skip checks if Rule is DSCP continue elif rule.rule_type == qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: if "max_kbps" in rule_data and ( int(rule.min_kbps) > int(rule_data["max_kbps"])): raise n_exc.QoSRuleParameterConflict( rule_value=rule_data["max_kbps"], policy_id=policy["id"], existing_rule=rule.rule_type, existing_value=rule.min_kbps) elif rule.rule_type == qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: if "min_kbps" in rule_data and ( int(rule.max_kbps) < int(rule_data["min_kbps"])): raise n_exc.QoSRuleParameterConflict( rule_value=rule_data["min_kbps"], policy_id=policy["id"], existing_rule=rule.rule_type, existing_value=rule.max_kbps) def check_rules_conflict(policy, rule_obj): """Implementation of the QoS Policy rules conflicts. This function checks if the new rule to be associated with policy doesn't have any duplicate rule already in policy. Raises an exception if conflict is identified. """ for rule in policy.rules: # NOTE(slaweq): we don't want to raise exception when compared rules # have got same id as it means that it is probably exactly the same # rule so there is no conflict if rule.id == getattr(rule_obj, "id", None): continue if rule.duplicates(rule_obj): raise n_exc.QoSRulesConflict( new_rule_type=rule_obj.rule_type, rule_id=rule.id, policy_id=policy.id) neutron-12.1.1/neutron/objects/qos/rule_type.py0000664000175000017500000000626013553660047021634 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins import constants from neutron_lib.plugins import directory from neutron_lib.services.qos import constants as qos_consts from oslo_utils import versionutils from oslo_versionedobjects import fields as obj_fields from neutron.objects import base from neutron.objects import common_types class RuleTypeField(obj_fields.BaseEnumField): def __init__(self, **kwargs): self.AUTO_TYPE = obj_fields.Enum( valid_values=qos_consts.VALID_RULE_TYPES) super(RuleTypeField, self).__init__(**kwargs) @base.NeutronObjectRegistry.register class QosRuleType(base.NeutronObject): # Version 1.0: Initial version # Version 1.1: Added QosDscpMarkingRule # Version 1.2: Added QosMinimumBandwidthRule # Version 1.3: Added drivers field VERSION = '1.3' fields = { 'type': RuleTypeField(), 'drivers': obj_fields.ListOfObjectsField( 'QosRuleTypeDriver', nullable=True) } synthetic_fields = ['drivers'] # we don't receive context because we don't need db access at all @classmethod def get_object(cls, rule_type_name, **kwargs): plugin = directory.get_plugin(alias=constants.QOS) drivers = plugin.supported_rule_type_details(rule_type_name) drivers_obj = [QosRuleTypeDriver( name=driver['name'], supported_parameters=driver['supported_parameters']) for driver in drivers] return cls(type=rule_type_name, drivers=drivers_obj) # we don't receive context because we don't need db access at all @classmethod def get_objects(cls, validate_filters=True, **kwargs): if validate_filters: cls.validate_filters(**kwargs) rule_types = ( directory.get_plugin(alias=constants.QOS).supported_rule_types) # TODO(ihrachys): apply filters to returned result return [cls(type=type_) for type_ in rule_types] def obj_make_compatible(self, primitive, target_version): _target_version = versionutils.convert_version_to_tuple(target_version) if _target_version < (1, 3): primitive.pop('drivers', None) @base.NeutronObjectRegistry.register class QosRuleTypeDriver(base.NeutronObject): # Version 1.0: Initial version VERSION = '1.0' fields = { 'name': obj_fields.StringField(), 'supported_parameters': common_types.ListOfDictOfMiscValuesField() } def to_dict(self): return { 'name': self.name, 'supported_parameters': self.supported_parameters} @classmethod def get_objects(cls, context, **kwargs): raise NotImplementedError() neutron-12.1.1/neutron/objects/qos/rule.py0000664000175000017500000001540013553660047020567 0ustar zuulzuul00000000000000# Copyright 2015 Huawei Technologies India Pvt Ltd, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import sys from neutron_lib import constants from neutron_lib.services.qos import constants as qos_consts from neutron_lib.utils import helpers from oslo_utils import versionutils from oslo_versionedobjects import exception from oslo_versionedobjects import fields as obj_fields import six from neutron.db.qos import models as qos_db_model from neutron.objects import base from neutron.objects import common_types DSCP_MARK = 'dscp_mark' def get_rules(obj_cls, context, qos_policy_id): all_rules = [] with obj_cls.db_context_reader(context): for rule_type in qos_consts.VALID_RULE_TYPES: rule_cls_name = 'Qos%sRule' % helpers.camelize(rule_type) rule_cls = getattr(sys.modules[__name__], rule_cls_name) rules = rule_cls.get_objects(context, qos_policy_id=qos_policy_id) all_rules.extend(rules) return all_rules @six.add_metaclass(abc.ABCMeta) class QosRule(base.NeutronDbObject): # Version 1.0: Initial version, only BandwidthLimitRule # 1.1: Added DscpMarkingRule # 1.2: Added QosMinimumBandwidthRule # 1.3: Added direction for BandwidthLimitRule # #NOTE(mangelajo): versions need to be handled from the top QosRule object # because it's the only reference QosPolicy can make # to them via obj_relationships version map VERSION = '1.3' fields = { 'id': common_types.UUIDField(), 'qos_policy_id': common_types.UUIDField() } fields_no_update = ['id', 'qos_policy_id'] # should be redefined in subclasses rule_type = None duplicates_compare_fields = () def duplicates(self, other_rule): """Returns True if rules have got same values in fields defined in 'duplicates_compare_fields' list. In case when subclass don't have defined any field in duplicates_compare_fields, only rule types are compared. """ if self.rule_type != other_rule.rule_type: return False if self.duplicates_compare_fields: for field in self.duplicates_compare_fields: if getattr(self, field) != getattr(other_rule, field): return False return True def to_dict(self): dict_ = super(QosRule, self).to_dict() dict_['type'] = self.rule_type return dict_ def should_apply_to_port(self, port): """Check whether a rule can be applied to a specific port. This function has the logic to decide whether a rule should be applied to a port or not, depending on the source of the policy (network, or port). Eventually rules could override this method, or we could make it abstract to allow different rule behaviour. """ is_port_policy = self.qos_policy_id == port[qos_consts.QOS_POLICY_ID] is_network_policy_only = port[qos_consts.QOS_POLICY_ID] is None is_network_device_port = any(port['device_owner'].startswith(prefix) for prefix in constants.DEVICE_OWNER_PREFIXES) # NOTE(miouge): Network QoS policies should apply to ext routers ports: # - DEVICE_OWNER_AGENT_GW for DVR routers # - DEVICE_OWNER_ROUTER_GW for normal neutron routers is_router_gw = any(port['device_owner'].startswith(prefix) for prefix in [constants.DEVICE_OWNER_AGENT_GW, constants.DEVICE_OWNER_ROUTER_GW]) # NOTE(ralonsoh): return True if: # - Is a port QoS policy (not a network QoS policy) # - Is not an internal network device (e.g. router) and is a network # QoS policy and there is no port QoS policy return (is_port_policy or ((is_router_gw or not is_network_device_port) and is_network_policy_only)) @base.NeutronObjectRegistry.register class QosBandwidthLimitRule(QosRule): db_model = qos_db_model.QosBandwidthLimitRule fields = { 'max_kbps': obj_fields.IntegerField(nullable=True), 'max_burst_kbps': obj_fields.IntegerField(nullable=True), 'direction': common_types.FlowDirectionEnumField( default=constants.EGRESS_DIRECTION) } duplicates_compare_fields = ['direction'] rule_type = qos_consts.RULE_TYPE_BANDWIDTH_LIMIT def obj_make_compatible(self, primitive, target_version): _target_version = versionutils.convert_version_to_tuple(target_version) if _target_version < (1, 3) and 'direction' in primitive: direction = primitive.pop('direction') if direction == constants.INGRESS_DIRECTION: raise exception.IncompatibleObjectVersion( objver=target_version, objtype="QosBandwidthLimitRule") @base.NeutronObjectRegistry.register class QosDscpMarkingRule(QosRule): db_model = qos_db_model.QosDscpMarkingRule fields = { DSCP_MARK: common_types.DscpMarkField(), } rule_type = qos_consts.RULE_TYPE_DSCP_MARKING def obj_make_compatible(self, primitive, target_version): _target_version = versionutils.convert_version_to_tuple(target_version) if _target_version < (1, 1): raise exception.IncompatibleObjectVersion( objver=target_version, objname="QosDscpMarkingRule") @base.NeutronObjectRegistry.register class QosMinimumBandwidthRule(QosRule): db_model = qos_db_model.QosMinimumBandwidthRule fields = { 'min_kbps': obj_fields.IntegerField(nullable=True), 'direction': common_types.FlowDirectionEnumField(), } duplicates_compare_fields = ['direction'] rule_type = qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH def obj_make_compatible(self, primitive, target_version): _target_version = versionutils.convert_version_to_tuple(target_version) if _target_version < (1, 2): raise exception.IncompatibleObjectVersion( objver=target_version, objname="QosMinimumBandwidthRule") neutron-12.1.1/neutron/objects/qos/policy.py0000664000175000017500000003771113553660047021130 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools from neutron_lib import constants as n_const from oslo_db import exception as db_exc from oslo_utils import versionutils from oslo_versionedobjects import exception from oslo_versionedobjects import fields as obj_fields from neutron.common import exceptions from neutron.db.models import l3 from neutron.db import models_v2 from neutron.db.qos import models as qos_db_model from neutron.db import rbac_db_models from neutron.objects import base as base_db from neutron.objects import common_types from neutron.objects.db import api as obj_db_api from neutron.objects.qos import binding from neutron.objects.qos import rule as rule_obj_impl from neutron.objects import rbac_db @base_db.NeutronObjectRegistry.register class QosPolicyRBAC(base_db.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = rbac_db_models.QosPolicyRBAC fields = { 'object_id': obj_fields.StringField(), 'target_tenant': obj_fields.StringField(), 'action': obj_fields.StringField(), } @base_db.NeutronObjectRegistry.register class QosPolicy(rbac_db.NeutronRbacObject): # Version 1.0: Initial version # Version 1.1: QosDscpMarkingRule introduced # Version 1.2: Added QosMinimumBandwidthRule # Version 1.3: Added standard attributes (created_at, revision, etc) # Version 1.4: Changed tenant_id to project_id # Version 1.5: Direction for bandwidth limit rule added # Version 1.6: Added "is_default" field # Version 1.7: Added floating IP bindings VERSION = '1.7' # required by RbacNeutronMetaclass rbac_db_cls = QosPolicyRBAC db_model = qos_db_model.QosPolicy fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(), 'name': obj_fields.StringField(), 'shared': obj_fields.BooleanField(default=False), 'rules': obj_fields.ListOfObjectsField('QosRule', subclasses=True), 'is_default': obj_fields.BooleanField(default=False), } fields_no_update = ['id', 'project_id'] synthetic_fields = ['rules', 'is_default'] extra_filter_names = {'is_default'} binding_models = {'port': binding.QosPolicyPortBinding, 'network': binding.QosPolicyNetworkBinding, 'fip': binding.QosPolicyFloatingIPBinding} def obj_load_attr(self, attrname): if attrname == 'rules': return self._reload_rules() elif attrname == 'is_default': return self._reload_is_default() return super(QosPolicy, self).obj_load_attr(attrname) def _reload_rules(self): rules = rule_obj_impl.get_rules(self, self.obj_context, self.id) setattr(self, 'rules', rules) self.obj_reset_changes(['rules']) def _reload_is_default(self): if self.get_default() == self.id: setattr(self, 'is_default', True) else: setattr(self, 'is_default', False) self.obj_reset_changes(['is_default']) def get_rule_by_id(self, rule_id): """Return rule specified by rule_id. @raise QosRuleNotFound: if there is no such rule in the policy. """ for rule in self.rules: if rule_id == rule.id: return rule raise exceptions.QosRuleNotFound(policy_id=self.id, rule_id=rule_id) # TODO(hichihara): For tag mechanism. This will be removed in bug/1704137 def to_dict(self): _dict = super(QosPolicy, self).to_dict() try: _dict['tags'] = [t.tag for t in self.db_obj.standard_attr.tags] except AttributeError: # AttrtibuteError can be raised when accessing self.db_obj # or self.db_obj.standard_attr pass return _dict @classmethod def get_object(cls, context, **kwargs): # We want to get the policy regardless of its tenant id. We'll make # sure the tenant has permission to access the policy later on. admin_context = context.elevated() with cls.db_context_reader(admin_context): policy_obj = super(QosPolicy, cls).get_object(admin_context, **kwargs) if (not policy_obj or not cls.is_accessible(context, policy_obj)): return policy_obj.obj_load_attr('rules') policy_obj.obj_load_attr('is_default') return policy_obj @classmethod def get_objects(cls, context, _pager=None, validate_filters=True, **kwargs): # We want to get the policy regardless of its tenant id. We'll make # sure the tenant has permission to access the policy later on. admin_context = context.elevated() with cls.db_context_reader(admin_context): objs = super(QosPolicy, cls).get_objects(admin_context, _pager, validate_filters, **kwargs) result = [] for obj in objs: if not cls.is_accessible(context, obj): continue obj.obj_load_attr('rules') obj.obj_load_attr('is_default') result.append(obj) return result @classmethod def _get_object_policy(cls, context, binding_cls, **kwargs): with cls.db_context_reader(context): binding_db_obj = obj_db_api.get_object(binding_cls, context, **kwargs) if binding_db_obj: return cls.get_object(context, id=binding_db_obj['policy_id']) @classmethod def get_network_policy(cls, context, network_id): return cls._get_object_policy(context, binding.QosPolicyNetworkBinding, network_id=network_id) @classmethod def get_port_policy(cls, context, port_id): return cls._get_object_policy(context, binding.QosPolicyPortBinding, port_id=port_id) @classmethod def get_fip_policy(cls, context, fip_id): return cls._get_object_policy( context, binding.QosPolicyFloatingIPBinding, fip_id=fip_id) # TODO(QoS): Consider extending base to trigger registered methods for us def create(self): with self.db_context_writer(self.obj_context): super(QosPolicy, self).create() if self.is_default: self.set_default() self.obj_load_attr('rules') def update(self): with self.db_context_writer(self.obj_context): if 'is_default' in self.obj_what_changed(): if self.is_default: self.set_default() else: self.unset_default() super(QosPolicy, self).update() def delete(self): with self.db_context_writer(self.obj_context): for object_type, obj_class in self.binding_models.items(): pager = base_db.Pager(limit=1) binding_obj = obj_class.get_objects(self.obj_context, policy_id=self.id, _pager=pager) if binding_obj: raise exceptions.QosPolicyInUse( policy_id=self.id, object_type=object_type, object_id=binding_obj[0]['%s_id' % object_type]) super(QosPolicy, self).delete() def attach_network(self, network_id): network_binding = {'policy_id': self.id, 'network_id': network_id} network_binding_obj = binding.QosPolicyNetworkBinding( self.obj_context, **network_binding) try: network_binding_obj.create() except db_exc.DBReferenceError as e: raise exceptions.NetworkQosBindingError(policy_id=self.id, net_id=network_id, db_error=e) def attach_port(self, port_id): port_binding_obj = binding.QosPolicyPortBinding( self.obj_context, policy_id=self.id, port_id=port_id) try: port_binding_obj.create() except db_exc.DBReferenceError as e: raise exceptions.PortQosBindingError(policy_id=self.id, port_id=port_id, db_error=e) def attach_floatingip(self, fip_id): fip_binding_obj = binding.QosPolicyFloatingIPBinding( self.obj_context, policy_id=self.id, fip_id=fip_id) try: fip_binding_obj.create() except db_exc.DBReferenceError as e: raise exceptions.FloatingIPQosBindingError(policy_id=self.id, fip_id=fip_id, db_error=e) def detach_network(self, network_id): deleted = binding.QosPolicyNetworkBinding.delete_objects( self.obj_context, network_id=network_id) if not deleted: raise exceptions.NetworkQosBindingNotFound(net_id=network_id, policy_id=self.id) def detach_port(self, port_id): deleted = binding.QosPolicyPortBinding.delete_objects(self.obj_context, port_id=port_id) if not deleted: raise exceptions.PortQosBindingNotFound(port_id=port_id, policy_id=self.id) def detach_floatingip(self, fip_id): deleted = binding.QosPolicyFloatingIPBinding.delete_objects( self.obj_context, fip_id=fip_id) if not deleted: raise exceptions.FloatingIPQosBindingNotFound(fip_id=fip_id, policy_id=self.id) def set_default(self): if not self.get_default(): qos_default_policy = QosPolicyDefault(self.obj_context, qos_policy_id=self.id, project_id=self.project_id) qos_default_policy.create() elif self.get_default() != self.id: raise exceptions.QoSPolicyDefaultAlreadyExists( project_id=self.project_id) def unset_default(self): if self.get_default() == self.id: qos_default_policy = QosPolicyDefault.get_object( self.obj_context, project_id=self.project_id) qos_default_policy.delete() def get_default(self): qos_default_policy = QosPolicyDefault.get_object( self.obj_context, project_id=self.project_id) if qos_default_policy: return qos_default_policy.qos_policy_id def get_bound_networks(self): return [ nb.network_id for nb in binding.QosPolicyNetworkBinding.get_objects( self.obj_context, policy_id=self.id) ] def get_bound_ports(self): return [ pb.port_id for pb in binding.QosPolicyPortBinding.get_objects( self.obj_context, policy_id=self.id) ] def get_bound_floatingips(self): return [ fb.fip_id for fb in binding.QosPolicyFloatingIPBinding.get_objects( self.obj_context, policy_id=self.id) ] @classmethod def _get_bound_tenant_ids(cls, session, binding_db, bound_db, binding_db_id_column, policy_id): return list(itertools.chain.from_iterable( session.query(bound_db.tenant_id).join( binding_db, bound_db.id == binding_db_id_column).filter( binding_db.policy_id == policy_id).all())) @classmethod def get_bound_tenant_ids(cls, context, policy_id): """Implements RbacNeutronObject.get_bound_tenant_ids. :returns: set -- a set of tenants' ids dependent on QosPolicy. """ net = models_v2.Network qosnet = qos_db_model.QosNetworkPolicyBinding port = models_v2.Port qosport = qos_db_model.QosPortPolicyBinding fip = l3.FloatingIP qosfip = qos_db_model.QosFIPPolicyBinding bound_tenants = [] with cls.db_context_reader(context): bound_tenants.extend(cls._get_bound_tenant_ids( context.session, qosnet, net, qosnet.network_id, policy_id)) bound_tenants.extend( cls._get_bound_tenant_ids(context.session, qosport, port, qosport.port_id, policy_id)) bound_tenants.extend( cls._get_bound_tenant_ids(context.session, qosfip, fip, qosfip.fip_id, policy_id)) return set(bound_tenants) def obj_make_compatible(self, primitive, target_version): def filter_rules(obj_names, rules): return [rule for rule in rules if rule['versioned_object.name'] in obj_names] def filter_ingress_bandwidth_limit_rules(rules): bwlimit_obj_name = rule_obj_impl.QosBandwidthLimitRule.obj_name() filtered_rules = [] for rule in rules: if rule['versioned_object.name'] == bwlimit_obj_name: direction = rule['versioned_object.data'].get("direction") if direction == n_const.EGRESS_DIRECTION: rule['versioned_object.data'].pop('direction') filtered_rules.append(rule) else: filtered_rules.append(rule) return filtered_rules _target_version = versionutils.convert_version_to_tuple(target_version) names = [] if _target_version >= (1, 0): names.append(rule_obj_impl.QosBandwidthLimitRule.obj_name()) if _target_version >= (1, 1): names.append(rule_obj_impl.QosDscpMarkingRule.obj_name()) if _target_version >= (1, 2): names.append(rule_obj_impl.QosMinimumBandwidthRule.obj_name()) if 'rules' in primitive and names: primitive['rules'] = filter_rules(names, primitive['rules']) if _target_version < (1, 3): standard_fields = ['revision_number', 'created_at', 'updated_at'] for f in standard_fields: primitive.pop(f) if primitive['description'] is None: # description was not nullable before raise exception.IncompatibleObjectVersion( objver=target_version, objname='QoSPolicy') if _target_version < (1, 4): primitive['tenant_id'] = primitive.pop('project_id') if _target_version < (1, 5): if 'rules' in primitive: primitive['rules'] = filter_ingress_bandwidth_limit_rules( primitive['rules']) if _target_version < (1, 6): primitive.pop('is_default', None) @base_db.NeutronObjectRegistry.register class QosPolicyDefault(base_db.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = qos_db_model.QosPolicyDefault fields = { 'qos_policy_id': common_types.UUIDField(), 'project_id': obj_fields.StringField(), } primary_keys = ['project_id'] neutron-12.1.1/neutron/objects/qos/__init__.py0000664000175000017500000000000013553660046021344 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/qos/binding.py0000664000175000017500000000363613553660047021242 0ustar zuulzuul00000000000000# Copyright 2017 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.db.qos import models as qos_db_model from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class QosPolicyPortBinding(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = qos_db_model.QosPortPolicyBinding fields = { 'policy_id': common_types.UUIDField(), 'port_id': common_types.UUIDField() } primary_keys = ['port_id'] fields_no_update = ['policy_id', 'port_id'] @base.NeutronObjectRegistry.register class QosPolicyNetworkBinding(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = qos_db_model.QosNetworkPolicyBinding fields = { 'policy_id': common_types.UUIDField(), 'network_id': common_types.UUIDField() } primary_keys = ['network_id'] fields_no_update = ['policy_id', 'network_id'] @base.NeutronObjectRegistry.register class QosPolicyFloatingIPBinding(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = qos_db_model.QosFIPPolicyBinding fields = { 'policy_id': common_types.UUIDField(), 'fip_id': common_types.UUIDField() } primary_keys = ['policy_id', 'fip_id'] fields_no_update = ['policy_id', 'fip_id'] neutron-12.1.1/neutron/objects/servicetype.py0000664000175000017500000000225513553660046021363 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.models import servicetype as models from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class ProviderResourceAssociation(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.ProviderResourceAssociation primary_keys = ['provider_name', 'resource_id'] fields = { 'provider_name': obj_fields.StringField(), 'resource_id': common_types.UUIDField(), } neutron-12.1.1/neutron/objects/l3agent.py0000664000175000017500000000512513553660047020356 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields import sqlalchemy as sa from sqlalchemy.orm import joinedload from sqlalchemy import sql from neutron.db.models import agent as agent_model from neutron.db.models import l3_attrs from neutron.db.models import l3agent from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class RouterL3AgentBinding(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = l3agent.RouterL3AgentBinding primary_keys = ['router_id', 'l3_agent_id'] fields = { 'router_id': common_types.UUIDField(), 'l3_agent_id': common_types.UUIDField(), 'binding_index': obj_fields.IntegerField( default=l3agent.LOWEST_BINDING_INDEX), } # TODO(ihrachys) return OVO objects not models # TODO(ihrachys) move under Agent object class @classmethod def get_l3_agents_by_router_ids(cls, context, router_ids): query = context.session.query(l3agent.RouterL3AgentBinding) query = query.options(joinedload('l3_agent')).filter( l3agent.RouterL3AgentBinding.router_id.in_(router_ids)) return [db_obj.l3_agent for db_obj in query.all()] @classmethod def get_down_router_bindings(cls, context, cutoff): query = (context.session.query( l3agent.RouterL3AgentBinding). join(agent_model.Agent). filter(agent_model.Agent.heartbeat_timestamp < cutoff, agent_model.Agent.admin_state_up).outerjoin( l3_attrs.RouterExtraAttributes, l3_attrs.RouterExtraAttributes.router_id == l3agent.RouterL3AgentBinding.router_id).filter( sa.or_( l3_attrs.RouterExtraAttributes.ha == sql.false(), l3_attrs.RouterExtraAttributes.ha == sql.null()))) bindings = [cls._load_object(context, db_obj) for db_obj in query.all()] return bindings neutron-12.1.1/neutron/objects/metering.py0000664000175000017500000000521013553660047020626 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.common import utils from neutron.db.models import metering as metering_models from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class MeteringLabelRule(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = metering_models.MeteringLabelRule foreign_keys = {'MeteringLabel': {'metering_label_id': 'id'}} fields = { 'id': common_types.UUIDField(), 'direction': common_types.FlowDirectionEnumField(nullable=True), 'remote_ip_prefix': common_types.IPNetworkField(nullable=True), 'metering_label_id': common_types.UUIDField(), 'excluded': obj_fields.BooleanField(default=False), } fields_no_update = ['metering_label_id'] @classmethod def modify_fields_from_db(cls, db_obj): result = super(MeteringLabelRule, cls).modify_fields_from_db(db_obj) if 'remote_ip_prefix' in result: result['remote_ip_prefix'] = utils.AuthenticIPNetwork( result['remote_ip_prefix']) return result @classmethod def modify_fields_to_db(cls, fields): result = super(MeteringLabelRule, cls).modify_fields_to_db(fields) if 'remote_ip_prefix' in result: result['remote_ip_prefix'] = cls.filter_to_str( result['remote_ip_prefix']) return result @base.NeutronObjectRegistry.register class MeteringLabel(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = metering_models.MeteringLabel synthetic_fields = ['rules'] fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(), 'description': obj_fields.StringField(), 'rules': obj_fields.ListOfObjectsField('MeteringLabelRule', nullable=True), 'shared': obj_fields.BooleanField(default=False), } neutron-12.1.1/neutron/objects/common_types.py0000664000175000017500000002554713553660047021547 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import itertools import uuid import netaddr from neutron_lib import constants as lib_constants from neutron_lib.db import constants as lib_db_const from neutron_lib.objects import exceptions as o_exc from oslo_serialization import jsonutils from oslo_versionedobjects import fields as obj_fields import six from neutron._i18n import _ from neutron.common import constants from neutron.common import utils class HARouterEnumField(obj_fields.AutoTypedField): AUTO_TYPE = obj_fields.Enum(valid_values=constants.VALID_HA_STATES) class IPV6ModeEnumField(obj_fields.AutoTypedField): AUTO_TYPE = obj_fields.Enum(valid_values=lib_constants.IPV6_MODES) class RangeConstrainedInteger(obj_fields.Integer): def __init__(self, start, end, **kwargs): try: self._start = int(start) self._end = int(end) except (TypeError, ValueError): raise o_exc.NeutronRangeConstrainedIntegerInvalidLimit( start=start, end=end) super(RangeConstrainedInteger, self).__init__(**kwargs) def coerce(self, obj, attr, value): if not isinstance(value, six.integer_types): msg = _("Field value %s is not an integer") % value raise ValueError(msg) if not self._start <= value <= self._end: msg = _("Field value %s is invalid") % value raise ValueError(msg) return super(RangeConstrainedInteger, self).coerce(obj, attr, value) class IPNetworkPrefixLen(RangeConstrainedInteger): """IP network (CIDR) prefix length custom Enum""" def __init__(self, **kwargs): super(IPNetworkPrefixLen, self).__init__( start=0, end=lib_constants.IPv6_BITS, **kwargs) class IPNetworkPrefixLenField(obj_fields.AutoTypedField): AUTO_TYPE = IPNetworkPrefixLen() class PortRange(RangeConstrainedInteger): def __init__(self, start=constants.PORT_RANGE_MIN, **kwargs): super(PortRange, self).__init__(start=start, end=constants.PORT_RANGE_MAX, **kwargs) class PortRangeField(obj_fields.AutoTypedField): AUTO_TYPE = PortRange() class PortRangeWith0Field(obj_fields.AutoTypedField): AUTO_TYPE = PortRange(start=0) class VlanIdRange(RangeConstrainedInteger): def __init__(self, **kwargs): super(VlanIdRange, self).__init__(start=lib_constants.MIN_VLAN_TAG, end=lib_constants.MAX_VLAN_TAG, **kwargs) class VlanIdRangeField(obj_fields.AutoTypedField): AUTO_TYPE = VlanIdRange() class ListOfIPNetworksField(obj_fields.AutoTypedField): AUTO_TYPE = obj_fields.List(obj_fields.IPNetwork()) class SetOfUUIDsField(obj_fields.AutoTypedField): AUTO_TYPE = obj_fields.Set(obj_fields.UUID()) class DomainName(obj_fields.String): def coerce(self, obj, attr, value): if not isinstance(value, six.string_types): msg = _("Field value %s is not a string") % value raise ValueError(msg) if len(value) > lib_db_const.FQDN_FIELD_SIZE: msg = _("Domain name %s is too long") % value raise ValueError(msg) return super(DomainName, self).coerce(obj, attr, value) class DomainNameField(obj_fields.AutoTypedField): AUTO_TYPE = DomainName() class IntegerEnum(obj_fields.Integer): def __init__(self, valid_values=None, **kwargs): if not valid_values: msg = _("No possible values specified") raise ValueError(msg) for value in valid_values: if not isinstance(value, six.integer_types): msg = _("Possible value %s is not an integer") % value raise ValueError(msg) self._valid_values = valid_values super(IntegerEnum, self).__init__(**kwargs) def coerce(self, obj, attr, value): if not isinstance(value, six.integer_types): msg = _("Field value %s is not an integer") % value raise ValueError(msg) if value not in self._valid_values: msg = ( _("Field value %(value)s is not in the list " "of valid values: %(values)s") % {'value': value, 'values': self._valid_values} ) raise ValueError(msg) return super(IntegerEnum, self).coerce(obj, attr, value) class IPVersionEnum(IntegerEnum): """IP version integer Enum""" def __init__(self, **kwargs): super(IPVersionEnum, self).__init__( valid_values=constants.IP_ALLOWED_VERSIONS, **kwargs) class IPVersionEnumField(obj_fields.AutoTypedField): AUTO_TYPE = IPVersionEnum() class DscpMark(IntegerEnum): def __init__(self, valid_values=None, **kwargs): super(DscpMark, self).__init__( valid_values=lib_constants.VALID_DSCP_MARKS) class DscpMarkField(obj_fields.AutoTypedField): AUTO_TYPE = DscpMark() class FlowDirectionEnumField(obj_fields.AutoTypedField): AUTO_TYPE = obj_fields.Enum(valid_values=lib_constants.VALID_DIRECTIONS) class IpamAllocationStatusEnumField(obj_fields.AutoTypedField): AUTO_TYPE = obj_fields.Enum( valid_values=constants.VALID_IPAM_ALLOCATION_STATUSES) class EtherTypeEnumField(obj_fields.AutoTypedField): AUTO_TYPE = obj_fields.Enum(valid_values=constants.VALID_ETHERTYPES) class IpProtocolEnum(obj_fields.Enum): """IP protocol number Enum""" def __init__(self, **kwargs): super(IpProtocolEnum, self).__init__( valid_values=list( itertools.chain( lib_constants.IP_PROTOCOL_MAP.keys(), [str(v) for v in range(256)] ) ), **kwargs) class PortBindingStatusEnumField(obj_fields.AutoTypedField): AUTO_TYPE = obj_fields.Enum(valid_values=constants.PORT_BINDING_STATUSES) class IpProtocolEnumField(obj_fields.AutoTypedField): AUTO_TYPE = IpProtocolEnum() class MACAddress(obj_fields.FieldType): """MACAddress custom field. This custom field is different from the one provided by oslo.versionedobjects library: it uses netaddr.EUI type instead of strings. """ def coerce(self, obj, attr, value): if not isinstance(value, netaddr.EUI): msg = _("Field value %s is not a netaddr.EUI") % value raise ValueError(msg) return super(MACAddress, self).coerce(obj, attr, value) @staticmethod def to_primitive(obj, attr, value): return str(value) @staticmethod def from_primitive(obj, attr, value): try: return utils.AuthenticEUI(value) except Exception: msg = _("Field value %s is not a netaddr.EUI") % value raise ValueError(msg) class MACAddressField(obj_fields.AutoTypedField): AUTO_TYPE = MACAddress() class DictOfMiscValues(obj_fields.FieldType): """DictOfMiscValues custom field This custom field is handling dictionary with miscellaneous value types, including integer, float, boolean and list and nested dictionaries. """ @staticmethod def coerce(obj, attr, value): if isinstance(value, dict): return value if isinstance(value, six.string_types): try: return jsonutils.loads(value) except Exception: msg = _("Field value %s is not stringified JSON") % value raise ValueError(msg) msg = (_("Field value %s is not type of dict or stringified JSON") % value) raise ValueError(msg) @staticmethod def from_primitive(obj, attr, value): return DictOfMiscValues.coerce(obj, attr, value) @staticmethod def to_primitive(obj, attr, value): return jsonutils.dumps(value) @staticmethod def stringify(value): return jsonutils.dumps(value) class DictOfMiscValuesField(obj_fields.AutoTypedField): AUTO_TYPE = DictOfMiscValues class ListOfDictOfMiscValuesField(obj_fields.AutoTypedField): AUTO_TYPE = obj_fields.List(DictOfMiscValuesField()) class IPNetwork(obj_fields.FieldType): """IPNetwork custom field. This custom field is different from the one provided by oslo.versionedobjects library: it does not reset string representation for the field. """ def coerce(self, obj, attr, value): if not isinstance(value, netaddr.IPNetwork): msg = _("Field value %s is not a netaddr.IPNetwork") % value raise ValueError(msg) return super(IPNetwork, self).coerce(obj, attr, value) @staticmethod def to_primitive(obj, attr, value): return str(value) @staticmethod def from_primitive(obj, attr, value): try: return utils.AuthenticIPNetwork(value) except Exception: msg = _("Field value %s is not a netaddr.IPNetwork") % value raise ValueError(msg) class IPNetworkField(obj_fields.AutoTypedField): AUTO_TYPE = IPNetwork() class UUID(obj_fields.UUID): def coerce(self, obj, attr, value): uuid.UUID(str(value)) return str(value) class UUIDField(obj_fields.AutoTypedField): AUTO_TYPE = UUID() class FloatingIPStatusEnumField(obj_fields.AutoTypedField): AUTO_TYPE = obj_fields.Enum(valid_values=constants.VALID_FLOATINGIP_STATUS) class RouterStatusEnumField(obj_fields.AutoTypedField): AUTO_TYPE = obj_fields.Enum(valid_values=constants.VALID_ROUTER_STATUS) # Duplicate some fixes in later oslo.versionedobjects, so we can backport # fixes without modifying requirements. class List(obj_fields.List): def coerce(self, obj, attr, value): if (not isinstance(value, collections.Iterable) or isinstance(value, six.string_types + (collections.Mapping,))): raise ValueError(_('A list is required in field %(attr)s, ' 'not a %(type)s') % {'attr': attr, 'type': type(value).__name__}) coerced_list = obj_fields.CoercedList() coerced_list.enable_coercing(self._element_type, obj, attr) coerced_list.extend(value) return coerced_list class ListOfObjectsField(obj_fields.AutoTypedField): def __init__(self, objtype, subclasses=False, **kwargs): self.AUTO_TYPE = List(obj_fields.Object(objtype, subclasses)) self.objname = objtype super(ListOfObjectsField, self).__init__(**kwargs) neutron-12.1.1/neutron/objects/port/0000775000175000017500000000000013553660156017431 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/port/extensions/0000775000175000017500000000000013553660156021630 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/port/extensions/data_plane_status.py0000664000175000017500000000230113553660046025667 0ustar zuulzuul00000000000000# Copyright (c) 2017 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.models import data_plane_status as db_models from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class PortDataPlaneStatus(base.NeutronDbObject): # Version 1.0: Initial version VERSION = "1.0" db_model = db_models.PortDataPlaneStatus primary_keys = ['port_id'] fields = { 'port_id': common_types.UUIDField(), 'data_plane_status': obj_fields.StringField(), } foreign_keys = {'Port': {'port_id': 'id'}} neutron-12.1.1/neutron/objects/port/extensions/port_security.py0000664000175000017500000000166013553660046025116 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.db.port_security import models from neutron.objects import base from neutron.objects.extensions import port_security as base_ps @base.NeutronObjectRegistry.register class PortSecurity(base_ps._PortSecurity): # Version 1.0: Initial version VERSION = "1.0" fields_need_translation = {'id': 'port_id'} db_model = models.PortSecurityBinding neutron-12.1.1/neutron/objects/port/extensions/__init__.py0000664000175000017500000000000013553660046023725 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/port/extensions/allowedaddresspairs.py0000664000175000017500000000444313553660047026242 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.common import utils from neutron.db.models import allowed_address_pair as models from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class AllowedAddressPair(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.AllowedAddressPair primary_keys = ['port_id', 'mac_address', 'ip_address'] fields = { 'port_id': common_types.UUIDField(), 'mac_address': common_types.MACAddressField(), 'ip_address': common_types.IPNetworkField(), } foreign_keys = { 'Port': {'port_id': 'id'}, } # TODO(mhickey): get rid of it once we switch the db model to using # custom types. @classmethod def modify_fields_to_db(cls, fields): result = super(AllowedAddressPair, cls).modify_fields_to_db(fields) if 'ip_address' in result: result['ip_address'] = cls.filter_to_str(result['ip_address']) if 'mac_address' in result: result['mac_address'] = cls.filter_to_str(result['mac_address']) return result # TODO(mhickey): get rid of it once we switch the db model to using # custom types. @classmethod def modify_fields_from_db(cls, db_obj): fields = super(AllowedAddressPair, cls).modify_fields_from_db(db_obj) if 'ip_address' in fields: # retain string format as stored in the database fields['ip_address'] = utils.AuthenticIPNetwork( fields['ip_address']) if 'mac_address' in fields: # retain string format as stored in the database fields['mac_address'] = utils.AuthenticEUI( fields['mac_address']) return fields neutron-12.1.1/neutron/objects/port/extensions/extra_dhcp_opt.py0000664000175000017500000000237113553660046025206 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.extra_dhcp_opt import models from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class ExtraDhcpOpt(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.ExtraDhcpOpt fields = { 'id': common_types.UUIDField(), 'port_id': common_types.UUIDField(), 'opt_name': obj_fields.StringField(), 'opt_value': obj_fields.StringField(), 'ip_version': obj_fields.IntegerField(), } fields_no_update = ['port_id'] foreign_keys = { 'Port': {'port_id': 'id'}, } neutron-12.1.1/neutron/objects/port/__init__.py0000664000175000017500000000000013553660046021526 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/network.py0000664000175000017500000002771513553660047020523 0ustar zuulzuul00000000000000# Copyright (c) 2016 OpenStack Foundation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import availability_zone as az_def from neutron_lib.api.validators import availability_zone as az_validator from oslo_versionedobjects import fields as obj_fields from neutron.db.models import dns as dns_models from neutron.db.models import external_net as ext_net_model from neutron.db.models import segment as segment_model from neutron.db import models_v2 from neutron.db.network_dhcp_agent_binding import models as ndab_models from neutron.db.port_security import models as ps_models from neutron.db import rbac_db_models from neutron.objects import agent as agent_obj from neutron.objects import base from neutron.objects import common_types from neutron.objects.extensions import port_security as base_ps from neutron.objects.qos import binding from neutron.objects import rbac_db @base.NeutronObjectRegistry.register class NetworkRBAC(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = rbac_db_models.NetworkRBAC fields = { 'object_id': obj_fields.StringField(), 'target_tenant': obj_fields.StringField(), 'action': obj_fields.StringField(), } @base.NeutronObjectRegistry.register class NetworkDhcpAgentBinding(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = ndab_models.NetworkDhcpAgentBinding primary_keys = ['network_id', 'dhcp_agent_id'] fields = { 'network_id': common_types.UUIDField(), 'dhcp_agent_id': common_types.UUIDField(), } # NOTE(ndahiwade): The join was implemented this way as get_objects # currently doesn't support operators like '<' or '>' @classmethod def get_down_bindings(cls, context, cutoff): agent_objs = agent_obj.Agent.get_objects(context) dhcp_agent_ids = [obj.id for obj in agent_objs if obj.heartbeat_timestamp < cutoff] return cls.get_objects(context, dhcp_agent_id=dhcp_agent_ids) @base.NeutronObjectRegistry.register class NetworkSegment(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = segment_model.NetworkSegment fields = { 'id': common_types.UUIDField(), 'network_id': common_types.UUIDField(), 'name': obj_fields.StringField(nullable=True), 'network_type': obj_fields.StringField(), 'physical_network': obj_fields.StringField(nullable=True), 'segmentation_id': obj_fields.IntegerField(nullable=True), 'is_dynamic': obj_fields.BooleanField(default=False), 'segment_index': obj_fields.IntegerField(default=0), 'hosts': obj_fields.ListOfStringsField(nullable=True) } synthetic_fields = ['hosts'] fields_no_update = ['network_id'] foreign_keys = { 'Network': {'network_id': 'id'}, 'PortBindingLevel': {'id': 'segment_id'}, } def create(self): fields = self.obj_get_changes() with self.db_context_writer(self.obj_context): hosts = self.hosts if hosts is None: hosts = [] super(NetworkSegment, self).create() if 'hosts' in fields: self._attach_hosts(hosts) def update(self): fields = self.obj_get_changes() with self.db_context_writer(self.obj_context): super(NetworkSegment, self).update() if 'hosts' in fields: self._attach_hosts(fields['hosts']) def _attach_hosts(self, hosts): SegmentHostMapping.delete_objects( self.obj_context, segment_id=self.id, ) if hosts: for host in hosts: SegmentHostMapping( self.obj_context, segment_id=self.id, host=host).create() self.hosts = hosts self.obj_reset_changes(['hosts']) def obj_load_attr(self, attrname): if attrname == 'hosts': return self._load_hosts() super(NetworkSegment, self).obj_load_attr(attrname) def _load_hosts(self, db_obj=None): if db_obj: hosts = db_obj.get('segment_host_mapping', []) else: hosts = SegmentHostMapping.get_objects(self.obj_context, segment_id=self.id) self.hosts = [host['host'] for host in hosts] self.obj_reset_changes(['hosts']) def from_db_object(self, db_obj): super(NetworkSegment, self).from_db_object(db_obj) self._load_hosts(db_obj) @classmethod def get_objects(cls, context, _pager=None, **kwargs): if not _pager: _pager = base.Pager() if not _pager.sorts: # (NOTE) True means ASC, False is DESC _pager.sorts = [ (field, True) for field in ('network_id', 'segment_index') ] return super(NetworkSegment, cls).get_objects(context, _pager, **kwargs) @base.NeutronObjectRegistry.register class NetworkPortSecurity(base_ps._PortSecurity): # Version 1.0: Initial version VERSION = "1.0" db_model = ps_models.NetworkSecurityBinding fields_need_translation = {'id': 'network_id'} @base.NeutronObjectRegistry.register class ExternalNetwork(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = ext_net_model.ExternalNetwork foreign_keys = {'Network': {'network_id': 'id'}} primary_keys = ['network_id'] fields = { 'network_id': common_types.UUIDField(), 'is_default': obj_fields.BooleanField(default=False), } @base.NeutronObjectRegistry.register class Network(rbac_db.NeutronRbacObject): # Version 1.0: Initial version VERSION = '1.0' rbac_db_cls = NetworkRBAC db_model = models_v2.Network fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(nullable=True), 'status': obj_fields.StringField(nullable=True), 'admin_state_up': obj_fields.BooleanField(nullable=True), 'vlan_transparent': obj_fields.BooleanField(nullable=True), # TODO(ihrachys): consider converting to a field of stricter type 'availability_zone_hints': obj_fields.ListOfStringsField( nullable=True), 'shared': obj_fields.BooleanField(default=False), 'mtu': obj_fields.IntegerField(nullable=True), # TODO(ihrachys): consider exposing availability zones # TODO(ihrachys): consider converting to boolean 'security': obj_fields.ObjectField( 'NetworkPortSecurity', nullable=True), 'segments': obj_fields.ListOfObjectsField( 'NetworkSegment', nullable=True), 'dns_domain': common_types.DomainNameField(nullable=True), 'qos_policy_id': common_types.UUIDField(nullable=True, default=None), # TODO(ihrachys): add support for tags, probably through a base class # since it's a feature that will probably later be added for other # resources too # TODO(ihrachys): expose external network attributes } synthetic_fields = [ 'dns_domain', 'qos_policy_id', 'security', 'segments', ] fields_need_translation = { 'security': 'port_security', } def create(self): fields = self.obj_get_changes() with self.db_context_writer(self.obj_context): dns_domain = self.dns_domain qos_policy_id = self.qos_policy_id super(Network, self).create() if 'dns_domain' in fields: self._set_dns_domain(dns_domain) if 'qos_policy_id' in fields: self._attach_qos_policy(qos_policy_id) def update(self): fields = self.obj_get_changes() with self.db_context_writer(self.obj_context): super(Network, self).update() if 'dns_domain' in fields: self._set_dns_domain(fields['dns_domain']) if 'qos_policy_id' in fields: self._attach_qos_policy(fields['qos_policy_id']) def _attach_qos_policy(self, qos_policy_id): binding.QosPolicyNetworkBinding.delete_objects( self.obj_context, network_id=self.id) if qos_policy_id: net_binding_obj = binding.QosPolicyNetworkBinding( self.obj_context, policy_id=qos_policy_id, network_id=self.id) net_binding_obj.create() self.qos_policy_id = qos_policy_id self.obj_reset_changes(['qos_policy_id']) def _set_dns_domain(self, dns_domain): NetworkDNSDomain.delete_objects(self.obj_context, network_id=self.id) if dns_domain: NetworkDNSDomain(self.obj_context, network_id=self.id, dns_domain=dns_domain).create() self.dns_domain = dns_domain self.obj_reset_changes(['dns_domain']) @classmethod def modify_fields_from_db(cls, db_obj): result = super(Network, cls).modify_fields_from_db(db_obj) if az_def.AZ_HINTS in result: result[az_def.AZ_HINTS] = ( az_validator.convert_az_string_to_list( result[az_def.AZ_HINTS])) return result @classmethod def modify_fields_to_db(cls, fields): result = super(Network, cls).modify_fields_to_db(fields) if az_def.AZ_HINTS in result: result[az_def.AZ_HINTS] = ( az_validator.convert_az_list_to_string( result[az_def.AZ_HINTS])) return result def from_db_object(self, *objs): super(Network, self).from_db_object(*objs) for db_obj in objs: # extract domain name if db_obj.get('dns_domain'): self.dns_domain = ( db_obj.dns_domain.dns_domain ) else: self.dns_domain = None self.obj_reset_changes(['dns_domain']) # extract qos policy binding if db_obj.get('qos_policy_binding'): self.qos_policy_id = ( db_obj.qos_policy_binding.policy_id ) else: self.qos_policy_id = None self.obj_reset_changes(['qos_policy_id']) @classmethod def get_bound_tenant_ids(cls, context, policy_id): # TODO(ihrachys): provide actual implementation return set() @base.NeutronObjectRegistry.register class SegmentHostMapping(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = segment_model.SegmentHostMapping fields = { 'segment_id': common_types.UUIDField(), 'host': obj_fields.StringField(), } primary_keys = ['segment_id', 'host'] @base.NeutronObjectRegistry.register class NetworkDNSDomain(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = dns_models.NetworkDNSDomain primary_keys = ['network_id'] fields = { 'network_id': common_types.UUIDField(), 'dns_domain': common_types.DomainNameField(), } @classmethod def get_net_dns_from_port(cls, context, port_id): net_dns = context.session.query(cls.db_model).join( models_v2.Port, cls.db_model.network_id == models_v2.Port.network_id).filter_by( id=port_id).one_or_none() if net_dns is None: return None return super(NetworkDNSDomain, cls)._load_object(context, net_dns) neutron-12.1.1/neutron/objects/ipam.py0000664000175000017500000000662513553660046017754 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from oslo_versionedobjects import fields as obj_fields from neutron.ipam.drivers.neutrondb_ipam import db_models from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class IpamAllocationPool(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = db_models.IpamAllocationPool foreign_keys = {'IpamSubnet': {'ipam_subnet_id': 'id'}} fields = { 'id': common_types.UUIDField(), 'ipam_subnet_id': common_types.UUIDField(), 'first_ip': obj_fields.IPAddressField(), 'last_ip': obj_fields.IPAddressField(), } fields_no_update = ['ipam_subnet_id'] @classmethod def modify_fields_from_db(cls, db_obj): result = super(IpamAllocationPool, cls).modify_fields_from_db(db_obj) if 'first_ip' in result: result['first_ip'] = netaddr.IPAddress(result['first_ip']) if 'last_ip' in result: result['last_ip'] = netaddr.IPAddress(result['last_ip']) return result @classmethod def modify_fields_to_db(cls, fields): result = super(IpamAllocationPool, cls).modify_fields_to_db(fields) if 'first_ip' in result: result['first_ip'] = cls.filter_to_str(result['first_ip']) if 'last_ip' in result: result['last_ip'] = cls.filter_to_str(result['last_ip']) return result @base.NeutronObjectRegistry.register class IpamAllocation(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = db_models.IpamAllocation primary_keys = ['ip_address', 'ipam_subnet_id'] fields = { 'ip_address': obj_fields.IPAddressField(), 'status': common_types.IpamAllocationStatusEnumField(nullable=True), 'ipam_subnet_id': common_types.UUIDField() } @classmethod def modify_fields_from_db(cls, db_obj): result = super(IpamAllocation, cls).modify_fields_from_db(db_obj) if 'ip_address' in result: result['ip_address'] = netaddr.IPAddress(result['ip_address']) return result @classmethod def modify_fields_to_db(cls, fields): result = super(IpamAllocation, cls).modify_fields_to_db(fields) if 'ip_address' in result: result['ip_address'] = cls.filter_to_str(result['ip_address']) return result @base.NeutronObjectRegistry.register class IpamSubnet(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = db_models.IpamSubnet fields = { 'id': common_types.UUIDField(), 'neutron_subnet_id': common_types.UUIDField(nullable=True), 'allocation_pools': obj_fields.ListOfObjectsField( 'IpamAllocationPool') } synthetic_fields = ['allocation_pools'] neutron-12.1.1/neutron/objects/__init__.py0000664000175000017500000000145313553660046020557 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import sys def register_objects(): # local import to avoid circular import failure from neutron.common import utils dirn = os.path.dirname(sys.modules[__name__].__file__) utils.import_modules_recursively(dirn) neutron-12.1.1/neutron/objects/rbac_db.py0000664000175000017500000003354513553660047020404 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import itertools from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import exceptions as lib_exc from six import add_metaclass from six import with_metaclass from sqlalchemy import and_ from neutron._i18n import _ from neutron.common import exceptions as n_exc from neutron.db import _utils as db_utils from neutron.db import rbac_db_mixin from neutron.db import rbac_db_models as models from neutron.extensions import rbac as ext_rbac from neutron.objects import base from neutron.objects.db import api as obj_db_api @add_metaclass(abc.ABCMeta) class RbacNeutronDbObjectMixin(rbac_db_mixin.RbacPluginMixin, base.NeutronDbObject): rbac_db_cls = None @classmethod @abc.abstractmethod def get_bound_tenant_ids(cls, context, obj_id): """Returns ids of all tenants depending on this db object. Has to be implemented by classes using RbacNeutronMetaclass. The tenants are the ones that need the sharing or 'visibility' of the object to them. E.g: for QosPolicy that would be the tenants using the Networks and Ports with the shared QosPolicy applied to them. :returns: set -- a set of tenants' ids dependent on this object. """ @staticmethod def is_network_shared(context, rbac_entries): # NOTE(korzen) this method is copied from db_base_plugin_common. # The shared attribute for a network now reflects if the network # is shared to the calling tenant via an RBAC entry. matches = ('*',) + ((context.tenant_id,) if context else ()) for entry in rbac_entries: if (entry.action == models.ACCESS_SHARED and entry.target_tenant in matches): return True return False @staticmethod def get_shared_with_tenant(context, rbac_db_cls, obj_id, tenant_id): # NOTE(korzen) This method enables to query within already started # session rbac_db_model = rbac_db_cls.db_model return (db_utils.model_query(context, rbac_db_model).filter( and_(rbac_db_model.object_id == obj_id, rbac_db_model.action == models.ACCESS_SHARED, rbac_db_model.target_tenant.in_( ['*', tenant_id]))).count() != 0) @classmethod def is_shared_with_tenant(cls, context, obj_id, tenant_id): ctx = context.elevated() with cls.db_context_reader(ctx): return cls.get_shared_with_tenant(ctx, cls.rbac_db_cls, obj_id, tenant_id) @classmethod def is_accessible(cls, context, db_obj): return (super( RbacNeutronDbObjectMixin, cls).is_accessible(context, db_obj) or cls.is_shared_with_tenant(context, db_obj.id, context.tenant_id)) @classmethod def _get_db_obj_rbac_entries(cls, context, rbac_obj_id, rbac_action): rbac_db_model = cls.rbac_db_cls.db_model return db_utils.model_query(context, rbac_db_model).filter( and_(rbac_db_model.object_id == rbac_obj_id, rbac_db_model.action == rbac_action)) @classmethod def _get_tenants_with_shared_access_to_db_obj(cls, context, obj_id): rbac_db_model = cls.rbac_db_cls.db_model return set(itertools.chain.from_iterable(context.session.query( rbac_db_model.target_tenant).filter( and_(rbac_db_model.object_id == obj_id, rbac_db_model.action == models.ACCESS_SHARED, rbac_db_model.target_tenant != '*')))) @classmethod def _validate_rbac_policy_delete(cls, context, obj_id, target_tenant): ctx_admin = context.elevated() rb_model = cls.rbac_db_cls.db_model bound_tenant_ids = cls.get_bound_tenant_ids(ctx_admin, obj_id) db_obj_sharing_entries = cls._get_db_obj_rbac_entries( ctx_admin, obj_id, models.ACCESS_SHARED) def raise_policy_in_use(): raise ext_rbac.RbacPolicyInUse( object_id=obj_id, details='tenant_id={}'.format(target_tenant)) if target_tenant != '*': # if there is a wildcard rule, we can return early because it # shares the object globally wildcard_sharing_entries = db_obj_sharing_entries.filter( rb_model.target_tenant == '*') if wildcard_sharing_entries.count(): return if target_tenant in bound_tenant_ids: raise_policy_in_use() return # for the wildcard we need to query all of the rbac entries to # see if any allow the object sharing other_target_tenants = cls._get_tenants_with_shared_access_to_db_obj( ctx_admin, obj_id) if not bound_tenant_ids.issubset(other_target_tenants): raise_policy_in_use() @classmethod def validate_rbac_policy_delete(cls, resource, event, trigger, context, object_type, policy, **kwargs): """Callback to handle RBAC_POLICY, BEFORE_DELETE callback. :raises: RbacPolicyInUse -- in case the policy is in use. """ if policy['action'] != models.ACCESS_SHARED: return target_tenant = policy['target_tenant'] db_obj = obj_db_api.get_object( cls, context.elevated(), id=policy['object_id']) if db_obj.tenant_id == target_tenant: return cls._validate_rbac_policy_delete(context=context, obj_id=policy['object_id'], target_tenant=target_tenant) @classmethod def validate_rbac_policy_update(cls, resource, event, trigger, context, object_type, policy, **kwargs): """Callback to handle RBAC_POLICY, BEFORE_UPDATE callback. :raises: RbacPolicyInUse -- in case the update is forbidden. """ prev_tenant = policy['target_tenant'] new_tenant = kwargs['policy_update']['target_tenant'] if prev_tenant == new_tenant: return if new_tenant != '*': return cls.validate_rbac_policy_delete( resource, event, trigger, context, object_type, policy) @classmethod def validate_rbac_policy_change(cls, resource, event, trigger, context, object_type, policy, **kwargs): """Callback to validate RBAC_POLICY changes. This is the dispatching function for create, update and delete callbacks. On creation and update, verify that the creator is an admin or owns the resource being shared. """ # TODO(hdaniel): As this code was shamelessly stolen from # NeutronDbPluginV2.validate_network_rbac_policy_change(), those pieces # should be synced and contain the same bugs, until Network RBAC logic # (hopefully) melded with this one. if object_type != cls.rbac_db_cls.db_model.object_type: return db_obj = obj_db_api.get_object( cls, context.elevated(), id=policy['object_id']) if event in (events.BEFORE_CREATE, events.BEFORE_UPDATE): if (not context.is_admin and db_obj['tenant_id'] != context.tenant_id): msg = _("Only admins can manipulate policies on objects " "they do not own") raise lib_exc.InvalidInput(error_message=msg) callback_map = {events.BEFORE_UPDATE: cls.validate_rbac_policy_update, events.BEFORE_DELETE: cls.validate_rbac_policy_delete} if event in callback_map: return callback_map[event](resource, event, trigger, context, object_type, policy, **kwargs) def attach_rbac(self, obj_id, tenant_id, target_tenant='*'): obj_type = self.rbac_db_cls.db_model.object_type rbac_policy = {'rbac_policy': {'object_id': obj_id, 'target_tenant': target_tenant, 'tenant_id': tenant_id, 'object_type': obj_type, 'action': models.ACCESS_SHARED}} return self.create_rbac_policy(self.obj_context, rbac_policy) def update_shared(self, is_shared_new, obj_id): admin_context = self.obj_context.elevated() shared_prev = obj_db_api.get_object(self.rbac_db_cls, admin_context, object_id=obj_id, target_tenant='*', action=models.ACCESS_SHARED) is_shared_prev = bool(shared_prev) if is_shared_prev == is_shared_new: return # 'shared' goes False -> True if not is_shared_prev and is_shared_new: self.attach_rbac(obj_id, self.obj_context.tenant_id) return # 'shared' goes True -> False is actually an attempt to delete # rbac rule for sharing obj_id with target_tenant = '*' self._validate_rbac_policy_delete(self.obj_context, obj_id, '*') return self.obj_context.session.delete(shared_prev) def _update_post(self, obj_changes): if "shared" in obj_changes: self.update_shared(self.shared, self.id) def _update_hook(self, update_orig): with self.db_context_writer(self.obj_context): # NOTE(slaweq): copy of object changes is required to pass it later to # _update_post method because update() will reset all those changes obj_changes = self.obj_get_changes() update_orig(self) _update_post(self, obj_changes) def _create_post(self): if self.shared: self.attach_rbac(self.id, self.obj_context.tenant_id) def _create_hook(self, orig_create): with self.db_context_writer(self.obj_context): orig_create(self) _create_post(self) def _to_dict_hook(self, to_dict_orig): dct = to_dict_orig(self) if self.obj_context: dct['shared'] = self.is_shared_with_tenant(self.obj_context, self.id, self.obj_context.tenant_id) else: # most OVO objects on an agent will not have a context set on the # object because they will be generated from obj_from_primitive. dct['shared'] = False return dct class RbacNeutronMetaclass(type): """Adds support for RBAC in NeutronDbObjects. Injects code for CRUD operations and modifies existing ops to do so. """ @classmethod def _get_attribute(mcs, attribute_name, bases): for b in bases: attribute = getattr(b, attribute_name, None) if attribute: return attribute @classmethod def get_attribute(mcs, attribute_name, bases, dct): return (dct.get(attribute_name, None) or mcs._get_attribute(attribute_name, bases)) @classmethod def update_synthetic_fields(mcs, bases, dct): if not dct.get('synthetic_fields', None): synthetic_attr = mcs.get_attribute('synthetic_fields', bases, dct) dct['synthetic_fields'] = synthetic_attr or [] if 'shared' in dct['synthetic_fields']: raise n_exc.ObjectActionError( action=_('shared attribute switching to synthetic'), reason=_('already a synthetic attribute')) dct['synthetic_fields'].append('shared') @staticmethod def subscribe_to_rbac_events(class_instance): for e in (events.BEFORE_CREATE, events.BEFORE_UPDATE, events.BEFORE_DELETE): registry.subscribe(class_instance.validate_rbac_policy_change, rbac_db_mixin.RBAC_POLICY, e) @staticmethod def validate_existing_attrs(cls_name, dct): if 'shared' not in dct['fields']: raise KeyError(_('No shared key in %s fields') % cls_name) if 'rbac_db_cls' not in dct: raise AttributeError(_('rbac_db_cls not found in %s') % cls_name) @staticmethod def get_replaced_method(orig_method, new_method): def func(self): return new_method(self, orig_method) return func @classmethod def replace_class_methods_with_hooks(mcs, bases, dct): methods_replacement_map = {'create': _create_hook, 'update': _update_hook, 'to_dict': _to_dict_hook} for orig_method_name, new_method in methods_replacement_map.items(): orig_method = mcs.get_attribute(orig_method_name, bases, dct) hook_method = mcs.get_replaced_method(orig_method, new_method) dct[orig_method_name] = hook_method def __new__(mcs, name, bases, dct): mcs.validate_existing_attrs(name, dct) mcs.update_synthetic_fields(bases, dct) mcs.replace_class_methods_with_hooks(bases, dct) cls = type(name, (RbacNeutronDbObjectMixin,) + bases, dct) cls.add_extra_filter_name('shared') mcs.subscribe_to_rbac_events(cls) return cls NeutronRbacObject = with_metaclass(RbacNeutronMetaclass, base.NeutronDbObject) neutron-12.1.1/neutron/objects/db/0000775000175000017500000000000013553660156017032 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/db/api.py0000664000175000017500000001032113553660047020151 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # TODO(ihrachys): cover the module with functional tests targeting supported # backends from neutron_lib import exceptions as n_exc from oslo_utils import uuidutils from neutron.db import _model_query as model_query from neutron.objects import utils as obj_utils # Common database operation implementations def _get_filter_query(obj_cls, context, **kwargs): with obj_cls.db_context_reader(context): filters = _kwargs_to_filters(**kwargs) query = model_query.get_collection_query( context, obj_cls.db_model, filters) return query def get_object(obj_cls, context, **kwargs): return _get_filter_query(obj_cls, context, **kwargs).first() def count(obj_cls, context, **kwargs): return _get_filter_query(obj_cls, context, **kwargs).count() def _kwargs_to_filters(**kwargs): retain_classes = (list, set, obj_utils.StringMatchingFilterObj) return {k: v if isinstance(v, retain_classes) else [v] for k, v in kwargs.items()} def get_objects(obj_cls, context, _pager=None, **kwargs): with obj_cls.db_context_reader(context): filters = _kwargs_to_filters(**kwargs) return model_query.get_collection( context, obj_cls.db_model, dict_func=None, # return all the data filters=filters, **(_pager.to_kwargs(context, obj_cls) if _pager else {})) def create_object(obj_cls, context, values, populate_id=True): with obj_cls.db_context_writer(context): if (populate_id and 'id' not in values and hasattr(obj_cls.db_model, 'id')): values['id'] = uuidutils.generate_uuid() db_obj = obj_cls.db_model(**values) context.session.add(db_obj) return db_obj def _safe_get_object(obj_cls, context, **kwargs): db_obj = get_object(obj_cls, context, **kwargs) if db_obj is None: key = ", ".join(['%s=%s' % (key, value) for (key, value) in kwargs.items()]) raise n_exc.ObjectNotFound( id="%s(%s)" % (obj_cls.db_model.__name__, key)) return db_obj def update_object(obj_cls, context, values, **kwargs): with obj_cls.db_context_writer(context): db_obj = _safe_get_object(obj_cls, context, **kwargs) db_obj.update(values) db_obj.save(session=context.session) return db_obj def delete_object(obj_cls, context, **kwargs): with obj_cls.db_context_writer(context): db_obj = _safe_get_object(obj_cls, context, **kwargs) context.session.delete(db_obj) def update_objects(obj_cls, context, values, **kwargs): '''Update matching objects, if any. Return number of updated objects. This function does not raise exceptions if nothing matches. :param obj_cls: Object class :param values: values to update in matching objects :param kwargs: multiple filters defined by key=value pairs :return: Number of entries updated ''' with obj_cls.db_context_writer(context): if not values: return count(obj_cls, context, **kwargs) q = _get_filter_query(obj_cls, context, **kwargs) return q.update(values, synchronize_session=False) def delete_objects(obj_cls, context, **kwargs): '''Delete matching objects, if any. Return number of deleted objects. This function does not raise exceptions if nothing matches. :param obj_cls: Object class :param kwargs: multiple filters defined by key=value pairs :return: Number of entries deleted ''' with obj_cls.db_context_writer(context): db_objs = get_objects(obj_cls, context, **kwargs) for db_obj in db_objs: context.session.delete(db_obj) return len(db_objs) neutron-12.1.1/neutron/objects/db/__init__.py0000664000175000017500000000000013553660046021127 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/floatingip.py0000664000175000017500000000243613553660046021156 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.db.models import dns as models from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class FloatingIPDNS(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.FloatingIPDNS primary_keys = ['floatingip_id'] foreign_keys = {'FloatingIP': {'floatingip_id': 'id'}} fields = { 'floatingip_id': common_types.UUIDField(), 'dns_name': common_types.DomainNameField(), 'dns_domain': common_types.DomainNameField(), 'published_dns_name': common_types.DomainNameField(), 'published_dns_domain': common_types.DomainNameField(), } neutron-12.1.1/neutron/objects/tag.py0000664000175000017500000000200713553660047017570 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.models import tag as tag_model from neutron.objects import base @base.NeutronObjectRegistry.register class Tag(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = tag_model.Tag fields = { 'tag': obj_fields.StringField(), 'standard_attr_id': obj_fields.IntegerField() } primary_keys = ['tag', 'standard_attr_id'] neutron-12.1.1/neutron/objects/provisioning_blocks.py0000664000175000017500000000206613553660046023104 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.models import provisioning_block as pb_model from neutron.objects import base @base.NeutronObjectRegistry.register class ProvisioningBlock(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = pb_model.ProvisioningBlock fields = { 'standard_attr_id': obj_fields.IntegerField(), 'entity': obj_fields.StringField() } primary_keys = ['standard_attr_id', 'entity'] neutron-12.1.1/neutron/objects/router.py0000664000175000017500000002510613553660047020342 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools import netaddr from neutron_lib.api.definitions import availability_zone as az_def from neutron_lib.api.validators import availability_zone as az_validator from oslo_versionedobjects import fields as obj_fields import six from sqlalchemy import func from neutron.common import constants as n_const from neutron.common import utils from neutron.db.models import dvr as dvr_models from neutron.db.models import l3 from neutron.db.models import l3_attrs from neutron.db.models import l3agent as rb_model from neutron.db import models_v2 from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class RouterRoute(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = l3.RouterRoute fields = { 'router_id': common_types.UUIDField(), 'destination': common_types.IPNetworkField(), 'nexthop': obj_fields.IPAddressField() } primary_keys = ['router_id', 'destination', 'nexthop'] foreign_keys = {'Router': {'router_id': 'id'}} @classmethod def modify_fields_from_db(cls, db_obj): result = super(RouterRoute, cls).modify_fields_from_db(db_obj) if 'destination' in result: result['destination'] = utils.AuthenticIPNetwork( result['destination']) if 'nexthop' in result: result['nexthop'] = netaddr.IPAddress(result['nexthop']) return result @classmethod def modify_fields_to_db(cls, fields): result = super(RouterRoute, cls).modify_fields_to_db(fields) if 'destination' in result: result['destination'] = cls.filter_to_str(result['destination']) if 'nexthop' in result: result['nexthop'] = cls.filter_to_str(result['nexthop']) return result @base.NeutronObjectRegistry.register class RouterExtraAttributes(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = l3_attrs.RouterExtraAttributes fields = { 'router_id': common_types.UUIDField(), 'distributed': obj_fields.BooleanField(default=False), 'service_router': obj_fields.BooleanField(default=False), 'ha': obj_fields.BooleanField(default=False), 'ha_vr_id': obj_fields.IntegerField(nullable=True), 'availability_zone_hints': obj_fields.ListOfStringsField(nullable=True) } primary_keys = ['router_id'] foreign_keys = {'Router': {'router_id': 'id'}} @classmethod def modify_fields_from_db(cls, db_obj): result = super(RouterExtraAttributes, cls).modify_fields_from_db( db_obj) if az_def.AZ_HINTS in result: result[az_def.AZ_HINTS] = ( az_validator.convert_az_string_to_list( result[az_def.AZ_HINTS])) return result @classmethod def modify_fields_to_db(cls, fields): result = super(RouterExtraAttributes, cls).modify_fields_to_db(fields) if az_def.AZ_HINTS in result: result[az_def.AZ_HINTS] = ( az_validator.convert_az_list_to_string( result[az_def.AZ_HINTS])) return result @classmethod def get_router_agents_count(cls, context): # TODO(sshank): This is pulled out from l3_agentschedulers_db.py # until a way to handle joins is figured out. binding_model = rb_model.RouterL3AgentBinding sub_query = (context.session.query( binding_model.router_id, func.count(binding_model.router_id).label('count')). join(l3_attrs.RouterExtraAttributes, binding_model.router_id == l3_attrs.RouterExtraAttributes.router_id). join(l3.Router). group_by(binding_model.router_id).subquery()) query = (context.session.query(l3.Router, sub_query.c.count). outerjoin(sub_query)) return [(router, agent_count) for router, agent_count in query] @base.NeutronObjectRegistry.register class RouterPort(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = l3.RouterPort primary_keys = ['router_id', 'port_id'] foreign_keys = {'Router': {'router_id': 'id'}} fields = { 'router_id': common_types.UUIDField(), 'port_id': common_types.UUIDField(), 'port_type': obj_fields.StringField(nullable=True), } @classmethod def get_router_ids_by_subnetpool(cls, context, subnetpool_id): query = context.session.query(l3.RouterPort.router_id) query = query.join(models_v2.Port) query = query.join( models_v2.Subnet, models_v2.Subnet.network_id == models_v2.Port.network_id) query = query.filter( models_v2.Subnet.subnetpool_id == subnetpool_id, l3.RouterPort.port_type.in_(n_const.ROUTER_PORT_OWNERS)) query = query.distinct() return [r[0] for r in query] @base.NeutronObjectRegistry.register class DVRMacAddress(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = dvr_models.DistributedVirtualRouterMacAddress primary_keys = ['host'] fields = { 'host': obj_fields.StringField(), 'mac_address': common_types.MACAddressField() } @classmethod def modify_fields_from_db(cls, db_obj): fields = super(DVRMacAddress, cls).modify_fields_from_db(db_obj) if 'mac_address' in fields: # NOTE(tonytan4ever): Here uses AuthenticEUI to retain the format # passed from API. fields['mac_address'] = utils.AuthenticEUI(fields['mac_address']) return fields @classmethod def modify_fields_to_db(cls, fields): result = super(DVRMacAddress, cls).modify_fields_to_db(fields) if 'mac_address' in fields: result['mac_address'] = cls.filter_to_str(result['mac_address']) return result @base.NeutronObjectRegistry.register class Router(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = l3.Router fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(nullable=True), 'status': common_types.RouterStatusEnumField(nullable=True), 'admin_state_up': obj_fields.BooleanField(nullable=True), 'gw_port_id': common_types.UUIDField(nullable=True), 'enable_snat': obj_fields.BooleanField(default=True), 'flavor_id': common_types.UUIDField(nullable=True), 'extra_attributes': obj_fields.ObjectField( 'RouterExtraAttributes', nullable=True), } synthetic_fields = ['extra_attributes'] @base.NeutronObjectRegistry.register class FloatingIP(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = l3.FloatingIP fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'floating_ip_address': obj_fields.IPAddressField(), 'floating_network_id': common_types.UUIDField(), 'floating_port_id': common_types.UUIDField(), 'fixed_port_id': common_types.UUIDField(nullable=True), 'fixed_ip_address': obj_fields.IPAddressField(nullable=True), 'router_id': common_types.UUIDField(nullable=True), 'last_known_router_id': common_types.UUIDField(nullable=True), 'status': common_types.FloatingIPStatusEnumField(nullable=True), 'dns': obj_fields.ObjectField('FloatingIPDNS', nullable=True), } fields_no_update = ['project_id', 'floating_ip_address', 'floating_network_id', 'floating_port_id'] synthetic_fields = ['dns'] @classmethod def modify_fields_from_db(cls, db_obj): result = super(FloatingIP, cls).modify_fields_from_db(db_obj) if 'fixed_ip_address' in result: result['fixed_ip_address'] = netaddr.IPAddress( result['fixed_ip_address']) if 'floating_ip_address' in result: result['floating_ip_address'] = netaddr.IPAddress( result['floating_ip_address']) return result @classmethod def modify_fields_to_db(cls, fields): result = super(FloatingIP, cls).modify_fields_to_db(fields) if 'fixed_ip_address' in result: if result['fixed_ip_address'] is not None: result['fixed_ip_address'] = cls.filter_to_str( result['fixed_ip_address']) if 'floating_ip_address' in result: result['floating_ip_address'] = cls.filter_to_str( result['floating_ip_address']) return result @classmethod def get_scoped_floating_ips(cls, context, router_ids): query = context.session.query(l3.FloatingIP, models_v2.SubnetPool.address_scope_id) query = query.join(models_v2.Port, l3.FloatingIP.fixed_port_id == models_v2.Port.id) # Outer join of Subnet can cause each ip to have more than one row. query = query.outerjoin(models_v2.Subnet, models_v2.Subnet.network_id == models_v2.Port.network_id) query = query.filter(models_v2.Subnet.ip_version == 4) query = query.outerjoin(models_v2.SubnetPool, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id) # Filter out on router_ids query = query.filter(l3.FloatingIP.router_id.in_(router_ids)) return cls._unique_floatingip_iterator(context, query) @classmethod def _unique_floatingip_iterator(cls, context, query): """Iterates over only one row per floating ip. Ignores others.""" # Group rows by fip id. They must be sorted by same. q = query.order_by(l3.FloatingIP.id) keyfunc = lambda row: row[0]['id'] group_iterator = itertools.groupby(q, keyfunc) # Just hit the first row of each group for key, value in group_iterator: row = [r for r in six.next(value)] yield (cls._load_object(context, row[0]), row[1]) neutron-12.1.1/neutron/objects/logapi/0000775000175000017500000000000013553660156017720 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/logapi/logging_resource.py0000664000175000017500000000326313553660047023632 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.models import loggingapi as log_db from neutron.objects import base from neutron.objects import common_types from neutron.objects.logapi import event_types from neutron.services.logapi.common import constants as log_const @base.NeutronObjectRegistry.register class Log(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = log_db.Log fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(nullable=True), 'resource_type': obj_fields.StringField(), 'resource_id': common_types.UUIDField(nullable=True, default=None), 'target_id': common_types.UUIDField(nullable=True, default=None), 'event': event_types.SecurityEventField(default=log_const.ALL_EVENT), 'enabled': obj_fields.BooleanField(default=True), } fields_no_update = ['project_id', 'resource_type', 'resource_id', 'target_id', 'event'] neutron-12.1.1/neutron/objects/logapi/__init__.py0000664000175000017500000000000013553660046022015 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/objects/logapi/event_types.py0000664000175000017500000000265213553660047022643 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron._i18n import _ from neutron.services.logapi.common import constants as log_const class SecurityEvent(obj_fields.String): def __init__(self, valid_values, **kwargs): self._valid_values = valid_values super(SecurityEvent, self).__init__(**kwargs) def coerce(self, obj, attr, value): if value not in self._valid_values: msg = ( _("Field value %(value)s is not in the list " "of valid values: %(values)s") % {'value': value, 'values': self._valid_values} ) raise ValueError(msg) return super(SecurityEvent, self).coerce(obj, attr, value) class SecurityEventField(obj_fields.AutoTypedField): AUTO_TYPE = SecurityEvent(valid_values=log_const.LOG_EVENTS) neutron-12.1.1/neutron/objects/address_scope.py0000664000175000017500000000232613553660047021637 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.db.models import address_scope as models from neutron.objects import base from neutron.objects import common_types @base.NeutronObjectRegistry.register class AddressScope(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.AddressScope fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(nullable=True), 'name': obj_fields.StringField(), 'shared': obj_fields.BooleanField(), 'ip_version': common_types.IPVersionEnumField(), } neutron-12.1.1/neutron/objects/auto_allocate.py0000664000175000017500000000237113553660046021634 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import fields as obj_fields from neutron.objects import base from neutron.objects import common_types from neutron.services.auto_allocate import models @base.NeutronObjectRegistry.register class AutoAllocatedTopology(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = models.AutoAllocatedTopology primary_keys = ['project_id'] fields = { 'project_id': obj_fields.StringField(), 'network_id': common_types.UUIDField(), 'router_id': common_types.UUIDField(nullable=True), } fields_no_update = ['network_id', 'router_id'] neutron-12.1.1/neutron/plugins/0000775000175000017500000000000013553660156016475 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/__init__.py0000664000175000017500000000000013553660046020572 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/0000775000175000017500000000000013553660156017167 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/db.py0000664000175000017500000003222413553660047020130 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as n_const from neutron_lib.plugins import directory from oslo_db import exception as db_exc from oslo_log import log from oslo_utils import uuidutils import six from sqlalchemy import or_ from sqlalchemy.orm import exc from neutron._i18n import _ from neutron.db import api as db_api from neutron.db.models import securitygroup as sg_models from neutron.db import models_v2 from neutron.objects import ports as port_obj from neutron.plugins.ml2 import models from neutron.services.segments import exceptions as seg_exc LOG = log.getLogger(__name__) # limit the number of port OR LIKE statements in one query MAX_PORTS_PER_QUERY = 500 @db_api.context_manager.writer def add_port_binding(context, port_id): record = models.PortBinding( port_id=port_id, vif_type=portbindings.VIF_TYPE_UNBOUND) context.session.add(record) return record @db_api.context_manager.writer def set_binding_levels(context, levels): if levels: for level in levels: level.persist_state_to_session(context.session) LOG.debug("For port %(port_id)s, host %(host)s, " "set binding levels %(levels)s", {'port_id': levels[0].port_id, 'host': levels[0].host, 'levels': levels}) else: LOG.debug("Attempted to set empty binding levels") @db_api.context_manager.reader def get_binding_levels(context, port_id, host): if host: result = (context.session.query(models.PortBindingLevel). filter_by(port_id=port_id, host=host). order_by(models.PortBindingLevel.level). all()) LOG.debug("For port %(port_id)s, host %(host)s, " "got binding levels %(levels)s", {'port_id': port_id, 'host': host, 'levels': result}) return result @db_api.context_manager.writer def clear_binding_levels(context, port_id, host): if host: for l in (context.session.query(models.PortBindingLevel). filter_by(port_id=port_id, host=host)): context.session.delete(l) LOG.debug("For port %(port_id)s, host %(host)s, " "cleared binding levels", {'port_id': port_id, 'host': host}) def ensure_distributed_port_binding(context, port_id, host, router_id=None): with db_api.context_manager.reader.using(context): record = (context.session.query(models.DistributedPortBinding). filter_by(port_id=port_id, host=host).first()) if record: return record try: with db_api.context_manager.writer.using(context): record = models.DistributedPortBinding( port_id=port_id, host=host, router_id=router_id, vif_type=portbindings.VIF_TYPE_UNBOUND, vnic_type=portbindings.VNIC_NORMAL, status=n_const.PORT_STATUS_DOWN) context.session.add(record) return record except db_exc.DBDuplicateEntry: LOG.debug("Distributed Port %s already bound", port_id) with db_api.context_manager.reader.using(context): return (context.session.query(models.DistributedPortBinding). filter_by(port_id=port_id, host=host).one()) def delete_distributed_port_binding_if_stale(context, binding): if not binding.router_id and binding.status == n_const.PORT_STATUS_DOWN: with db_api.context_manager.writer.using(context): LOG.debug("Distributed port: Deleting binding %s", binding) context.session.delete(binding) def get_port(context, port_id): """Get port record for update within transaction.""" with db_api.context_manager.reader.using(context): try: # Set enable_eagerloads to True, so that lazy load can be # proceed later. record = (context.session.query(models_v2.Port). enable_eagerloads(True). filter(models_v2.Port.id.startswith(port_id)). one()) return record except exc.NoResultFound: return except exc.MultipleResultsFound: LOG.error("Multiple ports have port_id starting with %s", port_id) return @db_api.context_manager.reader def get_port_from_device_mac(context, device_mac): LOG.debug("get_port_from_device_mac() called for mac %s", device_mac) ports = port_obj.Port.get_objects(context, mac_address=device_mac) return ports.pop() if ports else None def get_ports_and_sgs(context, port_ids): """Get ports from database with security group info.""" # break large queries into smaller parts if len(port_ids) > MAX_PORTS_PER_QUERY: LOG.debug("Number of ports %(pcount)s exceeds the maximum per " "query %(maxp)s. Partitioning queries.", {'pcount': len(port_ids), 'maxp': MAX_PORTS_PER_QUERY}) return (get_ports_and_sgs(context, port_ids[:MAX_PORTS_PER_QUERY]) + get_ports_and_sgs(context, port_ids[MAX_PORTS_PER_QUERY:])) LOG.debug("get_ports_and_sgs() called for port_ids %s", port_ids) if not port_ids: # if port_ids is empty, avoid querying to DB to ask it for nothing return [] ports_to_sg_ids = get_sg_ids_grouped_by_port(context, port_ids) return [make_port_dict_with_security_groups(port, sec_groups) for port, sec_groups in six.iteritems(ports_to_sg_ids)] def get_sg_ids_grouped_by_port(context, port_ids): sg_ids_grouped_by_port = {} sg_binding_port = sg_models.SecurityGroupPortBinding.port_id with db_api.context_manager.reader.using(context): # partial UUIDs must be individually matched with startswith. # full UUIDs may be matched directly in an IN statement partial_uuids = set(port_id for port_id in port_ids if not uuidutils.is_uuid_like(port_id)) full_uuids = set(port_ids) - partial_uuids or_criteria = [models_v2.Port.id.startswith(port_id) for port_id in partial_uuids] if full_uuids: or_criteria.append(models_v2.Port.id.in_(full_uuids)) query = context.session.query( models_v2.Port, sg_models.SecurityGroupPortBinding.security_group_id) query = query.outerjoin(sg_models.SecurityGroupPortBinding, models_v2.Port.id == sg_binding_port) query = query.filter(or_(*or_criteria)) for port, sg_id in query: if port not in sg_ids_grouped_by_port: sg_ids_grouped_by_port[port] = [] if sg_id: sg_ids_grouped_by_port[port].append(sg_id) return sg_ids_grouped_by_port def make_port_dict_with_security_groups(port, sec_groups): plugin = directory.get_plugin() port_dict = plugin._make_port_dict(port) port_dict['security_groups'] = sec_groups port_dict['security_group_rules'] = [] port_dict['security_group_source_groups'] = [] port_dict['fixed_ips'] = [ip['ip_address'] for ip in port['fixed_ips']] return port_dict def get_port_binding_host(context, port_id): try: with db_api.context_manager.reader.using(context): query = (context.session.query(models.PortBinding.host). filter(models.PortBinding.port_id.startswith(port_id))) query = query.filter( models.PortBinding.status == n_const.ACTIVE).one() except exc.NoResultFound: LOG.debug("No binding found for port %(port_id)s", {'port_id': port_id}) return except exc.MultipleResultsFound: LOG.error("Multiple ports have port_id starting with %s", port_id) return return query.host @db_api.context_manager.reader def generate_distributed_port_status(context, port_id): # an OR'ed value of status assigned to parent port from the # distributedportbinding bucket query = context.session.query(models.DistributedPortBinding.status) final_status = n_const.PORT_STATUS_BUILD for bind in query.filter(models.DistributedPortBinding.port_id == port_id): if bind.status == n_const.PORT_STATUS_ACTIVE: return bind.status elif bind.status == n_const.PORT_STATUS_DOWN: final_status = bind.status return final_status def get_distributed_port_binding_by_host(context, port_id, host): with db_api.context_manager.reader.using(context): binding = (context.session.query(models.DistributedPortBinding). filter(models.DistributedPortBinding.port_id.startswith(port_id), models.DistributedPortBinding.host == host).first()) if not binding: LOG.debug("No binding for distributed port %(port_id)s with host " "%(host)s", {'port_id': port_id, 'host': host}) return binding def get_distributed_port_bindings(context, port_id): with db_api.context_manager.reader.using(context): bindings = (context.session.query(models.DistributedPortBinding). filter(models.DistributedPortBinding.port_id.startswith( port_id)).all()) if not bindings: LOG.debug("No bindings for distributed port %s", port_id) return bindings @db_api.context_manager.reader def partial_port_ids_to_full_ids(context, partial_ids): """Takes a list of the start of port IDs and returns full IDs. Returns dictionary of partial IDs to full IDs if a single match is found. """ result = {} to_full_query = (context.session.query(models_v2.Port.id). filter(or_(*[models_v2.Port.id.startswith(p) for p in partial_ids]))) candidates = [match[0] for match in to_full_query] for partial_id in partial_ids: matching = [c for c in candidates if c.startswith(partial_id)] if len(matching) == 1: result[partial_id] = matching[0] continue if len(matching) < 1: LOG.info("No ports have port_id starting with %s", partial_id) elif len(matching) > 1: LOG.error("Multiple ports have port_id starting with %s", partial_id) return result @db_api.context_manager.reader def get_port_db_objects(context, port_ids): """Takes a list of port_ids and returns matching port db objects. return format is a dictionary keyed by passed in IDs with db objects for values or None if the port was not present. """ port_qry = (context.session.query(models_v2.Port). filter(models_v2.Port.id.in_(port_ids))) result = {p: None for p in port_ids} for port in port_qry: result[port.id] = port return result @db_api.context_manager.reader def is_dhcp_active_on_any_subnet(context, subnet_ids): if not subnet_ids: return False return bool(context.session.query(models_v2.Subnet.id). enable_eagerloads(False).filter_by(enable_dhcp=True). filter(models_v2.Subnet.id.in_(subnet_ids)).count()) def _prevent_segment_delete_with_port_bound(resource, event, trigger, context, segment, for_net_delete=False): """Raise exception if there are any ports bound with segment_id.""" if for_net_delete: # don't check for network deletes return with db_api.context_manager.reader.using(context): segment_id = segment['id'] query = context.session.query(models_v2.Port.id) query = query.join( models.PortBindingLevel, models.PortBindingLevel.port_id == models_v2.Port.id) query = query.filter(models.PortBindingLevel.segment_id == segment_id) port_ids = [p.id for p in query] # There are still some ports in the segment, segment should not be deleted # TODO(xiaohhui): Should we delete the dhcp port automatically here? if port_ids: reason = _("The segment is still bound with port(s) " "%s") % ", ".join(port_ids) raise seg_exc.SegmentInUse(segment_id=segment_id, reason=reason) def subscribe(): registry.subscribe(_prevent_segment_delete_with_port_bound, resources.SEGMENT, events.BEFORE_DELETE) subscribe() neutron-12.1.1/neutron/plugins/ml2/ovo_rpc.py0000664000175000017500000001430713553660047021214 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import traceback import eventlet from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import context as n_ctx from oslo_concurrency import lockutils from oslo_log import log as logging from neutron.api.rpc.callbacks import events as rpc_events from neutron.api.rpc.handlers import resources_rpc from neutron.db import api as db_api from neutron.objects import network from neutron.objects import ports from neutron.objects import securitygroup from neutron.objects import subnet LOG = logging.getLogger(__name__) class _ObjectChangeHandler(object): def __init__(self, resource, object_class, resource_push_api): self._resource = resource self._obj_class = object_class self._resource_push_api = resource_push_api self._resources_to_push = {} self._worker_pool = eventlet.GreenPool() self._semantic_warned = False for event in (events.AFTER_CREATE, events.AFTER_UPDATE, events.AFTER_DELETE): registry.subscribe(self.handle_event, resource, event) def wait(self): """Waits for all outstanding events to be dispatched.""" self._worker_pool.waitall() def _is_session_semantic_violated(self, context, resource, event): """Return True and print an ugly error on transaction violation. This code is to print ugly errors when AFTER_CREATE/UPDATE event transaction semantics are violated by other parts of the code. """ if not context.session.is_active: return False if not self._semantic_warned: stack = traceback.extract_stack() stack = "".join(traceback.format_list(stack)) LOG.warning("This handler is supposed to handle AFTER " "events, as in 'AFTER it's committed', " "not BEFORE. Offending resource event: " "%(r)s, %(e)s. Location:\n%(l)s", {'r': resource, 'e': event, 'l': stack}) self._semantic_warned = True return True def handle_event(self, resource, event, trigger, context, *args, **kwargs): """Callback handler for resource change that pushes change to RPC. We always retrieve the latest state and ignore what was in the payload to ensure that we don't get any stale data. """ if self._is_session_semantic_violated(context, resource, event): return resource_id = self._extract_resource_id(kwargs) # we preserve the context so we can trace a receive on the agent back # to the server-side event that triggered it self._resources_to_push[resource_id] = context.to_dict() # spawn worker so we don't block main AFTER_UPDATE thread self._worker_pool.spawn(self.dispatch_events) @lockutils.synchronized('event-dispatch') def dispatch_events(self): # this is guarded by a lock to ensure we don't get too many concurrent # dispatchers hitting the database simultaneously. to_dispatch, self._resources_to_push = self._resources_to_push, {} # TODO(kevinbenton): now that we are batching these, convert to a # single get_objects call for all of them for resource_id, context_dict in to_dispatch.items(): context = n_ctx.Context.from_dict(context_dict) # attempt to get regardless of event type so concurrent delete # after create/update is the same code-path as a delete event with db_api.context_manager.independent.reader.using(context): obj = self._obj_class.get_object(context, id=resource_id) # CREATE events are always treated as UPDATE events to ensure # listeners are written to handle out-of-order messages if obj is None: rpc_event = rpc_events.DELETED # construct a fake object with the right ID so we can # have a payload for the delete message. obj = self._obj_class(id=resource_id) else: rpc_event = rpc_events.UPDATED self._resource_push_api.push(context, [obj], rpc_event) def _extract_resource_id(self, callback_kwargs): id_kwarg = '%s_id' % self._resource if id_kwarg in callback_kwargs: return callback_kwargs[id_kwarg] if self._resource in callback_kwargs: return callback_kwargs[self._resource]['id'] raise RuntimeError("Couldn't find resource ID in callback event") class OVOServerRpcInterface(object): """ML2 server-side RPC interface. Generates RPC callback notifications on ML2 object changes. """ def __init__(self): self._rpc_pusher = resources_rpc.ResourcesPushRpcApi() self._setup_change_handlers() LOG.debug("ML2 OVO RPC backend initialized.") def _setup_change_handlers(self): """Setup all of the local callback listeners for resource changes.""" resource_objclass_map = { resources.PORT: ports.Port, resources.SUBNET: subnet.Subnet, resources.NETWORK: network.Network, resources.SECURITY_GROUP: securitygroup.SecurityGroup, resources.SECURITY_GROUP_RULE: securitygroup.SecurityGroupRule, } self._resource_handlers = { res: _ObjectChangeHandler(res, obj_class, self._rpc_pusher) for res, obj_class in resource_objclass_map.items() } def wait(self): """Wait for all handlers to finish processing async events.""" for handler in self._resource_handlers.values(): handler.wait() neutron-12.1.1/neutron/plugins/ml2/drivers/0000775000175000017500000000000013553660156020645 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/type_vlan.py0000664000175000017500000002653013553660047023225 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from neutron_lib import constants as p_const from neutron_lib import context from neutron_lib import exceptions as exc from neutron_lib.plugins.ml2 import api from oslo_config import cfg from oslo_log import log from six import moves from neutron._i18n import _ from neutron.conf.plugins.ml2.drivers import driver_type from neutron.db import api as db_api from neutron.objects.plugins.ml2 import vlanallocation as vlanalloc from neutron.plugins.common import utils as plugin_utils from neutron.plugins.ml2.drivers import helpers LOG = log.getLogger(__name__) driver_type.register_ml2_drivers_vlan_opts() class VlanTypeDriver(helpers.SegmentTypeDriver): """Manage state for VLAN networks with ML2. The VlanTypeDriver implements the 'vlan' network_type. VLAN network segments provide connectivity between VMs and other devices using any connected IEEE 802.1Q conformant physical_network segmented into virtual networks via IEEE 802.1Q headers. Up to 4094 VLAN network segments can exist on each available physical_network. """ def __init__(self): super(VlanTypeDriver, self).__init__(vlanalloc.VlanAllocation) self._parse_network_vlan_ranges() def _parse_network_vlan_ranges(self): try: self.network_vlan_ranges = plugin_utils.parse_network_vlan_ranges( cfg.CONF.ml2_type_vlan.network_vlan_ranges) except Exception: LOG.exception("Failed to parse network_vlan_ranges. " "Service terminated!") sys.exit(1) LOG.info("Network VLAN ranges: %s", self.network_vlan_ranges) @db_api.retry_db_errors def _sync_vlan_allocations(self): ctx = context.get_admin_context() with db_api.context_manager.writer.using(ctx): # get existing allocations for all physical networks allocations = dict() allocs = vlanalloc.VlanAllocation.get_objects(ctx) for alloc in allocs: if alloc.physical_network not in allocations: allocations[alloc.physical_network] = list() allocations[alloc.physical_network].append(alloc) # process vlan ranges for each configured physical network for (physical_network, vlan_ranges) in self.network_vlan_ranges.items(): # determine current configured allocatable vlans for # this physical network vlan_ids = set() for vlan_min, vlan_max in vlan_ranges: vlan_ids |= set(moves.range(vlan_min, vlan_max + 1)) # remove from table unallocated vlans not currently # allocatable if physical_network in allocations: for alloc in allocations[physical_network]: try: # see if vlan is allocatable vlan_ids.remove(alloc.vlan_id) except KeyError: # it's not allocatable, so check if its allocated if not alloc.allocated: # it's not, so remove it from table LOG.debug("Removing vlan %(vlan_id)s on " "physical network " "%(physical_network)s from pool", {'vlan_id': alloc.vlan_id, 'physical_network': physical_network}) # This UPDATE WHERE statement blocks anyone # from concurrently changing the allocation # values to True while our transaction is # open so we don't accidentally delete # allocated segments. If someone has already # allocated, update_objects will return 0 so we # don't delete. if vlanalloc.VlanAllocation.update_objects( ctx, values={'allocated': False}, allocated=False, vlan_id=alloc.vlan_id, physical_network=physical_network): alloc.delete() del allocations[physical_network] # add missing allocatable vlans to table for vlan_id in sorted(vlan_ids): alloc = vlanalloc.VlanAllocation( ctx, physical_network=physical_network, vlan_id=vlan_id, allocated=False) alloc.create() # remove from table unallocated vlans for any unconfigured # physical networks for allocs in allocations.values(): for alloc in allocs: if not alloc.allocated: LOG.debug("Removing vlan %(vlan_id)s on physical " "network %(physical_network)s from pool", {'vlan_id': alloc.vlan_id, 'physical_network': alloc.physical_network}) alloc.delete() def get_type(self): return p_const.TYPE_VLAN def initialize(self): self._sync_vlan_allocations() LOG.info("VlanTypeDriver initialization complete") def is_partial_segment(self, segment): return segment.get(api.SEGMENTATION_ID) is None def validate_provider_segment(self, segment): physical_network = segment.get(api.PHYSICAL_NETWORK) segmentation_id = segment.get(api.SEGMENTATION_ID) if physical_network: if physical_network not in self.network_vlan_ranges: msg = (_("physical_network '%s' unknown " "for VLAN provider network") % physical_network) raise exc.InvalidInput(error_message=msg) if segmentation_id is not None: if not plugin_utils.is_valid_vlan_tag(segmentation_id): msg = (_("segmentation_id out of range (%(min)s through " "%(max)s)") % {'min': p_const.MIN_VLAN_TAG, 'max': p_const.MAX_VLAN_TAG}) raise exc.InvalidInput(error_message=msg) else: if not self.network_vlan_ranges.get(physical_network): msg = (_("Physical network %s requires segmentation_id " "to be specified when creating a provider " "network") % physical_network) raise exc.InvalidInput(error_message=msg) elif segmentation_id is not None: msg = _("segmentation_id requires physical_network for VLAN " "provider network") raise exc.InvalidInput(error_message=msg) for key, value in segment.items(): if value and key not in [api.NETWORK_TYPE, api.PHYSICAL_NETWORK, api.SEGMENTATION_ID]: msg = _("%s prohibited for VLAN provider network") % key raise exc.InvalidInput(error_message=msg) def reserve_provider_segment(self, context, segment): filters = {} physical_network = segment.get(api.PHYSICAL_NETWORK) if physical_network is not None: filters['physical_network'] = physical_network vlan_id = segment.get(api.SEGMENTATION_ID) if vlan_id is not None: filters['vlan_id'] = vlan_id if self.is_partial_segment(segment): alloc = self.allocate_partially_specified_segment( context, **filters) if not alloc: raise exc.NoNetworkAvailable() else: alloc = self.allocate_fully_specified_segment( context, **filters) if not alloc: raise exc.VlanIdInUse(**filters) return {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: alloc.physical_network, api.SEGMENTATION_ID: alloc.vlan_id, api.MTU: self.get_mtu(alloc.physical_network)} def allocate_tenant_segment(self, context): for physnet in self.network_vlan_ranges: alloc = self.allocate_partially_specified_segment( context, physical_network=physnet) if alloc: break else: return return {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: alloc.physical_network, api.SEGMENTATION_ID: alloc.vlan_id, api.MTU: self.get_mtu(alloc.physical_network)} def release_segment(self, context, segment): physical_network = segment[api.PHYSICAL_NETWORK] vlan_id = segment[api.SEGMENTATION_ID] ranges = self.network_vlan_ranges.get(physical_network, []) inside = any(lo <= vlan_id <= hi for lo, hi in ranges) count = False with db_api.context_manager.writer.using(context): alloc = vlanalloc.VlanAllocation.get_object( context, physical_network=physical_network, vlan_id=vlan_id) if alloc: if inside and alloc.allocated: count = True alloc.allocated = False alloc.update() LOG.debug("Releasing vlan %(vlan_id)s on physical " "network %(physical_network)s to pool", {'vlan_id': vlan_id, 'physical_network': physical_network}) else: count = True alloc.delete() LOG.debug("Releasing vlan %(vlan_id)s on physical " "network %(physical_network)s outside pool", {'vlan_id': vlan_id, 'physical_network': physical_network}) if not count: LOG.warning("No vlan_id %(vlan_id)s found on physical " "network %(physical_network)s", {'vlan_id': vlan_id, 'physical_network': physical_network}) def get_mtu(self, physical_network): seg_mtu = super(VlanTypeDriver, self).get_mtu() mtu = [] if seg_mtu > 0: mtu.append(seg_mtu) if physical_network in self.physnet_mtus: mtu.append(int(self.physnet_mtus[physical_network])) return min(mtu) if mtu else 0 neutron-12.1.1/neutron/plugins/ml2/drivers/type_gre.py0000664000175000017500000000410213553660047023031 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as p_const from neutron_lib import exceptions as n_exc from oslo_config import cfg from oslo_log import log from neutron.conf.plugins.ml2.drivers import driver_type from neutron.objects.plugins.ml2 import greallocation as gre_obj from neutron.plugins.ml2.drivers import type_tunnel LOG = log.getLogger(__name__) driver_type.register_ml2_drivers_gre_opts() class GreTypeDriver(type_tunnel.EndpointTunnelTypeDriver): def __init__(self): super(GreTypeDriver, self).__init__( gre_obj.GreAllocation, gre_obj.GreEndpoint) def get_type(self): return p_const.TYPE_GRE def initialize(self): try: self._initialize(cfg.CONF.ml2_type_gre.tunnel_id_ranges) except n_exc.NetworkTunnelRangeError: LOG.exception("Failed to parse tunnel_id_ranges. " "Service terminated!") raise SystemExit() def get_endpoints(self): """Get every gre endpoints from database.""" gre_endpoints = self._get_endpoints() return [{'ip_address': gre_endpoint.ip_address, 'host': gre_endpoint.host} for gre_endpoint in gre_endpoints] def add_endpoint(self, ip, host): return self._add_endpoint(ip, host) def get_mtu(self, physical_network=None): mtu = super(GreTypeDriver, self).get_mtu(physical_network) return mtu - p_const.GRE_ENCAP_OVERHEAD if mtu else 0 neutron-12.1.1/neutron/plugins/ml2/drivers/mech_agent.py0000664000175000017500000002620213553660047023312 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import resources from neutron_lib import constants as const from neutron_lib.plugins.ml2 import api from oslo_log import log import six from neutron.db import provisioning_blocks LOG = log.getLogger(__name__) @six.add_metaclass(abc.ABCMeta) class AgentMechanismDriverBase(api.MechanismDriver): """Base class for drivers that attach to networks using an L2 agent. The AgentMechanismDriverBase provides common code for mechanism drivers that integrate the ml2 plugin with L2 agents. Port binding with this driver requires the driver's associated agent to be running on the port's host, and that agent to have connectivity to at least one segment of the port's network. MechanismDrivers using this base class must pass the agent type to __init__(), and must implement try_to_bind_segment_for_agent(). """ def __init__(self, agent_type, supported_vnic_types=[portbindings.VNIC_NORMAL]): """Initialize base class for specific L2 agent type. :param agent_type: Constant identifying agent type in agents_db :param supported_vnic_types: The binding:vnic_type values we can bind """ self.agent_type = agent_type self.supported_vnic_types = supported_vnic_types def initialize(self): pass def create_port_precommit(self, context): self._insert_provisioning_block(context) def update_port_precommit(self, context): if context.host == context.original_host: return self._insert_provisioning_block(context) def _insert_provisioning_block(self, context): # we insert a status barrier to prevent the port from transitioning # to active until the agent reports back that the wiring is done port = context.current if not context.host or port['status'] == const.PORT_STATUS_ACTIVE: # no point in putting in a block if the status is already ACTIVE return vnic_type = context.current.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL) if vnic_type not in self.supported_vnic_types: # we check the VNIC type because there could be multiple agents # on a single host with different VNIC types return if context.host_agents(self.agent_type): provisioning_blocks.add_provisioning_component( context._plugin_context, port['id'], resources.PORT, provisioning_blocks.L2_AGENT_ENTITY) def bind_port(self, context): LOG.debug("Attempting to bind port %(port)s on " "network %(network)s", {'port': context.current['id'], 'network': context.network.current['id']}) vnic_type = context.current.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL) if vnic_type not in self.supported_vnic_types: LOG.debug("Refusing to bind due to unsupported vnic_type: %s", vnic_type) return agents = context.host_agents(self.agent_type) if not agents: LOG.debug("Port %(pid)s on network %(network)s not bound, " "no agent of type %(at)s registered on host %(host)s", {'pid': context.current['id'], 'at': self.agent_type, 'network': context.network.current['id'], 'host': context.host}) for agent in agents: LOG.debug("Checking agent: %s", agent) if agent['alive']: for segment in context.segments_to_bind: if self.try_to_bind_segment_for_agent(context, segment, agent): LOG.debug("Bound using segment: %s", segment) return else: LOG.warning("Refusing to bind port %(pid)s to dead agent: " "%(agent)s", {'pid': context.current['id'], 'agent': agent}) @abc.abstractmethod def try_to_bind_segment_for_agent(self, context, segment, agent): """Try to bind with segment for agent. :param context: PortContext instance describing the port :param segment: segment dictionary describing segment to bind :param agent: agents_db entry describing agent to bind :returns: True iff segment has been bound for agent Called outside any transaction during bind_port() so that derived MechanismDrivers can use agent_db data along with built-in knowledge of the corresponding agent's capabilities to attempt to bind to the specified network segment for the agent. If the segment can be bound for the agent, this function must call context.set_binding() with appropriate values and then return True. Otherwise, it must return False. """ @six.add_metaclass(abc.ABCMeta) class SimpleAgentMechanismDriverBase(AgentMechanismDriverBase): """Base class for simple drivers using an L2 agent. The SimpleAgentMechanismDriverBase provides common code for mechanism drivers that integrate the ml2 plugin with L2 agents, where the binding:vif_type and binding:vif_details values are the same for all bindings. Port binding with this driver requires the driver's associated agent to be running on the port's host, and that agent to have connectivity to at least one segment of the port's network. MechanismDrivers using this base class must pass the agent type and the values for binding:vif_type and binding:vif_details to __init__(), and must implement check_segment_for_agent(). """ def __init__(self, agent_type, vif_type, vif_details, supported_vnic_types=[portbindings.VNIC_NORMAL]): """Initialize base class for specific L2 agent type. :param agent_type: Constant identifying agent type in agents_db :param vif_type: Value for binding:vif_type when bound :param vif_details: Dictionary with details for VIF driver when bound :param supported_vnic_types: The binding:vnic_type values we can bind """ super(SimpleAgentMechanismDriverBase, self).__init__( agent_type, supported_vnic_types) self.vif_type = vif_type self.vif_details = vif_details def try_to_bind_segment_for_agent(self, context, segment, agent): if self.check_segment_for_agent(segment, agent): context.set_binding(segment[api.ID], self.get_vif_type(context, agent, segment), self.get_vif_details(context, agent, segment)) return True else: return False def get_vif_details(self, context, agent, segment): return self.vif_details def get_vif_type(self, context, agent, segment): """Return the vif type appropriate for the agent and segment.""" return self.vif_type @abc.abstractmethod def get_allowed_network_types(self, agent=None): """Return the agent's or driver's allowed network types. For example: return ('flat', ...). You can also refer to the configuration the given agent exposes. """ pass @abc.abstractmethod def get_mappings(self, agent): """Return the agent's bridge or interface mappings. For example: agent['configurations'].get('bridge_mappings', {}). """ pass def physnet_in_mappings(self, physnet, mappings): """Is the physical network part of the given mappings?""" return physnet in mappings def filter_hosts_with_segment_access( self, context, segments, candidate_hosts, agent_getter): hosts = set() filters = {'host': candidate_hosts, 'agent_type': [self.agent_type]} for agent in agent_getter(context, filters=filters): if any(self.check_segment_for_agent(s, agent) for s in segments): hosts.add(agent['host']) return hosts def check_segment_for_agent(self, segment, agent): """Check if segment can be bound for agent. :param segment: segment dictionary describing segment to bind :param agent: agents_db entry describing agent to bind :returns: True iff segment can be bound for agent Called outside any transaction during bind_port so that derived MechanismDrivers can use agent_db data along with built-in knowledge of the corresponding agent's capabilities to determine whether or not the specified network segment can be bound for the agent. """ mappings = self.get_mappings(agent) allowed_network_types = self.get_allowed_network_types(agent) LOG.debug("Checking segment: %(segment)s " "for mappings: %(mappings)s " "with network types: %(network_types)s", {'segment': segment, 'mappings': mappings, 'network_types': allowed_network_types}) network_type = segment[api.NETWORK_TYPE] if network_type not in allowed_network_types: LOG.debug( 'Network %(network_id)s with segment %(id)s is type ' 'of %(network_type)s but agent %(agent)s or mechanism driver ' 'only support %(allowed_network_types)s.', {'network_id': segment['network_id'], 'id': segment['id'], 'network_type': network_type, 'agent': agent['host'], 'allowed_network_types': allowed_network_types}) return False if network_type in [const.TYPE_FLAT, const.TYPE_VLAN]: physnet = segment[api.PHYSICAL_NETWORK] if not self.physnet_in_mappings(physnet, mappings): LOG.debug( 'Network %(network_id)s with segment %(id)s is connected ' 'to physical network %(physnet)s, but agent %(agent)s ' 'reported physical networks %(mappings)s. ' 'The physical network must be configured on the ' 'agent if binding is to succeed.', {'network_id': segment['network_id'], 'id': segment['id'], 'physnet': physnet, 'agent': agent['host'], 'mappings': mappings}) return False return True neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/0000775000175000017500000000000013553660156023161 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/0000775000175000017500000000000013553660156024257 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_capabilities.py0000664000175000017500000000171613553660046031661 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from neutron_lib import constants from neutron.plugins.ml2.drivers.agent import capabilities from neutron.services.trunk.drivers.linuxbridge.agent import driver def register(): """Register Linux Bridge capabilities.""" # Add capabilities to be loaded during agent initialization capabilities.register(driver.init_handler, constants.AGENT_TYPE_LINUXBRIDGE) neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_agent_extension_api.py0000664000175000017500000000231613553660046033250 0ustar zuulzuul00000000000000# Copyright 2017 OVH SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. class LinuxbridgeAgentExtensionAPI(object): '''Implements the Agent API for L2 agent. Extensions can gain access to this API by overriding the consume_api method which has been added to the AgentExtension class. ''' def __init__(self, iptables_manager): super(LinuxbridgeAgentExtensionAPI, self).__init__() self.iptables_manager = iptables_manager def get_iptables_manager(self): """Allows extensions to get an iptables manager, used by agent, to use for managing extension specific iptables rules """ return self.iptables_manager neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/__init__.py0000664000175000017500000000000013553660046026354 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py0000664000175000017500000012776213553660047032113 0ustar zuulzuul00000000000000#!/usr/bin/env python # Copyright 2012 Cisco Systems, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # # Performs per host Linux Bridge configuration for Neutron. # Based on the structure of the OpenVSwitch agent in the # Neutron OpenVSwitch Plugin. import sys import netaddr from neutron_lib import constants from neutron_lib.utils import helpers from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_service import service from oslo_utils import excutils from six import moves from neutron.agent.linux import bridge_lib from neutron.agent.linux import ip_lib from neutron.api.rpc.handlers import securitygroups_rpc as sg_rpc from neutron.common import config as common_config from neutron.common import exceptions from neutron.common import profiler as setup_profiler from neutron.common import topics from neutron.common import utils from neutron.conf.agent import common as agent_config from neutron.plugins.common import utils as p_utils from neutron.plugins.ml2.drivers.agent import _agent_manager_base as amb from neutron.plugins.ml2.drivers.agent import _common_agent as ca from neutron.plugins.ml2.drivers.agent import config as cagt_config # noqa from neutron.plugins.ml2.drivers.l2pop.rpc_manager \ import l2population_rpc as l2pop_rpc from neutron.plugins.ml2.drivers.linuxbridge.agent import arp_protect from neutron.plugins.ml2.drivers.linuxbridge.agent.common import config # noqa from neutron.plugins.ml2.drivers.linuxbridge.agent.common \ import constants as lconst from neutron.plugins.ml2.drivers.linuxbridge.agent.common \ import utils as lb_utils from neutron.plugins.ml2.drivers.linuxbridge.agent import \ linuxbridge_agent_extension_api as agent_extension_api from neutron.plugins.ml2.drivers.linuxbridge.agent \ import linuxbridge_capabilities LOG = logging.getLogger(__name__) LB_AGENT_BINARY = 'neutron-linuxbridge-agent' BRIDGE_NAME_PREFIX = "brq" MAX_VLAN_POSTFIX_LEN = 5 VXLAN_INTERFACE_PREFIX = "vxlan-" IPTABLES_DRIVERS = [ 'iptables', 'iptables_hybrid', 'neutron.agent.linux.iptables_firewall.IptablesFirewallDriver', 'neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver' ] class LinuxBridgeManager(amb.CommonAgentManagerBase): def __init__(self, bridge_mappings, interface_mappings): super(LinuxBridgeManager, self).__init__() self.bridge_mappings = bridge_mappings self.interface_mappings = interface_mappings self.validate_interface_mappings() self.validate_bridge_mappings() self.ip = ip_lib.IPWrapper() self.agent_api = None # VXLAN related parameters: self.local_ip = cfg.CONF.VXLAN.local_ip self.vxlan_mode = lconst.VXLAN_NONE if cfg.CONF.VXLAN.enable_vxlan: device = self.get_local_ip_device() self.validate_vxlan_group_with_local_ip() self.local_int = device.name self.check_vxlan_support() def validate_interface_mappings(self): for physnet, interface in self.interface_mappings.items(): if not ip_lib.device_exists(interface): LOG.error("Interface %(intf)s for physical network %(net)s" " does not exist. Agent terminated!", {'intf': interface, 'net': physnet}) sys.exit(1) def validate_bridge_mappings(self): for physnet, bridge in self.bridge_mappings.items(): if not ip_lib.device_exists(bridge): LOG.error("Bridge %(brq)s for physical network %(net)s" " does not exist. Agent terminated!", {'brq': bridge, 'net': physnet}) sys.exit(1) def _is_valid_multicast_range(self, mrange): try: addr, vxlan_min, vxlan_max = mrange.split(':') if int(vxlan_min) > int(vxlan_max): raise ValueError() try: local_ver = netaddr.IPAddress(self.local_ip).version n_addr = netaddr.IPAddress(addr) if not n_addr.is_multicast() or n_addr.version != local_ver: raise ValueError() except netaddr.core.AddrFormatError: raise ValueError() except ValueError: return False return True def validate_vxlan_group_with_local_ip(self): for r in cfg.CONF.VXLAN.multicast_ranges: if not self._is_valid_multicast_range(r): LOG.error("Invalid multicast_range %(r)s. Must be in " ":: format and " "addresses must be in the same family as local IP " "%(loc)s.", {'r': r, 'loc': self.local_ip}) sys.exit(1) if not cfg.CONF.VXLAN.vxlan_group: return try: ip_addr = netaddr.IPAddress(self.local_ip) # Ensure the configured group address/range is valid and multicast group_net = netaddr.IPNetwork(cfg.CONF.VXLAN.vxlan_group) if not group_net.is_multicast(): raise ValueError() if not ip_addr.version == group_net.version: raise ValueError() except (netaddr.core.AddrFormatError, ValueError): LOG.error("Invalid VXLAN Group: %(group)s, must be an address " "or network (in CIDR notation) in a multicast " "range of the same address family as local_ip: " "%(ip)s", {'group': cfg.CONF.VXLAN.vxlan_group, 'ip': self.local_ip}) sys.exit(1) def get_local_ip_device(self): """Return the device with local_ip on the host.""" device = self.ip.get_device_by_ip(self.local_ip) if not device: LOG.error("Tunneling cannot be enabled without the local_ip " "bound to an interface on the host. Please " "configure local_ip %s on the host interface to " "be used for tunneling and restart the agent.", self.local_ip) sys.exit(1) return device @staticmethod def get_bridge_name(network_id): if not network_id: LOG.warning("Invalid Network ID, will lead to incorrect " "bridge name") bridge_name = BRIDGE_NAME_PREFIX + \ network_id[:lconst.RESOURCE_ID_LENGTH] return bridge_name def get_subinterface_name(self, physical_interface, vlan_id): if not vlan_id: LOG.warning("Invalid VLAN ID, will lead to incorrect " "subinterface name") vlan_postfix = '.%s' % vlan_id # For the vlan subinterface name prefix we use: # * the physical_interface, if len(physical_interface) + # len(vlan_postifx) <= 15 for backward compatibility reasons # Example: physical_interface = eth0 # prefix = eth0.1 # prefix = eth0.1111 # # * otherwise a unique hash per physical_interface to help debugging # Example: physical_interface = long_interface # prefix = longHASHED.1 # prefix = longHASHED.1111 # # Remark: For some physical_interface values, the used prefix can be # both, the physical_interface itself or a hash, depending # on the vlan_postfix length. # Example: physical_interface = mix_interface # prefix = mix_interface.1 (backward compatible) # prefix = mix_iHASHED.1111 if (len(physical_interface) + len(vlan_postfix) > constants.DEVICE_NAME_MAX_LEN): physical_interface = p_utils.get_interface_name( physical_interface, max_len=(constants.DEVICE_NAME_MAX_LEN - MAX_VLAN_POSTFIX_LEN)) return "%s%s" % (physical_interface, vlan_postfix) @staticmethod def get_tap_device_name(interface_id): return lb_utils.get_tap_device_name(interface_id) def get_vxlan_device_name(self, segmentation_id): if 0 <= int(segmentation_id) <= constants.MAX_VXLAN_VNI: return VXLAN_INTERFACE_PREFIX + str(segmentation_id) else: LOG.warning("Invalid Segmentation ID: %s, will lead to " "incorrect vxlan device name", segmentation_id) @staticmethod def _match_multicast_range(segmentation_id): for mrange in cfg.CONF.VXLAN.multicast_ranges: addr, vxlan_min, vxlan_max = mrange.split(':') if int(vxlan_min) <= segmentation_id <= int(vxlan_max): return addr def get_vxlan_group(self, segmentation_id): mcast_addr = self._match_multicast_range(segmentation_id) if mcast_addr: net = netaddr.IPNetwork(mcast_addr) else: net = netaddr.IPNetwork(cfg.CONF.VXLAN.vxlan_group) # Map the segmentation ID to (one of) the group address(es) return str(net.network + (int(segmentation_id) & int(net.hostmask))) def get_deletable_bridges(self): bridge_list = bridge_lib.get_bridge_names() bridges = {b for b in bridge_list if b.startswith(BRIDGE_NAME_PREFIX)} bridges.difference_update(self.bridge_mappings.values()) return bridges def get_tap_devices_count(self, bridge_name): if_list = bridge_lib.BridgeDevice(bridge_name).get_interfaces() return len([interface for interface in if_list if interface.startswith(constants.TAP_DEVICE_PREFIX)]) def ensure_vlan_bridge(self, network_id, phy_bridge_name, physical_interface, vlan_id): """Create a vlan and bridge unless they already exist.""" interface = self.ensure_vlan(physical_interface, vlan_id) if phy_bridge_name: return self.ensure_bridge(phy_bridge_name) else: bridge_name = self.get_bridge_name(network_id) if self.ensure_bridge(bridge_name, interface): return interface def ensure_vxlan_bridge(self, network_id, segmentation_id): """Create a vxlan and bridge unless they already exist.""" interface = self.ensure_vxlan(segmentation_id) if not interface: LOG.error("Failed creating vxlan interface for " "%(segmentation_id)s", {segmentation_id: segmentation_id}) return bridge_name = self.get_bridge_name(network_id) self.ensure_bridge(bridge_name, interface, update_interface=False) return interface def get_interface_details(self, interface, ip_version): device = self.ip.device(interface) ips = device.addr.list(scope='global', ip_version=ip_version) # Update default gateway if necessary gateway = device.route.get_gateway(scope='global', ip_version=ip_version) return ips, gateway def ensure_flat_bridge(self, network_id, phy_bridge_name, physical_interface): """Create a non-vlan bridge unless it already exists.""" if phy_bridge_name: return self.ensure_bridge(phy_bridge_name) else: bridge_name = self.get_bridge_name(network_id) if self.ensure_bridge(bridge_name, physical_interface): return physical_interface def ensure_local_bridge(self, network_id, phy_bridge_name): """Create a local bridge unless it already exists.""" if phy_bridge_name: bridge_name = phy_bridge_name else: bridge_name = self.get_bridge_name(network_id) return self.ensure_bridge(bridge_name) def ensure_vlan(self, physical_interface, vlan_id): """Create a vlan unless it already exists.""" interface = self.get_subinterface_name(physical_interface, vlan_id) if not ip_lib.device_exists(interface): LOG.debug("Creating subinterface %(interface)s for " "VLAN %(vlan_id)s on interface " "%(physical_interface)s", {'interface': interface, 'vlan_id': vlan_id, 'physical_interface': physical_interface}) try: int_vlan = self.ip.add_vlan(interface, physical_interface, vlan_id) except RuntimeError: with excutils.save_and_reraise_exception() as ctxt: if ip_lib.vlan_in_use(vlan_id): ctxt.reraise = False LOG.error("Unable to create VLAN interface for " "VLAN ID %s because it is in use by " "another interface.", vlan_id) return int_vlan.disable_ipv6() int_vlan.link.set_up() LOG.debug("Done creating subinterface %s", interface) return interface def ensure_vxlan(self, segmentation_id): """Create a vxlan unless it already exists.""" interface = self.get_vxlan_device_name(segmentation_id) if not ip_lib.device_exists(interface): LOG.debug("Creating vxlan interface %(interface)s for " "VNI %(segmentation_id)s", {'interface': interface, 'segmentation_id': segmentation_id}) args = {'dev': self.local_int, 'srcport': (cfg.CONF.VXLAN.udp_srcport_min, cfg.CONF.VXLAN.udp_srcport_max), 'dstport': cfg.CONF.VXLAN.udp_dstport, 'ttl': cfg.CONF.VXLAN.ttl} if cfg.CONF.VXLAN.tos: args['tos'] = cfg.CONF.VXLAN.tos if cfg.CONF.AGENT.dscp or cfg.CONF.AGENT.dscp_inherit: LOG.warning('The deprecated tos option in group VXLAN ' 'is set and takes precedence over dscp and ' 'dscp_inherit in group AGENT.') elif cfg.CONF.AGENT.dscp_inherit: args['tos'] = 'inherit' elif cfg.CONF.AGENT.dscp: args['tos'] = int(cfg.CONF.AGENT.dscp) << 2 if self.vxlan_mode == lconst.VXLAN_MCAST: args['group'] = self.get_vxlan_group(segmentation_id) if cfg.CONF.VXLAN.l2_population: args['proxy'] = cfg.CONF.VXLAN.arp_responder try: int_vxlan = self.ip.add_vxlan(interface, segmentation_id, **args) except RuntimeError: with excutils.save_and_reraise_exception() as ctxt: # perform this check after an attempt rather than before # to avoid excessive lookups and a possible race condition. if ip_lib.vxlan_in_use(segmentation_id): ctxt.reraise = False LOG.error("Unable to create VXLAN interface for " "VNI %s because it is in use by another " "interface.", segmentation_id) return None int_vxlan.disable_ipv6() int_vxlan.link.set_up() LOG.debug("Done creating vxlan interface %s", interface) return interface def _update_interface_ip_details(self, destination, source, ips, gateway): dst_device = self.ip.device(destination) src_device = self.ip.device(source) # Append IP's to bridge if necessary if ips: for ip in ips: # If bridge ip address already exists, then don't add # otherwise will report error to = utils.cidr_to_ip(ip['cidr']) if not dst_device.addr.list(to=to): dst_device.addr.add(cidr=ip['cidr']) if gateway: # Ensure that the gateway can be updated by changing the metric metric = 100 if 'metric' in gateway: metric = gateway['metric'] - 1 dst_device.route.add_gateway(gateway=gateway['gateway'], metric=metric) src_device.route.delete_gateway(gateway=gateway['gateway']) # Remove IP's from interface if ips: for ip in ips: src_device.addr.delete(cidr=ip['cidr']) def update_interface_ip_details(self, destination, source): # Returns True if there were IPs or a gateway moved updated = False for ip_version in (constants.IP_VERSION_4, constants.IP_VERSION_6): ips, gateway = self.get_interface_details(source, ip_version) if ips or gateway: self._update_interface_ip_details(destination, source, ips, gateway) updated = True return updated def _bridge_exists_and_ensure_up(self, bridge_name): """Check if the bridge exists and make sure it is up.""" br = ip_lib.IPDevice(bridge_name) br.set_log_fail_as_error(False) try: # If the device doesn't exist this will throw a RuntimeError br.link.set_up() except RuntimeError: return False return True def ensure_bridge(self, bridge_name, interface=None, update_interface=True): """Create a bridge unless it already exists.""" # _bridge_exists_and_ensure_up instead of device_exists is used here # because there are cases where the bridge exists but it's not UP, # for example: # 1) A greenthread was executing this function and had not yet executed # "ip link set bridge_name up" before eventlet switched to this # thread running the same function # 2) The Nova VIF driver was running concurrently and had just created # the bridge, but had not yet put it UP if not self._bridge_exists_and_ensure_up(bridge_name): LOG.debug("Starting bridge %(bridge_name)s for subinterface " "%(interface)s", {'bridge_name': bridge_name, 'interface': interface}) bridge_device = bridge_lib.BridgeDevice.addbr(bridge_name) if bridge_device.setfd(0): return if bridge_device.disable_stp(): return if bridge_device.link.set_up(): return LOG.debug("Done starting bridge %(bridge_name)s for " "subinterface %(interface)s", {'bridge_name': bridge_name, 'interface': interface}) else: bridge_device = bridge_lib.BridgeDevice(bridge_name) if not interface: return bridge_name # Update IP info if necessary if update_interface: self.update_interface_ip_details(bridge_name, interface) # Check if the interface is part of the bridge if not bridge_device.owns_interface(interface): try: # Check if the interface is not enslaved in another bridge bridge = bridge_lib.BridgeDevice.get_interface_bridge( interface) if bridge: bridge.delif(interface) bridge_device.addif(interface) except Exception as e: LOG.error("Unable to add %(interface)s to %(bridge_name)s" "! Exception: %(e)s", {'interface': interface, 'bridge_name': bridge_name, 'e': e}) return return bridge_name def ensure_physical_in_bridge(self, network_id, network_type, physical_network, segmentation_id): if network_type == constants.TYPE_VXLAN: if self.vxlan_mode == lconst.VXLAN_NONE: LOG.error("Unable to add vxlan interface for network %s", network_id) return return self.ensure_vxlan_bridge(network_id, segmentation_id) # NOTE(nick-ma-z): Obtain mappings of physical bridge and interfaces physical_bridge = self.bridge_mappings.get(physical_network) physical_interface = self.interface_mappings.get(physical_network) if not physical_bridge and not physical_interface: LOG.error("No bridge or interface mappings" " for physical network %s", physical_network) return if network_type == constants.TYPE_FLAT: return self.ensure_flat_bridge(network_id, physical_bridge, physical_interface) elif network_type == constants.TYPE_VLAN: return self.ensure_vlan_bridge(network_id, physical_bridge, physical_interface, segmentation_id) else: LOG.error("Unknown network_type %(network_type)s for network " "%(network_id)s.", {network_type: network_type, network_id: network_id}) def add_tap_interface(self, network_id, network_type, physical_network, segmentation_id, tap_device_name, device_owner, mtu): """Add tap interface and handle interface missing exceptions.""" try: return self._add_tap_interface(network_id, network_type, physical_network, segmentation_id, tap_device_name, device_owner, mtu) except Exception: with excutils.save_and_reraise_exception() as ctx: if not ip_lib.device_exists(tap_device_name): # the exception was likely a side effect of the tap device # being removed during handling so we just return false # like we would if it didn't exist to begin with. ctx.reraise = False return False def _add_tap_interface(self, network_id, network_type, physical_network, segmentation_id, tap_device_name, device_owner, mtu): """Add tap interface. If a VIF has been plugged into a network, this function will add the corresponding tap device to the relevant bridge. """ if not ip_lib.device_exists(tap_device_name): LOG.debug("Tap device: %s does not exist on " "this host, skipped", tap_device_name) return False bridge_name = self.bridge_mappings.get(physical_network) if not bridge_name: bridge_name = self.get_bridge_name(network_id) if network_type == constants.TYPE_LOCAL: self.ensure_local_bridge(network_id, bridge_name) elif not self.ensure_physical_in_bridge(network_id, network_type, physical_network, segmentation_id): return False if mtu: # <-None with device_details from older neutron servers. # we ensure the MTU here because libvirt does not set the # MTU of a bridge it creates and the tap device it creates will # inherit from the bridge its plugged into, which will be 1500 # at the time. See bug/1684326 for details. self._set_tap_mtu(tap_device_name, mtu) # Avoid messing with plugging devices into a bridge that the agent # does not own if not device_owner.startswith(constants.DEVICE_OWNER_COMPUTE_PREFIX): # Check if device needs to be added to bridge if not bridge_lib.BridgeDevice.get_interface_bridge( tap_device_name): data = {'tap_device_name': tap_device_name, 'bridge_name': bridge_name} LOG.debug("Adding device %(tap_device_name)s to bridge " "%(bridge_name)s", data) if bridge_lib.BridgeDevice(bridge_name).addif(tap_device_name): return False else: data = {'tap_device_name': tap_device_name, 'device_owner': device_owner, 'bridge_name': bridge_name} LOG.debug("Skip adding device %(tap_device_name)s to " "%(bridge_name)s. It is owned by %(device_owner)s and " "thus added elsewhere.", data) return True def _set_tap_mtu(self, tap_device_name, mtu): ip_lib.IPDevice(tap_device_name).link.set_mtu(mtu) def plug_interface(self, network_id, network_segment, tap_name, device_owner): return self.add_tap_interface(network_id, network_segment.network_type, network_segment.physical_network, network_segment.segmentation_id, tap_name, device_owner, network_segment.mtu) def delete_bridge(self, bridge_name): bridge_device = bridge_lib.BridgeDevice(bridge_name) if bridge_device.exists(): physical_interfaces = set(self.interface_mappings.values()) interfaces_on_bridge = bridge_device.get_interfaces() for interface in interfaces_on_bridge: self.remove_interface(bridge_name, interface) if interface.startswith(VXLAN_INTERFACE_PREFIX): self.delete_interface(interface) else: # Match the vlan/flat interface in the bridge. # If the bridge has an IP, it mean that this IP was moved # from the current interface, which also mean that this # interface was not created by the agent. updated = self.update_interface_ip_details(interface, bridge_name) if not updated and interface not in physical_interfaces: self.delete_interface(interface) try: LOG.debug("Deleting bridge %s", bridge_name) if bridge_device.link.set_down(): return if bridge_device.delbr(): return LOG.debug("Done deleting bridge %s", bridge_name) except RuntimeError: with excutils.save_and_reraise_exception() as ctxt: if not bridge_device.exists(): # the exception was likely a side effect of the bridge # being removed by nova during handling, # so we just return ctxt.reraise = False LOG.debug("Cannot delete bridge %s; it does not exist", bridge_name) return else: LOG.debug("Cannot delete bridge %s; it does not exist", bridge_name) def remove_interface(self, bridge_name, interface_name): bridge_device = bridge_lib.BridgeDevice(bridge_name) if bridge_device.exists(): if not bridge_device.owns_interface(interface_name): return True LOG.debug("Removing device %(interface_name)s from bridge " "%(bridge_name)s", {'interface_name': interface_name, 'bridge_name': bridge_name}) try: bridge_device.delif(interface_name) LOG.debug("Done removing device %(interface_name)s from " "bridge %(bridge_name)s", {'interface_name': interface_name, 'bridge_name': bridge_name}) return True except RuntimeError: with excutils.save_and_reraise_exception() as ctxt: if not bridge_device.owns_interface(interface_name): # the exception was likely a side effect of the tap # being deleted by some other agent during handling ctxt.reraise = False LOG.debug("Cannot remove %(interface_name)s from " "%(bridge_name)s. It is not on the bridge.", {'interface_name': interface_name, 'bridge_name': bridge_name}) return False else: LOG.debug("Cannot remove device %(interface_name)s bridge " "%(bridge_name)s does not exist", {'interface_name': interface_name, 'bridge_name': bridge_name}) return False def delete_interface(self, interface): device = self.ip.device(interface) if device.exists(): LOG.debug("Deleting interface %s", interface) device.link.set_down() device.link.delete() LOG.debug("Done deleting interface %s", interface) def get_devices_modified_timestamps(self, devices): # NOTE(kevinbenton): we aren't returning real timestamps here. We # are returning interface indexes instead which change when the # interface is removed/re-added. This works for the direct # comparison the common agent loop performs with these. # See bug/1622833 for details. return {d: bridge_lib.get_interface_ifindex(d) for d in devices} def get_all_devices(self): devices = set() for device in bridge_lib.get_bridge_names(): if device.startswith(constants.TAP_DEVICE_PREFIX): devices.add(device) return devices def vxlan_ucast_supported(self): if not cfg.CONF.VXLAN.l2_population: return False if not ip_lib.iproute_arg_supported( ['bridge', 'fdb'], 'append'): LOG.warning('Option "%(option)s" must be supported by command ' '"%(command)s" to enable %(mode)s mode', {'option': 'append', 'command': 'bridge fdb', 'mode': 'VXLAN UCAST'}) return False test_iface = None for seg_id in moves.range(1, constants.MAX_VXLAN_VNI + 1): if (ip_lib.device_exists(self.get_vxlan_device_name(seg_id)) or ip_lib.vxlan_in_use(seg_id)): continue test_iface = self.ensure_vxlan(seg_id) break else: LOG.error('No valid Segmentation ID to perform UCAST test.') return False try: bridge_lib.FdbInterface.append(constants.FLOODING_ENTRY[0], test_iface, '1.1.1.1', log_fail_as_error=False) return True except RuntimeError: return False finally: self.delete_interface(test_iface) def vxlan_mcast_supported(self): if not cfg.CONF.VXLAN.vxlan_group: LOG.warning('VXLAN muticast group(s) must be provided in ' 'vxlan_group option to enable VXLAN MCAST mode') return False if not ip_lib.iproute_arg_supported( ['ip', 'link', 'add', 'type', 'vxlan'], 'proxy'): LOG.warning('Option "%(option)s" must be supported by command ' '"%(command)s" to enable %(mode)s mode', {'option': 'proxy', 'command': 'ip link add type vxlan', 'mode': 'VXLAN MCAST'}) return False return True def check_vxlan_support(self): self.vxlan_mode = lconst.VXLAN_NONE if self.vxlan_ucast_supported(): self.vxlan_mode = lconst.VXLAN_UCAST elif self.vxlan_mcast_supported(): self.vxlan_mode = lconst.VXLAN_MCAST else: raise exceptions.VxlanNetworkUnsupported() LOG.debug('Using %s VXLAN mode', self.vxlan_mode) def fdb_ip_entry_exists(self, mac, ip, interface): ip_version = utils.get_ip_version(ip) entry = ip_lib.dump_neigh_entries(ip_version, interface, dst=ip, lladdr=mac) return entry != [] def fdb_bridge_entry_exists(self, mac, interface, agent_ip=None): entries = bridge_lib.FdbInterface.show(interface) if not agent_ip: return mac in entries return (agent_ip in entries and mac in entries) def add_fdb_ip_entry(self, mac, ip, interface): if cfg.CONF.VXLAN.arp_responder: ip_lib.add_neigh_entry(ip, mac, interface) def remove_fdb_ip_entry(self, mac, ip, interface): if cfg.CONF.VXLAN.arp_responder: ip_lib.delete_neigh_entry(ip, mac, interface) def add_fdb_entries(self, agent_ip, ports, interface): for mac, ip in ports: if mac != constants.FLOODING_ENTRY[0]: self.add_fdb_ip_entry(mac, ip, interface) bridge_lib.FdbInterface.replace(mac, interface, agent_ip, check_exit_code=False) elif self.vxlan_mode == lconst.VXLAN_UCAST: if self.fdb_bridge_entry_exists(mac, interface): bridge_lib.FdbInterface.append(mac, interface, agent_ip, check_exit_code=False) else: bridge_lib.FdbInterface.add(mac, interface, agent_ip, check_exit_code=False) def remove_fdb_entries(self, agent_ip, ports, interface): for mac, ip in ports: if mac != constants.FLOODING_ENTRY[0]: self.remove_fdb_ip_entry(mac, ip, interface) bridge_lib.FdbInterface.delete(mac, interface, agent_ip, check_exit_code=False) elif self.vxlan_mode == lconst.VXLAN_UCAST: bridge_lib.FdbInterface.delete(mac, interface, agent_ip, check_exit_code=False) def get_agent_id(self): if self.bridge_mappings: mac = ip_lib.get_device_mac( list(self.bridge_mappings.values())[0]) else: devices = self.ip.get_devices(True) for device in devices: mac = ip_lib.get_device_mac(device.name) if mac: break else: LOG.error("Unable to obtain MAC address for unique ID. " "Agent terminated!") sys.exit(1) return 'lb%s' % mac.replace(":", "") def get_agent_configurations(self): configurations = {'bridge_mappings': self.bridge_mappings, 'interface_mappings': self.interface_mappings } if self.vxlan_mode != lconst.VXLAN_NONE: configurations['tunneling_ip'] = self.local_ip configurations['tunnel_types'] = [constants.TYPE_VXLAN] configurations['l2_population'] = cfg.CONF.VXLAN.l2_population return configurations def get_rpc_callbacks(self, context, agent, sg_agent): return LinuxBridgeRpcCallbacks(context, agent, sg_agent) def get_agent_api(self, **kwargs): if self.agent_api: return self.agent_api sg_agent = kwargs.get("sg_agent") iptables_manager = self._get_iptables_manager(sg_agent) self.agent_api = agent_extension_api.LinuxbridgeAgentExtensionAPI( iptables_manager) return self.agent_api def _get_iptables_manager(self, sg_agent): if not sg_agent: return None if cfg.CONF.SECURITYGROUP.firewall_driver in IPTABLES_DRIVERS: return sg_agent.firewall.iptables def get_rpc_consumers(self): consumers = [[topics.PORT, topics.UPDATE], [topics.NETWORK, topics.DELETE], [topics.NETWORK, topics.UPDATE], [topics.SECURITY_GROUP, topics.UPDATE]] if cfg.CONF.VXLAN.l2_population: consumers.append([topics.L2POPULATION, topics.UPDATE]) return consumers def ensure_port_admin_state(self, tap_name, admin_state_up): LOG.debug("Setting admin_state_up to %s for device %s", admin_state_up, tap_name) if admin_state_up: ip_lib.IPDevice(tap_name).link.set_up() else: ip_lib.IPDevice(tap_name).link.set_down() def setup_arp_spoofing_protection(self, device, device_details): arp_protect.setup_arp_spoofing_protection(device, device_details) def delete_arp_spoofing_protection(self, devices): arp_protect.delete_arp_spoofing_protection(devices) def delete_unreferenced_arp_protection(self, current_devices): arp_protect.delete_unreferenced_arp_protection(current_devices) def get_extension_driver_type(self): return lconst.EXTENSION_DRIVER_TYPE class LinuxBridgeRpcCallbacks( sg_rpc.SecurityGroupAgentRpcCallbackMixin, l2pop_rpc.L2populationRpcCallBackMixin, amb.CommonAgentManagerRpcCallBackBase): # Set RPC API version to 1.0 by default. # history # 1.1 Support Security Group RPC # 1.3 Added param devices_to_update to security_groups_provider_updated # 1.4 Added support for network_update target = oslo_messaging.Target(version='1.4') def network_delete(self, context, **kwargs): LOG.debug("network_delete received") network_id = kwargs.get('network_id') # NOTE(nick-ma-z): Don't remove pre-existing user-defined bridges if network_id in self.network_map: phynet = self.network_map[network_id].physical_network if phynet and phynet in self.agent.mgr.bridge_mappings: LOG.info("Physical network %s is defined in " "bridge_mappings and cannot be deleted.", network_id) return bridge_name = self.agent.mgr.get_bridge_name(network_id) LOG.debug("Delete %s", bridge_name) self.agent.mgr.delete_bridge(bridge_name) self.network_map.pop(network_id, None) def port_update(self, context, **kwargs): port_id = kwargs['port']['id'] device_name = self.agent.mgr.get_tap_device_name(port_id) # Put the device name in the updated_devices set. # Do not store port details, as if they're used for processing # notifications there is no guarantee the notifications are # processed in the same order as the relevant API requests. self.updated_devices.add(device_name) LOG.debug("port_update RPC received for port: %s", port_id) def network_update(self, context, **kwargs): network_id = kwargs['network']['id'] LOG.debug("network_update message processed for network " "%(network_id)s, with ports: %(ports)s", {'network_id': network_id, 'ports': self.agent.network_ports[network_id]}) for port_data in self.agent.network_ports[network_id]: self.updated_devices.add(port_data['device']) def fdb_add(self, context, fdb_entries): LOG.debug("fdb_add received") for network_id, values in fdb_entries.items(): segment = self.network_map.get(network_id) if not segment: return if segment.network_type != constants.TYPE_VXLAN: return interface = self.agent.mgr.get_vxlan_device_name( segment.segmentation_id) agent_ports = values.get('ports') for agent_ip, ports in agent_ports.items(): if agent_ip == self.agent.mgr.local_ip: continue self.agent.mgr.add_fdb_entries(agent_ip, ports, interface) def fdb_remove(self, context, fdb_entries): LOG.debug("fdb_remove received") for network_id, values in fdb_entries.items(): segment = self.network_map.get(network_id) if not segment: return if segment.network_type != constants.TYPE_VXLAN: return interface = self.agent.mgr.get_vxlan_device_name( segment.segmentation_id) agent_ports = values.get('ports') for agent_ip, ports in agent_ports.items(): if agent_ip == self.agent.mgr.local_ip: continue self.agent.mgr.remove_fdb_entries(agent_ip, ports, interface) def _fdb_chg_ip(self, context, fdb_entries): LOG.debug("update chg_ip received") for network_id, agent_ports in fdb_entries.items(): segment = self.network_map.get(network_id) if not segment: return if segment.network_type != constants.TYPE_VXLAN: return interface = self.agent.mgr.get_vxlan_device_name( segment.segmentation_id) for agent_ip, state in agent_ports.items(): if agent_ip == self.agent.mgr.local_ip: continue after = state.get('after', []) for mac, ip in after: self.agent.mgr.add_fdb_ip_entry(mac, ip, interface) before = state.get('before', []) for mac, ip in before: self.agent.mgr.remove_fdb_ip_entry(mac, ip, interface) def fdb_update(self, context, fdb_entries): LOG.debug("fdb_update received") for action, values in fdb_entries.items(): method = '_fdb_' + action if not hasattr(self, method): raise NotImplementedError() getattr(self, method)(context, values) def main(): common_config.init(sys.argv[1:]) common_config.setup_logging() agent_config.setup_privsep() try: interface_mappings = helpers.parse_mappings( cfg.CONF.LINUX_BRIDGE.physical_interface_mappings) except ValueError as e: LOG.error("Parsing physical_interface_mappings failed: %s. " "Agent terminated!", e) sys.exit(1) LOG.info("Interface mappings: %s", interface_mappings) try: bridge_mappings = helpers.parse_mappings( cfg.CONF.LINUX_BRIDGE.bridge_mappings) except ValueError as e: LOG.error("Parsing bridge_mappings failed: %s. " "Agent terminated!", e) sys.exit(1) LOG.info("Bridge mappings: %s", bridge_mappings) manager = LinuxBridgeManager(bridge_mappings, interface_mappings) linuxbridge_capabilities.register() polling_interval = cfg.CONF.AGENT.polling_interval quitting_rpc_timeout = cfg.CONF.AGENT.quitting_rpc_timeout agent = ca.CommonAgentLoop(manager, polling_interval, quitting_rpc_timeout, constants.AGENT_TYPE_LINUXBRIDGE, LB_AGENT_BINARY) setup_profiler.setup("neutron-linuxbridge-agent", cfg.CONF.host) LOG.info("Agent initialized successfully, now running... ") launcher = service.launch(cfg.CONF, agent) launcher.wait() neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/common/0000775000175000017500000000000013553660156025547 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/common/utils.py0000664000175000017500000000224213553660046027257 0ustar zuulzuul00000000000000# Copyright 2012 Cisco Systems, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from neutron_lib import constants as n_const from oslo_log import log from neutron.plugins.ml2.drivers.linuxbridge.agent.common import constants LOG = log.getLogger(__name__) def get_tap_device_name(interface_id): """Convert port ID into device name format expected by linux bridge.""" if not interface_id: LOG.warning("Invalid Interface ID, will lead to incorrect " "tap device name") tap_device_name = (n_const.TAP_DEVICE_PREFIX + interface_id[:constants.RESOURCE_ID_LENGTH]) return tap_device_name neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py0000664000175000017500000000153413553660046030136 0ustar zuulzuul00000000000000# Copyright 2012 Cisco Systems, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. FLAT_VLAN_ID = -1 LOCAL_VLAN_ID = -2 # Supported VXLAN features VXLAN_NONE = 'not_supported' VXLAN_MCAST = 'multicast_flooding' VXLAN_UCAST = 'unicast_flooding' EXTENSION_DRIVER_TYPE = 'linuxbridge' RESOURCE_ID_LENGTH = 11 neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/common/config.py0000664000175000017500000000133513553660046027366 0ustar zuulzuul00000000000000# Copyright 2012 Cisco Systems, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.conf.plugins.ml2.drivers import linuxbridge linuxbridge.register_linuxbridge_opts() neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/common/__init__.py0000664000175000017500000000000013553660046027644 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/extension_drivers/0000775000175000017500000000000013553660156030031 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/extension_drivers/qos_driver.py0000664000175000017500000001710113553660047032557 0ustar zuulzuul00000000000000# Copyright 2016 OVH SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as const from oslo_config import cfg from oslo_log import helpers as log_helpers from oslo_log import log from neutron.agent.l2.extensions import qos_linux as qos from neutron.agent.linux import iptables_manager from neutron.agent.linux import tc_lib from neutron.common import ipv6_utils from neutron.services.qos.drivers.linuxbridge import driver LOG = log.getLogger(__name__) class QosLinuxbridgeAgentDriver(qos.QosLinuxAgentDriver): # TODO(ralonsoh): # - All driver calls should include the rule parameter, including # the delete function, to have the 'direction' parameter. This QoS # extension modification is going to be implemented in # https://review.openstack.org/#/c/341186/ SUPPORTED_RULES = driver.SUPPORTED_RULES IPTABLES_DIRECTION = {const.INGRESS_DIRECTION: 'physdev-out', const.EGRESS_DIRECTION: 'physdev-in'} IPTABLES_DIRECTION_PREFIX = {const.INGRESS_DIRECTION: "i", const.EGRESS_DIRECTION: "o"} def __init__(self): super(QosLinuxbridgeAgentDriver, self).__init__() self.iptables_manager = None self.agent_api = None self.tbf_latency = cfg.CONF.QOS.tbf_latency def consume_api(self, agent_api): self.agent_api = agent_api def initialize(self): LOG.info("Initializing Linux bridge QoS extension") if self.agent_api: self.iptables_manager = self.agent_api.get_iptables_manager() if not self.iptables_manager: # If agent_api can't provide iptables_manager, it can be # created here for extension needs self.iptables_manager = iptables_manager.IptablesManager( state_less=True, use_ipv6=ipv6_utils.is_enabled_and_bind_by_default()) self.iptables_manager.initialize_mangle_table() def _dscp_chain_name(self, direction, device): return iptables_manager.get_chain_name( "qos-%s%s" % (self.IPTABLES_DIRECTION_PREFIX[direction], device[3:])) def _dscp_rule(self, direction, device): return ('-m physdev --%s %s --physdev-is-bridged ' '-j $%s') % (self.IPTABLES_DIRECTION[direction], device, self._dscp_chain_name(direction, device)) def _dscp_rule_tag(self, device): return "dscp-%s" % device @log_helpers.log_method_call def create_bandwidth_limit(self, port, rule): tc_wrapper = self._get_tc_wrapper(port) if rule.direction == const.INGRESS_DIRECTION: tc_wrapper.set_tbf_bw_limit( rule.max_kbps, rule.max_burst_kbps, self.tbf_latency) else: tc_wrapper.set_filters_bw_limit( rule.max_kbps, self._get_egress_burst_value(rule) ) @log_helpers.log_method_call def update_bandwidth_limit(self, port, rule): tc_wrapper = self._get_tc_wrapper(port) if rule.direction == const.INGRESS_DIRECTION: tc_wrapper.update_tbf_bw_limit( rule.max_kbps, rule.max_burst_kbps, self.tbf_latency) else: tc_wrapper.update_filters_bw_limit( rule.max_kbps, self._get_egress_burst_value(rule) ) @log_helpers.log_method_call def delete_bandwidth_limit(self, port): tc_wrapper = self._get_tc_wrapper(port) tc_wrapper.delete_filters_bw_limit() @log_helpers.log_method_call def delete_bandwidth_limit_ingress(self, port): tc_wrapper = self._get_tc_wrapper(port) tc_wrapper.delete_tbf_bw_limit() @log_helpers.log_method_call def create_dscp_marking(self, port, rule): with self.iptables_manager.defer_apply(): self._set_outgoing_qos_chain_for_port(port) self._set_dscp_mark_rule(port, rule.dscp_mark) @log_helpers.log_method_call def update_dscp_marking(self, port, rule): with self.iptables_manager.defer_apply(): self._delete_dscp_mark_rule(port) self._set_outgoing_qos_chain_for_port(port) self._set_dscp_mark_rule(port, rule.dscp_mark) @log_helpers.log_method_call def delete_dscp_marking(self, port): with self.iptables_manager.defer_apply(): self._delete_dscp_mark_rule(port) self._delete_outgoing_qos_chain_for_port(port) def _set_outgoing_qos_chain_for_port(self, port): chain_name = self._dscp_chain_name( const.EGRESS_DIRECTION, port['device']) chain_rule = self._dscp_rule( const.EGRESS_DIRECTION, port['device']) self.iptables_manager.ipv4['mangle'].add_chain(chain_name) self.iptables_manager.ipv6['mangle'].add_chain(chain_name) self.iptables_manager.ipv4['mangle'].add_rule('POSTROUTING', chain_rule) self.iptables_manager.ipv6['mangle'].add_rule('POSTROUTING', chain_rule) def _delete_outgoing_qos_chain_for_port(self, port): chain_name = self._dscp_chain_name( const.EGRESS_DIRECTION, port['device']) chain_rule = self._dscp_rule( const.EGRESS_DIRECTION, port['device']) if self._qos_chain_is_empty(port, 4): self.iptables_manager.ipv4['mangle'].remove_chain(chain_name) self.iptables_manager.ipv4['mangle'].remove_rule('POSTROUTING', chain_rule) if self._qos_chain_is_empty(port, 6): self.iptables_manager.ipv6['mangle'].remove_chain(chain_name) self.iptables_manager.ipv6['mangle'].remove_rule('POSTROUTING', chain_rule) def _set_dscp_mark_rule(self, port, dscp_value): chain_name = self._dscp_chain_name( const.EGRESS_DIRECTION, port['device']) # iptables rules use hexadecimal values with --set-dscp rule = "-j DSCP --set-dscp %s" % format(dscp_value, '#04x') self.iptables_manager.ipv4['mangle'].add_rule( chain_name, rule, tag=self._dscp_rule_tag(port['device'])) self.iptables_manager.ipv6['mangle'].add_rule( chain_name, rule, tag=self._dscp_rule_tag(port['device'])) def _delete_dscp_mark_rule(self, port): self.iptables_manager.ipv4['mangle'].clear_rules_by_tag( self._dscp_rule_tag(port['device'])) self.iptables_manager.ipv6['mangle'].clear_rules_by_tag( self._dscp_rule_tag(port['device'])) def _qos_chain_is_empty(self, port, ip_version=4): chain_name = self._dscp_chain_name( const.EGRESS_DIRECTION, port['device']) rules_in_chain = self.iptables_manager.get_chain( "mangle", chain_name, ip_version=ip_version) return len(rules_in_chain) == 0 def _get_tc_wrapper(self, port): return tc_lib.TcCommand( port['device'], cfg.CONF.QOS.kernel_hz, ) neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/extension_drivers/__init__.py0000664000175000017500000000000013553660046032126 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/agent/arp_protect.py0000664000175000017500000002175613553660047027165 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron_lib.utils import net from oslo_concurrency import lockutils from oslo_log import log as logging import tenacity from neutron.agent.linux import ip_lib LOG = logging.getLogger(__name__) SPOOF_CHAIN_PREFIX = 'neutronARP-' MAC_CHAIN_PREFIX = 'neutronMAC-' def setup_arp_spoofing_protection(vif, port_details): if not port_details.get('port_security_enabled', True): # clear any previous entries related to this port delete_arp_spoofing_protection([vif]) LOG.info("Skipping ARP spoofing rules for port '%s' because " "it has port security disabled", vif) return if net.is_port_trusted(port_details): # clear any previous entries related to this port delete_arp_spoofing_protection([vif]) LOG.debug("Skipping ARP spoofing rules for network owned port " "'%s'.", vif) return _setup_arp_spoofing_protection(vif, port_details) @lockutils.synchronized('ebtables') def _setup_arp_spoofing_protection(vif, port_details): current_rules = ebtables(['-L']).splitlines() _install_mac_spoofing_protection(vif, port_details, current_rules) # collect all of the addresses and cidrs that belong to the port addresses = {f['ip_address'] for f in port_details['fixed_ips']} if port_details.get('allowed_address_pairs'): addresses |= {p['ip_address'] for p in port_details['allowed_address_pairs']} addresses = {ip for ip in addresses if netaddr.IPNetwork(ip).version == 4} if any(netaddr.IPNetwork(ip).prefixlen == 0 for ip in addresses): # don't try to install protection because a /0 prefix allows any # address anyway and the ARP_SPA can only match on /1 or more. return _install_arp_spoofing_protection(vif, addresses, current_rules) def chain_name(vif): # start each chain with a common identifier for cleanup to find return '%s%s' % (SPOOF_CHAIN_PREFIX, vif) @lockutils.synchronized('ebtables') def delete_arp_spoofing_protection(vifs): current_rules = ebtables(['-L']).splitlines() _delete_arp_spoofing_protection(vifs, current_rules, table='nat', chain='PREROUTING') # TODO(haleyb) this can go away in "R" cycle, it's here to cleanup # old chains in the filter table current_rules = ebtables(['-L'], table='filter').splitlines() _delete_arp_spoofing_protection(vifs, current_rules, table='filter', chain='FORWARD') def _delete_arp_spoofing_protection(vifs, current_rules, table, chain): # delete the jump rule and then delete the whole chain jumps = [vif for vif in vifs if vif_jump_present(vif, current_rules)] for vif in jumps: ebtables(['-D', chain, '-i', vif, '-j', chain_name(vif), '-p', 'ARP'], table=table) for vif in vifs: if chain_exists(chain_name(vif), current_rules): ebtables(['-X', chain_name(vif)], table=table) _delete_mac_spoofing_protection(vifs, current_rules, table=table, chain=chain) def _delete_unreferenced_arp_protection(current_vifs, table, chain): # deletes all jump rules and chains that aren't in current_vifs but match # the spoof prefix current_rules = ebtables(['-L'], table=table).splitlines() to_delete = [] for line in current_rules: # we're looking to find and turn the following: # Bridge chain: SPOOF_CHAIN_PREFIXtap199, entries: 0, policy: DROP # into 'tap199' if line.startswith('Bridge chain: %s' % SPOOF_CHAIN_PREFIX): devname = line.split(SPOOF_CHAIN_PREFIX, 1)[1].split(',')[0] if devname not in current_vifs: to_delete.append(devname) LOG.info("Clearing orphaned ARP spoofing entries for devices %s", to_delete) _delete_arp_spoofing_protection(to_delete, current_rules, table=table, chain=chain) @lockutils.synchronized('ebtables') def delete_unreferenced_arp_protection(current_vifs): _delete_unreferenced_arp_protection(current_vifs, table='nat', chain='PREROUTING') # TODO(haleyb) this can go away in "R" cycle, it's here to cleanup # old chains in the filter table _delete_unreferenced_arp_protection(current_vifs, table='filter', chain='FORWARD') @lockutils.synchronized('ebtables') def install_arp_spoofing_protection(vif, addresses): current_rules = ebtables(['-L']).splitlines() _install_arp_spoofing_protection(vif, addresses, current_rules) def _install_arp_spoofing_protection(vif, addresses, current_rules): # make a VIF-specific ARP chain so we don't conflict with other rules vif_chain = chain_name(vif) if not chain_exists(vif_chain, current_rules): ebtables(['-N', vif_chain, '-P', 'DROP']) # flush the chain to clear previous accepts. this will cause dropped ARP # packets until the allows are installed, but that's better than leaked # spoofed packets and ARP can handle losses. ebtables(['-F', vif_chain]) for addr in sorted(addresses): ebtables(['-A', vif_chain, '-p', 'ARP', '--arp-ip-src', addr, '-j', 'ACCEPT']) # check if jump rule already exists, if not, install it if not vif_jump_present(vif, current_rules): ebtables(['-A', 'PREROUTING', '-i', vif, '-j', vif_chain, '-p', 'ARP']) def chain_exists(chain, current_rules): for rule in current_rules: if rule.startswith('Bridge chain: %s' % chain): return True return False def vif_jump_present(vif, current_rules): searches = (('-i %s' % vif), ('-j %s' % chain_name(vif)), ('-p ARP')) for line in current_rules: if all(s in line for s in searches): return True return False def _install_mac_spoofing_protection(vif, port_details, current_rules): mac_addresses = {port_details['mac_address']} if port_details.get('allowed_address_pairs'): mac_addresses |= {p['mac_address'] for p in port_details['allowed_address_pairs']} mac_addresses = list(mac_addresses) vif_chain = _mac_chain_name(vif) # mac filter chain for each vif which has a default deny if not chain_exists(vif_chain, current_rules): ebtables(['-N', vif_chain, '-P', 'DROP']) # check if jump rule already exists, if not, install it if not _mac_vif_jump_present(vif, current_rules): ebtables(['-A', 'PREROUTING', '-i', vif, '-j', vif_chain]) # we can't just feed all allowed macs at once because we can exceed # the maximum argument size. limit to 500 per rule. for chunk in (mac_addresses[i:i + 500] for i in range(0, len(mac_addresses), 500)): new_rule = ['-A', vif_chain, '-i', vif, '--among-src', ','.join(sorted(chunk)), '-j', 'RETURN'] ebtables(new_rule) _delete_vif_mac_rules(vif, current_rules) def _mac_vif_jump_present(vif, current_rules): searches = (('-i %s' % vif), ('-j %s' % _mac_chain_name(vif))) for line in current_rules: if all(s in line for s in searches): return True return False def _mac_chain_name(vif): return '%s%s' % (MAC_CHAIN_PREFIX, vif) def _delete_vif_mac_rules(vif, current_rules): chain = _mac_chain_name(vif) for rule in current_rules: if '-i %s' % vif in rule and '--among-src' in rule: ebtables(['-D', chain] + rule.split()) def _delete_mac_spoofing_protection(vifs, current_rules, table, chain): # delete the jump rule and then delete the whole chain jumps = [vif for vif in vifs if _mac_vif_jump_present(vif, current_rules)] for vif in jumps: ebtables(['-D', chain, '-i', vif, '-j', _mac_chain_name(vif)], table=table) for vif in vifs: chain = _mac_chain_name(vif) if chain_exists(chain, current_rules): ebtables(['-X', chain], table=table) # Used to scope ebtables commands in testing NAMESPACE = None @tenacity.retry( wait=tenacity.wait_exponential(multiplier=0.01), retry=tenacity.retry_if_exception(lambda e: e.returncode == 255), reraise=True ) def ebtables(comm, table='nat'): execute = ip_lib.IPWrapper(NAMESPACE).netns.execute return execute(['ebtables', '-t', table, '--concurrent'] + comm, run_as_root=True) neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/__init__.py0000664000175000017500000000000013553660046025256 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/mech_driver/0000775000175000017500000000000013553660156025450 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/mech_driver/__init__.py0000664000175000017500000000000013553660046027545 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/linuxbridge/mech_driver/mech_linuxbridge.py0000664000175000017500000000424313553660047031334 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron.agent import securitygroups_rpc from neutron.plugins.ml2.drivers import mech_agent from neutron.services.qos.drivers.linuxbridge import driver as lb_qos_driver class LinuxbridgeMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """Attach to networks using linuxbridge L2 agent. The LinuxbridgeMechanismDriver integrates the ml2 plugin with the linuxbridge L2 agent. Port binding with this driver requires the linuxbridge agent to be running on the port's host, and that agent to have connectivity to at least one segment of the port's network. """ def __init__(self): sg_enabled = securitygroups_rpc.is_firewall_enabled() super(LinuxbridgeMechanismDriver, self).__init__( constants.AGENT_TYPE_LINUXBRIDGE, portbindings.VIF_TYPE_BRIDGE, {portbindings.CAP_PORT_FILTER: sg_enabled}) lb_qos_driver.register() def get_allowed_network_types(self, agent): return (agent['configurations'].get('tunnel_types', []) + [constants.TYPE_LOCAL, constants.TYPE_FLAT, constants.TYPE_VLAN]) def get_mappings(self, agent): mappings = dict(agent['configurations'].get('interface_mappings', {}), **agent['configurations'].get('bridge_mappings', {})) return mappings def check_vlan_transparency(self, context): """Linuxbridge driver vlan transparency support.""" return True neutron-12.1.1/neutron/plugins/ml2/drivers/l2pop/0000775000175000017500000000000013553660156021701 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/l2pop/db.py0000664000175000017500000001615713553660047022651 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as const from oslo_serialization import jsonutils from oslo_utils import timeutils from sqlalchemy import orm from neutron.db.models import agent as agent_model from neutron.db.models import l3ha as l3ha_model from neutron.db import models_v2 from neutron.objects import agent as agent_objs from neutron.plugins.ml2 import models as ml2_models HA_ROUTER_PORTS = (const.DEVICE_OWNER_HA_REPLICATED_INT, const.DEVICE_OWNER_ROUTER_SNAT) def get_agent_ip_by_host(context, agent_host): agent = get_agent_by_host(context, agent_host) if agent: return get_agent_ip(agent) def _get_agent_conf_dict(agent): configuration = agent.configurations if not isinstance(configuration, dict): configuration = jsonutils.loads(configuration) return configuration def get_agent_ip(agent): configuration = _get_agent_conf_dict(agent) return configuration.get('tunneling_ip') def get_agent_uptime(agent): return timeutils.delta_seconds(agent.started_at, agent.heartbeat_timestamp) def get_agent_tunnel_types(agent): configuration = _get_agent_conf_dict(agent) return configuration.get('tunnel_types') def get_agent_l2pop_network_types(agent): configuration = _get_agent_conf_dict(agent) return configuration.get('l2pop_network_types') def get_agent_by_host(context, agent_host): """Return a L2 agent on the host.""" agents = agent_objs.Agent.get_objects(context, host=agent_host) for agent in agents: if get_agent_ip(agent): return agent def _get_active_network_ports(context, network_id): query = context.session.query(ml2_models.PortBinding, agent_model.Agent) query = query.join(agent_model.Agent, agent_model.Agent.host == ml2_models.PortBinding.host) query = query.join(models_v2.Port) query = query.options(orm.subqueryload(ml2_models.PortBinding.port)) query = query.filter(models_v2.Port.network_id == network_id, models_v2.Port.status == const.PORT_STATUS_ACTIVE) return query def _ha_router_interfaces_on_network_query(context, network_id): query = context.session.query(models_v2.Port) query = query.join(l3ha_model.L3HARouterAgentPortBinding, l3ha_model.L3HARouterAgentPortBinding.router_id == models_v2.Port.device_id) return query.filter( models_v2.Port.network_id == network_id, models_v2.Port.device_owner.in_(HA_ROUTER_PORTS)) def _get_ha_router_interface_ids(context, network_id): query = _ha_router_interfaces_on_network_query(context, network_id) return query.from_self(models_v2.Port.id).distinct() def get_nondistributed_active_network_ports(context, network_id): query = _get_active_network_ports(context, network_id) # Exclude DVR and HA router interfaces query = query.filter(models_v2.Port.device_owner != const.DEVICE_OWNER_DVR_INTERFACE) ha_iface_ids_query = _get_ha_router_interface_ids(context, network_id) query = query.filter(models_v2.Port.id.notin_(ha_iface_ids_query)) return [(bind, agent) for bind, agent in query.all() if get_agent_ip(agent)] def get_dvr_active_network_ports(context, network_id): query = context.session.query(ml2_models.DistributedPortBinding, agent_model.Agent) query = query.join(agent_model.Agent, agent_model.Agent.host == ml2_models.DistributedPortBinding.host) query = query.join(models_v2.Port) query = query.options( orm.subqueryload(ml2_models.DistributedPortBinding.port)) query = query.filter(models_v2.Port.network_id == network_id, models_v2.Port.status == const.PORT_STATUS_ACTIVE, models_v2.Port.device_owner == const.DEVICE_OWNER_DVR_INTERFACE) return [(bind, agent) for bind, agent in query.all() if get_agent_ip(agent)] def get_distributed_active_network_ports(context, network_id): return (get_dvr_active_network_ports(context, network_id) + get_ha_active_network_ports(context, network_id)) def get_ha_active_network_ports(context, network_id): agents = get_ha_agents(context, network_id=network_id) return [(None, agent) for agent in agents] def get_ha_agents(context, network_id=None, router_id=None): agents = agent_objs.Agent.get_ha_agents(context, network_id=network_id, router_id=router_id) return [agent for agent in agents if get_agent_ip(agent)] def get_ha_agents_by_router_id(context, router_id): return get_ha_agents(context, router_id=router_id) def get_agent_network_active_port_count(context, agent_host, network_id): query = context.session.query(models_v2.Port) query1 = query.join(ml2_models.PortBinding) query1 = query1.filter(models_v2.Port.network_id == network_id, models_v2.Port.status == const.PORT_STATUS_ACTIVE, models_v2.Port.device_owner != const.DEVICE_OWNER_DVR_INTERFACE, ml2_models.PortBinding.host == agent_host) ha_iface_ids_query = _get_ha_router_interface_ids(context, network_id) query1 = query1.filter(models_v2.Port.id.notin_(ha_iface_ids_query)) ha_port_count = get_ha_router_active_port_count( context, agent_host, network_id) query2 = query.join(ml2_models.DistributedPortBinding) query2 = query2.filter(models_v2.Port.network_id == network_id, ml2_models.DistributedPortBinding.status == const.PORT_STATUS_ACTIVE, models_v2.Port.device_owner == const.DEVICE_OWNER_DVR_INTERFACE, ml2_models.DistributedPortBinding.host == agent_host) return (query1.count() + query2.count() + ha_port_count) def get_ha_router_active_port_count(context, agent_host, network_id): # Return num of HA router interfaces on the given network and host query = _ha_router_interfaces_on_network_query(context, network_id) query = query.filter(models_v2.Port.status == const.PORT_STATUS_ACTIVE) query = query.join(agent_model.Agent) query = query.filter(agent_model.Agent.host == agent_host) return query.count() neutron-12.1.1/neutron/plugins/ml2/drivers/l2pop/rpc_manager/0000775000175000017500000000000013553660156024157 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/l2pop/rpc_manager/l2population_rpc.py0000664000175000017500000003100513553660046030022 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import itertools from neutron_lib import constants as n_const from oslo_config import cfg from oslo_log import helpers as log_helpers import six from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc from neutron.plugins.ml2.drivers.openvswitch.agent import vlanmanager @six.add_metaclass(abc.ABCMeta) class L2populationRpcCallBackMixin(object): '''General mixin class of L2-population RPC call back. The following methods are called through RPC. add_fdb_entries(), remove_fdb_entries(), update_fdb_entries() The following methods are used in an agent as internal methods. fdb_add(), fdb_remove(), fdb_update() ''' @log_helpers.log_method_call def add_fdb_entries(self, context, fdb_entries, host=None): if not host or host == cfg.CONF.host: self.fdb_add(context, self._unmarshall_fdb_entries(fdb_entries)) @log_helpers.log_method_call def remove_fdb_entries(self, context, fdb_entries, host=None): if not host or host == cfg.CONF.host: self.fdb_remove(context, self._unmarshall_fdb_entries(fdb_entries)) @log_helpers.log_method_call def update_fdb_entries(self, context, fdb_entries, host=None): if not host or host == cfg.CONF.host: self.fdb_update(context, self._unmarshall_fdb_entries(fdb_entries)) @staticmethod def _unmarshall_fdb_entries(fdb_entries): """Prepares fdb_entries from JSON. All methods in this class that receive messages should call this to unmarshall fdb_entries from the wire. :param fdb_entries: Original fdb_entries data-structure. Looks like: { : { ..., 'ports': { : [ [, ], ... ], ... Or in the case of an update: { 'chg_ip': { '': { '': { 'before': [ [, ], ... ], 'after' : [ [, ], ... ], }, '': { 'before': ... :returns: Deep copy with [, ] converted to PortInfo """ unmarshalled = dict(fdb_entries) chg_ip_nets = [net.values() for net in unmarshalled.get('chg_ip', {}).values()] for agent in itertools.chain.from_iterable(chg_ip_nets): for when in ('before', 'after'): if when in agent: agent[when] = [l2pop_rpc.PortInfo(*pi) for pi in agent[when]] for value in unmarshalled.values(): if 'ports' in value: value['ports'] = dict( (address, [l2pop_rpc.PortInfo(*pi) for pi in port_infos]) for address, port_infos in value['ports'].items() ) return unmarshalled @abc.abstractmethod def fdb_add(self, context, fdb_entries): pass @abc.abstractmethod def fdb_remove(self, context, fdb_entries): pass @abc.abstractmethod def fdb_update(self, context, fdb_entries): pass @six.add_metaclass(abc.ABCMeta) class L2populationRpcCallBackTunnelMixin(L2populationRpcCallBackMixin): '''Mixin class of L2-population call back for Tunnel. The following methods are all used in agents as internal methods. Some of the methods in this class use Local VLAN Mapping, aka lvm. It's a python object with at least the following attributes: ============ ========================================================= Attribute Description ============ ========================================================= vlan An identifier used by the agent to identify a neutron network. network_type A network type found in neutron.plugins.common.constants. ============ ========================================================= NOTE(yamamoto): "Local VLAN" is an OVS-agent term. OVS-agent internally uses 802.1q VLAN tagging to isolate networks. While this class inherited the terms from OVS-agent, it does not assume the specific underlying technologies. E.g. this class is also used by ofagent, where a different mechanism is used. ''' @abc.abstractmethod def add_fdb_flow(self, br, port_info, remote_ip, lvm, ofport): '''Add flow for fdb This method is assumed to be used by method fdb_add_tun. We expect to add a flow entry to send a packet to specified port on bridge. And you may edit some information for local arp response. :param br: represent the bridge on which add_fdb_flow should be applied. :param port_info: PortInfo instance to include mac and ip. .mac_address .ip_address :remote_ip: remote ip address. :param lvm: a local VLAN map of network. :param ofport: a port to add. ''' pass @abc.abstractmethod def del_fdb_flow(self, br, port_info, remote_ip, lvm, ofport): '''Delete flow for fdb This method is assumed to be used by method fdb_remove_tun. We expect to delete a flow entry to send a packet to specified port from bridge. And you may delete some information for local arp response. :param br: represent the bridge on which del_fdb_flow should be applied. :param port_info: PortInfo instance to include mac and ip. .mac_address .ip_address :remote_ip: remote ip address. :param lvm: local VLAN map of a network. See add_fdb_flow for more explanation. :param ofport: a port to delete. ''' pass @abc.abstractmethod def setup_tunnel_port(self, br, remote_ip, network_type): '''Setup an added tunnel port. This method is assumed to be used by method fdb_add_tun. We expect to prepare to call add_fdb_flow. It will be mainly adding a port to a bridge. If you need, you may do some preparations for a bridge. :param br: represent the bridge on which setup_tunnel_port should be applied. :param remote_ip: an ip for a port to setup. :param network_type: a type of a network. :returns: an ofport value. value 0 means the port is unavailable. ''' pass @abc.abstractmethod def cleanup_tunnel_port(self, br, tun_ofport, tunnel_type): '''Clean up a deleted tunnel port. This method is assumed to be used by method fdb_remove_tun. We expect to clean up after calling del_fdb_flow. It will be mainly deleting a port from a bridge. If you need, you may do some cleanup for a bridge. :param br: represent the bridge on which cleanup_tunnel_port should be applied. :param tun_ofport: a port value to cleanup. :param tunnel_type: a type of a tunnel. ''' pass @abc.abstractmethod def setup_entry_for_arp_reply(self, br, action, local_vid, mac_address, ip_address): '''Operate the ARP respond information. Update MAC/IPv4 associations, which is typically used by the local ARP responder. For example, OVS-agent sets up flow entries to perform ARP responses. :param br: represent the bridge on which setup_entry_for_arp_reply should be applied. :param action: add/remove flow for arp response information. :param local_vid: id in local VLAN map of network's ARP entry. :param mac_address: MAC string value. :param ip_address: IP string value. ''' pass def get_agent_ports(self, fdb_entries): """Generator to yield port info. For each known (i.e found in VLAN manager) network in fdb_entries, yield (lvm, fdb_entries[network_id]['ports']) pair. :param fdb_entries: l2pop fdb entries """ vlan_manager = vlanmanager.LocalVlanManager() for network_id, values in fdb_entries.items(): try: lvm = vlan_manager.get(network_id) except vlanmanager.MappingNotFound: continue agent_ports = values.get('ports') yield (lvm, agent_ports) @log_helpers.log_method_call def fdb_add_tun(self, context, br, lvm, agent_ports, lookup_port): for remote_ip, ports in agent_ports.items(): # Ensure we have a tunnel port with this remote agent ofport = lookup_port(lvm.network_type, remote_ip) if not ofport: ofport = self.setup_tunnel_port(br, remote_ip, lvm.network_type) if ofport == 0: continue for port in ports: self.add_fdb_flow(br, port, remote_ip, lvm, ofport) @log_helpers.log_method_call def fdb_remove_tun(self, context, br, lvm, agent_ports, lookup_port): for remote_ip, ports in agent_ports.items(): ofport = lookup_port(lvm.network_type, remote_ip) if not ofport: continue for port in ports: self.del_fdb_flow(br, port, remote_ip, lvm, ofport) if port == n_const.FLOODING_ENTRY: # Check if this tunnel port is still used self.cleanup_tunnel_port(br, ofport, lvm.network_type) @log_helpers.log_method_call def fdb_update(self, context, fdb_entries): '''Call methods named '_fdb_'. This method assumes that methods '_fdb_' are defined in class. Currently the following actions are available. chg_ip ''' for action, values in fdb_entries.items(): method = '_fdb_' + action if not hasattr(self, method): raise NotImplementedError() getattr(self, method)(context, values) @log_helpers.log_method_call def fdb_chg_ip_tun(self, context, br, fdb_entries, local_ip): '''fdb update when an IP of a port is updated. The ML2 l2-pop mechanism driver sends an fdb update rpc message when an IP of a port is updated. :param context: RPC context. :param br: represent the bridge on which fdb_chg_ip_tun should be applied. :param fdb_entries: fdb dicts that contain all mac/IP information per agent and network. {'net1': {'agent_ip': {'before': PortInfo, 'after': PortInfo } } 'net2': ... } PortInfo has .mac_address and .ip_address attrs. :param local_ip: local IP address of this agent. ''' vlan_manager = vlanmanager.LocalVlanManager() for network_id, agent_ports in fdb_entries.items(): try: lvm = vlan_manager.get(network_id) except vlanmanager.MappingNotFound: continue for agent_ip, state in agent_ports.items(): if agent_ip == local_ip: continue after = state.get('after', []) for mac_ip in after: self.setup_entry_for_arp_reply(br, 'add', lvm.vlan, mac_ip.mac_address, mac_ip.ip_address) before = state.get('before', []) for mac_ip in before: self.setup_entry_for_arp_reply(br, 'remove', lvm.vlan, mac_ip.mac_address, mac_ip.ip_address) neutron-12.1.1/neutron/plugins/ml2/drivers/l2pop/rpc_manager/__init__.py0000664000175000017500000000000013553660046026254 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/l2pop/__init__.py0000664000175000017500000000000013553660046023776 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/l2pop/rpc.py0000664000175000017500000000662313553660047023045 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from oslo_log import log as logging import oslo_messaging from neutron.common import rpc as n_rpc from neutron.common import topics LOG = logging.getLogger(__name__) PortInfo = collections.namedtuple("PortInfo", "mac_address ip_address") class L2populationAgentNotifyAPI(object): def __init__(self, topic=topics.AGENT): self.topic = topic self.topic_l2pop_update = topics.get_topic_name(topic, topics.L2POPULATION, topics.UPDATE) target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def _notification_fanout(self, context, method, fdb_entries): LOG.debug('Fanout notify l2population agents at %(topic)s ' 'the message %(method)s with %(fdb_entries)s', {'topic': self.topic, 'method': method, 'fdb_entries': fdb_entries}) cctxt = self.client.prepare(topic=self.topic_l2pop_update, fanout=True) cctxt.cast(context, method, fdb_entries=fdb_entries) def _notification_host(self, context, method, fdb_entries, host): LOG.debug('Notify l2population agent %(host)s at %(topic)s the ' 'message %(method)s with %(fdb_entries)s', {'host': host, 'topic': self.topic, 'method': method, 'fdb_entries': fdb_entries}) cctxt = self.client.prepare(topic=self.topic_l2pop_update, server=host) cctxt.cast(context, method, fdb_entries=fdb_entries) def add_fdb_entries(self, context, fdb_entries, host=None): if fdb_entries: if host: self._notification_host(context, 'add_fdb_entries', fdb_entries, host) else: self._notification_fanout(context, 'add_fdb_entries', fdb_entries) def remove_fdb_entries(self, context, fdb_entries, host=None): if fdb_entries: if host: self._notification_host(context, 'remove_fdb_entries', fdb_entries, host) else: self._notification_fanout(context, 'remove_fdb_entries', fdb_entries) def update_fdb_entries(self, context, fdb_entries, host=None): if fdb_entries: if host: self._notification_host(context, 'update_fdb_entries', fdb_entries, host) else: self._notification_fanout(context, 'update_fdb_entries', fdb_entries) neutron-12.1.1/neutron/plugins/ml2/drivers/l2pop/README0000664000175000017500000000371013553660046022560 0ustar zuulzuul00000000000000Neutron ML2 l2 population Mechanism Drivers l2 population (l2pop) mechanism drivers implements the ML2 driver to improve open source plugins overlay implementations (VXLAN with Linux bridge and GRE/VXLAN with OVS). This mechanism driver is implemented in ML2 to propagate the forwarding information among agents using a common RPC API. More informations could be found on the wiki page [1]. VXLAN Linux kernel: ------------------- The VXLAN Linux kernel module provide all necessary functionalities to populate the forwarding table and local ARP responder tables. This module appears on release 3.7 of the vanilla Linux kernel in experimental: - 3.8: first stable release, no edge replication (multicast necessary), - 3.9: edge replication only for the broadcasted packets, - 3.11: edge replication for broadcast, multicast and unknown packets. Note: Some distributions (like RHEL) have backported this module on precedent kernel version. OpenvSwitch: ------------ The OVS OpenFlow tables provide all of the necessary functionality to populate the forwarding table and local ARP responder tables. A wiki page describe how the flow tables did evolve on OVS agents: - [2] without local ARP responder - [3] with local ARP responder. /!\ This functionality is only available since the development branch 2.1. It's possible to disable (enable by default) it through the flag 'arp_responder'. /!\ Note: A difference persists between the LB and OVS agents when they are used with the l2-pop mechanism driver (and local ARP responder available). The LB agent will drop unknown unicast (VXLAN bridge mode), whereas the OVS agent will flood it. [1] https://wiki.openstack.org/wiki/L2population_blueprint [2] https://wiki.openstack.org/wiki/Ovs-flow-logic#OVS_flows_logic [3] https://wiki.openstack.org/wiki/Ovs-flow-logic#OVS_flows_logic_with_local_ARP_responderneutron-12.1.1/neutron/plugins/ml2/drivers/l2pop/mech_driver.py0000664000175000017500000003714213553660047024550 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as const from neutron_lib import context as n_context from neutron_lib import exceptions from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from neutron_lib.plugins.ml2 import api from oslo_config import cfg from oslo_log import log as logging from neutron._i18n import _ from neutron.conf.plugins.ml2.drivers import l2pop as config from neutron.db import l3_hamode_db from neutron.plugins.ml2.drivers.l2pop import db as l2pop_db from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc LOG = logging.getLogger(__name__) config.register_l2_population_opts() class L2populationMechanismDriver(api.MechanismDriver): def __init__(self): super(L2populationMechanismDriver, self).__init__() self.L2populationAgentNotify = l2pop_rpc.L2populationAgentNotifyAPI() def initialize(self): LOG.debug("Experimental L2 population driver") self.rpc_ctx = n_context.get_admin_context_without_session() def _get_port_fdb_entries(self, port): # the port might be concurrently deleted if not port or not port.get('fixed_ips'): return [] return [l2pop_rpc.PortInfo(mac_address=port['mac_address'], ip_address=ip['ip_address']) for ip in port['fixed_ips']] def _remove_flooding(self, fdb_entries): for network_fdb in fdb_entries.values(): for agent_fdb in network_fdb.get('ports', {}).values(): try: agent_fdb.remove(const.FLOODING_ENTRY) except ValueError: pass def check_vlan_transparency(self, context): """L2population driver vlan transparency support.""" return True def _get_ha_port_agents_fdb( self, context, network_id, router_id): other_fdb_ports = {} for agent in l2pop_db.get_ha_agents_by_router_id(context, router_id): agent_active_ports = l2pop_db.get_agent_network_active_port_count( context, agent.host, network_id) if agent_active_ports == 0: ip = l2pop_db.get_agent_ip(agent) other_fdb_ports[ip] = [const.FLOODING_ENTRY] return other_fdb_ports def delete_port_postcommit(self, context): port = context.current agent_host = context.host plugin_context = context._plugin_context fdb_entries = self._get_agent_fdb( plugin_context, context.bottom_bound_segment, port, agent_host) if fdb_entries and l3_hamode_db.is_ha_router_port( plugin_context, port['device_owner'], port['device_id']): network_id = port['network_id'] other_fdb_ports = self._get_ha_port_agents_fdb( plugin_context, network_id, port['device_id']) fdb_entries[network_id]['ports'] = other_fdb_ports self.L2populationAgentNotify.remove_fdb_entries(self.rpc_ctx, fdb_entries) def filter_hosts_with_segment_access( self, context, segments, candidate_hosts, agent_getter): # NOTE(cbrandily): let other mechanisms (openvswitch, linuxbridge, ...) # perform the filtering return set() def _get_diff_ips(self, orig, port): orig_ips = set([ip['ip_address'] for ip in orig['fixed_ips']]) port_ips = set([ip['ip_address'] for ip in port['fixed_ips']]) # check if an ip has been added or removed orig_chg_ips = orig_ips.difference(port_ips) port_chg_ips = port_ips.difference(orig_ips) if orig_chg_ips or port_chg_ips: return orig_chg_ips, port_chg_ips def _fixed_ips_changed(self, context, orig, port, diff_ips): orig_ips, port_ips = diff_ips if (port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE): agent_host = context.host else: agent_host = context.original_host if not agent_host: return agent_ip = l2pop_db.get_agent_ip_by_host(context._plugin_context, agent_host) orig_mac_ip = [l2pop_rpc.PortInfo(mac_address=port['mac_address'], ip_address=ip) for ip in orig_ips] port_mac_ip = [l2pop_rpc.PortInfo(mac_address=port['mac_address'], ip_address=ip) for ip in port_ips] upd_fdb_entries = {port['network_id']: {agent_ip: {}}} ports = upd_fdb_entries[port['network_id']][agent_ip] if orig_mac_ip: ports['before'] = orig_mac_ip if port_mac_ip: ports['after'] = port_mac_ip self.L2populationAgentNotify.update_fdb_entries( self.rpc_ctx, {'chg_ip': upd_fdb_entries}) return True def update_port_precommit(self, context): port = context.current orig = context.original if (orig['mac_address'] != port['mac_address'] and context.status == const.PORT_STATUS_ACTIVE): msg = _("unable to modify mac_address of ACTIVE port " "%s") % port['id'] raise exceptions.InvalidInput(error_message=msg) def update_port_postcommit(self, context): port = context.current orig = context.original plugin_context = context._plugin_context if l3_hamode_db.is_ha_router_port(plugin_context, port['device_owner'], port['device_id']): return diff_ips = self._get_diff_ips(orig, port) if diff_ips: self._fixed_ips_changed(context, orig, port, diff_ips) if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: if context.status == const.PORT_STATUS_ACTIVE: self.update_port_up(context) if context.status == const.PORT_STATUS_DOWN: agent_host = context.host fdb_entries = self._get_agent_fdb( plugin_context, context.bottom_bound_segment, port, agent_host) self.L2populationAgentNotify.remove_fdb_entries( self.rpc_ctx, fdb_entries) elif (context.host != context.original_host and context.original_status == const.PORT_STATUS_ACTIVE and context.status == const.PORT_STATUS_DOWN): # The port has been migrated. Send notification about port # removal from old host. fdb_entries = self._get_agent_fdb( plugin_context, context.original_bottom_bound_segment, orig, context.original_host) self.L2populationAgentNotify.remove_fdb_entries( self.rpc_ctx, fdb_entries) elif context.status != context.original_status: if context.status == const.PORT_STATUS_ACTIVE: self.update_port_up(context) elif context.status == const.PORT_STATUS_DOWN: fdb_entries = self._get_agent_fdb( plugin_context, context.bottom_bound_segment, port, context.host) self.L2populationAgentNotify.remove_fdb_entries( self.rpc_ctx, fdb_entries) def _validate_segment(self, segment, port_id, agent): if not segment: LOG.debug("Port %(port)s updated by agent %(agent)s isn't bound " "to any segment", {'port': port_id, 'agent': agent}) return False network_types = l2pop_db.get_agent_l2pop_network_types(agent) if network_types is None: network_types = l2pop_db.get_agent_tunnel_types(agent) if segment['network_type'] not in network_types: return False return True def _create_agent_fdb(self, context, agent, segment, network_id): agent_fdb_entries = {network_id: {'segment_id': segment['segmentation_id'], 'network_type': segment['network_type'], 'ports': {}}} tunnel_network_ports = ( l2pop_db.get_distributed_active_network_ports(context, network_id)) fdb_network_ports = ( l2pop_db.get_nondistributed_active_network_ports(context, network_id)) ports = agent_fdb_entries[network_id]['ports'] ports.update(self._get_tunnels( fdb_network_ports + tunnel_network_ports, agent.host)) for agent_ip, fdbs in ports.items(): for binding, agent in fdb_network_ports: if l2pop_db.get_agent_ip(agent) == agent_ip: fdbs.extend(self._get_port_fdb_entries(binding.port)) return agent_fdb_entries def _get_tunnels(self, tunnel_network_ports, exclude_host): agents = {} for __, agent in tunnel_network_ports: if agent.host == exclude_host: continue ip = l2pop_db.get_agent_ip(agent) if not ip: LOG.debug("Unable to retrieve the agent ip, check " "the agent %s configuration.", agent.host) continue if ip not in agents: agents[ip] = [const.FLOODING_ENTRY] return agents def agent_restarted(self, context): agent_host = context.host port_context = context._plugin_context agent = l2pop_db.get_agent_by_host(port_context, agent_host) if l2pop_db.get_agent_uptime(agent) < cfg.CONF.l2pop.agent_boot_time: return True return False def update_port_down(self, context): port = context.current agent_host = context.host l3plugin = directory.get_plugin(plugin_constants.L3) # when agent transitions to backup, don't remove flood flows if agent_host and l3plugin and getattr( l3plugin, "list_router_ids_on_host", None): admin_context = n_context.get_admin_context() port_context = context._plugin_context fdb_entries = self._get_agent_fdb( port_context, context.bottom_bound_segment, port, agent_host, include_ha_router_ports=True) if (fdb_entries and l3plugin.list_router_ids_on_host( admin_context, agent_host, [port['device_id']])): # NOTE(slaweq): in case this is HA router, remove unicast # entries to this port but don't remove flood entry self._remove_flooding(fdb_entries) self.L2populationAgentNotify.remove_fdb_entries( self.rpc_ctx, fdb_entries) def update_port_up(self, context, agent_restarted=None): port = context.current agent_host = context.host port_context = context._plugin_context agent = l2pop_db.get_agent_by_host(port_context, agent_host) if not agent: LOG.warning("Unable to retrieve active L2 agent on host %s", agent_host) return network_id = port['network_id'] agent_active_ports = l2pop_db.get_agent_network_active_port_count( port_context, agent_host, network_id) agent_ip = l2pop_db.get_agent_ip(agent) segment = context.bottom_bound_segment if not self._validate_segment(segment, port['id'], agent): return other_fdb_entries = self._get_fdb_entries_template( segment, agent_ip, network_id) other_fdb_ports = other_fdb_entries[network_id]['ports'] # with high concurrency more than 1 port may be activated on an agent # at the same time (like VM port + a DVR port) so checking for 1 or 2 is_first_port = agent_active_ports in (1, 2) if agent_restarted is None: # Only for backport compatibility, will be removed. agent_restarted = self.agent_restarted(context) if is_first_port or agent_restarted: # First port(s) activated on current agent in this network, # we have to provide it with the whole list of fdb entries agent_fdb_entries = self._create_agent_fdb(port_context, agent, segment, network_id) # And notify other agents to add flooding entry other_fdb_ports[agent_ip].append(const.FLOODING_ENTRY) if agent_fdb_entries[network_id]['ports'].keys(): self.L2populationAgentNotify.add_fdb_entries( self.rpc_ctx, agent_fdb_entries, agent_host) # Notify other agents to add fdb rule for current port if (port['device_owner'] != const.DEVICE_OWNER_DVR_INTERFACE and not l3_hamode_db.is_ha_router_port( port_context, port['device_owner'], port['device_id'])): other_fdb_ports[agent_ip] += self._get_port_fdb_entries(port) self.L2populationAgentNotify.add_fdb_entries(self.rpc_ctx, other_fdb_entries) def _get_agent_fdb(self, context, segment, port, agent_host, include_ha_router_ports=False): if not agent_host: return network_id = port['network_id'] agent_active_ports = l2pop_db.get_agent_network_active_port_count( context, agent_host, network_id) agent = l2pop_db.get_agent_by_host(context, agent_host) if not agent: LOG.warning("Unable to retrieve active L2 agent on host %s", agent_host) return if not self._validate_segment(segment, port['id'], agent): return agent_ip = l2pop_db.get_agent_ip(agent) other_fdb_entries = self._get_fdb_entries_template( segment, agent_ip, port['network_id']) if agent_active_ports == 0: # Agent is removing its last activated port in this network, # other agents needs to be notified to delete their flooding entry. other_fdb_entries[network_id]['ports'][agent_ip].append( const.FLOODING_ENTRY) # Notify other agents to remove fdb rules for current port if (port['device_owner'] != const.DEVICE_OWNER_DVR_INTERFACE and (include_ha_router_ports or not l3_hamode_db.is_ha_router_port(context, port['device_owner'], port['device_id']))): fdb_entries = self._get_port_fdb_entries(port) other_fdb_entries[network_id]['ports'][agent_ip] += fdb_entries return other_fdb_entries @classmethod def _get_fdb_entries_template(cls, segment, agent_ip, network_id): return { network_id: {'segment_id': segment['segmentation_id'], 'network_type': segment['network_type'], 'ports': {agent_ip: []}}} neutron-12.1.1/neutron/plugins/ml2/drivers/type_geneve.py0000664000175000017500000000423613553660047023535 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as p_const from neutron_lib import exceptions as n_exc from oslo_config import cfg from oslo_log import log from neutron.conf.plugins.ml2.drivers import driver_type from neutron.objects.plugins.ml2 import geneveallocation as geneve_obj from neutron.plugins.ml2.drivers import type_tunnel LOG = log.getLogger(__name__) driver_type.register_ml2_drivers_geneve_opts() class GeneveTypeDriver(type_tunnel.EndpointTunnelTypeDriver): def __init__(self): super(GeneveTypeDriver, self).__init__(geneve_obj.GeneveAllocation, geneve_obj.GeneveEndpoint) self.max_encap_size = cfg.CONF.ml2_type_geneve.max_header_size def get_type(self): return p_const.TYPE_GENEVE def initialize(self): try: self._initialize(cfg.CONF.ml2_type_geneve.vni_ranges) except n_exc.NetworkTunnelRangeError: LOG.error("Failed to parse vni_ranges. Service terminated!") raise SystemExit() def get_endpoints(self): """Get every geneve endpoints from database.""" geneve_endpoints = self._get_endpoints() return [{'ip_address': geneve_endpoint.ip_address, 'host': geneve_endpoint.host} for geneve_endpoint in geneve_endpoints] def add_endpoint(self, ip, host): return self._add_endpoint(ip, host) def get_mtu(self, physical_network=None): mtu = super(GeneveTypeDriver, self).get_mtu() return mtu - self.max_encap_size if mtu else 0 neutron-12.1.1/neutron/plugins/ml2/drivers/agent/0000775000175000017500000000000013553660156021743 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/agent/config.py0000664000175000017500000000150613553660046023562 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron.conf.agent import common as config from neutron.conf.plugins.ml2.drivers import agent agent.register_agent_opts() config.register_agent_state_opts_helper(cfg.CONF) neutron-12.1.1/neutron/plugins/ml2/drivers/agent/__init__.py0000664000175000017500000000000013553660046024040 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/agent/capabilities.py0000664000175000017500000000221113553660046024740 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events from neutron_lib.callbacks import registry def notify_init_event(agent_type, agent): """Notify init event for the specified agent.""" registry.publish(agent_type, events.AFTER_INIT, agent) def register(callback, agent_type): """Subscribe callback to init event for the specified agent. :param agent_type: an agent type as defined in neutron_lib.constants. :param callback: a callback that can process the agent init event. """ registry.subscribe(callback, agent_type, events.AFTER_INIT) neutron-12.1.1/neutron/plugins/ml2/drivers/agent/_common_agent.py0000664000175000017500000005122213553660047025123 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import contextlib import sys import time from neutron_lib.agent import constants as agent_consts from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources as local_resources from neutron_lib import constants from neutron_lib import context from oslo_config import cfg from oslo_log import log as logging from oslo_service import loopingcall from oslo_service import service from oslo_utils import excutils from osprofiler import profiler from neutron.agent.l2 import l2_agent_extensions_manager as ext_manager from neutron.agent import rpc as agent_rpc from neutron.agent import securitygroups_rpc as agent_sg_rpc from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import securitygroups_rpc as sg_rpc from neutron.common import config as common_config from neutron.common import topics from neutron.plugins.ml2.drivers.agent import _agent_manager_base as amb from neutron.plugins.ml2.drivers.agent import capabilities from neutron.plugins.ml2.drivers.agent import config as cagt_config # noqa LOG = logging.getLogger(__name__) @profiler.trace_cls("rpc") class CommonAgentLoop(service.Service): def __init__(self, manager, polling_interval, quitting_rpc_timeout, agent_type, agent_binary): """Constructor. :param manager: the manager object containing the impl specifics :param polling_interval: interval (secs) to poll DB. :param quitting_rpc_timeout: timeout in seconds for rpc calls after stop is called. :param agent_type: Specifies the type of the agent :param agent_binary: The agent binary string """ super(CommonAgentLoop, self).__init__() self.mgr = manager self._validate_manager_class() self.polling_interval = polling_interval self.quitting_rpc_timeout = quitting_rpc_timeout self.agent_type = agent_type self.agent_binary = agent_binary def _validate_manager_class(self): if not isinstance(self.mgr, amb.CommonAgentManagerBase): LOG.error("Manager class must inherit from " "CommonAgentManagerBase to ensure CommonAgent " "works properly.") sys.exit(1) def start(self): # stores all configured ports on agent self.network_ports = collections.defaultdict(list) # flag to do a sync after revival self.fullsync = False self.context = context.get_admin_context_without_session() self.setup_rpc() self.init_extension_manager(self.connection) configurations = {'extensions': self.ext_manager.names()} configurations.update(self.mgr.get_agent_configurations()) #TODO(mangelajo): optimize resource_versions (see ovs agent) self.agent_state = { 'binary': self.agent_binary, 'host': cfg.CONF.host, 'topic': constants.L2_AGENT_TOPIC, 'configurations': configurations, 'agent_type': self.agent_type, 'resource_versions': resources.LOCAL_RESOURCE_VERSIONS, 'start_flag': True} report_interval = cfg.CONF.AGENT.report_interval if report_interval: heartbeat = loopingcall.FixedIntervalLoopingCall( self._report_state) heartbeat.start(interval=report_interval) capabilities.notify_init_event(self.agent_type, self) # The initialization is complete; we can start receiving messages self.connection.consume_in_threads() self.daemon_loop() def stop(self, graceful=True): LOG.info("Stopping %s agent.", self.agent_type) if graceful and self.quitting_rpc_timeout: self.set_rpc_timeout(self.quitting_rpc_timeout) super(CommonAgentLoop, self).stop(graceful) def reset(self): common_config.setup_logging() def _report_state(self): try: devices = len(self.mgr.get_all_devices()) self.agent_state.get('configurations')['devices'] = devices agent_status = self.state_rpc.report_state(self.context, self.agent_state, True) if agent_status == agent_consts.AGENT_REVIVED: LOG.info('%s Agent has just been revived. ' 'Doing a full sync.', self.agent_type) self.fullsync = True # we only want to update resource versions on startup self.agent_state.pop('resource_versions', None) self.agent_state.pop('start_flag', None) except Exception: LOG.exception("Failed reporting state!") def _validate_rpc_endpoints(self): if not isinstance(self.endpoints[0], amb.CommonAgentManagerRpcCallBackBase): LOG.error("RPC Callback class must inherit from " "CommonAgentManagerRpcCallBackBase to ensure " "CommonAgent works properly.") sys.exit(1) def setup_rpc(self): self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN) self.sg_plugin_rpc = sg_rpc.SecurityGroupServerRpcApi(topics.PLUGIN) self.sg_agent = agent_sg_rpc.SecurityGroupAgentRpc( self.context, self.sg_plugin_rpc, defer_refresh_firewall=True) self.agent_id = self.mgr.get_agent_id() LOG.info("RPC agent_id: %s", self.agent_id) self.topic = topics.AGENT self.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS) # RPC network init # Handle updates from service self.rpc_callbacks = self.mgr.get_rpc_callbacks(self.context, self, self.sg_agent) self.endpoints = [self.rpc_callbacks] self._validate_rpc_endpoints() # Define the listening consumers for the agent consumers = self.mgr.get_rpc_consumers() self.connection = agent_rpc.create_consumers(self.endpoints, self.topic, consumers, start_listening=False) def init_extension_manager(self, connection): ext_manager.register_opts(cfg.CONF) self.ext_manager = ( ext_manager.L2AgentExtensionsManager(cfg.CONF)) agent_api = self.mgr.get_agent_api(sg_agent=self.sg_agent) self.ext_manager.initialize( connection, self.mgr.get_extension_driver_type(), agent_api) def _clean_network_ports(self, device): for netid, ports_list in self.network_ports.items(): for port_data in ports_list: if device == port_data['device']: ports_list.remove(port_data) if ports_list == []: self.network_ports.pop(netid) return port_data['port_id'] def _update_network_ports(self, network_id, port_id, device): self._clean_network_ports(device) self.network_ports[network_id].append({ "port_id": port_id, "device": device }) def process_network_devices(self, device_info): resync_a = False resync_b = False self.sg_agent.setup_port_filters(device_info.get('added'), device_info.get('updated')) # Updated devices are processed the same as new ones, as their # admin_state_up may have changed. The set union prevents duplicating # work when a device is new and updated in the same polling iteration. devices_added_updated = (set(device_info.get('added')) | set(device_info.get('updated'))) if devices_added_updated: resync_a = self.treat_devices_added_updated(devices_added_updated) if device_info.get('removed'): resync_b = self.treat_devices_removed(device_info['removed']) # If one of the above operations fails => resync with plugin return (resync_a | resync_b) def treat_devices_added_updated(self, devices): try: devices_details_list = self.plugin_rpc.get_devices_details_list( self.context, devices, self.agent_id, host=cfg.CONF.host) except Exception: LOG.exception("Unable to get port details for %s", devices) # resync is needed return True for device_details in devices_details_list: self._process_device_if_exists(device_details) # no resync is needed return False def _process_device_if_exists(self, device_details): # ignore exceptions from devices that disappear because they will # be handled as removed in the next iteration device = device_details['device'] with self._ignore_missing_device_exceptions(device): LOG.debug("Port %s added", device) if 'port_id' in device_details: LOG.info("Port %(device)s updated. Details: %(details)s", {'device': device, 'details': device_details}) self.mgr.setup_arp_spoofing_protection(device, device_details) segment = amb.NetworkSegment( device_details.get('network_type'), device_details['physical_network'], device_details.get('segmentation_id'), device_details.get('mtu') ) network_id = device_details['network_id'] self.rpc_callbacks.add_network(network_id, segment) interface_plugged = self.mgr.plug_interface( network_id, segment, device, device_details['device_owner']) # REVISIT(scheuran): Changed the way how ports admin_state_up # is implemented. # # Old lb implementation: # - admin_state_up: ensure that tap is plugged into bridge # - admin_state_down: remove tap from bridge # New lb implementation: # - admin_state_up: set tap device state to up # - admin_state_down: set tap device state to down # # However both approaches could result in races with # nova/libvirt and therefore to an invalid system state in the # scenario, where an instance is booted with a port configured # with admin_state_up = False: # # Libvirt does the following actions in exactly # this order (see libvirt virnetdevtap.c) # 1) Create the tap device, set its MAC and MTU # 2) Plug the tap into the bridge # 3) Set the tap online # # Old lb implementation: # A race could occur, if the lb agent removes the tap device # right after step 1). Then libvirt will add it to the bridge # again in step 2). # New lb implementation: # The race could occur if the lb-agent sets the taps device # state to down right after step 2). In step 3) libvirt # might set it to up again. # # This is not an issue if an instance is booted with a port # configured with admin_state_up = True. Libvirt would just # set the tap device up again. # # This refactoring is recommended for the following reasons: # 1) An existing race with libvirt caused by the behavior of # the old implementation. See Bug #1312016 # 2) The new code is much more readable if interface_plugged: self.mgr.ensure_port_admin_state( device, device_details['admin_state_up']) # update plugin about port status if admin_state is up if device_details['admin_state_up']: if interface_plugged: self.plugin_rpc.update_device_up(self.context, device, self.agent_id, cfg.CONF.host) else: self.plugin_rpc.update_device_down(self.context, device, self.agent_id, cfg.CONF.host) self._update_network_ports(device_details['network_id'], device_details['port_id'], device_details['device']) self.ext_manager.handle_port(self.context, device_details) registry.notify(local_resources.PORT_DEVICE, events.AFTER_UPDATE, self, context=self.context, device_details=device_details) else: LOG.info("Device %s not defined on plugin", device) @contextlib.contextmanager def _ignore_missing_device_exceptions(self, device): try: yield except Exception: with excutils.save_and_reraise_exception() as ectx: if device not in self.mgr.get_all_devices(): ectx.reraise = False LOG.debug("%s was removed during processing.", device) def treat_devices_removed(self, devices): resync = False self.sg_agent.remove_devices_filter(devices) for device in devices: LOG.info("Attachment %s removed", device) details = None try: details = self.plugin_rpc.update_device_down(self.context, device, self.agent_id, cfg.CONF.host) except Exception: LOG.exception("Error occurred while removing port %s", device) resync = True if details and details['exists']: LOG.info("Port %s updated.", device) else: LOG.debug("Device %s not defined on plugin", device) port_id = self._clean_network_ports(device) try: self.ext_manager.delete_port(self.context, {'device': device, 'port_id': port_id}) except Exception: LOG.exception("Error occurred while processing extensions " "for port removal %s", device) resync = True registry.notify(local_resources.PORT_DEVICE, events.AFTER_DELETE, self, context=self.context, device=device, port_id=port_id) self.mgr.delete_arp_spoofing_protection(devices) return resync @staticmethod def _get_devices_locally_modified(timestamps, previous_timestamps): """Returns devices with previous timestamps that do not match new. If a device did not have a timestamp previously, it will not be returned because this means it is new. """ return {device for device, timestamp in timestamps.items() if previous_timestamps.get(device) and timestamp != previous_timestamps.get(device)} def scan_devices(self, previous, sync): device_info = {} updated_devices = self.rpc_callbacks.get_and_clear_updated_devices() current_devices = self.mgr.get_all_devices() device_info['current'] = current_devices if previous is None: # This is the first iteration of daemon_loop(). previous = {'added': set(), 'current': set(), 'updated': set(), 'removed': set(), 'timestamps': {}} # clear any orphaned ARP spoofing rules (e.g. interface was # manually deleted) self.mgr.delete_unreferenced_arp_protection(current_devices) # check to see if any devices were locally modified based on their # timestamps changing since the previous iteration. If a timestamp # doesn't exist for a device, this calculation is skipped for that # device. device_info['timestamps'] = self.mgr.get_devices_modified_timestamps( current_devices) locally_updated = self._get_devices_locally_modified( device_info['timestamps'], previous['timestamps']) if locally_updated: LOG.debug("Adding locally changed devices to updated set: %s", locally_updated) updated_devices |= locally_updated if sync: # This is the first iteration, or the previous one had a problem. # Re-add all existing devices. device_info['added'] = current_devices # Retry cleaning devices that may not have been cleaned properly. # And clean any that disappeared since the previous iteration. device_info['removed'] = (previous['removed'] | previous['current'] - current_devices) # Retry updating devices that may not have been updated properly. # And any that were updated since the previous iteration. # Only update devices that currently exist. device_info['updated'] = (previous['updated'] | updated_devices & current_devices) else: device_info['added'] = current_devices - previous['current'] device_info['removed'] = previous['current'] - current_devices device_info['updated'] = updated_devices & current_devices return device_info def _device_info_has_changes(self, device_info): return (device_info.get('added') or device_info.get('updated') or device_info.get('removed')) def daemon_loop(self): LOG.info("%s Agent RPC Daemon Started!", self.agent_type) device_info = None sync = True while True: start = time.time() if self.fullsync: sync = True self.fullsync = False if sync: LOG.info("%s Agent out of sync with plugin!", self.agent_type) device_info = self.scan_devices(previous=device_info, sync=sync) sync = False if (self._device_info_has_changes(device_info) or self.sg_agent.firewall_refresh_needed()): LOG.debug("Agent loop found changes! %s", device_info) try: sync = self.process_network_devices(device_info) except Exception: LOG.exception("Error in agent loop. Devices info: %s", device_info) sync = True # sleep till end of polling interval elapsed = (time.time() - start) if (elapsed < self.polling_interval): time.sleep(self.polling_interval - elapsed) else: LOG.debug("Loop iteration exceeded interval " "(%(polling_interval)s vs. %(elapsed)s)!", {'polling_interval': self.polling_interval, 'elapsed': elapsed}) def set_rpc_timeout(self, timeout): for rpc_api in (self.plugin_rpc, self.sg_plugin_rpc, self.state_rpc): rpc_api.client.timeout = timeout neutron-12.1.1/neutron/plugins/ml2/drivers/agent/_agent_manager_base.py0000664000175000017500000001700413553660046026236 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import six class NetworkSegment(object): """Represents a Neutron network segment""" def __init__(self, network_type, physical_network, segmentation_id, mtu=None): self.network_type = network_type self.physical_network = physical_network self.segmentation_id = segmentation_id self.mtu = mtu @six.add_metaclass(abc.ABCMeta) class CommonAgentManagerRpcCallBackBase(object): """Base class for managers RPC callbacks. This class must be inherited by a RPC callback class that is used in combination with the common agent. """ def __init__(self, context, agent, sg_agent): self.context = context self.agent = agent self.sg_agent = sg_agent self.network_map = {} # stores received port_updates and port_deletes for # processing by the main loop self.updated_devices = set() @abc.abstractmethod def security_groups_rule_updated(self, context, **kwargs): """Callback for security group rule update. :param security_groups: list of updated security_groups """ @abc.abstractmethod def security_groups_member_updated(self, context, **kwargs): """Callback for security group member update. :param security_groups: list of updated security_groups """ def add_network(self, network_id, network_segment): """Add a network to the agent internal network list :param network_id: The UUID of the network :param network_segment: The NetworkSegment object for this network """ self.network_map[network_id] = network_segment def get_and_clear_updated_devices(self): """Get and clear the list of devices for which a update was received. :return: set - A set with updated devices. Format is ['tap1', 'tap2'] """ # Save and reinitialize the set variable that the port_update RPC uses. # This should be thread-safe as the greenthread should not yield # between these two statements. updated_devices = self.updated_devices self.updated_devices = set() return updated_devices @six.add_metaclass(abc.ABCMeta) class CommonAgentManagerBase(object): """Base class for managers that are used with the common agent loop. This class must be inherited by a manager class that is used in combination with the common agent. """ @abc.abstractmethod def ensure_port_admin_state(self, device, admin_state_up): """Enforce admin_state for a port :param device: The device for which the admin_state should be set :param admin_state_up: True for admin_state_up, False for admin_state_down """ @abc.abstractmethod def get_agent_configurations(self): """Establishes the agent configuration map. The content of this map is part of the agent state reports to the neutron server. :return: map -- the map containing the configuration values :rtype: dict """ @abc.abstractmethod def get_agent_id(self): """Calculate the agent id that should be used on this host :return: str -- agent identifier """ @abc.abstractmethod def get_all_devices(self): """Get a list of all devices of the managed type from this host A device in this context is a String that represents a network device. This can for example be the name of the device or its MAC address. This value will be stored in the Plug-in and be part of the device_details. Typically this list is retrieved from the sysfs. E.g. for linuxbridge it returns all names of devices of type 'tap' that start with a certain prefix. :return: set -- the set of all devices e.g. ['tap1', 'tap2'] """ @abc.abstractmethod def get_devices_modified_timestamps(self, devices): """Get a dictionary of modified timestamps by device The devices passed in are expected to be the same format that get_all_devices returns. :return: dict -- A dictionary of timestamps keyed by device """ @abc.abstractmethod def get_extension_driver_type(self): """Get the agent extension driver type. :return: str -- The String defining the agent extension type """ @abc.abstractmethod def get_rpc_callbacks(self, context, agent, sg_agent): """Returns the class containing all the agent rpc callback methods :return: class - the class containing the agent rpc callback methods. It must reflect the CommonAgentManagerRpcCallBackBase Interface. """ @abc.abstractmethod def get_agent_api(self, **kwargs): """Get L2 extensions drivers API interface class. :return: instance of the class containing Agent Extension API """ @abc.abstractmethod def get_rpc_consumers(self): """Get a list of topics for which an RPC consumer should be created :return: list -- A list of topics. Each topic in this list is a list consisting of a name, an operation, and an optional host param keying the subscription to topic.host for plugin calls. """ @abc.abstractmethod def plug_interface(self, network_id, network_segment, device, device_owner): """Plug the interface (device). :param network_id: The UUID of the Neutron network :param network_segment: The NetworkSegment object for this network :param device: The device that should be plugged :param device_owner: The device owner of the port :return: bool -- True if the interface is plugged now. False if the interface could not be plugged. """ @abc.abstractmethod def setup_arp_spoofing_protection(self, device, device_details): """Setup the arp spoofing protection for the given port. :param device: The device to set up arp spoofing rules for, where device is the device String that is stored in the Neutron Plug-in for this Port. E.g. 'tap1' :param device_details: The device_details map retrieved from the Neutron Plugin """ @abc.abstractmethod def delete_arp_spoofing_protection(self, devices): """Remove the arp spoofing protection for the given ports. :param devices: List of devices that have been removed, where device is the device String that is stored for this port in the Neutron Plug-in. E.g. ['tap1', 'tap2'] """ @abc.abstractmethod def delete_unreferenced_arp_protection(self, current_devices): """Cleanup arp spoofing protection entries. :param current_devices: List of devices that currently exist on this host, where device is the device String that could have been stored in the Neutron Plug-in. E.g. ['tap1', 'tap2'] """ neutron-12.1.1/neutron/plugins/ml2/drivers/type_flat.py0000664000175000017500000001207713553660047023214 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as p_const from neutron_lib import exceptions as exc from neutron_lib.objects import exceptions as obj_base from neutron_lib.plugins.ml2 import api from oslo_config import cfg from oslo_log import log from neutron._i18n import _ from neutron.common import exceptions as n_exc from neutron.conf.plugins.ml2.drivers import driver_type from neutron.db import api as db_api from neutron.objects.plugins.ml2 import flatallocation as flat_obj from neutron.plugins.ml2.drivers import helpers LOG = log.getLogger(__name__) driver_type.register_ml2_drivers_flat_opts() class FlatTypeDriver(helpers.BaseTypeDriver): """Manage state for flat networks with ML2. The FlatTypeDriver implements the 'flat' network_type. Flat network segments provide connectivity between VMs and other devices using any connected IEEE 802.1D conformant physical_network, without the use of VLAN tags, tunneling, or other segmentation mechanisms. Therefore at most one flat network segment can exist on each available physical_network. """ def __init__(self): super(FlatTypeDriver, self).__init__() self._parse_networks(cfg.CONF.ml2_type_flat.flat_networks) def _parse_networks(self, entries): self.flat_networks = entries if '*' in self.flat_networks: LOG.info("Arbitrary flat physical_network names allowed") self.flat_networks = None elif not self.flat_networks: LOG.info("Flat networks are disabled") else: LOG.info("Allowable flat physical_network names: %s", self.flat_networks) def get_type(self): return p_const.TYPE_FLAT def initialize(self): LOG.info("ML2 FlatTypeDriver initialization complete") def is_partial_segment(self, segment): return False def validate_provider_segment(self, segment): physical_network = segment.get(api.PHYSICAL_NETWORK) if not physical_network: msg = _("physical_network required for flat provider network") raise exc.InvalidInput(error_message=msg) if self.flat_networks is not None and not self.flat_networks: msg = _("Flat provider networks are disabled") raise exc.InvalidInput(error_message=msg) if self.flat_networks and physical_network not in self.flat_networks: msg = (_("physical_network '%s' unknown for flat provider network") % physical_network) raise exc.InvalidInput(error_message=msg) for key, value in segment.items(): if value and key not in [api.NETWORK_TYPE, api.PHYSICAL_NETWORK]: msg = _("%s prohibited for flat provider network") % key raise exc.InvalidInput(error_message=msg) def reserve_provider_segment(self, context, segment): physical_network = segment[api.PHYSICAL_NETWORK] try: LOG.debug("Reserving flat network on physical " "network %s", physical_network) alloc = flat_obj.FlatAllocation( context, physical_network=physical_network) alloc.create() except obj_base.NeutronDbObjectDuplicateEntry: raise n_exc.FlatNetworkInUse( physical_network=physical_network) segment[api.MTU] = self.get_mtu(alloc.physical_network) return segment def allocate_tenant_segment(self, context): # Tenant flat networks are not supported. return def release_segment(self, context, segment): physical_network = segment[api.PHYSICAL_NETWORK] with db_api.context_manager.writer.using(context): obj = flat_obj.FlatAllocation.get_object( context, physical_network=physical_network) if obj: obj.delete() LOG.debug("Releasing flat network on physical network %s", physical_network) else: LOG.warning( "No flat network found on physical network %s", physical_network) def get_mtu(self, physical_network): seg_mtu = super(FlatTypeDriver, self).get_mtu() mtu = [] if seg_mtu > 0: mtu.append(seg_mtu) if physical_network in self.physnet_mtus: mtu.append(int(self.physnet_mtus[physical_network])) return min(mtu) if mtu else 0 neutron-12.1.1/neutron/plugins/ml2/drivers/macvtap/0000775000175000017500000000000013553660156022300 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/macvtap/agent/0000775000175000017500000000000013553660156023376 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/macvtap/agent/macvtap_neutron_agent.py0000664000175000017500000002101413553660047030330 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import sys from neutron_lib import constants from neutron_lib.utils import helpers from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_service import service from neutron.agent.linux import ip_lib from neutron.api.rpc.handlers import securitygroups_rpc as sg_rpc from neutron.common import config as common_config from neutron.common import topics from neutron.conf.plugins.ml2.drivers import macvtap as config from neutron.plugins.ml2.drivers.agent import _agent_manager_base as amb from neutron.plugins.ml2.drivers.agent import _common_agent as ca from neutron.plugins.ml2.drivers.macvtap import macvtap_common LOG = logging.getLogger(__name__) MACVTAP_AGENT_BINARY = "neutron-macvtap-agent" MACVTAP_FS = "/sys/class/net/" EXTENSION_DRIVER_TYPE = 'macvtap' config.register_macvtap_opts() class MacvtapRPCCallBack(sg_rpc.SecurityGroupAgentRpcCallbackMixin, amb.CommonAgentManagerRpcCallBackBase): # Set RPC API version to 1.0 by default. # history # 1.1 Support Security Group RPC # 1.3 Added param devices_to_update to security_groups_provider_updated # 1.4 Added support for network_update target = oslo_messaging.Target(version='1.4') def network_delete(self, context, **kwargs): LOG.debug("network_delete received") network_id = kwargs.get('network_id') if network_id not in self.network_map: LOG.error("Network %s is not available.", network_id) return segment = self.network_map.get(network_id) if segment and segment.network_type == constants.TYPE_VLAN: if_mappings = self.agent.mgr.interface_mappings vlan_device_name = macvtap_common.get_vlan_device_name( if_mappings[segment.physical_network], str(segment.segmentation_id)) ip_dev = ip_lib.IPDevice(vlan_device_name) if ip_dev.exists(): LOG.debug("Delete %s", ip_dev.name) ip_dev.link.delete() else: LOG.debug("Cannot delete vlan device %s; it does not exist", vlan_device_name) def port_update(self, context, **kwargs): port = kwargs['port'] LOG.debug("port_update received for port %s ", port) mac = port['mac_address'] # Put the device name in the updated_devices set. # Do not store port details, as if they're used for processing # notifications there is no guarantee the notifications are # processed in the same order as the relevant API requests. self.updated_devices.add(mac) class MacvtapManager(amb.CommonAgentManagerBase): def __init__(self, interface_mappings): self.interface_mappings = interface_mappings self.validate_interface_mappings() self.mac_device_name_mappings = dict() def validate_interface_mappings(self): for physnet, interface in self.interface_mappings.items(): if not ip_lib.device_exists(interface): LOG.error("Interface %(intf)s for physical network " "%(net)s does not exist. Agent terminated!", {'intf': interface, 'net': physnet}) sys.exit(1) def ensure_port_admin_state(self, device, admin_state_up): LOG.debug("Setting admin_state_up to %s for device %s", admin_state_up, device) dev = ip_lib.IPDevice(self.mac_device_name_mappings[device]) if admin_state_up: dev.link.set_up() else: dev.link.set_down() def get_agent_configurations(self): return {'interface_mappings': self.interface_mappings} def get_agent_id(self): devices = ip_lib.IPWrapper().get_devices(True) if devices: mac = ip_lib.get_device_mac(devices[0].name) return 'macvtap%s' % mac.replace(":", "") else: LOG.error("Unable to obtain MAC address for unique ID. " "Agent terminated!") sys.exit(1) def get_devices_modified_timestamps(self, devices): # TODO(kevinbenton): this should be implemented to detect # rapid Nova instance rebuilds. return {} def get_all_devices(self): devices = set() all_device_names = os.listdir(MACVTAP_FS) # Refresh the mac_device_name mapping self.mac_device_name_mappings = dict() for device_name in all_device_names: if device_name.startswith(constants.MACVTAP_DEVICE_PREFIX): mac = ip_lib.get_device_mac(device_name) self.mac_device_name_mappings[mac] = device_name devices.add(mac) return devices def get_extension_driver_type(self): return EXTENSION_DRIVER_TYPE def get_rpc_callbacks(self, context, agent, sg_agent): return MacvtapRPCCallBack(context, agent, sg_agent) def get_agent_api(self, **kwargs): pass def get_rpc_consumers(self): consumers = [[topics.PORT, topics.UPDATE], [topics.NETWORK, topics.DELETE], [topics.SECURITY_GROUP, topics.UPDATE]] return consumers def plug_interface(self, network_id, network_segment, device, device_owner): # Setting ALLMULTICAST Flag on macvtap device to allow the guest # receiving traffic for arbitrary multicast addresses. # The alternative would be to let libvirt instantiate the macvtap # device with the 'trustGuestRxFilters' option. But doing so, the guest # would be able to change its mac address and therefore the mac # address of the macvtap device. dev = ip_lib.IPDevice(self.mac_device_name_mappings[device]) dev.link.set_allmulticast_on() return True def setup_arp_spoofing_protection(self, device, device_details): pass def delete_arp_spoofing_protection(self, devices): pass def delete_unreferenced_arp_protection(self, current_devices): pass def parse_interface_mappings(): if not cfg.CONF.macvtap.physical_interface_mappings: LOG.error("No physical_interface_mappings provided, but at least " "one mapping is required. Agent terminated!") sys.exit(1) try: interface_mappings = helpers.parse_mappings( cfg.CONF.macvtap.physical_interface_mappings) LOG.info("Interface mappings: %s", interface_mappings) return interface_mappings except ValueError as e: LOG.error("Parsing physical_interface_mappings failed: %s. " "Agent terminated!", e) sys.exit(1) def validate_firewall_driver(): fw_driver = cfg.CONF.SECURITYGROUP.firewall_driver supported_fw_drivers = ['neutron.agent.firewall.NoopFirewallDriver', 'noop'] if fw_driver not in supported_fw_drivers: LOG.error('Unsupported configuration option for "SECURITYGROUP.' 'firewall_driver"! Only the NoopFirewallDriver is ' 'supported by macvtap agent, but "%s" is configured. ' 'Set the firewall_driver to "noop" and start the ' 'agent again. Agent terminated!', fw_driver) sys.exit(1) def main(): common_config.init(sys.argv[1:]) common_config.setup_logging() validate_firewall_driver() interface_mappings = parse_interface_mappings() manager = MacvtapManager(interface_mappings) polling_interval = cfg.CONF.AGENT.polling_interval quitting_rpc_timeout = cfg.CONF.AGENT.quitting_rpc_timeout agent = ca.CommonAgentLoop(manager, polling_interval, quitting_rpc_timeout, constants.AGENT_TYPE_MACVTAP, MACVTAP_AGENT_BINARY) LOG.info("Agent initialized successfully, now running... ") launcher = service.launch(cfg.CONF, agent) launcher.wait() neutron-12.1.1/neutron/plugins/ml2/drivers/macvtap/agent/__init__.py0000664000175000017500000000000013553660046025473 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/macvtap/macvtap_common.py0000664000175000017500000000217313553660047025657 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as n_const from neutron.plugins.common import utils as p_utils MAX_VLAN_POSTFIX_LEN = 5 def get_vlan_device_name(src_dev, vlan): """Generating the vlan device name.""" # Ensure that independent of the vlan len the same name prefix is used. src_dev = p_utils.get_interface_name(src_dev, max_len=n_const.DEVICE_NAME_MAX_LEN - MAX_VLAN_POSTFIX_LEN) return "%s.%s" % (src_dev, vlan) neutron-12.1.1/neutron/plugins/ml2/drivers/macvtap/__init__.py0000664000175000017500000000000013553660046024375 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/macvtap/mech_driver/0000775000175000017500000000000013553660156024567 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/macvtap/mech_driver/__init__.py0000664000175000017500000000000013553660046026664 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/macvtap/mech_driver/mech_macvtap.py0000664000175000017500000001326513553660047027576 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib.plugins.ml2 import api from oslo_log import log from neutron.plugins.ml2.drivers.macvtap import macvtap_common from neutron.plugins.ml2.drivers import mech_agent LOG = log.getLogger(__name__) MACVTAP_MODE_BRIDGE = 'bridge' class MacvtapMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """Attach to networks using Macvtap L2 agent. The MacvtapMechanismDriver integrates the ml2 plugin with the macvtap L2 agent. Port binding with this driver requires the macvtap agent to be running on the port's host, and that agent to have connectivity to at least one segment of the port's network. """ def __init__(self): super(MacvtapMechanismDriver, self).__init__( constants.AGENT_TYPE_MACVTAP, portbindings.VIF_TYPE_MACVTAP, {portbindings.CAP_PORT_FILTER: False}) def get_allowed_network_types(self, agent): return [constants.TYPE_FLAT, constants.TYPE_VLAN] def get_mappings(self, agent): return agent['configurations'].get('interface_mappings', {}) def check_vlan_transparency(self, context): """Macvtap driver vlan transparency support.""" return False def _is_live_migration(self, context): # We cannot just check if # context.original['host_id'] != context.current['host_id'] # This condition is also true, if nova does a reschedule of a # instance when something went wrong during spawn. In this case, # context.original['host_id'] is set to the failed host. # The only safe way to detect a migration is to look into the binding # profiles 'migrating_to' attribute, which is set by Nova since patch # https://review.openstack.org/#/c/275073/. if not context.original: # new port return False port_profile = context.original.get(portbindings.PROFILE) if port_profile and port_profile.get('migrating_to', None): LOG.debug("Live migration with profile %s detected.", port_profile) return True else: return False def try_to_bind_segment_for_agent(self, context, segment, agent): if self.check_segment_for_agent(segment, agent): vif_details_segment = self.vif_details mappings = self.get_mappings(agent) interface = mappings[segment['physical_network']] network_type = segment[api.NETWORK_TYPE] if network_type == constants.TYPE_VLAN: vlan_id = segment[api.SEGMENTATION_ID] macvtap_src = macvtap_common.get_vlan_device_name(interface, vlan_id) vif_details_segment['vlan'] = vlan_id else: macvtap_src = interface if self._is_live_migration(context): # We can use the original port here, as during live migration # portbinding is done after the migration happened. Nova will # not do a reschedule of the instance migration if binding # fails, but just set the instance into error state. # Due to that we can be sure that the original port is the # migration source port. orig_vif_details = context.original[portbindings.VIF_DETAILS] orig_source = orig_vif_details[ portbindings.VIF_DETAILS_MACVTAP_SOURCE] if orig_source != macvtap_src: source_host = context.original[portbindings.HOST_ID] target_host = agent['host'] LOG.error("Vif binding denied by mechanism driver. " "MacVTap source device '%(target_dev)s' on " "the migration target '%(target_host)s'is " "not equal to device '%(source_dev)s' on " "the migration source '%(source_host)s. " "Make sure that the " "interface mapping of macvtap " "agent on both hosts is equal " "for the physical network '%(physnet)s'!", {'source_dev': orig_source, 'target_dev': macvtap_src, 'target_host': target_host, 'source_host': source_host, 'physnet': segment['physical_network']}) return False vif_details_segment['physical_interface'] = interface vif_details_segment['macvtap_source'] = macvtap_src vif_details_segment['macvtap_mode'] = MACVTAP_MODE_BRIDGE LOG.debug("Macvtap vif_details added to context binding: %s", vif_details_segment) context.set_binding(segment[api.ID], self.vif_type, vif_details_segment) return True return False neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/0000775000175000017500000000000013553660156023216 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/0000775000175000017500000000000013553660156024314 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/vlanmanager.py0000664000175000017500000000710413553660047027162 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions from neutron._i18n import _ class VifIdNotFound(exceptions.NeutronException): message = _('VIF ID %(vif_id)s not found in any network managed by ' 'VLAN Manager') class MappingAlreadyExists(exceptions.NeutronException): message = _('VLAN mapping for network with id %(net_id)s already exists') class MappingNotFound(exceptions.NeutronException): message = _('Mapping for network %(net_id)s not found.') class LocalVLANMapping(object): def __init__(self, vlan, network_type, physical_network, segmentation_id, vif_ports=None): self.vlan = vlan self.network_type = network_type self.physical_network = physical_network self.segmentation_id = segmentation_id self.vif_ports = vif_ports or {} # set of tunnel ports on which packets should be flooded self.tun_ofports = set() def __str__(self): return ("lv-id = %s type = %s phys-net = %s phys-id = %s" % (self.vlan, self.network_type, self.physical_network, self.segmentation_id)) def __eq__(self, other): return all(hasattr(other, a) and getattr(self, a) == getattr(other, a) for a in ['vlan', 'network_type', 'physical_network', 'segmentation_id', 'vif_ports']) def __hash__(self): return id(self) class LocalVlanManager(object): """Singleton manager that maps internal VLAN mapping to external network segmentation ids. """ def __new__(cls): if not hasattr(cls, '_instance'): cls._instance = super(LocalVlanManager, cls).__new__(cls) return cls._instance def __init__(self): if not hasattr(self, 'mapping'): self.mapping = {} def __contains__(self, key): return key in self.mapping def __iter__(self): for value in list(self.mapping.values()): yield value def items(self): for item in self.mapping.items(): yield item def add(self, net_id, vlan, network_type, physical_network, segmentation_id, vif_ports=None): if net_id in self.mapping: raise MappingAlreadyExists(net_id=net_id) self.mapping[net_id] = LocalVLANMapping( vlan, network_type, physical_network, segmentation_id, vif_ports) def get_net_uuid(self, vif_id): for network_id, vlan_mapping in self.mapping.items(): if vif_id in vlan_mapping.vif_ports: return network_id raise VifIdNotFound(vif_id=vif_id) def get(self, net_id): try: return self.mapping[net_id] except KeyError: raise MappingNotFound(net_id=net_id) def pop(self, net_id): try: return self.mapping.pop(net_id) except KeyError: raise MappingNotFound(net_id=net_id) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/0000775000175000017500000000000013553660156026145 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/__init__.py0000664000175000017500000000000013553660046030242 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/br_cookie.py0000664000175000017500000000411013553660046030445 0ustar zuulzuul00000000000000# Copyright 2016 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from neutron.agent.common import ovs_lib class OVSBridgeCookieMixin(object): '''Mixin to provide cookie retention functionality to the OVSAgentBridge ''' def __init__(self, *args, **kwargs): super(OVSBridgeCookieMixin, self).__init__(*args, **kwargs) self._reserved_cookies = set() @property def reserved_cookies(self): if self._default_cookie not in self._reserved_cookies: self._reserved_cookies.add(self._default_cookie) return set(self._reserved_cookies) def request_cookie(self): if self._default_cookie not in self._reserved_cookies: self._reserved_cookies.add(self._default_cookie) uuid_stamp = ovs_lib.generate_random_cookie() while uuid_stamp in self._reserved_cookies: uuid_stamp = ovs_lib.generate_random_cookie() self._reserved_cookies.add(uuid_stamp) return uuid_stamp def unset_cookie(self, cookie): self._reserved_cookies.discard(cookie) def set_agent_uuid_stamp(self, val): self._reserved_cookies.add(val) if self._default_cookie in self._reserved_cookies: self._reserved_cookies.remove(self._default_cookie) super(OVSBridgeCookieMixin, self).set_agent_uuid_stamp(val) def clone(self): '''Used by OVSCookieBridge, can be overridden by subclasses if a behavior different from copy.copy is needed. ''' return copy.copy(self) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/0000775000175000017500000000000013553660156030143 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_dvr_process.py0000664000175000017500000000731613553660047033537 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # Copyright 2011 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants class OVSDVRProcessMixin(object): """Common logic for br-tun and br-phys' DVR_PROCESS tables. Inheriters should provide self.dvr_process_table_id and self.dvr_process_next_table_id. """ def install_dvr_process_ipv4(self, vlan_tag, gateway_ip): # block ARP self.add_flow(table=self.dvr_process_table_id, priority=3, dl_vlan=vlan_tag, proto='arp', nw_dst=gateway_ip, actions='drop') def delete_dvr_process_ipv4(self, vlan_tag, gateway_ip): self.delete_flows(table=self.dvr_process_table_id, dl_vlan=vlan_tag, proto='arp', nw_dst=gateway_ip) def install_dvr_process_ipv6(self, vlan_tag, gateway_mac): # block RA self.add_flow(table=self.dvr_process_table_id, priority=3, dl_vlan=vlan_tag, proto='icmp6', icmp_type=constants.ICMPV6_TYPE_RA, dl_src=gateway_mac, actions='drop') def delete_dvr_process_ipv6(self, vlan_tag, gateway_mac): self.delete_flows(table=self.dvr_process_table_id, dl_vlan=vlan_tag, proto='icmp6', icmp_type=constants.ICMPV6_TYPE_RA, dl_src=gateway_mac) def install_dvr_process(self, vlan_tag, vif_mac, dvr_mac_address): self.add_flow(table=self.dvr_process_table_id, priority=2, dl_vlan=vlan_tag, dl_dst=vif_mac, actions="drop") self.add_flow(table=self.dvr_process_table_id, priority=1, dl_vlan=vlan_tag, dl_src=vif_mac, actions="mod_dl_src:%s,resubmit(,%s)" % (dvr_mac_address, self.dvr_process_next_table_id)) def delete_dvr_process(self, vlan_tag, vif_mac): self.delete_flows(table=self.dvr_process_table_id, dl_vlan=vlan_tag, dl_dst=vif_mac) self.delete_flows(table=self.dvr_process_table_id, dl_vlan=vlan_tag, dl_src=vif_mac) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_int.py0000664000175000017500000002130613553660047031773 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ * references ** OVS agent https://wiki.openstack.org/wiki/Ovs-flow-logic """ import netaddr from neutron_lib import constants as const from neutron.common import constants as n_const from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import ovs_bridge class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge): """openvswitch agent br-int specific logic.""" def setup_default_table(self): self.setup_canary_table() self.install_goto(dest_table_id=constants.TRANSIENT_TABLE) self.install_normal(table_id=constants.TRANSIENT_TABLE, priority=3) self.install_drop(table_id=constants.ARP_SPOOF_TABLE) self.install_drop(table_id=constants.LOCAL_SWITCHING, priority=constants.OPENFLOW_MAX_PRIORITY, dl_vlan=constants.DEAD_VLAN_TAG) def setup_canary_table(self): self.install_drop(constants.CANARY_TABLE) def check_canary_table(self): canary_flows = self.dump_flows(constants.CANARY_TABLE) if canary_flows == '': return constants.OVS_RESTARTED elif canary_flows is None: return constants.OVS_DEAD else: return constants.OVS_NORMAL def provision_local_vlan(self, port, lvid, segmentation_id): if segmentation_id is None: dl_vlan = 0xffff else: dl_vlan = segmentation_id self.add_flow(priority=3, in_port=port, dl_vlan=dl_vlan, actions="mod_vlan_vid:%s,resubmit(,%d)" % ( lvid, constants.TRANSIENT_TABLE)) def reclaim_local_vlan(self, port, segmentation_id): if segmentation_id is None: dl_vlan = 0xffff else: dl_vlan = segmentation_id self.delete_flows(in_port=port, dl_vlan=dl_vlan) @staticmethod def _dvr_to_src_mac_table_id(network_type): if network_type == const.TYPE_VLAN: return constants.DVR_TO_SRC_MAC_VLAN else: return constants.DVR_TO_SRC_MAC def install_dvr_to_src_mac(self, network_type, vlan_tag, gateway_mac, dst_mac, dst_port): table_id = self._dvr_to_src_mac_table_id(network_type) self.add_flow(table=table_id, priority=4, dl_vlan=vlan_tag, dl_dst=dst_mac, actions="mod_dl_src:%s," "resubmit(,%d)" % ( gateway_mac, constants.TRANSIENT_TABLE)) self.add_flow(table=constants.TRANSIENT_TABLE, priority=4, dl_vlan=vlan_tag, dl_dst=dst_mac, actions="strip_vlan,output:%s" % dst_port) def delete_dvr_to_src_mac(self, network_type, vlan_tag, dst_mac): table_id = self._dvr_to_src_mac_table_id(network_type) for table in (table_id, constants.TRANSIENT_TABLE): self.delete_flows(strict=True, priority=4, table=table, dl_vlan=vlan_tag, dl_dst=dst_mac) def add_dvr_mac_vlan(self, mac, port): self.install_goto(table_id=constants.LOCAL_SWITCHING, priority=4, in_port=port, eth_src=mac, dest_table_id=constants.DVR_TO_SRC_MAC_VLAN) def remove_dvr_mac_vlan(self, mac): # REVISIT(yamamoto): match in_port as well? self.delete_flows(table=constants.LOCAL_SWITCHING, dl_src=mac) def add_dvr_mac_tun(self, mac, port): # Table LOCAL_SWITCHING will now sort DVR traffic from other # traffic depending on in_port self.install_goto(table_id=constants.LOCAL_SWITCHING, priority=2, in_port=port, eth_src=mac, dest_table_id=constants.DVR_TO_SRC_MAC) def remove_dvr_mac_tun(self, mac, port): self.delete_flows(table=constants.LOCAL_SWITCHING, in_port=port, dl_src=mac) def install_icmpv6_na_spoofing_protection(self, port, ip_addresses): # Allow neighbor advertisements as long as they match addresses # that actually belong to the port. for ip in ip_addresses: self.install_goto( table_id=constants.ARP_SPOOF_TABLE, priority=2, dl_type=n_const.ETHERTYPE_IPV6, nw_proto=const.PROTO_NUM_IPV6_ICMP, icmp_type=const.ICMPV6_TYPE_NA, nd_target=ip, in_port=port, dest_table_id=constants.TRANSIENT_TABLE) # Now that the rules are ready, direct icmpv6 neighbor advertisement # traffic from the port into the anti-spoof table. self.add_flow(table=constants.LOCAL_SWITCHING, priority=10, dl_type=n_const.ETHERTYPE_IPV6, nw_proto=const.PROTO_NUM_IPV6_ICMP, icmp_type=const.ICMPV6_TYPE_NA, in_port=port, actions=("resubmit(,%s)" % constants.ARP_SPOOF_TABLE)) def set_allowed_macs_for_port(self, port, mac_addresses=None, allow_all=False): if allow_all: self.delete_flows(table=constants.LOCAL_SWITCHING, in_port=port) self.delete_flows(table=constants.MAC_SPOOF_TABLE, in_port=port) return mac_addresses = mac_addresses or [] for address in mac_addresses: self.install_goto( table_id=constants.MAC_SPOOF_TABLE, priority=2, eth_src=address, in_port=port, dest_table_id=constants.TRANSIENT_TABLE) # normalize so we can see if macs are the same mac_addresses = {netaddr.EUI(mac) for mac in mac_addresses} flows = self.dump_flows_for(table=constants.MAC_SPOOF_TABLE, in_port=port).splitlines() for flow in flows: if 'dl_src' not in flow: continue flow_mac = flow.split('dl_src=')[1].split(' ')[0].split(',')[0] if netaddr.EUI(flow_mac) not in mac_addresses: self.delete_flows(table=constants.MAC_SPOOF_TABLE, in_port=port, dl_src=flow_mac) self.add_flow(table=constants.LOCAL_SWITCHING, priority=9, in_port=port, actions=("resubmit(,%s)" % constants.MAC_SPOOF_TABLE)) def install_arp_spoofing_protection(self, port, ip_addresses): # allow ARPs as long as they match addresses that actually # belong to the port. for ip in ip_addresses: self.add_flow( table=constants.ARP_SPOOF_TABLE, priority=2, proto='arp', arp_spa=ip, in_port=port, actions=("resubmit(,%s)" % constants.MAC_SPOOF_TABLE)) # Now that the rules are ready, direct ARP traffic from the port into # the anti-spoof table. # This strategy fails gracefully because OVS versions that can't match # on ARP headers will just process traffic normally. self.add_flow(table=constants.LOCAL_SWITCHING, priority=10, proto='arp', in_port=port, actions=("resubmit(,%s)" % constants.ARP_SPOOF_TABLE)) def delete_arp_spoofing_protection(self, port): self.delete_flows(table=constants.LOCAL_SWITCHING, in_port=port, proto='arp') self.delete_flows(table=constants.LOCAL_SWITCHING, in_port=port, nw_proto=const.PROTO_NUM_IPV6_ICMP, icmp_type=const.ICMPV6_TYPE_NA) self.delete_arp_spoofing_allow_rules(port) def delete_arp_spoofing_allow_rules(self, port): self.delete_flows(table=constants.ARP_SPOOF_TABLE, in_port=port) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ofswitch.py0000664000175000017500000001015313553660047032342 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import re from oslo_log import log as logging LOG = logging.getLogger(__name__) # Field name mappings (from Ryu to ovs-ofctl) _keywords = { 'eth_src': 'dl_src', 'eth_dst': 'dl_dst', 'ipv4_src': 'nw_src', 'ipv4_dst': 'nw_dst', 'table_id': 'table', } class OpenFlowSwitchMixin(object): """Mixin to provide common convenient routines for an openflow switch.""" @staticmethod def _conv_args(kwargs): for our_name, ovs_ofctl_name in _keywords.items(): if our_name in kwargs: kwargs[ovs_ofctl_name] = kwargs.pop(our_name) return kwargs def dump_flows(self, table_id): return self.dump_flows_for_table(table_id) def dump_flows_all_tables(self): return self.dump_all_flows() def install_goto_next(self, table_id): self.install_goto(table_id=table_id, dest_table_id=table_id + 1) def install_output(self, port, table_id=0, priority=0, **kwargs): self.add_flow(table=table_id, priority=priority, actions="output:%s" % port, **self._conv_args(kwargs)) def install_normal(self, table_id=0, priority=0, **kwargs): self.add_flow(table=table_id, priority=priority, actions="normal", **self._conv_args(kwargs)) def install_goto(self, dest_table_id, table_id=0, priority=0, **kwargs): self.add_flow(table=table_id, priority=priority, actions="resubmit(,%s)" % dest_table_id, **self._conv_args(kwargs)) def install_drop(self, table_id=0, priority=0, **kwargs): self.add_flow(table=table_id, priority=priority, actions="drop", **self._conv_args(kwargs)) def install_instructions(self, instructions, table_id=0, priority=0, **kwargs): self.add_flow(table=table_id, priority=priority, actions=instructions, **self._conv_args(kwargs)) def uninstall_flows(self, **kwargs): # NOTE(yamamoto): super() points to ovs_lib.OVSBridge. # See ovs_bridge.py how this class is actually used. super(OpenFlowSwitchMixin, self).delete_flows( **self._conv_args(kwargs)) def _filter_flows(self, flows): cookie_list = self.reserved_cookies LOG.debug("Bridge cookies used to filter flows: %s", cookie_list) cookie_re = re.compile('cookie=(0x[A-Fa-f0-9]*)') table_re = re.compile('table=([0-9]*)') for flow in flows: fl_cookie = cookie_re.search(flow) if not fl_cookie: continue fl_cookie = fl_cookie.group(1) if int(fl_cookie, 16) not in cookie_list: fl_table = table_re.search(flow) if not fl_table: continue fl_table = fl_table.group(1) yield flow, fl_cookie, fl_table def cleanup_flows(self): flows = self.dump_flows_all_tables() for flow, cookie, table in self._filter_flows(flows): # deleting a stale flow should be rare. # it might deserve some attention LOG.warning("Deleting flow %s", flow) self.delete_flows(cookie=cookie + '/-1', table=table) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/main.py0000664000175000017500000000243713553660047031446 0ustar zuulzuul00000000000000# Copyright (C) 2015 VA Linux Systems Japan K.K. # Copyright (C) 2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import br_int from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import br_phys from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import br_tun from neutron.plugins.ml2.drivers.openvswitch.agent import ovs_neutron_agent def init_config(): pass def main(): bridge_classes = { 'br_int': br_int.OVSIntegrationBridge, 'br_phys': br_phys.OVSPhysicalBridge, 'br_tun': br_tun.OVSTunnelBridge, } ovs_neutron_agent.main(bridge_classes) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_phys.py0000664000175000017500000000467313553660047032174 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import br_dvr_process from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import ovs_bridge class OVSPhysicalBridge(ovs_bridge.OVSAgentBridge, br_dvr_process.OVSDVRProcessMixin): """openvswitch agent physical bridge specific logic.""" # Used by OVSDVRProcessMixin dvr_process_table_id = constants.DVR_PROCESS_VLAN dvr_process_next_table_id = constants.LOCAL_VLAN_TRANSLATION def setup_default_table(self): self.install_normal() def provision_local_vlan(self, port, lvid, segmentation_id, distributed): table_id = constants.LOCAL_VLAN_TRANSLATION if distributed else 0 if segmentation_id is None: self.add_flow(table=table_id, priority=4, in_port=port, dl_vlan=lvid, actions="strip_vlan,normal") else: self.add_flow(table=table_id, priority=4, in_port=port, dl_vlan=lvid, actions="mod_vlan_vid:%s,normal" % segmentation_id) def reclaim_local_vlan(self, port, lvid): self.delete_flows(in_port=port, dl_vlan=lvid) def add_dvr_mac_vlan(self, mac, port): self.install_output(table_id=constants.DVR_NOT_LEARN_VLAN, priority=2, eth_src=mac, port=port) def remove_dvr_mac_vlan(self, mac): # REVISIT(yamamoto): match in_port as well? self.delete_flows(table=constants.DVR_NOT_LEARN_VLAN, dl_src=mac) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/br_tun.py0000664000175000017500000002631213553660047032011 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # Copyright 2011 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import netaddr from neutron.agent.common import ovs_lib from neutron.plugins.ml2.drivers.openvswitch.agent.common \ import constants from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import br_dvr_process from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import ovs_bridge class OVSTunnelBridge(ovs_bridge.OVSAgentBridge, br_dvr_process.OVSDVRProcessMixin): """openvswitch agent tunnel bridge specific logic.""" # Used by OVSDVRProcessMixin dvr_process_table_id = constants.DVR_PROCESS dvr_process_next_table_id = constants.PATCH_LV_TO_TUN def setup_default_table(self, patch_int_ofport, arp_responder_enabled): # Table 0 (default) will sort incoming traffic depending on in_port with self.deferred() as deferred_br: deferred_br.add_flow(priority=1, in_port=patch_int_ofport, actions="resubmit(,%s)" % constants.PATCH_LV_TO_TUN) deferred_br.add_flow(priority=0, actions="drop") if arp_responder_enabled: # ARP broadcast-ed request go to the local ARP_RESPONDER # table to be locally resolved # REVISIT(yamamoto): add arp_op=arp.ARP_REQUEST matcher? deferred_br.add_flow(table=constants.PATCH_LV_TO_TUN, priority=1, proto='arp', dl_dst="ff:ff:ff:ff:ff:ff", actions=("resubmit(,%s)" % constants.ARP_RESPONDER)) # PATCH_LV_TO_TUN table will handle packets coming from patch_int # unicasts go to table UCAST_TO_TUN where remote addresses are # learnt deferred_br.add_flow(table=constants.PATCH_LV_TO_TUN, priority=0, dl_dst="00:00:00:00:00:00/01:00:00:00:00:00", actions=("resubmit(,%s)" % constants.UCAST_TO_TUN)) # Broadcasts/multicasts go to table FLOOD_TO_TUN that handles # flooding deferred_br.add_flow(table=constants.PATCH_LV_TO_TUN, priority=0, dl_dst="01:00:00:00:00:00/01:00:00:00:00:00", actions=("resubmit(,%s)" % constants.FLOOD_TO_TUN)) # Tables [tunnel_type]_TUN_TO_LV will set lvid depending on tun_id # for each tunnel type, and resubmit to table LEARN_FROM_TUN where # remote mac addresses will be learnt for tunnel_type in constants.TUNNEL_NETWORK_TYPES: deferred_br.add_flow(table=constants.TUN_TABLE[tunnel_type], priority=0, actions="drop") # LEARN_FROM_TUN table will have a single flow using a learn action # to dynamically set-up flows in UCAST_TO_TUN corresponding to # remote mac addresses (assumes that lvid has already been set by # a previous flow) learned_flow = ("cookie=%(cookie)s," "table=%(table)s," "priority=1," "hard_timeout=300," "NXM_OF_VLAN_TCI[0..11]," "NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]," "load:0->NXM_OF_VLAN_TCI[]," "load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[]," "output:NXM_OF_IN_PORT[]" % {'cookie': self.default_cookie, 'table': constants.UCAST_TO_TUN}) # Once remote mac addresses are learnt, output packet to patch_int deferred_br.add_flow(table=constants.LEARN_FROM_TUN, priority=1, actions="learn(%s),output:%s" % (learned_flow, patch_int_ofport)) # Egress unicast will be handled in table UCAST_TO_TUN, where # remote mac addresses will be learned. For now, just add a # default flow that will resubmit unknown unicasts to table # FLOOD_TO_TUN to treat them as broadcasts/multicasts deferred_br.add_flow(table=constants.UCAST_TO_TUN, priority=0, actions="resubmit(,%s)" % constants.FLOOD_TO_TUN) if arp_responder_enabled: # If none of the ARP entries correspond to the requested IP, # the broadcast-ed packet is resubmitted to the flooding table deferred_br.add_flow(table=constants.ARP_RESPONDER, priority=0, actions="resubmit(,%s)" % constants.FLOOD_TO_TUN) # FLOOD_TO_TUN will handle flooding in tunnels based on lvid, # for now, add a default drop action self.install_drop(table_id=constants.FLOOD_TO_TUN) def provision_local_vlan(self, network_type, lvid, segmentation_id, distributed=False): if distributed: table_id = constants.DVR_NOT_LEARN else: table_id = constants.LEARN_FROM_TUN self.add_flow(table=constants.TUN_TABLE[network_type], priority=1, tun_id=segmentation_id, actions="mod_vlan_vid:%s," "resubmit(,%s)" % (lvid, table_id)) def reclaim_local_vlan(self, network_type, segmentation_id): self.delete_flows(table=constants.TUN_TABLE[network_type], tun_id=segmentation_id) @staticmethod def _ofport_set_to_str(ports_set): return ",".join(map(str, ports_set)) def install_flood_to_tun(self, vlan, tun_id, ports, deferred_br=None): br = deferred_br if deferred_br else self br.mod_flow(table=constants.FLOOD_TO_TUN, dl_vlan=vlan, actions="strip_vlan,set_tunnel:%s,output:%s" % (tun_id, self._ofport_set_to_str(ports))) def delete_flood_to_tun(self, vlan, deferred_br=None): br = deferred_br if deferred_br else self br.delete_flows(table=constants.FLOOD_TO_TUN, dl_vlan=vlan) def install_unicast_to_tun(self, vlan, tun_id, port, mac, deferred_br=None): br = deferred_br if deferred_br else self br.add_flow(table=constants.UCAST_TO_TUN, priority=2, dl_vlan=vlan, dl_dst=mac, actions="strip_vlan,set_tunnel:%s,output:%s" % (tun_id, port)) def delete_unicast_to_tun(self, vlan, mac, deferred_br=None): br = deferred_br if deferred_br else self if mac is None: br.delete_flows(table=constants.UCAST_TO_TUN, dl_vlan=vlan) else: br.delete_flows(table=constants.UCAST_TO_TUN, dl_vlan=vlan, dl_dst=mac) def install_arp_responder(self, vlan, ip, mac, deferred_br=None): br = deferred_br if deferred_br else self actions = constants.ARP_RESPONDER_ACTIONS % { 'mac': netaddr.EUI(mac, dialect=netaddr.mac_unix), 'ip': netaddr.IPAddress(ip), } br.add_flow(table=constants.ARP_RESPONDER, priority=1, proto='arp', dl_vlan=vlan, nw_dst='%s' % ip, actions=actions) def delete_arp_responder(self, vlan, ip, deferred_br=None): br = deferred_br if deferred_br else self if ip is None: br.delete_flows(table=constants.ARP_RESPONDER, proto='arp', dl_vlan=vlan) else: br.delete_flows(table=constants.ARP_RESPONDER, proto='arp', dl_vlan=vlan, nw_dst='%s' % ip) def setup_tunnel_port(self, network_type, port, deferred_br=None): br = deferred_br if deferred_br else self br.add_flow(priority=1, in_port=port, actions="resubmit(,%s)" % constants.TUN_TABLE[network_type]) def cleanup_tunnel_port(self, port, deferred_br=None): br = deferred_br if deferred_br else self br.delete_flows(in_port=port) def add_dvr_mac_tun(self, mac, port): # Table DVR_NOT_LEARN ensures unique dvr macs in the cloud # are not learnt, as they may result in flow explosions self.install_output(table_id=constants.DVR_NOT_LEARN, priority=1, eth_src=mac, port=port) def remove_dvr_mac_tun(self, mac): # REVISIT(yamamoto): match in_port as well? self.delete_flows(table=constants.DVR_NOT_LEARN, dl_src=mac) def deferred(self): return DeferredOVSTunnelBridge(self) class DeferredOVSTunnelBridge(ovs_lib.DeferredOVSBridge): _METHODS = [ 'install_unicast_to_tun', 'delete_unicast_to_tun', 'install_flood_to_tun', 'delete_flood_to_tun', 'install_arp_responder', 'delete_arp_responder', 'setup_tunnel_port', 'cleanup_tunnel_port', ] def __getattr__(self, name): if name in self._METHODS: m = getattr(self.br, name) return functools.partial(m, deferred_br=self) return super(DeferredOVSTunnelBridge, self).__getattr__(name) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/__init__.py0000664000175000017500000000000013553660047032241 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ovs_bridge.py0000664000175000017500000000237613553660047032647 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.common import ovs_lib from neutron.plugins.ml2.drivers.openvswitch.agent.openflow \ import br_cookie from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import ofswitch class OVSAgentBridge(ofswitch.OpenFlowSwitchMixin, br_cookie.OVSBridgeCookieMixin, ovs_lib.OVSBridge): """Common code for bridges used by OVS agent""" def setup_controllers(self, conf): self.del_controller() def drop_port(self, in_port): self.install_drop(priority=2, in_port=in_port) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/0000775000175000017500000000000013553660156027433 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_dvr_process.py0000664000175000017500000001167213553660047033027 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ryu.lib.packet import ether_types from ryu.lib.packet import icmpv6 from ryu.lib.packet import in_proto class OVSDVRProcessMixin(object): """Common logic for br-tun and br-phys' DVR_PROCESS tables. Inheriters should provide self.dvr_process_table_id and self.dvr_process_next_table_id. """ @staticmethod def _dvr_process_ipv4_match(ofp, ofpp, vlan_tag, gateway_ip): return ofpp.OFPMatch(vlan_vid=vlan_tag | ofp.OFPVID_PRESENT, eth_type=ether_types.ETH_TYPE_ARP, arp_tpa=gateway_ip) def install_dvr_process_ipv4(self, vlan_tag, gateway_ip): # block ARP (_dp, ofp, ofpp) = self._get_dp() match = self._dvr_process_ipv4_match(ofp, ofpp, vlan_tag=vlan_tag, gateway_ip=gateway_ip) self.install_drop(table_id=self.dvr_process_table_id, priority=3, match=match) def delete_dvr_process_ipv4(self, vlan_tag, gateway_ip): (_dp, ofp, ofpp) = self._get_dp() match = self._dvr_process_ipv4_match(ofp, ofpp, vlan_tag=vlan_tag, gateway_ip=gateway_ip) self.uninstall_flows(table_id=self.dvr_process_table_id, match=match) @staticmethod def _dvr_process_ipv6_match(ofp, ofpp, vlan_tag, gateway_mac): return ofpp.OFPMatch(vlan_vid=vlan_tag | ofp.OFPVID_PRESENT, eth_type=ether_types.ETH_TYPE_IPV6, ip_proto=in_proto.IPPROTO_ICMPV6, icmpv6_type=icmpv6.ND_ROUTER_ADVERT, eth_src=gateway_mac) def install_dvr_process_ipv6(self, vlan_tag, gateway_mac): # block RA (_dp, ofp, ofpp) = self._get_dp() match = self._dvr_process_ipv6_match(ofp, ofpp, vlan_tag=vlan_tag, gateway_mac=gateway_mac) self.install_drop(table_id=self.dvr_process_table_id, priority=3, match=match) def delete_dvr_process_ipv6(self, vlan_tag, gateway_mac): (_dp, ofp, ofpp) = self._get_dp() match = self._dvr_process_ipv6_match(ofp, ofpp, vlan_tag=vlan_tag, gateway_mac=gateway_mac) self.uninstall_flows(table_id=self.dvr_process_table_id, match=match) @staticmethod def _dvr_process_in_match(ofp, ofpp, vlan_tag, vif_mac): return ofpp.OFPMatch(vlan_vid=vlan_tag | ofp.OFPVID_PRESENT, eth_dst=vif_mac) @staticmethod def _dvr_process_out_match(ofp, ofpp, vlan_tag, vif_mac): return ofpp.OFPMatch(vlan_vid=vlan_tag | ofp.OFPVID_PRESENT, eth_src=vif_mac) def install_dvr_process(self, vlan_tag, vif_mac, dvr_mac_address): (_dp, ofp, ofpp) = self._get_dp() match = self._dvr_process_in_match(ofp, ofpp, vlan_tag=vlan_tag, vif_mac=vif_mac) table_id = self.dvr_process_table_id self.install_drop(table_id=table_id, priority=2, match=match) match = self._dvr_process_out_match(ofp, ofpp, vlan_tag=vlan_tag, vif_mac=vif_mac) actions = [ ofpp.OFPActionSetField(eth_src=dvr_mac_address), ] instructions = [ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions), ofpp.OFPInstructionGotoTable( table_id=self.dvr_process_next_table_id), ] self.install_instructions(table_id=table_id, priority=1, match=match, instructions=instructions) def delete_dvr_process(self, vlan_tag, vif_mac): (_dp, ofp, ofpp) = self._get_dp() table_id = self.dvr_process_table_id match = self._dvr_process_in_match(ofp, ofpp, vlan_tag=vlan_tag, vif_mac=vif_mac) self.uninstall_flows(table_id=table_id, match=match) match = self._dvr_process_out_match(ofp, ofpp, vlan_tag=vlan_tag, vif_mac=vif_mac) self.uninstall_flows(table_id=table_id, match=match) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py0000664000175000017500000002621013553660047031262 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ * references ** OVS agent https://wiki.openstack.org/wiki/Ovs-flow-logic """ import netaddr from neutron_lib import constants as p_const from oslo_log import log as logging from ryu.lib.packet import ether_types from ryu.lib.packet import icmpv6 from ryu.lib.packet import in_proto from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import ovs_bridge LOG = logging.getLogger(__name__) class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge): """openvswitch agent br-int specific logic.""" of_tables = constants.INT_BR_ALL_TABLES def setup_default_table(self): self.setup_canary_table() self.install_goto(dest_table_id=constants.TRANSIENT_TABLE) self.install_normal(table_id=constants.TRANSIENT_TABLE, priority=3) self.install_drop(table_id=constants.ARP_SPOOF_TABLE) self.install_drop(table_id=constants.LOCAL_SWITCHING, priority=constants.OPENFLOW_MAX_PRIORITY, vlan_vid=constants.DEAD_VLAN_TAG) def setup_canary_table(self): self.install_drop(constants.CANARY_TABLE) def check_canary_table(self): try: flows = self.dump_flows(constants.CANARY_TABLE) except RuntimeError: LOG.exception("Failed to communicate with the switch") return constants.OVS_DEAD return constants.OVS_NORMAL if flows else constants.OVS_RESTARTED @staticmethod def _local_vlan_match(_ofp, ofpp, port, vlan_vid): return ofpp.OFPMatch(in_port=port, vlan_vid=vlan_vid) def provision_local_vlan(self, port, lvid, segmentation_id): (_dp, ofp, ofpp) = self._get_dp() if segmentation_id is None: vlan_vid = ofp.OFPVID_NONE actions = [ofpp.OFPActionPushVlan()] else: vlan_vid = segmentation_id | ofp.OFPVID_PRESENT actions = [] match = self._local_vlan_match(ofp, ofpp, port, vlan_vid) actions += [ ofpp.OFPActionSetField(vlan_vid=lvid | ofp.OFPVID_PRESENT), ] instructions = [ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions), ofpp.OFPInstructionGotoTable(table_id=constants.TRANSIENT_TABLE), ] self.install_instructions( instructions=instructions, priority=3, match=match, ) def reclaim_local_vlan(self, port, segmentation_id): (_dp, ofp, ofpp) = self._get_dp() if segmentation_id is None: vlan_vid = ofp.OFPVID_NONE else: vlan_vid = segmentation_id | ofp.OFPVID_PRESENT match = self._local_vlan_match(ofp, ofpp, port, vlan_vid) self.uninstall_flows(match=match) @staticmethod def _dvr_to_src_mac_match(ofp, ofpp, vlan_tag, dst_mac): return ofpp.OFPMatch(vlan_vid=vlan_tag | ofp.OFPVID_PRESENT, eth_dst=dst_mac) @staticmethod def _dvr_to_src_mac_table_id(network_type): if network_type == p_const.TYPE_VLAN: return constants.DVR_TO_SRC_MAC_VLAN else: return constants.DVR_TO_SRC_MAC def install_dvr_to_src_mac(self, network_type, vlan_tag, gateway_mac, dst_mac, dst_port): table_id = self._dvr_to_src_mac_table_id(network_type) (_dp, ofp, ofpp) = self._get_dp() match = self._dvr_to_src_mac_match(ofp, ofpp, vlan_tag=vlan_tag, dst_mac=dst_mac) actions = [ ofpp.OFPActionSetField(eth_src=gateway_mac), ] instructions = [ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions), ofpp.OFPInstructionGotoTable(table_id=constants.TRANSIENT_TABLE), ] self.install_instructions(table_id=table_id, priority=4, match=match, instructions=instructions) actions = [ ofpp.OFPActionPopVlan(), ofpp.OFPActionOutput(dst_port, 0), ] self.install_apply_actions(table_id=constants.TRANSIENT_TABLE, priority=4, match=match, actions=actions) def delete_dvr_to_src_mac(self, network_type, vlan_tag, dst_mac): table_id = self._dvr_to_src_mac_table_id(network_type) (_dp, ofp, ofpp) = self._get_dp() match = self._dvr_to_src_mac_match(ofp, ofpp, vlan_tag=vlan_tag, dst_mac=dst_mac) for table in (table_id, constants.TRANSIENT_TABLE): self.uninstall_flows( strict=True, priority=4, table_id=table, match=match) def add_dvr_mac_vlan(self, mac, port): self.install_goto(table_id=constants.LOCAL_SWITCHING, priority=4, in_port=port, eth_src=mac, dest_table_id=constants.DVR_TO_SRC_MAC_VLAN) def remove_dvr_mac_vlan(self, mac): # REVISIT(yamamoto): match in_port as well? self.uninstall_flows(table_id=constants.LOCAL_SWITCHING, eth_src=mac) def add_dvr_mac_tun(self, mac, port): self.install_goto(table_id=constants.LOCAL_SWITCHING, priority=2, in_port=port, eth_src=mac, dest_table_id=constants.DVR_TO_SRC_MAC) def remove_dvr_mac_tun(self, mac, port): self.uninstall_flows(table_id=constants.LOCAL_SWITCHING, in_port=port, eth_src=mac) @staticmethod def _arp_reply_match(ofp, ofpp, port): return ofpp.OFPMatch(in_port=port, eth_type=ether_types.ETH_TYPE_ARP) @staticmethod def _icmpv6_reply_match(ofp, ofpp, port): return ofpp.OFPMatch(in_port=port, eth_type=ether_types.ETH_TYPE_IPV6, ip_proto=in_proto.IPPROTO_ICMPV6, icmpv6_type=icmpv6.ND_NEIGHBOR_ADVERT) def install_icmpv6_na_spoofing_protection(self, port, ip_addresses): # Allow neighbor advertisements as long as they match addresses # that actually belong to the port. for ip in ip_addresses: masked_ip = self._cidr_to_ryu(ip) self.install_goto( table_id=constants.ARP_SPOOF_TABLE, priority=2, eth_type=ether_types.ETH_TYPE_IPV6, ip_proto=in_proto.IPPROTO_ICMPV6, icmpv6_type=icmpv6.ND_NEIGHBOR_ADVERT, ipv6_nd_target=masked_ip, in_port=port, dest_table_id=constants.TRANSIENT_TABLE) # Now that the rules are ready, direct icmpv6 neighbor advertisement # traffic from the port into the anti-spoof table. (_dp, ofp, ofpp) = self._get_dp() match = self._icmpv6_reply_match(ofp, ofpp, port=port) self.install_goto(table_id=constants.LOCAL_SWITCHING, priority=10, match=match, dest_table_id=constants.ARP_SPOOF_TABLE) def set_allowed_macs_for_port(self, port, mac_addresses=None, allow_all=False): if allow_all: self.uninstall_flows(table_id=constants.LOCAL_SWITCHING, in_port=port) self.uninstall_flows(table_id=constants.MAC_SPOOF_TABLE, in_port=port) return mac_addresses = mac_addresses or [] for address in mac_addresses: self.install_goto( table_id=constants.MAC_SPOOF_TABLE, priority=2, eth_src=address, in_port=port, dest_table_id=constants.TRANSIENT_TABLE) # normalize so we can see if macs are the same mac_addresses = {netaddr.EUI(mac) for mac in mac_addresses} flows = self.dump_flows(constants.MAC_SPOOF_TABLE) for flow in flows: matches = dict(flow.match.items()) if matches.get('in_port') != port: continue if not matches.get('eth_src'): continue flow_mac = matches['eth_src'] if netaddr.EUI(flow_mac) not in mac_addresses: self.uninstall_flows(table_id=constants.MAC_SPOOF_TABLE, in_port=port, eth_src=flow_mac) self.install_goto(table_id=constants.LOCAL_SWITCHING, priority=9, in_port=port, dest_table_id=constants.MAC_SPOOF_TABLE) def install_arp_spoofing_protection(self, port, ip_addresses): # allow ARP replies as long as they match addresses that actually # belong to the port. for ip in ip_addresses: masked_ip = self._cidr_to_ryu(ip) self.install_goto(table_id=constants.ARP_SPOOF_TABLE, priority=2, eth_type=ether_types.ETH_TYPE_ARP, arp_spa=masked_ip, in_port=port, dest_table_id=constants.MAC_SPOOF_TABLE) # Now that the rules are ready, direct ARP traffic from the port into # the anti-spoof table. # This strategy fails gracefully because OVS versions that can't match # on ARP headers will just process traffic normally. (_dp, ofp, ofpp) = self._get_dp() match = self._arp_reply_match(ofp, ofpp, port=port) self.install_goto(table_id=constants.LOCAL_SWITCHING, priority=10, match=match, dest_table_id=constants.ARP_SPOOF_TABLE) def delete_arp_spoofing_protection(self, port): (_dp, ofp, ofpp) = self._get_dp() match = self._arp_reply_match(ofp, ofpp, port=port) self.uninstall_flows(table_id=constants.LOCAL_SWITCHING, match=match) match = self._icmpv6_reply_match(ofp, ofpp, port=port) self.uninstall_flows(table_id=constants.LOCAL_SWITCHING, match=match) self.delete_arp_spoofing_allow_rules(port) def delete_arp_spoofing_allow_rules(self, port): self.uninstall_flows(table_id=constants.ARP_SPOOF_TABLE, in_port=port) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/ofswitch.py0000664000175000017500000002224113553660047031633 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import eventlet import netaddr from oslo_config import cfg from oslo_log import log as logging from oslo_utils import excutils from oslo_utils import timeutils import ryu.app.ofctl.api as ofctl_api import ryu.exception as ryu_exc from ryu.lib import ofctl_string from ryu.ofproto import ofproto_parser import six from neutron._i18n import _ from neutron.agent.common import ovs_lib LOG = logging.getLogger(__name__) COOKIE_DEFAULT = object() class OpenFlowSwitchMixin(object): """Mixin to provide common convenient routines for an openflow switch. NOTE(yamamoto): super() points to ovs_lib.OVSBridge. See ovs_bridge.py how this class is actually used. """ @staticmethod def _cidr_to_ryu(ip): n = netaddr.IPNetwork(ip) if n.hostmask: return (str(n.ip), str(n.netmask)) return str(n.ip) def __init__(self, *args, **kwargs): self._app = kwargs.pop('ryu_app') super(OpenFlowSwitchMixin, self).__init__(*args, **kwargs) def _get_dp_by_dpid(self, dpid_int): """Get Ryu datapath object for the switch.""" timeout_sec = cfg.CONF.OVS.of_connect_timeout start_time = timeutils.now() while True: dp = ofctl_api.get_datapath(self._app, dpid_int) if dp is not None: break # The switch has not established a connection to us. # Wait for a little. if timeutils.now() > start_time + timeout_sec: m = _("Switch connection timeout") LOG.error(m) # NOTE(yamamoto): use RuntimeError for compat with ovs_lib raise RuntimeError(m) eventlet.sleep(1) return dp def _send_msg(self, msg, reply_cls=None, reply_multi=False): timeout_sec = cfg.CONF.OVS.of_request_timeout timeout = eventlet.Timeout(seconds=timeout_sec) try: result = ofctl_api.send_msg(self._app, msg, reply_cls, reply_multi) except ryu_exc.RyuException as e: m = _("ofctl request %(request)s error %(error)s") % { "request": msg, "error": e, } LOG.error(m) # NOTE(yamamoto): use RuntimeError for compat with ovs_lib raise RuntimeError(m) except eventlet.Timeout as e: with excutils.save_and_reraise_exception() as ctx: if e is timeout: ctx.reraise = False m = _("ofctl request %(request)s timed out") % { "request": msg, } LOG.error(m) # NOTE(yamamoto): use RuntimeError for compat with ovs_lib raise RuntimeError(m) finally: timeout.cancel() LOG.debug("ofctl request %(request)s result %(result)s", {"request": msg, "result": result}) return result @staticmethod def _match(_ofp, ofpp, match, **match_kwargs): if match is not None: return match return ofpp.OFPMatch(**match_kwargs) def uninstall_flows(self, table_id=None, strict=False, priority=0, cookie=COOKIE_DEFAULT, cookie_mask=0, match=None, **match_kwargs): (dp, ofp, ofpp) = self._get_dp() if table_id is None: table_id = ofp.OFPTT_ALL if cookie == ovs_lib.COOKIE_ANY: cookie = 0 if cookie_mask != 0: raise Exception("cookie=COOKIE_ANY but cookie_mask set to %s" % cookie_mask) elif cookie == COOKIE_DEFAULT: cookie = self._default_cookie cookie_mask = ovs_lib.UINT64_BITMASK match = self._match(ofp, ofpp, match, **match_kwargs) if strict: cmd = ofp.OFPFC_DELETE_STRICT else: cmd = ofp.OFPFC_DELETE msg = ofpp.OFPFlowMod(dp, command=cmd, cookie=cookie, cookie_mask=cookie_mask, table_id=table_id, match=match, priority=priority, out_group=ofp.OFPG_ANY, out_port=ofp.OFPP_ANY) self._send_msg(msg) def dump_flows(self, table_id=None): (dp, ofp, ofpp) = self._get_dp() if table_id is None: table_id = ofp.OFPTT_ALL msg = ofpp.OFPFlowStatsRequest(dp, table_id=table_id) replies = self._send_msg(msg, reply_cls=ofpp.OFPFlowStatsReply, reply_multi=True) flows = [] for rep in replies: flows += rep.body return flows def _dump_and_clean(self, table_id=None): cookies = set([f.cookie for f in self.dump_flows(table_id)]) - \ self.reserved_cookies for c in cookies: LOG.warning("Deleting flow with cookie 0x%(cookie)x", {'cookie': c}) self.uninstall_flows(cookie=c, cookie_mask=ovs_lib.UINT64_BITMASK) def install_goto_next(self, table_id): self.install_goto(table_id=table_id, dest_table_id=table_id + 1) def cleanup_flows(self): LOG.info("Reserved cookies for %s: %s", self.br_name, self.reserved_cookies) for table_id in self.of_tables: self._dump_and_clean(table_id) def install_output(self, port, table_id=0, priority=0, match=None, **match_kwargs): (_dp, ofp, ofpp) = self._get_dp() actions = [ofpp.OFPActionOutput(port, 0)] instructions = [ofpp.OFPInstructionActions( ofp.OFPIT_APPLY_ACTIONS, actions)] self.install_instructions(table_id=table_id, priority=priority, instructions=instructions, match=match, **match_kwargs) def install_normal(self, table_id=0, priority=0, match=None, **match_kwargs): (_dp, ofp, _ofpp) = self._get_dp() self.install_output(port=ofp.OFPP_NORMAL, table_id=table_id, priority=priority, match=match, **match_kwargs) def install_goto(self, dest_table_id, table_id=0, priority=0, match=None, **match_kwargs): (_dp, _ofp, ofpp) = self._get_dp() instructions = [ofpp.OFPInstructionGotoTable(table_id=dest_table_id)] self.install_instructions(table_id=table_id, priority=priority, instructions=instructions, match=match, **match_kwargs) def install_drop(self, table_id=0, priority=0, match=None, **match_kwargs): self.install_instructions(table_id=table_id, priority=priority, instructions=[], match=match, **match_kwargs) def install_instructions(self, instructions, table_id=0, priority=0, match=None, **match_kwargs): (dp, ofp, ofpp) = self._get_dp() match = self._match(ofp, ofpp, match, **match_kwargs) if isinstance(instructions, six.string_types): # NOTE: instructions must be str for the ofctl of_interface. # After the ofctl driver is removed, a deprecation warning # could be added here. jsonlist = ofctl_string.ofp_instruction_from_str( ofp, instructions) instructions = ofproto_parser.ofp_instruction_from_jsondict( dp, jsonlist) msg = ofpp.OFPFlowMod(dp, table_id=table_id, cookie=self.default_cookie, match=match, priority=priority, instructions=instructions) self._send_msg(msg) def install_apply_actions(self, actions, table_id=0, priority=0, match=None, **match_kwargs): (dp, ofp, ofpp) = self._get_dp() instructions = [ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions), ] self.install_instructions(table_id=table_id, priority=priority, match=match, instructions=instructions, **match_kwargs) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/main.py0000664000175000017500000000235113553660047030731 0ustar zuulzuul00000000000000# Copyright (C) 2015 VA Linux Systems Japan K.K. # Copyright (C) 2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from ryu.base import app_manager from ryu import cfg as ryu_cfg cfg.CONF.import_group( 'OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.common.config') def init_config(): ryu_cfg.CONF(project='ryu', args=[]) ryu_cfg.CONF.ofp_listen_host = cfg.CONF.OVS.of_listen_address ryu_cfg.CONF.ofp_tcp_listen_port = cfg.CONF.OVS.of_listen_port def main(): app_manager.AppManager.run_apps([ 'neutron.plugins.ml2.drivers.openvswitch.agent.' 'openflow.native.ovs_ryuapp', ]) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py0000664000175000017500000000544213553660047031457 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import br_dvr_process from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import ovs_bridge class OVSPhysicalBridge(ovs_bridge.OVSAgentBridge, br_dvr_process.OVSDVRProcessMixin): """openvswitch agent physical bridge specific logic.""" # Used by OVSDVRProcessMixin dvr_process_table_id = constants.DVR_PROCESS_VLAN dvr_process_next_table_id = constants.LOCAL_VLAN_TRANSLATION of_tables = constants.PHY_BR_ALL_TABLES def setup_default_table(self): self.install_normal() @staticmethod def _local_vlan_match(ofp, ofpp, port, lvid): return ofpp.OFPMatch(in_port=port, vlan_vid=lvid | ofp.OFPVID_PRESENT) def provision_local_vlan(self, port, lvid, segmentation_id, distributed): table_id = constants.LOCAL_VLAN_TRANSLATION if distributed else 0 (_dp, ofp, ofpp) = self._get_dp() match = self._local_vlan_match(ofp, ofpp, port, lvid) if segmentation_id is None: actions = [ofpp.OFPActionPopVlan()] else: vlan_vid = segmentation_id | ofp.OFPVID_PRESENT actions = [ofpp.OFPActionSetField(vlan_vid=vlan_vid)] actions += [ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0)] self.install_apply_actions(table_id=table_id, priority=4, match=match, actions=actions) def reclaim_local_vlan(self, port, lvid): (_dp, ofp, ofpp) = self._get_dp() match = self._local_vlan_match(ofp, ofpp, port, lvid) self.uninstall_flows(match=match) def add_dvr_mac_vlan(self, mac, port): self.install_output(table_id=constants.DVR_NOT_LEARN_VLAN, priority=2, eth_src=mac, port=port) def remove_dvr_mac_vlan(self, mac): # REVISIT(yamamoto): match in_port as well? self.uninstall_flows(table_id=constants.DVR_NOT_LEARN_VLAN, eth_src=mac) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_tun.py0000664000175000017500000003232013553660047031275 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # Copyright 2011 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ryu.lib.packet import arp from ryu.lib.packet import ether_types from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import br_dvr_process from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import ovs_bridge class OVSTunnelBridge(ovs_bridge.OVSAgentBridge, br_dvr_process.OVSDVRProcessMixin): """openvswitch agent tunnel bridge specific logic.""" # Used by OVSDVRProcessMixin dvr_process_table_id = constants.DVR_PROCESS dvr_process_next_table_id = constants.PATCH_LV_TO_TUN of_tables = constants.TUN_BR_ALL_TABLES def setup_default_table(self, patch_int_ofport, arp_responder_enabled): (dp, ofp, ofpp) = self._get_dp() # Table 0 (default) will sort incoming traffic depending on in_port self.install_goto(dest_table_id=constants.PATCH_LV_TO_TUN, priority=1, in_port=patch_int_ofport) self.install_drop() # default drop if arp_responder_enabled: # ARP broadcast-ed request go to the local ARP_RESPONDER table to # be locally resolved # REVISIT(yamamoto): add arp_op=arp.ARP_REQUEST matcher? self.install_goto(dest_table_id=constants.ARP_RESPONDER, table_id=constants.PATCH_LV_TO_TUN, priority=1, eth_dst="ff:ff:ff:ff:ff:ff", eth_type=ether_types.ETH_TYPE_ARP) # PATCH_LV_TO_TUN table will handle packets coming from patch_int # unicasts go to table UCAST_TO_TUN where remote addresses are learnt self.install_goto(dest_table_id=constants.UCAST_TO_TUN, table_id=constants.PATCH_LV_TO_TUN, eth_dst=('00:00:00:00:00:00', '01:00:00:00:00:00')) # Broadcasts/multicasts go to table FLOOD_TO_TUN that handles flooding self.install_goto(dest_table_id=constants.FLOOD_TO_TUN, table_id=constants.PATCH_LV_TO_TUN, eth_dst=('01:00:00:00:00:00', '01:00:00:00:00:00')) # Tables [tunnel_type]_TUN_TO_LV will set lvid depending on tun_id # for each tunnel type, and resubmit to table LEARN_FROM_TUN where # remote mac addresses will be learnt for tunnel_type in constants.TUNNEL_NETWORK_TYPES: self.install_drop(table_id=constants.TUN_TABLE[tunnel_type]) # LEARN_FROM_TUN table will have a single flow using a learn action to # dynamically set-up flows in UCAST_TO_TUN corresponding to remote mac # addresses (assumes that lvid has already been set by a previous flow) # Once remote mac addresses are learnt, output packet to patch_int flow_specs = [ ofpp.NXFlowSpecMatch(src=('vlan_tci', 0), dst=('vlan_tci', 0), n_bits=12), ofpp.NXFlowSpecMatch(src=('eth_src', 0), dst=('eth_dst', 0), n_bits=48), ofpp.NXFlowSpecLoad(src=0, dst=('vlan_tci', 0), n_bits=16), ofpp.NXFlowSpecLoad(src=('tunnel_id', 0), dst=('tunnel_id', 0), n_bits=64), ofpp.NXFlowSpecOutput(src=('in_port', 0), dst='', n_bits=32), ] actions = [ ofpp.NXActionLearn(table_id=constants.UCAST_TO_TUN, cookie=self.default_cookie, priority=1, hard_timeout=300, specs=flow_specs), ofpp.OFPActionOutput(patch_int_ofport, 0), ] self.install_apply_actions(table_id=constants.LEARN_FROM_TUN, priority=1, actions=actions) # Egress unicast will be handled in table UCAST_TO_TUN, where remote # mac addresses will be learned. For now, just add a default flow that # will resubmit unknown unicasts to table FLOOD_TO_TUN to treat them # as broadcasts/multicasts self.install_goto(dest_table_id=constants.FLOOD_TO_TUN, table_id=constants.UCAST_TO_TUN) if arp_responder_enabled: # If none of the ARP entries correspond to the requested IP, the # broadcast-ed packet is resubmitted to the flooding table self.install_goto(dest_table_id=constants.FLOOD_TO_TUN, table_id=constants.ARP_RESPONDER) # FLOOD_TO_TUN will handle flooding in tunnels based on lvid, # for now, add a default drop action self.install_drop(table_id=constants.FLOOD_TO_TUN) @staticmethod def _local_vlan_match(_ofp, ofpp, tun_id): return ofpp.OFPMatch(tunnel_id=tun_id) def provision_local_vlan(self, network_type, lvid, segmentation_id, distributed=False): (_dp, ofp, ofpp) = self._get_dp() match = self._local_vlan_match(ofp, ofpp, segmentation_id) table_id = constants.TUN_TABLE[network_type] if distributed: dest_table_id = constants.DVR_NOT_LEARN else: dest_table_id = constants.LEARN_FROM_TUN actions = [ ofpp.OFPActionPushVlan(), ofpp.OFPActionSetField(vlan_vid=lvid | ofp.OFPVID_PRESENT), ] instructions = [ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions), ofpp.OFPInstructionGotoTable(table_id=dest_table_id)] self.install_instructions(table_id=table_id, priority=1, match=match, instructions=instructions) def reclaim_local_vlan(self, network_type, segmentation_id): (_dp, ofp, ofpp) = self._get_dp() match = self._local_vlan_match(ofp, ofpp, segmentation_id) table_id = constants.TUN_TABLE[network_type] self.uninstall_flows(table_id=table_id, match=match) @staticmethod def _flood_to_tun_match(ofp, ofpp, vlan): return ofpp.OFPMatch(vlan_vid=vlan | ofp.OFPVID_PRESENT) def install_flood_to_tun(self, vlan, tun_id, ports): (_dp, ofp, ofpp) = self._get_dp() match = self._flood_to_tun_match(ofp, ofpp, vlan) actions = [ofpp.OFPActionPopVlan(), ofpp.OFPActionSetField(tunnel_id=tun_id)] for port in ports: actions.append(ofpp.OFPActionOutput(port, 0)) self.install_apply_actions(table_id=constants.FLOOD_TO_TUN, priority=1, match=match, actions=actions) def delete_flood_to_tun(self, vlan): (_dp, ofp, ofpp) = self._get_dp() match = self._flood_to_tun_match(ofp, ofpp, vlan) self.uninstall_flows(table_id=constants.FLOOD_TO_TUN, match=match) @staticmethod def _unicast_to_tun_match(ofp, ofpp, vlan, mac): return ofpp.OFPMatch(vlan_vid=vlan | ofp.OFPVID_PRESENT, eth_dst=mac) def install_unicast_to_tun(self, vlan, tun_id, port, mac): (_dp, ofp, ofpp) = self._get_dp() match = self._unicast_to_tun_match(ofp, ofpp, vlan, mac) actions = [ofpp.OFPActionPopVlan(), ofpp.OFPActionSetField(tunnel_id=tun_id), ofpp.OFPActionOutput(port, 0)] self.install_apply_actions(table_id=constants.UCAST_TO_TUN, priority=2, match=match, actions=actions) def delete_unicast_to_tun(self, vlan, mac): (_dp, ofp, ofpp) = self._get_dp() if mac is None: match = ofpp.OFPMatch(vlan_vid=vlan | ofp.OFPVID_PRESENT) else: match = self._unicast_to_tun_match(ofp, ofpp, vlan, mac) self.uninstall_flows(table_id=constants.UCAST_TO_TUN, match=match) @staticmethod def _arp_responder_match(ofp, ofpp, vlan, ip): # REVISIT(yamamoto): add arp_op=arp.ARP_REQUEST matcher? return ofpp.OFPMatch(vlan_vid=vlan | ofp.OFPVID_PRESENT, eth_type=ether_types.ETH_TYPE_ARP, arp_tpa=ip) def install_arp_responder(self, vlan, ip, mac): (dp, ofp, ofpp) = self._get_dp() match = self._arp_responder_match(ofp, ofpp, vlan, ip) actions = [ofpp.OFPActionSetField(arp_op=arp.ARP_REPLY), ofpp.NXActionRegMove(src_field='arp_sha', dst_field='arp_tha', n_bits=48), ofpp.NXActionRegMove(src_field='arp_spa', dst_field='arp_tpa', n_bits=32), ofpp.OFPActionSetField(arp_sha=mac), ofpp.OFPActionSetField(arp_spa=ip), ofpp.NXActionRegMove(src_field='eth_src', dst_field='eth_dst', n_bits=48), ofpp.OFPActionSetField(eth_src=mac), ofpp.OFPActionOutput(ofp.OFPP_IN_PORT, 0)] self.install_apply_actions(table_id=constants.ARP_RESPONDER, priority=1, match=match, actions=actions) def delete_arp_responder(self, vlan, ip): (_dp, ofp, ofpp) = self._get_dp() if ip is None: # REVISIT(yamamoto): add arp_op=arp.ARP_REQUEST matcher? match = ofpp.OFPMatch(vlan_vid=vlan | ofp.OFPVID_PRESENT, eth_type=ether_types.ETH_TYPE_ARP) else: match = self._arp_responder_match(ofp, ofpp, vlan, ip) self.uninstall_flows(table_id=constants.ARP_RESPONDER, match=match) def setup_tunnel_port(self, network_type, port): self.install_goto(dest_table_id=constants.TUN_TABLE[network_type], priority=1, in_port=port) def cleanup_tunnel_port(self, port): self.uninstall_flows(in_port=port) def add_dvr_mac_tun(self, mac, port): self.install_output(table_id=constants.DVR_NOT_LEARN, priority=1, eth_src=mac, port=port) def remove_dvr_mac_tun(self, mac): # REVISIT(yamamoto): match in_port as well? self.uninstall_flows(table_id=constants.DVR_NOT_LEARN, eth_src=mac) def deferred(self): # REVISIT(yamamoto): This is for API compat with "ovs-ofctl" # interface. Consider removing this mechanism when obsoleting # "ovs-ofctl" interface. # For "ovs-ofctl" interface, "deferred" mechanism would improve # performance by batching flow-mods with a single ovs-ofctl command # invocation. # On the other hand, for this "native" interface, the overheads of # each flow-mods are already minimum and batching doesn't make much # sense. Thus this method is left as no-op. # It might be possible to send multiple flow-mods with a single # barrier. But it's unclear that level of performance optimization # is desirable while it would certainly complicate error handling. return self def __enter__(self): # REVISIT(yamamoto): See the comment on deferred(). return self def __exit__(self, exc_type, exc_value, traceback): # REVISIT(yamamoto): See the comment on deferred(). pass neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/ovs_ryuapp.py0000664000175000017500000000473413553660047032223 0ustar zuulzuul00000000000000# Copyright (C) 2015 VA Linux Systems Japan K.K. # Copyright (C) 2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools from oslo_log import log as logging from oslo_utils import excutils import ryu.app.ofctl.api # noqa from ryu.base import app_manager from ryu.lib import hub from ryu.ofproto import ofproto_v1_3 from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import br_int from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import br_phys from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import br_tun from neutron.plugins.ml2.drivers.openvswitch.agent \ import ovs_neutron_agent as ovs_agent LOG = logging.getLogger(__name__) def agent_main_wrapper(bridge_classes): try: ovs_agent.main(bridge_classes) except Exception: with excutils.save_and_reraise_exception(): LOG.exception("Agent main thread died of an exception") finally: # The following call terminates Ryu's AppManager.run_apps(), # which is needed for clean shutdown of an agent process. # The close() call must be called in another thread, otherwise # it suicides and ends prematurely. hub.spawn(app_manager.AppManager.get_instance().close) class OVSNeutronAgentRyuApp(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] def start(self): # Start Ryu event loop thread super(OVSNeutronAgentRyuApp, self).start() def _make_br_cls(br_cls): return functools.partial(br_cls, ryu_app=self) # Start agent main loop thread bridge_classes = { 'br_int': _make_br_cls(br_int.OVSIntegrationBridge), 'br_phys': _make_br_cls(br_phys.OVSPhysicalBridge), 'br_tun': _make_br_cls(br_tun.OVSTunnelBridge), } return hub.spawn(agent_main_wrapper, bridge_classes, raise_error=True) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/__init__.py0000664000175000017500000000000013553660046031530 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/ovs_bridge.py0000664000175000017500000001032613553660047032131 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from oslo_utils import excutils from neutron.agent.common import ovs_lib from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts from neutron.plugins.ml2.drivers.openvswitch.agent.openflow \ import br_cookie from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import ofswitch LOG = logging.getLogger(__name__) class OVSAgentBridge(ofswitch.OpenFlowSwitchMixin, br_cookie.OVSBridgeCookieMixin, ovs_lib.OVSBridge): """Common code for bridges used by OVS agent""" _cached_dpid = None def _get_dp(self): """Get (dp, ofp, ofpp) tuple for the switch. A convenient method for openflow message composers. """ while True: if self._cached_dpid is None: dpid = self.get_datapath_id() LOG.info("Bridge %(br_name)s has datapath-ID %(dpid)s", {"br_name": self.br_name, "dpid": dpid}) if dpid is None: raise RuntimeError("Unknown datapath id.") self._cached_dpid = int(dpid, 16) try: dp = self._get_dp_by_dpid(self._cached_dpid) return dp, dp.ofproto, dp.ofproto_parser except RuntimeError: with excutils.save_and_reraise_exception() as ctx: # Retry if dpid has been changed. # NOTE(yamamoto): Open vSwitch change its dpid on # some events. # REVISIT(yamamoto): Consider to set dpid statically. new_dpid = int(self.get_datapath_id(), 16) if new_dpid != self._cached_dpid: LOG.info("Bridge %(br_name)s changed its " "datapath-ID from %(old)x to %(new)x", { "br_name": self.br_name, "old": self._cached_dpid, "new": new_dpid, }) ctx.reraise = False self._cached_dpid = new_dpid def setup_controllers(self, conf): controllers = [ "tcp:%(address)s:%(port)s" % { "address": conf.OVS.of_listen_address, "port": conf.OVS.of_listen_port, } ] self.add_protocols(ovs_consts.OPENFLOW13) self.set_controller(controllers) # NOTE(ivc): Force "out-of-band" controller connection mode (see # "In-Band Control" [1]). # # By default openvswitch uses "in-band" controller connection mode # which adds hidden OpenFlow rules (only visible by issuing ovs-appctl # bridge/dump-flows
) and leads to a network loop on br-tun. As of # now the OF controller is hosted locally with OVS which fits the # "out-of-band" mode. If the remote OF controller is ever to be # supported by openvswitch agent in the future, "In-Band Control" [1] # should be taken into consideration for physical bridge only, but # br-int and br-tun must be configured with the "out-of-band" # controller connection mode. # # [1] https://github.com/openvswitch/ovs/blob/master/DESIGN.md self.set_controllers_connection_mode("out-of-band") self.set_controllers_inactivity_probe(conf.OVS.of_inactivity_probe) def drop_port(self, in_port): self.install_drop(priority=2, in_port=in_port) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py0000664000175000017500000032206213553660047030431 0ustar zuulzuul00000000000000# Copyright 2011 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import base64 import collections import functools import hashlib import signal import sys import time import netaddr from neutron_lib.agent import constants as agent_consts from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events as callback_events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources as callback_resources from neutron_lib import constants as n_const from neutron_lib import context from neutron_lib.utils import helpers from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_service import loopingcall from oslo_service import systemd from oslo_utils import netutils from osprofiler import profiler from six import moves from neutron._i18n import _ from neutron.agent.common import ip_lib from neutron.agent.common import ovs_lib from neutron.agent.common import polling from neutron.agent.common import utils from neutron.agent.l2 import l2_agent_extensions_manager as ext_manager from neutron.agent.linux import ovsdb_monitor from neutron.agent.linux import xenapi_root_helper from neutron.agent import rpc as agent_rpc from neutron.agent import securitygroups_rpc as agent_sg_rpc from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import dvr_rpc from neutron.api.rpc.handlers import securitygroups_rpc as sg_rpc from neutron.common import config from neutron.common import topics from neutron.common import utils as n_utils from neutron.conf.agent import xenapi_conf from neutron.plugins.common import utils as p_utils from neutron.plugins.ml2.drivers.agent import capabilities from neutron.plugins.ml2.drivers.l2pop.rpc_manager import l2population_rpc from neutron.plugins.ml2.drivers.openvswitch.agent.common \ import constants from neutron.plugins.ml2.drivers.openvswitch.agent \ import ovs_agent_extension_api as ovs_ext_api from neutron.plugins.ml2.drivers.openvswitch.agent \ import ovs_capabilities from neutron.plugins.ml2.drivers.openvswitch.agent \ import ovs_dvr_neutron_agent from neutron.plugins.ml2.drivers.openvswitch.agent import vlanmanager LOG = logging.getLogger(__name__) cfg.CONF.import_group('AGENT', 'neutron.plugins.ml2.drivers.openvswitch.' 'agent.common.config') cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.' 'common.config') class _mac_mydialect(netaddr.mac_unix): word_fmt = '%.2x' class OVSPluginApi(agent_rpc.CacheBackedPluginApi): pass def has_zero_prefixlen_address(ip_addresses): return any(netaddr.IPNetwork(ip).prefixlen == 0 for ip in ip_addresses) @profiler.trace_cls("rpc") class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, dvr_rpc.DVRAgentRpcCallbackMixin): '''Implements OVS-based tunneling, VLANs and flat networks. Two local bridges are created: an integration bridge (defaults to 'br-int') and a tunneling bridge (defaults to 'br-tun'). An additional bridge is created for each physical network interface used for VLANs and/or flat networks. All VM VIFs are plugged into the integration bridge. VM VIFs on a given virtual network share a common "local" VLAN (i.e. not propagated externally). The VLAN id of this local VLAN is mapped to the physical networking details realizing that virtual network. For virtual networks realized as GRE tunnels, a Logical Switch (LS) identifier is used to differentiate tenant traffic on inter-HV tunnels. A mesh of tunnels is created to other Hypervisors in the cloud. These tunnels originate and terminate on the tunneling bridge of each hypervisor. Port patching is done to connect local VLANs on the integration bridge to inter-hypervisor tunnels on the tunnel bridge. For each virtual network realized as a VLAN or flat network, a veth or a pair of patch ports is used to connect the local VLAN on the integration bridge with the physical network bridge, with flow rules adding, modifying, or stripping VLAN tags as necessary. ''' # history # 1.0 Initial version # 1.1 Support Security Group RPC # 1.2 Support DVR (Distributed Virtual Router) RPC # 1.3 Added param devices_to_update to security_groups_provider_updated # 1.4 Added support for network_update target = oslo_messaging.Target(version='1.4') def __init__(self, bridge_classes, ext_manager, conf=None): '''Constructor. :param bridge_classes: a dict for bridge classes. :param conf: an instance of ConfigOpts ''' super(OVSNeutronAgent, self).__init__() self.conf = conf or cfg.CONF self.ovs = ovs_lib.BaseOVS() self.ext_manager = ext_manager agent_conf = self.conf.AGENT ovs_conf = self.conf.OVS self.fullsync = False # init bridge classes with configured datapath type. self.br_int_cls, self.br_phys_cls, self.br_tun_cls = ( functools.partial(bridge_classes[b], datapath_type=ovs_conf.datapath_type) for b in ('br_int', 'br_phys', 'br_tun')) self.use_veth_interconnection = ovs_conf.use_veth_interconnection self.veth_mtu = agent_conf.veth_mtu self.available_local_vlans = set(moves.range(n_const.MIN_VLAN_TAG, n_const.MAX_VLAN_TAG + 1)) self.tunnel_types = agent_conf.tunnel_types or [] self.l2_pop = agent_conf.l2_population # TODO(ethuleau): Change ARP responder so it's not dependent on the # ML2 l2 population mechanism driver. self.enable_distributed_routing = agent_conf.enable_distributed_routing self.arp_responder_enabled = agent_conf.arp_responder and self.l2_pop host = self.conf.host self.agent_id = 'ovs-agent-%s' % host self.enable_tunneling = bool(self.tunnel_types) # Validate agent configurations self._check_agent_configurations() # Keep track of int_br's device count for use by _report_state() self.int_br_device_count = 0 self.int_br = self.br_int_cls(ovs_conf.integration_bridge) self.setup_integration_br() # Stores port update notifications for processing in main rpc loop self.updated_ports = set() # Stores port delete notifications self.deleted_ports = set() self.network_ports = collections.defaultdict(set) # keeps association between ports and ofports to detect ofport change self.vifname_to_ofport_map = {} # Stores newly created bridges self.added_bridges = list() self.bridge_mappings = self._parse_bridge_mappings( ovs_conf.bridge_mappings) self.setup_physical_bridges(self.bridge_mappings) self.vlan_manager = vlanmanager.LocalVlanManager() self._reset_tunnel_ofports() self.polling_interval = agent_conf.polling_interval self.minimize_polling = agent_conf.minimize_polling self.ovsdb_monitor_respawn_interval = ( agent_conf.ovsdb_monitor_respawn_interval or constants.DEFAULT_OVSDBMON_RESPAWN) self.local_ip = ovs_conf.local_ip self.tunnel_count = 0 self.vxlan_udp_port = agent_conf.vxlan_udp_port self.dont_fragment = agent_conf.dont_fragment self.tunnel_csum = agent_conf.tunnel_csum self.tos = ('inherit' if agent_conf.dscp_inherit else (int(agent_conf.dscp) << 2 if agent_conf.dscp else None)) self.tun_br = None self.patch_int_ofport = constants.OFPORT_INVALID self.patch_tun_ofport = constants.OFPORT_INVALID if self.enable_tunneling: # The patch_int_ofport and patch_tun_ofport are updated # here inside the call to setup_tunnel_br() self.setup_tunnel_br(ovs_conf.tunnel_bridge) self.setup_tunnel_br_flows() self.setup_rpc() agent_api = ovs_ext_api.OVSAgentExtensionAPI(self.int_br, self.tun_br) self.ext_manager.initialize( self.connection, constants.EXTENSION_DRIVER_TYPE, agent_api) self.dvr_agent = ovs_dvr_neutron_agent.OVSDVRNeutronAgent( self.context, self.dvr_plugin_rpc, self.int_br, self.tun_br, self.bridge_mappings, self.phys_brs, self.int_ofports, self.phys_ofports, self.patch_int_ofport, self.patch_tun_ofport, host, self.enable_tunneling, self.enable_distributed_routing) if self.enable_distributed_routing: self.dvr_agent.setup_dvr_flows() # Collect additional bridges to monitor self.ancillary_brs = self.setup_ancillary_bridges( ovs_conf.integration_bridge, ovs_conf.tunnel_bridge) # In order to keep existed device's local vlan unchanged, # restore local vlan mapping at start self._restore_local_vlan_map() # Security group agent support self.sg_agent = agent_sg_rpc.SecurityGroupAgentRpc( self.context, self.sg_plugin_rpc, defer_refresh_firewall=True, integration_bridge=self.int_br) self.sg_plugin_rpc.register_legacy_sg_notification_callbacks( self.sg_agent) # we default to False to provide backward compat with out of tree # firewall drivers that expect the logic that existed on the Neutron # server which only enabled hybrid plugging based on the use of the # hybrid driver. hybrid_plug = getattr(self.sg_agent.firewall, 'OVS_HYBRID_PLUG_REQUIRED', False) self.prevent_arp_spoofing = ( not self.sg_agent.firewall.provides_arp_spoofing_protection) #TODO(mangelajo): optimize resource_versions to only report # versions about resources which are common, # or which are used by specific extensions. self.agent_state = { 'binary': 'neutron-openvswitch-agent', 'host': host, 'topic': n_const.L2_AGENT_TOPIC, 'configurations': {'bridge_mappings': self.bridge_mappings, 'tunnel_types': self.tunnel_types, 'tunneling_ip': self.local_ip, 'l2_population': self.l2_pop, 'arp_responder_enabled': self.arp_responder_enabled, 'enable_distributed_routing': self.enable_distributed_routing, 'log_agent_heartbeats': agent_conf.log_agent_heartbeats, 'extensions': self.ext_manager.names(), 'datapath_type': ovs_conf.datapath_type, 'ovs_capabilities': self.ovs.capabilities, 'vhostuser_socket_dir': ovs_conf.vhostuser_socket_dir, portbindings.OVS_HYBRID_PLUG: hybrid_plug}, 'resource_versions': resources.LOCAL_RESOURCE_VERSIONS, 'agent_type': agent_conf.agent_type, 'start_flag': True} report_interval = agent_conf.report_interval if report_interval: heartbeat = loopingcall.FixedIntervalLoopingCall( self._report_state) heartbeat.start(interval=report_interval) # Initialize iteration counter self.iter_num = 0 self.run_daemon_loop = True self.catch_sigterm = False self.catch_sighup = False # The initialization is complete; we can start receiving messages self.connection.consume_in_threads() self.quitting_rpc_timeout = agent_conf.quitting_rpc_timeout def _parse_bridge_mappings(self, bridge_mappings): try: return helpers.parse_mappings(bridge_mappings) except ValueError as e: raise ValueError(_("Parsing bridge_mappings failed: %s.") % e) def _report_state(self): # How many devices are likely used by a VM self.agent_state.get('configurations')['devices'] = ( self.int_br_device_count) self.agent_state.get('configurations')['in_distributed_mode'] = ( self.dvr_agent.in_distributed_mode()) try: agent_status = self.state_rpc.report_state(self.context, self.agent_state, True) if agent_status == agent_consts.AGENT_REVIVED: LOG.info('Agent has just been revived. ' 'Doing a full sync.') self.fullsync = True # we only want to update resource versions on startup if self.agent_state.pop('resource_versions', None): # On initial start, we notify systemd after initialization # is complete. systemd.notify_once() if self.iter_num > 0: # agent is considered started after # initial sync with server (iter 0) is done self.agent_state.pop('start_flag', None) except Exception: LOG.exception("Failed reporting state!") def _restore_local_vlan_map(self): self._local_vlan_hints = {} # skip INVALID and UNASSIGNED to match scan_ports behavior ofport_filter = (ovs_lib.INVALID_OFPORT, ovs_lib.UNASSIGNED_OFPORT) cur_ports = self.int_br.get_vif_ports(ofport_filter) port_names = [p.port_name for p in cur_ports] port_info = self.int_br.get_ports_attributes( "Port", columns=["name", "other_config", "tag"], ports=port_names) by_name = {x['name']: x for x in port_info} for port in cur_ports: # if a port was deleted between get_vif_ports and # get_ports_attributes, we # will get a KeyError try: local_vlan_map = by_name[port.port_name]['other_config'] local_vlan = by_name[port.port_name]['tag'] except KeyError: continue if not local_vlan: continue net_uuid = local_vlan_map.get('net_uuid') if (net_uuid and net_uuid not in self._local_vlan_hints and local_vlan != constants.DEAD_VLAN_TAG): self.available_local_vlans.remove(local_vlan) self._local_vlan_hints[local_vlan_map['net_uuid']] = \ local_vlan def _dispose_local_vlan_hints(self): self.available_local_vlans.update(self._local_vlan_hints.values()) self._local_vlan_hints = {} def _reset_tunnel_ofports(self): self.tun_br_ofports = {n_const.TYPE_GENEVE: {}, n_const.TYPE_GRE: {}, n_const.TYPE_VXLAN: {}} def setup_rpc(self): self.plugin_rpc = OVSPluginApi(topics.PLUGIN) # allow us to receive port_update/delete callbacks from the cache self.plugin_rpc.register_legacy_notification_callbacks(self) self.sg_plugin_rpc = sg_rpc.SecurityGroupServerAPIShim( self.plugin_rpc.remote_resource_cache) self.dvr_plugin_rpc = dvr_rpc.DVRServerRpcApi(topics.PLUGIN) self.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS) # RPC network init self.context = context.get_admin_context_without_session() # Define the listening consumers for the agent consumers = [[constants.TUNNEL, topics.UPDATE], [constants.TUNNEL, topics.DELETE], [topics.DVR, topics.UPDATE]] if self.l2_pop: consumers.append([topics.L2POPULATION, topics.UPDATE]) self.connection = agent_rpc.create_consumers([self], topics.AGENT, consumers, start_listening=False) def port_update(self, context, **kwargs): port = kwargs.get('port') # Put the port identifier in the updated_ports set. # Even if full port details might be provided to this call, # they are not used since there is no guarantee the notifications # are processed in the same order as the relevant API requests self.updated_ports.add(port['id']) def port_delete(self, context, **kwargs): port_id = kwargs.get('port_id') self.deleted_ports.add(port_id) self.updated_ports.discard(port_id) def network_update(self, context, **kwargs): network_id = kwargs['network']['id'] for port_id in self.network_ports[network_id]: # notifications could arrive out of order, if the port is deleted # we don't want to update it anymore if port_id not in self.deleted_ports: self.updated_ports.add(port_id) LOG.debug("network_update message processed for network " "%(network_id)s, with ports: %(ports)s", {'network_id': network_id, 'ports': self.network_ports[network_id]}) def _clean_network_ports(self, port_id): for port_set in self.network_ports.values(): if port_id in port_set: port_set.remove(port_id) break def process_deleted_ports(self, port_info): # don't try to process removed ports as deleted ports since # they are already gone if 'removed' in port_info: self.deleted_ports -= port_info['removed'] deleted_ports = list(self.deleted_ports) while self.deleted_ports: port_id = self.deleted_ports.pop() port = self.int_br.get_vif_port_by_id(port_id) self._clean_network_ports(port_id) self.ext_manager.delete_port(self.context, {"vif_port": port, "port_id": port_id}) # move to dead VLAN so deleted ports no # longer have access to the network if port: # don't log errors since there is a chance someone will be # removing the port from the bridge at the same time self.port_dead(port, log_errors=False) self.port_unbound(port_id) # Flush firewall rules after ports are put on dead VLAN to be # more secure self.sg_agent.remove_devices_filter(deleted_ports) def tunnel_update(self, context, **kwargs): LOG.debug("tunnel_update received") if not self.enable_tunneling: return tunnel_ip = kwargs.get('tunnel_ip') tunnel_type = kwargs.get('tunnel_type') if not tunnel_type: LOG.error("No tunnel_type specified, cannot create tunnels") return if tunnel_type not in self.tunnel_types: LOG.error("tunnel_type %s not supported by agent", tunnel_type) return if tunnel_ip == self.local_ip: return tun_name = self.get_tunnel_name(tunnel_type, self.local_ip, tunnel_ip) if tun_name is None: return if not self.l2_pop: self._setup_tunnel_port(self.tun_br, tun_name, tunnel_ip, tunnel_type) self._setup_tunnel_flood_flow(self.tun_br, tunnel_type) def tunnel_delete(self, context, **kwargs): LOG.debug("tunnel_delete received") if not self.enable_tunneling: return tunnel_ip = kwargs.get('tunnel_ip') if not tunnel_ip: LOG.error("No tunnel_ip specified, cannot delete tunnels") return tunnel_type = kwargs.get('tunnel_type') if not tunnel_type: LOG.error("No tunnel_type specified, cannot delete tunnels") return if tunnel_type not in self.tunnel_types: LOG.error("tunnel_type %s not supported by agent", tunnel_type) return ofport = self.tun_br_ofports[tunnel_type].get(tunnel_ip) self.cleanup_tunnel_port(self.tun_br, ofport, tunnel_type) def _tunnel_port_lookup(self, network_type, remote_ip): return self.tun_br_ofports[network_type].get(remote_ip) def fdb_add(self, context, fdb_entries): LOG.debug("fdb_add received") for lvm, agent_ports in self.get_agent_ports(fdb_entries): agent_ports.pop(self.local_ip, None) if len(agent_ports): if not self.enable_distributed_routing: with self.tun_br.deferred() as deferred_br: self.fdb_add_tun(context, deferred_br, lvm, agent_ports, self._tunnel_port_lookup) else: self.fdb_add_tun(context, self.tun_br, lvm, agent_ports, self._tunnel_port_lookup) def fdb_remove(self, context, fdb_entries): LOG.debug("fdb_remove received") for lvm, agent_ports in self.get_agent_ports(fdb_entries): agent_ports.pop(self.local_ip, None) if len(agent_ports): if not self.enable_distributed_routing: with self.tun_br.deferred() as deferred_br: self.fdb_remove_tun(context, deferred_br, lvm, agent_ports, self._tunnel_port_lookup) else: self.fdb_remove_tun(context, self.tun_br, lvm, agent_ports, self._tunnel_port_lookup) def add_fdb_flow(self, br, port_info, remote_ip, lvm, ofport): if port_info == n_const.FLOODING_ENTRY: lvm.tun_ofports.add(ofport) br.install_flood_to_tun(lvm.vlan, lvm.segmentation_id, lvm.tun_ofports) else: self.setup_entry_for_arp_reply(br, 'add', lvm.vlan, port_info.mac_address, port_info.ip_address) br.install_unicast_to_tun(lvm.vlan, lvm.segmentation_id, ofport, port_info.mac_address) def del_fdb_flow(self, br, port_info, remote_ip, lvm, ofport): if port_info == n_const.FLOODING_ENTRY: if ofport not in lvm.tun_ofports: LOG.debug("attempt to remove a non-existent port %s", ofport) return lvm.tun_ofports.remove(ofport) if len(lvm.tun_ofports) > 0: br.install_flood_to_tun(lvm.vlan, lvm.segmentation_id, lvm.tun_ofports) else: # This local vlan doesn't require any more tunneling br.delete_flood_to_tun(lvm.vlan) else: self.setup_entry_for_arp_reply(br, 'remove', lvm.vlan, port_info.mac_address, port_info.ip_address) br.delete_unicast_to_tun(lvm.vlan, port_info.mac_address) def _fdb_chg_ip(self, context, fdb_entries): LOG.debug("update chg_ip received") with self.tun_br.deferred() as deferred_br: self.fdb_chg_ip_tun(context, deferred_br, fdb_entries, self.local_ip) def setup_entry_for_arp_reply(self, br, action, local_vid, mac_address, ip_address): '''Set the ARP respond entry. When the l2 population mechanism driver and OVS supports to edit ARP fields, a table (ARP_RESPONDER) to resolve ARP locally is added to the tunnel bridge. ''' if not self.arp_responder_enabled: return ip = netaddr.IPAddress(ip_address) if ip.version == 6: return ip = str(ip) mac = str(netaddr.EUI(mac_address, dialect=_mac_mydialect)) if action == 'add': br.install_arp_responder(local_vid, ip, mac) elif action == 'remove': br.delete_arp_responder(local_vid, ip) else: LOG.warning('Action %s not supported', action) def _local_vlan_for_flat(self, lvid, physical_network): phys_br = self.phys_brs[physical_network] phys_port = self.phys_ofports[physical_network] int_br = self.int_br int_port = self.int_ofports[physical_network] phys_br.provision_local_vlan(port=phys_port, lvid=lvid, segmentation_id=None, distributed=False) int_br.provision_local_vlan(port=int_port, lvid=lvid, segmentation_id=None) def _local_vlan_for_vlan(self, lvid, physical_network, segmentation_id): distributed = self.enable_distributed_routing phys_br = self.phys_brs[physical_network] phys_port = self.phys_ofports[physical_network] int_br = self.int_br int_port = self.int_ofports[physical_network] phys_br.provision_local_vlan(port=phys_port, lvid=lvid, segmentation_id=segmentation_id, distributed=distributed) int_br.provision_local_vlan(port=int_port, lvid=lvid, segmentation_id=segmentation_id) def provision_local_vlan(self, net_uuid, network_type, physical_network, segmentation_id): '''Provisions a local VLAN. :param net_uuid: the uuid of the network associated with this vlan. :param network_type: the network type ('gre', 'vxlan', 'vlan', 'flat', 'local', 'geneve') :param physical_network: the physical network for 'vlan' or 'flat' :param segmentation_id: the VID for 'vlan' or tunnel ID for 'tunnel' ''' # On a restart or crash of OVS, the network associated with this VLAN # will already be assigned, so check for that here before assigning a # new one. try: lvm = self.vlan_manager.get(net_uuid) lvid = lvm.vlan except vlanmanager.MappingNotFound: lvid = self._local_vlan_hints.pop(net_uuid, None) if lvid is None: if not self.available_local_vlans: LOG.error("No local VLAN available for net-id=%s", net_uuid) return lvid = self.available_local_vlans.pop() self.vlan_manager.add( net_uuid, lvid, network_type, physical_network, segmentation_id) LOG.info("Assigning %(vlan_id)s as local vlan for " "net-id=%(net_uuid)s", {'vlan_id': lvid, 'net_uuid': net_uuid}) if network_type in constants.TUNNEL_NETWORK_TYPES: if self.enable_tunneling: # outbound broadcast/multicast ofports = list(self.tun_br_ofports[network_type].values()) if ofports: self.tun_br.install_flood_to_tun(lvid, segmentation_id, ofports) # inbound from tunnels: set lvid in the right table # and resubmit to Table LEARN_FROM_TUN for mac learning if self.enable_distributed_routing: self.dvr_agent.process_tunneled_network( network_type, lvid, segmentation_id) else: self.tun_br.provision_local_vlan( network_type=network_type, lvid=lvid, segmentation_id=segmentation_id) else: LOG.error("Cannot provision %(network_type)s network for " "net-id=%(net_uuid)s - tunneling disabled", {'network_type': network_type, 'net_uuid': net_uuid}) elif network_type == n_const.TYPE_FLAT: if physical_network in self.phys_brs: self._local_vlan_for_flat(lvid, physical_network) else: LOG.error("Cannot provision flat network for " "net-id=%(net_uuid)s - no bridge for " "physical_network %(physical_network)s", {'net_uuid': net_uuid, 'physical_network': physical_network}) elif network_type == n_const.TYPE_VLAN: if physical_network in self.phys_brs: self._local_vlan_for_vlan(lvid, physical_network, segmentation_id) else: LOG.error("Cannot provision VLAN network for " "net-id=%(net_uuid)s - no bridge for " "physical_network %(physical_network)s", {'net_uuid': net_uuid, 'physical_network': physical_network}) elif network_type == n_const.TYPE_LOCAL: # no flows needed for local networks pass else: LOG.error("Cannot provision unknown network type " "%(network_type)s for net-id=%(net_uuid)s", {'network_type': network_type, 'net_uuid': net_uuid}) def reclaim_local_vlan(self, net_uuid): '''Reclaim a local VLAN. :param net_uuid: the network uuid associated with this vlan. ''' try: lvm = vlanmanager.LocalVlanManager().pop(net_uuid) except KeyError: LOG.debug("Network %s not used on agent.", net_uuid) return LOG.info("Reclaiming vlan = %(vlan_id)s from " "net-id = %(net_uuid)s", {'vlan_id': lvm.vlan, 'net_uuid': net_uuid}) if lvm.network_type in constants.TUNNEL_NETWORK_TYPES: if self.enable_tunneling: self.tun_br.reclaim_local_vlan( network_type=lvm.network_type, segmentation_id=lvm.segmentation_id) self.tun_br.delete_flood_to_tun(lvm.vlan) self.tun_br.delete_unicast_to_tun(lvm.vlan, None) self.tun_br.delete_arp_responder(lvm.vlan, None) if self.l2_pop: # Try to remove tunnel ports if not used by other networks for ofport in lvm.tun_ofports: self.cleanup_tunnel_port(self.tun_br, ofport, lvm.network_type) elif lvm.network_type == n_const.TYPE_FLAT: if lvm.physical_network in self.phys_brs: # outbound br = self.phys_brs[lvm.physical_network] br.reclaim_local_vlan( port=self.phys_ofports[lvm.physical_network], lvid=lvm.vlan) # inbound br = self.int_br br.reclaim_local_vlan( port=self.int_ofports[lvm.physical_network], segmentation_id=None) elif lvm.network_type == n_const.TYPE_VLAN: if lvm.physical_network in self.phys_brs: # outbound br = self.phys_brs[lvm.physical_network] br.reclaim_local_vlan( port=self.phys_ofports[lvm.physical_network], lvid=lvm.vlan) # inbound br = self.int_br br.reclaim_local_vlan( port=self.int_ofports[lvm.physical_network], segmentation_id=lvm.segmentation_id) elif lvm.network_type == n_const.TYPE_LOCAL: # no flows needed for local networks pass else: LOG.error("Cannot reclaim unknown network type " "%(network_type)s for net-id=%(net_uuid)s", {'network_type': lvm.network_type, 'net_uuid': net_uuid}) self.available_local_vlans.add(lvm.vlan) def port_bound(self, port, net_uuid, network_type, physical_network, segmentation_id, fixed_ips, device_owner, provisioning_needed): '''Bind port to net_uuid/lsw_id and install flow for inbound traffic to vm. :param port: an ovs_lib.VifPort object. :param net_uuid: the net_uuid this port is to be associated with. :param network_type: the network type ('gre', 'vlan', 'flat', 'local') :param physical_network: the physical network for 'vlan' or 'flat' :param segmentation_id: the VID for 'vlan' or tunnel ID for 'tunnel' :param fixed_ips: the ip addresses assigned to this port :param device_owner: the string indicative of owner of this port :param provisioning_needed: indicates if this is called for an OVS restart or recreated physical bridges and requires to do local vlan provisioning ''' if net_uuid not in self.vlan_manager or provisioning_needed: self.provision_local_vlan(net_uuid, network_type, physical_network, segmentation_id) lvm = self.vlan_manager.get(net_uuid) lvm.vif_ports[port.vif_id] = port self.dvr_agent.bind_port_to_dvr(port, lvm, fixed_ips, device_owner) port_other_config = self.int_br.db_get_val("Port", port.port_name, "other_config") if port_other_config is None: if port.vif_id in self.deleted_ports: LOG.debug("Port %s deleted concurrently", port.vif_id) elif port.vif_id in self.updated_ports: LOG.error("Expected port %s not found", port.vif_id) else: LOG.debug("Unable to get config for port %s", port.vif_id) return False vlan_mapping = {'net_uuid': net_uuid, 'network_type': network_type, 'physical_network': str(physical_network)} if segmentation_id is not None: vlan_mapping['segmentation_id'] = str(segmentation_id) port_other_config.update(vlan_mapping) self.int_br.set_db_attribute("Port", port.port_name, "other_config", port_other_config) return True def _add_port_tag_info(self, need_binding_ports): port_names = [p['vif_port'].port_name for p in need_binding_ports] port_info = self.int_br.get_ports_attributes( "Port", columns=["name", "tag", "other_config"], ports=port_names, if_exists=True) info_by_port = { x['name']: { 'tag': x['tag'], 'other_config': x['other_config'] or {} } for x in port_info } for port_detail in need_binding_ports: try: lvm = self.vlan_manager.get(port_detail['network_id']) except vlanmanager.MappingNotFound: continue port = port_detail['vif_port'] try: cur_info = info_by_port[port.port_name] except KeyError: continue str_vlan = str(lvm.vlan) other_config = cur_info['other_config'] if (cur_info['tag'] != lvm.vlan or other_config.get('tag') != str_vlan): other_config['tag'] = str_vlan self.int_br.set_db_attribute( "Port", port.port_name, "other_config", other_config) # Uninitialized port has tag set to [] if cur_info['tag']: LOG.warning("Uninstall flows of ofport %s due to " "local vlan change.", port.ofport) self.int_br.uninstall_flows(in_port=port.ofport) def _bind_devices(self, need_binding_ports): devices_up = [] devices_down = [] failed_devices = [] port_names = [p['vif_port'].port_name for p in need_binding_ports] port_info = self.int_br.get_ports_attributes( "Port", columns=["name", "tag"], ports=port_names, if_exists=True) tags_by_name = {x['name']: x['tag'] for x in port_info} for port_detail in need_binding_ports: try: lvm = self.vlan_manager.get(port_detail['network_id']) except vlanmanager.MappingNotFound: # network for port was deleted. skip this port since it # will need to be handled as a DEAD port in the next scan continue port = port_detail['vif_port'] device = port_detail['device'] # Do not bind a port if it's already bound cur_tag = tags_by_name.get(port.port_name) if cur_tag is None: LOG.debug("Port %s was deleted concurrently, skipping it", port.port_name) continue if self.prevent_arp_spoofing: self.setup_arp_spoofing_protection(self.int_br, port, port_detail) if cur_tag != lvm.vlan: self.int_br.set_db_attribute( "Port", port.port_name, "tag", lvm.vlan) # update plugin about port status # FIXME(salv-orlando): Failures while updating device status # must be handled appropriately. Otherwise this might prevent # neutron server from sending network-vif-* events to the nova # API server, thus possibly preventing instance spawn. if port_detail.get('admin_state_up'): LOG.debug("Setting status for %s to UP", device) devices_up.append(device) else: LOG.debug("Setting status for %s to DOWN", device) devices_down.append(device) if devices_up or devices_down: # When the iter_num == 0, that indicate the ovs-agent is doing # the initialization work. L2 pop needs this precise knowledge # to notify the agent to refresh the tunnel related flows. # Otherwise, these flows will be cleaned as stale due to the # different cookie id. agent_restarted = self.iter_num == 0 devices_set = self.plugin_rpc.update_device_list( self.context, devices_up, devices_down, self.agent_id, self.conf.host, agent_restarted=agent_restarted) failed_devices = (devices_set.get('failed_devices_up') + devices_set.get('failed_devices_down')) if failed_devices: LOG.error("Configuration for devices %s failed!", failed_devices) LOG.info("Configuration for devices up %(up)s and devices " "down %(down)s completed.", {'up': devices_up, 'down': devices_down}) return set(failed_devices) @staticmethod def setup_arp_spoofing_protection(bridge, vif, port_details): if not port_details.get('port_security_enabled', True): LOG.info("Skipping ARP spoofing rules for port '%s' because " "it has port security disabled", vif.port_name) bridge.delete_arp_spoofing_protection(port=vif.ofport) bridge.set_allowed_macs_for_port(port=vif.ofport, allow_all=True) return if port_details['device_owner'].startswith( n_const.DEVICE_OWNER_NETWORK_PREFIX): LOG.debug("Skipping ARP spoofing rules for network owned port " "'%s'.", vif.port_name) bridge.delete_arp_spoofing_protection(port=vif.ofport) bridge.set_allowed_macs_for_port(port=vif.ofport, allow_all=True) return # clear any previous flows related to this port in our ARP table bridge.delete_arp_spoofing_allow_rules(port=vif.ofport) # collect all of the addresses and cidrs that belong to the port addresses = {f['ip_address'] for f in port_details['fixed_ips']} mac_addresses = {vif.vif_mac} if port_details.get('allowed_address_pairs'): addresses |= {p['ip_address'] for p in port_details['allowed_address_pairs']} mac_addresses |= {p['mac_address'] for p in port_details['allowed_address_pairs'] if p.get('mac_address')} bridge.set_allowed_macs_for_port(vif.ofport, mac_addresses) ipv6_addresses = {ip for ip in addresses if netaddr.IPNetwork(ip).version == 6} # Allow neighbor advertisements for LLA address. ipv6_addresses |= {str(netutils.get_ipv6_addr_by_EUI64( n_const.IPv6_LLA_PREFIX, mac)) for mac in mac_addresses} if not has_zero_prefixlen_address(ipv6_addresses): # Install protection only when prefix is not zero because a /0 # prefix allows any address anyway and the nd_target can only # match on /1 or more. bridge.install_icmpv6_na_spoofing_protection(port=vif.ofport, ip_addresses=ipv6_addresses) ipv4_addresses = {ip for ip in addresses if netaddr.IPNetwork(ip).version == 4} if not has_zero_prefixlen_address(ipv4_addresses): # Install protection only when prefix is not zero because a /0 # prefix allows any address anyway and the ARP_SPA can only # match on /1 or more. bridge.install_arp_spoofing_protection(port=vif.ofport, ip_addresses=ipv4_addresses) else: bridge.delete_arp_spoofing_protection(port=vif.ofport) def port_unbound(self, vif_id, net_uuid=None): '''Unbind port. Removes corresponding local vlan mapping object if this is its last VIF. :param vif_id: the id of the vif :param net_uuid: the net_uuid this port is associated with. ''' try: net_uuid = net_uuid or self.vlan_manager.get_net_uuid(vif_id) except vlanmanager.VifIdNotFound: LOG.info( 'port_unbound(): net_uuid %s not managed by VLAN manager', net_uuid) return lvm = self.vlan_manager.get(net_uuid) if vif_id in lvm.vif_ports: vif_port = lvm.vif_ports[vif_id] self.dvr_agent.unbind_port_from_dvr(vif_port, lvm) lvm.vif_ports.pop(vif_id, None) if not lvm.vif_ports: self.reclaim_local_vlan(net_uuid) def port_dead(self, port, log_errors=True): '''Once a port has no binding, put it on the "dead vlan". :param port: an ovs_lib.VifPort object. ''' # Don't kill a port if it's already dead cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag", log_errors=log_errors) if cur_tag and cur_tag != constants.DEAD_VLAN_TAG: self.int_br.set_db_attribute("Port", port.port_name, "tag", constants.DEAD_VLAN_TAG, log_errors=log_errors) self.int_br.drop_port(in_port=port.ofport) def setup_integration_br(self): '''Setup the integration bridge. ''' # Ensure the integration bridge is created. # ovs_lib.OVSBridge.create() will run # ovs-vsctl -- --may-exist add-br BRIDGE_NAME # which does nothing if bridge already exists. self.int_br.create() self.int_br.set_secure_mode() self.int_br.setup_controllers(self.conf) if self.conf.AGENT.drop_flows_on_start: # Delete the patch port between br-int and br-tun if we're deleting # the flows on br-int, so that traffic doesn't get flooded over # while flows are missing. self.int_br.delete_port(self.conf.OVS.int_peer_patch_port) self.int_br.uninstall_flows(cookie=ovs_lib.COOKIE_ANY) self.int_br.setup_default_table() def setup_ancillary_bridges(self, integ_br, tun_br): '''Setup ancillary bridges - for example br-ex.''' ovs = ovs_lib.BaseOVS() ovs_bridges = set(ovs.get_bridges()) # Remove all known bridges ovs_bridges.remove(integ_br) if self.enable_tunneling: ovs_bridges.remove(tun_br) br_names = [self.phys_brs[physical_network].br_name for physical_network in self.phys_brs] ovs_bridges.difference_update(br_names) # Filter list of bridges to those that have external # bridge-id's configured br_names = [] for bridge in ovs_bridges: bridge_id = ovs.get_bridge_external_bridge_id(bridge, log_errors=False) if bridge_id != bridge: br_names.append(bridge) ovs_bridges.difference_update(br_names) ancillary_bridges = [] for bridge in ovs_bridges: br = ovs_lib.OVSBridge(bridge) LOG.info('Adding %s to list of bridges.', bridge) ancillary_bridges.append(br) return ancillary_bridges def setup_tunnel_br(self, tun_br_name=None): '''(re)initialize the tunnel bridge. Creates tunnel bridge, and links it to the integration bridge using a patch port. :param tun_br_name: the name of the tunnel bridge. ''' if not self.tun_br: self.tun_br = self.br_tun_cls(tun_br_name) # tun_br.create() won't recreate bridge if it exists, but will handle # cases where something like datapath_type has changed self.tun_br.create(secure_mode=True) self.tun_br.setup_controllers(self.conf) if (not self.int_br.port_exists(self.conf.OVS.int_peer_patch_port) or self.patch_tun_ofport == ovs_lib.INVALID_OFPORT): self.patch_tun_ofport = self.int_br.add_patch_port( self.conf.OVS.int_peer_patch_port, self.conf.OVS.tun_peer_patch_port) if (not self.tun_br.port_exists(self.conf.OVS.tun_peer_patch_port) or self.patch_int_ofport == ovs_lib.INVALID_OFPORT): self.patch_int_ofport = self.tun_br.add_patch_port( self.conf.OVS.tun_peer_patch_port, self.conf.OVS.int_peer_patch_port) if ovs_lib.INVALID_OFPORT in (self.patch_tun_ofport, self.patch_int_ofport): LOG.error("Failed to create OVS patch port. Cannot have " "tunneling enabled on this agent, since this " "version of OVS does not support tunnels or patch " "ports. Agent terminated!") sys.exit(1) if self.conf.AGENT.drop_flows_on_start: self.tun_br.uninstall_flows(cookie=ovs_lib.COOKIE_ANY) def setup_tunnel_br_flows(self): '''Setup the tunnel bridge. Add all flows to the tunnel bridge. ''' self.tun_br.setup_default_table(self.patch_int_ofport, self.arp_responder_enabled) def _reconfigure_physical_bridges(self, bridges): try: sync = self._do_reconfigure_physical_bridges(bridges) self.added_bridges = [] except RuntimeError: # If there was error and bridges aren't properly reconfigured, # there is no need to do full sync once again. It will be done when # reconfiguration of physical bridges will be finished without # errors sync = False self.added_bridges = bridges LOG.warning("RuntimeError during setup of physical bridges: %s", bridges) return sync def _do_reconfigure_physical_bridges(self, bridges): sync = False bridge_mappings = {} for bridge in bridges: LOG.info("Physical bridge %s was just re-created.", bridge) for phys_net, phys_br in self.bridge_mappings.items(): if bridge == phys_br: bridge_mappings[phys_net] = bridge if bridge_mappings: sync = True self.setup_physical_bridges(bridge_mappings) return sync def _check_bridge_datapath_id(self, bridge, datapath_ids_set): """Check for bridges with duplicate datapath-id Bottom 48 bits auto-derived from MAC of NIC. Upper 12 bits free, so we OR it with (bridge # << 48) to create a unique ID It must be exactly 64 bits, else OVS will reject it - zfill :param bridge: (OVSPhysicalBridge) bridge :param datapath_ids_set: (set) used datapath ids in OVS """ dpid = int(bridge.get_datapath_id(), 16) dpid_hex = format(dpid, '0x').zfill(16) if dpid_hex in datapath_ids_set: dpid_hex = format( dpid + (len(datapath_ids_set) << 48), '0x').zfill(16) bridge.set_datapath_id(dpid_hex) LOG.info('Bridge %s datapath-id = 0x%s', bridge.br_name, dpid_hex) datapath_ids_set.add(dpid_hex) def setup_physical_bridges(self, bridge_mappings): '''Setup the physical network bridges. Creates physical network bridges and links them to the integration bridge using veths or patch ports. :param bridge_mappings: map physical network names to bridge names. ''' self.phys_brs = {} self.int_ofports = {} self.phys_ofports = {} datapath_ids_set = set() ip_wrapper = ip_lib.IPWrapper() ovs = ovs_lib.BaseOVS() ovs_bridges = ovs.get_bridges() for physical_network, bridge in bridge_mappings.items(): LOG.info("Mapping physical network %(physical_network)s to " "bridge %(bridge)s", {'physical_network': physical_network, 'bridge': bridge}) # setup physical bridge if bridge not in ovs_bridges: LOG.error("Bridge %(bridge)s for physical network " "%(physical_network)s does not exist. Agent " "terminated!", {'physical_network': physical_network, 'bridge': bridge}) sys.exit(1) br = self.br_phys_cls(bridge) self._check_bridge_datapath_id(br, datapath_ids_set) # The bridge already exists, so create won't recreate it, but will # handle things like changing the datapath_type br.create() br.set_secure_mode() br.setup_controllers(self.conf) if cfg.CONF.AGENT.drop_flows_on_start: br.uninstall_flows(cookie=ovs_lib.COOKIE_ANY) br.setup_default_table() self.phys_brs[physical_network] = br # interconnect physical and integration bridges using veth/patches int_if_name = p_utils.get_interface_name( bridge, prefix=constants.PEER_INTEGRATION_PREFIX) phys_if_name = p_utils.get_interface_name( bridge, prefix=constants.PEER_PHYSICAL_PREFIX) # Interface type of port for physical and integration bridges must # be same, so check only one of them. # Not logging error here, as the interface may not exist yet. # Type check is done to cleanup wrong interface if any. int_type = self.int_br.db_get_val("Interface", int_if_name, "type", log_errors=False) if self.use_veth_interconnection: # Drop ports if the interface types doesn't match the # configuration value. if int_type == 'patch': self.int_br.delete_port(int_if_name) br.delete_port(phys_if_name) device = ip_lib.IPDevice(int_if_name) if device.exists(): device.link.delete() # Give udev a chance to process its rules here, to avoid # race conditions between commands launched by udev rules # and the subsequent call to ip_wrapper.add_veth utils.execute(['udevadm', 'settle', '--timeout=10']) int_veth, phys_veth = ip_wrapper.add_veth(int_if_name, phys_if_name) int_ofport = self.int_br.add_port(int_if_name) phys_ofport = br.add_port(phys_if_name) else: # Drop ports if the interface type doesn't match the # configuration value if int_type == 'veth': self.int_br.delete_port(int_if_name) br.delete_port(phys_if_name) # Setup int_br to physical bridge patches. If they already # exist we leave them alone, otherwise we create them but don't # connect them until after the drop rules are in place. if self.int_br.port_exists(int_if_name): int_ofport = self.int_br.get_port_ofport(int_if_name) else: int_ofport = self.int_br.add_patch_port( int_if_name, constants.NONEXISTENT_PEER) if br.port_exists(phys_if_name): phys_ofport = br.get_port_ofport(phys_if_name) else: phys_ofport = br.add_patch_port( phys_if_name, constants.NONEXISTENT_PEER) self.int_ofports[physical_network] = int_ofport self.phys_ofports[physical_network] = phys_ofport # block all untranslated traffic between bridges self.int_br.drop_port(in_port=int_ofport) br.drop_port(in_port=phys_ofport) if self.use_veth_interconnection: # enable veth to pass traffic int_veth.link.set_up() phys_veth.link.set_up() if self.veth_mtu: # set up mtu size for veth interfaces int_veth.link.set_mtu(self.veth_mtu) phys_veth.link.set_mtu(self.veth_mtu) else: # associate patch ports to pass traffic self.int_br.set_db_attribute('Interface', int_if_name, 'options', {'peer': phys_if_name}) br.set_db_attribute('Interface', phys_if_name, 'options', {'peer': int_if_name}) def update_stale_ofport_rules(self): # ARP spoofing rules and drop-flow upon port-delete # use ofport-based rules previous = self.vifname_to_ofport_map current = self.int_br.get_vif_port_to_ofport_map() # if any ofport numbers have changed, re-process the devices as # added ports so any rules based on ofport numbers are updated. moved_ports = self._get_ofport_moves(current, previous) # delete any stale rules based on removed ofports ofports_deleted = set(previous.values()) - set(current.values()) for ofport in ofports_deleted: if self.prevent_arp_spoofing: self.int_br.delete_arp_spoofing_protection(port=ofport) self.int_br.uninstall_flows(in_port=ofport) # store map for next iteration self.vifname_to_ofport_map = current return moved_ports @staticmethod def _get_ofport_moves(current, previous): """Returns a list of moved ports. Takes two port->ofport maps and returns a list ports that moved to a different ofport. Deleted ports are not included. """ port_moves = [] for name, ofport in previous.items(): if name not in current: continue current_ofport = current[name] if ofport != current_ofport: port_moves.append(name) return port_moves def _get_port_info(self, registered_ports, cur_ports, readd_registered_ports): port_info = {'current': cur_ports, 'added': set(), 'removed': set()} # FIXME(salv-orlando): It's not really necessary to return early # if nothing has changed. if not readd_registered_ports and cur_ports == registered_ports: return port_info if readd_registered_ports: port_info['added'] = cur_ports else: port_info['added'] = cur_ports - registered_ports # Update port_info with ports not found on the integration bridge port_info['removed'] = registered_ports - cur_ports return port_info def _update_port_info_failed_devices_stats(self, port_info, failed_devices): # remove failed devices that don't need to be retried failed_devices['added'] -= port_info['removed'] failed_devices['removed'] -= port_info['added'] # Disregard devices that were never noticed by the agent port_info['removed'] &= port_info['current'] # retry failed devices port_info['added'] |= failed_devices['added'] if failed_devices['added']: LOG.debug("retrying failed devices %s", failed_devices['added']) port_info['removed'] |= failed_devices['removed'] # Update current ports port_info['current'] |= port_info['added'] port_info['current'] -= port_info['removed'] def process_ports_events(self, events, registered_ports, ancillary_ports, old_ports_not_ready, failed_devices, failed_ancillary_devices, updated_ports=None): port_info = {} port_info['added'] = set() port_info['removed'] = set() port_info['current'] = registered_ports ancillary_port_info = {} ancillary_port_info['added'] = set() ancillary_port_info['removed'] = set() ancillary_port_info['current'] = ancillary_ports ports_not_ready_yet = set() # if a port was added and then removed or viceversa since the agent # can't know the order of the operations, check the status of the port # to determine if the port was added or deleted added_ports = {p['name'] for p in events['added']} removed_ports = {p['name'] for p in events['removed']} ports_removed_and_added = added_ports & removed_ports for p in ports_removed_and_added: if ovs_lib.BaseOVS().port_exists(p): events['removed'] = [e for e in events['removed'] if e['name'] != p] else: events['added'] = [e for e in events['added'] if e['name'] != p] #TODO(rossella_s): scanning the ancillary bridge won't be needed # anymore when https://review.openstack.org/#/c/203381 since the bridge # id stored in external_ids will be used to identify the bridge the # port belongs to cur_ancillary_ports = set() for bridge in self.ancillary_brs: cur_ancillary_ports |= bridge.get_vif_port_set() cur_ancillary_ports |= ancillary_port_info['current'] def _process_port(port, ports, ancillary_ports): # check 'iface-id' is set otherwise is not a port # the agent should care about if 'attached-mac' in port.get('external_ids', []): iface_id = self.int_br.portid_from_external_ids( port['external_ids']) if iface_id: if port['ofport'] == ovs_lib.UNASSIGNED_OFPORT: LOG.debug("Port %s not ready yet on the bridge", iface_id) ports_not_ready_yet.add(port['name']) return # check if port belongs to ancillary bridge if iface_id in cur_ancillary_ports: ancillary_ports.add(iface_id) else: ports.add(iface_id) if old_ports_not_ready: old_ports_not_ready_attrs = self.int_br.get_ports_attributes( 'Interface', columns=['name', 'external_ids', 'ofport'], ports=old_ports_not_ready, if_exists=True) now_ready_ports = set( [p['name'] for p in old_ports_not_ready_attrs]) LOG.debug("Ports %s are now ready", now_ready_ports) old_ports_not_ready_yet = old_ports_not_ready - now_ready_ports removed_ports = set([p['name'] for p in events['removed']]) old_ports_not_ready_yet -= removed_ports LOG.debug("Ports %s were not ready at last iteration and are not " "ready yet", old_ports_not_ready_yet) ports_not_ready_yet |= old_ports_not_ready_yet events['added'].extend(old_ports_not_ready_attrs) for port in events['added']: _process_port(port, port_info['added'], ancillary_port_info['added']) for port in events['removed']: _process_port(port, port_info['removed'], ancillary_port_info['removed']) self._update_port_info_failed_devices_stats(port_info, failed_devices) self._update_port_info_failed_devices_stats(ancillary_port_info, failed_ancillary_devices) if updated_ports is None: updated_ports = set() updated_ports.update(self.check_changed_vlans()) if updated_ports: # Some updated ports might have been removed in the # meanwhile, and therefore should not be processed. # In this case the updated port won't be found among # current ports. updated_ports &= port_info['current'] port_info['updated'] = updated_ports return port_info, ancillary_port_info, ports_not_ready_yet def scan_ports(self, registered_ports, sync, updated_ports=None): cur_ports = self.int_br.get_vif_port_set() self.int_br_device_count = len(cur_ports) port_info = self._get_port_info(registered_ports, cur_ports, sync) if updated_ports is None: updated_ports = set() updated_ports.update(self.check_changed_vlans()) if updated_ports: # Some updated ports might have been removed in the # meanwhile, and therefore should not be processed. # In this case the updated port won't be found among # current ports. updated_ports &= cur_ports if updated_ports: port_info['updated'] = updated_ports return port_info def scan_ancillary_ports(self, registered_ports, sync): cur_ports = set() for bridge in self.ancillary_brs: cur_ports |= bridge.get_vif_port_set() return self._get_port_info(registered_ports, cur_ports, sync) def check_changed_vlans(self): """Check for changed VLAN tags. If changes, notify server and return. The returned value is a set of port ids of the ports concerned by a vlan tag loss. """ port_tags = self.int_br.get_port_tag_dict() changed_ports = set() for lvm in self.vlan_manager: for port in lvm.vif_ports.values(): if ( port.port_name in port_tags and port_tags[port.port_name] != lvm.vlan ): LOG.info( "Port '%(port_name)s' has lost " "its vlan tag '%(vlan_tag)d'!", {'port_name': port.port_name, 'vlan_tag': lvm.vlan} ) changed_ports.add(port.vif_id) if changed_ports: # explicitly mark these DOWN on the server since they have been # manipulated (likely a nova unplug/replug) and need to be rewired devices_down = self.plugin_rpc.update_device_list(self.context, [], changed_ports, self.agent_id, self.conf.host) failed_devices = set(devices_down.get('failed_devices_down')) if failed_devices: LOG.debug("Status updated failed for %s", failed_devices) return changed_ports def treat_vif_port(self, vif_port, port_id, network_id, network_type, physical_network, segmentation_id, admin_state_up, fixed_ips, device_owner, provisioning_needed): # When this function is called for a port, the port should have # an OVS ofport configured, as only these ports were considered # for being treated. If that does not happen, it is a potential # error condition of which operators should be aware port_needs_binding = True if not vif_port.ofport: LOG.warning("VIF port: %s has no ofport configured, " "and might not be able to transmit", vif_port.vif_id) if vif_port: if admin_state_up: port_needs_binding = self.port_bound( vif_port, network_id, network_type, physical_network, segmentation_id, fixed_ips, device_owner, provisioning_needed) else: LOG.info("VIF port: %s admin state up disabled, " "putting on the dead VLAN", vif_port.vif_id) self.port_dead(vif_port) self.plugin_rpc.update_device_down( self.context, port_id, self.agent_id, self.conf.host) port_needs_binding = False else: LOG.debug("No VIF port for port %s defined on agent.", port_id) return port_needs_binding def _setup_tunnel_port(self, br, port_name, remote_ip, tunnel_type): try: if (netaddr.IPAddress(self.local_ip).version != netaddr.IPAddress(remote_ip).version): LOG.error("IP version mismatch, cannot create tunnel: " "local_ip=%(lip)s remote_ip=%(rip)s", {'lip': self.local_ip, 'rip': remote_ip}) return 0 except Exception: LOG.error("Invalid local or remote IP, cannot create tunnel: " "local_ip=%(lip)s remote_ip=%(rip)s", {'lip': self.local_ip, 'rip': remote_ip}) return 0 ofport = br.add_tunnel_port(port_name, remote_ip, self.local_ip, tunnel_type, self.vxlan_udp_port, self.dont_fragment, self.tunnel_csum, self.tos) if ofport == ovs_lib.INVALID_OFPORT: LOG.error("Failed to set-up %(type)s tunnel port to %(ip)s", {'type': tunnel_type, 'ip': remote_ip}) return 0 self.tun_br_ofports[tunnel_type][remote_ip] = ofport # Add flow in default table to resubmit to the right # tunneling table (lvid will be set in the latter) br.setup_tunnel_port(tunnel_type, ofport) return ofport def _setup_tunnel_flood_flow(self, br, tunnel_type): ofports = self.tun_br_ofports[tunnel_type].values() if ofports and not self.l2_pop: # Update flooding flows to include the new tunnel for vlan_mapping in self.vlan_manager: if vlan_mapping.network_type == tunnel_type: br.install_flood_to_tun(vlan_mapping.vlan, vlan_mapping.segmentation_id, ofports) def setup_tunnel_port(self, br, remote_ip, network_type): port_name = self.get_tunnel_name( network_type, self.local_ip, remote_ip) if port_name is None: return 0 ofport = self._setup_tunnel_port(br, port_name, remote_ip, network_type) self._setup_tunnel_flood_flow(br, network_type) return ofport def cleanup_tunnel_port(self, br, tun_ofport, tunnel_type): # Check if this tunnel port is still used for lvm in self.vlan_manager: if tun_ofport in lvm.tun_ofports: break # If not, remove it else: items = list(self.tun_br_ofports[tunnel_type].items()) for remote_ip, ofport in items: if ofport == tun_ofport: port_name = self.get_tunnel_name( tunnel_type, self.local_ip, remote_ip) br.delete_port(port_name) br.cleanup_tunnel_port(ofport) self.tun_br_ofports[tunnel_type].pop(remote_ip, None) def treat_devices_added_or_updated(self, devices, provisioning_needed): skipped_devices = [] need_binding_devices = [] devices_details_list = ( self.plugin_rpc.get_devices_details_list_and_failed_devices( self.context, devices, self.agent_id, self.conf.host)) failed_devices = set(devices_details_list.get('failed_devices')) devices = devices_details_list.get('devices') vif_by_id = self.int_br.get_vifs_by_ids( [vif['device'] for vif in devices]) for details in devices: device = details['device'] LOG.debug("Processing port: %s", device) port = vif_by_id.get(device) if not port: # The port disappeared and cannot be processed LOG.info("Port %s was not found on the integration bridge " "and will therefore not be processed", device) self.ext_manager.delete_port(self.context, {'port_id': device}) skipped_devices.append(device) continue if 'port_id' in details: LOG.info("Port %(device)s updated. Details: %(details)s", {'device': device, 'details': details}) details['vif_port'] = port need_binding = self.treat_vif_port(port, details['port_id'], details['network_id'], details['network_type'], details['physical_network'], details['segmentation_id'], details['admin_state_up'], details['fixed_ips'], details['device_owner'], provisioning_needed) if need_binding: need_binding_devices.append(details) self._update_port_network(details['port_id'], details['network_id']) self.ext_manager.handle_port(self.context, details) else: LOG.warning( "Device %s not defined on plugin or binding failed", device) if (port and port.ofport != -1): self.port_dead(port) return (skipped_devices, need_binding_devices, failed_devices) def _update_port_network(self, port_id, network_id): self._clean_network_ports(port_id) self.network_ports[network_id].add(port_id) def treat_ancillary_devices_added(self, devices): devices_details_list = ( self.plugin_rpc.get_devices_details_list_and_failed_devices( self.context, devices, self.agent_id, self.conf.host)) failed_devices = set(devices_details_list.get('failed_devices')) devices_added = [ d['device'] for d in devices_details_list.get('devices')] # update plugin about port status devices_set_up = ( self.plugin_rpc.update_device_list(self.context, devices_added, [], self.agent_id, self.conf.host)) failed_devices |= set(devices_set_up.get('failed_devices_up')) LOG.info("Ancillary Ports %(added)s added, failed devices " "%(failed)s", {'added': devices, 'failed': failed_devices}) return failed_devices def treat_devices_removed(self, devices): self.sg_agent.remove_devices_filter(devices) LOG.info("Ports %s removed", devices) devices_down = self.plugin_rpc.update_device_list(self.context, [], devices, self.agent_id, self.conf.host) failed_devices = set(devices_down.get('failed_devices_down')) LOG.debug("Port removal failed for %s", failed_devices) for device in devices: self.ext_manager.delete_port(self.context, {'port_id': device}) self.port_unbound(device) return failed_devices def treat_ancillary_devices_removed(self, devices): LOG.info("Ancillary ports %s removed", devices) devices_down = self.plugin_rpc.update_device_list(self.context, [], devices, self.agent_id, self.conf.host) LOG.info("Devices down %s ", devices_down) failed_devices = set(devices_down.get('failed_devices_down')) if failed_devices: LOG.debug("Port removal failed for %s", failed_devices) for detail in devices_down.get('devices_down'): if detail['exists']: LOG.info("Port %s updated.", detail['device']) # Nothing to do regarding local networking else: LOG.debug("Device %s not defined on plugin", detail['device']) return failed_devices def treat_devices_skipped(self, devices): LOG.info("Ports %s skipped, changing status to down", devices) devices_down = self.plugin_rpc.update_device_list(self.context, [], devices, self.agent_id, self.conf.host) failed_devices = set(devices_down.get('failed_devices_down')) if failed_devices: LOG.debug("Port down failed for %s", failed_devices) def process_network_ports(self, port_info, provisioning_needed): failed_devices = {'added': set(), 'removed': set()} # TODO(salv-orlando): consider a solution for ensuring notifications # are processed exactly in the same order in which they were # received. This is tricky because there are two notification # sources: the neutron server, and the ovs db monitor process # If there is an exception while processing security groups ports # will not be wired anyway, and a resync will be triggered # VIF wiring needs to be performed always for 'new' devices. # For updated ports, re-wiring is not needed in most cases, but needs # to be performed anyway when the admin state of a device is changed. # A device might be both in the 'added' and 'updated' # list at the same time; avoid processing it twice. devices_added_updated = (port_info.get('added', set()) | port_info.get('updated', set())) need_binding_devices = [] skipped_devices = set() start = time.time() if devices_added_updated: (skipped_devices, need_binding_devices, failed_devices['added']) = ( self.treat_devices_added_or_updated( devices_added_updated, provisioning_needed)) LOG.debug("process_network_ports - iteration:%(iter_num)d - " "treat_devices_added_or_updated completed. " "Skipped %(num_skipped)d devices of " "%(num_current)d devices currently available. " "Time elapsed: %(elapsed).3f", {'iter_num': self.iter_num, 'num_skipped': len(skipped_devices), 'num_current': len(port_info['current']), 'elapsed': time.time() - start}) # Update the list of current ports storing only those which # have been actually processed. skipped_devices = set(skipped_devices) port_info['current'] = (port_info['current'] - skipped_devices) # TODO(salv-orlando): Optimize avoiding applying filters # unnecessarily, (eg: when there are no IP address changes) added_ports = port_info.get('added', set()) - skipped_devices self._add_port_tag_info(need_binding_devices) self.sg_agent.setup_port_filters(added_ports, port_info.get('updated', set())) LOG.info("process_network_ports - iteration:%(iter_num)d - " "agent port security group processed in %(elapsed).3f", {'iter_num': self.iter_num, 'elapsed': time.time() - start}) failed_devices['added'] |= self._bind_devices(need_binding_devices) if 'removed' in port_info and port_info['removed']: start = time.time() failed_devices['removed'] |= self.treat_devices_removed( port_info['removed']) LOG.debug("process_network_ports - iteration:%(iter_num)d - " "treat_devices_removed completed in %(elapsed).3f", {'iter_num': self.iter_num, 'elapsed': time.time() - start}) if skipped_devices: start = time.time() self.treat_devices_skipped(skipped_devices) LOG.debug("process_network_ports - iteration:%(iter_num)d - " "treat_devices_skipped completed in %(elapsed).3f", {'iter_num': self.iter_num, 'elapsed': time.time() - start}) return failed_devices def process_ancillary_network_ports(self, port_info): failed_devices = {'added': set(), 'removed': set()} if 'added' in port_info and port_info['added']: start = time.time() failed_added = self.treat_ancillary_devices_added( port_info['added']) LOG.debug("process_ancillary_network_ports - iteration: " "%(iter_num)d - treat_ancillary_devices_added " "completed in %(elapsed).3f", {'iter_num': self.iter_num, 'elapsed': time.time() - start}) failed_devices['added'] = failed_added if 'removed' in port_info and port_info['removed']: start = time.time() failed_removed = self.treat_ancillary_devices_removed( port_info['removed']) failed_devices['removed'] = failed_removed LOG.debug("process_ancillary_network_ports - iteration: " "%(iter_num)d - treat_ancillary_devices_removed " "completed in %(elapsed).3f", {'iter_num': self.iter_num, 'elapsed': time.time() - start}) return failed_devices @classmethod def get_tunnel_hash(cls, ip_address, hashlen): try: addr = netaddr.IPAddress(ip_address) if addr.version == n_const.IP_VERSION_4: # We cannot change this from 8, since it could break # backwards-compatibility return '%08x' % addr else: # Create 32-bit Base32 encoded hash sha1 = hashlib.sha1(ip_address.encode()) iphash = base64.b32encode(sha1.digest()) return iphash[:hashlen].decode().lower() except Exception: LOG.warning("Invalid remote IP: %s", ip_address) return def tunnel_sync(self): LOG.debug("Configuring tunnel endpoints to other OVS agents") try: for tunnel_type in self.tunnel_types: details = self.plugin_rpc.tunnel_sync(self.context, self.local_ip, tunnel_type, self.conf.host) if not self.l2_pop: tunnels = details['tunnels'] for tunnel in tunnels: if self.local_ip != tunnel['ip_address']: remote_ip = tunnel['ip_address'] tun_name = self.get_tunnel_name( tunnel_type, self.local_ip, remote_ip) if tun_name is None: continue self._setup_tunnel_port(self.tun_br, tun_name, tunnel['ip_address'], tunnel_type) self._setup_tunnel_flood_flow(self.tun_br, tunnel_type) except Exception as e: LOG.debug("Unable to sync tunnel IP %(local_ip)s: %(e)s", {'local_ip': self.local_ip, 'e': e}) return True return False @classmethod def get_tunnel_name(cls, network_type, local_ip, remote_ip): # This string is used to build port and interface names in OVS. # Port and interface names can be max 16 characters long, # including NULL, and must be unique per table per host. # We make the name as long as possible given the network_type, # for example, 'vxlan-012345678' or 'geneve-01234567'. # Remove length of network type and dash hashlen = n_const.DEVICE_NAME_MAX_LEN - len(network_type) - 1 remote_tunnel_hash = cls.get_tunnel_hash(remote_ip, hashlen) if not remote_tunnel_hash: return None return '%s-%s' % (network_type, remote_tunnel_hash) def _agent_has_updates(self, polling_manager): return (polling_manager.is_polling_required or self.updated_ports or self.deleted_ports or self.sg_agent.firewall_refresh_needed()) def _port_info_has_changes(self, port_info): return (port_info.get('added') or port_info.get('removed') or port_info.get('updated')) def check_ovs_status(self): try: # Check for the canary flow status = self.int_br.check_canary_table() except Exception: LOG.exception("Failure while checking for the canary flow") status = constants.OVS_DEAD if status == constants.OVS_RESTARTED: LOG.warning("OVS is restarted. OVSNeutronAgent will reset " "bridges and recover ports.") elif status == constants.OVS_DEAD: LOG.warning("OVS is dead. OVSNeutronAgent will keep running " "and checking OVS status periodically.") return status def loop_count_and_wait(self, start_time, port_stats): # sleep till end of polling interval elapsed = time.time() - start_time LOG.debug("Agent rpc_loop - iteration:%(iter_num)d " "completed. Processed ports statistics: " "%(port_stats)s. Elapsed:%(elapsed).3f", {'iter_num': self.iter_num, 'port_stats': port_stats, 'elapsed': elapsed}) if elapsed < self.polling_interval: time.sleep(self.polling_interval - elapsed) else: LOG.debug("Loop iteration exceeded interval " "(%(polling_interval)s vs. %(elapsed)s)!", {'polling_interval': self.polling_interval, 'elapsed': elapsed}) self.iter_num = self.iter_num + 1 def get_port_stats(self, port_info, ancillary_port_info): port_stats = { 'regular': { 'added': len(port_info.get('added', [])), 'updated': len(port_info.get('updated', [])), 'removed': len(port_info.get('removed', []))}} if self.ancillary_brs: port_stats['ancillary'] = { 'added': len(ancillary_port_info.get('added', [])), 'removed': len(ancillary_port_info.get('removed', []))} return port_stats def cleanup_stale_flows(self): LOG.info("Cleaning stale %s flows", self.int_br.br_name) self.int_br.cleanup_flows() for pby_br in self.phys_brs.values(): LOG.info("Cleaning stale %s flows", pby_br.br_name) pby_br.cleanup_flows() if self.enable_tunneling: LOG.info("Cleaning stale %s flows", self.tun_br.br_name) self.tun_br.cleanup_flows() def process_port_info(self, start, polling_manager, sync, ovs_restarted, ports, ancillary_ports, updated_ports_copy, consecutive_resyncs, ports_not_ready_yet, failed_devices, failed_ancillary_devices): # There are polling managers that don't have get_events, e.g. # AlwaysPoll used by windows implementations # REVISIT (rossella_s) This needs to be reworked to hide implementation # details regarding polling in BasePollingManager subclasses if sync or not (hasattr(polling_manager, 'get_events')): if sync: LOG.info("Agent out of sync with plugin!") consecutive_resyncs = consecutive_resyncs + 1 if (consecutive_resyncs >= constants.MAX_DEVICE_RETRIES): LOG.warning( "Clearing cache of registered ports," " retries to resync were > %s", constants.MAX_DEVICE_RETRIES) ports.clear() ancillary_ports.clear() consecutive_resyncs = 0 else: consecutive_resyncs = 0 # TODO(rossella_s): For implementations that use AlwaysPoll # resync if a device failed. This can be improved in future sync = (any(failed_devices.values()) or any(failed_ancillary_devices.values())) # NOTE(rossella_s) don't empty the queue of events # calling polling_manager.get_events() since # the agent might miss some event (for example a port # deletion) reg_ports = (set() if ovs_restarted else ports) port_info = self.scan_ports(reg_ports, sync, updated_ports_copy) # Treat ancillary devices if they exist if self.ancillary_brs: ancillary_port_info = self.scan_ancillary_ports( ancillary_ports, sync) LOG.debug("Agent rpc_loop - iteration:%(iter_num)d" " - ancillary port info retrieved. " "Elapsed:%(elapsed).3f", {'iter_num': self.iter_num, 'elapsed': time.time() - start}) else: ancillary_port_info = {} else: consecutive_resyncs = 0 events = polling_manager.get_events() port_info, ancillary_port_info, ports_not_ready_yet = ( self.process_ports_events(events, ports, ancillary_ports, ports_not_ready_yet, failed_devices, failed_ancillary_devices, updated_ports_copy)) registry.notify( constants.OVSDB_RESOURCE, callback_events.AFTER_READ, self, ovsdb_events=events) return (port_info, ancillary_port_info, consecutive_resyncs, ports_not_ready_yet) def _remove_devices_not_to_retry(self, failed_devices, failed_ancillary_devices, devices_not_to_retry, ancillary_devices_not_to_retry): """This method removes the devices that exceeded the number of retries from failed_devices and failed_ancillary_devices """ for event in ['added', 'removed']: failed_devices[event] = ( failed_devices[event] - devices_not_to_retry[event]) failed_ancillary_devices[event] = ( failed_ancillary_devices[event] - ancillary_devices_not_to_retry[event]) def _get_devices_not_to_retry(self, failed_devices, failed_ancillary_devices, failed_devices_retries_map): """Return the devices not to retry and update the retries map""" new_failed_devices_retries_map = {} devices_not_to_retry = {} ancillary_devices_not_to_retry = {} def _increase_retries(devices_set): devices_not_to_retry = set() for dev in devices_set: retries = failed_devices_retries_map.get(dev, 0) if retries >= constants.MAX_DEVICE_RETRIES: devices_not_to_retry.add(dev) LOG.warning( "Device %(dev)s failed for %(times)s times and won't " "be retried anymore", { 'dev': dev, 'times': constants.MAX_DEVICE_RETRIES}) else: new_failed_devices_retries_map[dev] = retries + 1 return devices_not_to_retry for event in ['added', 'removed']: devices_not_to_retry[event] = _increase_retries( failed_devices[event]) ancillary_devices_not_to_retry[event] = _increase_retries( failed_ancillary_devices[event]) return (new_failed_devices_retries_map, devices_not_to_retry, ancillary_devices_not_to_retry) def update_retries_map_and_remove_devs_not_to_retry( self, failed_devices, failed_ancillary_devices, failed_devices_retries_map): (new_failed_devices_retries_map, devices_not_to_retry, ancillary_devices_not_to_retry) = self._get_devices_not_to_retry( failed_devices, failed_ancillary_devices, failed_devices_retries_map) self._remove_devices_not_to_retry( failed_devices, failed_ancillary_devices, devices_not_to_retry, ancillary_devices_not_to_retry) return new_failed_devices_retries_map def rpc_loop(self, polling_manager=None, bridges_monitor=None): if not polling_manager: polling_manager = polling.get_polling_manager( minimize_polling=False) sync = False ports = set() updated_ports_copy = set() ancillary_ports = set() tunnel_sync = True ovs_restarted = False consecutive_resyncs = 0 need_clean_stale_flow = True ports_not_ready_yet = set() failed_devices = {'added': set(), 'removed': set()} failed_ancillary_devices = {'added': set(), 'removed': set()} failed_devices_retries_map = {} while self._check_and_handle_signal(): if self.fullsync: LOG.info("rpc_loop doing a full sync.") sync = True self.fullsync = False bridges_recreated = False port_info = {} ancillary_port_info = {} start = time.time() LOG.debug("Agent rpc_loop - iteration:%d started", self.iter_num) ovs_status = self.check_ovs_status() if ovs_status == constants.OVS_RESTARTED: self.setup_integration_br() self.setup_physical_bridges(self.bridge_mappings) if self.enable_tunneling: self._reset_tunnel_ofports() self.setup_tunnel_br() self.setup_tunnel_br_flows() self.agent_state['start_flag'] = True tunnel_sync = True # Force state report to avoid race condition # with l2pop fdb entries update self._report_state() if self.enable_distributed_routing: self.dvr_agent.reset_ovs_parameters(self.int_br, self.tun_br, self.patch_int_ofport, self.patch_tun_ofport) self.dvr_agent.reset_dvr_parameters() self.dvr_agent.setup_dvr_flows() # notify that OVS has restarted registry.notify( callback_resources.AGENT, callback_events.OVS_RESTARTED, self) # restart the polling manager so that it will signal as added # all the current ports # REVISIT (rossella_s) Define a method "reset" in # BasePollingManager that will be implemented by AlwaysPoll as # no action and by InterfacePollingMinimizer as start/stop if isinstance( polling_manager, polling.InterfacePollingMinimizer): polling_manager.stop() polling_manager.start() elif ovs_status == constants.OVS_DEAD: # Agent doesn't apply any operations when ovs is dead, to # prevent unexpected failure or crash. Sleep and continue # loop in which ovs status will be checked periodically. port_stats = self.get_port_stats({}, {}) self.loop_count_and_wait(start, port_stats) continue # Check if any physical bridge wasn't recreated recently if bridges_monitor: added_bridges = ( bridges_monitor.bridges_added + self.added_bridges) bridges_recreated = self._reconfigure_physical_bridges( added_bridges) sync |= bridges_recreated # Notify the plugin of tunnel IP if self.enable_tunneling and tunnel_sync: try: tunnel_sync = self.tunnel_sync() except Exception: LOG.exception("Error while configuring tunnel endpoints") tunnel_sync = True ovs_restarted |= (ovs_status == constants.OVS_RESTARTED) devices_need_retry = (any(failed_devices.values()) or any(failed_ancillary_devices.values()) or ports_not_ready_yet) if (self._agent_has_updates(polling_manager) or sync or devices_need_retry): try: LOG.debug("Agent rpc_loop - iteration:%(iter_num)d - " "starting polling. Elapsed:%(elapsed).3f", {'iter_num': self.iter_num, 'elapsed': time.time() - start}) # Save updated ports dict to perform rollback in # case resync would be needed, and then clear # self.updated_ports. As the greenthread should not yield # between these two statements, this will be thread-safe updated_ports_copy = self.updated_ports self.updated_ports = set() (port_info, ancillary_port_info, consecutive_resyncs, ports_not_ready_yet) = (self.process_port_info( start, polling_manager, sync, ovs_restarted, ports, ancillary_ports, updated_ports_copy, consecutive_resyncs, ports_not_ready_yet, failed_devices, failed_ancillary_devices)) sync = False self.process_deleted_ports(port_info) ofport_changed_ports = self.update_stale_ofport_rules() if ofport_changed_ports: port_info.setdefault('updated', set()).update( ofport_changed_ports) LOG.debug("Agent rpc_loop - iteration:%(iter_num)d - " "port information retrieved. " "Elapsed:%(elapsed).3f", {'iter_num': self.iter_num, 'elapsed': time.time() - start}) # Secure and wire/unwire VIFs and update their status # on Neutron server if (self._port_info_has_changes(port_info) or self.sg_agent.firewall_refresh_needed() or ovs_restarted): LOG.debug("Starting to process devices in:%s", port_info) provisioning_needed = ( ovs_restarted or bridges_recreated) failed_devices = self.process_network_ports( port_info, provisioning_needed) if need_clean_stale_flow: self.cleanup_stale_flows() need_clean_stale_flow = False LOG.debug("Agent rpc_loop - iteration:%(iter_num)d - " "ports processed. Elapsed:%(elapsed).3f", {'iter_num': self.iter_num, 'elapsed': time.time() - start}) ports = port_info['current'] if self.ancillary_brs: failed_ancillary_devices = ( self.process_ancillary_network_ports( ancillary_port_info)) LOG.debug("Agent rpc_loop - iteration: " "%(iter_num)d - ancillary ports " "processed. Elapsed:%(elapsed).3f", {'iter_num': self.iter_num, 'elapsed': time.time() - start}) ancillary_ports = ancillary_port_info['current'] polling_manager.polling_completed() failed_devices_retries_map = ( self.update_retries_map_and_remove_devs_not_to_retry( failed_devices, failed_ancillary_devices, failed_devices_retries_map)) # Keep this flag in the last line of "try" block, # so we can sure that no other Exception occurred. ovs_restarted = False self._dispose_local_vlan_hints() except Exception: LOG.exception("Error while processing VIF ports") # Put the ports back in self.updated_port self.updated_ports |= updated_ports_copy sync = True port_stats = self.get_port_stats(port_info, ancillary_port_info) self.loop_count_and_wait(start, port_stats) def daemon_loop(self): # Start everything. LOG.info("Agent initialized successfully, now running... ") signal.signal(signal.SIGTERM, self._handle_sigterm) if hasattr(signal, 'SIGHUP'): signal.signal(signal.SIGHUP, self._handle_sighup) br_names = [br.br_name for br in self.phys_brs.values()] with polling.get_polling_manager( self.minimize_polling, self.ovsdb_monitor_respawn_interval) as pm,\ ovsdb_monitor.get_bridges_monitor( br_names, self.ovsdb_monitor_respawn_interval) as bm: self.rpc_loop(polling_manager=pm, bridges_monitor=bm) def _handle_sigterm(self, signum, frame): self.catch_sigterm = True if self.quitting_rpc_timeout: LOG.info( 'SIGTERM received, capping RPC timeout by %d seconds.', self.quitting_rpc_timeout) self.set_rpc_timeout(self.quitting_rpc_timeout) def _handle_sighup(self, signum, frame): self.catch_sighup = True def _check_and_handle_signal(self): if self.catch_sigterm: LOG.info("Agent caught SIGTERM, quitting daemon loop.") self.run_daemon_loop = False self.catch_sigterm = False if self.catch_sighup: LOG.info("Agent caught SIGHUP, resetting.") self.conf.reload_config_files() config.setup_logging() LOG.debug('Full set of CONF:') self.conf.log_opt_values(LOG, logging.DEBUG) self.catch_sighup = False return self.run_daemon_loop def set_rpc_timeout(self, timeout): for rpc_api in (self.plugin_rpc, self.sg_plugin_rpc, self.dvr_plugin_rpc, self.state_rpc): rpc_api.client.set_max_timeout(timeout) def _check_agent_configurations(self): if (self.enable_distributed_routing and self.enable_tunneling and not self.l2_pop): raise ValueError(_("DVR deployments for VXLAN/GRE/Geneve " "underlays require L2-pop to be enabled, " "in both the Agent and Server side.")) def validate_local_ip(local_ip): """Verify if the ip exists on the agent's host.""" if not ip_lib.IPWrapper().get_device_by_ip(local_ip): LOG.error("Tunneling can't be enabled with invalid local_ip '%s'." " IP couldn't be found on this host's interfaces.", local_ip) raise SystemExit(1) def validate_tunnel_config(tunnel_types, local_ip): """Verify local ip and tunnel config if tunneling is enabled.""" if not tunnel_types: return validate_local_ip(local_ip) for tun in tunnel_types: if tun not in constants.TUNNEL_NETWORK_TYPES: LOG.error('Invalid tunnel type specified: %s', tun) raise SystemExit(1) def prepare_xen_compute(): is_xen_compute_host = 'rootwrap-xen-dom0' in cfg.CONF.AGENT.root_helper \ or xenapi_root_helper.ROOT_HELPER_DAEMON_TOKEN == \ cfg.CONF.AGENT.root_helper_daemon if is_xen_compute_host: xenapi_conf.register_xenapi_opts() # Force ip_lib to always use the root helper to ensure that ip # commands target xen dom0 rather than domU. cfg.CONF.register_opts(ip_lib.OPTS) cfg.CONF.set_default('ip_lib_force_root', True) def main(bridge_classes): prepare_xen_compute() ovs_capabilities.register() ext_manager.register_opts(cfg.CONF) ext_mgr = ext_manager.L2AgentExtensionsManager(cfg.CONF) # now that all extensions registered their options, we can log them n_utils.log_opt_values(LOG) validate_tunnel_config(cfg.CONF.AGENT.tunnel_types, cfg.CONF.OVS.local_ip) try: agent = OVSNeutronAgent(bridge_classes, ext_mgr, cfg.CONF) capabilities.notify_init_event(n_const.AGENT_TYPE_OVS, agent) except (RuntimeError, ValueError) as e: LOG.error("%s Agent terminated!", e) sys.exit(1) agent.daemon_loop() neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/main.py0000664000175000017500000000311513553660047025611 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014 Fumihiko Kakuma # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from oslo_config import cfg from oslo_utils import importutils from neutron.common import config as common_config from neutron.common import profiler cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.' 'common.config') _main_modules = { 'ovs-ofctl': 'neutron.plugins.ml2.drivers.openvswitch.agent.openflow.' 'ovs_ofctl.main', 'native': 'neutron.plugins.ml2.drivers.openvswitch.agent.openflow.' 'native.main', } def main(): common_config.init(sys.argv[1:]) driver_name = cfg.CONF.OVS.of_interface mod_name = _main_modules[driver_name] mod = importutils.import_module(mod_name) mod.init_config() common_config.setup_logging() profiler.setup("neutron-ovs-agent", cfg.CONF.host) mod.main() neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/__init__.py0000664000175000017500000000000013553660046026411 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py0000664000175000017500000007224113553660047031305 0ustar zuulzuul00000000000000# Copyright 2014, Hewlett-Packard Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys import netaddr from neutron_lib import constants as n_const from oslo_log import log as logging import oslo_messaging from oslo_utils import excutils from osprofiler import profiler from neutron.common import utils as n_utils from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants LOG = logging.getLogger(__name__) # A class to represent a DVR-hosted subnet including vif_ports resident on # that subnet class LocalDVRSubnetMapping(object): def __init__(self, subnet, csnat_ofport=constants.OFPORT_INVALID): # set of compute ports on this dvr subnet self.compute_ports = {} self.subnet = subnet self.csnat_ofport = csnat_ofport self.dvr_owned = False def __str__(self): return ("subnet = %s compute_ports = %s csnat_port = %s" " is_dvr_owned = %s" % (self.subnet, self.get_compute_ofports(), self.get_csnat_ofport(), self.is_dvr_owned())) def get_subnet_info(self): return self.subnet def set_dvr_owned(self, owned): self.dvr_owned = owned def is_dvr_owned(self): return self.dvr_owned def add_compute_ofport(self, vif_id, ofport): self.compute_ports[vif_id] = ofport def remove_compute_ofport(self, vif_id): self.compute_ports.pop(vif_id, 0) def remove_all_compute_ofports(self): self.compute_ports.clear() def get_compute_ofports(self): return self.compute_ports def set_csnat_ofport(self, ofport): self.csnat_ofport = ofport def get_csnat_ofport(self): return self.csnat_ofport class OVSPort(object): def __init__(self, id, ofport, mac, device_owner): self.id = id self.mac = mac self.ofport = ofport self.subnets = set() self.device_owner = device_owner def __str__(self): return ("OVSPort: id = %s, ofport = %s, mac = %s, " "device_owner = %s, subnets = %s" % (self.id, self.ofport, self.mac, self.device_owner, self.subnets)) def add_subnet(self, subnet_id): self.subnets.add(subnet_id) def remove_subnet(self, subnet_id): self.subnets.remove(subnet_id) def remove_all_subnets(self): self.subnets.clear() def get_subnets(self): return self.subnets def get_device_owner(self): return self.device_owner def get_mac(self): return self.mac def get_ofport(self): return self.ofport @profiler.trace_cls("ovs_dvr_agent") class OVSDVRNeutronAgent(object): ''' Implements OVS-based DVR(Distributed Virtual Router), for overlay networks. ''' # history # 1.0 Initial version def __init__(self, context, plugin_rpc, integ_br, tun_br, bridge_mappings, phys_brs, int_ofports, phys_ofports, patch_int_ofport=constants.OFPORT_INVALID, patch_tun_ofport=constants.OFPORT_INVALID, host=None, enable_tunneling=False, enable_distributed_routing=False): self.context = context self.plugin_rpc = plugin_rpc self.host = host self.enable_tunneling = enable_tunneling self.enable_distributed_routing = enable_distributed_routing self.bridge_mappings = bridge_mappings self.phys_brs = phys_brs self.int_ofports = int_ofports self.phys_ofports = phys_ofports self.reset_ovs_parameters(integ_br, tun_br, patch_int_ofport, patch_tun_ofport) self.reset_dvr_parameters() self.dvr_mac_address = None if self.enable_distributed_routing: self.get_dvr_mac_address() def setup_dvr_flows(self): self.setup_dvr_flows_on_integ_br() self.setup_dvr_flows_on_tun_br() self.setup_dvr_flows_on_phys_br() self.setup_dvr_mac_flows_on_all_brs() def reset_ovs_parameters(self, integ_br, tun_br, patch_int_ofport, patch_tun_ofport): '''Reset the openvswitch parameters''' self.int_br = integ_br self.tun_br = tun_br self.patch_int_ofport = patch_int_ofport self.patch_tun_ofport = patch_tun_ofport def reset_dvr_parameters(self): '''Reset the DVR parameters''' self.local_dvr_map = {} self.local_csnat_map = {} self.local_ports = {} self.registered_dvr_macs = set() def get_dvr_mac_address(self): try: self.get_dvr_mac_address_with_retry() except oslo_messaging.RemoteError as e: LOG.error('L2 agent could not get DVR MAC address at ' 'startup due to RPC error. It happens when the ' 'server does not support this RPC API. Detailed ' 'message: %s', e) except oslo_messaging.MessagingTimeout: LOG.error('DVR: Failed to obtain a valid local ' 'DVR MAC address') if not self.in_distributed_mode(): sys.exit(1) def get_dvr_mac_address_with_retry(self): # Get the local DVR MAC Address from the Neutron Server. # This is the first place where we contact the server on startup # so retry in case it's not ready to respond for retry_count in reversed(range(5)): try: details = self.plugin_rpc.get_dvr_mac_address_by_host( self.context, self.host) except oslo_messaging.MessagingTimeout as e: with excutils.save_and_reraise_exception() as ctx: if retry_count > 0: ctx.reraise = False LOG.warning('L2 agent could not get DVR MAC ' 'address from server. Retrying. ' 'Detailed message: %s', e) else: LOG.debug("L2 Agent DVR: Received response for " "get_dvr_mac_address_by_host() from " "plugin: %r", details) self.dvr_mac_address = ( netaddr.EUI(details['mac_address'], dialect=netaddr.mac_unix_expanded)) return def setup_dvr_flows_on_integ_br(self): '''Setup up initial dvr flows into br-int''' LOG.info("L2 Agent operating in DVR Mode with MAC %s", self.dvr_mac_address) # Add a canary flow to int_br to track OVS restarts self.int_br.setup_canary_table() # Insert 'drop' action as the default for Table DVR_TO_SRC_MAC self.int_br.install_drop(table_id=constants.DVR_TO_SRC_MAC, priority=1) self.int_br.install_drop(table_id=constants.DVR_TO_SRC_MAC_VLAN, priority=1) for physical_network in self.bridge_mappings: self.int_br.install_drop(table_id=constants.LOCAL_SWITCHING, priority=2, in_port=self.int_ofports[ physical_network]) def setup_dvr_flows_on_tun_br(self): '''Setup up initial dvr flows into br-tun''' if not self.enable_tunneling: return self.tun_br.install_goto(dest_table_id=constants.DVR_PROCESS, priority=1, in_port=self.patch_int_ofport) # table-miss should be sent to learning table self.tun_br.install_goto(table_id=constants.DVR_NOT_LEARN, dest_table_id=constants.LEARN_FROM_TUN) self.tun_br.install_goto(table_id=constants.DVR_PROCESS, dest_table_id=constants.PATCH_LV_TO_TUN) def setup_dvr_flows_on_phys_br(self): '''Setup up initial dvr flows into br-phys''' for physical_network in self.bridge_mappings: self.phys_brs[physical_network].install_goto( in_port=self.phys_ofports[physical_network], priority=2, dest_table_id=constants.DVR_PROCESS_VLAN) self.phys_brs[physical_network].install_goto( priority=1, dest_table_id=constants.DVR_NOT_LEARN_VLAN) self.phys_brs[physical_network].install_goto( table_id=constants.DVR_PROCESS_VLAN, priority=0, dest_table_id=constants.LOCAL_VLAN_TRANSLATION) self.phys_brs[physical_network].install_drop( table_id=constants.LOCAL_VLAN_TRANSLATION, in_port=self.phys_ofports[physical_network], priority=2) self.phys_brs[physical_network].install_normal( table_id=constants.DVR_NOT_LEARN_VLAN, priority=1) def _add_dvr_mac_for_phys_br(self, physical_network, mac): self.int_br.add_dvr_mac_vlan(mac=mac, port=self.int_ofports[physical_network]) phys_br = self.phys_brs[physical_network] phys_br.add_dvr_mac_vlan(mac=mac, port=self.phys_ofports[physical_network]) def _remove_dvr_mac_for_phys_br(self, physical_network, mac): # REVISIT(yamamoto): match in_port as well? self.int_br.remove_dvr_mac_vlan(mac=mac) phys_br = self.phys_brs[physical_network] # REVISIT(yamamoto): match in_port as well? phys_br.remove_dvr_mac_vlan(mac=mac) def _add_dvr_mac_for_tun_br(self, mac): self.int_br.add_dvr_mac_tun(mac=mac, port=self.patch_tun_ofport) self.tun_br.add_dvr_mac_tun(mac=mac, port=self.patch_int_ofport) def _remove_dvr_mac_for_tun_br(self, mac): self.int_br.remove_dvr_mac_tun(mac=mac, port=self.patch_tun_ofport) # REVISIT(yamamoto): match in_port as well? self.tun_br.remove_dvr_mac_tun(mac=mac) def _add_dvr_mac(self, mac): for physical_network in self.bridge_mappings: self._add_dvr_mac_for_phys_br(physical_network, mac) if self.enable_tunneling: self._add_dvr_mac_for_tun_br(mac) LOG.debug("Added DVR MAC flow for %s", mac) self.registered_dvr_macs.add(mac) def _remove_dvr_mac(self, mac): for physical_network in self.bridge_mappings: self._remove_dvr_mac_for_phys_br(physical_network, mac) if self.enable_tunneling: self._remove_dvr_mac_for_tun_br(mac) LOG.debug("Removed DVR MAC flow for %s", mac) self.registered_dvr_macs.remove(mac) def setup_dvr_mac_flows_on_all_brs(self): dvr_macs = self.plugin_rpc.get_dvr_mac_address_list(self.context) LOG.debug("L2 Agent DVR: Received these MACs: %r", dvr_macs) for mac in dvr_macs: c_mac = netaddr.EUI(mac['mac_address'], dialect=netaddr.mac_unix_expanded) if c_mac == self.dvr_mac_address: continue self._add_dvr_mac(c_mac) def dvr_mac_address_update(self, dvr_macs): if not self.dvr_mac_address: LOG.debug("Self mac unknown, ignoring this " "dvr_mac_address_update() ") return dvr_host_macs = set() for entry in dvr_macs: e_mac = netaddr.EUI(entry['mac_address'], dialect=netaddr.mac_unix_expanded) if e_mac == self.dvr_mac_address: continue dvr_host_macs.add(e_mac) if dvr_host_macs == self.registered_dvr_macs: LOG.debug("DVR Mac address already up to date") return dvr_macs_added = dvr_host_macs - self.registered_dvr_macs dvr_macs_removed = self.registered_dvr_macs - dvr_host_macs for oldmac in dvr_macs_removed: self._remove_dvr_mac(oldmac) for newmac in dvr_macs_added: self._add_dvr_mac(newmac) def in_distributed_mode(self): return self.dvr_mac_address is not None def process_tunneled_network(self, network_type, lvid, segmentation_id): self.tun_br.provision_local_vlan( network_type=network_type, lvid=lvid, segmentation_id=segmentation_id, distributed=self.in_distributed_mode()) def _bind_distributed_router_interface_port(self, port, lvm, fixed_ips, device_owner): # since distributed router port must have only one fixed # IP, directly use fixed_ips[0] fixed_ip = fixed_ips[0] subnet_uuid = fixed_ip['subnet_id'] if subnet_uuid in self.local_dvr_map: ldm = self.local_dvr_map[subnet_uuid] else: # set up LocalDVRSubnetMapping available for this subnet subnet_info = self.plugin_rpc.get_subnet_for_dvr( self.context, subnet_uuid, fixed_ips=fixed_ips) if not subnet_info: LOG.warning("DVR: Unable to retrieve subnet information " "for subnet_id %s. The subnet or the gateway " "may have already been deleted", subnet_uuid) return LOG.debug("get_subnet_for_dvr for subnet %(uuid)s " "returned with %(info)s", {"uuid": subnet_uuid, "info": subnet_info}) ldm = LocalDVRSubnetMapping(subnet_info) self.local_dvr_map[subnet_uuid] = ldm # DVR takes over ldm.set_dvr_owned(True) vlan_to_use = lvm.vlan if lvm.network_type == n_const.TYPE_VLAN: vlan_to_use = lvm.segmentation_id subnet_info = ldm.get_subnet_info() ip_version = subnet_info['ip_version'] local_compute_ports = ( self.plugin_rpc.get_ports_on_host_by_subnet( self.context, self.host, subnet_uuid)) LOG.debug("DVR: List of ports received from " "get_ports_on_host_by_subnet %s", local_compute_ports) vif_by_id = self.int_br.get_vifs_by_ids( [local_port['id'] for local_port in local_compute_ports]) for local_port in local_compute_ports: vif = vif_by_id.get(local_port['id']) if not vif: continue ldm.add_compute_ofport(vif.vif_id, vif.ofport) if vif.vif_id in self.local_ports: # ensure if a compute port is already on # a different dvr routed subnet # if yes, queue this subnet to that port comp_ovsport = self.local_ports[vif.vif_id] comp_ovsport.add_subnet(subnet_uuid) else: # the compute port is discovered first here that its on # a dvr routed subnet queue this subnet to that port comp_ovsport = OVSPort(vif.vif_id, vif.ofport, vif.vif_mac, local_port['device_owner']) comp_ovsport.add_subnet(subnet_uuid) self.local_ports[vif.vif_id] = comp_ovsport # create rule for just this vm port self.int_br.install_dvr_to_src_mac( network_type=lvm.network_type, vlan_tag=vlan_to_use, gateway_mac=subnet_info['gateway_mac'], dst_mac=comp_ovsport.get_mac(), dst_port=comp_ovsport.get_ofport()) if lvm.network_type == n_const.TYPE_VLAN: # TODO(vivek) remove the IPv6 related flows once SNAT is not # used for IPv6 DVR. br = self.phys_brs[lvm.physical_network] if lvm.network_type in constants.TUNNEL_NETWORK_TYPES: br = self.tun_br # TODO(vivek) remove the IPv6 related flows once SNAT is not # used for IPv6 DVR. if ip_version == 4: br.install_dvr_process_ipv4( vlan_tag=lvm.vlan, gateway_ip=fixed_ip['ip_address']) else: br.install_dvr_process_ipv6( vlan_tag=lvm.vlan, gateway_mac=port.vif_mac) br.install_dvr_process( vlan_tag=lvm.vlan, vif_mac=port.vif_mac, dvr_mac_address=self.dvr_mac_address) # the dvr router interface is itself a port, so capture it # queue this subnet to that port. A subnet appears only once as # a router interface on any given router ovsport = OVSPort(port.vif_id, port.ofport, port.vif_mac, device_owner) ovsport.add_subnet(subnet_uuid) self.local_ports[port.vif_id] = ovsport def _bind_port_on_dvr_subnet(self, port, lvm, fixed_ips, device_owner): # Handle new compute port added use-case subnet_uuid = None for ips in fixed_ips: if ips['subnet_id'] not in self.local_dvr_map: continue subnet_uuid = ips['subnet_id'] ldm = self.local_dvr_map[subnet_uuid] if not ldm.is_dvr_owned(): # well this is CSNAT stuff, let dvr come in # and do plumbing for this vm later continue # This confirms that this compute port belongs # to a dvr hosted subnet. # Accommodate this VM Port into the existing rule in # the integration bridge LOG.debug("DVR: Plumbing compute port %s", port.vif_id) subnet_info = ldm.get_subnet_info() ldm.add_compute_ofport(port.vif_id, port.ofport) if port.vif_id in self.local_ports: # ensure if a compute port is already on a different # dvr routed subnet # if yes, queue this subnet to that port ovsport = self.local_ports[port.vif_id] ovsport.add_subnet(subnet_uuid) else: # the compute port is discovered first here that its # on a dvr routed subnet, queue this subnet to that port ovsport = OVSPort(port.vif_id, port.ofport, port.vif_mac, device_owner) ovsport.add_subnet(subnet_uuid) self.local_ports[port.vif_id] = ovsport vlan_to_use = lvm.vlan if lvm.network_type == n_const.TYPE_VLAN: vlan_to_use = lvm.segmentation_id # create a rule for this vm port self.int_br.install_dvr_to_src_mac( network_type=lvm.network_type, vlan_tag=vlan_to_use, gateway_mac=subnet_info['gateway_mac'], dst_mac=ovsport.get_mac(), dst_port=ovsport.get_ofport()) def _bind_centralized_snat_port_on_dvr_subnet(self, port, lvm, fixed_ips, device_owner): # since centralized-SNAT (CSNAT) port must have only one fixed # IP, directly use fixed_ips[0] fixed_ip = fixed_ips[0] if port.vif_id in self.local_ports: # throw an error if CSNAT port is already on a different # dvr routed subnet ovsport = self.local_ports[port.vif_id] subs = list(ovsport.get_subnets()) if subs[0] == fixed_ip['subnet_id']: return LOG.error("Centralized-SNAT port %(port)s on subnet " "%(port_subnet)s already seen on a different " "subnet %(orig_subnet)s", { "port": port.vif_id, "port_subnet": fixed_ip['subnet_id'], "orig_subnet": subs[0], }) return subnet_uuid = fixed_ip['subnet_id'] ldm = None subnet_info = None if subnet_uuid not in self.local_dvr_map: # no csnat ports seen on this subnet - create csnat state # for this subnet subnet_info = self.plugin_rpc.get_subnet_for_dvr( self.context, subnet_uuid, fixed_ips=None) if not subnet_info: LOG.warning("DVR: Unable to retrieve subnet information " "for subnet_id %s. The subnet or the gateway " "may have already been deleted", subnet_uuid) return LOG.debug("get_subnet_for_dvr for subnet %(uuid)s " "returned with %(info)s", {"uuid": subnet_uuid, "info": subnet_info}) ldm = LocalDVRSubnetMapping(subnet_info, port.ofport) self.local_dvr_map[subnet_uuid] = ldm else: ldm = self.local_dvr_map[subnet_uuid] subnet_info = ldm.get_subnet_info() # Store csnat OF Port in the existing DVRSubnetMap ldm.set_csnat_ofport(port.ofport) # create ovsPort footprint for csnat port ovsport = OVSPort(port.vif_id, port.ofport, port.vif_mac, device_owner) ovsport.add_subnet(subnet_uuid) self.local_ports[port.vif_id] = ovsport vlan_to_use = lvm.vlan if lvm.network_type == n_const.TYPE_VLAN: vlan_to_use = lvm.segmentation_id self.int_br.install_dvr_to_src_mac( network_type=lvm.network_type, vlan_tag=vlan_to_use, gateway_mac=subnet_info['gateway_mac'], dst_mac=ovsport.get_mac(), dst_port=ovsport.get_ofport()) def bind_port_to_dvr(self, port, local_vlan_map, fixed_ips, device_owner): if not self.in_distributed_mode(): return if local_vlan_map.network_type not in (constants.TUNNEL_NETWORK_TYPES + [n_const.TYPE_VLAN]): LOG.debug("DVR: Port %s is with network_type %s not supported" " for dvr plumbing", port.vif_id, local_vlan_map.network_type) return if (port.vif_id in self.local_ports and self.local_ports[port.vif_id].ofport != port.ofport): LOG.info("DVR: Port %(vif)s changed port number to " "%(ofport)s, rebinding.", {'vif': port.vif_id, 'ofport': port.ofport}) self.unbind_port_from_dvr(port, local_vlan_map) if device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE: self._bind_distributed_router_interface_port(port, local_vlan_map, fixed_ips, device_owner) if device_owner and n_utils.is_dvr_serviced(device_owner): self._bind_port_on_dvr_subnet(port, local_vlan_map, fixed_ips, device_owner) if device_owner == n_const.DEVICE_OWNER_ROUTER_SNAT: self._bind_centralized_snat_port_on_dvr_subnet(port, local_vlan_map, fixed_ips, device_owner) def _unbind_distributed_router_interface_port(self, port, lvm): ovsport = self.local_ports[port.vif_id] # removal of distributed router interface subnet_ids = ovsport.get_subnets() subnet_set = set(subnet_ids) network_type = lvm.network_type physical_network = lvm.physical_network vlan_to_use = lvm.vlan if network_type == n_const.TYPE_VLAN: vlan_to_use = lvm.segmentation_id # ensure we process for all the subnets laid on this removed port for sub_uuid in subnet_set: if sub_uuid not in self.local_dvr_map: continue ldm = self.local_dvr_map[sub_uuid] subnet_info = ldm.get_subnet_info() ip_version = subnet_info['ip_version'] # DVR is no more owner ldm.set_dvr_owned(False) # remove all vm rules for this dvr subnet # clear of compute_ports altogether compute_ports = ldm.get_compute_ofports() for vif_id in compute_ports: comp_port = self.local_ports[vif_id] self.int_br.delete_dvr_to_src_mac( network_type=network_type, vlan_tag=vlan_to_use, dst_mac=comp_port.get_mac()) ldm.remove_all_compute_ofports() if ldm.get_csnat_ofport() == constants.OFPORT_INVALID: # if there is no csnat port for this subnet, remove # this subnet from local_dvr_map, as no dvr (or) csnat # ports available on this agent anymore self.local_dvr_map.pop(sub_uuid, None) if network_type == n_const.TYPE_VLAN: br = self.phys_brs[physical_network] if network_type in constants.TUNNEL_NETWORK_TYPES: br = self.tun_br if ip_version == 4: if subnet_info['gateway_ip']: br.delete_dvr_process_ipv4( vlan_tag=lvm.vlan, gateway_ip=subnet_info['gateway_ip']) else: br.delete_dvr_process_ipv6( vlan_tag=lvm.vlan, gateway_mac=subnet_info['gateway_mac']) ovsport.remove_subnet(sub_uuid) if lvm.network_type == n_const.TYPE_VLAN: br = self.phys_brs[physical_network] if lvm.network_type in constants.TUNNEL_NETWORK_TYPES: br = self.tun_br br.delete_dvr_process(vlan_tag=lvm.vlan, vif_mac=port.vif_mac) # release port state self.local_ports.pop(port.vif_id, None) def _unbind_port_on_dvr_subnet(self, port, lvm): ovsport = self.local_ports[port.vif_id] # This confirms that this compute port being removed belonged # to a dvr hosted subnet. LOG.debug("DVR: Removing plumbing for compute port %s", port) subnet_ids = ovsport.get_subnets() # ensure we process for all the subnets laid on this port for sub_uuid in subnet_ids: if sub_uuid not in self.local_dvr_map: continue ldm = self.local_dvr_map[sub_uuid] ldm.remove_compute_ofport(port.vif_id) vlan_to_use = lvm.vlan if lvm.network_type == n_const.TYPE_VLAN: vlan_to_use = lvm.segmentation_id # first remove this vm port rule self.int_br.delete_dvr_to_src_mac( network_type=lvm.network_type, vlan_tag=vlan_to_use, dst_mac=ovsport.get_mac()) # release port state self.local_ports.pop(port.vif_id, None) def _unbind_centralized_snat_port_on_dvr_subnet(self, port, lvm): ovsport = self.local_ports[port.vif_id] # This confirms that this compute port being removed belonged # to a dvr hosted subnet. LOG.debug("DVR: Removing plumbing for csnat port %s", port) sub_uuid = list(ovsport.get_subnets())[0] # ensure we process for all the subnets laid on this port if sub_uuid not in self.local_dvr_map: return ldm = self.local_dvr_map[sub_uuid] ldm.set_csnat_ofport(constants.OFPORT_INVALID) vlan_to_use = lvm.vlan if lvm.network_type == n_const.TYPE_VLAN: vlan_to_use = lvm.segmentation_id # then remove csnat port rule self.int_br.delete_dvr_to_src_mac( network_type=lvm.network_type, vlan_tag=vlan_to_use, dst_mac=ovsport.get_mac()) if not ldm.is_dvr_owned(): # if not owned by DVR (only used for csnat), remove this # subnet state altogether self.local_dvr_map.pop(sub_uuid, None) # release port state self.local_ports.pop(port.vif_id, None) def unbind_port_from_dvr(self, vif_port, local_vlan_map): if not self.in_distributed_mode(): return # Handle port removed use-case if vif_port and vif_port.vif_id not in self.local_ports: LOG.debug("DVR: Non distributed port, ignoring %s", vif_port) return ovsport = self.local_ports[vif_port.vif_id] device_owner = ovsport.get_device_owner() if device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE: self._unbind_distributed_router_interface_port(vif_port, local_vlan_map) if device_owner and n_utils.is_dvr_serviced(device_owner): self._unbind_port_on_dvr_subnet(vif_port, local_vlan_map) if device_owner == n_const.DEVICE_OWNER_ROUTER_SNAT: self._unbind_centralized_snat_port_on_dvr_subnet(vif_port, local_vlan_map) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/common/0000775000175000017500000000000013553660156025604 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py0000664000175000017500000001225713553660047030200 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib import constants as p_const # Special vlan_id value in ovs_vlan_allocations table indicating flat network FLAT_VLAN_ID = -1 # Topic for tunnel notifications between the plugin and agent TUNNEL = 'tunnel' # Name prefixes for veth device or patch port pair linking the integration # bridge with the physical bridge for a physical network PEER_INTEGRATION_PREFIX = 'int-' PEER_PHYSICAL_PREFIX = 'phy-' # Nonexistent peer used to create patch ports without associating them, it # allows to define flows before association NONEXISTENT_PEER = 'nonexistent-peer' # The different types of tunnels TUNNEL_NETWORK_TYPES = [p_const.TYPE_GRE, p_const.TYPE_VXLAN, p_const.TYPE_GENEVE] # --- OpenFlow table IDs # --- Integration bridge (int_br) LOCAL_SWITCHING = 0 # Various tables for DVR use of integration bridge flows DVR_TO_SRC_MAC = 1 DVR_TO_SRC_MAC_VLAN = 2 CANARY_TABLE = 23 # Table for ARP poison/spoofing prevention rules ARP_SPOOF_TABLE = 24 # Table for MAC spoof filtering MAC_SPOOF_TABLE = 25 # Table to decide whether further filtering is needed TRANSIENT_TABLE = 60 # Tables used for ovs firewall BASE_EGRESS_TABLE = 71 RULES_EGRESS_TABLE = 72 ACCEPT_OR_INGRESS_TABLE = 73 BASE_INGRESS_TABLE = 81 RULES_INGRESS_TABLE = 82 OVS_FIREWALL_TABLES = ( BASE_EGRESS_TABLE, RULES_EGRESS_TABLE, ACCEPT_OR_INGRESS_TABLE, BASE_INGRESS_TABLE, RULES_INGRESS_TABLE, ) # Tables for parties interacting with ovs firewall ACCEPTED_EGRESS_TRAFFIC_TABLE = 91 ACCEPTED_INGRESS_TRAFFIC_TABLE = 92 DROPPED_TRAFFIC_TABLE = 93 ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE = 94 INT_BR_ALL_TABLES = ( LOCAL_SWITCHING, DVR_TO_SRC_MAC, DVR_TO_SRC_MAC_VLAN, CANARY_TABLE, ARP_SPOOF_TABLE, MAC_SPOOF_TABLE, TRANSIENT_TABLE, BASE_EGRESS_TABLE, RULES_EGRESS_TABLE, ACCEPT_OR_INGRESS_TABLE, BASE_INGRESS_TABLE, RULES_INGRESS_TABLE, ACCEPTED_EGRESS_TRAFFIC_TABLE, ACCEPTED_INGRESS_TRAFFIC_TABLE, DROPPED_TRAFFIC_TABLE) # --- Tunnel bridge (tun_br) # Various tables for tunneling flows DVR_PROCESS = 1 PATCH_LV_TO_TUN = 2 GRE_TUN_TO_LV = 3 VXLAN_TUN_TO_LV = 4 GENEVE_TUN_TO_LV = 6 DVR_NOT_LEARN = 9 LEARN_FROM_TUN = 10 UCAST_TO_TUN = 20 ARP_RESPONDER = 21 FLOOD_TO_TUN = 22 TUN_BR_ALL_TABLES = ( LOCAL_SWITCHING, DVR_PROCESS, PATCH_LV_TO_TUN, GRE_TUN_TO_LV, VXLAN_TUN_TO_LV, GENEVE_TUN_TO_LV, DVR_NOT_LEARN, LEARN_FROM_TUN, UCAST_TO_TUN, ARP_RESPONDER, FLOOD_TO_TUN) # --- Physical Bridges (phys_brs) # Various tables for DVR use of physical bridge flows DVR_PROCESS_VLAN = 1 LOCAL_VLAN_TRANSLATION = 2 DVR_NOT_LEARN_VLAN = 3 PHY_BR_ALL_TABLES = ( LOCAL_SWITCHING, DVR_PROCESS_VLAN, LOCAL_VLAN_TRANSLATION, DVR_NOT_LEARN_VLAN) # --- end of OpenFlow table IDs # type for ARP reply in ARP header ARP_REPLY = '0x2' # Map tunnel types to tables number TUN_TABLE = {p_const.TYPE_GRE: GRE_TUN_TO_LV, p_const.TYPE_VXLAN: VXLAN_TUN_TO_LV, p_const.TYPE_GENEVE: GENEVE_TUN_TO_LV} # The default respawn interval for the ovsdb monitor DEFAULT_OVSDBMON_RESPAWN = 30 # Represent invalid OF Port OFPORT_INVALID = -1 ARP_RESPONDER_ACTIONS = ('move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],' 'mod_dl_src:%(mac)s,' 'load:0x2->NXM_OF_ARP_OP[],' 'move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],' 'move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],' 'load:%(mac)#x->NXM_NX_ARP_SHA[],' 'load:%(ip)#x->NXM_OF_ARP_SPA[],' 'in_port') # Represent ovs status OVS_RESTARTED = 0 OVS_NORMAL = 1 OVS_DEAD = 2 EXTENSION_DRIVER_TYPE = 'ovs' # ovs datapath types OVS_DATAPATH_SYSTEM = 'system' OVS_DATAPATH_NETDEV = 'netdev' OVS_DPDK_VHOST_USER = 'dpdkvhostuser' OVS_DPDK_VHOST_USER_CLIENT = 'dpdkvhostuserclient' OVS_DPDK_PORT_TYPES = [OVS_DPDK_VHOST_USER, OVS_DPDK_VHOST_USER_CLIENT] # default ovs vhost-user socket location VHOST_USER_SOCKET_DIR = '/var/run/openvswitch' MAX_DEVICE_RETRIES = 5 # OpenFlow version constants OPENFLOW10 = "OpenFlow10" OPENFLOW11 = "OpenFlow11" OPENFLOW12 = "OpenFlow12" OPENFLOW13 = "OpenFlow13" OPENFLOW14 = "OpenFlow14" OPENFLOW_MAX_PRIORITY = 65535 # A placeholder for dead vlans. DEAD_VLAN_TAG = p_const.MAX_VLAN_TAG + 1 # callback resource for setting 'bridge_name' in the 'binding:vif_details' OVS_BRIDGE_NAME = 'ovs_bridge_name' # callback resource for notifying to ovsdb handler OVSDB_RESOURCE = 'ovsdb' # Used in ovs port 'external_ids' in order mark it for no cleanup when # ovs_cleanup script is used. SKIP_CLEANUP = 'skip_cleanup' neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/common/config.py0000664000175000017500000000160613553660046027424 0ustar zuulzuul00000000000000# Copyright 2012 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron.conf.agent import common as config from neutron.conf.plugins.ml2.drivers import agent from neutron.conf.plugins.ml2.drivers import ovs_conf agent.register_agent_opts() ovs_conf.register_ovs_agent_opts() config.register_agent_state_opts_helper(cfg.CONF) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/common/__init__.py0000664000175000017500000000000013553660046027701 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_capabilities.py0000664000175000017500000000167213553660046030212 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron.plugins.ml2.drivers.agent import capabilities from neutron.services.trunk.drivers.openvswitch.agent import driver def register(): """Register OVS capabilities.""" # Add capabilities to be loaded during agent initialization capabilities.register(driver.init_handler, constants.AGENT_TYPE_OVS) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_agent_extension_api.py0000664000175000017500000000406513553660047031604 0ustar zuulzuul00000000000000# Copyright 2016 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. class OVSCookieBridge(object): '''Bridge restricting flow operations to its own distinct cookie This class creates a bridge derived from a bridge passed at init (which has to inherit from OVSBridgeCookieMixin), but that has its own cookie, registered to the underlying bridge, and that will use this cookie in all flow operations. ''' def __new__(cls, bridge): cookie_bridge = bridge.clone() cookie_bridge.set_agent_uuid_stamp(bridge.request_cookie()) return cookie_bridge def __init__(self, bridge): pass class OVSAgentExtensionAPI(object): '''Implements the Agent API for Open vSwitch agent. Extensions can gain access to this API by overriding the consume_api method which has been added to the AgentExtension class. ''' def __init__(self, int_br, tun_br): super(OVSAgentExtensionAPI, self).__init__() self.br_int = int_br self.br_tun = tun_br def request_int_br(self): """Allows extensions to request an integration bridge to use for extension specific flows. """ return OVSCookieBridge(self.br_int) def request_tun_br(self): """Allows extensions to request a tunnel bridge to use for extension specific flows. If tunneling is not enabled, this method will return None. """ if not self.br_tun: return None return OVSCookieBridge(self.br_tun) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/0000775000175000017500000000000013553660156030066 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/qos_driver.py0000664000175000017500000001645413553660047032626 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from neutron_lib import constants from neutron_lib.services.qos import constants as qos_consts from oslo_config import cfg from oslo_log import log as logging from neutron.agent.l2.extensions import qos_linux as qos from neutron.services.qos.drivers.openvswitch import driver LOG = logging.getLogger(__name__) class QosOVSAgentDriver(qos.QosLinuxAgentDriver): SUPPORTED_RULES = driver.SUPPORTED_RULES def __init__(self): super(QosOVSAgentDriver, self).__init__() self.br_int_name = cfg.CONF.OVS.integration_bridge self.br_int = None self.agent_api = None self.ports = collections.defaultdict(dict) def consume_api(self, agent_api): self.agent_api = agent_api def initialize(self): self.br_int = self.agent_api.request_int_br() self.cookie = self.br_int.default_cookie def create_bandwidth_limit(self, port, rule): self.update_bandwidth_limit(port, rule) def update_bandwidth_limit(self, port, rule): vif_port = port.get('vif_port') if not vif_port: port_id = port.get('port_id') LOG.debug("update_bandwidth_limit was received for port %s but " "vif_port was not found. It seems that port is already " "deleted", port_id) return self.ports[port['port_id']][(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, rule.direction)] = port if rule.direction == constants.INGRESS_DIRECTION: self._update_ingress_bandwidth_limit(vif_port, rule) else: self._update_egress_bandwidth_limit(vif_port, rule) def delete_bandwidth_limit(self, port): port_id = port.get('port_id') vif_port = port.get('vif_port') port = self.ports[port_id].pop((qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, constants.EGRESS_DIRECTION), None) if not port and not vif_port: LOG.debug("delete_bandwidth_limit was received " "for port %s but port was not found. " "It seems that bandwidth_limit is already deleted", port_id) return vif_port = vif_port or port.get('vif_port') self.br_int.delete_egress_bw_limit_for_port(vif_port.port_name) def delete_bandwidth_limit_ingress(self, port): port_id = port.get('port_id') vif_port = port.get('vif_port') port = self.ports[port_id].pop((qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, constants.INGRESS_DIRECTION), None) if not port and not vif_port: LOG.debug("delete_bandwidth_limit_ingress was received " "for port %s but port was not found. " "It seems that bandwidth_limit is already deleted", port_id) return vif_port = vif_port or port.get('vif_port') self.br_int.delete_ingress_bw_limit_for_port(vif_port.port_name) def create_dscp_marking(self, port, rule): self.update_dscp_marking(port, rule) def update_dscp_marking(self, port, rule): self.ports[port['port_id']][qos_consts.RULE_TYPE_DSCP_MARKING] = port vif_port = port.get('vif_port') if not vif_port: port_id = port.get('port_id') LOG.debug("update_dscp_marking was received for port %s but " "vif_port was not found. It seems that port is already " "deleted", port_id) return port_name = vif_port.port_name port = self.br_int.get_port_ofport(port_name) mark = rule.dscp_mark #mark needs to be bit shifted 2 left to not overwrite the #lower 2 bits of type of service packet header. #source: man ovs-ofctl (/mod_nw_tos) mark = str(mark << 2) # reg2 is a metadata field that does not alter packets. # By loading a value into this field and checking if the value is # altered it allows the packet to be resubmitted and go through # the flow table again to be identified by other flows. flows = self.br_int.dump_flows_for(cookie=self.cookie, table=0, in_port=port, reg2=0) if not flows: actions = ("mod_nw_tos:" + mark + ",load:55->NXM_NX_REG2[0..5]," + "resubmit(,0)") self.br_int.add_flow(in_port=port, table=0, priority=65535, reg2=0, actions=actions) else: for flow in flows: actions = str(flow).partition("actions=")[2] acts = actions.split(',') # mod_nw_tos = modify type of service header # This is the second byte of the IPv4 packet header. # DSCP makes up the upper 6 bits of this header field. actions = "mod_nw_tos:" + mark + "," actions += ','.join([act for act in acts if "mod_nw_tos:" not in act]) self.br_int.mod_flow(reg2=0, in_port=port, table=0, actions=actions) def delete_dscp_marking(self, port): vif_port = port.get('vif_port') dscp_port = self.ports[port['port_id']].pop(qos_consts. RULE_TYPE_DSCP_MARKING, 0) if not dscp_port and not vif_port: LOG.debug("delete_dscp_marking was received for port %s but " "no port information was stored to be deleted", port['port_id']) return vif_port = vif_port or dscp_port.get('vif_port') port_num = vif_port.ofport self.br_int.uninstall_flows(in_port=port_num, table_id=0, reg2=0) def _update_egress_bandwidth_limit(self, vif_port, rule): max_kbps = rule.max_kbps # NOTE(slaweq): According to ovs docs: # http://openvswitch.org/support/dist-docs/ovs-vswitchd.conf.db.5.html # ovs accepts only integer values of burst: max_burst_kbps = int(self._get_egress_burst_value(rule)) self.br_int.create_egress_bw_limit_for_port(vif_port.port_name, max_kbps, max_burst_kbps) def _update_ingress_bandwidth_limit(self, vif_port, rule): port_name = vif_port.port_name max_kbps = rule.max_kbps or 0 max_burst_kbps = rule.max_burst_kbps or 0 self.br_int.update_ingress_bw_limit_for_port( port_name, max_kbps, max_burst_kbps ) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/__init__.py0000664000175000017500000000000013553660046032163 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/__init__.py0000664000175000017500000000000013553660046025313 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/mech_driver/0000775000175000017500000000000013553660156025505 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.py0000664000175000017500000001622713553660047031433 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import constants from oslo_config import cfg from oslo_log import log from neutron.agent import securitygroups_rpc from neutron.plugins.ml2.drivers import mech_agent from neutron.plugins.ml2.drivers.openvswitch.agent.common \ import constants as a_const from neutron.services.logapi.drivers.openvswitch import driver as log_driver from neutron.services.qos.drivers.openvswitch import driver as ovs_qos_driver LOG = log.getLogger(__name__) IPTABLES_FW_DRIVER_FULL = ("neutron.agent.linux.iptables_firewall." "OVSHybridIptablesFirewallDriver") class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """Attach to networks using openvswitch L2 agent. The OpenvswitchMechanismDriver integrates the ml2 plugin with the openvswitch L2 agent. Port binding with this driver requires the openvswitch agent to be running on the port's host, and that agent to have connectivity to at least one segment of the port's network. """ def __init__(self): sg_enabled = securitygroups_rpc.is_firewall_enabled() hybrid_plug_required = (not cfg.CONF.SECURITYGROUP.firewall_driver or cfg.CONF.SECURITYGROUP.firewall_driver in ( IPTABLES_FW_DRIVER_FULL, 'iptables_hybrid')) and sg_enabled vif_details = {portbindings.CAP_PORT_FILTER: sg_enabled, portbindings.OVS_HYBRID_PLUG: hybrid_plug_required} # NOTE(moshele): Bind DIRECT (SR-IOV) port allows # to offload the OVS flows using tc to the SR-IOV NIC. # We are using OVS mechanism driver because the openvswitch (>=2.8.0) # support hardware offload via tc and that allow us to manage the VF by # OpenFlow control plane using representor net-device. super(OpenvswitchMechanismDriver, self).__init__( constants.AGENT_TYPE_OVS, portbindings.VIF_TYPE_OVS, vif_details, supported_vnic_types=[portbindings.VNIC_NORMAL, portbindings.VNIC_DIRECT]) ovs_qos_driver.register() log_driver.register() def get_allowed_network_types(self, agent): return (agent['configurations'].get('tunnel_types', []) + [constants.TYPE_LOCAL, constants.TYPE_FLAT, constants.TYPE_VLAN]) def get_mappings(self, agent): return agent['configurations'].get('bridge_mappings', {}) def check_vlan_transparency(self, context): """Currently Openvswitch driver doesn't support vlan transparency.""" return False def bind_port(self, context): vnic_type = context.current.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL) profile = context.current.get(portbindings.PROFILE) capabilities = [] if profile: capabilities = profile.get('capabilities', []) if (vnic_type == portbindings.VNIC_DIRECT and 'switchdev' not in capabilities): LOG.debug("Refusing to bind due to unsupported vnic_type: %s with " "no switchdev capability", portbindings.VNIC_DIRECT) return super(OpenvswitchMechanismDriver, self).bind_port(context) def get_vif_type(self, context, agent, segment): caps = agent['configurations'].get('ovs_capabilities', {}) if (any(x in caps.get('iface_types', []) for x in [a_const.OVS_DPDK_VHOST_USER, a_const.OVS_DPDK_VHOST_USER_CLIENT]) and agent['configurations'].get('datapath_type') == a_const.OVS_DATAPATH_NETDEV): return portbindings.VIF_TYPE_VHOST_USER return self.vif_type def get_vhost_mode(self, iface_types): # NOTE(sean-k-mooney): this function converts the ovs vhost user # driver mode into the qemu vhost user mode. If OVS is the server, # qemu is the client and vice-versa. if (a_const.OVS_DPDK_VHOST_USER_CLIENT in iface_types): return portbindings.VHOST_USER_MODE_SERVER return portbindings.VHOST_USER_MODE_CLIENT def get_vif_details(self, context, agent, segment): vif_details = self._pre_get_vif_details(agent, context) self._set_bridge_name(context.current, vif_details) return vif_details @staticmethod def _set_bridge_name(port, vif_details): # REVISIT(rawlin): add BridgeName as a nullable column to the Port # model and simply check here if it's set and insert it into the # vif_details. def set_bridge_name_inner(bridge_name): vif_details[portbindings.VIF_DETAILS_BRIDGE_NAME] = bridge_name registry.publish(a_const.OVS_BRIDGE_NAME, events.BEFORE_READ, set_bridge_name_inner, payload=events.EventPayload( None, metadata={'port': port})) def _pre_get_vif_details(self, agent, context): a_config = agent['configurations'] vif_type = self.get_vif_type(context, agent, segment=None) if vif_type != portbindings.VIF_TYPE_VHOST_USER: details = dict(self.vif_details) hybrid = portbindings.OVS_HYBRID_PLUG if hybrid in a_config: # we only override the vif_details for hybrid plugging set # in the constructor if the agent specifically requests it details[hybrid] = a_config[hybrid] else: sock_path = self.agent_vhu_sockpath(agent, context.current['id']) caps = a_config.get('ovs_capabilities', {}) mode = self.get_vhost_mode(caps.get('iface_types', [])) details = {portbindings.CAP_PORT_FILTER: False, portbindings.OVS_HYBRID_PLUG: False, portbindings.VHOST_USER_MODE: mode, portbindings.VHOST_USER_OVS_PLUG: True, portbindings.VHOST_USER_SOCKET: sock_path} details[portbindings.OVS_DATAPATH_TYPE] = a_config.get( 'datapath_type', a_const.OVS_DATAPATH_SYSTEM) return details @staticmethod def agent_vhu_sockpath(agent, port_id): """Return the agent's vhost-user socket path for a given port""" sockdir = agent['configurations'].get('vhostuser_socket_dir', a_const.VHOST_USER_SOCKET_DIR) sock_name = (constants.VHOST_USER_DEVICE_PREFIX + port_id)[:14] return os.path.join(sockdir, sock_name) neutron-12.1.1/neutron/plugins/ml2/drivers/openvswitch/mech_driver/__init__.py0000664000175000017500000000000013553660046027602 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/type_local.py0000664000175000017500000000426513553660047023360 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as p_const from neutron_lib import exceptions as exc from neutron_lib.plugins.ml2 import api from oslo_log import log from neutron._i18n import _ LOG = log.getLogger(__name__) class LocalTypeDriver(api.ML2TypeDriver): """Manage state for local networks with ML2. The LocalTypeDriver implements the 'local' network_type. Local network segments provide connectivity between VMs and other devices running on the same node, provided that a common local network bridging technology is available to those devices. Local network segments do not provide any connectivity between nodes. """ def __init__(self): LOG.info("ML2 LocalTypeDriver initialization complete") def get_type(self): return p_const.TYPE_LOCAL def initialize(self): pass def is_partial_segment(self, segment): return False def validate_provider_segment(self, segment): for key, value in segment.items(): if value and key != api.NETWORK_TYPE: msg = _("%s prohibited for local provider network") % key raise exc.InvalidInput(error_message=msg) def reserve_provider_segment(self, context, segment): # No resources to reserve return segment def allocate_tenant_segment(self, context): # No resources to allocate return {api.NETWORK_TYPE: p_const.TYPE_LOCAL} def release_segment(self, context, segment): # No resources to release pass def get_mtu(self, physical_network=None): pass neutron-12.1.1/neutron/plugins/ml2/drivers/type_vxlan.py0000664000175000017500000000426613553660047023417 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as p_const from neutron_lib import exceptions as n_exc from oslo_config import cfg from oslo_log import log from neutron.conf.plugins.ml2.drivers import driver_type from neutron.objects.plugins.ml2 import vxlanallocation as vxlan_obj from neutron.plugins.ml2.drivers import type_tunnel LOG = log.getLogger(__name__) driver_type.register_ml2_drivers_vxlan_opts() class VxlanTypeDriver(type_tunnel.EndpointTunnelTypeDriver): def __init__(self): super(VxlanTypeDriver, self).__init__( vxlan_obj.VxlanAllocation, vxlan_obj.VxlanEndpoint) def get_type(self): return p_const.TYPE_VXLAN def initialize(self): try: self._initialize(cfg.CONF.ml2_type_vxlan.vni_ranges) except n_exc.NetworkTunnelRangeError: LOG.exception("Failed to parse vni_ranges. " "Service terminated!") raise SystemExit() def get_endpoints(self): """Get every vxlan endpoints from database.""" vxlan_endpoints = self._get_endpoints() return [{'ip_address': vxlan_endpoint.ip_address, 'udp_port': vxlan_endpoint.udp_port, 'host': vxlan_endpoint.host} for vxlan_endpoint in vxlan_endpoints] def add_endpoint(self, ip, host, udp_port=p_const.VXLAN_UDP_PORT): return self._add_endpoint(ip, host, udp_port=udp_port) def get_mtu(self, physical_network=None): mtu = super(VxlanTypeDriver, self).get_mtu() return mtu - p_const.VXLAN_ENCAP_OVERHEAD if mtu else 0 neutron-12.1.1/neutron/plugins/ml2/drivers/helpers.py0000664000175000017500000001557113553660047022671 0ustar zuulzuul00000000000000# Copyright (c) 2014 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import random from neutron_lib import context as neutron_ctx from neutron_lib.plugins.ml2 import api from neutron_lib.utils import helpers from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import log from neutron.common import exceptions as exc from neutron.db import api as db_api from neutron.objects import base as base_obj from neutron.plugins.common import utils as p_utils LOG = log.getLogger(__name__) IDPOOL_SELECT_SIZE = 100 class BaseTypeDriver(api.ML2TypeDriver): """BaseTypeDriver for functions common to Segment and flat.""" def __init__(self): try: self.physnet_mtus = helpers.parse_mappings( cfg.CONF.ml2.physical_network_mtus, unique_values=False ) except Exception as e: LOG.error("Failed to parse physical_network_mtus: %s", e) self.physnet_mtus = [] def get_mtu(self, physical_network=None): return p_utils.get_deployment_physnet_mtu() class SegmentTypeDriver(BaseTypeDriver): """SegmentTypeDriver for segment allocation. Provide methods helping to perform segment allocation fully or partially specified. """ def __init__(self, model): super(SegmentTypeDriver, self).__init__() if issubclass(model, base_obj.NeutronDbObject): self.model = model.db_model else: self.model = model self.primary_keys = set(dict(self.model.__table__.columns)) self.primary_keys.remove("allocated") # TODO(ataraday): get rid of this method when old TypeDriver won't be used def _get_session(self, arg): if isinstance(arg, neutron_ctx.Context): return arg.session, db_api.context_manager.writer.using(arg) return arg, arg.session.begin(subtransactions=True) def allocate_fully_specified_segment(self, context, **raw_segment): """Allocate segment fully specified by raw_segment. If segment exists, then try to allocate it and return db object If segment does not exists, then try to create it and return db object If allocation/creation failed, then return None """ network_type = self.get_type() session, ctx_manager = self._get_session(context) try: with ctx_manager: alloc = ( session.query(self.model).filter_by(**raw_segment). first()) if alloc: if alloc.allocated: # Segment already allocated return else: # Segment not allocated LOG.debug("%(type)s segment %(segment)s allocate " "started ", {"type": network_type, "segment": raw_segment}) count = (session.query(self.model). filter_by(allocated=False, **raw_segment). update({"allocated": True})) if count: LOG.debug("%(type)s segment %(segment)s allocate " "done ", {"type": network_type, "segment": raw_segment}) return alloc # Segment allocated or deleted since select LOG.debug("%(type)s segment %(segment)s allocate " "failed: segment has been allocated or " "deleted", {"type": network_type, "segment": raw_segment}) # Segment to create or already allocated LOG.debug("%(type)s segment %(segment)s create started", {"type": network_type, "segment": raw_segment}) alloc = self.model(allocated=True, **raw_segment) alloc.save(session) LOG.debug("%(type)s segment %(segment)s create done", {"type": network_type, "segment": raw_segment}) except db_exc.DBDuplicateEntry: # Segment already allocated (insert failure) alloc = None LOG.debug("%(type)s segment %(segment)s create failed", {"type": network_type, "segment": raw_segment}) return alloc def allocate_partially_specified_segment(self, context, **filters): """Allocate model segment from pool partially specified by filters. Return allocated db object or None. """ network_type = self.get_type() session, ctx_manager = self._get_session(context) with ctx_manager: select = (session.query(self.model). filter_by(allocated=False, **filters)) # Selected segment can be allocated before update by someone else, allocs = select.limit(IDPOOL_SELECT_SIZE).all() if not allocs: # No resource available return alloc = random.choice(allocs) raw_segment = dict((k, alloc[k]) for k in self.primary_keys) LOG.debug("%(type)s segment allocate from pool " "started with %(segment)s ", {"type": network_type, "segment": raw_segment}) count = (session.query(self.model). filter_by(allocated=False, **raw_segment). update({"allocated": True})) if count: LOG.debug("%(type)s segment allocate from pool " "success with %(segment)s ", {"type": network_type, "segment": raw_segment}) return alloc # Segment allocated since select LOG.debug("Allocate %(type)s segment from pool " "failed with segment %(segment)s", {"type": network_type, "segment": raw_segment}) # saving real exception in case we exceeded amount of attempts raise db_exc.RetryRequest( exc.NoNetworkFoundInMaximumAllowedAttempts()) neutron-12.1.1/neutron/plugins/ml2/drivers/__init__.py0000664000175000017500000000000013553660046022742 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/type_tunnel.py0000664000175000017500000004755513553660047023604 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import itertools import operator import netaddr from neutron_lib import constants as p_const from neutron_lib import context from neutron_lib import exceptions as exc from neutron_lib.plugins.ml2 import api from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import log import six from six import moves from sqlalchemy import or_ from neutron._i18n import _ from neutron.common import topics from neutron.db import api as db_api from neutron.objects import base as base_obj from neutron.plugins.common import utils as plugin_utils from neutron.plugins.ml2.drivers import helpers LOG = log.getLogger(__name__) TUNNEL = 'tunnel' def chunks(iterable, chunk_size): """Chunks data into chunk with size<=chunk_size.""" iterator = iter(iterable) chunk = list(itertools.islice(iterator, 0, chunk_size)) while chunk: yield chunk chunk = list(itertools.islice(iterator, 0, chunk_size)) @six.add_metaclass(abc.ABCMeta) class _TunnelTypeDriverBase(helpers.SegmentTypeDriver): BULK_SIZE = 100 def __init__(self, model): super(_TunnelTypeDriverBase, self).__init__(model) self.segmentation_key = next(iter(self.primary_keys)) @abc.abstractmethod def add_endpoint(self, ip, host): """Register the endpoint in the type_driver database. :param ip: the IP address of the endpoint :param host: the Host name of the endpoint """ @abc.abstractmethod def get_endpoints(self): """Get every endpoint managed by the type_driver :returns: a list of dict [{ip_address:endpoint_ip, host:endpoint_host}, ..] """ @abc.abstractmethod def get_endpoint_by_host(self, host): """Get endpoint for a given host managed by the type_driver :param host: the Host name of the endpoint if host found in type_driver database :returns: db object for that particular host else :returns: None """ @abc.abstractmethod def get_endpoint_by_ip(self, ip): """Get endpoint for a given tunnel ip managed by the type_driver :param ip: the IP address of the endpoint if ip found in type_driver database :returns: db object for that particular ip else :returns: None """ @abc.abstractmethod def delete_endpoint(self, ip): """Delete the endpoint in the type_driver database. :param ip: the IP address of the endpoint """ @abc.abstractmethod def delete_endpoint_by_host_or_ip(self, host, ip): """Delete the endpoint in the type_driver database. This function will delete any endpoint matching the specified ip or host. :param host: the host name of the endpoint :param ip: the IP address of the endpoint """ def _initialize(self, raw_tunnel_ranges): self.tunnel_ranges = [] self._parse_tunnel_ranges(raw_tunnel_ranges, self.tunnel_ranges) self.sync_allocations() def _parse_tunnel_ranges(self, tunnel_ranges, current_range): for entry in tunnel_ranges: entry = entry.strip() try: tun_min, tun_max = entry.split(':') tun_min = tun_min.strip() tun_max = tun_max.strip() tunnel_range = int(tun_min), int(tun_max) except ValueError as ex: raise exc.NetworkTunnelRangeError(tunnel_range=entry, error=ex) plugin_utils.verify_tunnel_range(tunnel_range, self.get_type()) current_range.append(tunnel_range) LOG.info("%(type)s ID ranges: %(range)s", {'type': self.get_type(), 'range': current_range}) @db_api.retry_db_errors def sync_allocations(self): # determine current configured allocatable tunnel ids tunnel_ids = set() for tun_min, tun_max in self.tunnel_ranges: tunnel_ids |= set(moves.range(tun_min, tun_max + 1)) tunnel_id_getter = operator.attrgetter(self.segmentation_key) tunnel_col = getattr(self.model, self.segmentation_key) ctx = context.get_admin_context() with db_api.context_manager.writer.using(ctx): # remove from table unallocated tunnels not currently allocatable # fetch results as list via all() because we'll be iterating # through them twice allocs = ctx.session.query(self.model).all() # collect those vnis that needs to be deleted from db unallocateds = ( tunnel_id_getter(a) for a in allocs if not a.allocated) to_remove = (x for x in unallocateds if x not in tunnel_ids) # Immediately delete tunnels in chunks. This leaves no work for # flush at the end of transaction for chunk in chunks(to_remove, self.BULK_SIZE): (ctx.session.query(self.model).filter(tunnel_col.in_(chunk)). filter_by(allocated=False).delete(synchronize_session=False)) # collect vnis that need to be added existings = {tunnel_id_getter(a) for a in allocs} missings = list(tunnel_ids - existings) for chunk in chunks(missings, self.BULK_SIZE): bulk = [{self.segmentation_key: x, 'allocated': False} for x in chunk] ctx.session.execute(self.model.__table__.insert(), bulk) def is_partial_segment(self, segment): return segment.get(api.SEGMENTATION_ID) is None def validate_provider_segment(self, segment): physical_network = segment.get(api.PHYSICAL_NETWORK) if physical_network: msg = _("provider:physical_network specified for %s " "network") % segment.get(api.NETWORK_TYPE) raise exc.InvalidInput(error_message=msg) for key, value in segment.items(): if value and key not in [api.NETWORK_TYPE, api.SEGMENTATION_ID]: msg = (_("%(key)s prohibited for %(tunnel)s provider network"), {'key': key, 'tunnel': segment.get(api.NETWORK_TYPE)}) raise exc.InvalidInput(error_message=msg) def get_mtu(self, physical_network=None): seg_mtu = super(_TunnelTypeDriverBase, self).get_mtu() mtu = [] if seg_mtu > 0: mtu.append(seg_mtu) if cfg.CONF.ml2.path_mtu > 0: mtu.append(cfg.CONF.ml2.path_mtu) version = cfg.CONF.ml2.overlay_ip_version ip_header_length = p_const.IP_HEADER_LENGTH[version] return min(mtu) - ip_header_length if mtu else 0 @six.add_metaclass(abc.ABCMeta) class TunnelTypeDriver(_TunnelTypeDriverBase): """Define stable abstract interface for ML2 type drivers. tunnel type networks rely on tunnel endpoints. This class defines abstract methods to manage these endpoints. ML2 type driver that passes session to functions: - reserve_provider_segment - allocate_tenant_segment - release_segment - get_allocation """ def reserve_provider_segment(self, session, segment): if self.is_partial_segment(segment): alloc = self.allocate_partially_specified_segment(session) if not alloc: raise exc.NoNetworkAvailable() else: segmentation_id = segment.get(api.SEGMENTATION_ID) alloc = self.allocate_fully_specified_segment( session, **{self.segmentation_key: segmentation_id}) if not alloc: raise exc.TunnelIdInUse(tunnel_id=segmentation_id) return {api.NETWORK_TYPE: self.get_type(), api.PHYSICAL_NETWORK: None, api.SEGMENTATION_ID: getattr(alloc, self.segmentation_key), api.MTU: self.get_mtu()} def allocate_tenant_segment(self, session): alloc = self.allocate_partially_specified_segment(session) if not alloc: return return {api.NETWORK_TYPE: self.get_type(), api.PHYSICAL_NETWORK: None, api.SEGMENTATION_ID: getattr(alloc, self.segmentation_key), api.MTU: self.get_mtu()} def release_segment(self, session, segment): tunnel_id = segment[api.SEGMENTATION_ID] inside = any(lo <= tunnel_id <= hi for lo, hi in self.tunnel_ranges) info = {'type': self.get_type(), 'id': tunnel_id} with session.begin(subtransactions=True): query = (session.query(self.model). filter_by(**{self.segmentation_key: tunnel_id})) if inside: count = query.update({"allocated": False}) if count: LOG.debug("Releasing %(type)s tunnel %(id)s to pool", info) else: count = query.delete() if count: LOG.debug("Releasing %(type)s tunnel %(id)s outside pool", info) if not count: LOG.warning("%(type)s tunnel %(id)s not found", info) def get_allocation(self, session, tunnel_id): return (session.query(self.model). filter_by(**{self.segmentation_key: tunnel_id}). first()) @six.add_metaclass(abc.ABCMeta) class ML2TunnelTypeDriver(_TunnelTypeDriverBase): """Define stable abstract interface for ML2 type drivers. tunnel type networks rely on tunnel endpoints. This class defines abstract methods to manage these endpoints. ML2 type driver that passes context as argument to functions: - reserve_provider_segment - allocate_tenant_segment - release_segment - get_allocation """ def reserve_provider_segment(self, context, segment): if self.is_partial_segment(segment): alloc = self.allocate_partially_specified_segment(context) if not alloc: raise exc.NoNetworkAvailable() else: segmentation_id = segment.get(api.SEGMENTATION_ID) alloc = self.allocate_fully_specified_segment( context, **{self.segmentation_key: segmentation_id}) if not alloc: raise exc.TunnelIdInUse(tunnel_id=segmentation_id) return {api.NETWORK_TYPE: self.get_type(), api.PHYSICAL_NETWORK: None, api.SEGMENTATION_ID: getattr(alloc, self.segmentation_key), api.MTU: self.get_mtu()} def allocate_tenant_segment(self, context): alloc = self.allocate_partially_specified_segment(context) if not alloc: return return {api.NETWORK_TYPE: self.get_type(), api.PHYSICAL_NETWORK: None, api.SEGMENTATION_ID: getattr(alloc, self.segmentation_key), api.MTU: self.get_mtu()} def release_segment(self, context, segment): tunnel_id = segment[api.SEGMENTATION_ID] inside = any(lo <= tunnel_id <= hi for lo, hi in self.tunnel_ranges) info = {'type': self.get_type(), 'id': tunnel_id} with db_api.context_manager.writer.using(context): query = (context.session.query(self.model). filter_by(**{self.segmentation_key: tunnel_id})) if inside: count = query.update({"allocated": False}) if count: LOG.debug("Releasing %(type)s tunnel %(id)s to pool", info) else: count = query.delete() if count: LOG.debug("Releasing %(type)s tunnel %(id)s outside pool", info) if not count: LOG.warning("%(type)s tunnel %(id)s not found", info) @db_api.context_manager.reader def get_allocation(self, context, tunnel_id): return (context.session.query(self.model). filter_by(**{self.segmentation_key: tunnel_id}). first()) class EndpointTunnelTypeDriver(ML2TunnelTypeDriver): def __init__(self, segment_model, endpoint_model): super(EndpointTunnelTypeDriver, self).__init__(segment_model) if issubclass(endpoint_model, base_obj.NeutronDbObject): self.endpoint_model = endpoint_model.db_model else: self.endpoint_model = endpoint_model self.segmentation_key = next(iter(self.primary_keys)) def get_endpoint_by_host(self, host): LOG.debug("get_endpoint_by_host() called for host %s", host) session = db_api.get_reader_session() return (session.query(self.endpoint_model). filter_by(host=host).first()) def get_endpoint_by_ip(self, ip): LOG.debug("get_endpoint_by_ip() called for ip %s", ip) session = db_api.get_reader_session() return (session.query(self.endpoint_model). filter_by(ip_address=ip).first()) def delete_endpoint(self, ip): LOG.debug("delete_endpoint() called for ip %s", ip) session = db_api.get_writer_session() session.query(self.endpoint_model).filter_by(ip_address=ip).delete() def delete_endpoint_by_host_or_ip(self, host, ip): LOG.debug("delete_endpoint_by_host_or_ip() called for " "host %(host)s or %(ip)s", {'host': host, 'ip': ip}) session = db_api.get_writer_session() session.query(self.endpoint_model).filter( or_(self.endpoint_model.host == host, self.endpoint_model.ip_address == ip)).delete() def _get_endpoints(self): LOG.debug("_get_endpoints() called") session = db_api.get_reader_session() return session.query(self.endpoint_model) def _add_endpoint(self, ip, host, **kwargs): LOG.debug("_add_endpoint() called for ip %s", ip) session = db_api.get_writer_session() try: endpoint = self.endpoint_model(ip_address=ip, host=host, **kwargs) endpoint.save(session) except db_exc.DBDuplicateEntry: endpoint = (session.query(self.endpoint_model). filter_by(ip_address=ip).one()) LOG.warning("Endpoint with ip %s already exists", ip) return endpoint class TunnelRpcCallbackMixin(object): def setup_tunnel_callback_mixin(self, notifier, type_manager): self._notifier = notifier self._type_manager = type_manager def tunnel_sync(self, rpc_context, **kwargs): """Update new tunnel. Updates the database with the tunnel IP. All listening agents will also be notified about the new tunnel IP. """ tunnel_ip = kwargs.get('tunnel_ip') if not tunnel_ip: msg = _("Tunnel IP value needed by the ML2 plugin") raise exc.InvalidInput(error_message=msg) host = kwargs.get('host') version = netaddr.IPAddress(tunnel_ip).version if version != cfg.CONF.ml2.overlay_ip_version: msg = (_("Tunnel IP version does not match ML2 " "overlay_ip_version, host: %(host)s, tunnel_ip: %(ip)s"), {'host': host, 'ip': tunnel_ip}) raise exc.InvalidInput(error_message=msg) tunnel_type = kwargs.get('tunnel_type') if not tunnel_type: msg = _("Network type value needed by the ML2 plugin") raise exc.InvalidInput(error_message=msg) driver = self._type_manager.drivers.get(tunnel_type) if driver: # The given conditional statements will verify the following # things: # 1. If host is not passed from an agent, it is a legacy mode. # 2. If passed host and tunnel_ip are not found in the DB, # it is a new endpoint. # 3. If host is passed from an agent and it is not found in DB # but the passed tunnel_ip is found, delete the endpoint # from DB and add the endpoint with (tunnel_ip, host), # it is an upgrade case. # 4. If passed host is found in DB and passed tunnel ip is not # found, delete the endpoint belonging to that host and # add endpoint with latest (tunnel_ip, host), it is a case # where local_ip of an agent got changed. # 5. If the passed host had another ip in the DB the host-id has # roamed to a different IP then delete any reference to the new # local_ip or the host id. Don't notify tunnel_delete for the # old IP since that one could have been taken by a different # agent host-id (neutron-ovs-cleanup should be used to clean up # the stale endpoints). # Finally create a new endpoint for the (tunnel_ip, host). if host: host_endpoint = driver.obj.get_endpoint_by_host(host) ip_endpoint = driver.obj.get_endpoint_by_ip(tunnel_ip) if (ip_endpoint and ip_endpoint.host is None and host_endpoint is None): driver.obj.delete_endpoint(ip_endpoint.ip_address) elif (ip_endpoint and ip_endpoint.host != host): LOG.info( "Tunnel IP %(ip)s was used by host %(host)s and " "will be assigned to %(new_host)s", {'ip': ip_endpoint.ip_address, 'host': ip_endpoint.host, 'new_host': host}) driver.obj.delete_endpoint_by_host_or_ip( host, ip_endpoint.ip_address) elif (host_endpoint and host_endpoint.ip_address != tunnel_ip): # Notify all other listening agents to delete stale tunnels self._notifier.tunnel_delete(rpc_context, host_endpoint.ip_address, tunnel_type) driver.obj.delete_endpoint(host_endpoint.ip_address) tunnel = driver.obj.add_endpoint(tunnel_ip, host) tunnels = driver.obj.get_endpoints() entry = {'tunnels': tunnels} # Notify all other listening agents self._notifier.tunnel_update(rpc_context, tunnel.ip_address, tunnel_type) # Return the list of tunnels IP's to the agent return entry else: msg = _("Network type value '%s' not supported") % tunnel_type raise exc.InvalidInput(error_message=msg) class TunnelAgentRpcApiMixin(object): def _get_tunnel_update_topic(self): return topics.get_topic_name(self.topic, TUNNEL, topics.UPDATE) def tunnel_update(self, context, tunnel_ip, tunnel_type): cctxt = self.client.prepare(topic=self._get_tunnel_update_topic(), fanout=True) cctxt.cast(context, 'tunnel_update', tunnel_ip=tunnel_ip, tunnel_type=tunnel_type) def _get_tunnel_delete_topic(self): return topics.get_topic_name(self.topic, TUNNEL, topics.DELETE) def tunnel_delete(self, context, tunnel_ip, tunnel_type): cctxt = self.client.prepare(topic=self._get_tunnel_delete_topic(), fanout=True) cctxt.cast(context, 'tunnel_delete', tunnel_ip=tunnel_ip, tunnel_type=tunnel_type) neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/0000775000175000017500000000000013553660156023003 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/0000775000175000017500000000000013553660156024101 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py0000664000175000017500000005004713553660047027631 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import collections import itertools import socket import sys import time from neutron_lib.api.definitions import portbindings from neutron_lib import constants as n_constants from neutron_lib import context from neutron_lib.utils import helpers from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_service import loopingcall from osprofiler import profiler import six from neutron._i18n import _ from neutron.agent.l2 import l2_agent_extensions_manager as ext_manager from neutron.agent import rpc as agent_rpc from neutron.agent import securitygroups_rpc as agent_sg_rpc from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import securitygroups_rpc as sg_rpc from neutron.common import config as common_config from neutron.common import profiler as setup_profiler from neutron.common import topics from neutron.plugins.ml2.drivers.mech_sriov.agent.common import config from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ import exceptions as exc from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm LOG = logging.getLogger(__name__) class SriovNicSwitchRpcCallbacks(sg_rpc.SecurityGroupAgentRpcCallbackMixin): # Set RPC API version to 1.0 by default. # history # 1.1 Support Security Group RPC (works with NoopFirewallDriver) # 1.2 Support DVR (Distributed Virtual Router) RPC (not supported) # 1.3 Added param devices_to_update to security_groups_provider_updated # (works with NoopFirewallDriver) # 1.4 Added support for network_update target = oslo_messaging.Target(version='1.4') def __init__(self, context, agent, sg_agent): super(SriovNicSwitchRpcCallbacks, self).__init__() self.context = context self.agent = agent self.sg_agent = sg_agent def port_update(self, context, **kwargs): LOG.debug("port_update received") port = kwargs.get('port') vnic_type = port.get(portbindings.VNIC_TYPE) if vnic_type and vnic_type == portbindings.VNIC_DIRECT_PHYSICAL: LOG.debug("The SR-IOV agent doesn't handle %s ports.", portbindings.VNIC_DIRECT_PHYSICAL) return # Put the port mac address in the updated_devices set. # Do not store port details, as if they're used for processing # notifications there is no guarantee the notifications are # processed in the same order as the relevant API requests. mac = port['mac_address'] pci_slot = None if port.get(portbindings.PROFILE): pci_slot = port[portbindings.PROFILE].get('pci_slot') if pci_slot: self.agent.updated_devices.add((mac, pci_slot)) LOG.debug("port_update RPC received for port: %(id)s with MAC " "%(mac)s and PCI slot %(pci_slot)s slot", {'id': port['id'], 'mac': mac, 'pci_slot': pci_slot}) else: LOG.debug("No PCI Slot for port %(id)s with MAC %(mac)s; " "skipping", {'id': port['id'], 'mac': mac, 'pci_slot': pci_slot}) def network_update(self, context, **kwargs): network_id = kwargs['network']['id'] LOG.debug("network_update message received for network " "%(network_id)s, with ports: %(ports)s", {'network_id': network_id, 'ports': self.agent.network_ports[network_id]}) for port_data in self.agent.network_ports[network_id]: self.agent.updated_devices.add(port_data['device']) @profiler.trace_cls("rpc") class SriovNicSwitchAgent(object): def __init__(self, physical_devices_mappings, exclude_devices, polling_interval): self.polling_interval = polling_interval self.network_ports = collections.defaultdict(list) self.conf = cfg.CONF self.device_mappings = physical_devices_mappings self.exclude_devices = exclude_devices self.setup_eswitch_mgr(physical_devices_mappings, exclude_devices) # Stores port update notifications for processing in the main loop self.updated_devices = set() self.context = context.get_admin_context_without_session() self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN) self.sg_plugin_rpc = sg_rpc.SecurityGroupServerRpcApi(topics.PLUGIN) self.sg_agent = agent_sg_rpc.SecurityGroupAgentRpc(self.context, self.sg_plugin_rpc) self._setup_rpc() self.ext_manager = self._create_agent_extension_manager( self.connection) configurations = {'device_mappings': physical_devices_mappings, 'extensions': self.ext_manager.names()} #TODO(mangelajo): optimize resource_versions (see ovs agent) self.agent_state = { 'binary': 'neutron-sriov-nic-agent', 'host': self.conf.host, 'topic': n_constants.L2_AGENT_TOPIC, 'configurations': configurations, 'agent_type': n_constants.AGENT_TYPE_NIC_SWITCH, 'resource_versions': resources.LOCAL_RESOURCE_VERSIONS, 'start_flag': True} # The initialization is complete; we can start receiving messages self.connection.consume_in_threads() # Initialize iteration counter self.iter_num = 0 def _setup_rpc(self): self.agent_id = 'nic-switch-agent.%s' % socket.gethostname() LOG.info("RPC agent_id: %s", self.agent_id) self.topic = topics.AGENT self.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS) # RPC network init # Handle updates from service self.endpoints = [SriovNicSwitchRpcCallbacks(self.context, self, self.sg_agent)] # Define the listening consumers for the agent consumers = [[topics.PORT, topics.UPDATE], [topics.NETWORK, topics.UPDATE], [topics.SECURITY_GROUP, topics.UPDATE]] self.connection = agent_rpc.create_consumers(self.endpoints, self.topic, consumers, start_listening=False) report_interval = cfg.CONF.AGENT.report_interval if report_interval: heartbeat = loopingcall.FixedIntervalLoopingCall( self._report_state) heartbeat.start(interval=report_interval) def _report_state(self): try: self.state_rpc.report_state(self.context, self.agent_state) # we only want to update resource versions on startup self.agent_state.pop('resource_versions', None) self.agent_state.pop('start_flag', None) except Exception: LOG.exception("Failed reporting state!") def _create_agent_extension_manager(self, connection): ext_manager.register_opts(self.conf) mgr = ext_manager.L2AgentExtensionsManager(self.conf) mgr.initialize(connection, 'sriov') return mgr def setup_eswitch_mgr(self, device_mappings, exclude_devices=None): exclude_devices = exclude_devices or {} self.eswitch_mgr = esm.ESwitchManager() self.eswitch_mgr.discover_devices(device_mappings, exclude_devices) def scan_devices(self, registered_devices, updated_devices): curr_devices = self.eswitch_mgr.get_assigned_devices_info() self.agent_state.get('configurations')['devices'] = len(curr_devices) device_info = {} device_info['current'] = curr_devices device_info['added'] = curr_devices - registered_devices # we need to clean up after devices are removed device_info['removed'] = registered_devices - curr_devices # we don't want to process updates for devices that don't exist device_info['updated'] = (updated_devices & curr_devices - device_info['removed']) return device_info def _device_info_has_changes(self, device_info): return (device_info.get('added') or device_info.get('updated') or device_info.get('removed')) def process_network_devices(self, device_info): resync_a = False resync_b = False self.sg_agent.prepare_devices_filter(device_info.get('added')) if device_info.get('updated'): self.sg_agent.refresh_firewall() # Updated devices are processed the same as new ones, as their # admin_state_up may have changed. The set union prevents duplicating # work when a device is new and updated in the same polling iteration. devices_added_updated = (set(device_info.get('added')) | set(device_info.get('updated'))) if devices_added_updated: resync_a = self.treat_devices_added_updated(devices_added_updated) if device_info.get('removed'): resync_b = self.treat_devices_removed(device_info['removed']) # If one of the above operations fails => resync with plugin return (resync_a | resync_b) def treat_device(self, device, pci_slot, admin_state_up, spoofcheck=True): if self.eswitch_mgr.device_exists(device, pci_slot): try: self.eswitch_mgr.set_device_spoofcheck(device, pci_slot, spoofcheck) except Exception: LOG.warning("Failed to set spoofcheck for device %s", device) LOG.info("Device %(device)s spoofcheck %(spoofcheck)s", {"device": device, "spoofcheck": spoofcheck}) try: self.eswitch_mgr.set_device_state(device, pci_slot, admin_state_up) except exc.IpCommandOperationNotSupportedError: LOG.warning("Device %s does not support state change", device) except exc.SriovNicError: LOG.warning("Failed to set device %s state", device) return False else: LOG.info("No device with MAC %s defined on agent.", device) return False return True def _update_network_ports(self, network_id, port_id, mac_pci_slot): self._clean_network_ports(mac_pci_slot) self.network_ports[network_id].append({ "port_id": port_id, "device": mac_pci_slot}) def _clean_network_ports(self, mac_pci_slot): for netid, ports_list in self.network_ports.items(): for port_data in ports_list: if mac_pci_slot == port_data['device']: ports_list.remove(port_data) if ports_list == []: self.network_ports.pop(netid) return port_data['port_id'] def treat_devices_added_updated(self, devices_info): try: macs_list = set([device_info[0] for device_info in devices_info]) devices_details_list = self.plugin_rpc.get_devices_details_list( self.context, macs_list, self.agent_id) except Exception as e: LOG.debug("Unable to get port details for devices " "with MAC addresses %(devices)s: %(e)s", {'devices': macs_list, 'e': e}) # resync is needed return True devices_up = set() devices_down = set() resync = False for device_details in devices_details_list: device = device_details['device'] LOG.debug("Port with MAC address %s is added", device) if 'port_id' in device_details: LOG.info("Port %(device)s updated. Details: %(details)s", {'device': device, 'details': device_details}) port_id = device_details['port_id'] profile = device_details['profile'] spoofcheck = device_details.get('port_security_enabled', True) if self.treat_device(device, profile.get('pci_slot'), device_details['admin_state_up'], spoofcheck): if device_details['admin_state_up']: devices_up.add(device) else: devices_down.add(device) else: resync = True self._update_network_ports(device_details['network_id'], port_id, (device, profile.get('pci_slot'))) self.ext_manager.handle_port(self.context, device_details) else: LOG.info("Device with MAC %s not defined on plugin", device) self.plugin_rpc.update_device_list(self.context, devices_up, devices_down, self.agent_id, self.conf.host) return resync def treat_devices_removed(self, devices): resync = False for device in devices: mac, pci_slot = device LOG.info("Removing device with MAC address %(mac)s and " "PCI slot %(pci_slot)s", {'mac': mac, 'pci_slot': pci_slot}) try: port_id = self._clean_network_ports(device) if port_id: port = {'port_id': port_id, 'device': mac, 'profile': {'pci_slot': pci_slot}} self.ext_manager.delete_port(self.context, port) else: LOG.warning("port_id to device with MAC " "%s not found", mac) dev_details = self.plugin_rpc.update_device_down(self.context, mac, self.agent_id, cfg.CONF.host) except Exception as e: LOG.debug("Removing port failed for device with MAC address " "%(mac)s and PCI slot %(pci_slot)s due to %(exc)s", {'mac': mac, 'pci_slot': pci_slot, 'exc': e}) resync = True continue if dev_details['exists']: LOG.info("Port with MAC %(mac)s and PCI slot " "%(pci_slot)s updated.", {'mac': mac, 'pci_slot': pci_slot}) else: LOG.debug("Device with MAC %(mac)s and PCI slot " "%(pci_slot)s not defined on plugin", {'mac': mac, 'pci_slot': pci_slot}) return resync def daemon_loop(self): sync = True devices = set() LOG.info("SRIOV NIC Agent RPC Daemon Started!") while True: start = time.time() LOG.debug("Agent rpc_loop - iteration:%d started", self.iter_num) if sync: LOG.info("Agent out of sync with plugin!") devices.clear() sync = False device_info = {} # Save updated devices dict to perform rollback in case # resync would be needed, and then clear self.updated_devices. # As the greenthread should not yield between these # two statements, this will should be thread-safe. updated_devices_copy = self.updated_devices self.updated_devices = set() try: self.eswitch_mgr.discover_devices(self.device_mappings, self.exclude_devices) device_info = self.scan_devices(devices, updated_devices_copy) if self._device_info_has_changes(device_info): LOG.debug("Agent loop found changes! %s", device_info) # If treat devices fails - indicates must resync with # plugin sync = self.process_network_devices(device_info) devices = device_info['current'] except Exception: LOG.exception("Error in agent loop. Devices info: %s", device_info) sync = True # Restore devices that were removed from this set earlier # without overwriting ones that may have arrived since. self.updated_devices |= updated_devices_copy # sleep till end of polling interval elapsed = (time.time() - start) if (elapsed < self.polling_interval): time.sleep(self.polling_interval - elapsed) else: LOG.debug("Loop iteration exceeded interval " "(%(polling_interval)s vs. %(elapsed)s)!", {'polling_interval': self.polling_interval, 'elapsed': elapsed}) self.iter_num = self.iter_num + 1 class SriovNicAgentConfigParser(object): def __init__(self): self.device_mappings = {} self.exclude_devices = {} def parse(self): """Parses device_mappings and exclude_devices. Parse and validate the consistency in both mappings """ self.device_mappings = helpers.parse_mappings( cfg.CONF.SRIOV_NIC.physical_device_mappings, unique_keys=False) self.exclude_devices = config.parse_exclude_devices( cfg.CONF.SRIOV_NIC.exclude_devices) self._validate() def _validate(self): """Validate configuration. Validate that network_device in excluded_device exists in device mappings """ dev_net_set = set(itertools.chain.from_iterable( six.itervalues(self.device_mappings))) for dev_name in self.exclude_devices.keys(): if dev_name not in dev_net_set: raise ValueError(_("Device name %(dev_name)s is missing from " "physical_device_mappings") % {'dev_name': dev_name}) def main(): common_config.init(sys.argv[1:]) common_config.setup_logging() try: config_parser = SriovNicAgentConfigParser() config_parser.parse() device_mappings = config_parser.device_mappings exclude_devices = config_parser.exclude_devices except ValueError: LOG.exception("Failed on Agent configuration parse. " "Agent terminated!") raise SystemExit(1) LOG.info("Physical Devices mappings: %s", device_mappings) LOG.info("Exclude Devices: %s", exclude_devices) polling_interval = cfg.CONF.AGENT.polling_interval try: agent = SriovNicSwitchAgent(device_mappings, exclude_devices, polling_interval) except exc.SriovNicError: LOG.exception("Agent Initialization Failed") raise SystemExit(1) # Start everything. setup_profiler.setup("neutron-sriov-nic-agent", cfg.CONF.host) LOG.info("Agent initialized successfully, now running... ") agent.daemon_loop() neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/__init__.py0000664000175000017500000000000013553660046026176 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/common/0000775000175000017500000000000013553660156025371 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/common/exceptions.py0000664000175000017500000000233313553660046030123 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib import exceptions from neutron._i18n import _ class SriovNicError(exceptions.NeutronException): pass class InvalidDeviceError(SriovNicError): message = _("Invalid Device %(dev_name)s: %(reason)s") class IpCommandError(SriovNicError): message = _("ip command failed: %(reason)s") class IpCommandOperationNotSupportedError(SriovNicError): message = _("Operation not supported on device %(dev_name)s") class InvalidPciSlotError(SriovNicError): message = _("Invalid pci slot %(pci_slot)s") class IpCommandDeviceError(SriovNicError): message = _("ip command failed on device %(dev_name)s: %(reason)s") neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/common/config.py0000664000175000017500000000427413553660047027216 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_config import cfg from neutron._i18n import _ from neutron.conf.agent import common as config from neutron.conf.plugins.ml2.drivers.mech_sriov import agent_common as \ agent_common_config def parse_exclude_devices(exclude_list): """Parse Exclude devices list parses excluded device list in the form: dev_name:pci_dev_1;pci_dev_2 @param exclude list: list of string pairs in "key:value" format the key part represents the network device name the value part is a list of PCI slots separated by ";" """ exclude_mapping = {} for dev_mapping in exclude_list: try: dev_name, exclude_devices = dev_mapping.split(":", 1) except ValueError: raise ValueError(_("Invalid mapping: '%s'") % dev_mapping) dev_name = dev_name.strip() if not dev_name: raise ValueError(_("Missing key in mapping: '%s'") % dev_mapping) if dev_name in exclude_mapping: raise ValueError(_("Device %(dev_name)s in mapping: %(mapping)s " "not unique") % {'dev_name': dev_name, 'mapping': dev_mapping}) exclude_devices_list = exclude_devices.split(";") exclude_devices_set = set() for dev in exclude_devices_list: dev = dev.strip() if dev: exclude_devices_set.add(dev) exclude_mapping[dev_name] = exclude_devices_set return exclude_mapping agent_common_config.register_agent_sriov_nic_opts() config.register_agent_state_opts_helper(cfg.CONF) neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/common/__init__.py0000664000175000017500000000000013553660046027466 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py0000664000175000017500000001714013553660047026056 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import re from oslo_log import log as logging from neutron.agent.linux import ip_lib from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ import exceptions as exc LOG = logging.getLogger(__name__) class PciDeviceIPWrapper(ip_lib.IPWrapper): """Wrapper class for ip link commands. wrapper for getting/setting pci device details using ip link... """ VF_PATTERN = r"^vf\s+(?P\d+)\s+" MAC_PATTERN = r"MAC\s+(?P[a-fA-F0-9:]+)," STATE_PATTERN = r"\s+link-state\s+(?P\w+)" ANY_PATTERN = ".*," MACVTAP_PATTERN = r".*macvtap[0-9]+@(?P[a-zA-Z0-9_]+):" VF_LINE_FORMAT = VF_PATTERN + MAC_PATTERN + ANY_PATTERN + STATE_PATTERN VF_DETAILS_REG_EX = re.compile(VF_LINE_FORMAT) MACVTAP_REG_EX = re.compile(MACVTAP_PATTERN) IP_LINK_OP_NOT_SUPPORTED = 'RTNETLINK answers: Operation not supported' class LinkState(object): ENABLE = "enable" DISABLE = "disable" def __init__(self, dev_name): super(PciDeviceIPWrapper, self).__init__() self.dev_name = dev_name def _set_feature(self, vf_index, feature, value): """Sets vf feature Checks if the feature is not supported or there's some general error during ip link invocation and raises exception accordingly. :param vf_index: vf index :param feature: name of a feature to be passed to ip link, such as 'state' or 'spoofchk' :param value: value of the feature setting """ try: self._as_root([], "link", ("set", self.dev_name, "vf", str(vf_index), feature, value)) except Exception as e: if self.IP_LINK_OP_NOT_SUPPORTED in str(e): raise exc.IpCommandOperationNotSupportedError( dev_name=self.dev_name) else: raise exc.IpCommandDeviceError(dev_name=self.dev_name, reason=str(e)) def get_assigned_macs(self, vf_list): """Get assigned mac addresses for vf list. @param vf_list: list of vf indexes @return: dict mapping of vf to mac """ try: out = self._as_root([], "link", ("show", self.dev_name)) except Exception as e: LOG.exception("Failed executing ip command") raise exc.IpCommandDeviceError(dev_name=self.dev_name, reason=e) vf_to_mac_mapping = {} vf_lines = self._get_vf_link_show(vf_list, out) if vf_lines: for vf_line in vf_lines: vf_details = self._parse_vf_link_show(vf_line) if vf_details: vf_num = vf_details.get('vf') vf_mac = vf_details.get("MAC") vf_to_mac_mapping[vf_num] = vf_mac return vf_to_mac_mapping def get_vf_state(self, vf_index): """Get vf state {True/False} @param vf_index: vf index @todo: Handle "auto" state """ try: out = self._as_root([], "link", ("show", self.dev_name)) except Exception as e: LOG.exception("Failed executing ip command") raise exc.IpCommandDeviceError(dev_name=self.dev_name, reason=e) vf_lines = self._get_vf_link_show([vf_index], out) if vf_lines: vf_details = self._parse_vf_link_show(vf_lines[0]) if vf_details: state = vf_details.get("link-state", self.LinkState.DISABLE) if state != self.LinkState.DISABLE: return True return False def set_vf_state(self, vf_index, state): """sets vf state. @param vf_index: vf index @param state: required state {True/False} """ status_str = self.LinkState.ENABLE if state else \ self.LinkState.DISABLE self._set_feature(vf_index, "state", status_str) def set_vf_spoofcheck(self, vf_index, enabled): """sets vf spoofcheck @param vf_index: vf index @param enabled: True to enable spoof checking, False to disable """ setting = "on" if enabled else "off" self._set_feature(vf_index, "spoofchk", setting) def set_vf_rate(self, vf_index, rate_type, rate_value): """sets vf rate. @param vf_index: vf index @param rate_type: vf rate type ('rate', 'min_tx_rate') @param rate_value: vf rate in Mbps """ self._set_feature(vf_index, rate_type, str(rate_value)) def _get_vf_link_show(self, vf_list, link_show_out): """Get link show output for VFs get vf link show command output filtered by given vf list @param vf_list: list of vf indexes @param link_show_out: link show command output @return: list of output rows regarding given vf_list """ vf_lines = [] for line in link_show_out.split("\n"): line = line.strip() if line.startswith("vf"): details = line.split() index = int(details[1]) if index in vf_list: vf_lines.append(line) if not vf_lines: LOG.warning("Cannot find vfs %(vfs)s in device %(dev_name)s", {'vfs': vf_list, 'dev_name': self.dev_name}) return vf_lines def _parse_vf_link_show(self, vf_line): """Parses vf link show command output line. @param vf_line: link show vf line """ vf_details = {} pattern_match = self.VF_DETAILS_REG_EX.match(vf_line) if pattern_match: vf_details["vf"] = int(pattern_match.group("vf_index")) vf_details["MAC"] = pattern_match.group("mac") vf_details["link-state"] = pattern_match.group("state") else: LOG.warning("failed to parse vf link show line %(line)s: " "for %(device)s", {'line': vf_line, 'device': self.dev_name}) return vf_details def link_show(self): try: out = self._as_root([], "link", ("show", )) except Exception as e: LOG.error("Failed executing ip command: %s", e) raise exc.IpCommandError(reason=e) return out @classmethod def is_macvtap_assigned(cls, ifname, ip_link_show_output): """Check if vf has macvtap interface assigned Parses the output of ip link show command and checks if macvtap[0-9]+@ regex matches the output. @param ifname: vf interface name @param ip_link_show_output: 'ip link show' result to parse @return: True on match otherwise False """ for line in ip_link_show_output.splitlines(): pattern_match = cls.MACVTAP_REG_EX.match(line) if pattern_match: if ifname == pattern_match.group('vf_interface'): return True return False neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py0000664000175000017500000004747013553660047027626 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import os import re from neutron_lib.utils import helpers from oslo_log import log as logging from neutron._i18n import _ from neutron.agent.linux import ip_link_support from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ import exceptions as exc from neutron.plugins.ml2.drivers.mech_sriov.agent import pci_lib LOG = logging.getLogger(__name__) class PciOsWrapper(object): """OS wrapper for checking virtual functions""" DEVICE_PATH = "/sys/class/net/%s/device" PCI_PATH = "/sys/class/net/%s/device/virtfn%s/net" NUMVFS_PATH = "/sys/class/net/%s/device/sriov_numvfs" VIRTFN_FORMAT = r"^virtfn(?P\d+)" VIRTFN_REG_EX = re.compile(VIRTFN_FORMAT) @classmethod def scan_vf_devices(cls, dev_name): """Scan os directories to get VF devices @param dev_name: pf network device name @return: list of virtual functions """ vf_list = [] dev_path = cls.DEVICE_PATH % dev_name if not os.path.isdir(dev_path): LOG.error("Failed to get devices for %s", dev_name) raise exc.InvalidDeviceError(dev_name=dev_name, reason=_("Device not found")) file_list = os.listdir(dev_path) for file_name in file_list: pattern_match = cls.VIRTFN_REG_EX.match(file_name) if pattern_match: vf_index = int(pattern_match.group("vf_index")) file_path = os.path.join(dev_path, file_name) if os.path.islink(file_path): file_link = os.readlink(file_path) pci_slot = os.path.basename(file_link) vf_list.append((pci_slot, vf_index)) return vf_list @classmethod def pf_device_exists(cls, dev_name): return os.path.isdir(cls.DEVICE_PATH % dev_name) @classmethod def is_assigned_vf(cls, dev_name, vf_index, ip_link_show_output): """Check if VF is assigned. Checks if a given vf index of a given device name is assigned by checking the relevant path in the system: VF is assigned if: Direct VF: PCI_PATH does not exist. Macvtap VF: macvtap@ interface exists in ip link show @param dev_name: pf network device name @param vf_index: vf index @param ip_link_show_output: 'ip link show' output """ if not cls.pf_device_exists(dev_name): # If the root PCI path does not exist, then the VF cannot # actually have been allocated and there is no way we can # manage it. return False path = cls.PCI_PATH % (dev_name, vf_index) try: ifname_list = os.listdir(path) except OSError: # PCI_PATH does not exist means that the DIRECT VF assigned return True # Note(moshele) kernel < 3.13 doesn't create symbolic link # for macvtap interface. Therefore we workaround it # by parsing ip link show and checking if macvtap interface exists for ifname in ifname_list: if pci_lib.PciDeviceIPWrapper.is_macvtap_assigned( ifname, ip_link_show_output): return True return False @classmethod def get_numvfs(cls, dev_name): """Get configured number of VFs on device @param dev_name: pf network device name @return: integer number of VFs or -1 if sriov_numvfs file not found (device doesn't support this config) """ try: with open(cls.NUMVFS_PATH % dev_name) as f: numvfs = int(f.read()) LOG.debug("Number of VFs configured on device %s: %s", dev_name, numvfs) return numvfs except IOError: LOG.warning("Error reading sriov_numvfs file for device %s, " "probably not supported by this device", dev_name) return -1 class EmbSwitch(object): """Class to manage logical embedded switch entity. Embedded Switch object is logical entity representing all VFs connected to same physical network Each physical network is mapped to PF network device interface, meaning all its VF, excluding the devices in exclude_device list. @ivar pci_slot_map: dictionary for mapping each pci slot to vf index @ivar pci_dev_wrapper: pci device wrapper """ def __init__(self, dev_name, exclude_devices): """Constructor @param dev_name: network device name @param exclude_devices: list of pci slots to exclude """ self.dev_name = dev_name self.pci_slot_map = {} self.scanned_pci_list = [] self.pci_dev_wrapper = pci_lib.PciDeviceIPWrapper(dev_name) self._load_devices(exclude_devices) def _load_devices(self, exclude_devices): """Load devices from driver and filter if needed. @param exclude_devices: excluded devices mapping device_name: pci slots """ self.scanned_pci_list = PciOsWrapper.scan_vf_devices(self.dev_name) for pci_slot, vf_index in self.scanned_pci_list: if pci_slot not in exclude_devices: self.pci_slot_map[pci_slot] = vf_index def get_pci_slot_list(self): """Get list of VF addresses.""" return self.pci_slot_map.keys() def get_assigned_devices_info(self): """Get assigned Virtual Functions mac and pci slot information and populates vf_to_pci_slot mappings @return: list of VF pair (mac address, pci slot) """ vf_to_pci_slot_mapping = {} assigned_devices_info = [] ls = self.pci_dev_wrapper.link_show() for pci_slot, vf_index in self.pci_slot_map.items(): if not PciOsWrapper.is_assigned_vf(self.dev_name, vf_index, ls): continue vf_to_pci_slot_mapping[vf_index] = pci_slot if vf_to_pci_slot_mapping: vf_to_mac_mapping = self.pci_dev_wrapper.get_assigned_macs( list(vf_to_pci_slot_mapping.keys())) for vf_index, mac in vf_to_mac_mapping.items(): pci_slot = vf_to_pci_slot_mapping[vf_index] assigned_devices_info.append((mac, pci_slot)) return assigned_devices_info def get_device_state(self, pci_slot): """Get device state. @param pci_slot: Virtual Function address """ vf_index = self._get_vf_index(pci_slot) return self.pci_dev_wrapper.get_vf_state(vf_index) def set_device_state(self, pci_slot, state): """Set device state. @param pci_slot: Virtual Function address @param state: link state """ vf_index = self._get_vf_index(pci_slot) return self.pci_dev_wrapper.set_vf_state(vf_index, state) def set_device_rate(self, pci_slot, rate_type, rate_kbps): """Set device rate: rate (max_tx_rate), min_tx_rate @param pci_slot: Virtual Function address @param rate_type: device rate name type. Could be 'rate' and 'min_tx_rate'. @param rate_kbps: device rate in kbps """ vf_index = self._get_vf_index(pci_slot) #NOTE(ralonsoh): ip link sets rate in Mbps therefore we need to convert #the rate_kbps value from kbps to Mbps. #Zero means to disable the rate so the lowest rate available is 1Mbps. #Floating numbers are not allowed if rate_kbps > 0 and rate_kbps < 1000: rate_mbps = 1 else: rate_mbps = helpers.round_val(rate_kbps / 1000.0) log_dict = { 'rate_mbps': rate_mbps, 'rate_kbps': rate_kbps, 'vf_index': vf_index, 'rate_type': rate_type } if rate_kbps % 1000 != 0: LOG.debug("'%(rate_type)s' for SR-IOV ports is counted in Mbps; " "setting %(rate_mbps)s Mbps limit for port %(vf_index)s " "instead of %(rate_kbps)s kbps", log_dict) else: LOG.debug("Setting %(rate_mbps)s Mbps limit for port %(vf_index)s", log_dict) return self.pci_dev_wrapper.set_vf_rate(vf_index, rate_type, rate_mbps) def _get_vf_index(self, pci_slot): vf_index = self.pci_slot_map.get(pci_slot) if vf_index is None: LOG.warning("Cannot find vf index for pci slot %s", pci_slot) raise exc.InvalidPciSlotError(pci_slot=pci_slot) return vf_index def set_device_spoofcheck(self, pci_slot, enabled): """Set device spoofchecking @param pci_slot: Virtual Function address @param enabled: True to enable spoofcheck, False to disable """ vf_index = self.pci_slot_map.get(pci_slot) if vf_index is None: raise exc.InvalidPciSlotError(pci_slot=pci_slot) return self.pci_dev_wrapper.set_vf_spoofcheck(vf_index, enabled) def get_pci_device(self, pci_slot): """Get mac address for given Virtual Function address @param pci_slot: pci slot @return: MAC address of virtual function """ vf_index = self.pci_slot_map.get(pci_slot) mac = None if vf_index is not None: ls = self.pci_dev_wrapper.link_show() if PciOsWrapper.is_assigned_vf(self.dev_name, vf_index, ls): macs = self.pci_dev_wrapper.get_assigned_macs([vf_index]) mac = macs.get(vf_index) return mac class ESwitchManager(object): """Manages logical Embedded Switch entities for physical network.""" def __new__(cls): # make it a singleton if not hasattr(cls, '_instance'): cls._instance = super(ESwitchManager, cls).__new__(cls) cls.emb_switches_map = {} cls.pci_slot_map = {} cls.skipped_devices = set() return cls._instance def device_exists(self, device_mac, pci_slot): """Verify if device exists. Check if a device mac exists and matches the given VF pci slot @param device_mac: device mac @param pci_slot: VF address """ embedded_switch = self._get_emb_eswitch(device_mac, pci_slot) if embedded_switch: return True return False def get_assigned_devices_info(self, phys_net=None): """Get all assigned devices. Get all assigned devices belongs to given embedded switch @param phys_net: physical network, if none get all assigned devices @return: set of assigned VFs (mac address, pci slot) pair """ if phys_net: eswitch_objects = self.emb_switches_map.get(phys_net, set()) else: eswitch_objects = set() for eswitch_list in self.emb_switches_map.values(): eswitch_objects |= set(eswitch_list) assigned_devices = set() for embedded_switch in eswitch_objects: for device in embedded_switch.get_assigned_devices_info(): assigned_devices.add(device) return assigned_devices def get_device_state(self, device_mac, pci_slot): """Get device state. Get the device state (up/True or down/False) @param device_mac: device mac @param pci_slot: VF PCI slot @return: device state (True/False) None if failed """ embedded_switch = self._get_emb_eswitch(device_mac, pci_slot) if embedded_switch: return embedded_switch.get_device_state(pci_slot) return False def set_device_max_rate(self, device_mac, pci_slot, max_kbps): """Set device max rate Sets the device max rate in kbps @param device_mac: device mac @param pci_slot: pci slot @param max_kbps: device max rate in kbps """ embedded_switch = self._get_emb_eswitch(device_mac, pci_slot) if embedded_switch: embedded_switch.set_device_rate( pci_slot, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, max_kbps) def set_device_min_tx_rate(self, device_mac, pci_slot, min_kbps): """Set device min_tx_rate Sets the device min_tx_rate in kbps @param device_mac: device mac @param pci_slot: pci slot @param max_kbps: device min_tx_rate in kbps """ embedded_switch = self._get_emb_eswitch(device_mac, pci_slot) if embedded_switch: embedded_switch.set_device_rate( pci_slot, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE, min_kbps) def set_device_state(self, device_mac, pci_slot, admin_state_up): """Set device state Sets the device state (up or down) @param device_mac: device mac @param pci_slot: pci slot @param admin_state_up: device admin state True/False """ embedded_switch = self._get_emb_eswitch(device_mac, pci_slot) if embedded_switch: embedded_switch.set_device_state(pci_slot, admin_state_up) def set_device_spoofcheck(self, device_mac, pci_slot, enabled): """Set device spoofcheck Sets device spoofchecking (enabled or disabled) @param device_mac: device mac @param pci_slot: pci slot @param enabled: device spoofchecking """ embedded_switch = self._get_emb_eswitch(device_mac, pci_slot) if embedded_switch: embedded_switch.set_device_spoofcheck(pci_slot, enabled) def _process_emb_switch_map(self, phys_net, dev_name, exclude_devices): """Process emb_switch_map @param phys_net: physical network @param dev_name: device name @param exclude_devices: PCI devices to ignore. """ emb_switches = self.emb_switches_map.get(phys_net, []) for switch in emb_switches: if switch.dev_name == dev_name: if not PciOsWrapper.pf_device_exists(dev_name): # If the device is given to the VM as PCI-PT # then delete the respective emb_switch from map self.emb_switches_map.get(phys_net).remove(switch) return # We don't know about this device at the moment, so add to the map. if PciOsWrapper.pf_device_exists(dev_name): self._create_emb_switch(phys_net, dev_name, exclude_devices.get(dev_name, set())) def discover_devices(self, device_mappings, exclude_devices): """Discover which Virtual functions to manage. Discover devices, and create embedded switch object for network device @param device_mappings: device mapping physical_network:device_name @param exclude_devices: excluded devices mapping device_name: pci slots """ if exclude_devices is None: exclude_devices = {} for phys_net, dev_names in device_mappings.items(): for dev_name in dev_names: self._process_emb_switch_map(phys_net, dev_name, exclude_devices) def _create_emb_switch(self, phys_net, dev_name, exclude_devices): embedded_switch = EmbSwitch(dev_name, exclude_devices) numvfs = PciOsWrapper.get_numvfs(dev_name) if numvfs == 0: # numvfs might be 0 on pre-up state of a device # giving such devices one more chance to initialize if dev_name not in self.skipped_devices: self.skipped_devices.add(dev_name) LOG.info("Device %s has 0 VFs configured. Skipping " "for now to let the device initialize", dev_name) return else: # looks like device indeed has 0 VFs configured # it is probably used just as direct-physical LOG.info("Device %s has 0 VFs configured", dev_name) numvfs_cur = len(embedded_switch.scanned_pci_list) if numvfs >= 0 and numvfs > numvfs_cur: LOG.info("Not all VFs were initialized on device %(device)s: " "expected - %(expected)s, actual - %(actual)s. Skipping.", {'device': dev_name, 'expected': numvfs, 'actual': numvfs_cur}) self.skipped_devices.add(dev_name) return self.emb_switches_map.setdefault(phys_net, []).append(embedded_switch) for pci_slot in embedded_switch.get_pci_slot_list(): self.pci_slot_map[pci_slot] = embedded_switch self.skipped_devices.discard(dev_name) def _get_emb_eswitch(self, device_mac, pci_slot): """Get embedded switch. Get embedded switch by pci slot and validate pci has device mac @param device_mac: device mac @param pci_slot: pci slot """ embedded_switch = self.pci_slot_map.get(pci_slot) if embedded_switch: used_device_mac = embedded_switch.get_pci_device(pci_slot) if used_device_mac != device_mac: LOG.warning("device pci mismatch: %(device_mac)s " "- %(pci_slot)s", {"device_mac": device_mac, "pci_slot": pci_slot}) embedded_switch = None return embedded_switch def clear_max_rate(self, pci_slot): """Clear the VF "rate" parameter Clear the "rate" configuration from VF by setting it to 0. @param pci_slot: VF PCI slot """ self._clear_rate(pci_slot, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE) def clear_min_tx_rate(self, pci_slot): """Clear the VF "min_tx_rate" parameter Clear the "min_tx_rate" configuration from VF by setting it to 0. @param pci_slot: VF PCI slot """ self._clear_rate(pci_slot, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE) def _clear_rate(self, pci_slot, rate_type): """Clear the VF rate parameter specified in rate_type Clear the rate configuration from VF by setting it to 0. @param pci_slot: VF PCI slot @param rate_type: rate to clear ('rate', 'min_tx_rate') """ #NOTE(Moshe Levi): we don't use the self._get_emb_eswitch here, because #when clearing the VF it may be not assigned. This happens when #libvirt releases the VF back to the hypervisor on delete VM. Therefore #we should just clear the VF rate according to pci_slot no matter #if VF is assigned or not. embedded_switch = self.pci_slot_map.get(pci_slot) if embedded_switch: #NOTE(Moshe Levi): check the pci_slot is not assigned to some #other port before resetting the rate. if embedded_switch.get_pci_device(pci_slot) is None: embedded_switch.set_device_rate(pci_slot, rate_type, 0) else: LOG.warning("VF with PCI slot %(pci_slot)s is already " "assigned; skipping reset for '%(rate_type)s' " "device configuration parameter", {'pci_slot': pci_slot, 'rate_type': rate_type}) else: LOG.error("PCI slot %(pci_slot)s has no mapping to Embedded " "Switch; skipping", {'pci_slot': pci_slot}) neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/0000775000175000017500000000000013553660156027653 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/qos_driver.py0000664000175000017500000000666713553660047032420 0ustar zuulzuul00000000000000# Copyright 2015 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from neutron.agent.l2.extensions import qos_linux as qos from neutron.plugins.ml2.drivers.mech_sriov.agent.common import ( exceptions as exc) from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm from neutron.services.qos.drivers.sriov import driver LOG = logging.getLogger(__name__) class QosSRIOVAgentDriver(qos.QosLinuxAgentDriver): SUPPORTED_RULES = driver.SUPPORTED_RULES def __init__(self): super(QosSRIOVAgentDriver, self).__init__() self.eswitch_mgr = None def initialize(self): self.eswitch_mgr = esm.ESwitchManager() def create_bandwidth_limit(self, port, rule): self.update_bandwidth_limit(port, rule) def update_bandwidth_limit(self, port, rule): pci_slot = port['profile'].get('pci_slot') device = port['device'] self._set_vf_max_rate(device, pci_slot, rule.max_kbps) def delete_bandwidth_limit(self, port): pci_slot = port['profile'].get('pci_slot') if port.get('device_owner') is None: self.eswitch_mgr.clear_max_rate(pci_slot) else: device = port['device'] self._set_vf_max_rate(device, pci_slot) def _set_vf_max_rate(self, device, pci_slot, max_kbps=0): if self.eswitch_mgr.device_exists(device, pci_slot): try: self.eswitch_mgr.set_device_max_rate( device, pci_slot, max_kbps) except exc.SriovNicError: LOG.exception( "Failed to set device %s max rate", device) else: LOG.info("No device with MAC %s defined on agent.", device) # TODO(ihrachys): those handlers are pretty similar, probably could make # use of some code deduplication def create_minimum_bandwidth(self, port, rule): self.update_minimum_bandwidth(port, rule) def update_minimum_bandwidth(self, port, rule): pci_slot = port['profile'].get('pci_slot') device = port['device'] self._set_vf_min_tx_rate(device, pci_slot, rule.min_kbps) def delete_minimum_bandwidth(self, port): pci_slot = port['profile'].get('pci_slot') if port.get('device_owner') is None: self.eswitch_mgr.clear_min_tx_rate(pci_slot) else: device = port['device'] self._set_vf_min_tx_rate(device, pci_slot) def _set_vf_min_tx_rate(self, device, pci_slot, min_tx_kbps=0): if self.eswitch_mgr.device_exists(device, pci_slot): try: self.eswitch_mgr.set_device_min_tx_rate( device, pci_slot, min_tx_kbps) except exc.SriovNicError: LOG.exception( "Failed to set device %s min_tx_rate", device) else: LOG.info("No device with MAC %s defined on agent.", device) neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/__init__.py0000664000175000017500000000000013553660046031750 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/__init__.py0000664000175000017500000000000013553660046025100 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/mech_driver/0000775000175000017500000000000013553660156025272 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/mech_driver/exceptions.py0000664000175000017500000000165113553660046030026 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Exceptions used by SRIOV Mechanism Driver.""" from neutron_lib import exceptions from neutron._i18n import _ class SriovUnsupportedNetworkType(exceptions.NeutronException): """Method was invoked for unsupported network type.""" message = _("Unsupported network type %(net_type)s.") neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/mech_driver/__init__.py0000664000175000017500000000000013553660046027367 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/drivers/mech_sriov/mech_driver/mech_driver.py0000664000175000017500000001645413553660047030144 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib.plugins.ml2 import api from oslo_log import log from neutron.plugins.ml2.drivers import mech_agent from neutron.plugins.ml2.drivers.mech_sriov.mech_driver \ import exceptions as exc from neutron.services.qos.drivers.sriov import driver as sriov_qos_driver LOG = log.getLogger(__name__) FLAT_VLAN = 0 class SriovNicSwitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """Mechanism Driver for SR-IOV capable NIC based switching. The SriovNicSwitchMechanismDriver integrates the ml2 plugin with the sriovNicSwitch L2 agent depending on configuration option. Port binding with this driver may require the sriovNicSwitch agent to be running on the port's host, and that agent to have connectivity to at least one segment of the port's network. L2 agent is not essential for port binding; port binding is handled by VIF Driver via libvirt domain XML. L2 Agent presents in order to manage port update events. """ def __init__(self, agent_type=constants.AGENT_TYPE_NIC_SWITCH, vif_details={portbindings.CAP_PORT_FILTER: False}, supported_vnic_types=[portbindings.VNIC_DIRECT, portbindings.VNIC_MACVTAP, portbindings.VNIC_DIRECT_PHYSICAL]): """Initialize base class for SriovNicSwitch L2 agent type. :param agent_type: Constant identifying agent type in agents_db :param vif_details: Dictionary with details for VIF driver when bound :param supported_vnic_types: The binding:vnic_type values we can bind """ self.agent_type = agent_type self.supported_vnic_types = supported_vnic_types # NOTE(ndipanov): PF passthrough requires a different vif type self.vnic_type_for_vif_type = ( {vtype: portbindings.VIF_TYPE_HOSTDEV_PHY if vtype == portbindings.VNIC_DIRECT_PHYSICAL else portbindings.VIF_TYPE_HW_VEB for vtype in self.supported_vnic_types}) self.vif_details = vif_details sriov_qos_driver.register() def get_allowed_network_types(self, agent): return (constants.TYPE_FLAT, constants.TYPE_VLAN) def get_mappings(self, agent): return agent['configurations'].get('device_mappings', {}) def bind_port(self, context): LOG.debug("Attempting to bind port %(port)s on " "network %(network)s", {'port': context.current['id'], 'network': context.network.current['id']}) profile = context.current.get(portbindings.PROFILE) vnic_type = context.current.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL) capabilities = [] if profile: capabilities = profile.get('capabilities', []) if (vnic_type == portbindings.VNIC_DIRECT and 'switchdev' in capabilities): LOG.debug("Refusing to bind due to unsupported vnic_type: %s " "with switchdev capability", portbindings.VNIC_DIRECT) return if vnic_type not in self.supported_vnic_types: LOG.debug("Refusing to bind due to unsupported vnic_type: %s", vnic_type) return if vnic_type == portbindings.VNIC_DIRECT_PHYSICAL: # Physical functions don't support things like QoS properties, # spoof checking, etc. so we might as well side-step the agent # for now. The agent also doesn't currently recognize non-VF # PCI devices so we won't get port status change updates # either. This should be changed in the future so physical # functions can use device mapping checks and the plugin can # get port status updates. for segment in context.segments_to_bind: if self.try_to_bind_segment_for_agent(context, segment, agent=None): break return for agent in context.host_agents(self.agent_type): LOG.debug("Checking agent: %s", agent) if agent['alive']: for segment in context.segments_to_bind: if self.try_to_bind_segment_for_agent(context, segment, agent): return else: LOG.warning("Attempting to bind with dead agent: %s", agent) def try_to_bind_segment_for_agent(self, context, segment, agent): vnic_type = context.current.get(portbindings.VNIC_TYPE, portbindings.VNIC_DIRECT) vif_type = self.vnic_type_for_vif_type.get( vnic_type, portbindings.VIF_TYPE_HW_VEB) if not self.check_segment_for_agent(segment, agent): return False port_status = (constants.PORT_STATUS_ACTIVE if agent is None else constants.PORT_STATUS_DOWN) context.set_binding(segment[api.ID], vif_type, self._get_vif_details(segment), port_status) LOG.debug("Bound using segment: %s", segment) return True def check_segment_for_agent(self, segment, agent=None): """Check if segment can be bound. :param segment: segment dictionary describing segment to bind :param agent: agents_db entry describing agent to bind or None :returns: True if segment can be bound for agent """ network_type = segment[api.NETWORK_TYPE] if network_type in self.get_allowed_network_types(agent): if agent: mappings = self.get_mappings(agent) LOG.debug("Checking segment: %(segment)s " "for mappings: %(mappings)s ", {'segment': segment, 'mappings': mappings}) return segment[api.PHYSICAL_NETWORK] in mappings return True return False def check_vlan_transparency(self, context): """SR-IOV driver vlan transparency support.""" return True def _get_vif_details(self, segment): network_type = segment[api.NETWORK_TYPE] if network_type == constants.TYPE_FLAT: vlan_id = FLAT_VLAN elif network_type == constants.TYPE_VLAN: vlan_id = segment[api.SEGMENTATION_ID] else: raise exc.SriovUnsupportedNetworkType(net_type=network_type) vif_details = self.vif_details.copy() vif_details[portbindings.VIF_DETAILS_VLAN] = str(vlan_id) return vif_details neutron-12.1.1/neutron/plugins/ml2/managers.py0000664000175000017500000013051413553660047021341 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import external_net as extnet_apidef from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import provider_net as provider from neutron_lib.api import validators from neutron_lib import constants from neutron_lib import exceptions as exc from neutron_lib.plugins.ml2 import api from oslo_config import cfg from oslo_log import log from oslo_utils import excutils import stevedore from neutron._i18n import _ from neutron.conf.plugins.ml2 import config from neutron.db import api as db_api from neutron.db import segments_db from neutron.extensions import multiprovidernet as mpnet from neutron.extensions import vlantransparent from neutron.plugins.ml2.common import exceptions as ml2_exc from neutron.plugins.ml2 import models LOG = log.getLogger(__name__) MAX_BINDING_LEVELS = 10 config.register_ml2_plugin_opts() class TypeManager(stevedore.named.NamedExtensionManager): """Manage network segment types using drivers.""" def __init__(self): # Mapping from type name to DriverManager self.drivers = {} LOG.info("Configured type driver names: %s", cfg.CONF.ml2.type_drivers) super(TypeManager, self).__init__('neutron.ml2.type_drivers', cfg.CONF.ml2.type_drivers, invoke_on_load=True) LOG.info("Loaded type driver names: %s", self.names()) self._register_types() self._check_tenant_network_types(cfg.CONF.ml2.tenant_network_types) self._check_external_network_type(cfg.CONF.ml2.external_network_type) def _register_types(self): for ext in self: network_type = ext.obj.get_type() if network_type in self.drivers: LOG.error("Type driver '%(new_driver)s' ignored because" " type driver '%(old_driver)s' is already" " registered for type '%(type)s'", {'new_driver': ext.name, 'old_driver': self.drivers[network_type].name, 'type': network_type}) else: self.drivers[network_type] = ext LOG.info("Registered types: %s", self.drivers.keys()) def _check_tenant_network_types(self, types): self.tenant_network_types = [] for network_type in types: if network_type in self.drivers: self.tenant_network_types.append(network_type) else: LOG.error("No type driver for tenant network_type: %s. " "Service terminated!", network_type) raise SystemExit(1) LOG.info("Tenant network_types: %s", self.tenant_network_types) def _check_external_network_type(self, ext_network_type): if ext_network_type and ext_network_type not in self.drivers: LOG.error("No type driver for external network_type: %s. " "Service terminated!", ext_network_type) raise SystemExit(1) def _process_provider_segment(self, segment): (network_type, physical_network, segmentation_id) = (self._get_attribute(segment, attr) for attr in provider.ATTRIBUTES) if validators.is_attr_set(network_type): segment = {api.NETWORK_TYPE: network_type, api.PHYSICAL_NETWORK: physical_network, api.SEGMENTATION_ID: segmentation_id} self.validate_provider_segment(segment) return segment msg = _("network_type required") raise exc.InvalidInput(error_message=msg) def _process_provider_create(self, network): if any(validators.is_attr_set(network.get(attr)) for attr in provider.ATTRIBUTES): # Verify that multiprovider and provider attributes are not set # at the same time. if validators.is_attr_set(network.get(mpnet.SEGMENTS)): raise mpnet.SegmentsSetInConjunctionWithProviders() segment = self._get_provider_segment(network) return [self._process_provider_segment(segment)] elif validators.is_attr_set(network.get(mpnet.SEGMENTS)): segments = [self._process_provider_segment(s) for s in network[mpnet.SEGMENTS]] mpnet.check_duplicate_segments(segments, self.is_partial_segment) return segments def _match_segment(self, segment, filters): return all(not filters.get(attr) or segment.get(attr) in filters[attr] for attr in provider.ATTRIBUTES) def _get_provider_segment(self, network): # TODO(manishg): Placeholder method # Code intended for operating on a provider segment should use # this method to extract the segment, even though currently the # segment attributes are part of the network dictionary. In the # future, network and segment information will be decoupled and # here we will do the job of extracting the segment information. return network def network_matches_filters(self, network, filters): if not filters: return True if any(validators.is_attr_set(network.get(attr)) for attr in provider.ATTRIBUTES): segments = [self._get_provider_segment(network)] elif validators.is_attr_set(network.get(mpnet.SEGMENTS)): segments = self._get_attribute(network, mpnet.SEGMENTS) else: return True return any(self._match_segment(s, filters) for s in segments) def _get_attribute(self, attrs, key): value = attrs.get(key) if value is constants.ATTR_NOT_SPECIFIED: value = None return value def extend_network_dict_provider(self, context, network): # this method is left for backward compat even though it would be # easy to change the callers in tree to use the bulk function return self.extend_networks_dict_provider(context, [network]) def extend_networks_dict_provider(self, context, networks): ids = [network['id'] for network in networks] net_segments = segments_db.get_networks_segments(context, ids) for network in networks: segments = net_segments[network['id']] self._extend_network_dict_provider(network, segments) def _extend_network_dict_provider(self, network, segments): if not segments: LOG.debug("Network %s has no segments", network['id']) for attr in provider.ATTRIBUTES: network[attr] = None elif len(segments) > 1: network[mpnet.SEGMENTS] = [ {provider.NETWORK_TYPE: segment[api.NETWORK_TYPE], provider.PHYSICAL_NETWORK: segment[api.PHYSICAL_NETWORK], provider.SEGMENTATION_ID: segment[api.SEGMENTATION_ID]} for segment in segments] else: segment = segments[0] network[provider.NETWORK_TYPE] = segment[api.NETWORK_TYPE] network[provider.PHYSICAL_NETWORK] = segment[ api.PHYSICAL_NETWORK] network[provider.SEGMENTATION_ID] = segment[ api.SEGMENTATION_ID] def initialize(self): for network_type, driver in self.drivers.items(): LOG.info("Initializing driver for type '%s'", network_type) driver.obj.initialize() def _add_network_segment(self, context, network_id, segment, segment_index=0): segments_db.add_network_segment( context, network_id, segment, segment_index) def create_network_segments(self, context, network, tenant_id): """Call type drivers to create network segments.""" segments = self._process_provider_create(network) with db_api.context_manager.writer.using(context): network_id = network['id'] if segments: for segment_index, segment in enumerate(segments): segment = self.reserve_provider_segment( context, segment) self._add_network_segment(context, network_id, segment, segment_index) elif (cfg.CONF.ml2.external_network_type and self._get_attribute(network, extnet_apidef.EXTERNAL)): segment = self._allocate_ext_net_segment(context) self._add_network_segment(context, network_id, segment) else: segment = self._allocate_tenant_net_segment(context) self._add_network_segment(context, network_id, segment) def reserve_network_segment(self, context, segment_data): """Call type drivers to reserve a network segment.""" # Validate the data of segment if not validators.is_attr_set(segment_data[api.NETWORK_TYPE]): msg = _("network_type required") raise exc.InvalidInput(error_message=msg) net_type = self._get_attribute(segment_data, api.NETWORK_TYPE) phys_net = self._get_attribute(segment_data, api.PHYSICAL_NETWORK) seg_id = self._get_attribute(segment_data, api.SEGMENTATION_ID) segment = {api.NETWORK_TYPE: net_type, api.PHYSICAL_NETWORK: phys_net, api.SEGMENTATION_ID: seg_id} self.validate_provider_segment(segment) # Reserve segment in type driver with db_api.context_manager.writer.using(context): return self.reserve_provider_segment(context, segment) def is_partial_segment(self, segment): network_type = segment[api.NETWORK_TYPE] driver = self.drivers.get(network_type) if driver: return driver.obj.is_partial_segment(segment) else: msg = _("network_type value '%s' not supported") % network_type raise exc.InvalidInput(error_message=msg) def validate_provider_segment(self, segment): network_type = segment[api.NETWORK_TYPE] driver = self.drivers.get(network_type) if driver: driver.obj.validate_provider_segment(segment) else: msg = _("network_type value '%s' not supported") % network_type raise exc.InvalidInput(error_message=msg) def reserve_provider_segment(self, context, segment): network_type = segment.get(api.NETWORK_TYPE) driver = self.drivers.get(network_type) if isinstance(driver.obj, api.TypeDriver): return driver.obj.reserve_provider_segment(context.session, segment) else: return driver.obj.reserve_provider_segment(context, segment) def _allocate_segment(self, context, network_type): driver = self.drivers.get(network_type) if isinstance(driver.obj, api.TypeDriver): return driver.obj.allocate_tenant_segment(context.session) else: return driver.obj.allocate_tenant_segment(context) def _allocate_tenant_net_segment(self, context): for network_type in self.tenant_network_types: segment = self._allocate_segment(context, network_type) if segment: return segment raise exc.NoNetworkAvailable() def _allocate_ext_net_segment(self, context): network_type = cfg.CONF.ml2.external_network_type segment = self._allocate_segment(context, network_type) if segment: return segment raise exc.NoNetworkAvailable() def release_network_segments(self, context, network_id): segments = segments_db.get_network_segments(context, network_id, filter_dynamic=None) for segment in segments: self.release_network_segment(context, segment) def release_network_segment(self, context, segment): network_type = segment.get(api.NETWORK_TYPE) driver = self.drivers.get(network_type) if driver: if isinstance(driver.obj, api.TypeDriver): driver.obj.release_segment(context.session, segment) else: driver.obj.release_segment(context, segment) else: LOG.error("Failed to release segment '%s' because " "network type is not supported.", segment) def allocate_dynamic_segment(self, context, network_id, segment): """Allocate a dynamic segment using a partial or full segment dict.""" dynamic_segment = segments_db.get_dynamic_segment( context, network_id, segment.get(api.PHYSICAL_NETWORK), segment.get(api.SEGMENTATION_ID)) if dynamic_segment: return dynamic_segment driver = self.drivers.get(segment.get(api.NETWORK_TYPE)) if isinstance(driver.obj, api.TypeDriver): dynamic_segment = driver.obj.reserve_provider_segment( context.session, segment) else: dynamic_segment = driver.obj.reserve_provider_segment( context, segment) segments_db.add_network_segment(context, network_id, dynamic_segment, is_dynamic=True) return dynamic_segment def release_dynamic_segment(self, context, segment_id): """Delete a dynamic segment.""" segment = segments_db.get_segment_by_id(context, segment_id) if segment: driver = self.drivers.get(segment.get(api.NETWORK_TYPE)) if driver: if isinstance(driver.obj, api.TypeDriver): driver.obj.release_segment(context.session, segment) else: driver.obj.release_segment(context, segment) segments_db.delete_network_segment(context, segment_id) else: LOG.error("Failed to release segment '%s' because " "network type is not supported.", segment) else: LOG.debug("No segment found with id %(segment_id)s", segment_id) class MechanismManager(stevedore.named.NamedExtensionManager): """Manage networking mechanisms using drivers.""" def __init__(self): # Registered mechanism drivers, keyed by name. self.mech_drivers = {} # Ordered list of mechanism drivers, defining # the order in which the drivers are called. self.ordered_mech_drivers = [] LOG.info("Configured mechanism driver names: %s", cfg.CONF.ml2.mechanism_drivers) super(MechanismManager, self).__init__( 'neutron.ml2.mechanism_drivers', cfg.CONF.ml2.mechanism_drivers, invoke_on_load=True, name_order=True, on_missing_entrypoints_callback=self._driver_not_found, on_load_failure_callback=self._driver_not_loaded ) LOG.info("Loaded mechanism driver names: %s", self.names()) self._register_mechanisms() self.host_filtering_supported = self.is_host_filtering_supported() if not self.host_filtering_supported: LOG.info("No mechanism drivers provide segment reachability " "information for agent scheduling.") def _driver_not_found(self, names): msg = (_("The following mechanism drivers were not found: %s") % names) LOG.critical(msg) raise SystemExit(msg) def _driver_not_loaded(self, manager, entrypoint, exception): LOG.critical("The '%(entrypoint)s' entrypoint could not be" " loaded for the following reason: '%(reason)s'.", {'entrypoint': entrypoint, 'reason': exception}) raise SystemExit(str(exception)) def _register_mechanisms(self): """Register all mechanism drivers. This method should only be called once in the MechanismManager constructor. """ for ext in self: self.mech_drivers[ext.name] = ext self.ordered_mech_drivers.append(ext) LOG.info("Registered mechanism drivers: %s", [driver.name for driver in self.ordered_mech_drivers]) def initialize(self): for driver in self.ordered_mech_drivers: LOG.info("Initializing mechanism driver '%s'", driver.name) driver.obj.initialize() def _check_vlan_transparency(self, context): """Helper method for checking vlan transparecncy support. :param context: context parameter to pass to each method call :raises: neutron.extensions.vlantransparent. VlanTransparencyDriverError if any mechanism driver doesn't support vlan transparency. """ if context.current.get('vlan_transparent'): for driver in self.ordered_mech_drivers: if not driver.obj.check_vlan_transparency(context): raise vlantransparent.VlanTransparencyDriverError() def _call_on_drivers(self, method_name, context, continue_on_failure=False, raise_db_retriable=False): """Helper method for calling a method across all mechanism drivers. :param method_name: name of the method to call :param context: context parameter to pass to each method call :param continue_on_failure: whether or not to continue to call all mechanism drivers once one has raised an exception :param raise_db_retriable: whether or not to treat retriable db exception by mechanism drivers to propagate up to upper layer so that upper layer can handle it or error in ML2 player :raises: neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver call fails. or DB retriable error when raise_db_retriable=False. See neutron.db.api.is_retriable for what db exception is retriable """ errors = [] for driver in self.ordered_mech_drivers: try: getattr(driver.obj, method_name)(context) except Exception as e: if raise_db_retriable and db_api.is_retriable(e): with excutils.save_and_reraise_exception(): LOG.debug("DB exception raised by Mechanism driver " "'%(name)s' in %(method)s", {'name': driver.name, 'method': method_name}, exc_info=e) LOG.exception( "Mechanism driver '%(name)s' failed in %(method)s", {'name': driver.name, 'method': method_name} ) errors.append(e) if not continue_on_failure: break if errors: raise ml2_exc.MechanismDriverError( method=method_name, errors=errors ) def create_network_precommit(self, context): """Notify all mechanism drivers during network creation. :raises: DB retriable error if create_network_precommit raises them See neutron.db.api.is_retriable for what db exception is retriable or neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver create_network_precommit call fails. Called within the database transaction. If a mechanism driver raises an exception, then a MechanismDriverError is propagated to the caller, triggering a rollback. There is no guarantee that all mechanism drivers are called in this case. """ self._check_vlan_transparency(context) self._call_on_drivers("create_network_precommit", context, raise_db_retriable=True) def create_network_postcommit(self, context): """Notify all mechanism drivers after network creation. :raises: neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver create_network_postcommit call fails. Called after the database transaction. If a mechanism driver raises an exception, then a MechanismDriverError is propagated to the caller, where the network will be deleted, triggering any required cleanup. There is no guarantee that all mechanism drivers are called in this case. """ self._call_on_drivers("create_network_postcommit", context) def update_network_precommit(self, context): """Notify all mechanism drivers during network update. :raises: DB retriable error if create_network_precommit raises them See neutron.db.api.is_retriable for what db exception is retriable or neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver update_network_precommit call fails. Called within the database transaction. If a mechanism driver raises an exception, then a MechanismDriverError is propagated to the caller, triggering a rollback. There is no guarantee that all mechanism drivers are called in this case. """ self._call_on_drivers("update_network_precommit", context, raise_db_retriable=True) def update_network_postcommit(self, context): """Notify all mechanism drivers after network update. :raises: neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver update_network_postcommit call fails. Called after the database transaction. If any mechanism driver raises an error, then the error is logged but we continue to call every other mechanism driver. A MechanismDriverError is then reraised at the end to notify the caller of a failure. """ self._call_on_drivers("update_network_postcommit", context, continue_on_failure=True) def delete_network_precommit(self, context): """Notify all mechanism drivers during network deletion. :raises: DB retriable error if create_network_precommit raises them See neutron.db.api.is_retriable for what db exception is retriable or neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver delete_network_precommit call fails. Called within the database transaction. If a mechanism driver raises an exception, then a MechanismDriverError is propagated to the caller, triggering a rollback. There is no guarantee that all mechanism drivers are called in this case. """ self._call_on_drivers("delete_network_precommit", context, raise_db_retriable=True) def delete_network_postcommit(self, context): """Notify all mechanism drivers after network deletion. :raises: neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver delete_network_postcommit call fails. Called after the database transaction. If any mechanism driver raises an error, then the error is logged but we continue to call every other mechanism driver. A MechanismDriverError is then reraised at the end to notify the caller of a failure. In general we expect the caller to ignore the error, as the network resource has already been deleted from the database and it doesn't make sense to undo the action by recreating the network. """ self._call_on_drivers("delete_network_postcommit", context, continue_on_failure=True) def create_subnet_precommit(self, context): """Notify all mechanism drivers during subnet creation. :raises: DB retriable error if create_network_precommit raises them See neutron.db.api.is_retriable for what db exception is retriable or neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver create_subnet_precommit call fails. Called within the database transaction. If a mechanism driver raises an exception, then a MechanismDriverError is propagated to the caller, triggering a rollback. There is no guarantee that all mechanism drivers are called in this case. """ self._call_on_drivers("create_subnet_precommit", context, raise_db_retriable=True) def create_subnet_postcommit(self, context): """Notify all mechanism drivers after subnet creation. :raises: neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver create_subnet_postcommit call fails. Called after the database transaction. If a mechanism driver raises an exception, then a MechanismDriverError is propagated to the caller, where the subnet will be deleted, triggering any required cleanup. There is no guarantee that all mechanism drivers are called in this case. """ self._call_on_drivers("create_subnet_postcommit", context) def update_subnet_precommit(self, context): """Notify all mechanism drivers during subnet update. :raises: DB retriable error if create_network_precommit raises them See neutron.db.api.is_retriable for what db exception is retriable or neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver update_subnet_precommit call fails. Called within the database transaction. If a mechanism driver raises an exception, then a MechanismDriverError is propagated to the caller, triggering a rollback. There is no guarantee that all mechanism drivers are called in this case. """ self._call_on_drivers("update_subnet_precommit", context, raise_db_retriable=True) def update_subnet_postcommit(self, context): """Notify all mechanism drivers after subnet update. :raises: neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver update_subnet_postcommit call fails. Called after the database transaction. If any mechanism driver raises an error, then the error is logged but we continue to call every other mechanism driver. A MechanismDriverError is then reraised at the end to notify the caller of a failure. """ self._call_on_drivers("update_subnet_postcommit", context, continue_on_failure=True) def delete_subnet_precommit(self, context): """Notify all mechanism drivers during subnet deletion. :raises: DB retriable error if create_network_precommit raises them See neutron.db.api.is_retriable for what db exception is retriable or neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver delete_subnet_precommit call fails. Called within the database transaction. If a mechanism driver raises an exception, then a MechanismDriverError is propagated to the caller, triggering a rollback. There is no guarantee that all mechanism drivers are called in this case. """ self._call_on_drivers("delete_subnet_precommit", context, raise_db_retriable=True) def delete_subnet_postcommit(self, context): """Notify all mechanism drivers after subnet deletion. :raises: neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver delete_subnet_postcommit call fails. Called after the database transaction. If any mechanism driver raises an error, then the error is logged but we continue to call every other mechanism driver. A MechanismDriverError is then reraised at the end to notify the caller of a failure. In general we expect the caller to ignore the error, as the subnet resource has already been deleted from the database and it doesn't make sense to undo the action by recreating the subnet. """ self._call_on_drivers("delete_subnet_postcommit", context, continue_on_failure=True) def create_port_precommit(self, context): """Notify all mechanism drivers during port creation. :raises: DB retriable error if create_network_precommit raises them See neutron.db.api.is_retriable for what db exception is retriable or neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver create_port_precommit call fails. Called within the database transaction. If a mechanism driver raises an exception, then a MechanismDriverError is propagated to the caller, triggering a rollback. There is no guarantee that all mechanism drivers are called in this case. """ self._call_on_drivers("create_port_precommit", context, raise_db_retriable=True) def create_port_postcommit(self, context): """Notify all mechanism drivers of port creation. :raises: neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver create_port_postcommit call fails. Called after the database transaction. Errors raised by mechanism drivers are left to propagate to the caller, where the port will be deleted, triggering any required cleanup. There is no guarantee that all mechanism drivers are called in this case. """ self._call_on_drivers("create_port_postcommit", context) def update_port_precommit(self, context): """Notify all mechanism drivers during port update. :raises: DB retriable error if create_network_precommit raises them See neutron.db.api.is_retriable for what db exception is retriable or neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver update_port_precommit call fails. Called within the database transaction. If a mechanism driver raises an exception, then a MechanismDriverError is propagated to the caller, triggering a rollback. There is no guarantee that all mechanism drivers are called in this case. """ self._call_on_drivers("update_port_precommit", context, raise_db_retriable=True) def update_port_postcommit(self, context): """Notify all mechanism drivers after port update. :raises: neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver update_port_postcommit call fails. Called after the database transaction. If any mechanism driver raises an error, then the error is logged but we continue to call every other mechanism driver. A MechanismDriverError is then reraised at the end to notify the caller of a failure. """ self._call_on_drivers("update_port_postcommit", context, continue_on_failure=True) def delete_port_precommit(self, context): """Notify all mechanism drivers during port deletion. :raises:DB retriable error if create_network_precommit raises them See neutron.db.api.is_retriable for what db exception is retriable or neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver delete_port_precommit call fails. Called within the database transaction. If a mechanism driver raises an exception, then a MechanismDriverError is propagated to the caller, triggering a rollback. There is no guarantee that all mechanism drivers are called in this case. """ self._call_on_drivers("delete_port_precommit", context, raise_db_retriable=True) def delete_port_postcommit(self, context): """Notify all mechanism drivers after port deletion. :raises: neutron.plugins.ml2.common.MechanismDriverError if any mechanism driver delete_port_postcommit call fails. Called after the database transaction. If any mechanism driver raises an error, then the error is logged but we continue to call every other mechanism driver. A MechanismDriverError is then reraised at the end to notify the caller of a failure. In general we expect the caller to ignore the error, as the port resource has already been deleted from the database and it doesn't make sense to undo the action by recreating the port. """ self._call_on_drivers("delete_port_postcommit", context, continue_on_failure=True) def bind_port(self, context): """Attempt to bind a port using registered mechanism drivers. :param context: PortContext instance describing the port Called outside any transaction to attempt to establish a port binding. """ binding = context._binding LOG.debug("Attempting to bind port %(port)s on host %(host)s " "for vnic_type %(vnic_type)s with profile %(profile)s", {'port': context.current['id'], 'host': context.host, 'vnic_type': binding.vnic_type, 'profile': binding.profile}) context._clear_binding_levels() if not self._bind_port_level(context, 0, context.network.network_segments): binding.vif_type = portbindings.VIF_TYPE_BINDING_FAILED LOG.error("Failed to bind port %(port)s on host %(host)s " "for vnic_type %(vnic_type)s using segments " "%(segments)s", {'port': context.current['id'], 'host': context.host, 'vnic_type': binding.vnic_type, 'segments': context.network.network_segments}) def _bind_port_level(self, context, level, segments_to_bind): binding = context._binding port_id = context.current['id'] LOG.debug("Attempting to bind port %(port)s on host %(host)s " "at level %(level)s using segments %(segments)s", {'port': port_id, 'host': context.host, 'level': level, 'segments': segments_to_bind}) if level == MAX_BINDING_LEVELS: LOG.error("Exceeded maximum binding levels attempting to bind " "port %(port)s on host %(host)s", {'port': context.current['id'], 'host': context.host}) return False for driver in self.ordered_mech_drivers: if not self._check_driver_to_bind(driver, segments_to_bind, context._binding_levels): continue try: context._prepare_to_bind(segments_to_bind) driver.obj.bind_port(context) segment = context._new_bound_segment if segment: context._push_binding_level( models.PortBindingLevel(port_id=port_id, host=context.host, level=level, driver=driver.name, segment_id=segment)) next_segments = context._next_segments_to_bind if next_segments: # Continue binding another level. if self._bind_port_level(context, level + 1, next_segments): return True else: LOG.warning("Failed to bind port %(port)s on " "host %(host)s at level %(lvl)s", {'port': context.current['id'], 'host': context.host, 'lvl': level + 1}) context._pop_binding_level() else: # Binding complete. LOG.debug("Bound port: %(port)s, " "host: %(host)s, " "vif_type: %(vif_type)s, " "vif_details: %(vif_details)s, " "binding_levels: %(binding_levels)s", {'port': port_id, 'host': context.host, 'vif_type': binding.vif_type, 'vif_details': binding.vif_details, 'binding_levels': context.binding_levels}) return True except Exception: LOG.exception("Mechanism driver %s failed in " "bind_port", driver.name) def is_host_filtering_supported(self): return all(driver.obj.is_host_filtering_supported() for driver in self.ordered_mech_drivers) def filter_hosts_with_segment_access( self, context, segments, candidate_hosts, agent_getter): """Filter hosts with access to at least one segment. :returns: a subset of candidate_hosts. This method returns all hosts from candidate_hosts with access to a segment according to at least one driver. """ candidate_hosts = set(candidate_hosts) if not self.host_filtering_supported: return candidate_hosts hosts_with_access = set() for driver in self.ordered_mech_drivers: hosts = driver.obj.filter_hosts_with_segment_access( context, segments, candidate_hosts, agent_getter) hosts_with_access |= hosts candidate_hosts -= hosts if not candidate_hosts: break return hosts_with_access def _check_driver_to_bind(self, driver, segments_to_bind, binding_levels): # To prevent a possible binding loop, don't try to bind with # this driver if the same driver has already bound at a higher # level to one of the segments we are currently trying to # bind. Note that it is OK for the same driver to bind at # multiple levels using different segments. segment_ids_to_bind = {s[api.SEGMENTATION_ID] for s in segments_to_bind} for level in binding_levels: if (level.driver == driver.name and level.segment_id in segment_ids_to_bind): LOG.debug("segment %(segment)s is already bound " "by driver %(driver)s", {"segment": level.segment_id, "driver": level.driver}) return False return True def get_workers(self): workers = [] for driver in self.ordered_mech_drivers: workers += driver.obj.get_workers() return workers class ExtensionManager(stevedore.named.NamedExtensionManager): """Manage extension drivers using drivers.""" def __init__(self): # Ordered list of extension drivers, defining # the order in which the drivers are called. self.ordered_ext_drivers = [] LOG.info("Configured extension driver names: %s", cfg.CONF.ml2.extension_drivers) super(ExtensionManager, self).__init__('neutron.ml2.extension_drivers', cfg.CONF.ml2.extension_drivers, invoke_on_load=True, name_order=True) LOG.info("Loaded extension driver names: %s", self.names()) self._register_drivers() def _register_drivers(self): """Register all extension drivers. This method should only be called once in the ExtensionManager constructor. """ for ext in self: self.ordered_ext_drivers.append(ext) LOG.info("Registered extension drivers: %s", [driver.name for driver in self.ordered_ext_drivers]) def initialize(self): # Initialize each driver in the list. for driver in self.ordered_ext_drivers: LOG.info("Initializing extension driver '%s'", driver.name) driver.obj.initialize() def extension_aliases(self): exts = [] for driver in self.ordered_ext_drivers: aliases = driver.obj.extension_aliases for alias in aliases: if not alias: continue exts.append(alias) LOG.info("Got %(alias)s extension from driver '%(drv)s'", {'alias': alias, 'drv': driver.name}) return exts def _call_on_ext_drivers(self, method_name, plugin_context, data, result): """Helper method for calling a method across all extension drivers.""" for driver in self.ordered_ext_drivers: try: getattr(driver.obj, method_name)(plugin_context, data, result) except Exception: with excutils.save_and_reraise_exception(): LOG.info("Extension driver '%(name)s' failed in " "%(method)s", {'name': driver.name, 'method': method_name}) def process_create_network(self, plugin_context, data, result): """Notify all extension drivers during network creation.""" self._call_on_ext_drivers("process_create_network", plugin_context, data, result) def process_update_network(self, plugin_context, data, result): """Notify all extension drivers during network update.""" self._call_on_ext_drivers("process_update_network", plugin_context, data, result) def process_create_subnet(self, plugin_context, data, result): """Notify all extension drivers during subnet creation.""" self._call_on_ext_drivers("process_create_subnet", plugin_context, data, result) def process_update_subnet(self, plugin_context, data, result): """Notify all extension drivers during subnet update.""" self._call_on_ext_drivers("process_update_subnet", plugin_context, data, result) def process_create_port(self, plugin_context, data, result): """Notify all extension drivers during port creation.""" self._call_on_ext_drivers("process_create_port", plugin_context, data, result) def process_update_port(self, plugin_context, data, result): """Notify all extension drivers during port update.""" self._call_on_ext_drivers("process_update_port", plugin_context, data, result) def _call_on_dict_driver(self, method_name, session, base_model, result): for driver in self.ordered_ext_drivers: try: getattr(driver.obj, method_name)(session, base_model, result) except Exception: LOG.exception("Extension driver '%(name)s' failed in " "%(method)s", {'name': driver.name, 'method': method_name}) raise ml2_exc.ExtensionDriverError(driver=driver.name) def extend_network_dict(self, session, base_model, result): """Notify all extension drivers to extend network dictionary.""" self._call_on_dict_driver("extend_network_dict", session, base_model, result) def extend_subnet_dict(self, session, base_model, result): """Notify all extension drivers to extend subnet dictionary.""" self._call_on_dict_driver("extend_subnet_dict", session, base_model, result) def extend_port_dict(self, session, base_model, result): """Notify all extension drivers to extend port dictionary.""" self._call_on_dict_driver("extend_port_dict", session, base_model, result) neutron-12.1.1/neutron/plugins/ml2/extensions/0000775000175000017500000000000013553660156021366 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/extensions/qos.py0000664000175000017500000000375013553660046022545 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins.ml2 import api from oslo_log import log as logging from neutron.core_extensions import base as base_core from neutron.core_extensions import qos as qos_core LOG = logging.getLogger(__name__) QOS_EXT_DRIVER_ALIAS = 'qos' class QosExtensionDriver(api.ExtensionDriver): def initialize(self): self.core_ext_handler = qos_core.QosCoreResourceExtension() LOG.debug("QosExtensionDriver initialization complete") def process_create_network(self, context, data, result): self.core_ext_handler.process_fields( context, base_core.NETWORK, base_core.EVENT_CREATE, data, result) def process_update_network(self, context, data, result): self.core_ext_handler.process_fields( context, base_core.NETWORK, base_core.EVENT_UPDATE, data, result) def process_create_port(self, context, data, result): self.core_ext_handler.process_fields( context, base_core.PORT, base_core.EVENT_UPDATE, data, result) process_update_port = process_create_port def extend_network_dict(self, session, db_data, result): result.update( self.core_ext_handler.extract_fields( base_core.NETWORK, db_data)) def extend_port_dict(self, session, db_data, result): result.update( self.core_ext_handler.extract_fields(base_core.PORT, db_data)) neutron-12.1.1/neutron/plugins/ml2/extensions/data_plane_status.py0000664000175000017500000000312313553660046025430 0ustar zuulzuul00000000000000# Copyright (c) 2017 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import data_plane_status as dps_lib from neutron_lib.plugins.ml2 import api from oslo_log import log as logging from neutron.db import data_plane_status_db as dps_db LOG = logging.getLogger(__name__) class DataPlaneStatusExtensionDriver(api.ExtensionDriver, dps_db.DataPlaneStatusMixin): _supported_extension_alias = 'data-plane-status' def initialize(self): LOG.info("DataPlaneStatusExtensionDriver initialization complete") @property def extension_alias(self): return self._supported_extension_alias def process_update_port(self, plugin_context, data, result): if dps_lib.DATA_PLANE_STATUS in data: self._process_update_port_data_plane_status(plugin_context, data, result) def extend_port_dict(self, session, db_data, result): self._extend_port_data_plane_status(result, db_data) neutron-12.1.1/neutron/plugins/ml2/extensions/port_security.py0000664000175000017500000000640513553660047024657 0ustar zuulzuul00000000000000# Copyright 2015 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import port_security as psec from neutron_lib.api import validators from neutron_lib.plugins.ml2 import api from neutron_lib.utils import net from oslo_log import log as logging from neutron.db import common_db_mixin from neutron.db import portsecurity_db_common as ps_db_common LOG = logging.getLogger(__name__) class PortSecurityExtensionDriver(api.ExtensionDriver, ps_db_common.PortSecurityDbCommon, common_db_mixin.CommonDbMixin): _supported_extension_alias = 'port-security' def initialize(self): LOG.info("PortSecurityExtensionDriver initialization complete") @property def extension_alias(self): return self._supported_extension_alias def process_create_network(self, context, data, result): # Create the network extension attributes. if psec.PORTSECURITY not in data: data[psec.PORTSECURITY] = psec.DEFAULT_PORT_SECURITY self._process_network_port_security_create(context, data, result) def process_update_network(self, context, data, result): # Update the network extension attributes. if psec.PORTSECURITY in data: self._process_network_port_security_update(context, data, result) def process_create_port(self, context, data, result): # Create the port extension attributes. data[psec.PORTSECURITY] = self._determine_port_security(context, data) self._process_port_port_security_create(context, data, result) def process_update_port(self, context, data, result): if psec.PORTSECURITY in data: self._process_port_port_security_update( context, data, result) def extend_network_dict(self, session, db_data, result): self._extend_port_security_dict(result, db_data) def extend_port_dict(self, session, db_data, result): self._extend_port_security_dict(result, db_data) def _determine_port_security(self, context, port): """Returns a boolean (port_security_enabled). Port_security is the value associated with the port if one is present otherwise the value associated with the network is returned. """ # we don't apply security groups for dhcp, router if port.get('device_owner') and net.is_port_trusted(port): return False if validators.is_attr_set(port.get(psec.PORTSECURITY)): port_security_enabled = port[psec.PORTSECURITY] else: port_security_enabled = self._get_network_security_binding( context, port['network_id']) return port_security_enabled neutron-12.1.1/neutron/plugins/ml2/extensions/__init__.py0000664000175000017500000000000013553660046023463 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/extensions/dns_integration.py0000664000175000017500000005501313553660047025132 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import dns as dns_apidef from neutron_lib.api import validators from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as lib_const from neutron_lib.exceptions import dns as dns_exc from neutron_lib.plugins import directory from neutron_lib.plugins.ml2 import api from oslo_config import cfg from oslo_log import log as logging from neutron.db import segments_db from neutron.objects import network as net_obj from neutron.objects import ports as port_obj from neutron.plugins.common import utils as plugin_utils from neutron.services.externaldns import driver LOG = logging.getLogger(__name__) class DNSExtensionDriver(api.ExtensionDriver): _supported_extension_alias = dns_apidef.ALIAS @property def extension_alias(self): return self._supported_extension_alias def process_create_network(self, plugin_context, request_data, db_data): dns_domain = request_data.get(dns_apidef.DNSDOMAIN) if not validators.is_attr_set(dns_domain): return if dns_domain: net_obj.NetworkDNSDomain(plugin_context, network_id=db_data['id'], dns_domain=dns_domain).create() db_data[dns_apidef.DNSDOMAIN] = dns_domain def process_update_network(self, plugin_context, request_data, db_data): new_value = request_data.get(dns_apidef.DNSDOMAIN) if not validators.is_attr_set(new_value): return current_dns_domain = db_data.get(dns_apidef.DNSDOMAIN) if current_dns_domain == new_value: return net_id = db_data['id'] if current_dns_domain: net_dns_domain = net_obj.NetworkDNSDomain.get_object( plugin_context, network_id=net_id) if new_value: net_dns_domain['dns_domain'] = new_value db_data[dns_apidef.DNSDOMAIN] = new_value net_dns_domain.update() else: net_dns_domain.delete() db_data[dns_apidef.DNSDOMAIN] = '' elif new_value: net_obj.NetworkDNSDomain(plugin_context, network_id=net_id, dns_domain=new_value).create() db_data[dns_apidef.DNSDOMAIN] = new_value def process_create_port(self, plugin_context, request_data, db_data): if not (request_data.get(dns_apidef.DNSNAME) or request_data.get(dns_apidef.DNSDOMAIN)): return dns_name, is_dns_domain_default = self._get_request_dns_name( request_data) if is_dns_domain_default: return network = self._get_network(plugin_context, db_data['network_id']) self._create_port_dns_record(plugin_context, request_data, db_data, network, dns_name) def _create_port_dns_record(self, plugin_context, request_data, db_data, network, dns_name): external_dns_domain = (request_data.get(dns_apidef.DNSDOMAIN) or network.get(dns_apidef.DNSDOMAIN)) current_dns_name, current_dns_domain = ( self._calculate_current_dns_name_and_domain( dns_name, external_dns_domain, self.external_dns_not_needed(plugin_context, network))) dns_data_obj = port_obj.PortDNS( plugin_context, port_id=db_data['id'], current_dns_name=current_dns_name, current_dns_domain=current_dns_domain, previous_dns_name='', previous_dns_domain='', dns_name=dns_name, dns_domain=request_data.get(dns_apidef.DNSDOMAIN, '')) dns_data_obj.create() return dns_data_obj def _calculate_current_dns_name_and_domain(self, dns_name, external_dns_domain, no_external_dns_service): # When creating a new PortDNS object, the current_dns_name and # current_dns_domain fields hold the data that the integration driver # will send to the external DNS service. They are set to non-blank # values only if all the following conditions are met: # 1) There is an external DNS integration driver configured # 2) The user request contains a valid non-blank value for the port's # dns_name # 3) The user request contains a valid non-blank value for the port's # dns_domain or the port's network has a non-blank value in its # dns_domain attribute are_both_dns_attributes_set = dns_name and external_dns_domain if no_external_dns_service or not are_both_dns_attributes_set: return '', '' return dns_name, external_dns_domain def _update_dns_db(self, plugin_context, request_data, db_data, network): dns_name = request_data.get(dns_apidef.DNSNAME) dns_domain = request_data.get(dns_apidef.DNSDOMAIN) has_fixed_ips = 'fixed_ips' in request_data dns_data_db = port_obj.PortDNS.get_object( plugin_context, port_id=db_data['id']) if dns_data_db: is_dns_name_changed = (dns_name is not None and dns_data_db[dns_apidef.DNSNAME] != dns_name) is_dns_domain_changed = (dns_domain is not None and dns_data_db[dns_apidef.DNSDOMAIN] != dns_domain) if (is_dns_name_changed or is_dns_domain_changed or (has_fixed_ips and dns_data_db['current_dns_name'])): dns_data_db = self._populate_previous_external_dns_data( dns_data_db) dns_data_db = self._populate_current_external_dns_data( request_data, network, dns_data_db, dns_name, dns_domain, is_dns_name_changed, is_dns_domain_changed) elif not dns_data_db['current_dns_name']: # If port was removed from external DNS service in previous # update, make sure we don't attempt removal again dns_data_db['previous_dns_name'] = '' dns_data_db['previous_dns_domain'] = '' dns_data_db.update() return dns_data_db if dns_name or dns_domain: dns_data_db = self._create_port_dns_record(plugin_context, request_data, db_data, network, dns_name or '') return dns_data_db def _populate_previous_external_dns_data(self, dns_data_db): dns_data_db['previous_dns_name'] = ( dns_data_db['current_dns_name']) dns_data_db['previous_dns_domain'] = ( dns_data_db['current_dns_domain']) return dns_data_db def _populate_current_external_dns_data(self, request_data, network, dns_data_db, dns_name, dns_domain, is_dns_name_changed, is_dns_domain_changed): if is_dns_name_changed or is_dns_domain_changed: if is_dns_name_changed: dns_data_db[dns_apidef.DNSNAME] = dns_name external_dns_domain = (dns_data_db[dns_apidef.DNSDOMAIN] or network.get(dns_apidef.DNSDOMAIN)) if is_dns_domain_changed: dns_data_db[dns_apidef.DNSDOMAIN] = dns_domain external_dns_domain = request_data[dns_apidef.DNSDOMAIN] if not external_dns_domain: external_dns_domain = network.get(dns_apidef.DNSDOMAIN) dns_data_db['current_dns_name'] = dns_data_db[dns_apidef.DNSNAME] dns_data_db['current_dns_domain'] = external_dns_domain if not (dns_data_db['current_dns_name'] and dns_data_db['current_dns_domain']): dns_data_db['current_dns_name'] = '' dns_data_db['current_dns_domain'] = '' return dns_data_db def process_update_port(self, plugin_context, request_data, db_data): has_dns_name = dns_apidef.DNSNAME in request_data has_fixed_ips = 'fixed_ips' in request_data has_dns_domain = dns_apidef.DNSDOMAIN in request_data if not any((has_dns_name, has_fixed_ips, has_dns_domain)): return is_dns_domain_default = self._get_request_dns_name( request_data)[1] if is_dns_domain_default: self._extend_port_dict(plugin_context.session, db_data, db_data, None) return network = self._get_network(plugin_context, db_data['network_id']) dns_data_db = None if self.external_dns_not_needed(plugin_context, network): # No need to update external DNS service. Only process the port's # dns_name or dns_domain attributes if necessary if has_dns_name or has_dns_domain: dns_data_db = self._process_only_port_update( plugin_context, request_data, db_data) else: dns_data_db = self._update_dns_db(plugin_context, request_data, db_data, network) self._extend_port_dict(plugin_context.session, db_data, db_data, dns_data_db) def _process_only_port_update(self, plugin_context, request_data, db_data): dns_name = request_data.get(dns_apidef.DNSNAME) dns_domain = request_data.get(dns_apidef.DNSDOMAIN) dns_data_db = port_obj.PortDNS.get_object( plugin_context, port_id=db_data['id']) if dns_data_db: if dns_name is not None and dns_data_db[ dns_apidef.DNSNAME] != dns_name: dns_data_db[dns_apidef.DNSNAME] = dns_name if (dns_domain is not None and dns_data_db[dns_apidef.DNSDOMAIN] != dns_domain): dns_data_db[dns_apidef.DNSDOMAIN] = dns_domain dns_data_db.update() return dns_data_db dns_data_db = port_obj.PortDNS(plugin_context, port_id=db_data['id'], current_dns_name='', current_dns_domain='', previous_dns_name='', previous_dns_domain='', dns_name=dns_name or '', dns_domain=dns_domain or '') dns_data_db.create() return dns_data_db def external_dns_not_needed(self, context, network): """Decide if ports in network need to be sent to the DNS service. :param context: plugin request context :param network: network dictionary :return: True or False """ pass def extend_network_dict(self, session, db_data, response_data): response_data[dns_apidef.DNSDOMAIN] = '' if db_data.dns_domain: response_data[dns_apidef.DNSDOMAIN] = db_data.dns_domain[ dns_apidef.DNSDOMAIN] return response_data def _get_dns_domain(self): if not cfg.CONF.dns_domain: return '' if cfg.CONF.dns_domain.endswith('.'): return cfg.CONF.dns_domain return '%s.' % cfg.CONF.dns_domain def _get_request_dns_name(self, port): dns_domain = self._get_dns_domain() if dns_domain and dns_domain != lib_const.DNS_DOMAIN_DEFAULT: return port.get(dns_apidef.DNSNAME, ''), False return '', True def _get_request_dns_name_and_domain_name(self, dns_data_db): dns_domain = self._get_dns_domain() dns_name = '' if dns_domain and dns_domain != lib_const.DNS_DOMAIN_DEFAULT: if dns_data_db: dns_name = dns_data_db.dns_name return dns_name, dns_domain def _get_dns_names_for_port(self, ips, dns_data_db): dns_assignment = [] dns_name, dns_domain = self._get_request_dns_name_and_domain_name( dns_data_db) for ip in ips: if dns_name: hostname = dns_name fqdn = dns_name if not dns_name.endswith('.'): fqdn = '%s.%s' % (dns_name, dns_domain) else: hostname = 'host-%s' % ip['ip_address'].replace( '.', '-').replace(':', '-') fqdn = hostname if dns_domain: fqdn = '%s.%s' % (hostname, dns_domain) dns_assignment.append({'ip_address': ip['ip_address'], 'hostname': hostname, 'fqdn': fqdn}) return dns_assignment def _get_dns_name_for_port_get(self, port, dns_data_db): if port['fixed_ips']: return self._get_dns_names_for_port(port['fixed_ips'], dns_data_db) return [] def _extend_port_dict(self, session, db_data, response_data, dns_data_db): if not dns_data_db: response_data[dns_apidef.DNSNAME] = '' else: response_data[dns_apidef.DNSNAME] = dns_data_db[dns_apidef.DNSNAME] response_data['dns_assignment'] = self._get_dns_name_for_port_get( db_data, dns_data_db) return response_data def extend_port_dict(self, session, db_data, response_data): dns_data_db = db_data.dns return self._extend_port_dict(session, db_data, response_data, dns_data_db) def _get_network(self, context, network_id): plugin = directory.get_plugin() return plugin.get_network(context, network_id) class DNSExtensionDriverML2(DNSExtensionDriver): def initialize(self): LOG.info("DNSExtensionDriverML2 initialization complete") def _is_tunnel_tenant_network(self, provider_net): if provider_net['network_type'] == 'geneve': tunnel_ranges = cfg.CONF.ml2_type_geneve.vni_ranges elif provider_net['network_type'] == 'vxlan': tunnel_ranges = cfg.CONF.ml2_type_vxlan.vni_ranges else: tunnel_ranges = cfg.CONF.ml2_type_gre.tunnel_id_ranges segmentation_id = int(provider_net['segmentation_id']) for entry in tunnel_ranges: entry = entry.strip() tun_min, tun_max = entry.split(':') tun_min = tun_min.strip() tun_max = tun_max.strip() return int(tun_min) <= segmentation_id <= int(tun_max) def _is_vlan_tenant_network(self, provider_net): network_vlan_ranges = plugin_utils.parse_network_vlan_ranges( cfg.CONF.ml2_type_vlan.network_vlan_ranges) vlan_ranges = network_vlan_ranges[provider_net['physical_network']] if not vlan_ranges: return False segmentation_id = int(provider_net['segmentation_id']) for vlan_range in vlan_ranges: if vlan_range[0] <= segmentation_id <= vlan_range[1]: return True def external_dns_not_needed(self, context, network): dns_driver = _get_dns_driver() if not dns_driver: return True if network['router:external']: return True segments = segments_db.get_network_segments(context, network['id']) if len(segments) > 1: return False provider_net = segments[0] if provider_net['network_type'] == 'local': return True if provider_net['network_type'] == 'flat': return False if provider_net['network_type'] == 'vlan': return self._is_vlan_tenant_network(provider_net) if provider_net['network_type'] in ['gre', 'vxlan', 'geneve']: return self._is_tunnel_tenant_network(provider_net) return True class DNSDomainPortsExtensionDriver(DNSExtensionDriverML2): _supported_extension_aliases = [dns_apidef.ALIAS, 'dns-domain-ports'] @property def extension_aliases(self): return self._supported_extension_aliases def initialize(self): LOG.info("DNSDomainPortsExtensionDriver initialization complete") def extend_port_dict(self, session, db_data, response_data): response_data = ( super(DNSDomainPortsExtensionDriver, self).extend_port_dict( session, db_data, response_data)) dns_data_db = db_data.dns response_data[dns_apidef.DNSDOMAIN] = '' if dns_data_db: response_data[dns_apidef.DNSDOMAIN] = dns_data_db[ dns_apidef.DNSDOMAIN] DNS_DRIVER = None def _get_dns_driver(): global DNS_DRIVER if DNS_DRIVER: return DNS_DRIVER if not cfg.CONF.external_dns_driver: return try: DNS_DRIVER = driver.ExternalDNSService.get_instance() LOG.debug("External DNS driver loaded: %s", cfg.CONF.external_dns_driver) return DNS_DRIVER except ImportError: LOG.exception("ImportError exception occurred while loading " "the external DNS service driver") raise dns_exc.ExternalDNSDriverNotFound( driver=cfg.CONF.external_dns_driver) def _create_port_in_external_dns_service(resource, event, trigger, **kwargs): dns_driver = _get_dns_driver() if not dns_driver: return context = kwargs['context'] port = kwargs['port'] dns_data_db = port_obj.PortDNS.get_object( context, port_id=port['id']) if not (dns_data_db and dns_data_db['current_dns_name']): return records = [ip['ip_address'] for ip in port['fixed_ips']] _send_data_to_external_dns_service(context, dns_driver, dns_data_db['current_dns_domain'], dns_data_db['current_dns_name'], records) def _send_data_to_external_dns_service(context, dns_driver, dns_domain, dns_name, records): try: dns_driver.create_record_set(context, dns_domain, dns_name, records) except (dns_exc.DNSDomainNotFound, dns_exc.DuplicateRecordSet) as e: LOG.exception("Error publishing port data in external DNS " "service. Name: '%(name)s'. Domain: '%(domain)s'. " "DNS service driver message '%(message)s'", {"name": dns_name, "domain": dns_domain, "message": e.msg}) def _remove_data_from_external_dns_service(context, dns_driver, dns_domain, dns_name, records): try: dns_driver.delete_record_set(context, dns_domain, dns_name, records) except (dns_exc.DNSDomainNotFound, dns_exc.DuplicateRecordSet) as e: LOG.exception("Error deleting port data from external DNS " "service. Name: '%(name)s'. Domain: '%(domain)s'. " "IP addresses '%(ips)s'. DNS service driver message " "'%(message)s'", {"name": dns_name, "domain": dns_domain, "message": e.msg, "ips": ', '.join(records)}) def _update_port_in_external_dns_service(resource, event, trigger, **kwargs): dns_driver = _get_dns_driver() if not dns_driver: return context = kwargs['context'] updated_port = kwargs['port'] original_port = kwargs.get('original_port') if not original_port: return original_ips = [ip['ip_address'] for ip in original_port['fixed_ips']] updated_ips = [ip['ip_address'] for ip in updated_port['fixed_ips']] is_dns_name_changed = (updated_port[dns_apidef.DNSNAME] != original_port[dns_apidef.DNSNAME]) is_dns_domain_changed = (dns_apidef.DNSDOMAIN in updated_port and updated_port[dns_apidef.DNSDOMAIN] != original_port[dns_apidef.DNSDOMAIN]) ips_changed = set(original_ips) != set(updated_ips) if not any((is_dns_name_changed, is_dns_domain_changed, ips_changed)): return dns_data_db = port_obj.PortDNS.get_object( context, port_id=updated_port['id']) if not (dns_data_db and (dns_data_db['previous_dns_name'] or dns_data_db[ 'current_dns_name'])): return if dns_data_db['previous_dns_name']: _remove_data_from_external_dns_service( context, dns_driver, dns_data_db['previous_dns_domain'], dns_data_db['previous_dns_name'], original_ips) if dns_data_db['current_dns_name']: _send_data_to_external_dns_service(context, dns_driver, dns_data_db['current_dns_domain'], dns_data_db['current_dns_name'], updated_ips) def _delete_port_in_external_dns_service(resource, event, trigger, **kwargs): dns_driver = _get_dns_driver() if not dns_driver: return context = kwargs['context'] port_id = kwargs['port_id'] dns_data_db = port_obj.PortDNS.get_object( context, port_id=port_id) if not dns_data_db: return if dns_data_db['current_dns_name']: ip_allocations = port_obj.IPAllocation.get_objects(context, port_id=port_id) records = [str(alloc['ip_address']) for alloc in ip_allocations] _remove_data_from_external_dns_service( context, dns_driver, dns_data_db['current_dns_domain'], dns_data_db['current_dns_name'], records) def subscribe(): registry.subscribe( _create_port_in_external_dns_service, resources.PORT, events.AFTER_CREATE) registry.subscribe( _update_port_in_external_dns_service, resources.PORT, events.AFTER_UPDATE) registry.subscribe( _delete_port_in_external_dns_service, resources.PORT, events.BEFORE_DELETE) subscribe() neutron-12.1.1/neutron/plugins/ml2/models.py0000664000175000017500000001232113553660047021022 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db import models_v2 BINDING_PROFILE_LEN = 4095 class PortBinding(model_base.BASEV2): """Represent binding-related state of a port. A port binding stores the port attributes required for the portbindings extension, as well as internal ml2 state such as which MechanismDriver and which segment are used by the port binding. """ __tablename__ = 'ml2_port_bindings' port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True) host = sa.Column(sa.String(255), nullable=False, default='', server_default='', primary_key=True) vnic_type = sa.Column(sa.String(64), nullable=False, default=portbindings.VNIC_NORMAL, server_default=portbindings.VNIC_NORMAL) profile = sa.Column(sa.String(BINDING_PROFILE_LEN), nullable=False, default='', server_default='') vif_type = sa.Column(sa.String(64), nullable=False) vif_details = sa.Column(sa.String(4095), nullable=False, default='', server_default='') status = sa.Column(sa.String(16), nullable=False, default=constants.ACTIVE, server_default=constants.ACTIVE) # Add a relationship to the Port model in order to instruct SQLAlchemy to # eagerly load port bindings port = orm.relationship( models_v2.Port, load_on_pending=True, backref=orm.backref("port_binding", lazy='joined', uselist=False, cascade='delete')) revises_on_change = ('port', ) class PortBindingLevel(model_base.BASEV2): """Represent each level of a port binding. Stores information associated with each level of an established port binding. Different levels might correspond to the host and ToR switch, for instance. """ __tablename__ = 'ml2_port_binding_levels' port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True) host = sa.Column(sa.String(255), nullable=False, primary_key=True) level = sa.Column(sa.Integer, primary_key=True, autoincrement=False) driver = sa.Column(sa.String(64)) segment_id = sa.Column(sa.String(36), sa.ForeignKey('networksegments.id', ondelete="SET NULL")) # Add a relationship to the Port model in order to instruct SQLAlchemy to # eagerly load port bindings port = orm.relationship( models_v2.Port, load_on_pending=True, backref=orm.backref("binding_levels", lazy='subquery', cascade='delete')) revises_on_change = ('port', ) class DistributedPortBinding(model_base.BASEV2): """Represent binding-related state of a Distributed Router(DVR, HA) port. Port binding for all the ports associated to a Distributed router(DVR, HA) identified by router_id. Currently DEVICE_OWNER_ROUTER_SNAT(DVR+HA router), DEVICE_OWNER_DVR_INTERFACE, DEVICE_OWNER_HA_REPLICATED_INT are distributed router ports. """ __tablename__ = 'ml2_distributed_port_bindings' port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True) host = sa.Column(sa.String(255), nullable=False, primary_key=True) router_id = sa.Column(sa.String(36), nullable=True) vif_type = sa.Column(sa.String(64), nullable=False) vif_details = sa.Column(sa.String(4095), nullable=False, default='', server_default='') vnic_type = sa.Column(sa.String(64), nullable=False, default=portbindings.VNIC_NORMAL, server_default=portbindings.VNIC_NORMAL) profile = sa.Column(sa.String(BINDING_PROFILE_LEN), nullable=False, default='', server_default='') status = sa.Column(sa.String(16), nullable=False) # Add a relationship to the Port model in order to instruct SQLAlchemy to # eagerly load port bindings port = orm.relationship( models_v2.Port, load_on_pending=True, backref=orm.backref("distributed_port_binding", lazy='subquery', cascade='delete')) revises_on_change = ('port', ) neutron-12.1.1/neutron/plugins/ml2/driver_context.py0000664000175000017500000002616313553660047022607 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib.plugins.ml2 import api from oslo_log import log from oslo_serialization import jsonutils import sqlalchemy from neutron.db import segments_db LOG = log.getLogger(__name__) class InstanceSnapshot(object): """Used to avoid holding references to DB objects in PortContext.""" def __init__(self, obj): self._model_class = obj.__class__ self._identity_key = sqlalchemy.orm.util.identity_key(instance=obj)[1] self._cols = [col.key for col in sqlalchemy.inspect(self._model_class).columns] for col in self._cols: setattr(self, col, getattr(obj, col)) def persist_state_to_session(self, session): """Updates the state of the snapshot in the session. Finds the SQLA object in the session if it exists or creates a new object and updates the object with the column values stored in this snapshot. """ db_obj = session.query(self._model_class).get(self._identity_key) if db_obj: for col in self._cols: setattr(db_obj, col, getattr(self, col)) else: session.add(self._model_class(**{col: getattr(self, col) for col in self._cols})) class MechanismDriverContext(object): """MechanismDriver context base class.""" def __init__(self, plugin, plugin_context): self._plugin = plugin # This temporarily creates a reference loop, but the # lifetime of PortContext is limited to a single # method call of the plugin. self._plugin_context = plugin_context class NetworkContext(MechanismDriverContext, api.NetworkContext): def __init__(self, plugin, plugin_context, network, original_network=None, segments=None): super(NetworkContext, self).__init__(plugin, plugin_context) self._network = network self._original_network = original_network self._segments = segments_db.get_network_segments( plugin_context, network['id']) if segments is None else segments @property def current(self): return self._network @property def original(self): return self._original_network @property def network_segments(self): return self._segments class SubnetContext(MechanismDriverContext, api.SubnetContext): def __init__(self, plugin, plugin_context, subnet, network, original_subnet=None): super(SubnetContext, self).__init__(plugin, plugin_context) self._subnet = subnet self._original_subnet = original_subnet self._network_context = NetworkContext(plugin, plugin_context, network) if network else None @property def current(self): return self._subnet @property def original(self): return self._original_subnet @property def network(self): if self._network_context is None: network = self._plugin.get_network( self._plugin_context, self.current['network_id']) self._network_context = NetworkContext( self._plugin, self._plugin_context, network) return self._network_context class PortContext(MechanismDriverContext, api.PortContext): def __init__(self, plugin, plugin_context, port, network, binding, binding_levels, original_port=None): super(PortContext, self).__init__(plugin, plugin_context) self._port = port self._original_port = original_port if isinstance(network, NetworkContext): self._network_context = network else: self._network_context = NetworkContext( plugin, plugin_context, network) if network else None # NOTE(kevinbenton): InstanceSnapshot can go away once we are working # with OVO objects instead of native SQLA objects. self._binding = InstanceSnapshot(binding) self._binding_levels = [InstanceSnapshot(l) for l in (binding_levels or [])] self._segments_to_bind = None self._new_bound_segment = None self._next_segments_to_bind = None if original_port: self._original_vif_type = binding.vif_type self._original_vif_details = self._plugin._get_vif_details(binding) self._original_binding_levels = self._binding_levels else: self._original_vif_type = None self._original_vif_details = None self._original_binding_levels = None self._new_port_status = None # The following methods are for use by the ML2 plugin and are not # part of the driver API. def _prepare_to_bind(self, segments_to_bind): self._segments_to_bind = segments_to_bind self._new_bound_segment = None self._next_segments_to_bind = None def _clear_binding_levels(self): self._binding_levels = [] def _push_binding_level(self, binding_level): self._binding_levels.append(InstanceSnapshot(binding_level)) def _pop_binding_level(self): return self._binding_levels.pop() # The following implement the abstract methods and properties of # the driver API. @property def current(self): return self._port @property def original(self): return self._original_port @property def status(self): # REVISIT(rkukura): Eliminate special DVR case as part of # resolving bug 1367391? if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE: return self._binding.status return self._port['status'] @property def original_status(self): # REVISIT(rkukura): Should return host-specific status for DVR # ports. Fix as part of resolving bug 1367391. if self._original_port: return self._original_port['status'] @property def network(self): if not self._network_context: network = self._plugin.get_network( self._plugin_context, self.current['network_id']) self._network_context = NetworkContext( self._plugin, self._plugin_context, network) return self._network_context @property def binding_levels(self): if self._binding_levels: return [{ api.BOUND_DRIVER: level.driver, api.BOUND_SEGMENT: self._expand_segment(level.segment_id) } for level in self._binding_levels] @property def original_binding_levels(self): if self._original_binding_levels: return [{ api.BOUND_DRIVER: level.driver, api.BOUND_SEGMENT: self._expand_segment(level.segment_id) } for level in self._original_binding_levels] @property def top_bound_segment(self): if self._binding_levels: return self._expand_segment(self._binding_levels[0].segment_id) @property def original_top_bound_segment(self): if self._original_binding_levels: return self._expand_segment( self._original_binding_levels[0].segment_id) @property def bottom_bound_segment(self): if self._binding_levels: return self._expand_segment(self._binding_levels[-1].segment_id) @property def original_bottom_bound_segment(self): if self._original_binding_levels: return self._expand_segment( self._original_binding_levels[-1].segment_id) def _expand_segment(self, segment_id): for s in self.network.network_segments: if s['id'] == segment_id: return s # TODO(kevinbenton): eliminate the query below. The above should # always return since the port is bound to a network segment. Leaving # in for now for minimally invasive change for back-port. segment = segments_db.get_segment_by_id(self._plugin_context, segment_id) if not segment: LOG.warning("Could not expand segment %s", segment_id) return segment @property def host(self): # REVISIT(rkukura): Eliminate special DVR case as part of # resolving bug 1367391? if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE: return self._binding.host return self._port.get(portbindings.HOST_ID) @property def original_host(self): # REVISIT(rkukura): Eliminate special DVR case as part of # resolving bug 1367391? if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE: return self._original_port and self._binding.host else: return (self._original_port and self._original_port.get(portbindings.HOST_ID)) @property def vif_type(self): return self._binding.vif_type @property def original_vif_type(self): return self._original_vif_type @property def vif_details(self): return self._plugin._get_vif_details(self._binding) @property def original_vif_details(self): return self._original_vif_details @property def segments_to_bind(self): return self._segments_to_bind def host_agents(self, agent_type): return self._plugin.get_agents(self._plugin_context, filters={'agent_type': [agent_type], 'host': [self._binding.host]}) def set_binding(self, segment_id, vif_type, vif_details, status=None): # TODO(rkukura) Verify binding allowed, segment in network self._new_bound_segment = segment_id self._binding.vif_type = vif_type self._binding.vif_details = jsonutils.dumps(vif_details) self._new_port_status = status def continue_binding(self, segment_id, next_segments_to_bind): # TODO(rkukura) Verify binding allowed, segment in network self._new_bound_segment = segment_id self._next_segments_to_bind = next_segments_to_bind def allocate_dynamic_segment(self, segment): network_id = self._network_context.current['id'] return self._plugin.type_manager.allocate_dynamic_segment( self._plugin_context, network_id, segment) def release_dynamic_segment(self, segment_id): return self._plugin.type_manager.release_dynamic_segment( self._plugin_context, segment_id) neutron-12.1.1/neutron/plugins/ml2/plugin.py0000664000175000017500000027725713553660047021062 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from eventlet import greenthread from neutron_lib.agent import constants as agent_consts from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef from neutron_lib.api.definitions import availability_zone as az_def from neutron_lib.api.definitions import extra_dhcp_opt as edo_ext from neutron_lib.api.definitions import network as net_def from neutron_lib.api.definitions import network_mtu_writable as mtuw_apidef from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import port_security as psec from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import subnet as subnet_def from neutron_lib.api import validators from neutron_lib.api.validators import availability_zone as az_validator from neutron_lib.callbacks import events from neutron_lib.callbacks import exceptions from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as const from neutron_lib import exceptions as exc from neutron_lib.exceptions import allowedaddresspairs as addr_exc from neutron_lib.exceptions import port_security as psec_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from neutron_lib.plugins.ml2 import api from neutron_lib.services.qos import constants as qos_consts from oslo_config import cfg from oslo_db import exception as os_db_exception from oslo_log import helpers as log_helpers from oslo_log import log from oslo_serialization import jsonutils from oslo_utils import excutils from oslo_utils import importutils from oslo_utils import uuidutils import sqlalchemy from sqlalchemy import or_ from sqlalchemy.orm import exc as sa_exc from neutron._i18n import _ from neutron.agent import securitygroups_rpc as sg_rpc from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api from neutron.api.rpc.handlers import dhcp_rpc from neutron.api.rpc.handlers import dvr_rpc from neutron.api.rpc.handlers import metadata_rpc from neutron.api.rpc.handlers import resources_rpc from neutron.api.rpc.handlers import securitygroups_rpc from neutron.common import constants as n_const from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.common import utils from neutron.db import _model_query as model_query from neutron.db import _resource_extend as resource_extend from neutron.db import _utils as db_utils from neutron.db import address_scope_db from neutron.db import agents_db from neutron.db import agentschedulers_db from neutron.db import allowedaddresspairs_db as addr_pair_db from neutron.db import api as db_api from neutron.db import db_base_plugin_v2 from neutron.db import dvr_mac_db from neutron.db import external_net_db from neutron.db import extradhcpopt_db from neutron.db.models import securitygroup as sg_models from neutron.db import models_v2 from neutron.db import provisioning_blocks from neutron.db.quota import driver # noqa from neutron.db import securitygroups_rpc_base as sg_db_rpc from neutron.db import segments_db from neutron.db import subnet_service_type_db_models as service_type_db from neutron.db import vlantransparent_db from neutron.extensions import providernet as provider from neutron.extensions import vlantransparent from neutron.objects import ports as ports_obj from neutron.plugins.common import utils as p_utils from neutron.plugins.ml2.common import exceptions as ml2_exc from neutron.plugins.ml2 import db from neutron.plugins.ml2 import driver_context from neutron.plugins.ml2.extensions import qos as qos_ext from neutron.plugins.ml2 import managers from neutron.plugins.ml2 import models from neutron.plugins.ml2 import ovo_rpc from neutron.plugins.ml2 import rpc from neutron.quota import resource_registry from neutron.services.segments import plugin as segments_plugin LOG = log.getLogger(__name__) MAX_BIND_TRIES = 10 SERVICE_PLUGINS_REQUIRED_DRIVERS = { 'qos': [qos_ext.QOS_EXT_DRIVER_ALIAS] } def _ml2_port_result_filter_hook(query, filters): values = filters and filters.get(portbindings.HOST_ID, []) if not values: return query bind_criteria = models.PortBinding.host.in_(values) return query.filter(models_v2.Port.port_binding.has(bind_criteria)) @resource_extend.has_resource_extenders @registry.has_registry_receivers class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, dvr_mac_db.DVRDbMixin, external_net_db.External_net_db_mixin, sg_db_rpc.SecurityGroupServerRpcMixin, agentschedulers_db.AZDhcpAgentSchedulerDbMixin, addr_pair_db.AllowedAddressPairsMixin, vlantransparent_db.Vlantransparent_db_mixin, extradhcpopt_db.ExtraDhcpOptMixin, address_scope_db.AddressScopeDbMixin, service_type_db.SubnetServiceTypeMixin): """Implement the Neutron L2 abstractions using modules. Ml2Plugin is a Neutron plugin based on separately extensible sets of network types and mechanisms for connecting to networks of those types. The network types and mechanisms are implemented as drivers loaded via Python entry points. Networks can be made up of multiple segments (not yet fully implemented). """ # This attribute specifies whether the plugin supports or not # bulk/pagination/sorting operations. Name mangling is used in # order to ensure it is qualified by class __native_bulk_support = True __native_pagination_support = True __native_sorting_support = True # List of supported extensions _supported_extension_aliases = ["provider", "external-net", "binding", "quotas", "security-group", "agent", "dhcp_agent_scheduler", "multi-provider", "allowed-address-pairs", "extra_dhcp_opt", "subnet_allocation", "net-mtu", "net-mtu-writable", "vlan-transparent", "address-scope", "availability_zone", "network_availability_zone", "default-subnetpools", "subnet-service-types", "ip-substring-filtering"] # List of agent types for which all binding_failed ports should try to be # rebound when agent revive _rebind_on_revive_agent_types = [const.AGENT_TYPE_OVS] @property def supported_extension_aliases(self): if not hasattr(self, '_aliases'): aliases = self._supported_extension_aliases[:] aliases += self.extension_manager.extension_aliases() sg_rpc.disable_security_group_extension_by_config(aliases) vlantransparent.disable_extension_by_config(aliases) self._aliases = aliases return self._aliases def __new__(cls, *args, **kwargs): model_query.register_hook( models_v2.Port, "ml2_port_bindings", query_hook=None, filter_hook=None, result_filters=_ml2_port_result_filter_hook) return super(Ml2Plugin, cls).__new__(cls, *args, **kwargs) @resource_registry.tracked_resources( network=models_v2.Network, port=models_v2.Port, subnet=models_v2.Subnet, subnetpool=models_v2.SubnetPool, security_group=sg_models.SecurityGroup, security_group_rule=sg_models.SecurityGroupRule) def __init__(self): # First load drivers, then initialize DB, then initialize drivers self.type_manager = managers.TypeManager() self.extension_manager = managers.ExtensionManager() self.mechanism_manager = managers.MechanismManager() super(Ml2Plugin, self).__init__() self.type_manager.initialize() self.extension_manager.initialize() self.mechanism_manager.initialize() self._setup_dhcp() self._start_rpc_notifiers() self.add_agent_status_check_worker(self.agent_health_check) self.add_workers(self.mechanism_manager.get_workers()) self._verify_service_plugins_requirements() LOG.info("Modular L2 Plugin initialization complete") def _setup_rpc(self): """Initialize components to support agent communication.""" self.endpoints = [ rpc.RpcCallbacks(self.notifier, self.type_manager), securitygroups_rpc.SecurityGroupServerRpcCallback(), dvr_rpc.DVRServerRpcCallback(), dhcp_rpc.DhcpRpcCallback(), agents_db.AgentExtRpcCallback(), metadata_rpc.MetadataRpcCallback(), resources_rpc.ResourcesPullRpcCallback() ] def _setup_dhcp(self): """Initialize components to support DHCP.""" self.network_scheduler = importutils.import_object( cfg.CONF.network_scheduler_driver ) self.add_periodic_dhcp_agent_status_check() def _verify_service_plugins_requirements(self): for service_plugin in cfg.CONF.service_plugins: extension_drivers = SERVICE_PLUGINS_REQUIRED_DRIVERS.get( service_plugin, [] ) for extension_driver in extension_drivers: if extension_driver not in self.extension_manager.names(): raise ml2_exc.ExtensionDriverNotFound( driver=extension_driver, service_plugin=service_plugin ) @registry.receives(resources.PORT, [provisioning_blocks.PROVISIONING_COMPLETE]) def _port_provisioned(self, rtype, event, trigger, context, object_id, **kwargs): port_id = object_id port = db.get_port(context, port_id) if not port or not port.port_binding: LOG.debug("Port %s was deleted so its status cannot be updated.", port_id) return if port.port_binding.vif_type in (portbindings.VIF_TYPE_BINDING_FAILED, portbindings.VIF_TYPE_UNBOUND): # NOTE(kevinbenton): we hit here when a port is created without # a host ID and the dhcp agent notifies that its wiring is done LOG.debug("Port %s cannot update to ACTIVE because it " "is not bound.", port_id) return else: # port is bound, but we have to check for new provisioning blocks # one last time to detect the case where we were triggered by an # unbound port and the port became bound with new provisioning # blocks before 'get_port' was called above if provisioning_blocks.is_object_blocked(context, port_id, resources.PORT): LOG.debug("Port %s had new provisioning blocks added so it " "will not transition to active.", port_id) return if not port.admin_state_up: LOG.debug("Port %s is administratively disabled so it will " "not transition to active.", port_id) return self.update_port_status(context, port_id, const.PORT_STATUS_ACTIVE) @log_helpers.log_method_call def _start_rpc_notifiers(self): """Initialize RPC notifiers for agents.""" self.ovo_notifier = ovo_rpc.OVOServerRpcInterface() self.notifier = rpc.AgentNotifierApi(topics.AGENT) self.agent_notifiers[const.AGENT_TYPE_DHCP] = ( dhcp_rpc_agent_api.DhcpAgentNotifyAPI() ) @log_helpers.log_method_call def start_rpc_listeners(self): """Start the RPC loop to let the plugin communicate with agents.""" self._setup_rpc() self.topic = topics.PLUGIN self.conn = n_rpc.create_connection() self.conn.create_consumer(self.topic, self.endpoints, fanout=False) self.conn.create_consumer( topics.SERVER_RESOURCE_VERSIONS, [resources_rpc.ResourcesPushToServerRpcCallback()], fanout=True) # process state reports despite dedicated rpc workers self.conn.create_consumer(topics.REPORTS, [agents_db.AgentExtRpcCallback()], fanout=False) return self.conn.consume_in_threads() def start_rpc_state_reports_listener(self): self.conn_reports = n_rpc.create_connection() self.conn_reports.create_consumer(topics.REPORTS, [agents_db.AgentExtRpcCallback()], fanout=False) return self.conn_reports.consume_in_threads() def _filter_nets_provider(self, context, networks, filters): return [network for network in networks if self.type_manager.network_matches_filters(network, filters) ] def _check_mac_update_allowed(self, orig_port, port, binding): unplugged_types = (portbindings.VIF_TYPE_BINDING_FAILED, portbindings.VIF_TYPE_UNBOUND) new_mac = port.get('mac_address') mac_change = (new_mac is not None and orig_port['mac_address'] != new_mac) if (mac_change and binding.vif_type not in unplugged_types): raise exc.PortBound(port_id=orig_port['id'], vif_type=binding.vif_type, old_mac=orig_port['mac_address'], new_mac=port['mac_address']) return mac_change def _reset_mac_for_direct_physical(self, orig_port, port, binding): # when unbinding direct-physical port we need to free # physical device MAC address so that other ports may reuse it if (binding.vnic_type == portbindings.VNIC_DIRECT_PHYSICAL and port.get('device_id') == '' and port.get('device_owner') == '' and orig_port['device_id'] != ''): port['mac_address'] = self._generate_mac() return True else: return False @registry.receives(resources.AGENT, [events.AFTER_UPDATE]) def _retry_binding_revived_agents(self, resource, event, trigger, **kwargs): context = kwargs['context'] host = kwargs['host'] agent = kwargs.get('agent', {}) agent_status = agent.get('agent_status') agent_type = agent.get('agent_type') if (agent_status != agent_consts.AGENT_REVIVED or not agent.get('admin_state_up') or agent_type not in self._rebind_on_revive_agent_types): return ports = ports_obj.Port.get_ports_by_binding_type_and_host( context, portbindings.VIF_TYPE_BINDING_FAILED, host) for port in ports: if not port.binding: LOG.debug('No bindings found for port %(port_id)s ' 'on host %(host)s', {'port_id': port.id, 'host': host}) continue port_dict = self._make_port_dict(port.db_obj) network = self.get_network(context, port.network_id) try: levels = db.get_binding_levels( context, port.id, port.binding.host) # TODO(slaweq): use binding OVO instead of binding.db_obj when # ML2 plugin will switch to use Port Binding OVO everywhere mech_context = driver_context.PortContext( self, context, port_dict, network, port.binding.db_obj, levels) self._bind_port_if_needed(mech_context) except Exception as e: LOG.warning('Attempt to bind port %(port_id)s after agent ' '%(agent_type)s on host %(host)s revived failed. ' 'Error: %(error)s', {'port_id': port.id, 'agent_type': agent_type, 'host': host, 'error': e}) def _process_port_binding(self, mech_context, attrs): plugin_context = mech_context._plugin_context binding = mech_context._binding port = mech_context.current port_id = port['id'] changes = False host = const.ATTR_NOT_SPECIFIED if attrs and portbindings.HOST_ID in attrs: host = attrs.get(portbindings.HOST_ID) or '' original_host = binding.host if (validators.is_attr_set(host) and original_host != host): binding.host = host changes = True vnic_type = attrs.get(portbindings.VNIC_TYPE) if attrs else None if (validators.is_attr_set(vnic_type) and binding.vnic_type != vnic_type): binding.vnic_type = vnic_type changes = True # treat None as clear of profile. profile = None if attrs and portbindings.PROFILE in attrs: profile = attrs.get(portbindings.PROFILE) or {} if profile not in (None, const.ATTR_NOT_SPECIFIED, self._get_profile(binding)): binding.profile = jsonutils.dumps(profile) if len(binding.profile) > models.BINDING_PROFILE_LEN: msg = _("binding:profile value too large") raise exc.InvalidInput(error_message=msg) changes = True # Unbind the port if needed. if changes: binding.vif_type = portbindings.VIF_TYPE_UNBOUND binding.vif_details = '' db.clear_binding_levels(plugin_context, port_id, original_host) mech_context._clear_binding_levels() port['status'] = const.PORT_STATUS_DOWN super(Ml2Plugin, self).update_port( mech_context._plugin_context, port_id, {port_def.RESOURCE_NAME: {'status': const.PORT_STATUS_DOWN}}) if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: binding.vif_type = portbindings.VIF_TYPE_UNBOUND binding.vif_details = '' db.clear_binding_levels(plugin_context, port_id, original_host) mech_context._clear_binding_levels() binding.host = '' self._update_port_dict_binding(port, binding) binding.persist_state_to_session(plugin_context.session) return changes @db_api.retry_db_errors def _bind_port_if_needed(self, context, allow_notify=False, need_notify=False): if not context.network.network_segments: LOG.debug("Network %s has no segments, skipping binding", context.network.current['id']) return context for count in range(1, MAX_BIND_TRIES + 1): if count > 1: # yield for binding retries so that we give other threads a # chance to do their work greenthread.sleep(0) # multiple attempts shouldn't happen very often so we log each # attempt after the 1st. LOG.info("Attempt %(count)s to bind port %(port)s", {'count': count, 'port': context.current['id']}) bind_context, need_notify, try_again = self._attempt_binding( context, need_notify) if count == MAX_BIND_TRIES or not try_again: if self._should_bind_port(context): # At this point, we attempted to bind a port and reached # its final binding state. Binding either succeeded or # exhausted all attempts, thus no need to try again. # Now, the port and its binding state should be committed. context, need_notify, try_again = ( self._commit_port_binding(context, bind_context, need_notify, try_again)) else: context = bind_context if not try_again: if allow_notify and need_notify: self._notify_port_updated(context) return context LOG.error("Failed to commit binding results for %(port)s " "after %(max)s tries", {'port': context.current['id'], 'max': MAX_BIND_TRIES}) return context def _should_bind_port(self, context): return (context._binding.host and context._binding.vif_type in (portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_BINDING_FAILED)) def _attempt_binding(self, context, need_notify): try_again = False if self._should_bind_port(context): bind_context = self._bind_port(context) if bind_context.vif_type != portbindings.VIF_TYPE_BINDING_FAILED: # Binding succeeded. Suggest notifying of successful binding. need_notify = True else: # Current attempt binding failed, try to bind again. try_again = True context = bind_context return context, need_notify, try_again def _bind_port(self, orig_context): # Construct a new PortContext from the one from the previous # transaction. port = orig_context.current orig_binding = orig_context._binding new_binding = models.PortBinding( host=orig_binding.host, vnic_type=orig_binding.vnic_type, profile=orig_binding.profile, vif_type=portbindings.VIF_TYPE_UNBOUND, vif_details='' ) self._update_port_dict_binding(port, new_binding) new_context = driver_context.PortContext( self, orig_context._plugin_context, port, orig_context.network.current, new_binding, None, original_port=orig_context.original) # Attempt to bind the port and return the context with the # result. self.mechanism_manager.bind_port(new_context) return new_context def _commit_port_binding(self, orig_context, bind_context, need_notify, try_again): port_id = orig_context.current['id'] plugin_context = orig_context._plugin_context orig_binding = orig_context._binding new_binding = bind_context._binding # TODO(yamahata): revise what to be passed or new resource # like PORTBINDING should be introduced? # It would be addressed during EventPayload conversion. registry.notify(resources.PORT, events.BEFORE_UPDATE, self, context=plugin_context, port=orig_context.current, original_port=orig_context.current, orig_binding=orig_binding, new_binding=new_binding) # After we've attempted to bind the port, we begin a # transaction, get the current port state, and decide whether # to commit the binding results. with db_api.context_manager.writer.using(plugin_context): # Get the current port state and build a new PortContext # reflecting this state as original state for subsequent # mechanism driver update_port_*commit() calls. try: port_db = self._get_port(plugin_context, port_id) cur_binding = port_db.port_binding except exc.PortNotFound: port_db, cur_binding = None, None if not port_db or not cur_binding: # The port has been deleted concurrently, so just # return the unbound result from the initial # transaction that completed before the deletion. LOG.debug("Port %s has been deleted concurrently", port_id) return orig_context, False, False # Since the mechanism driver bind_port() calls must be made # outside a DB transaction locking the port state, it is # possible (but unlikely) that the port's state could change # concurrently while these calls are being made. If another # thread or process succeeds in binding the port before this # thread commits its results, the already committed results are # used. If attributes such as binding:host_id, binding:profile, # or binding:vnic_type are updated concurrently, the try_again # flag is returned to indicate that the commit was unsuccessful. oport = self._make_port_dict(port_db) port = self._make_port_dict(port_db) network = bind_context.network.current if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: # REVISIT(rkukura): The PortBinding instance from the # ml2_port_bindings table, returned as cur_binding # from port_db.port_binding above, is # currently not used for DVR distributed ports, and is # replaced here with the DistributedPortBinding instance from # the ml2_distributed_port_bindings table specific to the host # on which the distributed port is being bound. It # would be possible to optimize this code to avoid # fetching the PortBinding instance in the DVR case, # and even to avoid creating the unused entry in the # ml2_port_bindings table. But the upcoming resolution # for bug 1367391 will eliminate the # ml2_distributed_port_bindings table, use the # ml2_port_bindings table to store non-host-specific # fields for both distributed and non-distributed # ports, and introduce a new ml2_port_binding_hosts # table for the fields that need to be host-specific # in the distributed case. Since the PortBinding # instance will then be needed, it does not make sense # to optimize this code to avoid fetching it. cur_binding = db.get_distributed_port_binding_by_host( plugin_context, port_id, orig_binding.host) cur_context = driver_context.PortContext( self, plugin_context, port, network, cur_binding, None, original_port=oport) # Commit our binding results only if port has not been # successfully bound concurrently by another thread or # process and no binding inputs have been changed. commit = ((cur_binding.vif_type in [portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_BINDING_FAILED]) and orig_binding.host == cur_binding.host and orig_binding.vnic_type == cur_binding.vnic_type and orig_binding.profile == cur_binding.profile) if commit: # Update the port's binding state with our binding # results. cur_binding.vif_type = new_binding.vif_type cur_binding.vif_details = new_binding.vif_details db.clear_binding_levels(plugin_context, port_id, cur_binding.host) db.set_binding_levels(plugin_context, bind_context._binding_levels) # refresh context with a snapshot of updated state cur_context._binding = driver_context.InstanceSnapshot( cur_binding) cur_context._binding_levels = bind_context._binding_levels # Update PortContext's port dictionary to reflect the # updated binding state. self._update_port_dict_binding(port, cur_binding) # Update the port status if requested by the bound driver. if (bind_context._binding_levels and bind_context._new_port_status): port_db.status = bind_context._new_port_status port['status'] = bind_context._new_port_status # Call the mechanism driver precommit methods, commit # the results, and call the postcommit methods. self.mechanism_manager.update_port_precommit(cur_context) else: # Try to populate the PortContext with the current binding # levels so that the RPC notification won't get suppressed. # This is to avoid leaving ports stuck in a DOWN state. # For more information see bug: # https://bugs.launchpad.net/neutron/+bug/1755810 LOG.warning("Concurrent port binding operations failed on " "port %s", port_id) levels = db.get_binding_levels(plugin_context, port_id, cur_binding.host) for level in levels: cur_context._push_binding_level(level) # refresh context with a snapshot of the current binding state cur_context._binding = driver_context.InstanceSnapshot( cur_binding) if commit: # Continue, using the port state as of the transaction that # just finished, whether that transaction committed new # results or discovered concurrent port state changes. # Also, Trigger notification for successful binding commit. kwargs = { 'context': plugin_context, 'port': self._make_port_dict(port_db), # ensure latest state 'mac_address_updated': False, 'original_port': oport, } registry.notify(resources.PORT, events.AFTER_UPDATE, self, **kwargs) self.mechanism_manager.update_port_postcommit(cur_context) need_notify = True try_again = False else: try_again = True return cur_context, need_notify, try_again def _update_port_dict_binding(self, port, binding): port[portbindings.VNIC_TYPE] = binding.vnic_type port[portbindings.PROFILE] = self._get_profile(binding) if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: port[portbindings.HOST_ID] = '' port[portbindings.VIF_TYPE] = portbindings.VIF_TYPE_DISTRIBUTED port[portbindings.VIF_DETAILS] = {} else: port[portbindings.HOST_ID] = binding.host port[portbindings.VIF_TYPE] = binding.vif_type port[portbindings.VIF_DETAILS] = self._get_vif_details(binding) def _get_vif_details(self, binding): if binding.vif_details: try: return jsonutils.loads(binding.vif_details) except Exception: LOG.error("Serialized vif_details DB value '%(value)s' " "for port %(port)s is invalid", {'value': binding.vif_details, 'port': binding.port_id}) return {} def _get_profile(self, binding): if binding.profile: try: return jsonutils.loads(binding.profile) except Exception: LOG.error("Serialized profile DB value '%(value)s' for " "port %(port)s is invalid", {'value': binding.profile, 'port': binding.port_id}) return {} @staticmethod @resource_extend.extends([port_def.COLLECTION_NAME]) def _ml2_extend_port_dict_binding(port_res, port_db): plugin = directory.get_plugin() # None when called during unit tests for other plugins. if port_db.port_binding: plugin._update_port_dict_binding(port_res, port_db.port_binding) # ML2's resource extend functions allow extension drivers that extend # attributes for the resources to add those attributes to the result. @staticmethod @resource_extend.extends([net_def.COLLECTION_NAME]) def _ml2_md_extend_network_dict(result, netdb): plugin = directory.get_plugin() session = plugin._object_session_or_new_session(netdb) plugin.extension_manager.extend_network_dict(session, netdb, result) @staticmethod @resource_extend.extends([port_def.COLLECTION_NAME]) def _ml2_md_extend_port_dict(result, portdb): plugin = directory.get_plugin() session = plugin._object_session_or_new_session(portdb) plugin.extension_manager.extend_port_dict(session, portdb, result) @staticmethod @resource_extend.extends([subnet_def.COLLECTION_NAME]) def _ml2_md_extend_subnet_dict(result, subnetdb): plugin = directory.get_plugin() session = plugin._object_session_or_new_session(subnetdb) plugin.extension_manager.extend_subnet_dict(session, subnetdb, result) @staticmethod def _object_session_or_new_session(sql_obj): session = sqlalchemy.inspect(sql_obj).session if not session: session = db_api.get_reader_session() return session def _notify_port_updated(self, mech_context): port = mech_context.current segment = mech_context.bottom_bound_segment if not segment: # REVISIT(rkukura): This should notify agent to unplug port network = mech_context.network.current LOG.debug("In _notify_port_updated(), no bound segment for " "port %(port_id)s on network %(network_id)s", {'port_id': port['id'], 'network_id': network['id']}) return self.notifier.port_update(mech_context._plugin_context, port, segment[api.NETWORK_TYPE], segment[api.SEGMENTATION_ID], segment[api.PHYSICAL_NETWORK]) def _delete_objects(self, context, resource, objects): delete_op = getattr(self, 'delete_%s' % resource) for obj in objects: try: delete_op(context, obj['result']['id']) except KeyError: LOG.exception("Could not find %s to delete.", resource) except Exception: LOG.exception("Could not delete %(res)s %(id)s.", {'res': resource, 'id': obj['result']['id']}) def _create_bulk_ml2(self, resource, context, request_items): objects = [] collection = "%ss" % resource items = request_items[collection] obj_before_create = getattr(self, '_before_create_%s' % resource) for item in items: obj_before_create(context, item) with db_api.context_manager.writer.using(context): obj_creator = getattr(self, '_create_%s_db' % resource) for item in items: try: attrs = item[resource] result, mech_context = obj_creator(context, item) objects.append({'mech_context': mech_context, 'result': result, 'attributes': attrs}) except Exception as e: with excutils.save_and_reraise_exception(): utils.attach_exc_details( e, ("An exception occurred while creating " "the %(resource)s:%(item)s"), {'resource': resource, 'item': item}) postcommit_op = getattr(self, '_after_create_%s' % resource) for obj in objects: try: postcommit_op(context, obj['result'], obj['mech_context']) except Exception: with excutils.save_and_reraise_exception(): resource_ids = [res['result']['id'] for res in objects] LOG.exception("ML2 _after_create_%(res)s " "failed for %(res)s: " "'%(failed_id)s'. Deleting " "%(res)ss %(resource_ids)s", {'res': resource, 'failed_id': obj['result']['id'], 'resource_ids': ', '.join(resource_ids)}) # _after_handler will have deleted the object that threw to_delete = [o for o in objects if o != obj] self._delete_objects(context, resource, to_delete) return objects def _get_network_mtu(self, network_db, validate=True): mtus = [] try: segments = network_db['segments'] except KeyError: segments = [network_db] for s in segments: segment_type = s.get('network_type') if segment_type is None: continue try: type_driver = self.type_manager.drivers[segment_type].obj except KeyError: # NOTE(ihrachys) This can happen when type driver is not loaded # for an existing segment, or simply when the network has no # segments at the specific time this is computed. # In the former case, while it's probably an indication of # a bad setup, it's better to be safe than sorry here. Also, # several unit tests use non-existent driver types that may # trigger the exception here. if segment_type and s['segmentation_id']: LOG.warning( "Failed to determine MTU for segment " "%(segment_type)s:%(segment_id)s; network " "%(network_id)s MTU calculation may be not " "accurate", { 'segment_type': segment_type, 'segment_id': s['segmentation_id'], 'network_id': network_db['id'], } ) else: mtu = type_driver.get_mtu(s['physical_network']) # Some drivers, like 'local', may return None; the assumption # then is that for the segment type, MTU has no meaning or # unlimited, and so we should then ignore those values. if mtu: mtus.append(mtu) max_mtu = min(mtus) if mtus else p_utils.get_deployment_physnet_mtu() net_mtu = network_db.get('mtu') if validate: # validate that requested mtu conforms to allocated segments if net_mtu and max_mtu and max_mtu < net_mtu: msg = _("Requested MTU is too big, maximum is %d") % max_mtu raise exc.InvalidInput(error_message=msg) # if mtu is not set in database, use the maximum possible return net_mtu or max_mtu def _before_create_network(self, context, network): net_data = network[net_def.RESOURCE_NAME] registry.notify(resources.NETWORK, events.BEFORE_CREATE, self, context=context, network=net_data) def _create_network_db(self, context, network): net_data = network[net_def.RESOURCE_NAME] tenant_id = net_data['tenant_id'] with db_api.context_manager.writer.using(context): net_db = self.create_network_db(context, network) net_data['id'] = net_db.id self.type_manager.create_network_segments(context, net_data, tenant_id) net_db.mtu = self._get_network_mtu(net_db) result = self._make_network_dict(net_db, process_extensions=False, context=context) self.extension_manager.process_create_network( context, # NOTE(ihrachys) extensions expect no id in the dict {k: v for k, v in net_data.items() if k != 'id'}, result) self._process_l3_create(context, result, net_data) self.type_manager.extend_network_dict_provider(context, result) # Update the transparent vlan if configured if utils.is_extension_supported(self, 'vlan-transparent'): vlt = vlantransparent.get_vlan_transparent(net_data) net_db['vlan_transparent'] = vlt result['vlan_transparent'] = vlt if az_def.AZ_HINTS in net_data: self.validate_availability_zones(context, 'network', net_data[az_def.AZ_HINTS]) az_hints = az_validator.convert_az_list_to_string( net_data[az_def.AZ_HINTS]) net_db[az_def.AZ_HINTS] = az_hints result[az_def.AZ_HINTS] = az_hints registry.notify(resources.NETWORK, events.PRECOMMIT_CREATE, self, context=context, request=net_data, network=result) resource_extend.apply_funcs('networks', result, net_db) mech_context = driver_context.NetworkContext(self, context, result) self.mechanism_manager.create_network_precommit(mech_context) return result, mech_context @utils.transaction_guard @db_api.retry_if_session_inactive() def create_network(self, context, network): self._before_create_network(context, network) result, mech_context = self._create_network_db(context, network) return self._after_create_network(context, result, mech_context) def _after_create_network(self, context, result, mech_context): kwargs = {'context': context, 'network': result} registry.notify(resources.NETWORK, events.AFTER_CREATE, self, **kwargs) try: self.mechanism_manager.create_network_postcommit(mech_context) except ml2_exc.MechanismDriverError: with excutils.save_and_reraise_exception(): LOG.error("mechanism_manager.create_network_postcommit " "failed, deleting network '%s'", result['id']) self.delete_network(context, result['id']) return result @utils.transaction_guard @db_api.retry_if_session_inactive() def create_network_bulk(self, context, networks): objects = self._create_bulk_ml2( net_def.RESOURCE_NAME, context, networks) return [obj['result'] for obj in objects] @utils.transaction_guard @db_api.retry_if_session_inactive() def update_network(self, context, id, network): net_data = network[net_def.RESOURCE_NAME] provider._raise_if_updates_provider_attributes(net_data) need_network_update_notify = False with db_api.context_manager.writer.using(context): original_network = super(Ml2Plugin, self).get_network(context, id) updated_network = super(Ml2Plugin, self).update_network(context, id, network) self.extension_manager.process_update_network(context, net_data, updated_network) self._process_l3_update(context, updated_network, net_data) # ToDO(QoS): This would change once EngineFacade moves out db_network = self._get_network(context, id) # Expire the db_network in current transaction, so that the join # relationship can be updated. context.session.expire(db_network) if ( mtuw_apidef.MTU in net_data or # NOTE(ihrachys) mtu may be null for existing networks, # calculate and update it as needed; the conditional can be # removed in Queens when we populate all mtu attributes and # enforce it's not nullable on database level db_network.mtu is None): db_network.mtu = self._get_network_mtu(db_network, validate=False) # agents should now update all ports to reflect new MTU need_network_update_notify = True updated_network = self._make_network_dict( db_network, context=context) self.type_manager.extend_network_dict_provider( context, updated_network) registry.publish(resources.NETWORK, events.PRECOMMIT_UPDATE, self, payload=events.DBEventPayload( context, request_body=net_data, states=(original_network,), resource_id=id, desired_state=updated_network)) # TODO(QoS): Move out to the extension framework somehow. need_network_update_notify |= ( qos_consts.QOS_POLICY_ID in net_data and original_network[qos_consts.QOS_POLICY_ID] != updated_network[qos_consts.QOS_POLICY_ID]) mech_context = driver_context.NetworkContext( self, context, updated_network, original_network=original_network) self.mechanism_manager.update_network_precommit(mech_context) # TODO(apech) - handle errors raised by update_network, potentially # by re-calling update_network with the previous attributes. For # now the error is propagated to the caller, which is expected to # either undo/retry the operation or delete the resource. kwargs = {'context': context, 'network': updated_network, 'original_network': original_network} registry.notify(resources.NETWORK, events.AFTER_UPDATE, self, **kwargs) self.mechanism_manager.update_network_postcommit(mech_context) if need_network_update_notify: self.notifier.network_update(context, updated_network) return updated_network @db_api.retry_if_session_inactive() def get_network(self, context, id, fields=None): # NOTE(ihrachys) use writer manager to be able to update mtu # TODO(ihrachys) remove in Queens+ when mtu is not nullable with db_api.context_manager.writer.using(context): net_db = self._get_network(context, id) # NOTE(ihrachys) pre Pike networks may have null mtus; update them # in database if needed # TODO(ihrachys) remove in Queens+ when mtu is not nullable if net_db.mtu is None: net_db.mtu = self._get_network_mtu(net_db, validate=False) net_data = self._make_network_dict(net_db, context=context) self.type_manager.extend_network_dict_provider(context, net_data) return db_utils.resource_fields(net_data, fields) @db_api.retry_if_session_inactive() def get_networks(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): # NOTE(ihrachys) use writer manager to be able to update mtu # TODO(ihrachys) remove in Queens when mtu is not nullable with db_api.context_manager.writer.using(context): nets_db = super(Ml2Plugin, self)._get_networks( context, filters, None, sorts, limit, marker, page_reverse) # NOTE(ihrachys) pre Pike networks may have null mtus; update them # in database if needed # TODO(ihrachys) remove in Queens+ when mtu is not nullable net_data = [] for net in nets_db: if net.mtu is None: net.mtu = self._get_network_mtu(net, validate=False) net_data.append(self._make_network_dict(net, context=context)) self.type_manager.extend_networks_dict_provider(context, net_data) nets = self._filter_nets_provider(context, net_data, filters) return [db_utils.resource_fields(net, fields) for net in nets] def get_network_contexts(self, context, network_ids): """Return a map of network_id to NetworkContext for network_ids.""" net_filters = {'id': list(set(network_ids))} nets_by_netid = { n['id']: n for n in self.get_networks(context, filters=net_filters) } segments_by_netid = segments_db.get_networks_segments( context, list(nets_by_netid.keys())) netctxs_by_netid = { net_id: driver_context.NetworkContext( self, context, nets_by_netid[net_id], segments=segments_by_netid[net_id]) for net_id in nets_by_netid.keys() } return netctxs_by_netid @utils.transaction_guard def delete_network(self, context, id): # the only purpose of this override is to protect this from being # called inside of a transaction. return super(Ml2Plugin, self).delete_network(context, id) @registry.receives(resources.NETWORK, [events.PRECOMMIT_DELETE]) def _network_delete_precommit_handler(self, rtype, event, trigger, context, network_id, **kwargs): network = self.get_network(context, network_id) mech_context = driver_context.NetworkContext(self, context, network) # TODO(kevinbenton): move this mech context into something like # a 'delete context' so it's not polluting the real context object setattr(context, '_mech_context', mech_context) self.mechanism_manager.delete_network_precommit( mech_context) @registry.receives(resources.NETWORK, [events.AFTER_DELETE]) def _network_delete_after_delete_handler(self, rtype, event, trigger, context, network, **kwargs): try: self.mechanism_manager.delete_network_postcommit( context._mech_context) except ml2_exc.MechanismDriverError: # TODO(apech) - One or more mechanism driver failed to # delete the network. Ideally we'd notify the caller of # the fact that an error occurred. LOG.error("mechanism_manager.delete_network_postcommit" " failed") self.notifier.network_delete(context, network['id']) def _before_create_subnet(self, context, subnet): # TODO(kevinbenton): BEFORE notification should be added here pass def _create_subnet_db(self, context, subnet): with db_api.context_manager.writer.using(context): result, net_db, ipam_sub = self._create_subnet_precommit( context, subnet) # NOTE(ihrachys) pre Pike networks may have null mtus; update them # in database if needed # TODO(ihrachys) remove in Queens+ when mtu is not nullable if net_db['mtu'] is None: net_db['mtu'] = self._get_network_mtu(net_db, validate=False) self.extension_manager.process_create_subnet( context, subnet[subnet_def.RESOURCE_NAME], result) network = self._make_network_dict(net_db, context=context) self.type_manager.extend_network_dict_provider(context, network) mech_context = driver_context.SubnetContext(self, context, result, network) self.mechanism_manager.create_subnet_precommit(mech_context) return result, mech_context @utils.transaction_guard @db_api.retry_if_session_inactive() def create_subnet(self, context, subnet): self._before_create_subnet(context, subnet) result, mech_context = self._create_subnet_db(context, subnet) return self._after_create_subnet(context, result, mech_context) def _after_create_subnet(self, context, result, mech_context): # db base plugin post commit ops self._create_subnet_postcommit(context, result) kwargs = {'context': context, 'subnet': result} registry.notify(resources.SUBNET, events.AFTER_CREATE, self, **kwargs) try: self.mechanism_manager.create_subnet_postcommit(mech_context) except ml2_exc.MechanismDriverError: with excutils.save_and_reraise_exception(): LOG.error("mechanism_manager.create_subnet_postcommit " "failed, deleting subnet '%s'", result['id']) self.delete_subnet(context, result['id']) return result @utils.transaction_guard @db_api.retry_if_session_inactive() def create_subnet_bulk(self, context, subnets): objects = self._create_bulk_ml2( subnet_def.RESOURCE_NAME, context, subnets) return [obj['result'] for obj in objects] @utils.transaction_guard @db_api.retry_if_session_inactive() def update_subnet(self, context, id, subnet): with db_api.context_manager.writer.using(context): updated_subnet, original_subnet = self._update_subnet_precommit( context, id, subnet) self.extension_manager.process_update_subnet( context, subnet[subnet_def.RESOURCE_NAME], updated_subnet) updated_subnet = self.get_subnet(context, id) mech_context = driver_context.SubnetContext( self, context, updated_subnet, network=None, original_subnet=original_subnet) self.mechanism_manager.update_subnet_precommit(mech_context) self._update_subnet_postcommit(context, original_subnet, updated_subnet) # TODO(apech) - handle errors raised by update_subnet, potentially # by re-calling update_subnet with the previous attributes. For # now the error is propagated to the caller, which is expected to # either undo/retry the operation or delete the resource. self.mechanism_manager.update_subnet_postcommit(mech_context) return updated_subnet @utils.transaction_guard def delete_subnet(self, context, id): # the only purpose of this override is to protect this from being # called inside of a transaction. return super(Ml2Plugin, self).delete_subnet(context, id) @registry.receives(resources.SUBNET, [events.PRECOMMIT_DELETE]) def _subnet_delete_precommit_handler(self, rtype, event, trigger, context, subnet_id, **kwargs): subnet_obj = self._get_subnet_object(context, subnet_id) subnet = self._make_subnet_dict(subnet_obj, context=context) network = self.get_network(context, subnet['network_id']) mech_context = driver_context.SubnetContext(self, context, subnet, network) # TODO(kevinbenton): move this mech context into something like # a 'delete context' so it's not polluting the real context object setattr(context, '_mech_context', mech_context) self.mechanism_manager.delete_subnet_precommit(mech_context) @registry.receives(resources.SUBNET, [events.AFTER_DELETE]) def _subnet_delete_after_delete_handler(self, rtype, event, trigger, context, subnet, **kwargs): try: self.mechanism_manager.delete_subnet_postcommit( context._mech_context) except ml2_exc.MechanismDriverError: # TODO(apech) - One or more mechanism driver failed to # delete the subnet. Ideally we'd notify the caller of # the fact that an error occurred. LOG.error("mechanism_manager.delete_subnet_postcommit failed") # TODO(yalei) - will be simplified after security group and address pair be # converted to ext driver too. def _portsec_ext_port_create_processing(self, context, port_data, port): attrs = port[port_def.RESOURCE_NAME] port_security = ((port_data.get(psec.PORTSECURITY) is None) or port_data[psec.PORTSECURITY]) # allowed address pair checks if self._check_update_has_allowed_address_pairs(port): if not port_security: raise addr_exc.AddressPairAndPortSecurityRequired() else: # remove ATTR_NOT_SPECIFIED attrs[addr_apidef.ADDRESS_PAIRS] = [] if port_security: self._ensure_default_security_group_on_port(context, port) elif self._check_update_has_security_groups(port): raise psec_exc.PortSecurityAndIPRequiredForSecurityGroups() def _setup_dhcp_agent_provisioning_component(self, context, port): subnet_ids = [f['subnet_id'] for f in port['fixed_ips']] if (db.is_dhcp_active_on_any_subnet(context, subnet_ids) and len(self.get_dhcp_agents_hosting_networks(context, [port['network_id']]))): # the agents will tell us when the dhcp config is ready so we setup # a provisioning component to prevent the port from going ACTIVE # until a dhcp_ready_on_port notification is received. provisioning_blocks.add_provisioning_component( context, port['id'], resources.PORT, provisioning_blocks.DHCP_ENTITY) else: provisioning_blocks.remove_provisioning_component( context, port['id'], resources.PORT, provisioning_blocks.DHCP_ENTITY) def _before_create_port(self, context, port): attrs = port[port_def.RESOURCE_NAME] if not attrs.get('status'): attrs['status'] = const.PORT_STATUS_DOWN registry.notify(resources.PORT, events.BEFORE_CREATE, self, context=context, port=attrs) # NOTE(kevinbenton): triggered outside of transaction since it # emits 'AFTER' events if it creates. self._ensure_default_security_group(context, attrs['tenant_id']) def _create_port_db(self, context, port): attrs = port[port_def.RESOURCE_NAME] with db_api.context_manager.writer.using(context): dhcp_opts = attrs.get(edo_ext.EXTRADHCPOPTS, []) port_db = self.create_port_db(context, port) result = self._make_port_dict(port_db, process_extensions=False) self.extension_manager.process_create_port(context, attrs, result) self._portsec_ext_port_create_processing(context, result, port) # sgids must be got after portsec checked with security group sgids = self._get_security_groups_on_port(context, port) self._process_port_create_security_group(context, result, sgids) network = self.get_network(context, result['network_id']) binding = db.add_port_binding(context, result['id']) mech_context = driver_context.PortContext(self, context, result, network, binding, None) self._process_port_binding(mech_context, attrs) result[addr_apidef.ADDRESS_PAIRS] = ( self._process_create_allowed_address_pairs( context, result, attrs.get(addr_apidef.ADDRESS_PAIRS))) self._process_port_create_extra_dhcp_opts(context, result, dhcp_opts) kwargs = {'context': context, 'port': result} registry.notify( resources.PORT, events.PRECOMMIT_CREATE, self, **kwargs) self.mechanism_manager.create_port_precommit(mech_context) self._setup_dhcp_agent_provisioning_component(context, result) resource_extend.apply_funcs('ports', result, port_db) return result, mech_context @utils.transaction_guard @db_api.retry_if_session_inactive() def create_port(self, context, port): self._before_create_port(context, port) result, mech_context = self._create_port_db(context, port) return self._after_create_port(context, result, mech_context) def _after_create_port(self, context, result, mech_context): # notify any plugin that is interested in port create events kwargs = {'context': context, 'port': result} registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs) try: self.mechanism_manager.create_port_postcommit(mech_context) except ml2_exc.MechanismDriverError: with excutils.save_and_reraise_exception(): LOG.error("mechanism_manager.create_port_postcommit " "failed, deleting port '%s'", result['id']) self.delete_port(context, result['id'], l3_port_check=False) try: bound_context = self._bind_port_if_needed(mech_context) except ml2_exc.MechanismDriverError: with excutils.save_and_reraise_exception(): LOG.error("_bind_port_if_needed " "failed, deleting port '%s'", result['id']) self.delete_port(context, result['id'], l3_port_check=False) return bound_context.current @utils.transaction_guard @db_api.retry_if_session_inactive() def create_port_bulk(self, context, ports): objects = self._create_bulk_ml2(port_def.RESOURCE_NAME, context, ports) return [obj['result'] for obj in objects] # TODO(yalei) - will be simplified after security group and address pair be # converted to ext driver too. def _portsec_ext_port_update_processing(self, updated_port, context, port, id): port_security = ((updated_port.get(psec.PORTSECURITY) is None) or updated_port[psec.PORTSECURITY]) if port_security: return # check the address-pairs if self._check_update_has_allowed_address_pairs(port): # has address pairs in request raise addr_exc.AddressPairAndPortSecurityRequired() elif (not self._check_update_deletes_allowed_address_pairs(port)): # not a request for deleting the address-pairs updated_port[addr_apidef.ADDRESS_PAIRS] = ( self.get_allowed_address_pairs(context, id)) # check if address pairs has been in db, if address pairs could # be put in extension driver, we can refine here. if updated_port[addr_apidef.ADDRESS_PAIRS]: raise addr_exc.AddressPairAndPortSecurityRequired() # checks if security groups were updated adding/modifying # security groups, port security is set if self._check_update_has_security_groups(port): raise psec_exc.PortSecurityAndIPRequiredForSecurityGroups() elif (not self._check_update_deletes_security_groups(port)): if not utils.is_extension_supported(self, 'security-group'): return # Update did not have security groups passed in. Check # that port does not have any security groups already on it. filters = {'port_id': [id]} security_groups = ( super(Ml2Plugin, self)._get_port_security_group_bindings( context, filters) ) if security_groups: raise psec_exc.PortSecurityPortHasSecurityGroup() @utils.transaction_guard @db_api.retry_if_session_inactive() def update_port(self, context, id, port): attrs = port[port_def.RESOURCE_NAME] need_port_update_notify = False bound_mech_contexts = [] original_port = self.get_port(context, id) registry.notify(resources.PORT, events.BEFORE_UPDATE, self, context=context, port=attrs, original_port=original_port) with db_api.context_manager.writer.using(context): port_db = self._get_port(context, id) binding = port_db.port_binding if not binding: raise exc.PortNotFound(port_id=id) mac_address_updated = self._check_mac_update_allowed( port_db, attrs, binding) mac_address_updated |= self._reset_mac_for_direct_physical( port_db, attrs, binding) need_port_update_notify |= mac_address_updated original_port = self._make_port_dict(port_db) updated_port = super(Ml2Plugin, self).update_port(context, id, port) self.extension_manager.process_update_port(context, attrs, updated_port) self._portsec_ext_port_update_processing(updated_port, context, port, id) if (psec.PORTSECURITY in attrs) and ( original_port[psec.PORTSECURITY] != updated_port[psec.PORTSECURITY]): need_port_update_notify = True # TODO(QoS): Move out to the extension framework somehow. # Follow https://review.openstack.org/#/c/169223 for a solution. if (qos_consts.QOS_POLICY_ID in attrs and original_port[qos_consts.QOS_POLICY_ID] != updated_port[qos_consts.QOS_POLICY_ID]): need_port_update_notify = True if addr_apidef.ADDRESS_PAIRS in attrs: need_port_update_notify |= ( self.update_address_pairs_on_port(context, id, port, original_port, updated_port)) need_port_update_notify |= self.update_security_group_on_port( context, id, port, original_port, updated_port) network = self.get_network(context, original_port['network_id']) need_port_update_notify |= self._update_extra_dhcp_opts_on_port( context, id, port, updated_port) levels = db.get_binding_levels(context, id, binding.host) # one of the operations above may have altered the model call # _make_port_dict again to ensure latest state is reflected so mech # drivers, callback handlers, and the API caller see latest state. # We expire here to reflect changed relationships on the obj. # Repeatable read will ensure we still get the state from this # transaction in spite of concurrent updates/deletes. context.session.expire(port_db) updated_port.update(self._make_port_dict(port_db)) mech_context = driver_context.PortContext( self, context, updated_port, network, binding, levels, original_port=original_port) need_port_update_notify |= self._process_port_binding( mech_context, attrs) registry.publish( resources.PORT, events.PRECOMMIT_UPDATE, self, payload=events.DBEventPayload( context, request_body=attrs, states=(original_port,), resource_id=id, desired_state=updated_port)) # For DVR router interface ports we need to retrieve the # DVRPortbinding context instead of the normal port context. # The normal Portbinding context does not have the status # of the ports that are required by the l2pop to process the # postcommit events. # NOTE:Sometimes during the update_port call, the DVR router # interface port may not have the port binding, so we cannot # create a generic bindinglist that will address both the # DVR and non-DVR cases here. # TODO(Swami): This code need to be revisited. if port_db['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: dist_binding_list = db.get_distributed_port_bindings(context, id) for dist_binding in dist_binding_list: levels = db.get_binding_levels(context, id, dist_binding.host) dist_mech_context = driver_context.PortContext( self, context, updated_port, network, dist_binding, levels, original_port=original_port) self.mechanism_manager.update_port_precommit( dist_mech_context) bound_mech_contexts.append(dist_mech_context) else: self.mechanism_manager.update_port_precommit(mech_context) if any(updated_port[k] != original_port[k] for k in ('fixed_ips', 'mac_address')): # only add block if fixed_ips or mac_address changed self._setup_dhcp_agent_provisioning_component( context, updated_port) bound_mech_contexts.append(mech_context) # Notifications must be sent after the above transaction is complete kwargs = { 'context': context, 'port': updated_port, 'mac_address_updated': mac_address_updated, 'original_port': original_port, } registry.notify(resources.PORT, events.AFTER_UPDATE, self, **kwargs) # Note that DVR Interface ports will have bindings on # multiple hosts, and so will have multiple mech_contexts, # while other ports typically have just one. # Since bound_mech_contexts has both the DVR and non-DVR # contexts we can manage just with a single for loop. try: for mech_context in bound_mech_contexts: self.mechanism_manager.update_port_postcommit( mech_context) except ml2_exc.MechanismDriverError: LOG.error("mechanism_manager.update_port_postcommit " "failed for port %s", id) self.check_and_notify_security_group_member_changed( context, original_port, updated_port) need_port_update_notify |= self.is_security_group_member_updated( context, original_port, updated_port) if original_port['admin_state_up'] != updated_port['admin_state_up']: need_port_update_notify = True if original_port['status'] != updated_port['status']: need_port_update_notify = True # NOTE: In the case of DVR ports, the port-binding is done after # router scheduling when sync_routers is called and so this call # below may not be required for DVR routed interfaces. But still # since we don't have the mech_context for the DVR router interfaces # at certain times, we just pass the port-context and return it, so # that we don't disturb other methods that are expecting a return # value. bound_context = self._bind_port_if_needed( mech_context, allow_notify=True, need_notify=need_port_update_notify) return bound_context.current def _process_distributed_port_binding(self, mech_context, context, attrs): plugin_context = mech_context._plugin_context binding = mech_context._binding port = mech_context.current port_id = port['id'] if binding.vif_type != portbindings.VIF_TYPE_UNBOUND: binding.vif_details = '' binding.vif_type = portbindings.VIF_TYPE_UNBOUND if binding.host: db.clear_binding_levels(plugin_context, port_id, binding.host) binding.host = '' self._update_port_dict_binding(port, binding) binding.host = attrs and attrs.get(portbindings.HOST_ID) binding.router_id = attrs and attrs.get('device_id') # merge into session to reflect changes binding.persist_state_to_session(plugin_context.session) @utils.transaction_guard @db_api.retry_if_session_inactive() def update_distributed_port_binding(self, context, id, port): attrs = port[port_def.RESOURCE_NAME] host = attrs and attrs.get(portbindings.HOST_ID) host_set = validators.is_attr_set(host) if not host_set: LOG.error("No Host supplied to bind DVR Port %s", id) return binding = db.get_distributed_port_binding_by_host(context, id, host) device_id = attrs and attrs.get('device_id') router_id = binding and binding.get('router_id') update_required = (not binding or binding.vif_type == portbindings.VIF_TYPE_BINDING_FAILED or router_id != device_id) if update_required: try: with db_api.context_manager.writer.using(context): orig_port = self.get_port(context, id) if not binding: binding = db.ensure_distributed_port_binding( context, id, host, router_id=device_id) network = self.get_network(context, orig_port['network_id']) levels = db.get_binding_levels(context, id, host) mech_context = driver_context.PortContext(self, context, orig_port, network, binding, levels, original_port=orig_port) self._process_distributed_port_binding( mech_context, context, attrs) except (os_db_exception.DBReferenceError, exc.PortNotFound): LOG.debug("DVR Port %s has been deleted concurrently", id) return self._bind_port_if_needed(mech_context) def _pre_delete_port(self, context, port_id, port_check): """Do some preliminary operations before deleting the port.""" LOG.debug("Deleting port %s", port_id) try: # notify interested parties of imminent port deletion; # a failure here prevents the operation from happening kwargs = { 'context': context, 'port_id': port_id, 'port_check': port_check } registry.notify( resources.PORT, events.BEFORE_DELETE, self, **kwargs) except exceptions.CallbackFailure as e: # NOTE(armax): preserve old check's behavior if len(e.errors) == 1: raise e.errors[0].error raise exc.ServicePortInUse(port_id=port_id, reason=e) @utils.transaction_guard @db_api.retry_if_session_inactive() def delete_port(self, context, id, l3_port_check=True): self._pre_delete_port(context, id, l3_port_check) # TODO(armax): get rid of the l3 dependency in the with block router_ids = [] l3plugin = directory.get_plugin(plugin_constants.L3) with db_api.context_manager.writer.using(context): try: port_db = self._get_port(context, id) binding = port_db.port_binding except exc.PortNotFound: LOG.debug("The port '%s' was deleted", id) return port = self._make_port_dict(port_db) network = self.get_network(context, port['network_id']) bound_mech_contexts = [] kwargs = { 'context': context, 'id': id, 'network': network, 'port': port, 'port_db': port_db, 'bindings': binding, } device_owner = port['device_owner'] if device_owner == const.DEVICE_OWNER_DVR_INTERFACE: bindings = db.get_distributed_port_bindings(context, id) for bind in bindings: levels = db.get_binding_levels(context, id, bind.host) kwargs['bind'] = bind kwargs['levels'] = levels registry.notify(resources.PORT, events.PRECOMMIT_DELETE, self, **kwargs) mech_context = driver_context.PortContext( self, context, port, network, bind, levels) self.mechanism_manager.delete_port_precommit(mech_context) bound_mech_contexts.append(mech_context) else: levels = db.get_binding_levels(context, id, binding.host) kwargs['bind'] = None kwargs['levels'] = levels registry.notify(resources.PORT, events.PRECOMMIT_DELETE, self, **kwargs) mech_context = driver_context.PortContext( self, context, port, network, binding, levels) self.mechanism_manager.delete_port_precommit(mech_context) bound_mech_contexts.append(mech_context) if l3plugin: router_ids = l3plugin.disassociate_floatingips( context, id, do_notify=False) LOG.debug("Calling delete_port for %(port_id)s owned by %(owner)s", {"port_id": id, "owner": device_owner}) super(Ml2Plugin, self).delete_port(context, id) self._post_delete_port( context, port, router_ids, bound_mech_contexts) def _post_delete_port( self, context, port, router_ids, bound_mech_contexts): kwargs = { 'context': context, 'port': port, 'router_ids': router_ids, } registry.notify(resources.PORT, events.AFTER_DELETE, self, **kwargs) try: # Note that DVR Interface ports will have bindings on # multiple hosts, and so will have multiple mech_contexts, # while other ports typically have just one. for mech_context in bound_mech_contexts: self.mechanism_manager.delete_port_postcommit(mech_context) except ml2_exc.MechanismDriverError: # TODO(apech) - One or more mechanism driver failed to # delete the port. Ideally we'd notify the caller of the # fact that an error occurred. LOG.error("mechanism_manager.delete_port_postcommit failed for" " port %s", port['id']) self.notifier.port_delete(context, port['id']) @utils.transaction_guard @db_api.retry_if_session_inactive(context_var_name='plugin_context') def get_bound_port_context(self, plugin_context, port_id, host=None, cached_networks=None): # NOTE(ihrachys) use writer manager to be able to update mtu when # fetching network # TODO(ihrachys) remove in Queens+ when mtu is not nullable with db_api.context_manager.writer.using(plugin_context) as session: try: port_db = (session.query(models_v2.Port). enable_eagerloads(False). filter(models_v2.Port.id.startswith(port_id)). one()) except sa_exc.NoResultFound: LOG.info("No ports have port_id starting with %s", port_id) return except sa_exc.MultipleResultsFound: LOG.error("Multiple ports have port_id starting with %s", port_id) return port = self._make_port_dict(port_db) network = (cached_networks or {}).get(port['network_id']) if not network: network = self.get_network(plugin_context, port['network_id']) if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: binding = db.get_distributed_port_binding_by_host( plugin_context, port['id'], host) if not binding: LOG.error("Binding info for DVR port %s not found", port_id) return None levels = db.get_binding_levels(plugin_context, port_db.id, host) port_context = driver_context.PortContext( self, plugin_context, port, network, binding, levels) else: # since eager loads are disabled in port_db query # related attribute port_binding could disappear in # concurrent port deletion. # It's not an error condition. binding = port_db.port_binding if not binding: LOG.info("Binding info for port %s was not found, " "it might have been deleted already.", port_id) return levels = db.get_binding_levels(plugin_context, port_db.id, port_db.port_binding.host) port_context = driver_context.PortContext( self, plugin_context, port, network, binding, levels) return self._bind_port_if_needed(port_context) @utils.transaction_guard @db_api.retry_if_session_inactive(context_var_name='plugin_context') def get_bound_ports_contexts(self, plugin_context, dev_ids, host=None): result = {} # NOTE(ihrachys) use writer manager to be able to update mtu when # fetching network # TODO(ihrachys) remove in Queens+ when mtu is not nullable with db_api.context_manager.writer.using(plugin_context): dev_to_full_pids = db.partial_port_ids_to_full_ids( plugin_context, dev_ids) # get all port objects for IDs port_dbs_by_id = db.get_port_db_objects( plugin_context, dev_to_full_pids.values()) # get all networks for PortContext construction netctxs_by_netid = self.get_network_contexts( plugin_context, {p.network_id for p in port_dbs_by_id.values()}) for dev_id in dev_ids: port_id = dev_to_full_pids.get(dev_id) port_db = port_dbs_by_id.get(port_id) if (not port_id or not port_db or port_db.network_id not in netctxs_by_netid): result[dev_id] = None continue port = self._make_port_dict(port_db) if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: binding = db.get_distributed_port_binding_by_host( plugin_context, port['id'], host) bindlevelhost_match = host else: binding = port_db.port_binding bindlevelhost_match = binding.host if binding else None if not binding: LOG.info("Binding info for port %s was not found, " "it might have been deleted already.", port_id) result[dev_id] = None continue levels = [l for l in port_db.binding_levels if l.host == bindlevelhost_match] levels = sorted(levels, key=lambda l: l.level) network_ctx = netctxs_by_netid.get(port_db.network_id) port_context = driver_context.PortContext( self, plugin_context, port, network_ctx, binding, levels) result[dev_id] = port_context return {d: self._bind_port_if_needed(pctx) if pctx else None for d, pctx in result.items()} def update_port_status(self, context, port_id, status, host=None, network=None): """ Returns port_id (non-truncated uuid) if the port exists. Otherwise returns None. 'network' is deprecated and has no effect """ full = db.partial_port_ids_to_full_ids(context, [port_id]) if port_id not in full: return None port_id = full[port_id] return self.update_port_statuses( context, {port_id: status}, host)[port_id] @utils.transaction_guard @db_api.retry_if_session_inactive() def update_port_statuses(self, context, port_id_to_status, host=None): result = {} port_ids = port_id_to_status.keys() port_dbs_by_id = db.get_port_db_objects(context, port_ids) for port_id, status in port_id_to_status.items(): if not port_dbs_by_id.get(port_id): LOG.debug("Port %(port)s update to %(val)s by agent not found", {'port': port_id, 'val': status}) result[port_id] = None continue result[port_id] = self._safe_update_individual_port_db_status( context, port_dbs_by_id[port_id], status, host) return result def _safe_update_individual_port_db_status(self, context, port, status, host): port_id = port.id try: return self._update_individual_port_db_status( context, port, status, host) except Exception: with excutils.save_and_reraise_exception() as ectx: # don't reraise if port doesn't exist anymore ectx.reraise = bool(db.get_port(context, port_id)) def _update_individual_port_db_status(self, context, port, status, host): updated = False network = None port_id = port.id if ((port.status != status and port['device_owner'] != const.DEVICE_OWNER_DVR_INTERFACE) or port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE): attr = { 'id': port.id, portbindings.HOST_ID: host, 'status': status } registry.notify(resources.PORT, events.BEFORE_UPDATE, self, original_port=port, context=context, port=attr) with db_api.context_manager.writer.using(context): context.session.add(port) # bring port into writer session if (port.status != status and port['device_owner'] != const.DEVICE_OWNER_DVR_INTERFACE): original_port = self._make_port_dict(port) port.status = status # explicit flush before _make_port_dict to ensure extensions # listening for db events can modify the port if necessary context.session.flush() updated_port = self._make_port_dict(port) levels = db.get_binding_levels(context, port.id, port.port_binding.host) mech_context = driver_context.PortContext( self, context, updated_port, network, port.port_binding, levels, original_port=original_port) self.mechanism_manager.update_port_precommit(mech_context) updated = True elif port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: binding = db.get_distributed_port_binding_by_host( context, port['id'], host) if not binding: return if binding.status != status: binding.status = status updated = True if (updated and port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE): with db_api.context_manager.writer.using(context): port = db.get_port(context, port_id) if not port: LOG.warning("Port %s not found during update", port_id) return original_port = self._make_port_dict(port) network = network or self.get_network( context, original_port['network_id']) port.status = db.generate_distributed_port_status(context, port['id']) updated_port = self._make_port_dict(port) levels = db.get_binding_levels(context, port_id, host) mech_context = (driver_context.PortContext( self, context, updated_port, network, binding, levels, original_port=original_port)) self.mechanism_manager.update_port_precommit(mech_context) if updated: self.mechanism_manager.update_port_postcommit(mech_context) kwargs = {'context': context, 'port': mech_context.current, 'original_port': original_port} if status == const.PORT_STATUS_ACTIVE: # NOTE(kevinbenton): this kwarg was carried over from # the RPC handler that used to call this. it's not clear # who uses it so maybe it can be removed. added in commit # 3f3874717c07e2b469ea6c6fd52bcb4da7b380c7 kwargs['update_device_up'] = True registry.notify(resources.PORT, events.AFTER_UPDATE, self, **kwargs) if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: db.delete_distributed_port_binding_if_stale(context, binding) return port['id'] @db_api.retry_if_session_inactive() def port_bound_to_host(self, context, port_id, host): if not host: return port = db.get_port(context, port_id) if not port: LOG.debug("No Port match for: %s", port_id) return if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: bindings = db.get_distributed_port_bindings(context, port_id) for b in bindings: if b.host == host: return port LOG.debug("No binding found for DVR port %s", port['id']) return else: port_host = db.get_port_binding_host(context, port_id) return port if (port_host == host) else None @db_api.retry_if_session_inactive() def get_ports_from_devices(self, context, devices): port_ids_to_devices = dict( (self._device_to_port_id(context, device), device) for device in devices) port_ids = list(port_ids_to_devices.keys()) ports = db.get_ports_and_sgs(context, port_ids) for port in ports: # map back to original requested id port_id = next((port_id for port_id in port_ids if port['id'].startswith(port_id)), None) port['device'] = port_ids_to_devices.get(port_id) return ports @staticmethod def _device_to_port_id(context, device): # REVISIT(rkukura): Consider calling into MechanismDrivers to # process device names, or having MechanismDrivers supply list # of device prefixes to strip. for prefix in n_const.INTERFACE_PREFIXES: if device.startswith(prefix): return device[len(prefix):] # REVISIT(irenab): Consider calling into bound MD to # handle the get_device_details RPC if not uuidutils.is_uuid_like(device): port = db.get_port_from_device_mac(context, device) if port: return port.id return device def _get_ports_query(self, context, filters=None, *args, **kwargs): filters = filters or {} fixed_ips = filters.get('fixed_ips', {}) ip_addresses_s = fixed_ips.get('ip_address_substr') query = super(Ml2Plugin, self)._get_ports_query(context, filters, *args, **kwargs) if ip_addresses_s: substr_filter = or_(*[models_v2.Port.fixed_ips.any( models_v2.IPAllocation.ip_address.like('%%%s%%' % ip)) for ip in ip_addresses_s]) query = query.filter(substr_filter) return query def filter_hosts_with_network_access( self, context, network_id, candidate_hosts): segments = segments_db.get_network_segments(context, network_id) return self.mechanism_manager.filter_hosts_with_segment_access( context, segments, candidate_hosts, self.get_agents) def check_segment_for_agent(self, segment, agent): for mech_driver in self.mechanism_manager.ordered_mech_drivers: driver_agent_type = getattr(mech_driver.obj, 'agent_type', None) if driver_agent_type and driver_agent_type == agent['agent_type']: if mech_driver.obj.check_segment_for_agent(segment, agent): return True return False @registry.receives(resources.SEGMENT, (events.PRECOMMIT_CREATE, events.PRECOMMIT_DELETE, events.AFTER_CREATE, events.AFTER_DELETE)) def _handle_segment_change(self, rtype, event, trigger, context, segment): if (event == events.PRECOMMIT_CREATE and not isinstance(trigger, segments_plugin.Plugin)): # TODO(xiaohhui): Now, when create network, ml2 will reserve # segment and trigger this event handler. This event handler # will reserve segment again, which will lead to error as the # segment has already been reserved. This check could be removed # by unifying segment creation procedure. return network_id = segment.get('network_id') if event == events.PRECOMMIT_CREATE: updated_segment = self.type_manager.reserve_network_segment( context, segment) # The segmentation id might be from ML2 type driver, update it # in the original segment. segment[api.SEGMENTATION_ID] = updated_segment[api.SEGMENTATION_ID] elif event == events.PRECOMMIT_DELETE: self.type_manager.release_network_segment(context, segment) # change in segments could affect resulting network mtu, so let's # recalculate it network_db = self._get_network(context, network_id) network_db.mtu = self._get_network_mtu(network_db, validate=(event != events.PRECOMMIT_DELETE)) network_db.save(session=context.session) try: self._notify_mechanism_driver_for_segment_change( event, context, network_id) except ml2_exc.MechanismDriverError: with excutils.save_and_reraise_exception(): LOG.error("mechanism_manager error occurred when " "handle event %(event)s for segment " "'%(segment)s'", {'event': event, 'segment': segment['id']}) def _notify_mechanism_driver_for_segment_change(self, event, context, network_id): network_with_segments = self.get_network(context, network_id) mech_context = driver_context.NetworkContext( self, context, network_with_segments, original_network=network_with_segments) if (event == events.PRECOMMIT_CREATE or event == events.PRECOMMIT_DELETE): self.mechanism_manager.update_network_precommit(mech_context) elif event == events.AFTER_CREATE or event == events.AFTER_DELETE: self.mechanism_manager.update_network_postcommit(mech_context) neutron-12.1.1/neutron/plugins/ml2/__init__.py0000664000175000017500000000000013553660046021264 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/common/0000775000175000017500000000000013553660156020457 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/common/exceptions.py0000664000175000017500000000332013553660046023206 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Exceptions used by ML2.""" from neutron_lib import exceptions from neutron._i18n import _ class MechanismDriverError(exceptions.MultipleExceptions): """Mechanism driver call failed.""" def __init__(self, method, errors=None): # The message is not used by api, because api will unwrap # MultipleExceptions and return inner exceptions. Keep it # for backward-compatibility, in case other code use it. self.message = _("%s failed.") % method super(MechanismDriverError, self).__init__(errors or []) class ExtensionDriverError(exceptions.InvalidInput): """Extension driver call failed.""" message = _("Extension %(driver)s failed.") class ExtensionDriverNotFound(exceptions.InvalidConfigurationOption): """Required extension driver not found in ML2 config.""" message = _("Extension driver %(driver)s required for " "service plugin %(service_plugin)s not found.") class UnknownNetworkType(exceptions.NeutronException): """Network with unknown type.""" message = _("Unknown network type %(network_type)s.") neutron-12.1.1/neutron/plugins/ml2/common/__init__.py0000664000175000017500000000000013553660046022554 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/ml2/rpc.py0000664000175000017500000004675413553660047020344 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import port_security as psec from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import resources from neutron_lib import constants as n_const from neutron_lib.plugins import directory from neutron_lib.plugins.ml2 import api from neutron_lib.services.qos import constants as qos_consts from oslo_log import log import oslo_messaging from sqlalchemy.orm import exc from neutron.api.rpc.handlers import dvr_rpc from neutron.api.rpc.handlers import securitygroups_rpc as sg_rpc from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.db import l3_hamode_db from neutron.db import provisioning_blocks from neutron.plugins.ml2 import db as ml2_db from neutron.plugins.ml2.drivers import type_tunnel # REVISIT(kmestery): Allow the type and mechanism drivers to supply the # mixins and eventually remove the direct dependencies on type_tunnel. LOG = log.getLogger(__name__) class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin): # history # 1.0 Initial version (from openvswitch/linuxbridge) # 1.1 Support Security Group RPC # 1.2 Support get_devices_details_list # 1.3 get_device_details rpc signature upgrade to obtain 'host' and # return value to include fixed_ips and device_owner for # the device port # 1.4 tunnel_sync rpc signature upgrade to obtain 'host' # 1.5 Support update_device_list and # get_devices_details_list_and_failed_devices target = oslo_messaging.Target(version='1.5') def __init__(self, notifier, type_manager): self.setup_tunnel_callback_mixin(notifier, type_manager) super(RpcCallbacks, self).__init__() def _get_new_status(self, host, port_context): port = port_context.current if not host or host == port_context.host: new_status = (n_const.PORT_STATUS_BUILD if port['admin_state_up'] else n_const.PORT_STATUS_DOWN) if port['status'] != new_status: return new_status def get_device_details(self, rpc_context, **kwargs): """Agent requests device details.""" agent_id = kwargs.get('agent_id') device = kwargs.get('device') host = kwargs.get('host') # cached networks used for reducing number of network db calls # for server internal usage only cached_networks = kwargs.get('cached_networks') LOG.debug("Device %(device)s details requested by agent " "%(agent_id)s with host %(host)s", {'device': device, 'agent_id': agent_id, 'host': host}) plugin = directory.get_plugin() port_id = plugin._device_to_port_id(rpc_context, device) port_context = plugin.get_bound_port_context(rpc_context, port_id, host, cached_networks) if not port_context: LOG.debug("Device %(device)s requested by agent " "%(agent_id)s not found in database", {'device': device, 'agent_id': agent_id}) return {'device': device} port = port_context.current # caching information about networks for future use if cached_networks is not None: if port['network_id'] not in cached_networks: cached_networks[port['network_id']] = ( port_context.network.current) result = self._get_device_details(rpc_context, agent_id=agent_id, host=host, device=device, port_context=port_context) if 'network_id' in result: # success so we update status new_status = self._get_new_status(host, port_context) if new_status: plugin.update_port_status(rpc_context, port_id, new_status, host, port_context.network.current) return result def _get_device_details(self, rpc_context, agent_id, host, device, port_context): segment = port_context.bottom_bound_segment port = port_context.current if not segment: LOG.warning("Device %(device)s requested by agent " "%(agent_id)s on network %(network_id)s not " "bound, vif_type: %(vif_type)s", {'device': device, 'agent_id': agent_id, 'network_id': port['network_id'], 'vif_type': port_context.vif_type}) return {'device': device} network_qos_policy_id = port_context.network._network.get( qos_consts.QOS_POLICY_ID) entry = {'device': device, 'network_id': port['network_id'], 'port_id': port['id'], 'mac_address': port['mac_address'], 'admin_state_up': port['admin_state_up'], 'network_type': segment[api.NETWORK_TYPE], 'segmentation_id': segment[api.SEGMENTATION_ID], 'physical_network': segment[api.PHYSICAL_NETWORK], 'mtu': port_context.network._network.get('mtu'), 'fixed_ips': port['fixed_ips'], 'device_owner': port['device_owner'], 'allowed_address_pairs': port['allowed_address_pairs'], 'port_security_enabled': port.get(psec.PORTSECURITY, True), 'qos_policy_id': port.get(qos_consts.QOS_POLICY_ID), 'network_qos_policy_id': network_qos_policy_id, 'profile': port[portbindings.PROFILE]} LOG.debug("Returning: %s", entry) return entry def get_devices_details_list(self, rpc_context, **kwargs): # cached networks used for reducing number of network db calls cached_networks = {} return [ self.get_device_details( rpc_context, device=device, cached_networks=cached_networks, **kwargs ) for device in kwargs.pop('devices', []) ] def get_devices_details_list_and_failed_devices(self, rpc_context, **kwargs): devices = [] failed_devices = [] devices_to_fetch = kwargs.pop('devices', []) plugin = directory.get_plugin() host = kwargs.get('host') bound_contexts = plugin.get_bound_ports_contexts(rpc_context, devices_to_fetch, host) for device in devices_to_fetch: if not bound_contexts.get(device): # unbound bound LOG.debug("Device %(device)s requested by agent " "%(agent_id)s not found in database", {'device': device, 'agent_id': kwargs.get('agent_id')}) devices.append({'device': device}) continue try: devices.append(self._get_device_details( rpc_context, agent_id=kwargs.get('agent_id'), host=host, device=device, port_context=bound_contexts[device])) except Exception: LOG.exception("Failed to get details for device %s", device) failed_devices.append(device) new_status_map = {ctxt.current['id']: self._get_new_status(host, ctxt) for ctxt in bound_contexts.values() if ctxt} # filter out any without status changes new_status_map = {p: s for p, s in new_status_map.items() if s} try: plugin.update_port_statuses(rpc_context, new_status_map, host) except Exception: LOG.exception("Failure updating statuses, retrying all") failed_devices = devices_to_fetch devices = [] return {'devices': devices, 'failed_devices': failed_devices} def update_device_down(self, rpc_context, **kwargs): """Device no longer exists on agent.""" # TODO(garyk) - live migration and port status agent_id = kwargs.get('agent_id') device = kwargs.get('device') host = kwargs.get('host') LOG.debug("Device %(device)s no longer exists at agent " "%(agent_id)s", {'device': device, 'agent_id': agent_id}) plugin = directory.get_plugin() port_id = plugin._device_to_port_id(rpc_context, device) port_exists = True if (host and not plugin.port_bound_to_host(rpc_context, port_id, host)): LOG.debug("Device %(device)s not bound to the" " agent host %(host)s", {'device': device, 'host': host}) else: try: port_exists = bool(plugin.update_port_status( rpc_context, port_id, n_const.PORT_STATUS_DOWN, host)) except exc.StaleDataError: port_exists = False LOG.debug("delete_port and update_device_down are being " "executed concurrently. Ignoring StaleDataError.") return {'device': device, 'exists': port_exists} self.notify_l2pop_port_wiring(port_id, rpc_context, n_const.PORT_STATUS_DOWN, host) return {'device': device, 'exists': port_exists} def update_device_up(self, rpc_context, **kwargs): """Device is up on agent.""" agent_id = kwargs.get('agent_id') device = kwargs.get('device') host = kwargs.get('host') agent_restarted = kwargs.pop('agent_restarted', None) LOG.debug("Device %(device)s up at agent %(agent_id)s", {'device': device, 'agent_id': agent_id}) plugin = directory.get_plugin() port_id = plugin._device_to_port_id(rpc_context, device) port = plugin.port_bound_to_host(rpc_context, port_id, host) if host and not port: LOG.debug("Device %(device)s not bound to the" " agent host %(host)s", {'device': device, 'host': host}) # this might mean that a VM is in the process of live migration # and vif was plugged on the destination compute node; # need to notify nova explicitly port = ml2_db.get_port(rpc_context, port_id) # _device_to_port_id may have returned a truncated UUID if the # agent did not provide a full one (e.g. Linux Bridge case). if not port: LOG.debug("Port %s not found, will not notify nova.", port_id) return else: if port.device_owner.startswith( n_const.DEVICE_OWNER_COMPUTE_PREFIX): plugin.nova_notifier.notify_port_active_direct(port) return else: self.update_port_status_to_active(port, rpc_context, port_id, host) self.notify_l2pop_port_wiring(port_id, rpc_context, n_const.PORT_STATUS_ACTIVE, host, agent_restarted) def update_port_status_to_active(self, port, rpc_context, port_id, host): plugin = directory.get_plugin() if port and port['device_owner'] == n_const.DEVICE_OWNER_DVR_INTERFACE: # NOTE(kevinbenton): we have to special case DVR ports because of # the special multi-binding status update logic they have that # depends on the host plugin.update_port_status(rpc_context, port_id, n_const.PORT_STATUS_ACTIVE, host) else: # _device_to_port_id may have returned a truncated UUID if the # agent did not provide a full one (e.g. Linux Bridge case). We # need to look up the full one before calling provisioning_complete if not port: port = ml2_db.get_port(rpc_context, port_id) if not port: # port doesn't exist, no need to add a provisioning block return provisioning_blocks.provisioning_complete( rpc_context, port['id'], resources.PORT, provisioning_blocks.L2_AGENT_ENTITY) def notify_l2pop_port_wiring(self, port_id, rpc_context, status, host, agent_restarted=None): """Notify the L2pop driver that a port has been wired/unwired. The L2pop driver uses this notification to broadcast forwarding entries to other agents on the same network as the port for port_id. """ plugin = directory.get_plugin() l2pop_driver = plugin.mechanism_manager.mech_drivers.get( 'l2population') if not l2pop_driver: return port = ml2_db.get_port(rpc_context, port_id) if not port: return port_context = plugin.get_bound_port_context( rpc_context, port_id, host) if not port_context: # port deleted return # NOTE: DVR ports are already handled and updated through l2pop # and so we don't need to update it again here. But, l2pop did not # handle DVR ports while restart neutron-*-agent, we need to handle # it here. if port['device_owner'] == n_const.DEVICE_OWNER_DVR_INTERFACE: if agent_restarted is None: agent_restarted = l2pop_driver.obj.agent_restarted( port_context) if not agent_restarted: return port = port_context.current if (port['device_owner'] != n_const.DEVICE_OWNER_DVR_INTERFACE and status == n_const.PORT_STATUS_ACTIVE and port[portbindings.HOST_ID] != host and not l3_hamode_db.is_ha_router_port(rpc_context, port['device_owner'], port['device_id'])): # don't setup ACTIVE forwarding entries unless bound to this # host or if it's an HA or DVR port (which is special-cased in # the mech driver) return port_context.current['status'] = status port_context.current[portbindings.HOST_ID] = host if status == n_const.PORT_STATUS_ACTIVE: l2pop_driver.obj.update_port_up(port_context, agent_restarted) else: l2pop_driver.obj.update_port_down(port_context) def update_device_list(self, rpc_context, **kwargs): devices_up = [] failed_devices_up = [] devices_down = [] failed_devices_down = [] devices = kwargs.get('devices_up') if devices: for device in devices: try: self.update_device_up( rpc_context, device=device, **kwargs) except Exception as e: failed_devices_up.append(device) LOG.error("Failed to update device %s up: %s", device, e) else: devices_up.append(device) devices = kwargs.get('devices_down') if devices: for device in devices: try: dev = self.update_device_down( rpc_context, device=device, **kwargs) except Exception as e: failed_devices_down.append(device) LOG.error("Failed to update device %s down: %s", device, e) else: devices_down.append(dev) return {'devices_up': devices_up, 'failed_devices_up': failed_devices_up, 'devices_down': devices_down, 'failed_devices_down': failed_devices_down} class AgentNotifierApi(dvr_rpc.DVRAgentRpcApiMixin, sg_rpc.SecurityGroupAgentRpcApiMixin, type_tunnel.TunnelAgentRpcApiMixin): """Agent side of the openvswitch rpc API. API version history: 1.0 - Initial version. 1.1 - Added get_active_networks_info, create_dhcp_port, update_dhcp_port, and removed get_dhcp_port methods. 1.4 - Added network_update """ def __init__(self, topic): self.topic = topic self.topic_network_delete = topics.get_topic_name(topic, topics.NETWORK, topics.DELETE) self.topic_port_update = topics.get_topic_name(topic, topics.PORT, topics.UPDATE) self.topic_port_delete = topics.get_topic_name(topic, topics.PORT, topics.DELETE) self.topic_network_update = topics.get_topic_name(topic, topics.NETWORK, topics.UPDATE) target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def network_delete(self, context, network_id): cctxt = self.client.prepare(topic=self.topic_network_delete, fanout=True) cctxt.cast(context, 'network_delete', network_id=network_id) def port_update(self, context, port, network_type, segmentation_id, physical_network): cctxt = self.client.prepare(topic=self.topic_port_update, fanout=True) cctxt.cast(context, 'port_update', port=port, network_type=network_type, segmentation_id=segmentation_id, physical_network=physical_network) def port_delete(self, context, port_id): cctxt = self.client.prepare(topic=self.topic_port_delete, fanout=True) cctxt.cast(context, 'port_delete', port_id=port_id) def network_update(self, context, network): cctxt = self.client.prepare(topic=self.topic_network_update, fanout=True, version='1.4') cctxt.cast(context, 'network_update', network=network) neutron-12.1.1/neutron/plugins/ml2/README0000664000175000017500000000570413553660046020053 0ustar zuulzuul00000000000000The Modular Layer 2 (ML2) plugin is a framework allowing OpenStack Networking to simultaneously utilize the variety of layer 2 networking technologies found in complex real-world data centers. It supports the Open vSwitch, Linux bridge, and Hyper-V L2 agents, replacing and deprecating the monolithic plugins previously associated with those agents, and can also support hardware devices and SDN controllers. The ML2 framework is intended to greatly simplify adding support for new L2 networking technologies, requiring much less initial and ongoing effort than would be required for an additional monolithic core plugin. It is also intended to foster innovation through its organization as optional driver modules. The ML2 plugin supports all the non-vendor-specific neutron API extensions, and works with the standard neutron DHCP agent. It utilizes the service plugin interface to implement the L3 router abstraction, allowing use of either the standard neutron L3 agent or alternative L3 solutions. Additional service plugins can also be used with the ML2 core plugin. Drivers within ML2 implement separately extensible sets of network types and of mechanisms for accessing networks of those types. Multiple mechanisms can be used simultaneously to access different ports of the same virtual network. Mechanisms can utilize L2 agents via RPC and/or interact with external devices or controllers. By utilizing the multiprovidernet extension, virtual networks can be composed of multiple segments of the same or different types. Type and mechanism drivers are loaded as python entrypoints using the stevedore library. Each available network type is managed by an ML2 type driver. Type drivers maintain any needed type-specific network state, and perform provider network validation and tenant network allocation. As of the havana release, drivers for the local, flat, vlan, gre, and vxlan network types are included. Each available networking mechanism is managed by an ML2 mechanism driver. All registered mechanism drivers are called twice when networks, subnets, and ports are created, updated, or deleted. They are first called as part of the DB transaction, where they can maintain any needed driver-specific state. Once the transaction has been committed, they are called again, at which point they can interact with external devices and controllers. Mechanism drivers are also called as part of the port binding process, to determine whether the associated mechanism can provide connectivity for the network, and if so, the network segment and VIF driver to be used. The havana release includes mechanism drivers for the Open vSwitch, Linux bridge, and Hyper-V L2 agents, and for vendor switches/controllers/etc. It also includes an L2 Population mechanism driver that can help optimize tunneled virtual network traffic. For additional information regarding the ML2 plugin and its collection of type and mechanism drivers, see the OpenStack manuals and http://wiki.openstack.org/wiki/Neutron/ML2. neutron-12.1.1/neutron/plugins/common/0000775000175000017500000000000013553660156017765 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/plugins/common/utils.py0000664000175000017500000002212213553660047021475 0ustar zuulzuul00000000000000# Copyright 2013 Cisco Systems, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Common utilities and helper functions for OpenStack Networking Plugins. """ import collections import contextlib import hashlib from neutron_lib.api import attributes as lib_attrs from neutron_lib.api.definitions import network as net_def from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import subnet as subnet_def from neutron_lib import constants as n_const from neutron_lib import exceptions from oslo_config import cfg from oslo_log import log as logging from oslo_utils import encodeutils from oslo_utils import excutils import webob.exc from neutron._i18n import _ from neutron.api.v2 import attributes from neutron.common import exceptions as n_exc INTERFACE_HASH_LEN = 6 LOG = logging.getLogger(__name__) def get_deployment_physnet_mtu(): """Retrieves global physical network MTU setting. Plugins should use this function to retrieve the MTU set by the operator that is equal to or less than the MTU of their nodes' physical interfaces. Note that it is the responsibility of the plugin to deduct the value of any encapsulation overhead required before advertising it to VMs. """ return cfg.CONF.global_physnet_mtu def is_valid_vlan_tag(vlan): return n_const.MIN_VLAN_TAG <= vlan <= n_const.MAX_VLAN_TAG def is_valid_gre_id(gre_id): return n_const.MIN_GRE_ID <= gre_id <= n_const.MAX_GRE_ID def is_valid_vxlan_vni(vni): return n_const.MIN_VXLAN_VNI <= vni <= n_const.MAX_VXLAN_VNI def is_valid_geneve_vni(vni): return n_const.MIN_GENEVE_VNI <= vni <= n_const.MAX_GENEVE_VNI def verify_tunnel_range(tunnel_range, tunnel_type): """Raise an exception for invalid tunnel range or malformed range.""" mappings = {n_const.TYPE_GRE: is_valid_gre_id, n_const.TYPE_VXLAN: is_valid_vxlan_vni, n_const.TYPE_GENEVE: is_valid_geneve_vni} if tunnel_type in mappings: for ident in tunnel_range: if not mappings[tunnel_type](ident): raise exceptions.NetworkTunnelRangeError( tunnel_range=tunnel_range, error=_("%(id)s is not a valid %(type)s identifier") % {'id': ident, 'type': tunnel_type}) if tunnel_range[1] < tunnel_range[0]: raise exceptions.NetworkTunnelRangeError( tunnel_range=tunnel_range, error=_("End of tunnel range is less " "than start of tunnel range")) def raise_invalid_tag(vlan_str, vlan_range): """Raise an exception for invalid tag.""" raise n_exc.NetworkVlanRangeError( vlan_range=vlan_range, error=_("%s is not a valid VLAN tag") % vlan_str) def verify_vlan_range(vlan_range): """Raise an exception for invalid tags or malformed range.""" for vlan_tag in vlan_range: if not is_valid_vlan_tag(vlan_tag): raise_invalid_tag(str(vlan_tag), vlan_range) if vlan_range[1] < vlan_range[0]: raise n_exc.NetworkVlanRangeError( vlan_range=vlan_range, error=_("End of VLAN range is less than start of VLAN range")) def parse_network_vlan_range(network_vlan_range): """Interpret a string as network[:vlan_begin:vlan_end].""" entry = network_vlan_range.strip() if ':' in entry: if entry.count(':') != 2: raise n_exc.NetworkVlanRangeError( vlan_range=entry, error=_("Need exactly two values for VLAN range")) network, vlan_min, vlan_max = entry.split(':') if not network: raise n_exc.PhysicalNetworkNameError() try: vlan_min = int(vlan_min) except ValueError: raise_invalid_tag(vlan_min, entry) try: vlan_max = int(vlan_max) except ValueError: raise_invalid_tag(vlan_max, entry) vlan_range = (vlan_min, vlan_max) verify_vlan_range(vlan_range) return network, vlan_range else: return entry, None def parse_network_vlan_ranges(network_vlan_ranges_cfg_entries): """Interpret a list of strings as network[:vlan_begin:vlan_end] entries.""" networks = collections.OrderedDict() for entry in network_vlan_ranges_cfg_entries: network, vlan_range = parse_network_vlan_range(entry) if vlan_range: networks.setdefault(network, []).append(vlan_range) else: networks.setdefault(network, []) return networks def in_pending_status(status): return status in (n_const.PENDING_CREATE, n_const.PENDING_UPDATE, n_const.PENDING_DELETE) def _fixup_res_dict(context, attr_name, res_dict, check_allow_post=True): attr_info = attributes.RESOURCE_ATTRIBUTE_MAP[attr_name] attr_ops = lib_attrs.AttributeInfo(attr_info) try: attr_ops.populate_project_id(context, res_dict, True) lib_attrs.populate_project_info(attr_info) attr_ops.verify_attributes(res_dict) except webob.exc.HTTPBadRequest as e: # convert webob exception into ValueError as these functions are # for internal use. webob exception doesn't make sense. raise ValueError(e.detail) attr_ops.fill_post_defaults(res_dict, check_allow_post=check_allow_post) attr_ops.convert_values(res_dict) return res_dict def create_network(core_plugin, context, net, check_allow_post=True): net_data = _fixup_res_dict(context, net_def.COLLECTION_NAME, net.get('network', {}), check_allow_post=check_allow_post) return core_plugin.create_network(context, {'network': net_data}) def create_subnet(core_plugin, context, subnet, check_allow_post=True): subnet_data = _fixup_res_dict(context, subnet_def.COLLECTION_NAME, subnet.get('subnet', {}), check_allow_post=check_allow_post) return core_plugin.create_subnet(context, {'subnet': subnet_data}) def create_port(core_plugin, context, port, check_allow_post=True): port_data = _fixup_res_dict(context, port_def.COLLECTION_NAME, port.get('port', {}), check_allow_post=check_allow_post) return core_plugin.create_port(context, {'port': port_data}) @contextlib.contextmanager def delete_port_on_error(core_plugin, context, port_id): try: yield except Exception: with excutils.save_and_reraise_exception(): try: core_plugin.delete_port(context, port_id, l3_port_check=False) except exceptions.PortNotFound: LOG.debug("Port %s not found", port_id) except Exception: LOG.exception("Failed to delete port: %s", port_id) @contextlib.contextmanager def update_port_on_error(core_plugin, context, port_id, revert_value): try: yield except Exception: with excutils.save_and_reraise_exception(): try: core_plugin.update_port(context, port_id, {'port': revert_value}) except Exception: LOG.exception("Failed to update port: %s", port_id) def get_interface_name(name, prefix='', max_len=n_const.DEVICE_NAME_MAX_LEN): """Construct an interface name based on the prefix and name. The interface name can not exceed the maximum length passed in. Longer names are hashed to help ensure uniqueness. """ requested_name = prefix + name if len(requested_name) <= max_len: return requested_name # We can't just truncate because interfaces may be distinguished # by an ident at the end. A hash over the name should be unique. # Leave part of the interface name on for easier identification if (len(prefix) + INTERFACE_HASH_LEN) > max_len: raise ValueError(_("Too long prefix provided. New name would exceed " "given length for an interface name.")) namelen = max_len - len(prefix) - INTERFACE_HASH_LEN hashed_name = hashlib.sha1(encodeutils.to_utf8(name)) new_name = ('%(prefix)s%(truncated)s%(hash)s' % {'prefix': prefix, 'truncated': name[0:namelen], 'hash': hashed_name.hexdigest()[0:INTERFACE_HASH_LEN]}) LOG.info("The requested interface name %(requested_name)s exceeds the " "%(limit)d character limitation. It was shortened to " "%(new_name)s to fit.", {'requested_name': requested_name, 'limit': max_len, 'new_name': new_name}) return new_name neutron-12.1.1/neutron/plugins/common/constants.py0000664000175000017500000000253213553660047022354 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron_lib.plugins import constants as p_const # Maps extension alias to service type that # can be implemented by the core plugin. EXT_TO_SERVICE_MAPPING = { 'lbaas': p_const.LOADBALANCER, 'lbaasv2': p_const.LOADBALANCERV2, 'fwaas': p_const.FIREWALL, 'vpnaas': p_const.VPN, 'metering': p_const.METERING, 'router': constants.L3, 'qos': p_const.QOS, } # Maps default service plugins entry points to their extension aliases DEFAULT_SERVICE_PLUGINS = { 'auto_allocate': 'auto-allocated-topology', 'tag': 'tag', 'timestamp': 'timestamp', 'network_ip_availability': 'network-ip-availability', 'flavors': 'flavors', 'revisions': 'revisions', } neutron-12.1.1/neutron/plugins/common/__init__.py0000664000175000017500000000000013553660046022062 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/worker.py0000664000175000017500000000303113553660047016673 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import worker from oslo_service import loopingcall class PeriodicWorker(worker.BaseWorker): """A worker that runs a function at a fixed interval.""" def __init__(self, check_func, interval, initial_delay): super(PeriodicWorker, self).__init__(worker_process_count=0) self._check_func = check_func self._loop = None self._interval = interval self._initial_delay = initial_delay def start(self): super(PeriodicWorker, self).start() if self._loop is None: self._loop = loopingcall.FixedIntervalLoopingCall(self._check_func) self._loop.start(interval=self._interval, initial_delay=self._initial_delay) def wait(self): if self._loop is not None: self._loop.wait() def stop(self): if self._loop is not None: self._loop.stop() def reset(self): self.stop() self.wait() self.start() neutron-12.1.1/neutron/server/0000775000175000017500000000000013553660156016322 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/server/wsgi_eventlet.py0000664000175000017500000000273113553660046021554 0ustar zuulzuul00000000000000#!/usr/bin/env python # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import eventlet from oslo_log import log from neutron import service LOG = log.getLogger(__name__) def eventlet_wsgi_server(): neutron_api = service.serve_wsgi(service.NeutronApiService) start_api_and_rpc_workers(neutron_api) def start_api_and_rpc_workers(neutron_api): try: worker_launcher = service.start_all_workers() pool = eventlet.GreenPool() api_thread = pool.spawn(neutron_api.wait) plugin_workers_thread = pool.spawn(worker_launcher.wait) # api and other workers should die together. When one dies, # kill the other. api_thread.link(lambda gt: plugin_workers_thread.kill()) plugin_workers_thread.link(lambda gt: api_thread.kill()) pool.waitall() except NotImplementedError: LOG.info("RPC was already started in parent process by " "plugin.") neutron_api.wait() neutron-12.1.1/neutron/server/rpc_eventlet.py0000664000175000017500000000236513553660047021373 0ustar zuulzuul00000000000000#!/usr/bin/env python # Copyright 2011 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # If ../neutron/__init__.py exists, add ../ to Python search path, so that # it will override what happens to be installed in /usr/(local/)lib/python... from oslo_log import log from neutron import manager from neutron import service LOG = log.getLogger(__name__) def eventlet_rpc_server(): LOG.info("Eventlet based AMQP RPC server starting...") try: manager.init() rpc_workers_launcher = service.start_all_workers() except NotImplementedError: LOG.info("RPC was already started in parent process by " "plugin.") else: rpc_workers_launcher.wait() neutron-12.1.1/neutron/server/__init__.py0000664000175000017500000000325113553660047020433 0ustar zuulzuul00000000000000# Copyright 2011 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # If ../neutron/__init__.py exists, add ../ to Python search path, so that # it will override what happens to be installed in /usr/(local/)lib/python... import sys from oslo_config import cfg from neutron._i18n import _ from neutron.common import config from neutron.common import profiler def _init_configuration(): # the configuration will be read into the cfg.CONF global data structure config.init(sys.argv[1:]) config.setup_logging() config.set_config_defaults() if not cfg.CONF.config_file: sys.exit(_("ERROR: Unable to find configuration file via the default" " search paths (~/.neutron/, ~/, /etc/neutron/, /etc/) and" " the '--config-file' option!")) def boot_server(server_func): _init_configuration() try: server_func() except KeyboardInterrupt: pass except RuntimeError as e: sys.exit(_("ERROR: %s") % e) def get_application(): _init_configuration() profiler.setup('neutron-server', cfg.CONF.host) return config.load_paste_app('neutron') neutron-12.1.1/neutron/locale/0000775000175000017500000000000013553660156016253 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/ja/0000775000175000017500000000000013553660156016645 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/ja/LC_MESSAGES/0000775000175000017500000000000013553660156020432 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/ja/LC_MESSAGES/neutron.po0000664000175000017500000037552013553660047022477 0ustar zuulzuul00000000000000# Translations template for neutron. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the neutron project. # # Translators: # Akihiro Motoki , 2013 # Akihiro Motoki , 2013 # Sasuke(Kyohei MORIYAMA) <>, 2015 # NachiUeno , 2013 # NachiUeno , 2013 # Sasuke(Kyohei MORIYAMA) <>, 2015 # Tomoyuki KATO , 2013 # Akihiro Motoki , 2016. #zanata # Andreas Jaeger , 2016. #zanata # 笹原 昌美 , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: neutron VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-03-14 04:19+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-04-16 09:21+0000\n" "Last-Translator: 笹原 昌美 \n" "Language: ja\n" "Plural-Forms: nplurals=1; plural=0;\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 4.3.3\n" "Language-Team: Japanese\n" #, python-format msgid "" "\n" "Command: %(cmd)s\n" "Exit code: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" msgstr "" "\n" "コマンド: %(cmd)s\n" "終了コード: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" #, python-format msgid "" "%(branch)s HEAD file does not match migration timeline head, expected: " "%(head)s" msgstr "" "%(branch)s ã® HEAD ファイルãŒäºˆæœŸã•れる移行ã®ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ãƒ˜ãƒƒãƒ‰ã¨åˆè‡´ã—ã¾ã›" "ã‚“: %(head)s" #, python-format msgid "%(id)s is not a valid %(type)s identifier" msgstr "%(id)s ã¯ã€æœ‰åŠ¹ãª %(type)s ID ã§ã¯ã‚りã¾ã›ã‚“" #, python-format msgid "" "%(invalid_dirs)s is invalid value for sort_dirs, valid value is '%(asc)s' " "and '%(desc)s'" msgstr "" "%(invalid_dirs)s 㯠sort_dirs ã«ã¯ç„¡åйãªå€¤ã§ã™ã€‚有効ãªå€¤ã¯ '%(asc)s' ãŠã‚ˆã³ " "'%(desc)s' ã§ã™" #, python-format msgid "%(key)s prohibited for %(tunnel)s provider network" msgstr "%(key)s 㯠%(tunnel)s プロãƒã‚¤ãƒ€ãƒ¼ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã§ç¦æ­¢ã•れã¦ã„ã¾ã™" #, python-format msgid "%(name)s '%(addr)s' does not match the ip_version '%(ip_version)s'" msgstr "%(name)s '%(addr)s' ㌠ip_version '%(ip_version)s' ã¨ä¸€è‡´ã—ã¾ã›ã‚“" #, python-format msgid "%s cannot be called while in offline mode" msgstr "オフラインモードã§ã¯ã€%s を呼ã³å‡ºã›ã¾ã›ã‚“" #, python-format msgid "%s is invalid attribute for sort_keys" msgstr "%sã¯ã€sort_keys ã«ã¯ç„¡åйãªå±žæ€§ã§ã™" #, python-format msgid "%s is not a valid VLAN tag" msgstr "%s ã¯æœ‰åŠ¹ãª VLAN ã‚¿ã‚°ã§ã¯ã‚りã¾ã›ã‚“" #, python-format msgid "%s must implement get_port_from_device or get_ports_from_devices." msgstr "" "%s 㯠get_port_from_device ã¾ãŸã¯ get_ports_from_devices を実装ã—ã¦ã„ãªã‘れã°" "ãªã‚Šã¾ã›ã‚“。" #, python-format msgid "%s prohibited for VLAN provider network" msgstr "%s 㯠VLAN プロãƒã‚¤ãƒ€ãƒ¼ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã§ç¦æ­¢ã•れã¦ã„ã¾ã™" #, python-format msgid "%s prohibited for flat provider network" msgstr "%s 㯠flat プロãƒã‚¤ãƒ€ãƒ¼ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã§ç¦æ­¢ã•れã¦ã„ã¾ã™" #, python-format msgid "%s prohibited for local provider network" msgstr "%s 㯠local プロãƒã‚¤ãƒ€ãƒ¼ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã§ç¦æ­¢ã•れã¦ã„ã¾ã™" #, python-format msgid "'%s' is not a valid RBAC object type" msgstr "'%s' 㯠RBAC ã®æœ‰åйãªã‚ªãƒ–ジェクトタイプã§ã¯ã‚りã¾ã›ã‚“" #, python-format msgid "'%s' is not supported for filtering" msgstr "'%s' ã¯ãƒ•ィルタリングã§ã¯ã‚µãƒãƒ¼ãƒˆã•れã¾ã›ã‚“" #, python-format msgid "'module' object has no attribute '%s'" msgstr "'module' オブジェクトã«å±žæ€§ '%s' ãŒã‚りã¾ã›ã‚“" msgid "'port_max' is smaller than 'port_min'" msgstr "'port_max' ㌠'port_min' よりもå°ã•ããªã£ã¦ã„ã¾ã™" msgid "0 is not allowed as CIDR prefix length" msgstr "0 㯠CIDR プレフィックス長ã¨ã—ã¦è¨±å¯ã•れã¦ã„ã¾ã›ã‚“" msgid "A cidr must be specified in the absence of a subnet pool" msgstr "サブãƒãƒƒãƒˆãƒ—ールãŒãªã„å ´åˆã€cidr ã®æŒ‡å®šã¯å¿…é ˆã§ã™" msgid "" "A decimal value as Vendor's Registered Private Enterprise Number as required " "by RFC3315 DUID-EN." msgstr "RFC3315 DUID-EN ãŒå¿…è¦ã¨ã™ã‚‹ãƒ™ãƒ³ãƒ€ãƒ¼ãŒç™»éŒ²ã—ãŸç§ä¼æ¥­ç•ªå·ã® 10 進数値。" #, python-format msgid "A default external network already exists: %(net_id)s." msgstr "デフォルトã®å¤–部ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãŒæ—¢ã«å­˜åœ¨ã—ã¾ã™: %(net_id)s" msgid "" "A default subnetpool for this IP family has already been set. Only one " "default may exist per IP family" msgstr "" "ã“ã® IP ファミリーã«å¯¾ã™ã‚‹ãƒ‡ãƒ•ォルトã®ã‚µãƒ–ãƒãƒƒãƒˆãƒ—ãƒ¼ãƒ«ãŒæ—¢ã«è¨­å®šã•れã¦ã„ã¾" "ã™ã€‚IP ファミリーã”ã¨ã«ãƒ‡ãƒ•ォルト㯠1 ã¤ã—ã‹è¨­å®šã§ãã¾ã›ã‚“。" msgid "A metering driver must be specified" msgstr "計測ドライãƒãƒ¼ã‚’指定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™" msgid "API for retrieving service providers for Neutron advanced services" msgstr "Neutron 拡張サービス用ã®ã‚µãƒ¼ãƒ“スプロãƒã‚¤ãƒ€ãƒ¼ã‚’å–å¾—ã™ã‚‹ãŸã‚ã® API" msgid "Aborting periodic_sync_routers_task due to an error." msgstr "エラーã®ãŸã‚ periodic_sync_routers_task を中止ã—ã¾ã™ã€‚" msgid "Access to this resource was denied." msgstr "ã“ã®ãƒªã‚½ãƒ¼ã‚¹ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã¯æ‹’å¦ã•れã¾ã—ãŸã€‚" msgid "Action to be executed when a child process dies" msgstr "å­ãƒ—ロセスãŒç•°å¸¸çµ‚了ã—ãŸã¨ãã«å®Ÿè¡Œã•れるアクション" msgid "" "Add comments to iptables rules. Set to false to disallow the addition of " "comments to generated iptables rules that describe each rule's purpose. " "System must support the iptables comments module for addition of comments." msgstr "" "iptables ルールã«ã‚³ãƒ¡ãƒ³ãƒˆã‚’追加ã—ã¾ã™ã€‚ã“ã®å€¤ã‚’ False ã«è¨­å®šã™ã‚‹ã¨ã€ç”Ÿæˆã•れ" "ã‚‹ iptalbes ルールã«ãƒ«ãƒ¼ãƒ«ã®ç›®çš„を説明ã™ã‚‹ã‚³ãƒ¡ãƒ³ãƒˆã‚’追加ã—ãªããªã‚Šã¾ã™ã€‚シス" "テムã§ã¯ã€ã‚³ãƒ¡ãƒ³ãƒˆã®è¿½åŠ ç”¨ã« iptables コメントモジュールãŒã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã‚‹" "å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "Address not present on interface" msgstr "インターフェース上ã«å­˜åœ¨ã—ãªã„アドレス" msgid "" "Address to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "OpenFlow 接続をリッスンã™ã‚‹ã‚¢ãƒ‰ãƒ¬ã‚¹ã€‚「ãƒã‚¤ãƒ†ã‚£ãƒ–ã€ã®ãƒ‰ãƒ©ã‚¤ãƒãƒ¼ã§ã®ã¿ä½¿ç”¨ã§ã" "ã¾ã™ã€‚" msgid "Adds test attributes to core resources." msgstr "テスト属性をコアリソースã«è¿½åŠ ã—ã¾ã™ã€‚" #, python-format msgid "Agent %(id)s is not a L3 Agent or has been disabled" msgstr "" "エージェント %(id)s ã¯ã€L3 エージェントã§ãªã„ã‹ã€ä½¿ç”¨ä¸å¯ã«ãªã£ã¦ã„ã¾ã™" #, python-format msgid "Agent %(id)s is not a valid DHCP Agent or has been disabled" msgstr "" "エージェント %(id)s ã¯ã€æœ‰åŠ¹ãª DHCP エージェントã§ãªã„ã‹ã€ä½¿ç”¨ä¸å¯ã«ãªã£ã¦ã„" "ã¾ã™" msgid "" "Agent starts with admin_state_up=False when enable_new_agents=False. In the " "case, user's resources will not be scheduled automatically to the agent " "until admin changes admin_state_up to True." msgstr "" "enable_new_agents=False ã®å ´åˆã€ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã¯ admin_state_up=False ã®çŠ¶æ…‹ã§" "処ç†ã‚’é–‹å§‹ã—ã¾ã™ã€‚ã“ã®å ´åˆã€ 管ç†è€…㌠admin_state_up ã‚’ True ã«å¤‰æ›´ã™ã‚‹ã¾ã§ã€" "ユーザーã®ãƒªã‚½ãƒ¼ã‚¹ãŒè‡ªå‹•çš„ã«ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã«ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«è¨­å®šã•れるã“ã¨ã¯ã‚りã¾" "ã›ã‚“。" #, python-format msgid "Agent updated: %(payload)s" msgstr "ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆãŒæ›´æ–°ã•れã¾ã—ãŸ: %(payload)s" msgid "Allow auto scheduling networks to DHCP agent." msgstr "DHCP エージェントã«å¯¾ã™ã‚‹ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®è‡ªå‹•スケジューリングを許å¯" msgid "Allow auto scheduling of routers to L3 agent." msgstr "" "L3 エージェントã«å¯¾ã™ã‚‹ãƒ«ãƒ¼ã‚¿ãƒ¼ã®è‡ªå‹•スケジューリングを許å¯ã—ã¦ãã ã•ã„。" msgid "" "Allow overlapping IP support in Neutron. Attention: the following parameter " "MUST be set to False if Neutron is being used in conjunction with Nova " "security groups." msgstr "" "Neutron ã§é‡è¤‡ã™ã‚‹ IP ã®ã‚µãƒãƒ¼ãƒˆã‚’許容ã—ã¾ã™ã€‚注æ„: Nova ã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¼ã‚°" "ループã¨ã¨ã‚‚ã« Neutron を使用ã™ã‚‹å ´åˆã¯ã€ä»¥ä¸‹ã®ãƒ‘ラメーターを必㚠False ã«è¨­" "定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "Allow running metadata proxy." msgstr "メタデータプロキシーã®å®Ÿè¡Œã‚’許å¯ã—ã¾ã™ã€‚" msgid "Allow sending resource operation notification to DHCP agent" msgstr "DHCP エージェントã¸ã®ãƒªã‚½ãƒ¼ã‚¹æ“作通知ã®é€ä¿¡ã®è¨±å¯" msgid "Allow the creation of PTR records" msgstr "PTR レコードã®ä½œæˆã®è¨±å¯" msgid "Allow the usage of the bulk API" msgstr "Bulk API ã®ä½¿ç”¨ã‚’許å¯" msgid "Allow to perform insecure SSL (https) requests to nova metadata" msgstr "" "Nova メタデータã«å¯¾ã™ã‚‹éžã‚»ã‚­ãƒ¥ã‚¢ãª SSL (https) è¦æ±‚を実行ã™ã‚‹ã“ã¨ã‚’許å¯ã—ã¾" "ã™" msgid "" "Allows for serving metadata requests coming from a dedicated metadata access " "network whose CIDR is 169.254.169.254/16 (or larger prefix), and is " "connected to a Neutron router from which the VMs send metadata:1 request. In " "this case DHCP Option 121 will not be injected in VMs, as they will be able " "to reach 169.254.169.254 through a router. This option requires " "enable_isolated_metadata = True." msgstr "" "CIDR ㌠169.254.169.254/16 (ã¾ãŸã¯ã“れより大ããªãƒ—レフィックス) ã§ VM ㌠" "metadata:1 è¦æ±‚ã‚’é€ä¿¡ã™ã‚‹ Neuron ãƒ«ãƒ¼ã‚¿ãƒ¼ã«æŽ¥ç¶šã—ã¦ã„ã‚‹ã€ç‰¹å®šã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã‚¢ã‚¯" "セスãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‹ã‚‰å‡ºã•ã‚Œã‚‹ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿è¦æ±‚ã«å¯¾å¿œã—ã¾ã™ã€‚ã“ã®å ´åˆã€DHCP オプ" "ション 121 ã¯ãƒ«ãƒ¼ã‚¿ãƒ¼ã‚’経由ã—㦠169.254.169.254 ã«åˆ°é”ã§ãã‚‹ãŸã‚ã€VM ã«æŒ¿å…¥ã•" "れã¾ã›ã‚“。ã“ã®ã‚ªãƒ—ションを設定ã™ã‚‹ã«ã¯ enable_isolated_metadata = True ã¨è¨­å®š" "ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "An RBAC policy already exists with those values." msgstr "ã“れらã®å€¤ã«é–¢ã—㦠RBAC ãƒãƒªã‚·ãƒ¼ãŒæ—¢ã«å­˜åœ¨ã—ã¾ã™ã€‚" msgid "An identifier must be specified when updating a subnet" msgstr "サブãƒãƒƒãƒˆã‚’æ›´æ–°ã™ã‚‹éš›ã«ã¯ ID を指定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™" msgid "An interface driver must be specified" msgstr "インターフェースドライãƒãƒ¼ã‚’指定ã—ã¦ãã ã•ã„" msgid "" "An ordered list of extension driver entrypoints to be loaded from the " "neutron.ml2.extension_drivers namespace. For example: extension_drivers = " "port_security,qos" msgstr "" "neutron.ml2.extension_drivers ã®åå‰ç©ºé–“ã‹ã‚‰ãƒ­ãƒ¼ãƒ‰ã•れる拡張ドライãƒãƒ¼ã®ã‚¨ãƒ³" "トリーãƒã‚¤ãƒ³ãƒˆã‚’一定ã®é †åºã«ä¸¦ã¹ãŸãƒªã‚¹ãƒˆã€‚例: extension_drivers = " "port_security,qos" msgid "" "An ordered list of networking mechanism driver entrypoints to be loaded from " "the neutron.ml2.mechanism_drivers namespace." msgstr "" "neutron.ml2.mechanism_drivers åå‰ç©ºé–“ã‹ã‚‰ãƒ­ãƒ¼ãƒ‰ã•れるãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚­ãƒ³ã‚°ãƒ¡ã‚«ãƒ‹" "ズムドライãƒãƒ¼ã®ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆã®é †åºä»˜ãリスト。" msgid "An unknown error has occurred. Please try your request again." msgstr "䏿˜Žãªã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚è¦æ±‚ã‚’å†è©¦è¡Œã—ã¦ãã ã•ã„。" msgid "Async process didn't respawn" msgstr "éžåŒæœŸãƒ—ロセスãŒå†ç”Ÿæˆã•れã¾ã›ã‚“ã§ã—ãŸ" msgid "Authorization URL for connecting to designate in admin context" msgstr "管ç†è€…ã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã«ãŠã‘ã‚‹ designate ã¸ã®æŽ¥ç¶šç”¨èªè¨¼ URL" msgid "Automatically remove networks from offline DHCP agents." msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’オフライン DHCP エージェントã‹ã‚‰è‡ªå‹•çš„ã«å‰Šé™¤ã—ã¾ã™ã€‚" msgid "" "Automatically reschedule routers from offline L3 agents to online L3 agents." msgstr "" "ルーターã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«ã‚’オフライン L3 エージェントã‹ã‚‰ã‚ªãƒ³ãƒ©ã‚¤ãƒ³ L3 エージェ" "ントã«è‡ªå‹•çš„ã«å¤‰æ›´ã—ã¾ã™ã€‚" msgid "Availability zone of this node" msgstr "ã“ã®ãƒŽãƒ¼ãƒ‰ã®ã‚¢ãƒ™ã‚¤ãƒ©ãƒ“リティーゾーン" msgid "Available commands" msgstr "使用å¯èƒ½ãªã‚³ãƒžãƒ³ãƒ‰" msgid "Backend does not support VLAN Transparency." msgstr "ãƒãƒƒã‚¯ã‚¨ãƒ³ãƒ‰ã§ã¯ VLAN Transparency ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。" #, python-format msgid "Base MAC: %s" msgstr "ベース MAC: %s" msgid "" "Base log dir for dnsmasq logging. The log contains DHCP and DNS log " "information and is useful for debugging issues with either DHCP or DNS. If " "this section is null, disable dnsmasq log." msgstr "" "dnsmasq ã®ãƒ­ã‚°ã‚’ä¿å­˜ã™ã‚‹åŸºæœ¬ã¨ãªã‚‹ãƒ­ã‚°ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªãƒ¼ã€‚ログã«ã¯ DHCP 㨠DNS ã®" "ログ情報ãŒå«ã¾ã‚Œã€DHCP ã¾ãŸã¯ DNS ã®ãƒ‡ãƒãƒƒã‚°ã‚’行ã†ãŸã‚ã«å½¹ç«‹ã¡ã¾ã™ã€‚ã“ã®ã‚»ã‚¯" "ションã«ä½•ã®å€¤ã‚‚設定ã—ãªã„å ´åˆã¯ã€dnsmasq ログを無効化ã—ã¾ã™ã€‚" msgid "Body contains invalid data" msgstr "本文ã«ç„¡åйãªãƒ‡ãƒ¼ã‚¿ãŒå«ã¾ã‚Œã¦ã„ã¾ã™" msgid "Both network_id and router_id are None. One must be provided." msgstr "" "network_id 㨠router_id ã®ä¸¡æ–¹ãŒ None ã«ãªã£ã¦ã„ã¾ã™ã€‚ã“ã®ã†ã¡ã® 1 ã¤ã‚’æä¾›ã™" "ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" #, python-format msgid "Bridge %(bridge)s does not exist." msgstr "ブリッジ %(bridge)s ã¯å­˜åœ¨ã—ã¾ã›ã‚“。" msgid "Bulk operation not supported" msgstr "ãƒãƒ«ã‚¯æ“作ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“" msgid "CIDR to monitor" msgstr "モニター対象㮠CIDR" #, python-format msgid "Callback for %(resource_type)s not found" msgstr "%(resource_type)s ã®ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" #, python-format msgid "Callback for %(resource_type)s returned wrong resource type" msgstr "%(resource_type)s ã®ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ãŒé–“é•ã£ãŸãƒªã‚½ãƒ¼ã‚¹ã‚¿ã‚¤ãƒ—ã‚’è¿”ã—ã¾ã—ãŸ" #, python-format msgid "Cannot add floating IP to port %s that has no fixed IPv4 addresses" msgstr "" "固定㮠IPv4 アドレスをæŒãŸãªã„ãƒãƒ¼ãƒˆ %s ã« Floating IP を追加ã™ã‚‹ã“ã¨ã¯ã§ãã¾" "ã›ã‚“" #, python-format msgid "Cannot add multiple callbacks for %(resource_type)s" msgstr "%(resource_type)s ã«è¤‡æ•°ã®ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã‚’追加ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“" #, python-format msgid "Cannot allocate IPv%(req_ver)s subnet from IPv%(pool_ver)s subnet pool" msgstr "" "IPv%(pool_ver)s サブãƒãƒƒãƒˆãƒ—ールã‹ã‚‰ IPv%(req_ver)s サブãƒãƒƒãƒˆã‚’割り当ã¦ã‚‹ã“" "ã¨ã¯ã§ãã¾ã›ã‚“" msgid "Cannot allocate requested subnet from the available set of prefixes" msgstr "" "è¦æ±‚ã•れãŸã‚µãƒ–ãƒãƒƒãƒˆã‚’使用å¯èƒ½ãªãƒ—レフィックスã®ã‚»ãƒƒãƒˆã‹ã‚‰å‰²ã‚ŠæŒ¯ã‚‹ã“ã¨ãŒã§ã" "ã¾ã›ã‚“" msgid "Cannot disable enable_dhcp with ipv6 attributes set" msgstr "ipv6 属性ãŒè¨­å®šã•れãŸçŠ¶æ…‹ã§ enable_dhcp を無効ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“" #, python-format msgid "Cannot handle subnet of type %(subnet_type)s" msgstr "タイプ %(subnet_type)s ã®ã‚µãƒ–ãƒãƒƒãƒˆã‚’処ç†ã§ãã¾ã›ã‚“" msgid "Cannot have multiple IPv4 subnets on router port" msgstr "ルーターãƒãƒ¼ãƒˆã«ã¯è¤‡æ•°ã® IPv4 サブãƒãƒƒãƒˆã¯è¨­å®šã§ãã¾ã›ã‚“" #, python-format msgid "" "Cannot have multiple router ports with the same network id if both contain " "IPv6 subnets. Existing port %(p)s has IPv6 subnet(s) and network id %(nid)s" msgstr "" "åŒã˜ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ ID ã‚’æŒã¤è¤‡æ•°ã®ãƒ«ãƒ¼ã‚¿ãƒ¼ãƒãƒ¼ãƒˆã®ã„ãšã‚Œã«ã‚‚ IPv6 サブãƒãƒƒãƒˆãŒ" "å«ã¾ã‚Œã‚‹å ´åˆã€ã“れらã®ãƒãƒ¼ãƒˆã¯ä½¿ç”¨ã§ãã¾ã›ã‚“。既存ã®ãƒãƒ¼ãƒˆ %(p)s ã«ã¯ IPv6 サ" "ブãƒãƒƒãƒˆãŒã‚りã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ ID 㯠%(nid)s ã§ã™" #, python-format msgid "" "Cannot host distributed router %(router_id)s on legacy L3 agent %(agent_id)s." msgstr "" "レガシー㮠L3 エージェント %(agent_id)s ã§åˆ†æ•£ãƒ«ãƒ¼ã‚¿ãƒ¼ %(router_id)s をホスト" "ã§ãã¾ã›ã‚“。" msgid "Cannot mix IPv4 and IPv6 prefixes in a subnet pool." msgstr "" "1 ã¤ã®ã‚µãƒ–ãƒãƒƒãƒˆãƒ—ール㧠IPv4 ã®ãƒ—レフィックス㨠IPv6 ã®ãƒ—レフィックスを混用" "ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。" msgid "Cannot specify both subnet-id and port-id" msgstr "subnet-id 㨠port-id ã®ä¸¡æ–¹ã‚’指定ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“" msgid "Cannot understand JSON" msgstr "JSON を解釈ã§ãã¾ã›ã‚“" #, python-format msgid "Cannot update read-only attribute %s" msgstr "読ã¿å–り専用属性 %s ã‚’æ›´æ–°ã§ãã¾ã›ã‚“" msgid "Certificate Authority public key (CA cert) file for ssl" msgstr "ssl ã®èªè¨¼å±€å…¬é–‹éµ (CA cert) ファイル" #, python-format msgid "" "Change would make usage less than 0 for the following resources: %(unders)s." msgstr "変更ã«ã‚ˆã£ã¦ã€ä»¥ä¸‹ã®ãƒªã‚½ãƒ¼ã‚¹ã®ä½¿ç”¨é‡ãŒ 0 未満ã«ãªã‚Šã¾ã™: %(unders)s。" msgid "Check ebtables installation" msgstr "ebtables ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã®æ¤œæŸ»" msgid "Check for ARP header match support" msgstr "ARP ヘッダーマッãƒã®ã‚µãƒãƒ¼ãƒˆã®æ¤œæŸ»" msgid "Check for ARP responder support" msgstr "ARP 応答å´ã‚µãƒãƒ¼ãƒˆã®æ¤œæŸ»" msgid "Check for ICMPv6 header match support" msgstr "ICMPv6 ヘッダーマッãƒã®ã‚µãƒãƒ¼ãƒˆã®æ¤œæŸ»" msgid "Check for OVS Geneve support" msgstr "OVS Geneve サãƒãƒ¼ãƒˆã®æ¤œæŸ»" msgid "Check for OVS vxlan support" msgstr "OVS vxlan サãƒãƒ¼ãƒˆã®æ¤œæŸ»" msgid "Check for VF management support" msgstr "VF 管ç†ã‚µãƒãƒ¼ãƒˆã®æ¤œæŸ»" msgid "Check for iproute2 vxlan support" msgstr "iproute2 vxlan サãƒãƒ¼ãƒˆã®æ¤œæŸ»" msgid "Check for nova notification support" msgstr "Nova 通知サãƒãƒ¼ãƒˆã®æ¤œæŸ»" msgid "Check for patch port support" msgstr "パッãƒãƒãƒ¼ãƒˆã®ã‚µãƒãƒ¼ãƒˆã®æ¤œæŸ»" msgid "Check ip6tables installation" msgstr "ip6tables ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã®æ¤œæŸ»" msgid "Check ipset installation" msgstr "ipset ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã®æ¤œæŸ»" msgid "Check keepalived IPv6 support" msgstr "Keepalived ã® IPv6 サãƒãƒ¼ãƒˆã®æ¤œæŸ»" msgid "Check minimal dibbler version" msgstr "Dibbler ã®æœ€å°ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®æ¤œæŸ»" msgid "Check minimal dnsmasq version" msgstr "æœ€å° dnsmasq ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®æ¤œæŸ»" msgid "Check netns permission settings" msgstr "netns 許å¯è¨­å®šã®æ¤œæŸ»" msgid "Check ovs conntrack support" msgstr "ovs ã® conntrack サãƒãƒ¼ãƒˆã®æ¤œæŸ»" msgid "Check ovsdb native interface support" msgstr "ovsdb ãƒã‚¤ãƒ†ã‚£ãƒ–インターフェースã®ã‚µãƒãƒ¼ãƒˆã®æ¤œæŸ»" #, python-format msgid "" "Cidr %(subnet_cidr)s of subnet %(subnet_id)s overlaps with cidr %(cidr)s of " "subnet %(sub_id)s" msgstr "" "サブãƒãƒƒãƒˆ %(subnet_id)s ã® CIDR %(subnet_cidr)s ãŒã‚µãƒ–ãƒãƒƒãƒˆ %(sub_id)s ã® " "CIDR %(cidr)s ã¨ã‚ªãƒ¼ãƒãƒ¼ãƒ©ãƒƒãƒ—ã—ã¦ã„ã¾ã™" msgid "Cleanup resources of a specific agent type only." msgstr "特定ã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã‚¿ã‚¤ãƒ—ã®ã¿ã®ãƒªã‚½ãƒ¼ã‚¹ã‚’クリーンアップã—ã¾ã™ã€‚" msgid "Client certificate for nova metadata api server." msgstr "Nova メタデータ API サーãƒãƒ¼ç”¨ã®ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆè¨¼æ˜Žæ›¸ã€‚" msgid "" "Comma-separated list of : tuples, mapping " "network_device to the agent's node-specific list of virtual functions that " "should not be used for virtual networking. vfs_to_exclude is a semicolon-" "separated list of virtual functions to exclude from network_device. The " "network_device in the mapping should appear in the physical_device_mappings " "list." msgstr "" "コンマã§åŒºåˆ‡ã‚‰ã‚ŒãŸ ã®ãƒªã‚¹ãƒˆ: タプルã¯ã€ä»®æƒ³" "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚­ãƒ³ã‚°ã«ä½¿ç”¨ã—ã¦ã¯ãªã‚‰ãªã„仮想機能ã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã®ãƒŽãƒ¼ãƒ‰å›ºæœ‰ã®ãƒªã‚¹" "ト㫠network_device をマッピングã—ã¾ã™ã€‚vfs_to_exclude ã¯ã€ã‚»ãƒŸã‚³ãƒ­ãƒ³ã§åŒºåˆ‡ã‚‰" "れ㟠network_device ã‹ã‚‰é™¤å¤–ã•れる仮想機能ã®ãƒªã‚¹ãƒˆã§ã™ã€‚マッピングã«å«ã¾ã‚Œã‚‹ " "network_device ã¯ã€physical_device_mappings リストã«è¡¨ç¤ºã•れã¾ã™ã€‚" msgid "" "Comma-separated list of : tuples mapping " "physical network names to the agent's node-specific physical network device " "interfaces of SR-IOV physical function to be used for VLAN networks. All " "physical networks listed in network_vlan_ranges on the server should have " "mappings to appropriate interfaces on each agent." msgstr "" "コンマã§åŒºåˆ‡ã‚‰ã‚ŒãŸ ã®ãƒªã‚¹ãƒˆ: 物ç†ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯åをエージェ" "ントã®ãƒŽãƒ¼ãƒ‰å›ºæœ‰ã®ç‰©ç†ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®ãƒ‡ãƒã‚¤ã‚¹ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェース (SR-IOV ã®ç‰©ç†æ©Ÿ" "能をæŒã¤) ã«ãƒžãƒƒãƒ”ングã™ã‚‹ タプルã¯ã€VLAN ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã§ä½¿ç”¨" "ã•れã¾ã™ã€‚サーãƒãƒ¼ä¸Šã® network_vlan_ranges ã«ãƒªã‚¹ãƒˆã•れるã™ã¹ã¦ã®ç‰©ç†ãƒãƒƒãƒˆ" "ワークã¯ã€å„エージェントã®é©åˆ‡ãªã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェースã«ãƒžãƒƒãƒ”ングã•れる必è¦ãŒã‚り" "ã¾ã™ã€‚" msgid "" "Comma-separated list of : tuples " "mapping physical network names to the agent's node-specific physical network " "interfaces to be used for flat and VLAN networks. All physical networks " "listed in network_vlan_ranges on the server should have mappings to " "appropriate interfaces on each agent." msgstr "" "コンマã§åŒºåˆ‡ã‚‰ã‚ŒãŸ ã®ãƒªã‚¹ãƒˆ: 物ç†ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯åをエージェ" "ントã®ãƒŽãƒ¼ãƒ‰å›ºæœ‰ã®ç‰©ç†ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェースã«ãƒžãƒƒãƒ”ングã™ã‚‹ " " タプルã¯ã€ãƒ•ラットãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã¨ VLAN ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã§ä½¿ç”¨" "ã•れã¾ã™ã€‚サーãƒãƒ¼ä¸Šã® network_vlan_ranges ã«ãƒªã‚¹ãƒˆã•れるã™ã¹ã¦ã®ç‰©ç†ãƒãƒƒãƒˆ" "ワークã¯ã€å„エージェントã®é©åˆ‡ãªã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェースã¨ãƒžãƒƒãƒ”ングã•れる必è¦ãŒã‚り" "ã¾ã™ã€‚" msgid "" "Comma-separated list of : tuples enumerating ranges of GRE " "tunnel IDs that are available for tenant network allocation" msgstr "" "テナントãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®å‰²ã‚Šå½“ã¦ã«ä½¿ç”¨å¯èƒ½ãª GRE トンãƒãƒ« ID ã®ç¯„囲を列挙ã™ã‚‹ " ": タプルã®ã‚³ãƒ³ãƒžåŒºåˆ‡ã‚Šãƒªã‚¹ãƒˆ" msgid "" "Comma-separated list of : tuples enumerating ranges of " "Geneve VNI IDs that are available for tenant network allocation" msgstr "" "テナントãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®å‰²ã‚Šå½“ã¦ã«ä½¿ç”¨å¯èƒ½ãª Geneve VNI ID ã®ç¯„囲をエミュレート" "ã™ã‚‹ã€ã‚³ãƒ³ãƒžã§åŒºåˆ‡ã£ãŸ : タプルã®ãƒªã‚¹ãƒˆã€‚" msgid "" "Comma-separated list of : tuples enumerating ranges of " "VXLAN VNI IDs that are available for tenant network allocation" msgstr "" "テナントãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®å‰²ã‚Šå½“ã¦ã«ä½¿ç”¨å¯èƒ½ãª VXLAN VNI ID ã®ç¯„囲を列挙ã™ã‚‹ " ": タプルã®ã‚³ãƒ³ãƒžåŒºåˆ‡ã‚Šãƒªã‚¹ãƒˆ" msgid "" "Comma-separated list of the DNS servers which will be used as forwarders." msgstr "フォワーダーã¨ã—ã¦ä½¿ç”¨ã•れる DNS サーãƒãƒ¼ã®ã‚«ãƒ³ãƒžåŒºåˆ‡ã‚Šã®ãƒªã‚¹ãƒˆã€‚" msgid "Command to execute" msgstr "実行ã™ã‚‹ã‚³ãƒžãƒ³ãƒ‰" msgid "Config file for interface driver (You may also use l3_agent.ini)" msgstr "" "インターフェースドライãƒãƒ¼ã®æ§‹æˆãƒ•ァイル (l3_agent.ini を使用ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾" "ã™)" #, python-format msgid "Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s" msgstr "CIDR %(cidr)s ã¨ã‚¤ãƒ¼ã‚µãƒãƒƒãƒˆã‚¿ã‚¤ãƒ—値 %(ethertype)s ãŒç«¶åˆã—ã¦ã„ã¾ã™" msgid "" "Controls whether the neutron security group API is enabled in the server. It " "should be false when using no security groups or using the nova security " "group API." msgstr "" "Neutron セキュリティーグループ API をサーãƒãƒ¼ã§æœ‰åŠ¹åŒ–ã™ã‚‹ã‹ã©ã†ã‹ã‚’制御ã—ã¾" "ã™ã€‚セキュリティーグループを使用ã—ãªã„å ´åˆã€ã¾ãŸã¯ Nova セキュリティーグルー" "プ API を使用ã™ã‚‹å ´åˆã«ã¯ã€False ã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" #, python-format msgid "Could not bind to %(host)s:%(port)s after trying for %(time)d seconds" msgstr "" "%(time)d 秒間試行ã—ã¾ã—ãŸãŒ %(host)s:%(port)s ã«ãƒã‚¤ãƒ³ãƒ‰ã§ãã¾ã›ã‚“ã§ã—ãŸ" msgid "Could not deserialize data" msgstr "シリアライズã•れãŸãƒ‡ãƒ¼ã‚¿ã‚’復元ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ" #, python-format msgid "" "Current gateway ip %(ip_address)s already in use by port %(port_id)s. Unable " "to update." msgstr "" "ç¾åœ¨ã®ã‚²ãƒ¼ãƒˆã‚¦ã‚§ã‚¤ IP %(ip_address)s ã¯ãƒãƒ¼ãƒˆ %(port_id)s ã«ã‚ˆã£ã¦æ—¢ã«ä½¿ç”¨ã•" "れã¦ã„ã¾ã™ã€‚æ›´æ–°ã§ãã¾ã›ã‚“。" msgid "" "DHCP lease duration (in seconds). Use -1 to tell dnsmasq to use infinite " "lease times." msgstr "" "DHCP リース期間 (ç§’)。dnsmasq ã«ç„¡åˆ¶é™ã®ãƒªãƒ¼ã‚¹æ™‚é–“ã®ä½¿ç”¨ã‚’指示ã™ã‚‹ã«ã¯ã€-1 ã‚’" "使用ã—ã¾ã™ã€‚" msgid "" "DVR deployments for VXLAN/GRE/Geneve underlays require L2-pop to be enabled, " "in both the Agent and Server side." msgstr "" "VXLANã€GREã€Geneve ã®ã‚¢ãƒ³ãƒ€ãƒ¼ãƒ¬ã‚¤ã® DVR 実装環境ã§ã¯ã€ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆå´ã¨ã‚µãƒ¼" "ãƒãƒ¼å´ã§ L2-pop を有効化ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "" "Database engine for which script will be generated when using offline " "migration." msgstr "オフラインã§ç§»è¡Œã‚’行ã†éš›ã«ã‚¹ã‚¯ãƒªãƒ—トãŒç”Ÿæˆã•れるデータベースエンジン。" msgid "Default external networks must be shared to everyone." msgstr "デフォルトã®å¤–部ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã¯å…¨ãƒ¡ãƒ³ãƒãƒ¼ã«å…±æœ‰ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™" msgid "" "Default network type for external networks when no provider attributes are " "specified. By default it is None, which means that if provider attributes " "are not specified while creating external networks then they will have the " "same type as tenant networks. Allowed values for external_network_type " "config option depend on the network type values configured in type_drivers " "config option." msgstr "" "プロãƒã‚¤ãƒ€ãƒ¼ã®å±žæ€§ãŒæŒ‡å®šã•れãªã„å ´åˆã®å¤–部ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ç”¨ã®ãƒ‡ãƒ•ォルトã®ãƒãƒƒãƒˆ" "ワークタイプ。デフォルト値㯠None ã§ã™ã€‚ã“れã¯ã€å¤–部ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®ä½œæˆæ™‚ã«ãƒ—" "ロãƒã‚¤ãƒ€ãƒ¼ã®å±žæ€§ãŒæŒ‡å®šã•れãªã„å ´åˆã«ã€ãƒ†ãƒŠãƒ³ãƒˆãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã¨åŒã˜ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯" "タイプを使用ã™ã‚‹ã“ã¨ã‚’æ„味ã—ã¾ã™ã€‚external_network_type ã®è¨­å®šã‚ªãƒ—ションã¨ã—" "ã¦è¨±å®¹ã•れる値ã¯ã€type_drivers ã®è¨­å®šã‚ªãƒ—ションã§è¨­å®šã•れãŸãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¿ã‚¤ãƒ—" "値ã«ã‚ˆã£ã¦æ±ºã¾ã‚Šã¾ã™ã€‚" msgid "" "Default number of RBAC entries allowed per tenant. A negative value means " "unlimited." msgstr "" "テナントã”ã¨ã® RBAC é …ç›®ã®ãƒ‡ãƒ•ォルト数。負ã®å€¤ãŒã‚ã‚‹å ´åˆã€åˆ¶é™ãŒè¨­å®šã•れã¦ã„" "ãªã„ã“ã¨ã‚’指ã—ã¾ã™ã€‚" msgid "" "Default number of resource allowed per tenant. A negative value means " "unlimited." msgstr "" "テナント当ãŸã‚Šã«è¨±å¯ã•れるリソースã®ãƒ‡ãƒ•ォルト数。負ã®å€¤ã¯ç„¡åˆ¶é™ã‚’æ„味ã—ã¾" "ã™ã€‚" msgid "Default security group" msgstr "デフォルトセキュリティグループ" msgid "Default security group already exists." msgstr "デフォルトã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¼ã‚°ãƒ«ãƒ¼ãƒ—ãŒæ—¢ã«å­˜åœ¨ã—ã¾ã™ã€‚" msgid "" "Default value of availability zone hints. The availability zone aware " "schedulers use this when the resources availability_zone_hints is empty. " "Multiple availability zones can be specified by a comma separated string. " "This value can be empty. In this case, even if availability_zone_hints for a " "resource is empty, availability zone is considered for high availability " "while scheduling the resource." msgstr "" "アベイラビリティーゾーンã®ãƒ’ントã®ãƒ‡ãƒ•ォルト値。リソース㮠" "availability_zone_hints ãŒç©ºã®å ´åˆã€ã‚¢ãƒ™ã‚¤ãƒ©ãƒ“リティーゾーンをå‚ç…§ã™ã‚‹ã‚¹ã‚±" "ジューラーãŒã“ã®å€¤ã‚’使用ã—ã¾ã™ã€‚コンマã§åŒºåˆ‡ã‚‰ã‚ŒãŸæ–‡å­—列ã«ã‚ˆã£ã¦è¤‡æ•°ã®ã‚¢ãƒ™ã‚¤" "ラビリティーゾーンを指定ã§ãã¾ã™ã€‚ã“ã®å€¤ã¯ç©ºã§ã‚ã‚‹å ´åˆãŒã‚りã¾ã™ã€‚ãã®å ´åˆã€" "リソース㮠availability_zone_hints ãŒç©ºã§ã‚ã£ã¦ã‚‚ã€ãƒªã‚½ãƒ¼ã‚¹ã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒªãƒ³ã‚°" "を行ã†éš›ã«ã€é«˜å¯ç”¨æ€§ã‚’実ç¾ã™ã‚‹ã‚ˆã†ã‚¢ãƒ™ã‚¤ãƒ©ãƒ“ãƒªãƒ†ã‚£ãƒ¼ã‚¾ãƒ¼ãƒ³ã®æ¤œè¨ŽãŒè¡Œã‚れã¾" "ã™ã€‚" msgid "" "Define the default value of enable_snat if not provided in " "external_gateway_info." msgstr "" "enable_snat ã®ãƒ‡ãƒ•ォルト値㌠external_gateway_info ã§æä¾›ã•れã¦ã„ãªã„å ´åˆã¯ã€" "定義ã—ã¦ãã ã•ã„。" msgid "" "Defines providers for advanced services using the format: :" ":[:default]" msgstr "" "次ã®ãƒ•ォーマットを使用ã—ã¦æ‹¡å¼µã‚µãƒ¼ãƒ“スã®ãƒ—ロãƒã‚¤ãƒ€ãƒ¼ãŒå®šç¾©ã•れã¾ã™: " "::[:default]" msgid "Delete the namespace by removing all devices." msgstr "ã™ã¹ã¦ã®ãƒ‡ãƒã‚¤ã‚¹ã‚’削除ã—ã¦ã€åå‰ç©ºé–“を削除ã—ã¾ã™ã€‚" #, python-format msgid "Deleting port %s" msgstr "ãƒãƒ¼ãƒˆ %s を削除ã—ã¦ã„ã¾ã™" #, python-format msgid "Deployment error: %(reason)s." msgstr "デプロイメントエラー: %(reason)s" msgid "Destroy IPsets even if there is an iptables reference." msgstr "iptables ã®å‚ç…§ãŒã‚ã‚‹å ´åˆã§ã‚‚ IPset を破棄ã—ã¾ã™ã€‚" msgid "Destroy all IPsets." msgstr "ã™ã¹ã¦ã® IPset を破棄ã—ã¾ã™ã€‚" #, python-format msgid "Device %(dev_name)s in mapping: %(mapping)s not unique" msgstr "マッピング: %(mapping)s 内ã®ãƒ‡ãƒã‚¤ã‚¹ %(dev_name)s ãŒä¸€æ„ã§ã¯ã‚りã¾ã›ã‚“" #, python-format msgid "Device name %(dev_name)s is missing from physical_device_mappings" msgstr "デãƒã‚¤ã‚¹å %(dev_name)s ㌠physical_device_mappings ã«ã‚りã¾ã›ã‚“" msgid "Device not found" msgstr "デãƒã‚¤ã‚¹ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" #, python-format msgid "" "Distributed Virtual Router Mac Address for host %(host)s does not exist." msgstr "ホスト %(host)s ã®åˆ†æ•£ä»®æƒ³ãƒ«ãƒ¼ã‚¿ãƒ¼ MAC アドレスãŒå­˜åœ¨ã—ã¾ã›ã‚“。" msgid "Domain to use for building the hostnames" msgstr "ホストåã®ä½œæˆã«ä½¿ç”¨ã™ã‚‹ãƒ‰ãƒ¡ã‚¤ãƒ³" msgid "Downgrade no longer supported" msgstr "ダウングレードã¯ç¾åœ¨ã§ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“" #, python-format msgid "Driver %s is not unique across providers" msgstr "ドライãƒãƒ¼ %s ã¯ãƒ—ロãƒã‚¤ãƒ€ãƒ¼å…¨ä½“ã§å›ºæœ‰ã§ã¯ã‚りã¾ã›ã‚“" msgid "Driver for external DNS integration." msgstr "外部 DNS ã®é€£æºã®ãŸã‚ã®ãƒ‰ãƒ©ã‚¤ãƒãƒ¼ã€‚" msgid "Driver for security groups firewall in the L2 agent" msgstr "L2 エージェントã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¼ã‚°ãƒ«ãƒ¼ãƒ—ã®ãƒ•ァイアウォールã®ãƒ‰ãƒ©ã‚¤ãƒãƒ¼" msgid "Driver to use for scheduling network to DHCP agent" msgstr "" "DHCP エージェントã«å¯¾ã™ã‚‹ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒªãƒ³ã‚°ã«ä½¿ç”¨ã™ã‚‹ãƒ‰ãƒ©ã‚¤ãƒãƒ¼" msgid "Driver to use for scheduling router to a default L3 agent" msgstr "" "デフォルト㮠L3 エージェントã«å¯¾ã™ã‚‹ãƒ«ãƒ¼ã‚¿ãƒ¼ã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒªãƒ³ã‚°ã«ä½¿ç”¨ã™ã‚‹ãƒ‰ãƒ©" "イãƒãƒ¼" msgid "" "Driver used for ipv6 prefix delegation. This needs to be an entry point " "defined in the neutron.agent.linux.pd_drivers namespace. See setup.cfg for " "entry points included with the neutron source." msgstr "" "ipv6 ã®ãƒ—レフィックスデリゲーションを行ã†ãŸã‚ã®ãƒ‰ãƒ©ã‚¤ãƒãƒ¼ã€‚neutron.agent." "linux.pd_drivers ã®åå‰ç©ºé–“ã§å®šç¾©ã—ãŸã‚¨ãƒ³ãƒˆãƒªãƒ¼ãƒã‚¤ãƒ³ãƒˆã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" "neutron ã®ã‚½ãƒ¼ã‚¹ã«å«ã¾ã‚Œã‚‹ã‚¨ãƒ³ãƒˆãƒªãƒ¼ãƒã‚¤ãƒ³ãƒˆã«ã¤ã„ã¦ã¯ã€setup.cfg ã‚’å‚ç…§ã—ã¦" "ãã ã•ã„。" #, python-format msgid "" "Duplicate L3HARouterAgentPortBinding is created for router(s) %(router)s. " "Database cannot be upgraded. Please, remove all duplicates before upgrading " "the database." msgstr "" "ルーター %(router)s ã«é–¢ã—ã¦ã€é‡è¤‡ã™ã‚‹ L3HARouterAgentPortBinding ãŒä½œæˆã•れ" "ã¾ã™ã€‚データベースã¯ã‚¢ãƒƒãƒ—グレードã§ãã¾ã›ã‚“。データベースをアップグレードã™" "ã‚‹å‰ã«ã™ã¹ã¦ã®é‡è¤‡ã‚’削除ã—ã¦ãã ã•ã„。" msgid "Duplicate Security Group Rule in POST." msgstr "POST ã«é‡è¤‡ã™ã‚‹ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¼ã‚°ãƒ«ãƒ¼ãƒ—ルールãŒã‚りã¾ã™ã€‚" msgid "Duplicate address detected" msgstr "検出ã•れãŸé‡è¤‡ã‚¢ãƒ‰ãƒ¬ã‚¹" msgid "Duplicate segment entry in request." msgstr "é‡è¤‡ã™ã‚‹ã‚»ã‚°ãƒ¡ãƒ³ãƒˆã‚¨ãƒ³ãƒˆãƒªãƒ¼ãŒè¦æ±‚ã«å«ã¾ã‚Œã¦ã„ã¾ã™ã€‚" #, python-format msgid "ERROR: %s" msgstr "エラー: %s" msgid "" "ERROR: Unable to find configuration file via the default search paths (~/." "neutron/, ~/, /etc/neutron/, /etc/) and the '--config-file' option!" msgstr "" "エラー: ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã®æ¤œç´¢ãƒ‘ス (~/.neutron/, ~/, /etc/neutron/, /etc/) ãŠã‚ˆã³ " "'--config-file' オプションを使用ã—ã¦ã€æ§‹æˆãƒ•ァイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。" msgid "" "Either one of parameter network_id or router_id must be passed to _get_ports " "method." msgstr "" "パラメーター network_id ã¾ãŸã¯ router_id ã®ã„ãšã‚Œã‹ã‚’ _get_ports ãƒ¡ã‚½ãƒƒãƒ‰ã«æ¸¡" "ã™å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "Either subnet_id or port_id must be specified" msgstr "subnet_id ã¾ãŸã¯ port_id ã®ã„ãšã‚Œã‹ã‚’指定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™" msgid "Empty physical network name." msgstr "物ç†ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯åãŒç©ºã§ã™ã€‚" msgid "Empty subnet pool prefix list." msgstr "サブãƒãƒƒãƒˆãƒ—ールã®ãƒ—レフィックスリストãŒç©ºã§ã™ã€‚" msgid "Enable HA mode for virtual routers." msgstr "仮想ルーターã®ãŸã‚ã« HA モードを有効化ã—ã¾ã™ã€‚" msgid "Enable SSL on the API server" msgstr "API サーãƒãƒ¼ä¸Šã§ SSL を有効ã«ã—ã¾ã™" msgid "" "Enable VXLAN on the agent. Can be enabled when agent is managed by ml2 " "plugin using linuxbridge mechanism driver" msgstr "" "エージェント㧠VXLAN を有効ã«ã—ã¦ãã ã•ã„。linuxbridge メカニズムドライãƒãƒ¼ã‚’" "使用ã—ã¦ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆãŒ ml2 プラグインã«ã‚ˆã£ã¦ç®¡ç†ã•れã¦ã„ã‚‹ã¨ãã«ã€VXLAN を有" "効ã«ã§ãã¾ã™" msgid "" "Enable local ARP responder if it is supported. Requires OVS 2.1 and ML2 " "l2population driver. Allows the switch (when supporting an overlay) to " "respond to an ARP request locally without performing a costly ARP broadcast " "into the overlay." msgstr "" "ローカルã§ã® ARP 応答ãŒã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã‚‹å ´åˆã€ã“れを有効ã«ã—ã¾ã™ã€‚OVS 2.1 ãŠ" "よ㳠ML2 l2population ドライãƒãƒ¼ãŒå¿…è¦ã§ã™ã€‚スイッãƒãŒã€ã‚³ã‚¹ãƒˆã®ã‹ã‹ã‚‹ã€ã‚ªãƒ¼" "ãƒãƒ¼ãƒ¬ã‚¤ã¸ã® ARP ブロードキャストを実行ã›ãšã«ã€ARP è¦æ±‚ã«ãƒ­ãƒ¼ã‚«ãƒ«ã§å¿œç­”ã™ã‚‹ã‚ˆ" "ã†ã«ã—ã¾ã™ (オーãƒãƒ¼ãƒ¬ã‚¤ãŒã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã‚‹å ´åˆ)。" msgid "" "Enable services on an agent with admin_state_up False. If this option is " "False, when admin_state_up of an agent is turned False, services on it will " "be disabled. Agents with admin_state_up False are not selected for automatic " "scheduling regardless of this option. But manual scheduling to such agents " "is available if this option is True." msgstr "" "admin_state_up ㌠False ã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã§ã‚µãƒ¼ãƒ“スを有効化ã—ã¾ã™ã€‚ã“ã®ã‚ªãƒ—ショ" "ン㌠False ã®å ´åˆã€ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã® admin_state_up ㌠False ã«å¤‰æ›´ã•れるã¨ã€ã" "ã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã§ã®ã‚µãƒ¼ãƒ“スã¯ç„¡åŠ¹åŒ–ã•れã¾ã™ã€‚admin_state_up ㌠False ã®ã‚¨ãƒ¼" "ジェントã¯ã€ã“ã®ã‚ªãƒ—ションã¨ã¯ç„¡é–¢ä¿‚ã«ã€è‡ªå‹•スケジューリング用ã«ã¯é¸æŠžã•れã¾" "ã›ã‚“。ãŸã ã—ã€ã“ã®ã‚ªãƒ—ション㌠True ã®å ´åˆã€ã“ã®ã‚ˆã†ãªã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã«å¯¾ã—ã¦ã¯" "手動スケジューリングãŒä½¿ç”¨ã§ãã¾ã™ã€‚" msgid "" "Enables IPv6 Prefix Delegation for automatic subnet CIDR allocation. Set to " "True to enable IPv6 Prefix Delegation for subnet allocation in a PD-capable " "environment. Users making subnet creation requests for IPv6 subnets without " "providing a CIDR or subnetpool ID will be given a CIDR via the Prefix " "Delegation mechanism. Note that enabling PD will override the behavior of " "the default IPv6 subnetpool." msgstr "" "自動的ã«ã‚µãƒ–ãƒãƒƒãƒˆã® CIDR を割り当ã¦ã‚‹ãŸã‚ã« IPv6 ã®ãƒ—レフィックスデリゲー" "ションを有効化ã—ã¾ã™ã€‚True ã«è¨­å®šã—ãŸå ´åˆã€PD ã«å¯¾å¿œã—ãŸç’°å¢ƒã§ã®ã‚µãƒ–ãƒãƒƒãƒˆã®" "割り当ã¦ã®ãŸã‚ã«ã€IPv6 ã®ãƒ—レフィックスデリゲーションを有効化ã—ã¾ã™ã€‚CIDR ã¾" "ãŸã¯ã‚µãƒ–ãƒãƒƒãƒˆãƒ—ール㮠ID を指定ã›ãšã« IPv6 ã®ã‚µãƒ–ãƒãƒƒãƒˆã®ä½œæˆè¦æ±‚を行ã†ãƒ¦ãƒ¼" "ザーã«ã¯ã€ãƒ—レフィックスデリゲーションã®ãƒ¡ã‚«ãƒ‹ã‚ºãƒ çµŒç”±ã§ CIDR ãŒæä¾›ã•れã¾" "ã™ã€‚PD を有効化ã™ã‚‹ã¨ãƒ‡ãƒ•ォルト㮠IPv6 サブãƒãƒƒãƒˆãƒ—ãƒ¼ãƒ«ã®æŒ™å‹•ãŒã‚ªãƒ¼ãƒãƒ¼ãƒ©ã‚¤ãƒ‰" "ã•れるã“ã¨ã«æ³¨æ„ã—ã¦ãã ã•ã„。" msgid "" "Enables the dnsmasq service to provide name resolution for instances via DNS " "resolvers on the host running the DHCP agent. Effectively removes the '--no-" "resolv' option from the dnsmasq process arguments. Adding custom DNS " "resolvers to the 'dnsmasq_dns_servers' option disables this feature." msgstr "" "dnsmasq サービスを有効化ã™ã‚‹ã¨ã€DHCP エージェントを実行ã™ã‚‹ãƒ›ã‚¹ãƒˆä¸Šã§ DNS リ" "ゾルãƒãƒ¼çµŒç”±ã§ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®åå‰è§£æ±ºã‚’行ã†ã“ã¨ãŒã§ãã¾ã™ã€‚dnsmasq プロセスã®" "引数ã‹ã‚‰ '--no-resolv' オプションを効果的ã«å‰Šé™¤ã—ã¾ã™ã€‚'dnsmasq_dns_servers' " "オプションã«ã‚«ã‚¹ã‚¿ãƒ ã® DNS リゾルãƒãƒ¼ã‚’追加ã™ã‚‹ã¨ã€ã“ã®æ©Ÿèƒ½ã‚’無効化ã§ãã¾ã™ã€‚" msgid "End of VLAN range is less than start of VLAN range" msgstr "VLAN 範囲ã®çµ‚ã‚り㌠VLAN 範囲ã®é–‹å§‹ã‚ˆã‚Šå°ã•ããªã£ã¦ã„ã¾ã™" msgid "End of tunnel range is less than start of tunnel range" msgstr "トンãƒãƒ«ç¯„囲ã®çµ‚ã‚りãŒã€ãƒˆãƒ³ãƒãƒ«ç¯„囲ã®é–‹å§‹ã‚ˆã‚Šå°ã•ããªã£ã¦ã„ã¾ã™" #, python-format msgid "Error %(reason)s while attempting the operation." msgstr "æ“作ã®è©¦è¡Œä¸­ã«ç™ºç”Ÿã—ãŸã‚¨ãƒ©ãƒ¼ %(reason)s。" #, python-format msgid "Error parsing dns address %s" msgstr "DNS アドレス %s ã®è§£æžä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ" #, python-format msgid "Error while reading %s" msgstr "%s ã®èª­ã¿å–り中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ" #, python-format msgid "" "Exceeded %s second limit waiting for address to leave the tentative state." msgstr "" "アドレスãŒä¸€æ™‚çš„ãªçŠ¶æ…‹ã‚’çµ‚äº†ã™ã‚‹ã¾ã§ã®å¾…機時間ã®ä¸Šé™ã® %s ç§’ã‚’è¶…éŽã—ã¾ã—ãŸã€‚" msgid "Existing prefixes must be a subset of the new prefixes" msgstr "" "既存ã®ãƒ—ãƒ¬ãƒ•ã‚£ãƒƒã‚¯ã‚¹ã¯æ–°è¦ãƒ—レフィックスã®ã‚µãƒ–セットã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“" #, python-format msgid "" "Exit code: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" msgstr "" "終了コード: %(returncode)dã€Stdin: %(stdin)sã€Stdout: %(stdout)sã€Stderr: " "%(stderr)s" #, python-format msgid "Extension %(driver)s failed." msgstr "æ‹¡å¼µ %(driver)s ãŒå¤±æ•—ã—ã¾ã—ãŸã€‚" #, python-format msgid "" "Extension driver %(driver)s required for service plugin %(service_plugin)s " "not found." msgstr "" "サービスプラグイン %(service_plugin)s ã«å¿…è¦ãªæ‹¡å¼µãƒ‰ãƒ©ã‚¤ãƒãƒ¼ %(driver)s ãŒè¦‹" "ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚" msgid "" "Extension to use alongside ml2 plugin's l2population mechanism driver. It " "enables the plugin to populate VXLAN forwarding table." msgstr "" "ml2 プラグイン㮠l2population メカニズムドライãƒãƒ¼ã¨ã¨ã‚‚ã«ä½¿ç”¨ã™ã‚‹æ‹¡å¼µæ©Ÿèƒ½ã€‚" "ã“れã«ã‚ˆã‚Šã€ã“ã®ãƒ—ラグイン㯠VXLAN 転é€ãƒ†ãƒ¼ãƒ–ルã«ãƒ‡ãƒ¼ã‚¿ã‚’追加ã§ãるよã†ã«ãªã‚Š" "ã¾ã™ã€‚" #, python-format msgid "Extension with alias %s does not exist" msgstr "エイリアス %s ã‚’æŒã¤æ‹¡å¼µã¯å­˜åœ¨ã—ã¾ã›ã‚“" msgid "Extensions list to use" msgstr "使用ã™ã‚‹æ‹¡å¼µæ©Ÿèƒ½ãƒªã‚¹ãƒˆ" #, python-format msgid "Extensions not found: %(extensions)s." msgstr "æ‹¡å¼µãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“: %(extensions)s。" #, python-format msgid "External IP %s is the same as the gateway IP" msgstr "外部 IP %s ã¯ã‚²ãƒ¼ãƒˆã‚¦ã‚§ã‚¤ IP ã¨åŒä¸€ã§ã™" #, python-format msgid "Failed rescheduling router %(router_id)s: no eligible l3 agent found." msgstr "" "ルーター %(router_id)s ã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«å¤‰æ›´ã«å¤±æ•—ã—ã¾ã—ãŸ: 驿 ¼ãª L3 エージェン" "トãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。" #, python-format msgid "Failed scheduling router %(router_id)s to the L3 Agent %(agent_id)s." msgstr "" "L3 エージェント %(agent_id)s ã«å¯¾ã™ã‚‹ãƒ«ãƒ¼ã‚¿ãƒ¼ %(router_id)s ã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒªãƒ³" "ã‚°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚" #, python-format msgid "Failed to allocate subnet: %(reason)s." msgstr "サブãƒãƒƒãƒˆã®å‰²ã‚Šå½“ã¦ã«å¤±æ•—ã—ã¾ã—ãŸ: %(reason)s。" msgid "" "Failed to associate address scope: subnetpools within an address scope must " "have unique prefixes." msgstr "" "アドレススコープã®é–¢é€£ä»˜ã‘ã«å¤±æ•—ã—ã¾ã—ãŸ: アドレススコープ内ã®ã‚µãƒ–ãƒãƒƒãƒˆãƒ—ー" "ルã¯å›ºæœ‰ã®ãƒ—レフィックスをæŒã¤å¿…è¦ãŒã‚りã¾ã™ã€‚" #, python-format msgid "" "Failed to create port on network %(network_id)s, because fixed_ips included " "invalid subnet %(subnet_id)s" msgstr "" "fixed_ips ãŒç„¡åйãªã‚µãƒ–ãƒãƒƒãƒˆ %(subnet_id)s ã«å«ã¾ã‚Œã¦ã„ãŸãŸã‚ã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ " "%(network_id)s ã§ãƒãƒ¼ãƒˆã‚’作æˆã§ãã¾ã›ã‚“ã§ã—ãŸ" #, python-format msgid "Failed to locate source for %s." msgstr "%s ã®ã‚½ãƒ¼ã‚¹ã®ç‰¹å®šã«å¤±æ•—ã—ã¾ã—ãŸã€‚" msgid "Failed to remove supplemental groups" msgstr "補足グループã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ" #, python-format msgid "Failed to set gid %s" msgstr "gid %s ã®è¨­å®šã«å¤±æ•—ã—ã¾ã—ãŸã€‚" #, python-format msgid "Failed to set uid %s" msgstr "uid %s ã®è¨­å®šã«å¤±æ•—ã—ã¾ã—ãŸ" #, python-format msgid "Failed to set-up %(type)s tunnel port to %(ip)s" msgstr "%(ip)s ã«å¯¾ã™ã‚‹ %(type)s トンãƒãƒ«ãƒãƒ¼ãƒˆã‚’セットアップã§ãã¾ã›ã‚“ã§ã—ãŸ" msgid "Failure applying iptables rules" msgstr "iptables ルールé©ç”¨ã®å¤±æ•—" #, python-format msgid "Failure waiting for address %(address)s to become ready: %(reason)s" msgstr "アドレス %(address)s ã®æº–å‚™ãŒã§ãã‚‹ã¾ã§ã®å¾…機ã®å¤±æ•—: %(reason)s" msgid "Flat provider networks are disabled" msgstr "flat プロãƒã‚¤ãƒ€ãƒ¼ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãŒç„¡åŠ¹åŒ–ã•れã¦ã„ã¾ã™" msgid "For TCP/UDP protocols, port_range_min must be <= port_range_max" msgstr "" "TCP/UDP プロトコルã®å ´åˆã€port_range_min 㯠port_range_max 以下ã§ãªã‘れã°ãªã‚Š" "ã¾ã›ã‚“" msgid "Force ip_lib calls to use the root helper" msgstr "ip_lib 呼ã³å‡ºã—ã§ãƒ«ãƒ¼ãƒˆãƒ˜ãƒ«ãƒ‘ーを強制的ã«ä½¿ç”¨ã—ã¾ã™" #, python-format msgid "Found duplicate extension: %(alias)s." msgstr "é‡è¤‡ã™ã‚‹æ‹¡å¼µãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸ: %(alias)s。" #, python-format msgid "" "Found overlapping allocation pools: %(pool_1)s %(pool_2)s for subnet " "%(subnet_cidr)s." msgstr "" "サブãƒãƒƒãƒˆã®é‡ãªã‚Šåˆã£ãŸå‰²ã‚Šå½“ã¦ãƒ—ール %(pool_1)s %(pool_2)s ãŒè¦‹ã¤ã‹ã‚Šã¾ã—" "ãŸ%(subnet_cidr)s。" msgid "Gateway IP version inconsistent with allocation pool version" msgstr "" "ゲートウェイ㮠IP ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¯å‰²ã‚Šå½“ã¦ãƒ—ールã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¨ä¸€è‡´ã™ã‚‹å¿…è¦ãŒã‚り" "ã¾ã™" #, python-format msgid "Gateway ip %(ip_address)s conflicts with allocation pool %(pool)s." msgstr "" "ゲートウェイ IP %(ip_address)s ãŒå‰²ã‚Šå½“ã¦ãƒ—ール %(pool)s ã¨ç«¶åˆã—ã¦ã„ã¾ã™ã€‚" msgid "Gateway is not valid on subnet" msgstr "ゲートウェイãŒã‚µãƒ–ãƒãƒƒãƒˆä¸Šã§ç„¡åйã§ã™" msgid "" "Geneve encapsulation header size is dynamic, this value is used to calculate " "the maximum MTU for the driver. This is the sum of the sizes of the outer " "ETH + IP + UDP + GENEVE header sizes. The default size for this field is 50, " "which is the size of the Geneve header without any additional option headers." msgstr "" "Geneve ã®ã‚«ãƒ—セル化ã®ãƒ˜ãƒƒãƒ€ãƒ¼ã‚µã‚¤ã‚ºã¯å‹•çš„ãªãŸã‚ã€ã“ã®å€¤ã‚’使用ã—ã¦ãƒ‰ãƒ©ã‚¤ãƒãƒ¼" "ã® MTU を計算ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ã“ã®å€¤ã¯å¤–部㮠ETHã€IPã€UDPã€GENEVE ã®ãƒ˜ãƒƒ" "ダーサイズã®åˆè¨ˆã§ã™ã€‚ã“ã®ãƒ•ィールドã®ãƒ‡ãƒ•ォルトã®ã‚µã‚¤ã‚ºã¯ 50 ã§ã‚りã€ã“れã¯" "追加ã®ã‚ªãƒ—ションヘッダーを使用ã—ãªã„ Geneve ã®ãƒ˜ãƒƒãƒ€ãƒ¼ã®ã‚µã‚¤ã‚ºã§ã™ã€‚" msgid "" "Group (gid or name) running metadata proxy after its initialization (if " "empty: agent effective group)." msgstr "" "åˆæœŸåŒ–後ã«ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ãƒ—ロキシーを実行ã—ã¦ã„るグループ (gid ã¾ãŸã¯åå‰) (空ã®å ´" "åˆ: エージェント有効グループ)。" msgid "Group (gid or name) running this process after its initialization" msgstr "åˆæœŸåŒ–後ã«ã“ã®ãƒ—ロセスを実行ã™ã‚‹ã‚°ãƒ«ãƒ¼ãƒ— (gid ã¾ãŸã¯åå‰)" msgid "" "Hostname to be used by the Neutron server, agents and services running on " "this machine. All the agents and services running on this machine must use " "the same host value." msgstr "" "ã“ã®ãƒžã‚·ãƒ³ä¸Šã§ç¨¼åƒã™ã‚‹ Neutron ã®ã‚µãƒ¼ãƒãƒ¼ã€ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã€ã‚µãƒ¼ãƒ“スãŒä½¿ç”¨ã™ã‚‹ãƒ›" "ストå。ã“ã®ãƒžã‚·ãƒ³ä¸Šã§ç¨¼åƒã™ã‚‹ã™ã¹ã¦ã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã¨ã‚µãƒ¼ãƒ“スã¯åŒã˜ãƒ›ã‚¹ãƒˆå€¤ã‚’" "使用ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" #, python-format msgid "" "ICMP code (port-range-max) %(value)s is provided but ICMP type (port-range-" "min) is missing." msgstr "" "ICMP コード (port-range-max) %(value)s ãŒæŒ‡å®šã•れã¾ã—ãŸãŒã€ICMP タイプ (port-" "range-min) ãŒã‚りã¾ã›ã‚“。" msgid "ID of network" msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã® ID" msgid "ID of network to probe" msgstr "プローブã™ã‚‹ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã® ID" msgid "ID of probe port to delete" msgstr "削除ã™ã‚‹ãƒ—ローブãƒãƒ¼ãƒˆã® ID" msgid "ID of probe port to execute command" msgstr "コマンドを実行ã™ã‚‹ãƒ—ローブ ãƒãƒ¼ãƒˆã® ID" msgid "ID of the router" msgstr "ルーター㮠ID" #, python-format msgid "IP address %(ip)s already allocated in subnet %(subnet_id)s" msgstr "" "IP アドレス %(ip)s ãŒæ—¢ã«ã‚µãƒ–ãƒãƒƒãƒˆ %(subnet_id)s ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„ã¾ã™" #, python-format msgid "IP address %(ip)s does not belong to subnet %(subnet_id)s" msgstr "IP アドレス %(ip)s ãŒã‚µãƒ–ãƒãƒƒãƒˆ %(subnet_id)s ã«å±žã—ã¦ã„ã¾ã›ã‚“" msgid "IP allocation failed. Try again later." msgstr "IP ã®å‰²ã‚Šå½“ã¦ãŒå¤±æ•—ã—ã¾ã—ãŸã€‚後ã§å†ã³å‰²ã‚Šå½“ã¦ã¦ãã ã•ã„。" msgid "IP allocation requires subnet_id or ip_address" msgstr "IP を割り当ã¦ã‚‹ã«ã¯ subnet_id ã¾ãŸã¯ ip_address ãŒå¿…è¦ã§ã™" #, python-format msgid "" "IPTablesManager.apply failed to apply the following set of iptables rules:\n" "%s" msgstr "" "IPTablesManager.apply ãŒã€æ¬¡ã®ä¸€é€£ã® iptables è¦å‰‡ã®é©ç”¨ã«å¤±æ•—ã—ã¾ã—ãŸ: \n" "%s" msgid "IPtables conntrack zones exhausted, iptables rules cannot be applied." msgstr "" "iptables ã®ã™ã¹ã¦ã® conntrack ゾーンãŒä½¿ç”¨ã•れãŸãŸã‚ã€iptables ルールをé©ç”¨ã§" "ãã¾ã›ã‚“。" msgid "IPv6 Address Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "プレフィックスデリゲーションを行ã†ã«ã¯ã€IPv6 アドレスモード㯠SLAAC ã¾ãŸã¯ " "Stateless ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "IPv6 RA Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "プレフィックスデリゲーションを行ã†ã«ã¯ã€IPv6 RA モード㯠SLAAC ã¾ãŸã¯ " "Stateless ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" #, python-format msgid "" "IPv6 address %(ip)s cannot be directly assigned to a port on subnet " "%(subnet_id)s as the subnet is configured for automatic addresses" msgstr "" "サブãƒãƒƒãƒˆ %(subnet_id)s ã¯è‡ªå‹•アドレス用ã«è¨­å®šã•れã¦ã„ã‚‹ãŸã‚ã€IPv6 アドレス " "%(ip)s ã‚’ã“ã®ã‚µãƒ–ãƒãƒƒãƒˆä¸Šã®ãƒãƒ¼ãƒˆã«ç›´æŽ¥å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“" #, python-format msgid "" "IPv6 subnet %s configured to receive RAs from an external router cannot be " "added to Neutron Router." msgstr "" "外部ルーターã‹ã‚‰ RA ã‚’å—ã‘å–るよã†ã«æ§‹æˆã•れ㟠IPv6 サブãƒãƒƒãƒˆ %s ã‚’Neutron " "ルーターã«è¿½åŠ ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。" msgid "" "If True, then allow plugins that support it to create VLAN transparent " "networks." msgstr "" "True ã®å ´åˆã€IPAM ドライãƒãƒ¼ã‚’サãƒãƒ¼ãƒˆã™ã‚‹ãƒ—ラグイン㫠VLAN トランスペアレン" "トãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®ä½œæˆã‚’許å¯ã—ã¾ã™ã€‚" msgid "Illegal IP version number" msgstr "IP ãƒãƒ¼ã‚¸ãƒ§ãƒ³ç•ªå·ãŒæ­£ã—ãã‚りã¾ã›ã‚“" #, python-format msgid "" "Illegal prefix bounds: %(prefix_type)s=%(prefixlen)s, %(base_prefix_type)s=" "%(base_prefixlen)s." msgstr "" "æ­£ã—ããªã„プレフィックス境界: %(prefix_type)s=" "%(prefixlen)sã€%(base_prefix_type)s=%(base_prefixlen)s。" #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot " "associate with address scope %(address_scope_id)s because subnetpool " "ip_version is not %(ip_version)s." msgstr "" "サブãƒãƒƒãƒˆãƒ—ãƒ¼ãƒ«ã®æ­£ã—ããªã„関連付ã‘: サブãƒãƒƒãƒˆãƒ—ール㮠ip_version ㌠" "%(ip_version)s ã§ãªã„ãŸã‚ã€ã‚µãƒ–ãƒãƒƒãƒˆãƒ—ール %(subnetpool_id)s をアドレスス" "コープ %(address_scope_id)s ã¨é–¢é€£ä»˜ã‘ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。" #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot be " "associated with address scope %(address_scope_id)s." msgstr "" "サブãƒãƒƒãƒˆãƒ—ãƒ¼ãƒ«ã®æ­£ã—ããªã„関連付ã‘: サブãƒãƒƒãƒˆãƒ—ール %(subnetpool_id)s ã‚’" "アドレススコープ %(address_scope_id)s ã¨é–¢é€£ä»˜ã‘ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。" #, python-format msgid "Illegal subnetpool update : %(reason)s." msgstr "æ­£ã—ããªã„サブãƒãƒƒãƒˆãƒ—ãƒ¼ãƒ«ã®æ›´æ–°: %(reason)s。" #, python-format msgid "Illegal update to prefixes: %(msg)s." msgstr "プレフィックスã«å¯¾ã™ã‚‹æ­£ã—ããªã„æ›´æ–°: %(msg)s。" msgid "" "In some cases the Neutron router is not present to provide the metadata IP " "but the DHCP server can be used to provide this info. Setting this value " "will force the DHCP server to append specific host routes to the DHCP " "request. If this option is set, then the metadata service will be activated " "for all the networks." msgstr "" "Neutron ルーターãŒå­˜åœ¨ã›ãšã€ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ IP ã‚’æä¾›ã§ããªã„å ´åˆã«ã€DHCP サーãƒãƒ¼" "を使用ã—ã¦ã“ã®æƒ…報をæä¾›ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ã“ã®å€¤ã‚’設定ã™ã‚‹ã¨ã€DHCP サーãƒãƒ¼" "㯠DHCP è¦æ±‚ã«å¯¾ã—ã¦ç‰¹å®šã®ãƒ›ã‚¹ãƒˆã®çµŒè·¯ã‚’追加ã—ã¾ã™ã€‚ã“ã®ã‚ªãƒ—ションを設定ã™ã‚‹" "ã¨ã€ã™ã¹ã¦ã®ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«å¯¾ã—ã¦ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã‚µãƒ¼ãƒ“ã‚¹ãŒæœ‰åŠ¹åŒ–ã•れã¾ã™ã€‚" msgid "" "Indicates that this L3 agent should also handle routers that do not have an " "external network gateway configured. This option should be True only for a " "single agent in a Neutron deployment, and may be False for all agents if all " "routers must have an external network gateway." msgstr "" "ã“ã® L3 エージェント㌠外部ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ç”¨ã®ã‚²ãƒ¼ãƒˆã‚¦ã‚§ã‚¤ã‚’設定ã—ã¦ã„ãªãƒ«ãƒ¼ã‚¿ãƒ¼" "ã«ã‚‚対応ã™ã‚‹å¿…è¦ãŒã‚ã‚‹ã“ã¨ã‚’示ã—ã¦ã„ã¾ã™ã€‚ã™ã¹ã¦ã®ãƒ«ãƒ¼ã‚¿ãƒ¼ãŒå¤–部ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯" "用ã®ã‚²ãƒ¼ãƒˆã‚¦ã‚§ã‚¤ã‚’æŒã¤å¿…è¦ãŒã‚ã‚‹å ´åˆã€Neutron ã®å®Ÿè£…環境㮠1 ã¤ã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆ" "ã«å¯¾ã—ã¦ã®ã¿ã€ã“ã®ã‚ªãƒ—ションを True ã«è¨­å®šã™ã‚‹å¿…è¦ãŒã‚りã€ã™ã¹ã¦ã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³" "トã«å¯¾ã—ã¦ã¯é€šå¸¸ã€ã“ã®ã‚ªãƒ—ションを False を設定ã—ã¾ã™ã€‚" #, python-format msgid "Instance of class %(module)s.%(class)s must contain _cache attribute" msgstr "" "クラス %(module)s ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã€‚%(class)s 㯠_cache 属性をå«ã‚€å¿…è¦ãŒã‚りã¾" "ã™" #, python-format msgid "Insufficient prefix space to allocate subnet size /%s" msgstr "サブãƒãƒƒãƒˆã‚µã‚¤ã‚º /%s を割り振るãŸã‚ã®ãƒ—レフィックス空間ãŒä¸å分ã§ã™" msgid "Insufficient rights for removing default security group." msgstr "デフォルトã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¼ã‚°ãƒ«ãƒ¼ãƒ—を削除ã™ã‚‹ãŸã‚ã®æ¨©é™ãŒä¸å分ã§ã™ã€‚" msgid "" "Integration bridge to use. Do not change this parameter unless you have a " "good reason to. This is the name of the OVS integration bridge. There is one " "per hypervisor. The integration bridge acts as a virtual 'patch bay'. All VM " "VIFs are attached to this bridge and then 'patched' according to their " "network connectivity." msgstr "" "使用ã™ã‚‹çµ±åˆãƒ–リッジ。é©åˆ‡ãªç†ç”±ãŒãªã„é™ã‚Šã€ã“ã®ãƒ‘ラメーターを変更ã—ãªã„ã§ã" "ã ã•ã„。ã“れ㯠OVS ã®çµ±åˆãƒ–リッジã®åå‰ã¨ãªã‚Šã¾ã™ã€‚ãƒã‚¤ãƒ‘ーãƒã‚¤ã‚¶ãƒ¼ã”ã¨ã« 1 " "ã¤ã®ãƒ–リッジãŒå­˜åœ¨ã—ã¾ã™ã€‚ã“ã®çµ±åˆãƒ–リッジã¯ä»®æƒ³ã®ã€Œãƒ‘ッãƒãƒ™ã‚¤ã€ã¨ã—ã¦æ©Ÿèƒ½ã—" "ã¾ã™ã€‚ã™ã¹ã¦ã® VM ã® VIF ã¯ã“ã®ãƒ–ãƒªãƒƒã‚¸ã«æŽ¥ç¶šã—ã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯æŽ¥ç¶šã«åŸºã¥ã„ã¦" "パッãƒãŒé©ç”¨ã•れã¾ã™ã€‚" msgid "Interface to monitor" msgstr "モニター対象ã®ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェース" msgid "" "Interval between checks of child process liveness (seconds), use 0 to disable" msgstr "å­ãƒ—ロセスã®å‹•作状æ³ã‚’確èªã™ã‚‹é–“éš” (ç§’)ã€ç„¡åйã«ã™ã‚‹ã«ã¯ 0 を指定ã—ã¾ã™" msgid "Interval between two metering measures" msgstr "2 ã¤ã®è¨ˆæ¸¬é–“ã®é–“éš”" msgid "Interval between two metering reports" msgstr "2 ã¤ã®è¨ˆæ¸¬ãƒ¬ãƒãƒ¼ãƒˆé–“ã®é–“éš”" #, python-format msgid "Invalid CIDR %(input)s given as IP prefix." msgstr "IP ã®ãƒ—レフィックスã¨ã—ã¦æŒ‡å®šã•れ㟠CIDR %(input)s ãŒç„¡åйã§ã™ã€‚" #, python-format msgid "Invalid Device %(dev_name)s: %(reason)s" msgstr "無効ãªãƒ‡ãƒã‚¤ã‚¹ %(dev_name)s: %(reason)s" #, python-format msgid "" "Invalid action '%(action)s' for object type '%(object_type)s'. Valid " "actions: %(valid_actions)s" msgstr "" "オブジェクトタイプ '%(object_type)s' ã®ç„¡åйãªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ '%(action)s' 。有効ãª" "アクション: %(valid_actions)s" #, python-format msgid "" "Invalid authentication type: %(auth_type)s, valid types are: " "%(valid_auth_types)s" msgstr "" "èªè¨¼ã‚¿ã‚¤ãƒ— %(auth_type)s ã¯ç„¡åйã§ã™ã€‚有効ãªã‚¿ã‚¤ãƒ—㯠%(valid_auth_types)s ã§ã™" #, python-format msgid "Invalid ethertype %(ethertype)s for protocol %(protocol)s." msgstr "プロトコル %(protocol)s ã«é–¢ã™ã‚‹ç„¡åйãªã‚¤ãƒ¼ã‚µã‚¿ã‚¤ãƒ— %(ethertype)s。" #, python-format msgid "Invalid format: %s" msgstr "無効ãªå½¢å¼: %s" #, python-format msgid "Invalid instance state: %(state)s, valid states are: %(valid_states)s" msgstr "" "インスタンス状態 %(state)s ã¯ç„¡åйã§ã™ã€‚有効ãªçŠ¶æ…‹ã¯ %(valid_states)s ã§ã™" #, python-format msgid "Invalid mapping: '%s'" msgstr "無効ãªãƒžãƒƒãƒ”ング: '%s'" #, python-format msgid "Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'." msgstr "無効ãªãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ VLAN ã®ç¯„囲: '%(vlan_range)s' - '%(error)s'" #, python-format msgid "Invalid network VXLAN port range: '%(vxlan_range)s'." msgstr "無効ãªãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ VXLAN ãƒãƒ¼ãƒˆã®ç¯„囲: '%(vxlan_range)s'" #, python-format msgid "Invalid pci slot %(pci_slot)s" msgstr "無効㪠PCI スロット %(pci_slot)s" #, python-format msgid "Invalid provider format. Last part should be 'default' or empty: %s" msgstr "" "プロãƒã‚¤ãƒ€ãƒ¼ã®æŒ‡å®šå½¢å¼ãŒç„¡åйã§ã™ã€‚最後ã®éƒ¨åˆ†ã¯ 'default' ã¾ãŸã¯ç©ºã«ã—ã¦ãã ã•" "ã„: %s" #, python-format msgid "Invalid resource type %(resource_type)s" msgstr "無効ãªãƒªã‚½ãƒ¼ã‚¹ã‚¿ã‚¤ãƒ— %(resource_type)s" #, python-format msgid "Invalid route: %s" msgstr "無効ãªçµŒè·¯: %s" msgid "Invalid service provider format" msgstr "サービスプロãƒã‚¤ãƒ€ãƒ¼ã®æŒ‡å®šå½¢å¼ãŒç„¡åйã§ã™" #, python-format msgid "" "Invalid value for ICMP %(field)s (%(attr)s) %(value)s. It must be 0 to 255." msgstr "" "ICMP %(field)s (%(attr)s) %(value)s ã«ç„¡åйãªå€¤ã§ã™ã€‚ã“れ㯠0 ã‹ã‚‰ 255 ã¾ã§ã§" "ãªã‘れã°ãªã‚Šã¾ã›ã‚“。" #, python-format msgid "Invalid value for port %(port)s" msgstr "ãƒãƒ¼ãƒˆ %(port)s ã®ç„¡åй値" msgid "" "Iptables mangle mark used to mark ingress from external network. This mark " "will be masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Iptables ã¯å¤–部ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‹ã‚‰ã®é€²å…¥ã‚’示ã™ãŸã‚ã«ä½¿ç”¨ã•れるマークを分割ã—ã¾" "ã™ã€‚ã“ã®ãƒžãƒ¼ã‚¯ã‚’ 0xffff ã§ãƒžã‚¹ã‚­ãƒ³ã‚°ã™ã‚‹ã“ã¨ã§ã€å¾ŒåŠã® 16 ビットã®ã¿ã‚’使用ã—" "ã¾ã™ã€‚" msgid "" "Iptables mangle mark used to mark metadata valid requests. This mark will be " "masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Iptables ã¯ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã®é©åˆ‡ãªè¦æ±‚を示ã™ãŸã‚ã«ä½¿ç”¨ã•れるマークを分割ã—ã¾ã™ã€‚ã“" "ã®ãƒžãƒ¼ã‚¯ã‚’ 0xffff ã§ãƒžã‚¹ã‚­ãƒ³ã‚°ã™ã‚‹ã“ã¨ã§ã€å¾ŒåŠã® 16 ビットã®ã¿ã‚’使用ã—ã¾ã™ã€‚" msgid "Keepalived didn't respawn" msgstr "keepalived ãŒå†ä½œæˆã•れã¾ã›ã‚“ã§ã—ãŸ" msgid "Keepalived didn't spawn" msgstr "Keepalived ãŒç”Ÿæˆã•れã¾ã›ã‚“ã§ã—ãŸ" #, python-format msgid "" "Kernel HZ value %(value)s is not valid. This value must be greater than 0." msgstr "" "カーãƒãƒ« HZ ã®å€¤ %(value)s ãŒç„¡åйã§ã™ã€‚0 より大ãã„値ã«ã—ãªã‘れã°ãªã‚Šã¾ã›ã‚“。" msgid "L3 agent failure to setup NAT for floating IPs" msgstr "L3 エージェントã«ã‚ˆã‚‹ Floating IP ã¸ã® NAT ã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—ã®å¤±æ•—" msgid "L3 agent failure to setup floating IPs" msgstr "L3 エージェントã«ã‚ˆã‚‹ Floating IP ã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—ã®å¤±æ•—" msgid "Limit number of leases to prevent a denial-of-service." msgstr "Denial-of-Service を防ããŸã‚ã«ãƒªãƒ¼ã‚¹ã®æ•°ã‚’制é™ã—ã¦ãã ã•ã„。" msgid "List of :" msgstr " ã®ãƒªã‚¹ãƒˆ: " msgid "" "List of :: or " "specifying physical_network names usable for VLAN provider and tenant " "networks, as well as ranges of VLAN tags on each available for allocation to " "tenant networks." msgstr "" ":: ã¾ãŸã¯ ã®ãƒªã‚¹ãƒˆã€‚" "ã“ã®ãƒªã‚¹ãƒˆã«ã¯ã€VLAN プロãƒã‚¤ãƒ€ãƒ¼/テナントãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«ä½¿ç”¨ã§ãã‚‹ " "physical_network åãŒæŒ‡å®šã•れるã ã‘ã§ãªãã€ãƒ†ãƒŠãƒ³ãƒˆãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«å‰²ã‚ŠæŒ¯ã‚‹ã“ã¨" "ãŒã§ãã‚‹å„物ç†ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã® VLAN ã‚¿ã‚°ã®ç¯„囲も指定ã•れã¾ã™ã€‚" msgid "" "List of network type driver entrypoints to be loaded from the neutron.ml2." "type_drivers namespace." msgstr "" "neutron.ml2.type_drivers åå‰ç©ºé–“ã‹ã‚‰ãƒ­ãƒ¼ãƒ‰ã™ã‚‹ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¿ã‚¤ãƒ—ドライãƒãƒ¼ã®" "エントリーãƒã‚¤ãƒ³ãƒˆã®ãƒªã‚¹ãƒˆã€‚" msgid "" "List of physical_network names with which flat networks can be created. Use " "default '*' to allow flat networks with arbitrary physical_network names. " "Use an empty list to disable flat networks." msgstr "" "フラットãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®ä½œæˆãŒå¯èƒ½ãª physical_network åã®ãƒªã‚¹ãƒˆã€‚デフォルト値" "ã® '*' を使用ã™ã‚‹ã¨ã€ä»»æ„ã® physical_network åã‚’æŒã¤ãƒ•ラットãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’作" "æˆã§ãã¾ã™ã€‚空ã®ãƒªã‚¹ãƒˆã‚’使用ã™ã‚‹ã¨ã€ãƒ•ラットãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãŒç„¡åŠ¹åŒ–ã•れã¾ã™ã€‚" msgid "Location for Metadata Proxy UNIX domain socket." msgstr "メタデータプロキシー UNIX ドメインソケットã®ãƒ­ã‚±ãƒ¼ã‚·ãƒ§ãƒ³ã€‚" msgid "Location of Metadata Proxy UNIX domain socket" msgstr "メタデータプロキシー㮠UNIX ドメインソケットã®å ´æ‰€" msgid "Location to store DHCP server config files." msgstr "DHCP サーãƒãƒ¼ã®æ§‹æˆãƒ•ァイルをä¿å­˜ã™ã‚‹ãƒ­ã‚±ãƒ¼ã‚·ãƒ§ãƒ³ã€‚" msgid "Location to store IPv6 PD files." msgstr "IPv6 PD ファイルをä¿å­˜ã™ã‚‹ãƒ­ã‚±ãƒ¼ã‚·ãƒ§ãƒ³ã€‚" msgid "Location to store IPv6 RA config files" msgstr "IPv6 RA 設定ファイルをä¿å­˜ã™ã‚‹å ´æ‰€" msgid "Location to store child pid files" msgstr "å­ãƒ—ロセス㮠PID ãƒ•ã‚¡ã‚¤ãƒ«ã‚’ä¿æŒã™ã‚‹å ´æ‰€" msgid "Location to store keepalived/conntrackd config files" msgstr "keepalived/conntrackd æ§‹æˆãƒ•ァイルをä¿å­˜ã™ã‚‹å ´æ‰€" msgid "Log agent heartbeats" msgstr "エージェントã®ãƒãƒ¼ãƒˆãƒ“ートを記録ã—ã¾ã™" msgid "" "MTU of the underlying physical network. Neutron uses this value to calculate " "MTU for all virtual network components. For flat and VLAN networks, neutron " "uses this value without modification. For overlay networks such as VXLAN, " "neutron automatically subtracts the overlay protocol overhead from this " "value. Defaults to 1500, the standard value for Ethernet." msgstr "" "基盤ã¨ãªã‚‹ç‰©ç†ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã® MTU。Neutron ã¯ã“ã®å€¤ã‚’使用ã—ã¦ã€ã™ã¹ã¦ã®ä»®æƒ³" "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã® MTU を計算ã—ã¾ã™ã€‚フラットãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãŠã‚ˆã³ " "VLAN ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®å ´åˆã¯ã€Neutron ã¯ã“ã®å€¤ã‚’変更ã™ã‚‹ã“ã¨ãªã使用ã—ã¾ã™ã€‚" "VXLAN ã®ã‚ˆã†ãªã‚ªãƒ¼ãƒãƒ¼ãƒ¬ã‚¤ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®å ´åˆã¯ã€Neutron ã¯ã“ã®å€¤ã‹ã‚‰ã‚ªãƒ¼ãƒãƒ¼" "レイプロトコルオーãƒãƒ¼ãƒ˜ãƒƒãƒ‰ã®å€¤ã‚’è‡ªå‹•çš„ã«æ¸›ç®—ã—ã¾ã™ã€‚デフォルト値㯠" "Ethernet ã®æ¨™æº–値ã§ã‚ã‚‹ 1500 ã§ã™ã€‚" msgid "MTU size of veth interfaces" msgstr "veth インターフェース㮠MTU サイズ" msgid "Make the l2 agent run in DVR mode." msgstr "L2 エージェントを DVR モードã§å®Ÿè¡Œã—ã¾ã™ã€‚" msgid "Malformed request body" msgstr "誤ã£ãŸå½¢å¼ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆæœ¬æ–‡" #, python-format msgid "Malformed request body: %(reason)s." msgstr "誤ã£ãŸå½¢å¼ã®è¦æ±‚本文: %(reason)s。" msgid "MaxRtrAdvInterval setting for radvd.conf" msgstr "radvd.conf ã® MaxRtrAdvInterval 設定" msgid "Maximum number of DNS nameservers per subnet" msgstr "サブãƒãƒƒãƒˆã”ã¨ã® DNS ãƒãƒ¼ãƒ ã‚µãƒ¼ãƒãƒ¼ã®æœ€å¤§æ•°" msgid "" "Maximum number of L3 agents which a HA router will be scheduled on. If it is " "set to 0 then the router will be scheduled on every agent." msgstr "" "HA ルーターãŒã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«è¨­å®šã•れる L3 ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã®æœ€å¤§æ•°ã€‚ã“ã®å€¤ã‚’ 0 ã«è¨­" "定ã™ã‚‹ã¨ã€ãƒ«ãƒ¼ã‚¿ãƒ¼ã¯ã™ã¹ã¦ã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã«å¯¾ã—ã¦ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«è¨­å®šã•れã¾ã™ã€‚" msgid "Maximum number of allowed address pairs" msgstr "許å¯ã•れãŸã‚¢ãƒ‰ãƒ¬ã‚¹ãƒšã‚¢ã®æœ€å¤§æ•°" msgid "Maximum number of host routes per subnet" msgstr "サブãƒãƒƒãƒˆã”ã¨ã®ãƒ›ã‚¹ãƒˆçµŒè·¯ã®æœ€å¤§æ•°" msgid "Maximum number of routes per router" msgstr "ルーターã”ã¨ã«è¨­å®šå¯èƒ½ãªçµŒè·¯ã®æœ€å¤§æ•°" msgid "" "Metadata Proxy UNIX domain socket mode, 4 values allowed: 'deduce': deduce " "mode from metadata_proxy_user/group values, 'user': set metadata proxy " "socket mode to 0o644, to use when metadata_proxy_user is agent effective " "user or root, 'group': set metadata proxy socket mode to 0o664, to use when " "metadata_proxy_group is agent effective group or root, 'all': set metadata " "proxy socket mode to 0o666, to use otherwise." msgstr "" "メタデータプロキシー㮠UNIX ドメインã®ã‚½ã‚±ãƒƒãƒˆãƒ¢ãƒ¼ãƒ‰ã§ã¯ 4 ã¤ã®å€¤ã‚’使用ã§ãã¾" "ã™ã€‚'deduce' 㯠metadata_proxy_user/group ã®å€¤ã‹ã‚‰ãƒ¢ãƒ¼ãƒ‰ã‚’推測ã—ã¾ã™ã€‚'user' " "ã¯ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ãƒ—ロキシーã®ã‚½ã‚±ãƒƒãƒˆãƒ¢ãƒ¼ãƒ‰ã‚’ 0o644 ã«è¨­å®šã—ã¾ã™ " "(metadata_proxy_user ãŒã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã®æœ‰åйãªãƒ¦ãƒ¼ã‚¶ãƒ¼ã¾ãŸã¯ãƒ«ãƒ¼ãƒˆã§ã‚ã‚‹å ´åˆã«ä½¿" "用)。'group' ã¯ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ãƒ—ロキシーã®ã‚½ã‚±ãƒƒãƒˆãƒ¢ãƒ¼ãƒ‰ã‚’ 0o664 ã«è¨­å®šã—ã¾ã™ " "(metadata_proxy_group ãŒã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã®æœ‰åйãªã‚°ãƒ«ãƒ¼ãƒ—ã¾ãŸã¯ãƒ«ãƒ¼ãƒˆã§ã‚ã‚‹å ´åˆã«" "使用)。'all' ã¯ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ãƒ—ロキシーã®ã‚½ã‚±ãƒƒãƒˆãƒ¢ãƒ¼ãƒ‰ã‚’ 0o666 ã«è¨­å®šã—ã¾ã™ (ã" "ã®ä»–ã®å ´åˆã«ä½¿ç”¨)。" msgid "Metering driver" msgstr "計測ドライãƒãƒ¼" msgid "MinRtrAdvInterval setting for radvd.conf" msgstr " radvd.conf ã® MinRtrAdvInterval 設定" msgid "Minimize polling by monitoring ovsdb for interface changes." msgstr "" "ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ã‚§ãƒ¼ã‚¹å¤‰æ›´ã®æ¤œå‡ºã«é–¢ã—㦠ovsdb をモニターã™ã‚‹ã“ã¨ã§ãƒãƒ¼ãƒªãƒ³ã‚°ãŒæœ€å°" "化ã•れã¾ã™ã€‚" #, python-format msgid "Missing key in mapping: '%s'" msgstr "マッピングã«ã‚­ãƒ¼ãŒã‚りã¾ã›ã‚“: '%s'" msgid "" "Multicast group for VXLAN. When configured, will enable sending all " "broadcast traffic to this multicast group. When left unconfigured, will " "disable multicast VXLAN mode." msgstr "" "VXLAN ã®ãƒžãƒ«ãƒã‚­ãƒ£ã‚¹ãƒˆã‚°ãƒ«ãƒ¼ãƒ—。設定ã•れるã¨ã€ã“ã®ãƒžãƒ«ãƒã‚­ãƒ£ã‚¹ãƒˆã‚°ãƒ«ãƒ¼ãƒ—ã«ã™" "ã¹ã¦ã®ãƒ–ロードキャストトラフィックをé€ä¿¡ã§ãã¾ã™ã€‚設定ã•れãªã„ã¾ã¾ã«ã™ã‚‹ã¨ã€" "マルãƒã‚­ãƒ£ã‚¹ãƒˆ VXLAN モードを無効化ã—ã¾ã™ã€‚" msgid "" "Multicast group(s) for vxlan interface. A range of group addresses may be " "specified by using CIDR notation. Specifying a range allows different VNIs " "to use different group addresses, reducing or eliminating spurious broadcast " "traffic to the tunnel endpoints. To reserve a unique group for each possible " "(24-bit) VNI, use a /8 such as 239.0.0.0/8. This setting must be the same on " "all the agents." msgstr "" "vxlan インターフェースã®ãƒžãƒ«ãƒã‚­ãƒ£ã‚¹ãƒˆã‚°ãƒ«ãƒ¼ãƒ—。CIDR 表記を使用ã™ã‚‹ã“ã¨ã§ã€ä¸€" "定ã®ç¯„囲ã®ã‚°ãƒ«ãƒ¼ãƒ—アドレスを指定ã§ãã¾ã™ã€‚範囲を指定ã™ã‚‹ã¨ã•ã¾ã–ã¾ãª VNI ãŒã•" "ã¾ã–ã¾ãªã‚°ãƒ«ãƒ¼ãƒ—アドレスを使用ã§ãã‚‹ãŸã‚ã€ãƒˆãƒ³ãƒãƒ«ã®ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆã¸ã®ä¸é©åˆ‡" "ãªãƒ–ロードキャストトラフィックを削減ã¾ãŸã¯æŽ’除ã§ãã¾ã™ã€‚使用ã™ã‚‹å¯èƒ½æ€§ã®ã‚ã‚‹" "å„ (24 ビットã®) VNI ã«ç‹¬è‡ªã®ã‚°ãƒ«ãƒ¼ãƒ—を予約ã™ã‚‹ã«ã¯ã€ /8 (239.0.0.0/8 ãªã©) " "を使用ã—ã¾ã™ã€‚ã“ã®è¨­å®šã¯ã™ã¹ã¦ã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã§åŒã˜ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" #, python-format msgid "Multiple default providers for service %s" msgstr "サービス %s ã®ãƒ‡ãƒ•ォルトã®ãƒ—ロãƒã‚¤ãƒ€ãƒ¼ãŒè¤‡æ•°ã‚りã¾ã™" #, python-format msgid "Multiple plugins for service %s were configured" msgstr "サービス %s ã«å¯¾ã—ã¦è¤‡æ•°ã®ãƒ—ãƒ©ã‚°ã‚¤ãƒ³ãŒæ§‹æˆã•れã¦ã„ã¾ã—ãŸ" #, python-format msgid "Multiple providers specified for service %s" msgstr "複数ã®ãƒ—ロãƒã‚¤ãƒ€ãƒ¼ãŒã‚µãƒ¼ãƒ“ス %s ã«å¯¾ã—ã¦æŒ‡å®šã•れã¾ã—ãŸ" msgid "Multiple tenant_ids in bulk security group rule create not allowed" msgstr "" "ãƒãƒ«ã‚¯ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¼ã‚°ãƒ«ãƒ¼ãƒ—ルールã®ä½œæˆã§è¤‡æ•°ã® tenant_id ã¯è¨±å¯ã•れã¾ã›ã‚“" msgid "Must also specify protocol if port range is given." msgstr "" "ãƒãƒ¼ãƒˆã®ç¯„å›²ãŒæä¾›ã•れã¦ã„ã‚‹å ´åˆã¯ã€ãƒ—ロトコルも指定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "Must specify one or more actions on flow addition or modification" msgstr "フローã®è¿½åŠ ã¾ãŸã¯å¤‰æ›´ã«ã¤ã„ã¦ã€1 ã¤ä»¥ä¸Šã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’指定ã—ã¦ãã ã•ã„" msgid "Name of Open vSwitch bridge to use" msgstr "使用ã™ã‚‹ Open vSwitch ブリッジã®åå‰" msgid "" "Name of nova region to use. Useful if keystone manages more than one region." msgstr "" "使用ã™ã‚‹ nova リージョンã®åå‰ã€‚Keystone ã§è¤‡æ•°ã®ãƒªãƒ¼ã‚¸ãƒ§ãƒ³ã‚’管ç†ã™ã‚‹å ´åˆã«å½¹" "ç«‹ã¡ã¾ã™ã€‚" msgid "Namespace of the router" msgstr "ルーターã®åå‰ç©ºé–“" msgid "Native pagination depend on native sorting" msgstr "ãƒã‚¤ãƒ†ã‚£ãƒ–ページ編集ã¯ãƒã‚¤ãƒ†ã‚£ãƒ–ソートã«ä¾å­˜ã—ã¾ã™" #, python-format msgid "" "Need to apply migrations from %(project)s contract branch. This will require " "all Neutron server instances to be shutdown before proceeding with the " "upgrade." msgstr "" "%(project)s ã®åŽç¸®æžã‹ã‚‰ã®ç§»è¡Œã‚’é©ç”¨ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚アップグレードを行ã†" "å‰ã«ã€ã™ã¹ã¦ã® Neutron サーãƒãƒ¼ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã‚’シャットダウンã™ã‚‹å¿…è¦ãŒã‚りã¾" "ã™ã€‚" msgid "Negative delta (downgrade) not supported" msgstr "è² ã®ãƒ‡ãƒ«ã‚¿ (ダウングレード) ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“" msgid "Negative relative revision (downgrade) not supported" msgstr "è² ã®ç›¸å¯¾çš„ãªå¤‰æ›´ (ダウングレード) ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“" #, python-format msgid "Network %s does not contain any IPv4 subnet" msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ %s ã«ã¯ IPv4 サブãƒãƒƒãƒˆãŒå«ã¾ã‚Œã¾ã›ã‚“" #, python-format msgid "Network %s is not a valid external network" msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ %s ã¯æœ‰åйãªå¤–部ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã§ã¯ã‚りã¾ã›ã‚“" #, python-format msgid "Network %s is not an external network" msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ %s ã¯å¤–部ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã§ã¯ã‚りã¾ã›ã‚“" #, python-format msgid "" "Network of size %(size)s, from IP range %(parent_range)s excluding IP ranges " "%(excluded_ranges)s was not found." msgstr "" "IP 範囲 %(parent_range)s (IP 範囲 %(excluded_ranges)s を除ã) ã‹ã‚‰ã®ã‚µã‚¤ã‚º " "%(size)s ã®ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚" #, python-format msgid "Network type value '%s' not supported" msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¿ã‚¤ãƒ—値 '%s' ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“" msgid "Network type value needed by the ML2 plugin" msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¿ã‚¤ãƒ—値㌠ML2 プラグインã«å¿…è¦ã§ã™" msgid "Network types supported by the agent (gre and/or vxlan)." msgstr "" "エージェントã«ã‚ˆã£ã¦ã‚µãƒãƒ¼ãƒˆã•れるãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¿ã‚¤ãƒ— (gre 㨠vxlan ã®ã„ãšã‚Œ" "ã‹ã€ã¾ãŸã¯ä¸¡æ–¹)。" msgid "Neutron Service Type Management" msgstr "Neutron サービスタイプ管ç†" msgid "Neutron core_plugin not configured!" msgstr "Neutron ã® core_plugin ãŒè¨­å®šã•れã¦ã„ã¾ã›ã‚“。" msgid "No default router:external network" msgstr "デフォルト㮠router:external ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãŒã‚りã¾ã›ã‚“" #, python-format msgid "No default subnetpool found for IPv%s" msgstr "IPv%s ã«é–¢ã™ã‚‹ãƒ‡ãƒ•ォルトã®ã‚µãƒ–ãƒãƒƒãƒˆãƒ—ールãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" msgid "No default subnetpools defined" msgstr "定義ã•れãŸãƒ‡ãƒ•ォルトã®ã‚µãƒ–ãƒãƒƒãƒˆãƒ—ールãŒã‚りã¾ã›ã‚“" #, python-format msgid "No eligible l3 agent associated with external network %s found" msgstr "" "外部ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ %s ã«é–¢é€£ä»˜ã‘ã‚‰ã‚Œã‚‹é©æ ¼ãª L3 エージェントãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" #, python-format msgid "No more IP addresses available for subnet %(subnet_id)s." msgstr "" "サブãƒãƒƒãƒˆ %(subnet_id)s ã§ã¯ã“れ以上使用å¯èƒ½ãª IP アドレスãŒã‚りã¾ã›ã‚“。" msgid "No offline migrations pending." msgstr "オフラインã§å®Ÿè¡Œä¸­ã®ç§»è¡Œã¯ã‚りã¾ã›ã‚“。" #, python-format msgid "No shared key in %s fields" msgstr "%s フィールドã«å…±æœ‰éµãŒå­˜åœ¨ã—ã¾ã›ã‚“" msgid "Not allowed to manually assign a router to an agent in 'dvr' mode." msgstr "" "'dvr' モードã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã¸ã®æ‰‹å‹•ã§ã®ãƒ«ãƒ¼ã‚¿ãƒ¼å‰²ã‚Šå½“ã¦ã¯è¨±å¯ã•れã¾ã›ã‚“。" msgid "Not allowed to manually remove a router from an agent in 'dvr' mode." msgstr "'dvr' モードã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã‹ã‚‰ã®æ‰‹å‹•ã§ã®ãƒ«ãƒ¼ã‚¿ãƒ¼å‰Šé™¤ã¯è¨±å¯ã•れã¾ã›ã‚“。" msgid "" "Number of DHCP agents scheduled to host a tenant network. If this number is " "greater than 1, the scheduler automatically assigns multiple DHCP agents for " "a given tenant network, providing high availability for DHCP service." msgstr "" "テナントãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’ホストã™ã‚‹ã‚ˆã†ã«ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«è¨­å®šã•れ㟠DHCP エージェン" "ãƒˆã®æ•°ã€‚ã“ã®æ•°ãŒ 1 より大ãã„å ´åˆã€ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ©ãƒ¼ãŒè‡ªå‹•çš„ã«ç‰¹å®šã®ãƒ†ãƒŠãƒ³ãƒˆãƒãƒƒ" "トワークã«è¤‡æ•°ã® DHCP エージェントを割り当ã¦ã‚‹ãŸã‚ã€DHCP サービスã®é«˜å¯ç”¨æ€§ãŒ" "実ç¾ã—ã¾ã™ã€‚" msgid "Number of backlog requests to configure the metadata server socket with" msgstr "メタデータサーãƒãƒ¼ã‚½ã‚±ãƒƒãƒˆã®æ§‹æˆã«ä½¿ç”¨ã•れるãƒãƒƒã‚¯ãƒ­ã‚°è¦æ±‚ã®æ•°" msgid "Number of backlog requests to configure the socket with" msgstr "ソケットã«è¨­å®šã™ã‚‹ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®ãƒãƒƒã‚¯ãƒ­ã‚°æ•°" msgid "" "Number of bits in an ipv4 PTR zone that will be considered network prefix. " "It has to align to byte boundary. Minimum value is 8. Maximum value is 24. " "As a consequence, range of values is 8, 16 and 24" msgstr "" "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®ãƒ—レフィックスã¨ã¿ãªã•れる Ipv4 PTR ゾーン内ã®ãƒ“ット数。ãƒã‚¤ãƒˆ" "境界ã¨åŒã˜æ¡æ•°ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚最å°å€¤ã¯ 8 ã§ã€æœ€å¤§å€¤ã¯ 24 ã§ã™ã€‚ãã®ãŸ" "ã‚ã€ä½¿ç”¨ã§ãる値㯠8ã€16ã€ãŠã‚ˆã³ 24 ã§ã™ã€‚" msgid "" "Number of bits in an ipv6 PTR zone that will be considered network prefix. " "It has to align to nyble boundary. Minimum value is 4. Maximum value is 124. " "As a consequence, range of values is 4, 8, 12, 16,..., 124" msgstr "" "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®ãƒ—レフィックスã¨ã¿ãªã•れる Ipv6 PTR ゾーン内ã®ãƒ“ット数。nyble " "境界ã¨åŒã˜æ¡æ•°ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚最å°å€¤ã¯ 4 ã§ã€æœ€å¤§å€¤ã¯ 124 ã§ã™ã€‚ãã®ãŸ" "ã‚ã€ä½¿ç”¨ã§ãる値㯠4〠8ã€12ã€16ã€... 124 ã§ã™ã€‚" msgid "" "Number of floating IPs allowed per tenant. A negative value means unlimited." msgstr "" "テナント当ãŸã‚Šã«è¨±å¯ã•れる Floating IP 数。負ã®å€¤ã¯ç„¡åˆ¶é™ã‚’æ„味ã—ã¾ã™ã€‚ " msgid "" "Number of networks allowed per tenant. A negative value means unlimited." msgstr "テナント当ãŸã‚Šã«è¨±å¯ã•れるãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯æ•°ã€‚è² ã®å€¤ã¯ç„¡åˆ¶é™ã‚’æ„味ã—ã¾ã™ã€‚" msgid "Number of ports allowed per tenant. A negative value means unlimited." msgstr "テナント当ãŸã‚Šã«è¨±å¯ã•れるãƒãƒ¼ãƒˆæ•°ã€‚è² ã®å€¤ã¯ç„¡åˆ¶é™ã‚’æ„味ã—ã¾ã™ã€‚" msgid "Number of routers allowed per tenant. A negative value means unlimited." msgstr "テナント当ãŸã‚Šã«è¨±å¯ã•れるルーター数。負ã®å€¤ã¯ç„¡åˆ¶é™ã‚’æ„味ã—ã¾ã™ã€‚" msgid "" "Number of seconds between sending events to nova if there are any events to " "send." msgstr "é€ä¿¡ã™ã‚‹ã‚¤ãƒ™ãƒ³ãƒˆãŒã‚ã‚‹å ´åˆã® nova ã¸ã®ã‚¤ãƒ™ãƒ³ãƒˆé€ä¿¡é–“ã®ç§’数。" msgid "Number of seconds to keep retrying to listen" msgstr "リッスンを試行ã—ç¶šã‘ã‚‹ç§’æ•°" msgid "" "Number of security groups allowed per tenant. A negative value means " "unlimited." msgstr "" "テナント当ãŸã‚Šã«è¨±å¯ã•れるセキュリティーグループ数。負ã®å€¤ã¯ç„¡åˆ¶é™ã‚’æ„味ã—ã¾" "ã™ã€‚ " msgid "" "Number of security rules allowed per tenant. A negative value means " "unlimited." msgstr "" "テナント当ãŸã‚Šã«è¨±å¯ã•れるセキュリティールール数。負ã®å€¤ã¯ç„¡åˆ¶é™ã‚’æ„味ã—ã¾" "ã™ã€‚ " msgid "" "Number of separate API worker processes for service. If not specified, the " "default is equal to the number of CPUs available for best performance." msgstr "" "API サービスã«ä½¿ç”¨ã™ã‚‹å€‹åˆ¥ãƒ¯ãƒ¼ã‚«ãƒ¼ãƒ—ãƒ­ã‚»ã‚¹ã®æ•°ã€‚指定ã•れãªã‹ã£ãŸå ´åˆã€ãƒ‡ãƒ•ã‚©" "ãƒ«ãƒˆå€¤ã¯æ€§èƒ½ã‚’最大é™å¾—ã‚‹ãŸã‚ã«ä½¿ç”¨å¯èƒ½ãª CPU ã®æ•°ã¨ä¸€è‡´ã—ã¾ã™ã€‚" msgid "" "Number of separate worker processes for metadata server (defaults to half of " "the number of CPUs)" msgstr "" "メタデータサーãƒãƒ¼ã®å€‹åˆ¥ã®ãƒ¯ãƒ¼ã‚«ãƒ¼ãƒ—ãƒ­ã‚»ã‚¹ã®æ•° (デフォルト値㯠CPU æ•°ã®åŠæ•°)" msgid "Number of subnets allowed per tenant, A negative value means unlimited." msgstr "テナント当ãŸã‚Šã«è¨±å¯ã•れるサブãƒãƒƒãƒˆæ•°ã€‚è² ã®å€¤ã¯ç„¡åˆ¶é™ã‚’æ„味ã—ã¾ã™ã€‚" msgid "" "Number of threads to use during sync process. Should not exceed connection " "pool size configured on server." msgstr "" "åŒæœŸãƒ—ロセス中ã«ä½¿ç”¨ã™ã‚‹ã‚¹ãƒ¬ãƒƒãƒ‰ã®æ•°ã€‚サーãƒãƒ¼ã§è¨­å®šã—ãŸæŽ¥ç¶šãƒ—ãƒ¼ãƒ«ã‚µã‚¤ã‚ºã®å€¤" "ã‚’è¶…ãˆã¦ã¯ãªã‚Šã¾ã›ã‚“。" msgid "OK" msgstr "OK" msgid "" "OVS datapath to use. 'system' is the default value and corresponds to the " "kernel datapath. To enable the userspace datapath set this value to 'netdev'." msgstr "" "使用ã™ã‚‹ OVS データパス。デフォルト値㯠'system' ã§ã‚りã€ã“れã¯ã‚«ãƒ¼ãƒãƒ«ã®ãƒ‡ãƒ¼" "タパスã«è©²å½“ã—ã¾ã™ã€‚ユーザースペースã®ãƒ‡ãƒ¼ã‚¿ãƒ‘スを有効化ã™ã‚‹ã«ã¯ã€ã“ã®å€¤ã‚’ " "'netdev' ã«è¨­å®šã—ã¾ã™ã€‚" msgid "OVS vhost-user socket directory." msgstr "OVS ã® vhost-user ソケットディレクトリー。" #, python-format msgid "Object action %(action)s failed because: %(reason)s." msgstr "" "オブジェクトã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ %(action)s ã¯æ¬¡ã®åŽŸå› ã§å¤±æ•—ã—ã¾ã—ãŸ: %(reason)s。" msgid "Only admin can view or configure quota" msgstr "admin ã®ã¿ãŒå‰²ã‚Šå½“ã¦é‡ã‚’表示ã¾ãŸã¯æ§‹æˆã§ãã¾ã™" msgid "Only admin is authorized to access quotas for another tenant" msgstr "別ã®ãƒ†ãƒŠãƒ³ãƒˆã®å‰²ã‚Šå½“ã¦é‡ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ãŒè¨±å¯ã•れã¦ã„ã‚‹ã®ã¯ç®¡ç†è€…ã®ã¿ã§ã™" msgid "Only admins can manipulate policies on objects they do not own" msgstr "" "自分ãŒã‚ªãƒ¼ãƒŠãƒ¼ã§ã¯ãªã„オブジェクトã®ãƒãƒªã‚·ãƒ¼ã‚’æ“作ã§ãã‚‹ã®ã¯ç®¡ç†è€…ã«é™ã‚‰ã‚Œã¾" "ã™" msgid "Only allowed to update rules for one security profile at a time" msgstr "一度㫠1 ã¤ã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¼ãƒ—ロファイルã®ãƒ«ãƒ¼ãƒ«ã®ã¿ã‚’æ›´æ–°ã§ãã¾ã™" msgid "Only remote_ip_prefix or remote_group_id may be provided." msgstr "remote_ip_prefix ã¾ãŸã¯ remote_group_id ã®ã¿ã‚’指定ã§ãã¾ã™ã€‚" msgid "OpenFlow interface to use." msgstr "使用ã™ã‚‹ OpenFlow インターフェース。" #, python-format msgid "" "Operation %(op)s is not supported for device_owner %(device_owner)s on port " "%(port_id)s." msgstr "" "æ“作 %(op)s ã¯ãƒãƒ¼ãƒˆ %(port_id)s ã® device_owner %(device_owner)s ã§ã¯ã‚µãƒãƒ¼" "トã•れã¦ã„ã¾ã›ã‚“。" #, python-format msgid "Operation not supported on device %(dev_name)s" msgstr "デãƒã‚¤ã‚¹ %(dev_name)s ã§ã‚µãƒãƒ¼ãƒˆã•れãªã„処ç†" msgid "" "Ordered list of network_types to allocate as tenant networks. The default " "value 'local' is useful for single-box testing but provides no connectivity " "between hosts." msgstr "" "テナントãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã¨ã—ã¦å‰²ã‚Šå½“ã¦ã‚‹ network_types を一定ã®é †åºã«ä¸¦ã¹ãŸãƒªã‚¹" "ト。デフォルト値㮠'local' ã¯ã‚·ãƒ³ã‚°ãƒ«ãƒœãƒƒã‚¯ã‚¹ãƒ†ã‚¹ãƒˆã«å½¹ç«‹ã¤ã‚‚ã®ã®ã€ãƒ›ã‚¹ãƒˆé–“ã®" "æŽ¥ç¶šã¯æä¾›ã—ã¾ã›ã‚“。" msgid "Override the default dnsmasq settings with this file." msgstr "" "ã“ã®ãƒ•ァイルを使用ã—ã¦ã€ãƒ‡ãƒ•ォルト㮠dnsmasq 設定をオーãƒãƒ¼ãƒ©ã‚¤ãƒ‰ã—ã¾ã™ã€‚" msgid "Owner type of the device: network/compute" msgstr "デãƒã‚¤ã‚¹ã®æ‰€æœ‰è€…タイプ: network/compute" msgid "POST requests are not supported on this resource." msgstr "POST è¦æ±‚ã¯ã€ã“ã®ãƒªã‚½ãƒ¼ã‚¹ã§ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。" #, python-format msgid "Package %s not installed" msgstr "パッケージ %s ã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã¾ã›ã‚“" #, python-format msgid "Parsing bridge_mappings failed: %s." msgstr "bridge_mappings ã®è§£æžã«å¤±æ•—ã—ã¾ã—ãŸ: %s。" msgid "Password for connecting to designate in admin context" msgstr "管ç†è€…ã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã«ãŠã‘ã‚‹ designate ã¸ã®æŽ¥ç¶šç”¨ãƒ‘スワード" msgid "Path to PID file for this process" msgstr "ã“ã®ãƒ—ロセス㮠PID ファイルã®ãƒ‘ス" msgid "Path to the router directory" msgstr "ルーターディレクトリーã®ãƒ‘ス" msgid "Peer patch port in integration bridge for tunnel bridge." msgstr "トンãƒãƒ«ãƒ–リッジã®çµ±åˆãƒ–リッジ内ã®ãƒ”アパッãƒãƒãƒ¼ãƒˆã€‚" msgid "Peer patch port in tunnel bridge for integration bridge." msgstr "çµ±åˆãƒ–リッジã®ãƒˆãƒ³ãƒãƒ«ãƒ–リッジ内ã®ãƒ”アパッãƒãƒãƒ¼ãƒˆã€‚" msgid "Per-tenant subnet pool prefix quota exceeded." msgstr "" "テナントã”ã¨ã®ã‚µãƒ–ãƒãƒƒãƒˆãƒ—ールã®ãƒ—レフィックスã®ã‚¯ã‚©ãƒ¼ã‚¿ã‚’è¶…éŽã—ã¾ã—ãŸã€‚" msgid "Phase upgrade options do not accept revision specification" msgstr "" "フェーズã®ã‚¢ãƒƒãƒ—グレードオプションã§ã¯ã€å¤‰æ›´ã®æŒ‡å®šã‚’行ã†ã“ã¨ã¯ã§ãã¾ã›ã‚“" msgid "Ping timeout" msgstr "ping タイムアウト" msgid "Plugin does not support updating provider attributes" msgstr "プラグインã§ã¯ã€ãƒ—ロãƒã‚¤ãƒ€ãƒ¼å±žæ€§ã®æ›´æ–°ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“" #, python-format msgid "Port %(id)s does not have fixed ip %(address)s" msgstr "ãƒãƒ¼ãƒˆ %(id)s ã« Fixed IP %(address)s ãŒã‚りã¾ã›ã‚“" #, python-format msgid "Port %(port_id)s is already acquired by another DHCP agent" msgstr "ãƒãƒ¼ãƒˆ %(port_id)s ã¯æ—¢ã«åˆ¥ã® DHCP エージェントã«å–å¾—ã•れã¦ã„ã¾ã™ã€‚" #, python-format msgid "" "Port %s has multiple fixed IPv4 addresses. Must provide a specific IPv4 " "address when assigning a floating IP" msgstr "" "ãƒãƒ¼ãƒˆ %s ã«ã¯è¤‡æ•°ã®å›ºå®š IPv4 アドレスãŒã‚りã¾ã™ã€‚Floating IP を割り当ã¦ã‚‹éš›" "ã«ã¯ã€ç‰¹å®šã® IPv4 アドレスをæä¾›ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™" msgid "" "Port to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "OpenFlow 接続をリッスンã™ã‚‹ãƒãƒ¼ãƒˆã€‚「ãƒã‚¤ãƒ†ã‚£ãƒ–ã€ã®ãƒ‰ãƒ©ã‚¤ãƒãƒ¼ã§ã®ã¿ä½¿ç”¨ã§ãã¾" "ã™ã€‚" #, python-format msgid "Prefix '%(prefix)s' not supported in IPv%(version)s pool." msgstr "" "プレフィックス '%(prefix)s' 㯠IPv%(version)s プールã§ã¯ã‚µãƒãƒ¼ãƒˆã•れã¾ã›ã‚“。" msgid "Prefix Delegation can only be used with IPv6 subnets." msgstr "" "プレフィックスデリゲーションãŒä½¿ç”¨ã§ãã‚‹ã®ã¯ IPv6 ã®ã‚µãƒ–ãƒãƒƒãƒˆã«é™ã‚‰ã‚Œã¾ã™ã€‚" msgid "Private key of client certificate." msgstr "クライアント証明書ã®ç§˜å¯†éµã€‚" #, python-format msgid "Probe %s deleted" msgstr "プローブ %s ãŒå‰Šé™¤ã•れã¾ã—ãŸ" #, python-format msgid "Probe created : %s " msgstr "作æˆã•れãŸãƒ—ローブ: %s " msgid "Process is already started" msgstr "ãƒ—ãƒ­ã‚»ã‚¹ãŒæ—¢ã«å®Ÿè¡Œã•れã¦ã„ã¾ã™" msgid "Process is not running." msgstr "プロセスãŒå®Ÿè¡Œã•れã¦ã„ã¾ã›ã‚“" msgid "Protocol to access nova metadata, http or https" msgstr "Nova メタデータã€httpã€ã¾ãŸã¯ https ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ãŸã‚ã®ãƒ—ロトコル" #, python-format msgid "Provider name %(name)s is limited by %(len)s characters" msgstr "プロãƒã‚¤ãƒ€ãƒ¼å %(name)s ã®åˆ¶é™ã¯ %(len)s 文字ã¾ã§ã§ã™ã€‚" #, python-format msgid "QoS Policy %(policy_id)s is used by %(object_type)s %(object_id)s." msgstr "" "QoS ãƒãƒªã‚·ãƒ¼ %(policy_id)s 㯠%(object_type)s %(object_id)s ã«ã‚ˆã£ã¦ä½¿ç”¨ã•れ" "ã¦ã„ã¾ã™ã€‚" #, python-format msgid "" "QoS binding for network %(net_id)s and policy %(policy_id)s could not be " "found." msgstr "" "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ %(net_id)s ã¨ãƒãƒªã‚·ãƒ¼ %(policy_id)s ã«ãƒã‚¤ãƒ³ãƒ‰ã™ã‚‹ QoS ãŒè¦‹ã¤ã‹" "りã¾ã›ã‚“ã§ã—ãŸã€‚" #, python-format msgid "" "QoS binding for port %(port_id)s and policy %(policy_id)s could not be found." msgstr "" "ãƒãƒ¼ãƒˆ %(port_id)s ã¨ãƒãƒªã‚·ãƒ¼ %(policy_id)s ã«ãƒã‚¤ãƒ³ãƒ‰ã™ã‚‹ QoS ãŒè¦‹ã¤ã‹ã‚Šã¾ã›" "ã‚“ã§ã—ãŸã€‚" #, python-format msgid "QoS policy %(policy_id)s could not be found." msgstr "QoS ãƒãƒªã‚·ãƒ¼ %(policy_id)s ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚" #, python-format msgid "QoS rule %(rule_id)s for policy %(policy_id)s could not be found." msgstr "" "ãƒãƒªã‚·ãƒ¼ %(policy_id)s ã® QoS ルール %(rule_id)s ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚" #, python-format msgid "RBAC policy of type %(object_type)s with ID %(id)s not found" msgstr "" "ID %(id)s ã‚’æŒã¤ã‚¿ã‚¤ãƒ— %(object_type)s ã® RBAC ãƒãƒªã‚·ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" #, python-format msgid "" "RBAC policy on object %(object_id)s cannot be removed because other objects " "depend on it.\n" "Details: %(details)s" msgstr "" "ä»–ã®ã‚ªãƒ–ジェクトãŒã“ã® RBAC ãƒãƒªã‚·ãƒ¼ã«ä¾å­˜ã—ã¦ã„ã‚‹ãŸã‚ã€ã‚ªãƒ–ジェクト " "%(object_id)s ã«å¯¾ã™ã‚‹ RBAC ãƒãƒªã‚·ãƒ¼ã‚’削除ã§ãã¾ã›ã‚“。\n" "詳細: %(details)s" msgid "" "Range of seconds to randomly delay when starting the periodic task scheduler " "to reduce stampeding. (Disable by setting to 0)" msgstr "" "集中状態を緩和ã™ã‚‹ãŸã‚ã€å®šæœŸã‚¿ã‚¹ã‚¯ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ©ãƒ¼ã®é–‹å§‹æ™‚ã«æŒ¿å…¥ã™ã‚‹ãƒ©ãƒ³ãƒ€ãƒ ãª" "é…延時間 (ç§’) ã®ç¯„囲。(無効ã«ã™ã‚‹ã«ã¯ 0 ã«è¨­å®š)" msgid "Ranges must be in the same IP version" msgstr "範囲ã¯åŒã˜ IP ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™" msgid "Ranges must be netaddr.IPRange" msgstr "範囲㯠netaddr.IPRange ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™" msgid "Ranges must not overlap" msgstr "範囲ã¯é‡è¤‡ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.EUI type." msgstr "" "タイプ '%(type)s' ã¨å€¤ '%(value)s' ã‚’å—é ˜ã—ã¾ã—ãŸãŒã€äºˆæœŸã—ã¦ã„ãŸã®ã¯ " "netaddr.EUI タイプã§ã™" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPAddress " "type." msgstr "" "タイプ '%(type)s' ã¨å€¤ '%(value)s' ã‚’å—é ˜ã—ã¾ã—ãŸãŒã€äºˆæœŸã—ã¦ã„ãŸã®ã¯ " "netaddr.IPAddress タイプã§ã™ã€‚" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPNetwork " "type." msgstr "" "タイプ '%(type)s' ã¨å€¤ '%(value)s' ã‚’å—é ˜ã—ã¾ã—ãŸãŒã€äºˆæœŸã—ã¦ã„ãŸã®ã¯ " "netaddr.IPNetwork タイプã§ã™ã€‚" #, python-format msgid "" "Release aware branch labels (%s) are deprecated. Please switch to expand@ " "and contract@ labels." msgstr "" "リリースをèªè­˜ã™ã‚‹åˆ†å²ãƒ©ãƒ™ãƒ« (%s) ã¯æä¾›ã‚’çµ‚äº†ã—ã¦ã„ã¾ã™ã€‚expand@ ラベル㨠" "contract@ ラベルã«å¤‰æ›´ã—ã¦ãã ã•ã„。" msgid "Remote metadata server experienced an internal server error." msgstr "リモートメタデータサーãƒãƒ¼ã§å†…部サーãƒãƒ¼ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚" msgid "" "Repository does not contain HEAD files for contract and expand branches." msgstr "リãƒã‚¸ãƒˆãƒªãƒ¼ã«ç¸®å°åˆ†å²ã¨æ‹¡å¼µåˆ†å²ã® HEAD ファイルãŒå«ã¾ã‚Œã¦ã„ã¾ã›ã‚“。" msgid "" "Representing the resource type whose load is being reported by the agent. " "This can be \"networks\", \"subnets\" or \"ports\". When specified (Default " "is networks), the server will extract particular load sent as part of its " "agent configuration object from the agent report state, which is the number " "of resources being consumed, at every report_interval.dhcp_load_type can be " "used in combination with network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler When the network_scheduler_driver is " "WeightScheduler, dhcp_load_type can be configured to represent the choice " "for the resource being balanced. Example: dhcp_load_type=networks" msgstr "" "è² è·ãŒã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã«ã‚ˆã£ã¦å ±å‘Šã•れã¦ã„るリソースタイプを表ã—ã¾ã™ã€‚ã“ã®ã‚¿ã‚¤ãƒ—" "ã«ã¯ã€\"networks\"ã€\"subnets\"ã€ã¾ãŸã¯ \"ports\" ãŒã‚りã¾ã™ã€‚指定ã—ãŸå ´åˆ " "(デフォルト㯠networks)ã€ã‚µãƒ¼ãƒãƒ¼ã¯ã€ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆãƒ¬ãƒãƒ¼ãƒˆçŠ¶æ…‹ " "(report_interval ã”ã¨ã«æ¶ˆè²»ã•れるリソース数) ã‹ã‚‰ãã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆæ§‹æˆã‚ªãƒ–ジェ" "クトã®ä¸€éƒ¨ã¨ã—ã¦é€ä¿¡ã•れãŸç‰¹å®šã®è² è·ã‚’抽出ã—ã¾ã™ã€‚dhcp_load_type 㯠" "network_scheduler_driver = neutron.scheduler.dhcp_agent_scheduler." "WeightScheduler ã¨çµ„ã¿åˆã‚ã›ã¦ä½¿ç”¨ã§ãã¾ã™ã€‚network_scheduler_driver ㌠" "WeightScheduler ã®å ´åˆã€dhcp_load_type ã¯å¹³è¡¡ã‚’å–るリソースã®é¸æŠžè‚¢ã‚’表ã™ã‚ˆã†" "ã«æ§‹æˆã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚例: dhcp_load_type=networks" msgid "Request Failed: internal server error while processing your request." msgstr "è¦æ±‚ãŒå¤±æ•—ã—ã¾ã—ãŸã€‚è¦æ±‚ã®å‡¦ç†ä¸­ã«å†…部サーãƒãƒ¼ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚" msgid "" "Reset flow table on start. Setting this to True will cause brief traffic " "interruption." msgstr "" "起動時ã«ãƒ•ローテーブルをリセットã—ã¾ã™ã€‚ã“ã®å€¤ã‚’ True ã«è¨­å®šã™ã‚‹ã¨ã€ä¸€æ™‚çš„ã«" "トラフィックãŒä¸­æ–­ã—ã¾ã™ã€‚" #, python-format msgid "Resource %(resource)s %(resource_id)s could not be found." msgstr "リソース %(resource)s %(resource_id)s ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚" #, python-format msgid "Resource %(resource_id)s of type %(resource_type)s not found" msgstr "タイプ %(resource_type)s ã®ãƒªã‚½ãƒ¼ã‚¹ %(resource_id)s ã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" #, python-format msgid "" "Resource '%(resource_id)s' is already associated with provider " "'%(provider)s' for service type '%(service_type)s'" msgstr "" "リソース '%(resource_id)s' ã¯æ—¢ã«ã‚µãƒ¼ãƒ“スタイプ '%(service_type)s' ã®ãƒ—ロãƒã‚¤" "ダー '%(provider)s' ã«é–¢é€£ä»˜ã‘られã¦ã„ã¾ã™" msgid "Resource body required" msgstr "リソース本文ãŒå¿…è¦ã§ã™" msgid "Resource not found." msgstr "リソースãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。" msgid "Resources required" msgstr "リソースãŒå¿…è¦ã§ã™" msgid "" "Root helper application. Use 'sudo neutron-rootwrap /etc/neutron/rootwrap." "conf' to use the real root filter facility. Change to 'sudo' to skip the " "filtering and just run the command directly." msgstr "" "ルートヘルパーã®ã‚¢ãƒ—リケーション。実際ã®ãƒ«ãƒ¼ãƒˆãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã®æ©Ÿèƒ½ã‚’使用ã™ã‚‹ãŸã‚" "ã«ã¯ã€'sudo neutron-rootwrap /etc/neutron/rootwrap.conf' を使用ã—ã¾ã™ã€‚フィル" "タリングをスキップã—ã¦ã€ç›´æŽ¥ã‚³ãƒžãƒ³ãƒ‰ã‚’実行ã™ã‚‹ã«ã¯ã€'sudo' を使用ã—ã¾ã™ã€‚" msgid "Root permissions are required to drop privileges." msgstr "特権を除去ã™ã‚‹ã«ã¯ãƒ«ãƒ¼ãƒˆè¨±å¯ãŒå¿…è¦ã§ã™ã€‚" #, python-format msgid "Router '%(router_id)s' is not compatible with this agent." msgstr "ルーター '%(router_id)s' ã¯ã“ã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã¨äº’æ›æ€§ãŒã‚りã¾ã›ã‚“。" #, python-format msgid "Router already has a port on subnet %s" msgstr "ルーターã«ã€æ—¢ã«ã‚µãƒ–ãƒãƒƒãƒˆ %s 上ã®ãƒãƒ¼ãƒˆãŒã‚りã¾ã™" msgid "Router port must have at least one fixed IP" msgstr "ルーターãƒãƒ¼ãƒˆã«ã¯ 1 ã¤ä»¥ä¸Šã® Fixed IP を設定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™" #, python-format msgid "Running %(cmd)s (%(desc)s) for %(project)s ..." msgstr "%(project)s ã«å¯¾ã—㦠%(cmd)s (%(desc)s) を実行中ã§ã™..." #, python-format msgid "Running %(cmd)s for %(project)s ..." msgstr "%(project)s ã«å¯¾ã—㦠%(cmd)s を実行中ã§ã™..." msgid "" "Seconds between nodes reporting state to server; should be less than " "agent_down_time, best if it is half or less than agent_down_time." msgstr "" "ノード状態をサーãƒãƒ¼ã«å ±å‘Šã™ã‚‹é–“éš” (ç§’)。agent_down_time 未満ã§ã‚ã‚‹å¿…è¦ãŒã‚" "りã¾ã™ã€‚agent_down_time ã®åŠåˆ†ä»¥ä¸‹ã§ã‚ã‚Œã°æœ€é©ã§ã™ã€‚" msgid "" "Seconds to regard the agent is down; should be at least twice " "report_interval, to be sure the agent is down for good." msgstr "" "エージェントãŒãƒ€ã‚¦ãƒ³ã—ã¦ã„ã‚‹ã¨è¦‹ãªã™ã¾ã§ã®æ™‚é–“ (ç§’)。エージェントãŒå®Œå…¨ã«ãƒ€ã‚¦" "ンã—ã¦ã„ã‚‹ã“ã¨ã‚’確実ã«ã™ã‚‹ã«ã¯ã€ã“ã®å€¤ã‚’å°‘ãªãã¨ã‚‚ report_interval ã® 2 å€ã«" "ã—ã¦ãã ã•ã„。" #, python-format msgid "Security Group %(id)s %(reason)s." msgstr "セキュリティーグループ %(id)s %(reason)s。" #, python-format msgid "Security Group Rule %(id)s %(reason)s." msgstr "セキュリティーグループルール %(id)s %(reason)s。" #, python-format msgid "Security group %(id)s does not exist" msgstr "セキュリティーグループ %(id)s ã¯å­˜åœ¨ã—ã¾ã›ã‚“" #, python-format msgid "Security group rule %(id)s does not exist" msgstr "セキュリティーグループルール %(id)s ã¯å­˜åœ¨ã—ã¾ã›ã‚“" #, python-format msgid "Security group rule already exists. Rule id is %(rule_id)s." msgstr "" "ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¼ã‚°ãƒ«ãƒ¼ãƒ—ãƒ«ãƒ¼ãƒ«ãŒæ—¢ã«å­˜åœ¨ã—ã¦ã„ã¾ã™ã€‚ルール ID 㯠%(rule_id)s ã§" "ã™ã€‚" #, python-format msgid "" "Security group rule for ethertype '%(ethertype)s' not supported. Allowed " "values are %(values)s." msgstr "" "イーサãƒãƒƒãƒˆã‚¿ã‚¤ãƒ— '%(ethertype)s' ã«é–¢ã™ã‚‹ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¼ã‚°ãƒ«ãƒ¼ãƒ—ルールãŒå¯¾å¿œ" "ã—ã¦ã„ã¾ã›ã‚“。許容ã•れる値㯠%(values)s ã§ã™ã€‚" #, python-format msgid "" "Security group rule protocol %(protocol)s not supported. Only protocol " "values %(values)s and integer representations [0 to 255] are supported." msgstr "" "セキュリティーグループルールã®ãƒ—ロトコル %(protocol)s ãŒå¯¾å¿œã—ã¦ã„ã¾ã›ã‚“。使" "用ã§ãã‚‹ã®ã¯ã€ãƒ—ロトコルã®å€¤ %(values)s ã¨æ•´æ•°å€¤ [0 ã‹ã‚‰ 255 ã¾ã§] ã®ã¿ã§ã™ã€‚" msgid "Segments and provider values cannot both be set." msgstr "セグメントã¨ãƒ—ロãƒã‚¤ãƒ€ãƒ¼ã®ä¸¡æ–¹ã‚’設定ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。" msgid "Selects the Agent Type reported" msgstr "報告ã•れる Agent Type ã®é¸æŠž" msgid "" "Send notification to nova when port data (fixed_ips/floatingip) changes so " "nova can update its cache." msgstr "" "nova ãŒãã®ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚’æ›´æ–°ã§ãるよã†ã«ã€ãƒãƒ¼ãƒˆãƒ‡ãƒ¼ã‚¿ (fixed_ips/floatingip) " "ãŒå¤‰æ›´ã•れãŸã¨ãã«é€šçŸ¥ã‚’ nova ã«é€ä¿¡ã—ã¾ã™ã€‚" msgid "Send notification to nova when port status changes" msgstr "ãƒãƒ¼ãƒˆçŠ¶æ…‹ã®å¤‰æ›´æ™‚ã® nova ã¸ã®é€šçŸ¥é€ä¿¡" #, python-format msgid "" "Service provider '%(provider)s' could not be found for service type " "%(service_type)s" msgstr "" "サービスタイプ %(service_type)s ã®ã‚µãƒ¼ãƒ“スプロãƒã‚¤ãƒ€ãƒ¼ '%(provider)s' ã¯è¦‹ã¤" "ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ" msgid "Service to handle DHCPv6 Prefix delegation." msgstr "DHCPv6 ã®ãƒ—レフィックスデリゲーションを処ç†ã™ã‚‹ã‚µãƒ¼ãƒ“ス。" #, python-format msgid "Service type %(service_type)s does not have a default service provider" msgstr "" "サービスタイプ %(service_type)s ã«ã¯ãƒ‡ãƒ•ォルトã®ã‚µãƒ¼ãƒ“スプロãƒã‚¤ãƒ€ãƒ¼ãŒã‚りã¾" "ã›ã‚“" msgid "" "Set new timeout in seconds for new rpc calls after agent receives SIGTERM. " "If value is set to 0, rpc timeout won't be changed" msgstr "" "エージェントã«ã‚ˆã‚‹ SIGTERM å—ä¿¡å¾Œã®æ–°è¦ rpc 呼ã³å‡ºã—ã®æ–°è¦ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆ(ç§’) ã‚’" "設定ã—ã¾ã™ã€‚値を 0 ã«è¨­å®šã™ã‚‹ã¨ã€rpc タイムアウトã¯å¤‰æ›´ã•れã¾ã›ã‚“" msgid "" "Set or un-set the don't fragment (DF) bit on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "GRE/VXLAN トンãƒãƒ«ã‚’æ§‹æˆã—ãŸç™ºä¿¡ IP パケットã§ã€ãƒ•ãƒ©ã‚°ãƒ¡ãƒ³ãƒˆç¦æ­¢ (DF) ビット" "を設定ã¾ãŸã¯è¨­å®šè§£é™¤ã—ã¾ã™ã€‚" msgid "" "Set or un-set the tunnel header checksum on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "GRE/VXLAN トンãƒãƒ« を使用ã™ã‚‹ç¾åœ¨æœ‰åŠ¹ãª IP パケットã«å¯¾ã™ã‚‹ãƒˆãƒ³ãƒãƒ«ãƒ˜ãƒƒãƒ€ãƒ¼ã®" "ãƒã‚§ãƒƒã‚¯ã‚µãƒ ã®è¨­å®šã¾ãŸã¯è¨­å®šè§£é™¤ã‚’行ã„ã¾ã™ã€‚" msgid "Shared address scope can't be unshared" msgstr "共有アドレススコープã¯å…±æœ‰ã‚’解除ã§ãã¾ã›ã‚“" msgid "String prefix used to match IPset names." msgstr "IPset åã¨ã®ãƒžãƒƒãƒãƒ³ã‚°ã‚’行ã†ãŸã‚ã«ä½¿ç”¨ã™ã‚‹æ–‡å­—列ã®ãƒ—レフィックス。" #, python-format msgid "Sub-project %s not installed." msgstr "サブプロジェクト %s ã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã¾ã›ã‚“" msgid "Subnet for router interface must have a gateway IP" msgstr "ルーターインターフェースã®ã‚µãƒ–ãƒãƒƒãƒˆã«ã¯ã‚²ãƒ¼ãƒˆã‚¦ã‚§ã‚¤ IP ãŒå¿…è¦ã§ã™" #, python-format msgid "Subnet pool %(subnetpool_id)s could not be found." msgstr "サブãƒãƒƒãƒˆãƒ—ール %(subnetpool_id)s ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚" msgid "Subnet pool has existing allocations" msgstr "サブãƒãƒƒãƒˆãƒ—ãƒ¼ãƒ«ã«æ—¢å­˜ã®å‰²ã‚Šå½“ã¦ãŒã‚りã¾ã™" msgid "Subnet used for the l3 HA admin network." msgstr "l3 HA 管ç†ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«ä½¿ç”¨ã•れるサブãƒãƒƒãƒˆã€‚" msgid "" "Subnets hosted on the same network must be allocated from the same subnet " "pool." msgstr "" "åŒã˜ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ä¸Šã§ãƒ›ã‚¹ãƒˆã•れるサブãƒãƒƒãƒˆã¯ã€åŒã˜ã‚µãƒ–ãƒãƒƒãƒˆãƒ—ールã‹ã‚‰å‰²ã‚Šå½“" "ã¦ã‚‰ã‚Œã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "" "System-wide flag to determine the type of router that tenants can create. " "Only admin can override." msgstr "" "テナントã§ä½œæˆå¯èƒ½ãªãƒ«ãƒ¼ã‚¿ãƒ¼ã®ã‚¿ã‚¤ãƒ—を判別ã™ã‚‹ãŸã‚ã®ã‚·ã‚¹ãƒ†ãƒ å…¨ä½“ã®ãƒ•ラグ。管" "ç†è€…ã®ã¿ãŒã‚ªãƒ¼ãƒãƒ¼ãƒ©ã‚¤ãƒ‰ã§ãã¾ã™ã€‚" msgid "TCP Port used by Neutron metadata namespace proxy." msgstr "Neutron メタデータåå‰ç©ºé–“プロキシーãŒä½¿ç”¨ã™ã‚‹ TCP Port" msgid "TCP Port used by Nova metadata server." msgstr "Nova メタデータサーãƒãƒ¼ã«ã‚ˆã£ã¦ä½¿ç”¨ã•れる TCP ãƒãƒ¼ãƒˆã€‚" msgid "TTL for vxlan interface protocol packets." msgstr "vxlan インターフェースプロトコルパケット㮠TTL。" #, python-format msgid "Tag %(tag)s could not be found." msgstr "ã‚¿ã‚° %(tag)s ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚" #, python-format msgid "Tenant %(tenant_id)s not allowed to create %(resource)s on this network" msgstr "" "テナント %(tenant_id)s ã¯ã€ã“ã®ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã§ã® %(resource)s ã®ä½œæˆã‚’許å¯ã•れ" "ã¦ã„ã¾ã›ã‚“" msgid "Tenant id for connecting to designate in admin context" msgstr "管ç†è€…ã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã«ãŠã‘ã‚‹ designate ã¸ã®æŽ¥ç¶šç”¨ãƒ†ãƒŠãƒ³ãƒˆ ID" msgid "Tenant name for connecting to designate in admin context" msgstr "管ç†è€…ã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã«ãŠã‘ã‚‹ designate ã¸ã®æŽ¥ç¶šç”¨ãƒ†ãƒŠãƒ³ãƒˆå" msgid "Tenant network creation is not enabled." msgstr "テナントãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®ä½œæˆã¯ä½¿ç”¨ã§ãã¾ã›ã‚“。" msgid "Tenant-id was missing from quota request." msgstr "テナント ID ãŒã‚¯ã‚©ãƒ¼ã‚¿è¦æ±‚ã«ã‚りã¾ã›ã‚“ã§ã—ãŸã€‚" msgid "" "The 'gateway_external_network_id' option must be configured for this agent " "as Neutron has more than one external network." msgstr "" "Neutron ã«è¤‡æ•°ã®å¤–部ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãŒã‚ã‚‹ãŸã‚ã€ã“ã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆã«å¯¾ã—㦠" "'gateway_external_network_id' オプションを設定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "" "The DHCP agent will resync its state with Neutron to recover from any " "transient notification or RPC errors. The interval is number of seconds " "between attempts." msgstr "" "DHCP エージェント㌠Neutron ã¨ã®é–“ã§çŠ¶æ…‹ã®åŒæœŸã‚’å–ã‚‹ã“ã¨ã«ã‚ˆã‚Šã€ä¸€æ™‚çš„ãªé€šçŸ¥" "ã‚„ RPC エラーã‹ã‚‰ã®ãƒªã‚«ãƒãƒªãƒ¼ã‚’行ã„ã¾ã™ã€‚é–“éš”ã¯åŒæœŸã®è©¦è¡Œçµ‚了ã‹ã‚‰æ¬¡ã®è©¦è¡Œã¾ã§" "ã®ç§’æ•°ã§ã™ã€‚" msgid "" "The DHCP server can assist with providing metadata support on isolated " "networks. Setting this value to True will cause the DHCP server to append " "specific host routes to the DHCP request. The metadata service will only be " "activated when the subnet does not contain any router port. The guest " "instance must be configured to request host routes via DHCP (Option 121). " "This option doesn't have any effect when force_metadata is set to True." msgstr "" "DHCP サーãƒãƒ¼ã¯å­¤ç«‹ã—ãŸãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«å¯¾ã—ã¦ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã‚µãƒãƒ¼ãƒˆã‚’æä¾›ã™ã‚‹ã“ã¨ãŒ" "ã§ãã¾ã™ã€‚ã“ã®å€¤ã‚’ True ã«è¨­å®šã™ã‚‹ã¨ã€DHCP サーãƒãƒ¼ã¯ DHCP è¦æ±‚ã«å¯¾ã™ã‚‹ç‰¹å®šã®" "ホストã¸ã®çµŒè·¯ã‚’追加ã§ãã¾ã™ã€‚ã“ã®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã‚µãƒ¼ãƒ“ã‚¹ãŒæœ‰åŠ¹åŒ–ã•れるã®ã¯ã€ã‚µãƒ–" "ãƒãƒƒãƒˆã«ãƒ«ãƒ¼ã‚¿ãƒ¼ã®ãƒãƒ¼ãƒˆãŒå«ã¾ã‚Œãªã„å ´åˆã«é™ã‚‰ã‚Œã¾ã™ã€‚ゲストインスタンスã«" "ã¯ã€DHCP (オプション 121) 経由ã§ãƒ›ã‚¹ãƒˆã®çµŒè·¯ã‚’è¦æ±‚ã™ã‚‹ã‚ˆã†è¨­å®šã‚’行ã†å¿…è¦ãŒã‚" "りã¾ã™ã€‚force_metadata ã‚’ True ã«è¨­å®šã™ã‚‹å ´åˆã€ã“ã®ã‚ªãƒ—ã‚·ãƒ§ãƒ³ã¯æ©Ÿèƒ½ã—ã¾ã›ã‚“。" msgid "The UDP port to use for VXLAN tunnels." msgstr "VXLAN トンãƒãƒ«ã§ä½¿ç”¨ã™ã‚‹ UDP ãƒãƒ¼ãƒˆã€‚" #, python-format msgid "" "The address allocation request could not be satisfied because: %(reason)s" msgstr "%(reason)s ã®ãŸã‚ã€ã‚¢ãƒ‰ãƒ¬ã‚¹ã®å‰²ã‚Šå½“ã¦è¦æ±‚ã«å¯¾å¿œã§ãã¾ã›ã‚“ã§ã—ãŸ" msgid "The advertisement interval in seconds" msgstr "通知間隔 (ç§’)" #, python-format msgid "The allocation pool %(pool)s is not valid." msgstr "割り当ã¦ãƒ—ール %(pool)s ãŒç„¡åйã§ã™ã€‚" #, python-format msgid "" "The allocation pool %(pool)s spans beyond the subnet cidr %(subnet_cidr)s." msgstr "" "割り当ã¦ãƒ—ール %(pool)s ãŒã‚µãƒ–ãƒãƒƒãƒˆ CIDR %(subnet_cidr)s ã‚’è¶Šãˆã¦ã„ã¾ã™ã€‚" msgid "" "The base MAC address Neutron will use for VIFs. The first 3 octets will " "remain unchanged. If the 4th octet is not 00, it will also be used. The " "others will be randomly generated." msgstr "" "Neutron ㌠VIF 用ã«ä½¿ç”¨ã™ã‚‹åŸºæœ¬ã® MAC アドレス。最åˆã® 3 ã¤ã®ã‚ªã‚¯ãƒ†ãƒƒãƒˆã¯å¤‰æ›´" "ã—ã¾ã›ã‚“。4 ã¤ç›®ã®ã‚ªã‚¯ãƒ†ãƒƒãƒˆãŒ 00 ã®å ´åˆã€ã“れも使用ã§ãã¾ã™ã€‚ãã®ä»–ã®ã‚ªã‚¯" "テットã¯ãƒ©ãƒ³ãƒ€ãƒ ã«ç”Ÿæˆã•れã¾ã™ã€‚" msgid "" "The base mac address used for unique DVR instances by Neutron. The first 3 " "octets will remain unchanged. If the 4th octet is not 00, it will also be " "used. The others will be randomly generated. The 'dvr_base_mac' *must* be " "different from 'base_mac' to avoid mixing them up with MAC's allocated for " "tenant ports. A 4 octet example would be dvr_base_mac = fa:16:3f:4f:00:00. " "The default is 3 octet" msgstr "" "Neutron ã«ã‚ˆã£ã¦ç‹¬è‡ªã® DVR インスタンスã«ä½¿ç”¨ã•れる基本㮠MAC アドレス。最åˆ" "ã® 3 ã¤ã®ã‚ªã‚¯ãƒ†ãƒƒãƒˆã¯å¤‰æ›´ã•れã¾ã›ã‚“。4 ã¤ç›®ã®ã‚ªã‚¯ãƒ†ãƒƒãƒˆãŒ 00 ã§ãªã„å ´åˆã¯ã€" "ã“ã®ã‚ªã‚¯ãƒ†ãƒƒãƒˆã‚‚使用ã§ãã¾ã™ã€‚ãã®ä»–ã®ã‚ªã‚¯ãƒ†ãƒƒãƒˆã¯ãƒ©ãƒ³ãƒ€ãƒ ã«ç”Ÿæˆã•れã¾ã™ã€‚ テ" "ナントãƒãƒ¼ãƒˆã«å‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸ MAC アドレスã¨ã®æ··åŒã‚’é¿ã‘ã‚‹ãŸã‚ã«ã€ " "'dvr_base_mac' 㯠'base_mac' ã¨ã¯é•ã†å€¤ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚4 ã¤ç›®ã®ã‚ªã‚¯ãƒ†ãƒƒ" "トã®ä¾‹ã¨ã—ã¦ã¯ã€dvr_base_mac = fa:16:3f:4f:00:00 ãŒã‚りã¾ã™ã€‚デフォルトã¯ã€3 " "ã¤ç›®ã®ã‚ªã‚¯ãƒ†ãƒƒãƒˆã§ã™ã€‚" msgid "The core plugin Neutron will use" msgstr "Neutron ãŒä½¿ç”¨ã™ã‚‹ã‚³ã‚¢ãƒ—ラグイン" msgid "The driver used to manage the DHCP server." msgstr "DHCP サーãƒãƒ¼ã®ç®¡ç†ã«ä½¿ç”¨ã•れるドライãƒãƒ¼ã€‚" msgid "The driver used to manage the virtual interface." msgstr "仮想インターフェースã®ç®¡ç†ã«ä½¿ç”¨ã•れるドライãƒãƒ¼ã€‚" msgid "" "The email address to be used when creating PTR zones. If not specified, the " "email address will be admin@" msgstr "" "PTR ゾーンã®ä½œæˆæ™‚ã«ä½¿ç”¨ã™ã‚‹ E メールアドレス。指定ã—ãªã„å ´åˆã€E メールアドレ" "ス㯠admin@ ã«ãªã‚Šã¾ã™ã€‚" #, python-format msgid "" "The following device_id %(device_id)s is not owned by your tenant or matches " "another tenants router." msgstr "" "次㮠device_id %(device_id)s ã¯ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ†ãƒŠãƒ³ãƒˆã«ã‚ˆã£ã¦æ‰€æœ‰ã•れã¦ã„ãªã„ã‹ã€" "ã¾ãŸã¯åˆ¥ã®ãƒ†ãƒŠãƒ³ãƒˆãƒ«ãƒ¼ã‚¿ãƒ¼ã¨ä¸€è‡´ã—ã¾ã™ã€‚" msgid "The interface for interacting with the OVSDB" msgstr "OVSDB ã¨ç›¸äº’作用ã™ã‚‹ãŸã‚ã®ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェース" msgid "" "The maximum number of items returned in a single response, value was " "'infinite' or negative integer means no limit" msgstr "" "1 回ã®å¿œç­”ã§æœ€å¤§æ•°ã®é …ç›®ãŒè¿”ã•れã¾ã—ãŸã€‚値㯠'infinite' ã¾ãŸã¯ (無制é™ã‚’æ„味" "ã™ã‚‹) è² ã®æ•´æ•°ã§ã—ãŸ" #, python-format msgid "" "The network %(network_id)s has been already hosted by the DHCP Agent " "%(agent_id)s." msgstr "" "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ %(network_id)s ã¯ã€æ—¢ã« DHCP エージェント %(agent_id)s ã«ã‚ˆã£ã¦" "ホストã•れã¦ã„ã¾ã™ã€‚" #, python-format msgid "" "The network %(network_id)s is not hosted by the DHCP agent %(agent_id)s." msgstr "" "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ %(network_id)s 㯠DHCP エージェント %(agent_id)s ã«ã‚ˆã£ã¦ãƒ›ã‚¹ãƒˆ" "ã•れã¦ã„ã¾ã›ã‚“。" msgid "" "The network type to use when creating the HA network for an HA router. By " "default or if empty, the first 'tenant_network_types' is used. This is " "helpful when the VRRP traffic should use a specific network which is not the " "default one." msgstr "" "HA ルーター用㫠HA ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’作æˆã™ã‚‹éš›ã«ä½¿ç”¨ã™ã‚‹ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¿ã‚¤ãƒ—。デ" "フォルトã¾ãŸã¯ã“ã®å€¤ãŒç©ºã®å ´åˆã€æœ€åˆã® 'tenant_network_types' ãŒä½¿ç”¨ã•れã¾" "ã™ã€‚VRRP トラフィックãŒãƒ‡ãƒ•ォルトã§ã¯ãªã„特定ã®ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’使用ã—ãªã‘れã°ãª" "らãªã„å ´åˆã«ã¯ã€ã“ã®è¨­å®šãŒå½¹ç«‹ã¡ã¾ã™ã€‚" msgid "" "The number of seconds the agent will wait between polling for local device " "changes." msgstr "" "ローカルデãƒã‚¤ã‚¹ã®å¤‰æ›´ã®ãƒãƒ¼ãƒªãƒ³ã‚°é–“ã«ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆãŒå¾…機ã™ã‚‹é–“éš” (ç§’)。" msgid "" "The number of seconds to wait before respawning the ovsdb monitor after " "losing communication with it." msgstr "" "ovsdb モニターã¨ã®é€šä¿¡ãŒé€”çµ¶ãˆãŸå¾Œã§ ovsdb モニターをå†ä½œæˆã™ã‚‹å‰ã«å¾…機ã™ã‚‹æ™‚" "é–“ (ç§’)" msgid "The number of sort_keys and sort_dirs must be same" msgstr "sort_keys 㨠sort_dirs ã®æ•°ã¯åŒã˜ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“" msgid "" "The path for API extensions. Note that this can be a colon-separated list of " "paths. For example: api_extensions_path = extensions:/path/to/more/exts:/" "even/more/exts. The __path__ of neutron.extensions is appended to this, so " "if your extensions are in there you don't need to specify them here." msgstr "" "API 強化機能ã®ãƒ‘ス。ã“ã®ãƒ‘スã¯ã‚³ãƒ­ãƒ³ã§åŒºåˆ‡ã‚‰ã‚ŒãŸãƒ‘スã®ãƒªã‚¹ãƒˆã§ã‚ã‚‹ã“ã¨ã«æ³¨æ„" "ã—ã¦ãã ã•ã„。例ã¨ã—ã¦ã¯ã€api_extensions_path = extensions:/path/to/more/" "exts:/even/more/exts ãŒã‚りã¾ã™ã€‚ã“ã®ãƒ‘スã«ã¯ neutron.extensions ã® __path__ " "ãŒä»˜ã„ã¦ã„ã‚‹ãŸã‚ã€å¼·åŒ–機能ãŒã“ã®ãƒ‘スã«ã‚ã‚‹å ´åˆã€ã“ã“ã§æŒ‡å®šã™ã‚‹å¿…è¦ã¯ã‚りã¾ã›" "ん。" msgid "The physical network name with which the HA network can be created." msgstr "HA ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’作æˆå¯èƒ½ãªç‰©ç†ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯å。" #, python-format msgid "The port '%s' was deleted" msgstr "ãƒãƒ¼ãƒˆ '%s' ãŒå‰Šé™¤ã•れã¾ã—ãŸ" msgid "The port to bind to" msgstr "ãƒã‚¤ãƒ³ãƒ‰å…ˆã®ãƒãƒ¼ãƒˆ" #, python-format msgid "The requested content type %s is invalid." msgstr "è¦æ±‚ã•れãŸã‚³ãƒ³ãƒ†ãƒ³ãƒ„タイプ %s ã¯ç„¡åйã§ã™ã€‚" msgid "The resource could not be found." msgstr "リソースãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚" #, python-format msgid "" "The router %(router_id)s has been already hosted by the L3 Agent " "%(agent_id)s." msgstr "" "ルーター %(router_id)s ã¯ã€æ—¢ã« L3 エージェント %(agent_id)s ã«ã‚ˆã£ã¦ãƒ›ã‚¹ãƒˆã•" "れã¦ã„ã¾ã™ã€‚" msgid "" "The server has either erred or is incapable of performing the requested " "operation." msgstr "" "サーãƒãƒ¼ã«èª¤ã‚ŠãŒã‚ã‚‹ã‹ã€ã¾ãŸã¯è¦æ±‚ã•ã‚ŒãŸæ“作を実行ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。" msgid "The service plugins Neutron will use" msgstr "Neutron ãŒä½¿ç”¨ã™ã‚‹ã‚µãƒ¼ãƒ“スプラグイン" #, python-format msgid "The subnet request could not be satisfied because: %(reason)s" msgstr "%(reason)s ã®ãŸã‚ã€ã‚µãƒ–ãƒãƒƒãƒˆã®è¦æ±‚ã«å¯¾å¿œã§ãã¾ã›ã‚“ã§ã—ãŸ" #, python-format msgid "The subproject to execute the command against. Can be one of: '%s'." msgstr "" "コマンドã®å®Ÿè¡Œã®å¯¾è±¡ã¨ãªã‚‹ã‚µãƒ–コマンド。'%s' ã®ã†ã¡ã®ã„ãšã‚Œã‹ã«ã™ã‚‹ã“ã¨ãŒã§ã" "ã¾ã™ã€‚" msgid "The type of authentication to use" msgstr "使用ã™ã‚‹èªè¨¼ã®ã‚¿ã‚¤ãƒ—" msgid "" "There are routers attached to this network that depend on this policy for " "access." msgstr "" "ã“ã®ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«ã¯ãƒ«ãƒ¼ã‚¿ãƒ¼ãŒå­˜åœ¨ã—ã€ãƒ«ãƒ¼ã‚¿ãƒ¼ã¯ã‚¢ã‚¯ã‚»ã‚¹ã®éš›ã«ã“ã®ãƒãƒªã‚·ãƒ¼ã‚’" "使用ã—ã¾ã™ã€‚" msgid "" "Timeout in seconds to wait for a single OpenFlow request. Used only for " "'native' driver." msgstr "" "å˜ä¸€ã® OpenFlow リクエストを待機ã™ã‚‹ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆæ™‚é–“ (ç§’)。「ãƒã‚¤ãƒ†ã‚£ãƒ–ã€ã®ãƒ‰" "ライãƒãƒ¼ã§ã®ã¿ä½¿ç”¨ã§ãã¾ã™ã€‚" msgid "" "Timeout in seconds to wait for the local switch connecting the controller. " "Used only for 'native' driver." msgstr "" "ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ©ãƒ¼ã«æŽ¥ç¶šã™ã‚‹ãƒ­ãƒ¼ã‚«ãƒ«ã‚¹ã‚¤ãƒƒãƒã‚’待機ã™ã‚‹ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆæ™‚é–“ (ç§’)。「ãƒ" "イティブã€ã®ãƒ‰ãƒ©ã‚¤ãƒãƒ¼ã§ã®ã¿ä½¿ç”¨ã§ãã¾ã™ã€‚" msgid "" "Too long prefix provided. New name would exceed given length for an " "interface name." msgstr "" "æä¾›ã•れãŸãƒ—レフィックスãŒé•·ã™ãŽã¾ã™ã€‚æ–°è¦ã®åå‰ãŒã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェースåã«è¨­å®šã•" "れãŸé•·ã•ã‚’è¶…ãˆã¾ã™ã€‚" msgid "" "True to delete all ports on all the OpenvSwitch bridges. False to delete " "ports created by Neutron on integration and external network bridges." msgstr "" "ã™ã¹ã¦ã® OpenvSwitch ブリッジã§ã™ã¹ã¦ã®ãƒãƒ¼ãƒˆã‚’削除ã™ã‚‹å ´åˆã¯ True。統åˆãŠã‚ˆ" "ã³å¤–部ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãƒ–リッジ㧠Neutron ã«ã‚ˆã£ã¦ä½œæˆã•れãŸãƒãƒ¼ãƒˆã‚’削除ã™ã‚‹å ´åˆ" "㯠False。" msgid "Tunnel IP value needed by the ML2 plugin" msgstr "トンãƒãƒ« IP 値㌠ML2 プラグインã«å¿…è¦ã§ã™" msgid "Tunnel bridge to use." msgstr "使用ã™ã‚‹ãƒˆãƒ³ãƒãƒ«ãƒ–リッジ。" msgid "" "Type of the nova endpoint to use. This endpoint will be looked up in the " "keystone catalog and should be one of public, internal or admin." msgstr "" "使用ã™ã‚‹ nova ã®ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆã®ã‚¿ã‚¤ãƒ—。ã“ã®ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆã¯ Keystone ã®ã‚«ã‚¿" "ログã§å‚ç…§ã•れã€publicã€internalã€ã¾ãŸã¯ admin ã®ã„ãšã‚Œã‹ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾" "ã™ã€‚" msgid "URL for connecting to designate" msgstr "designate ã¸ã®æŽ¥ç¶šç”¨ URL" msgid "URL to database" msgstr "データベースã¸ã® URL" #, python-format msgid "Unable to access %s" msgstr "%s ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, maximum allowed " "prefix is %(max_prefixlen)s." msgstr "" "é•·ã•㌠%(prefixlen)s ã®ãƒ—レフィックスをæŒã¤ã‚µãƒ–ãƒãƒƒãƒˆã‚’割り当ã¦ã‚‹ã“ã¨ã¯ã§ãã¾" "ã›ã‚“。許å¯ã•ã‚Œã‚‹ãƒ—ãƒ¬ãƒ•ã‚£ãƒƒã‚¯ã‚¹é•·ã®æœ€å¤§å€¤ã¯ %(max_prefixlen)s ã§ã™ã€‚" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, minimum allowed " "prefix is %(min_prefixlen)s." msgstr "" "é•·ã•㌠%(prefixlen)s ã®ãƒ—レフィックスをæŒã¤ã‚µãƒ–ãƒãƒƒãƒˆã‚’割り当ã¦ã‚‹ã“ã¨ã¯ã§ãã¾" "ã›ã‚“。許å¯ã•ã‚Œã‚‹ãƒ—ãƒ¬ãƒ•ã‚£ãƒƒã‚¯ã‚¹é•·ã®æœ€å°å€¤ã¯ %(min_prefixlen)s ã§ã™ã€‚" #, python-format msgid "Unable to calculate %(address_type)s address because of:%(reason)s" msgstr "%(reason)s ã®ãŸã‚ %(address_type)s アドレスを計算ã§ãã¾ã›ã‚“" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of DNS " "nameservers exceeds the limit %(quota)s." msgstr "" "%(subnet_id)s ã®æ“作を完了ã§ãã¾ã›ã‚“。DNS ãƒãƒ¼ãƒ ã‚µãƒ¼ãƒãƒ¼ã®æ•°ãŒåˆ¶é™ %(quota)s " "ã‚’è¶…ãˆã¦ã„ã¾ã™ã€‚" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of host routes " "exceeds the limit %(quota)s." msgstr "" "%(subnet_id)s ã®æ“作を完了ã§ãã¾ã›ã‚“ã€‚ãƒ›ã‚¹ãƒˆçµŒè·¯ã®æ•°ãŒåˆ¶é™ %(quota)s ã‚’è¶…ãˆã¦" "ã„ã¾ã™ã€‚" #, python-format msgid "Unable to convert value in %s" msgstr "%s ã§å€¤ã‚’変æ›ã§ãã¾ã›ã‚“" msgid "Unable to create the Agent Gateway Port" msgstr "エージェントゲートウェイãƒãƒ¼ãƒˆã®ä½œæˆãŒã§ãã¾ã›ã‚“" msgid "Unable to create the SNAT Interface Port" msgstr "SNAT インターフェースãƒãƒ¼ãƒˆã®ä½œæˆãŒã§ãã¾ã›ã‚“" #, python-format msgid "" "Unable to create the flat network. Physical network %(physical_network)s is " "in use." msgstr "" "フラットãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’作æˆã§ãã¾ã›ã‚“。物ç†ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ %(physical_network)s " "ã¯ä½¿ç”¨ä¸­ã§ã™ã€‚" msgid "" "Unable to create the network. No available network found in maximum allowed " "attempts." msgstr "" "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’作æˆã§ãã¾ã›ã‚“。許å¯ã•れる最大試行回数ã§ã€ä½¿ç”¨å¯èƒ½ãªãƒãƒƒãƒˆãƒ¯ãƒ¼" "クãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。" #, python-format msgid "Unable to delete subnet pool: %(reason)s." msgstr "サブãƒãƒƒãƒˆãƒ—ールを削除ã§ãã¾ã›ã‚“: %(reason)s。" #, python-format msgid "Unable to determine mac address for %s" msgstr "%s ã® MAC アドレスを決定ã§ãã¾ã›ã‚“" #, python-format msgid "Unable to find '%s' in request body" msgstr "è¦æ±‚本体㧠'%s' ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" #, python-format msgid "Unable to find IP address %(ip_address)s on subnet %(subnet_id)s" msgstr "" "サブãƒãƒƒãƒˆ %(subnet_id)s 上㧠IP アドレス %(ip_address)s ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" #, python-format msgid "Unable to find resource name in %s" msgstr "%s ã«ãƒªã‚½ãƒ¼ã‚¹åを見ã¤ã‘ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“" #, python-format msgid "Unable to generate unique mac on network %(net_id)s." msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ %(net_id)s ã§å›ºæœ‰ã® MAC を生æˆã§ãã¾ã›ã‚“。" #, python-format msgid "" "Unable to identify a target field from:%s. Match should be in the form " "%%()s" msgstr "" "%s ã‹ã‚‰ã‚¿ãƒ¼ã‚²ãƒƒãƒˆãƒ•ィールドを特定ã§ãã¾ã›ã‚“。一致ã®å½¢å¼ã¯ %%()s " "ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“" msgid "Unable to provide external connectivity" msgstr "外部接続をæä¾›ã§ãã¾ã›ã‚“" msgid "Unable to provide tenant private network" msgstr "テナントã®ãƒ—ライベートãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’æä¾›ã§ãã¾ã›ã‚“" #, python-format msgid "" "Unable to reconfigure sharing settings for network %(network)s. Multiple " "tenants are using it." msgstr "" "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ %(network)s ã®å…±æœ‰è¨­å®šã‚’å†è¨­å®šã§ãã¾ã›ã‚“。複数ã®ãƒ†ãƒŠãƒ³ãƒˆãŒã“ã®è¨­" "定を使用ã—ã¦ã„ã¾ã™ã€‚" #, python-format msgid "" "Unable to verify match:%(match)s as the parent resource: %(res)s was not " "found" msgstr "" "match:%(match)s を親リソースã¨ã—ã¦æ¤œæŸ»ã§ãã¾ã›ã‚“: %(res)s ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§" "ã—ãŸ" #, python-format msgid "Unexpected label for script %(script_name)s: %(labels)s" msgstr "スクリプト %(script_name)s ã«é–¢ã™ã‚‹äºˆæœŸã—ãªã„ラベル: %(labels)s" #, python-format msgid "Unexpected number of alembic branch points: %(branchpoints)s" msgstr "alembic ã®åˆ†å²ç‚¹ã®äºˆæœŸã—ãªã„æ•°: %(branchpoints)s" #, python-format msgid "Unexpected response code: %s" msgstr "予期ã—ãªã„応答コード: %s" #, python-format msgid "Unexpected response: %s" msgstr "予期ã—ãªã„応答: %s" #, python-format msgid "Unit name '%(unit)s' is not valid." msgstr "ユニットå '%(unit)s' ãŒç„¡åйã§ã™ã€‚" #, python-format msgid "Unknown address type %(address_type)s" msgstr "䏿˜Žãªã‚¢ãƒ‰ãƒ¬ã‚¹ã‚¿ã‚¤ãƒ— %(address_type)s" #, python-format msgid "Unknown attribute '%s'." msgstr "属性 '%s' ãŒä¸æ˜Žã§ã™ã€‚" #, python-format msgid "Unknown chain: %r" msgstr "䏿˜Žãªãƒã‚§ãƒ¼ãƒ³: %r" #, python-format msgid "Unknown network type %(network_type)s." msgstr "䏿˜Žãªãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¿ã‚¤ãƒ— %(network_type)s。" #, python-format msgid "Unknown quota resources %(unknown)s." msgstr "䏿˜Žãªã‚¯ã‚©ãƒ¼ã‚¿ãƒªã‚½ãƒ¼ã‚¹ %(unknown)s。" msgid "Unmapped error" msgstr "マップ解除エラー" msgid "Unrecognized action" msgstr "èªè­˜ã•れãªã„アクション" #, python-format msgid "Unrecognized attribute(s) '%s'" msgstr "èªè­˜ã•れãªã„属性 '%s'" msgid "Unrecognized field" msgstr "èªè­˜ã•れãªã„フィールド" msgid "Unsupported Content-Type" msgstr "サãƒãƒ¼ãƒˆã•れãªã„ Content-Type" #, python-format msgid "Unsupported network type %(net_type)s." msgstr "サãƒãƒ¼ãƒˆã•れãªã„ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¿ã‚¤ãƒ— %(net_type)s" #, python-format msgid "Unsupported port state: %(port_state)s." msgstr "サãƒãƒ¼ãƒˆã•れãªã„ãƒãƒ¼ãƒˆã®çŠ¶æ…‹: %(port_state)s。" msgid "Unsupported request type" msgstr "サãƒãƒ¼ãƒˆã•れãªã„è¦æ±‚タイプã§ã™" msgid "Updating default security group not allowed." msgstr "デフォルトã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¼ã‚°ãƒ«ãƒ¼ãƒ—ã®æ›´æ–°ã¯è¨±å¯ã•れã¾ã›ã‚“。" msgid "" "Use ML2 l2population mechanism driver to learn remote MAC and IPs and " "improve tunnel scalability." msgstr "" "リモート MAC ãŠã‚ˆã³ IP ã‚’èªè­˜ã—ã¦ãƒˆãƒ³ãƒãƒ«ã®ã‚¹ã‚±ãƒ¼ãƒ©ãƒ“リティーをå‘上ã•ã›ã‚‹ã«" "ã¯ã€ML2 l2population メカニズムドライãƒãƒ¼ã‚’使用ã—ã¦ãã ã•ã„。" msgid "Use broadcast in DHCP replies." msgstr "DHCP ã®å¿œç­”㧠ブロードキャストを使用ã—ã¾ã™ã€‚" msgid "Use either --delta or relative revision, not both" msgstr "--delta ã¨ç›¸å¯¾çš„ãªå¤‰æ›´ã®ä¸¡æ–¹ã§ã¯ãªãã€ã©ã¡ã‚‰ã‹ä¸€æ–¹ã‚’使用ã—ã¦ãã ã•ã„" msgid "" "Use ipset to speed-up the iptables based security groups. Enabling ipset " "support requires that ipset is installed on L2 agent node." msgstr "" "iptables ベースã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¼ã‚°ãƒ«ãƒ¼ãƒ—ã®é€Ÿåº¦ã‚’å‘上ã•ã›ã‚‹ãŸã‚ã«ã€ipset を使用" "ã—ã¾ã™ã€‚ipset ã¸ã®ã‚µãƒãƒ¼ãƒˆã‚’有効ã«ã™ã‚‹ã«ã¯ã€L2 ã®ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆãƒŽãƒ¼ãƒ‰ã« ipset " "をインストールã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "" "Use the root helper when listing the namespaces on a system. This may not be " "required depending on the security configuration. If the root helper is not " "required, set this to False for a performance improvement." msgstr "" "システム上ã®åå‰ç©ºé–“を一覧表示ã™ã‚‹éš›ã«ã¯ãƒ«ãƒ¼ãƒˆãƒ˜ãƒ«ãƒ‘ーを使用ã—ã¾ã™ã€‚セキュリ" "ティー設定ã«ã‚ˆã£ã¦ã¯ã€æœ‰åйã«ã™ã‚‹å¿…è¦ãŒãªã„å ´åˆãŒã‚りã¾ã™ã€‚ルートヘルパーãŒå¿…" "è¦ãªã„å ´åˆã¯ã€ãƒ‘フォーマンスを高ã‚ã‚‹ãŸã‚ã«ã“ã®å€¤ã‚’ False ã«è¨­å®šã—ã¾ã™ã€‚" msgid "" "Use veths instead of patch ports to interconnect the integration bridge to " "physical networks. Support kernel without Open vSwitch patch port support so " "long as it is set to True." msgstr "" "patch port ã®ä»£ã‚り㫠veths を使用ã—ã¦ã€çµ±åˆãƒ–リッジを物ç†ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«ç›¸äº’" "接続ã—ã¾ã™ã€‚Open vSwitch ã® patch port ã®ã‚µãƒãƒ¼ãƒˆãŒ True ã«è¨­å®šã•れã¦ã„ã‚‹é™" "りã€å½“該サãƒãƒ¼ãƒˆãŒå­˜åœ¨ã—ãªã„å ´åˆã§ã‚‚カーãƒãƒ«ã‚’サãƒãƒ¼ãƒˆã—ã¾ã™ã€‚" msgid "" "User (uid or name) running metadata proxy after its initialization (if " "empty: agent effective user)." msgstr "" "åˆæœŸåŒ–後ã«ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ãƒ—ロキシーを実行ã—ã¦ã„るユーザー (uid ã¾ãŸã¯åå‰) (空ã®å ´" "åˆ: エージェント有効ユーザー)。" msgid "User (uid or name) running this process after its initialization" msgstr "åˆæœŸåŒ–後ã«ã“ã®ãƒ—ロセスを実行ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ (uid ã¾ãŸã¯åå‰)" msgid "Username for connecting to designate in admin context" msgstr "管ç†è€…ã®ã‚³ãƒ³ãƒ†ã‚­ã‚¹ãƒˆã«ãŠã‘ã‚‹ designate ã¸ã®æŽ¥ç¶šç”¨ãƒ¦ãƒ¼ã‚¶ãƒ¼å" msgid "VRRP authentication password" msgstr "VRRP èªè¨¼ãƒ‘スワード" msgid "VRRP authentication type" msgstr "VRRP èªè¨¼ã‚¿ã‚¤ãƒ—" msgid "VXLAN network unsupported." msgstr "VXLAN ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。" msgid "" "Value of host kernel tick rate (hz) for calculating minimum burst value in " "bandwidth limit rules for a port with QoS. See kernel configuration file for " "HZ value and tc-tbf manual for more information." msgstr "" "QoS を設定ã—ãŸãƒãƒ¼ãƒˆã«ã¤ã„ã¦ã€å¸¯åŸŸå¹…ã®åˆ¶é™ãƒ«ãƒ¼ãƒ«ã«åŸºã¥ã„ã¦æœ€å°ãƒãƒ¼ã‚¹ãƒˆå€¤ã‚’計" "ç®—ã™ã‚‹ãŸã‚ã®ãƒ›ã‚¹ãƒˆã®ã‚«ãƒ¼ãƒãƒ«ã®ãƒãƒƒã‚¯ãƒ¬ãƒ¼ãƒˆå€¤ (Hz)。詳細情報ã«ã¤ã„ã¦ã¯ã€Hz 値" "ã®ã‚«ãƒ¼ãƒãƒ«è¨­å®šãƒ•ァイル㨠tc-tbf マニュアルをå‚ç…§ã—ã¦ãã ã•ã„。" msgid "" "Value of latency (ms) for calculating size of queue for a port with QoS. See " "tc-tbf manual for more information." msgstr "" "QoS を設定ã—ãŸãƒãƒ¼ãƒˆã«ã¤ã„ã¦ã€ã‚­ãƒ¥ãƒ¼ã®ã‚µã‚¤ã‚ºã‚’検索ã™ã‚‹ãŸãŸã‚ã®ãƒ¬ã‚¤ãƒ†ãƒ³ã‚·ãƒ¼å€¤ " "(ミリ秒)。詳細情報ã«ã¤ã„ã¦ã¯ã€tc-tbf マニュアルをå‚ç…§ã—ã¦ãã ã•ã„。" msgid "" "When external_network_bridge is set, each L3 agent can be associated with no " "more than one external network. This value should be set to the UUID of that " "external network. To allow L3 agent support multiple external networks, both " "the external_network_bridge and gateway_external_network_id must be left " "empty." msgstr "" "external_network_bridge を設定ã™ã‚‹ã¨ã€å„ L3 エージェントã«ã¯ 1 ã¤ã®å¤–部ãƒãƒƒãƒˆ" "ワークã—ã‹å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ãŒã§ããªããªã‚Šã¾ã™ã€‚ã“ã®å€¤ã¨ã—ã¦è©²å½“ã™ã‚‹å¤–部ãƒãƒƒãƒˆ" "ワーク㮠UUID を設定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚L3 エージェントãŒè¤‡æ•°ã®å¤–部ãƒãƒƒãƒˆãƒ¯ãƒ¼" "クをサãƒãƒ¼ãƒˆã§ãるよã†ã«ã™ã‚‹ã«ã¯ã€external_network_bridge 㨠" "gateway_external_network_id ã®ä¸¡æ–¹ã‚’空ã®å€¤ã«è¨­å®šã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" msgid "" "When proxying metadata requests, Neutron signs the Instance-ID header with a " "shared secret to prevent spoofing. You may select any string for a secret, " "but it must match here and in the configuration used by the Nova Metadata " "Server. NOTE: Nova uses the same config key, but in [neutron] section." msgstr "" "ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿è¦æ±‚ã®ãƒ—ロキシーを実行ã™ã‚‹éš›ã«ã€Neutron ã¯ã‚¹ãƒ—ーフィングを防止ã™ã‚‹" "ãŸã‚ã«å…±æœ‰ç§˜å¯†éµã‚’使用ã—㦠インスタンス ID ヘッダーã«ç½²åã—ã¾ã™ã€‚秘密éµã¨ã—ã¦" "ä»»æ„ã®æ–‡å­—åˆ—ã‚’é¸æŠžã§ãã‚‹ã‚‚ã®ã®ã€ãã®å€¤ã¯ã“ã“㨠Nova Metadata Server ãŒä½¿ç”¨ã™" "る設定ã§ä¸€è‡´ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚注æ„: Nova ã¯åŒã˜è¨­å®šéµã‚’使用ã™ã‚‹ã‚‚ã®ã®ã€ãã®" "値㯠[neutron] セクションã«ã‚りã¾ã™ã€‚" msgid "" "Where to store Neutron state files. This directory must be writable by the " "agent." msgstr "" "Neutron 状態ファイルã®ä¿ç®¡å ´æ‰€ã€‚ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªãƒ¼ã¯ã€ã‚¨ãƒ¼ã‚¸ã‚§ãƒ³ãƒˆãŒæ›¸ãè¾¼ã¿" "を行ãˆã‚‹å ´æ‰€ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“。" msgid "" "With IPv6, the network used for the external gateway does not need to have " "an associated subnet, since the automatically assigned link-local address " "(LLA) can be used. However, an IPv6 gateway address is needed for use as the " "next-hop for the default route. If no IPv6 gateway address is configured " "here, (and only then) the neutron router will be configured to get its " "default route from router advertisements (RAs) from the upstream router; in " "which case the upstream router must also be configured to send these RAs. " "The ipv6_gateway, when configured, should be the LLA of the interface on the " "upstream router. If a next-hop using a global unique address (GUA) is " "desired, it needs to be done via a subnet allocated to the network and not " "through this parameter. " msgstr "" "IPv6 ã§ã¯ã€è‡ªå‹•çš„ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸãƒªãƒ³ã‚¯ãƒ­ãƒ¼ã‚«ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ (LLA) を使用ã§ãã‚‹ãŸ" "ã‚ã€å¤–部ゲートウェイã«ä½¿ç”¨ã™ã‚‹ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«ã¯ã‚µãƒ–ãƒãƒƒãƒˆã‚’関連付ã‘ã‚‹å¿…è¦ã¯ã‚" "りã¾ã›ã‚“。ãŸã ã—ã€IPv6 ゲートウェイアドレスã¯ãƒ‡ãƒ•ォルト経路ã®ãƒã‚¯ã‚¹ãƒˆãƒ›ãƒƒãƒ—ã¨" "ã—ã¦ä½¿ç”¨ã™ã‚‹ãŸã‚ã«å¿…è¦ã§ã™ã€‚IPv6 ゲートウェイアドレスをã“ã“ã§æ§‹æˆã—ãªã„å ´åˆã«" "ã®ã¿ã€ä¸Šæµãƒ«ãƒ¼ã‚¿ãƒ¼ã®ãƒ«ãƒ¼ã‚¿ãƒ¼åºƒå‘Š (RA) ã‹ã‚‰ãƒ‡ãƒ•ォルト経路をå–å¾—ã™ã‚‹ã‚ˆã†ã« " "Neutron ãƒ«ãƒ¼ã‚¿ãƒ¼ãŒæ§‹æˆã•れã¾ã™ã€‚ã“ã®å ´åˆã€ã“れら㮠RA ã‚’é€ä¿¡ã™ã‚‹ã‚ˆã†ã«ä¸Šæµ" "ルーターを構æˆã™ã‚‹ã“ã¨ã‚‚å¿…è¦ã§ã™ã€‚ipv6_gateway ã‚’æ§‹æˆã™ã‚‹å ´åˆã€ã“れã¯ä¸Šæµãƒ«ãƒ¼" "ター上ã®ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェース㮠LLA ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“。グローãƒãƒ«ãƒ¦ãƒ‹ãƒ¼ã‚¯ã‚¢ãƒ‰ãƒ¬" "ス (GUA) を使用ã—ãŸãƒã‚¯ã‚¹ãƒˆãƒ›ãƒƒãƒ—ãŒå¿…è¦ãªå ´åˆã¯ã€ã“ã®ãƒ‘ラメーターを使用ã™ã‚‹ã®" "ã§ã¯ãªãã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«å‰²ã‚ŠæŒ¯ã‚‰ã‚ŒãŸã‚µãƒ–ãƒãƒƒãƒˆã‚’介ã—ã¦ã“れを行ã†å¿…è¦ãŒã‚りã¾" "ã™ã€‚" msgid "You must implement __call__" msgstr "__call__ を実装ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™" msgid "" "You must provide a config file for bridge - either --config-file or " "env[NEUTRON_TEST_CONFIG_FILE]" msgstr "" "ãƒ–ãƒªãƒƒã‚¸ã®æ§‹æˆãƒ•ァイルã¨ã—㦠--config-file ã¾ãŸã¯ " "env[NEUTRON_TEST_CONFIG_FILE] ã®ã„ãšã‚Œã‹ã‚’指定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™" msgid "You must provide a revision or relative delta" msgstr "変更ã¾ãŸã¯ç›¸å¯¾ãƒ‡ãƒ«ã‚¿ã‚’指定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™" msgid "a subnetpool must be specified in the absence of a cidr" msgstr "cidr ãŒãªã„å ´åˆã€ã‚µãƒ–ãƒãƒƒãƒˆãƒ—ãƒ¼ãƒ«ã®æŒ‡å®šã¯å¿…é ˆã§ã™" msgid "add_ha_port cannot be called inside of a transaction." msgstr "トランザクション内㫠add_ha_port を呼ã³å‡ºã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。" msgid "allocation_pools allowed only for specific subnet requests." msgstr "allocation_pools ã¯ç‰¹å®šã®ã‚µãƒ–ãƒãƒƒãƒˆè¦æ±‚ã«ã®ã¿è¨±å¯ã•れã¾ã™ã€‚" msgid "allocation_pools are not in the subnet" msgstr "allocation_pools ãŒã‚µãƒ–ãƒãƒƒãƒˆå†…ã«å­˜åœ¨ã—ã¾ã›ã‚“" msgid "allocation_pools use the wrong ip version" msgstr "allocation_pools ãŒé–“é•ã£ãŸIP ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’使用ã—ã¦ã„ã¾ã™" msgid "already a synthetic attribute" msgstr "既㫠synthetic 属性を使用" msgid "binding:profile value too large" msgstr "binding:profile 値ãŒå¤§ãã™ãŽã¾ã™" #, python-format msgid "cannot perform %(event)s due to %(reason)s" msgstr "%(reason)s ã®ãŸã‚ %(event)s を実行ã§ãã¾ã›ã‚“" msgid "cidr and prefixlen must not be supplied together" msgstr "cidr 㨠prefixlen ã‚’åŒæ™‚ã«æŒ‡å®šã—ã¦ã¯ãªã‚Šã¾ã›ã‚“" #, python-format msgid "dhcp_agents_per_network must be >= 1. '%s' is invalid." msgstr "" "dhcp_agents_per_network 㯠1 以上ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“。'%s' ã¯ç„¡åйã§ã™ã€‚" msgid "dns_domain cannot be specified without a dns_name" msgstr "dns_domain 㯠dns_name ãªã—ã§ã¯æŒ‡å®šã§ãã¾ã›ã‚“" msgid "dns_name cannot be specified without a dns_domain" msgstr "dns_name 㯠dns_domain ãªã—ã§ã¯æŒ‡å®šã§ãã¾ã›ã‚“" msgid "fixed_ip_address cannot be specified without a port_id" msgstr "fixed_ip_address ã¯ã€port_id ãªã—ã§ã¯æŒ‡å®šã§ãã¾ã›ã‚“" #, python-format msgid "has device owner %s" msgstr "デãƒã‚¤ã‚¹æ‰€æœ‰è€… %s" msgid "in use" msgstr "使用ã•れã¦ã„ã¾ã™" #, python-format msgid "ip command failed on device %(dev_name)s: %(reason)s" msgstr "ip コマンドãŒãƒ‡ãƒã‚¤ã‚¹ %(dev_name)s ã§å¤±æ•—ã—ã¾ã—ãŸ: %(reason)s" #, python-format msgid "ip command failed: %(reason)s" msgstr "IP コマンドãŒå¤±æ•—ã—ã¾ã—ãŸ: %(reason)s" #, python-format msgid "ip link capability %(capability)s is not supported" msgstr "ip リンク機能 %(capability)s ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“" #, python-format msgid "ip link command is not supported: %(reason)s" msgstr "ip リンクコマンドã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“: %(reason)s" msgid "ip_version must be specified in the absence of cidr and subnetpool_id" msgstr "cidr ãŠã‚ˆã³ subnetpool_id ãŒãªã„å ´åˆã€ip_version ã®æŒ‡å®šã¯å¿…é ˆã§ã™" msgid "ipv6_address_mode is not valid when ip_version is 4" msgstr "ip_version ㌠4 ã®å ´åˆã€ipv6_address_mode ã¯ç„¡åйã§ã™" msgid "ipv6_ra_mode is not valid when ip_version is 4" msgstr "ip_version ㌠4 ã®å ´åˆã€ipv6_ra_mode ã¯ç„¡åйã§ã™" #, python-format msgid "" "ipv6_ra_mode set to '%(ra_mode)s' with ipv6_address_mode set to " "'%(addr_mode)s' is not valid. If both attributes are set, they must be the " "same value" msgstr "" "ipv6_ra_mode ㌠'%(ra_mode)s' ã«ã€ipv6_address_mode ㌠'%(addr_mode)s' ã«è¨­å®š" "ã•れã¦ã„ã¾ã™ãŒã€ã“れã¯ç„¡åйã§ã™ã€‚両方ã®å±žæ€§ã‚’設定ã™ã‚‹å ´åˆã€ã“れらã¯åŒã˜å€¤ã§ãª" "ã‘れã°ãªã‚Šã¾ã›ã‚“" msgid "mac address update" msgstr "mac アドレス更新" msgid "must provide exactly 2 arguments - cidr and MAC" msgstr "å¿…ãš 2 ã¤ã®å¼•æ•° (cidr ãŠã‚ˆã³ MAC) ã‚’æä¾›ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™" msgid "network_type required" msgstr "network_type ãŒå¿…è¦ã§ã™" #, python-format msgid "network_type value '%s' not supported" msgstr "network_type 値 '%s' ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“" msgid "new subnet" msgstr "æ–°è¦ã‚µãƒ–ãƒãƒƒãƒˆ" #, python-format msgid "physical_network '%s' unknown for flat provider network" msgstr "flat プロãƒã‚¤ãƒ€ãƒ¼ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã® physical_network '%s' ãŒä¸æ˜Žã§ã™" msgid "physical_network required for flat provider network" msgstr "flat プロãƒã‚¤ãƒ€ãƒ¼ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«ã¯ physical_network ãŒå¿…è¦ã§ã™" #, python-format msgid "provider:physical_network specified for %s network" msgstr "%s ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã« provider:physical_network ãŒæŒ‡å®šã•れã¾ã—ãŸ" msgid "respawn_interval must be >= 0 if provided." msgstr "respawn_interval ã¯ã€æŒ‡å®šã™ã‚‹å ´åˆã¯ 0 以上ã«ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" #, python-format msgid "segmentation_id out of range (%(min)s through %(max)s)" msgstr "segmentation_id ãŒç¯„囲 (%(min)s ã‹ã‚‰ %(max)s) 外ã§ã™" msgid "segmentation_id requires physical_network for VLAN provider network" msgstr "" "segmentation_id ã«ã¯ã€VLAN プロãƒã‚¤ãƒ€ãƒ¼ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã® physical_network ãŒå¿…è¦" "ã§ã™" msgid "shared attribute switching to synthetic" msgstr "共有属性を synthetic ã«å¤‰æ›´ã—ã¾ã™" #, python-format msgid "" "subnetpool %(subnetpool_id)s cannot be updated when associated with shared " "address scope %(address_scope_id)s" msgstr "" "サブãƒãƒƒãƒˆãƒ—ール %(subnetpool_id)s ãŒå…±æœ‰ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚¹ã‚³ãƒ¼ãƒ— " "%(address_scope_id)s ã¨é–¢é€£ä»˜ã‘られã¦ã„ã‚‹å ´åˆã¯ã€ã‚µãƒ–ãƒãƒƒãƒˆãƒ—ールを更新ã™ã‚‹ã“" "ã¨ã¯ã§ãã¾ã›ã‚“" msgid "subnetpool_id and use_default_subnetpool cannot both be specified" msgstr "" "subnetpool_id 㨠use_default_subnetpool ã®ä¸¡æ–¹ã‚’指定ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“" msgid "the nexthop is not connected with router" msgstr "ルーターã«ã‚ˆã£ã¦ãƒã‚¯ã‚¹ãƒˆãƒ›ãƒƒãƒ—ãŒæŽ¥ç¶šã•れã¦ã„ã¾ã›ã‚“" msgid "the nexthop is used by router" msgstr "ãƒã‚¯ã‚¹ãƒˆãƒ›ãƒƒãƒ—ãŒãƒ«ãƒ¼ã‚¿ãƒ¼ã«ã‚ˆã£ã¦ä½¿ç”¨ã•れã¦ã„ã¾ã™" neutron-12.1.1/neutron/locale/es/0000775000175000017500000000000013553660156016662 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/es/LC_MESSAGES/0000775000175000017500000000000013553660156020447 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/es/LC_MESSAGES/neutron.po0000664000175000017500000034171213553660047022510 0ustar zuulzuul00000000000000# Translations template for neutron. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the neutron project. # # Translators: # Victoria Martínez de la Cruz , 2013 # Andreas Jaeger , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: neutron VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-03-14 04:19+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-04-12 05:53+0000\n" "Last-Translator: Copied by Zanata \n" "Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 4.3.3\n" "Language-Team: Spanish\n" #, python-format msgid "" "\n" "Command: %(cmd)s\n" "Exit code: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" msgstr "" "\n" "Mandato: %(cmd)s\n" "Código de salida: %(code)s\n" "Stdin: %(stdin)s\n" "Salida estándar: %(stdout)s\n" "Error estándar: %(stderr)s" #, python-format msgid "" "%(branch)s HEAD file does not match migration timeline head, expected: " "%(head)s" msgstr "" "El archivo HEAD de %(branch)s no coincide con el head de la línea de tiempo " "de migración, se esperaba: %(head)s" #, python-format msgid "%(id)s is not a valid %(type)s identifier" msgstr "%(id)s no es un identificador %(type)s válido" #, python-format msgid "" "%(invalid_dirs)s is invalid value for sort_dirs, valid value is '%(asc)s' " "and '%(desc)s'" msgstr "" "%(invalid_dirs)s es un valor no válido para sort_dirs, los valores válidos " "son '%(asc)s' y '%(desc)s'" #, python-format msgid "%(key)s prohibited for %(tunnel)s provider network" msgstr "%(key)s prohibido para red de proveedor %(tunnel)s" #, python-format msgid "%(name)s '%(addr)s' does not match the ip_version '%(ip_version)s'" msgstr "%(name)s '%(addr)s' no coincide con la versión de IP '%(ip_version)s'" #, python-format msgid "%s cannot be called while in offline mode" msgstr "%s no puede invocarse en la modalidad fuera de línea" #, python-format msgid "%s is invalid attribute for sort_keys" msgstr "%s es un atributo no válido para sort_keys" #, python-format msgid "%s is not a valid VLAN tag" msgstr "%s no es una etiqueta VLAN válida" #, python-format msgid "%s must implement get_port_from_device or get_ports_from_devices." msgstr "%s debe implementar get_port_from_device o get_ports_from_devices." #, python-format msgid "%s prohibited for VLAN provider network" msgstr "%s prohibido para la red de proveedor VLAN" #, python-format msgid "%s prohibited for flat provider network" msgstr "%s prohibido para la red de proveedor simple" #, python-format msgid "%s prohibited for local provider network" msgstr "%s prohibido para la red de proveedor local" #, python-format msgid "'%s' is not a valid RBAC object type" msgstr "'%s' no es un tipo de objeto RBAC válido" #, python-format msgid "'%s' is not supported for filtering" msgstr "'%s' no está admitido para filtrar" #, python-format msgid "'module' object has no attribute '%s'" msgstr "El objeto 'module' no tiene ningún atributo '%s'" msgid "'port_max' is smaller than 'port_min'" msgstr "'port_max' es menor que 'port_min'" msgid "0 is not allowed as CIDR prefix length" msgstr "0 no está permitido como longitud del prefijo de CIDR" msgid "A cidr must be specified in the absence of a subnet pool" msgstr "Debe especificarse un cidr en ausencia de una agrupación de subred" msgid "" "A decimal value as Vendor's Registered Private Enterprise Number as required " "by RFC3315 DUID-EN." msgstr "" "Valor decimal como número de empresa privada registrada del proveedor según " "lo exigido en RFC3315 DUID-EN." #, python-format msgid "A default external network already exists: %(net_id)s." msgstr "Ya existe una red externa predeterminada: %(net_id)s." msgid "" "A default subnetpool for this IP family has already been set. Only one " "default may exist per IP family" msgstr "" "Ya se ha definido una agrupación de subredes predeterminada para esta " "familia de IP. Solo puede haber un valor predeterminado por cada familia de " "IP" msgid "A metering driver must be specified" msgstr "Se debe especificar un controlador de medición" msgid "API for retrieving service providers for Neutron advanced services" msgstr "" "API para recuperar los proveedores de servicio para servicios avanzados de " "Neutron" msgid "Aborting periodic_sync_routers_task due to an error." msgstr "Terminando anormalmente periodic_sync_routers_task debido a un error." msgid "Access to this resource was denied." msgstr "Se ha denegado el acceso a este recurso." msgid "Action to be executed when a child process dies" msgstr "Acción para ejecutar cuando termina un proceso secundario" msgid "" "Add comments to iptables rules. Set to false to disallow the addition of " "comments to generated iptables rules that describe each rule's purpose. " "System must support the iptables comments module for addition of comments." msgstr "" "Añadir comentarios a reglas de iptables. Establézcalo en False para no " "permitir la adición, en las reglas de iptables generadas, de comentarios " "para describir el propósito de cada regla. El sistema debe admitir el módulo " "de comentarios de iptables para que se puedan añadir comentarios." msgid "Address not present on interface" msgstr "La dirección no está presente en la interfaz" msgid "" "Address to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "Dirección en la que se escuchan las conexiones OpenFlow. Se utiliza sólo " "para el controlador 'native'." msgid "Adds test attributes to core resources." msgstr "Añade atributos de prueba a recursos de núcleo." #, python-format msgid "Agent %(id)s is not a L3 Agent or has been disabled" msgstr "El agente %(id)s no es un agente L3 válido o se ha inhabilitado" #, python-format msgid "Agent %(id)s is not a valid DHCP Agent or has been disabled" msgstr "El agente %(id)s no es un agente DHCP válido o se ha inhabilitado" msgid "" "Agent starts with admin_state_up=False when enable_new_agents=False. In the " "case, user's resources will not be scheduled automatically to the agent " "until admin changes admin_state_up to True." msgstr "" "El agente se inicia con admin_state_up=False cuando enable_new_agents=False. " "En ese caso, los recursos del usuario no se planificarán automáticamente en " "el agente hasque que admin cambie admin_state_up a True." #, python-format msgid "Agent updated: %(payload)s" msgstr "El agente se ha actualizado: %(payload)s" msgid "Allow auto scheduling networks to DHCP agent." msgstr "Permita la planificación automática de redes para el agente DHCP." msgid "Allow auto scheduling of routers to L3 agent." msgstr "Permitir auto programación de enrutadores al agente L3." msgid "" "Allow overlapping IP support in Neutron. Attention: the following parameter " "MUST be set to False if Neutron is being used in conjunction with Nova " "security groups." msgstr "" "Permitir soporte de solapamiento de IP en Neutron. Atención: el siguiente " "parámetro se DEBE definir a False si se utiliza Neutron conjuntamente con " "los grupos de seguridad de Nova." msgid "Allow running metadata proxy." msgstr "Permitir ejecutar el proxy de metadatos." msgid "Allow sending resource operation notification to DHCP agent" msgstr "" "Notificación de la operación de permitir el envío de recurso al agente DHCP" msgid "Allow the creation of PTR records" msgstr "Permitir la creación de registros PTR" msgid "Allow the usage of the bulk API" msgstr "Permitir el uso de la API masiva" msgid "Allow to perform insecure SSL (https) requests to nova metadata" msgstr "" "Permitir ejecutar solicitudes SSL (https) no seguras en los metadatos de Nova" msgid "" "Allows for serving metadata requests coming from a dedicated metadata access " "network whose CIDR is 169.254.169.254/16 (or larger prefix), and is " "connected to a Neutron router from which the VMs send metadata:1 request. In " "this case DHCP Option 121 will not be injected in VMs, as they will be able " "to reach 169.254.169.254 through a router. This option requires " "enable_isolated_metadata = True." msgstr "" "Permite atender solicitudes de metadatos procedentes de una red de acceso a " "metadatos dedicada cuyo CIDR es 169.254.169.254/16 (o un prefijo más largo) " "y está conectada a un direccionador Neutron desde el cual las VM envían la " "solicitud metadata:1. En este caso, no se inyectará la opción DHCP 121 en " "las VM, porqué podrán alcanzar 169.254.169.254 mediante un direccionador. " "Esta opción requiere enable_isolated_metadata = True." msgid "An RBAC policy already exists with those values." msgstr "Ya existe una política RBAC con esos valores." msgid "An identifier must be specified when updating a subnet" msgstr "Se debe especificar un identificador al actualizar una subred" msgid "An interface driver must be specified" msgstr "Se debe especificar un controlador de interfaz" msgid "" "An ordered list of extension driver entrypoints to be loaded from the " "neutron.ml2.extension_drivers namespace. For example: extension_drivers = " "port_security,qos" msgstr "" "Lista de puntos de entrada del controlador de extensión para cargar desde el " "espacio de nombres neutron.ml2.extension_drivers. Por ejemplo: " "extension_drivers = port_security,qos" msgid "" "An ordered list of networking mechanism driver entrypoints to be loaded from " "the neutron.ml2.mechanism_drivers namespace." msgstr "" "Una lista ordenada de puntos de entrada de controlador de mecanismo de red a " "cargar desde el espacio de nombres neutron.ml2.mechanism_drivers." msgid "An unknown error has occurred. Please try your request again." msgstr "Se ha producido un error desconocido. Intente la solicitud otra vez." msgid "Async process didn't respawn" msgstr "El proceso de asignación no se ha vuelto a generar" msgid "Authorization URL for connecting to designate in admin context" msgstr "" "URL de autorización para establecer conexión con el designado en el contexto " "de administración" msgid "Automatically remove networks from offline DHCP agents." msgstr "Eliminar automáticamente las redes de los agentes DHCP fuera de línea." msgid "" "Automatically reschedule routers from offline L3 agents to online L3 agents." msgstr "" "Volver a planificar automáticamente los direccionadores de los agentes L3 " "fuera de línea a los agentes L3 en línea." msgid "Availability zone of this node" msgstr "Zona de disponibilidad de este nodo" msgid "Available commands" msgstr "Mandatos disponibles" msgid "Backend does not support VLAN Transparency." msgstr "El programa de fondo no soporta la transparencia de VLAN." #, python-format msgid "Base MAC: %s" msgstr "MAC base: %s" msgid "" "Base log dir for dnsmasq logging. The log contains DHCP and DNS log " "information and is useful for debugging issues with either DHCP or DNS. If " "this section is null, disable dnsmasq log." msgstr "" "Directorio de registro base para registro dnsmasq. El registro contiene " "información de registro DHCP y DNS y es útil para problemas de depuración " "con DHCP o DNS. Si esta sección es nula, inhabilite el registro dnsmasq." msgid "Body contains invalid data" msgstr "El cuerpo contiene datos no válidos" msgid "Both network_id and router_id are None. One must be provided." msgstr "Tanto network_id como router_id son None. Se debe proporcionar uno." #, python-format msgid "Bridge %(bridge)s does not exist." msgstr "El puente %(bridge)s no existe." msgid "Bulk operation not supported" msgstr "No se soporta operación masiva" msgid "CIDR to monitor" msgstr "CIDR a supervisar" #, python-format msgid "Callback for %(resource_type)s not found" msgstr "No se ha encontrado la devolución de llamada para %(resource_type)s" #, python-format msgid "Callback for %(resource_type)s returned wrong resource type" msgstr "" "La devolución de llamada para %(resource_type)s ha devuelto el tipo de " "recurso equivocado" #, python-format msgid "Cannot add floating IP to port %s that has no fixed IPv4 addresses" msgstr "" "No se puede añadir una IP flotante al puerto %s que no tiene direcciones " "IPv4 fijas" #, python-format msgid "Cannot add multiple callbacks for %(resource_type)s" msgstr "" "No se pueden añadir varias devoluciones de llamada para %(resource_type)s" #, python-format msgid "Cannot allocate IPv%(req_ver)s subnet from IPv%(pool_ver)s subnet pool" msgstr "" "No se puede asignar una subred IPv%(req_ver)s de la agrupación de subred IPv" "%(pool_ver)s" msgid "Cannot allocate requested subnet from the available set of prefixes" msgstr "" "No se puede asignar la subred solicitada a partir del conjunto disponible de " "prefijos" msgid "Cannot disable enable_dhcp with ipv6 attributes set" msgstr "No se puede inhabilitar enable_dhcp con atributos ipv6 establecidos" #, python-format msgid "Cannot handle subnet of type %(subnet_type)s" msgstr "No se puede manejar la subred de tipo %(subnet_type)s" msgid "Cannot have multiple IPv4 subnets on router port" msgstr "No puede tener varias subredes IPv4 en el puerto del direccionador" #, python-format msgid "" "Cannot have multiple router ports with the same network id if both contain " "IPv6 subnets. Existing port %(p)s has IPv6 subnet(s) and network id %(nid)s" msgstr "" "No puede tener varios puertos de direccionador con el mismo ID de red si " "amboscontienen subredes IPv6. El puerto existente %(p)s tiene subredes IPv6 " "y un id de red %(nid)s" #, python-format msgid "" "Cannot host distributed router %(router_id)s on legacy L3 agent %(agent_id)s." msgstr "" "No se puede alojar el direccionador distribuido %(router_id)s en el agente " "L3 heredado %(agent_id)s." msgid "Cannot mix IPv4 and IPv6 prefixes in a subnet pool." msgstr "No se pueden mezclar prefijos IPv4 y IPv6 en una agrupación de subred." msgid "Cannot specify both subnet-id and port-id" msgstr "No se puede especificar el ID de subred y el ID de puerto" msgid "Cannot understand JSON" msgstr "No se puede entender JSON" #, python-format msgid "Cannot update read-only attribute %s" msgstr "No se puede actualizar el atributo de sólo lectura %s" msgid "Certificate Authority public key (CA cert) file for ssl" msgstr "" "Archivo de clave pública de entidad emisora de certificados (cert CA) para " "ssl" #, python-format msgid "" "Change would make usage less than 0 for the following resources: %(unders)s." msgstr "" "El cambio produciría un uso inferior a 0 para los recursos siguientes: " "%(unders)s." msgid "Check ebtables installation" msgstr "Compruebe la instalación de ebtables" msgid "Check for ARP header match support" msgstr "Comprobar el soporte de coincidencia de cabeceras ARP" msgid "Check for ARP responder support" msgstr "Comprobar el soporte de encuestado de ARP" msgid "Check for ICMPv6 header match support" msgstr "Comprobar el soporte de coincidencia de cabeceras ICMPv6" msgid "Check for OVS Geneve support" msgstr "Comprobar el soporte de OVS Geneve" msgid "Check for OVS vxlan support" msgstr "Comprobar el soporte vxlan OVS" msgid "Check for VF management support" msgstr "Comprobar el soporte de gestión VF" msgid "Check for iproute2 vxlan support" msgstr "Comprobar el soporte vxlan iproute2" msgid "Check for nova notification support" msgstr "Comprobar el soporte de notificación nova" msgid "Check for patch port support" msgstr "Comprobar el soporte de puerto de parche" msgid "Check ip6tables installation" msgstr "Compruebe la instalación de ip6tables" msgid "Check ipset installation" msgstr "Compruebe la instalación de ipset" msgid "Check keepalived IPv6 support" msgstr "Compruebe el soporte keepalived IPv6" msgid "Check minimal dibbler version" msgstr "Compruebe la versión mínima de dibbler" msgid "Check minimal dnsmasq version" msgstr "Comprobar la versión mínima de dnsmasq" msgid "Check netns permission settings" msgstr "Comprobar los valores de permiso netns" msgid "Check ovs conntrack support" msgstr "Compruebe el soporte para ovs conntrack" msgid "Check ovsdb native interface support" msgstr "Comprobar el soporte de interfaz nativa ovsdb" #, python-format msgid "" "Cidr %(subnet_cidr)s of subnet %(subnet_id)s overlaps with cidr %(cidr)s of " "subnet %(sub_id)s" msgstr "" "El Cidr %(subnet_cidr)s de la subred %(subnet_id)s se solapa con el cidr " "%(cidr)s de la subred %(sub_id)s" msgid "Cleanup resources of a specific agent type only." msgstr "Limpiar solo los recursos de un tipo de agente específico." msgid "Client certificate for nova metadata api server." msgstr "Certificado de cliente para el servidor de la API de metadatos de Nova" msgid "" "Comma-separated list of : tuples, mapping " "network_device to the agent's node-specific list of virtual functions that " "should not be used for virtual networking. vfs_to_exclude is a semicolon-" "separated list of virtual functions to exclude from network_device. The " "network_device in the mapping should appear in the physical_device_mappings " "list." msgstr "" "Lista separada por comas de tuplas : que " "correlaciona network_device con la lista específica de nodo del agente de " "funciones virtuales que no deben utilizarse para las redes virtuales. " "vfs_to_exclude es una lista separada por punto y coma de funciones virtuales " "a excluir de network_device. El dispositivo de red (network_device) de la " "correlación debe aparecer en la lista physical_device_mappings." msgid "" "Comma-separated list of : tuples mapping " "physical network names to the agent's node-specific physical network device " "interfaces of SR-IOV physical function to be used for VLAN networks. All " "physical networks listed in network_vlan_ranges on the server should have " "mappings to appropriate interfaces on each agent." msgstr "" "Lista separada por comas de tuplas de : " "que correlaciona nombres de red física con las interfaces de dispositivo de " "red física específica de nodo del agente de la función física SR-IOV que se " "va a utilizar para las redes VLAN. Todas las redes físicas listadas en " "network_vlan_ranges en el servidor deben tener correlaciones con las " "interfaces adecuadas en cada agente." msgid "" "Comma-separated list of : tuples " "mapping physical network names to the agent's node-specific physical network " "interfaces to be used for flat and VLAN networks. All physical networks " "listed in network_vlan_ranges on the server should have mappings to " "appropriate interfaces on each agent." msgstr "" "Lista separada por comas de tuplas : " "para correlacionar nombres de red física con las interfaces físicas " "específicas de nodo del agente a utilizar para redes simples y VLAN. Todas " "las redes físicas que se enumeran en network_vlan_ranges en el servidor " "deberían tener correlaciones a las interfaces adecuadas de cada agente." msgid "" "Comma-separated list of : tuples enumerating ranges of GRE " "tunnel IDs that are available for tenant network allocation" msgstr "" "La lista separada por comas de conjuntos de variables : " "enumera los rangos de Los ID de túnel GRE que están disponibles para la " "asignación de red de arrendatario" msgid "" "Comma-separated list of : tuples enumerating ranges of " "Geneve VNI IDs that are available for tenant network allocation" msgstr "" "Lista separada por comas de conjuntos de variables : que " "enumeran los rangos de ID de Geneve VNI disponibles para la asignación de " "red de arrendatario" msgid "" "Comma-separated list of : tuples enumerating ranges of " "VXLAN VNI IDs that are available for tenant network allocation" msgstr "" "Lista separada por comas de conjuntos de variables : que " "enumeran los rangos de ID de VXLAN VNI que están disponibles para la " "asignación de red de arrendatario" msgid "" "Comma-separated list of the DNS servers which will be used as forwarders." msgstr "" "Lista separada por comas de los servidores DNS que se utilizarán como " "reenviadores." msgid "Command to execute" msgstr "Mandato a ejecutar" msgid "Config file for interface driver (You may also use l3_agent.ini)" msgstr "" "Archivo de configuración para controlador de interfaz (También puede " "utilizar l3_agent.ini)" #, python-format msgid "Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s" msgstr "Valor ethertype %(ethertype)s en conflicto para CIDR %(cidr)s" msgid "" "Controls whether the neutron security group API is enabled in the server. It " "should be false when using no security groups or using the nova security " "group API." msgstr "" "Controla si la API de grupo de seguridad neutron está habilitada en el " "servidor. Debe ser false cuando no hay grupos de seguridad o se utiliza la " "API de grupo de seguridad nova." #, python-format msgid "Could not bind to %(host)s:%(port)s after trying for %(time)d seconds" msgstr "" "No se puede enlazar a %(host)s:%(port)s después de intentar por %(time)d " "segundos" msgid "Could not deserialize data" msgstr "No se han podido deserializar los datos" #, python-format msgid "" "Current gateway ip %(ip_address)s already in use by port %(port_id)s. Unable " "to update." msgstr "" "IP de puerta de enlace actual %(ip_address)s ya está en uso por el puerto " "%(port_id)s. No es posible actualizar." msgid "" "DHCP lease duration (in seconds). Use -1 to tell dnsmasq to use infinite " "lease times." msgstr "" "Duración de concesión de DHCP (en segundos). Utilice -1 para indicar a " "dnsmasq que utilice tiempos de concesión infinitos." msgid "" "DVR deployments for VXLAN/GRE/Geneve underlays require L2-pop to be enabled, " "in both the Agent and Server side." msgstr "" "Los despliegues de DVR para las subcapas VXLAN/GRE/Geneve requieren que L2-" "pop esté habilitado, el lado del agente y del servidor." msgid "" "Database engine for which script will be generated when using offline " "migration." msgstr "" "Motor de base de datos para el cual se generará el script cuando se utilice " "la migración fuera de línea." msgid "Default external networks must be shared to everyone." msgstr "" "Las redes externas predeterminadas se deben compartir con todo el mundo." msgid "" "Default network type for external networks when no provider attributes are " "specified. By default it is None, which means that if provider attributes " "are not specified while creating external networks then they will have the " "same type as tenant networks. Allowed values for external_network_type " "config option depend on the network type values configured in type_drivers " "config option." msgstr "" "Tipo de red predeterminado para redes externas si se especifican atributos " "sin proveedor . De manera predeterminada, es Ninguno, que significa que si " "no se especifican atributos de proveedor al crear redes externas, tendrán el " "mismo tipo que las redes de arrendatario. Los valores permitidos de la " "opción de configuración external_network_type dependen de los valores de " "tipo de red configurados en la opción de configuración de type_drivers." msgid "" "Default number of RBAC entries allowed per tenant. A negative value means " "unlimited." msgstr "" "Número predeterminado de entradas RBAC permitidas por arrendatario. Un valor " "negativo significa ilimitados." msgid "" "Default number of resource allowed per tenant. A negative value means " "unlimited." msgstr "" "Número predeterminado de recursos permitidos por arrendatario. Un valor " "negativo significa ilimitados." msgid "Default security group" msgstr "Grupo de seguridad predeterminado" msgid "Default security group already exists." msgstr "El grupo de seguridad predeterminado ya existe." msgid "" "Default value of availability zone hints. The availability zone aware " "schedulers use this when the resources availability_zone_hints is empty. " "Multiple availability zones can be specified by a comma separated string. " "This value can be empty. In this case, even if availability_zone_hints for a " "resource is empty, availability zone is considered for high availability " "while scheduling the resource." msgstr "" "Valor predeteminado de las sugerencias de zonas de disponibilidad. Los " "planificadores conocedores de zonas de disponibilidad utilizan esto cuando " "availability_zone_hints de los recursos está vacío. Se pueden especificar " "múltiples zonas de disponibilidad en una cadena separada por comas. Este " "valor puede estar vacío. En este caso, aunque availability_zone_hints de un " "recurso esté vacío, se tiene en cuenta la zona de disponibilidad para la " "alta disponibilidad al planificar el recurso." msgid "" "Define the default value of enable_snat if not provided in " "external_gateway_info." msgstr "" "Defina el valor predeterminado de enable_snat si no se proporciona en " "external_gateway_info." msgid "" "Defines providers for advanced services using the format: :" ":[:default]" msgstr "" "Define proveedores para servicios avanzados con el formato: :" ":[:predeterminados]" msgid "Delete the namespace by removing all devices." msgstr "Suprimir el espacio de nombres eliminando todos los dispositivos. " #, python-format msgid "Deleting port %s" msgstr "Suprimiendo el puerto %s" #, python-format msgid "Deployment error: %(reason)s." msgstr "Error de despliegue: %(reason)s." msgid "Destroy IPsets even if there is an iptables reference." msgstr "Destruir IPsets aunque haya una referencia iptables." msgid "Destroy all IPsets." msgstr "Destruir todos los IPsets." #, python-format msgid "Device %(dev_name)s in mapping: %(mapping)s not unique" msgstr "" "El dispositivo %(dev_name)s en la correlación: %(mapping)s no es exclusivo" #, python-format msgid "Device name %(dev_name)s is missing from physical_device_mappings" msgstr "" "Falta el nombre de dispositivo %(dev_name)s en physical_device_mappings" msgid "Device not found" msgstr "No se ha encontrado el dispositivo" #, python-format msgid "" "Distributed Virtual Router Mac Address for host %(host)s does not exist." msgstr "" "La dirección Mac del direccionador virtual distribuido para el host %(host)s " "no existe." msgid "Domain to use for building the hostnames" msgstr "Dominio a utilizar par crear los nombres de host" msgid "Downgrade no longer supported" msgstr "La degradación ya no está soportada" #, python-format msgid "Driver %s is not unique across providers" msgstr "El controlador %s no es único entre los proveedores" msgid "Driver for external DNS integration." msgstr "Controlador para la integración externa de DNS." msgid "Driver for security groups firewall in the L2 agent" msgstr "Controlador para el cortafuegos de grupos de seguridad en el agente L2" msgid "Driver to use for scheduling network to DHCP agent" msgstr "" "Controlador que utilizar para la planificación de la red para el agente DHCP" msgid "Driver to use for scheduling router to a default L3 agent" msgstr "" "Controlador que utilizar para la planificación del direccionador para un " "agente L3 predeterminado" msgid "" "Driver used for ipv6 prefix delegation. This needs to be an entry point " "defined in the neutron.agent.linux.pd_drivers namespace. See setup.cfg for " "entry points included with the neutron source." msgstr "" "Controlador utilizado para la delegación de prefijos ipv6. Debe ser un punto " "de entrada definido en el espacio de nombres neutron.agent.linux.pd_drivers. " "Consulte setup.cfg para ver los puntos de entrada incluidos con el origen de " "neutron." #, python-format msgid "" "Duplicate L3HARouterAgentPortBinding is created for router(s) %(router)s. " "Database cannot be upgraded. Please, remove all duplicates before upgrading " "the database." msgstr "" "Se ha creado un L3HARouterAgentPortBinding duplicado para en direccionador " "%(router)s. No se puede actualizar la base de datos. Elimine todos los " "duplicados antes de actualizar la base de datos." msgid "Duplicate Security Group Rule in POST." msgstr "Regla de grupo de seguridad duplicada en POST." msgid "Duplicate address detected" msgstr "Se ha detectado una dirección duplicada" msgid "Duplicate segment entry in request." msgstr "Entrada de segmento duplicada en la solicitud." #, python-format msgid "ERROR: %s" msgstr "ERROR: %s" msgid "" "ERROR: Unable to find configuration file via the default search paths (~/." "neutron/, ~/, /etc/neutron/, /etc/) and the '--config-file' option!" msgstr "" "ERROR: no se ha podido encontrar el archivo de configuración por medio de " "las rutas de búsqueda predeterminada (~/.neutron/, ~/, /etc/neutron/, /etc/) " "¡y la opción '--config-file'!" msgid "" "Either one of parameter network_id or router_id must be passed to _get_ports " "method." msgstr "Debe pasarse un parámetro network_id o router_id al método _get_ports." msgid "Either subnet_id or port_id must be specified" msgstr "Se debe especificar el ID de subred o el ID de puerto" msgid "Empty physical network name." msgstr "Nombre de red física vacío." msgid "Empty subnet pool prefix list." msgstr "Lista de prefijos de agrupación de subred vacía." msgid "Enable HA mode for virtual routers." msgstr "Habilitar modo HA para direccionadores virtuales." msgid "Enable SSL on the API server" msgstr "Habilitar SSL en el servidor API" msgid "" "Enable VXLAN on the agent. Can be enabled when agent is managed by ml2 " "plugin using linuxbridge mechanism driver" msgstr "" "Habilitar VXLAN en el agente. Se puede habilitar cuando el agente es " "gestionado por ml2 plugin usando controlador de mecanismo linuxbridge" msgid "" "Enable local ARP responder if it is supported. Requires OVS 2.1 and ML2 " "l2population driver. Allows the switch (when supporting an overlay) to " "respond to an ARP request locally without performing a costly ARP broadcast " "into the overlay." msgstr "" "Habilite el encuestado de ARP local si está soportado. Requiere OVS 2.1 y el " "controlador ML2 l2population. Permite que el conmutador (cuando da soporte a " "una superposición) responda a una solicitud ARP localmente sin realizar una " "difusión de ARP costosa en la superposición." msgid "" "Enable services on an agent with admin_state_up False. If this option is " "False, when admin_state_up of an agent is turned False, services on it will " "be disabled. Agents with admin_state_up False are not selected for automatic " "scheduling regardless of this option. But manual scheduling to such agents " "is available if this option is True." msgstr "" "Habilite servicios en un agente con admin_state_up False. Si esta opción es " "False, cuando el valor admin_state_up de un agente se convierte en False, " "los servicios en élse inhabilitarán. Los agentes con admin_state_up False no " "se seleccionan para laplanificación automática independientemente de esta " "opción. No obstante, la planificación manual paraestos agentes está " "disponible si esta opción es True." msgid "" "Enables IPv6 Prefix Delegation for automatic subnet CIDR allocation. Set to " "True to enable IPv6 Prefix Delegation for subnet allocation in a PD-capable " "environment. Users making subnet creation requests for IPv6 subnets without " "providing a CIDR or subnetpool ID will be given a CIDR via the Prefix " "Delegation mechanism. Note that enabling PD will override the behavior of " "the default IPv6 subnetpool." msgstr "" "Habilita la delegación de prefijo IPv6 para la asignación automática de CIDR " "de subred. Establézcalo en True para habilitar la delegación de prefijo IPv6 " "para la asignación de subred en un entorno que admita PD (Prefix " "Delegation). A los usuarios que realicen solicitudes de creación de subred " "para subredes IPv6 sin proporcionar un CIDR o un ID de agrupación de subred " "se les dará un CIDR mediante el mecanismo de delegación de prefijos. Tenga " "en cuenta que al habilitar PD se sustituirá el comportamiento de la " "agrupación de subred IPv6 predeterminada." msgid "" "Enables the dnsmasq service to provide name resolution for instances via DNS " "resolvers on the host running the DHCP agent. Effectively removes the '--no-" "resolv' option from the dnsmasq process arguments. Adding custom DNS " "resolvers to the 'dnsmasq_dns_servers' option disables this feature." msgstr "" "Permite al servicio dnsmasq proporcionar resolución de nombres para " "instancias mediante resolvedores DNS en el host donde se ejecuta el agente " "DHCP. Elimina la opción '--no-resolv' de los argumentos del proceso dnsmasq. " "Si se añaden resolvedores DNS personalizados a la opción " "'dnsmasq_dns_servers' se deshabilita esta característica." msgid "End of VLAN range is less than start of VLAN range" msgstr "El final del rango VLAN es menor que el inicio del rango VLAN" msgid "End of tunnel range is less than start of tunnel range" msgstr "El final del rango de túnel es menor que el inicio del rango de túnel" #, python-format msgid "Error %(reason)s while attempting the operation." msgstr "Error %(reason)s al intentar realizar la operación." #, python-format msgid "Error parsing dns address %s" msgstr "Error al analizar la dirección dns %s" #, python-format msgid "Error while reading %s" msgstr "Error al leer %s " #, python-format msgid "" "Exceeded %s second limit waiting for address to leave the tentative state." msgstr "" "Se ha superado el límite de %s segundos esperando que la dirección abandone " "el estado de tentativa." msgid "Existing prefixes must be a subset of the new prefixes" msgstr "Los prefijos existentes deben ser una subred de los prefijos nuevos" #, python-format msgid "" "Exit code: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" msgstr "" "Código de salida: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; " "Stderr: %(stderr)s" #, python-format msgid "Extension %(driver)s failed." msgstr "Error en la extenxión %(driver)s." #, python-format msgid "" "Extension driver %(driver)s required for service plugin %(service_plugin)s " "not found." msgstr "" "No se ha encontrado el controlador de extensión %(driver)s necesario para el " "plugin de servicio %(service_plugin)s." msgid "" "Extension to use alongside ml2 plugin's l2population mechanism driver. It " "enables the plugin to populate VXLAN forwarding table." msgstr "" "Extensión para usar unto con el controlador de mecanismo l2population del " "plug-in ml2. Este habilita el plugin para completar la tabla de reenvío " "VXLAN." #, python-format msgid "Extension with alias %s does not exist" msgstr "La ampliación con el alias %s no existe" msgid "Extensions list to use" msgstr "Lista de extensiones que se va a utilizar" #, python-format msgid "Extensions not found: %(extensions)s." msgstr "Extensiones no encontradas: %(extensions)s." #, python-format msgid "External IP %s is the same as the gateway IP" msgstr "El IP externo %s es el mismo que el IP de pasarela" #, python-format msgid "Failed rescheduling router %(router_id)s: no eligible l3 agent found." msgstr "" "No se ha podido volver a programar el direccionador %(router_id)s: no se ha " "encontrado ningún agente l3 elegible." #, python-format msgid "Failed scheduling router %(router_id)s to the L3 Agent %(agent_id)s." msgstr "" "Se ha encontrado un error la planificación del direccionador %(router_id)s " "para el agente L3 %(agent_id)s." #, python-format msgid "Failed to allocate subnet: %(reason)s." msgstr "No se ha podido asignar la subred: %(reason)s." msgid "" "Failed to associate address scope: subnetpools within an address scope must " "have unique prefixes." msgstr "" "No se ha podido asociar el ámbito de dirección: las agrupaciones de subred " "dentro de un ámbito de dirección deben tener prefijos exclusivos." #, python-format msgid "" "Failed to create port on network %(network_id)s, because fixed_ips included " "invalid subnet %(subnet_id)s" msgstr "" "No se ha podido Se ha encontrado un error al crear un puerto en la red " "%(network_id)s, porque fixed_ips incluía una subred no válida %(subnet_id)s" #, python-format msgid "Failed to locate source for %s." msgstr "No se ha podido buscar el origen de %s." msgid "Failed to remove supplemental groups" msgstr "No se han podido eliminar los grupos suplementarios" #, python-format msgid "Failed to set gid %s" msgstr "No se ha podido establecer el gid %s" #, python-format msgid "Failed to set uid %s" msgstr "No se ha podido establecer el uid %s" #, python-format msgid "Failed to set-up %(type)s tunnel port to %(ip)s" msgstr "Ha fallado al configurar %(type)s el puerto de túnel a %(ip)s" msgid "Failure applying iptables rules" msgstr "Error al aplicar las reglas de iptables" #, python-format msgid "Failure waiting for address %(address)s to become ready: %(reason)s" msgstr "" "Error al esperar que la dirección %(address)s pase a estar lista: %(reason)s" msgid "Flat provider networks are disabled" msgstr "Las redes de proveedor simples están deshabilitadas" msgid "For TCP/UDP protocols, port_range_min must be <= port_range_max" msgstr "Para los protocolos TCP/UDP, port_range_min debe ser <= port_range_max" msgid "Force ip_lib calls to use the root helper" msgstr "Forzar llamadas ip_lib para usar el ayudante raíz" #, python-format msgid "Found duplicate extension: %(alias)s." msgstr "Se ha encontrado una ampliación duplicada: %(alias)s" #, python-format msgid "" "Found overlapping allocation pools: %(pool_1)s %(pool_2)s for subnet " "%(subnet_cidr)s." msgstr "" "Se ha encontrado solapamiento de agrupaciones de asignación:%(pool_1)s " "%(pool_2)s para subred %(subnet_cidr)s." msgid "Gateway IP version inconsistent with allocation pool version" msgstr "" "La versión de la IP de pasarela no es consistente con la versión de la " "agrupación de asignación" #, python-format msgid "Gateway ip %(ip_address)s conflicts with allocation pool %(pool)s." msgstr "" "La IP de pasarela %(ip_address)s está en conflicto con la agrupación de " "asignación %(pool)s." msgid "Gateway is not valid on subnet" msgstr "La pasarela no es válida en la subred" msgid "" "Geneve encapsulation header size is dynamic, this value is used to calculate " "the maximum MTU for the driver. This is the sum of the sizes of the outer " "ETH + IP + UDP + GENEVE header sizes. The default size for this field is 50, " "which is the size of the Geneve header without any additional option headers." msgstr "" "El tamaño de la cabecera de encapsulación de Geneve es dinámico, este valor " "se utiliza para calcular el MTU máximo para el controlador. Es la suma de " "los tamaños de las cabeceras exteriores de ETH + IP + UDP + GENEVE. El " "tamaño predeterminado para este campo es 50, que es el tamaño de la cabecera " "de Geneve sin ninguna cabecera de opción adicional." msgid "" "Group (gid or name) running metadata proxy after its initialization (if " "empty: agent effective group)." msgstr "" "Grupo (gid o nombre) que ejecuta el proxy de metadatos después de su " "inicialización (si está vacío: grupo efectivo del agente)." msgid "Group (gid or name) running this process after its initialization" msgstr "" "Grupo (gid o nombre) que ejecuta este proceso después de su inicialización" msgid "" "Hostname to be used by the Neutron server, agents and services running on " "this machine. All the agents and services running on this machine must use " "the same host value." msgstr "" "Nombre de host a utilizar por los agentes, servicios y el servidor de " "Neutron que se ejecutan en esta máquina. Todos los agentes y servicios que " "se ejecutan en esta máquina deben utilizar el mismo valor de host." #, python-format msgid "" "ICMP code (port-range-max) %(value)s is provided but ICMP type (port-range-" "min) is missing." msgstr "" "Se proporciona el código ICMP (port-range-max) %(value)s, pero falta el tipo " "ICMP (port-range-min)." msgid "ID of network" msgstr "ID de red" msgid "ID of network to probe" msgstr "ID de red a analizar" msgid "ID of probe port to delete" msgstr "ID de puerto de analizador a suprimir" msgid "ID of probe port to execute command" msgstr "ID de puerto de analizador para ejecutar mandato" msgid "ID of the router" msgstr "ID del direccionador" #, python-format msgid "IP address %(ip)s already allocated in subnet %(subnet_id)s" msgstr "La dirección IP %(ip)s ya está asignada en la subred %(subnet_id)s" #, python-format msgid "IP address %(ip)s does not belong to subnet %(subnet_id)s" msgstr "La dirección IP %(ip)s no pertenece a la subred %(subnet_id)s" msgid "IP allocation failed. Try again later." msgstr "Ha fallado la asignación de IP Inténtelo de nuevo más tarde." msgid "IP allocation requires subnet_id or ip_address" msgstr "La asignación de IP necesita subnet_id o ip_address" #, python-format msgid "" "IPTablesManager.apply failed to apply the following set of iptables rules:\n" "%s" msgstr "" "IPTablesManager.apply no ha podido aplicar el siguiente conjunto de reglas " "de iptables:\n" "%s" msgid "IPtables conntrack zones exhausted, iptables rules cannot be applied." msgstr "" "Las zonas conntrack de IPtables se han agotado, no se puedne aplicar las " "reglas de iptables." msgid "IPv6 Address Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "La modalidad de dirección IPv6 debe ser SLAAC o sin estado para la " "delegación de prefijos." msgid "IPv6 RA Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "La modalidad de IPv6 RA debe ser SLAAC o sin estado para la delegación de " "prefijos." #, python-format msgid "" "IPv6 address %(ip)s cannot be directly assigned to a port on subnet " "%(subnet_id)s as the subnet is configured for automatic addresses" msgstr "" "La dirección IPv6 %(ip)s no se puede asignar directamente a un puerto en la " "subred %(subnet_id)s ya que la subred está configurada para direcciones " "automáticas" #, python-format msgid "" "IPv6 subnet %s configured to receive RAs from an external router cannot be " "added to Neutron Router." msgstr "" "La subred IPv6 %s configurada para recibir RA de un direccionador externo no " "se puede añadir al direccionador de Neutron." msgid "" "If True, then allow plugins that support it to create VLAN transparent " "networks." msgstr "" "Si es True, permite a los plug-in que la soportan crear redes VLAN " "transparentes." msgid "Illegal IP version number" msgstr "Número de versión IP no permitido" #, python-format msgid "" "Illegal prefix bounds: %(prefix_type)s=%(prefixlen)s, %(base_prefix_type)s=" "%(base_prefixlen)s." msgstr "" "Límites de prefijo no permitidos: %(prefix_type)s=%(prefixlen)s, " "%(base_prefix_type)s=%(base_prefixlen)s." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot " "associate with address scope %(address_scope_id)s because subnetpool " "ip_version is not %(ip_version)s." msgstr "" "Asociación de agrupación de subred no permitida: la agrupación de subred " "%(subnetpool_id)s no se puede asociar al ámbito de dirección " "%(address_scope_id)s porque la versión de IP (ip_version) de la misma no es " "%(ip_version)s." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot be " "associated with address scope %(address_scope_id)s." msgstr "" "Asociación de agrupación de subred no permitida: la agrupación de subred " "%(subnetpool_id)s no se puede asociar al ámbito de dirección " "%(address_scope_id)s." #, python-format msgid "Illegal subnetpool update : %(reason)s." msgstr "Actualización de agrupación de subred no permitida: %(reason)s." #, python-format msgid "Illegal update to prefixes: %(msg)s." msgstr "Actualización no permitida para prefijos: %(msg)s." msgid "" "In some cases the Neutron router is not present to provide the metadata IP " "but the DHCP server can be used to provide this info. Setting this value " "will force the DHCP server to append specific host routes to the DHCP " "request. If this option is set, then the metadata service will be activated " "for all the networks." msgstr "" "En algunos casos, el direccionador Neutron no está presente para " "proporcionar el IP de los metadatos, pero se puede utilizar el servidor " "DHCP para proporcionar esta información. Si se define este valor, se forzará " "al servidor DHCP a añadir rutas de host específicas a la solicitud DHCP. Si " "no se define esta opción, el servicio de metadatos estará activado para " "todas las redes." msgid "" "Indicates that this L3 agent should also handle routers that do not have an " "external network gateway configured. This option should be True only for a " "single agent in a Neutron deployment, and may be False for all agents if all " "routers must have an external network gateway." msgstr "" "Indica que este agente L3 también debería manejar direccionadores que no " "tengan una pasarela de red externa configurada. Esta opción solo puede ser " "True para un único agente en un despliegue Neutron, y puede ser False para " "todos los agentes si todos los direccionadores tienen que tener una pasarela " "de red externa." #, python-format msgid "Instance of class %(module)s.%(class)s must contain _cache attribute" msgstr "" "La intancia de clase %(module)s.%(class)s debe contener el atributo _cache" #, python-format msgid "Insufficient prefix space to allocate subnet size /%s" msgstr "Espacio de prefijo insuficiente para asignar el tamaño de subred %s" msgid "Insufficient rights for removing default security group." msgstr "" "No hay derechos suficientes para eliminar el grupo de seguridad " "predeterminado." msgid "" "Integration bridge to use. Do not change this parameter unless you have a " "good reason to. This is the name of the OVS integration bridge. There is one " "per hypervisor. The integration bridge acts as a virtual 'patch bay'. All VM " "VIFs are attached to this bridge and then 'patched' according to their " "network connectivity." msgstr "" "Puente de integración a utilizar. No cambie este parámetro a menos que tenga " "un buen motivo para hacerlo. Es el nombre del puente de integración OVS. Hay " "un por cada hipervisor. El puente de integración actúa como 'bahía de " "parche' virtual. Todas las VIF de la VM se conectan a ese puente y después " "se 'parchean' según su conectividad de red." msgid "Interface to monitor" msgstr "Interfaz a supervisar" msgid "" "Interval between checks of child process liveness (seconds), use 0 to disable" msgstr "" "Intervalo entre comprobaciones de vida de procesos secundarios (segundos), " "utilice 0 para inhabilitarlo" msgid "Interval between two metering measures" msgstr "Intervalo entre dos medidas de medición" msgid "Interval between two metering reports" msgstr "Intervalo entre dos informes de medición" #, python-format msgid "Invalid CIDR %(input)s given as IP prefix." msgstr "Se ha proporcionado un CIDR %(input)s no válido como prefijo de IP" #, python-format msgid "Invalid Device %(dev_name)s: %(reason)s" msgstr "Dispositivo no válido %(dev_name)s: %(reason)s" #, python-format msgid "" "Invalid action '%(action)s' for object type '%(object_type)s'. Valid " "actions: %(valid_actions)s" msgstr "" "Acción no válida '%(action)s' para el tipo de objeto '%(object_type)s'. " "Acciones válidas : %(valid_actions)s" #, python-format msgid "" "Invalid authentication type: %(auth_type)s, valid types are: " "%(valid_auth_types)s" msgstr "" "Tipo de autenticación no válida: %(auth_type)s, los tipos válidos son: " "%(valid_auth_types)s" #, python-format msgid "Invalid ethertype %(ethertype)s for protocol %(protocol)s." msgstr "Ethertype no válido %(ethertype)s para el protocolo %(protocol)s." #, python-format msgid "Invalid format: %s" msgstr "Formato no válido: %s" #, python-format msgid "Invalid instance state: %(state)s, valid states are: %(valid_states)s" msgstr "" "Estado de instancia no válido: %(state)s, los estados válidos son: " "%(valid_states)s" #, python-format msgid "Invalid mapping: '%s'" msgstr "Correlación no válida: '%s'" #, python-format msgid "Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'." msgstr "Rango de VLAN de red no válido: '%(vlan_range)s' - '%(error)s'." #, python-format msgid "Invalid network VXLAN port range: '%(vxlan_range)s'." msgstr "Rango de puerto VXLAN de red no válido: '%(vxlan_range)s'." #, python-format msgid "Invalid pci slot %(pci_slot)s" msgstr "Ranura pci no válida %(pci_slot)s" #, python-format msgid "Invalid provider format. Last part should be 'default' or empty: %s" msgstr "" "Formato de proveedor no válido. La última parte debe ser 'predeterminado' o " "vacío: %s" #, python-format msgid "Invalid resource type %(resource_type)s" msgstr "Tipo de recurso %(resource_type)s no válido" #, python-format msgid "Invalid route: %s" msgstr "Ruta no válida: %s" msgid "Invalid service provider format" msgstr "Formato de proveedor de servicio no válido" #, python-format msgid "" "Invalid value for ICMP %(field)s (%(attr)s) %(value)s. It must be 0 to 255." msgstr "" "Valor no válido para ICMP %(field)s (%(attr)s) %(value)s. Debe ser 0 a 255." #, python-format msgid "Invalid value for port %(port)s" msgstr "Valor no válido para el puerto %(port)s" msgid "" "Iptables mangle mark used to mark ingress from external network. This mark " "will be masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Marca de corte de iptables utilizada para marcar la entrada desde la red " "externa. Esta marca se enmascara con 0xffff de modo que sólo se utilizarán " "los 16 bits inferiores." msgid "" "Iptables mangle mark used to mark metadata valid requests. This mark will be " "masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Marca de corte de iptables utilizada para marcar solicitudes válidas de " "metadatos. Esta marca se enmascara con 0xffff de modo que sólo se utilizarán " "los 16 bits inferiores." msgid "Keepalived didn't respawn" msgstr "Keepalived no se ha vuelto a generar" msgid "Keepalived didn't spawn" msgstr "Keepalived no se ha generado" #, python-format msgid "" "Kernel HZ value %(value)s is not valid. This value must be greater than 0." msgstr "El valor de Kernel HZ %(value)s no es válido. Debe ser mayor que 0." msgid "L3 agent failure to setup NAT for floating IPs" msgstr "El agente L3 no ha podido configurar NAT para las IP flotantes" msgid "L3 agent failure to setup floating IPs" msgstr "El agente L3 no ha podido configurar las IP flotantes" msgid "Limit number of leases to prevent a denial-of-service." msgstr "Límite de número de alquileres para evitar denegación de servicio." msgid "List of :" msgstr "Lista de :" msgid "" "List of :: or " "specifying physical_network names usable for VLAN provider and tenant " "networks, as well as ranges of VLAN tags on each available for allocation to " "tenant networks." msgstr "" "Lista de :: o especificar " "nombres de physical_network utilizables para proveedor de VLAN y " "arrendatario redes, así como rangos de etiquetas VLAN en cada uno disponible " "para asignación para las redes de arrendatarios." msgid "" "List of network type driver entrypoints to be loaded from the neutron.ml2." "type_drivers namespace." msgstr "" "Lista de puntos de entrada del controlador de tipo de red a cargar desde el " "espacio de nombres neutron.ml2.type_drivers." msgid "" "List of physical_network names with which flat networks can be created. Use " "default '*' to allow flat networks with arbitrary physical_network names. " "Use an empty list to disable flat networks." msgstr "" "Lista de nombres de physical_network con los cuales se pueden crear redes " "simples. Utillice el valor predeterminado '*' para permitir redes simples " "con nombres de physical_network arbitrarios. Utillice una lista vacía para " "deshabilitar las redes simples." msgid "Location for Metadata Proxy UNIX domain socket." msgstr "Ubicación para socket de dominio UNIX de proxy de metadatos." msgid "Location of Metadata Proxy UNIX domain socket" msgstr "Ubicación de socket de dominio UNIX de proxy de metadatos" msgid "Location to store DHCP server config files." msgstr "" "Ubicación donde almacenar los archivos de configuración de servidor DHCP." msgid "Location to store IPv6 PD files." msgstr "Ubicación donde se almacenan los archivos PD de IPv6." msgid "Location to store IPv6 RA config files" msgstr "Ubicación para almacenar archivos de configuración de IPv6 RA" msgid "Location to store child pid files" msgstr "Ubicación para almacenar archivos pid hijos" msgid "Location to store keepalived/conntrackd config files" msgstr "" "Ubicación para almacenar los archivos de configuración keepalived/conntrackd" msgid "Log agent heartbeats" msgstr "Registrar señales de supervisión de agente" msgid "" "MTU of the underlying physical network. Neutron uses this value to calculate " "MTU for all virtual network components. For flat and VLAN networks, neutron " "uses this value without modification. For overlay networks such as VXLAN, " "neutron automatically subtracts the overlay protocol overhead from this " "value. Defaults to 1500, the standard value for Ethernet." msgstr "" "MTU de la red física subyacente. Neutron utiliza este valor para calcular el " "MTU de todos los componentes de la red virtual. Para redes planas y VLAN, " "neutron utiliza este valor sin modificar. Para redes superpuestas tales como " "VXLAN, neutron automáticamente sustrae el protocolo de superposición por " "encima de este valor. El valor predeterminado es 1500, que es el valor " "estándar para Ethernet." msgid "MTU size of veth interfaces" msgstr "Tamaño de MTU de la interfaz de veth" msgid "Make the l2 agent run in DVR mode." msgstr "Hacer que el agente l2 se ejecute en modalidad DVR." msgid "Malformed request body" msgstr "Cuerpo de solicitud formado incorrectamente" #, python-format msgid "Malformed request body: %(reason)s." msgstr "Cuerpo de solicitud mal formado: %(reason)s." msgid "MaxRtrAdvInterval setting for radvd.conf" msgstr "Parámetro MaxRtrAdvInterval para radvd.conf" msgid "Maximum number of DNS nameservers per subnet" msgstr "Número máximo de servidores de nombres DNS por subred" msgid "" "Maximum number of L3 agents which a HA router will be scheduled on. If it is " "set to 0 then the router will be scheduled on every agent." msgstr "" "Número máximo de agentes L3 a los que se replanificará un direccionador HA. " "Si está definido a 0, se replanificará el direccionador a cada uno de los " "agentes." msgid "Maximum number of allowed address pairs" msgstr "Número máximo de pares de direcciones permitidos" msgid "Maximum number of host routes per subnet" msgstr "Número máximo de rutas de host por subred" msgid "Maximum number of routes per router" msgstr "Número máximo de rutas por direccionador" msgid "" "Metadata Proxy UNIX domain socket mode, 4 values allowed: 'deduce': deduce " "mode from metadata_proxy_user/group values, 'user': set metadata proxy " "socket mode to 0o644, to use when metadata_proxy_user is agent effective " "user or root, 'group': set metadata proxy socket mode to 0o664, to use when " "metadata_proxy_group is agent effective group or root, 'all': set metadata " "proxy socket mode to 0o666, to use otherwise." msgstr "" "Modalidad de socket de dominio UNIX de proxy de metadatos, 4 valores " "permitidos: 'deduce': deducir la modalidad de los valores " "metadata_proxy_user/group; 'user': establecer la modalidad de socket de " "proxy de metadatos en 0o644 y utilizarse cuando metadata_proxy_user es " "usuario efectivo de agente o raíz; 'group': establecer la modalidad de " "socket de proxy de metadatos en 0o664 y utilizarse cuando " "metadata_proxy_group es grupo efectivo de agente o raíz, 'all': establecer " "la modalidad de socket de proxy de metadatos en 0o666 y, de lo contrario, " "utilizarse." msgid "Metering driver" msgstr "Controlador de medición" msgid "MinRtrAdvInterval setting for radvd.conf" msgstr "Parámetro MinRtrAdvInterval para radvd.conf" msgid "Minimize polling by monitoring ovsdb for interface changes." msgstr "Minimizar sondeo supervisando ovsdb para cambios de interfaz." #, python-format msgid "Missing key in mapping: '%s'" msgstr "Falta clave en correlación: '%s'" msgid "" "Multicast group for VXLAN. When configured, will enable sending all " "broadcast traffic to this multicast group. When left unconfigured, will " "disable multicast VXLAN mode." msgstr "" "Grupo de multidifusión para VXLAN. Cuando esté configurada, permitirá enviar " "todo el tráfico de difusión a este grupo de multidifusión. Si se deja sin " "configurar, se deshabilitará el modo de multidifusión VXLAN." msgid "" "Multicast group(s) for vxlan interface. A range of group addresses may be " "specified by using CIDR notation. Specifying a range allows different VNIs " "to use different group addresses, reducing or eliminating spurious broadcast " "traffic to the tunnel endpoints. To reserve a unique group for each possible " "(24-bit) VNI, use a /8 such as 239.0.0.0/8. This setting must be the same on " "all the agents." msgstr "" "Grupo de multidifusión para la interfaz VXLAN. Se puede especificar un rango " "de direcciones de grupo utilizando la notación CIDR. Especificar un rango " "permite que diferentes VNI utilicen diferentes direcciones de grupos, " "reduciendo o eliminando tráfico de difusión espurio a los puntos finales del " "tunel. Para reservar un grupo exclusivo para cada posible VNI (24-bit) , " "utilice un /8, como por ejemplo 239.0.0.0/8. Este ajuste debe ser el mismo " "en todos los agentes." #, python-format msgid "Multiple default providers for service %s" msgstr "Múltiples proveedores predeterminados para servicio %s" #, python-format msgid "Multiple plugins for service %s were configured" msgstr "Se han configurado varios complementos para el servicio %s" #, python-format msgid "Multiple providers specified for service %s" msgstr "Múltiples proveedores especificados para servicio %s" msgid "Multiple tenant_ids in bulk security group rule create not allowed" msgstr "" "No se permiten varios Id de arrendatario en creación de regla de grupo de " "seguridad masiva" msgid "Must also specify protocol if port range is given." msgstr "" "Se debe especificar también el protocolo si se indica el rango de puertos." msgid "Must specify one or more actions on flow addition or modification" msgstr "" "Debe especificar una o más acciones en la adición o modificación de flujo" msgid "Name of Open vSwitch bridge to use" msgstr "Nombre de puente de Open vSwitch a utilizar" msgid "" "Name of nova region to use. Useful if keystone manages more than one region." msgstr "" "Nombre de región de nova a utilizar. Es útil si keystone gestiona más de una " "región." msgid "Namespace of the router" msgstr "Espacio de nombres del direccionador" msgid "Native pagination depend on native sorting" msgstr "La paginación nativa depende de la ordenación nativa" #, python-format msgid "" "Need to apply migrations from %(project)s contract branch. This will require " "all Neutron server instances to be shutdown before proceeding with the " "upgrade." msgstr "" "Es necesario aplicar migraciones desde la bifurcación de contratación de " "%(project)s. Esto requerirá apagar todas las instancias de servidor de " "Neutron antes de proceder con la actualización." msgid "Negative delta (downgrade) not supported" msgstr "El delta negativo (degradación) no está soportado" msgid "Negative relative revision (downgrade) not supported" msgstr "La revisión relativa negativa (degradación) no está soportada" #, python-format msgid "Network %s does not contain any IPv4 subnet" msgstr "La red %s no contiene ninguna subred IPv4" #, python-format msgid "Network %s is not a valid external network" msgstr "La red %s no es una red externa válida" #, python-format msgid "Network %s is not an external network" msgstr "La red %s no es una red externa" #, python-format msgid "" "Network of size %(size)s, from IP range %(parent_range)s excluding IP ranges " "%(excluded_ranges)s was not found." msgstr "" "No se ha encontrado la red de tamaño %(size)s, de rango de IP " "%(parent_range)s, excluyendo los rangos %(excluded_ranges)s." #, python-format msgid "Network type value '%s' not supported" msgstr "No hay soporte para el valor de tipo de red '%s'" msgid "Network type value needed by the ML2 plugin" msgstr "El plugin ML2 necesita el valor de tipo de red" msgid "Network types supported by the agent (gre and/or vxlan)." msgstr "Tipos de red admitidos por el agente (gre o vxlan)." msgid "Neutron Service Type Management" msgstr "Administración del tipo de servicio Neutron" msgid "Neutron core_plugin not configured!" msgstr "¡Neutron core_plugin no está configurado!" msgid "No default router:external network" msgstr "No hay ninguna red router:external predeterminada" #, python-format msgid "No default subnetpool found for IPv%s" msgstr "" "No se ha encontrado ninguna agrupación de subredes predeterminada para IPv%s" msgid "No default subnetpools defined" msgstr "No se han definido agrupaciones de subredes predeterminadas" #, python-format msgid "No eligible l3 agent associated with external network %s found" msgstr "" "No se ha encontrado ningún agente l3 elegible asociado con la red externa %s" #, python-format msgid "No more IP addresses available for subnet %(subnet_id)s." msgstr "No hay más direcciones IP disponibles en la subred %(subnet_id)s." msgid "No offline migrations pending." msgstr "No hay migraciones fuera de línea pendientes." #, python-format msgid "No shared key in %s fields" msgstr "No hay ninguna clave compartida en los campos de %s" msgid "Not allowed to manually assign a router to an agent in 'dvr' mode." msgstr "" "No está permitido asignar manualmente un direccionador a un agente en modo " "'dvr'." msgid "Not allowed to manually remove a router from an agent in 'dvr' mode." msgstr "" "No está permitido eliminar manualmente un direccionador de un agente en modo " "'dvr'." msgid "" "Number of DHCP agents scheduled to host a tenant network. If this number is " "greater than 1, the scheduler automatically assigns multiple DHCP agents for " "a given tenant network, providing high availability for DHCP service." msgstr "" "Número de agentes DHCP programados para alojar una red de arrendatarios. Si " "este número es mayor que 1, el planificador asigna automáticamente varios " "agentes DHCP de una red de arrendatarios dada y proporciona alta " "disponibilidad para el servicio DHCP." msgid "Number of backlog requests to configure the metadata server socket with" msgstr "" "Número de solicitudes de retraso para configurar el socket de servidor de " "metadatos con" msgid "Number of backlog requests to configure the socket with" msgstr "" "Número de solicitudes de registro de reserva para configurar el socket con" msgid "" "Number of bits in an ipv4 PTR zone that will be considered network prefix. " "It has to align to byte boundary. Minimum value is 8. Maximum value is 24. " "As a consequence, range of values is 8, 16 and 24" msgstr "" "Número de bits en una zona PTR ipv4 que se considerarán prefijo de red. " "Tiene que estar alineado con el límite de bytes. El valor mínimo es 8. El " "valor máximo es 24. En consecuencia, el rango de valores es 8, 16 y 24." msgid "" "Number of bits in an ipv6 PTR zone that will be considered network prefix. " "It has to align to nyble boundary. Minimum value is 4. Maximum value is 124. " "As a consequence, range of values is 4, 8, 12, 16,..., 124" msgstr "" "Número de bits en una zona PTR ipv6 que se considerarán prefijo de red. " "Tiene que estar alineado con el límite de nyble. El valor mínimo es 4. El " "valor máximo es 124. En consecuencia, el rango de valores es 4, 8, 12, " "16,..., 124." msgid "" "Number of floating IPs allowed per tenant. A negative value means unlimited." msgstr "" "Número de IP flotantes permitidas por arrendatario. Un valor negativo " "significa ilimitados." msgid "" "Number of networks allowed per tenant. A negative value means unlimited." msgstr "" "Número de redes permitidas por arrendatario. Un valor negativo significa " "ilimitado." msgid "Number of ports allowed per tenant. A negative value means unlimited." msgstr "" "Número de puertos permitidos por arrendatario. Un valor negativo significa " "ilimitado." msgid "Number of routers allowed per tenant. A negative value means unlimited." msgstr "" "Número de direccionadores permitidos por arrendatario. Un valor negativo " "significa ilimitado." msgid "" "Number of seconds between sending events to nova if there are any events to " "send." msgstr "" "Número de segundos entre en el envío de sucesos a nova si hay sucesos a " "enviar." msgid "Number of seconds to keep retrying to listen" msgstr "Número de segundos en seguir intentando escuchar" msgid "" "Number of security groups allowed per tenant. A negative value means " "unlimited." msgstr "" "Número de grupos de seguridad permitidos por arrendatario. Un valor negativo " "significa ilimitados." msgid "" "Number of security rules allowed per tenant. A negative value means " "unlimited." msgstr "" "Número de reglas de seguridad permitidas por arrendatario. Un valor negativo " "significa ilimitados." msgid "" "Number of separate API worker processes for service. If not specified, the " "default is equal to the number of CPUs available for best performance." msgstr "" "Número de procesos de trabajador de API independientes para servicio. Si no " "se especifica, el valor predeterminado es igual al número de CPU disponible " "para garantizar el mejor rendimiento." msgid "" "Number of separate worker processes for metadata server (defaults to half of " "the number of CPUs)" msgstr "" "Número de procesos de trabajador independientes para servidor de metadatos " "(por omisión es la mitad del número de CPU)" msgid "Number of subnets allowed per tenant, A negative value means unlimited." msgstr "" "Número de subredes permitidas por arrendatario. Un valor negativo significa " "ilimitado." msgid "" "Number of threads to use during sync process. Should not exceed connection " "pool size configured on server." msgstr "" "Número de hebras a usar durante el proceso de sincronización. No debería " "exceder el tamaño de la agrupación de conexión configurado en el servidor." msgid "OK" msgstr "OK" msgid "" "OVS datapath to use. 'system' is the default value and corresponds to the " "kernel datapath. To enable the userspace datapath set this value to 'netdev'." msgstr "" "Base de datos OVS a utilizar. 'system' es el valor predeterminado y " "corresponde a la vía de acceso a datos del kernel. Para habilitar la vía de " "acceso a datos del espacio de usuario, defina este valor a 'netdev'." msgid "OVS vhost-user socket directory." msgstr "Directorio del socket de vhost-user de OVS" #, python-format msgid "Object action %(action)s failed because: %(reason)s." msgstr "La acción objeto %(action)s falló debido a: %(reason)s." msgid "Only admin can view or configure quota" msgstr "Solo los administradores pueden ver o configurar cuotas" msgid "Only admin is authorized to access quotas for another tenant" msgstr "" "Sólo está autorizado el administrador para acceder a cuotas para otro " "arrendatario" msgid "Only admins can manipulate policies on objects they do not own" msgstr "" "Sólo los admininstradores pueden manipular políticas en objetos de los que " "no son propietarios" msgid "Only allowed to update rules for one security profile at a time" msgstr "Solo se permite actualizar reglas para un perfil de seguridad a la vez" msgid "Only remote_ip_prefix or remote_group_id may be provided." msgstr "Solo se puede proporcionar remote_ip_prefix o remote_group_id." msgid "OpenFlow interface to use." msgstr "Interfaz OpenFlow que se va a utilizar." #, python-format msgid "" "Operation %(op)s is not supported for device_owner %(device_owner)s on port " "%(port_id)s." msgstr "" "No hay soporte para la operación %(op)s para device_owner %(device_owner)s " "en el puerto %(port_id)s." #, python-format msgid "Operation not supported on device %(dev_name)s" msgstr "Operación no admitida en el dispositivo %(dev_name)s" msgid "" "Ordered list of network_types to allocate as tenant networks. The default " "value 'local' is useful for single-box testing but provides no connectivity " "between hosts." msgstr "" "Lista ordenada de network_types para asignar como redes de arrendatarios. El " "valor predeterminado 'local' es útil para pruebas en un solo recuadro, pero " "no proporciona ninguna conectividad entre hosts." msgid "Override the default dnsmasq settings with this file." msgstr "" "Alterar temporalmente los valores dnsmasq predeterminados con este archivo." msgid "Owner type of the device: network/compute" msgstr "Tipo de propietario del dispositivo: red/cálculo" msgid "POST requests are not supported on this resource." msgstr "Las solicitudes de POST no son admitidas en este recurso." #, python-format msgid "Package %s not installed" msgstr "El paquete %s no se ha instalado" #, python-format msgid "Parsing bridge_mappings failed: %s." msgstr "El análisis de bridge_mappings ha fallado: %s." msgid "Password for connecting to designate in admin context" msgstr "" "Contraseña para establecer conexión con el designado en el contexto de " "administración" msgid "Path to PID file for this process" msgstr "Vía de acceso al archivo de PID para este proceso" msgid "Path to the router directory" msgstr "Vía de acceso al directorio del direccionador" msgid "Peer patch port in integration bridge for tunnel bridge." msgstr "" "Puerto de parche de igual en puente de integración para puente de túnel." msgid "Peer patch port in tunnel bridge for integration bridge." msgstr "" "Puerto de parche de igual en puente de túnel para puente de integración." msgid "Per-tenant subnet pool prefix quota exceeded." msgstr "" "Se ha superado la cuota de prefijo de agrupación de subred por inquilino." msgid "Phase upgrade options do not accept revision specification" msgstr "" "Las opciones de actualización de fase no aceptan la especificación de " "revisión" msgid "Ping timeout" msgstr "Tiempo de espera de ping" msgid "Plugin does not support updating provider attributes" msgstr "El plug-in no soporta la actualización de atributos de proveedor" #, python-format msgid "Port %(id)s does not have fixed ip %(address)s" msgstr "El puerto %(id)s no tiene una IP fija %(address)s" #, python-format msgid "Port %(port_id)s is already acquired by another DHCP agent" msgstr "El puerto %(port_id)s ya ha sido adquirido por otro agente DHCP" #, python-format msgid "" "Port %s has multiple fixed IPv4 addresses. Must provide a specific IPv4 " "address when assigning a floating IP" msgstr "" "El puerto %s tiene varias direcciones IPv4 fijas. Debe proporcionar una " "dirección IPv4 específica al asignar una IP flotante" msgid "" "Port to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "Puerto en el que se escuchan las conexiones OpenFlow. Se utiliza sólo para " "el controlador 'native'." #, python-format msgid "Prefix '%(prefix)s' not supported in IPv%(version)s pool." msgstr "" "El prefijo '%(prefix)s' no está soportado en la agrupación IPv%(version)s." msgid "Prefix Delegation can only be used with IPv6 subnets." msgstr "La delegación de prefijos sólo se puede utilizar con subredes IPv6." msgid "Private key of client certificate." msgstr "Clave privada del certificado de cliente." #, python-format msgid "Probe %s deleted" msgstr "Se ha suprimido el analizador %s" #, python-format msgid "Probe created : %s " msgstr "Se ha creado analizador: %s " msgid "Process is already started" msgstr "El proceso ya se ha iniciado" msgid "Process is not running." msgstr "El proceso no se está ejecutando." msgid "Protocol to access nova metadata, http or https" msgstr "El protocolo para acceder a los metadatos de Nova, http o https" #, python-format msgid "Provider name %(name)s is limited by %(len)s characters" msgstr "El nombre de proveedor %(name)s está limitado a %(len)s caracteres" #, python-format msgid "QoS Policy %(policy_id)s is used by %(object_type)s %(object_id)s." msgstr "" "La política de calidad de servicio %(policy_id)s la está utilizando " "%(object_type)s %(object_id)s." #, python-format msgid "" "QoS binding for network %(net_id)s and policy %(policy_id)s could not be " "found." msgstr "" "No se ha podido encontrar el enlace QoS para la red %(net_id)s y la política " "%(policy_id)s." #, python-format msgid "" "QoS binding for port %(port_id)s and policy %(policy_id)s could not be found." msgstr "" "No se ha podido encontrar el enlace QoS para el puerto %(port_id)s y la " "política %(policy_id)s." #, python-format msgid "QoS policy %(policy_id)s could not be found." msgstr "No se ha podido encontrar la política QoS %(policy_id)s." #, python-format msgid "QoS rule %(rule_id)s for policy %(policy_id)s could not be found." msgstr "" "No se ha podido encontrar la regla QoS %(rule_id)s para la política " "%(policy_id)s." #, python-format msgid "RBAC policy of type %(object_type)s with ID %(id)s not found" msgstr "" "No se ha encontrado la política RBAC de tipo %(object_type)s con el ID %(id)s" #, python-format msgid "" "RBAC policy on object %(object_id)s cannot be removed because other objects " "depend on it.\n" "Details: %(details)s" msgstr "" "No se puede eliminar la política RBAC en el objeto %(object_id)s porque " "otros objetos dependen de ella.\n" "Detlles: %(details)s" msgid "" "Range of seconds to randomly delay when starting the periodic task scheduler " "to reduce stampeding. (Disable by setting to 0)" msgstr "" "Rango de segundos para retrasar aleatoriamente al iniciar la tarea periódica " "programador para reducir avalanchas. (Inhabilitar al establecer en 0)" msgid "Ranges must be in the same IP version" msgstr "Los rangos deben ser de la misma versión de IP." msgid "Ranges must be netaddr.IPRange" msgstr "Los rangos deben ser netaddr.IPRange" msgid "Ranges must not overlap" msgstr "Los rangos no se pueden solapar." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.EUI type." msgstr "" "Se ha recibido el tipo '%(type)s' y el valor '%(value)s'. Se esperaba el " "tipo netaddr.EUI." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPAddress " "type." msgstr "" "Se ha recibido el tipo '%(type)s' y el valor '%(value)s'. Se esperaba el " "tipo netaddr.IPAddress." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPNetwork " "type." msgstr "" "Se ha recibido el tipo '%(type)s' y el valor '%(value)s'. Se esperaba el " "tipo netaddr.IPNetwork." #, python-format msgid "" "Release aware branch labels (%s) are deprecated. Please switch to expand@ " "and contract@ labels." msgstr "" "Las etiquetas de rama para el release (%s) están en desusuo. Cambie a las " "etiquetas expand@ y contract@." msgid "Remote metadata server experienced an internal server error." msgstr "" "El servidor de metadatos remoto ha experimentado un error de servidor " "interno. " msgid "" "Repository does not contain HEAD files for contract and expand branches." msgstr "" "El repositorio no contiene archivos HEAD para bifurcaciones de contratación " "y ampliación." msgid "" "Representing the resource type whose load is being reported by the agent. " "This can be \"networks\", \"subnets\" or \"ports\". When specified (Default " "is networks), the server will extract particular load sent as part of its " "agent configuration object from the agent report state, which is the number " "of resources being consumed, at every report_interval.dhcp_load_type can be " "used in combination with network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler When the network_scheduler_driver is " "WeightScheduler, dhcp_load_type can be configured to represent the choice " "for the resource being balanced. Example: dhcp_load_type=networks" msgstr "" "Representando el tipo de recurso cuya carga está notificando el agente. " "Puede ser \"networks\", \"subnets\" o \"ports\". Cuando se especifica (el " "valor predeterminado es redes), el servidor extraerá la carga particular " "enviada como parte del objeto de configuración de agentes desde el estado de " "informe del agente, que es el número de recursos que se está consumiendo, en " "cada report_interval.dhcp_load_type que puede utilizarse junto con " "network_scheduler_driver = neutron.scheduler.dhcp_agent_scheduler." "WeightScheduler. Cuando network_scheduler_driver es WeightScheduler, " "dhcp_load_type se puede configurar para representar la opción para el " "recurso que se está equilibrando. Ejemplo: dhcp_load_type=networks" msgid "Request Failed: internal server error while processing your request." msgstr "" "Ha fallado la solicitar: error interno de servidor al procesar la solicitud." msgid "" "Reset flow table on start. Setting this to True will cause brief traffic " "interruption." msgstr "" "Restablecer tabla de flujo al iniciar. Si se establece en True, se producirá " "una interrupción breve del tráfico ." #, python-format msgid "Resource %(resource)s %(resource_id)s could not be found." msgstr "No se ha podido encontrar el recurso %(resource)s %(resource_id)s." #, python-format msgid "Resource %(resource_id)s of type %(resource_type)s not found" msgstr "" "No se ha encontrado el recurso %(resource_id)s de tipo %(resource_type)s" #, python-format msgid "" "Resource '%(resource_id)s' is already associated with provider " "'%(provider)s' for service type '%(service_type)s'" msgstr "" "El recurso '%(resource_id)s' ya está asociado con el proveedor " "'%(provider)s' para el tipo de servicio '%(service_type)s'" msgid "Resource body required" msgstr "Se necesita cuerpo de recurso" msgid "Resource not found." msgstr "Recurso no encontrado." msgid "Resources required" msgstr "Recursos necesarios " msgid "" "Root helper application. Use 'sudo neutron-rootwrap /etc/neutron/rootwrap." "conf' to use the real root filter facility. Change to 'sudo' to skip the " "filtering and just run the command directly." msgstr "" "Aplicación de ayudante raíz . Utilice 'neutron-rootwrap /etc/neutron/" "rootwrap.conf de sudo ' para utilizar el recurso de filtro raíz real. Cambie " "a 'sudo' para saltar el filtrado y ejecutar directamente el comando." msgid "Root permissions are required to drop privileges." msgstr "Se necesitan permisos de root para descartar privilegios." #, python-format msgid "Router '%(router_id)s' is not compatible with this agent." msgstr "El direccionador '%(router_id)s' no es compatible con este agente." #, python-format msgid "Router already has a port on subnet %s" msgstr "El direccionador ya tiene un puerto en la subred %s" msgid "Router port must have at least one fixed IP" msgstr "El puerto del direccionador debe tener al menos una IP fija" #, python-format msgid "Running %(cmd)s (%(desc)s) for %(project)s ..." msgstr "Ejecutando %(cmd)s (%(desc)s) para %(project)s ..." #, python-format msgid "Running %(cmd)s for %(project)s ..." msgstr "Ejecutando %(cmd)s para %(project)s ..." msgid "" "Seconds between nodes reporting state to server; should be less than " "agent_down_time, best if it is half or less than agent_down_time." msgstr "" "Segundos entre nodos que informan del estado al servidor; debe ser menor que " "agent_down_time, mejor si es la mitad o menos que agent_down_time." msgid "" "Seconds to regard the agent is down; should be at least twice " "report_interval, to be sure the agent is down for good." msgstr "" "Segundos para considerar que el agente está inactivo; debe ser como mínimo " "el doble de report_interval, para asegurarse de que el agente está inactivo " "definitivamente." #, python-format msgid "Security Group %(id)s %(reason)s." msgstr "Grupo de seguridad %(id)s %(reason)s." #, python-format msgid "Security Group Rule %(id)s %(reason)s." msgstr "Regla de grupo de seguridad %(id)s %(reason)s." #, python-format msgid "Security group %(id)s does not exist" msgstr "El grupo de seguridad %(id)s no existe" #, python-format msgid "Security group rule %(id)s does not exist" msgstr "La regla de grupo de seguridad %(id)s no existe" #, python-format msgid "Security group rule already exists. Rule id is %(rule_id)s." msgstr "" "La regla de grupo de seguridad ya existe. El id de regla es %(rule_id)s." #, python-format msgid "" "Security group rule for ethertype '%(ethertype)s' not supported. Allowed " "values are %(values)s." msgstr "" "No se admite la regla de grupo de seguridad para ethertype '%(ethertype)s'. " "Los valores permitidos son %(values)s." #, python-format msgid "" "Security group rule protocol %(protocol)s not supported. Only protocol " "values %(values)s and integer representations [0 to 255] are supported." msgstr "" "El protocolo de la regla del grupo de seguridad %(protocol)s no es admitido. " "Solo se admiten valores de protocolo %(values)s y representaciones de " "enteros [de 0 a 255]." msgid "Segments and provider values cannot both be set." msgstr "" "Los valores de segmentos y proveedor no pueden estar establecidos ambos." msgid "Selects the Agent Type reported" msgstr "Selecciona el tipo de agente notificado" msgid "" "Send notification to nova when port data (fixed_ips/floatingip) changes so " "nova can update its cache." msgstr "" "Envíe notificación a nova cuando cambien los datos de puerto (fixed_ips/" "floatingip) para que nova pueda actualizar la memoria caché." msgid "Send notification to nova when port status changes" msgstr "Envíe notificación a nova cuando cambie el estado de puerto" #, python-format msgid "" "Service provider '%(provider)s' could not be found for service type " "%(service_type)s" msgstr "" "El proveedor de servicio '%(provider)s' no se ha podido encontrar para el " "tipo de servicio %(service_type)s" msgid "Service to handle DHCPv6 Prefix delegation." msgstr "Servicio que gestiona la delegación de prefijos DHCPv6." #, python-format msgid "Service type %(service_type)s does not have a default service provider" msgstr "" "El tipo de servicio %(service_type)s no tiene un proveedor de servicio " "predeterminado" msgid "" "Set new timeout in seconds for new rpc calls after agent receives SIGTERM. " "If value is set to 0, rpc timeout won't be changed" msgstr "" "Establecer el nuevo tiempo de espera en segundos para nuevas llamadas rpc " "después de que el agente reciba SIGTERM. Si el valor se establece en 0, no " "se modificará el tiempo de espera de rpc" msgid "" "Set or un-set the don't fragment (DF) bit on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "Establecer o anular el establecimiento del bit DF (don't fragment) en el " "paquete de IP saliente que lleva el túnel GRE/VXLAN." msgid "" "Set or un-set the tunnel header checksum on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "Establecer o anular el establecimiento de la suma de comprobación de " "cabecera de túnel en paquete de IP saliente que transporta el túnel GRE/" "VXLAN." msgid "Shared address scope can't be unshared" msgstr "No se puede dejar de compartir el ámbito de la dirección compartida" msgid "String prefix used to match IPset names." msgstr "Prefijo de serie utilizado para coincidir con nombres IPset." #, python-format msgid "Sub-project %s not installed." msgstr "El subproyecto %s no se ha instalado." msgid "Subnet for router interface must have a gateway IP" msgstr "" "La subred para la interfaz de direccionador debe tener una IP de pasarela" #, python-format msgid "Subnet pool %(subnetpool_id)s could not be found." msgstr "No se ha podido encontrar la agrupación de subred %(subnetpool_id)s." msgid "Subnet pool has existing allocations" msgstr "La agrupación de subred tiene asignaciones existentes" msgid "Subnet used for the l3 HA admin network." msgstr "Subred utilizada con la red de administradores HA l3." msgid "" "Subnets hosted on the same network must be allocated from the same subnet " "pool." msgstr "" "Las subredes alojadas en la misma red se deben asignar desde la misma " "agrupación de subredes." msgid "" "System-wide flag to determine the type of router that tenants can create. " "Only admin can override." msgstr "" "Distintivo en todo el sistema para determinar el tipo de direccionador que " "pueden crear los arrendatarios. Sólo el administrador puede alterarlo " "temporalmente." msgid "TCP Port used by Neutron metadata namespace proxy." msgstr "" "Puerto TCP usado por el proxy de espacio de nombre de metadatos Neutron." msgid "TCP Port used by Nova metadata server." msgstr "Puerto TCP utilizado por el servidor de metadatos de Nova." msgid "TTL for vxlan interface protocol packets." msgstr "TTL para paquetes de protocolo de interfaz vxlan." #, python-format msgid "Tag %(tag)s could not be found." msgstr "No se ha podido encontrar la etiqueta %(tag)s." #, python-format msgid "Tenant %(tenant_id)s not allowed to create %(resource)s on this network" msgstr "" "El arrendatario %(tenant_id)s no está autorizado a crear %(resource)s en " "esta red" msgid "Tenant id for connecting to designate in admin context" msgstr "" "ID de inquilino para establecer conexión con el designado en el contexto de " "administración" msgid "Tenant name for connecting to designate in admin context" msgstr "" "Nombre de inquilino para establecer conexión con el designado en el contexto " "de administración" msgid "Tenant network creation is not enabled." msgstr "La creación de red de arrendatario no se ha habilitado." msgid "Tenant-id was missing from quota request." msgstr "Faltaba el ID de arrendatario en la solicitud de cuota." msgid "" "The 'gateway_external_network_id' option must be configured for this agent " "as Neutron has more than one external network." msgstr "" "La opción 'gateway_external_network_id' se debe configurar para este agente " "ya que Neutron tiene más de una red externa." msgid "" "The DHCP agent will resync its state with Neutron to recover from any " "transient notification or RPC errors. The interval is number of seconds " "between attempts." msgstr "" "El agente DHCP resincronizará su estado con Neutron para recuperarse de " "cualquier posible notificación transitoria o errorres de RPC. El intervalo " "es el número de segundos entre intentos." msgid "" "The DHCP server can assist with providing metadata support on isolated " "networks. Setting this value to True will cause the DHCP server to append " "specific host routes to the DHCP request. The metadata service will only be " "activated when the subnet does not contain any router port. The guest " "instance must be configured to request host routes via DHCP (Option 121). " "This option doesn't have any effect when force_metadata is set to True." msgstr "" "El servidor DHCP puede ajudar a proporcionar soporte para metadatos en " "redes aisladas. Si se define este valor a True, provocará que el servidor " "DHCP añada rutas específicas de host a la solicitud DHCP. El servicio de " "metadatos sólo se activará cuando la subred no contenga ningún puerto de " "direccionador. La instancia de invitado debe estar configurada para " "solicitar rutas de host vía DHCP (Opción 121). Esta opción no tiene ningún " "efecto cuando force_metadata está definido en True." msgid "The UDP port to use for VXLAN tunnels." msgstr "El puerto UDP para a usar para los túneles VXLAN." #, python-format msgid "" "The address allocation request could not be satisfied because: %(reason)s" msgstr "" "No se ha podido satisfacer la solicitud de asignación de dirección porque: " "%(reason)s" msgid "The advertisement interval in seconds" msgstr "Intervalo de anuncio en segundos" #, python-format msgid "The allocation pool %(pool)s is not valid." msgstr "La agrupación de asignación %(pool)s no es válida. " #, python-format msgid "" "The allocation pool %(pool)s spans beyond the subnet cidr %(subnet_cidr)s." msgstr "" "La agrupación de asignación %(pool)s abarca más allá de cidr de subred " "%(subnet_cidr)s." msgid "" "The base MAC address Neutron will use for VIFs. The first 3 octets will " "remain unchanged. If the 4th octet is not 00, it will also be used. The " "others will be randomly generated." msgstr "" "Dirección MAC base que Neutron utiliza para las VIF. Los 3 primeros octetos " "permanecerán sin cambios. Si el cuarto octeto no es 00, también se " "utilizará. Los otros se generan aleatoriamente. " msgid "" "The base mac address used for unique DVR instances by Neutron. The first 3 " "octets will remain unchanged. If the 4th octet is not 00, it will also be " "used. The others will be randomly generated. The 'dvr_base_mac' *must* be " "different from 'base_mac' to avoid mixing them up with MAC's allocated for " "tenant ports. A 4 octet example would be dvr_base_mac = fa:16:3f:4f:00:00. " "The default is 3 octet" msgstr "" "Dirección mac base que Neutron utiliza para las instancias DVR exclusivas. " "Los 3 primeros octetos permanecerán sin cambios. Si el cuarto octeto no es " "00, también se utilizará. Los otros se generan aleatoriamente. " "'dvr_base_mac' *debe* ser diferente de 'base_mac' para evitar que se mezclen " "con MAC asignado para los puertos de arrendatarios. Un ejemplo de 4 octetos " "sería dvr_base_mac = fa:16:3f:4f:00:00. El valor predeterminado es 3 octetos." msgid "The core plugin Neutron will use" msgstr "El core plugin Neutron usará" msgid "The driver used to manage the DHCP server." msgstr "El controlador utilizado para gestionar el servidor DHCP." msgid "The driver used to manage the virtual interface." msgstr "El controlador utilizado para gestionar la interfaz virtual." msgid "" "The email address to be used when creating PTR zones. If not specified, the " "email address will be admin@" msgstr "" "La dirección de correo electrónico a utilizar al crear zonas PTR. Si no se " "especifica la dirección de correo electrónico será admin@" #, python-format msgid "" "The following device_id %(device_id)s is not owned by your tenant or matches " "another tenants router." msgstr "" "El siguiente device_id %(device_id)s no es propiedad de su arrendatario o " "coincide con el direccionador de otros arrendatarios." msgid "The interface for interacting with the OVSDB" msgstr "Interfaz para la interacción con la OVSDB" msgid "" "The maximum number of items returned in a single response, value was " "'infinite' or negative integer means no limit" msgstr "" "El número máximo de elementos devueltos en una única respuesta, el valor " "'infinite' o un entero negativo significa que no hay límite" #, python-format msgid "" "The network %(network_id)s has been already hosted by the DHCP Agent " "%(agent_id)s." msgstr "" "La red %(network_id)s ya está alojada por el agente de DHCP %(agent_id)s." #, python-format msgid "" "The network %(network_id)s is not hosted by the DHCP agent %(agent_id)s." msgstr "" "La red %(network_id)s no está alojada por el agente de DHCP %(agent_id)s." msgid "" "The network type to use when creating the HA network for an HA router. By " "default or if empty, the first 'tenant_network_types' is used. This is " "helpful when the VRRP traffic should use a specific network which is not the " "default one." msgstr "" "Tipo de red que se debe utilizar al crear la red de alta disponibilidad para " "un direccionador HA. De manera predeterminada o si está vacío, se utilizan " "el primer 'tenant_network_types'. Esto es útil cuando el tráfico VRRP debe " "utilizar una red específica que no sea el valor predeterminado." msgid "" "The number of seconds the agent will wait between polling for local device " "changes." msgstr "" "El número de segundos que el agente esperará entre sondeos de cambios de " "dispositivo local." msgid "" "The number of seconds to wait before respawning the ovsdb monitor after " "losing communication with it." msgstr "" "Número de segundos a esperar antes de volver a generar el supervisor ovsdb " "después de perder la comunicación con él." msgid "The number of sort_keys and sort_dirs must be same" msgstr "El número de sort_keys y sort_dirs debe ser igual" msgid "" "The path for API extensions. Note that this can be a colon-separated list of " "paths. For example: api_extensions_path = extensions:/path/to/more/exts:/" "even/more/exts. The __path__ of neutron.extensions is appended to this, so " "if your extensions are in there you don't need to specify them here." msgstr "" "La vía de acceso para ampliaciones de API. Observe que puede ser una lista " "de vías de acceso separadas por punto y coma. Por ejemplo: " "api_extensions_path = extensions:/path/to/more/exts:/even/more/exts. Además, " "se añade __path__ of neutron.extensions, de forma que si sus extensiones " "están ahí no es necesario especificarlas aquí." msgid "The physical network name with which the HA network can be created." msgstr "Nombre de la red física con la que se puede crear la red HA." #, python-format msgid "The port '%s' was deleted" msgstr "El puerto '%s' se ha suprimido" msgid "The port to bind to" msgstr "El puerto al que enlazar" #, python-format msgid "The requested content type %s is invalid." msgstr "El tipo de contenido solicitado %s no es válido." msgid "The resource could not be found." msgstr "El recurso no se ha podido encontrar." #, python-format msgid "" "The router %(router_id)s has been already hosted by the L3 Agent " "%(agent_id)s." msgstr "" "El direccionador %(router_id)s ya está alojado por el agente L3 %(agent_id)s." msgid "" "The server has either erred or is incapable of performing the requested " "operation." msgstr "" "El servidor tiene un error o no puede ejecutar la operación solicitada." msgid "The service plugins Neutron will use" msgstr "Los plug-ins de servicio que utilizará Neutron" #, python-format msgid "The subnet request could not be satisfied because: %(reason)s" msgstr "No se ha podido satisfacer la solicitud de subred porque: %(reason)s" #, python-format msgid "The subproject to execute the command against. Can be one of: '%s'." msgstr "Subproyecto con el que ejecutar el mandato. Puede ser uno de: '%s'." msgid "The type of authentication to use" msgstr "El tipo de autenticación a utilizar" msgid "" "There are routers attached to this network that depend on this policy for " "access." msgstr "" "Hay direccionadores conectados a esta red que dependen de esta política para " "su acceso." msgid "" "Timeout in seconds to wait for a single OpenFlow request. Used only for " "'native' driver." msgstr "" "Tiempo en segundos que se espera una solicitud OpenFlow. Se utiliza sólo " "para el controlador 'native'." msgid "" "Timeout in seconds to wait for the local switch connecting the controller. " "Used only for 'native' driver." msgstr "" "Tiempo en segundos que se espera a que el conmutador local se conecte al " "controlador. Sólo se utiliza para el controlador 'native'." msgid "" "Too long prefix provided. New name would exceed given length for an " "interface name." msgstr "" "Se ha proporcionado un prefijo demasiado largo. El nuevo nombre superaría la " "longitud indicada para un nombre de interfaz." msgid "" "True to delete all ports on all the OpenvSwitch bridges. False to delete " "ports created by Neutron on integration and external network bridges." msgstr "" "Verdadero para suprimir todos los puertos en todos los puentes OpenvSwitch. " "Falso para suprimir puertos creados por Neutron por los puentes de red " "externos y de integración." msgid "Tunnel IP value needed by the ML2 plugin" msgstr "El plugin ML2 necesita el valor de IP de túnel" msgid "Tunnel bridge to use." msgstr "Puente de túnel para utilizar." msgid "" "Type of the nova endpoint to use. This endpoint will be looked up in the " "keystone catalog and should be one of public, internal or admin." msgstr "" "Tipo de punto final de nova a utilizar. Este punto final se consultará en el " "catálogo de keystone y debe ser uno de los siguientes: público, interno o " "administrativo." msgid "URL for connecting to designate" msgstr "URL para establecer conexión con el designado" msgid "URL to database" msgstr "URL en base de datos" #, python-format msgid "Unable to access %s" msgstr "No se puede acceder a %s " #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, maximum allowed " "prefix is %(max_prefixlen)s." msgstr "" "No se puede asignar la subred con la longitud de prefijo %(prefixlen)s, el " "prefijo máximo permitido es %(max_prefixlen)s" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, minimum allowed " "prefix is %(min_prefixlen)s." msgstr "" "No se puede asignar la subred con la longitud de prefijo %(prefixlen)s, el " "prefijo mínimo permitido es %(min_prefixlen)s." #, python-format msgid "Unable to calculate %(address_type)s address because of:%(reason)s" msgstr "" "No se puede calcular la dirección %(address_type)s debido a: %(reason)s" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of DNS " "nameservers exceeds the limit %(quota)s." msgstr "" "No se ha podido completar la operación para %(subnet_id)s. El número de " "servidores de nombres de DNS supera el límite %(quota)s." #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of host routes " "exceeds the limit %(quota)s." msgstr "" "No se ha podido completar la operación para %(subnet_id)s. El número de " "rutas de host supera el límite %(quota)s." #, python-format msgid "Unable to convert value in %s" msgstr "No se puede convertir el valor en %s " msgid "Unable to create the Agent Gateway Port" msgstr "No se puede crear el puerto de pasarela de agente" msgid "Unable to create the SNAT Interface Port" msgstr "No se puede crear el puerto de interfaz SNAT" #, python-format msgid "" "Unable to create the flat network. Physical network %(physical_network)s is " "in use." msgstr "" "No se ha podido crear la red plana. La red física %(physical_network)s está " "en uso." msgid "" "Unable to create the network. No available network found in maximum allowed " "attempts." msgstr "" "No se ha podido crear la red. No se ha encontrado ninguna red disponible en " "el máximo de intentos permitidos." #, python-format msgid "Unable to delete subnet pool: %(reason)s." msgstr "No se puede suprimir la agrupación de subred: %(reason)s." #, python-format msgid "Unable to determine mac address for %s" msgstr "No se ha podido determinar la dirección mac para %s" #, python-format msgid "Unable to find '%s' in request body" msgstr "No se puede encontrar '%s' en el cuerpo de solicitud " #, python-format msgid "Unable to find IP address %(ip_address)s on subnet %(subnet_id)s" msgstr "" "No se ha encontrado la dirección IP %(ip_address)s en la subred %(subnet_id)s" #, python-format msgid "Unable to find resource name in %s" msgstr "No se ha podido encontrar el nombre del recurso en %s" #, python-format msgid "Unable to generate unique mac on network %(net_id)s." msgstr "No se puede generar mac exclusivo en la red %(net_id)s. " #, python-format msgid "" "Unable to identify a target field from:%s. Match should be in the form " "%%()s" msgstr "" "No se ha podido identificar un campo destino desde: %s. La coincidencia debe " "tener la forma %%()s" msgid "Unable to provide external connectivity" msgstr "No se puede proporcionar conectividad externa" msgid "Unable to provide tenant private network" msgstr "No se puede proporcionar una red privada de inquilino" #, python-format msgid "" "Unable to reconfigure sharing settings for network %(network)s. Multiple " "tenants are using it." msgstr "" "No se ha podido volver a configurar los valores para la red %(network)s. " "Varios arrendatarios la están utilizando." #, python-format msgid "" "Unable to verify match:%(match)s as the parent resource: %(res)s was not " "found" msgstr "" "No se ha podido verificar la coincidencia:%(match)s como recurso primario: " "%(res)s no se ha encontrado" #, python-format msgid "Unexpected label for script %(script_name)s: %(labels)s" msgstr "Etiqueta inesperada para el script %(script_name)s: %(labels)s" #, python-format msgid "Unexpected number of alembic branch points: %(branchpoints)s" msgstr "Número inesperado de puntos de bifurcación alémbica: %(branchpoints)s" #, python-format msgid "Unexpected response code: %s" msgstr "Código de respuesta inesperado: %s" #, python-format msgid "Unexpected response: %s" msgstr "Respuesta inesperada : %s " #, python-format msgid "Unit name '%(unit)s' is not valid." msgstr "El nombre de unidad '%(unit)s' no es válido." #, python-format msgid "Unknown address type %(address_type)s" msgstr "Tipo de dirección desconocido %(address_type)s" #, python-format msgid "Unknown attribute '%s'." msgstr "Atributo desconocido '%s'." #, python-format msgid "Unknown chain: %r" msgstr "Cadena desconocida: %r" #, python-format msgid "Unknown network type %(network_type)s." msgstr "Tipo de red desconocido %(network_type)s." #, python-format msgid "Unknown quota resources %(unknown)s." msgstr "Recursos de cuota desconocidos %(unknown)s." msgid "Unmapped error" msgstr "Error no correlacionado" msgid "Unrecognized action" msgstr "Acción no reconocida" #, python-format msgid "Unrecognized attribute(s) '%s'" msgstr "Atributo(s) no reconocido(s) '%s'" msgid "Unrecognized field" msgstr "Campo no reconocido" msgid "Unsupported Content-Type" msgstr "Tipo de contenido no soportado" #, python-format msgid "Unsupported network type %(net_type)s." msgstr "Tipo de red no soportado %(net_type)s." #, python-format msgid "Unsupported port state: %(port_state)s." msgstr "Estado de puerto no soportado: %(port_state)s." msgid "Unsupported request type" msgstr "Tipo de solicitud no soportado" msgid "Updating default security group not allowed." msgstr "Actualización del grupo de seguridad predeterminado no permitida." msgid "" "Use ML2 l2population mechanism driver to learn remote MAC and IPs and " "improve tunnel scalability." msgstr "" "Use el controlador del mecanismo ML2 l2population para aprender el uso " "remoto MAC e IPs y mejorar la escalabilidad del túnel." msgid "Use broadcast in DHCP replies." msgstr "Utilizar la difusión en respuestas DHCP." msgid "Use either --delta or relative revision, not both" msgstr "Utilice --delta o la revisión relativa, pero no ambas" msgid "" "Use ipset to speed-up the iptables based security groups. Enabling ipset " "support requires that ipset is installed on L2 agent node." msgstr "" "Utilice ipset para agilizar los grupos de seguridad basados en iptables. " "Para habilitar el soporte para ipset es necesario que ipset esté instalado " "en el nodo agente L2." msgid "" "Use the root helper when listing the namespaces on a system. This may not be " "required depending on the security configuration. If the root helper is not " "required, set this to False for a performance improvement." msgstr "" "Utilice el ayudante raíz para listar los espacios de nombres en un sistema. " "Esto puede no ser necesario dependiendo de la configuración de seguridad. Si " "el ayudante raíz no es necesario, establézcalo en False para mejorar el " "rendimiento." msgid "" "Use veths instead of patch ports to interconnect the integration bridge to " "physical networks. Support kernel without Open vSwitch patch port support so " "long as it is set to True." msgstr "" "Utilice veths en lugar de puertos de parche para interconectar el puente de " "integración con las redes físicas. Se admite kernel sin soporte de puertos " "de parche Open vSwitch siempre y cuando esté definido a True." msgid "" "User (uid or name) running metadata proxy after its initialization (if " "empty: agent effective user)." msgstr "" "Usuario (uid o nombre) que ejecuta el proxy de metadatos después de su " "inicialización (si está vacío: usuario efectivo del agente)." msgid "User (uid or name) running this process after its initialization" msgstr "" "Usuario (uid o nombre) que ejecuta este proceso después de su inicialización" msgid "Username for connecting to designate in admin context" msgstr "" "Nombre de usuario para establecer conexión con el designado en el contexto " "de administración" msgid "VRRP authentication password" msgstr "Contraseña de autenticación de VRRP" msgid "VRRP authentication type" msgstr "Tipo de autenticación VRRP" msgid "VXLAN network unsupported." msgstr "Red VXLAN no soportada." msgid "" "Value of host kernel tick rate (hz) for calculating minimum burst value in " "bandwidth limit rules for a port with QoS. See kernel configuration file for " "HZ value and tc-tbf manual for more information." msgstr "" "Valor del tickrate del kernel host para calcular el valor mínimo de ráfaga " "en las reglas de limitación de ancho de banda para un puerto con QoS, " "Consulte en el archivo de configuración el valor de HZ y consulte el manual " "de tc-tbf para obtener más información." msgid "" "Value of latency (ms) for calculating size of queue for a port with QoS. See " "tc-tbf manual for more information." msgstr "" "Valor de latencia (ms) para calcular el tamaño de la cola para un puerto con " "QoS. Consulte el manual de tc-tbf para obtener más información." msgid "" "When external_network_bridge is set, each L3 agent can be associated with no " "more than one external network. This value should be set to the UUID of that " "external network. To allow L3 agent support multiple external networks, both " "the external_network_bridge and gateway_external_network_id must be left " "empty." msgstr "" "Cuando la opción external_network_bridge está establecida, cada agente L3 se " "pued asociar con una única red externa. Este valor se debe definir con el " "UUID de la red externa. Para permitir que el agente L3 admita varias redes " "externas, tanto external_network_bridge como gateway_external_network_id " "deben estar vacías." msgid "" "When proxying metadata requests, Neutron signs the Instance-ID header with a " "shared secret to prevent spoofing. You may select any string for a secret, " "but it must match here and in the configuration used by the Nova Metadata " "Server. NOTE: Nova uses the same config key, but in [neutron] section." msgstr "" "Cuando se envían solicitudes de metadatos por proxy, Neutron firma la " "cabecera ID de instancia con un secreto compartido para evitar la " "suplantación de identidad. Puede seleccionar cualquier cadena como secreto, " "pero debe coincidir con la que se haya utilizado en la configuration del " "servidor de metadatos de Nova. NOTA: Nova utiliza la misma clave de " "configuración, pero en la sección [neutron]." msgid "" "Where to store Neutron state files. This directory must be writable by the " "agent." msgstr "" "Dónde almacenar archivos de estado Neutron. Este directorio se debe poder " "escribir por el agente." msgid "" "With IPv6, the network used for the external gateway does not need to have " "an associated subnet, since the automatically assigned link-local address " "(LLA) can be used. However, an IPv6 gateway address is needed for use as the " "next-hop for the default route. If no IPv6 gateway address is configured " "here, (and only then) the neutron router will be configured to get its " "default route from router advertisements (RAs) from the upstream router; in " "which case the upstream router must also be configured to send these RAs. " "The ipv6_gateway, when configured, should be the LLA of the interface on the " "upstream router. If a next-hop using a global unique address (GUA) is " "desired, it needs to be done via a subnet allocated to the network and not " "through this parameter. " msgstr "" "Con IPv6, la red utilizada para la pasarela externa no debetener una subred " "asociada, ya que puede utilizarse la dirección de enlace local(LLA) asignada " "automáticamente. No obstante, se necesita una dirección de pasarela IPv6 " "parautilizarla como siguiente salto para la ruta predeterminada. Si no se " "configura aquí ningunadirección de pasarela IPv6, (y sólo entonces) se " "configurará un direccionador de Neutronpara obtener su ruta predeterminada " "de los avisos de direccionador (RA) deldireccionador en sentido ascendente; " "en este caso, el direccionador en sentido ascendente también " "debeconfigurarse para enviar estos RA. ipv6_gateway, cuando se configurada, " "debeser la LLA de interfaz en el direccionador en sentido ascendente. Si " "desea un siguiente salto utilizando una dirección exclusivo global (GUA), " "debe hacerse utilizando una subred asignada a la red, no mediante este " "parámetro." msgid "You must implement __call__" msgstr "Debe implementar __call__" msgid "" "You must provide a config file for bridge - either --config-file or " "env[NEUTRON_TEST_CONFIG_FILE]" msgstr "" "Debe proporcionar un archivo config para puente, ya sea --config-file o " "env[NEUTRON_TEST_CONFIG_FILE]" msgid "You must provide a revision or relative delta" msgstr "Debe proporcionar una revisión o delta relativa" msgid "a subnetpool must be specified in the absence of a cidr" msgstr "se debe especificar una agrupación de subredes si no hay un cidr" msgid "add_ha_port cannot be called inside of a transaction." msgstr "no se puede invocar a add_ha_port dentro de una transacción." msgid "allocation_pools allowed only for specific subnet requests." msgstr "" "allocation_pools sólo se permite para solicitudes de subred específicas." msgid "allocation_pools are not in the subnet" msgstr "allocation_pools no están en la subred" msgid "allocation_pools use the wrong ip version" msgstr "allocation_pools utiliza la versión de IP incorrecta" msgid "already a synthetic attribute" msgstr "ya es un atributo sintético" msgid "binding:profile value too large" msgstr "Valor de binding:profile demasiado grande" #, python-format msgid "cannot perform %(event)s due to %(reason)s" msgstr "no se puede llevar a cabo %(event)s debido a %(reason)s" msgid "cidr and prefixlen must not be supplied together" msgstr "cidr y prefixlen no pueden proporcionarse conjuntamente" #, python-format msgid "dhcp_agents_per_network must be >= 1. '%s' is invalid." msgstr "dhcp_agents_per_network debe ser >= 1. '%s' no es válido." msgid "dns_domain cannot be specified without a dns_name" msgstr "No se puede especificar dns_domain sin un dns_name" msgid "dns_name cannot be specified without a dns_domain" msgstr "No se puede especificar dns_name sin un dns_domain" msgid "fixed_ip_address cannot be specified without a port_id" msgstr "fixed_ip_address no se puede especificar sin un port_id" #, python-format msgid "has device owner %s" msgstr "tiene el propietario de dispositivo %s" msgid "in use" msgstr "en uso" #, python-format msgid "ip command failed on device %(dev_name)s: %(reason)s" msgstr "El mandato ip ha fallado en el dispositivo %(dev_name)s: %(reason)s" #, python-format msgid "ip command failed: %(reason)s" msgstr "Ha fallado el mandato ip: %(reason)s" #, python-format msgid "ip link capability %(capability)s is not supported" msgstr "No hay soporte para la función de ip link %(capability)s" #, python-format msgid "ip link command is not supported: %(reason)s" msgstr "No hay soporte para el mandato ip link: %(reason)s" msgid "ip_version must be specified in the absence of cidr and subnetpool_id" msgstr "ip_version debe especificarse en ausencia de cidr y subnetpool_id" msgid "ipv6_address_mode is not valid when ip_version is 4" msgstr "ipv6_address_mode no es válido cuando ip_version es 4" msgid "ipv6_ra_mode is not valid when ip_version is 4" msgstr "ipv6_ra_mode no es válido cuando ip_version es 4" #, python-format msgid "" "ipv6_ra_mode set to '%(ra_mode)s' with ipv6_address_mode set to " "'%(addr_mode)s' is not valid. If both attributes are set, they must be the " "same value" msgstr "" "ipv6_ra_mode establecido en '%(ra_mode)s' con ipv6_address_mode establecido " "en '%(addr_mode)s' no es válido. Si se establecen ambos atributos, deben " "tener el mismo valor" msgid "mac address update" msgstr "Actualización de la dirección MAC" msgid "must provide exactly 2 arguments - cidr and MAC" msgstr "debe dar exactamente 2 argumentos: cidr y MAC" msgid "network_type required" msgstr "network_type requerido" #, python-format msgid "network_type value '%s' not supported" msgstr "valor network_type '%s' no admitido" msgid "new subnet" msgstr "nueva subred" #, python-format msgid "physical_network '%s' unknown for flat provider network" msgstr "physical_network '%s' desconocida para la red de proveedor simple" msgid "physical_network required for flat provider network" msgstr "se requiere physical_network para la red de proveedor simple" #, python-format msgid "provider:physical_network specified for %s network" msgstr "proveedor:physical_network especificado para la red %s" msgid "respawn_interval must be >= 0 if provided." msgstr "respawn_interval debe ser >= 0 si se proporciona." #, python-format msgid "segmentation_id out of range (%(min)s through %(max)s)" msgstr "segmentation_id fuera de rango (%(min)s a %(max)s)" msgid "segmentation_id requires physical_network for VLAN provider network" msgstr "" "segmentation_id requiere physical_network para la red de proveedor VLAN" msgid "shared attribute switching to synthetic" msgstr "atributo compartido cambiando a sintético" #, python-format msgid "" "subnetpool %(subnetpool_id)s cannot be updated when associated with shared " "address scope %(address_scope_id)s" msgstr "" "La agrupación de subred %(subnetpool_id)s no se puede actualizar cuando está " "asociada con el ámbito de dirección compartida %(address_scope_id)s" msgid "subnetpool_id and use_default_subnetpool cannot both be specified" msgstr "" "No se puede especificar a la vez subnetpool_id y use_default_subnetpool" msgid "the nexthop is not connected with router" msgstr "el siguiente salto no está conectado con el direccionador" msgid "the nexthop is used by router" msgstr "el siguiente salto lo está utilizando el direccionador" neutron-12.1.1/neutron/locale/pt_BR/0000775000175000017500000000000013553660156017261 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/pt_BR/LC_MESSAGES/0000775000175000017500000000000013553660156021046 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/pt_BR/LC_MESSAGES/neutron.po0000664000175000017500000033464213553660047023113 0ustar zuulzuul00000000000000# Translations template for neutron. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the neutron project. # # Translators: # Gabriel Wainer, 2013 # Andreas Jaeger , 2016. #zanata # Fernando Pimenta , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: neutron VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-03-14 04:19+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-03-28 07:43+0000\n" "Last-Translator: Copied by Zanata \n" "Language: pt_BR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 4.3.3\n" "Language-Team: Portuguese (Brazil)\n" #, python-format msgid "" "\n" "Command: %(cmd)s\n" "Exit code: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" msgstr "" "\n" "Comando: %(cmd)s\n" "Código de saída: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" #, python-format msgid "" "%(branch)s HEAD file does not match migration timeline head, expected: " "%(head)s" msgstr "" "O arquivo HEAD %(branch)s não corresponde ao cabeçalho da linha de tempo de " "migração, esperado: %(head)s" #, python-format msgid "%(id)s is not a valid %(type)s identifier" msgstr "%(id)s não é um identificador %(type)s válido" #, python-format msgid "" "%(invalid_dirs)s is invalid value for sort_dirs, valid value is '%(asc)s' " "and '%(desc)s'" msgstr "" "%(invalid_dirs)s é um valor inválido para sort_dirs, o valor válido é " "'%(asc)s' e '%(desc)s'" #, python-format msgid "%(key)s prohibited for %(tunnel)s provider network" msgstr "%(key)s proibida para rede de provedor %(tunnel)s" #, python-format msgid "%(name)s '%(addr)s' does not match the ip_version '%(ip_version)s'" msgstr "%(name)s '%(addr)s' não corresponde à ip_version '%(ip_version)s'" #, python-format msgid "%s cannot be called while in offline mode" msgstr "%s não pode ser chamado durante o modo offline" #, python-format msgid "%s is invalid attribute for sort_keys" msgstr "%s é um atributo inválido para sort_keys" #, python-format msgid "%s is not a valid VLAN tag" msgstr "%s não é um tag de VLAN válido" #, python-format msgid "%s must implement get_port_from_device or get_ports_from_devices." msgstr "%s deve implementar get_port_from_device ou get_ports_from_devices." #, python-format msgid "%s prohibited for VLAN provider network" msgstr "%s proibido para rede de provedor VLAN" #, python-format msgid "%s prohibited for flat provider network" msgstr "%s proibido para rede de provedor flat" #, python-format msgid "%s prohibited for local provider network" msgstr "%s proibido para rede de provedor local" #, python-format msgid "'%s' is not a valid RBAC object type" msgstr "'%s' não é um tipo de objeto RBAC válido" #, python-format msgid "'%s' is not supported for filtering" msgstr "'%s' não é suportado para filtragem" #, python-format msgid "'module' object has no attribute '%s'" msgstr "O objeto 'módulo' não possui nenhum atributo '%s'" msgid "'port_max' is smaller than 'port_min'" msgstr "'port_max' é menor que 'port_min'" msgid "0 is not allowed as CIDR prefix length" msgstr "0 não é permitido como um comprimento do prefixo CIDR" msgid "A cidr must be specified in the absence of a subnet pool" msgstr "Um cidr deve ser especificado na ausência de um conjunto de sub-rede" msgid "" "A decimal value as Vendor's Registered Private Enterprise Number as required " "by RFC3315 DUID-EN." msgstr "" "Um valor decimal, como o Número de Empresa Privada Registrada do Fornecedor, " "conforme requerido pelo RFC3315 DUID-EN." #, python-format msgid "A default external network already exists: %(net_id)s." msgstr "Uma rede externa padrão já existe: %(net_id)s." msgid "" "A default subnetpool for this IP family has already been set. Only one " "default may exist per IP family" msgstr "" "Um conjunto de sub-redes padrão para essa família de IP já foi configurado. " "Apenas um padrão pode existir por família de IP." msgid "A metering driver must be specified" msgstr "Um driver de medição deve ser especificado" msgid "API for retrieving service providers for Neutron advanced services" msgstr "" "API para recuperação de provedores de serviço para serviços avançados do " "Neutron" msgid "Aborting periodic_sync_routers_task due to an error." msgstr "Interrompendo periodic_sync_routers_task devido a um erro." msgid "Access to this resource was denied." msgstr "Acesso à este recurso foi negado." msgid "Action to be executed when a child process dies" msgstr "Ação a ser executada quando um processo-filho morre" msgid "" "Add comments to iptables rules. Set to false to disallow the addition of " "comments to generated iptables rules that describe each rule's purpose. " "System must support the iptables comments module for addition of comments." msgstr "" "Adicionar comentários a regras do iptables. Configure como falso para " "desabilitar a adição de comentários para tabelas de regras geradas que " "descrevam o propósito de cada regra. O sistema deve suportar o módulo de " "comentários do iptables para adicionar comentários." msgid "Address not present on interface" msgstr "Endereço não está presente na interface" msgid "" "Address to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "Endereço para atender conexões OpenFlow. Usado somente para driver 'native'." msgid "Adds test attributes to core resources." msgstr "Inclui atributos de teste aos recursos principais." #, python-format msgid "Agent %(id)s is not a L3 Agent or has been disabled" msgstr "O agente %(id)s não é um agente L3 ou foi desativado" #, python-format msgid "Agent %(id)s is not a valid DHCP Agent or has been disabled" msgstr "O agente %(id)s não é um Agente DHCP válido ou foi desativado" msgid "" "Agent starts with admin_state_up=False when enable_new_agents=False. In the " "case, user's resources will not be scheduled automatically to the agent " "until admin changes admin_state_up to True." msgstr "" "Agente inicia com admin_state_up=False quando enable_new_agents=False. No " "caso, os recursos do usuário não serão planejados automaticamente para o " "agente até que o administrador mude admin_state_up para True." #, python-format msgid "Agent updated: %(payload)s" msgstr "Agente atualizado:%(payload)s" msgid "Allow auto scheduling networks to DHCP agent." msgstr "Permitir o planejamento automático de redes para o agente DHCP." msgid "Allow auto scheduling of routers to L3 agent." msgstr "Permitir planejamento automático de roteadores para agente L3." msgid "" "Allow overlapping IP support in Neutron. Attention: the following parameter " "MUST be set to False if Neutron is being used in conjunction with Nova " "security groups." msgstr "" "Permitir sobreposição de suporte IP no Neutron. Atenção: o parâmetro a " "seguir DEVERà ser configurado para False se o Neutron estiver sendo usado em " "conjunto com os grupos de segurança do Nova." msgid "Allow running metadata proxy." msgstr "Permite executar proxy de metadados." msgid "Allow sending resource operation notification to DHCP agent" msgstr "" "Permitir envio de notificação de operação de recurso para o agente DHCP" msgid "Allow the creation of PTR records" msgstr "Permitir a criação de registros PTR" msgid "Allow the usage of the bulk API" msgstr "Permitir o uso da API em massa" msgid "Allow to perform insecure SSL (https) requests to nova metadata" msgstr "" "Permita executar solicitações (https) de SSL inseguras para metadados nova" msgid "" "Allows for serving metadata requests coming from a dedicated metadata access " "network whose CIDR is 169.254.169.254/16 (or larger prefix), and is " "connected to a Neutron router from which the VMs send metadata:1 request. In " "this case DHCP Option 121 will not be injected in VMs, as they will be able " "to reach 169.254.169.254 through a router. This option requires " "enable_isolated_metadata = True." msgstr "" "Permite entregar solicitações de metadados provenientes de uma rede de " "acesso de metadados dedicada cujo CIDR é 169.254.169.254/16 (ou um prefixo " "maior), e é conectado a um roteador Neutron a partir do qual as MVs enviam a " "solicitação metadata:1. Nesse caso, a Opção 121 do DHCP não será injetada " "nas MVs já que elas poderão acessar 169.254.169.254 por meio de um roteador. " "Essa opção requer enable_isolated_metadata = True." msgid "An RBAC policy already exists with those values." msgstr "Uma política RBAC já existe com esses valores." msgid "An identifier must be specified when updating a subnet" msgstr "Um identificador deve ser especificado ao atualizar uma sub-rede" msgid "An interface driver must be specified" msgstr "Um driver de interface deve ser especificado" msgid "" "An ordered list of extension driver entrypoints to be loaded from the " "neutron.ml2.extension_drivers namespace. For example: extension_drivers = " "port_security,qos" msgstr "" "Uma lista ordenada de pontos de entrada do driver de extensão a serem " "carregados a partir do namespace neutron.ml2.extension_drivers. Por exemplo: " "extension_drivers = port_security,qos" msgid "" "An ordered list of networking mechanism driver entrypoints to be loaded from " "the neutron.ml2.mechanism_drivers namespace." msgstr "" "Lista ordenada de pontos de entrada do driver de mecanismo de rede que será " "carregada do namespace neutron.ml2.mechanism_drivers." msgid "An unknown error has occurred. Please try your request again." msgstr "Ocorreu um erro desconhecido. Tente a solicitação novamente." msgid "Async process didn't respawn" msgstr "O processo assíncrono não sofreu spawn novamente" msgid "Authorization URL for connecting to designate in admin context" msgstr "" "URL de autorização para conexão ao Designate no contexto de admnistrador" msgid "Automatically remove networks from offline DHCP agents." msgstr "Remover automaticamente as redes de agentes DHCP offline." msgid "" "Automatically reschedule routers from offline L3 agents to online L3 agents." msgstr "" "Reagende roteadores automaticamente de agentes L3 offline para agentes L3 " "online." msgid "Availability zone of this node" msgstr "Zona de disponibilidade deste nó" msgid "Available commands" msgstr "Comandos disponíveis" msgid "Backend does not support VLAN Transparency." msgstr "O backend não suporta a Transparência da VLAN." #, python-format msgid "Base MAC: %s" msgstr "MAC Base: %s" msgid "" "Base log dir for dnsmasq logging. The log contains DHCP and DNS log " "information and is useful for debugging issues with either DHCP or DNS. If " "this section is null, disable dnsmasq log." msgstr "" "Diretório de log base para criação de log dnsmasq. O log contém informações " "de log DHCP e DNS e é útil para depurar problemas com DHCP ou DNS. Se esta " "seção for nula, desative o log dnsmasq." msgid "Body contains invalid data" msgstr "O corpo contém dados inválidos" msgid "Both network_id and router_id are None. One must be provided." msgstr "O network_id e o router_id são Nenhum. Um deles deve ser fornecido." #, python-format msgid "Bridge %(bridge)s does not exist." msgstr "A ponte %(bridge)s não existe." msgid "Bulk operation not supported" msgstr "Operação em massa não suportada" msgid "CIDR to monitor" msgstr "CIDR para monitorar" #, python-format msgid "Callback for %(resource_type)s not found" msgstr "Retorno de chamada para %(resource_type)s não localizado" #, python-format msgid "Callback for %(resource_type)s returned wrong resource type" msgstr "" "Retorno de chamada para %(resource_type)s retornou tipo de recurso errado" #, python-format msgid "Cannot add floating IP to port %s that has no fixed IPv4 addresses" msgstr "" "Não é possível incluir IP flutuante na porta %s que não tem endereços IPv4 " "fixos" #, python-format msgid "Cannot add multiple callbacks for %(resource_type)s" msgstr "" "Não é possível incluir vários retornos de chamada para %(resource_type)s" #, python-format msgid "Cannot allocate IPv%(req_ver)s subnet from IPv%(pool_ver)s subnet pool" msgstr "" "Não é possível alocar a sub-rede IPv%(req_ver)s a partir do conjunto de sub-" "rede IPv%(pool_ver)s" msgid "Cannot allocate requested subnet from the available set of prefixes" msgstr "" "Não é possível alocar a sub-rede solicitada a partir do conjunto disponível " "de prefixos" msgid "Cannot disable enable_dhcp with ipv6 attributes set" msgstr "Não é possível desativar enable_dhcp com conjunto de atributos ipv6" #, python-format msgid "Cannot handle subnet of type %(subnet_type)s" msgstr "Não é possível manipular a sub-rede do tipo %(subnet_type)s" msgid "Cannot have multiple IPv4 subnets on router port" msgstr "Não é possível ter diversas sub-redes IPV4 na porta do roteador" #, python-format msgid "" "Cannot have multiple router ports with the same network id if both contain " "IPv6 subnets. Existing port %(p)s has IPv6 subnet(s) and network id %(nid)s" msgstr "" "Não é possível ter várias portas de roteador com o mesmo ID de rede se ambas " "contiverem sub-redes IPv6. A porta existente %(p)s possui sub-rede(s) IPv6 e " "o ID de rede %(nid)s" #, python-format msgid "" "Cannot host distributed router %(router_id)s on legacy L3 agent %(agent_id)s." msgstr "" "Não é possível hospedar o roteador distribuído %(router_id)s no agente L3 " "legacy %(agent_id)s." msgid "Cannot mix IPv4 and IPv6 prefixes in a subnet pool." msgstr "" "Não é possível combinarprefixos IPv4 e IPv6 em um conjunto de sub-rede." msgid "Cannot specify both subnet-id and port-id" msgstr "Não é possível especificar subnet-id e port-id" msgid "Cannot understand JSON" msgstr "Não é possível entender JSON" #, python-format msgid "Cannot update read-only attribute %s" msgstr "Não é possível atualizar o atributo de leitura %s" msgid "Certificate Authority public key (CA cert) file for ssl" msgstr "" "Arquivo de chave pública da autoridade de certificação (certificado CA) para " "ssl" #, python-format msgid "" "Change would make usage less than 0 for the following resources: %(unders)s." msgstr "A mudança faria uso de menos de 0 dos recursos a seguir: %(unders)s." msgid "Check ebtables installation" msgstr "Verificar instalação de ebtables" msgid "Check for ARP header match support" msgstr "Verificar suporte de correspondência de cabeçalho ARP" msgid "Check for ARP responder support" msgstr "Verifique se há suporte respondente para ARP" msgid "Check for ICMPv6 header match support" msgstr "Verificar suporte de correspondência de cabeçalho ICMPv6" msgid "Check for OVS Geneve support" msgstr "Verificar suporte a OVS Geneve" msgid "Check for OVS vxlan support" msgstr "Verifique o suporte do vxlan do OVS" msgid "Check for VF management support" msgstr "Verifique o suporte de gerenciamento de VF" msgid "Check for iproute2 vxlan support" msgstr "Verifique o suporte do vxlan do iproute2" msgid "Check for nova notification support" msgstr "Verifique suporte para nova notificação" msgid "Check for patch port support" msgstr "Verifique o suporte para a porta de correção" msgid "Check ip6tables installation" msgstr "Verificar instalação do ip6tables" msgid "Check ipset installation" msgstr "Verificar instalação do ipset" msgid "Check keepalived IPv6 support" msgstr "Verificar suporte a keepalived IPv6" msgid "Check minimal dibbler version" msgstr "Verificar versão do dibbler mínima" msgid "Check minimal dnsmasq version" msgstr "Verifique a versão dnsmasq mínima" msgid "Check netns permission settings" msgstr "Verifique as configurações de permissão netns" msgid "Check ovs conntrack support" msgstr "Verificar suporte conntrack do OVS" msgid "Check ovsdb native interface support" msgstr "Verifique o suporte da interface nativa ovsdb" #, python-format msgid "" "Cidr %(subnet_cidr)s of subnet %(subnet_id)s overlaps with cidr %(cidr)s of " "subnet %(sub_id)s" msgstr "" "O cidr %(subnet_cidr)s de sub-rede %(subnet_id)s se sobrepõe com o cidr " "%(cidr)s da sub-rede %(sub_id)s" msgid "Cleanup resources of a specific agent type only." msgstr "Limpar recursos somente de um tipo de agente específico." msgid "Client certificate for nova metadata api server." msgstr "Certificado do cliente para o servidor da API de metadados nova." msgid "" "Comma-separated list of : tuples, mapping " "network_device to the agent's node-specific list of virtual functions that " "should not be used for virtual networking. vfs_to_exclude is a semicolon-" "separated list of virtual functions to exclude from network_device. The " "network_device in the mapping should appear in the physical_device_mappings " "list." msgstr "" "Lista separada por vírgulas de tuplas :, " "mapeando network_device para a lista específica do nó do agente de funções " "virtuais que não devem ser usadas para rede virtual. vfs_to_exclude é uma " "lista separada por ponto-e-vírgula de funções virtuais para excluir do " "network_device. O network_device no mapeamento deve aparecer na lista " "physical_device_mappings." msgid "" "Comma-separated list of : tuples mapping " "physical network names to the agent's node-specific physical network device " "interfaces of SR-IOV physical function to be used for VLAN networks. All " "physical networks listed in network_vlan_ranges on the server should have " "mappings to appropriate interfaces on each agent." msgstr "" "Lista separada por vírgula de : tuplas que " "mapeiam nomes de rede física para as interfaces de dispositivo de rede " "física específicas do nó do agente da função física SR-IOV a serem usadas " "para redes VLAN. Todas as redes físicas listadas em network_vlan_ranges no " "servidor devem ter mapeamentos para as interfaces apropriadas em cada agente." msgid "" "Comma-separated list of : tuples " "mapping physical network names to the agent's node-specific physical network " "interfaces to be used for flat and VLAN networks. All physical networks " "listed in network_vlan_ranges on the server should have mappings to " "appropriate interfaces on each agent." msgstr "" "Lista separada por vírgulas de : " "tuplas que mapeiam nomes de rede física para interfaces de rede física " "específicas do nó do agente a serem usadas para redes simples e de VLAN. " "Todas as redes físicas listadas em network_vlan_ranges no servidor devem ter " "mapeamentos para as interfaces apropriadas em cada agente." msgid "" "Comma-separated list of : tuples enumerating ranges of GRE " "tunnel IDs that are available for tenant network allocation" msgstr "" "Lista separada por vírgula de tuplas : enumerando as " "faixas de IDs de túnel GRE que estão disponíveis para alocação de redes de " "tenant" msgid "" "Comma-separated list of : tuples enumerating ranges of " "Geneve VNI IDs that are available for tenant network allocation" msgstr "" "Lista separada por vírgula de tuplas : enumerando " "intervalos de IDs Geneve VNI que estão disponíveis para alocação de rede " "locatária" msgid "" "Comma-separated list of : tuples enumerating ranges of " "VXLAN VNI IDs that are available for tenant network allocation" msgstr "" "Lista de valores separados por vírgula de tuplas : " "enumerando faixas de VXLAN VNI IDs que estão disponíveis para alocação de " "redes de tenant" msgid "" "Comma-separated list of the DNS servers which will be used as forwarders." msgstr "" "Lista separada por vírgula dos servidores DNS que será utilizada como " "encaminhadores." msgid "Command to execute" msgstr "Comando a ser executado" msgid "Config file for interface driver (You may also use l3_agent.ini)" msgstr "" "Arquivo de configuração para driver de interface (também é possível usar " "l3_agent.ini)" #, python-format msgid "Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s" msgstr "O valor conflitante ethertype %(ethertype)s para CIDR %(cidr)s" msgid "" "Controls whether the neutron security group API is enabled in the server. It " "should be false when using no security groups or using the nova security " "group API." msgstr "" "Controla se a API do grupo de segurança neutron está ativada no servidor. " "Ele deve ser false quando não usa nenhum grupo de segurança ou usa a API do " "grupo de segurança nova." #, python-format msgid "Could not bind to %(host)s:%(port)s after trying for %(time)d seconds" msgstr "" "Não foi possível associar-se à %(host)s:%(port)s após tentar por %(time)d " "segundos" msgid "Could not deserialize data" msgstr "Não foi possível desserializar dados" #, python-format msgid "" "Current gateway ip %(ip_address)s already in use by port %(port_id)s. Unable " "to update." msgstr "" "IP atual do gateway %(ip_address)s já está em uso pela porta %(port_id)s. " "Não é possível atualizar." msgid "" "DHCP lease duration (in seconds). Use -1 to tell dnsmasq to use infinite " "lease times." msgstr "" "Duração de lease de DHCP (em segundos). Use -1 para dizer ao dnsmasq para " "usar lease infinitas vezes." msgid "" "DVR deployments for VXLAN/GRE/Geneve underlays require L2-pop to be enabled, " "in both the Agent and Server side." msgstr "" "Implementações de DVR para bases VXLAN/GRE/Geneve requerem que L2-pop esteja " "ativado, no lado do Agente e do Servidor." msgid "" "Database engine for which script will be generated when using offline " "migration." msgstr "" "Mecanismo de bancos de dados para o qual o script será gerado ao usar a " "migração off-line." msgid "Default external networks must be shared to everyone." msgstr "Redes externas padrão devem ser compartilhadas para todos." msgid "" "Default network type for external networks when no provider attributes are " "specified. By default it is None, which means that if provider attributes " "are not specified while creating external networks then they will have the " "same type as tenant networks. Allowed values for external_network_type " "config option depend on the network type values configured in type_drivers " "config option." msgstr "" "Tipo de rede padrão para redes externas quando nenhum atributo de provedor é " "especificado. Por padrão, é Nenhum, o que significa que se os atributos de " "provedor não forem especificados durante a criação de redes externas, eles " "terão o mesmo tipo que as redes locatárias. Os valores permitidos para a " "opção de configuração external_network_type dependem dos valores de tipo de " "rede configurados em type_drivers CapturePostTypes." msgid "" "Default number of RBAC entries allowed per tenant. A negative value means " "unlimited." msgstr "" "Número padrão de entradas RBAC permitido por locatário. Um valor negativo " "significa ilimitado." msgid "" "Default number of resource allowed per tenant. A negative value means " "unlimited." msgstr "" "Número padrão de recurso permitido por locatário. Um valor negativo " "significa ilimitado." msgid "Default security group" msgstr "Grupo de segurança padrão" msgid "Default security group already exists." msgstr "O grupo de segurança padrão já existe." msgid "" "Default value of availability zone hints. The availability zone aware " "schedulers use this when the resources availability_zone_hints is empty. " "Multiple availability zones can be specified by a comma separated string. " "This value can be empty. In this case, even if availability_zone_hints for a " "resource is empty, availability zone is considered for high availability " "while scheduling the resource." msgstr "" "Valor padrão das dicas de zona de disponibilidade. A zona de disponibilidade " "reconhece que os planejadores utilizam esse valor quando " "availability_zone_hints de recursos estiver vazio. Diversas zonas de " "disponibilidades podem ser especificadas por uma sequência separada por " "vírgulas. Esse valor pode ser vazio. Nesse caso, mesmo que " "availability_zone_hints de um recurso esteja vazio, a zona de " "disponibilidade será considerada para alta disponibilidade ao planejar o " "recurso." msgid "" "Define the default value of enable_snat if not provided in " "external_gateway_info." msgstr "" "Defina o valor padrão de enable_snat se não fornecido em " "external_gateway_info." msgid "" "Defines providers for advanced services using the format: :" ":[:default]" msgstr "" "Define provedores para serviços avançados usando o formato::" ":[:default]" msgid "Delete the namespace by removing all devices." msgstr "Excluir o namespace removendo todos os dispositivos." #, python-format msgid "Deleting port %s" msgstr "Excluindo porta %s" #, python-format msgid "Deployment error: %(reason)s." msgstr "Erro de implementação: %(reason)s" msgid "Destroy IPsets even if there is an iptables reference." msgstr "Destrua os IPsets mesmo se houver uma referência de iptables." msgid "Destroy all IPsets." msgstr "Destrua todos os IPsets." #, python-format msgid "Device %(dev_name)s in mapping: %(mapping)s not unique" msgstr "Dispositivo %(dev_name)s no mapeamento: %(mapping)s não exclusivo" #, python-format msgid "Device name %(dev_name)s is missing from physical_device_mappings" msgstr "" "Nome do dispositivo %(dev_name)s está ausente no physical_device_mappings" msgid "Device not found" msgstr "Dispositivo não localizado." #, python-format msgid "" "Distributed Virtual Router Mac Address for host %(host)s does not exist." msgstr "" "O endereço Mac do Roteador Virtual Distribuído para o host %(host)s não " "existe." msgid "Domain to use for building the hostnames" msgstr "Domínio a ser usado para construir os nomes dos hosts" msgid "Downgrade no longer supported" msgstr "O downgrade não é mais suportado" #, python-format msgid "Driver %s is not unique across providers" msgstr "Driver %s não é único em todos provedores" msgid "Driver for external DNS integration." msgstr "O driver para intgração do DNS externa." msgid "Driver for security groups firewall in the L2 agent" msgstr "Driver para firewall para grupos de segurança no agente L2" msgid "Driver to use for scheduling network to DHCP agent" msgstr "Driver a ser usado para planejar a rede para o agente DHCP" msgid "Driver to use for scheduling router to a default L3 agent" msgstr "Driver a ser usado para planejar o roteador para um agente L3 padrão" msgid "" "Driver used for ipv6 prefix delegation. This needs to be an entry point " "defined in the neutron.agent.linux.pd_drivers namespace. See setup.cfg for " "entry points included with the neutron source." msgstr "" "Driver usado para delegação de prefixo ipv6. Este precisa ser um ponto de " "entrada definido no namespace neutron.agent.linux.pd_drivers. Consulte setup." "cfg para pontos de entrada incluídos com a origem neutron." #, python-format msgid "" "Duplicate L3HARouterAgentPortBinding is created for router(s) %(router)s. " "Database cannot be upgraded. Please, remove all duplicates before upgrading " "the database." msgstr "" "L3HARouterAgentPortBinding duplicado é criado para um ou mais roteadores " "%(router)s. O banco de dados não pode ser atualizado. Remova todas as " "duplicatas antes de fazer upgrade do banco de dados." msgid "Duplicate Security Group Rule in POST." msgstr "Regra do Grupo de Segurança Duplicada no Autoteste Inicial." msgid "Duplicate address detected" msgstr "Endereço duplicado detectado" msgid "Duplicate segment entry in request." msgstr "Entrada duplicada de segmento na requisição." #, python-format msgid "ERROR: %s" msgstr "ERRO: %s" msgid "" "ERROR: Unable to find configuration file via the default search paths (~/." "neutron/, ~/, /etc/neutron/, /etc/) and the '--config-file' option!" msgstr "" "ERRO: Não é possível localizar o arquivo de configuração através dos " "caminhos de procura padrão (~/.neutron/, ~/, /etc/neutron/, /etc/) e a opção " "'--config-file'!" msgid "" "Either one of parameter network_id or router_id must be passed to _get_ports " "method." msgstr "" "Um dos parâmetros network_id ou router_id deve ser transmitido para o método " "_get_ports." msgid "Either subnet_id or port_id must be specified" msgstr "subnet_id ou port_id deve ser especificado" msgid "Empty physical network name." msgstr "Nome da rede física vazio." msgid "Empty subnet pool prefix list." msgstr "Lista do prefixo do conjunto de sub-rede vazia." msgid "Enable HA mode for virtual routers." msgstr "Ative o modo de alta disponibilidade para roteadores virtuais." msgid "Enable SSL on the API server" msgstr "Habilite SSL no servidor de API" msgid "" "Enable VXLAN on the agent. Can be enabled when agent is managed by ml2 " "plugin using linuxbridge mechanism driver" msgstr "" "Ative o VXLAN no agente. Pode ser ativado quando o agente é gerenciado pelo " "plug-in ml2 usando o driver do mecanismo linuxbridge" msgid "" "Enable local ARP responder if it is supported. Requires OVS 2.1 and ML2 " "l2population driver. Allows the switch (when supporting an overlay) to " "respond to an ARP request locally without performing a costly ARP broadcast " "into the overlay." msgstr "" "Ative respondente ARP local se ele for suportado. Requer OVS 2.1 e driver " "ML2 l2population. Permite que o comutador (ao suportar uma sobreposição) " "para responder a uma solicitação de ARP localmente, sem executar uma " "transmissão dispendiosa de ARP na sobreposição." msgid "" "Enable services on an agent with admin_state_up False. If this option is " "False, when admin_state_up of an agent is turned False, services on it will " "be disabled. Agents with admin_state_up False are not selected for automatic " "scheduling regardless of this option. But manual scheduling to such agents " "is available if this option is True." msgstr "" "Ativar os serviços em um agente com admin_state_up False. Se essa opção for " "False, quando admin_state_up de um agente tornar-se False, os serviços nele " "serão desativados. Os agentes com admin_state_up False não são selecionados " "para planejamento automático, independentemente dessa opção. Mas o " "planejamento manual para tais agentes estará disponível se essa opção for " "True." msgid "" "Enables IPv6 Prefix Delegation for automatic subnet CIDR allocation. Set to " "True to enable IPv6 Prefix Delegation for subnet allocation in a PD-capable " "environment. Users making subnet creation requests for IPv6 subnets without " "providing a CIDR or subnetpool ID will be given a CIDR via the Prefix " "Delegation mechanism. Note that enabling PD will override the behavior of " "the default IPv6 subnetpool." msgstr "" "Ativa o IPv6 Prefix Delegation para alocação automática de CIDR de sub-rede. " "Configure para True para ativar o IPv6 Prefix Delegation para alocação de " "sub-rede em um ambiente apto para PD. Os usuários que fazem solicitações de " "criação de sub-rede para sub-redes IPv6 sem fornecer um CIDR ou um ID de " "conjunto de sub-redes receberão um CIDR por meio do mecanismo Prefix " "Delegation. Observe que a ativação do PD substitui o comportamento do " "conjunto de sub-redes IPv6 padrão. " msgid "" "Enables the dnsmasq service to provide name resolution for instances via DNS " "resolvers on the host running the DHCP agent. Effectively removes the '--no-" "resolv' option from the dnsmasq process arguments. Adding custom DNS " "resolvers to the 'dnsmasq_dns_servers' option disables this feature." msgstr "" "Permite que o serviço dnsmasq forneça resolução de nome para instâncias por " "meio dos resolvedores de DNS no host que executa o agente DHCP. Remove " "efetivamente a opção '--no-resolv' dos argumentos do processo dnsmasq. A " "inclusão dos resolvedores de DNS customizados na opção " "'dnsmasq_dns_servers' desativa esse recurso." msgid "End of VLAN range is less than start of VLAN range" msgstr "Final da faixa de VLAN é menor que o início da faixa de VLAN" msgid "End of tunnel range is less than start of tunnel range" msgstr "" "O término do intervalo do túnel é inferior ao início do intervalo do túnel" #, python-format msgid "Error %(reason)s while attempting the operation." msgstr "Erro %(reason)s ao tentar a operação." #, python-format msgid "Error parsing dns address %s" msgstr "Erro ao analisar endereço dns %s" #, python-format msgid "Error while reading %s" msgstr "Erro ao ler %s" #, python-format msgid "" "Exceeded %s second limit waiting for address to leave the tentative state." msgstr "" "Excedido limite de %s segundos ao aguardar o endereço sair do estado de " "tentativa." msgid "Existing prefixes must be a subset of the new prefixes" msgstr "Prefixos existentes devem ser um subconjunto dos novos prefixos" #, python-format msgid "" "Exit code: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" msgstr "" "Código de saída: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; " "Stderr: %(stderr)s" #, python-format msgid "Extension %(driver)s failed." msgstr "Extensão %(driver)s com falha." #, python-format msgid "" "Extension driver %(driver)s required for service plugin %(service_plugin)s " "not found." msgstr "" "Driver da extensão %(driver)s necessário para o plug-in de serviço " "%(service_plugin)s não localizado." msgid "" "Extension to use alongside ml2 plugin's l2population mechanism driver. It " "enables the plugin to populate VXLAN forwarding table." msgstr "" "Extensão a ser usada ao lado do driver do mecanismo l2population do plug-in " "ml2. Ela permite que o plug-in preencha a tabela de encaminhamento de VXLAN." #, python-format msgid "Extension with alias %s does not exist" msgstr "A extensão com %s não existe" msgid "Extensions list to use" msgstr "Lista de extensões a serem usadas" #, python-format msgid "Extensions not found: %(extensions)s." msgstr "Extensões não localizadas: %(extensions)s." #, python-format msgid "External IP %s is the same as the gateway IP" msgstr "O IP externo %s é o mesmo que o IP de gateway" #, python-format msgid "Failed rescheduling router %(router_id)s: no eligible l3 agent found." msgstr "" "Falha ao reagendar o roteador %(router_id)s: nenhum agente l3 elegível " "encontrado." #, python-format msgid "Failed scheduling router %(router_id)s to the L3 Agent %(agent_id)s." msgstr "" "Falha ao planejar o roteador %(router_id)s para o Agente L3 %(agent_id)s." #, python-format msgid "Failed to allocate subnet: %(reason)s." msgstr "Falha ao alocar a sub-rede: %(reason)s." msgid "" "Failed to associate address scope: subnetpools within an address scope must " "have unique prefixes." msgstr "" "Falha ao associar o escopo de endereço: Os conjuntos de sub-redes dentro de " "um escopo de endereço devem possui prefixos exclusivos." #, python-format msgid "" "Failed to create port on network %(network_id)s, because fixed_ips included " "invalid subnet %(subnet_id)s" msgstr "" "Falha ao criar a porta na rede %(network_id)s, porque fixed_ips incluía uma " "sub-rede inválida %(subnet_id)s" #, python-format msgid "Failed to locate source for %s." msgstr "Falha ao localizar origem para %s." msgid "Failed to remove supplemental groups" msgstr "Falha ao remover grupos suplementares" #, python-format msgid "Failed to set gid %s" msgstr "Falha ao configurar gid %s" #, python-format msgid "Failed to set uid %s" msgstr "Falha ao configurar uid %s" #, python-format msgid "Failed to set-up %(type)s tunnel port to %(ip)s" msgstr "Falha ao setar porta do túnel %(type)s para %(ip)s" msgid "Failure applying iptables rules" msgstr "Falha ao aplicar regras do iptables" #, python-format msgid "Failure waiting for address %(address)s to become ready: %(reason)s" msgstr "Falha ao aguardar o endereço %(address)s ficar pronto: %(reason)s" msgid "Flat provider networks are disabled" msgstr "Redes de provedor simples são desativadas." msgid "For TCP/UDP protocols, port_range_min must be <= port_range_max" msgstr "Para protocolos TCP/UDP, port_range_min deve ser <= port_range_max" msgid "Force ip_lib calls to use the root helper" msgstr "Força chamadas ip_lib para utilizar o ajudante raiz" #, python-format msgid "Found duplicate extension: %(alias)s." msgstr "Localizada extensão duplicada: %(alias)s." #, python-format msgid "" "Found overlapping allocation pools: %(pool_1)s %(pool_2)s for subnet " "%(subnet_cidr)s." msgstr "" "Conjuntos de alocação de sobreposição localizados:%(pool_1)s %(pool_2)s para " "a sub-rede %(subnet_cidr)s." msgid "Gateway IP version inconsistent with allocation pool version" msgstr "" "Versão de IP do gateway inconsistente com a versão do conjunto de alocações." #, python-format msgid "Gateway ip %(ip_address)s conflicts with allocation pool %(pool)s." msgstr "" "O IP de gateway %(ip_address)s está em conflito com o conjunto de alocações " "%(pool)s." msgid "Gateway is not valid on subnet" msgstr "O gateway não é válido na sub-rede" msgid "" "Geneve encapsulation header size is dynamic, this value is used to calculate " "the maximum MTU for the driver. This is the sum of the sizes of the outer " "ETH + IP + UDP + GENEVE header sizes. The default size for this field is 50, " "which is the size of the Geneve header without any additional option headers." msgstr "" "O tamanho do cabeçalho de encapsulação Geneve é dinâmico, e esse valor é " "usado para calcular o MTU máximo para o driver. Essa é a soma dos tamanhos " "de cabeçalhos ETH + IP + UDP + GENEVE externos. O tamanho padrão para esse " "campo é 50, que é o tamanho do cabeçalho Geneve sem nenhum cabeçalho de " "opção adicional. " msgid "" "Group (gid or name) running metadata proxy after its initialization (if " "empty: agent effective group)." msgstr "" "Grupo (gid ou nome) executando proxy de metadados após sua inicialização (se " "vazio: grupo efetivo do agente)." msgid "Group (gid or name) running this process after its initialization" msgstr "Grupo (gid ou nome) executando esse processo após sua inicialização" msgid "" "Hostname to be used by the Neutron server, agents and services running on " "this machine. All the agents and services running on this machine must use " "the same host value." msgstr "" "O nome do host a ser usado pelo servidor, agentes e serviços do Neutron em " "execução nesta máquina. Todos os agentes e serviços em execução nesta " "máquina devem usar o mesmo valor do host." #, python-format msgid "" "ICMP code (port-range-max) %(value)s is provided but ICMP type (port-range-" "min) is missing." msgstr "" "O código do ICMP (port-range-max) %(value)s é fornecido, mas o tipo do ICMP " "(port-range-min) está ausente." msgid "ID of network" msgstr "ID da rede" msgid "ID of network to probe" msgstr "ID da rede para análise" msgid "ID of probe port to delete" msgstr "ID da porta da análise a ser excluída" msgid "ID of probe port to execute command" msgstr "ID da porta da análise para executar comando" msgid "ID of the router" msgstr "ID do roteador" #, python-format msgid "IP address %(ip)s already allocated in subnet %(subnet_id)s" msgstr "Endereço IP %(ip)s já alocado na sub-rede %(subnet_id)s" #, python-format msgid "IP address %(ip)s does not belong to subnet %(subnet_id)s" msgstr "O endereço IP %(ip)s não pertence à sub-rede %(subnet_id)s" msgid "IP allocation failed. Try again later." msgstr "A alocação de IP falhou. Tente novamente mais tarde" msgid "IP allocation requires subnet_id or ip_address" msgstr "A alocação de IP requer subnet_id ou ip_address" #, python-format msgid "" "IPTablesManager.apply failed to apply the following set of iptables rules:\n" "%s" msgstr "" "IPTablesManager.apply falhou ao aplicar o seguinte conjunto de regras de " "tabelas de IP: \n" "%s" msgid "IPtables conntrack zones exhausted, iptables rules cannot be applied." msgstr "" "Zonas de IPtables conntrack esgotadas; regras de iptables não podem ser " "aplicadas." msgid "IPv6 Address Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "O Modo de endereço IPv6 deve ser SLAAC ou Stateless para delegação de " "prefixo." msgid "IPv6 RA Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "O modo IPv6 RA deve ser SLAAC ou Stateless para delegação de prefixo." #, python-format msgid "" "IPv6 address %(ip)s cannot be directly assigned to a port on subnet " "%(subnet_id)s as the subnet is configured for automatic addresses" msgstr "" "O endereço IPv6 %(ip)s não pode ser designado diretamente a uma porta na sub-" "rede %(subnet_id)s porque a sub-rede está configurada para endereços " "automáticos" #, python-format msgid "" "IPv6 subnet %s configured to receive RAs from an external router cannot be " "added to Neutron Router." msgstr "" "A sub-rede IPv6 %s configurada para receber RAs de um roteador externo não " "pode ser incluída ao Neutron Router." msgid "" "If True, then allow plugins that support it to create VLAN transparent " "networks." msgstr "" "Se True, então permita que plug-ins que suportam-no criem redes " "transparentes da VLAN." msgid "Illegal IP version number" msgstr "Número de versão de IP ilegal" #, python-format msgid "" "Illegal prefix bounds: %(prefix_type)s=%(prefixlen)s, %(base_prefix_type)s=" "%(base_prefixlen)s." msgstr "" "Limites de prefixo ilegal: %(prefix_type)s=%(prefixlen)s, " "%(base_prefix_type)s=%(base_prefixlen)s." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot " "associate with address scope %(address_scope_id)s because subnetpool " "ip_version is not %(ip_version)s." msgstr "" "Associação do conjunto de sub-redes ilegal: O conjunto de sub-redes " "%(subnetpool_id)s não pode ser associado ao escopo de endereço " "%(address_scope_id)s porque a ip_version do conjunto de sub-redes não é " "%(ip_version)s." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot be " "associated with address scope %(address_scope_id)s." msgstr "" "Associação do conjunto de sub-redes ilegal: O conjunto de sub-redes " "%(subnetpool_id)s não pode ser associado ao escopo de endereço " "%(address_scope_id)s." #, python-format msgid "Illegal subnetpool update : %(reason)s." msgstr "Atualização ilegal do conjunto de sub-redes: %(reason)s." #, python-format msgid "Illegal update to prefixes: %(msg)s." msgstr "Atualização ilegal para prefixos: %(msg)s." msgid "" "In some cases the Neutron router is not present to provide the metadata IP " "but the DHCP server can be used to provide this info. Setting this value " "will force the DHCP server to append specific host routes to the DHCP " "request. If this option is set, then the metadata service will be activated " "for all the networks." msgstr "" "Em alguns casos, o roteador Neutron não está presente para fornecer o IP de " "metadados, mas o servidor DHCP poderá ser usado para fornecer essas " "informações. A configuração desse valor forçará o servidor DHCP a anexar " "rotas de host específicas à solicitação DHCP. Se essa opção for configurada, " "o serviço de metadados será ativado para todas as redes." msgid "" "Indicates that this L3 agent should also handle routers that do not have an " "external network gateway configured. This option should be True only for a " "single agent in a Neutron deployment, and may be False for all agents if all " "routers must have an external network gateway." msgstr "" "Indica que esse agente L3 também deve manipular roteadores que não possuírem " "um gateway de rede externo configurado. Essa opção deverá ser True somente " "para um agente único em uma implementação Neutron, e poderá ser False para " "todos os agentes se todos os roteadores tiverem um gateway de rede externo. " #, python-format msgid "Instance of class %(module)s.%(class)s must contain _cache attribute" msgstr "" "A instância da classe %(module)s.%(class)s deve conter o atributo _cache" #, python-format msgid "Insufficient prefix space to allocate subnet size /%s" msgstr "Espaço de prefixo insuficiente para alocar o tamanho da sub-rede /%s" msgid "Insufficient rights for removing default security group." msgstr "Direitos insuficientes para remover o grupo de segurança padrão." msgid "" "Integration bridge to use. Do not change this parameter unless you have a " "good reason to. This is the name of the OVS integration bridge. There is one " "per hypervisor. The integration bridge acts as a virtual 'patch bay'. All VM " "VIFs are attached to this bridge and then 'patched' according to their " "network connectivity." msgstr "" "Ponte de integração a ser utilizada. Não altere esse parâmetro, a menos que " "haja uma boa razão para isso. Esse é o nome da ponte de integração do OVS. " "Há uma por hypervisor. A ponte de integração atua como um 'compartimento de " "correção' virtual. Todos os VIFs da MV são conectados a essa ponte e, em " "seguida, 'corrigidos' de acordo com sua conectividade de rede." msgid "Interface to monitor" msgstr "Interface para monitorar" msgid "" "Interval between checks of child process liveness (seconds), use 0 to disable" msgstr "" "Intervalo entre verificações de um processo-filho em tempo real (segundos), " "use 0 para desativar" msgid "Interval between two metering measures" msgstr "Intervalo entre duas medidas de medição" msgid "Interval between two metering reports" msgstr "Intervalo entre dois relatórios de medição" #, python-format msgid "Invalid CIDR %(input)s given as IP prefix." msgstr "CIDR inválido %(input)s determinado como prefixo do IP." #, python-format msgid "Invalid Device %(dev_name)s: %(reason)s" msgstr "Dispositivo Inválido %(dev_name)s:%(reason)s" #, python-format msgid "" "Invalid action '%(action)s' for object type '%(object_type)s'. Valid " "actions: %(valid_actions)s" msgstr "" "Ação inválida '%(action)s' para o tipo de objeto '%(object_type)s'. Ações " "válidas: %(valid_actions)s" #, python-format msgid "" "Invalid authentication type: %(auth_type)s, valid types are: " "%(valid_auth_types)s" msgstr "" "Tipo de autenticação inválido: %(auth_type)s, os tipos válidos são: " "%(valid_auth_types)s" #, python-format msgid "Invalid ethertype %(ethertype)s for protocol %(protocol)s." msgstr "Ethertype %(ethertype)s inválido para o protocolo %(protocol)s." #, python-format msgid "Invalid format: %s" msgstr "Formato inválido: %s" #, python-format msgid "Invalid instance state: %(state)s, valid states are: %(valid_states)s" msgstr "" "Estado da instância inválido: %(state)s, os estados válidos são: " "%(valid_states)s" #, python-format msgid "Invalid mapping: '%s'" msgstr "Mapeamento inválido: '%s'" #, python-format msgid "Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'." msgstr "Intervalo de VLAN de rede inválido: '%(vlan_range)s' - '%(error)s'." #, python-format msgid "Invalid network VXLAN port range: '%(vxlan_range)s'." msgstr "Intervalo de portas de VXLAN de rede inválido: '%(vxlan_range)s'." #, python-format msgid "Invalid pci slot %(pci_slot)s" msgstr "Slot pci inválido %(pci_slot)s" #, python-format msgid "Invalid provider format. Last part should be 'default' or empty: %s" msgstr "" "Formato de provedor inválido. Última parte deve ser 'default' ou vazia: %s" #, python-format msgid "Invalid resource type %(resource_type)s" msgstr "Tipo de recurso inválido %(resource_type)s" #, python-format msgid "Invalid route: %s" msgstr "Rota inválida: %s" msgid "Invalid service provider format" msgstr "Formato inválido de provedor de serviço" #, python-format msgid "" "Invalid value for ICMP %(field)s (%(attr)s) %(value)s. It must be 0 to 255." msgstr "" "Valor inválido para ICMP %(field)s (%(attr)s) %(value)s. Deve ser de 0 a 255." #, python-format msgid "Invalid value for port %(port)s" msgstr "Valor inválido para a porta %(port)s" msgid "" "Iptables mangle mark used to mark ingress from external network. This mark " "will be masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Marca de tratamento de Iptables usada para marcar ingresso de rede externa. " "Essa marca será mascarada com 0xffff, de modo que apenas os 16 bits " "inferiores serão usados." msgid "" "Iptables mangle mark used to mark metadata valid requests. This mark will be " "masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Marca de tratamento de Iptables usada para marcar solicitações válidas de " "metadados. Essa marca será mascarada com 0xffff, de modo que apenas os 16 " "bits inferiores serão usados." msgid "Keepalived didn't respawn" msgstr "Keepalived não sofreu spawn novamente" msgid "Keepalived didn't spawn" msgstr "Keepalived não sofreu spawn" #, python-format msgid "" "Kernel HZ value %(value)s is not valid. This value must be greater than 0." msgstr "" "Valor do Kernel HZ %(value)s não é válido. Esse valor deve ser maior que 0." msgid "L3 agent failure to setup NAT for floating IPs" msgstr "Falaha do agente L3 ao configurar o NAT para IPs flutuantes" msgid "L3 agent failure to setup floating IPs" msgstr "Falha do agente L3 ao configurar IPs flutuantes" msgid "Limit number of leases to prevent a denial-of-service." msgstr "Limitar o número de concessões para impedir uma negação de serviço." msgid "List of :" msgstr "Lista de :" msgid "" "List of :: or " "specifying physical_network names usable for VLAN provider and tenant " "networks, as well as ranges of VLAN tags on each available for allocation to " "tenant networks." msgstr "" "Lista de :: ou " "especificando nomes physical_network utilizáveis para provedores VLAN e " "redes de tenant, bem como faixas de tags de VLAN em cada um disponível para " "alocação pelas redes de tenant." msgid "" "List of network type driver entrypoints to be loaded from the neutron.ml2." "type_drivers namespace." msgstr "" "Lista de pontos de entrada do driver de tipo de rede que será carregado do " "namespace neutron.ml2.type_drivers namespace." msgid "" "List of physical_network names with which flat networks can be created. Use " "default '*' to allow flat networks with arbitrary physical_network names. " "Use an empty list to disable flat networks." msgstr "" "Lista de physical_network em que redes simples podem ser criadas. Utilize o " "padrão '*' para permitir redes simples com nomes physical_network " "arbitrários. Use uma lista vazia para desativar redes simples." msgid "Location for Metadata Proxy UNIX domain socket." msgstr "Local para soquete de domínio UNIX de Proxy de Metadados." msgid "Location of Metadata Proxy UNIX domain socket" msgstr "Local de soquete de domínio UNIX de Proxy de Metadados" msgid "Location to store DHCP server config files." msgstr "Local para armazenar arquivos de configuração do servidor DHCP" msgid "Location to store IPv6 PD files." msgstr "Local para armazenar arquivos IPv6 PD." msgid "Location to store IPv6 RA config files" msgstr "Local para armazenar arquivos de configuração RA IPv6" msgid "Location to store child pid files" msgstr "Local para armazenar arquivos pid filhos" msgid "Location to store keepalived/conntrackd config files" msgstr "Local para armazenar os arquivos de configuração keepalived/conntrackd" msgid "Log agent heartbeats" msgstr "Registrar pulsações do agente" msgid "" "MTU of the underlying physical network. Neutron uses this value to calculate " "MTU for all virtual network components. For flat and VLAN networks, neutron " "uses this value without modification. For overlay networks such as VXLAN, " "neutron automatically subtracts the overlay protocol overhead from this " "value. Defaults to 1500, the standard value for Ethernet." msgstr "" "O MTU da rede física subjacente. O Neutron usa esse valor para calcular o " "MTU de todos os componentes de rede virtual. Para redes simples e VLAN, o " "Neutron usa esse valor sem modificação. Para redes sobrepostas, como VXLAN, " "o Neutron subtrai automaticamente a sobrecarga de protocolo sobreposta desse " "valor. Padronizado para 1500, o valor padrão para Ethernet." msgid "MTU size of veth interfaces" msgstr "Tamanho MTU de interfaces vEth" msgid "Make the l2 agent run in DVR mode." msgstr "Faça com que o agente l2 seja executado no modo DVR." msgid "Malformed request body" msgstr "Corpo da solicitação malformado" #, python-format msgid "Malformed request body: %(reason)s." msgstr "Corpo da solicitação malformado: %(reason)s." msgid "MaxRtrAdvInterval setting for radvd.conf" msgstr "Configuração de MaxRtrAdvInterval para o radvd.conf" msgid "Maximum number of DNS nameservers per subnet" msgstr "Número máximo de servidores de nomes DNS por sub-rede" msgid "" "Maximum number of L3 agents which a HA router will be scheduled on. If it is " "set to 0 then the router will be scheduled on every agent." msgstr "" "O número máximo de agentes L3 em que um roteador de HA será planejado. Se " "configurado para 0, o roteador será planejado em cada agente." msgid "Maximum number of allowed address pairs" msgstr "Número máximo de pares de endereço permitido" msgid "Maximum number of host routes per subnet" msgstr "Número máximo de rotas do host por sub-rede" msgid "Maximum number of routes per router" msgstr "Número máximo de rotas por roteador" msgid "" "Metadata Proxy UNIX domain socket mode, 4 values allowed: 'deduce': deduce " "mode from metadata_proxy_user/group values, 'user': set metadata proxy " "socket mode to 0o644, to use when metadata_proxy_user is agent effective " "user or root, 'group': set metadata proxy socket mode to 0o664, to use when " "metadata_proxy_group is agent effective group or root, 'all': set metadata " "proxy socket mode to 0o666, to use otherwise." msgstr "" "Modo de soquete de domínio UNIX de proxy de metadados, 4 valores permitidos: " "'deduce': deduzir modo de valores de metadata_proxy_user/group, 'user': " "definir modo de soquete de proxy de metadados para 0o644, para uso quando " "metadata_proxy_user for usuário ou raiz de agente efetivo, 'group': definir " "modo de soquete de proxy de metadados para 0o664, para uso quando " "metadata_proxy_group for grupo ou raiz de agente efetivo, 'all': definir " "modo de soquete de proxy de metadados para 0o666, para uso de outra forma." msgid "Metering driver" msgstr "Driver de medição" msgid "MinRtrAdvInterval setting for radvd.conf" msgstr "Configuração de MinRtrAdvInterval para o radvd.conf" msgid "Minimize polling by monitoring ovsdb for interface changes." msgstr "Minimizar pesquisa monitorando ovsdb para alterações da interface." #, python-format msgid "Missing key in mapping: '%s'" msgstr "Chave ausente no mapeamento: '%s'" msgid "" "Multicast group for VXLAN. When configured, will enable sending all " "broadcast traffic to this multicast group. When left unconfigured, will " "disable multicast VXLAN mode." msgstr "" "O grupo multicast para VXLAN. Quando configurado, permitirá o envio de todo " "o tráfego de transmissão para esse grupo multicast. Quando desconfigurado, " "desativa o modo VXLAN multicast." msgid "" "Multicast group(s) for vxlan interface. A range of group addresses may be " "specified by using CIDR notation. Specifying a range allows different VNIs " "to use different group addresses, reducing or eliminating spurious broadcast " "traffic to the tunnel endpoints. To reserve a unique group for each possible " "(24-bit) VNI, use a /8 such as 239.0.0.0/8. This setting must be the same on " "all the agents." msgstr "" "Um ou mais grupos multicast para a interface VXLAN. Um intervalo de " "endereços de grupo pode ser especificado usando a notação CIDR. Especificar " "um intervalo permite que diferentes VNIs utilizem diferentes endereços de " "grupo, reduzindo ou eliminando tráfego de transmissão falso para os " "terminais do túnel. Para reservar um grupo exclusivo para cada VNI (24 bits) " "possível, use um /8 como 239.0.0.0/8. Essa configuração deve ser a mesma em " "todos os agentes." #, python-format msgid "Multiple default providers for service %s" msgstr "Mútliplos provedores padrão para o serviço %s" #, python-format msgid "Multiple plugins for service %s were configured" msgstr "Vários plug-ins para o serviço %s foram configurados" #, python-format msgid "Multiple providers specified for service %s" msgstr "Mútliplos provedores especificados para o serviço %s" msgid "Multiple tenant_ids in bulk security group rule create not allowed" msgstr "" "Vários tenant_ids na criação da regra do grupo de segurança em massa não " "permitido" msgid "Must also specify protocol if port range is given." msgstr "" "Deve-se também especificar o protocolo se o intervalo de portas for " "fornecido." msgid "Must specify one or more actions on flow addition or modification" msgstr "Deve especificar uma ou mais ações na adição ou modificação do fluxo" msgid "Name of Open vSwitch bridge to use" msgstr "Nome da ponte Open vSwitch a ser usado" msgid "" "Name of nova region to use. Useful if keystone manages more than one region." msgstr "" "Nome da região do nova para utilização. Útil se keystone gerencia mais de " "uma região." msgid "Namespace of the router" msgstr "Namespace do roteador" msgid "Native pagination depend on native sorting" msgstr "A paginação nativa depende da classificação nativa" #, python-format msgid "" "Need to apply migrations from %(project)s contract branch. This will require " "all Neutron server instances to be shutdown before proceeding with the " "upgrade." msgstr "" "É necessário aplicar migrações a partir da ramificação de contrato " "%(project)s. Isso requer que todas as instâncias do servidor Neutron sejam " "encerradas antes de continuar com o upgrade." msgid "Negative delta (downgrade) not supported" msgstr "Delta negativo (downgrade) não suportado" msgid "Negative relative revision (downgrade) not supported" msgstr "Revisão relativa negativa (downgrade) não suportada" #, python-format msgid "Network %s does not contain any IPv4 subnet" msgstr "A rede %s não contém nenhuma sub-rede IPv4" #, python-format msgid "Network %s is not a valid external network" msgstr "A rede %s não é uma rede externa válida" #, python-format msgid "Network %s is not an external network" msgstr "A rede %s não é uma rede externa" #, python-format msgid "" "Network of size %(size)s, from IP range %(parent_range)s excluding IP ranges " "%(excluded_ranges)s was not found." msgstr "" "Rede de tamanho %(size)s, do intervalo de IP %(parent_range)s, excluindo " "intervalos de IP %(excluded_ranges)s não foi localizada." #, python-format msgid "Network type value '%s' not supported" msgstr "Valor do tipo de rede '%s' não suportado" msgid "Network type value needed by the ML2 plugin" msgstr "Valor de tipo de rede necessário pelo plug-in ML2" msgid "Network types supported by the agent (gre and/or vxlan)." msgstr "Tipos de rede suportados pelo agente (gre e/ou vxlan)." msgid "Neutron Service Type Management" msgstr "Gerenciamento do Tipo de Serviço Neuron" msgid "Neutron core_plugin not configured!" msgstr "Neutron core_plugin não configurado!" msgid "No default router:external network" msgstr "Nenhuma rede router:external padrão" #, python-format msgid "No default subnetpool found for IPv%s" msgstr "Nenhum conjunto de sub-redes padrão localizado para IPv%s" msgid "No default subnetpools defined" msgstr "Nenhum conjunto de sub-redes padrão definido" #, python-format msgid "No eligible l3 agent associated with external network %s found" msgstr "Nenhum agente l3 elegível associado com a rede externa %s localizado" #, python-format msgid "No more IP addresses available for subnet %(subnet_id)s." msgstr "Nenhum outro endereço IP disponível para a sub-rede %(subnet_id)s." msgid "No offline migrations pending." msgstr "Nenhuma migração off-line pendente." #, python-format msgid "No shared key in %s fields" msgstr "Nenhuma chave compartilhada nos campos %s" msgid "Not allowed to manually assign a router to an agent in 'dvr' mode." msgstr "" "Não é permitido designar manualmente um roteador para um agente no modo " "'dvr'." msgid "Not allowed to manually remove a router from an agent in 'dvr' mode." msgstr "" "Não é permitido remover manualmente um roteador de um agente no modo 'dvr'." msgid "" "Number of DHCP agents scheduled to host a tenant network. If this number is " "greater than 1, the scheduler automatically assigns multiple DHCP agents for " "a given tenant network, providing high availability for DHCP service." msgstr "" "Número de agentes DHCP planejados para hospedar uma rede de um locatário. Se " "esse número for maior que 1, o planejador designará automaticamente vários " "agentes DHCP para uma determinada rede locatária, fornecendo alta " "disponibilidade para o serviço DHCP." msgid "Number of backlog requests to configure the metadata server socket with" msgstr "" "Número de solicitações de lista não processada para configurar o soquete do " "servidor de metadados com" msgid "Number of backlog requests to configure the socket with" msgstr "Número de requisições de backlog para configurar no socket" msgid "" "Number of bits in an ipv4 PTR zone that will be considered network prefix. " "It has to align to byte boundary. Minimum value is 8. Maximum value is 24. " "As a consequence, range of values is 8, 16 and 24" msgstr "" "Número de bits em uma zona PTR IPV4 que será considerada como um prefixo de " "rede. Ele deve estar alinhado ao limite de byte. O valor mínimo é 8. O valor " "máximo é 24. Consequentemente, um intervalo de valores é 8, 16 e 24." msgid "" "Number of bits in an ipv6 PTR zone that will be considered network prefix. " "It has to align to nyble boundary. Minimum value is 4. Maximum value is 124. " "As a consequence, range of values is 4, 8, 12, 16,..., 124" msgstr "" "Número de bits em uma zona PTR IPV6 que será considerada como um prefixo de " "rede. Ele deve estar alinhado ao limite nyble. O valor mínimo é 4. O valor " "máximo é 124. Consequentemente, um intervalo de valores é 4, 8, 12, " "16, ...., 24." msgid "" "Number of floating IPs allowed per tenant. A negative value means unlimited." msgstr "" "Número de IPs flutuantes permitido por locatário. Um valor negativo " "significa ilimitado." msgid "" "Number of networks allowed per tenant. A negative value means unlimited." msgstr "" "Número de redes permitidas por locatário. Um valor negativo significa " "ilimitado." msgid "Number of ports allowed per tenant. A negative value means unlimited." msgstr "" "Número de portas permitidas por locatário. Um valor negativo significa " "ilimitado." msgid "Number of routers allowed per tenant. A negative value means unlimited." msgstr "" "Número de roteadores permitidos por locatário. Um valor negativo significa " "ilimitado." msgid "" "Number of seconds between sending events to nova if there are any events to " "send." msgstr "" "Número de segundos entre o envio de eventos para nova se houver qualquer " "evento a enviar." msgid "Number of seconds to keep retrying to listen" msgstr "Número de segundos para continuar tentando escutar" msgid "" "Number of security groups allowed per tenant. A negative value means " "unlimited." msgstr "" "Número de grupos de segurança permitidos por locatário. Um valor negativo " "significa ilimitado." msgid "" "Number of security rules allowed per tenant. A negative value means " "unlimited." msgstr "" "Número de regras de segurança permitidas por locatário. Um valor negativo " "significa ilimitado." msgid "" "Number of separate API worker processes for service. If not specified, the " "default is equal to the number of CPUs available for best performance." msgstr "" "Número de processos de API do trabalhador separados para serviço. Se não for " "especificado, o padrão será igual ao número de CPUs disponíveis para melhor " "desempenho." msgid "" "Number of separate worker processes for metadata server (defaults to half of " "the number of CPUs)" msgstr "" "Número de processos do trabalhador separados para o servidor de metadados " "(padrão para metade do número de CPUs)" msgid "Number of subnets allowed per tenant, A negative value means unlimited." msgstr "" "Número de sub-redes permitidas por locatário. Um valor negativo significa " "ilimitado." msgid "" "Number of threads to use during sync process. Should not exceed connection " "pool size configured on server." msgstr "" "Número de threads utilizadas durante o processo de sincronização. Não devem " "exceder o limite do pool de conexões configurado no servidor." msgid "OK" msgstr "OK" msgid "" "OVS datapath to use. 'system' is the default value and corresponds to the " "kernel datapath. To enable the userspace datapath set this value to 'netdev'." msgstr "" "O caminho de dados do OVS a ser utilizado. 'system' é o valor padrão e " "corresponde ao caminho de dados do kernel. Para ativar o caminho de dados do " "espaço do usuário, configure esse valor para 'netdev'." msgid "OVS vhost-user socket directory." msgstr "O diretório de soquete do usuário vhost do OVS." #, python-format msgid "Object action %(action)s failed because: %(reason)s." msgstr "A ação do objeto %(action)s falhou porque: %(reason)s." msgid "Only admin can view or configure quota" msgstr "Somente admin pode visualizar ou configurar cota" msgid "Only admin is authorized to access quotas for another tenant" msgstr "" "Somente o administrador está autorizado a acessar as cotas para outro " "locatário" msgid "Only admins can manipulate policies on objects they do not own" msgstr "" "Apenas administradores podem manipular políticas em objetos que não " "pertencem a eles." msgid "Only allowed to update rules for one security profile at a time" msgstr "Permitido apenas atualizar regras para um perfil de segurança por vez" msgid "Only remote_ip_prefix or remote_group_id may be provided." msgstr "Apenas remote_ip_prefix ou remote_group_id pode ser fornecido." msgid "OpenFlow interface to use." msgstr "Interface OpenFlow a ser usada." #, python-format msgid "" "Operation %(op)s is not supported for device_owner %(device_owner)s on port " "%(port_id)s." msgstr "" "A operação %(op)s não é suportada para device_owner %(device_owner)s na " "porta %(port_id)s." #, python-format msgid "Operation not supported on device %(dev_name)s" msgstr "Operação não suportada no dispositivo %(dev_name)s" msgid "" "Ordered list of network_types to allocate as tenant networks. The default " "value 'local' is useful for single-box testing but provides no connectivity " "between hosts." msgstr "" "Lista ordenada de network_types a serem alocados como redes locatárias. O " "valor padrão 'local' é útil para teste single-box, mas não fornece nenhuma " "conectividade entre os hosts." msgid "Override the default dnsmasq settings with this file." msgstr "Sobrescreva as configurações padrão de dnsmasq com este arquivo." msgid "Owner type of the device: network/compute" msgstr "Tipo de proprietário do dispositivo: rede/cálculo" msgid "POST requests are not supported on this resource." msgstr "Requisições POST não são suportadas neste recurso." #, python-format msgid "Package %s not installed" msgstr "Pacote %s não instalado" #, python-format msgid "Parsing bridge_mappings failed: %s." msgstr "Falha na análise de bridge_mappings: %s." msgid "Password for connecting to designate in admin context" msgstr "Senha para conexão ao Designate no contexto de admnistrador" msgid "Path to PID file for this process" msgstr "Caminho para o arquivo PID para este processo" msgid "Path to the router directory" msgstr "Caminho para o diretório do roteador" msgid "Peer patch port in integration bridge for tunnel bridge." msgstr "" "Porta de correção do peer na ponte de integração para a ponte do túnel." msgid "Peer patch port in tunnel bridge for integration bridge." msgstr "" "Porta da correção do peer na ponte do túnel para a ponte de integração." msgid "Per-tenant subnet pool prefix quota exceeded." msgstr "Cota de prefixo do conjunto de sub-redes por locatário excedida." msgid "Phase upgrade options do not accept revision specification" msgstr "Opções de upgrade de fase não aceitam especificação de revisão" msgid "Ping timeout" msgstr "Tempo Limite de Ping" msgid "Plugin does not support updating provider attributes" msgstr "O plug-in não suporta atualização de atributos do provedor" #, python-format msgid "Port %(id)s does not have fixed ip %(address)s" msgstr "A porta %(id)s não possui IP fixo %(address)s" #, python-format msgid "Port %(port_id)s is already acquired by another DHCP agent" msgstr "A porta %(port_id)s já foi adquirida por outro agente DHCP " #, python-format msgid "" "Port %s has multiple fixed IPv4 addresses. Must provide a specific IPv4 " "address when assigning a floating IP" msgstr "" "A porta %s tem vários endereços IPv4 fixos. Deve-se fornecer um endereço " "IPv4 específico ao designar um IP flutuante" msgid "" "Port to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "Porta para atender conexões OpenFlow. Usada somente para driver 'native'." #, python-format msgid "Prefix '%(prefix)s' not supported in IPv%(version)s pool." msgstr "Prefixo ‘%(prefix)s' não suportado no conjunto do IPv%(version)s." msgid "Prefix Delegation can only be used with IPv6 subnets." msgstr "A delegação de prefixo só pode ser usada com sub-redes IPv6." msgid "Private key of client certificate." msgstr "Chave privada de certificado do cliente." #, python-format msgid "Probe %s deleted" msgstr "Análise %s excluída" #, python-format msgid "Probe created : %s " msgstr "Análise criada: %s " msgid "Process is already started" msgstr "O processo já está iniciado" msgid "Process is not running." msgstr "O processo não está em execução." msgid "Protocol to access nova metadata, http or https" msgstr "Protocolo para acessar os metadados de nova, http ou https" #, python-format msgid "Provider name %(name)s is limited by %(len)s characters" msgstr "O nome do provedor %(name)s é limitado a %(len)s caracteres" #, python-format msgid "QoS Policy %(policy_id)s is used by %(object_type)s %(object_id)s." msgstr "" "A política de QoS %(policy_id)s é usada por %(object_type)s %(object_id)s." #, python-format msgid "" "QoS binding for network %(net_id)s and policy %(policy_id)s could not be " "found." msgstr "" "A ligação do QoS para a rede %(net_id)s e política %(policy_id)s não pôde " "ser localizada." #, python-format msgid "" "QoS binding for port %(port_id)s and policy %(policy_id)s could not be found." msgstr "" "A ligação do QoS para a porta %(port_id)s e política %(policy_id)s não pôde " "ser localizada." #, python-format msgid "QoS policy %(policy_id)s could not be found." msgstr "A política de QoS %(policy_id)s não pôde ser localizada." #, python-format msgid "QoS rule %(rule_id)s for policy %(policy_id)s could not be found." msgstr "" "A regra do QoS %(rule_id)s para a política %(policy_id)s não pôde ser " "localizada." #, python-format msgid "RBAC policy of type %(object_type)s with ID %(id)s not found" msgstr "Política RBAC do tipo %(object_type)s com o ID %(id)s não localizada" #, python-format msgid "" "RBAC policy on object %(object_id)s cannot be removed because other objects " "depend on it.\n" "Details: %(details)s" msgstr "" "A política RBAC no objeto %(object_id)s não pode ser removida porque outros " "objetos dependem dela.\n" "Detalhes: %(details)s" msgid "" "Range of seconds to randomly delay when starting the periodic task scheduler " "to reduce stampeding. (Disable by setting to 0)" msgstr "" "Intervalo de segundos para atrasar aleatoriamente quando iniciar o " "planejador de tarefas periódicas para reduzir registro de data e hora. " "(Desativar configurando como 0)" msgid "Ranges must be in the same IP version" msgstr "Os intervalos devem estar na mesma versão do IP" msgid "Ranges must be netaddr.IPRange" msgstr "Os intervalos devem ser netaddr.IPRange" msgid "Ranges must not overlap" msgstr "Os intervalos não devem se sobrepor" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.EUI type." msgstr "" "Recebidos o tipo '%(type)s' e o valor '%(value)s'. Esperando o tipo netaddr." "EUI. " #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPAddress " "type." msgstr "" "Recebidos o tipo '%(type)s' e o valor '%(value)s'. Esperando o tipo netaddr." "IPAddress." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPNetwork " "type." msgstr "" "Recebidos o tipo '%(type)s' e o valor '%(value)s'. Esperando o tipo netaddr." "IPNetwork." #, python-format msgid "" "Release aware branch labels (%s) are deprecated. Please switch to expand@ " "and contract@ labels." msgstr "" "Rótulos de ramificações cientes da Liberação (%s) foram descontinuados. " "Alterne para os rótulos expand@ e contract@." msgid "Remote metadata server experienced an internal server error." msgstr "" "O servidor de metadados remoto experimentou um erro de servidor interno." msgid "" "Repository does not contain HEAD files for contract and expand branches." msgstr "" "O repositório não contém arquivos HEAD para ramificações de contrato e de " "expansão." msgid "" "Representing the resource type whose load is being reported by the agent. " "This can be \"networks\", \"subnets\" or \"ports\". When specified (Default " "is networks), the server will extract particular load sent as part of its " "agent configuration object from the agent report state, which is the number " "of resources being consumed, at every report_interval.dhcp_load_type can be " "used in combination with network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler When the network_scheduler_driver is " "WeightScheduler, dhcp_load_type can be configured to represent the choice " "for the resource being balanced. Example: dhcp_load_type=networks" msgstr "" "Representando o tipo de recurso cujo carregamento está sendo relatado pelo " "agente. Isso pode ser \"redes\", \"sub-redes\" ou \"portas\". Quando " "especificado (o padrão é redes), o servidor irá extrair carregamento " "particular enviado como parte do seu objeto de configuração do agente do " "relatório de estado do agente, que é o número de recursos sendo consumido, " "em cada report_interval.dhcp_load_type pode ser usado em combinação com " "network_scheduler_driver = neutron.scheduler.dhcp_agent_scheduler." "WeightScheduler Quando o network_scheduler_driver é WeightScheduler, " "dhcp_load_type pode ser configurado para representar a opção para o recurso " "que está sendo balanceado. Exemplo: dhcp_load_type=networks" msgid "Request Failed: internal server error while processing your request." msgstr "" "Falha de solicitação: erro do servidor interno ao processar sua solicitação." msgid "" "Reset flow table on start. Setting this to True will cause brief traffic " "interruption." msgstr "" "Reconfigure a tabela de fluxo ao iniciar. Configurar isso como True causará " "uma breve interrupção do tráfego." #, python-format msgid "Resource %(resource)s %(resource_id)s could not be found." msgstr "O recurso %(resource)s %(resource_id)s não pôde ser localizado." #, python-format msgid "Resource %(resource_id)s of type %(resource_type)s not found" msgstr "Recurso %(resource_id)s do tipo %(resource_type)s não localizado" #, python-format msgid "" "Resource '%(resource_id)s' is already associated with provider " "'%(provider)s' for service type '%(service_type)s'" msgstr "" "Recurso '%(resource_id)s' já está associado com o provedor '%(provider)s' " "para o tipo de serviço '%(service_type)s'" msgid "Resource body required" msgstr "Corpo do recurso necessário" msgid "Resource not found." msgstr "Recurso não encontrado." msgid "Resources required" msgstr "Recursos necessários" msgid "" "Root helper application. Use 'sudo neutron-rootwrap /etc/neutron/rootwrap." "conf' to use the real root filter facility. Change to 'sudo' to skip the " "filtering and just run the command directly." msgstr "" "Aplicação ajudante de Root. Use 'sudo neutron-rootwrap /etc/neutron/rootwrap." "conf' para usar a habilidade the filtragem de root real. Modifique para " "'sudo' para pular a filtragem e apenas executar o comando diretamente." msgid "Root permissions are required to drop privileges." msgstr "As permissões de raiz são necessárias para descartar privilégios." #, python-format msgid "Router '%(router_id)s' is not compatible with this agent." msgstr "O roteador '%(router_id)s‘ não é compatível com este agente." #, python-format msgid "Router already has a port on subnet %s" msgstr "O roteador já possui uma porta na sub-rede %s" msgid "Router port must have at least one fixed IP" msgstr "A porta do Roteador deve ter pelo menos um IP fixo" #, python-format msgid "Running %(cmd)s (%(desc)s) for %(project)s ..." msgstr "Executando %(cmd)s (%(desc)s) para %(project)s..." #, python-format msgid "Running %(cmd)s for %(project)s ..." msgstr "Executando %(cmd)s para %(project)s..." msgid "" "Seconds between nodes reporting state to server; should be less than " "agent_down_time, best if it is half or less than agent_down_time." msgstr "" "Segundos entre os nós que relatam o estado para o servidor; deve ser menor " "que agent_down_time, melhor se for metade ou menos do que agent_down_time." msgid "" "Seconds to regard the agent is down; should be at least twice " "report_interval, to be sure the agent is down for good." msgstr "" "Segundos para considerar que o agente está inativo; deve ser no mínimo duas " "vezes report_interval, para ter certeza de que o agente está inativo." #, python-format msgid "Security Group %(id)s %(reason)s." msgstr "Grupo de segurança %(id)s %(reason)s." #, python-format msgid "Security Group Rule %(id)s %(reason)s." msgstr "Regra de grupo de segurança %(id)s %(reason)s." #, python-format msgid "Security group %(id)s does not exist" msgstr "O grupo de segurança %(id)s não existe" #, python-format msgid "Security group rule %(id)s does not exist" msgstr "A regra do grupo de segurança %(id)s não existe" #, python-format msgid "Security group rule already exists. Rule id is %(rule_id)s." msgstr "A regra do grupo de segurança já existe. ID de regra é %(rule_id)s." #, python-format msgid "" "Security group rule for ethertype '%(ethertype)s' not supported. Allowed " "values are %(values)s." msgstr "" "A regra do grupo de segurança para ethertype '%(ethertype)s' não é " "suportada. Os valores permitidos são %(values)s." #, python-format msgid "" "Security group rule protocol %(protocol)s not supported. Only protocol " "values %(values)s and integer representations [0 to 255] are supported." msgstr "" "Protocolo de regra do grupo de segurança %(protocol)s não suportado. Apenas " "valores valores %(values)s e representações de número inteiro [0 a 255] são " "suportados." msgid "Segments and provider values cannot both be set." msgstr "Valores, de segmento e provedor não podem ser ambos setados." msgid "Selects the Agent Type reported" msgstr "Seleciona o Tipo de agente relatado" msgid "" "Send notification to nova when port data (fixed_ips/floatingip) changes so " "nova can update its cache." msgstr "" "Enviar notificação para nova quando dados da porta (fixed_ips/floatingip) " "muda de modo que nova possa atualizar seu cache." msgid "Send notification to nova when port status changes" msgstr "Enviar notificação para nova quando o status da porta muda" #, python-format msgid "" "Service provider '%(provider)s' could not be found for service type " "%(service_type)s" msgstr "" "Provedor de serviço '%(provider)s' não pôde ser encontrado para o tipo de " "serviço %(service_type)s" msgid "Service to handle DHCPv6 Prefix delegation." msgstr "Serviço para manipular a delegação de Prefixo DHCPv6." #, python-format msgid "Service type %(service_type)s does not have a default service provider" msgstr "" "Tipo de serviço %(service_type)s não possui um provedor de serviço padrão" msgid "" "Set new timeout in seconds for new rpc calls after agent receives SIGTERM. " "If value is set to 0, rpc timeout won't be changed" msgstr "" "Configure novo tempo limite em segundos para novas chamadas rpc depois que o " "agente receber SIGTERM. Se o valor for configurado como 0, o tempo limite de " "rpc não será alterado" msgid "" "Set or un-set the don't fragment (DF) bit on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "Configure ou desconfigure o bit don't fragment (DF) no pacote IP de saída " "que transporta o túnel GRE/VXLAN." msgid "" "Set or un-set the tunnel header checksum on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "Configure ou desconfigure a soma de verificação do cabeçalho de túnel no " "pacote IP de saída que transporta o túnel GRE/VXLAN." msgid "Shared address scope can't be unshared" msgstr "O escopo de endereço compartilhado não pode ser descompartilhado" msgid "String prefix used to match IPset names." msgstr "Prefixo de sequência usado para corresponder nomes de IPset." #, python-format msgid "Sub-project %s not installed." msgstr "O subprojeto %s não está instalado." msgid "Subnet for router interface must have a gateway IP" msgstr "A sub-rede para a interface do roteador deve ter um IP de gateway" #, python-format msgid "Subnet pool %(subnetpool_id)s could not be found." msgstr "O conjunto de sub-rede %(subnetpool_id)s não pôde ser localizado." msgid "Subnet pool has existing allocations" msgstr "O conjunto de sub-rede possui alocações existentes" msgid "Subnet used for the l3 HA admin network." msgstr "Sub-rede usada para a rede administrativa de alta disponibilidade l3." msgid "" "Subnets hosted on the same network must be allocated from the same subnet " "pool." msgstr "" "As sub-redes hospedadas na mesma rede devem ser alocadas a partir do mesmo " "conjunto de sub-redes." msgid "" "System-wide flag to determine the type of router that tenants can create. " "Only admin can override." msgstr "" "Sinalizador do Sistema Inteiro para determinar o tipo de roteador que " "locatários podem criar. Somente administrador pode substituir." msgid "TCP Port used by Neutron metadata namespace proxy." msgstr "Porta TCP usada pelo proxy de namespace de metadados Neutron." msgid "TCP Port used by Nova metadata server." msgstr "Porta TCP usada pelo servidor de metadados Nova." msgid "TTL for vxlan interface protocol packets." msgstr "TTL para pacotes de protocolo da interface vxlan." #, python-format msgid "Tag %(tag)s could not be found." msgstr "A tag %(tag)s não pôde ser localizada." #, python-format msgid "Tenant %(tenant_id)s not allowed to create %(resource)s on this network" msgstr "" "Arrendatário %(tenant_id)s não permitido para criar %(resource)s nesta rede" msgid "Tenant id for connecting to designate in admin context" msgstr "ID de locatário para conexão ao Designate no contexto de admnistrador" msgid "Tenant name for connecting to designate in admin context" msgstr "" "Nome do locatário para conexão ao Designate no contexto de admnistrador" msgid "Tenant network creation is not enabled." msgstr "A criação da rede do arrendatário não está ativada." msgid "Tenant-id was missing from quota request." msgstr "O ID do locatário estava ausente da solicitação de cota." msgid "" "The 'gateway_external_network_id' option must be configured for this agent " "as Neutron has more than one external network." msgstr "" "A opção 'gateway_external_network_id' deve estar configurada para este " "agente pois o Neutron possui mais de uma rede externa." msgid "" "The DHCP agent will resync its state with Neutron to recover from any " "transient notification or RPC errors. The interval is number of seconds " "between attempts." msgstr "" "O agente DHCP ressincronizará seu estado com o Neutron para recuperar-se de " "quaisquer notificações ou erros de RPC temporários. O intervalo é o número " "de segundos entre as tentativas." msgid "" "The DHCP server can assist with providing metadata support on isolated " "networks. Setting this value to True will cause the DHCP server to append " "specific host routes to the DHCP request. The metadata service will only be " "activated when the subnet does not contain any router port. The guest " "instance must be configured to request host routes via DHCP (Option 121). " "This option doesn't have any effect when force_metadata is set to True." msgstr "" "O servidor DHCP pode ajudar a fornecer suporte de metadados em redes " "isoladas. Configurar esse valor para True fará com que o servidor DHCP anexe " "rotas de host específicas à solicitação DHCP. O serviço de metadados será " "ativado somente quando a sub-rede não contiver nenhuma porta do roteador. A " "instância convidada deve ser configurada para solicitar rotas de host por " "meio de DHCP (Opção 121). Essa opção não tem efeito algum quando " "force_metadata estiver configurado para True." msgid "The UDP port to use for VXLAN tunnels." msgstr "A porta UDP utilizada para túneis VXLAN." #, python-format msgid "" "The address allocation request could not be satisfied because: %(reason)s" msgstr "" "A solicitação de alocação de endereço não pôde ser satisfeita porque: " "%(reason)s" msgid "The advertisement interval in seconds" msgstr "O intervalo de propaganda em segundos" #, python-format msgid "The allocation pool %(pool)s is not valid." msgstr "O pool de alocação %(pool)s não é válido." #, python-format msgid "" "The allocation pool %(pool)s spans beyond the subnet cidr %(subnet_cidr)s." msgstr "" "O pool de alocações %(pool)s abrange além da sub-rede CIDR %(subnet_cidr)s." msgid "" "The base MAC address Neutron will use for VIFs. The first 3 octets will " "remain unchanged. If the 4th octet is not 00, it will also be used. The " "others will be randomly generated." msgstr "" "O endereço MAC de base que o Neutron usará para VIFs. Os 3 primeiros octetos " "permanecerão inalterados. Se o 4º octeto não for 00, ele também será " "utilizado, Os outros serão gerados aleatoriamente." msgid "" "The base mac address used for unique DVR instances by Neutron. The first 3 " "octets will remain unchanged. If the 4th octet is not 00, it will also be " "used. The others will be randomly generated. The 'dvr_base_mac' *must* be " "different from 'base_mac' to avoid mixing them up with MAC's allocated for " "tenant ports. A 4 octet example would be dvr_base_mac = fa:16:3f:4f:00:00. " "The default is 3 octet" msgstr "" "O endereço mac base usado para instâncias DVR exclusivas pelo Neutron. Os " "primeiros 3 octetos permanecerão inalterados. Se o quarto octeto não for 00, " "ele também será usado. Os outros serão aleatoriamente gerados. O " "'dvr_base_mac' *deve* ser diferente de 'base_mac' para evitar misturá-los " "com os do MAC alocados para portas locatárias. Um exemplo de 4 octetos seria " "dvr_base_mac = fa:16:3f:4f:00:00. O padrão é 3 octetos" msgid "The core plugin Neutron will use" msgstr "O plug-in principal que o Neutron irá utilizar." msgid "The driver used to manage the DHCP server." msgstr "O driver usado para gerenciar o servidor DHCP." msgid "The driver used to manage the virtual interface." msgstr "Driver usado para gerenciar a interface virtual." msgid "" "The email address to be used when creating PTR zones. If not specified, the " "email address will be admin@" msgstr "" "O endereço de e-mail a ser usado ao criar zonas PTR. Se não especificado, o " "endereço de e-mail será admin@" #, python-format msgid "" "The following device_id %(device_id)s is not owned by your tenant or matches " "another tenants router." msgstr "" "O seguinte device_id %(device_id)s não pertence ao seu locatário ou " "corresponde a outro roteador de locatários." msgid "The interface for interacting with the OVSDB" msgstr "A interface para interação com o OVSDB" msgid "" "The maximum number of items returned in a single response, value was " "'infinite' or negative integer means no limit" msgstr "" "O número máximo de itens retornados em uma única resposta, o valor era " "'infinito' ou um número inteiro negativo significa que não há limite" #, python-format msgid "" "The network %(network_id)s has been already hosted by the DHCP Agent " "%(agent_id)s." msgstr "A rede %(network_id)s já foi hospedada pelo Agente DHCP %(agent_id)s." #, python-format msgid "" "The network %(network_id)s is not hosted by the DHCP agent %(agent_id)s." msgstr "" "A rede %(network_id)s não está hospedada pelo agente DHCP %(agent_id)s." msgid "" "The network type to use when creating the HA network for an HA router. By " "default or if empty, the first 'tenant_network_types' is used. This is " "helpful when the VRRP traffic should use a specific network which is not the " "default one." msgstr "" "O tipo de rede a ser usado ao criar a rede HA para um roteador HA. Por " "padrão ou se em branco, o primeiro 'tenant_network_types' será usado. Isso é " "útil quando o tráfego VRRP deve usar uma rede específica que não é a padrão." msgid "" "The number of seconds the agent will wait between polling for local device " "changes." msgstr "" "O número de segundos que o agente aguardará entre as pesquisas para mudanças " "do dispositivo local." msgid "" "The number of seconds to wait before respawning the ovsdb monitor after " "losing communication with it." msgstr "" "O número de segundos a aguardar antes de reiniciar o monitor ovsdb após " "perder comunicação com ele." msgid "The number of sort_keys and sort_dirs must be same" msgstr "Os números de sort_keys e sort_dirs devem ser os mesmos" msgid "" "The path for API extensions. Note that this can be a colon-separated list of " "paths. For example: api_extensions_path = extensions:/path/to/more/exts:/" "even/more/exts. The __path__ of neutron.extensions is appended to this, so " "if your extensions are in there you don't need to specify them here." msgstr "" "O caminho para extensões da API. Note que isso pode ser uma lista separada " "por vírgula de caminhos. Por exemplo: api_extensions_path = extensions:/path/" "to/more/exts:/even/more/exts. O __path__ of neutron.extensions é anexado a " "isso, de modo que se suas extensões estiverem lá, não será necessário " "especificá-las aqui. " msgid "The physical network name with which the HA network can be created." msgstr "O nome da rede física com o qual a rede HA pode ser criada." #, python-format msgid "The port '%s' was deleted" msgstr "A porta '%s' foi excluída" msgid "The port to bind to" msgstr "A porta para ligar a" #, python-format msgid "The requested content type %s is invalid." msgstr "O tipo de conteúdo requisitado %s é inválido." msgid "The resource could not be found." msgstr "O recurso não pôde ser encontrado." #, python-format msgid "" "The router %(router_id)s has been already hosted by the L3 Agent " "%(agent_id)s." msgstr "O roteador %(router_id)s já foi hospedado pelo Agente L3 %(agent_id)s." msgid "" "The server has either erred or is incapable of performing the requested " "operation." msgstr "" "O servidor possui um erro ou é incapaz de executar a operação solicitada." msgid "The service plugins Neutron will use" msgstr "Os plugins de serviço que o Neutron irá utilizar" #, python-format msgid "The subnet request could not be satisfied because: %(reason)s" msgstr "A solicitação de sub-rede não pôde ser satisfeita porque: %(reason)s" #, python-format msgid "The subproject to execute the command against. Can be one of: '%s'." msgstr "" "O subprojeto com relação ao qual executar o comando. Pode ser um de: '%s'." msgid "The type of authentication to use" msgstr "O tipo de autenticação a ser usado" msgid "" "There are routers attached to this network that depend on this policy for " "access." msgstr "" "Há roteadores conectados a essa rede que dependem dessa política para acesso." msgid "" "Timeout in seconds to wait for a single OpenFlow request. Used only for " "'native' driver." msgstr "" "Tempo limite em segundos a ser aguardado para uma solicitação OpenFlow " "única. Usado somente para driver 'native'." msgid "" "Timeout in seconds to wait for the local switch connecting the controller. " "Used only for 'native' driver." msgstr "" "Tempo limite em segundos de espera para que o comutador local conecte o " "controlador. Usado somente para driver 'native'." msgid "" "Too long prefix provided. New name would exceed given length for an " "interface name." msgstr "" "Prefixo muito longo fornecido. O novo nome excede o comprimento fornecido de " "um nome de instância." msgid "" "True to delete all ports on all the OpenvSwitch bridges. False to delete " "ports created by Neutron on integration and external network bridges." msgstr "" "True para excluir todas as portas em todas as pontes OpenvSwitch. False para " "excluir portas criadas pelo Neutron na integração e pontes de rede externa." msgid "Tunnel IP value needed by the ML2 plugin" msgstr "Valor do IP do túnel necessário pelo plug-in ML2" msgid "Tunnel bridge to use." msgstr "Ponte do túnel a ser utilizada." msgid "" "Type of the nova endpoint to use. This endpoint will be looked up in the " "keystone catalog and should be one of public, internal or admin." msgstr "" "O tipo do terminal Nova a ser utilizado. Esse terminal será bloqueado no " "catálogo de keystone e deverá ser público, interno ou de administração." msgid "URL for connecting to designate" msgstr "URL para conexão ao Designate" msgid "URL to database" msgstr "URL para banco de dados" #, python-format msgid "Unable to access %s" msgstr "Não é possível acessar %s" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, maximum allowed " "prefix is %(max_prefixlen)s." msgstr "" "Não é possível alocar a sub-rede com o comprimento de prefixo %(prefixlen)s, " "o máximo de prefixo permitido é %(max_prefixlen)s." #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, minimum allowed " "prefix is %(min_prefixlen)s." msgstr "" "Não é possível alocar a sub-rede com o comprimento de prefixo %(prefixlen)s, " "o mínimo de prefixo permitido é %(min_prefixlen)s." #, python-format msgid "Unable to calculate %(address_type)s address because of:%(reason)s" msgstr "" "Não é possível calcular o endereço %(address_type)s devido a: %(reason)s" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of DNS " "nameservers exceeds the limit %(quota)s." msgstr "" "Não é possível concluir a operação para %(subnet_id)s. O número de " "servidores de nomes DNS excede o limite %(quota)s." #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of host routes " "exceeds the limit %(quota)s." msgstr "" "Não é possível concluir a operação para %(subnet_id)s. O número de rotas do " "host excede o limite %(quota)s." #, python-format msgid "Unable to convert value in %s" msgstr "Não é possível converter valor em %s" msgid "Unable to create the Agent Gateway Port" msgstr "Não é possível criar a porta do Gateway do Agente" msgid "Unable to create the SNAT Interface Port" msgstr "Não é possível criar a Porta da Interface SNAT" #, python-format msgid "" "Unable to create the flat network. Physical network %(physical_network)s is " "in use." msgstr "" "Não é possível criar a rede simples. A rede física %(physical_network)s está " "em uso." msgid "" "Unable to create the network. No available network found in maximum allowed " "attempts." msgstr "" "Não é possível criar a rede. Nenhuma rede disponível encontrada no máximo de " "tentativas permitidas." #, python-format msgid "Unable to delete subnet pool: %(reason)s." msgstr "Não é possível excluir o conjunto de sub-redes: %(reason)s." #, python-format msgid "Unable to determine mac address for %s" msgstr "Não foi possível determinar o endereço MAC para %s" #, python-format msgid "Unable to find '%s' in request body" msgstr "Não foi possível localizar '%s' no corpo da solicitação" #, python-format msgid "Unable to find IP address %(ip_address)s on subnet %(subnet_id)s" msgstr "" "Não é possível localizar endereço IP %(ip_address)s na sub-rede %(subnet_id)s" #, python-format msgid "Unable to find resource name in %s" msgstr "Não foi possível encontrar nome de recurso em %s" #, python-format msgid "Unable to generate unique mac on network %(net_id)s." msgstr "Não é possível gerar um mac exclusivo na rede %(net_id)s." #, python-format msgid "" "Unable to identify a target field from:%s. Match should be in the form " "%%()s" msgstr "" "Não é possível identificar um campo de destino de: %s. A correspondência " "deve estar no formato %%()s" msgid "Unable to provide external connectivity" msgstr "Não é possível fornecer conectividade externa" msgid "Unable to provide tenant private network" msgstr "Não é possível fornecer rede privada do locatário." #, python-format msgid "" "Unable to reconfigure sharing settings for network %(network)s. Multiple " "tenants are using it." msgstr "" "Não é possível redefinir as configurações de compartilhamento para a rede " "%(network)s. Ela está sendo usada por diversos locatários." #, python-format msgid "" "Unable to verify match:%(match)s as the parent resource: %(res)s was not " "found" msgstr "" "Não foi possível verificar resultados:%(match)s pois o recurso pai: %(res)s " "não foi encontrado" #, python-format msgid "Unexpected label for script %(script_name)s: %(labels)s" msgstr "Rótulo inesperado para o script %(script_name)s: %(labels)s" #, python-format msgid "Unexpected number of alembic branch points: %(branchpoints)s" msgstr "Número inesperado de pontos de ramificação alembic: %(branchpoints)s" #, python-format msgid "Unexpected response code: %s" msgstr "Código de resposta inesperado: %s" #, python-format msgid "Unexpected response: %s" msgstr "Resposta inesperada: %s" #, python-format msgid "Unit name '%(unit)s' is not valid." msgstr "O nome da unidade '%(unit)s' não é válido." #, python-format msgid "Unknown address type %(address_type)s" msgstr "Tipo de endereço desconhecido %(address_type)s" #, python-format msgid "Unknown attribute '%s'." msgstr "Atributo desconhecido '%s'." #, python-format msgid "Unknown chain: %r" msgstr "Cadeia desconhecida: %r" #, python-format msgid "Unknown network type %(network_type)s." msgstr "Tipo de rede desconhecido %(network_type)s." #, python-format msgid "Unknown quota resources %(unknown)s." msgstr "Recursos da cota desconhecidos %(unknown)s." msgid "Unmapped error" msgstr "Erro não mapeado" msgid "Unrecognized action" msgstr "Ação não reconhecida" #, python-format msgid "Unrecognized attribute(s) '%s'" msgstr "Atributo(s) não reconhecido(s) '%s'" msgid "Unrecognized field" msgstr "Campo não reconhecido" msgid "Unsupported Content-Type" msgstr "Tipo de Conteúdo Não Suportado" #, python-format msgid "Unsupported network type %(net_type)s." msgstr "Tipo de rede não suportado %(net_type)s." #, python-format msgid "Unsupported port state: %(port_state)s." msgstr "Estado da porta não suportado: %(port_state)s." msgid "Unsupported request type" msgstr "Tipo de solicitação não suportado" msgid "Updating default security group not allowed." msgstr "Não permitido atualizar o grupo de segurança padrão." msgid "" "Use ML2 l2population mechanism driver to learn remote MAC and IPs and " "improve tunnel scalability." msgstr "" "Utilize o driver de mecanismo de população ML2 l2 para aprender sobre MAC e " "IPs remotos e melhorar a escalabilidade do túnel." msgid "Use broadcast in DHCP replies." msgstr "Usar broadcast em respostas DHCP." msgid "Use either --delta or relative revision, not both" msgstr "Use --delta ou revisão relativa, não ambos" msgid "" "Use ipset to speed-up the iptables based security groups. Enabling ipset " "support requires that ipset is installed on L2 agent node." msgstr "" "Utilize ipset para acelerar os grupos de segurança baseados em iptables. " "Para habilitar o suporte a ipset é necessário que o ipset esteja instalado " "no nó do agente L2." msgid "" "Use the root helper when listing the namespaces on a system. This may not be " "required depending on the security configuration. If the root helper is not " "required, set this to False for a performance improvement." msgstr "" "Use o ajudante de Root quando listar os namespaces no sistema. Isso pode não " "ser necessário dependendo das configurações de segurança. Se o ajudante de " "root não for necessário, configure isto para falso para melhorar a " "performance." msgid "" "Use veths instead of patch ports to interconnect the integration bridge to " "physical networks. Support kernel without Open vSwitch patch port support so " "long as it is set to True." msgstr "" "Use veths em vez de portas de correção para interconectar a ponte de " "integração a redes físicas. Suporta kernel sem o suporte da porta de " "correção Open vSwitch desde que configurado como True." msgid "" "User (uid or name) running metadata proxy after its initialization (if " "empty: agent effective user)." msgstr "" "Usuário (uid ou nome) executando proxy de metadados após sua inicialização " "(se vazio: usuário efetivo do agente)." msgid "User (uid or name) running this process after its initialization" msgstr "Usuário (uid ou nome) executando esse processo após sua inicialização" msgid "Username for connecting to designate in admin context" msgstr "Nome de usuário para conexão ao Designate no contexto de admnistrador" msgid "VRRP authentication password" msgstr "Senha de autenticação do VRRP" msgid "VRRP authentication type" msgstr "Tipo de autenticação do VRRP" msgid "VXLAN network unsupported." msgstr "Rede VXLAN não suportada." msgid "" "Value of host kernel tick rate (hz) for calculating minimum burst value in " "bandwidth limit rules for a port with QoS. See kernel configuration file for " "HZ value and tc-tbf manual for more information." msgstr "" "Valor da taxa tick (hz) do kernel do host para calcular o valor de burst " "mínimo nas regras de limite de largura da banda para uma porta com QoS. " "Consulte o arquivo de configuração do kernel para obter o valor de HZ e o " "manual tc-tbf para obter mais informações." msgid "" "Value of latency (ms) for calculating size of queue for a port with QoS. See " "tc-tbf manual for more information." msgstr "" "Valor de latência (ms) para calcular o tamanho da fila de uma porta com QoS. " "Consulte o manual tc-tbf para obter mais informações." msgid "" "When external_network_bridge is set, each L3 agent can be associated with no " "more than one external network. This value should be set to the UUID of that " "external network. To allow L3 agent support multiple external networks, both " "the external_network_bridge and gateway_external_network_id must be left " "empty." msgstr "" "Quando external_network_bridge é configurado, cada agente L3 apode ser " "associado à, no máximo, uma rede externa. Esse valor deve ser configurado " "para o UUID dessa rede externa. Para permitir que o agente L3 suporte " "diversas redes externas, o external_network_bridge e o " "gateway_external_network_id deverão ser deixados vazios." msgid "" "When proxying metadata requests, Neutron signs the Instance-ID header with a " "shared secret to prevent spoofing. You may select any string for a secret, " "but it must match here and in the configuration used by the Nova Metadata " "Server. NOTE: Nova uses the same config key, but in [neutron] section." msgstr "" "Ao configurar o proxy de solicitações de metadados, o Neutron designa o " "cabeçalho Instance-ID com um segredo compartilhado para evitar spoofing. É " "possível selecionar qualquer sequência de um segredo, mas ela deverá " "corresponder aqui e na configurada usada pelo Nova Metadata Server. NOTA: O " "Nova usa a mesma chave de configuração, mas na seção [neutro]." msgid "" "Where to store Neutron state files. This directory must be writable by the " "agent." msgstr "" "Onde armazenar arquivos de estado Neutron. O agente deve ter permissão de " "escrita neste diretório." msgid "" "With IPv6, the network used for the external gateway does not need to have " "an associated subnet, since the automatically assigned link-local address " "(LLA) can be used. However, an IPv6 gateway address is needed for use as the " "next-hop for the default route. If no IPv6 gateway address is configured " "here, (and only then) the neutron router will be configured to get its " "default route from router advertisements (RAs) from the upstream router; in " "which case the upstream router must also be configured to send these RAs. " "The ipv6_gateway, when configured, should be the LLA of the interface on the " "upstream router. If a next-hop using a global unique address (GUA) is " "desired, it needs to be done via a subnet allocated to the network and not " "through this parameter. " msgstr "" "Com IPv6, a rede usada para o gateway externo não precisa ter um sub-rede " "associada, pois o Link-local Address (LLA) designado automaticamente pode " "ser usado. No entanto, um endereço do gateway IPv6 é necessário para ser " "usado como o próximo hop para a rota padrão. Se nenhum endereço do gateway " "IPv6 for configurado aqui, (somente então) o roteador neutron será " "configurado para obter sua rota padrão de router advertisements (RAs) do " "roteador de envio de dados; em cujo caso o roteador de envio de dados também " "deve ser configurado para enviar esses RAs. O ipv6_gateway, quando " "configurado, deve ser o LLA da interface no roteador de envio de dados. Se " "um próximo hop usando um global unique address (GUA) for desejado, isso " "precisará ser feito por meio de uma sub-rede alocada para a rede e não por " "meio desse parâmetro. " msgid "You must implement __call__" msgstr "Você deve implementar __call__" msgid "" "You must provide a config file for bridge - either --config-file or " "env[NEUTRON_TEST_CONFIG_FILE]" msgstr "" "Você deve fornecer um arquivo de configuração para a ponte - --config-file " "ou env[NEUTRON_TEST_CONFIG_FILE]" msgid "You must provide a revision or relative delta" msgstr "Você deve fornecer uma revisão ou um delta relativo" msgid "a subnetpool must be specified in the absence of a cidr" msgstr "Um conjunto de sub-redes deve ser especificado na ausência de um CIDR" msgid "add_ha_port cannot be called inside of a transaction." msgstr "add_ha_port não pode ser chamado dentro de uma transação." msgid "allocation_pools allowed only for specific subnet requests." msgstr "" "allocation_pools permitido somente para solicitações de sub-rede específicas." msgid "allocation_pools are not in the subnet" msgstr "allocation_pools não estão na sub-rede" msgid "allocation_pools use the wrong ip version" msgstr "allocation_pools usam versão de IP errada" msgid "already a synthetic attribute" msgstr "já é um atributo sintético" msgid "binding:profile value too large" msgstr "ligação: valor de perfil muito grande" #, python-format msgid "cannot perform %(event)s due to %(reason)s" msgstr "Não é possível executar %(event)s devido a %(reason)s" msgid "cidr and prefixlen must not be supplied together" msgstr "cidr e prefixlen não devem ser fornecidos juntos" #, python-format msgid "dhcp_agents_per_network must be >= 1. '%s' is invalid." msgstr "dhcp_agents_per_network deve ser >= 1. '%s' é inválido." msgid "dns_domain cannot be specified without a dns_name" msgstr "dns_domain não pode ser especificado sem um dns_name" msgid "dns_name cannot be specified without a dns_domain" msgstr "dns_name não pode ser especificado sem um dns_domain" msgid "fixed_ip_address cannot be specified without a port_id" msgstr "fixed_ip_address não pode ser especificado sem um port_id" #, python-format msgid "has device owner %s" msgstr "possui o proprietário do dispositivo %s" msgid "in use" msgstr "em uso" #, python-format msgid "ip command failed on device %(dev_name)s: %(reason)s" msgstr "comando ip falhou no dispositivo %(dev_name)s:%(reason)s" #, python-format msgid "ip command failed: %(reason)s" msgstr "O comando ip falhou: %(reason)s" #, python-format msgid "ip link capability %(capability)s is not supported" msgstr "a capacidade %(capability)s de link de IP não é suportada" #, python-format msgid "ip link command is not supported: %(reason)s" msgstr "o comando de link do IP não é suportado: %(reason)s" msgid "ip_version must be specified in the absence of cidr and subnetpool_id" msgstr "" "ip_version deve ser especificado na ausência de cidr e de subnetpool_id" msgid "ipv6_address_mode is not valid when ip_version is 4" msgstr "ipv6_address_mode não é válido quando ip_version for 4" msgid "ipv6_ra_mode is not valid when ip_version is 4" msgstr "ipv6_ra_mode não será válido quando ip_version for 4" #, python-format msgid "" "ipv6_ra_mode set to '%(ra_mode)s' with ipv6_address_mode set to " "'%(addr_mode)s' is not valid. If both attributes are set, they must be the " "same value" msgstr "" "ipv6_ra_mode configurado para '%(ra_mode)s' com ipv6_address_mode " "configurado para '%(addr_mode)s' não é válido. Se ambos os atributos forem " "configurados, eles devem ter o mesmo valor" msgid "mac address update" msgstr "atualização do endereço mac" msgid "must provide exactly 2 arguments - cidr and MAC" msgstr "Deve-se fornece exatamente 2 argumentos - cidr e MAC" msgid "network_type required" msgstr "network_type necessário" #, python-format msgid "network_type value '%s' not supported" msgstr "Valor de network_type '%s' não suportado" msgid "new subnet" msgstr "nova sub-rede" #, python-format msgid "physical_network '%s' unknown for flat provider network" msgstr "physical_network '%s' desconhecida para rede de provedor flat" msgid "physical_network required for flat provider network" msgstr "physical_network requerida para rede de provedor flat" #, python-format msgid "provider:physical_network specified for %s network" msgstr "provider:physical_network especificado para a rede %s" msgid "respawn_interval must be >= 0 if provided." msgstr "respawn_interval deve ser >= 0 se fornecida." #, python-format msgid "segmentation_id out of range (%(min)s through %(max)s)" msgstr "segmentation_id fora da faixa (%(min)s até %(max)s)" msgid "segmentation_id requires physical_network for VLAN provider network" msgstr "segmentation_id requer physical_network para rede de provedor VLAN" msgid "shared attribute switching to synthetic" msgstr "atributo compartilhado alternando para sintético" #, python-format msgid "" "subnetpool %(subnetpool_id)s cannot be updated when associated with shared " "address scope %(address_scope_id)s" msgstr "" "O conjunto de sub-rede %(subnetpool_id)s não pode ser atualizado quando " "associado ao escopo de endereço compartilhado %(address_scope_id)s" msgid "subnetpool_id and use_default_subnetpool cannot both be specified" msgstr "" "O subnetpool_id e o use_default_subnetpool não podem ser especificados. " msgid "the nexthop is not connected with router" msgstr "o nexthop não está conectado com o roteador" msgid "the nexthop is used by router" msgstr "o nexthop é usado pelo roteador" neutron-12.1.1/neutron/locale/ko_KR/0000775000175000017500000000000013553660156017260 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/ko_KR/LC_MESSAGES/0000775000175000017500000000000013553660156021045 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/ko_KR/LC_MESSAGES/neutron.po0000664000175000017500000034611613553660047023111 0ustar zuulzuul00000000000000# Translations template for neutron. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the neutron project. # # Translators: # Seong-ho Cho , 2013 # Seong-ho Cho , 2013 # Sungjin Kang , 2013 # Sungjin Kang , 2013 # Sungjin Kang , 2013 # Andreas Jaeger , 2016. #zanata # HYUNGBAI PARK , 2016. #zanata # jtjang , 2016. #zanata # Heetae Ahn , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: neutron VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-03-14 04:19+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-07-18 04:28+0000\n" "Last-Translator: Heetae Ahn \n" "Language: ko_KR\n" "Plural-Forms: nplurals=1; plural=0;\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 4.3.3\n" "Language-Team: Korean (South Korea)\n" #, python-format msgid "" "\n" "Command: %(cmd)s\n" "Exit code: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" msgstr "" "\n" "명령: %(cmd)s\n" "종료 코드: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" #, python-format msgid "" "%(branch)s HEAD file does not match migration timeline head, expected: " "%(head)s" msgstr "" "%(branch)s HEAD 파ì¼ì´ 마ì´ê·¸ë ˆì´ì…˜ 타임ë¼ì¸ 헤드와 ì¼ì¹˜í•˜ì§€ 않ìŒ, 예ìƒê°’: " "%(head)s" #, python-format msgid "%(id)s is not a valid %(type)s identifier" msgstr "%(id)sì´(ê°€) 올바른 %(type)s IDê°€ 아님" #, python-format msgid "" "%(invalid_dirs)s is invalid value for sort_dirs, valid value is '%(asc)s' " "and '%(desc)s'" msgstr "" "%(invalid_dirs)sì€(는) sort_dirsì— ëŒ€í•´ 올바르지 ì•Šì€ ê°’ì´ë©°, 올바른 ê°’ì€ " "'%(asc)s' ë° '%(desc)s'입니다. " #, python-format msgid "%(key)s prohibited for %(tunnel)s provider network" msgstr "%(tunnel)s ì œê³µìž ë„¤íŠ¸ì›Œí¬ì— 대해 %(key)sì´(ê°€) 금지ë¨" #, python-format msgid "%(name)s '%(addr)s' does not match the ip_version '%(ip_version)s'" msgstr "" "%(name)s '%(addr)s'ì´(ê°€) ip_version '%(ip_version)s'ê³¼(와) ì¼ì¹˜í•˜ì§€ 않ìŒ" #, python-format msgid "%s cannot be called while in offline mode" msgstr "%sì€(는) 오프ë¼ì¸ 모드 중 호출할 수 없습니다. " #, python-format msgid "%s is invalid attribute for sort_keys" msgstr "%sì´(는) sort_keysì— ëŒ€í•´ 올바르지 ì•Šì€ ì†ì„±ìž…니다. " #, python-format msgid "%s is not a valid VLAN tag" msgstr "%sì´(ê°€) 올바른 VLAN 태그가 아님" #, python-format msgid "%s must implement get_port_from_device or get_ports_from_devices." msgstr "" "%sì€(는) get_port_from_device ë˜ëŠ” get_ports_from_devices를 구현해야 합니다." #, python-format msgid "%s prohibited for VLAN provider network" msgstr "VLAN ì œê³µìž ë„¤íŠ¸ì›Œí¬ì— 대해 %sì´(ê°€) 금지ë¨" #, python-format msgid "%s prohibited for flat provider network" msgstr "플랫 ì œê³µìž ë„¤íŠ¸ì›Œí¬ì— 대해 %sì´(ê°€) 금지ë¨" #, python-format msgid "%s prohibited for local provider network" msgstr "로컬 ì œê³µìž ë„¤íŠ¸ì›Œí¬ì— 대해 %sì´(ê°€) 금지ë¨" #, python-format msgid "'%s' is not a valid RBAC object type" msgstr "'%s'ì€(는) 올바른 RBAC 오브ì íЏ ìœ í˜•ì´ ì•„ë‹˜" #, python-format msgid "'%s' is not supported for filtering" msgstr "'%s'ì€(는) í•„í„°ë§ì„ 위해 ì§€ì›ë˜ì§€ 않ìŒ" #, python-format msgid "'module' object has no attribute '%s'" msgstr "'module' 오브ì íŠ¸ì— '%s' ì†ì„±ì´ ì—†ìŒ" msgid "'port_max' is smaller than 'port_min'" msgstr "'port_max'ê°€ 'port_min'보다 ìž‘ìŒ" msgid "0 is not allowed as CIDR prefix length" msgstr "0ì€ CIDR ì ‘ë‘ë¶€ 길ì´ë¡œ 허용ë˜ì§€ 않ìŒ" msgid "A cidr must be specified in the absence of a subnet pool" msgstr "서브넷 í’€ì´ ì—†ëŠ” 경우 cidrì„ ì§€ì •í•´ì•¼ 함" msgid "" "A decimal value as Vendor's Registered Private Enterprise Number as required " "by RFC3315 DUID-EN." msgstr "" "RFC3315 DUID-ENì—서 요구하는 대로 벤ë”ì˜ ë“±ë¡ëœ ê°œì¸ìš© 엔터프ë¼ì´ì¦ˆ 번호로서" "ì˜ 10진수 값입니다. " #, python-format msgid "A default external network already exists: %(net_id)s." msgstr "기본 외부 네트워í¬ê°€ ì´ë¯¸ 있ìŒ: %(net_id)s." msgid "" "A default subnetpool for this IP family has already been set. Only one " "default may exist per IP family" msgstr "" "ì´ IP ì œí’ˆêµ°ì˜ ê¸°ë³¸ subnetpoolì´ ì´ë¯¸ 설정ë˜ì—ˆìŠµë‹ˆë‹¤. IP 제품군당 ê¸°ë³¸ê°’ì€ í•˜" "나만 ìžˆì„ ìˆ˜ 있습니다." msgid "A metering driver must be specified" msgstr "측정 드ë¼ì´ë²„를 지정해야 함" msgid "API for retrieving service providers for Neutron advanced services" msgstr "Neutron 고급 ì„œë¹„ìŠ¤ì— ëŒ€í•œ 서비스 제공ìžë¥¼ 검색하기 위한 API" msgid "Aborting periodic_sync_routers_task due to an error." msgstr "오류로 ì¸í•´ periodic_sync_routers_task를 중단합니다." msgid "Access to this resource was denied." msgstr "ì´ ìžì›ì— 대한 액세스가 ê±°ë¶€ë˜ì—ˆìŠµë‹ˆë‹¤." msgid "Action to be executed when a child process dies" msgstr "하위 프로세스가 ì •ì§€ë  ë•Œ 조치가 실행ë¨" msgid "" "Add comments to iptables rules. Set to false to disallow the addition of " "comments to generated iptables rules that describe each rule's purpose. " "System must support the iptables comments module for addition of comments." msgstr "" "iptables ê·œì¹™ì— ì£¼ì„ì„ ì¶”ê°€í•˜ì‹­ì‹œì˜¤. false로 설정하면 ê° ê·œì¹™ì˜ ìš©ë„를 설명하" "는 ìƒì„±ëœ iptables ê·œì¹™ì— ì£¼ì„ì„ ì¶”ê°€í•  수 없습니다. 시스템ì—서 주ì„ì„ ì¶”ê°€í•˜" "기 위한 iptables ì£¼ì„ ëª¨ë“ˆì„ ì§€ì›í•´ì•¼ 합니다." msgid "Address not present on interface" msgstr "ì¸í„°íŽ˜ì´ìŠ¤ì— ì£¼ì†Œê°€ ì—†ìŒ" msgid "" "Address to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "OpenFlow ì—°ê²°ì„ ìœ„í•´ 청취할 주소입니다. '네ì´í‹°ë¸Œ' 드ë¼ì´ë²„ì—ë§Œ 사용ë©ë‹ˆë‹¤. " msgid "Adds test attributes to core resources." msgstr "코어 ìžì›ì— 테스트 ì†ì„±ì„ 추가합니다." #, python-format msgid "Agent %(id)s is not a L3 Agent or has been disabled" msgstr "%(id)s ì—ì´ì „트가 L3 ì—ì´ì „트가 아니거나 사용 안함 ìƒíƒœìž„" #, python-format msgid "Agent %(id)s is not a valid DHCP Agent or has been disabled" msgstr "%(id)s ì—ì´ì „트가 올바른 DHCP ì—ì´ì „트가 아니거나 사용 안함 ìƒíƒœìž„" msgid "" "Agent starts with admin_state_up=False when enable_new_agents=False. In the " "case, user's resources will not be scheduled automatically to the agent " "until admin changes admin_state_up to True." msgstr "" "enable_new_agents=Falseì¸ ê²½ìš° ì—ì´ì „트는 admin_state_up=False로 시작합니다. " "ì´ ê²½ìš° 사용ìžì˜ ìžì›ì€ 관리ìžê°€ admin_state_upì„ True로 변경할 때까지 ì—ì´ì „" "íŠ¸ì— ëŒ€í•´ ìžë™ìœ¼ë¡œ 스케줄ë˜ì§€ 않습니다. " #, python-format msgid "Agent updated: %(payload)s" msgstr "ì—…ë°ì´íŠ¸ëœ ì—ì´ì „트: %(payload)s" msgid "Allow auto scheduling networks to DHCP agent." msgstr "DHCP ì—ì´ì „íŠ¸ì— ëŒ€í•œ ë„¤íŠ¸ì›Œí¬ ìžë™ 스케줄ë§ì„ 허용합니다. " msgid "Allow auto scheduling of routers to L3 agent." msgstr "L3 ì—ì´ì „íŠ¸ì— ëŒ€í•œ ë¼ìš°í„° ìžë™ 스케줄ë§ì„ 허용합니다." msgid "" "Allow overlapping IP support in Neutron. Attention: the following parameter " "MUST be set to False if Neutron is being used in conjunction with Nova " "security groups." msgstr "" "Neutronì—서 중복 IP ì§€ì›ì„ 허용합니다. 주ì˜: Neutronì„ Nova 보안 그룹과 함께 " "사용하는 경우 ë‹¤ìŒ ë§¤ê°œë³€ìˆ˜ë¥¼ False로 설정해야 합니다." msgid "Allow running metadata proxy." msgstr "메타ë°ì´í„° 프ë¡ì‹œ ì‹¤í–‰ì„ í—ˆìš©í•©ë‹ˆë‹¤." msgid "Allow sending resource operation notification to DHCP agent" msgstr "DHCP ì—ì´ì „íŠ¸ì— ìžì› ì¡°ìž‘ 알림 전송 허용" msgid "Allow the creation of PTR records" msgstr "PTR ë ˆì½”ë“œì˜ ìž‘ì„± 허용" msgid "Allow the usage of the bulk API" msgstr "ë²Œí¬ API 사용 허용" msgid "Allow to perform insecure SSL (https) requests to nova metadata" msgstr "nova 메타ë°ì´í„°ì— 대한 비보안 SSL(https) 요청 수행 허용" msgid "" "Allows for serving metadata requests coming from a dedicated metadata access " "network whose CIDR is 169.254.169.254/16 (or larger prefix), and is " "connected to a Neutron router from which the VMs send metadata:1 request. In " "this case DHCP Option 121 will not be injected in VMs, as they will be able " "to reach 169.254.169.254 through a router. This option requires " "enable_isolated_metadata = True." msgstr "" "CIDRì´ 169.254.169.254/16(ë˜ëŠ” ë” í° ì ‘ë‘사)ì´ë©° VMì—서 metadata:1 ìš”ì²­ì„ ë³´" "내는 Neutron ë¼ìš°í„°ì— ì—°ê²°ëœ ì „ìš© 메타ë°ì´í„° 액세스 네트워í¬ì—서 전송ë˜ëŠ” ë©”" "타ë°ì´í„° ìš”ì²­ì— ëŒ€í•´ 서비스를 제공할 수 있습니다. ì´ ê²½ìš° ë¼ìš°í„°ë¥¼ 통해 " "169.254.169.254ì— ì—°ê²°í•  수 있으므로 DHCP 옵션 121ì€ VMì— ì‚½ìž…ë˜ì§€ 않습니다. " "ì´ ì˜µì…˜ì—는 enable_isolated_metadata = Trueê°€ 필요합니다." msgid "An RBAC policy already exists with those values." msgstr "해당 ê°’ì˜ RBAC ì •ì±…ì´ ì´ë¯¸ 있습니다." msgid "An identifier must be specified when updating a subnet" msgstr "ì„œë¸Œë„·ì„ ì—…ë°ì´íŠ¸í•  때 ID를 지정해야 함" msgid "An interface driver must be specified" msgstr "ì¸í„°íŽ˜ì´ìФ 드ë¼ì´ë²„ê°€ 지정ë˜ì–´ì•¼ 함" msgid "" "An ordered list of extension driver entrypoints to be loaded from the " "neutron.ml2.extension_drivers namespace. For example: extension_drivers = " "port_security,qos" msgstr "" "neutron.ml2.mechanism_drivers 네임스페ì´ìŠ¤ë¡œë¶€í„° 로드할 확장 드ë¼ì´ë²„ 엔드í¬" "ì¸íŠ¸ì˜ ì •ë ¬ëœ ëª©ë¡ìž…니다. 예: extension_drivers = port_security,qos" msgid "" "An ordered list of networking mechanism driver entrypoints to be loaded from " "the neutron.ml2.mechanism_drivers namespace." msgstr "" "neutron.ml2.mechanism_drivers 네임스페ì´ìŠ¤ë¡œë¶€í„° 로드할 네트워킹 메커니즘 드" "ë¼ì´ë²„ 시작ì ì˜ ì •ë ¬ëœ ëª©ë¡ìž…니다." msgid "An unknown error has occurred. Please try your request again." msgstr "알 수 없는 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. ìš”ì²­ì„ ë‹¤ì‹œ 시ë„하십시오. " msgid "Async process didn't respawn" msgstr "비ë™ê¸° 프로세스가 다시 파ìƒë˜ì§€ 않ìŒ" msgid "Authorization URL for connecting to designate in admin context" msgstr "관리 컨í…스트ì—서 지정하기 위해 ì—°ê²°í•  ì¸ì¦ URL" msgid "Automatically remove networks from offline DHCP agents." msgstr "오프ë¼ì¸ DHCP ì—ì´ì „트ì—서 네트워í¬ë¥¼ ìžë™ìœ¼ë¡œ 제거합니다." msgid "" "Automatically reschedule routers from offline L3 agents to online L3 agents." msgstr "" "오프ë¼ì¸ L3 ì—ì´ì „트부터 온ë¼ì¸ L3 ì—ì´ì „트까지 ë¼ìš°íŠ¸ë¥¼ ìžë™ìœ¼ë¡œ 다시 스케줄" "합니다." msgid "Availability zone of this node" msgstr "ì´ ë…¸ë“œì˜ ê°€ìš© 구역" msgid "Available commands" msgstr "사용 가능한 명령" msgid "Backend does not support VLAN Transparency." msgstr "백엔드는 VLAN 투명ë„를 ì§€ì›í•˜ì§€ 않습니다." #, python-format msgid "Base MAC: %s" msgstr "기본 MAC: %s" msgid "" "Base log dir for dnsmasq logging. The log contains DHCP and DNS log " "information and is useful for debugging issues with either DHCP or DNS. If " "this section is null, disable dnsmasq log." msgstr "" "dnsmasq ë¡œê¹…ì„ ìœ„í•œ 기본 로그 디렉토리입니다. ì´ ë¡œê·¸ëŠ” DHCP ë° DNS 로그 ì •ë³´" "를 í¬í•¨í•˜ê³  있으며 DHCP ë˜ëŠ” DNSì— ëŒ€í•œ 문제를 디버깅하는 ë° ìœ ìš©í•©ë‹ˆë‹¤. ì´ " "ì„¹ì…˜ì´ ë„ì¸ ê²½ìš°ì—는 dnsmasq 로그를 사용 안함으로 설정하십시오. " msgid "Body contains invalid data" msgstr "ë³¸ë¬¸ì— ì˜¬ë°”ë¥´ì§€ ì•Šì€ ë°ì´í„°ê°€ í¬í•¨ë˜ì–´ 있ìŒ" msgid "Both network_id and router_id are None. One must be provided." msgstr "network_id ë° router_idê°€ ëª¨ë‘ None입니다. 하나가 제공ë˜ì–´ì•¼ 합니다. " #, python-format msgid "Bridge %(bridge)s does not exist." msgstr "%(bridge)s 브릿지가 존재하지 않습니다. " msgid "Bulk operation not supported" msgstr "ë²Œí¬ ì˜¤í¼ë ˆì´ì…˜ì€ ì§€ì›ë˜ì§€ 않ìŒ" msgid "CIDR to monitor" msgstr "모니터할 CIDR" #, python-format msgid "Callback for %(resource_type)s not found" msgstr "%(resource_type)sì— ëŒ€í•œ ì½œë°±ì„ ì°¾ì„ ìˆ˜ ì—†ìŒ" #, python-format msgid "Callback for %(resource_type)s returned wrong resource type" msgstr "%(resource_type)sì— ëŒ€í•œ 콜백ì—서 ìž˜ëª»ëœ ìžì› ìœ í˜•ì„ ë¦¬í„´í•¨" #, python-format msgid "Cannot add floating IP to port %s that has no fixed IPv4 addresses" msgstr "ê³ ì • IPv4 주소가 없는 í¬íЏ %sì— ë¶€ë™ IP를 추가할 수 ì—†ìŒ" #, python-format msgid "Cannot add multiple callbacks for %(resource_type)s" msgstr "%(resource_type)sì— ëŒ€í•œ 다중 ì½œë°±ì„ ì¶”ê°€í•  수 ì—†ìŒ" #, python-format msgid "Cannot allocate IPv%(req_ver)s subnet from IPv%(pool_ver)s subnet pool" msgstr "IPv%(pool_ver)s 서브넷 í’€ì—서 IPv%(req_ver)s ì„œë¸Œë„·ì„ í• ë‹¹í•  수 ì—†ìŒ" msgid "Cannot allocate requested subnet from the available set of prefixes" msgstr "사용 가능한 ì ‘ë‘ë¶€ 세트ì—서 요청한 ì„œë¸Œë„·ì„ í• ë‹¹í•  수 ì—†ìŒ" msgid "Cannot disable enable_dhcp with ipv6 attributes set" msgstr "ipv6 ì†ì„±ì´ ì„¤ì •ëœ enable_dhcp를 사용할 수 ì—†ìŒ" #, python-format msgid "Cannot handle subnet of type %(subnet_type)s" msgstr "%(subnet_type)s ìœ í˜•ì˜ ì„œë¸Œë„·ì„ ì²˜ë¦¬í•  수 ì—†ìŒ" msgid "Cannot have multiple IPv4 subnets on router port" msgstr "ë¼ìš°í„° í¬íŠ¸ì— IPv4 ì„œë¸Œë„·ì´ ì—¬ëŸ¬ ê°œì¼ ìˆ˜ ì—†ìŒ" #, python-format msgid "" "Cannot have multiple router ports with the same network id if both contain " "IPv6 subnets. Existing port %(p)s has IPv6 subnet(s) and network id %(nid)s" msgstr "" "ëª¨ë‘ IPv6 ì„œë¸Œë„·ì´ ìžˆëŠ” 경우 ê°™ì€ ë„¤íŠ¸ì›Œí¬ ID를 사용하는 ë¼ìš°í„° í¬íŠ¸ë¥¼ 여러 " "ê°œ 사용할 수 ì—†ìŒ. 기존 í¬íЏ %(p)sì— IPv6 서브넷 ë° ë„¤íŠ¸ì›Œí¬ ID %(nid)sì´" "(ê°€) 있ìŒ." #, python-format msgid "" "Cannot host distributed router %(router_id)s on legacy L3 agent %(agent_id)s." msgstr "" "레거시 L3 ì—ì´ì „트 %(agent_id)sì—서 ë¶„ì‚°ëœ ë¼ìš°í„° %(router_id)sì„(를) 호스팅" "í•  수 업습니다." msgid "Cannot mix IPv4 and IPv6 prefixes in a subnet pool." msgstr "서브넷 í’€ì—서 IPv4 ë° IPv6 ì ‘ë‘부를 혼합하여 사용할 수 없습니다." msgid "Cannot specify both subnet-id and port-id" msgstr "subnet-id와 port-id를 둘 다 지정할 수 ì—†ìŒ" msgid "Cannot understand JSON" msgstr "JSONì„ ì´í•´í•  수 ì—†ìŒ" #, python-format msgid "Cannot update read-only attribute %s" msgstr "ì½ê¸° ì „ìš© ì†ì„± %sì„(를) ì—…ë°ì´íŠ¸í•  수 ì—†ìŒ" msgid "Certificate Authority public key (CA cert) file for ssl" msgstr "sslìš© ì¸ì¦ 기관 공개 키(CA cert) íŒŒì¼ " #, python-format msgid "" "Change would make usage less than 0 for the following resources: %(unders)s." msgstr "변경하면 ë‹¤ìŒ ìžì›ì— 대한 ì‚¬ìš©ëŸ‰ì´ 0보다 작아ì§: %(unders)s." msgid "Check ebtables installation" msgstr "ebtables 설치 확ì¸" msgid "Check for ARP header match support" msgstr "ARP í—¤ë” ì¼ì¹˜ ì§€ì› í™•ì¸" msgid "Check for ARP responder support" msgstr "ARP ì‘답기 ì§€ì› í™•ì¸" msgid "Check for ICMPv6 header match support" msgstr "ICMPv6 í—¤ë” ì¼ì¹˜ ì§€ì› í™•ì¸" msgid "Check for OVS Geneve support" msgstr "OVS Geneve ì§€ì› í™•ì¸" msgid "Check for OVS vxlan support" msgstr "OVS vxlan ì§€ì› í™•ì¸" msgid "Check for VF management support" msgstr "VF 관리 ì§€ì› í™•ì¸" msgid "Check for iproute2 vxlan support" msgstr "iproute2 vxlan ì§€ì› í™•ì¸" msgid "Check for nova notification support" msgstr "nova 알림 ì§€ì› í™•ì¸" msgid "Check for patch port support" msgstr "패치 í¬íЏ ì§€ì› í™•ì¸" msgid "Check ip6tables installation" msgstr "ip6tables 설치 확ì¸" msgid "Check ipset installation" msgstr "ipset 설치 확ì¸" msgid "Check keepalived IPv6 support" msgstr "keepalived IPv6 ì§€ì› í™•ì¸" msgid "Check minimal dibbler version" msgstr "최소 dibbler 버전 확ì¸" msgid "Check minimal dnsmasq version" msgstr "최소 dnsmasq 버전 확ì¸" msgid "Check netns permission settings" msgstr "netns 권한 설정 확ì¸" msgid "Check ovs conntrack support" msgstr "ovs conntrack ì§€ì› í™•ì¸" msgid "Check ovsdb native interface support" msgstr "ovsdb 네ì´í‹°ë¸Œ ì¸í„°íŽ˜ì´ìФ ì§€ì› í™•ì¸" #, python-format msgid "" "Cidr %(subnet_cidr)s of subnet %(subnet_id)s overlaps with cidr %(cidr)s of " "subnet %(sub_id)s" msgstr "" "서브넷 %(subnet_id)sì˜ cidr %(subnet_cidr)sì´(ê°€) 서브넷 %(sub_id)sì˜ cidr " "%(cidr)sê³¼(와) 겹침" msgid "Cleanup resources of a specific agent type only." msgstr "특정 ì—ì´ì „트 ìœ í˜•ì˜ ìžì›ë§Œ 정리합니다." msgid "Client certificate for nova metadata api server." msgstr "nova 메타ë°ì´í„° api ì„œë²„ì— ëŒ€í•œ í´ë¼ì´ì–¸íЏ ì¸ì¦ì„œìž…니다." msgid "" "Comma-separated list of : tuples, mapping " "network_device to the agent's node-specific list of virtual functions that " "should not be used for virtual networking. vfs_to_exclude is a semicolon-" "separated list of virtual functions to exclude from network_device. The " "network_device in the mapping should appear in the physical_device_mappings " "list." msgstr "" "ê°€ìƒ ë„¤íŠ¸ì›Œí‚¹ì— ì‚¬ìš©í•´ì„œëŠ” 안ë˜ëŠ” ê°€ìƒ í•¨ìˆ˜ì˜ ì—ì´ì „트 노드별 목ë¡ì— " "network_device를 맵핑하는 쉼표로 êµ¬ë¶„ëœ : 튜" "플 목ë¡ìž…니다. vfs_to_exclude는 network_deviceì—서 제외시킬 세미콜론으로 구분" "ëœ ê°€ìƒ í•¨ìˆ˜ 목ë¡ìž…니다. ë§µí•‘ì— ì‚¬ìš©ëœ network_device는 " "physical_device_mappings 목ë¡ì— 표시ë˜ì–´ì•¼ 합니다." msgid "" "Comma-separated list of : tuples mapping " "physical network names to the agent's node-specific physical network device " "interfaces of SR-IOV physical function to be used for VLAN networks. All " "physical networks listed in network_vlan_ranges on the server should have " "mappings to appropriate interfaces on each agent." msgstr "" "VLAN 네트워í¬ì— 사용할 SR-IOV 실제 ê¸°ëŠ¥ì˜ ì—ì´ì „트 노드별 실제 ë„¤íŠ¸ì›Œí¬ ë””ë°”" "ì´ìФ ì¸í„°íŽ˜ì´ìŠ¤ì— ì‹¤ì œ ë„¤íŠ¸ì›Œí¬ ì´ë¦„ì„ ë§µí•‘í•˜ëŠ” 쉼표로 êµ¬ë¶„ëœ " ": 튜플 목ë¡ìž…니다. ì„œë²„ì˜ " "network_vlan_rangesì— ë‚˜ì—´ëœ ëª¨ë“  실제 네트워í¬ëŠ” ê° ì—ì´ì „íŠ¸ì˜ í•´ë‹¹ ì¸í„°íŽ˜ì´" "ìŠ¤ì— ëŒ€í•œ ë§µí•‘ì´ ìžˆì–´ì•¼ 합니다." msgid "" "Comma-separated list of : tuples " "mapping physical network names to the agent's node-specific physical network " "interfaces to be used for flat and VLAN networks. All physical networks " "listed in network_vlan_ranges on the server should have mappings to " "appropriate interfaces on each agent." msgstr "" "플랫 ë° VLAN 네트워í¬ì— 사용할 ì—ì´ì „트 노드별 실제 ë„¤íŠ¸ì›Œí¬ ì¸í„°íŽ˜ì´ìŠ¤ì— ì‹¤" "ì œ ë„¤íŠ¸ì›Œí¬ ì´ë¦„ì„ ë§µí•‘í•˜ëŠ” 쉼표로 êµ¬ë¶„ëœ :" " 튜플 목ë¡ìž…니다. ì„œë²„ì˜ network_vlan_rangesì— ë‚˜ì—´ëœ ëª¨" "ë“  실제 네트워í¬ëŠ” ê° ì—ì´ì „íŠ¸ì˜ í•´ë‹¹ ì¸í„°íŽ˜ì´ìŠ¤ì— ëŒ€í•œ ë§µí•‘ì´ ìžˆì–´ì•¼ 합니다." msgid "" "Comma-separated list of : tuples enumerating ranges of GRE " "tunnel IDs that are available for tenant network allocation" msgstr "" "테넌트 ë„¤íŠ¸ì›Œí¬ í• ë‹¹ì— ì‚¬ìš© 가능한 GRE í„°ë„ IDì˜ ë²”ìœ„ë¥¼ 열거한 :" " íŠœí”Œì„ ì‰¼í‘œë¡œ 구분한 목ë¡ìž…니다." msgid "" "Comma-separated list of : tuples enumerating ranges of " "Geneve VNI IDs that are available for tenant network allocation" msgstr "" "테넌트 ë„¤íŠ¸ì›Œí¬ í• ë‹¹ì— ì‚¬ìš© 가능한 Geneve VNI IDì˜ ë²”ìœ„ë¥¼ 열거하는 :" " íŠœí”Œì˜ ì‰¼í‘œë¡œ êµ¬ë¶„ëœ ëª©ë¡ìž…니다. " msgid "" "Comma-separated list of : tuples enumerating ranges of " "VXLAN VNI IDs that are available for tenant network allocation" msgstr "" "테넌트 ë„¤íŠ¸ì›Œí¬ í• ë‹¹ì— ì‚¬ìš© 가능한 VXLAN VNI IDì˜ ë²”ìœ„ë¥¼ 열거한 :" " íŠœí”Œì˜ ì‰¼í‘œë¡œ êµ¬ë¶„ëœ ëª©ë¡ìž…니다. " msgid "" "Comma-separated list of the DNS servers which will be used as forwarders." msgstr "쉼표로 ë¶„ë¦¬ëœ DNS ì„œë²„ì˜ ëª©ë¡ì´ë©° 전달ìžë¡œ 사용ë©ë‹ˆë‹¤." msgid "Command to execute" msgstr "실행할 명령" msgid "Config file for interface driver (You may also use l3_agent.ini)" msgstr "ì¸í„°íŽ˜ì´ìФ 드ë¼ì´ë²„ì— ëŒ€í•œ 구성 파ì¼(l3_agent.inië„ ì‚¬ìš©í•  수 있ìŒ)" #, python-format msgid "Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s" msgstr "CIDR %(cidr)sì— ëŒ€í•œ ì¶©ëŒí•˜ëŠ” ê°’ ethertype %(ethertype)s" msgid "" "Controls whether the neutron security group API is enabled in the server. It " "should be false when using no security groups or using the nova security " "group API." msgstr "" "서버ì—서 neutron 보안 그룹 APIê°€ 사용ë˜ëŠ”ì§€ 여부를 제어합니다.보안 ê·¸ë£¹ì„ ì‚¬" "용하지 않거나 nova 보안 그룹 API를 사용할 때는 falseì´ì–´ì•¼ 합니다." #, python-format msgid "Could not bind to %(host)s:%(port)s after trying for %(time)d seconds" msgstr "%(time)d후 시ë„한 ë‹¤ìŒ %(host)s:%(port)sì— ë°”ì¸ë”©í•  수 없습니다" msgid "Could not deserialize data" msgstr "ë°ì´í„°ë¥¼ ì§ë ¬í™” 해제할 수 ì—†ìŒ" #, python-format msgid "" "Current gateway ip %(ip_address)s already in use by port %(port_id)s. Unable " "to update." msgstr "" "현재 게ì´íŠ¸ì›¨ì´ ip %(ip_address)sì„(를) í¬íЏ %(port_id)sì—서 ì´ë¯¸ 사용하고 있" "습니다.ì—…ë°ì´íŠ¸í•  수 없습니다." msgid "" "DHCP lease duration (in seconds). Use -1 to tell dnsmasq to use infinite " "lease times." msgstr "" "DHCP 리스 기간(ì´ˆ)입니다. dnsmasqì— ë¬´í•œ 리스 ì‹œê°„ì„ ì‚¬ìš©í•˜ë„ë¡ ì§€ì‹œí•˜ë ¤ë©´ -1" "ì„ ì‚¬ìš©í•˜ì‹­ì‹œì˜¤." msgid "" "DVR deployments for VXLAN/GRE/Geneve underlays require L2-pop to be enabled, " "in both the Agent and Server side." msgstr "" "VXLAN/GRE/Geneve 기초를 위한 DVR 배치를 수행하려면 ì—ì´ì „트 측과 서버 측 모ë‘" "ì—서 L2-popì„ ì‚¬ìš©ìœ¼ë¡œ 설정해야 합니다. " msgid "" "Database engine for which script will be generated when using offline " "migration." msgstr "" "오프ë¼ì¸ 마ì´ê·¸ë ˆì´ì…˜ì„ 사용할 때 스í¬ë¦½íŠ¸ê°€ ìƒì„±ë  ë°ì´í„°ë² ì´ìФ 엔진입니다." msgid "Default external networks must be shared to everyone." msgstr "기본 외부 네트워í¬ë¥¼ 모든 사용ìžì™€ 공유해야 합니다." msgid "" "Default network type for external networks when no provider attributes are " "specified. By default it is None, which means that if provider attributes " "are not specified while creating external networks then they will have the " "same type as tenant networks. Allowed values for external_network_type " "config option depend on the network type values configured in type_drivers " "config option." msgstr "" "ì œê³µìž ì†ì„±ì´ 지정ë˜ì§€ ì•Šì€ ê²½ìš° 외부 네트워í¬ì˜ 기본 ë„¤íŠ¸ì›Œí¬ ìœ í˜•ìž…ë‹ˆë‹¤. 기" "본ì ìœ¼ë¡œ ì´ ìœ í˜•ì€ Noneì´ë©° ì´ëŠ” 외부 네트워í¬ë¥¼ 작성하는 ì¤‘ì— ì œê³µìž ì†ì„±ì´ " "지정ë˜ì§€ ì•Šì€ ê²½ìš° 해당 ì œê³µìž ì†ì„±ì€ 테넌트 네트워í¬ì™€ ë™ì¼í•œ ìœ í˜•ì„ ê°€ì§„ë‹¤" "는 ê²ƒì„ ì˜ë¯¸í•©ë‹ˆë‹¤. external_network_type 구성 ì˜µì…˜ì— ëŒ€í•´ 허용ë˜ëŠ” ê°’ì€ " "type_drivers 구성 옵션ì—서 êµ¬ì„±ëœ ë„¤íŠ¸ì›Œí¬ ìœ í˜• ê°’ì— ë”°ë¼ ë‹¤ë¦…ë‹ˆë‹¤. " msgid "" "Default number of RBAC entries allowed per tenant. A negative value means " "unlimited." msgstr "" "테넌트당 허용ë˜ëŠ” 기본 RBAC 항목 수입니다. ìŒìˆ˜ ê°’ì€ ë¬´ì œí•œì„ ì˜ë¯¸í•©ë‹ˆë‹¤. " msgid "" "Default number of resource allowed per tenant. A negative value means " "unlimited." msgstr "테넌트당 허용ë˜ëŠ” 기본 ìžì› 수입니다. ìŒìˆ˜ ê°’ì€ ë¬´ì œí•œì„ ì˜ë¯¸í•©ë‹ˆë‹¤." msgid "Default security group" msgstr "기본 보안 그룹" msgid "Default security group already exists." msgstr "기본 보안 ê·¸ë£¹ì´ ì´ë¯¸ 존재합니다. " msgid "" "Default value of availability zone hints. The availability zone aware " "schedulers use this when the resources availability_zone_hints is empty. " "Multiple availability zones can be specified by a comma separated string. " "This value can be empty. In this case, even if availability_zone_hints for a " "resource is empty, availability zone is considered for high availability " "while scheduling the resource." msgstr "" "가용 구역 ížŒíŠ¸ì˜ ê¸°ë³¸ê°’ìž…ë‹ˆë‹¤. 리소스 availability_zone_hintsê°€ 비어 있으면 " "가용 구역 ì¸ì‹ 스케줄러ì—서 ì´ ê°’ì„ ì‚¬ìš©í•©ë‹ˆë‹¤. 쉼표로 êµ¬ë¶„ëœ ë¬¸ìžì—´ì„ 사용하" "ì—¬ 여러 가용 êµ¬ì—­ì„ ì§€ì •í•  수 있습니다. ì´ ê°’ì€ ë¹„ì–´ìžˆì„ ìˆ˜ 있습니다. ì´ ê²½" "ìš° ìžì›ì˜ availability_zone_hintsê°€ 비어 ìžˆì–´ë„ ìžì›ì„ 스케줄ë§í•˜ëŠ” ë™ì•ˆ ê³ ê°€" "ìš©ì„±ì„ ìœ„í•´ 가용 êµ¬ì—­ì„ ê³ ë ¤í•©ë‹ˆë‹¤." msgid "" "Define the default value of enable_snat if not provided in " "external_gateway_info." msgstr "" "external_gateway_infoì— ì œê³µë˜ì§€ ì•Šì€ ê²½ìš° enable_snatì˜ ê¸°ë³¸ê°’ì„ ì •ì˜í•˜ì‹­ì‹œ" "오. " msgid "" "Defines providers for advanced services using the format: :" ":[:default]" msgstr "" "ë‹¤ìŒ í˜•ì‹ì„ 사용하여 고급 ì„œë¹„ìŠ¤ì— ëŒ€í•œ ì œê³µìž ì •ì˜: ::" "[:default]" msgid "Delete the namespace by removing all devices." msgstr "모든 디바ì´ìŠ¤ë¥¼ 제거하여 네임스페ì´ìŠ¤ë¥¼ 삭제하십시오. " #, python-format msgid "Deleting port %s" msgstr "í¬íЏ %s ì‚­ì œ 중" #, python-format msgid "Deployment error: %(reason)s." msgstr "ë°°í¬ ì˜¤ë¥˜: %(reason)s." msgid "Destroy IPsets even if there is an iptables reference." msgstr "iptables 참조가 있는 경우ì—ë„ IPset를 ì˜êµ¬ 삭제하십시오. " msgid "Destroy all IPsets." msgstr "모든 IPset를 ì˜êµ¬ 삭제하십시오. " #, python-format msgid "Device %(dev_name)s in mapping: %(mapping)s not unique" msgstr "%(mapping)s ë§µí•‘ì˜ %(dev_name)s 디바ì´ìŠ¤ê°€ 고유하지 않ìŒ" #, python-format msgid "Device name %(dev_name)s is missing from physical_device_mappings" msgstr "physical_device_mappingsì—서 디바ì´ìФ ì´ë¦„ %(dev_name)sì´(ê°€) 누ë½ë¨" msgid "Device not found" msgstr "디바ì´ìŠ¤ë¥¼ ì°¾ì„ ìˆ˜ ì—†ìŒ" #, python-format msgid "" "Distributed Virtual Router Mac Address for host %(host)s does not exist." msgstr "%(host)s í˜¸ìŠ¤íŠ¸ì˜ ë¶„ì‚° ê°€ìƒ ë¼ìš°í„° Mac 주소가 없습니다." msgid "Domain to use for building the hostnames" msgstr "호스트 ì´ë¦„ ë¹Œë“œì— ì‚¬ìš©í•  ë„ë©”ì¸" msgid "Downgrade no longer supported" msgstr "다운그레ì´ë“œëŠ” 현재 ì§€ì›í•˜ì§€ 않ìŒ" #, python-format msgid "Driver %s is not unique across providers" msgstr "%s 드ë¼ì´ë²„ê°€ 제공ìžì—서 고유하지 않ìŒ" msgid "Driver for external DNS integration." msgstr "외부 DNS í†µí•©ì„ ìœ„í•œ 드ë¼ì´ë²„." msgid "Driver for security groups firewall in the L2 agent" msgstr "L2 ì—ì´ì „íŠ¸ì˜ ë³´ì•ˆ 그룹 ë°©í™”ë²½ì— ëŒ€í•œ 드ë¼ì´ë²„" msgid "Driver to use for scheduling network to DHCP agent" msgstr "DHCP ì—ì´ì „íŠ¸ì— ëŒ€í•œ ë„¤íŠ¸ì›Œí¬ ìŠ¤ì¼€ì¤„ë§ì— 사용할 드ë¼ì´ë²„" msgid "Driver to use for scheduling router to a default L3 agent" msgstr "기본 L3 ì—ì´ì „íŠ¸ì— ëŒ€í•œ ë¼ìš°í„° 스케줄ë§ì— 사용할 드ë¼ì´ë²„" msgid "" "Driver used for ipv6 prefix delegation. This needs to be an entry point " "defined in the neutron.agent.linux.pd_drivers namespace. See setup.cfg for " "entry points included with the neutron source." msgstr "" "ipv6 ì ‘ë‘ë¶€ ìœ„ìž„ì— ì‚¬ìš©ë˜ëŠ” 드ë¼ì´ë²„입니다. ì´ëŠ” neutron.agent.linux." "pd_drivers 네임스페ì´ìФì—서 ì •ì˜ëœ 시작ì ì´ì–´ì•¼ 합니다. neutron 소스와 함께 " "í¬í•¨ëœ 시작ì ì€ setup.cfg를 참조하십시오. " #, python-format msgid "" "Duplicate L3HARouterAgentPortBinding is created for router(s) %(router)s. " "Database cannot be upgraded. Please, remove all duplicates before upgrading " "the database." msgstr "" "ë¼ìš°í„° %(router)sì˜ ì¤‘ë³µ L3HARouterAgentPortBindingì´ ìž‘ì„±ë˜ì—ˆìŠµë‹ˆë‹¤. ë°ì´í„°" "ë² ì´ìŠ¤ë¥¼ 업그레ì´ë“œí•  수 없습니다. ë°ì´í„°ë² ì´ìŠ¤ë¥¼ 업그레ì´ë“œí•˜ê¸° ì „ì— ëª¨ë“  중" "ë³µì„ ì œê±°í•˜ì‹­ì‹œì˜¤." msgid "Duplicate Security Group Rule in POST." msgstr "POSTì— ì¤‘ë³µ 보안 그룹 ê·œì¹™ì´ ìžˆìŠµë‹ˆë‹¤. " msgid "Duplicate address detected" msgstr "중복 주소 발견" msgid "Duplicate segment entry in request." msgstr "ìš”ì²­ì— ì¤‘ë³µë˜ëŠ” 세그먼트 í•­ëª©ì´ ìžˆìŒ." #, python-format msgid "ERROR: %s" msgstr "오류: %s" msgid "" "ERROR: Unable to find configuration file via the default search paths (~/." "neutron/, ~/, /etc/neutron/, /etc/) and the '--config-file' option!" msgstr "" "오류: 기본 검색 경로(~/.quantum/, ~/, /etc/quantum/, /etc/) ë° '--config-" "file' (~/.neutron/, ~/, /etc/neutron/, /etc/) ë° '--config-file' 옵션!" msgid "" "Either one of parameter network_id or router_id must be passed to _get_ports " "method." msgstr "" "매개변수 network_id ë° router_id 중 하나를 _get_ports ë©”ì†Œë“œì— ì „ë‹¬í•´ì•¼ 합니" "다." msgid "Either subnet_id or port_id must be specified" msgstr "subnet_id ë˜ëŠ” port_id 중 하나를 지정해야 함" msgid "Empty physical network name." msgstr "실제 ë„¤íŠ¸ì›Œí¬ ì´ë¦„ì´ ë¹„ì–´ 있습니다." msgid "Empty subnet pool prefix list." msgstr "서브넷 í’€ ì ‘ë‘ë¶€ 목ë¡ì´ 비어 있습니다." msgid "Enable HA mode for virtual routers." msgstr "ê°€ìƒ ë¼ìš°í„°ì— 대해 HA 모드를 사용합니다." msgid "Enable SSL on the API server" msgstr "API 서버ì—서 SSL ì—°ê²° 활성화" msgid "" "Enable VXLAN on the agent. Can be enabled when agent is managed by ml2 " "plugin using linuxbridge mechanism driver" msgstr "" "ì—ì´ì „트ì—서 VXLANì„ ì‚¬ìš© 가능하게 설정하십시오. linuxbridge 메커니즘 드ë¼ì´" "버를 사용하여 ml2 플러그ì¸ì´ ì—ì´ì „트를 관리할 경우 사용할 수 있습니다." msgid "" "Enable local ARP responder if it is supported. Requires OVS 2.1 and ML2 " "l2population driver. Allows the switch (when supporting an overlay) to " "respond to an ARP request locally without performing a costly ARP broadcast " "into the overlay." msgstr "" "로컬 ARP ì‘답기가 ì§€ì›ë˜ëŠ” 경우 ì´ë¥¼ 사용합니다. OVS 2.1 ë° ML2 l2population " "드ë¼ì´ë²„ê°€ 필요합니다. 스위치(오버레ì´ë¥¼ ì§€ì›í•˜ëŠ” 경우)ê°€ 오버레ì´ë¡œ ë¹„ìš©ì´ " "ë§Žì´ ë“œëŠ” ARP 브로드ìºìŠ¤íŠ¸ë¥¼ 수행하지 않고 로컬로 ARP ìš”ì²­ì— ì‘답할 수 있ë„" "ë¡ í•©ë‹ˆë‹¤." msgid "" "Enable services on an agent with admin_state_up False. If this option is " "False, when admin_state_up of an agent is turned False, services on it will " "be disabled. Agents with admin_state_up False are not selected for automatic " "scheduling regardless of this option. But manual scheduling to such agents " "is available if this option is True." msgstr "" "admin_state_up Falseì¸ ì—ì´ì „íŠ¸ì˜ ì„œë¹„ìŠ¤ 사용. ì´ ì˜µì…˜ì´ Falseì´ë©´ ì—ì´ì „트" "ì˜ admin_state_upì´ Falseê°€ ë  ë•Œ 해당 서비스가 사용 안함으로 설정ë©ë‹ˆë‹¤. " "admin_state_up Falseì¸ ì—ì´ì „트는 ì´ ì˜µì…˜ê³¼ 관계 ì—†ì´ ìžë™ 스케줄ë§ì— 사용하" "ë„ë¡ ì„ íƒí•˜ì§€ 않습니다. 그러나 ì´ ì˜µì…˜ì´ Trueì´ë©´ ì´ëŸ¬í•œ ì—ì´ì „íŠ¸ì— ìˆ˜ë™ ìŠ¤ì¼€" "줄ë§ì„ 사용할 수 있습니다." msgid "" "Enables IPv6 Prefix Delegation for automatic subnet CIDR allocation. Set to " "True to enable IPv6 Prefix Delegation for subnet allocation in a PD-capable " "environment. Users making subnet creation requests for IPv6 subnets without " "providing a CIDR or subnetpool ID will be given a CIDR via the Prefix " "Delegation mechanism. Note that enabling PD will override the behavior of " "the default IPv6 subnetpool." msgstr "" "ì§€ë™ ì„œë¸Œë„· CIDR í• ë‹¹ì„ ìœ„í•´ IPV6 ì ‘ë‘ì–´ ìœ„ìž„ì„ ì‚¬ìš©í•©ë‹ˆë‹¤. PD 가능 환경ì—서 " "서브넷 í• ë‹¹ì„ ìœ„í•´ IPv6 ì ‘ë‘ì–´ ìœ„ìž„ì„ ì‚¬ìš©í•˜ë ¤ë©´ True로 설정합니다. 사용ìžê°€ " "CIDR ë˜ëŠ” subnetpool ID를 제공하지 않고 IPv6 ì„œë¸Œë„·ì˜ ì„œë¸Œë„· ìž‘ì„±ì„ ìš”ì²­í•˜ë©´ " "ì ‘ë‘ì–´ 위임 ë©”ì»¤ë‹ˆì¦˜ì„ í†µí•´ CIDRì´ ì œê³µë©ë‹ˆë‹¤. PD를 사용하면 기본 IPv6 " "subnetpoolì˜ ë™ìž‘ì´ ìž¬ì •ì˜ë©ë‹ˆë‹¤." msgid "" "Enables the dnsmasq service to provide name resolution for instances via DNS " "resolvers on the host running the DHCP agent. Effectively removes the '--no-" "resolv' option from the dnsmasq process arguments. Adding custom DNS " "resolvers to the 'dnsmasq_dns_servers' option disables this feature." msgstr "" "DHCP ì—ì´ì „트를 실행 ì¤‘ì¸ í˜¸ìŠ¤íŠ¸ì—서 DNS ë¶„ì„기를 통해 ì¸ìŠ¤í„´ìŠ¤ì˜ ì´ë¦„ ë¶„ì„" "ì„ ì œê³µí•˜ëŠ” dnsmasq 서비스를 사용합니다. dnsmasq 프로세스 ì¸ìˆ˜ì—서 '--no-" "resolv' ì˜µì…˜ì„ íš¨ê³¼ì ìœ¼ë¡œ 제거합니다. ì‚¬ìš©ìž ì •ì˜ DNS ë¶„ì„기를 " "'dnsmasq_dns_servers' ì˜µì…˜ì— ì¶”ê°€í•˜ë©´ ì´ ê¸°ëŠ¥ì´ ì‚¬ìš©ë˜ì§€ 않습니다." msgid "End of VLAN range is less than start of VLAN range" msgstr "VLAN ë²”ìœ„ì˜ ëì´ VLAN ë²”ìœ„ì˜ ì‹œìž‘ë³´ë‹¤ 작습니다. " msgid "End of tunnel range is less than start of tunnel range" msgstr "í„°ë„ ë²”ìœ„ì˜ ëì´ í„°ë„ ë²”ìœ„ì˜ ì‹œìž‘ë³´ë‹¤ ìž‘ìŒ" #, python-format msgid "Error %(reason)s while attempting the operation." msgstr "ì¡°ìž‘ ì‹œë„ ì¤‘ 오류 %(reason)sì´(ê°€) ë°œìƒí–ˆìŠµë‹ˆë‹¤. " #, python-format msgid "Error parsing dns address %s" msgstr "DNS 주소 %s 구문 ë¶„ì„ ì˜¤ë¥˜" #, python-format msgid "Error while reading %s" msgstr "%sì„(를) ì½ëŠ” ì¤‘ì— ì˜¤ë¥˜ ë°œìƒ" #, python-format msgid "" "Exceeded %s second limit waiting for address to leave the tentative state." msgstr "" "주소가 임시 ìƒíƒœë¥¼ ë²—ì–´ë‚  때까지 대기하는 ë™ì•ˆ %sì´ˆ ì œí•œì´ ì´ˆê³¼ë˜ì–´ì”ƒã…‚니다." msgid "Existing prefixes must be a subset of the new prefixes" msgstr "기존 ì ‘ë‘부는 새 ì ‘ë‘ë¶€ì˜ ì„œë¸Œë„·ì´ì–´ì•¼ 함" #, python-format msgid "" "Exit code: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" msgstr "" "종료 코드: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" #, python-format msgid "Extension %(driver)s failed." msgstr "확장 %(driver)sì´(ê°€) 실패했습니다. " #, python-format msgid "" "Extension driver %(driver)s required for service plugin %(service_plugin)s " "not found." msgstr "" "서비스 í”ŒëŸ¬ê·¸ì¸ %(service_plugin)sì— í•„ìš”í•œ 확장 드ë¼ì´ë²„ %(driver)sì„(를) ì°¾" "ì„ ìˆ˜ ì—†ìŒ" msgid "" "Extension to use alongside ml2 plugin's l2population mechanism driver. It " "enables the plugin to populate VXLAN forwarding table." msgstr "" "ml2 플러그ì¸ì˜ l2population 메커니즘 드ë¼ì´ë²„와 함께 사용할 확장기능. ì´ë¥¼ 통" "해플러그ì¸ì´ VXLAN 전달 í…Œì´ë¸”ì„ ì±„ìš¸ 수 있습니다." #, python-format msgid "Extension with alias %s does not exist" msgstr "ë³„ëª…ì´ %sì¸ í™•ìž¥ì´ ì¡´ìž¬í•˜ì§€ 않ìŒ" msgid "Extensions list to use" msgstr "사용할 확장 목ë¡" #, python-format msgid "Extensions not found: %(extensions)s." msgstr "í™•ìž¥ê¸°ëŠ¥ì„ ì°¾ì„ ìˆ˜ ì—†ìŒ: %(extensions)s." #, python-format msgid "External IP %s is the same as the gateway IP" msgstr "외부 IP %sì´(ê°€) 게ì´íŠ¸ì›¨ì´ IP와 ê°™ìŒ" #, python-format msgid "Failed rescheduling router %(router_id)s: no eligible l3 agent found." msgstr "" "%(router_id)s ë¼ìš°í„°ë¥¼ 다시 스케줄하지 못함: ì í•©í•œ l3 ì—ì´ì „트를 ì°¾ì„ ìˆ˜ ì—†" "습니다." #, python-format msgid "Failed scheduling router %(router_id)s to the L3 Agent %(agent_id)s." msgstr "" "L3 ì—ì´ì „트 %(agent_id)sì— ëŒ€í•œ %(router_id)s ë¼ìš°í„°ë¥¼ 스케줄ë§í•˜ì§€ 못했습니" "다. " #, python-format msgid "Failed to allocate subnet: %(reason)s." msgstr "서브넷 할당 실패: %(reason)s." msgid "" "Failed to associate address scope: subnetpools within an address scope must " "have unique prefixes." msgstr "" "주소 범위를 연관시키는 ë° ì‹¤íŒ¨: 주소 ë²”ìœ„ì˜ subnetpoolì—는 고유한 ì ‘ë‘ì–´ê°€ 있" "어야 합니다." #, python-format msgid "" "Failed to create port on network %(network_id)s, because fixed_ips included " "invalid subnet %(subnet_id)s" msgstr "" "fixed_ipsì— ì˜¬ë°”ë¥´ì§€ ì•Šì€ ì„œë¸Œë„· %(subnet_id)sì´(ê°€) í¬í•¨ë˜ì–´ 있어서 네트워" "í¬ %(network_id)sì—서 í¬íŠ¸ë¥¼ 작성하지 못했습니다. " #, python-format msgid "Failed to locate source for %s." msgstr "%sì— ëŒ€í•œ 소스를 찾지 못했습니다. " #, python-format msgid "" "Failed to remove provided policy %(policy_id)s because you are not " "authorized." msgstr "" "ë‹¹ì‹ ì€ ê¶Œí•œì´ ì—†ê¸° ë•Œë¬¸ì— ì œê³µí•˜ëŠ” ì •ì±… %(policy_id)sì„ ì œê±°í•  수 없습니다." msgid "Failed to remove supplemental groups" msgstr "ë³´ì¡° ê·¸ë£¹ì„ ì œê±°í•˜ì§€ 못함" #, python-format msgid "Failed to set gid %s" msgstr "gid %sì„(를) 설정하지 못함" #, python-format msgid "Failed to set uid %s" msgstr "uid %sì„(를) 설정하지 못함" #, python-format msgid "Failed to set-up %(type)s tunnel port to %(ip)s" msgstr "%(type)s í„°ë„ í¬íŠ¸ë¥¼ %(ip)s(으)로 설정하지 못함" msgid "Failure applying iptables rules" msgstr "iptables 규칙 ì ìš© 실패" #, python-format msgid "Failure waiting for address %(address)s to become ready: %(reason)s" msgstr "주소 %(address)sì´(ê°€) ì¤€ë¹„ë  ë•Œê¹Œì§€ 기다리는 ë° ì‹¤íŒ¨í•¨: %(reason)s" msgid "Flat provider networks are disabled" msgstr "플랫 ì œê³µìž ë„¤íŠ¸ì›Œí¬ê°€ 사용ë˜ì§€ 않ìŒ" msgid "For TCP/UDP protocols, port_range_min must be <= port_range_max" msgstr "TCP/UDP í”„ë¡œí† ì½œì˜ ê²½ìš° port_range_minì€ port_range_max ì´í•˜ì—¬ì•¼ 함" msgid "Force ip_lib calls to use the root helper" msgstr "루트 í—¬í¼ë¥¼ 사용하는 ip_lib í˜¸ì¶œì„ ê°•ì œí•©ë‹ˆë‹¤" #, python-format msgid "Found duplicate extension: %(alias)s." msgstr "중복 확장 발견: %(alias)s." #, python-format msgid "" "Found overlapping allocation pools: %(pool_1)s %(pool_2)s for subnet " "%(subnet_cidr)s." msgstr "겹치는 할당 í’€ 발견: 서브넷 %(subnet_cidr)sì˜ %(pool_1)s %(pool_2)s." msgid "Gateway IP version inconsistent with allocation pool version" msgstr "게ì´íŠ¸ì›¨ì´ IP ë²„ì „ì´ í• ë‹¹ í’€ 버전과 ì¼ì¹˜í•˜ì§€ 않ìŒ" #, python-format msgid "Gateway ip %(ip_address)s conflicts with allocation pool %(pool)s." msgstr "게ì´íŠ¸ì›¨ì´ IP %(ip_address)sì´(ê°€) 할당 í’€ %(pool)sê³¼(와) ì¶©ëŒí•©ë‹ˆë‹¤. " msgid "Gateway is not valid on subnet" msgstr "게ì´íŠ¸ì›¨ì´ê°€ 서브넷ì—서 올바르지 않ìŒ" msgid "" "Geneve encapsulation header size is dynamic, this value is used to calculate " "the maximum MTU for the driver. This is the sum of the sizes of the outer " "ETH + IP + UDP + GENEVE header sizes. The default size for this field is 50, " "which is the size of the Geneve header without any additional option headers." msgstr "" "Geneve 캡ìŠí™” í—¤ë” í¬ê¸°ê°€ ë™ì ìž…니다. ì´ ê°’ì€ ë“œë¼ì´ë²„ì˜ ìµœëŒ€ MTU를 계산하는 " "ë° ì‚¬ìš©í•©ë‹ˆë‹¤. ì´ ê°’ì€ ì™¸ë¶€ ETH + IP + UDP + GENEVE í—¤ë” í¬ê¸°ì˜ 합계입니다. " "ì´ í•„ë“œì´ ê¸°ë³¸ í¬ê¸°ëŠ” 50으로서, 추가 옵션 í—¤ë”ê°€ 없는 Geneve í—¤ë”ì˜ í¬ê¸°ìž…니" "다." msgid "" "Group (gid or name) running metadata proxy after its initialization (if " "empty: agent effective group)." msgstr "" "초기화 í›„ì— ë©”íƒ€ë°ì´í„° 프ë¡ì‹œë¥¼ 실행하는 그룹(gid ë˜ëŠ” ì´ë¦„)(비어 있는 경우: " "ì—ì´ì „트 유효 그룹)." msgid "Group (gid or name) running this process after its initialization" msgstr "초기화 ì´í›„ ì´ í”„ë¡œì„¸ìŠ¤ë¥¼ 실행하는 그룹(gid ë˜ëŠ” ì´ë¦„)" msgid "" "Hostname to be used by the Neutron server, agents and services running on " "this machine. All the agents and services running on this machine must use " "the same host value." msgstr "" "ì´ ì‹œìŠ¤í…œì—서 실행 ì¤‘ì¸ neutron 서버, ì—ì´ì „트 ë° ì„œë¹„ìŠ¤ì—서 사용할 호스트 ì´" "름입니다. ì´ ì‹œìŠ¤í…œì—서 실행 ì¤‘ì¸ ëª¨ë“  ì—ì´ì „트 ë° ì„œë¹„ìŠ¤ëŠ” ê°™ì€ í˜¸ìŠ¤íŠ¸ ê°’" "ì„ ì‚¬ìš©í•´ì•¼ 합니다." #, python-format msgid "" "ICMP code (port-range-max) %(value)s is provided but ICMP type (port-range-" "min) is missing." msgstr "" "ICMP 코드 (port-range-max) %(value)sì´(ê°€) 제공ë˜ì§€ë§Œ ICMP 유형(port-range-" "min)ì´ ëˆ„ë½ë˜ì—ˆìŠµë‹ˆë‹¤." msgid "ID of network" msgstr "네트워í¬ì˜ ID" msgid "ID of network to probe" msgstr "프로브할 네트워í¬ì˜ ID" msgid "ID of probe port to delete" msgstr "삭제할 프로브 í¬íŠ¸ì˜ ID" msgid "ID of probe port to execute command" msgstr "ëª…ë ¹ì„ ì‹¤í–‰í•  프로브 í¬íŠ¸ì˜ ID" msgid "ID of the router" msgstr "ë¼ìš°í„°ì˜ ID" #, python-format msgid "IP address %(ip)s already allocated in subnet %(subnet_id)s" msgstr "IP 주소 %(ip)sì´(ê°€) ì´ë¯¸ 서브넷 %(subnet_id)sì—서 할당ë˜ì–´ 있ìŒ" #, python-format msgid "IP address %(ip)s does not belong to subnet %(subnet_id)s" msgstr "IP 주소 %(ip)sì´(ê°€) 서브넷 %(subnet_id)sì— ì†í•˜ì§€ 않ìŒ" msgid "IP allocation failed. Try again later." msgstr "IP í• ë‹¹ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤. ë‚˜ì¤‘ì— ë‹¤ì‹œ 시ë„하십시오." msgid "IP allocation requires subnet_id or ip_address" msgstr "IP í• ë‹¹ì€ subnet_id ë˜ëŠ” ip_addressê°€ 필요함" msgid "IP allocation requires subnets for network" msgstr "IP í• ë‹¹ì€ ë„¤íŠ¸ì›Œí¬ì˜ ì„œë¸Œë„·ì„ í•„ìš”ë¡œ 합니다." #, python-format msgid "" "IPTablesManager.apply failed to apply the following set of iptables rules:\n" "%s" msgstr "" "IPTablesManager.applyê°€ ë‹¤ìŒ iptables 규칙 세트를 ì ìš©í•˜ì§€ 못함:\n" "%s" msgid "IPtables conntrack zones exhausted, iptables rules cannot be applied." msgstr "" "IPtables conntrack êµ¬ì—­ì´ ì†Œì§„ë˜ì—ˆìŠµë‹ˆë‹¤. iptables ê·œì¹™ì„ ì ìš©í•  수 없습니" "다. " msgid "IPv6 Address Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "IPv6 주소 모드는 ì ‘ë‘ë¶€ ìœ„ìž„ì— ëŒ€í•´ Stateless ë˜ëŠ” SLAAC여야 합니다. " msgid "IPv6 RA Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "IPv6 RA 모드는 ì ‘ë‘ë¶€ ìœ„ìž„ì— ëŒ€í•´ Stateless ë˜ëŠ” SLAAC여야 합니다. " #, python-format msgid "" "IPv6 address %(ip)s cannot be directly assigned to a port on subnet " "%(subnet_id)s as the subnet is configured for automatic addresses" msgstr "" "ì„œë¸Œë„·ì´ ìžë™ ì£¼ì†Œì— ëŒ€í•´ 구성ë˜ì–´ 있기 ë•Œë¬¸ì— IPv6 주소 %(ip)sì„(를) 서브넷 " "%(subnet_id)sì˜ í¬íŠ¸ì— ì§ì ‘ 지정할 수 ì—†ìŒ" #, python-format msgid "" "IPv6 subnet %s configured to receive RAs from an external router cannot be " "added to Neutron Router." msgstr "" "외부 ë¼ìš°í„°ì—서 RA를 수신하ë„ë¡ êµ¬ì„±ëœ IPv6 서브넷 %sì„(를) Neutron ë¼ìš°í„°ì— " "추가할 수 없습니다." msgid "" "If True, then allow plugins that support it to create VLAN transparent " "networks." msgstr "" "Trueì¸ ê²½ìš° ì´ë¥¼ ì§€ì›í•˜ëŠ” 플러그ì¸ì„ 사용하여 VLAN 투명 네트워í¬ë¥¼ 작성할 수 " "있습니다." msgid "Illegal IP version number" msgstr "올바르지 ì•Šì€ IP 버전 번호" #, python-format msgid "" "Illegal prefix bounds: %(prefix_type)s=%(prefixlen)s, %(base_prefix_type)s=" "%(base_prefixlen)s." msgstr "" "ìž˜ëª»ëœ ì ‘ë‘ë¶€ 바운드: %(prefix_type)s=%(prefixlen)s, %(base_prefix_type)s=" "%(base_prefixlen)s." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot " "associate with address scope %(address_scope_id)s because subnetpool " "ip_version is not %(ip_version)s." msgstr "" "ìž˜ëª»ëœ subnetpool ì—°ê´€: subnetpool ip_versionì´ %(ip_version)sì´(ê°€) 아니므" "로 subnetpool %(subnetpool_id)sì„(를) 주소 범위 %(address_scope_id)sê³¼(와) ì—°" "관시킬 수 없습니다." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot be " "associated with address scope %(address_scope_id)s." msgstr "" "ìž˜ëª»ëœ subnetpool ì—°ê´€: subnetpool %(subnetpool_id)sì„(를) 주소 범위 " "%(address_scope_id)sê³¼(와) 연관시킬 수 없습니다." #, python-format msgid "Illegal subnetpool update : %(reason)s." msgstr "ìž˜ëª»ëœ subnetpool ì—…ë°ì´íЏ : %(reason)s." #, python-format msgid "Illegal update to prefixes: %(msg)s." msgstr "ìž˜ëª»ëœ ì ‘ë‘ë¶€ ì—…ë°ì´íЏ: %(msg)s." msgid "" "In some cases the Neutron router is not present to provide the metadata IP " "but the DHCP server can be used to provide this info. Setting this value " "will force the DHCP server to append specific host routes to the DHCP " "request. If this option is set, then the metadata service will be activated " "for all the networks." msgstr "" "ê²½ìš°ì— ë”°ë¼ ë©”íƒ€ë°ì´í„° IP를 제공하는 Neutron ë¼ìš°í„°ëŠ” 없지만 DHCP 서버를 사용" "하여 ì´ ì •ë³´ë¥¼ 제공할 수 있습니다. ì´ ê°’ì„ ì„¤ì •í•˜ë©´ DHCP 서버가 특정 호스트 " "경로를 DHCP ìš”ì²­ì— ê°•ì œë¡œ 추가합니다. ì´ ì˜µì…˜ì´ ì„¤ì •ë˜ë©´ 모든 네트워í¬ì˜ 메타" "ë°ì´í„° 서비스가 활성화ë©ë‹ˆë‹¤." msgid "" "Indicates that this L3 agent should also handle routers that do not have an " "external network gateway configured. This option should be True only for a " "single agent in a Neutron deployment, and may be False for all agents if all " "routers must have an external network gateway." msgstr "" "ì´ L3 ì—ì´ì „트ì—서 외부 ë„¤íŠ¸ì›Œí¬ ê²Œì´íŠ¸ì›¨ì´ê°€ 구성ë˜ì§€ ì•Šì€ ë¼ìš°í„°ë„ 처리해" "야 í•¨ì„ ë‚˜íƒ€ëƒ…ë‹ˆë‹¤. ì´ ì˜µì…˜ì€ Neutron ë°°í¬ì˜ ë‹¨ì¼ ì—ì´ì „트ì—ë§Œ True여야 하" "ë©°, ë¼ìš°í„°ì— 외부 ë„¤íŠ¸ì›Œí¬ ê²Œì´íŠ¸ì›¨ì´ê°€ 있어야 하는 경우ì—는 모든 ì—ì´ì „íŠ¸ì— " "Falseì¼ ìˆ˜ 있습니다." #, python-format msgid "Instance of class %(module)s.%(class)s must contain _cache attribute" msgstr "" "í´ëž˜ìФ %(module)s.%(class)sì˜ ì¸ìŠ¤í„´ìŠ¤ì— _cache ì†ì„±ì´ í¬í•¨ë˜ì–´ì•¼ 합니다." #, python-format msgid "Insufficient prefix space to allocate subnet size /%s" msgstr "ì ‘ë‘ë¶€ ê³µê°„ì´ ë¶€ì¡±í•˜ì—¬ 서브넷 í¬ê¸° /%sì„(를) 할당할 수 ì—†ìŒ" msgid "Insufficient rights for removing default security group." msgstr "기본 보안 ê·¸ë£¹ì„ ì œê±°í•  수 있는 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤." msgid "" "Integration bridge to use. Do not change this parameter unless you have a " "good reason to. This is the name of the OVS integration bridge. There is one " "per hypervisor. The integration bridge acts as a virtual 'patch bay'. All VM " "VIFs are attached to this bridge and then 'patched' according to their " "network connectivity." msgstr "" "사용할 통합 브릿지입니다. 합당한 ì´ìœ ê°€ 없으면 ì´ ë§¤ê°œë³€ìˆ˜ë¥¼ 변경하지 마십시" "오. ì´ ë§¤ê°œë³€ìˆ˜ëŠ” OVS 통합 ë¸Œë¦¿ì§€ì˜ ì´ë¦„입니다. 하ì´í¼ë°”ì´ì €ë‹¹ 한 개가 있습니" "다. 통합 브릿지는 ê°€ìƒ '패치 ë² ì´'ì˜ ì—­í• ì„ ìˆ˜í–‰í•©ë‹ˆë‹¤. 모든 VM VIFê°€ ì´ ë¸Œë¦¿" "ì§€ì— ì—°ê²°ëœ ë‹¤ìŒ ë„¤íŠ¸ì›Œí¬ ì—°ê²°ì„±ì— ë”°ë¼ \"패치\"ë©ë‹ˆë‹¤." msgid "Interface to monitor" msgstr "모니터할 ì¸í„°íŽ˜ì´ìФ" msgid "" "Interval between checks of child process liveness (seconds), use 0 to disable" msgstr "" "하위 프로세스 í™œë™ í™•ì¸ ê°„ê²©(ì´ˆ), 사용 안함으로 설정하려면 0ì„ ì§€ì •í•˜ì‹­ì‹œì˜¤." msgid "Interval between two metering measures" msgstr "2ê°œì˜ ì¸¡ì • 조치 ê°„ì˜ ê°„ê²©" msgid "Interval between two metering reports" msgstr "2ê°œì˜ ì¸¡ì • 보고서 ê°„ì˜ ê°„ê²©" #, python-format msgid "Invalid CIDR %(input)s given as IP prefix." msgstr "IP ì ‘ë‘부로 ì§€ì •ëœ CIDR %(input)sì´(ê°€) 올바르지 않습니다." #, python-format msgid "Invalid Device %(dev_name)s: %(reason)s" msgstr "올바르지 ì•Šì€ ë””ë°”ì´ìФ %(dev_name)s: %(reason)s" #, python-format msgid "" "Invalid action '%(action)s' for object type '%(object_type)s'. Valid " "actions: %(valid_actions)s" msgstr "" "오브ì íЏ 유형 '%(object_type)s'ì— ëŒ€í•œ 조치 '%(action)s'ì´(ê°€) 올바르지 않습" "니다. 올바른 조치: %(valid_actions)s" #, python-format msgid "" "Invalid authentication type: %(auth_type)s, valid types are: " "%(valid_auth_types)s" msgstr "" "올바르지 ì•Šì€ ì¸ì¦ 유형임: %(auth_type)s, 올바른 ìœ í˜•ì€ ë‹¤ìŒê³¼ ê°™ìŒ: " "%(valid_auth_types)s" #, python-format msgid "Invalid ethertype %(ethertype)s for protocol %(protocol)s." msgstr "" "프로토콜 %(protocol)sì˜ ethertype %(ethertype)sì´(ê°€) 올바르지 않습니다." #, python-format msgid "Invalid format: %s" msgstr "올바르지 ì•Šì€ í˜•ì‹: %s" #, python-format msgid "Invalid instance state: %(state)s, valid states are: %(valid_states)s" msgstr "" "올바르지 ì•Šì€ ì¸ìŠ¤í„´ìŠ¤ ìƒíƒœ: %(state)s, 올바른 ìƒíƒœëŠ” %(valid_states)sìž„" #, python-format msgid "Invalid mapping: '%s'" msgstr "올바르지 ì•Šì€ ë§µí•‘: '%s'" #, python-format msgid "Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'." msgstr "올바르지 ì•Šì€ ë„¤íŠ¸ì›Œí¬ VLAN 범위: '%(vlan_range)s' - '%(error)s'." #, python-format msgid "Invalid network VXLAN port range: '%(vxlan_range)s'." msgstr "올바르지 ì•Šì€ ë„¤íŠ¸ì›Œí¬ VXLAN í¬íЏ 범위: '%(vxlan_range)s'." #, python-format msgid "Invalid pci slot %(pci_slot)s" msgstr "올바르지 ì•Šì€ pci 슬롯 %(pci_slot)s" #, python-format msgid "Invalid provider format. Last part should be 'default' or empty: %s" msgstr "" "올바르지 ì•Šì€ ì œê³µìž í˜•ì‹. 마지막 ë¶€ë¶„ì´ '기본값'ì´ê±°ë‚˜ 비어 있어야 함: %s" #, python-format msgid "Invalid resource type %(resource_type)s" msgstr "올바르지 ì•Šì€ ìžì› 유형 %(resource_type)s" #, python-format msgid "Invalid route: %s" msgstr "올바르지 ì•Šì€ ë¼ìš°íЏ: %s" msgid "Invalid service provider format" msgstr "올바르지 ì•Šì€ ì„œë¹„ìŠ¤ ì œê³µìž í˜•ì‹" #, python-format msgid "" "Invalid value for ICMP %(field)s (%(attr)s) %(value)s. It must be 0 to 255." msgstr "" "ICMP %(field)s (%(attr)s) %(value)sì˜ ê°’ì´ ì˜¬ë°”ë¥´ì§€ 않ìŒ. ì´ ê°’ì€ 0ì—서 255 " "사ì´ì—¬ì•¼ 합니다. " #, python-format msgid "Invalid value for port %(port)s" msgstr "%(port)s í¬íŠ¸ì— ëŒ€í•œ 올바르지 ì•Šì€ ê°’" msgid "" "Iptables mangle mark used to mark ingress from external network. This mark " "will be masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "외부 네트워í¬ì˜ 입구를 표시하는 ë° ì‚¬ìš©ë˜ëŠ” Iptables mangle 표시입니다. ì´ í‘œ" "시는 하위 16비트만 사용ë˜ë„ë¡ 0xffff로 마스í¬ë©ë‹ˆë‹¤. " msgid "" "Iptables mangle mark used to mark metadata valid requests. This mark will be " "masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "메타ë°ì´í„° 올바른 ìš”ì²­ì„ í‘œì‹œí•˜ëŠ” ë° ì‚¬ìš©ë˜ëŠ” Iptables mangle 표시입니다. ì´ " "표시는 하위 16비트만 사용ë˜ë„ë¡ 0xffff로 마스í¬ë©ë‹ˆë‹¤. " msgid "Keepalived didn't respawn" msgstr "유휴ë˜ë©´ 다시 파ìƒë˜ì§€ 않ìŒ" msgid "Keepalived didn't spawn" msgstr "활성 유지(keepalive)ê°€ 파ìƒë˜ì§€ 않ìŒ" #, python-format msgid "" "Kernel HZ value %(value)s is not valid. This value must be greater than 0." msgstr "" "ì»¤ë„ HZ ê°’ %(value)sì´(ê°€) 올바르지 않습니다. ì´ ê°’ì€ 0보다 커야 합니다." msgid "L3 agent failure to setup NAT for floating IPs" msgstr "L3 ì—ì´ì „트ì—서 Floating IPì˜ NAT 설정 실패" msgid "L3 agent failure to setup floating IPs" msgstr "L3 ì—ì´ì „트ì—서 Floating IP 설정 실패" msgid "Limit number of leases to prevent a denial-of-service." msgstr "서비스 ê±°ë¶€(DoS)를 막기 위해 리스 수를 제한합니다." msgid "List of :" msgstr ":ì˜ ëª©ë¡" msgid "" "List of :: or " "specifying physical_network names usable for VLAN provider and tenant " "networks, as well as ranges of VLAN tags on each available for allocation to " "tenant networks." msgstr "" "테넌트 네트워í¬ì— 대한 í• ë‹¹ì— ì‚¬ìš©í•  수 있는 ê° VLAN íƒœê·¸ì˜ ë²”ìœ„ ë°VLAN 제공" "ìžì™€ 테넌트 네트워í¬ì— 사용할 수 있는 실제 ë„¤íŠ¸ì›Œí¬ ì´ë¦„ì„ ì§€ì •í•˜ëŠ” " ":: ë˜ëŠ” ì˜ ëª©ë¡ìž…니" "다." msgid "" "List of network type driver entrypoints to be loaded from the neutron.ml2." "type_drivers namespace." msgstr "" "neutron.ml2.type_drivers 네임스페ì´ìФì—서 ë¡œë“œí• ë„¤íŠ¸ì›Œí¬ ìœ í˜• 드ë¼ì´ë²„ 시작ì " "ì˜ ëª©ë¡ìž…니다. " msgid "" "List of physical_network names with which flat networks can be created. Use " "default '*' to allow flat networks with arbitrary physical_network names. " "Use an empty list to disable flat networks." msgstr "" "플랫 네트워í¬ë¥¼ 작성할 수 있는 실제 ë„¤íŠ¸ì›Œí¬ ì´ë¦„ì˜ ëª©ë¡ìž…니다. 플랫 네트워í¬" "ì— ìž„ì˜ì˜ physical_network ì´ë¦„ì„ ì‚¬ìš©í•˜ë ¤ë©´ 기본값 '*'를 사용하십시오. 빈 목" "ë¡ì„ 사용하여 플랫 네트워í¬ë¥¼ 비활성화합니다." msgid "Location for Metadata Proxy UNIX domain socket." msgstr "메타ë°ì´í„° 프ë¡ì‹œ UNIX ë„ë©”ì¸ ì†Œì¼“ì˜ ìœ„ì¹˜ìž…ë‹ˆë‹¤." msgid "Location of Metadata Proxy UNIX domain socket" msgstr "메타ë°ì´í„° 프ë¡ì‹œ UNIX ë„ë©”ì¸ ì†Œì¼“ì˜ ìœ„ì¹˜" msgid "Location to store DHCP server config files." msgstr "DHCP 서버 구성 파ì¼ì„ 저장할 위치." msgid "Location to store IPv6 PD files." msgstr "IPv6 PD 파ì¼ì„ 저장할 위치입니다. " msgid "Location to store IPv6 RA config files" msgstr "IPv6 RA 구성 파ì¼ì„ 저장할 위치" msgid "Location to store child pid files" msgstr "하위 pid 파ì¼ì„ 저장할 위치" msgid "Location to store keepalived/conntrackd config files" msgstr "keepalived/conntrackd 구성 파ì¼ì„ 저장할 위치" msgid "Log agent heartbeats" msgstr "로그 ì—ì´ì „트 하트비트" msgid "" "MTU of the underlying physical network. Neutron uses this value to calculate " "MTU for all virtual network components. For flat and VLAN networks, neutron " "uses this value without modification. For overlay networks such as VXLAN, " "neutron automatically subtracts the overlay protocol overhead from this " "value. Defaults to 1500, the standard value for Ethernet." msgstr "" "기본 물리 네트워í¬ì˜ MTU입니다. Neutronì—서는 ì´ ê°’ì„ ì‚¬ìš©í•˜ì—¬ 모든 ê°€ìƒ ë„¤íŠ¸" "ì›Œí¬ êµ¬ì„± ìš”ì†Œì˜ MTU를 계산합니다. ì¼ë°˜ ë° VLAN 네트워í¬ì˜ 경우 neutronì—서" "는 ì´ ê°’ì„ ìˆ˜ì •í•˜ì§€ 않고 사용합니다. VXLANê³¼ ê°™ì€ ì˜¤ë²„ë ˆì´ ë„¤íŠ¸ì›Œí¬ì˜ 경우 " "neutronì´ ì´ ê°’ì—서 ì˜¤ë²„ë ˆì´ í”„ë¡œí† ì½œ 오버헤드를 ìžë™ìœ¼ë¡œ 제거합니다. ì´ë”ë„·" "ì˜ í‘œì¤€ ê°’ì¸ 1500으로 ê¸°ë³¸ê°’ì´ ì§€ì •ë©ë‹ˆë‹¤." msgid "MTU size of veth interfaces" msgstr "veth ì¸í„°íŽ˜ì´ìŠ¤ì˜ MTU í¬ê¸°" msgid "Make the l2 agent run in DVR mode." msgstr "l2 ì—ì´ì „트를 DVR 모드ì—서 실행하십시오." msgid "Malformed request body" msgstr "형ì‹ì´ 틀린 요청 본문" #, python-format msgid "Malformed request body: %(reason)s." msgstr "형ì‹ì´ ìž˜ëª»ëœ ìš”ì²­ 본문: %(reason)s." msgid "MaxRtrAdvInterval setting for radvd.conf" msgstr "radvd.confì˜ MaxRtrAdvInterval 설정" msgid "Maximum number of DNS nameservers per subnet" msgstr "서브넷당 최대 DNS 네임스페ì´ìФ 수" msgid "" "Maximum number of L3 agents which a HA router will be scheduled on. If it is " "set to 0 then the router will be scheduled on every agent." msgstr "" "HA ë¼ìš°í„°ê°€ ìŠ¤ì¼€ì¤„ë  ìµœëŒ€ L3 ì—ì´ì „트 수입니다. ì´ ìˆ˜ê°€ 0으로 설정ë˜ë©´ ë¼ìš°í„°" "ê°€ 모든 ì—ì´ì „트ì—서 스케줄ë©ë‹ˆë‹¤." msgid "Maximum number of allowed address pairs" msgstr "허용ë˜ëŠ” 주소 ìŒ ìµœëŒ€ 수" msgid "Maximum number of host routes per subnet" msgstr "서브넷당 호스트 ë¼ìš°íŠ¸ì˜ ìµœëŒ€ 수" msgid "Maximum number of routes per router" msgstr "ë¼ìš°í„°ë‹¹ 최대 경로 수" msgid "" "Metadata Proxy UNIX domain socket mode, 4 values allowed: 'deduce': deduce " "mode from metadata_proxy_user/group values, 'user': set metadata proxy " "socket mode to 0o644, to use when metadata_proxy_user is agent effective " "user or root, 'group': set metadata proxy socket mode to 0o664, to use when " "metadata_proxy_group is agent effective group or root, 'all': set metadata " "proxy socket mode to 0o666, to use otherwise." msgstr "" "메타ë°ì´í„° 프ë¡ì‹œ UNIX ë„ë©”ì¸ ì†Œì¼“ 모드, 4ê°œì˜ ê°’ì´ í—ˆìš©ë¨: 'deduce': " "metadata_proxy_user/group ê°’ì˜ ì¶”ë¡  모드, 'user': 메타ë°ì´í„° 프ë¡ì‹œ 소켓 모드" "를 0o644로 설정, metadata_proxy_userê°€ ì—ì´ì „트 유효 ì‚¬ìš©ìž ë˜ëŠ” ë£¨íŠ¸ì¸ ê²½ìš° " "사용, 'group': 메타ë°ì´í„° 프ë¡ì‹œ 소켓 모드를 0o664로 설정, " "metadata_proxy_groupì´ ì—ì´ì „트 유효 그룹 ë˜ëŠ” ë£¨íŠ¸ì¸ ê²½ìš° 사용, 'all': 메타" "ë°ì´í„° 프ë¡ì‹œ 소켓 모드를 0o666으로 설정, 기타 ê²½ìš°ì— ì‚¬ìš©" msgid "Metering driver" msgstr "측정 드ë¼ì´ë²„" msgid "MinRtrAdvInterval setting for radvd.conf" msgstr "radvd.confì˜ MinRtrAdvInterval 설정" msgid "Minimize polling by monitoring ovsdb for interface changes." msgstr "ì¸í„°íŽ˜ì´ìФ ë³€ê²½ì‚¬í•­ì— ëŒ€í•œ ovsdb를 모니터ë§í•˜ì—¬ í´ë§ì„ 최소화합니다." #, python-format msgid "Missing key in mapping: '%s'" msgstr "맵핑ì—서 키 누ë½: '%s'" msgid "" "Multicast group for VXLAN. When configured, will enable sending all " "broadcast traffic to this multicast group. When left unconfigured, will " "disable multicast VXLAN mode." msgstr "" "VXLANì˜ ë©€í‹°ìºìŠ¤íŠ¸ 그룹입니다. ì´ ê·¸ë£¹ì´ êµ¬ì„±ë˜ë©´ 모든 브로드ìºìŠ¤íŠ¸ íŠ¸ëž˜í”½ì„ " "ì´ ë©€í‹°ìºìŠ¤íŠ¸ ê·¸ë£¹ì— ë³´ë‚¼ 수 있습니다. 구성ë˜ì§€ ì•Šì€ ìƒíƒœë¡œ ë‘ë©´ 멀티ìºìŠ¤íŠ¸ " "VXLAN 모드가 사용ë˜ì§€ 않습니다." msgid "" "Multicast group(s) for vxlan interface. A range of group addresses may be " "specified by using CIDR notation. Specifying a range allows different VNIs " "to use different group addresses, reducing or eliminating spurious broadcast " "traffic to the tunnel endpoints. To reserve a unique group for each possible " "(24-bit) VNI, use a /8 such as 239.0.0.0/8. This setting must be the same on " "all the agents." msgstr "" "vxlan ì¸í„°íŽ˜ì´ìŠ¤ì˜ ë©€í‹°ìºìŠ¤íŠ¸ 그룹입니다. 그룹 ì£¼ì†Œì˜ ë²”ìœ„ëŠ” CIDR í‘œê¸°ë²•ì„ ì‚¬" "용하여 지정할 수 있습니다. 범위를 지정하면 여러 다른 VNIì—서 여러 다른 그룹 " "주소를 사용할 수 있으므로, í„°ë„ ì—”ë“œí¬ì¸íŠ¸ì— ëŒ€í•œ ì˜ì‚¬ 브로드ìºìŠ¤íŠ¸ íŠ¸ëž˜í”½ì´ " "ê°ì†Œí•˜ê±°ë‚˜ 제거ë©ë‹ˆë‹¤. 가능한 ê° (24비트) VNIì˜ ê³ ìœ  ê·¸ë£¹ì„ ìœ ì§€í•˜ë ¤ë©´ /8ì„ " "사용하십시오(예: 239.0.0.0/8). ì´ ì„¤ì •ì€ ëª¨ë“  ì—ì´ì „트ì—서 같아야 합니다." #, python-format msgid "Multiple default providers for service %s" msgstr "%s ì„œë¹„ìŠ¤ì— ëŒ€í•œ 다중 기본 ì œê³µìž " #, python-format msgid "Multiple plugins for service %s were configured" msgstr "%s ì„œë¹„ìŠ¤ì— ëŒ€í•œ 다중 플러그ì¸ì´ 구성ë˜ì—ˆìŒ" #, python-format msgid "Multiple providers specified for service %s" msgstr "%s ì„œë¹„ìŠ¤ì— ëŒ€í•´ 다중 제공ìžê°€ 지정ë¨" msgid "Multiple tenant_ids in bulk security group rule create not allowed" msgstr "ë²Œí¬ ë³´ì•ˆ 그룹 규칙 ìž‘ì„±ì˜ ë‹¤ì¤‘ tenant_id는 허용ë˜ì§€ 않ìŒ" msgid "Must also specify protocol if port range is given." msgstr "í¬íЏ 범위가 제공ë˜ëŠ” 경우 í”„ë¡œí† ì½œë„ ì§€ì •í•´ì•¼ 합니다. " msgid "Must specify one or more actions on flow addition or modification" msgstr "플로우 추가 ë˜ëŠ” 수정 시 하나 ì´ìƒì˜ 조치를 지정해야 함" msgid "Name of Open vSwitch bridge to use" msgstr "사용할 열린 vSwitch ë¸Œë¦¿ì§€ì˜ ì´ë¦„" msgid "" "Name of nova region to use. Useful if keystone manages more than one region." msgstr "" "사용할 nova ë¦¬ì ¼ì˜ ì´ë¦„입니다. í‚¤ìŠ¤í†¤ì´ ë‘˜ ì´ìƒì˜ ë¦¬ì ¼ì„ ê´€ë¦¬í•  경우 유용합니" "다." msgid "Namespace of the router" msgstr "ë¼ìš°í„°ì˜ 네임스페ì´ìФ" msgid "Native pagination depend on native sorting" msgstr "네ì´í‹°ë¸Œ 페ì´ì§€ 번호 매기기는 네ì´í‹°ë¸Œ ì •ë ¬ì— ë”°ë¼ ë‹¤ë¦„" #, python-format msgid "" "Need to apply migrations from %(project)s contract branch. This will require " "all Neutron server instances to be shutdown before proceeding with the " "upgrade." msgstr "" "%(project)s 계약 분기ì—서 마ì´ê·¸ë ˆì´ì…˜ì„ ì ìš©í•´ì•¼ 합니다. 업그레ì´ë“œë¥¼ 계ì†í•˜" "기 ì „ì— ëª¨ë“  Neutron 서버 ì¸ìŠ¤í„´ìŠ¤ë¥¼ 종료해야 합니다." msgid "Negative delta (downgrade) not supported" msgstr "ìŒìˆ˜ì˜ ë¸íƒ€(다운그레ì´ë“œ)는 ì§€ì›í•˜ì§€ 않ìŒ" msgid "Negative relative revision (downgrade) not supported" msgstr "ìŒìˆ˜ì˜ ìƒëŒ€ì  개정íŒ(다운그레ì´ë“œ)ì€ ì§€ì›í•˜ì§€ 않ìŒ" #, python-format msgid "Network %s does not contain any IPv4 subnet" msgstr "ë„¤íŠ¸ì›Œí¬ %sì— IPv4 ì„œë¸Œë„·ì´ í¬í•¨ë˜ì–´ 있지 않ìŒ" #, python-format msgid "Network %s is not a valid external network" msgstr "%s 네트워í¬ê°€ 올바른 외부 네트워í¬ê°€ 아님" #, python-format msgid "Network %s is not an external network" msgstr "%s 네트워í¬ê°€ 외부 네트워í¬ê°€ 아님" #, python-format msgid "" "Network of size %(size)s, from IP range %(parent_range)s excluding IP ranges " "%(excluded_ranges)s was not found." msgstr "" "IP 범위가 %(parent_range)sì´ê³  í¬ê¸°ê°€ %(size)sì¸(IP 범위 %(excluded_ranges)s " "제외) 네트워í¬ë¥¼ 발견하지 못했습니다." #, python-format msgid "Network type value '%s' not supported" msgstr "ë„¤íŠ¸ì›Œí¬ ìœ í˜• ê°’ '%s'ì´(ê°€) ì§€ì›ë˜ì§€ 않ìŒ" msgid "Network type value needed by the ML2 plugin" msgstr "ML2 플러그ì¸ì— ë„¤íŠ¸ì›Œí¬ ìœ í˜• ê°’ì´ í•„ìš”í•¨" msgid "Network types supported by the agent (gre and/or vxlan)." msgstr "ì—ì´ì „트ì—서 ì§€ì›í•˜ëŠ” ë„¤íŠ¸ì›Œí¬ ìœ í˜•(gre ë°/ë˜ëŠ” vxlan)입니다." msgid "Neutron Service Type Management" msgstr "Neutron 서비스 유형 관리" msgid "Neutron core_plugin not configured!" msgstr "Neutron core_pluginì´ êµ¬ì„±ë˜ì§€ 않았습니다" msgid "No default router:external network" msgstr "기본 router:external 네트워í¬ê°€ ì—†ìŒ" #, python-format msgid "No default subnetpool found for IPv%s" msgstr "IPv%sì˜ ê¸°ë³¸ subnetpoolì´ ì—†ìŒ" msgid "No default subnetpools defined" msgstr "기본 subnetpoolsê°€ ì •ì˜ë˜ì§€ 않ìŒ" #, python-format msgid "No eligible l3 agent associated with external network %s found" msgstr "외부 ë„¤íŠ¸ì›Œí¬ %sê³¼(와) ì—°ê´€ëœ ì í•©í•œ l3 ì—ì´ì „트를 ì°¾ì„ ìˆ˜ ì—†ìŒ" #, python-format msgid "No more IP addresses available for subnet %(subnet_id)s." msgstr "서브넷 %(subnet_id)sì— ëŒ€í•´ 사용 가능한 IP 주소가 ë” ì´ìƒ 없습니다. " msgid "No more IP addresses available." msgstr "사용 가능한 IP 주소가 ë” ì´ìƒ 없습니다." msgid "No offline migrations pending." msgstr "보류 ì¤‘ì¸ ì˜¤í”„ë¼ì¸ 마ì´ê·¸ë ˆì´ì…˜ì´ 없습니다." #, python-format msgid "No shared key in %s fields" msgstr "%s í•„ë“œì˜ ê³µìœ  키가 ì—†ìŒ" msgid "Not allowed to manually assign a router to an agent in 'dvr' mode." msgstr "'dvr' 모드ì—서 수ë™ìœ¼ë¡œ ì—ì´ì „íŠ¸ì— ë¼ìš°í„°ë¥¼ 지정할 수 없습니다." msgid "Not allowed to manually remove a router from an agent in 'dvr' mode." msgstr "'dvr' 모드ì—서 수ë™ìœ¼ë¡œ ì—ì´ì „트ì—서 ë¼ìš°í„°ë¥¼ 제거할 수 없습니다." msgid "" "Number of DHCP agents scheduled to host a tenant network. If this number is " "greater than 1, the scheduler automatically assigns multiple DHCP agents for " "a given tenant network, providing high availability for DHCP service." msgstr "" "테넌트 네트워í¬ë¥¼ 호스팅하기 위해 ìŠ¤ì¼€ì¤„ëœ DHCP ì—ì´ì „íŠ¸ì˜ ìˆ˜ìž…ë‹ˆë‹¤. ì´ ìˆ«ìž" "ê°€ 1보다 í¬ë©´ 스케줄러는 ì§€ì •ëœ í…Œë„ŒíŠ¸ 네트워í¬ì— 대해 다중 DHCP ì—ì´ì „트를 " "ìžë™ìœ¼ë¡œ 지정하여 DHCP ì„œë¹„ìŠ¤ì— ëŒ€í•œ ê³ ê°€ìš©ì„±ì„ ì œê³µí•©ë‹ˆë‹¤. " msgid "Number of backlog requests to configure the metadata server socket with" msgstr "메타ë°ì´í„° 서버 ì†Œì¼“ì„ êµ¬ì„±í•˜ê¸° 위한 백로그 요청 수" msgid "Number of backlog requests to configure the socket with" msgstr "ì†Œì¼“ì„ ì„¤ì •í•˜ë ¤ëŠ” 백로그 요청 횟수" msgid "" "Number of bits in an ipv4 PTR zone that will be considered network prefix. " "It has to align to byte boundary. Minimum value is 8. Maximum value is 24. " "As a consequence, range of values is 8, 16 and 24" msgstr "" "ë„¤íŠ¸ì›Œí¬ ì ‘ë‘어로 ê³ ë ¤ë  ipv4 PTR êµ¬ì—­ì˜ ë¹„íŠ¸ 수입니다. ë°”ì´íЏ ê²½ê³„ì— ë§žê²Œ ì •" "렬해야 합니다. ìµœì†Œê°’ì€ 8ì´ê³  ìµœëŒ€ê°’ì€ 24입니다. ê²°ê³¼ì ìœ¼ë¡œ ê°’ì˜ ë²”ìœ„ëŠ” 8, " "16 ë° 24입니다." msgid "" "Number of bits in an ipv6 PTR zone that will be considered network prefix. " "It has to align to nyble boundary. Minimum value is 4. Maximum value is 124. " "As a consequence, range of values is 4, 8, 12, 16,..., 124" msgstr "" "ë„¤íŠ¸ì›Œí¬ ì ‘ë‘어로 ê³ ë ¤ë  ipv6 PTR êµ¬ì—­ì˜ ë¹„íŠ¸ 수입니다. nyble ê²½ê³„ì— ë§žê²Œ ì •" "렬해야 합니다. ìµœì†Œê°’ì€ 4ì´ê³  ìµœëŒ€ê°’ì€ 124입니다. ê²°ê³¼ì ìœ¼ë¡œ ê°’ì˜ ë²”ìœ„ëŠ” 4, " "8, 12, 16,..., 124입니다." msgid "" "Number of floating IPs allowed per tenant. A negative value means unlimited." msgstr "테넌트당 í—ˆìš©ëœ ë¶€ë™ IP 수입니다. ìŒìˆ˜ ê°’ì€ ë¬´ì œí•œì„ ì˜ë¯¸í•©ë‹ˆë‹¤." msgid "" "Number of networks allowed per tenant. A negative value means unlimited." msgstr "테넌트당 허용ë˜ëŠ” ë„¤íŠ¸ì›Œí¬ ìˆ˜ìž…ë‹ˆë‹¤. ìŒìˆ˜ ê°’ì€ ë¬´ì œí•œì„ ì˜ë¯¸í•©ë‹ˆë‹¤." msgid "Number of ports allowed per tenant. A negative value means unlimited." msgstr "테넌트당 허용ë˜ëŠ” í¬íЏ 수입니다. ìŒìˆ˜ ê°’ì€ ë¬´ì œí•œì„ ì˜ë¯¸í•©ë‹ˆë‹¤." msgid "Number of routers allowed per tenant. A negative value means unlimited." msgstr "테넌트당 í—ˆìš©ëœ ë¼ìš°í„° 수입니다. ìŒìˆ˜ ê°’ì€ ë¬´ì œí•œì„ ì˜ë¯¸í•©ë‹ˆë‹¤." msgid "" "Number of seconds between sending events to nova if there are any events to " "send." msgstr "보낼 ì´ë²¤íŠ¸ê°€ 있는 경우 novaì— ì „ì†¡í•˜ëŠ” ì´ë²¤íЏ ê°„ì˜ ì‹œê°„(ì´ˆ)입니다." msgid "Number of seconds to keep retrying to listen" msgstr "ê°ì²­ ìž¬ì‹œë„ ê³„ì†í•  시간" msgid "" "Number of security groups allowed per tenant. A negative value means " "unlimited." msgstr "테넌트당 í—ˆìš©ëœ ë³´ì•ˆ 그룹 수입니다. ìŒìˆ˜ ê°’ì€ ë¬´ì œí•œì„ ì˜ë¯¸í•©ë‹ˆë‹¤." msgid "" "Number of security rules allowed per tenant. A negative value means " "unlimited." msgstr "테넌트당 í—ˆìš©ëœ ë³´ì•ˆ 규칙 수입니다. ìŒìˆ˜ ê°’ì€ ë¬´ì œí•œì„ ì˜ë¯¸í•©ë‹ˆë‹¤." msgid "" "Number of separate API worker processes for service. If not specified, the " "default is equal to the number of CPUs available for best performance." msgstr "" "ì„œë¹„ìŠ¤ì— ëŒ€í•œ 별ë„ì˜ API ìž‘ì—…ìž í”„ë¡œì„¸ìŠ¤ 수입니다. 지정ë˜ì§€ ì•Šì€ ê²½ìš° 기본값" "ì€ ìµœì  ì„±ëŠ¥ì„ ìœ„í•´ 사용 가능한 CPU 수와 ë™ì¼í•©ë‹ˆë‹¤. " msgid "" "Number of separate worker processes for metadata server (defaults to half of " "the number of CPUs)" msgstr "" "메타ë°ì´í„° ì„œë²„ì— ëŒ€í•œ 별ë„ì˜ ìž‘ì—…ìž í”„ë¡œì„¸ìŠ¤ 수(ê¸°ë³¸ê°’ì€ CPU ìˆ˜ì˜ ì ˆë°˜ìœ¼ë¡œ " "지정ë¨)" msgid "Number of subnets allowed per tenant, A negative value means unlimited." msgstr "테넌트당 허용ë˜ëŠ” 서브넷 수입니다. ìŒìˆ˜ ê°’ì€ ë¬´ì œí•œì„ ì˜ë¯¸í•©ë‹ˆë‹¤." msgid "" "Number of threads to use during sync process. Should not exceed connection " "pool size configured on server." msgstr "" "ë™ê¸°í™” 프로세스 ì¤‘ì— ì‚¬ìš©í•  스레드 수입니다. ì„œë²„ì— êµ¬ì„±ëœ ì—°ê²° í’€ í¬ê¸°ë¥¼ ì´ˆ" "과하지 않아야 합니다." msgid "OK" msgstr "OK" msgid "" "OVS datapath to use. 'system' is the default value and corresponds to the " "kernel datapath. To enable the userspace datapath set this value to 'netdev'." msgstr "" "사용할 OVS ë°ì´í„° 경로입니다. '시스템'ì€ ê¸°ë³¸ê°’ì´ë©° ì»¤ë„ ë°ì´í„° ê²½ë¡œì— í•´ë‹¹í•©" "니다. ì‚¬ìš©ìž ê³µê°„ ë°ì´í„° 경로를 사용하려면 ì´ ê°’ì„ 'netdev'로 설정하십시오." msgid "OVS vhost-user socket directory." msgstr "OVS vhost-ì‚¬ìš©ìž ì†Œì¼“ 디렉토리." #, python-format msgid "Object action %(action)s failed because: %(reason)s." msgstr "%(action)s 오브ì íЏ 조치가 실패함. ì´ìœ : %(reason)s." msgid "Only admin can view or configure quota" msgstr "관리ìžë§Œì´ í• ë‹¹ëŸ‰ì„ ë³´ê±°ë‚˜ 구성할 수 있습니다. " msgid "Only admin is authorized to access quotas for another tenant" msgstr "관리ìžë§Œ 다른 í…Œë„ŒíŠ¸ì˜ í• ë‹¹ëŸ‰ì— ì•¡ì„¸ìŠ¤í•  수 있는 ê¶Œí•œì´ ìžˆìŒ" msgid "Only admins can manipulate policies on objects they do not own" msgstr "소유하지 ì•Šì€ ì˜¤ë¸Œì íŠ¸ì˜ ì •ì±…ì€ ê´€ë¦¬ìžë§Œ 조작할 수 있ìŒ" msgid "Only allowed to update rules for one security profile at a time" msgstr "한 ë²ˆì— í•˜ë‚˜ì˜ ë³´ì•ˆ 프로파ì¼ì— 대한 규칙만 ì—…ë°ì´íŠ¸í•˜ë„ë¡ í—ˆìš©ë¨" msgid "Only remote_ip_prefix or remote_group_id may be provided." msgstr "remote_ip_prefix ë˜ëŠ” remote_group_idë§Œì´ ì œê³µë  ìˆ˜ 있습니다. " msgid "OpenFlow interface to use." msgstr "사용할 OpenFlow ì¸í„°íŽ˜ì´ìŠ¤ìž…ë‹ˆë‹¤. " #, python-format msgid "" "Operation %(op)s is not supported for device_owner %(device_owner)s on port " "%(port_id)s." msgstr "" "ë‹¤ìŒ í¬íŠ¸ì˜ device_owner %(device_owner)sì— ëŒ€í•´ ì¡°ìž‘ %(op)sì´(ê°€) ì§€ì›ë˜ì§€ " "않ìŒ. í¬íЏ: %(port_id)s." #, python-format msgid "Operation not supported on device %(dev_name)s" msgstr "디바ì´ìФ %(dev_name)sì— ëŒ€í•œ ì¡°ìž‘ì´ ì§€ì›ë˜ì§€ 않ìŒ" msgid "" "Ordered list of network_types to allocate as tenant networks. The default " "value 'local' is useful for single-box testing but provides no connectivity " "between hosts." msgstr "" "테넌트 네트워í¬ë¡œ 할당할 network_typesì˜ ì •ë ¬ëœ ëª©ë¡ìž…니다. 기본값 'local'ì€ " "ë‹¨ì¼ ìƒìž í…ŒìŠ¤íŠ¸ì— ìœ ìš©í•˜ì§€ë§Œ 호스트 ê°„ ì—°ê²°ì„ ì œê³µí•˜ì§€ 않습니다." msgid "Override the default dnsmasq settings with this file." msgstr "기본 dnsmasq ì„¤ì •ì„ ì´ íŒŒì¼ë¡œ 대체합니다." msgid "Owner type of the device: network/compute" msgstr "디바ì´ìŠ¤ì˜ ì†Œìœ ìž ìœ í˜•: network/compute" msgid "POST requests are not supported on this resource." msgstr "ì´ ìžì›ì—서 POST ìš”ì²­ì´ ì§€ì›ë˜ì§€ 않습니다." #, python-format msgid "Package %s not installed" msgstr "패키지 %sì´(ê°€) 설치ë˜ì§€ 않ìŒ" #, python-format msgid "Parsing bridge_mappings failed: %s." msgstr "bridge_mappings 구문 ë¶„ì„ ì‹¤íŒ¨: %s." msgid "Password for connecting to designate in admin context" msgstr "관리 컨í…스트ì—서 지정하기 위해 ì—°ê²°í•  암호" msgid "Path to PID file for this process" msgstr "ì´ í”„ë¡œì„¸ìŠ¤ì— ëŒ€í•œ PID 파ì¼ì˜ 경로" msgid "Path to the router directory" msgstr "ë¼ìš°í„° ë””ë ‰í† ë¦¬ì˜ ê²½ë¡œ" msgid "Peer patch port in integration bridge for tunnel bridge." msgstr "í„°ë„ ë¸Œë¦¿ì§€ì— ëŒ€í•œ 통합 ë¸Œë¦¿ì§€ì— ìžˆëŠ” 피어 패치 í¬íŠ¸ìž…ë‹ˆë‹¤." msgid "Peer patch port in tunnel bridge for integration bridge." msgstr "통합 ë¸Œë¦¿ì§€ì— ëŒ€í•œ í„°ë„ ë¸Œë¦¿ì§€ì— ìžˆëŠ” 피어 패치 í¬íŠ¸ìž…ë‹ˆë‹¤." msgid "Per-tenant subnet pool prefix quota exceeded." msgstr "테넌트당 서브넷 í’€ ì ‘ë‘ë¶€ í• ë‹¹ëŸ‰ì´ ì´ˆê³¼ë˜ì—ˆìŠµë‹ˆë‹¤." msgid "Phase upgrade options do not accept revision specification" msgstr "단계 업그레ì´ë“œ ì˜µì…˜ì´ ê°œì • ìŠ¤íŽ™ì„ ìŠ¹ì¸í•˜ì§€ 않ìŒ" msgid "Ping timeout" msgstr "Ping 제한시간 초과" msgid "Plugin does not support updating provider attributes" msgstr "플러그ì¸ì´ ì œê³µìž ì†ì„± ì—…ë°ì´íŠ¸ë¥¼ ì§€ì›í•˜ì§€ 않ìŒ" #, python-format msgid "Port %(id)s does not have fixed ip %(address)s" msgstr "%(id)s í¬íŠ¸ê°€ ê³ ì • IP %(address)sì„(를) ê°–ì§€ 않ìŒ" #, python-format msgid "Port %(port_id)s is already acquired by another DHCP agent" msgstr "다른 DHCP ì—ì´ì „트ì—ê°€ ì´ë¯¸ í¬íЏ %(port_id)sì„(를) 확보했습니다." #, python-format msgid "" "Port %s has multiple fixed IPv4 addresses. Must provide a specific IPv4 " "address when assigning a floating IP" msgstr "" "í¬íЏ %sì— ë‹¤ì¤‘ ê³ ì • IPv4 주소가 있습니다. ë¶€ë™ IP를 지정하는 경우ì—는 특정 " "IPv4 주소를 제공해야 합니다. " msgid "" "Port to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "OpenFlow ì—°ê²°ì„ ìœ„í•´ 청취할 í¬íŠ¸ìž…ë‹ˆë‹¤. '네ì´í‹°ë¸Œ' 드ë¼ì´ë²„ì—ë§Œ 사용ë©ë‹ˆë‹¤. " #, python-format msgid "Prefix '%(prefix)s' not supported in IPv%(version)s pool." msgstr "IPv%(version)s í’€ì—서 ì ‘ë‘ë¶€ '%(prefix)s'ì´(ê°€) ì§€ì›ë˜ì§€ 않습니다." msgid "Prefix Delegation can only be used with IPv6 subnets." msgstr "ì ‘ë‘ë¶€ ìœ„ìž„ì€ IPv6 서브넷ì—ë§Œ 사용할 수 있습니다. " msgid "Private key of client certificate." msgstr "í´ë¼ì´ì–¸íЏ ì¸ì¦ì„œì˜ ê°œì¸ í‚¤ìž…ë‹ˆë‹¤." #, python-format msgid "Probe %s deleted" msgstr "%s 프로브가 ì‚­ì œë˜ì—ˆìŒ" #, python-format msgid "Probe created : %s " msgstr "프로브 작성: %s " msgid "Process is already started" msgstr "프로세스가 ì´ë¯¸ 시작ë¨" msgid "Process is not running." msgstr "프로세스가 실행 중ì´ì§€ 않습니다." msgid "Protocol to access nova metadata, http or https" msgstr "nova 메타ë°ì´í„°ì— 액세스하기 위한 프로토콜, http ë˜ëŠ” https" #, python-format msgid "Provider name %(name)s is limited by %(len)s characters" msgstr "ì œê³µìž ì´ë¦„ %(name)sì€(는) %(len)sìžë¡œ 제한ë¨" #, python-format msgid "QoS Policy %(policy_id)s is used by %(object_type)s %(object_id)s." msgstr "" "QoS ì •ì±… %(policy_id)sì´(ê°€) %(object_type)s %(object_id)sì— ì˜í•´ 사용ë©ë‹ˆ" "다. " #, python-format msgid "" "QoS binding for network %(net_id)s and policy %(policy_id)s could not be " "found." msgstr "" "ë„¤íŠ¸ì›Œí¬ %(net_id)s ë° ì •ì±… %(policy_id)sì˜ QoS ë°”ì¸ë”©ì„ ì°¾ì„ ìˆ˜ 없습니다." #, python-format msgid "" "QoS binding for port %(port_id)s and policy %(policy_id)s could not be found." msgstr "" "í¬íЏ %(port_id)s ë° ì •ì±… %(policy_id)sì˜ QoS ë°”ì¸ë”©ì„ ì°¾ì„ ìˆ˜ 없습니다." #, python-format msgid "QoS policy %(policy_id)s could not be found." msgstr "QoS ì •ì±… %(policy_id)sì„(를) ì°¾ì„ ìˆ˜ 없습니다." #, python-format msgid "QoS rule %(rule_id)s for policy %(policy_id)s could not be found." msgstr "ì •ì±… %(policy_id)sì˜ QoS 규칙 %(rule_id)sì„(를) ì°¾ì„ ìˆ˜ 없습니다." #, python-format msgid "RBAC policy of type %(object_type)s with ID %(id)s not found" msgstr "IDê°€ %(id)sì¸ %(object_type)s ìœ í˜•ì˜ RBAC ì •ì±…ì„ ì°¾ì„ ìˆ˜ ì—†ìŒ" #, python-format msgid "" "RBAC policy on object %(object_id)s cannot be removed because other objects " "depend on it.\n" "Details: %(details)s" msgstr "" "다른 오브ì íŠ¸ê°€ ì˜ì¡´í•˜ê³  있기 ë•Œë¬¸ì— ì˜¤ë¸Œì íЏ %(object_id)sì— ëŒ€í•œ RBAC ì •ì±…" "ì„ ì œê±°í•  수 없습니다. \n" "세부사항: %(details)s" msgid "" "Range of seconds to randomly delay when starting the periodic task scheduler " "to reduce stampeding. (Disable by setting to 0)" msgstr "" "몰리지 않ë„ë¡ ì£¼ê¸°ì  íƒœìŠ¤í¬ ìŠ¤ì¼€ì¤„ëŸ¬ë¥¼ 시작할 때 무작위로 지연할 ì‹œê°„ì˜ ë²”ìœ„" "(ì´ˆ)입니다. (0으로 설정하여 사용 안함) " msgid "Ranges must be in the same IP version" msgstr "범위가 ë™ì¼í•œ IP ë²„ì „ì— ìžˆì–´ì•¼ 함" msgid "Ranges must be netaddr.IPRange" msgstr "범위는 netaddr.IPRange여야 함" msgid "Ranges must not overlap" msgstr "범위는 중첩ë˜ì§€ 않아야 함" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.EUI type." msgstr "" "유형 '%(type)s' ë° ê°’ '%(value)s'ì„(를) 수신했습니다. netaddr.EUI ìœ í˜•ì´ í•„ìš”" "합니다." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPAddress " "type." msgstr "" "유형 '%(type)s' ë° ê°’ '%(value)s'ì„(를) 수신했습니다. netaddr.IPAddress 유형" "ì´ í•„ìš”í•©ë‹ˆë‹¤." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPNetwork " "type." msgstr "" "유형 '%(type)s' ë° ê°’ '%(value)s'ì„(를) 수신했습니다. netaddr.IPNetwork 유형" "ì´ í•„ìš”í•©ë‹ˆë‹¤." #, python-format msgid "" "Release aware branch labels (%s) are deprecated. Please switch to expand@ " "and contract@ labels." msgstr "" "분기 ë ˆì´ë¸”(%s)ì€ ë” ì´ìƒ 사용ë˜ì§€ 않습니다. expand@ ë° contract@ ë ˆì´ë¸”로 ì „" "환하십시오. " msgid "Remote metadata server experienced an internal server error." msgstr "ì›ê²© 메타ë°ì´í„° 서버ì—서 ë‚´ë¶€ 서버 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. " msgid "" "Repository does not contain HEAD files for contract and expand branches." msgstr "ì €ìž¥ì†Œì— ê³„ì•½ ë° í™•ìž¥ ë¶„ê¸°ì˜ HEAD 파ì¼ì´ í¬í•¨ë˜ì§€ 않습니다." msgid "" "Representing the resource type whose load is being reported by the agent. " "This can be \"networks\", \"subnets\" or \"ports\". When specified (Default " "is networks), the server will extract particular load sent as part of its " "agent configuration object from the agent report state, which is the number " "of resources being consumed, at every report_interval.dhcp_load_type can be " "used in combination with network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler When the network_scheduler_driver is " "WeightScheduler, dhcp_load_type can be configured to represent the choice " "for the resource being balanced. Example: dhcp_load_type=networks" msgstr "" "ì—ì´ì „트ì—서 로드를 보고하는 ìžì› ìœ í˜•ì„ ë‚˜íƒ€ëƒ…ë‹ˆë‹¤. ì´ëŠ” \"네트워í¬\", \"서" "브넷\" ë˜ëŠ” \"í¬íЏ\"입니다. ì´ë¥¼ 지정하는 경우 (ê¸°ë³¸ê°’ì€ ë„¤íŠ¸ì›Œí¬ìž„) 서버는 " "network_scheduler_driver = neutron.scheduler.dhcp_agent_scheduler." "WeightSchedulerì™€ì˜ ì¡°í•©ì—서 report_interval.dhcp_load_typeì„ ì‚¬ìš©í•  수 ìžˆì„ " "때마다 ì—ì´ì „트 ë³´ê³  ìƒíƒœì—서 ì—ì´ì „트 구성 오브ì íŠ¸ì˜ ì¼ë¶€ë¡œ 보낸 특정 로드" "를 추출하는ë°, ì´ëŠ” ì´ìš© ì¤‘ì¸ ìžì› 수입니다. network_scheduler_driverê°€ " "WeightSchedulerì¸ ê²½ìš° dhcp_load_typeì„ êµ¬ì„±í•˜ì—¬ 밸런스 ì¡°ì • ì¤‘ì¸ ìžì›ì— 대" "한 ì„ íƒì„ 표시할 수 있습니다. 예: dhcp_load_type=networks" msgid "Request Failed: internal server error while processing your request." msgstr "요청 실패: ìš”ì²­ì„ ì²˜ë¦¬í•˜ëŠ” ì¤‘ì— ë‚´ë¶€ 서버 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. " msgid "" "Reset flow table on start. Setting this to True will cause brief traffic " "interruption." msgstr "" "시작 시 플로우 í…Œì´ë¸”ì„ ìž¬ì„¤ì •í•˜ì‹­ì‹œì˜¤. ì´ë¥¼ True로 설정하면 ì§§ì€ íŠ¸ëž˜í”½ ì¸í„°" "럽트가 ë°œìƒí•©ë‹ˆë‹¤. " #, python-format msgid "Resource %(resource)s %(resource_id)s could not be found." msgstr "ìžì› %(resource)s %(resource_id)sì„(를) ì°¾ì„ ìˆ˜ 없습니다." #, python-format msgid "Resource %(resource_id)s of type %(resource_type)s not found" msgstr "유형 %(resource_type)sì˜ ìžì› %(resource_id)sì„(를) ì°¾ì„ ìˆ˜ ì—†ìŒ" #, python-format msgid "" "Resource '%(resource_id)s' is already associated with provider " "'%(provider)s' for service type '%(service_type)s'" msgstr "" "'%(resource_id)s' ìžì›ì´ ì´ë¯¸ '%(service_type)s' 서비스 ìœ í˜•ì— ëŒ€í•œ " "'%(provider)s' 제공ìžì™€ ì—°ê´€ë˜ì–´ 있ìŒ" msgid "Resource body required" msgstr "ìžì› 본문 필수" msgid "Resource not found." msgstr "ìžì›ì„ ì°¾ì„ ìˆ˜ 없습니다." msgid "Resources required" msgstr "ìžì› 필수" msgid "" "Root helper application. Use 'sudo neutron-rootwrap /etc/neutron/rootwrap." "conf' to use the real root filter facility. Change to 'sudo' to skip the " "filtering and just run the command directly." msgstr "" "루트 í—¬í¼ ì• í”Œë¦¬ì¼€ì´ì…˜. 'sudo neutron-rootwrap /etc/neutron/rootwrap.conf'를 " "사용하여 실제 루트 í•„í„° ê¸°ëŠ¥ì„ ì‚¬ìš©í•©ë‹ˆë‹¤. 'sudo'로 변경하여 í•„í„°ë§ì„ 건너뛰" "ê³  ëª…ë ¹ì„ ì§ì ‘ 실행하기만 하면 ë©ë‹ˆë‹¤." msgid "Root permissions are required to drop privileges." msgstr "ê¶Œí•œì„ ì‚­ì œí•˜ë ¤ë©´ 루트 ê¶Œí•œì´ í•„ìš”í•©ë‹ˆë‹¤." #, python-format msgid "Router '%(router_id)s' is not compatible with this agent." msgstr "ë¼ìš°í„° '%(router_id)s'ì´(ê°€) ì´ ì—ì´ì „트와 호환ë˜ì§€ 않습니다." #, python-format msgid "Router already has a port on subnet %s" msgstr "ë¼ìš°í„°ê°€ ì´ë¯¸ %s ì„œë¸Œë„·ì— í¬íŠ¸ë¥¼ ê°–ê³  있ìŒ" msgid "Router port must have at least one fixed IP" msgstr "ë¼ìš°í„° í¬íЏì—는 하나 ì´ìƒì˜ Fixed IPê°€ 있어야 함" #, python-format msgid "Running %(cmd)s (%(desc)s) for %(project)s ..." msgstr "%(project)sì— ëŒ€í•œ %(cmd)s(%(desc)s) 실행 중..." #, python-format msgid "Running %(cmd)s for %(project)s ..." msgstr "%(project)sì— ëŒ€í•œ %(cmd)s 실행 중..." msgid "" "Seconds between nodes reporting state to server; should be less than " "agent_down_time, best if it is half or less than agent_down_time." msgstr "" "ì„œë²„ì— ëŒ€í•œ ìƒíƒœë¥¼ 보고하는 노드 사ì´ì˜ 시간(ì´ˆ)ì´ë©° agent_down_time보다 ì§§ì•„" "야 하며 절반ì´ê±°ë‚˜ agent_down_time보다 ì§§ì€ ê²½ìš° 최ì ìž…니다." msgid "" "Seconds to regard the agent is down; should be at least twice " "report_interval, to be sure the agent is down for good." msgstr "" "ì—ì´ì „트가 ìž‘ë™ ì¤‘ì§€ë˜ì—ˆë‹¤ê³  간주ë˜ëŠ” 시간(ì´ˆ)ì´ë©° ì—ì´ì „트가 ê³„ì† ìž‘ë™ ì¤‘ì§€ " "ìƒíƒœì¸ì§€ 확ì¸í•  수 있ë„ë¡ report_intervalì˜ ë‘ ë°° ì´ìƒì´ì–´ì•¼ 합니다." #, python-format msgid "Security Group %(id)s %(reason)s." msgstr "보안 그룹 %(id)s %(reason)s입니다. " #, python-format msgid "Security Group Rule %(id)s %(reason)s." msgstr "보안 그룹 규칙 %(id)s %(reason)s." #, python-format msgid "Security group %(id)s does not exist" msgstr "%(id)s 보안 ê·¸ë£¹ì´ ì¡´ìž¬í•˜ì§€ 않ìŒ" #, python-format msgid "Security group rule %(id)s does not exist" msgstr "보안 그룹 규칙 %(id)sì´(ê°€) 존재하지 않ìŒ" #, python-format msgid "Security group rule already exists. Rule id is %(rule_id)s." msgstr "보안 그룹 ê·œì¹™ì´ ì´ë¯¸ 있습니다. 규칙 ID는 %(rule_id)s입니다." #, python-format msgid "" "Security group rule for ethertype '%(ethertype)s' not supported. Allowed " "values are %(values)s." msgstr "" "ethertype '%(ethertype)s'ì˜ ë³´ì•ˆ 그룹 ê·œì¹™ì´ ì§€ì›ë˜ì§€ 않습니다. í—ˆìš©ëœ ê°’ì€ " "%(values)s입니다." #, python-format msgid "" "Security group rule protocol %(protocol)s not supported. Only protocol " "values %(values)s and integer representations [0 to 255] are supported." msgstr "" "보안 그룹 규칙 프로토콜 %(protocol)sì´(ê°€) ì§€ì›ë˜ì§€ 않습니다. 프로토콜 ê°’ " "%(values)s ë° ì •ìˆ˜ 표시 [0 - 255]ë§Œ ì§€ì›ë©ë‹ˆë‹¤. " msgid "Segments and provider values cannot both be set." msgstr "세그먼트 ë° ì œê³µìž ê°’ì„ ëª¨ë‘ ì„¤ì •í•  수 없습니다." msgid "Selects the Agent Type reported" msgstr "ë³´ê³ ëœ ì—ì´ì „트 ìœ í˜•ì„ ì„ íƒí•¨" msgid "" "Send notification to nova when port data (fixed_ips/floatingip) changes so " "nova can update its cache." msgstr "" "í¬íЏ ë°ì´í„°(fixed_ips/floatingip)ê°€ 변경ë˜ë©´ ì•Œë¦¼ì„ novaì— ë³´ëƒ…ë‹ˆë‹¤. ì´ì— ë”°" "ë¼ nova는 해당 ìºì‹œë¥¼ ì—…ë°ì´íŠ¸í•  수 있습니다." msgid "Send notification to nova when port status changes" msgstr "í¬íЏ ìƒíƒœê°€ 변경ë˜ë©´ ì•Œë¦¼ì„ novaì— ë³´ëƒ…ë‹ˆë‹¤." #, python-format msgid "" "Service provider '%(provider)s' could not be found for service type " "%(service_type)s" msgstr "" "서비스 ìœ í˜•ì— ëŒ€í•œ '%(provider)s' 서비스 제공ìžë¥¼ ì°¾ì„ ìˆ˜ ì—†ìŒ: " "%(service_type)s" msgid "Service to handle DHCPv6 Prefix delegation." msgstr "DHCPv6 ì ‘ë‘ë¶€ ìœ„ìž„ì„ ì²˜ë¦¬í•  서비스입니다. " #, python-format msgid "Service type %(service_type)s does not have a default service provider" msgstr "%(service_type)s 서비스 ìœ í˜•ì— ê¸°ë³¸ 서비스 제공ìžê°€ ì—†ìŒ" msgid "" "Set new timeout in seconds for new rpc calls after agent receives SIGTERM. " "If value is set to 0, rpc timeout won't be changed" msgstr "" "ì—ì´ì „트ì—서 SIGTERMì„ ìˆ˜ì‹ í•œ í›„ì— ìƒˆ rpc í˜¸ì¶œì— ëŒ€í•œ 새 제한시간(ì´ˆ)ì„ ì„¤ì •" "합니다. ê°’ì„ 0으로 설정하면 rpc ì œí•œì‹œê°„ì´ ë³€ê²½ë˜ì§€ 않습니다." msgid "" "Set or un-set the don't fragment (DF) bit on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "GRE/VXLAN í„°ë„ì„ ì „ì†¡í•˜ëŠ” 발신 IP íŒ¨í‚·ì— DF(Don't Fragment) 비트를 설정하거" "나 설정 해제하십시오." msgid "" "Set or un-set the tunnel header checksum on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "GRE/VXLAN í„°ë„ì„ ì „ë‹¬í•˜ëŠ” 발신 IP 패킷ì—서 í„°ë„ í—¤ë” ì²´í¬ì„¬ì„ 설정하거나 설" "ì • 해제하십시오. " msgid "Shared address scope can't be unshared" msgstr "공유 주소 범위는 공유 해제할 수 ì—†ìŒ" msgid "String prefix used to match IPset names." msgstr "IPset ì´ë¦„ì„ ì¼ì¹˜ì‹œí‚¤ëŠ” ë° ì‚¬ìš©ë˜ëŠ” 문ìžì—´ ì ‘ë‘부입니다. " #, python-format msgid "Sub-project %s not installed." msgstr "하위 프로ì íЏ %sì´(ê°€) 설치ë˜ì§€ 않았습니다. " msgid "Subnet for router interface must have a gateway IP" msgstr "ë¼ìš°í„° ì¸í„°íŽ˜ì´ìŠ¤ì— ëŒ€í•œ ì„œë¸Œë„·ì€ ê²Œì´íŠ¸ì›¨ì´ IP를 가져야 함" #, python-format msgid "Subnet pool %(subnetpool_id)s could not be found." msgstr "서브넷 í’€ %(subnetpool_id)sì„(를) ì°¾ì„ ìˆ˜ 없습니다." msgid "Subnet pool has existing allocations" msgstr "서브넷 í’€ì— ê¸°ì¡´ í• ë‹¹ì´ ìžˆìŒ" msgid "Subnet used for the l3 HA admin network." msgstr "l3 HA 관리 네트워í¬ì— ì‚¬ìš©ëœ ì„œë¸Œë„·ìž…ë‹ˆë‹¤." msgid "" "Subnets hosted on the same network must be allocated from the same subnet " "pool." msgstr "" "ë™ì¼í•œ 네트워í¬ì—서 호스트ë˜ëŠ” ì„œë¸Œë„·ì„ ë™ì¼í•œ 서브넷 í’€ì—서 할당해야 합니다." msgid "" "System-wide flag to determine the type of router that tenants can create. " "Only admin can override." msgstr "" "테넌트가 작성할 수 있는 ë¼ìš°í„° ìœ í˜•ì„ íŒë³„하는 시스템 범위 플래그입니다. 관리" "ìžë§Œ 대체할 수 있습니다." msgid "TCP Port used by Neutron metadata namespace proxy." msgstr "Neutron 메타ë°ì´í„° 네임스페ì´ìФ 프ë¡ì‹œê°€ 사용하는 TCP í¬íŠ¸ìž…ë‹ˆë‹¤. " msgid "TCP Port used by Nova metadata server." msgstr "Nova 메타ë°ì´í„° 서버가 사용한 TCP í¬íŠ¸ìž…ë‹ˆë‹¤. " msgid "TTL for vxlan interface protocol packets." msgstr "vxlan ì¸í„°íŽ˜ì´ìФ 프로토콜 패킷용 TTL." #, python-format msgid "Tag %(tag)s could not be found." msgstr "%(tag)s 태그를 ì°¾ì„ ìˆ˜ 없습니다." #, python-format msgid "Tenant %(tenant_id)s not allowed to create %(resource)s on this network" msgstr "" "%(tenant_id)s 테넌트는 ì´ ë„¤íŠ¸ì›Œí¬ì— %(resource)sì„(를) 작성하ë„ë¡ í—ˆìš©ë˜ì§€ " "않ìŒ" msgid "Tenant id for connecting to designate in admin context" msgstr "관리 컨í…스트ì—서 지정하기 위해 ì—°ê²°í•  테넌트 ID" msgid "Tenant name for connecting to designate in admin context" msgstr "관리 컨í…스트ì—서 지정하기 위해 ì—°ê²°í•  테넌트 ì´ë¦„" msgid "Tenant network creation is not enabled." msgstr "테넌트 ë„¤íŠ¸ì›Œí¬ ìž‘ì„±ì€ ì‚¬ìš©ë˜ì§€ 않습니다. " msgid "Tenant-id was missing from quota request." msgstr "테넌트 IDê°€ 할당량 요청ì—서 누ë½ë˜ì—ˆìŠµë‹ˆë‹¤. " msgid "" "The 'gateway_external_network_id' option must be configured for this agent " "as Neutron has more than one external network." msgstr "" "'gateway_external_network_id' ì˜µì…˜ì€ Neutronì´ ë‘ ê°œ ì´ìƒì˜ 외부 네트워í¬ë¥¼ " "가지므로 ì´ ì—ì´ì „íŠ¸ì— ëŒ€í•´ 구성ë˜ì–´ì•¼ 합니다. " msgid "" "The DHCP agent will resync its state with Neutron to recover from any " "transient notification or RPC errors. The interval is number of seconds " "between attempts." msgstr "" "DHCP ì—ì´ì „트가 임시 알림 ë˜ëŠ” RPC 오류로부터 복구하기 위해 해당 ìƒíƒœë¥¼ " "Neutronê³¼ 다시 ë™ê¸°í™”합니다. ê°„ê²©ì€ ì‹œë„ ì‚¬ì´ì˜ ì´ˆ 수입니다." msgid "" "The DHCP server can assist with providing metadata support on isolated " "networks. Setting this value to True will cause the DHCP server to append " "specific host routes to the DHCP request. The metadata service will only be " "activated when the subnet does not contain any router port. The guest " "instance must be configured to request host routes via DHCP (Option 121). " "This option doesn't have any effect when force_metadata is set to True." msgstr "" "DHCP 서버는 ê²©ë¦¬ëœ ë„¤íŠ¸ì›Œí¬ì—서 메타ë°ì´í„° ì§€ì›ì„ 제공하ë„ë¡ ì§€ì›í•  수 있습니" "다. ì´ ê°’ì„ True로 설정하면 DHCP 서버가 특정 호스트 경로를 DHCP ìš”ì²­ì— ì¶”ê°€í•©" "니다. 메타ë°ì´í„° 서비스는 ì„œë¸Œë„·ì— ë¼ìš°í„° í¬íŠ¸ê°€ í¬í•¨ë˜ì§€ ì•Šì€ ê²½ìš°ì—ë§Œ 활성" "í™”ë©ë‹ˆë‹¤. DHCP를 통해 호스트 경로를 요청하려면 게스트 ì¸ìŠ¤í„´ìŠ¤ê°€ 구성ë˜ì–´ì•¼ " "합니다(옵션 121). ì´ ì˜µì…˜ì€ force_metadataê°€ True로 ì„¤ì •ëœ ê²½ìš° ì ìš©ë˜ì§€ 않습" "니다." msgid "The UDP port to use for VXLAN tunnels." msgstr "VXLAN í„°ë„ì— ì‚¬ìš©í•˜ëŠ” UDP í¬íЏ" #, python-format msgid "" "The address allocation request could not be satisfied because: %(reason)s" msgstr "ë‹¤ìŒ ì›ì¸ìœ¼ë¡œ ì¸í•´ 주소 할당 ìš”ì²­ì„ ì¶©ì¡±í•  수 ì—†ìŒ: %(reason)s" msgid "The advertisement interval in seconds" msgstr "ê´‘ê³  간격(ì´ˆ)" #, python-format msgid "The allocation pool %(pool)s is not valid." msgstr "할당 í’€ %(pool)sì´(ê°€) 올바르지 않습니다. " #, python-format msgid "" "The allocation pool %(pool)s spans beyond the subnet cidr %(subnet_cidr)s." msgstr "" "할당 í’€ %(pool)sì´(ê°€) 서브넷 cidr %(subnet_cidr)s ì´ìƒìœ¼ë¡œ 확장합니다. " msgid "" "The base MAC address Neutron will use for VIFs. The first 3 octets will " "remain unchanged. If the 4th octet is not 00, it will also be used. The " "others will be randomly generated." msgstr "" "VIFì—기본 MAC 주소 Neutronì„ ì‚¬ìš©í•©ë‹ˆë‹¤. ì²˜ìŒ ì„¸ ê°œì˜ ì˜¥í…Ÿì€ ë³€ê²½ë˜ì§€ ì•Šì€ ìƒ" "태로 남습니다. 네 번째 ì˜¥í…Ÿì´ 00ì´ ì•„ë‹ˆë©´ ì´ ì˜¥í…Ÿë„ ì‚¬ìš©ë©ë‹ˆë‹¤. 다른 ì˜¥í…Ÿì€ " "ìž„ì˜ë¡œ ìƒì„±ë©ë‹ˆë‹¤." msgid "" "The base mac address used for unique DVR instances by Neutron. The first 3 " "octets will remain unchanged. If the 4th octet is not 00, it will also be " "used. The others will be randomly generated. The 'dvr_base_mac' *must* be " "different from 'base_mac' to avoid mixing them up with MAC's allocated for " "tenant ports. A 4 octet example would be dvr_base_mac = fa:16:3f:4f:00:00. " "The default is 3 octet" msgstr "" "Neutronì— ì˜í•´ 고유 DVR ì¸ìŠ¤í„´ìŠ¤ì— ì‚¬ìš©ë˜ëŠ” 기본 mac 주소입니다. ì²˜ìŒ ì„¸ ê°œ " "ì˜¥í…Ÿì€ ë³€ê²½ë˜ì§€ 않고 남아 있습니다. 네 번째 ì˜¥í…Ÿì´ 00ì´ ì•„ë‹ˆë©´ ì´ ì˜¥í…Ÿë„ ì‚¬ìš©" "ë©ë‹ˆë‹¤. 다른 ì˜¥í…Ÿì€ ë¬´ìž‘ìœ„ë¡œ ìƒì„±ë©ë‹ˆë‹¤. 테넌트 í¬íŠ¸ì— ëŒ€í•´ í• ë‹¹ëœ MACê³¼ì˜ í˜¼" "í•©ì„ ë°©ì§€í•˜ê¸° 위해 'dvr_base_mac'ì€ 'base_mac'ê³¼ 달ë¼ì•¼ *합니다*. 4 옥텟 예제" "는 dvr_base_mac = fa:16:3f:4f:00:00입니다. ê¸°ë³¸ê°’ì€ 3 옥텟입니다. " msgid "The core plugin Neutron will use" msgstr "Neutronì´ ì‚¬ìš©í•  코어 플러그ì¸" msgid "The driver used to manage the DHCP server." msgstr "DHCP 서버를 관리하는 ë° ì‚¬ìš©ë˜ëŠ” 드ë¼ì´ë²„입니다. " msgid "The driver used to manage the virtual interface." msgstr "ê°€ìƒ ì¸í„°íŽ˜ì´ìŠ¤ë¥¼ 관리하는 ë° ì‚¬ìš©ë˜ëŠ” 드ë¼ì´ë²„입니다. " msgid "" "The email address to be used when creating PTR zones. If not specified, the " "email address will be admin@" msgstr "" "PTR êµ¬ì—­ì„ ìž‘ì„±í•  때 사용할 ì´ë©”ì¼ ì£¼ì†Œìž…ë‹ˆë‹¤. 지정ë˜ì§€ ì•Šì€ ê²½ìš° ì´ë©”ì¼ ì£¼ì†Œ" "는 admin@입니다." #, python-format msgid "" "The following device_id %(device_id)s is not owned by your tenant or matches " "another tenants router." msgstr "" "device_id %(device_id)sì´(ê°€) ì‚¬ìš©ìž í…Œë„ŒíŠ¸ì˜ ì†Œìœ ê°€ 아니거나 다른 테넌트 ë¼" "우터와 ì¼ì¹˜í•©ë‹ˆë‹¤." msgid "The interface for interacting with the OVSDB" msgstr "OVSDB와 ìƒí˜¸ìž‘용하는 ë° í•„ìš”í•œ ì¸í„°íŽ˜ì´ìФ" msgid "" "The maximum number of items returned in a single response, value was " "'infinite' or negative integer means no limit" msgstr "" "ë‹¨ì¼ ì‘답으로 최대 항목 수가 리턴ë˜ì—ˆìŠµë‹ˆë‹¤. ê°’ì´ 'infinite' ë˜ëŠ” ìŒìˆ˜ì¸ ê²½" "ìš° ì œí•œì´ ì—†ë‹¤ëŠ” ì˜ë¯¸ìž…니다. " #, python-format msgid "" "The network %(network_id)s has been already hosted by the DHCP Agent " "%(agent_id)s." msgstr "" "DHCP ì—ì´ì „트 %(agent_id)sì—서 %(network_id)s 네트워í¬ë¥¼ ì´ë¯¸ 호스트하고 있습" "니다. " #, python-format msgid "" "The network %(network_id)s is not hosted by the DHCP agent %(agent_id)s." msgstr "" "DHCP ì—ì´ì „트 %(agent_id)sì—서 %(network_id)s 네트워í¬ë¥¼ 호스트하지 않습니" "다. " msgid "" "The network type to use when creating the HA network for an HA router. By " "default or if empty, the first 'tenant_network_types' is used. This is " "helpful when the VRRP traffic should use a specific network which is not the " "default one." msgstr "" "HA ë¼ìš°í„°ì— 대한 HA ë„¤íŠ¸ì›Œí¬ ìž‘ì„± 시 사용할 ë„¤íŠ¸ì›Œí¬ ìœ í˜•ìž…ë‹ˆë‹¤. 기본ì ìœ¼ë¡œ " "ë˜ëŠ” 비어 있는 경우 첫 번째 'tenant_network_types'ê°€ 사용ë©ë‹ˆë‹¤. ì´ëŠ” VRRP 트" "ëž˜í”½ì´ ê¸°ë³¸ê°’ì´ ì•„ë‹Œ 특정 네트워í¬ë¥¼ 사용해야 하는 ê²½ìš°ì— ìœ ìš©í•©ë‹ˆë‹¤. " msgid "" "The number of seconds the agent will wait between polling for local device " "changes." msgstr "ì—ì´ì „트가 로컬 디바ì´ìФ ë³€ê²½ì„ í´ë§í•˜ëŠ” 사ì´ì— 대기하는 시간(ì´ˆ). " msgid "" "The number of seconds to wait before respawning the ovsdb monitor after " "losing communication with it." msgstr "" "í†µì‹ ì´ ìœ ì‹¤ëœ í›„ì— ovsdb 모니터를 재파ìƒí•˜ê¸° ì „ì— ëŒ€ê¸°í•  시간(ì´ˆ)입니다." msgid "The number of sort_keys and sort_dirs must be same" msgstr "sort_keys ë° sort_dirsì˜ ìˆ˜ê°€ 같아야 함" msgid "" "The path for API extensions. Note that this can be a colon-separated list of " "paths. For example: api_extensions_path = extensions:/path/to/more/exts:/" "even/more/exts. The __path__ of neutron.extensions is appended to this, so " "if your extensions are in there you don't need to specify them here." msgstr "" "API í™•ìž¥ì˜ ê²½ë¡œìž…ë‹ˆë‹¤. ì´ ê²½ë¡œëŠ” 콜론으로 êµ¬ë¶„ëœ ê²½ë¡œ 목ë¡ì¼ 수 있습니다. " "예: api_extensions_path = extensions:/path/to/more/exts:/even/more/exts. " "neutron.extensionsì˜ __path__ê°€ ì´ ê²½ë¡œì— ì¶”ê°€ë˜ë¯€ë¡œ, 해당 ìœ„ì¹˜ì— í™•ìž¥ì´ ìžˆìœ¼" "ë©´ ì—¬ê¸°ì— ì§€ì •í•˜ì§€ ì•Šì•„ë„ ë©ë‹ˆë‹¤." msgid "The physical network name with which the HA network can be created." msgstr "HA 네트워í¬ë¥¼ 작성하는 ë° ì‚¬ìš©í•  수 있는 실제 ë„¤íŠ¸ì›Œí¬ ì´ë¦„입니다. " #, python-format msgid "The port '%s' was deleted" msgstr "í¬íЏ '%s'ì´(ê°€) ì‚­ì œë¨" msgid "The port to bind to" msgstr "ë°”ì¸ë“œí•  í¬íЏ" #, python-format msgid "The requested content type %s is invalid." msgstr "요청한 컨í…츠 유형 %sì´(ê°€) 올바르지 않습니다." msgid "The resource could not be found." msgstr "ìžì›ì„ ì°¾ì„ ìˆ˜ 없습니다. " #, python-format msgid "" "The router %(router_id)s has been already hosted by the L3 Agent " "%(agent_id)s." msgstr "" "L3 ì—ì´ì „트 %(agent_id)sì—서 %(router_id)s ë¼ìš°í„°ë¥¼ ì´ë¯¸ 호스트하고 있습니" "다. " msgid "" "The server has either erred or is incapable of performing the requested " "operation." msgstr "ì„œë²„ì— ì˜¤ë¥˜ê°€ 있거나 서버가 ìš”ì²­ëœ ì¡°ìž‘ì„ ìˆ˜í–‰í•  수 없습니다." msgid "The service plugins Neutron will use" msgstr "Neutronì´ ì‚¬ìš©í•  서비스 플러그ì¸" #, python-format msgid "The subnet request could not be satisfied because: %(reason)s" msgstr "ë‹¤ìŒ ì´ìœ ë¡œ ì¸í•´ 서브넷 ìš”ì²­ì„ ì¶©ì¡±í•  수 ì—†ìŒ: %(reason)s" #, python-format msgid "The subproject to execute the command against. Can be one of: '%s'." msgstr "ëª…ë ¹ì„ ì‹¤í–‰í•  하위 프로ì íŠ¸ìž…ë‹ˆë‹¤. ë‹¤ìŒ ì¤‘ 하나가 ë  ìˆ˜ 있ìŒ: '%s'." msgid "The type of authentication to use" msgstr "사용할 ì¸ì¦ 유형" msgid "" "There are routers attached to this network that depend on this policy for " "access." msgstr "" "ì´ ë„¤íŠ¸ì›Œí¬ì— ì—°ê²°ëœ ë¼ìš°í„°ê°€ 있으며, 해당 ë¼ìš°í„°ì—는 액세스를 위해 ì´ ì •ì±…" "ì´ í•„ìš”í•©ë‹ˆë‹¤." msgid "" "Timeout in seconds to wait for a single OpenFlow request. Used only for " "'native' driver." msgstr "" "ë‹¨ì¼ OpenFlow ìš”ì²­ì„ ê¸°ë‹¤ë¦¬ëŠ” 제한시간(ì´ˆ)입니다. '네ì´í‹°ë¸Œ' 드ë¼ì´ë²„ì—ë§Œ 사" "ìš©ë©ë‹ˆë‹¤. " msgid "" "Timeout in seconds to wait for the local switch connecting the controller. " "Used only for 'native' driver." msgstr "" "ì œì–´ê¸°ì— ì—°ê²°ë˜ëŠ” 로컬 스위치를 기다리는 제한시간(ì´ˆ)입니다. '네ì´í‹°ë¸Œ' 드ë¼" "ì´ë²„ì—ë§Œ 사용ë©ë‹ˆë‹¤. " msgid "" "Too long prefix provided. New name would exceed given length for an " "interface name." msgstr "" "너무 긴 ì ‘ë‘ì–´ê°€ 제공ë˜ì—ˆìŠµë‹ˆë‹¤. 새 ì´ë¦„ì´ ì¸í„°íŽ˜ì´ìФ ì´ë¦„ì— ì§€ì •ëœ ê¸¸ì´ë¥¼ ì´ˆ" "과합니다." msgid "" "True to delete all ports on all the OpenvSwitch bridges. False to delete " "ports created by Neutron on integration and external network bridges." msgstr "" "모든 OpenvSwitch ë¸Œë¦¿ì§€ì˜ ëª¨ë“  í¬íŠ¸ë¥¼ 삭제하려면 true입니다. 통합 ë° ì™¸ë¶€ 네" "íŠ¸ì›Œí¬ ë¸Œë¦¿ì§€ì— Neutronì´ ìž‘ì„±í•œ í¬íŠ¸ë¥¼ 삭제하려면 false입니다. " msgid "Tunnel IP value needed by the ML2 plugin" msgstr "ML2 플러그ì¸ì— í„°ë„ IP ê°’ì´ í•„ìš”í•¨" msgid "Tunnel bridge to use." msgstr "사용할 í„°ë„ ë¸Œë¦¿ì§€ìž…ë‹ˆë‹¤." msgid "" "Type of the nova endpoint to use. This endpoint will be looked up in the " "keystone catalog and should be one of public, internal or admin." msgstr "" "사용할 nova 엔드í¬ì¸íŠ¸ì˜ ìœ í˜•ìž…ë‹ˆë‹¤. ì´ ì—”ë“œí¬ì¸íŠ¸ëŠ” keystone 카탈로그ì—서 ê²€" "색하며 공용, ë‚´ë¶€ ë˜ëŠ” 관리 중 하나여야 합니다." msgid "URL for connecting to designate" msgstr "지정하기 위해 ì—°ê²°í•  URL" msgid "URL to database" msgstr "ë°ì´í„°ë² ì´ìŠ¤ì— ëŒ€í•œ URL" #, python-format msgid "Unable to access %s" msgstr "%sì— ì•¡ì„¸ìŠ¤í•  수 ì—†ìŒ" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, maximum allowed " "prefix is %(max_prefixlen)s." msgstr "" "ì ‘ë‘ë¶€ 길ì´ê°€ %(prefixlen)sì¸ ì„œë¸Œë„·ì„ í• ë‹¹í•  수 없습니다. 허용ë˜ëŠ” 최대 ì ‘ë‘" "ë¶€ 길ì´ëŠ” %(max_prefixlen)s입니다. " #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, minimum allowed " "prefix is %(min_prefixlen)s." msgstr "" "ì ‘ë‘ë¶€ 길ì´ê°€ %(prefixlen)sì¸ ì„œë¸Œë„·ì„ í• ë‹¹í•  수 없습니다. 허용ë˜ëŠ” 최소 ì ‘ë‘" "ë¶€ 길ì´ëŠ” %(min_prefixlen)s입니다. " #, python-format msgid "Unable to calculate %(address_type)s address because of:%(reason)s" msgstr "ë‹¤ìŒ ì›ì¸ìœ¼ë¡œ ì¸í•´ %(address_type)s 주소를 계산할 수 ì—†ìŒ: %(reason)s" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of DNS " "nameservers exceeds the limit %(quota)s." msgstr "" "%(subnet_id)sì— ëŒ€í•œ ì¡°ìž‘ì„ ì™„ë£Œí•  수 없습니다. DNS 네임서버 수가 %(quota)s " "한계를 초과했습니다. " #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of host routes " "exceeds the limit %(quota)s." msgstr "" "%(subnet_id)sì— ëŒ€í•œ ì¡°ìž‘ì„ ì™„ë£Œí•  수 없습니다. 호스트 ë¼ìš°íЏ 수가 %(quota)s " "한계를 초과했습니다. " #, python-format msgid "Unable to convert value in %s" msgstr "%sì˜ ê°’ì„ ë³€í™˜í•  수 ì—†ìŒ" msgid "Unable to create the Agent Gateway Port" msgstr "ì—ì´ì „트 게ì´íŠ¸ì›¨ì´ í¬íŠ¸ë¥¼ 작성할 수 ì—†ìŒ" msgid "Unable to create the SNAT Interface Port" msgstr "SNAT ì¸í„°íŽ˜ì´ìФ í¬íŠ¸ë¥¼ 작성할 수 ì—†ìŒ" #, python-format msgid "" "Unable to create the flat network. Physical network %(physical_network)s is " "in use." msgstr "" "ì¼ë°˜ 네트워í¬ë¥¼ 작성할 수 없습니다. 실제 ë„¤íŠ¸ì›Œí¬ %(physical_network)sì´(ê°€) " "사용 중입니다. " msgid "" "Unable to create the network. No available network found in maximum allowed " "attempts." msgstr "" "네트워í¬ë¥¼ 작성할 수 없습니다. 허용ë˜ëŠ” 최대 ì‹œë„ ìˆ˜ë§Œí¼ ì‹œë„한 후 사용 가능" "한 네트워í¬ë¥¼ ì°¾ì„ ìˆ˜ 없습니다." #, python-format msgid "Unable to delete subnet pool: %(reason)s." msgstr "서브넷 í’€ì„ ì‚­ì œí•  수 ì—†ìŒ: %(reason)s." #, python-format msgid "Unable to determine mac address for %s" msgstr "%sì˜ ë§¥ 주소를 확ì¸í•  수 없습니다" #, python-format msgid "Unable to find '%s' in request body" msgstr "요청 본문ì—서 '%s'ì„(를) ì°¾ì„ ìˆ˜ ì—†ìŒ" #, python-format msgid "Unable to find IP address %(ip_address)s on subnet %(subnet_id)s" msgstr "서브넷 %(subnet_id)sì—서 IP 주소 %(ip_address)sì„(를) ì°¾ì„ ìˆ˜ ì—†ìŒ" #, python-format msgid "Unable to find resource name in %s" msgstr "%sì—서 ìžì› ì´ë¦„ì„ ì°¾ì„ ìˆ˜ ì—†ìŒ" #, python-format msgid "Unable to generate unique mac on network %(net_id)s." msgstr "%(net_id)s 네트워í¬ì— 고유 MACì„ ìƒì„±í•  수 없습니다. " #, python-format msgid "" "Unable to identify a target field from:%s. Match should be in the form " "%%()s" msgstr "" "%sì—서 ëŒ€ìƒ í•„ë“œë¥¼ ì‹ë³„í•  수 ì—†ìŒ. ì¼ì¹˜ê°€ ë‹¤ìŒ ì–‘ì‹ì´ì–´ì•¼ 함." "%%()s" msgid "Unable to provide external connectivity" msgstr "외부 ì—°ê²°ì„ ì œê³µí•  수 ì—†ìŒ" msgid "Unable to provide tenant private network" msgstr "테넌트 ê°œì¸ ë„¤íŠ¸ì›Œí¬ë¥¼ 제공할 수 ì—†ìŒ" #, python-format msgid "" "Unable to reconfigure sharing settings for network %(network)s. Multiple " "tenants are using it." msgstr "" "%(network)s 네트워í¬ì— 대한 공유 ì„¤ì •ì„ ìž¬êµ¬ì„±í•  수 없습니다. 여러 ê°œì˜ í…Œë„Œ" "트가 ì´ë¥¼ 사용 중입니다. " #, python-format msgid "" "Unable to verify match:%(match)s as the parent resource: %(res)s was not " "found" msgstr "" "ìƒìœ„ 소스로서 ì¼ì¹˜ %(match)sì„(를) 확ì¸í•  수 ì—†ìŒ. %(res)sì„(를) ì°¾ì„ ìˆ˜ ì—†ìŒ" #, python-format msgid "Unexpected label for script %(script_name)s: %(labels)s" msgstr "스í¬ë¦½íЏ %(script_name)sì— ëŒ€í•´ 예ìƒì¹˜ ì•Šì€ ë ˆì´ë¸”: %(labels)s" #, python-format msgid "Unexpected number of alembic branch points: %(branchpoints)s" msgstr "예ìƒì¹˜ 못한 ìˆ˜ì˜ ë³€í˜• 장치(alembic) 분기ì : %(branchpoints)s" #, python-format msgid "Unexpected response code: %s" msgstr "예기치 ì•Šì€ ì‘답 코드: %s" #, python-format msgid "Unexpected response: %s" msgstr "예ìƒì¹˜ ì•Šì€ ì‘답: %s" #, python-format msgid "Unit name '%(unit)s' is not valid." msgstr "단위 ì´ë¦„ '%(unit)s'ì´(ê°€) 올바르지 않습니다." #, python-format msgid "Unknown address type %(address_type)s" msgstr "알 수 없는 주소 유형 %(address_type)s" #, python-format msgid "Unknown attribute '%s'." msgstr "알 수 없는 ì†ì„± '%s'입니다." #, python-format msgid "Unknown chain: %r" msgstr "알 수 없는 ì²´ì¸: %r" #, python-format msgid "Unknown network type %(network_type)s." msgstr "알 수 없는 ë„¤íŠ¸ì›Œí¬ ìœ í˜• %(network_type)s." #, python-format msgid "Unknown quota resources %(unknown)s." msgstr "알 수 없는 할당량 ìžì› %(unknown)s." msgid "Unmapped error" msgstr "맵핑ë˜ì§€ ì•Šì€ ì˜¤ë¥˜" msgid "Unrecognized action" msgstr "ì¸ì‹ë˜ì§€ 않는 조치" #, python-format msgid "Unrecognized attribute(s) '%s'" msgstr "ì¸ì‹ë˜ì§€ 않는 ì†ì„± '%s'" msgid "Unrecognized field" msgstr "ì¸ì‹ë˜ì§€ 않는 필드" msgid "Unsupported Content-Type" msgstr "ì§€ì›ë˜ì§€ 않는 Content-Type" #, python-format msgid "Unsupported network type %(net_type)s." msgstr "ì§€ì›ë˜ì§€ 않는 ë„¤íŠ¸ì›Œí¬ ìœ í˜• %(net_type)s입니다." #, python-format msgid "Unsupported port state: %(port_state)s." msgstr "ì§€ì›ë˜ì§€ 않는 í¬íЏ ìƒíƒœ: %(port_state)s" msgid "Unsupported request type" msgstr "ì§€ì›ë˜ì§€ 않는 요청 유형" msgid "Updating default security group not allowed." msgstr "기본 보안 그룹 ì—…ë°ì´íŠ¸ê°€ 허용ë˜ì§€ 않습니다. " msgid "" "Use ML2 l2population mechanism driver to learn remote MAC and IPs and " "improve tunnel scalability." msgstr "" "ì›ê²© MAC ë° IP를 학습하고 í„°ë„ í™•ìž¥ì„±ì„ ê°œì„ í•˜ë ¤ë©´ ML2 l2population 메커니즘 " "드ë¼ì´ë²„를 사용하십시오." msgid "Use broadcast in DHCP replies." msgstr "DHCP 복제본ì—서 브로드ìºìŠ¤íŒ…ì„ ì‚¬ìš©í•˜ì‹­ì‹œì˜¤." msgid "Use either --delta or relative revision, not both" msgstr "--ë¸íƒ€ ë˜ëŠ” ìƒëŒ€ì  ê°œì •íŒ ì¤‘ 하나 사용" msgid "" "Use ipset to speed-up the iptables based security groups. Enabling ipset " "support requires that ipset is installed on L2 agent node." msgstr "" "ipsetì„ ì‚¬ìš©í•˜ì—¬ iptables 기반 보안 ê·¸ë£¹ì˜ ì†ë„를 높입니다. ipset ì§€ì›ì„ 사용" "하려면 ipsetì´ L2 ì—ì´ì „트 ë…¸ë“œì— ì„¤ì¹˜ë˜ì–´ì•¼ 합니다." msgid "" "Use the root helper when listing the namespaces on a system. This may not be " "required depending on the security configuration. If the root helper is not " "required, set this to False for a performance improvement." msgstr "" "ì‹œìŠ¤í…œì— ë„¤ìž„ìŠ¤íŽ˜ì´ìŠ¤ë¥¼ 나열할 때 루트 í—¬í¼ë¥¼ 사용하십시오. 보안 êµ¬ì„±ì— ë”°ë¼ " "ì´ ìž‘ì—…ì€ í•„ìš”í•˜ì§€ ì•Šì„ ìˆ˜ 있습니다. 루트 í—¬í¼ê°€ 필요하지 않으면 ì„±ëŠ¥ì´ í–¥ìƒ" "ë˜ë„ë¡ False로 설정하십시오." msgid "" "Use veths instead of patch ports to interconnect the integration bridge to " "physical networks. Support kernel without Open vSwitch patch port support so " "long as it is set to True." msgstr "" "패치 í¬íЏ 대신 veth를 사용하여 통합 브릿지와 실제 브릿지를 ìƒí˜¸ì—°ê²°í•˜ì‹­ì‹œì˜¤. " "True로 ì„¤ì •ëœ ê²½ìš°ì— í•œí•´ Open vSwitch 패치 í¬íŠ¸ê°€ 없는 커ë„ì´ ì§€ì›ë©ë‹ˆë‹¤." msgid "" "User (uid or name) running metadata proxy after its initialization (if " "empty: agent effective user)." msgstr "" "초기화 í›„ì— ë©”íƒ€ë°ì´í„° 프ë¡ì‹œë¥¼ 실행하는 사용ìž(uid ë˜ëŠ” ì´ë¦„)(비어 있는 ê²½" "ìš°: ì—ì´ì „트 유효 사용ìž)." msgid "User (uid or name) running this process after its initialization" msgstr "초기화 ì´í›„ ì´ í”„ë¡œì„¸ìŠ¤ë¥¼ 실행하는 사용ìž(uid ë˜ëŠ” ì´ë¦„)" msgid "Username for connecting to designate in admin context" msgstr "관리 컨í…스트ì—서 지정하기 위해 ì—°ê²°í•  ì‚¬ìš©ìž ì´ë¦„" msgid "VRRP authentication password" msgstr "VRRP ì¸ì¦ 비밀번호" msgid "VRRP authentication type" msgstr "VRRP ì¸ì¦ 유형" msgid "VXLAN network unsupported." msgstr "VXLAN 네트워í¬ê°€ ì§€ì›ë˜ì§€ 않습니다." msgid "" "Value of host kernel tick rate (hz) for calculating minimum burst value in " "bandwidth limit rules for a port with QoS. See kernel configuration file for " "HZ value and tc-tbf manual for more information." msgstr "" "QoSê°€ 있는 í¬íŠ¸ì˜ ëŒ€ì—­í­ í•œê³„ 규칙ì—서 최소 버스트 ê°’ì„ ê³„ì‚°í•˜ê¸° 위한 호스트 " "ì»¤ë„ í‹±(tick) ì†ë„(hz) 값입니다. ìžì„¸í•œ ë‚´ìš©ì€ HZ ê°’ ë° tc-tbf ë§¤ë‰´ì–¼ì˜ ì»¤ë„ " "구성 파ì¼ì„ 참조하십시오." msgid "" "Value of latency (ms) for calculating size of queue for a port with QoS. See " "tc-tbf manual for more information." msgstr "" "QoSê°€ 있는 í¬íŠ¸ì˜ í í¬ê¸°ë¥¼ 계산하기 위한 지연 시간 ê°’(ms)입니다. ìžì„¸í•œ ì •ë³´" "는 tc-tbf ë§¤ë‰´ì–¼ì„ ì°¸ì¡°í•˜ì‹­ì‹œì˜¤." msgid "" "When external_network_bridge is set, each L3 agent can be associated with no " "more than one external network. This value should be set to the UUID of that " "external network. To allow L3 agent support multiple external networks, both " "the external_network_bridge and gateway_external_network_id must be left " "empty." msgstr "" "external_network_bridgeê°€ 설정ë˜ë©´ ê° L3 ì—ì´ì „트가 ë‘ ê°œ ì´ìƒì˜ 외부 네트워" "í¬ì™€ ì—°ê²°ë  ìˆ˜ 없습니다. ì´ ê°’ì€ ì™¸ë¶€ 네트워í¬ì˜ UUID로 설정ë˜ì–´ì•¼ 합니다. " "L3 ì—ì´ì „트ì—서 여러 외부 네트워í¬ë¥¼ ì§€ì›í•  수 있으려면 " "external_network_bridge와 gateway_external_network_idê°€ 비어 있어야 합니다." msgid "" "When proxying metadata requests, Neutron signs the Instance-ID header with a " "shared secret to prevent spoofing. You may select any string for a secret, " "but it must match here and in the configuration used by the Nova Metadata " "Server. NOTE: Nova uses the same config key, but in [neutron] section." msgstr "" "메타ë°ì´í„° ìš”ì²­ì˜ í”„ë¡ì‹œ ì—­í• ì„ ìˆ˜í–‰í•  때 Neutronì´ ìœ„ì¡°ë¥¼ 방지하기 위해 ê³µ" "유 시í¬ë¦¿ìœ¼ë¡œ Instance-ID í—¤ë”ì— ì„œëª…í•©ë‹ˆë‹¤. 시í¬ë¦¿ìœ¼ë¡œ ìž„ì˜ì˜ 문ìžì—´ì„ ì„ íƒ" "í•  수 있지만 ì—¬ê¸°ì— ìžˆëŠ” 문ìžì—´ ë° Nova Metadata Serverì—서 사용하는 구성과 " "ì¼ì¹˜í•´ì•¼ 합니다. 참고: Novaì—서는 [neutron] ì„¹ì…˜ì— ìžˆëŠ” ë™ì¼í•œ 구성 키를 사용" "합니다." msgid "" "Where to store Neutron state files. This directory must be writable by the " "agent." msgstr "" "Neutron ìƒíƒœ 파ì¼ì„ 저장할 위치. ì—ì´ì „트가 ì´ ë””ë ‰í† ë¦¬ì— ì“¸ 수 있어야 합니" "다." msgid "" "With IPv6, the network used for the external gateway does not need to have " "an associated subnet, since the automatically assigned link-local address " "(LLA) can be used. However, an IPv6 gateway address is needed for use as the " "next-hop for the default route. If no IPv6 gateway address is configured " "here, (and only then) the neutron router will be configured to get its " "default route from router advertisements (RAs) from the upstream router; in " "which case the upstream router must also be configured to send these RAs. " "The ipv6_gateway, when configured, should be the LLA of the interface on the " "upstream router. If a next-hop using a global unique address (GUA) is " "desired, it needs to be done via a subnet allocated to the network and not " "through this parameter. " msgstr "" "IPv6를 사용하면 ìžë™ìœ¼ë¡œ 지정ë˜ëŠ” ë§í¬ 로컬 주소(LLA)를 사용할 수 있으므로 외" "ë¶€ 게ì´íŠ¸ì›¨ì´ì— 사용한 네트워í¬ì— ì—°ê´€ ì„œë¸Œë„·ì´ í•„ìš”í•˜ì§€ 않습니다. 그러나 기" "본 ë¼ìš°íŠ¸ì˜ ë‹¤ìŒ í™‰ìœ¼ë¡œ 사용할 IPv6 게ì´íŠ¸ì›¨ì´ ì£¼ì†Œê°€ 필요합니다. 여기서 " "IPv6 게ì´íŠ¸ì›¨ì´ ì£¼ì†Œë¥¼ 구성하지 않으면(ë˜í•œ ì´ ê²½ìš°ì—ë§Œ)ìƒìœ„ ë¼ìš°í„°ì˜ " "RA(Router Advertisement)ì—서 해당 기본 ë¼ìš°íŠ¸ë¥¼ 가져오ë„ë¡ Neutron ë¼ìš°í„°ë¥¼ " "구성할 수 있습니다. ì´ ê²½ìš° ì´ëŸ¬í•œ RA를 ë³´ë‚´ë„ë¡ ìƒìœ„ ë¼ìš°í„°ë¥¼ 구성할 ìˆ˜ë„ ìžˆ" "습니다. ipv6_gateway를 구성한 경우, ì´ ê²Œì´íŠ¸ì›¨ì´ê°€ ìƒìœ„ ë¼ìš°í„°ì˜ ì¸í„°íŽ˜ì´ìФ" "ì— ëŒ€í•œ LLA여야 합니다. 글로벌 고유 주소(GUA)를 사용하는 ë‹¤ìŒ í•©ì´ í•„ìš”í•œ ê²½" "ìš°, ì´ ë§¤ê°œë³€ìˆ˜ê°€ 아닌 네트워í¬ì— í• ë‹¹ëœ ì„œë¸Œë„·ì„ í†µí•´ 수행해야 합니다. " msgid "You must implement __call__" msgstr "__call__ì„ êµ¬í˜„í•´ì•¼ 합니다. " msgid "" "You must provide a config file for bridge - either --config-file or " "env[NEUTRON_TEST_CONFIG_FILE]" msgstr "" "ë¸Œë¦¿ì§€ì— ëŒ€í•œ 구성 파ì¼, 즉 --config-file ë˜ëŠ” env[QUANTUM_TEST_CONFIG_FILE] " "env[NEUTRON_TEST_CONFIG_FILE]" msgid "You must provide a revision or relative delta" msgstr "ê°œì •íŒ ë˜ëŠ” ìƒëŒ€ì  ë¸íƒ€ë¥¼ 제공해야 함" msgid "a subnetpool must be specified in the absence of a cidr" msgstr "cidrì´ ì—†ëŠ” 경우 subnetpoolì„ ì§€ì •í•´ì•¼ 함" msgid "add_ha_port cannot be called inside of a transaction." msgstr "트랜잭션 ë‚´ì—서 add_ha_port를 호출할 수 없습니다." msgid "allocation_pools allowed only for specific subnet requests." msgstr "allocation_pools는 특정 서브넷 요청ì—ë§Œ 사용할 수 있습니다." msgid "allocation_pools are not in the subnet" msgstr "allocation_poolsê°€ ì„œë¸Œë„·ì— ì—†ìŒ" msgid "allocation_pools use the wrong ip version" msgstr "allocation_poolsì—서 ìž˜ëª»ëœ ip ë²„ì „ì„ ì‚¬ìš©í•¨" msgid "already a synthetic attribute" msgstr "ì´ë¯¸ 합성 ì†ì„±ìž„" msgid "binding:profile value too large" msgstr "ë°”ì¸ë”©:í”„ë¡œíŒŒì¼ ê°’ì´ ë„ˆë¬´ ê¹€" #, python-format msgid "cannot perform %(event)s due to %(reason)s" msgstr "%(reason)s(으)로 ì¸í•´ %(event)sì„(를) 수행할 수 ì—†ìŒ" msgid "cidr and prefixlen must not be supplied together" msgstr "cidr ë° prefixlenì„ í•¨ê»˜ 입력하지 않아야 함" #, python-format msgid "dhcp_agents_per_network must be >= 1. '%s' is invalid." msgstr "" "dhcp_agents_per_network는 1 ì´ìƒì´ì–´ì•¼ 합니다. '%s'ì€(는) 올바르지 않습니다. " msgid "dns_domain cannot be specified without a dns_name" msgstr "dns_name ì—†ì´ dns_domainì„ ì§€ì •í•  수 ì—†ìŒ" msgid "dns_name cannot be specified without a dns_domain" msgstr "dns_domain ì—†ì´ dns_nameì„ ì§€ì •í•  수 ì—†ìŒ" msgid "fixed_ip_address cannot be specified without a port_id" msgstr "fixed_ip_address는 port_id ì—†ì´ ì§€ì •í•  수 ì—†ìŒ" #, python-format msgid "has device owner %s" msgstr "디바ì´ìФ ì†Œìœ ìž %sì´(ê°€) 있ìŒ" msgid "in use" msgstr "사용 중" #, python-format msgid "ip command failed on device %(dev_name)s: %(reason)s" msgstr "%(dev_name)s 디바ì´ìŠ¤ì— ëŒ€í•œ ip 명령 실패: %(reason)s" #, python-format msgid "ip command failed: %(reason)s" msgstr "ip 명령 실패: %(reason)s" #, python-format msgid "ip link capability %(capability)s is not supported" msgstr "ip ë§í¬ 기능 %(capability)sì´(ê°€) ì§€ì›ë˜ì§€ 않ìŒ" #, python-format msgid "ip link command is not supported: %(reason)s" msgstr "ip ë§í¬ ëª…ë ¹ì´ ì§€ì›ë˜ì§€ 않ìŒ: %(reason)s" msgid "ip_version must be specified in the absence of cidr and subnetpool_id" msgstr "cidr ë° subnetpool_idê°€ 없는 경우 ip_versionì„ ì§€ì •í•´ì•¼ 함" msgid "ipv6_address_mode is not valid when ip_version is 4" msgstr "ip_versionì´ 4ì¸ ê²½ìš° ipv6_address_modeê°€ 올바르지 않ìŒ" msgid "ipv6_ra_mode is not valid when ip_version is 4" msgstr "ip_versionì´ 4ì¸ ê²½ìš° ipv6_ra_modeê°€ 올바르지 않ìŒ" #, python-format msgid "" "ipv6_ra_mode set to '%(ra_mode)s' with ipv6_address_mode set to " "'%(addr_mode)s' is not valid. If both attributes are set, they must be the " "same value" msgstr "" "'%(ra_mode)s'(으)로 ì„¤ì •ëœ ipv6_ra_mode('%(addr_mode)s'(으)로 ì„¤ì •ëœ " "ipv6_address_mode í¬í•¨)ê°€ 올바르지 않습니다. ë‘ ì„¤ì • 다 ì„¤ì •ëœ ê²½ìš° ë™ì¼í•œ ê°’" "ì´ì–´ì•¼ 합니다." msgid "mac address update" msgstr "mac 주소 ì—…ë°ì´íЏ" msgid "must provide exactly 2 arguments - cidr and MAC" msgstr "정확히 ë‘ ê°œì˜ ì¸ìˆ˜ - cidr ë° MAC를 제공해야 함" msgid "network_type required" msgstr "network_typeì´ í•„ìš”í•¨" #, python-format msgid "network_type value '%s' not supported" msgstr "network_typeì—서 '%s' ê°’ì„ ì§€ì›í•˜ì§€ 않습니다" msgid "new subnet" msgstr "새 서브넷" #, python-format msgid "physical_network '%s' unknown for flat provider network" msgstr "플랫 ì œê³µìž ë„¤íŠ¸ì›Œí¬ì— 대해 실제 ë„¤íŠ¸ì›Œí¬ '%s'ì„(를) 알 수 ì—†ìŒ. " msgid "physical_network required for flat provider network" msgstr "플랫 ì œê³µìž ë„¤íŠ¸ì›Œí¬ì— 실제 ë„¤íŠ¸ì›Œí¬ í•„ìš”" #, python-format msgid "provider:physical_network specified for %s network" msgstr "%s 네트워í¬ì— 대해 ì§€ì •ëœ provider:physical_network 입니다" msgid "respawn_interval must be >= 0 if provided." msgstr "respawn_intervalì€ >= 0ì´ì–´ì•¼ 합니다(ì œê³µëœ ê²½ìš°)." #, python-format msgid "segmentation_id out of range (%(min)s through %(max)s)" msgstr "segmentation_idê°€ 범위(%(min)s - %(max)s)를 벗어남" msgid "segmentation_id requires physical_network for VLAN provider network" msgstr "segmentation_id는 VLAN ì œê³µìž ë„¤íŠ¸ì›Œí¬ì˜ physical_networkê°€ 필요함" msgid "shared attribute switching to synthetic" msgstr "공유 ì†ì„±ì„ 합성으로 전환" #, python-format msgid "" "subnetpool %(subnetpool_id)s cannot be updated when associated with shared " "address scope %(address_scope_id)s" msgstr "" "공유 주소 범위 %(address_scope_id)sê³¼(와) ì—°ê´€ëœ ê²½ìš° 서브넷 í’€ " "%(subnetpool_id)sì„(를) ì—…ë°ì´íŠ¸í•  수 ì—†ìŒ" msgid "subnetpool_id and use_default_subnetpool cannot both be specified" msgstr "subnetpool_id ë° use_default_subnetpoolì„ ëª¨ë‘ ì§€ì •í•  수 ì—†ìŒ" msgid "the nexthop is not connected with router" msgstr "nexthopì´ ë¼ìš°í„°ì™€ ì—°ê²°ë˜ì§€ 않ìŒ" msgid "the nexthop is used by router" msgstr "ë¼ìš°í„°ê°€ nexthopì„ ì‚¬ìš©í•¨" neutron-12.1.1/neutron/locale/ru/0000775000175000017500000000000013553660156016701 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/ru/LC_MESSAGES/0000775000175000017500000000000013553660156020466 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/ru/LC_MESSAGES/neutron.po0000664000175000017500000043067013553660047022531 0ustar zuulzuul00000000000000# Translations template for neutron. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the neutron project. # # Translators: # Andreas Jaeger , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: neutron VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-03-14 04:19+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-04-12 05:54+0000\n" "Last-Translator: Copied by Zanata \n" "Language: ru\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" "%100>=11 && n%100<=14)? 2 : 3);\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 4.3.3\n" "Language-Team: Russian\n" #, python-format msgid "" "\n" "Command: %(cmd)s\n" "Exit code: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" msgstr "" "\n" "Команда: %(cmd)s\n" "Код выхода: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" #, python-format msgid "" "%(branch)s HEAD file does not match migration timeline head, expected: " "%(head)s" msgstr "" "Файл HEAD %(branch)s отличаетÑÑ Ð¾Ñ‚ ожидаемого Ð´Ð»Ñ Ð³Ñ€Ð°Ñ„Ð¸ÐºÐ° миграции: %(head)s" #, python-format msgid "%(id)s is not a valid %(type)s identifier" msgstr "%(id)s не ÑвлÑетÑÑ Ð´Ð¾Ð¿ÑƒÑтимым идентификатором %(type)s" #, python-format msgid "" "%(invalid_dirs)s is invalid value for sort_dirs, valid value is '%(asc)s' " "and '%(desc)s'" msgstr "" "Значение %(invalid_dirs)s недопуÑтимо Ð´Ð»Ñ sort_dirs; допуÑтимое значение: " "'%(asc)s' и '%(desc)s'" #, python-format msgid "%(key)s prohibited for %(tunnel)s provider network" msgstr "%(key)s запрещен Ð´Ð»Ñ Ñети поÑтавщика %(tunnel)s" #, python-format msgid "%(name)s '%(addr)s' does not match the ip_version '%(ip_version)s'" msgstr "%(name)s '%(addr)s' не ÑоответÑтвует верÑии IP '%(ip_version)s'" #, python-format msgid "%s cannot be called while in offline mode" msgstr "%s Ð½ÐµÐ»ÑŒÐ·Ñ Ð²Ñ‹Ð·Ñ‹Ð²Ð°Ñ‚ÑŒ в режиме без подключениÑ" #, python-format msgid "%s is invalid attribute for sort_keys" msgstr "Ðтрибут %s недопуÑтим Ð´Ð»Ñ sort_keys" #, python-format msgid "%s is not a valid VLAN tag" msgstr "%s не ÑвлÑетÑÑ Ð´Ð¾Ð¿ÑƒÑтимым тегом VLAN" #, python-format msgid "%s must implement get_port_from_device or get_ports_from_devices." msgstr "%s должен реализовать get_port_from_device или get_ports_from_devices." #, python-format msgid "%s prohibited for VLAN provider network" msgstr "%s запрещено Ð´Ð»Ñ Ñети VLAN провайдера" #, python-format msgid "%s prohibited for flat provider network" msgstr "%s запрещено Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾ÑƒÑ€Ð¾Ð²Ð½ÐµÐ²Ð¾Ð¹ Ñети провайдера" #, python-format msgid "%s prohibited for local provider network" msgstr "%s запрещено Ð´Ð»Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ð¹ Ñети провайдера" #, python-format msgid "'%s' is not a valid RBAC object type" msgstr "'%s' не ÑвлÑетÑÑ Ð´Ð¾Ð¿ÑƒÑтимым типом объекта RBAC" #, python-format msgid "'%s' is not supported for filtering" msgstr "'%s' не поддерживает фильтрацию" #, python-format msgid "'module' object has no attribute '%s'" msgstr "Объект 'module' не Ñодержит атрибута '%s'" msgid "'port_max' is smaller than 'port_min'" msgstr "'port_max' меньше чем 'port_min'" msgid "0 is not allowed as CIDR prefix length" msgstr "Ðулевое значение запрещено в качеÑтве длины префикÑа CIDR" msgid "A cidr must be specified in the absence of a subnet pool" msgstr "Значение cidr должно быть указано при отÑутÑтвии пула подÑетей" msgid "" "A decimal value as Vendor's Registered Private Enterprise Number as required " "by RFC3315 DUID-EN." msgstr "" "ДеÑÑтичное значение в качеÑтве зарегиÑтрированного номера чаÑтного " "предприÑÑ‚Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ Ð² ÑоответÑтвии Ñ RFC3315 DUID-EN." #, python-format msgid "A default external network already exists: %(net_id)s." msgstr "ВнешнÑÑ Ñеть по умолчанию уже ÑущеÑтвует: %(net_id)s." msgid "" "A default subnetpool for this IP family has already been set. Only one " "default may exist per IP family" msgstr "" "Пул подÑетей по умолчанию уже задан Ð´Ð»Ñ Ñтой верÑии IP. Ð”Ð»Ñ Ð²ÐµÑ€Ñии IP может " "быть задан только один пул по умолчанию" msgid "A metering driver must be specified" msgstr "Ðеобходимо указать драйвер измерений" msgid "API for retrieving service providers for Neutron advanced services" msgstr "API Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ñтавщиков Ñлужб Ð´Ð»Ñ Ñ€Ð°Ñширенных Ñлужб Neutron" msgid "Aborting periodic_sync_routers_task due to an error." msgstr "Задача periodic_sync_routers_task прервана из-за ошибки." msgid "Access to this resource was denied." msgstr "ДоÑтуп к Ñтому реÑурÑу запрещен." msgid "Action to be executed when a child process dies" msgstr "ДейÑтвие, выполнÑемое при завершении дочернего процеÑÑа" msgid "" "Add comments to iptables rules. Set to false to disallow the addition of " "comments to generated iptables rules that describe each rule's purpose. " "System must support the iptables comments module for addition of comments." msgstr "" "Добавить комментарии в правила iptables. Укажите значение false, чтобы не " "добавлÑть комментарии в Ñгенерированные правила iptables Ñ Ð¾Ð¿Ð¸Ñанием кажого " "правила. Добавление комментариев возможно, только еÑли ÑиÑтема поддерживает " "модуль комментариев iptables." msgid "Address not present on interface" msgstr "ÐÐ´Ñ€ÐµÑ Ð½Ðµ задан Ð´Ð»Ñ Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñа" msgid "" "Address to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "ÐÐ´Ñ€ÐµÑ Ð´Ð»Ñ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ запроÑов на Ñоединение OpenFlow. ИÑпользуетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ " "'вÑтроенного' драйвера." msgid "Adds test attributes to core resources." msgstr "ДобавлÑет атрибуты теÑта в базовые реÑурÑÑ‹." #, python-format msgid "Agent %(id)s is not a L3 Agent or has been disabled" msgstr "Ðгент %(id)s выключен или не ÑвлÑетÑÑ Ð°Ð³ÐµÐ½Ñ‚Ð¾Ð¼ L3" #, python-format msgid "Agent %(id)s is not a valid DHCP Agent or has been disabled" msgstr "Ðгент %(id)s выключен или не ÑвлÑетÑÑ Ð´Ð¾Ð¿ÑƒÑтимым агентом DHCP" msgid "" "Agent starts with admin_state_up=False when enable_new_agents=False. In the " "case, user's resources will not be scheduled automatically to the agent " "until admin changes admin_state_up to True." msgstr "" "Ðгент запуÑкаетÑÑ Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð¼ admin_state_up=False, еÑли включен параметр " "enable_new_agents=False. Ð’ Ñтом Ñлучае пользовательÑкие реÑурÑÑ‹ не будут " "автоматичеÑки запланированы в агенте, пока админиÑтратор не изменит " "значение параметра admin_state_up на True." #, python-format msgid "Agent updated: %(payload)s" msgstr "Ðгент обновлен: %(payload)s" msgid "Allow auto scheduling networks to DHCP agent." msgstr "Разрешить автоматичеÑкое планирование Ñетей Ð´Ð»Ñ Ð°Ð³ÐµÐ½Ñ‚Ð° DHCP." msgid "Allow auto scheduling of routers to L3 agent." msgstr "Разрешить автоматичеÑкое планирование маршрутизаторов Ð´Ð»Ñ Ð°Ð³ÐµÐ½Ñ‚Ð° L3." msgid "" "Allow overlapping IP support in Neutron. Attention: the following parameter " "MUST be set to False if Neutron is being used in conjunction with Nova " "security groups." msgstr "" "Разрешить поддержку перекрывающихÑÑ IP-адреÑов в Neutron. Внимание: " "Ñледующий параметр ДОЛЖЕРбыть задан равным False, еÑли Neutron иÑпользуетÑÑ " "ÑовмеÑтно Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð°Ð¼Ð¸ защиты Nova." msgid "Allow running metadata proxy." msgstr "Разрешить выполнение прокÑи метаданных." msgid "Allow sending resource operation notification to DHCP agent" msgstr "Разрешить отправку ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾Ð± операции реÑурÑа агенту DHCP" msgid "Allow the creation of PTR records" msgstr "Разрешить Ñоздание запиÑей PTR" msgid "Allow the usage of the bulk API" msgstr "Разрешить иÑпользование Bulk API" msgid "Allow to perform insecure SSL (https) requests to nova metadata" msgstr "Разрешить незащищенные запроÑÑ‹ SSL (https) метаданных nova" msgid "" "Allows for serving metadata requests coming from a dedicated metadata access " "network whose CIDR is 169.254.169.254/16 (or larger prefix), and is " "connected to a Neutron router from which the VMs send metadata:1 request. In " "this case DHCP Option 121 will not be injected in VMs, as they will be able " "to reach 169.254.169.254 through a router. This option requires " "enable_isolated_metadata = True." msgstr "" "Разрешает обÑлуживать запроÑÑ‹ метаданных, иÑходÑщие из выделенной Ñети Ð´Ð»Ñ " "метаданных Ñ CIDR 169.254.169.254/16 (или подÑети), подключенной к " "маршрутизатору Neutron, из которой VM отправлÑÑŽÑ‚ Ð·Ð°Ð¿Ñ€Ð¾Ñ metadata:1. Ð’ Ñтом " "Ñлучае DHCP Option 121 не будет добавлÑтьÑÑ Ð² VM, так как они обращаютÑÑ Ðº " "Ñети 169.254.169.254 через машрутизатор. Ð”Ð»Ñ Ñтой опции необходимо указать " "enable_isolated_metadata = True." msgid "An RBAC policy already exists with those values." msgstr "Ð¡Ñ‚Ñ€Ð°Ñ‚ÐµÐ³Ð¸Ñ RBAC Ñ Ñ‚Ð°ÐºÐ¸Ð¼Ð¸ параметрами уже ÑущеÑтвует." msgid "An identifier must be specified when updating a subnet" msgstr "При обновлении подÑети необходимо указать идентификатор" msgid "An interface driver must be specified" msgstr "Ðе указан драйвер интерфейÑа" msgid "" "An ordered list of extension driver entrypoints to be loaded from the " "neutron.ml2.extension_drivers namespace. For example: extension_drivers = " "port_security,qos" msgstr "" "УпорÑдоченный ÑпиÑок конечных точек драйверов раÑширениÑ, загружаемых из " "проÑтранÑтва имен neutron.ml2.extension_drivers. Пример: extension_drivers = " "port_security,qos" msgid "" "An ordered list of networking mechanism driver entrypoints to be loaded from " "the neutron.ml2.mechanism_drivers namespace." msgstr "" "УпорÑдоченный ÑпиÑок конечных точек драйверов механизмов ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñетей, " "загружаемых из проÑтранÑтва имен neutron.ml2.mechanism_drivers." msgid "An unknown error has occurred. Please try your request again." msgstr "" "Произошла неизвеÑÑ‚Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°. ПожалуйÑта, попытайтеÑÑŒ повторить ваш запроÑ." msgid "Async process didn't respawn" msgstr "ÐÑинхронный процеÑÑ Ð½Ðµ перезапуÑтилÑÑ" msgid "Authorization URL for connecting to designate in admin context" msgstr "" "URL авторизации Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº назначенному объекту в админиÑтративном " "контекÑте" msgid "Automatically remove networks from offline DHCP agents." msgstr "ÐвтоматичеÑки удалÑть Ñети из отключенных агентов DHCP." msgid "" "Automatically reschedule routers from offline L3 agents to online L3 agents." msgstr "" "ÐвтоматичеÑки перепланировать маршрутизаторы Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ‹Ñ… агентов L3 на " "включенные агенты L3 ." msgid "Availability zone of this node" msgstr "Зона доÑтупноÑти узла." msgid "Available commands" msgstr "ДоÑтупные команды" msgid "Backend does not support VLAN Transparency." msgstr "Базовый Ñервер не поддерживает прозрачный режим VLAN." #, python-format msgid "Base MAC: %s" msgstr "Базовый MAC: %s" msgid "" "Base log dir for dnsmasq logging. The log contains DHCP and DNS log " "information and is useful for debugging issues with either DHCP or DNS. If " "this section is null, disable dnsmasq log." msgstr "" "Базовый каталог Ð´Ð»Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² протокола dnsmasq. Протокол Ñодержит ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ " "DHCP и DNS и иÑпользуетÑÑ Ð´Ð»Ñ Ð¾Ñ‚Ð»Ð°Ð´ÐºÐ¸ ошибок в DHCP или DNS. ЕÑли Ñтот " "раздел пуÑÑ‚, протокол dnsmasq отключен." msgid "Body contains invalid data" msgstr "Ð’ теле ÑодержатÑÑ Ð½ÐµÐ´Ð¾Ð¿ÑƒÑтимые данные" msgid "Both network_id and router_id are None. One must be provided." msgstr "" "И network_id, и router_id имеют значение None. Ðеобходимо задать Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ " "один параметр." #, python-format msgid "Bridge %(bridge)s does not exist." msgstr "МоÑÑ‚ %(bridge)s не ÑущеÑтвует." msgid "Bulk operation not supported" msgstr "Ð“Ñ€ÑƒÐ¿Ð¿Ð¾Ð²Ð°Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð½Ðµ поддерживаетÑÑ" msgid "CIDR to monitor" msgstr "CIDR Ð´Ð»Ñ Ð¼Ð¾Ð½Ð¸Ñ‚Ð¾Ñ€Ð°" #, python-format msgid "Callback for %(resource_type)s not found" msgstr "Обратный вызов Ð´Ð»Ñ %(resource_type)s не найден" #, python-format msgid "Callback for %(resource_type)s returned wrong resource type" msgstr "Обратный вызов %(resource_type)s вернул неправильный тип реÑурÑа" #, python-format msgid "Cannot add floating IP to port %s that has no fixed IPv4 addresses" msgstr "" "Ðе удаетÑÑ Ð´Ð¾Ð±Ð°Ð²Ð¸Ñ‚ÑŒ нефикÑированный IP-Ð°Ð´Ñ€ÐµÑ Ð² порт %s, Ñодержащий " "фикÑированные адреÑа IPv4" #, python-format msgid "Cannot add multiple callbacks for %(resource_type)s" msgstr "Ðе удалоÑÑŒ добавить неÑколько обратных вызовов Ð´Ð»Ñ %(resource_type)s" #, python-format msgid "Cannot allocate IPv%(req_ver)s subnet from IPv%(pool_ver)s subnet pool" msgstr "" "Ðе удалоÑÑŒ ÑвÑзать подÑеть IPv%(req_ver)s из пула подÑетей IPv%(pool_ver)s" msgid "Cannot allocate requested subnet from the available set of prefixes" msgstr "Ðевозможно выделить запрошенную подÑеть из доÑтупного набора префикÑов" msgid "Cannot disable enable_dhcp with ipv6 attributes set" msgstr "Ðевозможно отключить enable_dhcp, еÑли заданы атрибуты ipv6" #, python-format msgid "Cannot handle subnet of type %(subnet_type)s" msgstr "Ðе удаетÑÑ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚Ð°Ñ‚ÑŒ подÑеть Ñ Ñ‚Ð¸Ð¿Ð¾Ð¼ %(subnet_type)s" msgid "Cannot have multiple IPv4 subnets on router port" msgstr "С портом маршрутизатора не может быть ÑвÑзано неÑколько подÑетей IPv4" #, python-format msgid "" "Cannot have multiple router ports with the same network id if both contain " "IPv6 subnets. Existing port %(p)s has IPv6 subnet(s) and network id %(nid)s" msgstr "" "Ðевозможно иметь неÑколько портов маршрутизатора Ñ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ñ‹Ð¼Ð¸ ИД Ñети, еÑли " "обаÑодержат подÑети IPv6. СущеÑтвующий порт %(p)s имеет ИД Ñети и подÑетей " "IPv6 %(nid)s" #, python-format msgid "" "Cannot host distributed router %(router_id)s on legacy L3 agent %(agent_id)s." msgstr "" "РаÑпределенный маршрутизатор %(router_id)s не может работать на уÑтаревшем " "агенте L3 %(agent_id)s." msgid "Cannot mix IPv4 and IPv6 prefixes in a subnet pool." msgstr "ÐÐµÐ»ÑŒÐ·Ñ Ñмешивать префикÑÑ‹ IPv4 и IPv6 в пуле подÑетей." msgid "Cannot specify both subnet-id and port-id" msgstr "subnet-id и port-id Ð½ÐµÐ»ÑŒÐ·Ñ ÑƒÐºÐ°Ð·Ñ‹Ð²Ð°Ñ‚ÑŒ одновременно" msgid "Cannot understand JSON" msgstr "Ðевозможно раÑпознать JSON" #, python-format msgid "Cannot update read-only attribute %s" msgstr "Ðевозможно обновить атрибут %s, доÑтупный только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ" msgid "Certificate Authority public key (CA cert) file for ssl" msgstr "Файл общего ключа CA (CA cert) Ð´Ð»Ñ ssl" #, python-format msgid "" "Change would make usage less than 0 for the following resources: %(unders)s." msgstr "" "Ð’ Ñлучае Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ‡Ð¸Ñло иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð±Ñ‹Ð»Ð¾ бы меньше 0 Ð´Ð»Ñ Ñледующих " "реÑурÑов: %(unders)s." msgid "Check ebtables installation" msgstr "Проверить уÑтановку ebtables" msgid "Check for ARP header match support" msgstr "Проверить Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ ÑопоÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ° ARP" msgid "Check for ARP responder support" msgstr "Проверка Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ промежуточного клиента ARP" msgid "Check for ICMPv6 header match support" msgstr "Проверить Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ ÑопоÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ° ICMPv6" msgid "Check for OVS Geneve support" msgstr "Проверить на наличие поддержки Geneve OVS" msgid "Check for OVS vxlan support" msgstr "Проверить на наличие поддержки OVS vxlan" msgid "Check for VF management support" msgstr "Проверить Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ VF" msgid "Check for iproute2 vxlan support" msgstr "Проверка Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ iproute2 vxlan" msgid "Check for nova notification support" msgstr "Проверка Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ уведомлений nova" msgid "Check for patch port support" msgstr "Проверка Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ портов иÑправлений" msgid "Check ip6tables installation" msgstr "Проверить уÑтановку ip6tables" msgid "Check ipset installation" msgstr "Проверить уÑтановку ipset" msgid "Check keepalived IPv6 support" msgstr "Проверить наличие поддержки IPv6 в конфигурации keepalived" msgid "Check minimal dibbler version" msgstr "Проверить минимальную верÑию dibbler" msgid "Check minimal dnsmasq version" msgstr "Проверить минимальную верÑию dnsmasq" msgid "Check netns permission settings" msgstr "Проверить параметры прав доÑтупа netns" msgid "Check ovs conntrack support" msgstr "Проверить поддержку conntrack в ovs" msgid "Check ovsdb native interface support" msgstr "Проверить поддержку ÑобÑтвенного интерфейÑа ovsdb" #, python-format msgid "" "Cidr %(subnet_cidr)s of subnet %(subnet_id)s overlaps with cidr %(cidr)s of " "subnet %(sub_id)s" msgstr "" "Cidr %(subnet_cidr)s подÑети %(subnet_id)s перекрываетÑÑ Ñ cidr %(cidr)s " "подÑети %(sub_id)s" msgid "Cleanup resources of a specific agent type only." msgstr "ОчиÑтить реÑурÑÑ‹ только Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ типа агента." msgid "Client certificate for nova metadata api server." msgstr "Сертификат клиента Ð´Ð»Ñ Ñервера API метаданных nova." msgid "" "Comma-separated list of : tuples, mapping " "network_device to the agent's node-specific list of virtual functions that " "should not be used for virtual networking. vfs_to_exclude is a semicolon-" "separated list of virtual functions to exclude from network_device. The " "network_device in the mapping should appear in the physical_device_mappings " "list." msgstr "" "СпиÑок запиÑей, разделенных запÑтыми, вида <Ñетевое-уÑтройÑтво>:<иÑключаемые-" "vfs>, ÑвÑзывающих Ñетевое уÑтройÑтво Ñо ÑпиÑком виртуальных функций узла " "агента, которые не должны иÑпользоватьÑÑ Ð´Ð»Ñ Ð²Ð¸Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ñ‹Ñ… Ñетей. иÑключаемые-" "vfs - Ñто ÑпиÑок виртуальных функций, иÑключаемых Ð´Ð»Ñ Ñетевого уÑтройÑтва, " "разделенный точкой Ñ Ð·Ð°Ð¿Ñтой. СвÑзанное Ñетевое уÑтройÑтво должно входить в " "ÑпиÑок physical_device_mappings." msgid "" "Comma-separated list of : tuples mapping " "physical network names to the agent's node-specific physical network device " "interfaces of SR-IOV physical function to be used for VLAN networks. All " "physical networks listed in network_vlan_ranges on the server should have " "mappings to appropriate interfaces on each agent." msgstr "" "СпиÑок запиÑей, разделенных запÑтыми, вида <физичеÑкаÑ-Ñеть>:<Ñетевое-" "уÑтройÑтво>, ÑвÑзывающих имена физичеÑких Ñетей Ñ Ñ„Ð¸Ð·Ð¸Ñ‡ÐµÑким Ñетевым " "уÑтройÑтвом узла агента физичеÑкой функции SR-IOV Ð´Ð»Ñ Ñетей VLAN. Ð’Ñе " "физичеÑкие Ñети, перечиÑленные в network_vlan_ranges на Ñервере, должны " "иметь ÑвÑзи Ñ ÑоответÑтвующими интерфейÑами на каждом агенте." msgid "" "Comma-separated list of : tuples " "mapping physical network names to the agent's node-specific physical network " "interfaces to be used for flat and VLAN networks. All physical networks " "listed in network_vlan_ranges on the server should have mappings to " "appropriate interfaces on each agent." msgstr "" "СпиÑок запиÑей, разделенных запÑтыми, вида <физичеÑкаÑ-Ñеть>:<физичеÑкий-" "интерфейÑ>, ÑвÑзывающих имена физичеÑких Ñетей Ñ Ñ„Ð¸Ð·Ð¸Ñ‡ÐµÑкими Ñетевыми " "интерфейÑами узла агента Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾ÑƒÑ€Ð¾Ð²Ð½ÐµÐ²Ñ‹Ñ… Ñетей и Ñетей VLAN. Ð’Ñе " "физичеÑкие Ñети, перечиÑленные в network_vlan_ranges на Ñервере, должны " "иметь ÑвÑзи Ñ ÑоответÑтвующими интерфейÑами на каждом агенте." msgid "" "Comma-separated list of : tuples enumerating ranges of GRE " "tunnel IDs that are available for tenant network allocation" msgstr "" "Разделенный запÑтой ÑпиÑок кортежей :, в котором " "перечиÑлены диапазоны ИД туннелей GRE, доÑтупные Ð´Ð»Ñ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ñети " "арендатора" msgid "" "Comma-separated list of : tuples enumerating ranges of " "Geneve VNI IDs that are available for tenant network allocation" msgstr "" "Разделенный запÑтой ÑпиÑок кортежей :, в котором " "перечиÑлены ИД VNI Geneve, доÑтупные Ð´Ð»Ñ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ñети арендатора" msgid "" "Comma-separated list of : tuples enumerating ranges of " "VXLAN VNI IDs that are available for tenant network allocation" msgstr "" "Разделенный запÑтой ÑпиÑок кортежей :, в котором " "перечиÑлены идентификаторы VNI VXLAN, доÑтупные Ð´Ð»Ñ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ñети арендатора" msgid "" "Comma-separated list of the DNS servers which will be used as forwarders." msgstr "" "Разделенный запÑтыми ÑпиÑок Ñерверов DNS, которые будут иÑпользоватьÑÑ Ð´Ð»Ñ " "переÑылки." msgid "Command to execute" msgstr "ВыполнÑÐµÐ¼Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð°" msgid "Config file for interface driver (You may also use l3_agent.ini)" msgstr "" "Файл конфигурации Ð´Ð»Ñ Ð´Ñ€Ð°Ð¹Ð²ÐµÑ€Ð° интерфейÑа (Можно также иÑпользовать l3_agent." "ini)" #, python-format msgid "Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s" msgstr "Конфликтующее значение ethertype %(ethertype)s Ð´Ð»Ñ CIDR %(cidr)s" msgid "" "Controls whether the neutron security group API is enabled in the server. It " "should be false when using no security groups or using the nova security " "group API." msgstr "" "Контролирует, включен ли API групп защиты neutron на Ñервере. Значение " "должно быть false, когда группы защиты не иÑпользуютÑÑ Ð¸Ð»Ð¸ иÑпользуетÑÑ API " "групп защиты nova." #, python-format msgid "Could not bind to %(host)s:%(port)s after trying for %(time)d seconds" msgstr "" "Ðе удалоÑÑŒ подключитьÑÑ Ðº порту %(host)s:%(port)s по иÑтечении %(time)d " "Ñекунд" msgid "Could not deserialize data" msgstr "Ðе удалоÑÑŒ деÑериализовать данные" #, python-format msgid "" "Current gateway ip %(ip_address)s already in use by port %(port_id)s. Unable " "to update." msgstr "" "Текущий ip-Ð°Ð´Ñ€ÐµÑ ÑˆÐ»ÑŽÐ·Ð° %(ip_address)s уже иÑпользуетÑÑ Ð¿Ð¾Ñ€Ñ‚Ð¾Ð¼ %(port_id)s. " "Обновление невозможно." msgid "" "DHCP lease duration (in seconds). Use -1 to tell dnsmasq to use infinite " "lease times." msgstr "" "ПродолжительноÑть Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð°Ð´Ñ€ÐµÑа DHCP (в Ñекундах). Укажите -1, чтобы " "dnsmasq иÑпользовала беÑконечное Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ." msgid "" "DVR deployments for VXLAN/GRE/Geneve underlays require L2-pop to be enabled, " "in both the Agent and Server side." msgstr "" "При развертывании DVR Ð´Ð»Ñ Ð¾Ñновных функций VXLAN/GRE/Geneve включить L2-pop " "как на Ñтороне агента, так и на Ñтороне Ñервера." msgid "" "Database engine for which script will be generated when using offline " "migration." msgstr "" "Служба базы данных, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð¹ будет Ñоздан Ñценарий при иÑпользовании " "миграции Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸ÐµÐ¼." msgid "Default external networks must be shared to everyone." msgstr "ВнешнÑÑ Ñеть по умолчанию должна быть общедоÑтупной." msgid "" "Default network type for external networks when no provider attributes are " "specified. By default it is None, which means that if provider attributes " "are not specified while creating external networks then they will have the " "same type as tenant networks. Allowed values for external_network_type " "config option depend on the network type values configured in type_drivers " "config option." msgstr "" "Тип по умолчанию Ð´Ð»Ñ Ð²Ð½ÐµÑˆÐ½Ð¸Ñ… Ñетей, еÑли не указаны атрибуты провайдера. " "Значение по умолчанию Ðет, означающее, что еÑли во Ð²Ñ€ÐµÐ¼Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð²Ð½ÐµÑˆÐ½Ð¸Ñ… " "Ñетей не были указаны атрибуты провайдера, то их тип будет Ñовпадать Ñ Ñ‚Ð¸Ð¿Ð¾Ð¼ " "Ñетей арендатора. Разрешенные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¾Ð¿Ñ†Ð¸Ð¸ конфигурации " "external_network_type завиÑÑÑ‚ от значений типа Ñети, наÑтроенных в опции " "конфигурации type_drivers." msgid "" "Default number of RBAC entries allowed per tenant. A negative value means " "unlimited." msgstr "" "КоличеÑтво запиÑей RBAC по умолчанию на одного арендатора. Отрицательное " "значение - без ограничений." msgid "" "Default number of resource allowed per tenant. A negative value means " "unlimited." msgstr "" "КоличеÑтво реÑурÑов по умолчанию на одного арендатора. Отрицательное " "значение - не ограничено." msgid "Default security group" msgstr "Группа защиты по умолчанию" msgid "Default security group already exists." msgstr "Группа защиты по умолчанию уже ÑущеÑтвует." msgid "" "Default value of availability zone hints. The availability zone aware " "schedulers use this when the resources availability_zone_hints is empty. " "Multiple availability zones can be specified by a comma separated string. " "This value can be empty. In this case, even if availability_zone_hints for a " "resource is empty, availability zone is considered for high availability " "while scheduling the resource." msgstr "" "Значение по умолчанию параметра availability_zone_hints. Планировщики, " "учитывающие зону доÑтупноÑти, иÑпользуют Ñтот параметр, когда параметр " "availability_zone_hints Ð´Ð»Ñ Ñ€ÐµÑурÑов пуÑтой. ÐеÑколько зон доÑтупноÑти " "разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтыми. Значение может быть пуÑтым. Ð’ Ñтом Ñлучае, даже еÑли " "параметр availability_zone_hints Ð´Ð»Ñ Ñ€ÐµÑурÑа пуÑтой, зона доÑтупноÑти " "ÑчитаетÑÑ Ð¿Ñ€Ð¸Ð³Ð¾Ð´Ð½Ð¾Ð¹ Ð´Ð»Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¹ выÑокой готовноÑти при планировании реÑурÑа." msgid "" "Define the default value of enable_snat if not provided in " "external_gateway_info." msgstr "" "Определить значение по умолчанию enable_snat, еÑли оно не указано в " "external_gateway_info." msgid "" "Defines providers for advanced services using the format: :" ":[:default]" msgstr "" "ОпределÑет поÑтавщиков Ð´Ð»Ñ Ñ€Ð°Ñширенных Ñлужб в формате: :" ":[:default]" msgid "Delete the namespace by removing all devices." msgstr "Удалите проÑтранÑтво имен, удалив вÑе уÑтройÑтва." #, python-format msgid "Deleting port %s" msgstr "Удаление порта %s" #, python-format msgid "Deployment error: %(reason)s." msgstr "Ошибка развертываниÑ: %(reason)s." msgid "Destroy IPsets even if there is an iptables reference." msgstr "Уничтожить IPset даже при наличии ÑÑылок iptables." msgid "Destroy all IPsets." msgstr "Уничтожить вÑе IPset." #, python-format msgid "Device %(dev_name)s in mapping: %(mapping)s not unique" msgstr "УÑтройÑтво %(dev_name)s в карте ÑвÑзей %(mapping)s неуникально" #, python-format msgid "Device name %(dev_name)s is missing from physical_device_mappings" msgstr "Ð˜Ð¼Ñ ÑƒÑтройÑтва %(dev_name)s не указано в physical_device_mappings" msgid "Device not found" msgstr "УÑтройÑтво не найдено" #, python-format msgid "" "Distributed Virtual Router Mac Address for host %(host)s does not exist." msgstr "" "MAC-Ð°Ð´Ñ€ÐµÑ Ñ€Ð°Ñпределенного виртуального маршрутизатора Ð´Ð»Ñ Ñ…Ð¾Ñта %(host)s не " "ÑущеÑтвует." msgid "Domain to use for building the hostnames" msgstr "Домен, иÑпользуемый Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð½Ð¾Ð²ÐºÐ¸ имен хоÑтов" msgid "Downgrade no longer supported" msgstr "Понижение больше не поддерживаетÑÑ" #, python-format msgid "Driver %s is not unique across providers" msgstr "Драйвер %s не ÑвлÑетÑÑ ÑƒÐ½Ð¸ÐºÐ°Ð»ÑŒÐ½Ñ‹Ð¼ Ñреди поÑтавщиков" msgid "Driver for external DNS integration." msgstr "Драйвер Ð´Ð»Ñ Ð¸Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ð¸ Ñ Ð²Ð½ÐµÑˆÐ½Ð¸Ð¼ DNS." msgid "Driver for security groups firewall in the L2 agent" msgstr "Драйвер Ð´Ð»Ñ Ð±Ñ€Ð°Ð½Ð´Ð¼Ð°ÑƒÑра групп защиты в агенте L2" msgid "Driver to use for scheduling network to DHCP agent" msgstr "Драйвер, иÑпользуемый Ð´Ð»Ñ Ð¿Ð»Ð°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñети Ð´Ð»Ñ Ð°Ð³ÐµÐ½Ñ‚Ð° DHCP" msgid "Driver to use for scheduling router to a default L3 agent" msgstr "" "Драйвер, иÑпользуемый Ð´Ð»Ñ Ð¿Ð»Ð°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¼Ð°Ñ€ÑˆÑ€ÑƒÑ‚Ð¸Ð·Ð°Ñ‚Ð¾Ñ€Ð° Ð´Ð»Ñ Ð°Ð³ÐµÐ½Ñ‚Ð° L3 по " "умолчанию" msgid "" "Driver used for ipv6 prefix delegation. This needs to be an entry point " "defined in the neutron.agent.linux.pd_drivers namespace. See setup.cfg for " "entry points included with the neutron source." msgstr "" "Драйвер Ð´Ð»Ñ Ð´ÐµÐ»ÐµÐ³Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑа ipv6. Должен быть точкой входа, " "определенной в проÑтранÑтве имен neutron.agent.linux.pd_drivers. См. файл " "setup.cfg на наличие точек входа, включенных в иÑходный код neutron." #, python-format msgid "" "Duplicate L3HARouterAgentPortBinding is created for router(s) %(router)s. " "Database cannot be upgraded. Please, remove all duplicates before upgrading " "the database." msgstr "" "Обнаружен повторÑющийÑÑ L3HARouterAgentPortBinding Ð´Ð»Ñ Ð¼Ð°Ñ€ÑˆÑ€ÑƒÑ‚Ð¸Ð·Ð°Ñ‚Ð¾Ñ€Ð¾Ð² " "%(router)s. Обновление базы данных невозможно. Перед обновлением базы данных " "уÑтраните вÑе повторы." msgid "Duplicate Security Group Rule in POST." msgstr "Совпадающие правила группы защиты в POST." msgid "Duplicate address detected" msgstr "ПовторÑющийÑÑ Ð°Ð´Ñ€ÐµÑ" msgid "Duplicate segment entry in request." msgstr "Дубликат запиÑи Ñегмента в запроÑе." #, python-format msgid "ERROR: %s" msgstr "Ошибка: %s" msgid "" "ERROR: Unable to find configuration file via the default search paths (~/." "neutron/, ~/, /etc/neutron/, /etc/) and the '--config-file' option!" msgstr "" "ОШИБКÐ: Ðе удалоÑÑŒ найти файл конфигурации Ñ Ð¸Ñпользованием путей поиÑка по " "умолчанию (~/.neutron/, ~/, /etc/neutron/, /etc/) и Ð¾Ð¿Ñ†Ð¸Ñ '--config-file'!" msgid "" "Either one of parameter network_id or router_id must be passed to _get_ports " "method." msgstr "" "Либо один из параметров network_id, либо router_id должен быть передан в " "метод _get_ports." msgid "Either subnet_id or port_id must be specified" msgstr "Ðеобходимо указать или subnet_id, или port_id" msgid "Empty physical network name." msgstr "ПуÑтое Ð¸Ð¼Ñ Ñ„Ð¸Ð·Ð¸Ñ‡ÐµÑкой Ñети." msgid "Empty subnet pool prefix list." msgstr "ПуÑтой ÑпиÑок префикÑов пула подÑетей." msgid "Enable HA mode for virtual routers." msgstr "Включить режим выÑокой готовноÑти Ð´Ð»Ñ Ð²Ð¸Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ñ‹Ñ… маршрутизаторов." msgid "Enable SSL on the API server" msgstr "Разрешить применение SSL на Ñервере API" msgid "" "Enable VXLAN on the agent. Can be enabled when agent is managed by ml2 " "plugin using linuxbridge mechanism driver" msgstr "" "Ðктивируйте VXLAN на агенте. ÐÐºÑ‚Ð¸Ð²Ð°Ñ†Ð¸Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð°, еÑли агентом управлÑет " "модуль ml2, иÑпользующий драйвер механизма linuxbridge" msgid "" "Enable local ARP responder if it is supported. Requires OVS 2.1 and ML2 " "l2population driver. Allows the switch (when supporting an overlay) to " "respond to an ARP request locally without performing a costly ARP broadcast " "into the overlay." msgstr "" "Включить локальный промежуточный клиент ARP, еÑли он поддерживаетÑÑ. " "ТребуетÑÑ OVS 2.1 и драйвер ML2 l2population. ПозволÑет коммутатору (когда " "поддерживаетÑÑ Ð¿ÐµÑ€ÐµÐºÑ€Ñ‹Ñ‚Ð¸Ðµ) отвечать на Ð·Ð°Ð¿Ñ€Ð¾Ñ ARP локально, без Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ " "дорогоÑтоÑщего Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ñ ARP в перекрытии." msgid "" "Enable services on an agent with admin_state_up False. If this option is " "False, when admin_state_up of an agent is turned False, services on it will " "be disabled. Agents with admin_state_up False are not selected for automatic " "scheduling regardless of this option. But manual scheduling to such agents " "is available if this option is True." msgstr "" "Включить Ñлужбы на агенте Ñ admin_state_up False. ЕÑли Ñта Ð¾Ð¿Ñ†Ð¸Ñ Ñ€Ð°Ð²Ð½Ð° " "False, когда admin_state_up агента уÑтанавливаетÑÑ False, Ñлужбы на нем " "будут выключены. Ðгенты Ñ admin_state_up False не выбраны Ð´Ð»Ñ " "автоматичеÑкого Ð¿Ð»Ð°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½ÐµÐ·Ð°Ð²Ð¸Ñимо от Ñтой опции. Ðо ручное " "планирование Ð´Ð»Ñ Ñ‚Ð°ÐºÐ¸Ñ… агентов доÑтупно, еÑли Ð¾Ð¿Ñ†Ð¸Ñ Ñ€Ð°Ð²Ð½Ð° True." msgid "" "Enables IPv6 Prefix Delegation for automatic subnet CIDR allocation. Set to " "True to enable IPv6 Prefix Delegation for subnet allocation in a PD-capable " "environment. Users making subnet creation requests for IPv6 subnets without " "providing a CIDR or subnetpool ID will be given a CIDR via the Prefix " "Delegation mechanism. Note that enabling PD will override the behavior of " "the default IPv6 subnetpool." msgstr "" "Разрешает делегирование префикÑа IPv6 при автоматичеÑком выделении CIDR " "подÑети. ПриÑвойте параметру значение True, чтобы включить делегирование " "префикÑа IPv6 при выделении Ñети в Ñреде Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¾Ð¹ Ð´ÐµÐ»ÐµÐ³Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ " "префикÑа. Пользователи, запрашивающие Ñоздание подÑети IPv6 без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ " "CIDR или ИД пула подÑетей, получают CIDR поÑредÑтвом Ð´ÐµÐ»ÐµÐ³Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑа. " "Учтите, что включение Ð´ÐµÐ»ÐµÐ³Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑа переопределÑет Ñтандартное " "поведение пула подÑетей IPv6." msgid "" "Enables the dnsmasq service to provide name resolution for instances via DNS " "resolvers on the host running the DHCP agent. Effectively removes the '--no-" "resolv' option from the dnsmasq process arguments. Adding custom DNS " "resolvers to the 'dnsmasq_dns_servers' option disables this feature." msgstr "" "Включает Ñлужбу dnsmasq Ð´Ð»Ñ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ запроÑов DNS на хоÑте, на котором " "работает агент DHCP. Ðннулирует дейÑтвие опции '--no-resolv' в аргументах " "процеÑÑа dnsmasq. Эта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²Ñ‹ÐºÐ»ÑŽÑ‡Ð°ÐµÑ‚ÑÑ, еÑли в опцию " "'dnsmasq_dns_servers' добавлÑÑŽÑ‚ÑÑ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒÑкие обработчики запроÑов DNS." msgid "End of VLAN range is less than start of VLAN range" msgstr "Конечное значение диапазона VLAN меньше его начального значениÑ" msgid "End of tunnel range is less than start of tunnel range" msgstr "Конечное значение диапазона туннелей меньше его начального значениÑ" #, python-format msgid "Error %(reason)s while attempting the operation." msgstr "Ошибка %(reason)s во Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¸." #, python-format msgid "Error parsing dns address %s" msgstr "Ошибка при анализе адреÑа dns %s" #, python-format msgid "Error while reading %s" msgstr "Ошибка при чтении %s" #, python-format msgid "" "Exceeded %s second limit waiting for address to leave the tentative state." msgstr "" "Превышено Ð²Ñ€ÐµÐ¼Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð²Ñ‹Ñ…Ð¾Ð´Ð° адреÑа из временного ÑоÑтоÑÐ½Ð¸Ñ ( %s Ñекунд)" msgid "Existing prefixes must be a subset of the new prefixes" msgstr "СущеÑтвующие префикÑÑ‹ должны быть подмножеÑтвом новых префикÑов" #, python-format msgid "" "Exit code: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" msgstr "" "Код возврата: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" #, python-format msgid "Extension %(driver)s failed." msgstr "Сбой раÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ %(driver)s." #, python-format msgid "" "Extension driver %(driver)s required for service plugin %(service_plugin)s " "not found." msgstr "" "Ðе найден драйвер раÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ %(driver)s, необходимый Ð´Ð»Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ Ñлужбы " "%(service_plugin)s." msgid "" "Extension to use alongside ml2 plugin's l2population mechanism driver. It " "enables the plugin to populate VXLAN forwarding table." msgstr "" "РаÑширение Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½Ð°Ñ€Ñду Ñ Ð´Ñ€Ð°Ð¹Ð²ÐµÑ€Ð¾Ð¼ механизма l2population " "Ð¼Ð¾Ð´ÑƒÐ»Ñ ml2. Оно обеÑпечивает заполнение модулем таблицы переÑылки VXLAN." #, python-format msgid "Extension with alias %s does not exist" msgstr "РаÑширение Ñ Ð¿Ñевдонимом %s не ÑущеÑтвует" msgid "Extensions list to use" msgstr "СпиÑок иÑпользуемых раÑширений" #, python-format msgid "Extensions not found: %(extensions)s." msgstr "РаÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ Ð½Ðµ найдены: %(extensions)s." #, python-format msgid "External IP %s is the same as the gateway IP" msgstr "Внешний IP-Ð°Ð´Ñ€ÐµÑ %s Ñовпадает Ñ IP-адреÑом шлюза" #, python-format msgid "Failed rescheduling router %(router_id)s: no eligible l3 agent found." msgstr "" "Ðе удалоÑÑŒ перепланировать маршрутизатор %(router_id)s: не найден допуÑтимый " "агент L3." #, python-format msgid "Failed scheduling router %(router_id)s to the L3 Agent %(agent_id)s." msgstr "" "Ðе удалоÑÑŒ запланировать маршрутизатор %(router_id)s Ð´Ð»Ñ Ð°Ð³ÐµÐ½Ñ‚Ð° L3 " "%(agent_id)s." #, python-format msgid "Failed to allocate subnet: %(reason)s." msgstr "Ðе удалоÑÑŒ выделить подÑеть: %(reason)s." msgid "" "Failed to associate address scope: subnetpools within an address scope must " "have unique prefixes." msgstr "" "Ðе удалоÑÑŒ ÑвÑзать облаÑть адреÑов: пулы подÑетей в облаÑти адреÑов должны " "иметь уникальные префикÑÑ‹." #, python-format msgid "" "Failed to create port on network %(network_id)s, because fixed_ips included " "invalid subnet %(subnet_id)s" msgstr "" "Ðе удалоÑÑŒ Ñоздать порт в Ñети %(network_id)s, так как fixed_ips Ñодержат " "недопуÑтимую подÑеть %(subnet_id)s" #, python-format msgid "Failed to locate source for %s." msgstr "Ðе удалоÑÑŒ найти иÑточник Ð´Ð»Ñ %s." msgid "Failed to remove supplemental groups" msgstr "Ðе удалоÑÑŒ удалить дополнительные группы" #, python-format msgid "Failed to set gid %s" msgstr "Ðе удалоÑÑŒ получить gid %s" #, python-format msgid "Failed to set uid %s" msgstr "Ðе удалоÑÑŒ задать uid %s" #, python-format msgid "Failed to set-up %(type)s tunnel port to %(ip)s" msgstr "Ðе удалоÑÑŒ наÑтроить порт Ñ‚ÑƒÐ½Ð½ÐµÐ»Ñ %(type)s на %(ip)s" msgid "Failure applying iptables rules" msgstr "Ðе удалоÑÑŒ применить правила iptables" #, python-format msgid "Failure waiting for address %(address)s to become ready: %(reason)s" msgstr "Сбой Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð³Ð¾Ñ‚Ð¾Ð²Ð½Ð¾Ñти адреÑа %(address)s: %(reason)s" msgid "Flat provider networks are disabled" msgstr "Одноуровневые Ñети выключены" msgid "For TCP/UDP protocols, port_range_min must be <= port_range_max" msgstr "" "Ð”Ð»Ñ Ð¿Ñ€Ð¾Ñ‚Ð¾ÐºÐ¾Ð»Ð¾Ð² TCP/UDP значение port_range_min должно быть <= port_range_max" msgid "Force ip_lib calls to use the root helper" msgstr "" "ИÑпользовать в вызовах ip_lib вÑпомогательную программу Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð°Ð² " "доÑтупа root" #, python-format msgid "Found duplicate extension: %(alias)s." msgstr "Ðайдены повторÑющиеÑÑ Ñ€Ð°ÑширениÑ: %(alias)s." #, python-format msgid "" "Found overlapping allocation pools: %(pool_1)s %(pool_2)s for subnet " "%(subnet_cidr)s." msgstr "" "Обнаружено перекрытие пулов Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ %(pool_1)s %(pool_2)s Ð´Ð»Ñ Ð¿Ð¾Ð´Ñети " "%(subnet_cidr)s." msgid "Gateway IP version inconsistent with allocation pool version" msgstr "ВерÑÐ¸Ñ IP шлюза неÑовмеÑтима Ñ Ð²ÐµÑ€Ñией Ð´Ð»Ñ Ð¿ÑƒÐ»Ð° Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð°Ð´Ñ€ÐµÑов" #, python-format msgid "Gateway ip %(ip_address)s conflicts with allocation pool %(pool)s." msgstr "IP-Ð°Ð´Ñ€ÐµÑ ÑˆÐ»ÑŽÐ·Ð° %(ip_address)s конфликтует Ñ Ð¿ÑƒÐ»Ð¾Ð¼ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ %(pool)s." msgid "Gateway is not valid on subnet" msgstr "Шлюз недопуÑтим в подÑети" msgid "" "Geneve encapsulation header size is dynamic, this value is used to calculate " "the maximum MTU for the driver. This is the sum of the sizes of the outer " "ETH + IP + UDP + GENEVE header sizes. The default size for this field is 50, " "which is the size of the Geneve header without any additional option headers." msgstr "" "Размер заголовка Geneve менÑетÑÑ Ð´Ð¸Ð½Ð°Ð¼Ð¸Ñ‡ÐµÑки, и его значение иÑпользуетÑÑ " "при вычиÑлении MTU Ð´Ð»Ñ Ð´Ñ€Ð°Ð¹Ð²ÐµÑ€Ð°. Сюда входит Ñумма размеров заголовков ETH + " "IP + UDP + GENEVE. Размер Ð¿Ð¾Ð»Ñ Ð¿Ð¾ умолчанию равен 50, Ñто размер заголовков " "Geneve без заголовков дополнительных опций." msgid "" "Group (gid or name) running metadata proxy after its initialization (if " "empty: agent effective group)." msgstr "" "Группа (gid или имÑ) иÑпользует proxy метаданных поÑле инициализации (еÑли " "пуÑтое, иÑпользуетÑÑ Ð³Ñ€ÑƒÐ¿Ð¿Ð° агента). " msgid "Group (gid or name) running this process after its initialization" msgstr "Группа (gid или имÑ) запуÑкает Ñтот процеÑÑ Ð¿Ð¾Ñле инициализации" msgid "" "Hostname to be used by the Neutron server, agents and services running on " "this machine. All the agents and services running on this machine must use " "the same host value." msgstr "" "Ð˜Ð¼Ñ Ñ…Ð¾Ñта Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñервером Neutron, агентами и Ñлужбами, " "запущенными в Ñтой ÑиÑтеме. Ð’Ñе агенты и Ñлужбы, запущенные в Ñтой ÑиÑтеме, " "должны иÑпользовать одно и то же значение хоÑта." #, python-format msgid "" "ICMP code (port-range-max) %(value)s is provided but ICMP type (port-range-" "min) is missing." msgstr "" "Код ICMP (port-range-max) %(value)s указан, но тип ICMP (port-range-min) " "отÑутÑтвует." msgid "ID of network" msgstr "ИД Ñети" msgid "ID of network to probe" msgstr "ИД Ñети Ð´Ð»Ñ Ñ‚ÐµÑтированиÑ" msgid "ID of probe port to delete" msgstr "ИД удалÑемого теÑтового порта" msgid "ID of probe port to execute command" msgstr "ИД теÑтового порта Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹" msgid "ID of the router" msgstr "ИД маршрутизатора" #, python-format msgid "IP address %(ip)s already allocated in subnet %(subnet_id)s" msgstr "IP-Ð°Ð´Ñ€ÐµÑ %(ip)s уже выделен в подÑети %(subnet_id)s" #, python-format msgid "IP address %(ip)s does not belong to subnet %(subnet_id)s" msgstr "IP-Ð°Ð´Ñ€ÐµÑ %(ip)s не принадлежит подÑети %(subnet_id)s" msgid "IP allocation failed. Try again later." msgstr "Ðе удалоÑÑŒ выделить IP-адреÑ. Повторите попытку позже." msgid "IP allocation requires subnet_id or ip_address" msgstr "Ð”Ð»Ñ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ IP-адреÑа требуетÑÑ subnet_id или ip_address" #, python-format msgid "" "IPTablesManager.apply failed to apply the following set of iptables rules:\n" "%s" msgstr "" "Функции IPTablesManager.apply не удалоÑÑŒ применить Ñледующий набор правил " "iptables :\n" "%s" msgid "IPtables conntrack zones exhausted, iptables rules cannot be applied." msgstr "" "Зоны ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»Ñ ÑоÑтоÑÐ½Ð¸Ñ Ñоединений IPtables conntrack израÑходованы, правила " "iptables не могут быть применены." msgid "IPv6 Address Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "ДопуÑтимое значение режима адреÑов IPv6 Ð´Ð»Ñ Ð´ÐµÐ»ÐµÐ³Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑа: SLAAC " "или без ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÑоÑтоÑниÑ." msgid "IPv6 RA Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "ДопуÑтимое значение режима RA IPv6 Ð´Ð»Ñ Ð´ÐµÐ»ÐµÐ³Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑа: SLAAC или без " "ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÑоÑтоÑниÑ." #, python-format msgid "" "IPv6 address %(ip)s cannot be directly assigned to a port on subnet " "%(subnet_id)s as the subnet is configured for automatic addresses" msgstr "" "ÐÐ´Ñ€ÐµÑ IPv6 %(ip)s Ð½ÐµÐ»ÑŒÐ·Ñ Ð½Ð°Ð¿Ñ€Ñмую ÑвÑзывать Ñ Ð¿Ð¾Ñ€Ñ‚Ð¾Ð¼ в подÑети " "%(subnet_id)s, так как подÑеть наÑтроена Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкого Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ " "адреÑов" #, python-format msgid "" "IPv6 subnet %s configured to receive RAs from an external router cannot be " "added to Neutron Router." msgstr "" "ПодÑеть IPv6 %s, наÑÑ‚Ñ€Ð¾ÐµÐ½Ð½Ð°Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¸ÐµÐ¼Ð° RA из внешнего маршрутизатора, не " "может быть добавлена в маршрутизатор Neutron." msgid "" "If True, then allow plugins that support it to create VLAN transparent " "networks." msgstr "" "ЕÑли True, разрешаютÑÑ Ð¼Ð¾Ð´ÑƒÐ»Ð¸, поддерживающие Ñоздание прозрачных Ñетей VLAN." msgid "Illegal IP version number" msgstr "Запрещенный номер верÑии IP" #, python-format msgid "" "Illegal prefix bounds: %(prefix_type)s=%(prefixlen)s, %(base_prefix_type)s=" "%(base_prefixlen)s." msgstr "" "ÐедопуÑтимые Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑа: %(prefix_type)s=%(prefixlen)s, " "%(base_prefix_type)s=%(base_prefixlen)s." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot " "associate with address scope %(address_scope_id)s because subnetpool " "ip_version is not %(ip_version)s." msgstr "" "ÐедопуÑÑ‚Ð¸Ð¼Ð°Ñ ÑвÑзь пула подÑетей: пул подÑетей %(subnetpool_id)s не может " "быть ÑвÑзан Ñ Ð¾Ð±Ð»Ð°Ñтью адреÑов %(address_scope_id)s, поÑкольку ip_version " "Ð´Ð»Ñ Ð¿ÑƒÐ»Ð° подÑетей не равен %(ip_version)s." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot be " "associated with address scope %(address_scope_id)s." msgstr "" "ÐедопуÑÑ‚Ð¸Ð¼Ð°Ñ ÑвÑзь пула подÑетей: пул подÑетей %(subnetpool_id)s не может " "быть ÑвÑзан Ñ Ð¾Ð±Ð»Ð°Ñтью адреÑов %(address_scope_id)s." #, python-format msgid "Illegal subnetpool update : %(reason)s." msgstr "ÐедопуÑтимое обновление пула подÑетей: %(reason)s." #, python-format msgid "Illegal update to prefixes: %(msg)s." msgstr "ÐедопуÑтимое обновление префикÑов: %(msg)s." msgid "" "In some cases the Neutron router is not present to provide the metadata IP " "but the DHCP server can be used to provide this info. Setting this value " "will force the DHCP server to append specific host routes to the DHCP " "request. If this option is set, then the metadata service will be activated " "for all the networks." msgstr "" "Ð’ некоторых ÑитуациÑÑ… машрутизатор Neutron отÑутÑтвует и не предоÑтавлÑет " "метаданные Ð´Ð»Ñ IP, но Ñту задачу решает Ñервер DHCP. ЕÑли задан Ñтот " "параметр, то Ñервер DHCP будет добавлÑть маршруты к хоÑтам в Ð·Ð°Ð¿Ñ€Ð¾Ñ DHCP. С " "Ñтим параметром Ñлужба метаданных будет активирована Ð´Ð»Ñ Ð²Ñех Ñетей." msgid "" "Indicates that this L3 agent should also handle routers that do not have an " "external network gateway configured. This option should be True only for a " "single agent in a Neutron deployment, and may be False for all agents if all " "routers must have an external network gateway." msgstr "" "Указывает, что агент L3 должен также обрабатывать маршрутизаторы, Ð´Ð»Ñ " "которых не наÑтроен шлюз во внешнюю Ñеть. Опции Ñледует приÑвоить значение " "True, еÑли в развертывании Neutron учаÑтвует только один агент. Опции можно " "приÑвоить значение False Ð´Ð»Ñ Ð²Ñех агентов, еÑли вÑе маршрутизаторы должны " "иметь шлюз во внешнюю Ñеть." #, python-format msgid "Instance of class %(module)s.%(class)s must contain _cache attribute" msgstr "" "ЭкземплÑÑ€ клаÑÑа %(module)s.%(class)s должен Ñодержать _cache attribute" #, python-format msgid "Insufficient prefix space to allocate subnet size /%s" msgstr "ÐедоÑтаточное проÑтранÑтво префикÑов Ð´Ð»Ñ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð¼ÐµÑ€Ð° Ñети /%s" msgid "Insufficient rights for removing default security group." msgstr "" "ОтÑутÑтвуют требуемые права доÑтупа Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ защиты по умолчанию." msgid "" "Integration bridge to use. Do not change this parameter unless you have a " "good reason to. This is the name of the OVS integration bridge. There is one " "per hypervisor. The integration bridge acts as a virtual 'patch bay'. All VM " "VIFs are attached to this bridge and then 'patched' according to their " "network connectivity." msgstr "" "ИÑпользуемый моÑÑ‚ интеграции. Ðе изменÑйте Ñтот параметр без Ñерьезных " "причин. Это Ð¸Ð¼Ñ Ð¼Ð¾Ñта интеграции OVS. Ð”Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ гипервизора предуÑмотрен " "один моÑÑ‚. МоÑÑ‚ интеграции работает как Ð²Ð¸Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ð°Ñ ÐºÐ¾Ð¼Ð¼ÑƒÑ‚Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ Ð¿Ð°Ð½ÐµÐ»ÑŒ. " "Ð’Ñе виртуальные интерфейÑÑ‹ VM подключаютÑÑ Ðº Ñтому моÑту и затем " "коммутируютÑÑ ÑоглаÑно топологии Ñети." msgid "Interface to monitor" msgstr "Ð˜Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð´Ð»Ñ Ð¼Ð¾Ð½Ð¸Ñ‚Ð¾Ñ€Ð°" msgid "" "Interval between checks of child process liveness (seconds), use 0 to disable" msgstr "" "Интервал между проверками работы дочернего процеÑÑа (в Ñекундах), 0 Ð´Ð»Ñ " "отключениÑ" msgid "Interval between two metering measures" msgstr "Интервал между Ð´Ð²ÑƒÐ¼Ñ Ð¿Ð¾ÐºÐ°Ð·Ð°Ñ‚ÐµÐ»Ñми измерений" msgid "Interval between two metering reports" msgstr "Интервал между Ð´Ð²ÑƒÐ¼Ñ Ð¾Ñ‚Ñ‡ÐµÑ‚Ð°Ð¼Ð¸ измерений" #, python-format msgid "Invalid CIDR %(input)s given as IP prefix." msgstr "Указан недопуÑтимый CIDR %(input)s как Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ IP-адреÑа." #, python-format msgid "Invalid Device %(dev_name)s: %(reason)s" msgstr "ÐедопуÑтимое уÑтройÑтво %(dev_name)s: %(reason)s" #, python-format msgid "" "Invalid action '%(action)s' for object type '%(object_type)s'. Valid " "actions: %(valid_actions)s" msgstr "" "ÐедопуÑтимое дейÑтвие '%(action)s' Ð´Ð»Ñ Ñ‚Ð¸Ð¿Ð° объекта '%(object_type)s'. " "ДопуÑтимые дейÑтвиÑ: %(valid_actions)s" #, python-format msgid "" "Invalid authentication type: %(auth_type)s, valid types are: " "%(valid_auth_types)s" msgstr "" "ÐедопуÑтимый тип идентификации: %(auth_type)s. ДопуÑтимые типы: " "%(valid_auth_types)s" #, python-format msgid "Invalid ethertype %(ethertype)s for protocol %(protocol)s." msgstr "ÐедопуÑтимый тип %(ethertype)s Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ‚Ð¾ÐºÐ¾Ð»Ð° %(protocol)s." #, python-format msgid "Invalid format: %s" msgstr "Ðеправильный формат: %s" #, python-format msgid "Invalid instance state: %(state)s, valid states are: %(valid_states)s" msgstr "" "ÐедопуÑтимое ÑоÑтоÑние ÑкземплÑра: %(state)s. ДопуÑтимые ÑоÑтоÑниÑ: " "%(valid_states)s" #, python-format msgid "Invalid mapping: '%s'" msgstr "ÐедопуÑтимое отображение: '%s'" #, python-format msgid "Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'." msgstr "ÐедопуÑтимый диапазон VLAN Ñети: '%(vlan_range)s' - '%(error)s'." #, python-format msgid "Invalid network VXLAN port range: '%(vxlan_range)s'." msgstr "ÐедопуÑтимый диапазон портов Ñети VXLAN: '%(vxlan_range)s'." #, python-format msgid "Invalid pci slot %(pci_slot)s" msgstr "ÐедопуÑтимый разъем pci %(pci_slot)s" #, python-format msgid "Invalid provider format. Last part should be 'default' or empty: %s" msgstr "" "ÐедопуÑтимый формат поÑтавщика. ПоÑледнÑÑ Ñ‡Ð°Ñть должна иметь вид 'default' " "или быть пуÑтой: %s" #, python-format msgid "Invalid resource type %(resource_type)s" msgstr "ÐедопуÑтимый тип реÑурÑа %(resource_type)s" #, python-format msgid "Invalid route: %s" msgstr "ÐедопуÑтимый маршрут: %s" msgid "Invalid service provider format" msgstr "ÐедопуÑтимый формат поÑтавщика Ñлужбы" #, python-format msgid "" "Invalid value for ICMP %(field)s (%(attr)s) %(value)s. It must be 0 to 255." msgstr "" "ÐедопуÑтимое значение Ð´Ð»Ñ ICMP %(field)s (%(attr)s) %(value)s. Значение " "должно лежать в диапазоне от 0 до 255." #, python-format msgid "Invalid value for port %(port)s" msgstr "ÐедопуÑтимое значение Ð´Ð»Ñ Ð¿Ð¾Ñ€Ñ‚Ð° %(port)s" msgid "" "Iptables mangle mark used to mark ingress from external network. This mark " "will be masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Пометка mangle в iptables применÑетÑÑ Ð´Ð»Ñ Ð¿Ð¾Ð¼ÐµÑ‚ÐºÐ¸ входа из внешней Ñети. Эта " "пометка будет применÑтьÑÑ Ñ Ð¼Ð°Ñкой 0xffff Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ младших " "16 бит." msgid "" "Iptables mangle mark used to mark metadata valid requests. This mark will be " "masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Пометка mangle в Iptables, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ Ð´Ð»Ñ Ð¿Ð¾Ð¼ÐµÑ‚ÐºÐ¸ допуÑтимых запроÑов " "метаданных. Эта пометка будет применÑтьÑÑ Ñ Ð¼Ð°Ñкой 0xffff Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ " "только младших 16 бит." msgid "Keepalived didn't respawn" msgstr "Демон keepalived не выполнил повторное порождение" msgid "Keepalived didn't spawn" msgstr "Демон keepalived не запуÑтилÑÑ" #, python-format msgid "" "Kernel HZ value %(value)s is not valid. This value must be greater than 0." msgstr "" "ÐедопуÑтимое значение HZ Ñдра %(value)s. Значение должно быть больше 0." msgid "L3 agent failure to setup NAT for floating IPs" msgstr "Ошибка агента L3 при наÑтройке NAT Ð´Ð»Ñ Ð½ÐµÑ„Ð¸ÐºÑированных IP" msgid "L3 agent failure to setup floating IPs" msgstr "Ошибка агента L3 при наÑтройке нефикÑированных IP" msgid "Limit number of leases to prevent a denial-of-service." msgstr "Ограничить чиÑло выделений во избежание отказа в обÑлуживании." msgid "List of :" msgstr "СпиÑок <физичеÑкаÑ-Ñеть>:<физичеÑкий-моÑÑ‚>" msgid "" "List of :: or " "specifying physical_network names usable for VLAN provider and tenant " "networks, as well as ranges of VLAN tags on each available for allocation to " "tenant networks." msgstr "" "СпиÑок :: или , " "Ñодержащий имена физичеÑких Ñетей, которые могут иÑпользоватьÑÑ Ð´Ð»Ñ Ñетей " "VLAN провайдера и арендатора, а также диапазоны тегов VLAN Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ Ñети, " "доÑтупной Ð´Ð»Ñ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð°Ñ€ÐµÐ½Ð´Ð°Ñ‚Ð¾Ñ€Ð°Ð¼." msgid "" "List of network type driver entrypoints to be loaded from the neutron.ml2." "type_drivers namespace." msgstr "" "СпиÑок конечных точек драйвера типа Ñети, загружаемых из проÑтранÑтва имен " "neutron.ml2.type_drivers." msgid "" "List of physical_network names with which flat networks can be created. Use " "default '*' to allow flat networks with arbitrary physical_network names. " "Use an empty list to disable flat networks." msgstr "" "СпиÑок имен физичеÑких Ñетей, Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¼Ð¸ можно Ñоздавать одноуровневые Ñети. " "Ð”Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¾Ð´Ð½Ð¾ÑƒÑ€Ð¾Ð²Ð½ÐµÐ²Ñ‹Ñ… Ñетей Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð»ÑŒÐ½Ñ‹Ð¼Ð¸ именами физичеÑких Ñетей " "иÑпользуйте Ñимвол *. ПуÑтой ÑпиÑок запрещает Ñоздание одноуровневых Ñетей." msgid "Location for Metadata Proxy UNIX domain socket." msgstr "РаÑположение Ñокета домена UNIX прокÑи метаданных. " msgid "Location of Metadata Proxy UNIX domain socket" msgstr "РаÑположение Ñокета домена UNIX прокÑи метаданных" msgid "Location to store DHCP server config files." msgstr "РаÑположение Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² конфигурации Ñервера DHCP." msgid "Location to store IPv6 PD files." msgstr "РаÑположение Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² PD IPv6." msgid "Location to store IPv6 RA config files" msgstr "РаÑположение Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² конфигурации RA IPv6" msgid "Location to store child pid files" msgstr "РаÑположение Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð´Ð¾Ñ‡ÐµÑ€Ð½Ð¸Ñ… файлов pid" msgid "Location to store keepalived/conntrackd config files" msgstr "РаÑположение Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² конфигурации keepalived/conntrackd" msgid "Log agent heartbeats" msgstr "ВеÑти протокол периодичеÑких Ñигналов агента" msgid "" "MTU of the underlying physical network. Neutron uses this value to calculate " "MTU for all virtual network components. For flat and VLAN networks, neutron " "uses this value without modification. For overlay networks such as VXLAN, " "neutron automatically subtracts the overlay protocol overhead from this " "value. Defaults to 1500, the standard value for Ethernet." msgstr "" "MTU физичеÑкой Ñреды. Neutron иÑпользует Ñто значение Ð´Ð»Ñ Ñ€Ð°Ñчета MTU Ð´Ð»Ñ " "вÑех компонентов виртуальной Ñети. Ð”Ð»Ñ Ð¿Ñ€Ð¾Ñтых Ñетей и VLAN Neutron не " "изменÑет Ñто значение. Ð”Ð»Ñ Ñетей Ñ Ð¿ÐµÑ€ÐµÐºÑ€Ñ‹Ñ‚Ð¸ÐµÐ¼, таких как VXLAN, Neutron " "автоматичеÑки вычитает байты, необходимые Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ‚Ð¾ÐºÐ¾Ð»Ð° перекрытиÑ, из Ñтого " "значениÑ. Значение по умолчанию: 1500, Ñтандартное Ð´Ð»Ñ Ethernet." msgid "MTU size of veth interfaces" msgstr "Размер MTU интерфейÑов veth" msgid "Make the l2 agent run in DVR mode." msgstr "Создать агент L2, выполнÑемый в режиме DVR." msgid "Malformed request body" msgstr "Ðеправильное тело запроÑа" #, python-format msgid "Malformed request body: %(reason)s." msgstr "Ðеправильно Ñформированное тело запроÑа: %(reason)s." msgid "MaxRtrAdvInterval setting for radvd.conf" msgstr "Параметр MaxRtrAdvInterval Ð´Ð»Ñ radvd.conf" msgid "Maximum number of DNS nameservers per subnet" msgstr "МакÑимальное количеÑтво Ñерверов DNS Ð´Ð»Ñ Ð¿Ð¾Ð´Ñети" msgid "" "Maximum number of L3 agents which a HA router will be scheduled on. If it is " "set to 0 then the router will be scheduled on every agent." msgstr "" "МакÑимальное чиÑло агентов L3, где будет запланирован маршрутизатор выÑокой " "готовноÑти. ЕÑли параметр равен 0, то маршрутизатор будет запланирован на " "каждом агенте." msgid "Maximum number of allowed address pairs" msgstr "МакÑимальное чиÑло разрешенных пар адреÑов" msgid "Maximum number of host routes per subnet" msgstr "МакÑимальное количеÑтво маршрутов хоÑта на подÑеть" msgid "Maximum number of routes per router" msgstr "МакÑимальное количеÑтво маршрутов на маршрутизатор" msgid "" "Metadata Proxy UNIX domain socket mode, 4 values allowed: 'deduce': deduce " "mode from metadata_proxy_user/group values, 'user': set metadata proxy " "socket mode to 0o644, to use when metadata_proxy_user is agent effective " "user or root, 'group': set metadata proxy socket mode to 0o664, to use when " "metadata_proxy_group is agent effective group or root, 'all': set metadata " "proxy socket mode to 0o666, to use otherwise." msgstr "" "Режим Ñокета домена UNIX Proxy метаданных, допуÑкаетÑÑ 4 значениÑ: 'deduce': " "получать режим из значений metadata_proxy_user/group, 'user': приÑвоить " "режиму Ñокета proxy метаданных значение 0o644 Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² Ñлучае, еÑли " "значением metadata_proxy_user ÑвлÑетÑÑ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒ root или Ñффективный " "пользователь агента, 'group': приÑвоить режиму Ñокета proxy метаданных " "значение 0o664 Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² Ñлучае, еÑли значением metadata_proxy_group " "ÑвлÑетÑÑ root или ÑÑ„Ñ„ÐµÐºÑ‚Ð¸Ð²Ð½Ð°Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð° агента, 'all': приÑвоить режиму Ñокета " "proxy метаданных значение 0o666 Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² оÑтальных ÑлучаÑÑ…." msgid "Metering driver" msgstr "Драйвер измерениÑ" msgid "MinRtrAdvInterval setting for radvd.conf" msgstr "Параметр MinRtrAdvInterval Ð´Ð»Ñ radvd.conf" msgid "Minimize polling by monitoring ovsdb for interface changes." msgstr "" "Минимизировать Ð¾Ð¿Ñ€Ð¾Ñ Ð¿ÑƒÑ‚ÐµÐ¼ мониторинга ovsdb на предмет изменений интерфейÑа." #, python-format msgid "Missing key in mapping: '%s'" msgstr "ОтÑутÑтвует ключ в отображении: '%s'" msgid "" "Multicast group for VXLAN. When configured, will enable sending all " "broadcast traffic to this multicast group. When left unconfigured, will " "disable multicast VXLAN mode." msgstr "" "МногоадреÑÐ½Ð°Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð° Ð´Ð»Ñ VXLAN. ЕÑли она наÑтроена, то веÑÑŒ " "широковещательный трафик направлÑетÑÑ Ð² Ñту группу. ЕÑли она не задана, то " "режим многоадреÑной передачи VXLAN выключен." msgid "" "Multicast group(s) for vxlan interface. A range of group addresses may be " "specified by using CIDR notation. Specifying a range allows different VNIs " "to use different group addresses, reducing or eliminating spurious broadcast " "traffic to the tunnel endpoints. To reserve a unique group for each possible " "(24-bit) VNI, use a /8 such as 239.0.0.0/8. This setting must be the same on " "all the agents." msgstr "" "МногоадреÑные группы Ð´Ð»Ñ Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñа vxlan. Диапазон адреÑов группы можно " "указать в нотации CIDR. ЕÑли указан диапазон, то различные VNI Ñмогут " "иÑпользовать разные адреÑа группы, что Ñнижает или даже иÑключает " "интенÑивный широковещательный трафик Ð´Ð»Ñ ÐºÐ¾Ð½ÐµÑ‡Ð½Ñ‹Ñ… точек туннелÑ. Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾ " "чтобы зарезервировать уникальную группу Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ возможного VNI (24 " "бита), иÑпользуйте формат /8, например, 239.0.0.0/8. Этот параметр должен " "быть одинаковым во вÑех агентах." #, python-format msgid "Multiple default providers for service %s" msgstr "ÐеÑколько поÑтавщиков по умолчанию Ð´Ð»Ñ Ñлужбы %s" #, python-format msgid "Multiple plugins for service %s were configured" msgstr "Ð”Ð»Ñ Ñлужбы %s наÑтроено неÑколько модулей" #, python-format msgid "Multiple providers specified for service %s" msgstr "ÐеÑколько поÑтавщиков задано Ð´Ð»Ñ Ñлужбы %s" msgid "Multiple tenant_ids in bulk security group rule create not allowed" msgstr "" "Ð“Ñ€ÑƒÐ¿Ð¿Ð¾Ð²Ð°Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð½ÐµÑкольких tenant_ids в правиле группы защиты не " "разрешена" msgid "Must also specify protocol if port range is given." msgstr "При указании диапазона портов необходимо задать протокол." msgid "Must specify one or more actions on flow addition or modification" msgstr "" "Ðеобходимо указать одно или неÑколько дейÑтвий Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ " "потока" msgid "Name of Open vSwitch bridge to use" msgstr "Ð˜Ð¼Ñ Ð¸Ñпользуемого моÑта Open vSwitch" msgid "" "Name of nova region to use. Useful if keystone manages more than one region." msgstr "" "Ð˜Ð¼Ñ Ð¸Ñпользуемого региона nova. Ðеобходимо, еÑли keystone управлÑет " "неÑколькими регионами." msgid "Namespace of the router" msgstr "ПроÑтранÑтво имен маршрутизатора" msgid "Native pagination depend on native sorting" msgstr "Внутреннее разбиение на Ñтраницы завиÑит от внутренней Ñортировки" #, python-format msgid "" "Need to apply migrations from %(project)s contract branch. This will require " "all Neutron server instances to be shutdown before proceeding with the " "upgrade." msgstr "" "ТребуетÑÑ Ð¿Ñ€Ð¸Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ миграцию из ветви contract %(project)s. При Ñтом " "необходимо выключить вÑе Ñерверы Neutron перед началом обновлениÑ." msgid "Negative delta (downgrade) not supported" msgstr "ÐžÑ‚Ñ€Ð¸Ñ†Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð´ÐµÐ»ÑŒÑ‚Ð° (понижение) не поддерживаетÑÑ" msgid "Negative relative revision (downgrade) not supported" msgstr "ÐžÑ‚Ñ€Ð¸Ñ†Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¾Ñ‚Ð½Ð¾ÑÐ¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ñ€ÐµÐ²Ð¸Ð·Ð¸Ñ (понижение) не поддерживаетÑÑ" #, python-format msgid "Network %s does not contain any IPv4 subnet" msgstr "Сеть %s не Ñодержит подÑетей IPv4" #, python-format msgid "Network %s is not a valid external network" msgstr "Сеть %s не ÑвлÑетÑÑ Ð´Ð¾Ð¿ÑƒÑтимой внешней Ñетью" #, python-format msgid "Network %s is not an external network" msgstr "Сеть %s не ÑвлÑетÑÑ Ð²Ð½ÐµÑˆÐ½ÐµÐ¹" #, python-format msgid "" "Network of size %(size)s, from IP range %(parent_range)s excluding IP ranges " "%(excluded_ranges)s was not found." msgstr "" "Сеть размера %(size)s из диапазона IP-адреÑов %(parent_range)s, кроме " "диапазонов IP-адреÑов %(excluded_ranges)s, не найдена." #, python-format msgid "Network type value '%s' not supported" msgstr "Значение типа Ñети '%s' не поддерживаетÑÑ" msgid "Network type value needed by the ML2 plugin" msgstr "Ð”Ð»Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ ML2 требуетÑÑ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ типа Ñети" msgid "Network types supported by the agent (gre and/or vxlan)." msgstr "Типы Ñетей, поддерживаемые агентом (gre или vxlan)." msgid "Neutron Service Type Management" msgstr "Управление типами Ñлужб Neutron" msgid "Neutron core_plugin not configured!" msgstr "Ðе наÑтроен core_plugin Neutron!" msgid "No default router:external network" msgstr "Ðе задан маршрут по умолчанию во внешнюю Ñеть" #, python-format msgid "No default subnetpool found for IPv%s" msgstr "Ðе найден пул подÑетей по умолчанию Ð´Ð»Ñ IPv%s" msgid "No default subnetpools defined" msgstr "Ðе определены пулы подÑетей по умолчанию" #, python-format msgid "No eligible l3 agent associated with external network %s found" msgstr "Ðе найдены допуÑтимые агенты l3, ÑвÑзанные Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ¹ Ñетью %s" #, python-format msgid "No more IP addresses available for subnet %(subnet_id)s." msgstr "Ð’ подÑети %(subnet_id)s больше нет доÑтупных IP-адреÑов." msgid "No offline migrations pending." msgstr "Ðет ожидающих миграций Ñ Ð²Ñ‹ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸ÐµÐ¼." #, python-format msgid "No shared key in %s fields" msgstr "Ðет общего ключа в полÑÑ… %s" msgid "Not allowed to manually assign a router to an agent in 'dvr' mode." msgstr "Ðевозможно вручную приÑвоить маршрутизатор агенту в режиме 'dvr'." msgid "Not allowed to manually remove a router from an agent in 'dvr' mode." msgstr "Ðевозможно вручную удалить маршрутизатор из агента в режиме 'dvr'." msgid "" "Number of DHCP agents scheduled to host a tenant network. If this number is " "greater than 1, the scheduler automatically assigns multiple DHCP agents for " "a given tenant network, providing high availability for DHCP service." msgstr "" "ЧиÑло агентов DHCP, запланированных Ð´Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñети арендатора. ЕÑли Ñто " "значение больше 1, планировщик автоматичеÑки приÑваивает неÑколько агентов " "DHCP Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ð½Ð½Ð¾Ð¹ Ñети арендатора, обеÑÐ¿ÐµÑ‡Ð¸Ð²Ð°Ñ Ð²Ñ‹Ñокую готовноÑть Ñлужбы " "DHCP." msgid "Number of backlog requests to configure the metadata server socket with" msgstr "" "КоличеÑтво непереданных запроÑов Ð´Ð»Ñ Ð½Ð°Ñтройки Ñокета Ñервера метаданных" msgid "Number of backlog requests to configure the socket with" msgstr "КоличеÑтво непереданных запроÑов Ð´Ð»Ñ Ð½Ð°Ñтройки Ñокета" msgid "" "Number of bits in an ipv4 PTR zone that will be considered network prefix. " "It has to align to byte boundary. Minimum value is 8. Maximum value is 24. " "As a consequence, range of values is 8, 16 and 24" msgstr "" "ЧиÑло разрÑдов в зоне PTR ipv4, которые будут обрабатыватьÑÑ ÐºÐ°Ðº Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ " "Ñети. Должно быть выравнено на границу байта. Минимальное значение: 8. " "МакÑимальное значение: 24. ДопуÑтимые значениÑ: 8, 16 и 24" msgid "" "Number of bits in an ipv6 PTR zone that will be considered network prefix. " "It has to align to nyble boundary. Minimum value is 4. Maximum value is 124. " "As a consequence, range of values is 4, 8, 12, 16,..., 124" msgstr "" "ЧиÑло разрÑдов в зоне PTR ipv6, которые будут обрабатыватьÑÑ ÐºÐ°Ðº Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ " "Ñети. Должно быть выравнено на границу полубайта. Минимальное значение: 4. " "МакÑимальное значение: 124. ДопуÑтимые значениÑ: 4, 8, 12, 16,..., 124" msgid "" "Number of floating IPs allowed per tenant. A negative value means unlimited." msgstr "" "КоличеÑтво нефикÑированных IP-адреÑов на одного арендатора. Отрицательное " "значение - не ограничено." msgid "" "Number of networks allowed per tenant. A negative value means unlimited." msgstr "" "ЧиÑло разрешенных Ñетей на одного арендатора. Отрицательное значение " "означает отÑутÑтвие ограничений." msgid "Number of ports allowed per tenant. A negative value means unlimited." msgstr "" "КоличеÑтво портов на одного арендатора. Отрицательное значение - не " "ограничено." msgid "Number of routers allowed per tenant. A negative value means unlimited." msgstr "" "КоличеÑтво маршрутизаторов на одного арендатора. Отрицательное значение - не " "ограничено." msgid "" "Number of seconds between sending events to nova if there are any events to " "send." msgstr "" "Интервал, в Ñекундах, между отправкой Ñобытий nova, еÑли имеютÑÑ ÑобытиÑ, " "требующие отправки." msgid "Number of seconds to keep retrying to listen" msgstr "Интервал (в Ñекундах) Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð¿Ñ‹Ñ‚Ð¾Ðº приема" msgid "" "Number of security groups allowed per tenant. A negative value means " "unlimited." msgstr "" "КоличеÑтво групп защиты на одного арендатора. Отрицательное значение - не " "ограничено." msgid "" "Number of security rules allowed per tenant. A negative value means " "unlimited." msgstr "" "КоличеÑтво правил защиты на одного арендатора. Отрицательное значение - не " "ограничено." msgid "" "Number of separate API worker processes for service. If not specified, the " "default is equal to the number of CPUs available for best performance." msgstr "" "ЧиÑло отдельных процеÑÑов обработчиков API Ð´Ð»Ñ Ñлужбы. ЕÑли не указано, по " "умолчанию равно чиÑлу доÑтупных процеÑÑоров в Ð´Ð»Ñ Ð´Ð¾ÑÑ‚Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð°ÐºÑимальной " "производительноÑти." msgid "" "Number of separate worker processes for metadata server (defaults to half of " "the number of CPUs)" msgstr "" "КоличеÑтво отдельных процеÑÑов обработчика Ð´Ð»Ñ Ñервера метаданных (значение " "по умолчанию: половина от количеÑтва процеÑÑоров)" msgid "Number of subnets allowed per tenant, A negative value means unlimited." msgstr "" "КоличеÑтво подÑетей на одного арендатора. Отрицательное значение - не " "ограничено." msgid "" "Number of threads to use during sync process. Should not exceed connection " "pool size configured on server." msgstr "" "ЧиÑло нитей, иÑпользуемых в процеÑÑе Ñинхронизации. Оно не должно превышать " "размер пула Ñоединений, наÑтроенный на Ñервере." msgid "OK" msgstr "OK" msgid "" "OVS datapath to use. 'system' is the default value and corresponds to the " "kernel datapath. To enable the userspace datapath set this value to 'netdev'." msgstr "" "Путь к данным OVS. Значение по умолчанию 'system' ÑоответÑтвует пути к " "данным, задаваемому Ñдром. Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾ чтобы иÑпользовать пользовательÑкий путь " "к данным, укажите значение netdev'." msgid "OVS vhost-user socket directory." msgstr "Каталог Ñокетов Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð´Ð»Ñ Ð²Ð¸Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ñ‹Ñ… хоÑтов OVS." #, python-format msgid "Object action %(action)s failed because: %(reason)s." msgstr "ДейÑтвие объекта %(action)s не выполнено, причина: %(reason)s." msgid "Only admin can view or configure quota" msgstr "Только админиÑтратор может проÑматривать и наÑтраивать квоту" msgid "Only admin is authorized to access quotas for another tenant" msgstr "Только админиÑтратор имеет доÑтуп к квотам других арендаторов" msgid "Only admins can manipulate policies on objects they do not own" msgstr "" "Только админиÑтраторы могут управлÑть ÑтратегиÑми объектов, владельцами " "которых они не ÑвлÑÑŽÑ‚ÑÑ." msgid "Only allowed to update rules for one security profile at a time" msgstr "" "Разрешено обновлÑть правила одновременно только Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ профайла защиты" msgid "Only remote_ip_prefix or remote_group_id may be provided." msgstr "Можно задать только remote_ip_prefix или remote_group_id." msgid "OpenFlow interface to use." msgstr "Ð˜Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ OpenFlow Ð´Ð»Ñ Ð¸ÑпользованиÑ." #, python-format msgid "" "Operation %(op)s is not supported for device_owner %(device_owner)s on port " "%(port_id)s." msgstr "" "ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ %(op)s не поддерживаетÑÑ Ð´Ð»Ñ device_owner %(device_owner)s, порт: " "%(port_id)s." #, python-format msgid "Operation not supported on device %(dev_name)s" msgstr "ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð½Ðµ поддерживаетÑÑ Ð² уÑтройÑтве %(dev_name)s" msgid "" "Ordered list of network_types to allocate as tenant networks. The default " "value 'local' is useful for single-box testing but provides no connectivity " "between hosts." msgstr "" "УпорÑдоченный ÑпиÑок типов Ñетей Ð´Ð»Ñ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð² качеÑтве Ñетей арендатора. " "Значение по умолчанию 'local' полезно Ð´Ð»Ñ Ñ‚ÐµÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð°Ð²Ñ‚Ð¾Ð½Ð¾Ð¼Ð½Ð¾Ð¹ ÑиÑтемы, " "но не позволÑет ÑвÑзать хоÑты." msgid "Override the default dnsmasq settings with this file." msgstr "" "Переопределите параметры по умолчанию Ð´Ð»Ñ dnsmasq Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ñтого файла." msgid "Owner type of the device: network/compute" msgstr "Тип владельца уÑтройÑтва: network/compute" msgid "POST requests are not supported on this resource." msgstr "ЗапроÑÑ‹ POST не поддерживаютÑÑ Ñтим реÑурÑом." #, python-format msgid "Package %s not installed" msgstr "Пакет %s не уÑтановлен" #, python-format msgid "Parsing bridge_mappings failed: %s." msgstr "СинтакÑичеÑкий анализ bridge_mappings не выполнен: %s." msgid "Password for connecting to designate in admin context" msgstr "" "Пароль Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº назначенному объекту в админиÑтративном контекÑте" msgid "Path to PID file for this process" msgstr "Путь к файлу PID Ð´Ð»Ñ Ñтого процеÑÑа" msgid "Path to the router directory" msgstr "Путь к каталогу маршрутизатора" msgid "Peer patch port in integration bridge for tunnel bridge." msgstr "Равноправный порт иÑправлений в моÑте интеграции Ð´Ð»Ñ Ð¼Ð¾Ñта туннелÑ." msgid "Peer patch port in tunnel bridge for integration bridge." msgstr "Равноправный порт иÑправлений в моÑте Ñ‚ÑƒÐ½Ð½ÐµÐ»Ñ Ð´Ð»Ñ Ð¼Ð¾Ñта интеграции." msgid "Per-tenant subnet pool prefix quota exceeded." msgstr "Превышена квота префикÑа подÑети Ð´Ð»Ñ Ð°Ñ€ÐµÐ½Ð´Ð°Ñ‚Ð¾Ñ€Ð°." msgid "Phase upgrade options do not accept revision specification" msgstr "Опции Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ„Ð°Ð·Ñ‹ не принимают Ñпецификацию ревизии" msgid "Ping timeout" msgstr "Тайм-аут проверки ÑвÑзи" msgid "Plugin does not support updating provider attributes" msgstr "Модуль не поддерживает обновление атрибутов поÑтавщика" #, python-format msgid "Port %(id)s does not have fixed ip %(address)s" msgstr "Порт %(id)s не имеет фикÑированного IP-адреÑа %(address)s" #, python-format msgid "Port %(port_id)s is already acquired by another DHCP agent" msgstr "Порт %(port_id)s уже занÑÑ‚ другим агентом DHCP. " #, python-format msgid "" "Port %s has multiple fixed IPv4 addresses. Must provide a specific IPv4 " "address when assigning a floating IP" msgstr "" "Порт %s Ñодержит неÑколько фикÑированных адреÑов IPv4. При назначении " "нефикÑированного IP-адреÑа необходимо указать конкретный Ð°Ð´Ñ€ÐµÑ IPv4" msgid "" "Port to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "Порт Ð´Ð»Ñ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ запроÑов на Ñоединение OpenFlow. ИÑпользуетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ " "'вÑтроенного' драйвера." #, python-format msgid "Prefix '%(prefix)s' not supported in IPv%(version)s pool." msgstr "ÐŸÑ€ÐµÑ„Ð¸ÐºÑ %(prefix)s не поддерживаетÑÑ Ð² пуле IPv%(version)s." msgid "Prefix Delegation can only be used with IPv6 subnets." msgstr "Делегирование префикÑа можно иÑпользовать только в подÑетÑÑ… IPv6." msgid "Private key of client certificate." msgstr "Личный ключ Ñертификата клиента." #, python-format msgid "Probe %s deleted" msgstr "ТеÑÑ‚ %s удален" #, python-format msgid "Probe created : %s " msgstr "Создан теÑÑ‚ %s " msgid "Process is already started" msgstr "ПроцеÑÑ ÑƒÐ¶Ðµ запущен" msgid "Process is not running." msgstr "ПроцеÑÑ Ð½Ðµ запущен." msgid "Protocol to access nova metadata, http or https" msgstr "Протокол Ð´Ð»Ñ Ð´Ð¾Ñтупа к метаданным nova (http или https)" #, python-format msgid "Provider name %(name)s is limited by %(len)s characters" msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ñтавщика %(name)s, не более %(len)s Ñимволов" #, python-format msgid "QoS Policy %(policy_id)s is used by %(object_type)s %(object_id)s." msgstr "" "Ð¡Ñ‚Ñ€Ð°Ñ‚ÐµÐ³Ð¸Ñ QoS %(policy_id)s иÑпользуетÑÑ %(object_type)s %(object_id)s." #, python-format msgid "" "QoS binding for network %(net_id)s and policy %(policy_id)s could not be " "found." msgstr "" "Ðе найдено ÑвÑзывание QoS Ð´Ð»Ñ Ñети %(net_id)s и Ñтратегии %(policy_id)s." #, python-format msgid "" "QoS binding for port %(port_id)s and policy %(policy_id)s could not be found." msgstr "" "Ðе найдено ÑвÑзывание QoS Ð´Ð»Ñ Ð¿Ð¾Ñ€Ñ‚Ð° %(port_id)s и Ñтратегии %(policy_id)s." #, python-format msgid "QoS policy %(policy_id)s could not be found." msgstr "Ðе найдена ÑÑ‚Ñ€Ð°Ñ‚ÐµÐ³Ð¸Ñ QoS %(policy_id)s." #, python-format msgid "QoS rule %(rule_id)s for policy %(policy_id)s could not be found." msgstr "Ðе найдено правило QoS %(rule_id)s Ð´Ð»Ñ Ñтратегии %(policy_id)s." #, python-format msgid "RBAC policy of type %(object_type)s with ID %(id)s not found" msgstr "Ðе найдена ÑÑ‚Ñ€Ð°Ñ‚ÐµÐ³Ð¸Ñ RBAC Ñ Ñ‚Ð¸Ð¿Ð¾Ð¼ %(object_type)s и ИД %(id)s" #, python-format msgid "" "RBAC policy on object %(object_id)s cannot be removed because other objects " "depend on it.\n" "Details: %(details)s" msgstr "" "Ðе удаетÑÑ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ Ñтратегию RBAC Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð° %(object_id)s, так как от нее " "завиÑÑÑ‚ другие объекты.\n" "СведениÑ: %(details)s" msgid "" "Range of seconds to randomly delay when starting the periodic task scheduler " "to reduce stampeding. (Disable by setting to 0)" msgstr "" "Диапазон Ñлучайных задержек (в Ñекундах) при запуÑке планировщика " "периодичеÑких задач во избежание взрывного запуÑка. (Ð”Ð»Ñ Ð²Ñ‹ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð·Ð°Ð´Ð°Ð¹Ñ‚Ðµ " "0)" msgid "Ranges must be in the same IP version" msgstr "ВерÑÐ¸Ñ IP Ð´Ð»Ñ Ð´Ð¸Ð°Ð¿Ð°Ð·Ð¾Ð½Ð¾Ð² должна Ñовпадать" msgid "Ranges must be netaddr.IPRange" msgstr "Формат диапазонов: netaddr.IPRange" msgid "Ranges must not overlap" msgstr "Диапазоны не должны перекрыватьÑÑ" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.EUI type." msgstr "" "Получено: тип '%(type)s', значение '%(value)s'. ОжидалÑÑ Ñ‚Ð¸Ð¿ netaddr.EUI." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPAddress " "type." msgstr "" "Получено: тип '%(type)s', значение '%(value)s'. ОжидалÑÑ Ñ‚Ð¸Ð¿ netaddr." "IPAddress." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPNetwork " "type." msgstr "" "Получено: тип '%(type)s', значение '%(value)s'. ОжидалÑÑ Ñ‚Ð¸Ð¿ netaddr." "IPNetwork." #, python-format msgid "" "Release aware branch labels (%s) are deprecated. Please switch to expand@ " "and contract@ labels." msgstr "" "Метки ветви информации о выпуÑке (%s) уÑтарели. Перейдите на иÑпользование " "меток expand@ и contract@." msgid "Remote metadata server experienced an internal server error." msgstr "ВнутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° удаленного Ñервера метаданных." msgid "" "Repository does not contain HEAD files for contract and expand branches." msgstr "Хранилище не Ñодержит файлы HEAD Ð´Ð»Ñ Ð²ÐµÑ‚Ð²ÐµÐ¹ contract и expand." msgid "" "Representing the resource type whose load is being reported by the agent. " "This can be \"networks\", \"subnets\" or \"ports\". When specified (Default " "is networks), the server will extract particular load sent as part of its " "agent configuration object from the agent report state, which is the number " "of resources being consumed, at every report_interval.dhcp_load_type can be " "used in combination with network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler When the network_scheduler_driver is " "WeightScheduler, dhcp_load_type can be configured to represent the choice " "for the resource being balanced. Example: dhcp_load_type=networks" msgstr "" "ПредÑтавление типа реÑурÑа, о чьей загрузке Ñообщает агент. Это может быть " "\"networks\", \"subnets\" или \"ports\". Когда указано (по умолчанию " "networks), Ñервер извлекает определенную загрузку, отправленную как чаÑть " "его объекта конфигурации агента из ÑоÑтоÑÐ½Ð¸Ñ Ð¾Ñ‚Ñ‡ÐµÑ‚Ð° агента, который Ñодержит " "количеÑтво потребленных реÑурÑов за каждый интервал report_interval. " "dhcp_load_type можно иÑпользовать в Ñочетании Ñ network_scheduler_driver = " "neutron.scheduler.dhcp_agent_scheduler.WeightScheduler Когда " "network_scheduler_driver - WeightScheduler, dhcp_load_type можно наÑтроить " "Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð²Ñ‹Ð±Ð¾Ñ€Ð° баланÑируемого реÑурÑа. Пример: " "dhcp_load_type=networks" msgid "Request Failed: internal server error while processing your request." msgstr "" "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ðµ выполнен: при обработке запроÑа произошла внутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° " "Ñервера." msgid "" "Reset flow table on start. Setting this to True will cause brief traffic " "interruption." msgstr "" "Выполнить ÑÐ±Ñ€Ð¾Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ потоков при Ñтарте. При значении True вызовет " "кратковременное прерывание потока." #, python-format msgid "Resource %(resource)s %(resource_id)s could not be found." msgstr "РеÑÑƒÑ€Ñ %(resource)s %(resource_id)s не найден." #, python-format msgid "Resource %(resource_id)s of type %(resource_type)s not found" msgstr "РеÑÑƒÑ€Ñ %(resource_id)s Ñ Ñ‚Ð¸Ð¿Ð¾Ð¼ %(resource_type)s не найден" #, python-format msgid "" "Resource '%(resource_id)s' is already associated with provider " "'%(provider)s' for service type '%(service_type)s'" msgstr "" "РеÑÑƒÑ€Ñ '%(resource_id)s' уже ÑвÑзан Ñ Ð¿Ð¾Ñтавщиком '%(provider)s' Ð´Ð»Ñ Ñ‚Ð¸Ð¿Ð° " "Ñлужбы '%(service_type)s'" msgid "Resource body required" msgstr "ТребуетÑÑ Ñ‚ÐµÐ»Ð¾ реÑурÑа" msgid "Resource not found." msgstr "РеÑÑƒÑ€Ñ Ð½Ðµ найден." msgid "Resources required" msgstr "ТребуютÑÑ Ñ€ÐµÑурÑÑ‹" msgid "" "Root helper application. Use 'sudo neutron-rootwrap /etc/neutron/rootwrap." "conf' to use the real root filter facility. Change to 'sudo' to skip the " "filtering and just run the command directly." msgstr "" "Ð’Ñпомогательное приложение Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð°Ð² root. Команла 'sudo neutron-" "rootwrap /etc/neutron/rootwrap.conf' вызывает утилиту фильтрации Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ " "root. Вызовите 'sudo', чтобы пропуÑтить фильтрацию и выполнить команду " "непоÑредÑтвенно." msgid "Root permissions are required to drop privileges." msgstr "Ð”Ð»Ñ ÑброÑа прав доÑтупа требуютÑÑ Ð¿Ñ€Ð°Ð²Ð° доÑтупа Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Root." #, python-format msgid "Router '%(router_id)s' is not compatible with this agent." msgstr "Маршрутизатор '%(router_id)s' неÑовмеÑтим Ñ Ñтим агентом." #, python-format msgid "Router already has a port on subnet %s" msgstr "У маршрутизатора уже еÑть порт в подÑети %s" msgid "Router port must have at least one fixed IP" msgstr "Порт маршрутизатора должне иметь Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один фикÑированный IP-адреÑ" #, python-format msgid "Running %(cmd)s (%(desc)s) for %(project)s ..." msgstr "ВыполнÑетÑÑ %(cmd)s (%(desc)s) Ð´Ð»Ñ %(project)s ..." #, python-format msgid "Running %(cmd)s for %(project)s ..." msgstr "ВыполнÑетÑÑ %(cmd)s Ð´Ð»Ñ %(project)s ..." msgid "" "Seconds between nodes reporting state to server; should be less than " "agent_down_time, best if it is half or less than agent_down_time." msgstr "" "Интервал отправки Ñообщений о ÑоÑтоÑнии узлов на Ñервер (в Ñекундах). " "Значение должно быть меньше, чем agent_down_time, оптимально - не больше " "половины Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ agent_down_time." msgid "" "Seconds to regard the agent is down; should be at least twice " "report_interval, to be sure the agent is down for good." msgstr "" "Интервал (в Ñекундах), в течение которого агент ÑчитаетÑÑ Ð²Ñ‹ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ‹Ð¼; " "должен по меньшей мере вдвое превышать значение report_interval, чтобы " "убедитьÑÑ Ð² том, что агент выключен навÑегда." #, python-format msgid "Security Group %(id)s %(reason)s." msgstr "Группа защиты %(id)s %(reason)s." #, python-format msgid "Security Group Rule %(id)s %(reason)s." msgstr "Правило группы защиты %(id)s %(reason)s." #, python-format msgid "Security group %(id)s does not exist" msgstr "Группа защиты %(id)s не ÑущеÑтвует" #, python-format msgid "Security group rule %(id)s does not exist" msgstr "Правило группы защиты %(id)s не ÑущеÑтвует" #, python-format msgid "Security group rule already exists. Rule id is %(rule_id)s." msgstr "Правило группы защиты уже ÑущеÑтвует. ИД правила: %(rule_id)s." #, python-format msgid "" "Security group rule for ethertype '%(ethertype)s' not supported. Allowed " "values are %(values)s." msgstr "" "Правило группы защиты Ð´Ð»Ñ Ñ‚Ð¸Ð¿Ð° '%(ethertype)s' не поддерживаетÑÑ. ДопуÑтимые " "значениÑ: %(values)s." #, python-format msgid "" "Security group rule protocol %(protocol)s not supported. Only protocol " "values %(values)s and integer representations [0 to 255] are supported." msgstr "" "Протокол правил группы защиты %(protocol)s не поддерживаетÑÑ. ПоддерживаютÑÑ " "Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ %(values)s и целочиÑленные предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ [0 - 255]." msgid "Segments and provider values cannot both be set." msgstr "ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ð´Ð½Ð¾Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð¾ задавать Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñегментов и поÑтавщика." msgid "Selects the Agent Type reported" msgstr "Выбирает указанный Тип агента" msgid "" "Send notification to nova when port data (fixed_ips/floatingip) changes so " "nova can update its cache." msgstr "" "Отправить уведомление nova в Ñлучае Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… порта (fixed_ips/" "floatingip), чтобы обеÑпечить обновление кÑша nova." msgid "Send notification to nova when port status changes" msgstr "Отправить уведомление nova в Ñлучае Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÑоÑтоÑÐ½Ð¸Ñ Ð¿Ð¾Ñ€Ñ‚Ð°" #, python-format msgid "" "Service provider '%(provider)s' could not be found for service type " "%(service_type)s" msgstr "" "ПоÑтавщик Ñлужбы '%(provider)s' не найден Ð´Ð»Ñ Ñ‚Ð¸Ð¿Ð° Ñлужбы %(service_type)s" msgid "Service to handle DHCPv6 Prefix delegation." msgstr "Служба Ð´Ð»Ñ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ Ð´ÐµÐ»ÐµÐ³Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑа DHCPv6." #, python-format msgid "Service type %(service_type)s does not have a default service provider" msgstr "Тип Ñлужбы %(service_type)s не Ñодержит поÑтавщика Ñлужбы по умолчанию" msgid "" "Set new timeout in seconds for new rpc calls after agent receives SIGTERM. " "If value is set to 0, rpc timeout won't be changed" msgstr "" "Задать новый тайм-аут (в Ñекундах) Ð´Ð»Ñ Ð½Ð¾Ð²Ñ‹Ñ… вызовов rpc поÑле Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ " "агентом Ñигнала SIGTERM. При значении 0 тайм-аут rpc не может быть изменен" msgid "" "Set or un-set the don't fragment (DF) bit on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "УÑтановка/ÑÐ±Ñ€Ð¾Ñ Ð±Ð¸Ñ‚Ð° Ðе разбивать на фрагменты (DF) в иÑходÑщем пакете IP, " "неÑущем туннель GRE/VXLAN." msgid "" "Set or un-set the tunnel header checksum on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "УÑтанавливает или ÑбраÑывает контрольную Ñумму заголовка Ñ‚ÑƒÐ½Ð½ÐµÐ»Ñ Ð² иÑходÑщем " "IP-пакете, поддерживающем туннель GRE/VXLAN." msgid "Shared address scope can't be unshared" msgstr "Ð”Ð»Ñ Ð¾Ð±Ñ‰ÐµÐ¹ адреÑной облаÑти Ð½ÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ ÑовмеÑтное иÑпользование" msgid "String prefix used to match IPset names." msgstr "ÐŸÑ€ÐµÑ„Ð¸ÐºÑ Ñтроки Ð´Ð»Ñ ÑопоÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð¼ÐµÐ½ IPset." #, python-format msgid "Sub-project %s not installed." msgstr "Подпроект %s не уÑтановлен." msgid "Subnet for router interface must have a gateway IP" msgstr "" "МаÑка подÑети Ð´Ð»Ñ Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñа маршрутизатора должна иметь IP-Ð°Ð´Ñ€ÐµÑ ÑˆÐ»ÑŽÐ·Ð°" #, python-format msgid "Subnet pool %(subnetpool_id)s could not be found." msgstr "Ðе найден пул подÑетей %(subnetpool_id)s." msgid "Subnet pool has existing allocations" msgstr "Пул подÑетей имеет ÑущеÑтвующие выделениÑ" msgid "Subnet used for the l3 HA admin network." msgstr "" "ПодÑеть, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ Ð´Ð»Ñ Ñети админиÑÑ‚Ñ€Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð²Ñ‹Ñокой готовноÑти L3." msgid "" "Subnets hosted on the same network must be allocated from the same subnet " "pool." msgstr "" "ПодÑети в одной и той же Ñети должны выделÑтьÑÑ Ð¸Ð· одного пула подÑетей." msgid "" "System-wide flag to determine the type of router that tenants can create. " "Only admin can override." msgstr "" "ОбщеÑиÑтемный флаг Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ñ‚Ð¸Ð¿Ð° маршрутизаторов, которые арендаторы " "могут Ñоздавать. Может быть переопределен только админиÑтратором." msgid "TCP Port used by Neutron metadata namespace proxy." msgstr "Порт TCP, применÑемый прокÑи проÑтранÑтва имен метаданных." msgid "TCP Port used by Nova metadata server." msgstr "Порт TCP, иÑпользуемый Ñервером метаданных Nova." msgid "TTL for vxlan interface protocol packets." msgstr "TTL Ð´Ð»Ñ Ð¿Ð°ÐºÐµÑ‚Ð¾Ð² протокола интерфейÑа vxlan." #, python-format msgid "Tag %(tag)s could not be found." msgstr "Тег %(tag)s не найден." #, python-format msgid "Tenant %(tenant_id)s not allowed to create %(resource)s on this network" msgstr "" "Ðрендатору %(tenant_id)s не разрешено Ñоздание реÑурÑа %(resource)s в Ñтой " "Ñети" msgid "Tenant id for connecting to designate in admin context" msgstr "" "ИД арендатора Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº назначенному объекту в админиÑтративном " "контекÑте" msgid "Tenant name for connecting to designate in admin context" msgstr "" "Ð˜Ð¼Ñ Ð°Ñ€ÐµÐ½Ð´Ð°Ñ‚Ð¾Ñ€Ð° Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº назначенному объекту в админиÑтративном " "контекÑте" msgid "Tenant network creation is not enabled." msgstr "Создание Ñети арендатора не разрешено." msgid "Tenant-id was missing from quota request." msgstr "Ð’ запроÑе квоты отÑутÑтвует ИД арендатора." msgid "" "The 'gateway_external_network_id' option must be configured for this agent " "as Neutron has more than one external network." msgstr "" "Ð”Ð»Ñ Ñтого агента необходимо наÑтроить опцию 'gateway_external_network_id', " "так как Neutron имеет неÑколько внешних Ñетей." msgid "" "The DHCP agent will resync its state with Neutron to recover from any " "transient notification or RPC errors. The interval is number of seconds " "between attempts." msgstr "" "Ðгент DHCP будет заново Ñинхронизировать Ñвое ÑоÑтоÑние Ñ Neutron Ð´Ð»Ñ " "воÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ñле временных ошибок RPC. Интервал между попытками задаетÑÑ " "в Ñекундах." msgid "" "The DHCP server can assist with providing metadata support on isolated " "networks. Setting this value to True will cause the DHCP server to append " "specific host routes to the DHCP request. The metadata service will only be " "activated when the subnet does not contain any router port. The guest " "instance must be configured to request host routes via DHCP (Option 121). " "This option doesn't have any effect when force_metadata is set to True." msgstr "" "Сервер DHCP может помогать в получении метаданных в изолированных ÑетÑÑ…. " "ЕÑли параметру приÑвоено значение True, то Ñервер DHCP будет добавлÑть " "маршруты к хоÑтам в Ð·Ð°Ð¿Ñ€Ð¾Ñ DHCP. Служба метаданных активируетÑÑ, только " "когда подÑеть не Ñодержит портов маршрутизатора. ГоÑтевой ÑкземплÑÑ€ должен " "быть наÑтроен Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов маршрутов к хоÑтам через DHCP (Option 121). Этот " "параметр ни на что не влиÑет, еÑли force_metadata задан равным True." msgid "The UDP port to use for VXLAN tunnels." msgstr "Порт UDP, применÑемый Ð´Ð»Ñ Ñ‚ÑƒÐ½Ð½ÐµÐ»ÐµÐ¹ VXLAN." #, python-format msgid "" "The address allocation request could not be satisfied because: %(reason)s" msgstr "Ðе удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° выделение адреÑа, причина: %(reason)s" msgid "The advertisement interval in seconds" msgstr "Интервал объÑÐ²Ð»ÐµÐ½Ð¸Ñ Ð² Ñекундах" #, python-format msgid "The allocation pool %(pool)s is not valid." msgstr "Пул Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ %(pool)s недопуÑтим." #, python-format msgid "" "The allocation pool %(pool)s spans beyond the subnet cidr %(subnet_cidr)s." msgstr "" "Пул Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ %(pool)s выходит за пределы cidr подÑети %(subnet_cidr)s." msgid "" "The base MAC address Neutron will use for VIFs. The first 3 octets will " "remain unchanged. If the 4th octet is not 00, it will also be used. The " "others will be randomly generated." msgstr "" "Базовый mac-адреÑ, иÑпользуемый в Neutron Ð´Ð»Ñ VIF. Первые 3 октета не будут " "изменены. ЕÑли 4-й октет не равен 00, он тоже будет иÑпользоватьÑÑ. " "ОÑтальные будут Ñозданы Ñлучайным образом." msgid "" "The base mac address used for unique DVR instances by Neutron. The first 3 " "octets will remain unchanged. If the 4th octet is not 00, it will also be " "used. The others will be randomly generated. The 'dvr_base_mac' *must* be " "different from 'base_mac' to avoid mixing them up with MAC's allocated for " "tenant ports. A 4 octet example would be dvr_base_mac = fa:16:3f:4f:00:00. " "The default is 3 octet" msgstr "" "Базовый mac-адреÑ, иÑпользуемый в Neutron Ð´Ð»Ñ ÑƒÐ½Ð¸ÐºÐ°Ð»ÑŒÐ½Ñ‹Ñ… ÑкземплÑров DVR. " "Первые 3 октета не будут изменены. ЕÑли 4-й октет не равен 00, он тоже будет " "иÑпользоватьÑÑ. ОÑтальные будут Ñозданы Ñлучайным образом. Параметр " "'dvr_base_mac' *должен* отличатьÑÑ Ð¾Ñ‚ 'base_mac' Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´Ð¾Ñ‚Ð²Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ " "ÑÐ¼ÐµÑˆÐ¸Ð²Ð°Ð½Ð¸Ñ Ð¸Ñ… Ñ MAC-адреÑами, выделенными Ð´Ð»Ñ Ð¿Ð¾Ñ€Ñ‚Ð¾Ð² арендатора. Пример 4 " "октетов: dvr_base_mac = fa:16:3f:4f:00:00. ПО умолчанию иÑпользуетÑÑ 3 октета" msgid "The core plugin Neutron will use" msgstr "Будет иÑпользоватьÑÑ Ð±Ð°Ð·Ð¾Ð²Ñ‹Ð¹ модуль Neutron" msgid "The driver used to manage the DHCP server." msgstr "драйвер, иÑпользуемый Ð´Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñервером DHCP." msgid "The driver used to manage the virtual interface." msgstr "Драйвер, иÑпользуемый Ð´Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð²Ð¸Ñ€Ñ‚ÑƒÐ°Ð»ÑŒÐ½Ñ‹Ð¼ интерфейÑом." msgid "" "The email address to be used when creating PTR zones. If not specified, the " "email address will be admin@" msgstr "" "ÐÐ´Ñ€ÐµÑ Ñлектронной почты Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð·Ð°Ð¿Ð¸Ñей PTR. ЕÑли не указан, будет " "иÑпользоватьÑÑ admin@" #, python-format msgid "" "The following device_id %(device_id)s is not owned by your tenant or matches " "another tenants router." msgstr "" "Следующий device_id %(device_id)s не принадлежит вашему арендатору или " "ÑоответÑтвует маршрутизатору другого арендатора." msgid "The interface for interacting with the OVSDB" msgstr "Ð˜Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð´Ð»Ñ Ð²Ð·Ð°Ð¸Ð¼Ð¾Ð´ÐµÐ¹ÑÑ‚Ð²Ð¸Ñ Ñ OVSDB" msgid "" "The maximum number of items returned in a single response, value was " "'infinite' or negative integer means no limit" msgstr "" "МакÑимальное количеÑтво Ñлементов, возвращаемых в одном ответе; значение " "было 'infinite' или отрицательным целым, что означает беÑконечное чиÑло" #, python-format msgid "" "The network %(network_id)s has been already hosted by the DHCP Agent " "%(agent_id)s." msgstr "Сеть %(network_id)s уже была размещена агентом DHCP %(agent_id)s." #, python-format msgid "" "The network %(network_id)s is not hosted by the DHCP agent %(agent_id)s." msgstr "Сеть %(network_id)s не размещена агентом DHCP %(agent_id)s." msgid "" "The network type to use when creating the HA network for an HA router. By " "default or if empty, the first 'tenant_network_types' is used. This is " "helpful when the VRRP traffic should use a specific network which is not the " "default one." msgstr "" "Тип Ñети при Ñоздании Ñети HA Ð´Ð»Ñ Ð¼Ð°Ñ€ÑˆÑ€ÑƒÑ‚Ð¸Ð·Ð°Ñ‚Ð¾Ñ€Ð° HA. По умолчанию (или при " "пуÑтом значении) иÑпользуетÑÑ Ð¿ÐµÑ€Ð²Ð¾Ðµ значение 'tenant_network_types'. Такой " "подход помогает, еÑли поток данных VRRP должен иÑпользовать Ñеть, не " "ÑвлÑющуюÑÑ Ñтандартной." msgid "" "The number of seconds the agent will wait between polling for local device " "changes." msgstr "" "Интервал опроÑа агентом локальных уÑтройÑтв на предмет Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹." msgid "" "The number of seconds to wait before respawning the ovsdb monitor after " "losing communication with it." msgstr "" "Ð’Ñ€ÐµÐ¼Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ, в Ñекундах, повторного Ð¿Ð¾Ñ€Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð½Ð¸Ñ‚Ð¾Ñ€Ð° ovsdb поÑле " "потери ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ Ð½Ð¸Ð¼." msgid "The number of sort_keys and sort_dirs must be same" msgstr "КоличеÑтво sort_keys и sort_dirs должно быть одинаковым" msgid "" "The path for API extensions. Note that this can be a colon-separated list of " "paths. For example: api_extensions_path = extensions:/path/to/more/exts:/" "even/more/exts. The __path__ of neutron.extensions is appended to this, so " "if your extensions are in there you don't need to specify them here." msgstr "" "Путь Ð´Ð»Ñ Ñ€Ð°Ñширений API. Пути разделÑÑŽÑ‚ÑÑ Ñ‚Ð¾Ñ‡ÐºÐ¾Ð¹ Ñ Ð·Ð°Ð¿Ñтой. Пример: " "api_extensions_path = extensions:/path/to/more/exts:/even/more/exts. " "__path__ Ð´Ð»Ñ Ñ€Ð°Ñширений neutron добавлÑетÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки, и еÑли раÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ " "ÑодержатÑÑ Ñ‚Ð°Ð¼, их не требуетÑÑ ÑƒÐºÐ°Ð·Ñ‹Ð²Ð°Ñ‚ÑŒ здеÑÑŒ." msgid "The physical network name with which the HA network can be created." msgstr "Ð˜Ð¼Ñ Ñ„Ð¸Ð·Ð¸Ñ‡ÐµÑкой Ñети Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñети HA." #, python-format msgid "The port '%s' was deleted" msgstr "Порт '%s' был удален" msgid "The port to bind to" msgstr "Порт Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº" #, python-format msgid "The requested content type %s is invalid." msgstr "Запрашиваемый тип Ñодержимого %s ÑвлÑетÑÑ Ð½ÐµÐ´Ð¾Ð¿ÑƒÑтимым." msgid "The resource could not be found." msgstr "РеÑÑƒÑ€Ñ Ð½Ðµ найден." #, python-format msgid "" "The router %(router_id)s has been already hosted by the L3 Agent " "%(agent_id)s." msgstr "Маршрутизатор %(router_id)s уже был размещен агентом L3 %(agent_id)s." msgid "" "The server has either erred or is incapable of performing the requested " "operation." msgstr "" "Ðа Ñервере возникла ошибка, или он не поддерживает выполнение запрошенной " "операции." msgid "The service plugins Neutron will use" msgstr "Будут иÑпользоватьÑÑ Ð¼Ð¾Ð´ÑƒÐ»Ð¸ Ñлужб Neutron" #, python-format msgid "The subnet request could not be satisfied because: %(reason)s" msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾Ð´Ñети не удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ, причина: %(reason)s" #, python-format msgid "The subproject to execute the command against. Can be one of: '%s'." msgstr "Подпроект Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹. ДопуÑтимые значениÑ: '%s'." msgid "The type of authentication to use" msgstr "ПрименÑемый тип идентификации" msgid "" "There are routers attached to this network that depend on this policy for " "access." msgstr "" "К Ñети подключены маршрутизаторы, доÑтуп к которым завиÑит от Ñтой Ñтратегии." msgid "" "Timeout in seconds to wait for a single OpenFlow request. Used only for " "'native' driver." msgstr "" "Тайм-аут (в Ñекундах) Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð¾Ð´Ð¸Ð½Ð¾Ñ‡Ð½Ð¾Ð³Ð¾ запроÑа OpenFlow. ИÑпользуетÑÑ " "только Ð´Ð»Ñ 'вÑтроенного' драйвера." msgid "" "Timeout in seconds to wait for the local switch connecting the controller. " "Used only for 'native' driver." msgstr "" "Тайм-аут (в Ñекундах) Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ð³Ð¾ Ð¿ÐµÑ€ÐµÐºÐ»ÑŽÑ‡Ð°Ñ‚ÐµÐ»Ñ Ñ " "контроллером. ИÑпользуетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ 'вÑтроенного' драйвера." msgid "" "Too long prefix provided. New name would exceed given length for an " "interface name." msgstr "" "Слишком длинный префикÑ. Ðовое Ð¸Ð¼Ñ Ð¿Ñ€ÐµÐ²Ñ‹Ñило бы заданную длину Ð´Ð»Ñ Ð¸Ð¼ÐµÐ½Ð¸ " "интерфейÑа." msgid "" "True to delete all ports on all the OpenvSwitch bridges. False to delete " "ports created by Neutron on integration and external network bridges." msgstr "" "True - удалить вÑе порты Ð´Ð»Ñ Ð²Ñех моÑтов OpenvSwitch. False - удалить порты, " "Ñозданные Neutron Ð´Ð»Ñ Ð¼Ð¾Ñтов интеграции и внешних Ñетей." msgid "Tunnel IP value needed by the ML2 plugin" msgstr "Ð”Ð»Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ ML2 требуетÑÑ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ IP-адреÑа туннелÑ" msgid "Tunnel bridge to use." msgstr "ИÑпользуемый моÑÑ‚ туннелÑ." msgid "" "Type of the nova endpoint to use. This endpoint will be looked up in the " "keystone catalog and should be one of public, internal or admin." msgstr "" "Тип иÑпользуемой конечной точки nova. ПоиÑк конечной точки выполнÑетÑÑ Ð² " "каталоге keystone, ÐºÐ¾Ð½ÐµÑ‡Ð½Ð°Ñ Ñ‚Ð¾Ñ‡ÐºÐ° может быть общедоÑтупной, внутренней или " "админиÑтративной." msgid "URL for connecting to designate" msgstr "URL Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº назначенному объекту" msgid "URL to database" msgstr "URL базы данных" #, python-format msgid "Unable to access %s" msgstr "Ошибка доÑтупа к %s" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, maximum allowed " "prefix is %(max_prefixlen)s." msgstr "" "Ðевозможно выделить подÑеть Ñ Ð´Ð»Ð¸Ð½Ð¾Ð¹ префикÑа %(prefixlen)s, макÑимальный " "разрешенный Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ - %(max_prefixlen)s." #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, minimum allowed " "prefix is %(min_prefixlen)s." msgstr "" "Ðевозможно выделить подÑеть Ñ Ð´Ð»Ð¸Ð½Ð¾Ð¹ префикÑа %(prefixlen)s, минимальный " "разрешенный Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ - %(min_prefixlen)s." #, python-format msgid "Unable to calculate %(address_type)s address because of:%(reason)s" msgstr "Ðе удалоÑÑŒ вычиÑлить Ð°Ð´Ñ€ÐµÑ %(address_type)s, причина:%(reason)s" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of DNS " "nameservers exceeds the limit %(quota)s." msgstr "" "Ðевозможно выполнить операцию Ð´Ð»Ñ %(subnet_id)s. ЧиÑло Ñерверов имен DNS " "превышает допуÑтимый макÑимум %(quota)s." #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of host routes " "exceeds the limit %(quota)s." msgstr "" "Ðевозможно выполнить операцию Ð´Ð»Ñ %(subnet_id)s. ЧиÑло маршрутов хоÑта " "превышает допуÑтимый макÑимум %(quota)s." #, python-format msgid "Unable to convert value in %s" msgstr "Ðевозможно преобразовать значение в %s" msgid "Unable to create the Agent Gateway Port" msgstr "Ðе удалоÑÑŒ Ñоздать порт шлюза агента" msgid "Unable to create the SNAT Interface Port" msgstr "Ðе удалоÑÑŒ Ñоздать порт интерфейÑа SNAT" #, python-format msgid "" "Unable to create the flat network. Physical network %(physical_network)s is " "in use." msgstr "" "Ðевозможно Ñоздать одноуровневую Ñеть. ФизичеÑÐºÐ°Ñ Ñеть %(physical_network)s " "занÑта." msgid "" "Unable to create the network. No available network found in maximum allowed " "attempts." msgstr "" "Ðе удалоÑÑŒ Ñоздать Ñеть. Ðе найдена доÑÑ‚ÑƒÐ¿Ð½Ð°Ñ Ñеть за макÑимальное чиÑло " "попыток." #, python-format msgid "Unable to delete subnet pool: %(reason)s." msgstr "Ðевозможно удалить пул подÑетей: %(reason)s." #, python-format msgid "Unable to determine mac address for %s" msgstr "Ðевозможно определить mac-Ð°Ð´Ñ€ÐµÑ Ð´Ð»Ñ %s" #, python-format msgid "Unable to find '%s' in request body" msgstr "ОтÑутÑтвует '%s' в теле запроÑа" #, python-format msgid "Unable to find IP address %(ip_address)s on subnet %(subnet_id)s" msgstr "Ðе удалоÑÑŒ найти IP-Ð°Ð´Ñ€ÐµÑ %(ip_address)s в подÑети %(subnet_id)s" #, python-format msgid "Unable to find resource name in %s" msgstr "Ð’ %s не найдено Ð¸Ð¼Ñ Ñ€ÐµÑурÑа" #, python-format msgid "Unable to generate unique mac on network %(net_id)s." msgstr "Ðевозможно Ñгенерировать уникальный mac в Ñети %(net_id)s." #, python-format msgid "" "Unable to identify a target field from:%s. Match should be in the form " "%%()s" msgstr "" "Ðевозможно идентифицировать целевое поле из %s. Совпадение должно быть в " "форме %%()s" msgid "Unable to provide external connectivity" msgstr "Ðе удалоÑÑŒ предоÑтавить ÑвÑзь Ñо внешней Ñетью" msgid "Unable to provide tenant private network" msgstr "Ðе удалоÑÑŒ предоÑтавить чаÑтную Ñеть арендатора" #, python-format msgid "" "Unable to reconfigure sharing settings for network %(network)s. Multiple " "tenants are using it." msgstr "" "Ðе удаетÑÑ Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ конфигурацию общих параметров Ð´Ð»Ñ Ñети %(network)s. Она " "иÑпользуетÑÑ Ð½ÐµÑколькими арендаторами." #, python-format msgid "" "Unable to verify match:%(match)s as the parent resource: %(res)s was not " "found" msgstr "" "Ðевозможно проверить Ñовпадение %(match)s, так как родительÑкий реÑÑƒÑ€Ñ " "%(res)s не найдено" #, python-format msgid "Unexpected label for script %(script_name)s: %(labels)s" msgstr "ÐÐµÐ¿Ñ€ÐµÐ´Ð²Ð¸Ð´ÐµÐ½Ð½Ð°Ñ Ð¼ÐµÑ‚ÐºÐ° Ð´Ð»Ñ ÑÑ†ÐµÐ½Ð°Ñ€Ð¸Ñ %(script_name)s: %(labels)s" #, python-format msgid "Unexpected number of alembic branch points: %(branchpoints)s" msgstr "Ðепредвиденное чиÑло переходных точек ветвлениÑ: %(branchpoints)s" #, python-format msgid "Unexpected response code: %s" msgstr "Ðепредвиденный код ответа: %s" #, python-format msgid "Unexpected response: %s" msgstr "Ðепредвиденный ответ: %s" #, python-format msgid "Unit name '%(unit)s' is not valid." msgstr "ÐедопуÑтимое Ð¸Ð¼Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ '%(unit)s'." #, python-format msgid "Unknown address type %(address_type)s" msgstr "ÐеизвеÑтный тип адреÑа %(address_type)s" #, python-format msgid "Unknown attribute '%s'." msgstr "ÐеизвеÑтный атрибут '%s'." #, python-format msgid "Unknown chain: %r" msgstr "ÐеизвеÑÑ‚Ð½Ð°Ñ Ñ†ÐµÐ¿Ð¾Ñ‡ÐºÐ°: %r" #, python-format msgid "Unknown network type %(network_type)s." msgstr "ÐеизвеÑтный тип Ñети %(network_type)s." #, python-format msgid "Unknown quota resources %(unknown)s." msgstr "ÐеизвеÑтные реÑурÑÑ‹ квоты: %(unknown)s." msgid "Unmapped error" msgstr "Ошибка без преобразованиÑ" msgid "Unrecognized action" msgstr "ÐеизвеÑтное дейÑтвие" #, python-format msgid "Unrecognized attribute(s) '%s'" msgstr "ÐераÑпознаваемые атрибуты '%s'" msgid "Unrecognized field" msgstr "ÐеизвеÑтное поле" msgid "Unsupported Content-Type" msgstr "Ðе поддерживаемый тип Ñодержимого" #, python-format msgid "Unsupported network type %(net_type)s." msgstr "Ðеподдерживаемый тип Ñети %(net_type)s." #, python-format msgid "Unsupported port state: %(port_state)s." msgstr "Ðеподдерживаемое ÑоÑтоÑние порта: %(port_state)s." msgid "Unsupported request type" msgstr "Ðеподдерживаемый тип запроÑа" msgid "Updating default security group not allowed." msgstr "Обновление группы защиты по умолчанию не разрешено." msgid "" "Use ML2 l2population mechanism driver to learn remote MAC and IPs and " "improve tunnel scalability." msgstr "" "ИÑпользовать драйвер механизма ML2 l2population Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð½Ñ‹Ñ… " "MAC- и IP-адреÑов и ÑƒÐ»ÑƒÑ‡ÑˆÐµÐ½Ð¸Ñ Ð¼Ð°ÑштабируемоÑти туннелÑ." msgid "Use broadcast in DHCP replies." msgstr "ИÑпользовать широковещательные пакеты в ответах DHCP." msgid "Use either --delta or relative revision, not both" msgstr "ИÑпользуйте или --delta, или отноÑительную ревизию, но не оба" msgid "" "Use ipset to speed-up the iptables based security groups. Enabling ipset " "support requires that ipset is installed on L2 agent node." msgstr "" "ИÑпользовать ipset Ð´Ð»Ñ ÑƒÑÐºÐ¾Ñ€ÐµÐ½Ð¸Ñ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ групп защиты на оÑнове iptables. " "Поддержка ipset требует, чтобы ipset был уÑтановлен в узле агента L2." msgid "" "Use the root helper when listing the namespaces on a system. This may not be " "required depending on the security configuration. If the root helper is not " "required, set this to False for a performance improvement." msgstr "" "ИÑпользовать вÑпомогательное приложение Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð°Ð² root Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ " "проÑтранÑтв имен в ÑиÑтеме. Это может не потребоватьÑÑ Ð¿Ñ€Ð¸ ÑоответÑтвующим " "образом наÑтроенной конфигурации защиты. ЕÑли вÑпомогательное приложение Ð´Ð»Ñ " "Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð°Ð² root не иÑпользуетÑÑ, приÑвойте параметру значение false Ð´Ð»Ñ " "Ð¿Ð¾Ð²Ñ‹ÑˆÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñти." msgid "" "Use veths instead of patch ports to interconnect the integration bridge to " "physical networks. Support kernel without Open vSwitch patch port support so " "long as it is set to True." msgstr "" "ИÑпользовать интерфейÑÑ‹ veth вмеÑто коммутационных портов Ð´Ð»Ñ ÑвÑзи моÑта " "интеграции Ñ Ñ„Ð¸Ð·Ð¸Ñ‡ÐµÑкими моÑтами. ЕÑли параметр равен True, то может " "иÑпользоватьÑÑ Ñдро без поддержки коммутационных портов Open vSwitch." msgid "" "User (uid or name) running metadata proxy after its initialization (if " "empty: agent effective user)." msgstr "" "Пользователь (uid или имÑ) иÑпользует proxy метаданных поÑле инициализации " "(еÑли пуÑтое, иÑпользуетÑÑ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒ агента). " msgid "User (uid or name) running this process after its initialization" msgstr "Пользователь (uid или имÑ) запуÑкает Ñтот процеÑÑ Ð¿Ð¾Ñле инициализации" msgid "Username for connecting to designate in admin context" msgstr "" "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº назначенному объекту в админиÑтративном " "контекÑте" msgid "VRRP authentication password" msgstr "Пароль идентификации VRRP" msgid "VRRP authentication type" msgstr "Тип идентификации VRRP" msgid "VXLAN network unsupported." msgstr "Сеть VXLAN не поддерживаетÑÑ." msgid "" "Value of host kernel tick rate (hz) for calculating minimum burst value in " "bandwidth limit rules for a port with QoS. See kernel configuration file for " "HZ value and tc-tbf manual for more information." msgstr "" "ЧаÑтота отÑчетов времени в Ñдре (в Гц) Ð´Ð»Ñ Ð²Ñ‹Ñ‡Ð¸ÑÐ»ÐµÐ½Ð¸Ñ Ð¼Ð¸Ð½Ð¸Ð¼Ð°Ð»ÑŒÐ½Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ " "пакета в правилах Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¿ÑƒÑкной ÑпоÑобноÑти канала Ð´Ð»Ñ Ð¿Ð¾Ñ€Ñ‚Ð° Ñ QoS. " "За дополнительной информацией обратитеÑÑŒ к опиÑанию параметра Ñдра HZ и " "руководÑтву по tc-tbf." msgid "" "Value of latency (ms) for calculating size of queue for a port with QoS. See " "tc-tbf manual for more information." msgstr "" "Задержка в миллиÑекундах Ð´Ð»Ñ Ð²Ñ‹Ñ‡Ð¸ÑÐ»ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ð¼ÐµÑ€Ð° очереди Ð´Ð»Ñ Ð¿Ð¾Ñ€Ñ‚Ð° Ñ QoS. За " "дополнительной информацией обратитеÑÑŒ к руководÑтву по tc-tbf." msgid "" "When external_network_bridge is set, each L3 agent can be associated with no " "more than one external network. This value should be set to the UUID of that " "external network. To allow L3 agent support multiple external networks, both " "the external_network_bridge and gateway_external_network_id must be left " "empty." msgstr "" "ЕÑли задана Ð¾Ð¿Ñ†Ð¸Ñ external_network_bridge, то каждый агент L3 может быть " "ÑвÑзан не более чем Ñ Ð¾Ð´Ð½Ð¾Ð¹ внешней Ñетью. ПриÑвойте опции значение UUID " "Ñтой внешней Ñети. Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾ чтобы агент L3 поддерживал неÑколько внешних " "Ñетей, оба параметра, external_network_bridge и gateway_external_network_id, " "должны быть пуÑтыми." msgid "" "When proxying metadata requests, Neutron signs the Instance-ID header with a " "shared secret to prevent spoofing. You may select any string for a secret, " "but it must match here and in the configuration used by the Nova Metadata " "Server. NOTE: Nova uses the same config key, but in [neutron] section." msgstr "" "При прокÑировании запроÑов метаданных Neutron подпиÑывает заголовок Instance-" "ID общим Ñекретным ключом Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´Ð¾Ñ‚Ð²Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ð¼ÐµÐ½Ñ‹. Ключом может быть " "Ð»ÑŽÐ±Ð°Ñ Ñтрока, но она должна Ñовпадать Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð¹ в конфигурации Ð´Ð»Ñ Ñервера " "метаданных Nova. Примечание: Nova иÑпользует тот же ключ конфигурации, но в " "разделе [neutron]." msgid "" "Where to store Neutron state files. This directory must be writable by the " "agent." msgstr "" "РаÑположение Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² ÑоÑтоÑÐ½Ð¸Ñ Neutron. Этот каталог должен быть " "доÑтупен Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи агентом." msgid "" "With IPv6, the network used for the external gateway does not need to have " "an associated subnet, since the automatically assigned link-local address " "(LLA) can be used. However, an IPv6 gateway address is needed for use as the " "next-hop for the default route. If no IPv6 gateway address is configured " "here, (and only then) the neutron router will be configured to get its " "default route from router advertisements (RAs) from the upstream router; in " "which case the upstream router must also be configured to send these RAs. " "The ipv6_gateway, when configured, should be the LLA of the interface on the " "upstream router. If a next-hop using a global unique address (GUA) is " "desired, it needs to be done via a subnet allocated to the network and not " "through this parameter. " msgstr "" "При иÑпользовании IPv6 применÑемой Ð´Ð»Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ³Ð¾ шлюза Ñети не обÑзательно " "иметь ÑвÑзанную подÑеть, так как может быть иÑпользован автоматичеÑки " "назначаемый Ð°Ð´Ñ€ÐµÑ link-local (LLA). Однако, Ð°Ð´Ñ€ÐµÑ ÑˆÐ»ÑŽÐ·Ð° IPv6 необходим в " "качеÑтве Ñледующего узла Ð´Ð»Ñ Ð¼Ð°Ñ€ÑˆÑ€ÑƒÑ‚Ð° по умолчанию. ЕÑли Ð°Ð´Ñ€ÐµÑ ÑˆÐ»ÑŽÐ·Ð° IPv6 не " "указан здеÑÑŒ, (и только в Ñтом Ñлучае) будет наÑтроен маршрутизатор Neutron " "Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¼Ð°Ñ€ÑˆÑ€ÑƒÑ‚Ð° по умолчанию из объÑвлений маршрутизатора (RA) от " "маршрутизатора выше по течению. Ð’ Ñтом Ñлучае маршрутизатор выше по течению " "должен быть также наÑтроен Ð´Ð»Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸ Ñтих RA. Когда указано значение " "ipv6_gateway, оно должно указывать на LLA интерфейÑа маршрутизатора выше по " "течению. ЕÑли Ñледующий узел, иÑпользующийглобальный уникальный Ð°Ð´Ñ€ÐµÑ (GUA), " "ÑвлÑетÑÑ Ð¿Ñ€ÐµÐ´Ð¿Ð¾Ñ‡Ð¸Ñ‚Ð°ÐµÐ¼, Ñто необходимо обеÑпечить поÑредÑтвом подÑети, " "выделенной Ð´Ð»Ñ Ñети, а не Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ñтого параметра. " msgid "You must implement __call__" msgstr "ОтÑутÑтвует Ñ€ÐµÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ __call__" msgid "" "You must provide a config file for bridge - either --config-file or " "env[NEUTRON_TEST_CONFIG_FILE]" msgstr "" "Ðеобходимо задать файл конфигурации Ð´Ð»Ñ Ð¼Ð¾Ñта, или --config-file, или " "env[NEUTRON_TEST_CONFIG_FILE]" msgid "You must provide a revision or relative delta" msgstr "Ðеобходимо указать ревизию или отноÑительную дельта" msgid "a subnetpool must be specified in the absence of a cidr" msgstr "пул подÑетей должен быть указан в отÑутÑтвие cidr" msgid "add_ha_port cannot be called inside of a transaction." msgstr "add_ha_port Ð½ÐµÐ»ÑŒÐ·Ñ Ð²Ñ‹Ð·Ñ‹Ð²Ð°Ñ‚ÑŒ внутри транзакции." msgid "allocation_pools allowed only for specific subnet requests." msgstr "allocation_pools разрешено только Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð½Ñ‹Ñ… запроÑов подÑетей. " msgid "allocation_pools are not in the subnet" msgstr "allocation_pools не принадлежит подÑети" msgid "allocation_pools use the wrong ip version" msgstr "ÐедопуÑÑ‚Ð¸Ð¼Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ IP Ð´Ð»Ñ allocation_pools" msgid "already a synthetic attribute" msgstr "атрибут уже ÑвлÑетÑÑ ÑинтетичеÑким" msgid "binding:profile value too large" msgstr "Слишком большое значение binding:profile" #, python-format msgid "cannot perform %(event)s due to %(reason)s" msgstr "не удаетÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ %(event)s, причина: %(reason)s" msgid "cidr and prefixlen must not be supplied together" msgstr "cidr и prefixlen не должны быть указаны вмеÑте" #, python-format msgid "dhcp_agents_per_network must be >= 1. '%s' is invalid." msgstr "" "Значение dhcp_agents_per_network должно быть >= 1. Значение '%s' недопуÑтимо." msgid "dns_domain cannot be specified without a dns_name" msgstr "dns_domain должен указыватьÑÑ Ð²Ð¼ÐµÑте Ñ dns_name" msgid "dns_name cannot be specified without a dns_domain" msgstr "dns_name должен указыватьÑÑ Ð²Ð¼ÐµÑте Ñ dns_domain" msgid "fixed_ip_address cannot be specified without a port_id" msgstr "fixed_ip_address должен указыватьÑÑ Ð²Ð¼ÐµÑте Ñ port_id" #, python-format msgid "has device owner %s" msgstr "имеет владельца уÑтройÑтва %s" msgid "in use" msgstr "ИÑпользуетÑÑ" #, python-format msgid "ip command failed on device %(dev_name)s: %(reason)s" msgstr "Ðе удалоÑÑŒ выполнить команду ip на уÑтройÑтве %(dev_name)s: %(reason)s" #, python-format msgid "ip command failed: %(reason)s" msgstr "Команда ip не выполнена: %(reason)s" #, python-format msgid "ip link capability %(capability)s is not supported" msgstr "Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ ip link %(capability)s не поддерживаетÑÑ" #, python-format msgid "ip link command is not supported: %(reason)s" msgstr "Команда ip link не поддерживаетÑÑ: %(reason)s" msgid "ip_version must be specified in the absence of cidr and subnetpool_id" msgstr "ip_version должно быть указано при отÑутÑтвии cidr and subnetpool_id" msgid "ipv6_address_mode is not valid when ip_version is 4" msgstr "ipv6_address_mode недопуÑтим, когда ip_version - 4" msgid "ipv6_ra_mode is not valid when ip_version is 4" msgstr "ipv6_ra_mode недопуÑтим, когда ip_version - 4" #, python-format msgid "" "ipv6_ra_mode set to '%(ra_mode)s' with ipv6_address_mode set to " "'%(addr_mode)s' is not valid. If both attributes are set, they must be the " "same value" msgstr "" "Ð”Ð»Ñ ipv6_ra_mode задано значение '%(ra_mode)s', а значение " "ipv6_address_mode, заданное как '%(addr_mode)s', ÑвлÑетÑÑ Ð½ÐµÐ´Ð¾Ð¿ÑƒÑтимым. ЕÑли " "указаны оба атрибута, их Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ñ‹ Ñовпадать" msgid "mac address update" msgstr "Обновление mac-адреÑа" msgid "must provide exactly 2 arguments - cidr and MAC" msgstr "Ðеобходимо задать ровно 2 аргумента - cidr и MAC" msgid "network_type required" msgstr "ТребуетÑÑ network_type" #, python-format msgid "network_type value '%s' not supported" msgstr "Ð”Ð»Ñ network_type не поддерживаетÑÑ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ '%s'" msgid "new subnet" msgstr "Ð½Ð¾Ð²Ð°Ñ Ð¿Ð¾Ð´Ñеть" #, python-format msgid "physical_network '%s' unknown for flat provider network" msgstr "ÐеизвеÑÑ‚Ð½Ð°Ñ Ñ„Ð¸Ð·Ð¸Ñ‡ÐµÑÐºÐ°Ñ Ñеть '%s' Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾ÑƒÑ€Ð¾Ð²Ð½ÐµÐ²Ð¾Ð¹ Ñети провайдера" msgid "physical_network required for flat provider network" msgstr "Ð”Ð»Ñ Ð¾Ð´Ð½Ð¾ÑƒÑ€Ð¾Ð²Ð½ÐµÐ²Ð¾Ð¹ Ñети провайдера требуетÑÑ physical_network" #, python-format msgid "provider:physical_network specified for %s network" msgstr "Ð”Ð»Ñ Ñети %s указан provider:physical_network" msgid "respawn_interval must be >= 0 if provided." msgstr "Значение respawn_interval, еÑли оно указано, должно быть >= 0." #, python-format msgid "segmentation_id out of range (%(min)s through %(max)s)" msgstr "segmentation_id вне диапазона (%(min)s - %(max)s)" msgid "segmentation_id requires physical_network for VLAN provider network" msgstr "" "Ð”Ð»Ñ segmentation_id требуетÑÑ physical_network Ð´Ð»Ñ Ñети VLAN провайдера" msgid "shared attribute switching to synthetic" msgstr "общий атрибут изменен на ÑинтетичеÑкий" #, python-format msgid "" "subnetpool %(subnetpool_id)s cannot be updated when associated with shared " "address scope %(address_scope_id)s" msgstr "" "Пул подÑетей %(subnetpool_id)s Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ð·Ð¼ÐµÐ½Ñть, еÑли он ÑвÑзан Ñ " "общедоÑтупной адреÑной облаÑтью %(address_scope_id)s" msgid "subnetpool_id and use_default_subnetpool cannot both be specified" msgstr "" "ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ð´Ð½Ð¾Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð¾ задавать Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ subnetpool_id и use_default_subnetpool" msgid "the nexthop is not connected with router" msgstr "Ñледующий узел не Ñоединен Ñ Ð¼Ð°Ñ€ÑˆÑ€ÑƒÑ‚Ð¸Ð·Ð°Ñ‚Ð¾Ñ€Ð¾Ð¼" msgid "the nexthop is used by router" msgstr "Ñледующий узел иÑпользуетÑÑ Ð¼Ð°Ñ€ÑˆÑ€ÑƒÑ‚Ð¸Ð·Ð°Ñ‚Ð¾Ñ€Ð¾Ð¼" neutron-12.1.1/neutron/locale/zh_CN/0000775000175000017500000000000013553660156017254 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/zh_CN/LC_MESSAGES/0000775000175000017500000000000013553660156021041 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/zh_CN/LC_MESSAGES/neutron.po0000664000175000017500000031026613553660047023102 0ustar zuulzuul00000000000000# Translations template for neutron. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the neutron project. # # Translators: # Allerson Yao, 2015 # Zhong Chaoliang , 2013 # lyndon zhang , 2014 # MA QIANG , 2014 # MA QIANG , 2014 # Zhong Chaoliang , 2013 # 汪军 , 2015 # 陈展奇 , 2013-2014 # 颜海峰 , 2014 # Andreas Jaeger , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: neutron VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-03-14 04:19+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-04-12 05:55+0000\n" "Last-Translator: Copied by Zanata \n" "Language: zh_CN\n" "Plural-Forms: nplurals=1; plural=0;\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 4.3.3\n" "Language-Team: Chinese (China)\n" #, python-format msgid "" "\n" "Command: %(cmd)s\n" "Exit code: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" msgstr "" "\n" "命令:%(cmd)s\n" "退出代ç ï¼š%(code)s\n" "标准输入:%(stdin)s\n" "标准输出:%(stdout)s\n" "标准错误:%(stderr)s" #, python-format msgid "" "%(branch)s HEAD file does not match migration timeline head, expected: " "%(head)s" msgstr "%(branch)s HEAD 文件与è¿ç§»æ—¶é—´çº¿å¤´ä¸åŒ¹é…,需è¦ï¼š%(head)s" #, python-format msgid "%(id)s is not a valid %(type)s identifier" msgstr "%(id)s 䏿˜¯æœ‰æ•ˆçš„ %(type)s 标识" #, python-format msgid "" "%(invalid_dirs)s is invalid value for sort_dirs, valid value is '%(asc)s' " "and '%(desc)s'" msgstr "" "%(invalid_dirs)s 对于 sort_dirs 是无效值,有效值是“%(asc)sâ€å’Œâ€œ%(desc)sâ€" #, python-format msgid "%(key)s prohibited for %(tunnel)s provider network" msgstr "对于 %(tunnel)s æä¾›ç¨‹åºç½‘ç»œï¼Œå·²ç¦æ­¢ %(key)s" #, python-format msgid "%(name)s '%(addr)s' does not match the ip_version '%(ip_version)s'" msgstr "%(name)s“%(addr)sâ€ä¸Ž ip_version“%(ip_version)sâ€ä¸åŒ¹é…" #, python-format msgid "%s cannot be called while in offline mode" msgstr "在 %s å¤„äºŽè„±æœºæ–¹å¼æ—¶ï¼Œæ— æ³•对其进行调用" #, python-format msgid "%s is invalid attribute for sort_keys" msgstr "%s 对于 sort_keys 是无效属性" #, python-format msgid "%s is not a valid VLAN tag" msgstr "%s 䏿˜¯ä¸€ä¸ªæœ‰æ•ˆçš„æ ‡ç­¾" #, python-format msgid "%s must implement get_port_from_device or get_ports_from_devices." msgstr "%s 必须实现 get_port_from_device 或 get_ports_from_devices。" #, python-format msgid "%s prohibited for VLAN provider network" msgstr "VLANæä¾›è€…ç½‘ç»œä¸­ç¦æ­¢%s" #, python-format msgid "%s prohibited for flat provider network" msgstr "在平é¢ä¾›åº”å•†ç½‘ç»œä¸­ç¦æ­¢%s" #, python-format msgid "%s prohibited for local provider network" msgstr "åœ¨æœ¬åœ°ä¾›åº”å•†ç½‘ç»œä¸­ç¦æ­¢%s" #, python-format msgid "'%s' is not a valid RBAC object type" msgstr "“%sâ€ä¸æ˜¯æœ‰æ•ˆçš„ RBAC 对象类型" #, python-format msgid "'%s' is not supported for filtering" msgstr "“%sâ€ä¸æ”¯æŒè¿›è¡Œè¿‡æ»¤" #, python-format msgid "'module' object has no attribute '%s'" msgstr "“moduleâ€å¯¹è±¡æ²¡æœ‰å±žæ€§â€œ%sâ€" msgid "'port_max' is smaller than 'port_min'" msgstr "“port_maxâ€å°äºŽâ€œport_minâ€" msgid "0 is not allowed as CIDR prefix length" msgstr "0ä¸å…许作为CIDRå‰ç¼€é•¿åº¦" msgid "A cidr must be specified in the absence of a subnet pool" msgstr "在缺少å­ç½‘池的情况下,必须指定 cidr" msgid "" "A decimal value as Vendor's Registered Private Enterprise Number as required " "by RFC3315 DUID-EN." msgstr "作为 RFC3315 DUID-EN 所需è¦çš„供应商的已注册ç§è¥ä¼ä¸šå·çš„å进制值。" #, python-format msgid "A default external network already exists: %(net_id)s." msgstr "缺çœå¤–部网络已存在:%(net_id)s。" msgid "" "A default subnetpool for this IP family has already been set. Only one " "default may exist per IP family" msgstr "已对此 IP 系列设置缺çœå­ç½‘池。对于æ¯ä¸ª IP 系列,åªèƒ½æœ‰ä¸€ä¸ªç¼ºçœå­ç½‘池。" msgid "A metering driver must be specified" msgstr "必须指定测é‡é©±åŠ¨ç¨‹åº" msgid "API for retrieving service providers for Neutron advanced services" msgstr "用于为 Neutron 高级æœåŠ¡æ£€ç´¢æœåŠ¡æä¾›ç¨‹åºçš„ API" msgid "Aborting periodic_sync_routers_task due to an error." msgstr "正在异常中止 periodic_sync_routers_task,因为å‘生了错误。" msgid "Access to this resource was denied." msgstr "访问该资æºè¢«æ‹’ç»ã€‚" msgid "Action to be executed when a child process dies" msgstr "当å­è¿›ç¨‹ç»ˆæ­¢æ—¶è¦æ‰§è¡Œçš„æ“ä½œ" msgid "" "Add comments to iptables rules. Set to false to disallow the addition of " "comments to generated iptables rules that describe each rule's purpose. " "System must support the iptables comments module for addition of comments." msgstr "" "å‘ iptable 规则添加注释。设置为 false ä»¥ç¦æ­¢å‘æè¿°è§„åˆ™ç”¨é€”çš„å·²ç”Ÿæˆ iptable æ·»" "åŠ æ³¨é‡Šã€‚ç³»ç»Ÿå¿…é¡»æ”¯æŒ iptable 注释模å—以添加注释。" msgid "Address not present on interface" msgstr "接å£ä¸Šæ²¡æœ‰åœ°å€" msgid "" "Address to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "对于 OpenFlow 连接,è¦ä¾¦å¬çš„地å€ã€‚仅用于“本机â€é©±åŠ¨ç¨‹åºã€‚" msgid "Adds test attributes to core resources." msgstr "将测试属性添加至核心资æºã€‚" #, python-format msgid "Agent %(id)s is not a L3 Agent or has been disabled" msgstr "ä»£ç† %(id)s 䏿˜¯ L3 ä»£ç†æˆ–å·²ç¦ç”¨" #, python-format msgid "Agent %(id)s is not a valid DHCP Agent or has been disabled" msgstr "ä»£ç† %(id)s 是无效 DHCP ä»£ç†æˆ–å·²ç¦ç”¨" msgid "" "Agent starts with admin_state_up=False when enable_new_agents=False. In the " "case, user's resources will not be scheduled automatically to the agent " "until admin changes admin_state_up to True." msgstr "" "当 enable_new_agents=False 时,代ç†å°†ä½¿ç”¨ admin_state_up=False å¯åŠ¨ã€‚åœ¨è¿™ç§æƒ…" "况下,将ä¸ä¼šè‡ªåЍ坹代ç†è°ƒåº¦ç”¨æˆ·çš„资æºï¼Œç›´åˆ°ç®¡ç†å‘˜å°† admin_state_up 更改为 " "True 为止。" #, python-format msgid "Agent updated: %(payload)s" msgstr "进程更新: %(payload)s" msgid "Allow auto scheduling networks to DHCP agent." msgstr "å…许自动对 DHCP 代ç†è°ƒåº¦ç½‘络。" msgid "Allow auto scheduling of routers to L3 agent." msgstr "å…许自动对 L3 代ç†è°ƒåº¦è·¯ç”±å™¨ã€‚" msgid "" "Allow overlapping IP support in Neutron. Attention: the following parameter " "MUST be set to False if Neutron is being used in conjunction with Nova " "security groups." msgstr "" "å…许在 Neutron 中é‡å  IP 支æŒã€‚注æ„:如果将 Neutron 与 Nova 安全组é…åˆä½¿ç”¨ï¼Œ" "é‚£ä¹ˆä»¥ä¸‹å‚æ•°å¿…须设置为 False。" msgid "Allow running metadata proxy." msgstr "å…许è¿è¡Œ metadata代ç†" msgid "Allow sending resource operation notification to DHCP agent" msgstr "å…è®¸å°†èµ„æºæ“作通知å‘é€è‡³ DHCP 代ç†" msgid "Allow the creation of PTR records" msgstr "å…许创建 PTR 记录" msgid "Allow the usage of the bulk API" msgstr "å…è®¸ä½¿ç”¨æˆæ‰¹ API" msgid "Allow to perform insecure SSL (https) requests to nova metadata" msgstr "å…许对 nova å…ƒæ•°æ®æ‰§è¡Œéžå®‰å…¨ SSL (HTTPS) 请求" msgid "" "Allows for serving metadata requests coming from a dedicated metadata access " "network whose CIDR is 169.254.169.254/16 (or larger prefix), and is " "connected to a Neutron router from which the VMs send metadata:1 request. In " "this case DHCP Option 121 will not be injected in VMs, as they will be able " "to reach 169.254.169.254 through a router. This option requires " "enable_isolated_metadata = True." msgstr "" "å…è®¸å¤„ç†æ¥è‡ªä¸“用元数æ®è®¿é—®ç½‘络的元数æ®è¯·æ±‚,此网络的 CIDR 为 " "169.254.169.254/16(或更大å‰ç¼€ï¼‰ï¼Œå¹¶ä¸”连接至 VM 从其å‘é€å…ƒæ•°æ®ï¼ˆ1 个请求)的 " "Neutron 路由器。在此情况下,ä¸ä¼šåœ¨ VM 中æ’å…¥ DHCP 选项 121,因为它们能够通过" "路由器到达 169.254.169.254ã€‚æ­¤é€‰é¡¹è¦æ±‚ enable_isolated_metadata = True。" msgid "An RBAC policy already exists with those values." msgstr "已存在带有这些值的 RBAC 策略。" msgid "An identifier must be specified when updating a subnet" msgstr "æ›´æ–°å­ç½‘时,必须指定标识" msgid "An interface driver must be specified" msgstr "必须指定接å£é©±åŠ¨ç¨‹åº" msgid "" "An ordered list of extension driver entrypoints to be loaded from the " "neutron.ml2.extension_drivers namespace. For example: extension_drivers = " "port_security,qos" msgstr "" "è¦ä»Ž neutron.ml2.extension_drivers å称空间装入的扩展驱动程åºå…¥å£ç‚¹çš„æœ‰åºåˆ—" "表。例如:extension_drivers = port_security,qos" msgid "" "An ordered list of networking mechanism driver entrypoints to be loaded from " "the neutron.ml2.mechanism_drivers namespace." msgstr "" "è¦ä»Ž neutron.ml2.mechanism_drivers å称空间装入的è”网机制驱动程åºå…¥å£ç‚¹çš„已排" "åºåˆ—表。" msgid "An unknown error has occurred. Please try your request again." msgstr "å‘ç”ŸæœªçŸ¥é”™è¯¯ã€‚è¯·å†æ¬¡å°è¯•您的请求。" msgid "Async process didn't respawn" msgstr "åŒæ­¥è¿›ç¨‹æœªé‡æ–°è¡ç”Ÿ" msgid "Authorization URL for connecting to designate in admin context" msgstr "管ç†å‘˜ä¸Šä¸‹æ–‡ä¸­è¦æŒ‡å®šçš„è¿žæŽ¥æŽˆæƒ URL" msgid "Automatically remove networks from offline DHCP agents." msgstr "自动从脱机 DHCP 代ç†ç§»é™¤ç½‘络。" msgid "" "Automatically reschedule routers from offline L3 agents to online L3 agents." msgstr "将路由器从脱机 L3 代ç†è‡ªåЍ釿–°å®‰æŽ’è‡³è”æœº L3 代ç†ç¨‹åºã€‚" msgid "Availability zone of this node" msgstr "此节点的å¯ç”¨åŒºåŸŸ" msgid "Available commands" msgstr "å¯ç”¨çš„命令" msgid "Backend does not support VLAN Transparency." msgstr "åŽç«¯ä¸æ”¯æŒ VLAN 逿˜Žã€‚" #, python-format msgid "Base MAC: %s" msgstr "基本 MAC:%s" msgid "" "Base log dir for dnsmasq logging. The log contains DHCP and DNS log " "information and is useful for debugging issues with either DHCP or DNS. If " "this section is null, disable dnsmasq log." msgstr "" "用于 dnsmasq æ—¥å¿—è®°å½•çš„åŸºæœ¬æ—¥å¿—ç›®å½•ã€‚æ—¥å¿—åŒ…å« DHCP å’Œ DNS 日志信æ¯ï¼Œå¯¹äºŽè°ƒè¯• " "DHCP 或 DNS 存在的问题很有用。如果此部分为空,请ç¦ç”¨ dnsmasq 日志。" msgid "Body contains invalid data" msgstr "ä¸»ä½“ä¸­åŒ…å«æ— æ•ˆæ•°æ®" msgid "Both network_id and router_id are None. One must be provided." msgstr "network_id å’Œ router_id 都为 None。必须æä¾›å…¶ä¸­ä¸€é¡¹ã€‚" #, python-format msgid "Bridge %(bridge)s does not exist." msgstr "网桥 %(bridge)s ä¸å­˜åœ¨ã€‚" msgid "Bulk operation not supported" msgstr "æˆæ‰¹æ“作ä¸å—支æŒ" msgid "CIDR to monitor" msgstr "è¦ç›‘视的 CIDR" #, python-format msgid "Callback for %(resource_type)s not found" msgstr "找ä¸åˆ°é’ˆå¯¹ %(resource_type)s 的回调" #, python-format msgid "Callback for %(resource_type)s returned wrong resource type" msgstr "针对 %(resource_type)s 的回调返回了错误的资æºç±»åž‹" #, python-format msgid "Cannot add floating IP to port %s that has no fixed IPv4 addresses" msgstr "无法将浮动 IP 添加至没有固定 IPv4 地å€çš„ç«¯å£ %s" #, python-format msgid "Cannot add multiple callbacks for %(resource_type)s" msgstr "无法添加针对 %(resource_type)s 的多个回调" #, python-format msgid "Cannot allocate IPv%(req_ver)s subnet from IPv%(pool_ver)s subnet pool" msgstr "无法从 IPv%(pool_ver)s å­ç½‘æ± åˆ†é… IPv%(req_ver)s å­ç½‘" msgid "Cannot allocate requested subnet from the available set of prefixes" msgstr "无法从å¯ç”¨çš„一组å‰ç¼€åˆ†é…所请求的å­ç½‘" msgid "Cannot disable enable_dhcp with ipv6 attributes set" msgstr "在设置了 ipv6 属性的情况下,无法ç¦ç”¨ enable_dhcp" #, python-format msgid "Cannot handle subnet of type %(subnet_type)s" msgstr "无法处ç†ç±»åž‹ä¸º %(subnet_type)s çš„å­ç½‘" msgid "Cannot have multiple IPv4 subnets on router port" msgstr "路由器端å£ä¸Šä¸èƒ½æœ‰å¤šä¸ª IPv4 å­ç½‘" #, python-format msgid "" "Cannot have multiple router ports with the same network id if both contain " "IPv6 subnets. Existing port %(p)s has IPv6 subnet(s) and network id %(nid)s" msgstr "" "ä¸å¾—存在多个具有相åŒç½‘络标识的路由器端å£ï¼ˆå¦‚æžœå®ƒä»¬éƒ½åŒ…å« IPv6 å­ç½‘)。现有端" "å£ %(p)s 具有 IPv6 å­ç½‘和网络标识 %(nid)s" #, python-format msgid "" "Cannot host distributed router %(router_id)s on legacy L3 agent %(agent_id)s." msgstr "无法在传统 L3 代ç†ç¨‹åº %(agent_id)s 上托管分布å¼è·¯ç”±å™¨ %(router_id)s。" msgid "Cannot mix IPv4 and IPv6 prefixes in a subnet pool." msgstr "ä¸èƒ½åœ¨å­ç½‘æ± ä¸­åŒæ—¶ä½¿ç”¨ IPv4 å‰ç¼€å’Œ IPv6 å‰ç¼€ã€‚" msgid "Cannot specify both subnet-id and port-id" msgstr "æ— æ³•åŒæ—¶æŒ‡å®š subnet-id å’Œ port-id" msgid "Cannot understand JSON" msgstr "无法ç†è§£ JSON" #, python-format msgid "Cannot update read-only attribute %s" msgstr "无法更新åªè¯»å±žæ€§ %s" msgid "Certificate Authority public key (CA cert) file for ssl" msgstr "用于 SSL 的认è¯ä¸­å¿ƒå…¬ç”¨å¯†é’¥ï¼ˆCA è¯ä¹¦ï¼‰æ–‡ä»¶" #, python-format msgid "" "Change would make usage less than 0 for the following resources: %(unders)s." msgstr "更改导致以下资æºçš„使用率低于 0:%(unders)s。" msgid "Check ebtables installation" msgstr "检查 ebtables 安装" msgid "Check for ARP header match support" msgstr "检查 ARP å¤´åŒ¹é…æ”¯æŒ" msgid "Check for ARP responder support" msgstr "检查 ARP å“åº”ç¨‹åºæ”¯æŒ" msgid "Check for ICMPv6 header match support" msgstr "检查 ICMPv6 å¤´åŒ¹é…æ”¯æŒ" msgid "Check for OVS Geneve support" msgstr "检查 OVS Geneve 支æŒ" msgid "Check for OVS vxlan support" msgstr "检查OVS vxlan支æŒ" msgid "Check for VF management support" msgstr "检查 VF ç®¡ç†æ”¯æŒ" msgid "Check for iproute2 vxlan support" msgstr "检查 iproute2 vxlan 支æŒ" msgid "Check for nova notification support" msgstr "检查 nova 通知支æŒ" msgid "Check for patch port support" msgstr "检查补ä¸ç«¯å£æ”¯æŒ" msgid "Check ip6tables installation" msgstr "检查 ip6tables 安装" msgid "Check ipset installation" msgstr "检查 ipset 安装" msgid "Check keepalived IPv6 support" msgstr "æ£€æŸ¥ä¿æŒæ´»åŠ¨çš„ IPv6 支æŒ" msgid "Check minimal dibbler version" msgstr "检查最低点播器版本" msgid "Check minimal dnsmasq version" msgstr "æ£€æŸ¥æœ€å° dnsmasq 版本" msgid "Check netns permission settings" msgstr "检查 netns è®¸å¯æƒè®¾ç½®" msgid "Check ovs conntrack support" msgstr "检查 ovs conntrack 支æŒ" msgid "Check ovsdb native interface support" msgstr "检查 ovsdb æœ¬æœºæŽ¥å£æ”¯æŒ" #, python-format msgid "" "Cidr %(subnet_cidr)s of subnet %(subnet_id)s overlaps with cidr %(cidr)s of " "subnet %(sub_id)s" msgstr "" "å­ç½‘ %(subnet_id)s çš„ cidr %(subnet_cidr)s 与å­ç½‘ %(sub_id)s çš„ cidr " "%(cidr)s é‡å " msgid "Cleanup resources of a specific agent type only." msgstr "仅清除特定代ç†ç¨‹åºç±»åž‹çš„资æºã€‚" msgid "Client certificate for nova metadata api server." msgstr "nova å…ƒæ•°æ® API æœåŠ¡å™¨çš„å®¢æˆ·æœºè¯ä¹¦ã€‚" msgid "" "Comma-separated list of : tuples, mapping " "network_device to the agent's node-specific list of virtual functions that " "should not be used for virtual networking. vfs_to_exclude is a semicolon-" "separated list of virtual functions to exclude from network_device. The " "network_device in the mapping should appear in the physical_device_mappings " "list." msgstr "" ": 元组的逗å·åˆ†éš”列表,这些元组将 " "network_device 映射至代ç†ç¨‹åºçš„特定于节点的ä¸åº”用于虚拟è”网的功能列表。" "vfs_to_exclude 是è¦ä»Ž network_device 中排除的虚拟功能的分å·åˆ†éš”列表。映射中" "çš„ network_vlan_ranges 应出现在 physical_device_mappings 列表中。" msgid "" "Comma-separated list of : tuples mapping " "physical network names to the agent's node-specific physical network device " "interfaces of SR-IOV physical function to be used for VLAN networks. All " "physical networks listed in network_vlan_ranges on the server should have " "mappings to appropriate interfaces on each agent." msgstr "" " : 元组的逗å·åˆ†éš”列表,这些元组将物ç†ç½‘络å" "称映射至代ç†ç¨‹åºçš„ SR-IOV 物ç†åŠŸèƒ½çš„ç‰¹å®šäºŽèŠ‚ç‚¹çš„ç‰©ç†ç½‘络设备接å£ï¼ˆå°†ç”¨äºŽ " "VLAN 网络)。æœåŠ¡å™¨ä¸Šçš„ network_vlan_ranges 中列示的所有物ç†ç½‘络在æ¯ä¸ªä»£ç†ç¨‹" "åºä¸Šåº”具有指å‘相应接å£çš„æ˜ å°„。" msgid "" "Comma-separated list of : tuples " "mapping physical network names to the agent's node-specific physical network " "interfaces to be used for flat and VLAN networks. All physical networks " "listed in network_vlan_ranges on the server should have mappings to " "appropriate interfaces on each agent." msgstr "" ": 元组的逗å·åˆ†éš”列表,这些元组将物ç†ç½‘" "络å称映射至代ç†ç¨‹åºçš„特定于节点的物ç†ç½‘络接å£ï¼ˆå°†ç”¨äºŽå¹³é¢ç½‘络和 VLAN 网" "络)。æœåŠ¡å™¨ä¸Šçš„ network_vlan_ranges 中列示的所有物ç†ç½‘络在æ¯ä¸ªä»£ç†ç¨‹åºä¸Šåº”å…·" "有指å‘相应接å£çš„æ˜ å°„。" msgid "" "Comma-separated list of : tuples enumerating ranges of GRE " "tunnel IDs that are available for tenant network allocation" msgstr "" "å¯ç”¨äºŽç§Ÿæˆ·ç½‘络分é…çš„ GRE éš§é“æ ‡è¯†çš„ : 元组枚举范围的逗å·åˆ†" "隔列表" msgid "" "Comma-separated list of : tuples enumerating ranges of " "Geneve VNI IDs that are available for tenant network allocation" msgstr "" "枚举å¯ç”¨äºŽç§Ÿæˆ·ç½‘络分é…çš„ Geneve VNI 标识的范围的: 元组的逗" "å·åˆ†éš”列表" msgid "" "Comma-separated list of : tuples enumerating ranges of " "VXLAN VNI IDs that are available for tenant network allocation" msgstr "" "å¯ç”¨äºŽç§Ÿæˆ·ç½‘络分é…çš„ VXLAN VNI 标识的 : 元组枚举范围的逗å·" "分隔列表" msgid "" "Comma-separated list of the DNS servers which will be used as forwarders." msgstr "将用作转å‘器的 DNS æœåŠ¡å™¨çš„é€—å·åˆ†éš”列表。" msgid "Command to execute" msgstr "è¦æ‰§è¡Œçš„命令" msgid "Config file for interface driver (You may also use l3_agent.ini)" msgstr "用于接å£é©±åŠ¨ç¨‹åºçš„é…置文件(还å¯ä½¿ç”¨ l3_agent.ini)" #, python-format msgid "Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s" msgstr "CIDR %(cidr)s 具有冲çªå€¼ ethertype %(ethertype)s " msgid "" "Controls whether the neutron security group API is enabled in the server. It " "should be false when using no security groups or using the nova security " "group API." msgstr "" "控制是å¦åœ¨æœåС噍䏭å¯ç”¨äº† neutron 安全组 API。未使用安全组或使用 nova安全组 " "API 时,它应该为 false。" #, python-format msgid "Could not bind to %(host)s:%(port)s after trying for %(time)d seconds" msgstr "在å°è¯•%(time)d 秒之åŽä¸èƒ½ç»‘定 %(host)s:%(port)s " msgid "Could not deserialize data" msgstr "未能对数æ®è¿›è¡Œååºåˆ—化" #, python-format msgid "" "Current gateway ip %(ip_address)s already in use by port %(port_id)s. Unable " "to update." msgstr "当å‰ç½‘å…³ IP %(ip_address)s å·²ç”±ç«¯å£ %(port_id)s 使用。无法更新。" msgid "" "DHCP lease duration (in seconds). Use -1 to tell dnsmasq to use infinite " "lease times." msgstr "DHCP ç§ŸèµæŒç»­æ—¶é—´ï¼ˆä»¥ç§’计)。使用 -1 告诉 dnsmasq 使用无é™ç§Ÿèµæ—¶é—´ã€‚" msgid "" "DVR deployments for VXLAN/GRE/Geneve underlays require L2-pop to be enabled, " "in both the Agent and Server side." msgstr "VXLAN/GRE/Geneve 底层的 DVR 部署需è¦åœ¨ä»£ç†ç«¯å’ŒæœåŠ¡å™¨ç«¯éƒ½å¯ç”¨ L2-pop。" msgid "" "Database engine for which script will be generated when using offline " "migration." msgstr "使用脱机è¿ç§»æ—¶å°†å¯¹å…¶ç”Ÿæˆè„šæœ¬çš„æ•°æ®åº“引擎。" msgid "Default external networks must be shared to everyone." msgstr "缺çœå¤–部网络必须共享给æ¯ä¸ªäººã€‚" msgid "" "Default network type for external networks when no provider attributes are " "specified. By default it is None, which means that if provider attributes " "are not specified while creating external networks then they will have the " "same type as tenant networks. Allowed values for external_network_type " "config option depend on the network type values configured in type_drivers " "config option." msgstr "" "在未指定æä¾›è€…属性时,外部网络的缺çœç½‘ç»œç±»åž‹ã€‚ç¼ºçœæƒ…况下,它为“无â€ï¼Œè¿™æ„味ç€" "如果在创建外部网络时未指定æä¾›è€…属性,那么它们将与租户网络具有相åŒç±»åž‹ã€‚" "external_network_type é…置选项的å…许值å–决于在 type_drivers é…置选项中所é…ç½®" "的网络类型值。" msgid "" "Default number of RBAC entries allowed per tenant. A negative value means " "unlimited." msgstr "æ¯ä¸ªç§Ÿæˆ·å…è®¸çš„ç¼ºçœ RBAC æ¡ç›®æ•°ã€‚负值表示无é™åˆ¶ã€‚" msgid "" "Default number of resource allowed per tenant. A negative value means " "unlimited." msgstr "æ¯ä¸ªç§Ÿæˆ·å…许的缺çœèµ„æºæ•°ã€‚负值表示无é™ã€‚" msgid "Default security group" msgstr "缺çœå®‰å…¨ç»„" msgid "Default security group already exists." msgstr "缺çœå®‰å…¨ç»„已存在。" msgid "" "Default value of availability zone hints. The availability zone aware " "schedulers use this when the resources availability_zone_hints is empty. " "Multiple availability zones can be specified by a comma separated string. " "This value can be empty. In this case, even if availability_zone_hints for a " "resource is empty, availability zone is considered for high availability " "while scheduling the resource." msgstr "" "å¯ç”¨åŒºåŸŸæç¤ºçš„缺çœå€¼ã€‚如果资æºçš„ availability_zone_hints 为空,那么å¯ç”¨åŒºåŸŸæ„Ÿ" "知调度程åºä½¿ç”¨æ­¤é¡¹ã€‚å¯é€šè¿‡é€—å·åˆ†éš”çš„å­—ç¬¦ä¸²æ¥æŒ‡å®šå¤šä¸ªå¯ç”¨åŒºåŸŸã€‚此值å¯ä¸ºç©ºã€‚在" "此情况下,å³ä½¿èµ„æºçš„ availability_zone_hints ä¸ºç©ºï¼Œé‚£ä¹ˆè°ƒåº¦è¯¥èµ„æºæ—¶ï¼Œå¯ç”¨åŒºåŸŸ" "ä»è¢«è§†ä¸ºå…·å¤‡é«˜å¯ç”¨æ€§ã€‚" msgid "" "Define the default value of enable_snat if not provided in " "external_gateway_info." msgstr "" "如果未æä¾› enable_snat 的值,请在 external_gateway_info 中定义 enable_snat çš„" "缺çœå€¼ã€‚" msgid "" "Defines providers for advanced services using the format: :" ":[:default]" msgstr "" "会使用以下格å¼ä¸ºé«˜çº§æœåŠ¡å®šä¹‰æä¾›ç¨‹åºï¼š::[:" "default]" msgid "Delete the namespace by removing all devices." msgstr "请通过除去所有设备æ¥åˆ é™¤å称空间。" #, python-format msgid "Deleting port %s" msgstr "æ­£åœ¨åˆ é™¤ç«¯å£ %s" #, python-format msgid "Deployment error: %(reason)s." msgstr "部署错误:%(reason)s。" msgid "Destroy IPsets even if there is an iptables reference." msgstr "å³ä½¿å­˜åœ¨ iptables 引用,也会破å IPset。" msgid "Destroy all IPsets." msgstr "ç ´åæ‰€æœ‰ IPset。" #, python-format msgid "Device %(dev_name)s in mapping: %(mapping)s not unique" msgstr "映射 %(mapping)s 中的设备 %(dev_name)s å¹¶éžå”¯ä¸€" #, python-format msgid "Device name %(dev_name)s is missing from physical_device_mappings" msgstr "physical_device_mappings 中缺少设备åç§° %(dev_name)s" msgid "Device not found" msgstr "找ä¸åˆ°è®¾å¤‡" #, python-format msgid "" "Distributed Virtual Router Mac Address for host %(host)s does not exist." msgstr "主机 %(host)s 的分布å¼è™šæ‹Ÿè·¯ç”±å™¨ MAC 地å€ä¸å­˜åœ¨ã€‚" msgid "Domain to use for building the hostnames" msgstr "è¦ç”¨äºŽæž„建主机å的域" msgid "Downgrade no longer supported" msgstr "é™çº§ä¸å†æ”¯æŒ" #, python-format msgid "Driver %s is not unique across providers" msgstr "é©±åŠ¨ç¨‹åº %s 在æä¾›ç¨‹åºä¸­ä¸å”¯ä¸€" msgid "Driver for external DNS integration." msgstr "外部 DNS 集æˆçš„驱动程åºã€‚" msgid "Driver for security groups firewall in the L2 agent" msgstr "L2 代ç†ç¨‹åºä¸­çš„安全组防ç«å¢™çš„驱动程åº" msgid "Driver to use for scheduling network to DHCP agent" msgstr "è¦ç”¨äºŽå¯¹ DHCP 代ç†è°ƒåº¦ç½‘络的驱动程åº" msgid "Driver to use for scheduling router to a default L3 agent" msgstr "è¦ç”¨äºŽå¯¹ç¼ºçœ L3 代ç†è°ƒåº¦è·¯ç”±å™¨çš„驱动程åº" msgid "" "Driver used for ipv6 prefix delegation. This needs to be an entry point " "defined in the neutron.agent.linux.pd_drivers namespace. See setup.cfg for " "entry points included with the neutron source." msgstr "" "用于 IPv6 å‰ç¼€æŽˆæƒçš„驱动程åºã€‚è¿™éœ€è¦æ˜¯ neutron.agent.linux.pd_drivers å称空" "间中定义的入å£ç‚¹ã€‚请å‚阅 setup.cfg 以了解 Neutron æºéšé™„的入å£ç‚¹ã€‚" #, python-format msgid "" "Duplicate L3HARouterAgentPortBinding is created for router(s) %(router)s. " "Database cannot be upgraded. Please, remove all duplicates before upgrading " "the database." msgstr "" "对路由器 %(router)s 创建了é‡å¤ L3HARouterAgentPortBinding。无法å‡çº§æ•°æ®åº“。请" "移除所有é‡å¤é¡¹ï¼Œç„¶åŽå‡çº§æ•°æ®åº“。" msgid "Duplicate Security Group Rule in POST." msgstr "POST 中的安全组规则é‡å¤ã€‚" msgid "Duplicate address detected" msgstr "检测到é‡å¤åœ°å€ã€‚" msgid "Duplicate segment entry in request." msgstr "请求中的段æ¡ç›®é‡å¤ã€‚" #, python-format msgid "ERROR: %s" msgstr "错误:%s" msgid "" "ERROR: Unable to find configuration file via the default search paths (~/." "neutron/, ~/, /etc/neutron/, /etc/) and the '--config-file' option!" msgstr "" "é”™è¯¯ï¼šæ— æ³•é€šè¿‡ç¼ºçœæœç´¢è·¯å¾„(~/.neutron/ã€~/ã€/etc/neutron/ å’Œ /etc/)以åŠâ€œ--" "config-fileâ€é€‰é¡¹æ‰¾åˆ°é…置文件ï¼" msgid "" "Either one of parameter network_id or router_id must be passed to _get_ports " "method." msgstr "傿•° network_id 或 router_id 的其中之一必须传递至_get_ports 方法。" msgid "Either subnet_id or port_id must be specified" msgstr "必须指定 subnet_id 或 port_id" msgid "Empty physical network name." msgstr "空的物ç†ç½‘络å。" msgid "Empty subnet pool prefix list." msgstr "å­ç½‘æ± å‰ç¼€åˆ—表为空。" msgid "Enable HA mode for virtual routers." msgstr "为虚拟路由器å¯ç”¨HA模å¼ã€‚" msgid "Enable SSL on the API server" msgstr "在API æœåŠ¡å™¨ä¸Šæ‰“å¼€SSL" msgid "" "Enable VXLAN on the agent. Can be enabled when agent is managed by ml2 " "plugin using linuxbridge mechanism driver" msgstr "" "请在代ç†ä¸Šå¯ç”¨ VXLAN。å¯åœ¨é€šè¿‡ä½¿ç”¨ linuxbridge 机制驱动程åºç”± ml2 æ’件管ç†ä»£" "ç†æ—¶å¯ç”¨" msgid "" "Enable local ARP responder if it is supported. Requires OVS 2.1 and ML2 " "l2population driver. Allows the switch (when supporting an overlay) to " "respond to an ARP request locally without performing a costly ARP broadcast " "into the overlay." msgstr "" "å¯ç”¨æœ¬åœ° ARP å“应程åºï¼ˆå¦‚æžœå®ƒå—æ”¯æŒï¼‰ã€‚éœ€è¦ OVS 2.1 å’Œ ML2 l2population 驱动" "程åºã€‚å…è®¸äº¤æ¢æœºï¼ˆæ”¯æŒ Overlay 时)在本地å“应ARP 请求而ä¸å¿…æ‰§è¡Œæˆæœ¬é«˜æ˜‚çš„ ARP" "广播到 Overlay 中。" msgid "" "Enable services on an agent with admin_state_up False. If this option is " "False, when admin_state_up of an agent is turned False, services on it will " "be disabled. Agents with admin_state_up False are not selected for automatic " "scheduling regardless of this option. But manual scheduling to such agents " "is available if this option is True." msgstr "" "在 admin_state_up 为 False 的代ç†ä¸Šå¯ç”¨æœåŠ¡ã€‚å¦‚æžœæ­¤é€‰é¡¹ä¸º False,那么当代ç†" "çš„ admin_state_up å˜ä¸º False 时,将ç¦ç”¨è¯¥ä»£ç†ä¸Šçš„æœåŠ¡ã€‚æ— è®ºæ­¤é€‰é¡¹å¦‚ä½•ï¼Œéƒ½ä¸ä¼š" "选择 admin_state_up 为 False 的代ç†è¿›è¡Œè‡ªåŠ¨è°ƒåº¦ã€‚ä½†æ˜¯ï¼Œå¦‚æžœæ­¤é€‰é¡¹ä¸º True,那" "么å¯ä»¥æ‰‹åŠ¨è°ƒåº¦è¿™æ ·çš„ä»£ç†ã€‚" msgid "" "Enables IPv6 Prefix Delegation for automatic subnet CIDR allocation. Set to " "True to enable IPv6 Prefix Delegation for subnet allocation in a PD-capable " "environment. Users making subnet creation requests for IPv6 subnets without " "providing a CIDR or subnetpool ID will be given a CIDR via the Prefix " "Delegation mechanism. Note that enabling PD will override the behavior of " "the default IPv6 subnetpool." msgstr "" "对自动å­ç½‘ CIDR 分é…å¯ç”¨ IPv6 å‰ç¼€ä»£ç†ã€‚设置为 True å°†åœ¨æ”¯æŒ PD 的环境中对å­" "网分é…å¯ç”¨ IPv6 å‰ç¼€ä»£ç†ã€‚如果用户对 IPv6 å­ç½‘å‘出创建å­ç½‘请求但未æä¾› CIDR " "或å­ç½‘池标识,那么系统将通过å‰ç¼€ä»£ç†æœºåˆ¶ä¸ºè¯¥ç”¨æˆ·æä¾› CIDR。请注æ„,å¯ç”¨ PD å°†" "è¦†ç›–ç¼ºçœ IPv6 å­ç½‘池的行为。" msgid "" "Enables the dnsmasq service to provide name resolution for instances via DNS " "resolvers on the host running the DHCP agent. Effectively removes the '--no-" "resolv' option from the dnsmasq process arguments. Adding custom DNS " "resolvers to the 'dnsmasq_dns_servers' option disables this feature." msgstr "" "å¯ç”¨ dnsmasq æœåŠ¡ä»¥åœ¨è¿è¡Œ DHCP 代ç†ç¨‹åºçš„主机上通过 DNS è§£æžå™¨æä¾›å®žä¾‹çš„åç§°" "è§£æžã€‚实际上会从 dnsmasq 进程自å˜é‡ä¸­ç§»é™¤â€œ--no-resolvâ€é€‰é¡¹ã€‚将定制 DNS è§£æžå™¨" "添加至“dnsmasq_dns_serversâ€é€‰é¡¹ä¼šç¦ç”¨æ­¤åŠŸèƒ½éƒ¨ä»¶ã€‚" msgid "End of VLAN range is less than start of VLAN range" msgstr "VLAN范围结æŸå€¼æ¯”开始值å°" msgid "End of tunnel range is less than start of tunnel range" msgstr "éš§é“范围的结æŸå°äºŽéš§é“范围的起始" #, python-format msgid "Error %(reason)s while attempting the operation." msgstr "å°è¯•执行该æ“作时å‘生错误 %(reason)s。" #, python-format msgid "Error parsing dns address %s" msgstr "è§£æž dns åœ°å€ %s 时出错" #, python-format msgid "Error while reading %s" msgstr "è¯»å– %s 时出错" #, python-format msgid "" "Exceeded %s second limit waiting for address to leave the tentative state." msgstr "" "Exceeded %s second limit waiting for address to leave the tentative state." msgid "Existing prefixes must be a subset of the new prefixes" msgstr "现有å‰ç¼€å¿…须是新å‰ç¼€çš„å­é›†" #, python-format msgid "" "Exit code: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" msgstr "" "退出代ç ï¼š%(returncode)dï¼›Stdin:%(stdin)sï¼›Stdout:%(stdout)sï¼›Stderr:" "%(stderr)s" #, python-format msgid "Extension %(driver)s failed." msgstr "扩展 %(driver)s 失败。" #, python-format msgid "" "Extension driver %(driver)s required for service plugin %(service_plugin)s " "not found." msgstr "找ä¸åˆ°æœåŠ¡æ’ä»¶ %(service_plugin)s æ‰€éœ€çš„æ‰©å±•é©±åŠ¨ç¨‹åº %(driver)s。" msgid "" "Extension to use alongside ml2 plugin's l2population mechanism driver. It " "enables the plugin to populate VXLAN forwarding table." msgstr "" "è¦ä¸Ž ml2 æ’ä»¶çš„ l2population 机制驱动程åºä¸€èµ·ä½¿ç”¨çš„æ‰©å±•。它使该æ’件能够填充 " "VXLAN 转å‘表。" #, python-format msgid "Extension with alias %s does not exist" msgstr "具有别å %s 的扩展ä¸å­˜åœ¨" msgid "Extensions list to use" msgstr "è¦ä½¿ç”¨çš„æ‰©å±•列表" #, python-format msgid "Extensions not found: %(extensions)s." msgstr "找ä¸åˆ°æ‰©å±•:%(extensions)s。" #, python-format msgid "External IP %s is the same as the gateway IP" msgstr "外部 IP %s 和网关IP相åŒ" #, python-format msgid "Failed rescheduling router %(router_id)s: no eligible l3 agent found." msgstr "釿–°å®‰æŽ’路由器 %(router_id)s 失败:找ä¸åˆ°åˆæ ¼ L3 代ç†ã€‚" #, python-format msgid "Failed scheduling router %(router_id)s to the L3 Agent %(agent_id)s." msgstr "将路由器 %(router_id)s 调度到 L3 ä»£ç† %(agent_id)s 失败。" #, python-format msgid "Failed to allocate subnet: %(reason)s." msgstr "无法分é…å­ç½‘:%(reason)s。" msgid "" "Failed to associate address scope: subnetpools within an address scope must " "have unique prefixes." msgstr "无法关è”地å€èŒƒå›´ï¼šåœ°å€èŒƒå›´ä¸­çš„å­ç½‘池必须具有唯一å‰ç¼€ã€‚" #, python-format msgid "" "Failed to create port on network %(network_id)s, because fixed_ips included " "invalid subnet %(subnet_id)s" msgstr "" "未能在网络 %(network_id)s 上创建端å£ï¼Œå› ä¸º fixed_ips 包括了无效å­ç½‘ " "%(subnet_id)s" #, python-format msgid "Failed to locate source for %s." msgstr "未能找到 %s çš„æºã€‚" msgid "Failed to remove supplemental groups" msgstr "未能移除补充组" #, python-format msgid "Failed to set gid %s" msgstr "设置gid %s 失败" #, python-format msgid "Failed to set uid %s" msgstr "设置uid %s 失败" #, python-format msgid "Failed to set-up %(type)s tunnel port to %(ip)s" msgstr "未能将 %(type)s éš§é“端å£è®¾ç½®ä¸º %(ip)s" msgid "Failure applying iptables rules" msgstr "应用 iptable 规则时失败" #, python-format msgid "Failure waiting for address %(address)s to become ready: %(reason)s" msgstr "ç­‰å¾…åœ°å€ %(address)s å˜å¾—就绪时å‘生故障:%(reason)s" msgid "Flat provider networks are disabled" msgstr "å¹³é¢ä¾›åº”商网络被ç¦ç”¨" msgid "For TCP/UDP protocols, port_range_min must be <= port_range_max" msgstr "对于 TCP/UDP å议,port_range_min å¿…é¡»å°äºŽç­‰äºŽ port_range_max" msgid "Force ip_lib calls to use the root helper" msgstr "强制ip_lib呼å«ä½¿ç”¨root helper" #, python-format msgid "Found duplicate extension: %(alias)s." msgstr "å‘现é‡å¤æ‰©å±•:%(alias)s。" #, python-format msgid "" "Found overlapping allocation pools: %(pool_1)s %(pool_2)s for subnet " "%(subnet_cidr)s." msgstr "对于å­ç½‘ %(subnet_cidr)s,找到é‡å çš„åˆ†é…æ± ï¼š%(pool_1)s %(pool_2)s" msgid "Gateway IP version inconsistent with allocation pool version" msgstr "网关 IP ç‰ˆæœ¬ä¸Žåˆ†é…æ± ç‰ˆæœ¬ä¸ä¸€è‡´" #, python-format msgid "Gateway ip %(ip_address)s conflicts with allocation pool %(pool)s." msgstr "网关 IP %(ip_address)s ä¸Žåˆ†é…æ±  %(pool)s 冲çªã€‚" msgid "Gateway is not valid on subnet" msgstr "网关在å­ç½‘上无效" msgid "" "Geneve encapsulation header size is dynamic, this value is used to calculate " "the maximum MTU for the driver. This is the sum of the sizes of the outer " "ETH + IP + UDP + GENEVE header sizes. The default size for this field is 50, " "which is the size of the Geneve header without any additional option headers." msgstr "" "Geneve å°è£…头大尿˜¯åЍæ€çš„,此值用于计算驱动程åºçš„æœ€å¤§ MTU。这是外部 ETH + IP " "+ UDP + GENEVE 头大å°çš„æ€»å’Œã€‚此字段的缺çœå¤§å°ä¸º 50,这是没有任何附加选项头的 " "Geneve 头的大å°ã€‚" msgid "" "Group (gid or name) running metadata proxy after its initialization (if " "empty: agent effective group)." msgstr "" "在元数æ®ä»£ç†çš„åˆå§‹åŒ–之åŽï¼Œè¿è¡Œè¯¥ä»£ç†çš„组(gid 或å称),(如果此组为空,那么" "è¿™æ˜¯ä»£ç†æœ‰æ•ˆç»„)。" msgid "Group (gid or name) running this process after its initialization" msgstr "在此进程的åˆå§‹åŒ–之åŽï¼Œè¿è¡Œæ­¤è¿›ç¨‹çš„组(gid 或å称)" msgid "" "Hostname to be used by the Neutron server, agents and services running on " "this machine. All the agents and services running on this machine must use " "the same host value." msgstr "" "Neutron æœåŠ¡å™¨ä»¥åŠæ­¤æœºå™¨ä¸Šè¿è¡Œçš„代ç†ç¨‹åºå’ŒæœåŠ¡è¦ä½¿ç”¨çš„主机å。此机器上è¿è¡Œçš„" "所有代ç†ç¨‹åºå’ŒæœåŠ¡å¿…é¡»ä½¿ç”¨åŒä¸€ä¸»æœºå€¼ã€‚" #, python-format msgid "" "ICMP code (port-range-max) %(value)s is provided but ICMP type (port-range-" "min) is missing." msgstr "" "å·²æä¾› ICMP ä»£ç  (port-range-max) %(value)s,但缺少 ICMP 类型 (port-range-" "min)。" msgid "ID of network" msgstr "网络的标识" msgid "ID of network to probe" msgstr "è¦æŽ¢æµ‹çš„ç½‘ç»œçš„æ ‡è¯†" msgid "ID of probe port to delete" msgstr "è¦åˆ é™¤çš„æŽ¢æµ‹å™¨ç«¯å£çš„æ ‡è¯†" msgid "ID of probe port to execute command" msgstr "用于执行命令的探测器端å£çš„æ ‡è¯†" msgid "ID of the router" msgstr "路由器ID" #, python-format msgid "IP address %(ip)s already allocated in subnet %(subnet_id)s" msgstr "在å­ç½‘ %(subnet_id)s ä¸­å·²åˆ†é… IP åœ°å€ %(ip)s" #, python-format msgid "IP address %(ip)s does not belong to subnet %(subnet_id)s" msgstr "IP åœ°å€ %(ip)s ä¸å±žäºŽå­ç½‘ %(subnet_id)s" msgid "IP allocation failed. Try again later." msgstr "IP 分é…失败。请ç¨åŽé‡è¯•。" msgid "IP allocation requires subnet_id or ip_address" msgstr "IP 分é…éœ€è¦ subnet_id 或 ip_address" #, python-format msgid "" "IPTablesManager.apply failed to apply the following set of iptables rules:\n" "%s" msgstr "" "IPTablesManager.apply 无法应用以下 iptables规则集:\n" "%s" msgid "IPtables conntrack zones exhausted, iptables rules cannot be applied." msgstr "IPtables 连接跟踪区域已用完,无法应用 iptables 规则。" msgid "IPv6 Address Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "对于å‰ç¼€æŽˆæƒï¼ŒIPv6 坻倿–¹å¼å¿…须为 SLAAC 或者“无状æ€â€ã€‚" msgid "IPv6 RA Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "对于å‰ç¼€æŽˆæƒï¼ŒIPv6 RA æ–¹å¼å¿…须为 SLAAC 或者“无状æ€â€ã€‚" #, python-format msgid "" "IPv6 address %(ip)s cannot be directly assigned to a port on subnet " "%(subnet_id)s as the subnet is configured for automatic addresses" msgstr "" "无法直接将 IPv6 åœ°å€ %(ip)s 分é…ç»™å­ç½‘%(subnet_id)s,因为针对自动地å€é…置了该" "å­ç½‘" #, python-format msgid "" "IPv6 subnet %s configured to receive RAs from an external router cannot be " "added to Neutron Router." msgstr "" "无法将已é…置为从外部路由器接收 RA çš„ IPv6 å­ç½‘ %s 添加至 Neutron 路由器。" msgid "" "If True, then allow plugins that support it to create VLAN transparent " "networks." msgstr "如果为 True,那么å…许那些支æŒå®ƒçš„æ’ä»¶åˆ›å»º VLAN 逿˜Žç½‘络。" msgid "Illegal IP version number" msgstr "IP 版本å·ä¸åˆæ³•" #, python-format msgid "" "Illegal prefix bounds: %(prefix_type)s=%(prefixlen)s, %(base_prefix_type)s=" "%(base_prefixlen)s." msgstr "" "éžæ³•å‰ç¼€ç•Œé™ï¼š%(prefix_type)s=%(prefixlen)s,%(base_prefix_type)s=" "%(base_prefixlen)s。" #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot " "associate with address scope %(address_scope_id)s because subnetpool " "ip_version is not %(ip_version)s." msgstr "" "éžæ³•å­ç½‘æ± å…³è”:å­ç½‘æ±  %(subnetpool_id)s ä¸èƒ½ä¸Žåœ°å€èŒƒå›´ %(address_scope_id)s " "å…³è”,因为 subnetpool ip_version å¹¶éž %(ip_version)s。" #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot be " "associated with address scope %(address_scope_id)s." msgstr "" "éžæ³•å­ç½‘æ± å…³è”:å­ç½‘æ±  %(subnetpool_id)s ä¸èƒ½ä¸Žåœ°å€èŒƒå›´ %(address_scope_id)s " "å…³è”。" #, python-format msgid "Illegal subnetpool update : %(reason)s." msgstr "éžæ³•å­ç½‘池更新:%(reason)s。" #, python-format msgid "Illegal update to prefixes: %(msg)s." msgstr "对å‰ç¼€è¿›è¡Œçš„æ›´æ–°éžæ³•:%(msg)s。" msgid "" "In some cases the Neutron router is not present to provide the metadata IP " "but the DHCP server can be used to provide this info. Setting this value " "will force the DHCP server to append specific host routes to the DHCP " "request. If this option is set, then the metadata service will be activated " "for all the networks." msgstr "" "在æŸäº›æƒ…况下,没有 Neutron è·¯ç”±å™¨å¯æä¾›å…ƒæ•°æ® IP,但 DHCP æœåС噍å¯ç”¨äºŽæä¾›æ­¤" "ä¿¡æ¯ã€‚设置此值将强制 DHCP æœåŠ¡å™¨å°†ç‰¹å®šä¸»æœºè·¯ç”±è¿½åŠ è‡³ DHCP 请求。如果设置了此" "é€‰é¡¹ï¼Œé‚£ä¹ˆå°†å¯¹æ‰€æœ‰ç½‘ç»œæ¿€æ´»æ­¤å…ƒæ•°æ®æœåŠ¡ã€‚" msgid "" "Indicates that this L3 agent should also handle routers that do not have an " "external network gateway configured. This option should be True only for a " "single agent in a Neutron deployment, and may be False for all agents if all " "routers must have an external network gateway." msgstr "" "指示此 L3 代ç†ç¨‹åºè¿˜åº”å¤„ç†æœªé…ç½®å¤–éƒ¨ç½‘ç»œç½‘å…³çš„è·¯ç”±å™¨ã€‚æ­¤é€‰é¡¹åªæœ‰åœ¨ç”¨äºŽ " "Neutron 部署中的å•个代ç†ç¨‹åºæ—¶æ‰åº”为 True,在所有路由器必须具有外部网络网关的" "情况下用于所有代ç†ç¨‹åºæ—¶å¯ä¸º False。" #, python-format msgid "Instance of class %(module)s.%(class)s must contain _cache attribute" msgstr "ç±» %(module)s.%(class)s çš„å®žä¾‹å¿…é¡»åŒ…å« _cache 属性" #, python-format msgid "Insufficient prefix space to allocate subnet size /%s" msgstr "没有足够的å‰ç¼€ç©ºé—´æ¥åˆ†é…å­ç½‘å¤§å° /%s" msgid "Insufficient rights for removing default security group." msgstr "æƒåˆ©ä¸è¶³ï¼Œæ— æ³•移除缺çœå®‰å…¨ç»„。" msgid "" "Integration bridge to use. Do not change this parameter unless you have a " "good reason to. This is the name of the OVS integration bridge. There is one " "per hypervisor. The integration bridge acts as a virtual 'patch bay'. All VM " "VIFs are attached to this bridge and then 'patched' according to their " "network connectivity." msgstr "" "è¦ä½¿ç”¨çš„集æˆç½‘桥。ä¸è¦æ›´æ”¹æ­¤å‚æ•°ï¼Œé™¤éžæ‚¨æœ‰æ­£å½“原因。这是 OVS 集æˆç½‘桥的å称。" "æ¯ä¸ª hypervisor 都有一个集æˆç½‘桥。此集æˆç½‘桥充当虚拟“接线架â€ã€‚所有 VM VIF 附" "åŠ è‡³æ­¤ç½‘æ¡¥ï¼Œç„¶åŽæ ¹æ®å…¶ç½‘络连接进行“接线â€ã€‚" msgid "Interface to monitor" msgstr "è¦ç›‘视的接å£" msgid "" "Interval between checks of child process liveness (seconds), use 0 to disable" msgstr "å­è¿›ç¨‹æ´»æ€§æ£€æŸ¥ä¹‹é—´çš„æ—¶é—´é—´éš”(秒),使用 0 æ¥è¿›è¡Œç¦ç”¨" msgid "Interval between two metering measures" msgstr "在采å–ä¸¤ç§æµ‹é‡æŽªæ–½ä¹‹é—´çš„æ—¶é—´é—´éš”" msgid "Interval between two metering reports" msgstr "在生æˆä¸¤ä¸ªæµ‹é‡æŠ¥å‘Šä¹‹é—´çš„æ—¶é—´é—´éš”" #, python-format msgid "Invalid CIDR %(input)s given as IP prefix." msgstr "作为 IP å‰ç¼€æä¾›çš„ CIDR %(input)s 无效。" #, python-format msgid "Invalid Device %(dev_name)s: %(reason)s" msgstr "无效设备 %(dev_name)s:%(reason)s" #, python-format msgid "" "Invalid action '%(action)s' for object type '%(object_type)s'. Valid " "actions: %(valid_actions)s" msgstr "" "针对对象类型“%(object_type)sâ€çš„æ“ä½œâ€œ%(action)sâ€æ— æ•ˆã€‚有效æ“作为:" "%(valid_actions)s" #, python-format msgid "" "Invalid authentication type: %(auth_type)s, valid types are: " "%(valid_auth_types)s" msgstr "认è¯ç±»åž‹ %(auth_type)s 无效,以下是有效类型:%(valid_auth_types)s" #, python-format msgid "Invalid ethertype %(ethertype)s for protocol %(protocol)s." msgstr "ethertype %(ethertype)s 对åè®® %(protocol)s 无效。" #, python-format msgid "Invalid format: %s" msgstr "æ ¼å¼æ— æ•ˆï¼š%s" #, python-format msgid "Invalid instance state: %(state)s, valid states are: %(valid_states)s" msgstr "å®žä¾‹çŠ¶æ€ %(state)s 无效,以下是有效状æ€ï¼š%(valid_states)s" #, python-format msgid "Invalid mapping: '%s'" msgstr "映射无效:“%sâ€" #, python-format msgid "Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'." msgstr "无效网络 VLAN 范围:“%(vlan_range)sâ€-“%(error)sâ€ã€‚" #, python-format msgid "Invalid network VXLAN port range: '%(vxlan_range)s'." msgstr "无效网络 VXLAN 端å£èŒƒå›´ï¼šâ€œ%(vxlan_range)sâ€ã€‚" #, python-format msgid "Invalid pci slot %(pci_slot)s" msgstr "无效 PCI æ’æ§½ %(pci_slot)s" #, python-format msgid "Invalid provider format. Last part should be 'default' or empty: %s" msgstr "æä¾›ç¨‹åºæ ¼å¼æ— æ•ˆã€‚最åŽéƒ¨åˆ†åº”该为“defaultâ€æˆ–空:%s" #, python-format msgid "Invalid resource type %(resource_type)s" msgstr "无效资æºç±»åž‹ %(resource_type)s" #, python-format msgid "Invalid route: %s" msgstr "路由无效:%s" msgid "Invalid service provider format" msgstr "æœåŠ¡æä¾›ç¨‹åºæ ¼å¼æ— æ•ˆ" #, python-format msgid "" "Invalid value for ICMP %(field)s (%(attr)s) %(value)s. It must be 0 to 255." msgstr "ICMP %(field)s (%(attr)s) 的值 %(value)s 无效。它必须为 0 到 255。" #, python-format msgid "Invalid value for port %(port)s" msgstr "ç«¯å£ %(port)s 的值无效" msgid "" "Iptables mangle mark used to mark ingress from external network. This mark " "will be masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "ç”¨æ¥æ ‡è®°å¤–部网络中的入å£çš„ Iptables mangle 标记。将使用 0xffff æ¥å±è”½æ­¤æ ‡è®°ï¼Œ" "以便将仅使用低ä½çš„ 16 ä½ã€‚" msgid "" "Iptables mangle mark used to mark metadata valid requests. This mark will be " "masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "ç”¨æ¥æ ‡è®°å…ƒæ•°æ®æœ‰æ•ˆè¯·æ±‚çš„ Iptables mangle 标记。将使用 0xffff æ¥å±è”½æ­¤æ ‡è®°ï¼Œä»¥" "便将仅使用低ä½çš„ 16 ä½ã€‚" msgid "Keepalived didn't respawn" msgstr "ä¿æŒæ´»åŠ¨çš„æœªé‡æ–°è¡ç”Ÿ" msgid "Keepalived didn't spawn" msgstr "Keepalived 未è¡ç”Ÿ" #, python-format msgid "" "Kernel HZ value %(value)s is not valid. This value must be greater than 0." msgstr "内核 HZ 值 %(value)s 无效。此值必须大于 0。" msgid "L3 agent failure to setup NAT for floating IPs" msgstr "L3 代ç†ç¨‹åºæ— æ³•为浮动 IP 设置 NAT" msgid "L3 agent failure to setup floating IPs" msgstr "L3 代ç†ç¨‹åºæ— æ³•设置浮动 IP" msgid "Limit number of leases to prevent a denial-of-service." msgstr "è¯·å¯¹ç§Ÿèµæ•°è¿›è¡Œé™åˆ¶ï¼Œä»¥é˜²æ­¢æ‹’ç»æœåŠ¡ã€‚" msgid "List of :" msgstr ": 的列表" msgid "" "List of :: or " "specifying physical_network names usable for VLAN provider and tenant " "networks, as well as ranges of VLAN tags on each available for allocation to " "tenant networks." msgstr "" "为VLANæä¾›å•†å’Œç§Ÿæˆ·ç½‘络æä¾›:: 或 " "专属物ç†ç½‘络å称,从事实现æ¯ä¸ªç§Ÿæˆ·ç½‘络å¯ä»¥åˆ†é…到相应的VLAN" "标识。" msgid "" "List of network type driver entrypoints to be loaded from the neutron.ml2." "type_drivers namespace." msgstr "" "è¦ä»Ž neutron.ml2.type_drivers å称空间装入的网络类型驱动程åºå…¥å£ç‚¹çš„列表。" msgid "" "List of physical_network names with which flat networks can be created. Use " "default '*' to allow flat networks with arbitrary physical_network names. " "Use an empty list to disable flat networks." msgstr "" "å¯é€šè¿‡å…¶åˆ›å»ºå¹³é¢ç½‘络的 physical_network å称的列表。使用缺çœå€¼â€œ*â€å°†å…许平é¢ç½‘" "ç»œä½¿ç”¨ä»»æ„ physical_network å称。使用空列表将ç¦ç”¨å¹³é¢ç½‘络。" msgid "Location for Metadata Proxy UNIX domain socket." msgstr "元数æ®ä»£ç† UNIX 域套接字的ä½ç½®ã€‚" msgid "Location of Metadata Proxy UNIX domain socket" msgstr "元数æ®ä»£ç† UNIX 域套接字的ä½ç½®" msgid "Location to store DHCP server config files." msgstr "用于存储 DHCP æœåС噍é…置文件的ä½ç½®ã€‚" msgid "Location to store IPv6 PD files." msgstr "用æ¥å­˜å‚¨ IPv6 PD 文件的ä½ç½®ã€‚" msgid "Location to store IPv6 RA config files" msgstr "用于存储 IPv6 RA é…置文件的ä½ç½®" msgid "Location to store child pid files" msgstr "ç”¨äºŽå­˜å‚¨å­ pid 文件的ä½ç½®" msgid "Location to store keepalived/conntrackd config files" msgstr "ç”¨äºŽå­˜å‚¨ä¿æŒæ´»åŠ¨çš„/连接跟踪的é…置文件的ä½ç½®" msgid "Log agent heartbeats" msgstr "日志代ç†ç¨‹åºè„‰åŠ¨ä¿¡å·" msgid "" "MTU of the underlying physical network. Neutron uses this value to calculate " "MTU for all virtual network components. For flat and VLAN networks, neutron " "uses this value without modification. For overlay networks such as VXLAN, " "neutron automatically subtracts the overlay protocol overhead from this " "value. Defaults to 1500, the standard value for Ethernet." msgstr "" "底层物ç†ç½‘络的 MTU。Neutron 使用此值计算所有虚拟网络组件的 MTU。对于平é¢ç½‘络" "å’Œ VLAN 网络,neutron 使用此值而ä¸åšä¿®æ”¹ã€‚对于 VXLAN 之类的覆盖网络,neutron " "自动从此值å‡åŽ»è¦†ç›–å议开销。缺çœä¸º 1500(这是以太网的标准值)。" msgid "MTU size of veth interfaces" msgstr "veth 接å£çš„ MTU 大å°" msgid "Make the l2 agent run in DVR mode." msgstr "使 l2 代ç†åœ¨ DVR æ–¹å¼ä¸‹è¿è¡Œã€‚" msgid "Malformed request body" msgstr "请求主体的格å¼ä¸æ­£ç¡®" #, python-format msgid "Malformed request body: %(reason)s." msgstr "请求主体格å¼ä¸æ­£ç¡®ï¼š%(reason)s。" msgid "MaxRtrAdvInterval setting for radvd.conf" msgstr "radvd.conf çš„ MaxRtrAdvInterval 设置" msgid "Maximum number of DNS nameservers per subnet" msgstr "æ¯ä¸ªå­ç½‘çš„ DNS åç§°æœåŠ¡å™¨çš„æœ€å¤§æ•°ç›®" msgid "" "Maximum number of L3 agents which a HA router will be scheduled on. If it is " "set to 0 then the router will be scheduled on every agent." msgstr "" "将对其调度 HA 路由器的 L3 代ç†ç¨‹åºçš„æœ€å¤§æ•°ç›®ã€‚如果设置为 0,那么将对æ¯ä¸ªä»£ç†" "程åºè°ƒåº¦è¯¥è·¯ç”±å™¨ã€‚" msgid "Maximum number of allowed address pairs" msgstr "å…许的最大地å€å¯¹æ•°" msgid "Maximum number of host routes per subnet" msgstr "æ¯ä¸ªå­ç½‘的最大主机路由数" msgid "Maximum number of routes per router" msgstr "æ¯ä¸ªè·¯ç”±å™¨çš„æœ€å¤§è·¯ç”±æ•°ç›®" msgid "" "Metadata Proxy UNIX domain socket mode, 4 values allowed: 'deduce': deduce " "mode from metadata_proxy_user/group values, 'user': set metadata proxy " "socket mode to 0o644, to use when metadata_proxy_user is agent effective " "user or root, 'group': set metadata proxy socket mode to 0o664, to use when " "metadata_proxy_group is agent effective group or root, 'all': set metadata " "proxy socket mode to 0o666, to use otherwise." msgstr "" "元数æ®ä»£ç† UNIX 域套接字方å¼ï¼Œå…许使用以下 4 个值:“deduceâ€ï¼šæ ¹æ® " "metadata_proxy_user/group 值进行推论的推论方å¼ï¼›â€œuserâ€ï¼šå°†å…ƒæ•°æ®ä»£ç†å¥—接字方" "å¼è®¾ç½®ä¸º 0o644,以在 metadata_proxy_user ä¸ºä»£ç†æœ‰æ•ˆç”¨æˆ·æˆ–者 root 用户时使" "用;“groupâ€ï¼šå°†å…ƒæ•°æ®ä»£ç†å¥—接字方å¼è®¾ç½®ä¸º 0o664,以在 metadata_proxy_group 为" "ä»£ç†æœ‰æ•ˆç»„或者 root 用户时使用;“allâ€ï¼šå°†å…ƒæ•°æ®ä»£ç†å¥—接字方å¼è®¾ç½®ä¸º 0o666,以" "在其他情况下使用。" msgid "Metering driver" msgstr "测é‡é©±åŠ¨ç¨‹åº" msgid "MinRtrAdvInterval setting for radvd.conf" msgstr "radvd.conf çš„ MinRtrAdvInterval 设置" msgid "Minimize polling by monitoring ovsdb for interface changes." msgstr "请通过监视 ovsdb ä»¥èŽ·å–æŽ¥å£æ›´æ”¹æ¥æœ€å¤§ç¨‹åº¦åœ°å‡å°‘轮询。" #, python-format msgid "Missing key in mapping: '%s'" msgstr "映射中缺少键:“%sâ€" msgid "" "Multicast group for VXLAN. When configured, will enable sending all " "broadcast traffic to this multicast group. When left unconfigured, will " "disable multicast VXLAN mode." msgstr "" "VXLAN 的多点广播组。如果é…置了此项,那么系统å…许将所有广播æµé‡å‘é€è‡³æ­¤å¤šç‚¹å¹¿" "播组。如果ä¿ç•™ä¸ºæœªé…置,那么系统将ç¦ç”¨å¤šç‚¹å¹¿æ’­ VXLAN æ–¹å¼ã€‚" msgid "" "Multicast group(s) for vxlan interface. A range of group addresses may be " "specified by using CIDR notation. Specifying a range allows different VNIs " "to use different group addresses, reducing or eliminating spurious broadcast " "traffic to the tunnel endpoints. To reserve a unique group for each possible " "(24-bit) VNI, use a /8 such as 239.0.0.0/8. This setting must be the same on " "all the agents." msgstr "" "VXLAN 接å£çš„多点广播组。必须使用 CIDR 注释指定一定范围的组地å€ã€‚指定范围å…许" "ä¸åŒ VNI 使用ä¸åŒç»„地å€ï¼Œä»Žè€Œå‡å°‘或消除å‘é€è‡³é€šé“端点的伪造广播æµé‡ã€‚为对æ¯ä¸ª" "å¯èƒ½çš„(24 ä½ï¼‰VNI ä¿ç•™å”¯ä¸€ç»„,请使用 /8,例如,239.0.0.0/8。此设置在所有代ç†" "程åºä¸Šå¿…须相åŒã€‚" #, python-format msgid "Multiple default providers for service %s" msgstr "对于æœåŠ¡ %sï¼Œå­˜åœ¨å¤šä¸ªç¼ºçœæä¾›ç¨‹åº" #, python-format msgid "Multiple plugins for service %s were configured" msgstr "å·²é…置多个用于æœåŠ¡ %s çš„æ’ä»¶" #, python-format msgid "Multiple providers specified for service %s" msgstr "对于æœåŠ¡ %s,已指定多个æä¾›ç¨‹åº" msgid "Multiple tenant_ids in bulk security group rule create not allowed" msgstr "ä¸å…è®¸åœ¨æˆæ‰¹å®‰å…¨ç»„规则创建中使用多个 tenant_id" msgid "Must also specify protocol if port range is given." msgstr "如果指定了端å£èŒƒå›´ï¼Œé‚£ä¹ˆè¿˜å¿…须指定å议。" msgid "Must specify one or more actions on flow addition or modification" msgstr "å¿…é¡»åœ¨æ·»åŠ æˆ–åˆ é™¤æµæ—¶æŒ‡å®šä¸€ä¸ªæˆ–多个æ“作" msgid "Name of Open vSwitch bridge to use" msgstr "è¦ä½¿ç”¨çš„已打开 vSwitch 网桥的åç§°" msgid "" "Name of nova region to use. Useful if keystone manages more than one region." msgstr "è¦ä½¿ç”¨çš„ nova 区域的å称。如果 keystone 管ç†å¤šä¸ªåŒºåŸŸï¼Œé‚£ä¹ˆè¿™å¾ˆæœ‰ç”¨ã€‚" msgid "Namespace of the router" msgstr "路由器å字空间" msgid "Native pagination depend on native sorting" msgstr "本机分页ä¾èµ–于本机排åº" #, python-format msgid "" "Need to apply migrations from %(project)s contract branch. This will require " "all Neutron server instances to be shutdown before proceeding with the " "upgrade." msgstr "" "需è¦ä»Ž %(project)s åˆåŒåˆ†æ”¯åº”用è¿ç§»ã€‚è¿™è¦æ±‚所有 Neutron æœåŠ¡å™¨å®žä¾‹å…³é—­ï¼Œç„¶åŽ" "ç»§ç»­å‡çº§ã€‚" msgid "Negative delta (downgrade) not supported" msgstr "䏿”¯æŒä¸ºè´Ÿæ•°çš„增é‡ä¿®è®¢ç‰ˆï¼ˆé™çº§ï¼‰" msgid "Negative relative revision (downgrade) not supported" msgstr "䏿”¯æŒä¸ºè´Ÿæ•°çš„相关修订版(é™çº§ï¼‰" #, python-format msgid "Network %s does not contain any IPv4 subnet" msgstr "网络 %s 中ä¸åŒ…å«ä»»ä½• IPv4 å­ç½‘" #, python-format msgid "Network %s is not a valid external network" msgstr "网络 %s 是无效外部网络" #, python-format msgid "Network %s is not an external network" msgstr "网络 %s 䏿˜¯å¤–部网络" #, python-format msgid "" "Network of size %(size)s, from IP range %(parent_range)s excluding IP ranges " "%(excluded_ranges)s was not found." msgstr "" "从 IP 范围 %(parent_range)s(排除 IP 范围%(excluded_ranges)s)中找ä¸åˆ°å¤§å°ä¸º " "%(size)s 的网络。" #, python-format msgid "Network type value '%s' not supported" msgstr "网络类型值“%sâ€ä¸å—支æŒ" msgid "Network type value needed by the ML2 plugin" msgstr "ML2 æ’件需è¦ç½‘络类型值" msgid "Network types supported by the agent (gre and/or vxlan)." msgstr "ä»£ç†æ”¯æŒçš„网络类型(gre å’Œ/或 vxlan)。" msgid "Neutron Service Type Management" msgstr "Neutron æœåŠ¡ç±»åž‹ç®¡ç†" msgid "Neutron core_plugin not configured!" msgstr "未é…ç½® Neutron core_plugin!" msgid "No default router:external network" msgstr "没有缺çœè·¯ç”±å™¨ï¼šå¤–部网络" #, python-format msgid "No default subnetpool found for IPv%s" msgstr "找ä¸åˆ°å¯¹åº” IPv%s 的缺çœå­ç½‘æ± " msgid "No default subnetpools defined" msgstr "未定义缺çœå­ç½‘æ± " #, python-format msgid "No eligible l3 agent associated with external network %s found" msgstr "找ä¸åˆ°åˆæ ¼çš„与外部网络 %s å…³è”çš„ L3 代ç†" #, python-format msgid "No more IP addresses available for subnet %(subnet_id)s." msgstr "没有更多 IP 地å€å¯ç”¨äºŽå­ç½‘ %(subnet_id)s。" msgid "No offline migrations pending." msgstr "没有脱机è¿ç§»å¤„于暂挂状æ€ã€‚" #, python-format msgid "No shared key in %s fields" msgstr "%s 字段中没有共享键" msgid "Not allowed to manually assign a router to an agent in 'dvr' mode." msgstr "ä¸å…许以“dvrâ€æ–¹å¼å°†è·¯ç”±å™¨æ‰‹åŠ¨åˆ†é…给代ç†ç¨‹åºã€‚" msgid "Not allowed to manually remove a router from an agent in 'dvr' mode." msgstr "ä¸å…许以“dvrâ€æ–¹å¼ä»Žä»£ç†ç¨‹åºæ‰‹åŠ¨ç§»é™¤è·¯ç”±å™¨ã€‚" msgid "" "Number of DHCP agents scheduled to host a tenant network. If this number is " "greater than 1, the scheduler automatically assigns multiple DHCP agents for " "a given tenant network, providing high availability for DHCP service." msgstr "" "已调度的主管租户网络的 DHCP ä»£ç†æ•°ã€‚如果此数目大于 1,那么调度程åºä¼šè‡ªåŠ¨ä¸ºæ‰€" "给定的租户网络分é…多个 DHCP 代ç†ï¼Œä»Žè€Œä¸º DHCP æœåŠ¡æä¾›é«˜å¯ç”¨æ€§ã€‚" msgid "Number of backlog requests to configure the metadata server socket with" msgstr "关于é…ç½®å…ƒæ•°æ®æœåŠ¡å™¨å¥—æŽ¥å­—çš„å‚¨å¤‡è¯·æ±‚æ•°" msgid "Number of backlog requests to configure the socket with" msgstr "积压许多é…ç½®socket的请求" msgid "" "Number of bits in an ipv4 PTR zone that will be considered network prefix. " "It has to align to byte boundary. Minimum value is 8. Maximum value is 24. " "As a consequence, range of values is 8, 16 and 24" msgstr "" "ipv4 PTR 区域中将被视为网络å‰ç¼€çš„使•°ã€‚它必须符åˆå­—节é™åˆ¶ã€‚最å°å€¼ä¸º 8。最大值" "为 24。因此,有效值包括:8ã€16 å’Œ 24。" msgid "" "Number of bits in an ipv6 PTR zone that will be considered network prefix. " "It has to align to nyble boundary. Minimum value is 4. Maximum value is 124. " "As a consequence, range of values is 4, 8, 12, 16,..., 124" msgstr "" "ipv6 PTR 区域中将被视为网络å‰ç¼€çš„使•°ã€‚它必须符åˆå­—节é™åˆ¶ã€‚最å°å€¼ä¸º 4。最大值" "为 124。因此,有效值包括:4,8,12,16,...,124。" msgid "" "Number of floating IPs allowed per tenant. A negative value means unlimited." msgstr "æ¯ä¸ªç§Ÿæˆ·å…许的浮动 IP 数。负值表示无é™ã€‚" msgid "" "Number of networks allowed per tenant. A negative value means unlimited." msgstr "æ¯ä¸ªç§Ÿæˆ·å…许的网络数。负值表示无é™ã€‚" msgid "Number of ports allowed per tenant. A negative value means unlimited." msgstr "æ¯ä¸ªç§Ÿæˆ·å…è®¸çš„ç«¯å£æ•°ã€‚负值表示无é™ã€‚" msgid "Number of routers allowed per tenant. A negative value means unlimited." msgstr "æ¯ä¸ªç§Ÿæˆ·å…许的路由器数。负值表示无é™ã€‚" msgid "" "Number of seconds between sending events to nova if there are any events to " "send." msgstr "å‰åŽä¸¤æ¬¡å°†äº‹ä»¶å‘é€è‡³ nova 的间隔秒数(如果有事件è¦å‘é€ï¼‰ã€‚" msgid "Number of seconds to keep retrying to listen" msgstr "è‹¥å¹²ç§’ä¿æŒé‡è¯•监å¬" msgid "" "Number of security groups allowed per tenant. A negative value means " "unlimited." msgstr "æ¯ä¸ªç§Ÿæˆ·å…许的安全组数。负值表示无é™ã€‚" msgid "" "Number of security rules allowed per tenant. A negative value means " "unlimited." msgstr "æ¯ä¸ªç§Ÿæˆ·å…许的安全性规则数。负值表示无é™ã€‚" msgid "" "Number of separate API worker processes for service. If not specified, the " "default is equal to the number of CPUs available for best performance." msgstr "" "针对æœåŠ¡çš„ä¸åŒAPI执行程åºçš„ç¼–å·ã€‚如果没有指定,默认等于最佳性能下的å¯ç”¨CPUçš„" "个数值" msgid "" "Number of separate worker processes for metadata server (defaults to half of " "the number of CPUs)" msgstr "å…ƒæ•°æ®æœåŠ¡å™¨çš„å•独工作程åºè¿›ç¨‹æ•°ï¼ˆç¼ºçœè®¾ç½®ä¸º CPU 数目的一åŠï¼‰" msgid "Number of subnets allowed per tenant, A negative value means unlimited." msgstr "æ¯ä¸ªç§Ÿæˆ·å…许的å­ç½‘数。负值表示无é™ã€‚" msgid "" "Number of threads to use during sync process. Should not exceed connection " "pool size configured on server." msgstr "åŒæ­¥è¿‡ç¨‹ä¸­è¦ä½¿ç”¨çš„线程数。ä¸åº”超过在æœåŠ¡å™¨ä¸Šé…置的连接池大å°ã€‚" msgid "OK" msgstr "确定" msgid "" "OVS datapath to use. 'system' is the default value and corresponds to the " "kernel datapath. To enable the userspace datapath set this value to 'netdev'." msgstr "" "è¦ä½¿ç”¨çš„ OVS æ•°æ®è·¯å¾„。“systemâ€æ˜¯ç¼ºçœå€¼ï¼Œå¯¹åº”内核数æ®è·¯å¾„,è¦å¯ç”¨ç”¨æˆ·ç©ºé—´æ•°æ®" "路径,请将此值设置为“netdevâ€ã€‚" msgid "OVS vhost-user socket directory." msgstr "OVS vhost-user 套接字目录。" #, python-format msgid "Object action %(action)s failed because: %(reason)s." msgstr "对象æ“作 %(action)s 失败,因为:%(reason)s。" msgid "Only admin can view or configure quota" msgstr "åªæœ‰ç®¡ç†å‘˜æ‰èƒ½æŸ¥çœ‹æˆ–é…ç½®é…é¢" msgid "Only admin is authorized to access quotas for another tenant" msgstr "åªæœ‰ç®¡ç†å‘˜æ‰æœ‰æƒè®¿é—®å¦ä¸€ç§Ÿæˆ·çš„é…é¢" msgid "Only admins can manipulate policies on objects they do not own" msgstr "åªæœ‰ç®¡ç†å‘˜æ‰èƒ½å¤„ç†é’ˆå¯¹å¹¶éžä»–们所有的对象的策略" msgid "Only allowed to update rules for one security profile at a time" msgstr "一次仅å…è®¸ä¸ºä¸€ä¸ªå®‰å…¨æ¦‚è¦æ–‡ä»¶æ›´æ–°è§„则" msgid "Only remote_ip_prefix or remote_group_id may be provided." msgstr "åªèƒ½æä¾› remote_ip_prefix 或 remote_group_id。" msgid "OpenFlow interface to use." msgstr "è¦ä½¿ç”¨çš„ OpenFlow 接å£ã€‚" #, python-format msgid "" "Operation %(op)s is not supported for device_owner %(device_owner)s on port " "%(port_id)s." msgstr "" "ç«¯å£ %(port_id)s 上的 device_owner %(device_owner)s 䏿”¯æŒæ“作 %(op)s。" #, python-format msgid "Operation not supported on device %(dev_name)s" msgstr "æ“作在设备 %(dev_name)s 上ä¸å—支æŒ" msgid "" "Ordered list of network_types to allocate as tenant networks. The default " "value 'local' is useful for single-box testing but provides no connectivity " "between hosts." msgstr "" "è¦ä½œä¸ºç§Ÿæˆ·ç½‘络分é…çš„ network_type 的有åºåˆ—表。缺çœå€¼â€œlocalâ€å¯¹å•框测试很有用," "但ä¸ä¼šåœ¨ä¸»æœºä¹‹é—´æä¾›è¿žæŽ¥ã€‚" msgid "Override the default dnsmasq settings with this file." msgstr "ä½¿ç”¨æ­¤æ–‡ä»¶è¦†ç›–ç¼ºçœ dnsmasq 设置。" msgid "Owner type of the device: network/compute" msgstr "设备的所有者类型如下:网络/计算" msgid "POST requests are not supported on this resource." msgstr "POST 请求在此资æºä¸Šä¸å—支æŒã€‚" #, python-format msgid "Package %s not installed" msgstr "未安装软件包 %s" #, python-format msgid "Parsing bridge_mappings failed: %s." msgstr "è§£æž bridge_mappings 失败:%s。" msgid "Password for connecting to designate in admin context" msgstr "管ç†å‘˜ä¸Šä¸‹æ–‡ä¸­è¦æŒ‡å®šçš„连接密ç " msgid "Path to PID file for this process" msgstr "此进程的 PID 文件的路径" msgid "Path to the router directory" msgstr "直连路由器的路径" msgid "Peer patch port in integration bridge for tunnel bridge." msgstr "集æˆç½‘桥中的åŒçº§è¡¥ä¸ç«¯å£ï¼ˆå¯¹äºŽéš§é“网桥)。" msgid "Peer patch port in tunnel bridge for integration bridge." msgstr "éš§é“网桥中的åŒçº§è¡¥ä¸ç«¯å£ï¼ˆå¯¹äºŽé›†æˆç½‘桥)。" msgid "Per-tenant subnet pool prefix quota exceeded." msgstr "超出æ¯ä¸ªç§Ÿæˆ·çš„å­ç½‘æ± å‰ç¼€é…é¢ã€‚" msgid "Phase upgrade options do not accept revision specification" msgstr "阶段å‡çº§é€‰é¡¹ä¸æŽ¥å—修订规范" msgid "Ping timeout" msgstr "Ping è¶…æ—¶" msgid "Plugin does not support updating provider attributes" msgstr "æ’件䏿”¯æŒæ›´æ–°æä¾›ç¨‹åºå±žæ€§" #, python-format msgid "Port %(id)s does not have fixed ip %(address)s" msgstr "ç«¯å£ %(id)s 没有固定 ip %(address)s" #, python-format msgid "Port %(port_id)s is already acquired by another DHCP agent" msgstr "å¦ä¸€ DHCP 代ç†ç¨‹åºå°šæœªèŽ·å–ç«¯å£ %(port_id)s" #, python-format msgid "" "Port %s has multiple fixed IPv4 addresses. Must provide a specific IPv4 " "address when assigning a floating IP" msgstr "" "ç«¯å£ %s 具有多个固定 IPv4 地å€ã€‚å½“åˆ†é…æµ®åЍ IP 时,必须æä¾›ç‰¹å®š IPv4 地å€" msgid "" "Port to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "对于 OpenFlow 连接,è¦ä¾¦å¬çš„端å£ã€‚仅用于“本机â€é©±åŠ¨ç¨‹åºã€‚" #, python-format msgid "Prefix '%(prefix)s' not supported in IPv%(version)s pool." msgstr "å‰ç¼€â€œ%(prefix)sâ€åœ¨ IPv%(version)s 池中ä¸å—支æŒã€‚" msgid "Prefix Delegation can only be used with IPv6 subnets." msgstr "å‰ç¼€æŽˆæƒåªèƒ½ç”¨äºŽ IPv6 å­ç½‘。" msgid "Private key of client certificate." msgstr "客户机è¯ä¹¦çš„专用密钥。" #, python-format msgid "Probe %s deleted" msgstr "已删除探测器 %s" #, python-format msgid "Probe created : %s " msgstr "已创建探测器:%s " msgid "Process is already started" msgstr "进程已ç»å¯åЍ" msgid "Process is not running." msgstr "进程未è¿è¡Œ" msgid "Protocol to access nova metadata, http or https" msgstr "用于访问 nova 元数æ®çš„å议(HTTP 或 HTTPS)" #, python-format msgid "Provider name %(name)s is limited by %(len)s characters" msgstr "供应商åç§° %(name)s 被é™åˆ¶ä¸ºä¸è¶…过 %(len)s 个字符" #, python-format msgid "QoS Policy %(policy_id)s is used by %(object_type)s %(object_id)s." msgstr "%(object_type)s %(object_id)s 使用了 QoS ç­–ç•¥ %(policy_id)s。" #, python-format msgid "" "QoS binding for network %(net_id)s and policy %(policy_id)s could not be " "found." msgstr "找ä¸åˆ°ç½‘络 %(net_id)s 和策略 %(policy_id)s çš„ QoS 绑定。" #, python-format msgid "" "QoS binding for port %(port_id)s and policy %(policy_id)s could not be found." msgstr "找ä¸åˆ°ç«¯å£ %(port_id)s 和策略 %(policy_id)s çš„ QoS 绑定。" #, python-format msgid "QoS policy %(policy_id)s could not be found." msgstr "找ä¸åˆ° QoS ç­–ç•¥ %(policy_id)s。" #, python-format msgid "QoS rule %(rule_id)s for policy %(policy_id)s could not be found." msgstr "找ä¸åˆ°ç­–ç•¥ %(policy_id)s çš„ QoS 规则 %(rule_id)s。" #, python-format msgid "RBAC policy of type %(object_type)s with ID %(id)s not found" msgstr "找ä¸åˆ°æ ‡è¯†ä¸º %(id)s çš„ %(object_type)s 类型的 RBAC ç­–ç•¥" #, python-format msgid "" "RBAC policy on object %(object_id)s cannot be removed because other objects " "depend on it.\n" "Details: %(details)s" msgstr "" "无法移除对象 %(object_id)s çš„ RBAC 策略,因为其他对象ä¾èµ–于它。\n" "详细信æ¯ï¼š%(details)s" msgid "" "Range of seconds to randomly delay when starting the periodic task scheduler " "to reduce stampeding. (Disable by setting to 0)" msgstr "" "当å¯åŠ¨å®šæœŸä»»åŠ¡è°ƒåº¦ç¨‹åºä»¥å‡å°‘拥堵时è¦éšæœºå»¶è¿Ÿçš„ç§’æ•°èŒƒå›´.(通过设置为 0 æ¥ç¦ç”¨)" msgid "Ranges must be in the same IP version" msgstr "范围必须为åŒä¸€ IP 版本" msgid "Ranges must be netaddr.IPRange" msgstr "范围必须为 netaddr.IPRange" msgid "Ranges must not overlap" msgstr "范围ä¸èƒ½é‡å " #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.EUI type." msgstr "接收到类型“%(type)sâ€å’Œå€¼â€œ%(value)sâ€ã€‚éœ€è¦ netaddr.EUI 类型。" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPAddress " "type." msgstr "接收到类型“%(type)sâ€å’Œå€¼â€œ%(value)sâ€ã€‚éœ€è¦ netaddr.IPAddress 类型。" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPNetwork " "type." msgstr "接收到类型“%(type)sâ€å’Œå€¼â€œ%(value)sâ€ã€‚éœ€è¦ netaddr.IPNetwork 类型。" #, python-format msgid "" "Release aware branch labels (%s) are deprecated. Please switch to expand@ " "and contract@ labels." msgstr "" "建议ä¸è¦ä½¿ç”¨å¯¹å‘è¡Œç‰ˆæ•æ„Ÿçš„分支标签 (%s)。请切æ¢åˆ° expand@ å’Œ contract@ 标签。" msgid "Remote metadata server experienced an internal server error." msgstr "è¿œç¨‹å…ƒæ•°æ®æœåС噍é‡åˆ°å†…部æœåŠ¡å™¨é”™è¯¯ã€‚" msgid "" "Repository does not contain HEAD files for contract and expand branches." msgstr "存储库未包å«ç”¨äºŽåˆåŒåˆ†æ”¯å’Œæ‰©å±•分支的 HEAD 文件。" msgid "" "Representing the resource type whose load is being reported by the agent. " "This can be \"networks\", \"subnets\" or \"ports\". When specified (Default " "is networks), the server will extract particular load sent as part of its " "agent configuration object from the agent report state, which is the number " "of resources being consumed, at every report_interval.dhcp_load_type can be " "used in combination with network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler When the network_scheduler_driver is " "WeightScheduler, dhcp_load_type can be configured to represent the choice " "for the resource being balanced. Example: dhcp_load_type=networks" msgstr "" "表示其负载è¦ç”±ä»£ç†æŠ¥å‘Šçš„资æºç±»åž‹ã€‚è¿™å¯ä»¥æ˜¯â€œç½‘络â€ã€â€œå­ç½‘â€æˆ–“端å£â€ã€‚如果已指定" "(缺çœå€¼ä¸ºâ€œç½‘络â€ï¼‰ï¼Œé‚£ä¹ˆæœåŠ¡å™¨å°†æ ¹æ®ä»£ç†æŠ¥å‘ŠçŠ¶æ€æŠ½å–特定负载(作为其代ç†é…ç½®" "对象的一部分å‘é€ï¼‰ï¼Œè¿™æ˜¯åœ¨æ¯ä¸ª report_interval è¦æ¶ˆè€—çš„èµ„æºæ•°ã€‚" "dhcp_load_type å¯ä¸Ž network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler 一起使用。当 network_scheduler_driver " "为 WeightScheduler 时,dhcp_load_type å¯é…置为表示针对è¦å‡è¡¡çš„资æºçš„选择。示" "例:dhcp_load_type=networks" msgid "Request Failed: internal server error while processing your request." msgstr "请求失败:在处ç†è¯·æ±‚时,å‘生内部æœåŠ¡å™¨é”™è¯¯ã€‚" msgid "" "Reset flow table on start. Setting this to True will cause brief traffic " "interruption." msgstr "在å¯åŠ¨æ—¶é‡ç½®æµè¡¨ã€‚将此项设置为 True 将导致短暂的通信中断。" #, python-format msgid "Resource %(resource)s %(resource_id)s could not be found." msgstr "找ä¸åˆ°èµ„æº %(resource)s %(resource_id)s。" #, python-format msgid "Resource %(resource_id)s of type %(resource_type)s not found" msgstr "找ä¸åˆ°ç±»åž‹ä¸º %(resource_type)s çš„èµ„æº %(resource_id)s" #, python-format msgid "" "Resource '%(resource_id)s' is already associated with provider " "'%(provider)s' for service type '%(service_type)s'" msgstr "" "对于æœåŠ¡ç±»åž‹â€œ%(service_type)sâ€ï¼Œèµ„æºâ€œ%(resource_id)sâ€å·²ç»ä¸Žæä¾›ç¨‹" "åºâ€œ%(provider)sâ€å…³è”" msgid "Resource body required" msgstr "需è¦èµ„æºä¸»ä½“" msgid "Resource not found." msgstr "找ä¸åˆ°èµ„æºã€‚" msgid "Resources required" msgstr "需è¦èµ„æº" msgid "" "Root helper application. Use 'sudo neutron-rootwrap /etc/neutron/rootwrap." "conf' to use the real root filter facility. Change to 'sudo' to skip the " "filtering and just run the command directly." msgstr "" "root helper 应用程åºã€‚使用“sudo neutron-rootwrap /etc/neutron/rootwrap." "confâ€ä»¥ä½¿ç”¨çœŸå®žæ ¹è¿‡æ»¤å·¥å…·ã€‚更改为“sudoâ€å°†è·³è¿‡è¿‡æ»¤å¹¶ä¸”仅直接è¿è¡Œè¯¥å‘½ä»¤ã€‚" msgid "Root permissions are required to drop privileges." msgstr "删除特æƒéœ€è¦ root ç”¨æˆ·è®¸å¯æƒã€‚" #, python-format msgid "Router '%(router_id)s' is not compatible with this agent." msgstr "路由器“%(router_id)sâ€ä¸Žæ­¤ä»£ç†ç¨‹åºä¸å…¼å®¹ã€‚" #, python-format msgid "Router already has a port on subnet %s" msgstr "路由器已在å­ç½‘ %s 上具有端å£" msgid "Router port must have at least one fixed IP" msgstr "路由器端å£å¿…须具有至少一个固定 IP" #, python-format msgid "Running %(cmd)s (%(desc)s) for %(project)s ..." msgstr "正在对 %(project)s è¿è¡Œ %(cmd)s (%(desc)s)..." #, python-format msgid "Running %(cmd)s for %(project)s ..." msgstr "正在对 %(project)s è¿è¡Œ %(cmd)s..." msgid "" "Seconds between nodes reporting state to server; should be less than " "agent_down_time, best if it is half or less than agent_down_time." msgstr "" "èŠ‚ç‚¹å‘æœåŠ¡å™¨æŠ¥å‘ŠçŠ¶æ€çš„间隔秒数;应该å°äºŽ agent_down_time,最好å°äºŽ " "agent_down_time 或是它的一åŠã€‚" msgid "" "Seconds to regard the agent is down; should be at least twice " "report_interval, to be sure the agent is down for good." msgstr "" "认为代ç†å·²å…³é—­çš„秒数;应该至少为 report_interval 的两å€ï¼Œä»¥ç¡®ä¿ä»£ç†å·²æ­£å¸¸å…³" "闭。" #, python-format msgid "Security Group %(id)s %(reason)s." msgstr "安全组 %(id)s %(reason)s。" #, python-format msgid "Security Group Rule %(id)s %(reason)s." msgstr "安全组规则 %(id)s %(reason)s。" #, python-format msgid "Security group %(id)s does not exist" msgstr "安全组 %(id)s ä¸å­˜åœ¨" #, python-format msgid "Security group rule %(id)s does not exist" msgstr "安全组规则 %(id)s ä¸å­˜åœ¨" #, python-format msgid "Security group rule already exists. Rule id is %(rule_id)s." msgstr "安全组规则已存在,规则标识为 %(rule_id)s。" #, python-format msgid "" "Security group rule for ethertype '%(ethertype)s' not supported. Allowed " "values are %(values)s." msgstr "" "ethertype “%(ethertype)s†的安全组规则ä¸å—支æŒã€‚å…许的值为 %(values)s。" #, python-format msgid "" "Security group rule protocol %(protocol)s not supported. Only protocol " "values %(values)s and integer representations [0 to 255] are supported." msgstr "" "安全组规则åè®® %(protocol)s ä¸å—支æŒã€‚åªæœ‰å议值 %(values)s 和整数表示 [0 到 " "255] å—æ”¯æŒã€‚" msgid "Segments and provider values cannot both be set." msgstr "æ— æ³•åŒæ—¶è®¾ç½®æ®µå’Œæä¾›ç¨‹åºå€¼ã€‚" msgid "Selects the Agent Type reported" msgstr "选择所报告的代ç†ç±»åž‹" msgid "" "Send notification to nova when port data (fixed_ips/floatingip) changes so " "nova can update its cache." msgstr "" "å½“ç«¯å£æ•°æ®ï¼ˆå›ºå®š IP/floatingip)更改时,将通知å‘é€è‡³ nova,以便 nova 坿›´æ–°å…¶" "高速缓存。" msgid "Send notification to nova when port status changes" msgstr "当端å£çŠ¶æ€æ›´æ”¹æ—¶ï¼Œå°†é€šçŸ¥å‘é€è‡³ nova" #, python-format msgid "" "Service provider '%(provider)s' could not be found for service type " "%(service_type)s" msgstr "对于以下æœåŠ¡ç±»åž‹ï¼Œæ‰¾ä¸åˆ°æœåŠ¡æä¾›ç¨‹åºâ€œ%(provider)sâ€ï¼š%(service_type)s" msgid "Service to handle DHCPv6 Prefix delegation." msgstr "用æ¥å¤„ç† DHCPv6 å‰ç¼€æŽˆæƒçš„æœåŠ¡ã€‚" #, python-format msgid "Service type %(service_type)s does not have a default service provider" msgstr "æœåŠ¡ç±»åž‹ %(service_type)s æ²¡æœ‰ç¼ºçœæœåŠ¡æä¾›ç¨‹åº" msgid "" "Set new timeout in seconds for new rpc calls after agent receives SIGTERM. " "If value is set to 0, rpc timeout won't be changed" msgstr "" "åœ¨ä»£ç†æŽ¥æ”¶åˆ° SIGTERM 之åŽï¼Œä¸ºæ–°çš„ RPC 调用设置新超时(以秒计)。如果值设置为 " "0,那么 RPC è¶…æ—¶å°†ä¸æ›´æ”¹" msgid "" "Set or un-set the don't fragment (DF) bit on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "在承载 GRE/VXLAN éš§é“的出局 IP åŒ…ä¸Šè®¾ç½®æˆ–å–æ¶ˆè®¾ç½®ä¸åˆ†æ®µ (DF) ä½ã€‚" msgid "" "Set or un-set the tunnel header checksum on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "对承载 GRE/VXLAN éš§é“的出局 IP åŒ…è®¾ç½®æˆ–å–æ¶ˆè®¾ç½®éš§é“头校验和。" msgid "Shared address scope can't be unshared" msgstr "æ— æ³•å–æ¶ˆå…±äº«å·²å…±äº«çš„地å€èŒƒå›´" msgid "String prefix used to match IPset names." msgstr "用æ¥åŒ¹é… IPset å称的字符串å‰ç¼€ã€‚" #, python-format msgid "Sub-project %s not installed." msgstr "未安装å­é¡¹ç›® %s。" msgid "Subnet for router interface must have a gateway IP" msgstr "路由器接å£çš„å­ç½‘必须具有网关 IP" #, python-format msgid "Subnet pool %(subnetpool_id)s could not be found." msgstr "找ä¸åˆ°å­ç½‘åè®® %(subnetpool_id)s。" msgid "Subnet pool has existing allocations" msgstr "å­ç½‘池具有现有分é…" msgid "Subnet used for the l3 HA admin network." msgstr "用于 l3 HA 管ç†ç½‘络的å­ç½‘。" msgid "" "Subnets hosted on the same network must be allocated from the same subnet " "pool." msgstr "åŒä¸€ç½‘络上的å­ç½‘必须分é…自åŒä¸€å­ç½‘池。" msgid "" "System-wide flag to determine the type of router that tenants can create. " "Only admin can override." msgstr "系统范围标记,用于确定租户å¯åˆ›å»ºçš„路由器类型。仅管ç†å‘˜å¯ä»¥è¦†ç›–。" msgid "TCP Port used by Neutron metadata namespace proxy." msgstr "TCP 端å£å·²ç”± Neutron 元数æ®å称空间代ç†ä½¿ç”¨ã€‚" msgid "TCP Port used by Nova metadata server." msgstr "Nova å…ƒæ•°æ®æœåŠ¡å™¨ä½¿ç”¨çš„ TCP 端å£ã€‚" msgid "TTL for vxlan interface protocol packets." msgstr "用于 vxlan 接å£å议包的 TTL。" #, python-format msgid "Tag %(tag)s could not be found." msgstr "找ä¸åˆ°æ ‡è®° %(tag)s。" #, python-format msgid "Tenant %(tenant_id)s not allowed to create %(resource)s on this network" msgstr "ä¸å…许租户 %(tenant_id)s 在此网络上创建 %(resource)s" msgid "Tenant id for connecting to designate in admin context" msgstr "管ç†å‘˜ä¸Šä¸‹æ–‡ä¸­è¦æŒ‡å®šçš„连接租户标识" msgid "Tenant name for connecting to designate in admin context" msgstr "管ç†å‘˜ä¸Šä¸‹æ–‡ä¸­è¦æŒ‡å®šçš„连接租户å" msgid "Tenant network creation is not enabled." msgstr "未å¯ç”¨ç§Ÿæˆ·ç½‘络创建。" msgid "Tenant-id was missing from quota request." msgstr "é…é¢è¯·æ±‚中缺少 tenant-id。" msgid "" "The 'gateway_external_network_id' option must be configured for this agent " "as Neutron has more than one external network." msgstr "" "必须为此代ç†é…置“gateway_external_network_idâ€é€‰é¡¹ï¼Œå› ä¸º Neutron 具有多个外部" "网络。" msgid "" "The DHCP agent will resync its state with Neutron to recover from any " "transient notification or RPC errors. The interval is number of seconds " "between attempts." msgstr "" "DHCP 代ç†ç¨‹åºå°†å…¶çжæ€é‡æ–°åŒæ­¥è‡³ Neutron,以从任何瞬时通知或 RPC 错误æ¢å¤ã€‚æ—¶" "间间隔为两次å°è¯•之间的秒数。" msgid "" "The DHCP server can assist with providing metadata support on isolated " "networks. Setting this value to True will cause the DHCP server to append " "specific host routes to the DHCP request. The metadata service will only be " "activated when the subnet does not contain any router port. The guest " "instance must be configured to request host routes via DHCP (Option 121). " "This option doesn't have any effect when force_metadata is set to True." msgstr "" "DHCP æœåС噍å¯å¸®åŠ©åœ¨éš”ç¦»ç½‘ç»œä¸Šæä¾›å…ƒæ•°æ®æ”¯æŒã€‚将此值设置为 True 将导致 DHCP æœ" "务器将特定主机路由追加至 DHCP 请求。仅当å­ç½‘未包å«ä»»ä½•è·¯ç”±å™¨ç«¯å£æ—¶ï¼Œæ‰ä¼šæ¿€æ´»" "å…ƒæ•°æ®æœåŠ¡ã€‚è®¿å®¢å®žä¾‹å¿…é¡»é…置为通过 DHCP 请求主机路由(选项 121)。如果 " "force_metadata 设置为 True,那么此选项没有任何效果。" msgid "The UDP port to use for VXLAN tunnels." msgstr "UDP端å£ç”¨äºŽVXLANéš§é“" #, python-format msgid "" "The address allocation request could not be satisfied because: %(reason)s" msgstr "未能满足地å€åˆ†é…请求,原因:%(reason)s" msgid "The advertisement interval in seconds" msgstr "通告间隔(秒)" #, python-format msgid "The allocation pool %(pool)s is not valid." msgstr "åˆ†é…æ±  %(pool)s 无效。" #, python-format msgid "" "The allocation pool %(pool)s spans beyond the subnet cidr %(subnet_cidr)s." msgstr "åˆ†é…æ±  %(pool)s 范围超出å­ç½‘ cidr %(subnet_cidr)s。" msgid "" "The base MAC address Neutron will use for VIFs. The first 3 octets will " "remain unchanged. If the 4th octet is not 00, it will also be used. The " "others will be randomly generated." msgstr "" "基本 MAC åœ°å€ Neutron 将用于 VIFã€‚å‰ 3 个八ä½å…ƒå°†ä¿æŒä¸å˜ã€‚如果第 4 个八ä½å…ƒ" "å¹¶éž 00,那么也将使用该八ä½å…ƒã€‚å°†éšæœºç”Ÿæˆå…¶ä»–å…«ä½å…ƒã€‚" msgid "" "The base mac address used for unique DVR instances by Neutron. The first 3 " "octets will remain unchanged. If the 4th octet is not 00, it will also be " "used. The others will be randomly generated. The 'dvr_base_mac' *must* be " "different from 'base_mac' to avoid mixing them up with MAC's allocated for " "tenant ports. A 4 octet example would be dvr_base_mac = fa:16:3f:4f:00:00. " "The default is 3 octet" msgstr "" "ç”± Neutron 用于唯一 DVR 实例的基本 MAC 地å€ã€‚å‰ä¸‰ä¸ªå…«ä½å…ƒå°†ä¿æŒä¸å˜ã€‚如果第四" "个八ä½å…ƒä¸ä¸º 00,那么也将使用该八ä½å…ƒã€‚å°†éšæœºç”Ÿæˆå…¶ä»–å…«ä½å…ƒã€‚“dvr_base_macâ€å¿…" "é¡»ä¸åŒäºŽâ€œbase_macâ€ï¼Œä»¥é¿å…将它们与为租户端å£åˆ†é…çš„ MAC æ··åˆä½¿ç”¨ã€‚以下是一个具" "有 4 个八ä½å…ƒçš„示例:dvr_base_mac = fa:16:3f:4f:00:00。缺çœå€¼ä¸º 3 个八ä½å…ƒ" msgid "The core plugin Neutron will use" msgstr "Neutron 将使用的核心æ’ä»¶" msgid "The driver used to manage the DHCP server." msgstr "ç”¨äºŽç®¡ç† DHCP æœåŠ¡å™¨çš„é©±åŠ¨ç¨‹åºã€‚" msgid "The driver used to manage the virtual interface." msgstr "用于管ç†è™šæ‹ŸæŽ¥å£çš„驱动程åºã€‚" msgid "" "The email address to be used when creating PTR zones. If not specified, the " "email address will be admin@" msgstr "" "创建 PTR 区域时è¦ä½¿ç”¨çš„电å­é‚®ä»¶åœ°å€ã€‚如果未指定,那么电å­é‚®ä»¶åœ°å€å°†ä¸º " "admin@" #, python-format msgid "" "The following device_id %(device_id)s is not owned by your tenant or matches " "another tenants router." msgstr "以下 device_id %(device_id)s ä¸å±žäºŽæ‚¨çš„租户或与å¦ä¸€ç§Ÿæˆ·è·¯ç”±å™¨ 匹é…。" msgid "The interface for interacting with the OVSDB" msgstr "用于与 OVSDB 进行交互的接å£" msgid "" "The maximum number of items returned in a single response, value was " "'infinite' or negative integer means no limit" msgstr "在å•个å“应中返回的最大项数,值为“无é™â€æˆ–负整数表示无é™åˆ¶" #, python-format msgid "" "The network %(network_id)s has been already hosted by the DHCP Agent " "%(agent_id)s." msgstr "网络 %(network_id)s 已由 DHCP ä»£ç† %(agent_id)s 主管。" #, python-format msgid "" "The network %(network_id)s is not hosted by the DHCP agent %(agent_id)s." msgstr "网络 %(network_id)s 未由 DHCP ä»£ç† %(agent_id)s 主管。" msgid "" "The network type to use when creating the HA network for an HA router. By " "default or if empty, the first 'tenant_network_types' is used. This is " "helpful when the VRRP traffic should use a specific network which is not the " "default one." msgstr "" "为 HA 路由器创建 HA 网络时è¦ä½¿ç”¨çš„ç½‘ç»œç±»åž‹ã€‚ç¼ºçœæƒ…况下,或者如果网络类型为" "空,那么将使用第一个“tenant_network_typesâ€ã€‚这在 VRRP æµé‡åº”使用特定网络(该" "ç½‘ç»œä¸æ˜¯ç¼ºçœç½‘络)时很有帮助。" msgid "" "The number of seconds the agent will wait between polling for local device " "changes." msgstr "在轮询本地设备更改之间,代ç†å°†ç­‰å¾…的秒数。" msgid "" "The number of seconds to wait before respawning the ovsdb monitor after " "losing communication with it." msgstr "在与 ovsdb 监视器失去通信è”系之åŽé‡æ–°è¡ç”Ÿè¯¥ç›‘视器之å‰è¦ç­‰å¾…的秒数。" msgid "The number of sort_keys and sort_dirs must be same" msgstr "sort_keys 的数字与 sort_dirs 的数字必须相åŒ" msgid "" "The path for API extensions. Note that this can be a colon-separated list of " "paths. For example: api_extensions_path = extensions:/path/to/more/exts:/" "even/more/exts. The __path__ of neutron.extensions is appended to this, so " "if your extensions are in there you don't need to specify them here." msgstr "" "API 扩展的路径。请注æ„,它å¯èƒ½æ˜¯è·¯å¾„的冒å·åˆ†éš”列表。例如:" "api_extensions_path = extensions:/path/to/more/exts:/even/more/exts。neutron." "extensions çš„ __path__ 将追加至此项之åŽï¼Œæ‰€ä»¥ï¼Œå¦‚果扩展ä½äºŽè¯¥å¤„,那么ä¸éœ€è¦åœ¨" "此处指定它们。" msgid "The physical network name with which the HA network can be created." msgstr "å¯ä»¥ç”¨æ¥åˆ›å»º HA 网络的物ç†ç½‘络å称。" #, python-format msgid "The port '%s' was deleted" msgstr "已删除端å£â€œ%sâ€" msgid "The port to bind to" msgstr "端å£è¦ç»‘定至" #, python-format msgid "The requested content type %s is invalid." msgstr "请求的内容类型%séžæ³•。" msgid "The resource could not be found." msgstr "找ä¸åˆ°è¯¥èµ„æºã€‚" #, python-format msgid "" "The router %(router_id)s has been already hosted by the L3 Agent " "%(agent_id)s." msgstr "路由器 %(router_id)s 已由 L3 ä»£ç† %(agent_id)s 主管。" msgid "" "The server has either erred or is incapable of performing the requested " "operation." msgstr "æœåŠ¡å™¨å·²å‡ºé”™æˆ–æ— æ³•æ‰§è¡Œæ‰€è¯·æ±‚æ“作。" msgid "The service plugins Neutron will use" msgstr "Neutron 将使用的æœåŠ¡æ’ä»¶" #, python-format msgid "The subnet request could not be satisfied because: %(reason)s" msgstr "未能满足å­ç½‘请求,原因:%(reason)s" #, python-format msgid "The subproject to execute the command against. Can be one of: '%s'." msgstr "è¦å¯¹å…¶æ‰§è¡Œå‘½ä»¤çš„å­é¡¹ç›®ã€‚å¯ä»¥æ˜¯â€œ%sâ€çš„其中一项。" msgid "The type of authentication to use" msgstr "è¦ä½¿ç”¨çš„认è¯çš„类型" msgid "" "There are routers attached to this network that depend on this policy for " "access." msgstr "æ ¹æ®æ­¤ç­–略,有一些路由器附加至此网络以用于访问。" msgid "" "Timeout in seconds to wait for a single OpenFlow request. Used only for " "'native' driver." msgstr "等待å•个 OpenFlow 请求时的超时(秒)。仅用于“本机â€é©±åŠ¨ç¨‹åºã€‚" msgid "" "Timeout in seconds to wait for the local switch connecting the controller. " "Used only for 'native' driver." msgstr "ç­‰å¾…ç”¨äºŽè¿žæŽ¥æŽ§åˆ¶å™¨çš„æœ¬åœ°äº¤æ¢æœºæ—¶çš„超时(秒)。仅用于“本机â€é©±åŠ¨ç¨‹åºã€‚" msgid "" "Too long prefix provided. New name would exceed given length for an " "interface name." msgstr "æä¾›çš„å‰ç¼€å¤ªé•¿ã€‚æ–°å称将超出接å£å称的给定长度。" msgid "" "True to delete all ports on all the OpenvSwitch bridges. False to delete " "ports created by Neutron on integration and external network bridges." msgstr "" "True 表示删除所有 OpenvSwitch 网桥上的所有端å£ã€‚False 表示删除集æˆå’Œå¤–部网络" "网桥上由 Neutron 创建的端å£ã€‚" msgid "Tunnel IP value needed by the ML2 plugin" msgstr "ML2 æ’件需è¦éš§é“ IP 值" msgid "Tunnel bridge to use." msgstr "è¦ä½¿ç”¨çš„éš§é“网桥。" msgid "" "Type of the nova endpoint to use. This endpoint will be looked up in the " "keystone catalog and should be one of public, internal or admin." msgstr "" "è¦ä½¿ç”¨çš„ nova 端点的类型。系统将在 keystone 目录中查找此端点,值应该为 " "publicã€internal 或 admin 的其中之一。" msgid "URL for connecting to designate" msgstr "è¦æŒ‡å®šçš„连接 URL" msgid "URL to database" msgstr "æŒ‡å‘æ•°æ®åº“çš„ URL" #, python-format msgid "Unable to access %s" msgstr "无法访问 %s" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, maximum allowed " "prefix is %(max_prefixlen)s." msgstr "" "无法分é…å‰ç¼€é•¿åº¦ä¸º %(prefixlen)s çš„å­ç½‘,å…许的最大å‰ç¼€é•¿åº¦ä¸º " "%(max_prefixlen)s" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, minimum allowed " "prefix is %(min_prefixlen)s." msgstr "" "无法分é…å‰ç¼€é•¿åº¦ä¸º %(prefixlen)s çš„å­ç½‘,å…许的最低å‰ç¼€é•¿åº¦ä¸º " "%(min_prefixlen)s。" #, python-format msgid "Unable to calculate %(address_type)s address because of:%(reason)s" msgstr "无法计算 %(address_type)s 地å€ï¼ŒåŽŸå› ï¼š%(reason)s" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of DNS " "nameservers exceeds the limit %(quota)s." msgstr "对于 %(subnet_id)sï¼Œæ— æ³•å®Œæˆæ“作。DNS åç§°æœåŠ¡å™¨æ•°è¶…è¿‡é™åˆ¶ %(quota)s。" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of host routes " "exceeds the limit %(quota)s." msgstr "对于 %(subnet_id)sï¼Œæ— æ³•å®Œæˆæ“作。主机路由数超过é™åˆ¶ %(quota)s。" #, python-format msgid "Unable to convert value in %s" msgstr "æ— æ³•è½¬æ¢ %s 中的值" msgid "Unable to create the Agent Gateway Port" msgstr "无法创建代ç†ç½‘关端å£" msgid "Unable to create the SNAT Interface Port" msgstr "无法创建 SNAT 接å£ç«¯å£" #, python-format msgid "" "Unable to create the flat network. Physical network %(physical_network)s is " "in use." msgstr "无法创建该平é¢ç½‘络。物ç†ç½‘络 %(physical_network)s 在使用中。" msgid "" "Unable to create the network. No available network found in maximum allowed " "attempts." msgstr "无法创建网络。未在最大å…许å°è¯•次数中å‘现任何å¯ç”¨ç½‘络。" #, python-format msgid "Unable to delete subnet pool: %(reason)s." msgstr "无法删除å­ç½‘池:%(reason)s。" #, python-format msgid "Unable to determine mac address for %s" msgstr "无法为 %s 确定网å¡åœ°å€" #, python-format msgid "Unable to find '%s' in request body" msgstr "在请求主体中找ä¸åˆ°â€œ%sâ€" #, python-format msgid "Unable to find IP address %(ip_address)s on subnet %(subnet_id)s" msgstr "在å­ç½‘ %(subnet_id)s 上找ä¸åˆ° IP åœ°å€ %(ip_address)s" #, python-format msgid "Unable to find resource name in %s" msgstr "在%s中找ä¸åˆ°æºçš„åç§°" #, python-format msgid "Unable to generate unique mac on network %(net_id)s." msgstr "无法在网络 %(net_id)s 上生æˆå”¯ä¸€ MAC。" #, python-format msgid "" "Unable to identify a target field from:%s. Match should be in the form " "%%()s" msgstr "无法从:%s中匹é…目标域. 匹é…应该使用以下形å¼%%(域å)s" msgid "Unable to provide external connectivity" msgstr "无法æä¾›å¤–部连接" msgid "Unable to provide tenant private network" msgstr "无法æä¾›ç§Ÿæˆ·ä¸“用网络" #, python-format msgid "" "Unable to reconfigure sharing settings for network %(network)s. Multiple " "tenants are using it." msgstr "æ— æ³•é‡æ–°é…置网络 %(network)s 的共享设置。多个租户正在使用该网络。" #, python-format msgid "" "Unable to verify match:%(match)s as the parent resource: %(res)s was not " "found" msgstr "无法验è¯è¯¥åŒ¹é…%(match)s为父资æº:未找到%(res)s" #, python-format msgid "Unexpected label for script %(script_name)s: %(labels)s" msgstr "脚本 %(script_name)s çš„æ„外标签:%(labels)s" #, python-format msgid "Unexpected number of alembic branch points: %(branchpoints)s" msgstr "净化器分支点的数目异常:%(branchpoints)s" #, python-format msgid "Unexpected response code: %s" msgstr "æ„外å“应代ç ï¼š%s" #, python-format msgid "Unexpected response: %s" msgstr "æ„外å“应:%s" #, python-format msgid "Unit name '%(unit)s' is not valid." msgstr "å•å…ƒå称“%(unit)sâ€æ— æ•ˆã€‚" #, python-format msgid "Unknown address type %(address_type)s" msgstr "未知地å€ç±»åž‹ %(address_type)s" #, python-format msgid "Unknown attribute '%s'." msgstr "属性“%sâ€æœªçŸ¥ã€‚" #, python-format msgid "Unknown chain: %r" msgstr "链未知:%r" #, python-format msgid "Unknown network type %(network_type)s." msgstr "未知网络类型 %(network_type)s。" #, python-format msgid "Unknown quota resources %(unknown)s." msgstr "é…é¢èµ„æº %(unknown)s 未知。" msgid "Unmapped error" msgstr "已喿¶ˆæ˜ å°„错误" msgid "Unrecognized action" msgstr "无法识别动作" #, python-format msgid "Unrecognized attribute(s) '%s'" msgstr "无法识别属性“%sâ€" msgid "Unrecognized field" msgstr "无法识别字段" msgid "Unsupported Content-Type" msgstr "Content-Type ä¸å—支æŒ" #, python-format msgid "Unsupported network type %(net_type)s." msgstr "网络类型 %(net_type)s ä¸å—支æŒã€‚" #, python-format msgid "Unsupported port state: %(port_state)s." msgstr "ä¸å—支æŒçš„端å£çжæ€ï¼š%(port_state)s。" msgid "Unsupported request type" msgstr "未支æŒè¯·æ±‚类型" msgid "Updating default security group not allowed." msgstr "正在更新的默认安全组内容ä¸åˆæ³•" msgid "" "Use ML2 l2population mechanism driver to learn remote MAC and IPs and " "improve tunnel scalability." msgstr "" "请使用 ML2 l2population 机制驱动程åºä»¥äº†è§£è¿œç¨‹ MAC å’Œ IP å¹¶æé«˜éš§é“å¯ä¼¸ç¼©æ€§ã€‚" msgid "Use broadcast in DHCP replies." msgstr "在 DHCP 应答中使用广播。" msgid "Use either --delta or relative revision, not both" msgstr "请使用 --delta 或者相关修订版,但是ä¸èƒ½åŒæ—¶æŒ‡å®šè¿™ä¸¤è€…" msgid "" "Use ipset to speed-up the iptables based security groups. Enabling ipset " "support requires that ipset is installed on L2 agent node." msgstr "" "使用 ipset 以加速基于安全组的 iptable。å¯ç”¨ ipset 支æŒè¦æ±‚该 ipset 安装在 L2 " "代ç†ç¨‹åºèŠ‚ç‚¹ä¸Šã€‚" msgid "" "Use the root helper when listing the namespaces on a system. This may not be " "required depending on the security configuration. If the root helper is not " "required, set this to False for a performance improvement." msgstr "" "列示系统上的å称空间时使用 root helper。根æ®å®‰å…¨é…置,这å¯èƒ½ä¸æ˜¯å¿…需的。如果 " "root helper 䏿˜¯å¿…需的,请将其设置为 False 以改进性能。" msgid "" "Use veths instead of patch ports to interconnect the integration bridge to " "physical networks. Support kernel without Open vSwitch patch port support so " "long as it is set to True." msgstr "" "使用 veth è€Œä¸æ˜¯æŽ¥çº¿ç«¯å£ä»¥ä½¿é›†æˆç½‘桥与物ç†ç½‘络互连。设置为 True 时支æŒä¸å…·å¤‡ " "Open vSwitch æŽ¥çº¿ç«¯å£æ”¯æŒçš„内核。" msgid "" "User (uid or name) running metadata proxy after its initialization (if " "empty: agent effective user)." msgstr "" "在元数æ®ä»£ç†çš„åˆå§‹åŒ–之åŽï¼Œè¿è¡Œè¯¥ä»£ç†çš„用户(uid 或å称),(如果此用户为空," "é‚£ä¹ˆè¿™æ˜¯ä»£ç†æœ‰æ•ˆç”¨æˆ·ï¼‰ã€‚" msgid "User (uid or name) running this process after its initialization" msgstr "在此进程的åˆå§‹åŒ–之åŽï¼Œè¿è¡Œæ­¤è¿›ç¨‹çš„用户(uid 或å称)" msgid "Username for connecting to designate in admin context" msgstr "管ç†å‘˜ä¸Šä¸‹æ–‡ä¸­è¦æŒ‡å®šçš„连接用户å" msgid "VRRP authentication password" msgstr "VRRP认è¯å¯†ç " msgid "VRRP authentication type" msgstr "VRRP认è¯ç±»åž‹" msgid "VXLAN network unsupported." msgstr "VXLAN 网络ä¸å—支æŒã€‚" msgid "" "Value of host kernel tick rate (hz) for calculating minimum burst value in " "bandwidth limit rules for a port with QoS. See kernel configuration file for " "HZ value and tc-tbf manual for more information." msgstr "" "主机内核节æ‹çŽ‡çš„å€¼ (hz),用于计算带有 QoS 的端å£çš„带宽é™åˆ¶è§„则中的最å°è„‰å†²" "值。请å‚阅内核é…ç½®æ–‡ä»¶ä»¥èŽ·å– HZ 值,并å‚阅 tc-tbf æ‰‹å†Œä»¥èŽ·å–æ›´å¤šä¿¡æ¯ã€‚" msgid "" "Value of latency (ms) for calculating size of queue for a port with QoS. See " "tc-tbf manual for more information." msgstr "" "延迟值 (ms),用于计算带有 QoS 的端å£çš„队列的大å°ã€‚请å‚阅 tc-tbf 手册以了解更" "多信æ¯ã€‚" msgid "" "When external_network_bridge is set, each L3 agent can be associated with no " "more than one external network. This value should be set to the UUID of that " "external network. To allow L3 agent support multiple external networks, both " "the external_network_bridge and gateway_external_network_id must be left " "empty." msgstr "" "如果设置了 external_network_bridge,那么æ¯ä¸ª L3 agent 最多å¯ä¸Žä¸€ä¸ªå¤–部网络相" "å…³è”。此值应设置为该外部网络的 UUID。为å…许 L3 代ç†ç¨‹åºæ”¯æŒå¤šä¸ªå¤–部网络," "external_network_bridge å’Œ gateway_external_network_id 必须留空。" msgid "" "When proxying metadata requests, Neutron signs the Instance-ID header with a " "shared secret to prevent spoofing. You may select any string for a secret, " "but it must match here and in the configuration used by the Nova Metadata " "Server. NOTE: Nova uses the same config key, but in [neutron] section." msgstr "" "代ç†å…ƒæ•°æ®è¯·æ±‚时,Neutron 会使用共享密钥签署 Instance-ID 头以é¿å…电å­è¯ˆéª—。å¯" "选择任何字符串作为密钥,但此处的密钥必须与 Nova Metadata Server 使用的é…置中" "的密钥相匹é…。注æ„:Nova 使用åŒä¸€é…置密钥,但在 [neutron] 节中。" msgid "" "Where to store Neutron state files. This directory must be writable by the " "agent." msgstr "用于存储 Neutron çŠ¶æ€æ–‡ä»¶çš„ä½ç½®ã€‚此目录对于代ç†å¿…须为å¯å†™ã€‚" msgid "" "With IPv6, the network used for the external gateway does not need to have " "an associated subnet, since the automatically assigned link-local address " "(LLA) can be used. However, an IPv6 gateway address is needed for use as the " "next-hop for the default route. If no IPv6 gateway address is configured " "here, (and only then) the neutron router will be configured to get its " "default route from router advertisements (RAs) from the upstream router; in " "which case the upstream router must also be configured to send these RAs. " "The ipv6_gateway, when configured, should be the LLA of the interface on the " "upstream router. If a next-hop using a global unique address (GUA) is " "desired, it needs to be done via a subnet allocated to the network and not " "through this parameter. " msgstr "" "对于 IPv6,用于外部网关的网络ä¸éœ€è¦å…·å¤‡ç›¸å…³è”çš„å­ç½‘,因为å¯ä»¥ä½¿ç”¨è‡ªåŠ¨æŒ‡å®šçš„é“¾" "è·¯æœ¬åœ°åœ°å€ (LLA)ã€‚ä½†æ˜¯ï¼Œéœ€è¦ IPv6 网关地å€ç”¨ä½œç¼ºçœè·¯ç”±çš„下一个路由器。如果此" "处未é…ç½® IPv6 网关地å€ï¼Œé‚£ä¹ˆå°†é…ç½® Neutron 路由器,以从上游的路由器中获å–路由" "器广告(RA)中的缺çœè·¯ç”±ï¼›åœ¨è¿™ç§æƒ…况下,还必须é…置上游路由器以å‘é€è¿™äº› RA。é…" "置了 ipv6_gateway 时,ipv6_gateway 应为上游路由器上的接å£çš„ LLA。如果需è¦ä¸‹ä¸€" "ä¸ªä½¿ç”¨å…¨å±€å”¯ä¸€åœ°å€ (GUA) 的路由器,那么它需è¦é€šè¿‡åˆ†é…给该网络的å­ç½‘æ¥å®Œæˆï¼Œè€Œ" "䏿˜¯é€šè¿‡æ­¤å‚æ•°æ¥å®Œæˆã€‚" msgid "You must implement __call__" msgstr "必须实现 __call__" msgid "" "You must provide a config file for bridge - either --config-file or " "env[NEUTRON_TEST_CONFIG_FILE]" msgstr "必须为网桥æä¾›é…置文件 - --config-file 或env[NEUTRON_TEST_CONFIG_FILE]" msgid "You must provide a revision or relative delta" msgstr "å¿…é¡»æä¾›ä¿®è®¢æˆ–相对å˜åŒ–é‡" msgid "a subnetpool must be specified in the absence of a cidr" msgstr "缺少 CIDR 时必须指定å­ç½‘æ± " msgid "add_ha_port cannot be called inside of a transaction." msgstr "ä¸èƒ½åœ¨äº‹åŠ¡å†…éƒ¨è°ƒç”¨ add_ha_port。" msgid "allocation_pools allowed only for specific subnet requests." msgstr "ä»…å…许将 allocation_pools 用于特定å­ç½‘请求。" msgid "allocation_pools are not in the subnet" msgstr "allocation_pools ä¸åœ¨å­ç½‘内" msgid "allocation_pools use the wrong ip version" msgstr "allocation_pools 使用错误的 IP 版本" msgid "already a synthetic attribute" msgstr "已是综åˆå±žæ€§" msgid "binding:profile value too large" msgstr "binding:profile 值太大" #, python-format msgid "cannot perform %(event)s due to %(reason)s" msgstr "无法执行 %(event)s,因为 %(reason)s" msgid "cidr and prefixlen must not be supplied together" msgstr "ä¸å¾—åŒæ—¶æŒ‡å®š cidr å’Œ prefixlen" #, python-format msgid "dhcp_agents_per_network must be >= 1. '%s' is invalid." msgstr "dhcp_agents_per_network 必须是>= 1. '%s' 是ä¸åˆæ³•çš„ã€" msgid "dns_domain cannot be specified without a dns_name" msgstr "ä¸èƒ½åœ¨æ²¡æœ‰ dns_name 的情况下指定 dns_domain" msgid "dns_name cannot be specified without a dns_domain" msgstr "ä¸èƒ½åœ¨æ²¡æœ‰ dns_domain 的情况下指定 dns_name" msgid "fixed_ip_address cannot be specified without a port_id" msgstr "在没有 port_id 的情况下,无法指定 fixed_ip_address" #, python-format msgid "has device owner %s" msgstr "具有设备所有者 %s" msgid "in use" msgstr "正在使用" #, python-format msgid "ip command failed on device %(dev_name)s: %(reason)s" msgstr "对设备 %(dev_name)s 执行 IP 命令失败:%(reason)s" #, python-format msgid "ip command failed: %(reason)s" msgstr "ip 命令失败:%(reason)s" #, python-format msgid "ip link capability %(capability)s is not supported" msgstr "IP 链接功能 %(capability)s ä¸å—支æŒ" #, python-format msgid "ip link command is not supported: %(reason)s" msgstr "ip 链路命令未支æŒ: %(reason)s" msgid "ip_version must be specified in the absence of cidr and subnetpool_id" msgstr "在缺少 cidr å’Œ subnetpool_id 的情况下,必须指定 ip_version" msgid "ipv6_address_mode is not valid when ip_version is 4" msgstr "ip_version 为 4 时,ipv6_address_mode 无效" msgid "ipv6_ra_mode is not valid when ip_version is 4" msgstr "ip_version 为 4 时,ipv6_ra_mode 无效" #, python-format msgid "" "ipv6_ra_mode set to '%(ra_mode)s' with ipv6_address_mode set to " "'%(addr_mode)s' is not valid. If both attributes are set, they must be the " "same value" msgstr "" "设置为“%(ra_mode)sâ€çš„ ipv6_ra_mode(在 ipv6_address_mode 设置" "为“%(addr_mode)sâ€çš„æƒ…况下)无效。如果设置了这两个属性,那么它们必须为åŒä¸€ä¸ªå€¼" msgid "mac address update" msgstr "MAC åœ°å€æ›´æ–°" msgid "must provide exactly 2 arguments - cidr and MAC" msgstr "å¿…é¡»æä¾›æ­£å¥½ 2 个自å˜é‡ï¼šcidr å’Œ MAC" msgid "network_type required" msgstr "éœ€è¦ network_type" #, python-format msgid "network_type value '%s' not supported" msgstr "䏿”¯æŒçš„网络类型值 '%s'" msgid "new subnet" msgstr "æ–°å­ç½‘" #, python-format msgid "physical_network '%s' unknown for flat provider network" msgstr "å¹³é¢ä¾›åº”商网络的物ç†ç½‘络 '%s'为未知状æ€" msgid "physical_network required for flat provider network" msgstr "å¹³é¢ä¾›åº”商网络需è¦çš„物ç†ç½‘络" #, python-format msgid "provider:physical_network specified for %s network" msgstr "æä¾›ç¨‹åºï¼šå·²ä¸º%s 网络指定 physical_network" msgid "respawn_interval must be >= 0 if provided." msgstr "respawn_interval å¿…é¡»ä¸å°äºŽ 0(如果已æä¾›æ­¤é¡¹ï¼‰ã€‚" #, python-format msgid "segmentation_id out of range (%(min)s through %(max)s)" msgstr "segmentation_id 超出范围,从(%(min)s 到 %(max)s)" msgid "segmentation_id requires physical_network for VLAN provider network" msgstr "segmentation_id éœ€è¦ VLAN æä¾›ç¨‹åºç½‘络的 physical_network" msgid "shared attribute switching to synthetic" msgstr "共享属性正切æ¢ä¸ºç»¼åˆå±žæ€§" #, python-format msgid "" "subnetpool %(subnetpool_id)s cannot be updated when associated with shared " "address scope %(address_scope_id)s" msgstr "" "当å­ç½‘æ±  %(subnetpool_id)s 与共享地å€èŒƒå›´ %(address_scope_id)s ç›¸å…³è”æ—¶ï¼Œå°†æ— " "法更新该å­ç½‘æ± " msgid "subnetpool_id and use_default_subnetpool cannot both be specified" msgstr "ä¸èƒ½åŒæ—¶æŒ‡å®š subnetpool_id å’Œ use_default_subnetpool" msgid "the nexthop is not connected with router" msgstr "下一中继段未与路由器连接" msgid "the nexthop is used by router" msgstr "路由器已使用下一中继段" neutron-12.1.1/neutron/locale/zh_TW/0000775000175000017500000000000013553660156017306 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/zh_TW/LC_MESSAGES/0000775000175000017500000000000013553660156021073 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/zh_TW/LC_MESSAGES/neutron.po0000664000175000017500000031232413553660047023131 0ustar zuulzuul00000000000000# Translations template for neutron. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the neutron project. # # Translators: # Andreas Jaeger , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: neutron VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-03-14 04:19+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-04-12 05:56+0000\n" "Last-Translator: Copied by Zanata \n" "Language: zh_TW\n" "Plural-Forms: nplurals=1; plural=0;\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 4.3.3\n" "Language-Team: Chinese (Taiwan)\n" #, python-format msgid "" "\n" "Command: %(cmd)s\n" "Exit code: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" msgstr "" "\n" "指令:%(cmd)s\n" "çµæŸç¢¼ï¼š%(code)s\n" "標準輸入:%(stdin)s\n" "標準輸出:%(stdout)s\n" "標準錯誤:%(stderr)s" #, python-format msgid "" "%(branch)s HEAD file does not match migration timeline head, expected: " "%(head)s" msgstr "%(branch)s HEAD 檔與移轉時間表表頭ä¸ç¬¦ï¼Œé æœŸç‚ºï¼š%(head)s" #, python-format msgid "%(id)s is not a valid %(type)s identifier" msgstr "%(id)s 䏿˜¯æœ‰æ•ˆçš„ %(type)s ID" #, python-format msgid "" "%(invalid_dirs)s is invalid value for sort_dirs, valid value is '%(asc)s' " "and '%(desc)s'" msgstr "" "å°æ–¼ sort_dir 來說,%(invalid_dirs)s 是無效值,有效值為 '%(asc)s' åŠ " "'%(desc)s'" #, python-format msgid "%(key)s prohibited for %(tunnel)s provider network" msgstr "%(tunnel)s æä¾›è€…ç¶²è·¯å·²ç¦æ­¢ %(key)s" #, python-format msgid "%(name)s '%(addr)s' does not match the ip_version '%(ip_version)s'" msgstr "%(name)s '%(addr)s' 與 ip_version '%(ip_version)s' ä¸ç¬¦" #, python-format msgid "%s cannot be called while in offline mode" msgstr "ç•¶ %s è™•æ–¼é›¢ç·šæ¨¡å¼æ™‚,無法å°å…¶é€²è¡Œå‘¼å«" #, python-format msgid "%s is invalid attribute for sort_keys" msgstr "å°æ–¼ sort_key 來說,%s 是無效的屬性" #, python-format msgid "%s is not a valid VLAN tag" msgstr "%s 䏿˜¯æœ‰æ•ˆçš„ VLAN 標籤" #, python-format msgid "%s must implement get_port_from_device or get_ports_from_devices." msgstr "%s 必須實作 get_port_from_device 或 get_ports_from_devices。" #, python-format msgid "%s prohibited for VLAN provider network" msgstr "VLAN æä¾›è€…ç¶²è·¯å·²ç¦æ­¢ %s" #, python-format msgid "%s prohibited for flat provider network" msgstr "å¹³é¢æä¾›è€…ç¶²è·¯å·²ç¦æ­¢ %s" #, python-format msgid "%s prohibited for local provider network" msgstr "本端æä¾›è€…ç¶²è·¯å·²ç¦æ­¢ %s" #, python-format msgid "'%s' is not a valid RBAC object type" msgstr "'%s' 䏿˜¯æœ‰æ•ˆçš„ RBAC 物件類型" #, python-format msgid "'%s' is not supported for filtering" msgstr "'%s' 䏿”¯æ´éŽæ¿¾" #, python-format msgid "'module' object has no attribute '%s'" msgstr "'module' 物件ä¸å«å±¬æ€§ '%s'" msgid "'port_max' is smaller than 'port_min'" msgstr "'port_max' å°æ–¼ 'port_min'" msgid "0 is not allowed as CIDR prefix length" msgstr "ä¸æŽ¥å— 0 作為 CIDR 字首長度" msgid "A cidr must be specified in the absence of a subnet pool" msgstr "如果未指定å­ç¶²è·¯å„²å­˜å€ï¼Œå‰‡å¿…須指定 cidr" msgid "" "A decimal value as Vendor's Registered Private Enterprise Number as required " "by RFC3315 DUID-EN." msgstr "å進ä½å€¼ï¼Œä¾ RFC3315 DUID-EN 的需è¦ï¼Œä½œç‚ºä¾›æ‡‰å•†çš„å·²ç™»éŒ„å°ˆç”¨ä¼æ¥­è™Ÿç¢¼ã€‚" #, python-format msgid "A default external network already exists: %(net_id)s." msgstr "é è¨­å¤–部網路已經存在:%(net_id)s。" msgid "" "A default subnetpool for this IP family has already been set. Only one " "default may exist per IP family" msgstr "已經設定了此 IP 系列的é è¨­å­ç¶²è·¯å„²å­˜å€ã€‚æ¯å€‹ IP 系列åªèƒ½å­˜åœ¨ä¸€å€‹é è¨­å€¼" msgid "A metering driver must be specified" msgstr "必須指定計é‡é©…動程å¼" msgid "API for retrieving service providers for Neutron advanced services" msgstr "æ­¤ API ç”¨æ–¼æ“·å– Neutron 進階æœå‹™çš„æœå‹™æä¾›è€…" msgid "Aborting periodic_sync_routers_task due to an error." msgstr "由於發生錯誤,正在中斷 periodic_sync_routers_task。" msgid "Access to this resource was denied." msgstr "æ‹’çµ•å­˜å–æ­¤è³‡æºã€‚" msgid "Action to be executed when a child process dies" msgstr "å­ç¨‹åºç•¶æŽ‰æ™‚è¦åŸ·è¡Œçš„動作" msgid "" "Add comments to iptables rules. Set to false to disallow the addition of " "comments to generated iptables rules that describe each rule's purpose. " "System must support the iptables comments module for addition of comments." msgstr "" "將註解新增至 iptables è¦å‰‡ã€‚設為 false å¯ç¦æ­¢å‘所產生用來說明æ¯ä¸€å€‹è¦å‰‡ä¹‹ç›®çš„" "çš„ iptables è¦å‰‡æ–°å¢žè¨»è§£ã€‚ç³»çµ±å¿…é ˆæ”¯æ´ iptables 註解模組æ‰èƒ½æ–°å¢žè¨»è§£ã€‚" msgid "Address not present on interface" msgstr "ä½å€æœªå‘ˆç¾åœ¨ä»‹é¢ä¸Š" msgid "" "Address to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "ç”¨æ–¼æŽ¥è½ OpenFlow 連線的ä½å€ã€‚僅用於 'native' 驅動程å¼ã€‚" msgid "Adds test attributes to core resources." msgstr "將測試屬性新增至核心資æºã€‚" #, python-format msgid "Agent %(id)s is not a L3 Agent or has been disabled" msgstr "代ç†ç¨‹å¼ %(id)s 䏿˜¯ L3 代ç†ç¨‹å¼æˆ–者已åœç”¨" #, python-format msgid "Agent %(id)s is not a valid DHCP Agent or has been disabled" msgstr "代ç†ç¨‹å¼ %(id)s 䏿˜¯æœ‰æ•ˆçš„ DHCP 代ç†ç¨‹å¼æˆ–者已åœç”¨" msgid "" "Agent starts with admin_state_up=False when enable_new_agents=False. In the " "case, user's resources will not be scheduled automatically to the agent " "until admin changes admin_state_up to True." msgstr "" "ç•¶ enable_new_agents=False 時,代ç†ç¨‹å¼å¾ž admin_state_up=False 開始。在這種情" "æ³ä¸‹ï¼Œä¸æœƒå°‡ä½¿ç”¨è€…的資æºè‡ªå‹•排程到代ç†ç¨‹å¼ï¼Œç›´åˆ°ç®¡ç†è€…å°‡ admin_state_up 變更" "為 True 為止。" #, python-format msgid "Agent updated: %(payload)s" msgstr "已更新代ç†ç¨‹å¼ï¼š%(payload)s" msgid "Allow auto scheduling networks to DHCP agent." msgstr "容許自動將網路排程到 DHCP 代ç†ç¨‹å¼ã€‚" msgid "Allow auto scheduling of routers to L3 agent." msgstr "容許自動將路由器排定到 L3 代ç†ç¨‹å¼ã€‚" msgid "" "Allow overlapping IP support in Neutron. Attention: the following parameter " "MUST be set to False if Neutron is being used in conjunction with Nova " "security groups." msgstr "" "容許 Neutron 中的é‡ç–Š IP 支æ´ã€‚注æ„:如果將 Neutron 與 Nova 安全性群組一起使" "ç”¨ï¼Œå‰‡å¿…é ˆå°‡ä¸‹åˆ—åƒæ•¸è¨­ç‚º False。" msgid "Allow running metadata proxy." msgstr "容許執行 meta 資料 Proxy。" msgid "Allow sending resource operation notification to DHCP agent" msgstr "容許將資æºä½œæ¥­é€šçŸ¥å‚³é€çµ¦ DHCP 代ç†ç¨‹å¼" msgid "Allow the creation of PTR records" msgstr "容許建立 PTR 記錄" msgid "Allow the usage of the bulk API" msgstr "容許使用主體 API" msgid "Allow to perform insecure SSL (https) requests to nova metadata" msgstr "å®¹è¨±å° Nova meta 資料執行ä¸å®‰å…¨çš„ SSL (HTTPS) è¦æ±‚" msgid "" "Allows for serving metadata requests coming from a dedicated metadata access " "network whose CIDR is 169.254.169.254/16 (or larger prefix), and is " "connected to a Neutron router from which the VMs send metadata:1 request. In " "this case DHCP Option 121 will not be injected in VMs, as they will be able " "to reach 169.254.169.254 through a router. This option requires " "enable_isolated_metadata = True." msgstr "" "容許負責處ç†ä¾†è‡ªå°ˆç”¨ meta 資料存å–網路的 meta è³‡æ–™è¦æ±‚,該網路的 CIDR 是 " "169.254.169.254/16(或更大字首)並且已連接至 Neutron 路由器(VM 從此 Neutron " "è·¯ç”±å™¨å‚³é€ metadata:1 è¦æ±‚)。在這種情æ³ä¸‹ï¼ŒDHCP é¸é … 121 將䏿³¨å…¥ VM,因為它" "們能夠é€éŽè·¯ç”±å™¨å‘¼å«åˆ° 169.254.169.254。這個é¸é …éœ€è¦ " "enable_isolated_metadata = True。" msgid "An RBAC policy already exists with those values." msgstr "包å«é‚£äº›å€¼çš„ RBAC 原則已經存在。" msgid "An identifier must be specified when updating a subnet" msgstr "æ›´æ–°å­ç¶²è·¯æ™‚,必須æä¾› ID" msgid "An interface driver must be specified" msgstr "必須指定介é¢é©…動程å¼" msgid "" "An ordered list of extension driver entrypoints to be loaded from the " "neutron.ml2.extension_drivers namespace. For example: extension_drivers = " "port_security,qos" msgstr "" "è¦å¾ž neutron.ml2.extension_drivers å稱空間載入之延伸驅動程å¼é€²å…¥é»žçš„æœ‰åºæ¸…" "單。例如:extension_drivers = port_security,qos" msgid "" "An ordered list of networking mechanism driver entrypoints to be loaded from " "the neutron.ml2.mechanism_drivers namespace." msgstr "" "è¦å¾ž neutron.ml2.mechanism_drivers å稱空間載入的網路機制驅動程å¼é€²å…¥é»žæœ‰åºæ¸…" "單。" msgid "An unknown error has occurred. Please try your request again." msgstr "ç™¼ç”Ÿä¸æ˜ŽéŒ¯èª¤ã€‚è«‹é‡è©¦è¦æ±‚。" msgid "Async process didn't respawn" msgstr "åŒæ­¥ç¨‹åºæœªå†æ¬¡å¤§é‡ç”¢ç”Ÿ" msgid "Authorization URL for connecting to designate in admin context" msgstr "用於連接以在管ç†ç’°å¢ƒå®šç¾©ä¸­æŒ‡å®šçš„æŽˆæ¬Š URL" msgid "Automatically remove networks from offline DHCP agents." msgstr "從離線 DHCP 代ç†ç¨‹å¼è‡ªå‹•移除網路。" msgid "" "Automatically reschedule routers from offline L3 agents to online L3 agents." msgstr "自動將路由器從離線 L3 代ç†ç¨‹å¼é‡æ–°æŽ’定至線上 L3代ç†ç¨‹å¼ã€‚" msgid "Availability zone of this node" msgstr "此節點的å¯ç”¨æ€§å€åŸŸã€‚" msgid "Available commands" msgstr "å¯ç”¨çš„æŒ‡ä»¤" msgid "Backend does not support VLAN Transparency." msgstr "å¾Œç«¯ä¸æ”¯æ´ VLAN é€é€šæ€§ã€‚" #, python-format msgid "Base MAC: %s" msgstr "基本 MAC:%s" msgid "" "Base log dir for dnsmasq logging. The log contains DHCP and DNS log " "information and is useful for debugging issues with either DHCP or DNS. If " "this section is null, disable dnsmasq log." msgstr "" "用於 dnsmasq è¨˜è¼‰çš„åŸºæœ¬æ—¥èªŒç›®éŒ„ã€‚æ—¥èªŒåŒ…å« DHCP åŠ DNS æ—¥èªŒè³‡è¨Šï¼Œä¸¦ä¸”åœ¨å° " "DHCP 或 DNS æ–¹é¢çš„å•é¡Œé€²è¡Œé™¤éŒ¯æ™‚å¾ˆæœ‰ç”¨ã€‚å¦‚æžœæ­¤å€æ®µæ˜¯ç©ºå€¼ï¼Œå‰‡å°‡åœç”¨ dnsmasq æ—¥" "誌。" msgid "Body contains invalid data" msgstr "內文包å«ç„¡æ•ˆè³‡æ–™" msgid "Both network_id and router_id are None. One must be provided." msgstr "network_id åŠ router_id 皆為 None。必須æä¾›å…¶ä¸­ä¸€å€‹ã€‚" #, python-format msgid "Bridge %(bridge)s does not exist." msgstr "橋接器 %(bridge)s ä¸å­˜åœ¨ã€‚" msgid "Bulk operation not supported" msgstr "䏿”¯æ´ä¸»é«”作業" msgid "CIDR to monitor" msgstr "è¦ç›£è¦–çš„ CIDR" #, python-format msgid "Callback for %(resource_type)s not found" msgstr "找ä¸åˆ° %(resource_type)s 的回呼" #, python-format msgid "Callback for %(resource_type)s returned wrong resource type" msgstr "%(resource_type)s 的回呼傳回了錯誤的資æºé¡žåž‹" #, python-format msgid "Cannot add floating IP to port %s that has no fixed IPv4 addresses" msgstr "無法將浮動 IP 新增至沒有固定 IPv4 ä½å€çš„埠 %s" #, python-format msgid "Cannot add multiple callbacks for %(resource_type)s" msgstr "無法新增 %(resource_type)s 的多é‡å›žå‘¼" #, python-format msgid "Cannot allocate IPv%(req_ver)s subnet from IPv%(pool_ver)s subnet pool" msgstr "無法從 IPv%(pool_ver)s å­ç¶²è·¯å„²å­˜å€é…ç½® IPv%(req_ver)s å­ç¶²è·¯" msgid "Cannot allocate requested subnet from the available set of prefixes" msgstr "無法é…ç½®å¯ç”¨å­—é¦–é›†ä¸­çš„æ‰€è¦æ±‚å­ç¶²è·¯" msgid "Cannot disable enable_dhcp with ipv6 attributes set" msgstr "在設定了 ipv6 屬性的情æ³ä¸‹ï¼Œç„¡æ³•åœç”¨ enable_dhcp" #, python-format msgid "Cannot handle subnet of type %(subnet_type)s" msgstr "無法處ç†é¡žåž‹ç‚º %(subnet_type)s çš„å­ç¶²è·¯" msgid "Cannot have multiple IPv4 subnets on router port" msgstr "路由器埠上ä¸èƒ½å…·æœ‰å¤šå€‹ IPv4 å­ç¶²è·¯" #, python-format msgid "" "Cannot have multiple router ports with the same network id if both contain " "IPv6 subnets. Existing port %(p)s has IPv6 subnet(s) and network id %(nid)s" msgstr "" "ä¸èƒ½å…·æœ‰å¤šå€‹åŒ…å«ç›¸åŒç¶²è·¯ ID çš„è·¯ç”±å™¨åŸ ï¼ˆå¦‚æžœå…©è€…éƒ½åŒ…å« IPv6 å­ç¶²è·¯ï¼‰ã€‚ç¾æœ‰åŸ  " "%(p)s 具有 IPv6 å­ç¶²è·¯å’Œç¶²è·¯ ID %(nid)s" #, python-format msgid "" "Cannot host distributed router %(router_id)s on legacy L3 agent %(agent_id)s." msgstr "ç„¡æ³•åœ¨èˆŠå¼ L3 代ç†ç¨‹å¼ %(agent_id)s 上管ç†åˆ†æ•£å¼è·¯ç”±å™¨ %(router_id)s。" msgid "Cannot mix IPv4 and IPv6 prefixes in a subnet pool." msgstr "ä¸èƒ½åœ¨ä¸€å€‹å­ç¶²è·¯å„²å­˜å€ä¸­æ··åˆ IPv4 與 IPv6 字首。" msgid "Cannot specify both subnet-id and port-id" msgstr "ç„¡æ³•åŒæ™‚指定 subnet-id åŠ port-id" msgid "Cannot understand JSON" msgstr "無法ç†è§£ JSON" #, python-format msgid "Cannot update read-only attribute %s" msgstr "無法更新唯讀屬性 %s" msgid "Certificate Authority public key (CA cert) file for ssl" msgstr "用於 SSL 的「憑證管ç†ä¸­å¿ƒã€å…¬é–‹é‡‘鑰(CA 憑證)檔案" #, python-format msgid "" "Change would make usage less than 0 for the following resources: %(unders)s." msgstr "變更會使下列資æºçš„用é‡å°æ–¼ 0:%(unders)s。" msgid "Check ebtables installation" msgstr "檢查 ebtables 安è£" msgid "Check for ARP header match support" msgstr "檢查 ARP æ¨™é ­ç¬¦åˆæ”¯æ´" msgid "Check for ARP responder support" msgstr "檢查 ARP 回應者支æ´" msgid "Check for ICMPv6 header match support" msgstr "檢查 ICMPv6 æ¨™é ­ç¬¦åˆæ”¯æ´" msgid "Check for OVS Geneve support" msgstr "檢查 OVS Geneve 支æ´" msgid "Check for OVS vxlan support" msgstr "檢查 OVS vxlan 支æ´" msgid "Check for VF management support" msgstr "檢查 VF ç®¡ç†æ”¯æ´" msgid "Check for iproute2 vxlan support" msgstr "檢查 iproute2 vxlan 支æ´" msgid "Check for nova notification support" msgstr "檢查 Nova 通知支æ´" msgid "Check for patch port support" msgstr "檢查修補程å¼åŸ æ”¯æ´" msgid "Check ip6tables installation" msgstr "檢查 ip6tables 安è£" msgid "Check ipset installation" msgstr "檢查 ipset 安è£" msgid "Check keepalived IPv6 support" msgstr "檢查 keepalived IPv6 支æ´" msgid "Check minimal dibbler version" msgstr "檢查 dibbler 版本下é™" msgid "Check minimal dnsmasq version" msgstr "檢查最低 dnsmasq 版本" msgid "Check netns permission settings" msgstr "檢查 netns 權é™è¨­å®š" msgid "Check ovs conntrack support" msgstr "檢查 ovs conntrack 支æ´" msgid "Check ovsdb native interface support" msgstr "檢查 OVSDB åŽŸç”Ÿä»‹é¢æ”¯æ´" #, python-format msgid "" "Cidr %(subnet_cidr)s of subnet %(subnet_id)s overlaps with cidr %(cidr)s of " "subnet %(sub_id)s" msgstr "" "å­ç¶²è·¯ %(subnet_id)s çš„ CIDR %(subnet_cidr)s 與å­ç¶²è·¯ %(sub_id)s çš„ CIDR " "%(cidr)s é‡ç–Š" msgid "Cleanup resources of a specific agent type only." msgstr "åªæ¸…除特定代ç†ç¨‹å¼é¡žåž‹çš„資æºã€‚" msgid "Client certificate for nova metadata api server." msgstr "Nova meta 資料 API 伺æœå™¨çš„用戶端憑證。" msgid "" "Comma-separated list of : tuples, mapping " "network_device to the agent's node-specific list of virtual functions that " "should not be used for virtual networking. vfs_to_exclude is a semicolon-" "separated list of virtual functions to exclude from network_device. The " "network_device in the mapping should appear in the physical_device_mappings " "list." msgstr "" "network_device èˆ‡ä¸æ‡‰ç”¨æ–¼è™›æ“¬ç¶²è·¯ä¹‹è™›æ“¬å‡½æ•¸çš„代ç†ç¨‹å¼ç¯€é»žå°ˆç”¨æ¸…單的 " ": å€¼çµ„å°æ˜ æ¸…單(使用逗點å€éš”)。" "vfs_to_exclude 是è¦å¾ž network_device 中排除之虛擬函數的分號å€é𔿏…å–®ã€‚å°æ˜ ä¸­" "çš„ network_device 應該出ç¾åœ¨ physical_device_mappings 清單中。" msgid "" "Comma-separated list of : tuples mapping " "physical network names to the agent's node-specific physical network device " "interfaces of SR-IOV physical function to be used for VLAN networks. All " "physical networks listed in network_vlan_ranges on the server should have " "mappings to appropriate interfaces on each agent." msgstr "" "實體網路å稱與è¦ç”¨æ–¼ VLAN 網路之 SR-IOV 實體功能的代ç†ç¨‹å¼ç¯€é»žå°ˆç”¨å¯¦é«”網路è£" "置介é¢çš„: å€¼çµ„å°æ˜ æ¸…單(使用逗點å€éš”)。列" "在伺æœå™¨ä¸Šnetwork_vlan_ranges 中的所有實體網路都應該具有與æ¯å€‹ä»£ç†ç¨‹å¼ä¸Šé©ç•¶" "介é¢çš„å°æ˜ ã€‚" msgid "" "Comma-separated list of : tuples " "mapping physical network names to the agent's node-specific physical network " "interfaces to be used for flat and VLAN networks. All physical networks " "listed in network_vlan_ranges on the server should have mappings to " "appropriate interfaces on each agent." msgstr "" "實體網路å稱與è¦ç”¨æ–¼å¹³é¢ç¶²è·¯åŠ VLAN 網路之代ç†ç¨‹å¼ç¯€é»žå°ˆç”¨å¯¦é«”網路介é¢çš„" ": å€¼çµ„å°æ˜ æ¸…單(使用逗點å€éš”)。列在伺" "æœå™¨ä¸Šnetwork_vlan_ranges 中的所有實體網路都應該具有與æ¯å€‹ä»£ç†ç¨‹å¼ä¸Šé©ç•¶ä»‹é¢" "çš„å°æ˜ ã€‚" msgid "" "Comma-separated list of : tuples enumerating ranges of GRE " "tunnel IDs that are available for tenant network allocation" msgstr "" ": 值組的逗點å€é𔿏…單,用於列舉å¯ç”¨æ–¼æ‰¿ç§Ÿäººç¶²è·¯é…置的 GRE 通" "é“ ID 範åœ" msgid "" "Comma-separated list of : tuples enumerating ranges of " "Geneve VNI IDs that are available for tenant network allocation" msgstr "" ": 值組的逗點å€é𔿏…單,用於列舉å¯ç”¨æ–¼æ‰¿ç§Ÿäººç¶²è·¯é…置的 " "Geneve VNI ID 範åœ" msgid "" "Comma-separated list of : tuples enumerating ranges of " "VXLAN VNI IDs that are available for tenant network allocation" msgstr "" ": 值組的逗點å€é𔿏…單,用於列舉å¯ç”¨æ–¼æ‰¿ç§Ÿäººç¶²è·¯é…置的 VXLAN " "VNI ID 範åœ" msgid "" "Comma-separated list of the DNS servers which will be used as forwarders." msgstr "將用來作為轉éžç¨‹å¼çš„ DNS 伺æœå™¨é€—點å€é𔿏…單。" msgid "Command to execute" msgstr "è¦åŸ·è¡Œçš„æŒ‡ä»¤" msgid "Config file for interface driver (You may also use l3_agent.ini)" msgstr "介é¢é©…動程å¼çš„é…置檔(您也å¯ä½¿ç”¨ l3_agent.ini)" #, python-format msgid "Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s" msgstr "CIDR %(cidr)s 的乙太網路類型 %(ethertype)s 值有è¡çª" msgid "" "Controls whether the neutron security group API is enabled in the server. It " "should be false when using no security groups or using the nova security " "group API." msgstr "" "控制是å¦åœ¨ä¼ºæœå™¨ä¸­å•Ÿç”¨ Neutron 安全群組 API。當ä¸ä½¿ç”¨å®‰å…¨ç¾¤çµ„時或者使用 Nova " "安全群組 API 時,它應該是 false。" #, python-format msgid "Could not bind to %(host)s:%(port)s after trying for %(time)d seconds" msgstr "嘗試 %(time)d 秒後ä»ç„¡æ³•連çµè‡³ %(host)s:%(port)s" msgid "Could not deserialize data" msgstr "無法解除åºåˆ—化資料" #, python-format msgid "" "Current gateway ip %(ip_address)s already in use by port %(port_id)s. Unable " "to update." msgstr "埠 %(port_id)s 已在使用ç¾è¡Œé–˜é“ IP %(ip_address)s。無法更新。" msgid "" "DHCP lease duration (in seconds). Use -1 to tell dnsmasq to use infinite " "lease times." msgstr "" "DHCP 租賃期é™ï¼ˆä»¥ç§’為單ä½ï¼‰ã€‚使用 -1 å¯å‘ŠçŸ¥ dnsmasq 使用無é™çš„租賃時間。" msgid "" "DVR deployments for VXLAN/GRE/Geneve underlays require L2-pop to be enabled, " "in both the Agent and Server side." msgstr "" "VXLAN/GRE/Geneve 基礎的 DVR 部署需è¦åŒæ™‚在代ç†ç¨‹å¼ç«¯åŠä¼ºæœå™¨ç«¯å•Ÿç”¨ L2-pop。" msgid "" "Database engine for which script will be generated when using offline " "migration." msgstr "使用離線移轉時,將å°å…¶ç”¢ç”Ÿ Script 的資料庫引擎。" msgid "Default external networks must be shared to everyone." msgstr "必須將é è¨­å¤–部網路與所有使用者共用。" msgid "" "Default network type for external networks when no provider attributes are " "specified. By default it is None, which means that if provider attributes " "are not specified while creating external networks then they will have the " "same type as tenant networks. Allowed values for external_network_type " "config option depend on the network type values configured in type_drivers " "config option." msgstr "" "在未指定æä¾›è€…屬性時,外部網路的é è¨­ç¶²è·¯é¡žåž‹ã€‚ä¾é è¨­ï¼Œå®ƒæ˜¯ã€Œç„¡ã€ï¼Œé€™è¡¨ç¤ºå¦‚æžœ" "在建立外部網路時未指定æä¾›è€…屬性,則它們將相åŒçš„類型作為承租人網路。" "external_network_type é…ç½®é¸é …所接å—的值視 type_drivers é…ç½®é¸é …中é…置的網路" "類型值而定。" msgid "" "Default number of RBAC entries allowed per tenant. A negative value means " "unlimited." msgstr "æ¯å€‹æ‰¿ç§Ÿäººæ‰€å®¹è¨±çš„ RBAC 項目數目。負數值表示無é™åˆ¶ã€‚" msgid "" "Default number of resource allowed per tenant. A negative value means " "unlimited." msgstr "æ¯å€‹æ‰¿ç§Ÿäººæ‰€å®¹è¨±çš„é è¨­è³‡æºæ•¸ç›®ã€‚負數值表示無é™åˆ¶ã€‚" msgid "Default security group" msgstr "é è¨­å®‰å…¨ç¾¤çµ„" msgid "Default security group already exists." msgstr "é è¨­å®‰å…¨ç¾¤çµ„已存在。" msgid "" "Default value of availability zone hints. The availability zone aware " "schedulers use this when the resources availability_zone_hints is empty. " "Multiple availability zones can be specified by a comma separated string. " "This value can be empty. In this case, even if availability_zone_hints for a " "resource is empty, availability zone is considered for high availability " "while scheduling the resource." msgstr "" "å¯ç”¨æ€§å€åŸŸæç¤ºçš„é è¨­å€¼ã€‚ç•¶è³‡æº availability_zone_hints 為空時,å¯ç”¨æ€§å€åŸŸçŸ¥é“" "排程器使用此項。å¯ä»¥é€éŽé€—點å€éš”的字串來指定多個å¯ç”¨æ€§å€åŸŸã€‚此值å¯ä»¥æ˜¯ç©ºçš„。" "在這種情æ³ä¸‹ï¼Œå³ä½¿è³‡æº availability_zone_hints ç‚ºç©ºï¼Œä¹Ÿæœƒåœ¨æŽ’ç¨‹è³‡æºæ™‚å°‡å¯ç”¨æ€§" "å€åŸŸè¦–為高å¯ç”¨æ€§ã€‚" msgid "" "Define the default value of enable_snat if not provided in " "external_gateway_info." msgstr "" "定義 enable_snat çš„é è¨­å€¼ï¼ˆå¦‚果未在external_gateway_info 中æä¾›çš„話)。" msgid "" "Defines providers for advanced services using the format: :" ":[:default]" msgstr "" "使用下列格å¼ï¼Œçµ¦é€²éšŽæœå‹™å®šç¾©æä¾›è€…:::[:default]" msgid "Delete the namespace by removing all devices." msgstr "é€éŽç§»é™¤æ‰€æœ‰è£ç½®ä¾†åˆªé™¤å稱空間。" #, python-format msgid "Deleting port %s" msgstr "正在刪除埠 %s" #, python-format msgid "Deployment error: %(reason)s." msgstr "部署錯誤:%(reason)s。" msgid "Destroy IPsets even if there is an iptables reference." msgstr "å³ä½¿æœ‰ iptables åƒç…§ï¼Œä¹Ÿæ¯€æ IPset。" msgid "Destroy all IPsets." msgstr "æ¯€ææ‰€æœ‰ IPset。" #, python-format msgid "Device %(dev_name)s in mapping: %(mapping)s not unique" msgstr "å°æ˜  %(mapping)s 中的è£ç½® %(dev_name)s 䏿˜¯å”¯ä¸€çš„" #, python-format msgid "Device name %(dev_name)s is missing from physical_device_mappings" msgstr "physical_device_mappings ä¸­éºæ¼äº†è£ç½®å稱 %(dev_name)s" msgid "Device not found" msgstr "找ä¸åˆ°è£ç½®" #, python-format msgid "" "Distributed Virtual Router Mac Address for host %(host)s does not exist." msgstr "主機 %(host)s 的分散å¼è™›æ“¬è·¯ç”±å™¨ MAC ä½å€ä¸å­˜åœ¨ã€‚" msgid "Domain to use for building the hostnames" msgstr "用於建置主機å稱的網域" msgid "Downgrade no longer supported" msgstr "ä¸å†æ”¯æ´é™ç´š" #, python-format msgid "Driver %s is not unique across providers" msgstr "é©…å‹•ç¨‹å¼ %s 在æä¾›è€…之間䏿˜¯å”¯ä¸€çš„" msgid "Driver for external DNS integration." msgstr "用於外部 DNS æ•´åˆçš„驅動程å¼ã€‚" msgid "Driver for security groups firewall in the L2 agent" msgstr "L2 代ç†ç¨‹å¼ä¸­å®‰å…¨ç¾¤çµ„防ç«ç‰†çš„驅動程å¼" msgid "Driver to use for scheduling network to DHCP agent" msgstr "用於將網路排程到 DHCP 代ç†ç¨‹å¼çš„驅動程å¼" msgid "Driver to use for scheduling router to a default L3 agent" msgstr "用於將路由器排程到é è¨­ L3 代ç†ç¨‹å¼çš„驅動程å¼" msgid "" "Driver used for ipv6 prefix delegation. This needs to be an entry point " "defined in the neutron.agent.linux.pd_drivers namespace. See setup.cfg for " "entry points included with the neutron source." msgstr "" "用於 IPv6 字首委派的驅動程å¼ã€‚é€™éœ€è¦æ˜¯ neutron.agent.linux.pd_drivers 中定義" "的一個進入點。請åƒé–± setup.cfg,以å–å¾—Neutron 來æºéš¨é™„的進入點。" #, python-format msgid "" "Duplicate L3HARouterAgentPortBinding is created for router(s) %(router)s. " "Database cannot be upgraded. Please, remove all duplicates before upgrading " "the database." msgstr "" "為路由器 %(router)s 建立了é‡è¤‡çš„ L3HARouterAgentPortBinding。無法å‡ç´šè³‡æ–™åº«ã€‚" "請先移除所有é‡è¤‡é …目,然後å†å‡ç´šè³‡æ–™åº«ã€‚" msgid "Duplicate Security Group Rule in POST." msgstr "POST 中的安全群組è¦å‰‡é‡è¤‡ã€‚" msgid "Duplicate address detected" msgstr "嵿¸¬åˆ°é‡è¤‡ä½å€" msgid "Duplicate segment entry in request." msgstr "è¦æ±‚ä¸­çš„å€æ®µé …ç›®é‡è¤‡ã€‚" #, python-format msgid "ERROR: %s" msgstr "錯誤:%s" msgid "" "ERROR: Unable to find configuration file via the default search paths (~/." "neutron/, ~/, /etc/neutron/, /etc/) and the '--config-file' option!" msgstr "" "錯誤:無法é€éŽé è¨­æœå°‹è·¯å¾‘(~/.neutron/ã€~/ã€/etc/neutron/åŠ /etc/)與 '--" "config-file' é¸é …來找到é…置檔ï¼" msgid "" "Either one of parameter network_id or router_id must be passed to _get_ports " "method." msgstr "必須將 network_id 或 router_id ä¸­çš„ä¸€å€‹åƒæ•¸å‚³éžè‡³_get_ports 方法。" msgid "Either subnet_id or port_id must be specified" msgstr "必須指定 subnet_id 或 port_id" msgid "Empty physical network name." msgstr "空的實體網路å稱。" msgid "Empty subnet pool prefix list." msgstr "空的å­ç¶²è·¯å„²å­˜å€å­—首清單。" msgid "Enable HA mode for virtual routers." msgstr "啟用虛擬路由器的 HA 模å¼ã€‚" msgid "Enable SSL on the API server" msgstr "在 API 伺æœå™¨ä¸Šå•Ÿç”¨ SSL" msgid "" "Enable VXLAN on the agent. Can be enabled when agent is managed by ml2 " "plugin using linuxbridge mechanism driver" msgstr "" "在代ç†ç¨‹å¼ä¸Šå•Ÿç”¨ VXLAN。代ç†ç¨‹å¼æ˜¯ç”± ML2 外掛程å¼ï¼ˆä½¿ç”¨ LinuxBridge 機制驅動" "程å¼ï¼‰ç®¡ç†æ™‚,å¯ä»¥å•Ÿç”¨ VXLAN" msgid "" "Enable local ARP responder if it is supported. Requires OVS 2.1 and ML2 " "l2population driver. Allows the switch (when supporting an overlay) to " "respond to an ARP request locally without performing a costly ARP broadcast " "into the overlay." msgstr "" "å¦‚æžœæ”¯æ´æœ¬ç«¯ ARP å›žæ‡‰è€…ï¼Œè«‹å°‡å…¶å•Ÿç”¨ã€‚éœ€è¦ OVS 2.1 åŠ ML2 l2population 驅動程" "å¼ã€‚容許交æ›å™¨ï¼ˆç•¶æ”¯æ´å¥—ç‰ˆæ™‚ï¼‰åœ¨æœ¬ç«¯å° ARP è¦æ±‚åšå‡ºå›žæ‡‰ï¼Œä½†ä¸åŸ·è¡Œé«˜æˆæœ¬çš„ " "ARP æ’­é€è‡³å¥—版。" msgid "" "Enable services on an agent with admin_state_up False. If this option is " "False, when admin_state_up of an agent is turned False, services on it will " "be disabled. Agents with admin_state_up False are not selected for automatic " "scheduling regardless of this option. But manual scheduling to such agents " "is available if this option is True." msgstr "" "å° admin_state_up 為 False 的代ç†ç¨‹å¼å•Ÿç”¨æœå‹™ã€‚如果此é¸é …為 False,則當代ç†ç¨‹" "å¼çš„ admin_state_up 變為 False 時,將åœç”¨å…¶ä¸Šçš„æœå‹™ã€‚ç„¡è«–æ­¤é¸é …ç‚ºä½•ï¼Œéƒ½ä¸æœƒé¸" "å– admin_state_up 為 False的代ç†ç¨‹å¼ä»¥é€²è¡Œè‡ªå‹•排程。但如果此é¸é …為 True,則å¯" "以使用此類代ç†ç¨‹å¼çš„æ‰‹å‹•排程。" msgid "" "Enables IPv6 Prefix Delegation for automatic subnet CIDR allocation. Set to " "True to enable IPv6 Prefix Delegation for subnet allocation in a PD-capable " "environment. Users making subnet creation requests for IPv6 subnets without " "providing a CIDR or subnetpool ID will be given a CIDR via the Prefix " "Delegation mechanism. Note that enabling PD will override the behavior of " "the default IPv6 subnetpool." msgstr "" "é‡å°è‡ªå‹•å­ç¶²è·¯ CIDR é…置啟用 IPv6 字首委派。設為 True å¯é‡å°æ”¯æ´ PD 之環境中" "çš„å­ç¶²è·¯é…置,啟用 IPv6 字首委派。將é€éŽå­—首委派機制å‘é‡å° IPv6 å­ç¶²è·¯ç™¼å‡ºå­" "ç¶²è·¯å»ºç«‹è¦æ±‚但廿œªæä¾› CIDR 或å­ç¶²è·¯å„²å­˜å€ ID 的使用者,æä¾› CIDR。請注æ„,啟" "用 PD 將置æ›é è¨­ IPv6 å­ç¶²è·¯å„²å­˜å€çš„行為。" msgid "" "Enables the dnsmasq service to provide name resolution for instances via DNS " "resolvers on the host running the DHCP agent. Effectively removes the '--no-" "resolv' option from the dnsmasq process arguments. Adding custom DNS " "resolvers to the 'dnsmasq_dns_servers' option disables this feature." msgstr "" "容許 dnsmasq æœå‹™é€éŽåŸ·è¡Œ DHCP 代ç†ç¨‹å¼çš„主機上的 DNS è§£æžå™¨ï¼Œç‚ºå¯¦ä¾‹æä¾›å稱" "è§£æžã€‚從 dnsmasq 程åºå¼•數中有效地移除 '--no-resolv' é¸é …。將自訂 DNS è§£æžå™¨æ–°" "增至 'dnsmasq_dns_servers' é¸é …會åœç”¨æ­¤åŠŸèƒ½ã€‚" msgid "End of VLAN range is less than start of VLAN range" msgstr "VLAN 範åœçš„çµ‚æ­¢å€¼å°æ–¼ VLAN 範åœçš„起始值" msgid "End of tunnel range is less than start of tunnel range" msgstr "通é“範åœçš„çµ‚æ­¢å€¼å°æ–¼é€šé“範åœçš„起始值" #, python-format msgid "Error %(reason)s while attempting the operation." msgstr "嘗試執行作業時發生錯誤 %(reason)s。" #, python-format msgid "Error parsing dns address %s" msgstr "å‰–æž DNS ä½å€ %s 時發生錯誤" #, python-format msgid "Error while reading %s" msgstr "è®€å– %s 時發生錯誤" #, python-format msgid "" "Exceeded %s second limit waiting for address to leave the tentative state." msgstr "等待ä½å€é›¢é–‹æš«è¨‚ç‹€æ…‹æ™‚ï¼Œå·²è¶…éŽ %s ç§’é™åˆ¶ã€‚" msgid "Existing prefixes must be a subset of the new prefixes" msgstr "ç¾æœ‰å­—首必須是新字首的å­é›†" #, python-format msgid "" "Exit code: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" msgstr "" "çµæŸç¢¼ï¼š%(returncode)d;標準輸入:%(stdin)s;標準輸出:%(stdout)s,標準錯誤:" "%(stderr)s" #, python-format msgid "Extension %(driver)s failed." msgstr "延伸 %(driver)s 失敗。" #, python-format msgid "" "Extension driver %(driver)s required for service plugin %(service_plugin)s " "not found." msgstr "找ä¸åˆ°æœå‹™å¤–æŽ›ç¨‹å¼ %(service_plugin)s æ‰€éœ€çš„å»¶ä¼¸é©…å‹•ç¨‹å¼ %(driver)s。" msgid "" "Extension to use alongside ml2 plugin's l2population mechanism driver. It " "enables the plugin to populate VXLAN forwarding table." msgstr "" "與 ML2 外掛程å¼çš„ l2population 機制驅動程å¼ä¸€èµ·ä½¿ç”¨çš„延伸。它支æ´è©²å¤–掛程å¼å°‡" "資料移入 VXLAN 轉éžè¡¨æ ¼ã€‚" #, python-format msgid "Extension with alias %s does not exist" msgstr "別å為 %s 的延伸ä¸å­˜åœ¨" msgid "Extensions list to use" msgstr "è¦ä½¿ç”¨çš„延伸清單" #, python-format msgid "Extensions not found: %(extensions)s." msgstr "找ä¸åˆ°å»¶ä¼¸ï¼š%(extensions)s。" #, python-format msgid "External IP %s is the same as the gateway IP" msgstr "外部 IP %s èˆ‡é–˜é“ IP 相åŒ" #, python-format msgid "Failed rescheduling router %(router_id)s: no eligible l3 agent found." msgstr "ç„¡æ³•é‡æ–°æŽ’定路由器 %(router_id)s:找ä¸åˆ°é©ç”¨çš„ L3 代ç†ç¨‹å¼ã€‚" #, python-format msgid "Failed scheduling router %(router_id)s to the L3 Agent %(agent_id)s." msgstr "無法將路由器 %(router_id)s 排程到 L3 代ç†ç¨‹å¼ %(agent_id)s。" #, python-format msgid "Failed to allocate subnet: %(reason)s." msgstr "無法é…ç½®å­ç¶²è·¯ï¼š%(reason)s。" msgid "" "Failed to associate address scope: subnetpools within an address scope must " "have unique prefixes." msgstr "無法與ä½å€ç¯„åœå»ºç«‹é—œè¯ï¼šä½å€ç¯„åœå…§çš„å­ç¶²è·¯å„²å­˜å€å¿…須具有唯一字首。" #, python-format msgid "" "Failed to create port on network %(network_id)s, because fixed_ips included " "invalid subnet %(subnet_id)s" msgstr "" "無法在網路 %(network_id)s 上建立埠,因為 fixed_ips 包å«ç„¡æ•ˆçš„å­ç¶²è·¯ " "%(subnet_id)s" #, python-format msgid "Failed to locate source for %s." msgstr "找ä¸åˆ° %s 的來æºã€‚" msgid "Failed to remove supplemental groups" msgstr "無法移除增補群組" #, python-format msgid "Failed to set gid %s" msgstr "無法設定 GID %s" #, python-format msgid "Failed to set uid %s" msgstr "無法設定 UID %s" #, python-format msgid "Failed to set-up %(type)s tunnel port to %(ip)s" msgstr "無法將 %(type)s 通é“埠設為 %(ip)s" msgid "Failure applying iptables rules" msgstr "套用 iptables è¦å‰‡æ™‚失敗" #, python-format msgid "Failure waiting for address %(address)s to become ready: %(reason)s" msgstr "等待ä½å€ %(address)s 變æˆå‚™å¦¥æ™‚失敗:%(reason)s" msgid "Flat provider networks are disabled" msgstr "å·²åœç”¨å¹³é¢æä¾›ç¨‹åºç¶²è·¯" msgid "For TCP/UDP protocols, port_range_min must be <= port_range_max" msgstr "å°æ–¼ TCP/UDP 通訊å”定,port_range_min å¿…é ˆ <= port_range_max" msgid "Force ip_lib calls to use the root helper" msgstr "強制 ip_lib 呼å«ä½¿ç”¨æ ¹èªªæ˜Žç¨‹å¼" #, python-format msgid "Found duplicate extension: %(alias)s." msgstr "發ç¾é‡è¤‡å»¶ä¼¸ï¼š%(alias)s。" #, python-format msgid "" "Found overlapping allocation pools: %(pool_1)s %(pool_2)s for subnet " "%(subnet_cidr)s." msgstr "" "發ç¾ä»¥ä¸‹å­ç¶²è·¯çš„é…ç½®å„²å­˜å€ %(pool_1)s %(pool_2)sé‡ç–Šï¼š%(subnet_cidr)s。" msgid "Gateway IP version inconsistent with allocation pool version" msgstr "é–˜é“ IP 版本與é…置儲存å€ç‰ˆæœ¬ä¸ä¸€è‡´" #, python-format msgid "Gateway ip %(ip_address)s conflicts with allocation pool %(pool)s." msgstr "é–˜é“ IP %(ip_address)s 與é…ç½®å„²å­˜å€ %(pool)s 相è¡çªã€‚" msgid "Gateway is not valid on subnet" msgstr "é–˜é“在å­ç¶²è·¯ä¸Šç„¡æ•ˆ" msgid "" "Geneve encapsulation header size is dynamic, this value is used to calculate " "the maximum MTU for the driver. This is the sum of the sizes of the outer " "ETH + IP + UDP + GENEVE header sizes. The default size for this field is 50, " "which is the size of the Geneve header without any additional option headers." msgstr "" "Geneve å°è£æ¨™é ­å¤§å°æ˜¯å‹•態的,此值用於計算驅動程å¼çš„ MTU 上é™ã€‚這是其他 ETH çš„" "å¤§å° + IP + UDP + GENEVE 標頭大å°çš„總和。此欄ä½çš„é è¨­å¤§å°æ˜¯ 50,這是 Geneve " "標頭(ä¸å«ä»»ä½•å…¶ä»–é¸é …標頭)的大å°ã€‚" msgid "" "Group (gid or name) running metadata proxy after its initialization (if " "empty: agent effective group)." msgstr "" "在 meta 資料 Proxy 起始設定之後執行該 Proxy 的群組(GID 或å稱)(如果為空:" "則為代ç†ç¨‹å¼æœ‰æ•ˆç¾¤çµ„)。" msgid "Group (gid or name) running this process after its initialization" msgstr "在此程åºèµ·å§‹è¨­å®šä¹‹å¾ŒåŸ·è¡Œæ­¤ç¨‹åºçš„群組(GID 或å稱)" msgid "" "Hostname to be used by the Neutron server, agents and services running on " "this machine. All the agents and services running on this machine must use " "the same host value." msgstr "" "在此機器上執行之 Neutron 伺æœå™¨ã€ä»£ç†ç¨‹å¼åŠæœå‹™è¦ä½¿ç”¨çš„主機å稱。在此機器上執" "行的所有代ç†ç¨‹å¼åŠæœå‹™éƒ½å¿…須使用相åŒçš„主機值。" #, python-format msgid "" "ICMP code (port-range-max) %(value)s is provided but ICMP type (port-range-" "min) is missing." msgstr "" "æä¾›äº† ICMP 代碼 (port-range-max) %(value)sï¼Œä½†éºæ¼äº† ICMP 類型(port-range-" "min)。" msgid "ID of network" msgstr "網路的 ID" msgid "ID of network to probe" msgstr "è¦æŽ¢æ¸¬çš„ç¶²è·¯ ID" msgid "ID of probe port to delete" msgstr "è¦åˆªé™¤çš„æŽ¢é‡åŸ  ID" msgid "ID of probe port to execute command" msgstr "è¦åŸ·è¡ŒæŒ‡ä»¤çš„æŽ¢é‡åŸ  ID" msgid "ID of the router" msgstr "路由器 ID" #, python-format msgid "IP address %(ip)s already allocated in subnet %(subnet_id)s" msgstr "IP ä½å€ %(ip)s 已經在å­ç¶²è·¯ %(subnet_id)s 中得到é…ç½®" #, python-format msgid "IP address %(ip)s does not belong to subnet %(subnet_id)s" msgstr "IP ä½å€ %(ip)s ä¸å±¬æ–¼å­ç¶²è·¯ %(subnet_id)s" msgid "IP allocation failed. Try again later." msgstr "IP é…置失敗。請ç¨å¾Œå†è©¦ã€‚" msgid "IP allocation requires subnet_id or ip_address" msgstr "IP é…ç½®éœ€è¦ subnet_id 或 ip_address" #, python-format msgid "" "IPTablesManager.apply failed to apply the following set of iptables rules:\n" "%s" msgstr "" "IPTablesManager.apply 無法套用下列 iptables è¦å‰‡é›†ï¼š\n" "%s" msgid "IPtables conntrack zones exhausted, iptables rules cannot be applied." msgstr "iptables conntrack å€åŸŸå·²ç”¨ç›¡ï¼Œç„¡æ³•套用 iptables è¦å‰‡ã€‚" msgid "IPv6 Address Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "é‡å°å­—首委派,IPv6 ä½å€æ¨¡å¼å¿…須是 SLAAC 或 Stateless。" msgid "IPv6 RA Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "é‡å°å­—首委派,IPv6 RA 模å¼å¿…須是 SLAAC 或 Stateless。" #, python-format msgid "" "IPv6 address %(ip)s cannot be directly assigned to a port on subnet " "%(subnet_id)s as the subnet is configured for automatic addresses" msgstr "" "無法直接將 IPv6 ä½å€ %(ip)s 指派給å­ç¶²è·¯%(subnet_id)s 上的埠,因為該å­ç¶²è·¯é…" "置為用於自動ä½å€" #, python-format msgid "" "IPv6 subnet %s configured to receive RAs from an external router cannot be " "added to Neutron Router." msgstr "" "無法將é…置為從外部路由器接收 RA çš„ IPv6 å­ç¶²è·¯ %s 新增至 Neutron 路由器。" msgid "" "If True, then allow plugins that support it to create VLAN transparent " "networks." msgstr "如果為 True,則容許支æ´å®ƒçš„外掛程å¼å»ºç«‹ VLAN é€é€šç¶²è·¯ã€‚" msgid "Illegal IP version number" msgstr "無效的 IP 版本號碼" #, python-format msgid "" "Illegal prefix bounds: %(prefix_type)s=%(prefixlen)s, %(base_prefix_type)s=" "%(base_prefixlen)s." msgstr "" "無效的字首範åœï¼š%(prefix_type)s=%(prefixlen)s,%(base_prefix_type)s=" "%(base_prefixlen)s。" #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot " "associate with address scope %(address_scope_id)s because subnetpool " "ip_version is not %(ip_version)s." msgstr "" "å­ç¶²è·¯å„²å­˜å€é—œè¯ç„¡æ•ˆï¼šç„¡æ³•å°‡å­ç¶²è·¯å„²å­˜å€ %(subnetpool_id)s 與ä½å€ç¯„åœ " "%(address_scope_id)s 建立關è¯ï¼Œå› ç‚ºå­ç¶²è·¯å„²å­˜å€ ip_version 䏿˜¯ " "%(ip_version)s。" #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot be " "associated with address scope %(address_scope_id)s." msgstr "" "å­ç¶²è·¯å„²å­˜å€é—œè¯ç„¡æ•ˆï¼šç„¡æ³•å°‡å­ç¶²è·¯å„²å­˜å€ %(subnetpool_id)s 與ä½å€ç¯„åœ " "%(address_scope_id)s 建立關è¯ã€‚" #, python-format msgid "Illegal subnetpool update : %(reason)s." msgstr "å­ç¶²è·¯å„²å­˜å€æ›´æ–°ç„¡æ•ˆï¼š%(reason)s。" #, python-format msgid "Illegal update to prefixes: %(msg)s." msgstr "字首更新無效:%(msg)s。" msgid "" "In some cases the Neutron router is not present to provide the metadata IP " "but the DHCP server can be used to provide this info. Setting this value " "will force the DHCP server to append specific host routes to the DHCP " "request. If this option is set, then the metadata service will be activated " "for all the networks." msgstr "" "在部分情æ³ä¸‹ï¼ŒNeutron 路由器未呈ç¾ä»¥æä¾› meta 資料 IP,但 DHCP 伺æœå™¨å¯ç”¨æ–¼æ" "供此資訊。設定此值會強制讓 DHCP 伺æœå™¨å°‡ç‰¹å®šçš„主機路線附加至 DHCP è¦æ±‚。如果" "設定此é¸é …ï¼Œå‰‡å°‡å°æ‰€æœ‰ç¶²è·¯å•Ÿå‹• meta 資料æœå‹™ã€‚" msgid "" "Indicates that this L3 agent should also handle routers that do not have an " "external network gateway configured. This option should be True only for a " "single agent in a Neutron deployment, and may be False for all agents if all " "routers must have an external network gateway." msgstr "" "指示此 L3 代ç†ç¨‹å¼é‚„應該處ç†å°šæœªé…置外部網路閘é“的路由器。é‡å° Neutron 部署中" "的單個代ç†ç¨‹å¼ï¼Œé€™å€‹é¸é …åªæ‡‰è©²ç‚º True;如果所有路由器都必須具有外部網路閘é“," "則é‡å°æ‰€æœ‰è·¯ç”±å™¨ï¼Œé€™å€‹é¸é …應該為 False。" #, python-format msgid "Instance of class %(module)s.%(class)s must contain _cache attribute" msgstr "類別 %(module)s 的實例。%(class)s å¿…é ˆåŒ…å« _cache 屬性" #, python-format msgid "Insufficient prefix space to allocate subnet size /%s" msgstr "字首空間ä¸è¶³ï¼Œç„¡æ³•é…ç½®å­ç¶²è·¯å¤§å° /%s" msgid "Insufficient rights for removing default security group." msgstr "權é™ä¸è¶³ï¼Œç„¡æ³•移除é è¨­å®‰å…¨ç¾¤çµ„。" msgid "" "Integration bridge to use. Do not change this parameter unless you have a " "good reason to. This is the name of the OVS integration bridge. There is one " "per hypervisor. The integration bridge acts as a virtual 'patch bay'. All VM " "VIFs are attached to this bridge and then 'patched' according to their " "network connectivity." msgstr "" "è¦ä½¿ç”¨çš„æ•´åˆæ©‹æŽ¥å™¨ã€‚é™¤éžæ‚¨æœ‰é©ç•¶çš„原因,å¦å‰‡è«‹å‹¿è®Šæ›´æ­¤åƒæ•¸ã€‚這是 OVS æ•´åˆæ©‹æŽ¥" "器的å稱。æ¯å€‹ Hypervisor æœ‰ä¸€å€‹æ•´åˆæ©‹æŽ¥å™¨ã€‚æ•´åˆæ©‹æŽ¥å™¨å……ç•¶è™›æ“¬çš„ã€Œä¿®è£œç¨‹å¼æ©Ÿ" "æž¶ã€ã€‚所有 VM VIF 都已連接至此橋接器,然後根據其網路連線功能進行「修補ã€ã€‚" msgid "Interface to monitor" msgstr "è¦ç›£è¦–的介é¢" msgid "" "Interval between checks of child process liveness (seconds), use 0 to disable" msgstr "å­ç¨‹åºå­˜æ´»æª¢æŸ¥ä¹‹é–“的間隔(秒),使用 0 以åœç”¨" msgid "Interval between two metering measures" msgstr "å…©æ¬¡è¨ˆé‡æ¸¬é‡ä¹‹é–“的間隔" msgid "Interval between two metering reports" msgstr "兩次計é‡å ±å‘Šä¹‹é–“的間隔" #, python-format msgid "Invalid CIDR %(input)s given as IP prefix." msgstr "作為 IP 字首而æä¾›çš„ CIDR %(input)s 無效。" #, python-format msgid "Invalid Device %(dev_name)s: %(reason)s" msgstr "無效的è£ç½® %(dev_name)s:%(reason)s" #, python-format msgid "" "Invalid action '%(action)s' for object type '%(object_type)s'. Valid " "actions: %(valid_actions)s" msgstr "" "é‡å°ç‰©ä»¶é¡žåž‹ '%(object_type)s' 的動作 '%(action)s' 無效。有效動作:" "%(valid_actions)s" #, python-format msgid "" "Invalid authentication type: %(auth_type)s, valid types are: " "%(valid_auth_types)s" msgstr "無效的鑑別類型:%(auth_type)s,有效的類型為:%(valid_auth_types)s" #, python-format msgid "Invalid ethertype %(ethertype)s for protocol %(protocol)s." msgstr "通訊å”定 %(protocol)s 的乙太網路類型 %(ethertype)s 無效。" #, python-format msgid "Invalid format: %s" msgstr "無效的格å¼ï¼š%s" #, python-format msgid "Invalid instance state: %(state)s, valid states are: %(valid_states)s" msgstr "無效的實例狀態:%(state)s,有效的狀態為:%(valid_states)s" #, python-format msgid "Invalid mapping: '%s'" msgstr "ç„¡æ•ˆçš„å°æ˜ ï¼š'%s'" #, python-format msgid "Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'." msgstr "無效的網路 VLAN 範åœï¼š'%(vlan_range)s' - '%(error)s'。" #, python-format msgid "Invalid network VXLAN port range: '%(vxlan_range)s'." msgstr "無效的網路 VXLAN 埠範åœï¼š'%(vxlan_range)s'。" #, python-format msgid "Invalid pci slot %(pci_slot)s" msgstr "無效的 PCI æ’æ§½ %(pci_slot)s" #, python-format msgid "Invalid provider format. Last part should be 'default' or empty: %s" msgstr "無效的æä¾›è€…æ ¼å¼ã€‚最後一個部分應該是 'default' 或空白:%s" #, python-format msgid "Invalid resource type %(resource_type)s" msgstr "資æºé¡žåž‹ %(resource_type)s 無效" #, python-format msgid "Invalid route: %s" msgstr "無效的路徑:%s" msgid "Invalid service provider format" msgstr "無效的æœå‹™æä¾›è€…æ ¼å¼" #, python-format msgid "" "Invalid value for ICMP %(field)s (%(attr)s) %(value)s. It must be 0 to 255." msgstr "" "ICMP %(field)s (%(attr)s) 的值 %(value)s 無效。該值必須在 0 到255 之間。" #, python-format msgid "Invalid value for port %(port)s" msgstr "埠 %(port)s 的值無效" msgid "" "Iptables mangle mark used to mark ingress from external network. This mark " "will be masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "用於標示外部網路中入å£çš„ iptables 破壞標記。此標記將以 0xffff 進行é®ç½©ï¼Œä»¥ä¾¿" "åªä½¿ç”¨è¼ƒä½Žçš„ 16 ä½å…ƒã€‚" msgid "" "Iptables mangle mark used to mark metadata valid requests. This mark will be " "masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "用於標示 meta è³‡æ–™æœ‰æ•ˆè¦æ±‚çš„ iptables 破壞標記。此標記將以 0xffff 進行é®ç½©ï¼Œ" "以便åªä½¿ç”¨è¼ƒä½Žçš„ 16 ä½å…ƒã€‚" msgid "Keepalived didn't respawn" msgstr "Keepalived æœªå†æ¬¡å¤§é‡ç”¢ç”Ÿ" msgid "Keepalived didn't spawn" msgstr "Keepalived 未大é‡ç”¢ç”Ÿ" #, python-format msgid "" "Kernel HZ value %(value)s is not valid. This value must be greater than 0." msgstr "核心 HZ 值 %(value)s 無效。此值必須大於 0。" msgid "L3 agent failure to setup NAT for floating IPs" msgstr "L3 代ç†ç¨‹å¼ç„¡æ³•é‡å°æµ®å‹• IP 設定 NAT" msgid "L3 agent failure to setup floating IPs" msgstr "L3 代ç†ç¨‹å¼ç„¡æ³•設定浮動 IP" msgid "Limit number of leases to prevent a denial-of-service." msgstr "é™åˆ¶ç§Ÿè³ƒæ¬¡æ•¸ä»¥é˜²æ­¢é˜»æ–·æœå‹™æ”»æ“Šã€‚" msgid "List of :" msgstr ": 的清單" msgid "" "List of :: or " "specifying physical_network names usable for VLAN provider and tenant " "networks, as well as ranges of VLAN tags on each available for allocation to " "tenant networks." msgstr "" ":: 或 的清單,指定å¯" "用於 VLAN æä¾›è€…åŠæ‰¿ç§Ÿäººç¶²è·¯çš„ physical_network å稱,以åŠåœ¨æ¯å€‹å¯ç”¨æ–¼é…置給" "承租人網路的 physical_network 上指定 VLAN標籤範åœã€‚" msgid "" "List of network type driver entrypoints to be loaded from the neutron.ml2." "type_drivers namespace." msgstr "" "è¦å¾ž neutron.ml2.type_drivers å稱空間中載入的網路類型驅動程å¼é€²å…¥é»žæ¸…單。" msgid "" "List of physical_network names with which flat networks can be created. Use " "default '*' to allow flat networks with arbitrary physical_network names. " "Use an empty list to disable flat networks." msgstr "" "å¯ç”¨ä¾†å»ºç«‹å¹³é¢ç¶²è·¯çš„ physical_network å稱清單。使用é è¨­å€¼ '*' å¯å®¹è¨±å«æœ‰ä»»" "æ„ physical_network å稱的平é¢ç¶²è·¯ã€‚使用空白清單å¯åœç”¨å¹³é¢ç¶²è·¯ã€‚" msgid "Location for Metadata Proxy UNIX domain socket." msgstr "meta 資料 Proxy UNIX 網域 Socket çš„ä½ç½®" msgid "Location of Metadata Proxy UNIX domain socket" msgstr "meta 資料 Proxy UNIX 網域 Socket çš„ä½ç½®" msgid "Location to store DHCP server config files." msgstr "DHCP 伺æœå™¨é…置檔的儲存ä½ç½®ã€‚" msgid "Location to store IPv6 PD files." msgstr "用於儲存 IPv6 PD 檔的ä½ç½®ã€‚" msgid "Location to store IPv6 RA config files" msgstr "用於儲存 IPv6 RA é…置檔的ä½ç½®" msgid "Location to store child pid files" msgstr "å­é … PID 檔案的儲存ä½ç½®" msgid "Location to store keepalived/conntrackd config files" msgstr "用於儲存 keepalived/conntrackd é…置檔的ä½ç½®" msgid "Log agent heartbeats" msgstr "日誌代ç†ç¨‹å¼æ´»å‹•訊號" msgid "" "MTU of the underlying physical network. Neutron uses this value to calculate " "MTU for all virtual network components. For flat and VLAN networks, neutron " "uses this value without modification. For overlay networks such as VXLAN, " "neutron automatically subtracts the overlay protocol overhead from this " "value. Defaults to 1500, the standard value for Ethernet." msgstr "" "基礎實體網路的 MTU。Neutron 使用此值來計算所有虛擬網路元件的 MTU。若為平é¢ç¶²" "路和 VLAN 網路,Neutron 使用此值,而ä¸é€²è¡Œä¿®æ”¹ã€‚若為套版網路(例如 VXLAN)," "Neutron 會自動從此值中扣除套版通訊å”定é¡å¤–負擔。é è¨­å€¼ç‚º 1500,這是乙太網路的" "標準值。" msgid "MTU size of veth interfaces" msgstr "veth 介é¢çš„ MTU 大å°" msgid "Make the l2 agent run in DVR mode." msgstr "讓 L2 代ç†ç¨‹å¼åœ¨ DVR 模å¼ä¸‹åŸ·è¡Œã€‚" msgid "Malformed request body" msgstr "è¦æ±‚內文的格å¼ä¸æ­£ç¢º" #, python-format msgid "Malformed request body: %(reason)s." msgstr "è¦æ±‚內文的格å¼ä¸æ­£ç¢ºï¼š%(reason)s。" msgid "MaxRtrAdvInterval setting for radvd.conf" msgstr "radvd.conf çš„ MaxRtrAdvInterval 設定" msgid "Maximum number of DNS nameservers per subnet" msgstr "æ¯å€‹å­ç¶²è·¯çš„ DNS å稱伺æœå™¨æ•¸ç›®ä¸Šé™" msgid "" "Maximum number of L3 agents which a HA router will be scheduled on. If it is " "set to 0 then the router will be scheduled on every agent." msgstr "" "將在其中排程 HA 路由器的 L3 代ç†ç¨‹å¼æ•¸ç›®ä¸Šé™ã€‚如果將其設為 0,則將在æ¯ä¸€å€‹ä»£" "ç†ç¨‹å¼ä¸ŠæŽ’程該路由器。" msgid "Maximum number of allowed address pairs" msgstr "所容許的ä½å€é…å°æ•¸ç›®ä¸Šé™" msgid "Maximum number of host routes per subnet" msgstr "æ¯å€‹å­ç¶²è·¯çš„主機路徑數目上é™" msgid "Maximum number of routes per router" msgstr "æ¯å€‹è·¯ç”±å™¨çš„路徑數目上é™" msgid "" "Metadata Proxy UNIX domain socket mode, 4 values allowed: 'deduce': deduce " "mode from metadata_proxy_user/group values, 'user': set metadata proxy " "socket mode to 0o644, to use when metadata_proxy_user is agent effective " "user or root, 'group': set metadata proxy socket mode to 0o664, to use when " "metadata_proxy_group is agent effective group or root, 'all': set metadata " "proxy socket mode to 0o666, to use otherwise." msgstr "" "meta 資料 Proxy UNIX 網域 Socket 模å¼ï¼Œå®¹è¨±ä¸‹åˆ—四個值:'deduce':來自 " "metadata_proxy_user/group 值的 deduce 模å¼ï¼›'user':將 meta 資料 ProxySocket " "模å¼è¨­å®šç‚º 0o644,以在 metadata_proxy_user 是代ç†ç¨‹å¼æœ‰æ•ˆä½¿ç”¨è€…或 root 使用者" "時使用;'group':將 meta 資料 Proxy Socket 模å¼è¨­å®šç‚º 0o664,以在 " "metadata_proxy_group 是有效群組或 root 使用者時使用;'all':將 meta 資料" "Proxy Socket 模å¼è¨­å®šç‚º 0o666,以在其他情æ³ä¸‹ä½¿ç”¨ã€‚" msgid "Metering driver" msgstr "計é‡é©…動程å¼" msgid "MinRtrAdvInterval setting for radvd.conf" msgstr "radvd.conf çš„ MinRtrAdvInterval 設定" msgid "Minimize polling by monitoring ovsdb for interface changes." msgstr "é€éŽç›£è¦– OVSDB æ˜¯å¦æœ‰ä»‹é¢è®Šæ›´ï¼Œå°‡è¼ªè©¢æ¸›è‡³æœ€å°‘。" #, python-format msgid "Missing key in mapping: '%s'" msgstr "å°æ˜ ä¸­éºæ¼äº†ç´¢å¼•éµï¼š'%s'" msgid "" "Multicast group for VXLAN. When configured, will enable sending all " "broadcast traffic to this multicast group. When left unconfigured, will " "disable multicast VXLAN mode." msgstr "" "VXLAN çš„å¤šé‡æ’­é€ç¾¤çµ„。當已é…置時,容許將所有廣播資料æµé‡å‚³é€è‡³æ­¤å¤šé‡æ’­é€ç¾¤" "çµ„ã€‚ç•¶ä¿æŒæœªé…置時,將åœç”¨å¤šé‡æ’­é€ VXLAN 模å¼ã€‚" msgid "" "Multicast group(s) for vxlan interface. A range of group addresses may be " "specified by using CIDR notation. Specifying a range allows different VNIs " "to use different group addresses, reducing or eliminating spurious broadcast " "traffic to the tunnel endpoints. To reserve a unique group for each possible " "(24-bit) VNI, use a /8 such as 239.0.0.0/8. This setting must be the same on " "all the agents." msgstr "" "VXLAN 介é¢çš„å¤šé‡æ’­é€ç¾¤çµ„。å¯ä»¥ä½¿ç”¨ CIDR 表示法來指定群組ä½å€çš„範åœã€‚指定一個" "ç¯„åœæœƒå®¹è¨±ä¸åŒçš„ VNI 使用ä¸åŒçš„群組ä½å€ï¼Œä»¥æ¸›å°‘或刪除傳é€è‡³é€šé“端點的虛å‡å»£æ’­" "資料æµé‡ã€‚如果è¦ç‚ºæ¯ä¸€å€‹å¯èƒ½çš„(24 ä½å…ƒï¼‰VNI ä¿ç•™ä¸€å€‹å”¯ä¸€ç¾¤çµ„,請使用 /8(例" "如 239.0.0.0/8)。在所有代ç†ç¨‹å¼ä¸Šï¼Œæ­¤è¨­å®šå¿…須相åŒã€‚" #, python-format msgid "Multiple default providers for service %s" msgstr "æœå‹™ %s 的多個é è¨­æä¾›è€…" #, python-format msgid "Multiple plugins for service %s were configured" msgstr "已給æœå‹™ %s é…置多個外掛程å¼" #, python-format msgid "Multiple providers specified for service %s" msgstr "給æœå‹™ %s 指定了多個æä¾›è€…" msgid "Multiple tenant_ids in bulk security group rule create not allowed" msgstr "ä¸å®¹è¨±ä¸»é«”安全群組è¦å‰‡å»ºç«‹ä½œæ¥­ä¸­å­˜åœ¨å¤šå€‹ tenant_id" msgid "Must also specify protocol if port range is given." msgstr "如果給定了埠範åœï¼Œå‰‡ä¹Ÿå¿…須指定通訊å”定。" msgid "Must specify one or more actions on flow addition or modification" msgstr "å¿…é ˆå°æµç¨‹æ–°å¢žä½œæ¥­æˆ–修改作業指定一個以上的動作" msgid "Name of Open vSwitch bridge to use" msgstr "è¦ä½¿ç”¨çš„ Open vSwitch 橋接器å稱" msgid "" "Name of nova region to use. Useful if keystone manages more than one region." msgstr "è¦ä½¿ç”¨çš„ Nova å€åŸŸå稱。如果 Keystone 管ç†å¤šå€‹å€åŸŸï¼Œå‰‡å¾ˆæœ‰ç”¨ã€‚" msgid "Namespace of the router" msgstr "路由器å稱空間" msgid "Native pagination depend on native sorting" msgstr "原生分é ç›¸ä¾æ–¼åŽŸç”ŸæŽ’åº" #, python-format msgid "" "Need to apply migrations from %(project)s contract branch. This will require " "all Neutron server instances to be shutdown before proceeding with the " "upgrade." msgstr "" "需è¦å¥—用來自 %(project)s åˆç´„分支的移轉。這將需è¦å…ˆé—œé–‰æ‰€æœ‰ Neutron 伺æœå™¨å¯¦" "例,然後å†ç¹¼çºŒå‡ç´šã€‚" msgid "Negative delta (downgrade) not supported" msgstr "䏿”¯æ´è² æ•¸å·®ç•°ï¼ˆé™ç´šï¼‰" msgid "Negative relative revision (downgrade) not supported" msgstr "䏿”¯æ´è² é¢çš„相å°ä¿®è¨‚(é™ç´šï¼‰" #, python-format msgid "Network %s does not contain any IPv4 subnet" msgstr "網路 %s ä¸åŒ…å«ä»»ä½• IPv4 å­ç¶²è·¯" #, python-format msgid "Network %s is not a valid external network" msgstr "網路 %s 䏿˜¯æœ‰æ•ˆçš„外部網路" #, python-format msgid "Network %s is not an external network" msgstr "網路 %s 䏿˜¯å¤–部網路" #, python-format msgid "" "Network of size %(size)s, from IP range %(parent_range)s excluding IP ranges " "%(excluded_ranges)s was not found." msgstr "" "在 IP ç¯„åœ %(parent_range)s(排除 IP ç¯„åœ %(excluded_ranges)s)中找ä¸åˆ°å¤§å°" "為 %(size)s 的網路。" #, python-format msgid "Network type value '%s' not supported" msgstr "䏿”¯æ´ç¶²è·¯é¡žåž‹å€¼ '%s'" msgid "Network type value needed by the ML2 plugin" msgstr "ML2 外掛程å¼éœ€è¦çš„網路類型值" msgid "Network types supported by the agent (gre and/or vxlan)." msgstr "代ç†ç¨‹å¼æ”¯æ´çš„網路類型(GRE åŠ/或 VXLAN)。" msgid "Neutron Service Type Management" msgstr "Neutron æœå‹™é¡žåž‹ç®¡ç†" msgid "Neutron core_plugin not configured!" msgstr "未é…ç½® Neutron core_pluginï¼" msgid "No default router:external network" msgstr "沒有é è¨­ router:external 網路" #, python-format msgid "No default subnetpool found for IPv%s" msgstr "找ä¸åˆ° IPv%s çš„é è¨­å­ç¶²è·¯å„²å­˜å€" msgid "No default subnetpools defined" msgstr "未定義é è¨­å­ç¶²è·¯å„²å­˜å€" #, python-format msgid "No eligible l3 agent associated with external network %s found" msgstr "找ä¸åˆ°èˆ‡å¤–部網路 %s 相關è¯çš„é©ç”¨ L3 代ç†ç¨‹å¼" #, python-format msgid "No more IP addresses available for subnet %(subnet_id)s." msgstr "沒有其他 IP ä½å€å¯ç”¨æ–¼å­ç¶²è·¯ %(subnet_id)s。" msgid "No offline migrations pending." msgstr "沒有擱置中的離線移轉。" #, python-format msgid "No shared key in %s fields" msgstr "%s 欄ä½ä¸­æ²’有共用金鑰" msgid "Not allowed to manually assign a router to an agent in 'dvr' mode." msgstr "ä¸å®¹è¨±å°‡è·¯ç”±å™¨æ‰‹å‹•指派給處於 'dvr' 模å¼çš„代ç†ç¨‹å¼ã€‚" msgid "Not allowed to manually remove a router from an agent in 'dvr' mode." msgstr "ä¸å®¹è¨±å¾žè™•æ–¼ 'dvr' 模å¼çš„代ç†ç¨‹å¼ä¸­æ‰‹å‹•移除路由器。" msgid "" "Number of DHCP agents scheduled to host a tenant network. If this number is " "greater than 1, the scheduler automatically assigns multiple DHCP agents for " "a given tenant network, providing high availability for DHCP service." msgstr "" "å·²æŽ’ç¨‹ç®¡ç†æ‰¿ç§Ÿäººç¶²è·¯çš„ DHCP 代ç†ç¨‹å¼æ•¸ç›®ã€‚如果此數目大於 1,則排程器會自動為" "給定的承租人網路指派多個 DHCP 代ç†ç¨‹å¼ï¼Œç‚º DHCP æœå‹™æä¾›é«˜å¯ç”¨æ€§ã€‚" msgid "Number of backlog requests to configure the metadata server socket with" msgstr "è¦é…置給 meta 資料伺æœå™¨ Socket çš„å¾…è¾¦äº‹é …è¦æ±‚數目" msgid "Number of backlog requests to configure the socket with" msgstr "è¦é…置給 Socket çš„å¾…è¾¦äº‹é …è¦æ±‚數目" msgid "" "Number of bits in an ipv4 PTR zone that will be considered network prefix. " "It has to align to byte boundary. Minimum value is 8. Maximum value is 24. " "As a consequence, range of values is 8, 16 and 24" msgstr "" "將被視為網路字首之 IPv4 PTR å€åŸŸä¸­çš„ä½å…ƒæ•¸ç›®ã€‚它必須與ä½å…ƒçµ„界é™å°é½Šã€‚下é™å€¼" "為 8。上é™å€¼ç‚º 24ã€‚å› æ­¤ï¼Œå€¼çš„ç¯„åœæ˜¯ 8ã€16 å’Œ 24" msgid "" "Number of bits in an ipv6 PTR zone that will be considered network prefix. " "It has to align to nyble boundary. Minimum value is 4. Maximum value is 124. " "As a consequence, range of values is 4, 8, 12, 16,..., 124" msgstr "" "將被視為網路字首之 IPv6 PTR å€åŸŸä¸­çš„ä½å…ƒæ•¸ç›®ã€‚它必須與 nyble 界é™å°é½Šã€‚下é™å€¼" "為 4。上é™å€¼ç‚º 124ã€‚å› æ­¤ï¼Œå€¼çš„ç¯„åœæ˜¯ 4ã€8ã€12ã€16ã€...ã€124" msgid "" "Number of floating IPs allowed per tenant. A negative value means unlimited." msgstr "æ¯å€‹æ‰¿ç§Ÿäººæ‰€å®¹è¨±çš„æµ®å‹• IP 數目。負數值表示無é™åˆ¶ã€‚" msgid "" "Number of networks allowed per tenant. A negative value means unlimited." msgstr "æ¯å€‹æ‰¿ç§Ÿäººæ‰€å®¹è¨±çš„網路數目。負數值表示無é™åˆ¶ã€‚" msgid "Number of ports allowed per tenant. A negative value means unlimited." msgstr "æ¯å€‹æ‰¿ç§Ÿäººæ‰€å®¹è¨±çš„埠數目。負數值表示無é™åˆ¶ã€‚" msgid "Number of routers allowed per tenant. A negative value means unlimited." msgstr "æ¯å€‹æ‰¿ç§Ÿäººæ‰€å®¹è¨±çš„路由器數目。負數值表示無é™åˆ¶ã€‚" msgid "" "Number of seconds between sending events to nova if there are any events to " "send." msgstr "兩次將事件傳é€è‡³ Nova 之間的秒數(如果有任何事件è¦å‚³é€ï¼‰ã€‚" msgid "Number of seconds to keep retrying to listen" msgstr "䏿–·é‡è©¦æŽ¥è½çš„秒數" msgid "" "Number of security groups allowed per tenant. A negative value means " "unlimited." msgstr "æ¯å€‹æ‰¿ç§Ÿäººæ‰€å®¹è¨±çš„安全群組數目。負數值表示無é™åˆ¶ã€‚" msgid "" "Number of security rules allowed per tenant. A negative value means " "unlimited." msgstr "æ¯å€‹æ‰¿ç§Ÿäººæ‰€å®¹è¨±çš„安全è¦å‰‡æ•¸ç›®ã€‚負數值表示無é™åˆ¶ã€‚" msgid "" "Number of separate API worker processes for service. If not specified, the " "default is equal to the number of CPUs available for best performance." msgstr "" "é©ç”¨æ–¼æœå‹™çš„ç¨ç«‹ API 工作者處ç†ç¨‹åºæ•¸ç›®ã€‚如果未指定,則é è¨­å€¼ç‚ºå¯ç”¨æ–¼æœ€ä½³æ•ˆèƒ½" "çš„ CPU 數目。" msgid "" "Number of separate worker processes for metadata server (defaults to half of " "the number of CPUs)" msgstr "meta 資料伺æœå™¨çš„ç¨ç«‹å·¥ä½œè€…處ç†ç¨‹åºæ•¸ç›®ï¼ˆé è¨­ç‚ºCPU 數目的一åŠï¼‰" msgid "Number of subnets allowed per tenant, A negative value means unlimited." msgstr "æ¯å€‹æ‰¿ç§Ÿäººæ‰€å®¹è¨±çš„å­ç¶²è·¯æ•¸ç›®ã€‚負數值表示無é™åˆ¶ã€‚" msgid "" "Number of threads to use during sync process. Should not exceed connection " "pool size configured on server." msgstr "" "åŸ·è¡ŒåŒæ­¥ç¨‹åºæœŸé–“è¦ä½¿ç”¨çš„åŸ·è¡Œç·’æ•¸ç›®ã€‚ä¸æ‡‰è¶…éŽä¼ºæœå™¨ä¸Šé…置的連線儲存å€å¤§å°ã€‚" msgid "OK" msgstr "確定" msgid "" "OVS datapath to use. 'system' is the default value and corresponds to the " "kernel datapath. To enable the userspace datapath set this value to 'netdev'." msgstr "" "è¦ä½¿ç”¨çš„ OVS 資料路徑。'system' 是é è¨­å€¼ï¼Œä¸”èˆ‡æ ¸å¿ƒè³‡æ–™è·¯å¾‘å°æ‡‰ã€‚如果è¦å•Ÿç”¨ä½¿" "用者空間資料路徑,請將此值設為 'netdev'。" msgid "OVS vhost-user socket directory." msgstr "OVS vhost 使用者 Socket 目錄。" #, python-format msgid "Object action %(action)s failed because: %(reason)s." msgstr "物件動作 %(action)s 失敗,原因:%(reason)s。" msgid "Only admin can view or configure quota" msgstr "åªæœ‰ç®¡ç†è€…æ‰èƒ½æª¢è¦–或é…ç½®é…é¡" msgid "Only admin is authorized to access quotas for another tenant" msgstr "åªæœ‰ç®¡ç†è€…æ‰ç²æŽˆæ¬Šä¾†å­˜å–å¦ä¸€å€‹æ‰¿ç§Ÿäººçš„é…é¡" msgid "Only admins can manipulate policies on objects they do not own" msgstr "åªæœ‰ç®¡ç†è€…æ‰å¯ä»¥æ“ä½œä¸æ­¸ä»–å€‘æ“æœ‰ä¹‹ç‰©ä»¶ä¸Šçš„原則" msgid "Only allowed to update rules for one security profile at a time" msgstr "一次åªå®¹è¨±æ›´æ–°ä¸€å€‹å®‰å…¨è¨­å®šæª”çš„è¦å‰‡" msgid "Only remote_ip_prefix or remote_group_id may be provided." msgstr "åªèƒ½æä¾› remote_ip_prefix 或 remote_group_id。" msgid "OpenFlow interface to use." msgstr "è¦ä½¿ç”¨çš„ OpenFlow 介é¢ã€‚" #, python-format msgid "" "Operation %(op)s is not supported for device_owner %(device_owner)s on port " "%(port_id)s." msgstr "埠 %(port_id)s 上的è£ç½®æ“有者 %(device_owner)s 䏿”¯æ´ä½œæ¥­ %(op)s。" #, python-format msgid "Operation not supported on device %(dev_name)s" msgstr "作業在è£ç½® %(dev_name)s 上ä¸å—支æ´" msgid "" "Ordered list of network_types to allocate as tenant networks. The default " "value 'local' is useful for single-box testing but provides no connectivity " "between hosts." msgstr "" "è¦é…置作為租戶網路的 network_type æœ‰åºæ¸…單。é è¨­å€¼ 'local' å°å–®æ¡†æ¸¬è©¦å¾ˆæœ‰ç”¨ï¼Œ" "但å»ä¸æä¾›ä¸»æ©Ÿä¹‹é–“的連線功能。" msgid "Override the default dnsmasq settings with this file." msgstr "使用此檔案來置æ›é è¨­ dnsmasq 設定。" msgid "Owner type of the device: network/compute" msgstr "è£ç½®çš„æ“æœ‰è€…é¡žåž‹ï¼šç¶²è·¯/計算" msgid "POST requests are not supported on this resource." msgstr "此資æºä¸Šä¸æ”¯æ´ POST è¦æ±‚。" #, python-format msgid "Package %s not installed" msgstr "未安è£å¥—ä»¶ %s" #, python-format msgid "Parsing bridge_mappings failed: %s." msgstr "å‰–æž bridge_mappings 時失敗:%s。" msgid "Password for connecting to designate in admin context" msgstr "用於連接以在管ç†ç’°å¢ƒå®šç¾©ä¸­æŒ‡å®šçš„密碼" msgid "Path to PID file for this process" msgstr "用於此程åºçš„ PID 檔案路徑" msgid "Path to the router directory" msgstr "路由器目錄的路徑" msgid "Peer patch port in integration bridge for tunnel bridge." msgstr "æ•´åˆæ©‹æŽ¥å™¨ä¸­ç”¨æ–¼é€šé“橋接器的åŒå±¤ç´šä¿®è£œç¨‹å¼åŸ ã€‚" msgid "Peer patch port in tunnel bridge for integration bridge." msgstr "é€šé“æ©‹æŽ¥å™¨ä¸­ç”¨æ–¼æ•´åˆæ©‹æŽ¥å™¨çš„åŒå±¤ç´šä¿®è£œç¨‹å¼åŸ ã€‚" msgid "Per-tenant subnet pool prefix quota exceeded." msgstr "已超出æ¯å€‹ç§Ÿæˆ¶çš„å­ç¶²è·¯å„²å­˜å€å­—首é…é¡ã€‚" msgid "Phase upgrade options do not accept revision specification" msgstr "階段å‡ç´šé¸é …䏿ޥå—ä¿®è¨‚è¦æ ¼" msgid "Ping timeout" msgstr "連通測試逾時值" msgid "Plugin does not support updating provider attributes" msgstr "外掛程å¼ä¸æ”¯æ´æ›´æ–°æä¾›è€…屬性" #, python-format msgid "Port %(id)s does not have fixed ip %(address)s" msgstr "埠 %(id)s 沒有固定 IP %(address)s" #, python-format msgid "Port %(port_id)s is already acquired by another DHCP agent" msgstr "å¦ä¸€å€‹ DHCP 代ç†ç¨‹å¼å·²ç¶“ç²å¾—了埠 %(port_id)s" #, python-format msgid "" "Port %s has multiple fixed IPv4 addresses. Must provide a specific IPv4 " "address when assigning a floating IP" msgstr "埠 %s 具有多個固定 IPv4 ä½å€ã€‚指派浮動 IP 時,必須æä¾›ç‰¹å®šçš„ IPv4 ä½å€" msgid "" "Port to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "ç”¨æ–¼æŽ¥è½ OpenFlow 連線的埠。僅用於 'native' 驅動程å¼ã€‚" #, python-format msgid "Prefix '%(prefix)s' not supported in IPv%(version)s pool." msgstr "字首 '%(prefix)s' 在 IPv%(version)s 儲存å€ä¸­ä¸å—支æ´ã€‚" msgid "Prefix Delegation can only be used with IPv6 subnets." msgstr "字首委派åªèƒ½èˆ‡ IPv6 å­ç¶²è·¯æ­é…使用。" msgid "Private key of client certificate." msgstr "用戶端憑證的ç§å¯†é‡‘鑰。" #, python-format msgid "Probe %s deleted" msgstr "å·²åˆªé™¤æŽ¢é‡ %s" #, python-format msgid "Probe created : %s " msgstr "已建立探é‡ï¼š%s " msgid "Process is already started" msgstr "程åºå·²å•Ÿå‹•" msgid "Process is not running." msgstr "程åºä¸åœ¨åŸ·è¡Œä¸­ã€‚" msgid "Protocol to access nova metadata, http or https" msgstr "ç”¨æ–¼å­˜å– Nova meta 資料的通訊å”定:HTTP 或 HTTPS" #, python-format msgid "Provider name %(name)s is limited by %(len)s characters" msgstr "æä¾›è€…å稱 %(name)s 最多åªèƒ½åŒ…å« %(len)s 個字元" #, python-format msgid "QoS Policy %(policy_id)s is used by %(object_type)s %(object_id)s." msgstr "æœå‹™å“質原則 %(policy_id)s ç”± %(object_type)s %(object_id)s 使用。" #, python-format msgid "" "QoS binding for network %(net_id)s and policy %(policy_id)s could not be " "found." msgstr "找ä¸åˆ°ç¶²è·¯ %(net_id)s 和原則 %(policy_id)s çš„æœå‹™å“質連çµã€‚" #, python-format msgid "" "QoS binding for port %(port_id)s and policy %(policy_id)s could not be found." msgstr "找ä¸åˆ°åŸ  %(port_id)s 和原則 %(policy_id)s çš„æœå‹™å“質連çµã€‚" #, python-format msgid "QoS policy %(policy_id)s could not be found." msgstr "找ä¸åˆ°æœå‹™å“質原則 %(policy_id)s。" #, python-format msgid "QoS rule %(rule_id)s for policy %(policy_id)s could not be found." msgstr "找ä¸åˆ°åŽŸå‰‡ %(policy_id)s çš„æœå‹™å“質è¦å‰‡ %(rule_id)s。" #, python-format msgid "RBAC policy of type %(object_type)s with ID %(id)s not found" msgstr "找ä¸åˆ° ID 為 %(id)s 且類型為 %(object_type)s çš„ RBAC 原則" #, python-format msgid "" "RBAC policy on object %(object_id)s cannot be removed because other objects " "depend on it.\n" "Details: %(details)s" msgstr "" "無法移除物件 %(object_id)s 上的 RBAC åŽŸå‰‡ï¼Œå› ç‚ºå…¶ä»–ç‰©ä»¶ç›¸ä¾æ–¼è©²åŽŸå‰‡ã€‚\n" "詳細資料:%(details)s" msgid "" "Range of seconds to randomly delay when starting the periodic task scheduler " "to reduce stampeding. (Disable by setting to 0)" msgstr "" "啟動定期作業排定器以減少大混亂的隨機延é²ç§’數範åœã€‚(如果è¦åœç”¨ï¼Œå‰‡è¨­ç‚º 0)" msgid "Ranges must be in the same IP version" msgstr "範åœå¿…é ˆä½æ–¼ç›¸åŒçš„ IP 版本中" msgid "Ranges must be netaddr.IPRange" msgstr "範åœå¿…須是 netaddr.IPRange" msgid "Ranges must not overlap" msgstr "範åœä¸å¾—é‡ç–Š" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.EUI type." msgstr "接收到類型 '%(type)s' 和值 '%(value)s'ã€‚é æœŸç‚º netaddr.EUI 類型。" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPAddress " "type." msgstr "" "接收到類型 '%(type)s' 和值 '%(value)s'ã€‚é æœŸç‚º netaddr.IPAddress 類型。" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPNetwork " "type." msgstr "" "接收到類型 '%(type)s' 和值 '%(value)s'ã€‚é æœŸç‚º netaddr.IPNetwork 類型。" #, python-format msgid "" "Release aware branch labels (%s) are deprecated. Please switch to expand@ " "and contract@ labels." msgstr "版本相關分支標籤 (%s) å·²é­åˆ°æ·˜æ±°ã€‚請切æ›è‡³ expand@ å’Œ contract@ 標籤。" msgid "Remote metadata server experienced an internal server error." msgstr "é ç«¯ meta 資料伺æœå™¨ç™¼ç”Ÿå…§éƒ¨ä¼ºæœå™¨éŒ¯èª¤ã€‚" msgid "" "Repository does not contain HEAD files for contract and expand branches." msgstr "儲存庫ä¸å«åˆç´„åŠå»¶ä¼¸åˆ†æ”¯çš„ HEAD 檔。" msgid "" "Representing the resource type whose load is being reported by the agent. " "This can be \"networks\", \"subnets\" or \"ports\". When specified (Default " "is networks), the server will extract particular load sent as part of its " "agent configuration object from the agent report state, which is the number " "of resources being consumed, at every report_interval.dhcp_load_type can be " "used in combination with network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler When the network_scheduler_driver is " "WeightScheduler, dhcp_load_type can be configured to represent the choice " "for the resource being balanced. Example: dhcp_load_type=networks" msgstr "" "代表將由代ç†ç¨‹å¼å ±å‘Šå…¶è² è¼‰çš„資æºé¡žåž‹ã€‚它å¯ä»¥ç‚ºã€Œç¶²è·¯ã€ã€ã€Œå­ç¶²è·¯ã€æˆ–「埠ã€ã€‚" "指定時(é è¨­å€¼ç‚ºç¶²è·¯ï¼‰ï¼Œä¼ºæœå™¨å°‡å¾žä»£ç†ç¨‹å¼å ±å‘Šç‹€æ…‹ï¼ˆç‚ºæ‰€è€—ç”¨çš„è³‡æºæ•¸ç›®ï¼‰æ“·å–" "作為其代ç†ç¨‹å¼é…置物件一部分傳é€çš„特定負載,擷å–間隔為 report_interval." "dhcp_load_type å¯ä»¥èˆ‡network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler 組åˆä½¿ç”¨ã€‚ç•¶ network_scheduler_driver " "為 WeightScheduler 時,å¯ä»¥å°‡ dhcp_load_typeé…ç½®ç‚ºä»£è¡¨æ‚¨é¸æ“‡è¦é€²è¡Œå¹³è¡¡çš„資" "æºã€‚範例:dhcp_load_type=網路" msgid "Request Failed: internal server error while processing your request." msgstr "è¦æ±‚失敗:處ç†è¦æ±‚時發生內部伺æœå™¨éŒ¯èª¤ã€‚" msgid "" "Reset flow table on start. Setting this to True will cause brief traffic " "interruption." msgstr "" "在啟動時é‡è¨­æµç¨‹è¡¨æ ¼ã€‚如果將此項設定為 True,則將導致簡短的資料æµé‡å²”斷。" #, python-format msgid "Resource %(resource)s %(resource_id)s could not be found." msgstr "找ä¸åˆ°è³‡æº %(resource)s %(resource_id)s。" #, python-format msgid "Resource %(resource_id)s of type %(resource_type)s not found" msgstr "找ä¸åˆ°é¡žåž‹ç‚º %(resource_type)s çš„è³‡æº %(resource_id)s" #, python-format msgid "" "Resource '%(resource_id)s' is already associated with provider " "'%(provider)s' for service type '%(service_type)s'" msgstr "" "è³‡æº '%(resource_id)s' 已與æœå‹™é¡žåž‹ '%(service_type)s' çš„æä¾›è€… " "'%(provider)s' 產生關è¯" msgid "Resource body required" msgstr "需è¦è³‡æºä¸»é«”" msgid "Resource not found." msgstr "找ä¸åˆ°è³‡æºã€‚" msgid "Resources required" msgstr "需è¦è³‡æº" msgid "" "Root helper application. Use 'sudo neutron-rootwrap /etc/neutron/rootwrap." "conf' to use the real root filter facility. Change to 'sudo' to skip the " "filtering and just run the command directly." msgstr "" "æ ¹èªªæ˜Žç¨‹å¼æ‡‰ç”¨ç¨‹å¼ã€‚利用 'sudo neutron-rootwrap /etc/neutron/rootwrap.conf' " "ä¾†ä½¿ç”¨å¯¦éš›çš„æ ¹éŽæ¿¾å™¨å·¥å…·ã€‚變更為 'sudo' 以跳éŽéŽæ¿¾ä¸¦ä¸”僅直接執行該指令。" msgid "Root permissions are required to drop privileges." msgstr "éœ€è¦ root æ¬Šé™æ‰èƒ½æ¨æ£„專用權。" #, python-format msgid "Router '%(router_id)s' is not compatible with this agent." msgstr "路由器 '%(router_id)s' 與此代ç†ç¨‹å¼ä¸ç›¸å®¹ã€‚" #, python-format msgid "Router already has a port on subnet %s" msgstr "路由器在å­ç¶²è·¯ %s 上已經有埠" msgid "Router port must have at least one fixed IP" msgstr "路由器埠必須具有至少一個固定 IP" #, python-format msgid "Running %(cmd)s (%(desc)s) for %(project)s ..." msgstr "æ­£åœ¨å° %(project)s 執行 %(cmd)s (%(desc)s)..." #, python-format msgid "Running %(cmd)s for %(project)s ..." msgstr "æ­£åœ¨å° %(project)s 執行 %(cmd)s..." msgid "" "Seconds between nodes reporting state to server; should be less than " "agent_down_time, best if it is half or less than agent_down_time." msgstr "" "兩個節點將狀態報告給伺æœå™¨çš„é–“éš”ç§’æ•¸ï¼›æ‡‰è©²å°æ–¼ agent_down_time;如果是 " "agent_down_time çš„ä¸€åŠæˆ–è€…å°æ–¼ agent_down_time,則最佳。" msgid "" "Seconds to regard the agent is down; should be at least twice " "report_interval, to be sure the agent is down for good." msgstr "" "將代ç†ç¨‹å¼è¦–為已關閉的秒數;應該至少是report_interval 的兩å€ï¼Œä»¥ç¢ºä¿ä»£ç†ç¨‹å¼" "已永久關閉。" #, python-format msgid "Security Group %(id)s %(reason)s." msgstr "安全群組 %(id)s %(reason)s。" #, python-format msgid "Security Group Rule %(id)s %(reason)s." msgstr "安全群組è¦å‰‡ %(id)s %(reason)s。" #, python-format msgid "Security group %(id)s does not exist" msgstr "安全群組 %(id)s ä¸å­˜åœ¨" #, python-format msgid "Security group rule %(id)s does not exist" msgstr "安全群組è¦å‰‡ %(id)s ä¸å­˜åœ¨" #, python-format msgid "Security group rule already exists. Rule id is %(rule_id)s." msgstr "安全群組è¦å‰‡å·²ç¶“存在。è¦å‰‡ ID 為 %(rule_id)s。" #, python-format msgid "" "Security group rule for ethertype '%(ethertype)s' not supported. Allowed " "values are %(values)s." msgstr "" "䏿”¯æ´ä¹™å¤ªç¶²è·¯é¡žåž‹ '%(ethertype)s' 的安全群組è¦å‰‡ã€‚容許的值為 %(values)s。" #, python-format msgid "" "Security group rule protocol %(protocol)s not supported. Only protocol " "values %(values)s and integer representations [0 to 255] are supported." msgstr "" "䏿”¯æ´å®‰å…¨ç¾¤çµ„è¦å‰‡é€šè¨Šå”定 %(protocol)s。僅支æ´é€šè¨Šå”定值 %(values)s 和整數表" "示法 [0 到 255]。" msgid "Segments and provider values cannot both be set." msgstr "ç„¡æ³•åŒæ™‚è¨­å®šå€æ®µåŠæä¾›è€…å€¼ã€‚" msgid "Selects the Agent Type reported" msgstr "é¸å–報告的代ç†ç¨‹å¼é¡žåž‹" msgid "" "Send notification to nova when port data (fixed_ips/floatingip) changes so " "nova can update its cache." msgstr "" "埠資料 (fixed_ips/floatingip) 變更時,將通知傳é€è‡³ Nova,以便 Nova å¯ä»¥æ›´æ–°å…¶" "å¿«å–。" msgid "Send notification to nova when port status changes" msgstr "埠狀態變更時,將通知傳é€è‡³ Nova" #, python-format msgid "" "Service provider '%(provider)s' could not be found for service type " "%(service_type)s" msgstr "找ä¸åˆ°æœå‹™é¡žåž‹ %(service_type)s çš„æœå‹™æä¾›è€… '%(provider)s'" msgid "Service to handle DHCPv6 Prefix delegation." msgstr "ç”¨æ–¼è™•ç† DHCPv6 字首委派的æœå‹™ã€‚" #, python-format msgid "Service type %(service_type)s does not have a default service provider" msgstr "æœå‹™é¡žåž‹ %(service_type)s ä¸å…·æœ‰é è¨­æœå‹™æä¾›è€…" msgid "" "Set new timeout in seconds for new rpc calls after agent receives SIGTERM. " "If value is set to 0, rpc timeout won't be changed" msgstr "" "在代ç†ç¨‹å¼æŽ¥æ”¶ SIGTERM 之後為新 RPC 呼å«è¨­å®šæ–°é€¾æ™‚(以秒為單ä½ï¼‰ã€‚如果值設定" "為 0,RPC é€¾æ™‚å°‡ä¸æœƒè®Šæ›´" msgid "" "Set or un-set the don't fragment (DF) bit on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "在帶有 GRE/VXLAN 通é“çš„é€å‡º IP å°åŒ…ä¸Šï¼Œè¨­å®šæˆ–å–æ¶ˆè¨­å®šã€Œä¸åŠƒåˆ†ç‰‡æ®µ (DF)ã€ä½" "元。" msgid "" "Set or un-set the tunnel header checksum on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "在é€å‡ºçš„ IP å°åŒ…(攜帶 GRE/VXLAN 通é“ï¼‰ä¸Šï¼Œè¨­å®šæˆ–å–æ¶ˆè¨­å®šé€šé“標頭總和檢查。" msgid "Shared address scope can't be unshared" msgstr "無法將已共用的ä½å€ç¯„åœå–消共用" msgid "String prefix used to match IPset names." msgstr "ç”¨ä¾†ç¬¦åˆ IPset å稱的字串字首。" #, python-format msgid "Sub-project %s not installed." msgstr "未安è£å­å°ˆæ¡ˆ %s。" msgid "Subnet for router interface must have a gateway IP" msgstr "路由器介é¢çš„å­ç¶²è·¯å¿…é ˆå…·æœ‰é–˜é“ IP" #, python-format msgid "Subnet pool %(subnetpool_id)s could not be found." msgstr "找ä¸åˆ°å­ç¶²è·¯å„²å­˜å€ %(subnetpool_id)s。" msgid "Subnet pool has existing allocations" msgstr "å­ç¶²è·¯å„²å­˜å€å…·æœ‰ç¾æœ‰çš„é…ç½®" msgid "Subnet used for the l3 HA admin network." msgstr "用於 l3 HA 管ç†ç¶²è·¯çš„å­ç¶²è·¯ã€‚" msgid "" "Subnets hosted on the same network must be allocated from the same subnet " "pool." msgstr "在åŒä¸€ç¶²è·¯ä¸Šç®¡ç†çš„å­ç¶²è·¯å¿…須從åŒä¸€å­ç¶²è·¯å„²å­˜å€ä¸­é€²è¡Œé…置。" msgid "" "System-wide flag to determine the type of router that tenants can create. " "Only admin can override." msgstr "æ­¤ç³»çµ±å±¤é¢æ——標用來決定承租人å¯ä»¥å»ºç«‹çš„è·¯ç”±å™¨é¡žåž‹ã€‚åªæœ‰ç®¡ç†è€…æ‰èƒ½ç½®æ›ã€‚" msgid "TCP Port used by Neutron metadata namespace proxy." msgstr "Neutron meta 資料å稱空間 Proxy 所使用的 TCP 埠。" msgid "TCP Port used by Nova metadata server." msgstr "Nova meta 資料伺æœå™¨æ‰€ä½¿ç”¨çš„ TCP 埠。" msgid "TTL for vxlan interface protocol packets." msgstr "VXLAN 介é¢é€šè¨Šå”定å°åŒ…çš„ TTL。" #, python-format msgid "Tag %(tag)s could not be found." msgstr "找ä¸åˆ°æ¨™è¨˜ %(tag)s。" #, python-format msgid "Tenant %(tenant_id)s not allowed to create %(resource)s on this network" msgstr "Tenant %(tenant_id)s ä¸å¯åœ¨æ­¤ç¶²è·¯ä¸Šå»ºç«‹ %(resource)s" msgid "Tenant id for connecting to designate in admin context" msgstr "用於連接以在管ç†ç’°å¢ƒå®šç¾©ä¸­æŒ‡å®šçš„租戶 ID" msgid "Tenant name for connecting to designate in admin context" msgstr "用於連接以在管ç†ç’°å¢ƒå®šç¾©ä¸­æŒ‡å®šçš„租戶å稱" msgid "Tenant network creation is not enabled." msgstr "未啟用 Tenant 網路建立作業。" msgid "Tenant-id was missing from quota request." msgstr "é…é¡è¦æ±‚ä¸­éºæ¼äº† Tenant-id。" msgid "" "The 'gateway_external_network_id' option must be configured for this agent " "as Neutron has more than one external network." msgstr "" "必須為此代ç†ç¨‹å¼é…ç½® 'gateway_external_network_id' é¸é …,因為 Neutron 具有多" "個外部網路。" msgid "" "The DHCP agent will resync its state with Neutron to recover from any " "transient notification or RPC errors. The interval is number of seconds " "between attempts." msgstr "" "DHCP 代ç†ç¨‹å¼æœƒå°‡è‡ªå·±çš„狀態與 Neutron é€²è¡ŒåŒæ­¥ï¼Œä»¥å¾žä»»ä½•暫時性通知或 RPC 錯誤" "進行回復。間隔為兩次嘗試之間的秒數。" msgid "" "The DHCP server can assist with providing metadata support on isolated " "networks. Setting this value to True will cause the DHCP server to append " "specific host routes to the DHCP request. The metadata service will only be " "activated when the subnet does not contain any router port. The guest " "instance must be configured to request host routes via DHCP (Option 121). " "This option doesn't have any effect when force_metadata is set to True." msgstr "" "DHCP 伺æœå™¨å¯é€éŽåœ¨éš”離網路上æä¾› meta 資料支æ´é€²è¡Œå”助。將此值設為 True 會導" "致 DHCP 伺æœå™¨å°‡ç‰¹å®šçš„主機路線附加至 DHCP è¦æ±‚。僅當å­ç¶²è·¯ä¸åŒ…å«ä»»ä½•路由器埠" "時,æ‰å•Ÿå‹• meta 資料æœå‹™ã€‚訪客實例必須é…ç½®æˆé€éŽ DHCP ä¾†è¦æ±‚主機路線(é¸é … " "121)。將 force_metadata 設為 True 時,這個é¸é …沒有任何效果。" msgid "The UDP port to use for VXLAN tunnels." msgstr "è¦ç”¨æ–¼ VXLAN 通é“çš„ UDP 埠。" #, python-format msgid "" "The address allocation request could not be satisfied because: %(reason)s" msgstr "無法滿足ä½å€é…ç½®è¦æ±‚,原因:%(reason)s" msgid "The advertisement interval in seconds" msgstr "廣告間隔(以秒為單ä½ï¼‰" #, python-format msgid "The allocation pool %(pool)s is not valid." msgstr "é…ç½®å„²å­˜å€ %(pool)s 無效。" #, python-format msgid "" "The allocation pool %(pool)s spans beyond the subnet cidr %(subnet_cidr)s." msgstr "é…ç½®å„²å­˜å€ %(pool)s 跨越了å­ç¶²è·¯ CIDR %(subnet_cidr)s。" msgid "" "The base MAC address Neutron will use for VIFs. The first 3 octets will " "remain unchanged. If the 4th octet is not 00, it will also be used. The " "others will be randomly generated." msgstr "" "Neutron 將用於 VIF 的基本 MAC ä½å€ã€‚å‰ 3 個八ä½å…ƒçµ„å°‡ä¿æŒä¸è®Šã€‚如果第 4 個八" "ä½å…ƒçµ„䏿˜¯ 00,則也將使用該八ä½å…ƒçµ„。其他å„項將隨機產生。" msgid "" "The base mac address used for unique DVR instances by Neutron. The first 3 " "octets will remain unchanged. If the 4th octet is not 00, it will also be " "used. The others will be randomly generated. The 'dvr_base_mac' *must* be " "different from 'base_mac' to avoid mixing them up with MAC's allocated for " "tenant ports. A 4 octet example would be dvr_base_mac = fa:16:3f:4f:00:00. " "The default is 3 octet" msgstr "" "Neutron 用於唯一 DVR 實例的基本 MAC ä½å€ã€‚å‰ 3 個八ä½å…ƒçµ„å°‡ä¿æŒä¸è®Šã€‚如果第 " "4 個八ä½å…ƒçµ„䏿˜¯ 00,則也將使用該八ä½å…ƒçµ„。其他個項將隨機產" "生。'dvr_base_mac' 必須與 'base_mac' ä¸åŒï¼Œä»¥é¿å…å°‡å®ƒå€‘èˆ‡å°æ‰¿ç§ŸäººåŸ é…置的 " "MAC æ··åˆã€‚4 個八ä½å…ƒçµ„範例如下:dvr_base_mac = fa:16:3f:4f:00:00。é è¨­å€¼ç‚º 3 " "個八ä½å…ƒçµ„" msgid "The core plugin Neutron will use" msgstr "Neutron 將使用的核心外掛程å¼" msgid "The driver used to manage the DHCP server." msgstr "ç”¨æ–¼ç®¡ç† DHCP 伺æœå™¨çš„驅動程å¼ã€‚" msgid "The driver used to manage the virtual interface." msgstr "用於管ç†è™›æ“¬ä»‹é¢çš„驅動程å¼ã€‚" msgid "" "The email address to be used when creating PTR zones. If not specified, the " "email address will be admin@" msgstr "" "建立 PTR å€åŸŸæ™‚,è¦ä½¿ç”¨çš„é›»å­éƒµä»¶ä½å€ã€‚如果未指定,則電å­éƒµä»¶ä½å€å°‡æ˜¯ " "admin@" #, python-format msgid "" "The following device_id %(device_id)s is not owned by your tenant or matches " "another tenants router." msgstr "" "下列 device_id %(device_id)s 䏿˜¯ç”±æ‚¨çš„æ‰¿ç§Ÿäººæ‰€æ“有者,或者與å¦ä¸€å€‹æ‰¿ç§Ÿäººè·¯ç”±" "器相符。" msgid "The interface for interacting with the OVSDB" msgstr "用於與 OVSDB 互動的介é¢" msgid "" "The maximum number of items returned in a single response, value was " "'infinite' or negative integer means no limit" msgstr "在單一回應中傳回的項目數上é™ï¼Œå€¼ç‚º 'infinite' 或負整數時表示無é™åˆ¶" #, python-format msgid "" "The network %(network_id)s has been already hosted by the DHCP Agent " "%(agent_id)s." msgstr "網路 %(network_id)s 已經由 DHCP 代ç†ç¨‹å¼ %(agent_id)s 管ç†ã€‚" #, python-format msgid "" "The network %(network_id)s is not hosted by the DHCP agent %(agent_id)s." msgstr "網路 %(network_id)s 䏿˜¯ç”± DHCP 代ç†ç¨‹å¼ %(agent_id)s 管ç†ã€‚" msgid "" "The network type to use when creating the HA network for an HA router. By " "default or if empty, the first 'tenant_network_types' is used. This is " "helpful when the VRRP traffic should use a specific network which is not the " "default one." msgstr "" "為 HA 路由器建立 HA 網路時è¦ä½¿ç”¨çš„網路類型。ä¾é è¨­æˆ–者在空白的情æ³ä¸‹ï¼Œå°‡ä½¿ç”¨" "第一個 'tenant_network_types'。當 VRRP 資料æµé‡æ‡‰è©²ä½¿ç”¨çš„ç‰¹å®šç¶²è·¯ä¸æ˜¯é è¨­ç¶²è·¯" "時,這很有用。" msgid "" "The number of seconds the agent will wait between polling for local device " "changes." msgstr "輪詢本端è£ç½®è®Šæ›´ä¹‹é–“代ç†ç¨‹å¼å°‡ç­‰å¾…的秒數。" msgid "" "The number of seconds to wait before respawning the ovsdb monitor after " "losing communication with it." msgstr "與 OVSDB ç›£è¦–å™¨çš„é€šè¨Šä¸­æ–·å¾Œé‡æ–°å¤§é‡ç”¢ç”ŸOVSDB 監視器之å‰ç­‰å¾…的秒數。" msgid "The number of sort_keys and sort_dirs must be same" msgstr "sort_key æ•¸ç›®åŠ sort_dir 數目必須相åŒ" msgid "" "The path for API extensions. Note that this can be a colon-separated list of " "paths. For example: api_extensions_path = extensions:/path/to/more/exts:/" "even/more/exts. The __path__ of neutron.extensions is appended to this, so " "if your extensions are in there you don't need to specify them here." msgstr "" "API 延伸的路徑。請注æ„,這å¯ä»¥æ˜¯åˆ†è™Ÿå€éš”的路徑清單。例如:" "api_extensions_path = extensions:/path/to/more/exts:/even/more/exts。已將 " "neutron.extensions çš„ __path__ 附加到此項,所以如果您的延伸在這裡,則ä¸éœ€è¦åœ¨" "這裡指定它們。" msgid "The physical network name with which the HA network can be created." msgstr "建立 HA 網路時å¯ä»¥ä½¿ç”¨çš„實體網路å稱。" #, python-format msgid "The port '%s' was deleted" msgstr "已刪除埠 '%s'" msgid "The port to bind to" msgstr "è¦é€£çµè‡³çš„埠" #, python-format msgid "The requested content type %s is invalid." msgstr "æ‰€è¦æ±‚的內容類型 %s 無效。" msgid "The resource could not be found." msgstr "找ä¸åˆ°è³‡æºã€‚" #, python-format msgid "" "The router %(router_id)s has been already hosted by the L3 Agent " "%(agent_id)s." msgstr "路由器 %(router_id)s 已經由 L3 代ç†ç¨‹å¼ %(agent_id)s 管ç†ã€‚" msgid "" "The server has either erred or is incapable of performing the requested " "operation." msgstr "伺æœå™¨ç™¼ç”ŸéŒ¯èª¤ï¼Œæˆ–è€…ç„¡æ³•åŸ·è¡Œæ‰€è¦æ±‚的作業。" msgid "The service plugins Neutron will use" msgstr "Neutron 將使用的æœå‹™å¤–掛程å¼" #, python-format msgid "The subnet request could not be satisfied because: %(reason)s" msgstr "無法滿足å­ç¶²è·¯è¦æ±‚,原因:%(reason)s" #, python-format msgid "The subproject to execute the command against. Can be one of: '%s'." msgstr "è¦å°å…¶åŸ·è¡ŒæŒ‡ä»¤çš„å­å°ˆæ¡ˆã€‚å¯ä»¥æ˜¯ä¸‹åˆ—其中一個:'%s'。" msgid "The type of authentication to use" msgstr "è¦ä½¿ç”¨çš„鑑別類型" msgid "" "There are routers attached to this network that depend on this policy for " "access." msgstr "有ä¾è³´æ–¼æ­¤å­˜å–原則的路由器已連接至此網路。" msgid "" "Timeout in seconds to wait for a single OpenFlow request. Used only for " "'native' driver." msgstr "" "等待單個 OpenFlow è¦æ±‚的逾時值(以秒為單ä½ï¼‰ã€‚僅用於'native' 驅動程å¼ã€‚" msgid "" "Timeout in seconds to wait for the local switch connecting the controller. " "Used only for 'native' driver." msgstr "" "等待本端交æ›å™¨é€£æŽ¥æŽ§åˆ¶å™¨æ™‚的逾時值(以秒為單ä½ï¼‰ã€‚僅用於 'native' 驅動程å¼ã€‚" msgid "" "Too long prefix provided. New name would exceed given length for an " "interface name." msgstr "所æä¾›çš„字首太長。新å稱將超éŽä»‹é¢å稱的給定長度。" msgid "" "True to delete all ports on all the OpenvSwitch bridges. False to delete " "ports created by Neutron on integration and external network bridges." msgstr "" "如果為 True,則刪除所有 OpenvSwitch 橋接器上的所有埠。如果為 False,則刪除" "Neutron 在整åˆåŠå¤–部網路橋接器上建立的埠。" msgid "Tunnel IP value needed by the ML2 plugin" msgstr "ML2 外掛程å¼éœ€è¦çš„é€šé“ IP 值" msgid "Tunnel bridge to use." msgstr "è¦ä½¿ç”¨çš„é€šé“æ©‹æŽ¥å™¨ã€‚" msgid "" "Type of the nova endpoint to use. This endpoint will be looked up in the " "keystone catalog and should be one of public, internal or admin." msgstr "" "è¦ä½¿ç”¨ä¹‹ Nova 端點的類型。此端點將在 Keystone 型錄予以查閱,並且應該是共用ã€" "內部或管ç†çš„其中一個。" msgid "URL for connecting to designate" msgstr "用於連接以指定的 URL" msgid "URL to database" msgstr "資料庫 URL" #, python-format msgid "Unable to access %s" msgstr "ç„¡æ³•å­˜å– %s" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, maximum allowed " "prefix is %(max_prefixlen)s." msgstr "" "無法é…置字首長度為 %(prefixlen)s çš„å­ç¶²è·¯ï¼Œå®¹è¨±çš„字首上é™" "為%(max_prefixlen)s。" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, minimum allowed " "prefix is %(min_prefixlen)s." msgstr "" "無法é…置字首長度為 %(prefixlen)s çš„å­ç¶²è·¯ï¼Œå®¹è¨±çš„字首下é™" "為%(min_prefixlen)s。" #, python-format msgid "Unable to calculate %(address_type)s address because of:%(reason)s" msgstr "無法計算 %(address_type)s ä½å€ï¼ŒåŽŸå› ï¼š%(reason)s" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of DNS " "nameservers exceeds the limit %(quota)s." msgstr "" "ç„¡æ³•å° %(subnet_id)s 完æˆä½œæ¥­ã€‚DNS å稱伺æœå™¨çš„æ•¸ç›®è¶…出é™åˆ¶ %(quota)s。" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of host routes " "exceeds the limit %(quota)s." msgstr "ç„¡æ³•å° %(subnet_id)s 完æˆä½œæ¥­ã€‚主機路徑數目超出é™åˆ¶ %(quota)s。" #, python-format msgid "Unable to convert value in %s" msgstr "ç„¡æ³•è½‰æ› %s 中的值" msgid "Unable to create the Agent Gateway Port" msgstr "無法建立「代ç†ç¨‹å¼é–˜é“埠ã€" msgid "Unable to create the SNAT Interface Port" msgstr "無法建立「SNAT 介é¢åŸ ã€" #, python-format msgid "" "Unable to create the flat network. Physical network %(physical_network)s is " "in use." msgstr "無法建立平é¢ç¶²è·¯ã€‚實體網路 %(physical_network)s 正在使用中。" msgid "" "Unable to create the network. No available network found in maximum allowed " "attempts." msgstr "無法建立網路。在所容許的嘗試次數上é™å…§ï¼Œæ‰¾ä¸åˆ°å¯ç”¨çš„網路。" #, python-format msgid "Unable to delete subnet pool: %(reason)s." msgstr "無法刪除å­ç¶²è·¯å„²å­˜å€ï¼š%(reason)s。" #, python-format msgid "Unable to determine mac address for %s" msgstr "無法判定 %s çš„ MAC ä½å€" #, python-format msgid "Unable to find '%s' in request body" msgstr "åœ¨è¦æ±‚內文中找ä¸åˆ° '%s'" #, python-format msgid "Unable to find IP address %(ip_address)s on subnet %(subnet_id)s" msgstr "在å­ç¶²è·¯ %(subnet_id)s 上找ä¸åˆ° IP ä½å€ %(ip_address)s" #, python-format msgid "Unable to find resource name in %s" msgstr "在 %s 中找ä¸åˆ°è³‡æºå稱" #, python-format msgid "Unable to generate unique mac on network %(net_id)s." msgstr "無法在網路 %(net_id)s 上產生唯一 MAC ä½å€ã€‚" #, python-format msgid "" "Unable to identify a target field from:%s. Match should be in the form " "%%()s" msgstr "無法識別來自 %s 的目標欄ä½ã€‚ç›¸ç¬¦é …çš„æ ¼å¼æ‡‰è©²ç‚º%%()s" msgid "Unable to provide external connectivity" msgstr "無法æä¾›å¤–部連線功能" msgid "Unable to provide tenant private network" msgstr "無法æä¾›ç§Ÿæˆ¶å°ˆç”¨ç¶²è·¯" #, python-format msgid "" "Unable to reconfigure sharing settings for network %(network)s. Multiple " "tenants are using it." msgstr "ç„¡æ³•é‡æ–°é…置網路 %(network)s 的共用設定。多個租戶正在使用該網路。" #, python-format msgid "" "Unable to verify match:%(match)s as the parent resource: %(res)s was not " "found" msgstr "無法驗證相符項 %(match)s,因為找ä¸åˆ°æ¯é …è³‡æº %(res)s" #, python-format msgid "Unexpected label for script %(script_name)s: %(labels)s" msgstr "Script %(script_name)s 的標籤ä¸ç¬¦åˆé æœŸï¼š%(labels)s" #, python-format msgid "Unexpected number of alembic branch points: %(branchpoints)s" msgstr "alembic 分支點數目ä¸ç¬¦åˆé æœŸï¼š%(branchpoints)s" #, python-format msgid "Unexpected response code: %s" msgstr "éžé æœŸçš„回應碼:%s" #, python-format msgid "Unexpected response: %s" msgstr "éžé æœŸçš„回應:%s" #, python-format msgid "Unit name '%(unit)s' is not valid." msgstr "單元å稱 '%(unit)s' 無效。" #, python-format msgid "Unknown address type %(address_type)s" msgstr "䏿˜Žçš„ä½å€é¡žåž‹ %(address_type)s" #, python-format msgid "Unknown attribute '%s'." msgstr "䏿˜Žå±¬æ€§ '%s'。" #, python-format msgid "Unknown chain: %r" msgstr "䏿˜Žéˆï¼š%r" #, python-format msgid "Unknown network type %(network_type)s." msgstr "䏿˜Žçš„網路類型 %(network_type)s。" #, python-format msgid "Unknown quota resources %(unknown)s." msgstr "䏿˜Žçš„é…é¡è³‡æº %(unknown)s。" msgid "Unmapped error" msgstr "ã€Œæœªå°æ˜ ã€éŒ¯èª¤" msgid "Unrecognized action" msgstr "無法辨識的動作" #, python-format msgid "Unrecognized attribute(s) '%s'" msgstr "無法辨識屬性 '%s'" msgid "Unrecognized field" msgstr "無法辨識的欄ä½" msgid "Unsupported Content-Type" msgstr "䏿”¯æ´çš„內容類型" #, python-format msgid "Unsupported network type %(net_type)s." msgstr "䏿”¯æ´ç¶²è·¯é¡žåž‹ %(net_type)s。" #, python-format msgid "Unsupported port state: %(port_state)s." msgstr "䏿”¯æ´çš„埠狀態:%(port_state)s。" msgid "Unsupported request type" msgstr "ä¸å—支æ´çš„è¦æ±‚類型" msgid "Updating default security group not allowed." msgstr "ä¸å®¹è¨±æ›´æ–°é è¨­å®‰å…¨ç¾¤çµ„。" msgid "" "Use ML2 l2population mechanism driver to learn remote MAC and IPs and " "improve tunnel scalability." msgstr "" "使用 ML2 l2population 機制驅動程å¼ï¼Œä¾†çž­è§£é ç«¯ MAC åŠ IP ä½å€ï¼Œä¸¦æå‡é€šé“å¯èª¿" "整性。" msgid "Use broadcast in DHCP replies." msgstr "在 DHCP 回覆中使用廣播。" msgid "Use either --delta or relative revision, not both" msgstr "使用 --delta 或相å°ä¿®è¨‚,但ä¸è¦åŒæ™‚使用兩者" msgid "" "Use ipset to speed-up the iptables based security groups. Enabling ipset " "support requires that ipset is installed on L2 agent node." msgstr "" "使用 ipset 來加速 iptables 型安全群組。啟用 ipset 支æ´éœ€è¦åœ¨ L2 代ç†ç¨‹å¼ç¯€é»ž" "ä¸Šå®‰è£ ipset。" msgid "" "Use the root helper when listing the namespaces on a system. This may not be " "required depending on the security configuration. If the root helper is not " "required, set this to False for a performance improvement." msgstr "" "列出系統上的å稱空間時,請使用根說明程å¼ã€‚視安全性é…置而定,å¯èƒ½ä¸éœ€è¦é€™æ¨£" "åšã€‚如果ä¸éœ€è¦æ ¹èªªæ˜Žç¨‹å¼ï¼Œè«‹å°‡æ­¤åƒæ•¸è¨­ç‚º False 以改進效能。" msgid "" "Use veths instead of patch ports to interconnect the integration bridge to " "physical networks. Support kernel without Open vSwitch patch port support so " "long as it is set to True." msgstr "" "使用 veths(而ä¸ä½¿ç”¨ä¿®è£œç¨‹å¼åŸ ï¼‰å°‡æ•´åˆæ©‹æŽ¥å™¨äº¤äº’連接至實體網路。åªè¦å°‡å…¶è¨­ç‚º " "True,å³å¯æ”¯æ´ä¸å« Open vSwitch 修補程å¼åŸ æ”¯æ´çš„æ ¸å¿ƒã€‚" msgid "" "User (uid or name) running metadata proxy after its initialization (if " "empty: agent effective user)." msgstr "" "在 meta 資料 Proxy 起始設定之後執行該 Proxy 的使用者(UID 或å稱)(如果為" "空:則為代ç†ç¨‹å¼æœ‰æ•ˆä½¿ç”¨è€…)。" msgid "User (uid or name) running this process after its initialization" msgstr "在此程åºèµ·å§‹è¨­å®šä¹‹å¾ŒåŸ·è¡Œæ­¤ç¨‹åºçš„使用者(UID 或å稱)" msgid "Username for connecting to designate in admin context" msgstr "用於連接以在管ç†ç’°å¢ƒå®šç¾©ä¸­æŒ‡å®šçš„使用者å稱" msgid "VRRP authentication password" msgstr "VRRP 鑑別密碼" msgid "VRRP authentication type" msgstr "VRRP 鑑別類型" msgid "VXLAN network unsupported." msgstr "䏿”¯æ´ VXLAN 網路。" msgid "" "Value of host kernel tick rate (hz) for calculating minimum burst value in " "bandwidth limit rules for a port with QoS. See kernel configuration file for " "HZ value and tc-tbf manual for more information." msgstr "" "主機核心記號率 (hz) 的值,用來é€éŽæœå‹™å“質計算埠的頻寬é™åˆ¶è¦å‰‡ä¸­çš„æ¿€å¢žå€¼ä¸‹" "é™ã€‚如需相關資訊,請åƒé–± HZ 值的核心é…置檔和 tc-tbf 手冊。" msgid "" "Value of latency (ms) for calculating size of queue for a port with QoS. See " "tc-tbf manual for more information." msgstr "" "é€éŽæœå‹™å“è³ªä¾†è¨ˆç®—åŸ çš„ä½‡åˆ—å¤§å°æ™‚的延é²å€¼ï¼ˆæ¯«ç§’)。如需相關資訊,請åƒé–± tc-" "tbf 手冊。" msgid "" "When external_network_bridge is set, each L3 agent can be associated with no " "more than one external network. This value should be set to the UUID of that " "external network. To allow L3 agent support multiple external networks, both " "the external_network_bridge and gateway_external_network_id must be left " "empty." msgstr "" "當設定了 external_network_bridge 時,æ¯ä¸€å€‹ L3 代ç†ç¨‹å¼åªèƒ½èˆ‡ä¸è¶…éŽ 1 個外部" "網路建立關è¯ã€‚此值應該設為該外部網路的 UUID。如果è¦å®¹è¨± L3 代ç†ç¨‹å¼æ”¯æ´å¤šå€‹å¤–" "部網路,則必須將 external_network_bridge å’Œ gateway_external_network_id ç•™" "空。" msgid "" "When proxying metadata requests, Neutron signs the Instance-ID header with a " "shared secret to prevent spoofing. You may select any string for a secret, " "but it must match here and in the configuration used by the Nova Metadata " "Server. NOTE: Nova uses the same config key, but in [neutron] section." msgstr "" "å° meta è³‡æ–™è¦æ±‚執行 Proxy 動作時,Neutron 會使用共用密碼來簽署 Instance-ID " "標頭,以防止盜用。您å¯ä»¥é¸å–任何字串用作密碼,但該字串在此處以åŠåœ¨ Nova meta " "資料伺æœå™¨ä½¿ç”¨çš„é…置中必須相符。附註:Nova 使用相åŒçš„é…置金鑰,但å»åœ¨ " "[neutron] 倿®µä¸­ã€‚" msgid "" "Where to store Neutron state files. This directory must be writable by the " "agent." msgstr "Neutron 狀態檔的儲存ä½ç½®ã€‚此目錄必須å¯ä»¥ç”±ä»£ç†ç¨‹å¼å¯«å…¥ã€‚" msgid "" "With IPv6, the network used for the external gateway does not need to have " "an associated subnet, since the automatically assigned link-local address " "(LLA) can be used. However, an IPv6 gateway address is needed for use as the " "next-hop for the default route. If no IPv6 gateway address is configured " "here, (and only then) the neutron router will be configured to get its " "default route from router advertisements (RAs) from the upstream router; in " "which case the upstream router must also be configured to send these RAs. " "The ipv6_gateway, when configured, should be the LLA of the interface on the " "upstream router. If a next-hop using a global unique address (GUA) is " "desired, it needs to be done via a subnet allocated to the network and not " "through this parameter. " msgstr "" "如果使用 IPv6,則用於外部閘é“的網路ä¸éœ€è¦å…·æœ‰ç›¸é—œè¯çš„å­ç¶²è·¯ï¼Œå› ç‚ºå¯ä»¥ä½¿ç”¨è‡ªå‹•" "指派的éˆçµæœ¬ç«¯ä½å€ (LLA)。但是,IPv6 é–˜é“ä½å€éœ€è¦ç”¨ä½œé è¨­è·¯ç”±çš„下一個中繼站。" "如果未在這裡é…ç½® IPv6 é–˜é“ä½å€ï¼Œï¼ˆä¸”僅當那時)則將會é…ç½® Neutron 路由器以從上" "游路由器的路由器通告 (RA) 中å–å¾—å…¶é è¨­è·¯ç”±ï¼›åœ¨è©²æƒ…æ³ä¸‹ï¼Œä¹Ÿå¿…é ˆé…置上游路由器" "以傳é€é€™äº› RA。ipv6_gateway(如果已é…置)應為上游路由器介é¢çš„ LLA。如果需è¦ä½¿" "用廣域唯一ä½å€ (GUA) 的下一個中繼站,則需è¦é€éŽé…置給網路的å­ç¶²è·¯ä¾†åŸ·è¡Œæ­¤é…" "ç½®ï¼Œè€Œä¸æ˜¯é€éŽæ­¤åƒæ•¸ã€‚" msgid "You must implement __call__" msgstr "必須實作 __call__" msgid "" "You must provide a config file for bridge - either --config-file or " "env[NEUTRON_TEST_CONFIG_FILE]" msgstr "必須為橋接器æä¾›é…置檔:--config-file,或env[NEUTRON_TEST_CONFIG_FILE]" msgid "You must provide a revision or relative delta" msgstr "å¿…é ˆæä¾›ä¿®è¨‚或相å°å·®ç•°" msgid "a subnetpool must be specified in the absence of a cidr" msgstr "如果未指定 CIDR,則必須指定å­ç¶²è·¯å„²å­˜å€" msgid "add_ha_port cannot be called inside of a transaction." msgstr "ç„¡æ³•åœ¨äº¤æ˜“å…§å‘¼å« add_ha_port。" msgid "allocation_pools allowed only for specific subnet requests." msgstr "僅容許用於特定å­ç¶²è·¯è¦æ±‚çš„ allocation_pools。" msgid "allocation_pools are not in the subnet" msgstr "allocation_pools ä¸åœ¨å­ç¶²è·¯ä¸­" msgid "allocation_pools use the wrong ip version" msgstr "allocation_pools 使用了錯誤的 IP 版本" msgid "already a synthetic attribute" msgstr "å·²ç¶“æ˜¯åˆæˆå±¬æ€§" msgid "binding:profile value too large" msgstr "binding:profile 值太大" #, python-format msgid "cannot perform %(event)s due to %(reason)s" msgstr "無法執行 %(event)s,原因:%(reason)s" msgid "cidr and prefixlen must not be supplied together" msgstr "ä¸å¾—åŒæ™‚æä¾› cidr å’Œ prefixlen" #, python-format msgid "dhcp_agents_per_network must be >= 1. '%s' is invalid." msgstr "dhcp_agents_per_network å¿…é ˆ >= 1。'%s' 無效。" msgid "dns_domain cannot be specified without a dns_name" msgstr "如果未指定 dns_name,則無法指定 dns_domain" msgid "dns_name cannot be specified without a dns_domain" msgstr "如果未指定 dns_domain,則無法指定 dns_name" msgid "fixed_ip_address cannot be specified without a port_id" msgstr "如果未指定 port_id,則無法指定 fixed_ip_address" #, python-format msgid "has device owner %s" msgstr "具有è£ç½®æ“有者 %s" msgid "in use" msgstr "使用中" #, python-format msgid "ip command failed on device %(dev_name)s: %(reason)s" msgstr "å°è£ç½® %(dev_name)s 執行的 IP 指令失敗:%(reason)s" #, python-format msgid "ip command failed: %(reason)s" msgstr "ip 指令失敗:%(reason)s" #, python-format msgid "ip link capability %(capability)s is not supported" msgstr "䏿”¯æ´ ip link 功能 %(capability)s" #, python-format msgid "ip link command is not supported: %(reason)s" msgstr "䏿”¯æ´ ip link 指令:%(reason)s" msgid "ip_version must be specified in the absence of cidr and subnetpool_id" msgstr "如果未指定 cidr å’Œ subnetpool_id,則必須指定 ip_version" msgid "ipv6_address_mode is not valid when ip_version is 4" msgstr "ç•¶ ip_version 是 4 時,ipv6_address_mode 無效" msgid "ipv6_ra_mode is not valid when ip_version is 4" msgstr "ç•¶ ip_version 是 4 時,ipv6_ra_mode 無效" #, python-format msgid "" "ipv6_ra_mode set to '%(ra_mode)s' with ipv6_address_mode set to " "'%(addr_mode)s' is not valid. If both attributes are set, they must be the " "same value" msgstr "" "如果在 ipv6_address_mode 設為 '%(addr_mode)s' 時將 ipv6_ra_mode 設" "為'%(ra_mode)s'ï¼Œå‰‡ç„¡æ•ˆã€‚å¦‚æžœå…©å€‹å±¬æ€§åŒæ™‚設定,則它們的值必須相åŒ" msgid "mac address update" msgstr "MAC ä½å€æ›´æ–°" msgid "must provide exactly 2 arguments - cidr and MAC" msgstr "å¿…é ˆæä¾› 2 個確切引數 - cidr å’Œ MAC" msgid "network_type required" msgstr "éœ€è¦ network_type" #, python-format msgid "network_type value '%s' not supported" msgstr "䏿”¯æ´ network_type 值 '%s'" msgid "new subnet" msgstr "æ–°å­ç¶²è·¯" #, python-format msgid "physical_network '%s' unknown for flat provider network" msgstr "å¹³é¢æä¾›è€…ç¶²è·¯çš„ physical_network '%s' 䏿˜Ž" msgid "physical_network required for flat provider network" msgstr "å¹³é¢æä¾›è€…ç¶²è·¯æ‰€éœ€çš„ physical_network" #, python-format msgid "provider:physical_network specified for %s network" msgstr "為 %s 網路指定了 provider:physical_network" msgid "respawn_interval must be >= 0 if provided." msgstr "如果æä¾›çš„話,則 respawn_interval å¿…é ˆ >= 0。" #, python-format msgid "segmentation_id out of range (%(min)s through %(max)s)" msgstr "segmentation_id 超出範åœï¼ˆ%(min)s 到 %(max)s)" msgid "segmentation_id requires physical_network for VLAN provider network" msgstr "segmentation_id éœ€è¦ VLAN æä¾›è€…網路的 physical_network" msgid "shared attribute switching to synthetic" msgstr "共用屬性正在切æ›è‡³åˆæˆå±¬æ€§" #, python-format msgid "" "subnetpool %(subnetpool_id)s cannot be updated when associated with shared " "address scope %(address_scope_id)s" msgstr "" "å­ç¶²è·¯å„²å­˜å€ %(subnetpool_id)s 在下列情æ³ä¸‹ç„¡æ³•更新:與共用ä½å€ç¯„åœ " "%(address_scope_id)s ç›¸é—œè¯æ™‚" msgid "subnetpool_id and use_default_subnetpool cannot both be specified" msgstr "ä¸èƒ½åŒæ™‚指定 subnetpool_id å’Œ use_default_subnetpool" msgid "the nexthop is not connected with router" msgstr "下一個中繼站未與路由器連接" msgid "the nexthop is used by router" msgstr "路由器已使用下一個中繼站" neutron-12.1.1/neutron/locale/fr/0000775000175000017500000000000013553660156016662 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/fr/LC_MESSAGES/0000775000175000017500000000000013553660156020447 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/fr/LC_MESSAGES/neutron.po0000664000175000017500000034532213553660047022511 0ustar zuulzuul00000000000000# Translations template for neutron. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the neutron project. # # Translators: # François Bureau, 2013 # Maxime COQUEREL , 2014-2015 # Patrice LACHANCE , 2013 # Patrice LACHANCE , 2013 # Andreas Jaeger , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: neutron VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-03-14 04:19+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-04-12 06:24+0000\n" "Last-Translator: Copied by Zanata \n" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 4.3.3\n" "Language-Team: French\n" #, python-format msgid "" "\n" "Command: %(cmd)s\n" "Exit code: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" msgstr "" "\n" "Commande : %(cmd)s\n" "Code de sortie : %(code)s\n" "Stdin : %(stdin)s\n" "Stdout : %(stdout)s\n" "Stderr : %(stderr)s" #, python-format msgid "" "%(branch)s HEAD file does not match migration timeline head, expected: " "%(head)s" msgstr "" "Le fichier HEAD %(branch)s ne correspond pas à l'élément Head de calendrier " "de migration, attendu : %(head)s" #, python-format msgid "%(id)s is not a valid %(type)s identifier" msgstr "%(id)s n'est pas un identificateur %(type)s valide" #, python-format msgid "" "%(invalid_dirs)s is invalid value for sort_dirs, valid value is '%(asc)s' " "and '%(desc)s'" msgstr "" "%(invalid_dirs)s est une valeur non valide pour sort_dirs ; les valeurs " "valides sont '%(asc)s' et '%(desc)s'" #, python-format msgid "%(key)s prohibited for %(tunnel)s provider network" msgstr "%(key)s interdit pour le réseau de fournisseur %(tunnel)s" #, python-format msgid "%(name)s '%(addr)s' does not match the ip_version '%(ip_version)s'" msgstr "%(name)s '%(addr)s' ne correspond pas à ip_version '%(ip_version)s'" #, python-format msgid "%s cannot be called while in offline mode" msgstr "%s ne peut pas être appelé en mode hors ligne" #, python-format msgid "%s is invalid attribute for sort_keys" msgstr "%s est un attribut non valide pour sort_keys" #, python-format msgid "%s is not a valid VLAN tag" msgstr "%s n'est pas une balise VLAN (réseau local virtuel) valide" #, python-format msgid "%s must implement get_port_from_device or get_ports_from_devices." msgstr "%s doit implémenter get_port_from_device ou get_ports_from_devices." #, python-format msgid "%s prohibited for VLAN provider network" msgstr "%s interdit pour le réseau de fournisseurs de réseau local virtuel" #, python-format msgid "%s prohibited for flat provider network" msgstr "%s interdit pour le réseau de fournisseurs non hiérarchique" #, python-format msgid "%s prohibited for local provider network" msgstr "%s interdit pour le réseau de fournisseurs local" #, python-format msgid "'%s' is not a valid RBAC object type" msgstr "'%s' n'est pas un type d'objet RBAC valide" #, python-format msgid "'%s' is not supported for filtering" msgstr "'%s' n'est pas pris en charge pour le filtrage " #, python-format msgid "'module' object has no attribute '%s'" msgstr "L'objet 'module' n'a pas d'attribut '%s'" msgid "'port_max' is smaller than 'port_min'" msgstr "'port_max' est plus petit que 'port_min'" msgid "0 is not allowed as CIDR prefix length" msgstr "La longueur 0 n'est pas autorisée pour le préfixe CIDR" msgid "A cidr must be specified in the absence of a subnet pool" msgstr "" "Une valeur cidr doit être indiquée si aucun pool de sous-réseau n'est défini" msgid "" "A decimal value as Vendor's Registered Private Enterprise Number as required " "by RFC3315 DUID-EN." msgstr "" "Valeur décimale telle que Numéro d'entreprise privé enregistré du " "fournisseur comme requis par RFC3315 DUID-EN." #, python-format msgid "A default external network already exists: %(net_id)s." msgstr "Un réseau externe par défaut existe déjà : %(net_id)s." msgid "" "A default subnetpool for this IP family has already been set. Only one " "default may exist per IP family" msgstr "" "Un pool de sous-réseau par défaut pour cette famille IP est déjà défini. Il " "ne peut y avoir qu'un seul pool par défaut par famille IP." msgid "A metering driver must be specified" msgstr "Un pilote de mesure doit être spécifié." msgid "API for retrieving service providers for Neutron advanced services" msgstr "" "API d'extraction des fournisseurs de service pour les services avancés de " "Neutron" msgid "Aborting periodic_sync_routers_task due to an error." msgstr "Interruption de periodic_sync_routers_task en raison d'une erreur." msgid "Access to this resource was denied." msgstr "L'accès a cette ressource était refusé" msgid "Action to be executed when a child process dies" msgstr "Action à exécuter quand un processus enfant meurt" msgid "" "Add comments to iptables rules. Set to false to disallow the addition of " "comments to generated iptables rules that describe each rule's purpose. " "System must support the iptables comments module for addition of comments." msgstr "" "Ajouter des commentaires aux règles iptables. Définir avec la valeur false " "pour interdire l'ajout de commentaires aux règles iptables générées qui " "décrivent l'objectif de chaque règle. Le système doit prendre en charge le " "module de commentaires iptables pour l'ajout de commentaires." msgid "Address not present on interface" msgstr "Une adresse n'est pas présente sur l'interface" msgid "" "Address to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "Adresse à utiliser pour l'écoute des connexions OpenFlow. Utilisée " "uniquement pour le pilote 'natif'." msgid "Adds test attributes to core resources." msgstr "Ajoute les attributs de test aux ressources principales." #, python-format msgid "Agent %(id)s is not a L3 Agent or has been disabled" msgstr "L'agent %(id)s n'est pas un agent L3 ou a été désactivé" #, python-format msgid "Agent %(id)s is not a valid DHCP Agent or has been disabled" msgstr "L'agent %(id)s n'est pas un agent DHCP valide ou a été désactivé" msgid "" "Agent starts with admin_state_up=False when enable_new_agents=False. In the " "case, user's resources will not be scheduled automatically to the agent " "until admin changes admin_state_up to True." msgstr "" "L'agent démarre avec admin_state_up=False si enable_new_agents=False. Dans " "ce cas, les ressources de l'utilisateur ne sont pas planifiées " "automatiquement pour l'agent sauf si l'administrateur affecte la valeur True " "à admin_state_up. " #, python-format msgid "Agent updated: %(payload)s" msgstr "Mise à jour de l'agent: %(payload)s" msgid "Allow auto scheduling networks to DHCP agent." msgstr "Autorise la planification automatique des réseaux de l'agent DHCP." msgid "Allow auto scheduling of routers to L3 agent." msgstr "Autorise la planification automatique des routeurs vers l'agent L3." msgid "" "Allow overlapping IP support in Neutron. Attention: the following parameter " "MUST be set to False if Neutron is being used in conjunction with Nova " "security groups." msgstr "" "Autoriser la prise en charge IP du chevauchement dans Neutron. Attention : " "le paramètre ci-après DOIT être défini sur False si Neutron est utilisé " "conjointement avec des groupes de sécurité Nova." msgid "Allow running metadata proxy." msgstr "Autorisez le proxy de métadonnées en cours d'exécution." msgid "Allow sending resource operation notification to DHCP agent" msgstr "" "Autoriser l'envoi de notifications d'opérations de ressources à l'agent DHCP" msgid "Allow the creation of PTR records" msgstr "Autoriser la création d'enregistrements PTR" msgid "Allow the usage of the bulk API" msgstr "Autoriser l'utilisation de l'API de traitement en bloc" msgid "Allow to perform insecure SSL (https) requests to nova metadata" msgstr "" "Permet d'effectuer des requêtes (https) non sécurisées aux métadonnées de " "nova" msgid "" "Allows for serving metadata requests coming from a dedicated metadata access " "network whose CIDR is 169.254.169.254/16 (or larger prefix), and is " "connected to a Neutron router from which the VMs send metadata:1 request. In " "this case DHCP Option 121 will not be injected in VMs, as they will be able " "to reach 169.254.169.254 through a router. This option requires " "enable_isolated_metadata = True." msgstr "" "Permet le traitement des demandes de métadonnées en provenance d'un réseau " "d'accès de métadonnées dédié dont le CIDR est 169.254.169.254/16 (ou un " "préfixe plus long), et qui est connecté à un routeur Neutron depuis lequel " "les machines virtuelles envoient une demande metadata:1. Dans ce cas, DHCP " "Option 121 n'est pas injecté dans les machines virtuelles, car celles-ci " "pourront accéder à l'adresse 169.254.169.254 via un routeur. Cette option " "requiert enable_isolated_metadata = True." msgid "An RBAC policy already exists with those values." msgstr "Une stratégie RBAC existe déjà avec ces valeurs." msgid "An identifier must be specified when updating a subnet" msgstr "" "Un identificateur doit être spécifié lors de la mise à jour d'un sous-réseau" msgid "An interface driver must be specified" msgstr "Un pilote d'interface doit être spécifié." msgid "" "An ordered list of extension driver entrypoints to be loaded from the " "neutron.ml2.extension_drivers namespace. For example: extension_drivers = " "port_security,qos" msgstr "" "An ordered list of extension driver entrypoints to be loaded from the " "neutron.ml2.extension_drivers namespace. For example: extension_drivers = " "port_security,qos" msgid "" "An ordered list of networking mechanism driver entrypoints to be loaded from " "the neutron.ml2.mechanism_drivers namespace." msgstr "" "Liste ordonnée de points d'entrée de pilote de mécanisme à charger à partir " "de l'espace de nom neutron.ml2.mechanism_drivers." msgid "An unknown error has occurred. Please try your request again." msgstr "Une erreur inconnue s'est produite. Renouvelez votre demande." msgid "Async process didn't respawn" msgstr "Le processus Async n'a pas été relancé" msgid "Authorization URL for connecting to designate in admin context" msgstr "" "URL d'autorisation pour la connexion au réseau désigné dans un contexte admin" msgid "Automatically remove networks from offline DHCP agents." msgstr "Supprime automatiquement les réseaux des agents DHCP hors ligne." msgid "" "Automatically reschedule routers from offline L3 agents to online L3 agents." msgstr "" "Replanifier automatiquement les routeurs pour qu'ils passent d'agents L3 " "hors connexion aux agents L3 connectés." msgid "Availability zone of this node" msgstr "Zone de disponibilité du noeud" msgid "Available commands" msgstr "Commandes disponibles" msgid "Backend does not support VLAN Transparency." msgstr "Le backend ne prend pas en charge la transparence VLAN." #, python-format msgid "Base MAC: %s" msgstr "MAC de base : %s" msgid "" "Base log dir for dnsmasq logging. The log contains DHCP and DNS log " "information and is useful for debugging issues with either DHCP or DNS. If " "this section is null, disable dnsmasq log." msgstr "" "Répertoire de journaux de base pour la consignation dnsmasq. Le journal " "contient des informations de journal DHCP et DNS et s'avère utile pour " "déboguer les problèmes liés à DHCP ou DNS. Si cette section est NULL, " "désactivez la consignation dnsmasq. " msgid "Body contains invalid data" msgstr "Le corps contient des données non valides" msgid "Both network_id and router_id are None. One must be provided." msgstr "" "Les paramètres Both network_id et router_id sont définis sur None. L'un des " "deux doit obligatoirement être fourni." #, python-format msgid "Bridge %(bridge)s does not exist." msgstr "Le pont %(bridge)s n'existe pas." msgid "Bulk operation not supported" msgstr "Opération globale non prise en charge" msgid "CIDR to monitor" msgstr "CIDR à surveiller" #, python-format msgid "Callback for %(resource_type)s not found" msgstr "Rappel pour %(resource_type)s introuvable" #, python-format msgid "Callback for %(resource_type)s returned wrong resource type" msgstr "" "Le rappel pour %(resource_type)s a renvoyé un type de ressource incorrect" #, python-format msgid "Cannot add floating IP to port %s that has no fixed IPv4 addresses" msgstr "" "Impossible d'ajouter une adresse IP flottante au port %s qui n'a pas " "d'adresse IPv4 fixe" #, python-format msgid "Cannot add multiple callbacks for %(resource_type)s" msgstr "Impossible d'ajouter plusieurs rappels pour %(resource_type)s" #, python-format msgid "Cannot allocate IPv%(req_ver)s subnet from IPv%(pool_ver)s subnet pool" msgstr "" "Impossible d'allouer le sous-réseau IPv%(req_ver)s à partir du pool de sous-" "réseau IPv%(pool_ver)s" msgid "Cannot allocate requested subnet from the available set of prefixes" msgstr "" "Impossible d'allouer le sous-réseau demandé à partir de l'ensemble de " "préfixes disponibles" msgid "Cannot disable enable_dhcp with ipv6 attributes set" msgstr "Impossible de désactiver enable_dhcp avec des attributs ipv6 définis" #, python-format msgid "Cannot handle subnet of type %(subnet_type)s" msgstr "Impossible de traiter le sous-réseau de type %(subnet_type)s" msgid "Cannot have multiple IPv4 subnets on router port" msgstr "Impossible d'avoir plusieurs sous-réseaux IPv4 sur le port de routeur" #, python-format msgid "" "Cannot have multiple router ports with the same network id if both contain " "IPv6 subnets. Existing port %(p)s has IPv6 subnet(s) and network id %(nid)s" msgstr "" "Impossible d'avoir plusieurs ports de routeur avec le même ID réseau s'ils " "contiennent tous des sous-réseaux IPv6. Le port %(p)s existant a un ou " "plusieurs sous-réseaux IPv6 et l'ID réseau %(nid)s" #, python-format msgid "" "Cannot host distributed router %(router_id)s on legacy L3 agent %(agent_id)s." msgstr "" "Impossible d'héberger un routeur distribué %(router_id)s sur l'agent L3 " "existant %(agent_id)s." msgid "Cannot mix IPv4 and IPv6 prefixes in a subnet pool." msgstr "" "Impossible d'associer les préfixes IPv4 et IPv6 dans un pool de sous-réseau." msgid "Cannot specify both subnet-id and port-id" msgstr "Impossible de spécifier l'ID sous-réseau et l'ID port" msgid "Cannot understand JSON" msgstr "Impossible de comprendre JSON" #, python-format msgid "Cannot update read-only attribute %s" msgstr "Impossible de mettre à jour l'attribut en lecture seule %s" msgid "Certificate Authority public key (CA cert) file for ssl" msgstr "" "Fichier de clés publiques de l'autorité de certification (CA cert) pour SSL" #, python-format msgid "" "Change would make usage less than 0 for the following resources: %(unders)s." msgstr "" "Une modification entraînerait une utilisation inférieure à 0 pour les " "ressources suivantes : %(unders)s." msgid "Check ebtables installation" msgstr "Vérifier l'installation ebtables " msgid "Check for ARP header match support" msgstr "Vérifier le support de correspondance d'en-tête ARP" msgid "Check for ARP responder support" msgstr "Vérifier le support de programme de réponse ARP" msgid "Check for ICMPv6 header match support" msgstr "Vérifier le support de correspondance d'en-tête ICMPv6" msgid "Check for OVS Geneve support" msgstr "Vérifier le support OVS Geneve" msgid "Check for OVS vxlan support" msgstr "Vérifier le support OVS vxlan" msgid "Check for VF management support" msgstr "Vérifier le support de gestion VF" msgid "Check for iproute2 vxlan support" msgstr "Vérifier le support iproute2 vxlan" msgid "Check for nova notification support" msgstr "Vérifier le support de notification de Nova" msgid "Check for patch port support" msgstr "Vérifier le support de port de correctif" msgid "Check ip6tables installation" msgstr "Consultez l'installation ip6tables" msgid "Check ipset installation" msgstr "Vérifier l'installation ipset" msgid "Check keepalived IPv6 support" msgstr "Vérifier le support de keepalived IPv6" msgid "Check minimal dibbler version" msgstr "Vérifier la version minimale de dibbler" msgid "Check minimal dnsmasq version" msgstr "Vérifier la version minimale de dnsmasq" msgid "Check netns permission settings" msgstr "Vérifier les autorisations netns" msgid "Check ovs conntrack support" msgstr "Consultez le support ovs conntrack" msgid "Check ovsdb native interface support" msgstr "Consulter le support d'interface native ovsdb" #, python-format msgid "" "Cidr %(subnet_cidr)s of subnet %(subnet_id)s overlaps with cidr %(cidr)s of " "subnet %(sub_id)s" msgstr "" "Le routage CIDR %(subnet_cidr)s du sous-réseau %(subnet_id)s chevauche le " "routage CIDR %(cidr)s du sous-réseau %(sub_id)s" msgid "Cleanup resources of a specific agent type only." msgstr "Ressources de nettoyage d'un type d'agent spécifique uniquement." msgid "Client certificate for nova metadata api server." msgstr "Certificat client pour le serveur d'API des métadonnées nova." msgid "" "Comma-separated list of : tuples, mapping " "network_device to the agent's node-specific list of virtual functions that " "should not be used for virtual networking. vfs_to_exclude is a semicolon-" "separated list of virtual functions to exclude from network_device. The " "network_device in the mapping should appear in the physical_device_mappings " "list." msgstr "" "Liste des uplets :, séparés par des " "virgules, qui mappent network_device à la liste de fonctions spécifique à un " "nÅ“ud d'agent des fonctions virtuelles qui ne doivent pas être utilisées pour " "une mise en réseau virtuelle. vfs_to_exclude est une liste de fonctions " "virtuelles, séparées par des virgules, à exclure de network_device. " "network_device dans le mappage doit figurer dans la liste " "physical_device_mappings." msgid "" "Comma-separated list of : tuples mapping " "physical network names to the agent's node-specific physical network device " "interfaces of SR-IOV physical function to be used for VLAN networks. All " "physical networks listed in network_vlan_ranges on the server should have " "mappings to appropriate interfaces on each agent." msgstr "" "Liste des uplets : séparés par des " "virgules, qui mappent les noms de réseau physique aux interfaces d'unité " "réseau physiques spécifiques à un nÅ“ud d'agent de la fonction physique SR-" "IOV à utiliser pour les réseaux VLAN. Tous les réseaux physiques répertoriés " "dans network_vlan_ranges sur le serveur doivent avoir des mappages aux " "interfaces appropriées sur chaque agent." msgid "" "Comma-separated list of : tuples " "mapping physical network names to the agent's node-specific physical network " "interfaces to be used for flat and VLAN networks. All physical networks " "listed in network_vlan_ranges on the server should have mappings to " "appropriate interfaces on each agent." msgstr "" "Liste des uplets : séparés par des " "virgules qui mappent les noms de réseau physique aux interfaces de réseau " "physique spécifiques à un nÅ“ud d'agent à utiliser pour les réseaux " "centralisés et VLAN. Tous les réseaux physiques répertoriés dans " "network_vlan_ranges sur le serveur doivent avoir des mappages aux interfaces " "appropriées sur chaque agent." msgid "" "Comma-separated list of : tuples enumerating ranges of GRE " "tunnel IDs that are available for tenant network allocation" msgstr "" "Liste d'uplets : séparés par des virgules énumérant des " "plages d'ID GRE disponibles pour l'allocation de réseau locataire" msgid "" "Comma-separated list of : tuples enumerating ranges of " "Geneve VNI IDs that are available for tenant network allocation" msgstr "" "Liste d'uplets : séparés par des virgules énumérant des " "plages d'ID VNI Geneve disponibles pour l'allocation de réseau locataire" msgid "" "Comma-separated list of : tuples enumerating ranges of " "VXLAN VNI IDs that are available for tenant network allocation" msgstr "" "Liste d'uplets : séparés par des virgules énumérant des " "plages d'ID VNI VXLAN disponibles pour l'allocation de réseau locataire" msgid "" "Comma-separated list of the DNS servers which will be used as forwarders." msgstr "" "Liste séparée par des virgules des serveurs DNS qui seront utilisés comme " "réexpéditeurs." msgid "Command to execute" msgstr "Commande à exécuter" msgid "Config file for interface driver (You may also use l3_agent.ini)" msgstr "" "Fichier de configuration du pilote d'interface (vous pouvez aussi utiliser " "l3_agent.ini)" #, python-format msgid "Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s" msgstr "Valeur en conflit ethertype %(ethertype)s pour le CIDR %(cidr)s" msgid "" "Controls whether the neutron security group API is enabled in the server. It " "should be false when using no security groups or using the nova security " "group API." msgstr "" "Indique si l'API de groupe de sécurité neutron est activée sur le serveur. " "Elle doit être false si aucun groupe de sécurité n'est utilisé ou en cas " "d'utilisation de l'API du groupe de sécurité neutron." #, python-format msgid "Could not bind to %(host)s:%(port)s after trying for %(time)d seconds" msgstr "" "Echec lors de la liaison à %(host)s:%(port)s après attente de %(time)d " "secondes" msgid "Could not deserialize data" msgstr "Impossible de désérialiser des données" #, python-format msgid "" "Current gateway ip %(ip_address)s already in use by port %(port_id)s. Unable " "to update." msgstr "" "L'adresse IP de la passerelle en cours %(ip_address)s est déjà en cours " "d'utilisation par le port %(port_id)s. Impossible de mettre à jour" msgid "" "DHCP lease duration (in seconds). Use -1 to tell dnsmasq to use infinite " "lease times." msgstr "" "Durée de bail DHCP (en secondes). Utiliser -1 pour ordonner dnsmasq pour " "utiliser des durées de bail illimitées." msgid "" "DVR deployments for VXLAN/GRE/Geneve underlays require L2-pop to be enabled, " "in both the Agent and Server side." msgstr "" "Les déploiements DVR pour les sous-couches VXLAN/GRE/Geneve nécessitent que " "L2-pop soit activé, à la fois côté agent et côté serveur." msgid "" "Database engine for which script will be generated when using offline " "migration." msgstr "" "Moteur de base de données pour lequel le script va être généré lors de " "l'utilisation d'une migration hors ligne." msgid "Default external networks must be shared to everyone." msgstr "Les réseaux externes par défaut doivent être partagés par tous." msgid "" "Default network type for external networks when no provider attributes are " "specified. By default it is None, which means that if provider attributes " "are not specified while creating external networks then they will have the " "same type as tenant networks. Allowed values for external_network_type " "config option depend on the network type values configured in type_drivers " "config option." msgstr "" "Type de réseau par défaut pour des réseaux externes lorsqu'aucun attribut de " "fournisseur n'est spécifié. La valeur par défaut None signifie que si des " "attributs de fournisseur ne sont pas spécifiés lors de la création de " "réseaux externes, ces derniers prennent le même type que les réseaux " "locataires. Les valeurs autorisées pour l'option de config " "external_network_type dépendent des valeurs de type de réseau configurées " "dans l'option de config type_drivers. " msgid "" "Default number of RBAC entries allowed per tenant. A negative value means " "unlimited." msgstr "" "Nombre par défaut d'entrées RBAC autorisées par locataire. Une valeur " "négative signifie que le nombre est illimité." msgid "" "Default number of resource allowed per tenant. A negative value means " "unlimited." msgstr "" "Nombre de ressources par défaut autorisées par le locataire. Une valeur " "négative signifie illimité." msgid "Default security group" msgstr "Groupe de sécurité par défaut" msgid "Default security group already exists." msgstr "Le groupe de sécurité par défaut existe déjà." msgid "" "Default value of availability zone hints. The availability zone aware " "schedulers use this when the resources availability_zone_hints is empty. " "Multiple availability zones can be specified by a comma separated string. " "This value can be empty. In this case, even if availability_zone_hints for a " "resource is empty, availability zone is considered for high availability " "while scheduling the resource." msgstr "" "Valeur par défaut des suggestions de zone de disponibilité. Les " "planificateurs de zone de disponibilité utilisent cette valeur lorsque le " "paramètre resources availability_zone_hints est à blanc. Plusieurs zones de " "disponibilité peuvent être indiquées en les séparant par une virgule. Cette " "valeur peut être vide. Dans ce cas, même si le paramètre " "availability_zone_hints d'une ressource est à blanc, la zone de " "disponibilité est prise en compte pour la haute disponibilité lors de la " "planification de la ressource." msgid "" "Define the default value of enable_snat if not provided in " "external_gateway_info." msgstr "" "Définissez la valeur par défaut de enable_snat si elle n'est pas indiquée " "dans external_gateway_info." msgid "" "Defines providers for advanced services using the format: :" ":[:default]" msgstr "" "Définit des fournisseurs pour les services avancés utilisant le format : " "::[:default]" msgid "Delete the namespace by removing all devices." msgstr "Supprimez l'espace de nom en supprimant toutes les unités." #, python-format msgid "Deleting port %s" msgstr "Suppression du port %s" #, python-format msgid "Deployment error: %(reason)s." msgstr "Erreur de déploiement : %(reason)s." msgid "Destroy IPsets even if there is an iptables reference." msgstr "Détruire les IPsets même s'il y a une référence iptables." msgid "Destroy all IPsets." msgstr "Destruction de tous les IPsets." #, python-format msgid "Device %(dev_name)s in mapping: %(mapping)s not unique" msgstr "Périphérique %(dev_name)s non unique dans le mappage '%(mapping)s'" #, python-format msgid "Device name %(dev_name)s is missing from physical_device_mappings" msgstr "" "Le nom de périphérique %(dev_name)s est manquant dans " "physical_device_mappings" msgid "Device not found" msgstr "Equipement non trouvé" #, python-format msgid "" "Distributed Virtual Router Mac Address for host %(host)s does not exist." msgstr "" "L'adresse MAC DVR (routeur virtuel distribué) n'existe pas pour l'hôte " "%(host)s." msgid "Domain to use for building the hostnames" msgstr "Domaine à utiliser pour générer les noms d'hôte" msgid "Downgrade no longer supported" msgstr "La rétromigration n'est plus prise en charge" #, python-format msgid "Driver %s is not unique across providers" msgstr "Le pilote %s n'est pas unique entre les fournisseurs" msgid "Driver for external DNS integration." msgstr "Pilote pour intégration DNS externe." msgid "Driver for security groups firewall in the L2 agent" msgstr "" "Pilote pour le pare-feu de groupes de sécurité dans l'agent de niveau 2" msgid "Driver to use for scheduling network to DHCP agent" msgstr "Pilote à utiliser pour la planification du réseau de l'agent DHCP" msgid "Driver to use for scheduling router to a default L3 agent" msgstr "" "Pilote à utiliser pour la planification du routeur de l'agent L3 par défaut" msgid "" "Driver used for ipv6 prefix delegation. This needs to be an entry point " "defined in the neutron.agent.linux.pd_drivers namespace. See setup.cfg for " "entry points included with the neutron source." msgstr "" "Pilote utilisé pour la délégation de préfixe ipv6. Il doit s'agir d'un point " "d'entrée défini dans l'espace de nom neutron.agent.linux.pd_drivers. Voir " "setup.cfg pour connaître les points d'entrée inclus avec la source de " "neutron. " #, python-format msgid "" "Duplicate L3HARouterAgentPortBinding is created for router(s) %(router)s. " "Database cannot be upgraded. Please, remove all duplicates before upgrading " "the database." msgstr "" "Un élément L3HARouterAgentPortBinding en double est créé pour le ou les " "routeurs %(router)s. La base de données ne peut pas être mise à niveau. " "Retirez tous les éléments en double avant de mettre à niveau la base de " "données." msgid "Duplicate Security Group Rule in POST." msgstr "" "Règle de groupe de sécurité en double dans l'autotest à la mise sous tension." msgid "Duplicate address detected" msgstr "Adresse en double détectée" msgid "Duplicate segment entry in request." msgstr "Entrée de segment en double dans la demande." #, python-format msgid "ERROR: %s" msgstr "ERREUR : %s" msgid "" "ERROR: Unable to find configuration file via the default search paths (~/." "neutron/, ~/, /etc/neutron/, /etc/) and the '--config-file' option!" msgstr "" "ERREUR : Impossible de trouver le fichier de configuration via les chemins " "de recherche par défaut (~/.neutron/, ~/, /etc/neutron/, /etc/) et l'option " "'--config-file' !" msgid "" "Either one of parameter network_id or router_id must be passed to _get_ports " "method." msgstr "" "Le paramètre network_id ou le paramètre router_id doit être passé à la " "méthode _get_ports." msgid "Either subnet_id or port_id must be specified" msgstr "L'ID sous-réseau ou l'ID port doit être spécifié." msgid "Empty physical network name." msgstr "Nom du Réseau Physique vide." msgid "Empty subnet pool prefix list." msgstr "Liste de préfixes de pool de sous-réseau vide." msgid "Enable HA mode for virtual routers." msgstr "Activer le mode haute disponibilité pour les routeurs virtuels." msgid "Enable SSL on the API server" msgstr "Active SSL sur le serveur API" msgid "" "Enable VXLAN on the agent. Can be enabled when agent is managed by ml2 " "plugin using linuxbridge mechanism driver" msgstr "" "Activer VXLAN sur l'agent. Il peut être activé lorsque l'agent est géré par " "le plug-in ml2 utilisant le pilote de mécanisme linuxbridge" msgid "" "Enable local ARP responder if it is supported. Requires OVS 2.1 and ML2 " "l2population driver. Allows the switch (when supporting an overlay) to " "respond to an ARP request locally without performing a costly ARP broadcast " "into the overlay." msgstr "" "Activez le canal répondeur ARP local s'il est pris en charge. Requiert le " "pilote l2population OVS 2.1 et ML2. Permet au commutateur (lors de la prise " "en charge d'une superposition) de répondre à une demande ARP locale sans " "effectuer de diffusion ARP coûteuse dans le réseau Overlay." msgid "" "Enable services on an agent with admin_state_up False. If this option is " "False, when admin_state_up of an agent is turned False, services on it will " "be disabled. Agents with admin_state_up False are not selected for automatic " "scheduling regardless of this option. But manual scheduling to such agents " "is available if this option is True." msgstr "" "Activer les services sur un agent ayant admin_state_up avec une valeur " "False. Si cette option est False, lorsque admin_state_up d'un agent se voit " "attribuer la valeur False, les services qui y sont associés seront " "automatiquement désactivés. Les agents ayant admin_state_up avec la valeur " "False ne sont pas sélectionnés pour la planification automatique, quelle que " "soit la valeur de cette option. Toutefois, il est possible de procéder à une " "planification manuelle pour ces agents si cette option a pour valeur True." msgid "" "Enables IPv6 Prefix Delegation for automatic subnet CIDR allocation. Set to " "True to enable IPv6 Prefix Delegation for subnet allocation in a PD-capable " "environment. Users making subnet creation requests for IPv6 subnets without " "providing a CIDR or subnetpool ID will be given a CIDR via the Prefix " "Delegation mechanism. Note that enabling PD will override the behavior of " "the default IPv6 subnetpool." msgstr "" "Active la délégation de préfixe IPv6 pour l'allocation CIDR de sous-réseau " "automatique. Définissez ce paramètre sur True pour activer la délégation de " "préfixe IPv6 pour l'allocation de sous-réseau dans un environnement " "compatible PD. Les utilisateurs effectuant des demandes de création de sous-" "réseau pour des sous-réseaux IPv6 sans indiquer de CIDR ou d'ID de pool de " "sous-réseau se verront affecter un CIDR via le mécanisme de délégation de " "préfixe. Notez que l'activation de PD se substitue au comportement du pool " "de sous-réseau IPv6 par défaut." msgid "" "Enables the dnsmasq service to provide name resolution for instances via DNS " "resolvers on the host running the DHCP agent. Effectively removes the '--no-" "resolv' option from the dnsmasq process arguments. Adding custom DNS " "resolvers to the 'dnsmasq_dns_servers' option disables this feature." msgstr "" "Permet au service dnsmasq de fournir la résolution de nom pour les instances " "via des programmes de résolution DNS sur l'hôte exécutant l'agent DHCP. " "Supprime l'option '--no-resolv' des arguments de processus dnsmasq. L'ajout " "de programmes de résolution DNS personnalisés à l'option " "'dnsmasq_dns_servers' désactive cette fonction." msgid "End of VLAN range is less than start of VLAN range" msgstr "La fin de la plage de réseaux locaux virtuels est inférieure au début" msgid "End of tunnel range is less than start of tunnel range" msgstr "La fin de la plage de tunnels est inférieure au début" #, python-format msgid "Error %(reason)s while attempting the operation." msgstr "Erreur %(reason)s lors de la tentative d'exécution de l'opération." #, python-format msgid "Error parsing dns address %s" msgstr "Erreur lors de l'analyse syntaxique de l'adresse DNS %s" #, python-format msgid "Error while reading %s" msgstr "Erreur lors de la lecture de %s" #, python-format msgid "" "Exceeded %s second limit waiting for address to leave the tentative state." msgstr "" "Limite dépassée de %s secondes, en attente adresse pour sortie de l'état de " "tentative." msgid "Existing prefixes must be a subset of the new prefixes" msgstr "" "Les préfixes existants doivent être un sous-réseau des nouveaux préfixes" #, python-format msgid "" "Exit code: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" msgstr "" "Code de sortie: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; " "Stderr: %(stderr)s" #, python-format msgid "Extension %(driver)s failed." msgstr "Echec de l'extension %(driver)s. " #, python-format msgid "" "Extension driver %(driver)s required for service plugin %(service_plugin)s " "not found." msgstr "" "Le pilote d'extension %(driver)s requis pour le plugin de service " "%(service_plugin)s est introuvable." msgid "" "Extension to use alongside ml2 plugin's l2population mechanism driver. It " "enables the plugin to populate VXLAN forwarding table." msgstr "" "Extension à utiliser avec le pilote de mécanisme l2population du plug-in " "ml2. Elle permet au plug-in de remplir la table de réacheminement VXLAN." #, python-format msgid "Extension with alias %s does not exist" msgstr "L'extension avec l'alias %s n'existe pas" msgid "Extensions list to use" msgstr "Liste d'extensions à utiliser." #, python-format msgid "Extensions not found: %(extensions)s." msgstr "Extensions non trouvé: %(extensions)s " #, python-format msgid "External IP %s is the same as the gateway IP" msgstr "L'adresse IP externe %s est identique à l'adresse IP de passerelle" #, python-format msgid "Failed rescheduling router %(router_id)s: no eligible l3 agent found." msgstr "" "Echec de la replanification du routeur %(router_id)s : aucun agent l3 " "éligible trouvé." #, python-format msgid "Failed scheduling router %(router_id)s to the L3 Agent %(agent_id)s." msgstr "" "Echec de planification du routeur %(router_id)s vers l'agent L3 %(agent_id)s." #, python-format msgid "Failed to allocate subnet: %(reason)s." msgstr "Échec d'allocation de sous-réseau : %(reason)s." msgid "" "Failed to associate address scope: subnetpools within an address scope must " "have unique prefixes." msgstr "" "Échec de l'association de la portée d'adresse : les pools de sous-réseau au " "sein d'une portée d'adresse doivent avoir des préfixes uniques." #, python-format msgid "" "Failed to create port on network %(network_id)s, because fixed_ips included " "invalid subnet %(subnet_id)s" msgstr "" "Echec de la création de port sur le réseau %(network_id)s car les adresses " "IP fixes incluent le sous-réseau non valide %(subnet_id)s " #, python-format msgid "Failed to locate source for %s." msgstr "Échec pour localiser la source de %s." msgid "Failed to remove supplemental groups" msgstr "Echec de la suppression des groupes supplémentaires" #, python-format msgid "Failed to set gid %s" msgstr "Echec de la définition du GID %s" #, python-format msgid "Failed to set uid %s" msgstr "Echec de la définition de l'UID %s" #, python-format msgid "Failed to set-up %(type)s tunnel port to %(ip)s" msgstr "Echec de la configuration du port de tunnel %(type)s sur %(ip)s" msgid "Failure applying iptables rules" msgstr "Échec lors de la mise à jour des règles iptables" #, python-format msgid "Failure waiting for address %(address)s to become ready: %(reason)s" msgstr "" "Echec lors de l'attente du passage de l'adresse %(address)s à l'état prêt : " "%(reason)s" #, python-format msgid "Field value %s is invalid" msgstr "La valeur du champs %s est non valide." msgid "Flat provider networks are disabled" msgstr "Les réseaux de fournisseurs centralisés sont désactivés" msgid "For TCP/UDP protocols, port_range_min must be <= port_range_max" msgstr "Pour les protocole TCP/UDP, port_range_min doit être <= port_range_max" msgid "Force ip_lib calls to use the root helper" msgstr "Forcez les appels ip_lib à utiliser Root Helper" #, python-format msgid "Found duplicate extension: %(alias)s." msgstr "Extension en double trouvée : %(alias)s." #, python-format msgid "" "Found overlapping allocation pools: %(pool_1)s %(pool_2)s for subnet " "%(subnet_cidr)s." msgstr "" "Chevauchement de pools d'allocation trouvé :%(pool_1)s %(pool_2)s pour le " "sous-réseau %(subnet_cidr)s." msgid "Gateway IP version inconsistent with allocation pool version" msgstr "" "Version IP de passerelle non cohérente avec la version de pool d'allocation" #, python-format msgid "Gateway ip %(ip_address)s conflicts with allocation pool %(pool)s." msgstr "" "Conflits de l'IP passerelle %(ip_address)s avec le pool d'allocation " "%(pool)s." msgid "Gateway is not valid on subnet" msgstr "La passerelle n'est pas valide sur le sous-réseau." msgid "" "Geneve encapsulation header size is dynamic, this value is used to calculate " "the maximum MTU for the driver. This is the sum of the sizes of the outer " "ETH + IP + UDP + GENEVE header sizes. The default size for this field is 50, " "which is the size of the Geneve header without any additional option headers." msgstr "" "La taille de l'en-tête d'encapsulation Geneve est dynamique. Cette valeur " "est utilisée pour calculer la valeur MTU maximum du pilote. Il s'agit de la " "somme des tailles des en-têtes ETH + IP + UDP + GENEVE externes. La taille " "par défaut pour ce champ est de 50, taille de l'en-tête Geneve sans aucun en-" "tête d'option supplémentaire." msgid "" "Group (gid or name) running metadata proxy after its initialization (if " "empty: agent effective group)." msgstr "" "Groupe (UID ou nom) exécutant le proxy de métadonnées après son " "initialisation (si vide : groupe effectif de l'agent)." msgid "Group (gid or name) running this process after its initialization" msgstr "Groupe (UID ou nom) exécutant ce processus après son initialisation" msgid "" "Hostname to be used by the Neutron server, agents and services running on " "this machine. All the agents and services running on this machine must use " "the same host value." msgstr "" "Nom d'hôte qui doit être utilisé par le serveur, les agents et les services " "Neutron qui s'exécutent sur cette machine. Tous les agents et services qui " "s'exécutent sur cette machine doivent utiliser la même valeur d'hôte." #, python-format msgid "" "ICMP code (port-range-max) %(value)s is provided but ICMP type (port-range-" "min) is missing." msgstr "" "Le code ICMP (port-range-max) %(value)s est fourni mais le type ICMP (port-" "range-min) est manquant." msgid "ID of network" msgstr "ID du réseau" msgid "ID of network to probe" msgstr "ID du réseau à sonder" msgid "ID of probe port to delete" msgstr "ID du port sonde à supprimer" msgid "ID of probe port to execute command" msgstr "ID du port sonde pour exécuter la commande" msgid "ID of the router" msgstr "Identifiant du routeur" #, python-format msgid "IP address %(ip)s already allocated in subnet %(subnet_id)s" msgstr "L'adresse IP %(ip)s est déjà dans le sous-réseaux %(subnet_id)s" #, python-format msgid "IP address %(ip)s does not belong to subnet %(subnet_id)s" msgstr "L'adresse IP %(ip)s n'appartient pas au sous-réseau %(subnet_id)s" msgid "IP allocation failed. Try again later." msgstr "Échec de l'allocation IP. Réessayez ultérieurement." msgid "IP allocation requires subnet_id or ip_address" msgstr "L'allocation d'adresse IP requiert subnet_id ou ip_address" #, python-format msgid "" "IPTablesManager.apply failed to apply the following set of iptables rules:\n" "%s" msgstr "" "IPTablesManager.apply n'a pas pu appliquer l'ensemble d'iptables suivant " "iptables :\n" "%s" msgid "IPtables conntrack zones exhausted, iptables rules cannot be applied." msgstr "" "Zones conntrack IPtables épuisées ; impossible d'appliquer les règles " "iptables. " msgid "IPv6 Address Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "Le mode Adresse IPv6 doit être SLAAC ou Sans état pour la délégation de " "préfixe. " msgid "IPv6 RA Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "Le mode RA IPv6 doit être SLAAC ou Sans état pour la délégation de préfixe. " #, python-format msgid "" "IPv6 address %(ip)s cannot be directly assigned to a port on subnet " "%(subnet_id)s as the subnet is configured for automatic addresses" msgstr "" "L'adresse IPv6 %(ip)s ne peut pas être directement affectée à un port du " "sous-réseau%(subnet_id)s car celui-ci est configuré pour l'obtention " "automatique d'adresses " #, python-format msgid "" "IPv6 subnet %s configured to receive RAs from an external router cannot be " "added to Neutron Router." msgstr "" "Le sous-réseau IPv6 %s configuré pour recevoir les avertissements (RA) d'un " "routeur externe ne peut pas être ajouté au routeur Neutron." msgid "" "If True, then allow plugins that support it to create VLAN transparent " "networks." msgstr "" "Si True, autorisez les plug-in qui les prennent en charge pour créer les " "réseaux VLAN transparents." msgid "Illegal IP version number" msgstr "Numéro de version IP illégal" #, python-format msgid "" "Illegal prefix bounds: %(prefix_type)s=%(prefixlen)s, %(base_prefix_type)s=" "%(base_prefixlen)s." msgstr "" "Limites de préfixe non conformes : %(prefix_type)s=%(prefixlen)s, " "%(base_prefix_type)s=%(base_prefixlen)s." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot " "associate with address scope %(address_scope_id)s because subnetpool " "ip_version is not %(ip_version)s." msgstr "" "Association de pool de sous-réseau non conforme : le pool de sous-réseau " "%(subnetpool_id)s ne peut pas s'associer à la portée d'adresse " "%(address_scope_id)s car ip_version du pool de sous-réseau n'est pas " "%(ip_version)s." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot be " "associated with address scope %(address_scope_id)s." msgstr "" "Association de pool de sous-réseau non conforme : le sous-réseau " "%(subnetpool_id)s ne peut pas être associé à la portée d'adresse " "%(address_scope_id)s." #, python-format msgid "Illegal subnetpool update : %(reason)s." msgstr "Mise à jour de pool de sous-réseau non conforme : %(reason)s." #, python-format msgid "Illegal update to prefixes: %(msg)s." msgstr "Mise à jour de préfixes non conforme : %(msg)s." msgid "" "In some cases the Neutron router is not present to provide the metadata IP " "but the DHCP server can be used to provide this info. Setting this value " "will force the DHCP server to append specific host routes to the DHCP " "request. If this option is set, then the metadata service will be activated " "for all the networks." msgstr "" "Dans certains cas, le routeur Neutron n'est pas présent pour fournir l'IP de " "métadonnées mais le serveur DHCP peut être utilisé pour fournir ces " "informations. Lorsque cette valeur est définie, cela force le serveur DHCP à " "ajouter des routes hôtes spécifiques à la demande DHCP. Lorsque cette option " "est définie, le service de métadonnées est activé pour tous les réseaux." msgid "" "Indicates that this L3 agent should also handle routers that do not have an " "external network gateway configured. This option should be True only for a " "single agent in a Neutron deployment, and may be False for all agents if all " "routers must have an external network gateway." msgstr "" "Indique que cet agent L3 doit aussi traiter les routeurs pour lesquels " "aucune passerelle de réseau externe n'est configurée. Cette option doit être " "définie sur True uniquement pour un seul agent dans un déploiement Neutron ; " "elle peut être définie sur False pour tous les agents si tous les routeurs " "doivent avoir une passerelle de réseau externe." #, python-format msgid "Instance of class %(module)s.%(class)s must contain _cache attribute" msgstr "" "L'instance de la classe %(module)s.%(class)s doit contenir l'attribut _cache." #, python-format msgid "Insufficient prefix space to allocate subnet size /%s" msgstr "" "Espace préfixe insuffisant pour l'allocation de la taille de sous-réseau /%s" msgid "Insufficient rights for removing default security group." msgstr "Droits insuffisants pour retirer le groupe de sécurité par défaut." msgid "" "Integration bridge to use. Do not change this parameter unless you have a " "good reason to. This is the name of the OVS integration bridge. There is one " "per hypervisor. The integration bridge acts as a virtual 'patch bay'. All VM " "VIFs are attached to this bridge and then 'patched' according to their " "network connectivity." msgstr "" "Pont d'intégration à utiliser. Ne modifiez pas ce paramètre à moins d'avoir " "une bonne raison pour cela. Il s'agit du nom du pont d'intégration OVS. Il y " "en a un par hyperviseur. Le pont d'intégration fait office de 'baie " "corrective' virtuelle. Tous les VIF de machine virtuelle sont connectés à ce " "pont puis 'corrigés' d'après leur connectivité réseau." msgid "Interface to monitor" msgstr "Interface à surveiller" msgid "" "Interval between checks of child process liveness (seconds), use 0 to disable" msgstr "" "Intervalle entre les vérifications de l'activité du processus enfant (en " "secondes). Utilisez 0 pour désactiver" msgid "Interval between two metering measures" msgstr "Intervalle entre deux mesures" msgid "Interval between two metering reports" msgstr "Intervalle entre deux rapports de mesures" #, python-format msgid "Invalid CIDR %(input)s given as IP prefix." msgstr "CIDR non valide %(input)s fourni comme préfixe IP." #, python-format msgid "Invalid Device %(dev_name)s: %(reason)s" msgstr "Périphérique non valide %(dev_name)s : %(reason)s" #, python-format msgid "" "Invalid action '%(action)s' for object type '%(object_type)s'. Valid " "actions: %(valid_actions)s" msgstr "" "Action non valide %(action)s' pour le type d'objet %(object_type)s'. Actions " "valides : %(valid_actions)s" #, python-format msgid "" "Invalid authentication type: %(auth_type)s, valid types are: " "%(valid_auth_types)s" msgstr "" "Type d'authentification non valide : %(auth_type)s, les types valides sont : " "%(valid_auth_types)s" #, python-format msgid "Invalid ethertype %(ethertype)s for protocol %(protocol)s." msgstr "ethertype %(ethertype)s non valide pour le protocole %(protocol)s." #, python-format msgid "Invalid format: %s" msgstr "Format non valide : %s" #, python-format msgid "Invalid instance state: %(state)s, valid states are: %(valid_states)s" msgstr "" "Etat d'instance non valide : %(state)s, les états valides sont : " "%(valid_states)s" #, python-format msgid "Invalid mapping: '%s'" msgstr "Mappage non valide : '%s'" #, python-format msgid "Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'." msgstr "Plage VLAN de réseau non valide : '%(vlan_range)s' - '%(error)s'." #, python-format msgid "Invalid network VXLAN port range: '%(vxlan_range)s'." msgstr "Réseau non valide pour le range port VXLAN: '%(vxlan_range)s'." #, python-format msgid "Invalid pci slot %(pci_slot)s" msgstr "Port pci invalide %(pci_slot)s" #, python-format msgid "Invalid provider format. Last part should be 'default' or empty: %s" msgstr "" "Format de fournisseur non valide. La dernière partie doit être 'default' ou " "vide : %s" #, python-format msgid "Invalid resource type %(resource_type)s" msgstr "Ressource type %(resource_type)s non valide" #, python-format msgid "Invalid route: %s" msgstr "Chemin non valide : %s" msgid "Invalid service provider format" msgstr "Format de fournisseur de service non valide" #, python-format msgid "" "Invalid value for ICMP %(field)s (%(attr)s) %(value)s. It must be 0 to 255." msgstr "" "Valeur non valide pour ICMP %(field)s (%(attr)s) %(value)s. Elle doit être " "comprise entre 0 et 255." #, python-format msgid "Invalid value for port %(port)s" msgstr "Valeur non valide pour le port %(port)s" msgid "" "Iptables mangle mark used to mark ingress from external network. This mark " "will be masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Marque Mangle Iptables utilisée pour marquer les entrées du réseau externe. " "Cette marque sera masquée avec 0xffff de sorte que seuls les 16 bits les " "plus bas soient utilisés. " msgid "" "Iptables mangle mark used to mark metadata valid requests. This mark will be " "masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Marque Mangle Iptables utilisée pour marquer les demandes valides de " "métadonnées. Cette marque sera masquée avec 0xffff de sorte que seuls les 16 " "bits les plus bas soient utilisés. " msgid "Keepalived didn't respawn" msgstr "Keepalived n'a pas été relancée" msgid "Keepalived didn't spawn" msgstr "Keepalived n'a pas été lancé" #, python-format msgid "" "Kernel HZ value %(value)s is not valid. This value must be greater than 0." msgstr "" "La valeur HZ du noyau %(value)s n'est pas valide. Cette valeur doit être " "supérieure à 0." msgid "L3 agent failure to setup NAT for floating IPs" msgstr "L'agent L3 n'a pas pu configurer NAT pour les IP flottantes" msgid "L3 agent failure to setup floating IPs" msgstr "L'agent L3 n'a pas pu configurer les IP flottantes" msgid "Limit number of leases to prevent a denial-of-service." msgstr "Limiter le nombre de baux pour éviter un déni de service." msgid "List of :" msgstr "Liste de :" msgid "" "List of :: or " "specifying physical_network names usable for VLAN provider and tenant " "networks, as well as ranges of VLAN tags on each available for allocation to " "tenant networks." msgstr "" "Liste de :: ou " "indiquant des noms physical_network utilisables pour les réseaux de " "fournisseurs VLAN et de locataires, ainsi que les plages d'étiquettes VLAN " "disponibles dans le cadre de l'allocation aux réseaux locataires." msgid "" "List of network type driver entrypoints to be loaded from the neutron.ml2." "type_drivers namespace." msgstr "" "Liste des points d'entrées du pilote de type de réseau à charger à partir de " "l'espace de nom neutron.ml2.type_drivers." msgid "" "List of physical_network names with which flat networks can be created. Use " "default '*' to allow flat networks with arbitrary physical_network names. " "Use an empty list to disable flat networks." msgstr "" "Liste de noms de réseau physique qui peuvent être utilisés pour créer des " "réseaux centralisés. Utilisez par défaut '*' pour autoriser les réseaux " "centralisés avec des noms de réseau physique arbitraires. Utilisez une " "liste vide pour désactiver les réseaux centralisés." msgid "Location for Metadata Proxy UNIX domain socket." msgstr "Emplacement du socket de domaine UNIX du proxy de métadonnées." msgid "Location of Metadata Proxy UNIX domain socket" msgstr "Emplacement du socket de domaine UNIX du proxy de métadonnées" msgid "Location to store DHCP server config files." msgstr "Emplacement de stockage des fichiers de configuration du serveur DHCP." msgid "Location to store IPv6 PD files." msgstr "Emplacement pour stocker les fichiers IPv6 PD" msgid "Location to store IPv6 RA config files" msgstr "Emplacement de stockage des fichiers de configuration IPv6 RA" msgid "Location to store child pid files" msgstr "Emplacement de stockage des fichiers de PID enfant" msgid "Location to store keepalived/conntrackd config files" msgstr "" "Emplacement de stockage des fichiers de configuration keepalived/conntrackd" msgid "Log agent heartbeats" msgstr "Consigner les pulsations d'agent" msgid "" "MTU of the underlying physical network. Neutron uses this value to calculate " "MTU for all virtual network components. For flat and VLAN networks, neutron " "uses this value without modification. For overlay networks such as VXLAN, " "neutron automatically subtracts the overlay protocol overhead from this " "value. Defaults to 1500, the standard value for Ethernet." msgstr "" "MTU du réseau physique sous-jacent. Neutron utilise cette valeur pour " "calculer la valeur MTU de tous les composants réseau virtuels. Pour les " "réseaux centralisés et VLAN, Neutron utilise cette valeur sans la modifier. " "Pour les réseaux superposés, tels que VXLAN, Neutron soustrait " "automatiquement la surcharge du protocole de superposition de cette valeur. " "La valeur par défaut est 1500, valeur standard pour Ethernet." msgid "MTU size of veth interfaces" msgstr "Taille de MTU des interfaces veth" msgid "Make the l2 agent run in DVR mode." msgstr "Exécuter l'agent l2 dans le mode DVR." msgid "Malformed request body" msgstr "Format de corps de demande incorrect" #, python-format msgid "Malformed request body: %(reason)s." msgstr "Format de corps de demande incorrect : %(reason)s" msgid "MaxRtrAdvInterval setting for radvd.conf" msgstr "Paramètre MaxRtrAdvInterval pour radvd.conf" msgid "Maximum number of DNS nameservers per subnet" msgstr "Nombre maximum de serveurs de noms DNS par sous-réseau" msgid "" "Maximum number of L3 agents which a HA router will be scheduled on. If it is " "set to 0 then the router will be scheduled on every agent." msgstr "" "Nombre maximum d'agents L3 sur lesquels un routeur HA sera planifié. Si ce " "paramètre est défini sur 0, le routeur sera planifié sur chaque agent." msgid "Maximum number of allowed address pairs" msgstr "Nombre maximal de paires d'adresses autorisé" msgid "Maximum number of host routes per subnet" msgstr "Nombre maximal de routes hôte par sous-réseau" msgid "Maximum number of routes per router" msgstr "Nombre maximum de routes par routeur" msgid "" "Metadata Proxy UNIX domain socket mode, 4 values allowed: 'deduce': deduce " "mode from metadata_proxy_user/group values, 'user': set metadata proxy " "socket mode to 0o644, to use when metadata_proxy_user is agent effective " "user or root, 'group': set metadata proxy socket mode to 0o664, to use when " "metadata_proxy_group is agent effective group or root, 'all': set metadata " "proxy socket mode to 0o666, to use otherwise." msgstr "" "Mode du socket de domaine UNIX de proxy de métadonnées, 4 valeurs " "autorisées : 'deduce' : mode de déduction à partir des valeurs de " "metadata_proxy_user/group, 'user' : mode du socket de proxy de métadonnées " "défini sur 0o644, à utiliser lorsque metadata_proxy_user correspond à " "l'utilisateur ou la racine effectif de l'agent, 'group' : mode du socket de " "proxy de métadonnées défini sur 0o664,à utiliser lorsque " "metadata_proxy_group correspond au groupe ou à la racine effectif de " "l'agent, 'all' : mode du socket de proxy de métadonnées défini sur 0o666, à " "utiliser dans les autres cas." msgid "Metering driver" msgstr "Pilote de décompte" msgid "MinRtrAdvInterval setting for radvd.conf" msgstr "Paramètre MinRtrAdvInterval pour radvd.conf" msgid "Minimize polling by monitoring ovsdb for interface changes." msgstr "" "Réduire au minimum l'interrogation en surveillant les changements " "d'interface de l'ovsdb." #, python-format msgid "Missing key in mapping: '%s'" msgstr "Clé manquante dans le mappage : '%s'" msgid "" "Multicast group for VXLAN. When configured, will enable sending all " "broadcast traffic to this multicast group. When left unconfigured, will " "disable multicast VXLAN mode." msgstr "" "Groupe de multidiffusion pour VXLAN. Lorsque ce paramètre est configuré, il " "permet l'envoi de tout le trafic de diffusion vers ce groupe de " "multidiffusion. Dans le cas contraire, il désactive le mode VXLAN de " "multidiffusion." msgid "" "Multicast group(s) for vxlan interface. A range of group addresses may be " "specified by using CIDR notation. Specifying a range allows different VNIs " "to use different group addresses, reducing or eliminating spurious broadcast " "traffic to the tunnel endpoints. To reserve a unique group for each possible " "(24-bit) VNI, use a /8 such as 239.0.0.0/8. This setting must be the same on " "all the agents." msgstr "" "Groupe(s) de multidiffusion pour l'interface vxlan. Une plage d'adresses de " "groupe peut être spécifiée en utilisant la notation CIDR. Si une plage est " "indiquée, différents VNI peuvent utiliser différentes adresses de groupe, ce " "qui réduit ou élimine le trafic de multidiffusion fallacieux vers les nÅ“uds " "finaux de tunnel. Pour réserver un groupe unique pour chaque VNI possible " "(24 bits), utilisez /8, par exemple 239.0.0.0/8. Ce paramètre doit être " "identique sur tous les agents." #, python-format msgid "Multiple default providers for service %s" msgstr "Fournisseurs multiples par défaut pour le service %s" #, python-format msgid "Multiple plugins for service %s were configured" msgstr "Plusieurs plug-in pour le service %s ont été configurés." #, python-format msgid "Multiple providers specified for service %s" msgstr "Fournisseurs multiples indiqués pour le service %s" msgid "Multiple tenant_ids in bulk security group rule create not allowed" msgstr "" "L'existence de plusieurs ID titulaire n'est pas autorisée lors de la " "création du règle de groupe de sécurité en bloc." msgid "Must also specify protocol if port range is given." msgstr "" "Un protocole doit aussi être précisé si une plage de ports est fournie." msgid "Must specify one or more actions on flow addition or modification" msgstr "" "Doit indiquer une ou plusieurs actions sur l'ajout ou la modification de flux" msgid "Name of Open vSwitch bridge to use" msgstr "Nom du pont Open vSwitch à utiliser" msgid "" "Name of nova region to use. Useful if keystone manages more than one region." msgstr "" "Nom de la région nova à utiliser. Utile si keystone gère plusieurs régions." msgid "Namespace of the router" msgstr "Espace de nom du routeur" msgid "Native pagination depend on native sorting" msgstr "La mise en page native dépend du tri natif" #, python-format msgid "" "Need to apply migrations from %(project)s contract branch. This will require " "all Neutron server instances to be shutdown before proceeding with the " "upgrade." msgstr "" "La migrations doit être appliquée depuis la branche contract %(project)s. " "Cela va impliquer l'arrêt de toutes les instances de serveur Neutron avant " "la mise à niveau." msgid "Negative delta (downgrade) not supported" msgstr "Delta négatif (rétromigration) non pris en charge" msgid "Negative relative revision (downgrade) not supported" msgstr "Révision relative négative (rétromigration) non prise en charge" #, python-format msgid "Network %s does not contain any IPv4 subnet" msgstr "Le réseau %s ne contient pas de sous-réseau IPv4 " #, python-format msgid "Network %s is not a valid external network" msgstr "Le réseau %s n'est pas un réseau externe valide." #, python-format msgid "Network %s is not an external network" msgstr "Réseau %s n'est pas un réseau externe" #, python-format msgid "" "Network of size %(size)s, from IP range %(parent_range)s excluding IP ranges " "%(excluded_ranges)s was not found." msgstr "" "Le réseau de taille %(size)s, de plage IP %(parent_range)s (hors plages IP " "%(excluded_ranges)s) est introuvable." #, python-format msgid "Network type value '%s' not supported" msgstr "Valeur de type de réseau '%s' non prise en charge" msgid "Network type value needed by the ML2 plugin" msgstr "Valeur de type de réseau requise par le plug-in ML2" msgid "Network types supported by the agent (gre and/or vxlan)." msgstr "Types de réseau pris en charge par l'agent (gre et/ou vxlan)." msgid "Neutron Service Type Management" msgstr "Gestion du type de service Neutron" msgid "Neutron core_plugin not configured!" msgstr "Neutron core_plugin n'est pas configuré ! " msgid "No default router:external network" msgstr "Aucun réseau router:external par défaut" #, python-format msgid "No default subnetpool found for IPv%s" msgstr "Aucun pool de sous-réseau par défaut trouvé pour IPv%s" msgid "No default subnetpools defined" msgstr "Aucun pool de sous-réseau défini" #, python-format msgid "No eligible l3 agent associated with external network %s found" msgstr "Aucun agent l3 admissible trouvé associé au réseau %s" #, python-format msgid "No more IP addresses available for subnet %(subnet_id)s." msgstr "" "Pas d'autres adresses IP disponibles pour le sous-réseau %(subnet_id)s." msgid "No offline migrations pending." msgstr "Aucune migration hors ligne en attente." #, python-format msgid "No shared key in %s fields" msgstr "Aucune clé partagée dans les champs %s" msgid "Not allowed to manually assign a router to an agent in 'dvr' mode." msgstr "" "Non autorisé à affecter manuellement un routeur à un agent en mode 'dvr'." msgid "Not allowed to manually remove a router from an agent in 'dvr' mode." msgstr "" "Non autorisé à retirer manuellement un routeur d'un agent en mode 'dvr'." msgid "" "Number of DHCP agents scheduled to host a tenant network. If this number is " "greater than 1, the scheduler automatically assigns multiple DHCP agents for " "a given tenant network, providing high availability for DHCP service." msgstr "" "Nombre d'agents DHCP planifiés pour héberger un réseau titulaire. Si ce " "nombre est supérieur à 1, le planificateur affecte automatiquement plusieurs " "agents DHCP pour un réseau titulaire donné, ce qui fournit de la haute " "disponibilité au service DHCP. " msgid "Number of backlog requests to configure the metadata server socket with" msgstr "" "Nombre de demandes en attente avec lequel configurer le socket du serveur de " "métadonnées" msgid "Number of backlog requests to configure the socket with" msgstr "Nombre de demandes en attente avec lequel configurer le socket" msgid "" "Number of bits in an ipv4 PTR zone that will be considered network prefix. " "It has to align to byte boundary. Minimum value is 8. Maximum value is 24. " "As a consequence, range of values is 8, 16 and 24" msgstr "" "Nombre de bits dans une zone PTR ipv4 qui fera office de préfixe réseau. " "Doit s'aligner sur la frontière de bit. La valeur minimum est 8. La valeur " "maximum est 24. Par conséquent, la plage de valeurs est 8, 16 et 24" msgid "" "Number of bits in an ipv6 PTR zone that will be considered network prefix. " "It has to align to nyble boundary. Minimum value is 4. Maximum value is 124. " "As a consequence, range of values is 4, 8, 12, 16,..., 124" msgstr "" "Nombre de bits dans une zone PTR ipv6 qui fera office de préfixe réseau. " "Doit s'aligner sur la frontière nyble. La valeur minimum est 4. La valeur " "maximum est 124. Par conséquent, la plage de valeurs est 4, 8, 12, 16,..., " "124" msgid "" "Number of floating IPs allowed per tenant. A negative value means unlimited." msgstr "" "Nombre d'adresses IP flottantes autorisées par locataire. Une valeur " "négative signifie illimité." msgid "" "Number of networks allowed per tenant. A negative value means unlimited." msgstr "" "Nombre de réseaux autorisés par le locataire. Une valeur négative signifie " "illimité" msgid "Number of ports allowed per tenant. A negative value means unlimited." msgstr "" "Nombre de ports autorisés par le locataire. Une valeur négative signifie " "illimité" msgid "Number of routers allowed per tenant. A negative value means unlimited." msgstr "" "Nombre de routeurs autorisés par locataire. Une valeur négative signifie " "illimité" msgid "" "Number of seconds between sending events to nova if there are any events to " "send." msgstr "" "Nombre de secondes entre deux envois d'événements à nova s'il y a des " "événements à envoyer." msgid "Number of seconds to keep retrying to listen" msgstr "Nombre de secondes a attendre avant d'essayer d'écouter à nouveau" msgid "" "Number of security groups allowed per tenant. A negative value means " "unlimited." msgstr "" "Nombre de groupes de sécurité autorisés par locataire. Une valeur négative " "signifie illimité." msgid "" "Number of security rules allowed per tenant. A negative value means " "unlimited." msgstr "" "Nombre de règles de sécurité autorisées par locataire. Une valeur négative " "signifie illimité." msgid "" "Number of separate API worker processes for service. If not specified, the " "default is equal to the number of CPUs available for best performance." msgstr "" "Nombre de processus de traitement d'API séparés pour le service. Si ce " "nombre n'est pas spécifié, la valeur par défaut est égale au nombre d'UC " "disponibles pour optimiser les performances. " msgid "" "Number of separate worker processes for metadata server (defaults to half of " "the number of CPUs)" msgstr "" "Nombre de processus de traitement séparés pour le serveur de métadonnées " "(par défaut, la moitié du nombre d'unités centrales)" msgid "Number of subnets allowed per tenant, A negative value means unlimited." msgstr "" "Nombre de sous-réseaux autorisés par le locataire. Une valeur négative " "signifie illimité" msgid "" "Number of threads to use during sync process. Should not exceed connection " "pool size configured on server." msgstr "" "Nombres d'unités d'exécution à utiliser durant le processus de " "synchronisation. Ce nombre ne doit pas être supérieur à la taille de pool de " "connexion configurée sur le serveur." msgid "OK" msgstr "OK" msgid "" "OVS datapath to use. 'system' is the default value and corresponds to the " "kernel datapath. To enable the userspace datapath set this value to 'netdev'." msgstr "" "Chemin de données OVS à utiliser. 'system' est la valeur par défaut et elle " "correspond au chemin de données du noyau. Pour activer le chemin de données " "de l'espace utilisateur, définissez cette valeur sur 'netdev'." msgid "OVS vhost-user socket directory." msgstr "Répertoire de socket OVS vhost-user." #, python-format msgid "Object action %(action)s failed because: %(reason)s." msgstr "L'action de l'objet %(action)s a échoué car : %(reason)s" msgid "Only admin can view or configure quota" msgstr "Seul l'administrateur peut afficher ou configurer des quotas" msgid "Only admin is authorized to access quotas for another tenant" msgstr "" "Seul l'administrateur est autorisé à accéder aux quotas d'un autre locataire" msgid "Only admins can manipulate policies on objects they do not own" msgstr "" "Seuls les administrateurs peuvent gérer des stratégies sur des objets qui ne " "leur appartiennent pas" msgid "Only allowed to update rules for one security profile at a time" msgstr "" "Les règles peuvent être mises à jour pour un seul profil de sécurité à la " "fois." msgid "Only remote_ip_prefix or remote_group_id may be provided." msgstr "Seul remote_ip_prefix ou remote_group_id peut être fourni." msgid "OpenFlow interface to use." msgstr "Interface OpenFlow à utiliser. " #, python-format msgid "" "Operation %(op)s is not supported for device_owner %(device_owner)s on port " "%(port_id)s." msgstr "" "L'opération %(op)s n'est pas prise en charge pour device_owner " "%(device_owner)s sur le port %(port_id)s." #, python-format msgid "Operation not supported on device %(dev_name)s" msgstr "Opération non prise en charge sur le périphérique %(dev_name)s" msgid "" "Ordered list of network_types to allocate as tenant networks. The default " "value 'local' is useful for single-box testing but provides no connectivity " "between hosts." msgstr "" "Liste triée des éléments network_types à allouer en tant que réseaux " "locataires. La valeur par défaut 'local' est utile pour les tests single-box " "mais elle ne fournit aucune connectivité entre les hôtes." msgid "Override the default dnsmasq settings with this file." msgstr "Remplacez les paramètres dnsmasq par défaut par ce fichier." msgid "Owner type of the device: network/compute" msgstr "Type de propriétaire de l'unité : réseau/ordinateur" msgid "POST requests are not supported on this resource." msgstr "Les requêtes POST ne sont pas prises en charge sur cette ressource." #, python-format msgid "Package %s not installed" msgstr "Le package %s n'est pas installé" #, python-format msgid "Parsing bridge_mappings failed: %s." msgstr "Echec de l'analyse syntaxique bridge_mappings : %s." msgid "Password for connecting to designate in admin context" msgstr "" "Mot de passe pour la connexion au réseau désigné dans un contexte admin" msgid "Path to PID file for this process" msgstr "Chemin d'accès au fichier PID pour ce processus" msgid "Path to the router directory" msgstr "Chemin d'accès au répertoire du routeur" msgid "Peer patch port in integration bridge for tunnel bridge." msgstr "" "Port correctif homologue dans le pont d'intégration pour le pont de tunnel." msgid "Peer patch port in tunnel bridge for integration bridge." msgstr "" "Port correctif homologue dans le pont d'intégration tunnel pour le pont " "d'intégration." msgid "Per-tenant subnet pool prefix quota exceeded." msgstr "Quota de préfixes de pool de sous réseau par locataire dépassé." msgid "Phase upgrade options do not accept revision specification" msgstr "" "Les options de mise à niveau de phase n'acceptent pas la spécification de " "révision" msgid "Ping timeout" msgstr "Délai d'expiration de la commande ping" msgid "Plugin does not support updating provider attributes" msgstr "" "Le plug-in ne prend pas en charge la mise à jour des attributs de fournisseur" #, python-format msgid "Port %(id)s does not have fixed ip %(address)s" msgstr "Le port %(id)s ne dispose pas de l'adresse IP fixe %(address)s." #, python-format msgid "Port %(port_id)s is already acquired by another DHCP agent" msgstr "Le port %(port_id)s est déjà acquis par un autre agent DHCP" #, python-format msgid "" "Port %s has multiple fixed IPv4 addresses. Must provide a specific IPv4 " "address when assigning a floating IP" msgstr "" "Le port %s comporte plusieurs adresses IPv4 fixes. Une adresse IPv4 " "spécifique doit être fournie lors de l'affectation d'une adresse IP " "flottante." msgid "" "Port to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "Port à utiliser pour l'écoute des connexions OpenFlow. Utilisé uniquement " "pour le pilote 'natif'." #, python-format msgid "Prefix '%(prefix)s' not supported in IPv%(version)s pool." msgstr "Préfixe '%(prefix)s' non pris en charge dans le pool IPv%(version)s." msgid "Prefix Delegation can only be used with IPv6 subnets." msgstr "" "La délégation de préfixe peut uniquement être utilisée avec des sous-réseaux " "IPv6. " msgid "Private key of client certificate." msgstr "Clé privée pour le certificat client." #, python-format msgid "Probe %s deleted" msgstr "Sonde %s supprimée" #, python-format msgid "Probe created : %s " msgstr "Sonde créée : %s " msgid "Process is already started" msgstr "Le processus est déjà démarré" msgid "Process is not running." msgstr "Le processus n'est pas en fonctionnement." msgid "Protocol to access nova metadata, http or https" msgstr "Protocole d'accès aux métadonnées de nova, HTTP ou https" #, python-format msgid "Provider name %(name)s is limited by %(len)s characters" msgstr "Le nom de fournisseur %(name)s est limité à %(len)s caractères" #, python-format msgid "QoS Policy %(policy_id)s is used by %(object_type)s %(object_id)s." msgstr "" "Stratégie QoS %(policy_id)s est utilisée par %(object_type)s %(object_id)s." #, python-format msgid "" "QoS binding for network %(net_id)s and policy %(policy_id)s could not be " "found." msgstr "" "La liaison QoS du réseau %(net_id)s et de la stratégie %(policy_id)s est " "introuvable." #, python-format msgid "" "QoS binding for port %(port_id)s and policy %(policy_id)s could not be found." msgstr "" "La liaison QoS du port %(port_id)s et de la stratégie %(policy_id)s est " "introuvable." #, python-format msgid "QoS policy %(policy_id)s could not be found." msgstr "La politique de QoS %(policy_id)s est introuvable." #, python-format msgid "QoS rule %(rule_id)s for policy %(policy_id)s could not be found." msgstr "" "La règle QoS %(rule_id)s pour la politique %(policy_id)s est inexistante." #, python-format msgid "RBAC policy of type %(object_type)s with ID %(id)s not found" msgstr "" "La stratégie RBAC de type %(object_type)s avec l'ID %(id)s est introuvable" #, python-format msgid "" "RBAC policy on object %(object_id)s cannot be removed because other objects " "depend on it.\n" "Details: %(details)s" msgstr "" "La stratégie RBAC sur l'objet %(object_id)s ne peut pas être retirée car " "d'autres objets en dépendent.\n" "Détails : %(details)s" msgid "" "Range of seconds to randomly delay when starting the periodic task scheduler " "to reduce stampeding. (Disable by setting to 0)" msgstr "" "Intervalle en secondes de retard au hasard lors du démarrage du " "planificateur de tâches périodiques de manière à réduire les encombrements " "(définissez ce chiffre sur 0 pour désactiver cette fonction)." msgid "Ranges must be in the same IP version" msgstr "Les plages doivent être dans la même version IP" msgid "Ranges must be netaddr.IPRange" msgstr "Les plages doivent être netaddr.IPRange" msgid "Ranges must not overlap" msgstr "Les plages ne doivent pas se chevaucher" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.EUI type." msgstr "Type '%(type)s' et valeur '%(value)s' reçus. Type netaddr.EUI reçu." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPAddress " "type." msgstr "" "Type '%(type)s' et valeur '%(value)s' reçus. Type netaddr.IPAddress attendu." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPNetwork " "type." msgstr "" "Type '%(type)s' et valeur '%(value)s' reçus. Type netaddr.IPNetwork attendu." #, python-format msgid "" "Release aware branch labels (%s) are deprecated. Please switch to expand@ " "and contract@ labels." msgstr "" "Les libellés de branche orientés édition (%s) sont obsolètes. Passez aux " "libellés expand@ et contract@. " msgid "Remote metadata server experienced an internal server error." msgstr "" "Le serveur de métadonnées distant a subi une erreur de serveur interne." msgid "" "Repository does not contain HEAD files for contract and expand branches." msgstr "" "Le référentiel ne contient pas les fichiers HEAD pour les branches contract " "et expand." msgid "" "Representing the resource type whose load is being reported by the agent. " "This can be \"networks\", \"subnets\" or \"ports\". When specified (Default " "is networks), the server will extract particular load sent as part of its " "agent configuration object from the agent report state, which is the number " "of resources being consumed, at every report_interval.dhcp_load_type can be " "used in combination with network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler When the network_scheduler_driver is " "WeightScheduler, dhcp_load_type can be configured to represent the choice " "for the resource being balanced. Example: dhcp_load_type=networks" msgstr "" "Représentation du type de ressource dont la charge est signalée par l'agent. " "Il peut s'agir de \"réseaux\", \"sous-réseaux\" ou \"ports\". Lorsqu'il est " "spécifié (la valeur par défaut est réseaux), le serveur extrait la charge " "particulière envoyée en tant que composant de son objet de configuration " "d'agent depuis l'état de rapport d'agent, qui correspond au nombre de " "ressources consommées, à chaque intervalle report_interval.dhcp_load_type, " "et pouvant être utilisées en combinaison avec network_scheduler_driver = " "neutron.scheduler.dhcp_agent_scheduler.WeightScheduler Lorsque " "network_scheduler_driver est WeightScheduler, dhcp_load_type peut être " "configuré pour représenter le choix pour la ressource équilibrée. Exemple : " "dhcp_load_type=networks" msgid "Request Failed: internal server error while processing your request." msgstr "" "Echec de la demande : erreur de serveur interne lors du traitement de votre " "demande." msgid "" "Reset flow table on start. Setting this to True will cause brief traffic " "interruption." msgstr "" "Réinitialiser la table de flux au démarrage. Affecter la valeur True à ce " "paramètre entraîne une courte interruption du trafic." #, python-format msgid "Resource %(resource)s %(resource_id)s could not be found." msgstr "La ressource %(resource)s %(resource_id)s est introuvable." #, python-format msgid "Resource %(resource_id)s of type %(resource_type)s not found" msgstr "Ressource %(resource_id)s de type %(resource_type)s non trouvée." #, python-format msgid "" "Resource '%(resource_id)s' is already associated with provider " "'%(provider)s' for service type '%(service_type)s'" msgstr "" "La ressource '%(resource_id)s' est déjà associée au fournisseur " "'%(provider)s' pour le type de service '%(service_type)s'" msgid "Resource body required" msgstr "Corps de ressource obligatoire" msgid "Resource not found." msgstr "Ressource non trouvé." msgid "Resources required" msgstr "Ressources obligatoires" msgid "" "Root helper application. Use 'sudo neutron-rootwrap /etc/neutron/rootwrap." "conf' to use the real root filter facility. Change to 'sudo' to skip the " "filtering and just run the command directly." msgstr "" "Application d'assistant racine. Utilisez 'sudo neutron-rootwrap /etc/neutron/" "rootwrap.conf' pour utiliser la véritable fonction de filtre racine. " "Remplacez par 'sudo' pour ignorer le filtrage et exécuter simplement la " "commande directement." msgid "Root permissions are required to drop privileges." msgstr "Les droits root sont obligatoires pour supprimer des privilèges." #, python-format msgid "Router '%(router_id)s' is not compatible with this agent." msgstr "Le routeur '%(router_id)s' n'est pas compatible avec cet agent." #, python-format msgid "Router already has a port on subnet %s" msgstr "Le routeur dispose déjà d'un port sur le sous-réseau %s." msgid "Router port must have at least one fixed IP" msgstr "Le port de routeur doit avoir au moins une IP fixe" #, python-format msgid "Running %(cmd)s (%(desc)s) for %(project)s ..." msgstr "Exécution de %(cmd)s (%(desc)s) pour %(project)s... " #, python-format msgid "Running %(cmd)s for %(project)s ..." msgstr "Exécution de %(cmd)s pour %(project)s... " msgid "" "Seconds between nodes reporting state to server; should be less than " "agent_down_time, best if it is half or less than agent_down_time." msgstr "" "Secondes entre les noeuds signalant l'état au serveur ; cette valeur doit " "être inférieure à agent_down_time, et au mieux, inférieure ou égale à la " "moitié de agent_down_time." msgid "" "Seconds to regard the agent is down; should be at least twice " "report_interval, to be sure the agent is down for good." msgstr "" "Nombre de secondes avant de considérer que l'agent est arrêté ; cette valeur " "doit être au moins le double de report_interval, pour s'assurer que l'agent " "est effectivement arrêté." #, python-format msgid "Security Group %(id)s %(reason)s." msgstr "Groupe de sécurité %(id)s %(reason)s." #, python-format msgid "Security Group Rule %(id)s %(reason)s." msgstr "Règle de groupe de sécurité %(id)s %(reason)s." #, python-format msgid "Security group %(id)s does not exist" msgstr "Le groupe de sécurité %(id)s n'existe pas." #, python-format msgid "Security group rule %(id)s does not exist" msgstr "La règle de groupe de sécurité %(id)s n'existe pas." #, python-format msgid "Security group rule already exists. Rule id is %(rule_id)s." msgstr "" "Une règle de groupe de sécurité existe déjà. L'ID règle est %(rule_id)s." #, python-format msgid "" "Security group rule for ethertype '%(ethertype)s' not supported. Allowed " "values are %(values)s." msgstr "" "Règle de groupe de sécurité pour ethertype '%(ethertype)s' non prise en " "charge. Les valeurs autorisées sont %(values)s." #, python-format msgid "" "Security group rule protocol %(protocol)s not supported. Only protocol " "values %(values)s and integer representations [0 to 255] are supported." msgstr "" "Le protocole %(protocol)s de la règle du groupe de sécurité n'est pas pris " "en charge. Seules les valeurs de protocole Les valeurs %(values)s et les " "représentations sous forme d'entier [0 à 255] sont prises en charge." msgid "Segments and provider values cannot both be set." msgstr "" "Il n'est pas possible de définir à la fois des segments et des valeurs de " "fournisseur." msgid "Selects the Agent Type reported" msgstr "Sélectionne le type d'agent signalé" msgid "" "Send notification to nova when port data (fixed_ips/floatingip) changes so " "nova can update its cache." msgstr "" "Envoyer une notification à nova lors de la modification des données de port " "(fixed_ips/floatingip) pour que nova puisse mettre à jour son cache." msgid "Send notification to nova when port status changes" msgstr "" "Envoyer une notification à nova lors de la modification du statut de port" #, python-format msgid "" "Service provider '%(provider)s' could not be found for service type " "%(service_type)s" msgstr "" "Fournisseur de services '%(provider)s' introuvable pour le type de service " "%(service_type)s" msgid "Service to handle DHCPv6 Prefix delegation." msgstr "Service de traitement de la délégation de préfixe DHCPv6. " #, python-format msgid "Service type %(service_type)s does not have a default service provider" msgstr "" "Le type de service %(service_type)s ne possède pas de fournisseur de " "services par défaut" msgid "" "Set new timeout in seconds for new rpc calls after agent receives SIGTERM. " "If value is set to 0, rpc timeout won't be changed" msgstr "" "Redéfinir le délai d'attente (en secondes) des nouveaux appels RPC observé " "une fois que l'agent a reçu SIGTERM. Si la valeur est définie sur 0, le " "délai d'attente RPC reste inchangé" msgid "" "Set or un-set the don't fragment (DF) bit on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "Définissez ou annulez la définition du bit de fragment sur le paquet IP " "sortant véhiculant le tunnel GRE/VXLAN." msgid "" "Set or un-set the tunnel header checksum on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "Définir ou annuler la définition du total de contrôle de l'en-tête de tunnel " "sur un paquet IP en cours qui transporte le tunnel GRE/VXLAN. " msgid "Shared address scope can't be unshared" msgstr "Impossible d'annuler le partage d'une portée d'adresse partagée" msgid "String prefix used to match IPset names." msgstr "Préfixe de chaîne utilisé pour correspondre aux noms IPset." #, python-format msgid "Sub-project %s not installed." msgstr "Le sous projet %s n'est pas installé." msgid "Subnet for router interface must have a gateway IP" msgstr "" "Le sous-réseau de l'interface de routeur doit avoir une adresse IP " "passerelle." #, python-format msgid "Subnet pool %(subnetpool_id)s could not be found." msgstr "Le pool de sous-réseaux %(subnetpool_id)s est introuvable." msgid "Subnet pool has existing allocations" msgstr "Le pool de sous-réseau dispose d'allocations existantes" msgid "Subnet used for the l3 HA admin network." msgstr "" "Sous-réseau utilisé pour le réseau administrateur haute disponibilité L3." msgid "" "Subnets hosted on the same network must be allocated from the same subnet " "pool." msgstr "" "Les sous-réseaux hébergés sur le même réseau doivent être alloués à partir " "du même pool de sous-réseau." msgid "" "System-wide flag to determine the type of router that tenants can create. " "Only admin can override." msgstr "" "Indicateur système pour déterminer le type de router que les locataires " "peuvent créer. Seul l'administrateur peut outrepasser cela" msgid "TCP Port used by Neutron metadata namespace proxy." msgstr "Port TCP utilisé par le proxy d'espace de nom de métadonnées Neutron" msgid "TCP Port used by Nova metadata server." msgstr "Port TCP utilisé par le serveur de métadonnées Nova" msgid "TTL for vxlan interface protocol packets." msgstr "Durée de vie pour les paquets du protocole d'interface vxlan." #, python-format msgid "Tag %(tag)s could not be found." msgstr "Tag %(tag)s introuvable." #, python-format msgid "Tenant %(tenant_id)s not allowed to create %(resource)s on this network" msgstr "" "Titulaire %(tenant_id)s non autorisé à créer %(resource)s sur ce réseau" msgid "Tenant id for connecting to designate in admin context" msgstr "" "ID locataire pour la connexion au réseau désigné dans un contexte admin" msgid "Tenant name for connecting to designate in admin context" msgstr "" "Nom de locataire pour la connexion au réseau désigné dans un contexte admin" msgid "Tenant network creation is not enabled." msgstr "La création de réseau titulaire n'est pas activée." msgid "Tenant-id was missing from quota request." msgstr "ID titulaire manquant dans la demande de quota." msgid "" "The 'gateway_external_network_id' option must be configured for this agent " "as Neutron has more than one external network." msgstr "" "L'option 'gateway_external_network_id' doit être configuré pour cet agent " "car Neutron a plus d'un réseau externe." msgid "" "The DHCP agent will resync its state with Neutron to recover from any " "transient notification or RPC errors. The interval is number of seconds " "between attempts." msgstr "" "L'agent DHCP va resynchroniser son état avec Neutron afin de récupérer après " "une notification transitoire ou des erreurs RPC. L'intervalle est le nombre " "de secondes entre les tentatives." msgid "" "The DHCP server can assist with providing metadata support on isolated " "networks. Setting this value to True will cause the DHCP server to append " "specific host routes to the DHCP request. The metadata service will only be " "activated when the subnet does not contain any router port. The guest " "instance must be configured to request host routes via DHCP (Option 121). " "This option doesn't have any effect when force_metadata is set to True." msgstr "" "Le serveur DHCP peut contribuer à fournir un support de métadonnées sur des " "réseaux isolés. Si cette valeur est définie sur True, le serveur DHCP ajoute " "des routes hôtes spécifiques à la demande DHCP. Le service de métadonnées " "n'est activé que lorsque le sous-réseau ne contient aucun port de routeur. " "L'instance invitée doit être configurée pour la demande de routes hôtes via " "DHCP (Option 121). Cette option n'a aucun effet lorsque force_metadata est " "défini sur True." msgid "The UDP port to use for VXLAN tunnels." msgstr "Port UDP a utiliser pour les tunnels VXLAN." #, python-format msgid "" "The address allocation request could not be satisfied because: %(reason)s" msgstr "" "Impossible de répondre à la demande d'allocation d'adresse. Motif : " "%(reason)s" msgid "The advertisement interval in seconds" msgstr "Intervalle de publication en secondes" #, python-format msgid "The allocation pool %(pool)s is not valid." msgstr "Le pool d'allocation %(pool)s n'est pas valide." #, python-format msgid "" "The allocation pool %(pool)s spans beyond the subnet cidr %(subnet_cidr)s." msgstr "" "Le pool d'allocation %(pool)s s'étend au-delà du routage CIDR de sous-réseau " "%(subnet_cidr)s." msgid "" "The base MAC address Neutron will use for VIFs. The first 3 octets will " "remain unchanged. If the 4th octet is not 00, it will also be used. The " "others will be randomly generated." msgstr "" "Adresse MAC de base que Neutron va utiliser pour les VIF. Les 3 premiers " "octets demeurent inchangés. Si le 4e octet est différent de 00, il sera " "également utilisé. Les autres seront générés de manière aléatoire." msgid "" "The base mac address used for unique DVR instances by Neutron. The first 3 " "octets will remain unchanged. If the 4th octet is not 00, it will also be " "used. The others will be randomly generated. The 'dvr_base_mac' *must* be " "different from 'base_mac' to avoid mixing them up with MAC's allocated for " "tenant ports. A 4 octet example would be dvr_base_mac = fa:16:3f:4f:00:00. " "The default is 3 octet" msgstr "" "Adresse MAC de base utilisée pour les instances DVR uniques par Neutron. Les " "3 premiers octets restent inchangés. Si le 4ème octet est différent de 00, " "il sera également utilisé. Les autres seront générés de manière aléatoire. " "L'adresse 'dvr_base_mac' *doit* être différente de l'adresse 'base_mac' pour " "éviter de les confondre avec les adresses MAC allouées pour les ports " "titulaires. 3 octets sont utilisés par défaut. " msgid "The core plugin Neutron will use" msgstr "Le core plugin de Neutron va etre utiliser" msgid "The driver used to manage the DHCP server." msgstr "Pilote utilisé pour gérer le serveur DHCP" msgid "The driver used to manage the virtual interface." msgstr "Pilote utilisé pour gérer l'interface virtuelle" msgid "" "The email address to be used when creating PTR zones. If not specified, the " "email address will be admin@" msgstr "" "Adresse e-mail à utiliser lors de la création de zones PTR. Si elle n'est " "pas indiquée, il s'agira de l'adresse admin@" #, python-format msgid "" "The following device_id %(device_id)s is not owned by your tenant or matches " "another tenants router." msgstr "" "Le device_id %(device_id)s suivant n'appartient pas à votre locataire ou " "correspond au routeur d'un autre locataire." msgid "The interface for interacting with the OVSDB" msgstr "Interface d'interaction avec OVSDB" msgid "" "The maximum number of items returned in a single response, value was " "'infinite' or negative integer means no limit" msgstr "" "Nombre maximal d'éléments renvoyés dans une seule réponse, valeur définie " "sur 'infinite' ou sur un entier négatif qui signifie illimité" #, python-format msgid "" "The network %(network_id)s has been already hosted by the DHCP Agent " "%(agent_id)s." msgstr "" "Le réseau %(network_id)s est déjà hébergé par l'agent DHCP %(agent_id)s." #, python-format msgid "" "The network %(network_id)s is not hosted by the DHCP agent %(agent_id)s." msgstr "" "Le réseau %(network_id)s n'est pas hébergé par l'agent DHCP %(agent_id)s." msgid "" "The network type to use when creating the HA network for an HA router. By " "default or if empty, the first 'tenant_network_types' is used. This is " "helpful when the VRRP traffic should use a specific network which is not the " "default one." msgstr "" "Type de réseau à utiliser lors de la création du réseau haute disponibilité " "pour un routeur haute disponibilité. Par défaut ou si cette zone est vide, " "le premier élément 'tenant_network_types' est utilisé. Cela s'avère utile " "lorsque le trafic VRRP doit utiliser un réseau spécifique différent de celui " "défini par défaut. " msgid "" "The number of seconds the agent will wait between polling for local device " "changes." msgstr "" "Temps en secondes pendant lequel l'agent attendra les interrogations sur les " "modifications de l'unité locale." msgid "" "The number of seconds to wait before respawning the ovsdb monitor after " "losing communication with it." msgstr "" "Le nombre de secondes d'attente avant de régénérer le moniteur ovsdb après " "avoir perdu la communication avec ce dernier." msgid "The number of sort_keys and sort_dirs must be same" msgstr "" "Le nombre de clés de tri (sort_keys) et de répertoires de tri (sort_dirs) " "doit être identique" msgid "" "The path for API extensions. Note that this can be a colon-separated list of " "paths. For example: api_extensions_path = extensions:/path/to/more/exts:/" "even/more/exts. The __path__ of neutron.extensions is appended to this, so " "if your extensions are in there you don't need to specify them here." msgstr "" "Chemin des extensions API. Notez qu'il peut s'agir d'une liste de chemins " "séparés par des virgules. Par exemple : api_extensions_path = extensions:/" "path/to/more/exts:/even/more/exts. Le __chemin__ de neutron.extensions lui " "est ajouté, de sorte que si vos extensions figurent dans ce chemin, vous " "n'avez pas besoin de les indiquer ici." msgid "The physical network name with which the HA network can be created." msgstr "" "Nom de réseau physique avec lequel le réseau haute disponibilité peut être " "créé. " #, python-format msgid "The port '%s' was deleted" msgstr "Le port '%s' a été supprimé" msgid "The port to bind to" msgstr "Port à connecter" #, python-format msgid "The requested content type %s is invalid." msgstr "Le type de contenu %s de la requete est invalide." msgid "The resource could not be found." msgstr "La ressource est introuvable." #, python-format msgid "" "The router %(router_id)s has been already hosted by the L3 Agent " "%(agent_id)s." msgstr "Le routeur %(router_id)s est déjà hébergé par l'agent L3 %(agent_id)s." msgid "" "The server has either erred or is incapable of performing the requested " "operation." msgstr "" "Le serveur a perdu la connexion ou est incapable d'effectuer l'opération " "demandée." msgid "The service plugins Neutron will use" msgstr "Plug-in de service utilisés ultérieurement par Neutron" #, python-format msgid "The subnet request could not be satisfied because: %(reason)s" msgstr "Impossible de répondre à la demande de sous-réseau. Motif : %(reason)s" #, python-format msgid "The subproject to execute the command against. Can be one of: '%s'." msgstr "" "Sous-projet sur lequel la commande doit être exécutée. Valeurs possibles : " "'%s'." msgid "The type of authentication to use" msgstr "Type d'authentification à utiliser" msgid "" "There are routers attached to this network that depend on this policy for " "access." msgstr "" "Certains routeurs connectés à ce réseau dépendent de cette stratégie pour " "l'accès." msgid "" "Timeout in seconds to wait for a single OpenFlow request. Used only for " "'native' driver." msgstr "" "Délai d'attente en secondes pour une seule demande OpenFlow. Utilisé " "uniquement pour le pilote 'natif'. " msgid "" "Timeout in seconds to wait for the local switch connecting the controller. " "Used only for 'native' driver." msgstr "" "Délai d'attente en secondes pour la connexion du commutateur local au " "contrôleur. Utilisé uniquement pour le pilote 'natif'. " msgid "" "Too long prefix provided. New name would exceed given length for an " "interface name." msgstr "" "Le préfixe fourni est trop long. Un nouveau nom dépasserait la longueur " "indiquée pour un nom d'interface." msgid "" "True to delete all ports on all the OpenvSwitch bridges. False to delete " "ports created by Neutron on integration and external network bridges." msgstr "" "La valeur est vraie pour la suppression de tous les ports sur tous les ponts " "OpenvSwitch. Elle est fausse pour la suppression des ports créés par Neutron " "lors de l'intégration et des ponts de réseau externes." msgid "Tunnel IP value needed by the ML2 plugin" msgstr "Valeur IP de tunnel requise par le plug-in ML2" msgid "Tunnel bridge to use." msgstr "Pont de tunnel à utiliser." msgid "" "Type of the nova endpoint to use. This endpoint will be looked up in the " "keystone catalog and should be one of public, internal or admin." msgstr "" "Type de nÅ“ud final Nova à utiliser. Ce nÅ“ud final sera recherché dans le " "catalogue Keystone et il doit être de type public, interne ou admin." msgid "URL for connecting to designate" msgstr "URL pour la connexion au réseau désigné" msgid "URL to database" msgstr "URL de la base de données" #, python-format msgid "Unable to access %s" msgstr "Impossible d'accéder à %s" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, maximum allowed " "prefix is %(max_prefixlen)s." msgstr "" "Impossible d'allouer le sous-réseau avec la longueur de préfixe " "%(prefixlen)s, la longueur de préfixe maximum autorisée est de " "%(max_prefixlen)s." #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, minimum allowed " "prefix is %(min_prefixlen)s." msgstr "" "Impossible d'allouer le sous-réseau avec la longueur de préfixe " "%(prefixlen)s, la longueur de préfixe minimum autorisée est de " "%(min_prefixlen)s." #, python-format msgid "Unable to calculate %(address_type)s address because of:%(reason)s" msgstr "Impossible de calculer l'adresse %(address_type)s. Motif : %(reason)s" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of DNS " "nameservers exceeds the limit %(quota)s." msgstr "" "Impossible de terminer l'opération pour le sous-réseau %(subnet_id)s. Le " "nombre de serveurs DNS dépasse la limite %(quota)s." #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of host routes " "exceeds the limit %(quota)s." msgstr "" "Impossible de terminer l'opération pour le sous-réseau %(subnet_id)s. Le " "nombre de routes hôtes dépasse la limite %(quota)s." #, python-format msgid "Unable to convert value in %s" msgstr "Impossible de convertir la valeur en %s" msgid "Unable to create the Agent Gateway Port" msgstr "Impossible de créer le port de passerelle d'agent" msgid "Unable to create the SNAT Interface Port" msgstr "Impossible de créer l'interface du port SNAT" #, python-format msgid "" "Unable to create the flat network. Physical network %(physical_network)s is " "in use." msgstr "" "Impossible de créer le réseau centralisé. Le réseau physique " "%(physical_network)s est en cours d'utilisation " msgid "" "Unable to create the network. No available network found in maximum allowed " "attempts." msgstr "" "Impossible de créer le réseau. Aucun réseau disponible trouvé dans le " "maximum de tentatives autorisées." #, python-format msgid "Unable to delete subnet pool: %(reason)s." msgstr "Impossible de supprimer le pool de sous-réseau : %(reason)s." #, python-format msgid "Unable to determine mac address for %s" msgstr "Impossible de déterminer l'adresse mac pour %s" #, python-format msgid "Unable to find '%s' in request body" msgstr "Impossible de trouver '%s' dans la corps de demande" #, python-format msgid "Unable to find IP address %(ip_address)s on subnet %(subnet_id)s" msgstr "" "Impossible de trouver l'adresse IP %(ip_address)s dans le sous réseau " "%(subnet_id)s" #, python-format msgid "Unable to find resource name in %s" msgstr "Impossible de trouver le nom de la ressource dans %s" #, python-format msgid "Unable to generate unique mac on network %(net_id)s." msgstr "Impossible de générer une adresse MAC unique sur le réseau %(net_id)s." #, python-format msgid "" "Unable to identify a target field from:%s. Match should be in the form " "%%()s" msgstr "" "Impossible d'identifier une zone cible à partir de : %s. La correspondance " "doit être au format %%()s" msgid "Unable to provide external connectivity" msgstr "Impossible de fournir une connectivité externe" msgid "Unable to provide tenant private network" msgstr "Impossible de fournir un réseau privé locataire" #, python-format msgid "" "Unable to reconfigure sharing settings for network %(network)s. Multiple " "tenants are using it." msgstr "" "Impossible de reconfigurer les paramètres de partage pour le réseau " "%(network)s. Plusieurs locataires l'utilisent." #, python-format msgid "" "Unable to verify match:%(match)s as the parent resource: %(res)s was not " "found" msgstr "" "Impossible de vérifier la correspondance %(match)s comme ressource parent : " "%(res)s n'a pas été trouvée" #, python-format msgid "Unexpected label for script %(script_name)s: %(labels)s" msgstr "Libellé inattendu pour le script %(script_name)s : %(labels)s" #, python-format msgid "Unexpected number of alembic branch points: %(branchpoints)s" msgstr "Nombre de points de branche alembic inattendu : %(branchpoints)s" #, python-format msgid "Unexpected response code: %s" msgstr "Code de réponse inattendu : %s" #, python-format msgid "Unexpected response: %s" msgstr "Réponse inattendue : %s" #, python-format msgid "Unit name '%(unit)s' is not valid." msgstr "Le nom d'unité '%(unit)s' n'est pas valide." #, python-format msgid "Unknown address type %(address_type)s" msgstr "Type d'adresse inconnu %(address_type)s" #, python-format msgid "Unknown attribute '%s'." msgstr "Attribut inconnu '%s'." #, python-format msgid "Unknown chain: %r" msgstr "Chaîne inconnue : %r" #, python-format msgid "Unknown network type %(network_type)s." msgstr "Type de réseau inconnu %(network_type)s." #, python-format msgid "Unknown quota resources %(unknown)s." msgstr "Ressources de quota inconnues %(unknown)s." msgid "Unmapped error" msgstr "Erreur de non-correspondance" msgid "Unrecognized action" msgstr "Action inconnu" #, python-format msgid "Unrecognized attribute(s) '%s'" msgstr "Attribut(s) non reconnu(s) '%s'" msgid "Unrecognized field" msgstr "Champ non reconnu" msgid "Unsupported Content-Type" msgstr "Type de contenu non pris en charge" #, python-format msgid "Unsupported network type %(net_type)s." msgstr "Le type de réseau %(net_type)s n'est pas pris en charge." #, python-format msgid "Unsupported port state: %(port_state)s." msgstr "L'état du port n'est pas supporté: %(port_state)s." msgid "Unsupported request type" msgstr "Type de demande non pris en charge" msgid "Updating default security group not allowed." msgstr "Mise à jour du groupe de sécurité par défaut non autorisée" msgid "" "Use ML2 l2population mechanism driver to learn remote MAC and IPs and " "improve tunnel scalability." msgstr "" "Utilisez le pilote de mécanisme l2population ML2 pour connaître les adresses " "MAC et IP et pour améliorer l'évolutivité du tunnel." msgid "Use broadcast in DHCP replies." msgstr "Utilisez la diffusion dans les réponses DHCP." msgid "Use either --delta or relative revision, not both" msgstr "" "Utiliser soit un --delta, soit une révision relative, mais pas les deux" msgid "" "Use ipset to speed-up the iptables based security groups. Enabling ipset " "support requires that ipset is installed on L2 agent node." msgstr "" "Utiliser ipset pour accélérer les groupes de sécurité basés sur iptables. " "L'activation du support ipset nécessite l'installation d'ipset sur le noeud " "d'agent L2." msgid "" "Use the root helper when listing the namespaces on a system. This may not be " "required depending on the security configuration. If the root helper is not " "required, set this to False for a performance improvement." msgstr "" "Utilisez l'assistant racine lors de l'affichage de la liste des espaces de " "noms sur un système. Cette opération n'est peut-être pas obligatoire selon " "la configuration de sécurité. Si l'assistant racine n'est pas requis, " "définissez cette option sur False afin d'améliorer les performances." msgid "" "Use veths instead of patch ports to interconnect the integration bridge to " "physical networks. Support kernel without Open vSwitch patch port support so " "long as it is set to True." msgstr "" "Utilisez veths au lieu de ports de correctif pour interconnecter le pont " "d'intégration avec des réseaux physiques. Le noyau sans port de correctif " "Open vSwitch est pris en charge si le paramètre est défini sur True." msgid "" "User (uid or name) running metadata proxy after its initialization (if " "empty: agent effective user)." msgstr "" "Utilisateur (UID ou nom) exécutant le proxy de métadonnées après son " "initialisation (si vide : utilisateur effectif de l'agent)." msgid "User (uid or name) running this process after its initialization" msgstr "Utilisateur (UID ou nom) exécutant ce process après son initialisation" msgid "Username for connecting to designate in admin context" msgstr "" "Nom d'utilisateur pour la connexion au réseau désigné dans un contexte admin" msgid "VRRP authentication password" msgstr "Mot de passe pour l'authentification VRRP" msgid "VRRP authentication type" msgstr "Type d'authentification VRRP" msgid "VXLAN network unsupported." msgstr "Réseau VXLAN non supporté." msgid "" "Value of host kernel tick rate (hz) for calculating minimum burst value in " "bandwidth limit rules for a port with QoS. See kernel configuration file for " "HZ value and tc-tbf manual for more information." msgstr "" "Valeur du rythme de noyau hôte (hz) pour le calcul de la valeur de rafale " "minimum dans les règles de limite de bande passante pour un port avec QoS. " "Consultez le fichier de configuration du noyau pour la valeur HZ et le " "manuel tc-tbf pour plus d'informations." msgid "" "Value of latency (ms) for calculating size of queue for a port with QoS. See " "tc-tbf manual for more information." msgstr "" "Valeur de latence (ms) pour le calcul de la taille de file d'attente d'un " "port avec QoS. Consultez le manuel tc-tbf pour plus d'informations." msgid "" "When external_network_bridge is set, each L3 agent can be associated with no " "more than one external network. This value should be set to the UUID of that " "external network. To allow L3 agent support multiple external networks, both " "the external_network_bridge and gateway_external_network_id must be left " "empty." msgstr "" "Lorsque le paramètre external_network_bridge est défini, chaque agent L3 ne " "peut être associé qu'à un seul réseau externe. Cette valeur doit être " "définie sur l'UUID de ce réseau externe. Pour permettre la prise en charge " "par l'agent L3 de plusieurs réseaux externes, il est nécessaire de laisser à " "blanc les paramètres external_network_bridge et gateway_external_network_id." msgid "" "When proxying metadata requests, Neutron signs the Instance-ID header with a " "shared secret to prevent spoofing. You may select any string for a secret, " "but it must match here and in the configuration used by the Nova Metadata " "Server. NOTE: Nova uses the same config key, but in [neutron] section." msgstr "" "Lors de la mise en cache des demandes de métadonnées, Neutron signe l'en-" "tête Instance-ID à l'aide d'un secret partagé afin d'éviter toute " "usurpation. Vous pouvez choisir une chaîne comme secret, mais elle doit être " "identique ici et dans la configuration utilisée par le serveur de " "métadonnées Nova. REMARQUE : Nova utilise la même clé de configuration, mais " "dans la section [neutron]." msgid "" "Where to store Neutron state files. This directory must be writable by the " "agent." msgstr "" "Où stocker des fichiers d'état de Neutron. Ce répertoire doit être " "accessible en écriture par l'agent." msgid "" "With IPv6, the network used for the external gateway does not need to have " "an associated subnet, since the automatically assigned link-local address " "(LLA) can be used. However, an IPv6 gateway address is needed for use as the " "next-hop for the default route. If no IPv6 gateway address is configured " "here, (and only then) the neutron router will be configured to get its " "default route from router advertisements (RAs) from the upstream router; in " "which case the upstream router must also be configured to send these RAs. " "The ipv6_gateway, when configured, should be the LLA of the interface on the " "upstream router. If a next-hop using a global unique address (GUA) is " "desired, it needs to be done via a subnet allocated to the network and not " "through this parameter. " msgstr "" "Avec IPv6, le réseau utilisé pour la passerelle externe ne doit pas " "obligatoirement disposer d'un sous-réseau associé, étant donné que l'adresse " "link-local (LLA) automatiquement affectée peut être utilisée. En revanche, " "une adresse de passerelle IPv6 est nécessaire pour pouvoir faire un saut sur " "le chemin par défaut. Si aucune adresse de passerelle IPv6 n'estconfigurée " "dans ce cas, le routeur Neutron sera configuré pour obtenir son chemin par " "défaut (et uniquement dans ce but) à partir des annonces du routeur en " "amont ; dans cette situation, le routeur en amont doit être également " "configuré pour envoyer lesdites annonces. ipv6_gateway, lorsqu'il est " "configuré, doit constituer la LLA de l'interface du routeur en amont. Si un " "saut utilisantune adresse unique globale (GUA) est souhaité, il doit être " "effectué via un sous-réseau attribué au réseau, et non pas par " "l'intermédiaire de ce paramètre. " msgid "You must implement __call__" msgstr "Vous devez implémenter __call__" msgid "" "You must provide a config file for bridge - either --config-file or " "env[NEUTRON_TEST_CONFIG_FILE]" msgstr "" "Vous devez fournir un fichier de configuration pour le pont --config-file ou " "env[NEUTRON_TEST_CONFIG_FILE]" msgid "You must provide a revision or relative delta" msgstr "Vous devez fournir une révision ou un delta relatif." msgid "a subnetpool must be specified in the absence of a cidr" msgstr "Un pool de sous-réseau doit être spécifié en l'absence d'un cidr" msgid "add_ha_port cannot be called inside of a transaction." msgstr "" "Le paramètre add_ha_port ne peut pas être appelé à l'intérieur d'une " "transaction." msgid "allocation_pools allowed only for specific subnet requests." msgstr "" "allocation_pools autorisé uniquement pour les requêtes de sous-réseau " "spécifiques." msgid "allocation_pools are not in the subnet" msgstr "allocation_pools ne figurent pas dans le sous-réseau" msgid "allocation_pools use the wrong ip version" msgstr "allocation_pools utilise une version IP erronée" msgid "already a synthetic attribute" msgstr "déjà un attribut synthétique" msgid "binding:profile value too large" msgstr "Valeur de liaison:profil excessive" #, python-format msgid "cannot perform %(event)s due to %(reason)s" msgstr "Impossible d'exécuter %(event)s en raison de %(reason)s" msgid "cidr and prefixlen must not be supplied together" msgstr "cidr et prefixlen ne doivent pas être fournis ensemble" #, python-format msgid "dhcp_agents_per_network must be >= 1. '%s' is invalid." msgstr "dhcp_agents_per_network doit être >= 1. '%s' n'est pas valide." msgid "dns_domain cannot be specified without a dns_name" msgstr "dns_domain ne peut pas être spécifié sans dns_name" msgid "dns_name cannot be specified without a dns_domain" msgstr "dns_name ne peut pas être spécifié sans dns_domain" msgid "fixed_ip_address cannot be specified without a port_id" msgstr "Impossible de spécifier une adresse IP fixe sans ID port" #, python-format msgid "has device owner %s" msgstr "a le propriétaire de terminal %s" msgid "in use" msgstr "utilisé" #, python-format msgid "ip command failed on device %(dev_name)s: %(reason)s" msgstr "Echec de la commande sur le périphérique %(dev_name)s : %(reason)s" #, python-format msgid "ip command failed: %(reason)s" msgstr "Échec de commande IP : %(reason)s" #, python-format msgid "ip link capability %(capability)s is not supported" msgstr "Fonctionnalité de liaison IP %(capability)s non prise en charge" #, python-format msgid "ip link command is not supported: %(reason)s" msgstr "Commande link IP non prise en charge : %(reason)s" msgid "ip_version must be specified in the absence of cidr and subnetpool_id" msgstr "" "ip_version doit être indiqué si cidr et subnetpool_id ne sont pas définis" msgid "ipv6_address_mode is not valid when ip_version is 4" msgstr "ipv6_address_mode est non valide quand ip_version est 4" msgid "ipv6_ra_mode is not valid when ip_version is 4" msgstr "ipv6_ra_mode est non valide quand ip_version est 4" #, python-format msgid "" "ipv6_ra_mode set to '%(ra_mode)s' with ipv6_address_mode set to " "'%(addr_mode)s' is not valid. If both attributes are set, they must be the " "same value" msgstr "" "ipv6_ra_mode défini sur '%(ra_mode)s' avec ipv6_address_mode défini sur " "'%(addr_mode)s' n'est pas correct. Si les deux attributs sont définis, ils " "doivent avoir la même valeur" msgid "mac address update" msgstr "Mise à jour d'adresse MAC" msgid "must provide exactly 2 arguments - cidr and MAC" msgstr "doit fournir exactement 2 arguments - cidr et MAC" msgid "network_type required" msgstr "network_type requis" #, python-format msgid "network_type value '%s' not supported" msgstr "Valeur network_type '%s' non prise en charge" msgid "new subnet" msgstr "nouveau sous-réseau" #, python-format msgid "physical_network '%s' unknown for flat provider network" msgstr "" "physical_network '%s' inconnu pour le réseau de fournisseurs non hiérarchique" msgid "physical_network required for flat provider network" msgstr "" "physical_network obligatoire pour le réseau de fournisseurs non hiérarchique" #, python-format msgid "provider:physical_network specified for %s network" msgstr "provider:physical_network spécifié pour le réseau %s" msgid "respawn_interval must be >= 0 if provided." msgstr "respawn_interval doit être >= 0 si fourni." #, python-format msgid "segmentation_id out of range (%(min)s through %(max)s)" msgstr "segmentation_id hors plage (%(min)s à %(max)s)" msgid "segmentation_id requires physical_network for VLAN provider network" msgstr "" "segmentation_id requiert physical_network pour le réseau de fournisseurs de " "réseau local virtuel" msgid "shared attribute switching to synthetic" msgstr "commutation d'attribut partagé vers attribut synthétique" #, python-format msgid "" "subnetpool %(subnetpool_id)s cannot be updated when associated with shared " "address scope %(address_scope_id)s" msgstr "" "Le pool de sous-réseau %(subnetpool_id)s ne peut pas être mis à jour s'il " "est associé avec la portée d'adresse partagée %(address_scope_id)s" msgid "subnetpool_id and use_default_subnetpool cannot both be specified" msgstr "" "Les paramètres subnetpool_id et use_default_subnetpool ne peuvent pas être " "tous deux spécifiés" msgid "the nexthop is not connected with router" msgstr "nexthop n'est pas connecté au routeur" msgid "the nexthop is used by router" msgstr "nexthop est utilisé par le routeur" neutron-12.1.1/neutron/locale/de/0000775000175000017500000000000013553660156016643 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/de/LC_MESSAGES/0000775000175000017500000000000013553660156020430 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/de/LC_MESSAGES/neutron.po0000664000175000017500000035212513553660047022471 0ustar zuulzuul00000000000000# Translations template for neutron. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the neutron project. # # Translators: # Ettore Atalan , 2014 # Andreas Jaeger , 2016. #zanata # Frank Kloeker , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: neutron VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-03-14 04:19+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-06-03 01:41+0000\n" "Last-Translator: Andreas Jaeger \n" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 4.3.3\n" "Language-Team: German\n" #, python-format msgid "" "\n" "Command: %(cmd)s\n" "Exit code: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" msgstr "" "\n" "Befehl: %(cmd)s\n" "Beendigungscode: %(code)s\n" "Standardeingabe: %(stdin)s\n" "Standardausgabe: %(stdout)s\n" "Standardfehler: %(stderr)s" #, python-format msgid "" "%(branch)s HEAD file does not match migration timeline head, expected: " "%(head)s" msgstr "" "%(branch)s-HEAD-Datei stimmt nicht mit Migrationszeitplan für HEAD überein. " "Erwartet: %(head)s" #, python-format msgid "%(id)s is not a valid %(type)s identifier" msgstr "%(id)s ist keine gültige ID für %(type)s" #, python-format msgid "" "%(invalid_dirs)s is invalid value for sort_dirs, valid value is '%(asc)s' " "and '%(desc)s'" msgstr "" "%(invalid_dirs)s ist ein ungültiger Wert für 'sort_dirs'; gültige Werte sind " "'%(asc)s' und '%(desc)s'" #, python-format msgid "%(key)s prohibited for %(tunnel)s provider network" msgstr "%(key)s untersagt für %(tunnel)s-Anbieter-Netz" #, python-format msgid "%(name)s '%(addr)s' does not match the ip_version '%(ip_version)s'" msgstr "" "%(name)s '%(addr)s' stimmt nicht mit 'ip_version' '%(ip_version)s' überein" #, python-format msgid "%s cannot be called while in offline mode" msgstr "%s kann nicht im Offlinemodus aufgerufen werden" #, python-format msgid "%s is invalid attribute for sort_keys" msgstr "%s ist ein ungültiges Attribut für 'sort_keys'" #, python-format msgid "%s is not a valid VLAN tag" msgstr "%s ist kein gültiger VLAN-Tag" #, python-format msgid "%s must implement get_port_from_device or get_ports_from_devices." msgstr "" "%s muss get_port_from_device oder get_ports_from_devices implementieren." #, python-format msgid "%s prohibited for VLAN provider network" msgstr "%s untersagt für VLAN-Provider-Netz" #, python-format msgid "%s prohibited for flat provider network" msgstr "%s untersagt für einfaches Anbieternetzwerk" #, python-format msgid "%s prohibited for local provider network" msgstr "%s untersagt für lokales Anbieternetzwerk" #, python-format msgid "'%s' is not a valid RBAC object type" msgstr "'%s' ist kein gültiger RBAC-Objekttyp" #, python-format msgid "'%s' is not supported for filtering" msgstr "'%s' wird für die Filterung nicht unterstützt" #, python-format msgid "'module' object has no attribute '%s'" msgstr "Das 'module'-Objekt hat kein Attribut '%s'." msgid "'port_max' is smaller than 'port_min'" msgstr "'port_max' ist kleiner als 'port_min'" msgid "0 is not allowed as CIDR prefix length" msgstr "0 ist als Länge für CIDR-Präfix nicht zulässig" msgid "A cidr must be specified in the absence of a subnet pool" msgstr "Ein cidr muss angegeben werden, wenn kein Teilnetzpool vorhanden ist" msgid "" "A decimal value as Vendor's Registered Private Enterprise Number as required " "by RFC3315 DUID-EN." msgstr "" "Ein Dezimalwert als Registered Private Enterprise Number des Anbieters, wie " "es durch RFC3315 DUID-EN gefordert wird." #, python-format msgid "A default external network already exists: %(net_id)s." msgstr "Es ist bereits ein externes Standardnetz vorhanden: %(net_id)s." msgid "" "A default subnetpool for this IP family has already been set. Only one " "default may exist per IP family" msgstr "" "Es wurde bereits ein Standardsubnetzpool für diese IP-Familie definiert. Pro " "IP-Familie darf nur ein Standardpool vorhanden sein. " msgid "" "A list of mappings of physical networks to MTU values. The format of the " "mapping is :. This mapping allows specifying a physical " "network MTU value that differs from the default global_physnet_mtu value." msgstr "" "Eine Liste der Zuordnungen von physischen Netzen zu MTU-Werten. Das Format " "der Zuordnung ist :. Diese Zuordnung lässt die Angabe " "eines physischen Netz-MTU-Werts zu, der sich vom Standardwert für " "global_physnet_mtu unterscheidet." msgid "A metering driver must be specified" msgstr "Ein Messungstreiber muss angegeben sein" msgid "API for retrieving service providers for Neutron advanced services" msgstr "API zum Abrufen von Diensteanbietern für erweiterte Neutron-Dienste" msgid "Aborting periodic_sync_routers_task due to an error." msgstr "periodic_sync_routers_task wird aufgrund eines Fehlers abgebrochen." msgid "Access to this resource was denied." msgstr "Zugriff auf diese Ressource wurde verweigert." msgid "Action to be executed when a child process dies" msgstr "" "Aktion, die ausgeführt werden soll, wenn ein untergeordneter Prozess " "abgebrochen wird" msgid "" "Add comments to iptables rules. Set to false to disallow the addition of " "comments to generated iptables rules that describe each rule's purpose. " "System must support the iptables comments module for addition of comments." msgstr "" "Fügen Sie Kommentare zu iptables-Regeln hinzu. Setzen Sie den Wert auf " "'false', um das Hinzufügen von Kommentaren zu generierten iptables-Regeln, " "die den Zweck der einzelnen Regeln beschreiben, zu unterbinden. Das System " "muss das Modul für iptables-Kommentare zum Hinzufügen von Kommentaren " "unterstützen. " msgid "Address not present on interface" msgstr "Adresse an der Schnittstelle nicht vorhanden." msgid "" "Address to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "Adresse, die auf OpenFlow-Verbindungen überwacht werden soll. Wird nur für " "'native' Treiber verwendet." msgid "Adds test attributes to core resources." msgstr "Fügt Testattribute zu Kernressourcen hinzu." #, python-format msgid "Agent %(id)s is not a L3 Agent or has been disabled" msgstr "Agent %(id)s ist kein L3-Agent oder wurde inaktiviert" #, python-format msgid "Agent %(id)s is not a valid DHCP Agent or has been disabled" msgstr "Agent %(id)s ist kein gültiger DHCP-Agent oder wurde inaktiviert" msgid "" "Agent starts with admin_state_up=False when enable_new_agents=False. In the " "case, user's resources will not be scheduled automatically to the agent " "until admin changes admin_state_up to True." msgstr "" "Agent startet mit admin_state_up=False, wenn enable_new_agents=False. In " "diesem Fall werden die Ressourcen eines Benutzers nur dann automatisch für " "den Agenten geplant, wenn der Administrator admin_state_up auf True festlegt." #, python-format msgid "Agent updated: %(payload)s" msgstr "Agent aktualisiert: %(payload)s" msgid "Allow auto scheduling networks to DHCP agent." msgstr "Automatische Netzzuordnung zum DHCP-Agenten zulassen." msgid "Allow auto scheduling of routers to L3 agent." msgstr "Automatische Routerzuordnung zum L3-Agenten zulassen." msgid "" "Allow overlapping IP support in Neutron. Attention: the following parameter " "MUST be set to False if Neutron is being used in conjunction with Nova " "security groups." msgstr "" "Überschneidung bei IP-Support in Neutron zulassen. Achtung: Die folgenden " "Parameter müssen auf 'False' gesetzt werden, wenn Neutron zusammen mit Nova-" "Sicherheitsgruppen verwendet wird." msgid "Allow running metadata proxy." msgstr "Aktiven Metadaten-Proxy zulassen." msgid "Allow sending resource operation notification to DHCP agent" msgstr "" "Senden von Benachrichtigungen zu Ressourcenoperationen an den DHCP-Agenten " "zulassen" msgid "Allow the creation of PTR records" msgstr "Erstellen von PTR-Datensätzen zulassen" msgid "Allow the usage of the bulk API" msgstr "Nutzung der Massenzuweisungs-API zulassen" msgid "Allow to perform insecure SSL (https) requests to nova metadata" msgstr "" "Durchführung von unsicheren SSL-Anforderungen (HTTPS) an Nova-Metadaten" msgid "" "Allows for serving metadata requests coming from a dedicated metadata access " "network whose CIDR is 169.254.169.254/16 (or larger prefix), and is " "connected to a Neutron router from which the VMs send metadata:1 request. In " "this case DHCP Option 121 will not be injected in VMs, as they will be able " "to reach 169.254.169.254 through a router. This option requires " "enable_isolated_metadata = True." msgstr "" "Ermöglicht die Bereitstellung von Metadatenanforderungen aus einem " "dedizierten Metadatenzugriffsnetz mit der CIDR 169.254.169.254/16 (oder " "einem längeren Präfix), das mit einem Neutron-Router verbunden ist, über den " "die VMs Anforderungen vom Typ metadata:1 senden. In diesem Fall wird die " "DHCP-Option 121 nicht in die VMs injiziert, da sie 169.254.169.254 über " "einen Router erreichen können. Diese Option setzt die Einstellung " "'enable_isolated_metadata = True' voraus." msgid "An RBAC policy already exists with those values." msgstr "Es ist bereits eine RBAC-Richtlinie mit diesen Werten vorhanden." msgid "An identifier must be specified when updating a subnet" msgstr "" "Bei der Aktualisierung eines Subnetzes muss ein Bezeichner angegeben werden." msgid "An interface driver must be specified" msgstr "Ein Schnittstellentreiber muss angegeben sein" msgid "" "An ordered list of extension driver entrypoints to be loaded from the " "neutron.ml2.extension_drivers namespace. For example: extension_drivers = " "port_security,qos" msgstr "" "Sortierte Liste von Eingangspunkten für Erweiterungstreiber, die aus dem " "Namensraum neutron.ml2.extension_drivers geladen werden sollen. Beispiel: " "extension_drivers = port_security,qos" msgid "" "An ordered list of networking mechanism driver entrypoints to be loaded from " "the neutron.ml2.mechanism_drivers namespace." msgstr "" "Sortierte Liste der Eingangspunkte für Netzmechanismustreiber die aus dem " "Namensbereich neutron.ml2.mechanism_drivers geladen werden." msgid "An unknown error has occurred. Please try your request again." msgstr "" "Ein unbekannter Fehler ist aufgetreten. Stellen Sie Ihre Anforderung erneut." msgid "Async process didn't respawn" msgstr "Der asynchrone Prozess hat keinen erneuten Prozess erstellt." msgid "Authorization URL for connecting to designate in admin context" msgstr "" "Autorisierungs-URL zum Herstellen einer Verbindung zu Designate im " "Administratorkontext." msgid "Automatically remove networks from offline DHCP agents." msgstr "Netze automatisch von DHCP-Agenten, die offline sind, entfernen." msgid "" "Automatically reschedule routers from offline L3 agents to online L3 agents." msgstr "" "Automatische Neuterminierung für Router von Offline-L3-Agenten zu Online-L3-" "Agenten." msgid "Availability zone of this node" msgstr "Verfügbarkeitszone dieses Knotens" msgid "Available commands" msgstr "Verfügbare Befehle" msgid "Backend does not support VLAN Transparency." msgstr "Backend unterstützt keine VLAN-Transparenz." #, python-format msgid "Base MAC: %s" msgstr "Basis-MAC-Adresse: %s" msgid "" "Base log dir for dnsmasq logging. The log contains DHCP and DNS log " "information and is useful for debugging issues with either DHCP or DNS. If " "this section is null, disable dnsmasq log." msgstr "" "Basisprotokollverzeichnis für dnsmasq-Protokollierung. Das Protokoll enthält " "DHCP- und DNS-Protokollinformationen und ist für das Debugging von Problemen " "mit DHCP oder DNS nützlich. Wenn dieser Abschnitt null ist, dann " "deaktivieren Sie das dnsmasq-Protokoll." msgid "Body contains invalid data" msgstr "Hauptteil enthält ungültige Daten" msgid "Both network_id and router_id are None. One must be provided." msgstr "" "Sowohl 'network_id' als auch 'router_id' sind 'None'. Ein Wert muss " "angegeben sein." #, python-format msgid "Bridge %(bridge)s does not exist." msgstr "Brücke %(bridge)s ist nicht vorhanden." msgid "Bulk operation not supported" msgstr "Massenoperation nicht unterstützt" msgid "CIDR to monitor" msgstr "Zu überwachendes CIDR" #, python-format msgid "Callback for %(resource_type)s not found" msgstr "Callback nach %(resource_type)s nicht gefunden" #, python-format msgid "Callback for %(resource_type)s returned wrong resource type" msgstr "" "Callback nach %(resource_type)s hat den falschen Ressourcentyp zurückgegeben" #, python-format msgid "Cannot add floating IP to port %s that has no fixed IPv4 addresses" msgstr "" "Zu Port %s, der keine statischen IPv4-Adressen besitzt, kann keine " "dynamische IP hinzugefügt werden" #, python-format msgid "Cannot add multiple callbacks for %(resource_type)s" msgstr "" "Es können nicht mehrere Callbacks nach %(resource_type)s hinzugefügt werden" #, python-format msgid "Cannot allocate IPv%(req_ver)s subnet from IPv%(pool_ver)s subnet pool" msgstr "" "IPv%(req_ver)s-Teilnetz kann nicht aus IPv%(pool_ver)s-Teilnetzpool " "zugeordnet werden" msgid "Cannot allocate requested subnet from the available set of prefixes" msgstr "" "Das angeforderte Teilnetz kann nicht aus der verfügbaren Gruppe mit Präfixen " "zugeordnet werden" msgid "Cannot disable enable_dhcp with ipv6 attributes set" msgstr "" "enable_dhcp kann nicht inaktiviert werden, wenn ipv6-Attribute gesetzt sind" #, python-format msgid "Cannot handle subnet of type %(subnet_type)s" msgstr "Teilnetz des Typs %(subnet_type)s kann nicht behandelt werden" msgid "Cannot have multiple IPv4 subnets on router port" msgstr "Mehrere IPv4-Subnetze an Router-Port nicht möglich" #, python-format msgid "" "Cannot have multiple router ports with the same network id if both contain " "IPv6 subnets. Existing port %(p)s has IPv6 subnet(s) and network id %(nid)s" msgstr "" "Mehrere Routerports können nicht dieselbe Netz-ID verwenden, wenn beide IPv6-" "Teilnetze enthalten. Der vorhandene Port %(p)s verfügt über das IPv6-" "Teilnetz und die Netz-ID %(nid)s" #, python-format msgid "" "Cannot host distributed router %(router_id)s on legacy L3 agent %(agent_id)s." msgstr "" "Der verteilte Router %(router_id)s kann am traditionellen L3-Agenten " "%(agent_id)s nicht gehostet werden." msgid "Cannot mix IPv4 and IPv6 prefixes in a subnet pool." msgstr "" "IPv4- und IPv6-Präfixe können in einem Subnetzpool nicht gemischt werden." msgid "Cannot specify both subnet-id and port-id" msgstr "Angabe sowohl von Teilnetz-ID als auch von Port-ID nicht möglich" msgid "Cannot understand JSON" msgstr "Kann JSON nicht verstehen" #, python-format msgid "Cannot update read-only attribute %s" msgstr "Schreibgeschütztes Attribut %s kann nicht aktualisiert werden" msgid "Certificate Authority public key (CA cert) file for ssl" msgstr "Öffentliche Schlüsseldatei der Zertifizierungsstelle für SSL" #, python-format msgid "" "Change would make usage less than 0 for the following resources: %(unders)s." msgstr "" "Durch die Änderung wäre die Nutzung kleiner als 0 für die folgenden " "Ressourcen: %(unders)s." msgid "Check ebtables installation" msgstr "Installation von ebtables überprüfen" msgid "Check for ARP header match support" msgstr "Auf Unterstützung des Vergleichs von ARP-Headern überprüfen" msgid "Check for ARP responder support" msgstr "Überprüfen Sie, ob ARP-Responder unterstützt werden" msgid "Check for ICMPv6 header match support" msgstr "Auf Unterstützung des Vergleichs von ICMPv6-Headern überprüfen" msgid "Check for OVS Geneve support" msgstr "Auf OVS-Geneve-Unterstützung überprüfen" msgid "Check for OVS vxlan support" msgstr "Überprüfen Sie, ob OVS-VXLAN-Unterstützung vorliegt" msgid "Check for VF management support" msgstr "Überprüfen Sie, ob VF-Management unterstützt wird" msgid "Check for iproute2 vxlan support" msgstr "Überprüfen Sie, ob iproute2-VXLAN-Unterstützung vorliegt" msgid "Check for nova notification support" msgstr "Überprüfen Sie, ob Nova-Benachrichtigungen unterstützt werden" msgid "Check for patch port support" msgstr "Überprüfen Sie, ob Patch-Ports unterstützt werden" msgid "Check ip6tables installation" msgstr "Überprüfen Sie die ip6tables-Installation." msgid "Check ipset installation" msgstr "Überprüfen Sie die ipset-Installation." msgid "Check keepalived IPv6 support" msgstr "IPv6-Unterstützung von keepalived überprüfen" msgid "Check minimal dibbler version" msgstr "Mindestversion von dibbler überprüfen" msgid "Check minimal dnsmasq version" msgstr "Überprüfen Sie die Mindestversion für dnsmasq" msgid "Check netns permission settings" msgstr "Überprüfen Sie die netns-Berechtigungseinstellungen" msgid "Check ovs conntrack support" msgstr "Überprüfen Sie, ob OVS-Conntrack-Unterstützung vorhanden ist." msgid "Check ovsdb native interface support" msgstr "Unterstützung für native ovsdb-Schnittstelle überprüfen" #, python-format msgid "" "Cidr %(subnet_cidr)s of subnet %(subnet_id)s overlaps with cidr %(cidr)s of " "subnet %(sub_id)s" msgstr "" "Überschneidungen zwischen CIDR %(subnet_cidr)s von Teilnetz %(subnet_id)s " "und CIDR %(cidr)s von Teilnetz %(sub_id)s" msgid "Cleanup resources of a specific agent type only." msgstr "Bereinigen Sie nur Ressourcen mit einem bestimmten Agententyp." msgid "Client certificate for nova metadata api server." msgstr "Clientzertifikat zu API-Server für Nova-Metadaten." msgid "" "Comma-separated list of : tuples, mapping " "network_device to the agent's node-specific list of virtual functions that " "should not be used for virtual networking. vfs_to_exclude is a semicolon-" "separated list of virtual functions to exclude from network_device. The " "network_device in the mapping should appear in the physical_device_mappings " "list." msgstr "" "Liste mit durch Kommas voneinander getrennten Tupeln vom Typ " ":, in der eine Netzeinheit der " "knotenspezifischen Liste virtueller Funktionen des Agenten zugeordnet wird, " "die nicht für den virtuellen Netzbetrieb verwendet werden sollen. Bei " "'Auszuschließende_VFS' handelt es sich um eine durch Semikolons getrennte " "Liste virtueller Funktionen, die aus der Netzeinheit auszuschließen sind. " "Die Netzeinheit in der Zuordnung sollte in der Liste " "'physical_device_mappings' angezeigt werden." msgid "" "Comma-separated list of : tuples mapping physical " "network names to the agent's node-specific Open vSwitch bridge names to be " "used for flat and VLAN networks. The length of bridge names should be no " "more than 11. Each bridge must exist, and should have a physical network " "interface configured as a port. All physical networks configured on the " "server should have mappings to appropriate bridges on each agent. Note: If " "you remove a bridge from this mapping, make sure to disconnect it from the " "integration bridge as it won't be managed by the agent anymore." msgstr "" "Liste mit durch Kommas voneinander getrennten Tuplen vom Typ " ":, in der die physischen Netznamen den " "knotenspezifischen Open vSwitch-Brückennamen des Agenten zugeordnet sind, " "die für einfache und VLAN-Netze verwendet werden sollen. Die Länge der " "Brückennamen darf 11 Zeichen nicht überschreiten. Jede Brücke muss vorhanden " "sein und eine als Port konfigurierte physische Netzschnittstelle haben. Alle " "auf dem Server konfigurierten physischen Netze müssen Zuordnungen zu den " "entsprechenden Brücken in jedem Agenten haben. Hinweis: Wenn Sie aus dieser " "Zuordnung eine Brücke entfernen, stellen Sie sicher, dass Sie die Verbindung " "der Brücke zur Integrationsbrücke unterbrechen, da sie nicht mehr vom " "Agenten verwaltet wird." msgid "" "Comma-separated list of : tuples mapping " "physical network names to the agent's node-specific physical network device " "interfaces of SR-IOV physical function to be used for VLAN networks. All " "physical networks listed in network_vlan_ranges on the server should have " "mappings to appropriate interfaces on each agent." msgstr "" "Liste mit durch Kommas voneinander getrennten Tupeln vom Typ " ":, in der die Namen physischer Netze den " "knotenspezifischen Netzeinheitenschnittstellen des Agenten mit der " "physischen SR-IOV-Funktion zugeordnet wird, die für VLAN-Netze verwendet " "werden soll. Alle physischen Netze, die in 'network_vlan_ranges' auf dem " "Server aufgeführt sind, sollten entsprechenden Schnittstellen in jedem " "Agenten zugeordnet werden." msgid "" "Comma-separated list of : tuples " "mapping physical network names to the agent's node-specific physical network " "interfaces to be used for flat and VLAN networks. All physical networks " "listed in network_vlan_ranges on the server should have mappings to " "appropriate interfaces on each agent." msgstr "" "Liste mit durch Kommas voneinander getrennten Tupeln vom Typ " ":, in der die physischen Netznamen den " "knotenspezifischen, physischen Netzschnittstellen des Agenten zugeordnet " "sind, die für einfache und VLNA-Netze verwendet werden sollen. Alle " "physischen Netze, die über die Eigenschaft 'network_vlan_ranges' auf dem " "Server aufgelistet sind, müssen Zuordnungen zu den entsprechenden " "Schnittstellen in jedem Agenten haben." msgid "" "Comma-separated list of : tuples enumerating ranges of GRE " "tunnel IDs that are available for tenant network allocation" msgstr "" "Durch Kommas getrennte Liste von : Tupeln, die Bereiche " "von GRE-Tunnel-IDs aufzählen, die für eine Nutzernetzzuordnung verfügbar sind" msgid "" "Comma-separated list of : tuples enumerating ranges of " "Geneve VNI IDs that are available for tenant network allocation" msgstr "" "Durch Kommas getrennte Liste mit den :-Tupeln, die die " "Bereiche der Geneve-VNI-IDs aufzählen, die für eine Nutzernetzzuordnung " "verfügbar sind" msgid "" "Comma-separated list of : tuples enumerating ranges of " "VXLAN VNI IDs that are available for tenant network allocation" msgstr "" "Durch Kommas getrennte Liste von : Tupeln, die Bereiche " "von VXLAN-VNI-IDs aufzählen, die für eine Nutzernetzzuordnung verfügbar sind" msgid "" "Comma-separated list of the DNS servers which will be used as forwarders." msgstr "" "Durch Kommas getrennte Liste der DNS-Server, die künftig als " "Weiterleitungsserver verwendet werden." msgid "Command to execute" msgstr "Auszuführender Befehl" msgid "Config file for interface driver (You may also use l3_agent.ini)" msgstr "" "Konfigurationsdatei für Schnittstellentreiber (Sie können auch 'l3_agent." "ini' verwenden)" #, python-format msgid "Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s" msgstr "Kollidierender Wert bei Ethernet-Typ %(ethertype)s für CIDR %(cidr)s" msgid "" "Controls whether the neutron security group API is enabled in the server. It " "should be false when using no security groups or using the nova security " "group API." msgstr "" "Steuert, ob die Neutron-Sicherheitsgruppen-API im Server aktiviert ist. " "Sollte 'false' sein, wenn keine Sicherheitsgruppen verwendet werden oder " "wenn die Nova-Sicherheitsgruppen-API verwendet wird." #, python-format msgid "Could not bind to %(host)s:%(port)s after trying for %(time)d seconds" msgstr "" "Keine Bindung an %(host)s:%(port)s möglich nach Versuch über %(time)d " "Sekunden" msgid "Could not deserialize data" msgstr "Daten konnten nicht deserialisiert werden" #, python-format msgid "" "Current gateway ip %(ip_address)s already in use by port %(port_id)s. Unable " "to update." msgstr "" "Aktuelle Gateway-IP-Adresse %(ip_address)s wird bereits verwendet von Port " "%(port_id)s. Aktualisierung nicht möglich." msgid "" "DHCP lease duration (in seconds). Use -1 to tell dnsmasq to use infinite " "lease times." msgstr "" "DHCP-Leasedauer (in Sekunden). Verwenden Sie -1, damit dnsmasq unbegrenzte " "Leasedauern verwendet." msgid "" "DVR deployments for VXLAN/GRE/Geneve underlays require L2-pop to be enabled, " "in both the Agent and Server side." msgstr "" "DVR-Implementierungen für VXLAN/GRE/Geneve-Underlays erfordern die " "Aktivierung von L2-pop sowohl auf der Agenten- als auch auf der Serverseite." msgid "" "Database engine for which script will be generated when using offline " "migration." msgstr "" "Datenbankengine, für die bei Verwendung der Offline-Migration ein Script " "generiert wird." msgid "Default driver to use for quota checks." msgstr "Standardtreiber zur Verwendung für Kontingentprüfungen." msgid "Default external networks must be shared to everyone." msgstr "Externe Standardnetze müssen für alle freigegeben werden." msgid "" "Default network type for external networks when no provider attributes are " "specified. By default it is None, which means that if provider attributes " "are not specified while creating external networks then they will have the " "same type as tenant networks. Allowed values for external_network_type " "config option depend on the network type values configured in type_drivers " "config option." msgstr "" "Standardnetztyp für externe Netze, wenn keine Anbieterattribut angegeben " "wurden. Der Standardwert None bedeutet, dass, wenn keine Anbieterattribut " "beim Erstellen von externen Netzen angegeben werden, derselbe Typ wie bei " "Nutzernetzen verwendet wird. Die zulässigen Werte für die " "Konfigurationsoption external_network_type hängen von den konfigurierten " "Netztypwerten in der Konfigurationsoption type_drivers ab." msgid "" "Default number of RBAC entries allowed per tenant. A negative value means " "unlimited." msgstr "" "Standardanzahl an zulässigen RBAC-Einträgen pro Nutzer. Ein negativer Wert " "bedeutet unbegrenzt." msgid "" "Default number of resource allowed per tenant. A negative value means " "unlimited." msgstr "" "Standardanzahl an zulässigen Ressourcen pro Nutzer. Ein negativer Wert " "bedeutet unbegrenzt." msgid "Default security group" msgstr "Standardsicherheitsgruppe" msgid "Default security group already exists." msgstr "Standardsicherheitsgruppe ist bereits vorhanden." msgid "" "Default value of availability zone hints. The availability zone aware " "schedulers use this when the resources availability_zone_hints is empty. " "Multiple availability zones can be specified by a comma separated string. " "This value can be empty. In this case, even if availability_zone_hints for a " "resource is empty, availability zone is considered for high availability " "while scheduling the resource." msgstr "" "Der Standardwert für die Eigenschaft 'availability_zone_hints'. Die mit " "Verfügbarkeitszonen kompatiblen Scheduler verwenden diesen Wert, wenn der " "Wert für 'availability_zone_hints' der Ressourcen leer ist. Mehrere " "Verfügbarkeitszonen können als Zeichenfolge, durch Kommas getrennt, " "angegeben werden. Dieser Wert kann leer sein. In diesem Fall wird die " "Verfügbarkeitszone bei der Ressourcenplanung als hoch verfügbar betrachtet, " "auch dann, wenn die Eigenschaft 'availability_zone_hints' für eine Ressource " "leer ist. " msgid "" "Define the default value of enable_snat if not provided in " "external_gateway_info." msgstr "" "Definieren Sie den Standardwert von enable_snat, falls in " "external_gateway_info nichts angegeben ist." msgid "" "Defines providers for advanced services using the format: :" ":[:default]" msgstr "" "Definiert Provider für erweiterte Services mit dem folgenden Format: " "::[:default]" msgid "Delete the namespace by removing all devices." msgstr "Löschen Sie den Namensbereich durch Entfernen aller Geräte." #, python-format msgid "Deleting port %s" msgstr "Port %s wird gelöscht" #, python-format msgid "Deployment error: %(reason)s." msgstr "Implementierungsfehler: %(reason)s." msgid "Destroy IPsets even if there is an iptables reference." msgstr "IPsets löschen, auch wenn eine iptables-Referenz vorhanden ist." msgid "Destroy all IPsets." msgstr "Alle IPsets löschen." #, python-format msgid "Device %(dev_name)s in mapping: %(mapping)s not unique" msgstr "Einheit %(dev_name)s in Zuordnung %(mapping)s nicht eindeutig" #, python-format msgid "Device name %(dev_name)s is missing from physical_device_mappings" msgstr "Einheitenname %(dev_name)s fehlt in physical_device_mappings" msgid "Device not found" msgstr "Einheit nicht gefunden" #, python-format msgid "" "Distributed Virtual Router Mac Address for host %(host)s does not exist." msgstr "" "MAC-Adresse von verteiltem virtuellem Router für Host %(host)s ist nicht " "vorhanden." msgid "Domain to use for building the hostnames" msgstr "Für das Erstellen von Hostnamen zu verwendende Domäne" msgid "Downgrade no longer supported" msgstr "Herabstufung wird nicht mehr unterstützt" #, python-format msgid "Driver %s is not unique across providers" msgstr "Treiber %s ist für Anbieter nicht eindeutig" msgid "Driver for external DNS integration." msgstr "Treiber für externe DNS-Integration." msgid "Driver for security groups firewall in the L2 agent" msgstr "Treiber für Sicherheitsgruppen-Firewall im L2-Agenten" msgid "Driver to use for scheduling network to DHCP agent" msgstr "Zu verwendender Treiber bei Netzzuordnung zum DHCP-Agenten" msgid "Driver to use for scheduling router to a default L3 agent" msgstr "Zu verwendender Treiber bei Routerzuordnung zum Standard-L3-Agenten" msgid "" "Driver used for ipv6 prefix delegation. This needs to be an entry point " "defined in the neutron.agent.linux.pd_drivers namespace. See setup.cfg for " "entry points included with the neutron source." msgstr "" "Treiber, der für die IPv6-Präfixdelegierung verwendet wird. Dies muss ein " "Einstiegspunkt sein, der im Namensbereich neutron.agent.linux.pd_drivers " "definiert ist. In setup.cfg finden Sie die Einstiegspunkte, die in der " "Neutron-Quelle enthalten sind." #, python-format msgid "" "Duplicate L3HARouterAgentPortBinding is created for router(s) %(router)s. " "Database cannot be upgraded. Please, remove all duplicates before upgrading " "the database." msgstr "" "Doppelte L3HARouterAgentPortBinding wird für Router %(router)s erstellt. Es " "konnte kein Aktualisierung für die Datenbank durchgeführt werden. Entfernen " "Sie alle Duplikate, bevor Sie die Aktualisierung der Datenbank durchführen." msgid "Duplicate Security Group Rule in POST." msgstr "Doppelte Sicherheitsgruppenregel in POST." msgid "Duplicate address detected" msgstr "Doppelte Adresse erkannt." msgid "Duplicate segment entry in request." msgstr "Doppelter Segmenteintrag in Anforderung." #, python-format msgid "ERROR: %s" msgstr "FEHLER: %s" msgid "" "ERROR: Unable to find configuration file via the default search paths (~/." "neutron/, ~/, /etc/neutron/, /etc/) and the '--config-file' option!" msgstr "" "FEHLER: Konfigurationsdatei kann über die Standardsuchpfade (~/.neutron/, " "~/, /etc/neutron/, /etc/) und über die Option '--config-file' nicht gefunden " "werden!" msgid "" "Either one of parameter network_id or router_id must be passed to _get_ports " "method." msgstr "" "Einer der Parameter network_id und router_id muss an die Methode _get_ports " "übergeben werden." msgid "Either subnet_id or port_id must be specified" msgstr "Entweder 'subnet_id' oder 'port_id' muss angegeben sein" msgid "Empty physical network name." msgstr "Leerer Name für physisches Netz." msgid "Empty subnet pool prefix list." msgstr "Leere Präfixliste für Subnetzpool" msgid "Enable HA mode for virtual routers." msgstr "Hochverfügbarkeitsmodus für virtuelle Router aktivieren." msgid "Enable SSL on the API server" msgstr "SSL auf dem API-Server aktivieren" msgid "" "Enable VXLAN on the agent. Can be enabled when agent is managed by ml2 " "plugin using linuxbridge mechanism driver" msgstr "" "VXLAN auf dem Agenten aktivieren. Kann aktiviert werden, wenn der Agent vom " "ml2-Plug-in mithilfe eines Linuxbridge-Mechanismus-Treibers verwaltet wird" msgid "" "Enable local ARP responder if it is supported. Requires OVS 2.1 and ML2 " "l2population driver. Allows the switch (when supporting an overlay) to " "respond to an ARP request locally without performing a costly ARP broadcast " "into the overlay." msgstr "" "Aktivieren Sie den lokalen ARP-Responder, wenn dies unterstützt wird. Dies " "erfordert OVS 2.1 und einen ML2-l2population-Treiber. Dadurch wird es dem " "Switch (bei Unterstützung eines Overlay) ermöglicht, lokal auf eine ARP-" "Anforderung zu reagieren, ohne einen aufwändigen ARP-Broadcast in das " "Overlay durchzuführen." msgid "" "Enable local ARP responder which provides local responses instead of " "performing ARP broadcast into the overlay. Enabling local ARP responder is " "not fully compatible with the allowed-address-pairs extension." msgstr "" "Aktivieren Sie den lokalen ARP-Responder, der lokale Antworten bereitstellt " "anstatt ARP-Broadcasts im Overlay durchzuführen. Die Aktivierung eines " "lokalen ARP-Responders ist nicht vollständig kompatibel mit der Erweiterung " "zulässiger Adresspaare." msgid "" "Enable services on an agent with admin_state_up False. If this option is " "False, when admin_state_up of an agent is turned False, services on it will " "be disabled. Agents with admin_state_up False are not selected for automatic " "scheduling regardless of this option. But manual scheduling to such agents " "is available if this option is True." msgstr "" "Aktivieren Sie Services auf einem Agenten mit admin_state_up False. Wenn " "diese Option 'False' lautet und wenn admin_state_up eines Agenten auf " "'False' gesetzt wird, werden die Dienste darauf deaktiviert. Agenten mit " "admin_state_up False werden, unabhängig von dieser Option, nicht für die " "automatische Planung ausgewählt. Die manuelle Planung ist für solche Agenten " "jedoch verfügbar, wenn diese Option auf 'True' gesetzt ist." msgid "" "Enables IPv6 Prefix Delegation for automatic subnet CIDR allocation. Set to " "True to enable IPv6 Prefix Delegation for subnet allocation in a PD-capable " "environment. Users making subnet creation requests for IPv6 subnets without " "providing a CIDR or subnetpool ID will be given a CIDR via the Prefix " "Delegation mechanism. Note that enabling PD will override the behavior of " "the default IPv6 subnetpool." msgstr "" "Aktiviert die Delegierung von IPv6-Präfixen für die automatische Subnetz-" "CIDR-Zuordnung. Setzen Sie den Wert auf 'True', um die Delegierung von IPv6-" "Präfixen für die Subnetzzuordnung in einer für die Präfixdelegierung " "geeigneten Umgebung zu aktivieren. Benutzer, die " "Subnetzerstellunganforderungen für IPv6-Subnetze ohne Angabe einer CIDR oder " "Subnetzpool-ID stellen, erhalten eine CIDR über den " "Präfixdelegierungsmechanismus. Beachten Sie, dass die Aktivierung der " "Präfixdelegierung das Verhalten für den IPv6-Standardsubnetzpool außer Kraft " "setzt." msgid "" "Enables the dnsmasq service to provide name resolution for instances via DNS " "resolvers on the host running the DHCP agent. Effectively removes the '--no-" "resolv' option from the dnsmasq process arguments. Adding custom DNS " "resolvers to the 'dnsmasq_dns_servers' option disables this feature." msgstr "" "Aktiviert den dnsmasq-Dienst zur Bereitstellung von Namensauflösungen für " "Instanzen mithilfe DNS-Resolvern auf dem Host, auf dem der DHCP-Agent " "ausgeführt wird. Entfernt die Option '--no-resolv' aus den dnsmasq-" "Verarbeitungsargumenten. Dieses Feature wird deaktiviert, wenn angepasste " "DNS-Resolver zur Option 'dnsmasq_dns_servers' hinzugefügt werden." msgid "End of VLAN range is less than start of VLAN range" msgstr "Ende des VLAN-Bereichs ist kleiner als Anfang des VLAN-Bereichs" msgid "End of tunnel range is less than start of tunnel range" msgstr "Ende des Tunnelbereichs ist kleiner als Anfang des Tunnelbereichs" #, python-format msgid "Error %(reason)s while attempting the operation." msgstr "Fehler %(reason)s beim Ausführen der Operation." #, python-format msgid "Error parsing dns address %s" msgstr "Fehler bei Auswertung der DNS-Adresse %s" #, python-format msgid "Error while reading %s" msgstr "Fehler beim Lesen von %s" #, python-format msgid "" "Exceeded %s second limit waiting for address to leave the tentative state." msgstr "" "Der Grenzwert von %s Sekunde(n) wurde überschritten, als darauf gewartet " "wurde, dass sich der vorläufige Status der Adresse ändert." msgid "Existing prefixes must be a subset of the new prefixes" msgstr "Vorhandene Präfixe müssen eine Untergruppe der neuen Präfixe sein" #, python-format msgid "" "Exit code: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" msgstr "" "Exit-Code: %(returncode)d; Standardeingabe: %(stdin)s; Standardausgabe: " "%(stdout)s; Standardfehler: %(stderr)s" #, python-format msgid "Extension %(driver)s failed." msgstr "Erweiterungs-%(driver)s fehlgeschlagen." #, python-format msgid "" "Extension driver %(driver)s required for service plugin %(service_plugin)s " "not found." msgstr "" "Der Erweiterungstreiber %(driver)s, der für das Dienste-Plugin " "%(service_plugin)s erforderlich ist, wurde nicht gefunden." msgid "" "Extension to use alongside ml2 plugin's l2population mechanism driver. It " "enables the plugin to populate VXLAN forwarding table." msgstr "" "Erweiterung zur Verwendung mit dem l2population-Mechanismus-Treiber des ml2-" "Plug-ins. Sie ermöglicht dem Plug-in das Belegen der VXLAN-" "Weiterleitungstabelle." #, python-format msgid "Extension with alias %s does not exist" msgstr "Erweiterung mit Alias %s ist nicht vorhanden" msgid "Extensions list to use" msgstr "Zur verwendende Liste der Erweiterungen" #, python-format msgid "Extensions not found: %(extensions)s." msgstr "Erweiterungen nicht gefunden: %(extensions)s." #, python-format msgid "External IP %s is the same as the gateway IP" msgstr "Externe IP %s entspricht der Gateway-IP" #, python-format msgid "Failed rescheduling router %(router_id)s: no eligible l3 agent found." msgstr "" "Fehler bei Neuterminierung von Router %(router_id)s: kein auswählbarer L3-" "Agent gefunden." #, python-format msgid "Failed scheduling router %(router_id)s to the L3 Agent %(agent_id)s." msgstr "" "Zuordnung des Routers %(router_id)s zum L3-Agenten %(agent_id)s ist " "fehlgeschlagen." #, python-format msgid "Failed to allocate subnet: %(reason)s." msgstr "Fehler beim Zuordnen von Subnetz: %(reason)s." msgid "" "Failed to associate address scope: subnetpools within an address scope must " "have unique prefixes." msgstr "" "Der Adressbereich konnte nicht zugeordnet werden. Subnetzpools innerhalb " "eines Adressbereichs müssen eindeutige Präfixe haben." #, python-format msgid "" "Failed to create port on network %(network_id)s, because fixed_ips included " "invalid subnet %(subnet_id)s" msgstr "" "Port auf Netz %(network_id)s wurde nicht erstellt, da 'fixed_ips' ungültiges " "Teilnetz %(subnet_id)s enthielt" #, python-format msgid "Failed to locate source for %s." msgstr "Quelle für %s nicht gefunden." msgid "Failed to remove supplemental groups" msgstr "Fehler beim Entfernen zusätzlicher Gruppen" #, python-format msgid "Failed to set gid %s" msgstr "Fehler beim Festlegen von GID %s" #, python-format msgid "Failed to set uid %s" msgstr "Fehler beim Festlegen von Benutzer-ID %s" #, python-format msgid "Failed to set-up %(type)s tunnel port to %(ip)s" msgstr "Fehler bei der Konfiguration eines %(type)s-Tunnel-Ports auf %(ip)s" msgid "Failure applying iptables rules" msgstr "Fehler beim Anwenden von iptables-Regeln." #, python-format msgid "Failure waiting for address %(address)s to become ready: %(reason)s" msgstr "" "Fehler beim Warten darauf, dass Adresse %(address)s bereit ist: %(reason)s" msgid "Flat provider networks are disabled" msgstr "Einfache Anbieternetzwerke sind deaktiviert." msgid "For TCP/UDP protocols, port_range_min must be <= port_range_max" msgstr "Für TCP/UDP-Protokolle muss 'port_range_min' '<= port_range_max' sein" msgid "Force ip_lib calls to use the root helper" msgstr "ip_lib-Aufrufe erzwingen, um Roothilfeprogramm zu verwenden" #, python-format msgid "Found duplicate extension: %(alias)s." msgstr "Doppelte Erweiterung gefunden: %(alias)s." #, python-format msgid "" "Found overlapping allocation pools: %(pool_1)s %(pool_2)s for subnet " "%(subnet_cidr)s." msgstr "" "Überschneidung bei Zuordnungspools %(pool_1)s %(pool_2)s für Teilnetz " "%(subnet_cidr)s gefunden." msgid "Gateway IP version inconsistent with allocation pool version" msgstr "" "Die Version der Gateway-IP stimmt nicht mit der Version des Zuordnungspools " "überein." #, python-format msgid "Gateway ip %(ip_address)s conflicts with allocation pool %(pool)s." msgstr "" "Gateway-IP '%(ip_address)s' steht im Konflikt mit Zuordnungspool %(pool)s." msgid "Gateway is not valid on subnet" msgstr "Gateway ist auf Teilnetz nicht gültig" msgid "" "Geneve encapsulation header size is dynamic, this value is used to calculate " "the maximum MTU for the driver. This is the sum of the sizes of the outer " "ETH + IP + UDP + GENEVE header sizes. The default size for this field is 50, " "which is the size of the Geneve header without any additional option headers." msgstr "" "Die Größe des Geneve-Kapselungsheaders ist dynamisch. Dieser Wert wird " "verwendet, um den maximalen MTU-Wert für den Treiber zu berechnen. Dies ist " "die Summe aus den Größen der äußeren Headergrößen für ETH + IP + UDP + " "GENEVE. Die Standardgröße für dieses Feld ist 50 und entspricht der Größe " "des Geneve-Headers ohne zusätzliche Optionsheader." msgid "" "Group (gid or name) running metadata proxy after its initialization (if " "empty: agent effective group)." msgstr "" "Gruppe (Gruppen-ID oder Name), die Metadaten-Proxy nach der Initialisierung " "ausführt (falls leer: Agent-ausführende Gruppe)." msgid "Group (gid or name) running this process after its initialization" msgstr "" "Gruppe (Gruppen-ID oder Name), die diesen Prozess nach der Initialisierung " "ausführt" msgid "" "Hostname to be used by the Neutron server, agents and services running on " "this machine. All the agents and services running on this machine must use " "the same host value." msgstr "" "Hostname, der vom Neutron-Server, von Agenten und Services auf dieser " "Maschine verwendet werden soll. Alle auf dieser Maschine ausgeführten " "Agenten und Services müssen denselben Hostwert verwenden." #, python-format msgid "" "ICMP code (port-range-max) %(value)s is provided but ICMP type (port-range-" "min) is missing." msgstr "" "ICMP-Code (port-range-max) %(value)s ist angegeben, aber ICMP-Typ (port-" "range-min) fehlt." msgid "ID of network" msgstr "Netz-ID" msgid "ID of network to probe" msgstr "ID von Netz das überprüft werden soll" msgid "ID of probe port to delete" msgstr "ID von Überprüfungsport der gelöscht werden soll" msgid "ID of probe port to execute command" msgstr "ID von Überprüfungsport zum Ausführen des Befehls" msgid "ID of the router" msgstr "ID des Routers" #, python-format msgid "IP address %(ip)s already allocated in subnet %(subnet_id)s" msgstr "IP-Adresse %(ip)s bereits in Teilnetz %(subnet_id)s zugeordnet" #, python-format msgid "IP address %(ip)s does not belong to subnet %(subnet_id)s" msgstr "IP-Adresse %(ip)s gehört nicht zu Teilnetz %(subnet_id)s" msgid "IP allocation failed. Try again later." msgstr "IP-Zuordnung fehlgeschlagen. Versuchen Sie es später noch einmal." msgid "IP allocation requires subnet_id or ip_address" msgstr "'subnet_id' oder 'ip_address' für IP-Zuordnung erforderlich" #, python-format msgid "" "IPTablesManager.apply failed to apply the following set of iptables rules:\n" "%s" msgstr "" "IPTablesManager.apply hat den folgenden Satz an iptables-Regeln nicht " "angewendet:\n" "%s" msgid "IPtables conntrack zones exhausted, iptables rules cannot be applied." msgstr "" "IPtables-conntrack-Zonen erschöpft. iptables-Regeln können nicht angewendet " "werden." msgid "IPv6 Address Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "Für Präfixdelegierung muss der IPv6-Adressmodus SLAAC oder Stateless sein." msgid "IPv6 RA Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "Für Präfixdelegierung muss der IPv6-RA-Modus SLAAC oder Stateless sein." #, python-format msgid "" "IPv6 address %(ip)s cannot be directly assigned to a port on subnet " "%(subnet_id)s as the subnet is configured for automatic addresses" msgstr "" "IPv6-Adresse %(ip)s kann nicht direkt einem Port im Teilnetz %(subnet_id)s " "zugeordnet werden, da das Teilnetz für automatische Adressen konfiguriert " "wurde" #, python-format msgid "" "IPv6 subnet %s configured to receive RAs from an external router cannot be " "added to Neutron Router." msgstr "" "IPv6-Teilnetz %s, das für den Empfang von RAs von einem externen Router " "konfiguriert ist, kann nicht zum Neutron-Router hinzugefügt werden." msgid "" "If True, then allow plugins that support it to create VLAN transparent " "networks." msgstr "" "Bei 'True' sollen Plugins, die dies unterstützen, VLAN-transparente Netze " "erstellen dürfen." msgid "Illegal IP version number" msgstr "Illegale IP-Versionsnummer" #, python-format msgid "" "Illegal prefix bounds: %(prefix_type)s=%(prefixlen)s, %(base_prefix_type)s=" "%(base_prefixlen)s." msgstr "" "Unzulässige Präfix-Bindungen: %(prefix_type)s=%(prefixlen)s, " "%(base_prefix_type)s=%(base_prefixlen)s." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot " "associate with address scope %(address_scope_id)s because subnetpool " "ip_version is not %(ip_version)s." msgstr "" "Unzulässige Subnetzpoolzuordnung: Subnetzpool %(subnetpool_id)s kann dem " "Addressbereich %(address_scope_id)s nicht zugeordnet werden, da 'ip_version' " "für den Subnetzpool nicht %(ip_version)s ist." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot be " "associated with address scope %(address_scope_id)s." msgstr "" "Unzulässige Subnetzpoolzuordnung: Subnetzpool %(subnetpool_id)s kann nicht " "dem Adressbereich %(address_scope_id)s zugeorndet werden." #, python-format msgid "Illegal subnetpool update : %(reason)s." msgstr "Unzulässige Aktualisierung von Subnetzen : %(reason)s." #, python-format msgid "Illegal update to prefixes: %(msg)s." msgstr "Unzulässige Aktualisierung von Präfixen: %(msg)s." msgid "" "In some cases the Neutron router is not present to provide the metadata IP " "but the DHCP server can be used to provide this info. Setting this value " "will force the DHCP server to append specific host routes to the DHCP " "request. If this option is set, then the metadata service will be activated " "for all the networks." msgstr "" "In einigen Fällen ist kein Neutron-Router vorhanden, um die Metadaten-IP " "bereitzustellen. Der DHCP-Server kann jedoch für die Bereitstellung dieser " "Informationen verwendet werden. Setzen dieses Werts bewirkt, dass der DHCP-" "Server bestimmte Hostrouten an die DHCP-Anforderung anhängt. Bei Aktivierung " "dieser Option wird der Metadatendienst für alle Netze aktiviert. " msgid "" "Indicates that this L3 agent should also handle routers that do not have an " "external network gateway configured. This option should be True only for a " "single agent in a Neutron deployment, and may be False for all agents if all " "routers must have an external network gateway." msgstr "" "Gibt an, dass dieser L3-Agent auch Router ohne ein konfiguriertes externes " "Netzgateway verarbeiten soll. Diese Option sollte nur für einen einzelnen " "Agenten in einer Neutron-Implementierung auf 'True' gesetzt werden und kann " "für alle Agenten auf 'False' gesetzt werden, wenn alle Router ein externes " "Netzgateway erfordern." #, python-format msgid "Instance of class %(module)s.%(class)s must contain _cache attribute" msgstr "" "Die Instanz der Klasse %(module)s.%(class)s muss das Attribut ' _cache' " "enthalten." #, python-format msgid "Insufficient prefix space to allocate subnet size /%s" msgstr "" "Unzureichender Präfixspeicherplatz für die Zuordnung von Teilnetzgröße /%s" msgid "Insufficient rights for removing default security group." msgstr "" "Berechtigungen sind für das Entfernen der Standardsicherheitsgruppe nicht " "ausreichend." msgid "" "Integration bridge to use. Do not change this parameter unless you have a " "good reason to. This is the name of the OVS integration bridge. There is one " "per hypervisor. The integration bridge acts as a virtual 'patch bay'. All VM " "VIFs are attached to this bridge and then 'patched' according to their " "network connectivity." msgstr "" "Zu verwendende Integrationsbrücke. Ändern Sie diesen Parameter nur, wenn Sie " "gute Gründe dafür haben. Dies ist der Name der OVS-Integrationsbrücke. Es " "gibt eine pro Hypervisor. Die Integrationsbrücke agiert als virtuelle Patch-" "Bay. Alle VM-VIFs werden an diese Brücke angehängt und anschließend " "entsprechend ihrer Netzkonnektivität gepatched." msgid "Interface to monitor" msgstr "Zu überwachende Schnittstelle" msgid "" "Interval between checks of child process liveness (seconds), use 0 to disable" msgstr "" "Intervall zwischen Überprüfungen der Aktivität von untergeordneten Prozessen " "(Sekunden), verwenden Sie zum Deaktivieren '0'" msgid "Interval between two metering measures" msgstr "Intervall zwischen zwei Messungsmaßnahmen" msgid "Interval between two metering reports" msgstr "Intervall zwischen zwei Messungsberichten" #, python-format msgid "Invalid CIDR %(input)s given as IP prefix." msgstr "Ungültiges CIDR %(input)s als IP-Präfix angegeben." #, python-format msgid "Invalid Device %(dev_name)s: %(reason)s" msgstr "Ungültige Einheit %(dev_name)s: %(reason)s" #, python-format msgid "" "Invalid action '%(action)s' for object type '%(object_type)s'. Valid " "actions: %(valid_actions)s" msgstr "" "Ungültige Aktion '%(action)s' für Objekttyp '%(object_type)s'. Gültige " "Aktionen: %(valid_actions)s" #, python-format msgid "" "Invalid authentication type: %(auth_type)s, valid types are: " "%(valid_auth_types)s" msgstr "" "Ungültiger Authentifizierungstyp: %(auth_type)s, gültige Typen sind: " "%(valid_auth_types)s" #, python-format msgid "Invalid ethertype %(ethertype)s for protocol %(protocol)s." msgstr "Ungültiger Ethernet-Typ %(ethertype)s für Protokoll %(protocol)s." #, python-format msgid "Invalid format: %s" msgstr "Ungültiges Format: %s" #, python-format msgid "Invalid instance state: %(state)s, valid states are: %(valid_states)s" msgstr "" "Ungültiger Instanzstatus: %(state)s, gültige Status sind: %(valid_states)s" #, python-format msgid "Invalid mapping: '%s'" msgstr "Ungültige Zuordnung: '%s'" #, python-format msgid "Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'." msgstr "Ungültiger Bereich für Netz-VLAN: '%(vlan_range)s' - '%(error)s'." #, python-format msgid "Invalid network VXLAN port range: '%(vxlan_range)s'." msgstr "Ungültiger Netz-VXLAN-Portbereich: '%(vxlan_range)s'." #, python-format msgid "Invalid pci slot %(pci_slot)s" msgstr "Ungültiger PCI-Steckplatz %(pci_slot)s" #, python-format msgid "Invalid provider format. Last part should be 'default' or empty: %s" msgstr "" "Ungültiges Anbieterformat. Letzter Teil sollte 'default' oder leer sein: %s" #, python-format msgid "Invalid resource type %(resource_type)s" msgstr "Ungültiger Ressourcentyp %(resource_type)s" #, python-format msgid "Invalid route: %s" msgstr "Ungültige Route: %s" msgid "Invalid service provider format" msgstr "Ungültiges Diensteanbieterformat" #, python-format msgid "" "Invalid value for ICMP %(field)s (%(attr)s) %(value)s. It must be 0 to 255." msgstr "" "Ungültiger Wert für ICMP %(field)s (%(attr)s) %(value)s. Er muss zwischen 0 " "und 255 liegen." #, python-format msgid "Invalid value for port %(port)s" msgstr "Ungültiger Wert für Port %(port)s" msgid "" "Iptables mangle mark used to mark ingress from external network. This mark " "will be masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Iptables-Mangling-Markierung zum Markieren des Eingangs vom externen Netz. " "Diese Markierung wird mit 0xffff maskiert, sodass nur die unteren 16 Bits " "verwendet werden." msgid "" "Iptables mangle mark used to mark metadata valid requests. This mark will be " "masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Iptables-Mangling-Markierung zum Markieren von Metadaten gültiger " "Anforderungen. Diese Markierung wird mit 0xffff maskiert, sodass nur die " "unteren 16 Bits verwendet werden." msgid "" "Keep in track in the database of current resource quota usage. Plugins which " "do not leverage the neutron database should set this flag to False." msgstr "" "Überwachen der aktuellen Kontingentnutzung in der Datenbank. Bei Plugins, " "die die Neutron-Datenbank nicht verwenden, sollte dieses Flag auf False " "festgelegt werden" msgid "Keepalived didn't respawn" msgstr "Keepalived wurde nicht generiert" msgid "Keepalived didn't spawn" msgstr "Keepalived hat keinen Prozess erstellt." #, python-format msgid "" "Kernel HZ value %(value)s is not valid. This value must be greater than 0." msgstr "" "Der Kernel-HZ-Wert %(value)s ist nicht gültig. Dieser Wert muss größer als 0 " "sein." msgid "L3 agent failure to setup NAT for floating IPs" msgstr "L3-Agentenfehler bei der NAT-Konfiguration für Floating IPs." msgid "L3 agent failure to setup floating IPs" msgstr "L3-Agentenfehler bei der Konfiguration von Floating IPs." msgid "Limit number of leases to prevent a denial-of-service." msgstr "Anzahl von Leases begrenzen, um eine Dienstverweigerung zu verhindern." msgid "List of :" msgstr "Liste mit den Elementen :" msgid "" "List of :: or " "specifying physical_network names usable for VLAN provider and tenant " "networks, as well as ranges of VLAN tags on each available for allocation to " "tenant networks." msgstr "" "Liste mit :: oder , " "die physical_network-Namen angeben, die für VLAN-Provider- und Nutzer-Netze " "verwendet werden können, wie auch als Bereiche von VLAN-Tags für jedes " "verfügbare Netz für die Zuordnung zu Nutzernetzen." msgid "" "List of network type driver entrypoints to be loaded from the neutron.ml2." "type_drivers namespace." msgstr "" "Liste der Netztypentreibereingangspunkte, die aus dem Namensbereich neutron." "ml2.type_drivers geladen werden." msgid "" "List of physical_network names with which flat networks can be created. Use " "default '*' to allow flat networks with arbitrary physical_network names. " "Use an empty list to disable flat networks." msgstr "" "Liste von physical_network-Namen, mit denen einfache Netze erstellt werden " "können. Verwenden Sie den Standardwert '*', um einfache Netze mit beliebigen " "physical_network-Namen zuzulassen. Verwenden Sie eine leere Liste, um " "einfache Netze zu inaktivieren." msgid "Location for Metadata Proxy UNIX domain socket." msgstr "Position für UNIX-Domänensocket von Metadaten-Proxy." msgid "Location of Metadata Proxy UNIX domain socket" msgstr "Position von UNIX-Domänensocket von Metadatenproxy" msgid "Location to store DHCP server config files." msgstr "Position zum Speichern von Konfigurationsdateien des DHCP-Servers." msgid "Location to store IPv6 PD files." msgstr "Position zum Speichern von IPv6-PD-Dateien." msgid "Location to store IPv6 RA config files" msgstr "Position zum Speichern von IPv6-RA-Konfigurationsdateien" msgid "Location to store child pid files" msgstr "Position zum Speichern von untergeordneten PID-Dateien" msgid "Location to store keepalived/conntrackd config files" msgstr "Position zum Speichern von keepalived/conntrackd-Konfigurationsdateien" msgid "Log agent heartbeats" msgstr "Überwachungssignale von Agenten protokollieren" msgid "" "MTU of the underlying physical network. Neutron uses this value to calculate " "MTU for all virtual network components. For flat and VLAN networks, neutron " "uses this value without modification. For overlay networks such as VXLAN, " "neutron automatically subtracts the overlay protocol overhead from this " "value. Defaults to 1500, the standard value for Ethernet." msgstr "" "MTU des zugrunde liegenden physischen Netzes. Neutron verwendet diesen Wert, " "um MTU für alle virtuellen Netzkomponenten zu berechnen. Bei einfachen und " "bei VLAN-Netzen verwendet Neutron diesen Wert ohne Modifikation. Bei Overlay-" "Netzen, wie z. B. VXLAN, zieht Neutron den Overhead des Overlay-Protokolls " "automatisch von diesem Wert ab. Nimmt standardmäßig den Wert 1500 an, dem " "Standardwert für Ethernet." msgid "MTU size of veth interfaces" msgstr "MTU-Größe von Veth-Schnittstellen" msgid "Make the l2 agent run in DVR mode." msgstr "L2-Agent im DVR-Modus ausführen." msgid "Malformed request body" msgstr "Fehlerhafter Anforderungshauptteil" #, python-format msgid "Malformed request body: %(reason)s." msgstr "Fehlerhafter Anforderungshauptteil: %(reason)s." msgid "MaxRtrAdvInterval setting for radvd.conf" msgstr "MaxRtrAdvInterval-Einstellung für radvd.conf" msgid "Maximum number of DNS nameservers per subnet" msgstr "Maximale Anzahl an DNS-Namensservern pro Subnetz" msgid "" "Maximum number of L3 agents which a HA router will be scheduled on. If it is " "set to 0 then the router will be scheduled on every agent." msgstr "" "Maximale Anzahl an L3-Agenten, für die ein HA-Router geplant wird. Bei " "Angabe von 0 wird der Router für jeden Agenten geplant." msgid "Maximum number of allowed address pairs" msgstr "Maximale Anzahl an zulässigen Adresspaaren" msgid "Maximum number of host routes per subnet" msgstr "Maximale Anzahl an Hostroutes pro Subnetz" msgid "Maximum number of routes per router" msgstr "Maximale Anzahl an Routen pro Router" msgid "" "Metadata Proxy UNIX domain socket mode, 4 values allowed: 'deduce': deduce " "mode from metadata_proxy_user/group values, 'user': set metadata proxy " "socket mode to 0o644, to use when metadata_proxy_user is agent effective " "user or root, 'group': set metadata proxy socket mode to 0o664, to use when " "metadata_proxy_group is agent effective group or root, 'all': set metadata " "proxy socket mode to 0o666, to use otherwise." msgstr "" "Modus von UNIX-Domänensocket für Metadaten-Proxy, 4 Werte zulässig: " "'deduce': Modus aus Werten von metadata_proxy_user/group ableiten, 'user': " "Modus von Metadaten-Proxy-Socket auf 0o644 festlegen, zur Verwendung, wenn " "metadata_proxy_user Agent-ausführender Benutzer oder Root ist, 'group': " "Modus von Metadaten-Proxy-Socket auf 0o664 festlegen, zur Verwendung, wenn " "metadata_proxy_group Agent-ausführende Gruppe oder Root ist, 'all': Modus " "von Metadaten-Proxy-Socket auf 0o666 festlegen, zur anderweitigen Verwendung." msgid "Metering driver" msgstr "Messungstreiber" msgid "MinRtrAdvInterval setting for radvd.conf" msgstr "MinRtrAdvInterval-Einstellung für radvd.conf" msgid "Minimize polling by monitoring ovsdb for interface changes." msgstr "" "Abfrage minimieren durch Überwachung von ovsdb auf Schnittstellenänderungen." #, python-format msgid "Missing key in mapping: '%s'" msgstr "Fehlender Schlüssel in Zuordnung: '%s'" msgid "" "Multicast group for VXLAN. When configured, will enable sending all " "broadcast traffic to this multicast group. When left unconfigured, will " "disable multicast VXLAN mode." msgstr "" "Multicastgruppe für VXLAN. Wenn sie konfiguriert ist, kann der gesamte " "Broadcastverkehr an diese Multicastgruppe gesendet werden. Ohne " "Konfiguration ist der Multicast-VXLAN-Modus inaktiviert." msgid "" "Multicast group(s) for vxlan interface. A range of group addresses may be " "specified by using CIDR notation. Specifying a range allows different VNIs " "to use different group addresses, reducing or eliminating spurious broadcast " "traffic to the tunnel endpoints. To reserve a unique group for each possible " "(24-bit) VNI, use a /8 such as 239.0.0.0/8. This setting must be the same on " "all the agents." msgstr "" "Multicastgruppe(n) für VXLAN-Schnittstelle. Ein Gruppenadressbereich, der " "mit der CIDR-Notation angegeben werden kann. Durch die Angabe eines Bereichs " "können unterschiedliche VNIs verschiedene Gruppenadressen verwenden und so " "fehlerhaften Broadcastverkehr an Tunnelendpunkt senkden oder entfernen. Wenn " "Sie eine eindeutige Gruppe für jede mögliche VNI (24 Bit) reservieren " "möchten, verwenden Sie die Einstellung /8, wie z. B. 239.0.0.0/8. Diese " "Einstellung muss für alle Agenten gleich sein." #, python-format msgid "Multiple default providers for service %s" msgstr "Mehrere Standardanbieter für Dienst %s" #, python-format msgid "Multiple plugins for service %s were configured" msgstr "Mehrere Plugins für Dienst %s wurden konfiguriert" #, python-format msgid "Multiple providers specified for service %s" msgstr "Mehrere Anbieter angegeben für Dienst %s" msgid "Multiple tenant_ids in bulk security group rule create not allowed" msgstr "" "Mehrere 'tenant_ids' bei Erstellung von Sicherheitsgruppenregel für " "Massenerstellung nicht zulässig" msgid "Must also specify protocol if port range is given." msgstr "" "Bei angegebenem Portbereich muss ebenfalls ein Protokoll angegeben werden." msgid "Must specify one or more actions on flow addition or modification" msgstr "" "Angabe von einer oder mehreren Aktionen für Ablaufhinzufügung oder Änderung " "erforderlich" msgid "Name of Open vSwitch bridge to use" msgstr "Name der zu verwendenden Open vSwitch-Brücke" msgid "" "Name of nova region to use. Useful if keystone manages more than one region." msgstr "" "Name der zu verwendenden Nova-Region. Nützlich, wenn Keystone mehrere " "Regionen verwaltet. " msgid "Namespace of the router" msgstr "Namensbereich des Routers" msgid "Native pagination depend on native sorting" msgstr "Die native Paginierung ist von der nativen Sortierung abhängig" #, python-format msgid "" "Need to apply migrations from %(project)s contract branch. This will require " "all Neutron server instances to be shutdown before proceeding with the " "upgrade." msgstr "" "Es müssen Migrationen aus dem %(project)s-Contract-Branch angewendet werden. " "Hierfür müssen alle Neutron-Serverinstanzen heruntergefahren werden, bevor " "die Aktualisierung fortgesetzt werden kann. " msgid "Negative delta (downgrade) not supported" msgstr "Negatives Delta (Herabstufung) nicht unterstützt" msgid "Negative relative revision (downgrade) not supported" msgstr "Negative relative Revision (Herabstufung) nicht unterstützt" #, python-format msgid "Network %s does not contain any IPv4 subnet" msgstr "Netz %s enthält kein IPv4-Teilnetz" #, python-format msgid "Network %s is not a valid external network" msgstr "Netz %s ist kein gültiges externes Netz" #, python-format msgid "Network %s is not an external network" msgstr "Netz %s ist kein externes Netz" #, python-format msgid "" "Network of size %(size)s, from IP range %(parent_range)s excluding IP ranges " "%(excluded_ranges)s was not found." msgstr "" "Netz der Größe %(size)s, aus IP-Bereich %(parent_range)s ausschließlich der " "IP-Bereiche %(excluded_ranges)s wurde nicht gefunden." #, python-format msgid "Network type value '%s' not supported" msgstr "Netztypwert '%s' wird nicht unterstützt" msgid "Network type value needed by the ML2 plugin" msgstr "Netztypwert für ML2-Plug-in erforderlich" msgid "Network types supported by the agent (gre and/or vxlan)." msgstr "" "Netztypen, die vom Agenten unterstützt werden ('gre' und/oder 'vxlan')." msgid "Neutron Service Type Management" msgstr "Neutron-Dienstetypverwaltung" msgid "Neutron core_plugin not configured!" msgstr "Neutron-'core_plugin' nicht konfiguriert!" msgid "No default router:external network" msgstr "Kein router:external-Standardnetz" #, python-format msgid "No default subnetpool found for IPv%s" msgstr "Kein Standardsubnetzpool für IPv%s gefunden." msgid "No default subnetpools defined" msgstr "Es wurden keine Subnetzpools definiert." #, python-format msgid "No eligible l3 agent associated with external network %s found" msgstr "Kein auswählbarer dem externen Netz %s zugeordneter L3-Agent gefunden" #, python-format msgid "No more IP addresses available for subnet %(subnet_id)s." msgstr "Keine weiteren IP-Adressen für Teilnetz %(subnet_id)s verfügbar." msgid "No more IP addresses available." msgstr "Keine weiteren IP-Adressen verfügbar." msgid "No offline migrations pending." msgstr "Keine Offline-Migrationen anstehend." #, python-format msgid "No shared key in %s fields" msgstr "Kein gemeinsam genutzter Schlüssel in %s-Feldern" msgid "Not allowed to manually assign a router to an agent in 'dvr' mode." msgstr "" "Es ist nicht zulässig, einem Agenten im Modus 'dvr' manuell einen Router " "zuzuordnen." msgid "Not allowed to manually remove a router from an agent in 'dvr' mode." msgstr "" "Es ist nicht zulässig, einen Router aus einem Agenten im Modus 'dvr' manuell " "zu entfernen." msgid "" "Number of DHCP agents scheduled to host a tenant network. If this number is " "greater than 1, the scheduler automatically assigns multiple DHCP agents for " "a given tenant network, providing high availability for DHCP service." msgstr "" "Anzahl der DHCP-Agenten, die zum Hosten eines Mandatennetzwerkes geplant " "werden. Wenn diese Zahl größer als 1 ist, weist der Scheduler automatisch " "mehrere DHCP-Agenten für ein angegebenes Nutzernetz zu, wodurch " "Hochverfügbarkeit für den DHCP-Service erreicht wird." msgid "Number of RPC worker processes dedicated to state reports queue." msgstr "" "Anzahl der RPC-Worker-Prozesse, die der Statusberichtswarteschlange " "zugewiesen ist." msgid "Number of RPC worker processes for service." msgstr "Anzahl der RPC-Verarbeitungsprozesse für den Dienst." msgid "Number of backlog requests to configure the metadata server socket with" msgstr "" "Anzahl der Rückstandanforderungen, mit denen der Metadatenserver-Socket " "konfiguriert werden soll" msgid "Number of backlog requests to configure the socket with" msgstr "" "Anzahl der Rückstandanforderungen, mit denen der Socket konfiguriert werden " "soll" msgid "" "Number of bits in an ipv4 PTR zone that will be considered network prefix. " "It has to align to byte boundary. Minimum value is 8. Maximum value is 24. " "As a consequence, range of values is 8, 16 and 24" msgstr "" "Anzahl von Bits in einer ipv4-PTR-Zone, die als Netzpräfix betrachtet wird. " "Es muss an der Bytegrenze ausgerichtet werden. Der Mindestwert ist 8. Der " "maximal zulässige Wert ist 24. Daraus ergibt sich ein Wertebereich von 8, 16 " "und 24. " msgid "" "Number of bits in an ipv6 PTR zone that will be considered network prefix. " "It has to align to nyble boundary. Minimum value is 4. Maximum value is 124. " "As a consequence, range of values is 4, 8, 12, 16,..., 124" msgstr "" "Anzahl von Bits in einer ipv6-PTR-Zone, die als Netzpräfix betrachtet wird. " "Es muss an der nyble-Grenze ausgerichtet werden. Der Mindestwert ist 4. Der " "maximal zulässige Wert ist 124. Daraus ergibt sich ein Wertebereich von 4, " "8, 12, 16,..., 124." msgid "" "Number of floating IPs allowed per tenant. A negative value means unlimited." msgstr "" "Anzahl an zulässigen dynamischen IPs pro Nutzer. Ein negativer Wert bedeutet " "unbegrenzt." msgid "" "Number of networks allowed per tenant. A negative value means unlimited." msgstr "" "Anzahl an zulässigen Netzen pro Nutzer. Ein negativer Wert bedeutet " "unbegrenzt." msgid "Number of ports allowed per tenant. A negative value means unlimited." msgstr "" "Anzahl an zulässigen Ports pro Nutzer. Ein negativer Wert bedeutet " "unbegrenzt." msgid "Number of routers allowed per tenant. A negative value means unlimited." msgstr "" "Anzahl an zulässigen Routern pro Nutzer. Ein negativer Wert bedeutet " "unbegrenzt." msgid "" "Number of seconds between sending events to nova if there are any events to " "send." msgstr "" "Anzahl der Sekunden zwischen dem Senden von Ereignissen an Nova, wenn " "Ereignisse zum Senden vorhanden sind. " msgid "Number of seconds to keep retrying to listen" msgstr "" "Anzahl der Sekunden, in denen wiederholt versucht wird, empfangsbereit zu " "sein" msgid "" "Number of security groups allowed per tenant. A negative value means " "unlimited." msgstr "" "Anzahl an zulässigen Sicherheitsgruppen pro Nutzer. Ein negativer Wert " "bedeutet unbegrenzt." msgid "" "Number of security rules allowed per tenant. A negative value means " "unlimited." msgstr "" "Anzahl an zulässigen Sicherheitsregeln pro Nutzer. Ein negativer Wert " "bedeutet unbegrenzt." msgid "" "Number of separate API worker processes for service. If not specified, the " "default is equal to the number of CPUs available for best performance." msgstr "" "Anzahl der separaten API-Worker-Prozesse für Dienst. Ohne Angabe wird als " "Standardwert die Anzahl der verfügbaren CPUs verwendet, damit die beste " "Leistung erzielt werden kann." msgid "" "Number of separate worker processes for metadata server (defaults to half of " "the number of CPUs)" msgstr "" "Anzahl der separaten Worker-Prozesse für Metadatenserver (wird standardmäßig " "auf die Hälfte der Anzahl der CPUs festgelegt)" msgid "Number of subnets allowed per tenant, A negative value means unlimited." msgstr "" "Anzahl an zulässigen Teilnetzen pro Nutzer. Ein negativer Wert bedeutet " "unbegrenzt." msgid "" "Number of threads to use during sync process. Should not exceed connection " "pool size configured on server." msgstr "" "Die Anzahl der während des Synchronisationsprozesses zu verwendenden " "Threads. Die Größe des auf dem Server konfigurierten Verbindungspools darf " "nicht überschritten werden." msgid "OK" msgstr "OK" msgid "" "OVS datapath to use. 'system' is the default value and corresponds to the " "kernel datapath. To enable the userspace datapath set this value to 'netdev'." msgstr "" "Zu verwendender OVS-Datenpfad. 'system' ist der Standardwert und entspricht " "dem Kernel-Datenpfad. Setzen Sie diesen Wert auf 'netdev', wenn Sie den " "Benutzerbereichsdatenpfad aktivieren möchten." msgid "OVS vhost-user socket directory." msgstr "OVS-vhost-user-Socketverzeichnis." #, python-format msgid "Object action %(action)s failed because: %(reason)s." msgstr "Objektaktion %(action)s fehlgeschlagen, weil: %(reason)s." msgid "Only admin can view or configure quota" msgstr "Nur Admins können Kontingente anzeigen oder konfigurieren" msgid "Only admin is authorized to access quotas for another tenant" msgstr "" "Nur Administratoren sind dazu berechtigt, auf Kontingente für andere Nutzer " "zuzugreifen" msgid "Only admins can manipulate policies on objects they do not own" msgstr "" "Nur Administratoren können Richtlinien an Objekten bearbeiten, deren Eigner " "sie nicht sind." msgid "Only allowed to update rules for one security profile at a time" msgstr "" "Aktualisierung von Regeln nicht für mehrere Sicherheitsprofile gleichzeitig " "zulässig" msgid "Only remote_ip_prefix or remote_group_id may be provided." msgstr "Nur Angabe von 'remote_ip_prefix' oder 'remote_group_id' ist zulässig." msgid "OpenFlow interface to use." msgstr "Zu verwendende OpenFlow-Schnittstelle." #, python-format msgid "" "Operation %(op)s is not supported for device_owner %(device_owner)s on port " "%(port_id)s." msgstr "" "Operation %(op)s wird nicht unterstützt für device_owner %(device_owner)s " "auf Port %(port_id)s." #, python-format msgid "Operation not supported on device %(dev_name)s" msgstr "Operation auf Einheit %(dev_name)s nicht unterstützt" msgid "" "Ordered list of network_types to allocate as tenant networks. The default " "value 'local' is useful for single-box testing but provides no connectivity " "between hosts." msgstr "" "Sortierte Liste der network_types für die Zuordnung als Mandantennetze. Der " "Standardwert 'local' ist hilfreich für Einzeltests, bietet jedoch keine " "Konnektivität zwischen Hosts." msgid "Override the default dnsmasq settings with this file." msgstr "Standard-'dnsmasq'-Einstellungen mit dieser Datei außer Kraft setzen." msgid "Owner type of the device: network/compute" msgstr "Eigentümertyp des Geräts: Netz/Rechenknoten" msgid "POST requests are not supported on this resource." msgstr "POST-Anforderungen werden auf dieser Ressource nicht unterstützt." #, python-format msgid "Package %s not installed" msgstr "Paket %s nicht installiert" #, python-format msgid "Parsing bridge_mappings failed: %s." msgstr "Analysieren von 'bridge_mappings' fehlgeschlagen: %s." msgid "Password for connecting to designate in admin context" msgstr "" "Kennwort zum Herstellen einer Verbindung zu Designate im " "Administratorkontext." msgid "Path to PID file for this process" msgstr "Pfad zur PID-Datei für diesen Prozess" msgid "Path to the router directory" msgstr "Pfad zum Routerverzeichnis" msgid "Peer patch port in integration bridge for tunnel bridge." msgstr "Peer-Patch-Port in Integrationsbrücke für Tunnelbrücke." msgid "Peer patch port in tunnel bridge for integration bridge." msgstr "Peer-Patch-Port in Tunnelbrücke für Integrationsbrücke." msgid "Per-tenant subnet pool prefix quota exceeded." msgstr "Kontingent für Subnetzpoolpräfix pro Mandant überschritten." msgid "Phase upgrade options do not accept revision specification" msgstr "Phasenupgradeoptionen akzeptieren keine Revisionsspezifikation" msgid "Ping timeout" msgstr "Ping-Zeitlimitüberschreitung" msgid "Plugin does not support updating provider attributes" msgstr "" "Aktualisieren von Provider-Attributen wird von Plugin nicht unterstützt" #, python-format msgid "Port %(id)s does not have fixed ip %(address)s" msgstr "Port %(id)s verfügt nicht über statische IP-Adresse %(address)s" #, python-format msgid "Port %(port_id)s is already acquired by another DHCP agent" msgstr "" "Der Port %(port_id)s wird bereits von einem anderen DHCP-Agenten verwendet." #, python-format msgid "" "Port %s has multiple fixed IPv4 addresses. Must provide a specific IPv4 " "address when assigning a floating IP" msgstr "" "Port %s besitzt mehrere statische IPv4-Adressen. Es muss eine bestimmte IPv4-" "Adresse angegeben werden, wenn eine dynamische IP zugewiesen wird" msgid "" "Port to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "Port, der auf OpenFlow-Verbindungen überwacht werden soll. Wird nur für " "'native' Treiber verwendet." #, python-format msgid "Prefix '%(prefix)s' not supported in IPv%(version)s pool." msgstr "Präfix '%(prefix)s' wird in IPv%(version)s-Pool nicht unterstützt." msgid "Prefix Delegation can only be used with IPv6 subnets." msgstr "Präfixdelegierung kann nur bei IPv6-Teilnetzen verwendet werden." msgid "Private key of client certificate." msgstr "Privater Schlüssel für Clientzertifikat." #, python-format msgid "Probe %s deleted" msgstr "Stichprobe %s gelöscht" #, python-format msgid "Probe created : %s " msgstr "Stichprobe erstellt: %s " msgid "Process is already started" msgstr "Prozess wurde bereits gestartet" msgid "Process is not running." msgstr "Prozess läuft nicht." msgid "Protocol to access nova metadata, http or https" msgstr "Protokoll für den Zugriff auf Nova-Metadaten, HTTP oder HTTPS" #, python-format msgid "Provider name %(name)s is limited by %(len)s characters" msgstr "Der Providername %(name)s ist auf %(len)s Zeichen begrenzt." #, python-format msgid "QoS Policy %(policy_id)s is used by %(object_type)s %(object_id)s." msgstr "" "QoS-Richtlinie %(policy_id)s wird durch %(object_type)s %(object_id)s " "verwendet." #, python-format msgid "" "QoS binding for network %(net_id)s and policy %(policy_id)s could not be " "found." msgstr "" "Die QoS-Bindung für das Netz %(net_id)s und die Richtlinie %(policy_id)s " "konnten nicht gefunden werden." #, python-format msgid "" "QoS binding for port %(port_id)s and policy %(policy_id)s could not be found." msgstr "" "Die QoS-Bindung für den Port %(port_id)s und die Richtlinie %(policy_id)s " "konnten nicht gefunden werden." #, python-format msgid "QoS policy %(policy_id)s could not be found." msgstr "Die QoS-Richtlinie %(policy_id)s konnte nicht gefunden werden. " #, python-format msgid "QoS rule %(rule_id)s for policy %(policy_id)s could not be found." msgstr "" "Die QoS-Regel %(rule_id)s für die Richtlinie %(policy_id)s konnte nicht " "gefunden werden." #, python-format msgid "Quota for tenant %(tenant_id)s could not be found." msgstr "Das Kontingent für Tenant %(tenant_id)s wurde nicht gefunden." #, python-format msgid "RBAC policy of type %(object_type)s with ID %(id)s not found" msgstr "RBAC-Richtlinie des Typs %(object_type)s mit ID %(id)s nicht gefunden" #, python-format msgid "" "RBAC policy on object %(object_id)s cannot be removed because other objects " "depend on it.\n" "Details: %(details)s" msgstr "" "RBAC-Richtlinie für Objekt %(object_id)s kann nicht entfernt werden, da " "weitere Objekte von ihr abhängen.\n" "Details: %(details)s" msgid "" "Range of seconds to randomly delay when starting the periodic task scheduler " "to reduce stampeding. (Disable by setting to 0)" msgstr "" "Dauer in Sekunden, für die zufallsgeneriert beim Starten des Schedulers für " "regelmäßige Tasks gewartet werden soll, um die Belastung zu reduzieren. " "(Inaktivierung durch Festlegen auf 0)" msgid "Ranges must be in the same IP version" msgstr "Bereiche müssen dieselbe IP-Version haben." msgid "Ranges must be netaddr.IPRange" msgstr "Bereiche müssen 'netaddr.IPRange' sein." msgid "Ranges must not overlap" msgstr "Bereiche dürfen nicht überlappen." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.EUI type." msgstr "Typ '%(type)s' und Wert '%(value)s'. Erwartet wurde netaddr.EUI-Typ." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPAddress " "type." msgstr "" "Typ '%(type)s' und Wert '%(value)s' empfangen. Erwartet wurde netaddr." "IPAddress-Typ." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPNetwork " "type." msgstr "" "Typ '%(type)s' und Wert '%(value)s' empfangen. Erwartet wurde netaddr." "IPNetwork-Typ." #, python-format msgid "" "Release aware branch labels (%s) are deprecated. Please switch to expand@ " "and contract@ labels." msgstr "" "Versionssensitive Zweigbezeichnungen (%s) werden nicht weiter unterstützt. " "Wechseln Sie zu expand@- und contract@-Bezeichnungen." msgid "Remote metadata server experienced an internal server error." msgstr "Interner Serverfehler bei fernem Metadatenserver." msgid "" "Repository does not contain HEAD files for contract and expand branches." msgstr "" "Das Repository enthält keine HEAD-Dateien für Contract- und Expand-Branches. " msgid "" "Representing the resource type whose load is being reported by the agent. " "This can be \"networks\", \"subnets\" or \"ports\". When specified (Default " "is networks), the server will extract particular load sent as part of its " "agent configuration object from the agent report state, which is the number " "of resources being consumed, at every report_interval.dhcp_load_type can be " "used in combination with network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler When the network_scheduler_driver is " "WeightScheduler, dhcp_load_type can be configured to represent the choice " "for the resource being balanced. Example: dhcp_load_type=networks" msgstr "" "Darstellung des Ressourcentyps, zu dessen Arbeitslast vom Agenten Bericht " "erstattet wird. Dies kann \"networks\", \"subnets\" oder \"ports\" sein. Bei " "Angabe (Standardwert ist 'networks') extrahiert der Server bei jedem " "report_interval eine bestimmte Arbeitslast, die als Teil des " "Agentenkonfigurationsobjekts vom Agentenberichtsstatus, der der Anzahl der " "konsumierten Ressourcen entspricht, gesendet wird. dhcp_load_type kann in " "Verbindung mit network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler verwendet werden. Wenn der " "network_scheduler_driver WeightScheduler ist, kann dhcp_load_type so " "konfiguriert werden, dass die Auswahl für die Ressource mit Lastausgleich " "dargestellt wird. Beispiel: dhcp_load_type=networks" msgid "Request Failed: internal server error while processing your request." msgstr "" "Anforderung fehlgeschlagen: interner Serverfehler bei Verarbeitung Ihrer " "Anforderung." msgid "" "Reset flow table on start. Setting this to True will cause brief traffic " "interruption." msgstr "" "Zurücksetzen der Ablauftabelle beim Start. Bei der Einstellung True erfolgt " "eine kurze Unterbrechung des Datenverkehrs." #, python-format msgid "Resource %(resource)s %(resource_id)s could not be found." msgstr "Ressource %(resource)s %(resource_id)s konnte nicht gefunden werden." #, python-format msgid "Resource %(resource_id)s of type %(resource_type)s not found" msgstr "Ressource %(resource_id)s des Typs %(resource_type)s nicht gefunden" #, python-format msgid "" "Resource '%(resource_id)s' is already associated with provider " "'%(provider)s' for service type '%(service_type)s'" msgstr "" "Ressource '%(resource_id)s' ist bereits Anbieter '%(provider)s' für " "Dienstetyp '%(service_type)s' zugeordnet" msgid "Resource body required" msgstr "Ressourcen-Nachrichtentext erforderlich" msgid "Resource not found." msgstr "Ressource nicht gefunden." msgid "Resources required" msgstr "Ressourcen erforderlich" msgid "" "Root helper application. Use 'sudo neutron-rootwrap /etc/neutron/rootwrap." "conf' to use the real root filter facility. Change to 'sudo' to skip the " "filtering and just run the command directly." msgstr "" "Roothilfeprogramm. Setzen Sie 'sudo neutron-rootwrap /etc/neutron/rootwrap." "conf' ab, um die echte Rootfilterfunktion zu verwenden. Wechseln Sie zu " "'sudo', um das Filtern zu überspringen und den Befehl direkt auszuführen. " msgid "Root permissions are required to drop privileges." msgstr "Rootberechtigungen sind zum Löschen von Berechtigungen erforderlich." #, python-format msgid "Router '%(router_id)s' is not compatible with this agent." msgstr "Router '%(router_id)s' ist mit diesem Agenten nicht kompatibel." #, python-format msgid "Router already has a port on subnet %s" msgstr "Router verfügt bereits über einen Port auf Teilnetz %s" msgid "Router port must have at least one fixed IP" msgstr "Der Router-Port muss mindestens eine feste IP-Adresse haben." #, python-format msgid "Running %(cmd)s (%(desc)s) for %(project)s ..." msgstr "Ausführen von %(cmd)s (%(desc)s) für %(project)s ..." #, python-format msgid "Running %(cmd)s for %(project)s ..." msgstr "Ausführen von %(cmd)s für %(project)s ..." msgid "" "Seconds between nodes reporting state to server; should be less than " "agent_down_time, best if it is half or less than agent_down_time." msgstr "" "Sekunden zwischen Status-Berichten von Knoten an Server; sollte geringer " "sein als agent_down_time; am besten sollte es die Hälfte oder weniger von " "agent_down_time betragen." msgid "Seconds between running periodic tasks." msgstr "Sekunden zwischen Ausführungen regelmäßig wiederkehrender Tasks." msgid "" "Seconds to regard the agent is down; should be at least twice " "report_interval, to be sure the agent is down for good." msgstr "" "Sekunden bis zur Annahme, dass der Agent inaktiv ist; sollte mindestens " "doppelt so hoch sein wie report_interval, damit sichergestellt ist, dass der " "Agent wirklich inaktiv ist." #, python-format msgid "Security Group %(id)s %(reason)s." msgstr "Sicherheitsgruppe %(id)s %(reason)s." #, python-format msgid "Security Group Rule %(id)s %(reason)s." msgstr "Sicherheitsgruppenregel %(id)s %(reason)s." #, python-format msgid "Security group %(id)s does not exist" msgstr "Sicherheitsgruppe %(id)s ist nicht vorhanden" #, python-format msgid "Security group rule %(id)s does not exist" msgstr "Sicherheitsgruppenregel %(id)s ist nicht vorhanden" #, python-format msgid "Security group rule already exists. Rule id is %(rule_id)s." msgstr "" "Die Sicherheitsgruppenregel ist bereits vorhanden. Die Regel-ID ist " "%(rule_id)s." #, python-format msgid "" "Security group rule for ethertype '%(ethertype)s' not supported. Allowed " "values are %(values)s." msgstr "" "Sicherheitsgruppenregel für Ethernet-Typ '%(ethertype)s' wird nicht " "unterstützt. Zulässige Werte: %(values)s." #, python-format msgid "" "Security group rule protocol %(protocol)s not supported. Only protocol " "values %(values)s and integer representations [0 to 255] are supported." msgstr "" "Regelprotokoll %(protocol)s für Sicherheitsgruppe nicht unterstützt. Nur " "Protokollwerte %(values)s und ganzzahlige Darstellungen [0 bis 255] werden " "unterstützt." msgid "Segments and provider values cannot both be set." msgstr "" "Es können nicht Segment- und Providerwerte gleichzeitig festgelegt werden." msgid "Selects the Agent Type reported" msgstr "Wählt den gemeldeten Agententyp aus" msgid "" "Send notification to nova when port data (fixed_ips/floatingip) changes so " "nova can update its cache." msgstr "" "Benachrichtigung an Nova senden, wenn sich die Portdaten (fixed_ips/" "floatingip) ändern, damit Nova den Zwischenspeicher aktualisieren kann. " msgid "Send notification to nova when port status changes" msgstr "Benachrichtigung an Nova senden, wenn sich der Portstatus ändert" #, python-format msgid "" "Service provider '%(provider)s' could not be found for service type " "%(service_type)s" msgstr "" "Diensteanbieter '%(provider)s' konnte nicht für Dienstetyp %(service_type)s " "gefunden werden " msgid "Service to handle DHCPv6 Prefix delegation." msgstr "Service zum Behandeln der DHCPv6-Präfixdelegierung." #, python-format msgid "Service type %(service_type)s does not have a default service provider" msgstr "Dienstetyp %(service_type)s weist keinen Standard-Diensteanbieter auf" msgid "" "Set new timeout in seconds for new rpc calls after agent receives SIGTERM. " "If value is set to 0, rpc timeout won't be changed" msgstr "" "Neues Zeitlimit in Sekunden für neue RPC-Aufrufe festlegen, nachdem Agent " "SIGTERM empfängt. Wenn der Wert auf 0 gesetzt ist, wird das RPC-Zeitlimit " "nicht geändert" msgid "" "Set or un-set the don't fragment (DF) bit on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "DF-Bit (Don't Fragment) auf GRE/VXLAN-Tunnel für abgehende IP-Pakete " "festlegen oder die Festlegung aufheben." msgid "" "Set or un-set the tunnel header checksum on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "Kontrollsumme für Tunnelheader auf GRE/VXLAN-Tunnel für abgehende IP-Pakete " "festlegen oder die Festlegung aufheben." msgid "Shared address scope can't be unshared" msgstr "" "Freigabe des gemeinsam genutzten Adressbereichs kann nicht aufgehoben werden" msgid "String prefix used to match IPset names." msgstr "Zeichenfolgepräfix zum Abgleichen von IPset-Namen." #, python-format msgid "Sub-project %s not installed." msgstr "Unterprojekt %s nicht installiert." msgid "Subnet for router interface must have a gateway IP" msgstr "" "Teilnetz für Routerschnittstelle muss über eine Gateway-IP-Adresse verfügen" #, python-format msgid "Subnet pool %(subnetpool_id)s could not be found." msgstr "Subnetzpool %(subnetpool_id)s konnte nicht gefunden werden." msgid "Subnet pool has existing allocations" msgstr "Der Teilnetzpool verfügt über vorhandene Zuordnungen" msgid "Subnet used for the l3 HA admin network." msgstr "" "Teilnetz, das für das L3-Verwaltungsnetz für hohe Verfügbarkeit verwendet " "wird." msgid "" "Subnets hosted on the same network must be allocated from the same subnet " "pool." msgstr "" "Subnetze, die in demselben Netz gehostet werden, müssen aus demselben " "Subnetzpool zugeordnet werden." msgid "" "System-wide flag to determine the type of router that tenants can create. " "Only admin can override." msgstr "" "Systemweites Flag zum Bestimmen des Routertyps, den Nutzer erstellen können. " "Kann nur vom Administrator überschrieben werden." msgid "TCP Port used by Neutron metadata namespace proxy." msgstr "Von Neutron-Metadaten-Namensbereichsproxy verwendeter TCP-Port." msgid "TCP Port used by Nova metadata server." msgstr "Von Nova-Metadatenserver verwendeter TCP-Port." msgid "TTL for vxlan interface protocol packets." msgstr "TTL für VXLAN-Schnittstellenprotokollpakete." #, python-format msgid "Tag %(tag)s could not be found." msgstr "Schlagwort %(tag)s konnte nicht gefunden werden." #, python-format msgid "Tenant %(tenant_id)s not allowed to create %(resource)s on this network" msgstr "Nutzer %(tenant_id)s darf %(resource)s auf diesem Netz nicht erstellen" msgid "Tenant id for connecting to designate in admin context" msgstr "" "Mandanten-ID zum Herstellen einer Verbindung zu Designate im " "Administratorkontext." msgid "Tenant name for connecting to designate in admin context" msgstr "" "Mandantenname zum Herstellen einer Verbindung zu Designate im " "Administratorkontext." msgid "Tenant network creation is not enabled." msgstr "Erstellung von Mandantennetzwerken ist nicht aktiviert." msgid "Tenant-id was missing from quota request." msgstr "Fehlende Mandanten-ID in der Kontigentanforderung." msgid "" "The 'gateway_external_network_id' option must be configured for this agent " "as Neutron has more than one external network." msgstr "" "Die Option 'gateway_external_network_id' muss für diesen Agenten " "konfiguriert werden, da Neutron über mehr als ein externes Netz verfügt." msgid "" "The DHCP agent will resync its state with Neutron to recover from any " "transient notification or RPC errors. The interval is number of seconds " "between attempts." msgstr "" "Der DHCP-Agent resynchronisiert seinen Status mit Neutron zur " "Wiederherstellung nach temporären Benachrichtigungen oder RPC-Fehlern. Das " "Intervall ist die Anzahl der Sekunden zwischen den " "Wiederherstellungsversuchen." msgid "" "The DHCP server can assist with providing metadata support on isolated " "networks. Setting this value to True will cause the DHCP server to append " "specific host routes to the DHCP request. The metadata service will only be " "activated when the subnet does not contain any router port. The guest " "instance must be configured to request host routes via DHCP (Option 121). " "This option doesn't have any effect when force_metadata is set to True." msgstr "" "Der DHCP-Server kann zur Bereitstellung von Metadatenunterstützung für " "isolierte Netze beitragen. Wenn Sie diesen Wert auf 'True' setzen, hängt der " "DHCP-Server bestimmte Hostrouten an die DHCP-Anforderung an. Der " "Metadatendienst wird nur aktiviet, wenn das Subnetz keinen Router-Port " "enthält. Die Gastinstanz muss so konfiguriert sein, dass Hostrouten über " "DHCP (Option 121) angefordert werden. Diese Option ist wirkungslos, wenn " "'force_metadata' auf 'True' gesetzt wird." msgid "The UDP port to use for VXLAN tunnels." msgstr "UDP-Port für VXLAN-Tunnel." #, python-format msgid "" "The address allocation request could not be satisfied because: %(reason)s" msgstr "" "Die Adresszuordnungsanforderung konnte nicht erfüllt werden: %(reason)s" msgid "The advertisement interval in seconds" msgstr "Ankündigungsintervall in Sekunden" #, python-format msgid "The allocation pool %(pool)s is not valid." msgstr "Der Zuordnungspool %(pool)s ist nicht gültig." #, python-format msgid "" "The allocation pool %(pool)s spans beyond the subnet cidr %(subnet_cidr)s." msgstr "" "Der Zuordnungspool %(pool)s geht über das Teilnetz-CIDR %(subnet_cidr)s " "hinaus." msgid "" "The base MAC address Neutron will use for VIFs. The first 3 octets will " "remain unchanged. If the 4th octet is not 00, it will also be used. The " "others will be randomly generated." msgstr "" "Die MAC-Basisadresse, die Neutron für VIFs verwendet. Die ersten drei " "Oktetts bleiben unverändert. Wenn das vierte Oktett nicht 00 ist, wird es " "ebenfalls verwendet. Die anderen werden zufällig generiert. " msgid "" "The base mac address used for unique DVR instances by Neutron. The first 3 " "octets will remain unchanged. If the 4th octet is not 00, it will also be " "used. The others will be randomly generated. The 'dvr_base_mac' *must* be " "different from 'base_mac' to avoid mixing them up with MAC's allocated for " "tenant ports. A 4 octet example would be dvr_base_mac = fa:16:3f:4f:00:00. " "The default is 3 octet" msgstr "" "Die MAC-Basisadresse, die durch Neutron für eindeutige DVR-Instanzen " "verwendet wird. Die ersten 3 Oktetts bleiben unverändert. Wenn das 4. Oktett " "nicht 00 ist, wird es ebenfalls verwendet. Die anderen werden zufällig " "generiert. Die 'dvr_base_mac' *muss* sich von 'base_mac' unterscheiden, um " "eine Vermischung mit zugeordneten MACs für Nutzerports zu vermeiden. " "Beispiel mit 4 Oktetts: dvr_base_mac = fa:16:3f:4f:00:00. Standardmäßig " "werden 3 Oktetts verwendet" msgid "The core plugin Neutron will use" msgstr "Core-Plugin, das Neutron verwenden wird" msgid "The driver used to manage the DHCP server." msgstr "Der für die Verwaltung des DHCP-Servers verwendete Treiber." msgid "The driver used to manage the virtual interface." msgstr "" "Der für die Verwaltung der virtuellen Schnittstelle verwendete Treiber." msgid "" "The email address to be used when creating PTR zones. If not specified, the " "email address will be admin@" msgstr "" "Die beim Erstellen PTR-Zoenen zu verwendende E-Mail-Adresse. Ohne Angabe " "einer E-Mail-Adresse wird die E-Mail-Adresse admin@ verwendet." #, python-format msgid "" "The following device_id %(device_id)s is not owned by your tenant or matches " "another tenants router." msgstr "" "Die folgende device_id %(device_id)s gehört weder Ihrem Nutzer, noch " "entspricht sie dem Router eines anderen Nutzers." msgid "The interface for interacting with the OVSDB" msgstr "Die Schnittstelle zur Kommunikation mit OVSDB" msgid "" "The maximum number of items returned in a single response, value was " "'infinite' or negative integer means no limit" msgstr "" "Maximale Anzahl an in einer einzelnen Antwort zurückgegebenen Elementen. Der " "Wert 'infinite' oder eine negative Ganzzahl bedeuten, dass es keine " "Begrenzung gibt." #, python-format msgid "" "The network %(network_id)s has been already hosted by the DHCP Agent " "%(agent_id)s." msgstr "" "Das Netz %(network_id)s wurde bereits vom DHCP-Agenten %(agent_id)s gehostet." #, python-format msgid "" "The network %(network_id)s is not hosted by the DHCP agent %(agent_id)s." msgstr "" "Das Netz %(network_id)s wird nicht vom DHCP-Agenten %(agent_id)s gehostet." msgid "" "The network type to use when creating the HA network for an HA router. By " "default or if empty, the first 'tenant_network_types' is used. This is " "helpful when the VRRP traffic should use a specific network which is not the " "default one." msgstr "" "Der Netztyp, der beim Erstellen des HA-Netzes für einen HA-Router verwendet " "werden soll. Standardmäßig oder bei fehlender Angabe wird das erste " "'tenant_network_types' verwendet. Dies ist hilfreich, wenn der VRRP-" "Datenverkehr ein bestimmtes Netz verwenden soll, das nicht das Standardnetz " "ist." msgid "" "The number of seconds the agent will wait between polling for local device " "changes." msgstr "" "Die Anzahl an Sekunden, die der Agent zwischen Abfragen lokaler " "Geräteänderungen wartet." msgid "" "The number of seconds to wait before respawning the ovsdb monitor after " "losing communication with it." msgstr "" "Die Anzahl an Sekunden, die gewartet werden soll, bevor die ovsdb-" "Überwachung nach einer Kommunikationsunterbrechung erneut generiert wird." msgid "The number of sort_keys and sort_dirs must be same" msgstr "Die Anzahl an 'sort_keys' und 'sort_dirs' muss gleich sein" msgid "" "The path for API extensions. Note that this can be a colon-separated list of " "paths. For example: api_extensions_path = extensions:/path/to/more/exts:/" "even/more/exts. The __path__ of neutron.extensions is appended to this, so " "if your extensions are in there you don't need to specify them here." msgstr "" "Der Pfad für API-Erweiterungen. Beachten Sie, dass dies eine durch Punkte " "getrennte Liste von Pfaden sein kann. Beispiel: api_extensions_path = " "extensions:/path/to/more/exts:/even/more/exts. An diesen Pfad wird '__path__ " "of neutron.extensions' angehängt, sodass Sie Ihre Erweiterungen hier nicht " "mehr angeben müssen, wenn Sie dort bereits angegeben wurden." msgid "The physical network name with which the HA network can be created." msgstr "" "Der Name des physischen Netzes, mit dem das HA-Netz erstellt werden kann." #, python-format msgid "The port '%s' was deleted" msgstr "Port '%s' wurde gelöscht" msgid "The port to bind to" msgstr "Der Port an den gebunden werden soll" #, python-format msgid "The requested content type %s is invalid." msgstr "Der angeforderte Inhaltstyp %s ist ungültig." msgid "The resource could not be found." msgstr "Die Ressource konnte nicht gefunden werden." #, python-format msgid "" "The router %(router_id)s has been already hosted by the L3 Agent " "%(agent_id)s." msgstr "" "Der Router %(router_id)s wurde bereits vom L3-Agenten %(agent_id)s gehostet." msgid "" "The server has either erred or is incapable of performing the requested " "operation." msgstr "" "Auf dem Server ist entweder ein Fehler aufgetreten oder der Server kann die " "angeforderte Operation nicht ausführen." msgid "The service plugins Neutron will use" msgstr "Service-Plugins, die Neutron verwenden wird" #, python-format msgid "The subnet request could not be satisfied because: %(reason)s" msgstr "Die Teilnetzanforderung konnte nicht erfüllt werden: %(reason)s" #, python-format msgid "The subproject to execute the command against. Can be one of: '%s'." msgstr "" "Das Unterprojekt für das der Befehl ausgeführt werden soll. Mögliche Werte: " "'%s'." msgid "The type of authentication to use" msgstr "Der zu verwendende Authentifizierungtyp" msgid "" "There are routers attached to this network that depend on this policy for " "access." msgstr "" "Diesem Netz sind Router zugeordnet, die für den Zugriff von dieser " "Richtlinie abhängig sind." msgid "" "Timeout in seconds to wait for a single OpenFlow request. Used only for " "'native' driver." msgstr "" "Zeitlimit in Sekunden für die Wartezeit auf eine einzelne OpenFlow-" "Anforderung. Wird nur für 'native' Treiber verwendet." msgid "" "Timeout in seconds to wait for the local switch connecting the controller. " "Used only for 'native' driver." msgstr "" "Zeitlimit in Sekunden für die Wartezeit, in der der lokale Switch die " "Verbindung mit dem Controller hergestellt haben muss. Wird nur für 'native' " "Treiber verwendet." msgid "" "Too long prefix provided. New name would exceed given length for an " "interface name." msgstr "" "Es wurde ein Präfix angegeben, das zu lang ist. Der neue Name überschreitet " "damit die für einen Schnittstellennamen vorgegebene Länge." msgid "" "True to delete all ports on all the OpenvSwitch bridges. False to delete " "ports created by Neutron on integration and external network bridges." msgstr "" "'True' zum Löschen aller Ports auf den OpenvSwitch-Brücken. 'False' zum " "Löschen von Ports, die von Neutron auf Integrationsbrücken und externen " "Netzbrücken erstellt wurden." msgid "Tunnel IP value needed by the ML2 plugin" msgstr "Tunnel-IP-Wert für ML2-Plug-in erforderlich" msgid "Tunnel bridge to use." msgstr "Zu verwendende Tunnelbrücke." msgid "" "Type of the nova endpoint to use. This endpoint will be looked up in the " "keystone catalog and should be one of public, internal or admin." msgstr "" "Typ des zu verwendenden Nova-Endpunkts. Dieser Endpunkt wird im Keystone-" "Katalog gesucht und muss vom Typ 'public', 'internal' oder 'admin' sein." msgid "URL for connecting to designate" msgstr "URL zum Herstellen einer Verbindung zu Designate. " msgid "URL to database" msgstr "URL an Datenbank" #, python-format msgid "Unable to access %s" msgstr "Kein Zugriff auf %s möglich" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, maximum allowed " "prefix is %(max_prefixlen)s." msgstr "" "Das Subnetz mit der Präfixlänge %(prefixlen)s kann nicht zugeordnet werden. " "Die zulässige Maximalpräfixlänge ist %(max_prefixlen)s." #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, minimum allowed " "prefix is %(min_prefixlen)s." msgstr "" "Das Subnetz mit der Präfixlänge %(prefixlen)s kann nicht zugeordnet werden. " "Die zulässige Mindestpräfixlänge ist %(min_prefixlen)s." #, python-format msgid "Unable to calculate %(address_type)s address because of:%(reason)s" msgstr "Fehler beim Berechnen der %(address_type)s-Adresse. Grund: %(reason)s" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of DNS " "nameservers exceeds the limit %(quota)s." msgstr "" "Operation kann für %(subnet_id)s nicht abgeschlossen werden. Die Anzahl an " "DNS-Namensservern überschreitet den Grenzwert %(quota)s." #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of host routes " "exceeds the limit %(quota)s." msgstr "" "Operation kann für %(subnet_id)s nicht abgeschlossen werden. Die Anzahl an " "Hostroutes überschreitet den Grenzwert %(quota)s." #, python-format msgid "Unable to convert value in %s" msgstr "Wert in %s kann nicht konvertiert werden" msgid "Unable to create the Agent Gateway Port" msgstr "Agent-Gateway-Port kann nicht erstellt werden" msgid "Unable to create the SNAT Interface Port" msgstr "SNAT-Schnittstellenport kann nicht erstellt werden" #, python-format msgid "" "Unable to create the flat network. Physical network %(physical_network)s is " "in use." msgstr "" "Das einfache Netz kann nicht erstellt werden. Das physische Netz " "%(physical_network)s ist belegt." msgid "" "Unable to create the network. No available network found in maximum allowed " "attempts." msgstr "" "Das Netz kann nicht erstellt werden. Es wurde bei den maximal zulässigen " "Versuchen kein verfügbares Netz gefunden." #, python-format msgid "Unable to delete subnet pool: %(reason)s." msgstr "Löschen von Subnetzpool nicht möglich: %(reason)s" #, python-format msgid "Unable to determine mac address for %s" msgstr "MAC-Adresse für %s kann nicht bestimmt werden" #, python-format msgid "Unable to find '%s' in request body" msgstr "'%s' kann in Anforderungshauptteil nicht gefunden werden" #, python-format msgid "Unable to find IP address %(ip_address)s on subnet %(subnet_id)s" msgstr "" "IP-Adresse %(ip_address)s auf Teilnetz %(subnet_id)s wurde nicht gefunden" #, python-format msgid "Unable to find resource name in %s" msgstr "Ressourcenname kann nicht in %s gefunden werden" #, python-format msgid "Unable to generate unique mac on network %(net_id)s." msgstr "" "Eindeutige MAC-Adresse kann auf Netz %(net_id)s nicht generiert werden." #, python-format msgid "" "Unable to identify a target field from:%s. Match should be in the form " "%%()s" msgstr "" "Zielfeld kann nicht aus %s identifiziert werden. Übereinstimmung sollte im " "Format %%()s vorliegen" msgid "Unable to provide external connectivity" msgstr "Externe Konnektivität kann nicht bereitgestellt werden." msgid "Unable to provide tenant private network" msgstr "Das private Mandantennetz kann nicht bereitgestellt werden." #, python-format msgid "" "Unable to reconfigure sharing settings for network %(network)s. Multiple " "tenants are using it." msgstr "" "Freigabeeinstellungen für Netz %(network)s können nicht rekonfiguriert " "werden. Mehrere Mandanten verwenden es." #, python-format msgid "" "Unable to verify match:%(match)s as the parent resource: %(res)s was not " "found" msgstr "" "Übereinstimmung %(match)s kann nicht als übergeordnete Ressource bestätigt " "werden: %(res)s wurde nicht gefunden" #, python-format msgid "Unexpected label for script %(script_name)s: %(labels)s" msgstr "Nicht erwartete Bezeichnung für Script %(script_name)s: %(labels)s" #, python-format msgid "Unexpected number of alembic branch points: %(branchpoints)s" msgstr "Unerwartete Anzahl an Alembic-Verzweigungspunkten: %(branchpoints)s" #, python-format msgid "Unexpected response code: %s" msgstr "Unerwarteter Antwortcode: %s" #, python-format msgid "Unexpected response: %s" msgstr "Unerwartete Antwort: %s" #, python-format msgid "Unit name '%(unit)s' is not valid." msgstr "Einheitenname '%(unit)s' ist nicht gültig." #, python-format msgid "Unknown address type %(address_type)s" msgstr "Unbekannter Adresstyp %(address_type)s" #, python-format msgid "Unknown attribute '%s'." msgstr "Unbekanntes Attribut '%s'." #, python-format msgid "Unknown chain: %r" msgstr "Unbekannte Kette: %r" #, python-format msgid "Unknown network type %(network_type)s." msgstr "Unerwarteter Netztyp %(network_type)s." #, python-format msgid "Unknown quota resources %(unknown)s." msgstr "Unbekannte Quotenressourcen %(unknown)s." msgid "Unmapped error" msgstr "Nicht zugeordneter Fehler" msgid "Unrecognized action" msgstr "Nicht erkannte Aktion" #, python-format msgid "Unrecognized attribute(s) '%s'" msgstr "Nicht erkannte(s) Attribut(e) '%s'" msgid "Unrecognized field" msgstr "Nicht erkanntes Feld" msgid "Unsupported Content-Type" msgstr "Nicht unterstützter Inhaltstyp" #, python-format msgid "Unsupported network type %(net_type)s." msgstr "Nicht unterstützter Netztyp %(net_type)s." #, python-format msgid "Unsupported port state: %(port_state)s." msgstr "Nicht unterstützter Portstatus: %(port_state)s." msgid "Unsupported request type" msgstr "Nicht unterstützter Anforderungstyp" msgid "Updating default security group not allowed." msgstr "Aktualisieren von Standardsicherheitsgruppe nicht zulässig." msgid "" "Use ML2 l2population mechanism driver to learn remote MAC and IPs and " "improve tunnel scalability." msgstr "" "ML2-l2population-Mechanismus-Treiber verwenden, um ferne MAC- und IP-" "Adressen abzurufen und die Tunnelskalierbarkeit zu verbessern." msgid "Use broadcast in DHCP replies." msgstr "Verwenden Sie Broadcast in DHCP-Antworten." msgid "Use either --delta or relative revision, not both" msgstr "" "Verwenden Sie entweder --delta oder relative Revision, nicht beides gemeinsam" msgid "" "Use ipset to speed-up the iptables based security groups. Enabling ipset " "support requires that ipset is installed on L2 agent node." msgstr "" "Verwenden Sie ipset, um die Geschwindigkeit der auf iptables basierenden " "Sicherheitsgruppen zu verbessern. Für die Aktivierung der ipset-" "Unterstützung ist es erforderlich, dass ipset auf einem L2-Agentenknoten " "installiert ist." msgid "" "Use the root helper when listing the namespaces on a system. This may not be " "required depending on the security configuration. If the root helper is not " "required, set this to False for a performance improvement." msgstr "" "Verwenden Sie das Roothilfeprogramm beim Auflisten von Namensbereichen in " "einem System. Dies ist möglicherweise je nach Sicherheitskonfiguration nicht " "erforderlich. Wenn das Roothilfeprogramm nicht erforderlich ist, setzen Sie " "es zugunsten einer Leistungsverbesserung auf 'False'." msgid "" "Use veths instead of patch ports to interconnect the integration bridge to " "physical networks. Support kernel without Open vSwitch patch port support so " "long as it is set to True." msgstr "" "Verwenden Sie virtuelles Ethernet anstelle von Patch-Ports, um die " "Integrationsbrücke mit physischen Netzen zu verbinden. Kernels ohne Patch-" "Port-Unterstützung durch Open vSwitch werden unterstützt, vorausgesetzt der " "Wert ist auf 'True' gesetzt." msgid "" "User (uid or name) running metadata proxy after its initialization (if " "empty: agent effective user)." msgstr "" "Benutzer (Benutzer-ID oder Name), der Metadaten-Proxy nach der " "Initialisierung ausführt (falls leer: Agent-ausführender Benutzer)." msgid "User (uid or name) running this process after its initialization" msgstr "" "Benutzer (Benutzer-ID oder Name), der diesen Prozess nach der " "Initialisierung ausführt" msgid "Username for connecting to designate in admin context" msgstr "" "Benutzername zum Herstellen einer Verbindung zu Designate im " "Administratorkontext." msgid "VRRP authentication password" msgstr "VRRP-Authentifizierungskennwort" msgid "VRRP authentication type" msgstr "VRRP-Authentifizierungstyp" msgid "VXLAN network unsupported." msgstr "VXLAN-Netzwerk nicht unterstützt." msgid "" "Value of host kernel tick rate (hz) for calculating minimum burst value in " "bandwidth limit rules for a port with QoS. See kernel configuration file for " "HZ value and tc-tbf manual for more information." msgstr "" "Der Wert der Host-Kernel-Aktualisierungsrate (hz) für die Berechnung des " "Mindest-Burst-Werts für Bandbreitengrenzwertregeln für einen Port mit QoS. " "Informationen zum HZ-Wert finden Sie in der Kernel-Konfigurationsdatei und " "im tc-tbf-Handbuch." msgid "" "Value of latency (ms) for calculating size of queue for a port with QoS. See " "tc-tbf manual for more information." msgstr "" "Wert der Latenzzeit (ms) für die Berechnung der Warteschlangengröße für " "einen Port mit QoS. Weitere Informationen finden Sie im tc-tbf-Handbuch." msgid "" "When external_network_bridge is set, each L3 agent can be associated with no " "more than one external network. This value should be set to the UUID of that " "external network. To allow L3 agent support multiple external networks, both " "the external_network_bridge and gateway_external_network_id must be left " "empty." msgstr "" "Wenn 'external_network_bridge' definiert ist, kann jeder L3-Agent maximal " "einem externen Netz zugeordnet werden. Dieser Wert sollte für dieses externe " "Netz auf 'UUID' gesetzt werden. Wenn L3-Agenten mehrere externe Netze " "unterstützen können sollen, müssen sowohl der Wert für " "'external_network_bridge' als auch der Wert für " "'gateway_external_network_id' leer bleiben. " msgid "" "When proxying metadata requests, Neutron signs the Instance-ID header with a " "shared secret to prevent spoofing. You may select any string for a secret, " "but it must match here and in the configuration used by the Nova Metadata " "Server. NOTE: Nova uses the same config key, but in [neutron] section." msgstr "" "Beim Proxy-Vorgang von Metadatenanforderungen unterzeichnet Neutron den " "Instanz-ID-Header mit einem geheimen Schlüssel für gemeinsame Nutzung, um " "Spoofing zu verhindern. Sie können für einen geheimen Schlüssel eine " "beliebige Zeichenfolge auswählen. Sie muss jedoch hier und in der vom Nova-" "Metadatenserver verwendeten Konfiguration identisch sein. Hinweis: Nova " "verwendet denselben Konfigurationsschlüssel, allerdings im Abschnitt " "[neutron]." msgid "" "Where to store Neutron state files. This directory must be writable by the " "agent." msgstr "" "Position zum Speichern von Neutron-Statusdateien. Dieses Verzeichnis muss " "für den Agenten beschreibbar sein." msgid "" "With IPv6, the network used for the external gateway does not need to have " "an associated subnet, since the automatically assigned link-local address " "(LLA) can be used. However, an IPv6 gateway address is needed for use as the " "next-hop for the default route. If no IPv6 gateway address is configured " "here, (and only then) the neutron router will be configured to get its " "default route from router advertisements (RAs) from the upstream router; in " "which case the upstream router must also be configured to send these RAs. " "The ipv6_gateway, when configured, should be the LLA of the interface on the " "upstream router. If a next-hop using a global unique address (GUA) is " "desired, it needs to be done via a subnet allocated to the network and not " "through this parameter. " msgstr "" "Mit IPv6 benötigt das Netz, das für das externe Gateway verwendet wird, kein " "zugehöriges Teilnetz, da die automatisch zugewiesene LLA (Link-Local " "Address) verwendet werden kann. Eine IPv6-Gateway-Adresse ist jedoch für die " "Verwendung als Next-Hop für die Standardroute erforderlich. Ist hier keine " "IPv6-Gateway-Adresse konfiguriert (und nur dann), wird der Neutron-Router so " "konfiguriert, dass er die Standardroute von RAs (Router Advertisements) vom " "vorgeschalteten Router erhält; in diesem Fall muss der vorgeschaltete Router " "ebenfalls zum Senden dieser RAs konfiguriert sein. Wenn das ipv6_gateway " "konfiguriert ist, sollte es die LLA der Schnittstelle auf dem " "vorgeschalteten Router sein. Wenn ein Next-Hop benötigt wird, der eine GUA " "(Global Unique Address) verwendet, muss dies über ein Teilnetz geschehen, " "das dem Netz zugeordnet ist, nicht über diesen Parameter. " msgid "You must implement __call__" msgstr "Sie müssen '__call__' implementieren" msgid "" "You must provide a config file for bridge - either --config-file or " "env[NEUTRON_TEST_CONFIG_FILE]" msgstr "" "Sie müssen eine Konfigurationsdatei für die Brücke angeben: entweder '--" "config-file' oder env[NEUTRON_TEST_CONFIG_FILE]" msgid "You must provide a revision or relative delta" msgstr "Sie müssen eine Überarbeitung oder ein relatives Delta bereitstellen" msgid "a subnetpool must be specified in the absence of a cidr" msgstr "Ein Subnetzpool muss angegeben werden, wenn cidr nicht angegeben ist." msgid "add_ha_port cannot be called inside of a transaction." msgstr "" "'add_ha_port' kann nicht aus einer Transaktion heraus aufgerufenn werden." msgid "allocation_pools allowed only for specific subnet requests." msgstr "" "allocation_pools sind nur für bestimmte Teilnetzanforderungen zulässig." msgid "allocation_pools are not in the subnet" msgstr "allocation_pools sind nicht im Subnetz." msgid "allocation_pools use the wrong ip version" msgstr "allocation_pools verwenden die falsche IP-Version." msgid "already a synthetic attribute" msgstr "Ist bereits ein synthetisches Attribut" msgid "binding:profile value too large" msgstr "Bindung: Profilwert zu groß" #, python-format msgid "cannot perform %(event)s due to %(reason)s" msgstr "Ausführen von %(event)s nicht möglich. Ursache: %(reason)s" msgid "cidr and prefixlen must not be supplied together" msgstr "cidr und prefixlen dürfen nicht gemeinsam angegeben werden" #, python-format msgid "dhcp_agents_per_network must be >= 1. '%s' is invalid." msgstr "dhcp_agents_per_network muss >= 1 sein. '%s' ist ungültig." msgid "dns_domain cannot be specified without a dns_name" msgstr "'dns_domain' kann nicht ohne 'dns_name' angegeben werden." msgid "dns_name cannot be specified without a dns_domain" msgstr "'dns_name' kann nicht ohne 'dns_domain' angegeben werden." msgid "fixed_ip_address cannot be specified without a port_id" msgstr "'fixed_ip_address' kann nicht ohne 'port_id' angegeben werden" #, python-format msgid "has device owner %s" msgstr "hat Geräteeigentümer %s" msgid "in use" msgstr "im Gebrauch" #, python-format msgid "ip command failed on device %(dev_name)s: %(reason)s" msgstr "IP-Befehl fehlgeschlagen auf Einheit %(dev_name)s: %(reason)s" #, python-format msgid "ip command failed: %(reason)s" msgstr "IP-Befehl fehlgeschlagen: %(reason)s" #, python-format msgid "ip link capability %(capability)s is not supported" msgstr "IP-Link-Fähigkeit %(capability)s wird nicht unterstützt" #, python-format msgid "ip link command is not supported: %(reason)s" msgstr "IP-Link-Befehl wird nicht unterstützt: %(reason)s" msgid "ip_version must be specified in the absence of cidr and subnetpool_id" msgstr "" "ip_version muss angegeben werden, wenn cidr und subnetpool_id nicht " "angegeben sind" msgid "ipv6_address_mode is not valid when ip_version is 4" msgstr "ipv6_address_mode ist nicht gültig, wenn ip_version 4 ist" msgid "ipv6_ra_mode is not valid when ip_version is 4" msgstr "ipv6_ra_mode ist nicht gültig, wenn ip_version 4 ist" #, python-format msgid "" "ipv6_ra_mode set to '%(ra_mode)s' with ipv6_address_mode set to " "'%(addr_mode)s' is not valid. If both attributes are set, they must be the " "same value" msgstr "" "ipv6_ra_mode kann nicht auf '%(ra_mode)s' gesetzt sein, wenn " "ipv6_address_mode auf '%(addr_mode)s' gesetzt ist. Sind beide Attribute " "gesetzt, müssen sie denselben Wert aufweisen" msgid "mac address update" msgstr "MAC-Adressaktualisierung" msgid "must provide exactly 2 arguments - cidr and MAC" msgstr "Es müssen exakt 2 Argumente angegeben werden: cidr und MAC." msgid "network_type required" msgstr "network_type erforderlich" #, python-format msgid "network_type value '%s' not supported" msgstr "network_type-Wert '%s' wird nicht unterstützt" msgid "new subnet" msgstr "Neues Teilnetz" #, python-format msgid "physical_network '%s' unknown for flat provider network" msgstr "physical_network '%s' unbekannt für einfaches Anbieternetzwerk" msgid "physical_network required for flat provider network" msgstr "physical_network erforderlich für einfaches Anbieternetzwerk" #, python-format msgid "provider:physical_network specified for %s network" msgstr "'provider:physical_network' für %s-Netz angegeben" msgid "respawn_interval must be >= 0 if provided." msgstr "respawn_interval muss >= 0 sein, falls angegeben." #, python-format msgid "segmentation_id out of range (%(min)s through %(max)s)" msgstr "" "'segmentation_id' außerhalb des gültigen Bereichs (%(min)s bis %(max)s)" msgid "segmentation_id requires physical_network for VLAN provider network" msgstr "segmentation_id erfordert physical_network für VLAN-Provider-Netz" msgid "shared attribute switching to synthetic" msgstr "Gemeinsam genutztes Attribut wird in synthetisches Attribut geändert." #, python-format msgid "" "subnetpool %(subnetpool_id)s cannot be updated when associated with shared " "address scope %(address_scope_id)s" msgstr "" "Teilnetzpool %(subnetpool_id)s kann nach Zuordnung eines gemeinsam genutzten " "Adressbereichs %(address_scope_id)s nicht aktualisiert werden" msgid "subnetpool_id and use_default_subnetpool cannot both be specified" msgstr "" "Es können nicht subnetpool_id und use_default_subnetpool gleichzeitig " "festgelegt werden." msgid "the nexthop is not connected with router" msgstr "Der nächste Hop ist nicht mit dem Router verbunden" msgid "the nexthop is used by router" msgstr "Der nächste Hop wird vom Router verwendet" neutron-12.1.1/neutron/locale/tr_TR/0000775000175000017500000000000013553660156017305 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/tr_TR/LC_MESSAGES/0000775000175000017500000000000013553660156021072 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/tr_TR/LC_MESSAGES/neutron.po0000664000175000017500000016203213553660047023127 0ustar zuulzuul00000000000000# Translations template for neutron. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the neutron project. # # Translators: # ADİL REŞİT DURSUN , 2015 # Alper Çiftçi , 2015 # OpenStack Infra , 2015. #zanata # Andreas Jaeger , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: neutron VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-03-14 04:19+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-04-12 05:55+0000\n" "Last-Translator: Copied by Zanata \n" "Language: tr_TR\n" "Plural-Forms: nplurals=1; plural=0;\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 4.3.3\n" "Language-Team: Turkish (Turkey)\n" #, python-format msgid "" "\n" "Command: %(cmd)s\n" "Exit code: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" msgstr "" "\n" "Komut: %(cmd)s\n" "Çıkış kodu: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" #, python-format msgid "%(id)s is not a valid %(type)s identifier" msgstr "%(id)s geçerli bir %(type)s tanımlayıcı deÄŸil" #, python-format msgid "" "%(invalid_dirs)s is invalid value for sort_dirs, valid value is '%(asc)s' " "and '%(desc)s'" msgstr "" "%(invalid_dirs)s sort_dirs için geçersiz deÄŸer, geçerli deÄŸer '%(asc)s' ve " "'%(desc)s'" #, python-format msgid "%(key)s prohibited for %(tunnel)s provider network" msgstr "%(key)s %(tunnel)s saÄŸlayıcı ağı için yasaklanmış" #, python-format msgid "%(name)s '%(addr)s' does not match the ip_version '%(ip_version)s'" msgstr "%(name)s '%(addr)s' ip_version '%(ip_version)s' ile eÅŸleÅŸmiyor" #, python-format msgid "%s cannot be called while in offline mode" msgstr "%s çevrim dışı kipte çaÄŸrılamaz" #, python-format msgid "%s is invalid attribute for sort_keys" msgstr "%s sort_keys için geçersiz öznitelik" #, python-format msgid "%s is not a valid VLAN tag" msgstr "%s geçerli bir VLAN etiketi deÄŸil" #, python-format msgid "%s must implement get_port_from_device or get_ports_from_devices." msgstr "%s get_port_from_device veya get_ports_from_devices uygulamalıdır." #, python-format msgid "%s prohibited for VLAN provider network" msgstr "%s VLAN saÄŸlayıcı ağı için yasaklanmış" #, python-format msgid "%s prohibited for flat provider network" msgstr "%s düz saÄŸlayıcı ağı için yasaklanmış" #, python-format msgid "%s prohibited for local provider network" msgstr "%s yerel saÄŸlayıcı ağı için yasaklanmış" msgid "0 is not allowed as CIDR prefix length" msgstr "0 CIDR önek uzunluÄŸuna izin verilmez" msgid "A cidr must be specified in the absence of a subnet pool" msgstr "Alt aÄŸ havuzu olmadığında bir cidr belirtilmelidir" msgid "A metering driver must be specified" msgstr "Bir ölçme sürücüsü belirtilmeli" msgid "API for retrieving service providers for Neutron advanced services" msgstr "Neutron geliÅŸmiÅŸ servisleri için servis saÄŸlayıcıları alma API'si" msgid "Access to this resource was denied." msgstr "Bu kaynaÄŸa eriÅŸime izin verilmiyor." msgid "Action to be executed when a child process dies" msgstr "Alt süreç öldüğünde çalıştırılacak eylem" msgid "Address not present on interface" msgstr "Adres arayüzde mevcut deÄŸil" msgid "Adds test attributes to core resources." msgstr "Çekirdek kaynaklara test özniteliklerini ekler." #, python-format msgid "Agent %(id)s is not a L3 Agent or has been disabled" msgstr "Ajan %(id)s bir L3 Ajanı deÄŸil ya da kapalı" #, python-format msgid "Agent %(id)s is not a valid DHCP Agent or has been disabled" msgstr "Ajan %(id)s geçerli bir DHCP Ajanı deÄŸil veya kapalı" #, python-format msgid "Agent updated: %(payload)s" msgstr "Ajan güncellendi: %(payload)s" msgid "Allow auto scheduling networks to DHCP agent." msgstr "AÄŸların DHCP ajanlarına otomatik zamanlanmasına izin ver." msgid "Allow auto scheduling of routers to L3 agent." msgstr "Yönlendiricilerin L3 ajanına otomatik zamanlanmasına izin ver." msgid "Allow running metadata proxy." msgstr "Metadata vekili çalıştırmaya izin ver." msgid "Allow sending resource operation notification to DHCP agent" msgstr "DHCP ajanına kaynak iÅŸlem bildirimi göndermeye izin ver" msgid "Allow the usage of the bulk API" msgstr "Toplu API'nin kullanımına izin ver" msgid "Allow to perform insecure SSL (https) requests to nova metadata" msgstr "Nova metadata'ya güvensiz SSL (https) istekleri yapmaya izin ver" msgid "An interface driver must be specified" msgstr "Bir arayüz sürücüsü belirtmeniz gerekmektedir" msgid "" "An ordered list of networking mechanism driver entrypoints to be loaded from " "the neutron.ml2.mechanism_drivers namespace." msgstr "" "neutron.ml2.mechanism_drivers isim uzayından yüklenecek aÄŸ mekanizması " "sürücü giriÅŸ noktalarının sıralı listesi." msgid "An unknown error has occurred. Please try your request again." msgstr "Bilinmeyen bir hata oluÅŸtu. Lütfen tekrar deneyin." msgid "Automatically remove networks from offline DHCP agents." msgstr "AÄŸları çevrimdışı DHCP ajanlarından otomatik olarak çıkar." msgid "" "Automatically reschedule routers from offline L3 agents to online L3 agents." msgstr "" "Yönlendiricileri çevrimdışı L3 ajanlarından çevrimiçi L3 ajanlarına otomatik " "olarak yeniden zamanla." msgid "Available commands" msgstr "Kullanılabilir komutlar" msgid "Backend does not support VLAN Transparency." msgstr "Arka uç VLAN ÅŸeffaflığını desteklemiyor." #, python-format msgid "Base MAC: %s" msgstr "Taban MAC: %s" msgid "Body contains invalid data" msgstr "Gövde geçersiz veri içeriyor" #, python-format msgid "Bridge %(bridge)s does not exist." msgstr "Köprü %(bridge)s mevcut deÄŸil." msgid "Bulk operation not supported" msgstr "Toplu iÅŸlem desteklenmiyor" msgid "CIDR to monitor" msgstr "İzlenecek CIDR" #, python-format msgid "Cannot allocate IPv%(req_ver)s subnet from IPv%(pool_ver)s subnet pool" msgstr "IPv%(pool_ver)s alt aÄŸ havuzundan IPv%(req_ver)s alt ağı ayrılamaz" msgid "Cannot allocate requested subnet from the available set of prefixes" msgstr "İstenen alt aÄŸ kullanılabilir önek kümesinden ayrılamıyor" msgid "Cannot disable enable_dhcp with ipv6 attributes set" msgstr "ipv6 öznitelikleri ayarlıyken enable_dhcp kapatılamaz" #, python-format msgid "Cannot handle subnet of type %(subnet_type)s" msgstr "%(subnet_type)s türünde alt aÄŸ iÅŸlenemiyor" msgid "Cannot have multiple IPv4 subnets on router port" msgstr "Yönlendirici baÄŸlantı noktasında birden fazla IPv4 alt ağı olamaz" #, python-format msgid "" "Cannot have multiple router ports with the same network id if both contain " "IPv6 subnets. Existing port %(p)s has IPv6 subnet(s) and network id %(nid)s" msgstr "" "İkisi de IPv6 alt ağı içeriyorsa aynı aÄŸ id'si ile birden fazla yönlendirici " "baÄŸlantı noktası olamaz. Mevcut baÄŸlantı noktası %(p)s IPv6 alt aÄŸ(lar)ına " "ve %(nid)s aÄŸ kimliÄŸine sahip" msgid "Cannot specify both subnet-id and port-id" msgstr "Hem subnet-id hem port-id belirtilemez" msgid "Cannot understand JSON" msgstr "JSON anlaşılamıyor" #, python-format msgid "Cannot update read-only attribute %s" msgstr "Yalnızca okunabilir öznitelik %s güncellenemez" msgid "Certificate Authority public key (CA cert) file for ssl" msgstr "Ssl için Sertifika Yetkilisi açık anahtarı (CA cert)" msgid "Check ebtables installation" msgstr "Ebtables kurulumunu kontrol et" msgid "Check for ARP header match support" msgstr "ARP baÅŸlık eÅŸleÅŸtirme desteÄŸini kontrol et" msgid "Check for ARP responder support" msgstr "ARP yanıtlayıcısı desteÄŸini kontrol et" msgid "Check for OVS vxlan support" msgstr "OVS vxlan desteÄŸini kontrol et" msgid "Check for VF management support" msgstr "VF yönetim desteÄŸini kontrol et" msgid "Check for iproute2 vxlan support" msgstr "Iproute2 vxlan desteÄŸini kontrol et" msgid "Check for nova notification support" msgstr "Nova bildirim desteÄŸini kontrol et" msgid "Check for patch port support" msgstr "Yama baÄŸlantı noktası desteÄŸini kontrol et" msgid "Check minimal dnsmasq version" msgstr "Asgari dnsmasq sürümünü kontrol et" msgid "Check netns permission settings" msgstr "Netns izin ayarlarını kontrol et" msgid "Check ovsdb native interface support" msgstr "Ovsdb doÄŸal arayüz desteÄŸini kontrol et" #, python-format msgid "" "Cidr %(subnet_cidr)s of subnet %(subnet_id)s overlaps with cidr %(cidr)s of " "subnet %(sub_id)s" msgstr "" "%(subnet_id)s alt ağının %(subnet_cidr)s cidr'i %(sub_id)s alt ağının " "%(cidr)s cidr'i ile çakışıyor" msgid "Client certificate for nova metadata api server." msgstr "Nova metadata api sunucusu için istemci sertifikası." msgid "" "Comma-separated list of : tuples enumerating ranges of GRE " "tunnel IDs that are available for tenant network allocation" msgstr "" "Kiracı aÄŸ ayırma için kullanılabilir GRE tünel kimliklerinin aralığını " "numaralandıran : demetlerinin virgülle ayrılmış listesi" msgid "" "Comma-separated list of : tuples enumerating ranges of " "VXLAN VNI IDs that are available for tenant network allocation" msgstr "" "Kiracı ağı ayırmaları için kullanılabilir VXLAN VNI ID'lerinin aralıklarını " "numaralandıran : demetlerinin virgülle ayrılmış listesi" msgid "" "Comma-separated list of the DNS servers which will be used as forwarders." msgstr "" "Yönlendirici olarak kullanılacak DNS sunucularının virgülle ayrılmış listesi." msgid "Command to execute" msgstr "Çalıştırılacak komut" msgid "Config file for interface driver (You may also use l3_agent.ini)" msgstr "" "Arayüz sürücüsü için yapılandırma dosyası (l3_agent.ini de kullanabilirsiniz)" #, python-format msgid "Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s" msgstr "CIDR %(cidr)s için çatışan deÄŸer ethertype %(ethertype)s" msgid "" "Controls whether the neutron security group API is enabled in the server. It " "should be false when using no security groups or using the nova security " "group API." msgstr "" "Neutron güvenlik grubu API'sinin sunucuda etkin olup olmadığını kontrol " "eder. Güvenlik grubu kullanılmadığında veeya nova güvenlik grubu API'si " "kullanıldığında false olmalıdır." #, python-format msgid "Could not bind to %(host)s:%(port)s after trying for %(time)d seconds" msgstr "%(time)d saniye denedikten sonra %(host)s:%(port)s'a baÄŸlanamadı" msgid "Could not deserialize data" msgstr "Veri serisi çözülemedi" #, python-format msgid "" "Current gateway ip %(ip_address)s already in use by port %(port_id)s. Unable " "to update." msgstr "" "Mevcut geçit ip'si %(ip_address)s %(port_id)s baÄŸlantı noktası tarafından " "zaten kullanılıyor. Güncellenemiyor." msgid "" "DHCP lease duration (in seconds). Use -1 to tell dnsmasq to use infinite " "lease times." msgstr "" "DHCP kira süresi (saniye olarak). Dnsmasq'a süresiz kira zamanları " "kullanmasını söylemek için -1 kullanın." msgid "" "Default network type for external networks when no provider attributes are " "specified. By default it is None, which means that if provider attributes " "are not specified while creating external networks then they will have the " "same type as tenant networks. Allowed values for external_network_type " "config option depend on the network type values configured in type_drivers " "config option." msgstr "" "SaÄŸlayıcı öznitelikleri belirtilmediÄŸinde harici aÄŸlar için varsayılan aÄŸ " "türü. Varsayılan olarak None'dir, bunun anlamı harici aÄŸ oluÅŸtururken " "saÄŸlayıcı öznitelikleri belirtilmemiÅŸse kiracı aÄŸlarla aynı türe sahip " "olacaklarıdır. external_network_type yapılandırma seçeneÄŸi için izin verilen " "deÄŸerler type_drivers yapılandırma seçeneÄŸinde yapılandırılan aÄŸ türü " "deÄŸerlerine baÄŸlıdır." msgid "" "Default number of resource allowed per tenant. A negative value means " "unlimited." msgstr "" "Kiracı başına izin verilecek varsayılan kaynak sayısı. Negatif deÄŸer " "sınırsız anlamına gelir." msgid "Default security group" msgstr "Varsayılan güvenlik grubu" msgid "Default security group already exists." msgstr "Varsayılan güvenlik grubu zaten mevcut." msgid "" "Define the default value of enable_snat if not provided in " "external_gateway_info." msgstr "" "external_gateway_info'da saÄŸlanmamışsa enable_snat'ın varsayılan deÄŸerini " "tanımla." msgid "" "Defines providers for advanced services using the format: :" ":[:default]" msgstr "" "Åžu biçimi kullanarak gleiÅŸmiÅŸ servisler için saÄŸlayıcılar tanımlar: " "::[:default]" msgid "Delete the namespace by removing all devices." msgstr "İsim uzayını tüm aygıtları kaldırarak sil." #, python-format msgid "Deleting port %s" msgstr "BaÄŸlantı noktası %s siliniyor" msgid "Destroy IPsets even if there is an iptables reference." msgstr "Iptables referansı olsa bile IPset'leri sil." msgid "Destroy all IPsets." msgstr "Tüm IPset'leri sil." #, python-format msgid "Device %(dev_name)s in mapping: %(mapping)s not unique" msgstr "%(mapping)s eÅŸleÅŸtirmesindeki aygıt %(dev_name)s benzersiz" #, python-format msgid "Device name %(dev_name)s is missing from physical_device_mappings" msgstr "Aygıt ismi %(dev_name)s physical_device_mappings'de eksik" msgid "Device not found" msgstr "Aygıt bulunamadı" #, python-format msgid "" "Distributed Virtual Router Mac Address for host %(host)s does not exist." msgstr "" "%(host)s istemcisi için Dağıtık Sanal Yönlendirici Mac Adresi mevcut deÄŸil." msgid "Domain to use for building the hostnames" msgstr "Makine adlarını inÅŸa için kullanılacak alan" msgid "Downgrade no longer supported" msgstr "Alçaltma artık desteklenmiyor" #, python-format msgid "Driver %s is not unique across providers" msgstr "%s sürücüsü saÄŸlayıcılar arasında benzersiz deÄŸil" msgid "Driver for security groups firewall in the L2 agent" msgstr "L2 ajanındaki güvenlik grubunun güvenlik duvarı için sürücü" msgid "Driver to use for scheduling network to DHCP agent" msgstr "Ağın DHCP ajanlarına zamanlanması için kullanılacak sürücü" msgid "Driver to use for scheduling router to a default L3 agent" msgstr "Yönlendiriciyi bir L3 ajanına zamanlamak için gerekli sürücü" msgid "Duplicate Security Group Rule in POST." msgstr "POST'da Kopya Güvenlik Grubu Kuralı." msgid "Duplicate segment entry in request." msgstr "İstekte kopya dilim girdisi." #, python-format msgid "ERROR: %s" msgstr "HATA: %s" msgid "" "ERROR: Unable to find configuration file via the default search paths (~/." "neutron/, ~/, /etc/neutron/, /etc/) and the '--config-file' option!" msgstr "" "HATA: Varsayılan arama yollarıyla ve (~/.neutron/, ~/, /etc/neutron/, /etc/) " "ve '--config-file' seçeneÄŸiyle yapılandırma dosyası bulunamadı!" msgid "" "Either one of parameter network_id or router_id must be passed to _get_ports " "method." msgstr "" "_get_ports metoduna network_id veya router_id parametrelerinden biri " "verilmelidir." msgid "Either subnet_id or port_id must be specified" msgstr "subnet_id veya port_id belirtilmeli" msgid "Empty physical network name." msgstr "BoÅŸ fiziksel aÄŸ ismi." msgid "Enable HA mode for virtual routers." msgstr "Sanal yönlendiriciler için HA kipini etkinleÅŸtir." msgid "Enable SSL on the API server" msgstr "API sunucuda SSL etkinleÅŸtir" msgid "" "Enable VXLAN on the agent. Can be enabled when agent is managed by ml2 " "plugin using linuxbridge mechanism driver" msgstr "" "Ajanda VXLAN etkinleÅŸtir. Ajan linuxbridge mekanizma sürücüsünü kullanan ml2 " "eklentisi ile yönetildiÄŸinde etkinleÅŸtirilebilir" msgid "" "Enable local ARP responder if it is supported. Requires OVS 2.1 and ML2 " "l2population driver. Allows the switch (when supporting an overlay) to " "respond to an ARP request locally without performing a costly ARP broadcast " "into the overlay." msgstr "" "Destekleniyorsa yerel ARP yanıtlayıcıyı etkinleÅŸtir. OVS 2.1 ve ML2 " "I2population sürücüsüne ihtiyaç duyar. Anahtarın (kaplama desteklediÄŸinde) " "bir ARP isteÄŸine yerel olarak, kaplamaya maliyetli ARP yayını yapmadan yanıt " "vermesini saÄŸlar." msgid "" "Enable services on an agent with admin_state_up False. If this option is " "False, when admin_state_up of an agent is turned False, services on it will " "be disabled. Agents with admin_state_up False are not selected for automatic " "scheduling regardless of this option. But manual scheduling to such agents " "is available if this option is True." msgstr "" "admin_state_up False olan bir ajan üzerinde servisleri etkinleÅŸtir. Bu " "seçenek False ise, bir ajanın admin_state_up'u False yapıldığında, " "üzerindeki servisler kapatılacaktır. admin_state_up False olan ajanlar bu " "seçeneÄŸe bakılmaksızın otomatik zamanlama için seçilmezler. Ama bu seçenek " "True ise bu tür ajanlara elle zamanlama yapılabilir." msgid "End of VLAN range is less than start of VLAN range" msgstr "VLAN aralığı sonu VLAN aralığı başından daha küçük" msgid "End of tunnel range is less than start of tunnel range" msgstr "Tünel aralığı sonu tünel aralığı başından daha küçük" #, python-format msgid "Error %(reason)s while attempting the operation." msgstr "İşlem denenirken %(reason)s hatası." #, python-format msgid "Error parsing dns address %s" msgstr "%s dns adresinin ayrıştırılmasında hata" #, python-format msgid "Error while reading %s" msgstr "%s okunurken hata" #, python-format msgid "" "Exceeded %s second limit waiting for address to leave the tentative state." msgstr "" "Adresin belirsiz durumdan çıkması için %s saniye bekleme sınırı aşıldı." msgid "Existing prefixes must be a subset of the new prefixes" msgstr "Mevcut önekler yeni öneklerin alt kümesi olmalıdır" msgid "" "Extension to use alongside ml2 plugin's l2population mechanism driver. It " "enables the plugin to populate VXLAN forwarding table." msgstr "" "ml2 eklentisinin l2population mekanizma sürücüsünün yanında kullanılacak " "eklenti. Eklentiyi VXLAN iletim tablosunu doldurması için etkinleÅŸtirir." #, python-format msgid "Extension with alias %s does not exist" msgstr "%s rumuzlu eklenti mevcut deÄŸil" #, python-format msgid "External IP %s is the same as the gateway IP" msgstr "Harici IP %s geçit IP ile aynı" #, python-format msgid "Failed rescheduling router %(router_id)s: no eligible l3 agent found." msgstr "" "%(router_id)s yönlendiricisini yeniden zamanlama baÅŸarısız: seçilebilir l3 " "ajanı bulunamadı." #, python-format msgid "Failed scheduling router %(router_id)s to the L3 Agent %(agent_id)s." msgstr "" "%(router_id)s yönlendiricisinin %(agent_id)s L3 Ajanına zamanlanması " "baÅŸarısız." #, python-format msgid "" "Failed to create port on network %(network_id)s, because fixed_ips included " "invalid subnet %(subnet_id)s" msgstr "" "%(network_id)s ağı üzerinde baÄŸlantı noktası oluÅŸturma baÅŸarısız, çünkü " "fixed_ips geçersiz %(subnet_id)s alt ağını içeriyor" msgid "Failed to remove supplemental groups" msgstr "Destekleyici gruplar kaldırılamadı" #, python-format msgid "Failed to set gid %s" msgstr "Gid %s ayarlanamadı" #, python-format msgid "Failed to set uid %s" msgstr "Uid %s ayarlanamadı" #, python-format msgid "Failed to set-up %(type)s tunnel port to %(ip)s" msgstr "%(ip)s'ye %(type)s tünel baÄŸlantı noktası kurulumu baÅŸarısız" msgid "Failure applying iptables rules" msgstr "Iptables kuralları uygulanırken baÅŸarısız olundu" #, python-format msgid "Failure waiting for address %(address)s to become ready: %(reason)s" msgstr "%(address)s adresinin hazır olmasını bekleme baÅŸarısız: %(reason)s" msgid "For TCP/UDP protocols, port_range_min must be <= port_range_max" msgstr "" "TCP/UDP iletiÅŸim kuralları için, port_range_min <= port_range_max olmalı" msgid "Force ip_lib calls to use the root helper" msgstr "ip_lib çaÄŸrılarını kök yardımcıyı kullanmaya zorla" #, python-format msgid "" "Found overlapping allocation pools: %(pool_1)s %(pool_2)s for subnet " "%(subnet_cidr)s." msgstr "" "%(subnet_cidr)s alt ağı için çakışan ayırma havuzları: %(pool_1)s %(pool_2)s " "bulundu." msgid "Gateway is not valid on subnet" msgstr "Geçit alt aÄŸda geçerli deÄŸil" msgid "" "Group (gid or name) running metadata proxy after its initialization (if " "empty: agent effective group)." msgstr "" "İlklendirilmesinden sonra metadata vekilini çalıştıran grup (gid veya isim) " "(boÅŸsa: ajan etkin grup)." msgid "Group (gid or name) running this process after its initialization" msgstr "İlklendirilmesinden sonra bu süreci çalıştıran grup (gid veya isim)" #, python-format msgid "" "ICMP code (port-range-max) %(value)s is provided but ICMP type (port-range-" "min) is missing." msgstr "" "ICMP kodu (port-range-max) %(value)s saÄŸlanmış ama ICMP türü (port-range-" "min) eksik." msgid "ID of network" msgstr "AÄŸ kimliÄŸi" msgid "ID of network to probe" msgstr "Sorgulanacak aÄŸ ID'si" msgid "ID of probe port to delete" msgstr "Silinecek deneme baÄŸlantı noktasının kimliÄŸi" msgid "ID of probe port to execute command" msgstr "Komutun çalıştırılacağı deneme baÄŸlantı noktası kimliÄŸi" msgid "ID of the router" msgstr "Yönetici kimliÄŸi" #, python-format msgid "IP address %(ip)s already allocated in subnet %(subnet_id)s" msgstr "%(ip)s IP adresi %(subnet_id)s alt ağında zaten ayrılmış" #, python-format msgid "IP address %(ip)s does not belong to subnet %(subnet_id)s" msgstr "%(ip)s IP adresi %(subnet_id)s alt ağına ait deÄŸil" msgid "IP allocation requires subnet_id or ip_address" msgstr "IP ayırma subnet_id veya ip_address gerektirir" #, python-format msgid "" "IPTablesManager.apply failed to apply the following set of iptables rules:\n" "%s" msgstr "" "IPTablesManager.apply aÅŸağıdakı iptables bilgileri uygulanamadı\n" "%s" #, python-format msgid "" "IPv6 address %(ip)s cannot be directly assigned to a port on subnet " "%(subnet_id)s as the subnet is configured for automatic addresses" msgstr "" "Alt aÄŸ otomatik adres olarak yapıladırıldığı için %(ip)s IPv6 adresi " "doÄŸrudan %(subnet_id)s alt ağındaki bir baÄŸlantı noktasına atanamaz." #, python-format msgid "" "IPv6 subnet %s configured to receive RAs from an external router cannot be " "added to Neutron Router." msgstr "" "Harici bir yönlendiriciden RA almak için yapılandırılmış %s IPv6 alt ağı " "Neutron Yönlendiriciye eklenemez." msgid "" "If True, then allow plugins that support it to create VLAN transparent " "networks." msgstr "" "True ise, destekleyen eklentilerin VLAN ÅŸeffaf aÄŸlar oluÅŸturmasına izin ver." msgid "Illegal IP version number" msgstr "Kuraldışı IP sürüm numarası" #, python-format msgid "Insufficient prefix space to allocate subnet size /%s" msgstr "/%s boyutunda alt aÄŸ ayırmak için yetersiz önek alanı" msgid "Insufficient rights for removing default security group." msgstr "Varsayılan güvenlik grubunu silmek için yeterli izin yok." msgid "Interface to monitor" msgstr "İzlenecek arayüz" msgid "" "Interval between checks of child process liveness (seconds), use 0 to disable" msgstr "" "Alt süreç canlılığı kontrolleri aralığı (saniye), kapatmak için 0 kullanın" msgid "Interval between two metering measures" msgstr "İki ölçüm arasındaki aralık" msgid "Interval between two metering reports" msgstr "İki ölçme raporu arasındaki aralık" #, python-format msgid "Invalid Device %(dev_name)s: %(reason)s" msgstr "Geçersiz Aygıt %(dev_name)s: %(reason)s" #, python-format msgid "" "Invalid authentication type: %(auth_type)s, valid types are: " "%(valid_auth_types)s" msgstr "" "Geçersiz kimlik doÄŸrulama türü: %(auth_type)s, geçerli türler: " "%(valid_auth_types)s" #, python-format msgid "Invalid format: %s" msgstr "Geçersiz biçim: %s" #, python-format msgid "Invalid instance state: %(state)s, valid states are: %(valid_states)s" msgstr "Geçersiz sunucu durumu: %(state)s, geçerli durumlar: %(valid_states)s" #, python-format msgid "Invalid mapping: '%s'" msgstr "Geçersiz eÅŸleÅŸtirme: '%s'" #, python-format msgid "Invalid pci slot %(pci_slot)s" msgstr "Geçersiz pci yuvası %(pci_slot)s" #, python-format msgid "Invalid provider format. Last part should be 'default' or empty: %s" msgstr "Geçersiz saÄŸlayıcı biçimi. Son kısım 'default' ya da boÅŸ olmalı: %s" #, python-format msgid "Invalid route: %s" msgstr "Geçersiz rota: %s" msgid "Invalid service provider format" msgstr "Geçersiz servis saÄŸlayıcı biçimi" #, python-format msgid "" "Invalid value for ICMP %(field)s (%(attr)s) %(value)s. It must be 0 to 255." msgstr "" "ICMP %(field)s (%(attr)s) %(value)s için geçersiz deÄŸer. 0 dan 255'e kadar " "olmalı." #, python-format msgid "Invalid value for port %(port)s" msgstr "%(port)s baÄŸlantı noktası için geçersiz deÄŸer" msgid "Keepalived didn't respawn" msgstr "Keepalived yeniden baÅŸlamadı" msgid "Limit number of leases to prevent a denial-of-service." msgstr "Servis engellemeyi önlemek için kiralama sayısını sınırla." msgid "" "List of :: or " "specifying physical_network names usable for VLAN provider and tenant " "networks, as well as ranges of VLAN tags on each available for allocation to " "tenant networks." msgstr "" "VLAN saÄŸlayıcı ve kiracı aÄŸlar için kullanılabilir physical_network " "isimlerini belirten :: veya " " listesi, aynı zamanda her birinde kiracı aÄŸlara ayırma " "için VLAN etiketleri aralıkları." msgid "" "List of network type driver entrypoints to be loaded from the neutron.ml2." "type_drivers namespace." msgstr "" "neutron.ml2.type_drivers isim uzayından yüklenecek aÄŸ türü sürücü giriÅŸ " "noktaları listesi." msgid "Location for Metadata Proxy UNIX domain socket." msgstr "Metadata Vekil UNIX alan soketi için konum." msgid "Location of Metadata Proxy UNIX domain socket" msgstr "Metadata Vekil UNIX alan soketi konumu" msgid "Location to store IPv6 RA config files" msgstr "IPv6 RA yapılandırma dosyalarının kaydedileceÄŸi konum" msgid "Location to store child pid files" msgstr "Alt süreç dosyalarının kaydedileceÄŸi konum" msgid "Location to store keepalived/conntrackd config files" msgstr "Keepalived/conntrackd yapılandırma dosyalarının tutulacağı konum" msgid "Log agent heartbeats" msgstr "Ajan kalp atışlarını kaydet" msgid "MTU size of veth interfaces" msgstr "veth arayüzlerinin MTU boyutu" msgid "Make the l2 agent run in DVR mode." msgstr "L2 ajanın DVR kipinde çalışmasını saÄŸla." msgid "Malformed request body" msgstr "Kusurlu istek gövdesi" msgid "Maximum number of allowed address pairs" msgstr "İzin verilen adres çiftlerinin azami sayısı" msgid "Maximum number of host routes per subnet" msgstr "Alt aÄŸ başına azami istemci sayısı" msgid "Metering driver" msgstr "Ölçme sürücüsü" msgid "Minimize polling by monitoring ovsdb for interface changes." msgstr "" "Sorgulamayı ovsdb arayüzünü deÄŸiÅŸiklikler için izleyerek olabildiÄŸince azalt." #, python-format msgid "Missing key in mapping: '%s'" msgstr "EÅŸleÅŸtirmede anahtar eksik: '%s'" #, python-format msgid "Multiple default providers for service %s" msgstr "%s servisi için birden fazla varsayılan saÄŸlayıcı" #, python-format msgid "Multiple plugins for service %s were configured" msgstr "%s servisi için birden fazla eklenti yapılandırılmış" #, python-format msgid "Multiple providers specified for service %s" msgstr "%s servisi için birden fazla saÄŸlayıcı belirtilmiÅŸ" msgid "Multiple tenant_ids in bulk security group rule create not allowed" msgstr "" "Toplu güvenlik grubu kuralı oluÅŸturmada birden çok tenant_id'ye izin " "verilmiyor" msgid "Must specify one or more actions on flow addition or modification" msgstr "Akış ekleme ya da deÄŸiÅŸtirmede bir ya da fazla eylem belirtilmeli" msgid "Name of Open vSwitch bridge to use" msgstr "Kullanılacak Open vSwitch köprüsünün ismi" msgid "" "Name of nova region to use. Useful if keystone manages more than one region." msgstr "" "Kullanılacak nova gölgesinin ismi. Keystone birden fazla bölgeyi yönetiyorsa " "kullanışlıdır." msgid "Namespace of the router" msgstr "Yönetici isim uzayı" msgid "Native pagination depend on native sorting" msgstr "DoÄŸal sayfalama doÄŸal sıralamaya baÄŸlıdır" msgid "Negative delta (downgrade) not supported" msgstr "Negatif fark (alt sürüm) desteklenmiyor" msgid "Negative relative revision (downgrade) not supported" msgstr "Negatif iliÅŸkili sürüm (alt sürüm) desteklenmiyor" #, python-format msgid "Network %s is not a valid external network" msgstr "%s ağı geçerli bir harici aÄŸ deÄŸil" #, python-format msgid "Network %s is not an external network" msgstr "AÄŸ %s harici bir aÄŸ deÄŸil" #, python-format msgid "" "Network of size %(size)s, from IP range %(parent_range)s excluding IP ranges " "%(excluded_ranges)s was not found." msgstr "" "%(excluded_ranges)s IP aralıkları hariç %(parent_range)s IP aralığından " "%(size)s botyutunda aÄŸ bulunamadı." #, python-format msgid "Network type value '%s' not supported" msgstr "AÄŸ türü deÄŸeri '%s' desteklenmiyor" msgid "Network type value needed by the ML2 plugin" msgstr "ML2 eklentisi aÄŸ türü deÄŸerine ihtiyaç duyuyor" msgid "Network types supported by the agent (gre and/or vxlan)." msgstr "Ajan tarafından desteklenen aÄŸ türleri (gre ve/veya vxlan)." msgid "Neutron Service Type Management" msgstr "Neutron Servis Türü Yönetimi" msgid "Neutron core_plugin not configured!" msgstr "Neutron core_plugin yapılandırılmamış!" #, python-format msgid "No eligible l3 agent associated with external network %s found" msgstr "%s harici ağıyla iliÅŸkilendirilmiÅŸ uygun l3 ajanı bulunamadı" #, python-format msgid "No more IP addresses available for subnet %(subnet_id)s." msgstr "%(subnet_id)s alt ağı için kullanılabilir baÅŸka IP adresi yok." msgid "" "Number of DHCP agents scheduled to host a tenant network. If this number is " "greater than 1, the scheduler automatically assigns multiple DHCP agents for " "a given tenant network, providing high availability for DHCP service." msgstr "" "Bir kiracı ağı sunmak için zamanlanan DHCP ajanları sayısı. Bu sayı 1'den " "büyükse, zamanlayıcı verilen bir kiracı aÄŸa otomatik olarak birden çok DHCP " "ajanı atar, ve DHCP servisi için yüksek kullanılabilirlik saÄŸlar." msgid "Number of backlog requests to configure the metadata server socket with" msgstr "Metadata sunucu soketinin yapılandırılacağı birikmiÅŸ isteklerin sayısı" msgid "Number of backlog requests to configure the socket with" msgstr "Soketin birlikte yapılandırılacağı backlog isteklerinin sayısı" msgid "" "Number of floating IPs allowed per tenant. A negative value means unlimited." msgstr "" "Kiracı başına izin verilen deÄŸiÅŸken IP sayısı. Negatif deÄŸer sınırsız " "demektir." msgid "" "Number of networks allowed per tenant. A negative value means unlimited." msgstr "" "Kiracı başına izin verilen aÄŸ sayısı. Negatif deÄŸer sınırsız anlamına gelir." msgid "Number of ports allowed per tenant. A negative value means unlimited." msgstr "" "Kiracı başına izin verilen baÄŸlantı noktası sayısı. Negatif deÄŸer sınırsız " "anlamına gelir." msgid "Number of routers allowed per tenant. A negative value means unlimited." msgstr "" "Kiracı başına izin verilen yönlendirici sayısı. Negatif deÄŸer sınırsız " "anlamına gelir." msgid "" "Number of seconds between sending events to nova if there are any events to " "send." msgstr "" "Gönderilecek olay varsa olayların nova'ya gönderilmesi arasında beklenecek " "saniye sayısı." msgid "Number of seconds to keep retrying to listen" msgstr "Dinlemeye devam etmek için saniye sayısı" msgid "" "Number of security groups allowed per tenant. A negative value means " "unlimited." msgstr "" "Kiracı başına izin verilen güvenlik grubu sayısı. Negatif deÄŸer sınırsız " "anlamına gelir." msgid "" "Number of security rules allowed per tenant. A negative value means " "unlimited." msgstr "" "Kiracı başına izin verilen güvenlik kuralı sayısı. Negatif bir deÄŸer " "sınırsız demektir." msgid "" "Number of separate API worker processes for service. If not specified, the " "default is equal to the number of CPUs available for best performance." msgstr "" "Servis için ayrı API işçi süreçlerinin sayısı. Belirtilmezse, varsayılan " "olarak en iyi performans için CPU sayısına eÅŸit deÄŸerdir." msgid "" "Number of separate worker processes for metadata server (defaults to half of " "the number of CPUs)" msgstr "" "Metadata sunucu için ayrı işçi süreçleri sayısı (CPU sayısının yarısı " "varsayılır)" msgid "Number of subnets allowed per tenant, A negative value means unlimited." msgstr "" "Kiracı başına izin verilen alt aÄŸ sayısı, negatif deÄŸer sınırsız anlamına " "gelir." msgid "OK" msgstr "Tamam" msgid "Only admin can view or configure quota" msgstr "Yalnızca yönetici kotaları görüntüleyebilir ya da yapılandırabilir" msgid "Only admin is authorized to access quotas for another tenant" msgstr "Yalnızca yönetici baÅŸka bir kiracı için kotalara eriÅŸebilir" msgid "Only allowed to update rules for one security profile at a time" msgstr "" "Tek seferde bir güvenlik profili için kuralların güncellenmesine izin verilir" msgid "Only remote_ip_prefix or remote_group_id may be provided." msgstr "Yalnızca remote_ip_prefix veya remote_group_id saÄŸlanabilir." msgid "OpenFlow interface to use." msgstr "Kullanılacak OpenFlow arayüzü." #, python-format msgid "" "Operation %(op)s is not supported for device_owner %(device_owner)s on port " "%(port_id)s." msgstr "" "İşlem %(op)s %(port_id)s baÄŸlantı noktası üzerinde %(device_owner)s " "device_owner için desteklenmiyor." msgid "Owner type of the device: network/compute" msgstr "Aygıt sahip türü: aÄŸ/hesap" msgid "POST requests are not supported on this resource." msgstr "POST istekleri bu kaynakta desteklenmiyor." #, python-format msgid "Parsing bridge_mappings failed: %s." msgstr "bridge_mappins ayrıştırma baÅŸarısız: %s." msgid "Path to PID file for this process" msgstr "Bu sürecin PID dosyasının yolu" msgid "Path to the router directory" msgstr "Yönlendirici dizininin yolu" msgid "Peer patch port in integration bridge for tunnel bridge." msgstr "Tünel köprüsü için tümleÅŸtirme köprüsündeki eÅŸ yama baÄŸlantı noktası." msgid "Peer patch port in tunnel bridge for integration bridge." msgstr "TümleÅŸtirme köprüsü için tünel köprüsündeki eÅŸ yama baÄŸlantı noktası." msgid "Ping timeout" msgstr "Ping zaman aşımı" msgid "Plugin does not support updating provider attributes" msgstr "Eklenti saÄŸlayıcı özniteliklerini güncellemeyi desteklemiyor" #, python-format msgid "Port %(id)s does not have fixed ip %(address)s" msgstr "%(id)s baÄŸlantı noktası %(address)s sabit ip'sine sahip deÄŸil" msgid "Private key of client certificate." msgstr "İstemci sertifikasının özel anahtarı." #, python-format msgid "Probe %s deleted" msgstr "Deneme %s silindi" #, python-format msgid "Probe created : %s " msgstr "Deneme oluÅŸturuldu: %s " msgid "Process is already started" msgstr "Süreç zaten baÅŸlamış" msgid "Process is not running." msgstr "Süreç çalışmıyor." msgid "Protocol to access nova metadata, http or https" msgstr "Nova metadata'ya eriÅŸmek için iletiÅŸim kuralı, http veya https" msgid "" "Range of seconds to randomly delay when starting the periodic task scheduler " "to reduce stampeding. (Disable by setting to 0)" msgstr "" "Devresel görev zamanlayıcıyı baÅŸlatırken izdiham yaratmayı engellemek için " "beklenecek rastgele saniye aralığı. (0 olarak ayarlayıp kapatabilirsiniz)" msgid "Remote metadata server experienced an internal server error." msgstr "Uzak metadata sunucu dahil sunucu hatası yaÅŸadı." msgid "" "Representing the resource type whose load is being reported by the agent. " "This can be \"networks\", \"subnets\" or \"ports\". When specified (Default " "is networks), the server will extract particular load sent as part of its " "agent configuration object from the agent report state, which is the number " "of resources being consumed, at every report_interval.dhcp_load_type can be " "used in combination with network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler When the network_scheduler_driver is " "WeightScheduler, dhcp_load_type can be configured to represent the choice " "for the resource being balanced. Example: dhcp_load_type=networks" msgstr "" "Ajan tarafından yükü rapor edilen kaynak türünü temsil eder. Bu \"aÄŸlar\", " "\"alt aÄŸlar\", veya \"baÄŸlantı noktaları\" olabilir. BelirtildiÄŸinde " "(varsayılanı aÄŸlardır), sunucu ajan yapılandırma nesnesinin parçası olarak " "gönderilen belirli yükü ajan rapor durumundan çıkartır, ki bu her " "report_interval'da tüketilen kaynak sayısıdır. dhcp_load_type " "network_scheduler_driver WeightScheduler olduÄŸunda network_scheduler_driver " "= neutron.scheduler.WeightScheduler ile birlikte kullanılabilir, " "dhcp_load_type dengelenen kaynak için seçimi temsil edecek ÅŸekilde " "yapılandırılabilir. ÖrneÄŸin: dhcp_load_type=networks" msgid "Request Failed: internal server error while processing your request." msgstr "İstek BaÅŸarısız: isteÄŸiniz iÅŸlenirken dahili sunucu hatası oluÅŸtu." #, python-format msgid "" "Resource '%(resource_id)s' is already associated with provider " "'%(provider)s' for service type '%(service_type)s'" msgstr "" "'%(resource_id)s' kaynağı '%(service_type)s' servis türü için zaten " "'%(provider)s' saÄŸlayıcısıyla iliÅŸkilendirilmiÅŸ" msgid "Resource body required" msgstr "Kaynak gövdesi gerekiyor" msgid "Resource not found." msgstr "Kaynak bulunamadı." msgid "Resources required" msgstr "Kaynaklar gerekiyor" msgid "Root permissions are required to drop privileges." msgstr "İzinlerin düşürülmesi için Root izinleri gerekli." #, python-format msgid "Router already has a port on subnet %s" msgstr "Yönlendirici zaten %s alt ağında bir baÄŸlantı noktasına sahip" msgid "" "Seconds between nodes reporting state to server; should be less than " "agent_down_time, best if it is half or less than agent_down_time." msgstr "" "Düğümlerin sunucuya durum raporu yapması arasında geçen saniye; " "agent_down_time'dan az olmalı, en iyisi agent_down_time'ın yarısı ya da daha " "azı olmasıdır." msgid "" "Seconds to regard the agent is down; should be at least twice " "report_interval, to be sure the agent is down for good." msgstr "" "Ajanın çalışmıyor olduÄŸuna karar vermek için geçmesi gereken saniye; ajanın " "gerçekten kapalı olduÄŸundan emin olmak için report_interval deÄŸerinin en az " "iki katı olmalı." #, python-format msgid "Security Group %(id)s %(reason)s." msgstr "Güvenlik Grubu %(id)s %(reason)s." #, python-format msgid "Security Group Rule %(id)s %(reason)s." msgstr "Güvenlik Grubu Kuralı %(id)s %(reason)s." #, python-format msgid "Security group %(id)s does not exist" msgstr "%(id)s güvenlik grubu mevcut deÄŸil" #, python-format msgid "Security group rule %(id)s does not exist" msgstr "Güvenlik grubu kuralı %(id)s mevcut deÄŸil" #, python-format msgid "" "Security group rule protocol %(protocol)s not supported. Only protocol " "values %(values)s and integer representations [0 to 255] are supported." msgstr "" "Güvenlik grubu kuralı iletiÅŸim kuralı %(protocol)s desteklenmiyor. Yalnızca " "iletiÅŸim kuralı deÄŸerleri %(values)s ve tam sayı temsilleri [0 dan 255 e] " "destekleniyor." msgid "Segments and provider values cannot both be set." msgstr "Dilimler ve saÄŸlayıcı deÄŸerleri aynı anda ayarlanamaz." msgid "" "Send notification to nova when port data (fixed_ips/floatingip) changes so " "nova can update its cache." msgstr "" "BaÄŸlantı noktası verisi (sabit_ipler/deÄŸiÅŸkenip) deÄŸiÅŸtiÄŸinde nova'ya " "bildirim gönder ki nova zulasını güncelleyebilsin." msgid "Send notification to nova when port status changes" msgstr "BaÄŸlantı noktası durumu deÄŸiÅŸtiÄŸinde nova'ya bildirim gönder" #, python-format msgid "" "Service provider '%(provider)s' could not be found for service type " "%(service_type)s" msgstr "" "%(service_type)s servis türü için '%(provider)s' servis saÄŸlayıcı bulunamadı" #, python-format msgid "Service type %(service_type)s does not have a default service provider" msgstr "%(service_type)s servis türü varsayılan servis saÄŸlayıcıya sahip deÄŸil" msgid "" "Set new timeout in seconds for new rpc calls after agent receives SIGTERM. " "If value is set to 0, rpc timeout won't be changed" msgstr "" "Ajan SIGTERM aldıktan sonra yeni rpc çaÄŸrıları için saniye olarak yeni zaman " "aşımı ayarla. DeÄŸer 0 olarak ayarlanırsa, rpc zaman aşımı deÄŸiÅŸtirilmeyecek" msgid "" "Set or un-set the don't fragment (DF) bit on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "Dışa giden IP paketi taşıyan GRE/VXLAN tünelinde bölümlenme yapma (DF) " "bitini ayarla ya da ayarlama." msgid "Shared address scope can't be unshared" msgstr "Paylaşılan adres kapsamının paylaÅŸtırılması durdurulamaz" msgid "String prefix used to match IPset names." msgstr "IPset isimleriyle eÅŸleÅŸtirme için kullanılan karakter dizisi önekleri." msgid "Subnet for router interface must have a gateway IP" msgstr "Yönlendirici arayüzü için alt aÄŸ bir geçit IP'ye sahip olmalı" msgid "Subnet pool has existing allocations" msgstr "Alt aÄŸ havuzunun mevcut ayırmaları var" msgid "Subnet used for the l3 HA admin network." msgstr "L3 HA yönetici ağı için kullanılan alt aÄŸ." msgid "" "System-wide flag to determine the type of router that tenants can create. " "Only admin can override." msgstr "" "Kiracıların oluÅŸturabileceÄŸi yönlendirici türünü belirlemek için sistem " "genelinde bayrak. Yalnızca yönetici üzerine yazabilir." msgid "TCP Port used by Neutron metadata namespace proxy." msgstr "" "Neutron metadata isim uzayı vekili tarafından kullanılan TCP baÄŸlantı " "noktası." msgid "TCP Port used by Nova metadata server." msgstr "Nova metadata sunucusu tarafından kullanılan TCP BaÄŸlantı noktası." msgid "TTL for vxlan interface protocol packets." msgstr "Vxlan arayüz iletiÅŸim kuralı paketleri için TTL." #, python-format msgid "Tenant %(tenant_id)s not allowed to create %(resource)s on this network" msgstr "" "Kiracı %(tenant_id)s'in bu aÄŸda %(resource)s oluÅŸturmasına izin verilmiyor" msgid "Tenant network creation is not enabled." msgstr "Kiracı aÄŸ oluÅŸturma etkin deÄŸil." msgid "" "The 'gateway_external_network_id' option must be configured for this agent " "as Neutron has more than one external network." msgstr "" "Neutron birden fazla harici aÄŸa sahip olduÄŸundan " "'gateway_external_network_id' seçeneÄŸi bu ajan için yapılandırılmalıdır." msgid "The UDP port to use for VXLAN tunnels." msgstr "VXLAN tünelleri için kullanılacak UDP baÄŸlantı noktası." #, python-format msgid "" "The address allocation request could not be satisfied because: %(reason)s" msgstr "Adres ayırma isteÄŸi saÄŸlanamadı çünkü: %(reason)s" msgid "The advertisement interval in seconds" msgstr "Saniye cinsinden duyuru aralığı" #, python-format msgid "The allocation pool %(pool)s is not valid." msgstr "Ayırma havuzu %(pool)s geçerli deÄŸil." #, python-format msgid "" "The allocation pool %(pool)s spans beyond the subnet cidr %(subnet_cidr)s." msgstr "" "Ayırma havuzu %(pool)s %(subnet_cidr)s alt aÄŸ cidr'inin ötesine uzanıyor." msgid "The core plugin Neutron will use" msgstr "Neutron'un kullanacağı çekirdek eklenti" msgid "The driver used to manage the DHCP server." msgstr "DHCP sunucusunu yönetmek için kullanılan sürücü." msgid "The driver used to manage the virtual interface." msgstr "Sanal arayüzü yönetmek için kullanılan sürücü." #, python-format msgid "" "The following device_id %(device_id)s is not owned by your tenant or matches " "another tenants router." msgstr "" "device_id %(device_id)s sizin kiracınıza ait deÄŸil veya baÅŸka bir kiracının " "yönlendiricisiyle eÅŸleÅŸiyor." msgid "The interface for interacting with the OVSDB" msgstr "OVSDB ile etkileÅŸim için arayüz" msgid "" "The maximum number of items returned in a single response, value was " "'infinite' or negative integer means no limit" msgstr "" "Tek bir yanıtta döndürülecek azami öğe sayısı, 'infinite' deÄŸeri ya da " "negatif tam sayı sınır yok demektir" #, python-format msgid "" "The network %(network_id)s has been already hosted by the DHCP Agent " "%(agent_id)s." msgstr "AÄŸ %(network_id)s zaten %(agent_id)s DHCP Ajanı tarafından sunuluyor." #, python-format msgid "" "The network %(network_id)s is not hosted by the DHCP agent %(agent_id)s." msgstr "AÄŸ %(network_id)s %(agent_id)s DHCP ajanı tarafından sunulmuyor." msgid "" "The number of seconds the agent will wait between polling for local device " "changes." msgstr "" "Ajanın yerel aygıt deÄŸiÅŸiklikleri için sorgulama yapma aralığında " "bekleyeceÄŸi saniye sayısı." msgid "" "The number of seconds to wait before respawning the ovsdb monitor after " "losing communication with it." msgstr "" "İletiÅŸim koptuktan sonra ovsdb izleyiciyi yeniden baÅŸlatmak için beklenecek " "saniye sayısı." msgid "The number of sort_keys and sort_dirs must be same" msgstr "sort_keys ile sort_dirs sayıları aynı olmalı" #, python-format msgid "The port '%s' was deleted" msgstr "BaÄŸlantı noktası '%s' silinmiÅŸ" msgid "The port to bind to" msgstr "BaÄŸlanılacak baÄŸlantı noktası" #, python-format msgid "The requested content type %s is invalid." msgstr "İstenen içerik türü %s geçersiz." msgid "The resource could not be found." msgstr "Kaynak bulunamadı." #, python-format msgid "" "The router %(router_id)s has been already hosted by the L3 Agent " "%(agent_id)s." msgstr "" "%(router_id)s yönlendiricisi zaten %(agent_id)s L3 Ajanı tarafından " "sunuluyor." msgid "" "The server has either erred or is incapable of performing the requested " "operation." msgstr "" "Sunucu ya hata verdi ya da istenen iÅŸlemi yapabilecek yeterlilikte deÄŸil." msgid "The service plugins Neutron will use" msgstr "Neutron'un kullanacağı servis eklentileri" #, python-format msgid "The subnet request could not be satisfied because: %(reason)s" msgstr "Alt aÄŸ isteÄŸi saÄŸlanamadı çünkü: %(reason)s" msgid "The type of authentication to use" msgstr "Kullanılacak kimlik doÄŸrulama türü" msgid "" "True to delete all ports on all the OpenvSwitch bridges. False to delete " "ports created by Neutron on integration and external network bridges." msgstr "" "Tüm OpenvSwitch köprülerinde tüm baÄŸlantı noktalarını silmek için True. " "Neutron tarafından tümleÅŸtirme ve harici aÄŸ köprüleri üzerinde oluÅŸturulan " "baÄŸlantı noktalarını silmek için False." msgid "Tunnel IP value needed by the ML2 plugin" msgstr "Tünel IP deÄŸerine ML2 eklentisi tarafından ihtiyaç duyuluyor" msgid "Tunnel bridge to use." msgstr "Kullanılacak tünel köprüsü." msgid "URL to database" msgstr "Veri tabanı URL'si" #, python-format msgid "Unable to access %s" msgstr "%s'e eriÅŸilemiyor" #, python-format msgid "Unable to calculate %(address_type)s address because of:%(reason)s" msgstr "%(reason)s sebebiyle %(address_type)s adresi hesaplanamıyor" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of DNS " "nameservers exceeds the limit %(quota)s." msgstr "" "%(subnet_id)s için iÅŸlem tamamlanamadı. DNS isim sunucuları sayısı %(quota)s " "sayısını aşıyor." #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of host routes " "exceeds the limit %(quota)s." msgstr "" "%(subnet_id)s için iÅŸlem tamamlanamadı. İstemci rotaları sayısı %(quota)s " "sınırını aşıyor." #, python-format msgid "Unable to convert value in %s" msgstr "%s degeri dönüştürülemiyor" msgid "Unable to create the Agent Gateway Port" msgstr "Ajan Geçit BaÄŸlantı Noktası oluÅŸturulamıyor" msgid "Unable to create the SNAT Interface Port" msgstr "SNAT Arayüz BaÄŸlantı Noktası oluÅŸturulamıyor" #, python-format msgid "" "Unable to create the flat network. Physical network %(physical_network)s is " "in use." msgstr "Düz aÄŸ oluÅŸturulamıyor. Fiziksel aÄŸ %(physical_network)s kullanımda." msgid "" "Unable to create the network. No available network found in maximum allowed " "attempts." msgstr "" "AÄŸ oluÅŸturulamıyor. İzin verilen azami deneme içinde kullanılabilir aÄŸ " "bulunamadı." #, python-format msgid "Unable to determine mac address for %s" msgstr "%s içim mac adresi tanımlanamadı" #, python-format msgid "Unable to find '%s' in request body" msgstr "İstek gövdesinde '%s' bulunamadı" #, python-format msgid "Unable to find IP address %(ip_address)s on subnet %(subnet_id)s" msgstr "%(subnet_id)s alt ağında %(ip_address)s IP adresi bulunamıyor" #, python-format msgid "Unable to find resource name in %s" msgstr "%s içinde kaynak ismi bulunamadı" #, python-format msgid "Unable to generate unique mac on network %(net_id)s." msgstr "%(net_id)s ağı üzerinde benzersiz mac üretilemedi." #, python-format msgid "" "Unable to identify a target field from:%s. Match should be in the form " "%%()s" msgstr "" "%s den bir hedef alan tanımlanamadı. EÅŸleÅŸme %%()s biçiminde " "olmalı" #, python-format msgid "" "Unable to verify match:%(match)s as the parent resource: %(res)s was not " "found" msgstr "EÅŸleÅŸme:%(match)s doÄŸrulanamadı çünkü üst kaynak: %(res)s bulunamadı" #, python-format msgid "Unexpected response code: %s" msgstr "Beklenmedik yanıt kodu: %s" #, python-format msgid "Unexpected response: %s" msgstr "Beklenmeyen yanıt: %s" #, python-format msgid "Unknown address type %(address_type)s" msgstr "Bilinmeyen adres türü %(address_type)s" #, python-format msgid "Unknown attribute '%s'." msgstr "Bilinmeyen öznitelik '%s'." #, python-format msgid "Unknown chain: %r" msgstr "Tanınmayan zincir: %r" #, python-format msgid "Unknown quota resources %(unknown)s." msgstr "Bilinmeyen kota kaynakları %(unknown)s." msgid "Unmapped error" msgstr "Unmapped hata" msgid "Unrecognized action" msgstr "Tanınmayan eylem" #, python-format msgid "Unrecognized attribute(s) '%s'" msgstr "Tanınmayan öznitelik(ler) '%s'" msgid "Unsupported Content-Type" msgstr "Desteklenmeyen içerik türü" #, python-format msgid "Unsupported network type %(net_type)s." msgstr "Desteklenmeyen aÄŸ türü %(net_type)s." msgid "Unsupported request type" msgstr "Desteklenmeyen istek türü" msgid "Updating default security group not allowed." msgstr "Varsayılan güvenlik grubunu güncellemeye izin verilmiyor." msgid "" "Use ML2 l2population mechanism driver to learn remote MAC and IPs and " "improve tunnel scalability." msgstr "" "Uzak MAC ve IP'leri öğrenmek ve tünel ölçeklenebilirliÄŸini artırmak için ML2 " "l2population mekanizması sürücüsünü kullan." msgid "Use either --delta or relative revision, not both" msgstr "Ya --delta ya iliÅŸkili sürüm kullanın, ikisini birden deÄŸil" msgid "" "User (uid or name) running metadata proxy after its initialization (if " "empty: agent effective user)." msgstr "" "İlklendirilmesinden sonra metadata vekilini çalıştıran kullanıcı (uid veya " "isim) (boÅŸsa: ajan etkin kullanıcı)." msgid "User (uid or name) running this process after its initialization" msgstr "" "İlklendirilmesinden sonra bu süreci çalıştıran kullanıcı (uid veya isim)" msgid "VRRP authentication password" msgstr "VRRP kimlik doÄŸrulama parolası" msgid "VRRP authentication type" msgstr "VRRP kimlik doÄŸrulama türü" msgid "" "Where to store Neutron state files. This directory must be writable by the " "agent." msgstr "" "Neutron durum dosyalarının nerede depolanacağı. Bu dizin ajan tarafından " "yazılabilir olmalıdır." msgid "" "With IPv6, the network used for the external gateway does not need to have " "an associated subnet, since the automatically assigned link-local address " "(LLA) can be used. However, an IPv6 gateway address is needed for use as the " "next-hop for the default route. If no IPv6 gateway address is configured " "here, (and only then) the neutron router will be configured to get its " "default route from router advertisements (RAs) from the upstream router; in " "which case the upstream router must also be configured to send these RAs. " "The ipv6_gateway, when configured, should be the LLA of the interface on the " "upstream router. If a next-hop using a global unique address (GUA) is " "desired, it needs to be done via a subnet allocated to the network and not " "through this parameter. " msgstr "" "IPv6 ile, harici geçit için kullanılan aÄŸ iliÅŸkili bir alt aÄŸa sahip olmak " "zorunda deÄŸildir, çünkü otomatik olarak atanan baÄŸlantı-yerel adres (LLA) " "kullanılabilir. Ancak bir IPv6 geçit adresine varsayılan rota için sonraki-" "nokta olarak kullanılmak üzere ihtiyaç vardır. Burada bir IPv6 geçit adresi " "yapılandırılmazsa, (ve yalnızca bu durumda) neutron yönlendirici, varsayılan " "rotasını üst seviye yönlendirici duyurularından (RA) alacak ÅŸekilde " "yapılandırılır; ki bu durumda üst seviye yönlendirici bu RA'ları gönderecek " "ÅŸekilde yapılandırılmalıdır. ipv6_gateway, yapılandırıldığında, üst seviye " "yönlendirici üzerindeki arayüzün LLA'sı olmalıdır. EÄŸer genel benzersiz " "adres (GUA) kullanan bir sonraki-nokta isteniyorsa, bu aÄŸa ayrılmış bir alt " "aÄŸ vasıtasıyla yapılmalıdır, bu parametre ile deÄŸil. " msgid "You must implement __call__" msgstr "__call__ fonksiyonunu uygulamalısınız." msgid "" "You must provide a config file for bridge - either --config-file or " "env[NEUTRON_TEST_CONFIG_FILE]" msgstr "" "Köprü için bir yapılandırma dosyası saÄŸlamalısınız - ya --config-file ya da " "env[NEUTRON_TEST_CONFIG_FILE]" msgid "You must provide a revision or relative delta" msgstr "Bir sürüm ya da iliÅŸkili fark saÄŸlamalısınız" msgid "allocation_pools allowed only for specific subnet requests." msgstr "allocation_pools yalnızca belirli alt aÄŸ istekleri için izinli." msgid "binding:profile value too large" msgstr "baÄŸ:profil deÄŸeri çok büyük" msgid "cidr and prefixlen must not be supplied together" msgstr "cidr ve prefixlen birlikte verilmemelidir" #, python-format msgid "dhcp_agents_per_network must be >= 1. '%s' is invalid." msgstr "dhcp_agents_per_network >= 1 olmalı. '%s' geçersiz." msgid "fixed_ip_address cannot be specified without a port_id" msgstr "fixed_ip_addres port_id olmadan belirtilemez" #, python-format msgid "has device owner %s" msgstr "%s aygıt sahibine sahip" msgid "in use" msgstr "kullanımda" #, python-format msgid "ip command failed on device %(dev_name)s: %(reason)s" msgstr "ip komutu %(dev_name)s aygıtı üzerinde baÅŸarısız: %(reason)s" #, python-format msgid "ip link capability %(capability)s is not supported" msgstr "ip baÄŸlantı yeteneÄŸi %(capability)s desteklenmiyor" #, python-format msgid "ip link command is not supported: %(reason)s" msgstr "ip baÄŸlantı komutu desteklenmiyor: %(reason)s" msgid "ip_version must be specified in the absence of cidr and subnetpool_id" msgstr "subnetpool_id ve cidr olmadığında ip_version belirtilmelidir" msgid "ipv6_address_mode is not valid when ip_version is 4" msgstr "ip_version 4 olduÄŸunda ipv6_address_mode geçerli deÄŸildir" msgid "ipv6_ra_mode is not valid when ip_version is 4" msgstr "ip_version 4 olduÄŸunda ipv6_ra_mode geçerli deÄŸildir" #, python-format msgid "" "ipv6_ra_mode set to '%(ra_mode)s' with ipv6_address_mode set to " "'%(addr_mode)s' is not valid. If both attributes are set, they must be the " "same value" msgstr "" "ipv6_ra_mode kipi '%(ra_mode)s' olarak ipv6_address_mode '%(addr_mode)s' " "olarak ayarlanması geçersizdir. İki öznitelik de ayarlanıyorsa, aynı deÄŸerde " "olmalılar" msgid "mac address update" msgstr "mac adres güncellemesi" msgid "network_type required" msgstr "network_type gerekli" #, python-format msgid "network_type value '%s' not supported" msgstr "network_type deÄŸeri '%s' desteklenmiyor" msgid "new subnet" msgstr "yeni alt aÄŸ" #, python-format msgid "physical_network '%s' unknown for flat provider network" msgstr "physical_network '%s' düz saÄŸlayıcı ağı için bilinmiyor" msgid "physical_network required for flat provider network" msgstr "Düz saÄŸlayıcı ağı için physical_network gerekir" #, python-format msgid "provider:physical_network specified for %s network" msgstr "saÄŸlayıcı:physical_network %s ağı için belirtildi" msgid "respawn_interval must be >= 0 if provided." msgstr "eÄŸer saÄŸlanmışsa respawn_interval >= 0 olmalı." #, python-format msgid "segmentation_id out of range (%(min)s through %(max)s)" msgstr "segmentation_id aralık dışında (%(min)s %(max)s arasında)" msgid "segmentation_id requires physical_network for VLAN provider network" msgstr "" "segmentation_id VLAN saÄŸlayıcı ağı için physical_network'e ihtiyaç duyar" msgid "the nexthop is not connected with router" msgstr "Sonraki nokta yönlendiriciyle baÄŸlı deÄŸil" msgid "the nexthop is used by router" msgstr "sonraki nokta yönlendirici tarafından kullanılıyor" neutron-12.1.1/neutron/locale/it/0000775000175000017500000000000013553660156016667 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/it/LC_MESSAGES/0000775000175000017500000000000013553660156020454 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/locale/it/LC_MESSAGES/neutron.po0000664000175000017500000033457213553660047022523 0ustar zuulzuul00000000000000# Translations template for neutron. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the neutron project. # # Translators: # Andreas Jaeger , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: neutron VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-03-14 04:19+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-04-12 05:53+0000\n" "Last-Translator: Copied by Zanata \n" "Language: it\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 4.3.3\n" "Language-Team: Italian\n" #, python-format msgid "" "\n" "Command: %(cmd)s\n" "Exit code: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" msgstr "" "\n" "Comando: %(cmd)s\n" "Codice di uscita: %(code)s\n" "Stdin: %(stdin)s\n" "Stdout: %(stdout)s\n" "Stderr: %(stderr)s" #, python-format msgid "" "%(branch)s HEAD file does not match migration timeline head, expected: " "%(head)s" msgstr "" "Il file HEAD %(branch)s non corrisponde all'head di durata della migrazione, " "previsto: %(head)s" #, python-format msgid "%(id)s is not a valid %(type)s identifier" msgstr "%(id)s non è un identificativo %(type)s valido" #, python-format msgid "" "%(invalid_dirs)s is invalid value for sort_dirs, valid value is '%(asc)s' " "and '%(desc)s'" msgstr "" "%(invalid_dirs)s non è un valore valido per sort_dirs, il valore valido è " "'%(asc)s' e '%(desc)s'" #, python-format msgid "%(key)s prohibited for %(tunnel)s provider network" msgstr "%(key)s non consentito per la rete del provider %(tunnel)s" #, python-format msgid "%(name)s '%(addr)s' does not match the ip_version '%(ip_version)s'" msgstr "%(name)s '%(addr)s' non corrisponde alla ip_version '%(ip_version)s'" #, python-format msgid "%s cannot be called while in offline mode" msgstr "%s Impossibile chiamare durante la modalità offline" #, python-format msgid "%s is invalid attribute for sort_keys" msgstr "%s è un attributo non valido per sort_keys" #, python-format msgid "%s is not a valid VLAN tag" msgstr "%s non un tag VLAN valido" #, python-format msgid "%s must implement get_port_from_device or get_ports_from_devices." msgstr "%s deve implementare get_port_from_device o get_ports_from_devices." #, python-format msgid "%s prohibited for VLAN provider network" msgstr "%s vietato per la rete del provider VLAN" #, python-format msgid "%s prohibited for flat provider network" msgstr "%s vietato per rete flat del provider" #, python-format msgid "%s prohibited for local provider network" msgstr "%s è vietato per la rete del provider locale" #, python-format msgid "'%s' is not a valid RBAC object type" msgstr "'%s' non è un tipo di oggetto valido" #, python-format msgid "'%s' is not supported for filtering" msgstr "'%s' non è supportata per il filtro" #, python-format msgid "'module' object has no attribute '%s'" msgstr "L'oggetto 'module' non ha un attributo '%s'" msgid "'port_max' is smaller than 'port_min'" msgstr "'port_max' è più piccolo di 'port_min'" msgid "0 is not allowed as CIDR prefix length" msgstr "0 non è consentito come lunghezza del prefisso CIDR" msgid "A cidr must be specified in the absence of a subnet pool" msgstr "È necessario specificare un cidr in assenza di un pool di sottoreti" msgid "" "A decimal value as Vendor's Registered Private Enterprise Number as required " "by RFC3315 DUID-EN." msgstr "" "Un valore decimale come il numero dell'azienda privata registrato dal " "fornitore come richiesto da RFC3315 DUID-EN." #, python-format msgid "A default external network already exists: %(net_id)s." msgstr "Una rete esterna predefinita esiste già: %(net_id)s." msgid "" "A default subnetpool for this IP family has already been set. Only one " "default may exist per IP family" msgstr "" "Un pool di sottorete predefinito per questa famiglia IP è già stato " "impostato. Solo un valore predefinito può esistere per famiglia IP" msgid "A metering driver must be specified" msgstr "Specificare un driver di misurazione" msgid "API for retrieving service providers for Neutron advanced services" msgstr "" "API per il richiamo dei provider del servizio per i servizi Neutron avanzati" msgid "Aborting periodic_sync_routers_task due to an error." msgstr "Interruzione di periodic_sync_routers_task a causa di un errore." msgid "Access to this resource was denied." msgstr "L'accesso a questa risorsa è stato negato." msgid "Action to be executed when a child process dies" msgstr "Azione da eseguire quando termina un processo child" msgid "" "Add comments to iptables rules. Set to false to disallow the addition of " "comments to generated iptables rules that describe each rule's purpose. " "System must support the iptables comments module for addition of comments." msgstr "" "Aggiungere commenti alle regole iptables. Impostare su false per non " "consentire l'aggiunta di commenti alle regole iptables generate che " "descrivono lo scopo di ciascun ruolo. Il sistema deve supportare il modulo " "di commenti iptables per l'aggiunta di commenti." msgid "Address not present on interface" msgstr "Indirizzo non presente sull'interfaccia" msgid "" "Address to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "Indirizzo di ascolto per le connessioni OpenFlow. Utilizzato solo per driver " "'native'." msgid "Adds test attributes to core resources." msgstr "Aggiunge gli attributi di test alle risorse principali." #, python-format msgid "Agent %(id)s is not a L3 Agent or has been disabled" msgstr "L'agent %(id)s non è un agent L3 oppure è stato disabilitato" #, python-format msgid "Agent %(id)s is not a valid DHCP Agent or has been disabled" msgstr "Agent %(id)s non è un agent DHCP valido oppure è stato disabilitato" msgid "" "Agent starts with admin_state_up=False when enable_new_agents=False. In the " "case, user's resources will not be scheduled automatically to the agent " "until admin changes admin_state_up to True." msgstr "" "L'agent inizia con admin_state_up=False quando enable_new_agents=False. In " "tal caso, le risorse dell'utente non saranno pianificate automaticamente per " "l'agent finché l'admin non modifica admin_state_up in True." #, python-format msgid "Agent updated: %(payload)s" msgstr "Agent aggiornato: %(payload)s" msgid "Allow auto scheduling networks to DHCP agent." msgstr "Consenti pianificazione automatica delle reti nell'agent DHCP." msgid "Allow auto scheduling of routers to L3 agent." msgstr "Consenti pianificazione automatica dei router nell'agent L3." msgid "" "Allow overlapping IP support in Neutron. Attention: the following parameter " "MUST be set to False if Neutron is being used in conjunction with Nova " "security groups." msgstr "" "Consentire la sovrapposizione del supporto IP in Neutron. Attenzione: il " "seguente parametro DEVE essere impostato su False se Neutron viene " "utilizzato insieme ai gruppi di sicurezza Nova." msgid "Allow running metadata proxy." msgstr "Consenti l'esecuzione del proxy di metadati." msgid "Allow sending resource operation notification to DHCP agent" msgstr "Consenti notifica operazione di invio risorse all'agent DHCP" msgid "Allow the creation of PTR records" msgstr "Consenti la creazione di record PTR" msgid "Allow the usage of the bulk API" msgstr "Consenti l'utilizzo dell'API bulk" msgid "Allow to perform insecure SSL (https) requests to nova metadata" msgstr "" "Consentire l'esecuzione di richieste SSL (https) non protette sui metadati " "nova" msgid "" "Allows for serving metadata requests coming from a dedicated metadata access " "network whose CIDR is 169.254.169.254/16 (or larger prefix), and is " "connected to a Neutron router from which the VMs send metadata:1 request. In " "this case DHCP Option 121 will not be injected in VMs, as they will be able " "to reach 169.254.169.254 through a router. This option requires " "enable_isolated_metadata = True." msgstr "" "Consente di servire le richieste di metadati da una rete di accesso ai " "metadati dedicata il cui CIDR è 169.254.169.254/16 (o un prefisso più " "esteso) ed è connessa a un router Neutron da cui le VM inviano i metadati:1 " "richiesta. In questo caso, l'opzione DHCP 121 non sarà inserita nelle VM in " "quanto non saranno in grado di raggiungere 169.254.169.254 tramite un " "router. Questa opzione richiede enable_isolated_metadata = True." msgid "An RBAC policy already exists with those values." msgstr "Una politica RBAC esiste già con questi valori." msgid "An identifier must be specified when updating a subnet" msgstr "" "Un identificativo deve essere specificato durante l'aggiornamento di una " "sottorete" msgid "An interface driver must be specified" msgstr "È necessario specificare un driver di interfaccia" msgid "" "An ordered list of extension driver entrypoints to be loaded from the " "neutron.ml2.extension_drivers namespace. For example: extension_drivers = " "port_security,qos" msgstr "" "Un elenco ordinato di punti di ingresso del driver di estensione da caricare " "dallo spazio dei nomi neutron.ml2.extension_drivers. Ad esempio: " "extension_drivers = port_security,qos" msgid "" "An ordered list of networking mechanism driver entrypoints to be loaded from " "the neutron.ml2.mechanism_drivers namespace." msgstr "" "Un elenco ordinato dei punti di ingresso del driver del meccanismo di rete " "da caricare dallo spazio dei nomi neutron.ml2.mechanism_drivers." msgid "An unknown error has occurred. Please try your request again." msgstr "Si è verificato un errore sconosciuto. Ritentare la richiesta." msgid "Async process didn't respawn" msgstr "Il processo async non ha eseguito la nuova generazione" msgid "Authorization URL for connecting to designate in admin context" msgstr "URL autorizzazione per la connessione da designare nel contesto admin" msgid "Automatically remove networks from offline DHCP agents." msgstr "Rimuove automaticamente le reti dagli agent DHCP offline." msgid "" "Automatically reschedule routers from offline L3 agents to online L3 agents." msgstr "" "Ripianifica automaticamente i router dagli agent L3 offline agli agent L3 " "online." msgid "Availability zone of this node" msgstr "Zona di disponibilità di questo nodo" msgid "Available commands" msgstr "Comandi disponibili" msgid "Backend does not support VLAN Transparency." msgstr "Il backend non supporta la trasparenza VLAN." #, python-format msgid "Base MAC: %s" msgstr "MAC base: %s" msgid "" "Base log dir for dnsmasq logging. The log contains DHCP and DNS log " "information and is useful for debugging issues with either DHCP or DNS. If " "this section is null, disable dnsmasq log." msgstr "" "Directory log di base per la registrazione dnsmasq. Il log contiene info di " "log DHCP e DNS ed è utile per il debug dei problemi con DHCP o DNS. Se " "questa sezione è null, disabilitare il log dnsmasq." msgid "Body contains invalid data" msgstr "Il corpo contiene dati non validi" msgid "Both network_id and router_id are None. One must be provided." msgstr "network_id e router_id non esistono. È necessario fornirne uno." #, python-format msgid "Bridge %(bridge)s does not exist." msgstr "Il bridge %(bridge)s non esiste." msgid "Bulk operation not supported" msgstr "Operazione massiccia non supportata" msgid "CIDR to monitor" msgstr "CIDR da monitorare" #, python-format msgid "Callback for %(resource_type)s not found" msgstr "Callback per %(resource_type)s non trovata" #, python-format msgid "Callback for %(resource_type)s returned wrong resource type" msgstr "La callback per %(resource_type)s ha restituito un tipo risorsa errato" #, python-format msgid "Cannot add floating IP to port %s that has no fixed IPv4 addresses" msgstr "" "Impossibile aggiungere l'IP mobile alla porta %s che non ha indirizzi IPv4 " "fissi" #, python-format msgid "Cannot add multiple callbacks for %(resource_type)s" msgstr "Impossibile aggiungere più callback per %(resource_type)s" #, python-format msgid "Cannot allocate IPv%(req_ver)s subnet from IPv%(pool_ver)s subnet pool" msgstr "" "Impossibile assegnare la sottorete IPv%(req_ver)s dal pool di sottoreti IPv" "%(pool_ver)s" msgid "Cannot allocate requested subnet from the available set of prefixes" msgstr "" "Impossibile assegnare la sottorete richiesta dall'insieme di prefissi " "disponibili" msgid "Cannot disable enable_dhcp with ipv6 attributes set" msgstr "Impossibile disabilitare enable_dhcp con gli attributi ipv6 impostati" #, python-format msgid "Cannot handle subnet of type %(subnet_type)s" msgstr "Impossibile gestire la sottorete di tipo %(subnet_type)s" msgid "Cannot have multiple IPv4 subnets on router port" msgstr "Impossibile avere più sottoreti IPv4 sulla porta del router" #, python-format msgid "" "Cannot have multiple router ports with the same network id if both contain " "IPv6 subnets. Existing port %(p)s has IPv6 subnet(s) and network id %(nid)s" msgstr "" "Impossibile avere più porte router con lo stesso ID di rete se entrambe " "contengono sottoreti IPv6. La porta esistente %(p)s ha sottoreti IPv6 e ID " "di rete %(nid)s" #, python-format msgid "" "Cannot host distributed router %(router_id)s on legacy L3 agent %(agent_id)s." msgstr "" "Impossibile ospitare il router distribuito %(router_id)s sull'agent legacy " "L3 %(agent_id)s." msgid "Cannot mix IPv4 and IPv6 prefixes in a subnet pool." msgstr "Impossibile combinare i prefissi IPv4 e IPv6 in un pool di sottorete." msgid "Cannot specify both subnet-id and port-id" msgstr "Impossibile specificare entrambi subnet_id e port_id" msgid "Cannot understand JSON" msgstr "Impossibile riconoscere JSON" #, python-format msgid "Cannot update read-only attribute %s" msgstr "Impossibile aggiornare l'attributo di sola lettura %s" msgid "Certificate Authority public key (CA cert) file for ssl" msgstr "File di chiave pubblica Certificate Authority (CA cert) per ssl" #, python-format msgid "" "Change would make usage less than 0 for the following resources: %(unders)s." msgstr "" "La modifica renderebbe l'utilizzo inferiore a 0 per le seguenti risorse: " "%(unders)s." msgid "Check ebtables installation" msgstr "Controlla installazione di ebtables" msgid "Check for ARP header match support" msgstr "Verifica il supporto di corrispondenza intestazione ARP" msgid "Check for ARP responder support" msgstr "Verifica il supporto responder ARP" msgid "Check for ICMPv6 header match support" msgstr "Verifica il supporto di corrispondenza intestazione ICMPv6" msgid "Check for OVS Geneve support" msgstr "Verifica il supporto OVS Geneve" msgid "Check for OVS vxlan support" msgstr "Verifica il supporto OVS vxlan" msgid "Check for VF management support" msgstr "Verifica il supporto di gestione VF management" msgid "Check for iproute2 vxlan support" msgstr "Verifica il supporto iproute2 vxlan" msgid "Check for nova notification support" msgstr "Verifica il supporto di notifica nova" msgid "Check for patch port support" msgstr "Verifica il supporto porta patch" msgid "Check ip6tables installation" msgstr "Controlla installazione di ip6tables" msgid "Check ipset installation" msgstr "Controlla installazione di ipset" msgid "Check keepalived IPv6 support" msgstr "Controlla supporto IPv6 con keepalive" msgid "Check minimal dibbler version" msgstr "Controlla versione dibbler minima" msgid "Check minimal dnsmasq version" msgstr "Verifica versione dnsmasq minima" msgid "Check netns permission settings" msgstr "Verifica le impostazioni di autorizzazione netns" msgid "Check ovs conntrack support" msgstr "Verifica il supporto OVS conntrack" msgid "Check ovsdb native interface support" msgstr "Verifica supporto interfaccia nativa ovsdb" #, python-format msgid "" "Cidr %(subnet_cidr)s of subnet %(subnet_id)s overlaps with cidr %(cidr)s of " "subnet %(sub_id)s" msgstr "" "Cidr %(subnet_cidr)s della sottorete %(subnet_id)s si sovrappone con il cidr " "%(cidr)s della sottorete %(sub_id)s" msgid "Cleanup resources of a specific agent type only." msgstr "Ripulire solo le risorse di un tipo di agent specifico." msgid "Client certificate for nova metadata api server." msgstr "Certificato client per il server api dei metadati nova" msgid "" "Comma-separated list of : tuples, mapping " "network_device to the agent's node-specific list of virtual functions that " "should not be used for virtual networking. vfs_to_exclude is a semicolon-" "separated list of virtual functions to exclude from network_device. The " "network_device in the mapping should appear in the physical_device_mappings " "list." msgstr "" "Elenco di tuple : che associano " "network_device all'elenco specifico del nodo dell'agent delle funzioni " "virtuali che non devono essere utilizzate per la rete virtuale. " "vfs_to_exclude è un elenco separato da punto e virgola delle funzioni " "virtuali da escludere da network_device. Il network_device nell'associazione " "deve essere presente nell'elenco physical_device_mappings." msgid "" "Comma-separated list of : tuples mapping " "physical network names to the agent's node-specific physical network device " "interfaces of SR-IOV physical function to be used for VLAN networks. All " "physical networks listed in network_vlan_ranges on the server should have " "mappings to appropriate interfaces on each agent." msgstr "" "Elenco di tuple : separate di virgole che " "associano i nomi della rete fisica alle interfacce del dispositivo di rete " "fisico specifico del nodo dell'agent della funzione fisica SR-IOV da " "utilizzare per le reti VLAN. Tutte le reti fisiche elencate in " "network_vlan_ranges sul server devono avere associazioni alle interfacce " "appropriate su ogni agent." msgid "" "Comma-separated list of : tuples " "mapping physical network names to the agent's node-specific physical network " "interfaces to be used for flat and VLAN networks. All physical networks " "listed in network_vlan_ranges on the server should have mappings to " "appropriate interfaces on each agent." msgstr "" "Elenco di tuple : che associano i nomi " "della rete fisica all'interfaccia di rete fisica specifica del nodo " "dell'agent da utilizzare per le reti VLAN. Tutte le reti fisiche elencate in " "network_vlan_ranges sul server devono avere associazioni alle interfacce " "appropriate su ogni agent" msgid "" "Comma-separated list of : tuples enumerating ranges of GRE " "tunnel IDs that are available for tenant network allocation" msgstr "" "Elenco separato da virgole di intervalli di enumerazione tuple :" " ID tunnel GRE disponibili per l'assegnazione di rete tenant" msgid "" "Comma-separated list of : tuples enumerating ranges of " "Geneve VNI IDs that are available for tenant network allocation" msgstr "" "Elenco separato da virgole di intervalli di enumerazione tuple :" " di ID VNI Geneve disponibili per l'assegnazione della rete titolare" msgid "" "Comma-separated list of : tuples enumerating ranges of " "VXLAN VNI IDs that are available for tenant network allocation" msgstr "" "Elenco separato da virgole di intervalli di enumerazione tuple :" " di VXLAN VNI ID disponibili per l'assegnazione della rete tenant" msgid "" "Comma-separated list of the DNS servers which will be used as forwarders." msgstr "" "Elenco separato da virgole dei server DNS che verranno utilizzati come " "server di inoltro." msgid "Command to execute" msgstr "Comando da eseguire" msgid "Config file for interface driver (You may also use l3_agent.ini)" msgstr "" "File di configurazione per il driver di interfaccia (È possibile utilizzare " "anche l3_agent.ini)" #, python-format msgid "Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s" msgstr "Valore ethertype %(ethertype)s in conflitto per CIDR %(cidr)s" msgid "" "Controls whether the neutron security group API is enabled in the server. It " "should be false when using no security groups or using the nova security " "group API." msgstr "" "Controlla se l'API del gruppo di sicurezza neutron è abilitata sul server. " "Dovrebbe essere impostata su false quando non si utilizzano gruppi di " "sicurezza o si utilizza l'API del gruppo di sicurezza nova." #, python-format msgid "Could not bind to %(host)s:%(port)s after trying for %(time)d seconds" msgstr "" "Impossibile effettuare il bind a %(host)s:%(port)s dopo aver provato per " "%(time)d secondi" msgid "Could not deserialize data" msgstr "Impossibile deserializzare i dati" #, python-format msgid "" "Current gateway ip %(ip_address)s already in use by port %(port_id)s. Unable " "to update." msgstr "" "L'ip gateway corrente %(ip_address)s è già in uso dalla porta %(port_id)s. " "Impossibile effettuare l'aggiornamento." msgid "" "DHCP lease duration (in seconds). Use -1 to tell dnsmasq to use infinite " "lease times." msgstr "" "Durata rilascio DHCP (in secondi). Utilizzare -1 per informare dnsmasq di " "utilizzare infinite volte il rilascio." msgid "" "DVR deployments for VXLAN/GRE/Geneve underlays require L2-pop to be enabled, " "in both the Agent and Server side." msgstr "" "Le distribuzioni DVR per VXLAN/GRE/Geneve sottostanti richiedono che sia " "abilitato L2-pop, sia sul lato agent che server." msgid "" "Database engine for which script will be generated when using offline " "migration." msgstr "" "Motore di database per cui verrà generato lo script quando si utilizza la " "migrazione offline." msgid "Default external networks must be shared to everyone." msgstr "Le reti esterne predefinite devono essere condivise con chiunque." msgid "" "Default network type for external networks when no provider attributes are " "specified. By default it is None, which means that if provider attributes " "are not specified while creating external networks then they will have the " "same type as tenant networks. Allowed values for external_network_type " "config option depend on the network type values configured in type_drivers " "config option." msgstr "" "Il tipo di rete predefinito per le reti esterne quando non si specificano " "attributi provider. Per impostazione predefinita è Nessuno, che indica che " "se gli attributi provider non sono stati specificati durante la creazione di " "reti esterne, avranno lo stesso tipo delle reti titolari. I valori " "consentiti per l'opzione config external_network_type dipendono dai valori " "del tipo di rete configurati nell'opzione config type_drivers." msgid "" "Default number of RBAC entries allowed per tenant. A negative value means " "unlimited." msgstr "" "Numero predefinito di voci RBAC consentite per titolare. Un valore negativo " "indica un numero illimitato." msgid "" "Default number of resource allowed per tenant. A negative value means " "unlimited." msgstr "" "Numero predefinito di risorse consentite per tenant. Un valore negativo " "indica un numero illimitato." msgid "Default security group" msgstr "Gruppo di sicurezza predefinito" msgid "Default security group already exists." msgstr "Il gruppo di sicurezza predefinito già esiste." msgid "" "Default value of availability zone hints. The availability zone aware " "schedulers use this when the resources availability_zone_hints is empty. " "Multiple availability zones can be specified by a comma separated string. " "This value can be empty. In this case, even if availability_zone_hints for a " "resource is empty, availability zone is considered for high availability " "while scheduling the resource." msgstr "" "Il valore predefinito dei suggerimenti per la zona di disponibilità. Gli " "scheduler che riconoscono la zona di disponibilità utilizzano questo valore " "quando le risorse availability_zone_hints sono vuote. Più zone di " "disponibilità possono essere specificate mediante una stringa separata da " "virgole. Questo valore non può essere vuoto. In questo caso, anche se " "availability_zone_hints per una risorsa è vuoto, la zona di disponibilità " "viene considerata per l'elevata disponibilità durante la pianificazione " "della risorsa." msgid "" "Define the default value of enable_snat if not provided in " "external_gateway_info." msgstr "" "Definire il valore predefinito di enable_snat se non fornito in " "external_gateway_info." msgid "" "Defines providers for advanced services using the format: :" ":[:default]" msgstr "" "Definisce i provider per i servizi avanzati utilizzando il formato: " "::[:default]" msgid "Delete the namespace by removing all devices." msgstr "Elimina lo spazio dei nomi rimuovendo tutti i dispositivi." #, python-format msgid "Deleting port %s" msgstr "Eliminazione della porta %s" #, python-format msgid "Deployment error: %(reason)s." msgstr "Errore di distribuzione: %(reason)s." msgid "Destroy IPsets even if there is an iptables reference." msgstr "Distruggere gli IPset anche se c'è un riferimento iptables." msgid "Destroy all IPsets." msgstr "Distruggere tutti gli IPset." #, python-format msgid "Device %(dev_name)s in mapping: %(mapping)s not unique" msgstr "Dispositivo %(dev_name)s nell'associazione: %(mapping)s non univoco" #, python-format msgid "Device name %(dev_name)s is missing from physical_device_mappings" msgstr "Il nome dispositivo %(dev_name)s manca da physical_device_mappings" msgid "Device not found" msgstr "Dispositivo non trovato" #, python-format msgid "" "Distributed Virtual Router Mac Address for host %(host)s does not exist." msgstr "" "L'indirizzo MAC del router virtuale distribuito per l'host %(host)s non " "esiste." msgid "Domain to use for building the hostnames" msgstr "Dominio da utilizzare per creare i nomi host" msgid "Downgrade no longer supported" msgstr "Riduzione non più supportata" #, python-format msgid "Driver %s is not unique across providers" msgstr "Il driver %s non è univoco tra i provider" msgid "Driver for external DNS integration." msgstr "Driver per l'integrazione DNS esterna." msgid "Driver for security groups firewall in the L2 agent" msgstr "Driver per il firewall dei gruppi di sicurezza nell'agent L2" msgid "Driver to use for scheduling network to DHCP agent" msgstr "Driver da utilizzare per la pianificazione della rete nell'agent DHCP" msgid "Driver to use for scheduling router to a default L3 agent" msgstr "" "Driver da utilizzare per la pianificazione del router nell'agent L3 " "predefinito" msgid "" "Driver used for ipv6 prefix delegation. This needs to be an entry point " "defined in the neutron.agent.linux.pd_drivers namespace. See setup.cfg for " "entry points included with the neutron source." msgstr "" "Il driver utilizzato per la delega prefisso ipv6. Deve essere un punto di " "immissione definito nello spazio dei nomi neutron.agent.linux.pd_drivers. " "Consultare setup.cfg per i punti di immissione inclusi con l'origine neutron." #, python-format msgid "" "Duplicate L3HARouterAgentPortBinding is created for router(s) %(router)s. " "Database cannot be upgraded. Please, remove all duplicates before upgrading " "the database." msgstr "" "L3HARouterAgentPortBinding duplicato viene creato per i router %(router)s. " "Il database non può essere aggiornato. Rimuovere tutti i duplicati prima di " "aggiornare il database." msgid "Duplicate Security Group Rule in POST." msgstr "Regola del gruppo di sicurezza duplicata in POST." msgid "Duplicate address detected" msgstr "Rilevato indirizzo duplicato" msgid "Duplicate segment entry in request." msgstr "Voce del segmento duplicata nella richiesta." #, python-format msgid "ERROR: %s" msgstr "ERRORE: %s" msgid "" "ERROR: Unable to find configuration file via the default search paths (~/." "neutron/, ~/, /etc/neutron/, /etc/) and the '--config-file' option!" msgstr "" "ERRORE: Impossibile trovare il file di configurazione utilizzando i percorsi " "di ricerca predefiniti (~/.neutron/, ~/, /etc/neutron/, /etc/) e l'opzione " "'--config-file'!" msgid "" "Either one of parameter network_id or router_id must be passed to _get_ports " "method." msgstr "" "Uno dei parametri network_id o router_id deve essere passato al metodo " "_get_ports." msgid "Either subnet_id or port_id must be specified" msgstr "È necessario specificare subnet_id o port_id" msgid "Empty physical network name." msgstr "Nome rete fisica vuoto." msgid "Empty subnet pool prefix list." msgstr "Elenco prefisso pool di sottorete vuoto." msgid "Enable HA mode for virtual routers." msgstr "Abilitare la modalità HA per i router virtuali." msgid "Enable SSL on the API server" msgstr "Abilitazione di SSL sul server API" msgid "" "Enable VXLAN on the agent. Can be enabled when agent is managed by ml2 " "plugin using linuxbridge mechanism driver" msgstr "" "Abilitare VXLAN sull'agent. Può essere abilitata quando l'agent è gestito " "dal plugin ml2 utilizzando il driver del meccanismo linuxbridge" msgid "" "Enable local ARP responder if it is supported. Requires OVS 2.1 and ML2 " "l2population driver. Allows the switch (when supporting an overlay) to " "respond to an ARP request locally without performing a costly ARP broadcast " "into the overlay." msgstr "" "Abilitare il responder ARP locale se è supportato. Richiede il driver OVS " "2.1 e ML2 l2population. Consentire allo switch (quando supporta una " "sovrapposizione) di rispondere ad una richiesta ARP in locale senza eseguire " "un broadcast ARP oneroso nella sovrapposizione." msgid "" "Enable services on an agent with admin_state_up False. If this option is " "False, when admin_state_up of an agent is turned False, services on it will " "be disabled. Agents with admin_state_up False are not selected for automatic " "scheduling regardless of this option. But manual scheduling to such agents " "is available if this option is True." msgstr "" "Abilitare i servizi sull'agent con admin_state_up False. Se questa opzione è " "False, quando admin_state_up di un agent è su False, verranno disabilitati i " "servizi su tale agent. Gli agent con admin_state_up False non vengono " "selezionati per la pianificazione automatica indipendentemente da questa " "opzione. Ma è disponibile la pianificazione manuale di tali agent se questa " "opzione è impostata su True." msgid "" "Enables IPv6 Prefix Delegation for automatic subnet CIDR allocation. Set to " "True to enable IPv6 Prefix Delegation for subnet allocation in a PD-capable " "environment. Users making subnet creation requests for IPv6 subnets without " "providing a CIDR or subnetpool ID will be given a CIDR via the Prefix " "Delegation mechanism. Note that enabling PD will override the behavior of " "the default IPv6 subnetpool." msgstr "" "Abilita la delegazione del prefisso IPv6 per l'allocazione CIDR della " "sottorete automatica. Impostare su True per abilitare la delegazione del " "prefisso IPv6 per l'allocazione della sottorete in un ambiente con capacità " "PD. Gli utenti che effettuano richieste di creazione di una sottorete per le " "sottoreti IPv6 senza fornire un CIDR o un ID pool di sottorete riceveranno " "un CIDR mediante il meccanismo di delegazione del prefisso. L'abilitazione " "di PD sostituirà il comportamento del pool di sottorete IPv6 predefinito." msgid "" "Enables the dnsmasq service to provide name resolution for instances via DNS " "resolvers on the host running the DHCP agent. Effectively removes the '--no-" "resolv' option from the dnsmasq process arguments. Adding custom DNS " "resolvers to the 'dnsmasq_dns_servers' option disables this feature." msgstr "" "Abilita il servizio dnsmasq a fornire la risoluzione dei nomi per le istanze " "mediante i resolver DNS sull'host che esegue l'agent DHCP. In realtà rimuove " "l'opzione '--no-resolv' dagli argomenti del processo dnsmasq. L'aggiunta di " "risolver DNS personalizzati all'opzione 'dnsmasq_dns_servers' disabilita " "questa funzione." msgid "End of VLAN range is less than start of VLAN range" msgstr "La fine dell'intervallo VLAN è minore dell'inizio dell'intervallo VLAN" msgid "End of tunnel range is less than start of tunnel range" msgstr "" "L'intervallo finale del tunnel è inferiore all'intervallo iniziale del " "tunnel." #, python-format msgid "Error %(reason)s while attempting the operation." msgstr "Errore %(reason)s durante l'operazione." #, python-format msgid "Error parsing dns address %s" msgstr "Errore durante l'analisi dell'indirizzo dns %s" #, python-format msgid "Error while reading %s" msgstr "Errore durante le lettura di %s" #, python-format msgid "" "Exceeded %s second limit waiting for address to leave the tentative state." msgstr "" "Superato il limite di %s in attesa dell'indirizzo da lasciare nello stato di " "tentativo." msgid "Existing prefixes must be a subset of the new prefixes" msgstr "I prefissi esistenti devono essere un sottoinsieme dei nuovi prefissi" #, python-format msgid "" "Exit code: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" msgstr "" "Codice uscita: %(returncode)d; Stdin: %(stdin)s; Stdout: %(stdout)s; Stderr: " "%(stderr)s" #, python-format msgid "Extension %(driver)s failed." msgstr "Estensione %(driver)s non riuscita." #, python-format msgid "" "Extension driver %(driver)s required for service plugin %(service_plugin)s " "not found." msgstr "" "Driver di estensione %(driver)s richiesto per il plugin di servizio " "%(service_plugin)s non trovato." msgid "" "Extension to use alongside ml2 plugin's l2population mechanism driver. It " "enables the plugin to populate VXLAN forwarding table." msgstr "" "Estensione per utilizzare insieme del driver del meccanismo l2population del " "plugin m12. Essa abilita il plugin per popolare la tabella di inoltro VXLAN." #, python-format msgid "Extension with alias %s does not exist" msgstr "L'estensione con alias %s non esiste" msgid "Extensions list to use" msgstr "Elenco estensioni da utilizzare" #, python-format msgid "Extensions not found: %(extensions)s." msgstr "Estensioni non trovate: %(extensions)s." #, python-format msgid "External IP %s is the same as the gateway IP" msgstr "L'IP esterno %s è uguale all'IP gateway" #, python-format msgid "Failed rescheduling router %(router_id)s: no eligible l3 agent found." msgstr "" "Impossibile ripianificare il router %(router_id)s: non è stato trovato " "nessun agent L3 adatto." #, python-format msgid "Failed scheduling router %(router_id)s to the L3 Agent %(agent_id)s." msgstr "" "Impossibile pianificare il router %(router_id)s per l'agent L3 %(agent_id)s." #, python-format msgid "Failed to allocate subnet: %(reason)s." msgstr "Impossibile assegnare la sottorete: %(reason)s." msgid "" "Failed to associate address scope: subnetpools within an address scope must " "have unique prefixes." msgstr "" "Impossibile associare l'ambito di indirizzo: i pool di sottorete in un " "ambito di indirizzo devono avere prefissi univoci." #, python-format msgid "" "Failed to create port on network %(network_id)s, because fixed_ips included " "invalid subnet %(subnet_id)s" msgstr "" "Impossibile creare la porta nella rete %(network_id)s perché fixed_ips ha " "incluso una sottorete %(subnet_id)s non valida" #, python-format msgid "Failed to locate source for %s." msgstr "Impossibile individuare l'origine per %s." msgid "Failed to remove supplemental groups" msgstr "Impossibile rimuovere i gruppi supplementari" #, python-format msgid "Failed to set gid %s" msgstr "Impossibile impostare il gid %s" #, python-format msgid "Failed to set uid %s" msgstr "Impossibile impostare l'uid %s" #, python-format msgid "Failed to set-up %(type)s tunnel port to %(ip)s" msgstr "Impossibile impostare la porta tunnel %(type)s su %(ip)s" msgid "Failure applying iptables rules" msgstr "Errore nell'applicazione di regole iptables" #, python-format msgid "Failure waiting for address %(address)s to become ready: %(reason)s" msgstr "" "Errore durante l'attesa della disponibilità dell'indirizzo %(address)s: " "%(reason)s" msgid "Flat provider networks are disabled" msgstr "Le reti flat del provider sono disabilitate" msgid "For TCP/UDP protocols, port_range_min must be <= port_range_max" msgstr "Per i protocolli TCP/UDP, port_range_min deve essere <= port_range_max" msgid "Force ip_lib calls to use the root helper" msgstr "Forzare le chiamate ip_lib ad utilizzare root helper" #, python-format msgid "Found duplicate extension: %(alias)s." msgstr "Trovata estensione duplicata: %(alias)s." #, python-format msgid "" "Found overlapping allocation pools: %(pool_1)s %(pool_2)s for subnet " "%(subnet_cidr)s." msgstr "" "Trovati pool di allocazione di sovrapposizione:%(pool_1)s %(pool_2)s per la " "sottorete %(subnet_cidr)s." msgid "Gateway IP version inconsistent with allocation pool version" msgstr "Versione IP gateway incoerente con la versione del pool di allocazione" #, python-format msgid "Gateway ip %(ip_address)s conflicts with allocation pool %(pool)s." msgstr "" "L'ip gateway %(ip_address)s è in conflitto con il pool di allocazione " "%(pool)s." msgid "Gateway is not valid on subnet" msgstr "Il gateway non è valido sulla sottorete" msgid "" "Geneve encapsulation header size is dynamic, this value is used to calculate " "the maximum MTU for the driver. This is the sum of the sizes of the outer " "ETH + IP + UDP + GENEVE header sizes. The default size for this field is 50, " "which is the size of the Geneve header without any additional option headers." msgstr "" "La dimensione dell'intestazione di incapsulamento Geneve è dinamica, questo " "valore viene utilizzato per calcolare la MTU massima per il driver. " "Rappresenta la somma delle dimensioni delle intestazioni ETH + IP + UDP + " "GENEVE esterne. La dimensione predefinita per questo campo è 50, che " "rappresenta la dimensione dell'intestazione Geneve senza intestazioni di " "opzioni aggiuntive." msgid "" "Group (gid or name) running metadata proxy after its initialization (if " "empty: agent effective group)." msgstr "" "Gruppo (gid o nome) che esegue il proxy di metadati dopo la relativa " "inizializzazione (se vuoto: gruppo operativo dell'agent)." msgid "Group (gid or name) running this process after its initialization" msgstr "" "Gruppo (gid o name) che esegue questo processo dopo la relativa " "inizializzazione" msgid "" "Hostname to be used by the Neutron server, agents and services running on " "this machine. All the agents and services running on this machine must use " "the same host value." msgstr "" "Il nome host da utilizzare dal server Neutron, gli agent e servizi in " "esecuzione su questa macchina. Tutti gli agent ed i servizi in esecuzione su " "questa macchina devono utilizzare lo stesso valore host." #, python-format msgid "" "ICMP code (port-range-max) %(value)s is provided but ICMP type (port-range-" "min) is missing." msgstr "" "Il codice ICMP (port-range-max) %(value)s è stato fornito, ma il tipo ICMP " "(port-range-min) manca." msgid "ID of network" msgstr "ID della rete" msgid "ID of network to probe" msgstr "ID di rete per probe" msgid "ID of probe port to delete" msgstr "ID della porta probe da eliminare" msgid "ID of probe port to execute command" msgstr "ID della porta probe per eseguire il comando" msgid "ID of the router" msgstr "ID del router" #, python-format msgid "IP address %(ip)s already allocated in subnet %(subnet_id)s" msgstr "Indirizzo IP %(ip)s già assegnato nella sottorete %(subnet_id)s" #, python-format msgid "IP address %(ip)s does not belong to subnet %(subnet_id)s" msgstr "L'indirizzo IP %(ip)s non appartiene alla sottorete %(subnet_id)s" msgid "IP allocation failed. Try again later." msgstr "Allocazione IP non riuscita. Provare successivamente." msgid "IP allocation requires subnet_id or ip_address" msgstr "L'assegnazione IP richiede subnet_id o ip_address" #, python-format msgid "" "IPTablesManager.apply failed to apply the following set of iptables rules:\n" "%s" msgstr "" "IPTablesManager.apply non è riuscito ad applicare la seguente serie di " "regole iptables:\n" "%s" msgid "IPtables conntrack zones exhausted, iptables rules cannot be applied." msgstr "" "Zone IPtables conntrack esaurite, impossibile applicare le regole iptables." msgid "IPv6 Address Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "La modalità indirizzo IPv6 deve essere SLAAC o stateless per la delega " "prefisso." msgid "IPv6 RA Mode must be SLAAC or Stateless for Prefix Delegation." msgstr "" "La modalità RA IPv6 deve essere SLAAC o stateless per la delega prefisso." #, python-format msgid "" "IPv6 address %(ip)s cannot be directly assigned to a port on subnet " "%(subnet_id)s as the subnet is configured for automatic addresses" msgstr "" "L'indirizzo IPv6 %(ip)s non può essere assegnato direttamente ad una porta " "sulla sottorete %(subnet_id)s perché la sottorete è configurata per gli " "indirizzi automatici" #, python-format msgid "" "IPv6 subnet %s configured to receive RAs from an external router cannot be " "added to Neutron Router." msgstr "" "La sottorete IPv6 %s configurata per ricevere RA da un router esterno non " "può essere aggiunta a Neutron Router." msgid "" "If True, then allow plugins that support it to create VLAN transparent " "networks." msgstr "" "Se True, consentire ai plugin che lo supportano di creare reti VLAN " "trasparenti." msgid "Illegal IP version number" msgstr "Numero della versione IP non valido" #, python-format msgid "" "Illegal prefix bounds: %(prefix_type)s=%(prefixlen)s, %(base_prefix_type)s=" "%(base_prefixlen)s." msgstr "" "Limiti di prefisso non consentiti: %(prefix_type)s=%(prefixlen)s, " "%(base_prefix_type)s=%(base_prefixlen)s." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot " "associate with address scope %(address_scope_id)s because subnetpool " "ip_version is not %(ip_version)s." msgstr "" "Associazione pool di sottorete non valida: il pool di sottorete " "%(subnetpool_id)s non può essere associato all'ambito di indirizzo " "%(address_scope_id)s perché la versione IP del pool di sottorete non è " "%(ip_version)s." #, python-format msgid "" "Illegal subnetpool association: subnetpool %(subnetpool_id)s cannot be " "associated with address scope %(address_scope_id)s." msgstr "" "Associazione pool di sottorete non valida: il pool di sottorete " "%(subnetpool_id)s non può essere associato all'ambito di indirizzo " "%(address_scope_id)s." #, python-format msgid "Illegal subnetpool update : %(reason)s." msgstr "Aggiornamento pool di sottorete non valido: %(reason)s." #, python-format msgid "Illegal update to prefixes: %(msg)s." msgstr "Aggiornamento non valido dei prefissi: %(msg)s." msgid "" "In some cases the Neutron router is not present to provide the metadata IP " "but the DHCP server can be used to provide this info. Setting this value " "will force the DHCP server to append specific host routes to the DHCP " "request. If this option is set, then the metadata service will be activated " "for all the networks." msgstr "" "In alcuni casi il router Neutron non è presente per fornire l'IP dei " "metadati ma il server DHCP può essere utilizzato per fornire queste " "informazioni. L'impostazione di questo valore su True farà in modo che il " "server DHCP aggiunga instradamenti host specifici alla richiesta DHCP. Se " "questa opzione è impostata, il servizio di metadati verrà attivato per tutte " "le reti." msgid "" "Indicates that this L3 agent should also handle routers that do not have an " "external network gateway configured. This option should be True only for a " "single agent in a Neutron deployment, and may be False for all agents if all " "routers must have an external network gateway." msgstr "" "Indica che questo agent L3 deve anche gestire i router che non hanno un " "gateway di rete esterna configurato. Questa opzione deve essere True solo " "per un singolo agent di una distribuzione Neutron e può essere False per " "tutti gli agent se tutti i router devono avere un gateway di rete esterna." #, python-format msgid "Instance of class %(module)s.%(class)s must contain _cache attribute" msgstr "" "L'istanza di classe %(module)s.%(class)s deve contenere l'attributo _cache" #, python-format msgid "Insufficient prefix space to allocate subnet size /%s" msgstr "" "Spazio prefisso insufficiente per assegnare la dimensione della sottorete /%s" msgid "Insufficient rights for removing default security group." msgstr "" "Diritti non sufficienti per rimuovere il gruppo di sicurezza predefinito." msgid "" "Integration bridge to use. Do not change this parameter unless you have a " "good reason to. This is the name of the OVS integration bridge. There is one " "per hypervisor. The integration bridge acts as a virtual 'patch bay'. All VM " "VIFs are attached to this bridge and then 'patched' according to their " "network connectivity." msgstr "" "Il bridge di integrazione da utilizzare. Non modificare questo parametro a " "meno che non si abbia una buona ragione per farlo. Questo è il nome del " "bridge di integrazione OVS. Esiste un bridge per ciascun hypervisor. Il " "bridge di integrazione agisce come un 'patch bay' virtuale. Tutti i VIF VM " "sono collegati a questo bridge e quindi 'corretti' in base alla rispettiva " "connettività di rete." msgid "Interface to monitor" msgstr "Interfaccia da monitorare" msgid "" "Interval between checks of child process liveness (seconds), use 0 to disable" msgstr "" "Intervallo tra i controlli dell'attività del processo child (secondi), " "utilizzare 0 per disabilitare" msgid "Interval between two metering measures" msgstr "Intervallo tra due misure" msgid "Interval between two metering reports" msgstr "Intervallo tra due report di misurazione" #, python-format msgid "Invalid CIDR %(input)s given as IP prefix." msgstr "CIDR non valido %(input)s fornito come prefisso IP." #, python-format msgid "Invalid Device %(dev_name)s: %(reason)s" msgstr "Dispositivo non valido %(dev_name)s: %(reason)s" #, python-format msgid "" "Invalid action '%(action)s' for object type '%(object_type)s'. Valid " "actions: %(valid_actions)s" msgstr "" "Azione non valida '%(action)s' per tipo di oggetto '%(object_type)s'. Azioni " "valide: %(valid_actions)s" #, python-format msgid "" "Invalid authentication type: %(auth_type)s, valid types are: " "%(valid_auth_types)s" msgstr "" "Tipo di autenticazione non valido: %(auth_type)s, i tipi validi sono: " "%(valid_auth_types)s" #, python-format msgid "Invalid ethertype %(ethertype)s for protocol %(protocol)s." msgstr "ethertype %(ethertype)s non valido per il protocollo %(protocol)s." #, python-format msgid "Invalid format: %s" msgstr "Formato non valido: %s" #, python-format msgid "Invalid instance state: %(state)s, valid states are: %(valid_states)s" msgstr "" "Stato istanza non valido: %(state)s, gli stati validi sono: %(valid_states)s" #, python-format msgid "Invalid mapping: '%s'" msgstr "Associazione non valida: '%s'" #, python-format msgid "Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'." msgstr "Intervallo VLAN della rete non valido: '%(vlan_range)s' - '%(error)s'." #, python-format msgid "Invalid network VXLAN port range: '%(vxlan_range)s'." msgstr "Intervallo porta VXLAN di rete non valida: '%(vxlan_range)s'." #, python-format msgid "Invalid pci slot %(pci_slot)s" msgstr "pci slot non valido %(pci_slot)s" #, python-format msgid "Invalid provider format. Last part should be 'default' or empty: %s" msgstr "" "Formato del provider non valido. L'ultima parte deve essere 'default' o " "vuota: %s" #, python-format msgid "Invalid resource type %(resource_type)s" msgstr "Tipo di risorsa non valido %(resource_type)s" #, python-format msgid "Invalid route: %s" msgstr "Route invalido: %s" msgid "Invalid service provider format" msgstr "Formato del provider del servizio non valido" #, python-format msgid "" "Invalid value for ICMP %(field)s (%(attr)s) %(value)s. It must be 0 to 255." msgstr "" "Valore non valido per ICMP %(field)s (%(attr)s) %(value)s. Deve essere " "compreso tra 0 e 255." #, python-format msgid "Invalid value for port %(port)s" msgstr "Valore invalido per la porta %(port)s" msgid "" "Iptables mangle mark used to mark ingress from external network. This mark " "will be masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Indicatore mangle iptables per contrassegnare l'ingresso dalla rete esterna. " "Tale indicatoreverrà mascherato con 0xffff in modo che verranno utilizzati " "solo i 16 bit inferiori. " msgid "" "Iptables mangle mark used to mark metadata valid requests. This mark will be " "masked with 0xffff so that only the lower 16 bits will be used." msgstr "" "Indicatore mangle iptables per contrassegnare le richieste valide di " "metadati. Tale indicatore verràmascherato con 0xffff in modo che verranno " "utilizzati solo i 16 bit inferiori. " msgid "Keepalived didn't respawn" msgstr "Keepalived non ha eseguito la nuova generazione" msgid "Keepalived didn't spawn" msgstr "Keepalived non ha eseguito la generazione" #, python-format msgid "" "Kernel HZ value %(value)s is not valid. This value must be greater than 0." msgstr "" "Il valore Kernel HZ %(value)s non è valido. Questo valore deve essere " "maggiore di 0." msgid "L3 agent failure to setup NAT for floating IPs" msgstr "Errore dell'agent L3 durante la configurazione di NAT per IP mobili" msgid "L3 agent failure to setup floating IPs" msgstr "Errore dell'agent L3 durante la configurazione di IP mobili" msgid "Limit number of leases to prevent a denial-of-service." msgstr "Limitare il numero di lease per evitare un denial-of-service." msgid "List of :" msgstr "Elenco di :" msgid "" "List of :: or " "specifying physical_network names usable for VLAN provider and tenant " "networks, as well as ranges of VLAN tags on each available for allocation to " "tenant networks." msgstr "" "Elenco di :: o che " "specificano nomi physical_network utilizzabili per le reti tenant e provider " "VLAN, come anche gli intervalli di tag VLAN su ciascuno disponibile per " "l'assegnazione alle reti tenant." msgid "" "List of network type driver entrypoints to be loaded from the neutron.ml2." "type_drivers namespace." msgstr "" "Elenco dei punti di ingresso del driver del tipo di rete da caricare dallo " "spazio dei nomi neutron.ml2.type_drivers." msgid "" "List of physical_network names with which flat networks can be created. Use " "default '*' to allow flat networks with arbitrary physical_network names. " "Use an empty list to disable flat networks." msgstr "" "Elenco di nomi physical_network con cui possono essere create reti flat. " "Utilizzare il valore '*' predefinito per consentire reti flat con nomi " "physical_network arbitrari. Utilizzare un elenco vuoto per disabilitare le " "reti flat." msgid "Location for Metadata Proxy UNIX domain socket." msgstr "Ubicazione per il socket del dominio UNIX del proxy di metadati." msgid "Location of Metadata Proxy UNIX domain socket" msgstr "Ubicazione del socket del dominio UNIX del proxy di metadati" msgid "Location to store DHCP server config files." msgstr "Ubicazione per archiviare i file di configurazione del server DHCP." msgid "Location to store IPv6 PD files." msgstr "Ubicazione per archiviare i file PD IPv6." msgid "Location to store IPv6 RA config files" msgstr "Ubicazione per memorizzare i file di configurazione IPv6 RA" msgid "Location to store child pid files" msgstr "Ubicazione per archiviare i file pid dell'elemento child" msgid "Location to store keepalived/conntrackd config files" msgstr "" "Ubicazione per archiviare i file di configurazione keepalived/conntrackd" msgid "Log agent heartbeats" msgstr "Registra gli heartbeat dell'agent" msgid "" "MTU of the underlying physical network. Neutron uses this value to calculate " "MTU for all virtual network components. For flat and VLAN networks, neutron " "uses this value without modification. For overlay networks such as VXLAN, " "neutron automatically subtracts the overlay protocol overhead from this " "value. Defaults to 1500, the standard value for Ethernet." msgstr "" "MTU della rete fisica sottostante. Neutron utilizza questo valore per " "calcolare la MTU per tutti i componenti della rete virtuale. Per le reti " "flat e VLAN, neutron utilizza questo valore senza modifica. Per le reti di " "sovrapposizione come VXLAN, neutron sottrae automaticamente l'overhead del " "protocollo di sovrapposizione da questo valore. Il valore predefinito è " "impostato su 1500, il valore standard per Ethernet." msgid "MTU size of veth interfaces" msgstr "Dimensione MTU delle interfacce veth" msgid "Make the l2 agent run in DVR mode." msgstr "Eseguire l'agent L2 in modalità DVR." msgid "Malformed request body" msgstr "Corpo richiesta non corretto" #, python-format msgid "Malformed request body: %(reason)s." msgstr "Corpo richiesta non corretto: %(reason)s." msgid "MaxRtrAdvInterval setting for radvd.conf" msgstr "Impostazione MaxRtrAdvInterval per radvd.conf" msgid "Maximum number of DNS nameservers per subnet" msgstr "Numero massimo di server dei nomi DNS per la sottorete" msgid "" "Maximum number of L3 agents which a HA router will be scheduled on. If it is " "set to 0 then the router will be scheduled on every agent." msgstr "" "Numero massimo di agent L3 su cui verrà pianificato un router HA. Se è " "impostato su 0, il router verrà pianificato su ciascun agent." msgid "Maximum number of allowed address pairs" msgstr "Numero massimo di coppie di indirizzi consentito" msgid "Maximum number of host routes per subnet" msgstr "Numero massimo di route host per la sottorete" msgid "Maximum number of routes per router" msgstr "Numero massimo di instradamenti per router" msgid "" "Metadata Proxy UNIX domain socket mode, 4 values allowed: 'deduce': deduce " "mode from metadata_proxy_user/group values, 'user': set metadata proxy " "socket mode to 0o644, to use when metadata_proxy_user is agent effective " "user or root, 'group': set metadata proxy socket mode to 0o664, to use when " "metadata_proxy_group is agent effective group or root, 'all': set metadata " "proxy socket mode to 0o666, to use otherwise." msgstr "" "Modalità socket del dominio UNIX del proxy di metadati, 4 valori consentiti: " "'deduce': modalità deduzione da valori metadata_proxy_user/group, 'user': " "impostare modalità socket proxy metadati su 0o644, da usare quando " "metadata_proxy_user è l'utente effettivo agent o root, 'group': impostare " "modalità socket proxy metadati su 0o664, da usare quando " "metadata_proxy_group è gruppo effettivo agent o root, 'all': impostare " "modalità socket proxy metadati su 0o666, per usare altrimenti." msgid "Metering driver" msgstr "Driver di misurazione" msgid "MinRtrAdvInterval setting for radvd.conf" msgstr "Impostazione MinRtrAdvInterval per radvd.conf" msgid "Minimize polling by monitoring ovsdb for interface changes." msgstr "" "Ridurre al minimo il polling controllando ovsdb per le modifiche " "all'interfaccia." #, python-format msgid "Missing key in mapping: '%s'" msgstr "Chiave mancante nell'associazione: '%s'" msgid "" "Multicast group for VXLAN. When configured, will enable sending all " "broadcast traffic to this multicast group. When left unconfigured, will " "disable multicast VXLAN mode." msgstr "" "Gruppo multicast per VXLAN. Quando configurato, abilita l'invio di tutto il " "traffico broadcast a questo gruppo multicast. Quando non configurato, " "disabilita la modalità multicast VXLAN." msgid "" "Multicast group(s) for vxlan interface. A range of group addresses may be " "specified by using CIDR notation. Specifying a range allows different VNIs " "to use different group addresses, reducing or eliminating spurious broadcast " "traffic to the tunnel endpoints. To reserve a unique group for each possible " "(24-bit) VNI, use a /8 such as 239.0.0.0/8. This setting must be the same on " "all the agents." msgstr "" "Gruppo multicast per l'interfaccia vxlan. Un intervallo di indirizzi di " "gruppo può essere specificato utilizzando la notazione CIDR. La definizione " "di un intervallo consente a VNI diversi di utilizzare indirizzi di gruppo " "diversi, riducendo o eliminando il traffico di broadcast spurio agli " "endpoint del tunnel. Per riservare un gruppo univoco per ciascun VNI " "possibile (24-bit), utilizzare /8, ad esempio, 239.0.0.0/8. Questa " "impostazione deve essere la stessa su tutti gli agent." #, python-format msgid "Multiple default providers for service %s" msgstr "Più provider predefiniti per il servizio %s" #, python-format msgid "Multiple plugins for service %s were configured" msgstr "Sono stati configurati più plugin per il servizio %s" #, python-format msgid "Multiple providers specified for service %s" msgstr "Più provider specificati per il servizio %s" msgid "Multiple tenant_ids in bulk security group rule create not allowed" msgstr "" "La creazione in massa di più tenant_id nella regola del gruppo di sicurezza " "non è consentita" msgid "Must also specify protocol if port range is given." msgstr "" "È necessario anche specificare il protocollo se è fornito l'intervallo di " "porta." msgid "Must specify one or more actions on flow addition or modification" msgstr "" "È necessario specificare una o più azioni nell'aggiunta o modifica del flusso" msgid "Name of Open vSwitch bridge to use" msgstr "Nome del bridge Open vSwitch da utilizzare" msgid "" "Name of nova region to use. Useful if keystone manages more than one region." msgstr "" "Nome della regione nova da utilizzare. Utile nel caso in cui keystone " "gestisce più di una regione." msgid "Namespace of the router" msgstr "Spazio dei nomi del router" msgid "Native pagination depend on native sorting" msgstr "La paginazione nativa deipende dall'ordinamento nativo" #, python-format msgid "" "Need to apply migrations from %(project)s contract branch. This will require " "all Neutron server instances to be shutdown before proceeding with the " "upgrade." msgstr "" "È necessario applicare le migrazioni dal ramo di contratto %(project)s. " "Prima di procedere con l'aggiornamento è necessario che tutte le istanze del " "server Neutron vengano chiuse." msgid "Negative delta (downgrade) not supported" msgstr "Delta negativo (riduzione) non supportato" msgid "Negative relative revision (downgrade) not supported" msgstr "Revisione relativa negativa (riduzione) non suportata" #, python-format msgid "Network %s does not contain any IPv4 subnet" msgstr "La rete %s non contiene alcuna sottorete IPv4" #, python-format msgid "Network %s is not a valid external network" msgstr "La rete %s non è una rete esterna valida" #, python-format msgid "Network %s is not an external network" msgstr "La rete %s non è una rete esterna" #, python-format msgid "" "Network of size %(size)s, from IP range %(parent_range)s excluding IP ranges " "%(excluded_ranges)s was not found." msgstr "" "Rete di dimensione %(size)s, dall'intervallo IP %(parent_range)s esclusi gli " "intervalli IP %(excluded_ranges)s non trovata." #, python-format msgid "Network type value '%s' not supported" msgstr "Valore del tipo di rete '%s' non supportato" msgid "Network type value needed by the ML2 plugin" msgstr "Valore Tipo di rete richiesto dal plugin ML2" msgid "Network types supported by the agent (gre and/or vxlan)." msgstr "Tipi di reti supportati dall'agent (gre e/o vxlan)." msgid "Neutron Service Type Management" msgstr "Gestione tipo servizio Neutron" msgid "Neutron core_plugin not configured!" msgstr "Neutron core_plugin non configurato!" msgid "No default router:external network" msgstr "Nessuna rete router:external predefinita" #, python-format msgid "No default subnetpool found for IPv%s" msgstr "Nessun pool di sottorete predefinito trovato per IPv%s" msgid "No default subnetpools defined" msgstr "Nessun pool di sottorete predefinito definito" #, python-format msgid "No eligible l3 agent associated with external network %s found" msgstr "" "Non è stato trovato nessun agent L3 adatto associato alla rete esterna %s" #, python-format msgid "No more IP addresses available for subnet %(subnet_id)s." msgstr "Indirizzi IP non più disponibili per la sottorete %(subnet_id)s." msgid "No offline migrations pending." msgstr "Nessuna migrazione offline in sospeso." #, python-format msgid "No shared key in %s fields" msgstr "Nessuna chiave condivisa in %s campi" msgid "Not allowed to manually assign a router to an agent in 'dvr' mode." msgstr "" "Attualmente non è consentito assegnare manualmente un router ad un agent in " "modalità 'dvr'." msgid "Not allowed to manually remove a router from an agent in 'dvr' mode." msgstr "" "Attualmente non è consentito rimuovere manualmente un router da un agent in " "modalità 'dvr'." msgid "" "Number of DHCP agents scheduled to host a tenant network. If this number is " "greater than 1, the scheduler automatically assigns multiple DHCP agents for " "a given tenant network, providing high availability for DHCP service." msgstr "" "Numero di agent DHCP pianificati per ospitare una rete titolare. Se questo " "numero è maggiore di 1, lo scheduler assegna automaticamente più agent DHCP " "per una data rete titolare, fornendo l'alta disponibilità per il servizio " "DHCP." msgid "Number of backlog requests to configure the metadata server socket with" msgstr "" "Numero di richieste di backlog con cui configurare il socket server dei " "metadati" msgid "Number of backlog requests to configure the socket with" msgstr "Numero di richieste di backlog per configurare il socket con" msgid "" "Number of bits in an ipv4 PTR zone that will be considered network prefix. " "It has to align to byte boundary. Minimum value is 8. Maximum value is 24. " "As a consequence, range of values is 8, 16 and 24" msgstr "" "Il numero di bit in una zona PTR ipv4 che verrà considerato prefisso di " "rete. Deve allinearsi al limite di byte. Il valore minimo è 8. Il valore " "massimo è 24. Di conseguenza, l'intervallo di valori è 8, 16 e 24" msgid "" "Number of bits in an ipv6 PTR zone that will be considered network prefix. " "It has to align to nyble boundary. Minimum value is 4. Maximum value is 124. " "As a consequence, range of values is 4, 8, 12, 16,..., 124" msgstr "" "Il numero di bit in una zona PTR ipv6 che verrà considerato prefisso di " "rete. Deve allinearsi al limite nyble. Il valore minimo è 4. Il valore " "massimo è 124. Di conseguenza, l'intervallo di valori è 4, 8, 12, 16, ...., " "124" msgid "" "Number of floating IPs allowed per tenant. A negative value means unlimited." msgstr "" "Numero di IP mobili consentiti per tenant. Un valore negativo indica un " "numero illimitato." msgid "" "Number of networks allowed per tenant. A negative value means unlimited." msgstr "" "Numero di reti consentite per tenant. Un valore negativo indica un numero " "illimitato." msgid "Number of ports allowed per tenant. A negative value means unlimited." msgstr "" "Numero di porte consentite per tenant. Un valore negativo indica un numero " "illimitato." msgid "Number of routers allowed per tenant. A negative value means unlimited." msgstr "" "Numero di router consentiti per tenant. Un valore negativo indica un numero " "illimitato." msgid "" "Number of seconds between sending events to nova if there are any events to " "send." msgstr "" "Numero di secondi tra l'invio di eventi a nova se vi sono eventuali eventi " "da inviare." msgid "Number of seconds to keep retrying to listen" msgstr "Numero di secondi per trattenere i nuovi tentativi di ascolto" msgid "" "Number of security groups allowed per tenant. A negative value means " "unlimited." msgstr "" "Numero di gruppi di sicurezza consentiti per tenant. Un valore negativo " "indica un numero illimitato." msgid "" "Number of security rules allowed per tenant. A negative value means " "unlimited." msgstr "" "Numero di regole di sicurezza consentite per tenant. Un valore negativo " "indica un numero illimitato." msgid "" "Number of separate API worker processes for service. If not specified, the " "default is equal to the number of CPUs available for best performance." msgstr "" "Il numero di processi worker API separati per il servizio. Se non " "specificato, il valore predefinito è uguale al numero di CPU disponibili per " "prestazioni ottimali." msgid "" "Number of separate worker processes for metadata server (defaults to half of " "the number of CPUs)" msgstr "" "Numero di processi worker separati per server di metadati (il valore " "predefinito è metà del numero di CPU)" msgid "Number of subnets allowed per tenant, A negative value means unlimited." msgstr "" "Numero di sottoreti consentite per tenant. Un valore negativo indica un " "numero illimitato." msgid "" "Number of threads to use during sync process. Should not exceed connection " "pool size configured on server." msgstr "" "Numero di thread da utilizzare durante il processo di sincronizzazione. Non " "deve superare la dimensione del pool di connessione configurata sul server." msgid "OK" msgstr "OK" msgid "" "OVS datapath to use. 'system' is the default value and corresponds to the " "kernel datapath. To enable the userspace datapath set this value to 'netdev'." msgstr "" "Datapath OVS da utilizzare. 'system' è il valore predefinito e corrisponde " "al datapath del kernel. Per abilitare il datapath dello spazio dei nomi, " "impostare questo valore su 'netdev'." msgid "OVS vhost-user socket directory." msgstr "Directory socket vhost-user OVS." #, python-format msgid "Object action %(action)s failed because: %(reason)s." msgstr "Azione dell'oggetto %(action)s non riuscita perché: %(reason)s" msgid "Only admin can view or configure quota" msgstr "Solo admin può visualizzare o configurare una quota" msgid "Only admin is authorized to access quotas for another tenant" msgstr "Solo l'admin è autorizzato ad accedere alle quote per un altro tenant" msgid "Only admins can manipulate policies on objects they do not own" msgstr "" "Solo gli admin possono gestire le politiche su oggetti che non possiedono" msgid "Only allowed to update rules for one security profile at a time" msgstr "" "Al momento è consentito solo aggiornare le regole per un profilo di " "sicurezza." msgid "Only remote_ip_prefix or remote_group_id may be provided." msgstr "È possibile fornire solo remote_ip_prefix o remote_group_id." msgid "OpenFlow interface to use." msgstr "L'interfaccia OpenFlow da utilizzare." #, python-format msgid "" "Operation %(op)s is not supported for device_owner %(device_owner)s on port " "%(port_id)s." msgstr "" "Operazione %(op)s non supportata per device_owner %(device_owner)s sulla " "porta %(port_id)s." #, python-format msgid "Operation not supported on device %(dev_name)s" msgstr "Operazione non supportata sul dispositivo %(dev_name)s" msgid "" "Ordered list of network_types to allocate as tenant networks. The default " "value 'local' is useful for single-box testing but provides no connectivity " "between hosts." msgstr "" "Elenco ordinato di network_types da assegnare come reti tenant. Il valore " "predefinito 'local' è utile per la verifica single-box ma non fornisce " "alcuna connettività tra host." msgid "Override the default dnsmasq settings with this file." msgstr "" "Sostituire le impostazioni dnsmasq predefinite utilizzando questo file." msgid "Owner type of the device: network/compute" msgstr "Tipo proprietario dell'unità: rete/compute" msgid "POST requests are not supported on this resource." msgstr "Le richieste POST non sono supportate su questa risorsa." #, python-format msgid "Package %s not installed" msgstr "Pacchetto %s non installato" #, python-format msgid "Parsing bridge_mappings failed: %s." msgstr "Analisi bridge_mappings non riuscita: %s." msgid "Password for connecting to designate in admin context" msgstr "Password per la connessione da designare nel contesto admin" msgid "Path to PID file for this process" msgstr "Percorso per il file PID per questo processo" msgid "Path to the router directory" msgstr "Percorso per la directory del router" msgid "Peer patch port in integration bridge for tunnel bridge." msgstr "Porta patch peer nel bridge di integrazione per il bridge tunnel." msgid "Peer patch port in tunnel bridge for integration bridge." msgstr "Porta patch peer nel bridge tunnel per il bridge di integrazione." msgid "Per-tenant subnet pool prefix quota exceeded." msgstr "Quota prefisso pool di sottorete per-tenant superata." msgid "Phase upgrade options do not accept revision specification" msgstr "" "Le opzioni di aggiornamento fase non accettano la specifica di revisione" msgid "Ping timeout" msgstr "Timeout di ping" msgid "Plugin does not support updating provider attributes" msgstr "Il plugin non supporta l'aggiornamento degli attributi provider" #, python-format msgid "Port %(id)s does not have fixed ip %(address)s" msgstr "La porta %(id)s non dispone di un ip fisso %(address)s" #, python-format msgid "Port %(port_id)s is already acquired by another DHCP agent" msgstr "La porta %(port_id)s è già acquisita da un altro agent DHCP " #, python-format msgid "" "Port %s has multiple fixed IPv4 addresses. Must provide a specific IPv4 " "address when assigning a floating IP" msgstr "" "La porta %s dispone di più indirizzi IPv4 fissi. È necessario fornirne uno " "specifico durante l'assegnazione di un IP mobile" msgid "" "Port to listen on for OpenFlow connections. Used only for 'native' driver." msgstr "" "Porta di ascolto per le connessioni OpenFlow. Utilizzata solo per driver " "'native'." #, python-format msgid "Prefix '%(prefix)s' not supported in IPv%(version)s pool." msgstr "Il prefisso '%(prefix)s' non è supportato nel pool IPv%(version)s." msgid "Prefix Delegation can only be used with IPv6 subnets." msgstr "La delega prefisso può essere utilizzata solo con sottoreti IPv6." msgid "Private key of client certificate." msgstr "Chiave privata del certificato client." #, python-format msgid "Probe %s deleted" msgstr "Probe %s eliminato" #, python-format msgid "Probe created : %s " msgstr "Probe creato : %s " msgid "Process is already started" msgstr "Processo già avviato" msgid "Process is not running." msgstr "Il processo non è in esecuzione." msgid "Protocol to access nova metadata, http or https" msgstr "Protocollo per accedere ai metadati nova, http o https" #, python-format msgid "Provider name %(name)s is limited by %(len)s characters" msgstr "Il nome del provider %(name)s è limitato a %(len)s caratteri" #, python-format msgid "QoS Policy %(policy_id)s is used by %(object_type)s %(object_id)s." msgstr "" "La politica QoS %(policy_id)s è utilizzata da %(object_type)s %(object_id)s." #, python-format msgid "" "QoS binding for network %(net_id)s and policy %(policy_id)s could not be " "found." msgstr "" "Impossibile trovare il collegamento QoS per la rete %(net_id)s e la politica " "%(policy_id)s." #, python-format msgid "" "QoS binding for port %(port_id)s and policy %(policy_id)s could not be found." msgstr "" "Impossibile trovare il collegamento QoS per la porta %(port_id)s e la " "politica %(policy_id)s." #, python-format msgid "QoS policy %(policy_id)s could not be found." msgstr "Impossibile trovare la politica QoS %(policy_id)s." #, python-format msgid "QoS rule %(rule_id)s for policy %(policy_id)s could not be found." msgstr "" "Impossibile trovare la regola QoS %(rule_id)s for policy %(policy_id)s." #, python-format msgid "RBAC policy of type %(object_type)s with ID %(id)s not found" msgstr "Politica RBAC di tipo %(object_type)s con ID %(id)s non trovata" #, python-format msgid "" "RBAC policy on object %(object_id)s cannot be removed because other objects " "depend on it.\n" "Details: %(details)s" msgstr "" "La politica RBAC sull'oggetto %(object_id)s non può essere rimossa perché " "altri oggetti dipendono da essa.\n" "Dettagli: %(details)s" msgid "" "Range of seconds to randomly delay when starting the periodic task scheduler " "to reduce stampeding. (Disable by setting to 0)" msgstr "" "Intervallo di secondi per ritardare casualmente l'avvio di attività " "periodiche programma di pianificazione per ridurre la modifica data/ora. " "(Disabilitare impostando questa opzione a 0)" msgid "Ranges must be in the same IP version" msgstr "Gli intervalli devono essere nella stessa versione IP" msgid "Ranges must be netaddr.IPRange" msgstr "Gli intervalli devono essere netaddr.IPRange" msgid "Ranges must not overlap" msgstr "Gli intervalli non devono sovrapporsi" #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.EUI type." msgstr "" "Ricevuto tipo '%(type)s' e valore '%(value)s'. Previsto il tipo netaddr.EUI." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPAddress " "type." msgstr "" "Ricevuto tipo '%(type)s' e valore '%(value)s'. Previsto il tipo netaddr." "IPAddress." #, python-format msgid "" "Received type '%(type)s' and value '%(value)s'. Expecting netaddr.IPNetwork " "type." msgstr "" "Ricevuto tipo '%(type)s' e valore '%(value)s'. Previsto il tipo netaddr." "IPNetwork." #, python-format msgid "" "Release aware branch labels (%s) are deprecated. Please switch to expand@ " "and contract@ labels." msgstr "" "Le etichette ramo che riconoscono la release (%s) sono sconsigliate. Passare " "alle etichette expand@ e contract@." msgid "Remote metadata server experienced an internal server error." msgstr "Il server di metadati remoto ha rilevato un errore di server interno." msgid "" "Repository does not contain HEAD files for contract and expand branches." msgstr "" "Il repository non contiene i file HEAD per i rami di contratto ed espansione." msgid "" "Representing the resource type whose load is being reported by the agent. " "This can be \"networks\", \"subnets\" or \"ports\". When specified (Default " "is networks), the server will extract particular load sent as part of its " "agent configuration object from the agent report state, which is the number " "of resources being consumed, at every report_interval.dhcp_load_type can be " "used in combination with network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler When the network_scheduler_driver is " "WeightScheduler, dhcp_load_type can be configured to represent the choice " "for the resource being balanced. Example: dhcp_load_type=networks" msgstr "" "Rappresentazione del tipo di risorsa il cui carico è segnalato dall'agent. " "Può essere \"networks\", \"subnets\" o \"ports\". Quando specificato " "(L'impostazione predefinita è networks), il server estrarrà il carico " "particolare inviato come parte del relativo oggetto di configurazione agent " "dallo stato del report agent, il quale rappresenta il numero di risorse " "utilizzate, ad ogni report_interval. dhcp_load_type può essere utilizzato in " "combinazione con network_scheduler_driver = neutron.scheduler." "dhcp_agent_scheduler.WeightScheduler Quando network_scheduler_driver è " "WeightScheduler, dhcp_load_type può essere configurato per rappresentare la " "scelta per la risorsa in fase di bilanciamento. Esempio: " "dhcp_load_type=networks" msgid "Request Failed: internal server error while processing your request." msgstr "" "Richiesta non riuscita: errore server interno durante l'elaborazione della " "richiesta." msgid "" "Reset flow table on start. Setting this to True will cause brief traffic " "interruption." msgstr "" "Reimpostare tabella flusso all'avvio. Impostandolo su True si provoca una " "breve interruzione del traffico." #, python-format msgid "Resource %(resource)s %(resource_id)s could not be found." msgstr "Impossibile trovare la risorsa %(resource)s %(resource_id)s." #, python-format msgid "Resource %(resource_id)s of type %(resource_type)s not found" msgstr "Risorsa %(resource_id)s di tipo %(resource_type)s non trovata" #, python-format msgid "" "Resource '%(resource_id)s' is already associated with provider " "'%(provider)s' for service type '%(service_type)s'" msgstr "" "La risorsa '%(resource_id)s' è già associata al provider '%(provider)s' per " "il tipo di servizio '%(service_type)s'" msgid "Resource body required" msgstr "Corpo risorsa richiesto" msgid "Resource not found." msgstr "Risorsa non trovata." msgid "Resources required" msgstr "Risorse richieste" msgid "" "Root helper application. Use 'sudo neutron-rootwrap /etc/neutron/rootwrap." "conf' to use the real root filter facility. Change to 'sudo' to skip the " "filtering and just run the command directly." msgstr "" "Applicazione root helper. Utilizzare 'sudo neutron-rootwrap /etc/neutron/" "rootwrap.conf' per utilizzare la funzione di filtro root reale. Passare su " "'sudo' per ignorare il filtro e semplicemente eseguire il comando " "direttamente." msgid "Root permissions are required to drop privileges." msgstr "Per rilasciare i privilegi sono necessarie le autorizzazioni root." #, python-format msgid "Router '%(router_id)s' is not compatible with this agent." msgstr "Il router '%(router_id)s' non è compatibile con questo agent." #, python-format msgid "Router already has a port on subnet %s" msgstr "Il router dispone già di una porta sulla sottorete %s" msgid "Router port must have at least one fixed IP" msgstr "La porta del router deve avere almeno un IP fisso" #, python-format msgid "Running %(cmd)s (%(desc)s) for %(project)s ..." msgstr "Esecuzione di %(cmd)s (%(desc)s) per %(project)s ..." #, python-format msgid "Running %(cmd)s for %(project)s ..." msgstr "Esecuzione di %(cmd)s per %(project)s ..." msgid "" "Seconds between nodes reporting state to server; should be less than " "agent_down_time, best if it is half or less than agent_down_time." msgstr "" "Secondi tra lo stato riportato dai nodi al server; deve essere inferiore di " "agent_down_time, è preferibile che sia la metà o meno di agent_down_time." msgid "" "Seconds to regard the agent is down; should be at least twice " "report_interval, to be sure the agent is down for good." msgstr "" "Secondi per considerare che l'agent è inattivo; deve essere almeno il doppio " "di report_interval, per essere sicuri che l'agent è definitivamente inattivo." #, python-format msgid "Security Group %(id)s %(reason)s." msgstr "Gruppo di sicurezza %(id)s %(reason)s." #, python-format msgid "Security Group Rule %(id)s %(reason)s." msgstr "Regola gruppo di sicurezza %(id)s %(reason)s." #, python-format msgid "Security group %(id)s does not exist" msgstr "Il gruppo di sicurezza %(id)s non esiste" #, python-format msgid "Security group rule %(id)s does not exist" msgstr "La regola del gruppo di sicurezza %(id)s non esiste" #, python-format msgid "Security group rule already exists. Rule id is %(rule_id)s." msgstr "" "La regola del gruppo di sicurezza già esiste. L'ID regola è %(rule_id)s." #, python-format msgid "" "Security group rule for ethertype '%(ethertype)s' not supported. Allowed " "values are %(values)s." msgstr "" "Regola del gruppo di sicurezza per ethertype '%(ethertype)s' non supportata. " "I valori consentiti sono %(values)s." #, python-format msgid "" "Security group rule protocol %(protocol)s not supported. Only protocol " "values %(values)s and integer representations [0 to 255] are supported." msgstr "" "Il protocollo della regole del gruppo di sicurezza %(protocol)s non è " "supportato. Solo i valori del protocollo %(values)s e le rappresentazioni " "numeri interi [0-255] sono supportati." msgid "Segments and provider values cannot both be set." msgstr "Impossibile impostare i segmenti e i valori del provider." msgid "Selects the Agent Type reported" msgstr "Seleziona il tipo di agent riportato" msgid "" "Send notification to nova when port data (fixed_ips/floatingip) changes so " "nova can update its cache." msgstr "" "Invia una notifica a nova quando i dati porta (fixed_ips/floatingip) vengono " "modificati e in tal modo nova può aggiornare la propria cache." msgid "Send notification to nova when port status changes" msgstr "Invia una notifica a nova quando lo stato della porta cambia" #, python-format msgid "" "Service provider '%(provider)s' could not be found for service type " "%(service_type)s" msgstr "" "Provider del servizio '%(provider)s' non trovato per il tipo di servizio " "%(service_type)s" msgid "Service to handle DHCPv6 Prefix delegation." msgstr "Il servizio per gestire la delega prefisso DHCPv6." #, python-format msgid "Service type %(service_type)s does not have a default service provider" msgstr "" "Il tipo del servizio %(service_type)s non ha un provider del servizio " "predefinito" msgid "" "Set new timeout in seconds for new rpc calls after agent receives SIGTERM. " "If value is set to 0, rpc timeout won't be changed" msgstr "" "Impostare il nuovo timeout in secondi per le nuove chiamate rpc dopo che " "l'agent riceve SIGTERM. Se il valore è impostato su 0, il timeout rpc non " "verrà modificato" msgid "" "Set or un-set the don't fragment (DF) bit on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "Impostare o annullare l'impostazione del bit del frammento non DF sul " "pacchetto IP in uscita che trasporta il tunnel GRE/VXLAN." msgid "" "Set or un-set the tunnel header checksum on outgoing IP packet carrying GRE/" "VXLAN tunnel." msgstr "" "Impostare o annullare l'impostazione del checksum intestazione tunnel sul " "pacchetto IP in uscita che usail tunnel GRE/VXLAN." msgid "Shared address scope can't be unshared" msgstr "Impossibile annullare la condivisione di un ambito indirizzo condiviso" msgid "String prefix used to match IPset names." msgstr "Prefisso stringa utilizzato per la corrispondenza con i nomi IPset." #, python-format msgid "Sub-project %s not installed." msgstr "Sottoprogetto %s non installato." msgid "Subnet for router interface must have a gateway IP" msgstr "La sottorete per l'interfaccia del router deve avere un IP gateway" #, python-format msgid "Subnet pool %(subnetpool_id)s could not be found." msgstr "Impossibile trovare il pool di sottorete %(subnetpool_id)s" msgid "Subnet pool has existing allocations" msgstr "Il pool di sottoreti ha assegnazioni esistenti" msgid "Subnet used for the l3 HA admin network." msgstr "Sottorete utilizzata per la rete admin HA L3" msgid "" "Subnets hosted on the same network must be allocated from the same subnet " "pool." msgstr "" "Le sottoreti ospitate sulla stessa rete devono essere allocate dallo stesso " "pool di sottoreti." msgid "" "System-wide flag to determine the type of router that tenants can create. " "Only admin can override." msgstr "" "L'indicatore lato sistema per determinare il tipo di router che i tenant " "possono creare. Solo l'Admin può sovrascrivere." msgid "TCP Port used by Neutron metadata namespace proxy." msgstr "Porta TCP utilizzata dal proxy spazio dei nomi dei metadati Neutron." msgid "TCP Port used by Nova metadata server." msgstr "Porta TCP utilizzata dal server di metadati Nova." msgid "TTL for vxlan interface protocol packets." msgstr "Pacchetti del protocollo dell'interfaccia TTL per vxlan." #, python-format msgid "Tag %(tag)s could not be found." msgstr "Impossibile trovare il tag %(tag)s." #, python-format msgid "Tenant %(tenant_id)s not allowed to create %(resource)s on this network" msgstr "" "Tenant %(tenant_id)s non consentito per creare %(resource)s su questa rete" msgid "Tenant id for connecting to designate in admin context" msgstr "ID tenant per la connessione da designare nel contesto admin" msgid "Tenant name for connecting to designate in admin context" msgstr "Nome tenant per la connessione da designare nel contesto admin" msgid "Tenant network creation is not enabled." msgstr "La creazione della rete tenant non è consentita." msgid "Tenant-id was missing from quota request." msgstr "Tenant-id mancante dalla richiesta della quota." msgid "" "The 'gateway_external_network_id' option must be configured for this agent " "as Neutron has more than one external network." msgstr "" "L'opzione 'gateway_external_network_id' deve essere configurata per questo " "agent poiché Neutron ha più di una rete esterna." msgid "" "The DHCP agent will resync its state with Neutron to recover from any " "transient notification or RPC errors. The interval is number of seconds " "between attempts." msgstr "" "L'agent DHCP risincronizzerà il suo stato con Neutron per il ripristino da " "qualsiasi notifica transitoria o errore RPC. L'intervallo è il numero di " "secondi tra i tentativi." msgid "" "The DHCP server can assist with providing metadata support on isolated " "networks. Setting this value to True will cause the DHCP server to append " "specific host routes to the DHCP request. The metadata service will only be " "activated when the subnet does not contain any router port. The guest " "instance must be configured to request host routes via DHCP (Option 121). " "This option doesn't have any effect when force_metadata is set to True." msgstr "" "Il server DHCP può fornire il supporto di metadati nelle reti isolate. " "L'impostazione di questo valore su True farà in modo che il server DHCP " "aggiunga instradamenti host specifici alla richiesta DHCP. Il servizio di " "metadati verrà attivato solo quando la sottorete non contiene porte del " "router. L'istanza guest deve essere configurata per richiedere gli " "instradamenti host mediante DHCP (Opzione 121). Questa opzione non ha alcun " "effetto quando force_metadata è impostato su True." msgid "The UDP port to use for VXLAN tunnels." msgstr "La porta UDP da utilizzare per i tunnel VXLAN." #, python-format msgid "" "The address allocation request could not be satisfied because: %(reason)s" msgstr "" "La richiesta di assegnazione dell'indirizzo non può essere soddisfatta " "perché: %(reason)s" msgid "The advertisement interval in seconds" msgstr "L'intervallo di annuncio in secondi" #, python-format msgid "The allocation pool %(pool)s is not valid." msgstr "Il pool di allocazione %(pool)s non è valido." #, python-format msgid "" "The allocation pool %(pool)s spans beyond the subnet cidr %(subnet_cidr)s." msgstr "" "Il pool di allocazione %(pool)s si estende oltre il cidr della sottorete " "%(subnet_cidr)s." msgid "" "The base MAC address Neutron will use for VIFs. The first 3 octets will " "remain unchanged. If the 4th octet is not 00, it will also be used. The " "others will be randomly generated." msgstr "" "L'indirizzo MAC di base utilizzato da Neutron per i VIF. I primi 3 ottetti " "rimangono inalterati. Se il quarto ottetto non è 00, potrà anche essere " "utilizzato. Gli altri vengono generati casualmente. " msgid "" "The base mac address used for unique DVR instances by Neutron. The first 3 " "octets will remain unchanged. If the 4th octet is not 00, it will also be " "used. The others will be randomly generated. The 'dvr_base_mac' *must* be " "different from 'base_mac' to avoid mixing them up with MAC's allocated for " "tenant ports. A 4 octet example would be dvr_base_mac = fa:16:3f:4f:00:00. " "The default is 3 octet" msgstr "" "L'indirizzo mac di base utilizzato per istanze DVR univoche da Neutron. I " "primi 3 ottetti rimangono inalterati. Se il quarto ottetto non è 00, potrà " "anche essere utilizzato. Gli altri vengono generati casualmente. " "'dvr_base_mac' *deve* essere diverso da 'base_mac' per evitare la confusione " "con i MAC assegnati per le porte titolari. Un esempio di 4 ottetti è " "dvr_base_mac = fa:16:3f:4f:00:00. Il valore predefinito è 3 ottetti" msgid "The core plugin Neutron will use" msgstr "Il plugin principale che Neutron utilizzerà" msgid "The driver used to manage the DHCP server." msgstr "Il driver utilizzato per gestire il server DHCP." msgid "The driver used to manage the virtual interface." msgstr "Il driver utilizzato per gestire l'interfaccia virtuale." msgid "" "The email address to be used when creating PTR zones. If not specified, the " "email address will be admin@" msgstr "" "L'indirizzo email da utilizzare durante la creazione di zone PTR. Se non " "specificato, l'indirizzo email sarà admin@" #, python-format msgid "" "The following device_id %(device_id)s is not owned by your tenant or matches " "another tenants router." msgstr "" "Il seguente device_id %(device_id)s non è posseduto dal proprio tenant o " "corrisponde ad un altro router tenant." msgid "The interface for interacting with the OVSDB" msgstr "L'interfaccia per l'interazione con OVSDB" msgid "" "The maximum number of items returned in a single response, value was " "'infinite' or negative integer means no limit" msgstr "" "Il numero massimo di elementi restituiti in una singola risposta, il valore " "era 'infinite' oppure un numero intero negativo che indica nessun limite" #, python-format msgid "" "The network %(network_id)s has been already hosted by the DHCP Agent " "%(agent_id)s." msgstr "" "La rete %(network_id)s è stata già ospitata dall'agent DHCP %(agent_id)s." #, python-format msgid "" "The network %(network_id)s is not hosted by the DHCP agent %(agent_id)s." msgstr "" "La rete %(network_id)s non è stata ospitata dall'agent DHCP %(agent_id)s." msgid "" "The network type to use when creating the HA network for an HA router. By " "default or if empty, the first 'tenant_network_types' is used. This is " "helpful when the VRRP traffic should use a specific network which is not the " "default one." msgstr "" "Il tipo di rete da utilizzare quando si crea la rete HA per un router HA. " "Per impostazione predefinita o se vuoto, è utilizzato il primo " "'tenant_network_types'. Ciò è utile quando il traffico VRRP deve utilizzare " "una rete specifica che non è quella predefinita." msgid "" "The number of seconds the agent will wait between polling for local device " "changes." msgstr "" "Il numero di secondi in l'agent attenderà tra i polling per le modifiche " "dell'unità locale." msgid "" "The number of seconds to wait before respawning the ovsdb monitor after " "losing communication with it." msgstr "" "Il numero di secondi di attesa prima di generare nuovamente il monitor ovsdb " "dopo la perdita di comunicazione." msgid "The number of sort_keys and sort_dirs must be same" msgstr "Il numero di sort_keys e sort_dirs deve essere uguale" msgid "" "The path for API extensions. Note that this can be a colon-separated list of " "paths. For example: api_extensions_path = extensions:/path/to/more/exts:/" "even/more/exts. The __path__ of neutron.extensions is appended to this, so " "if your extensions are in there you don't need to specify them here." msgstr "" "Il percorso per le estensioni API. Può essere un elenco di percorsi separato " "dai due punti. Ad esempio: api_extensions_path = extensions:/path/to/more/" "exts:/even/more/exts. Il __percorso__ di neutron.extensions è aggiunto a " "tale percorso, per cui, se le estensioni si trovano nel percorso non è " "necessario specificarle." msgid "The physical network name with which the HA network can be created." msgstr "Il nome della rete fisica con cui può essere creata la rete HA." #, python-format msgid "The port '%s' was deleted" msgstr "La porta '%s' è stata eliminata" msgid "The port to bind to" msgstr "La porta a cui collegarsi" #, python-format msgid "The requested content type %s is invalid." msgstr "Il tipo di contenuto richiesto %s non è valido." msgid "The resource could not be found." msgstr "Impossibile trovare la risorsa." #, python-format msgid "" "The router %(router_id)s has been already hosted by the L3 Agent " "%(agent_id)s." msgstr "" "Il router %(router_id)s è stato già ospitato dall'agent L3 %(agent_id)s." msgid "" "The server has either erred or is incapable of performing the requested " "operation." msgstr "" "Il server è in errore o non è capace di eseguire l'operazione richiesta." msgid "The service plugins Neutron will use" msgstr "Il plugin del servizio che Neutron utilizzerà" #, python-format msgid "The subnet request could not be satisfied because: %(reason)s" msgstr "" "La richiesta della sottorete non può essere soddisfatta perché: %(reason)s" #, python-format msgid "The subproject to execute the command against. Can be one of: '%s'." msgstr "Il sottoprogetto su cui eseguire il comando. Può essere uno di: '%s'." msgid "The type of authentication to use" msgstr "Il tipo di autenticazione da utilizzare" msgid "" "There are routers attached to this network that depend on this policy for " "access." msgstr "" "Sono presenti router collegati a questa rete che dipendono da questa " "politica per l'accesso." msgid "" "Timeout in seconds to wait for a single OpenFlow request. Used only for " "'native' driver." msgstr "" "Timeout in secondi da attendere per una singola richiesta OpenFlow. " "Utilizzato solo per driver 'native'." msgid "" "Timeout in seconds to wait for the local switch connecting the controller. " "Used only for 'native' driver." msgstr "" "Timeout in secondi da attendere per la connessione dello switch locale al " "controller. Utilizzato solo per driver 'native'." msgid "" "Too long prefix provided. New name would exceed given length for an " "interface name." msgstr "" "Fornito prefisso troppo lungo. Il nuovo nome supererebbe la lunghezza " "specificata per un nome di interfaccia." msgid "" "True to delete all ports on all the OpenvSwitch bridges. False to delete " "ports created by Neutron on integration and external network bridges." msgstr "" "True per eliminare tutte le porte su tutti i bridge OpenvSwitch. False per " "eliminare le porte create da Neutron nell'integrazione e i bridge di reti " "esterne." msgid "Tunnel IP value needed by the ML2 plugin" msgstr "Valore IP tunnel IP richiesto dal plugin ML2" msgid "Tunnel bridge to use." msgstr "Bridge del tunnel da utilizzare." msgid "" "Type of the nova endpoint to use. This endpoint will be looked up in the " "keystone catalog and should be one of public, internal or admin." msgstr "" "Tipo di endpoint nova da utilizzare. Questo endpoint verrà ricercato nel " "catalogo keystone e deve essere public, internal o admin." msgid "URL for connecting to designate" msgstr "URL per la connessione da designare" msgid "URL to database" msgstr "URL per il database" #, python-format msgid "Unable to access %s" msgstr "Impossibile accedere a %s" #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, maximum allowed " "prefix is %(max_prefixlen)s." msgstr "" "Impossibile assegnare la sottorete con lunghezza del prefisso %(prefixlen)s, " "il prefisso massimo consentito è %(max_prefixlen)s." #, python-format msgid "" "Unable to allocate subnet with prefix length %(prefixlen)s, minimum allowed " "prefix is %(min_prefixlen)s." msgstr "" "Impossibile assegnare la sottorete con lunghezza del prefisso %(prefixlen)s, " "il prefisso minimo consentito è %(min_prefixlen)s." #, python-format msgid "Unable to calculate %(address_type)s address because of:%(reason)s" msgstr "" "Impossibile calcolare l'indirizzo %(address_type)s a causa di: %(reason)s" #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of DNS " "nameservers exceeds the limit %(quota)s." msgstr "" "Impossibile completare l'operazione per %(subnet_id)s. Il numero di server " "nome DNS supera il limite %(quota)s." #, python-format msgid "" "Unable to complete operation for %(subnet_id)s. The number of host routes " "exceeds the limit %(quota)s." msgstr "" "Impossibile completare l'operazione per %(subnet_id)s. Il numero di route " "host supera il limite %(quota)s." #, python-format msgid "Unable to convert value in %s" msgstr "Impossibile convertire il valore in %s" msgid "Unable to create the Agent Gateway Port" msgstr "Impossibile creare la porta gateway agent" msgid "Unable to create the SNAT Interface Port" msgstr "Impossibile creare la porta dell'interfaccia SNAT" #, python-format msgid "" "Unable to create the flat network. Physical network %(physical_network)s is " "in use." msgstr "" "Impossibile creare la rete flat. La rete fisica %(physical_network)s è in " "uso." msgid "" "Unable to create the network. No available network found in maximum allowed " "attempts." msgstr "" "Impossibile creare la rete. Non è stata trovata alcuna rete nel numero " "massimo di tentativi consentiti." #, python-format msgid "Unable to delete subnet pool: %(reason)s." msgstr "Impossibile eliminare il pool di sottorete: %(reason)s." #, python-format msgid "Unable to determine mac address for %s" msgstr "Impossibile determinare l'indirizzo mac per %s" #, python-format msgid "Unable to find '%s' in request body" msgstr "Impossibile trovare '%s' nel corpo della richiesta" #, python-format msgid "Unable to find IP address %(ip_address)s on subnet %(subnet_id)s" msgstr "" "Impossibile trovare l'indirizzo IP %(ip_address)s nella sottorete " "%(subnet_id)s" #, python-format msgid "Unable to find resource name in %s" msgstr "Impossibile trovare il nome risorsa in %s" #, python-format msgid "Unable to generate unique mac on network %(net_id)s." msgstr "Impossibile generare mac univoco sulla rete %(net_id)s." #, python-format msgid "" "Unable to identify a target field from:%s. Match should be in the form " "%%()s" msgstr "" "Impossibile identificare un campo di destinazione da:%s. La corrispondenza " "deve essere presente nel modulo %%()s" msgid "Unable to provide external connectivity" msgstr "Impossibile fornire la connettività esterna" msgid "Unable to provide tenant private network" msgstr "Impossibile fornire la rete privata del tenant" #, python-format msgid "" "Unable to reconfigure sharing settings for network %(network)s. Multiple " "tenants are using it." msgstr "" "Impossibile riconfigurare le impostazioni di condivisione per la rete " "%(network)s. Più tenants la stanno utilizzando." #, python-format msgid "" "Unable to verify match:%(match)s as the parent resource: %(res)s was not " "found" msgstr "" "Impossibile verificare la corrispondenza:%(match)s come risorsa parent: " "%(res)s non è stata trovata" #, python-format msgid "Unexpected label for script %(script_name)s: %(labels)s" msgstr "Etichetta imprevista per lo script %(script_name)s: %(labels)s" #, python-format msgid "Unexpected number of alembic branch points: %(branchpoints)s" msgstr "Numero non previsto di punti di ramo alembic: %(branchpoints)s" #, python-format msgid "Unexpected response code: %s" msgstr "Imprevisto codice di risposta: %s" #, python-format msgid "Unexpected response: %s" msgstr "Risposta imprevista: %s" #, python-format msgid "Unit name '%(unit)s' is not valid." msgstr "Il nome unità '%(unit)s' non è valido." #, python-format msgid "Unknown address type %(address_type)s" msgstr "Tipo di indirizzo sconosciuto %(address_type)s" #, python-format msgid "Unknown attribute '%s'." msgstr "Attributo sconosciuto '%s'." #, python-format msgid "Unknown chain: %r" msgstr "Catena sconosciuta: %r" #, python-format msgid "Unknown network type %(network_type)s." msgstr "Tipo di rete %(network_type)s sconosciuto." #, python-format msgid "Unknown quota resources %(unknown)s." msgstr "Risorse quota sconosciute %(unknown)s." msgid "Unmapped error" msgstr "Errore non associato" msgid "Unrecognized action" msgstr "Azione non riconosciuta" #, python-format msgid "Unrecognized attribute(s) '%s'" msgstr "Attributi non riconosciuti '%s'" msgid "Unrecognized field" msgstr "Campo non riconosciuto" msgid "Unsupported Content-Type" msgstr "Tipo-contenuto non supportato" #, python-format msgid "Unsupported network type %(net_type)s." msgstr "Tipo di rete non supportato %(net_type)s." #, python-format msgid "Unsupported port state: %(port_state)s." msgstr "Stato porta non supportato: %(port_state)s." msgid "Unsupported request type" msgstr "Tipo di richiesta non supportato" msgid "Updating default security group not allowed." msgstr "L'aggiornamento del gruppo di sicurezza predefinito non è consentito." msgid "" "Use ML2 l2population mechanism driver to learn remote MAC and IPs and " "improve tunnel scalability." msgstr "" "utilizzare il driver del meccanismo ML2 l2population per conoscere MAC e IP " "remoti e migliorare la scalabilità del tunnel." msgid "Use broadcast in DHCP replies." msgstr "Utilizzare broadcast nelle risposte DHCP." msgid "Use either --delta or relative revision, not both" msgstr "Utilizzare --revisione delta o relativa, non entrambe" msgid "" "Use ipset to speed-up the iptables based security groups. Enabling ipset " "support requires that ipset is installed on L2 agent node." msgstr "" "Utilizzare ipset per velocizzare i gruppi di sicurezza basati su iptable. " "L'abilitazione del supporto ipset richiede che ipset sia installato sul nodo " "dell'agent L2." msgid "" "Use the root helper when listing the namespaces on a system. This may not be " "required depending on the security configuration. If the root helper is not " "required, set this to False for a performance improvement." msgstr "" "Utilizzare il root helper per visualizzare gli spazi dei nomi in un sistema " "operativo. Ciò potrebbe non essere richiesto in base alla configurazione di " "sicurezza. Se il root helper non è richiesto, impostare su False per un " "miglioramento delle prestazioni." msgid "" "Use veths instead of patch ports to interconnect the integration bridge to " "physical networks. Support kernel without Open vSwitch patch port support so " "long as it is set to True." msgstr "" "Utilizzare veths invece delle porte patch per interconnettere il bridge di " "integrazione alle reti fisiche. Supporta kernel senza supporto per porta " "patch Open vSwitch se impostato su True." msgid "" "User (uid or name) running metadata proxy after its initialization (if " "empty: agent effective user)." msgstr "" "Utente (uid o nome) che esegue il proxy di metadati dopo la relativa " "inizializzazione (se vuoto: utente operativo dell'agent)." msgid "User (uid or name) running this process after its initialization" msgstr "" "Utente (uid o name) che esegue questo processo dopo la relativa " "inizializzazione" msgid "Username for connecting to designate in admin context" msgstr "Nome utente per la connessione da designare nel contesto admin" msgid "VRRP authentication password" msgstr "Password di autenticazione VRRP" msgid "VRRP authentication type" msgstr "Tipo di autenticazione VRRP" msgid "VXLAN network unsupported." msgstr "Rete VXLAN non supportata." msgid "" "Value of host kernel tick rate (hz) for calculating minimum burst value in " "bandwidth limit rules for a port with QoS. See kernel configuration file for " "HZ value and tc-tbf manual for more information." msgstr "" "Il valore della velocità di tick (hz) del kernel host per il calcolo del " "valore burst minimo nelle regole del limite di larghezza di banda per una " "porta con QoS. Vedere il file di configurazione kernel per il valore HZ e il " "manuale tc-tbf per ulteriori informazioni." msgid "" "Value of latency (ms) for calculating size of queue for a port with QoS. See " "tc-tbf manual for more information." msgstr "" "Il valore di latenza (ms) per il calcolo della dimensione della coda per una " "porta con QoS. Per ulteriori informazioni, vedere il manuale tc-tbf." msgid "" "When external_network_bridge is set, each L3 agent can be associated with no " "more than one external network. This value should be set to the UUID of that " "external network. To allow L3 agent support multiple external networks, both " "the external_network_bridge and gateway_external_network_id must be left " "empty." msgstr "" "Quando external_network_bridge è impostato, ciascun agent L3 può essere " "associato con non più di una rete esterna. Questo valore non deve essere " "impostato sull'UUID della rete esterna. Per consentire all'agent L3 di " "supportare più reti esterne, external_network_bridge e " "gateway_external_network_id devono essere lasciati vuoti." msgid "" "When proxying metadata requests, Neutron signs the Instance-ID header with a " "shared secret to prevent spoofing. You may select any string for a secret, " "but it must match here and in the configuration used by the Nova Metadata " "Server. NOTE: Nova uses the same config key, but in [neutron] section." msgstr "" "Quando si trasferiscono richieste di metadati, Neutron firma l'intestazione " "Instance-ID con un segreto condiviso per evitare lo spoofing. È possibile " "selezionare una qualsiasi stringa per un segreto ma deve corrispondere qui e " "nella configurazione utilizzata da Nova Metadata Server. NOTA: Nova utilizza " "la stessa chiave di configurazione, ma nella sezione [neutron]." msgid "" "Where to store Neutron state files. This directory must be writable by the " "agent." msgstr "" "Dove memorizzare i file di stato Neutron. Questa directory deve essere " "scrivibile dall'agent." msgid "" "With IPv6, the network used for the external gateway does not need to have " "an associated subnet, since the automatically assigned link-local address " "(LLA) can be used. However, an IPv6 gateway address is needed for use as the " "next-hop for the default route. If no IPv6 gateway address is configured " "here, (and only then) the neutron router will be configured to get its " "default route from router advertisements (RAs) from the upstream router; in " "which case the upstream router must also be configured to send these RAs. " "The ipv6_gateway, when configured, should be the LLA of the interface on the " "upstream router. If a next-hop using a global unique address (GUA) is " "desired, it needs to be done via a subnet allocated to the network and not " "through this parameter. " msgstr "" "Con IPv6, non è necessario che la rete utilizzata per il gateway esterno " "disponga di una sottorete associata, poiché verrà utilizzato il LLA (link-" "local address) assegnato automaticamente. Tuttavia, è necessario un " "indirizzo gateway IPv6 per l'utilizzo come successivo hop per " "l'instradamento predefinito. Se qui non è configuratonessun indirizzo " "gateway Ipv6 (e solo poi) verrà configurato il router Neutron per ottenere " "il relativo instradamento predefinito da RA (Router Advertisement) dal " "router upstream; in tal caso il router upstream deve essere anche " "configuratoper inviare questi RA. Ipv6_gateway, quando configurato, " "deveessere il LLA dell'interfaccia sul router upstream. Se si desidera un " "hop successivo che utilizzi un GUA (Global Uunique Address) è necessario " "ottenerlo mediante una sottorete assegnata alla rete e non attraverso questo " "parametro." msgid "You must implement __call__" msgstr "È necessario implementare __call__" msgid "" "You must provide a config file for bridge - either --config-file or " "env[NEUTRON_TEST_CONFIG_FILE]" msgstr "" "È necessario fornire un file di configurazione per il bridge - --config-file " "o env[NEUTRON_TEST_CONFIG_FILE]" msgid "You must provide a revision or relative delta" msgstr "È necessario fornire una revisione o delta relativo" msgid "a subnetpool must be specified in the absence of a cidr" msgstr "un pool di sottorete deve essere specificato in assenza di un cidr" msgid "add_ha_port cannot be called inside of a transaction." msgstr "add_ha_port non può essere richiamato all'interno di una transazione." msgid "allocation_pools allowed only for specific subnet requests." msgstr "" "allocation_pools consentita solo per specifiche richieste della sottorete." msgid "allocation_pools are not in the subnet" msgstr "allocation_pools non presenti nella sottorete" msgid "allocation_pools use the wrong ip version" msgstr "allocation_pools utilizzano la versione IP errata" msgid "already a synthetic attribute" msgstr "è già presente un attributo synthetic" msgid "binding:profile value too large" msgstr "valore binding:profile troppo esteso" #, python-format msgid "cannot perform %(event)s due to %(reason)s" msgstr "impossibile esegure %(event)s a causa di %(reason)s" msgid "cidr and prefixlen must not be supplied together" msgstr "non devono essere forniti insieme cidr e prefixlen" #, python-format msgid "dhcp_agents_per_network must be >= 1. '%s' is invalid." msgstr "dhcp_agents_per_network deve essere >= 1. '%s' non è valido." msgid "dns_domain cannot be specified without a dns_name" msgstr "dns_domain non può essere specificato senza un dns_name" msgid "dns_name cannot be specified without a dns_domain" msgstr "dns_name non può essere specificato senza un dns_domain" msgid "fixed_ip_address cannot be specified without a port_id" msgstr "Impossibile specificare un fixed_ip_address senza un porta_id" #, python-format msgid "has device owner %s" msgstr "ha il proprietario del dispositivo %s" msgid "in use" msgstr "in uso" #, python-format msgid "ip command failed on device %(dev_name)s: %(reason)s" msgstr "comando ip non riuscito sul dispositivo %(dev_name)s: %(reason)s" #, python-format msgid "ip command failed: %(reason)s" msgstr "Comando IP non riuscito: %(reason)s" #, python-format msgid "ip link capability %(capability)s is not supported" msgstr "La funzione ip link %(capability)s non è supportata" #, python-format msgid "ip link command is not supported: %(reason)s" msgstr "Il comando ip link non è supportato: %(reason)s" msgid "ip_version must be specified in the absence of cidr and subnetpool_id" msgstr "è necessario specificare ip_version in assenza di cidr e subnetpool_id" msgid "ipv6_address_mode is not valid when ip_version is 4" msgstr "ipv6_address_mode non è valida quando ip_version è 4" msgid "ipv6_ra_mode is not valid when ip_version is 4" msgstr "ipv6_ra_mode non è valida quando ip_version è 4" #, python-format msgid "" "ipv6_ra_mode set to '%(ra_mode)s' with ipv6_address_mode set to " "'%(addr_mode)s' is not valid. If both attributes are set, they must be the " "same value" msgstr "" "ipv6_ra_mode impostato su '%(ra_mode)s' con ipv6_address_mode impostato su " "'%(addr_mode)s' non è valido. Se sono impostati entrambi gli attributi, essi " "devono avere lo stesso valore" msgid "mac address update" msgstr "aggiornamento indirizzo mac" msgid "must provide exactly 2 arguments - cidr and MAC" msgstr "è necessario fornire esattamente 2 argomenti - cidr e MAC" msgid "network_type required" msgstr "network_type obbligatorio" #, python-format msgid "network_type value '%s' not supported" msgstr "Valore network_type '%s' non supportato" msgid "new subnet" msgstr "nuova sottorete" #, python-format msgid "physical_network '%s' unknown for flat provider network" msgstr "physical_network '%s' sconosciuta per rete flat del provider" msgid "physical_network required for flat provider network" msgstr "physical_network richiesta per rete flat del provider" #, python-format msgid "provider:physical_network specified for %s network" msgstr "provider:physical_network specificata per la rete %s" msgid "respawn_interval must be >= 0 if provided." msgstr "respawn_interval deve essere >= 0 se fornito." #, python-format msgid "segmentation_id out of range (%(min)s through %(max)s)" msgstr "segmentation_id fuori dall'intervallo (da %(min)s a %(max)s)" msgid "segmentation_id requires physical_network for VLAN provider network" msgstr "" "segmentation_id richiede physical_network per la rete del provider VLAN" msgid "shared attribute switching to synthetic" msgstr "passaggio dell'attributo condiviso su synthetic" #, python-format msgid "" "subnetpool %(subnetpool_id)s cannot be updated when associated with shared " "address scope %(address_scope_id)s" msgstr "" "Il pool di sottorete %(subnetpool_id)s non può essere aggiornato quando " "associato all'ambito indirizzo condiviso %(address_scope_id)s" msgid "subnetpool_id and use_default_subnetpool cannot both be specified" msgstr "" "subnetpool_id e use_default_subnetpool non possono essere entrambi " "specificati" msgid "the nexthop is not connected with router" msgstr "l'hop successivo non è connesso al router" msgid "the nexthop is used by router" msgstr "l'hop successivo è utilizzato dal router" neutron-12.1.1/neutron/agent/0000775000175000017500000000000013553660156016112 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/agent_extension.py0000664000175000017500000000345013553660047021657 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import six @six.add_metaclass(abc.ABCMeta) class AgentExtension(object): """Define stable abstract interface for agent extensions. An agent extension extends the agent core functionality. """ @abc.abstractmethod def initialize(self, connection, driver_type): """Perform agent core resource extension initialization. :param connection: RPC connection that can be reused by the extension to define its RPC endpoints :param driver_type: a string that defines the agent type to the extension. Can be used to choose the right backend implementation. Called after all extensions have been loaded. No resource (port, policy, router, etc.) handling will be called before this method. """ def consume_api(self, agent_api): """Consume the AgentAPI instance from the AgentExtensionsManager. Allows an extension to gain access to resources internal to the neutron agent and otherwise unavailable to the extension. Examples of such resources include bridges, ports, and routers. :param agent_api: An instance of an agent-specific API. """ neutron-12.1.1/neutron/agent/windows/0000775000175000017500000000000013553660156017604 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/windows/utils.py0000664000175000017500000001234413553660047021321 0ustar zuulzuul00000000000000# Copyright 2015 Cloudbase Solutions. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import io import os import eventlet from eventlet import tpool from neutron_lib.utils import helpers from oslo_log import log as logging from oslo_utils import encodeutils import six from neutron._i18n import _ from neutron.common import exceptions if os.name == 'nt': import wmi LOG = logging.getLogger(__name__) # subprocess.Popen will spawn two threads consuming stdout/stderr when passing # data through stdin. We need to make sure that *native* threads will be used # as pipes are blocking on Windows. subprocess = eventlet.patcher.original('subprocess') subprocess.threading = eventlet.patcher.original('threading') def create_process(cmd, run_as_root=False, addl_env=None, tpool_proxy=True): cmd = list(map(str, cmd)) LOG.debug("Running command: %s", cmd) env = os.environ.copy() if addl_env: env.update(addl_env) popen = subprocess.Popen obj = popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, preexec_fn=None, close_fds=False) if tpool_proxy and eventlet.getcurrent().parent: # If we intend to access the process streams, we need to wrap this # in a tpool proxy object, avoding blocking other greenthreads. # # The 'file' type is not available on Python 3.x. file_type = getattr(six.moves.builtins, 'file', io.IOBase) obj = tpool.Proxy(obj, autowrap=(file_type, )) return obj, cmd def _get_wmi_process(pid): if not pid: return None conn = wmi.WMI() processes = conn.Win32_Process(ProcessId=pid) if processes: return processes[0] return None def kill_process(pid, signal, run_as_root=False): """Kill the process with the given pid using the given signal.""" process = _get_wmi_process(pid) try: if process: process.Terminate() except Exception: if _get_wmi_process(pid): raise def execute(cmd, process_input=None, addl_env=None, check_exit_code=True, return_stderr=False, log_fail_as_error=True, extra_ok_codes=None, run_as_root=False, do_decode=True): if process_input is not None: _process_input = encodeutils.to_utf8(process_input) else: _process_input = None obj, cmd = create_process(cmd, addl_env=addl_env, tpool_proxy=False) _stdout, _stderr = avoid_blocking_call(obj.communicate, _process_input) obj.stdin.close() _stdout = helpers.safe_decode_utf8(_stdout) _stderr = helpers.safe_decode_utf8(_stderr) m = _("\nCommand: %(cmd)s\nExit code: %(code)s\nStdin: %(stdin)s\n" "Stdout: %(stdout)s\nStderr: %(stderr)s") % \ {'cmd': cmd, 'code': obj.returncode, 'stdin': process_input or '', 'stdout': _stdout, 'stderr': _stderr} extra_ok_codes = extra_ok_codes or [] if obj.returncode and obj.returncode in extra_ok_codes: obj.returncode = None log_msg = m.strip().replace('\n', '; ') if obj.returncode and log_fail_as_error: LOG.error(log_msg) else: LOG.debug(log_msg) if obj.returncode and check_exit_code: raise exceptions.ProcessExecutionError(m, returncode=obj.returncode) return (_stdout, _stderr) if return_stderr else _stdout def avoid_blocking_call(f, *args, **kwargs): """Ensure that the method "f" will not block other greenthreads. Performs the call to the function "f" received as parameter in a different thread using tpool.execute when called from a greenthread. This will ensure that the function "f" will not block other greenthreads. If not called from a greenthread, it will invoke the function "f" directly. The function "f" will receive as parameters the arguments "args" and keyword arguments "kwargs". """ # Note that eventlet.getcurrent will always return a greenlet object. # In case of a greenthread, the parent greenlet will always be the hub # loop greenlet. if eventlet.getcurrent().parent: return tpool.execute(f, *args, **kwargs) else: return f(*args, **kwargs) def get_root_helper_child_pid(pid, expected_cmd, run_as_root=False): # We don't use a root helper on Windows. return str(pid) def process_is_running(pid): """Find if the given PID is running in the system.""" return _get_wmi_process(pid) is not None def pid_invoked_with_cmdline(pid, expected_cmd): process = _get_wmi_process(pid) if not process: return False command = process.CommandLine return command == " ".join(expected_cmd) neutron-12.1.1/neutron/agent/windows/__init__.py0000664000175000017500000000000013553660046021701 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/windows/polling.py0000664000175000017500000000175113553660047021625 0ustar zuulzuul00000000000000# Copyright 2015 Cloudbase Solutions. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib from neutron.agent.common import base_polling @contextlib.contextmanager def get_polling_manager(minimize_polling, ovsdb_monitor_respawn_interval): pm = base_polling.AlwaysPoll() yield pm # TODO(atuvenie): make this manager inherit from # that fully fledged polling manager interface class InterfacePollingMinimizer(object): pass neutron-12.1.1/neutron/agent/windows/ip_lib.py0000664000175000017500000000467313553660047021425 0ustar zuulzuul00000000000000# Copyright 2016 Cloudbase Solutions. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netifaces from oslo_log import log as logging LOG = logging.getLogger(__name__) class IPWrapper(object): def get_device_by_ip(self, ip): if not ip: return for device in self.get_devices(): if device.device_has_ip(ip): return device def get_devices(self): try: return [IPDevice(iface) for iface in netifaces.interfaces()] except (OSError, MemoryError): LOG.error("Failed to get network interfaces.") return [] class IPDevice(object): def __init__(self, name): self.name = name self.link = IPLink(self) def read_ifaddresses(self): try: device_addresses = netifaces.ifaddresses(self.name) except ValueError: LOG.error("The device does not exist on the system: %s.", self.name) return except OSError: LOG.error("Failed to get interface addresses: %s.", self.name) return return device_addresses def device_has_ip(self, ip): device_addresses = self.read_ifaddresses() if device_addresses is None: return False addresses = [ip_addr['addr'] for ip_addr in device_addresses.get(netifaces.AF_INET, [])] return ip in addresses class IPLink(object): def __init__(self, parent): self._parent = parent @property def address(self): device_addresses = self._parent.read_ifaddresses() if device_addresses is None: return False return [eth_addr['addr'] for eth_addr in device_addresses.get(netifaces.AF_LINK, [])] def add_namespace_to_cmd(cmd, namespace=None): """Add an optional namespace to the command.""" return cmd neutron-12.1.1/neutron/agent/metadata_agent.py0000664000175000017500000000264713553660046021431 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from oslo_config import cfg from oslo_log import log as logging from neutron.agent.metadata import agent from neutron.common import cache_utils as cache from neutron.common import config from neutron.common import utils from neutron.conf.agent import common as agent_conf from neutron.conf.agent.metadata import config as meta LOG = logging.getLogger(__name__) def main(): meta.register_meta_conf_opts(meta.SHARED_OPTS) meta.register_meta_conf_opts(meta.UNIX_DOMAIN_METADATA_PROXY_OPTS) meta.register_meta_conf_opts(meta.METADATA_PROXY_HANDLER_OPTS) cache.register_oslo_configs(cfg.CONF) agent_conf.register_agent_state_opts_helper(cfg.CONF) config.init(sys.argv[1:]) config.setup_logging() utils.log_opt_values(LOG) proxy = agent.UnixDomainMetadataProxy(cfg.CONF) proxy.run() neutron-12.1.1/neutron/agent/linux/0000775000175000017500000000000013553660156017251 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/linux/openvswitch_firewall/0000775000175000017500000000000013553660156023507 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/linux/openvswitch_firewall/exceptions.py0000664000175000017500000000213113553660046026235 0ustar zuulzuul00000000000000# Copyright 2016, Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions from neutron._i18n import _ class OVSFWPortNotFound(exceptions.NeutronException): message = _("Port %(port_id)s is not managed by this agent.") class OVSFWTagNotFound(exceptions.NeutronException): message = _( "Cannot get tag for port %(port_name)s from its other_config: " "%(other_config)s") class OVSFWPortNotHandled(exceptions.NeutronException): message = ("Port %(port_id)s is not handled by the firewall.") neutron-12.1.1/neutron/agent/linux/openvswitch_firewall/rules.py0000664000175000017500000003247013553660047025220 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import netaddr from neutron_lib import constants as n_consts from neutron.agent import firewall from neutron.agent.linux.openvswitch_firewall import constants as ovsfw_consts from neutron.common import utils from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts CT_STATES = [ ovsfw_consts.OF_STATE_ESTABLISHED_NOT_REPLY, ovsfw_consts.OF_STATE_NEW_NOT_ESTABLISHED] FLOW_FIELD_FOR_IPVER_AND_DIRECTION = { (n_consts.IP_VERSION_4, firewall.EGRESS_DIRECTION): 'nw_dst', (n_consts.IP_VERSION_6, firewall.EGRESS_DIRECTION): 'ipv6_dst', (n_consts.IP_VERSION_4, firewall.INGRESS_DIRECTION): 'nw_src', (n_consts.IP_VERSION_6, firewall.INGRESS_DIRECTION): 'ipv6_src', } FORBIDDEN_PREFIXES = (n_consts.IPv4_ANY, n_consts.IPv6_ANY) def is_valid_prefix(ip_prefix): # IPv6 have multiple ways how to describe ::/0 network, converting to # IPNetwork and back to string unifies it return (ip_prefix and str(netaddr.IPNetwork(ip_prefix)) not in FORBIDDEN_PREFIXES) def _assert_mergeable_rules(rule_conj_list): """Assert a given (rule, conj_ids) list has mergeable rules. The given rules must be the same except for port_range_{min,max} differences. """ rule_tmpl = rule_conj_list[0][0].copy() rule_tmpl.pop('port_range_min', None) rule_tmpl.pop('port_range_max', None) for rule, conj_id in rule_conj_list[1:]: rule1 = rule.copy() rule1.pop('port_range_min', None) rule1.pop('port_range_max', None) if rule_tmpl != rule1: raise RuntimeError( "Incompatible SG rules detected: %(rule1)s and %(rule2)s. " "They cannot be merged. This should not happen." % {'rule1': rule_tmpl, 'rule2': rule}) def merge_common_rules(rule_conj_list): """Take a list of (rule, conj_id) and merge elements with the same rules. Return a list of (rule, conj_id_list). """ if len(rule_conj_list) == 1: rule, conj_id = rule_conj_list[0] return [(rule, [conj_id])] _assert_mergeable_rules(rule_conj_list) rule_conj_map = collections.defaultdict(list) for rule, conj_id in rule_conj_list: rule_conj_map[(rule.get('port_range_min'), rule.get('port_range_max'))].append(conj_id) result = [] rule_tmpl = rule_conj_list[0][0] rule_tmpl.pop('port_range_min', None) rule_tmpl.pop('port_range_max', None) for (port_min, port_max), conj_ids in rule_conj_map.items(): rule = rule_tmpl.copy() if port_min is not None: rule['port_range_min'] = port_min if port_max is not None: rule['port_range_max'] = port_max result.append((rule, conj_ids)) return result def _merge_port_ranges_helper(port_range_item): # Sort with 'port' but 'min' things must come first. port, m, dummy = port_range_item return port * 2 + (0 if m == 'min' else 1) def merge_port_ranges(rule_conj_list): """Take a list of (rule, conj_id) and transform into a list whose rules don't overlap. Return a list of (rule, conj_id_list). """ if len(rule_conj_list) == 1: rule, conj_id = rule_conj_list[0] return [(rule, [conj_id])] _assert_mergeable_rules(rule_conj_list) port_ranges = [] for rule, conj_id in rule_conj_list: port_ranges.append((rule.get('port_range_min', 1), 'min', conj_id)) port_ranges.append((rule.get('port_range_max', 65535), 'max', conj_id)) port_ranges.sort(key=_merge_port_ranges_helper) # The idea here is to scan the port_ranges list in an ascending order, # keeping active conjunction IDs and range in cur_conj and cur_range_min. # A 'min' port_ranges item means an addition to cur_conj, while a 'max' # item means a removal. result = [] rule_tmpl = rule_conj_list[0][0] cur_conj = {} cur_range_min = None for port, m, conj_id in port_ranges: if m == 'min': if conj_id in cur_conj: cur_conj[conj_id] += 1 continue if cur_conj and cur_range_min != port: rule = rule_tmpl.copy() rule['port_range_min'] = cur_range_min rule['port_range_max'] = port - 1 result.append((rule, list(cur_conj.keys()))) cur_range_min = port cur_conj[conj_id] = 1 else: if cur_conj[conj_id] > 1: cur_conj[conj_id] -= 1 continue if cur_range_min <= port: rule = rule_tmpl.copy() rule['port_range_min'] = cur_range_min rule['port_range_max'] = port result.append((rule, list(cur_conj.keys()))) # The next port range without 'port' starts from (port + 1) cur_range_min = port + 1 del cur_conj[conj_id] if (len(result) == 1 and result[0][0]['port_range_min'] == 1 and result[0][0]['port_range_max'] == 65535): del result[0][0]['port_range_min'] del result[0][0]['port_range_max'] return result def flow_priority_offset(rule, conjunction=False): """Calculate flow priority offset from rule. Whether the rule belongs to conjunction flows or not is decided upon existence of rule['remote_group_id'] but can be overridden to be True using the optional conjunction arg. """ conj_offset = 0 if 'remote_group_id' in rule or conjunction else 4 protocol = rule.get('protocol') if protocol is None: return conj_offset if protocol in [n_consts.PROTO_NUM_ICMP, n_consts.PROTO_NUM_IPV6_ICMP]: if 'port_range_min' not in rule: return conj_offset + 1 elif 'port_range_max' not in rule: return conj_offset + 2 return conj_offset + 3 def create_flows_from_rule_and_port(rule, port, conjunction=False): """Create flows from given args. For description of the optional conjunction arg, see flow_priority_offset. """ ethertype = rule['ethertype'] direction = rule['direction'] dst_ip_prefix = rule.get('dest_ip_prefix') src_ip_prefix = rule.get('source_ip_prefix') flow_template = { 'priority': 70 + flow_priority_offset(rule, conjunction), 'dl_type': ovsfw_consts.ethertype_to_dl_type_map[ethertype], 'reg_port': port.ofport, } if is_valid_prefix(dst_ip_prefix): flow_template[FLOW_FIELD_FOR_IPVER_AND_DIRECTION[( utils.get_ip_version(dst_ip_prefix), firewall.EGRESS_DIRECTION)] ] = dst_ip_prefix if is_valid_prefix(src_ip_prefix): flow_template[FLOW_FIELD_FOR_IPVER_AND_DIRECTION[( utils.get_ip_version(src_ip_prefix), firewall.INGRESS_DIRECTION)] ] = src_ip_prefix flows = create_protocol_flows(direction, flow_template, port, rule) return flows def populate_flow_common(direction, flow_template, port): """Initialize common flow fields.""" if direction == firewall.INGRESS_DIRECTION: flow_template['table'] = ovs_consts.RULES_INGRESS_TABLE flow_template['actions'] = "output:{:d}".format(port.ofport) elif direction == n_consts.EGRESS_DIRECTION: flow_template['table'] = ovs_consts.RULES_EGRESS_TABLE # Traffic can be both ingress and egress, check that no ingress rules # should be applied flow_template['actions'] = 'resubmit(,{:d})'.format( ovs_consts.ACCEPT_OR_INGRESS_TABLE) return flow_template def create_protocol_flows(direction, flow_template, port, rule): flow_template = populate_flow_common(direction, flow_template.copy(), port) protocol = rule.get('protocol') if protocol is not None: flow_template['nw_proto'] = protocol if protocol in [n_consts.PROTO_NUM_ICMP, n_consts.PROTO_NUM_IPV6_ICMP]: flows = create_icmp_flows(flow_template, rule) else: flows = create_port_range_flows(flow_template, rule) return flows or [flow_template] def create_port_range_flows(flow_template, rule): protocol = ovsfw_consts.REVERSE_IP_PROTOCOL_MAP_WITH_PORTS.get( rule.get('protocol')) if protocol is None: return [] flows = [] src_port_match = '{:s}_src'.format(protocol) src_port_min = rule.get('source_port_range_min') src_port_max = rule.get('source_port_range_max') dst_port_match = '{:s}_dst'.format(protocol) dst_port_min = rule.get('port_range_min') dst_port_max = rule.get('port_range_max') dst_port_range = [] if dst_port_min and dst_port_max: dst_port_range = utils.port_rule_masking(dst_port_min, dst_port_max) src_port_range = [] if src_port_min and src_port_max: src_port_range = utils.port_rule_masking(src_port_min, src_port_max) for port in src_port_range: flow = flow_template.copy() flow[src_port_match] = port if dst_port_range: for port in dst_port_range: dst_flow = flow.copy() dst_flow[dst_port_match] = port flows.append(dst_flow) else: flows.append(flow) else: for port in dst_port_range: flow = flow_template.copy() flow[dst_port_match] = port flows.append(flow) return flows def create_icmp_flows(flow_template, rule): icmp_type = rule.get('port_range_min') if icmp_type is None: return flow = flow_template.copy() flow['icmp_type'] = icmp_type icmp_code = rule.get('port_range_max') if icmp_code is not None: flow['icmp_code'] = icmp_code return [flow] def _flow_priority_offset_from_conj_id(conj_id): "Return a flow priority offset encoded in a conj_id." # A base conj_id, which is returned by ConjIdMap.get_conj_id, is a # multiple of 8, and we use 2 conj_ids per offset. return conj_id % 8 // 2 def create_flows_for_ip_address(ip_address, direction, ethertype, vlan_tag, conj_ids): """Create flows from a rule and an ip_address derived from remote_group_id """ # Group conj_ids per priority. conj_id_lists = [[] for i in range(4)] for conj_id in conj_ids: conj_id_lists[ _flow_priority_offset_from_conj_id(conj_id)].append(conj_id) ip_prefix = str(netaddr.IPNetwork(ip_address).cidr) flow_template = { 'dl_type': ovsfw_consts.ethertype_to_dl_type_map[ethertype], 'reg_net': vlan_tag, # needed for project separation } ip_ver = utils.get_ip_version(ip_prefix) if direction == firewall.EGRESS_DIRECTION: flow_template['table'] = ovs_consts.RULES_EGRESS_TABLE elif direction == firewall.INGRESS_DIRECTION: flow_template['table'] = ovs_consts.RULES_INGRESS_TABLE flow_template[FLOW_FIELD_FOR_IPVER_AND_DIRECTION[( ip_ver, direction)]] = ip_prefix result = [] for offset, conj_id_list in enumerate(conj_id_lists): if not conj_id_list: continue flow_template['priority'] = 70 + offset result.extend( substitute_conjunction_actions([flow_template], 1, conj_id_list)) return result def create_accept_flows(flow): flow['ct_state'] = CT_STATES[0] result = [flow.copy()] flow['ct_state'] = CT_STATES[1] if flow['table'] == ovs_consts.RULES_INGRESS_TABLE: flow['actions'] = ( 'ct(commit,zone=NXM_NX_REG{:d}[0..15]),{:s},' 'resubmit(,{:d})'.format( ovsfw_consts.REG_NET, flow['actions'], ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE) ) result.append(flow) return result def substitute_conjunction_actions(flows, dimension, conj_ids): result = [] for flow in flows: for i in range(2): new_flow = flow.copy() new_flow['ct_state'] = CT_STATES[i] new_flow['actions'] = ','.join( ["conjunction(%d,%d/2)" % (s + i, dimension) for s in conj_ids]) result.append(new_flow) return result def create_conj_flows(port, conj_id, direction, ethertype): """Generate "accept" flows for a given conjunction ID.""" flow_template = { 'priority': 70 + _flow_priority_offset_from_conj_id(conj_id), 'conj_id': conj_id, 'dl_type': ovsfw_consts.ethertype_to_dl_type_map[ethertype], # This reg_port matching is for delete_all_port_flows. # The matching is redundant as it has been done by # conjunction(...,2/2) flows and flows can be summarized # without this. 'reg_port': port.ofport, } flow_template = populate_flow_common(direction, flow_template, port) flows = create_accept_flows(flow_template) flows[1]['conj_id'] += 1 return flows neutron-12.1.1/neutron/agent/linux/openvswitch_firewall/constants.py0000664000175000017500000000316313553660047026077 0ustar zuulzuul00000000000000# Copyright 2015 # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron.common import constants as n_const OF_STATE_NOT_TRACKED = "-trk" OF_STATE_TRACKED = "+trk" OF_STATE_NEW_NOT_ESTABLISHED = "+new-est" OF_STATE_NOT_ESTABLISHED = "-est" OF_STATE_ESTABLISHED = "+est" OF_STATE_ESTABLISHED_NOT_REPLY = "+est-rel-rpl" OF_STATE_ESTABLISHED_REPLY = "+est-rel+rpl" OF_STATE_RELATED = "-new-est+rel-inv" OF_STATE_INVALID = "+trk+inv" OF_STATE_NEW = "+new" OF_STATE_NOT_REPLY_NOT_NEW = "-new-rpl" CT_MARK_NORMAL = '0x0' CT_MARK_INVALID = '0x1' REG_PORT = 5 REG_NET = 6 # for logging remote group rule REG_REMOTE_GROUP = 7 PROTOCOLS_WITH_PORTS = (constants.PROTO_NAME_SCTP, constants.PROTO_NAME_TCP, constants.PROTO_NAME_UDP) # Only map protocols that need special handling REVERSE_IP_PROTOCOL_MAP_WITH_PORTS = { constants.IP_PROTOCOL_MAP[proto]: proto for proto in PROTOCOLS_WITH_PORTS} ethertype_to_dl_type_map = { constants.IPv4: n_const.ETHERTYPE_IP, constants.IPv6: n_const.ETHERTYPE_IPV6, } neutron-12.1.1/neutron/agent/linux/openvswitch_firewall/iptables.py0000664000175000017500000000670613553660047025674 0ustar zuulzuul00000000000000# Copyright 2017 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.common import constants as n_const def get_device_port_name(port_id): return ('qvo' + port_id)[:n_const.LINUX_DEV_LEN] def get_iptables_driver_instance(): """Load hybrid iptables firewall driver.""" from neutron.agent.linux import iptables_firewall class HybridIptablesHelper( iptables_firewall.OVSHybridIptablesFirewallDriver): """Don't remove conntrack when removing iptables rules.""" def _remove_conntrack_entries_from_port_deleted(self, port): pass return HybridIptablesHelper() class Helper(object): """Helper to avoid loading firewall driver. The main purpose is to avoid loading iptables driver for cases where no ports have hybrid plugging on given node. The helper stores metadata for iptables cleanup into br-int ovsdb Bridge table. Specifically it checks for other_config['iptables_cleaned'] boolean value. """ HYBRID_PORT_PREFIX = 'qvo' CLEANED_METADATA = 'iptables_cleaned' def __init__(self, int_br): self.int_br = int_br self.hybrid_ports = None self.iptables_driver = None def load_driver_if_needed(self): self.hybrid_ports = self.get_hybrid_ports() if self.hybrid_ports and self.has_not_been_cleaned: self.iptables_driver = get_iptables_driver_instance() def get_hybrid_ports(self): """Return True if there is a port with hybrid plugging.""" return { port_name for port_name in self.int_br.get_port_name_list() if port_name.startswith(self.HYBRID_PORT_PREFIX)} def cleanup_port(self, port): if not self.iptables_driver: return device_name = get_device_port_name(port['device']) try: self.hybrid_ports.remove(device_name) except KeyError: # It's not a hybrid plugged port return # TODO(jlibosva): Optimize, add port to firewall without installing # iptables rules and then call remove from firewall self.iptables_driver.prepare_port_filter(port) self.iptables_driver.remove_port_filter(port) if not self.hybrid_ports: self.mark_as_cleaned() # Let GC remove iptables driver self.iptables_driver = None @property def has_not_been_cleaned(self): other_config = self.int_br.db_get_val( 'Bridge', self.int_br.br_name, 'other_config') return other_config.get(self.CLEANED_METADATA, '').lower() != 'true' def mark_as_cleaned(self): # TODO(jlibosva): Make it a single transaction other_config = self.int_br.db_get_val( 'Bridge', self.int_br.br_name, 'other_config') other_config[self.CLEANED_METADATA] = 'true' self.int_br.set_db_attribute( 'Bridge', self.int_br.br_name, 'other_config', other_config) neutron-12.1.1/neutron/agent/linux/openvswitch_firewall/firewall.py0000664000175000017500000013450513553660047025675 0ustar zuulzuul00000000000000# Copyright 2015 # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import contextlib import copy import eventlet import netaddr from neutron_lib.callbacks import events as callbacks_events from neutron_lib.callbacks import registry as callbacks_registry from neutron_lib.callbacks import resources as callbacks_resources from neutron_lib import constants as lib_const from oslo_config import cfg from oslo_log import log as logging from oslo_utils import netutils from neutron.agent import firewall from neutron.agent.linux.openvswitch_firewall import constants as ovsfw_consts from neutron.agent.linux.openvswitch_firewall import exceptions from neutron.agent.linux.openvswitch_firewall import iptables from neutron.agent.linux.openvswitch_firewall import rules from neutron.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts LOG = logging.getLogger(__name__) def _replace_register(flow_params, register_number, register_value): """Replace value from flows to given register number 'register_value' key in dictionary will be replaced by register number given by 'register_number' :param flow_params: Dictionary containing defined flows :param register_number: The number of register where value will be stored :param register_value: Key to be replaced by register number """ try: reg_port = flow_params[register_value] del flow_params[register_value] flow_params['reg{:d}'.format(register_number)] = reg_port except KeyError: pass def create_reg_numbers(flow_params): """Replace reg_(port|net) values with defined register numbers""" _replace_register(flow_params, ovsfw_consts.REG_PORT, 'reg_port') _replace_register(flow_params, ovsfw_consts.REG_NET, 'reg_net') _replace_register( flow_params, ovsfw_consts.REG_REMOTE_GROUP, 'reg_remote_group') def get_tag_from_other_config(bridge, port_name): """Return tag stored in OVSDB other_config metadata. :param bridge: OVSBridge instance where port is. :param port_name: Name of the port. :raises OVSFWTagNotFound: In case tag cannot be found in OVSDB. """ other_config = None try: other_config = bridge.db_get_val( 'Port', port_name, 'other_config') return int(other_config['tag']) except (KeyError, TypeError, ValueError): raise exceptions.OVSFWTagNotFound( port_name=port_name, other_config=other_config) class SecurityGroup(object): def __init__(self, id_): self.id = id_ self.raw_rules = [] self.remote_rules = [] self.members = {} self.ports = set() def update_rules(self, rules): """Separate raw and remote rules. If a rule has a protocol field, it is normalized to a number here in order to ease later processing. """ self.raw_rules = [] self.remote_rules = [] for rule in copy.deepcopy(rules): protocol = rule.get('protocol') if protocol is not None: if protocol.isdigit(): rule['protocol'] = int(protocol) elif (rule.get('ethertype') == lib_const.IPv6 and protocol == lib_const.PROTO_NAME_ICMP): rule['protocol'] = lib_const.PROTO_NUM_IPV6_ICMP else: rule['protocol'] = lib_const.IP_PROTOCOL_MAP.get( protocol, protocol) if 'remote_group_id' in rule: self.remote_rules.append(rule) else: self.raw_rules.append(rule) def get_ethertype_filtered_addresses(self, ethertype): return self.members.get(ethertype, []) class OFPort(object): def __init__(self, port_dict, ovs_port, vlan_tag): self.id = port_dict['device'] self.vlan_tag = vlan_tag self.mac = ovs_port.vif_mac self.lla_address = str(netutils.get_ipv6_addr_by_EUI64( lib_const.IPv6_LLA_PREFIX, self.mac)) self.ofport = ovs_port.ofport self.sec_groups = list() self.fixed_ips = port_dict.get('fixed_ips', []) self.neutron_port_dict = port_dict.copy() self.allowed_pairs_v4 = self._get_allowed_pairs(port_dict, version=4) self.allowed_pairs_v6 = self._get_allowed_pairs(port_dict, version=6) @staticmethod def _get_allowed_pairs(port_dict, version): aap_dict = port_dict.get('allowed_address_pairs', set()) return {(aap['mac_address'], aap['ip_address']) for aap in aap_dict if netaddr.IPNetwork(aap['ip_address']).version == version} @property def all_allowed_macs(self): macs = {item[0] for item in self.allowed_pairs_v4.union( self.allowed_pairs_v6)} macs.add(self.mac) return macs @property def ipv4_addresses(self): return [ip_addr for ip_addr in self.fixed_ips if netaddr.IPAddress(ip_addr).version == 4] @property def ipv6_addresses(self): return [ip_addr for ip_addr in self.fixed_ips if netaddr.IPAddress(ip_addr).version == 6] def update(self, port_dict): self.allowed_pairs_v4 = self._get_allowed_pairs(port_dict, version=4) self.allowed_pairs_v6 = self._get_allowed_pairs(port_dict, version=6) # Neighbour discovery uses LLA self.allowed_pairs_v6.add((self.mac, self.lla_address)) self.fixed_ips = port_dict.get('fixed_ips', []) self.neutron_port_dict = port_dict.copy() class SGPortMap(object): def __init__(self): self.ports = {} self.sec_groups = {} # Maps port_id to ofport number self.unfiltered = {} def get_sg(self, sg_id): return self.sec_groups.get(sg_id, None) def get_or_create_sg(self, sg_id): try: sec_group = self.sec_groups[sg_id] except KeyError: sec_group = SecurityGroup(sg_id) self.sec_groups[sg_id] = sec_group return sec_group def delete_sg(self, sg_id): del self.sec_groups[sg_id] def create_port(self, port, port_dict): self.ports[port.id] = port self.update_port(port, port_dict) def update_port(self, port, port_dict): for sec_group in self.sec_groups.values(): sec_group.ports.discard(port) port.sec_groups = [self.get_or_create_sg(sg_id) for sg_id in port_dict['security_groups']] for sec_group in port.sec_groups: sec_group.ports.add(port) port.update(port_dict) def remove_port(self, port): for sec_group in port.sec_groups: sec_group.ports.discard(port) del self.ports[port.id] def update_rules(self, sg_id, rules): sec_group = self.get_or_create_sg(sg_id) sec_group.update_rules(rules) def update_members(self, sg_id, members): sec_group = self.get_or_create_sg(sg_id) sec_group.members = members class ConjIdMap(object): """Handle conjunction ID allocations and deallocations.""" def __new__(cls): if not hasattr(cls, '_instance'): cls._instance = super(ConjIdMap, cls).__new__(cls) return cls._instance def __init__(self): self.id_map = collections.defaultdict(self._conj_id_factory) self.id_free = collections.deque() self.max_id = 0 def _conj_id_factory(self): # If there is any freed ID, use one. if self.id_free: return self.id_free.popleft() # Allocate new one. It must be divisible by 8. (See the next function.) self.max_id += 8 return self.max_id def get_conj_id(self, sg_id, remote_sg_id, direction, ethertype): """Return a conjunction ID specified by the arguments. Allocate one if necessary. The returned ID is divisible by 8, as there are 4 priority levels (see rules.flow_priority_offset) and 2 conjunction IDs are needed per priority. """ if direction not in [firewall.EGRESS_DIRECTION, firewall.INGRESS_DIRECTION]: raise ValueError("Invalid direction '%s'" % direction) if ethertype not in [lib_const.IPv4, lib_const.IPv6]: raise ValueError("Invalid ethertype '%s'" % ethertype) return self.id_map[(sg_id, remote_sg_id, direction, ethertype)] def delete_sg(self, sg_id): """Free all conj_ids associated with the sg_id and return a list of (remote_sg_id, conj_id), which are no longer in use. """ result = [] for k in list(self.id_map.keys()): if sg_id in k[0:2]: conj_id = self.id_map.pop(k) result.append((k[1], conj_id)) self.id_free.append(conj_id) return result class ConjIPFlowManager(object): """Manage conj_id allocation and remote securitygroups derived conjunction flows. Flows managed by this class is of form: nw_src=10.2.3.4,reg_net=0xf00 actions=conjunction(123,1/2) These flows are managed per network and are usually per remote_group_id, but flows from different remote_group need to be merged on shared networks, where the complexity arises and this manager is needed. """ def __init__(self, driver): self.conj_id_map = ConjIdMap() self.driver = driver # The following two are dict of dicts and are indexed like: # self.x[vlan_tag][(direction, ethertype)] self.conj_ids = collections.defaultdict(dict) self.flow_state = collections.defaultdict( lambda: collections.defaultdict(dict)) def _build_addr_conj_id_map(self, ethertype, sg_conj_id_map): """Build a map of addr -> list of conj_ids.""" addr_to_conj = collections.defaultdict(list) for remote_id, conj_id_set in sg_conj_id_map.items(): remote_group = self.driver.sg_port_map.get_sg(remote_id) if not remote_group: LOG.debug('No member for SG %s', remote_id) continue for addr in remote_group.get_ethertype_filtered_addresses( ethertype): addr_to_conj[addr].extend(conj_id_set) return addr_to_conj def _update_flows_for_vlan_subr(self, direction, ethertype, vlan_tag, flow_state, addr_to_conj): """Do the actual flow updates for given direction and ethertype.""" current_ips = set(flow_state.keys()) self.driver.delete_flows_for_ip_addresses( current_ips - set(addr_to_conj.keys()), direction, ethertype, vlan_tag) for addr, conj_ids in addr_to_conj.items(): conj_ids.sort() if flow_state.get(addr) == conj_ids: continue for flow in rules.create_flows_for_ip_address( addr, direction, ethertype, vlan_tag, conj_ids): self.driver._add_flow(**flow) def update_flows_for_vlan(self, vlan_tag): """Install action=conjunction(conj_id, 1/2) flows, which depend on IP addresses of remote_group_id. """ for (direction, ethertype), sg_conj_id_map in ( self.conj_ids[vlan_tag].items()): # TODO(toshii): optimize when remote_groups have # no address overlaps. addr_to_conj = self._build_addr_conj_id_map( ethertype, sg_conj_id_map) self._update_flows_for_vlan_subr(direction, ethertype, vlan_tag, self.flow_state[vlan_tag][(direction, ethertype)], addr_to_conj) self.flow_state[vlan_tag][(direction, ethertype)] = addr_to_conj def add(self, vlan_tag, sg_id, remote_sg_id, direction, ethertype, priority_offset): """Get conj_id specified by the arguments and notify the manager that (remote_sg_id, direction, ethertype, conj_id) flows need to be populated on the vlan_tag network. A caller must call update_flows_for_vlan to have the change in effect. """ conj_id = self.conj_id_map.get_conj_id( sg_id, remote_sg_id, direction, ethertype) + priority_offset * 2 if (direction, ethertype) not in self.conj_ids[vlan_tag]: self.conj_ids[vlan_tag][(direction, ethertype)] = ( collections.defaultdict(set)) self.conj_ids[vlan_tag][(direction, ethertype)][remote_sg_id].add( conj_id) return conj_id def sg_removed(self, sg_id): """Handle SG removal events. Free all conj_ids associated with the sg_id and clean up obsolete entries from the self.conj_ids map. Unlike the add method, it also updates flows. """ id_list = self.conj_id_map.delete_sg(sg_id) unused_dict = collections.defaultdict(set) for remote_sg_id, conj_id in id_list: unused_dict[remote_sg_id].add(conj_id) for vlan_tag, vlan_conj_id_map in self.conj_ids.items(): update = False for sg_conj_id_map in vlan_conj_id_map.values(): for remote_sg_id, unused in unused_dict.items(): if (remote_sg_id in sg_conj_id_map and sg_conj_id_map[remote_sg_id] & unused): sg_conj_id_map[remote_sg_id] -= unused if not sg_conj_id_map[remote_sg_id]: del sg_conj_id_map[remote_sg_id] update = True if update: self.update_flows_for_vlan(vlan_tag) class OVSFirewallDriver(firewall.FirewallDriver): REQUIRED_PROTOCOLS = [ ovs_consts.OPENFLOW10, ovs_consts.OPENFLOW11, ovs_consts.OPENFLOW12, ovs_consts.OPENFLOW13, ovs_consts.OPENFLOW14, ] provides_arp_spoofing_protection = True def __init__(self, integration_bridge): """Initialize object :param integration_bridge: Bridge on which openflow rules will be applied """ self.permitted_ethertypes = cfg.CONF.SECURITYGROUP.permitted_ethertypes self.int_br = self.initialize_bridge(integration_bridge) self.sg_port_map = SGPortMap() self.conj_ip_manager = ConjIPFlowManager(self) self.sg_to_delete = set() self._update_cookie = None self._deferred = False self.iptables_helper = iptables.Helper(self.int_br.br) self.iptables_helper.load_driver_if_needed() self._initialize_firewall() callbacks_registry.subscribe( self._init_firewall_callback, callbacks_resources.AGENT, callbacks_events.OVS_RESTARTED) def _init_firewall_callback(self, resource, event, trigger, **kwargs): LOG.info("Reinitialize Openvswitch firewall after OVS restart.") self._initialize_firewall() def _initialize_firewall(self): self._drop_all_unmatched_flows() self._initialize_common_flows() self._initialize_third_party_tables() @contextlib.contextmanager def update_cookie_context(self): try: self._update_cookie = self.int_br.br.request_cookie() yield finally: self.int_br.br.unset_cookie(self._update_cookie) self._update_cookie = None def security_group_updated(self, action_type, sec_group_ids, device_ids=None): """The current driver doesn't make use of this method. It exists here to avoid NotImplementedError raised from the parent class's method. """ def _accept_flow(self, **flow): for f in rules.create_accept_flows(flow): self._add_flow(**f) def _add_flow(self, **kwargs): dl_type = kwargs.get('dl_type') create_reg_numbers(kwargs) if isinstance(dl_type, int): kwargs['dl_type'] = "0x{:04x}".format(dl_type) if self._update_cookie: kwargs['cookie'] = self._update_cookie if self._deferred: self.int_br.add_flow(**kwargs) else: self.int_br.br.add_flow(**kwargs) def _delete_flows(self, **kwargs): create_reg_numbers(kwargs) if self._deferred: self.int_br.delete_flows(**kwargs) else: self.int_br.br.delete_flows(**kwargs) def _strict_delete_flow(self, **kwargs): """Delete given flow right away even if bridge is deferred. Delete command will use strict delete. """ create_reg_numbers(kwargs) self.int_br.br.delete_flows(strict=True, **kwargs) @staticmethod def initialize_bridge(int_br): int_br.add_protocols(*OVSFirewallDriver.REQUIRED_PROTOCOLS) return int_br.deferred(full_ordered=True, use_bundle=True) def _drop_all_unmatched_flows(self): for table in ovs_consts.OVS_FIREWALL_TABLES: self.int_br.br.add_flow(table=table, priority=0, actions='drop') def _initialize_common_flows(self): # Remove conntrack information from tracked packets self._add_flow( table=ovs_consts.BASE_EGRESS_TABLE, priority=110, ct_state=ovsfw_consts.OF_STATE_TRACKED, actions='ct_clear,' 'resubmit(,%d)' % ovs_consts.BASE_EGRESS_TABLE, ) def _initialize_third_party_tables(self): self.int_br.br.add_flow( table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE, priority=1, actions='normal') self.int_br.br.add_flow( table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE, priority=1, actions='resubmit(,%d)' % ( ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE) ) for table in (ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE, ovs_consts.DROPPED_TRAFFIC_TABLE): self.int_br.br.add_flow( table=table, priority=0, actions='drop') def get_ovs_port(self, port_id): ovs_port = self.int_br.br.get_vif_port_by_id(port_id) if not ovs_port: raise exceptions.OVSFWPortNotFound(port_id=port_id) return ovs_port def _get_port_vlan_tag(self, port_name): return get_tag_from_other_config(self.int_br.br, port_name) def get_ofport(self, port): port_id = port['device'] return self.sg_port_map.ports.get(port_id) def get_or_create_ofport(self, port): """Get ofport specified by port['device'], checking and reflecting ofport changes. If ofport is nonexistent, create and return one. """ port_id = port['device'] ovs_port = self.get_ovs_port(port_id) try: of_port = self.sg_port_map.ports[port_id] except KeyError: port_vlan_id = self._get_port_vlan_tag(ovs_port.port_name) of_port = OFPort(port, ovs_port, port_vlan_id) self.sg_port_map.create_port(of_port, port) else: if of_port.ofport != ovs_port.ofport: self.sg_port_map.remove_port(of_port) of_port = OFPort(port, ovs_port, of_port.vlan_tag) self.sg_port_map.create_port(of_port, port) else: self.sg_port_map.update_port(of_port, port) return of_port def is_port_managed(self, port): return port['device'] in self.sg_port_map.ports def prepare_port_filter(self, port): self.iptables_helper.cleanup_port(port) if not firewall.port_sec_enabled(port): self._initialize_egress_no_port_security(port['device']) return old_of_port = self.get_ofport(port) of_port = self.get_or_create_ofport(port) if old_of_port: LOG.info("Initializing port %s that was already initialized.", port['device']) self._update_flows_for_port(of_port, old_of_port) else: self._set_port_filters(of_port) def update_port_filter(self, port): """Update rules for given port Current existing filtering rules are removed and new ones are generated based on current loaded security group rules and members. """ if not firewall.port_sec_enabled(port): self.remove_port_filter(port) self._initialize_egress_no_port_security(port['device']) return elif not self.is_port_managed(port): try: self._remove_egress_no_port_security(port['device']) except exceptions.OVSFWPortNotHandled as e: LOG.debug(e) else: self.prepare_port_filter(port) return try: # Make sure delete old allowed_address_pair MACs because # allowed_address_pair MACs will be updated in # self.get_or_create_ofport(port) old_of_port = self.get_ofport(port) of_port = self.get_or_create_ofport(port) if old_of_port: self._update_flows_for_port(of_port, old_of_port) else: self._set_port_filters(of_port) except exceptions.OVSFWPortNotFound as not_found_error: LOG.info("port %(port_id)s does not exist in ovsdb: %(err)s.", {'port_id': port['device'], 'err': not_found_error}) def _set_port_filters(self, of_port): self.initialize_port_flows(of_port) self.add_flows_from_rules(of_port) def _update_flows_for_port(self, of_port, old_of_port): with self.update_cookie_context(): self._set_port_filters(of_port) # Flush the flows caused by changes made to deferred bridge. The reason # is that following delete_all_port_flows() call uses --strict # parameter that cannot be combined with other non-strict rules, hence # all parameters with --strict are applied right away. In order to # avoid applying delete rules with --strict *before* # _set_port_filters() we dump currently cached flows here. self.int_br.apply_flows() self.delete_all_port_flows(old_of_port) # Rewrite update cookie with default cookie self._set_port_filters(of_port) def remove_port_filter(self, port): """Remove port from firewall All flows related to this port are removed from ovs. Port is also removed from ports managed by this firewall. """ if self.is_port_managed(port): of_port = self.get_ofport(port) self.delete_all_port_flows(of_port) self.sg_port_map.remove_port(of_port) for sec_group in of_port.sec_groups: self._schedule_sg_deletion_maybe(sec_group.id) def update_security_group_rules(self, sg_id, rules): self.sg_port_map.update_rules(sg_id, rules) def update_security_group_members(self, sg_id, member_ips): self.sg_port_map.update_members(sg_id, member_ips) if not member_ips: self._schedule_sg_deletion_maybe(sg_id) def _schedule_sg_deletion_maybe(self, sg_id): """Schedule possible deletion of the given SG. This function must be called when the number of ports associated to sg_id drops to zero, as it isn't possible to know SG deletions from agents due to RPC API design. """ sec_group = self.sg_port_map.get_or_create_sg(sg_id) if not sec_group.members or not sec_group.ports: self.sg_to_delete.add(sg_id) def _cleanup_stale_sg(self): sg_to_delete = self.sg_to_delete self.sg_to_delete = set() for sg_id in sg_to_delete: sec_group = self.sg_port_map.get_sg(sg_id) if sec_group.members and sec_group.ports: # sec_group is still in use continue self.conj_ip_manager.sg_removed(sg_id) self.sg_port_map.delete_sg(sg_id) def process_trusted_ports(self, port_ids): """Pass packets from these ports directly to ingress pipeline.""" for port_id in port_ids: self._initialize_egress_no_port_security(port_id) # yield to let other greenthreads proceed eventlet.sleep(0) def remove_trusted_ports(self, port_ids): for port_id in port_ids: try: self._remove_egress_no_port_security(port_id) except exceptions.OVSFWPortNotHandled as e: LOG.debug(e) def filter_defer_apply_on(self): self._deferred = True def filter_defer_apply_off(self): if self._deferred: self._cleanup_stale_sg() self.int_br.apply_flows() self._deferred = False @property def ports(self): return {id_: port.neutron_port_dict for id_, port in self.sg_port_map.ports.items()} def initialize_port_flows(self, port): """Set base flows for port :param port: OFPort instance """ # Identify egress flow self._add_flow( table=ovs_consts.TRANSIENT_TABLE, priority=100, in_port=port.ofport, actions='set_field:{:d}->reg{:d},' 'set_field:{:d}->reg{:d},' 'resubmit(,{:d})'.format( port.ofport, ovsfw_consts.REG_PORT, port.vlan_tag, ovsfw_consts.REG_NET, ovs_consts.BASE_EGRESS_TABLE) ) # Identify ingress flows for mac_addr in port.all_allowed_macs: self._add_flow( table=ovs_consts.TRANSIENT_TABLE, priority=90, dl_dst=mac_addr, dl_vlan='0x%x' % port.vlan_tag, actions='set_field:{:d}->reg{:d},' 'set_field:{:d}->reg{:d},' 'strip_vlan,resubmit(,{:d})'.format( port.ofport, ovsfw_consts.REG_PORT, port.vlan_tag, ovsfw_consts.REG_NET, ovs_consts.BASE_INGRESS_TABLE), ) self._initialize_egress(port) self._initialize_ingress(port) def _initialize_egress_ipv6_icmp(self, port): for icmp_type in firewall.ICMPV6_ALLOWED_EGRESS_TYPES: self._add_flow( table=ovs_consts.BASE_EGRESS_TABLE, priority=95, in_port=port.ofport, reg_port=port.ofport, dl_type=constants.ETHERTYPE_IPV6, nw_proto=lib_const.PROTO_NUM_IPV6_ICMP, icmp_type=icmp_type, actions='resubmit(,%d)' % ( ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE) ) def _initialize_egress_no_port_security(self, port_id): try: ovs_port = self.get_ovs_port(port_id) vlan_tag = self._get_port_vlan_tag(ovs_port.port_name) except exceptions.OVSFWTagNotFound: # It's a patch port, don't set anything return except exceptions.OVSFWPortNotFound as not_found_e: LOG.error("Initializing unfiltered port %(port_id)s that does not " "exist in ovsdb: %(err)s.", {'port_id': port_id, 'err': not_found_e}) return self.sg_port_map.unfiltered[port_id] = ovs_port.ofport self._add_flow( table=ovs_consts.TRANSIENT_TABLE, priority=100, in_port=ovs_port.ofport, actions='set_field:%d->reg%d,' 'set_field:%d->reg%d,' 'resubmit(,%d)' % ( ovs_port.ofport, ovsfw_consts.REG_PORT, vlan_tag, ovsfw_consts.REG_NET, ovs_consts.ACCEPT_OR_INGRESS_TABLE) ) self._add_flow( table=ovs_consts.ACCEPT_OR_INGRESS_TABLE, priority=80, reg_port=ovs_port.ofport, actions='resubmit(,%d)' % ( ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE) ) def _remove_egress_no_port_security(self, port_id): try: ofport = self.sg_port_map.unfiltered[port_id] except KeyError: raise exceptions.OVSFWPortNotHandled(port_id=port_id) self._delete_flows( table=ovs_consts.TRANSIENT_TABLE, in_port=ofport ) self._delete_flows( table=ovs_consts.ACCEPT_OR_INGRESS_TABLE, reg_port=ofport ) del self.sg_port_map.unfiltered[port_id] def _initialize_egress(self, port): """Identify egress traffic and send it to egress base""" self._initialize_egress_ipv6_icmp(port) # Apply mac/ip pairs for IPv4 allowed_pairs = port.allowed_pairs_v4.union( {(port.mac, ip_addr) for ip_addr in port.ipv4_addresses}) for mac_addr, ip_addr in allowed_pairs: self._add_flow( table=ovs_consts.BASE_EGRESS_TABLE, priority=95, in_port=port.ofport, reg_port=port.ofport, dl_src=mac_addr, dl_type=constants.ETHERTYPE_ARP, arp_spa=ip_addr, actions='resubmit(,%d)' % ( ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE) ) self._add_flow( table=ovs_consts.BASE_EGRESS_TABLE, priority=65, reg_port=port.ofport, dl_type=constants.ETHERTYPE_IP, in_port=port.ofport, dl_src=mac_addr, nw_src=ip_addr, actions='ct(table={:d},zone=NXM_NX_REG{:d}[0..15])'.format( ovs_consts.RULES_EGRESS_TABLE, ovsfw_consts.REG_NET) ) # Apply mac/ip pairs for IPv6 allowed_pairs = port.allowed_pairs_v6.union( {(port.mac, ip_addr) for ip_addr in port.ipv6_addresses}) for mac_addr, ip_addr in allowed_pairs: self._add_flow( table=ovs_consts.BASE_EGRESS_TABLE, priority=65, reg_port=port.ofport, in_port=port.ofport, dl_type=constants.ETHERTYPE_IPV6, dl_src=mac_addr, ipv6_src=ip_addr, actions='ct(table={:d},zone=NXM_NX_REG{:d}[0..15])'.format( ovs_consts.RULES_EGRESS_TABLE, ovsfw_consts.REG_NET) ) # DHCP discovery for dl_type, src_port, dst_port in ( (constants.ETHERTYPE_IP, 68, 67), (constants.ETHERTYPE_IPV6, 546, 547)): self._add_flow( table=ovs_consts.BASE_EGRESS_TABLE, priority=80, reg_port=port.ofport, in_port=port.ofport, dl_type=dl_type, nw_proto=lib_const.PROTO_NUM_UDP, tp_src=src_port, tp_dst=dst_port, actions='resubmit(,{:d})'.format( ovs_consts.ACCEPT_OR_INGRESS_TABLE) ) # Ban dhcp service running on an instance for dl_type, src_port, dst_port in ( (constants.ETHERTYPE_IP, 67, 68), (constants.ETHERTYPE_IPV6, 547, 546)): self._add_flow( table=ovs_consts.BASE_EGRESS_TABLE, priority=70, in_port=port.ofport, reg_port=port.ofport, dl_type=dl_type, nw_proto=lib_const.PROTO_NUM_UDP, tp_src=src_port, tp_dst=dst_port, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Drop Router Advertisements from instances self._add_flow( table=ovs_consts.BASE_EGRESS_TABLE, priority=70, in_port=port.ofport, reg_port=port.ofport, dl_type=constants.ETHERTYPE_IPV6, nw_proto=lib_const.PROTO_NUM_IPV6_ICMP, icmp_type=lib_const.ICMPV6_TYPE_RA, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Allow custom ethertypes for permitted_ethertype in self.permitted_ethertypes: if permitted_ethertype[:2] == '0x': try: hex_ethertype = hex(int(permitted_ethertype, base=16)) action = ('resubmit(,%d)' % ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE) self._add_flow( table=ovs_consts.BASE_EGRESS_TABLE, priority=95, dl_type=hex_ethertype, reg_port=port.ofport, actions=action ) continue except ValueError: pass LOG.warning("Custom ethertype %(permitted_ethertype)s is not " "a hexadecimal number.", {'permitted_ethertype': permitted_ethertype}) # Drop all remaining egress connections self._add_flow( table=ovs_consts.BASE_EGRESS_TABLE, priority=10, in_port=port.ofport, reg_port=port.ofport, actions='ct_clear,' 'resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Fill in accept_or_ingress table by checking that traffic is ingress # and if not, accept it for mac_addr in port.all_allowed_macs: self._add_flow( table=ovs_consts.ACCEPT_OR_INGRESS_TABLE, priority=100, dl_dst=mac_addr, reg_net=port.vlan_tag, actions='set_field:{:d}->reg{:d},resubmit(,{:d})'.format( port.ofport, ovsfw_consts.REG_PORT, ovs_consts.BASE_INGRESS_TABLE), ) for ethertype in [constants.ETHERTYPE_IP, constants.ETHERTYPE_IPV6]: self._add_flow( table=ovs_consts.ACCEPT_OR_INGRESS_TABLE, priority=90, dl_type=ethertype, reg_port=port.ofport, ct_state=ovsfw_consts.OF_STATE_NEW_NOT_ESTABLISHED, actions='ct(commit,zone=NXM_NX_REG{:d}[0..15]),' 'resubmit(,{:d})'.format( ovsfw_consts.REG_NET, ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE) ) self._add_flow( table=ovs_consts.ACCEPT_OR_INGRESS_TABLE, priority=80, reg_port=port.ofport, actions='resubmit(,%d)' % ( ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE) ) def _initialize_tracked_egress(self, port): # Drop invalid packets self._add_flow( table=ovs_consts.RULES_EGRESS_TABLE, priority=50, ct_state=ovsfw_consts.OF_STATE_INVALID, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Drop traffic for removed sg rules self._add_flow( table=ovs_consts.RULES_EGRESS_TABLE, priority=50, reg_port=port.ofport, ct_mark=ovsfw_consts.CT_MARK_INVALID, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) for state in ( ovsfw_consts.OF_STATE_ESTABLISHED_REPLY, ovsfw_consts.OF_STATE_RELATED, ): self._add_flow( table=ovs_consts.RULES_EGRESS_TABLE, priority=50, ct_state=state, ct_mark=ovsfw_consts.CT_MARK_NORMAL, reg_port=port.ofport, ct_zone=port.vlan_tag, actions='resubmit(,%d)' % ( ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE) ) self._add_flow( table=ovs_consts.RULES_EGRESS_TABLE, priority=40, reg_port=port.ofport, ct_state=ovsfw_consts.OF_STATE_NOT_ESTABLISHED, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) for ethertype in [constants.ETHERTYPE_IP, constants.ETHERTYPE_IPV6]: self._add_flow( table=ovs_consts.RULES_EGRESS_TABLE, priority=40, dl_type=ethertype, reg_port=port.ofport, ct_state=ovsfw_consts.OF_STATE_ESTABLISHED, actions="ct(commit,zone=NXM_NX_REG{:d}[0..15]," "exec(set_field:{:s}->ct_mark))".format( ovsfw_consts.REG_NET, ovsfw_consts.CT_MARK_INVALID) ) def _initialize_ingress_ipv6_icmp(self, port): for icmp_type in firewall.ICMPV6_ALLOWED_INGRESS_TYPES: self._add_flow( table=ovs_consts.BASE_INGRESS_TABLE, priority=100, reg_port=port.ofport, dl_type=constants.ETHERTYPE_IPV6, nw_proto=lib_const.PROTO_NUM_IPV6_ICMP, icmp_type=icmp_type, actions='output:{:d}'.format(port.ofport) ) def _initialize_ingress(self, port): # Allow incoming ARPs self._add_flow( table=ovs_consts.BASE_INGRESS_TABLE, priority=100, dl_type=constants.ETHERTYPE_ARP, reg_port=port.ofport, actions='output:{:d}'.format(port.ofport) ) self._initialize_ingress_ipv6_icmp(port) # DHCP offers for dl_type, src_port, dst_port in ( (constants.ETHERTYPE_IP, 67, 68), (constants.ETHERTYPE_IPV6, 547, 546)): self._add_flow( table=ovs_consts.BASE_INGRESS_TABLE, priority=95, reg_port=port.ofport, dl_type=dl_type, nw_proto=lib_const.PROTO_NUM_UDP, tp_src=src_port, tp_dst=dst_port, actions='output:{:d}'.format(port.ofport) ) # Track untracked for dl_type in (constants.ETHERTYPE_IP, constants.ETHERTYPE_IPV6): self._add_flow( table=ovs_consts.BASE_INGRESS_TABLE, priority=90, reg_port=port.ofport, dl_type=dl_type, ct_state=ovsfw_consts.OF_STATE_NOT_TRACKED, actions='ct(table={:d},zone=NXM_NX_REG{:d}[0..15])'.format( ovs_consts.RULES_INGRESS_TABLE, ovsfw_consts.REG_NET) ) self._add_flow( table=ovs_consts.BASE_INGRESS_TABLE, ct_state=ovsfw_consts.OF_STATE_TRACKED, priority=80, reg_port=port.ofport, actions='resubmit(,{:d})'.format(ovs_consts.RULES_INGRESS_TABLE) ) def _initialize_tracked_ingress(self, port): # Drop invalid packets self._add_flow( table=ovs_consts.RULES_INGRESS_TABLE, priority=50, ct_state=ovsfw_consts.OF_STATE_INVALID, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Drop traffic for removed sg rules self._add_flow( table=ovs_consts.RULES_INGRESS_TABLE, priority=50, reg_port=port.ofport, ct_mark=ovsfw_consts.CT_MARK_INVALID, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Allow established and related connections for state in (ovsfw_consts.OF_STATE_ESTABLISHED_REPLY, ovsfw_consts.OF_STATE_RELATED): self._add_flow( table=ovs_consts.RULES_INGRESS_TABLE, priority=50, reg_port=port.ofport, ct_state=state, ct_mark=ovsfw_consts.CT_MARK_NORMAL, ct_zone=port.vlan_tag, actions='output:{:d}'.format(port.ofport) ) self._add_flow( table=ovs_consts.RULES_INGRESS_TABLE, priority=40, reg_port=port.ofport, ct_state=ovsfw_consts.OF_STATE_NOT_ESTABLISHED, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) for ethertype in [constants.ETHERTYPE_IP, constants.ETHERTYPE_IPV6]: self._add_flow( table=ovs_consts.RULES_INGRESS_TABLE, priority=40, dl_type=ethertype, reg_port=port.ofport, ct_state=ovsfw_consts.OF_STATE_ESTABLISHED, actions="ct(commit,zone=NXM_NX_REG{:d}[0..15]," "exec(set_field:{:s}->ct_mark))".format( ovsfw_consts.REG_NET, ovsfw_consts.CT_MARK_INVALID) ) def _add_non_ip_conj_flows(self, port): """Install conjunction flows that don't depend on IP address of remote groups, which consist of actions=conjunction(conj_id, 2/2) flows and actions=accept flows. The remaining part is done by ConjIPFlowManager. """ port_rules = collections.defaultdict(list) for sec_group_id, rule in ( self._create_remote_rules_generator_for_port(port)): direction = rule['direction'] ethertype = rule['ethertype'] protocol = rule.get('protocol') priority_offset = rules.flow_priority_offset(rule) conj_id = self.conj_ip_manager.add(port.vlan_tag, sec_group_id, rule['remote_group_id'], direction, ethertype, priority_offset) rule1 = rule.copy() del rule1['remote_group_id'] port_rules_key = (direction, ethertype, protocol) port_rules[port_rules_key].append((rule1, conj_id)) for (direction, ethertype, protocol), rule_conj_list in ( port_rules.items()): all_conj_ids = set() for rule, conj_id in rule_conj_list: all_conj_ids.add(conj_id) if protocol in [lib_const.PROTO_NUM_SCTP, lib_const.PROTO_NUM_TCP, lib_const.PROTO_NUM_UDP]: rule_conj_list = rules.merge_port_ranges(rule_conj_list) else: rule_conj_list = rules.merge_common_rules(rule_conj_list) for rule, conj_ids in rule_conj_list: flows = rules.create_flows_from_rule_and_port( rule, port, conjunction=True) for flow in rules.substitute_conjunction_actions( flows, 2, conj_ids): self._add_flow(**flow) # Install accept flows and store conj_id to reg7 for future process for conj_id in all_conj_ids: for flow in rules.create_conj_flows( port, conj_id, direction, ethertype): flow['actions'] = "set_field:{:d}->reg{:d},{:s}".format( flow['conj_id'], ovsfw_consts.REG_REMOTE_GROUP, flow['actions'] ) self._add_flow(**flow) def add_flows_from_rules(self, port): self._initialize_tracked_ingress(port) self._initialize_tracked_egress(port) LOG.debug('Creating flow rules for port %s that is port %d in OVS', port.id, port.ofport) for rule in self._create_rules_generator_for_port(port): # NOTE(toshii): A better version of merge_common_rules and # its friend should be applied here in order to avoid # overlapping flows. flows = rules.create_flows_from_rule_and_port(rule, port) LOG.debug("RULGEN: Rules generated for flow %s are %s", rule, flows) for flow in flows: self._accept_flow(**flow) self._add_non_ip_conj_flows(port) self.conj_ip_manager.update_flows_for_vlan(port.vlan_tag) def _create_rules_generator_for_port(self, port): for sec_group in port.sec_groups: for rule in sec_group.raw_rules: yield rule def _create_remote_rules_generator_for_port(self, port): for sec_group in port.sec_groups: for rule in sec_group.remote_rules: yield sec_group.id, rule def delete_all_port_flows(self, port): """Delete all flows for given port""" for mac_addr in port.all_allowed_macs: self._strict_delete_flow(priority=90, table=ovs_consts.TRANSIENT_TABLE, dl_dst=mac_addr, dl_vlan=port.vlan_tag) self._delete_flows(table=ovs_consts.ACCEPT_OR_INGRESS_TABLE, dl_dst=mac_addr, reg_net=port.vlan_tag) self._strict_delete_flow(priority=100, table=ovs_consts.TRANSIENT_TABLE, in_port=port.ofport) self._delete_flows(reg_port=port.ofport) def delete_flows_for_ip_addresses( self, ip_addresses, direction, ethertype, vlan_tag): for ip_addr in ip_addresses: # Generate deletion template with bogus conj_id. flows = rules.create_flows_for_ip_address( ip_addr, direction, ethertype, vlan_tag, [0]) for f in flows: # The following del statements are partly for # complying the OpenFlow spec. It forbids the use of # these field in non-strict delete flow messages, and # the actions field is bogus anyway. del f['actions'] del f['priority'] self._delete_flows(**f) neutron-12.1.1/neutron/agent/linux/openvswitch_firewall/__init__.py0000664000175000017500000000132513553660046025617 0ustar zuulzuul00000000000000# Copyright 2015 # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.linux.openvswitch_firewall import firewall OVSFirewallDriver = firewall.OVSFirewallDriver neutron-12.1.1/neutron/agent/linux/pd_driver.py0000664000175000017500000000340413553660046021600 0ustar zuulzuul00000000000000# Copyright 2015 Cisco Systems # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import six from neutron.conf.agent import common as agent_conf agent_conf.register_pddriver_opts() @six.add_metaclass(abc.ABCMeta) class PDDriverBase(object): def __init__(self, router_id, subnet_id, ri_ifname): self.router_id = router_id self.subnet_id = subnet_id self.ri_ifname = ri_ifname @abc.abstractmethod def enable(self, pmon, router_ns, ex_gw_ifname, lla): """Enable IPv6 Prefix Delegation for this PDDriver on the given external interface, with the given link local address """ @abc.abstractmethod def disable(self, pmon, router_ns): """Disable IPv6 Prefix Delegation for this PDDriver """ @abc.abstractmethod def get_prefix(self): """Get the current assigned prefix for this PDDriver from the PD agent. If no prefix is currently assigned, return neutron_lib.constants.PROVISIONAL_IPV6_PD_PREFIX """ @staticmethod @abc.abstractmethod def get_sync_data(): """Get the latest router_id, subnet_id, and ri_ifname from the PD agent so that the PDDriver can be kept up to date """ neutron-12.1.1/neutron/agent/linux/ovsdb_monitor.py0000664000175000017500000001456413553660047022520 0ustar zuulzuul00000000000000# Copyright 2013 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib import eventlet from oslo_config import cfg from oslo_log import log as logging from oslo_serialization import jsonutils from neutron.agent.linux import async_process from neutron.agent.ovsdb import api as ovsdb from neutron.agent.ovsdb.native import helpers from neutron.common import utils from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants LOG = logging.getLogger(__name__) OVSDB_ACTION_INITIAL = 'initial' OVSDB_ACTION_INSERT = 'insert' OVSDB_ACTION_DELETE = 'delete' OVSDB_ACTION_NEW = 'new' @contextlib.contextmanager def get_bridges_monitor( bridges, ovsdb_monitor_respawn_interval=( constants.DEFAULT_OVSDBMON_RESPAWN)): mon = SimpleBridgesMonitor( bridges, respawn_interval=ovsdb_monitor_respawn_interval, ovsdb_connection=cfg.CONF.OVS.ovsdb_connection) mon.start() try: yield mon finally: mon.stop() class OvsdbMonitor(async_process.AsyncProcess): """Manages an invocation of 'ovsdb-client monitor'.""" def __init__(self, table_name, columns=None, format=None, respawn_interval=None, ovsdb_connection=None): self.table_name = table_name if ovsdb_connection: # if ovsdb connection is configured (e.g. tcp:ip:port), use it, # and there is no need to run as root helpers.enable_connection_uri(ovsdb_connection) cmd = ['ovsdb-client', 'monitor', ovsdb_connection, table_name] run_as_root = False else: cmd = ['ovsdb-client', 'monitor', table_name] run_as_root = True if columns: cmd.append(','.join(columns)) if format: cmd.append('--format=%s' % format) super(OvsdbMonitor, self).__init__(cmd, run_as_root=run_as_root, respawn_interval=respawn_interval, log_output=True, die_on_error=False) self.new_events = {'added': [], 'removed': []} def get_events(self): self.process_events() events = self.new_events self.new_events = {'added': [], 'removed': []} return events def start(self, block=False, timeout=5): super(OvsdbMonitor, self).start() if block: utils.wait_until_true(self.is_active) class SimpleInterfaceMonitor(OvsdbMonitor): """Monitors the Interface table of the local host's ovsdb for changes. The has_updates() method indicates whether changes to the ovsdb Interface table have been detected since the monitor started or since the previous access. """ def __init__(self, respawn_interval=None, ovsdb_connection=None): super(SimpleInterfaceMonitor, self).__init__( 'Interface', columns=['name', 'ofport', 'external_ids'], format='json', respawn_interval=respawn_interval, ovsdb_connection=ovsdb_connection ) @property def has_updates(self): """Indicate whether the ovsdb Interface table has been updated. If the monitor process is not active an error will be logged since it won't be able to communicate any update. This situation should be temporary if respawn_interval is set. """ if not self.is_active(): LOG.error("%s monitor is not active", self.table_name) else: self.process_events() return bool(self.new_events['added'] or self.new_events['removed']) def process_events(self): devices_added = [] devices_removed = [] dev_to_ofport = {} for row in self.iter_stdout(): json = jsonutils.loads(row).get('data') for ovs_id, action, name, ofport, external_ids in json: if external_ids: external_ids = ovsdb.val_to_py(external_ids) if ofport: ofport = ovsdb.val_to_py(ofport) device = {'name': name, 'ofport': ofport, 'external_ids': external_ids} if action in (OVSDB_ACTION_INITIAL, OVSDB_ACTION_INSERT): devices_added.append(device) elif action == OVSDB_ACTION_DELETE: devices_removed.append(device) elif action == OVSDB_ACTION_NEW: dev_to_ofport[name] = ofport self.new_events['added'].extend(devices_added) self.new_events['removed'].extend(devices_removed) # update any events with ofports received from 'new' action for event in self.new_events['added']: event['ofport'] = dev_to_ofport.get(event['name'], event['ofport']) class SimpleBridgesMonitor(OvsdbMonitor): """Monitors the Bridge table of the local host's ovsdb for changes. The bridges_added() method returns all newly created bridges in ovsdb since the monitor started or since the previous access. """ def __init__(self, bridges, respawn_interval=None, ovsdb_connection=None): super(SimpleBridgesMonitor, self).__init__( 'Bridge', columns=['name'], format='json', respawn_interval=respawn_interval, ovsdb_connection=ovsdb_connection ) self.bridges = bridges @property def bridges_added(self): eventlet.sleep() return self.get_events()['added'] def process_events(self): bridges_added = [] for row in self.iter_stdout(): json = jsonutils.loads(row).get('data') for ovs_id, action, name in json: if name in self.bridges and action == OVSDB_ACTION_INSERT: bridges_added.append(name) self.new_events['added'].extend(bridges_added) neutron-12.1.1/neutron/agent/linux/ip_monitor.py0000664000175000017500000000523113553660047022002 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from oslo_utils import excutils from neutron.agent.linux import async_process from neutron.agent.linux import ip_lib LOG = logging.getLogger(__name__) class IPMonitorEvent(object): def __init__(self, line, added, interface, cidr): self.line = line self.added = added self.interface = interface self.cidr = cidr def __str__(self): return self.line @classmethod def from_text(cls, line): route = line.split() try: first_word = route[0] except IndexError: with excutils.save_and_reraise_exception(): LOG.error('Unable to parse route "%s"', line) added = (first_word != 'Deleted') if not added: route = route[1:] try: interface = ip_lib.remove_interface_suffix(route[1]) cidr = route[3] except IndexError: with excutils.save_and_reraise_exception(): LOG.error('Unable to parse route "%s"', line) return cls(line, added, interface, cidr) class IPMonitor(async_process.AsyncProcess): """Wrapper over `ip monitor address`. To monitor and react indefinitely: m = IPMonitor(namespace='tmp', root_as_root=True) m.start() for iterable in m: event = IPMonitorEvent.from_text(iterable) print(event, event.added, event.interface, event.cidr) """ def __init__(self, namespace=None, run_as_root=True, respawn_interval=None): super(IPMonitor, self).__init__(['ip', '-o', 'monitor', 'address'], run_as_root=run_as_root, respawn_interval=respawn_interval, namespace=namespace) def __iter__(self): return self.iter_stdout(block=True) def start(self): super(IPMonitor, self).start(block=True) def stop(self): super(IPMonitor, self).stop(block=True) neutron-12.1.1/neutron/agent/linux/external_process.py0000664000175000017500000002414413553660047023207 0ustar zuulzuul00000000000000# Copyright 2012 New Dream Network, LLC (DreamHost) # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import collections import os.path import eventlet from oslo_concurrency import lockutils from oslo_config import cfg from oslo_log import log as logging from oslo_utils import fileutils import psutil import six from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.conf.agent import common as agent_cfg LOG = logging.getLogger(__name__) agent_cfg.register_external_process_opts() agent_cfg.register_process_monitor_opts(cfg.CONF) @six.add_metaclass(abc.ABCMeta) class MonitoredProcess(object): @abc.abstractproperty def active(self): """Boolean representing the running state of the process.""" @abc.abstractmethod def enable(self): """Enable the service, or respawn the process.""" class ProcessManager(MonitoredProcess): """An external process manager for Neutron spawned processes. Note: The manager expects uuid to be in cmdline. """ def __init__(self, conf, uuid, namespace=None, service=None, pids_path=None, default_cmd_callback=None, cmd_addl_env=None, pid_file=None, run_as_root=False, custom_reload_callback=None): self.conf = conf self.uuid = uuid self.namespace = namespace self.default_cmd_callback = default_cmd_callback self.cmd_addl_env = cmd_addl_env self.pids_path = pids_path or self.conf.external_pids self.pid_file = pid_file self.run_as_root = run_as_root or self.namespace is not None self.custom_reload_callback = custom_reload_callback if service: self.service_pid_fname = 'pid.' + service self.service = service else: self.service_pid_fname = 'pid' self.service = 'default-service' fileutils.ensure_tree(os.path.dirname(self.get_pid_file_name()), mode=0o755) def enable(self, cmd_callback=None, reload_cfg=False): if not self.active: if not cmd_callback: cmd_callback = self.default_cmd_callback cmd = cmd_callback(self.get_pid_file_name()) ip_wrapper = ip_lib.IPWrapper(namespace=self.namespace) ip_wrapper.netns.execute(cmd, addl_env=self.cmd_addl_env, run_as_root=self.run_as_root) elif reload_cfg: self.reload_cfg() def reload_cfg(self): if self.custom_reload_callback: self.disable(get_stop_command=self.custom_reload_callback) else: self.disable('HUP') def disable(self, sig='9', get_stop_command=None): pid = self.pid if self.active: if get_stop_command: cmd = get_stop_command(self.get_pid_file_name()) ip_wrapper = ip_lib.IPWrapper(namespace=self.namespace) ip_wrapper.netns.execute(cmd, addl_env=self.cmd_addl_env, run_as_root=self.run_as_root) else: cmd = ['kill', '-%s' % (sig), pid] utils.execute(cmd, run_as_root=self.run_as_root) # In the case of shutting down, remove the pid file if sig == '9': fileutils.delete_if_exists(self.get_pid_file_name()) elif pid: LOG.debug('Process for %(uuid)s pid %(pid)d is stale, ignoring ' 'signal %(signal)s', {'uuid': self.uuid, 'pid': pid, 'signal': sig}) else: LOG.debug('No process started for %s', self.uuid) def get_pid_file_name(self): """Returns the file name for a given kind of config file.""" if self.pid_file: return self.pid_file else: return utils.get_conf_file_name(self.pids_path, self.uuid, self.service_pid_fname) @property def pid(self): """Last known pid for this external process spawned for this uuid.""" return utils.get_value_from_file(self.get_pid_file_name(), int) @property def active(self): cmdline = self.cmdline return self.uuid in cmdline if cmdline else False @property def cmdline(self): pid = self.pid if not pid: return try: return ' '.join(psutil.Process(pid).cmdline()) except (psutil.NoSuchProcess, psutil.AccessDenied): return ServiceId = collections.namedtuple('ServiceId', ['uuid', 'service']) class ProcessMonitor(object): def __init__(self, config, resource_type): """Handle multiple process managers and watch over all of them. :param config: oslo config object with the agent configuration. :type config: oslo_config.ConfigOpts :param resource_type: can be dhcp, router, load_balancer, etc. :type resource_type: str """ self._config = config self._resource_type = resource_type self._monitored_processes = {} if self._config.AGENT.check_child_processes_interval: self._spawn_checking_thread() def register(self, uuid, service_name, monitored_process): """Start monitoring a process. The given monitored_process will be tied to it's uuid+service_name replacing the old one if it existed already. The monitored_process should be enabled before registration, otherwise ProcessMonitor could try to enable the process itself, which could lead to double enable and if unlucky enough, two processes running, and also errors in the logs. :param uuid: An ID of the resource for which the process is running. :param service_name: A logical service name for this process monitor, so the same uuid provided via process manager can reference several different services. :param monitored_process: MonitoredProcess we want to monitor. """ service_id = ServiceId(uuid, service_name) self._monitored_processes[service_id] = monitored_process def unregister(self, uuid, service_name): """Stop monitoring a process. The uuid+service_name will be removed from the monitored processes. The service must be disabled **after** unregistering, otherwise if process monitor checks after you disable the process, and before you unregister it, the process will be respawned, and left orphaned into the system. :param uuid: An ID of the resource for which the process is running. :param service_name: A logical service name for this process monitor, so the same uuid provided via process manager can reference several different services. """ service_id = ServiceId(uuid, service_name) self._monitored_processes.pop(service_id, None) def stop(self): """Stop the process monitoring. This method will stop the monitoring thread, but no monitored process will be stopped. """ self._monitor_processes = False def _spawn_checking_thread(self): self._monitor_processes = True eventlet.spawn(self._periodic_checking_thread) @lockutils.synchronized("_check_child_processes") def _check_child_processes(self): # we build the list of keys before iterating in the loop to cover # the case where other threads add or remove items from the # dictionary which otherwise will cause a RuntimeError for service_id in list(self._monitored_processes): pm = self._monitored_processes.get(service_id) if pm and not pm.active: LOG.error("%(service)s for %(resource_type)s " "with uuid %(uuid)s not found. " "The process should not have died", {'service': service_id.service, 'resource_type': self._resource_type, 'uuid': service_id.uuid}) self._execute_action(service_id) eventlet.sleep(0) def _periodic_checking_thread(self): while self._monitor_processes: eventlet.sleep(self._config.AGENT.check_child_processes_interval) eventlet.spawn(self._check_child_processes) def _execute_action(self, service_id): action = self._config.AGENT.check_child_processes_action action_function = getattr(self, "_%s_action" % action) action_function(service_id) def _respawn_action(self, service_id): LOG.warning("Respawning %(service)s for uuid %(uuid)s", {'service': service_id.service, 'uuid': service_id.uuid}) self._monitored_processes[service_id].enable() def _exit_action(self, service_id): LOG.error("Exiting agent as programmed in check_child_processes_" "actions") self._exit_handler(service_id.uuid, service_id.service) def _exit_handler(self, uuid, service): """This is an exit handler for the ProcessMonitor. It will be called if the administrator configured the exit action in check_child_processes_actions, and one of our external processes die unexpectedly. """ LOG.error("Exiting agent because of a malfunction with the " "%(service)s process identified by uuid %(uuid)s", {'service': service, 'uuid': uuid}) raise SystemExit(1) neutron-12.1.1/neutron/agent/linux/utils.py0000664000175000017500000003612013553660047020764 0ustar zuulzuul00000000000000# Copyright 2012 Locaweb. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import glob import grp import os import pwd import shlex import socket import threading import time import eventlet from eventlet.green import subprocess from neutron_lib.utils import helpers from oslo_config import cfg from oslo_log import log as logging from oslo_rootwrap import client from oslo_utils import encodeutils from oslo_utils import excutils from oslo_utils import fileutils from six.moves import http_client as httplib from neutron._i18n import _ from neutron.agent.linux import xenapi_root_helper from neutron.common import exceptions from neutron.common import utils from neutron.conf.agent import common as config from neutron import wsgi LOG = logging.getLogger(__name__) class RootwrapDaemonHelper(object): __client = None __lock = threading.Lock() def __new__(cls): """There is no reason to instantiate this class""" raise NotImplementedError() @classmethod def get_client(cls): with cls.__lock: if cls.__client is None: if xenapi_root_helper.ROOT_HELPER_DAEMON_TOKEN == \ cfg.CONF.AGENT.root_helper_daemon: cls.__client = xenapi_root_helper.XenAPIClient() else: cls.__client = client.Client( shlex.split(cfg.CONF.AGENT.root_helper_daemon)) return cls.__client def addl_env_args(addl_env): """Build arguments for adding additional environment vars with env""" # NOTE (twilson) If using rootwrap, an EnvFilter should be set up for the # command instead of a CommandFilter. if addl_env is None: return [] return ['env'] + ['%s=%s' % pair for pair in addl_env.items()] def create_process(cmd, run_as_root=False, addl_env=None): """Create a process object for the given command. The return value will be a tuple of the process object and the list of command arguments used to create it. """ cmd = list(map(str, addl_env_args(addl_env) + cmd)) if run_as_root: cmd = shlex.split(config.get_root_helper(cfg.CONF)) + cmd LOG.debug("Running command: %s", cmd) obj = utils.subprocess_popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return obj, cmd def execute_rootwrap_daemon(cmd, process_input, addl_env): cmd = list(map(str, addl_env_args(addl_env) + cmd)) # NOTE(twilson) oslo_rootwrap.daemon will raise on filter match # errors, whereas oslo_rootwrap.cmd converts them to return codes. # In practice, no neutron code should be trying to execute something that # would throw those errors, and if it does it should be fixed as opposed to # just logging the execution error. LOG.debug("Running command (rootwrap daemon): %s", cmd) client = RootwrapDaemonHelper.get_client() try: return client.execute(cmd, process_input) except Exception: with excutils.save_and_reraise_exception(): LOG.error("Rootwrap error running command: %s", cmd) def execute(cmd, process_input=None, addl_env=None, check_exit_code=True, return_stderr=False, log_fail_as_error=True, extra_ok_codes=None, run_as_root=False): try: if process_input is not None: _process_input = encodeutils.to_utf8(process_input) else: _process_input = None if run_as_root and cfg.CONF.AGENT.root_helper_daemon: returncode, _stdout, _stderr = ( execute_rootwrap_daemon(cmd, process_input, addl_env)) else: obj, cmd = create_process(cmd, run_as_root=run_as_root, addl_env=addl_env) _stdout, _stderr = obj.communicate(_process_input) returncode = obj.returncode obj.stdin.close() _stdout = helpers.safe_decode_utf8(_stdout) _stderr = helpers.safe_decode_utf8(_stderr) extra_ok_codes = extra_ok_codes or [] if returncode and returncode not in extra_ok_codes: msg = _("Exit code: %(returncode)d; " "Stdin: %(stdin)s; " "Stdout: %(stdout)s; " "Stderr: %(stderr)s") % { 'returncode': returncode, 'stdin': process_input or '', 'stdout': _stdout, 'stderr': _stderr} if log_fail_as_error: LOG.error(msg) if check_exit_code: raise exceptions.ProcessExecutionError(msg, returncode=returncode) finally: # NOTE(termie): this appears to be necessary to let the subprocess # call clean something up in between calls, without # it two execute calls in a row hangs the second one time.sleep(0) return (_stdout, _stderr) if return_stderr else _stdout def find_child_pids(pid, recursive=False): """Retrieve a list of the pids of child processes of the given pid. It can also find all children through the hierarchy if recursive=True """ try: raw_pids = execute(['ps', '--ppid', pid, '-o', 'pid='], log_fail_as_error=False) except exceptions.ProcessExecutionError as e: # Unexpected errors are the responsibility of the caller with excutils.save_and_reraise_exception() as ctxt: # Exception has already been logged by execute no_children_found = e.returncode == 1 if no_children_found: ctxt.reraise = False return [] child_pids = [x.strip() for x in raw_pids.split('\n') if x.strip()] if recursive: for child in child_pids: child_pids = child_pids + find_child_pids(child, True) return child_pids def find_parent_pid(pid): """Retrieve the pid of the parent process of the given pid. If the pid doesn't exist in the system, this function will return None """ try: ppid = execute(['ps', '-o', 'ppid=', pid], log_fail_as_error=False) except exceptions.ProcessExecutionError as e: # Unexpected errors are the responsibility of the caller with excutils.save_and_reraise_exception() as ctxt: # Exception has already been logged by execute no_such_pid = e.returncode == 1 if no_such_pid: ctxt.reraise = False return return ppid.strip() def find_fork_top_parent(pid): """Retrieve the pid of the top parent of the given pid through a fork. This function will search the top parent with its same cmdline. If the given pid has no parent, its own pid will be returned """ while True: ppid = find_parent_pid(pid) if (ppid and ppid != pid and pid_invoked_with_cmdline(ppid, get_cmdline_from_pid(pid))): pid = ppid else: return pid def kill_process(pid, signal, run_as_root=False): """Kill the process with the given pid using the given signal.""" try: execute(['kill', '-%d' % signal, pid], run_as_root=run_as_root) except exceptions.ProcessExecutionError: if process_is_running(pid): raise def _get_conf_base(cfg_root, uuid, ensure_conf_dir): #TODO(mangelajo): separate responsibilities here, ensure_conf_dir # should be a separate function conf_dir = os.path.abspath(os.path.normpath(cfg_root)) conf_base = os.path.join(conf_dir, uuid) if ensure_conf_dir: fileutils.ensure_tree(conf_dir, mode=0o755) return conf_base def get_conf_file_name(cfg_root, uuid, cfg_file, ensure_conf_dir=False): """Returns the file name for a given kind of config file.""" conf_base = _get_conf_base(cfg_root, uuid, ensure_conf_dir) return "%s.%s" % (conf_base, cfg_file) def get_value_from_file(filename, converter=None): try: with open(filename, 'r') as f: try: return converter(f.read()) if converter else f.read() except ValueError: LOG.error('Unable to convert value in %s', filename) except IOError: LOG.debug('Unable to access %s', filename) def remove_conf_files(cfg_root, uuid): conf_base = _get_conf_base(cfg_root, uuid, False) for file_path in glob.iglob("%s.*" % conf_base): os.unlink(file_path) def get_root_helper_child_pid(pid, expected_cmd, run_as_root=False): """ Get the first non root_helper child pid in the process hierarchy. If root helper was used, two or more processes would be created: - a root helper process (e.g. sudo myscript) - possibly a rootwrap script (e.g. neutron-rootwrap) - a child process (e.g. myscript) - possibly its child processes Killing the root helper process will leave the child process running, re-parented to init, so the only way to ensure that both die is to target the child process directly. """ pid = str(pid) if run_as_root: while True: try: # We shouldn't have more than one child per process # so keep getting the children of the first one pid = find_child_pids(pid)[0] except IndexError: return # We never found the child pid with expected_cmd # If we've found a pid with no root helper, return it. # If we continue, we can find transient children. if pid_invoked_with_cmdline(pid, expected_cmd): break return pid def remove_abs_path(cmd): """Remove absolute path of executable in cmd Note: New instance of list is returned :param cmd: parsed shlex command (e.g. ['/bin/foo', 'param1', 'param two']) """ if cmd and os.path.isabs(cmd[0]): cmd = list(cmd) cmd[0] = os.path.basename(cmd[0]) return cmd def process_is_running(pid): """Find if the given PID is running in the system. """ return pid and os.path.exists('/proc/%s' % pid) def get_cmdline_from_pid(pid): if not process_is_running(pid): return [] with open('/proc/%s/cmdline' % pid, 'r') as f: cmdline = f.readline() cmdline_args = cmdline.split('\0')[:-1] # NOTE(slaweq): sometimes it may happen that values in # /proc/{pid}/cmdline are separated by space instead of NUL char, # in such case we would have everything in one element of cmdline_args # list and it would not match to expected cmd so we need to try to # split it by spaces if len(cmdline_args) == 1: cmdline_args = cmdline_args[0].split(' ') LOG.debug("Found cmdline %s for rocess with PID %s.", cmdline_args, pid) return cmdline_args def cmd_matches_expected(cmd, expected_cmd): abs_cmd = remove_abs_path(cmd) abs_expected_cmd = remove_abs_path(expected_cmd) if abs_cmd != abs_expected_cmd: # Commands executed with #! are prefixed with the script # executable. Check for the expected cmd being a subset of the # actual cmd to cover this possibility. abs_cmd = remove_abs_path(abs_cmd[1:]) return abs_cmd == abs_expected_cmd def pid_invoked_with_cmdline(pid, expected_cmd): """Validate process with given pid is running with provided parameters """ cmd = get_cmdline_from_pid(pid) return cmd_matches_expected(cmd, expected_cmd) def ensure_directory_exists_without_file(path): dirname = os.path.dirname(path) if os.path.isdir(dirname): try: os.unlink(path) except OSError: with excutils.save_and_reraise_exception() as ctxt: if not os.path.exists(path): ctxt.reraise = False else: fileutils.ensure_tree(dirname, mode=0o755) def is_effective_user(user_id_or_name): """Returns True if user_id_or_name is effective user (id/name).""" euid = os.geteuid() if str(user_id_or_name) == str(euid): return True effective_user_name = pwd.getpwuid(euid).pw_name return user_id_or_name == effective_user_name def is_effective_group(group_id_or_name): """Returns True if group_id_or_name is effective group (id/name).""" egid = os.getegid() if str(group_id_or_name) == str(egid): return True effective_group_name = grp.getgrgid(egid).gr_name return group_id_or_name == effective_group_name class UnixDomainHTTPConnection(httplib.HTTPConnection): """Connection class for HTTP over UNIX domain socket.""" def __init__(self, host, port=None, strict=None, timeout=None, proxy_info=None): httplib.HTTPConnection.__init__(self, host, port, strict) self.timeout = timeout self.socket_path = cfg.CONF.metadata_proxy_socket def connect(self): self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) if self.timeout: self.sock.settimeout(self.timeout) self.sock.connect(self.socket_path) class UnixDomainHttpProtocol(eventlet.wsgi.HttpProtocol): def __init__(self, request, client_address, server): if not client_address: client_address = ('', 0) # base class is old-style, so super does not work properly eventlet.wsgi.HttpProtocol.__init__(self, request, client_address, server) class UnixDomainWSGIServer(wsgi.Server): def __init__(self, name, num_threads=None): self._socket = None self._launcher = None self._server = None super(UnixDomainWSGIServer, self).__init__(name, disable_ssl=True, num_threads=num_threads) def start(self, application, file_socket, workers, backlog, mode=None): self._socket = eventlet.listen(file_socket, family=socket.AF_UNIX, backlog=backlog) if mode is not None: os.chmod(file_socket, mode) self._launch(application, workers=workers) def _run(self, application, socket): """Start a WSGI service in a new green thread.""" logger = logging.getLogger('eventlet.wsgi.server') eventlet.wsgi.server(socket, application, max_size=self.num_threads, protocol=UnixDomainHttpProtocol, log=logger, log_format=cfg.CONF.wsgi_log_format) neutron-12.1.1/neutron/agent/linux/pd.py0000664000175000017500000004054213553660047020232 0ustar zuulzuul00000000000000# Copyright 2015 Cisco Systems # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import signal import eventlet from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as n_const from neutron_lib.utils import runtime from oslo_log import log as logging from oslo_utils import netutils import six from stevedore import driver from neutron.common import constants as l3_constants from neutron.common import utils LOG = logging.getLogger(__name__) class PrefixDelegation(object): def __init__(self, context, pmon, intf_driver, notifier, pd_update_cb, agent_conf): self.context = context self.pmon = pmon self.intf_driver = intf_driver self.notifier = notifier self.routers = {} self.pd_update_cb = pd_update_cb self.agent_conf = agent_conf self.pd_dhcp_driver = driver.DriverManager( namespace='neutron.agent.linux.pd_drivers', name=agent_conf.prefix_delegation_driver, ).driver registry.subscribe(add_router, resources.ROUTER, events.BEFORE_CREATE) registry.subscribe(update_router, resources.ROUTER, events.AFTER_UPDATE) registry.subscribe(remove_router, resources.ROUTER, events.AFTER_DELETE) self._get_sync_data() def _is_pd_master_router(self, router): return router['master'] @runtime.synchronized("l3-agent-pd") def enable_subnet(self, router_id, subnet_id, prefix, ri_ifname, mac): router = self.routers.get(router_id) if router is None: return pd_info = router['subnets'].get(subnet_id) if not pd_info: pd_info = PDInfo(ri_ifname=ri_ifname, mac=mac) router['subnets'][subnet_id] = pd_info pd_info.bind_lla = self._get_lla(mac) if pd_info.sync: pd_info.mac = mac pd_info.old_prefix = prefix elif self._is_pd_master_router(router): self._add_lla(router, pd_info.get_bind_lla_with_mask()) def _delete_pd(self, router, pd_info): if not self._is_pd_master_router(router): return self._delete_lla(router, pd_info.get_bind_lla_with_mask()) if pd_info.client_started: pd_info.driver.disable(self.pmon, router['ns_name']) @runtime.synchronized("l3-agent-pd") def disable_subnet(self, router_id, subnet_id): prefix_update = {} router = self.routers.get(router_id) if not router: return pd_info = router['subnets'].get(subnet_id) if not pd_info: return self._delete_pd(router, pd_info) if self._is_pd_master_router(router): prefix_update[subnet_id] = n_const.PROVISIONAL_IPV6_PD_PREFIX LOG.debug("Update server with prefixes: %s", prefix_update) self.notifier(self.context, prefix_update) del router['subnets'][subnet_id] @runtime.synchronized("l3-agent-pd") def update_subnet(self, router_id, subnet_id, prefix): router = self.routers.get(router_id) if router is not None: pd_info = router['subnets'].get(subnet_id) if pd_info and pd_info.old_prefix != prefix: old_prefix = pd_info.old_prefix pd_info.old_prefix = prefix pd_info.prefix = prefix return old_prefix @runtime.synchronized("l3-agent-pd") def add_gw_interface(self, router_id, gw_ifname): router = self.routers.get(router_id) if not router: return router['gw_interface'] = gw_ifname if not self._is_pd_master_router(router): return prefix_update = {} for pd_info in six.itervalues(router['subnets']): # gateway is added after internal router ports. # If a PD is being synced, and if the prefix is available, # send update if prefix out of sync; If not available, # start the PD client bind_lla_with_mask = pd_info.get_bind_lla_with_mask() if pd_info.sync: pd_info.sync = False if pd_info.client_started: if pd_info.prefix != pd_info.old_prefix: prefix_update['subnet_id'] = pd_info.prefix else: self._delete_lla(router, bind_lla_with_mask) self._add_lla(router, bind_lla_with_mask) else: self._add_lla(router, bind_lla_with_mask) if prefix_update: LOG.debug("Update server with prefixes: %s", prefix_update) self.notifier(self.context, prefix_update) def delete_router_pd(self, router): if not self._is_pd_master_router(router): return prefix_update = {} for subnet_id, pd_info in router['subnets'].items(): self._delete_lla(router, pd_info.get_bind_lla_with_mask()) if pd_info.client_started: pd_info.driver.disable(self.pmon, router['ns_name']) pd_info.prefix = None pd_info.client_started = False prefix = n_const.PROVISIONAL_IPV6_PD_PREFIX prefix_update[subnet_id] = prefix if prefix_update: LOG.debug("Update server with prefixes: %s", prefix_update) self.notifier(self.context, prefix_update) @runtime.synchronized("l3-agent-pd") def remove_gw_interface(self, router_id): router = self.routers.get(router_id) if router is not None: router['gw_interface'] = None self.delete_router_pd(router) @runtime.synchronized("l3-agent-pd") def get_preserve_ips(self, router_id): preserve_ips = [] router = self.routers.get(router_id) if router is not None: for pd_info in six.itervalues(router['subnets']): preserve_ips.append(pd_info.get_bind_lla_with_mask()) return preserve_ips @runtime.synchronized("l3-agent-pd") def sync_router(self, router_id): router = self.routers.get(router_id) if router is not None and router['gw_interface'] is None: self.delete_router_pd(router) @runtime.synchronized("l3-agent-pd") def remove_stale_ri_ifname(self, router_id, stale_ifname): router = self.routers.get(router_id) if router is not None: subnet_to_delete = None for subnet_id, pd_info in six.iteritems(router['subnets']): if pd_info.ri_ifname == stale_ifname: self._delete_pd(router, pd_info) subnet_to_delete = subnet_id break if subnet_to_delete: del router['subnets'][subnet_to_delete] @staticmethod def _get_lla(mac): lla = netutils.get_ipv6_addr_by_EUI64(n_const.IPv6_LLA_PREFIX, mac) return lla def _get_llas(self, gw_ifname, ns_name): try: return self.intf_driver.get_ipv6_llas(gw_ifname, ns_name) except RuntimeError: # The error message was printed as part of the driver call # This could happen if the gw_ifname was removed # simply return and exit the thread return def _add_lla(self, router, lla_with_mask): if router['gw_interface']: self.intf_driver.add_ipv6_addr(router['gw_interface'], lla_with_mask, router['ns_name'], 'link') # There is a delay before the LLA becomes active. # This is because the kernel runs DAD to make sure LLA uniqueness # Spawn a thread to wait for the interface to be ready self._spawn_lla_thread(router['gw_interface'], router['ns_name'], lla_with_mask) def _spawn_lla_thread(self, gw_ifname, ns_name, lla_with_mask): eventlet.spawn_n(self._ensure_lla_task, gw_ifname, ns_name, lla_with_mask) def _delete_lla(self, router, lla_with_mask): if lla_with_mask and router['gw_interface']: try: self.intf_driver.delete_ipv6_addr(router['gw_interface'], lla_with_mask, router['ns_name']) except RuntimeError: # Ignore error if the lla doesn't exist pass def _ensure_lla_task(self, gw_ifname, ns_name, lla_with_mask): # It would be insane for taking so long unless DAD test failed # In that case, the subnet would never be assigned a prefix. utils.wait_until_true(functools.partial(self._lla_available, gw_ifname, ns_name, lla_with_mask), timeout=l3_constants.LLA_TASK_TIMEOUT, sleep=2) def _lla_available(self, gw_ifname, ns_name, lla_with_mask): llas = self._get_llas(gw_ifname, ns_name) if self._is_lla_active(lla_with_mask, llas): LOG.debug("LLA %s is active now", lla_with_mask) self.pd_update_cb() return True @staticmethod def _is_lla_active(lla_with_mask, llas): for lla in llas: if lla_with_mask == lla['cidr']: return not lla['tentative'] return False @runtime.synchronized("l3-agent-pd") def process_ha_state(self, router_id, master): router = self.routers.get(router_id) if router is None or router['master'] == master: return router['master'] = master if master: for pd_info in six.itervalues(router['subnets']): bind_lla_with_mask = pd_info.get_bind_lla_with_mask() self._add_lla(router, bind_lla_with_mask) else: for pd_info in six.itervalues(router['subnets']): self._delete_lla(router, pd_info.get_bind_lla_with_mask()) if pd_info.client_started: pd_info.driver.disable(self.pmon, router['ns_name'], switch_over=True) pd_info.client_started = False @runtime.synchronized("l3-agent-pd") def process_prefix_update(self): LOG.debug("Processing IPv6 PD Prefix Update") prefix_update = {} for router_id, router in self.routers.items(): if not (self._is_pd_master_router(router) and router['gw_interface']): continue llas = None for subnet_id, pd_info in router['subnets'].items(): if pd_info.client_started: prefix = pd_info.driver.get_prefix() if prefix != pd_info.prefix: pd_info.prefix = prefix prefix_update[subnet_id] = prefix else: if not llas: llas = self._get_llas(router['gw_interface'], router['ns_name']) if self._is_lla_active(pd_info.get_bind_lla_with_mask(), llas): if not pd_info.driver: pd_info.driver = self.pd_dhcp_driver( router_id, subnet_id, pd_info.ri_ifname) prefix = None if (pd_info.prefix != n_const.PROVISIONAL_IPV6_PD_PREFIX): prefix = pd_info.prefix pd_info.driver.enable(self.pmon, router['ns_name'], router['gw_interface'], pd_info.bind_lla, prefix) pd_info.client_started = True if prefix_update: LOG.debug("Update server with prefixes: %s", prefix_update) self.notifier(self.context, prefix_update) def after_start(self): LOG.debug('SIGUSR1 signal handler set') signal.signal(signal.SIGUSR1, self._handle_sigusr1) def _handle_sigusr1(self, signum, frame): """Update PD on receiving SIGUSR1. The external DHCPv6 client uses SIGUSR1 to notify agent of prefix changes. """ self.pd_update_cb() def _get_sync_data(self): sync_data = self.pd_dhcp_driver.get_sync_data() for pd_info in sync_data: router_id = pd_info.router_id if not self.routers.get(router_id): self.routers[router_id] = {'master': True, 'gw_interface': None, 'ns_name': None, 'subnets': {}} new_pd_info = PDInfo(pd_info=pd_info) subnets = self.routers[router_id]['subnets'] subnets[pd_info.subnet_id] = new_pd_info @runtime.synchronized("l3-agent-pd") def remove_router(resource, event, l3_agent, **kwargs): router_id = kwargs['router'].router_id router = l3_agent.pd.routers.get(router_id) l3_agent.pd.delete_router_pd(router) del l3_agent.pd.routers[router_id]['subnets'] del l3_agent.pd.routers[router_id] def get_router_entry(ns_name, master): return {'master': master, 'gw_interface': None, 'ns_name': ns_name, 'subnets': {}} @runtime.synchronized("l3-agent-pd") def add_router(resource, event, l3_agent, **kwargs): added_router = kwargs['router'] router = l3_agent.pd.routers.get(added_router.router_id) gw_ns_name = added_router.get_gw_ns_name() master = added_router.is_router_master() if not router: l3_agent.pd.routers[added_router.router_id] = ( get_router_entry(gw_ns_name, master)) else: # This will happen during l3 agent restart router['ns_name'] = gw_ns_name router['master'] = master @runtime.synchronized("l3-agent-pd") def update_router(resource, event, l3_agent, **kwargs): updated_router = kwargs['router'] router = l3_agent.pd.routers.get(updated_router.router_id) if not router: LOG.exception("Router to be updated is not in internal routers " "list: %s", updated_router.router_id) else: router['ns_name'] = updated_router.get_gw_ns_name() class PDInfo(object): """A class to simplify storing and passing of information relevant to Prefix Delegation operations for a given subnet. """ def __init__(self, pd_info=None, ri_ifname=None, mac=None): if pd_info is None: self.prefix = n_const.PROVISIONAL_IPV6_PD_PREFIX self.old_prefix = n_const.PROVISIONAL_IPV6_PD_PREFIX self.ri_ifname = ri_ifname self.mac = mac self.bind_lla = None self.sync = False self.driver = None self.client_started = False else: self.prefix = pd_info.prefix self.old_prefix = None self.ri_ifname = pd_info.ri_ifname self.mac = None self.bind_lla = None self.sync = True self.driver = pd_info.driver self.client_started = pd_info.client_started def get_bind_lla_with_mask(self): bind_lla_with_mask = '%s/64' % self.bind_lla return bind_lla_with_mask neutron-12.1.1/neutron/agent/linux/xenapi_root_helper.py0000664000175000017500000000677613553660047023530 0ustar zuulzuul00000000000000# Copyright (c) 2016 Citrix System. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """xenapi root helper For xenapi, we may need to run some commands in dom0 with additional privilege. This xenapi root helper contains the class of XenAPIClient to support it: XenAPIClient will keep a XenAPI session to dom0 and allow to run commands in dom0 via calling XenAPI plugin. The XenAPI plugin is responsible to determine whether a command is safe to execute. """ from os_xenapi.client import session from os_xenapi.client import XenAPI from oslo_config import cfg from oslo_log import log as logging from oslo_rootwrap import cmd as oslo_rootwrap_cmd from oslo_serialization import jsonutils from neutron.conf.agent import xenapi_conf ROOT_HELPER_DAEMON_TOKEN = 'xenapi_root_helper' RC_UNKNOWN_XENAPI_ERROR = 80 MSG_UNAUTHORIZED = "Unauthorized command" MSG_NOT_FOUND = "Executable not found" XENAPI_PLUGIN_FAILURE_ID = "XENAPI_PLUGIN_FAILURE" LOG = logging.getLogger(__name__) xenapi_conf.register_xenapi_opts(cfg.CONF) class XenAPIClient(object): def __init__(self): self._session = self._create_session( cfg.CONF.xenapi.connection_url, cfg.CONF.xenapi.connection_username, cfg.CONF.xenapi.connection_password) def _call_plugin(self, plugin, fn, args): return self._session.call_plugin(plugin, fn, args) def _create_session(self, url, username, password): return session.XenAPISession(url, username, password, originator="neutron") def _get_return_code(self, failure_details): # The details will be as: # [XENAPI_PLUGIN_FAILURE_ID, methodname, except_class_name, message] # We can distinguish the error type by checking the message string. if (len(failure_details) == 4 and XENAPI_PLUGIN_FAILURE_ID == failure_details[0]): if (MSG_UNAUTHORIZED == failure_details[3]): return oslo_rootwrap_cmd.RC_UNAUTHORIZED elif (MSG_NOT_FOUND == failure_details[3]): return oslo_rootwrap_cmd.RC_NOEXECFOUND # otherwise we get unexpected exception. return RC_UNKNOWN_XENAPI_ERROR def execute(self, cmd, stdin=None): out = "" err = "" if cmd is None or len(cmd) == 0: err = "No command specified." return oslo_rootwrap_cmd.RC_NOCOMMAND, out, err try: result_raw = self._call_plugin( 'netwrap.py', 'run_command', {'cmd': jsonutils.dumps(cmd), 'cmd_input': jsonutils.dumps(stdin)}) result = jsonutils.loads(result_raw) returncode = result['returncode'] out = result['out'] err = result['err'] return returncode, out, err except XenAPI.Failure as failure: LOG.exception('Failed to execute command: %s', cmd) returncode = self._get_return_code(failure.details) return returncode, out, err neutron-12.1.1/neutron/agent/linux/ip_conntrack.py0000664000175000017500000002453013553660047022300 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import re import eventlet import netaddr from oslo_concurrency import lockutils from oslo_log import log as logging from neutron.agent.linux import utils as linux_utils from neutron.common import constants as n_const from neutron.common import exceptions as n_exc LOG = logging.getLogger(__name__) CONTRACK_MGRS = {} MAX_CONNTRACK_ZONES = 65535 ZONE_START = 4097 WORKERS = 8 class IpConntrackUpdate(object): """Encapsulates a conntrack update An instance of this object carries the information necessary to process a request to update the conntrack table. """ def __init__(self, device_info_list, rule, remote_ips): self.device_info_list = device_info_list self.rule = rule self.remote_ips = remote_ips def __repr__(self): return ('' % (self.device_info_list, self.rule, self.remote_ips)) @lockutils.synchronized('conntrack') def get_conntrack(get_rules_for_table_func, filtered_ports, unfiltered_ports, execute=None, namespace=None, zone_per_port=False): try: return CONTRACK_MGRS[namespace] except KeyError: ipconntrack = IpConntrackManager(get_rules_for_table_func, filtered_ports, unfiltered_ports, execute, namespace, zone_per_port) CONTRACK_MGRS[namespace] = ipconntrack return CONTRACK_MGRS[namespace] class IpConntrackManager(object): """Smart wrapper for ip conntrack.""" def __init__(self, get_rules_for_table_func, filtered_ports, unfiltered_ports, execute=None, namespace=None, zone_per_port=False): self.get_rules_for_table_func = get_rules_for_table_func self.execute = execute or linux_utils.execute self.namespace = namespace self.filtered_ports = filtered_ports self.unfiltered_ports = unfiltered_ports self.zone_per_port = zone_per_port # zone per port vs per network self._populate_initial_zone_map() self._queue = eventlet.queue.LightQueue() self._start_process_queue() def _start_process_queue(self): LOG.debug("Starting ip_conntrack _process_queue_worker() threads") pool = eventlet.GreenPool(size=WORKERS) for i in range(WORKERS): pool.spawn_n(self._process_queue_worker) def _process_queue_worker(self): # While it's technically not necessary to have this method, the # 'while True' could just be in _process_queue(), the tests have # to be able to drain the queue without blocking, so _process_queue() # is made standalone. while True: self._process_queue() def _process_queue(self): update = None try: # this will block until an entry gets added to the queue update = self._queue.get() if update.remote_ips: for remote_ip in update.remote_ips: self._delete_conntrack_state( update.device_info_list, update.rule, remote_ip) else: self._delete_conntrack_state( update.device_info_list, update.rule) except Exception: LOG.exception("Failed to process ip_conntrack queue entry: %s", update) def _process(self, device_info_list, rule, remote_ips=None): # queue the update to allow the caller to resume its work update = IpConntrackUpdate(device_info_list, rule, remote_ips) self._queue.put(update) @staticmethod def _generate_conntrack_cmd_by_rule(rule, namespace): ethertype = rule.get('ethertype') protocol = rule.get('protocol') direction = rule.get('direction') cmd = ['conntrack', '-D'] if protocol: cmd.extend(['-p', str(protocol)]) cmd.extend(['-f', str(ethertype).lower()]) cmd.append('-d' if direction == 'ingress' else '-s') cmd_ns = [] if namespace: cmd_ns.extend(['ip', 'netns', 'exec', namespace]) cmd_ns.extend(cmd) return cmd_ns def _get_conntrack_cmds(self, device_info_list, rule, remote_ip=None): conntrack_cmds = set() cmd = self._generate_conntrack_cmd_by_rule(rule, self.namespace) ethertype = rule.get('ethertype') for device_info in device_info_list: zone_id = self.get_device_zone(device_info, create=False) if not zone_id: LOG.debug("No zone for device %(dev)s. Will not try to " "clear conntrack state. Zone map: %(zm)s", {'dev': device_info['device'], 'zm': self._device_zone_map}) continue ips = device_info.get('fixed_ips', []) for ip in ips: net = netaddr.IPNetwork(ip) if str(net.version) not in ethertype: continue ip_cmd = [str(net.ip), '-w', zone_id] if remote_ip and str( netaddr.IPNetwork(remote_ip).version) in ethertype: if rule.get('direction') == 'ingress': direction = '-s' else: direction = '-d' ip_cmd.extend([direction, str(remote_ip)]) conntrack_cmds.add(tuple(cmd + ip_cmd)) return conntrack_cmds def _delete_conntrack_state(self, device_info_list, rule, remote_ip=None): conntrack_cmds = self._get_conntrack_cmds(device_info_list, rule, remote_ip) for cmd in conntrack_cmds: try: self.execute(list(cmd), run_as_root=True, check_exit_code=True, extra_ok_codes=[1]) except RuntimeError: LOG.exception("Failed execute conntrack command %s", cmd) def delete_conntrack_state_by_rule(self, device_info_list, rule): self._process(device_info_list, rule) def delete_conntrack_state_by_remote_ips(self, device_info_list, ethertype, remote_ips): for direction in ['ingress', 'egress']: rule = {'ethertype': str(ethertype).lower(), 'direction': direction} self._process(device_info_list, rule, remote_ips) def _populate_initial_zone_map(self): """Setup the map between devices and zones based on current rules.""" self._device_zone_map = {} rules = self.get_rules_for_table_func('raw') for rule in rules: match = re.match(r'.* --physdev-in (?P[a-zA-Z0-9\-]+)' r'.* -j CT --zone (?P\d+).*', rule) if match: # strip off any prefix that the interface is using short_port_id = (match.group('dev') [n_const.LINUX_DEV_PREFIX_LEN:]) self._device_zone_map[short_port_id] = int(match.group('zone')) LOG.debug("Populated conntrack zone map: %s", self._device_zone_map) def _device_key(self, port): # we have to key the device_zone_map based on the fragment of the # UUID that shows up in the interface name. This is because the initial # map is populated strictly based on interface names that we don't know # the full UUID of. if self.zone_per_port: identifier = port['device'][n_const.LINUX_DEV_PREFIX_LEN:] else: identifier = port['network_id'] return identifier[:(n_const.LINUX_DEV_LEN - n_const.LINUX_DEV_PREFIX_LEN)] def get_device_zone(self, port, create=True): device_key = self._device_key(port) try: return self._device_zone_map[device_key] except KeyError: if create: return self._generate_device_zone(device_key) def _free_zones_from_removed_ports(self): """Clears any entries from the zone map of removed ports.""" existing_ports = [ self._device_key(port) for port in (list(self.filtered_ports.values()) + list(self.unfiltered_ports.values())) ] removed = set(self._device_zone_map) - set(existing_ports) for dev in removed: self._device_zone_map.pop(dev, None) def _generate_device_zone(self, short_device_id): """Generates a unique conntrack zone for the passed in ID.""" try: zone = self._find_open_zone() except n_exc.CTZoneExhaustedError: # Free some zones and try again, repeat failure will not be caught self._free_zones_from_removed_ports() zone = self._find_open_zone() self._device_zone_map[short_device_id] = zone LOG.debug("Assigned CT zone %(z)s to device %(dev)s.", {'z': zone, 'dev': short_device_id}) return self._device_zone_map[short_device_id] def _find_open_zone(self): # call set to dedup because old ports may be mapped to the same zone. zones_in_use = sorted(set(self._device_zone_map.values())) if not zones_in_use: return ZONE_START # attempt to increment onto the highest used zone first. if we hit the # end, go back and look for any gaps left by removed devices. last = zones_in_use[-1] if last < MAX_CONNTRACK_ZONES: return max(last + 1, ZONE_START) for index, used in enumerate(zones_in_use): if used - index != ZONE_START: # gap found, let's use it! return index + ZONE_START # conntrack zones exhausted :( :( raise n_exc.CTZoneExhaustedError() neutron-12.1.1/neutron/agent/linux/tc_lib.py0000664000175000017500000002163313553660047021063 0ustar zuulzuul00000000000000# Copyright 2016 OVH SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import re from neutron_lib import exceptions from neutron_lib.services.qos import constants as qos_consts from neutron._i18n import _ from neutron.agent.linux import ip_lib from neutron.common import constants from neutron.common import utils INGRESS_QDISC_ID = "ffff:" MAX_MTU_VALUE = 65535 LATENCY_UNIT = "ms" BW_LIMIT_UNIT = "kbit" # kilobits per second in tc's notation BURST_UNIT = "kbit" # kilobits in tc's notation # Those are RATES (bits per second) and SIZE (bytes) unit names from tc manual UNITS = { "k": 1, "m": 2, "g": 3, "t": 4 } filters_pattern = re.compile(r"police \w+ rate (\w+) burst (\w+)") tbf_pattern = re.compile( r"qdisc (\w+) \w+: \w+ refcnt \d rate (\w+) burst (\w+) \w*") class InvalidKernelHzValue(exceptions.NeutronException): message = _("Kernel HZ value %(value)s is not valid. This value must be " "greater than 0.") class InvalidUnit(exceptions.NeutronException): message = _("Unit name '%(unit)s' is not valid.") def convert_to_kilobits(value, base): value = value.lower() if "bit" in value: input_in_bits = True value = value.replace("bit", "") else: input_in_bits = False value = value.replace("b", "") # if it is now bare number then it is in bits, so we return it simply if value.isdigit(): value = int(value) if input_in_bits: return utils.bits_to_kilobits(value, base) else: bits_value = utils.bytes_to_bits(value) return utils.bits_to_kilobits(bits_value, base) unit = value[-1:] if unit not in UNITS.keys(): raise InvalidUnit(unit=unit) val = int(value[:-1]) if input_in_bits: bits_value = val * (base ** UNITS[unit]) else: bits_value = utils.bytes_to_bits(val * (base ** UNITS[unit])) return utils.bits_to_kilobits(bits_value, base) class TcCommand(ip_lib.IPDevice): def __init__(self, name, kernel_hz, namespace=None): if kernel_hz <= 0: raise InvalidKernelHzValue(value=kernel_hz) super(TcCommand, self).__init__(name, namespace=namespace) self.kernel_hz = kernel_hz def _execute_tc_cmd(self, cmd, **kwargs): cmd = ['tc'] + cmd ip_wrapper = ip_lib.IPWrapper(self.namespace) return ip_wrapper.netns.execute(cmd, run_as_root=True, **kwargs) @staticmethod def get_ingress_qdisc_burst_value(bw_limit, burst_limit): """Return burst value used in ingress qdisc. If burst value is not specified given than it will be set to default rate to ensure that limit for TCP traffic will work well """ if not burst_limit: return float(bw_limit) * qos_consts.DEFAULT_BURST_RATE return burst_limit def get_filters_bw_limits(self, qdisc_id=INGRESS_QDISC_ID): cmd = ['filter', 'show', 'dev', self.name, 'parent', qdisc_id] cmd_result = self._execute_tc_cmd(cmd) if not cmd_result: return None, None for line in cmd_result.split("\n"): m = filters_pattern.match(line.strip()) if m: #NOTE(slaweq): because tc is giving bw limit in SI units # we need to calculate it as 1000bit = 1kbit: bw_limit = convert_to_kilobits(m.group(1), constants.SI_BASE) #NOTE(slaweq): because tc is giving burst limit in IEC units # we need to calculate it as 1024bit = 1kbit: burst_limit = convert_to_kilobits( m.group(2), constants.IEC_BASE) return bw_limit, burst_limit return None, None def get_tbf_bw_limits(self): cmd = ['qdisc', 'show', 'dev', self.name] cmd_result = self._execute_tc_cmd(cmd) if not cmd_result: return None, None m = tbf_pattern.match(cmd_result) if not m: return None, None qdisc_name = m.group(1) if qdisc_name != "tbf": return None, None #NOTE(slaweq): because tc is giving bw limit in SI units # we need to calculate it as 1000bit = 1kbit: bw_limit = convert_to_kilobits(m.group(2), constants.SI_BASE) #NOTE(slaweq): because tc is giving burst limit in IEC units # we need to calculate it as 1024bit = 1kbit: burst_limit = convert_to_kilobits(m.group(3), constants.IEC_BASE) return bw_limit, burst_limit def set_filters_bw_limit(self, bw_limit, burst_limit): """Set ingress qdisc and filter for police ingress traffic on device This will allow to police traffic incoming to interface. It means that it is fine to limit egress traffic from instance point of view. """ #because replace of tc filters is not working properly and it's adding # new filters each time instead of replacing existing one first old # ingress qdisc should be deleted and then added new one so update will # be called to do that: return self.update_filters_bw_limit(bw_limit, burst_limit) def set_tbf_bw_limit(self, bw_limit, burst_limit, latency_value): """Set token bucket filter qdisc on device This will allow to limit speed of packets going out from interface. It means that it is fine to limit ingress traffic from instance point of view. """ return self._replace_tbf_qdisc(bw_limit, burst_limit, latency_value) def update_filters_bw_limit(self, bw_limit, burst_limit, qdisc_id=INGRESS_QDISC_ID): self.delete_filters_bw_limit() return self._set_filters_bw_limit(bw_limit, burst_limit, qdisc_id) def update_tbf_bw_limit(self, bw_limit, burst_limit, latency_value): return self._replace_tbf_qdisc(bw_limit, burst_limit, latency_value) def delete_filters_bw_limit(self): #NOTE(slaweq): For limit traffic egress from instance we need to use # qdisc "ingress" because it is ingress traffic from interface POV: self._delete_qdisc("ingress") def delete_tbf_bw_limit(self): self._delete_qdisc("root") def _set_filters_bw_limit(self, bw_limit, burst_limit, qdisc_id=INGRESS_QDISC_ID): cmd = ['qdisc', 'add', 'dev', self.name, 'ingress', 'handle', qdisc_id] self._execute_tc_cmd(cmd) return self._add_policy_filter(bw_limit, burst_limit) def _delete_qdisc(self, qdisc_name): cmd = ['qdisc', 'del', 'dev', self.name, qdisc_name] # Return_code=2 is fine because it means # "RTNETLINK answers: No such file or directory" what is fine when we # are trying to delete qdisc # Return_code=1 means "RTNETLINK answers: Cannot find device ". # If the device doesn't exist, the qdisc is already deleted. return self._execute_tc_cmd(cmd, extra_ok_codes=[1, 2]) def _get_tbf_burst_value(self, bw_limit, burst_limit): min_burst_value = float(bw_limit) / float(self.kernel_hz) return max(min_burst_value, burst_limit) def _replace_tbf_qdisc(self, bw_limit, burst_limit, latency_value): burst = "%s%s" % ( self._get_tbf_burst_value(bw_limit, burst_limit), BURST_UNIT) latency = "%s%s" % (latency_value, LATENCY_UNIT) rate_limit = "%s%s" % (bw_limit, BW_LIMIT_UNIT) cmd = [ 'qdisc', 'replace', 'dev', self.name, 'root', 'tbf', 'rate', rate_limit, 'latency', latency, 'burst', burst ] return self._execute_tc_cmd(cmd) def _add_policy_filter(self, bw_limit, burst_limit, qdisc_id=INGRESS_QDISC_ID): rate_limit = "%s%s" % (bw_limit, BW_LIMIT_UNIT) burst = "%s%s" % ( self.get_ingress_qdisc_burst_value(bw_limit, burst_limit), BURST_UNIT ) #NOTE(slaweq): it is made in exactly same way how openvswitch is doing # it when configuing ingress traffic limit on port. It can be found in # lib/netdev-linux.c#L4698 in openvswitch sources: cmd = [ 'filter', 'add', 'dev', self.name, 'parent', qdisc_id, 'protocol', 'all', 'prio', '49', 'basic', 'police', 'rate', rate_limit, 'burst', burst, 'mtu', MAX_MTU_VALUE, 'drop'] return self._execute_tc_cmd(cmd) neutron-12.1.1/neutron/agent/linux/l3_tc_lib.py0000664000175000017500000001653113553660047021462 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import re from neutron_lib import constants from oslo_log import log as logging from neutron.agent.linux import ip_lib from neutron.agent.linux import tc_lib from neutron.common import exceptions LOG = logging.getLogger(__name__) QDISC_IN_REGEX = re.compile(r"qdisc ingress (\w+:) *") QDISC_OUT_REGEX = re.compile(r"qdisc htb (\w+:) *") FILTER_ID_REGEX = re.compile(r"filter protocol ip u32 fh (\w+::\w+) *") FILTER_STATS_REGEX = re.compile(r"Sent (\w+) bytes (\w+) pkts *") class FloatingIPTcCommandBase(ip_lib.IPDevice): def _execute_tc_cmd(self, cmd, **kwargs): cmd = ['tc'] + cmd ip_wrapper = ip_lib.IPWrapper(self.namespace) return ip_wrapper.netns.execute(cmd, run_as_root=True, **kwargs) def _get_qdiscs(self): cmd = ['qdisc', 'show', 'dev', self.name] return self._execute_tc_cmd(cmd) def _get_qdisc_id_for_filter(self, direction): qdisc_results = self._get_qdiscs().split('\n') for qdisc in qdisc_results: pattern = (QDISC_OUT_REGEX if direction == constants.EGRESS_DIRECTION else QDISC_IN_REGEX) m = pattern.match(qdisc) if m: # No chance to get multiple qdiscs return m.group(1) def _add_qdisc(self, direction): if direction == constants.EGRESS_DIRECTION: args = ['root', 'handle', '1:', 'htb'] else: args = ['ingress'] cmd = ['qdisc', 'add', 'dev', self.name] + args self._execute_tc_cmd(cmd) def _get_filters(self, qdisc_id): cmd = ['-p', '-s', '-d', 'filter', 'show', 'dev', self.name, 'parent', qdisc_id, 'prio', 1] return self._execute_tc_cmd(cmd) def _get_filterid_for_ip(self, qdisc_id, ip): filterids_for_ip = [] filters_output = self._get_filters(qdisc_id) if not filters_output: raise exceptions.FilterIDForIPNotFound(ip=ip) filter_lines = filters_output.split('\n') for line in filter_lines: line = line.strip() m = FILTER_ID_REGEX.match(line) if m: filter_id = m.group(1) # It matched, so ip/32 is not here. continue continue elif not line.startswith('match'): continue parts = line.split(" ") if ip + '/32' in parts: filterids_for_ip.append(filter_id) if len(filterids_for_ip) > 1: raise exceptions.MultipleFilterIDForIPFound(ip=ip) elif len(filterids_for_ip) == 0: raise exceptions.FilterIDForIPNotFound(ip=ip) return filterids_for_ip[0] def _del_filter_by_id(self, qdisc_id, filter_id): cmd = ['filter', 'del', 'dev', self.name, 'parent', qdisc_id, 'prio', 1, 'handle', filter_id, 'u32'] self._execute_tc_cmd(cmd) def _get_qdisc_filters(self, qdisc_id): filterids = [] filters_output = self._get_filters(qdisc_id) if not filters_output: return filterids filter_lines = filters_output.split('\n') for line in filter_lines: line = line.strip() m = FILTER_ID_REGEX.match(line) if m: filter_id = m.group(1) filterids.append(filter_id) return filterids def _add_filter(self, qdisc_id, direction, ip, rate, burst): rate_value = "%s%s" % (rate, tc_lib.BW_LIMIT_UNIT) burst_value = "%s%s" % ( tc_lib.TcCommand.get_ingress_qdisc_burst_value(rate, burst), tc_lib.BURST_UNIT ) protocol = ['protocol', 'ip'] prio = ['prio', 1] _match = 'src' if direction == constants.EGRESS_DIRECTION else 'dst' match = ['u32', 'match', 'ip', _match, ip] police = ['police', 'rate', rate_value, 'burst', burst_value, 'mtu', '64kb', 'drop', 'flowid', ':1'] args = protocol + prio + match + police cmd = ['filter', 'add', 'dev', self.name, 'parent', qdisc_id] + args self._execute_tc_cmd(cmd) def _get_or_create_qdisc(self, direction): qdisc_id = self._get_qdisc_id_for_filter(direction) if not qdisc_id: self._add_qdisc(direction) qdisc_id = self._get_qdisc_id_for_filter(direction) if not qdisc_id: raise exceptions.FailedToAddQdiscToDevice(direction=direction, device=self.name) return qdisc_id class FloatingIPTcCommand(FloatingIPTcCommandBase): def clear_all_filters(self, direction): qdisc_id = self._get_qdisc_id_for_filter(direction) if not qdisc_id: return filterids = self._get_qdisc_filters(qdisc_id) for filter_id in filterids: self._del_filter_by_id(qdisc_id, filter_id) def get_filter_id_for_ip(self, direction, ip): qdisc_id = self._get_qdisc_id_for_filter(direction) if not qdisc_id: return return self._get_filterid_for_ip(qdisc_id, ip) def get_existing_filter_ids(self, direction): qdisc_id = self._get_qdisc_id_for_filter(direction) if not qdisc_id: return return self._get_qdisc_filters(qdisc_id) def delete_filter_ids(self, direction, filterids): qdisc_id = self._get_qdisc_id_for_filter(direction) if not qdisc_id: return for filter_id in filterids: self._del_filter_by_id(qdisc_id, filter_id) def set_ip_rate_limit(self, direction, ip, rate, burst): qdisc_id = self._get_or_create_qdisc(direction) try: filter_id = self._get_filterid_for_ip(qdisc_id, ip) LOG.debug("Filter %(filter)s for IP %(ip)s in %(direction)s " "qdisc already existed, removing.", {'filter': filter_id, 'ip': ip, 'direction': direction}) self._del_filter_by_id(qdisc_id, filter_id) except exceptions.FilterIDForIPNotFound: pass LOG.debug("Adding filter for IP %(ip)s in %(direction)s.", {'ip': ip, 'direction': direction}) self._add_filter(qdisc_id, direction, ip, rate, burst) def clear_ip_rate_limit(self, direction, ip): qdisc_id = self._get_qdisc_id_for_filter(direction) if not qdisc_id: return try: filter_id = self._get_filterid_for_ip(qdisc_id, ip) self._del_filter_by_id(qdisc_id, filter_id) except exceptions.FilterIDForIPNotFound: LOG.debug("No filter found for IP %(ip)s in %(direction)s, " "skipping deletion.", {'ip': ip, 'direction': direction}) neutron-12.1.1/neutron/agent/linux/dhcp.py0000664000175000017500000020344113553660047020544 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import collections import os import re import shutil import time import netaddr from neutron_lib.api.definitions import extra_dhcp_opt as edo_ext from neutron_lib import constants from neutron_lib import exceptions from neutron_lib.utils import file as file_utils from oslo_log import log as logging from oslo_utils import excutils from oslo_utils import fileutils from oslo_utils import uuidutils import six from neutron._i18n import _ from neutron.agent.common import utils as agent_common_utils from neutron.agent.linux import external_process from neutron.agent.linux import ip_lib from neutron.agent.linux import iptables_manager from neutron.cmd import runtime_checks as checks from neutron.common import constants as n_const from neutron.common import exceptions as n_exc from neutron.common import ipv6_utils from neutron.common import utils as common_utils from neutron.ipam import utils as ipam_utils LOG = logging.getLogger(__name__) UDP = 'udp' TCP = 'tcp' DNS_PORT = 53 DHCPV4_PORT = 67 DHCPV6_PORT = 547 METADATA_DEFAULT_PREFIX = 16 METADATA_DEFAULT_IP = '169.254.169.254' METADATA_DEFAULT_CIDR = '%s/%d' % (METADATA_DEFAULT_IP, METADATA_DEFAULT_PREFIX) METADATA_PORT = 80 WIN2k3_STATIC_DNS = 249 NS_PREFIX = 'qdhcp-' DNSMASQ_SERVICE_NAME = 'dnsmasq' DHCP_RELEASE_TRIES = 3 DHCP_RELEASE_TRIES_SLEEP = 0.3 # this variable will be removed when neutron-lib is updated with this value DHCP_OPT_CLIENT_ID_NUM = 61 class DictModel(dict): """Convert dict into an object that provides attribute access to values.""" def __init__(self, *args, **kwargs): """Convert dict values to DictModel values.""" super(DictModel, self).__init__(*args, **kwargs) def needs_upgrade(item): """Check if `item` is a dict and needs to be changed to DictModel. """ return isinstance(item, dict) and not isinstance(item, DictModel) def upgrade(item): """Upgrade item if it needs to be upgraded.""" if needs_upgrade(item): return DictModel(item) else: return item for key, value in self.items(): if isinstance(value, (list, tuple)): # Keep the same type but convert dicts to DictModels self[key] = type(value)( (upgrade(item) for item in value) ) elif needs_upgrade(value): # Change dict instance values to DictModel instance values self[key] = DictModel(value) def __getattr__(self, name): try: return self[name] except KeyError as e: raise AttributeError(e) def __setattr__(self, name, value): self[name] = value def __delattr__(self, name): del self[name] def __str__(self): pairs = ['%s=%s' % (k, v) for k, v in self.items()] return ', '.join(sorted(pairs)) class NetModel(DictModel): def __init__(self, d): super(NetModel, self).__init__(d) self._ns_name = "%s%s" % (NS_PREFIX, self.id) @property def namespace(self): return self._ns_name @six.add_metaclass(abc.ABCMeta) class DhcpBase(object): def __init__(self, conf, network, process_monitor, version=None, plugin=None): self.conf = conf self.network = network self.process_monitor = process_monitor self.device_manager = DeviceManager(self.conf, plugin) self.version = version @abc.abstractmethod def enable(self): """Enables DHCP for this network.""" @abc.abstractmethod def disable(self, retain_port=False, block=False): """Disable dhcp for this network.""" def restart(self): """Restart the dhcp service for the network.""" self.disable(retain_port=True, block=True) self.enable() @abc.abstractproperty def active(self): """Boolean representing the running state of the DHCP server.""" @abc.abstractmethod def reload_allocations(self): """Force the DHCP server to reload the assignment database.""" @classmethod def existing_dhcp_networks(cls, conf): """Return a list of existing networks ids that we have configs for.""" raise NotImplementedError() @classmethod def check_version(cls): """Execute version checks on DHCP server.""" raise NotImplementedError() @classmethod def get_isolated_subnets(cls, network): """Returns a dict indicating whether or not a subnet is isolated""" raise NotImplementedError() @classmethod def should_enable_metadata(cls, conf, network): """True if the metadata-proxy should be enabled for the network.""" raise NotImplementedError() @six.add_metaclass(abc.ABCMeta) class DhcpLocalProcess(DhcpBase): PORTS = [] def __init__(self, conf, network, process_monitor, version=None, plugin=None): super(DhcpLocalProcess, self).__init__(conf, network, process_monitor, version, plugin) self.confs_dir = self.get_confs_dir(conf) self.network_conf_dir = os.path.join(self.confs_dir, network.id) fileutils.ensure_tree(self.network_conf_dir, mode=0o755) @staticmethod def get_confs_dir(conf): return os.path.abspath(os.path.normpath(conf.dhcp_confs)) def get_conf_file_name(self, kind): """Returns the file name for a given kind of config file.""" return os.path.join(self.network_conf_dir, kind) def _remove_config_files(self): shutil.rmtree(self.network_conf_dir, ignore_errors=True) @staticmethod def _get_all_subnets(network): non_local_subnets = getattr(network, 'non_local_subnets', []) return network.subnets + non_local_subnets def _enable_dhcp(self): """check if there is a subnet within the network with dhcp enabled.""" for subnet in self.network.subnets: if subnet.enable_dhcp: return True return False def enable(self): """Enables DHCP for this network by spawning a local process.""" try: common_utils.wait_until_true(self._enable) except common_utils.WaitTimeout: LOG.error("Failed to start DHCP process for network %s", self.network.id) def _enable(self): try: if self.active: self.restart() elif self._enable_dhcp(): fileutils.ensure_tree(self.network_conf_dir, mode=0o755) interface_name = self.device_manager.setup(self.network) self.interface_name = interface_name self.spawn_process() return True except n_exc.ProcessExecutionError as error: LOG.debug("Spawning DHCP process for network %s failed; " "Error: %s", self.network.id, error) return False def _get_process_manager(self, cmd_callback=None): return external_process.ProcessManager( conf=self.conf, uuid=self.network.id, namespace=self.network.namespace, default_cmd_callback=cmd_callback, pid_file=self.get_conf_file_name('pid'), run_as_root=True) def disable(self, retain_port=False, block=False): """Disable DHCP for this network by killing the local process.""" self.process_monitor.unregister(self.network.id, DNSMASQ_SERVICE_NAME) self._get_process_manager().disable() if block: common_utils.wait_until_true(lambda: not self.active) if not retain_port: self._destroy_namespace_and_port() self._remove_config_files() def _destroy_namespace_and_port(self): try: self.device_manager.destroy(self.network, self.interface_name) except RuntimeError: LOG.warning('Failed trying to delete interface: %s', self.interface_name) if not ip_lib.network_namespace_exists(self.network.namespace): LOG.debug("Namespace already deleted: %s", self.network.namespace) return try: ip_lib.delete_network_namespace(self.network.namespace) except RuntimeError: LOG.warning('Failed trying to delete namespace: %s', self.network.namespace) def _get_value_from_conf_file(self, kind, converter=None): """A helper function to read a value from one of the state files.""" file_name = self.get_conf_file_name(kind) msg = _('Error while reading %s') try: with open(file_name, 'r') as f: try: return converter(f.read()) if converter else f.read() except ValueError: msg = _('Unable to convert value in %s') except IOError: msg = _('Unable to access %s') LOG.debug(msg, file_name) return None @property def interface_name(self): return self._get_value_from_conf_file('interface') @interface_name.setter def interface_name(self, value): interface_file_path = self.get_conf_file_name('interface') file_utils.replace_file(interface_file_path, value) @property def active(self): return self._get_process_manager().active @abc.abstractmethod def spawn_process(self): pass class Dnsmasq(DhcpLocalProcess): # The ports that need to be opened when security policies are active # on the Neutron port used for DHCP. These are provided as a convenience # for users of this class. PORTS = {constants.IP_VERSION_4: [(UDP, DNS_PORT), (TCP, DNS_PORT), (UDP, DHCPV4_PORT)], constants.IP_VERSION_6: [(UDP, DNS_PORT), (TCP, DNS_PORT), (UDP, DHCPV6_PORT)], } _TAG_PREFIX = 'tag%d' _ID = 'id:' _IS_DHCP_RELEASE6_SUPPORTED = None @classmethod def check_version(cls): pass @classmethod def existing_dhcp_networks(cls, conf): """Return a list of existing networks ids that we have configs for.""" confs_dir = cls.get_confs_dir(conf) try: return [ c for c in os.listdir(confs_dir) if uuidutils.is_uuid_like(c) ] except OSError: return [] def _build_cmdline_callback(self, pid_file): # We ignore local resolv.conf if dns servers are specified # or if local resolution is explicitly disabled. _no_resolv = ( '--no-resolv' if self.conf.dnsmasq_dns_servers or not self.conf.dnsmasq_local_resolv else '') cmd = [ 'dnsmasq', '--no-hosts', _no_resolv, '--pid-file=%s' % pid_file, '--dhcp-hostsfile=%s' % self.get_conf_file_name('host'), '--addn-hosts=%s' % self.get_conf_file_name('addn_hosts'), '--dhcp-optsfile=%s' % self.get_conf_file_name('opts'), '--dhcp-leasefile=%s' % self.get_conf_file_name('leases'), '--dhcp-match=set:ipxe,175', '--local-service', '--bind-dynamic', ] if not self.device_manager.driver.bridged: cmd += [ '--bridge-interface=%s,tap*' % self.interface_name, ] possible_leases = 0 for i, subnet in enumerate(self._get_all_subnets(self.network)): mode = None # if a subnet is specified to have dhcp disabled if not subnet.enable_dhcp: continue if subnet.ip_version == 4: mode = 'static' else: # Note(scollins) If the IPv6 attributes are not set, set it as # static to preserve previous behavior addr_mode = getattr(subnet, 'ipv6_address_mode', None) ra_mode = getattr(subnet, 'ipv6_ra_mode', None) if (addr_mode in [constants.DHCPV6_STATEFUL, constants.DHCPV6_STATELESS] or not addr_mode and not ra_mode): mode = 'static' cidr = netaddr.IPNetwork(subnet.cidr) if self.conf.dhcp_lease_duration == -1: lease = 'infinite' else: lease = '%ss' % self.conf.dhcp_lease_duration # mode is optional and is not set - skip it if mode: if subnet.ip_version == 4: cmd.append('--dhcp-range=%s%s,%s,%s,%s,%s' % ('set:', self._TAG_PREFIX % i, cidr.network, mode, cidr.netmask, lease)) else: if cidr.prefixlen < 64: LOG.debug('Ignoring subnet %(subnet)s, CIDR has ' 'prefix length < 64: %(cidr)s', {'subnet': subnet.id, 'cidr': cidr}) continue cmd.append('--dhcp-range=%s%s,%s,%s,%d,%s' % ('set:', self._TAG_PREFIX % i, cidr.network, mode, cidr.prefixlen, lease)) possible_leases += cidr.size mtu = getattr(self.network, 'mtu', 0) # Do not advertise unknown mtu if mtu > 0: cmd.append('--dhcp-option-force=option:mtu,%d' % mtu) # Cap the limit because creating lots of subnets can inflate # this possible lease cap. cmd.append('--dhcp-lease-max=%d' % min(possible_leases, self.conf.dnsmasq_lease_max)) if self.conf.dhcp_renewal_time > 0: cmd.append('--dhcp-option-force=option:T1,%ds' % self.conf.dhcp_renewal_time) if self.conf.dhcp_rebinding_time > 0: cmd.append('--dhcp-option-force=option:T2,%ds' % self.conf.dhcp_rebinding_time) cmd.append('--conf-file=%s' % self.conf.dnsmasq_config_file) for server in self.conf.dnsmasq_dns_servers: cmd.append('--server=%s' % server) if self.conf.dns_domain: cmd.append('--domain=%s' % self.conf.dns_domain) if self.conf.dhcp_broadcast_reply: cmd.append('--dhcp-broadcast') if self.conf.dnsmasq_base_log_dir: log_dir = os.path.join( self.conf.dnsmasq_base_log_dir, self.network.id) try: if not os.path.exists(log_dir): os.makedirs(log_dir) except OSError: LOG.error('Error while create dnsmasq log dir: %s', log_dir) else: log_filename = os.path.join(log_dir, 'dhcp_dns_log') cmd.append('--log-queries') cmd.append('--log-dhcp') cmd.append('--log-facility=%s' % log_filename) return cmd def spawn_process(self): """Spawn the process, if it's not spawned already.""" # we only need to generate the lease file the first time dnsmasq starts # rather than on every reload since dnsmasq will keep the file current self._output_init_lease_file() self._spawn_or_reload_process(reload_with_HUP=False) def _spawn_or_reload_process(self, reload_with_HUP): """Spawns or reloads a Dnsmasq process for the network. When reload_with_HUP is True, dnsmasq receives a HUP signal, or it's reloaded if the process is not running. """ self._output_config_files() pm = self._get_process_manager( cmd_callback=self._build_cmdline_callback) pm.enable(reload_cfg=reload_with_HUP) self.process_monitor.register(uuid=self.network.id, service_name=DNSMASQ_SERVICE_NAME, monitored_process=pm) def _is_dhcp_release6_supported(self): if self._IS_DHCP_RELEASE6_SUPPORTED is None: self._IS_DHCP_RELEASE6_SUPPORTED = checks.dhcp_release6_supported() if not self._IS_DHCP_RELEASE6_SUPPORTED: LOG.warning("dhcp_release6 is not present on this system, " "will not call it again.") return self._IS_DHCP_RELEASE6_SUPPORTED def _release_lease(self, mac_address, ip, ip_version, client_id=None, server_id=None, iaid=None): """Release a DHCP lease.""" if ip_version == constants.IP_VERSION_6: if not self._is_dhcp_release6_supported(): return cmd = ['dhcp_release6', '--iface', self.interface_name, '--ip', ip, '--client-id', client_id, '--server-id', server_id, '--iaid', iaid] else: cmd = ['dhcp_release', self.interface_name, ip, mac_address] if client_id: cmd.append(client_id) ip_wrapper = ip_lib.IPWrapper(namespace=self.network.namespace) try: ip_wrapper.netns.execute(cmd, run_as_root=True) except RuntimeError as e: # when failed to release single lease there's # no need to propagate error further LOG.warning('DHCP release failed for %(cmd)s. ' 'Reason: %(e)s', {'cmd': cmd, 'e': e}) def _output_config_files(self): self._output_hosts_file() self._output_addn_hosts_file() self._output_opts_file() def reload_allocations(self): """Rebuild the dnsmasq config and signal the dnsmasq to reload.""" # If all subnets turn off dhcp, kill the process. if not self._enable_dhcp(): self.disable() LOG.debug('Killing dnsmasq for network since all subnets have ' 'turned off DHCP: %s', self.network.id) return if not self.interface_name: # we land here if above has been called and we receive port # delete notifications for the network LOG.debug('Agent does not have an interface on this network ' 'anymore, skipping reload: %s', self.network.id) return self._release_unused_leases() self._spawn_or_reload_process(reload_with_HUP=True) LOG.debug('Reloading allocations for network: %s', self.network.id) self.device_manager.update(self.network, self.interface_name) def _sort_fixed_ips_for_dnsmasq(self, fixed_ips, v6_nets): """Sort fixed_ips so that stateless IPv6 subnets appear first. For example, If a port with v6 extra_dhcp_opts is on a network with IPv4 and IPv6 stateless subnets. Then dhcp host file will have below 2 entries for same MAC, fa:16:3e:8f:9d:65,30.0.0.5,set:aabc7d33-4874-429e-9637-436e4232d2cd (entry for IPv4 dhcp) fa:16:3e:8f:9d:65,set:aabc7d33-4874-429e-9637-436e4232d2cd (entry for stateless IPv6 for v6 options) dnsmasq internal details for processing host file entries 1) dnsmasq reads the host file from EOF. 2) So it first picks up stateless IPv6 entry, fa:16:3e:8f:9d:65,set:aabc7d33-4874-429e-9637-436e4232d2cd 3) But dnsmasq doesn't have sufficient checks to skip this entry and pick next entry, to process dhcp IPv4 request. 4) So dnsmasq uses this entry to process dhcp IPv4 request. 5) As there is no ip in this entry, dnsmasq logs "no address available" and fails to send DHCPOFFER message. As we rely on internal details of dnsmasq to understand and fix the issue, Ihar sent a mail to dnsmasq-discuss mailing list http://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2015q2/ 009650.html So if we reverse the order of writing entries in host file, so that entry for stateless IPv6 comes first, then dnsmasq can correctly fetch the IPv4 address. """ return sorted( fixed_ips, key=lambda fip: ((fip.subnet_id in v6_nets) and ( v6_nets[fip.subnet_id].ipv6_address_mode == ( constants.DHCPV6_STATELESS))), reverse=True) def _iter_hosts(self): """Iterate over hosts. For each host on the network we yield a tuple containing: ( port, # a DictModel instance representing the port. alloc, # a DictModel instance of the allocated ip and subnet. # if alloc is None, it means there is no need to allocate # an IPv6 address because of stateless DHCPv6 network. host_name, # Host name. name, # Canonical hostname in the format 'hostname[.domain]'. no_dhcp, # A flag indicating that the address doesn't need a DHCP # IP address. no_opts, # A flag indication that options shouldn't be written ) """ v6_nets = dict((subnet.id, subnet) for subnet in self._get_all_subnets(self.network) if subnet.ip_version == 6) for port in self.network.ports: fixed_ips = self._sort_fixed_ips_for_dnsmasq(port.fixed_ips, v6_nets) # Confirm whether Neutron server supports dns_name attribute in the # ports API dns_assignment = getattr(port, 'dns_assignment', None) if dns_assignment: dns_ip_map = {d.ip_address: d for d in dns_assignment} for alloc in fixed_ips: no_dhcp = False no_opts = False if alloc.subnet_id in v6_nets: addr_mode = v6_nets[alloc.subnet_id].ipv6_address_mode no_dhcp = addr_mode in (constants.IPV6_SLAAC, constants.DHCPV6_STATELESS) # we don't setup anything for SLAAC. It doesn't make sense # to provide options for a client that won't use DHCP no_opts = addr_mode == constants.IPV6_SLAAC # If dns_name attribute is supported by ports API, return the # dns_assignment generated by the Neutron server. Otherwise, # generate hostname and fqdn locally (previous behaviour) if dns_assignment: hostname = dns_ip_map[alloc.ip_address].hostname fqdn = dns_ip_map[alloc.ip_address].fqdn else: hostname = 'host-%s' % alloc.ip_address.replace( '.', '-').replace(':', '-') fqdn = hostname if self.conf.dns_domain: fqdn = '%s.%s' % (fqdn, self.conf.dns_domain) yield (port, alloc, hostname, fqdn, no_dhcp, no_opts) def _get_port_extra_dhcp_opts(self, port): return getattr(port, edo_ext.EXTRADHCPOPTS, False) def _output_init_lease_file(self): """Write a fake lease file to bootstrap dnsmasq. The generated file is passed to the --dhcp-leasefile option of dnsmasq. This is used as a bootstrapping mechanism to avoid NAKing active leases when a dhcp server is scheduled to another agent. Using a leasefile will also prevent dnsmasq from NAKing or ignoring renewals after a restart. Format is as follows: epoch-timestamp mac_addr ip_addr hostname client-ID """ filename = self.get_conf_file_name('leases') buf = six.StringIO() LOG.debug('Building initial lease file: %s', filename) # we make up a lease time for the database entry if self.conf.dhcp_lease_duration == -1: # Even with an infinite lease, a client may choose to renew a # previous lease on reboot or interface bounce so we should have # an entry for it. # Dnsmasq timestamp format for an infinite lease is 0. timestamp = 0 else: timestamp = int(time.time()) + self.conf.dhcp_lease_duration dhcpv4_enabled_subnet_ids = [ s.id for s in self._get_all_subnets(self.network) if s.enable_dhcp and s.ip_version == constants.IP_VERSION_4] for host_tuple in self._iter_hosts(): port, alloc, hostname, name, no_dhcp, no_opts = host_tuple # don't write ip address which belongs to a dhcp disabled subnet # or an IPv6 subnet. if no_dhcp or alloc.subnet_id not in dhcpv4_enabled_subnet_ids: continue # all that matters is the mac address and IP. the hostname and # client ID will be overwritten on the next renewal. buf.write('%s %s %s * *\n' % (timestamp, port.mac_address, alloc.ip_address)) contents = buf.getvalue() file_utils.replace_file(filename, contents) LOG.debug('Done building initial lease file %s with contents:\n%s', filename, contents) return filename @staticmethod def _format_address_for_dnsmasq(address): # (dzyu) Check if it is legal ipv6 address, if so, need wrap # it with '[]' to let dnsmasq to distinguish MAC address from # IPv6 address. if netaddr.valid_ipv6(address): return '[%s]' % address return address def _output_hosts_file(self): """Writes a dnsmasq compatible dhcp hosts file. The generated file is sent to the --dhcp-hostsfile option of dnsmasq, and lists the hosts on the network which should receive a dhcp lease. Each line in this file is in the form:: 'mac_address,FQDN,ip_address' IMPORTANT NOTE: a dnsmasq instance does not resolve hosts defined in this file if it did not give a lease to a host listed in it (e.g.: multiple dnsmasq instances on the same network if this network is on multiple network nodes). This file is only defining hosts which should receive a dhcp lease, the hosts resolution in itself is defined by the `_output_addn_hosts_file` method. """ buf = six.StringIO() filename = self.get_conf_file_name('host') LOG.debug('Building host file: %s', filename) dhcp_enabled_subnet_ids = [s.id for s in self._get_all_subnets(self.network) if s.enable_dhcp] # NOTE(ihrachyshka): the loop should not log anything inside it, to # avoid potential performance drop when lots of hosts are dumped for host_tuple in self._iter_hosts(): port, alloc, hostname, name, no_dhcp, no_opts = host_tuple if no_dhcp: if not no_opts and self._get_port_extra_dhcp_opts(port): buf.write('%s,%s%s\n' % (port.mac_address, 'set:', port.id)) continue # don't write ip address which belongs to a dhcp disabled subnet. if alloc.subnet_id not in dhcp_enabled_subnet_ids: continue ip_address = self._format_address_for_dnsmasq(alloc.ip_address) if self._get_port_extra_dhcp_opts(port): client_id = self._get_client_id(port) if client_id and len(port.extra_dhcp_opts) > 1: buf.write('%s,%s%s,%s,%s,%s%s\n' % (port.mac_address, self._ID, client_id, name, ip_address, 'set:', port.id)) elif client_id and len(port.extra_dhcp_opts) == 1: buf.write('%s,%s%s,%s,%s\n' % (port.mac_address, self._ID, client_id, name, ip_address)) else: buf.write('%s,%s,%s,%s%s\n' % (port.mac_address, name, ip_address, 'set:', port.id)) else: buf.write('%s,%s,%s\n' % (port.mac_address, name, ip_address)) file_utils.replace_file(filename, buf.getvalue()) LOG.debug('Done building host file %s', filename) return filename def _get_client_id(self, port): if self._get_port_extra_dhcp_opts(port): for opt in port.extra_dhcp_opts: if opt.opt_name in (edo_ext.DHCP_OPT_CLIENT_ID, DHCP_OPT_CLIENT_ID_NUM): return opt.opt_value def _read_hosts_file_leases(self, filename): leases = set() try: with open(filename) as f: for l in f.readlines(): host = l.strip().split(',') mac = host[0] client_id = None if host[1].startswith('set:'): continue if host[1].startswith(self._ID): ip = host[3].strip('[]') client_id = host[1][len(self._ID):] else: ip = host[2].strip('[]') leases.add((ip, mac, client_id)) except (OSError, IOError): LOG.debug('Error while reading hosts file %s', filename) return leases def _read_leases_file_leases(self, filename, ip_version=None): """ Read information from leases file, which is needed to pass to dhcp_release6 command line utility if some of these leases are not needed anymore each line in dnsmasq leases file is one of the following * duid entry: duid server_duid There MUST be single duid entry per file * ipv4 entry: space separated list - The expiration time (seconds since unix epoch) or duration (if dnsmasq is compiled with HAVE_BROKEN_RTC) of the lease. 0 means infinite. - The link address, in format XX-YY:YY:YY[...], where XX is the ARP hardware type. "XX-" may be omitted for Ethernet. - The IPv4 address - The hostname (sent by the client or assigned by dnsmasq) or '*' for none. - The client identifier (colon-separated hex bytes) or '*' for none. * ipv6 entry: space separated list - The expiration time or duration - The IAID as a Big Endian decimal number, prefixed by T for IA_TAs (temporary addresses). - The IPv6 address - The hostname or '*' - The client DUID (colon-separated hex bytes) or '*' if unknown original discussion is in dnsmasq mailing list http://lists.thekelleys.org.uk/pipermail/\ dnsmasq-discuss/2016q2/010595.html :param filename: leases file :param ip_version: IP version of entries to return, or None for all :return: dict, keys are IP(v6) addresses, values are dicts containing iaid, client_id and server_id """ leases = {} server_id = None if os.path.exists(filename): with open(filename) as f: for l in f.readlines(): if l.startswith('duid'): if not server_id: server_id = l.strip().split()[1] continue else: LOG.warning('Multiple DUID entries in %s ' 'lease file, dnsmasq is possibly ' 'not functioning properly', filename) continue parts = l.strip().split() if len(parts) != 5: LOG.warning('Invalid lease entry %s found in %s ' 'lease file, ignoring', parts, filename) continue (iaid, ip, client_id) = parts[1], parts[2], parts[4] ip = ip.strip('[]') if (ip_version and netaddr.IPAddress(ip).version != ip_version): continue leases[ip] = {'iaid': iaid, 'client_id': client_id, 'server_id': server_id } return leases def _release_unused_leases(self): filename = self.get_conf_file_name('host') old_leases = self._read_hosts_file_leases(filename) leases_filename = self.get_conf_file_name('leases') cur_leases = self._read_leases_file_leases(leases_filename) if not cur_leases: return v4_leases = set() for (k, v) in cur_leases.items(): # IPv4 leases have a MAC, IPv6 ones do not, so we must ignore if netaddr.IPAddress(k).version == constants.IP_VERSION_4: # treat '*' as None, see note in _read_leases_file_leases() client_id = v['client_id'] if client_id is '*': client_id = None v4_leases.add((k, v['iaid'], client_id)) new_leases = set() for port in self.network.ports: client_id = self._get_client_id(port) for alloc in port.fixed_ips: new_leases.add((alloc.ip_address, port.mac_address, client_id)) # If an entry is in the leases or host file(s), but doesn't have # a fixed IP on a corresponding neutron port, consider it stale. entries_to_release = (v4_leases | old_leases) - new_leases if not entries_to_release: return # If the VM advertises a client ID in its lease, but its not set in # the port's Extra DHCP Opts, the lease will not be filtered above. # Release the lease only if client ID is set in port DB and a mismatch # Otherwise the lease is released when other ports are deleted/updated entries_with_no_client_id = set() for ip, mac, client_id in entries_to_release: if client_id: entry_no_client_id = (ip, mac, None) if (entry_no_client_id in old_leases and entry_no_client_id in new_leases): entries_with_no_client_id.add((ip, mac, client_id)) entries_to_release -= entries_with_no_client_id # Try DHCP_RELEASE_TRIES times to release a lease, re-reading the # file each time to see if it's still there. We loop +1 times to # check the lease file one last time before logging any remaining # entries. for i in range(DHCP_RELEASE_TRIES + 1): entries_not_present = set() for ip, mac, client_id in entries_to_release: try: entry = cur_leases[ip] except KeyError: entries_not_present.add((ip, mac, client_id)) continue # if not the final loop, try and release if i < DHCP_RELEASE_TRIES: ip_version = netaddr.IPAddress(ip).version if ip_version == constants.IP_VERSION_6: client_id = entry['client_id'] self._release_lease(mac, ip, ip_version, client_id, entry['server_id'], entry['iaid']) # Remove elements that were not in the current leases file, # no need to look for them again, and see if we're done. entries_to_release -= entries_not_present if not entries_to_release: break if i < DHCP_RELEASE_TRIES: time.sleep(DHCP_RELEASE_TRIES_SLEEP) cur_leases = self._read_leases_file_leases(leases_filename) if not cur_leases: break else: LOG.warning("Could not release DHCP leases for these IP " "addresses after %d tries: %s", DHCP_RELEASE_TRIES, ', '.join(ip for ip, m, c in entries_to_release)) def _output_addn_hosts_file(self): """Writes a dnsmasq compatible additional hosts file. The generated file is sent to the --addn-hosts option of dnsmasq, and lists the hosts on the network which should be resolved even if the dnsmasq instance did not give a lease to the host (see the `_output_hosts_file` method). Each line in this file is in the same form as a standard /etc/hosts file. """ buf = six.StringIO() for host_tuple in self._iter_hosts(): port, alloc, hostname, fqdn, no_dhcp, no_opts = host_tuple # It is compulsory to write the `fqdn` before the `hostname` in # order to obtain it in PTR responses. if alloc: buf.write('%s\t%s %s\n' % (alloc.ip_address, fqdn, hostname)) addn_hosts = self.get_conf_file_name('addn_hosts') file_utils.replace_file(addn_hosts, buf.getvalue()) return addn_hosts def _output_opts_file(self): """Write a dnsmasq compatible options file.""" options, subnet_index_map = self._generate_opts_per_subnet() options += self._generate_opts_per_port(subnet_index_map) name = self.get_conf_file_name('opts') file_utils.replace_file(name, '\n'.join(options)) return name def _generate_opts_per_subnet(self): options = [] subnet_index_map = {} if self.conf.enable_isolated_metadata or self.conf.force_metadata: subnet_to_interface_ip = self._make_subnet_interface_ip_map() isolated_subnets = self.get_isolated_subnets(self.network) for i, subnet in enumerate(self._get_all_subnets(self.network)): addr_mode = getattr(subnet, 'ipv6_address_mode', None) segment_id = getattr(subnet, 'segment_id', None) if (not subnet.enable_dhcp or (subnet.ip_version == 6 and addr_mode == constants.IPV6_SLAAC)): continue if subnet.dns_nameservers: if ((subnet.ip_version == 4 and subnet.dns_nameservers == ['0.0.0.0']) or (subnet.ip_version == 6 and subnet.dns_nameservers == ['::'])): # Special case: Do not announce DNS servers options.append( self._format_option( subnet.ip_version, i, 'dns-server')) else: options.append( self._format_option( subnet.ip_version, i, 'dns-server', ','.join( Dnsmasq._convert_to_literal_addrs( subnet.ip_version, subnet.dns_nameservers)))) else: # use the dnsmasq ip as nameservers only if there is no # dns-server submitted by the server subnet_index_map[subnet.id] = i if self.conf.dns_domain and subnet.ip_version == 6: options.append('tag:tag%s,option6:domain-search,%s' % (i, ''.join(self.conf.dns_domain))) gateway = subnet.gateway_ip host_routes = [] for hr in subnet.host_routes: if hr.destination == constants.IPv4_ANY: if not gateway: gateway = hr.nexthop else: host_routes.append("%s,%s" % (hr.destination, hr.nexthop)) # Add host routes for isolated network segments if ((self.conf.force_metadata or (isolated_subnets[subnet.id] and self.conf.enable_isolated_metadata)) and subnet.ip_version == 4): subnet_dhcp_ip = subnet_to_interface_ip.get(subnet.id) if subnet_dhcp_ip: host_routes.append( '%s/32,%s' % (METADATA_DEFAULT_IP, subnet_dhcp_ip) ) elif not isolated_subnets[subnet.id] and gateway: host_routes.append( '%s/32,%s' % (METADATA_DEFAULT_IP, gateway) ) if subnet.ip_version == 4: for s in self._get_all_subnets(self.network): sub_segment_id = getattr(s, 'segment_id', None) if (s.ip_version == 4 and s.cidr != subnet.cidr and sub_segment_id == segment_id): host_routes.append("%s,0.0.0.0" % s.cidr) if host_routes: if gateway: host_routes.append("%s,%s" % (constants.IPv4_ANY, gateway)) options.append( self._format_option(subnet.ip_version, i, 'classless-static-route', ','.join(host_routes))) options.append( self._format_option(subnet.ip_version, i, WIN2k3_STATIC_DNS, ','.join(host_routes))) if gateway: options.append(self._format_option(subnet.ip_version, i, 'router', gateway)) else: options.append(self._format_option(subnet.ip_version, i, 'router')) return options, subnet_index_map def _generate_opts_per_port(self, subnet_index_map): options = [] dhcp_ips = collections.defaultdict(list) for port in self.network.ports: if self._get_port_extra_dhcp_opts(port): port_ip_versions = set( [netaddr.IPAddress(ip.ip_address).version for ip in port.fixed_ips]) for opt in port.extra_dhcp_opts: if opt.opt_name in (edo_ext.DHCP_OPT_CLIENT_ID, DHCP_OPT_CLIENT_ID_NUM): continue opt_ip_version = opt.ip_version if opt_ip_version in port_ip_versions: options.append( self._format_option(opt_ip_version, port.id, opt.opt_name, opt.opt_value)) else: LOG.info("Cannot apply dhcp option %(opt)s " "because it's ip_version %(version)d " "is not in port's address IP versions", {'opt': opt.opt_name, 'version': opt_ip_version}) # provides all dnsmasq ip as dns-server if there is more than # one dnsmasq for a subnet and there is no dns-server submitted # by the server if port.device_owner == constants.DEVICE_OWNER_DHCP: for ip in port.fixed_ips: i = subnet_index_map.get(ip.subnet_id) if i is None: continue dhcp_ips[i].append(ip.ip_address) for i, ips in dhcp_ips.items(): for ip_version in (4, 6): vx_ips = [ip for ip in ips if netaddr.IPAddress(ip).version == ip_version] if len(vx_ips) > 1: options.append( self._format_option( ip_version, i, 'dns-server', ','.join( Dnsmasq._convert_to_literal_addrs(ip_version, vx_ips)))) return options def _make_subnet_interface_ip_map(self): ip_dev = ip_lib.IPDevice(self.interface_name, namespace=self.network.namespace) subnet_lookup = dict( (netaddr.IPNetwork(subnet.cidr), subnet.id) for subnet in self.network.subnets ) retval = {} for addr in ip_dev.addr.list(): ip_net = netaddr.IPNetwork(addr['cidr']) if ip_net in subnet_lookup: retval[subnet_lookup[ip_net]] = addr['cidr'].split('/')[0] return retval def _format_option(self, ip_version, tag, option, *args): """Format DHCP option by option name or code.""" option = str(option) pattern = "(tag:(.*),)?(.*)$" matches = re.match(pattern, option) extra_tag = matches.groups()[0] option = matches.groups()[2] if isinstance(tag, int): tag = self._TAG_PREFIX % tag if not option.isdigit(): if ip_version == 4: option = 'option:%s' % option else: option = 'option6:%s' % option if extra_tag: tags = ('tag:' + tag, extra_tag[:-1], '%s' % option) else: tags = ('tag:' + tag, '%s' % option) return ','.join(tags + args) @staticmethod def _convert_to_literal_addrs(ip_version, ips): if ip_version == 4: return ips return ['[' + ip + ']' for ip in ips] @classmethod def get_isolated_subnets(cls, network): """Returns a dict indicating whether or not a subnet is isolated A subnet is considered non-isolated if there is a port connected to the subnet, and the port's ip address matches that of the subnet's gateway. The port must be owned by a neutron router. """ isolated_subnets = collections.defaultdict(lambda: True) all_subnets = cls._get_all_subnets(network) subnets = dict((subnet.id, subnet) for subnet in all_subnets) for port in network.ports: if port.device_owner not in constants.ROUTER_INTERFACE_OWNERS: continue for alloc in port.fixed_ips: if (alloc.subnet_id in subnets and subnets[alloc.subnet_id].gateway_ip == alloc.ip_address): isolated_subnets[alloc.subnet_id] = False return isolated_subnets @staticmethod def has_metadata_subnet(subnets): """Check if the subnets has a metadata subnet.""" meta_cidr = netaddr.IPNetwork(METADATA_DEFAULT_CIDR) if any(netaddr.IPNetwork(s.cidr) in meta_cidr for s in subnets): return True return False @classmethod def should_enable_metadata(cls, conf, network): """Determine whether the metadata proxy is needed for a network This method returns True for truly isolated networks (ie: not attached to a router) when enable_isolated_metadata is True, or for all the networks when the force_metadata flags is True. This method also returns True when enable_metadata_network is True, and the network passed as a parameter has a subnet in the link-local CIDR, thus characterizing it as a "metadata" network. The metadata network is used by solutions which do not leverage the l3 agent for providing access to the metadata service via logical routers built with 3rd party backends. """ # Only IPv4 subnets, with dhcp enabled, will use the metadata proxy. all_subnets = cls._get_all_subnets(network) v4_dhcp_subnets = [s for s in all_subnets if s.ip_version == 4 and s.enable_dhcp] if not v4_dhcp_subnets: return False if conf.force_metadata: return True if not conf.enable_isolated_metadata: return False if (conf.enable_metadata_network and cls.has_metadata_subnet(all_subnets)): return True isolated_subnets = cls.get_isolated_subnets(network) return any(isolated_subnets[s.id] for s in v4_dhcp_subnets) class DeviceManager(object): def __init__(self, conf, plugin): self.conf = conf self.plugin = plugin self.driver = agent_common_utils.load_interface_driver(conf) def get_interface_name(self, network, port): """Return interface(device) name for use by the DHCP process.""" return self.driver.get_device_name(port) def get_device_id(self, network): """Return a unique DHCP device ID for this host on the network.""" # There could be more than one dhcp server per network, so create # a device id that combines host and network ids return common_utils.get_dhcp_agent_device_id(network.id, self.conf.host) def _set_default_route_ip_version(self, network, device_name, ip_version): device = ip_lib.IPDevice(device_name, namespace=network.namespace) gateway = device.route.get_gateway(ip_version=ip_version) if gateway: gateway = gateway.get('gateway') for subnet in network.subnets: skip_subnet = ( subnet.ip_version != ip_version or not subnet.enable_dhcp or subnet.gateway_ip is None) if skip_subnet: continue if subnet.ip_version == constants.IP_VERSION_6: # This is duplicating some of the API checks already done, # but some of the functional tests call directly prefixlen = netaddr.IPNetwork(subnet.cidr).prefixlen if prefixlen == 0 or prefixlen > 126: continue modes = [constants.IPV6_SLAAC, constants.DHCPV6_STATELESS] addr_mode = getattr(subnet, 'ipv6_address_mode', None) ra_mode = getattr(subnet, 'ipv6_ra_mode', None) if (prefixlen != 64 and (addr_mode in modes or ra_mode in modes)): continue if gateway != subnet.gateway_ip: LOG.debug('Setting IPv%(version)s gateway for dhcp netns ' 'on net %(n)s to %(ip)s', {'n': network.id, 'ip': subnet.gateway_ip, 'version': ip_version}) # Check for and remove the on-link route for the old # gateway being replaced, if it is outside the subnet is_old_gateway_not_in_subnet = (gateway and not ipam_utils.check_subnet_ip( subnet.cidr, gateway)) if is_old_gateway_not_in_subnet: onlink = device.route.list_onlink_routes(ip_version) existing_onlink_routes = set(r['cidr'] for r in onlink) if gateway in existing_onlink_routes: device.route.delete_route(gateway, scope='link') is_new_gateway_not_in_subnet = (subnet.gateway_ip and not ipam_utils.check_subnet_ip( subnet.cidr, subnet.gateway_ip)) if is_new_gateway_not_in_subnet: device.route.add_route(subnet.gateway_ip, scope='link') device.route.add_gateway(subnet.gateway_ip) return # No subnets on the network have a valid gateway. Clean it up to avoid # confusion from seeing an invalid gateway here. if gateway is not None: LOG.debug('Removing IPv%(version)s gateway for dhcp netns on ' 'net %(n)s', {'n': network.id, 'version': ip_version}) device.route.delete_gateway(gateway) def _set_default_route(self, network, device_name): """Sets the default gateway for this dhcp namespace. This method is idempotent and will only adjust the route if adjusting it would change it from what it already is. This makes it safe to call and avoids unnecessary perturbation of the system. """ for ip_version in (constants.IP_VERSION_4, constants.IP_VERSION_6): self._set_default_route_ip_version(network, device_name, ip_version) def _setup_existing_dhcp_port(self, network, device_id, dhcp_subnets): """Set up the existing DHCP port, if there is one.""" # To avoid pylint thinking that port might be undefined after # the following loop... port = None # Look for an existing DHCP port for this network. for port in network.ports: port_device_id = getattr(port, 'device_id', None) if port_device_id == device_id: # If using gateway IPs on this port, we can skip the # following code, whose purpose is just to review and # update the Neutron-allocated IP addresses for the # port. if self.driver.use_gateway_ips: return port # Otherwise break out, as we now have the DHCP port # whose subnets and addresses we need to review. break else: return None # Compare what the subnets should be against what is already # on the port. dhcp_enabled_subnet_ids = set(dhcp_subnets) port_subnet_ids = set(ip.subnet_id for ip in port.fixed_ips) # If those differ, we need to call update. if dhcp_enabled_subnet_ids != port_subnet_ids: # Collect the subnets and fixed IPs that the port already # has, for subnets that are still in the DHCP-enabled set. wanted_fixed_ips = [] for fixed_ip in port.fixed_ips: if fixed_ip.subnet_id in dhcp_enabled_subnet_ids: wanted_fixed_ips.append( {'subnet_id': fixed_ip.subnet_id, 'ip_address': fixed_ip.ip_address}) # Add subnet IDs for new DHCP-enabled subnets. wanted_fixed_ips.extend( dict(subnet_id=s) for s in dhcp_enabled_subnet_ids - port_subnet_ids) # Update the port to have the calculated subnets and fixed # IPs. The Neutron server will allocate a fresh IP for # each subnet that doesn't already have one. port = self.plugin.update_dhcp_port( port.id, {'port': {'network_id': network.id, 'fixed_ips': wanted_fixed_ips}}) if not port: raise exceptions.Conflict() return port def _setup_reserved_dhcp_port(self, network, device_id, dhcp_subnets): """Setup the reserved DHCP port, if there is one.""" LOG.debug('DHCP port %(device_id)s on network %(network_id)s' ' does not yet exist. Checking for a reserved port.', {'device_id': device_id, 'network_id': network.id}) for port in network.ports: port_device_id = getattr(port, 'device_id', None) if port_device_id == constants.DEVICE_ID_RESERVED_DHCP_PORT: port = self.plugin.update_dhcp_port( port.id, {'port': {'network_id': network.id, 'device_id': device_id}}) if port: return port def _setup_new_dhcp_port(self, network, device_id, dhcp_subnets): """Create and set up new DHCP port for the specified network.""" LOG.debug('DHCP port %(device_id)s on network %(network_id)s' ' does not yet exist. Creating new one.', {'device_id': device_id, 'network_id': network.id}) # Make a list of the subnets that need a unique IP address for # this DHCP port. if self.driver.use_gateway_ips: unique_ip_subnets = [] else: unique_ip_subnets = [dict(subnet_id=s) for s in dhcp_subnets] port_dict = dict( name='', admin_state_up=True, device_id=device_id, network_id=network.id, tenant_id=network.tenant_id, fixed_ips=unique_ip_subnets) return self.plugin.create_dhcp_port({'port': port_dict}) def setup_dhcp_port(self, network): """Create/update DHCP port for the host if needed and return port.""" # The ID that the DHCP port will have (or already has). device_id = self.get_device_id(network) # Get the set of DHCP-enabled local subnets on this network. dhcp_subnets = {subnet.id: subnet for subnet in network.subnets if subnet.enable_dhcp} # There are 3 cases: either the DHCP port already exists (but # might need to be updated for a changed set of subnets); or # some other code has already prepared a 'reserved' DHCP port, # and we just need to adopt that; or we need to create a new # DHCP port. Try each of those in turn until we have a DHCP # port. for setup_method in (self._setup_existing_dhcp_port, self._setup_reserved_dhcp_port, self._setup_new_dhcp_port): dhcp_port = setup_method(network, device_id, dhcp_subnets) if dhcp_port: break else: raise exceptions.Conflict() # FIXME(kevinbenton): ensure we have the IPs we actually need. # can be removed once bug/1627480 is fixed if not self.driver.use_gateway_ips: expected = set(dhcp_subnets) actual = {fip.subnet_id for fip in dhcp_port.fixed_ips} missing = expected - actual if missing: LOG.debug("Requested DHCP port with IPs on subnets " "%(expected)s but only got IPs on subnets " "%(actual)s.", {'expected': expected, 'actual': actual}) raise exceptions.SubnetMismatchForPort( port_id=dhcp_port.id, subnet_id=list(missing)[0]) # Convert subnet_id to subnet dict fixed_ips = [dict(subnet_id=fixed_ip.subnet_id, ip_address=fixed_ip.ip_address, subnet=dhcp_subnets[fixed_ip.subnet_id]) for fixed_ip in dhcp_port.fixed_ips # we don't care about any ips on subnets irrelevant # to us (e.g. auto ipv6 addresses) if fixed_ip.subnet_id in dhcp_subnets] ips = [DictModel(item) if isinstance(item, dict) else item for item in fixed_ips] dhcp_port.fixed_ips = ips return dhcp_port def _update_dhcp_port(self, network, port): for index in range(len(network.ports)): if network.ports[index].id == port.id: network.ports[index] = port break else: network.ports.append(port) def _cleanup_stale_devices(self, network, dhcp_port): """Unplug any devices found in the namespace except for dhcp_port.""" LOG.debug("Cleaning stale devices for network %s", network.id) skip_dev_name = (self.driver.get_device_name(dhcp_port) if dhcp_port else None) ns_ip = ip_lib.IPWrapper(namespace=network.namespace) if not ns_ip.netns.exists(network.namespace): return for d in ns_ip.get_devices(): # delete all devices except current active DHCP port device if d.name != skip_dev_name: LOG.debug("Found stale device %s, deleting", d.name) try: self.unplug(d.name, network) except Exception: LOG.exception("Exception during stale " "dhcp device cleanup") def plug(self, network, port, interface_name): """Plug device settings for the network's DHCP on this host.""" self.driver.plug(network.id, port.id, interface_name, port.mac_address, namespace=network.namespace, mtu=network.get('mtu')) def setup(self, network): """Create and initialize a device for network's DHCP on this host.""" try: port = self.setup_dhcp_port(network) except Exception: with excutils.save_and_reraise_exception(): # clear everything out so we don't leave dangling interfaces # if setup never succeeds in the future. self._cleanup_stale_devices(network, dhcp_port=None) self._update_dhcp_port(network, port) interface_name = self.get_interface_name(network, port) # Disable acceptance of RAs in the namespace so we don't # auto-configure an IPv6 address since we explicitly configure # them on the device. This must be done before any interfaces # are plugged since it could receive an RA by the time # plug() returns, so we have to create the namespace first. # It must also be done in the case there is an existing IPv6 # address here created via SLAAC, since it will be deleted # and added back statically in the call to init_l3() below. if network.namespace: ip_lib.IPWrapper().ensure_namespace(network.namespace) ip_lib.set_ip_nonlocal_bind_for_namespace(network.namespace, 1, root_namespace=True) if ipv6_utils.is_enabled_and_bind_by_default(): self.driver.configure_ipv6_ra(network.namespace, 'default', n_const.ACCEPT_RA_DISABLED) if ip_lib.ensure_device_is_ready(interface_name, namespace=network.namespace): LOG.debug('Reusing existing device: %s.', interface_name) # force mtu on the port for in case it was changed for the network mtu = getattr(network, 'mtu', 0) if mtu: self.driver.set_mtu(interface_name, mtu, namespace=network.namespace) else: try: self.plug(network, port, interface_name) except Exception: with excutils.save_and_reraise_exception(): LOG.exception('Unable to plug DHCP port for ' 'network %s. Releasing port.', network.id) # We should unplug the interface in bridge side. self.unplug(interface_name, network) self.plugin.release_dhcp_port(network.id, port.device_id) self.fill_dhcp_udp_checksums(namespace=network.namespace) ip_cidrs = [] for fixed_ip in port.fixed_ips: subnet = fixed_ip.subnet net = netaddr.IPNetwork(subnet.cidr) ip_cidr = '%s/%s' % (fixed_ip.ip_address, net.prefixlen) ip_cidrs.append(ip_cidr) if self.driver.use_gateway_ips: # For each DHCP-enabled subnet, add that subnet's gateway # IP address to the Linux device for the DHCP port. for subnet in network.subnets: if not subnet.enable_dhcp: continue gateway = subnet.gateway_ip if gateway: net = netaddr.IPNetwork(subnet.cidr) ip_cidrs.append('%s/%s' % (gateway, net.prefixlen)) if self.conf.force_metadata or self.conf.enable_isolated_metadata: ip_cidrs.append(METADATA_DEFAULT_CIDR) self.driver.init_l3(interface_name, ip_cidrs, namespace=network.namespace) self._set_default_route(network, interface_name) self._cleanup_stale_devices(network, port) return interface_name def update(self, network, device_name): """Update device settings for the network's DHCP on this host.""" self._set_default_route(network, device_name) def unplug(self, device_name, network): """Unplug device settings for the network's DHCP on this host.""" self.driver.unplug(device_name, namespace=network.namespace) def destroy(self, network, device_name): """Destroy the device used for the network's DHCP on this host.""" if device_name: self.unplug(device_name, network) else: LOG.debug('No interface exists for network %s', network.id) self.plugin.release_dhcp_port(network.id, self.get_device_id(network)) def fill_dhcp_udp_checksums(self, namespace): """Ensure DHCP reply packets always have correct UDP checksums.""" iptables_mgr = iptables_manager.IptablesManager(use_ipv6=True, namespace=namespace) ipv4_rule = ('-p udp -m udp --dport %d -j CHECKSUM --checksum-fill' % constants.DHCP_RESPONSE_PORT) ipv6_rule = ('-p udp -m udp --dport %d -j CHECKSUM --checksum-fill' % n_const.DHCPV6_CLIENT_PORT) iptables_mgr.ipv4['mangle'].add_rule('POSTROUTING', ipv4_rule) iptables_mgr.ipv6['mangle'].add_rule('POSTROUTING', ipv6_rule) iptables_mgr.apply() neutron-12.1.1/neutron/agent/linux/keepalived.py0000664000175000017500000004762413553660047021750 0ustar zuulzuul00000000000000# Copyright (C) 2014 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import errno import itertools import os import netaddr from neutron_lib import exceptions from neutron_lib.utils import file as file_utils from oslo_config import cfg from oslo_log import log as logging from oslo_utils import fileutils from neutron._i18n import _ from neutron.agent.linux import external_process from neutron.common import constants from neutron.common import utils VALID_STATES = ['MASTER', 'BACKUP'] VALID_AUTH_TYPES = ['AH', 'PASS'] HA_DEFAULT_PRIORITY = 50 PRIMARY_VIP_RANGE_SIZE = 24 KEEPALIVED_SERVICE_NAME = 'keepalived' KEEPALIVED_EMAIL_FROM = 'neutron@openstack.local' KEEPALIVED_ROUTER_ID = 'neutron' GARP_MASTER_DELAY = 60 HEALTH_CHECK_NAME = 'ha_health_check' LOG = logging.getLogger(__name__) def get_free_range(parent_range, excluded_ranges, size=PRIMARY_VIP_RANGE_SIZE): """Get a free IP range, from parent_range, of the specified size. :param parent_range: String representing an IP range. E.g: '169.254.0.0/16' :param excluded_ranges: A list of strings to be excluded from parent_range :param size: What should be the size of the range returned? :return: A string representing an IP range """ free_cidrs = netaddr.IPSet([parent_range]) - netaddr.IPSet(excluded_ranges) for cidr in free_cidrs.iter_cidrs(): if cidr.prefixlen <= size: return '%s/%s' % (cidr.network, size) raise ValueError(_('Network of size %(size)s, from IP range ' '%(parent_range)s excluding IP ranges ' '%(excluded_ranges)s was not found.') % {'size': size, 'parent_range': parent_range, 'excluded_ranges': excluded_ranges}) class InvalidInstanceStateException(exceptions.NeutronException): message = _('Invalid instance state: %(state)s, valid states are: ' '%(valid_states)s') def __init__(self, **kwargs): if 'valid_states' not in kwargs: kwargs['valid_states'] = ', '.join(VALID_STATES) super(InvalidInstanceStateException, self).__init__(**kwargs) class InvalidAuthenticationTypeException(exceptions.NeutronException): message = _('Invalid authentication type: %(auth_type)s, ' 'valid types are: %(valid_auth_types)s') def __init__(self, **kwargs): if 'valid_auth_types' not in kwargs: kwargs['valid_auth_types'] = ', '.join(VALID_AUTH_TYPES) super(InvalidAuthenticationTypeException, self).__init__(**kwargs) class KeepalivedVipAddress(object): """A virtual address entry of a keepalived configuration.""" def __init__(self, ip_address, interface_name, scope=None): self.ip_address = ip_address self.interface_name = interface_name self.scope = scope def __eq__(self, other): return (isinstance(other, KeepalivedVipAddress) and self.ip_address == other.ip_address) def __str__(self): return '[%s, %s, %s]' % (self.ip_address, self.interface_name, self.scope) def build_config(self): result = '%s dev %s' % (self.ip_address, self.interface_name) if self.scope: result += ' scope %s' % self.scope return result class KeepalivedVirtualRoute(object): """A virtual route entry of a keepalived configuration.""" def __init__(self, destination, nexthop, interface_name=None, scope=None): self.destination = destination self.nexthop = nexthop self.interface_name = interface_name self.scope = scope def build_config(self): output = self.destination if self.nexthop: output += ' via %s' % self.nexthop if self.interface_name: output += ' dev %s' % self.interface_name if self.scope: output += ' scope %s' % self.scope return output class KeepalivedInstanceRoutes(object): def __init__(self): self.gateway_routes = [] self.extra_routes = [] self.extra_subnets = [] def remove_routes_on_interface(self, interface_name): self.gateway_routes = [gw_rt for gw_rt in self.gateway_routes if gw_rt.interface_name != interface_name] # NOTE(amuller): extra_routes are initialized from the router's # 'routes' attribute. These routes do not have an interface # parameter and so cannot be removed via an interface_name lookup. self.extra_subnets = [route for route in self.extra_subnets if route.interface_name != interface_name] @property def routes(self): return self.gateway_routes + self.extra_routes + self.extra_subnets def __len__(self): return len(self.routes) def build_config(self): return itertools.chain([' virtual_routes {'], (' %s' % route.build_config() for route in self.routes), [' }']) class KeepalivedInstance(object): """Instance section of a keepalived configuration.""" def __init__(self, state, interface, vrouter_id, ha_cidrs, priority=HA_DEFAULT_PRIORITY, advert_int=None, mcast_src_ip=None, nopreempt=False, garp_master_delay=GARP_MASTER_DELAY, vrrp_health_check_interval=0, ha_conf_dir=None): self.name = 'VR_%s' % vrouter_id if state not in VALID_STATES: raise InvalidInstanceStateException(state=state) self.state = state self.interface = interface self.vrouter_id = vrouter_id self.priority = priority self.nopreempt = nopreempt self.advert_int = advert_int self.mcast_src_ip = mcast_src_ip self.garp_master_delay = garp_master_delay self.track_interfaces = [] self.vips = [] self.virtual_routes = KeepalivedInstanceRoutes() self.authentication = None self.track_script = None self.primary_vip_range = get_free_range( parent_range=constants.PRIVATE_CIDR_RANGE, excluded_ranges=[constants.METADATA_CIDR, constants.DVR_FIP_LL_CIDR] + ha_cidrs, size=PRIMARY_VIP_RANGE_SIZE) if vrrp_health_check_interval > 0: self.track_script = KeepalivedTrackScript( vrrp_health_check_interval, ha_conf_dir, self.vrouter_id) def set_authentication(self, auth_type, password): if auth_type not in VALID_AUTH_TYPES: raise InvalidAuthenticationTypeException(auth_type=auth_type) self.authentication = (auth_type, password) def add_vip(self, ip_cidr, interface_name, scope): vip = KeepalivedVipAddress(ip_cidr, interface_name, scope) if vip not in self.vips: self.vips.append(vip) else: LOG.debug('VIP %s already present in %s', vip, self.vips) def remove_vips_vroutes_by_interface(self, interface_name): self.vips = [vip for vip in self.vips if vip.interface_name != interface_name] self.virtual_routes.remove_routes_on_interface(interface_name) def remove_vip_by_ip_address(self, ip_address): self.vips = [vip for vip in self.vips if vip.ip_address != ip_address] def get_existing_vip_ip_addresses(self, interface_name): return [vip.ip_address for vip in self.vips if vip.interface_name == interface_name] def _build_track_interface_config(self): return itertools.chain( [' track_interface {'], (' %s' % i for i in self.track_interfaces), [' }']) def get_primary_vip(self): """Return an address in the primary_vip_range CIDR, with the router's VRID in the host section. For example, if primary_vip_range is 169.254.0.0/24, and this router's VRID is 5, the result is 169.254.0.5. Using the VRID assures that the primary VIP is consistent amongst HA router instances on different nodes. """ ip = (netaddr.IPNetwork(self.primary_vip_range).network + self.vrouter_id) return str(netaddr.IPNetwork('%s/%s' % (ip, PRIMARY_VIP_RANGE_SIZE))) def _build_vips_config(self): # NOTE(amuller): The primary VIP must be consistent in order to avoid # keepalived bugs. Changing the VIP in the 'virtual_ipaddress' and # SIGHUP'ing keepalived can remove virtual routers, including the # router's default gateway. # We solve this by never changing the VIP in the virtual_ipaddress # section, herein known as the primary VIP. # The only interface known to exist for HA routers is the HA interface # (self.interface). We generate an IP on that device and use it as the # primary VIP. The other VIPs (Internal interfaces IPs, the external # interface IP and floating IPs) are placed in the # virtual_ipaddress_excluded section. primary = KeepalivedVipAddress(self.get_primary_vip(), self.interface) vips_result = [' virtual_ipaddress {', ' %s' % primary.build_config(), ' }'] if self.vips: vips_result.extend( itertools.chain([' virtual_ipaddress_excluded {'], (' %s' % vip.build_config() for vip in sorted(self.vips, key=lambda vip: vip.ip_address)), [' }'])) return vips_result def _build_virtual_routes_config(self): return itertools.chain([' virtual_routes {'], (' %s' % route.build_config() for route in self.virtual_routes), [' }']) def build_config(self): if self.track_script: config = self.track_script.build_config_preamble() self.track_script.routes = self.virtual_routes.gateway_routes self.track_script.vips = self.vips else: config = [] config.extend(['vrrp_instance %s {' % self.name, ' state %s' % self.state, ' interface %s' % self.interface, ' virtual_router_id %s' % self.vrouter_id, ' priority %s' % self.priority, ' garp_master_delay %s' % self.garp_master_delay]) if self.nopreempt: config.append(' nopreempt') if self.advert_int: config.append(' advert_int %s' % self.advert_int) if self.authentication: auth_type, password = self.authentication authentication = [' authentication {', ' auth_type %s' % auth_type, ' auth_pass %s' % password, ' }'] config.extend(authentication) if self.mcast_src_ip: config.append(' mcast_src_ip %s' % self.mcast_src_ip) if self.track_interfaces: config.extend(self._build_track_interface_config()) config.extend(self._build_vips_config()) if len(self.virtual_routes): config.extend(self.virtual_routes.build_config()) if self.track_script: config.extend(self.track_script.build_config()) config.append('}') return config class KeepalivedConf(object): """A keepalived configuration.""" def __init__(self): self.reset() def reset(self): self.instances = {} def add_instance(self, instance): self.instances[instance.vrouter_id] = instance def get_instance(self, vrouter_id): return self.instances.get(vrouter_id) def build_config(self): config = ['global_defs {', ' notification_email_from %s' % KEEPALIVED_EMAIL_FROM, ' router_id %s' % KEEPALIVED_ROUTER_ID, '}' ] for instance in self.instances.values(): config.extend(instance.build_config()) return config def get_config_str(self): """Generates and returns the keepalived configuration. :return: Keepalived configuration string. """ return '\n'.join(self.build_config()) class KeepalivedManager(object): """Wrapper for keepalived. This wrapper permits to write keepalived config files, to start/restart keepalived process. """ def __init__(self, resource_id, config, process_monitor, conf_path='/tmp', namespace=None, throttle_restart_value=None): self.resource_id = resource_id self.config = config self.namespace = namespace self.process_monitor = process_monitor self.conf_path = conf_path # configure throttler for spawn to introduce delay between SIGHUPs, # otherwise keepalived master may unnecessarily flip to slave if throttle_restart_value is not None: self._throttle_spawn(throttle_restart_value) #pylint: disable=method-hidden def _throttle_spawn(self, threshold): self.spawn = utils.throttler(threshold)(self.spawn) def get_conf_dir(self): confs_dir = os.path.abspath(os.path.normpath(self.conf_path)) conf_dir = os.path.join(confs_dir, self.resource_id) return conf_dir def get_full_config_file_path(self, filename, ensure_conf_dir=True): conf_dir = self.get_conf_dir() if ensure_conf_dir: fileutils.ensure_tree(conf_dir, mode=0o755) return os.path.join(conf_dir, filename) def _output_config_file(self): config_str = self.config.get_config_str() LOG.debug("Router %s keepalived config: %s", self.resource_id, config_str) config_path = self.get_full_config_file_path('keepalived.conf') file_utils.replace_file(config_path, config_str) return config_path @staticmethod def _safe_remove_pid_file(pid_file): try: os.remove(pid_file) except OSError as e: if e.errno != errno.ENOENT: LOG.error("Could not delete file %s, keepalived can " "refuse to start.", pid_file) def get_vrrp_pid_file_name(self, base_pid_file): return '%s-vrrp' % base_pid_file def get_conf_on_disk(self): config_path = self.get_full_config_file_path('keepalived.conf') try: with open(config_path) as conf: return conf.read() except (OSError, IOError) as e: if e.errno != errno.ENOENT: raise def spawn(self): config_path = self._output_config_file() for key, instance in self.config.instances.items(): if instance.track_script: instance.track_script.write_check_script() keepalived_pm = self.get_process() vrrp_pm = self._get_vrrp_process( self.get_vrrp_pid_file_name(keepalived_pm.get_pid_file_name())) keepalived_pm.default_cmd_callback = ( self._get_keepalived_process_callback(vrrp_pm, config_path)) keepalived_pm.enable(reload_cfg=True) self.process_monitor.register(uuid=self.resource_id, service_name=KEEPALIVED_SERVICE_NAME, monitored_process=keepalived_pm) LOG.debug('Keepalived spawned with config %s', config_path) def disable(self): self.process_monitor.unregister(uuid=self.resource_id, service_name=KEEPALIVED_SERVICE_NAME) pm = self.get_process() pm.disable(sig='15') def get_process(self): return external_process.ProcessManager( cfg.CONF, self.resource_id, self.namespace, pids_path=self.conf_path) def _get_vrrp_process(self, pid_file): return external_process.ProcessManager( cfg.CONF, self.resource_id, self.namespace, pid_file=pid_file) def _get_keepalived_process_callback(self, vrrp_pm, config_path): def callback(pid_file): # If keepalived process crashed unexpectedly, the vrrp process # will be orphan and prevent keepalived process to be spawned. # A check here will let the l3-agent to kill the orphan process # and spawn keepalived successfully. if vrrp_pm.active: vrrp_pm.disable() self._safe_remove_pid_file(pid_file) self._safe_remove_pid_file(self.get_vrrp_pid_file_name(pid_file)) cmd = ['keepalived', '-P', '-f', config_path, '-p', pid_file, '-r', self.get_vrrp_pid_file_name(pid_file)] if logging.is_debug_enabled(cfg.CONF): cmd.append('-D') return cmd return callback class KeepalivedTrackScript(KeepalivedConf): """Track script generator for Keepalived""" def __init__(self, interval, conf_dir, vr_id): self.interval = interval self.conf_dir = conf_dir self.vr_id = vr_id self.routes = [] self.vips = [] def build_config_preamble(self): config = ['', 'vrrp_script %s_%s {' % (HEALTH_CHECK_NAME, self.vr_id), ' script "%s"' % self._get_script_location(), ' interval %s' % self.interval, ' fall 2', ' rise 2', '}', ''] return config def _is_needed(self): """Check if track script is needed by checking amount of routes. :return: True/False """ return len(self.routes) > 0 def build_config(self): if not self._is_needed(): return '' config = [' track_script {', ' %s_%s' % (HEALTH_CHECK_NAME, self.vr_id), ' }'] return config def build_script(self): return itertools.chain(['#!/bin/bash -eu'], ['%s' % self._check_ip_assigned()], ('%s' % self._add_ip_addr(route.nexthop) for route in self.routes if route.nexthop), ) def _add_ip_addr(self, ip_addr): cmd = { 4: 'ping', 6: 'ping6', }.get(netaddr.IPAddress(ip_addr).version) return '%s -c 1 -w 1 %s 1>/dev/null || exit 1' % (cmd, ip_addr) def _check_ip_assigned(self): cmd = 'ip a | grep %s || exit 0' return cmd % netaddr.IPNetwork(self.vips[0].ip_address).ip if len( self.vips) else '' def _get_script_str(self): """Generates and returns bash script to verify connectivity. :return: Bash script code """ return '\n'.join(self.build_script()) def _get_script_location(self): return os.path.join(self.conf_dir, 'ha_check_script_%s.sh' % self.vr_id) def write_check_script(self): if not self._is_needed(): return file_utils.replace_file( self._get_script_location(), self._get_script_str(), 0o520) neutron-12.1.1/neutron/agent/linux/daemon.py0000664000175000017500000001746313553660047021100 0ustar zuulzuul00000000000000# Copyright 2012 New Dream Network, LLC (DreamHost) # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import atexit import fcntl import grp import logging as std_logging from logging import handlers import os import pwd import signal import sys from oslo_log import log as logging from neutron._i18n import _ from neutron.common import exceptions LOG = logging.getLogger(__name__) DEVNULL = object() # Note: We can't use sys.std*.fileno() here. sys.std* objects may be # random file-like objects that may not match the true system std* fds # - and indeed may not even have a file descriptor at all (eg: test # fixtures that monkey patch fixtures.StringStream onto sys.stdout). # Below we always want the _real_ well-known 0,1,2 Unix fds during # os.dup2 manipulation. STDIN_FILENO = 0 STDOUT_FILENO = 1 STDERR_FILENO = 2 def setuid(user_id_or_name): try: new_uid = int(user_id_or_name) except (TypeError, ValueError): new_uid = pwd.getpwnam(user_id_or_name).pw_uid if new_uid != 0: try: os.setuid(new_uid) except OSError: msg = _('Failed to set uid %s') % new_uid LOG.critical(msg) raise exceptions.FailToDropPrivilegesExit(msg) def setgid(group_id_or_name): try: new_gid = int(group_id_or_name) except (TypeError, ValueError): new_gid = grp.getgrnam(group_id_or_name).gr_gid if new_gid != 0: try: os.setgid(new_gid) except OSError: msg = _('Failed to set gid %s') % new_gid LOG.critical(msg) raise exceptions.FailToDropPrivilegesExit(msg) def unwatch_log(): """Replace WatchedFileHandler handlers by FileHandler ones. Neutron logging uses WatchedFileHandler handlers but they do not support privileges drop, this method replaces them by FileHandler handlers supporting privileges drop. """ log_root = logging.getLogger(None).logger to_replace = [h for h in log_root.handlers if isinstance(h, handlers.WatchedFileHandler)] for handler in to_replace: # NOTE(cbrandily): we use default delay(=False) to ensure the log file # is opened before privileges drop. new_handler = std_logging.FileHandler(handler.baseFilename, mode=handler.mode, encoding=handler.encoding) log_root.removeHandler(handler) log_root.addHandler(new_handler) def drop_privileges(user=None, group=None): """Drop privileges to user/group privileges.""" if user is None and group is None: return if os.geteuid() != 0: msg = _('Root permissions are required to drop privileges.') LOG.critical(msg) raise exceptions.FailToDropPrivilegesExit(msg) if group is not None: try: os.setgroups([]) except OSError: msg = _('Failed to remove supplemental groups') LOG.critical(msg) raise exceptions.FailToDropPrivilegesExit(msg) setgid(group) if user is not None: setuid(user) LOG.info("Process runs with uid/gid: %(uid)s/%(gid)s", {'uid': os.getuid(), 'gid': os.getgid()}) class Pidfile(object): def __init__(self, pidfile, procname, uuid=None): self.pidfile = pidfile self.procname = procname self.uuid = uuid try: self.fd = os.open(pidfile, os.O_CREAT | os.O_RDWR) fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError: LOG.exception("Error while handling pidfile: %s", pidfile) sys.exit(1) def __str__(self): return self.pidfile def unlock(self): fcntl.flock(self.fd, fcntl.LOCK_UN) def write(self, pid): os.ftruncate(self.fd, 0) os.write(self.fd, b"%d" % pid) os.fsync(self.fd) def read(self): try: pid = int(os.read(self.fd, 128)) os.lseek(self.fd, 0, os.SEEK_SET) return pid except ValueError: return def is_running(self): pid = self.read() if not pid: return False cmdline = '/proc/%s/cmdline' % pid try: with open(cmdline, "r") as f: exec_out = f.readline() return self.procname in exec_out and (not self.uuid or self.uuid in exec_out) except IOError: return False class Daemon(object): """A generic daemon class. Usage: subclass the Daemon class and override the run() method """ def __init__(self, pidfile, stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL, procname='python', uuid=None, user=None, group=None): """Note: pidfile may be None.""" self.stdin = stdin self.stdout = stdout self.stderr = stderr self.procname = procname self.pidfile = (Pidfile(pidfile, procname, uuid) if pidfile is not None else None) self.user = user self.group = group def _fork(self): try: pid = os.fork() if pid > 0: os._exit(0) except OSError: LOG.exception('Fork failed') sys.exit(1) def daemonize(self): """Daemonize process by doing Stevens double fork.""" # flush any buffered data before fork/dup2. if self.stdout is not DEVNULL: self.stdout.flush() if self.stderr is not DEVNULL: self.stderr.flush() # sys.std* may not match STD{OUT,ERR}_FILENO. Tough. for f in (sys.stdout, sys.stderr): f.flush() # fork first time self._fork() # decouple from parent environment os.chdir("/") os.setsid() os.umask(0) # fork second time self._fork() # redirect standard file descriptors with open(os.devnull, 'w+') as devnull: stdin = devnull if self.stdin is DEVNULL else self.stdin stdout = devnull if self.stdout is DEVNULL else self.stdout stderr = devnull if self.stderr is DEVNULL else self.stderr os.dup2(stdin.fileno(), STDIN_FILENO) os.dup2(stdout.fileno(), STDOUT_FILENO) os.dup2(stderr.fileno(), STDERR_FILENO) if self.pidfile is not None: # write pidfile atexit.register(self.delete_pid) signal.signal(signal.SIGTERM, self.handle_sigterm) self.pidfile.write(os.getpid()) def delete_pid(self): if self.pidfile is not None: os.remove(str(self.pidfile)) def handle_sigterm(self, signum, frame): sys.exit(0) def start(self): """Start the daemon.""" if self.pidfile is not None and self.pidfile.is_running(): self.pidfile.unlock() LOG.error('Pidfile %s already exist. Daemon already ' 'running?', self.pidfile) sys.exit(1) # Start the daemon self.daemonize() self.run() def run(self): """Override this method and call super().run when subclassing Daemon. start() will call this method after the process has daemonized. """ unwatch_log() drop_privileges(self.user, self.group) neutron-12.1.1/neutron/agent/linux/__init__.py0000664000175000017500000000000013553660046021346 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/linux/dibbler.py0000664000175000017500000001610713553660046021231 0ustar zuulzuul00000000000000# Copyright 2015 Cisco Systems # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import shutil import jinja2 from neutron_lib import constants as lib_const from neutron_lib.utils import file as file_utils from oslo_config import cfg from oslo_log import log as logging import six from neutron.agent.linux import external_process from neutron.agent.linux import pd from neutron.agent.linux import pd_driver from neutron.agent.linux import utils LOG = logging.getLogger(__name__) PD_SERVICE_NAME = 'dibbler' CONFIG_TEMPLATE = jinja2.Template(""" # Config for dibbler-client. # Use enterprise number based duid duid-type duid-en {{ enterprise_number }} {{ va_id }} # 8 (Debug) is most verbose. 7 (Info) is usually the best option log-level 8 # No automatic downlink address assignment downlink-prefix-ifaces "none" # Use script to notify l3_agent of assigned prefix script {{ script_path }} # Ask for prefix over the external gateway interface iface {{ interface_name }} { # Bind to generated LLA bind-to-address {{ bind_address }} # ask for address {% if hint_prefix != None %} pd 1 { prefix {{ hint_prefix }} } {% else %} pd 1 {% endif %} } """) # The first line must be #!/usr/bin/env bash SCRIPT_TEMPLATE = jinja2.Template("""#!/usr/bin/env bash exec neutron-pd-notify $1 {{ prefix_path }} {{ l3_agent_pid }} """) class PDDibbler(pd_driver.PDDriverBase): def __init__(self, router_id, subnet_id, ri_ifname): super(PDDibbler, self).__init__(router_id, subnet_id, ri_ifname) self.requestor_id = "%s:%s:%s" % (self.router_id, self.subnet_id, self.ri_ifname) self.dibbler_client_working_area = "%s/%s" % (cfg.CONF.pd_confs, self.requestor_id) self.prefix_path = "%s/prefix" % self.dibbler_client_working_area self.pid_path = "%s/client.pid" % self.dibbler_client_working_area self.converted_subnet_id = self.subnet_id.replace('-', '') def _is_dibbler_client_running(self): return utils.get_value_from_file(self.pid_path) def _generate_dibbler_conf(self, ex_gw_ifname, lla, hint_prefix): dcwa = self.dibbler_client_working_area script_path = utils.get_conf_file_name(dcwa, 'notify', 'sh', True) buf = six.StringIO() buf.write('%s' % SCRIPT_TEMPLATE.render( prefix_path=self.prefix_path, l3_agent_pid=os.getpid())) file_utils.replace_file(script_path, buf.getvalue()) os.chmod(script_path, 0o744) dibbler_conf = utils.get_conf_file_name(dcwa, 'client', 'conf', False) buf = six.StringIO() buf.write('%s' % CONFIG_TEMPLATE.render( enterprise_number=cfg.CONF.vendor_pen, va_id='0x%s' % self.converted_subnet_id, script_path='"%s/notify.sh"' % dcwa, interface_name='"%s"' % ex_gw_ifname, bind_address='%s' % lla, hint_prefix=hint_prefix)) file_utils.replace_file(dibbler_conf, buf.getvalue()) return dcwa def _spawn_dibbler(self, pmon, router_ns, dibbler_conf): def callback(pid_file): dibbler_cmd = ['dibbler-client', 'start', '-w', '%s' % dibbler_conf] return dibbler_cmd pm = external_process.ProcessManager( uuid=self.requestor_id, default_cmd_callback=callback, namespace=router_ns, service=PD_SERVICE_NAME, conf=cfg.CONF, pid_file=self.pid_path) pm.enable(reload_cfg=False) pmon.register(uuid=self.requestor_id, service_name=PD_SERVICE_NAME, monitored_process=pm) def enable(self, pmon, router_ns, ex_gw_ifname, lla, prefix=None): LOG.debug("Enable IPv6 PD for router %s subnet %s ri_ifname %s", self.router_id, self.subnet_id, self.ri_ifname) if not self._is_dibbler_client_running(): dibbler_conf = self._generate_dibbler_conf(ex_gw_ifname, lla, prefix) self._spawn_dibbler(pmon, router_ns, dibbler_conf) LOG.debug("dibbler client enabled for router %s subnet %s" " ri_ifname %s", self.router_id, self.subnet_id, self.ri_ifname) def disable(self, pmon, router_ns, switch_over=False): LOG.debug("Disable IPv6 PD for router %s subnet %s ri_ifname %s", self.router_id, self.subnet_id, self.ri_ifname) dcwa = self.dibbler_client_working_area def callback(pid_file): dibbler_cmd = ['dibbler-client', 'stop', '-w', '%s' % dcwa] return dibbler_cmd pmon.unregister(uuid=self.requestor_id, service_name=PD_SERVICE_NAME) pm = external_process.ProcessManager( uuid=self.requestor_id, namespace=router_ns, service=PD_SERVICE_NAME, conf=cfg.CONF, pid_file=self.pid_path) if switch_over: pm.disable() else: pm.disable(get_stop_command=callback) shutil.rmtree(dcwa, ignore_errors=True) LOG.debug("dibbler client disabled for router %s subnet %s " "ri_ifname %s", self.router_id, self.subnet_id, self.ri_ifname) def get_prefix(self): prefix = utils.get_value_from_file(self.prefix_path) if not prefix: prefix = lib_const.PROVISIONAL_IPV6_PD_PREFIX return prefix @staticmethod def get_sync_data(): try: requestor_ids = os.listdir(cfg.CONF.pd_confs) except OSError: return [] sync_data = [] requestors = (r.split(':') for r in requestor_ids if r.count(':') == 2) for router_id, subnet_id, ri_ifname in requestors: pd_info = pd.PDInfo() pd_info.router_id = router_id pd_info.subnet_id = subnet_id pd_info.ri_ifname = ri_ifname pd_info.driver = PDDibbler(router_id, subnet_id, ri_ifname) pd_info.client_started = ( pd_info.driver._is_dibbler_client_running()) pd_info.prefix = pd_info.driver.get_prefix() sync_data.append(pd_info) return sync_data neutron-12.1.1/neutron/agent/linux/iptables_firewall.py0000664000175000017500000012377213553660047023326 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import ctypes from ctypes import util import sys import netaddr from neutron_lib import constants from neutron_lib.utils import helpers from oslo_config import cfg from oslo_log import log as logging from oslo_utils import netutils from neutron.agent import firewall from neutron.agent.linux import ip_conntrack from neutron.agent.linux import ipset_manager from neutron.agent.linux import iptables_comments as ic from neutron.agent.linux import iptables_manager from neutron.common import constants as n_const from neutron.common import ipv6_utils from neutron.common import utils as c_utils LOG = logging.getLogger(__name__) SG_CHAIN = 'sg-chain' SPOOF_FILTER = 'spoof-filter' CHAIN_NAME_PREFIX = {firewall.INGRESS_DIRECTION: 'i', firewall.EGRESS_DIRECTION: 'o', SPOOF_FILTER: 's'} IPSET_DIRECTION = {firewall.INGRESS_DIRECTION: 'src', firewall.EGRESS_DIRECTION: 'dst'} comment_rule = iptables_manager.comment_rule libc = ctypes.CDLL(util.find_library('libc.so.6')) # iptables protocols that support --dport and --sport IPTABLES_PORT_PROTOCOLS = [ constants.PROTO_NAME_DCCP, constants.PROTO_NAME_SCTP, constants.PROTO_NAME_TCP, constants.PROTO_NAME_UDP, constants.PROTO_NAME_UDPLITE ] def get_hybrid_port_name(port_name): return (constants.TAP_DEVICE_PREFIX + port_name)[:n_const.LINUX_DEV_LEN] class mac_iptables(netaddr.mac_eui48): """mac format class for netaddr to match iptables representation.""" word_sep = ':' class IptablesFirewallDriver(firewall.FirewallDriver): """Driver which enforces security groups through iptables rules.""" IPTABLES_DIRECTION = {firewall.INGRESS_DIRECTION: 'physdev-out', firewall.EGRESS_DIRECTION: 'physdev-in'} CONNTRACK_ZONE_PER_PORT = False def __init__(self, namespace=None): self.iptables = iptables_manager.IptablesManager(state_less=True, use_ipv6=ipv6_utils.is_enabled_and_bind_by_default(), namespace=namespace) # TODO(majopela, shihanzhang): refactor out ipset to a separate # driver composed over this one self.ipset = ipset_manager.IpsetManager(namespace=namespace) # list of port which has security group self.filtered_ports = {} self.unfiltered_ports = {} self.trusted_ports = [] self.ipconntrack = ip_conntrack.get_conntrack( self.iptables.get_rules_for_table, self.filtered_ports, self.unfiltered_ports, namespace=namespace, zone_per_port=self.CONNTRACK_ZONE_PER_PORT) self._add_fallback_chain_v4v6() self._defer_apply = False self._pre_defer_filtered_ports = None self._pre_defer_unfiltered_ports = None # List of security group rules for ports residing on this host self.sg_rules = {} self.pre_sg_rules = None # List of security group member ips for ports residing on this host self.sg_members = collections.defaultdict( lambda: collections.defaultdict(list)) self.pre_sg_members = None self.enable_ipset = cfg.CONF.SECURITYGROUP.enable_ipset self.updated_rule_sg_ids = set() self.updated_sg_members = set() self.devices_with_updated_sg_members = collections.defaultdict(list) self._iptables_protocol_name_map = {} @property def ports(self): return dict(self.filtered_ports, **self.unfiltered_ports) def _update_remote_security_group_members(self, sec_group_ids): for sg_id in sec_group_ids: for device in self.filtered_ports.values(): if sg_id in device.get('security_group_source_groups', []): self.devices_with_updated_sg_members[sg_id].append(device) def security_group_updated(self, action_type, sec_group_ids, device_ids=None): device_ids = device_ids or [] if action_type == 'sg_rule': self.updated_rule_sg_ids.update(sec_group_ids) elif action_type == 'sg_member': if device_ids: self.updated_sg_members.update(device_ids) else: self._update_remote_security_group_members(sec_group_ids) def process_trusted_ports(self, port_ids): """Process ports that are trusted and shouldn't be filtered.""" for port in port_ids: if port not in self.trusted_ports: jump_rule = self._generate_trusted_port_rules(port) self._add_rules_to_chain_v4v6( 'FORWARD', jump_rule, jump_rule, comment=ic.TRUSTED_ACCEPT) self.trusted_ports.append(port) def remove_trusted_ports(self, port_ids): for port in port_ids: if port in self.trusted_ports: jump_rule = self._generate_trusted_port_rules(port) self._remove_rule_from_chain_v4v6( 'FORWARD', jump_rule, jump_rule) self.trusted_ports.remove(port) def _generate_trusted_port_rules(self, port): rt = '-m physdev --%%s %s --physdev-is-bridged -j ACCEPT' % ( self._get_device_name(port)) return [rt % (self.IPTABLES_DIRECTION[constants.INGRESS_DIRECTION]), rt % (self.IPTABLES_DIRECTION[constants.EGRESS_DIRECTION])] def update_security_group_rules(self, sg_id, sg_rules): LOG.debug("Update rules of security group (%s)", sg_id) self.sg_rules[sg_id] = sg_rules def update_security_group_members(self, sg_id, sg_members): LOG.debug("Update members of security group (%s)", sg_id) self.sg_members[sg_id] = collections.defaultdict(list, sg_members) if self.enable_ipset: self._update_ipset_members(sg_id, sg_members) def _update_ipset_members(self, sg_id, sg_members): devices = self.devices_with_updated_sg_members.pop(sg_id, None) for ip_version, current_ips in sg_members.items(): add_ips, del_ips = self.ipset.set_members( sg_id, ip_version, current_ips) if devices and del_ips: # remove prefix from del_ips ips = [str(netaddr.IPNetwork(del_ip).ip) for del_ip in del_ips] self.ipconntrack.delete_conntrack_state_by_remote_ips( devices, ip_version, ips) def _set_ports(self, port): if not firewall.port_sec_enabled(port): self.unfiltered_ports[port['device']] = port self.filtered_ports.pop(port['device'], None) else: self.filtered_ports[port['device']] = port self.unfiltered_ports.pop(port['device'], None) def _unset_ports(self, port): self.unfiltered_ports.pop(port['device'], None) self.filtered_ports.pop(port['device'], None) def _remove_conntrack_entries_from_port_deleted(self, port): device_info = self.filtered_ports.get(port['device']) if not device_info: return for ethertype in [constants.IPv4, constants.IPv6]: self.ipconntrack.delete_conntrack_state_by_remote_ips( [device_info], ethertype, set()) def prepare_port_filter(self, port): LOG.debug("Preparing device (%s) filter", port['device']) self._set_ports(port) # each security group has it own chains self._setup_chains() return self.iptables.apply() def update_port_filter(self, port): LOG.debug("Updating device (%s) filter", port['device']) if port['device'] not in self.ports: LOG.info('Attempted to update port filter which is not ' 'filtered %s', port['device']) return self._remove_chains() self._set_ports(port) self._setup_chains() return self.iptables.apply() def remove_port_filter(self, port): LOG.debug("Removing device (%s) filter", port['device']) if port['device'] not in self.ports: LOG.info('Attempted to remove port filter which is not ' 'filtered %r', port) return self._remove_chains() self._remove_conntrack_entries_from_port_deleted(port) self._unset_ports(port) self._setup_chains() return self.iptables.apply() def _add_accept_rule_port_sec(self, port, direction): self._update_port_sec_rules(port, direction, add=True) def _remove_rule_port_sec(self, port, direction): self._update_port_sec_rules(port, direction, add=False) def _remove_rule_from_chain_v4v6(self, chain_name, ipv4_rules, ipv6_rules): for rule in ipv4_rules: self.iptables.ipv4['filter'].remove_rule(chain_name, rule) for rule in ipv6_rules: self.iptables.ipv6['filter'].remove_rule(chain_name, rule) def _setup_chains(self): """Setup ingress and egress chain for a port.""" if not self._defer_apply: self._setup_chains_apply(self.filtered_ports, self.unfiltered_ports) def _setup_chains_apply(self, ports, unfiltered_ports): self._add_chain_by_name_v4v6(SG_CHAIN) # sort by port so we always do this deterministically between # agent restarts and don't cause unnecessary rule differences for pname in sorted(ports): port = ports[pname] self._add_conntrack_jump(port) self._setup_chain(port, firewall.INGRESS_DIRECTION) self._setup_chain(port, firewall.EGRESS_DIRECTION) self.iptables.ipv4['filter'].add_rule(SG_CHAIN, '-j ACCEPT') self.iptables.ipv6['filter'].add_rule(SG_CHAIN, '-j ACCEPT') for port in unfiltered_ports.values(): self._add_accept_rule_port_sec(port, firewall.INGRESS_DIRECTION) self._add_accept_rule_port_sec(port, firewall.EGRESS_DIRECTION) def _remove_chains(self): """Remove ingress and egress chain for a port.""" if not self._defer_apply: self._remove_chains_apply(self.filtered_ports, self.unfiltered_ports) def _remove_chains_apply(self, ports, unfiltered_ports): for port in ports.values(): self._remove_chain(port, firewall.INGRESS_DIRECTION) self._remove_chain(port, firewall.EGRESS_DIRECTION) self._remove_chain(port, SPOOF_FILTER) self._remove_conntrack_jump(port) for port in unfiltered_ports.values(): self._remove_rule_port_sec(port, firewall.INGRESS_DIRECTION) self._remove_rule_port_sec(port, firewall.EGRESS_DIRECTION) self._remove_chain_by_name_v4v6(SG_CHAIN) def _setup_chain(self, port, DIRECTION): self._add_chain(port, DIRECTION) self._add_rules_by_security_group(port, DIRECTION) def _remove_chain(self, port, DIRECTION): chain_name = self._port_chain_name(port, DIRECTION) self._remove_chain_by_name_v4v6(chain_name) def _add_fallback_chain_v4v6(self): self.iptables.ipv4['filter'].add_chain('sg-fallback') self.iptables.ipv4['filter'].add_rule('sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP) self.iptables.ipv6['filter'].add_chain('sg-fallback') self.iptables.ipv6['filter'].add_rule('sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP) def _add_chain_by_name_v4v6(self, chain_name): self.iptables.ipv4['filter'].add_chain(chain_name) self.iptables.ipv6['filter'].add_chain(chain_name) def _remove_chain_by_name_v4v6(self, chain_name): self.iptables.ipv4['filter'].remove_chain(chain_name) self.iptables.ipv6['filter'].remove_chain(chain_name) def _add_rules_to_chain_v4v6(self, chain_name, ipv4_rules, ipv6_rules, top=False, comment=None): for rule in ipv4_rules: self.iptables.ipv4['filter'].add_rule(chain_name, rule, top=top, comment=comment) for rule in ipv6_rules: self.iptables.ipv6['filter'].add_rule(chain_name, rule, top=top, comment=comment) def _get_device_name(self, port): if not isinstance(port, dict): return port return port['device'] def _update_port_sec_rules(self, port, direction, add=False): # add/remove rules in FORWARD and INPUT chain device = self._get_device_name(port) jump_rule = ['-m physdev --%s %s --physdev-is-bridged ' '-j ACCEPT' % (self.IPTABLES_DIRECTION[direction], device)] if add: self._add_rules_to_chain_v4v6( 'FORWARD', jump_rule, jump_rule, comment=ic.PORT_SEC_ACCEPT) else: self._remove_rule_from_chain_v4v6('FORWARD', jump_rule, jump_rule) if direction == firewall.EGRESS_DIRECTION: if add: self._add_rules_to_chain_v4v6('INPUT', jump_rule, jump_rule, comment=ic.PORT_SEC_ACCEPT) else: self._remove_rule_from_chain_v4v6( 'INPUT', jump_rule, jump_rule) def _add_chain(self, port, direction): chain_name = self._port_chain_name(port, direction) self._add_chain_by_name_v4v6(chain_name) # Note(nati) jump to the security group chain (SG_CHAIN) # This is needed because the packet may much two rule in port # if the two port is in the same host # We accept the packet at the end of SG_CHAIN. # jump to the security group chain device = self._get_device_name(port) jump_rule = ['-m physdev --%s %s --physdev-is-bridged ' '-j $%s' % (self.IPTABLES_DIRECTION[direction], device, SG_CHAIN)] # Security group chain has to be applied before unfiltered # or trusted ports self._add_rules_to_chain_v4v6('FORWARD', jump_rule, jump_rule, top=True, comment=ic.VM_INT_SG) # jump to the chain based on the device jump_rule = ['-m physdev --%s %s --physdev-is-bridged ' '-j $%s' % (self.IPTABLES_DIRECTION[direction], device, chain_name)] self._add_rules_to_chain_v4v6(SG_CHAIN, jump_rule, jump_rule, comment=ic.SG_TO_VM_SG) if direction == firewall.EGRESS_DIRECTION: self._add_rules_to_chain_v4v6('INPUT', jump_rule, jump_rule, comment=ic.INPUT_TO_SG) def _get_br_device_name(self, port): return ('brq' + port['network_id'])[:n_const.LINUX_DEV_LEN] def _get_jump_rules(self, port): zone = self.ipconntrack.get_device_zone(port) br_dev = self._get_br_device_name(port) port_dev = self._get_device_name(port) # match by interface for bridge input match_interface = '-i %s' match_physdev = '-m physdev --physdev-in %s' # comment to prevent duplicate warnings for different devices using # same bridge. truncate start to remove prefixes comment = '-m comment --comment "Set zone for %s"' % port['device'][4:] rules = [] for dev, match in ((br_dev, match_physdev), (br_dev, match_interface), (port_dev, match_physdev)): match = match % dev rule = '%s %s -j CT --zone %s' % (match, comment, zone) rules.append(rule) return rules def _add_conntrack_jump(self, port): for jump_rule in self._get_jump_rules(port): self._add_raw_rule('PREROUTING', jump_rule) def _remove_conntrack_jump(self, port): for jump_rule in self._get_jump_rules(port): self._remove_raw_rule('PREROUTING', jump_rule) def _add_raw_rule(self, chain, rule, comment=None): self.iptables.ipv4['raw'].add_rule(chain, rule, comment=comment) self.iptables.ipv6['raw'].add_rule(chain, rule, comment=comment) def _remove_raw_rule(self, chain, rule): self.iptables.ipv4['raw'].remove_rule(chain, rule) self.iptables.ipv6['raw'].remove_rule(chain, rule) def _split_sgr_by_ethertype(self, security_group_rules): ipv4_sg_rules = [] ipv6_sg_rules = [] for rule in security_group_rules: if rule.get('ethertype') == constants.IPv4: ipv4_sg_rules.append(rule) elif rule.get('ethertype') == constants.IPv6: if rule.get('protocol') == 'icmp': rule['protocol'] = 'ipv6-icmp' ipv6_sg_rules.append(rule) return ipv4_sg_rules, ipv6_sg_rules def _select_sgr_by_direction(self, port, direction): return [rule for rule in port.get('security_group_rules', []) if rule['direction'] == direction] def _setup_spoof_filter_chain(self, port, table, mac_ip_pairs, rules): if mac_ip_pairs: chain_name = self._port_chain_name(port, SPOOF_FILTER) table.add_chain(chain_name) for mac, ip in mac_ip_pairs: if ip is None: # If fixed_ips is [] this rule will be added to the end # of the list after the allowed_address_pair rules. table.add_rule(chain_name, '-m mac --mac-source %s -j RETURN' % mac.upper(), comment=ic.PAIR_ALLOW) else: # we need to convert it into a prefix to match iptables ip = c_utils.ip_to_cidr(ip) table.add_rule(chain_name, '-s %s -m mac --mac-source %s -j RETURN' % (ip, mac.upper()), comment=ic.PAIR_ALLOW) table.add_rule(chain_name, '-j DROP', comment=ic.PAIR_DROP) rules.append('-j $%s' % chain_name) def _build_ipv4v6_mac_ip_list(self, mac, ip_address, mac_ipv4_pairs, mac_ipv6_pairs): mac = str(netaddr.EUI(mac, dialect=mac_iptables)) if netaddr.IPNetwork(ip_address).version == 4: mac_ipv4_pairs.append((mac, ip_address)) else: mac_ipv6_pairs.append((mac, ip_address)) lla = str(netutils.get_ipv6_addr_by_EUI64( constants.IPv6_LLA_PREFIX, mac)) if (mac, lla) not in mac_ipv6_pairs: # only add once so we don't generate duplicate rules mac_ipv6_pairs.append((mac, lla)) def _spoofing_rule(self, port, ipv4_rules, ipv6_rules): # Fixed rules for traffic sourced from unspecified addresses: 0.0.0.0 # and :: # Allow dhcp client discovery and request ipv4_rules += [comment_rule('-s 0.0.0.0/32 -d 255.255.255.255/32 ' '-p udp -m udp --sport 68 --dport 67 ' '-j RETURN', comment=ic.DHCP_CLIENT)] # Allow neighbor solicitation and multicast listener discovery # from the unspecified address for duplicate address detection for icmp6_type in constants.ICMPV6_ALLOWED_UNSPEC_ADDR_TYPES: ipv6_rules += [comment_rule('-s ::/128 -d ff02::/16 ' '-p ipv6-icmp -m icmp6 ' '--icmpv6-type %s -j RETURN' % icmp6_type, comment=ic.IPV6_ICMP_ALLOW)] mac_ipv4_pairs = [] mac_ipv6_pairs = [] if isinstance(port.get('allowed_address_pairs'), list): for address_pair in port['allowed_address_pairs']: self._build_ipv4v6_mac_ip_list(address_pair['mac_address'], address_pair['ip_address'], mac_ipv4_pairs, mac_ipv6_pairs) for ip in port['fixed_ips']: self._build_ipv4v6_mac_ip_list(port['mac_address'], ip, mac_ipv4_pairs, mac_ipv6_pairs) if not port['fixed_ips']: mac_ipv4_pairs.append((port['mac_address'], None)) mac_ipv6_pairs.append((port['mac_address'], None)) self._setup_spoof_filter_chain(port, self.iptables.ipv4['filter'], mac_ipv4_pairs, ipv4_rules) self._setup_spoof_filter_chain(port, self.iptables.ipv6['filter'], mac_ipv6_pairs, ipv6_rules) # Fixed rules for traffic after source address is verified # Allow dhcp client renewal and rebinding ipv4_rules += [comment_rule('-p udp -m udp --sport 68 --dport 67 ' '-j RETURN', comment=ic.DHCP_CLIENT)] # Drop Router Advts from the port. ipv6_rules += [comment_rule('-p ipv6-icmp -m icmp6 --icmpv6-type %s ' '-j DROP' % constants.ICMPV6_TYPE_RA, comment=ic.IPV6_RA_DROP)] ipv6_rules += [comment_rule('-p ipv6-icmp -j RETURN', comment=ic.IPV6_ICMP_ALLOW)] ipv6_rules += [comment_rule('-p udp -m udp --sport 546 ' '--dport 547 ' '-j RETURN', comment=ic.DHCP_CLIENT)] def _drop_dhcp_rule(self, ipv4_rules, ipv6_rules): #Note(nati) Drop dhcp packet from VM ipv4_rules += [comment_rule('-p udp -m udp --sport 67 ' '--dport 68 ' '-j DROP', comment=ic.DHCP_SPOOF)] ipv6_rules += [comment_rule('-p udp -m udp --sport 547 ' '--dport 546 ' '-j DROP', comment=ic.DHCP_SPOOF)] def _accept_inbound_icmpv6(self): # Allow multicast listener, neighbor solicitation and # neighbor advertisement into the instance icmpv6_rules = [] for icmp6_type in firewall.ICMPV6_ALLOWED_INGRESS_TYPES: icmpv6_rules += ['-p ipv6-icmp -m icmp6 --icmpv6-type %s ' '-j RETURN' % icmp6_type] return icmpv6_rules def _select_sg_rules_for_port(self, port, direction): """Select rules from the security groups the port is member of.""" port_sg_ids = port.get('security_groups', []) port_rules = [] for sg_id in port_sg_ids: for rule in self.sg_rules.get(sg_id, []): if rule['direction'] == direction: if self.enable_ipset: port_rules.append(rule) else: port_rules.extend( self._expand_sg_rule_with_remote_ips( rule, port, direction)) return port_rules def _expand_sg_rule_with_remote_ips(self, rule, port, direction): """Expand a remote group rule to rule per remote group IP.""" remote_group_id = rule.get('remote_group_id') if remote_group_id: ethertype = rule['ethertype'] port_ips = port.get('fixed_ips', []) for ip in self.sg_members[remote_group_id][ethertype]: if ip not in port_ips: ip_rule = rule.copy() direction_ip_prefix = firewall.DIRECTION_IP_PREFIX[ direction] ip_prefix = str(netaddr.IPNetwork(ip).cidr) ip_rule[direction_ip_prefix] = ip_prefix yield ip_rule else: yield rule def _get_remote_sg_ids(self, port, direction=None): sg_ids = port.get('security_groups', []) remote_sg_ids = {constants.IPv4: set(), constants.IPv6: set()} for sg_id in sg_ids: for rule in self.sg_rules.get(sg_id, []): if not direction or rule['direction'] == direction: remote_sg_id = rule.get('remote_group_id') ether_type = rule.get('ethertype') if remote_sg_id and ether_type: remote_sg_ids[ether_type].add(remote_sg_id) return remote_sg_ids def _add_rules_by_security_group(self, port, direction): # select rules for current port and direction security_group_rules = self._select_sgr_by_direction(port, direction) security_group_rules += self._select_sg_rules_for_port(port, direction) # split groups by ip version # for ipv4, iptables command is used # for ipv6, iptables6 command is used ipv4_sg_rules, ipv6_sg_rules = self._split_sgr_by_ethertype( security_group_rules) ipv4_iptables_rules = [] ipv6_iptables_rules = [] # include fixed egress/ingress rules if direction == firewall.EGRESS_DIRECTION: self._add_fixed_egress_rules(port, ipv4_iptables_rules, ipv6_iptables_rules) elif direction == firewall.INGRESS_DIRECTION: ipv6_iptables_rules += self._accept_inbound_icmpv6() # include IPv4 and IPv6 iptable rules from security group ipv4_iptables_rules += self._convert_sgr_to_iptables_rules( ipv4_sg_rules) ipv6_iptables_rules += self._convert_sgr_to_iptables_rules( ipv6_sg_rules) # finally add the rules to the port chain for a given direction self._add_rules_to_chain_v4v6(self._port_chain_name(port, direction), ipv4_iptables_rules, ipv6_iptables_rules) def _add_fixed_egress_rules(self, port, ipv4_iptables_rules, ipv6_iptables_rules): self._spoofing_rule(port, ipv4_iptables_rules, ipv6_iptables_rules) self._drop_dhcp_rule(ipv4_iptables_rules, ipv6_iptables_rules) def _generate_ipset_rule_args(self, sg_rule, remote_gid): ethertype = sg_rule.get('ethertype') ipset_name = self.ipset.get_name(remote_gid, ethertype) if not self.ipset.set_name_exists(ipset_name): #NOTE(mangelajo): ipsets for empty groups are not created # thus we can't reference them. return None ipset_direction = IPSET_DIRECTION[sg_rule.get('direction')] args = self._generate_protocol_and_port_args(sg_rule) args += ['-m set', '--match-set', ipset_name, ipset_direction] args += ['-j RETURN'] return args def _generate_protocol_and_port_args(self, sg_rule): is_port = (sg_rule.get('source_port_range_min') is not None or sg_rule.get('port_range_min') is not None) args = self._protocol_arg(sg_rule.get('protocol'), is_port) args += self._port_arg('sport', sg_rule.get('protocol'), sg_rule.get('source_port_range_min'), sg_rule.get('source_port_range_max')) args += self._port_arg('dport', sg_rule.get('protocol'), sg_rule.get('port_range_min'), sg_rule.get('port_range_max')) return args def _generate_plain_rule_args(self, sg_rule): # These arguments MUST be in the format iptables-save will # display them: source/dest, protocol, sport, dport, target # Otherwise the iptables_manager code won't be able to find # them to preserve their [packet:byte] counts. args = self._ip_prefix_arg('s', sg_rule.get('source_ip_prefix')) args += self._ip_prefix_arg('d', sg_rule.get('dest_ip_prefix')) args += self._generate_protocol_and_port_args(sg_rule) args += ['-j RETURN'] return args def _convert_sg_rule_to_iptables_args(self, sg_rule): remote_gid = sg_rule.get('remote_group_id') if self.enable_ipset and remote_gid: return self._generate_ipset_rule_args(sg_rule, remote_gid) else: return self._generate_plain_rule_args(sg_rule) def _convert_sgr_to_iptables_rules(self, security_group_rules): iptables_rules = [] self._allow_established(iptables_rules) seen_sg_rules = set() for rule in security_group_rules: args = self._convert_sg_rule_to_iptables_args(rule) if args: rule_command = ' '.join(args) if rule_command in seen_sg_rules: # since these rules are from multiple security groups, # there may be duplicates so we prune them out here continue seen_sg_rules.add(rule_command) iptables_rules.append(rule_command) self._drop_invalid_packets(iptables_rules) iptables_rules += [comment_rule('-j $sg-fallback', comment=ic.UNMATCHED)] return iptables_rules def _drop_invalid_packets(self, iptables_rules): # Always drop invalid packets iptables_rules += [comment_rule('-m state --state ' 'INVALID -j DROP', comment=ic.INVALID_DROP)] return iptables_rules def _allow_established(self, iptables_rules): # Allow established connections iptables_rules += [comment_rule( '-m state --state RELATED,ESTABLISHED -j RETURN', comment=ic.ALLOW_ASSOC)] return iptables_rules def _local_protocol_name_map(self): local_protocol_name_map = {} try: class protoent(ctypes.Structure): _fields_ = [("p_name", ctypes.c_char_p), ("p_aliases", ctypes.POINTER(ctypes.c_char_p)), ("p_proto", ctypes.c_int)] libc.getprotoent.restype = ctypes.POINTER(protoent) libc.setprotoent(0) while True: pr = libc.getprotoent() if not pr: break r = pr[0] p_name = helpers.safe_decode_utf8(r.p_name) local_protocol_name_map[str(r.p_proto)] = p_name except Exception: LOG.exception("Unable to create local protocol name map: %s", sys.exc_info()[0]) finally: libc.endprotoent() return local_protocol_name_map def _protocol_name_map(self): if not self._iptables_protocol_name_map: tmp_map = n_const.IPTABLES_PROTOCOL_NAME_MAP.copy() tmp_map.update(self._local_protocol_name_map()) self._iptables_protocol_name_map = tmp_map return self._iptables_protocol_name_map def _iptables_protocol_name(self, protocol): # protocol zero is a special case and requires no '-p' if protocol and protocol != '0': return self._protocol_name_map().get(protocol, protocol) def _protocol_arg(self, protocol, is_port): iptables_rule = [] rule_protocol = self._iptables_protocol_name(protocol) # protocol zero is a special case and requires no '-p' if rule_protocol: iptables_rule = ['-p', rule_protocol] if (is_port and rule_protocol in constants.IPTABLES_PROTOCOL_MAP): # iptables adds '-m protocol' when the port number is specified iptables_rule += ['-m', constants.IPTABLES_PROTOCOL_MAP[rule_protocol]] return iptables_rule def _port_arg(self, direction, protocol, port_range_min, port_range_max): args = [] if port_range_min is None: return args protocol = self._iptables_protocol_name(protocol) if protocol in ['icmp', 'ipv6-icmp']: protocol_type = 'icmpv6' if protocol == 'ipv6-icmp' else 'icmp' # Note(xuhanp): port_range_min/port_range_max represent # icmp type/code when protocol is icmp or icmpv6 args += ['--%s-type' % protocol_type, '%s' % port_range_min] # icmp code can be 0 so we cannot use "if port_range_max" here if port_range_max is not None: args[-1] += '/%s' % port_range_max elif protocol in IPTABLES_PORT_PROTOCOLS: if port_range_min == port_range_max: args += ['--%s' % direction, '%s' % (port_range_min,)] else: args += ['-m', 'multiport', '--%ss' % direction, '%s:%s' % (port_range_min, port_range_max)] return args def _ip_prefix_arg(self, direction, ip_prefix): #NOTE (nati) : source_group_id is converted to list of source_ # ip_prefix in server side if ip_prefix: if '/' not in ip_prefix: # we need to convert it into a prefix to match iptables ip_prefix = c_utils.ip_to_cidr(ip_prefix) elif ip_prefix.endswith('/0'): # an allow for every address is not a constraint so # iptables drops it return [] return ['-%s' % direction, ip_prefix] return [] def _port_chain_name(self, port, direction): return iptables_manager.get_chain_name( '%s%s' % (CHAIN_NAME_PREFIX[direction], port['device'][3:])) def filter_defer_apply_on(self): if not self._defer_apply: self.iptables.defer_apply_on() self._pre_defer_filtered_ports = dict(self.filtered_ports) self._pre_defer_unfiltered_ports = dict(self.unfiltered_ports) self.pre_sg_members = dict(self.sg_members) self.pre_sg_rules = dict(self.sg_rules) self._defer_apply = True def _remove_unused_security_group_info(self): """Remove any unnecessary local security group info or unused ipsets. This function has to be called after applying the last iptables rules, so we're in a point where no iptable rule depends on an ipset we're going to delete. """ filtered_ports = self.filtered_ports.values() remote_sgs_to_remove = self._determine_remote_sgs_to_remove( filtered_ports) for ip_version, remote_sg_ids in remote_sgs_to_remove.items(): if self.enable_ipset: self._remove_ipsets_for_remote_sgs(ip_version, remote_sg_ids) self._remove_sg_members(remote_sgs_to_remove) # Remove unused security group rules for remove_group_id in self._determine_sg_rules_to_remove( filtered_ports): self.sg_rules.pop(remove_group_id, None) def _determine_remote_sgs_to_remove(self, filtered_ports): """Calculate which remote security groups we don't need anymore. We do the calculation for each ip_version. """ sgs_to_remove_per_ipversion = {constants.IPv4: set(), constants.IPv6: set()} remote_group_id_sets = self._get_remote_sg_ids_sets_by_ipversion( filtered_ports) for ip_version, remote_group_id_set in remote_group_id_sets.items(): sgs_to_remove_per_ipversion[ip_version].update( set(self.pre_sg_members) - remote_group_id_set) return sgs_to_remove_per_ipversion def _get_remote_sg_ids_sets_by_ipversion(self, filtered_ports): """Given a port, calculates the remote sg references by ip_version.""" remote_group_id_sets = {constants.IPv4: set(), constants.IPv6: set()} for port in filtered_ports: remote_sg_ids = self._get_remote_sg_ids(port) for ip_version in (constants.IPv4, constants.IPv6): remote_group_id_sets[ip_version] |= remote_sg_ids[ip_version] return remote_group_id_sets def _determine_sg_rules_to_remove(self, filtered_ports): """Calculate which security groups need to be removed. We find out by subtracting our previous sg group ids, with the security groups associated to a set of ports. """ port_group_ids = self._get_sg_ids_set_for_ports(filtered_ports) return set(self.pre_sg_rules) - port_group_ids def _get_sg_ids_set_for_ports(self, filtered_ports): """Get the port security group ids as a set.""" port_group_ids = set() for port in filtered_ports: port_group_ids.update(port.get('security_groups', [])) return port_group_ids def _remove_ipsets_for_remote_sgs(self, ip_version, remote_sg_ids): """Remove system ipsets matching the provided parameters.""" for remote_sg_id in remote_sg_ids: self.ipset.destroy(remote_sg_id, ip_version) def _remove_sg_members(self, remote_sgs_to_remove): """Remove sg_member entries.""" ipv4_sec_group_set = remote_sgs_to_remove.get(constants.IPv4) ipv6_sec_group_set = remote_sgs_to_remove.get(constants.IPv6) for sg_id in (ipv4_sec_group_set & ipv6_sec_group_set): if sg_id in self.sg_members: del self.sg_members[sg_id] def _find_deleted_sg_rules(self, sg_id): del_rules = list() for pre_rule in self.pre_sg_rules.get(sg_id, []): if pre_rule not in self.sg_rules.get(sg_id, []): del_rules.append(pre_rule) return del_rules def _find_devices_on_security_group(self, sg_id): device_list = list() for device in self.filtered_ports.values(): if sg_id in device.get('security_groups', []): device_list.append(device) return device_list def _clean_deleted_sg_rule_conntrack_entries(self): deleted_sg_ids = set() for sg_id in set(self.updated_rule_sg_ids): del_rules = self._find_deleted_sg_rules(sg_id) if not del_rules: continue device_list = self._find_devices_on_security_group(sg_id) for rule in del_rules: self.ipconntrack.delete_conntrack_state_by_rule( device_list, rule) deleted_sg_ids.add(sg_id) for id in deleted_sg_ids: self.updated_rule_sg_ids.remove(id) def _clean_updated_sg_member_conntrack_entries(self): updated_device_ids = set() for device in set(self.updated_sg_members): sec_group_change = False device_info = self.filtered_ports.get(device) pre_device_info = self._pre_defer_filtered_ports.get(device) if not device_info or not pre_device_info: continue for sg_id in pre_device_info.get('security_groups', []): if sg_id not in device_info.get('security_groups', []): sec_group_change = True break if not sec_group_change: continue for ethertype in [constants.IPv4, constants.IPv6]: self.ipconntrack.delete_conntrack_state_by_remote_ips( [device_info], ethertype, set()) updated_device_ids.add(device) for id in updated_device_ids: self.updated_sg_members.remove(id) def _clean_deleted_remote_sg_members_conntrack_entries(self): deleted_sg_ids = set() for sg_id, devices in self.devices_with_updated_sg_members.items(): for ethertype in [constants.IPv4, constants.IPv6]: pre_ips = self._get_sg_members( self.pre_sg_members, sg_id, ethertype) cur_ips = self._get_sg_members( self.sg_members, sg_id, ethertype) ips = (pre_ips - cur_ips) if devices and ips: self.ipconntrack.delete_conntrack_state_by_remote_ips( devices, ethertype, ips) deleted_sg_ids.add(sg_id) for id in deleted_sg_ids: self.devices_with_updated_sg_members.pop(id, None) def _remove_conntrack_entries_from_sg_updates(self): self._clean_deleted_sg_rule_conntrack_entries() self._clean_updated_sg_member_conntrack_entries() if not self.enable_ipset: self._clean_deleted_remote_sg_members_conntrack_entries() def _get_sg_members(self, sg_info, sg_id, ethertype): return set(sg_info.get(sg_id, {}).get(ethertype, [])) def filter_defer_apply_off(self): if self._defer_apply: self._defer_apply = False self._remove_chains_apply(self._pre_defer_filtered_ports, self._pre_defer_unfiltered_ports) self._setup_chains_apply(self.filtered_ports, self.unfiltered_ports) self.iptables.defer_apply_off() self._remove_conntrack_entries_from_sg_updates() self._remove_unused_security_group_info() self._pre_defer_filtered_ports = None self._pre_defer_unfiltered_ports = None class OVSHybridIptablesFirewallDriver(IptablesFirewallDriver): OVS_HYBRID_PLUG_REQUIRED = True CONNTRACK_ZONE_PER_PORT = True def _port_chain_name(self, port, direction): return iptables_manager.get_chain_name( '%s%s' % (CHAIN_NAME_PREFIX[direction], port['device'])) def _get_br_device_name(self, port): return ('qvb' + port['device'])[:n_const.LINUX_DEV_LEN] def _get_device_name(self, port): device_name = super( OVSHybridIptablesFirewallDriver, self)._get_device_name(port) return get_hybrid_port_name(device_name) neutron-12.1.1/neutron/agent/linux/iptables_manager.py0000664000175000017500000010141313553660047023117 0ustar zuulzuul00000000000000# Copyright 2012 Locaweb. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # based on # https://github.com/openstack/nova/blob/master/nova/network/linux_net.py """Implements iptables rules using linux utilities.""" import collections import contextlib import difflib import os import re import sys from neutron_lib.utils import runtime from oslo_concurrency import lockutils from oslo_config import cfg from oslo_log import log as logging from oslo_utils import excutils from neutron._i18n import _ from neutron.agent.linux import ip_lib from neutron.agent.linux import iptables_comments as ic from neutron.agent.linux import utils as linux_utils from neutron.common import constants from neutron.common import exceptions as n_exc from neutron.common import utils from neutron.conf.agent import common as config LOG = logging.getLogger(__name__) config.register_iptables_opts(cfg.CONF) # NOTE(vish): Iptables supports chain names of up to 28 characters, and we # add up to 12 characters to binary_name which is used as a prefix, # so we limit it to 16 characters. # (max_chain_name_length - len('-POSTROUTING') == 16) def get_binary_name(): """Grab the name of the binary we're running in.""" return os.path.basename(sys.argv[0])[:16].replace(' ', '_') binary_name = get_binary_name() # A length of a chain name must be less than or equal to 11 characters. # - ( + '-') = 28-(16+1) = 11 MAX_CHAIN_LEN_WRAP = 11 MAX_CHAIN_LEN_NOWRAP = 28 # Number of iptables rules to print before and after a rule that causes a # a failure during iptables-restore IPTABLES_ERROR_LINES_OF_CONTEXT = 5 # RESOURCE_PROBLEM in include/xtables.h XTABLES_RESOURCE_PROBLEM_CODE = 4 # xlock wait interval, in microseconds XLOCK_WAIT_INTERVAL = 200000 def comment_rule(rule, comment): if not cfg.CONF.AGENT.comment_iptables_rules or not comment: return rule # iptables-save outputs the comment before the jump so we need to match # that order so _find_last_entry works comment = '-m comment --comment "%s"' % comment if rule.startswith('-j'): # this is a jump only rule so we just put the comment first return '%s %s' % (comment, rule) try: jpos = rule.index(' -j ') return ' '.join((rule[:jpos], comment, rule[jpos + 1:])) except ValueError: return '%s %s' % (rule, comment) def get_chain_name(chain_name, wrap=True): if wrap: return chain_name[:MAX_CHAIN_LEN_WRAP] else: return chain_name[:MAX_CHAIN_LEN_NOWRAP] class IptablesRule(object): """An iptables rule. You shouldn't need to use this class directly, it's only used by IptablesManager. """ def __init__(self, chain, rule, wrap=True, top=False, binary_name=binary_name, tag=None, comment=None): self.chain = get_chain_name(chain, wrap) self.rule = rule self.wrap = wrap self.top = top self.wrap_name = binary_name[:16] self.tag = tag self.comment = comment def __eq__(self, other): return ((self.chain == other.chain) and (self.rule == other.rule) and (self.top == other.top) and (self.wrap == other.wrap)) def __ne__(self, other): return not self == other def __str__(self): if self.wrap: chain = '%s-%s' % (self.wrap_name, self.chain) else: chain = self.chain rule = '-A %s %s' % (chain, self.rule) # If self.rule is '' the above will cause a trailing space, which # could cause us to not match on save/restore, so strip it now. return comment_rule(rule.strip(), self.comment) class IptablesTable(object): """An iptables table.""" def __init__(self, binary_name=binary_name): self.rules = [] self.remove_rules = [] self.chains = set() self.unwrapped_chains = set() self.remove_chains = set() self.wrap_name = binary_name[:16] def add_chain(self, name, wrap=True): """Adds a named chain to the table. The chain name is wrapped to be unique for the component creating it, so different components of Nova can safely create identically named chains without interfering with one another. At the moment, its wrapped name is -, so if neutron-openvswitch-agent creates a chain named 'OUTPUT', it'll actually end up being named 'neutron-openvswi-OUTPUT'. """ name = get_chain_name(name, wrap) if wrap: self.chains.add(name) else: self.unwrapped_chains.add(name) def _select_chain_set(self, wrap): if wrap: return self.chains else: return self.unwrapped_chains def remove_chain(self, name, wrap=True): """Remove named chain. This removal "cascades". All rule in the chain are removed, as are all rules in other chains that jump to it. If the chain is not found, this is merely logged. """ name = get_chain_name(name, wrap) chain_set = self._select_chain_set(wrap) if name not in chain_set: LOG.debug('Attempted to remove chain %s which does not exist', name) return chain_set.remove(name) if not wrap: # non-wrapped chains and rules need to be dealt with specially, # so we keep a list of them to be iterated over in apply() self.remove_chains.add(name) # Add rules to remove that have a matching chain name or # a matching jump chain jump_snippet = '-j %s' % name self.remove_rules += [str(r) for r in self.rules if r.chain == name or jump_snippet in r.rule] else: jump_snippet = '-j %s-%s' % (self.wrap_name, name) # Remove rules from list that have a matching chain name or # a matching jump chain self.rules = [r for r in self.rules if r.chain != name and jump_snippet not in r.rule] def add_rule(self, chain, rule, wrap=True, top=False, tag=None, comment=None): """Add a rule to the table. This is just like what you'd feed to iptables, just without the '-A ' bit at the start. However, if you need to jump to one of your wrapped chains, prepend its name with a '$' which will ensure the wrapping is applied correctly. """ chain = get_chain_name(chain, wrap) if wrap and chain not in self.chains: raise LookupError(_('Unknown chain: %r') % chain) if '$' in rule: rule = ' '.join( self._wrap_target_chain(e, wrap) for e in rule.split(' ')) self.rules.append(IptablesRule(chain, rule, wrap, top, self.wrap_name, tag, comment)) def _wrap_target_chain(self, s, wrap): if s.startswith('$'): s = ('%s-%s' % (self.wrap_name, get_chain_name(s[1:], wrap))) return s def remove_rule(self, chain, rule, wrap=True, top=False, comment=None): """Remove a rule from a chain. Note: The rule must be exactly identical to the one that was added. You cannot switch arguments around like you can with the iptables CLI tool. """ chain = get_chain_name(chain, wrap) try: if '$' in rule: rule = ' '.join( self._wrap_target_chain(e, wrap) for e in rule.split(' ')) self.rules.remove(IptablesRule(chain, rule, wrap, top, self.wrap_name, comment=comment)) if not wrap: self.remove_rules.append(str(IptablesRule(chain, rule, wrap, top, self.wrap_name, comment=comment))) except ValueError: LOG.warning('Tried to remove rule that was not there:' ' %(chain)r %(rule)r %(wrap)r %(top)r', {'chain': chain, 'rule': rule, 'top': top, 'wrap': wrap}) def _get_chain_rules(self, chain, wrap): chain = get_chain_name(chain, wrap) return [rule for rule in self.rules if rule.chain == chain and rule.wrap == wrap] def empty_chain(self, chain, wrap=True): """Remove all rules from a chain.""" chained_rules = self._get_chain_rules(chain, wrap) for rule in chained_rules: self.rules.remove(rule) def clear_rules_by_tag(self, tag): if not tag: return rules = [rule for rule in self.rules if rule.tag == tag] for rule in rules: self.rules.remove(rule) class IptablesManager(object): """Wrapper for iptables. See IptablesTable for some usage docs A number of chains are set up to begin with. First, neutron-filter-top. It's added at the top of FORWARD and OUTPUT. Its name is not wrapped, so it's shared between the various neutron workers. It's intended for rules that need to live at the top of the FORWARD and OUTPUT chains. It's in both the ipv4 and ipv6 set of tables. For ipv4 and ipv6, the built-in INPUT, OUTPUT, and FORWARD filter chains are wrapped, meaning that the "real" INPUT chain has a rule that jumps to the wrapped INPUT chain, etc. Additionally, there's a wrapped chain named "local" which is jumped to from neutron-filter-top. For ipv4, the built-in PREROUTING, OUTPUT, and POSTROUTING nat chains are wrapped in the same was as the built-in filter chains. Additionally, there's a snat chain that is applied after the POSTROUTING chain. """ # Flag to denote we've already tried and used -w successfully, so don't # run iptables-restore without it. use_table_lock = False # Flag to denote iptables supports --random-fully argument _random_fully = None def __init__(self, _execute=None, state_less=False, use_ipv6=False, namespace=None, binary_name=binary_name): if _execute: self.execute = _execute else: self.execute = linux_utils.execute self.use_ipv6 = use_ipv6 self.namespace = namespace self.iptables_apply_deferred = False self.wrap_name = binary_name[:16] self.ipv4 = {'filter': IptablesTable(binary_name=self.wrap_name)} self.ipv6 = {'filter': IptablesTable(binary_name=self.wrap_name)} # Add a neutron-filter-top chain. It's intended to be shared # among the various neutron components. It sits at the very top # of FORWARD and OUTPUT. for tables in [self.ipv4, self.ipv6]: tables['filter'].add_chain('neutron-filter-top', wrap=False) tables['filter'].add_rule('FORWARD', '-j neutron-filter-top', wrap=False, top=True) tables['filter'].add_rule('OUTPUT', '-j neutron-filter-top', wrap=False, top=True) tables['filter'].add_chain('local') tables['filter'].add_rule('neutron-filter-top', '-j $local', wrap=False) self.ipv4.update({'raw': IptablesTable(binary_name=self.wrap_name)}) self.ipv6.update({'raw': IptablesTable(binary_name=self.wrap_name)}) # Wrap the built-in chains builtin_chains = {4: {'filter': ['INPUT', 'OUTPUT', 'FORWARD']}, 6: {'filter': ['INPUT', 'OUTPUT', 'FORWARD']}} builtin_chains[4].update({'raw': ['PREROUTING', 'OUTPUT']}) builtin_chains[6].update({'raw': ['PREROUTING', 'OUTPUT']}) self._configure_builtin_chains(builtin_chains) if not state_less: self.initialize_mangle_table() self.initialize_nat_table() def initialize_mangle_table(self): self.ipv4.update( {'mangle': IptablesTable(binary_name=self.wrap_name)}) self.ipv6.update( {'mangle': IptablesTable(binary_name=self.wrap_name)}) builtin_chains = { 4: {'mangle': ['PREROUTING', 'INPUT', 'FORWARD', 'OUTPUT', 'POSTROUTING']}, 6: {'mangle': ['PREROUTING', 'INPUT', 'FORWARD', 'OUTPUT', 'POSTROUTING']}} self._configure_builtin_chains(builtin_chains) # Add a mark chain to mangle PREROUTING chain. It is used to # identify ingress packets from a certain interface. self.ipv4['mangle'].add_chain('mark') self.ipv4['mangle'].add_rule('PREROUTING', '-j $mark') def initialize_nat_table(self): self.ipv4.update( {'nat': IptablesTable(binary_name=self.wrap_name)}) builtin_chains = { 4: {'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING']}} self._configure_builtin_chains(builtin_chains) # Add a neutron-postrouting-bottom chain. It's intended to be # shared among the various neutron components. We set it as the # last chain of POSTROUTING chain. self.ipv4['nat'].add_chain('neutron-postrouting-bottom', wrap=False) self.ipv4['nat'].add_rule( 'POSTROUTING', '-j neutron-postrouting-bottom', wrap=False) # We add a snat chain to the shared neutron-postrouting-bottom # chain so that it's applied last. self.ipv4['nat'].add_chain('snat') self.ipv4['nat'].add_rule('neutron-postrouting-bottom', '-j $snat', wrap=False, comment=ic.SNAT_OUT) # And then we add a float-snat chain and jump to first thing in # the snat chain. self.ipv4['nat'].add_chain('float-snat') self.ipv4['nat'].add_rule('snat', '-j $float-snat') def _configure_builtin_chains(self, builtin_chains): for ip_version in builtin_chains: if ip_version == 4: tables = self.ipv4 elif ip_version == 6: tables = self.ipv6 for table, chains in builtin_chains[ip_version].items(): for chain in chains: tables[table].add_chain(chain) tables[table].add_rule(chain, '-j $%s' % (chain), wrap=False) def get_tables(self, ip_version): return {4: self.ipv4, 6: self.ipv6}[ip_version] def get_chain(self, table, chain, ip_version=4, wrap=True): try: requested_table = self.get_tables(ip_version)[table] except KeyError: return [] return requested_table._get_chain_rules(chain, wrap) def is_chain_empty(self, table, chain, ip_version=4, wrap=True): return not self.get_chain(table, chain, ip_version, wrap) @contextlib.contextmanager def defer_apply(self): """Defer apply context.""" self.defer_apply_on() try: yield finally: try: self.defer_apply_off() except n_exc.IpTablesApplyException: # already in the format we want, just reraise raise except Exception: msg = _('Failure applying iptables rules') LOG.exception(msg) raise n_exc.IpTablesApplyException(msg) def defer_apply_on(self): self.iptables_apply_deferred = True def defer_apply_off(self): self.iptables_apply_deferred = False self._apply() def apply(self): if self.iptables_apply_deferred: return return self._apply() def _apply(self): lock_name = 'iptables' if self.namespace: lock_name += '-' + self.namespace # NOTE(ihrachys) we may get rid of the lock once all supported # platforms get iptables with 999eaa241212d3952ddff39a99d0d55a74e3639e # ("iptables-restore: support acquiring the lock.") with lockutils.lock(lock_name, runtime.SYNCHRONIZED_PREFIX, True): first = self._apply_synchronized() if not cfg.CONF.AGENT.debug_iptables_rules: return first second = self._apply_synchronized() if second: msg = (_("IPTables Rules did not converge. Diff: %s") % '\n'.join(second)) LOG.error(msg) raise n_exc.IpTablesApplyException(msg) return first def get_rules_for_table(self, table): """Runs iptables-save on a table and returns the results.""" args = ['iptables-save', '-t', table] if self.namespace: args = ['ip', 'netns', 'exec', self.namespace] + args return self.execute(args, run_as_root=True).split('\n') def _get_version(self): # Output example is "iptables v1.6.2" args = ['iptables', '--version'] version = str(self.execute(args, run_as_root=True).split()[1][1:]) LOG.debug("IPTables version installed: %s", version) return version @property def random_fully(self): if self._random_fully is not None: return self._random_fully version = self._get_version() self.__class__._random_fully = utils.is_version_greater_equal( version, constants.IPTABLES_RANDOM_FULLY_VERSION) return self._random_fully @property def xlock_wait_time(self): # give agent some time to report back to server return str(max(int(cfg.CONF.AGENT.report_interval / 3.0), 1)) def _do_run_restore(self, args, commands, lock=False): args = args[:] if lock: args += ['-w', self.xlock_wait_time, '-W', XLOCK_WAIT_INTERVAL] try: kwargs = {} if lock else {'log_fail_as_error': False} self.execute(args, process_input='\n'.join(commands), run_as_root=True, **kwargs) except RuntimeError as error: return error def _run_restore(self, args, commands): # If we've already tried and used -w successfully, don't # run iptables-restore without it. if self.use_table_lock: return self._do_run_restore(args, commands, lock=True) err = self._do_run_restore(args, commands) if (isinstance(err, n_exc.ProcessExecutionError) and err.returncode == XTABLES_RESOURCE_PROBLEM_CODE): # maybe we run on a platform that includes iptables commit # 999eaa241212d3952ddff39a99d0d55a74e3639e (for example, latest # RHEL) and failed because of xlock acquired by another # iptables process running in parallel. Try to use -w to # acquire xlock. err = self._do_run_restore(args, commands, lock=True) if not err: self.__class__.use_table_lock = True return err def _log_restore_err(self, err, commands): try: line_no = int(re.search( 'iptables-restore: line ([0-9]+?) failed', str(err)).group(1)) context = IPTABLES_ERROR_LINES_OF_CONTEXT log_start = max(0, line_no - context) log_end = line_no + context except AttributeError: # line error wasn't found, print all lines instead log_start = 0 log_end = len(commands) log_lines = ('%7d. %s' % (idx, l) for idx, l in enumerate( commands[log_start:log_end], log_start + 1) ) LOG.error("IPTablesManager.apply failed to apply the " "following set of iptables rules:\n%s", '\n'.join(log_lines)) def _apply_synchronized(self): """Apply the current in-memory set of iptables rules. This will create a diff between the rules from the previous runs and replace them with the current set of rules. This happens atomically, thanks to iptables-restore. Returns a list of the changes that were sent to iptables-save. """ s = [('iptables', self.ipv4)] if self.use_ipv6: s += [('ip6tables', self.ipv6)] all_commands = [] # variable to keep track all commands for return val for cmd, tables in s: args = ['%s-save' % (cmd,)] if self.namespace: args = ['ip', 'netns', 'exec', self.namespace] + args try: save_output = self.execute(args, run_as_root=True) except RuntimeError: # We could be racing with a cron job deleting namespaces. # It is useless to try to apply iptables rules over and # over again in a endless loop if the namespace does not # exist. with excutils.save_and_reraise_exception() as ctx: if (self.namespace and not ip_lib.network_namespace_exists(self.namespace)): ctx.reraise = False LOG.error("Namespace %s was deleted during IPTables " "operations.", self.namespace) return [] all_lines = save_output.split('\n') commands = [] # Traverse tables in sorted order for predictable dump output for table_name in sorted(tables): table = tables[table_name] # isolate the lines of the table we are modifying start, end = self._find_table(all_lines, table_name) old_rules = all_lines[start:end] # generate the new table state we want new_rules = self._modify_rules(old_rules, table, table_name) # generate the iptables commands to get between the old state # and the new state changes = _generate_path_between_rules(old_rules, new_rules) if changes: # if there are changes to the table, we put on the header # and footer that iptables-save needs commands += (['# Generated by iptables_manager'] + ['*%s' % table_name] + changes + ['COMMIT', '# Completed by iptables_manager']) if not commands: continue all_commands += commands # always end with a new line commands.append('') args = ['%s-restore' % (cmd,), '-n'] if self.namespace: args = ['ip', 'netns', 'exec', self.namespace] + args err = self._run_restore(args, commands) if err: self._log_restore_err(err, commands) raise err LOG.debug("IPTablesManager.apply completed with success. %d iptables " "commands were issued", len(all_commands)) return all_commands def _find_table(self, lines, table_name): if len(lines) < 3: # length only <2 when fake iptables return (0, 0) try: start = lines.index('*%s' % table_name) except ValueError: # Couldn't find table_name LOG.debug('Unable to find table %s', table_name) return (0, 0) end = lines[start:].index('COMMIT') + start + 1 return (start, end) def _find_rules_index(self, lines): seen_chains = False rules_index = 0 for rules_index, rule in enumerate(lines): if not seen_chains: if rule.startswith(':'): seen_chains = True else: if not rule.startswith(':'): break if not seen_chains: rules_index = 2 return rules_index def _modify_rules(self, current_lines, table, table_name): # Chains are stored as sets to avoid duplicates. # Sort the output chains here to make their order predictable. unwrapped_chains = sorted(table.unwrapped_chains) chains = sorted(table.chains) rules = set(map(str, table.rules)) # we don't want to change any rules that don't belong to us so we start # the new_filter with these rules # there are some rules that belong to us but they don't have the wrap # name. we want to add them in the right location in case our new rules # changed the order # (e.g. '-A FORWARD -j neutron-filter-top') new_filter = [line.strip() for line in current_lines if self.wrap_name not in line and line.strip() not in rules] # generate our list of chain names our_chains = [':%s-%s' % (self.wrap_name, name) for name in chains] # the unwrapped chains (e.g. neutron-filter-top) may already exist in # the new_filter since they aren't marked by the wrap_name so we only # want to add them if they arent' already there our_chains += [':%s' % name for name in unwrapped_chains if not any(':%s' % name in s for s in new_filter)] our_top_rules = [] our_bottom_rules = [] for rule in table.rules: rule_str = str(rule) if rule.top: # rule.top == True means we want this rule to be at the top. our_top_rules += [rule_str] else: our_bottom_rules += [rule_str] our_chains_and_rules = our_chains + our_top_rules + our_bottom_rules # locate the position immediately after the existing chains to insert # our chains and rules rules_index = self._find_rules_index(new_filter) new_filter[rules_index:rules_index] = our_chains_and_rules def _weed_out_removes(line): # remove any rules or chains from the filter that were slated # for removal if line.startswith(':'): chain = line[1:] if chain in table.remove_chains: table.remove_chains.remove(chain) return False else: if line in table.remove_rules: table.remove_rules.remove(line) return False # Leave it alone return True seen_lines = set() # TODO(kevinbenton): remove this function and the next one. They are # just oversized brooms to sweep bugs under the rug!!! We generate the # rules and we shouldn't be generating duplicates. def _weed_out_duplicates(line): if line in seen_lines: thing = 'chain' if line.startswith(':') else 'rule' LOG.warning("Duplicate iptables %(thing)s detected. This " "may indicate a bug in the iptables " "%(thing)s generation code. Line: %(line)s", {'thing': thing, 'line': line}) return False seen_lines.add(line) # Leave it alone return True new_filter.reverse() new_filter = [line for line in new_filter if _weed_out_duplicates(line) and _weed_out_removes(line)] new_filter.reverse() # flush lists, just in case a rule or chain marked for removal # was already gone. (chains is a set, rules is a list) table.remove_chains.clear() table.remove_rules = [] return new_filter def _get_traffic_counters_cmd_tables(self, chain, wrap=True): name = get_chain_name(chain, wrap) cmd_tables = [('iptables', key) for key, table in self.ipv4.items() if name in table._select_chain_set(wrap)] if self.use_ipv6: cmd_tables += [('ip6tables', key) for key, table in self.ipv6.items() if name in table._select_chain_set(wrap)] return cmd_tables def get_traffic_counters(self, chain, wrap=True, zero=False): """Return the sum of the traffic counters of all rules of a chain.""" cmd_tables = self._get_traffic_counters_cmd_tables(chain, wrap) if not cmd_tables: LOG.warning('Attempted to get traffic counters of chain %s ' 'which does not exist', chain) return name = get_chain_name(chain, wrap) acc = {'pkts': 0, 'bytes': 0} for cmd, table in cmd_tables: args = [cmd, '-t', table, '-L', name, '-n', '-v', '-x', '-w', self.xlock_wait_time] if zero: args.append('-Z') if self.namespace: args = ['ip', 'netns', 'exec', self.namespace] + args current_table = self.execute(args, run_as_root=True) current_lines = current_table.split('\n') for line in current_lines[2:]: if not line: break data = line.split() if (len(data) < 2 or not data[0].isdigit() or not data[1].isdigit()): break acc['pkts'] += int(data[0]) acc['bytes'] += int(data[1]) return acc def _generate_path_between_rules(old_rules, new_rules): """Generates iptables commands to get from old_rules to new_rules. This function diffs the two rule sets and then calculates the iptables commands necessary to get from the old rules to the new rules using insert and delete commands. """ old_by_chain = _get_rules_by_chain(old_rules) new_by_chain = _get_rules_by_chain(new_rules) old_chains, new_chains = set(old_by_chain.keys()), set(new_by_chain.keys()) # all referenced chains should be declared at the top before rules. # NOTE(kevinbenton): sorting and grouping chains is for determinism in # tests. iptables doesn't care about the order here statements = [':%s - [0:0]' % c for c in sorted(new_chains - old_chains)] sg_chains = [] other_chains = [] for chain in sorted(old_chains | new_chains): if '-sg-' in chain: sg_chains.append(chain) else: other_chains.append(chain) for chain in other_chains + sg_chains: statements += _generate_chain_diff_iptables_commands( chain, old_by_chain[chain], new_by_chain[chain]) # unreferenced chains get the axe for chain in sorted(old_chains - new_chains): statements += ['-X %s' % chain] return statements def _get_rules_by_chain(rules): by_chain = collections.defaultdict(list) for line in rules: if line.startswith(':'): chain = line[1:].split(' ', 1)[0] # even though this is a default dict, we need to manually add # chains to ensure that ones without rules are included because # they might be a jump reference if chain not in by_chain: by_chain[chain] = [] elif line.startswith('-A'): chain = line[3:].split(' ', 1)[0] by_chain[chain].append(line) return by_chain def _generate_chain_diff_iptables_commands(chain, old_chain_rules, new_chain_rules): # keep track of the old index because we have to insert rules # in the right position old_index = 1 statements = [] for line in difflib.ndiff(old_chain_rules, new_chain_rules): if line.startswith('?'): # skip ? because that's a guide string for intraline differences continue elif line.startswith('-'): # line deleted statements.append('-D %s %d' % (chain, old_index)) # since we are removing a line from the old rules, we # backup the index by 1 old_index -= 1 elif line.startswith('+'): # line added # strip the chain name since we have to add it before the index rule = line[5:].split(' ', 1)[-1] # IptablesRule does not add trailing spaces for rules, so we # have to detect that here by making sure this chain isn't # referencing itself if rule == chain: rule = '' # rule inserted at this position statements.append('-I %s %d %s' % (chain, old_index, rule)) old_index += 1 return statements neutron-12.1.1/neutron/agent/linux/ipset_manager.py0000664000175000017500000001662613553660047022453 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import copy import netaddr from neutron_lib.utils import runtime from neutron.agent.linux import utils as linux_utils IPSET_ADD_BULK_THRESHOLD = 5 NET_PREFIX = 'N' SWAP_SUFFIX = '-n' IPSET_NAME_MAX_LENGTH = 31 - len(SWAP_SUFFIX) class IpsetManager(object): """Smart wrapper for ipset. Keeps track of ip addresses per set, using bulk or single ip add/remove for smaller changes. """ def __init__(self, execute=None, namespace=None): self.execute = execute or linux_utils.execute self.namespace = namespace self.ipset_sets = {} def _sanitize_addresses(self, addresses): """This method converts any address to ipset format. If an address has a mask of /0 we need to cover to it to a mask of /1 as ipset does not support /0 length addresses. Instead we use two /1's to represent the /0. """ sanitized_addresses = [] for ip in addresses: ip = netaddr.IPNetwork(ip) if ip.prefixlen == 0: if ip.version == 4: sanitized_addresses.append('0.0.0.0/1') sanitized_addresses.append('128.0.0.0/1') elif ip.version == 6: sanitized_addresses.append('::/1') sanitized_addresses.append('8000::/1') else: sanitized_addresses.append(str(ip)) return sanitized_addresses @staticmethod def get_name(id, ethertype): """Returns the given ipset name for an id+ethertype pair. This reference can be used from iptables. """ name = NET_PREFIX + ethertype + id return name[:IPSET_NAME_MAX_LENGTH] def set_name_exists(self, set_name): """Returns true if the set name is known to the manager.""" return set_name in self.ipset_sets def set_members(self, id, ethertype, member_ips): """Create or update a specific set by name and ethertype. It will make sure that a set is created, updated to add / remove new members, or swapped atomically if that's faster, and return added / removed members. """ member_ips = self._sanitize_addresses(member_ips) set_name = self.get_name(id, ethertype) add_ips = self._get_new_set_ips(set_name, member_ips) del_ips = self._get_deleted_set_ips(set_name, member_ips) if add_ips or del_ips or not self.set_name_exists(set_name): self.set_members_mutate(set_name, ethertype, member_ips) return add_ips, del_ips @runtime.synchronized('ipset', external=True) def set_members_mutate(self, set_name, ethertype, member_ips): if not self.set_name_exists(set_name): # The initial creation is handled with create/refresh to # avoid any downtime for existing sets (i.e. avoiding # a flush/restore), as the restore operation of ipset is # additive to the existing set. self._create_set(set_name, ethertype) self._refresh_set(set_name, member_ips, ethertype) # TODO(majopela,shihanzhang,haleyb): Optimize this by # gathering the system ipsets at start. So we can determine # if a normal restore is enough for initial creation. # That should speed up agent boot up time. else: add_ips = self._get_new_set_ips(set_name, member_ips) del_ips = self._get_deleted_set_ips(set_name, member_ips) if (len(add_ips) + len(del_ips) < IPSET_ADD_BULK_THRESHOLD): self._add_members_to_set(set_name, add_ips) self._del_members_from_set(set_name, del_ips) else: self._refresh_set(set_name, member_ips, ethertype) @runtime.synchronized('ipset', external=True) def destroy(self, id, ethertype, forced=False): set_name = self.get_name(id, ethertype) self._destroy(set_name, forced) def _add_member_to_set(self, set_name, member_ip): cmd = ['ipset', 'add', '-exist', set_name, member_ip] self._apply(cmd) self.ipset_sets[set_name].append(member_ip) def _refresh_set(self, set_name, member_ips, ethertype): new_set_name = set_name + SWAP_SUFFIX set_type = self._get_ipset_set_type(ethertype) process_input = ["create %s hash:net family %s" % (new_set_name, set_type)] for ip in member_ips: process_input.append("add %s %s" % (new_set_name, ip)) self._restore_sets(process_input) self._swap_sets(new_set_name, set_name) self._destroy(new_set_name, True) self.ipset_sets[set_name] = copy.copy(member_ips) def _del_member_from_set(self, set_name, member_ip): cmd = ['ipset', 'del', set_name, member_ip] self._apply(cmd, fail_on_errors=False) self.ipset_sets[set_name].remove(member_ip) def _create_set(self, set_name, ethertype): cmd = ['ipset', 'create', '-exist', set_name, 'hash:net', 'family', self._get_ipset_set_type(ethertype)] self._apply(cmd) self.ipset_sets[set_name] = [] def _apply(self, cmd, input=None, fail_on_errors=True): input = '\n'.join(input) if input else None cmd_ns = [] if self.namespace: cmd_ns.extend(['ip', 'netns', 'exec', self.namespace]) cmd_ns.extend(cmd) self.execute(cmd_ns, run_as_root=True, process_input=input, check_exit_code=fail_on_errors) def _get_new_set_ips(self, set_name, expected_ips): new_member_ips = (set(expected_ips) - set(self.ipset_sets.get(set_name, []))) return list(new_member_ips) def _get_deleted_set_ips(self, set_name, expected_ips): deleted_member_ips = (set(self.ipset_sets.get(set_name, [])) - set(expected_ips)) return list(deleted_member_ips) def _add_members_to_set(self, set_name, add_ips): for ip in add_ips: if ip not in self.ipset_sets[set_name]: self._add_member_to_set(set_name, ip) def _del_members_from_set(self, set_name, del_ips): for ip in del_ips: if ip in self.ipset_sets[set_name]: self._del_member_from_set(set_name, ip) def _get_ipset_set_type(self, ethertype): return 'inet6' if ethertype == 'IPv6' else 'inet' def _restore_sets(self, process_input): cmd = ['ipset', 'restore', '-exist'] self._apply(cmd, process_input) def _swap_sets(self, src_set, dest_set): cmd = ['ipset', 'swap', src_set, dest_set] self._apply(cmd) def _destroy(self, set_name, forced=False): if set_name in self.ipset_sets or forced: cmd = ['ipset', 'destroy', set_name] self._apply(cmd, fail_on_errors=False) self.ipset_sets.pop(set_name, None) neutron-12.1.1/neutron/agent/linux/ip_link_support.py0000664000175000017500000000717713553660046023056 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import re from neutron_lib import exceptions as n_exc from oslo_log import log as logging from neutron._i18n import _ from neutron.agent.linux import utils LOG = logging.getLogger(__name__) class IpLinkSupportError(n_exc.NeutronException): pass class UnsupportedIpLinkCommand(IpLinkSupportError): message = _("ip link command is not supported: %(reason)s") class InvalidIpLinkCapability(IpLinkSupportError): message = _("ip link capability %(capability)s is not supported") class IpLinkConstants(object): IP_LINK_CAPABILITY_STATE = "state" IP_LINK_CAPABILITY_VLAN = "vlan" IP_LINK_CAPABILITY_RATE = "rate" IP_LINK_CAPABILITY_MIN_TX_RATE = "min_tx_rate" IP_LINK_CAPABILITY_SPOOFCHK = "spoofchk" IP_LINK_SUB_CAPABILITY_QOS = "qos" class IpLinkSupport(object): VF_BLOCK_REGEX = r"\[ vf NUM(?P.*) \] \]" CAPABILITY_REGEX = r"\[ %s (.*)" SUB_CAPABILITY_REGEX = r"\[ %(cap)s (.*) \[ %(subcap)s (.*)" @classmethod def get_vf_mgmt_section(cls): """Parses ip link help output, and gets vf block""" output = cls._get_ip_link_output() vf_block_pattern = re.search(cls.VF_BLOCK_REGEX, output, re.DOTALL | re.MULTILINE) if vf_block_pattern: return vf_block_pattern.group("vf_block") @classmethod def vf_mgmt_capability_supported(cls, vf_section, capability, subcapability=None): """Validate vf capability support Checks if given vf capability (and sub capability if given) supported :param vf_section: vf Num block content :param capability: for example: vlan, rate, spoofchk, state :param subcapability: for example: qos """ if not vf_section: return False if subcapability: regex = cls.SUB_CAPABILITY_REGEX % {"cap": capability, "subcap": subcapability} else: regex = cls.CAPABILITY_REGEX % capability pattern_match = re.search(regex, vf_section, re.DOTALL | re.MULTILINE) return pattern_match is not None @classmethod def _get_ip_link_output(cls): """Gets the output of the ip link help command Runs ip link help command and stores its output Note: ip link help return error and writes its output to stderr so we get the output from there. however, if this issue will be solved and the command will write to stdout, we will get the output from there too. """ try: ip_cmd = ['ip', 'link', 'help'] _stdout, _stderr = utils.execute( ip_cmd, check_exit_code=False, return_stderr=True, log_fail_as_error=False) except Exception as e: LOG.exception("Failed executing ip command") raise UnsupportedIpLinkCommand(reason=e) return _stdout or _stderr neutron-12.1.1/neutron/agent/linux/async_process.py0000664000175000017500000002546113553660047022505 0ustar zuulzuul00000000000000# Copyright 2013 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import signal import eventlet import eventlet.event from eventlet.green import subprocess import eventlet.queue from neutron_lib.utils import helpers from oslo_log import log as logging from neutron._i18n import _ from neutron.agent.common import ip_lib from neutron.agent.common import utils from neutron.common import utils as common_utils LOG = logging.getLogger(__name__) class AsyncProcessException(Exception): pass class AsyncProcess(object): """Manages an asynchronous process. This class spawns a new process via subprocess and uses greenthreads to read stderr and stdout asynchronously into queues that can be read via repeatedly calling iter_stdout() and iter_stderr(). If respawn_interval is non-zero, any error in communicating with the managed process will result in the process and greenthreads being cleaned up and the process restarted after the specified interval. Example usage: >>> import time >>> proc = AsyncProcess(['ping']) >>> proc.start() >>> time.sleep(5) >>> proc.stop() >>> for line in proc.iter_stdout(): ... print(line) """ def __init__(self, cmd, run_as_root=False, respawn_interval=None, namespace=None, log_output=False, die_on_error=False): """Constructor. :param cmd: The list of command arguments to invoke. :param run_as_root: The process should run with elevated privileges. :param respawn_interval: Optional, the interval in seconds to wait to respawn after unexpected process death. Respawn will only be attempted if a value of 0 or greater is provided. :param namespace: Optional, start the command in the specified namespace. :param log_output: Optional, also log received output. :param die_on_error: Optional, kills the process on stderr output. """ self.cmd_without_namespace = cmd self._cmd = ip_lib.add_namespace_to_cmd(cmd, namespace) self.run_as_root = run_as_root if respawn_interval is not None and respawn_interval < 0: raise ValueError(_('respawn_interval must be >= 0 if provided.')) self.respawn_interval = respawn_interval self._process = None self._pid = None self._is_running = False self._kill_event = None self._reset_queues() self._watchers = [] self.log_output = log_output self.die_on_error = die_on_error @property def cmd(self): return ' '.join(self._cmd) def _reset_queues(self): self._stdout_lines = eventlet.queue.LightQueue() self._stderr_lines = eventlet.queue.LightQueue() def is_active(self): # If using sudo rootwrap as a root_helper, we have to wait until sudo # spawns rootwrap and rootwrap spawns the process. self.pid will make # sure to get the correct pid. return utils.pid_invoked_with_cmdline( self.pid, self.cmd_without_namespace) def start(self, block=False): """Launch a process and monitor it asynchronously. :param block: Block until the process has started. :raises utils.WaitTimeout if blocking is True and the process did not start in time. """ LOG.debug('Launching async process [%s].', self.cmd) if self._is_running: raise AsyncProcessException(_('Process is already started')) else: self._spawn() if block: common_utils.wait_until_true(self.is_active) def stop(self, block=False, kill_signal=None, kill_timeout=None): """Halt the process and watcher threads. :param block: Block until the process has stopped. :param kill_signal: Number of signal that will be sent to the process when terminating the process :param kill_timeout: If given, process will be killed with SIGKILL if timeout will be reached and process will still be running :raises utils.WaitTimeout if blocking is True and the process did not stop in time. """ kill_signal = kill_signal or getattr(signal, 'SIGKILL', signal.SIGTERM) if self._is_running: LOG.debug('Halting async process [%s].', self.cmd) self._kill(kill_signal, kill_timeout) else: raise AsyncProcessException(_('Process is not running.')) if block: common_utils.wait_until_true(lambda: not self.is_active()) def _spawn(self): """Spawn a process and its watchers.""" self._is_running = True self._pid = None self._kill_event = eventlet.event.Event() self._process, cmd = utils.create_process(self._cmd, run_as_root=self.run_as_root) self._watchers = [] for reader in (self._read_stdout, self._read_stderr): # Pass the stop event directly to the greenthread to # ensure that assignment of a new event to the instance # attribute does not prevent the greenthread from using # the original event. watcher = eventlet.spawn(self._watch_process, reader, self._kill_event) self._watchers.append(watcher) @property def pid(self): if self._process: if not self._pid: self._pid = utils.get_root_helper_child_pid( self._process.pid, self.cmd_without_namespace, run_as_root=self.run_as_root) return self._pid def _kill(self, kill_signal, kill_timeout=None): """Kill the process and the associated watcher greenthreads.""" pid = self.pid if pid: self._is_running = False self._pid = None self._kill_process_and_wait(pid, kill_signal, kill_timeout) # Halt the greenthreads if they weren't already. if self._kill_event: self._kill_event.send() self._kill_event = None def _kill_process_and_wait(self, pid, kill_signal, kill_timeout=None): kill_result = self._kill_process(pid, kill_signal) if kill_result is False: return kill_result if self._process: try: self._process.wait(kill_timeout) except subprocess.TimeoutExpired: LOG.warning("Process %(pid)s [%(cmd)s] still running after " "%(timeout)d seconds. Sending %(signal)d to kill " "it.", {'pid': pid, 'cmd': self.cmd, 'timeout': kill_timeout, 'signal': signal.SIGKILL}) return self._kill_process(pid, signal.SIGKILL) return True def _kill_process(self, pid, kill_signal): try: # A process started by a root helper will be running as # root and need to be killed via the same helper. utils.kill_process(pid, kill_signal, self.run_as_root) except Exception: LOG.exception('An error occurred while killing [%s].', self.cmd) return False return True def _handle_process_error(self): """Kill the async process and respawn if necessary.""" LOG.debug('Halting async process [%s] in response to an error.', self.cmd) self._kill(getattr(signal, 'SIGKILL', signal.SIGTERM)) if self.respawn_interval is not None and self.respawn_interval >= 0: eventlet.sleep(self.respawn_interval) LOG.debug('Respawning async process [%s].', self.cmd) try: self.start() except AsyncProcessException: # Process was already respawned by someone else... pass def _watch_process(self, callback, kill_event): while not kill_event.ready(): try: output = callback() if not output and output != "": break except Exception: LOG.exception('An error occurred while communicating ' 'with async process [%s].', self.cmd) break # Ensure that watching a process with lots of output does # not block execution of other greenthreads. eventlet.sleep() # self._is_running being True indicates that the loop was # broken out of due to an error in the watched process rather # than the loop condition being satisfied. if self._is_running: self._is_running = False self._handle_process_error() def _read(self, stream, queue): data = stream.readline() if data: data = helpers.safe_decode_utf8(data.strip()) queue.put(data) return data def _read_stdout(self): data = self._read(self._process.stdout, self._stdout_lines) if self.log_output: LOG.debug('Output received from [%(cmd)s]: %(data)s', {'cmd': self.cmd, 'data': data}) return data def _read_stderr(self): data = self._read(self._process.stderr, self._stderr_lines) if self.log_output: LOG.error('Error received from [%(cmd)s]: %(err)s', {'cmd': self.cmd, 'err': data}) if self.die_on_error: LOG.error("Process [%(cmd)s] dies due to the error: %(err)s", {'cmd': self.cmd, 'err': data}) # the callback caller will use None to indicate the need to bail # out of the thread return None return data def _iter_queue(self, queue, block): while True: try: yield queue.get(block=block) except eventlet.queue.Empty: break def iter_stdout(self, block=False): return self._iter_queue(self._stdout_lines, block) def iter_stderr(self, block=False): return self._iter_queue(self._stderr_lines, block) neutron-12.1.1/neutron/agent/linux/polling.py0000664000175000017500000000465013553660047021273 0ustar zuulzuul00000000000000# Copyright 2013 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib import eventlet from oslo_config import cfg from oslo_log import log as logging from neutron.agent.common import base_polling from neutron.agent.linux import async_process from neutron.agent.linux import ovsdb_monitor from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants LOG = logging.getLogger(__name__) @contextlib.contextmanager def get_polling_manager(minimize_polling=False, ovsdb_monitor_respawn_interval=( constants.DEFAULT_OVSDBMON_RESPAWN)): if minimize_polling: pm = InterfacePollingMinimizer( ovsdb_monitor_respawn_interval=ovsdb_monitor_respawn_interval) pm.start() else: pm = base_polling.AlwaysPoll() try: yield pm finally: if minimize_polling: pm.stop() class InterfacePollingMinimizer(base_polling.BasePollingManager): """Monitors ovsdb to determine when polling is required.""" def __init__( self, ovsdb_monitor_respawn_interval=constants.DEFAULT_OVSDBMON_RESPAWN): super(InterfacePollingMinimizer, self).__init__() self._monitor = ovsdb_monitor.SimpleInterfaceMonitor( respawn_interval=ovsdb_monitor_respawn_interval, ovsdb_connection=cfg.CONF.OVS.ovsdb_connection) def start(self): self._monitor.start(block=True) def stop(self): try: self._monitor.stop() except async_process.AsyncProcessException: LOG.debug("InterfacePollingMinimizer was not running when stopped") def _is_polling_required(self): # Maximize the chances of update detection having a chance to # collect output. eventlet.sleep() return self._monitor.has_updates def get_events(self): return self._monitor.get_events() neutron-12.1.1/neutron/agent/linux/ra.py0000664000175000017500000001572313553660047020234 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import pwd from itertools import chain as iter_chain import jinja2 import netaddr from neutron_lib import constants from neutron_lib.utils import file as file_utils from oslo_log import log as logging import six from neutron.agent.linux import external_process from neutron.agent.linux import utils from neutron.common import constants as n_const RADVD_SERVICE_NAME = 'radvd' RADVD_SERVICE_CMD = 'radvd' # We can configure max of 3 DNS servers in radvd RDNSS section. MAX_RDNSS_ENTRIES = 3 LOG = logging.getLogger(__name__) CONFIG_TEMPLATE = jinja2.Template("""interface {{ interface_name }} { AdvSendAdvert on; MinRtrAdvInterval {{ min_rtr_adv_interval }}; MaxRtrAdvInterval {{ max_rtr_adv_interval }}; {% if network_mtu >= n_const.IPV6_MIN_MTU %} AdvLinkMTU {{network_mtu}}; {% endif %} {% if constants.DHCPV6_STATELESS in ra_modes %} AdvOtherConfigFlag on; {% endif %} {% if constants.DHCPV6_STATEFUL in ra_modes %} AdvManagedFlag on; {% endif %} {% if dns_servers %} RDNSS {% for dns in dns_servers %} {{ dns }} {% endfor %} {}; {% endif %} {% for prefix in auto_config_prefixes %} prefix {{ prefix }} { AdvOnLink on; AdvAutonomous on; }; {% endfor %} {% for prefix in stateful_config_prefixes %} prefix {{ prefix }} { AdvOnLink on; AdvAutonomous off; }; {% endfor %} }; """) class DaemonMonitor(object): """Manage the data and state of an radvd process.""" def __init__(self, router_id, router_ns, process_monitor, dev_name_helper, agent_conf): self._router_id = router_id self._router_ns = router_ns self._process_monitor = process_monitor self._dev_name_helper = dev_name_helper self._agent_conf = agent_conf def _generate_radvd_conf(self, router_ports): radvd_conf = utils.get_conf_file_name(self._agent_conf.ra_confs, self._router_id, 'radvd.conf', True) buf = six.StringIO() for p in router_ports: subnets = p.get('subnets', []) v6_subnets = [subnet for subnet in subnets if netaddr.IPNetwork(subnet['cidr']).version == 6] if not v6_subnets: continue ra_modes = {subnet['ipv6_ra_mode'] for subnet in v6_subnets} auto_config_prefixes = [subnet['cidr'] for subnet in v6_subnets if subnet['ipv6_ra_mode'] == constants.IPV6_SLAAC or subnet['ipv6_ra_mode'] == constants.DHCPV6_STATELESS] stateful_config_prefixes = [subnet['cidr'] for subnet in v6_subnets if subnet['ipv6_ra_mode'] == constants.DHCPV6_STATEFUL] interface_name = self._dev_name_helper(p['id']) slaac_subnets = [subnet for subnet in v6_subnets if subnet['ipv6_ra_mode'] == constants.IPV6_SLAAC] dns_servers = list(iter_chain(*[subnet['dns_nameservers'] for subnet in slaac_subnets if subnet.get('dns_nameservers')])) network_mtu = p.get('mtu', 0) buf.write('%s' % CONFIG_TEMPLATE.render( ra_modes=list(ra_modes), interface_name=interface_name, auto_config_prefixes=auto_config_prefixes, stateful_config_prefixes=stateful_config_prefixes, dns_servers=dns_servers[0:MAX_RDNSS_ENTRIES], n_const=n_const, constants=constants, min_rtr_adv_interval=self._agent_conf.min_rtr_adv_interval, max_rtr_adv_interval=self._agent_conf.max_rtr_adv_interval, network_mtu=int(network_mtu))) contents = buf.getvalue() LOG.debug("radvd config = %s", contents) # radvd conf file can't be writeable by self/group file_utils.replace_file(radvd_conf, contents, file_mode=0o444) return radvd_conf def _get_radvd_process_manager(self, callback=None): return external_process.ProcessManager( uuid=self._router_id, default_cmd_callback=callback, namespace=self._router_ns, service=RADVD_SERVICE_NAME, conf=self._agent_conf, run_as_root=True) def _spawn_radvd(self, radvd_conf): def callback(pid_file): # drop radvd daemon privileges and run as the neutron user radvd_user = pwd.getpwuid(os.geteuid()).pw_name # we need to use -m syslog and f.e. not -m stderr (the default) # or -m stderr_syslog so that radvd 2.0+ will close stderr and # exit after daemonization; otherwise, the current thread will # be locked waiting for result from radvd that won't ever come # until the process dies radvd_cmd = [RADVD_SERVICE_CMD, '-C', '%s' % radvd_conf, '-p', '%s' % pid_file, '-u', '%s' % radvd_user, '-m', 'syslog'] return radvd_cmd pm = self._get_radvd_process_manager(callback) pm.enable(reload_cfg=True) self._process_monitor.register(uuid=self._router_id, service_name=RADVD_SERVICE_NAME, monitored_process=pm) LOG.debug("radvd enabled for router %s", self._router_id) def enable(self, router_ports): for p in router_ports: for subnet in p['subnets']: if netaddr.IPNetwork(subnet['cidr']).version == 6: LOG.debug("Enable IPv6 RA for router %s", self._router_id) radvd_conf = self._generate_radvd_conf(router_ports) self._spawn_radvd(radvd_conf) return # Kill the daemon if it's running self.disable() def disable(self): self._process_monitor.unregister(uuid=self._router_id, service_name=RADVD_SERVICE_NAME) pm = self._get_radvd_process_manager() pm.disable() utils.remove_conf_files(self._agent_conf.ra_confs, self._router_id) LOG.debug("radvd disabled for router %s", self._router_id) @property def enabled(self): return self._get_radvd_process_manager().active neutron-12.1.1/neutron/agent/linux/interface.py0000664000175000017500000005314013553660047021565 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import time import netaddr from neutron_lib import constants from oslo_log import log as logging from oslo_log import versionutils import six from neutron.agent.common import ovs_lib from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.common import constants as n_const from neutron.common import exceptions LOG = logging.getLogger(__name__) def _get_veth(name1, name2, namespace2): return (ip_lib.IPDevice(name1), ip_lib.IPDevice(name2, namespace=namespace2)) @six.add_metaclass(abc.ABCMeta) class LinuxInterfaceDriver(object): DEV_NAME_LEN = n_const.LINUX_DEV_LEN DEV_NAME_PREFIX = constants.TAP_DEVICE_PREFIX def __init__(self, conf): self.conf = conf self._mtu_update_warn_logged = False @property def use_gateway_ips(self): """Whether to use gateway IPs instead of unique IP allocations. In each place where the DHCP agent runs, and for each subnet for which DHCP is handling out IP addresses, the DHCP port needs - at the Linux level - to have an IP address within that subnet. Generally this needs to be a unique Neutron-allocated IP address, because the subnet's underlying L2 domain is bridged across multiple compute hosts and network nodes, and for HA there may be multiple DHCP agents running on that same bridged L2 domain. However, if the DHCP ports - on multiple compute/network nodes but for the same network - are _not_ bridged to each other, they do not need each to have a unique IP address. Instead they can all share the same address from the relevant subnet. This works, without creating any ambiguity, because those ports are not all present on the same L2 domain, and because no data within the network is ever sent to that address. (DHCP requests are broadcast, and it is the network's job to ensure that such a broadcast will reach at least one of the available DHCP servers. DHCP responses will be sent _from_ the DHCP port address.) Specifically, for networking backends where it makes sense, the DHCP agent allows all DHCP ports to use the subnet's gateway IP address, and thereby to completely avoid any unique IP address allocation. This behaviour is selected by running the DHCP agent with a configured interface driver whose 'use_gateway_ips' property is True. When an operator deploys Neutron with an interface driver that makes use_gateway_ips True, they should also ensure that a gateway IP address is defined for each DHCP-enabled subnet, and that the gateway IP address doesn't change during the subnet's lifetime. """ return False def init_l3(self, device_name, ip_cidrs, namespace=None, preserve_ips=None, clean_connections=False): """Set the L3 settings for the interface using data from the port. ip_cidrs: list of 'X.X.X.X/YY' strings preserve_ips: list of ip cidrs that should not be removed from device clean_connections: Boolean to indicate if we should cleanup connections associated to removed ips """ preserve_ips = preserve_ips or [] device = ip_lib.IPDevice(device_name, namespace=namespace) # The LLA generated by the operating system is not known to # Neutron, so it would be deleted if we added it to the 'previous' # list here default_ipv6_lla = ip_lib.get_ipv6_lladdr(device.link.address) cidrs = set() remove_ips = set() # normalize all the IP addresses first for ip_cidr in ip_cidrs: net = netaddr.IPNetwork(ip_cidr) # Convert to compact IPv6 address because the return values of # "ip addr list" are compact. if net.version == 6: ip_cidr = str(net) cidrs.add(ip_cidr) # Determine the addresses that must be added and removed for address in device.addr.list(): cidr = address['cidr'] dynamic = address['dynamic'] # skip the IPv6 link-local if cidr == default_ipv6_lla: # it's already configured, leave it alone cidrs.discard(cidr) continue if cidr in preserve_ips: continue # Statically created addresses are OK, dynamically created # addresses must be removed and replaced if cidr in cidrs and not dynamic: cidrs.remove(cidr) continue remove_ips.add(cidr) # Clean up any old addresses. This must be done first since there # could be a dynamic address being replaced with a static one. for ip_cidr in remove_ips: if clean_connections: device.delete_addr_and_conntrack_state(ip_cidr) else: device.addr.delete(ip_cidr) # add any new addresses for ip_cidr in cidrs: device.addr.add(ip_cidr) def init_router_port(self, device_name, ip_cidrs, namespace, preserve_ips=None, extra_subnets=None, clean_connections=False): """Set the L3 settings for a router interface using data from the port. ip_cidrs: list of 'X.X.X.X/YY' strings preserve_ips: list of ip cidrs that should not be removed from device clean_connections: Boolean to indicate if we should cleanup connections associated to removed ips extra_subnets: An iterable of cidrs to add as routes without address """ LOG.debug("init_router_port: device_name(%s), namespace(%s)", device_name, namespace) self.init_l3(device_name=device_name, ip_cidrs=ip_cidrs, namespace=namespace, preserve_ips=preserve_ips or [], clean_connections=clean_connections) self.set_onlink_routes(device_name, namespace, extra_subnets, preserve_ips) def set_onlink_routes(self, device_name, namespace, extra_subnets, preserve_ips=None, is_ipv6=True): """Manage on-link routes (routes without an associate address) :param device_name: interface name :param namespace: namespace name :param extra_subnets: subnets attached to this interface without an IP address set in this interface :param preserve_ips: IPs or CIDRs not to be deleted from the device on-link route list """ device = ip_lib.IPDevice(device_name, namespace=namespace) new_onlink_cidrs = set(s['cidr'] for s in extra_subnets or []) preserve_ips = set(preserve_ips if preserve_ips else []) onlink = device.route.list_onlink_routes(constants.IP_VERSION_4) if is_ipv6: onlink += device.route.list_onlink_routes(constants.IP_VERSION_6) existing_onlink_cidrs = set(r['cidr'] for r in onlink) for route in new_onlink_cidrs - existing_onlink_cidrs: LOG.debug('Adding onlink route (%s)', route) device.route.add_onlink_route(route) for route in existing_onlink_cidrs - new_onlink_cidrs - preserve_ips: LOG.debug('Deleting onlink route (%s)', route) device.route.delete_onlink_route(route) def add_ipv6_addr(self, device_name, v6addr, namespace, scope='global'): device = ip_lib.IPDevice(device_name, namespace=namespace) net = netaddr.IPNetwork(v6addr) device.addr.add(str(net), scope) def delete_ipv6_addr(self, device_name, v6addr, namespace): device = ip_lib.IPDevice(device_name, namespace=namespace) device.delete_addr_and_conntrack_state(v6addr) def delete_ipv6_addr_with_prefix(self, device_name, prefix, namespace): """Delete the first listed IPv6 address that falls within a given prefix. """ device = ip_lib.IPDevice(device_name, namespace=namespace) net = netaddr.IPNetwork(prefix) for address in device.addr.list(scope='global', filters=['permanent']): ip_address = netaddr.IPNetwork(address['cidr']) if ip_address in net: device.delete_addr_and_conntrack_state(address['cidr']) break def get_ipv6_llas(self, device_name, namespace): device = ip_lib.IPDevice(device_name, namespace=namespace) return device.addr.list(scope='link', ip_version=6) def check_bridge_exists(self, bridge): if not ip_lib.device_exists(bridge): raise exceptions.BridgeDoesNotExist(bridge=bridge) def get_device_name(self, port): return (self.DEV_NAME_PREFIX + port.id)[:self.DEV_NAME_LEN] def remove_vlan_tag(self, bridge, interface_name): """Remove vlan tag from given interface. This method is necessary only for the case when deprecated option 'external_network_bridge' is used in L3 agent as external gateway port is then created in this external bridge directly and it will have DEAD_VLAN_TAG added by default. """ # TODO(slaweq): remove it when external_network_bridge option will be # removed @staticmethod def configure_ipv6_ra(namespace, dev_name, value): """Configure handling of IPv6 Router Advertisements on an interface. See common/constants.py for possible values. """ cmd = ['net.ipv6.conf.%(dev)s.accept_ra=%(value)s' % {'dev': dev_name, 'value': value}] ip_lib.sysctl(cmd, namespace=namespace) @staticmethod def configure_ipv6_forwarding(namespace, dev_name, enabled): """Configure IPv6 forwarding on an interface.""" cmd = ['net.ipv6.conf.%(dev)s.forwarding=%(enabled)s' % {'dev': dev_name, 'enabled': int(enabled)}] ip_lib.sysctl(cmd, namespace=namespace) @abc.abstractmethod def plug_new(self, network_id, port_id, device_name, mac_address, bridge=None, namespace=None, prefix=None, mtu=None): """Plug in the interface only for new devices that don't exist yet.""" def plug(self, network_id, port_id, device_name, mac_address, bridge=None, namespace=None, prefix=None, mtu=None): if not ip_lib.device_exists(device_name, namespace=namespace): self.plug_new(network_id, port_id, device_name, mac_address, bridge, namespace, prefix, mtu) else: LOG.info("Device %s already exists", device_name) if mtu: self.set_mtu( device_name, mtu, namespace=namespace, prefix=prefix) else: LOG.warning("No MTU configured for port %s", port_id) @abc.abstractmethod def unplug(self, device_name, bridge=None, namespace=None, prefix=None): """Unplug the interface.""" @property def bridged(self): """Whether the DHCP port is bridged to the VM TAP interfaces. When the DHCP port is bridged to the TAP interfaces for the VMs for which it is providing DHCP service - as is the case for most Neutron network implementations - the DHCP server only needs to listen on the DHCP port, and will still receive DHCP requests from all the relevant VMs. If the DHCP port is not bridged to the relevant VM TAP interfaces, the DHCP server needs to listen explicitly on those TAP interfaces, and to treat those as aliases of the DHCP port where the IP subnet is defined. """ return True def set_mtu(self, device_name, mtu, namespace=None, prefix=None): """Set MTU on the interface.""" if not self._mtu_update_warn_logged: LOG.warning("Interface driver cannot update MTU for ports") self._mtu_update_warn_logged = True class NullDriver(LinuxInterfaceDriver): def plug_new(self, network_id, port_id, device_name, mac_address, bridge=None, namespace=None, prefix=None, mtu=None): pass def unplug(self, device_name, bridge=None, namespace=None, prefix=None): pass class OVSInterfaceDriver(LinuxInterfaceDriver): """Driver for creating an internal interface on an OVS bridge.""" DEV_NAME_PREFIX = constants.TAP_DEVICE_PREFIX def __init__(self, conf): super(OVSInterfaceDriver, self).__init__(conf) if self.conf.ovs_use_veth: self.DEV_NAME_PREFIX = 'ns-' def _get_tap_name(self, dev_name, prefix=None): if self.conf.ovs_use_veth: dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX, constants.TAP_DEVICE_PREFIX) return dev_name def _ovs_add_port(self, bridge, device_name, port_id, mac_address, internal=True): attrs = [('external_ids', {'iface-id': port_id, 'iface-status': 'active', 'attached-mac': mac_address})] if internal: attrs.insert(0, ('type', 'internal')) ovs = ovs_lib.OVSBridge(bridge) ovs.replace_port(device_name, *attrs) def remove_vlan_tag(self, bridge, interface): ovs = ovs_lib.OVSBridge(bridge) ovs.clear_db_attribute("Port", interface, "tag") def plug_new(self, network_id, port_id, device_name, mac_address, bridge=None, namespace=None, prefix=None, mtu=None): """Plug in the interface.""" if not bridge: bridge = self.conf.ovs_integration_bridge self.check_bridge_exists(bridge) ip = ip_lib.IPWrapper() tap_name = self._get_tap_name(device_name, prefix) if self.conf.ovs_use_veth: # Create ns_dev in a namespace if one is configured. root_dev, ns_dev = ip.add_veth(tap_name, device_name, namespace2=namespace) root_dev.disable_ipv6() else: ns_dev = ip.device(device_name) internal = not self.conf.ovs_use_veth self._ovs_add_port(bridge, tap_name, port_id, mac_address, internal=internal) for i in range(9): # workaround for the OVS shy port syndrome. ports sometimes # hide for a bit right after they are first created. # see bug/1618987 try: ns_dev.link.set_address(mac_address) break except RuntimeError as e: LOG.warning("Got error trying to set mac, retrying: %s", str(e)) time.sleep(1) else: # didn't break, we give it one last shot without catching ns_dev.link.set_address(mac_address) # Add an interface created by ovs to the namespace. if not self.conf.ovs_use_veth and namespace: namespace_obj = ip.ensure_namespace(namespace) namespace_obj.add_device_to_namespace(ns_dev) # NOTE(ihrachys): the order here is significant: we must set MTU after # the device is moved into a namespace, otherwise OVS bridge does not # allow to set MTU that is higher than the least of all device MTUs on # the bridge if mtu: self.set_mtu(device_name, mtu, namespace=namespace, prefix=prefix) else: LOG.warning("No MTU configured for port %s", port_id) ns_dev.link.set_up() if self.conf.ovs_use_veth: root_dev.link.set_up() def unplug(self, device_name, bridge=None, namespace=None, prefix=None): """Unplug the interface.""" if not bridge: bridge = self.conf.ovs_integration_bridge tap_name = self._get_tap_name(device_name, prefix) self.check_bridge_exists(bridge) ovs = ovs_lib.OVSBridge(bridge) try: ovs.delete_port(tap_name) if self.conf.ovs_use_veth: device = ip_lib.IPDevice(device_name, namespace=namespace) device.link.delete() LOG.debug("Unplugged interface '%s'", device_name) except RuntimeError: LOG.error("Failed unplugging interface '%s'", device_name) def set_mtu(self, device_name, mtu, namespace=None, prefix=None): if self.conf.ovs_use_veth: tap_name = self._get_tap_name(device_name, prefix) root_dev, ns_dev = _get_veth( tap_name, device_name, namespace2=namespace) root_dev.link.set_mtu(mtu) else: ns_dev = ip_lib.IPWrapper(namespace=namespace).device(device_name) ns_dev.link.set_mtu(mtu) class IVSInterfaceDriver(LinuxInterfaceDriver): """Driver for creating an internal interface on an IVS bridge.""" DEV_NAME_PREFIX = constants.TAP_DEVICE_PREFIX def __init__(self, conf): super(IVSInterfaceDriver, self).__init__(conf) versionutils.report_deprecated_feature( LOG, "IVS interface driver is deprecated in Queens and will be " "removed in Rocky.") self.DEV_NAME_PREFIX = 'ns-' def _get_tap_name(self, dev_name, prefix=None): dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX, constants.TAP_DEVICE_PREFIX) return dev_name def _ivs_add_port(self, device_name, port_id, mac_address): cmd = ['ivs-ctl', 'add-port', device_name] utils.execute(cmd, run_as_root=True) def plug_new(self, network_id, port_id, device_name, mac_address, bridge=None, namespace=None, prefix=None, mtu=None): """Plug in the interface.""" ip = ip_lib.IPWrapper() tap_name = self._get_tap_name(device_name, prefix) root_dev, ns_dev = ip.add_veth(tap_name, device_name) root_dev.disable_ipv6() self._ivs_add_port(tap_name, port_id, mac_address) ns_dev = ip.device(device_name) ns_dev.link.set_address(mac_address) if mtu: ns_dev.link.set_mtu(mtu) root_dev.link.set_mtu(mtu) else: LOG.warning("No MTU configured for port %s", port_id) if namespace: namespace_obj = ip.ensure_namespace(namespace) namespace_obj.add_device_to_namespace(ns_dev) ns_dev.link.set_up() root_dev.link.set_up() def unplug(self, device_name, bridge=None, namespace=None, prefix=None): """Unplug the interface.""" tap_name = self._get_tap_name(device_name, prefix) try: cmd = ['ivs-ctl', 'del-port', tap_name] utils.execute(cmd, run_as_root=True) device = ip_lib.IPDevice(device_name, namespace=namespace) device.link.delete() LOG.debug("Unplugged interface '%s'", device_name) except RuntimeError: LOG.error("Failed unplugging interface '%s'", device_name) class BridgeInterfaceDriver(LinuxInterfaceDriver): """Driver for creating bridge interfaces.""" DEV_NAME_PREFIX = 'ns-' def plug_new(self, network_id, port_id, device_name, mac_address, bridge=None, namespace=None, prefix=None, mtu=None): """Plugin the interface.""" ip = ip_lib.IPWrapper() # Enable agent to define the prefix tap_name = device_name.replace(prefix or self.DEV_NAME_PREFIX, constants.TAP_DEVICE_PREFIX) # Create ns_veth in a namespace if one is configured. root_veth, ns_veth = ip.add_veth(tap_name, device_name, namespace2=namespace) root_veth.disable_ipv6() ns_veth.link.set_address(mac_address) if mtu: self.set_mtu(device_name, mtu, namespace=namespace, prefix=prefix) else: LOG.warning("No MTU configured for port %s", port_id) root_veth.link.set_up() ns_veth.link.set_up() def unplug(self, device_name, bridge=None, namespace=None, prefix=None): """Unplug the interface.""" device = ip_lib.IPDevice(device_name, namespace=namespace) try: device.link.delete() LOG.debug("Unplugged interface '%s'", device_name) except RuntimeError: LOG.error("Failed unplugging interface '%s'", device_name) def set_mtu(self, device_name, mtu, namespace=None, prefix=None): tap_name = device_name.replace(prefix or self.DEV_NAME_PREFIX, constants.TAP_DEVICE_PREFIX) root_dev, ns_dev = _get_veth( tap_name, device_name, namespace2=namespace) root_dev.link.set_mtu(mtu) ns_dev.link.set_mtu(mtu) neutron-12.1.1/neutron/agent/linux/iptables_comments.py0000664000175000017500000000355413553660046023340 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """iptables comments""" # Do not translate these comments. These comments cannot contain a quote or # an escape character because they will end up in a call to iptables and # could interfere with other parameters. SNAT_OUT = 'Perform source NAT on outgoing traffic.' UNMATCH_DROP = 'Default drop rule for unmatched traffic.' VM_INT_SG = 'Direct traffic from the VM interface to the security group chain.' SG_TO_VM_SG = 'Jump to the VM specific chain.' INPUT_TO_SG = 'Direct incoming traffic from VM to the security group chain.' PAIR_ALLOW = 'Allow traffic from defined IP/MAC pairs.' PAIR_DROP = 'Drop traffic without an IP/MAC allow rule.' DHCP_CLIENT = 'Allow DHCP client traffic.' DHCP_SPOOF = 'Prevent DHCP Spoofing by VM.' UNMATCHED = 'Send unmatched traffic to the fallback chain.' INVALID_DROP = ("Drop packets that appear related to an existing connection " "(e.g. TCP ACK/FIN) but do not have an entry in conntrack.") ALLOW_ASSOC = ('Direct packets associated with a known session to the RETURN ' 'chain.') PORT_SEC_ACCEPT = 'Accept all packets when port security is disabled.' TRUSTED_ACCEPT = 'Accept all packets when port is trusted.' IPV6_RA_DROP = 'Drop IPv6 Router Advts from VM Instance.' IPV6_ICMP_ALLOW = 'Allow IPv6 ICMP traffic.' neutron-12.1.1/neutron/agent/linux/bridge_lib.py0000664000175000017500000001043713553660047021711 0ustar zuulzuul00000000000000# Copyright 2015 Intel Corporation. # Copyright 2015 Isaku Yamahata # # All Rights Reserved. # # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from oslo_utils import excutils from neutron.agent.linux import ip_lib from neutron.agent.linux import utils # NOTE(toabctl): Don't use /sys/devices/virtual/net here because not all tap # devices are listed here (i.e. when using Xen) BRIDGE_FS = "/sys/class/net/" BRIDGE_INTERFACE_FS = BRIDGE_FS + "%(bridge)s/brif/%(interface)s" BRIDGE_INTERFACES_FS = BRIDGE_FS + "%s/brif/" BRIDGE_PORT_FS_FOR_DEVICE = BRIDGE_FS + "%s/brport" BRIDGE_PATH_FOR_DEVICE = BRIDGE_PORT_FS_FOR_DEVICE + '/bridge' def is_bridged_interface(interface): if not interface: return False else: return os.path.exists(BRIDGE_PORT_FS_FOR_DEVICE % interface) def get_interface_ifindex(interface): try: with open(os.path.join(BRIDGE_FS, interface, 'ifindex'), 'r') as fh: return int(fh.read().strip()) except (IOError, ValueError): pass def get_bridge_names(): return os.listdir(BRIDGE_FS) class BridgeDevice(ip_lib.IPDevice): def _brctl(self, cmd): cmd = ['brctl'] + cmd ip_wrapper = ip_lib.IPWrapper(self.namespace) return ip_wrapper.netns.execute(cmd, run_as_root=True) @classmethod def addbr(cls, name, namespace=None): bridge = cls(name, namespace) try: bridge._brctl(['addbr', bridge.name]) except RuntimeError: with excutils.save_and_reraise_exception() as ectx: ectx.reraise = not bridge.exists() return bridge @classmethod def get_interface_bridge(cls, interface): try: path = os.readlink(BRIDGE_PATH_FOR_DEVICE % interface) except OSError: return None else: name = path.rpartition('/')[-1] return cls(name) def delbr(self): return self._brctl(['delbr', self.name]) def addif(self, interface): return self._brctl(['addif', self.name, interface]) def delif(self, interface): return self._brctl(['delif', self.name, interface]) def setfd(self, fd): return self._brctl(['setfd', self.name, str(fd)]) def disable_stp(self): return self._brctl(['stp', self.name, 'off']) def owns_interface(self, interface): return os.path.exists( BRIDGE_INTERFACE_FS % {'bridge': self.name, 'interface': interface}) def get_interfaces(self): try: return os.listdir(BRIDGE_INTERFACES_FS % self.name) except OSError: return [] class FdbInterface(object): """provide basic functionality to edit the FDB table""" @classmethod def _execute(cls, op, mac, dev, ip_dst, **kwargs): cmd = ['bridge', 'fdb', op, mac, 'dev', dev] if ip_dst is not None: cmd += ['dst', ip_dst] return utils.execute(cmd, run_as_root=True, **kwargs) @classmethod def add(cls, mac, dev, ip_dst=None, **kwargs): return cls._execute('add', mac, dev, ip_dst, **kwargs) @classmethod def append(cls, mac, dev, ip_dst=None, **kwargs): return cls._execute('append', mac, dev, ip_dst, **kwargs) @classmethod def replace(cls, mac, dev, ip_dst=None, **kwargs): return cls._execute('replace', mac, dev, ip_dst, **kwargs) @classmethod def delete(cls, mac, dev, ip_dst=None, **kwargs): return cls._execute('delete', mac, dev, ip_dst, **kwargs) @classmethod def show(cls, dev=None, **kwargs): cmd = ['bridge', 'fdb', 'show'] if dev: cmd += ['dev', dev] return utils.execute(cmd, run_as_root=True, **kwargs) neutron-12.1.1/neutron/agent/linux/ip_lib.py0000664000175000017500000013567413553660047021100 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import re import time from debtcollector import removals import eventlet import netaddr from neutron_lib import constants from neutron_lib import exceptions from oslo_config import cfg from oslo_log import log as logging from oslo_utils import excutils from pyroute2 import netns import six from neutron._i18n import _ from neutron.agent.common import utils from neutron.common import exceptions as n_exc from neutron.common import ipv6_utils from neutron.common import utils as common_utils from neutron.privileged.agent.linux import ip_lib as privileged LOG = logging.getLogger(__name__) IP_NONLOCAL_BIND = 'net.ipv4.ip_nonlocal_bind' LOOPBACK_DEVNAME = 'lo' GRE_TUNNEL_DEVICE_NAMES = ['gre0', 'gretap0'] SYS_NET_PATH = '/sys/class/net' DEFAULT_GW_PATTERN = re.compile(r"via (\S+)") METRIC_PATTERN = re.compile(r"metric (\S+)") DEVICE_NAME_PATTERN = re.compile(r"(\d+?): (\S+?):.*") def remove_interface_suffix(interface): """Remove a possible "@" suffix from an interface' name. This suffix can appear in some kernel versions, and intends on specifying, for example, a veth's pair. However, this interface name is useless to us as further 'ip' commands require that the suffix be removed. """ # If '@' is not present, this will do nothing. return interface.partition("@")[0] class AddressNotReady(exceptions.NeutronException): message = _("Failure waiting for address %(address)s to " "become ready: %(reason)s") class SubProcessBase(object): def __init__(self, namespace=None, log_fail_as_error=True): self.namespace = namespace self.log_fail_as_error = log_fail_as_error try: self.force_root = cfg.CONF.ip_lib_force_root except cfg.NoSuchOptError: # Only callers that need to force use of the root helper # need to register the option. self.force_root = False def _run(self, options, command, args): if self.namespace: return self._as_root(options, command, args) elif self.force_root: # Force use of the root helper to ensure that commands # will execute in dom0 when running under XenServer/XCP. return self._execute(options, command, args, run_as_root=True) else: return self._execute(options, command, args) def _as_root(self, options, command, args, use_root_namespace=False): namespace = self.namespace if not use_root_namespace else None return self._execute(options, command, args, run_as_root=True, namespace=namespace) def _execute(self, options, command, args, run_as_root=False, namespace=None): opt_list = ['-%s' % o for o in options] ip_cmd = add_namespace_to_cmd(['ip'], namespace) cmd = ip_cmd + opt_list + [command] + list(args) return utils.execute(cmd, run_as_root=run_as_root, log_fail_as_error=self.log_fail_as_error) def set_log_fail_as_error(self, fail_with_error): self.log_fail_as_error = fail_with_error def get_log_fail_as_error(self): return self.log_fail_as_error class IPWrapper(SubProcessBase): def __init__(self, namespace=None): super(IPWrapper, self).__init__(namespace=namespace) self.netns = IpNetnsCommand(self) def device(self, name): return IPDevice(name, namespace=self.namespace) def get_devices(self, exclude_loopback=True, exclude_gre_devices=True): retval = [] if self.namespace: # we call out manually because in order to avoid screen scraping # iproute2 we use find to see what is in the sysfs directory, as # suggested by Stephen Hemminger (iproute2 dev). try: cmd = ['ip', 'netns', 'exec', self.namespace, 'find', SYS_NET_PATH, '-maxdepth', '1', '-type', 'l', '-printf', '%f '] output = utils.execute( cmd, run_as_root=True, log_fail_as_error=self.log_fail_as_error).split() except RuntimeError: # We could be racing with a cron job deleting namespaces. # Just return a empty list if the namespace is deleted. with excutils.save_and_reraise_exception() as ctx: if not self.netns.exists(self.namespace): ctx.reraise = False return [] else: output = ( i for i in os.listdir(SYS_NET_PATH) if os.path.islink(os.path.join(SYS_NET_PATH, i)) ) for name in output: if (exclude_loopback and name == LOOPBACK_DEVNAME or exclude_gre_devices and name in GRE_TUNNEL_DEVICE_NAMES): continue retval.append(IPDevice(name, namespace=self.namespace)) return retval def get_device_by_ip(self, ip): """Get the IPDevice from system which has ip configured. @param ip: look for the device holding this ip. If this is None, None is returned. @type ip: str. """ if not ip: return None addr = IpAddrCommand(self) devices = addr.get_devices_with_ip(to=ip) if devices: return IPDevice(devices[0]['name'], namespace=self.namespace) def add_tuntap(self, name, mode='tap'): self._as_root([], 'tuntap', ('add', name, 'mode', mode)) return IPDevice(name, namespace=self.namespace) def add_veth(self, name1, name2, namespace2=None): args = ['add', name1, 'type', 'veth', 'peer', 'name', name2] if namespace2 is None: namespace2 = self.namespace else: self.ensure_namespace(namespace2) args += ['netns', namespace2] self._as_root([], 'link', tuple(args)) return (IPDevice(name1, namespace=self.namespace), IPDevice(name2, namespace=namespace2)) def add_macvtap(self, name, src_dev, mode='bridge'): args = ['add', 'link', src_dev, 'name', name, 'type', 'macvtap', 'mode', mode] self._as_root([], 'link', tuple(args)) return IPDevice(name, namespace=self.namespace) def del_veth(self, name): """Delete a virtual interface between two namespaces.""" self._as_root([], 'link', ('del', name)) def add_dummy(self, name): """Create a Linux dummy interface with the given name.""" self._as_root([], 'link', ('add', name, 'type', 'dummy')) return IPDevice(name, namespace=self.namespace) def ensure_namespace(self, name): if not self.netns.exists(name): ip = self.netns.add(name) lo = ip.device(LOOPBACK_DEVNAME) lo.link.set_up() else: ip = IPWrapper(namespace=name) return ip def namespace_is_empty(self): return not self.get_devices() def garbage_collect_namespace(self): """Conditionally destroy the namespace if it is empty.""" if self.namespace and self.netns.exists(self.namespace): if self.namespace_is_empty(): self.netns.delete(self.namespace) return True return False def add_device_to_namespace(self, device): if self.namespace: device.link.set_netns(self.namespace) def add_vlan(self, name, physical_interface, vlan_id): cmd = ['add', 'link', physical_interface, 'name', name, 'type', 'vlan', 'id', vlan_id] self._as_root([], 'link', cmd) return IPDevice(name, namespace=self.namespace) def add_vxlan(self, name, vni, group=None, dev=None, ttl=None, tos=None, local=None, srcport=None, dstport=None, proxy=False): cmd = ['add', name, 'type', 'vxlan', 'id', vni] if group: cmd.extend(['group', group]) if dev: cmd.extend(['dev', dev]) if ttl: cmd.extend(['ttl', ttl]) if tos: cmd.extend(['tos', tos]) if local: cmd.extend(['local', local]) if proxy: cmd.append('proxy') # tuple: min,max if srcport: if len(srcport) == 2 and srcport[0] <= srcport[1]: cmd.extend(['srcport', str(srcport[0]), str(srcport[1])]) else: raise n_exc.NetworkVxlanPortRangeError(vxlan_range=srcport) if dstport: cmd.extend(['dstport', str(dstport)]) self._as_root([], 'link', cmd) return (IPDevice(name, namespace=self.namespace)) @removals.remove(version='Queens', removal_version='Rocky', message="This will be removed in the future. Please use " "'neutron.agent.linux.ip_lib." "list_network_namespaces' instead.") @classmethod def get_namespaces(cls): return list_network_namespaces() class IPDevice(SubProcessBase): def __init__(self, name, namespace=None): super(IPDevice, self).__init__(namespace=namespace) self._name = name self.link = IpLinkCommand(self) self.addr = IpAddrCommand(self) self.route = IpRouteCommand(self) self.neigh = IpNeighCommand(self) def __eq__(self, other): return (other is not None and self.name == other.name and self.namespace == other.namespace) def __str__(self): return self.name def __repr__(self): return "" % (self._name, self.namespace) def exists(self): """Return True if the device exists in the namespace.""" # we must save and restore this before returning orig_log_fail_as_error = self.get_log_fail_as_error() self.set_log_fail_as_error(False) try: return bool(self.link.address) except RuntimeError: return False finally: self.set_log_fail_as_error(orig_log_fail_as_error) def delete_addr_and_conntrack_state(self, cidr): """Delete an address along with its conntrack state This terminates any active connections through an IP. :param cidr: the IP address for which state should be removed. This can be passed as a string with or without /NN. A netaddr.IPAddress or netaddr.Network representing the IP address can also be passed. """ self.addr.delete(cidr) self.delete_conntrack_state(cidr) def delete_conntrack_state(self, cidr): """Delete conntrack state rules Deletes both rules (if existing), the destination and the reply one. """ ip_str = str(netaddr.IPNetwork(cidr).ip) ip_wrapper = IPWrapper(namespace=self.namespace) # Delete conntrack state for ingress traffic # If 0 flow entries have been deleted # conntrack -D will return 1 try: ip_wrapper.netns.execute(["conntrack", "-D", "-d", ip_str], check_exit_code=True, extra_ok_codes=[1]) except RuntimeError: LOG.exception("Failed deleting ingress connection state of" " floatingip %s", ip_str) # Delete conntrack state for egress traffic try: ip_wrapper.netns.execute(["conntrack", "-D", "-q", ip_str], check_exit_code=True, extra_ok_codes=[1]) except RuntimeError: LOG.exception("Failed deleting egress connection state of" " floatingip %s", ip_str) def disable_ipv6(self): if not ipv6_utils.is_enabled_and_bind_by_default(): return sysctl_name = re.sub(r'\.', '/', self.name) cmd = ['net.ipv6.conf.%s.disable_ipv6=1' % sysctl_name] return sysctl(cmd, namespace=self.namespace) @property def name(self): if self._name: return self._name[:constants.DEVICE_NAME_MAX_LEN] return self._name @name.setter def name(self, name): self._name = name class IpCommandBase(object): COMMAND = '' def __init__(self, parent): self._parent = parent def _run(self, options, args): return self._parent._run(options, self.COMMAND, args) def _as_root(self, options, args, use_root_namespace=False): return self._parent._as_root(options, self.COMMAND, args, use_root_namespace=use_root_namespace) class IPRule(SubProcessBase): def __init__(self, namespace=None): super(IPRule, self).__init__(namespace=namespace) self.rule = IpRuleCommand(self) class IpRuleCommand(IpCommandBase): COMMAND = 'rule' @staticmethod def _make_canonical(ip_version, settings): """Converts settings to a canonical representation to compare easily""" def canonicalize_fwmark_string(fwmark_mask): """Reformats fwmark/mask in to a canonical form Examples, these are all equivalent: "0x1" 0x1 "0x1/0xfffffffff" (0x1, 0xfffffffff) :param fwmark_mask: The firewall and mask (default 0xffffffff) :type fwmark_mask: A string with / as delimiter, an iterable, or a single value. """ # Turn the value we were passed in to an iterable: fwmark[, mask] if isinstance(fwmark_mask, six.string_types): # A / separates the optional mask in a string iterable = fwmark_mask.split('/') else: try: iterable = iter(fwmark_mask) except TypeError: # At this point, it must be a single integer iterable = [fwmark_mask] def to_i(s): if isinstance(s, six.string_types): # Passing 0 as "base" arg to "int" causes it to determine # the base automatically. return int(s, 0) # s isn't a string, can't specify base argument return int(s) integers = [to_i(x) for x in iterable] # The default mask is all ones, the mask is 32 bits. if len(integers) == 1: integers.append(0xffffffff) # We now have two integers in a list. Convert to canonical string. return '{0:#x}/{1:#x}'.format(*integers) def canonicalize(item): k, v = item # ip rule shows these as 'any' if k == 'from' and v == 'all': return k, constants.IP_ANY[ip_version] # lookup and table are interchangeable. Use table every time. if k == 'lookup': return 'table', v if k == 'fwmark': return k, canonicalize_fwmark_string(v) return k, v if 'type' not in settings: settings['type'] = 'unicast' return {k: str(v) for k, v in map(canonicalize, settings.items())} def _parse_line(self, ip_version, line): # Typical rules from 'ip rule show': # 4030201: from 1.2.3.4/24 lookup 10203040 # 1024: from all iif qg-c43b1928-48 lookup noscope parts = line.split() if not parts: return {} # Format of line is: "priority: ... []" settings = {k: v for k, v in zip(parts[1::2], parts[2::2])} settings['priority'] = parts[0][:-1] if len(parts) % 2 == 0: # When line has an even number of columns, last one is the type. settings['type'] = parts[-1] return self._make_canonical(ip_version, settings) def list_rules(self, ip_version): lines = self._as_root([ip_version], ['show']).splitlines() return [self._parse_line(ip_version, line) for line in lines] def _exists(self, ip_version, **kwargs): return kwargs in self.list_rules(ip_version) def _make__flat_args_tuple(self, *args, **kwargs): for kwargs_item in sorted(kwargs.items(), key=lambda i: i[0]): args += kwargs_item return tuple(args) def add(self, ip, **kwargs): ip_version = common_utils.get_ip_version(ip) # In case we need to add a rule based on an incoming # interface, pass the "any" IP address, for example, 0.0.0.0/0, # else pass the given IP. if kwargs.get('iif'): kwargs.update({'from': constants.IP_ANY[ip_version]}) else: kwargs.update({'from': ip}) canonical_kwargs = self._make_canonical(ip_version, kwargs) if not self._exists(ip_version, **canonical_kwargs): args_tuple = self._make__flat_args_tuple('add', **canonical_kwargs) self._as_root([ip_version], args_tuple) def delete(self, ip, **kwargs): ip_version = common_utils.get_ip_version(ip) # In case we need to delete a rule based on an incoming # interface, pass the "any" IP address, for example, 0.0.0.0/0, # else pass the given IP. if kwargs.get('iif'): kwargs.update({'from': constants.IP_ANY[ip_version]}) else: kwargs.update({'from': ip}) canonical_kwargs = self._make_canonical(ip_version, kwargs) args_tuple = self._make__flat_args_tuple('del', **canonical_kwargs) self._as_root([ip_version], args_tuple) class IpDeviceCommandBase(IpCommandBase): @property def name(self): return self._parent.name class IpLinkCommand(IpDeviceCommandBase): COMMAND = 'link' def set_address(self, mac_address): self._as_root([], ('set', self.name, 'address', mac_address)) def set_allmulticast_on(self): self._as_root([], ('set', self.name, 'allmulticast', 'on')) def set_mtu(self, mtu_size): self._as_root([], ('set', self.name, 'mtu', mtu_size)) def set_up(self): return self._as_root([], ('set', self.name, 'up')) def set_down(self): return self._as_root([], ('set', self.name, 'down')) def set_netns(self, namespace): self._as_root([], ('set', self.name, 'netns', namespace)) self._parent.namespace = namespace def set_name(self, name): self._as_root([], ('set', self.name, 'name', name)) self._parent.name = name def set_alias(self, alias_name): self._as_root([], ('set', self.name, 'alias', alias_name)) def delete(self): self._as_root([], ('delete', self.name)) @property def address(self): return self.attributes.get('link/ether') @property def state(self): return self.attributes.get('state') @property def mtu(self): return self.attributes.get('mtu') @property def qdisc(self): return self.attributes.get('qdisc') @property def qlen(self): return self.attributes.get('qlen') @property def alias(self): return self.attributes.get('alias') @property def attributes(self): return self._parse_line(self._run(['o'], ('show', self.name))) def _parse_line(self, value): if not value: return {} device_name, settings = value.replace("\\", '').split('>', 1) tokens = settings.split() keys = tokens[::2] values = [int(v) if v.isdigit() else v for v in tokens[1::2]] retval = dict(zip(keys, values)) return retval class IpAddrCommand(IpDeviceCommandBase): COMMAND = 'addr' def add(self, cidr, scope='global', add_broadcast=True): net = netaddr.IPNetwork(cidr) args = ['add', cidr, 'scope', scope, 'dev', self.name] if add_broadcast and net.version == 4: args += ['brd', str(net[-1])] self._as_root([net.version], tuple(args)) def delete(self, cidr): ip_version = common_utils.get_ip_version(cidr) self._as_root([ip_version], ('del', cidr, 'dev', self.name)) def flush(self, ip_version): self._as_root([ip_version], ('flush', self.name)) def get_devices_with_ip(self, name=None, scope=None, to=None, filters=None, ip_version=None): """Get a list of all the devices with an IP attached in the namespace. :param name: if it's not None, only a device with that matching name will be returned. :param scope: address scope, for example, global, link, or host :param to: IP address or cidr to match. If cidr then it will match any IP within the specified subnet :param filters: list of any other filters supported by /sbin/ip :param ip_version: 4 or 6 """ options = [ip_version] if ip_version else [] args = ['show'] if name: args += [name] if filters: args += filters if scope: args += ['scope', scope] if to: args += ['to', to] retval = [] for line in self._run(options, tuple(args)).split('\n'): line = line.strip() match = DEVICE_NAME_PATTERN.search(line) if match: # Found a match for a device name, but its' addresses will # only appear in following lines, so we may as well continue. device_name = remove_interface_suffix(match.group(2)) continue elif not line.startswith('inet'): continue parts = line.split(" ") if parts[0] == 'inet6': scope = parts[3] else: if parts[2] == 'brd': scope = parts[5] else: scope = parts[3] retval.append(dict(name=device_name, cidr=parts[1], scope=scope, dynamic=('dynamic' == parts[-1]), tentative=('tentative' in line), dadfailed=('dadfailed' == parts[-1]))) return retval def list(self, scope=None, to=None, filters=None, ip_version=None): """Get device details of a device named .""" return self.get_devices_with_ip( self.name, scope, to, filters, ip_version) def wait_until_address_ready(self, address, wait_time=30): """Wait until an address is no longer marked 'tentative' raises AddressNotReady if times out or address not present on interface """ def is_address_ready(): try: addr_info = self.list(to=address)[0] except IndexError: raise AddressNotReady( address=address, reason=_('Address not present on interface')) if not addr_info['tentative']: return True if addr_info['dadfailed']: raise AddressNotReady( address=address, reason=_('Duplicate address detected')) errmsg = _("Exceeded %s second limit waiting for " "address to leave the tentative state.") % wait_time common_utils.wait_until_true( is_address_ready, timeout=wait_time, sleep=0.20, exception=AddressNotReady(address=address, reason=errmsg)) class IpRouteCommand(IpDeviceCommandBase): COMMAND = 'route' def __init__(self, parent, table=None): super(IpRouteCommand, self).__init__(parent) self._table = table def table(self, table): """Return an instance of IpRouteCommand which works on given table""" return IpRouteCommand(self._parent, table) def _table_args(self, override=None): if override: return ['table', override] return ['table', self._table] if self._table else [] def _dev_args(self): return ['dev', self.name] if self.name else [] def add_gateway(self, gateway, metric=None, table=None): ip_version = common_utils.get_ip_version(gateway) args = ['replace', 'default', 'via', gateway] if metric: args += ['metric', metric] args += self._dev_args() args += self._table_args(table) self._as_root([ip_version], tuple(args)) def _run_as_root_detect_device_not_found(self, *args, **kwargs): try: return self._as_root(*args, **kwargs) except RuntimeError as rte: with excutils.save_and_reraise_exception() as ctx: if "Cannot find device" in str(rte): ctx.reraise = False raise exceptions.DeviceNotFoundError(device_name=self.name) def delete_gateway(self, gateway, table=None): ip_version = common_utils.get_ip_version(gateway) args = ['del', 'default', 'via', gateway] args += self._dev_args() args += self._table_args(table) self._run_as_root_detect_device_not_found([ip_version], tuple(args)) def _parse_routes(self, ip_version, output, **kwargs): for line in output.splitlines(): parts = line.split() # Format of line is: "|default [ ] ..." route = {k: v for k, v in zip(parts[1::2], parts[2::2])} route['cidr'] = parts[0] # Avoids having to explicitly pass around the IP version if route['cidr'] == 'default': route['cidr'] = constants.IP_ANY[ip_version] # ip route drops things like scope and dev from the output if it # was specified as a filter. This allows us to add them back. if self.name: route['dev'] = self.name if self._table: route['table'] = self._table # Callers add any filters they use as kwargs route.update(kwargs) yield route def list_routes(self, ip_version, **kwargs): args = ['list'] args += self._dev_args() args += self._table_args() for k, v in kwargs.items(): args += [k, v] output = self._run([ip_version], tuple(args)) return [r for r in self._parse_routes(ip_version, output, **kwargs)] def list_onlink_routes(self, ip_version): routes = self.list_routes(ip_version, scope='link') return [r for r in routes if 'src' not in r] def add_onlink_route(self, cidr): self.add_route(cidr, scope='link') def delete_onlink_route(self, cidr): self.delete_route(cidr, scope='link') def get_gateway(self, scope=None, filters=None, ip_version=None): options = [ip_version] if ip_version else [] args = ['list'] args += self._dev_args() args += self._table_args() if filters: args += filters retval = None if scope: args += ['scope', scope] route_list_lines = self._run(options, tuple(args)).split('\n') default_route_line = next((x.strip() for x in route_list_lines if x.strip().startswith('default')), None) if default_route_line: retval = dict() gateway = DEFAULT_GW_PATTERN.search(default_route_line) if gateway: retval.update(gateway=gateway.group(1)) metric = METRIC_PATTERN.search(default_route_line) if metric: retval.update(metric=int(metric.group(1))) return retval def flush(self, ip_version, table=None, **kwargs): args = ['flush'] args += self._table_args(table) for k, v in kwargs.items(): args += [k, v] self._as_root([ip_version], tuple(args)) def add_route(self, cidr, via=None, table=None, **kwargs): ip_version = common_utils.get_ip_version(cidr) args = ['replace', cidr] if via: args += ['via', via] args += self._dev_args() args += self._table_args(table) for k, v in kwargs.items(): args += [k, v] self._run_as_root_detect_device_not_found([ip_version], tuple(args)) def delete_route(self, cidr, via=None, table=None, **kwargs): ip_version = common_utils.get_ip_version(cidr) args = ['del', cidr] if via: args += ['via', via] args += self._dev_args() args += self._table_args(table) for k, v in kwargs.items(): args += [k, v] self._run_as_root_detect_device_not_found([ip_version], tuple(args)) class IPRoute(SubProcessBase): def __init__(self, namespace=None, table=None): super(IPRoute, self).__init__(namespace=namespace) self.name = None self.route = IpRouteCommand(self, table=table) class IpNeighCommand(IpDeviceCommandBase): COMMAND = 'neigh' def add(self, ip_address, mac_address, **kwargs): add_neigh_entry(ip_address, mac_address, self.name, self._parent.namespace, **kwargs) def delete(self, ip_address, mac_address, **kwargs): delete_neigh_entry(ip_address, mac_address, self.name, self._parent.namespace, **kwargs) def dump(self, ip_version, **kwargs): return dump_neigh_entries(ip_version, self.name, self._parent.namespace, **kwargs) def flush(self, ip_version, ip_address): """Flush neighbour entries Given address entry is removed from neighbour cache (ARP or NDP). To flush all entries pass string 'all' as an address. :param ip_version: Either 4 or 6 for IPv4 or IPv6 respectively :param ip_address: The prefix selecting the neighbours to flush """ # NOTE(haleyb): There is no equivalent to 'flush' in pyroute2 self._as_root([ip_version], ('flush', 'to', ip_address)) class IpNetnsCommand(IpCommandBase): COMMAND = 'netns' def add(self, name): create_network_namespace(name) wrapper = IPWrapper(namespace=name) wrapper.netns.execute(['sysctl', '-w', 'net.ipv4.conf.all.promote_secondaries=1']) return wrapper def delete(self, name): delete_network_namespace(name) def execute(self, cmds, addl_env=None, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None, run_as_root=False): ns_params = [] kwargs = {'run_as_root': run_as_root} if self._parent.namespace: kwargs['run_as_root'] = True ns_params = ['ip', 'netns', 'exec', self._parent.namespace] env_params = [] if addl_env: env_params = (['env'] + ['%s=%s' % pair for pair in addl_env.items()]) cmd = ns_params + env_params + list(cmds) return utils.execute(cmd, check_exit_code=check_exit_code, extra_ok_codes=extra_ok_codes, log_fail_as_error=log_fail_as_error, **kwargs) def exists(self, name): return network_namespace_exists(name) def vlan_in_use(segmentation_id, namespace=None): """Return True if VLAN ID is in use by an interface, else False.""" ip_wrapper = IPWrapper(namespace=namespace) interfaces = ip_wrapper.netns.execute(["ip", "-d", "link", "list"], check_exit_code=True) return '802.1Q id %s ' % segmentation_id in interfaces def vxlan_in_use(segmentation_id, namespace=None): """Return True if VXLAN VNID is in use by an interface, else False.""" ip_wrapper = IPWrapper(namespace=namespace) interfaces = ip_wrapper.netns.execute(["ip", "-d", "link", "list"], check_exit_code=True) return 'vxlan id %s ' % segmentation_id in interfaces def device_exists(device_name, namespace=None): """Return True if the device exists in the namespace.""" return IPDevice(device_name, namespace=namespace).exists() def device_exists_with_ips_and_mac(device_name, ip_cidrs, mac, namespace=None): """Return True if the device with the given IP addresses and MAC address exists in the namespace. """ try: device = IPDevice(device_name, namespace=namespace) if mac and mac != device.link.address: return False device_ip_cidrs = [ip['cidr'] for ip in device.addr.list()] for ip_cidr in ip_cidrs: if ip_cidr not in device_ip_cidrs: return False except RuntimeError: return False else: return True def get_device_mac(device_name, namespace=None): """Return the MAC address of the device.""" return IPDevice(device_name, namespace=namespace).link.address NetworkNamespaceNotFound = privileged.NetworkNamespaceNotFound NetworkInterfaceNotFound = privileged.NetworkInterfaceNotFound def get_routing_table(ip_version, namespace=None): """Return a list of dictionaries, each representing a route. @param ip_version: the routes of version to return, for example 4 @param namespace @return: a list of dictionaries, each representing a route. The dictionary format is: {'destination': cidr, 'nexthop': ip, 'device': device_name, 'scope': scope} """ # oslo.privsep turns lists to tuples in its IPC code. Change it back return list(privileged.get_routing_table(ip_version, namespace)) # NOTE(haleyb): These neighbour functions live outside the IpNeighCommand # class since not all callers require it. def add_neigh_entry(ip_address, mac_address, device, namespace=None, **kwargs): """Add a neighbour entry. :param ip_address: IP address of entry to add :param mac_address: MAC address of entry to add :param device: Device name to use in adding entry :param namespace: The name of the namespace in which to add the entry """ ip_version = common_utils.get_ip_version(ip_address) privileged.add_neigh_entry(ip_version, ip_address, mac_address, device, namespace, **kwargs) def delete_neigh_entry(ip_address, mac_address, device, namespace=None, **kwargs): """Delete a neighbour entry. :param ip_address: IP address of entry to delete :param mac_address: MAC address of entry to delete :param device: Device name to use in deleting entry :param namespace: The name of the namespace in which to delete the entry """ ip_version = common_utils.get_ip_version(ip_address) privileged.delete_neigh_entry(ip_version, ip_address, mac_address, device, namespace, **kwargs) def dump_neigh_entries(ip_version, device=None, namespace=None, **kwargs): """Dump all neighbour entries. :param ip_version: IP version of entries to show (4 or 6) :param device: Device name to use in dumping entries :param namespace: The name of the namespace in which to dump the entries :param kwargs: Callers add any filters they use as kwargs :return: a list of dictionaries, each representing a neighbour. The dictionary format is: {'dst': ip_address, 'lladdr': mac_address, 'device': device_name} """ return list(privileged.dump_neigh_entries(ip_version, device, namespace, **kwargs)) def create_network_namespace(namespace, **kwargs): """Create a network namespace. :param namespace: The name of the namespace to create :param kwargs: Callers add any filters they use as kwargs """ privileged.create_netns(namespace, **kwargs) def delete_network_namespace(namespace, **kwargs): """Delete a network namespace. :param namespace: The name of the namespace to delete :param kwargs: Callers add any filters they use as kwargs """ privileged.remove_netns(namespace, **kwargs) def list_network_namespaces(**kwargs): """List all network namespace entries. :param kwargs: Callers add any filters they use as kwargs """ if cfg.CONF.AGENT.use_helper_for_ns_read: return privileged.list_netns(**kwargs) else: return netns.listnetns(**kwargs) def network_namespace_exists(namespace, try_is_ready=False, **kwargs): """Check if a network namespace exists. :param namespace: The name of the namespace to check :param try_is_ready: Try to open the namespace to know if the namespace is ready to be operated. :param kwargs: Callers add any filters they use as kwargs """ if not try_is_ready: output = list_network_namespaces(**kwargs) return namespace in output try: privileged.open_namespace(namespace) return True except (RuntimeError, OSError): pass return False def ensure_device_is_ready(device_name, namespace=None): dev = IPDevice(device_name, namespace=namespace) dev.set_log_fail_as_error(False) try: # Ensure the device has a MAC address and is up, even if it is already # up. If the device doesn't exist, a RuntimeError will be raised. if not dev.link.address: LOG.error("Device %s cannot be used as it has no MAC " "address", device_name) return False dev.link.set_up() except RuntimeError: return False return True def iproute_arg_supported(command, arg): command += ['help'] stdout, stderr = utils.execute(command, check_exit_code=False, return_stderr=True, log_fail_as_error=False) return any(arg in line for line in stderr.split('\n')) def _arping(ns_name, iface_name, address, count, log_exception): # Due to a Linux kernel bug*, it's advised to spread gratuitous updates # more, injecting an interval between consequent packets that is longer # than 1s which is currently hardcoded** in arping. To achieve that, we # call arping tool the 'count' number of times, each issuing a single ARP # update, and wait between iterations. # # * https://patchwork.ozlabs.org/patch/760372/ # ** https://github.com/iputils/iputils/pull/86 first = True # Since arping is used to send gratuitous ARP, a response is # not expected. In some cases (no response) and with some # platforms (>=Ubuntu 14.04), arping exit code can be 1. extra_ok_codes = [1] ip_wrapper = IPWrapper(namespace=ns_name) for i in range(count): if not first: # hopefully enough for kernel to get out of locktime loop time.sleep(2) # On the second (and subsequent) arping calls, we can get a # "bind: Cannot assign requested address" error since # the IP address might have been deleted concurrently. # We will log an error below if this isn't the case, so # no need to have execute() log one as well. extra_ok_codes = [1, 2] first = False # some Linux kernels* don't honour REPLYs. Send both gratuitous REQUEST # and REPLY packets (REQUESTs are left for backwards compatibility for # in case if some network peers, vice versa, honor REPLYs and not # REQUESTs) # # * https://patchwork.ozlabs.org/patch/763016/ for arg in ('-U', '-A'): arping_cmd = ['arping', arg, '-I', iface_name, '-c', 1, # Pass -w to set timeout to ensure exit if interface # removed while running '-w', 1.5, address] try: ip_wrapper.netns.execute(arping_cmd, extra_ok_codes=extra_ok_codes) except Exception as exc: # Since this is spawned in a thread and executed 2 seconds # apart, something may have been deleted while we were # sleeping. Downgrade message to info and return early # unless it was the first try. exists = device_exists_with_ips_and_mac(iface_name, [address], mac=None, namespace=ns_name) msg = _("Failed sending gratuitous ARP to %(addr)s on " "%(iface)s in namespace %(ns)s: %(err)s") logger_method = LOG.exception if not (log_exception and (first or exists)): logger_method = LOG.info logger_method(msg, {'addr': address, 'iface': iface_name, 'ns': ns_name, 'err': exc}) if not exists: LOG.info("Interface %(iface)s or address %(addr)s " "in namespace %(ns)s was deleted concurrently", {'iface': iface_name, 'addr': address, 'ns': ns_name}) return def send_ip_addr_adv_notif( ns_name, iface_name, address, count=3, log_exception=True): """Send advance notification of an IP address assignment. If the address is in the IPv4 family, send gratuitous ARP. If the address is in the IPv6 family, no advance notification is necessary, since the Neighbor Discovery Protocol (NDP), Duplicate Address Discovery (DAD), and (for stateless addresses) router advertisements (RAs) are sufficient for address resolution and duplicate address detection. :param ns_name: Namespace name which GARPs are gonna be sent from. :param iface_name: Name of interface which GARPs are gonna be sent from. :param address: Advertised IP address. :param count: (Optional) How many GARPs are gonna be sent. Default is 3. :param log_exception: (Optional) True if possible failures should be logged on exception level. Otherwise they are logged on WARNING level. Default is True. """ def arping(): _arping(ns_name, iface_name, address, count, log_exception) if count > 0 and netaddr.IPAddress(address).version == 4: eventlet.spawn_n(arping) def sysctl(cmd, namespace=None, log_fail_as_error=True): """Run sysctl command 'cmd' @param cmd: a list containing the sysctl command to run @param namespace: network namespace to run command in @param log_fail_as_error: failure logged as LOG.error execute() doesn't return the exit status of the command it runs, it returns stdout and stderr. Setting check_exit_code=True will cause it to raise a RuntimeError if the exit status of the command is non-zero, which in sysctl's case is an error. So we're normalizing that into zero (success) and one (failure) here to mimic what "echo $?" in a shell would be. This is all because sysctl is too verbose and prints the value you just set on success, unlike most other utilities that print nothing. execute() will have dumped a message to the logs with the actual output on failure, so it's not lost, and we don't need to print it here. """ cmd = ['sysctl', '-w'] + cmd ip_wrapper = IPWrapper(namespace=namespace) try: ip_wrapper.netns.execute(cmd, run_as_root=True, log_fail_as_error=log_fail_as_error) except RuntimeError as rte: LOG.warning( "Setting %(cmd)s in namespace %(ns)s failed: %(err)s.", {'cmd': cmd, 'ns': namespace, 'err': rte}) return 1 return 0 def add_namespace_to_cmd(cmd, namespace=None): """Add an optional namespace to the command.""" return ['ip', 'netns', 'exec', namespace] + cmd if namespace else cmd def get_ipv6_lladdr(mac_addr): return '%s/64' % netaddr.EUI(mac_addr).ipv6_link_local() def get_ip_nonlocal_bind(namespace=None): """Get kernel option value of ip_nonlocal_bind in given namespace.""" cmd = ['sysctl', '-bn', IP_NONLOCAL_BIND] ip_wrapper = IPWrapper(namespace) return int(ip_wrapper.netns.execute(cmd, run_as_root=True)) def set_ip_nonlocal_bind(value, namespace=None, log_fail_as_error=True): """Set sysctl knob of ip_nonlocal_bind to given value.""" cmd = ['%s=%d' % (IP_NONLOCAL_BIND, value)] return sysctl(cmd, namespace=namespace, log_fail_as_error=log_fail_as_error) def set_ip_nonlocal_bind_for_namespace(namespace, value, root_namespace=False): """Set ip_nonlocal_bind but don't raise exception on failure.""" failed = set_ip_nonlocal_bind(value, namespace=namespace, log_fail_as_error=False) if failed and root_namespace: # Somewhere in the 3.19 kernel timeframe ip_nonlocal_bind was # changed to be a per-namespace attribute. To be backwards # compatible we need to try both if at first we fail. LOG.debug('Namespace (%s) does not support setting %s, ' 'trying in root namespace', namespace, IP_NONLOCAL_BIND) return set_ip_nonlocal_bind(value) if failed: LOG.warning( "%s will not be set to %d in the root namespace in order to " "not break DVR, which requires this value be set to 1. This " "may introduce a race between moving a floating IP to a " "different network node, and the peer side getting a " "populated ARP cache for a given floating IP address.", IP_NONLOCAL_BIND, value) def get_ipv6_forwarding(device, namespace=None): """Get kernel value of IPv6 forwarding for device in given namespace.""" cmd = ['sysctl', '-b', "net.ipv6.conf.%s.forwarding" % device] ip_wrapper = IPWrapper(namespace) return int(ip_wrapper.netns.execute(cmd, run_as_root=True)) neutron-12.1.1/neutron/agent/metadata/0000775000175000017500000000000013553660156017672 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/metadata/agent.py0000664000175000017500000002757413553660047021360 0ustar zuulzuul00000000000000# Copyright 2012 New Dream Network, LLC (DreamHost) # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import hashlib import hmac from neutron_lib import constants from neutron_lib import context from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_service import loopingcall from oslo_utils import encodeutils import requests import six import six.moves.urllib.parse as urlparse import webob from neutron._i18n import _ from neutron.agent.linux import utils as agent_utils from neutron.agent import rpc as agent_rpc from neutron.common import cache_utils as cache from neutron.common import constants as n_const from neutron.common import ipv6_utils from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.conf.agent.metadata import config LOG = logging.getLogger(__name__) MODE_MAP = { config.USER_MODE: 0o644, config.GROUP_MODE: 0o664, config.ALL_MODE: 0o666, } class MetadataPluginAPI(object): """Agent-side RPC for metadata agent-to-plugin interaction. This class implements the client side of an rpc interface used by the metadata service to make calls back into the Neutron plugin. The server side is defined in neutron.api.rpc.handlers.metadata_rpc.MetadataRpcCallback. For more information about changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. API version history: 1.0 - Initial version. """ def __init__(self, topic): target = oslo_messaging.Target( topic=topic, namespace=n_const.RPC_NAMESPACE_METADATA, version='1.0') self.client = n_rpc.get_client(target) def get_ports(self, context, filters): cctxt = self.client.prepare() return cctxt.call(context, 'get_ports', filters=filters) class MetadataProxyHandler(object): def __init__(self, conf): self.conf = conf self._cache = cache.get_cache(self.conf) self.plugin_rpc = MetadataPluginAPI(topics.PLUGIN) self.context = context.get_admin_context_without_session() @webob.dec.wsgify(RequestClass=webob.Request) def __call__(self, req): try: LOG.debug("Request: %s", req) instance_id, tenant_id = self._get_instance_and_tenant_id(req) if instance_id: return self._proxy_request(instance_id, tenant_id, req) else: return webob.exc.HTTPNotFound() except Exception: LOG.exception("Unexpected error.") msg = _('An unknown error has occurred. ' 'Please try your request again.') explanation = six.text_type(msg) return webob.exc.HTTPInternalServerError(explanation=explanation) def _get_ports_from_server(self, router_id=None, ip_address=None, networks=None): """Get ports from server.""" filters = self._get_port_filters(router_id, ip_address, networks) return self.plugin_rpc.get_ports(self.context, filters) def _get_port_filters(self, router_id=None, ip_address=None, networks=None): filters = {} if router_id: filters['device_id'] = [router_id] filters['device_owner'] = constants.ROUTER_INTERFACE_OWNERS if ip_address: filters['fixed_ips'] = {'ip_address': [ip_address]} if networks: filters['network_id'] = networks return filters @cache.cache_method_results def _get_router_networks(self, router_id): """Find all networks connected to given router.""" internal_ports = self._get_ports_from_server(router_id=router_id) return tuple(p['network_id'] for p in internal_ports) @cache.cache_method_results def _get_ports_for_remote_address(self, remote_address, networks): """Get list of ports that has given ip address and are part of given networks. :param networks: list of networks in which the ip address will be searched for """ return self._get_ports_from_server(networks=networks, ip_address=remote_address) def _get_ports(self, remote_address, network_id=None, router_id=None): """Search for all ports that contain passed ip address and belongs to given network. If no network is passed ports are searched on all networks connected to given router. Either one of network_id or router_id must be passed. """ if network_id: networks = (network_id,) elif router_id: networks = self._get_router_networks(router_id) else: raise TypeError(_("Either one of parameter network_id or router_id" " must be passed to _get_ports method.")) return self._get_ports_for_remote_address(remote_address, networks) def _get_instance_and_tenant_id(self, req): remote_address = req.headers.get('X-Forwarded-For') network_id = req.headers.get('X-Neutron-Network-ID') router_id = req.headers.get('X-Neutron-Router-ID') ports = self._get_ports(remote_address, network_id, router_id) LOG.debug("Gotten ports for remote_address %(remote_address)s, " "network_id %(network_id)s, router_id %(router_id)s are: " "%(ports)s", {"remote_address": remote_address, "network_id": network_id, "router_id": router_id, "ports": ports}) if len(ports) == 1: return ports[0]['device_id'], ports[0]['tenant_id'] return None, None def _proxy_request(self, instance_id, tenant_id, req): headers = { 'X-Forwarded-For': req.headers.get('X-Forwarded-For'), 'X-Instance-ID': instance_id, 'X-Tenant-ID': tenant_id, 'X-Instance-ID-Signature': self._sign_instance_id(instance_id) } nova_host_port = ipv6_utils.valid_ipv6_url( self.conf.nova_metadata_host, self.conf.nova_metadata_port) url = urlparse.urlunsplit(( self.conf.nova_metadata_protocol, nova_host_port, req.path_info, req.query_string, '')) disable_ssl_certificate_validation = self.conf.nova_metadata_insecure if self.conf.auth_ca_cert and not disable_ssl_certificate_validation: verify_cert = self.conf.auth_ca_cert else: verify_cert = not disable_ssl_certificate_validation client_cert = None if self.conf.nova_client_cert and self.conf.nova_client_priv_key: client_cert = (self.conf.nova_client_cert, self.conf.nova_client_priv_key) resp = requests.request(method=req.method, url=url, headers=headers, data=req.body, cert=client_cert, verify=verify_cert) if resp.status_code == 200: req.response.content_type = resp.headers['content-type'] req.response.body = resp.content LOG.debug(str(resp)) return req.response elif resp.status_code == 403: LOG.warning( 'The remote metadata server responded with Forbidden. This ' 'response usually occurs when shared secrets do not match.' ) return webob.exc.HTTPForbidden() elif resp.status_code == 400: return webob.exc.HTTPBadRequest() elif resp.status_code == 404: return webob.exc.HTTPNotFound() elif resp.status_code == 409: return webob.exc.HTTPConflict() elif resp.status_code == 500: msg = _( 'Remote metadata server experienced an internal server error.' ) LOG.warning(msg) explanation = six.text_type(msg) return webob.exc.HTTPInternalServerError(explanation=explanation) else: raise Exception(_('Unexpected response code: %s') % resp.status_code) def _sign_instance_id(self, instance_id): secret = self.conf.metadata_proxy_shared_secret secret = encodeutils.to_utf8(secret) instance_id = encodeutils.to_utf8(instance_id) return hmac.new(secret, instance_id, hashlib.sha256).hexdigest() class UnixDomainMetadataProxy(object): def __init__(self, conf): self.conf = conf agent_utils.ensure_directory_exists_without_file( cfg.CONF.metadata_proxy_socket) def _init_state_reporting(self): self.context = context.get_admin_context_without_session() self.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS) self.agent_state = { 'binary': 'neutron-metadata-agent', 'host': cfg.CONF.host, 'topic': 'N/A', 'configurations': { 'metadata_proxy_socket': cfg.CONF.metadata_proxy_socket, 'nova_metadata_host': cfg.CONF.nova_metadata_host, 'nova_metadata_port': cfg.CONF.nova_metadata_port, 'log_agent_heartbeats': cfg.CONF.AGENT.log_agent_heartbeats, }, 'start_flag': True, 'agent_type': constants.AGENT_TYPE_METADATA} report_interval = cfg.CONF.AGENT.report_interval if report_interval: self.heartbeat = loopingcall.FixedIntervalLoopingCall( self._report_state) self.heartbeat.start(interval=report_interval) def _report_state(self): try: self.state_rpc.report_state( self.context, self.agent_state, use_call=self.agent_state.get('start_flag')) except AttributeError: # This means the server does not support report_state LOG.warning('Neutron server does not support state report.' ' State report for this agent will be disabled.') self.heartbeat.stop() return except Exception: LOG.exception("Failed reporting state!") return self.agent_state.pop('start_flag', None) def _get_socket_mode(self): mode = self.conf.metadata_proxy_socket_mode if mode == config.DEDUCE_MODE: user = self.conf.metadata_proxy_user if (not user or user == '0' or user == 'root' or agent_utils.is_effective_user(user)): # user is agent effective user or root => USER_MODE mode = config.USER_MODE else: group = self.conf.metadata_proxy_group if not group or agent_utils.is_effective_group(group): # group is agent effective group => GROUP_MODE mode = config.GROUP_MODE else: # otherwise => ALL_MODE mode = config.ALL_MODE return MODE_MAP[mode] def run(self): server = agent_utils.UnixDomainWSGIServer('neutron-metadata-agent') server.start(MetadataProxyHandler(self.conf), self.conf.metadata_proxy_socket, workers=self.conf.metadata_workers, backlog=self.conf.metadata_backlog, mode=self._get_socket_mode()) self._init_state_reporting() server.wait() neutron-12.1.1/neutron/agent/metadata/__init__.py0000664000175000017500000000000013553660046021767 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/metadata/driver.py0000664000175000017500000002755213553660047021551 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import errno import grp import os import pwd from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from oslo_config import cfg from oslo_log import log as logging from neutron._i18n import _ from neutron.agent.l3 import ha_router from neutron.agent.l3 import namespaces from neutron.agent.linux import external_process from neutron.common import constants from neutron.common import exceptions LOG = logging.getLogger(__name__) METADATA_SERVICE_NAME = 'metadata-proxy' PROXY_CONFIG_DIR = "ns-metadata-proxy" _HAPROXY_CONFIG_TEMPLATE = """ global log /dev/log local0 %(log_level)s log-tag %(log_tag)s user %(user)s group %(group)s maxconn 1024 pidfile %(pidfile)s daemon defaults log global mode http option httplog option dontlognull option http-server-close option forwardfor retries 3 timeout http-request 30s timeout connect 30s timeout client 32s timeout server 32s timeout http-keep-alive 30s listen listener bind %(host)s:%(port)s server metadata %(unix_socket_path)s http-request add-header X-Neutron-%(res_type)s-ID %(res_id)s """ class InvalidUserOrGroupException(Exception): pass class HaproxyConfigurator(object): def __init__(self, network_id, router_id, unix_socket_path, host, port, user, group, state_path, pid_file): self.network_id = network_id self.router_id = router_id if network_id is None and router_id is None: raise exceptions.NetworkIdOrRouterIdRequiredError() self.host = host self.port = port self.user = user self.group = group self.state_path = state_path self.unix_socket_path = unix_socket_path self.pidfile = pid_file self.log_level = ( 'debug' if logging.is_debug_enabled(cfg.CONF) else 'info') # log-tag will cause entries to have the string pre-pended, so use # the uuid haproxy will be started with. Additionally, if it # starts with "haproxy" then things will get logged to # /var/log/haproxy.log on Debian distros, instead of to syslog. uuid = network_id or router_id self.log_tag = "haproxy-" + METADATA_SERVICE_NAME + "-" + uuid def create_config_file(self): """Create the config file for haproxy.""" # Need to convert uid/gid into username/group try: username = pwd.getpwuid(int(self.user)).pw_name except (ValueError, KeyError): try: username = pwd.getpwnam(self.user).pw_name except KeyError: raise InvalidUserOrGroupException( _("Invalid user/uid: '%s'") % self.user) try: groupname = grp.getgrgid(int(self.group)).gr_name except (ValueError, KeyError): try: groupname = grp.getgrnam(self.group).gr_name except KeyError: raise InvalidUserOrGroupException( _("Invalid group/gid: '%s'") % self.group) cfg_info = { 'host': self.host, 'port': self.port, 'unix_socket_path': self.unix_socket_path, 'user': username, 'group': groupname, 'pidfile': self.pidfile, 'log_level': self.log_level, 'log_tag': self.log_tag } if self.network_id: cfg_info['res_type'] = 'Network' cfg_info['res_id'] = self.network_id else: cfg_info['res_type'] = 'Router' cfg_info['res_id'] = self.router_id haproxy_cfg = _HAPROXY_CONFIG_TEMPLATE % cfg_info LOG.debug("haproxy_cfg = %s", haproxy_cfg) cfg_dir = self.get_config_path(self.state_path) # uuid has to be included somewhere in the command line so that it can # be tracked by process_monitor. self.cfg_path = os.path.join(cfg_dir, "%s.conf" % cfg_info['res_id']) if not os.path.exists(cfg_dir): os.makedirs(cfg_dir) with open(self.cfg_path, "w") as cfg_file: cfg_file.write(haproxy_cfg) @staticmethod def get_config_path(state_path): return os.path.join(state_path or cfg.CONF.state_path, PROXY_CONFIG_DIR) @staticmethod def cleanup_config_file(uuid, state_path): """Delete config file created when metadata proxy was spawned.""" # Delete config file if it exists cfg_path = os.path.join( HaproxyConfigurator.get_config_path(state_path), "%s.conf" % uuid) try: os.unlink(cfg_path) except OSError as ex: # It can happen that this function is called but metadata proxy # was never spawned so its config file won't exist if ex.errno != errno.ENOENT: raise class MetadataDriver(object): monitors = {} def __init__(self, l3_agent): self.metadata_port = l3_agent.conf.metadata_port self.metadata_access_mark = l3_agent.conf.metadata_access_mark registry.subscribe( after_router_added, resources.ROUTER, events.AFTER_CREATE) registry.subscribe( after_router_updated, resources.ROUTER, events.AFTER_UPDATE) registry.subscribe( before_router_removed, resources.ROUTER, events.BEFORE_DELETE) @classmethod def metadata_filter_rules(cls, port, mark): return [('INPUT', '-m mark --mark %s/%s -j ACCEPT' % (mark, constants.ROUTER_MARK_MASK)), ('INPUT', '-p tcp -m tcp --dport %s ' '-j DROP' % port)] @classmethod def metadata_nat_rules(cls, port): return [('PREROUTING', '-d 169.254.169.254/32 ' '-i %(interface_name)s ' '-p tcp -m tcp --dport 80 -j REDIRECT ' '--to-ports %(port)s' % {'interface_name': namespaces.INTERNAL_DEV_PREFIX + '+', 'port': port})] @classmethod def _get_metadata_proxy_user_group(cls, conf): user = conf.metadata_proxy_user or str(os.geteuid()) group = conf.metadata_proxy_group or str(os.getegid()) return user, group @classmethod def _get_metadata_proxy_callback(cls, bind_address, port, conf, network_id=None, router_id=None): def callback(pid_file): metadata_proxy_socket = conf.metadata_proxy_socket user, group = ( cls._get_metadata_proxy_user_group(conf)) haproxy = HaproxyConfigurator(network_id, router_id, metadata_proxy_socket, bind_address, port, user, group, conf.state_path, pid_file) haproxy.create_config_file() proxy_cmd = ['haproxy', '-f', haproxy.cfg_path] return proxy_cmd return callback @classmethod def spawn_monitored_metadata_proxy(cls, monitor, ns_name, port, conf, bind_address="0.0.0.0", network_id=None, router_id=None): uuid = network_id or router_id callback = cls._get_metadata_proxy_callback( bind_address, port, conf, network_id=network_id, router_id=router_id) pm = cls._get_metadata_proxy_process_manager(uuid, conf, ns_name=ns_name, callback=callback) # TODO(dalvarez): Remove in Q cycle. This will kill running instances # of old ns-metadata-proxy Python version in order to be replaced by # haproxy. This will help with upgrading and shall be removed in next # cycle. cls._migrate_python_ns_metadata_proxy_if_needed(pm) pm.enable() monitor.register(uuid, METADATA_SERVICE_NAME, pm) cls.monitors[router_id] = pm @staticmethod def _migrate_python_ns_metadata_proxy_if_needed(pm): """Kill running Python version of ns-metadata-proxy. This function will detect if the current metadata proxy process is running the old Python version and kill it so that the new haproxy version is spawned instead. """ # Read cmdline to a local var to avoid reading twice from /proc file cmdline = pm.cmdline if cmdline and 'haproxy' not in cmdline: LOG.debug("Migrating old instance of python ns-metadata proxy to " "new one based on haproxy (%s)", cmdline) pm.disable() @classmethod def destroy_monitored_metadata_proxy(cls, monitor, uuid, conf, ns_name): monitor.unregister(uuid, METADATA_SERVICE_NAME) pm = cls._get_metadata_proxy_process_manager(uuid, conf, ns_name=ns_name) pm.disable() # Delete metadata proxy config file HaproxyConfigurator.cleanup_config_file(uuid, cfg.CONF.state_path) cls.monitors.pop(uuid, None) @classmethod def _get_metadata_proxy_process_manager(cls, router_id, conf, ns_name=None, callback=None): return external_process.ProcessManager( conf=conf, uuid=router_id, namespace=ns_name, default_cmd_callback=callback) def after_router_added(resource, event, l3_agent, **kwargs): router = kwargs['router'] proxy = l3_agent.metadata_driver for c, r in proxy.metadata_filter_rules(proxy.metadata_port, proxy.metadata_access_mark): router.iptables_manager.ipv4['filter'].add_rule(c, r) for c, r in proxy.metadata_nat_rules(proxy.metadata_port): router.iptables_manager.ipv4['nat'].add_rule(c, r) router.iptables_manager.apply() if not isinstance(router, ha_router.HaRouter): proxy.spawn_monitored_metadata_proxy( l3_agent.process_monitor, router.ns_name, proxy.metadata_port, l3_agent.conf, router_id=router.router_id) def after_router_updated(resource, event, l3_agent, **kwargs): router = kwargs['router'] proxy = l3_agent.metadata_driver if (not proxy.monitors.get(router.router_id) and not isinstance(router, ha_router.HaRouter)): proxy.spawn_monitored_metadata_proxy( l3_agent.process_monitor, router.ns_name, proxy.metadata_port, l3_agent.conf, router_id=router.router_id) def before_router_removed(resource, event, l3_agent, **kwargs): router = kwargs['router'] proxy = l3_agent.metadata_driver proxy.destroy_monitored_metadata_proxy(l3_agent.process_monitor, router.router['id'], l3_agent.conf, router.ns_name) neutron-12.1.1/neutron/agent/securitygroups_rpc.py0000664000175000017500000003107013553660047022437 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import functools from debtcollector import moves from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from neutron.agent import firewall from neutron.api.rpc.handlers import securitygroups_rpc from neutron.common import constants as common_constants from neutron.conf.agent import securitygroups_rpc as sc_cfg LOG = logging.getLogger(__name__) sc_cfg.register_securitygroups_opts() def is_firewall_enabled(): return cfg.CONF.SECURITYGROUP.enable_security_group def _disable_extension(extension, aliases): if extension in aliases: aliases.remove(extension) def disable_security_group_extension_by_config(aliases): if not is_firewall_enabled(): LOG.info('Disabled security-group extension.') _disable_extension('security-group', aliases) LOG.info('Disabled allowed-address-pairs extension.') _disable_extension('allowed-address-pairs', aliases) class SecurityGroupAgentRpc(object): """Enables SecurityGroup agent support in agent implementations.""" def __init__(self, context, plugin_rpc, local_vlan_map=None, defer_refresh_firewall=False, integration_bridge=None): self.context = context self.plugin_rpc = plugin_rpc self.init_firewall(defer_refresh_firewall, integration_bridge) def _get_trusted_devices(self, device_ids, devices): trusted_devices = [] # Devices which are already added in firewall ports should # not be treated as trusted devices but as regular ports all_devices = devices.copy() all_devices.update(self.firewall.ports) device_names = [ dev['device'] for dev in all_devices.values()] for device_id in device_ids: if (device_id not in all_devices.keys() and device_id not in device_names): trusted_devices.append(device_id) return trusted_devices def init_firewall(self, defer_refresh_firewall=False, integration_bridge=None): firewall_driver = cfg.CONF.SECURITYGROUP.firewall_driver or 'noop' LOG.debug("Init firewall settings (driver=%s)", firewall_driver) firewall_class = firewall.load_firewall_driver_class(firewall_driver) try: self.firewall = firewall_class( integration_bridge=integration_bridge) except TypeError: self.firewall = firewall_class() # The following flag will be set to true if port filter must not be # applied as soon as a rule or membership notification is received self.defer_refresh_firewall = defer_refresh_firewall # Stores devices for which firewall should be refreshed when # deferred refresh is enabled. self.devices_to_refilter = set() # Flag raised when a global refresh is needed self.global_refresh_firewall = False self._use_enhanced_rpc = None @property def use_enhanced_rpc(self): if self._use_enhanced_rpc is None: self._use_enhanced_rpc = ( self._check_enhanced_rpc_is_supported_by_server()) return self._use_enhanced_rpc def _check_enhanced_rpc_is_supported_by_server(self): try: self.plugin_rpc.security_group_info_for_devices( self.context, devices=[]) except oslo_messaging.UnsupportedVersion: LOG.warning('security_group_info_for_devices rpc call not ' 'supported by the server, falling back to old ' 'security_group_rules_for_devices which scales ' 'worse.') return False return True def skip_if_noopfirewall_or_firewall_disabled(func): @functools.wraps(func) def decorated_function(self, *args, **kwargs): if (isinstance(self.firewall, firewall.NoopFirewallDriver) or not is_firewall_enabled()): LOG.info("Skipping method %s as firewall is disabled " "or configured as NoopFirewallDriver.", func.__name__) else: return func(self, # pylint: disable=not-callable *args, **kwargs) return decorated_function @skip_if_noopfirewall_or_firewall_disabled def prepare_devices_filter(self, device_ids): if not device_ids: return LOG.info("Preparing filters for devices %s", device_ids) self._apply_port_filter(device_ids) def _apply_port_filter(self, device_ids, update_filter=False): step = common_constants.AGENT_RES_PROCESSING_STEP if self.use_enhanced_rpc: devices = {} security_groups = {} security_group_member_ips = {} for i in range(0, len(device_ids), step): devices_info = self.plugin_rpc.security_group_info_for_devices( self.context, list(device_ids)[i:i + step]) devices.update(devices_info['devices']) security_groups.update(devices_info['security_groups']) security_group_member_ips.update(devices_info['sg_member_ips']) else: devices = self.plugin_rpc.security_group_rules_for_devices( self.context, list(device_ids)) trusted_devices = self._get_trusted_devices(device_ids, devices) with self.firewall.defer_apply(): if self.use_enhanced_rpc: LOG.debug("Update security group information for ports %s", devices.keys()) self._update_security_group_info( security_groups, security_group_member_ips) for device in devices.values(): if update_filter: LOG.debug("Update port filter for %s", device['device']) self.firewall.update_port_filter(device) else: LOG.debug("Prepare port filter for %s", device['device']) self.firewall.prepare_port_filter(device) self.firewall.process_trusted_ports(trusted_devices) def _update_security_group_info(self, security_groups, security_group_member_ips): LOG.debug("Update security group information") for sg_id, sg_rules in security_groups.items(): self.firewall.update_security_group_rules(sg_id, sg_rules) for remote_sg_id, member_ips in security_group_member_ips.items(): self.firewall.update_security_group_members( remote_sg_id, member_ips) def security_groups_rule_updated(self, security_groups): LOG.info("Security group " "rule updated %r", security_groups) self._security_group_updated( security_groups, 'security_groups', 'sg_rule') def security_groups_member_updated(self, security_groups): LOG.info("Security group " "member updated %r", security_groups) self._security_group_updated( security_groups, 'security_group_source_groups', 'sg_member') def _security_group_updated(self, security_groups, attribute, action_type): devices = [] sec_grp_set = set(security_groups) for device in self.firewall.ports.values(): if sec_grp_set & set(device.get(attribute, [])): devices.append(device['device']) if devices: if self.use_enhanced_rpc: self.firewall.security_group_updated(action_type, sec_grp_set) if self.defer_refresh_firewall: LOG.debug("Adding %s devices to the list of devices " "for which firewall needs to be refreshed", devices) self.devices_to_refilter |= set(devices) else: self.refresh_firewall(devices) def remove_devices_filter(self, device_ids): if not device_ids: return LOG.info("Remove device filter for %r", device_ids) with self.firewall.defer_apply(): for device_id in device_ids: device = self.firewall.ports.get(device_id) if device: self.firewall.remove_port_filter(device) else: self.firewall.remove_trusted_ports([device_id]) @skip_if_noopfirewall_or_firewall_disabled def refresh_firewall(self, device_ids=None): LOG.info("Refresh firewall rules") if not device_ids: device_ids = self.firewall.ports.keys() if not device_ids: LOG.info("No ports here to refresh firewall") return self._apply_port_filter(device_ids, update_filter=True) def firewall_refresh_needed(self): return self.global_refresh_firewall or self.devices_to_refilter def setup_port_filters(self, new_devices, updated_devices): """Configure port filters for devices. This routine applies filters for new devices and refreshes firewall rules when devices have been updated, or when there are changes in security group membership or rules. :param new_devices: set containing identifiers for new devices :param updated_devices: set containing identifiers for updated devices """ # These data structures are cleared here in order to avoid # losing updates occurring during firewall refresh devices_to_refilter = self.devices_to_refilter global_refresh_firewall = self.global_refresh_firewall self.devices_to_refilter = set() self.global_refresh_firewall = False # We must call prepare_devices_filter() after we've grabbed # self.devices_to_refilter since an update for a new port # could arrive while we're processing, and we need to make # sure we don't skip it. It will get handled the next time. if new_devices: LOG.debug("Preparing device filters for %d new devices", len(new_devices)) self.prepare_devices_filter(new_devices) # TODO(salv-orlando): Avoid if possible ever performing the global # refresh providing a precise list of devices for which firewall # should be refreshed if global_refresh_firewall: LOG.debug("Refreshing firewall for all filtered devices") self.refresh_firewall() else: if self.use_enhanced_rpc and updated_devices: self.firewall.security_group_updated('sg_member', [], updated_devices) # If a device is both in new and updated devices # avoid reprocessing it updated_devices = ((updated_devices | devices_to_refilter) - new_devices) if updated_devices: LOG.debug("Refreshing firewall for %d devices", len(updated_devices)) self.refresh_firewall(updated_devices) # TODO(armax): For bw compat with external dependencies; to be dropped in P. # NOTE(dasm): Should be already removed, but didn't have DeprecationWarning. SG_RPC_VERSION = moves.moved_function( securitygroups_rpc.SecurityGroupAgentRpcApiMixin.SG_RPC_VERSION, 'SG_RPC_VERSION', __name__, version='Liberty', removal_version='Pike' ) SecurityGroupServerRpcApi = moves.moved_class( securitygroups_rpc.SecurityGroupServerRpcApi, 'SecurityGroupServerRpcApi', old_module_name=__name__, version='Liberty', removal_version='Pike' ) SecurityGroupAgentRpcApiMixin = moves.moved_class( securitygroups_rpc.SecurityGroupAgentRpcApiMixin, 'SecurityGroupAgentRpcApiMixin', old_module_name=__name__, version='Liberty', removal_version='Pike' ) SecurityGroupAgentRpcCallbackMixin = moves.moved_class( securitygroups_rpc.SecurityGroupAgentRpcCallbackMixin, 'SecurityGroupAgentRpcCallbackMixin', old_module_name=__name__, version='Liberty', removal_version='Pike' ) neutron-12.1.1/neutron/agent/dhcp_agent.py0000664000175000017500000000341713553660047020564 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from oslo_config import cfg from oslo_service import service from neutron.common import config as common_config from neutron.common import topics from neutron.conf.agent import common as config from neutron.conf.agent import dhcp as dhcp_config from neutron.conf.agent.metadata import config as meta_conf from neutron import service as neutron_service def register_options(conf): config.register_interface_driver_opts_helper(conf) config.register_agent_state_opts_helper(conf) config.register_availability_zone_opts_helper(conf) dhcp_config.register_agent_dhcp_opts(conf) meta_conf.register_meta_conf_opts(meta_conf.SHARED_OPTS, conf) config.register_interface_opts(conf) config.register_root_helper(conf) def main(): register_options(cfg.CONF) common_config.init(sys.argv[1:]) config.setup_logging() config.setup_privsep() server = neutron_service.Service.create( binary='neutron-dhcp-agent', topic=topics.DHCP_AGENT, report_interval=cfg.CONF.AGENT.report_interval, manager='neutron.agent.dhcp.agent.DhcpAgentWithStateReport') service.launch(cfg.CONF, server).wait() neutron-12.1.1/neutron/agent/agent_extensions_manager.py0000664000175000017500000000457013553660046023537 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log import stevedore from neutron.conf.agent import agent_extensions_manager as agent_ext_mgr_config LOG = log.getLogger(__name__) agent_ext_mgr_config.register_agent_ext_manager_opts() class AgentExtensionsManager(stevedore.named.NamedExtensionManager): """Manage agent extensions.""" def __init__(self, conf, namespace): super(AgentExtensionsManager, self).__init__( namespace, conf.agent.extensions, invoke_on_load=True, name_order=True) LOG.info("Loaded agent extensions: %s", self.names()) def initialize(self, connection, driver_type, agent_api=None): """Initialize enabled agent extensions. :param connection: RPC connection that can be reused by extensions to define their RPC endpoints :param driver_type: a string that defines the agent type to the extension. Can be used by the extension to choose the right backend implementation. :param agent_api: an AgentAPI instance that provides an API to interact with the agent that the manager is running in. """ # Initialize each agent extension in the list. for extension in self: LOG.info("Initializing agent extension '%s'", extension.name) # If the agent has provided an agent_api object, this object will # be passed to all interested extensions. This object must be # consumed by each such extension before the extension's # initialize() method is called, as the initialization step # relies on the agent_api already being available. extension.obj.consume_api(agent_api) extension.obj.initialize(connection, driver_type) neutron-12.1.1/neutron/agent/resource_cache.py0000664000175000017500000002572713553660047021452 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import context as n_ctx from oslo_log import log as logging from neutron.api.rpc.callbacks.consumer import registry as registry_rpc from neutron.api.rpc.callbacks import events as events_rpc from neutron.api.rpc.handlers import resources_rpc from neutron.common import rpc as n_rpc from neutron import objects LOG = logging.getLogger(__name__) objects.register_objects() class RemoteResourceCache(object): """Retrieves and stashes logical resources in their OVO format. This is currently only compatible with OVO objects that have an ID. """ def __init__(self, resource_types): self.resource_types = resource_types self._cache_by_type_and_id = {rt: {} for rt in self.resource_types} self._deleted_ids_by_type = {rt: set() for rt in self.resource_types} # track everything we've asked the server so we don't ask again self._satisfied_server_queries = set() self._puller = resources_rpc.ResourcesPullRpcApi() def _type_cache(self, rtype): if rtype not in self.resource_types: raise RuntimeError("Resource cache not tracking %s" % rtype) return self._cache_by_type_and_id[rtype] def start_watcher(self): self._watcher = RemoteResourceWatcher(self) def get_resource_by_id(self, rtype, obj_id): """Returns None if it doesn't exist.""" if obj_id in self._deleted_ids_by_type[rtype]: return None cached_item = self._type_cache(rtype).get(obj_id) if cached_item: return cached_item # try server in case object existed before agent start self._flood_cache_for_query(rtype, id=(obj_id, )) return self._type_cache(rtype).get(obj_id) def _flood_cache_for_query(self, rtype, **filter_kwargs): """Load info from server for first query. Queries the server if this is the first time a given query for rtype has been issued. """ query_ids = self._get_query_ids(rtype, filter_kwargs) if query_ids.issubset(self._satisfied_server_queries): # we've already asked the server this question so we don't # ask directly again because any updates will have been # pushed to us return context = n_ctx.get_admin_context() resources = self._puller.bulk_pull(context, rtype, filter_kwargs=filter_kwargs) for resource in resources: if self._is_stale(rtype, resource): # if the server was slow enough to respond the object may have # been updated already and pushed to us in another thread. LOG.debug("Ignoring stale update for %s: %s", rtype, resource) continue self.record_resource_update(context, rtype, resource) LOG.debug("%s resources returned for queries %s", len(resources), query_ids) self._satisfied_server_queries.update(query_ids) def _get_query_ids(self, rtype, filters): """Turns filters for a given rypte into a set of query IDs. This can result in multiple queries due to the nature of the query processing on the server side. Since multiple values are treated as an OR condition, a query for {'id': ('1', '2')} is equivalent to a query for {'id': ('1',)} and {'id': ('2')}. This method splits the former into the latter to ensure we aren't asking the server something we already know. """ query_ids = set() for k, values in tuple(sorted(filters.items())): if len(values) > 1: for v in values: new_filters = filters.copy() new_filters[k] = (v, ) query_ids.update(self._get_query_ids(rtype, new_filters)) break else: # no multiple value filters left so add an ID query_ids.add((rtype, ) + tuple(sorted(filters.items()))) return query_ids def get_resources(self, rtype, filters): """Find resources that match key:values in filters dict. If the attribute on the object is a list, each value is checked if it is in the list. The values in the dicionary for a single key are matched in an OR fashion. """ self._flood_cache_for_query(rtype, **filters) def match(obj): for key, values in filters.items(): for value in values: attr = getattr(obj, key) if isinstance(attr, (list, tuple, set)): # attribute is a list so we check if value is in # list if value in attr: break elif value == attr: break else: # no match found for this key return False return True return self.match_resources_with_func(rtype, match) def match_resources_with_func(self, rtype, matcher): """Returns a list of all resources satisfying func matcher.""" # TODO(kevinbenton): this is O(N), offer better lookup functions return [r for r in self._type_cache(rtype).values() if matcher(r)] def _is_stale(self, rtype, resource): """Determines if a given resource update is safe to ignore. It can be safe to ignore if it has already been deleted or if we have a copy with a higher revision number. """ if resource.id in self._deleted_ids_by_type[rtype]: return True existing = self._type_cache(rtype).get(resource.id) if existing and existing.revision_number > resource.revision_number: # NOTE(kevinbenton): we could be strict and check for >=, but this # makes us more tolerant of bugs on the server where we forget to # bump the revision_number. return True return False def record_resource_update(self, context, rtype, resource): """Takes in an OVO and generates an event on relevant changes. A change is deemed to be relevant if it is not stale and if any fields changed beyond the revision number and update time. Both creates and updates are handled in this function. """ if self._is_stale(rtype, resource): LOG.debug("Ignoring stale update for %s: %s", rtype, resource) return existing = self._type_cache(rtype).get(resource.id) self._type_cache(rtype)[resource.id] = resource changed_fields = self._get_changed_fields(existing, resource) if not changed_fields: LOG.debug("Received resource %s update without any changes: %s", rtype, resource.id) return if existing: LOG.debug("Resource %s %s updated (revision_number %s->%s). " "Old fields: %s New fields: %s", rtype, existing.id, existing.revision_number, resource.revision_number, {f: existing.get(f) for f in changed_fields}, {f: resource.get(f) for f in changed_fields}) else: LOG.debug("Received new resource %s: %s", rtype, resource) # local notification for agent internals to subscribe to registry.notify(rtype, events.AFTER_UPDATE, self, context=context, changed_fields=changed_fields, existing=existing, updated=resource, resource_id=resource.id) def record_resource_delete(self, context, rtype, resource_id): # deletions are final, record them so we never # accept new data for the same ID. LOG.debug("Resource %s deleted: %s", rtype, resource_id) # TODO(kevinbenton): we need a way to expire items from the set at # some TTL so it doesn't grow indefinitely with churn if resource_id in self._deleted_ids_by_type[rtype]: LOG.debug("Skipped duplicate delete event for %s", resource_id) return self._deleted_ids_by_type[rtype].add(resource_id) existing = self._type_cache(rtype).pop(resource_id, None) # local notification for agent internals to subscribe to registry.notify(rtype, events.AFTER_DELETE, self, context=context, existing=existing, resource_id=resource_id) def _get_changed_fields(self, old, new): """Returns changed fields excluding update time and revision.""" new = new.to_dict() changed = set(new) if old: for k, v in old.to_dict().items(): if v == new.get(k): changed.discard(k) for ignore in ('revision_number', 'updated_at'): changed.discard(ignore) return changed class RemoteResourceWatcher(object): """Converts RPC callback notifications to local registry notifications. This allows a constructor to listen for RPC callbacks for a given dictionary of resources and fields desired. This watcher will listen to the RPC callbacks as sent on the wire and handle things like out-of-order message detection and throwing away updates to fields the constructor doesn't care about. All watched resources must be primary keyed on a field called 'id' and have a standard attr revision number. """ def __init__(self, remote_resource_cache): self.rcache = remote_resource_cache self._init_rpc_listeners() def _init_rpc_listeners(self): endpoints = [resources_rpc.ResourcesPushRpcCallback()] self._connection = n_rpc.create_connection() for rtype in self.rcache.resource_types: registry_rpc.register(self.resource_change_handler, rtype) topic = resources_rpc.resource_type_versioned_topic(rtype) self._connection.create_consumer(topic, endpoints, fanout=True) self._connection.consume_in_threads() def resource_change_handler(self, context, rtype, resources, event_type): for r in resources: if event_type == events_rpc.DELETED: self.rcache.record_resource_delete(context, rtype, r.id) else: # creates and updates are treated equally self.rcache.record_resource_update(context, rtype, r) neutron-12.1.1/neutron/agent/firewall.py0000664000175000017500000001467713553660047020307 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import contextlib import six from neutron_lib.api.definitions import port_security as psec from neutron_lib import constants as n_const from neutron_lib.utils import runtime INGRESS_DIRECTION = n_const.INGRESS_DIRECTION EGRESS_DIRECTION = n_const.EGRESS_DIRECTION DIRECTION_IP_PREFIX = {INGRESS_DIRECTION: 'source_ip_prefix', EGRESS_DIRECTION: 'dest_ip_prefix'} # List of ICMPv6 types that should be permitted (ingress) by default. This list # depends on iptables conntrack behavior of recognizing ICMP errors (types 1-4) # as related traffic. ICMPV6_ALLOWED_INGRESS_TYPES = (n_const.ICMPV6_TYPE_MLD_QUERY, n_const.ICMPV6_TYPE_NS, n_const.ICMPV6_TYPE_NA) # List of ICMPv6 types that should be permitted (egress) by default. ICMPV6_ALLOWED_EGRESS_TYPES = (n_const.ICMPV6_TYPE_MLD_QUERY, n_const.ICMPV6_TYPE_RS, n_const.ICMPV6_TYPE_NS, n_const.ICMPV6_TYPE_NA) def port_sec_enabled(port): return port.get(psec.PORTSECURITY, True) def load_firewall_driver_class(driver): return runtime.load_class_by_alias_or_classname( 'neutron.agent.firewall_drivers', driver) @six.add_metaclass(abc.ABCMeta) class FirewallDriver(object): """Firewall Driver base class. Defines methods that any driver providing security groups and provider firewall functionality should implement. Note port attribute should have information of security group ids and security group rules. the dict of port should have device : interface name fixed_ips: ips of the device mac_address: mac_address of the device security_groups: [sgid, sgid] security_group_rules : [ rule, rule ] the rule must contain ethertype and direction the rule may contain security_group_id, protocol, port_min, port_max source_ip_prefix, source_port_min, source_port_max, dest_ip_prefix, and remote_group_id Note: source_group_ip in REST API should be converted by this rule if direction is ingress: remote_group_ip will be a source_ip_prefix if direction is egress: remote_group_ip will be a dest_ip_prefix Note: remote_group_id in REST API should be converted by this rule if direction is ingress: remote_group_id will be a list of source_ip_prefix if direction is egress: remote_group_id will be a list of dest_ip_prefix remote_group_id will also remaining membership update management """ # OVS agent installs arp spoofing openflow rules. If firewall is capable # of handling that, ovs agent doesn't need to install the protection. provides_arp_spoofing_protection = False @abc.abstractmethod def prepare_port_filter(self, port): """Prepare filters for the port. This method should be called before the port is created. """ def apply_port_filter(self, port): """Apply port filter. Once this method returns, the port should be firewalled appropriately. This method should as far as possible be a no-op. It's vastly preferred to get everything set up in prepare_port_filter. """ raise NotImplementedError() @abc.abstractmethod def update_port_filter(self, port): """Refresh security group rules from data store Gets called when a port gets added to or removed from the security group the port is a member of or if the group gains or looses a rule. """ def remove_port_filter(self, port): """Stop filtering port.""" raise NotImplementedError() def filter_defer_apply_on(self): """Defer application of filtering rule.""" pass def filter_defer_apply_off(self): """Turn off deferral of rules and apply the rules now.""" pass @property def ports(self): """Returns filtered ports.""" pass @contextlib.contextmanager def defer_apply(self): """Defer apply context.""" self.filter_defer_apply_on() try: yield finally: self.filter_defer_apply_off() def update_security_group_members(self, sg_id, ips): """Update group members in a security group.""" raise NotImplementedError() def update_security_group_rules(self, sg_id, rules): """Update rules in a security group.""" raise NotImplementedError() def security_group_updated(self, action_type, sec_group_ids, device_id=None): """Called when a security group is updated. Note: This method needs to be implemented by the firewall drivers which use enhanced RPC for security_groups. """ raise NotImplementedError() def process_trusted_ports(self, port_ids): """Process ports that are trusted and shouldn't be filtered.""" pass def remove_trusted_ports(self, port_ids): pass class NoopFirewallDriver(FirewallDriver): """Noop Firewall Driver. Firewall driver which does nothing. This driver is for disabling the firewall functionality. """ def prepare_port_filter(self, port): pass def apply_port_filter(self, port): pass def update_port_filter(self, port): pass def remove_port_filter(self, port): pass def filter_defer_apply_on(self): pass def filter_defer_apply_off(self): pass @property def ports(self): return {} def update_security_group_members(self, sg_id, ips): pass def update_security_group_rules(self, sg_id, rules): pass def security_group_updated(self, action_type, sec_group_ids, device_id=None): pass neutron-12.1.1/neutron/agent/__init__.py0000664000175000017500000000000013553660046020207 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/l3_agent.py0000664000175000017500000000373613553660047020170 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from oslo_config import cfg from oslo_service import service from neutron.common import config as common_config from neutron.common import topics from neutron.conf.agent import common as config from neutron.conf.agent.l3 import config as l3_config from neutron.conf.agent.l3 import ha as ha_conf from neutron.conf.agent.metadata import config as meta_conf from neutron import service as neutron_service def register_opts(conf): l3_config.register_l3_agent_config_opts(l3_config.OPTS, conf) ha_conf.register_l3_agent_ha_opts(conf) meta_conf.register_meta_conf_opts(meta_conf.SHARED_OPTS, conf) config.register_interface_driver_opts_helper(conf) config.register_agent_state_opts_helper(conf) config.register_interface_opts(conf) config.register_external_process_opts(conf) config.register_pddriver_opts(conf) config.register_ra_opts(conf) config.register_availability_zone_opts_helper(conf) def main(manager='neutron.agent.l3.agent.L3NATAgentWithStateReport'): register_opts(cfg.CONF) common_config.init(sys.argv[1:]) config.setup_logging() config.setup_privsep() server = neutron_service.Service.create( binary='neutron-l3-agent', topic=topics.L3_AGENT, report_interval=cfg.CONF.AGENT.report_interval, manager=manager) service.launch(cfg.CONF, server).wait() neutron-12.1.1/neutron/agent/ovsdb/0000775000175000017500000000000013553660156017227 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/ovsdb/api.py0000664000175000017500000000341613553660047020355 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import uuid from oslo_config import cfg from oslo_utils import importutils from neutron.conf.agent import ovsdb_api ovsdb_api.register_ovsdb_api_opts() def from_config(context, iface_name=None): """Return the configured OVSDB API implementation""" iface = importutils.import_module( ovsdb_api.interface_map[iface_name or cfg.CONF.OVS.ovsdb_interface]) return iface.api_factory(context) def val_to_py(val): """Convert a json ovsdb return value to native python object""" if isinstance(val, collections.Sequence) and len(val) == 2: if val[0] == "uuid": return uuid.UUID(val[1]) elif val[0] == "set": return [val_to_py(x) for x in val[1]] elif val[0] == "map": return {val_to_py(x): val_to_py(y) for x, y in val[1]} return val def py_to_val(pyval): """Convert python value to ovs-vsctl value argument""" if isinstance(pyval, bool): return 'true' if pyval is True else 'false' elif pyval == '': return '""' else: # NOTE(twilson) If a Command object, return its record_id as a value return getattr(pyval, "record_id", pyval) neutron-12.1.1/neutron/agent/ovsdb/impl_vsctl.py0000664000175000017500000003071713553660047021764 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import itertools import uuid from oslo_log import log as logging from oslo_serialization import jsonutils from oslo_utils import excutils from oslo_utils import uuidutils from ovsdbapp import api as ovsdb_api import six from neutron.agent.common import utils from neutron.agent.ovsdb import api as ovsdb LOG = logging.getLogger(__name__) def api_factory(context): return OvsdbVsctl(context) class Transaction(ovsdb_api.Transaction): def __init__(self, context, check_error=False, log_errors=True, opts=None): self.context = context self.check_error = check_error self.log_errors = log_errors self.opts = ["--timeout=%d" % self.context.vsctl_timeout, '--oneline', '--format=json'] if opts: self.opts += opts self.commands = [] def add(self, command): self.commands.append(command) return command def commit(self): args = [] for cmd in self.commands: cmd.result = None args += cmd.vsctl_args() res = self.run_vsctl(args) if res is None: return res = res.replace(r'\\', '\\').splitlines() for i, record in enumerate(res): self.commands[i].result = record return [cmd.result for cmd in self.commands] def run_vsctl(self, args): full_args = ["ovs-vsctl"] + self.opts + args try: # We log our own errors, so never have utils.execute do it return utils.execute(full_args, run_as_root=True, log_fail_as_error=False).rstrip() except Exception as e: with excutils.save_and_reraise_exception() as ctxt: if self.log_errors: LOG.error("Unable to execute %(cmd)s. " "Exception: %(exception)s", {'cmd': full_args, 'exception': e}) if not self.check_error: ctxt.reraise = False class BaseCommand(ovsdb_api.Command): def __init__(self, context, cmd, opts=None, args=None): self.context = context self.cmd = cmd self.opts = [] if opts is None else opts self.args = [] if args is None else args def execute(self, check_error=False, log_errors=True): with Transaction(self.context, check_error=check_error, log_errors=log_errors) as txn: txn.add(self) return self.result def vsctl_args(self): return itertools.chain(('--',), self.opts, (self.cmd,), self.args) class MultiLineCommand(BaseCommand): """Command for ovs-vsctl commands that return multiple lines""" @property def result(self): return self._result @result.setter def result(self, raw_result): self._result = raw_result.split(r'\n') if raw_result else [] class DbCommand(BaseCommand): def __init__(self, context, cmd, opts=None, args=None, columns=None): if opts is None: opts = [] if columns: opts += ['--columns=%s' % ",".join(columns)] super(DbCommand, self).__init__(context, cmd, opts, args) @property def result(self): return self._result @result.setter def result(self, raw_result): # If check_error=False, run_vsctl can return None if not raw_result: self._result = None return try: json = jsonutils.loads(raw_result) except (ValueError, TypeError) as e: # This shouldn't happen, but if it does and we check_errors # log and raise. with excutils.save_and_reraise_exception(): LOG.error("Could not parse: %(raw_result)s. " "Exception: %(exception)s", {'raw_result': raw_result, 'exception': e}) headings = json['headings'] data = json['data'] results = [] for record in data: obj = {} for pos, heading in enumerate(headings): obj[heading] = ovsdb.val_to_py(record[pos]) results.append(obj) self._result = results class DbGetCommand(DbCommand): @DbCommand.result.setter def result(self, val): # super()'s never worked for setters http://bugs.python.org/issue14965 DbCommand.result.fset(self, val) # DbCommand will return [{'column': value}] and we just want value. if self._result: self._result = list(self._result[0].values())[0] class DbCreateCommand(BaseCommand): def __init__(self, context, opts=None, args=None): super(DbCreateCommand, self).__init__(context, "create", opts, args) # NOTE(twilson) pre-commit result used for intra-transaction reference self.record_id = "@%s" % uuidutils.generate_uuid() self.opts.append("--id=%s" % self.record_id) @property def result(self): return self._result @result.setter def result(self, val): self._result = uuid.UUID(val) if val else val class BrExistsCommand(DbCommand): @DbCommand.result.setter def result(self, val): self._result = val is not None def execute(self): return super(BrExistsCommand, self).execute(check_error=False, log_errors=False) class OvsdbVsctl(ovsdb_api.API): def __init__(self, context): super(OvsdbVsctl, self).__init__() self.context = context def create_transaction(self, check_error=False, log_errors=True, **kwargs): return Transaction(self.context, check_error, log_errors, **kwargs) def add_manager(self, connection_uri): # This will add a new manager without overriding existing ones. conn_uri = 'target="%s"' % connection_uri args = ['create', 'Manager', conn_uri, '--', 'add', 'Open_vSwitch', '.', 'manager_options', '@manager'] return BaseCommand(self.context, '--id=@manager', args=args) def get_manager(self): return MultiLineCommand(self.context, 'get-manager') def remove_manager(self, connection_uri): args = ['get', 'Manager', connection_uri, '--', 'remove', 'Open_vSwitch', '.', 'manager_options', '@manager'] return BaseCommand(self.context, '--id=@manager', args=args) def add_br(self, name, may_exist=True, datapath_type=None): opts = ['--may-exist'] if may_exist else None params = [name] if datapath_type: params += ['--', 'set', 'Bridge', name, 'datapath_type=%s' % datapath_type] return BaseCommand(self.context, 'add-br', opts, params) def del_br(self, name, if_exists=True): opts = ['--if-exists'] if if_exists else None return BaseCommand(self.context, 'del-br', opts, [name]) def br_exists(self, name): return BrExistsCommand(self.context, 'list', args=['Bridge', name]) def port_to_br(self, name): return BaseCommand(self.context, 'port-to-br', args=[name]) def iface_to_br(self, name): return BaseCommand(self.context, 'iface-to-br', args=[name]) def list_br(self): return MultiLineCommand(self.context, 'list-br') def br_get_external_id(self, name, field): return BaseCommand(self.context, 'br-get-external-id', args=[name, field]) def db_create(self, table, **col_values): args = [table] args += _set_colval_args(*col_values.items()) return DbCreateCommand(self.context, args=args) def db_destroy(self, table, record): args = [table, record] return BaseCommand(self.context, 'destroy', args=args) def db_set(self, table, record, *col_values): args = [table, record] args += _set_colval_args(*col_values) return BaseCommand(self.context, 'set', args=args) def db_add(self, table, record, column, *values): args = [table, record, column] for value in values: if isinstance(value, collections.Mapping): args += ["{}={}".format(ovsdb.py_to_val(k), ovsdb.py_to_val(v)) for k, v in value.items()] else: args.append(ovsdb.py_to_val(value)) return BaseCommand(self.context, 'add', args=args) def db_clear(self, table, record, column): return BaseCommand(self.context, 'clear', args=[table, record, column]) def db_get(self, table, record, column): # Use the 'list' command as it can return json and 'get' cannot so that # we can get real return types instead of treating everything as string # NOTE: openvswitch can return a single atomic value for fields that # are sets, but only have one value. This makes directly iterating over # the result of a db_get() call unsafe. return DbGetCommand(self.context, 'list', args=[table, record], columns=[column]) def db_list(self, table, records=None, columns=None, if_exists=False): opts = ['--if-exists'] if if_exists else None args = [table] if records: args += records return DbCommand(self.context, 'list', opts=opts, args=args, columns=columns) def db_find(self, table, *conditions, **kwargs): columns = kwargs.pop('columns', None) args = itertools.chain([table], *[_set_colval_args(c) for c in conditions]) return DbCommand(self.context, 'find', args=args, columns=columns) def set_controller(self, bridge, controllers): return BaseCommand(self.context, 'set-controller', args=[bridge] + list(controllers)) def del_controller(self, bridge): return BaseCommand(self.context, 'del-controller', args=[bridge]) def get_controller(self, bridge): return MultiLineCommand(self.context, 'get-controller', args=[bridge]) def set_fail_mode(self, bridge, mode): return BaseCommand(self.context, 'set-fail-mode', args=[bridge, mode]) def add_port(self, bridge, port, may_exist=True): opts = ['--may-exist'] if may_exist else None return BaseCommand(self.context, 'add-port', opts, [bridge, port]) def del_port(self, port, bridge=None, if_exists=True): opts = ['--if-exists'] if if_exists else None args = filter(None, [bridge, port]) return BaseCommand(self.context, 'del-port', opts, args) def list_ports(self, bridge): return MultiLineCommand(self.context, 'list-ports', args=[bridge]) def list_ifaces(self, bridge): return MultiLineCommand(self.context, 'list-ifaces', args=[bridge]) def db_remove(self, table, record, column, *values, **keyvalues): raise NotImplementedError() def db_find_rows(self, table, *conditions, **kwargs): raise NotImplementedError() def db_list_rows(self, table, record=None, if_exists=False): raise NotImplementedError() def _set_colval_args(*col_values): args = [] # TODO(twilson) This is ugly, but set/find args are very similar except for # op. Will try to find a better way to default this op to '=' for entry in col_values: if len(entry) == 2: col, op, val = entry[0], '=', entry[1] else: col, op, val = entry if isinstance(val, collections.Mapping): args += ["%s:%s%s%s" % ( col, k, op, ovsdb.py_to_val(v)) for k, v in val.items()] elif (isinstance(val, collections.Sequence) and not isinstance(val, six.string_types)): if len(val) == 0: args.append("%s%s%s" % (col, op, "[]")) else: args.append( "%s%s%s" % (col, op, ",".join(map(ovsdb.py_to_val, val)))) else: args.append("%s%s%s" % (col, op, ovsdb.py_to_val(val))) return args neutron-12.1.1/neutron/agent/ovsdb/__init__.py0000664000175000017500000000000013553660046021324 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/ovsdb/native/0000775000175000017500000000000013553660156020515 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/ovsdb/native/commands.py0000664000175000017500000000134613553660046022672 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ovsdbapp.schema.open_vswitch import commands from neutron.common import _deprecate _deprecate._MovedGlobals(commands) neutron-12.1.1/neutron/agent/ovsdb/native/exceptions.py0000664000175000017500000000204013553660046023242 0ustar zuulzuul00000000000000# Copyright 2018 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions as e from neutron._i18n import _ class OvsdbSslConfigNotFound(e.NeutronException): message = _("Specified SSL file %(ssl_file)s could not be found") class OvsdbSslRequiredOptError(e.NeutronException): message = _("Required 'ovs' group option %(ssl_opt)s not set. SSL " "configuration options are required when using SSL " "ovsdb_connection URI") neutron-12.1.1/neutron/agent/ovsdb/native/vlog.py0000664000175000017500000000132313553660046022033 0ustar zuulzuul00000000000000# Copyright (c) 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ovsdbapp.backend.ovs_idl import vlog from neutron.common import _deprecate _deprecate._MovedGlobals(vlog) neutron-12.1.1/neutron/agent/ovsdb/native/helpers.py0000664000175000017500000000204113553660047022525 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools from debtcollector import moves from ovsdbapp.schema.open_vswitch import helpers from neutron.agent.common import utils _connection_to_manager_uri = moves.moved_function( helpers._connection_to_manager_uri, '_connection_to_manager_uri', __name__) enable_connection_uri = functools.partial( helpers.enable_connection_uri, execute=utils.execute, run_as_root=True, log_fail_as_error=False, check_exit_code=False) neutron-12.1.1/neutron/agent/ovsdb/native/__init__.py0000664000175000017500000000000013553660046022612 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/ovsdb/native/connection.py0000664000175000017500000000530613553660047023231 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from debtcollector import moves from oslo_config import cfg from ovs.db import idl from ovs.stream import Stream from ovsdbapp.backend.ovs_idl import connection as _connection from ovsdbapp.backend.ovs_idl import idlutils import tenacity from neutron.agent.ovsdb.native import exceptions as ovsdb_exc from neutron.agent.ovsdb.native import helpers TransactionQueue = moves.moved_class(_connection.TransactionQueue, 'TransactionQueue', __name__) Connection = moves.moved_class(_connection.Connection, 'Connection', __name__) def configure_ssl_conn(): """ Configures required settings for an SSL based OVSDB client connection :return: None """ req_ssl_opts = {'ssl_key_file': cfg.CONF.OVS.ssl_key_file, 'ssl_cert_file': cfg.CONF.OVS.ssl_cert_file, 'ssl_ca_cert_file': cfg.CONF.OVS.ssl_ca_cert_file} for ssl_opt, ssl_file in req_ssl_opts.items(): if not ssl_file: raise ovsdb_exc.OvsdbSslRequiredOptError(ssl_opt=ssl_opt) elif not os.path.exists(ssl_file): raise ovsdb_exc.OvsdbSslConfigNotFound(ssl_file=ssl_file) # TODO(ihrachys): move to ovsdbapp Stream.ssl_set_private_key_file(req_ssl_opts['ssl_key_file']) Stream.ssl_set_certificate_file(req_ssl_opts['ssl_cert_file']) Stream.ssl_set_ca_cert_file(req_ssl_opts['ssl_ca_cert_file']) def idl_factory(): conn = cfg.CONF.OVS.ovsdb_connection schema_name = 'Open_vSwitch' if conn.startswith('ssl:'): configure_ssl_conn() try: helper = idlutils.get_schema_helper(conn, schema_name) except Exception: helpers.enable_connection_uri(conn) @tenacity.retry(wait=tenacity.wait_exponential(multiplier=0.01), stop=tenacity.stop_after_delay(1), reraise=True) def do_get_schema_helper(): return idlutils.get_schema_helper(conn, schema_name) helper = do_get_schema_helper() # TODO(twilson) We should still select only the tables/columns we use helper.register_all() return idl.Idl(conn, helper) neutron-12.1.1/neutron/agent/ovsdb/impl_idl.py0000664000175000017500000000606713553660047021402 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from debtcollector import moves from oslo_config import cfg from ovsdbapp.backend.ovs_idl import command from ovsdbapp.backend.ovs_idl import connection from ovsdbapp.backend.ovs_idl import idlutils from ovsdbapp.backend.ovs_idl import transaction from ovsdbapp.backend.ovs_idl import vlog from ovsdbapp.schema.open_vswitch import impl_idl from neutron.agent.ovsdb.native import connection as n_connection from neutron.conf.agent import ovs_conf from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants NeutronOVSDBTransaction = moves.moved_class( impl_idl.OvsVsctlTransaction, 'NeutronOVSDBTransaction', __name__) VswitchdInterfaceAddException = moves.moved_class( impl_idl.VswitchdInterfaceAddException, 'VswitchdInterfaceAddException', __name__) Transaction = moves.moved_class(transaction.Transaction, 'Transaction', __name__) ovs_conf.register_ovs_agent_opts() _connection = None def api_factory(context): global _connection if _connection is None: _connection = connection.Connection( idl=n_connection.idl_factory(), timeout=cfg.CONF.OVS.ovsdb_timeout) return NeutronOvsdbIdl(_connection) class OvsCleanup(command.BaseCommand): def __init__(self, api, bridge, all_ports=False): super(OvsCleanup, self).__init__(api) self.bridge = bridge self.all_ports = all_ports def run_idl(self, txn): br = idlutils.row_by_value(self.api.idl, 'Bridge', 'name', self.bridge) for port in br.ports: if not any(self.is_deletable_port(iface) for iface in port.interfaces): continue br.delvalue('ports', port) for iface in port.interfaces: iface.delete() port.delete() def is_deletable_port(self, port): # Deletable defined as "looks like vif port and not set to skip delete" if self.all_ports: return True if constants.SKIP_CLEANUP in port.external_ids: return False if not all(field in port.external_ids for field in ('iface-id', 'attached-mac')): return False return True class NeutronOvsdbIdl(impl_idl.OvsdbIdl): def __init__(self, connection): vlog.use_python_logger() super(NeutronOvsdbIdl, self).__init__(connection) def ovs_cleanup(self, bridges, all_ports=False): return OvsCleanup(self, bridges, all_ports) neutron-12.1.1/neutron/agent/dhcp/0000775000175000017500000000000013553660156017030 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/dhcp/agent.py0000664000175000017500000011316613553660047020507 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import os import eventlet from neutron_lib.agent import constants as agent_consts from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions from oslo_concurrency import lockutils from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_service import loopingcall from oslo_utils import fileutils from oslo_utils import importutils import six from neutron._i18n import _ from neutron.agent.common import resource_processing_queue as queue from neutron.agent.linux import dhcp from neutron.agent.linux import external_process from neutron.agent.metadata import driver as metadata_driver from neutron.agent import rpc as agent_rpc from neutron.common import constants as n_const from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.common import utils from neutron import manager LOG = logging.getLogger(__name__) _SYNC_STATE_LOCK = lockutils.ReaderWriterLock() DEFAULT_PRIORITY = 255 DHCP_READY_PORTS_SYNC_MAX = 64 def _sync_lock(f): """Decorator to block all operations for a global sync call.""" @six.wraps(f) def wrapped(*args, **kwargs): with _SYNC_STATE_LOCK.write_lock(): return f(*args, **kwargs) return wrapped def _wait_if_syncing(f): """Decorator to wait if any sync operations are in progress.""" @six.wraps(f) def wrapped(*args, **kwargs): with _SYNC_STATE_LOCK.read_lock(): return f(*args, **kwargs) return wrapped class DhcpAgent(manager.Manager): """DHCP agent service manager. Note that the public methods of this class are exposed as the server side of an rpc interface. The neutron server uses neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.DhcpAgentNotifyApi as the client side to execute the methods here. For more information about changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. """ target = oslo_messaging.Target(version='1.0') def __init__(self, host=None, conf=None): super(DhcpAgent, self).__init__(host=host) self.needs_resync_reasons = collections.defaultdict(list) self.dhcp_ready_ports = set() self.conf = conf or cfg.CONF self.cache = NetworkCache() self.dhcp_driver_cls = importutils.import_class(self.conf.dhcp_driver) self.plugin_rpc = DhcpPluginApi(topics.PLUGIN, self.conf.host) # create dhcp dir to store dhcp info dhcp_dir = os.path.dirname("/%s/dhcp/" % self.conf.state_path) fileutils.ensure_tree(dhcp_dir, mode=0o755) self.dhcp_version = self.dhcp_driver_cls.check_version() self._populate_networks_cache() # keep track of mappings between networks and routers for # metadata processing self._metadata_routers = {} # {network_id: router_id} self._process_monitor = external_process.ProcessMonitor( config=self.conf, resource_type='dhcp') self._queue = queue.ResourceProcessingQueue() def init_host(self): self.sync_state() def _populate_networks_cache(self): """Populate the networks cache when the DHCP-agent starts.""" try: existing_networks = self.dhcp_driver_cls.existing_dhcp_networks( self.conf ) for net_id in existing_networks: net = dhcp.NetModel({"id": net_id, "subnets": [], "non_local_subnets": [], "ports": []}) self.cache.put(net) except NotImplementedError: # just go ahead with an empty networks cache LOG.debug("The '%s' DHCP-driver does not support retrieving of a " "list of existing networks", self.conf.dhcp_driver) def after_start(self): self.run() LOG.info("DHCP agent started") def run(self): """Activate the DHCP agent.""" self.periodic_resync() self.start_ready_ports_loop() eventlet.spawn_n(self._process_loop) def call_driver(self, action, network, **action_kwargs): """Invoke an action on a DHCP driver instance.""" LOG.debug('Calling driver for network: %(net)s action: %(action)s', {'net': network.id, 'action': action}) try: # the Driver expects something that is duck typed similar to # the base models. driver = self.dhcp_driver_cls(self.conf, network, self._process_monitor, self.dhcp_version, self.plugin_rpc) getattr(driver, action)(**action_kwargs) return True except exceptions.Conflict: # No need to resync here, the agent will receive the event related # to a status update for the network LOG.debug('Unable to %(action)s dhcp for %(net_id)s: there ' 'is a conflict with its current state; please ' 'check that the network and/or its subnet(s) ' 'still exist.', {'net_id': network.id, 'action': action}) except exceptions.SubnetMismatchForPort as e: # FIXME(kevinbenton): get rid of this once bug/1627480 is fixed LOG.debug("Error configuring DHCP port, scheduling resync: %s", e) self.schedule_resync(e, network.id) except Exception as e: if getattr(e, 'exc_type', '') != 'IpAddressGenerationFailure': # Don't resync if port could not be created because of an IP # allocation failure. When the subnet is updated with a new # allocation pool or a port is deleted to free up an IP, this # will automatically be retried on the notification self.schedule_resync(e, network.id) if (isinstance(e, oslo_messaging.RemoteError) and e.exc_type == 'NetworkNotFound' or isinstance(e, exceptions.NetworkNotFound)): LOG.debug("Network %s has been removed from the agent " "or deleted from DB.", network.id) else: LOG.exception('Unable to %(action)s dhcp for %(net_id)s.', {'net_id': network.id, 'action': action}) def schedule_resync(self, reason, network_id=None): """Schedule a resync for a given network and reason. If no network is specified, resync all networks. """ self.needs_resync_reasons[network_id].append(reason) @_sync_lock def sync_state(self, networks=None): """Sync the local DHCP state with Neutron. If no networks are passed, or 'None' is one of the networks, sync all of the networks. """ only_nets = set([] if (not networks or None in networks) else networks) LOG.info('Synchronizing state') pool = eventlet.GreenPool(self.conf.num_sync_threads) known_network_ids = set(self.cache.get_network_ids()) try: active_networks = self.plugin_rpc.get_active_networks_info( enable_dhcp_filter=False) LOG.info('All active networks have been fetched through RPC.') active_network_ids = set(network.id for network in active_networks) for deleted_id in known_network_ids - active_network_ids: try: self.disable_dhcp_helper(deleted_id) except Exception as e: self.schedule_resync(e, deleted_id) LOG.exception('Unable to sync network state on ' 'deleted network %s', deleted_id) for network in active_networks: if (not only_nets or # specifically resync all network.id not in known_network_ids or # missing net network.id in only_nets): # specific network to sync pool.spawn(self.safe_configure_dhcp_for_network, network) pool.waitall() # we notify all ports in case some were created while the agent # was down self.dhcp_ready_ports |= set(self.cache.get_port_ids(only_nets)) LOG.info('Synchronizing state complete') except Exception as e: if only_nets: for network_id in only_nets: self.schedule_resync(e, network_id) else: self.schedule_resync(e) LOG.exception('Unable to sync network state.') def _dhcp_ready_ports_loop(self): """Notifies the server of any ports that had reservations setup.""" while True: # this is just watching a set so we can do it really frequently eventlet.sleep(0.1) if self.dhcp_ready_ports: ports_to_send = set() for port_count in range(min(len(self.dhcp_ready_ports), DHCP_READY_PORTS_SYNC_MAX)): ports_to_send.add(self.dhcp_ready_ports.pop()) try: self.plugin_rpc.dhcp_ready_on_ports(ports_to_send) LOG.info("DHCP configuration for ports %s is completed", ports_to_send) continue except oslo_messaging.MessagingTimeout: LOG.error("Timeout notifying server of ports ready. " "Retrying...") except Exception: LOG.exception("Failure notifying DHCP server of " "ready DHCP ports. Will retry on next " "iteration.") self.dhcp_ready_ports |= ports_to_send def start_ready_ports_loop(self): """Spawn a thread to push changed ports to server.""" eventlet.spawn(self._dhcp_ready_ports_loop) @utils.exception_logger() def _periodic_resync_helper(self): """Resync the dhcp state at the configured interval.""" while True: eventlet.sleep(self.conf.resync_interval) if self.needs_resync_reasons: # be careful to avoid a race with additions to list # from other threads reasons = self.needs_resync_reasons self.needs_resync_reasons = collections.defaultdict(list) for net, r in reasons.items(): if not net: net = "*" LOG.debug("resync (%(network)s): %(reason)s", {"reason": r, "network": net}) self.sync_state(reasons.keys()) def periodic_resync(self): """Spawn a thread to periodically resync the dhcp state.""" eventlet.spawn(self._periodic_resync_helper) def safe_get_network_info(self, network_id): try: network = self.plugin_rpc.get_network_info(network_id) if not network: LOG.debug('Network %s has been deleted.', network_id) return network except Exception as e: self.schedule_resync(e, network_id) LOG.exception('Network %s info call failed.', network_id) def enable_dhcp_helper(self, network_id): """Enable DHCP for a network that meets enabling criteria.""" network = self.safe_get_network_info(network_id) if network: self.configure_dhcp_for_network(network) @utils.exception_logger() def safe_configure_dhcp_for_network(self, network): try: network_id = network.get('id') LOG.info('Starting network %s dhcp configuration', network_id) self.configure_dhcp_for_network(network) LOG.info('Finished network %s dhcp configuration', network_id) except (exceptions.NetworkNotFound, RuntimeError): LOG.warning('Network %s may have been deleted and ' 'its resources may have already been disposed.', network.id) def configure_dhcp_for_network(self, network): if not network.admin_state_up: return for subnet in network.subnets: if subnet.enable_dhcp: if self.call_driver('enable', network): self.update_isolated_metadata_proxy(network) self.cache.put(network) # After enabling dhcp for network, mark all existing # ports as ready. So that the status of ports which are # created before enabling dhcp can be updated. self.dhcp_ready_ports |= {p.id for p in network.ports} break def disable_dhcp_helper(self, network_id): """Disable DHCP for a network known to the agent.""" network = self.cache.get_network_by_id(network_id) if network: # NOTE(yamahata): Kill the metadata proxy process # unconditionally, as in the case where a network # is deleted, all the subnets and ports are deleted # before this function is called, so determining if # the proxy should be terminated is error prone. # destroy_monitored_metadata_proxy() is a noop when # there is no process running. self.disable_isolated_metadata_proxy(network) if self.call_driver('disable', network): self.cache.remove(network) def refresh_dhcp_helper(self, network_id): """Refresh or disable DHCP for a network depending on the current state of the network. """ old_network = self.cache.get_network_by_id(network_id) if not old_network: # DHCP current not running for network. return self.enable_dhcp_helper(network_id) network = self.safe_get_network_info(network_id) if not network: return if not any(s for s in network.subnets if s.enable_dhcp): self.disable_dhcp_helper(network.id) return old_non_local_subnets = getattr(old_network, 'non_local_subnets', []) new_non_local_subnets = getattr(network, 'non_local_subnets', []) old_cidrs = [s.cidr for s in (old_network.subnets + old_non_local_subnets) if s.enable_dhcp] new_cidrs = [s.cidr for s in (network.subnets + new_non_local_subnets) if s.enable_dhcp] if old_cidrs == new_cidrs: self.call_driver('reload_allocations', network) self.cache.put(network) elif self.call_driver('restart', network): self.cache.put(network) # mark all ports as active in case the sync included # new ports that we hadn't seen yet. self.dhcp_ready_ports |= {p.id for p in network.ports} # Update the metadata proxy after the dhcp driver has been updated self.update_isolated_metadata_proxy(network) def network_create_end(self, context, payload): """Handle the network.create.end notification event.""" update = queue.ResourceUpdate(payload['network']['id'], payload.get('priority', DEFAULT_PRIORITY), action='_network_create', resource=payload) self._queue.add(update) @_wait_if_syncing def _network_create(self, payload): network_id = payload['network']['id'] self.enable_dhcp_helper(network_id) def network_update_end(self, context, payload): """Handle the network.update.end notification event.""" update = queue.ResourceUpdate(payload['network']['id'], payload.get('priority', DEFAULT_PRIORITY), action='_network_update', resource=payload) self._queue.add(update) @_wait_if_syncing def _network_update(self, payload): network_id = payload['network']['id'] if payload['network']['admin_state_up']: self.enable_dhcp_helper(network_id) else: self.disable_dhcp_helper(network_id) def network_delete_end(self, context, payload): """Handle the network.delete.end notification event.""" update = queue.ResourceUpdate(payload['network_id'], payload.get('priority', DEFAULT_PRIORITY), action='_network_delete', resource=payload) self._queue.add(update) @_wait_if_syncing def _network_delete(self, payload): network_id = payload['network_id'] self.disable_dhcp_helper(network_id) def subnet_update_end(self, context, payload): """Handle the subnet.update.end notification event.""" update = queue.ResourceUpdate(payload['subnet']['network_id'], payload.get('priority', DEFAULT_PRIORITY), action='_subnet_update', resource=payload) self._queue.add(update) @_wait_if_syncing def _subnet_update(self, payload): network_id = payload['subnet']['network_id'] self.refresh_dhcp_helper(network_id) # Use the update handler for the subnet create event. subnet_create_end = subnet_update_end def _get_network_lock_id(self, payload): """Determine which lock to hold when servicing an RPC event""" # TODO(alegacy): in a future release this function can be removed and # uses of it can be replaced with payload['network_id']. It exists # only to satisfy backwards compatibility between older servers and # newer agents. Once the 'network_id' attribute is guaranteed to be # sent by the server on all *_delete_end events then it can be removed. if 'network_id' in payload: return payload['network_id'] elif 'subnet_id' in payload: subnet_id = payload['subnet_id'] network = self.cache.get_network_by_subnet_id(subnet_id) return network.id if network else None elif 'port_id' in payload: port_id = payload['port_id'] port = self.cache.get_port_by_id(port_id) return port.network_id if port else None def subnet_delete_end(self, context, payload): """Handle the subnet.delete.end notification event.""" network_id = self._get_network_lock_id(payload) if not network_id: return update = queue.ResourceUpdate(network_id, payload.get('priority', DEFAULT_PRIORITY), action='_subnet_delete', resource=payload) self._queue.add(update) @_wait_if_syncing def _subnet_delete(self, payload): network_id = self._get_network_lock_id(payload) if not network_id: return subnet_id = payload['subnet_id'] network = self.cache.get_network_by_subnet_id(subnet_id) if not network: return self.refresh_dhcp_helper(network.id) def _process_loop(self): LOG.debug("Starting _process_loop") pool = eventlet.GreenPool(size=8) while True: pool.spawn_n(self._process_resource_update) def _process_resource_update(self): for tmp, update in self._queue.each_update_to_next_resource(): method = getattr(self, update.action) method(update.resource) def port_update_end(self, context, payload): """Handle the port.update.end notification event.""" updated_port = dhcp.DictModel(payload['port']) if self.cache.is_port_message_stale(updated_port): LOG.debug("Discarding stale port update: %s", updated_port) return update = queue.ResourceUpdate(updated_port.network_id, payload.get('priority', DEFAULT_PRIORITY), action='_port_update', resource=updated_port) self._queue.add(update) @_wait_if_syncing def _port_update(self, updated_port): if self.cache.is_port_message_stale(updated_port): LOG.debug("Discarding stale port update: %s", updated_port) return network = self.cache.get_network_by_id(updated_port.network_id) if not network: return self.reload_allocations(updated_port, network) def reload_allocations(self, port, network): LOG.info("Trigger reload_allocations for port %s", port) driver_action = 'reload_allocations' if self._is_port_on_this_agent(port): orig = self.cache.get_port_by_id(port['id']) # assume IP change if not in cache orig = orig or {'fixed_ips': []} old_ips = {i['ip_address'] for i in orig['fixed_ips'] or []} new_ips = {i['ip_address'] for i in port['fixed_ips']} old_subs = {i['subnet_id'] for i in orig['fixed_ips'] or []} new_subs = {i['subnet_id'] for i in port['fixed_ips']} if new_subs != old_subs: # subnets being serviced by port have changed, this could # indicate a subnet_delete is in progress. schedule a # resync rather than an immediate restart so we don't # attempt to re-allocate IPs at the same time the server # is deleting them. self.schedule_resync("Agent port was modified", port.network_id) return elif old_ips != new_ips: LOG.debug("Agent IPs on network %s changed from %s to %s", network.id, old_ips, new_ips) driver_action = 'restart' self.cache.put_port(port) self.call_driver(driver_action, network) self.dhcp_ready_ports.add(port.id) self.update_isolated_metadata_proxy(network) def _is_port_on_this_agent(self, port): thishost = utils.get_dhcp_agent_device_id( port['network_id'], self.conf.host) return port['device_id'] == thishost def port_create_end(self, context, payload): """Handle the port.create.end notification event.""" created_port = dhcp.DictModel(payload['port']) update = queue.ResourceUpdate(created_port.network_id, payload.get('priority', DEFAULT_PRIORITY), action='_port_create', resource=created_port) self._queue.add(update) @_wait_if_syncing def _port_create(self, created_port): network = self.cache.get_network_by_id(created_port.network_id) if not network: return new_ips = {i['ip_address'] for i in created_port['fixed_ips']} for port_cached in network.ports: # if in the same network there are ports cached with the same # ip address but different MAC address and/or different id, # this indicate that the cache is out of sync cached_ips = {i['ip_address'] for i in port_cached['fixed_ips']} if (new_ips.intersection(cached_ips) and (created_port['id'] != port_cached['id'] or created_port['mac_address'] != port_cached['mac_address'])): self.schedule_resync("Duplicate IP addresses found, " "DHCP cache is out of sync", created_port.network_id) return self.reload_allocations(created_port, network) def port_delete_end(self, context, payload): """Handle the port.delete.end notification event.""" network_id = self._get_network_lock_id(payload) if not network_id: return update = queue.ResourceUpdate(network_id, payload.get('priority', DEFAULT_PRIORITY), action='_port_delete', resource=payload) self._queue.add(update) @_wait_if_syncing def _port_delete(self, payload): network_id = self._get_network_lock_id(payload) if not network_id: return port_id = payload['port_id'] port = self.cache.get_port_by_id(port_id) self.cache.deleted_ports.add(port_id) if not port: return network = self.cache.get_network_by_id(port.network_id) self.cache.remove_port(port) if self._is_port_on_this_agent(port): # the agent's port has been deleted. disable the service # and add the network to the resync list to create # (or acquire a reserved) port. self.call_driver('disable', network) self.schedule_resync("Agent port was deleted", port.network_id) else: self.call_driver('reload_allocations', network) self.update_isolated_metadata_proxy(network) def update_isolated_metadata_proxy(self, network): """Spawn or kill metadata proxy. According to return from driver class, spawn or kill the metadata proxy process. Spawn an existing metadata proxy or kill a nonexistent metadata proxy will just silently return. """ should_enable_metadata = self.dhcp_driver_cls.should_enable_metadata( self.conf, network) if should_enable_metadata: self.enable_isolated_metadata_proxy(network) else: self.disable_isolated_metadata_proxy(network) def enable_isolated_metadata_proxy(self, network): # The proxy might work for either a single network # or all the networks connected via a router # to the one passed as a parameter kwargs = {'network_id': network.id} # When the metadata network is enabled, the proxy might # be started for the router attached to the network if self.conf.enable_metadata_network: router_ports = [port for port in network.ports if (port.device_owner in constants.ROUTER_INTERFACE_OWNERS)] if router_ports: # Multiple router ports should not be allowed if len(router_ports) > 1: LOG.warning("%(port_num)d router ports found on the " "metadata access network. Only the port " "%(port_id)s, for router %(router_id)s " "will be considered", {'port_num': len(router_ports), 'port_id': router_ports[0].id, 'router_id': router_ports[0].device_id}) all_subnets = self.dhcp_driver_cls._get_all_subnets(network) if self.dhcp_driver_cls.has_metadata_subnet(all_subnets): kwargs = {'router_id': router_ports[0].device_id} self._metadata_routers[network.id] = ( router_ports[0].device_id) metadata_driver.MetadataDriver.spawn_monitored_metadata_proxy( self._process_monitor, network.namespace, dhcp.METADATA_PORT, self.conf, bind_address=dhcp.METADATA_DEFAULT_IP, **kwargs) def disable_isolated_metadata_proxy(self, network): if (self.conf.enable_metadata_network and network.id in self._metadata_routers): uuid = self._metadata_routers[network.id] is_router_id = True else: uuid = network.id is_router_id = False metadata_driver.MetadataDriver.destroy_monitored_metadata_proxy( self._process_monitor, uuid, self.conf, network.namespace) if is_router_id: del self._metadata_routers[network.id] class DhcpPluginApi(object): """Agent side of the dhcp rpc API. This class implements the client side of an rpc interface. The server side of this interface can be found in neutron.api.rpc.handlers.dhcp_rpc.DhcpRpcCallback. For more information about changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. API version history: 1.0 - Initial version. 1.1 - Added get_active_networks_info, create_dhcp_port, and update_dhcp_port methods. 1.5 - Added dhcp_ready_on_ports """ def __init__(self, topic, host): self.host = host target = oslo_messaging.Target( topic=topic, namespace=n_const.RPC_NAMESPACE_DHCP_PLUGIN, version='1.0') self.client = n_rpc.get_client(target) @property def context(self): # TODO(kevinbenton): the context should really be passed in to each of # these methods so a call can be tracked all of the way through the # system but that will require a larger refactor to pass the context # everywhere. We just generate a new one here on each call so requests # can be independently tracked server side. return context.get_admin_context_without_session() def get_active_networks_info(self, **kwargs): """Make a remote process call to retrieve all network info.""" cctxt = self.client.prepare(version='1.1') networks = cctxt.call(self.context, 'get_active_networks_info', host=self.host, **kwargs) return [dhcp.NetModel(n) for n in networks] def get_network_info(self, network_id): """Make a remote process call to retrieve network info.""" cctxt = self.client.prepare() network = cctxt.call(self.context, 'get_network_info', network_id=network_id, host=self.host) if network: return dhcp.NetModel(network) def create_dhcp_port(self, port): """Make a remote process call to create the dhcp port.""" cctxt = self.client.prepare(version='1.1') port = cctxt.call(self.context, 'create_dhcp_port', port=port, host=self.host) if port: return dhcp.DictModel(port) def update_dhcp_port(self, port_id, port): """Make a remote process call to update the dhcp port.""" cctxt = self.client.prepare(version='1.1') port = cctxt.call(self.context, 'update_dhcp_port', port_id=port_id, port=port, host=self.host) if port: return dhcp.DictModel(port) def release_dhcp_port(self, network_id, device_id): """Make a remote process call to release the dhcp port.""" cctxt = self.client.prepare() return cctxt.call(self.context, 'release_dhcp_port', network_id=network_id, device_id=device_id, host=self.host) def dhcp_ready_on_ports(self, port_ids): """Notify the server that DHCP is configured for the port.""" cctxt = self.client.prepare(version='1.5') return cctxt.call(self.context, 'dhcp_ready_on_ports', port_ids=port_ids) class NetworkCache(object): """Agent cache of the current network state.""" def __init__(self): self.cache = {} self.subnet_lookup = {} self.port_lookup = {} self.deleted_ports = set() def is_port_message_stale(self, payload): orig = self.get_port_by_id(payload['id']) or {} if orig.get('revision_number', 0) > payload.get('revision_number', 0): return True if payload['id'] in self.deleted_ports: return True return False def get_port_ids(self, network_ids=None): if not network_ids: return self.port_lookup.keys() return (p_id for p_id, net in self.port_lookup.items() if net in network_ids) def get_network_ids(self): return self.cache.keys() def get_network_by_id(self, network_id): return self.cache.get(network_id) def get_network_by_subnet_id(self, subnet_id): return self.cache.get(self.subnet_lookup.get(subnet_id)) def get_network_by_port_id(self, port_id): return self.cache.get(self.port_lookup.get(port_id)) def put(self, network): if network.id in self.cache: self.remove(self.cache[network.id]) self.cache[network.id] = network non_local_subnets = getattr(network, 'non_local_subnets', []) for subnet in (network.subnets + non_local_subnets): self.subnet_lookup[subnet.id] = network.id for port in network.ports: self.port_lookup[port.id] = network.id def remove(self, network): del self.cache[network.id] non_local_subnets = getattr(network, 'non_local_subnets', []) for subnet in (network.subnets + non_local_subnets): del self.subnet_lookup[subnet.id] for port in network.ports: del self.port_lookup[port.id] def put_port(self, port): network = self.get_network_by_id(port.network_id) for index in range(len(network.ports)): if network.ports[index].id == port.id: network.ports[index] = port break else: network.ports.append(port) self.port_lookup[port.id] = network.id def remove_port(self, port): network = self.get_network_by_port_id(port.id) for index in range(len(network.ports)): if network.ports[index] == port: del network.ports[index] del self.port_lookup[port.id] break def get_port_by_id(self, port_id): network = self.get_network_by_port_id(port_id) if network: for port in network.ports: if port.id == port_id: return port def get_state(self): net_ids = self.get_network_ids() num_nets = len(net_ids) num_subnets = 0 num_ports = 0 for net_id in net_ids: network = self.get_network_by_id(net_id) non_local_subnets = getattr(network, 'non_local_subnets', []) num_subnets += len(network.subnets) num_subnets += len(non_local_subnets) num_ports += len(network.ports) return {'networks': num_nets, 'subnets': num_subnets, 'ports': num_ports} class DhcpAgentWithStateReport(DhcpAgent): def __init__(self, host=None, conf=None): super(DhcpAgentWithStateReport, self).__init__(host=host, conf=conf) self.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS) self.agent_state = { 'binary': 'neutron-dhcp-agent', 'host': host, 'availability_zone': self.conf.AGENT.availability_zone, 'topic': topics.DHCP_AGENT, 'configurations': { 'dhcp_driver': self.conf.dhcp_driver, 'dhcp_lease_duration': self.conf.dhcp_lease_duration, 'log_agent_heartbeats': self.conf.AGENT.log_agent_heartbeats}, 'start_flag': True, 'agent_type': constants.AGENT_TYPE_DHCP} report_interval = self.conf.AGENT.report_interval if report_interval: self.heartbeat = loopingcall.FixedIntervalLoopingCall( self._report_state) self.heartbeat.start(interval=report_interval) def _report_state(self): try: self.agent_state.get('configurations').update( self.cache.get_state()) ctx = context.get_admin_context_without_session() agent_status = self.state_rpc.report_state( ctx, self.agent_state, True) if agent_status == agent_consts.AGENT_REVIVED: LOG.info("Agent has just been revived. " "Scheduling full sync") self.schedule_resync("Agent has just been revived") except AttributeError: # This means the server does not support report_state LOG.warning("Neutron server does not support state report. " "State report for this agent will be disabled.") self.heartbeat.stop() self.run() return except Exception: LOG.exception("Failed reporting state!") return if self.agent_state.pop('start_flag', None): self.run() def agent_updated(self, context, payload): """Handle the agent_updated notification event.""" self.schedule_resync(_("Agent updated: %(payload)s") % {"payload": payload}) LOG.info("agent_updated by server side %s!", payload) def after_start(self): LOG.info("DHCP agent started") neutron-12.1.1/neutron/agent/dhcp/__init__.py0000664000175000017500000000000013553660046021125 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/common/0000775000175000017500000000000013553660156017402 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/common/utils.py0000664000175000017500000000400113553660047021106 0ustar zuulzuul00000000000000# Copyright 2015 Cloudbase Solutions. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from neutron_lib.utils import runtime from oslo_config import cfg from oslo_log import log as logging from oslo_utils import timeutils from neutron.conf.agent import common as config from neutron.conf.agent.database import agents_db if os.name == 'nt': from neutron.agent.windows import utils else: from neutron.agent.linux import utils LOG = logging.getLogger(__name__) config.register_root_helper(cfg.CONF) agents_db.register_db_agents_opts() INTERFACE_NAMESPACE = 'neutron.interface_drivers' create_process = utils.create_process kill_process = utils.kill_process execute = utils.execute get_root_helper_child_pid = utils.get_root_helper_child_pid pid_invoked_with_cmdline = utils.pid_invoked_with_cmdline def load_interface_driver(conf): """Load interface driver for agents like DHCP or L3 agent. :param conf: driver configuration object :raises SystemExit of 1 if driver cannot be loaded """ try: loaded_class = runtime.load_class_by_alias_or_classname( INTERFACE_NAMESPACE, conf.interface_driver) return loaded_class(conf) except ImportError: LOG.error("Error loading interface driver '%s'", conf.interface_driver) raise SystemExit(1) def is_agent_down(heart_beat_time): return timeutils.is_older_than(heart_beat_time, cfg.CONF.agent_down_time) neutron-12.1.1/neutron/agent/common/resource_processing_queue.py0000664000175000017500000001471213553660047025247 0ustar zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import datetime from oslo_utils import timeutils from six.moves import queue as Queue # Lower value is higher priority PRIORITY_RELATED_ROUTER = 0 PRIORITY_RPC = 1 PRIORITY_SYNC_ROUTERS_TASK = 2 PRIORITY_PD_UPDATE = 3 # Actions DELETE_ROUTER = 1 DELETE_RELATED_ROUTER = 2 ADD_UPDATE_ROUTER = 3 ADD_UPDATE_RELATED_ROUTER = 4 PD_UPDATE = 5 RELATED_ACTION_MAP = {DELETE_ROUTER: DELETE_RELATED_ROUTER, ADD_UPDATE_ROUTER: ADD_UPDATE_RELATED_ROUTER} class ResourceUpdate(object): """Encapsulates a resource update An instance of this object carries the information necessary to prioritize and process a request to update a resource. Priority values are ordered from higher (0) to lower (>0) by the caller, and are therefore not defined here, but must be done by the consumer. """ def __init__(self, id, priority, action=None, resource=None, timestamp=None, tries=5): self.priority = priority self.timestamp = timestamp if not timestamp: self.timestamp = timeutils.utcnow() self.id = id self.action = action self.resource = resource self.tries = tries def __lt__(self, other): """Implements priority among updates Lower numerical priority always gets precedence. When comparing two updates of the same priority then the one with the earlier timestamp gets precedence. In the unlikely event that the timestamps are also equal it falls back to a simple comparison of ids meaning the precedence is essentially random. """ if self.priority != other.priority: return self.priority < other.priority if self.timestamp != other.timestamp: return self.timestamp < other.timestamp return self.id < other.id def hit_retry_limit(self): return self.tries < 0 class ExclusiveResourceProcessor(object): """Manager for access to a resource for processing This class controls access to a resource in a non-blocking way. The first instance to be created for a given ID is granted exclusive access to the resource. Other instances may be created for the same ID while the first instance has exclusive access. If that happens then it doesn't block and wait for access. Instead, it signals to the master instance that an update came in with the timestamp. This way, a thread will not block to wait for access to a resource. Instead it effectively signals to the thread that is working on the resource that something has changed since it started working on it. That thread will simply finish its current iteration and then repeat. This class keeps track of the last time that resource data was fetched and processed. The timestamp that it keeps must be before when the data used to process the resource last was fetched from the database. But, as close as possible. The timestamp should not be recorded, however, until the resource has been processed using the fetch data. """ _masters = {} _resource_timestamps = {} def __init__(self, id): self._id = id if id not in self._masters: self._masters[id] = self self._queue = Queue.PriorityQueue(-1) self._master = self._masters[id] def _i_am_master(self): return self == self._master def __enter__(self): return self def __exit__(self, type, value, traceback): if self._i_am_master(): del self._masters[self._id] def _get_resource_data_timestamp(self): return self._resource_timestamps.get(self._id, datetime.datetime.min) def fetched_and_processed(self, timestamp): """Records the timestamp after it is used to update the resource""" new_timestamp = max(timestamp, self._get_resource_data_timestamp()) self._resource_timestamps[self._id] = new_timestamp def queue_update(self, update): """Queues an update from a worker This is the queue used to keep new updates that come in while a resource is being processed. These updates have already bubbled to the front of the ResourceProcessingQueue. """ self._master._queue.put(update) def updates(self): """Processes the resource until updates stop coming Only the master instance will process the resource. However, updates may come in from other workers while it is in progress. This method loops until they stop coming. """ while self._i_am_master(): if self._queue.empty(): return # Get the update from the queue even if it is old. update = self._queue.get() # Process the update only if it is fresh. if self._get_resource_data_timestamp() < update.timestamp: yield update class ResourceProcessingQueue(object): """Manager of the queue of resources to process.""" def __init__(self): self._queue = Queue.PriorityQueue() def add(self, update): update.tries -= 1 self._queue.put(update) def each_update_to_next_resource(self): """Grabs the next resource from the queue and processes This method uses a for loop to process the resource repeatedly until updates stop bubbling to the front of the queue. """ next_update = self._queue.get() with ExclusiveResourceProcessor(next_update.id) as rp: # Queue the update whether this worker is the master or not. rp.queue_update(next_update) # Here, if the current worker is not the master, the call to # rp.updates() will not yield and so this will essentially be a # noop. for update in rp.updates(): yield (rp, update) neutron-12.1.1/neutron/agent/common/ovs_lib.py0000664000175000017500000012304013553660047021410 0ustar zuulzuul00000000000000# Copyright 2011 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import itertools import operator import random import time import uuid from neutron_lib import constants as p_const from neutron_lib import exceptions from oslo_config import cfg from oslo_log import log as logging import six import tenacity from neutron._i18n import _ from neutron.agent.common import ip_lib from neutron.agent.common import utils from neutron.agent.ovsdb import api as ovsdb_api from neutron.common import constants as common_constants from neutron.common import utils as common_utils from neutron.conf.agent import ovs_conf from neutron.plugins.ml2.drivers.openvswitch.agent.common \ import constants UINT64_BITMASK = (1 << 64) - 1 # Special return value for an invalid OVS ofport INVALID_OFPORT = -1 UNASSIGNED_OFPORT = [] # OVS bridge fail modes FAILMODE_SECURE = 'secure' FAILMODE_STANDALONE = 'standalone' # special values for cookies COOKIE_ANY = object() ovs_conf.register_ovs_agent_opts() LOG = logging.getLogger(__name__) OVS_DEFAULT_CAPS = { 'datapath_types': [], 'iface_types': [], } # It's default queue, all packets not tagged with 'set_queue' will go through # this one QOS_DEFAULT_QUEUE = 0 _SENTINEL = object() CTRL_RATE_LIMIT_MIN = 100 CTRL_BURST_LIMIT_MIN = 25 def _ovsdb_result_pending(result): """Return True if ovs-vsctl indicates the result is still pending.""" # ovs-vsctl can return '[]' for an ofport that has not yet been assigned return result == [] def _ovsdb_retry(fn): """Decorator for retrying when OVS has yet to assign an ofport. The instance's vsctl_timeout is used as the max waiting time. This relies on the fact that instance methods receive self as the first argument. """ @six.wraps(fn) def wrapped(*args, **kwargs): self = args[0] new_fn = tenacity.retry( reraise=True, retry=tenacity.retry_if_result(_ovsdb_result_pending), wait=tenacity.wait_exponential(multiplier=0.01, max=1), stop=tenacity.stop_after_delay( self.vsctl_timeout))(fn) return new_fn(*args, **kwargs) return wrapped class VifPort(object): def __init__(self, port_name, ofport, vif_id, vif_mac, switch): self.port_name = port_name self.ofport = ofport self.vif_id = vif_id self.vif_mac = vif_mac self.switch = switch def __str__(self): return ("iface-id=%s, vif_mac=%s, port_name=%s, ofport=%s, " "bridge_name=%s") % ( self.vif_id, self.vif_mac, self.port_name, self.ofport, self.switch.br_name) class BaseOVS(object): def __init__(self): self.vsctl_timeout = cfg.CONF.OVS.ovsdb_timeout self.ovsdb = ovsdb_api.from_config(self) def add_manager(self, connection_uri, timeout=_SENTINEL): """Have ovsdb-server listen for manager connections :param connection_uri: Manager target string :param timeout: The Manager probe_interval timeout value (defaults to ovsdb_timeout) """ if timeout is _SENTINEL: timeout = cfg.CONF.OVS.ovsdb_timeout with self.ovsdb.transaction() as txn: txn.add(self.ovsdb.add_manager(connection_uri)) if timeout: txn.add( self.ovsdb.db_set('Manager', connection_uri, ('inactivity_probe', timeout * 1000))) def get_manager(self): return self.ovsdb.get_manager().execute() def remove_manager(self, connection_uri): self.ovsdb.remove_manager(connection_uri).execute() def add_bridge(self, bridge_name, datapath_type=constants.OVS_DATAPATH_SYSTEM): br = OVSBridge(bridge_name, datapath_type=datapath_type) br.create() return br def delete_bridge(self, bridge_name): self.ovsdb.del_br(bridge_name).execute() def bridge_exists(self, bridge_name): return self.ovsdb.br_exists(bridge_name).execute() def port_exists(self, port_name): cmd = self.ovsdb.db_get('Port', port_name, 'name') return bool(cmd.execute(check_error=False, log_errors=False)) def get_bridge_for_iface(self, iface): return self.ovsdb.iface_to_br(iface).execute() def get_bridges(self): return self.ovsdb.list_br().execute(check_error=True) def get_bridge_external_bridge_id(self, bridge, check_error=False, log_errors=True): return self.ovsdb.br_get_external_id(bridge, 'bridge-id').execute( check_error=check_error, log_errors=log_errors) def set_db_attribute(self, table_name, record, column, value, check_error=False, log_errors=True): self.ovsdb.db_set(table_name, record, (column, value)).execute( check_error=check_error, log_errors=log_errors) def clear_db_attribute(self, table_name, record, column): self.ovsdb.db_clear(table_name, record, column).execute() def db_get_val(self, table, record, column, check_error=False, log_errors=True): return self.ovsdb.db_get(table, record, column).execute( check_error=check_error, log_errors=log_errors) @property def config(self): """A dict containing the only row from the root Open_vSwitch table This row contains several columns describing the Open vSwitch install and the system on which it is installed. Useful keys include: datapath_types: a list of supported datapath types iface_types: a list of supported interface types ovs_version: the OVS version """ return self.ovsdb.db_list("Open_vSwitch").execute()[0] @property def capabilities(self): _cfg = self.config return {k: _cfg.get(k, OVS_DEFAULT_CAPS[k]) for k in OVS_DEFAULT_CAPS} # Map from version string to on-the-wire protocol version encoding: OF_PROTOCOL_TO_VERSION = { constants.OPENFLOW10: 1, constants.OPENFLOW11: 2, constants.OPENFLOW12: 3, constants.OPENFLOW13: 4, constants.OPENFLOW14: 5 } def version_from_protocol(protocol): if protocol not in OF_PROTOCOL_TO_VERSION: raise Exception("unknown OVS protocol string, cannot compare: %s, " "(known: %s)" % (protocol, list(OF_PROTOCOL_TO_VERSION))) return OF_PROTOCOL_TO_VERSION[protocol] class OVSBridge(BaseOVS): def __init__(self, br_name, datapath_type=constants.OVS_DATAPATH_SYSTEM): super(OVSBridge, self).__init__() self.br_name = br_name self.datapath_type = datapath_type self._default_cookie = generate_random_cookie() self._highest_protocol_needed = constants.OPENFLOW10 @property def default_cookie(self): return self._default_cookie def set_agent_uuid_stamp(self, val): self._default_cookie = val def set_controller(self, controllers): self.ovsdb.set_controller(self.br_name, controllers).execute(check_error=True) def del_controller(self): self.ovsdb.del_controller(self.br_name).execute(check_error=True) def get_controller(self): return self.ovsdb.get_controller(self.br_name).execute( check_error=True) def _set_bridge_fail_mode(self, mode): self.ovsdb.set_fail_mode(self.br_name, mode).execute(check_error=True) def set_secure_mode(self): self._set_bridge_fail_mode(FAILMODE_SECURE) def set_standalone_mode(self): self._set_bridge_fail_mode(FAILMODE_STANDALONE) def add_protocols(self, *protocols): self.ovsdb.db_add('Bridge', self.br_name, 'protocols', *protocols).execute(check_error=True) def use_at_least_protocol(self, protocol): """Calls to ovs-ofctl will use a protocol version >= 'protocol'""" self.add_protocols(protocol) self._highest_protocol_needed = max(self._highest_protocol_needed, protocol, key=version_from_protocol) def create(self, secure_mode=False): other_config = { 'mac-table-size': str(cfg.CONF.OVS.bridge_mac_table_size)} with self.ovsdb.transaction() as txn: txn.add( self.ovsdb.add_br(self.br_name, datapath_type=self.datapath_type)) # the ovs-ofctl commands below in run_ofctl use OF10, so we # need to ensure that this version is enabled ; we could reuse # add_protocols, but doing ovsdb.db_add avoids doing two # transactions txn.add( self.ovsdb.db_add('Bridge', self.br_name, 'protocols', self._highest_protocol_needed)) txn.add( self.ovsdb.db_set('Bridge', self.br_name, ('other_config', other_config))) if secure_mode: txn.add(self.ovsdb.set_fail_mode(self.br_name, FAILMODE_SECURE)) def destroy(self): self.delete_bridge(self.br_name) def add_port(self, port_name, *interface_attr_tuples): with self.ovsdb.transaction() as txn: txn.add(self.ovsdb.add_port(self.br_name, port_name)) if interface_attr_tuples: txn.add(self.ovsdb.db_set('Interface', port_name, *interface_attr_tuples)) return self.get_port_ofport(port_name) def replace_port(self, port_name, *interface_attr_tuples): """Replace existing port or create it, and configure port interface.""" # NOTE(xiaohhui): If del_port is inside the transaction, there will # only be one command for replace_port. This will cause the new port # not be found by system, which will lead to Bug #1519926. self.ovsdb.del_port(port_name).execute() with self.ovsdb.transaction() as txn: txn.add(self.ovsdb.add_port(self.br_name, port_name, may_exist=False)) # NOTE(mangelajo): Port is added to dead vlan (4095) by default # until it's handled by the neutron-openvswitch-agent. Otherwise it # becomes a trunk port on br-int (receiving traffic for all vlans), # and also triggers issues on ovs-vswitchd related to the # datapath flow revalidator thread, see lp#1767422 txn.add(self.ovsdb.db_set( 'Port', port_name, ('tag', constants.DEAD_VLAN_TAG))) # TODO(mangelajo): We could accept attr tuples for the Port too # but, that could potentially break usage of this function in # stable branches (where we need to backport). # https://review.openstack.org/#/c/564825/4/neutron/agent/common/ # ovs_lib.py@289 if interface_attr_tuples: txn.add(self.ovsdb.db_set('Interface', port_name, *interface_attr_tuples)) def delete_port(self, port_name): self.ovsdb.del_port(port_name, self.br_name).execute() def run_ofctl(self, cmd, args, process_input=None): full_args = ["ovs-ofctl", cmd, "-O", self._highest_protocol_needed, self.br_name] + args # TODO(kevinbenton): This error handling is really brittle and only # detects one specific type of failure. The callers of this need to # be refactored to expect errors so we can re-raise and they can # take appropriate action based on the type of error. for i in range(1, 11): try: return utils.execute(full_args, run_as_root=True, process_input=process_input) except Exception as e: if "failed to connect to socket" in str(e): LOG.debug("Failed to connect to OVS. Retrying " "in 1 second. Attempt: %s/10", i) time.sleep(1) continue LOG.error("Unable to execute %(cmd)s. Exception: " "%(exception)s", {'cmd': full_args, 'exception': e}) break def count_flows(self): flow_list = self.run_ofctl("dump-flows", []).split("\n")[1:] return len(flow_list) - 1 def remove_all_flows(self): self.run_ofctl("del-flows", []) @_ovsdb_retry def _get_port_val(self, port_name, port_val): return self.db_get_val("Interface", port_name, port_val) def get_port_ofport(self, port_name): """Get the port's assigned ofport, retrying if not yet assigned.""" ofport = INVALID_OFPORT try: ofport = self._get_port_val(port_name, "ofport") except tenacity.RetryError: LOG.exception("Timed out retrieving ofport on port %s.", port_name) return ofport def get_port_external_ids(self, port_name): """Get the port's assigned ofport, retrying if not yet assigned.""" port_external_ids = dict() try: port_external_ids = self._get_port_val(port_name, "external_ids") except tenacity.RetryError: LOG.exception("Timed out retrieving external_ids on port %s.", port_name) return port_external_ids def get_port_mac(self, port_name): """Get the port's mac address. This is especially useful when the port is not a neutron port. E.g. networking-sfc needs the MAC address of "patch-tun """ return self.db_get_val("Interface", port_name, "mac_in_use") @_ovsdb_retry def _get_datapath_id(self): return self.db_get_val('Bridge', self.br_name, 'datapath_id') def get_datapath_id(self): try: return self._get_datapath_id() except tenacity.RetryError: # if ovs fails to find datapath_id then something is likely to be # broken here LOG.exception("Timed out retrieving datapath_id on bridge %s.", self.br_name) raise RuntimeError('No datapath_id on bridge %s' % self.br_name) def do_action_flows(self, action, kwargs_list, use_bundle=False): # we can't mix strict and non-strict, so we'll use the first kw # and check against other kw being different strict = kwargs_list[0].get('strict', False) for kw in kwargs_list: if action is 'del': if kw.get('cookie') == COOKIE_ANY: # special value COOKIE_ANY was provided, unset # cookie to match flows whatever their cookie is kw.pop('cookie') if kw.get('cookie_mask'): # non-zero cookie mask raise Exception("cookie=COOKIE_ANY but cookie_mask " "set to %s" % kw.get('cookie_mask')) elif 'cookie' in kw: # a cookie was specified, use it kw['cookie'] = check_cookie_mask(kw['cookie']) else: # nothing was specified about cookies, use default kw['cookie'] = "%d/-1" % self._default_cookie else: if 'cookie' not in kw: kw['cookie'] = self._default_cookie if action in ('mod', 'del'): if kw.pop('strict', False) != strict: msg = ("cannot mix 'strict' and not 'strict' in a batch " "call") raise exceptions.InvalidInput(error_message=msg) else: if kw.pop('strict', False): msg = "cannot use 'strict' with 'add' action" raise exceptions.InvalidInput(error_message=msg) extra_param = ["--strict"] if strict else [] if action == 'del' and {} in kwargs_list: # the 'del' case simplifies itself if kwargs_list has at least # one item that matches everything self.run_ofctl('%s-flows' % action, []) else: flow_strs = [_build_flow_expr_str(kw, action, strict) for kw in kwargs_list] LOG.debug("Processing %d OpenFlow rules.", len(flow_strs)) if use_bundle: extra_param.append('--bundle') step = common_constants.AGENT_RES_PROCESSING_STEP for i in range(0, len(flow_strs), step): self.run_ofctl('%s-flows' % action, extra_param + ['-'], '\n'.join(flow_strs[i:i + step])) def add_flow(self, **kwargs): self.do_action_flows('add', [kwargs]) def mod_flow(self, **kwargs): self.do_action_flows('mod', [kwargs]) def delete_flows(self, **kwargs): self.do_action_flows('del', [kwargs]) def dump_flows_for_table(self, table): return self.dump_flows_for(table=table) def dump_flows_for(self, **kwargs): retval = None if "cookie" in kwargs: kwargs["cookie"] = check_cookie_mask(str(kwargs["cookie"])) flow_str = ",".join("=".join([key, str(val)]) for key, val in kwargs.items()) flows = self.run_ofctl("dump-flows", [flow_str]) if flows: retval = '\n'.join(item for item in flows.splitlines() if is_a_flow_line(item)) return retval def dump_all_flows(self): return [f for f in self.run_ofctl("dump-flows", []).splitlines() if is_a_flow_line(f)] def deferred(self, **kwargs): return DeferredOVSBridge(self, **kwargs) def add_tunnel_port(self, port_name, remote_ip, local_ip, tunnel_type=p_const.TYPE_GRE, vxlan_udp_port=p_const.VXLAN_UDP_PORT, dont_fragment=True, tunnel_csum=False, tos=None): attrs = [('type', tunnel_type)] # TODO(twilson) This is an OrderedDict solely to make a test happy options = collections.OrderedDict() vxlan_uses_custom_udp_port = ( tunnel_type == p_const.TYPE_VXLAN and vxlan_udp_port != p_const.VXLAN_UDP_PORT ) if vxlan_uses_custom_udp_port: options['dst_port'] = str(vxlan_udp_port) options['df_default'] = str(dont_fragment).lower() options['remote_ip'] = remote_ip options['local_ip'] = local_ip options['in_key'] = 'flow' options['out_key'] = 'flow' options['egress_pkt_mark'] = '0' if tunnel_csum: options['csum'] = str(tunnel_csum).lower() if tos: options['tos'] = str(tos) attrs.append(('options', options)) return self.add_port(port_name, *attrs) def add_patch_port(self, local_name, remote_name): attrs = [('type', 'patch'), ('options', {'peer': remote_name})] return self.add_port(local_name, *attrs) def get_iface_name_list(self): # get the interface name list for this bridge return self.ovsdb.list_ifaces(self.br_name).execute(check_error=True) def get_port_name_list(self): # get the port name list for this bridge return self.ovsdb.list_ports(self.br_name).execute(check_error=True) def get_port_stats(self, port_name): return self.db_get_val("Interface", port_name, "statistics") def get_ports_attributes(self, table, columns=None, ports=None, check_error=True, log_errors=True, if_exists=False): port_names = ports or self.get_port_name_list() if not port_names: return [] return (self.ovsdb.db_list(table, port_names, columns=columns, if_exists=if_exists). execute(check_error=check_error, log_errors=log_errors)) # returns a VIF object for each VIF port def get_vif_ports(self, ofport_filter=None): edge_ports = [] port_info = self.get_ports_attributes( 'Interface', columns=['name', 'external_ids', 'ofport'], if_exists=True) for port in port_info: name = port['name'] external_ids = port['external_ids'] ofport = port['ofport'] if ofport_filter and ofport in ofport_filter: continue if "iface-id" in external_ids and "attached-mac" in external_ids: p = VifPort(name, ofport, external_ids["iface-id"], external_ids["attached-mac"], self) edge_ports.append(p) return edge_ports def get_vif_port_to_ofport_map(self): results = self.get_ports_attributes( 'Interface', columns=['name', 'external_ids', 'ofport'], if_exists=True) port_map = {} for r in results: # fall back to basic interface name key = self.portid_from_external_ids(r['external_ids']) or r['name'] try: port_map[key] = int(r['ofport']) except TypeError: # port doesn't yet have an ofport entry so we ignore it pass return port_map def get_vif_port_set(self): edge_ports = set() results = self.get_ports_attributes( 'Interface', columns=['name', 'external_ids', 'ofport'], if_exists=True) for result in results: if result['ofport'] == UNASSIGNED_OFPORT: LOG.warning("Found not yet ready openvswitch port: %s", result['name']) elif result['ofport'] == INVALID_OFPORT: LOG.warning("Found failed openvswitch port: %s", result['name']) elif 'attached-mac' in result['external_ids']: port_id = self.portid_from_external_ids(result['external_ids']) if port_id: edge_ports.add(port_id) return edge_ports def portid_from_external_ids(self, external_ids): if 'iface-id' in external_ids: return external_ids['iface-id'] def get_port_tag_dict(self): """Get a dict of port names and associated vlan tags. e.g. the returned dict is of the following form:: {u'int-br-eth2': [], u'patch-tun': [], u'qr-76d9e6b6-21': 1, u'tapce5318ff-78': 1, u'tape1400310-e6': 1} The TAG ID is only available in the "Port" table and is not available in the "Interface" table queried by the get_vif_port_set() method. """ results = self.get_ports_attributes( 'Port', columns=['name', 'tag'], if_exists=True) return {p['name']: p['tag'] for p in results} def get_vifs_by_ids(self, port_ids): interface_info = self.get_ports_attributes( "Interface", columns=["name", "external_ids", "ofport"], if_exists=True) by_id = {x['external_ids'].get('iface-id'): x for x in interface_info} result = {} for port_id in port_ids: result[port_id] = None if port_id not in by_id: LOG.info("Port %(port_id)s not present in bridge " "%(br_name)s", {'port_id': port_id, 'br_name': self.br_name}) continue pinfo = by_id[port_id] if not self._check_ofport(port_id, pinfo): continue mac = pinfo['external_ids'].get('attached-mac') result[port_id] = VifPort(pinfo['name'], pinfo['ofport'], port_id, mac, self) return result @staticmethod def _check_ofport(port_id, port_info): if port_info['ofport'] in [UNASSIGNED_OFPORT, INVALID_OFPORT]: LOG.warning("ofport: %(ofport)s for VIF: %(vif)s " "is not a positive integer", {'ofport': port_info['ofport'], 'vif': port_id}) return False return True def get_vif_port_by_id(self, port_id): ports = self.ovsdb.db_find( 'Interface', ('external_ids', '=', {'iface-id': port_id}), ('external_ids', '!=', {'attached-mac': ''}), columns=['external_ids', 'name', 'ofport']).execute() for port in ports: if self.br_name != self.get_bridge_for_iface(port['name']): continue if not self._check_ofport(port_id, port): continue mac = port['external_ids'].get('attached-mac') return VifPort(port['name'], port['ofport'], port_id, mac, self) LOG.info("Port %(port_id)s not present in bridge %(br_name)s", {'port_id': port_id, 'br_name': self.br_name}) def delete_ports(self, all_ports=False): if all_ports: port_names = self.get_port_name_list() else: port_names = (port.port_name for port in self.get_vif_ports()) for port_name in port_names: self.delete_port(port_name) def get_local_port_mac(self): """Retrieve the mac of the bridge's local port.""" address = ip_lib.IPDevice(self.br_name).link.address if address: return address else: msg = _('Unable to determine mac address for %s') % self.br_name raise Exception(msg) def set_controllers_connection_mode(self, connection_mode): """Set bridge controllers connection mode. :param connection_mode: "out-of-band" or "in-band" """ self.set_controller_field('connection_mode', connection_mode) def set_controllers_inactivity_probe(self, interval): """Set bridge controllers inactivity probe interval. :param interval: inactivity_probe value in seconds. """ self.set_controller_field('inactivity_probe', interval * 1000) def _set_egress_bw_limit_for_port(self, port_name, max_kbps, max_burst_kbps): with self.ovsdb.transaction(check_error=True) as txn: txn.add(self.ovsdb.db_set('Interface', port_name, ('ingress_policing_rate', max_kbps))) txn.add(self.ovsdb.db_set('Interface', port_name, ('ingress_policing_burst', max_burst_kbps))) def create_egress_bw_limit_for_port(self, port_name, max_kbps, max_burst_kbps): self._set_egress_bw_limit_for_port( port_name, max_kbps, max_burst_kbps) def get_egress_bw_limit_for_port(self, port_name): max_kbps = self.db_get_val('Interface', port_name, 'ingress_policing_rate') max_burst_kbps = self.db_get_val('Interface', port_name, 'ingress_policing_burst') max_kbps = max_kbps or None max_burst_kbps = max_burst_kbps or None return max_kbps, max_burst_kbps def delete_egress_bw_limit_for_port(self, port_name): if not self.port_exists(port_name): return self._set_egress_bw_limit_for_port( port_name, 0, 0) def find_qos(self, port_name): qos = self.ovsdb.db_find( 'QoS', ('external_ids', '=', {'id': port_name}), columns=['_uuid', 'other_config']).execute(check_error=True) if qos: return qos[0] def find_queue(self, port_name, queue_type): queues = self.ovsdb.db_find( 'Queue', ('external_ids', '=', {'id': port_name, 'queue_type': str(queue_type)}), columns=['_uuid', 'other_config']).execute(check_error=True) if queues: return queues[0] def _update_bw_limit_queue(self, txn, port_name, queue_uuid, queue_type, other_config): if queue_uuid: txn.add(self.ovsdb.db_set( 'Queue', queue_uuid, ('other_config', other_config))) else: external_ids = {'id': port_name, 'queue_type': str(queue_type)} queue_uuid = txn.add( self.ovsdb.db_create( 'Queue', external_ids=external_ids, other_config=other_config)) return queue_uuid def _update_bw_limit_profile(self, txn, port_name, qos_uuid, queue_uuid, queue_type, qos_other_config): queues = {queue_type: queue_uuid} if qos_uuid: txn.add(self.ovsdb.db_set( 'QoS', qos_uuid, ('queues', queues))) txn.add(self.ovsdb.db_set( 'QoS', qos_uuid, ('other_config', qos_other_config))) else: external_ids = {'id': port_name} qos_uuid = txn.add( self.ovsdb.db_create( 'QoS', external_ids=external_ids, type='linux-htb', queues=queues, other_config=qos_other_config)) return qos_uuid def _update_bw_limit_profile_dpdk(self, txn, port_name, qos_uuid, other_config): if qos_uuid: txn.add(self.ovsdb.db_set( 'QoS', qos_uuid, ('other_config', other_config))) else: external_ids = {'id': port_name} qos_uuid = txn.add( self.ovsdb.db_create( 'QoS', external_ids=external_ids, type='egress-policer', other_config=other_config)) return qos_uuid def _update_ingress_bw_limit_for_port( self, port_name, max_bw_in_bits, max_burst_in_bits): qos_other_config = { 'max-rate': str(max_bw_in_bits) } queue_other_config = { 'max-rate': str(max_bw_in_bits), 'burst': str(max_burst_in_bits), } qos = self.find_qos(port_name) queue = self.find_queue(port_name, QOS_DEFAULT_QUEUE) qos_uuid = qos['_uuid'] if qos else None queue_uuid = queue['_uuid'] if queue else None with self.ovsdb.transaction(check_error=True) as txn: queue_uuid = self._update_bw_limit_queue( txn, port_name, queue_uuid, QOS_DEFAULT_QUEUE, queue_other_config ) qos_uuid = self._update_bw_limit_profile( txn, port_name, qos_uuid, queue_uuid, QOS_DEFAULT_QUEUE, qos_other_config ) txn.add(self.ovsdb.db_set( 'Port', port_name, ('qos', qos_uuid))) def _update_ingress_bw_limit_for_dpdk_port( self, port_name, max_bw_in_bits, max_burst_in_bits): # cir and cbs should be set in bytes instead of bits qos_other_config = { 'cir': str(max_bw_in_bits / 8), 'cbs': str(max_burst_in_bits / 8) } qos = self.find_qos(port_name) qos_uuid = qos['_uuid'] if qos else None with self.ovsdb.transaction(check_error=True) as txn: qos_uuid = self._update_bw_limit_profile_dpdk( txn, port_name, qos_uuid, qos_other_config) txn.add(self.ovsdb.db_set( 'Port', port_name, ('qos', qos_uuid))) def update_ingress_bw_limit_for_port(self, port_name, max_kbps, max_burst_kbps): max_bw_in_bits = max_kbps * common_constants.SI_BASE max_burst_in_bits = max_burst_kbps * common_constants.SI_BASE port_type = self._get_port_val(port_name, "type") if port_type in constants.OVS_DPDK_PORT_TYPES: self._update_ingress_bw_limit_for_dpdk_port( port_name, max_bw_in_bits, max_burst_in_bits) else: self._update_ingress_bw_limit_for_port( port_name, max_bw_in_bits, max_burst_in_bits) def get_ingress_bw_limit_for_port(self, port_name): max_kbps = None qos_max_kbps = None queue_max_kbps = None max_burst_kbit = None qos_res = self.find_qos(port_name) if qos_res: other_config = qos_res['other_config'] max_bw_in_bits = other_config.get('max-rate') if max_bw_in_bits is not None: qos_max_kbps = int(max_bw_in_bits) / common_constants.SI_BASE queue_res = self.find_queue(port_name, QOS_DEFAULT_QUEUE) if queue_res: other_config = queue_res['other_config'] max_bw_in_bits = other_config.get('max-rate') if max_bw_in_bits is not None: queue_max_kbps = int(max_bw_in_bits) / common_constants.SI_BASE max_burst_in_bits = other_config.get('burst') if max_burst_in_bits is not None: max_burst_kbit = ( int(max_burst_in_bits) / common_constants.SI_BASE) if qos_max_kbps == queue_max_kbps: max_kbps = qos_max_kbps else: LOG.warning("qos max-rate %(qos_max_kbps)s is not equal to " "queue max-rate %(queue_max_kbps)s", {'qos_max_kbps': qos_max_kbps, 'queue_max_kbps': queue_max_kbps}) return max_kbps, max_burst_kbit def get_ingress_bw_limit_for_dpdk_port(self, port_name): max_kbps = None max_burst_kbit = None res = self.find_qos(port_name) if res: other_config = res['other_config'] max_bw_in_bytes = other_config.get("cir") if max_bw_in_bytes is not None: max_kbps = common_utils.bits_to_kilobits( common_utils.bytes_to_bits(int(max_bw_in_bytes)), common_constants.SI_BASE) max_burst_in_bytes = other_config.get("cbs") if max_burst_in_bytes is not None: max_burst_kbit = common_utils.bits_to_kilobits( common_utils.bytes_to_bits(int(max_burst_in_bytes)), common_constants.SI_BASE) return max_kbps, max_burst_kbit def delete_ingress_bw_limit_for_port(self, port_name): qos = self.find_qos(port_name) queue = self.find_queue(port_name, QOS_DEFAULT_QUEUE) does_port_exist = self.port_exists(port_name) with self.ovsdb.transaction(check_error=True) as txn: if does_port_exist: txn.add(self.ovsdb.db_clear("Port", port_name, 'qos')) if qos: txn.add(self.ovsdb.db_destroy('QoS', qos['_uuid'])) if queue: txn.add(self.ovsdb.db_destroy('Queue', queue['_uuid'])) def set_controller_field(self, field, value): attr = [(field, value)] controllers = self.db_get_val('Bridge', self.br_name, 'controller') controllers = [controllers] if isinstance( controllers, uuid.UUID) else controllers with self.ovsdb.transaction(check_error=True) as txn: for controller_uuid in controllers: txn.add(self.ovsdb.db_set( 'Controller', controller_uuid, *attr)) def set_controller_rate_limit(self, controller_rate_limit): """Set bridge controller_rate_limit :param controller_rate_limit: at least 100 """ if controller_rate_limit < CTRL_RATE_LIMIT_MIN: LOG.info("rate limit's value must be at least 100") controller_rate_limit = CTRL_RATE_LIMIT_MIN self.set_controller_field( 'controller_rate_limit', controller_rate_limit) def set_controller_burst_limit(self, controller_burst_limit): """Set bridge controller_burst_limit :param controller_burst_limit: at least 25 """ if controller_burst_limit < CTRL_BURST_LIMIT_MIN: LOG.info("burst limit's value must be at least 25") controller_burst_limit = CTRL_BURST_LIMIT_MIN self.set_controller_field( 'controller_burst_limit', controller_burst_limit) def set_datapath_id(self, datapath_id): dpid_cfg = {'datapath-id': datapath_id} self.set_db_attribute('Bridge', self.br_name, 'other_config', dpid_cfg, check_error=True) def __enter__(self): self.create() return self def __exit__(self, exc_type, exc_value, exc_tb): self.destroy() class DeferredOVSBridge(object): '''Deferred OVSBridge. This class wraps add_flow, mod_flow and delete_flows calls to an OVSBridge and defers their application until apply_flows call in order to perform bulk calls. It wraps also ALLOWED_PASSTHROUGHS calls to avoid mixing OVSBridge and DeferredOVSBridge uses. This class can be used as a context, in such case apply_flows is called on __exit__ except if an exception is raised. This class is not thread-safe, that's why for every use a new instance must be implemented. ''' ALLOWED_PASSTHROUGHS = 'add_port', 'add_tunnel_port', 'delete_port' def __init__(self, br, full_ordered=False, order=('add', 'mod', 'del'), use_bundle=False): '''Constructor. :param br: wrapped bridge :param full_ordered: Optional, disable flow reordering (slower) :param order: Optional, define in which order flow are applied :param use_bundle: Optional, a bool whether --bundle should be passed to all ofctl commands. Default is set to False. ''' self.br = br self.full_ordered = full_ordered self.order = order if not self.full_ordered: self.weights = dict((y, x) for x, y in enumerate(self.order)) self.action_flow_tuples = [] self.use_bundle = use_bundle def __getattr__(self, name): if name in self.ALLOWED_PASSTHROUGHS: return getattr(self.br, name) raise AttributeError(name) def add_flow(self, **kwargs): self.action_flow_tuples.append(('add', kwargs)) def mod_flow(self, **kwargs): self.action_flow_tuples.append(('mod', kwargs)) def delete_flows(self, **kwargs): self.action_flow_tuples.append(('del', kwargs)) def apply_flows(self): action_flow_tuples = self.action_flow_tuples self.action_flow_tuples = [] if not action_flow_tuples: return if not self.full_ordered: action_flow_tuples.sort(key=lambda af: self.weights[af[0]]) grouped = itertools.groupby(action_flow_tuples, key=operator.itemgetter(0)) itemgetter_1 = operator.itemgetter(1) for action, action_flow_list in grouped: flows = list(map(itemgetter_1, action_flow_list)) self.br.do_action_flows(action, flows, self.use_bundle) def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): if exc_type is None: self.apply_flows() else: LOG.exception("OVS flows could not be applied on bridge %s", self.br.br_name) def _build_flow_expr_str(flow_dict, cmd, strict): flow_expr_arr = [] actions = None if cmd == 'add': flow_expr_arr.append("hard_timeout=%s" % flow_dict.pop('hard_timeout', '0')) flow_expr_arr.append("idle_timeout=%s" % flow_dict.pop('idle_timeout', '0')) flow_expr_arr.append("priority=%s" % flow_dict.pop('priority', '1')) elif 'priority' in flow_dict: if not strict: msg = _("Cannot match priority on flow deletion or modification " "without 'strict'") raise exceptions.InvalidInput(error_message=msg) if cmd != 'del': if "actions" not in flow_dict: msg = _("Must specify one or more actions on flow addition" " or modification") raise exceptions.InvalidInput(error_message=msg) actions = "actions=%s" % flow_dict.pop('actions') for key, value in flow_dict.items(): if key == 'proto': flow_expr_arr.append(value) else: flow_expr_arr.append("%s=%s" % (key, str(value))) if actions: flow_expr_arr.append(actions) return ','.join(flow_expr_arr) def generate_random_cookie(): # The OpenFlow spec forbids use of -1 return random.randrange(UINT64_BITMASK) def check_cookie_mask(cookie): cookie = str(cookie) if '/' not in cookie: return cookie + '/-1' else: return cookie def is_a_flow_line(line): # this is used to filter out from ovs-ofctl dump-flows the lines that # are not flow descriptions but mere indications of the type of openflow # message that was used ; e.g.: # # # ovs-ofctl dump-flows br-int # NXST_FLOW reply (xid=0x4): # cookie=0xb7dff131a697c6a5, duration=2411726.809s, table=0, ... # cookie=0xb7dff131a697c6a5, duration=2411726.786s, table=23, ... # cookie=0xb7dff131a697c6a5, duration=2411726.760s, table=24, ... # return 'NXST' not in line and 'OFPST' not in line neutron-12.1.1/neutron/agent/common/__init__.py0000664000175000017500000000000013553660046021477 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/common/polling.py0000664000175000017500000000155513553660047021425 0ustar zuulzuul00000000000000# Copyright 2015 Cloudbase Solutions. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os if os.name == 'nt': from neutron.agent.windows import polling else: from neutron.agent.linux import polling get_polling_manager = polling.get_polling_manager InterfacePollingMinimizer = polling.InterfacePollingMinimizer neutron-12.1.1/neutron/agent/common/base_polling.py0000664000175000017500000000366213553660046022417 0ustar zuulzuul00000000000000# Copyright 2015 Cloudbase Solutions. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. class BasePollingManager(object): def __init__(self): self._force_polling = False self._polling_completed = True def force_polling(self): self._force_polling = True def polling_completed(self): self._polling_completed = True def _is_polling_required(self): raise NotImplementedError() @property def is_polling_required(self): # Always consume the updates to minimize polling. polling_required = self._is_polling_required() # Polling is required regardless of whether updates have been # detected. if self._force_polling: self._force_polling = False polling_required = True # Polling is required if not yet done for previously detected # updates. if not self._polling_completed: polling_required = True if polling_required: # Track whether polling has been completed to ensure that # polling can be required until the caller indicates via a # call to polling_completed() that polling has been # successfully performed. self._polling_completed = False return polling_required class AlwaysPoll(BasePollingManager): @property def is_polling_required(self): return True neutron-12.1.1/neutron/agent/common/ip_lib.py0000664000175000017500000000200513553660046021205 0ustar zuulzuul00000000000000# Copyright 2016 Cloudbase Solutions. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os if os.name == 'nt': from neutron.agent.windows import ip_lib from neutron.conf.agent import windows OPTS = windows.IP_LIB_OPTS_WINDOWS else: from neutron.agent.linux import ip_lib from neutron.conf.agent import linux OPTS = linux.IP_LIB_OPTS_LINUX IPWrapper = ip_lib.IPWrapper IPDevice = ip_lib.IPDevice add_namespace_to_cmd = ip_lib.add_namespace_to_cmd neutron-12.1.1/neutron/agent/l2/0000775000175000017500000000000013553660156016427 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/l2/agent_extension.py0000664000175000017500000000173313553660047022176 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mellanox Technologies, Ltd # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import six from neutron.agent.l2 import l2_agent_extension @six.add_metaclass(abc.ABCMeta) class AgentCoreResourceExtension(l2_agent_extension.L2AgentExtension): """This is a shim around L2AgentExtension class. It is intended for use by out of tree extensions that were inheriting AgentCoreResourceExtension. """ neutron-12.1.1/neutron/agent/l2/l2_agent_extensions_manager.py0000664000175000017500000000440713553660047024451 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log from neutron.agent import agent_extensions_manager as agent_ext_manager from neutron.conf.agent import agent_extensions_manager as agent_ext_mgr_config LOG = log.getLogger(__name__) L2_AGENT_EXT_MANAGER_NAMESPACE = 'neutron.agent.l2.extensions' def register_opts(conf): agent_ext_mgr_config.register_agent_ext_manager_opts(conf) class L2AgentExtensionsManager(agent_ext_manager.AgentExtensionsManager): """Manage l2 agent extensions. The handle_port and delete_port methods are guaranteed to be attributes of each extension because they have been marked as abc.abstractmethod in the extensions' abstract class. """ def __init__(self, conf): super(L2AgentExtensionsManager, self).__init__(conf, L2_AGENT_EXT_MANAGER_NAMESPACE) def handle_port(self, context, data): """Notify all agent extensions to handle port.""" for extension in self: if hasattr(extension.obj, 'handle_port'): extension.obj.handle_port(context, data) else: LOG.error( "Agent Extension '%(name)s' does not " "implement method handle_port", {'name': extension.name} ) def delete_port(self, context, data): """Notify all agent extensions to delete port.""" for extension in self: if hasattr(extension.obj, 'delete_port'): extension.obj.delete_port(context, data) else: LOG.error( "Agent Extension '%(name)s' does not " "implement method delete_port", {'name': extension.name} ) neutron-12.1.1/neutron/agent/l2/l2_agent_extension.py0000664000175000017500000000275313553660047022576 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import six from neutron.agent import agent_extension @six.add_metaclass(abc.ABCMeta) class L2AgentExtension(agent_extension.AgentExtension): """Define stable abstract interface for l2 agent extensions. An agent extension extends the agent core functionality. """ def initialize(self, connection, driver_type): """Initialize agent extension.""" @abc.abstractmethod def handle_port(self, context, data): """Handle agent extension for port. This can be called on either create or update, depending on the code flow. Thus, it's this function's responsibility to check what actually changed. :param context: rpc context :param data: port data """ @abc.abstractmethod def delete_port(self, context, data): """Delete port from agent extension. :param context: rpc context :param data: port data """ neutron-12.1.1/neutron/agent/l2/extensions/0000775000175000017500000000000013553660156020626 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/l2/extensions/qos.py0000664000175000017500000003007413553660047022005 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mellanox Technologies, Ltd # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import collections from neutron_lib import constants from neutron_lib.services.qos import constants as qos_consts from oslo_concurrency import lockutils from oslo_log import log as logging import six from neutron.agent.l2 import l2_agent_extension from neutron.api.rpc.callbacks.consumer import registry from neutron.api.rpc.callbacks import events from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc from neutron import manager LOG = logging.getLogger(__name__) @six.add_metaclass(abc.ABCMeta) class QosAgentDriver(object): """Defines stable abstract interface for QoS Agent Driver. QoS Agent driver defines the interface to be implemented by Agent for applying QoS Rules on a port. """ # Each QoS driver should define the set of rule types that it supports, and # corresponding handlers that has the following names: # # create_ # update_ # delete_ # # where is one of VALID_RULE_TYPES # There is exception from this rule for deletion of rules with # attribute direction set to ingress (e.g. bandwidth limit rule). # For deletion of such rule types delete handler has following name: # delete__ingress @abc.abstractmethod def initialize(self): """Perform QoS agent driver initialization. """ def create(self, port, qos_policy): """Apply QoS rules on port for the first time. :param port: port object. :param qos_policy: the QoS policy to be applied on port. """ self._handle_update_create_rules('create', port, qos_policy) def consume_api(self, agent_api): """Consume the AgentAPI instance from the QoSAgentExtension class This allows QosAgentDrivers to gain access to resources limited to the NeutronAgent when this method is overridden. :param agent_api: An instance of an agent specific API """ def update(self, port, qos_policy): """Apply QoS rules on port. :param port: port object. :param qos_policy: the QoS policy to be applied on port. """ self._handle_update_create_rules('update', port, qos_policy) def delete(self, port, qos_policy=None): """Remove QoS rules from port. :param port: port object. :param qos_policy: the QoS policy to be removed from port. """ if qos_policy is None: for rule_type in self.SUPPORTED_RULES: self._handle_rule_delete(port, rule_type) if self._rule_type_has_ingress_direction(rule_type): self._handle_rule_delete(port, rule_type, ingress=True) else: for rule in self._iterate_rules(qos_policy.rules): self._handle_rule_delete( port, rule.rule_type, ingress=self._rule_is_ingress_direction(rule)) def _iterate_rules(self, rules): for rule in rules: rule_type = rule.rule_type if rule_type in self.SUPPORTED_RULES: yield rule else: LOG.warning('Unsupported QoS rule type for %(rule_id)s: ' '%(rule_type)s; skipping', {'rule_id': rule.id, 'rule_type': rule_type}) def _handle_rule_delete(self, port, rule_type, ingress=False): handler_name = "".join(("delete_", rule_type)) if ingress: handler_name = "%s_%s" % (handler_name, constants.INGRESS_DIRECTION) handler = getattr(self, handler_name) handler(port) def _handle_update_create_rules(self, action, port, qos_policy): for rule in self._iterate_rules(qos_policy.rules): if rule.should_apply_to_port(port): handler_name = "".join((action, "_", rule.rule_type)) handler = getattr(self, handler_name) handler(port, rule) else: LOG.debug("Port %(port)s excluded from QoS rule %(rule)s", {'port': port, 'rule': rule.id}) def _rule_type_has_ingress_direction(self, rule_type): supported_rule = self.SUPPORTED_RULES[rule_type] if qos_consts.DIRECTION not in supported_rule.keys(): return False return (constants.INGRESS_DIRECTION in supported_rule[qos_consts.DIRECTION]['type:values']) def _rule_is_ingress_direction(self, rule): rule_direction = getattr(rule, "direction", constants.EGRESS_DIRECTION) return rule_direction == constants.INGRESS_DIRECTION class PortPolicyMap(object): def __init__(self): # we cannot use a dict of sets here because port dicts are not hashable self.qos_policy_ports = collections.defaultdict(dict) self.known_policies = {} self.port_policies = {} def get_ports(self, policy): return self.qos_policy_ports[policy.id].values() def get_policy(self, policy_id): return self.known_policies.get(policy_id) def update_policy(self, policy): self.known_policies[policy.id] = policy def has_policy_changed(self, port, policy_id): return self.port_policies.get(port['port_id']) != policy_id def get_port_policy(self, port): policy_id = self.port_policies.get(port['port_id']) if policy_id: return self.get_policy(policy_id) def set_port_policy(self, port, policy): """Attach a port to policy and return any previous policy on port.""" port_id = port['port_id'] old_policy = self.get_port_policy(port) self.known_policies[policy.id] = policy self.port_policies[port_id] = policy.id self.qos_policy_ports[policy.id][port_id] = port if old_policy and old_policy.id != policy.id: del self.qos_policy_ports[old_policy.id][port_id] return old_policy def clean_by_port(self, port): """Detach port from policy and cleanup data we don't need anymore.""" port_id = port['port_id'] if port_id in self.port_policies: del self.port_policies[port_id] for qos_policy_id, port_dict in self.qos_policy_ports.items(): if port_id in port_dict: del port_dict[port_id] if not port_dict: self._clean_policy_info(qos_policy_id) return LOG.debug("QoS extension did not have information on port %s", port_id) def _clean_policy_info(self, qos_policy_id): del self.qos_policy_ports[qos_policy_id] del self.known_policies[qos_policy_id] class QosAgentExtension(l2_agent_extension.L2AgentExtension): SUPPORTED_RESOURCE_TYPES = [resources.QOS_POLICY] def initialize(self, connection, driver_type): """Initialize agent extension.""" self.resource_rpc = resources_rpc.ResourcesPullRpcApi() self.qos_driver = manager.NeutronManager.load_class_for_provider( 'neutron.qos.agent_drivers', driver_type)() self.qos_driver.consume_api(self.agent_api) self.qos_driver.initialize() self.policy_map = PortPolicyMap() self._register_rpc_consumers(connection) def consume_api(self, agent_api): """Allows an extension to gain access to resources internal to the neutron agent and otherwise unavailable to the extension. """ self.agent_api = agent_api def _register_rpc_consumers(self, connection): """Allows an extension to receive notifications of updates made to items of interest. """ endpoints = [resources_rpc.ResourcesPushRpcCallback()] for resource_type in self.SUPPORTED_RESOURCE_TYPES: # We assume that the neutron server always broadcasts the latest # version known to the agent registry.register(self._handle_notification, resource_type) topic = resources_rpc.resource_type_versioned_topic(resource_type) connection.create_consumer(topic, endpoints, fanout=True) @lockutils.synchronized('qos-port') def _handle_notification(self, context, resource_type, qos_policies, event_type): # server does not allow to remove a policy that is attached to any # port, so we ignore DELETED events. Also, if we receive a CREATED # event for a policy, it means that there are no ports so far that are # attached to it. That's why we are interested in UPDATED events only if event_type == events.UPDATED: for qos_policy in qos_policies: self._process_update_policy(qos_policy) @lockutils.synchronized('qos-port') def handle_port(self, context, port): """Handle agent QoS extension for port. This method applies a new policy to a port using the QoS driver. Update events are handled in _handle_notification. """ port_id = port['port_id'] port_qos_policy_id = port.get('qos_policy_id') network_qos_policy_id = port.get('network_qos_policy_id') qos_policy_id = port_qos_policy_id or network_qos_policy_id if qos_policy_id is None: self._process_reset_port(port) return if not self.policy_map.has_policy_changed(port, qos_policy_id): return qos_policy = self.policy_map.get_policy( qos_policy_id) or self.resource_rpc.pull( context, resources.QOS_POLICY, qos_policy_id) if qos_policy is None: LOG.info("QoS policy %(qos_policy_id)s applied to port " "%(port_id)s is not available on server, " "it has been deleted. Skipping.", {'qos_policy_id': qos_policy_id, 'port_id': port_id}) self._process_reset_port(port) else: old_qos_policy = self.policy_map.set_port_policy(port, qos_policy) if old_qos_policy: self.qos_driver.delete(port, old_qos_policy) self.qos_driver.update(port, qos_policy) elif not qos_policy.rules: # There are no rules in new qos_policy, # so it should be deleted. # But new policy has a relationship with the port, # so reseting should not be called. self.qos_driver.delete(port) else: self.qos_driver.create(port, qos_policy) def delete_port(self, context, port): self._process_reset_port(port) def _policy_rules_modified(self, old_policy, policy): return not (len(old_policy.rules) == len(policy.rules) and all(i in old_policy.rules for i in policy.rules)) def _process_update_policy(self, qos_policy): old_qos_policy = self.policy_map.get_policy(qos_policy.id) if old_qos_policy: if self._policy_rules_modified(old_qos_policy, qos_policy): for port in self.policy_map.get_ports(qos_policy): #NOTE(QoS): for now, just reflush the rules on the port. # Later, we may want to apply the difference # between the old and new rule lists. self.qos_driver.delete(port, old_qos_policy) self.qos_driver.update(port, qos_policy) self.policy_map.update_policy(qos_policy) def _process_reset_port(self, port): self.policy_map.clean_by_port(port) self.qos_driver.delete(port) neutron-12.1.1/neutron/agent/l2/extensions/fdb_population.py0000664000175000017500000001705713553660047024216 0ustar zuulzuul00000000000000# Copyright (c) 2016 Mellanox Technologies, Ltd # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from neutron_lib import constants from neutron_lib.utils import helpers from oslo_config import cfg from oslo_log import log as logging from neutron.agent.l2 import l2_agent_extension from neutron.agent.linux import bridge_lib from neutron.conf.agent import l2_ext_fdb_population from neutron.plugins.ml2.drivers.linuxbridge.agent.common import ( constants as linux_bridge_constants) from neutron.plugins.ml2.drivers.openvswitch.agent.common import ( constants as ovs_constants) l2_ext_fdb_population.register_fdb_population_opts() LOG = logging.getLogger(__name__) class FdbPopulationAgentExtension( l2_agent_extension.L2AgentExtension): """The FDB population is an agent extension to OVS or linux bridge who's objective is to update the FDB table for existing instance using normal port, thus enabling communication between SR-IOV instances and normal instances. Additional information describing the problem can be found here: http://events.linuxfoundation.org/sites/events/files/slides/LinuxConJapan2014_makita_0.pdf """ # FDB udpates are triggered for ports with a certain device_owner only: # - device owner "compute": updates the FDB with normal port instances, # required in order to enable communication between # SR-IOV direct port instances and normal port instance. # - device owner "router_interface": updates the FDB with OVS/LB ports, # required in order to enable communication for SR-IOV instances # with floating ip that are located with the network node. # - device owner "DHCP": updates the FDB with the dhcp server. # When the lease expires a unicast renew message is sent # to the dhcp server. In case the FDB is not updated # the message will be sent to the wire, causing the message # to get lost in case the sender uses direct port and is # located on the same hypervisor as the network node. PERMITTED_DEVICE_OWNERS = {constants.DEVICE_OWNER_COMPUTE_PREFIX, constants.DEVICE_OWNER_ROUTER_INTF, constants.DEVICE_OWNER_DHCP} class FdbTableTracker(object): """FDB table tracker is a helper class intended to keep track of the existing FDB rules. """ def __init__(self, devices): self.device_to_macs = {} self.portid_to_mac = {} # update macs already in the physical interface's FDB table for device in devices: try: _stdout = bridge_lib.FdbInterface.show(device) except RuntimeError as e: LOG.warning( 'Unable to find FDB Interface %(device)s. ' 'Exception: %(e)s', {'device': device, 'e': e}) continue self.device_to_macs[device] = _stdout.split()[::3] def update_port(self, device, port_id, mac): # check if device is updated if self.device_to_macs.get(device) == mac: return # delete invalid port_id's mac from the FDB, # in case the port was updated to another mac self.delete_port([device], port_id) # update port id self.portid_to_mac[port_id] = mac # check if rule for mac already exists if mac in self.device_to_macs[device]: return try: bridge_lib.FdbInterface.add(mac, device) except RuntimeError as e: LOG.warning( 'Unable to add mac %(mac)s ' 'to FDB Interface %(device)s. ' 'Exception: %(e)s', {'mac': mac, 'device': device, 'e': e}) return self.device_to_macs[device].append(mac) def delete_port(self, devices, port_id): mac = self.portid_to_mac.get(port_id) if mac is None: LOG.warning('Port Id %(port_id)s does not have a rule for ' 'devices %(devices)s in FDB table', {'port_id': port_id, 'devices': devices}) return for device in devices: if mac in self.device_to_macs[device]: try: bridge_lib.FdbInterface.delete(mac, device) except RuntimeError as e: LOG.warning( 'Unable to delete mac %(mac)s ' 'from FDB Interface %(device)s. ' 'Exception: %(e)s', {'mac': mac, 'device': device, 'e': e}) return self.device_to_macs[device].remove(mac) del self.portid_to_mac[port_id] # class FdbPopulationAgentExtension implementation: def initialize(self, connection, driver_type): """Perform FDB Agent Extension initialization.""" valid_driver_types = (linux_bridge_constants.EXTENSION_DRIVER_TYPE, ovs_constants.EXTENSION_DRIVER_TYPE) if driver_type not in valid_driver_types: LOG.error('FDB extension is only supported for OVS and ' 'linux bridge agent, currently uses ' '%(driver_type)s', {'driver_type': driver_type}) sys.exit(1) self.device_mappings = helpers.parse_mappings( cfg.CONF.FDB.shared_physical_device_mappings, unique_keys=False) devices = self._get_devices() if not devices: LOG.error('Invalid configuration provided for FDB extension: ' 'no physical devices') sys.exit(1) self.fdb_tracker = self.FdbTableTracker(devices) def handle_port(self, context, details): """Handle agent FDB population extension for port.""" device_owner = details['device_owner'] if self._is_valid_device_owner(device_owner): mac = details['mac_address'] port_id = details['port_id'] physnet = details.get('physical_network') if physnet and physnet in self.device_mappings: for device in self.device_mappings[physnet]: self.fdb_tracker.update_port(device, port_id, mac) def delete_port(self, context, details): """Delete port from FDB population extension.""" port_id = details['port_id'] devices = self._get_devices() self.fdb_tracker.delete_port(devices, port_id) def _get_devices(self): def _flatten_list(l): return [item for sublist in l for item in sublist] return _flatten_list(self.device_mappings.values()) def _is_valid_device_owner(self, device_owner): for permitted_device_owner in self.PERMITTED_DEVICE_OWNERS: if device_owner.startswith(permitted_device_owner): return True return False neutron-12.1.1/neutron/agent/l2/extensions/qos_linux.py0000664000175000017500000000220613553660046023217 0ustar zuulzuul00000000000000# Copyright (c) 2017 Cloudbase Solutions # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.l2.extensions import qos from neutron.agent.linux import tc_lib class QosLinuxAgentDriver(qos.QosAgentDriver): def _get_egress_burst_value(self, rule): """Return burst value used for egress bandwidth limitation. Because Egress bw_limit is done on ingress qdisc by LB and ovs drivers so it will return burst_value used by tc on as ingress_qdisc. """ return tc_lib.TcCommand.get_ingress_qdisc_burst_value( rule.max_kbps, rule.max_burst_kbps) neutron-12.1.1/neutron/agent/l2/extensions/__init__.py0000664000175000017500000000000013553660046022723 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/l2/__init__.py0000664000175000017500000000000013553660046020524 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/rpc.py0000664000175000017500000003045113553660047017252 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from datetime import datetime import itertools import netaddr from neutron_lib.callbacks import events as callback_events from neutron_lib.callbacks import registry from neutron_lib import constants from oslo_log import log as logging import oslo_messaging from oslo_utils import uuidutils from neutron.agent import resource_cache from neutron.api.rpc.callbacks import resources from neutron.common import constants as n_const from neutron.common import rpc as n_rpc from neutron.common import topics from neutron import objects LOG = logging.getLogger(__name__) def create_consumers(endpoints, prefix, topic_details, start_listening=True): """Create agent RPC consumers. :param endpoints: The list of endpoints to process the incoming messages. :param prefix: Common prefix for the plugin/agent message queues. :param topic_details: A list of topics. Each topic has a name, an operation, and an optional host param keying the subscription to topic.host for plugin calls. :param start_listening: if True, it starts the processing loop :returns: A common Connection. """ connection = n_rpc.create_connection() for details in topic_details: topic, operation, node_name = itertools.islice( itertools.chain(details, [None]), 3) topic_name = topics.get_topic_name(prefix, topic, operation) connection.create_consumer(topic_name, endpoints, fanout=True) if node_name: node_topic_name = '%s.%s' % (topic_name, node_name) connection.create_consumer(node_topic_name, endpoints, fanout=False) if start_listening: connection.consume_in_threads() return connection class PluginReportStateAPI(object): """RPC client used to report state back to plugin. This class implements the client side of an rpc interface. The server side can be found in neutron.db.agents_db.AgentExtRpcCallback. For more information on changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. """ def __init__(self, topic): target = oslo_messaging.Target(topic=topic, version='1.0', namespace=n_const.RPC_NAMESPACE_STATE) self.client = n_rpc.get_client(target) def report_state(self, context, agent_state, use_call=False): cctxt = self.client.prepare( timeout=n_rpc.TRANSPORT.conf.rpc_response_timeout) # add unique identifier to a report # that can be logged on server side. # This create visible correspondence between events on # the agent and on the server agent_state['uuid'] = uuidutils.generate_uuid() kwargs = { 'agent_state': {'agent_state': agent_state}, 'time': datetime.utcnow().strftime(constants.ISO8601_TIME_FORMAT), } method = cctxt.call if use_call else cctxt.cast return method(context, 'report_state', **kwargs) class PluginApi(object): '''Agent side of the rpc API. API version history: 1.0 - Initial version. 1.3 - get_device_details rpc signature upgrade to obtain 'host' and return value to include fixed_ips and device_owner for the device port 1.4 - tunnel_sync rpc signature upgrade to obtain 'host' 1.5 - Support update_device_list and get_devices_details_list_and_failed_devices ''' def __init__(self, topic): target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def get_device_details(self, context, device, agent_id, host=None): cctxt = self.client.prepare() return cctxt.call(context, 'get_device_details', device=device, agent_id=agent_id, host=host) def get_devices_details_list(self, context, devices, agent_id, host=None): cctxt = self.client.prepare(version='1.3') return cctxt.call(context, 'get_devices_details_list', devices=devices, agent_id=agent_id, host=host) def get_devices_details_list_and_failed_devices(self, context, devices, agent_id, host=None): """Get devices details and the list of devices that failed. This method returns the devices details. If an error is thrown when retrieving the devices details, the device is put in a list of failed devices. """ cctxt = self.client.prepare(version='1.5') return cctxt.call( context, 'get_devices_details_list_and_failed_devices', devices=devices, agent_id=agent_id, host=host) def update_device_down(self, context, device, agent_id, host=None): cctxt = self.client.prepare() return cctxt.call(context, 'update_device_down', device=device, agent_id=agent_id, host=host) def update_device_up(self, context, device, agent_id, host=None): cctxt = self.client.prepare() return cctxt.call(context, 'update_device_up', device=device, agent_id=agent_id, host=host) def update_device_list(self, context, devices_up, devices_down, agent_id, host, agent_restarted=False): cctxt = self.client.prepare(version='1.5') ret_devices_up = [] failed_devices_up = [] ret_devices_down = [] failed_devices_down = [] step = n_const.RPC_RES_PROCESSING_STEP devices_up = list(devices_up) devices_down = list(devices_down) for i in range(0, max(len(devices_up), len(devices_down)), step): # Divide-and-conquer RPC timeout ret = cctxt.call(context, 'update_device_list', devices_up=devices_up[i:i + step], devices_down=devices_down[i:i + step], agent_id=agent_id, host=host, agent_restarted=agent_restarted) ret_devices_up.extend(ret.get("devices_up", [])) failed_devices_up.extend(ret.get("failed_devices_up", [])) ret_devices_down.extend(ret.get("devices_down", [])) failed_devices_down.extend(ret.get("failed_devices_down", [])) return {'devices_up': ret_devices_up, 'failed_devices_up': failed_devices_up, 'devices_down': ret_devices_down, 'failed_devices_down': failed_devices_down} def tunnel_sync(self, context, tunnel_ip, tunnel_type=None, host=None): cctxt = self.client.prepare(version='1.4') return cctxt.call(context, 'tunnel_sync', tunnel_ip=tunnel_ip, tunnel_type=tunnel_type, host=host) def create_cache_for_l2_agent(): """Create a push-notifications cache for L2 agent related resources.""" objects.register_objects() resource_types = [ resources.PORT, resources.SECURITYGROUP, resources.SECURITYGROUPRULE, resources.NETWORK, resources.SUBNET ] rcache = resource_cache.RemoteResourceCache(resource_types) rcache.start_watcher() return rcache class CacheBackedPluginApi(PluginApi): def __init__(self, *args, **kwargs): super(CacheBackedPluginApi, self).__init__(*args, **kwargs) self.remote_resource_cache = create_cache_for_l2_agent() def register_legacy_notification_callbacks(self, legacy_interface): """Emulates the server-side notifications from ml2 AgentNotifierApi. legacy_interface is an object with 'delete'/'update' methods for core resources. """ self._legacy_interface = legacy_interface for e in (callback_events.AFTER_UPDATE, callback_events.AFTER_DELETE): for r in (resources.PORT, resources.NETWORK): registry.subscribe(self._legacy_notifier, r, e) def _legacy_notifier(self, rtype, event, trigger, context, resource_id, **kwargs): """Checks if legacy interface is expecting calls for resource. looks for port_update, network_delete, etc and calls them with the payloads the handlers are expecting (an ID). """ rtype = rtype.lower() # all legacy handlers don't camelcase is_delete = event == callback_events.AFTER_DELETE suffix = 'delete' if is_delete else 'update' method = "%s_%s" % (rtype, suffix) if not hasattr(self._legacy_interface, method): # TODO(kevinbenton): once these notifications are stable, emit # a deprecation warning for legacy handlers return payload = {rtype: {'id': resource_id}, '%s_id' % rtype: resource_id} getattr(self._legacy_interface, method)(context, **payload) def get_devices_details_list_and_failed_devices(self, context, devices, agent_id, host=None): result = {'devices': [], 'failed_devices': []} for device in devices: try: result['devices'].append( self.get_device_details(context, device, agent_id, host)) except Exception: LOG.exception("Failed to get details for device %s", device) result['failed_devices'].append(device) return result def get_device_details(self, context, device, agent_id, host=None): port_obj = self.remote_resource_cache.get_resource_by_id( resources.PORT, device) if not port_obj: LOG.debug("Device %s does not exist in cache.", device) return {'device': device} if not port_obj.binding_levels: LOG.warning("Device %s is not bound.", port_obj) return {'device': device} segment = port_obj.binding_levels[-1].segment if not segment: LOG.debug("Device %s is not bound to any segment.", port_obj) return {'device': device} net = self.remote_resource_cache.get_resource_by_id( resources.NETWORK, port_obj.network_id) net_qos_policy_id = net.qos_policy_id # match format of old RPC interface mac_addr = str(netaddr.EUI(str(port_obj.mac_address), dialect=netaddr.mac_unix_expanded)) entry = { 'device': device, 'network_id': port_obj.network_id, 'port_id': port_obj.id, 'mac_address': mac_addr, 'admin_state_up': port_obj.admin_state_up, 'network_type': segment.network_type, 'segmentation_id': segment.segmentation_id, 'physical_network': segment.physical_network, 'fixed_ips': [{'subnet_id': o.subnet_id, 'ip_address': str(o.ip_address)} for o in port_obj.fixed_ips], 'device_owner': port_obj.device_owner, 'allowed_address_pairs': [{'mac_address': o.mac_address, 'ip_address': o.ip_address} for o in port_obj.allowed_address_pairs], 'port_security_enabled': getattr(port_obj.security, 'port_security_enabled', True), 'qos_policy_id': port_obj.qos_policy_id, 'network_qos_policy_id': net_qos_policy_id, 'profile': port_obj.binding.profile, 'security_groups': list(port_obj.security_group_ids) } LOG.debug("Returning: %s", entry) return entry def get_devices_details_list(self, context, devices, agent_id, host=None): return [self.get_device_details(context, device, agent_id, host) for device in devices] neutron-12.1.1/neutron/agent/l3/0000775000175000017500000000000013553660156016430 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/l3/keepalived_state_change.py0000664000175000017500000001552213553660047023624 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import signal import sys import httplib2 import netaddr from oslo_config import cfg from oslo_log import log as logging from neutron._i18n import _ from neutron.agent.l3 import ha from neutron.agent.linux import daemon from neutron.agent.linux import ip_lib from neutron.agent.linux import ip_monitor from neutron.agent.linux import utils as agent_utils from neutron.common import config from neutron.conf.agent import common as agent_config from neutron.conf.agent.l3 import keepalived LOG = logging.getLogger(__name__) class KeepalivedUnixDomainConnection(agent_utils.UnixDomainHTTPConnection): def __init__(self, *args, **kwargs): # Old style super initialization is required! agent_utils.UnixDomainHTTPConnection.__init__( self, *args, **kwargs) self.socket_path = ( ha.L3AgentKeepalivedStateChangeServer. get_keepalived_state_change_socket_path(cfg.CONF)) class MonitorDaemon(daemon.Daemon): def __init__(self, pidfile, router_id, user, group, namespace, conf_dir, interface, cidr): self.router_id = router_id self.namespace = namespace self.conf_dir = conf_dir self.interface = interface self.cidr = cidr self.monitor = None super(MonitorDaemon, self).__init__(pidfile, uuid=router_id, user=user, group=group) def run(self, run_as_root=False): self.monitor = ip_monitor.IPMonitor(namespace=self.namespace, run_as_root=run_as_root) self.monitor.start() # Only drop privileges if the process is currently running as root # (The run_as_root variable name here is unfortunate - It means to # use a root helper when the running process is NOT already running # as root if not run_as_root: super(MonitorDaemon, self).run() self.handle_initial_state() for iterable in self.monitor: self.parse_and_handle_event(iterable) def parse_and_handle_event(self, iterable): try: event = ip_monitor.IPMonitorEvent.from_text(iterable) if event.interface == self.interface and event.cidr == self.cidr: new_state = 'master' if event.added else 'backup' self.write_state_change(new_state) self.notify_agent(new_state) elif event.interface != self.interface and event.added: # Send GARPs for all new router interfaces. # REVISIT(jlibosva): keepalived versions 1.2.19 and below # contain bug where gratuitous ARPs are not sent on receiving # SIGHUP signal. This is a workaround to this bug. keepalived # has this issue fixed since 1.2.20 but the version is not # packaged in some distributions (RHEL/CentOS/Ubuntu Xenial). # Remove this code once new keepalived versions are available. self.send_garp(event) except Exception: LOG.exception('Failed to process or handle event for line %s', iterable) def handle_initial_state(self): try: state = 'backup' ip = ip_lib.IPDevice(self.interface, self.namespace) for address in ip.addr.list(): if address.get('cidr') == self.cidr: state = 'master' self.write_state_change(state) self.notify_agent(state) break LOG.debug('Initial status of router %s is %s', self.router_id, state) except Exception: LOG.exception('Failed to get initial status of router %s', self.router_id) def write_state_change(self, state): with open(os.path.join( self.conf_dir, 'state'), 'w') as state_file: state_file.write(state) LOG.debug('Wrote router %s state %s', self.router_id, state) def notify_agent(self, state): resp, content = httplib2.Http().request( # Note that the message is sent via a Unix domain socket so that # the URL doesn't matter. 'http://127.0.0.1/', headers={'X-Neutron-Router-Id': self.router_id, 'X-Neutron-State': state}, connection_type=KeepalivedUnixDomainConnection) if resp.status != 200: raise Exception(_('Unexpected response: %s') % resp) LOG.debug('Notified agent router %s, state %s', self.router_id, state) def send_garp(self, event): """Send gratuitous ARP for given event.""" ip_lib.send_ip_addr_adv_notif( self.namespace, event.interface, str(netaddr.IPNetwork(event.cidr).ip), log_exception=False ) def _kill_monitor(self): if self.monitor: # Kill PID instead of calling self.monitor.stop() because the ip # monitor is running as root while keepalived-state-change is not # (dropped privileges after launching the ip monitor) and will fail # with "Permission denied". Also, we can safely do this because the # monitor was launched with respawn_interval=None so it won't be # automatically respawned agent_utils.kill_process(self.monitor.pid, signal.SIGKILL, run_as_root=True) def handle_sigterm(self, signum, frame): self._kill_monitor() super(MonitorDaemon, self).handle_sigterm(signum, frame) def configure(conf): config.init(sys.argv[1:]) conf.set_override('log_dir', cfg.CONF.conf_dir) conf.set_override('debug', True) conf.set_override('use_syslog', True) config.setup_logging() agent_config.setup_privsep() def main(): keepalived.register_cli_l3_agent_keepalived_opts() keepalived.register_l3_agent_keepalived_opts() configure(cfg.CONF) MonitorDaemon(cfg.CONF.pid_file, cfg.CONF.router_id, cfg.CONF.user, cfg.CONF.group, cfg.CONF.namespace, cfg.CONF.conf_dir, cfg.CONF.monitor_interface, cfg.CONF.monitor_cidr).start() neutron-12.1.1/neutron/agent/l3/item_allocator.py0000664000175000017500000001166613553660047022011 0ustar zuulzuul00000000000000# Copyright 2015 IBM Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from oslo_log import log as logging LOG = logging.getLogger(__name__) class ItemAllocator(object): """Manages allocation of items from a pool Some of the allocations such as link local addresses used for routing inside the fip namespaces need to persist across agent restarts to maintain consistency. Persisting such allocations in the neutron database is unnecessary and would degrade performance. ItemAllocator utilizes local file system to track allocations made for objects of a given class. The persistent datastore is a file. The records are one per line of the format: keyvalue. For example if the delimiter is a ',' (the default value) then the records will be: key,value (one per line) """ def __init__(self, state_file, ItemClass, item_pool, delimiter=','): """Read the file with previous allocations recorded. See the note in the allocate method for more detail. """ self.ItemClass = ItemClass self.state_file = state_file self.allocations = {} self.remembered = {} self.pool = item_pool read_error = False for line in self._read(): try: key, saved_value = line.strip().split(delimiter) self.remembered[key] = self.ItemClass(saved_value) except ValueError: read_error = True LOG.warning("Invalid line in %(file)s, " "ignoring: %(line)s", {'file': state_file, 'line': line}) self.pool.difference_update(self.remembered.values()) if read_error: LOG.debug("Re-writing file %s due to read error", state_file) self._write_allocations() def lookup(self, key): """Try to lookup an item of ItemClass type. See if there are any current or remembered allocations for the key. """ if key in self.allocations: return self.allocations[key] if key in self.remembered: self.allocations[key] = self.remembered.pop(key) return self.allocations[key] def allocate(self, key): """Try to allocate an item of ItemClass type. I expect this to work in all cases because I expect the pool size to be large enough for any situation. Nonetheless, there is some defensive programming in here. Since the allocations are persisted, there is the chance to leak allocations which should have been released but were not. This leak could eventually exhaust the pool. So, if a new allocation is needed, the code first checks to see if there are any remembered allocations for the key. If not, it checks the free pool. If the free pool is empty then it dumps the remembered allocations to free the pool. This final desperate step will not happen often in practice. """ entry = self.lookup(key) if entry: return entry if not self.pool: # Desperate times. Try to get more in the pool. self.pool.update(self.remembered.values()) self.remembered.clear() if not self.pool: # The number of address pairs allocated from the # pool depends upon the prefix length specified # in DVR_FIP_LL_CIDR raise RuntimeError("Cannot allocate item of type:" " %s from pool using file %s" % (self.ItemClass, self.state_file)) self.allocations[key] = self.pool.pop() self._write_allocations() return self.allocations[key] def release(self, key): if self.lookup(key): self.pool.add(self.allocations.pop(key)) self._write_allocations() def _write_allocations(self): current = ["%s,%s\n" % (k, v) for k, v in self.allocations.items()] remembered = ["%s,%s\n" % (k, v) for k, v in self.remembered.items()] current.extend(remembered) self._write(current) def _write(self, lines): with open(self.state_file, "w") as f: f.writelines(lines) def _read(self): if not os.path.exists(self.state_file): return [] with open(self.state_file) as f: return f.readlines() neutron-12.1.1/neutron/agent/l3/dvr.py0000664000175000017500000000517513553660046017603 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import weakref from neutron.agent.l3 import dvr_fip_ns class AgentMixin(object): def __init__(self, host): # dvr data self._fip_namespaces = weakref.WeakValueDictionary() super(AgentMixin, self).__init__(host) def get_fip_ns(self, ext_net_id): # TODO(Carl) is this necessary? Code that this replaced was careful to # convert these to string like this so I preserved that. ext_net_id = str(ext_net_id) fip_ns = self._fip_namespaces.get(ext_net_id) if fip_ns and not fip_ns.destroyed: return fip_ns fip_ns = dvr_fip_ns.FipNamespace(ext_net_id, self.conf, self.driver, self.use_ipv6) self._fip_namespaces[ext_net_id] = fip_ns return fip_ns def get_ports_by_subnet(self, subnet_id): return self.plugin_rpc.get_ports_by_subnet(self.context, subnet_id) def _update_arp_entry(self, context, payload, action): router_id = payload['router_id'] ri = self.router_info.get(router_id) if not ri: return arp_table = payload['arp_table'] ip = arp_table['ip_address'] mac = arp_table['mac_address'] subnet_id = arp_table['subnet_id'] ri._update_arp_entry(ip, mac, subnet_id, action) def add_arp_entry(self, context, payload): """Add arp entry into router namespace. Called from RPC.""" self._update_arp_entry(context, payload, 'add') def del_arp_entry(self, context, payload): """Delete arp entry from router namespace. Called from RPC.""" self._update_arp_entry(context, payload, 'delete') def fipnamespace_delete_on_ext_net(self, context, ext_net_id): """Delete fip namespace after external network removed.""" fip_ns = self.get_fip_ns(ext_net_id) if fip_ns.agent_gateway_port and not fip_ns.destroyed: fip_ns.unsubscribe(ext_net_id) fip_ns.delete() neutron-12.1.1/neutron/agent/l3/agent.py0000664000175000017500000011210213553660047020074 0ustar zuulzuul00000000000000# Copyright 2012 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import eventlet import netaddr from neutron_lib.agent import constants as agent_consts from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as lib_const from neutron_lib import context as n_context from oslo_config import cfg from oslo_context import context as common_context from oslo_log import log as logging import oslo_messaging from oslo_serialization import jsonutils from oslo_service import loopingcall from oslo_service import periodic_task from oslo_utils import excutils from oslo_utils import timeutils from osprofiler import profiler from neutron._i18n import _ from neutron.agent.common import resource_processing_queue as queue from neutron.agent.common import utils as common_utils from neutron.agent.l3 import dvr from neutron.agent.l3 import dvr_edge_ha_router from neutron.agent.l3 import dvr_edge_router as dvr_router from neutron.agent.l3 import dvr_local_router from neutron.agent.l3 import ha from neutron.agent.l3 import ha_router from neutron.agent.l3 import l3_agent_extension_api as l3_ext_api from neutron.agent.l3 import l3_agent_extensions_manager as l3_ext_manager from neutron.agent.l3 import legacy_router from neutron.agent.l3 import namespace_manager from neutron.agent.linux import external_process from neutron.agent.linux import ip_lib from neutron.agent.linux import pd from neutron.agent.metadata import driver as metadata_driver from neutron.agent import rpc as agent_rpc from neutron.common import constants as l3_constants from neutron.common import exceptions as n_exc from neutron.common import ipv6_utils from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.common import utils from neutron import manager LOG = logging.getLogger(__name__) # Number of routers to fetch from server at a time on resync. # Needed to reduce load on server side and to speed up resync on agent side. SYNC_ROUTERS_MAX_CHUNK_SIZE = 256 SYNC_ROUTERS_MIN_CHUNK_SIZE = 32 def log_verbose_exc(message, router_payload): LOG.exception(message) LOG.debug("Payload:\n%s", utils.DelayedStringRenderer(jsonutils.dumps, router_payload, indent=5)) class L3PluginApi(object): """Agent side of the l3 agent RPC API. API version history: 1.0 - Initial version. 1.1 - Floating IP operational status updates 1.2 - DVR support: new L3 plugin methods added. - get_ports_by_subnet - get_agent_gateway_port Needed by the agent when operating in DVR/DVR_SNAT mode 1.3 - Get the list of activated services 1.4 - Added L3 HA update_router_state. This method was reworked in to update_ha_routers_states 1.5 - Added update_ha_routers_states 1.6 - Added process_prefix_update 1.7 - DVR support: new L3 plugin methods added. - delete_agent_gateway_port 1.8 - Added address scope information 1.9 - Added get_router_ids 1.10 Added update_all_ha_network_port_statuses """ def __init__(self, topic, host): self.host = host target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def get_routers(self, context, router_ids=None): """Make a remote process call to retrieve the sync data for routers.""" cctxt = self.client.prepare() return cctxt.call(context, 'sync_routers', host=self.host, router_ids=router_ids) def update_all_ha_network_port_statuses(self, context): """Make a remote process call to update HA network port status.""" cctxt = self.client.prepare(version='1.10') return cctxt.call(context, 'update_all_ha_network_port_statuses', host=self.host) def get_router_ids(self, context): """Make a remote process call to retrieve scheduled routers ids.""" cctxt = self.client.prepare(version='1.9') return cctxt.call(context, 'get_router_ids', host=self.host) def get_external_network_id(self, context): """Make a remote process call to retrieve the external network id. @raise oslo_messaging.RemoteError: with TooManyExternalNetworks as exc_type if there are more than one external network """ cctxt = self.client.prepare() return cctxt.call(context, 'get_external_network_id', host=self.host) def update_floatingip_statuses(self, context, router_id, fip_statuses): """Call the plugin update floating IPs's operational status.""" cctxt = self.client.prepare(version='1.1') return cctxt.call(context, 'update_floatingip_statuses', router_id=router_id, fip_statuses=fip_statuses) def get_ports_by_subnet(self, context, subnet_id): """Retrieve ports by subnet id.""" cctxt = self.client.prepare(version='1.2') return cctxt.call(context, 'get_ports_by_subnet', host=self.host, subnet_id=subnet_id) def get_agent_gateway_port(self, context, fip_net): """Get or create an agent_gateway_port.""" cctxt = self.client.prepare(version='1.2') return cctxt.call(context, 'get_agent_gateway_port', network_id=fip_net, host=self.host) def get_service_plugin_list(self, context): """Make a call to get the list of activated services.""" cctxt = self.client.prepare(version='1.3') return cctxt.call(context, 'get_service_plugin_list') def update_ha_routers_states(self, context, states): """Update HA routers states.""" cctxt = self.client.prepare(version='1.5') return cctxt.cast(context, 'update_ha_routers_states', host=self.host, states=states) def process_prefix_update(self, context, prefix_update): """Process prefix update whenever prefixes get changed.""" cctxt = self.client.prepare(version='1.6') return cctxt.call(context, 'process_prefix_update', subnets=prefix_update) def delete_agent_gateway_port(self, context, fip_net): """Delete Floatingip_agent_gateway_port.""" cctxt = self.client.prepare(version='1.7') return cctxt.call(context, 'delete_agent_gateway_port', host=self.host, network_id=fip_net) @profiler.trace_cls("l3-agent") class L3NATAgent(ha.AgentMixin, dvr.AgentMixin, manager.Manager): """Manager for L3NatAgent API version history: 1.0 initial Version 1.1 changed the type of the routers parameter to the routers_updated method. It was previously a list of routers in dict format. It is now a list of router IDs only. Per rpc versioning rules, it is backwards compatible. 1.2 - DVR support: new L3 agent methods added. - add_arp_entry - del_arp_entry 1.3 - fipnamespace_delete_on_ext_net - to delete fipnamespace after the external network is removed Needed by the L3 service when dealing with DVR 1.4 - support network_update to get MTU updates """ target = oslo_messaging.Target(version='1.4') def __init__(self, host, conf=None): if conf: self.conf = conf else: self.conf = cfg.CONF self.router_info = {} self._check_config_params() self.process_monitor = external_process.ProcessMonitor( config=self.conf, resource_type='router') self.driver = common_utils.load_interface_driver(self.conf) self._context = n_context.get_admin_context_without_session() self.plugin_rpc = L3PluginApi(topics.L3PLUGIN, host) self.fullsync = True self.sync_routers_chunk_size = SYNC_ROUTERS_MAX_CHUNK_SIZE # Get the list of service plugins from Neutron Server # This is the first place where we contact neutron-server on startup # so retry in case its not ready to respond. while True: try: self.neutron_service_plugins = ( self.plugin_rpc.get_service_plugin_list(self.context)) except oslo_messaging.MessagingTimeout as e: LOG.warning('l3-agent cannot contact neutron server ' 'to retrieve service plugins enabled. ' 'Check connectivity to neutron server. ' 'Retrying... ' 'Detailed message: %(msg)s.', {'msg': e}) continue break self.init_extension_manager(self.plugin_rpc) self.metadata_driver = None if self.conf.enable_metadata_proxy: self.metadata_driver = metadata_driver.MetadataDriver(self) self.namespaces_manager = namespace_manager.NamespaceManager( self.conf, self.driver, self.metadata_driver) self._queue = queue.ResourceProcessingQueue() super(L3NATAgent, self).__init__(host=self.conf.host) self.target_ex_net_id = None self.use_ipv6 = ipv6_utils.is_enabled_and_bind_by_default() self.pd = pd.PrefixDelegation(self.context, self.process_monitor, self.driver, self.plugin_rpc.process_prefix_update, self.create_pd_router_update, self.conf) # Consume network updates to trigger router resync consumers = [[topics.NETWORK, topics.UPDATE]] agent_rpc.create_consumers([self], topics.AGENT, consumers) # We set HA network port status to DOWN to let l2 agent update it # to ACTIVE after wiring. This allows us to spawn keepalived only # when l2 agent finished wiring the port. try: self.plugin_rpc.update_all_ha_network_port_statuses(self.context) except Exception: LOG.exception('update_all_ha_network_port_statuses failed') def _check_config_params(self): """Check items in configuration files. Check for required and invalid configuration items. The actual values are not verified for correctness. """ if not self.conf.interface_driver: msg = 'An interface driver must be specified' LOG.error(msg) raise SystemExit(1) if self.conf.ipv6_gateway: # ipv6_gateway configured. Check for valid v6 link-local address. try: msg = ("%s used in config as ipv6_gateway is not a valid " "IPv6 link-local address.") ip_addr = netaddr.IPAddress(self.conf.ipv6_gateway) if ip_addr.version != 6 or not ip_addr.is_link_local(): LOG.error(msg, self.conf.ipv6_gateway) raise SystemExit(1) except netaddr.AddrFormatError: LOG.error(msg, self.conf.ipv6_gateway) raise SystemExit(1) def _fetch_external_net_id(self, force=False): """Find UUID of single external network for this agent.""" if self.conf.gateway_external_network_id: return self.conf.gateway_external_network_id # L3 agent doesn't use external_network_bridge to handle external # networks, so bridge_mappings with provider networks will be used # and the L3 agent is able to handle any external networks. if not self.conf.external_network_bridge: return if not force and self.target_ex_net_id: return self.target_ex_net_id try: self.target_ex_net_id = self.plugin_rpc.get_external_network_id( self.context) return self.target_ex_net_id except oslo_messaging.RemoteError as e: with excutils.save_and_reraise_exception() as ctx: if e.exc_type == 'TooManyExternalNetworks': ctx.reraise = False msg = _( "The 'gateway_external_network_id' option must be " "configured for this agent as Neutron has more than " "one external network.") raise Exception(msg) def _create_router(self, router_id, router): args = [] kwargs = { 'agent': self, 'router_id': router_id, 'router': router, 'use_ipv6': self.use_ipv6, 'agent_conf': self.conf, 'interface_driver': self.driver, } if router.get('distributed'): kwargs['host'] = self.host if router.get('distributed') and router.get('ha'): # Case 1: If the router contains information about the HA interface # and if the requesting agent is a DVR_SNAT agent then go ahead # and create a HA router. # Case 2: If the router does not contain information about the HA # interface this means that this DVR+HA router needs to host only # the edge side of it, typically because it's landing on a node # that needs to provision a router namespace because of a DVR # service port (e.g. DHCP). So go ahead and create a regular DVR # edge router. if (self.conf.agent_mode == lib_const.L3_AGENT_MODE_DVR_SNAT and router.get(lib_const.HA_INTERFACE_KEY) is not None): kwargs['state_change_callback'] = self.enqueue_state_change return dvr_edge_ha_router.DvrEdgeHaRouter(*args, **kwargs) if router.get('distributed'): if self.conf.agent_mode == lib_const.L3_AGENT_MODE_DVR_SNAT: return dvr_router.DvrEdgeRouter(*args, **kwargs) else: return dvr_local_router.DvrLocalRouter(*args, **kwargs) if router.get('ha'): kwargs['state_change_callback'] = self.enqueue_state_change return ha_router.HaRouter(*args, **kwargs) return legacy_router.LegacyRouter(*args, **kwargs) def _router_added(self, router_id, router): ri = self._create_router(router_id, router) registry.notify(resources.ROUTER, events.BEFORE_CREATE, self, router=ri) self.router_info[router_id] = ri # If initialize() fails, cleanup and retrigger complete sync try: ri.initialize(self.process_monitor) except Exception: with excutils.save_and_reraise_exception(): del self.router_info[router_id] LOG.exception('Error while initializing router %s', router_id) self.namespaces_manager.ensure_router_cleanup(router_id) try: ri.delete() except Exception: LOG.exception('Error while deleting router %s', router_id) def _safe_router_removed(self, router_id): """Try to delete a router and return True if successful.""" try: self._router_removed(router_id) self.l3_ext_manager.delete_router(self.context, router_id) except Exception: LOG.exception('Error while deleting router %s', router_id) return False else: return True def _router_removed(self, router_id): ri = self.router_info.get(router_id) if ri is None: LOG.warning("Info for router %s was not found. " "Performing router cleanup", router_id) self.namespaces_manager.ensure_router_cleanup(router_id) return registry.notify(resources.ROUTER, events.BEFORE_DELETE, self, router=ri) ri.delete() del self.router_info[router_id] registry.notify(resources.ROUTER, events.AFTER_DELETE, self, router=ri) def init_extension_manager(self, connection): l3_ext_manager.register_opts(self.conf) self.agent_api = l3_ext_api.L3AgentExtensionAPI(self.router_info) self.l3_ext_manager = ( l3_ext_manager.L3AgentExtensionsManager(self.conf)) self.l3_ext_manager.initialize( connection, lib_const.L3_AGENT_MODE, self.agent_api) def router_deleted(self, context, router_id): """Deal with router deletion RPC message.""" LOG.debug('Got router deleted notification for %s', router_id) update = queue.ResourceUpdate(router_id, queue.PRIORITY_RPC, action=queue.DELETE_ROUTER) self._queue.add(update) def routers_updated(self, context, routers): """Deal with routers modification and creation RPC message.""" LOG.debug('Got routers updated notification :%s', routers) if routers: # This is needed for backward compatibility if isinstance(routers[0], dict): routers = [router['id'] for router in routers] for id in routers: update = queue.ResourceUpdate( id, queue.PRIORITY_RPC, action=queue.ADD_UPDATE_ROUTER) self._queue.add(update) def router_removed_from_agent(self, context, payload): LOG.debug('Got router removed from agent :%r', payload) router_id = payload['router_id'] update = queue.ResourceUpdate(router_id, queue.PRIORITY_RPC, action=queue.DELETE_ROUTER) self._queue.add(update) def router_added_to_agent(self, context, payload): LOG.debug('Got router added to agent :%r', payload) self.routers_updated(context, payload) def network_update(self, context, **kwargs): network_id = kwargs['network']['id'] for ri in self.router_info.values(): ports = list(ri.internal_ports) if ri.ex_gw_port: ports.append(ri.ex_gw_port) port_belongs = lambda p: p['network_id'] == network_id if any(port_belongs(p) for p in ports): update = queue.ResourceUpdate( ri.router_id, queue.PRIORITY_SYNC_ROUTERS_TASK) self._resync_router(update) def _process_router_if_compatible(self, router): if (self.conf.external_network_bridge and not ip_lib.device_exists(self.conf.external_network_bridge)): LOG.error("The external network bridge '%s' does not exist", self.conf.external_network_bridge) return # Either ex_net_id or handle_internal_only_routers must be set ex_net_id = (router['external_gateway_info'] or {}).get('network_id') if not ex_net_id and not self.conf.handle_internal_only_routers: raise n_exc.RouterNotCompatibleWithAgent(router_id=router['id']) # If target_ex_net_id and ex_net_id are set they must be equal target_ex_net_id = self._fetch_external_net_id() if (target_ex_net_id and ex_net_id and ex_net_id != target_ex_net_id): # Double check that our single external_net_id has not changed # by forcing a check by RPC. if ex_net_id != self._fetch_external_net_id(force=True): raise n_exc.RouterNotCompatibleWithAgent( router_id=router['id']) if router['id'] not in self.router_info: self._process_added_router(router) else: self._process_updated_router(router) def _process_added_router(self, router): self._router_added(router['id'], router) ri = self.router_info[router['id']] ri.router = router ri.process() registry.notify(resources.ROUTER, events.AFTER_CREATE, self, router=ri) self.l3_ext_manager.add_router(self.context, router) def _process_updated_router(self, router): ri = self.router_info[router['id']] is_dvr_snat_agent = (self.conf.agent_mode == lib_const.L3_AGENT_MODE_DVR_SNAT) is_dvr_only_agent = (self.conf.agent_mode in [lib_const.L3_AGENT_MODE_DVR, lib_const.L3_AGENT_MODE_DVR_NO_EXTERNAL]) old_router_ha_interface = ri.router.get(lib_const.HA_INTERFACE_KEY) current_router_ha_interface = router.get(lib_const.HA_INTERFACE_KEY) ha_interface_change = ((old_router_ha_interface is None and current_router_ha_interface is not None) or (old_router_ha_interface is not None and current_router_ha_interface is None)) is_dvr_ha_router = router.get('distributed') and router.get('ha') if is_dvr_snat_agent and is_dvr_ha_router and ha_interface_change: LOG.debug("Removing HA router %s, since it is not bound to " "the current agent, and recreating regular DVR router " "based on service port requirements.", router['id']) if self._safe_router_removed(router['id']): self._process_added_router(router) else: is_ha_router = getattr(ri, 'ha_state', False) # For HA routers check that DB state matches actual state if router.get('ha') and not is_dvr_only_agent and is_ha_router: self.check_ha_state_for_router( router['id'], router.get(l3_constants.HA_ROUTER_STATE_KEY)) ri.router = router registry.notify(resources.ROUTER, events.BEFORE_UPDATE, self, router=ri) ri.process() registry.notify( resources.ROUTER, events.AFTER_UPDATE, self, router=ri) self.l3_ext_manager.update_router(self.context, router) def _resync_router(self, router_update, priority=queue.PRIORITY_SYNC_ROUTERS_TASK): # Don't keep trying to resync if it's failing if router_update.hit_retry_limit(): LOG.warning("Hit retry limit with router update for %s, action %s", router_update.id, router_update.action) if router_update.action != queue.DELETE_ROUTER: LOG.debug("Deleting router %s", router_update.id) self._safe_router_removed(router_update.id) return router_update.timestamp = timeutils.utcnow() router_update.priority = priority router_update.resource = None # Force the agent to resync the router self._queue.add(router_update) def _process_router_update(self): for rp, update in self._queue.each_update_to_next_resource(): LOG.debug("Starting router update for %s, action %s, priority %s", update.id, update.action, update.priority) if update.action == queue.PD_UPDATE: self.pd.process_prefix_update() LOG.debug("Finished a router update for %s", update.id) continue routers = [update.resource] if update.resource else [] not_delete_no_routers = (update.action != queue.DELETE_ROUTER and not routers) related_action = update.action in (queue.DELETE_RELATED_ROUTER, queue.ADD_UPDATE_RELATED_ROUTER) if not_delete_no_routers or related_action: try: update.timestamp = timeutils.utcnow() routers = self.plugin_rpc.get_routers(self.context, [update.id]) except Exception: msg = "Failed to fetch router information for '%s'" LOG.exception(msg, update.id) self._resync_router(update) continue # For a related action, verify the router is still hosted here, # since it could have just been deleted and we don't want to # add it back. if related_action: routers = [r for r in routers if r['id'] == update.id] if not routers: removed = self._safe_router_removed(update.id) if not removed: self._resync_router(update) else: # need to update timestamp of removed router in case # there are older events for the same router in the # processing queue (like events from fullsync) in order to # prevent deleted router re-creation rp.fetched_and_processed(update.timestamp) LOG.debug("Finished a router update for %s", update.id) continue if not self._process_routers_if_compatible(routers, update): self._resync_router(update) continue rp.fetched_and_processed(update.timestamp) LOG.debug("Finished a router update for %s", update.id) def _process_routers_if_compatible(self, routers, update): process_result = True for router in routers: if router['id'] != update.id: # Don't do the work here, instead create a new update and # enqueue it, since there could be another thread working # on it already and we don't want to race. new_action = queue.RELATED_ACTION_MAP.get( update.action, queue.ADD_UPDATE_RELATED_ROUTER) new_update = queue.ResourceUpdate( router['id'], priority=queue.PRIORITY_RELATED_ROUTER, action=new_action) self._queue.add(new_update) LOG.debug('Queued a router update for %(router_id)s ' '(related router %(related_router_id)s). ' 'Original event action %(action)s, ' 'priority %(priority)s. ' 'New event action %(new_action)s, ' 'priority %(new_priority)s', {'router_id': router['id'], 'related_router_id': update.id, 'action': update.action, 'priority': update.priority, 'new_action': new_update.action, 'new_priority': new_update.priority}) continue try: self._process_router_if_compatible(router) except n_exc.RouterNotCompatibleWithAgent as e: log_verbose_exc(e.msg, router) # Was the router previously handled by this agent? if router['id'] in self.router_info: LOG.error("Removing incompatible router '%s'", router['id']) self._safe_router_removed(router['id']) except Exception: log_verbose_exc( "Failed to process compatible router: %s" % update.id, router) process_result = False return process_result def _process_routers_loop(self): LOG.debug("Starting _process_routers_loop") pool = eventlet.GreenPool(size=8) while True: pool.spawn_n(self._process_router_update) # NOTE(kevinbenton): this is set to 1 second because the actual interval # is controlled by a FixedIntervalLoopingCall in neutron/service.py that # is responsible for task execution. @periodic_task.periodic_task(spacing=1, run_immediately=True) def periodic_sync_routers_task(self, context): if not self.fullsync: return LOG.debug("Starting fullsync periodic_sync_routers_task") # self.fullsync is True at this point. If an exception -- caught or # uncaught -- prevents setting it to False below then the next call # to periodic_sync_routers_task will re-enter this code and try again. # Context manager self.namespaces_manager captures a picture of # namespaces *before* fetch_and_sync_all_routers fetches the full list # of routers from the database. This is important to correctly # identify stale ones. try: with self.namespaces_manager as ns_manager: self.fetch_and_sync_all_routers(context, ns_manager) except n_exc.AbortSyncRouters: self.fullsync = True def fetch_and_sync_all_routers(self, context, ns_manager): prev_router_ids = set(self.router_info) curr_router_ids = set() timestamp = timeutils.utcnow() router_ids = [] chunk = [] is_snat_agent = (self.conf.agent_mode == lib_const.L3_AGENT_MODE_DVR_SNAT) try: router_ids = self.plugin_rpc.get_router_ids(context) # fetch routers by chunks to reduce the load on server and to # start router processing earlier for i in range(0, len(router_ids), self.sync_routers_chunk_size): chunk = router_ids[i:i + self.sync_routers_chunk_size] routers = self.plugin_rpc.get_routers(context, chunk) LOG.debug('Processing :%r', routers) for r in routers: curr_router_ids.add(r['id']) ns_manager.keep_router(r['id']) if r.get('distributed'): # need to keep fip namespaces as well ext_net_id = (r['external_gateway_info'] or {}).get( 'network_id') if ext_net_id: ns_manager.keep_ext_net(ext_net_id) elif is_snat_agent and not r.get('ha'): ns_manager.ensure_snat_cleanup(r['id']) update = queue.ResourceUpdate( r['id'], queue.PRIORITY_SYNC_ROUTERS_TASK, resource=r, action=queue.ADD_UPDATE_ROUTER, timestamp=timestamp) self._queue.add(update) except oslo_messaging.MessagingTimeout: if self.sync_routers_chunk_size > SYNC_ROUTERS_MIN_CHUNK_SIZE: self.sync_routers_chunk_size = max( self.sync_routers_chunk_size / 2, SYNC_ROUTERS_MIN_CHUNK_SIZE) LOG.error('Server failed to return info for routers in ' 'required time, decreasing chunk size to: %s', self.sync_routers_chunk_size) else: LOG.error('Server failed to return info for routers in ' 'required time even with min chunk size: %s. ' 'It might be under very high load or ' 'just inoperable', self.sync_routers_chunk_size) raise except oslo_messaging.MessagingException: failed_routers = chunk or router_ids LOG.exception("Failed synchronizing routers '%s' " "due to RPC error", failed_routers) raise n_exc.AbortSyncRouters() self.fullsync = False LOG.debug("periodic_sync_routers_task successfully completed") # adjust chunk size after successful sync if self.sync_routers_chunk_size < SYNC_ROUTERS_MAX_CHUNK_SIZE: self.sync_routers_chunk_size = min( self.sync_routers_chunk_size + SYNC_ROUTERS_MIN_CHUNK_SIZE, SYNC_ROUTERS_MAX_CHUNK_SIZE) # Delete routers that have disappeared since the last sync for router_id in prev_router_ids - curr_router_ids: ns_manager.keep_router(router_id) update = queue.ResourceUpdate(router_id, queue.PRIORITY_SYNC_ROUTERS_TASK, timestamp=timestamp, action=queue.DELETE_ROUTER) self._queue.add(update) @property def context(self): # generate a new request-id on each call to make server side tracking # of RPC calls easier. self._context.request_id = common_context.generate_request_id() return self._context def after_start(self): # Note: the FWaaS' vArmourL3NATAgent is a subclass of L3NATAgent. It # calls this method here. So Removing this after_start() would break # vArmourL3NATAgent. We need to find out whether vArmourL3NATAgent # can have L3NATAgentWithStateReport as its base class instead of # L3NATAgent. eventlet.spawn_n(self._process_routers_loop) LOG.info("L3 agent started") def create_pd_router_update(self): router_id = None update = queue.ResourceUpdate(router_id, queue.PRIORITY_PD_UPDATE, timestamp=timeutils.utcnow(), action=queue.PD_UPDATE) self._queue.add(update) class L3NATAgentWithStateReport(L3NATAgent): def __init__(self, host, conf=None): super(L3NATAgentWithStateReport, self).__init__(host=host, conf=conf) self.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS) self.agent_state = { 'binary': 'neutron-l3-agent', 'host': host, 'availability_zone': self.conf.AGENT.availability_zone, 'topic': topics.L3_AGENT, 'configurations': { 'agent_mode': self.conf.agent_mode, 'handle_internal_only_routers': self.conf.handle_internal_only_routers, 'external_network_bridge': self.conf.external_network_bridge, 'gateway_external_network_id': self.conf.gateway_external_network_id, 'interface_driver': self.conf.interface_driver, 'log_agent_heartbeats': self.conf.AGENT.log_agent_heartbeats}, 'start_flag': True, 'agent_type': lib_const.AGENT_TYPE_L3} report_interval = self.conf.AGENT.report_interval if report_interval: self.heartbeat = loopingcall.FixedIntervalLoopingCall( self._report_state) self.heartbeat.start(interval=report_interval) def _report_state(self): num_ex_gw_ports = 0 num_interfaces = 0 num_floating_ips = 0 router_infos = self.router_info.values() num_routers = len(router_infos) for ri in router_infos: ex_gw_port = ri.get_ex_gw_port() if ex_gw_port: num_ex_gw_ports += 1 num_interfaces += len(ri.router.get(lib_const.INTERFACE_KEY, [])) num_floating_ips += len(ri.router.get(lib_const.FLOATINGIP_KEY, [])) configurations = self.agent_state['configurations'] configurations['routers'] = num_routers configurations['ex_gw_ports'] = num_ex_gw_ports configurations['interfaces'] = num_interfaces configurations['floating_ips'] = num_floating_ips try: agent_status = self.state_rpc.report_state(self.context, self.agent_state, True) if agent_status == agent_consts.AGENT_REVIVED: LOG.info('Agent has just been revived. ' 'Doing a full sync.') self.fullsync = True self.agent_state.pop('start_flag', None) except AttributeError: # This means the server does not support report_state LOG.warning("Neutron server does not support state report. " "State report for this agent will be disabled.") self.heartbeat.stop() return except Exception: LOG.exception("Failed reporting state!") def after_start(self): eventlet.spawn_n(self._process_routers_loop) LOG.info("L3 agent started") # Do the report state before we do the first full sync. self._report_state() self.pd.after_start() def agent_updated(self, context, payload): """Handle the agent_updated notification event.""" self.fullsync = True LOG.info("agent_updated by server side %s!", payload) neutron-12.1.1/neutron/agent/l3/ha_router.py0000664000175000017500000004701413553660047020777 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import shutil import signal import netaddr from neutron_lib.api.definitions import portbindings from neutron_lib import constants as n_consts from neutron_lib.utils import runtime from oslo_log import log as logging from neutron.agent.l3 import namespaces from neutron.agent.l3 import router_info as router from neutron.agent.linux import external_process from neutron.agent.linux import ip_lib from neutron.agent.linux import keepalived from neutron.common import constants as const from neutron.common import utils as common_utils LOG = logging.getLogger(__name__) HA_DEV_PREFIX = 'ha-' IP_MONITOR_PROCESS_SERVICE = 'ip_monitor' SIGTERM_TIMEOUT = 10 # The multiplier is used to compensate execution time of function sending # SIGHUP to keepalived process. The constant multiplies ha_vrrp_advert_int # config option and the result is the throttle delay. THROTTLER_MULTIPLIER = 1.5 class HaRouterNamespace(namespaces.RouterNamespace): """Namespace for HA router. This namespace sets the ip_nonlocal_bind to 0 for HA router namespaces. It does so to prevent sending gratuitous ARPs for interfaces that got VIP removed in the middle of processing. It also disables ipv6 forwarding by default. Forwarding will be enabled during router configuration processing only for the master node. It has to be disabled on all other nodes to avoid sending MLD packets which cause lost connectivity to Floating IPs. """ def create(self): super(HaRouterNamespace, self).create(ipv6_forwarding=False) # HA router namespaces should not have ip_nonlocal_bind enabled ip_lib.set_ip_nonlocal_bind_for_namespace(self.name, 0) class HaRouter(router.RouterInfo): def __init__(self, state_change_callback, *args, **kwargs): super(HaRouter, self).__init__(*args, **kwargs) self.ha_port = None self.keepalived_manager = None self.state_change_callback = state_change_callback def create_router_namespace_object( self, router_id, agent_conf, iface_driver, use_ipv6): return HaRouterNamespace( router_id, agent_conf, iface_driver, use_ipv6) @property def ha_priority(self): return self.router.get('priority', keepalived.HA_DEFAULT_PRIORITY) @property def ha_vr_id(self): return self.router.get('ha_vr_id') @property def ha_state(self): ha_state_path = self.keepalived_manager.get_full_config_file_path( 'state') try: with open(ha_state_path, 'r') as f: return f.read() except (OSError, IOError): LOG.debug('Error while reading HA state for %s', self.router_id) return None @ha_state.setter def ha_state(self, new_state): ha_state_path = self.keepalived_manager.get_full_config_file_path( 'state') try: with open(ha_state_path, 'w') as f: f.write(new_state) except (OSError, IOError): LOG.error('Error while writing HA state for %s', self.router_id) @property def ha_namespace(self): return self.ns_name def is_router_master(self): """this method is normally called before the ha_router object is fully initialized """ if self.router.get('_ha_state') == 'active': return True else: return False def initialize(self, process_monitor): ha_port = self.router.get(n_consts.HA_INTERFACE_KEY) if not ha_port: msg = ("Unable to process HA router %s without HA port" % self.router_id) LOG.exception(msg) raise Exception(msg) super(HaRouter, self).initialize(process_monitor) self.set_ha_port() self._init_keepalived_manager(process_monitor) self.ha_network_added() self.update_initial_state(self.state_change_callback) self.spawn_state_change_monitor(process_monitor) def _init_keepalived_manager(self, process_monitor): self.keepalived_manager = keepalived.KeepalivedManager( self.router['id'], keepalived.KeepalivedConf(), process_monitor, conf_path=self.agent_conf.ha_confs_path, namespace=self.ha_namespace, throttle_restart_value=( self.agent_conf.ha_vrrp_advert_int * THROTTLER_MULTIPLIER)) config = self.keepalived_manager.config interface_name = self.get_ha_device_name() subnets = self.ha_port.get('subnets', []) ha_port_cidrs = [subnet['cidr'] for subnet in subnets] instance = keepalived.KeepalivedInstance( 'BACKUP', interface_name, self.ha_vr_id, ha_port_cidrs, nopreempt=True, advert_int=self.agent_conf.ha_vrrp_advert_int, priority=self.ha_priority, vrrp_health_check_interval=( self.agent_conf.ha_vrrp_health_check_interval), ha_conf_dir=self.keepalived_manager.get_conf_dir()) instance.track_interfaces.append(interface_name) if self.agent_conf.ha_vrrp_auth_password: # TODO(safchain): use oslo.config types when it will be available # in order to check the validity of ha_vrrp_auth_type instance.set_authentication(self.agent_conf.ha_vrrp_auth_type, self.agent_conf.ha_vrrp_auth_password) config.add_instance(instance) def enable_keepalived(self): self.keepalived_manager.spawn() def disable_keepalived(self): if not self.keepalived_manager: LOG.debug('Error while disabling keepalived for %s - no manager', self.router_id) return self.keepalived_manager.disable() conf_dir = self.keepalived_manager.get_conf_dir() shutil.rmtree(conf_dir) def _get_keepalived_instance(self): return self.keepalived_manager.config.get_instance(self.ha_vr_id) def _get_primary_vip(self): return self._get_keepalived_instance().get_primary_vip() def get_ha_device_name(self): return (HA_DEV_PREFIX + self.ha_port['id'])[:self.driver.DEV_NAME_LEN] def ha_network_added(self): interface_name = self.get_ha_device_name() self.driver.plug(self.ha_port['network_id'], self.ha_port['id'], interface_name, self.ha_port['mac_address'], namespace=self.ha_namespace, prefix=HA_DEV_PREFIX, mtu=self.ha_port.get('mtu')) ip_cidrs = common_utils.fixed_ip_cidrs(self.ha_port['fixed_ips']) self.driver.init_l3(interface_name, ip_cidrs, namespace=self.ha_namespace, preserve_ips=[self._get_primary_vip()]) def ha_network_removed(self): if not self.ha_port: LOG.debug('Error while removing HA network for %s - no port', self.router_id) return self.driver.unplug(self.get_ha_device_name(), namespace=self.ha_namespace, prefix=HA_DEV_PREFIX) self.ha_port = None def _add_vips(self, port, interface_name): for ip_cidr in common_utils.fixed_ip_cidrs(port['fixed_ips']): self._add_vip(ip_cidr, interface_name) def _add_vip(self, ip_cidr, interface, scope=None): instance = self._get_keepalived_instance() instance.add_vip(ip_cidr, interface, scope) def _remove_vip(self, ip_cidr): instance = self._get_keepalived_instance() instance.remove_vip_by_ip_address(ip_cidr) def _clear_vips(self, interface): instance = self._get_keepalived_instance() instance.remove_vips_vroutes_by_interface(interface) def _get_cidrs_from_keepalived(self, interface_name): instance = self._get_keepalived_instance() return instance.get_existing_vip_ip_addresses(interface_name) def get_router_cidrs(self, device): return set(self._get_cidrs_from_keepalived(device.name)) def routes_updated(self, old_routes, new_routes): instance = self._get_keepalived_instance() instance.virtual_routes.extra_routes = [ keepalived.KeepalivedVirtualRoute( route['destination'], route['nexthop']) for route in new_routes] super(HaRouter, self).routes_updated(old_routes, new_routes) def _add_default_gw_virtual_route(self, ex_gw_port, interface_name): gateway_ips = self._get_external_gw_ips(ex_gw_port) default_gw_rts = [] instance = self._get_keepalived_instance() for gw_ip in gateway_ips: # TODO(Carl) This is repeated everywhere. A method would # be nice. default_gw = n_consts.IP_ANY[netaddr.IPAddress(gw_ip).version] default_gw_rts.append(keepalived.KeepalivedVirtualRoute( default_gw, gw_ip, interface_name)) instance.virtual_routes.gateway_routes = default_gw_rts def _add_extra_subnet_onlink_routes(self, ex_gw_port, interface_name): extra_subnets = ex_gw_port.get('extra_subnets', []) instance = self._get_keepalived_instance() onlink_route_cidrs = set(s['cidr'] for s in extra_subnets) instance.virtual_routes.extra_subnets = [ keepalived.KeepalivedVirtualRoute( onlink_route_cidr, None, interface_name, scope='link') for onlink_route_cidr in onlink_route_cidrs] def _should_delete_ipv6_lladdr(self, ipv6_lladdr): """Only the master should have any IP addresses configured. Let keepalived manage IPv6 link local addresses, the same way we let it manage IPv4 addresses. If the router is not in the master state, we must delete the address first as it is autoconfigured by the kernel. """ manager = self.keepalived_manager if manager.get_process().active: if self.ha_state != 'master': conf = manager.get_conf_on_disk() managed_by_keepalived = conf and ipv6_lladdr in conf if managed_by_keepalived: return False else: return False return True def _disable_ipv6_addressing_on_interface(self, interface_name): """Disable IPv6 link local addressing on the device and add it as a VIP to keepalived. This means that the IPv6 link local address will only be present on the master. """ device = ip_lib.IPDevice(interface_name, namespace=self.ha_namespace) ipv6_lladdr = ip_lib.get_ipv6_lladdr(device.link.address) if self._should_delete_ipv6_lladdr(ipv6_lladdr): self.driver.configure_ipv6_ra(self.ha_namespace, interface_name, const.ACCEPT_RA_DISABLED) device.addr.flush(n_consts.IP_VERSION_6) else: self.driver.configure_ipv6_ra(self.ha_namespace, interface_name, const.ACCEPT_RA_WITHOUT_FORWARDING) self._remove_vip(ipv6_lladdr) self._add_vip(ipv6_lladdr, interface_name, scope='link') def _add_gateway_vip(self, ex_gw_port, interface_name): self._add_vips(ex_gw_port, interface_name) self._add_default_gw_virtual_route(ex_gw_port, interface_name) self._add_extra_subnet_onlink_routes(ex_gw_port, interface_name) def add_floating_ip(self, fip, interface_name, device): fip_ip = fip['floating_ip_address'] ip_cidr = common_utils.ip_to_cidr(fip_ip) self._add_vip(ip_cidr, interface_name) return n_consts.FLOATINGIP_STATUS_ACTIVE def remove_floating_ip(self, device, ip_cidr): self._remove_vip(ip_cidr) to = common_utils.cidr_to_ip(ip_cidr) if device.addr.list(to=to): super(HaRouter, self).remove_floating_ip(device, ip_cidr) def internal_network_updated(self, interface_name, ip_cidrs, mtu): self.driver.set_mtu(interface_name, mtu, namespace=self.ns_name, prefix=router.INTERNAL_DEV_PREFIX) self._clear_vips(interface_name) self._disable_ipv6_addressing_on_interface(interface_name) for ip_cidr in ip_cidrs: self._add_vip(ip_cidr, interface_name) def _plug_ha_router_port(self, port, name_getter, prefix): port_id = port['id'] interface_name = name_getter(port_id) self.driver.plug(port['network_id'], port_id, interface_name, port['mac_address'], namespace=self.ha_namespace, prefix=prefix, mtu=port.get('mtu')) self._disable_ipv6_addressing_on_interface(interface_name) self._add_vips(port, interface_name) def internal_network_added(self, port): self._plug_ha_router_port( port, self.get_internal_device_name, router.INTERNAL_DEV_PREFIX) def internal_network_removed(self, port): super(HaRouter, self).internal_network_removed(port) interface_name = self.get_internal_device_name(port['id']) self._clear_vips(interface_name) def _get_state_change_monitor_process_manager(self): return external_process.ProcessManager( self.agent_conf, '%s.monitor' % self.router_id, self.ha_namespace, default_cmd_callback=self._get_state_change_monitor_callback()) def _get_state_change_monitor_callback(self): ha_device = self.get_ha_device_name() ha_cidr = self._get_primary_vip() def callback(pid_file): cmd = [ 'neutron-keepalived-state-change', '--router_id=%s' % self.router_id, '--namespace=%s' % self.ha_namespace, '--conf_dir=%s' % self.keepalived_manager.get_conf_dir(), '--monitor_interface=%s' % ha_device, '--monitor_cidr=%s' % ha_cidr, '--pid_file=%s' % pid_file, '--state_path=%s' % self.agent_conf.state_path, '--user=%s' % os.geteuid(), '--group=%s' % os.getegid()] return cmd return callback def spawn_state_change_monitor(self, process_monitor): pm = self._get_state_change_monitor_process_manager() pm.enable() process_monitor.register( self.router_id, IP_MONITOR_PROCESS_SERVICE, pm) def destroy_state_change_monitor(self, process_monitor): if not self.ha_port: LOG.debug('Error while destroying state change monitor for %s - ' 'no port', self.router_id) return pm = self._get_state_change_monitor_process_manager() process_monitor.unregister( self.router_id, IP_MONITOR_PROCESS_SERVICE) pm.disable(sig=str(int(signal.SIGTERM))) try: common_utils.wait_until_true(lambda: not pm.active, timeout=SIGTERM_TIMEOUT) except common_utils.WaitTimeout: pm.disable(sig=str(int(signal.SIGKILL))) def update_initial_state(self, callback): ha_device = ip_lib.IPDevice( self.get_ha_device_name(), self.ha_namespace) addresses = ha_device.addr.list() cidrs = (address['cidr'] for address in addresses) ha_cidr = self._get_primary_vip() state = 'master' if ha_cidr in cidrs else 'backup' self.ha_state = state callback(self.router_id, state) @staticmethod def _gateway_ports_equal(port1, port2): def _get_filtered_dict(d, ignore): return {k: v for k, v in d.items() if k not in ignore} keys_to_ignore = set([portbindings.HOST_ID]) port1_filtered = _get_filtered_dict(port1, keys_to_ignore) port2_filtered = _get_filtered_dict(port2, keys_to_ignore) return port1_filtered == port2_filtered def external_gateway_added(self, ex_gw_port, interface_name): self._plug_external_gateway(ex_gw_port, interface_name, self.ns_name) self._add_gateway_vip(ex_gw_port, interface_name) self._disable_ipv6_addressing_on_interface(interface_name) # Enable RA and IPv6 forwarding only for master instances. This will # prevent backup routers from sending packets to the upstream switch # and disrupt connections. enable = self.ha_state == 'master' self._configure_ipv6_params_on_gw(ex_gw_port, self.ns_name, interface_name, enable) def external_gateway_updated(self, ex_gw_port, interface_name): self._plug_external_gateway( ex_gw_port, interface_name, self.ha_namespace) ip_cidrs = common_utils.fixed_ip_cidrs(self.ex_gw_port['fixed_ips']) for old_gateway_cidr in ip_cidrs: self._remove_vip(old_gateway_cidr) self._add_gateway_vip(ex_gw_port, interface_name) def external_gateway_removed(self, ex_gw_port, interface_name): self._clear_vips(interface_name) if self.ha_state == 'master': super(HaRouter, self).external_gateway_removed(ex_gw_port, interface_name) else: # We are not the master node, so no need to delete ip addresses. self.driver.unplug(interface_name, bridge=self.agent_conf.external_network_bridge, namespace=self.ns_name, prefix=router.EXTERNAL_DEV_PREFIX) def delete(self): if self.process_monitor: self.destroy_state_change_monitor(self.process_monitor) self.disable_keepalived() self.ha_network_removed() super(HaRouter, self).delete() def set_ha_port(self): ha_port = self.router.get(n_consts.HA_INTERFACE_KEY) if not ha_port: return # NOTE: once HA port is set, it MUST remain this value no matter what # the server return. Because there is race condition between l3-agent # side sync router info for processing and server side router deleting. # TODO(liuyulong): make sure router HA ports never change. if not self.ha_port or (self.ha_port and self.ha_port['status'] != ha_port['status']): self.ha_port = ha_port def process(self): super(HaRouter, self).process() self.set_ha_port() LOG.debug("Processing HA router with HA port: %s", self.ha_port) if (self.ha_port and self.ha_port['status'] == n_consts.PORT_STATUS_ACTIVE): self.enable_keepalived() @runtime.synchronized('enable_radvd') def enable_radvd(self, internal_ports=None): if (self.keepalived_manager.get_process().active and self.ha_state == 'master'): super(HaRouter, self).enable_radvd(internal_ports) neutron-12.1.1/neutron/agent/l3/dvr_fip_ns.py0000664000175000017500000005431613553660047021143 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib import os from neutron_lib import constants as lib_constants from neutron_lib.utils import runtime from oslo_concurrency import lockutils from oslo_log import log as logging from oslo_utils import excutils from neutron._i18n import _ from neutron.agent.l3 import fip_rule_priority_allocator as frpa from neutron.agent.l3 import link_local_allocator as lla from neutron.agent.l3 import namespaces from neutron.agent.l3 import router_info from neutron.agent.linux import ip_lib from neutron.agent.linux import iptables_manager from neutron.common import constants from neutron.common import exceptions as n_exc from neutron.common import utils as common_utils from neutron.ipam import utils as ipam_utils LOG = logging.getLogger(__name__) FIP_NS_PREFIX = 'fip-' FIP_EXT_DEV_PREFIX = 'fg-' FIP_2_ROUTER_DEV_PREFIX = 'fpr-' ROUTER_2_FIP_DEV_PREFIX = namespaces.ROUTER_2_FIP_DEV_PREFIX # Route Table index for FIPs FIP_RT_TBL = 16 # Rule priority range for FIPs FIP_PR_START = 32768 FIP_PR_END = FIP_PR_START + 40000 # Fixed rule priority for Fast Path Exit rules FAST_PATH_EXIT_PR = 80000 class FipNamespace(namespaces.Namespace): def __init__(self, ext_net_id, agent_conf, driver, use_ipv6): name = self._get_ns_name(ext_net_id) super(FipNamespace, self).__init__( name, agent_conf, driver, use_ipv6) self._ext_net_id = ext_net_id self.agent_conf = agent_conf self.driver = driver self.use_ipv6 = use_ipv6 self.agent_gateway_port = None self._subscribers = set() path = os.path.join(agent_conf.state_path, 'fip-priorities') self._rule_priorities = frpa.FipRulePriorityAllocator(path, FIP_PR_START, FIP_PR_END) self._iptables_manager = iptables_manager.IptablesManager( namespace=self.get_name(), use_ipv6=self.use_ipv6) path = os.path.join(agent_conf.state_path, 'fip-linklocal-networks') self.local_subnets = lla.LinkLocalAllocator( path, constants.DVR_FIP_LL_CIDR) self.destroyed = False self._stale_fips_checked = False @classmethod def _get_ns_name(cls, ext_net_id): return namespaces.build_ns_name(FIP_NS_PREFIX, ext_net_id) def get_name(self): return self._get_ns_name(self._ext_net_id) def get_ext_device_name(self, port_id): return (FIP_EXT_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN] def get_int_device_name(self, router_id): return (FIP_2_ROUTER_DEV_PREFIX + router_id)[:self.driver.DEV_NAME_LEN] def get_rtr_ext_device_name(self, router_id): return (ROUTER_2_FIP_DEV_PREFIX + router_id)[:self.driver.DEV_NAME_LEN] def has_subscribers(self): return len(self._subscribers) != 0 def subscribe(self, external_net_id): is_first = not self.has_subscribers() self._subscribers.add(external_net_id) return is_first def unsubscribe(self, external_net_id): self._subscribers.discard(external_net_id) return not self.has_subscribers() def allocate_rule_priority(self, floating_ip): return self._rule_priorities.allocate(floating_ip) def deallocate_rule_priority(self, floating_ip): self._rule_priorities.release(floating_ip) @contextlib.contextmanager def _fip_port_lock(self, interface_name): # Use a namespace and port-specific lock semaphore to allow for # concurrency lock_name = 'port-lock-' + self.name + '-' + interface_name with lockutils.lock(lock_name, runtime.SYNCHRONIZED_PREFIX): try: yield except Exception: with excutils.save_and_reraise_exception(): LOG.error('DVR: FIP namespace config failure ' 'for interface %s', interface_name) def create_or_update_gateway_port(self, agent_gateway_port): interface_name = self.get_ext_device_name(agent_gateway_port['id']) # The lock is used to make sure another thread doesn't call to # update the gateway port before we are done initializing things. with self._fip_port_lock(interface_name): is_first = self.subscribe(agent_gateway_port['network_id']) if is_first: # Check for subnets that are populated for the agent # gateway port that was created on the server. if 'subnets' not in agent_gateway_port: self.unsubscribe(agent_gateway_port['network_id']) LOG.debug('DVR: Missing subnet in agent_gateway_port: %s', agent_gateway_port) return self._create_gateway_port(agent_gateway_port, interface_name) else: try: self._update_gateway_port( agent_gateway_port, interface_name) except Exception: # If an exception occurs at this point, then it is # good to clean up the namespace that has been created # and reraise the exception in order to resync the router with excutils.save_and_reraise_exception(): self.unsubscribe(agent_gateway_port['network_id']) self.delete() LOG.exception('DVR: Gateway update in ' 'FIP namespace failed') def _create_gateway_port(self, ex_gw_port, interface_name): """Create namespace, request port creationg from Plugin, then configure Floating IP gateway port. """ self.create() LOG.debug("DVR: adding gateway interface: %s", interface_name) ns_name = self.get_name() self.driver.plug(ex_gw_port['network_id'], ex_gw_port['id'], interface_name, ex_gw_port['mac_address'], bridge=self.agent_conf.external_network_bridge, namespace=ns_name, prefix=FIP_EXT_DEV_PREFIX, mtu=ex_gw_port.get('mtu')) if self.agent_conf.external_network_bridge: # NOTE(Swami): for OVS implementations remove the DEAD VLAN tag # on ports. DEAD VLAN tag is added to each newly created port # and should be removed by L2 agent but if # external_network_bridge is set than external gateway port is # created in this bridge and will not be touched by L2 agent. # This is related to lp#1767422 self.driver.remove_vlan_tag( self.agent_conf.external_network_bridge, interface_name) # Remove stale fg devices ip_wrapper = ip_lib.IPWrapper(namespace=ns_name) devices = ip_wrapper.get_devices() for device in devices: name = device.name if name.startswith(FIP_EXT_DEV_PREFIX) and name != interface_name: LOG.debug('DVR: unplug: %s', name) ext_net_bridge = self.agent_conf.external_network_bridge self.driver.unplug(name, bridge=ext_net_bridge, namespace=ns_name, prefix=FIP_EXT_DEV_PREFIX) ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips']) self.driver.init_l3(interface_name, ip_cidrs, namespace=ns_name, clean_connections=True) gw_cidrs = [sn['cidr'] for sn in ex_gw_port['subnets'] if sn.get('cidr')] self.driver.set_onlink_routes( interface_name, ns_name, ex_gw_port.get('extra_subnets', []), preserve_ips=gw_cidrs, is_ipv6=False) self.agent_gateway_port = ex_gw_port cmd = ['sysctl', '-w', 'net.ipv4.conf.%s.proxy_arp=1' % interface_name] ip_wrapper.netns.execute(cmd, check_exit_code=False) def create(self): LOG.debug("DVR: add fip namespace: %s", self.name) # parent class will ensure the namespace exists and turn-on forwarding super(FipNamespace, self).create() ip_lib.set_ip_nonlocal_bind_for_namespace(self.name, 1, root_namespace=True) # no connection tracking needed in fip namespace self._iptables_manager.ipv4['raw'].add_rule('PREROUTING', '-j CT --notrack') self._iptables_manager.apply() def delete(self): self.destroyed = True self._delete() self.agent_gateway_port = None @namespaces.check_ns_existence def _delete(self): ip_wrapper = ip_lib.IPWrapper(namespace=self.name) for d in ip_wrapper.get_devices(): if d.name.startswith(FIP_2_ROUTER_DEV_PREFIX): # internal link between IRs and FIP NS ip_wrapper.del_veth(d.name) elif d.name.startswith(FIP_EXT_DEV_PREFIX): # single port from FIP NS to br-ext # TODO(carl) Where does the port get deleted? LOG.debug('DVR: unplug: %s', d.name) ext_net_bridge = self.agent_conf.external_network_bridge self.driver.unplug(d.name, bridge=ext_net_bridge, namespace=self.name, prefix=FIP_EXT_DEV_PREFIX) # TODO(mrsmith): add LOG warn if fip count != 0 LOG.debug('DVR: destroy fip namespace: %s', self.name) super(FipNamespace, self).delete() def _check_for_gateway_ip_change(self, new_agent_gateway_port): def get_gateway_ips(gateway_port): gw_ips = {} if gateway_port: for subnet in gateway_port.get('subnets', []): gateway_ip = subnet.get('gateway_ip', None) if gateway_ip: ip_version = common_utils.get_ip_version(gateway_ip) gw_ips[ip_version] = gateway_ip return gw_ips new_gw_ips = get_gateway_ips(new_agent_gateway_port) old_gw_ips = get_gateway_ips(self.agent_gateway_port) return new_gw_ips != old_gw_ips def get_fip_table_indexes(self, ip_version): ns_ipr = ip_lib.IPRule(namespace=self.get_name()) ip_rules_list = ns_ipr.rule.list_rules(ip_version) tbl_index_list = [] for ip_rule in ip_rules_list: tbl_index = ip_rule['table'] if tbl_index in ['local', 'default', 'main']: continue tbl_index_list.append(tbl_index) return tbl_index_list def _add_default_gateway_for_fip(self, gw_ip, ip_device, tbl_index): """Adds default gateway for fip based on the tbl_index passed.""" if tbl_index is None: ip_version = common_utils.get_ip_version(gw_ip) tbl_index_list = self.get_fip_table_indexes(ip_version) for tbl_index in tbl_index_list: ip_device.route.add_gateway(gw_ip, table=tbl_index) else: ip_device.route.add_gateway(gw_ip, table=tbl_index) def _add_rtr_ext_route_rule_to_route_table(self, ri, fip_2_rtr, fip_2_rtr_name): """Creates external route table and adds routing rules.""" # TODO(Swami): Rename the _get_snat_idx function to some # generic name that can be used for SNAT and FIP rt_tbl_index = ri._get_snat_idx(fip_2_rtr) interface_name = self.get_ext_device_name( self.agent_gateway_port['id']) try: # The lock is used to make sure another thread doesn't call to # update the gateway route before we are done initializing things. with self._fip_port_lock(interface_name): self._update_gateway_route(self.agent_gateway_port, interface_name, tbl_index=rt_tbl_index) except Exception: # If an exception occurs at this point, then it is # good to unsubscribe this external network so that # the next call will trigger the interface to be plugged. # We reraise the exception in order to resync the router. with excutils.save_and_reraise_exception(): self.unsubscribe(self.agent_gateway_port['network_id']) self.agent_gateway_port = None LOG.exception('DVR: Gateway setup in FIP namespace ' 'failed') # Now add the filter match rule for the table. ip_rule = ip_lib.IPRule(namespace=self.get_name()) ip_rule.rule.add(ip=str(fip_2_rtr.ip), iif=fip_2_rtr_name, table=rt_tbl_index, priority=rt_tbl_index) def _update_gateway_port(self, agent_gateway_port, interface_name): if (not self.agent_gateway_port or self._check_for_gateway_ip_change(agent_gateway_port)): # Caller already holding lock self._update_gateway_route( agent_gateway_port, interface_name, tbl_index=None) # Cache the agent gateway port after successfully updating # the gateway route, so that checking on self.agent_gateway_port # will be a valid check self.agent_gateway_port = agent_gateway_port gw_cidrs = [sn['cidr'] for sn in agent_gateway_port['subnets'] if sn.get('cidr')] self.driver.set_onlink_routes( interface_name, self.get_name(), agent_gateway_port.get('extra_subnets', []), preserve_ips=gw_cidrs, is_ipv6=False) def _update_gateway_route(self, agent_gateway_port, interface_name, tbl_index): ns_name = self.get_name() ipd = ip_lib.IPDevice(interface_name, namespace=ns_name) # If the 'fg-' device doesn't exist in the namespace then trying # to send advertisements or configure the default route will just # throw exceptions. Unsubscribe this external network so that # the next call will trigger the interface to be plugged. if not ipd.exists(): LOG.warning('DVR: FIP gateway port with interface ' 'name: %(device)s does not exist in the given ' 'namespace: %(ns)s', {'device': interface_name, 'ns': ns_name}) msg = _('DVR: Gateway update route in FIP namespace failed, retry ' 'should be attempted on next call') raise n_exc.FloatingIpSetupException(msg) for fixed_ip in agent_gateway_port['fixed_ips']: ip_lib.send_ip_addr_adv_notif(ns_name, interface_name, fixed_ip['ip_address']) for subnet in agent_gateway_port['subnets']: gw_ip = subnet.get('gateway_ip') if gw_ip: is_gateway_not_in_subnet = not ipam_utils.check_subnet_ip( subnet.get('cidr'), gw_ip) if is_gateway_not_in_subnet: ipd.route.add_route(gw_ip, scope='link') self._add_default_gateway_for_fip(gw_ip, ipd, tbl_index) else: current_gateway = ipd.route.get_gateway() if current_gateway and current_gateway.get('gateway'): ipd.route.delete_gateway(current_gateway.get('gateway')) def _add_cidr_to_device(self, device, ip_cidr): to = common_utils.cidr_to_ip(ip_cidr) if not device.addr.list(to=to): device.addr.add(ip_cidr, add_broadcast=False) def delete_rtr_2_fip_link(self, ri): """Delete the interface between router and FloatingIP namespace.""" LOG.debug("Delete FIP link interfaces for router: %s", ri.router_id) rtr_2_fip_name = self.get_rtr_ext_device_name(ri.router_id) fip_2_rtr_name = self.get_int_device_name(ri.router_id) fip_ns_name = self.get_name() # remove default route entry if ri.rtr_fip_subnet is None: # see if there is a local subnet in the cache ri.rtr_fip_subnet = self.local_subnets.lookup(ri.router_id) if ri.rtr_fip_subnet: rtr_2_fip, fip_2_rtr = ri.rtr_fip_subnet.get_pair() device = ip_lib.IPDevice(rtr_2_fip_name, namespace=ri.ns_name) if device.exists(): device.route.delete_gateway(str(fip_2_rtr.ip), table=FIP_RT_TBL) if self.agent_gateway_port: interface_name = self.get_ext_device_name( self.agent_gateway_port['id']) fg_device = ip_lib.IPDevice( interface_name, namespace=fip_ns_name) if fg_device.exists(): # Remove the fip namespace rules and routes associated to # fpr interface route table. tbl_index = ri._get_snat_idx(fip_2_rtr) fip_rt_rule = ip_lib.IPRule(namespace=fip_ns_name) # Flush the table fg_device.route.flush(lib_constants.IP_VERSION_4, table=tbl_index) fg_device.route.flush(lib_constants.IP_VERSION_6, table=tbl_index) # Remove the rule lookup # /0 addresses for IPv4 and IPv6 are used to pass # IP protocol version information based on a # link-local address IP version. Using any of those # is equivalent to using 'from all' for iproute2. rule_ip = lib_constants.IP_ANY[fip_2_rtr.ip.version] fip_rt_rule.rule.delete(ip=rule_ip, iif=fip_2_rtr_name, table=tbl_index, priority=tbl_index) self.local_subnets.release(ri.router_id) ri.rtr_fip_subnet = None # Check for namespace before deleting the device if not self.destroyed: fns_ip = ip_lib.IPWrapper(namespace=fip_ns_name) if fns_ip.device(fip_2_rtr_name).exists(): fns_ip.del_veth(fip_2_rtr_name) def create_rtr_2_fip_link(self, ri): """Create interface between router and Floating IP namespace.""" LOG.debug("Create FIP link interfaces for router %s", ri.router_id) rtr_2_fip_name = self.get_rtr_ext_device_name(ri.router_id) fip_2_rtr_name = self.get_int_device_name(ri.router_id) fip_ns_name = self.get_name() # add link local IP to interface if ri.rtr_fip_subnet is None: ri.rtr_fip_subnet = self.local_subnets.allocate(ri.router_id) rtr_2_fip, fip_2_rtr = ri.rtr_fip_subnet.get_pair() rtr_2_fip_dev = ip_lib.IPDevice(rtr_2_fip_name, namespace=ri.ns_name) fip_2_rtr_dev = ip_lib.IPDevice(fip_2_rtr_name, namespace=fip_ns_name) if not rtr_2_fip_dev.exists(): ip_wrapper = ip_lib.IPWrapper(namespace=ri.ns_name) rtr_2_fip_dev, fip_2_rtr_dev = ip_wrapper.add_veth(rtr_2_fip_name, fip_2_rtr_name, fip_ns_name) rtr_2_fip_dev.link.set_up() fip_2_rtr_dev.link.set_up() mtu = ri.get_ex_gw_port().get('mtu') if mtu: rtr_2_fip_dev.link.set_mtu(mtu) fip_2_rtr_dev.link.set_mtu(mtu) self._add_cidr_to_device(rtr_2_fip_dev, str(rtr_2_fip)) self._add_cidr_to_device(fip_2_rtr_dev, str(fip_2_rtr)) # Add permanant ARP entries on each side of veth pair rtr_2_fip_dev.neigh.add(common_utils.cidr_to_ip(fip_2_rtr), fip_2_rtr_dev.link.address) fip_2_rtr_dev.neigh.add(common_utils.cidr_to_ip(rtr_2_fip), rtr_2_fip_dev.link.address) self._add_rtr_ext_route_rule_to_route_table(ri, fip_2_rtr, fip_2_rtr_name) # add default route for the link local interface rtr_2_fip_dev.route.add_gateway(str(fip_2_rtr.ip), table=FIP_RT_TBL) def scan_fip_ports(self, ri): # scan system for any existing fip ports rtr_2_fip_interface = self.get_rtr_ext_device_name(ri.router_id) device = ip_lib.IPDevice(rtr_2_fip_interface, namespace=ri.ns_name) if device.exists(): if len(ri.get_router_cidrs(device)): self.rtr_fip_connect = True else: self.rtr_fip_connect = False # On upgrade, there could be stale IP addresses configured, check # and remove them once. # TODO(haleyb): this can go away after a cycle or two if not self._stale_fips_checked: stale_cidrs = ( ip for ip in router_info.RouterInfo.get_router_cidrs( ri, device) if common_utils.is_cidr_host(ip)) for ip_cidr in stale_cidrs: LOG.debug("Removing stale floating ip %s from interface " "%s in namespace %s", ip_cidr, rtr_2_fip_interface, ri.ns_name) device.delete_addr_and_conntrack_state(ip_cidr) self._stale_fips_checked = True neutron-12.1.1/neutron/agent/l3/extensions/0000775000175000017500000000000013553660156020627 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/l3/extensions/__init__.py0000664000175000017500000000000013553660046022724 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/l3/extensions/fip_qos.py0000664000175000017500000004316713553660047022653 0ustar zuulzuul00000000000000# Copyright 2017 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from neutron_lib.agent import l3_extension from neutron_lib import constants from neutron_lib.db import constants as db_consts from neutron_lib.services.qos import constants as qos_consts from oslo_concurrency import lockutils from oslo_log import log as logging from neutron.agent.linux import ip_lib from neutron.agent.linux import l3_tc_lib as tc_lib from neutron.api.rpc.callbacks.consumer import registry from neutron.api.rpc.callbacks import events from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc from neutron.common import constants as n_const from neutron.common import rpc as n_rpc LOG = logging.getLogger(__name__) SUPPORTED_RULES = { qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: { qos_consts.MAX_KBPS: { 'type:range': [0, db_consts.DB_INTEGER_MAX_VALUE]}, qos_consts.MAX_BURST: { 'type:range': [0, db_consts.DB_INTEGER_MAX_VALUE]}, qos_consts.DIRECTION: { 'type:values': constants.VALID_DIRECTIONS} } } # We use the default values to illustrate: # 1. QoS policy does not have some direction `bandwidth_limit`, then we use # the default value. # 2. default value 0 will be treated as no limit. # 3. if one floating IP's rate was changed from x to 0, the extension will do # a tc filter clean procedure. FIP_DEFAULT_RATE = 0 FIP_DEFAULT_BURST = 0 class RouterFipRateLimitMaps(object): def __init__(self): self.qos_policy_fips = collections.defaultdict(dict) self.known_policies = {} self.fip_policies = {} """ The router_floating_ips will be: router_floating_ips = { router_id_1: set(fip1, fip2), router_id_2: set(), # default } """ self.router_floating_ips = {} """ The rate limits dict will be: xxx_ratelimits = { fip_1: (rate, burst), fip_2: (FIP_DEFAULT_RATE, FIP_DEFAULT_BURST), # default fip_3: (1, 2), fip_4: (3, 4), } """ self.ingress_ratelimits = {} self.egress_ratelimits = {} def update_policy(self, policy): self.known_policies[policy.id] = policy def get_policy(self, policy_id): return self.known_policies.get(policy_id) def get_fips(self, policy): return self.qos_policy_fips[policy.id].values() def get_fip_policy(self, fip): policy_id = self.fip_policies.get(fip) return self.get_policy(policy_id) def set_fip_policy(self, fip, policy): """Attach a fip to policy and return any previous policy on fip.""" old_policy = self.get_fip_policy(fip) self.update_policy(policy) self.fip_policies[fip] = policy.id self.qos_policy_fips[policy.id][fip] = fip if old_policy and old_policy.id != policy.id: del self.qos_policy_fips[old_policy.id][fip] def clean_by_fip(self, fip): """Detach fip from policy and cleanup data we don't need anymore.""" if fip in self.fip_policies: del self.fip_policies[fip] for qos_policy_id, fip_dict in self.qos_policy_fips.items(): if fip in fip_dict: del fip_dict[fip] if not fip_dict: self._clean_policy_info(qos_policy_id) return LOG.debug("Floating IP QoS extension did not have " "information on floating IP %s", fip) def _clean_policy_info(self, qos_policy_id): del self.qos_policy_fips[qos_policy_id] del self.known_policies[qos_policy_id] def find_fip_router_id(self, fip): for router_id, ips in self.router_floating_ips.items(): if fip in ips: return router_id class FipQosAgentExtension(l3_extension.L3AgentExtension): SUPPORTED_RESOURCE_TYPES = [resources.QOS_POLICY] def initialize(self, connection, driver_type): """Initialize agent extension.""" self.resource_rpc = resources_rpc.ResourcesPullRpcApi() self.fip_qos_map = RouterFipRateLimitMaps() self._register_rpc_consumers() def consume_api(self, agent_api): self.agent_api = agent_api @lockutils.synchronized('qos-fip') def _handle_notification(self, context, resource_type, qos_policies, event_type): if event_type == events.UPDATED: for qos_policy in qos_policies: self._process_update_policy(qos_policy) def _policy_rules_modified(self, old_policy, policy): return not (len(old_policy.rules) == len(policy.rules) and all(i in old_policy.rules for i in policy.rules)) def _process_update_policy(self, qos_policy): old_qos_policy = self.fip_qos_map.get_policy(qos_policy.id) if old_qos_policy: if self._policy_rules_modified(old_qos_policy, qos_policy): for fip in self.fip_qos_map.get_fips(qos_policy): router_id = self.fip_qos_map.find_fip_router_id(fip) router_info = self._get_router_info(router_id) if not router_info: continue device = self._get_rate_limit_ip_device(router_info) dvr_fip_device = self._get_dvr_fip_device(router_info) if not device and not dvr_fip_device: LOG.debug("Router %s does not have a floating IP " "related device, skipping.", router_id) continue rates = self.get_policy_rates(qos_policy) if device: self.process_ip_rates(fip, device, rates) if dvr_fip_device: self.process_ip_rates( fip, dvr_fip_device, rates, with_cache=False) self.fip_qos_map.update_policy(qos_policy) def _process_reset_fip(self, fip): self.fip_qos_map.clean_by_fip(fip) def _register_rpc_consumers(self): registry.register(self._handle_notification, resources.QOS_POLICY) self._connection = n_rpc.create_connection() endpoints = [resources_rpc.ResourcesPushRpcCallback()] topic = resources_rpc.resource_type_versioned_topic( resources.QOS_POLICY) self._connection.create_consumer(topic, endpoints, fanout=True) self._connection.consume_in_threads() def _get_tc_wrapper(self, device): return tc_lib.FloatingIPTcCommand(device.name, namespace=device.namespace) def process_ip_rate_limit(self, ip, direction, device, rate, burst): rate_limits_direction = direction + "_ratelimits" rate_limits = getattr(self.fip_qos_map, rate_limits_direction, {}) old_rate, old_burst = rate_limits.get(ip, (FIP_DEFAULT_RATE, FIP_DEFAULT_BURST)) if old_rate == rate and old_burst == burst: # Two possibilities here: # 1. Floating IP rate limit does not change. # 2. Floating IP bandwidth does not limit. return tc_wrapper = self._get_tc_wrapper(device) if rate == FIP_DEFAULT_RATE and burst == FIP_DEFAULT_BURST: # According to the agreements of default value definition, # floating IP bandwidth was changed to default value (no limit). # NOTE: l3_tc_lib will ignore exception FilterIDForIPNotFound. tc_wrapper.clear_ip_rate_limit(direction, ip) rate_limits.pop(ip, None) return # Finally just set it, l3_tc_lib will clean the old rules if exists. tc_wrapper.set_ip_rate_limit(direction, ip, rate, burst) rate_limits[ip] = (rate, burst) def _get_rate_limit_ip_device(self, router_info): ex_gw_port = router_info.get_ex_gw_port() if not ex_gw_port: return agent_mode = router_info.agent_conf.agent_mode is_distributed_router = router_info.router.get('distributed') if is_distributed_router and agent_mode == ( constants.L3_AGENT_MODE_DVR_SNAT): # DVR edge (or DVR edge ha) router if not router_info._is_this_snat_host(): return name = router_info.get_snat_external_device_interface_name( ex_gw_port) else: # DVR local router # Legacy/HA router name = router_info.get_external_device_interface_name(ex_gw_port) if not name: # DVR local router in dvr_no_external agent mode may not have # such rfp-device. return namespace = router_info.get_gw_ns_name() return ip_lib.IPDevice(name, namespace=namespace) def _remove_ip_rate_limit_cache(self, ip, direction): rate_limits_direction = direction + "_ratelimits" rate_limits = getattr(self.fip_qos_map, rate_limits_direction, {}) rate_limits.pop(ip, None) def _remove_fip_rate_limit(self, device, fip_ip): tc_wrapper = self._get_tc_wrapper(device) for direction in constants.VALID_DIRECTIONS: if device.exists(): tc_wrapper.clear_ip_rate_limit(direction, fip_ip) self._remove_ip_rate_limit_cache(fip_ip, direction) def get_fip_qos_rates(self, context, fip, policy_id): if policy_id is None: self._process_reset_fip(fip) # process_ip_rate_limit will treat value 0 as # cleaning the tc filters if exits or no action. return {constants.INGRESS_DIRECTION: {"rate": FIP_DEFAULT_RATE, "burst": FIP_DEFAULT_BURST}, constants.EGRESS_DIRECTION: {"rate": FIP_DEFAULT_RATE, "burst": FIP_DEFAULT_BURST}} policy = self.resource_rpc.pull( context, resources.QOS_POLICY, policy_id) self.fip_qos_map.set_fip_policy(fip, policy) return self.get_policy_rates(policy) def get_policy_rates(self, policy): rates = {} for rule in policy.rules: # NOTE(liuyulong): for now, the L3 agent floating IP QoS # extension only uses ``bandwidth_limit`` rules.. if rule.rule_type in SUPPORTED_RULES: if rule.direction not in rates: rates[rule.direction] = {"rate": rule.max_kbps, "burst": rule.max_burst_kbps} # The return rates dict must contain all directions. If there is no # one specific direction QoS rule, use the default values. for direction in constants.VALID_DIRECTIONS: if direction not in rates: LOG.debug("Policy %(id)s does not have '%(direction)s' " "bandwidth_limit rule, use default value instead.", {"id": policy.id, "direction": direction}) rates[direction] = {"rate": FIP_DEFAULT_RATE, "burst": FIP_DEFAULT_BURST} return rates def process_ip_rates(self, fip, device, rates, with_cache=True): for direction in constants.VALID_DIRECTIONS: rate = rates.get(direction) if with_cache: self.process_ip_rate_limit( fip, direction, device, rate['rate'], rate['burst']) else: tc_wrapper = self._get_tc_wrapper(device) if (rate['rate'] == FIP_DEFAULT_RATE and rate['burst'] == FIP_DEFAULT_BURST): # Default value is no limit tc_wrapper.clear_ip_rate_limit(direction, fip) else: tc_wrapper.set_ip_rate_limit(direction, fip, rate['rate'], rate['burst']) def _get_dvr_fip_device(self, router_info): is_distributed_router = router_info.router.get('distributed') agent_mode = router_info.agent_conf.agent_mode if is_distributed_router and agent_mode == ( constants.L3_AGENT_MODE_DVR_SNAT): gw_port = router_info.get_ex_gw_port() if gw_port and router_info.fip_ns: rfp_dev_name = router_info.get_external_device_interface_name( gw_port) if router_info.router_namespace.exists() and rfp_dev_name: return ip_lib.IPDevice( rfp_dev_name, namespace=router_info.ns_name) def process_floating_ip_addresses(self, context, router_info): # Loop all the router floating ips, the corresponding floating IP tc # rules will be configured: # 1. for legacy and HA router, it will be all floating IPs to qg-device # of qrouter-namespace in (all ha router hosted) network node. # 2. for dvr router, we can do this simple. No matter the agent # type is dvr or dvr_snat, we can just set all the # floating IP tc rules to the corresponding device: # 2.1 for dvr local router in compute node: # the namespace is qrouter-x, and the device is rfp-device. # 2.2 for dvr edge (ha) router in network node: # the namespace is snat-x, and the device is qg-device. # 3. for dvr local router, if agent_mod is dvr_no_external, no # floating IP rules will be configured. # 4. for dvr router in snat node, we should process the floating # IP QoS again in qrouter-namespace to cover the mixed deployment # with nova-compute scenario. is_distributed_router = router_info.router.get('distributed') agent_mode = router_info.agent_conf.agent_mode LOG.debug("Start processing floating IP QoS for " "router %(router_id)s, router " "distributed: %(distributed)s, " "agent mode: %(agent_mode)s", {"router_id": router_info.router_id, "distributed": is_distributed_router, "agent_mode": agent_mode}) if is_distributed_router and agent_mode == ( n_const.L3_AGENT_MODE_DVR_NO_EXTERNAL): # condition 3: dvr local router and dvr_no_external agent return device = self._get_rate_limit_ip_device(router_info) dvr_fip_device = self._get_dvr_fip_device(router_info) if not device and not dvr_fip_device: LOG.debug("No relevant QoS device found " "for router: %s", router_info.router_id) return floating_ips = router_info.get_floating_ips() current_fips = self.fip_qos_map.router_floating_ips.get( router_info.router_id, set()) new_fips = set() for fip in floating_ips: fip_addr = fip['floating_ip_address'] new_fips.add(fip_addr) rates = self.get_fip_qos_rates(context, fip_addr, fip.get(qos_consts.QOS_POLICY_ID)) if device: self.process_ip_rates(fip_addr, device, rates) if dvr_fip_device: # NOTE(liuyulong): for scenario 4 (mixed dvr_snat and compute # node), because floating IP qos rates may have been # processed in dvr snat-namespace, so here the cache was # already set. We just install the rules to the device in # qrouter-namesapce. self.process_ip_rates( fip_addr, dvr_fip_device, rates, with_cache=False) self.fip_qos_map.router_floating_ips[router_info.router_id] = new_fips fips_removed = current_fips - new_fips for fip in fips_removed: if device: self._remove_fip_rate_limit(device, fip) if dvr_fip_device: self._remove_fip_rate_limit(dvr_fip_device, fip) self._process_reset_fip(fip) def _get_router_info(self, router_id): router_info = self.agent_api.get_router_info(router_id) if router_info: return router_info LOG.debug("Router %s is not managed by this agent. " "It was possibly deleted concurrently.", router_id) @lockutils.synchronized('qos-fip') def add_router(self, context, data): router_info = self._get_router_info(data['id']) if router_info: self.process_floating_ip_addresses(context, router_info) @lockutils.synchronized('qos-fip') def update_router(self, context, data): router_info = self._get_router_info(data['id']) if router_info: self.process_floating_ip_addresses(context, router_info) def delete_router(self, context, data): # NOTE(liuyulong): to delete the router, you need to disassociate the # floating IP first, so the update_router has done the cache clean. pass def ha_state_change(self, context, data): pass neutron-12.1.1/neutron/agent/l3/dvr_local_router.py0000664000175000017500000011100213553660047022341 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import binascii import collections import netaddr from neutron_lib import constants as lib_constants from neutron_lib import exceptions from oslo_log import log as logging from oslo_utils import excutils import six from neutron.agent.l3 import dvr_fip_ns from neutron.agent.l3 import dvr_router_base from neutron.agent.linux import ip_lib from neutron.common import constants as n_const from neutron.common import utils as common_utils LOG = logging.getLogger(__name__) # xor-folding mask used for IPv6 rule index MASK_30 = 0x3fffffff # Tracks the arp entry cache Arp_entry = collections.namedtuple( 'Arp_entry', 'ip mac subnet_id operation') class DvrLocalRouter(dvr_router_base.DvrRouterBase): def __init__(self, host, *args, **kwargs): super(DvrLocalRouter, self).__init__(host, *args, **kwargs) self.floating_ips_dict = {} # Linklocal subnet for router and floating IP namespace link self.rtr_fip_subnet = None self.rtr_fip_connect = False self.fip_ns = None self._pending_arp_set = set() def migrate_centralized_floating_ip(self, fip, interface_name, device): # Remove the centralized fip first and then add fip to the host ip_cidr = common_utils.ip_to_cidr(fip['floating_ip_address']) self.floating_ip_removed_dist(ip_cidr) # Now add the floating_ip to the current host self.floating_ip_added_dist(fip, ip_cidr) def floating_forward_rules(self, fip): """Override this function defined in router_info for dvr routers.""" if not self.fip_ns: return [] if fip.get(n_const.DVR_SNAT_BOUND): return [] # For dvr_no_external node should not process any floating IP # iptables rules. if (self.agent_conf.agent_mode == lib_constants.L3_AGENT_MODE_DVR_NO_EXTERNAL): return [] fixed_ip = fip['fixed_ip_address'] floating_ip = fip['floating_ip_address'] rtr_2_fip_name = self.fip_ns.get_rtr_ext_device_name(self.router_id) dnat_from_floatingip_to_fixedip = ( 'PREROUTING', '-d %s/32 -i %s -j DNAT --to-destination %s' % ( floating_ip, rtr_2_fip_name, fixed_ip)) to_source = '-s %s/32 -j SNAT --to-source %s' % (fixed_ip, floating_ip) if self.iptables_manager.random_fully: to_source += ' --random-fully' snat_from_fixedip_to_floatingip = ('float-snat', to_source) return [dnat_from_floatingip_to_fixedip, snat_from_fixedip_to_floatingip] def floating_mangle_rules(self, floating_ip, fixed_ip, internal_mark): if not self.fip_ns: return [] rtr_2_fip_name = self.fip_ns.get_rtr_ext_device_name(self.router_id) mark_traffic_to_floating_ip = ( 'floatingip', '-d %s/32 -i %s -j MARK --set-xmark %s' % ( floating_ip, rtr_2_fip_name, internal_mark)) mark_traffic_from_fixed_ip = ( 'FORWARD', '-s %s/32 -j $float-snat' % fixed_ip) return [mark_traffic_to_floating_ip, mark_traffic_from_fixed_ip] def add_centralized_floatingip(self, fip, fip_cidr): """Implements floatingip in centralized network node. This is a dummy function and is overridden in dvr_edge_router.py to add the floatingip function to the snat namespace. """ def remove_centralized_floatingip(self, fip_cidr): """Removes floatingip from centralized network node. This is a dummy function and is overridden in dvr_edge_router.py to remove the floatingip function from the snat namespace. """ def floating_ip_added_dist(self, fip, fip_cidr): """Add floating IP to respective namespace based on agent mode.""" if fip.get(n_const.DVR_SNAT_BOUND): floating_ip_status = self.add_centralized_floatingip(fip, fip_cidr) return floating_ip_status if not self._check_if_floatingip_bound_to_host(fip): # TODO(Swami): Need to figure out what status # should be returned when the floating IP is # not destined for this agent and if the floating # IP is configured in a different compute host. # This should not happen once we fix the server # side code, but still a check to make sure if # the floating IP is intended for this host should # be done. return # dvr_no_external host should not process any floating IP route rules. if (self.agent_conf.agent_mode == lib_constants.L3_AGENT_MODE_DVR_NO_EXTERNAL): return floating_ip = fip['floating_ip_address'] fixed_ip = fip['fixed_ip_address'] self._add_floating_ip_rule(floating_ip, fixed_ip) fip_2_rtr_name = self.fip_ns.get_int_device_name(self.router_id) #Add routing rule in fip namespace fip_ns_name = self.fip_ns.get_name() if self.rtr_fip_subnet is None: self.rtr_fip_subnet = self.fip_ns.local_subnets.allocate( self.router_id) rtr_2_fip, __ = self.rtr_fip_subnet.get_pair() device = ip_lib.IPDevice(fip_2_rtr_name, namespace=fip_ns_name) device.route.add_route(fip_cidr, str(rtr_2_fip.ip)) interface_name = ( self.fip_ns.get_ext_device_name( self.fip_ns.agent_gateway_port['id'])) ip_lib.send_ip_addr_adv_notif(fip_ns_name, interface_name, floating_ip) return lib_constants.FLOATINGIP_STATUS_ACTIVE def _add_floating_ip_rule(self, floating_ip, fixed_ip): rule_pr = self.fip_ns.allocate_rule_priority(floating_ip) self.floating_ips_dict[floating_ip] = (fixed_ip, rule_pr) ip_rule = ip_lib.IPRule(namespace=self.ns_name) ip_rule.rule.add(ip=fixed_ip, table=dvr_fip_ns.FIP_RT_TBL, priority=rule_pr) def _remove_floating_ip_rule(self, floating_ip): if floating_ip in self.floating_ips_dict: fixed_ip, rule_pr = self.floating_ips_dict[floating_ip] ip_rule = ip_lib.IPRule(namespace=self.ns_name) ip_rule.rule.delete(ip=fixed_ip, table=dvr_fip_ns.FIP_RT_TBL, priority=rule_pr) self.fip_ns.deallocate_rule_priority(floating_ip) #TODO(rajeev): Handle else case - exception/log? def floating_ip_removed_dist(self, fip_cidr): """Remove floating IP from FIP namespace.""" centralized_fip_cidrs = self.get_centralized_fip_cidr_set() if fip_cidr in centralized_fip_cidrs: self.remove_centralized_floatingip(fip_cidr) return floating_ip = fip_cidr.split('/')[0] fip_2_rtr_name = self.fip_ns.get_int_device_name(self.router_id) if self.rtr_fip_subnet is None: self.rtr_fip_subnet = self.fip_ns.local_subnets.lookup( self.router_id) if self.rtr_fip_subnet: rtr_2_fip, fip_2_rtr = self.rtr_fip_subnet.get_pair() fip_ns_name = self.fip_ns.get_name() self._remove_floating_ip_rule(floating_ip) device = ip_lib.IPDevice(fip_2_rtr_name, namespace=fip_ns_name) device.route.delete_route(fip_cidr, str(rtr_2_fip.ip)) return device def floating_ip_moved_dist(self, fip): """Handle floating IP move between fixed IPs.""" floating_ip = fip['floating_ip_address'] self._remove_floating_ip_rule(floating_ip) self._add_floating_ip_rule(floating_ip, fip['fixed_ip_address']) def add_floating_ip(self, fip, interface_name, device): # Special Handling for DVR - update FIP namespace ip_cidr = common_utils.ip_to_cidr(fip['floating_ip_address']) return self.floating_ip_added_dist(fip, ip_cidr) def remove_floating_ip(self, device, ip_cidr): fip_2_rtr_device = self.floating_ip_removed_dist(ip_cidr) if fip_2_rtr_device: fip_2_rtr_device.delete_conntrack_state(ip_cidr) def move_floating_ip(self, fip): self.floating_ip_moved_dist(fip) return lib_constants.FLOATINGIP_STATUS_ACTIVE def _get_internal_port(self, subnet_id): """Return internal router port based on subnet_id.""" router_ports = self.router.get(lib_constants.INTERFACE_KEY, []) for port in router_ports: fips = port['fixed_ips'] for f in fips: if f['subnet_id'] == subnet_id: return port def _cache_arp_entry(self, ip, mac, subnet_id, operation): """Cache the arp entries if device not ready.""" arp_entry_tuple = Arp_entry(ip=ip, mac=mac, subnet_id=subnet_id, operation=operation) self._pending_arp_set.add(arp_entry_tuple) def _process_arp_cache_for_internal_port(self, subnet_id): """Function to process the cached arp entries.""" arp_remove = set() for arp_entry in self._pending_arp_set: if subnet_id == arp_entry.subnet_id: try: state = self._update_arp_entry( arp_entry.ip, arp_entry.mac, arp_entry.subnet_id, arp_entry.operation) except Exception: state = False if state: # If the arp update was successful, then # go ahead and add it to the remove set arp_remove.add(arp_entry) self._pending_arp_set -= arp_remove def _delete_arp_cache_for_internal_port(self, subnet_id): """Function to delete the cached arp entries.""" arp_delete = set() for arp_entry in self._pending_arp_set: if subnet_id == arp_entry.subnet_id: arp_delete.add(arp_entry) self._pending_arp_set -= arp_delete def _update_arp_entry(self, ip, mac, subnet_id, operation): """Add or delete arp entry into router namespace for the subnet.""" port = self._get_internal_port(subnet_id) # update arp entry only if the subnet is attached to the router if not port: return False try: # TODO(mrsmith): optimize the calls below for bulk calls interface_name = self.get_internal_device_name(port['id']) device = ip_lib.IPDevice(interface_name, namespace=self.ns_name) if device.exists(): if operation == 'add': device.neigh.add(ip, mac) elif operation == 'delete': device.neigh.delete(ip, mac) return True else: if operation == 'add': LOG.warning("Device %s does not exist so ARP entry " "cannot be updated, will cache " "information to be applied later " "when the device exists", device) self._cache_arp_entry(ip, mac, subnet_id, operation) return False except Exception: with excutils.save_and_reraise_exception(): LOG.exception("DVR: Failed updating arp entry") def _set_subnet_arp_info(self, subnet_id): """Set ARP info retrieved from Plugin for existing ports.""" # TODO(Carl) Can we eliminate the need to make this RPC while # processing a router. subnet_ports = self.agent.get_ports_by_subnet(subnet_id) ignored_device_owners = ( lib_constants.ROUTER_INTERFACE_OWNERS + tuple(common_utils.get_dvr_allowed_address_pair_device_owners())) for p in subnet_ports: if p['device_owner'] not in ignored_device_owners: for fixed_ip in p['fixed_ips']: self._update_arp_entry(fixed_ip['ip_address'], p['mac_address'], subnet_id, 'add') self._process_arp_cache_for_internal_port(subnet_id) @staticmethod def _get_snat_idx(ip_cidr): """Generate index for DVR snat rules and route tables. The index value has to be 32 bits or less but more than the system generated entries i.e. 32768. For IPv4 use the numeric value of the cidr. For IPv6 generate a crc32 bit hash and xor-fold to 30 bits. Use the freed range to extend smaller values so that they become greater than system generated entries. """ net = netaddr.IPNetwork(ip_cidr) if net.version == 6: if isinstance(ip_cidr, six.text_type): ip_cidr = ip_cidr.encode() # Needed for Python 3.x # the crc32 & 0xffffffff is for Python 2.6 and 3.0 compatibility snat_idx = binascii.crc32(ip_cidr) & 0xffffffff # xor-fold the hash to reserve upper range to extend smaller values snat_idx = (snat_idx >> 30) ^ (snat_idx & MASK_30) if snat_idx < 32768: snat_idx = snat_idx + MASK_30 else: snat_idx = net.value return snat_idx def _delete_gateway_device_if_exists(self, ns_ip_device, gw_ip_addr, snat_idx): try: ns_ip_device.route.delete_gateway(gw_ip_addr, table=snat_idx) except exceptions.DeviceNotFoundError: pass def _stale_ip_rule_cleanup(self, ns_ipr, ns_ipd, ip_version): ip_rules_list = ns_ipr.rule.list_rules(ip_version) snat_table_list = [] for ip_rule in ip_rules_list: snat_table = ip_rule['table'] priority = ip_rule['priority'] if snat_table in ['local', 'default', 'main']: continue if (ip_version == lib_constants.IP_VERSION_4 and snat_table in range(dvr_fip_ns.FIP_PR_START, dvr_fip_ns.FIP_PR_END)): continue gateway_cidr = ip_rule['from'] ns_ipr.rule.delete(ip=gateway_cidr, table=snat_table, priority=priority) snat_table_list.append(snat_table) for tb in snat_table_list: ns_ipd.route.flush(ip_version, table=tb) def gateway_redirect_cleanup(self, rtr_interface): ns_ipr = ip_lib.IPRule(namespace=self.ns_name) ns_ipd = ip_lib.IPDevice(rtr_interface, namespace=self.ns_name) self._stale_ip_rule_cleanup(ns_ipr, ns_ipd, lib_constants.IP_VERSION_4) self._stale_ip_rule_cleanup(ns_ipr, ns_ipd, lib_constants.IP_VERSION_6) def _snat_redirect_modify(self, gateway, sn_port, sn_int, is_add): """Adds or removes rules and routes for SNAT redirection.""" cmd = ['net.ipv4.conf.%s.send_redirects=0' % sn_int] try: ns_ipr = ip_lib.IPRule(namespace=self.ns_name) ns_ipd = ip_lib.IPDevice(sn_int, namespace=self.ns_name) for port_fixed_ip in sn_port['fixed_ips']: # Iterate and find the gateway IP address matching # the IP version port_ip_addr = port_fixed_ip['ip_address'] port_ip_vers = netaddr.IPAddress(port_ip_addr).version for gw_fixed_ip in gateway['fixed_ips']: gw_ip_addr = gw_fixed_ip['ip_address'] if netaddr.IPAddress(gw_ip_addr).version == port_ip_vers: sn_port_cidr = common_utils.ip_to_cidr( port_ip_addr, port_fixed_ip['prefixlen']) snat_idx = self._get_snat_idx(sn_port_cidr) if is_add: ns_ipd.route.add_gateway(gw_ip_addr, table=snat_idx) ns_ipr.rule.add(ip=sn_port_cidr, table=snat_idx, priority=snat_idx) ip_lib.sysctl(cmd, namespace=self.ns_name) else: self._delete_gateway_device_if_exists(ns_ipd, gw_ip_addr, snat_idx) ns_ipr.rule.delete(ip=sn_port_cidr, table=snat_idx, priority=snat_idx) except Exception: if is_add: exc = 'DVR: error adding redirection logic' else: exc = ('DVR: snat remove failed to clear the rule ' 'and device') LOG.exception(exc) def _snat_redirect_add(self, gateway, sn_port, sn_int): """Adds rules and routes for SNAT redirection.""" self._snat_redirect_modify(gateway, sn_port, sn_int, is_add=True) def _snat_redirect_remove(self, gateway, sn_port, sn_int): """Removes rules and routes for SNAT redirection.""" self._snat_redirect_modify(gateway, sn_port, sn_int, is_add=False) def internal_network_added(self, port): super(DvrLocalRouter, self).internal_network_added(port) # NOTE: The following function _set_subnet_arp_info # should be called to dynamically populate the arp # entries for the dvr services ports into the router # namespace. This does not have dependency on the # external_gateway port or the agent_mode. ex_gw_port = self.get_ex_gw_port() for subnet in port['subnets']: self._set_subnet_arp_info(subnet['id']) if ex_gw_port: # Check for address_scopes here if gateway exists. address_scopes_match = self._check_if_address_scopes_match( port, ex_gw_port) if (address_scopes_match and (self.agent_conf.agent_mode in [lib_constants.L3_AGENT_MODE_DVR, lib_constants.L3_AGENT_MODE_DVR_SNAT])): self._add_interface_routing_rule_to_router_ns(port) self._add_interface_route_to_fip_ns(port) self._snat_redirect_add_from_port(port) def _snat_redirect_add_from_port(self, port): ex_gw_port = self.get_ex_gw_port() if not ex_gw_port: return address_scopes_match = self._check_if_address_scopes_match( port, ex_gw_port) if (address_scopes_match and (self.agent_conf.agent_mode in [lib_constants.L3_AGENT_MODE_DVR, lib_constants.L3_AGENT_MODE_DVR_SNAT])): return sn_port = self.get_snat_port_for_internal_port(port) if not sn_port: return interface_name = self.get_internal_device_name(port['id']) self._snat_redirect_add(sn_port, port, interface_name) def _dvr_internal_network_removed(self, port): # Clean up the cached arp entries related to the port subnet for subnet in port['subnets']: self._delete_arp_cache_for_internal_port(subnet) if not self.ex_gw_port: return # Delete DVR address_scope static route for the removed interface # Check for address_scopes here. address_scopes_match = self._check_if_address_scopes_match( port, self.ex_gw_port) if (address_scopes_match and (self.agent_conf.agent_mode in [lib_constants.L3_AGENT_MODE_DVR, lib_constants.L3_AGENT_MODE_DVR_SNAT])): self._delete_interface_route_in_fip_ns(port) self._delete_interface_routing_rule_in_router_ns(port) # If address scopes match there is no need to cleanup the # snat redirect rules, hence return here. return sn_port = self.get_snat_port_for_internal_port(port, self.snat_ports) if not sn_port: return # DVR handling code for SNAT interface_name = self.get_internal_device_name(port['id']) self._snat_redirect_remove(sn_port, port, interface_name) def internal_network_removed(self, port): self._dvr_internal_network_removed(port) super(DvrLocalRouter, self).internal_network_removed(port) def get_floating_agent_gw_interface(self, ext_net_id): """Filter Floating Agent GW port for the external network.""" fip_ports = self.router.get(n_const.FLOATINGIP_AGENT_INTF_KEY, []) return next( (p for p in fip_ports if p['network_id'] == ext_net_id), None) def get_snat_external_device_interface_name(self, port_id): pass def get_external_device_interface_name(self, ex_gw_port): fip_int = self.fip_ns.get_int_device_name(self.router_id) if ip_lib.device_exists(fip_int, namespace=self.fip_ns.get_name()): return self.fip_ns.get_rtr_ext_device_name(self.router_id) def enable_snat_redirect_rules(self, ex_gw_port): for p in self.internal_ports: gateway = self.get_snat_port_for_internal_port(p) if not gateway: continue address_scopes_match = self._check_if_address_scopes_match( p, ex_gw_port) if (not address_scopes_match or (self.agent_conf.agent_mode == lib_constants.L3_AGENT_MODE_DVR_NO_EXTERNAL)): internal_dev = self.get_internal_device_name(p['id']) self._snat_redirect_add(gateway, p, internal_dev) def disable_snat_redirect_rules(self, ex_gw_port): for p in self.internal_ports: gateway = self.get_snat_port_for_internal_port( p, self.snat_ports) if not gateway: continue address_scopes_match = self._check_if_address_scopes_match( p, ex_gw_port) if (not address_scopes_match or (self.agent_conf.agent_mode == lib_constants.L3_AGENT_MODE_DVR_NO_EXTERNAL)): internal_dev = self.get_internal_device_name(p['id']) self._snat_redirect_remove(gateway, p, internal_dev) def external_gateway_added(self, ex_gw_port, interface_name): # TODO(Carl) Refactor external_gateway_added/updated/removed to use # super class implementation where possible. Looks like preserve_ips, # and ns_name are the key differences. cmd = ['net.ipv4.conf.all.send_redirects=0'] ip_lib.sysctl(cmd, namespace=self.ns_name) self.enable_snat_redirect_rules(ex_gw_port) for port in self.get_snat_interfaces(): for ip in port['fixed_ips']: self._update_arp_entry(ip['ip_address'], port['mac_address'], ip['subnet_id'], 'add') def external_gateway_updated(self, ex_gw_port, interface_name): pass def process_floating_ip_nat_rules(self): """Configure NAT rules for the router's floating IPs. Configures iptables rules for the floating ips of the given router """ # Clear out all iptables rules for floating ips self.iptables_manager.ipv4['nat'].clear_rules_by_tag('floating_ip') floating_ips = self.get_floating_ips() # Loop once to ensure that floating ips are configured. for fip in floating_ips: # If floating IP is snat_bound, then the iptables rule should # not be installed to qrouter namespace, since the mixed snat # namespace may already install it. if fip.get(lib_constants.DVR_SNAT_BOUND): continue # Rebuild iptables rules for the floating ip. for chain, rule in self.floating_forward_rules(fip): self.iptables_manager.ipv4['nat'].add_rule( chain, rule, tag='floating_ip') self.iptables_manager.apply() def external_gateway_removed(self, ex_gw_port, interface_name): # TODO(Carl) Should this be calling process_snat_dnat_for_fip? self.process_floating_ip_nat_rules() if self.fip_ns: to_fip_interface_name = ( self.get_external_device_interface_name(ex_gw_port)) self.process_floating_ip_addresses(to_fip_interface_name) # Remove the router to fip namespace connection after the # gateway is removed. self.fip_ns.delete_rtr_2_fip_link(self) self.rtr_fip_connect = False # NOTE:_snat_redirect_remove should be only called when the # gateway is cleared and should not be called when the gateway # is moved or rescheduled. if not self.router.get('gw_port'): self.disable_snat_redirect_rules(ex_gw_port) def _handle_router_snat_rules(self, ex_gw_port, interface_name): """Configures NAT rules for Floating IPs for DVR.""" self.iptables_manager.ipv4['nat'].empty_chain('POSTROUTING') self.iptables_manager.ipv4['nat'].empty_chain('snat') ex_gw_port = self.get_ex_gw_port() if not ex_gw_port: return ext_device_name = self.get_external_device_interface_name(ex_gw_port) floatingips = self.get_floating_ips() if not ext_device_name or not floatingips: # Without router to fip device, or without any floating ip, # the snat rules should not be added return # Add back the jump to float-snat self.iptables_manager.ipv4['nat'].add_rule('snat', '-j $float-snat') rule = self._prevent_snat_for_internal_traffic_rule(ext_device_name) self.iptables_manager.ipv4['nat'].add_rule(*rule) def _get_address_scope_mark(self): # Prepare address scope iptables rule for internal ports internal_ports = self.router.get(lib_constants.INTERFACE_KEY, []) ports_scopemark = self._get_port_devicename_scopemark( internal_ports, self.get_internal_device_name) # DVR local router will use rfp port as external port ext_port = self.get_ex_gw_port() if not ext_port: return ports_scopemark ext_device_name = self.get_external_device_interface_name(ext_port) if not ext_device_name: return ports_scopemark ext_scope = self._get_external_address_scope() ext_scope_mark = self.get_address_scope_mark_mask(ext_scope) ports_scopemark[lib_constants.IP_VERSION_4][ext_device_name] = ( ext_scope_mark) return ports_scopemark def _check_if_floatingip_bound_to_host(self, fip): """Check if the floating IP is bound to this host.""" return self.host in (fip.get('host'), fip.get('dest_host')) def process_external(self): if self.agent_conf.agent_mode != ( n_const.L3_AGENT_MODE_DVR_NO_EXTERNAL): ex_gw_port = self.get_ex_gw_port() if ex_gw_port: self.create_dvr_external_gateway_on_agent(ex_gw_port) self.connect_rtr_2_fip() super(DvrLocalRouter, self).process_external() def _check_rtr_2_fip_connect(self): """Checks if the rtr to fip connect exists, if not sets to false.""" fip_ns_name = self.fip_ns.get_name() if ip_lib.network_namespace_exists(fip_ns_name): fip_2_rtr_name = self.fip_ns.get_int_device_name(self.router_id) if not ip_lib.device_exists(fip_2_rtr_name, namespace=fip_ns_name): self.rtr_fip_connect = False def connect_rtr_2_fip(self): self._check_rtr_2_fip_connect() if self.fip_ns.agent_gateway_port and not self.rtr_fip_connect: ex_gw_port = self.get_ex_gw_port() self.fip_ns.create_rtr_2_fip_link(self) self.set_address_scope_interface_routes(ex_gw_port) self.rtr_fip_connect = True self.routes_updated([], self.router['routes']) def _check_if_address_scopes_match(self, int_port, ex_gw_port): """Checks and returns the matching state for v4 or v6 scopes.""" int_port_addr_scopes = int_port.get('address_scopes', {}) ext_port_addr_scopes = ex_gw_port.get('address_scopes', {}) key = ( lib_constants.IP_VERSION_6 if self._port_has_ipv6_subnet(int_port) else lib_constants.IP_VERSION_4) # NOTE: DVR does not support IPv6 for the floating namespace yet, so # until we fix it, we probably should use the snat redirect path for # the ports that have IPv6 address configured. int_port_addr_value = int_port_addr_scopes.get(str(key)) # If the address scope of the interface is none, then don't need # to compare and just return. if int_port_addr_value is None: return False if ((key != lib_constants.IP_VERSION_6) and int_port_addr_scopes.get(str(key)) in ext_port_addr_scopes.values()): return True return False def _delete_interface_route_in_fip_ns(self, router_port): rtr_2_fip_ip, fip_2_rtr_name = self.get_rtr_fip_ip_and_interface_name() fip_ns_name = self.fip_ns.get_name() if ip_lib.network_namespace_exists(fip_ns_name): device = ip_lib.IPDevice(fip_2_rtr_name, namespace=fip_ns_name) if not device.exists(): return for subnet in router_port['subnets']: rtr_port_cidr = subnet['cidr'] device.route.delete_route(rtr_port_cidr, str(rtr_2_fip_ip)) def _add_interface_route_to_fip_ns(self, router_port): rtr_2_fip_ip, fip_2_rtr_name = self.get_rtr_fip_ip_and_interface_name() fip_ns_name = self.fip_ns.get_name() if ip_lib.network_namespace_exists(fip_ns_name): device = ip_lib.IPDevice(fip_2_rtr_name, namespace=fip_ns_name) if not device.exists(): return for subnet in router_port['subnets']: rtr_port_cidr = subnet['cidr'] device.route.add_route(rtr_port_cidr, str(rtr_2_fip_ip)) def _add_interface_routing_rule_to_router_ns(self, router_port): ip_rule = ip_lib.IPRule(namespace=self.ns_name) for subnet in router_port['subnets']: rtr_port_cidr = subnet['cidr'] ip_rule.rule.add(ip=rtr_port_cidr, table=dvr_fip_ns.FIP_RT_TBL, priority=dvr_fip_ns.FAST_PATH_EXIT_PR) def _delete_interface_routing_rule_in_router_ns(self, router_port): ip_rule = ip_lib.IPRule(namespace=self.ns_name) for subnet in router_port['subnets']: rtr_port_cidr = subnet['cidr'] ip_rule.rule.delete(ip=rtr_port_cidr, table=dvr_fip_ns.FIP_RT_TBL, priority=dvr_fip_ns.FAST_PATH_EXIT_PR) def get_rtr_fip_ip_and_interface_name(self): """Function that returns the router to fip interface name and ip.""" if self.rtr_fip_subnet is None: self.rtr_fip_subnet = self.fip_ns.local_subnets.allocate( self.router_id) rtr_2_fip, __ = self.rtr_fip_subnet.get_pair() fip_2_rtr_name = self.fip_ns.get_int_device_name(self.router_id) return rtr_2_fip.ip, fip_2_rtr_name def set_address_scope_interface_routes(self, ex_gw_port): """Sets routing rules for router interfaces if addr scopes match.""" for port in self.internal_ports: if self._check_if_address_scopes_match(port, ex_gw_port): self._add_interface_routing_rule_to_router_ns(port) self._add_interface_route_to_fip_ns(port) def create_dvr_external_gateway_on_agent(self, ex_gw_port): fip_agent_port = self.get_floating_agent_gw_interface( ex_gw_port['network_id']) if not fip_agent_port: fip_agent_port = self.agent.plugin_rpc.get_agent_gateway_port( self.agent.context, ex_gw_port['network_id']) LOG.debug("FloatingIP agent gateway port received from the " "plugin: %s", fip_agent_port) self.fip_ns.create_or_update_gateway_port(fip_agent_port) def update_routing_table(self, operation, route): # TODO(Swami): The static routes should be added to the # specific namespace based on the availability of the # network interfaces. In the case of DVR the static routes # for local internal router networks can be added to the # snat_namespace and router_namespace but should not be # added to the fip namespace. Likewise the static routes # for the external router networks should only be added to # the snat_namespace and fip_namespace. # The current code adds static routes to all namespaces in # order to reduce the complexity. This should be revisited # later. if self.fip_ns and self.fip_ns.agent_gateway_port: fip_ns_name = self.fip_ns.get_name() agent_gw_port = self.fip_ns.agent_gateway_port route_apply = self._check_if_route_applicable_to_fip_namespace( route, agent_gw_port) if route_apply: if self.rtr_fip_subnet is None: self.rtr_fip_subnet = self.fip_ns.local_subnets.allocate( self.router_id) rtr_2_fip, fip_2_rtr = self.rtr_fip_subnet.get_pair() tbl_index = self._get_snat_idx(fip_2_rtr) self._update_fip_route_table_with_next_hop_routes( operation, route, fip_ns_name, tbl_index) super(DvrLocalRouter, self).update_routing_table(operation, route) def _update_fip_route_table_with_next_hop_routes( self, operation, route, fip_ns_name, tbl_index): cmd = ['ip', 'route', operation, 'to', route['destination'], 'via', route['nexthop'], 'table', tbl_index] ip_wrapper = ip_lib.IPWrapper(namespace=fip_ns_name) if ip_wrapper.netns.exists(fip_ns_name): ip_wrapper.netns.execute(cmd, check_exit_code=False) else: LOG.debug("The FIP namespace %(ns)s does not exist for " "router %(id)s", {'ns': fip_ns_name, 'id': self.router_id}) def _check_if_route_applicable_to_fip_namespace( self, route, agent_gateway_port): ip_cidrs = common_utils.fixed_ip_cidrs(agent_gateway_port['fixed_ips']) nexthop_cidr = netaddr.IPAddress(route['nexthop']) for gw_cidr in ip_cidrs: gw_subnet_cidr = netaddr.IPNetwork(gw_cidr) # NOTE: In the case of DVR routers apply the extra routes # on the FIP namespace only if it is associated with the # external agent gateway subnets. if nexthop_cidr in gw_subnet_cidr: return True return False def get_router_cidrs(self, device): """As no floatingip will be set on the rfp device. Get floatingip from the route of fip namespace. """ if not self.fip_ns: return set() fip_ns_name = self.fip_ns.get_name() fip_2_rtr_name = self.fip_ns.get_int_device_name(self.router_id) device = ip_lib.IPDevice(fip_2_rtr_name, namespace=fip_ns_name) if not device.exists(): return set() if self.rtr_fip_subnet is None: self.rtr_fip_subnet = self.fip_ns.local_subnets.allocate( self.router_id) rtr_2_fip, _fip_2_rtr = self.rtr_fip_subnet.get_pair() exist_routes = device.route.list_routes( lib_constants.IP_VERSION_4, via=str(rtr_2_fip.ip)) return {common_utils.ip_to_cidr(route['cidr']) for route in exist_routes} def process(self): ex_gw_port = self.get_ex_gw_port() if ex_gw_port: self.fip_ns = self.agent.get_fip_ns(ex_gw_port['network_id']) self.fip_ns.scan_fip_ports(self) super(DvrLocalRouter, self).process() neutron-12.1.1/neutron/agent/l3/dvr_edge_router.py0000664000175000017500000004071313553660047022165 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as lib_constants from oslo_log import log as logging from neutron.agent.l3 import dvr_local_router from neutron.agent.l3 import dvr_snat_ns from neutron.agent.l3 import router_info as router from neutron.agent.linux import ip_lib from neutron.agent.linux import iptables_manager from neutron.common import constants as n_const from neutron.common import utils as common_utils LOG = logging.getLogger(__name__) class DvrEdgeRouter(dvr_local_router.DvrLocalRouter): def __init__(self, host, *args, **kwargs): super(DvrEdgeRouter, self).__init__(host, *args, **kwargs) self.snat_namespace = dvr_snat_ns.SnatNamespace( self.router_id, self.agent_conf, self.driver, self.use_ipv6) self.snat_iptables_manager = None def get_gw_ns_name(self): return self.snat_namespace.name def external_gateway_added(self, ex_gw_port, interface_name): super(DvrEdgeRouter, self).external_gateway_added( ex_gw_port, interface_name) if self._is_this_snat_host(): self._create_dvr_gateway(ex_gw_port, interface_name) # NOTE: When a router is created without a gateway the routes get # added to the router namespace, but if we wanted to populate # the same routes to the snat namespace after the gateway port # is added, we need to call routes_updated here. self.routes_updated([], self.router['routes']) elif self.snat_namespace.exists(): # This is the case where the snat was moved manually or # rescheduled to a different agent when the agent was dead. LOG.debug("SNAT was moved or rescheduled to a different host " "and does not match with the current host. This is " "a stale namespace %s and will be cleared from the " "current dvr_snat host.", self.snat_namespace.name) self.external_gateway_removed(ex_gw_port, interface_name) def _list_centralized_floating_ip_cidrs(self): # Compute a list of addresses this gw is supposed to have. # This avoids unnecessarily removing those addresses and # causing a momentarily network outage. floating_ips = self.get_floating_ips() return [common_utils.ip_to_cidr(ip['floating_ip_address']) for ip in floating_ips if ip.get(lib_constants.DVR_SNAT_BOUND)] def external_gateway_updated(self, ex_gw_port, interface_name): if not self._is_this_snat_host(): # no centralized SNAT gateway for this node/agent LOG.debug("not hosting snat for router: %s", self.router['id']) if self.snat_namespace.exists(): LOG.debug("SNAT was rescheduled to host %s. Clearing snat " "namespace.", self.router.get('gw_port_host')) return self.external_gateway_removed( ex_gw_port, interface_name) return if not self.snat_namespace.exists(): # SNAT might be rescheduled to this agent; need to process like # newly created gateway return self.external_gateway_added(ex_gw_port, interface_name) else: preserve_ips = self._list_centralized_floating_ip_cidrs() self._external_gateway_added(ex_gw_port, interface_name, self.snat_namespace.name, preserve_ips) def _external_gateway_removed(self, ex_gw_port, interface_name): super(DvrEdgeRouter, self).external_gateway_removed(ex_gw_port, interface_name) if not self._is_this_snat_host() and not self.snat_namespace.exists(): # no centralized SNAT gateway for this node/agent LOG.debug("not hosting snat for router: %s", self.router['id']) return self.driver.unplug(interface_name, bridge=self.agent_conf.external_network_bridge, namespace=self.snat_namespace.name, prefix=router.EXTERNAL_DEV_PREFIX) def external_gateway_removed(self, ex_gw_port, interface_name): self._external_gateway_removed(ex_gw_port, interface_name) if self.snat_namespace.exists(): self.snat_namespace.delete() def internal_network_added(self, port): super(DvrEdgeRouter, self).internal_network_added(port) # TODO(gsagie) some of this checks are already implemented # in the base class, think how to avoid re-doing them if not self._is_this_snat_host(): return sn_port = self.get_snat_port_for_internal_port(port) if not sn_port: return ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(self.router['id']) interface_name = self._get_snat_int_device_name(sn_port['id']) self._internal_network_added( ns_name, sn_port['network_id'], sn_port['id'], sn_port['fixed_ips'], sn_port['mac_address'], interface_name, lib_constants.SNAT_INT_DEV_PREFIX, mtu=sn_port.get('mtu')) def _dvr_internal_network_removed(self, port): super(DvrEdgeRouter, self)._dvr_internal_network_removed(port) if not self.ex_gw_port: return sn_port = self.get_snat_port_for_internal_port(port, self.snat_ports) if not sn_port: return if not self._is_this_snat_host(): return snat_interface = self._get_snat_int_device_name(sn_port['id']) ns_name = self.snat_namespace.name prefix = lib_constants.SNAT_INT_DEV_PREFIX if ip_lib.device_exists(snat_interface, namespace=ns_name): self.driver.unplug(snat_interface, namespace=ns_name, prefix=prefix) def _plug_snat_port(self, port): interface_name = self._get_snat_int_device_name(port['id']) self._internal_network_added( self.snat_namespace.name, port['network_id'], port['id'], port['fixed_ips'], port['mac_address'], interface_name, lib_constants.SNAT_INT_DEV_PREFIX, mtu=port.get('mtu')) def _create_dvr_gateway(self, ex_gw_port, gw_interface_name): snat_ns = self._create_snat_namespace() # connect snat_ports to br_int from SNAT namespace for port in self.get_snat_interfaces(): self._plug_snat_port(port) self._external_gateway_added(ex_gw_port, gw_interface_name, snat_ns.name, preserve_ips=[]) self.snat_iptables_manager = iptables_manager.IptablesManager( namespace=snat_ns.name, use_ipv6=self.use_ipv6) self._initialize_address_scope_iptables(self.snat_iptables_manager) def _create_snat_namespace(self): """Create SNAT namespace.""" # TODO(mlavalle): in the near future, this method should contain the # code in the L3 agent that creates a gateway for a dvr. The first step # is to move the creation of the snat namespace here self.snat_namespace.create() return self.snat_namespace def _get_snat_int_device_name(self, port_id): long_name = lib_constants.SNAT_INT_DEV_PREFIX + port_id return long_name[:self.driver.DEV_NAME_LEN] def _is_this_snat_host(self): host = self.router.get('gw_port_host') if not host: LOG.debug("gw_port_host missing from router: %s", self.router['id']) return host == self.host def _handle_router_snat_rules(self, ex_gw_port, interface_name): super(DvrEdgeRouter, self)._handle_router_snat_rules( ex_gw_port, interface_name) if not self._is_this_snat_host(): return if not self.get_ex_gw_port(): return if not self.snat_iptables_manager: LOG.debug("DVR router: no snat rules to be handled") return with self.snat_iptables_manager.defer_apply(): self._empty_snat_chains(self.snat_iptables_manager) # NOTE: float-snat should be added for the # centralized floating-ips supported by the # snat namespace. self.snat_iptables_manager.ipv4['nat'].add_rule( 'snat', '-j $float-snat') self._add_snat_rules(ex_gw_port, self.snat_iptables_manager, interface_name) def update_routing_table(self, operation, route): if self.get_ex_gw_port() and self._is_this_snat_host(): ns_name = self.snat_namespace.name # NOTE: For now let us apply the static routes both in SNAT # namespace and Router Namespace, to reduce the complexity. if self.snat_namespace.exists(): super(DvrEdgeRouter, self)._update_routing_table( operation, route, namespace=ns_name) else: LOG.error("The SNAT namespace %s does not exist for " "the router.", ns_name) super(DvrEdgeRouter, self).update_routing_table(operation, route) def delete(self): super(DvrEdgeRouter, self).delete() if self.snat_namespace.exists(): self.snat_namespace.delete() def process_address_scope(self): super(DvrEdgeRouter, self).process_address_scope() if not self._is_this_snat_host(): return if not self.snat_iptables_manager: LOG.debug("DVR router: no snat rules to be handled") return # Prepare address scope iptables rule for dvr snat interfaces internal_ports = self.get_snat_interfaces() ports_scopemark = self._get_port_devicename_scopemark( internal_ports, self._get_snat_int_device_name) # Prepare address scope iptables rule for external port external_port = self.get_ex_gw_port() if external_port: external_port_scopemark = self._get_port_devicename_scopemark( [external_port], self.get_external_device_name) for ip_version in (lib_constants.IP_VERSION_4, lib_constants.IP_VERSION_6): ports_scopemark[ip_version].update( external_port_scopemark[ip_version]) with self.snat_iptables_manager.defer_apply(): self._add_address_scope_mark( self.snat_iptables_manager, ports_scopemark) def _delete_stale_external_devices(self, interface_name): if not self.snat_namespace.exists(): return ns_ip = ip_lib.IPWrapper(namespace=self.snat_namespace.name) for d in ns_ip.get_devices(): if (d.name.startswith(router.EXTERNAL_DEV_PREFIX) and d.name != interface_name): LOG.debug('Deleting stale external router device: %s', d.name) self.driver.unplug( d.name, bridge=self.agent_conf.external_network_bridge, namespace=self.snat_namespace.name, prefix=router.EXTERNAL_DEV_PREFIX) def get_snat_external_device_interface_name(self, ex_gw_port): long_name = router.EXTERNAL_DEV_PREFIX + ex_gw_port['id'] return long_name[:self.driver.DEV_NAME_LEN] def get_centralized_fip_cidr_set(self): """Returns the fip_cidr set for centralized floatingips.""" ex_gw_port = self.get_ex_gw_port() # Don't look for centralized FIP cidrs if gw_port not exists or # this is not snat host if (not ex_gw_port or not self._is_this_snat_host() or not self.snat_namespace.exists()): return set() interface_name = self.get_snat_external_device_interface_name( ex_gw_port) device = ip_lib.IPDevice( interface_name, namespace=self.snat_namespace.name) return set([addr['cidr'] for addr in device.addr.list()]) def get_router_cidrs(self, device): """Over-ride the get_router_cidrs function to return the list. This function is overridden to provide the complete list of floating_ip cidrs that the router hosts. This includes the centralized floatingip cidr list and the regular floatingip cidr list that are bound to fip namespace. """ fip_cidrs = super(DvrEdgeRouter, self).get_router_cidrs(device) centralized_cidrs = self.get_centralized_fip_cidr_set() return fip_cidrs | centralized_cidrs def remove_centralized_floatingip(self, fip_cidr): """Function to handle the centralized Floatingip remove.""" if not self.get_ex_gw_port(): return if not self._is_this_snat_host(): return interface_name = self.get_snat_external_device_interface_name( self.get_ex_gw_port()) device = ip_lib.IPDevice( interface_name, namespace=self.snat_namespace.name) device.delete_addr_and_conntrack_state(fip_cidr) self.process_floating_ip_nat_rules_for_centralized_floatingip() def add_centralized_floatingip(self, fip, fip_cidr): """Function to handle the centralized Floatingip addition.""" if not self.get_ex_gw_port(): return if not self._is_this_snat_host(): return interface_name = self.get_snat_external_device_interface_name( self.get_ex_gw_port()) device = ip_lib.IPDevice( interface_name, namespace=self.snat_namespace.name) try: device.addr.add(fip_cidr) except RuntimeError: LOG.warning("Unable to configure IP address for centralized " "floating IP: %s", fip['id']) return lib_constants.FLOATINGIP_STATUS_ERROR self.process_floating_ip_nat_rules_for_centralized_floatingip() # Send a GARP message on the external interface for the # centralized floatingip configured. ip_lib.send_ip_addr_adv_notif(self.snat_namespace.name, interface_name, fip['floating_ip_address']) return lib_constants.FLOATINGIP_STATUS_ACTIVE def _centralized_floating_forward_rules(self, floating_ip, fixed_ip): to_source = '-s %s/32 -j SNAT --to-source %s' % (fixed_ip, floating_ip) if self.snat_iptables_manager.random_fully: to_source += ' --random-fully' return [('PREROUTING', '-d %s/32 -j DNAT --to-destination %s' % (floating_ip, fixed_ip)), ('OUTPUT', '-d %s/32 -j DNAT --to-destination %s' % (floating_ip, fixed_ip)), ('float-snat', to_source)] def _set_floating_ip_nat_rules_for_centralized_floatingip(self, fip): if fip.get(n_const.DVR_SNAT_BOUND): fixed = fip['fixed_ip_address'] fip_ip = fip['floating_ip_address'] for chain, rule in self._centralized_floating_forward_rules( fip_ip, fixed): self.snat_iptables_manager.ipv4['nat'].add_rule( chain, rule, tag='floating_ip') def process_floating_ip_nat_rules_for_centralized_floatingip(self): self.snat_iptables_manager.ipv4['nat'].clear_rules_by_tag( 'floating_ip') floating_ips = self.get_floating_ips() for fip in floating_ips: self._set_floating_ip_nat_rules_for_centralized_floatingip(fip) self.snat_iptables_manager.apply() def process_floating_ip_nat_rules(self): if self._is_this_snat_host(): self.process_floating_ip_nat_rules_for_centralized_floatingip() # Cover mixed dvr_snat and compute node, aka a dvr_snat node has both # centralized and distributed floating IPs. super(DvrEdgeRouter, self).process_floating_ip_nat_rules() neutron-12.1.1/neutron/agent/l3/legacy_router.py0000664000175000017500000000244713553660046021653 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as lib_constants from neutron.agent.l3 import router_info as router from neutron.agent.linux import ip_lib class LegacyRouter(router.RouterInfo): def add_floating_ip(self, fip, interface_name, device): if not self._add_fip_addr_to_device(fip, device): return lib_constants.FLOATINGIP_STATUS_ERROR # As GARP is processed in a distinct thread the call below # won't raise an exception to be handled. ip_lib.send_ip_addr_adv_notif(self.ns_name, interface_name, fip['floating_ip_address']) return lib_constants.FLOATINGIP_STATUS_ACTIVE neutron-12.1.1/neutron/agent/l3/dvr_router_base.py0000664000175000017500000000400413553660047022164 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from neutron.agent.l3 import router_info as router from neutron.common import constants as l3_constants LOG = logging.getLogger(__name__) class DvrRouterBase(router.RouterInfo): def __init__(self, host, *args, **kwargs): super(DvrRouterBase, self).__init__(*args, **kwargs) self.host = host self.snat_ports = None def process(self): super(DvrRouterBase, self).process() # NOTE: Keep a copy of the interfaces around for when they are removed self.snat_ports = self.get_snat_interfaces() def get_snat_interfaces(self): return self.router.get(l3_constants.SNAT_ROUTER_INTF_KEY, []) def get_snat_port_for_internal_port(self, int_port, snat_ports=None): """Return the SNAT port for the given internal interface port.""" if snat_ports is None: snat_ports = self.get_snat_interfaces() if not snat_ports: return fixed_ips = int_port['fixed_ips'] subnet_ids = [fixed_ip['subnet_id'] for fixed_ip in fixed_ips] for p in snat_ports: for ip in p['fixed_ips']: if ip['subnet_id'] in subnet_ids: return p LOG.error('DVR: SNAT port not found in the list ' '%(snat_list)s for the given router ' 'internal port %(int_p)s', { 'snat_list': snat_ports, 'int_p': int_port}) neutron-12.1.1/neutron/agent/l3/l3_agent_extension_api.py0000664000175000017500000000516213553660047023426 0ustar zuulzuul00000000000000# Copyright 2016 Comcast # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.linux import ip_lib class L3AgentExtensionAPI(object): '''Implements the Agent API for the L3 agent. Extensions can gain access to this API by overriding the consume_api method which has been added to the AgentCoreResourceExtension class. The purpose of this API is to give L3 agent extensions access to the agent's RouterInfo object. ''' def __init__(self, router_info): self._router_info = router_info def _local_namespaces(self): local_ns_list = ip_lib.list_network_namespaces() return set(local_ns_list) def get_router_hosting_port(self, port_id): """Given a port_id, look up the router associated with that port in local namespace. Returns a RouterInfo object (or None if the router is not found). """ if port_id: local_namespaces = self._local_namespaces() for router_info in self._router_info.values(): if router_info.ns_name in local_namespaces: for port in router_info.internal_ports: if port['id'] == port_id: return router_info def get_routers_in_project(self, project_id): """Given a project_id, return a list of routers that are all in the given project. Returns empty list if the project_id provided doesn't evaluate to True. """ if project_id: return [ri for ri in self._router_info.values() if ri.router['project_id'] == project_id] else: return [] def is_router_in_namespace(self, router_id): """Given a router_id, make sure that the router is in a local namespace. """ local_namespaces = self._local_namespaces() ri = self._router_info.get(router_id) return ri and ri.ns_name in local_namespaces def get_router_info(self, router_id): """Return RouterInfo for the given router id.""" return self._router_info.get(router_id) neutron-12.1.1/neutron/agent/l3/router_info.py0000664000175000017500000015125213553660047021342 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import netaddr from neutron_lib import constants as lib_constants from neutron_lib.utils import helpers from oslo_log import log as logging from neutron._i18n import _ from neutron.agent.l3 import namespaces from neutron.agent.linux import ip_lib from neutron.agent.linux import iptables_manager from neutron.agent.linux import ra from neutron.common import constants as n_const from neutron.common import exceptions as n_exc from neutron.common import ipv6_utils from neutron.common import utils as common_utils from neutron.ipam import utils as ipam_utils LOG = logging.getLogger(__name__) INTERNAL_DEV_PREFIX = namespaces.INTERNAL_DEV_PREFIX EXTERNAL_DEV_PREFIX = namespaces.EXTERNAL_DEV_PREFIX FLOATINGIP_STATUS_NOCHANGE = object() ADDRESS_SCOPE_MARK_MASK = "0xffff0000" ADDRESS_SCOPE_MARK_ID_MIN = 1024 ADDRESS_SCOPE_MARK_ID_MAX = 2048 DEFAULT_ADDRESS_SCOPE = "noscope" class RouterInfo(object): def __init__(self, agent, router_id, router, agent_conf, interface_driver, use_ipv6=False): self.agent = agent self.router_id = router_id self.agent_conf = agent_conf self.ex_gw_port = None self._snat_enabled = None self.fip_map = {} self.internal_ports = [] self.pd_subnets = {} self.floating_ips = set() # Invoke the setter for establishing initial SNAT action self.router = router self.use_ipv6 = use_ipv6 ns = self.create_router_namespace_object( router_id, agent_conf, interface_driver, use_ipv6) self.router_namespace = ns self.ns_name = ns.name self.available_mark_ids = set(range(ADDRESS_SCOPE_MARK_ID_MIN, ADDRESS_SCOPE_MARK_ID_MAX)) self._address_scope_to_mark_id = { DEFAULT_ADDRESS_SCOPE: self.available_mark_ids.pop()} self.iptables_manager = iptables_manager.IptablesManager( use_ipv6=use_ipv6, namespace=self.ns_name) self.initialize_address_scope_iptables() self.initialize_metadata_iptables() self.routes = [] self.driver = interface_driver self.process_monitor = None # radvd is a neutron.agent.linux.ra.DaemonMonitor self.radvd = None def initialize(self, process_monitor): """Initialize the router on the system. This differs from __init__ in that this method actually affects the system creating namespaces, starting processes, etc. The other merely initializes the python object. This separates in-memory object initialization from methods that actually go do stuff to the system. :param process_monitor: The agent's process monitor instance. """ self.process_monitor = process_monitor self.radvd = ra.DaemonMonitor(self.router_id, self.ns_name, process_monitor, self.get_internal_device_name, self.agent_conf) self.router_namespace.create() def create_router_namespace_object( self, router_id, agent_conf, iface_driver, use_ipv6): return namespaces.RouterNamespace( router_id, agent_conf, iface_driver, use_ipv6) @property def router(self): return self._router @router.setter def router(self, value): self._router = value if not self._router: return # enable_snat by default if it wasn't specified by plugin self._snat_enabled = self._router.get('enable_snat', True) def is_router_master(self): return True def get_internal_device_name(self, port_id): return (INTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN] def get_external_device_name(self, port_id): return (EXTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN] def get_external_device_interface_name(self, ex_gw_port): return self.get_external_device_name(ex_gw_port['id']) def get_gw_ns_name(self): return self.ns_name def _update_routing_table(self, operation, route, namespace): cmd = ['ip', 'route', operation, 'to', route['destination'], 'via', route['nexthop']] ip_wrapper = ip_lib.IPWrapper(namespace=namespace) ip_wrapper.netns.execute(cmd, check_exit_code=False) def update_routing_table(self, operation, route): self._update_routing_table(operation, route, self.ns_name) def routes_updated(self, old_routes, new_routes): adds, removes = helpers.diff_list_of_dict(old_routes, new_routes) for route in adds: LOG.debug("Added route entry is '%s'", route) # remove replaced route from deleted route for del_route in removes: if route['destination'] == del_route['destination']: removes.remove(del_route) #replace success even if there is no existing route self.update_routing_table('replace', route) for route in removes: LOG.debug("Removed route entry is '%s'", route) self.update_routing_table('delete', route) def get_ex_gw_port(self): return self.router.get('gw_port') def get_floating_ips(self): """Filter Floating IPs to be hosted on this agent.""" return self.router.get(lib_constants.FLOATINGIP_KEY, []) def floating_forward_rules(self, fip): fixed_ip = fip['fixed_ip_address'] floating_ip = fip['floating_ip_address'] to_source = '-s %s/32 -j SNAT --to-source %s' % (fixed_ip, floating_ip) if self.iptables_manager.random_fully: to_source += ' --random-fully' return [('PREROUTING', '-d %s/32 -j DNAT --to-destination %s' % (floating_ip, fixed_ip)), ('OUTPUT', '-d %s/32 -j DNAT --to-destination %s' % (floating_ip, fixed_ip)), ('float-snat', to_source)] def floating_mangle_rules(self, floating_ip, fixed_ip, internal_mark): mark_traffic_to_floating_ip = ( 'floatingip', '-d %s/32 -j MARK --set-xmark %s' % ( floating_ip, internal_mark)) mark_traffic_from_fixed_ip = ( 'FORWARD', '-s %s/32 -j $float-snat' % fixed_ip) return [mark_traffic_to_floating_ip, mark_traffic_from_fixed_ip] def get_address_scope_mark_mask(self, address_scope=None): if not address_scope: address_scope = DEFAULT_ADDRESS_SCOPE if address_scope not in self._address_scope_to_mark_id: self._address_scope_to_mark_id[address_scope] = ( self.available_mark_ids.pop()) mark_id = self._address_scope_to_mark_id[address_scope] # NOTE: Address scopes use only the upper 16 bits of the 32 fwmark return "%s/%s" % (hex(mark_id << 16), ADDRESS_SCOPE_MARK_MASK) def get_port_address_scope_mark(self, port): """Get the IP version 4 and 6 address scope mark for the port :param port: A port dict from the RPC call :returns: A dict mapping the address family to the address scope mark """ port_scopes = port.get('address_scopes', {}) address_scope_mark_masks = ( (int(k), self.get_address_scope_mark_mask(v)) for k, v in port_scopes.items()) return collections.defaultdict(self.get_address_scope_mark_mask, address_scope_mark_masks) def process_floating_ip_nat_rules(self): """Configure NAT rules for the router's floating IPs. Configures iptables rules for the floating ips of the given router """ # Clear out all iptables rules for floating ips self.iptables_manager.ipv4['nat'].clear_rules_by_tag('floating_ip') floating_ips = self.get_floating_ips() # Loop once to ensure that floating ips are configured. for fip in floating_ips: # Rebuild iptables rules for the floating ip. for chain, rule in self.floating_forward_rules(fip): self.iptables_manager.ipv4['nat'].add_rule(chain, rule, tag='floating_ip') self.iptables_manager.apply() def _process_pd_iptables_rules(self, prefix, subnet_id): """Configure iptables rules for prefix delegated subnets""" ext_scope = self._get_external_address_scope() ext_scope_mark = self.get_address_scope_mark_mask(ext_scope) ex_gw_device = self.get_external_device_name( self.get_ex_gw_port()['id']) scope_rule = self.address_scope_mangle_rule(ex_gw_device, ext_scope_mark) self.iptables_manager.ipv6['mangle'].add_rule( 'scope', '-d %s ' % prefix + scope_rule, tag=('prefix_delegation_%s' % subnet_id)) def process_floating_ip_address_scope_rules(self): """Configure address scope related iptables rules for the router's floating IPs. """ # Clear out all iptables rules for floating ips self.iptables_manager.ipv4['mangle'].clear_rules_by_tag('floating_ip') all_floating_ips = self.get_floating_ips() ext_scope = self._get_external_address_scope() # Filter out the floating ips that have fixed ip in the same address # scope. Because the packets for them will always be in one address # scope, no need to manipulate MARK/CONNMARK for them. floating_ips = [fip for fip in all_floating_ips if fip.get('fixed_ip_address_scope') != ext_scope] if floating_ips: ext_scope_mark = self.get_address_scope_mark_mask(ext_scope) ports_scopemark = self._get_address_scope_mark() devices_in_ext_scope = { device for device, mark in ports_scopemark[lib_constants.IP_VERSION_4].items() if mark == ext_scope_mark} # Add address scope for floatingip egress for device in devices_in_ext_scope: self.iptables_manager.ipv4['mangle'].add_rule( 'float-snat', '-o %s -j MARK --set-xmark %s' % (device, ext_scope_mark), tag='floating_ip') # Loop once to ensure that floating ips are configured. for fip in floating_ips: # Rebuild iptables rules for the floating ip. fip_ip = fip['floating_ip_address'] # Send the floating ip traffic to the right address scope fixed_ip = fip['fixed_ip_address'] fixed_scope = fip.get('fixed_ip_address_scope') internal_mark = self.get_address_scope_mark_mask(fixed_scope) mangle_rules = self.floating_mangle_rules( fip_ip, fixed_ip, internal_mark) for chain, rule in mangle_rules: self.iptables_manager.ipv4['mangle'].add_rule( chain, rule, tag='floating_ip') def process_snat_dnat_for_fip(self): try: self.process_floating_ip_nat_rules() except Exception: # TODO(salv-orlando): Less broad catching msg = _('L3 agent failure to setup NAT for floating IPs') LOG.exception(msg) raise n_exc.FloatingIpSetupException(msg) def _add_fip_addr_to_device(self, fip, device): """Configures the floating ip address on the device. """ try: ip_cidr = common_utils.ip_to_cidr(fip['floating_ip_address']) device.addr.add(ip_cidr) return True except RuntimeError: # any exception occurred here should cause the floating IP # to be set in error state LOG.warning("Unable to configure IP address for " "floating IP: %s", fip['id']) def add_floating_ip(self, fip, interface_name, device): raise NotImplementedError() def migrate_centralized_floating_ip(self, fip, interface_name, device): pass def gateway_redirect_cleanup(self, rtr_interface): pass def remove_floating_ip(self, device, ip_cidr): device.delete_addr_and_conntrack_state(ip_cidr) def move_floating_ip(self, fip): return lib_constants.FLOATINGIP_STATUS_ACTIVE def remove_external_gateway_ip(self, device, ip_cidr): device.delete_addr_and_conntrack_state(ip_cidr) def get_router_cidrs(self, device): return set([addr['cidr'] for addr in device.addr.list()]) def get_centralized_fip_cidr_set(self): return set() def process_floating_ip_addresses(self, interface_name): """Configure IP addresses on router's external gateway interface. Ensures addresses for existing floating IPs and cleans up those that should not longer be configured. """ fip_statuses = {} if interface_name is None: LOG.debug('No Interface for floating IPs router: %s', self.router['id']) return fip_statuses device = ip_lib.IPDevice(interface_name, namespace=self.ns_name) existing_cidrs = self.get_router_cidrs(device) new_cidrs = set() gw_cidrs = self._get_gw_ips_cidr() centralized_fip_cidrs = self.get_centralized_fip_cidr_set() floating_ips = self.get_floating_ips() # Loop once to ensure that floating ips are configured. for fip in floating_ips: fip_ip = fip['floating_ip_address'] ip_cidr = common_utils.ip_to_cidr(fip_ip) new_cidrs.add(ip_cidr) fip_statuses[fip['id']] = lib_constants.FLOATINGIP_STATUS_ACTIVE if ip_cidr not in existing_cidrs: fip_statuses[fip['id']] = self.add_floating_ip( fip, interface_name, device) LOG.debug('Floating ip %(id)s added, status %(status)s', {'id': fip['id'], 'status': fip_statuses.get(fip['id'])}) elif (fip_ip in self.fip_map and self.fip_map[fip_ip] != fip['fixed_ip_address']): LOG.debug("Floating IP was moved from fixed IP " "%(old)s to %(new)s", {'old': self.fip_map[fip_ip], 'new': fip['fixed_ip_address']}) fip_statuses[fip['id']] = self.move_floating_ip(fip) elif (ip_cidr in centralized_fip_cidrs and fip.get('host') == self.host): LOG.debug("Floating IP is migrating from centralized " "to distributed: %s", fip) fip_statuses[fip['id']] = self.migrate_centralized_floating_ip( fip, interface_name, device) elif fip_statuses[fip['id']] == fip['status']: # mark the status as not changed. we can't remove it because # that's how the caller determines that it was removed fip_statuses[fip['id']] = FLOATINGIP_STATUS_NOCHANGE fips_to_remove = ( ip_cidr for ip_cidr in existing_cidrs - new_cidrs - gw_cidrs if common_utils.is_cidr_host(ip_cidr)) for ip_cidr in fips_to_remove: LOG.debug("Removing floating ip %s from interface %s in " "namespace %s", ip_cidr, interface_name, self.ns_name) self.remove_floating_ip(device, ip_cidr) return fip_statuses def _get_gw_ips_cidr(self): gw_cidrs = set() ex_gw_port = self.get_ex_gw_port() if ex_gw_port: for ip_addr in ex_gw_port['fixed_ips']: ex_gw_ip = ip_addr['ip_address'] addr = netaddr.IPAddress(ex_gw_ip) if addr.version == lib_constants.IP_VERSION_4: gw_cidrs.add(common_utils.ip_to_cidr(ex_gw_ip)) return gw_cidrs def configure_fip_addresses(self, interface_name): try: return self.process_floating_ip_addresses(interface_name) except Exception: # TODO(salv-orlando): Less broad catching msg = _('L3 agent failure to setup floating IPs') LOG.exception(msg) raise n_exc.FloatingIpSetupException(msg) def put_fips_in_error_state(self): fip_statuses = {} for fip in self.router.get(lib_constants.FLOATINGIP_KEY, []): fip_statuses[fip['id']] = lib_constants.FLOATINGIP_STATUS_ERROR return fip_statuses def delete(self): self.router['gw_port'] = None self.router[lib_constants.INTERFACE_KEY] = [] self.router[lib_constants.FLOATINGIP_KEY] = [] self.process_delete() self.disable_radvd() self.router_namespace.delete() def _internal_network_updated(self, port, subnet_id, prefix, old_prefix, updated_cidrs): interface_name = self.get_internal_device_name(port['id']) if prefix != lib_constants.PROVISIONAL_IPV6_PD_PREFIX: fixed_ips = port['fixed_ips'] for fixed_ip in fixed_ips: if fixed_ip['subnet_id'] == subnet_id: v6addr = common_utils.ip_to_cidr(fixed_ip['ip_address'], fixed_ip.get('prefixlen')) if v6addr not in updated_cidrs: self.driver.add_ipv6_addr(interface_name, v6addr, self.ns_name) else: self.driver.delete_ipv6_addr_with_prefix(interface_name, old_prefix, self.ns_name) def _internal_network_added(self, ns_name, network_id, port_id, fixed_ips, mac_address, interface_name, prefix, mtu=None): LOG.debug("adding internal network: prefix(%s), port(%s)", prefix, port_id) self.driver.plug(network_id, port_id, interface_name, mac_address, namespace=ns_name, prefix=prefix, mtu=mtu) ip_cidrs = common_utils.fixed_ip_cidrs(fixed_ips) self.driver.init_router_port( interface_name, ip_cidrs, namespace=ns_name) for fixed_ip in fixed_ips: ip_lib.send_ip_addr_adv_notif(ns_name, interface_name, fixed_ip['ip_address']) def internal_network_added(self, port): network_id = port['network_id'] port_id = port['id'] fixed_ips = port['fixed_ips'] mac_address = port['mac_address'] interface_name = self.get_internal_device_name(port_id) self._internal_network_added(self.ns_name, network_id, port_id, fixed_ips, mac_address, interface_name, INTERNAL_DEV_PREFIX, mtu=port.get('mtu')) def internal_network_removed(self, port): interface_name = self.get_internal_device_name(port['id']) LOG.debug("removing internal network: port(%s) interface(%s)", port['id'], interface_name) if ip_lib.device_exists(interface_name, namespace=self.ns_name): self.driver.unplug(interface_name, namespace=self.ns_name, prefix=INTERNAL_DEV_PREFIX) def _get_existing_devices(self): ip_wrapper = ip_lib.IPWrapper(namespace=self.ns_name) ip_devs = ip_wrapper.get_devices() return [ip_dev.name for ip_dev in ip_devs] def _update_internal_ports_cache(self, port): # NOTE(slaweq): self.internal_ports is a list of port objects but # when it is updated in _process_internal_ports() method, # but it can be based only on indexes of elements in # self.internal_ports as index of element to updated is unknown. # It has to be done based on port_id and this method is doing exactly # that. for index, p in enumerate(self.internal_ports): if p['id'] == port['id']: self.internal_ports[index] = port break else: self.internal_ports.append(port) @staticmethod def _get_updated_ports(existing_ports, current_ports): updated_ports = [] current_ports_dict = {p['id']: p for p in current_ports} for existing_port in existing_ports: current_port = current_ports_dict.get(existing_port['id']) if current_port: fixed_ips_changed = ( sorted(existing_port['fixed_ips'], key=helpers.safe_sort_key) != sorted(current_port['fixed_ips'], key=helpers.safe_sort_key)) mtu_changed = existing_port['mtu'] != current_port['mtu'] if fixed_ips_changed or mtu_changed: updated_ports.append(current_port) return updated_ports @staticmethod def _port_has_ipv6_subnet(port): if 'subnets' in port: for subnet in port['subnets']: if (netaddr.IPNetwork(subnet['cidr']).version == 6 and subnet['cidr'] != lib_constants.PROVISIONAL_IPV6_PD_PREFIX): return True def enable_radvd(self, internal_ports=None): LOG.debug('Spawning radvd daemon in router device: %s', self.router_id) if not internal_ports: internal_ports = self.internal_ports self.radvd.enable(internal_ports) def disable_radvd(self): LOG.debug('Terminating radvd daemon in router device: %s', self.router_id) self.radvd.disable() def internal_network_updated(self, interface_name, ip_cidrs, mtu): self.driver.set_mtu(interface_name, mtu, namespace=self.ns_name, prefix=INTERNAL_DEV_PREFIX) self.driver.init_router_port( interface_name, ip_cidrs=ip_cidrs, namespace=self.ns_name) def address_scope_mangle_rule(self, device_name, mark_mask): return '-i %s -j MARK --set-xmark %s' % (device_name, mark_mask) def address_scope_filter_rule(self, device_name, mark_mask): return '-o %s -m mark ! --mark %s -j DROP' % ( device_name, mark_mask) def _process_internal_ports(self): existing_port_ids = set(p['id'] for p in self.internal_ports) internal_ports = self.router.get(lib_constants.INTERFACE_KEY, []) current_port_ids = set(p['id'] for p in internal_ports if p['admin_state_up']) new_port_ids = current_port_ids - existing_port_ids new_ports = [p for p in internal_ports if p['id'] in new_port_ids] old_ports = [p for p in self.internal_ports if p['id'] not in current_port_ids] updated_ports = self._get_updated_ports(self.internal_ports, internal_ports) enable_ra = False for p in new_ports: self.internal_network_added(p) LOG.debug("appending port %s to internal_ports cache", p) self._update_internal_ports_cache(p) enable_ra = enable_ra or self._port_has_ipv6_subnet(p) for subnet in p['subnets']: if ipv6_utils.is_ipv6_pd_enabled(subnet): interface_name = self.get_internal_device_name(p['id']) self.agent.pd.enable_subnet(self.router_id, subnet['id'], subnet['cidr'], interface_name, p['mac_address']) if (subnet['cidr'] != lib_constants.PROVISIONAL_IPV6_PD_PREFIX): self.pd_subnets[subnet['id']] = subnet['cidr'] for p in old_ports: self.internal_network_removed(p) LOG.debug("removing port %s from internal_ports cache", p) self.internal_ports.remove(p) enable_ra = enable_ra or self._port_has_ipv6_subnet(p) for subnet in p['subnets']: if ipv6_utils.is_ipv6_pd_enabled(subnet): self.agent.pd.disable_subnet(self.router_id, subnet['id']) del self.pd_subnets[subnet['id']] updated_cidrs = [] for p in updated_ports: self._update_internal_ports_cache(p) interface_name = self.get_internal_device_name(p['id']) ip_cidrs = common_utils.fixed_ip_cidrs(p['fixed_ips']) LOG.debug("updating internal network for port %s", p) updated_cidrs += ip_cidrs self.internal_network_updated( interface_name, ip_cidrs, p['mtu']) enable_ra = enable_ra or self._port_has_ipv6_subnet(p) # Check if there is any pd prefix update for p in internal_ports: if p['id'] in (set(current_port_ids) & set(existing_port_ids)): for subnet in p.get('subnets', []): if ipv6_utils.is_ipv6_pd_enabled(subnet): old_prefix = self.agent.pd.update_subnet( self.router_id, subnet['id'], subnet['cidr']) if old_prefix: self._internal_network_updated(p, subnet['id'], subnet['cidr'], old_prefix, updated_cidrs) self.pd_subnets[subnet['id']] = subnet['cidr'] enable_ra = True # Enable RA if enable_ra: self.enable_radvd(internal_ports) existing_devices = self._get_existing_devices() current_internal_devs = set(n for n in existing_devices if n.startswith(INTERNAL_DEV_PREFIX)) current_port_devs = set(self.get_internal_device_name(port_id) for port_id in current_port_ids) stale_devs = current_internal_devs - current_port_devs for stale_dev in stale_devs: LOG.debug('Deleting stale internal router device: %s', stale_dev) self.agent.pd.remove_stale_ri_ifname(self.router_id, stale_dev) self.driver.unplug(stale_dev, namespace=self.ns_name, prefix=INTERNAL_DEV_PREFIX) def _list_floating_ip_cidrs(self): # Compute a list of addresses this router is supposed to have. # This avoids unnecessarily removing those addresses and # causing a momentarily network outage. floating_ips = self.get_floating_ips() return [common_utils.ip_to_cidr(ip['floating_ip_address']) for ip in floating_ips] def _plug_external_gateway(self, ex_gw_port, interface_name, ns_name): self.driver.plug(ex_gw_port['network_id'], ex_gw_port['id'], interface_name, ex_gw_port['mac_address'], bridge=self.agent_conf.external_network_bridge, namespace=ns_name, prefix=EXTERNAL_DEV_PREFIX, mtu=ex_gw_port.get('mtu')) if self.agent_conf.external_network_bridge: # NOTE(slaweq): for OVS implementations remove the DEAD VLAN tag # on ports. DEAD VLAN tag is added to each newly created port # and should be removed by L2 agent but if # external_network_bridge is set than external gateway port is # created in this bridge and will not be touched by L2 agent. # This is related to lp#1767422 self.driver.remove_vlan_tag( self.agent_conf.external_network_bridge, interface_name) def _get_external_gw_ips(self, ex_gw_port): gateway_ips = [] if 'subnets' in ex_gw_port: gateway_ips = [subnet['gateway_ip'] for subnet in ex_gw_port['subnets'] if subnet['gateway_ip']] if self.use_ipv6 and not self.is_v6_gateway_set(gateway_ips): # No IPv6 gateway is available, but IPv6 is enabled. if self.agent_conf.ipv6_gateway: # ipv6_gateway configured, use address for default route. gateway_ips.append(self.agent_conf.ipv6_gateway) return gateway_ips def _add_route_to_gw(self, ex_gw_port, device_name, namespace, preserve_ips): # Note: ipv6_gateway is an ipv6 LLA # and so doesn't need a special route for subnet in ex_gw_port.get('subnets', []): is_gateway_not_in_subnet = (subnet['gateway_ip'] and not ipam_utils.check_subnet_ip( subnet['cidr'], subnet['gateway_ip'])) if is_gateway_not_in_subnet: preserve_ips.append(subnet['gateway_ip']) device = ip_lib.IPDevice(device_name, namespace=namespace) device.route.add_route(subnet['gateway_ip'], scope='link') def _configure_ipv6_params_on_gw(self, ex_gw_port, ns_name, interface_name, enabled): if not self.use_ipv6: return disable_ra = not enabled if enabled: gateway_ips = self._get_external_gw_ips(ex_gw_port) if not self.is_v6_gateway_set(gateway_ips): # There is no IPv6 gw_ip, use RouterAdvt for default route. self.driver.configure_ipv6_ra( ns_name, interface_name, n_const.ACCEPT_RA_WITH_FORWARDING) else: # Otherwise, disable it disable_ra = True if disable_ra: self.driver.configure_ipv6_ra(ns_name, interface_name, n_const.ACCEPT_RA_DISABLED) self.driver.configure_ipv6_forwarding(ns_name, interface_name, enabled) # This will make sure the 'all' setting is the same as the interface, # which is needed for forwarding to work. Don't disable once it's # been enabled so as to not send spurious MLDv2 packets out. if enabled: self.driver.configure_ipv6_forwarding(ns_name, 'all', enabled) def _external_gateway_added(self, ex_gw_port, interface_name, ns_name, preserve_ips): LOG.debug("External gateway added: port(%s), interface(%s), ns(%s)", ex_gw_port, interface_name, ns_name) self._plug_external_gateway(ex_gw_port, interface_name, ns_name) # Build up the interface and gateway IP addresses that # will be added to the interface. ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips']) gateway_ips = self._get_external_gw_ips(ex_gw_port) self._add_route_to_gw(ex_gw_port, device_name=interface_name, namespace=ns_name, preserve_ips=preserve_ips) self.driver.init_router_port( interface_name, ip_cidrs, namespace=ns_name, extra_subnets=ex_gw_port.get('extra_subnets', []), preserve_ips=preserve_ips, clean_connections=True) device = ip_lib.IPDevice(interface_name, namespace=ns_name) current_gateways = set() for ip_version in (lib_constants.IP_VERSION_4, lib_constants.IP_VERSION_6): gateway = device.route.get_gateway(ip_version=ip_version) if gateway and gateway.get('gateway'): current_gateways.add(gateway.get('gateway')) for ip in current_gateways - set(gateway_ips): device.route.delete_gateway(ip) for ip in gateway_ips: device.route.add_gateway(ip) self._configure_ipv6_params_on_gw(ex_gw_port, ns_name, interface_name, True) for fixed_ip in ex_gw_port['fixed_ips']: ip_lib.send_ip_addr_adv_notif(ns_name, interface_name, fixed_ip['ip_address']) def is_v6_gateway_set(self, gateway_ips): """Check to see if list of gateway_ips has an IPv6 gateway. """ # Note - don't require a try-except here as all # gateway_ips elements are valid addresses, if they exist. return any(netaddr.IPAddress(gw_ip).version == 6 for gw_ip in gateway_ips) def external_gateway_added(self, ex_gw_port, interface_name): preserve_ips = self._list_floating_ip_cidrs() preserve_ips.extend(self.agent.pd.get_preserve_ips(self.router_id)) self._external_gateway_added( ex_gw_port, interface_name, self.ns_name, preserve_ips) def external_gateway_updated(self, ex_gw_port, interface_name): preserve_ips = self._list_floating_ip_cidrs() preserve_ips.extend(self.agent.pd.get_preserve_ips(self.router_id)) self._external_gateway_added( ex_gw_port, interface_name, self.ns_name, preserve_ips) def external_gateway_removed(self, ex_gw_port, interface_name): LOG.debug("External gateway removed: port(%s), interface(%s)", ex_gw_port, interface_name) device = ip_lib.IPDevice(interface_name, namespace=self.ns_name) for ip_addr in ex_gw_port['fixed_ips']: prefixlen = ip_addr.get('prefixlen') self.remove_external_gateway_ip(device, common_utils.ip_to_cidr( ip_addr['ip_address'], prefixlen)) self.driver.unplug(interface_name, bridge=self.agent_conf.external_network_bridge, namespace=self.ns_name, prefix=EXTERNAL_DEV_PREFIX) @staticmethod def _gateway_ports_equal(port1, port2): return port1 == port2 def _delete_stale_external_devices(self, interface_name): existing_devices = self._get_existing_devices() stale_devs = [dev for dev in existing_devices if dev.startswith(EXTERNAL_DEV_PREFIX) and dev != interface_name] for stale_dev in stale_devs: LOG.debug('Deleting stale external router device: %s', stale_dev) self.agent.pd.remove_gw_interface(self.router['id']) self.driver.unplug(stale_dev, bridge=self.agent_conf.external_network_bridge, namespace=self.ns_name, prefix=EXTERNAL_DEV_PREFIX) def _process_external_gateway(self, ex_gw_port): # TODO(Carl) Refactor to clarify roles of ex_gw_port vs self.ex_gw_port ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or self.ex_gw_port and self.ex_gw_port['id']) interface_name = None if ex_gw_port_id: interface_name = self.get_external_device_name(ex_gw_port_id) if ex_gw_port: if not self.ex_gw_port: self.external_gateway_added(ex_gw_port, interface_name) self.agent.pd.add_gw_interface(self.router['id'], interface_name) elif not self._gateway_ports_equal(ex_gw_port, self.ex_gw_port): self.external_gateway_updated(ex_gw_port, interface_name) elif not ex_gw_port and self.ex_gw_port: self.external_gateway_removed(self.ex_gw_port, interface_name) self.agent.pd.remove_gw_interface(self.router['id']) elif not ex_gw_port and not self.ex_gw_port: for p in self.internal_ports: interface_name = self.get_internal_device_name(p['id']) self.gateway_redirect_cleanup(interface_name) self._delete_stale_external_devices(interface_name) # Process SNAT rules for external gateway gw_port = self._router.get('gw_port') self._handle_router_snat_rules(gw_port, interface_name) def _prevent_snat_for_internal_traffic_rule(self, interface_name): return ( 'POSTROUTING', '! -i %(interface_name)s ' '! -o %(interface_name)s -m conntrack ! ' '--ctstate DNAT -j ACCEPT' % {'interface_name': interface_name}) def external_gateway_nat_fip_rules(self, ex_gw_ip, interface_name): dont_snat_traffic_to_internal_ports_if_not_to_floating_ip = ( self._prevent_snat_for_internal_traffic_rule(interface_name)) # Makes replies come back through the router to reverse DNAT ext_in_mark = self.agent_conf.external_ingress_mark to_source = ('-m mark ! --mark %s/%s ' '-m conntrack --ctstate DNAT ' '-j SNAT --to-source %s' % (ext_in_mark, n_const.ROUTER_MARK_MASK, ex_gw_ip)) if self.iptables_manager.random_fully: to_source += ' --random-fully' snat_internal_traffic_to_floating_ip = ('snat', to_source) return [dont_snat_traffic_to_internal_ports_if_not_to_floating_ip, snat_internal_traffic_to_floating_ip] def external_gateway_nat_snat_rules(self, ex_gw_ip, interface_name): to_source = '-o %s -j SNAT --to-source %s' % (interface_name, ex_gw_ip) if self.iptables_manager.random_fully: to_source += ' --random-fully' return [('snat', to_source)] def external_gateway_mangle_rules(self, interface_name): mark = self.agent_conf.external_ingress_mark mark_packets_entering_external_gateway_port = ( 'mark', '-i %s -j MARK --set-xmark %s/%s' % (interface_name, mark, n_const.ROUTER_MARK_MASK)) return [mark_packets_entering_external_gateway_port] def _empty_snat_chains(self, iptables_manager): iptables_manager.ipv4['nat'].empty_chain('POSTROUTING') iptables_manager.ipv4['nat'].empty_chain('snat') iptables_manager.ipv4['mangle'].empty_chain('mark') iptables_manager.ipv4['mangle'].empty_chain('POSTROUTING') def _add_snat_rules(self, ex_gw_port, iptables_manager, interface_name): self.process_external_port_address_scope_routing(iptables_manager) if ex_gw_port: # ex_gw_port should not be None in this case # NAT rules are added only if ex_gw_port has an IPv4 address for ip_addr in ex_gw_port['fixed_ips']: ex_gw_ip = ip_addr['ip_address'] if netaddr.IPAddress(ex_gw_ip).version == 4: if self._snat_enabled: rules = self.external_gateway_nat_snat_rules( ex_gw_ip, interface_name) for rule in rules: iptables_manager.ipv4['nat'].add_rule(*rule) rules = self.external_gateway_nat_fip_rules( ex_gw_ip, interface_name) for rule in rules: iptables_manager.ipv4['nat'].add_rule(*rule) rules = self.external_gateway_mangle_rules(interface_name) for rule in rules: iptables_manager.ipv4['mangle'].add_rule(*rule) break def _handle_router_snat_rules(self, ex_gw_port, interface_name): self._empty_snat_chains(self.iptables_manager) self.iptables_manager.ipv4['nat'].add_rule('snat', '-j $float-snat') self._add_snat_rules(ex_gw_port, self.iptables_manager, interface_name) def _process_external_on_delete(self): fip_statuses = {} try: ex_gw_port = self.get_ex_gw_port() self._process_external_gateway(ex_gw_port) if not ex_gw_port: return interface_name = self.get_external_device_interface_name( ex_gw_port) fip_statuses = self.configure_fip_addresses(interface_name) except n_exc.FloatingIpSetupException: # All floating IPs must be put in error state LOG.exception("Failed to process floating IPs.") fip_statuses = self.put_fips_in_error_state() finally: self.update_fip_statuses(fip_statuses) def process_external(self): fip_statuses = {} try: with self.iptables_manager.defer_apply(): ex_gw_port = self.get_ex_gw_port() self._process_external_gateway(ex_gw_port) if not ex_gw_port: return # Process SNAT/DNAT rules and addresses for floating IPs self.process_snat_dnat_for_fip() # Once NAT rules for floating IPs are safely in place # configure their addresses on the external gateway port interface_name = self.get_external_device_interface_name( ex_gw_port) fip_statuses = self.configure_fip_addresses(interface_name) except (n_exc.FloatingIpSetupException, n_exc.IpTablesApplyException): # All floating IPs must be put in error state LOG.exception("Failed to process floating IPs.") fip_statuses = self.put_fips_in_error_state() finally: self.update_fip_statuses(fip_statuses) def update_fip_statuses(self, fip_statuses): # Identify floating IPs which were disabled existing_floating_ips = self.floating_ips self.floating_ips = set(fip_statuses.keys()) for fip_id in existing_floating_ips - self.floating_ips: fip_statuses[fip_id] = lib_constants.FLOATINGIP_STATUS_DOWN # filter out statuses that didn't change fip_statuses = {f: stat for f, stat in fip_statuses.items() if stat != FLOATINGIP_STATUS_NOCHANGE} if not fip_statuses: return LOG.debug('Sending floating ip statuses: %s', fip_statuses) # Update floating IP status on the neutron server self.agent.plugin_rpc.update_floatingip_statuses( self.agent.context, self.router_id, fip_statuses) def initialize_address_scope_iptables(self): self._initialize_address_scope_iptables(self.iptables_manager) def _initialize_address_scope_iptables(self, iptables_manager): # Add address scope related chains iptables_manager.ipv4['mangle'].add_chain('scope') iptables_manager.ipv6['mangle'].add_chain('scope') iptables_manager.ipv4['mangle'].add_chain('floatingip') iptables_manager.ipv4['mangle'].add_chain('float-snat') iptables_manager.ipv4['filter'].add_chain('scope') iptables_manager.ipv6['filter'].add_chain('scope') iptables_manager.ipv4['filter'].add_rule('FORWARD', '-j $scope') iptables_manager.ipv6['filter'].add_rule('FORWARD', '-j $scope') # Add rules for marking traffic for address scopes mark_new_ingress_address_scope_by_interface = ( '-j $scope') copy_address_scope_for_existing = ( '-m connmark ! --mark 0x0/0xffff0000 ' '-j CONNMARK --restore-mark ' '--nfmask 0xffff0000 --ctmask 0xffff0000') mark_new_ingress_address_scope_by_floatingip = ( '-j $floatingip') save_mark_to_connmark = ( '-m connmark --mark 0x0/0xffff0000 ' '-j CONNMARK --save-mark ' '--nfmask 0xffff0000 --ctmask 0xffff0000') iptables_manager.ipv4['mangle'].add_rule( 'PREROUTING', mark_new_ingress_address_scope_by_interface) iptables_manager.ipv4['mangle'].add_rule( 'PREROUTING', copy_address_scope_for_existing) # The floating ip scope rules must come after the CONNTRACK rules # because the (CONN)MARK targets are non-terminating (this is true # despite them not being documented as such) and the floating ip # rules need to override the mark from CONNMARK to cross scopes. iptables_manager.ipv4['mangle'].add_rule( 'PREROUTING', mark_new_ingress_address_scope_by_floatingip) iptables_manager.ipv4['mangle'].add_rule( 'float-snat', save_mark_to_connmark) iptables_manager.ipv6['mangle'].add_rule( 'PREROUTING', mark_new_ingress_address_scope_by_interface) iptables_manager.ipv6['mangle'].add_rule( 'PREROUTING', copy_address_scope_for_existing) def initialize_metadata_iptables(self): # Always mark incoming metadata requests, that way any stray # requests that arrive before the filter metadata redirect # rule is installed will be dropped. mark_metadata_for_internal_interfaces = ( '-d 169.254.169.254/32 ' '-i %(interface_name)s ' '-p tcp -m tcp --dport 80 ' '-j MARK --set-xmark %(value)s/%(mask)s' % {'interface_name': INTERNAL_DEV_PREFIX + '+', 'value': self.agent_conf.metadata_access_mark, 'mask': n_const.ROUTER_MARK_MASK}) self.iptables_manager.ipv4['mangle'].add_rule( 'PREROUTING', mark_metadata_for_internal_interfaces) def _get_port_devicename_scopemark(self, ports, name_generator): devicename_scopemark = {lib_constants.IP_VERSION_4: dict(), lib_constants.IP_VERSION_6: dict()} for p in ports: device_name = name_generator(p['id']) ip_cidrs = common_utils.fixed_ip_cidrs(p['fixed_ips']) port_as_marks = self.get_port_address_scope_mark(p) for ip_version in {common_utils.get_ip_version(cidr) for cidr in ip_cidrs}: devicename_scopemark[ip_version][device_name] = ( port_as_marks[ip_version]) return devicename_scopemark def _get_address_scope_mark(self): # Prepare address scope iptables rule for internal ports internal_ports = self.router.get(lib_constants.INTERFACE_KEY, []) ports_scopemark = self._get_port_devicename_scopemark( internal_ports, self.get_internal_device_name) # Prepare address scope iptables rule for external port external_port = self.get_ex_gw_port() if external_port: external_port_scopemark = self._get_port_devicename_scopemark( [external_port], self.get_external_device_name) for ip_version in (lib_constants.IP_VERSION_4, lib_constants.IP_VERSION_6): ports_scopemark[ip_version].update( external_port_scopemark[ip_version]) return ports_scopemark def _add_address_scope_mark(self, iptables_manager, ports_scopemark): external_device_name = None external_port = self.get_ex_gw_port() if external_port: external_device_name = self.get_external_device_name( external_port['id']) # Process address scope iptables rules for ip_version in (lib_constants.IP_VERSION_4, lib_constants.IP_VERSION_6): scopemarks = ports_scopemark[ip_version] iptables = iptables_manager.get_tables(ip_version) iptables['mangle'].empty_chain('scope') iptables['filter'].empty_chain('scope') dont_block_external = (ip_version == lib_constants.IP_VERSION_4 and self._snat_enabled and external_port) for device_name, mark in scopemarks.items(): # Add address scope iptables rule iptables['mangle'].add_rule( 'scope', self.address_scope_mangle_rule(device_name, mark)) if dont_block_external and device_name == external_device_name: continue iptables['filter'].add_rule( 'scope', self.address_scope_filter_rule(device_name, mark)) for subnet_id, prefix in self.pd_subnets.items(): if prefix != lib_constants.PROVISIONAL_IPV6_PD_PREFIX: self._process_pd_iptables_rules(prefix, subnet_id) def process_ports_address_scope_iptables(self): ports_scopemark = self._get_address_scope_mark() self._add_address_scope_mark(self.iptables_manager, ports_scopemark) def _get_external_address_scope(self): external_port = self.get_ex_gw_port() if not external_port: return scopes = external_port.get('address_scopes', {}) return scopes.get(str(lib_constants.IP_VERSION_4)) def process_external_port_address_scope_routing(self, iptables_manager): if not self._snat_enabled: return external_port = self.get_ex_gw_port() if not external_port: return external_devicename = self.get_external_device_name( external_port['id']) # Saves the originating address scope by saving the packet MARK to # the CONNMARK for new connections so that returning traffic can be # match to it. rule = ('-o %s -m connmark --mark 0x0/0xffff0000 ' '-j CONNMARK --save-mark ' '--nfmask 0xffff0000 --ctmask 0xffff0000' % external_devicename) iptables_manager.ipv4['mangle'].add_rule('POSTROUTING', rule) address_scope = self._get_external_address_scope() if not address_scope: return # Prevents snat within the same address scope rule = '-o %s -m connmark --mark %s -j ACCEPT' % ( external_devicename, self.get_address_scope_mark_mask(address_scope)) iptables_manager.ipv4['nat'].add_rule('snat', rule) def process_address_scope(self): with self.iptables_manager.defer_apply(): self.process_ports_address_scope_iptables() self.process_floating_ip_address_scope_rules() @common_utils.exception_logger() def process_delete(self): """Process the delete of this router This method is the point where the agent requests that this router be deleted. This is a separate code path from process in that it avoids any changes to the qrouter namespace that will be removed at the end of the operation. :param agent: Passes the agent in order to send RPC messages. """ LOG.debug("Process delete, router %s", self.router['id']) if self.router_namespace.exists(): self._process_internal_ports() self.agent.pd.sync_router(self.router['id']) self._process_external_on_delete() else: LOG.warning("Can't gracefully delete the router %s: " "no router namespace found", self.router['id']) @common_utils.exception_logger() def process(self): """Process updates to this router This method is the point where the agent requests that updates be applied to this router. :param agent: Passes the agent in order to send RPC messages. """ LOG.debug("Process updates, router %s", self.router['id']) self._process_internal_ports() self.agent.pd.sync_router(self.router['id']) self.process_external() self.process_address_scope() # Process static routes for router self.routes_updated(self.routes, self.router['routes']) self.routes = self.router['routes'] # Update ex_gw_port on the router info cache self.ex_gw_port = self.get_ex_gw_port() self.fip_map = dict([(fip['floating_ip_address'], fip['fixed_ip_address']) for fip in self.get_floating_ips()]) neutron-12.1.1/neutron/agent/l3/l3_agent_extensions_manager.py0000664000175000017500000000612513553660047024452 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mellanox Technologies, Ltd # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log from neutron.agent import agent_extensions_manager as agent_ext_manager from neutron.conf.agent import agent_extensions_manager as agent_ext_mgr_config LOG = log.getLogger(__name__) L3_AGENT_EXT_MANAGER_NAMESPACE = 'neutron.agent.l3.extensions' def register_opts(conf): agent_ext_mgr_config.register_agent_ext_manager_opts(conf) class L3AgentExtensionsManager(agent_ext_manager.AgentExtensionsManager): """Manage l3 agent extensions.""" def __init__(self, conf): super(L3AgentExtensionsManager, self).__init__(conf, L3_AGENT_EXT_MANAGER_NAMESPACE) def add_router(self, context, data): """Notify all agent extensions to add router.""" for extension in self: if hasattr(extension.obj, 'add_router'): extension.obj.add_router(context, data) else: LOG.error( "Agent Extension '%(name)s' does not " "implement method add_router", {'name': extension.name} ) def update_router(self, context, data): """Notify all agent extensions to update router.""" for extension in self: if hasattr(extension.obj, 'update_router'): extension.obj.update_router(context, data) else: LOG.error( "Agent Extension '%(name)s' does not " "implement method update_router", {'name': extension.name} ) def delete_router(self, context, data): """Notify all agent extensions to delete router.""" for extension in self: if hasattr(extension.obj, 'delete_router'): extension.obj.delete_router(context, data) else: LOG.error( "Agent Extension '%(name)s' does not " "implement method delete_router", {'name': extension.name} ) def ha_state_change(self, context, data): """Notify all agent extensions for HA router state change.""" for extension in self: if hasattr(extension.obj, 'ha_state_change'): extension.obj.ha_state_change(context, data) else: LOG.warning( "Agent Extension '%(name)s' does not " "implement method ha_state_change", {'name': extension.name} ) neutron-12.1.1/neutron/agent/l3/namespace_manager.py0000664000175000017500000001331013553660046022424 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from neutron.agent.l3 import dvr_fip_ns from neutron.agent.l3 import dvr_snat_ns from neutron.agent.l3 import namespaces from neutron.agent.linux import external_process from neutron.agent.linux import ip_lib LOG = logging.getLogger(__name__) class NamespaceManager(object): """Keeps track of namespaces that need to be cleaned up. This is a context manager that looks to clean up stale namespaces that have not been touched by the end of the "with" statement it is called in. This formalizes the pattern used in the L3 agent which enumerated all of the namespaces known to the system before a full sync. Then, after the full sync completed, it cleaned up any that were not touched during the sync. The agent and this context manager use method keep_router to communicate. In the "with" statement, the agent calls keep_router to record the id's of the routers whose namespaces should be preserved. Any other router and snat namespace present in the system will be deleted by the __exit__ method of this context manager This pattern can be more generally applicable to other resources besides namespaces in the future because it is idempotent and, as such, does not rely on state recorded at runtime in the agent so it handles agent restarts gracefully. """ ns_prefix_to_class_map = { namespaces.NS_PREFIX: namespaces.RouterNamespace, dvr_snat_ns.SNAT_NS_PREFIX: dvr_snat_ns.SnatNamespace, dvr_fip_ns.FIP_NS_PREFIX: dvr_fip_ns.FipNamespace, } def __init__(self, agent_conf, driver, metadata_driver=None): """Initialize the NamespaceManager. :param agent_conf: configuration from l3 agent :param driver: to perform operations on devices :param metadata_driver: used to cleanup stale metadata proxy processes """ self.agent_conf = agent_conf self.driver = driver self._clean_stale = True self.metadata_driver = metadata_driver if metadata_driver: self.process_monitor = external_process.ProcessMonitor( config=agent_conf, resource_type='router') def __enter__(self): self._all_namespaces = set() self._ids_to_keep = set() if self._clean_stale: self._all_namespaces = self.list_all() return self def __exit__(self, exc_type, value, traceback): # TODO(carl) Preserves old behavior of L3 agent where cleaning # namespaces was only done once after restart. Still a good idea? if exc_type: # An exception occurred in the caller's with statement return False if not self._clean_stale: # No need to cleanup return True self._clean_stale = False for ns in self._all_namespaces: _ns_prefix, ns_id = self.get_prefix_and_id(ns) if ns_id in self._ids_to_keep: continue self._cleanup(_ns_prefix, ns_id) return True def keep_router(self, router_id): self._ids_to_keep.add(router_id) def keep_ext_net(self, ext_net_id): self._ids_to_keep.add(ext_net_id) def get_prefix_and_id(self, ns_name): """Get the prefix and id from the namespace name. :param ns_name: The name of the namespace :returns: tuple with prefix and id or None if no prefix matches """ prefix = namespaces.get_prefix_from_ns_name(ns_name) if prefix in self.ns_prefix_to_class_map: identifier = namespaces.get_id_from_ns_name(ns_name) return (prefix, identifier) def is_managed(self, ns_name): """Return True if the namespace name passed belongs to this manager.""" return self.get_prefix_and_id(ns_name) is not None def list_all(self): """Get a set of all namespaces on host managed by this manager.""" try: namespaces = ip_lib.list_network_namespaces() return set(ns for ns in namespaces if self.is_managed(ns)) except RuntimeError: LOG.exception('RuntimeError in obtaining namespace list for ' 'namespace cleanup.') return set() def ensure_router_cleanup(self, router_id): """Performs cleanup for a router""" for ns in self.list_all(): if ns.endswith(router_id): ns_prefix, ns_id = self.get_prefix_and_id(ns) self._cleanup(ns_prefix, ns_id) def ensure_snat_cleanup(self, router_id): prefix = dvr_snat_ns.SNAT_NS_PREFIX self._cleanup(prefix, router_id) def _cleanup(self, ns_prefix, ns_id): ns_class = self.ns_prefix_to_class_map[ns_prefix] ns = ns_class(ns_id, self.agent_conf, self.driver, use_ipv6=False) try: if self.metadata_driver: # cleanup stale metadata proxy processes first self.metadata_driver.destroy_monitored_metadata_proxy( self.process_monitor, ns_id, self.agent_conf, ns.name) ns.delete() except RuntimeError: LOG.exception('Failed to destroy stale namespace %s', ns) neutron-12.1.1/neutron/agent/l3/__init__.py0000664000175000017500000000000013553660046020525 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/agent/l3/fip_rule_priority_allocator.py0000664000175000017500000000373213553660046024613 0ustar zuulzuul00000000000000# Copyright 2015 IBM Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.l3.item_allocator import ItemAllocator class FipPriority(object): def __init__(self, index): self.index = index def __repr__(self): return str(self.index) def __hash__(self): return hash(self.__repr__()) def __eq__(self, other): if isinstance(other, FipPriority): return (self.index == other.index) else: return False class FipRulePriorityAllocator(ItemAllocator): """Manages allocation of floating ips rule priorities. IP rule priorities assigned to DVR floating IPs need to be preserved over L3 agent restarts. This class provides an allocator which saves the priorities to a datastore which will survive L3 agent restarts. """ def __init__(self, data_store_path, priority_rule_start, priority_rule_end): """Create the necessary pool and create the item allocator using ',' as the delimiter and FipRulePriorityAllocator as the class type """ pool = set(FipPriority(str(s)) for s in range(priority_rule_start, priority_rule_end)) super(FipRulePriorityAllocator, self).__init__(data_store_path, FipPriority, pool) neutron-12.1.1/neutron/agent/l3/ha.py0000664000175000017500000001761413553660047017402 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import eventlet from oslo_log import log as logging from oslo_utils import fileutils import webob from neutron.agent.linux import utils as agent_utils from neutron.common import constants from neutron.notifiers import batch_notifier LOG = logging.getLogger(__name__) KEEPALIVED_STATE_CHANGE_SERVER_BACKLOG = 4096 TRANSLATION_MAP = {'master': constants.HA_ROUTER_STATE_ACTIVE, 'backup': constants.HA_ROUTER_STATE_STANDBY, 'fault': constants.HA_ROUTER_STATE_STANDBY} class KeepalivedStateChangeHandler(object): def __init__(self, agent): self.agent = agent @webob.dec.wsgify(RequestClass=webob.Request) def __call__(self, req): router_id = req.headers['X-Neutron-Router-Id'] state = req.headers['X-Neutron-State'] self.enqueue(router_id, state) def enqueue(self, router_id, state): LOG.debug('Handling notification for router ' '%(router_id)s, state %(state)s', {'router_id': router_id, 'state': state}) self.agent.enqueue_state_change(router_id, state) class L3AgentKeepalivedStateChangeServer(object): def __init__(self, agent, conf): self.agent = agent self.conf = conf agent_utils.ensure_directory_exists_without_file( self.get_keepalived_state_change_socket_path(self.conf)) @classmethod def get_keepalived_state_change_socket_path(cls, conf): return os.path.join(conf.state_path, 'keepalived-state-change') def run(self): server = agent_utils.UnixDomainWSGIServer( 'neutron-keepalived-state-change', num_threads=self.conf.ha_keepalived_state_change_server_threads) server.start(KeepalivedStateChangeHandler(self.agent), self.get_keepalived_state_change_socket_path(self.conf), workers=0, backlog=KEEPALIVED_STATE_CHANGE_SERVER_BACKLOG) server.wait() class AgentMixin(object): def __init__(self, host): self._init_ha_conf_path() super(AgentMixin, self).__init__(host) # BatchNotifier queue is needed to ensure that the HA router # state change sequence is under the proper order. self.state_change_notifier = batch_notifier.BatchNotifier( self._calculate_batch_duration(), self.notify_server) eventlet.spawn(self._start_keepalived_notifications_server) def _get_router_info(self, router_id): try: return self.router_info[router_id] except KeyError: LOG.info('Router %s is not managed by this agent. It was ' 'possibly deleted concurrently.', router_id) def check_ha_state_for_router(self, router_id, current_state): ri = self._get_router_info(router_id) if ri and current_state != TRANSLATION_MAP[ri.ha_state]: LOG.debug("Updating server with state %(state)s for router " "%(router_id)s", {'router_id': router_id, 'state': ri.ha_state}) self.state_change_notifier.queue_event((router_id, ri.ha_state)) def _start_keepalived_notifications_server(self): state_change_server = ( L3AgentKeepalivedStateChangeServer(self, self.conf)) state_change_server.run() def _calculate_batch_duration(self): # Set the BatchNotifier interval to ha_vrrp_advert_int, # default 2 seconds. return self.conf.ha_vrrp_advert_int def enqueue_state_change(self, router_id, state): state_change_data = {"router_id": router_id, "state": state} LOG.info('Router %(router_id)s transitioned to %(state)s', state_change_data) ri = self._get_router_info(router_id) if ri is None: return # TODO(dalvarez): Fix bug 1677279 by moving the IPv6 parameters # configuration to keepalived-state-change in order to remove the # dependency that currently exists on l3-agent running for the IPv6 # failover. self._configure_ipv6_params(ri, state) if self.conf.enable_metadata_proxy: self._update_metadata_proxy(ri, router_id, state) self._update_radvd_daemon(ri, state) self.pd.process_ha_state(router_id, state == 'master') self.state_change_notifier.queue_event((router_id, state)) self.l3_ext_manager.ha_state_change(self.context, state_change_data) def _configure_ipv6_params(self, ri, state): if not self.use_ipv6: return ipv6_forwarding_enable = state == 'master' if ri.router.get('distributed', False): namespace = ri.ha_namespace else: namespace = ri.ns_name if ipv6_forwarding_enable: ri.driver.configure_ipv6_forwarding( namespace, 'all', ipv6_forwarding_enable) # If ipv6 is enabled on the platform, ipv6_gateway config flag is # not set and external_network associated to the router does not # include any IPv6 subnet, enable the gateway interface to accept # Router Advts from upstream router for default route on master # instances as well as ipv6 forwarding. Otherwise, disable them. ex_gw_port_id = ri.ex_gw_port and ri.ex_gw_port['id'] if ex_gw_port_id: interface_name = ri.get_external_device_name(ex_gw_port_id) ri._configure_ipv6_params_on_gw( ri.ex_gw_port, namespace, interface_name, ipv6_forwarding_enable) def _update_metadata_proxy(self, ri, router_id, state): # NOTE(slaweq): Since the metadata proxy is spawned in the qrouter # namespace and not in the snat namespace, even standby DVR-HA # routers needs to serve metadata requests to local ports. if state == 'master' or ri.router.get('distributed', False): LOG.debug('Spawning metadata proxy for router %s', router_id) self.metadata_driver.spawn_monitored_metadata_proxy( self.process_monitor, ri.ns_name, self.conf.metadata_port, self.conf, router_id=ri.router_id) else: LOG.debug('Closing metadata proxy for router %s', router_id) self.metadata_driver.destroy_monitored_metadata_proxy( self.process_monitor, ri.router_id, self.conf, ri.ns_name) def _update_radvd_daemon(self, ri, state): # Radvd has to be spawned only on the Master HA Router. If there are # any state transitions, we enable/disable radvd accordingly. if state == 'master': ri.enable_radvd() else: ri.disable_radvd() def notify_server(self, batched_events): eventlet.spawn_n(self._notify_server, batched_events) def _notify_server(self, batched_events): translated_states = dict((router_id, TRANSLATION_MAP[state]) for router_id, state in batched_events) LOG.debug('Updating server with HA routers states %s', translated_states) self.plugin_rpc.update_ha_routers_states( self.context, translated_states) def _init_ha_conf_path(self): ha_full_path = os.path.dirname("/%s/" % self.conf.ha_confs_path) fileutils.ensure_tree(ha_full_path, mode=0o755) neutron-12.1.1/neutron/agent/l3/dvr_edge_ha_router.py0000664000175000017500000001313013553660047022626 0ustar zuulzuul00000000000000# Copyright (c) 2015 Hewlett-Packard Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron.agent.l3 import dvr_edge_router from neutron.agent.l3 import ha_router from neutron.agent.l3 import router_info class DvrEdgeHaRouter(dvr_edge_router.DvrEdgeRouter, ha_router.HaRouter): """Router class which represents a centralized SNAT DVR router with HA capabilities. """ def __init__(self, host, *args, **kwargs): super(DvrEdgeHaRouter, self).__init__(host, *args, **kwargs) self.enable_snat = None @property def ha_namespace(self): if self.snat_namespace: return self.snat_namespace.name return None def internal_network_added(self, port): # Call RouterInfo's internal_network_added (Plugs the port, adds IP) router_info.RouterInfo.internal_network_added(self, port) for subnet in port['subnets']: self._set_subnet_arp_info(subnet['id']) self._snat_redirect_add_from_port(port) if not self.get_ex_gw_port() or not self._is_this_snat_host(): return sn_port = self.get_snat_port_for_internal_port(port) if not sn_port: return self._plug_ha_router_port( sn_port, self._get_snat_int_device_name, constants.SNAT_INT_DEV_PREFIX) def add_centralized_floatingip(self, fip, fip_cidr): interface_name = self.get_snat_external_device_interface_name( self.get_ex_gw_port()) self._add_vip(fip_cidr, interface_name) self.set_ha_port() if (self.is_router_master() and self.ha_port and self.ha_port['status'] == constants.PORT_STATUS_ACTIVE): return super(DvrEdgeHaRouter, self).add_centralized_floatingip( fip, fip_cidr) else: return constants.FLOATINGIP_STATUS_ACTIVE def remove_centralized_floatingip(self, fip_cidr): self._remove_vip(fip_cidr) if self.is_router_master(): super(DvrEdgeHaRouter, self).remove_centralized_floatingip( fip_cidr) def get_centralized_fip_cidr_set(self): ex_gw_port = self.get_ex_gw_port() if not ex_gw_port: return set() interface_name = self.get_snat_external_device_interface_name( ex_gw_port) return set(self._get_cidrs_from_keepalived(interface_name)) def external_gateway_added(self, ex_gw_port, interface_name): super(DvrEdgeHaRouter, self).external_gateway_added( ex_gw_port, interface_name) for port in self.get_snat_interfaces(): snat_interface_name = self._get_snat_int_device_name(port['id']) self._disable_ipv6_addressing_on_interface(snat_interface_name) self._add_vips( self.get_snat_port_for_internal_port(port), snat_interface_name) self._add_gateway_vip(ex_gw_port, interface_name) self._disable_ipv6_addressing_on_interface(interface_name) def external_gateway_removed(self, ex_gw_port, interface_name): for port in self.snat_ports: snat_interface = self._get_snat_int_device_name(port['id']) self.driver.unplug(snat_interface, namespace=self.ha_namespace, prefix=constants.SNAT_INT_DEV_PREFIX) self._clear_vips(snat_interface) super(DvrEdgeHaRouter, self)._external_gateway_removed( ex_gw_port, interface_name) self._clear_vips(interface_name) def external_gateway_updated(self, ex_gw_port, interface_name): ha_router.HaRouter.external_gateway_updated(self, ex_gw_port, interface_name) def initialize(self, process_monitor): self._create_snat_namespace() super(DvrEdgeHaRouter, self).initialize(process_monitor) def _external_gateway_added(self, ex_gw_port, interface_name, ns_name, preserve_ips): self._plug_external_gateway(ex_gw_port, interface_name, ns_name) def _is_this_snat_host(self): return (self.agent_conf.agent_mode == constants.L3_AGENT_MODE_DVR_SNAT) def _dvr_internal_network_removed(self, port): super(DvrEdgeHaRouter, self)._dvr_internal_network_removed(port) sn_port = self.get_snat_port_for_internal_port(port, self.snat_ports) if not sn_port: return self._clear_vips(self._get_snat_int_device_name(sn_port['id'])) def _plug_snat_port(self, port): """Used by _create_dvr_gateway in DvrEdgeRouter.""" interface_name = self._get_snat_int_device_name(port['id']) self.driver.plug(port['network_id'], port['id'], interface_name, port['mac_address'], namespace=self.snat_namespace.name, prefix=constants.SNAT_INT_DEV_PREFIX, mtu=port.get('mtu')) neutron-12.1.1/neutron/agent/l3/namespaces.py0000664000175000017500000001212513553660047021121 0ustar zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import functools from oslo_log import log as logging from oslo_utils import excutils from neutron.agent.linux import ip_lib LOG = logging.getLogger(__name__) NS_PREFIX = 'qrouter-' INTERNAL_DEV_PREFIX = 'qr-' EXTERNAL_DEV_PREFIX = 'qg-' # TODO(Carl) It is odd that this file needs this. It is a dvr detail. ROUTER_2_FIP_DEV_PREFIX = 'rfp-' def build_ns_name(prefix, identifier): """Builds a namespace name from the given prefix and identifier :param prefix: The prefix which must end with '-' for legacy reasons :param identifier: The id associated with the namespace """ return prefix + identifier def get_prefix_from_ns_name(ns_name): """Parses prefix from prefix-identifier :param ns_name: The name of a namespace :returns: The prefix ending with a '-' or None if there is no '-' """ dash_index = ns_name.find('-') if 0 <= dash_index: return ns_name[:dash_index + 1] def get_id_from_ns_name(ns_name): """Parses identifier from prefix-identifier :param ns_name: The name of a namespace :returns: Identifier or None if there is no - to end the prefix """ dash_index = ns_name.find('-') if 0 <= dash_index: return ns_name[dash_index + 1:] def check_ns_existence(f): @functools.wraps(f) def wrapped(self, *args, **kwargs): if not self.exists(): LOG.warning('Namespace %(name)s does not exist. Skipping ' '%(func)s', {'name': self.name, 'func': f.__name__}) return try: return f(self, *args, **kwargs) except RuntimeError: with excutils.save_and_reraise_exception() as ctx: if not self.exists(): LOG.debug('Namespace %(name)s was concurrently deleted', self.name) ctx.reraise = False return wrapped class Namespace(object): def __init__(self, name, agent_conf, driver, use_ipv6): self.name = name self.ip_wrapper_root = ip_lib.IPWrapper() self.agent_conf = agent_conf self.driver = driver self.use_ipv6 = use_ipv6 def create(self, ipv6_forwarding=True): # See networking (netdev) tree, file # Documentation/networking/ip-sysctl.txt for an explanation of # these sysctl values. ip_wrapper = self.ip_wrapper_root.ensure_namespace(self.name) cmd = ['sysctl', '-w', 'net.ipv4.ip_forward=1'] ip_wrapper.netns.execute(cmd) # 1. Reply only if the target IP address is local address configured # on the incoming interface; and # 2. Always use the best local address cmd = ['sysctl', '-w', 'net.ipv4.conf.all.arp_ignore=1'] ip_wrapper.netns.execute(cmd) cmd = ['sysctl', '-w', 'net.ipv4.conf.all.arp_announce=2'] ip_wrapper.netns.execute(cmd) if self.use_ipv6: cmd = ['sysctl', '-w', 'net.ipv6.conf.all.forwarding=%d' % int(ipv6_forwarding)] ip_wrapper.netns.execute(cmd) def delete(self): try: self.ip_wrapper_root.netns.delete(self.name) except RuntimeError: msg = 'Failed trying to delete namespace: %s' LOG.exception(msg, self.name) def exists(self): return self.ip_wrapper_root.netns.exists(self.name) class RouterNamespace(Namespace): def __init__(self, router_id, agent_conf, driver, use_ipv6): self.router_id = router_id name = self._get_ns_name(router_id) super(RouterNamespace, self).__init__( name, agent_conf, driver, use_ipv6) @classmethod def _get_ns_name(cls, router_id): return build_ns_name(NS_PREFIX, router_id) @check_ns_existence def delete(self): ns_ip = ip_lib.IPWrapper(namespace=self.name) for d in ns_ip.get_devices(): if d.name.startswith(INTERNAL_DEV_PREFIX): # device is on default bridge self.driver.unplug(d.name, namespace=self.name, prefix=INTERNAL_DEV_PREFIX) elif d.name.startswith(ROUTER_2_FIP_DEV_PREFIX): ns_ip.del_veth(d.name) elif d.name.startswith(EXTERNAL_DEV_PREFIX): self.driver.unplug( d.name, bridge=self.agent_conf.external_network_bridge, namespace=self.name, prefix=EXTERNAL_DEV_PREFIX) super(RouterNamespace, self).delete() neutron-12.1.1/neutron/agent/l3/link_local_allocator.py0000664000175000017500000000427613553660046023160 0ustar zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron.agent.l3.item_allocator import ItemAllocator class LinkLocalAddressPair(netaddr.IPNetwork): def __init__(self, addr): super(LinkLocalAddressPair, self).__init__(addr) def get_pair(self): """Builds an address pair from the first and last addresses. """ # TODO(kevinbenton): the callers of this seem only interested in an IP, # so we should just return two IPAddresses. return (netaddr.IPNetwork("%s/%s" % (self.network, self.prefixlen)), netaddr.IPNetwork("%s/%s" % (self[-1], self.prefixlen))) class LinkLocalAllocator(ItemAllocator): """Manages allocation of link local IP addresses. These link local addresses are used for routing inside the fip namespaces. The associations need to persist across agent restarts to maintain consistency. Without this, there is disruption in network connectivity as the agent rewires the connections with the new IP address associations. Persisting these in the database is unnecessary and would degrade performance. """ def __init__(self, data_store_path, subnet): """Create the necessary pool and item allocator using ',' as the delimiter and LinkLocalAllocator as the class type """ subnet = netaddr.IPNetwork(subnet) pool = set(LinkLocalAddressPair(s) for s in subnet.subnet(31)) super(LinkLocalAllocator, self).__init__(data_store_path, LinkLocalAddressPair, pool) neutron-12.1.1/neutron/agent/l3/l3_agent_extension.py0000664000175000017500000000344013553660047022572 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import six from neutron.agent import agent_extension @six.add_metaclass(abc.ABCMeta) class L3AgentCoreResourceExtension(agent_extension.AgentExtension): """Define stable abstract interface for l3 agent extensions. An agent extension extends the agent core functionality. """ @abc.abstractmethod def add_router(self, context, data): """add agent extension for router. Called on router create. :param context: rpc context :param data: router data """ @abc.abstractmethod def update_router(self, context, data): """Handle agent extension for update. Called on router update. :param context: rpc context :param data: router data """ @abc.abstractmethod def delete_router(self, context, data): """Delete router from agent extension. :param context: rpc context :param data: router data """ @abc.abstractmethod def ha_state_change(self, context, data): """Change router state from agent extension. Called on HA router state change. :param context: rpc context :param data: dict of router_id and new state """ neutron-12.1.1/neutron/agent/l3/dvr_snat_ns.py0000664000175000017500000000472413553660047021330 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from oslo_log import log as logging from neutron.agent.l3 import namespaces from neutron.agent.linux import ip_lib LOG = logging.getLogger(__name__) SNAT_NS_PREFIX = 'snat-' class SnatNamespace(namespaces.Namespace): def __init__(self, router_id, agent_conf, driver, use_ipv6): self.router_id = router_id name = self.get_snat_ns_name(router_id) super(SnatNamespace, self).__init__( name, agent_conf, driver, use_ipv6) def create(self): super(SnatNamespace, self).create() # This might be an HA router namespaces and it should not have # ip_nonlocal_bind enabled ip_lib.set_ip_nonlocal_bind_for_namespace(self.name, 0) # Set nf_conntrack_tcp_loose to 0 to ensure mid-stream # TCP conversations aren't taken over by SNAT cmd = ['net.netfilter.nf_conntrack_tcp_loose=0'] ip_lib.sysctl(cmd, namespace=self.name) @classmethod def get_snat_ns_name(cls, router_id): return namespaces.build_ns_name(SNAT_NS_PREFIX, router_id) @namespaces.check_ns_existence def delete(self): ns_ip = ip_lib.IPWrapper(namespace=self.name) for d in ns_ip.get_devices(): if d.name.startswith(constants.SNAT_INT_DEV_PREFIX): LOG.debug('Unplugging DVR device %s', d.name) self.driver.unplug(d.name, namespace=self.name, prefix=constants.SNAT_INT_DEV_PREFIX) elif d.name.startswith(namespaces.EXTERNAL_DEV_PREFIX): self.driver.unplug( d.name, bridge=self.agent_conf.external_network_bridge, namespace=self.name, prefix=namespaces.EXTERNAL_DEV_PREFIX) # TODO(mrsmith): delete ext-gw-port LOG.debug('DVR: destroy snat ns: %s', self.name) super(SnatNamespace, self).delete() neutron-12.1.1/neutron/wsgi.py0000664000175000017500000007032713553660047016347 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Utility methods for working with WSGI servers """ import errno import socket import sys import time import eventlet.wsgi from neutron_lib import context from neutron_lib import exceptions as exception from neutron_lib import worker as neutron_worker from oslo_config import cfg import oslo_i18n from oslo_log import log as logging from oslo_serialization import jsonutils from oslo_service import service as common_service from oslo_service import sslutils from oslo_service import systemd from oslo_service import wsgi from oslo_utils import encodeutils from oslo_utils import excutils import six import webob.dec import webob.exc from neutron._i18n import _ from neutron.common import config from neutron.common import exceptions as n_exc from neutron.conf import wsgi as wsgi_config from neutron.db import api CONF = cfg.CONF wsgi_config.register_socket_opts() LOG = logging.getLogger(__name__) def encode_body(body): """Encode unicode body. WebOb requires to encode unicode body used to update response body. """ return encodeutils.to_utf8(body) class WorkerService(neutron_worker.BaseWorker): """Wraps a worker to be handled by ProcessLauncher""" def __init__(self, service, application, disable_ssl=False, worker_process_count=0): super(WorkerService, self).__init__(worker_process_count) self._service = service self._application = application self._disable_ssl = disable_ssl self._server = None def start(self): super(WorkerService, self).start() # When api worker is stopped it kills the eventlet wsgi server which # internally closes the wsgi server socket object. This server socket # object becomes not usable which leads to "Bad file descriptor" # errors on service restart. # Duplicate a socket object to keep a file descriptor usable. dup_sock = self._service._socket.dup() if CONF.use_ssl and not self._disable_ssl: dup_sock = sslutils.wrap(CONF, dup_sock) self._server = self._service.pool.spawn(self._service._run, self._application, dup_sock) def wait(self): if isinstance(self._server, eventlet.greenthread.GreenThread): self._server.wait() def stop(self): if isinstance(self._server, eventlet.greenthread.GreenThread): self._server.kill() self._server = None @staticmethod def reset(): config.reset_service() class Server(object): """Server class to manage multiple WSGI sockets and applications.""" def __init__(self, name, num_threads=None, disable_ssl=False): # Raise the default from 8192 to accommodate large tokens eventlet.wsgi.MAX_HEADER_LINE = CONF.max_header_line self.num_threads = num_threads or CONF.wsgi_default_pool_size self.disable_ssl = disable_ssl # Pool for a greenthread in which wsgi server will be running self.pool = eventlet.GreenPool(1) self.name = name self._server = None # A value of 0 is converted to None because None is what causes the # wsgi server to wait forever. self.client_socket_timeout = CONF.client_socket_timeout or None if CONF.use_ssl and not self.disable_ssl: sslutils.is_enabled(CONF) def _get_socket(self, host, port, backlog): bind_addr = (host, port) # TODO(dims): eventlet's green dns/socket module does not actually # support IPv6 in getaddrinfo(). We need to get around this in the # future or monitor upstream for a fix try: info = socket.getaddrinfo(bind_addr[0], bind_addr[1], socket.AF_UNSPEC, socket.SOCK_STREAM)[0] family = info[0] bind_addr = info[-1] except Exception: LOG.exception("Unable to listen on %(host)s:%(port)s", {'host': host, 'port': port}) sys.exit(1) sock = None retry_until = time.time() + CONF.retry_until_window while not sock and time.time() < retry_until: try: sock = eventlet.listen(bind_addr, backlog=backlog, family=family) except socket.error as err: with excutils.save_and_reraise_exception() as ctxt: if err.errno == errno.EADDRINUSE: ctxt.reraise = False eventlet.sleep(0.1) if not sock: raise RuntimeError(_("Could not bind to %(host)s:%(port)s " "after trying for %(time)d seconds") % {'host': host, 'port': port, 'time': CONF.retry_until_window}) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # sockets can hang around forever without keepalive sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # This option isn't available in the OS X version of eventlet if hasattr(socket, 'TCP_KEEPIDLE'): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, CONF.tcp_keepidle) return sock def start(self, application, port, host='0.0.0.0', workers=0): """Run a WSGI server with the given application.""" self._host = host self._port = port backlog = CONF.backlog self._socket = self._get_socket(self._host, self._port, backlog=backlog) self._launch(application, workers) def _launch(self, application, workers=0): service = WorkerService(self, application, self.disable_ssl, workers) if workers < 1: # The API service should run in the current process. self._server = service # Dump the initial option values cfg.CONF.log_opt_values(LOG, logging.DEBUG) service.start() systemd.notify_once() else: # dispose the whole pool before os.fork, otherwise there will # be shared DB connections in child processes which may cause # DB errors. api.context_manager.dispose_pool() # The API service runs in a number of child processes. # Minimize the cost of checking for child exit by extending the # wait interval past the default of 0.01s. self._server = common_service.ProcessLauncher(cfg.CONF, wait_interval=1.0) self._server.launch_service(service, workers=service.worker_process_count) @property def host(self): return self._socket.getsockname()[0] if self._socket else self._host @property def port(self): return self._socket.getsockname()[1] if self._socket else self._port def stop(self): self._server.stop() def wait(self): """Wait until all servers have completed running.""" try: self._server.wait() except KeyboardInterrupt: pass def _run(self, application, socket): """Start a WSGI server in a new green thread.""" eventlet.wsgi.server(socket, application, max_size=self.num_threads, log=LOG, keepalive=CONF.wsgi_keep_alive, log_format=CONF.wsgi_log_format, socket_timeout=self.client_socket_timeout) class Request(wsgi.Request): def best_match_content_type(self): """Determine the most acceptable content-type. Based on: 1) URI extension (.json) 2) Content-type header 3) Accept* headers """ # First lookup http request path parts = self.path.rsplit('.', 1) if len(parts) > 1: _format = parts[1] if _format in ['json']: return 'application/{0}'.format(_format) #Then look up content header type_from_header = self.get_content_type() if type_from_header: return type_from_header ctypes = ['application/json'] #Finally search in Accept-* headers bm = self.accept.best_match(ctypes) return bm or 'application/json' def get_content_type(self): allowed_types = ("application/json",) if "Content-Type" not in self.headers: LOG.debug("Missing Content-Type") return None _type = self.content_type if _type in allowed_types: return _type return None def best_match_language(self): """Determines best available locale from the Accept-Language header. :returns: the best language match or None if the 'Accept-Language' header was not available in the request. """ if not self.accept_language: return None all_languages = oslo_i18n.get_available_languages('neutron') return self.accept_language.best_match(all_languages) @property def context(self): if 'neutron.context' not in self.environ: self.environ['neutron.context'] = context.get_admin_context() return self.environ['neutron.context'] class ActionDispatcher(object): """Maps method name to local methods through action name.""" def dispatch(self, *args, **kwargs): """Find and call local method.""" action = kwargs.pop('action', 'default') action_method = getattr(self, str(action), self.default) return action_method(*args, **kwargs) def default(self, data): raise NotImplementedError() class DictSerializer(ActionDispatcher): """Default request body serialization.""" def serialize(self, data, action='default'): return self.dispatch(data, action=action) def default(self, data): return "" class JSONDictSerializer(DictSerializer): """Default JSON request body serialization.""" def default(self, data): def sanitizer(obj): return six.text_type(obj) return encode_body(jsonutils.dumps(data, default=sanitizer)) class ResponseHeaderSerializer(ActionDispatcher): """Default response headers serialization.""" def serialize(self, response, data, action): self.dispatch(response, data, action=action) def default(self, response, data): response.status_int = 200 class ResponseSerializer(object): """Encode the necessary pieces into a response object.""" def __init__(self, body_serializers=None, headers_serializer=None): self.body_serializers = { 'application/json': JSONDictSerializer(), } self.body_serializers.update(body_serializers or {}) self.headers_serializer = (headers_serializer or ResponseHeaderSerializer()) def serialize(self, response_data, content_type, action='default'): """Serialize a dict into a string and wrap in a wsgi.Request object. :param response_data: dict produced by the Controller :param content_type: expected mimetype of serialized response body """ response = webob.Response() self.serialize_headers(response, response_data, action) self.serialize_body(response, response_data, content_type, action) return response def serialize_headers(self, response, data, action): self.headers_serializer.serialize(response, data, action) def serialize_body(self, response, data, content_type, action): response.headers['Content-Type'] = content_type if data is not None: serializer = self.get_body_serializer(content_type) response.body = serializer.serialize(data, action) def get_body_serializer(self, content_type): try: return self.body_serializers[content_type] except (KeyError, TypeError): raise exception.InvalidContentType(content_type=content_type) class TextDeserializer(ActionDispatcher): """Default request body deserialization.""" def deserialize(self, datastring, action='default'): return self.dispatch(datastring, action=action) def default(self, datastring): return {} class JSONDeserializer(TextDeserializer): def _from_json(self, datastring): try: return jsonutils.loads(datastring) except ValueError: msg = _("Cannot understand JSON") raise n_exc.MalformedRequestBody(reason=msg) def default(self, datastring): return {'body': self._from_json(datastring)} class RequestHeadersDeserializer(ActionDispatcher): """Default request headers deserializer.""" def deserialize(self, request, action): return self.dispatch(request, action=action) def default(self, request): return {} class RequestDeserializer(object): """Break up a Request object into more useful pieces.""" def __init__(self, body_deserializers=None, headers_deserializer=None): self.body_deserializers = { 'application/json': JSONDeserializer(), } self.body_deserializers.update(body_deserializers or {}) self.headers_deserializer = (headers_deserializer or RequestHeadersDeserializer()) def deserialize(self, request): """Extract necessary pieces of the request. :param request: Request object :returns: tuple of expected controller action name, dictionary of keyword arguments to pass to the controller, the expected content type of the response """ action_args = self.get_action_args(request.environ) action = action_args.pop('action', None) action_args.update(self.deserialize_headers(request, action)) action_args.update(self.deserialize_body(request, action)) accept = self.get_expected_content_type(request) return (action, action_args, accept) def deserialize_headers(self, request, action): return self.headers_deserializer.deserialize(request, action) def deserialize_body(self, request, action): try: content_type = request.best_match_content_type() except exception.InvalidContentType: LOG.debug("Unrecognized Content-Type provided in request") return {} if content_type is None: LOG.debug("No Content-Type provided in request") return {} if not len(request.body) > 0: LOG.debug("Empty body provided in request") return {} try: deserializer = self.get_body_deserializer(content_type) except exception.InvalidContentType: with excutils.save_and_reraise_exception(): LOG.debug("Unable to deserialize body as provided " "Content-Type") return deserializer.deserialize(request.body, action) def get_body_deserializer(self, content_type): try: return self.body_deserializers[content_type] except (KeyError, TypeError): raise exception.InvalidContentType(content_type=content_type) def get_expected_content_type(self, request): return request.best_match_content_type() def get_action_args(self, request_environment): """Parse dictionary created by routes library.""" try: args = request_environment['wsgiorg.routing_args'][1].copy() except Exception: return {} try: del args['controller'] except KeyError: pass try: del args['format'] except KeyError: pass return args class Application(object): """Base WSGI application wrapper. Subclasses need to implement __call__.""" @classmethod def factory(cls, global_config, **local_config): """Used for paste app factories in paste.deploy config files. Any local configuration (that is, values under the [app:APPNAME] section of the paste config) will be passed into the `__init__` method as kwargs. A hypothetical configuration would look like: [app:wadl] latest_version = 1.3 paste.app_factory = nova.api.fancy_api:Wadl.factory which would result in a call to the `Wadl` class as import neutron.api.fancy_api fancy_api.Wadl(latest_version='1.3') You could of course re-implement the `factory` method in subclasses, but using the kwarg passing it shouldn't be necessary. """ return cls(**local_config) def __call__(self, environ, start_response): r"""Subclasses will probably want to implement __call__ like this: @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): # Any of the following objects work as responses: # Option 1: simple string res = 'message\n' # Option 2: a nicely formatted HTTP exception page res = exc.HTTPForbidden(explanation='Nice try') # Option 3: a webob Response object (in case you need to play with # headers, or you want to be treated like an iterable, or or or) res = Response(); res.app_iter = open('somefile') # Option 4: any wsgi app to be run next res = self.application # Option 5: you can get a Response object for a wsgi app, too, to # play with headers etc res = req.get_response(self.application) # You can then just return your response... return res # ... or set req.response and return None. req.response = res See the end of http://pythonpaste.org/webob/modules/dec.html for more info. """ raise NotImplementedError(_('You must implement __call__')) class Resource(Application): """WSGI app that handles (de)serialization and controller dispatch. WSGI app that reads routing information supplied by RoutesMiddleware and calls the requested action method upon its controller. All controller action methods must accept a 'req' argument, which is the incoming wsgi.Request. If the operation is a PUT or POST, the controller method must also accept a 'body' argument (the deserialized request body). They may raise a webob.exc exception or return a dict, which will be serialized by requested content type. """ def __init__(self, controller, fault_body_function, deserializer=None, serializer=None): """Object initialization. :param controller: object that implement methods created by routes lib :param deserializer: object that can serialize the output of a controller into a webob response :param serializer: object that can deserialize a webob request into necessary pieces :param fault_body_function: a function that will build the response body for HTTP errors raised by operations on this resource object """ self.controller = controller self.deserializer = deserializer or RequestDeserializer() self.serializer = serializer or ResponseSerializer() self._fault_body_function = fault_body_function @webob.dec.wsgify(RequestClass=Request) def __call__(self, request): """WSGI method that controls (de)serialization and method dispatch.""" LOG.info("%(method)s %(url)s", {"method": request.method, "url": request.url}) try: action, args, accept = self.deserializer.deserialize(request) except exception.InvalidContentType: msg = _("Unsupported Content-Type") LOG.exception("InvalidContentType: %s", msg) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) except n_exc.MalformedRequestBody: msg = _("Malformed request body") LOG.exception("MalformedRequestBody: %s", msg) return Fault(webob.exc.HTTPBadRequest(explanation=msg)) try: action_result = self.dispatch(request, action, args) except webob.exc.HTTPException as ex: LOG.info("HTTP exception thrown: %s", ex) action_result = Fault(ex, self._fault_body_function) except Exception: LOG.exception("Internal error") # Do not include the traceback to avoid returning it to clients. action_result = Fault(webob.exc.HTTPServerError(), self._fault_body_function) if isinstance(action_result, dict) or action_result is None: response = self.serializer.serialize(action_result, accept, action=action) else: response = action_result try: LOG.info("%(url)s returned with HTTP %(status)d", dict(url=request.url, status=response.status_int)) except AttributeError as e: LOG.info("%(url)s returned a fault: %(exception)s", dict(url=request.url, exception=e)) return response def dispatch(self, request, action, action_args): """Find action-specific method on controller and call it.""" controller_method = getattr(self.controller, action) try: #NOTE(salvatore-orlando): the controller method must have # an argument whose name is 'request' return controller_method(request=request, **action_args) except TypeError: LOG.exception('Invalid request') return Fault(webob.exc.HTTPBadRequest()) def _default_body_function(wrapped_exc): code = wrapped_exc.status_int fault_data = { 'Error': { 'code': code, 'message': wrapped_exc.explanation}} # 'code' is an attribute on the fault tag itself metadata = {'attributes': {'Error': 'code'}} return fault_data, metadata class Fault(webob.exc.HTTPException): """Generates an HTTP response from a webob HTTP exception.""" def __init__(self, exception, body_function=None): """Creates a Fault for the given webob.exc.exception.""" self.wrapped_exc = exception self.status_int = self.wrapped_exc.status_int self._body_function = body_function or _default_body_function @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): """Generate a WSGI response based on the exception passed to ctor.""" # Replace the body with fault details. fault_data, metadata = self._body_function(self.wrapped_exc) content_type = req.best_match_content_type() serializer = { 'application/json': JSONDictSerializer(), }[content_type] self.wrapped_exc.body = serializer.serialize(fault_data) self.wrapped_exc.content_type = content_type return self.wrapped_exc # NOTE(salvatore-orlando): this class will go once the # extension API framework is updated class Controller(object): """WSGI app that dispatched to methods. WSGI app that reads routing information supplied by RoutesMiddleware and calls the requested action method upon itself. All action methods must, in addition to their normal parameters, accept a 'req' argument which is the incoming wsgi.Request. They raise a webob.exc exception, or return a dict which will be serialized by requested content type. """ @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): """Call the method specified in req.environ by RoutesMiddleware.""" arg_dict = req.environ['wsgiorg.routing_args'][1] action = arg_dict['action'] method = getattr(self, action) del arg_dict['controller'] del arg_dict['action'] if 'format' in arg_dict: del arg_dict['format'] arg_dict['request'] = req result = method(**arg_dict) if isinstance(result, dict) or result is None: if result is None: status = 204 content_type = '' body = None else: status = 200 content_type = req.best_match_content_type() body = self._serialize(result, content_type) response = webob.Response(status=status, content_type=content_type, body=body) LOG.debug("%(url)s returned with HTTP %(status)d", dict(url=req.url, status=response.status_int)) return response else: return result def _serialize(self, data, content_type): """Serialize the given dict to the provided content_type. Uses self._serialization_metadata if it exists, which is a dict mapping MIME types to information needed to serialize to that type. """ _metadata = getattr(type(self), '_serialization_metadata', {}) serializer = Serializer(_metadata) try: return serializer.serialize(data, content_type) except exception.InvalidContentType: msg = _('The requested content type %s is invalid.') % content_type raise webob.exc.HTTPNotAcceptable(msg) def _deserialize(self, data, content_type): """Deserialize the request body to the specified content type. Uses self._serialization_metadata if it exists, which is a dict mapping MIME types to information needed to serialize to that type. """ _metadata = getattr(type(self), '_serialization_metadata', {}) serializer = Serializer(_metadata) return serializer.deserialize(data, content_type)['body'] # NOTE(salvatore-orlando): this class will go once the # extension API framework is updated class Serializer(object): """Serializes and deserializes dictionaries to certain MIME types.""" def __init__(self, metadata=None): """Create a serializer based on the given WSGI environment. 'metadata' is an optional dict mapping MIME types to information needed to serialize a dictionary to that type. """ self.metadata = metadata or {} def _get_serialize_handler(self, content_type): handlers = { 'application/json': JSONDictSerializer(), } try: return handlers[content_type] except Exception: raise exception.InvalidContentType(content_type=content_type) def serialize(self, data, content_type): """Serialize a dictionary into the specified content type.""" return self._get_serialize_handler(content_type).serialize(data) def deserialize(self, datastring, content_type): """Deserialize a string to a dictionary. The string must be in the format of a supported MIME type. """ try: return self.get_deserialize_handler(content_type).deserialize( datastring) except Exception: raise webob.exc.HTTPBadRequest(_("Could not deserialize data")) def get_deserialize_handler(self, content_type): handlers = { 'application/json': JSONDeserializer(), } try: return handlers[content_type] except Exception: raise exception.InvalidContentType(content_type=content_type) neutron-12.1.1/neutron/manager.py0000664000175000017500000002520713553660047017005 0ustar zuulzuul00000000000000# Copyright 2011 VMware, Inc # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from collections import defaultdict from neutron_lib.plugins import constants as lib_const from neutron_lib.plugins import directory from neutron_lib.utils import runtime from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_service import periodic_task from oslo_utils import excutils from osprofiler import profiler import six from neutron._i18n import _ from neutron.plugins.common import constants LOG = logging.getLogger(__name__) CORE_PLUGINS_NAMESPACE = 'neutron.core_plugins' class ManagerMeta(profiler.TracedMeta, type(periodic_task.PeriodicTasks)): pass @six.add_metaclass(ManagerMeta) class Manager(periodic_task.PeriodicTasks): __trace_args__ = {"name": "rpc"} # Set RPC API version to 1.0 by default. target = oslo_messaging.Target(version='1.0') def __init__(self, host=None): if not host: host = cfg.CONF.host self.host = host conf = getattr(self, "conf", cfg.CONF) super(Manager, self).__init__(conf) def periodic_tasks(self, context, raise_on_error=False): self.run_periodic_tasks(context, raise_on_error=raise_on_error) def init_host(self): """Handle initialization if this is a standalone service. Child classes should override this method. """ pass def after_start(self): """Handler post initialization stuff. Child classes can override this method. """ pass def validate_post_plugin_load(): """Checks if the configuration variables are valid. If the configuration is invalid then the method will return an error message. If all is OK then it will return None. """ if ('dhcp_agents_per_network' in cfg.CONF and cfg.CONF.dhcp_agents_per_network <= 0): msg = _("dhcp_agents_per_network must be >= 1. '%s' " "is invalid.") % cfg.CONF.dhcp_agents_per_network return msg def validate_pre_plugin_load(): """Checks if the configuration variables are valid. If the configuration is invalid then the method will return an error message. If all is OK then it will return None. """ if cfg.CONF.core_plugin is None: msg = _('Neutron core_plugin not configured!') return msg @six.add_metaclass(profiler.TracedMeta) class NeutronManager(object): """Neutron's Manager class. Neutron's Manager class is responsible for parsing a config file and instantiating the correct plugin that concretely implements neutron_plugin_base class. """ # TODO(armax): use of the singleton pattern for this class is vestigial, # and it is mainly relied on by the unit tests. It is safer to get rid # of it once the entire codebase (neutron + subprojects) has switched # entirely to using the plugins directory. _instance = None __trace_args__ = {"name": "rpc"} def __init__(self, options=None, config_file=None): # If no options have been provided, create an empty dict if not options: options = {} msg = validate_pre_plugin_load() if msg: LOG.critical(msg) raise Exception(msg) # NOTE(jkoelker) Testing for the subclass with the __subclasshook__ # breaks tach monitoring. It has been removed # intentionally to allow v2 plugins to be monitored # for performance metrics. plugin_provider = cfg.CONF.core_plugin LOG.info("Loading core plugin: %s", plugin_provider) # NOTE(armax): keep hold of the actual plugin object plugin = self._get_plugin_instance(CORE_PLUGINS_NAMESPACE, plugin_provider) directory.add_plugin(lib_const.CORE, plugin) msg = validate_post_plugin_load() if msg: LOG.critical(msg) raise Exception(msg) # load services from the core plugin first self._load_services_from_core_plugin(plugin) self._load_service_plugins() # Used by pecan WSGI self.resource_plugin_mappings = {} self.resource_controller_mappings = {} self.path_prefix_resource_mappings = defaultdict(list) @staticmethod def load_class_for_provider(namespace, plugin_provider): """Loads plugin using alias or class name :param namespace: namespace where alias is defined :param plugin_provider: plugin alias or class name :returns: plugin that is loaded :raises ImportError: if fails to load plugin """ try: return runtime.load_class_by_alias_or_classname(namespace, plugin_provider) except ImportError: with excutils.save_and_reraise_exception(): LOG.error("Plugin '%s' not found.", plugin_provider) def _get_plugin_instance(self, namespace, plugin_provider): plugin_class = self.load_class_for_provider(namespace, plugin_provider) return plugin_class() def _load_services_from_core_plugin(self, plugin): """Puts core plugin in service_plugins for supported services.""" LOG.debug("Loading services supported by the core plugin") # supported service types are derived from supported extensions for ext_alias in getattr(plugin, "supported_extension_aliases", []): if ext_alias in constants.EXT_TO_SERVICE_MAPPING: service_type = constants.EXT_TO_SERVICE_MAPPING[ext_alias] directory.add_plugin(service_type, plugin) LOG.info("Service %s is supported by the core plugin", service_type) def _get_default_service_plugins(self): """Get default service plugins to be loaded.""" core_plugin = directory.get_plugin() if core_plugin.has_native_datastore(): return constants.DEFAULT_SERVICE_PLUGINS.keys() else: return [] def _load_service_plugins(self): """Loads service plugins. Starts from the core plugin and checks if it supports advanced services then loads classes provided in configuration. """ plugin_providers = cfg.CONF.service_plugins plugin_providers.extend(self._get_default_service_plugins()) LOG.debug("Loading service plugins: %s", plugin_providers) for provider in plugin_providers: if provider == '': continue LOG.info("Loading Plugin: %s", provider) plugin_inst = self._get_plugin_instance('neutron.service_plugins', provider) # only one implementation of svc_type allowed # specifying more than one plugin # for the same type is a fatal exception # TODO(armax): simplify this by moving the conditional into the # directory itself. plugin_type = plugin_inst.get_plugin_type() if directory.get_plugin(plugin_type): raise ValueError(_("Multiple plugins for service " "%s were configured") % plugin_type) directory.add_plugin(plugin_type, plugin_inst) # search for possible agent notifiers declared in service plugin # (needed by agent management extension) plugin = directory.get_plugin() if (hasattr(plugin, 'agent_notifiers') and hasattr(plugin_inst, 'agent_notifiers')): plugin.agent_notifiers.update(plugin_inst.agent_notifiers) LOG.debug("Successfully loaded %(type)s plugin. " "Description: %(desc)s", {"type": plugin_type, "desc": plugin_inst.get_plugin_description()}) @classmethod @runtime.synchronized("manager") def _create_instance(cls): if not cls.has_instance(): cls._instance = cls() @classmethod def has_instance(cls): return cls._instance is not None @classmethod def clear_instance(cls): cls._instance = None @classmethod def get_instance(cls): # double checked locking if not cls.has_instance(): cls._create_instance() return cls._instance @classmethod def set_plugin_for_resource(cls, resource, plugin): cls.get_instance().resource_plugin_mappings[resource] = plugin @classmethod def get_plugin_for_resource(cls, resource): return cls.get_instance().resource_plugin_mappings.get(resource) @classmethod def set_controller_for_resource(cls, resource, controller): cls.get_instance().resource_controller_mappings[resource] = controller @classmethod def get_controller_for_resource(cls, resource): resource = resource.replace('_', '-') res_ctrl_mappings = cls.get_instance().resource_controller_mappings # If no controller is found for resource, try replacing dashes with # underscores return res_ctrl_mappings.get( resource, res_ctrl_mappings.get(resource.replace('-', '_'))) # TODO(blogan): This isn't used by anything else other than tests and # probably should be removed @classmethod def get_service_plugin_by_path_prefix(cls, path_prefix): service_plugins = directory.get_unique_plugins() for service_plugin in service_plugins: plugin_path_prefix = getattr(service_plugin, 'path_prefix', None) if plugin_path_prefix and plugin_path_prefix == path_prefix: return service_plugin @classmethod def add_resource_for_path_prefix(cls, resource, path_prefix): resources = cls.get_instance().path_prefix_resource_mappings[ path_prefix].append(resource) return resources @classmethod def get_resources_for_path_prefix(cls, path_prefix): return cls.get_instance().path_prefix_resource_mappings[path_prefix] def init(): """Call to load the plugins (core+services) machinery.""" if not directory.is_loaded(): NeutronManager.get_instance() neutron-12.1.1/neutron/ipam/0000775000175000017500000000000013553660156015742 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/ipam/subnet_alloc.py0000664000175000017500000004511013553660047020766 0ustar zuulzuul00000000000000# Copyright (c) 2015 Hewlett-Packard Co. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import math import operator import netaddr from neutron_lib import constants from neutron_lib import exceptions as lib_exc from oslo_db import exception as db_exc from oslo_utils import uuidutils from neutron._i18n import _ from neutron.common import exceptions as n_exc from neutron.db import api as db_api from neutron.db import models_v2 from neutron.ipam import driver from neutron.ipam import exceptions as ipam_exc from neutron.ipam import requests as ipam_req from neutron.ipam import utils as ipam_utils class SubnetAllocator(driver.Pool): """Class for handling allocation of subnet prefixes from a subnet pool. This class leverages the pluggable IPAM interface where possible to make merging into IPAM framework easier in future cycles. """ def __init__(self, subnetpool, context): super(SubnetAllocator, self).__init__(subnetpool, context) self._sp_helper = SubnetPoolHelper() def _lock_subnetpool(self): """Lock subnetpool associated row. This method disallows to allocate concurrently 2 subnets in the same subnetpool, it's required to ensure non-overlapping cidrs in the same subnetpool. """ with db_api.context_manager.reader.using(self._context): current_hash = ( self._context.session.query(models_v2.SubnetPool.hash) .filter_by(id=self._subnetpool['id']).scalar()) if current_hash is None: # NOTE(cbrandily): subnetpool has been deleted raise n_exc.SubnetPoolNotFound( subnetpool_id=self._subnetpool['id']) new_hash = uuidutils.generate_uuid() # NOTE(cbrandily): the update disallows 2 concurrent subnet allocation # to succeed: at most 1 transaction will succeed, others will be # rolled back and be caught in neutron.db.v2.base with db_api.context_manager.writer.using(self._context): query = ( self._context.session.query(models_v2.SubnetPool).filter_by( id=self._subnetpool['id'], hash=current_hash)) count = query.update({'hash': new_hash}) if not count: raise db_exc.RetryRequest(lib_exc.SubnetPoolInUse( subnet_pool_id=self._subnetpool['id'])) def _get_allocated_cidrs(self): with db_api.context_manager.reader.using(self._context): query = self._context.session.query(models_v2.Subnet.cidr) subnets = query.filter_by(subnetpool_id=self._subnetpool['id']) return (x.cidr for x in subnets) def _get_available_prefix_list(self): prefixes = (x.cidr for x in self._subnetpool.prefixes) allocations = self._get_allocated_cidrs() prefix_set = netaddr.IPSet(iterable=prefixes) allocation_set = netaddr.IPSet(iterable=allocations) available_set = prefix_set.difference(allocation_set) available_set.compact() return sorted(available_set.iter_cidrs(), key=operator.attrgetter('prefixlen'), reverse=True) def _num_quota_units_in_prefixlen(self, prefixlen, quota_unit): return math.pow(2, quota_unit - prefixlen) def _allocations_used_by_tenant(self, quota_unit): subnetpool_id = self._subnetpool['id'] tenant_id = self._subnetpool['tenant_id'] with db_api.context_manager.reader.using(self._context): qry = self._context.session.query(models_v2.Subnet.cidr) allocations = qry.filter_by(subnetpool_id=subnetpool_id, tenant_id=tenant_id) value = 0 for allocation in allocations: prefixlen = netaddr.IPNetwork(allocation.cidr).prefixlen value += self._num_quota_units_in_prefixlen(prefixlen, quota_unit) return value def _check_subnetpool_tenant_quota(self, tenant_id, prefixlen): quota_unit = self._sp_helper.ip_version_subnetpool_quota_unit( self._subnetpool['ip_version']) quota = self._subnetpool.get('default_quota') if quota: used = self._allocations_used_by_tenant(quota_unit) requested_units = self._num_quota_units_in_prefixlen(prefixlen, quota_unit) if used + requested_units > quota: raise n_exc.SubnetPoolQuotaExceeded() def _allocate_any_subnet(self, request): with db_api.context_manager.writer.using(self._context): self._lock_subnetpool() self._check_subnetpool_tenant_quota(request.tenant_id, request.prefixlen) prefix_pool = self._get_available_prefix_list() for prefix in prefix_pool: if request.prefixlen >= prefix.prefixlen: subnet = next(prefix.subnet(request.prefixlen)) gateway_ip = request.gateway_ip if not gateway_ip: gateway_ip = subnet.network + 1 pools = ipam_utils.generate_pools(subnet.cidr, gateway_ip) return IpamSubnet(request.tenant_id, request.subnet_id, subnet.cidr, gateway_ip=gateway_ip, allocation_pools=pools) msg = _("Insufficient prefix space to allocate subnet size /%s") raise n_exc.SubnetAllocationError(reason=msg % str(request.prefixlen)) def _allocate_specific_subnet(self, request): with db_api.context_manager.writer.using(self._context): self._lock_subnetpool() self._check_subnetpool_tenant_quota(request.tenant_id, request.prefixlen) cidr = request.subnet_cidr available = self._get_available_prefix_list() matched = netaddr.all_matching_cidrs(cidr, available) if len(matched) is 1 and matched[0].prefixlen <= cidr.prefixlen: return IpamSubnet(request.tenant_id, request.subnet_id, cidr, gateway_ip=request.gateway_ip, allocation_pools=request.allocation_pools) msg = _("Cannot allocate requested subnet from the available " "set of prefixes") raise n_exc.SubnetAllocationError(reason=msg) def allocate_subnet(self, request): max_prefixlen = int(self._subnetpool['max_prefixlen']) min_prefixlen = int(self._subnetpool['min_prefixlen']) if request.prefixlen > max_prefixlen: raise n_exc.MaxPrefixSubnetAllocationError( prefixlen=request.prefixlen, max_prefixlen=max_prefixlen) if request.prefixlen < min_prefixlen: raise n_exc.MinPrefixSubnetAllocationError( prefixlen=request.prefixlen, min_prefixlen=min_prefixlen) if isinstance(request, ipam_req.AnySubnetRequest): return self._allocate_any_subnet(request) elif isinstance(request, ipam_req.SpecificSubnetRequest): return self._allocate_specific_subnet(request) else: msg = _("Unsupported request type") raise n_exc.SubnetAllocationError(reason=msg) def get_subnet(self, subnet_id): raise NotImplementedError() def update_subnet(self, request): raise NotImplementedError() def remove_subnet(self, subnet_id): raise NotImplementedError() def get_allocator(self, subnet_ids): return IpamSubnetGroup(self, subnet_ids) class IpamSubnet(driver.Subnet): def __init__(self, tenant_id, subnet_id, cidr, gateway_ip=None, allocation_pools=None): self._req = ipam_req.SpecificSubnetRequest( tenant_id, subnet_id, cidr, gateway_ip=gateway_ip, allocation_pools=allocation_pools) def allocate(self, address_request): raise NotImplementedError() def deallocate(self, address): raise NotImplementedError() def get_details(self): return self._req class IpamSubnetGroup(driver.SubnetGroup): def __init__(self, driver, subnet_ids): self._driver = driver self._subnet_ids = subnet_ids def allocate(self, address_request): '''Originally, the Neutron pluggable IPAM backend would ask the driver to try to allocate an IP from each subnet in turn, one by one. This implementation preserves that behavior so that existing drivers work as they did before while giving them the opportunity to optimize it by overridding the implementation. ''' for subnet_id in self._subnet_ids: try: ipam_subnet = self._driver.get_subnet(subnet_id) return ipam_subnet.allocate(address_request), subnet_id except ipam_exc.IpAddressGenerationFailure: continue raise ipam_exc.IpAddressGenerationFailureAllSubnets() class SubnetPoolReader(object): '''Class to assist with reading a subnetpool, loading defaults, and inferring IP version from prefix list. Provides a common way of reading a stored model or a create request with default table attributes. ''' MIN_PREFIX_TYPE = 'min' MAX_PREFIX_TYPE = 'max' DEFAULT_PREFIX_TYPE = 'default' _sp_helper = None def __init__(self, subnetpool): self._read_prefix_info(subnetpool) self._sp_helper = SubnetPoolHelper() self._read_id(subnetpool) self._read_prefix_bounds(subnetpool) self._read_attrs(subnetpool, ['tenant_id', 'name', 'is_default', 'shared']) self.description = subnetpool.get('description') self._read_address_scope(subnetpool) self.subnetpool = {'id': self.id, 'name': self.name, 'project_id': self.tenant_id, 'prefixes': self.prefixes, 'min_prefix': self.min_prefix, 'min_prefixlen': self.min_prefixlen, 'max_prefix': self.max_prefix, 'max_prefixlen': self.max_prefixlen, 'default_prefix': self.default_prefix, 'default_prefixlen': self.default_prefixlen, 'default_quota': self.default_quota, 'address_scope_id': self.address_scope_id, 'is_default': self.is_default, 'shared': self.shared, 'description': self.description} def _read_attrs(self, subnetpool, keys): for key in keys: setattr(self, key, subnetpool[key]) def _ip_version_from_cidr(self, cidr): return netaddr.IPNetwork(cidr).version def _prefixlen_from_cidr(self, cidr): return netaddr.IPNetwork(cidr).prefixlen def _read_id(self, subnetpool): id = subnetpool.get('id', constants.ATTR_NOT_SPECIFIED) if id is constants.ATTR_NOT_SPECIFIED: id = uuidutils.generate_uuid() self.id = id def _read_prefix_bounds(self, subnetpool): ip_version = self.ip_version default_min = self._sp_helper.default_min_prefixlen(ip_version) default_max = self._sp_helper.default_max_prefixlen(ip_version) self._read_prefix_bound(self.MIN_PREFIX_TYPE, subnetpool, default_min) self._read_prefix_bound(self.MAX_PREFIX_TYPE, subnetpool, default_max) self._read_prefix_bound(self.DEFAULT_PREFIX_TYPE, subnetpool, self.min_prefixlen) self._sp_helper.validate_min_prefixlen(self.min_prefixlen, self.max_prefixlen) self._sp_helper.validate_max_prefixlen(self.max_prefixlen, ip_version) self._sp_helper.validate_default_prefixlen(self.min_prefixlen, self.max_prefixlen, self.default_prefixlen) def _read_prefix_bound(self, type, subnetpool, default_bound=None): prefixlen_attr = type + '_prefixlen' prefix_attr = type + '_prefix' prefixlen = subnetpool.get(prefixlen_attr, constants.ATTR_NOT_SPECIFIED) wildcard = self._sp_helper.wildcard(self.ip_version) if prefixlen is constants.ATTR_NOT_SPECIFIED and default_bound: prefixlen = default_bound if prefixlen is not constants.ATTR_NOT_SPECIFIED: prefix_cidr = '/'.join((wildcard, str(prefixlen))) setattr(self, prefix_attr, prefix_cidr) setattr(self, prefixlen_attr, prefixlen) def _read_prefix_info(self, subnetpool): prefix_list = subnetpool['prefixes'] if not prefix_list: raise n_exc.EmptySubnetPoolPrefixList() ip_version = None for prefix in prefix_list: if not ip_version: ip_version = netaddr.IPNetwork(prefix).version elif netaddr.IPNetwork(prefix).version != ip_version: raise n_exc.PrefixVersionMismatch() self.default_quota = subnetpool.get('default_quota') if self.default_quota is constants.ATTR_NOT_SPECIFIED: self.default_quota = None self.ip_version = ip_version self.prefixes = self._compact_subnetpool_prefix_list(prefix_list) def _read_address_scope(self, subnetpool): address_scope_id = subnetpool.get('address_scope_id', constants.ATTR_NOT_SPECIFIED) if address_scope_id is constants.ATTR_NOT_SPECIFIED: address_scope_id = None self.address_scope_id = address_scope_id def _compact_subnetpool_prefix_list(self, prefix_list): """Compact any overlapping prefixes in prefix_list and return the result """ ip_set = netaddr.IPSet() for prefix in prefix_list: ip_set.add(netaddr.IPNetwork(prefix)) ip_set.compact() return [x.cidr for x in ip_set.iter_cidrs()] class SubnetPoolHelper(object): _PREFIX_VERSION_INFO = {4: {'max_prefixlen': constants.IPv4_BITS, 'wildcard': '0.0.0.0', 'default_min_prefixlen': 8, # IPv4 quota measured in units of /32 'quota_units': 32}, 6: {'max_prefixlen': constants.IPv6_BITS, 'wildcard': '::', 'default_min_prefixlen': 64, # IPv6 quota measured in units of /64 'quota_units': 64}} def validate_min_prefixlen(self, min_prefixlen, max_prefixlen): if min_prefixlen < 0: raise n_exc.UnsupportedMinSubnetPoolPrefix(prefix=min_prefixlen, version=4) if min_prefixlen > max_prefixlen: raise n_exc.IllegalSubnetPoolPrefixBounds( prefix_type='min_prefixlen', prefixlen=min_prefixlen, base_prefix_type='max_prefixlen', base_prefixlen=max_prefixlen) def validate_max_prefixlen(self, prefixlen, ip_version): max = self._PREFIX_VERSION_INFO[ip_version]['max_prefixlen'] if prefixlen > max: raise n_exc.IllegalSubnetPoolPrefixBounds( prefix_type='max_prefixlen', prefixlen=prefixlen, base_prefix_type='ip_version_max', base_prefixlen=max) def validate_default_prefixlen(self, min_prefixlen, max_prefixlen, default_prefixlen): if default_prefixlen < min_prefixlen: raise n_exc.IllegalSubnetPoolPrefixBounds( prefix_type='default_prefixlen', prefixlen=default_prefixlen, base_prefix_type='min_prefixlen', base_prefixlen=min_prefixlen) if default_prefixlen > max_prefixlen: raise n_exc.IllegalSubnetPoolPrefixBounds( prefix_type='default_prefixlen', prefixlen=default_prefixlen, base_prefix_type='max_prefixlen', base_prefixlen=max_prefixlen) def wildcard(self, ip_version): return self._PREFIX_VERSION_INFO[ip_version]['wildcard'] def default_max_prefixlen(self, ip_version): return self._PREFIX_VERSION_INFO[ip_version]['max_prefixlen'] def default_min_prefixlen(self, ip_version): return self._PREFIX_VERSION_INFO[ip_version]['default_min_prefixlen'] def ip_version_subnetpool_quota_unit(self, ip_version): return self._PREFIX_VERSION_INFO[ip_version]['quota_units'] neutron-12.1.1/neutron/ipam/exceptions.py0000664000175000017500000000565613553660047020510 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions from neutron._i18n import _ class InvalidSubnetRequestType(exceptions.BadRequest): message = _("Cannot handle subnet of type %(subnet_type)s") class AddressCalculationFailure(exceptions.NeutronException): message = _("Unable to calculate %(address_type)s address because of:" "%(reason)s") class InvalidAddressType(exceptions.NeutronException): message = _("Unknown address type %(address_type)s") class IpAddressAllocationNotFound(exceptions.NeutronException): message = _("Unable to find IP address %(ip_address)s on subnet " "%(subnet_id)s") class IpAddressAlreadyAllocated(exceptions.Conflict): message = _("IP address %(ip)s already allocated in subnet %(subnet_id)s") class InvalidIpForSubnet(exceptions.BadRequest): message = _("IP address %(ip)s does not belong to subnet %(subnet_id)s") class InvalidAddressRequest(exceptions.BadRequest): message = _("The address allocation request could not be satisfied " "because: %(reason)s") class InvalidSubnetRequest(exceptions.BadRequest): message = _("The subnet request could not be satisfied because: " "%(reason)s") class AllocationOnAutoAddressSubnet(exceptions.InvalidInput): message = _("IPv6 address %(ip)s cannot be directly " "assigned to a port on subnet %(subnet_id)s as the " "subnet is configured for automatic addresses") class IpAddressGenerationFailure(exceptions.Conflict): message = _("No more IP addresses available for subnet %(subnet_id)s.") class IpAddressGenerationFailureAllSubnets(IpAddressGenerationFailure): message = _("No more IP addresses available.") class IpAddressGenerationFailureNoMatchingSubnet(IpAddressGenerationFailure): message = _("No valid service subnet for the given device owner, " "network %(network_id)s, service type %(service_type)s.") class IPAllocationFailed(exceptions.NeutronException): message = _("IP allocation failed. Try again later.") class IpamValueInvalid(exceptions.Conflict): def __init__(self, message=None): self.message = message super(IpamValueInvalid, self).__init__() class DeferIpam(exceptions.NeutronException): message = _("Exception used to signal that IP allocation is deferred") neutron-12.1.1/neutron/ipam/requests.py0000664000175000017500000002641613553660047020177 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import netaddr from neutron_lib.api import validators from neutron_lib import constants from oslo_utils import netutils from oslo_utils import uuidutils import six from neutron._i18n import _ from neutron.common import utils as common_utils from neutron.ipam import exceptions as ipam_exc @six.add_metaclass(abc.ABCMeta) class SubnetPool(object): """Represents a pool of IPs available inside an address scope.""" @six.add_metaclass(abc.ABCMeta) class SubnetRequest(object): """Carries the data needed to make a subnet request The data validated and carried by an instance of this class is the data that is common to any type of request. This class shouldn't be instantiated on its own. Rather, a subclass of this class should be used. """ def __init__(self, tenant_id, subnet_id, gateway_ip=None, allocation_pools=None): """Initialize and validate :param tenant_id: The tenant id who will own the subnet :type tenant_id: str uuid :param subnet_id: Neutron's subnet ID :type subnet_id: str uuid :param gateway_ip: An IP to reserve for the subnet gateway. :type gateway_ip: None or convertible to netaddr.IPAddress :param allocation_pools: The pool from which IPAM should allocate addresses. The allocator *may* allow allocating addresses outside of this range if specifically requested. :type allocation_pools: A list of netaddr.IPRange. None if not specified. """ self._tenant_id = tenant_id self._subnet_id = subnet_id self._gateway_ip = None self._allocation_pools = None if gateway_ip is not None: self._gateway_ip = netaddr.IPAddress(gateway_ip) if allocation_pools is not None: allocation_pools = sorted(allocation_pools) previous = None for pool in allocation_pools: if not isinstance(pool, netaddr.ip.IPRange): raise TypeError(_("Ranges must be netaddr.IPRange")) if previous and pool.first <= previous.last: raise ValueError(_("Ranges must not overlap")) previous = pool if 1 < len(allocation_pools): # Checks that all the ranges are in the same IP version. # IPRange sorts first by ip version so we can get by with just # checking the first and the last range having sorted them # above. first_version = allocation_pools[0].version last_version = allocation_pools[-1].version if first_version != last_version: raise ValueError(_("Ranges must be in the same IP " "version")) self._allocation_pools = allocation_pools if self.gateway_ip and self.allocation_pools: if self.gateway_ip.version != self.allocation_pools[0].version: raise ValueError(_("Gateway IP version inconsistent with " "allocation pool version")) @property def tenant_id(self): return self._tenant_id @property def subnet_id(self): return self._subnet_id @property def gateway_ip(self): return self._gateway_ip @property def allocation_pools(self): return self._allocation_pools def _validate_with_subnet(self, subnet_cidr): if self.allocation_pools: if subnet_cidr.version != self.allocation_pools[0].version: raise ipam_exc.IpamValueInvalid(_( "allocation_pools use the wrong ip version")) for pool in self.allocation_pools: if pool not in subnet_cidr: raise ipam_exc.IpamValueInvalid(_( "allocation_pools are not in the subnet")) class AnySubnetRequest(SubnetRequest): """A template for allocating an unspecified subnet from IPAM Support for this type of request in a driver is optional. For example, the initial reference implementation will not support this. The API has no way of creating a subnet without a specific address until subnet-allocation is implemented. """ WILDCARDS = {constants.IPv4: '0.0.0.0', constants.IPv6: '::'} def __init__(self, tenant_id, subnet_id, version, prefixlen, gateway_ip=None, allocation_pools=None): """ :param version: Either constants.IPv4 or constants.IPv6 :param prefixlen: The prefix len requested. Must be within the min and max allowed. :type prefixlen: int """ super(AnySubnetRequest, self).__init__( tenant_id=tenant_id, subnet_id=subnet_id, gateway_ip=gateway_ip, allocation_pools=allocation_pools) net = netaddr.IPNetwork(self.WILDCARDS[version] + '/' + str(prefixlen)) self._validate_with_subnet(net) self._prefixlen = prefixlen @property def prefixlen(self): return self._prefixlen class SpecificSubnetRequest(SubnetRequest): """A template for allocating a specified subnet from IPAM The initial reference implementation will probably just allow any allocation, even overlapping ones. This can be expanded on by future blueprints. """ def __init__(self, tenant_id, subnet_id, subnet_cidr, gateway_ip=None, allocation_pools=None): """ :param subnet: The subnet requested. Can be IPv4 or IPv6. However, when IPAM tries to fulfill this request, the IP version must match the version of the address scope being used. :type subnet: netaddr.IPNetwork or convertible to one """ super(SpecificSubnetRequest, self).__init__( tenant_id=tenant_id, subnet_id=subnet_id, gateway_ip=gateway_ip, allocation_pools=allocation_pools) self._subnet_cidr = netaddr.IPNetwork(subnet_cidr) self._validate_with_subnet(self._subnet_cidr) @property def subnet_cidr(self): return self._subnet_cidr @property def prefixlen(self): return self._subnet_cidr.prefixlen @six.add_metaclass(abc.ABCMeta) class AddressRequest(object): """Abstract base class for address requests""" class SpecificAddressRequest(AddressRequest): """For requesting a specified address from IPAM""" def __init__(self, address): """ :param address: The address being requested :type address: A netaddr.IPAddress or convertible to one. """ super(SpecificAddressRequest, self).__init__() self._address = netaddr.IPAddress(address) @property def address(self): return self._address class AnyAddressRequest(AddressRequest): """Used to request any available address from the pool.""" class PreferNextAddressRequest(AnyAddressRequest): """Used to request next available IP address from the pool.""" class AutomaticAddressRequest(SpecificAddressRequest): """Used to create auto generated addresses, such as EUI64""" EUI64 = 'eui64' def _generate_eui64_address(self, **kwargs): if set(kwargs) != set(['prefix', 'mac']): raise ipam_exc.AddressCalculationFailure( address_type='eui-64', reason=_('must provide exactly 2 arguments - cidr and MAC')) prefix = kwargs['prefix'] mac_address = kwargs['mac'] return netutils.get_ipv6_addr_by_EUI64(prefix, mac_address) _address_generators = {EUI64: _generate_eui64_address} def __init__(self, address_type=EUI64, **kwargs): """ This constructor builds an automatic IP address. Parameter needed for generating it can be passed as optional keyword arguments. :param address_type: the type of address to generate. It could be an eui-64 address, a random IPv6 address, or an ipv4 link-local address. For the Kilo release only eui-64 addresses will be supported. """ address_generator = self._address_generators.get(address_type) if not address_generator: raise ipam_exc.InvalidAddressType(address_type=address_type) address = address_generator(self, **kwargs) super(AutomaticAddressRequest, self).__init__(address) class RouterGatewayAddressRequest(AddressRequest): """Used to request allocating the special router gateway address.""" class AddressRequestFactory(object): """Builds request using ip info Additional parameters(port and context) are not used in default implementation, but planned to be used in sub-classes provided by specific ipam driver, """ @classmethod def get_request(cls, context, port, ip_dict): """ :param context: context (not used here, but can be used in sub-classes) :param port: port dict (not used here, but can be used in sub-classes) :param ip_dict: dict that can contain 'ip_address', 'mac' and 'subnet_cidr' keys. Request to generate is selected depending on this ip_dict keys. :return: returns prepared AddressRequest (specific or any) """ if ip_dict.get('ip_address'): return SpecificAddressRequest(ip_dict['ip_address']) elif ip_dict.get('eui64_address'): return AutomaticAddressRequest(prefix=ip_dict['subnet_cidr'], mac=ip_dict['mac']) elif port['device_owner'] == constants.DEVICE_OWNER_DHCP: # preserve previous behavior of DHCP ports choosing start of pool return PreferNextAddressRequest() else: return AnyAddressRequest() class SubnetRequestFactory(object): """Builds request using subnet info""" @classmethod def get_request(cls, context, subnet, subnetpool): cidr = subnet.get('cidr') subnet_id = subnet.get('id', uuidutils.generate_uuid()) is_any_subnetpool_request = not validators.is_attr_set(cidr) if is_any_subnetpool_request: prefixlen = subnet['prefixlen'] if not validators.is_attr_set(prefixlen): prefixlen = int(subnetpool['default_prefixlen']) return AnySubnetRequest( subnet['tenant_id'], subnet_id, common_utils.ip_version_from_int(subnetpool['ip_version']), prefixlen) else: return SpecificSubnetRequest(subnet['tenant_id'], subnet_id, cidr, subnet.get('gateway_ip'), subnet.get('allocation_pools')) neutron-12.1.1/neutron/ipam/utils.py0000664000175000017500000000577113553660047017465 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron_lib import constants from neutron.common import constants as n_const def check_subnet_ip(cidr, ip_address, port_owner=None): """Validate that the IP address is on the subnet.""" ip = netaddr.IPAddress(ip_address) net = netaddr.IPNetwork(cidr) # Check that the IP is valid on subnet. In IPv4 this cannot be the # network or the broadcast address if net.version == constants.IP_VERSION_6: # NOTE(njohnston): In some cases the code cannot know the owner of the # port. In these cases port_owner should be None, and we pass it # through here. return ((port_owner in n_const.ROUTER_PORT_OWNERS or port_owner is None or ip != net.network) and net.netmask & ip == net.network) else: return (ip != net.network and ip != net[-1] and net.netmask & ip == net.network) def check_gateway_invalid_in_subnet(cidr, gateway): """Check whether the gw IP address is invalid on the subnet.""" ip = netaddr.IPAddress(gateway) net = netaddr.IPNetwork(cidr) # Check whether the gw IP is in-valid on subnet. # If gateway is in the subnet, it cannot be the # 'network' or the 'broadcast address (only in IPv4)'. # If gateway is out of subnet, there is no way to # check since we don't have gateway's subnet cidr. return (ip in net and (net.version == constants.IP_VERSION_4 and ip in (net.network, net[-1]))) def generate_pools(cidr, gateway_ip): """Create IP allocation pools for a specified subnet The Neutron API defines a subnet's allocation pools as a list of IPRange objects for defining the pool range. """ # Auto allocate the pool around gateway_ip net = netaddr.IPNetwork(cidr) ip_version = net.version first = netaddr.IPAddress(net.first, ip_version) last = netaddr.IPAddress(net.last, ip_version) if first == last: # handle single address subnet case return [netaddr.IPRange(first, last)] first_ip = first + 1 # last address is broadcast in v4 last_ip = last - (ip_version == 4) if first_ip >= last_ip: # /31 lands here return [] ipset = netaddr.IPSet(netaddr.IPRange(first_ip, last_ip)) if gateway_ip: ipset.remove(netaddr.IPAddress(gateway_ip, ip_version)) return list(ipset.iter_ipranges()) neutron-12.1.1/neutron/ipam/drivers/0000775000175000017500000000000013553660156017420 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/ipam/drivers/neutrondb_ipam/0000775000175000017500000000000013553660156022426 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/ipam/drivers/neutrondb_ipam/__init__.py0000664000175000017500000000000013553660046024523 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/ipam/drivers/neutrondb_ipam/db_api.py0000664000175000017500000001232713553660047024222 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_utils import uuidutils from neutron.common import constants as const from neutron.objects import ipam as ipam_objs # Database operations for Neutron's DB-backed IPAM driver class IpamSubnetManager(object): @classmethod def load_by_neutron_subnet_id(cls, context, neutron_subnet_id): objs = ipam_objs.IpamSubnet.get_objects( context, neutron_subnet_id=neutron_subnet_id) return objs.pop() if objs else None def __init__(self, ipam_subnet_id, neutron_subnet_id): self._ipam_subnet_id = ipam_subnet_id self._neutron_subnet_id = neutron_subnet_id @property def neutron_id(self): return self._neutron_subnet_id def create(self, context): """Create database models for an IPAM subnet. This method creates a subnet resource for the IPAM driver and associates it with its neutron identifier, if specified. :param context: neutron api request context :returns: the idenfier of created IPAM subnet """ if not self._ipam_subnet_id: self._ipam_subnet_id = uuidutils.generate_uuid() ipam_objs.IpamSubnet( context, id=self._ipam_subnet_id, neutron_subnet_id=self._neutron_subnet_id).create() return self._ipam_subnet_id @classmethod def delete(cls, context, neutron_subnet_id): """Delete IPAM subnet. IPAM subnet no longer has foreign key to neutron subnet, so need to perform delete manually :param context: neutron api request context :param neutron_subnet_id: neutron subnet id associated with ipam subnet """ return ipam_objs.IpamSubnet.delete_objects(context, neutron_subnet_id=neutron_subnet_id) def create_pool(self, context, pool_start, pool_end): """Create an allocation pool for the subnet. This method does not perform any validation on parameters; it simply persist data on the database. :param pool_start: string expressing the start of the pool :param pool_end: string expressing the end of the pool :return: the newly created pool object. """ ip_pool_obj = ipam_objs.IpamAllocationPool( context, ipam_subnet_id=self._ipam_subnet_id, first_ip=pool_start, last_ip=pool_end) ip_pool_obj.create() return ip_pool_obj def delete_allocation_pools(self, context): """Remove all allocation pools for the current subnet. :param context: neutron api request context """ ipam_objs.IpamAllocationPool.delete_objects( context, ipam_subnet_id=self._ipam_subnet_id) def list_pools(self, context): """Return pools for the current subnet.""" return ipam_objs.IpamAllocationPool.get_objects( context, ipam_subnet_id=self._ipam_subnet_id) def check_unique_allocation(self, context, ip_address): """Validate that the IP address on the subnet is not in use.""" return not ipam_objs.IpamAllocation.objects_exist( context, ipam_subnet_id=self._ipam_subnet_id, status=const.IPAM_ALLOCATION_STATUS_ALLOCATED, ip_address=ip_address) def list_allocations(self, context, status=const.IPAM_ALLOCATION_STATUS_ALLOCATED): """Return current allocations for the subnet. :param context: neutron api request context :param status: IP allocation status :returns: a list of IpamAllocation OVO objects """ return ipam_objs.IpamAllocation.get_objects( context, ipam_subnet_id=self._ipam_subnet_id, status=status) def create_allocation(self, context, ip_address, status=const.IPAM_ALLOCATION_STATUS_ALLOCATED): """Create an IP allocation entry. :param context: neutron api request context :param ip_address: the IP address to allocate :param status: IP allocation status """ ipam_objs.IpamAllocation( context, ip_address=ip_address, status=status, ipam_subnet_id=self._ipam_subnet_id).create() def delete_allocation(self, context, ip_address): """Remove an IP allocation for this subnet. :param context: neutron api request context :param ip_address: IP address for which the allocation entry should be removed. :returns: number of deleted allocation entries. """ return ipam_objs.IpamAllocation.delete_objects( context, ipam_subnet_id=self._ipam_subnet_id, ip_address=ip_address) neutron-12.1.1/neutron/ipam/drivers/neutrondb_ipam/db_models.py0000664000175000017500000000601713553660046024732 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm as sa_orm # Database models used by the neutron DB IPAM driver # NOTE(salv-orlando): The following data model creates redundancy with # models_v2.IPAllocationPool. This level of data redundancy could be tolerated # considering that the following model is specific to the IPAM driver logic. # It therefore represents an internal representation of a subnet allocation # pool and can therefore change in the future, where as # models_v2.IPAllocationPool is the representation of IP allocation pools in # the management layer and therefore its evolution is subject to APIs backward # compatibility policies class IpamAllocationPool(model_base.BASEV2, model_base.HasId): """Representation of an allocation pool in a Neutron subnet.""" ipam_subnet_id = sa.Column(sa.String(36), sa.ForeignKey('ipamsubnets.id', ondelete="CASCADE"), nullable=False) first_ip = sa.Column(sa.String(64), nullable=False) last_ip = sa.Column(sa.String(64), nullable=False) def __repr__(self): return "%s - %s" % (self.first_ip, self.last_ip) class IpamSubnet(model_base.BASEV2, model_base.HasId): """Association between IPAM entities and neutron subnets. For subnet data persistency - such as cidr and gateway IP, the IPAM driver relies on Neutron's subnet model as source of truth to limit data redundancy. """ neutron_subnet_id = sa.Column(sa.String(36), nullable=True) allocation_pools = sa_orm.relationship(IpamAllocationPool, backref='subnet', lazy="joined", cascade='delete') class IpamAllocation(model_base.BASEV2): """Model class for IP Allocation requests. """ ip_address = sa.Column(sa.String(64), nullable=False, primary_key=True) status = sa.Column(sa.String(36)) # The subnet identifier is redundant but come handy for looking up # IP addresses to remove. ipam_subnet_id = sa.Column(sa.String(36), sa.ForeignKey('ipamsubnets.id', ondelete="CASCADE"), primary_key=True, nullable=False) neutron-12.1.1/neutron/ipam/drivers/neutrondb_ipam/driver.py0000664000175000017500000003316613553660047024303 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools import random import netaddr from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from oslo_db import exception as db_exc from oslo_log import log from oslo_utils import uuidutils from neutron._i18n import _ from neutron.ipam import driver as ipam_base from neutron.ipam.drivers.neutrondb_ipam import db_api as ipam_db_api from neutron.ipam import exceptions as ipam_exc from neutron.ipam import requests as ipam_req from neutron.ipam import subnet_alloc from neutron.ipam import utils as ipam_utils LOG = log.getLogger(__name__) class NeutronDbSubnet(ipam_base.Subnet): """Manage IP addresses for Neutron DB IPAM driver. This class implements the strategy for IP address allocation and deallocation for the Neutron DB IPAM driver. """ @classmethod def create_allocation_pools(cls, subnet_manager, context, pools, cidr): for pool in pools: # IPv6 addresses that start '::1', '::2', etc cause IP version # ambiguity when converted to integers by pool.first and pool.last. # Infer the IP version from the subnet cidr. ip_version = cidr.version subnet_manager.create_pool( context, netaddr.IPAddress(pool.first, ip_version).format(), netaddr.IPAddress(pool.last, ip_version).format()) @classmethod def create_from_subnet_request(cls, subnet_request, ctx): ipam_subnet_id = uuidutils.generate_uuid() subnet_manager = ipam_db_api.IpamSubnetManager( ipam_subnet_id, subnet_request.subnet_id) # Create subnet resource subnet_manager.create(ctx) # If allocation pools are not specified, define them around # the subnet's gateway IP if not subnet_request.allocation_pools: pools = ipam_utils.generate_pools(subnet_request.subnet_cidr, subnet_request.gateway_ip) else: pools = subnet_request.allocation_pools # Create IPAM allocation pools cls.create_allocation_pools(subnet_manager, ctx, pools, subnet_request.subnet_cidr) return cls(ipam_subnet_id, ctx, cidr=subnet_request.subnet_cidr, allocation_pools=pools, gateway_ip=subnet_request.gateway_ip, tenant_id=subnet_request.tenant_id, subnet_id=subnet_request.subnet_id) @classmethod def load(cls, neutron_subnet_id, ctx): """Load an IPAM subnet from the database given its neutron ID. :param neutron_subnet_id: neutron subnet identifier. """ ipam_subnet = ipam_db_api.IpamSubnetManager.load_by_neutron_subnet_id( ctx, neutron_subnet_id) if not ipam_subnet: LOG.error("IPAM subnet referenced to " "Neutron subnet %s does not exist", neutron_subnet_id) raise n_exc.SubnetNotFound(subnet_id=neutron_subnet_id) pools = [] for pool in ipam_subnet.allocation_pools: pools.append(netaddr.IPRange(pool['first_ip'], pool['last_ip'])) neutron_subnet_obj = cls._fetch_subnet(ctx, neutron_subnet_id) return cls(ipam_subnet['id'], ctx, cidr=neutron_subnet_obj.cidr, allocation_pools=pools, gateway_ip=neutron_subnet_obj.gateway_ip, tenant_id=neutron_subnet_obj.tenant_id, subnet_id=neutron_subnet_id) @classmethod def _fetch_subnet(cls, context, id): plugin = directory.get_plugin() return plugin._get_subnet_object(context, id) def __init__(self, internal_id, ctx, cidr=None, allocation_pools=None, gateway_ip=None, tenant_id=None, subnet_id=None): # NOTE: In theory it could have been possible to grant the IPAM # driver direct access to the database. While this is possible, # it would have led to duplicate code and/or non-trivial # refactorings in neutron.db.db_base_plugin_v2. # This is because in the Neutron V2 plugin logic DB management is # encapsulated within the plugin. self._cidr = cidr self._pools = allocation_pools self._gateway_ip = gateway_ip self._tenant_id = tenant_id self._subnet_id = subnet_id self.subnet_manager = ipam_db_api.IpamSubnetManager(internal_id, self._subnet_id) self._context = ctx def _verify_ip(self, context, ip_address): """Verify whether IP address can be allocated on subnet. :param context: neutron api request context :param ip_address: String representing the IP address to verify :raises: InvalidInput, IpAddressAlreadyAllocated """ # Ensure that the IP's are unique if not self.subnet_manager.check_unique_allocation(context, ip_address): raise ipam_exc.IpAddressAlreadyAllocated( subnet_id=self.subnet_manager.neutron_id, ip=ip_address) # Ensure that the IP is valid on the subnet if not ipam_utils.check_subnet_ip(self._cidr, ip_address): raise ipam_exc.InvalidIpForSubnet( subnet_id=self.subnet_manager.neutron_id, ip=ip_address) def _generate_ip(self, context, prefer_next=False): """Generate an IP address from the set of available addresses.""" ip_allocations = netaddr.IPSet() for ipallocation in self.subnet_manager.list_allocations(context): ip_allocations.add(ipallocation.ip_address) for ip_pool in self.subnet_manager.list_pools(context): ip_set = netaddr.IPSet() ip_set.add(netaddr.IPRange(ip_pool.first_ip, ip_pool.last_ip)) av_set = ip_set.difference(ip_allocations) if av_set.size == 0: continue if prefer_next: window = 1 else: # Compute a value for the selection window window = min(av_set.size, 30) ip_index = random.randint(1, window) candidate_ips = list(itertools.islice(av_set, ip_index)) allocated_ip = candidate_ips[ random.randint(0, len(candidate_ips) - 1)] return str(allocated_ip), ip_pool.id raise ipam_exc.IpAddressGenerationFailure( subnet_id=self.subnet_manager.neutron_id) def allocate(self, address_request): # NOTE(pbondar): Ipam driver is always called in context of already # running transaction, which is started on create_port or upper level. # To be able to do rollback/retry actions correctly ipam driver # should not create new nested transaction blocks. all_pool_id = None # NOTE(salv-orlando): It would probably better to have a simpler # model for address requests and just check whether there is a # specific IP address specified in address_request if isinstance(address_request, ipam_req.SpecificAddressRequest): # This handles both specific and automatic address requests # Check availability of requested IP ip_address = str(address_request.address) self._verify_ip(self._context, ip_address) else: prefer_next = isinstance(address_request, ipam_req.PreferNextAddressRequest) ip_address, all_pool_id = self._generate_ip(self._context, prefer_next) # Create IP allocation request object # The only defined status at this stage is 'ALLOCATED'. # More states will be available in the future - e.g.: RECYCLABLE try: # TODO(ataraday): revisit this after objects switched to # new enginefacade with self._context.session.begin(subtransactions=True): # NOTE(kevinbenton): we use a subtransaction to force # a flush here so we can capture DBReferenceErrors due # to concurrent subnet deletions. (galera would deadlock # later on final commit) self.subnet_manager.create_allocation(self._context, ip_address) except db_exc.DBReferenceError: raise n_exc.SubnetNotFound( subnet_id=self.subnet_manager.neutron_id) return ip_address def deallocate(self, address): # This is almost a no-op because the Neutron DB IPAM driver does not # delete IPAllocation objects at every deallocation. The only # operation it performs is to delete an IPRequest entry. count = self.subnet_manager.delete_allocation( self._context, address) # count can hardly be greater than 1, but it can be 0... if not count: raise ipam_exc.IpAddressAllocationNotFound( subnet_id=self.subnet_manager.neutron_id, ip_address=address) def _no_pool_changes(self, context, pools): """Check if pool updates in db are required.""" db_pools = self.subnet_manager.list_pools(context) iprange_pools = [netaddr.IPRange(pool.first_ip, pool.last_ip) for pool in db_pools] return pools == iprange_pools def update_allocation_pools(self, pools, cidr): # Pools have already been validated in the subnet request object which # was sent to the subnet pool driver. Further validation should not be # required. if self._no_pool_changes(self._context, pools): return self.subnet_manager.delete_allocation_pools(self._context) self.create_allocation_pools(self.subnet_manager, self._context, pools, cidr) self._pools = pools def get_details(self): """Return subnet data as a SpecificSubnetRequest""" return ipam_req.SpecificSubnetRequest( self._tenant_id, self.subnet_manager.neutron_id, self._cidr, self._gateway_ip, self._pools) class NeutronDbPool(subnet_alloc.SubnetAllocator): """Subnet pools backed by Neutron Database. As this driver does not implement yet the subnet pool concept, most operations are either trivial or no-ops. """ def get_subnet(self, subnet_id): """Retrieve an IPAM subnet. :param subnet_id: Neutron subnet identifier :returns: a NeutronDbSubnet instance """ return NeutronDbSubnet.load(subnet_id, self._context) def allocate_subnet(self, subnet_request): """Create an IPAMSubnet object for the provided cidr. This method does not actually do any operation in the driver, given its simplified nature. :param cidr: subnet's CIDR :returns: a NeutronDbSubnet instance """ if self._subnetpool: subnet = super(NeutronDbPool, self).allocate_subnet(subnet_request) subnet_request = subnet.get_details() # SubnetRequest must be an instance of SpecificSubnet if not isinstance(subnet_request, ipam_req.SpecificSubnetRequest): raise ipam_exc.InvalidSubnetRequestType( subnet_type=type(subnet_request)) return NeutronDbSubnet.create_from_subnet_request(subnet_request, self._context) def update_subnet(self, subnet_request): """Update subnet info the in the IPAM driver. The only update subnet information the driver needs to be aware of are allocation pools. """ if not subnet_request.subnet_id: raise ipam_exc.InvalidSubnetRequest( reason=_("An identifier must be specified when updating " "a subnet")) if subnet_request.allocation_pools is None: LOG.debug("Update subnet request for subnet %s did not specify " "new allocation pools, there is nothing to do", subnet_request.subnet_id) return subnet = NeutronDbSubnet.load(subnet_request.subnet_id, self._context) cidr = netaddr.IPNetwork(subnet._cidr) subnet.update_allocation_pools(subnet_request.allocation_pools, cidr) return subnet def remove_subnet(self, subnet_id): """Remove data structures for a given subnet. IPAM-related data has no foreign key relationships to neutron subnet, so removing ipam subnet manually """ count = ipam_db_api.IpamSubnetManager.delete(self._context, subnet_id) if count < 1: LOG.error("IPAM subnet referenced to " "Neutron subnet %s does not exist", subnet_id) raise n_exc.SubnetNotFound(subnet_id=subnet_id) def needs_rollback(self): return False neutron-12.1.1/neutron/ipam/drivers/__init__.py0000664000175000017500000000000013553660046021515 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/ipam/__init__.py0000664000175000017500000000000013553660046020037 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/ipam/driver.py0000664000175000017500000001502513553660046017610 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from oslo_config import cfg import six from neutron.ipam import requests as ipam_req from neutron import manager @six.add_metaclass(abc.ABCMeta) class Pool(object): """Interface definition for an IPAM driver. There should be an instance of the driver for every subnet pool. """ def __init__(self, subnetpool, context): """Initialize pool :param subnetpool: SubnetPool of the address space to use. :type subnetpool: dict """ self._subnetpool = subnetpool self._context = context @classmethod def get_instance(cls, subnet_pool, context): """Returns an instance of the configured IPAM driver :param subnet_pool: Subnet pool of the address space to use. :type subnet_pool: dict :returns: An instance of Driver for the given subnet pool """ ipam_driver_name = cfg.CONF.ipam_driver mgr = manager.NeutronManager driver_class = mgr.load_class_for_provider('neutron.ipam_drivers', ipam_driver_name) return driver_class(subnet_pool, context) @abc.abstractmethod def allocate_subnet(self, request): """Allocates a subnet based on the subnet request :param request: Describes the allocation requested. :type request: An instance of a sub-class of SubnetRequest :returns: An instance of Subnet :raises: RequestNotSupported, IPAMAlreadyAllocated """ @abc.abstractmethod def get_subnet(self, subnet_id): """Gets the matching subnet if it has been allocated :param subnet_id: the subnet identifier :type subnet_id: str uuid :returns: An instance of IPAM Subnet :raises: IPAMAllocationNotFound """ @abc.abstractmethod def update_subnet(self, request): """Updates an already allocated subnet This is used to notify the external IPAM system of updates to a subnet. :param request: Update the subnet to match this request :type request: An instance of a sub-class of SpecificSubnetRequest :returns: An instance of IPAM Subnet :raises: RequestNotSupported, IPAMAllocationNotFound """ @abc.abstractmethod def remove_subnet(self, subnet_id): """Removes an allocation The initial reference implementation will probably do nothing. :param subnet_id: the subnet identifier :type subnet_id: str uuid :raises: IPAMAllocationNotFound """ def get_subnet_request_factory(self): """Returns default SubnetRequestFactory Can be overridden on driver level to return custom factory """ return ipam_req.SubnetRequestFactory def get_address_request_factory(self): """Returns default AddressRequestFactory Can be overridden on driver level to return custom factory """ return ipam_req.AddressRequestFactory @abc.abstractmethod def get_allocator(self, subnet_ids): """Gets an allocator for subnets passed in :param subnet_ids: ids for subnets from which the IP can be allocated :returns: An instance of IPAM SubnetGroup :raises: TODO(Carl) What sort of errors do we need to plan for? """ def needs_rollback(self): """Whether driver needs an explicit rollback when operations fail. A driver that (de)allocates resources in the same DB transaction passed to it by Neutron will not want explicit rollback. A truly external IPAM system would need to return True for sure. The default is True since all drivers were assumed to be designed to need it from the start. :returns: True if driver needs to be called on rollback """ return True @six.add_metaclass(abc.ABCMeta) class Subnet(object): """Interface definition for an IPAM subnet A subnet would typically be associated with a network but may not be. It could represent a dynamically routed IP address space in which case the normal network and broadcast addresses would be useable. It should always be a routable block of addresses and representable in CIDR notation. """ @abc.abstractmethod def allocate(self, address_request): """Allocates an IP address based on the request passed in :param address_request: Specifies what to allocate. :type address_request: An instance of a subclass of AddressRequest :returns: A netaddr.IPAddress :raises: AddressNotAvailable, AddressOutsideAllocationPool, AddressOutsideSubnet """ @abc.abstractmethod def deallocate(self, address): """Returns a previously allocated address to the pool :param address: The address to give back. :type address: A netaddr.IPAddress or convertible to one. :returns: None :raises: IPAMAllocationNotFound """ @abc.abstractmethod def get_details(self): """Returns the details of the subnet :returns: An instance of SpecificSubnetRequest with the subnet detail. """ @six.add_metaclass(abc.ABCMeta) class SubnetGroup(object): """Interface definition for a filtered group of IPAM Subnets Allocates from a group of semantically equivalent subnets. The list of candidate subnets *may* be ordered by preference but all of the subnets must be suitable for fulfilling the request. For example, all of them must be associated with the network we're trying to allocate an address for. """ @abc.abstractmethod def allocate(self, address_request): """Allocates an IP address based on the request passed in :param address_request: Specifies what to allocate. :type address_request: An instance of a subclass of AddressRequest :returns: A netaddr.IPAddress, subnet_id tuple :raises: AddressNotAvailable, AddressOutsideAllocationPool, AddressOutsideSubnet, IpAddressGenerationFailureAllSubnets """ neutron-12.1.1/neutron/quota/0000775000175000017500000000000013553660156016145 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/quota/resource_registry.py0000664000175000017500000002214213553660047022276 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from oslo_log import log import six from neutron._i18n import _ from neutron.db import api as db_api from neutron.quota import resource LOG = log.getLogger(__name__) # Wrappers for easing access to the ResourceRegistry singleton def register_resource(resource): ResourceRegistry.get_instance().register_resource(resource) def register_resource_by_name(resource_name, plural_name=None): ResourceRegistry.get_instance().register_resource_by_name( resource_name, plural_name) def get_all_resources(): return ResourceRegistry.get_instance().resources def unregister_all_resources(): if not ResourceRegistry._instance: return return ResourceRegistry.get_instance().unregister_resources() def get_resource(resource_name): return ResourceRegistry.get_instance().get_resource(resource_name) def is_tracked(resource_name): return ResourceRegistry.get_instance().is_tracked(resource_name) # auxiliary functions and decorators def set_resources_dirty(context): """Sets the dirty bit for resources with usage changes. This routine scans all registered resources, and, for those whose dirty status is True, sets the dirty bit to True in the database for the appropriate tenants. Please note that this routine begins a nested transaction, and it is not recommended that this transaction begins within another transaction. For this reason the function will raise a SqlAlchemy exception if such an attempt is made. :param context: a Neutron request context with a DB session """ if not cfg.CONF.QUOTAS.track_quota_usage: return for res in get_all_resources().values(): with db_api.context_manager.writer.using(context): if is_tracked(res.name) and res.dirty: res.mark_dirty(context) def resync_resource(context, resource_name, tenant_id): if not cfg.CONF.QUOTAS.track_quota_usage: return if is_tracked(resource_name): res = get_resource(resource_name) # If the resource is tracked count supports the resync_usage parameter res.resync(context, tenant_id) def mark_resources_dirty(f): """Decorator for functions which alter resource usage. This decorator ensures set_resource_dirty is invoked after completion of the decorated function. """ @six.wraps(f) def wrapper(_self, context, *args, **kwargs): ret_val = f(_self, context, *args, **kwargs) set_resources_dirty(context) return ret_val return wrapper class tracked_resources(object): """Decorator for specifying resources for which usage should be tracked. A plugin class can use this decorator to specify for which resources usage info should be tracked into an appropriate table rather than being explicitly counted. """ def __init__(self, override=False, **kwargs): self._tracked_resources = kwargs self._override = override def __call__(self, f): @six.wraps(f) def wrapper(*args, **kwargs): registry = ResourceRegistry.get_instance() for resource_name in self._tracked_resources: registry.set_tracked_resource( resource_name, self._tracked_resources[resource_name], self._override) return f(*args, **kwargs) return wrapper class ResourceRegistry(object): """Registry for resource subject to quota limits. This class keeps track of Neutron resources for which quota limits are enforced, regardless of whether their usage is being tracked or counted. For tracked-usage resources, that is to say those resources for which there are usage counters which are kept in sync with the actual number of rows in the database, this class allows the plugin to register their names either explicitly or through the @tracked_resources decorator, which should preferably be applied to the __init__ method of the class. """ _instance = None @classmethod def get_instance(cls): if cls._instance is None: cls._instance = cls() return cls._instance def __init__(self): self._resources = {} # Map usage tracked resources to the correspondent db model class self._tracked_resource_mappings = {} def __contains__(self, resource): return resource in self._resources def _create_resource_instance(self, resource_name, plural_name): """Factory function for quota Resource. This routine returns a resource instance of the appropriate type according to system configuration. If QUOTAS.track_quota_usage is True, and there is a model mapping for the current resource, this function will return an instance of AccountedResource; otherwise an instance of CountableResource. """ if (not cfg.CONF.QUOTAS.track_quota_usage or resource_name not in self._tracked_resource_mappings): LOG.info("Creating instance of CountableResource for " "resource:%s", resource_name) return resource.CountableResource( resource_name, resource._count_resource, 'quota_%s' % resource_name) else: LOG.info("Creating instance of TrackedResource for " "resource:%s", resource_name) return resource.TrackedResource( resource_name, self._tracked_resource_mappings[resource_name], 'quota_%s' % resource_name) def set_tracked_resource(self, resource_name, model_class, override=False): # Do not do anything if tracking is disabled by config if not cfg.CONF.QUOTAS.track_quota_usage: return if isinstance(self._resources.get(resource_name), resource.CountableResource): raise RuntimeError(_("Resource %s is already registered as a " "countable resource.") % resource_name) current_model_class = self._tracked_resource_mappings.setdefault( resource_name, model_class) # Check whether setdefault also set the entry in the dict if current_model_class != model_class: LOG.debug("A model class is already defined for %(resource)s: " "%(current_model_class)s. Override:%(override)s", {'resource': resource_name, 'current_model_class': current_model_class, 'override': override}) if override: self._tracked_resource_mappings[resource_name] = model_class LOG.debug("Tracking information for resource: %s configured", resource_name) def is_tracked(self, resource_name): """Find out if a resource if tracked or not. :param resource_name: name of the resource. :returns: True if resource_name is registered and tracked, otherwise False. Please note that here when False it returned it simply means that resource_name is not a TrackedResource instance, it does not necessarily mean that the resource is not registered. """ return resource_name in self._tracked_resource_mappings def register_resource(self, resource): if resource.name in self._resources: LOG.warning('%s is already registered', resource.name) if resource.name in self._tracked_resource_mappings: resource.register_events() self._resources[resource.name] = resource def register_resources(self, resources): for res in resources: self.register_resource(res) def register_resource_by_name(self, resource_name, plural_name=None): """Register a resource by name.""" resource = self._create_resource_instance( resource_name, plural_name) self.register_resource(resource) def unregister_resources(self): """Unregister all resources.""" for (res_name, res) in self._resources.items(): if res_name in self._tracked_resource_mappings: res.unregister_events() self._resources.clear() self._tracked_resource_mappings.clear() def get_resource(self, resource_name): """Return a resource given its name. :returns: The resource instance or None if the resource is not found """ return self._resources.get(resource_name) @property def resources(self): return self._resources neutron-12.1.1/neutron/quota/__init__.py0000664000175000017500000002557113553660047020267 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Quotas for instances, volumes, and floating ips.""" import sys from neutron_lib import exceptions as lib_exc from oslo_config import cfg from oslo_log import log as logging from oslo_log import versionutils from oslo_utils import importutils import six import webob from neutron._i18n import _ from neutron.common import exceptions from neutron.conf import quota from neutron.db.quota import api as quota_api from neutron.quota import resource_registry LOG = logging.getLogger(__name__) QUOTA_DB_MODULE = quota.QUOTA_DB_MODULE QUOTA_DB_DRIVER = quota.QUOTA_DB_DRIVER QUOTA_CONF_DRIVER = quota.QUOTA_CONF_DRIVER # Register the configuration options quota.register_quota_opts(quota.core_quota_opts) class ConfDriver(object): """Configuration driver. Driver to perform necessary checks to enforce quotas and obtain quota information. The default driver utilizes the default values in neutron.conf. """ def _get_quotas(self, context, resources): """Get quotas. A helper method which retrieves the quotas for the specific resources identified by keys, and which apply to the current context. :param context: The request context, for access checks. :param resources: A dictionary of the registered resources. """ quotas = {} for resource in resources.values(): quotas[resource.name] = resource.default return quotas def limit_check(self, context, tenant_id, resources, values): """Check simple quota limits. For limits--those quotas for which there is no usage synchronization function--this method checks that a set of proposed values are permitted by the limit restriction. If any of the proposed values is over the defined quota, an OverQuota exception will be raised with the sorted list of the resources which are too high. Otherwise, the method returns nothing. :param context: The request context, for access checks. :param tenant_id: The tenant_id to check quota. :param resources: A dictionary of the registered resources. :param values: A dictionary of the values to check against the quota. """ # Ensure no value is less than zero unders = [key for key, val in values.items() if val < 0] if unders: raise exceptions.InvalidQuotaValue(unders=sorted(unders)) # Get the applicable quotas quotas = self._get_quotas(context, resources) # Check the quotas and construct a list of the resources that # would be put over limit by the desired values overs = [key for key, val in values.items() if quotas[key] >= 0 and quotas[key] < val] if overs: raise lib_exc.OverQuota(overs=sorted(overs), quotas=quotas, usages={}) @staticmethod def get_tenant_quotas(context, resources, tenant_id): quotas = {} sub_resources = dict((k, v) for k, v in resources.items()) for resource in sub_resources.values(): quotas[resource.name] = resource.default return quotas @staticmethod def get_all_quotas(context, resources): return [] @staticmethod def delete_tenant_quota(context, tenant_id): msg = _('Access to this resource was denied.') raise webob.exc.HTTPForbidden(msg) @staticmethod def update_quota_limit(context, tenant_id, resource, limit): msg = _('Access to this resource was denied.') raise webob.exc.HTTPForbidden(msg) def make_reservation(self, context, tenant_id, resources, deltas, plugin): """This driver does not support reservations. This routine is provided for backward compatibility purposes with the API controllers which have now been adapted to make reservations rather than counting resources and checking limits - as this routine ultimately does. """ for resource in deltas.keys(): count = QUOTAS.count(context, resource, plugin, tenant_id) total_use = deltas.get(resource, 0) + count deltas[resource] = total_use self.limit_check( context, tenant_id, resource_registry.get_all_resources(), deltas) # return a fake reservation - the REST controller expects it return quota_api.ReservationInfo('fake', None, None, None) def commit_reservation(self, context, reservation_id): """This is a noop as this driver does not support reservations.""" def cancel_reservation(self, context, reservation_id): """This is a noop as this driver does not support reservations.""" class QuotaEngine(object): """Represent the set of recognized quotas.""" _instance = None @classmethod def get_instance(cls): if not cls._instance: cls._instance = cls() return cls._instance def __init__(self, quota_driver_class=None): """Initialize a Quota object.""" self._driver = None self._driver_class = quota_driver_class def get_driver(self): if self._driver is None: _driver_class = (self._driver_class or cfg.CONF.QUOTAS.quota_driver) if (_driver_class == QUOTA_DB_DRIVER and QUOTA_DB_MODULE not in sys.modules): # If quotas table is not loaded, force config quota driver. _driver_class = QUOTA_CONF_DRIVER LOG.info("ConfDriver is used as quota_driver because the " "loaded plugin does not support 'quotas' table.") if isinstance(_driver_class, six.string_types): _driver_class = importutils.import_object(_driver_class) if isinstance(_driver_class, ConfDriver): versionutils.report_deprecated_feature( LOG, ("The quota driver neutron.quota.ConfDriver is " "deprecated as of Liberty. " "neutron.db.quota.driver.DbQuotaDriver should " "be used in its place")) self._driver = _driver_class LOG.info('Loaded quota_driver: %s.', _driver_class) return self._driver def count(self, context, resource_name, *args, **kwargs): """Count a resource. For countable resources, invokes the count() function and returns its result. Arguments following the context and resource are passed directly to the count function declared by the resource. :param context: The request context, for access checks. :param resource_name: The name of the resource, as a string. """ # Get the resource res = resource_registry.get_resource(resource_name) if not res or not hasattr(res, 'count'): raise exceptions.QuotaResourceUnknown(unknown=[resource_name]) return res.count(context, *args, **kwargs) def make_reservation(self, context, tenant_id, deltas, plugin): # Verify that resources are managed by the quota engine # Ensure no value is less than zero unders = [key for key, val in deltas.items() if val < 0] if unders: raise exceptions.InvalidQuotaValue(unders=sorted(unders)) requested_resources = set(deltas.keys()) all_resources = resource_registry.get_all_resources() managed_resources = set([res for res in all_resources.keys() if res in requested_resources]) # Make sure we accounted for all of them... unknown_resources = requested_resources - managed_resources if unknown_resources: raise exceptions.QuotaResourceUnknown( unknown=sorted(unknown_resources)) # FIXME(salv-orlando): There should be no reason for sending all the # resource in the registry to the quota driver, but as other driver # APIs request them, this will be sorted out with a different patch. return self.get_driver().make_reservation( context, tenant_id, all_resources, deltas, plugin) def commit_reservation(self, context, reservation_id): self.get_driver().commit_reservation(context, reservation_id) def cancel_reservation(self, context, reservation_id): self.get_driver().cancel_reservation(context, reservation_id) def limit_check(self, context, tenant_id, **values): """Check simple quota limits. For limits--those quotas for which there is no usage synchronization function--this method checks that a set of proposed values are permitted by the limit restriction. The values to check are given as keyword arguments, where the key identifies the specific quota limit to check, and the value is the proposed value. This method will raise a QuotaResourceUnknown exception if a given resource is unknown or if it is not a countable resource. If any of the proposed values exceeds the respective quota defined for the tenant, an OverQuota exception will be raised. The exception will include a sorted list with the resources which exceed the quota limit. Otherwise, the method returns nothing. :param context: Request context :param tenant_id: Tenant for which the quota limit is being checked :param values: Dict specifying requested deltas for each resource """ # TODO(salv-orlando): Deprecate calls to this API # Verify that resources are managed by the quota engine requested_resources = set(values.keys()) managed_resources = set([res for res in resource_registry.get_all_resources() if res in requested_resources]) # Make sure we accounted for all of them... unknown_resources = requested_resources - managed_resources if unknown_resources: raise exceptions.QuotaResourceUnknown( unknown=sorted(unknown_resources)) return self.get_driver().limit_check( context, tenant_id, resource_registry.get_all_resources(), values) QUOTAS = QuotaEngine.get_instance() neutron-12.1.1/neutron/quota/resource.py0000664000175000017500000003631613553660047020356 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins import constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_log import log from oslo_utils import excutils from sqlalchemy import exc as sql_exc from sqlalchemy.orm import session as se from neutron.db import api as db_api from neutron.db.quota import api as quota_api LOG = log.getLogger(__name__) def _count_resource(context, collection_name, tenant_id): count_getter_name = "get_%s_count" % collection_name getter_name = "get_%s" % collection_name plugins = directory.get_plugins() for pname in sorted(plugins, # inspect core plugin first key=lambda n: n != constants.CORE): # Some plugins support a count method for particular resources, using a # DB's optimized counting features. We try to use that one if present. # Otherwise just use regular getter to retrieve all objects and count # in python, allowing older plugins to still be supported try: obj_count_getter = getattr(plugins[pname], count_getter_name) return obj_count_getter( context, filters={'tenant_id': [tenant_id]}) except (NotImplementedError, AttributeError): try: obj_getter = getattr(plugins[pname], getter_name) obj_list = obj_getter( context, filters={'tenant_id': [tenant_id]}) return len(obj_list) if obj_list else 0 except (NotImplementedError, AttributeError): pass raise NotImplementedError( 'No plugins that support counting %s found.' % collection_name) class BaseResource(object): """Describe a single resource for quota checking.""" def __init__(self, name, flag, plural_name=None): """Initializes a resource. :param name: The name of the resource, i.e., "instances". :param flag: The name of the flag or configuration option :param plural_name: Plural form of the resource name. If not specified, it is generated automatically by appending an 's' to the resource name, unless it ends with a 'y'. In that case the last letter is removed, and 'ies' is appended. Dashes are always converted to underscores. """ self.name = name # If a plural name is not supplied, default to adding an 's' to # the resource name, unless the resource name ends in 'y', in which # case remove the 'y' and add 'ies'. Even if the code should not fiddle # too much with English grammar, this is a rather common and easy to # implement rule. if plural_name: self.plural_name = plural_name elif self.name[-1] == 'y': self.plural_name = "%sies" % self.name[:-1] else: self.plural_name = "%ss" % self.name # always convert dashes to underscores self.plural_name = self.plural_name.replace('-', '_') self.flag = flag @property def default(self): """Return the default value of the quota.""" # Any negative value will be interpreted as an infinite quota, # and stored as -1 for compatibility with current behaviour value = getattr(cfg.CONF.QUOTAS, self.flag, cfg.CONF.QUOTAS.default_quota) return max(value, -1) @property def dirty(self): """Return the current state of the Resource instance. :returns: True if the resource count is out of sync with actual date, False if it is in sync, and None if the resource instance does not track usage. """ class CountableResource(BaseResource): """Describe a resource where the counts are determined by a function.""" def __init__(self, name, count, flag=None, plural_name=None): """Initializes a CountableResource. Countable resources are those resources which directly correspond to objects in the database, i.e., network, subnet, etc.,. A CountableResource must be constructed with a counting function, which will be called to determine the current counts of the resource. The counting function will be passed the context, along with the extra positional and keyword arguments that are passed to Quota.count(). It should return an integer specifying the count. :param name: The name of the resource, i.e., "instances". :param count: A callable which returns the count of the resource. The arguments passed are as described above. :param flag: The name of the flag or configuration option which specifies the default value of the quota for this resource. :param plural_name: Plural form of the resource name. If not specified, it is generated automatically by appending an 's' to the resource name, unless it ends with a 'y'. In that case the last letter is removed, and 'ies' is appended. Dashes are always converted to underscores. """ super(CountableResource, self).__init__( name, flag=flag, plural_name=plural_name) self._count_func = count def count(self, context, plugin, tenant_id, **kwargs): # NOTE(ihrachys) _count_resource doesn't receive plugin return self._count_func(context, self.plural_name, tenant_id) class TrackedResource(BaseResource): """Resource which keeps track of its usage data.""" def __init__(self, name, model_class, flag, plural_name=None): """Initializes an instance for a given resource. TrackedResource are directly mapped to data model classes. Resource usage is tracked in the database, and the model class to which this resource refers is monitored to ensure always "fresh" usage data are employed when performing quota checks. This class operates under the assumption that the model class describing the resource has a tenant identifier attribute. :param name: The name of the resource, i.e., "networks". :param model_class: The sqlalchemy model class of the resource for which this instance is being created :param flag: The name of the flag or configuration option which specifies the default value of the quota for this resource. :param plural_name: Plural form of the resource name. If not specified, it is generated automatically by appending an 's' to the resource name, unless it ends with a 'y'. In that case the last letter is removed, and 'ies' is appended. Dashes are always converted to underscores. """ super(TrackedResource, self).__init__( name, flag=flag, plural_name=plural_name) # Register events for addition/removal of records in the model class # As tenant_id is immutable for all Neutron objects there is no need # to register a listener for update events self._model_class = model_class self._dirty_tenants = set() self._out_of_sync_tenants = set() @property def dirty(self): return self._dirty_tenants def mark_dirty(self, context): if not self._dirty_tenants: return with db_api.context_manager.writer.using(context): # It is not necessary to protect this operation with a lock. # Indeed when this method is called the request has been processed # and therefore all resources created or deleted. # dirty_tenants will contain all the tenants for which the # resource count is changed. The list might contain also tenants # for which resource count was altered in other requests, but this # won't be harmful. dirty_tenants_snap = self._dirty_tenants.copy() for tenant_id in dirty_tenants_snap: quota_api.set_quota_usage_dirty(context, self.name, tenant_id) self._out_of_sync_tenants |= dirty_tenants_snap self._dirty_tenants -= dirty_tenants_snap def _db_event_handler(self, mapper, _conn, target): try: tenant_id = target['tenant_id'] except AttributeError: with excutils.save_and_reraise_exception(): LOG.error("Model class %s does not have a tenant_id " "attribute", target) self._dirty_tenants.add(tenant_id) # Retry the operation if a duplicate entry exception is raised. This # can happen is two or more workers are trying to create a resource of a # give kind for the same tenant concurrently. Retrying the operation will # ensure that an UPDATE statement is emitted rather than an INSERT one @db_api.retry_if_session_inactive() def _set_quota_usage(self, context, tenant_id, in_use): return quota_api.set_quota_usage( context, self.name, tenant_id, in_use=in_use) def _resync(self, context, tenant_id, in_use): # Update quota usage usage_info = self._set_quota_usage(context, tenant_id, in_use) self._dirty_tenants.discard(tenant_id) self._out_of_sync_tenants.discard(tenant_id) LOG.debug(("Unset dirty status for tenant:%(tenant_id)s on " "resource:%(resource)s"), {'tenant_id': tenant_id, 'resource': self.name}) return usage_info def resync(self, context, tenant_id): if tenant_id not in self._out_of_sync_tenants: return LOG.debug(("Synchronizing usage tracker for tenant:%(tenant_id)s on " "resource:%(resource)s"), {'tenant_id': tenant_id, 'resource': self.name}) in_use = context.session.query( self._model_class.tenant_id).filter_by( tenant_id=tenant_id).count() # Update quota usage return self._resync(context, tenant_id, in_use) def count_used(self, context, tenant_id, resync_usage=True): """Returns the current usage count for the resource. :param context: The request context. :param tenant_id: The ID of the tenant :param resync_usage: Default value is set to True. Syncs with in_use usage. """ # Load current usage data, setting a row-level lock on the DB usage_info = quota_api.get_quota_usage_by_resource_and_tenant( context, self.name, tenant_id) # If dirty or missing, calculate actual resource usage querying # the database and set/create usage info data # NOTE: this routine "trusts" usage counters at service startup. This # assumption is generally valid, but if the database is tampered with, # or if data migrations do not take care of usage counters, the # assumption will not hold anymore if (tenant_id in self._dirty_tenants or not usage_info or usage_info.dirty): LOG.debug(("Usage tracker for resource:%(resource)s and tenant:" "%(tenant_id)s is out of sync, need to count used " "quota"), {'resource': self.name, 'tenant_id': tenant_id}) in_use = context.session.query( self._model_class.tenant_id).filter_by( tenant_id=tenant_id).count() # Update quota usage, if requested (by default do not do that, as # typically one counts before adding a record, and that would mark # the usage counter as dirty again) if resync_usage: usage_info = self._resync(context, tenant_id, in_use) else: resource = usage_info.resource if usage_info else self.name tenant_id = usage_info.tenant_id if usage_info else tenant_id dirty = usage_info.dirty if usage_info else True usage_info = quota_api.QuotaUsageInfo( resource, tenant_id, in_use, dirty) LOG.debug(("Quota usage for %(resource)s was recalculated. " "Used quota:%(used)d."), {'resource': self.name, 'used': usage_info.used}) return usage_info.used def count_reserved(self, context, tenant_id): """Return the current reservation count for the resource.""" # NOTE(princenana) Current implementation of reservations # is ephemeral and returns the default value reservations = quota_api.get_reservations_for_resources( context, tenant_id, [self.name]) reserved = reservations.get(self.name, 0) return reserved def count(self, context, _plugin, tenant_id, resync_usage=True): """Return the count of the resource. The _plugin parameter is unused but kept for compatibility with the signature of the count method for CountableResource instances. """ return (self.count_used(context, tenant_id, resync_usage) + self.count_reserved(context, tenant_id)) def _except_bulk_delete(self, delete_context): if delete_context.mapper.class_ == self._model_class: raise RuntimeError("%s may not be deleted in bulk because " "it is tracked by the quota engine via " "SQLAlchemy event handlers, which are not " "compatible with bulk deletes." % self._model_class) def register_events(self): listen = db_api.sqla_listen listen(self._model_class, 'after_insert', self._db_event_handler) listen(self._model_class, 'after_delete', self._db_event_handler) listen(se.Session, 'after_bulk_delete', self._except_bulk_delete) def unregister_events(self): try: db_api.sqla_remove(self._model_class, 'after_insert', self._db_event_handler) db_api.sqla_remove(self._model_class, 'after_delete', self._db_event_handler) db_api.sqla_remove(se.Session, 'after_bulk_delete', self._except_bulk_delete) except sql_exc.InvalidRequestError: LOG.warning("No sqlalchemy event for resource %s found", self.name) neutron-12.1.1/neutron/policy.py0000664000175000017500000004645513553660047016702 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import re import sys from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions from neutron_lib.plugins import directory from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import log as logging from oslo_policy import policy from oslo_utils import excutils import six from neutron._i18n import _ from neutron.api.v2 import attributes from neutron.common import cache_utils as cache from neutron.common import constants as const LOG = logging.getLogger(__name__) _ENFORCER = None ADMIN_CTX_POLICY = 'context_is_admin' ADVSVC_CTX_POLICY = 'context_is_advsvc' def reset(): global _ENFORCER if _ENFORCER: _ENFORCER.clear() _ENFORCER = None def init(conf=cfg.CONF, policy_file=None): """Init an instance of the Enforcer class.""" global _ENFORCER if not _ENFORCER: _ENFORCER = policy.Enforcer(conf, policy_file=policy_file) _ENFORCER.load_rules(True) def refresh(policy_file=None): """Reset policy and init a new instance of Enforcer.""" reset() init(policy_file=policy_file) def get_resource_and_action(action, pluralized=None): """Return resource and enforce_attr_based_check(boolean) per resource and action extracted from api operation. """ data = action.split(':', 1)[0].split('_', 1) resource = pluralized or ("%ss" % data[-1]) enforce_attr_based_check = data[0] not in ('get', 'delete') return (resource, enforce_attr_based_check) def set_rules(policies, overwrite=True): """Set rules based on the provided dict of rules. :param policies: New policies to use. It should be an instance of dict. :param overwrite: Whether to overwrite current rules or update them with the new rules. """ LOG.debug("Loading policies from file: %s", _ENFORCER.policy_path) init() _ENFORCER.set_rules(policies, overwrite) def _is_attribute_explicitly_set(attribute_name, resource, target, action): """Verify that an attribute is present and is explicitly set.""" if target.get(const.ATTRIBUTES_TO_UPDATE): # In the case of update, the function should not pay attention to a # default value of an attribute, but check whether it was explicitly # marked as being updated instead. return (attribute_name in target[const.ATTRIBUTES_TO_UPDATE] and target[attribute_name] is not constants.ATTR_NOT_SPECIFIED) result = (attribute_name in target and target[attribute_name] is not constants.ATTR_NOT_SPECIFIED) if result and 'default' in resource[attribute_name]: return target[attribute_name] != resource[attribute_name]['default'] return result def _should_validate_sub_attributes(attribute, sub_attr): """Verify that sub-attributes are iterable and should be validated.""" validate = attribute.get('validate') return (validate and isinstance(sub_attr, collections.Iterable) and any([k.startswith('type:dict') and v for (k, v) in validate.items()])) def _build_subattr_match_rule(attr_name, attr, action, target): """Create the rule to match for sub-attribute policy checks.""" # TODO(salv-orlando): Instead of relying on validator info, introduce # typing for API attributes # Expect a dict as type descriptor validate = attr['validate'] key = [k for k in validate.keys() if k.startswith('type:dict')] if not key: LOG.warning("Unable to find data type descriptor for attribute %s", attr_name) return data = validate[key[0]] if not isinstance(data, dict): LOG.debug("Attribute type descriptor is not a dict. Unable to " "generate any sub-attr policy rule for %s.", attr_name) return sub_attr_rules = [policy.RuleCheck('rule', '%s:%s:%s' % (action, attr_name, sub_attr_name)) for sub_attr_name in data if sub_attr_name in target[attr_name]] return policy.AndCheck(sub_attr_rules) def _build_list_of_subattrs_rule(attr_name, attribute_value, action): rules = [] for sub_attr in attribute_value: if isinstance(sub_attr, dict): for k in sub_attr: rules.append(policy.RuleCheck( 'rule', '%s:%s:%s' % (action, attr_name, k))) if rules: return policy.AndCheck(rules) def _process_rules_list(rules, match_rule): """Recursively walk a policy rule to extract a list of match entries.""" if isinstance(match_rule, policy.RuleCheck): rules.append(match_rule.match) elif isinstance(match_rule, policy.AndCheck): for rule in match_rule.rules: _process_rules_list(rules, rule) return rules def _build_match_rule(action, target, pluralized): """Create the rule to match for a given action. The policy rule to be matched is built in the following way: 1) add entries for matching permission on objects 2) add an entry for the specific action (e.g.: create_network) 3) add an entry for attributes of a resource for which the action is being executed (e.g.: create_network:shared) 4) add an entry for sub-attributes of a resource for which the action is being executed (e.g.: create_router:external_gateway_info:network_id) """ match_rule = policy.RuleCheck('rule', action) resource, enforce_attr_based_check = get_resource_and_action( action, pluralized) if enforce_attr_based_check: # assigning to variable with short name for improving readability res_map = attributes.RESOURCE_ATTRIBUTE_MAP if resource in res_map: for attribute_name in res_map[resource]: if _is_attribute_explicitly_set(attribute_name, res_map[resource], target, action): attribute = res_map[resource][attribute_name] if 'enforce_policy' in attribute: attr_rule = policy.RuleCheck( 'rule', '%s:%s' % (action, attribute_name)) # Build match entries for sub-attributes if _should_validate_sub_attributes( attribute, target[attribute_name]): attr_rule = policy.AndCheck( [attr_rule, _build_subattr_match_rule( attribute_name, attribute, action, target)]) attribute_value = target[attribute_name] if isinstance(attribute_value, list): subattr_rule = _build_list_of_subattrs_rule( attribute_name, attribute_value, action) if subattr_rule: attr_rule = policy.AndCheck( [attr_rule, subattr_rule]) match_rule = policy.AndCheck([match_rule, attr_rule]) return match_rule # This check is registered as 'tenant_id' so that it can override # GenericCheck which was used for validating parent resource ownership. # This will prevent us from having to handling backward compatibility # for policy.json # TODO(salv-orlando): Reinstate GenericCheck for simple tenant_id checks @policy.register('tenant_id') class OwnerCheck(policy.Check): """Resource ownership check. This check verifies the owner of the current resource, or of another resource referenced by the one under analysis. In the former case it falls back to a regular GenericCheck, whereas in the latter case it leverages the plugin to load the referenced resource and perform the check. """ def __init__(self, kind, match): # Process the match try: self.target_field = re.findall(r'^\%\((.*)\)s$', match)[0] except IndexError: err_reason = (_("Unable to identify a target field from:%s. " "Match should be in the form %%()s") % match) LOG.exception(err_reason) raise exceptions.PolicyInitError( policy="%s:%s" % (kind, match), reason=err_reason) self._cache = cache._get_memory_cache_region(expiration_time=5) super(OwnerCheck, self).__init__(kind, match) @cache.cache_method_results def _extract(self, resource_type, resource_id, field): # NOTE(salv-orlando): This check currently assumes the parent # resource is handled by the core plugin. It might be worth # having a way to map resources to plugins so to make this # check more general f = getattr(directory.get_plugin(), 'get_%s' % resource_type) # f *must* exist, if not found it is better to let neutron # explode. Check will be performed with admin context try: data = f(context.get_admin_context(), resource_id, fields=[field]) except exceptions.NotFound as e: # NOTE(kevinbenton): a NotFound exception can occur if a # list operation is happening at the same time as one of # the parents and its children being deleted. So we issue # a RetryRequest so the API will redo the lookup and the # problem items will be gone. raise db_exc.RetryRequest(e) except Exception: with excutils.save_and_reraise_exception(): LOG.exception('Policy check error while calling %s!', f) return data[field] def __call__(self, target, creds, enforcer): if self.target_field not in target: # policy needs a plugin check # target field is in the form resource:field # however if they're not separated by a colon, use an underscore # as a separator for backward compatibility def do_split(separator): parent_res, parent_field = self.target_field.split( separator, 1) return parent_res, parent_field for separator in (':', '_'): try: parent_res, parent_field = do_split(separator) break except ValueError: LOG.debug("Unable to find ':' as separator in %s.", self.target_field) else: # If we are here split failed with both separators err_reason = (_("Unable to find resource name in %s") % self.target_field) LOG.error(err_reason) raise exceptions.PolicyCheckError( policy="%s:%s" % (self.kind, self.match), reason=err_reason) parent_foreign_key = attributes.RESOURCE_FOREIGN_KEYS.get( "%ss" % parent_res, None) if not parent_foreign_key: err_reason = (_("Unable to verify match:%(match)s as the " "parent resource: %(res)s was not found") % {'match': self.match, 'res': parent_res}) LOG.error(err_reason) raise exceptions.PolicyCheckError( policy="%s:%s" % (self.kind, self.match), reason=err_reason) target[self.target_field] = self._extract( parent_res, target[parent_foreign_key], parent_field) match = self.match % target if self.kind in creds: return match == six.text_type(creds[self.kind]) return False @policy.register('field') class FieldCheck(policy.Check): def __init__(self, kind, match): # Process the match resource, field_value = match.split(':', 1) field, value = field_value.split('=', 1) super(FieldCheck, self).__init__(kind, '%s:%s:%s' % (resource, field, value)) # Value might need conversion - we need help from the attribute map try: attr = attributes.RESOURCE_ATTRIBUTE_MAP[resource][field] conv_func = attr['convert_to'] except KeyError: conv_func = lambda x: x self.field = field self.resource = resource self.value = conv_func(value) self.regex = re.compile(value[1:]) if value.startswith('~') else None def __call__(self, target_dict, cred_dict, enforcer): target_value = self._get_target_value(target_dict) # target_value might be a boolean, explicitly compare with None if target_value is None: return False if self.regex: return bool(self.regex.match(target_value)) return target_value == self.value def _get_target_value(self, target_dict): if self.field in target_dict: return target_dict[self.field] # NOTE(slaweq): In case that target field is "networks:shared" we need # to treat it in "special" way as it may be used for resources other # than network, e.g. for port or subnet target_value = None if self.resource == "networks" and self.field == constants.SHARED: target_network_id = target_dict.get("network_id") if not target_network_id: LOG.debug("Unable to find network_id field in target: " "%(target_dict)s", {'field': self.field, 'target_dict': target_dict}) return project_id = target_dict.get('project_id') ctx = (context.Context(tenant_id=project_id) if project_id else context.get_admin_context()) plugin = directory.get_plugin() network = plugin.get_network(ctx, target_network_id) target_value = network.get(self.field) if target_value is None: LOG.debug("Unable to find requested field: %(field)s in target: " "%(target_dict)s", {'field': self.field, 'target_dict': target_dict}) return target_value def _prepare_check(context, action, target, pluralized): """Prepare rule, target, and credentials for the policy engine.""" # Compare with None to distinguish case in which target is {} if target is None: target = {} match_rule = _build_match_rule(action, target, pluralized) credentials = context.to_policy_values() return match_rule, target, credentials def log_rule_list(match_rule): if LOG.isEnabledFor(logging.DEBUG): rules = _process_rules_list([], match_rule) LOG.debug("Enforcing rules: %s", rules) def check(context, action, target, plugin=None, might_not_exist=False, pluralized=None): """Verifies that the action is valid on the target in this context. :param context: neutron context :param action: string representing the action to be checked this should be colon separated for clarity. :param target: dictionary representing the object of the action for object creation this should be a dictionary representing the location of the object e.g. ``{'project_id': context.project_id}`` :param plugin: currently unused and deprecated. Kept for backward compatibility. :param might_not_exist: If True the policy check is skipped (and the function returns True) if the specified policy does not exist. Defaults to false. :param pluralized: pluralized case of resource e.g. firewall_policy -> pluralized = "firewall_policies" :return: Returns True if access is permitted else False. """ # If we already know the context has admin rights do not perform an # additional check and authorize the operation if context.is_admin: return True if might_not_exist and not (_ENFORCER.rules and action in _ENFORCER.rules): return True match_rule, target, credentials = _prepare_check(context, action, target, pluralized) result = _ENFORCER.enforce(match_rule, target, credentials, pluralized=pluralized) return result def enforce(context, action, target, plugin=None, pluralized=None): """Verifies that the action is valid on the target in this context. :param context: neutron context :param action: string representing the action to be checked this should be colon separated for clarity. :param target: dictionary representing the object of the action for object creation this should be a dictionary representing the location of the object e.g. ``{'project_id': context.project_id}`` :param plugin: currently unused and deprecated. Kept for backward compatibility. :param pluralized: pluralized case of resource e.g. firewall_policy -> pluralized = "firewall_policies" :raises oslo_policy.policy.PolicyNotAuthorized: if verification fails. """ # If we already know the context has admin rights do not perform an # additional check and authorize the operation if context.is_admin: return True rule, target, credentials = _prepare_check(context, action, target, pluralized) try: result = _ENFORCER.enforce(rule, target, credentials, action=action, do_raise=True) except policy.PolicyNotAuthorized: with excutils.save_and_reraise_exception(): log_rule_list(rule) LOG.debug("Failed policy check for '%s'", action) return result def get_enforcer(): # NOTE(amotoki): This was borrowed from nova/policy.py. # This method is for use by oslo.policy CLI scripts. Those scripts need the # 'output-file' and 'namespace' options, but having those in sys.argv means # loading the neutron config options will fail as those are not expected to # be present. So we pass in an arg list with those stripped out. conf_args = [] # Start at 1 because cfg.CONF expects the equivalent of sys.argv[1:] i = 1 while i < len(sys.argv): if sys.argv[i].strip('-') in ['namespace', 'output-file']: i += 2 continue conf_args.append(sys.argv[i]) i += 1 cfg.CONF(conf_args, project='neutron') init() return _ENFORCER neutron-12.1.1/neutron/extensions/0000775000175000017500000000000013553660156017213 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/extensions/availability_zone.py0000664000175000017500000000372213553660046023274 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import abc from neutron_lib.api.definitions import availability_zone as az_def from neutron_lib.api import extensions as api_extensions from neutron_lib.plugins import directory import six from neutron.api import extensions from neutron.api.v2 import base class Availability_zone(api_extensions.APIExtensionDescriptor): """Availability zone extension.""" api_definition = az_def @classmethod def get_resources(cls): """Returns Ext Resources.""" plugin = directory.get_plugin() params = az_def.RESOURCE_ATTRIBUTE_MAP.get(az_def.COLLECTION_NAME) controller = base.create_resource(az_def.COLLECTION_NAME, az_def.RESOURCE_NAME, plugin, params) ex = extensions.ResourceExtension(az_def.COLLECTION_NAME, controller) return [ex] @six.add_metaclass(abc.ABCMeta) class AvailabilityZonePluginBase(object): """REST API to operate the Availability Zone.""" @abc.abstractmethod def get_availability_zones(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Return availability zones which a resource belongs to""" @abc.abstractmethod def validate_availability_zones(self, context, resource_type, availability_zones): """Verify that the availability zones exist.""" neutron-12.1.1/neutron/extensions/subnetallocation.py0000664000175000017500000000255313553660046023136 0ustar zuulzuul00000000000000# Copyright (c) 2015 Hewlett-Packard Development Company, L.P. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import extensions from neutron_lib import constants class Subnetallocation(extensions.ExtensionDescriptor): """Extension class supporting subnet allocation.""" @classmethod def get_name(cls): return "Subnet Allocation" @classmethod def get_alias(cls): return constants.SUBNET_ALLOCATION_EXT_ALIAS @classmethod def get_description(cls): return "Enables allocation of subnets from a subnet pool" @classmethod def get_updated(cls): return "2015-03-30T10:00:00-00:00" @classmethod def get_resources(cls): """Returns Ext Resources.""" return [] def get_extended_resources(self, version): return {} neutron-12.1.1/neutron/extensions/netmtu_writable.py0000664000175000017500000000147713553660046023001 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import network_mtu_writable as apidef from neutron_lib.api import extensions class Netmtu_writable(extensions.APIExtensionDescriptor): """Extension class supporting writable network MTU.""" api_definition = apidef neutron-12.1.1/neutron/extensions/tagging.py0000664000175000017500000002206713553660047021213 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.api import extensions as api_extensions from neutron_lib.api import faults from neutron_lib.api import validators from neutron_lib import exceptions from neutron_lib.plugins import directory from neutron_lib.services import base as service_base import six import webob.exc from neutron._i18n import _ from neutron.api import extensions from neutron.api.v2 import resource as api_resource from neutron.common import rpc as n_rpc from neutron.db import standard_attr TAG = 'tag' TAGS = TAG + 's' MAX_TAG_LEN = 60 TAG_PLUGIN_TYPE = 'TAG' # Not support resources supported by tag, tag-ext EXCEPTION_RESOURCES = ['networks', 'subnets', 'ports', 'subnetpools', 'routers'] # TODO(hichihara): This method is removed after tag, tag-ext extensions # have been removed. def get_tagging_supported_resources(): # Removes some resources supported by tag, tag-ext parent_map = standard_attr.get_tag_resource_parent_map() remove_resources = [res for res in parent_map if res in EXCEPTION_RESOURCES] for resource in remove_resources: del parent_map[resource] return parent_map TAG_SUPPORTED_RESOURCES = get_tagging_supported_resources() TAG_ATTRIBUTE_MAP = { TAGS: {'allow_post': False, 'allow_put': False, 'is_visible': True} } class TagResourceNotFound(exceptions.NotFound): message = _("Resource %(resource)s %(resource_id)s could not be found.") class TagNotFound(exceptions.NotFound): message = _("Tag %(tag)s could not be found.") def validate_tag(tag): msg = validators.validate_string(tag, MAX_TAG_LEN) if msg: raise exceptions.InvalidInput(error_message=msg) def validate_tags(body): if not isinstance(body, dict) or 'tags' not in body: raise exceptions.InvalidInput(error_message=_("Invalid tags body")) msg = validators.validate_list_of_unique_strings(body['tags'], MAX_TAG_LEN) if msg: raise exceptions.InvalidInput(error_message=msg) def notify_tag_action(context, action, parent, parent_id, tags=None): notifier = n_rpc.get_notifier('network') tag_event = 'tag.%s' % action # TODO(hichihara): Add 'updated_at' into payload payload = {'parent_resource': parent, 'parent_resource_id': parent_id} if tags is not None: payload['tags'] = tags notifier.info(context, tag_event, payload) class TaggingController(object): def __init__(self): self.plugin = directory.get_plugin(TAG_PLUGIN_TYPE) self.supported_resources = TAG_SUPPORTED_RESOURCES def _get_parent_resource_and_id(self, kwargs): for key in kwargs: for resource in self.supported_resources: if key == self.supported_resources[resource] + '_id': return resource, kwargs[key] return None, None def index(self, request, **kwargs): # GET /v2.0/networks/{network_id}/tags parent, parent_id = self._get_parent_resource_and_id(kwargs) return self.plugin.get_tags(request.context, parent, parent_id) def show(self, request, id, **kwargs): # GET /v2.0/networks/{network_id}/tags/{tag} # id == tag validate_tag(id) parent, parent_id = self._get_parent_resource_and_id(kwargs) return self.plugin.get_tag(request.context, parent, parent_id, id) def create(self, request, **kwargs): # not supported # POST /v2.0/networks/{network_id}/tags raise webob.exc.HTTPNotFound("not supported") def update(self, request, id, **kwargs): # PUT /v2.0/networks/{network_id}/tags/{tag} # id == tag validate_tag(id) parent, parent_id = self._get_parent_resource_and_id(kwargs) notify_tag_action(request.context, 'create.start', parent, parent_id, [id]) result = self.plugin.update_tag(request.context, parent, parent_id, id) notify_tag_action(request.context, 'create.end', parent, parent_id, [id]) return result def update_all(self, request, body, **kwargs): # PUT /v2.0/networks/{network_id}/tags # body: {"tags": ["aaa", "bbb"]} validate_tags(body) parent, parent_id = self._get_parent_resource_and_id(kwargs) notify_tag_action(request.context, 'update.start', parent, parent_id, body['tags']) result = self.plugin.update_tags(request.context, parent, parent_id, body) notify_tag_action(request.context, 'update.end', parent, parent_id, body['tags']) return result def delete(self, request, id, **kwargs): # DELETE /v2.0/networks/{network_id}/tags/{tag} # id == tag validate_tag(id) parent, parent_id = self._get_parent_resource_and_id(kwargs) notify_tag_action(request.context, 'delete.start', parent, parent_id, [id]) result = self.plugin.delete_tag(request.context, parent, parent_id, id) notify_tag_action(request.context, 'delete.end', parent, parent_id, [id]) return result def delete_all(self, request, **kwargs): # DELETE /v2.0/networks/{network_id}/tags parent, parent_id = self._get_parent_resource_and_id(kwargs) notify_tag_action(request.context, 'delete_all.start', parent, parent_id) result = self.plugin.delete_tags(request.context, parent, parent_id) notify_tag_action(request.context, 'delete_all.end', parent, parent_id) return result class Tagging(api_extensions.ExtensionDescriptor): """Extension class supporting tags.""" @classmethod def get_name(cls): return ("Tag support for resources with standard attribute: %s" % ', '.join(TAG_SUPPORTED_RESOURCES.values())) @classmethod def get_alias(cls): return "standard-attr-tag" @classmethod def get_description(cls): return "Enables to set tag on resources with standard attribute." @classmethod def get_updated(cls): return "2017-01-01T00:00:00-00:00" def get_required_extensions(self): # This is needed so that depending project easily moves from old # extensions although this extension self can run without them. return ['tag', 'tag-ext'] @classmethod def get_resources(cls): """Returns Ext Resources.""" exts = [] action_status = {'index': 200, 'show': 204, 'update': 201, 'update_all': 200, 'delete': 204, 'delete_all': 204} controller = api_resource.Resource(TaggingController(), faults.FAULT_MAP, action_status=action_status) collection_methods = {"delete_all": "DELETE", "update_all": "PUT"} exts = [] for collection_name, member_name in TAG_SUPPORTED_RESOURCES.items(): if 'security_group' in collection_name: collection_name = collection_name.replace('_', '-') parent = {'member_name': member_name, 'collection_name': collection_name} exts.append(extensions.ResourceExtension( TAGS, controller, parent, collection_methods=collection_methods)) return exts def get_extended_resources(self, version): if version != "2.0": return {} EXTENDED_ATTRIBUTES_2_0 = {} for collection_name in TAG_SUPPORTED_RESOURCES: EXTENDED_ATTRIBUTES_2_0[collection_name] = TAG_ATTRIBUTE_MAP return EXTENDED_ATTRIBUTES_2_0 @six.add_metaclass(abc.ABCMeta) class TagPluginBase(service_base.ServicePluginBase): """REST API to operate the Tag.""" def get_plugin_description(self): return "Tag support" @classmethod def get_plugin_type(cls): return TAG_PLUGIN_TYPE @abc.abstractmethod def get_tags(self, context, resource, resource_id): pass @abc.abstractmethod def get_tag(self, context, resource, resource_id, tag): pass @abc.abstractmethod def update_tags(self, context, resource, resource_id, body): pass @abc.abstractmethod def update_tag(self, context, resource, resource_id, tag): pass @abc.abstractmethod def delete_tags(self, context, resource, resource_id): pass @abc.abstractmethod def delete_tag(self, context, resource, resource_id, tag): pass neutron-12.1.1/neutron/extensions/pagination.py0000664000175000017500000000171013553660046021713 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.api.definitions import pagination as apidef from neutron_lib.api import extensions as api_extensions from neutron.api import extensions class Pagination(api_extensions.APIExtensionDescriptor): """Fake extension that indicates that pagination is enabled.""" api_definition = apidef extensions.register_custom_supported_check( apidef.ALIAS, lambda: True, plugin_agnostic=True ) neutron-12.1.1/neutron/extensions/l2_adjacency.py0000664000175000017500000000216513553660046022105 0ustar zuulzuul00000000000000# Copyright (c) 2016 NEC Technologies Ltd. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import l2_adjacency as apidef from neutron_lib.api import extensions class L2_adjacency(extensions.APIExtensionDescriptor): """Extension class supporting L2 Adjacency for Routed Networks The following class is used by neutron's extension framework to provide metadata related to the L2 Adjacency for Neutron Routed Network, exposing the same to clients. No new resources have been defined by this extension. """ api_definition = apidef neutron-12.1.1/neutron/extensions/revisionifmatch.py0000664000175000017500000000242513553660047022761 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.api import extensions as api_extensions class Revisionifmatch(api_extensions.ExtensionDescriptor): """Indicate that If-Match constraints on revision_number are supported.""" @classmethod def get_name(cls): return "If-Match constraints based on revision_number" @classmethod def get_alias(cls): return 'revision-if-match' @classmethod def get_description(cls): return ("Extension indicating that If-Match based on revision_number " "is supported.") @classmethod def get_updated(cls): return "2016-12-11T00:00:00-00:00" @classmethod def get_resources(cls): return [] def get_extended_resources(self, version): return {} neutron-12.1.1/neutron/extensions/trunk_details.py0000664000175000017500000000211313553660046022430 0ustar zuulzuul00000000000000# All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import trunk_details from neutron_lib.api import extensions # NOTE(armax): because of the API machinery, this extension must be on # its own. This aims at providing subport information for ports that # are parent in a trunk so that consumers of the Neutron API, like Nova # can efficiently access trunk information for things like metadata or # config-drive configuration. class Trunk_details(extensions.APIExtensionDescriptor): api_definition = trunk_details neutron-12.1.1/neutron/extensions/network_ip_availability.py0000664000175000017500000000317113553660046024500 0ustar zuulzuul00000000000000# Copyright 2016 GoDaddy. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.api.definitions import network_ip_availability as apidef from neutron_lib.api import extensions as api_extensions import neutron.api.extensions as extensions import neutron.api.v2.base as base import neutron.services.network_ip_availability.plugin as plugin class Network_ip_availability(api_extensions.APIExtensionDescriptor): """Extension class supporting network ip availability information.""" api_definition = apidef @classmethod def get_resources(cls): """Returns Extended Resource for service type management.""" resource_attributes = apidef.RESOURCE_ATTRIBUTE_MAP[ apidef.RESOURCE_PLURAL] controller = base.create_resource( apidef.RESOURCE_PLURAL, apidef.RESOURCE_NAME, plugin.NetworkIPAvailabilityPlugin.get_instance(), resource_attributes) return [extensions.ResourceExtension(apidef.COLLECTION_NAME, controller, attr_map=resource_attributes)] neutron-12.1.1/neutron/extensions/securitygroup.py0000664000175000017500000003323513553660047022516 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import netaddr from neutron_lib.api import converters from neutron_lib.api import extensions as api_extensions from neutron_lib.api import validators from neutron_lib import constants as const from neutron_lib.db import constants as db_const from neutron_lib import exceptions as nexception from neutron_lib.plugins import directory from oslo_utils import netutils import six from neutron._i18n import _ from neutron.api import extensions from neutron.api.v2 import base from neutron.common import exceptions from neutron.conf import quota from neutron.quota import resource_registry # Security group Exceptions class SecurityGroupInvalidPortRange(nexception.InvalidInput): message = _("For TCP/UDP protocols, port_range_min must be " "<= port_range_max") class SecurityGroupInvalidProtocolForPortRange(nexception.InvalidInput): message = _("Port range cannot be specified for protocol %(protocol)s. " "Port range is only supported for " "TCP, UDP, UDPLITE, SCTP and DCCP.") class SecurityGroupInvalidPortValue(nexception.InvalidInput): message = _("Invalid value for port %(port)s") class SecurityGroupInvalidIcmpValue(nexception.InvalidInput): message = _("Invalid value for ICMP %(field)s (%(attr)s) " "%(value)s. It must be 0 to 255.") class SecurityGroupEthertypeConflictWithProtocol(nexception.InvalidInput): message = _("Invalid ethertype %(ethertype)s for protocol " "%(protocol)s.") class SecurityGroupMissingIcmpType(nexception.InvalidInput): message = _("ICMP code (port-range-max) %(value)s is provided" " but ICMP type (port-range-min) is missing.") class SecurityGroupInUse(nexception.InUse): message = _("Security Group %(id)s %(reason)s.") def __init__(self, **kwargs): if 'reason' not in kwargs: kwargs['reason'] = _("in use") super(SecurityGroupInUse, self).__init__(**kwargs) class SecurityGroupCannotRemoveDefault(nexception.InUse): message = _("Insufficient rights for removing default security group.") class SecurityGroupCannotUpdateDefault(nexception.InUse): message = _("Updating default security group not allowed.") class SecurityGroupDefaultAlreadyExists(nexception.InUse): message = _("Default security group already exists.") class SecurityGroupRuleInvalidProtocol(nexception.InvalidInput): message = _("Security group rule protocol %(protocol)s not supported. " "Only protocol values %(values)s and integer representations " "[0 to 255] are supported.") class SecurityGroupRulesNotSingleTenant(nexception.InvalidInput): message = _("Multiple tenant_ids in bulk security group rule create" " not allowed") class SecurityGroupRemoteGroupAndRemoteIpPrefix(nexception.InvalidInput): message = _("Only remote_ip_prefix or remote_group_id may " "be provided.") class SecurityGroupProtocolRequiredWithPorts(nexception.InvalidInput): message = _("Must also specify protocol if port range is given.") class SecurityGroupNotSingleGroupRules(nexception.InvalidInput): message = _("Only allowed to update rules for " "one security profile at a time") class SecurityGroupNotFound(nexception.NotFound): message = _("Security group %(id)s does not exist") class SecurityGroupRuleNotFound(nexception.NotFound): message = _("Security group rule %(id)s does not exist") class DuplicateSecurityGroupRuleInPost(nexception.InUse): message = _("Duplicate Security Group Rule in POST.") class SecurityGroupRuleExists(nexception.InUse): message = _("Security group rule already exists. Rule id is %(rule_id)s.") class SecurityGroupRuleInUse(nexception.InUse): message = _("Security Group Rule %(id)s %(reason)s.") def __init__(self, **kwargs): if 'reason' not in kwargs: kwargs['reason'] = _("in use") super(SecurityGroupRuleInUse, self).__init__(**kwargs) class SecurityGroupRuleParameterConflict(nexception.InvalidInput): message = _("Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s") class SecurityGroupConflict(nexception.Conflict): message = _("Error %(reason)s while attempting the operation.") class SecurityGroupRuleInvalidEtherType(nexception.InvalidInput): message = _("Security group rule for ethertype '%(ethertype)s' not " "supported. Allowed values are %(values)s.") def convert_protocol(value): if value is None: return try: val = int(value) if val >= 0 and val <= 255: # Set value of protocol number to string due to bug 1381379, # PostgreSQL fails when it tries to compare integer with string, # that exists in db. return str(value) raise SecurityGroupRuleInvalidProtocol( protocol=value, values=sg_supported_protocols) except (ValueError, TypeError): if value.lower() in sg_supported_protocols: return value.lower() raise SecurityGroupRuleInvalidProtocol( protocol=value, values=sg_supported_protocols) except AttributeError: raise SecurityGroupRuleInvalidProtocol( protocol=value, values=sg_supported_protocols) def convert_ethertype_to_case_insensitive(value): if isinstance(value, six.string_types): for ethertype in sg_supported_ethertypes: if ethertype.lower() == value.lower(): return ethertype raise SecurityGroupRuleInvalidEtherType( ethertype=value, values=sg_supported_ethertypes) def convert_validate_port_value(port): if port is None: return port if netutils.is_valid_port(port): return int(port) else: raise SecurityGroupInvalidPortValue(port=port) def convert_ip_prefix_to_cidr(ip_prefix): if not ip_prefix: return try: cidr = netaddr.IPNetwork(ip_prefix) return str(cidr) except (ValueError, TypeError, netaddr.AddrFormatError): raise exceptions.InvalidCIDR(input=ip_prefix) def _validate_name_not_default(data, max_len=db_const.NAME_FIELD_SIZE): msg = validators.validate_string(data, max_len) if msg: return msg if data.lower() == "default": raise SecurityGroupDefaultAlreadyExists() validators.add_validator('name_not_default', _validate_name_not_default) sg_supported_protocols = ([None] + list(const.IP_PROTOCOL_MAP.keys())) sg_supported_ethertypes = ['IPv4', 'IPv6'] SECURITYGROUPS = 'security_groups' SECURITYGROUPRULES = 'security_group_rules' # Attribute Map RESOURCE_ATTRIBUTE_MAP = { SECURITYGROUPS: { 'id': {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True, 'primary_key': True}, 'name': {'allow_post': True, 'allow_put': True, 'is_visible': True, 'default': '', 'validate': { 'type:name_not_default': db_const.NAME_FIELD_SIZE}}, 'description': {'allow_post': True, 'allow_put': True, 'validate': { 'type:string': db_const.DESCRIPTION_FIELD_SIZE}, 'is_visible': True, 'default': ''}, 'tenant_id': {'allow_post': True, 'allow_put': False, 'required_by_policy': True, 'validate': { 'type:string': db_const.PROJECT_ID_FIELD_SIZE}, 'is_visible': True}, SECURITYGROUPRULES: {'allow_post': False, 'allow_put': False, 'is_visible': True}, }, SECURITYGROUPRULES: { 'id': {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True, 'primary_key': True}, 'security_group_id': {'allow_post': True, 'allow_put': False, 'is_visible': True, 'required_by_policy': True}, 'remote_group_id': {'allow_post': True, 'allow_put': False, 'default': None, 'is_visible': True}, 'direction': {'allow_post': True, 'allow_put': False, 'is_visible': True, 'validate': {'type:values': ['ingress', 'egress']}}, 'protocol': {'allow_post': True, 'allow_put': False, 'is_visible': True, 'default': None, 'convert_to': convert_protocol}, 'port_range_min': {'allow_post': True, 'allow_put': False, 'convert_to': convert_validate_port_value, 'default': None, 'is_visible': True}, 'port_range_max': {'allow_post': True, 'allow_put': False, 'convert_to': convert_validate_port_value, 'default': None, 'is_visible': True}, 'ethertype': {'allow_post': True, 'allow_put': False, 'is_visible': True, 'default': 'IPv4', 'convert_to': convert_ethertype_to_case_insensitive, 'validate': {'type:values': sg_supported_ethertypes}}, 'remote_ip_prefix': {'allow_post': True, 'allow_put': False, 'default': None, 'is_visible': True, 'convert_to': convert_ip_prefix_to_cidr}, 'tenant_id': {'allow_post': True, 'allow_put': False, 'required_by_policy': True, 'validate': { 'type:string': db_const.PROJECT_ID_FIELD_SIZE}, 'is_visible': True}, } } EXTENDED_ATTRIBUTES_2_0 = { 'ports': {SECURITYGROUPS: {'allow_post': True, 'allow_put': True, 'is_visible': True, 'convert_to': converters.convert_none_to_empty_list, 'validate': {'type:uuid_list': None}, 'default': const.ATTR_NOT_SPECIFIED}}} # Register the configuration options quota.register_quota_opts(quota.security_group_quota_opts) class Securitygroup(api_extensions.ExtensionDescriptor): """Security group extension.""" @classmethod def get_name(cls): return "security-group" @classmethod def get_alias(cls): return "security-group" @classmethod def get_description(cls): return "The security groups extension." @classmethod def get_updated(cls): return "2012-10-05T10:00:00-00:00" @classmethod def get_resources(cls): """Returns Ext Resources.""" exts = [] plugin = directory.get_plugin() for resource_name in ['security_group', 'security_group_rule']: collection_name = resource_name.replace('_', '-') + "s" params = RESOURCE_ATTRIBUTE_MAP.get(resource_name + "s", dict()) resource_registry.register_resource_by_name(resource_name) controller = base.create_resource(collection_name, resource_name, plugin, params, allow_bulk=True, allow_pagination=True, allow_sorting=True) ex = extensions.ResourceExtension(collection_name, controller, attr_map=params) exts.append(ex) return exts def update_attributes_map(self, attributes): super(Securitygroup, self).update_attributes_map( attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP) def get_extended_resources(self, version): if version == "2.0": return dict(list(EXTENDED_ATTRIBUTES_2_0.items()) + list(RESOURCE_ATTRIBUTE_MAP.items())) else: return {} @six.add_metaclass(abc.ABCMeta) class SecurityGroupPluginBase(object): @abc.abstractmethod def create_security_group(self, context, security_group): pass @abc.abstractmethod def update_security_group(self, context, id, security_group): pass @abc.abstractmethod def delete_security_group(self, context, id): pass @abc.abstractmethod def get_security_groups(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass @abc.abstractmethod def get_security_group(self, context, id, fields=None): pass @abc.abstractmethod def create_security_group_rule(self, context, security_group_rule): pass @abc.abstractmethod def delete_security_group_rule(self, context, id): pass @abc.abstractmethod def get_security_group_rules(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass @abc.abstractmethod def get_security_group_rule(self, context, id, fields=None): pass neutron-12.1.1/neutron/extensions/tag_ext.py0000664000175000017500000000700313553660047021217 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import subnet as subnet_def from neutron_lib.api.definitions import subnetpool as subnetpool_def from neutron_lib.api import extensions as api_extensions from neutron_lib.api import faults from neutron_lib.plugins import directory from neutron.api import extensions from neutron.api.v2 import resource as api_resource from neutron.extensions import tagging # This extension is deprecated because tagging supports all resources TAG_SUPPORTED_RESOURCES = { # We shouldn't add new resources here. If more resources need to be tagged, # we must add them in new extension. subnet_def.COLLECTION_NAME: subnet_def.RESOURCE_NAME, port_def.COLLECTION_NAME: port_def.RESOURCE_NAME, subnetpool_def.COLLECTION_NAME: subnetpool_def.RESOURCE_NAME, l3_apidef.ROUTERS: l3_apidef.ROUTER, } class TagExtController(tagging.TaggingController): def __init__(self): self.plugin = directory.get_plugin(tagging.TAG_PLUGIN_TYPE) self.supported_resources = TAG_SUPPORTED_RESOURCES class Tag_ext(api_extensions.ExtensionDescriptor): """Extension class supporting tags for ext resources.""" @classmethod def get_name(cls): return ("Tag support for resources: %s" % ', '.join(TAG_SUPPORTED_RESOURCES.values())) @classmethod def get_alias(cls): return "tag-ext" @classmethod def get_description(cls): return "Extends tag support to more L2 and L3 resources." @classmethod def get_updated(cls): return "2017-01-01T00:00:00-00:00" @classmethod def get_resources(cls): """Returns Ext Resources.""" exts = [] action_status = {'index': 200, 'show': 204, 'update': 201, 'update_all': 200, 'delete': 204, 'delete_all': 204} controller = api_resource.Resource(TagExtController(), faults.FAULT_MAP, action_status=action_status) collection_methods = {"delete_all": "DELETE", "update_all": "PUT"} exts = [] for collection_name, member_name in TAG_SUPPORTED_RESOURCES.items(): parent = {'member_name': member_name, 'collection_name': collection_name} exts.append(extensions.ResourceExtension( tagging.TAGS, controller, parent, collection_methods=collection_methods)) return exts def get_optional_extensions(self): return ['router'] def get_extended_resources(self, version): if version != "2.0": return {} EXTENDED_ATTRIBUTES_2_0 = {} for collection_name in TAG_SUPPORTED_RESOURCES: EXTENDED_ATTRIBUTES_2_0[collection_name] = ( tagging.TAG_ATTRIBUTE_MAP) return EXTENDED_ATTRIBUTES_2_0 neutron-12.1.1/neutron/extensions/sorting.py0000664000175000017500000000251213553660047021251 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.api import extensions as api_extensions from neutron.api import extensions _ALIAS = 'sorting' class Sorting(api_extensions.ExtensionDescriptor): """Fake extension that indicates that sorting is enabled.""" extensions.register_custom_supported_check( _ALIAS, lambda: True, plugin_agnostic=True ) @classmethod def get_name(cls): return "Sorting support" @classmethod def get_alias(cls): return _ALIAS @classmethod def get_description(cls): return "Extension that indicates that sorting is enabled." @classmethod def get_updated(cls): return "2016-06-12T00:00:00-00:00" @classmethod def get_resources(cls): return [] def get_extended_resources(self, version): return {} neutron-12.1.1/neutron/extensions/project_id.py0000664000175000017500000000205313553660046021705 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.api.definitions import project_id as apidef from neutron_lib.api import extensions as api_extensions from neutron.api import extensions class Project_id(api_extensions.APIExtensionDescriptor): """Extension that indicates that project_id is enabled. This extension indicates that the Keystone V3 'project_id' field is supported in the API. """ api_definition = apidef extensions.register_custom_supported_check( apidef.ALIAS, lambda: True, plugin_agnostic=True ) neutron-12.1.1/neutron/extensions/l3.py0000664000175000017500000000570413553660046020107 0ustar zuulzuul00000000000000# Copyright 2012 VMware, Inc. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.api import extensions from neutron_lib.plugins import constants import six from neutron.api.v2 import resource_helper from neutron.conf import quota # Register the configuration options quota.register_quota_opts(quota.l3_quota_opts) class L3(extensions.APIExtensionDescriptor): api_definition = l3_apidef @classmethod def get_resources(cls): """Returns Ext Resources.""" plural_mappings = resource_helper.build_plural_mappings( {}, l3_apidef.RESOURCE_ATTRIBUTE_MAP) return resource_helper.build_resource_info( plural_mappings, l3_apidef.RESOURCE_ATTRIBUTE_MAP, constants.L3, action_map=l3_apidef.ACTION_MAP, register_quota=True) @six.add_metaclass(abc.ABCMeta) class RouterPluginBase(object): @abc.abstractmethod def create_router(self, context, router): pass @abc.abstractmethod def update_router(self, context, id, router): pass @abc.abstractmethod def get_router(self, context, id, fields=None): pass @abc.abstractmethod def delete_router(self, context, id): pass @abc.abstractmethod def get_routers(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass @abc.abstractmethod def add_router_interface(self, context, router_id, interface_info=None): pass @abc.abstractmethod def remove_router_interface(self, context, router_id, interface_info): pass @abc.abstractmethod def create_floatingip(self, context, floatingip): pass @abc.abstractmethod def update_floatingip(self, context, id, floatingip): pass @abc.abstractmethod def get_floatingip(self, context, id, fields=None): pass @abc.abstractmethod def delete_floatingip(self, context, id): pass @abc.abstractmethod def get_floatingips(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass def get_routers_count(self, context, filters=None): raise NotImplementedError() def get_floatingips_count(self, context, filters=None): raise NotImplementedError() neutron-12.1.1/neutron/extensions/dvr.py0000664000175000017500000000265313553660047020365 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.api.definitions import dvr as apidef from neutron_lib.api import extensions from neutron_lib import exceptions import six from neutron._i18n import _ # TODO(boden): consume with I88e1aa2acf22389f69cb7d5704c80a5eb72a9bbe class DVRMacAddressNotFound(exceptions.NotFound): message = _("Distributed Virtual Router Mac Address for " "host %(host)s does not exist.") class Dvr(extensions.APIExtensionDescriptor): """Extension class supporting distributed virtual router.""" api_definition = apidef @six.add_metaclass(abc.ABCMeta) class DVRMacAddressPluginBase(object): @abc.abstractmethod def get_dvr_mac_address_list(self, context): pass @abc.abstractmethod def get_dvr_mac_address_by_host(self, context, host): pass neutron-12.1.1/neutron/extensions/dns_domain_ports.py0000664000175000017500000000157013553660046023130 0ustar zuulzuul00000000000000# Copyright (c) 2017 IBM # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import dns_domain_ports as apidef from neutron_lib.api import extensions class Dns_domain_ports(extensions.APIExtensionDescriptor): """Extension class supporting dns_domain attribute for ports.""" api_definition = apidef neutron-12.1.1/neutron/extensions/multiprovidernet.py0000664000175000017500000001041613553660047023202 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import converters from neutron_lib.api.definitions import provider_net as pnet from neutron_lib.api import extensions from neutron_lib.api import validators from neutron_lib import constants from neutron_lib import exceptions as nexception import webob.exc from neutron._i18n import _ SEGMENTS = 'segments' class SegmentsSetInConjunctionWithProviders(nexception.InvalidInput): message = _("Segments and provider values cannot both be set.") class SegmentsContainDuplicateEntry(nexception.InvalidInput): message = _("Duplicate segment entry in request.") def _convert_and_validate_segments(segments, valid_values=None): for segment in segments: segment.setdefault(pnet.NETWORK_TYPE, constants.ATTR_NOT_SPECIFIED) segment.setdefault(pnet.PHYSICAL_NETWORK, constants.ATTR_NOT_SPECIFIED) segmentation_id = segment.get(pnet.SEGMENTATION_ID) if segmentation_id: segment[pnet.SEGMENTATION_ID] = converters.convert_to_int( segmentation_id) else: segment[pnet.SEGMENTATION_ID] = constants.ATTR_NOT_SPECIFIED if len(segment.keys()) != 3: msg = (_("Unrecognized attribute(s) '%s'") % ', '.join(set(segment.keys()) - set([pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK, pnet.SEGMENTATION_ID]))) raise webob.exc.HTTPBadRequest(msg) def check_duplicate_segments(segments, is_partial_func=None): """Helper function checking duplicate segments. If is_partial_funcs is specified and not None, then SegmentsContainDuplicateEntry is raised if two segments are identical and non partially defined (is_partial_func(segment) == False). Otherwise SegmentsContainDuplicateEntry is raised if two segment are identical. """ if is_partial_func is not None: segments = [s for s in segments if not is_partial_func(s)] fully_specifieds = [tuple(sorted(s.items())) for s in segments] if len(set(fully_specifieds)) != len(fully_specifieds): raise SegmentsContainDuplicateEntry() validators.add_validator('convert_segments', _convert_and_validate_segments) EXTENDED_ATTRIBUTES_2_0 = { 'networks': { SEGMENTS: {'allow_post': True, 'allow_put': True, 'validate': {'type:convert_segments': None}, 'convert_list_to': converters.convert_kvp_list_to_dict, 'default': constants.ATTR_NOT_SPECIFIED, 'enforce_policy': True, 'is_visible': True}, } } class Multiprovidernet(extensions.ExtensionDescriptor): """Extension class supporting multiple provider networks. This class is used by neutron's extension framework to make metadata about the multiple provider network extension available to clients. No new resources are defined by this extension. Instead, the existing network resource's request and response messages are extended with 'segments' attribute. With admin rights, network dictionaries returned will also include 'segments' attribute. """ @classmethod def get_name(cls): return "Multi Provider Network" @classmethod def get_alias(cls): return "multi-provider" @classmethod def get_description(cls): return ("Expose mapping of virtual networks to multiple physical " "networks") @classmethod def get_updated(cls): return "2013-06-27T10:00:00-00:00" def get_extended_resources(self, version): if version == "2.0": return EXTENDED_ATTRIBUTES_2_0 else: return {} neutron-12.1.1/neutron/extensions/network_availability_zone.py0000664000175000017500000000204613553660046025043 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import abc from neutron_lib.api.definitions import network_availability_zone as apidef from neutron_lib.api import extensions import six class Network_availability_zone(extensions.APIExtensionDescriptor): """Network availability zone extension.""" api_definition = apidef @six.add_metaclass(abc.ABCMeta) class NetworkAvailabilityZonePluginBase(object): @abc.abstractmethod def get_network_availability_zones(self, network): """Return availability zones which a network belongs to""" neutron-12.1.1/neutron/extensions/qos.py0000664000175000017500000002273113553660047020373 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat Inc. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import itertools import re from neutron_lib.api.definitions import qos as apidef from neutron_lib.api import extensions as api_extensions from neutron_lib.plugins import constants from neutron_lib.plugins import directory from neutron_lib.services import base as service_base import six from neutron.api import extensions from neutron.api.v2 import base from neutron.api.v2 import resource_helper from neutron.objects.qos import rule as rule_object class Qos(api_extensions.APIExtensionDescriptor): """Quality of Service API extension.""" api_definition = apidef @classmethod def get_plugin_interface(cls): return QoSPluginBase @classmethod def get_resources(cls): """Returns Ext Resources.""" special_mappings = {'policies': 'policy'} plural_mappings = resource_helper.build_plural_mappings( special_mappings, itertools.chain( apidef.RESOURCE_ATTRIBUTE_MAP, apidef.SUB_RESOURCE_ATTRIBUTE_MAP)) resources = resource_helper.build_resource_info( plural_mappings, apidef.RESOURCE_ATTRIBUTE_MAP, constants.QOS, translate_name=True, allow_bulk=True) plugin = directory.get_plugin(constants.QOS) for collection_name in apidef.SUB_RESOURCE_ATTRIBUTE_MAP: resource_name = collection_name[:-1] parent = apidef.SUB_RESOURCE_ATTRIBUTE_MAP[ collection_name].get('parent') params = apidef.SUB_RESOURCE_ATTRIBUTE_MAP[collection_name].get( 'parameters') controller = base.create_resource(collection_name, resource_name, plugin, params, allow_bulk=True, parent=parent, allow_pagination=True, allow_sorting=True) resource = extensions.ResourceExtension( collection_name, controller, parent, path_prefix=apidef.API_PREFIX, attr_map=params) resources.append(resource) return resources def update_attributes_map(self, attributes, extension_attrs_map=None): # TODO(boden): remove with I8ae11633962a48de6e8559b85447b8c8c753d705 super(Qos, self).update_attributes_map( attributes, extension_attrs_map=dict( list(apidef.RESOURCE_ATTRIBUTE_MAP.items()) + list(apidef.SUB_RESOURCE_ATTRIBUTE_MAP.items()))) def get_extended_resources(self, version): # TODO(boden): remove with I8ae11633962a48de6e8559b85447b8c8c753d705 if version == "2.0": return dict(list(apidef.RESOURCE_ATTRIBUTE_MAP.items()) + list(apidef.SUB_RESOURCE_ATTRIBUTE_MAP.items())) else: return {} @six.add_metaclass(abc.ABCMeta) class QoSPluginBase(service_base.ServicePluginBase): path_prefix = apidef.API_PREFIX # The rule object type to use for each incoming rule-related request. rule_objects = {'bandwidth_limit': rule_object.QosBandwidthLimitRule, 'dscp_marking': rule_object.QosDscpMarkingRule, 'minimum_bandwidth': rule_object.QosMinimumBandwidthRule} # Patterns used to call method proxies for all policy-rule-specific # method calls (see __getattr__ docstring, below). qos_rule_method_patterns = [ re.compile( r"^((create|update|delete)_policy_(?P.*)_rule)$"), re.compile( r"^(get_policy_(?P.*)_(rules|rule))$"), ] def __getattr__(self, attrib): """Implement method proxies for all policy-rule-specific requests. For a given request type (such as to update a rule), a single method will handle requests for all rule types. For example, the update_policy_rule method will handle requests for both update_policy_dscp_marking_rule and update_policy_bandwidth_limit_rule. :param attrib: the requested method; in the normal case, this will be, for example, "update_policy_dscp_marking_rule" :type attrib: str """ # Find and call the proxy method that implements the requested one. for pattern in self.qos_rule_method_patterns: res = re.match(pattern, attrib) if res: rule_type = res.group('rule_type') if rule_type in self.rule_objects: # Remove the rule_type value (plus underscore) from attrib # in order to get the proxy method name. So, for instance, # from "delete_policy_dscp_marking_rule" we'll get # "delete_policy_rule". proxy_method = attrib.replace(rule_type + '_', '') rule_cls = self.rule_objects[rule_type] return self._call_proxy_method(proxy_method, rule_cls) # If we got here, then either attrib matched no pattern or the # rule_type embedded in attrib wasn't in self.rule_objects. raise AttributeError(attrib) def _call_proxy_method(self, method_name, rule_cls): """Call proxy method. We need to add the rule_cls, obtained from the self.rule_objects dictionary, to the incoming args. The context is passed to proxy method as first argument; the remaining args will follow rule_cls. Some of the incoming method calls have the policy rule name as one of the keys in the kwargs. For instance, the incoming kwargs for the create_policy_bandwidth_limit_rule take this form: { 'bandwidth_limit_rule': { u'bandwidth_limit_rule': { 'max_burst_kbps': 0, u'max_kbps': u'100', 'tenant_id': u'a8a31c9434ff431cb789c809777505ec'} }, 'policy_id': u'46985da5-9684-402e-b0d7-b7adac909c3a' } We need to generalize this structure for all rule types so will (effectively) rename the rule-specific keyword (e.g., in the above, the first occurrence of 'bandwidth_limit_rule') to be 'rule_data'. :param method_name: the name of the method to call :type method_name: str :param rule_cls: the rule class, which is sent as an argument to the proxy method :type rule_cls: a class from the rule_object (qos.objects.rule) module """ def _make_call(method_name, rule_cls, *args, **kwargs): context = args[0] args_list = list(args[1:]) params = kwargs rule_data_name = rule_cls.rule_type + "_rule" if rule_data_name in params: params['rule_data'] = params.pop(rule_data_name) return getattr(self, method_name)( context, rule_cls, *args_list, **params ) return lambda *args, **kwargs: _make_call( method_name, rule_cls, *args, **kwargs) def get_plugin_description(self): return "QoS Service Plugin for ports and networks" @classmethod def get_plugin_type(cls): return constants.QOS @abc.abstractmethod def get_rule_type(self, context, rule_type_name, fields=None): pass @abc.abstractmethod def get_rule_types(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass @abc.abstractmethod def create_policy(self, context, policy): pass @abc.abstractmethod def update_policy(self, context, policy_id, policy): pass @abc.abstractmethod def delete_policy(self, context, policy_id): pass @abc.abstractmethod def get_policy(self, context, policy_id, fields=None): pass @abc.abstractmethod def get_policies(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass @abc.abstractmethod def create_policy_rule(self, context, rule_cls, policy_id, rule_data): pass @abc.abstractmethod def update_policy_rule(self, context, rule_cls, rule_id, policy_id, rule_data): pass @abc.abstractmethod def delete_policy_rule(self, context, rule_cls, rule_id, policy_id): pass @abc.abstractmethod def get_policy_rule(self, context, rule_cls, rule_id, policy_id, fields=None): pass @abc.abstractmethod def get_policy_rules(self, context, rule_cls, policy_id, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass neutron-12.1.1/neutron/extensions/agent.py0000664000175000017500000000532413553660046020665 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import abc from neutron_lib.api.definitions import agent as apidef from neutron_lib.api import extensions as api_extensions from neutron_lib import exceptions from neutron_lib.plugins import directory import six from neutron.api import extensions from neutron.api.v2 import base class Agent(api_extensions.APIExtensionDescriptor): """Agent management extension.""" api_definition = apidef @classmethod def get_resources(cls): """Returns Ext Resources.""" plugin = directory.get_plugin() params = apidef.RESOURCE_ATTRIBUTE_MAP.get(apidef.COLLECTION_NAME) controller = base.create_resource(apidef.COLLECTION_NAME, apidef.RESOURCE_NAME, plugin, params) ex = extensions.ResourceExtension(apidef.COLLECTION_NAME, controller) return [ex] @six.add_metaclass(abc.ABCMeta) class AgentPluginBase(object): """REST API to operate the Agent. All of method must be in an admin context. """ def create_agent(self, context, agent): """Create agent. This operation is not allow in REST API. @raise exceptions.BadRequest: """ raise exceptions.BadRequest() @abc.abstractmethod def delete_agent(self, context, id): """Delete agent. Agents register themselves on reporting state. But if an agent does not report its status for a long time (for example, it is dead forever. ), admin can remove it. Agents must be disabled before being removed. """ pass @abc.abstractmethod def update_agent(self, context, agent): """Disable or Enable the agent. Description also can be updated. Some agents cannot be disabled, such as plugins, services. An error code should be reported in this case. @raise exceptions.BadRequest: """ pass @abc.abstractmethod def get_agents(self, context, filters=None, fields=None): pass @abc.abstractmethod def get_agent(self, context, id, fields=None): pass neutron-12.1.1/neutron/extensions/trunk.py0000664000175000017500000000240113553660046020723 0ustar zuulzuul00000000000000# Copyright (c) 2016 ZTE Inc. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import trunk from neutron_lib.api import extensions from neutron.api.v2 import resource_helper class Trunk(extensions.APIExtensionDescriptor): """Trunk API extension.""" api_definition = trunk @classmethod def get_resources(cls): """Returns Ext Resources.""" plural_mappings = resource_helper.build_plural_mappings( {}, trunk.RESOURCE_ATTRIBUTE_MAP) return resource_helper.build_resource_info( plural_mappings, trunk.RESOURCE_ATTRIBUTE_MAP, trunk.ALIAS, action_map=trunk.ACTION_MAP, register_quota=True) neutron-12.1.1/neutron/extensions/ip_substring_port_filtering.py0000664000175000017500000000163213553660047025405 0ustar zuulzuul00000000000000# Copyright (c) 2017 Huawei Technology, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import extensions from neutron.extensions import ip_substring_port_filtering_lib as apidef class Ip_substring_port_filtering(extensions.APIExtensionDescriptor): """Extension class supporting IP substring port filtering.""" api_definition = apidef neutron-12.1.1/neutron/extensions/l3_flavors.py0000664000175000017500000000143013553660046021633 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from neutron_lib.api.definitions import l3_flavors as apidef from neutron_lib.api import extensions class L3_flavors(extensions.APIExtensionDescriptor): """Extension class supporting flavors for routers.""" api_definition = apidef neutron-12.1.1/neutron/extensions/routerservicetype.py0000664000175000017500000000303413553660047023367 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import extensions SERVICE_TYPE_ID = 'service_type_id' EXTENDED_ATTRIBUTES_2_0 = { 'routers': { SERVICE_TYPE_ID: {'allow_post': True, 'allow_put': False, 'validate': {'type:uuid_or_none': None}, 'default': None, 'is_visible': True}, } } class Routerservicetype(extensions.ExtensionDescriptor): """Extension class supporting router service type.""" @classmethod def get_name(cls): return "Router Service Type" @classmethod def get_alias(cls): return "router-service-type" @classmethod def get_description(cls): return "Provides router service type" @classmethod def get_updated(cls): return "2013-01-29T00:00:00-00:00" def get_extended_resources(self, version): if version == "2.0": return EXTENDED_ATTRIBUTES_2_0 else: return {} neutron-12.1.1/neutron/extensions/quotasv2_detail.py0000664000175000017500000000640113553660047022673 0ustar zuulzuul00000000000000# Copyright 2017 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import extensions as api_extensions from neutron_lib.api import faults from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from oslo_config import cfg from neutron._i18n import _ from neutron.api import extensions from neutron.api.v2 import resource from neutron.extensions import quotasv2 from neutron.quota import resource_registry DETAIL_QUOTAS_ACTION = 'details' RESOURCE_NAME = 'quota' ALIAS = RESOURCE_NAME + '_' + DETAIL_QUOTAS_ACTION QUOTA_DRIVER = cfg.CONF.QUOTAS.quota_driver RESOURCE_COLLECTION = RESOURCE_NAME + "s" DB_QUOTA_DRIVER = 'neutron.db.quota.driver.DbQuotaDriver' EXTENDED_ATTRIBUTES_2_0 = { RESOURCE_COLLECTION: {} } class DetailQuotaSetsController(quotasv2.QuotaSetsController): def _get_detailed_quotas(self, request, tenant_id): return self._driver.get_detailed_tenant_quotas( request.context, resource_registry.get_all_resources(), tenant_id) def details(self, request, id): if id != request.context.project_id: # Check if admin if not request.context.is_admin: reason = _("Only admin is authorized to access quotas for" " another tenant") raise n_exc.AdminRequired(reason=reason) return {self._resource_name: self._get_detailed_quotas(request, id)} class Quotasv2_detail(api_extensions.ExtensionDescriptor): """Quota details management support.""" # Ensure new extension is not loaded with old conf driver. extensions.register_custom_supported_check( ALIAS, lambda: True if QUOTA_DRIVER == DB_QUOTA_DRIVER else False, plugin_agnostic=True) @classmethod def get_name(cls): return "Quota details management support" @classmethod def get_alias(cls): return ALIAS @classmethod def get_description(cls): return 'Expose functions for quotas usage statistics per project' @classmethod def get_updated(cls): return "2017-02-10T10:00:00-00:00" @classmethod def get_resources(cls): """Returns Extension Resources.""" controller = resource.Resource( DetailQuotaSetsController(directory.get_plugin()), faults=faults.FAULT_MAP) return [extensions.ResourceExtension( RESOURCE_COLLECTION, controller, member_actions={'details': 'GET'}, collection_actions={'tenant': 'GET'})] def get_extended_resources(self, version): return EXTENDED_ATTRIBUTES_2_0 if version == "2.0" else {} def get_required_extensions(self): return ["quotas"] neutron-12.1.1/neutron/extensions/auto_allocated_topology.py0000664000175000017500000000300513553660046024475 0ustar zuulzuul00000000000000# Copyright 2015-2016 Hewlett Packard Enterprise Development Company, LP # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import auto_allocated_topology from neutron_lib.api import extensions as api_extensions from neutron_lib.plugins import directory from neutron.api import extensions from neutron.api.v2 import base class Auto_allocated_topology(api_extensions.APIExtensionDescriptor): api_definition = auto_allocated_topology @classmethod def get_resources(cls): params = auto_allocated_topology.RESOURCE_ATTRIBUTE_MAP.get( auto_allocated_topology.COLLECTION_NAME, dict()) controller = base.create_resource( auto_allocated_topology.COLLECTION_NAME, auto_allocated_topology.ALIAS, directory.get_plugin(auto_allocated_topology.ALIAS), params, allow_bulk=False) return [extensions.ResourceExtension( auto_allocated_topology.ALIAS, controller)] neutron-12.1.1/neutron/extensions/data_plane_status.py0000664000175000017500000000150113553660046023253 0ustar zuulzuul00000000000000# Copyright (c) 2017 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import data_plane_status from neutron_lib.api import extensions class Data_plane_status(extensions.APIExtensionDescriptor): api_definition = data_plane_status neutron-12.1.1/neutron/extensions/default_subnetpools.py0000664000175000017500000000150413553660046023644 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import default_subnetpools as api_def from neutron_lib.api import extensions class Default_subnetpools(extensions.APIExtensionDescriptor): """Extension class supporting default subnetpools.""" api_definition = api_def neutron-12.1.1/neutron/extensions/flavors.py0000664000175000017500000000450513553660046021243 0ustar zuulzuul00000000000000# All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import flavors as apidef from neutron_lib.api import extensions as api_extensions from neutron_lib.plugins import constants from neutron_lib.plugins import directory from neutron.api import extensions from neutron.api.v2 import base from neutron.api.v2 import resource_helper class Flavors(api_extensions.APIExtensionDescriptor): api_definition = apidef @classmethod def get_resources(cls): """Returns Ext Resources.""" plural_mappings = resource_helper.build_plural_mappings( {}, apidef.RESOURCE_ATTRIBUTE_MAP) resources = resource_helper.build_resource_info( plural_mappings, apidef.RESOURCE_ATTRIBUTE_MAP, constants.FLAVORS) plugin = directory.get_plugin(constants.FLAVORS) for collection_name in apidef.SUB_RESOURCE_ATTRIBUTE_MAP: # Special handling needed for sub-resources with 'y' ending # (e.g. proxies -> proxy) resource_name = collection_name[:-1] parent = apidef.SUB_RESOURCE_ATTRIBUTE_MAP[collection_name].get( 'parent') params = apidef.SUB_RESOURCE_ATTRIBUTE_MAP[collection_name].get( 'parameters') controller = base.create_resource(collection_name, resource_name, plugin, params, allow_bulk=True, parent=parent) resource = extensions.ResourceExtension( collection_name, controller, parent, path_prefix=apidef.API_PREFIX, attr_map=params) resources.append(resource) return resources neutron-12.1.1/neutron/extensions/providernet.py0000664000175000017500000000346313553660047022133 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import provider_net from neutron_lib.api import extensions from neutron_lib.api import validators from neutron_lib import exceptions as n_exc from neutron._i18n import _ def _raise_if_updates_provider_attributes(attrs): """Raise exception if provider attributes are present. This method is used for plugins that do not support updating provider networks. """ if any(validators.is_attr_set(attrs.get(a)) for a in provider_net.ATTRIBUTES): msg = _("Plugin does not support updating provider attributes") raise n_exc.InvalidInput(error_message=msg) class Providernet(extensions.APIExtensionDescriptor): """Extension class supporting provider networks. This class is used by neutron's extension framework to make metadata about the provider network extension available to clients. No new resources are defined by this extension. Instead, the existing network resource's request and response messages are extended with attributes in the provider namespace. With admin rights, network dictionaries returned will also include provider attributes. """ api_definition = provider_net neutron-12.1.1/neutron/extensions/netmtu.py0000664000175000017500000000151513553660046021101 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import network_mtu as apidef from neutron_lib.api import extensions class Netmtu(extensions.APIExtensionDescriptor): """Extension class supporting network MTU.""" api_definition = apidef neutron-12.1.1/neutron/extensions/servicetype.py0000664000175000017500000000514713553660047022135 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import extensions as api_extensions from neutron._i18n import _ from neutron.api import extensions from neutron.api.v2 import base from neutron.db import servicetype_db RESOURCE_NAME = "service_provider" COLLECTION_NAME = "%ss" % RESOURCE_NAME SERVICE_ATTR = 'service_type' PLUGIN_ATTR = 'plugin' DRIVER_ATTR = 'driver' EXT_ALIAS = 'service-type' # Attribute Map for Service Provider Resource # Allow read-only access RESOURCE_ATTRIBUTE_MAP = { COLLECTION_NAME: { 'service_type': {'allow_post': False, 'allow_put': False, 'is_visible': True}, 'name': {'allow_post': False, 'allow_put': False, 'is_visible': True}, 'default': {'allow_post': False, 'allow_put': False, 'is_visible': True}, } } class Servicetype(api_extensions.ExtensionDescriptor): @classmethod def get_name(cls): return _("Neutron Service Type Management") @classmethod def get_alias(cls): return EXT_ALIAS @classmethod def get_description(cls): return _("API for retrieving service providers for " "Neutron advanced services") @classmethod def get_updated(cls): return "2013-01-20T00:00:00-00:00" @classmethod def get_resources(cls): """Returns Extended Resource for service type management.""" attr_map = RESOURCE_ATTRIBUTE_MAP[COLLECTION_NAME] collection_name = COLLECTION_NAME.replace('_', '-') controller = base.create_resource( collection_name, RESOURCE_NAME, servicetype_db.ServiceTypeManager.get_instance(), attr_map) return [extensions.ResourceExtension(collection_name, controller, attr_map=attr_map)] def get_extended_resources(self, version): if version == "2.0": return RESOURCE_ATTRIBUTE_MAP else: return {} neutron-12.1.1/neutron/extensions/ip_substring_port_filtering_lib.py0000664000175000017500000000362113553660047026233 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ TODO(hongbin): This module should be deleted once neutron-lib containing https://review.openstack.org/#/c/525284/ change is released. """ # The alias of the extension. ALIAS = 'ip-substring-filtering' # Whether or not this extension is simply signaling behavior to the user # or it actively modifies the attribute map. IS_SHIM_EXTENSION = True # Whether the extension is marking the adoption of standardattr model for # legacy resources, or introducing new standardattr attributes. False or # None if the standardattr model is adopted since the introduction of # resource extension. # If this is True, the alias for the extension should be prefixed with # 'standard-attr-'. IS_STANDARD_ATTR_EXTENSION = False # The name of the extension. NAME = 'IP address substring filtering' # The description of the extension. DESCRIPTION = "Provides IP address substring filtering when listing ports" # A timestamp of when the extension was introduced. UPDATED_TIMESTAMP = "2017-11-28T09:00:00-00:00" # The resource attribute map for the extension. RESOURCE_ATTRIBUTE_MAP = { } # The subresource attribute map for the extension. SUB_RESOURCE_ATTRIBUTE_MAP = { } # The action map. ACTION_MAP = { } # The action status. ACTION_STATUS = { } # The list of required extensions. REQUIRED_EXTENSIONS = [ ] # The list of optional extensions. OPTIONAL_EXTENSIONS = [ ] neutron-12.1.1/neutron/extensions/logging.py0000664000175000017500000000542413553660046021216 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import itertools from neutron_lib.api.definitions import logging as apidef from neutron_lib.api import extensions as api_extensions from neutron_lib.plugins import constants as plugin_const from neutron_lib.services import base as service_base import six from neutron.api.v2 import resource_helper class Logging(api_extensions.APIExtensionDescriptor): """Neutron logging api extension.""" api_definition = apidef @classmethod def get_plugin_interface(cls): return LoggingPluginBase @classmethod def get_resources(cls): """Returns Ext Resources.""" plural_mappings = resource_helper.build_plural_mappings( {}, itertools.chain(apidef.RESOURCE_ATTRIBUTE_MAP)) resources = resource_helper.build_resource_info( plural_mappings, apidef.RESOURCE_ATTRIBUTE_MAP, plugin_const.LOG_API, translate_name=True, allow_bulk=True) return resources @six.add_metaclass(abc.ABCMeta) class LoggingPluginBase(service_base.ServicePluginBase): path_prefix = apidef.API_PREFIX def get_plugin_description(self): return "Logging API Service Plugin" @classmethod def get_plugin_type(cls): return plugin_const.LOG_API @abc.abstractmethod def get_logs(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass @abc.abstractmethod def get_log(self, context, log_id, fields=None): pass @abc.abstractmethod def create_log(self, context, log): pass @abc.abstractmethod def update_log(self, context, log_id, log): pass @abc.abstractmethod def delete_log(self, context, log_id): pass @abc.abstractmethod def get_loggable_resources(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass neutron-12.1.1/neutron/extensions/portbindings.py0000664000175000017500000000210313553660046022261 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib.api import extensions class Portbindings(extensions.APIExtensionDescriptor): """Extension class supporting port bindings. This class is used by neutron's extension framework to make metadata about the port bindings available to external applications. With admin rights one will be able to update and read the values. """ api_definition = portbindings neutron-12.1.1/neutron/extensions/metering.py0000664000175000017500000000672013553660046021402 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.api.definitions import metering as metering_apidef from neutron_lib.api import extensions from neutron_lib.plugins import constants from neutron_lib.services import base as service_base import six from neutron.api.v2 import resource_helper class Metering(extensions.APIExtensionDescriptor): api_definition = metering_apidef @classmethod def get_plugin_interface(cls): return MeteringPluginBase @classmethod def get_resources(cls): """Returns Ext Resources.""" plural_mappings = resource_helper.build_plural_mappings( {}, metering_apidef.RESOURCE_ATTRIBUTE_MAP) # PCM: Metering sets pagination and sorting to True. Do we have cfg # entries for these so can be read? Else, must pass in. return resource_helper.build_resource_info( plural_mappings, metering_apidef.RESOURCE_ATTRIBUTE_MAP, constants.METERING, translate_name=True, allow_bulk=True) @six.add_metaclass(abc.ABCMeta) class MeteringPluginBase(service_base.ServicePluginBase): def get_plugin_description(self): return constants.METERING @classmethod def get_plugin_type(cls): return constants.METERING @abc.abstractmethod def create_metering_label(self, context, metering_label): """Create a metering label.""" pass def update_metering_label(self, context, id, metering_label): """Update a metering label.""" raise NotImplementedError() @abc.abstractmethod def delete_metering_label(self, context, label_id): """Delete a metering label.""" pass @abc.abstractmethod def get_metering_label(self, context, label_id, fields=None): """Get a metering label.""" pass @abc.abstractmethod def get_metering_labels(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """List all metering labels.""" pass @abc.abstractmethod def create_metering_label_rule(self, context, metering_label_rule): """Create a metering label rule.""" pass def update_metering_label_rule(self, context, id, metering_label_rule): """Update a metering label rule.""" raise NotImplementedError() @abc.abstractmethod def get_metering_label_rule(self, context, rule_id, fields=None): """Get a metering label rule.""" pass @abc.abstractmethod def delete_metering_label_rule(self, context, rule_id): """Delete a metering label rule.""" pass @abc.abstractmethod def get_metering_label_rules(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """List all metering label rules.""" pass neutron-12.1.1/neutron/extensions/standardattrdescription.py0000664000175000017500000000334713553660047024532 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import extensions from neutron_lib.db import constants as db_const from neutron.db import standard_attr DESCRIPTION_BODY = { 'description': {'allow_post': True, 'allow_put': True, 'validate': { 'type:string': db_const.DESCRIPTION_FIELD_SIZE}, 'is_visible': True, 'default': ''} } class Standardattrdescription(extensions.ExtensionDescriptor): @classmethod def get_name(cls): return "standard-attr-description" @classmethod def get_alias(cls): return "standard-attr-description" @classmethod def get_description(cls): return "Extension to add descriptions to standard attributes" @classmethod def get_updated(cls): return "2016-02-10T10:00:00-00:00" def get_optional_extensions(self): return ['security-group', 'router'] def get_extended_resources(self, version): if version != "2.0": return {} rs_map = standard_attr.get_standard_attr_resource_model_map() return {resource: DESCRIPTION_BODY for resource in rs_map} neutron-12.1.1/neutron/extensions/dhcpagentscheduler.py0000664000175000017500000001262613553660047023427 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.api import extensions as api_extensions from neutron_lib.api import faults from neutron_lib import constants from neutron_lib import exceptions from neutron_lib.exceptions import agent as agent_exc from neutron_lib.plugins import directory import six from neutron._i18n import _ from neutron.api import extensions from neutron.api.v2 import resource from neutron.common import rpc as n_rpc from neutron import policy from neutron import wsgi DHCP_NET = 'dhcp-network' DHCP_NETS = DHCP_NET + 's' DHCP_AGENT = 'dhcp-agent' DHCP_AGENTS = DHCP_AGENT + 's' class NetworkSchedulerController(wsgi.Controller): def index(self, request, **kwargs): plugin = directory.get_plugin() policy.enforce(request.context, "get_%s" % DHCP_NETS, {}) return plugin.list_networks_on_dhcp_agent( request.context, kwargs['agent_id']) def create(self, request, body, **kwargs): plugin = directory.get_plugin() policy.enforce(request.context, "create_%s" % DHCP_NET, {}) agent_id = kwargs['agent_id'] network_id = body['network_id'] result = plugin.add_network_to_dhcp_agent(request.context, agent_id, network_id) notify(request.context, 'dhcp_agent.network.add', network_id, agent_id) return result def delete(self, request, id, **kwargs): plugin = directory.get_plugin() policy.enforce(request.context, "delete_%s" % DHCP_NET, {}) agent_id = kwargs['agent_id'] result = plugin.remove_network_from_dhcp_agent(request.context, agent_id, id) notify(request.context, 'dhcp_agent.network.remove', id, agent_id) return result class DhcpAgentsHostingNetworkController(wsgi.Controller): def index(self, request, **kwargs): plugin = directory.get_plugin() policy.enforce(request.context, "get_%s" % DHCP_AGENTS, {}) return plugin.list_dhcp_agents_hosting_network( request.context, kwargs['network_id']) class Dhcpagentscheduler(api_extensions.ExtensionDescriptor): """Extension class supporting dhcp agent scheduler. """ @classmethod def get_name(cls): return "DHCP Agent Scheduler" @classmethod def get_alias(cls): return constants.DHCP_AGENT_SCHEDULER_EXT_ALIAS @classmethod def get_description(cls): return "Schedule networks among dhcp agents" @classmethod def get_updated(cls): return "2013-02-07T10:00:00-00:00" @classmethod def get_resources(cls): """Returns Ext Resources.""" exts = [] parent = dict(member_name="agent", collection_name="agents") controller = resource.Resource(NetworkSchedulerController(), faults.FAULT_MAP) exts.append(extensions.ResourceExtension( DHCP_NETS, controller, parent)) parent = dict(member_name="network", collection_name="networks") controller = resource.Resource(DhcpAgentsHostingNetworkController(), faults.FAULT_MAP) exts.append(extensions.ResourceExtension( DHCP_AGENTS, controller, parent)) return exts def get_extended_resources(self, version): return {} class InvalidDHCPAgent(agent_exc.AgentNotFound): message = _("Agent %(id)s is not a valid DHCP Agent or has been disabled") class NetworkHostedByDHCPAgent(exceptions.Conflict): message = _("The network %(network_id)s has been already hosted" " by the DHCP Agent %(agent_id)s.") class NetworkNotHostedByDhcpAgent(exceptions.Conflict): message = _("The network %(network_id)s is not hosted" " by the DHCP agent %(agent_id)s.") @six.add_metaclass(abc.ABCMeta) class DhcpAgentSchedulerPluginBase(object): """REST API to operate the DHCP agent scheduler. All of method must be in an admin context. """ @abc.abstractmethod def add_network_to_dhcp_agent(self, context, id, network_id): pass @abc.abstractmethod def remove_network_from_dhcp_agent(self, context, id, network_id): pass @abc.abstractmethod def list_networks_on_dhcp_agent(self, context, id): pass @abc.abstractmethod def list_dhcp_agents_hosting_network(self, context, network_id): pass def notify(context, action, network_id, agent_id): info = {'id': agent_id, 'network_id': network_id} notifier = n_rpc.get_notifier('network') notifier.info(context, action, {'agent': info}) neutron-12.1.1/neutron/extensions/dns.py0000664000175000017500000000153413553660046020352 0ustar zuulzuul00000000000000# Copyright (c) 2015 Rackspace # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import dns as dns_apidef from neutron_lib.api import extensions class Dns(extensions.APIExtensionDescriptor): """Extension class supporting DNS Integration.""" api_definition = dns_apidef neutron-12.1.1/neutron/extensions/external_net.py0000664000175000017500000000151213553660046022252 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import external_net as extnet_apidef from neutron_lib.api import extensions class External_net(extensions.APIExtensionDescriptor): api_definition = extnet_apidef neutron-12.1.1/neutron/extensions/l3_ext_gw_mode.py0000664000175000017500000000146413553660046022467 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import l3_ext_gw_mode as apidef from neutron_lib.api import extensions class L3_ext_gw_mode(extensions.APIExtensionDescriptor): api_definition = apidef neutron-12.1.1/neutron/extensions/qos_fip.py0000664000175000017500000000321513553660046021224 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import l3 from neutron_lib.api import extensions from neutron_lib.services.qos import constants as qos_consts FIP_QOS_ALIAS = "qos-fip" EXTENDED_ATTRIBUTES_2_0 = { l3.FLOATINGIPS: { qos_consts.QOS_POLICY_ID: { 'allow_post': True, 'allow_put': True, 'is_visible': True, 'default': None, 'validate': {'type:uuid_or_none': None}} } } class Qos_fip(extensions.ExtensionDescriptor): """Extension class supporting floating IP QoS in all router.""" @classmethod def get_name(cls): return "Floating IP QoS" @classmethod def get_alias(cls): return FIP_QOS_ALIAS @classmethod def get_description(cls): return "The floating IP Quality of Service extension" @classmethod def get_updated(cls): return "2017-07-20T00:00:00-00:00" def get_required_extensions(self): return ["router", "qos"] def get_extended_resources(self, version): if version == "2.0": return EXTENDED_ATTRIBUTES_2_0 else: return {} neutron-12.1.1/neutron/extensions/__init__.py0000664000175000017500000000000013553660046021310 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/extensions/vlantransparent.py0000664000175000017500000000477313553660047023021 0ustar zuulzuul00000000000000# Copyright (c) 2015 Cisco Systems, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import converters from neutron_lib.api import extensions from neutron_lib.api import validators from neutron_lib import constants from neutron_lib import exceptions from oslo_config import cfg from oslo_log import log as logging from neutron._i18n import _ LOG = logging.getLogger(__name__) class VlanTransparencyDriverError(exceptions.NeutronException): """Vlan Transparency not supported by all mechanism drivers.""" message = _("Backend does not support VLAN Transparency.") VLANTRANSPARENT = 'vlan_transparent' EXTENDED_ATTRIBUTES_2_0 = { 'networks': { VLANTRANSPARENT: {'allow_post': True, 'allow_put': False, 'convert_to': converters.convert_to_boolean, 'default': constants.ATTR_NOT_SPECIFIED, 'is_visible': True}, }, } def disable_extension_by_config(aliases): if not cfg.CONF.vlan_transparent: if 'vlan-transparent' in aliases: aliases.remove('vlan-transparent') LOG.info('Disabled vlantransparent extension.') def get_vlan_transparent(network): return (network['vlan_transparent'] if ('vlan_transparent' in network and validators.is_attr_set(network['vlan_transparent'])) else False) class Vlantransparent(extensions.ExtensionDescriptor): """Extension class supporting vlan transparent networks.""" @classmethod def get_name(cls): return "Vlantransparent" @classmethod def get_alias(cls): return "vlan-transparent" @classmethod def get_description(cls): return "Provides Vlan Transparent Networks" @classmethod def get_updated(cls): return "2015-03-23T09:00:00-00:00" def get_extended_resources(self, version): if version == "2.0": return EXTENDED_ATTRIBUTES_2_0 else: return {} neutron-12.1.1/neutron/extensions/revisions.py0000664000175000017500000000305413553660047021607 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import extensions from neutron.db import standard_attr REVISION = 'revision_number' REVISION_BODY = { REVISION: {'allow_post': False, 'allow_put': False, 'is_visible': True, 'default': None}, } class Revisions(extensions.ExtensionDescriptor): """Extension to expose revision number of standard attr resources.""" @classmethod def get_name(cls): return "Resource revision numbers" @classmethod def get_alias(cls): return "standard-attr-revisions" @classmethod def get_description(cls): return ("This extension will display the revision number of neutron " "resources.") @classmethod def get_updated(cls): return "2016-04-11T10:00:00-00:00" def get_extended_resources(self, version): if version != "2.0": return {} rs_map = standard_attr.get_standard_attr_resource_model_map() return {resource: REVISION_BODY for resource in rs_map} neutron-12.1.1/neutron/extensions/allowedaddresspairs.py0000664000175000017500000000177113553660046023625 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef from neutron_lib.api import extensions from neutron.conf.extensions import allowedaddresspairs as addr_pair addr_pair.register_allowed_address_pair_opts() class Allowedaddresspairs(extensions.APIExtensionDescriptor): """Extension class supporting allowed address pairs.""" api_definition = addr_apidef neutron-12.1.1/neutron/extensions/router_availability_zone.py0000664000175000017500000000204013553660046024664 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import abc from neutron_lib.api.definitions import router_availability_zone as apidef from neutron_lib.api import extensions import six class Router_availability_zone(extensions.APIExtensionDescriptor): """Router availability zone extension.""" api_definition = apidef @six.add_metaclass(abc.ABCMeta) class RouterAvailabilityZonePluginBase(object): @abc.abstractmethod def get_router_availability_zones(self, router): """Return availability zones which a router belongs to.""" neutron-12.1.1/neutron/extensions/qos_default.py0000664000175000017500000000423113553660047022072 0ustar zuulzuul00000000000000# Copyright (c) 2017 Intel Corporation. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import converters from neutron_lib.api.definitions import qos as qos_apidef from neutron_lib.api import extensions # The alias of the extension. ALIAS = 'qos-default' # The name of the extension. NAME = 'QoS default policy' # The description of the extension. DESCRIPTION = 'Expose the QoS default policy per project' # A timestamp of when the extension was introduced. TIMESTAMP = '2017-041-06T10:00:00-00:00' # The list of required extensions. REQUIRED_EXTENSIONS = [qos_apidef.ALIAS] # The list of optional extensions. OPTIONAL_EXTENSIONS = None # The resource attribute map for the extension. RESOURCE_ATTRIBUTE_MAP = { qos_apidef.POLICIES: { 'is_default': {'allow_post': True, 'allow_put': True, 'default': False, 'convert_to': converters.convert_to_boolean, 'is_visible': True} } } class Qos_default(extensions.ExtensionDescriptor): @classmethod def get_name(cls): return NAME @classmethod def get_alias(cls): return ALIAS @classmethod def get_description(cls): return DESCRIPTION @classmethod def get_updated(cls): return TIMESTAMP def get_required_extensions(self): return REQUIRED_EXTENSIONS or [] def get_optional_extensions(self): return OPTIONAL_EXTENSIONS or [] def get_extended_resources(self, version): if version == "2.0": return RESOURCE_ATTRIBUTE_MAP else: return {} neutron-12.1.1/neutron/extensions/segment.py0000664000175000017500000002154013553660047021230 0ustar zuulzuul00000000000000# Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.api import converters from neutron_lib.api.definitions import provider_net as providernet from neutron_lib.api.definitions import subnet as subnet_def from neutron_lib.api import extensions as api_extensions from neutron_lib import constants from neutron_lib.db import constants as db_const from neutron_lib.plugins import directory import six from neutron.api import extensions from neutron.api.v2 import base SEGMENT = 'segment' SEGMENTS = '%ss' % SEGMENT SEGMENT_ID = 'segment_id' NETWORK_TYPE = 'network_type' PHYSICAL_NETWORK = 'physical_network' SEGMENTATION_ID = 'segmentation_id' NAME_LEN = db_const.NAME_FIELD_SIZE DESC_LEN = db_const.DESCRIPTION_FIELD_SIZE # Attribute Map RESOURCE_ATTRIBUTE_MAP = { SEGMENTS: { 'id': {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True, 'primary_key': True}, 'tenant_id': {'allow_post': True, 'allow_put': False, 'validate': {'type:string': db_const.PROJECT_ID_FIELD_SIZE}, 'is_visible': False}, 'network_id': {'allow_post': True, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True}, PHYSICAL_NETWORK: {'allow_post': True, 'allow_put': False, 'default': constants.ATTR_NOT_SPECIFIED, 'validate': {'type:string': providernet.PHYSICAL_NETWORK_MAX_LEN}, 'is_visible': True}, NETWORK_TYPE: {'allow_post': True, 'allow_put': False, 'validate': {'type:string': providernet.NETWORK_TYPE_MAX_LEN}, 'is_visible': True}, SEGMENTATION_ID: {'allow_post': True, 'allow_put': False, 'default': constants.ATTR_NOT_SPECIFIED, 'convert_to': converters.convert_to_int, 'is_visible': True}, 'name': {'allow_post': True, 'allow_put': True, 'default': constants.ATTR_NOT_SPECIFIED, 'validate': {'type:string_or_none': NAME_LEN}, 'is_visible': True}, 'description': {'allow_post': True, 'allow_put': True, 'default': constants.ATTR_NOT_SPECIFIED, 'validate': {'type:string_or_none': DESC_LEN}, 'is_visible': True}, }, subnet_def.COLLECTION_NAME: { SEGMENT_ID: {'allow_post': True, 'allow_put': False, 'default': None, 'validate': {'type:uuid_or_none': None}, 'is_visible': True, }, }, } class Segment(api_extensions.ExtensionDescriptor): """Extension class supporting Segments.""" @classmethod def get_name(cls): return "Segment" @classmethod def get_alias(cls): return "segment" @classmethod def get_description(cls): return "Segments extension." @classmethod def get_updated(cls): return "2016-02-24T17:00:00-00:00" @classmethod def get_resources(cls): """Returns Extended Resource for service type management.""" resource_attributes = RESOURCE_ATTRIBUTE_MAP[SEGMENTS] controller = base.create_resource( SEGMENTS, SEGMENT, directory.get_plugin(SEGMENTS), resource_attributes) return [extensions.ResourceExtension(SEGMENTS, controller, attr_map=resource_attributes)] def get_extended_resources(self, version): if version == "2.0": return RESOURCE_ATTRIBUTE_MAP else: return {} @six.add_metaclass(abc.ABCMeta) class SegmentPluginBase(object): @abc.abstractmethod def create_segment(self, context, segment): """Create a segment. Create a segment, which represents an L2 segment of a network. :param context: neutron api request context :param segment: dictionary describing the segment, with keys as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/extensions/segment.py`. All keys will be populated. """ @abc.abstractmethod def update_segment(self, context, uuid, segment): """Update values of a segment. :param context: neutron api request context :param uuid: UUID representing the segment to update. :param segment: dictionary with keys indicating fields to update. valid keys are those that have a value of True for 'allow_put' as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/extensions/segment.py`. """ @abc.abstractmethod def get_segment(self, context, uuid, fields=None): """Retrieve a segment. :param context: neutron api request context :param uuid: UUID representing the segment to fetch. :param fields: a list of strings that are valid keys in a segment dictionary as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/extensions/segment.py`. Only these fields will be returned. """ @abc.abstractmethod def get_segments(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Retrieve a list of segments. The contents of the list depends on the identity of the user making the request (as indicated by the context) as well as any filters. :param context: neutron api request context :param filters: a dictionary with keys that are valid keys for a segment as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/extensions/segment.py`. Values in this dictionary are an iterable containing values that will be used for an exact match comparison for that value. Each result returned by this function will have matched one of the values for each key in filters. :param fields: a list of strings that are valid keys in a segment dictionary as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/extensions/segment.py`. Only these fields will be returned. """ @abc.abstractmethod def delete_segment(self, context, uuid): """Delete a segment. :param context: neutron api request context :param uuid: UUID representing the segment to delete. """ @abc.abstractmethod def get_segments_count(self, context, filters=None): """Return the number of segments. The result depends on the identity of the user making the request (as indicated by the context) as well as any filters. :param context: neutron api request context :param filters: a dictionary with keys that are valid keys for a segment as listed in the :obj:`RESOURCE_ATTRIBUTE_MAP` object in :file:`neutron/extensions/segment.py`. Values in this dictionary are an iterable containing values that will be used for an exact match comparison for that value. Each result returned by this function will have matched one of the values for each key in filters. """ def get_plugin_description(self): return "Network Segments" @classmethod def get_plugin_type(cls): return SEGMENTS neutron-12.1.1/neutron/extensions/quotasv2.py0000664000175000017500000001403213553660047021350 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import converters from neutron_lib.api import extensions as api_extensions from neutron_lib.api import faults from neutron_lib.db import constants as const from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import importutils import webob from neutron._i18n import _ from neutron.api import extensions from neutron.api.v2 import base from neutron.api.v2 import resource from neutron.common import exceptions from neutron import quota from neutron.quota import resource_registry from neutron import wsgi DEFAULT_QUOTAS_ACTION = 'default' RESOURCE_NAME = 'quota' RESOURCE_COLLECTION = RESOURCE_NAME + "s" QUOTAS = quota.QUOTAS DB_QUOTA_DRIVER = 'neutron.db.quota.driver.DbQuotaDriver' EXTENDED_ATTRIBUTES_2_0 = { RESOURCE_COLLECTION: {} } class QuotaSetsController(wsgi.Controller): def __init__(self, plugin): self._resource_name = RESOURCE_NAME self._plugin = plugin self._driver = importutils.import_class( cfg.CONF.QUOTAS.quota_driver ) self._update_extended_attributes = True def _update_attributes(self): for quota_resource in resource_registry.get_all_resources().keys(): attr_dict = EXTENDED_ATTRIBUTES_2_0[RESOURCE_COLLECTION] attr_dict[quota_resource] = { 'allow_post': False, 'allow_put': True, 'convert_to': converters.convert_to_int, 'validate': {'type:range': [-1, const.DB_INTEGER_MAX_VALUE]}, 'is_visible': True} self._update_extended_attributes = False def _get_quotas(self, request, tenant_id): return self._driver.get_tenant_quotas( request.context, resource_registry.get_all_resources(), tenant_id) def default(self, request, id): if id != request.context.tenant_id: self._check_admin(request.context, reason=_("Only admin is authorized " "to access quotas for another tenant")) return {self._resource_name: self._driver.get_default_quotas( context=request.context, resources=resource_registry.get_all_resources(), tenant_id=id)} def create(self, request, body=None): msg = _('POST requests are not supported on this resource.') raise webob.exc.HTTPNotImplemented(msg) def index(self, request): context = request.context self._check_admin(context) return {self._resource_name + "s": self._driver.get_all_quotas( context, resource_registry.get_all_resources())} def tenant(self, request): """Retrieve the tenant info in context.""" context = request.context if not context.tenant_id: raise exceptions.QuotaMissingTenant() return {'tenant': {'tenant_id': context.tenant_id}} def show(self, request, id): if id != request.context.tenant_id: self._check_admin(request.context, reason=_("Only admin is authorized " "to access quotas for another tenant")) return {self._resource_name: self._get_quotas(request, id)} def _check_admin(self, context, reason=_("Only admin can view or configure quota")): if not context.is_admin: raise n_exc.AdminRequired(reason=reason) def delete(self, request, id): self._check_admin(request.context) self._driver.delete_tenant_quota(request.context, id) def update(self, request, id, body=None): self._check_admin(request.context) if self._update_extended_attributes: self._update_attributes() body = base.Controller.prepare_request_body( request.context, body, False, self._resource_name, EXTENDED_ATTRIBUTES_2_0[RESOURCE_COLLECTION]) for key, value in body[self._resource_name].items(): self._driver.update_quota_limit(request.context, id, key, value) return {self._resource_name: self._get_quotas(request, id)} class Quotasv2(api_extensions.ExtensionDescriptor): """Quotas management support.""" extensions.register_custom_supported_check( RESOURCE_COLLECTION, lambda: True, plugin_agnostic=True) @classmethod def get_name(cls): return "Quota management support" @classmethod def get_alias(cls): return RESOURCE_COLLECTION @classmethod def get_description(cls): description = 'Expose functions for quotas management' if cfg.CONF.QUOTAS.quota_driver == DB_QUOTA_DRIVER: description += ' per tenant' return description @classmethod def get_updated(cls): return "2012-07-29T10:00:00-00:00" @classmethod def get_resources(cls): """Returns Ext Resources.""" controller = resource.Resource( QuotaSetsController(directory.get_plugin()), faults=faults.FAULT_MAP) return [extensions.ResourceExtension( Quotasv2.get_alias(), controller, member_actions={DEFAULT_QUOTAS_ACTION: 'GET'}, collection_actions={'tenant': 'GET'})] def get_extended_resources(self, version): if version == "2.0": return EXTENDED_ATTRIBUTES_2_0 else: return {} neutron-12.1.1/neutron/extensions/qos_bw_limit_direction.py0000664000175000017500000000447413553660046024324 0ustar zuulzuul00000000000000# Copyright (c) 2017 OVH SAS # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import qos as qos_apidef from neutron_lib.api import extensions as api_extensions from neutron_lib import constants as common_constants # The name of the extension. NAME = "Direction for QoS bandwidth limit rule" # The alias of the extension. ALIAS = "qos-bw-limit-direction" # The description of the extension. DESCRIPTION = ("Allow to configure QoS bandwidth limit rule with specific " "direction: ingress or egress") # The list of required extensions. REQUIRED_EXTENSIONS = [qos_apidef.ALIAS] # The list of optional extensions. OPTIONAL_EXTENSIONS = None # The resource attribute map for the extension. SUB_RESOURCE_ATTRIBUTE_MAP = { qos_apidef.BANDWIDTH_LIMIT_RULES: { 'parameters': { 'direction': { 'allow_post': True, 'allow_put': True, 'is_visible': True, 'default': common_constants.EGRESS_DIRECTION, 'validate': { 'type:values': common_constants.VALID_DIRECTIONS}}} } } class Qos_bw_limit_direction(api_extensions.ExtensionDescriptor): @classmethod def get_name(cls): return NAME @classmethod def get_alias(cls): return ALIAS @classmethod def get_description(cls): return DESCRIPTION @classmethod def get_updated(cls): return "2017-04-10T10:00:00-00:00" def get_required_extensions(self): return REQUIRED_EXTENSIONS or [] def get_optional_extensions(self): return OPTIONAL_EXTENSIONS or [] def get_extended_resources(self, version): if version == "2.0": return SUB_RESOURCE_ATTRIBUTE_MAP else: return {} neutron-12.1.1/neutron/extensions/tag.py0000664000175000017500000000572513553660047020350 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import network from neutron_lib.api import extensions as api_extensions from neutron_lib.api import faults from neutron_lib.plugins import directory from neutron.api import extensions from neutron.api.v2 import resource as api_resource from neutron.extensions import tagging # This extension is deprecated because tagging supports all resources TAG_SUPPORTED_RESOURCES = { # We shouldn't add new resources here. If more resources need to be tagged, # we must add them in new extension. network.COLLECTION_NAME: network.RESOURCE_NAME, } class TagController(tagging.TaggingController): def __init__(self): self.plugin = directory.get_plugin(tagging.TAG_PLUGIN_TYPE) self.supported_resources = TAG_SUPPORTED_RESOURCES class Tag(api_extensions.ExtensionDescriptor): """Extension class supporting tags.""" @classmethod def get_name(cls): return "Tag support" @classmethod def get_alias(cls): return "tag" @classmethod def get_description(cls): return "Enables to set tag on resources." @classmethod def get_updated(cls): return "2016-01-01T00:00:00-00:00" @classmethod def get_resources(cls): """Returns Ext Resources.""" exts = [] action_status = {'index': 200, 'show': 204, 'update': 201, 'update_all': 200, 'delete': 204, 'delete_all': 204} controller = api_resource.Resource(TagController(), faults.FAULT_MAP, action_status=action_status) collection_methods = {"delete_all": "DELETE", "update_all": "PUT"} exts = [] for collection_name, member_name in TAG_SUPPORTED_RESOURCES.items(): parent = {'member_name': member_name, 'collection_name': collection_name} exts.append(extensions.ResourceExtension( tagging.TAGS, controller, parent, collection_methods=collection_methods)) return exts def get_extended_resources(self, version): if version != "2.0": return {} EXTENDED_ATTRIBUTES_2_0 = {} for collection_name in TAG_SUPPORTED_RESOURCES: EXTENDED_ATTRIBUTES_2_0[collection_name] = ( tagging.TAG_ATTRIBUTE_MAP) return EXTENDED_ATTRIBUTES_2_0 neutron-12.1.1/neutron/extensions/portsecurity.py0000664000175000017500000000154013553660046022337 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import port_security from neutron_lib.api import extensions class Portsecurity(extensions.APIExtensionDescriptor): """Extension class supporting port security.""" api_definition = port_security neutron-12.1.1/neutron/extensions/timestamp.py0000664000175000017500000000355613553660047021600 0ustar zuulzuul00000000000000# Copyright 2015 HuaWei Technologies. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import extensions from neutron.db import standard_attr # Attribute Map CREATED = 'created_at' UPDATED = 'updated_at' TIMESTAMP_BODY = { CREATED: {'allow_post': False, 'allow_put': False, 'is_visible': True, 'default': None }, UPDATED: {'allow_post': False, 'allow_put': False, 'is_visible': True, 'default': None }, } class Timestamp(extensions.ExtensionDescriptor): """Extension class supporting timestamp. This class is used by neutron's extension framework for adding timestamp to neutron core resources. """ @classmethod def get_name(cls): return "Resource timestamps" @classmethod def get_alias(cls): return "standard-attr-timestamp" @classmethod def get_description(cls): return ("Adds created_at and updated_at fields to all Neutron " "resources that have Neutron standard attributes.") @classmethod def get_updated(cls): return "2016-09-12T10:00:00-00:00" def get_extended_resources(self, version): if version != "2.0": return {} rs_map = standard_attr.get_standard_attr_resource_model_map() return {resource: TIMESTAMP_BODY for resource in rs_map} neutron-12.1.1/neutron/extensions/rbac.py0000664000175000017500000001136513553660047020501 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import extensions as api_extensions from neutron_lib.db import constants as db_const from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from neutron._i18n import _ from neutron.api import extensions from neutron.api.v2 import base from neutron.conf import quota from neutron.db import rbac_db_models from neutron.quota import resource_registry class RbacPolicyNotFound(n_exc.NotFound): message = _("RBAC policy of type %(object_type)s with ID %(id)s not found") class RbacPolicyInUse(n_exc.Conflict): message = _("RBAC policy on object %(object_id)s cannot be removed " "because other objects depend on it.\nDetails: %(details)s") class DuplicateRbacPolicy(n_exc.Conflict): message = _("An RBAC policy already exists with those values.") def convert_valid_object_type(otype): normalized = otype.strip().lower() if normalized in rbac_db_models.get_type_model_map(): return normalized msg = _("'%s' is not a valid RBAC object type") % otype raise n_exc.InvalidInput(error_message=msg) RESOURCE_NAME = 'rbac_policy' RESOURCE_COLLECTION = 'rbac_policies' RESOURCE_ATTRIBUTE_MAP = { RESOURCE_COLLECTION: { 'id': {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True, 'primary_key': True}, 'object_type': {'allow_post': True, 'allow_put': False, 'convert_to': convert_valid_object_type, 'is_visible': True, 'default': None, 'enforce_policy': True}, 'object_id': {'allow_post': True, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True, 'enforce_policy': True}, 'target_tenant': {'allow_post': True, 'allow_put': True, 'validate': { 'type:string': db_const.PROJECT_ID_FIELD_SIZE}, 'is_visible': True, 'enforce_policy': True}, 'tenant_id': {'allow_post': True, 'allow_put': False, 'validate': { 'type:string': db_const.PROJECT_ID_FIELD_SIZE}, 'required_by_policy': True, 'is_visible': True}, 'action': {'allow_post': True, 'allow_put': False, # action depends on type so validation has to occur in # the extension 'validate': { 'type:string': db_const.DESCRIPTION_FIELD_SIZE}, # we set enforce_policy so operators can define policies # that restrict actions 'is_visible': True, 'enforce_policy': True} } } # Register the configuration options quota.register_quota_opts(quota.rbac_quota_opts) class Rbac(api_extensions.ExtensionDescriptor): """RBAC policy support.""" @classmethod def get_name(cls): return "RBAC Policies" @classmethod def get_alias(cls): return 'rbac-policies' @classmethod def get_description(cls): return ("Allows creation and modification of policies that control " "tenant access to resources.") @classmethod def get_updated(cls): return "2015-06-17T12:15:12-00:00" @classmethod def get_resources(cls): """Returns Ext Resources.""" plugin = directory.get_plugin() params = RESOURCE_ATTRIBUTE_MAP['rbac_policies'] collection_name = 'rbac-policies' resource_name = 'rbac_policy' resource_registry.register_resource_by_name(resource_name) controller = base.create_resource(collection_name, resource_name, plugin, params, allow_bulk=True, allow_pagination=False, allow_sorting=True) return [extensions.ResourceExtension(collection_name, controller, attr_map=params)] def get_extended_resources(self, version): if version == "2.0": return RESOURCE_ATTRIBUTE_MAP return {} neutron-12.1.1/neutron/extensions/extraroute.py0000664000175000017500000000147113553660046021770 0ustar zuulzuul00000000000000# Copyright 2013, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import extraroute as apidef from neutron_lib.api import extensions class Extraroute(extensions.APIExtensionDescriptor): api_definition = apidef neutron-12.1.1/neutron/extensions/subnet_service_types.py0000664000175000017500000000612113553660047024030 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import subnet as subnet_def from neutron_lib.api import extensions from neutron_lib.api import validators from neutron_lib import constants from neutron_lib import exceptions import six import webob.exc from neutron._i18n import _ # List for service plugins to register their own prefixes valid_prefixes = [] class InvalidSubnetServiceType(exceptions.InvalidInput): message = _("Subnet service type %(service_type)s does not correspond " "to a valid device owner.") class InvalidInputSubnetServiceType(exceptions.InvalidInput): message = _("Subnet service type %(service_type)s is not a string.") def _validate_subnet_service_types(service_types, valid_values=None): if service_types: if not isinstance(service_types, list): raise webob.exc.HTTPBadRequest( _("Subnet service types must be a list.")) prefixes = valid_prefixes # Include standard prefixes prefixes += list(constants.DEVICE_OWNER_PREFIXES) prefixes += constants.DEVICE_OWNER_COMPUTE_PREFIX for service_type in service_types: if not isinstance(service_type, six.text_type): raise InvalidInputSubnetServiceType(service_type=service_type) elif not service_type.startswith(tuple(prefixes)): raise InvalidSubnetServiceType(service_type=service_type) validators.add_validator('type:validate_subnet_service_types', _validate_subnet_service_types) EXTENDED_ATTRIBUTES_2_0 = { subnet_def.COLLECTION_NAME: { 'service_types': {'allow_post': True, 'allow_put': True, 'default': constants.ATTR_NOT_SPECIFIED, 'validate': {'type:validate_subnet_service_types': None}, 'is_visible': True, }, }, } class Subnet_service_types(extensions.ExtensionDescriptor): """Extension class supporting subnet service types.""" @classmethod def get_name(cls): return "Subnet service types" @classmethod def get_alias(cls): return "subnet-service-types" @classmethod def get_description(cls): return "Provides ability to set the subnet service_types field" @classmethod def get_updated(cls): return "2016-03-15T18:00:00-00:00" def get_extended_resources(self, version): if version == "2.0": return EXTENDED_ATTRIBUTES_2_0 else: return {} neutron-12.1.1/neutron/extensions/qos_rule_type_details.py0000664000175000017500000000377013553660047024172 0ustar zuulzuul00000000000000# Copyright (c) 2017 OVH SAS # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import qos as qos_apidef from neutron_lib.api import extensions as api_extensions # The name of the extension. NAME = "Details of QoS rule types" # The alias of the extension. ALIAS = "qos-rule-type-details" # The description of the extension. DESCRIPTION = ("Expose details about QoS rule types supported by loaded " "backend drivers") # The list of required extensions. REQUIRED_EXTENSIONS = [qos_apidef.ALIAS] # The list of optional extensions. OPTIONAL_EXTENSIONS = None # The resource attribute map for the extension. RESOURCE_ATTRIBUTE_MAP = { qos_apidef.RULE_TYPES: { 'drivers': {'allow_post': False, 'allow_put': False, 'is_visible': True} } } class Qos_rule_type_details(api_extensions.ExtensionDescriptor): @classmethod def get_name(cls): return NAME @classmethod def get_alias(cls): return ALIAS @classmethod def get_description(cls): return DESCRIPTION @classmethod def get_updated(cls): return "2017-06-22T10:00:00-00:00" def get_required_extensions(self): return REQUIRED_EXTENSIONS or [] def get_optional_extensions(self): return OPTIONAL_EXTENSIONS or [] def get_extended_resources(self, version): if version == "2.0": return RESOURCE_ATTRIBUTE_MAP else: return {} neutron-12.1.1/neutron/extensions/extra_dhcp_opt.py0000664000175000017500000000141113553660046022563 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.api.definitions import extra_dhcp_opt from neutron_lib.api import extensions class Extra_dhcp_opt(extensions.APIExtensionDescriptor): api_definition = extra_dhcp_opt neutron-12.1.1/neutron/extensions/l3_ext_ha_mode.py0000664000175000017500000000153713553660046022443 0ustar zuulzuul00000000000000# Copyright (C) 2014 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import l3_ext_ha_mode as apidef from neutron_lib.api import extensions class L3_ext_ha_mode(extensions.APIExtensionDescriptor): """Extension class supporting virtual router in HA mode.""" api_definition = apidef neutron-12.1.1/neutron/extensions/l3agentscheduler.py0000664000175000017500000001603313553660047023023 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.api import extensions as api_extensions from neutron_lib.api import faults from neutron_lib import constants from neutron_lib import exceptions from neutron_lib.exceptions import agent as agent_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_log import log as logging import six import webob.exc from neutron._i18n import _ from neutron.api import extensions from neutron.api.v2 import resource from neutron.common import rpc as n_rpc from neutron import policy from neutron import wsgi LOG = logging.getLogger(__name__) L3_ROUTER = 'l3-router' L3_ROUTERS = L3_ROUTER + 's' L3_AGENT = 'l3-agent' L3_AGENTS = L3_AGENT + 's' class RouterSchedulerController(wsgi.Controller): def get_plugin(self): plugin = directory.get_plugin(plugin_constants.L3) if not plugin: LOG.error('No plugin for L3 routing registered to handle ' 'router scheduling') msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) return plugin def index(self, request, **kwargs): plugin = self.get_plugin() policy.enforce(request.context, "get_%s" % L3_ROUTERS, {}) return plugin.list_routers_on_l3_agent( request.context, kwargs['agent_id']) def create(self, request, body, **kwargs): plugin = self.get_plugin() policy.enforce(request.context, "create_%s" % L3_ROUTER, {}) agent_id = kwargs['agent_id'] router_id = body['router_id'] result = plugin.add_router_to_l3_agent(request.context, agent_id, router_id) notify(request.context, 'l3_agent.router.add', router_id, agent_id) return result def delete(self, request, id, **kwargs): plugin = self.get_plugin() policy.enforce(request.context, "delete_%s" % L3_ROUTER, {}) agent_id = kwargs['agent_id'] result = plugin.remove_router_from_l3_agent(request.context, agent_id, id) notify(request.context, 'l3_agent.router.remove', id, agent_id) return result class L3AgentsHostingRouterController(wsgi.Controller): def get_plugin(self): plugin = directory.get_plugin(plugin_constants.L3) if not plugin: LOG.error('No plugin for L3 routing registered to handle ' 'router scheduling') msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) return plugin def index(self, request, **kwargs): plugin = self.get_plugin() policy.enforce(request.context, "get_%s" % L3_AGENTS, {}) return plugin.list_l3_agents_hosting_router( request.context, kwargs['router_id']) class L3agentscheduler(api_extensions.ExtensionDescriptor): """Extension class supporting l3 agent scheduler. """ @classmethod def get_name(cls): return "L3 Agent Scheduler" @classmethod def get_alias(cls): return constants.L3_AGENT_SCHEDULER_EXT_ALIAS @classmethod def get_description(cls): return "Schedule routers among l3 agents" @classmethod def get_updated(cls): return "2013-02-07T10:00:00-00:00" @classmethod def get_resources(cls): """Returns Ext Resources.""" exts = [] parent = dict(member_name="agent", collection_name="agents") controller = resource.Resource(RouterSchedulerController(), faults.FAULT_MAP) exts.append(extensions.ResourceExtension( L3_ROUTERS, controller, parent)) parent = dict(member_name="router", collection_name="routers") controller = resource.Resource(L3AgentsHostingRouterController(), faults.FAULT_MAP) exts.append(extensions.ResourceExtension( L3_AGENTS, controller, parent)) return exts def get_extended_resources(self, version): return {} class InvalidL3Agent(agent_exc.AgentNotFound): message = _("Agent %(id)s is not a L3 Agent or has been disabled") class RouterHostedByL3Agent(exceptions.Conflict): message = _("The router %(router_id)s has been already hosted " "by the L3 Agent %(agent_id)s.") class RouterSchedulingFailed(exceptions.Conflict): message = _("Failed scheduling router %(router_id)s to " "the L3 Agent %(agent_id)s.") class RouterReschedulingFailed(exceptions.Conflict): message = _("Failed rescheduling router %(router_id)s: " "no eligible l3 agent found.") class RouterL3AgentMismatch(exceptions.Conflict): message = _("Cannot host distributed router %(router_id)s " "on legacy L3 agent %(agent_id)s.") class DVRL3CannotAssignToDvrAgent(exceptions.Conflict): message = _("Not allowed to manually assign a router to an " "agent in 'dvr' mode.") class DVRL3CannotRemoveFromDvrAgent(exceptions.Conflict): message = _("Not allowed to manually remove a router from " "an agent in 'dvr' mode.") class RouterDoesntSupportScheduling(exceptions.Conflict): message = _("Router %(router_id)s does not support agent scheduling.") @six.add_metaclass(abc.ABCMeta) class L3AgentSchedulerPluginBase(object): """REST API to operate the l3 agent scheduler. All of method must be in an admin context. """ @abc.abstractmethod def add_router_to_l3_agent(self, context, id, router_id): pass @abc.abstractmethod def remove_router_from_l3_agent(self, context, id, router_id): pass @abc.abstractmethod def list_routers_on_l3_agent(self, context, id): pass @abc.abstractmethod def list_l3_agents_hosting_router(self, context, router_id): pass def router_supports_scheduling(self, context, router_id): """Override this method to conditionally schedule routers.""" return True def notify(context, action, router_id, agent_id): info = {'id': agent_id, 'router_id': router_id} notifier = n_rpc.get_notifier('router') notifier.info(context, action, {'agent': info}) neutron-12.1.1/neutron/extensions/address_scope.py0000664000175000017500000000477713553660046022420 0ustar zuulzuul00000000000000# Copyright (c) 2015 Huawei Technologies Co.,LTD. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.api.definitions import address_scope as apidef from neutron_lib.api import extensions as api_extensions from neutron_lib.plugins import directory import six from neutron.api import extensions from neutron.api.v2 import base class Address_scope(api_extensions.APIExtensionDescriptor): """Extension class supporting Address Scopes.""" api_definition = apidef @classmethod def get_resources(cls): """Returns Ext Resources.""" plugin = directory.get_plugin() collection_name = apidef.COLLECTION_NAME.replace('_', '-') params = apidef.RESOURCE_ATTRIBUTE_MAP.get( apidef.COLLECTION_NAME, dict()) controller = base.create_resource(collection_name, apidef.RESOURCE_NAME, plugin, params, allow_bulk=True, allow_pagination=True, allow_sorting=True) ex = extensions.ResourceExtension(collection_name, controller, attr_map=params) return [ex] @six.add_metaclass(abc.ABCMeta) class AddressScopePluginBase(object): @abc.abstractmethod def create_address_scope(self, context, address_scope): pass @abc.abstractmethod def update_address_scope(self, context, id, address_scope): pass @abc.abstractmethod def get_address_scope(self, context, id, fields=None): pass @abc.abstractmethod def get_address_scopes(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass @abc.abstractmethod def delete_address_scope(self, context, id): pass def get_address_scopes_count(self, context, filters=None): raise NotImplementedError() neutron-12.1.1/neutron/extensions/ip_allocation.py0000664000175000017500000000162213553660046022401 0ustar zuulzuul00000000000000# Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import ip_allocation as apidef from neutron_lib.api import extensions class Ip_allocation(extensions.APIExtensionDescriptor): """Extension indicates when ports use deferred or no IP allocation.""" api_definition = apidef neutron-12.1.1/neutron/privileged/0000775000175000017500000000000013553660156017146 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/privileged/agent/0000775000175000017500000000000013553660156020244 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/privileged/agent/linux/0000775000175000017500000000000013553660156021403 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/privileged/agent/linux/netlink_constants.py0000664000175000017500000000537313553660047025524 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Some parts are based on python-conntrack: # Copyright (c) 2009-2011,2015 Andrew Grigorev # # 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. # import socket CONNTRACK = 0 NFCT_O_PLAIN = 0 NFCT_OF_TIME_BIT = 1 NFCT_OF_TIME = 1 << NFCT_OF_TIME_BIT NFCT_Q_DESTROY = 2 NFCT_Q_FLUSH = 4 NFCT_Q_DUMP = 5 NFCT_T_DESTROY_BIT = 2 NFCT_T_DESTROY = 1 << NFCT_T_DESTROY_BIT ATTR_IPV4_SRC = 0 ATTR_IPV4_DST = 1 ATTR_IPV6_SRC = 4 ATTR_IPV6_DST = 5 ATTR_PORT_SRC = 8 ATTR_PORT_DST = 9 ATTR_ICMP_TYPE = 12 ATTR_ICMP_CODE = 13 ATTR_ICMP_ID = 14 ATTR_L3PROTO = 15 ATTR_L4PROTO = 17 ATTR_ZONE = 61 NFCT_T_NEW_BIT = 0 NFCT_T_NEW = 1 << NFCT_T_NEW_BIT NFCT_T_UPDATE_BIT = 1 NFCT_T_UPDATE = 1 << NFCT_T_UPDATE_BIT NFCT_T_DESTROY_BIT = 2 NFCT_T_DESTROY = 1 << NFCT_T_DESTROY_BIT NFCT_T_ALL = NFCT_T_NEW | NFCT_T_UPDATE | NFCT_T_DESTROY NFCT_CB_CONTINUE = 1 NFCT_CB_FAILURE = -1 NFNL_SUBSYS_CTNETLINK = 0 BUFFER = 1024 # IPv6 address memory buffer ADDR_BUFFER_6 = 16 ADDR_BUFFER_4 = 4 IPVERSION_SOCKET = {4: socket.AF_INET, 6: socket.AF_INET6} IPVERSION_BUFFER = {4: ADDR_BUFFER_4, 6: ADDR_BUFFER_6} ENTRY_IS_LOWER = -1 ENTRY_MATCHES = 0 ENTRY_IS_HIGHER = 1 neutron-12.1.1/neutron/privileged/agent/linux/netlink_lib.py0000664000175000017500000002541613553660047024256 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Some parts are based on python-conntrack: # Copyright (c) 2009-2011,2015 Andrew Grigorev # # 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. # import ctypes from ctypes import util import re from neutron_lib import constants from oslo_log import log as logging from neutron._i18n import _ from neutron.common import exceptions from neutron import privileged from neutron.privileged.agent.linux import netlink_constants as nl_constants LOG = logging.getLogger(__name__) nfct = ctypes.CDLL(util.find_library('netfilter_conntrack')) libc = ctypes.CDLL(util.find_library('libc.so.6')) IP_VERSIONS = [constants.IP_VERSION_4, constants.IP_VERSION_6] DATA_CALLBACK = None # position of attribute in raw conntrack entry ATTR_POSITIONS = { 'icmp': [('type', 6), ('code', 7), ('src', 4), ('dst', 5), ('id', 8), ('zone', 16)], 'icmpv6': [('type', 6), ('code', 7), ('src', 4), ('dst', 5), ('id', 8), ('zone', 16)], 'tcp': [('sport', 7), ('dport', 8), ('src', 5), ('dst', 6), ('zone', 15)], 'udp': [('sport', 6), ('dport', 7), ('src', 4), ('dst', 5), ('zone', 14)] } TARGET = {'src': {4: nl_constants.ATTR_IPV4_SRC, 6: nl_constants.ATTR_IPV6_SRC}, 'dst': {4: nl_constants.ATTR_IPV4_DST, 6: nl_constants.ATTR_IPV6_DST}, 'ipversion': {4: nl_constants.ATTR_L3PROTO, 6: nl_constants.ATTR_L3PROTO}, 'protocol': {4: nl_constants.ATTR_L4PROTO, 6: nl_constants.ATTR_L4PROTO}, 'code': {4: nl_constants.ATTR_ICMP_CODE, 6: nl_constants.ATTR_ICMP_CODE}, 'type': {4: nl_constants.ATTR_ICMP_TYPE, 6: nl_constants.ATTR_ICMP_TYPE}, 'id': {4: nl_constants.ATTR_ICMP_ID, 6: nl_constants.ATTR_ICMP_ID}, 'sport': {4: nl_constants.ATTR_PORT_SRC, 6: nl_constants.ATTR_PORT_SRC}, 'dport': {4: nl_constants.ATTR_PORT_DST, 6: nl_constants.ATTR_PORT_DST}, 'zone': {4: nl_constants.ATTR_ZONE, 6: nl_constants.ATTR_ZONE} } NFCT_CALLBACK = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p) class ConntrackManager(object): def __init__(self, family_socket=None): self.family_socket = family_socket self.set_functions = { 'src': {4: nfct.nfct_set_attr, 6: nfct.nfct_set_attr}, 'dst': {4: nfct.nfct_set_attr, 6: nfct.nfct_set_attr}, 'ipversion': {4: nfct.nfct_set_attr_u8, 6: nfct.nfct_set_attr_u8}, 'protocol': {4: nfct.nfct_set_attr_u8, 6: nfct.nfct_set_attr_u8}, 'type': {4: nfct.nfct_set_attr_u8, 6: nfct.nfct_set_attr_u8}, 'code': {4: nfct.nfct_set_attr_u8, 6: nfct.nfct_set_attr_u8}, 'id': {4: nfct.nfct_set_attr_u16, 6: nfct.nfct_set_attr_u16}, 'sport': {4: nfct.nfct_set_attr_u16, 6: nfct.nfct_set_attr_u16}, 'dport': {4: nfct.nfct_set_attr_u16, 6: nfct.nfct_set_attr_u16}, 'zone': {4: nfct.nfct_set_attr_u16, 6: nfct.nfct_set_attr_u16} } self.converters = {'src': str, 'dst': str, 'ipversion': nl_constants.IPVERSION_SOCKET.get, 'protocol': constants.IP_PROTOCOL_MAP.get, 'code': int, 'type': int, 'id': libc.htons, 'sport': libc.htons, 'dport': libc.htons, 'zone': int } def list_entries(self): entries = [] raw_entry = ctypes.create_string_buffer(nl_constants.BUFFER) @NFCT_CALLBACK def callback(type_, conntrack, data): nfct.nfct_snprintf(raw_entry, nl_constants.BUFFER, conntrack, type_, nl_constants.NFCT_O_PLAIN, nl_constants.NFCT_OF_TIME) entries.append(raw_entry.value) return nl_constants.NFCT_CB_CONTINUE self._callback_register(nl_constants.NFCT_T_ALL, callback, DATA_CALLBACK) data_ref = self._get_ref(self.family_socket or nl_constants.IPVERSION_SOCKET[4]) self._query(nl_constants.NFCT_Q_DUMP, data_ref) return entries def delete_entries(self, entries): conntrack = nfct.nfct_new() try: for entry in entries: self._set_attributes(conntrack, entry) self._query(nl_constants.NFCT_Q_DESTROY, conntrack) except Exception as e: msg = _("Failed to delete conntrack entries %s") % e LOG.critical(msg) raise exceptions.CTZoneExhaustedError() finally: nfct.nfct_destroy(conntrack) def _query(self, query_type, query_data): result = nfct.nfct_query(self.conntrack_handler, query_type, query_data) if result == nl_constants.NFCT_CB_FAILURE: LOG.warning("Netlink query failed") def _convert_text_to_binary(self, source, addr_family): dest = ctypes.create_string_buffer( nl_constants.IPVERSION_BUFFER[addr_family]) libc.inet_pton(nl_constants.IPVERSION_SOCKET[addr_family], source, dest) return dest.raw def _set_attributes(self, conntrack, entry): ipversion = entry.get('ipversion', 4) for attr, value in entry.items(): set_function = self.set_functions[attr][ipversion] target = TARGET[attr][ipversion] converter = self.converters[attr] if attr in ['src', 'dst']: # convert src and dst of IPv4 and IPv6 into same format value = self._convert_text_to_binary(value, ipversion) set_function(conntrack, target, converter(value)) def _callback_register(self, message_type, callback_func, data): nfct.nfct_callback_register(self.conntrack_handler, message_type, callback_func, data) def _get_ref(self, data): return ctypes.byref(ctypes.c_int(data)) def __enter__(self): self.conntrack_handler = nfct.nfct_open( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK) if not self.conntrack_handler: msg = _("Failed to open new conntrack handler") LOG.critical(msg) raise exceptions.CTZoneExhaustedError() return self def __exit__(self, *args): nfct.nfct_close(self.conntrack_handler) def _parse_entry(entry, ipversion, zone): """Parse entry from text to Python tuple :param entry: raw conntrack entry :param ipversion: ip version 4 or 6 :return: conntrack entry in Python tuple in format (ipversion, protocol, sport, dport, src_ip, dst_ip, zone) example: (4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2', 1) The attributes are ordered to be easy to compare with other entries and compare with firewall rule """ protocol = entry[1] parsed_entry = [ipversion, protocol] for attr, position in ATTR_POSITIONS[protocol]: val = entry[position].partition('=')[2] try: parsed_entry.append(int(val)) except ValueError: parsed_entry.append(val) parsed_entry[-1] = zone return tuple(parsed_entry) @privileged.default.entrypoint def list_entries(zone): """List and parse all conntrack entries in zone :param zone: zone in which entries belong to :return: sorted list of conntrack entries in Python tuple with sort key is dest port example: [(4, 'icmp', '8', '0', '1.1.1.1', '2.2.2.2', '1234'), (4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2')] """ parsed_entries = [] for ipversion in IP_VERSIONS: with ConntrackManager(nl_constants.IPVERSION_SOCKET[ipversion]) \ as conntrack: raw_entries = [entry for entry in conntrack.list_entries() if re.search(r'\bzone={}\b'.format(zone), entry) is not None] for raw_entry in raw_entries: _entry = raw_entry.split() parsed_entry = _parse_entry(_entry, ipversion, zone) parsed_entries.append(parsed_entry) # sort by dest port return sorted(parsed_entries, key=lambda x: x[3]) @privileged.default.entrypoint def delete_entries(entries): """Delete selected entries :param entries: list of parsed (as tuple) entries to delete :return: None """ entry_args = [] for entry in entries: entry_arg = {'ipversion': entry[0], 'protocol': entry[1]} for idx, attr in enumerate(ATTR_POSITIONS[entry_arg['protocol']]): entry_arg[attr[0]] = entry[idx + 2] entry_args.append(entry_arg) with ConntrackManager() as conntrack: conntrack.delete_entries(entry_args) neutron-12.1.1/neutron/privileged/agent/linux/__init__.py0000664000175000017500000000000013553660046023500 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/privileged/agent/linux/ip_lib.py0000664000175000017500000001577013553660047023224 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import errno import socket import pyroute2 from pyroute2.netlink import rtnl from pyroute2.netlink.rtnl import ndmsg from pyroute2 import NetlinkError from pyroute2 import netns from neutron._i18n import _ from neutron import privileged _IP_VERSION_FAMILY_MAP = {4: socket.AF_INET, 6: socket.AF_INET6} def _get_scope_name(scope): """Return the name of the scope (given as a number), or the scope number if the name is unknown. """ return rtnl.rt_scope.get(scope, scope) class NetworkNamespaceNotFound(RuntimeError): message = _("Network namespace %(netns_name)s could not be found.") def __init__(self, netns_name): super(NetworkNamespaceNotFound, self).__init__( self.message % {'netns_name': netns_name}) class NetworkInterfaceNotFound(RuntimeError): pass @privileged.default.entrypoint def get_routing_table(ip_version, namespace=None): """Return a list of dictionaries, each representing a route. :param ip_version: IP version of routes to return, for example 4 :param namespace: The name of the namespace from which to get the routes :return: a list of dictionaries, each representing a route. The dictionary format is: {'destination': cidr, 'nexthop': ip, 'device': device_name, 'scope': scope} """ family = _IP_VERSION_FAMILY_MAP[ip_version] try: netns = pyroute2.NetNS(namespace, flags=0) if namespace else None except OSError as e: if e.errno == errno.ENOENT: raise NetworkNamespaceNotFound(netns_name=namespace) raise with pyroute2.IPDB(nl=netns) as ipdb: ipdb_routes = ipdb.routes ipdb_interfaces = ipdb.interfaces routes = [{'destination': route['dst'], 'nexthop': route.get('gateway'), 'device': ipdb_interfaces[route['oif']]['ifname'], 'scope': _get_scope_name(route['scope'])} for route in ipdb_routes if route['family'] == family] return routes def _get_iproute(namespace): # From iproute.py: # `IPRoute` -- RTNL API to the current network namespace # `NetNS` -- RTNL API to another network namespace if namespace: # do not try and create the namespace return pyroute2.NetNS(namespace, flags=0) else: return pyroute2.IPRoute() def _run_iproute(command, device, namespace, **kwargs): try: with _get_iproute(namespace) as ip: idx = ip.link_lookup(ifname=device)[0] return ip.neigh(command, ifindex=idx, **kwargs) except IndexError: msg = _("Network interface %(device)s not found in namespace " "%(namespace)s.") % {'device': device, 'namespace': namespace} raise NetworkInterfaceNotFound(msg) except OSError as e: if e.errno == errno.ENOENT: raise NetworkNamespaceNotFound(netns_name=namespace) raise @privileged.default.entrypoint def open_namespace(namespace): """Open namespace to test if the namespace is ready to be manipulated""" with pyroute2.NetNS(namespace, flags=0): pass @privileged.default.entrypoint def add_neigh_entry(ip_version, ip_address, mac_address, device, namespace, **kwargs): """Add a neighbour entry. :param ip_address: IP address of entry to add :param mac_address: MAC address of entry to add :param device: Device name to use in adding entry :param namespace: The name of the namespace in which to add the entry """ family = _IP_VERSION_FAMILY_MAP[ip_version] _run_iproute('replace', device, namespace, dst=ip_address, lladdr=mac_address, family=family, state=ndmsg.states['permanent'], **kwargs) @privileged.default.entrypoint def delete_neigh_entry(ip_version, ip_address, mac_address, device, namespace, **kwargs): """Delete a neighbour entry. :param ip_address: IP address of entry to delete :param mac_address: MAC address of entry to delete :param device: Device name to use in deleting entry :param namespace: The name of the namespace in which to delete the entry """ family = _IP_VERSION_FAMILY_MAP[ip_version] try: _run_iproute('delete', device, namespace, dst=ip_address, lladdr=mac_address, family=family, **kwargs) except NetlinkError as e: # trying to delete a non-existent entry shouldn't raise an error if e.code == errno.ENOENT: return raise @privileged.default.entrypoint def dump_neigh_entries(ip_version, device, namespace, **kwargs): """Dump all neighbour entries. :param ip_version: IP version of entries to show (4 or 6) :param device: Device name to use in dumping entries :param namespace: The name of the namespace in which to dump the entries :param kwargs: Callers add any filters they use as kwargs :return: a list of dictionaries, each representing a neighbour. The dictionary format is: {'dst': ip_address, 'lladdr': mac_address, 'device': device_name} """ family = _IP_VERSION_FAMILY_MAP[ip_version] entries = [] dump = _run_iproute('dump', device, namespace, family=family, **kwargs) for entry in dump: attrs = dict(entry['attrs']) entries += [{'dst': attrs['NDA_DST'], 'lladdr': attrs.get('NDA_LLADDR'), 'device': device}] return entries @privileged.default.entrypoint def create_netns(name, **kwargs): """Create a network namespace. :param name: The name of the namespace to create """ try: netns.create(name, **kwargs) except OSError as e: if e.errno != errno.EEXIST: raise @privileged.default.entrypoint def remove_netns(name, **kwargs): """Remove a network namespace. :param name: The name of the namespace to remove """ netns.remove(name, **kwargs) @privileged.default.entrypoint def list_netns(**kwargs): """List network namespaces. Caller requires raised priveleges to list namespaces """ return netns.listnetns(**kwargs) neutron-12.1.1/neutron/privileged/agent/__init__.py0000664000175000017500000000000013553660046022341 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/privileged/__init__.py0000664000175000017500000000232113553660047021254 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_privsep import capabilities as caps from oslo_privsep import priv_context # It is expected that most (if not all) neutron operations can be # executed with these privileges. default = priv_context.PrivContext( __name__, cfg_section='privsep', pypath=__name__ + '.default', # TODO(gus): CAP_SYS_ADMIN is required (only?) for manipulating # network namespaces. SYS_ADMIN is a lot of scary powers, so # consider breaking this out into a separate minimal context. capabilities=[caps.CAP_SYS_ADMIN, caps.CAP_NET_ADMIN, caps.CAP_DAC_OVERRIDE, caps.CAP_DAC_READ_SEARCH], ) neutron-12.1.1/neutron/notifiers/0000775000175000017500000000000013553660156017016 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/notifiers/nova.py0000664000175000017500000002504513553660047020340 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneauth1 import loading as ks_loading from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions as exc from neutron_lib.plugins import directory from novaclient import api_versions from novaclient import client as nova_client from novaclient import exceptions as nova_exceptions from oslo_config import cfg from oslo_log import log as logging from oslo_utils import uuidutils from sqlalchemy.orm import attributes as sql_attr from neutron.notifiers import batch_notifier LOG = logging.getLogger(__name__) VIF_UNPLUGGED = 'network-vif-unplugged' VIF_PLUGGED = 'network-vif-plugged' VIF_DELETED = 'network-vif-deleted' NEUTRON_NOVA_EVENT_STATUS_MAP = {constants.PORT_STATUS_ACTIVE: 'completed', constants.PORT_STATUS_ERROR: 'failed', constants.PORT_STATUS_DOWN: 'completed'} NOVA_API_VERSION = "2.1" @registry.has_registry_receivers class Notifier(object): _instance = None @classmethod def get_instance(cls): if cls._instance is None: cls._instance = cls() return cls._instance def __init__(self): auth = ks_loading.load_auth_from_conf_options(cfg.CONF, 'nova') session = ks_loading.load_session_from_conf_options( cfg.CONF, 'nova', auth=auth) extensions = [ ext for ext in nova_client.discover_extensions(NOVA_API_VERSION) if ext.name == "server_external_events"] self.nclient = nova_client.Client( api_versions.APIVersion(NOVA_API_VERSION), session=session, region_name=cfg.CONF.nova.region_name, endpoint_type=cfg.CONF.nova.endpoint_type, extensions=extensions) self.batch_notifier = batch_notifier.BatchNotifier( cfg.CONF.send_events_interval, self.send_events) def _is_compute_port(self, port): try: if (port['device_id'] and uuidutils.is_uuid_like(port['device_id']) and port['device_owner'].startswith( constants.DEVICE_OWNER_COMPUTE_PREFIX)): return True except (KeyError, AttributeError): pass return False def _get_network_changed_event(self, port): return {'name': 'network-changed', 'server_uuid': port['device_id'], 'tag': port['id']} def _get_port_delete_event(self, port): return {'server_uuid': port['device_id'], 'name': VIF_DELETED, 'tag': port['id']} @registry.receives(resources.PORT, [events.BEFORE_RESPONSE]) @registry.receives(resources.FLOATING_IP, [events.BEFORE_RESPONSE]) def _send_nova_notification(self, resource, event, trigger, payload=None): self.send_network_change(payload.action, payload.states[0], payload.latest_state) def send_network_change(self, action, original_obj, returned_obj): """Called when a network change is made that nova cares about. :param action: the event that occurred. :param original_obj: the previous value of resource before action. :param returned_obj: the body returned to client as result of action. """ if not cfg.CONF.notify_nova_on_port_data_changes: return # When neutron re-assigns floating ip from an original instance # port to a new instance port without disassociate it first, an # event should be sent for original instance, that will make nova # know original instance's info, and update database for it. if (action == 'update_floatingip' and returned_obj['floatingip'].get('port_id') and original_obj.get('port_id')): disassociate_returned_obj = {'floatingip': {'port_id': None}} event = self.create_port_changed_event(action, original_obj, disassociate_returned_obj) self.batch_notifier.queue_event(event) event = self.create_port_changed_event(action, original_obj, returned_obj) self.batch_notifier.queue_event(event) def create_port_changed_event(self, action, original_obj, returned_obj): port = None if action in ['update_port', 'delete_port']: port = returned_obj['port'] elif action in ['update_floatingip', 'create_floatingip', 'delete_floatingip']: # NOTE(arosen) if we are associating a floatingip the # port_id is in the returned_obj. Otherwise on disassociate # it's in the original_object port_id = (returned_obj['floatingip'].get('port_id') or original_obj.get('port_id')) if port_id is None: return ctx = context.get_admin_context() try: port = directory.get_plugin().get_port(ctx, port_id) except exc.PortNotFound: LOG.debug("Port %s was deleted, no need to send any " "notification", port_id) return if port and self._is_compute_port(port): if action == 'delete_port': return self._get_port_delete_event(port) else: return self._get_network_changed_event(port) def _can_notify(self, port): if not port.id: LOG.warning("Port ID not set! Nova will not be notified of " "port status change.") return False # If there is no device_id set there is nothing we can do here. if not port.device_id: LOG.debug("device_id is not set on port %s yet.", port.id) return False # We only want to notify about nova ports. if not self._is_compute_port(port): return False return True def record_port_status_changed(self, port, current_port_status, previous_port_status, initiator): """Determine if nova needs to be notified due to port status change. """ # clear out previous _notify_event port._notify_event = None if not self._can_notify(port): return # We notify nova when a vif is unplugged which only occurs when # the status goes from ACTIVE to DOWN. if (previous_port_status == constants.PORT_STATUS_ACTIVE and current_port_status == constants.PORT_STATUS_DOWN): event_name = VIF_UNPLUGGED # We only notify nova when a vif is plugged which only occurs # when the status goes from: # NO_VALUE/DOWN/BUILD -> ACTIVE/ERROR. elif (previous_port_status in [sql_attr.NO_VALUE, constants.PORT_STATUS_DOWN, constants.PORT_STATUS_BUILD] and current_port_status in [constants.PORT_STATUS_ACTIVE, constants.PORT_STATUS_ERROR]): event_name = VIF_PLUGGED # All the remaining state transitions are of no interest to nova else: LOG.debug("Ignoring state change previous_port_status: " "%(pre_status)s current_port_status: %(cur_status)s" " port_id %(id)s", {'pre_status': previous_port_status, 'cur_status': current_port_status, 'id': port.id}) return port._notify_event = ( {'server_uuid': port.device_id, 'name': event_name, 'status': NEUTRON_NOVA_EVENT_STATUS_MAP.get(current_port_status), 'tag': port.id}) def send_port_status(self, mapper, connection, port): event = getattr(port, "_notify_event", None) self.batch_notifier.queue_event(event) port._notify_event = None def notify_port_active_direct(self, port): """Notify nova about active port Used when port was wired on the host other than port's current host according to port binding. This happens during live migration. In this case ml2 plugin skips port status update but we still we need to notify nova. """ if not self._can_notify(port): return port._notify_event = ( {'server_uuid': port.device_id, 'name': VIF_PLUGGED, 'status': 'completed', 'tag': port.id}) self.send_port_status(None, None, port) def send_events(self, batched_events): LOG.debug("Sending events: %s", batched_events) try: response = self.nclient.server_external_events.create( batched_events) except nova_exceptions.NotFound: LOG.debug("Nova returned NotFound for event: %s", batched_events) except Exception: LOG.exception("Failed to notify nova on events: %s", batched_events) else: if not isinstance(response, list): LOG.error("Error response returned from nova: %s", response) return response_error = False for event in response: try: code = event['code'] except KeyError: response_error = True continue if code != 200: LOG.warning("Nova event: %s returned with failed " "status", event) else: LOG.info("Nova event response: %s", event) if response_error: LOG.error("Error response returned from nova: %s", response) neutron-12.1.1/neutron/notifiers/batch_notifier.py0000664000175000017500000000454313553660047022355 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import eventlet from neutron_lib.utils import runtime from oslo_utils import uuidutils class BatchNotifier(object): def __init__(self, batch_interval, callback): self.pending_events = [] self.callback = callback self.batch_interval = batch_interval self._lock_identifier = 'notifier-%s' % uuidutils.generate_uuid() def queue_event(self, event): """Called to queue sending an event with the next batch of events. Sending events individually, as they occur, has been problematic as it can result in a flood of sends. Previously, there was a loopingcall thread that would send batched events on a periodic interval. However, maintaining a persistent thread in the loopingcall was also problematic. This replaces the loopingcall with a mechanism that creates a short-lived thread on demand whenever an event is queued. That thread will wait for a lock, send all queued events and then sleep for 'batch_interval' seconds to allow other events to queue up. This effectively acts as a rate limiter to only allow 1 batch per 'batch_interval' seconds. :param event: the event that occurred. """ if not event: return self.pending_events.append(event) @runtime.synchronized(self._lock_identifier) def synced_send(): self._notify() # sleeping after send while holding the lock allows subsequent # events to batch up eventlet.sleep(self.batch_interval) eventlet.spawn_n(synced_send) def _notify(self): if not self.pending_events: return batched_events = self.pending_events self.pending_events = [] self.callback(batched_events) neutron-12.1.1/neutron/notifiers/__init__.py0000664000175000017500000000000013553660046021113 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/version.py0000664000175000017500000000125613553660046017055 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pbr.version version_info = pbr.version.VersionInfo('neutron') neutron-12.1.1/neutron/cmd/0000775000175000017500000000000013553660156015557 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/cmd/sanity/0000775000175000017500000000000013553660156017066 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/cmd/sanity/__init__.py0000664000175000017500000000000013553660046021163 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/cmd/sanity/checks.py0000664000175000017500000004160413553660047020704 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import re import shutil import tempfile import netaddr from neutron_lib import constants as n_consts from oslo_config import cfg from oslo_log import log as logging from oslo_utils import uuidutils from neutron.agent.common import ovs_lib from neutron.agent.l3 import ha_router from neutron.agent.l3 import namespaces from neutron.agent.linux import external_process from neutron.agent.linux import ip_lib from neutron.agent.linux import ip_link_support from neutron.agent.linux import keepalived from neutron.agent.linux import utils as agent_utils from neutron.cmd import runtime_checks from neutron.common import constants from neutron.common import utils as common_utils from neutron.plugins.ml2.drivers.openvswitch.agent.common \ import constants as ovs_const LOG = logging.getLogger(__name__) MINIMUM_DNSMASQ_VERSION = 2.67 DNSMASQ_VERSION_DHCP_RELEASE6 = 2.76 MINIMUM_DIBBLER_VERSION = '1.0.1' def ovs_vxlan_supported(from_ip='192.0.2.1', to_ip='192.0.2.2'): name = common_utils.get_rand_device_name(prefix='vxlantest-') with ovs_lib.OVSBridge(name) as br: port = br.add_tunnel_port(from_ip, to_ip, n_consts.TYPE_VXLAN) return port != ovs_lib.INVALID_OFPORT def ovs_geneve_supported(from_ip='192.0.2.3', to_ip='192.0.2.4'): name = common_utils.get_rand_device_name(prefix='genevetest-') with ovs_lib.OVSBridge(name) as br: port = br.add_tunnel_port(from_ip, to_ip, n_consts.TYPE_GENEVE) return port != ovs_lib.INVALID_OFPORT def iproute2_vxlan_supported(): ip = ip_lib.IPWrapper() name = common_utils.get_rand_device_name(prefix='vxlantest-') port = ip.add_vxlan(name, 3000) ip.del_veth(name) return name == port.name def patch_supported(): name, peer_name, patch_name = common_utils.get_related_rand_device_names( ['patchtest-', 'peertest0-', 'peertest1-']) with ovs_lib.OVSBridge(name) as br: port = br.add_patch_port(patch_name, peer_name) return port != ovs_lib.INVALID_OFPORT def nova_notify_supported(): try: import neutron.notifiers.nova # noqa since unused return True except ImportError: return False def ofctl_arg_supported(cmd, **kwargs): """Verify if ovs-ofctl binary supports cmd with **kwargs. :param cmd: ovs-ofctl command to use for test. :param **kwargs: arguments to test with the command. :returns: a boolean if the supplied arguments are supported. """ br_name = common_utils.get_rand_device_name(prefix='br-test-') with ovs_lib.OVSBridge(br_name) as test_br: full_args = ["ovs-ofctl", cmd, test_br.br_name, ovs_lib._build_flow_expr_str(kwargs, cmd.split('-')[0], False)] try: agent_utils.execute(full_args, run_as_root=True) except RuntimeError as e: LOG.debug("Exception while checking supported feature via " "command %s. Exception: %s", full_args, e) return False except Exception: LOG.exception("Unexpected exception while checking supported" " feature via command: %s", full_args) return False else: return True def arp_responder_supported(): mac = netaddr.EUI('dead:1234:beef', dialect=netaddr.mac_unix) ip = netaddr.IPAddress('240.0.0.1') actions = ovs_const.ARP_RESPONDER_ACTIONS % {'mac': mac, 'ip': ip} return ofctl_arg_supported(cmd='add-flow', table=21, priority=1, proto='arp', dl_vlan=42, nw_dst='%s' % ip, actions=actions) def arp_header_match_supported(): return ofctl_arg_supported(cmd='add-flow', table=24, priority=1, proto='arp', arp_op='0x2', arp_spa='1.1.1.1', actions="NORMAL") def icmpv6_header_match_supported(): return ofctl_arg_supported(cmd='add-flow', table=ovs_const.ARP_SPOOF_TABLE, priority=1, dl_type=constants.ETHERTYPE_IPV6, nw_proto=n_consts.PROTO_NUM_IPV6_ICMP, icmp_type=n_consts.ICMPV6_TYPE_NA, nd_target='fdf8:f53b:82e4::10', actions="NORMAL") def _vf_management_support(required_caps): is_supported = True try: vf_section = ip_link_support.IpLinkSupport.get_vf_mgmt_section() for cap in required_caps: if not ip_link_support.IpLinkSupport.vf_mgmt_capability_supported( vf_section, cap): is_supported = False LOG.debug("ip link command does not support " "vf capability '%(cap)s'", {'cap': cap}) except ip_link_support.UnsupportedIpLinkCommand: LOG.exception("Unexpected exception while checking supported " "ip link command") return False return is_supported def vf_management_supported(): required_caps = ( ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_STATE, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_SPOOFCHK, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE) return _vf_management_support(required_caps) def vf_extended_management_supported(): required_caps = ( ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_STATE, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_SPOOFCHK, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE, ) return _vf_management_support(required_caps) def netns_read_requires_helper(): nsname = "netnsreadtest-" + uuidutils.generate_uuid() ip_lib.create_network_namespace(nsname) try: # read without root_helper. if exists, not required. exists = ip_lib.network_namespace_exists(nsname) finally: ip_lib.delete_network_namespace(nsname) return not exists def get_minimal_dnsmasq_version_supported(): return MINIMUM_DNSMASQ_VERSION def get_dnsmasq_version_with_dhcp_release6(): return DNSMASQ_VERSION_DHCP_RELEASE6 def dnsmasq_local_service_supported(): cmd = ['dnsmasq', '--test', '--local-service'] env = {'LC_ALL': 'C'} obj, cmd = agent_utils.create_process(cmd, addl_env=env) _stdout, _stderr = obj.communicate() returncode = obj.returncode if returncode == 127: LOG.debug("Exception while checking dnsmasq version. " "dnsmasq: No such file or directory") return False elif returncode == 1: return False return True def dnsmasq_version_supported(): try: cmd = ['dnsmasq', '--version'] env = {'LC_ALL': 'C'} out = agent_utils.execute(cmd, addl_env=env) m = re.search(r"version (\d+\.\d+)", out) ver = float(m.group(1)) if m else 0 if ver < MINIMUM_DNSMASQ_VERSION: return False except (OSError, RuntimeError, IndexError, ValueError) as e: LOG.debug("Exception while checking minimal dnsmasq version. " "Exception: %s", e) return False return True def dhcp_release6_supported(): return runtime_checks.dhcp_release6_supported() def bridge_firewalling_enabled(): for proto in ('arp', 'ip', 'ip6'): knob = 'net.bridge.bridge-nf-call-%stables' % proto cmd = ['sysctl', '-b', knob] try: out = agent_utils.execute(cmd) except (OSError, RuntimeError, IndexError, ValueError) as e: LOG.debug("Exception while extracting %(knob)s. " "Exception: %(e)s", {'knob': knob, 'e': e}) return False if out == '0': return False return True class KeepalivedIPv6Test(object): def __init__(self, ha_port, gw_port, gw_vip, default_gw): self.ha_port = ha_port self.gw_port = gw_port self.gw_vip = gw_vip self.default_gw = default_gw self.manager = None self.config = None self.config_path = None self.nsname = "keepalivedtest-" + uuidutils.generate_uuid() self.pm = None self.orig_interval = cfg.CONF.AGENT.check_child_processes_interval def configure(self): config = keepalived.KeepalivedConf() instance1 = keepalived.KeepalivedInstance('MASTER', self.ha_port, 1, ['169.254.192.0/18'], advert_int=5) instance1.track_interfaces.append(self.ha_port) # Configure keepalived with an IPv6 address (gw_vip) on gw_port. vip_addr1 = keepalived.KeepalivedVipAddress(self.gw_vip, self.gw_port) instance1.vips.append(vip_addr1) # Configure keepalived with an IPv6 default route on gw_port. gateway_route = keepalived.KeepalivedVirtualRoute(n_consts.IPv6_ANY, self.default_gw, self.gw_port) instance1.virtual_routes.gateway_routes = [gateway_route] config.add_instance(instance1) self.config = config def start_keepalived_process(self): # Disable process monitoring for Keepalived process. cfg.CONF.set_override('check_child_processes_interval', 0, 'AGENT') self.pm = external_process.ProcessMonitor(cfg.CONF, 'router') # Create a temp directory to store keepalived configuration. self.config_path = tempfile.mkdtemp() # Instantiate keepalived manager with the IPv6 configuration. self.manager = keepalived.KeepalivedManager('router1', self.config, namespace=self.nsname, process_monitor=self.pm, conf_path=self.config_path) self.manager.spawn() def verify_ipv6_address_assignment(self, gw_dev): process = self.manager.get_process() common_utils.wait_until_true(lambda: process.active) def _gw_vip_assigned(): iface_ip = gw_dev.addr.list(ip_version=6, scope='global') if iface_ip: return self.gw_vip == iface_ip[0]['cidr'] common_utils.wait_until_true(_gw_vip_assigned) def __enter__(self): ip_lib.create_network_namespace(self.nsname) return self def __exit__(self, exc_type, exc_value, exc_tb): if self.pm: self.pm.stop() if self.manager: self.manager.disable() if self.config_path: shutil.rmtree(self.config_path, ignore_errors=True) ip_lib.delete_network_namespace(self.nsname) cfg.CONF.set_override('check_child_processes_interval', self.orig_interval, 'AGENT') def keepalived_ipv6_supported(): """Check if keepalived supports IPv6 functionality. Validation is done as follows. 1. Create a namespace. 2. Create OVS bridge with two ports (ha_port and gw_port) 3. Move the ovs ports to the namespace. 4. Spawn keepalived process inside the namespace with IPv6 configuration. 5. Verify if IPv6 address is assigned to gw_port. 6. Verify if IPv6 default route is configured by keepalived. """ br_name, ha_port, gw_port = common_utils.get_related_rand_device_names( ['ka-test-', ha_router.HA_DEV_PREFIX, namespaces.INTERNAL_DEV_PREFIX]) gw_vip = 'fdf8:f53b:82e4::10/64' expected_default_gw = 'fe80:f816::1' with ovs_lib.OVSBridge(br_name) as br: with KeepalivedIPv6Test(ha_port, gw_port, gw_vip, expected_default_gw) as ka: br.add_port(ha_port, ('type', 'internal')) br.add_port(gw_port, ('type', 'internal')) ha_dev = ip_lib.IPDevice(ha_port) gw_dev = ip_lib.IPDevice(gw_port) ha_dev.link.set_netns(ka.nsname) gw_dev.link.set_netns(ka.nsname) ha_dev.link.set_up() gw_dev.link.set_up() ha_dev.addr.add('169.254.192.8/18') ka.configure() ka.start_keepalived_process() ka.verify_ipv6_address_assignment(gw_dev) default_gw = gw_dev.route.get_gateway(ip_version=6) if default_gw: default_gw = default_gw['gateway'] return expected_default_gw == default_gw def ovsdb_native_supported(): # Running the test should ensure we are configured for OVSDB native try: ovs = ovs_lib.BaseOVS() ovs.get_bridges() return True except ImportError as ex: LOG.error("Failed to import required modules. Ensure that the " "python-openvswitch package is installed. Error: %s", ex) except Exception: LOG.exception("Unexpected exception occurred.") return False def ovs_conntrack_supported(): br_name = common_utils.get_rand_device_name(prefix="ovs-test-") with ovs_lib.OVSBridge(br_name) as br: try: br.add_protocols(*["OpenFlow%d" % i for i in range(10, 15)]) except RuntimeError as e: LOG.debug("Exception while checking ovs conntrack support: %s", e) return False return ofctl_arg_supported(cmd='add-flow', ct_state='+trk', actions='drop') def ebtables_supported(): try: cmd = ['ebtables', '--version'] agent_utils.execute(cmd) return True except (OSError, RuntimeError, IndexError, ValueError) as e: LOG.debug("Exception while checking for installed ebtables. " "Exception: %s", e) return False def ipset_supported(): try: cmd = ['ipset', '--version'] agent_utils.execute(cmd) return True except (OSError, RuntimeError, IndexError, ValueError) as e: LOG.debug("Exception while checking for installed ipset. " "Exception: %s", e) return False def ip6tables_supported(): try: cmd = ['ip6tables', '--version'] agent_utils.execute(cmd) return True except (OSError, RuntimeError, IndexError, ValueError) as e: LOG.debug("Exception while checking for installed ip6tables. " "Exception: %s", e) return False def conntrack_supported(): try: cmd = ['conntrack', '--version'] agent_utils.execute(cmd) return True except (OSError, RuntimeError, IndexError, ValueError) as e: LOG.debug("Exception while checking for installed conntrack. " "Exception: %s", e) return False def get_minimal_dibbler_version_supported(): return MINIMUM_DIBBLER_VERSION def dibbler_version_supported(): try: cmd = ['dibbler-client', 'help'] out = agent_utils.execute(cmd) return '-w' in out except (OSError, RuntimeError, IndexError, ValueError) as e: LOG.debug("Exception while checking minimal dibbler version. " "Exception: %s", e) return False def _fix_ip_nonlocal_bind_root_value(original_value): current_value = ip_lib.get_ip_nonlocal_bind(namespace=None) if current_value != original_value: ip_lib.set_ip_nonlocal_bind(value=original_value, namespace=None) def ip_nonlocal_bind(): nsname1 = "ipnonlocalbind1-" + uuidutils.generate_uuid() nsname2 = "ipnonlocalbind2-" + uuidutils.generate_uuid() ip_lib.create_network_namespace(nsname1) try: ip_lib.create_network_namespace(nsname2) try: original_value = ip_lib.get_ip_nonlocal_bind(namespace=None) try: ip_lib.set_ip_nonlocal_bind(value=0, namespace=nsname1) ip_lib.set_ip_nonlocal_bind(value=1, namespace=nsname2) ns1_value = ip_lib.get_ip_nonlocal_bind(namespace=nsname1) finally: _fix_ip_nonlocal_bind_root_value(original_value) except RuntimeError as e: LOG.debug("Exception while checking ip_nonlocal_bind. " "Exception: %s", e) return False finally: ip_lib.delete_network_namespace(nsname2) finally: ip_lib.delete_network_namespace(nsname1) return ns1_value == 0 neutron-12.1.1/neutron/cmd/eventlet/0000775000175000017500000000000013553660156017405 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/cmd/eventlet/services/0000775000175000017500000000000013553660156021230 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/cmd/eventlet/services/metering_agent.py0000664000175000017500000000124113553660046024566 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.services.metering.agents import metering_agent def main(): metering_agent.main() neutron-12.1.1/neutron/cmd/eventlet/services/__init__.py0000664000175000017500000000000013553660046023325 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/cmd/eventlet/agents/0000775000175000017500000000000013553660156020666 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/cmd/eventlet/agents/metadata.py0000664000175000017500000000121613553660046023016 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent import metadata_agent def main(): metadata_agent.main() neutron-12.1.1/neutron/cmd/eventlet/agents/l3.py0000664000175000017500000000120213553660046021547 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent import l3_agent def main(): l3_agent.main() neutron-12.1.1/neutron/cmd/eventlet/agents/dhcp.py0000664000175000017500000000120613553660046022153 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent import dhcp_agent def main(): dhcp_agent.main() neutron-12.1.1/neutron/cmd/eventlet/agents/__init__.py0000664000175000017500000000000013553660046022763 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/cmd/eventlet/plugins/0000775000175000017500000000000013553660156021066 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/cmd/eventlet/plugins/macvtap_neutron_agent.py0000664000175000017500000000130013553660046026013 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.plugins.ml2.drivers.macvtap.agent import ( macvtap_neutron_agent as agent_main) def main(): agent_main.main() neutron-12.1.1/neutron/cmd/eventlet/plugins/sriov_nic_neutron_agent.py0000664000175000017500000000127313553660046026364 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import neutron.plugins.ml2.drivers.mech_sriov.agent.sriov_nic_agent \ as agent_main def main(): agent_main.main() neutron-12.1.1/neutron/cmd/eventlet/plugins/ovs_neutron_agent.py0000664000175000017500000000132113553660046025172 0ustar zuulzuul00000000000000# Copyright (c) 2015 Cloudbase Solutions. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import neutron.plugins.ml2.drivers.openvswitch.agent.main as agent_main def main(): agent_main.main() neutron-12.1.1/neutron/cmd/eventlet/plugins/__init__.py0000664000175000017500000000000013553660046023163 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/cmd/eventlet/plugins/linuxbridge_neutron_agent.py0000664000175000017500000000131013553660046026675 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import \ neutron.plugins.ml2.drivers.linuxbridge.agent.linuxbridge_neutron_agent \ as agent_main def main(): agent_main.main() neutron-12.1.1/neutron/cmd/eventlet/usage_audit.py0000664000175000017500000000337313553660047022256 0ustar zuulzuul00000000000000# Copyright (c) 2012 New Dream Network, LLC (DreamHost) # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Cron script to generate usage notifications for networks, ports and subnets. """ import sys from neutron_lib import context from neutron_lib.plugins import constants from neutron_lib.plugins import directory from neutron.common import config from neutron.common import rpc as n_rpc from neutron import manager def main(): config.init(sys.argv[1:]) config.setup_logging() cxt = context.get_admin_context() manager.init() plugin = directory.get_plugin() l3_plugin = directory.get_plugin(constants.L3) notifier = n_rpc.get_notifier('network') for network in plugin.get_networks(cxt): notifier.info(cxt, 'network.exists', {'network': network}) for subnet in plugin.get_subnets(cxt): notifier.info(cxt, 'subnet.exists', {'subnet': subnet}) for port in plugin.get_ports(cxt): notifier.info(cxt, 'port.exists', {'port': port}) for router in l3_plugin.get_routers(cxt): notifier.info(cxt, 'router.exists', {'router': router}) for floatingip in l3_plugin.get_floatingips(cxt): notifier.info(cxt, 'floatingip.exists', {'floatingip': floatingip}) neutron-12.1.1/neutron/cmd/eventlet/server/0000775000175000017500000000000013553660156020713 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/cmd/eventlet/server/__init__.py0000664000175000017500000000150613553660046023024 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron import server from neutron.server import rpc_eventlet from neutron.server import wsgi_eventlet def main(): server.boot_server(wsgi_eventlet.eventlet_wsgi_server) def main_rpc_eventlet(): server.boot_server(rpc_eventlet.eventlet_rpc_server) neutron-12.1.1/neutron/cmd/eventlet/__init__.py0000664000175000017500000000120613553660046021513 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.common import eventlet_utils eventlet_utils.monkey_patch() neutron-12.1.1/neutron/cmd/keepalived_state_change.py0000664000175000017500000000130713553660046022746 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.l3 import keepalived_state_change def main(): keepalived_state_change.main() neutron-12.1.1/neutron/cmd/ipset_cleanup.py0000664000175000017500000000634213553660046020767 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from oslo_log import log as logging from neutron.agent.linux import utils from neutron.common import config from neutron.conf.agent import cmd as command LOG = logging.getLogger(__name__) def setup_conf(): """Setup the cfg for the clean up utility. Use separate setup_conf for the utility because there are many options from the main config that do not apply during clean-up. """ conf = cfg.CONF command.register_cmd_opts(command.ip_opts, conf) return conf def remove_iptables_reference(ipset): # Remove any iptables reference to this IPset cmd = ['iptables-save'] if 'IPv4' in ipset else ['ip6tables-save'] iptables_save = utils.execute(cmd, run_as_root=True) if ipset in iptables_save: cmd = ['iptables'] if 'IPv4' in ipset else ['ip6tables'] cmd += ['-w', '10'] # wait for xlock release LOG.info("Removing iptables rule for IPset: %s", ipset) for rule in iptables_save.splitlines(): if '--match-set %s ' % ipset in rule and rule.startswith('-A'): # change to delete params = rule.split() params[0] = '-D' try: utils.execute(cmd + params, run_as_root=True) except Exception: LOG.exception('Error, unable to remove iptables rule ' 'for IPset: %s', ipset) def destroy_ipset(conf, ipset): # If there is an iptables reference and we don't remove it, the # IPset removal will fail below if conf.force: remove_iptables_reference(ipset) LOG.info("Destroying IPset: %s", ipset) cmd = ['ipset', 'destroy', ipset] try: utils.execute(cmd, run_as_root=True) except Exception: LOG.exception('Error, unable to destroy IPset: %s', ipset) def cleanup_ipsets(conf): # Identify ipsets for destruction. LOG.info("Destroying IPsets with prefix: %s", conf.prefix) cmd = ['ipset', '-L', '-n'] ipsets = utils.execute(cmd, run_as_root=True) for ipset in ipsets.split('\n'): if conf.allsets or ipset.startswith(conf.prefix): destroy_ipset(conf, ipset) LOG.info("IPset cleanup completed successfully") def main(): """Main method for cleaning up IPsets. The utility is designed to clean-up after the forced or unexpected termination of Neutron agents. The --allsets flag should only be used as part of the cleanup of a devstack installation as it will blindly destroy all IPsets. """ conf = setup_conf() conf() config.setup_logging() cleanup_ipsets(conf) neutron-12.1.1/neutron/cmd/runtime_checks.py0000664000175000017500000000243513553660046021136 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from neutron.agent.linux import utils as agent_utils LOG = logging.getLogger(__name__) # NOTE: Runtime checks are strongly discouraged in favor of sanity checks # which would be run at system setup time. Please consider writing a # sanity check instead. def dhcp_release6_supported(): try: cmd = ['dhcp_release6', '--help'] env = {'LC_ALL': 'C'} agent_utils.execute(cmd, addl_env=env) except (OSError, RuntimeError, IndexError, ValueError) as e: LOG.debug("Exception while checking dhcp_release6. " "Exception: %s", e) return False return True neutron-12.1.1/neutron/cmd/ovs_cleanup.py0000664000175000017500000001055113553660047020450 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from oslo_log import log as logging from neutron.agent.common import ovs_lib from neutron.agent.linux import ip_lib from neutron.common import config from neutron.conf.agent import cmd from neutron.conf.agent import common as agent_config from neutron.conf.agent.l3 import config as l3_config from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants LOG = logging.getLogger(__name__) # Default ovsdb_timeout value for this script. # It allows to clean bridges with even thousands of ports. CLEANUP_OVSDB_TIMEOUT = 600 def setup_conf(): """Setup the cfg for the clean up utility. Use separate setup_conf for the utility because there are many options from the main config that do not apply during clean-up. """ conf = cfg.CONF cmd.register_cmd_opts(cmd.ovs_opts, conf) l3_config.register_l3_agent_config_opts(l3_config.OPTS, conf) agent_config.register_interface_driver_opts_helper(conf) agent_config.register_interface_opts() conf.set_default("ovsdb_timeout", CLEANUP_OVSDB_TIMEOUT, "OVS") return conf def get_bridge_deletable_ports(br): """ Return a list of OVS Bridge ports, excluding the ports who should not be cleaned. such ports are tagged with the 'skip_cleanup' key in external_ids. """ return [port.port_name for port in br.get_vif_ports() if constants.SKIP_CLEANUP not in br.get_port_external_ids(port.port_name)] def collect_neutron_ports(bridges): """Collect ports created by Neutron from OVS.""" ports = [] for bridge in bridges: ovs = ovs_lib.OVSBridge(bridge) ports += get_bridge_deletable_ports(ovs) return ports def delete_neutron_ports(ports): """Delete non-internal ports created by Neutron Non-internal OVS ports need to be removed manually. """ for port in ports: device = ip_lib.IPDevice(port) if device.exists(): device.link.delete() LOG.info("Deleting port: %s", port) def main(): """Main method for cleaning up OVS bridges. The utility cleans up the integration bridges used by Neutron. """ conf = setup_conf() conf() config.setup_logging() do_main(conf) def do_main(conf): configuration_bridges = set([conf.ovs_integration_bridge, conf.external_network_bridge]) ovs = ovs_lib.BaseOVS() ovs_bridges = set(ovs.get_bridges()) available_configuration_bridges = configuration_bridges & ovs_bridges if conf.ovs_all_ports: bridges = ovs_bridges else: bridges = available_configuration_bridges try: # The ovs_cleanup method not added to the deprecated vsctl backend for bridge in bridges: LOG.info("Cleaning bridge: %s", bridge) ovs.ovsdb.ovs_cleanup(bridge, conf.ovs_all_ports).execute(check_error=True) except AttributeError: # Collect existing ports created by Neutron on configuration bridges. # After deleting ports from OVS bridges, we cannot determine which # ports were created by Neutron, so port information is collected now. ports = collect_neutron_ports(available_configuration_bridges) for bridge in bridges: LOG.info("Cleaning bridge: %s", bridge) ovs = ovs_lib.OVSBridge(bridge) if conf.ovs_all_ports: port_names = ovs.get_port_name_list() else: port_names = get_bridge_deletable_ports(ovs) for port_name in port_names: ovs.delete_port(port_name) # Remove remaining ports created by Neutron (usually veth pair) delete_neutron_ports(ports) LOG.info("OVS cleanup completed successfully") neutron-12.1.1/neutron/cmd/netns_cleanup.py0000664000175000017500000002430213553660047020767 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools import re import signal import time from neutron_lib import constants from oslo_config import cfg from oslo_log import log as logging from oslo_utils import importutils from neutron.agent.common import ovs_lib from neutron.agent.l3 import dvr_fip_ns from neutron.agent.l3 import dvr_snat_ns from neutron.agent.l3 import namespaces from neutron.agent.linux import dhcp from neutron.agent.linux import external_process from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.common import config from neutron.conf.agent import cmd from neutron.conf.agent import common as agent_config from neutron.conf.agent import dhcp as dhcp_config LOG = logging.getLogger(__name__) LB_NS_PREFIX = 'qlbaas-' NS_PREFIXES = { 'dhcp': [dhcp.NS_PREFIX], 'l3': [namespaces.NS_PREFIX, dvr_snat_ns.SNAT_NS_PREFIX, dvr_fip_ns.FIP_NS_PREFIX], 'lbaas': [LB_NS_PREFIX], } SIGTERM_WAITTIME = 10 NETSTAT_PIDS_REGEX = re.compile(r'.* (?P\d{2,6})/.*') class PidsInNamespaceException(Exception): pass class FakeDhcpPlugin(object): """Fake RPC plugin to bypass any RPC calls.""" def __getattribute__(self, name): def fake_method(*args): pass return fake_method def setup_conf(): """Setup the cfg for the clean up utility. Use separate setup_conf for the utility because there are many options from the main config that do not apply during clean-up. """ conf = cfg.CONF cmd.register_cmd_opts(cmd.netns_opts, conf) agent_config.register_interface_driver_opts_helper(conf) dhcp_config.register_agent_dhcp_opts(conf) agent_config.register_interface_opts() return conf def _get_dhcp_process_monitor(config): return external_process.ProcessMonitor(config=config, resource_type='dhcp') def kill_dhcp(conf, namespace): """Disable DHCP for a network if DHCP is still active.""" network_id = namespace.replace(dhcp.NS_PREFIX, '') dhcp_driver = importutils.import_object( conf.dhcp_driver, conf=conf, process_monitor=_get_dhcp_process_monitor(conf), network=dhcp.NetModel({'id': network_id}), plugin=FakeDhcpPlugin()) if dhcp_driver.active: dhcp_driver.disable() def eligible_for_deletion(conf, namespace, force=False): """Determine whether a namespace is eligible for deletion. Eligibility is determined by having only the lo device or if force is passed as a parameter. """ if conf.agent_type: prefixes = NS_PREFIXES.get(conf.agent_type) else: prefixes = itertools.chain(*NS_PREFIXES.values()) ns_mangling_pattern = '(%s%s)' % ('|'.join(prefixes), constants.UUID_PATTERN) # filter out namespaces without UUID as the name if not re.match(ns_mangling_pattern, namespace): return False ip = ip_lib.IPWrapper(namespace=namespace) return force or ip.namespace_is_empty() def unplug_device(conf, device): orig_log_fail_as_error = device.get_log_fail_as_error() device.set_log_fail_as_error(False) try: device.link.delete() except RuntimeError: device.set_log_fail_as_error(orig_log_fail_as_error) # Maybe the device is OVS port, so try to delete ovs = ovs_lib.BaseOVS() bridge_name = ovs.get_bridge_for_iface(device.name) if bridge_name: bridge = ovs_lib.OVSBridge(bridge_name) bridge.delete_port(device.name) else: LOG.debug('Unable to find bridge for device: %s', device.name) finally: device.set_log_fail_as_error(orig_log_fail_as_error) def find_listen_pids_namespace(namespace): """Retrieve a list of pids of listening processes within the given netns. It executes netstat -nlp and returns a set of unique pairs """ ip = ip_lib.IPWrapper(namespace=namespace) pids = set() cmd = ['netstat', '-nlp'] output = ip.netns.execute(cmd, run_as_root=True) for line in output.splitlines(): m = NETSTAT_PIDS_REGEX.match(line) if m: pids.add(m.group('pid')) return pids def wait_until_no_listen_pids_namespace(namespace, timeout=SIGTERM_WAITTIME): """Poll listening processes within the given namespace. If after timeout seconds, there are remaining processes in the namespace, then a PidsInNamespaceException will be thrown. """ # NOTE(dalvarez): This function can block forever if # find_listen_pids_in_namespace never returns which is really unlikely. We # can't use wait_until_true because we might get interrupted by eventlet # Timeout during our I/O with rootwrap daemon and that will lead to errors # in subsequent calls to utils.execute grabbing always the output of the # previous command start = end = time.time() while end - start < timeout: if not find_listen_pids_namespace(namespace): return time.sleep(1) end = time.time() raise PidsInNamespaceException def _kill_listen_processes(namespace, force=False): """Identify all listening processes within the given namespace. Then, for each one, find its top parent with same cmdline (in case this process forked) and issue a SIGTERM to all of them. If force is True, then a SIGKILL will be issued to all parents and all their children. Also, this function returns the number of listening processes. """ pids = find_listen_pids_namespace(namespace) pids_to_kill = {utils.find_fork_top_parent(pid) for pid in pids} kill_signal = signal.SIGTERM if force: kill_signal = signal.SIGKILL children = [utils.find_child_pids(pid, True) for pid in pids_to_kill] pids_to_kill.update(itertools.chain.from_iterable(children)) for pid in pids_to_kill: # Throw a warning since this particular cleanup may need a specific # implementation in the right module. Ideally, netns_cleanup wouldn't # kill any processes as the responsible module should've killed them # before cleaning up the namespace LOG.warning("Killing (%(signal)d) [%(pid)s] %(cmdline)s", {'signal': kill_signal, 'pid': pid, 'cmdline': ' '.join(utils.get_cmdline_from_pid(pid))[:80] }) try: utils.kill_process(pid, kill_signal, run_as_root=True) except Exception as ex: LOG.error('An error occurred while killing ' '[%(pid)s]: %(msg)s', {'pid': pid, 'msg': ex}) return len(pids) def kill_listen_processes(namespace): """Kill all processes listening within the given namespace. First it tries to kill them using SIGTERM, waits until they die gracefully and then kills remaining processes (if any) with SIGKILL """ if _kill_listen_processes(namespace, force=False): try: wait_until_no_listen_pids_namespace(namespace) except PidsInNamespaceException: _kill_listen_processes(namespace, force=True) # Allow some time for remaining processes to die wait_until_no_listen_pids_namespace(namespace) def destroy_namespace(conf, namespace, force=False): """Destroy a given namespace. If force is True, then dhcp (if it exists) will be disabled and all devices will be forcibly removed. """ try: ip = ip_lib.IPWrapper(namespace=namespace) if force: kill_dhcp(conf, namespace) # NOTE: The dhcp driver will remove the namespace if is it empty, # so a second check is required here. if ip.netns.exists(namespace): try: kill_listen_processes(namespace) except PidsInNamespaceException: # This is unlikely since, at this point, we have SIGKILLed # all remaining processes but if there are still some, log # the error and continue with the cleanup LOG.error('Not all processes were killed in %s', namespace) for device in ip.get_devices(): unplug_device(conf, device) ip.garbage_collect_namespace() except Exception: LOG.exception('Error unable to destroy namespace: %s', namespace) def cleanup_network_namespaces(conf): # Identify namespaces that are candidates for deletion. candidates = [ns for ns in ip_lib.list_network_namespaces() if eligible_for_deletion(conf, ns, conf.force)] if candidates: time.sleep(2) for namespace in candidates: destroy_namespace(conf, namespace, conf.force) def main(): """Main method for cleaning up network namespaces. This method will make two passes checking for namespaces to delete. The process will identify candidates, sleep, and call garbage collect. The garbage collection will re-verify that the namespace meets the criteria for deletion (ie it is empty). The period of sleep and the 2nd pass allow time for the namespace state to settle, so that the check prior deletion will re-confirm the namespace is empty. The utility is designed to clean-up after the forced or unexpected termination of Neutron agents. The --force flag should only be used as part of the cleanup of a devstack installation as it will blindly purge namespaces and their devices. This option also kills any lingering DHCP instances. """ conf = setup_conf() conf() config.setup_logging() agent_config.setup_privsep() cleanup_network_namespaces(conf) neutron-12.1.1/neutron/cmd/pd_notify.py0000664000175000017500000000252413553660047020126 0ustar zuulzuul00000000000000# Copyright (c) 2015 Cisco Systems. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import signal import sys from neutron_lib.utils import file as file_utils def main(): """Expected arguments: sys.argv[1] - The add/update/delete operation performed by the PD agent sys.argv[2] - The file where the new prefix should be written sys.argv[3] - The process ID of the L3 agent to be notified of this change """ operation = sys.argv[1] prefix_fname = sys.argv[2] agent_pid = sys.argv[3] prefix = os.getenv('PREFIX1', "::") if operation == "add" or operation == "update": file_utils.replace_file(prefix_fname, "%s/64" % prefix) elif operation == "delete": file_utils.replace_file(prefix_fname, "::/64") os.kill(int(agent_pid), signal.SIGUSR1) neutron-12.1.1/neutron/cmd/linuxbridge_cleanup.py0000664000175000017500000000474113553660047022161 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from neutron_lib.utils import helpers from oslo_config import cfg from oslo_log import log as logging from neutron.common import config from neutron.plugins.ml2.drivers.linuxbridge.agent \ import linuxbridge_neutron_agent LOG = logging.getLogger(__name__) def remove_empty_bridges(): try: interface_mappings = helpers.parse_mappings( cfg.CONF.LINUX_BRIDGE.physical_interface_mappings) except ValueError as e: LOG.error("Parsing physical_interface_mappings failed: %s.", e) sys.exit(1) LOG.info("Interface mappings: %s.", interface_mappings) try: bridge_mappings = helpers.parse_mappings( cfg.CONF.LINUX_BRIDGE.bridge_mappings) except ValueError as e: LOG.error("Parsing bridge_mappings failed: %s.", e) sys.exit(1) LOG.info("Bridge mappings: %s.", bridge_mappings) lb_manager = linuxbridge_neutron_agent.LinuxBridgeManager( bridge_mappings, interface_mappings) bridge_names = lb_manager.get_deletable_bridges() for bridge_name in bridge_names: if lb_manager.get_tap_devices_count(bridge_name): continue try: lb_manager.delete_bridge(bridge_name) LOG.info("Linux bridge %s deleted", bridge_name) except RuntimeError: LOG.exception("Linux bridge %s delete failed", bridge_name) LOG.info("Linux bridge cleanup completed successfully") def main(): """Main method for cleaning up empty linux bridges. This tool deletes every empty linux bridge managed by linuxbridge agent (brq.* linux bridges) except thes ones defined using bridge_mappings option in section LINUX_BRIDGE (created by deployers). This tool should not be called during an instance create, migrate, etc. as it can delete a linux bridge about to be used by nova. """ cfg.CONF(sys.argv[1:]) config.setup_logging() remove_empty_bridges() neutron-12.1.1/neutron/cmd/__init__.py0000664000175000017500000000210713553660046017666 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging as sys_logging from oslo_reports import guru_meditation_report as gmr from neutron import version # During the call to gmr.TextGuruMeditation.setup_autorun(), Guru Meditation # Report tries to start logging. Set a handler here to accommodate this. logger = sys_logging.getLogger(None) if not logger.handlers: logger.addHandler(sys_logging.StreamHandler()) _version_string = version.version_info.release_string() gmr.TextGuruMeditation.setup_autorun(version=_version_string) neutron-12.1.1/neutron/cmd/sanity_check.py0000664000175000017500000004062713553660047020605 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from oslo_config import cfg from oslo_log import log as logging from neutron._i18n import _ from neutron.agent import dhcp_agent from neutron.cmd.sanity import checks from neutron.common import config from neutron.conf.db import l3_hamode_db LOG = logging.getLogger(__name__) def setup_conf(): cfg.CONF.import_group('AGENT', 'neutron.plugins.ml2.drivers.openvswitch.' 'agent.common.config') cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.' 'agent.common.config') cfg.CONF.import_group('VXLAN', 'neutron.plugins.ml2.drivers.linuxbridge.' 'agent.common.config') cfg.CONF.import_group('ml2', 'neutron.conf.plugins.ml2.config') cfg.CONF.import_group('SECURITYGROUP', 'neutron.agent.securitygroups_rpc') dhcp_agent.register_options(cfg.CONF) cfg.CONF.register_opts(l3_hamode_db.L3_HA_OPTS) class BoolOptCallback(cfg.BoolOpt): def __init__(self, name, callback, **kwargs): if 'default' not in kwargs: kwargs['default'] = False self.callback = callback super(BoolOptCallback, self).__init__(name, **kwargs) def check_ovs_vxlan(): result = checks.ovs_vxlan_supported() if not result: LOG.error('Check for Open vSwitch VXLAN support failed. ' 'Please ensure that the version of openvswitch ' 'being used has VXLAN support.') return result def check_ovs_geneve(): result = checks.ovs_geneve_supported() if not result: LOG.error('Check for Open vSwitch Geneve support failed. ' 'Please ensure that the version of openvswitch ' 'and kernel being used has Geneve support.') return result def check_iproute2_vxlan(): result = checks.iproute2_vxlan_supported() if not result: LOG.error('Check for iproute2 VXLAN support failed. Please ensure ' 'that the iproute2 has VXLAN support.') return result def check_ovs_patch(): result = checks.patch_supported() if not result: LOG.error('Check for Open vSwitch patch port support failed. ' 'Please ensure that the version of openvswitch ' 'being used has patch port support or disable features ' 'requiring patch ports (gre/vxlan, etc.).') return result def check_read_netns(): required = checks.netns_read_requires_helper() if not required and cfg.CONF.AGENT.use_helper_for_ns_read: LOG.warning("The user that is executing neutron can read the " "namespaces without using the root_helper. Disable " "the use_helper_for_ns_read option to avoid a " "performance impact.") # Don't fail because nothing is actually broken. Just not optimal. result = True elif required and not cfg.CONF.AGENT.use_helper_for_ns_read: LOG.error("The user that is executing neutron does not have " "permissions to read the namespaces. Enable the " "use_helper_for_ns_read configuration option.") result = False else: # everything is configured appropriately result = True return result # NOTE(ihrachyshka): since the minimal version is currently capped due to # missing hwaddr matching in dnsmasq < 2.67, a better version of the check # would actually start dnsmasq server and issue a DHCP request using a IPv6 # DHCP client. def check_dnsmasq_version(): result = checks.dnsmasq_version_supported() if not result: LOG.error('The installed version of dnsmasq is too old. ' 'Please update to at least version %s.', checks.get_minimal_dnsmasq_version_supported()) return result def check_dnsmasq_local_service_supported(): result = checks.dnsmasq_local_service_supported() if not result: LOG.error('The installed version of dnsmasq is too old. ' 'Please update to a version supporting the ' '--local-service option.') return result def check_keepalived_ipv6_support(): result = checks.keepalived_ipv6_supported() if not result: LOG.error('The installed version of keepalived does not support ' 'IPv6. Please update to at least version 1.2.10 for ' 'IPv6 support.') return result def check_dibbler_version(): result = checks.dibbler_version_supported() if not result: LOG.error('The installed version of dibbler-client is too old. ' 'Please update to at least version %s.', checks.get_minimal_dibbler_version_supported()) return result def check_nova_notify(): result = checks.nova_notify_supported() if not result: LOG.error('Nova notifications are enabled, but novaclient is not ' 'installed. Either disable nova notifications or ' 'install python-novaclient.') return result def check_arp_responder(): result = checks.arp_responder_supported() if not result: LOG.error('Check for Open vSwitch ARP responder support failed. ' 'Please ensure that the version of openvswitch ' 'being used has ARP flows support.') return result def check_arp_header_match(): result = checks.arp_header_match_supported() if not result: LOG.error('Check for Open vSwitch support of ARP header matching ' 'failed. ARP spoofing suppression will not work. A ' 'newer version of OVS is required.') return result def check_icmpv6_header_match(): result = checks.icmpv6_header_match_supported() if not result: LOG.error('Check for Open vSwitch support of ICMPv6 header ' 'matching failed. ICMPv6 Neighbor Advt spoofing (part ' 'of arp spoofing) suppression will not work. A newer ' 'version of OVS is required.') return result def check_vf_management(): result = checks.vf_management_supported() if not result: LOG.error('Check for VF management support failed. ' 'Please ensure that the version of ip link ' 'being used has VF support.') return result def check_vf_extended_management(): result = checks.vf_extended_management_supported() if not result: LOG.error('Check for VF extended management support failed. ' 'Please ensure that the version of ip link ' 'being used has VF extended support: version ' '"iproute2-ss140804", git tag "v3.16.0"') return result def check_ovsdb_native(): cfg.CONF.set_override('ovsdb_interface', 'native', group='OVS') result = checks.ovsdb_native_supported() if not result: LOG.error('Check for native OVSDB support failed.') return result def check_ovs_conntrack(): result = checks.ovs_conntrack_supported() if not result: LOG.error('Check for Open vSwitch support of conntrack support ' 'failed. OVS/CT firewall will not work. A newer ' 'version of OVS (2.5+) and linux kernel (4.3+) are ' 'required. See ' 'https://github.com/openvswitch/ovs/blob/master/FAQ.md ' 'for more information.') return result def check_ebtables(): result = checks.ebtables_supported() if not result: LOG.error('Cannot run ebtables. Please ensure that it ' 'is installed.') return result def check_ipset(): result = checks.ipset_supported() if not result: LOG.error('Cannot run ipset. Please ensure that it ' 'is installed.') return result def check_ip6tables(): result = checks.ip6tables_supported() if not result: LOG.error('Cannot run ip6tables. Please ensure that it ' 'is installed.') return result def check_conntrack(): result = checks.conntrack_supported() if not result: LOG.error('Cannot run conntrack. Please ensure that it ' 'is installed.') return result def check_dhcp_release6(): result = checks.dhcp_release6_supported() if not result: LOG.error('No dhcp_release6 tool detected. The installed version ' 'of dnsmasq does not support releasing IPv6 leases. ' 'Please update to at least version %s if you need this ' 'feature. If you do not use IPv6 stateful subnets you ' 'can continue to use this version of dnsmasq, as ' 'other IPv6 address assignment mechanisms besides ' 'stateful DHCPv6 should continue to work without ' 'the dhcp_release6 utility. ' 'Current version of dnsmasq is ok if other checks ' 'pass.', checks.get_dnsmasq_version_with_dhcp_release6()) return result def check_bridge_firewalling_enabled(): result = checks.bridge_firewalling_enabled() if not result: LOG.error('Bridge firewalling is not enabled. It may be the case ' 'that bridge and/or br_netfilter kernel modules are not ' 'loaded. Alternatively, corresponding sysctl settings ' 'may be overridden to disable it by default.') return result def check_ip_nonlocal_bind(): result = checks.ip_nonlocal_bind() if not result: LOG.error('This kernel does not isolate ip_nonlocal_bind kernel ' 'option in namespaces. Please update to kernel ' 'version > 3.19.') return result # Define CLI opts to test specific features, with a callback for the test OPTS = [ BoolOptCallback('ovs_vxlan', check_ovs_vxlan, default=False, help=_('Check for OVS vxlan support')), BoolOptCallback('ovs_geneve', check_ovs_geneve, default=False, help=_('Check for OVS Geneve support')), BoolOptCallback('iproute2_vxlan', check_iproute2_vxlan, default=False, help=_('Check for iproute2 vxlan support')), BoolOptCallback('ovs_patch', check_ovs_patch, default=False, help=_('Check for patch port support')), BoolOptCallback('nova_notify', check_nova_notify, help=_('Check for nova notification support')), BoolOptCallback('arp_responder', check_arp_responder, help=_('Check for ARP responder support')), BoolOptCallback('arp_header_match', check_arp_header_match, help=_('Check for ARP header match support')), BoolOptCallback('icmpv6_header_match', check_icmpv6_header_match, help=_('Check for ICMPv6 header match support')), BoolOptCallback('vf_management', check_vf_management, help=_('Check for VF management support')), BoolOptCallback('vf_extended_management', check_vf_extended_management, help=_('Check for VF extended management support')), BoolOptCallback('read_netns', check_read_netns, help=_('Check netns permission settings')), BoolOptCallback('dnsmasq_local_service_supported', check_dnsmasq_local_service_supported, help=_('Check for local-service support in dnsmasq')), BoolOptCallback('dnsmasq_version', check_dnsmasq_version, help=_('Check minimal dnsmasq version'), deprecated_for_removal=True, deprecated_since='Pike'), BoolOptCallback('ovsdb_native', check_ovsdb_native, help=_('Check ovsdb native interface support')), BoolOptCallback('ovs_conntrack', check_ovs_conntrack, help=_('Check ovs conntrack support')), BoolOptCallback('ebtables_installed', check_ebtables, help=_('Check ebtables installation')), BoolOptCallback('keepalived_ipv6_support', check_keepalived_ipv6_support, help=_('Check keepalived IPv6 support')), BoolOptCallback('dibbler_version', check_dibbler_version, help=_('Check minimal dibbler version'), deprecated_for_removal=True, deprecated_since='Pike'), BoolOptCallback('ipset_installed', check_ipset, help=_('Check ipset installation')), BoolOptCallback('ip6tables_installed', check_ip6tables, help=_('Check ip6tables installation')), BoolOptCallback('conntrack_installed', check_conntrack, help=_('Check conntrack installation')), BoolOptCallback('dhcp_release6', check_dhcp_release6, help=_('Check dhcp_release6 installation')), BoolOptCallback('bridge_firewalling', check_bridge_firewalling_enabled, help=_('Check bridge firewalling'), default=False), BoolOptCallback('ip_nonlocal_bind', check_ip_nonlocal_bind, help=_('Check ip_nonlocal_bind kernel option works with ' 'network namespaces.'), default=False), ] def enable_tests_from_config(): """If a test can depend on configuration, use this function to set the appropriate CLI option to enable that test. It will then be possible to run all necessary tests, just by passing in the appropriate configs. """ cfg.CONF.set_default('vf_management', True) cfg.CONF.set_default('arp_header_match', True) cfg.CONF.set_default('icmpv6_header_match', True) if 'vxlan' in cfg.CONF.AGENT.tunnel_types: cfg.CONF.set_default('ovs_vxlan', True) if 'geneve' in cfg.CONF.AGENT.tunnel_types: cfg.CONF.set_default('ovs_geneve', True) if ('vxlan' in cfg.CONF.ml2.type_drivers or cfg.CONF.VXLAN.enable_vxlan): cfg.CONF.set_default('iproute2_vxlan', True) if cfg.CONF.AGENT.tunnel_types: cfg.CONF.set_default('ovs_patch', True) if not cfg.CONF.OVS.use_veth_interconnection: cfg.CONF.set_default('ovs_patch', True) if (cfg.CONF.notify_nova_on_port_status_changes or cfg.CONF.notify_nova_on_port_data_changes): cfg.CONF.set_default('nova_notify', True) if cfg.CONF.AGENT.arp_responder: cfg.CONF.set_default('arp_responder', True) if not cfg.CONF.AGENT.use_helper_for_ns_read: cfg.CONF.set_default('read_netns', True) if cfg.CONF.OVS.ovsdb_interface == 'native': cfg.CONF.set_default('ovsdb_native', True) if cfg.CONF.dhcp_driver == 'neutron.agent.linux.dhcp.Dnsmasq': cfg.CONF.set_default('dnsmasq_local_service_supported', True) cfg.CONF.set_default('dnsmasq_version', True) if cfg.CONF.l3_ha: cfg.CONF.set_default('keepalived_ipv6_support', True) cfg.CONF.set_default('ip_nonlocal_bind', True) if cfg.CONF.SECURITYGROUP.enable_ipset: cfg.CONF.set_default('ipset_installed', True) if cfg.CONF.SECURITYGROUP.enable_security_group: cfg.CONF.set_default('ip6tables_installed', True) if ('sriovnicswitch' in cfg.CONF.ml2.mechanism_drivers and 'qos' in cfg.CONF.ml2.extension_drivers): cfg.CONF.set_default('vf_extended_management', True) if cfg.CONF.SECURITYGROUP.firewall_driver in ( 'iptables', 'iptables_hybrid', ('neutron.agent.linux.iptables_firewall.' 'IptablesFirewallDriver'), ('neutron.agent.linux.iptables_firewall.' 'OVSHybridIptablesFirewallDriver'), ): cfg.CONF.set_default('bridge_firewalling', True) def all_tests_passed(): return all(opt.callback() for opt in OPTS if cfg.CONF.get(opt.name)) def main(): setup_conf() cfg.CONF.register_cli_opts(OPTS) cfg.CONF.set_override('use_stderr', True) config.setup_logging() config.init(sys.argv[1:], default_config_files=[]) if cfg.CONF.config_file: enable_tests_from_config() return 0 if all_tests_passed() else 1 neutron-12.1.1/neutron/conf/0000775000175000017500000000000013553660156015741 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/common.py0000664000175000017500000002173213553660047017607 0ustar zuulzuul00000000000000# Copyright 2011 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.utils import net from oslo_config import cfg from oslo_service import wsgi from neutron._i18n import _ from neutron.common import constants core_opts = [ cfg.HostAddressOpt('bind_host', default='0.0.0.0', help=_("The host IP to bind to.")), cfg.PortOpt('bind_port', default=9696, help=_("The port to bind to")), cfg.StrOpt('api_extensions_path', default="", help=_("The path for API extensions. " "Note that this can be a colon-separated list of paths. " "For example: api_extensions_path = " "extensions:/path/to/more/exts:/even/more/exts. " "The __path__ of neutron.extensions is appended to " "this, so if your extensions are in there you don't " "need to specify them here.")), cfg.StrOpt('auth_strategy', default='keystone', help=_("The type of authentication to use")), cfg.StrOpt('core_plugin', help=_("The core plugin Neutron will use")), cfg.ListOpt('service_plugins', default=[], help=_("The service plugins Neutron will use")), cfg.StrOpt('base_mac', default="fa:16:3e:00:00:00", help=_("The base MAC address Neutron will use for VIFs. " "The first 3 octets will remain unchanged. If the 4th " "octet is not 00, it will also be used. The others " "will be randomly generated.")), cfg.BoolOpt('allow_bulk', default=True, help=_("Allow the usage of the bulk API")), cfg.StrOpt('pagination_max_limit', default="-1", help=_("The maximum number of items returned in a single " "response, value was 'infinite' or negative integer " "means no limit")), cfg.ListOpt('default_availability_zones', default=[], help=_("Default value of availability zone hints. The " "availability zone aware schedulers use this when " "the resources availability_zone_hints is empty. " "Multiple availability zones can be specified by a " "comma separated string. This value can be empty. " "In this case, even if availability_zone_hints for " "a resource is empty, availability zone is " "considered for high availability while scheduling " "the resource.")), cfg.IntOpt('max_dns_nameservers', default=5, help=_("Maximum number of DNS nameservers per subnet")), cfg.IntOpt('max_subnet_host_routes', default=20, help=_("Maximum number of host routes per subnet")), cfg.BoolOpt('ipv6_pd_enabled', default=False, help=_("Enables IPv6 Prefix Delegation for automatic subnet " "CIDR allocation. " "Set to True to enable IPv6 Prefix Delegation for " "subnet allocation in a PD-capable environment. Users " "making subnet creation requests for IPv6 subnets " "without providing a CIDR or subnetpool ID will be " "given a CIDR via the Prefix Delegation mechanism. " "Note that enabling PD will override the behavior of " "the default IPv6 subnetpool.")), cfg.IntOpt('dhcp_lease_duration', default=86400, help=_("DHCP lease duration (in seconds). Use -1 to tell " "dnsmasq to use infinite lease times.")), cfg.StrOpt('dns_domain', default='openstacklocal', help=_('Domain to use for building the hostnames')), cfg.StrOpt('external_dns_driver', help=_('Driver for external DNS integration.')), cfg.BoolOpt('dhcp_agent_notification', default=True, help=_("Allow sending resource operation" " notification to DHCP agent")), cfg.BoolOpt('allow_overlapping_ips', default=False, help=_("Allow overlapping IP support in Neutron. " "Attention: the following parameter MUST be set to " "False if Neutron is being used in conjunction with " "Nova security groups.")), cfg.HostAddressOpt('host', default=net.get_hostname(), sample_default='example.domain', help=_("Hostname to be used by the Neutron server, " "agents and services running on this machine. " "All the agents and services running on this " "machine must use the same host value.")), cfg.StrOpt("network_link_prefix", help=_("This string is prepended to the normal URL that is " "returned in links to the OpenStack Network API. If it " "is empty (the default), the URLs are returned " "unchanged.")), cfg.BoolOpt('notify_nova_on_port_status_changes', default=True, help=_("Send notification to nova when port status changes")), cfg.BoolOpt('notify_nova_on_port_data_changes', default=True, help=_("Send notification to nova when port data (fixed_ips/" "floatingip) changes so nova can update its cache.")), cfg.IntOpt('send_events_interval', default=2, help=_('Number of seconds between sending events to nova if ' 'there are any events to send.')), cfg.StrOpt('ipam_driver', default='internal', help=_("Neutron IPAM (IP address management) driver to use. " "By default, the reference implementation of the " "Neutron IPAM driver is used.")), cfg.BoolOpt('vlan_transparent', default=False, help=_('If True, then allow plugins that support it to ' 'create VLAN transparent networks.')), cfg.IntOpt('global_physnet_mtu', default=constants.DEFAULT_NETWORK_MTU, deprecated_name='segment_mtu', deprecated_group='ml2', help=_('MTU of the underlying physical network. Neutron uses ' 'this value to calculate MTU for all virtual network ' 'components. For flat and VLAN networks, neutron uses ' 'this value without modification. For overlay networks ' 'such as VXLAN, neutron automatically subtracts the ' 'overlay protocol overhead from this value. Defaults ' 'to 1500, the standard value for Ethernet.')) ] core_cli_opts = [ cfg.StrOpt('state_path', default='/var/lib/neutron', help=_("Where to store Neutron state files. " "This directory must be writable by the agent.")), ] def register_core_common_config_opts(cfg=cfg.CONF): cfg.register_opts(core_opts) cfg.register_cli_opts(core_cli_opts) wsgi.register_opts(cfg) NOVA_CONF_SECTION = 'nova' nova_opts = [ cfg.StrOpt('region_name', help=_('Name of nova region to use. Useful if keystone manages' ' more than one region.')), cfg.StrOpt('endpoint_type', default='public', choices=['public', 'admin', 'internal'], help=_('Type of the nova endpoint to use. This endpoint will' ' be looked up in the keystone catalog and should be' ' one of public, internal or admin.')), ] def register_nova_opts(cfg=cfg.CONF): cfg.register_opts(nova_opts, group=NOVA_CONF_SECTION) PLACEMENT_CONF_SECTION = 'placement' placement_opts = [ cfg.StrOpt('region_name', help=_('Name of placement region to use. Useful if keystone ' 'manages more than one region.')), cfg.StrOpt('endpoint_type', default='public', choices=['public', 'admin', 'internal'], help=_('Type of the placement endpoint to use. This endpoint ' 'will be looked up in the keystone catalog and should ' 'be one of public, internal or admin.')), ] def register_placement_opts(cfg=cfg.CONF): cfg.register_opts(placement_opts, group=PLACEMENT_CONF_SECTION) neutron-12.1.1/neutron/conf/services/0000775000175000017500000000000013553660156017564 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/services/extdns_designate_driver.py0000664000175000017500000000551413553660047025045 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneauth1 import loading from oslo_config import cfg from neutron._i18n import _ designate_opts = [ cfg.StrOpt('url', help=_('URL for connecting to designate')), cfg.StrOpt('admin_username', help=_('Username for connecting to designate in admin ' 'context')), cfg.StrOpt('admin_password', help=_('Password for connecting to designate in admin ' 'context'), secret=True), cfg.StrOpt('admin_tenant_id', help=_('Tenant id for connecting to designate in admin ' 'context')), cfg.StrOpt('admin_tenant_name', help=_('Tenant name for connecting to designate in admin ' 'context')), cfg.StrOpt('admin_auth_url', help=_('Authorization URL for connecting to designate in admin ' 'context')), cfg.BoolOpt('allow_reverse_dns_lookup', default=True, help=_('Allow the creation of PTR records')), cfg.IntOpt('ipv4_ptr_zone_prefix_size', default=24, help=_('Number of bits in an ipv4 PTR zone that will be considered ' 'network prefix. It has to align to byte boundary. Minimum ' 'value is 8. Maximum value is 24. As a consequence, range ' 'of values is 8, 16 and 24')), cfg.IntOpt('ipv6_ptr_zone_prefix_size', default=120, help=_('Number of bits in an ipv6 PTR zone that will be considered ' 'network prefix. It has to align to nyble boundary. Minimum ' 'value is 4. Maximum value is 124. As a consequence, range ' 'of values is 4, 8, 12, 16,..., 124')), cfg.StrOpt('ptr_zone_email', default='', help=_('The email address to be used when creating PTR zones. ' 'If not specified, the email address will be ' 'admin@')), ] def register_designate_opts(CONF=cfg.CONF): CONF.register_opts(designate_opts, 'designate') loading.register_auth_conf_options(CONF, 'designate') loading.register_session_conf_options(conf=CONF, group='designate', deprecated_opts={'cafile': [cfg.DeprecatedOpt('ca_cert')]}) neutron-12.1.1/neutron/conf/services/metering_agent.py0000664000175000017500000000224713553660046023131 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ metering_agent_opts = [ cfg.StrOpt('driver', default='neutron.services.metering.drivers.noop.' 'noop_driver.NoopMeteringDriver', help=_("Metering driver")), cfg.IntOpt('measure_interval', default=30, help=_("Interval between two metering measures")), cfg.IntOpt('report_interval', default=300, help=_("Interval between two metering reports")), ] def register_metering_agent_opts(cfg=cfg.CONF): cfg.register_opts(metering_agent_opts) neutron-12.1.1/neutron/conf/services/logging.py0000664000175000017500000000226713553660046021571 0ustar zuulzuul00000000000000# Copyright 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from oslo_config import cfg from neutron._i18n import _ log_driver_opts = [ cfg.IntOpt( 'rate_limit', default=100, min=100, help=_('Maximum packets logging per second.')), cfg.IntOpt( 'burst_limit', default=25, min=25, help=_('Maximum number of packets per rate_limit.')), cfg.StrOpt( 'local_output_log_base', help=_('Output logfile path on agent side, default syslog file.')), ] def register_log_driver_opts(cfg=cfg.CONF): cfg.register_opts(log_driver_opts, 'network_log') neutron-12.1.1/neutron/conf/services/__init__.py0000664000175000017500000000000013553660046021661 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/services/provider_configuration.py0000664000175000017500000000207113553660046024715 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ serviceprovider_opts = [ cfg.MultiStrOpt('service_provider', default=[], help=_('Defines providers for advanced services ' 'using the format: ' '::[:default]')) ] def register_service_provider_opts(cfg=cfg.CONF): cfg.register_opts(serviceprovider_opts, 'service_providers') neutron-12.1.1/neutron/conf/plugins/0000775000175000017500000000000013553660156017422 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/plugins/__init__.py0000664000175000017500000000000013553660046021517 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/plugins/ml2/0000775000175000017500000000000013553660156020114 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/plugins/ml2/drivers/0000775000175000017500000000000013553660156021572 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/plugins/ml2/drivers/agent.py0000664000175000017500000000324013553660046023237 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ agent_opts = [ cfg.IntOpt('polling_interval', default=2, help=_("The number of seconds the agent will wait between " "polling for local device changes.")), cfg.IntOpt('quitting_rpc_timeout', default=10, help=_("Set new timeout in seconds for new rpc calls after " "agent receives SIGTERM. If value is set to 0, rpc " "timeout won't be changed")), cfg.IntOpt('dscp', min=0, max=63, help=_("The DSCP value to use for outer headers during tunnel " "encapsulation.")), cfg.BoolOpt('dscp_inherit', default=False, help=_("If set to True, the DSCP value of tunnel " "interfaces is overwritten and set to inherit. " "The DSCP value of the inner header is then " "copied to the outer header.")), ] def register_agent_opts(cfg=cfg.CONF): cfg.register_opts(agent_opts, "AGENT") neutron-12.1.1/neutron/conf/plugins/ml2/drivers/macvtap.py0000664000175000017500000000267213553660046023604 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ DEFAULT_INTERFACE_MAPPINGS = [] macvtap_opts = [ cfg.ListOpt('physical_interface_mappings', default=DEFAULT_INTERFACE_MAPPINGS, help=_("Comma-separated list of " ": tuples " "mapping physical network names to the agent's " "node-specific physical network interfaces to be used " "for flat and VLAN networks. All physical networks " "listed in network_vlan_ranges on the server should " "have mappings to appropriate interfaces on each " "agent.")), ] def register_macvtap_opts(cfg=cfg.CONF): cfg.register_opts(macvtap_opts, "macvtap") neutron-12.1.1/neutron/conf/plugins/ml2/drivers/linuxbridge.py0000664000175000017500000001366413553660046024470 0ustar zuulzuul00000000000000# Copyright 2012 Cisco Systems, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ DEFAULT_BRIDGE_MAPPINGS = [] DEFAULT_INTERFACE_MAPPINGS = [] DEFAULT_VXLAN_GROUP = '224.0.0.1' DEFAULT_KERNEL_HZ_VALUE = 250 # [Hz] DEFAULT_TC_TBF_LATENCY = 50 # [ms] vxlan_opts = [ cfg.BoolOpt('enable_vxlan', default=True, help=_("Enable VXLAN on the agent. Can be enabled when " "agent is managed by ml2 plugin using linuxbridge " "mechanism driver")), cfg.IntOpt('ttl', help=_("TTL for vxlan interface protocol packets.")), cfg.IntOpt('tos', deprecated_for_removal=True, help=_("TOS for vxlan interface protocol packets. This option " "is deprecated in favor of the dscp option in the AGENT " "section and will be removed in a future release. " "To convert the TOS value to DSCP, divide by 4.")), cfg.StrOpt('vxlan_group', default=DEFAULT_VXLAN_GROUP, help=_("Multicast group(s) for vxlan interface. A range of " "group addresses may be specified by using CIDR " "notation. Specifying a range allows different VNIs to " "use different group addresses, reducing or eliminating " "spurious broadcast traffic to the tunnel endpoints. " "To reserve a unique group for each possible " "(24-bit) VNI, use a /8 such as 239.0.0.0/8. This " "setting must be the same on all the agents.")), cfg.IPOpt('local_ip', help=_("IP address of local overlay (tunnel) network endpoint. " "Use either an IPv4 or IPv6 address that resides on one " "of the host network interfaces. The IP version of this " "value must match the value of the 'overlay_ip_version' " "option in the ML2 plug-in configuration file on the " "neutron server node(s).")), cfg.PortOpt('udp_srcport_min', default=0, help=_("The minimum of the UDP source port range used for " "VXLAN communication.")), cfg.PortOpt('udp_srcport_max', default=0, help=_("The maximum of the UDP source port range used for " "VXLAN communication.")), cfg.PortOpt('udp_dstport', help=_("The UDP port used for VXLAN communication. By " "default, the Linux kernel doesn't use the IANA " "assigned standard value, so if you want to use it, " "this option must be set to 4789. It is not set by " "default because of backward compatibiltiy.")), cfg.BoolOpt('l2_population', default=False, help=_("Extension to use alongside ml2 plugin's l2population " "mechanism driver. It enables the plugin to populate " "VXLAN forwarding table.")), cfg.BoolOpt('arp_responder', default=False, help=_("Enable local ARP responder which provides local " "responses instead of performing ARP broadcast into " "the overlay. Enabling local ARP responder is not " "fully compatible with the allowed-address-pairs " "extension.") ), cfg.ListOpt('multicast_ranges', default=[], help=_("Optional comma-separated list of " ":: triples " "describing how to assign a multicast address to " "VXLAN according to its VNI ID.")), ] bridge_opts = [ cfg.ListOpt('physical_interface_mappings', default=DEFAULT_INTERFACE_MAPPINGS, help=_("Comma-separated list of " ": tuples " "mapping physical network names to the agent's " "node-specific physical network interfaces to be used " "for flat and VLAN networks. All physical networks " "listed in network_vlan_ranges on the server should " "have mappings to appropriate interfaces on each " "agent.")), cfg.ListOpt('bridge_mappings', default=DEFAULT_BRIDGE_MAPPINGS, help=_("List of :")), ] qos_options = [ cfg.IntOpt('kernel_hz', default=DEFAULT_KERNEL_HZ_VALUE, help=_("Value of host kernel tick rate (hz) for calculating " "minimum burst value in bandwidth limit rules for " "a port with QoS. See kernel configuration file for " "HZ value and tc-tbf manual for more information.")), cfg.IntOpt('tbf_latency', default=DEFAULT_TC_TBF_LATENCY, help=_("Value of latency (ms) for calculating size of queue " "for a port with QoS. See tc-tbf manual for more " "information.")) ] def register_linuxbridge_opts(cfg=cfg.CONF): cfg.register_opts(vxlan_opts, "VXLAN") cfg.register_opts(bridge_opts, "LINUX_BRIDGE") cfg.register_opts(qos_options, "QOS") neutron-12.1.1/neutron/conf/plugins/ml2/drivers/l2pop.py0000664000175000017500000000176113553660047023204 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ l2_population_options = [ cfg.IntOpt('agent_boot_time', default=180, help=_('Delay within which agent is expected to update ' 'existing ports when it restarts')), ] def register_l2_population_opts(cfg=cfg.CONF): cfg.register_opts(l2_population_options, "l2pop") neutron-12.1.1/neutron/conf/plugins/ml2/drivers/driver_type.py0000664000175000017500000000743713553660046024511 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as p_const from oslo_config import cfg from neutron._i18n import _ gre_opts = [ cfg.ListOpt('tunnel_id_ranges', default=[], help=_("Comma-separated list of : tuples " "enumerating ranges of GRE tunnel IDs that are " "available for tenant network allocation")) ] flat_opts = [ cfg.ListOpt('flat_networks', default='*', help=_("List of physical_network names with which flat " "networks can be created. Use default '*' to allow " "flat networks with arbitrary physical_network names. " "Use an empty list to disable flat networks.")) ] geneve_opts = [ cfg.ListOpt('vni_ranges', default=[], help=_("Comma-separated list of : tuples " "enumerating ranges of Geneve VNI IDs that are " "available for tenant network allocation")), cfg.IntOpt('max_header_size', default=p_const.GENEVE_ENCAP_MIN_OVERHEAD, help=_("Geneve encapsulation header size is dynamic, this " "value is used to calculate the maximum MTU " "for the driver. " "This is the sum of the sizes of the outer " "ETH + IP + UDP + GENEVE header sizes. " "The default size for this field is 50, which is the " "size of the Geneve header without any additional " "option headers.")), ] vxlan_opts = [ cfg.ListOpt('vni_ranges', default=[], help=_("Comma-separated list of : tuples " "enumerating ranges of VXLAN VNI IDs that are " "available for tenant network allocation")), cfg.StrOpt('vxlan_group', help=_("Multicast group for VXLAN. When configured, will " "enable sending all broadcast traffic to this multicast " "group. When left unconfigured, will disable multicast " "VXLAN mode.")), ] vlan_opts = [ cfg.ListOpt('network_vlan_ranges', default=[], help=_("List of :: or " " specifying physical_network names " "usable for VLAN provider and tenant networks, as " "well as ranges of VLAN tags on each available for " "allocation to tenant networks.")) ] def register_ml2_drivers_gre_opts(cfg=cfg.CONF): cfg.register_opts(gre_opts, "ml2_type_gre") def register_ml2_drivers_flat_opts(cfg=cfg.CONF): cfg.register_opts(flat_opts, "ml2_type_flat") def register_ml2_drivers_geneve_opts(cfg=cfg.CONF): cfg.register_opts(geneve_opts, "ml2_type_geneve") def register_ml2_drivers_vxlan_opts(cfg=cfg.CONF): cfg.register_opts(vxlan_opts, "ml2_type_vxlan") def register_ml2_drivers_vlan_opts(cfg=cfg.CONF): cfg.register_opts(vlan_opts, "ml2_type_vlan") neutron-12.1.1/neutron/conf/plugins/ml2/drivers/ovs_conf.py0000664000175000017500000001751413553660047023767 0ustar zuulzuul00000000000000# Copyright 2012 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as n_const from oslo_config import cfg from neutron._i18n import _ from neutron.plugins.ml2.drivers.openvswitch.agent.common \ import constants DEFAULT_BRIDGE_MAPPINGS = [] DEFAULT_TUNNEL_TYPES = [] ovs_opts = [ cfg.StrOpt('integration_bridge', default='br-int', help=_("Integration bridge to use. " "Do not change this parameter unless you have a good " "reason to. This is the name of the OVS integration " "bridge. There is one per hypervisor. The integration " "bridge acts as a virtual 'patch bay'. All VM VIFs are " "attached to this bridge and then 'patched' according " "to their network connectivity.")), cfg.StrOpt('tunnel_bridge', default='br-tun', help=_("Tunnel bridge to use.")), cfg.StrOpt('int_peer_patch_port', default='patch-tun', help=_("Peer patch port in integration bridge for tunnel " "bridge.")), cfg.StrOpt('tun_peer_patch_port', default='patch-int', help=_("Peer patch port in tunnel bridge for integration " "bridge.")), cfg.IPOpt('local_ip', help=_("IP address of local overlay (tunnel) network endpoint. " "Use either an IPv4 or IPv6 address that resides on one " "of the host network interfaces. The IP version of this " "value must match the value of the 'overlay_ip_version' " "option in the ML2 plug-in configuration file on the " "neutron server node(s).")), cfg.ListOpt('bridge_mappings', default=DEFAULT_BRIDGE_MAPPINGS, help=_("Comma-separated list of : " "tuples mapping physical network names to the agent's " "node-specific Open vSwitch bridge names to be used " "for flat and VLAN networks. The length of bridge " "names should be no more than 11. Each bridge must " "exist, and should have a physical network interface " "configured as a port. All physical networks " "configured on the server should have mappings to " "appropriate bridges on each agent. " "Note: If you remove a bridge from this " "mapping, make sure to disconnect it from the " "integration bridge as it won't be managed by the " "agent anymore.")), cfg.BoolOpt('use_veth_interconnection', default=False, help=_("Use veths instead of patch ports to interconnect the " "integration bridge to physical networks. " "Support kernel without Open vSwitch patch port " "support so long as it is set to True.")), cfg.StrOpt('of_interface', default='native', deprecated_for_removal=True, choices=['ovs-ofctl', 'native'], help=_("OpenFlow interface to use.")), cfg.StrOpt('datapath_type', default=constants.OVS_DATAPATH_SYSTEM, choices=[constants.OVS_DATAPATH_SYSTEM, constants.OVS_DATAPATH_NETDEV], help=_("OVS datapath to use. 'system' is the default value and " "corresponds to the kernel datapath. To enable the " "userspace datapath set this value to 'netdev'.")), cfg.StrOpt('vhostuser_socket_dir', default=constants.VHOST_USER_SOCKET_DIR, help=_("OVS vhost-user socket directory.")), cfg.IPOpt('of_listen_address', default='127.0.0.1', help=_("Address to listen on for OpenFlow connections. " "Used only for 'native' driver.")), cfg.PortOpt('of_listen_port', default=6633, help=_("Port to listen on for OpenFlow connections. " "Used only for 'native' driver.")), cfg.IntOpt('of_connect_timeout', default=300, help=_("Timeout in seconds to wait for " "the local switch connecting the controller. " "Used only for 'native' driver.")), cfg.IntOpt('of_request_timeout', default=300, help=_("Timeout in seconds to wait for a single " "OpenFlow request. " "Used only for 'native' driver.")), cfg.IntOpt('of_inactivity_probe', default=10, help=_("The inactivity_probe interval in seconds for the local " "switch connection to the controller. " "A value of 0 disables inactivity probes. " "Used only for 'native' driver.")), ] agent_opts = [ cfg.BoolOpt('minimize_polling', default=True, help=_("Minimize polling by monitoring ovsdb for interface " "changes.")), cfg.IntOpt('ovsdb_monitor_respawn_interval', default=constants.DEFAULT_OVSDBMON_RESPAWN, help=_("The number of seconds to wait before respawning the " "ovsdb monitor after losing communication with it.")), cfg.ListOpt('tunnel_types', default=DEFAULT_TUNNEL_TYPES, help=_("Network types supported by the agent " "(gre and/or vxlan).")), cfg.PortOpt('vxlan_udp_port', default=n_const.VXLAN_UDP_PORT, help=_("The UDP port to use for VXLAN tunnels.")), cfg.IntOpt('veth_mtu', default=9000, help=_("MTU size of veth interfaces")), cfg.BoolOpt('l2_population', default=False, help=_("Use ML2 l2population mechanism driver to learn " "remote MAC and IPs and improve tunnel scalability.")), cfg.BoolOpt('arp_responder', default=False, help=_("Enable local ARP responder if it is supported. " "Requires OVS 2.1 and ML2 l2population driver. " "Allows the switch (when supporting an overlay) " "to respond to an ARP request locally without " "performing a costly ARP broadcast into the overlay.")), cfg.BoolOpt('dont_fragment', default=True, help=_("Set or un-set the don't fragment (DF) bit on " "outgoing IP packet carrying GRE/VXLAN tunnel.")), cfg.BoolOpt('enable_distributed_routing', default=False, help=_("Make the l2 agent run in DVR mode.")), cfg.BoolOpt('drop_flows_on_start', default=False, help=_("Reset flow table on start. Setting this to True will " "cause brief traffic interruption.")), cfg.BoolOpt('tunnel_csum', default=False, help=_("Set or un-set the tunnel header checksum on " "outgoing IP packet carrying GRE/VXLAN tunnel.")), cfg.StrOpt('agent_type', default=n_const.AGENT_TYPE_OVS, deprecated_for_removal=True, help=_("Selects the Agent Type reported")) ] def register_ovs_agent_opts(cfg=cfg.CONF): cfg.register_opts(ovs_opts, "OVS") cfg.register_opts(agent_opts, "AGENT") neutron-12.1.1/neutron/conf/plugins/ml2/drivers/__init__.py0000664000175000017500000000000013553660046023667 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/plugins/ml2/drivers/mech_sriov/0000775000175000017500000000000013553660156023730 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/plugins/ml2/drivers/mech_sriov/__init__.py0000664000175000017500000000000013553660046026025 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/plugins/ml2/drivers/mech_sriov/agent_common.py0000664000175000017500000000456713553660047026763 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_config import cfg from neutron._i18n import _ DEFAULT_DEVICE_MAPPINGS = [] DEFAULT_EXCLUDE_DEVICES = [] agent_opts = [ cfg.IntOpt('polling_interval', default=2, help=_("The number of seconds the agent will wait between " "polling for local device changes.")), ] sriov_nic_opts = [ cfg.ListOpt('physical_device_mappings', default=DEFAULT_DEVICE_MAPPINGS, help=_("Comma-separated list of " ": tuples mapping " "physical network names to the agent's node-specific " "physical network device interfaces of SR-IOV physical " "function to be used for VLAN networks. All physical " "networks listed in network_vlan_ranges on the server " "should have mappings to appropriate interfaces on " "each agent.")), cfg.ListOpt('exclude_devices', default=DEFAULT_EXCLUDE_DEVICES, help=_("Comma-separated list of " ": tuples, mapping " "network_device to the agent's node-specific list of " "virtual functions that should not be used for virtual " "networking. vfs_to_exclude is a semicolon-separated " "list of virtual functions to exclude from " "network_device. The network_device in the mapping " "should appear in the physical_device_mappings " "list.")), ] def register_agent_sriov_nic_opts(cfg=cfg.CONF): cfg.register_opts(agent_opts, 'AGENT') cfg.register_opts(sriov_nic_opts, 'SRIOV_NIC') neutron-12.1.1/neutron/conf/plugins/ml2/config.py0000664000175000017500000000722113553660046021733 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ ml2_opts = [ cfg.ListOpt('type_drivers', default=['local', 'flat', 'vlan', 'gre', 'vxlan', 'geneve'], help=_("List of network type driver entrypoints to be loaded " "from the neutron.ml2.type_drivers namespace.")), cfg.ListOpt('tenant_network_types', default=['local'], help=_("Ordered list of network_types to allocate as tenant " "networks. The default value 'local' is useful for " "single-box testing but provides no connectivity " "between hosts.")), cfg.ListOpt('mechanism_drivers', default=[], help=_("An ordered list of networking mechanism driver " "entrypoints to be loaded from the " "neutron.ml2.mechanism_drivers namespace.")), cfg.ListOpt('extension_drivers', default=[], help=_("An ordered list of extension driver " "entrypoints to be loaded from the " "neutron.ml2.extension_drivers namespace. " "For example: extension_drivers = port_security,qos")), cfg.IntOpt('path_mtu', default=0, help=_('Maximum size of an IP packet (MTU) that can traverse ' 'the underlying physical network infrastructure without ' 'fragmentation when using an overlay/tunnel protocol. ' 'This option allows specifying a physical network MTU ' 'value that differs from the default global_physnet_mtu ' 'value.')), cfg.ListOpt('physical_network_mtus', default=[], help=_("A list of mappings of physical networks to MTU " "values. The format of the mapping is " ":. This mapping allows " "specifying a physical network MTU value that " "differs from the default global_physnet_mtu value.")), cfg.StrOpt('external_network_type', help=_("Default network type for external networks when no " "provider attributes are specified. By default it is " "None, which means that if provider attributes are not " "specified while creating external networks then they " "will have the same type as tenant networks. Allowed " "values for external_network_type config option depend " "on the network type values configured in type_drivers " "config option.")), cfg.IntOpt('overlay_ip_version', default=4, help=_("IP version of all overlay (tunnel) network endpoints. " "Use a value of 4 for IPv4 or 6 for IPv6.")) ] def register_ml2_plugin_opts(cfg=cfg.CONF): cfg.register_opts(ml2_opts, "ml2") neutron-12.1.1/neutron/conf/plugins/ml2/__init__.py0000664000175000017500000000000013553660046022211 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/agent/0000775000175000017500000000000013553660156017037 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/agent/common.py0000664000175000017500000002142613553660047020705 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import shlex from oslo_config import cfg from oslo_privsep import priv_context from neutron._i18n import _ from neutron.common import config EXTERNAL_PROCESS_OPTS = [ cfg.StrOpt('external_pids', default='$state_path/external/pids', help=_('Location to store child pid files')), ] PD_OPTS = [ cfg.StrOpt('pd_dhcp_driver', default='dibbler', help=_('Service to handle DHCPv6 Prefix delegation.')), ] PD_DRIVER_OPTS = [ cfg.StrOpt('pd_confs', default='$state_path/pd', help=_('Location to store IPv6 PD files.')), cfg.StrOpt('vendor_pen', default='8888', help=_("A decimal value as Vendor's Registered Private " "Enterprise Number as required by RFC3315 DUID-EN.")), ] INTERFACE_OPTS = [ cfg.StrOpt('ovs_integration_bridge', default='br-int', help=_('Name of Open vSwitch bridge to use')), cfg.BoolOpt('ovs_use_veth', default=False, help=_("Uses veth for an OVS interface or not. " "Support kernels with limited namespace support " "(e.g. RHEL 6.5) and rate limiting on router's gateway " "port so long as ovs_use_veth is set to " "True.")), ] RA_OPTS = [ cfg.StrOpt('ra_confs', default='$state_path/ra', help=_('Location to store IPv6 RA config files')), cfg.IntOpt('min_rtr_adv_interval', default=30, help=_('MinRtrAdvInterval setting for radvd.conf')), cfg.IntOpt('max_rtr_adv_interval', default=100, help=_('MaxRtrAdvInterval setting for radvd.conf')), ] ROOT_HELPER_OPTS = [ cfg.StrOpt('root_helper', default='sudo', help=_("Root helper application. " "Use 'sudo neutron-rootwrap /etc/neutron/rootwrap.conf' " "to use the real root filter facility. Change to 'sudo' " "to skip the filtering and just run the command " "directly.")), cfg.BoolOpt('use_helper_for_ns_read', default=True, help=_("Use the root helper when listing the namespaces on a " "system. This may not be required depending on the " "security configuration. If the root helper is " "not required, set this to False for a performance " "improvement.")), # We can't just use root_helper=sudo neutron-rootwrap-daemon $cfg because # it isn't appropriate for long-lived processes spawned with create_process # Having a bool use_rootwrap_daemon option precludes specifying the # rootwrap daemon command, which may be necessary for Xen? cfg.StrOpt('root_helper_daemon', help=_("Root helper daemon application to use when possible. " "For the agent which needs to execute commands in Dom0 " "in the hypervisor of XenServer, this item should be " "set to 'xenapi_root_helper', so that it will keep a " "XenAPI session to pass commands to Dom0.")), ] AGENT_STATE_OPTS = [ cfg.FloatOpt('report_interval', default=30, help=_('Seconds between nodes reporting state to server; ' 'should be less than agent_down_time, best if it ' 'is half or less than agent_down_time.')), cfg.BoolOpt('log_agent_heartbeats', default=False, help=_('Log agent heartbeats')), ] INTERFACE_DRIVER_OPTS = [ cfg.StrOpt('interface_driver', help=_("The driver used to manage the virtual interface.")), ] IPTABLES_OPTS = [ cfg.BoolOpt('comment_iptables_rules', default=True, help=_("Add comments to iptables rules. " "Set to false to disallow the addition of comments to " "generated iptables rules that describe each rule's " "purpose. System must support the iptables comments " "module for addition of comments.")), cfg.BoolOpt('debug_iptables_rules', default=False, help=_("Duplicate every iptables difference calculation to " "ensure the format being generated matches the format " "of iptables-save. This option should not be turned " "on for production systems because it imposes a " "performance penalty.")), ] PROCESS_MONITOR_OPTS = [ cfg.StrOpt('check_child_processes_action', default='respawn', choices=['respawn', 'exit'], help=_('Action to be executed when a child process dies')), cfg.IntOpt('check_child_processes_interval', default=60, help=_('Interval between checks of child process liveness ' '(seconds), use 0 to disable')), ] AVAILABILITY_ZONE_OPTS = [ # The default AZ name "nova" is selected to match the default # AZ name in Nova and Cinder. cfg.StrOpt('availability_zone', max_length=255, default='nova', help=_("Availability zone of this node")), ] EXT_NET_BRIDGE_OPTS = [ cfg.StrOpt('external_network_bridge', default='', deprecated_for_removal=True, help=_("Name of bridge used for external network " "traffic. When this parameter is set, the L3 agent will " "plug an interface directly into an external bridge " "which will not allow any wiring by the L2 agent. Using " "this will result in incorrect port statuses. This " "option is deprecated and will be removed in Ocata.")) ] def get_log_args(conf, log_file_name, **kwargs): cmd_args = [] if conf.debug: cmd_args.append('--debug') if (conf.log_dir or conf.log_file): cmd_args.append('--log-file=%s' % log_file_name) log_dir = None if conf.log_dir and conf.log_file: log_dir = os.path.dirname( os.path.join(conf.log_dir, conf.log_file)) elif conf.log_dir: log_dir = conf.log_dir elif conf.log_file: log_dir = os.path.dirname(conf.log_file) if log_dir: cmd_args.append('--log-dir=%s' % log_dir) else: if conf.use_syslog: cmd_args.append('--use-syslog') if conf.syslog_log_facility: cmd_args.append( '--syslog-log-facility=%s' % conf.syslog_log_facility) return cmd_args def register_external_process_opts(cfg=cfg.CONF): cfg.register_opts(EXTERNAL_PROCESS_OPTS) def register_pd_opts(cfg=cfg.CONF): cfg.register_opts(PD_OPTS) def register_pddriver_opts(cfg=cfg.CONF): cfg.register_opts(PD_DRIVER_OPTS) def register_interface_opts(cfg=cfg.CONF): cfg.register_opts(INTERFACE_OPTS) def register_ra_opts(cfg=cfg.CONF): cfg.register_opts(RA_OPTS) def register_root_helper(conf=cfg.CONF): conf.register_opts(ROOT_HELPER_OPTS, 'AGENT') def register_agent_state_opts_helper(conf): conf.register_opts(AGENT_STATE_OPTS, 'AGENT') def register_interface_driver_opts_helper(conf): conf.register_opts(INTERFACE_DRIVER_OPTS) def register_iptables_opts(conf): conf.register_opts(IPTABLES_OPTS, 'AGENT') def register_process_monitor_opts(conf): conf.register_opts(PROCESS_MONITOR_OPTS, 'AGENT') def register_availability_zone_opts_helper(conf): conf.register_opts(AVAILABILITY_ZONE_OPTS, 'AGENT') def get_root_helper(conf): return conf.AGENT.root_helper def setup_conf(): bind_opts = [ cfg.StrOpt('state_path', default='/var/lib/neutron', help=_("Where to store Neutron state files. " "This directory must be writable by the agent.")), ] conf = cfg.ConfigOpts() conf.register_opts(bind_opts) return conf # add a logging setup method here for convenience setup_logging = config.setup_logging def setup_privsep(): priv_context.init(root_helper=shlex.split(get_root_helper(cfg.CONF))) neutron-12.1.1/neutron/conf/agent/cmd.py0000664000175000017500000000342313553660047020155 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ from neutron.agent.linux import ipset_manager ip_opts = [ cfg.BoolOpt('allsets', default=False, help=_('Destroy all IPsets.')), cfg.BoolOpt('force', default=False, help=_('Destroy IPsets even if there is an iptables ' 'reference.')), cfg.StrOpt('prefix', default=ipset_manager.NET_PREFIX, help=_('String prefix used to match IPset names.')), ] netns_opts = [ cfg.BoolOpt('force', default=False, help=_('Delete the namespace by removing all devices.')), cfg.StrOpt('agent-type', choices=['dhcp', 'l3', 'lbaas'], help=_('Cleanup resources of a specific agent type only.')), ] ovs_opts = [ cfg.BoolOpt('ovs_all_ports', default=False, help=_('True to delete all ports on all the OpenvSwitch ' 'bridges. False to delete ports created by ' 'Neutron on integration and external network ' 'bridges.')) ] def register_cmd_opts(opts, cfg=cfg.CONF): cfg.register_cli_opts(opts) neutron-12.1.1/neutron/conf/agent/metadata/0000775000175000017500000000000013553660156020617 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/agent/metadata/config.py0000664000175000017500000001104213553660046022432 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.utils import host from oslo_config import cfg from neutron._i18n import _ DEDUCE_MODE = 'deduce' USER_MODE = 'user' GROUP_MODE = 'group' ALL_MODE = 'all' SOCKET_MODES = (DEDUCE_MODE, USER_MODE, GROUP_MODE, ALL_MODE) SHARED_OPTS = [ cfg.StrOpt('metadata_proxy_socket', default='$state_path/metadata_proxy', help=_('Location for Metadata Proxy UNIX domain socket.')), cfg.StrOpt('metadata_proxy_user', default='', help=_("User (uid or name) running metadata proxy after " "its initialization (if empty: agent effective " "user).")), cfg.StrOpt('metadata_proxy_group', default='', help=_("Group (gid or name) running metadata proxy after " "its initialization (if empty: agent effective " "group).")) ] METADATA_PROXY_HANDLER_OPTS = [ cfg.StrOpt('auth_ca_cert', help=_("Certificate Authority public key (CA cert) " "file for ssl")), cfg.HostAddressOpt('nova_metadata_host', default='127.0.0.1', help=_("IP address or DNS name of Nova metadata " "server.")), cfg.PortOpt('nova_metadata_port', default=8775, help=_("TCP Port used by Nova metadata server.")), cfg.StrOpt('metadata_proxy_shared_secret', default='', help=_('When proxying metadata requests, Neutron signs the ' 'Instance-ID header with a shared secret to prevent ' 'spoofing. You may select any string for a secret, ' 'but it must match here and in the configuration used ' 'by the Nova Metadata Server. NOTE: Nova uses the same ' 'config key, but in [neutron] section.'), secret=True), cfg.StrOpt('nova_metadata_protocol', default='http', choices=['http', 'https'], help=_("Protocol to access nova metadata, http or https")), cfg.BoolOpt('nova_metadata_insecure', default=False, help=_("Allow to perform insecure SSL (https) requests to " "nova metadata")), cfg.StrOpt('nova_client_cert', default='', help=_("Client certificate for nova metadata api server.")), cfg.StrOpt('nova_client_priv_key', default='', help=_("Private key of client certificate.")) ] UNIX_DOMAIN_METADATA_PROXY_OPTS = [ cfg.StrOpt('metadata_proxy_socket_mode', default=DEDUCE_MODE, choices=SOCKET_MODES, help=_("Metadata Proxy UNIX domain socket mode, 4 values " "allowed: " "'deduce': deduce mode from metadata_proxy_user/group " "values, " "'user': set metadata proxy socket mode to 0o644, to " "use when metadata_proxy_user is agent effective user " "or root, " "'group': set metadata proxy socket mode to 0o664, to " "use when metadata_proxy_group is agent effective " "group or root, " "'all': set metadata proxy socket mode to 0o666, to use " "otherwise.")), cfg.IntOpt('metadata_workers', default=host.cpu_count() // 2, sample_default=' / 2', help=_('Number of separate worker processes for metadata ' 'server (defaults to half of the number of CPUs)')), cfg.IntOpt('metadata_backlog', default=4096, help=_('Number of backlog requests to configure the ' 'metadata server socket with')) ] def register_meta_conf_opts(opts, cfg=cfg.CONF): cfg.register_opts(opts) neutron-12.1.1/neutron/conf/agent/metadata/__init__.py0000664000175000017500000000000013553660046022714 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/agent/securitygroups_rpc.py0000664000175000017500000000333713553660046023370 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from oslo_config import cfg from neutron._i18n import _ security_group_opts = [ cfg.StrOpt( 'firewall_driver', help=_('Driver for security groups firewall in the L2 agent')), cfg.BoolOpt( 'enable_security_group', default=True, help=_( 'Controls whether the neutron security group API is enabled ' 'in the server. It should be false when using no security ' 'groups or using the nova security group API.')), cfg.BoolOpt( 'enable_ipset', default=True, help=_('Use ipset to speed-up the iptables based security groups. ' 'Enabling ipset support requires that ipset is installed on L2 ' 'agent node.')), cfg.ListOpt( 'permitted_ethertypes', default=[], help=_('Comma-separated list of ethertypes to be permitted, in ' 'hexadecimal (starting with "0x"). For example, "0x4008" ' 'to permit InfiniBand.')) ] def register_securitygroups_opts(cfg=cfg.CONF): cfg.register_opts(security_group_opts, 'SECURITYGROUP') neutron-12.1.1/neutron/conf/agent/xenapi_conf.py0000664000175000017500000000237413553660046021706 0ustar zuulzuul00000000000000# Copyright 2016 Citrix Systems. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ XENAPI_CONF_SECTION = 'xenapi' XENAPI_OPTS = [ cfg.StrOpt('connection_url', help=_("URL for connection to XenServer/Xen Cloud Platform.")), cfg.StrOpt('connection_username', help=_("Username for connection to XenServer/Xen Cloud " "Platform.")), cfg.StrOpt('connection_password', help=_("Password for connection to XenServer/Xen Cloud " "Platform."), secret=True) ] def register_xenapi_opts(cfg=cfg.CONF): cfg.register_opts(XENAPI_OPTS, group=XENAPI_CONF_SECTION) neutron-12.1.1/neutron/conf/agent/agent_extensions_manager.py0000664000175000017500000000155513553660046024464 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ AGENT_EXT_MANAGER_OPTS = [ cfg.ListOpt('extensions', default=[], help=_('Extensions list to use')), ] def register_agent_ext_manager_opts(cfg=cfg.CONF): cfg.register_opts(AGENT_EXT_MANAGER_OPTS, 'agent') neutron-12.1.1/neutron/conf/agent/linux.py0000664000175000017500000000165213553660046020552 0ustar zuulzuul00000000000000# Copyright 2017 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron._i18n import _ from oslo_config import cfg IP_LIB_OPTS_LINUX = [ cfg.BoolOpt('ip_lib_force_root', default=False, help=_('Force ip_lib calls to use the root helper')), ] def register_iplib_opts(cfg=cfg.CONF): cfg.register_opts(IP_LIB_OPTS_LINUX) neutron-12.1.1/neutron/conf/agent/dhcp.py0000664000175000017500000001260513553660047020332 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ DHCP_AGENT_OPTS = [ cfg.IntOpt('resync_interval', default=5, help=_("The DHCP agent will resync its state with Neutron to " "recover from any transient notification or RPC errors. " "The interval is number of seconds between attempts.")), cfg.StrOpt('dhcp_driver', default='neutron.agent.linux.dhcp.Dnsmasq', help=_("The driver used to manage the DHCP server.")), cfg.BoolOpt('enable_isolated_metadata', default=False, help=_("The DHCP server can assist with providing metadata " "support on isolated networks. Setting this value to " "True will cause the DHCP server to append specific " "host routes to the DHCP request. The metadata service " "will only be activated when the subnet does not " "contain any router port. The guest instance must be " "configured to request host routes via DHCP (Option " "121). This option doesn't have any effect when " "force_metadata is set to True.")), cfg.BoolOpt('force_metadata', default=False, help=_("In some cases the Neutron router is not present to " "provide the metadata IP but the DHCP server can be " "used to provide this info. Setting this value will " "force the DHCP server to append specific host routes " "to the DHCP request. If this option is set, then the " "metadata service will be activated for all the " "networks.")), cfg.BoolOpt('enable_metadata_network', default=False, help=_("Allows for serving metadata requests coming from a " "dedicated metadata access network whose CIDR is " "169.254.169.254/16 (or larger prefix), and is " "connected to a Neutron router from which the VMs send " "metadata:1 request. In this case DHCP Option 121 will " "not be injected in VMs, as they will be able to reach " "169.254.169.254 through a router. This option " "requires enable_isolated_metadata = True.")), cfg.IntOpt('num_sync_threads', default=4, help=_('Number of threads to use during sync process. ' 'Should not exceed connection pool size configured on ' 'server.')) ] DHCP_OPTS = [ cfg.StrOpt('dhcp_confs', default='$state_path/dhcp', help=_('Location to store DHCP server config files.')), ] DNSMASQ_OPTS = [ cfg.StrOpt('dnsmasq_config_file', default='', help=_('Override the default dnsmasq settings ' 'with this file.')), cfg.ListOpt('dnsmasq_dns_servers', default=[], help=_('Comma-separated list of the DNS servers which will be ' 'used as forwarders.')), cfg.StrOpt('dnsmasq_base_log_dir', help=_("Base log dir for dnsmasq logging. " "The log contains DHCP and DNS log information and " "is useful for debugging issues with either DHCP or " "DNS. If this section is null, disable dnsmasq log.")), cfg.BoolOpt('dnsmasq_local_resolv', default=False, help=_("Enables the dnsmasq service to provide name " "resolution for instances via DNS resolvers on the " "host running the DHCP agent. Effectively removes the " "'--no-resolv' option from the dnsmasq process " "arguments. Adding custom DNS resolvers to the " "'dnsmasq_dns_servers' option disables this feature.")), cfg.IntOpt( 'dnsmasq_lease_max', default=(2 ** 24), help=_('Limit number of leases to prevent a denial-of-service.')), cfg.BoolOpt('dhcp_broadcast_reply', default=False, help=_("Use broadcast in DHCP replies.")), cfg.IntOpt('dhcp_renewal_time', default=0, help=_("DHCP renewal time T1 (in seconds). If set to 0, it " "will default to half of the lease time.")), cfg.IntOpt('dhcp_rebinding_time', default=0, help=_("DHCP rebinding time T2 (in seconds). If set to 0, it " "will default to 7/8 of the lease time.")), ] def register_agent_dhcp_opts(cfg=cfg.CONF): cfg.register_opts(DHCP_AGENT_OPTS) cfg.register_opts(DHCP_OPTS) cfg.register_opts(DNSMASQ_OPTS) neutron-12.1.1/neutron/conf/agent/ovs_conf.py0000664000175000017500000000307413553660047021230 0ustar zuulzuul00000000000000# Copyright 2011 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ # Default timeout for ovsdb commands DEFAULT_OVSDB_TIMEOUT = 10 OPTS = [ cfg.IntOpt('ovsdb_timeout', default=DEFAULT_OVSDB_TIMEOUT, deprecated_name='ovs_vsctl_timeout', deprecated_group='DEFAULT', help=_('Timeout in seconds for ovsdb commands. ' 'If the timeout expires, ovsdb commands will fail with ' 'ALARMCLOCK error.')), cfg.IntOpt('bridge_mac_table_size', default=50000, help=_('The maximum number of MAC addresses to learn on ' 'a bridge managed by the Neutron OVS agent. Values ' 'outside a reasonable range (10 to 1,000,000) might be ' 'overridden by Open vSwitch according to the ' 'documentation.')), ] def register_ovs_agent_opts(cfg=cfg.CONF): cfg.register_opts(OPTS, 'OVS') neutron-12.1.1/neutron/conf/agent/__init__.py0000664000175000017500000000000013553660046021134 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/agent/windows.py0000664000175000017500000000122613553660046021102 0ustar zuulzuul00000000000000# Copyright 2017 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. IP_LIB_OPTS_WINDOWS = [] neutron-12.1.1/neutron/conf/agent/ovsdb_api.py0000664000175000017500000000434213553660047021361 0ustar zuulzuul00000000000000# Copyright 2017 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron._i18n import _ from oslo_config import cfg interface_map = { 'vsctl': 'neutron.agent.ovsdb.impl_vsctl', 'native': 'neutron.agent.ovsdb.impl_idl', } API_OPTS = [ cfg.StrOpt('ovsdb_interface', deprecated_for_removal=True, choices=interface_map.keys(), default='native', help=_('The interface for interacting with the OVSDB')), cfg.StrOpt('ovsdb_connection', default='tcp:127.0.0.1:6640', help=_('The connection string for the OVSDB backend. ' 'Will be used by ovsdb-client when monitoring and ' 'used for the all ovsdb commands when native ' 'ovsdb_interface is enabled' )), cfg.StrOpt('ssl_key_file', help=_('The SSL private key file to use when interacting with ' 'OVSDB. Required when using an "ssl:" prefixed ' 'ovsdb_connection' )), cfg.StrOpt('ssl_cert_file', help=_('The SSL certificate file to use when interacting ' 'with OVSDB. Required when using an "ssl:" prefixed ' 'ovsdb_connection' )), cfg.StrOpt('ssl_ca_cert_file', help=_('The Certificate Authority (CA) certificate to use ' 'when interacting with OVSDB. Required when using an ' '"ssl:" prefixed ovsdb_connection' )), ] def register_ovsdb_api_opts(cfg=cfg.CONF): cfg.register_opts(API_OPTS, 'OVS') neutron-12.1.1/neutron/conf/agent/database/0000775000175000017500000000000013553660156020603 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/agent/database/__init__.py0000664000175000017500000000000013553660046022700 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/agent/database/agents_db.py0000664000175000017500000000463413553660046023110 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ AGENT_OPTS = [ cfg.IntOpt('agent_down_time', default=75, help=_("Seconds to regard the agent is down; should be at " "least twice report_interval, to be sure the " "agent is down for good.")), cfg.StrOpt('dhcp_load_type', default='networks', choices=['networks', 'subnets', 'ports'], help=_('Representing the resource type whose load is being ' 'reported by the agent. This can be "networks", ' '"subnets" or "ports". ' 'When specified (Default is networks), the server will ' 'extract particular load sent as part of its agent ' 'configuration object from the agent report state, ' 'which is the number of resources being consumed, at ' 'every report_interval.' 'dhcp_load_type can be used in combination with ' 'network_scheduler_driver = ' 'neutron.scheduler.dhcp_agent_scheduler.WeightScheduler ' 'When the network_scheduler_driver is WeightScheduler, ' 'dhcp_load_type can be configured to represent the ' 'choice for the resource being balanced. ' 'Example: dhcp_load_type=networks')), cfg.BoolOpt('enable_new_agents', default=True, help=_("Agent starts with admin_state_up=False when " "enable_new_agents=False. In the case, user's " "resources will not be scheduled automatically to the " "agent until admin changes admin_state_up to True.")), ] def register_db_agents_opts(conf=cfg.CONF): conf.register_opts(AGENT_OPTS) neutron-12.1.1/neutron/conf/agent/database/agentschedulers_db.py0000664000175000017500000000441313553660047025003 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ AGENTS_SCHEDULER_OPTS = [ cfg.StrOpt('network_scheduler_driver', default='neutron.scheduler.' 'dhcp_agent_scheduler.WeightScheduler', help=_('Driver to use for scheduling network to DHCP agent')), cfg.BoolOpt('network_auto_schedule', default=True, help=_('Allow auto scheduling networks to DHCP agent.')), cfg.BoolOpt('allow_automatic_dhcp_failover', default=True, help=_('Automatically remove networks from offline DHCP ' 'agents.')), cfg.IntOpt('dhcp_agents_per_network', default=1, help=_('Number of DHCP agents scheduled to host a tenant ' 'network. If this number is greater than 1, the ' 'scheduler automatically assigns multiple DHCP agents ' 'for a given tenant network, providing high ' 'availability for DHCP service.')), cfg.BoolOpt('enable_services_on_agents_with_admin_state_down', default=False, help=_('Enable services on an agent with admin_state_up ' 'False. If this option is False, when admin_state_up ' 'of an agent is turned False, services on it will be ' 'disabled. Agents with admin_state_up False are not ' 'selected for automatic scheduling regardless of this ' 'option. But manual scheduling to such agents is ' 'available if this option is True.')), ] def register_db_agentschedulers_opts(conf=cfg.CONF): conf.register_opts(AGENTS_SCHEDULER_OPTS) neutron-12.1.1/neutron/conf/agent/l3/0000775000175000017500000000000013553660156017355 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/agent/l3/config.py0000664000175000017500000001404613553660047021200 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from oslo_config import cfg from neutron._i18n import _ from neutron.common import constants as common_consts from neutron.conf.agent import common as config OPTS = [ cfg.StrOpt('agent_mode', default=constants.L3_AGENT_MODE_LEGACY, choices=(constants.L3_AGENT_MODE_DVR, constants.L3_AGENT_MODE_DVR_SNAT, constants.L3_AGENT_MODE_LEGACY, common_consts.L3_AGENT_MODE_DVR_NO_EXTERNAL), help=_("The working mode for the agent. Allowed modes are: " "'legacy' - this preserves the existing behavior " "where the L3 agent is deployed on a centralized " "networking node to provide L3 services like DNAT, " "and SNAT. Use this mode if you do not want to " "adopt DVR. 'dvr' - this mode enables DVR " "functionality and must be used for an L3 agent " "that runs on a compute host. 'dvr_snat' - this " "enables centralized SNAT support in conjunction " "with DVR. This mode must be used for an L3 agent " "running on a centralized node (or in single-host " "deployments, e.g. devstack). " "'dvr_no_external' - this mode enables only East/West " "DVR routing functionality for a L3 agent that runs on " "a compute host, the North/South functionality such " "as DNAT and SNAT will be provided by the centralized " "network node that is running in 'dvr_snat' mode. " "This mode should be used when there is no " "external network connectivity on the compute host.")), cfg.PortOpt('metadata_port', default=9697, help=_("TCP Port used by Neutron metadata namespace proxy.")), cfg.BoolOpt('handle_internal_only_routers', default=True, help=_("Indicates that this L3 agent should also handle " "routers that do not have an external network gateway " "configured. This option should be True only for a " "single agent in a Neutron deployment, and may be " "False for all agents if all routers must have an " "external network gateway.")), cfg.StrOpt('gateway_external_network_id', default='', help=_("When external_network_bridge is set, each L3 agent can " "be associated with no more than one external network. " "This value should be set to the UUID of that external " "network. To allow L3 agent support multiple external " "networks, both the external_network_bridge and " "gateway_external_network_id must be left empty."), deprecated_for_removal=True), cfg.StrOpt('ipv6_gateway', default='', help=_("With IPv6, the network used for the external gateway " "does not need to have an associated subnet, since the " "automatically assigned link-local address (LLA) can " "be used. However, an IPv6 gateway address is needed " "for use as the next-hop for the default route. " "If no IPv6 gateway address is configured here, " "(and only then) the neutron router will be configured " "to get its default route from router advertisements " "(RAs) from the upstream router; in which case the " "upstream router must also be configured to send " "these RAs. " "The ipv6_gateway, when configured, should be the LLA " "of the interface on the upstream router. If a " "next-hop using a global unique address (GUA) is " "desired, it needs to be done via a subnet allocated " "to the network and not through this parameter. ")), cfg.StrOpt('prefix_delegation_driver', default='dibbler', help=_('Driver used for ipv6 prefix delegation. This needs to ' 'be an entry point defined in the ' 'neutron.agent.linux.pd_drivers namespace. See ' 'setup.cfg for entry points included with the neutron ' 'source.')), cfg.BoolOpt('enable_metadata_proxy', default=True, help=_("Allow running metadata proxy.")), cfg.StrOpt('metadata_access_mark', default='0x1', help=_('Iptables mangle mark used to mark metadata valid ' 'requests. This mark will be masked with 0xffff so ' 'that only the lower 16 bits will be used.')), cfg.StrOpt('external_ingress_mark', default='0x2', help=_('Iptables mangle mark used to mark ingress from ' 'external network. This mark will be masked with ' '0xffff so that only the lower 16 bits will be used.')), ] OPTS += config.EXT_NET_BRIDGE_OPTS def register_l3_agent_config_opts(opts, cfg=cfg.CONF): cfg.register_opts(opts) neutron-12.1.1/neutron/conf/agent/l3/keepalived.py0000664000175000017500000000327613553660046022046 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ CLI_OPTS = [ cfg.StrOpt('router_id', help=_('ID of the router')), cfg.StrOpt('namespace', help=_('Namespace of the router')), cfg.StrOpt('conf_dir', help=_('Path to the router directory')), cfg.StrOpt('monitor_interface', help=_('Interface to monitor')), cfg.StrOpt('monitor_cidr', help=_('CIDR to monitor')), cfg.StrOpt('pid_file', help=_('Path to PID file for this process')), cfg.StrOpt('user', help=_('User (uid or name) running this process ' 'after its initialization')), cfg.StrOpt('group', help=_('Group (gid or name) running this process ' 'after its initialization')) ] OPTS = [ cfg.StrOpt('metadata_proxy_socket', default='$state_path/metadata_proxy', help=_('Location of Metadata Proxy UNIX domain ' 'socket')) ] def register_cli_l3_agent_keepalived_opts(conf=cfg.CONF): conf.register_cli_opts(CLI_OPTS) def register_l3_agent_keepalived_opts(conf=cfg.CONF): conf.register_opts(OPTS) neutron-12.1.1/neutron/conf/agent/l3/__init__.py0000664000175000017500000000000013553660046021452 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/agent/l3/ha.py0000664000175000017500000000515213553660047020321 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.utils import host from oslo_config import cfg from neutron._i18n import _ from neutron.agent.linux import keepalived OPTS = [ cfg.StrOpt('ha_confs_path', default='$state_path/ha_confs', help=_('Location to store keepalived/conntrackd ' 'config files')), cfg.StrOpt('ha_vrrp_auth_type', default='PASS', choices=keepalived.VALID_AUTH_TYPES, help=_('VRRP authentication type')), cfg.StrOpt('ha_vrrp_auth_password', help=_('VRRP authentication password'), secret=True), cfg.IntOpt('ha_vrrp_advert_int', default=2, help=_('The advertisement interval in seconds')), cfg.IntOpt('ha_keepalived_state_change_server_threads', default=(1 + host.cpu_count()) // 2, sample_default='(1 + ) / 2', min=1, help=_('Number of concurrent threads for ' 'keepalived server connection requests. ' 'More threads create a higher CPU load ' 'on the agent node.')), cfg.IntOpt('ha_vrrp_health_check_interval', default=0, help=_('The VRRP health check interval in seconds. Values > 0 ' 'enable VRRP health checks. Setting it to 0 disables ' 'VRRP health checks. Recommended value is 5. ' 'This will cause pings to be sent to the gateway ' 'IP address(es) - requires ICMP_ECHO_REQUEST ' 'to be enabled on the gateway. ' 'If gateway fails, all routers will be reported ' 'as master, and master election will be repeated ' 'in round-robin fashion, until one of the router ' 'restore the gateway connection.')), ] def register_l3_agent_ha_opts(cfg=cfg.CONF): cfg.register_opts(OPTS) neutron-12.1.1/neutron/conf/agent/l2_ext_fdb_population.py0000664000175000017500000000243513553660046023675 0ustar zuulzuul00000000000000# Copyright (c) 2016 Mellanox Technologies, Ltd # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ # if shared_physical_device_mappings is not configured KeyError will be thrown fdb_population_opt = [ cfg.ListOpt('shared_physical_device_mappings', default=[], help=_("Comma-separated list of " ": tuples mapping " "physical network names to the agent's node-specific " "shared physical network device between " "SR-IOV and OVS or SR-IOV and linux bridge")) ] def register_fdb_population_opts(cfg=cfg.CONF): cfg.register_opts(fdb_population_opt, 'FDB') neutron-12.1.1/neutron/conf/quota.py0000664000175000017500000000741513553660046017451 0ustar zuulzuul00000000000000# Copyright 2016 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ QUOTA_DB_MODULE = 'neutron.db.quota.driver' QUOTA_DB_DRIVER = '%s.DbQuotaDriver' % QUOTA_DB_MODULE QUOTA_CONF_DRIVER = 'neutron.quota.ConfDriver' QUOTAS_CFG_GROUP = 'QUOTAS' DEFAULT_QUOTA = -1 DEFAULT_QUOTA_NETWORK = 100 DEFAULT_QUOTA_SUBNET = 100 DEFAULT_QUOTA_PORT = 500 DEFAULT_QUOTA_SG = 10 DEFAULT_QUOTA_SG_RULE = 100 DEFAULT_QUOTA_ROUTER = 10 DEFAULT_QUOTA_FIP = 50 DEFAULT_QUOTA_RBAC = 10 # quota_opts from neutron/quota/__init__.py # renamed quota_opts to core_quota_opts core_quota_opts = [ cfg.IntOpt('default_quota', default=DEFAULT_QUOTA, help=_('Default number of resource allowed per tenant. ' 'A negative value means unlimited.')), cfg.IntOpt('quota_network', default=DEFAULT_QUOTA_NETWORK, help=_('Number of networks allowed per tenant. ' 'A negative value means unlimited.')), cfg.IntOpt('quota_subnet', default=DEFAULT_QUOTA_SUBNET, help=_('Number of subnets allowed per tenant, ' 'A negative value means unlimited.')), cfg.IntOpt('quota_port', default=DEFAULT_QUOTA_PORT, help=_('Number of ports allowed per tenant. ' 'A negative value means unlimited.')), cfg.StrOpt('quota_driver', default=QUOTA_DB_DRIVER, help=_('Default driver to use for quota checks.')), cfg.BoolOpt('track_quota_usage', default=True, help=_('Keep in track in the database of current resource ' 'quota usage. Plugins which do not leverage the ' 'neutron database should set this flag to False.')), ] # security_group_quota_opts from neutron/extensions/securitygroup.py security_group_quota_opts = [ cfg.IntOpt('quota_security_group', default=DEFAULT_QUOTA_SG, help=_('Number of security groups allowed per tenant. ' 'A negative value means unlimited.')), cfg.IntOpt('quota_security_group_rule', default=DEFAULT_QUOTA_SG_RULE, help=_('Number of security rules allowed per tenant. ' 'A negative value means unlimited.')), ] # l3_quota_opts from neutron/extensions/l3.py l3_quota_opts = [ cfg.IntOpt('quota_router', default=DEFAULT_QUOTA_ROUTER, help=_('Number of routers allowed per tenant. ' 'A negative value means unlimited.')), cfg.IntOpt('quota_floatingip', default=DEFAULT_QUOTA_FIP, help=_('Number of floating IPs allowed per tenant. ' 'A negative value means unlimited.')), ] # rbac_quota_opts from neutron/extensions/rbac.py rbac_quota_opts = [ cfg.IntOpt('quota_rbac_policy', default=DEFAULT_QUOTA_RBAC, deprecated_name='quota_rbac_entry', help=_('Default number of RBAC entries allowed per tenant. ' 'A negative value means unlimited.')) ] def register_quota_opts(opts, cfg=cfg.CONF): cfg.register_opts(opts, QUOTAS_CFG_GROUP) neutron-12.1.1/neutron/conf/wsgi.py0000664000175000017500000000235613553660046017270 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from oslo_service import wsgi from neutron._i18n import _ socket_opts = [ cfg.IntOpt('backlog', default=4096, help=_("Number of backlog requests to configure " "the socket with")), cfg.IntOpt('retry_until_window', default=30, help=_("Number of seconds to keep retrying to listen")), cfg.BoolOpt('use_ssl', default=False, help=_('Enable SSL on the API server')), ] def register_socket_opts(cfg=cfg.CONF): cfg.register_opts(socket_opts) wsgi.register_opts(cfg) neutron-12.1.1/neutron/conf/extensions/0000775000175000017500000000000013553660156020140 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/extensions/__init__.py0000664000175000017500000000000013553660046022235 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/extensions/allowedaddresspairs.py0000664000175000017500000000177413553660047024556 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ allowed_address_pair_opts = [ #TODO(limao): use quota framework when it support quota for attributes cfg.IntOpt('max_allowed_address_pair', default=10, help=_("Maximum number of allowed address pairs")), ] def register_allowed_address_pair_opts(cfg=cfg.CONF): cfg.register_opts(allowed_address_pair_opts) neutron-12.1.1/neutron/conf/service.py0000664000175000017500000000332413553660047017754 0ustar zuulzuul00000000000000# Copyright 2011 VMware, Inc # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ service_opts = [ cfg.IntOpt('periodic_interval', default=40, help=_('Seconds between running periodic tasks.')), cfg.IntOpt('api_workers', help=_('Number of separate API worker processes for service. ' 'If not specified, the default is equal to the number ' 'of CPUs available for best performance.')), cfg.IntOpt('rpc_workers', default=1, help=_('Number of RPC worker processes for service.')), cfg.IntOpt('rpc_state_report_workers', default=1, help=_('Number of RPC worker processes dedicated to state ' 'reports queue.')), cfg.IntOpt('periodic_fuzzy_delay', default=5, help=_('Range of seconds to randomly delay when starting the ' 'periodic task scheduler to reduce stampeding. ' '(Disable by setting to 0)')), ] def register_service_opts(opts, conf=cfg.CONF): conf.register_opts(opts) neutron-12.1.1/neutron/conf/__init__.py0000664000175000017500000000000013553660046020036 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/db/0000775000175000017500000000000013553660156016326 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/db/extraroute_db.py0000664000175000017500000000163613553660046021553 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ EXTRA_ROUTE_OPTS = [ # TODO(nati): use quota framework when it support quota for attributes cfg.IntOpt('max_routes', default=30, help=_("Maximum number of routes per router")), ] def register_db_extraroute_opts(conf=cfg.CONF): conf.register_opts(EXTRA_ROUTE_OPTS) neutron-12.1.1/neutron/conf/db/l3_hamode_db.py0000664000175000017500000000360013553660047021176 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ from neutron.common import constants as n_const L3_HA_OPTS = [ cfg.BoolOpt('l3_ha', default=False, help=_('Enable HA mode for virtual routers.')), cfg.IntOpt('max_l3_agents_per_router', default=3, help=_("Maximum number of L3 agents which a HA router will be " "scheduled on. If it is set to 0 then the router will " "be scheduled on every agent.")), cfg.StrOpt('l3_ha_net_cidr', default=n_const.L3_HA_NET_CIDR, help=_('Subnet used for the l3 HA admin network.')), cfg.StrOpt('l3_ha_network_type', default='', help=_("The network type to use when creating the HA network " "for an HA router. By default or if empty, the first " "'tenant_network_types' is used. This is helpful when " "the VRRP traffic should use a specific network which " "is not the default one.")), cfg.StrOpt('l3_ha_network_physical_name', default='', help=_("The physical network name with which the HA network " "can be created.")) ] def register_db_l3_hamode_opts(conf=cfg.CONF): conf.register_opts(L3_HA_OPTS) neutron-12.1.1/neutron/conf/db/migration_cli.py0000664000175000017500000000373713553660047021531 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg import pkg_resources from neutron._i18n import _ MIGRATION_ENTRYPOINTS = 'neutron.db.alembic_migrations' migration_entrypoints = { entrypoint.name: entrypoint for entrypoint in pkg_resources.iter_entry_points(MIGRATION_ENTRYPOINTS) } INSTALLED_SUBPROJECTS = [project_ for project_ in migration_entrypoints] CORE_OPTS = [ cfg.StrOpt('subproject', choices=INSTALLED_SUBPROJECTS, help=(_("The subproject to execute the command against. " "Can be one of: '%s'.") % "', '".join(INSTALLED_SUBPROJECTS))), cfg.BoolOpt('split_branches', default=True, deprecated_for_removal=True, help=_("DEPRECATED in newton, will be removed in ocata." "Alembic environments integrating with " "Neutron must implement split (contract and expand) " "branches file structure.")) ] DB_OPTS = [ cfg.StrOpt('connection', default='', secret=True, help=_('URL to database')), cfg.StrOpt('engine', default='', help=_('Database engine for which script will be generated ' 'when using offline migration.')), ] def register_db_cli_opts(conf): conf.register_cli_opts(CORE_OPTS) conf.register_cli_opts(DB_OPTS, 'database') neutron-12.1.1/neutron/conf/db/dvr_mac_db.py0000664000175000017500000000265213553660046020763 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ DVR_MAC_ADDRESS_OPTS = [ cfg.StrOpt('dvr_base_mac', default="fa:16:3f:00:00:00", help=_("The base mac address used for unique " "DVR instances by Neutron. The first 3 octets will " "remain unchanged. If the 4th octet is not 00, it will " "also be used. The others will be randomly generated. " "The 'dvr_base_mac' *must* be different from " "'base_mac' to avoid mixing them up with MAC's " "allocated for tenant ports. A 4 octet example would be " "dvr_base_mac = fa:16:3f:4f:00:00. The default is 3 " "octet")), ] def register_db_dvr_mac_opts(conf=cfg.CONF): conf.register_opts(DVR_MAC_ADDRESS_OPTS) neutron-12.1.1/neutron/conf/db/l3_gwmode_db.py0000664000175000017500000000164313553660046021227 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ L3GWMODE_OPTS = [ cfg.BoolOpt('enable_snat_by_default', default=True, help=_('Define the default value of enable_snat if not ' 'provided in external_gateway_info.')) ] def register_db_l3_gwmode_opts(conf=cfg.CONF): conf.register_opts(L3GWMODE_OPTS) neutron-12.1.1/neutron/conf/db/__init__.py0000664000175000017500000000000013553660046020423 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/conf/db/l3_agentschedulers_db.py0000664000175000017500000000251713553660046023126 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ L3_AGENTS_SCHEDULER_OPTS = [ cfg.StrOpt('router_scheduler_driver', default='neutron.scheduler.l3_agent_scheduler.' 'LeastRoutersScheduler', help=_('Driver to use for scheduling ' 'router to a default L3 agent')), cfg.BoolOpt('router_auto_schedule', default=True, help=_('Allow auto scheduling of routers to L3 agent.')), cfg.BoolOpt('allow_automatic_l3agent_failover', default=False, help=_('Automatically reschedule routers from offline L3 ' 'agents to online L3 agents.')), ] def register_db_l3agentschedulers_opts(conf=cfg.CONF): conf.register_opts(L3_AGENTS_SCHEDULER_OPTS) neutron-12.1.1/neutron/conf/db/l3_dvr_db.py0000664000175000017500000000223513553660047020537 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ ROUTER_DISTRIBUTED_OPTS = [ cfg.BoolOpt('router_distributed', default=False, help=_("System-wide flag to determine the type of router " "that tenants can create. Only admin can override.")), cfg.BoolOpt('enable_dvr', default=True, help=_("Determine if setup is configured for DVR. If False, " "DVR API extension will be disabled.")), ] def register_db_l3_dvr_opts(conf=cfg.CONF): conf.register_opts(ROUTER_DISTRIBUTED_OPTS) neutron-12.1.1/neutron/hacking/0000775000175000017500000000000013553660156016420 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/hacking/__init__.py0000664000175000017500000000000013553660046020515 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/hacking/checks.py0000664000175000017500000002337013553660047020236 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import re from hacking import core from neutron_lib.hacking import checks def flake8ext(f): """Decorator to indicate flake8 extension. This is borrowed from hacking.core.flake8ext(), but at now it is used only for unit tests to know which are neutron flake8 extensions. """ f.name = __name__ return f # Guidelines for writing new hacking checks # # - Use only for Neutron specific tests. OpenStack general tests # should be submitted to the common 'hacking' module. # - Pick numbers in the range N3xx. Find the current test with # the highest allocated number and then pick the next value. # - Keep the test method code in the source file ordered based # on the N3xx value. # - List the new rule in the top level HACKING.rst file # - Add test cases for each new rule to # neutron/tests/unit/hacking/test_checks.py unittest_imports_dot = re.compile(r"\bimport[\s]+unittest\b") unittest_imports_from = re.compile(r"\bfrom[\s]+unittest\b") filter_match = re.compile(r".*filter\(lambda ") tests_imports_dot = re.compile(r"\bimport[\s]+neutron.tests\b") tests_imports_from1 = re.compile(r"\bfrom[\s]+neutron.tests\b") tests_imports_from2 = re.compile(r"\bfrom[\s]+neutron[\s]+import[\s]+tests\b") @flake8ext def check_assert_called_once_with(logical_line, filename): """N322 - Try to detect unintended calls of nonexistent mock methods like: assert_called_once assertCalledOnceWith assert_has_called called_once_with """ if 'neutron/tests/' in filename: if '.assert_called_once_with(' in logical_line: return uncased_line = logical_line.lower().replace('_', '') check_calls = ['.assertcalledonce', '.calledoncewith'] if any(x for x in check_calls if x in uncased_line): msg = ("N322: Possible use of no-op mock method. " "please use assert_called_once_with.") yield (0, msg) if '.asserthascalled' in uncased_line: msg = ("N322: Possible use of no-op mock method. " "please use assert_has_calls.") yield (0, msg) @flake8ext def check_asserttruefalse(logical_line, filename): """N328 - Don't use assertEqual(True/False, observed).""" if 'neutron/tests/' in filename: if re.search(r"assertEqual\(\s*True,[^,]*(,[^,]*)?", logical_line): msg = ("N328: Use assertTrue(observed) instead of " "assertEqual(True, observed)") yield (0, msg) if re.search(r"assertEqual\([^,]*,\s*True(,[^,]*)?", logical_line): msg = ("N328: Use assertTrue(observed) instead of " "assertEqual(True, observed)") yield (0, msg) if re.search(r"assertEqual\(\s*False,[^,]*(,[^,]*)?", logical_line): msg = ("N328: Use assertFalse(observed) instead of " "assertEqual(False, observed)") yield (0, msg) if re.search(r"assertEqual\([^,]*,\s*False(,[^,]*)?", logical_line): msg = ("N328: Use assertFalse(observed) instead of " "assertEqual(False, observed)") yield (0, msg) @flake8ext def check_assertempty(logical_line, filename): """N330 - Enforce using assertEqual parameter ordering in case of empty objects. """ if 'neutron/tests/' in filename: msg = ("N330: Use assertEqual(*empty*, observed) instead of " "assertEqual(observed, *empty*). *empty* contains " "{}, [], (), set(), '', \"\"") empties = r"(\[\s*\]|\{\s*\}|\(\s*\)|set\(\s*\)|'\s*'|\"\s*\")" reg = r"assertEqual\(([^,]*,\s*)+?%s\)\s*$" % empties if re.search(reg, logical_line): yield (0, msg) @flake8ext def check_assertisinstance(logical_line, filename): """N331 - Enforce using assertIsInstance.""" if 'neutron/tests/' in filename: if re.search(r"assertTrue\(\s*isinstance\(\s*[^,]*,\s*[^,]*\)\)", logical_line): msg = ("N331: Use assertIsInstance(observed, type) instead " "of assertTrue(isinstance(observed, type))") yield (0, msg) @flake8ext def check_assertequal_for_httpcode(logical_line, filename): """N332 - Enforce correct oredering for httpcode in assertEqual.""" msg = ("N332: Use assertEqual(expected_http_code, observed_http_code) " "instead of assertEqual(observed_http_code, expected_http_code)") if 'neutron/tests/' in filename: if re.search(r"assertEqual\(\s*[^,]*,[^,]*HTTP[^\.]*\.code\s*\)", logical_line): yield (0, msg) @flake8ext def check_oslo_i18n_wrapper(logical_line, filename, noqa): """N340 - Check for neutron.i18n usage. Okay(neutron/foo/bar.py): from neutron._i18n import _ Okay(neutron_lbaas/foo/bar.py): from neutron_lbaas._i18n import _ N340(neutron/foo/bar.py): from neutron.i18n import _ N340(neutron_lbaas/foo/bar.py): from neutron_lbaas.i18n import _ N340(neutron_lbaas/foo/bar.py): from neutron.i18n import _ N340(neutron_lbaas/foo/bar.py): from neutron._i18n import _ Okay(neutron/foo/bar.py): from neutron.i18n import _ # noqa """ if noqa: return split_line = logical_line.split() modulename = os.path.normpath(filename).split('/')[0] bad_i18n_module = '%s.i18n' % modulename if (len(split_line) > 1 and split_line[0] in ('import', 'from')): if (split_line[1] == bad_i18n_module or modulename != 'neutron' and split_line[1] in ('neutron.i18n', 'neutron._i18n')): msg = ("N340: %(found)s is found. Use %(module)s._i18n instead." % {'found': split_line[1], 'module': modulename}) yield (0, msg) @flake8ext def check_builtins_gettext(logical_line, tokens, filename, lines, noqa): """N341 - Check usage of builtins gettext _(). Okay(neutron/foo.py): from neutron._i18n import _\n_('foo') N341(neutron/foo.py): _('foo') Okay(neutron/_i18n.py): _('foo') Okay(neutron/i18n.py): _('foo') Okay(neutron/foo.py): _('foo') # noqa """ if noqa: return modulename = os.path.normpath(filename).split('/')[0] if '%s/tests' % modulename in filename: return if os.path.basename(filename) in ('i18n.py', '_i18n.py'): return token_values = [t[1] for t in tokens] i18n_wrapper = '%s._i18n' % modulename if '_' in token_values: i18n_import_line_found = False for line in lines: split_line = [elm.rstrip(',') for elm in line.split()] if (len(split_line) > 1 and split_line[0] == 'from' and split_line[1] == i18n_wrapper and '_' in split_line): i18n_import_line_found = True break if not i18n_import_line_found: msg = ("N341: _ from python builtins module is used. " "Use _ from %s instead." % i18n_wrapper) yield (0, msg) @core.flake8ext @core.off_by_default def check_unittest_imports(logical_line): """N334 - Use unittest2 instead of unittest""" if (re.match(unittest_imports_from, logical_line) or re.match(unittest_imports_dot, logical_line)): msg = "N334: '%s' must be used instead of '%s'." % ( logical_line.replace('unittest', 'unittest2'), logical_line) yield (0, msg) @flake8ext def check_no_imports_from_tests(logical_line, filename, noqa): """N343 Production code must not import from neutron.tests.* """ msg = ("N343 Production code must not import from neutron.tests.*") if noqa: return if 'neutron/tests/' in filename: return for regex in tests_imports_dot, tests_imports_from1, tests_imports_from2: if re.match(regex, logical_line): yield(0, msg) @flake8ext def check_python3_no_filter(logical_line): """N344 - Use list comprehension instead of filter(lambda).""" msg = ("N344: Use list comprehension instead of " "filter(lambda obj: test(obj), data) on python3.") if filter_match.match(logical_line): yield(0, msg) @flake8ext def check_no_sqlalchemy_event_import(logical_line, filename, noqa): """N346 - Use neutron.db.api.sqla_listen instead of sqlalchemy event.""" if noqa: return is_import = (logical_line.startswith('import') or logical_line.startswith('from')) if not is_import: return for kw in ('sqlalchemy', 'event'): if kw not in logical_line: return yield (0, "N346: Register sqlalchemy events through " "neutron.db.api.sqla_listen so they can be cleaned up between " "unit tests") def factory(register): checks.factory(register) register(check_assert_called_once_with) register(check_asserttruefalse) register(check_assertempty) register(check_assertisinstance) register(check_assertequal_for_httpcode) register(check_oslo_i18n_wrapper) register(check_builtins_gettext) register(check_unittest_imports) register(check_no_imports_from_tests) register(check_python3_no_filter) register(check_no_sqlalchemy_event_import) neutron-12.1.1/neutron/opts.py0000664000175000017500000002336613553660047016364 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import itertools import operator from keystoneauth1 import loading as ks_loading from oslo_config import cfg import neutron.agent.agent_extensions_manager import neutron.agent.securitygroups_rpc import neutron.common.cache_utils import neutron.conf.agent.agent_extensions_manager import neutron.conf.agent.common import neutron.conf.agent.database.agents_db import neutron.conf.agent.database.agentschedulers_db import neutron.conf.agent.dhcp import neutron.conf.agent.l3.config import neutron.conf.agent.l3.ha import neutron.conf.agent.linux import neutron.conf.agent.metadata.config as meta_conf import neutron.conf.agent.ovs_conf import neutron.conf.agent.ovsdb_api import neutron.conf.agent.xenapi_conf import neutron.conf.common import neutron.conf.db.dvr_mac_db import neutron.conf.db.extraroute_db import neutron.conf.db.l3_agentschedulers_db import neutron.conf.db.l3_dvr_db import neutron.conf.db.l3_gwmode_db import neutron.conf.db.l3_hamode_db import neutron.conf.extensions.allowedaddresspairs import neutron.conf.plugins.ml2.config import neutron.conf.plugins.ml2.drivers.agent import neutron.conf.plugins.ml2.drivers.driver_type import neutron.conf.plugins.ml2.drivers.l2pop import neutron.conf.plugins.ml2.drivers.linuxbridge import neutron.conf.plugins.ml2.drivers.macvtap import neutron.conf.plugins.ml2.drivers.mech_sriov.agent_common import neutron.conf.plugins.ml2.drivers.ovs_conf import neutron.conf.quota import neutron.conf.service import neutron.conf.services.logging import neutron.conf.services.metering_agent import neutron.conf.wsgi import neutron.db.migration.cli import neutron.extensions.l3 import neutron.extensions.securitygroup import neutron.plugins.ml2.drivers.mech_sriov.agent.common.config import neutron.wsgi NOVA_GROUP = 'nova' CONF = cfg.CONF deprecations = {'nova.cafile': [cfg.DeprecatedOpt('ca_certificates_file', group=NOVA_GROUP)], 'nova.insecure': [cfg.DeprecatedOpt('api_insecure', group=NOVA_GROUP)], 'nova.timeout': [cfg.DeprecatedOpt('url_timeout', group=NOVA_GROUP)]} _nova_options = ks_loading.register_session_conf_options( CONF, NOVA_GROUP, deprecated_opts=deprecations) def list_agent_opts(): return [ ('agent', itertools.chain( neutron.conf.agent.common.ROOT_HELPER_OPTS, neutron.conf.agent.common.AGENT_STATE_OPTS, neutron.conf.agent.common.IPTABLES_OPTS, neutron.conf.agent.common.PROCESS_MONITOR_OPTS, neutron.conf.agent.common.AVAILABILITY_ZONE_OPTS) ), ('DEFAULT', itertools.chain( neutron.conf.agent.common.INTERFACE_DRIVER_OPTS, neutron.conf.agent.metadata.config.SHARED_OPTS) ) ] def list_extension_opts(): return [ ('DEFAULT', neutron.conf.extensions.allowedaddresspairs .allowed_address_pair_opts), ('quotas', itertools.chain( neutron.conf.quota.l3_quota_opts, neutron.conf.quota.security_group_quota_opts) ) ] def list_db_opts(): return [ ('DEFAULT', itertools.chain( neutron.conf.agent.database.agents_db.AGENT_OPTS, neutron.conf.db.extraroute_db.EXTRA_ROUTE_OPTS, neutron.conf.db.l3_gwmode_db.L3GWMODE_OPTS, neutron.conf.agent.database.agentschedulers_db .AGENTS_SCHEDULER_OPTS, neutron.conf.db.dvr_mac_db.DVR_MAC_ADDRESS_OPTS, neutron.conf.db.l3_dvr_db.ROUTER_DISTRIBUTED_OPTS, neutron.conf.db.l3_agentschedulers_db.L3_AGENTS_SCHEDULER_OPTS, neutron.conf.db.l3_hamode_db.L3_HA_OPTS) ), ('database', neutron.db.migration.cli.get_engine_config()) ] def list_opts(): return [ ('DEFAULT', itertools.chain( neutron.conf.common.core_cli_opts, neutron.conf.common.core_opts, neutron.conf.wsgi.socket_opts, neutron.conf.service.service_opts) ), (neutron.conf.common.NOVA_CONF_SECTION, itertools.chain( neutron.conf.common.nova_opts) ), ('quotas', neutron.conf.quota.core_quota_opts) ] def list_base_agent_opts(): return [ ('DEFAULT', itertools.chain( neutron.conf.agent.common.INTERFACE_OPTS, neutron.conf.agent.common.INTERFACE_DRIVER_OPTS) ), ('agent', neutron.conf.agent.common.AGENT_STATE_OPTS), ('ovs', itertools.chain( neutron.conf.agent.ovsdb_api.API_OPTS, neutron.conf.agent.ovs_conf.OPTS) ), ] def list_az_agent_opts(): return [ ('agent', neutron.conf.agent.common.AVAILABILITY_ZONE_OPTS), ] def list_dhcp_agent_opts(): return [ ('DEFAULT', itertools.chain( neutron.conf.agent.dhcp.DHCP_AGENT_OPTS, neutron.conf.agent.dhcp.DHCP_OPTS, neutron.conf.agent.dhcp.DNSMASQ_OPTS) ) ] def list_linux_bridge_opts(): return [ ('linux_bridge', neutron.conf.plugins.ml2.drivers.linuxbridge.bridge_opts), ('vxlan', neutron.conf.plugins.ml2.drivers.linuxbridge.vxlan_opts), ('agent', itertools.chain( neutron.conf.plugins.ml2.drivers.agent.agent_opts, neutron.conf.agent.agent_extensions_manager. AGENT_EXT_MANAGER_OPTS) ), ('securitygroup', neutron.conf.agent.securitygroups_rpc.security_group_opts), ('network_log', neutron.conf.services.logging.log_driver_opts) ] def list_l3_agent_opts(): return [ ('DEFAULT', itertools.chain( neutron.conf.agent.l3.config.OPTS, neutron.conf.service.service_opts, neutron.conf.agent.l3.ha.OPTS, neutron.conf.agent.common.PD_DRIVER_OPTS, neutron.conf.agent.common.RA_OPTS) ), ('agent', neutron.conf.agent.agent_extensions_manager.AGENT_EXT_MANAGER_OPTS), ] def list_macvtap_opts(): return [ ('macvtap', neutron.conf.plugins.ml2.drivers.macvtap.macvtap_opts), ('agent', neutron.conf.plugins.ml2.drivers.agent.agent_opts), ('securitygroup', neutron.conf.agent.securitygroups_rpc.security_group_opts) ] def list_metadata_agent_opts(): return [ ('DEFAULT', itertools.chain( meta_conf.SHARED_OPTS, meta_conf.METADATA_PROXY_HANDLER_OPTS, meta_conf.UNIX_DOMAIN_METADATA_PROXY_OPTS) ), ('agent', neutron.conf.agent.common.AGENT_STATE_OPTS) ] def list_metering_agent_opts(): return [ ('DEFAULT', neutron.conf.services.metering_agent.metering_agent_opts), ] def list_ml2_conf_opts(): return [ ('ml2', neutron.conf.plugins.ml2.config.ml2_opts), ('ml2_type_flat', neutron.conf.plugins.ml2.drivers.driver_type.flat_opts), ('ml2_type_vlan', neutron.conf.plugins.ml2.drivers.driver_type.vlan_opts), ('ml2_type_gre', neutron.conf.plugins.ml2.drivers.driver_type.gre_opts), ('ml2_type_vxlan', neutron.conf.plugins.ml2.drivers.driver_type.vxlan_opts), ('ml2_type_geneve', neutron.conf.plugins.ml2.drivers.driver_type.geneve_opts), ('securitygroup', neutron.conf.agent.securitygroups_rpc.security_group_opts), ('l2pop', neutron.conf.plugins.ml2.drivers.l2pop.l2_population_options) ] def list_ovs_opts(): return [ ('ovs', itertools.chain( neutron.conf.plugins.ml2.drivers.ovs_conf.ovs_opts, neutron.conf.agent.ovsdb_api.API_OPTS) ), ('agent', itertools.chain( neutron.conf.plugins.ml2.drivers.ovs_conf.agent_opts, neutron.conf.agent.agent_extensions_manager. AGENT_EXT_MANAGER_OPTS) ), ('securitygroup', neutron.conf.agent.securitygroups_rpc.security_group_opts), ('network_log', neutron.conf.services.logging.log_driver_opts) ] def list_sriov_agent_opts(): return [ ('sriov_nic', neutron.conf.plugins.ml2.drivers.mech_sriov.agent_common. sriov_nic_opts), ('agent', neutron.conf.agent.agent_extensions_manager.AGENT_EXT_MANAGER_OPTS) ] def list_auth_opts(): opt_list = copy.deepcopy(_nova_options) opt_list.insert(0, ks_loading.get_auth_common_conf_options()[0]) # NOTE(mhickey): There are a lot of auth plugins, we just generate # the config options for a few common ones plugins = ['password', 'v2password', 'v3password'] for name in plugins: for plugin_option in ks_loading.get_auth_plugin_conf_options(name): if all(option.name != plugin_option.name for option in opt_list): opt_list.append(plugin_option) opt_list.sort(key=operator.attrgetter('name')) return [(NOVA_GROUP, opt_list)] def list_xenapi_opts(): return [ ('xenapi', neutron.conf.agent.xenapi_conf.XENAPI_OPTS) ] neutron-12.1.1/neutron/debug/0000775000175000017500000000000013553660156016102 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/debug/commands.py0000664000175000017500000001046513553660046020261 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from cliff import lister from neutronclient.common import utils from neutronclient.neutron import v2_0 as client from neutronclient.neutron.v2_0 import port from neutron._i18n import _ class ProbeCommand(client.NeutronCommand): def get_debug_agent(self): return self.app.debug_agent class CreateProbe(ProbeCommand): """Create probe port and interface, then plug it in.""" def get_parser(self, prog_name): parser = super(CreateProbe, self).get_parser(prog_name) parser.add_argument( 'id', metavar='network_id', help=_('ID of network to probe')) parser.add_argument( '--device-owner', default='network', choices=['network', 'compute'], help=_('Owner type of the device: network/compute')) return parser def take_action(self, parsed_args): debug_agent = self.get_debug_agent() probe_port = debug_agent.create_probe(parsed_args.id, parsed_args.device_owner) self.log.info(_('Probe created : %s '), probe_port.id) class DeleteProbe(ProbeCommand): """Delete probe - delete port then uplug.""" def get_parser(self, prog_name): parser = super(DeleteProbe, self).get_parser(prog_name) parser.add_argument( 'id', metavar='port_id', help=_('ID of probe port to delete')) return parser def take_action(self, parsed_args): debug_agent = self.get_debug_agent() debug_agent.delete_probe(parsed_args.id) self.log.info(_('Probe %s deleted'), parsed_args.id) class ListProbe(ProbeCommand, lister.Lister): """List probes.""" _formatters = {'fixed_ips': port._format_fixed_ips, } def take_action(self, parsed_args): debug_agent = self.get_debug_agent() info = debug_agent.list_probes() columns = sorted(info[0].keys()) if info else [] return (columns, (utils.get_item_properties( s, columns, formatters=self._formatters, ) for s in info), ) class ClearProbe(ProbeCommand): """Clear All probes.""" def take_action(self, parsed_args): debug_agent = self.get_debug_agent() cleared_probes_count = debug_agent.clear_probes() self.log.info('%d probe(s) deleted', cleared_probes_count) class ExecProbe(ProbeCommand): """Exec commands on the namespace of the probe.""" def get_parser(self, prog_name): parser = super(ExecProbe, self).get_parser(prog_name) parser.add_argument( 'id', metavar='port_id', help=_('ID of probe port to execute command')) parser.add_argument( 'command', metavar='command', nargs='?', default=None, help=_('Command to execute')) return parser def take_action(self, parsed_args): debug_agent = self.get_debug_agent() result = debug_agent.exec_command(parsed_args.id, parsed_args.command) self.app.stdout.write(result + '\n') class PingAll(ProbeCommand): """Ping all fixed_ip.""" def get_parser(self, prog_name): parser = super(PingAll, self).get_parser(prog_name) parser.add_argument( '--timeout', metavar='', default=10, help=_('Ping timeout')) parser.add_argument( '--id', metavar='network_id', default=None, help=_('ID of network')) return parser def take_action(self, parsed_args): debug_agent = self.get_debug_agent() result = debug_agent.ping_all(parsed_args.id, timeout=parsed_args.timeout) self.app.stdout.write(result + '\n') neutron-12.1.1/neutron/debug/debug_agent.py0000664000175000017500000001614313553660047020724 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import shlex import socket import netaddr from neutron_lib.api.definitions import portbindings from neutron_lib import constants from oslo_log import log as logging from neutron.agent.linux import dhcp from neutron.agent.linux import ip_lib LOG = logging.getLogger(__name__) DEVICE_OWNER_NETWORK_PROBE = constants.DEVICE_OWNER_NETWORK_PREFIX + 'probe' DEVICE_OWNER_COMPUTE_PROBE = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'probe' class NeutronDebugAgent(object): def __init__(self, conf, client, driver): self.conf = conf self.client = client self.driver = driver def _get_namespace(self, port): return "qprobe-%s" % port.id def create_probe(self, network_id, device_owner='network'): network = self._get_network(network_id) bridge = None if network.external: bridge = self.conf.external_network_bridge port = self._create_port(network, device_owner) interface_name = self.driver.get_device_name(port) namespace = self._get_namespace(port) if ip_lib.device_exists(interface_name, namespace=namespace): LOG.debug('Reusing existing device: %s.', interface_name) else: self.driver.plug(network.id, port.id, interface_name, port.mac_address, bridge=bridge, namespace=namespace) ip_cidrs = [] for fixed_ip in port.fixed_ips: subnet = fixed_ip.subnet net = netaddr.IPNetwork(subnet.cidr) ip_cidr = '%s/%s' % (fixed_ip.ip_address, net.prefixlen) ip_cidrs.append(ip_cidr) self.driver.init_l3(interface_name, ip_cidrs, namespace=namespace) return port def _get_subnet(self, subnet_id): subnet_dict = self.client.show_subnet(subnet_id)['subnet'] return dhcp.DictModel(subnet_dict) def _get_network(self, network_id): network_dict = self.client.show_network(network_id)['network'] network = dhcp.DictModel(network_dict) network.external = network_dict.get('router:external') obj_subnet = [self._get_subnet(s_id) for s_id in network.subnets] network.subnets = obj_subnet return network def clear_probes(self): """Returns number of deleted probes""" ports = self.client.list_ports( device_id=socket.gethostname(), device_owner=[DEVICE_OWNER_NETWORK_PROBE, DEVICE_OWNER_COMPUTE_PROBE]) info = ports['ports'] for port in info: self.delete_probe(port['id']) return len(info) def delete_probe(self, port_id): port = dhcp.DictModel(self.client.show_port(port_id)['port']) network = self._get_network(port.network_id) bridge = None if network.external: bridge = self.conf.external_network_bridge namespace = self._get_namespace(port) if ip_lib.network_namespace_exists(namespace): self.driver.unplug(self.driver.get_device_name(port), bridge=bridge, namespace=namespace) try: ip_lib.delete_network_namespace(namespace) except Exception: LOG.warning('Failed to delete namespace %s', namespace) else: self.driver.unplug(self.driver.get_device_name(port), bridge=bridge) self.client.delete_port(port.id) def list_probes(self): ports = self.client.list_ports( device_owner=[DEVICE_OWNER_NETWORK_PROBE, DEVICE_OWNER_COMPUTE_PROBE]) info = ports['ports'] for port in info: port['device_name'] = self.driver.get_device_name( dhcp.DictModel(port)) return info def exec_command(self, port_id, command=None): port = dhcp.DictModel(self.client.show_port(port_id)['port']) ip = ip_lib.IPWrapper() namespace = self._get_namespace(port) if not command: return "sudo ip netns exec %s" % self._get_namespace(port) namespace = ip.ensure_namespace(namespace) return namespace.netns.execute(shlex.split(command)) def ensure_probe(self, network_id): ports = self.client.list_ports(network_id=network_id, device_id=socket.gethostname(), device_owner=DEVICE_OWNER_NETWORK_PROBE) info = ports.get('ports', []) if info: return dhcp.DictModel(info[0]) else: return self.create_probe(network_id) def ping_all(self, network_id=None, timeout=1): if network_id: ports = self.client.list_ports(network_id=network_id)['ports'] else: ports = self.client.list_ports()['ports'] result = "" for port in ports: probe = self.ensure_probe(port['network_id']) if port['device_owner'] == DEVICE_OWNER_NETWORK_PROBE: continue for fixed_ip in port['fixed_ips']: address = fixed_ip['ip_address'] subnet = self._get_subnet(fixed_ip['subnet_id']) if subnet.ip_version == 4: ping_command = 'ping' else: ping_command = 'ping6' result += self.exec_command(probe.id, '%s -c 1 -w %s %s' % (ping_command, timeout, address)) return result def _create_port(self, network, device_owner): host = self.conf.host body = {'port': {'admin_state_up': True, 'network_id': network.id, 'device_id': '%s' % socket.gethostname(), 'device_owner': '%s:probe' % device_owner, 'tenant_id': network.tenant_id, portbindings.HOST_ID: host, 'fixed_ips': [dict(subnet_id=s.id) for s in network.subnets]}} port_dict = self.client.create_port(body)['port'] port = dhcp.DictModel(port_dict) port.network = network for fixed_ip in port.fixed_ips: fixed_ip.subnet = self._get_subnet(fixed_ip.subnet_id) return port neutron-12.1.1/neutron/debug/shell.py0000664000175000017500000000672313553660047017572 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from oslo_config import cfg from oslo_utils import importutils from neutron._i18n import _ from neutron.agent.common import utils from neutron.conf.agent import common as config from neutron.debug import debug_agent from neutronclient.common import exceptions as exc from neutronclient import shell COMMAND_V2 = { 'probe-create': importutils.import_class( 'neutron.debug.commands.CreateProbe'), 'probe-delete': importutils.import_class( 'neutron.debug.commands.DeleteProbe'), 'probe-list': importutils.import_class( 'neutron.debug.commands.ListProbe'), 'probe-clear': importutils.import_class( 'neutron.debug.commands.ClearProbe'), 'probe-exec': importutils.import_class( 'neutron.debug.commands.ExecProbe'), 'ping-all': importutils.import_class( 'neutron.debug.commands.PingAll'), #TODO(nati) ping, netcat , nmap, bench } COMMANDS = {'2.0': COMMAND_V2} class NeutronDebugShell(shell.NeutronShell): def __init__(self, api_version): super(NeutronDebugShell, self).__init__(api_version) for k, v in COMMANDS[api_version].items(): self.command_manager.add_command(k, v) def build_option_parser(self, description, version): parser = super(NeutronDebugShell, self).build_option_parser( description, version) default = ( shell.env('NEUTRON_TEST_CONFIG_FILE') or shell.env('QUANTUM_TEST_CONFIG_FILE') ) parser.add_argument( '--config-file', default=default, help=_('Config file for interface driver ' '(You may also use l3_agent.ini)')) return parser def initialize_app(self, argv): super(NeutronDebugShell, self).initialize_app(argv) if not self.options.config_file: raise exc.CommandError( _("You must provide a config file for bridge -" " either --config-file or env[NEUTRON_TEST_CONFIG_FILE]")) client = self.client_manager.neutron config.register_interface_opts() cfg.CONF.register_opts(config.EXT_NET_BRIDGE_OPTS) config.register_interface_driver_opts_helper(cfg.CONF) cfg.CONF(['--config-file', self.options.config_file]) config.setup_logging() driver = utils.load_interface_driver(cfg.CONF) self.debug_agent = debug_agent.NeutronDebugAgent(cfg.CONF, client, driver) self.log.warning('This tool is deprecated and will be removed ' 'in the future to be replaced with a more ' 'powerful troubleshooting toolkit.') def main(argv=None): return NeutronDebugShell(shell.NEUTRON_API_VERSION).run( argv or sys.argv[1:]) neutron-12.1.1/neutron/debug/__init__.py0000664000175000017500000000000013553660046020177 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/debug/README0000664000175000017500000000271213553660046016762 0ustar zuulzuul00000000000000Debug Helper Script for Neutron - Configure export NEUTRON_TEST_CONFIG_FILE=/etc/neutron/debug.ini or export NEUTRON_TEST_CONFIG_FILE=/etc/neutron/l3_agent.ini you can also specify config file by --config-file option - Usage neutron-debug commands probe-create Create probe port - create port and interface, then plug it in. This commands returns a port id of a probe port. A probe port is a port which is used to test. The port id is probe id. We can have multiple probe probes in a network, in order to check connectivity between ports. neutron-debug probe-exec probe_id_1 'nc -l 192.168.100.3 22' neutron-debug probe-exec probe_id_2 'nc -vz 192.168.100.4 22' Note: You should use a user and a tenant who has permission to modify network and subnet if you want to probe. For example, you need to be admin user if you want to probe external network. probe-delete Delete probe - delete port then uplug probe-exec 'command' Exec commands on the namespace of the probe `probe-exec ` 'interactive command' Exec interactive command (eg, ssh) probe-list List probes probe-clear Clear All probes ping-all --id --timeout 1 (optional) ping-all is all-in-one command to ping all fixed ip's in all network or a specified network. In the command probe is automatically created if needed. neutron-debug extends the shell of neutronclient, so you can use all the commands of neutron neutron-12.1.1/neutron/auth.py0000664000175000017500000000323613553660046016331 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import context from oslo_config import cfg from oslo_log import log as logging from oslo_middleware import base import webob.dec import webob.exc LOG = logging.getLogger(__name__) class NeutronKeystoneContext(base.ConfigurableMiddleware): """Make a request context from keystone headers.""" @webob.dec.wsgify def __call__(self, req): ctx = context.Context.from_environ(req.environ) if not ctx.user_id: LOG.debug("X_USER_ID is not found in request") return webob.exc.HTTPUnauthorized() # Inject the context... req.environ['neutron.context'] = ctx return self.application def pipeline_factory(loader, global_conf, **local_conf): """Create a paste pipeline based on the 'auth_strategy' config option.""" pipeline = local_conf[cfg.CONF.auth_strategy] pipeline = pipeline.split() filters = [loader.get_filter(n) for n in pipeline[:-1]] app = loader.get_app(pipeline[-1]) filters.reverse() for filter in filters: app = filter(app) return app neutron-12.1.1/neutron/service.py0000664000175000017500000003346213553660047017035 0ustar zuulzuul00000000000000# Copyright 2011 VMware, Inc # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import inspect import os import random from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import context from neutron_lib.plugins import directory from neutron_lib import worker as neutron_worker from oslo_concurrency import processutils from oslo_config import cfg from oslo_log import log as logging from oslo_messaging import server as rpc_server from oslo_service import loopingcall from oslo_service import service as common_service from oslo_utils import excutils from oslo_utils import importutils from neutron.common import config from neutron.common import profiler from neutron.common import rpc as n_rpc from neutron.conf import service from neutron.db import api as session from neutron import wsgi service.register_service_opts(service.service_opts) LOG = logging.getLogger(__name__) class WsgiService(object): """Base class for WSGI based services. For each api you define, you must also define these flags: :_listen: The address on which to listen :_listen_port: The port on which to listen """ def __init__(self, app_name): self.app_name = app_name self.wsgi_app = None def start(self): self.wsgi_app = _run_wsgi(self.app_name) def wait(self): self.wsgi_app.wait() class NeutronApiService(WsgiService): """Class for neutron-api service.""" def __init__(self, app_name): profiler.setup('neutron-server', cfg.CONF.host) super(NeutronApiService, self).__init__(app_name) @classmethod def create(cls, app_name='neutron'): # Setup logging early config.setup_logging() service = cls(app_name) return service def serve_wsgi(cls): try: service = cls.create() service.start() except Exception: with excutils.save_and_reraise_exception(): LOG.exception('Unrecoverable error: please check log ' 'for details.') registry.publish(resources.PROCESS, events.BEFORE_SPAWN, service) return service class RpcWorker(neutron_worker.BaseWorker): """Wraps a worker to be handled by ProcessLauncher""" start_listeners_method = 'start_rpc_listeners' def __init__(self, plugins, worker_process_count=1): super(RpcWorker, self).__init__( worker_process_count=worker_process_count ) self._plugins = plugins self._servers = [] def start(self): super(RpcWorker, self).start() for plugin in self._plugins: if hasattr(plugin, self.start_listeners_method): try: servers = getattr(plugin, self.start_listeners_method)() except NotImplementedError: continue self._servers.extend(servers) def wait(self): try: self._wait() except Exception: LOG.exception('done with wait') raise def _wait(self): LOG.debug('calling RpcWorker wait()') for server in self._servers: if isinstance(server, rpc_server.MessageHandlingServer): LOG.debug('calling wait on %s', server) server.wait() else: LOG.debug('NOT calling wait on %s', server) LOG.debug('returning from RpcWorker wait()') def stop(self): LOG.debug('calling RpcWorker stop()') for server in self._servers: if isinstance(server, rpc_server.MessageHandlingServer): LOG.debug('calling stop on %s', server) server.stop() @staticmethod def reset(): config.reset_service() class RpcReportsWorker(RpcWorker): start_listeners_method = 'start_rpc_state_reports_listener' def _get_rpc_workers(): plugin = directory.get_plugin() service_plugins = directory.get_plugins().values() if cfg.CONF.rpc_workers < 1: cfg.CONF.set_override('rpc_workers', 1) # If 0 < rpc_workers then start_rpc_listeners would be called in a # subprocess and we cannot simply catch the NotImplementedError. It is # simpler to check this up front by testing whether the plugin supports # multiple RPC workers. if not plugin.rpc_workers_supported(): LOG.debug("Active plugin doesn't implement start_rpc_listeners") if 0 < cfg.CONF.rpc_workers: LOG.error("'rpc_workers = %d' ignored because " "start_rpc_listeners is not implemented.", cfg.CONF.rpc_workers) raise NotImplementedError() # passing service plugins only, because core plugin is among them rpc_workers = [RpcWorker(service_plugins, worker_process_count=cfg.CONF.rpc_workers)] if (cfg.CONF.rpc_state_report_workers > 0 and plugin.rpc_state_report_workers_supported()): rpc_workers.append( RpcReportsWorker( [plugin], worker_process_count=cfg.CONF.rpc_state_report_workers ) ) return rpc_workers def _get_plugins_workers(): # NOTE(twilson) get_plugins also returns the core plugin plugins = directory.get_unique_plugins() # TODO(twilson) Instead of defaulting here, come up with a good way to # share a common get_workers default between NeutronPluginBaseV2 and # ServicePluginBase return [ plugin_worker for plugin in plugins if hasattr(plugin, 'get_workers') for plugin_worker in plugin.get_workers() ] class AllServicesNeutronWorker(neutron_worker.BaseWorker): def __init__(self, services, worker_process_count=1): super(AllServicesNeutronWorker, self).__init__(worker_process_count) self._services = services self._launcher = common_service.Launcher(cfg.CONF) def start(self): for srv in self._services: self._launcher.launch_service(srv) super(AllServicesNeutronWorker, self).start() def stop(self): self._launcher.stop() def wait(self): self._launcher.wait() def reset(self): self._launcher.restart() def _start_workers(workers): process_workers = [ plugin_worker for plugin_worker in workers if plugin_worker.worker_process_count > 0 ] try: if process_workers: worker_launcher = common_service.ProcessLauncher( cfg.CONF, wait_interval=1.0 ) # add extra process worker and spawn there all workers with # worker_process_count == 0 thread_workers = [ plugin_worker for plugin_worker in workers if plugin_worker.worker_process_count < 1 ] if thread_workers: process_workers.append( AllServicesNeutronWorker(thread_workers) ) # dispose the whole pool before os.fork, otherwise there will # be shared DB connections in child processes which may cause # DB errors. session.context_manager.dispose_pool() for worker in process_workers: worker_launcher.launch_service(worker, worker.worker_process_count) else: worker_launcher = common_service.ServiceLauncher(cfg.CONF) for worker in workers: worker_launcher.launch_service(worker) return worker_launcher except Exception: with excutils.save_and_reraise_exception(): LOG.exception('Unrecoverable error: please check log for ' 'details.') def start_all_workers(): workers = _get_rpc_workers() + _get_plugins_workers() launcher = _start_workers(workers) registry.publish(resources.PROCESS, events.AFTER_SPAWN, None) return launcher def start_rpc_workers(): rpc_workers = _get_rpc_workers() LOG.debug('using launcher for rpc, workers=%s', cfg.CONF.rpc_workers) return _start_workers(rpc_workers) def start_plugins_workers(): plugins_workers = _get_plugins_workers() return _start_workers(plugins_workers) def _get_api_workers(): workers = cfg.CONF.api_workers if workers is None: workers = processutils.get_worker_count() return workers def _run_wsgi(app_name): app = config.load_paste_app(app_name) if not app: LOG.error('No known API applications configured.') return return run_wsgi_app(app) def run_wsgi_app(app): server = wsgi.Server("Neutron") server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host, workers=_get_api_workers()) LOG.info("Neutron service started, listening on %(host)s:%(port)s", {'host': cfg.CONF.bind_host, 'port': cfg.CONF.bind_port}) return server class Service(n_rpc.Service): """Service object for binaries running on hosts. A service takes a manager and enables rpc by listening to queues based on topic. It also periodically runs tasks on the manager. """ def __init__(self, host, binary, topic, manager, report_interval=None, periodic_interval=None, periodic_fuzzy_delay=None, *args, **kwargs): self.binary = binary self.manager_class_name = manager manager_class = importutils.import_class(self.manager_class_name) self.manager = manager_class(host=host, *args, **kwargs) self.report_interval = report_interval self.periodic_interval = periodic_interval self.periodic_fuzzy_delay = periodic_fuzzy_delay self.saved_args, self.saved_kwargs = args, kwargs self.timers = [] profiler.setup(binary, host) super(Service, self).__init__(host, topic, manager=self.manager) def start(self): self.manager.init_host() super(Service, self).start() if self.report_interval: pulse = loopingcall.FixedIntervalLoopingCall(self.report_state) pulse.start(interval=self.report_interval, initial_delay=self.report_interval) self.timers.append(pulse) if self.periodic_interval: if self.periodic_fuzzy_delay: initial_delay = random.randint(0, self.periodic_fuzzy_delay) else: initial_delay = None periodic = loopingcall.FixedIntervalLoopingCall( self.periodic_tasks) periodic.start(interval=self.periodic_interval, initial_delay=initial_delay) self.timers.append(periodic) self.manager.after_start() def __getattr__(self, key): manager = self.__dict__.get('manager', None) return getattr(manager, key) @classmethod def create(cls, host=None, binary=None, topic=None, manager=None, report_interval=None, periodic_interval=None, periodic_fuzzy_delay=None): """Instantiates class and passes back application object. :param host: defaults to cfg.CONF.host :param binary: defaults to basename of executable :param topic: defaults to bin_name - 'neutron-' part :param manager: defaults to cfg.CONF._manager :param report_interval: defaults to cfg.CONF.report_interval :param periodic_interval: defaults to cfg.CONF.periodic_interval :param periodic_fuzzy_delay: defaults to cfg.CONF.periodic_fuzzy_delay """ if not host: host = cfg.CONF.host if not binary: binary = os.path.basename(inspect.stack()[-1][1]) if not topic: topic = binary.rpartition('neutron-')[2] topic = topic.replace("-", "_") if not manager: manager = cfg.CONF.get('%s_manager' % topic, None) if report_interval is None: report_interval = cfg.CONF.report_interval if periodic_interval is None: periodic_interval = cfg.CONF.periodic_interval if periodic_fuzzy_delay is None: periodic_fuzzy_delay = cfg.CONF.periodic_fuzzy_delay service_obj = cls(host, binary, topic, manager, report_interval=report_interval, periodic_interval=periodic_interval, periodic_fuzzy_delay=periodic_fuzzy_delay) return service_obj def kill(self): """Destroy the service object.""" self.stop() def stop(self): super(Service, self).stop() for x in self.timers: try: x.stop() except Exception: LOG.exception("Exception occurs when timer stops") self.timers = [] def wait(self): super(Service, self).wait() for x in self.timers: try: x.wait() except Exception: LOG.exception("Exception occurs when waiting for timer") def reset(self): config.reset_service() def periodic_tasks(self, raise_on_error=False): """Tasks to be run at a periodic interval.""" ctxt = context.get_admin_context() self.manager.periodic_tasks(ctxt, raise_on_error=raise_on_error) def report_state(self): """Update the state of this service.""" # Todo(gongysh) report state to neutron server pass neutron-12.1.1/neutron/scheduler/0000775000175000017500000000000013553660156016772 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/scheduler/base_resource_filter.py0000664000175000017500000000346113553660047023535 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import six @six.add_metaclass(abc.ABCMeta) class BaseResourceFilter(object): """Encapsulate logic that is specific to the resource type.""" @abc.abstractmethod def filter_agents(self, plugin, context, resource): """Return the agents that can host the resource.""" def bind(self, context, agents, resource_id): """Bind the resource to the agents.""" with context.session.begin(subtransactions=True): for agent in agents: # Load is being incremented here to reflect latest agent load # even within the agent report interval. This will be very # much necessary when bulk resource creation happens within a # agent report interval time. # NOTE: The resource being bound might or might not be of the # same type which is accounted for the load. It isn't a # problem because "+ 1" here does not meant to predict # precisely what the load of the agent will be. The value will # be corrected by the agent on the next report interval. agent.load += 1 agent.update() neutron-12.1.1/neutron/scheduler/l3_agent_scheduler.py0000664000175000017500000004353413553660047023106 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import collections import functools import itertools import random from neutron_lib.api.definitions import availability_zone as az_def from neutron_lib import constants as lib_const from neutron_lib.exceptions import l3 as l3_exc from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import log as logging import six from neutron.common import utils from neutron.conf.db import l3_hamode_db from neutron.db import api as db_api from neutron.db.models import l3agent as rb_model from neutron.objects import l3agent as rb_obj LOG = logging.getLogger(__name__) cfg.CONF.register_opts(l3_hamode_db.L3_HA_OPTS) @six.add_metaclass(abc.ABCMeta) class L3Scheduler(object): def __init__(self): self.max_ha_agents = cfg.CONF.max_l3_agents_per_router def schedule(self, plugin, context, router_id, candidates=None): """Schedule the router to an active L3 agent. Schedule the router only if it is not already scheduled. """ return self._schedule_router( plugin, context, router_id, candidates=candidates) def _router_has_binding(self, context, router_id, l3_agent_id): router_binding_model = rb_model.RouterL3AgentBinding query = context.session.query(router_binding_model.router_id) query = query.filter(router_binding_model.router_id == router_id, router_binding_model.l3_agent_id == l3_agent_id) return query.count() > 0 def _get_routers_can_schedule(self, plugin, context, routers, l3_agent): """Get the subset of routers that can be scheduled on the L3 agent.""" ids_to_discard = set() for router in routers: # check if the l3 agent is compatible with the router candidates = plugin.get_l3_agent_candidates( context, router, [l3_agent]) if not candidates: ids_to_discard.add(router['id']) return [r for r in routers if r['id'] not in ids_to_discard] def auto_schedule_routers(self, plugin, context, host): """Schedule under-scheduled routers to L3 Agents. An under-scheduled router is a router that is either completely un-scheduled (scheduled to 0 agents), or an HA router that is under-scheduled (scheduled to less than max_l3_agents configuration option. The function finds all the under-scheduled routers and schedules them. :param host: if unspecified, under-scheduled routers are scheduled to all agents (not necessarily from the requesting host). If specified, under-scheduled routers are scheduled only to the agent on 'host'. """ l3_agent = plugin.get_enabled_agent_on_host( context, lib_const.AGENT_TYPE_L3, host) if not l3_agent: return underscheduled_routers = self._get_underscheduled_routers( plugin, context) target_routers = self._get_routers_can_schedule( plugin, context, underscheduled_routers, l3_agent) for router in target_routers: self.schedule(plugin, context, router['id'], candidates=[l3_agent]) def _get_underscheduled_routers(self, plugin, context): underscheduled_routers = [] max_agents_for_ha = plugin.get_number_of_agents_for_scheduling(context) for router, count in plugin.get_routers_l3_agents_count(context): if (count < 1 or router.get('ha', False) and count < max_agents_for_ha): # Either the router was un-scheduled (scheduled to 0 agents), # or it's an HA router and it was under-scheduled (scheduled to # less than max_agents_for_ha). Either way, it should be added # to the list of routers we want to handle. underscheduled_routers.append(router) return underscheduled_routers def _get_candidates(self, plugin, context, sync_router): """Return L3 agents where a router could be scheduled.""" is_ha = sync_router.get('ha', False) with context.session.begin(subtransactions=True): # allow one router is hosted by just # one enabled l3 agent hosting since active is just a # timing problem. Non-active l3 agent can return to # active any time current_l3_agents = plugin.get_l3_agents_hosting_routers( context, [sync_router['id']], admin_state_up=True) if current_l3_agents and not is_ha: LOG.debug('Router %(router_id)s has already been hosted ' 'by L3 agent %(agent_id)s', {'router_id': sync_router['id'], 'agent_id': current_l3_agents[0]['id']}) return [] active_l3_agents = plugin.get_l3_agents(context, active=True) if not active_l3_agents: LOG.warning('No active L3 agents') return [] candidates = plugin.get_l3_agent_candidates(context, sync_router, active_l3_agents) if not candidates: LOG.warning('No L3 agents can host the router %s', sync_router['id']) return candidates def _bind_routers(self, plugin, context, routers, l3_agent): for router in routers: if router.get('ha'): if not self._router_has_binding(context, router['id'], l3_agent.id): self.create_ha_port_and_bind( plugin, context, router['id'], router['tenant_id'], l3_agent) else: self.bind_router(plugin, context, router['id'], l3_agent.id) @db_api.retry_db_errors def bind_router(self, plugin, context, router_id, agent_id, is_manual_scheduling=False, is_ha=False): """Bind the router to the l3 agent which has been chosen. The function tries to create a RouterL3AgentBinding object and add it to the database. It returns the binding that was created or None if it failed to create it due to some conflict. In the HA router case, when creating a RouterL3AgentBinding (with some binding_index) fails because some other RouterL3AgentBinding was concurrently created using the same binding_index, then the function will retry to create an entry with a new binding_index. This creation will be retried up to db_api.MAX_RETRIES times. If, still in the HA router case, the creation failed because the router has already been bound to the l3 agent in question or has been removed (by a concurrent operation), then no further attempts will be made and the function will return None. Note that for non-HA routers, the function will always perform exactly one try, regardless of the error preventing the addition of a new RouterL3AgentBinding object to the database. """ if rb_obj.RouterL3AgentBinding.objects_exist( context, router_id=router_id, l3_agent_id=agent_id): LOG.debug('Router %(router_id)s has already been scheduled ' 'to L3 agent %(agent_id)s.', {'router_id': router_id, 'agent_id': agent_id}) return if not is_ha: binding_index = rb_model.LOWEST_BINDING_INDEX if rb_obj.RouterL3AgentBinding.objects_exist( context, router_id=router_id, binding_index=binding_index): LOG.debug('Non-HA router %s has already been scheduled', router_id) return else: binding_index = plugin.get_vacant_binding_index( context, router_id, is_manual_scheduling) if binding_index < rb_model.LOWEST_BINDING_INDEX: LOG.debug('Unable to find a vacant binding_index for ' 'router %(router_id)s and agent %(agent_id)s', {'router_id': router_id, 'agent_id': agent_id}) return try: binding = rb_obj.RouterL3AgentBinding( context, l3_agent_id=agent_id, router_id=router_id, binding_index=binding_index) binding.create() LOG.debug('Router %(router_id)s is scheduled to L3 agent ' '%(agent_id)s with binding_index %(binding_index)d', {'router_id': router_id, 'agent_id': agent_id, 'binding_index': binding_index}) return binding except db_exc.DBReferenceError: LOG.debug('Router %s has already been removed ' 'by concurrent operation', router_id) def _schedule_router(self, plugin, context, router_id, candidates=None): if not plugin.router_supports_scheduling(context, router_id): return sync_router = plugin.get_router(context, router_id) candidates = candidates or self._get_candidates( plugin, context, sync_router) if not candidates: return elif sync_router.get('ha', False): chosen_agents = self._bind_ha_router(plugin, context, router_id, sync_router.get('tenant_id'), candidates) if not chosen_agents: return chosen_agent = chosen_agents[-1] else: chosen_agent = self._choose_router_agent( plugin, context, candidates) self.bind_router(plugin, context, router_id, chosen_agent.id) return chosen_agent @abc.abstractmethod def _choose_router_agent(self, plugin, context, candidates): """Choose an agent from candidates based on a specific policy.""" pass @abc.abstractmethod def _choose_router_agents_for_ha(self, plugin, context, candidates): """Choose agents from candidates based on a specific policy.""" pass def _get_num_of_agents_for_ha(self, candidates_count): return (min(self.max_ha_agents, candidates_count) if self.max_ha_agents else candidates_count) def _add_port_from_net_and_ensure_vr_id(self, plugin, ctxt, router_db, tenant_id, ha_net): plugin._ensure_vr_id(ctxt, router_db, ha_net) return plugin.add_ha_port(ctxt, router_db.id, ha_net.network_id, tenant_id) def create_ha_port_and_bind(self, plugin, context, router_id, tenant_id, agent, is_manual_scheduling=False): """Creates and binds a new HA port for this agent.""" ctxt = context.elevated() router_db = plugin._get_router(ctxt, router_id) creator = functools.partial(self._add_port_from_net_and_ensure_vr_id, plugin, ctxt, router_db, tenant_id) dep_getter = functools.partial(plugin.get_ha_network, ctxt, tenant_id) dep_creator = functools.partial(plugin._create_ha_network, ctxt, tenant_id) dep_deleter = functools.partial(plugin._delete_ha_network, ctxt) dep_id_attr = 'network_id' # This might fail in case of concurrent calls, which is good for us # as we can skip the rest of this function. binding = self.bind_router( plugin, context, router_id, agent['id'], is_manual_scheduling=is_manual_scheduling, is_ha=True) if not binding: return try: port_binding = utils.create_object_with_dependency( creator, dep_getter, dep_creator, dep_id_attr, dep_deleter)[0] with db_api.autonested_transaction(context.session): port_binding.l3_agent_id = agent['id'] except db_exc.DBDuplicateEntry: LOG.debug("Router %(router)s already scheduled for agent " "%(agent)s", {'router': router_id, 'agent': agent['id']}) port_id = port_binding.port_id # Below call will also delete entry from L3HARouterAgentPortBinding # and RouterPort tables plugin._core_plugin.delete_port(context, port_id, l3_port_check=False) except l3_exc.RouterNotFound: LOG.debug('Router %s has already been removed ' 'by concurrent operation', router_id) # we try to clear the HA network here in case the port we created # blocked the concurrent router delete operation from getting rid # of the HA network ha_net = plugin.get_ha_network(ctxt, tenant_id) if ha_net: plugin.safe_delete_ha_network(ctxt, ha_net, tenant_id) def _filter_scheduled_agents(self, plugin, context, router_id, candidates): hosting = plugin.get_l3_agents_hosting_routers(context, [router_id]) # convert to comparable types hosting_list = [tuple(host) for host in hosting] return list(set(candidates) - set(hosting_list)) def _bind_ha_router(self, plugin, context, router_id, tenant_id, candidates): """Bind a HA router to agents based on a specific policy.""" candidates = self._filter_scheduled_agents(plugin, context, router_id, candidates) chosen_agents = self._choose_router_agents_for_ha( plugin, context, candidates) for agent in chosen_agents: self.create_ha_port_and_bind(plugin, context, router_id, tenant_id, agent) return chosen_agents class ChanceScheduler(L3Scheduler): """Randomly allocate an L3 agent for a router.""" def _choose_router_agent(self, plugin, context, candidates): return random.choice(candidates) def _choose_router_agents_for_ha(self, plugin, context, candidates): num_agents = self._get_num_of_agents_for_ha(len(candidates)) return random.sample(candidates, num_agents) class LeastRoutersScheduler(L3Scheduler): """Allocate to an L3 agent with the least number of routers bound.""" def _choose_router_agent(self, plugin, context, candidates): candidate_ids = [candidate['id'] for candidate in candidates] chosen_agent = plugin.get_l3_agent_with_min_routers( context, candidate_ids) return chosen_agent def _choose_router_agents_for_ha(self, plugin, context, candidates): num_agents = self._get_num_of_agents_for_ha(len(candidates)) ordered_agents = plugin.get_l3_agents_ordered_by_num_routers( context, [candidate['id'] for candidate in candidates]) return ordered_agents[:num_agents] class AZLeastRoutersScheduler(LeastRoutersScheduler): """Availability zone aware scheduler. If a router is ha router, allocate L3 agents distributed AZs according to router's az_hints. """ def _get_az_hints(self, router): return (router.get(az_def.AZ_HINTS) or cfg.CONF.default_availability_zones) def _get_routers_can_schedule(self, plugin, context, routers, l3_agent): """Overwrite L3Scheduler's method to filter by availability zone.""" target_routers = [] for r in routers: az_hints = self._get_az_hints(r) if not az_hints or l3_agent['availability_zone'] in az_hints: target_routers.append(r) if not target_routers: return [] return super(AZLeastRoutersScheduler, self)._get_routers_can_schedule( plugin, context, target_routers, l3_agent) def _get_candidates(self, plugin, context, sync_router): """Overwrite L3Scheduler's method to filter by availability zone.""" all_candidates = ( super(AZLeastRoutersScheduler, self)._get_candidates( plugin, context, sync_router)) candidates = [] az_hints = self._get_az_hints(sync_router) for agent in all_candidates: if not az_hints or agent['availability_zone'] in az_hints: candidates.append(agent) return candidates def _choose_router_agents_for_ha(self, plugin, context, candidates): ordered_agents = plugin.get_l3_agents_ordered_by_num_routers( context, [candidate['id'] for candidate in candidates]) num_agents = self._get_num_of_agents_for_ha(len(ordered_agents)) # Order is kept in each az group_by_az = collections.defaultdict(list) for agent in ordered_agents: az = agent['availability_zone'] group_by_az[az].append(agent) selected_agents = [] for az, agents in itertools.cycle(group_by_az.items()): if not agents: continue selected_agents.append(agents.pop(0)) if len(selected_agents) >= num_agents: break return selected_agents neutron-12.1.1/neutron/scheduler/dhcp_agent_scheduler.py0000664000175000017500000003123513553660047023501 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from operator import itemgetter from neutron_lib.api.definitions import availability_zone as az_def from neutron_lib import constants from neutron_lib.objects import exceptions from oslo_config import cfg from oslo_log import log as logging from neutron.agent.common import utils as agent_utils from neutron.objects import agent as agent_obj from neutron.objects import network from neutron.scheduler import base_resource_filter from neutron.scheduler import base_scheduler LOG = logging.getLogger(__name__) class AutoScheduler(object): def auto_schedule_networks(self, plugin, context, host): """Schedule non-hosted networks to the DHCP agent on the specified host. """ agents_per_network = cfg.CONF.dhcp_agents_per_network # a list of (agent, net_ids) tuples bindings_to_add = [] with context.session.begin(subtransactions=True): fields = ['network_id', 'enable_dhcp', 'segment_id'] subnets = plugin.get_subnets(context, fields=fields) net_ids = {} net_segment_ids = collections.defaultdict(set) for s in subnets: if s['enable_dhcp']: net_segment_ids[s['network_id']].add(s.get('segment_id')) for network_id, segment_ids in net_segment_ids.items(): is_routed_network = any(segment_ids) net_ids[network_id] = is_routed_network if not net_ids: LOG.debug('No non-hosted networks') return False dhcp_agents = agent_obj.Agent.get_objects( context, agent_type=constants.AGENT_TYPE_DHCP, host=host, admin_state_up=True) segment_host_mapping = network.SegmentHostMapping.get_objects( context, host=host) segments_on_host = {s.segment_id for s in segment_host_mapping} for dhcp_agent in dhcp_agents: if agent_utils.is_agent_down( dhcp_agent.heartbeat_timestamp): LOG.warning('DHCP agent %s is not active', dhcp_agent.id) continue for net_id, is_routed_network in net_ids.items(): agents = plugin.get_dhcp_agents_hosting_networks( context, [net_id]) segments_on_network = net_segment_ids[net_id] if is_routed_network: if len(segments_on_network & segments_on_host) == 0: continue else: if len(agents) >= agents_per_network: continue if any(dhcp_agent.id == agent.id for agent in agents): continue net = plugin.get_network(context, net_id) az_hints = (net.get(az_def.AZ_HINTS) or cfg.CONF.default_availability_zones) if (az_hints and dhcp_agent['availability_zone'] not in az_hints): continue bindings_to_add.append((dhcp_agent, net_id)) # do it outside transaction so particular scheduling results don't # make other to fail for agent, net_id in bindings_to_add: self.resource_filter.bind(context, [agent], net_id) return True class ChanceScheduler(base_scheduler.BaseChanceScheduler, AutoScheduler): def __init__(self): super(ChanceScheduler, self).__init__(DhcpFilter()) class WeightScheduler(base_scheduler.BaseWeightScheduler, AutoScheduler): def __init__(self): super(WeightScheduler, self).__init__(DhcpFilter()) class AZAwareWeightScheduler(WeightScheduler): def select(self, plugin, context, resource_hostable_agents, resource_hosted_agents, num_agents_needed): """AZ aware scheduling If the network has multiple AZs, agents are scheduled as follows: - select AZ with least agents scheduled for the network - for AZs with same amount of scheduled agents, the AZ which contains least weight agent will be used first - choose agent in the AZ with WeightScheduler """ # The dict to record the agents in each AZ, the record will be sorted # according to the weight of agent. So that the agent with less weight # will be used first. hostable_az_agents = collections.defaultdict(list) # The dict to record the number of agents in each AZ. When the number # of agents in each AZ is the same and num_agents_needed is less than # the number of AZs, we want to select agents with less weight. # Use an OrderedDict here, so that the AZ with least weight agent # will be recorded first in the case described above. And, as a result, # the agent with least weight will be used first. num_az_agents = collections.OrderedDict() # resource_hostable_agents should be a list with agents in the order of # their weight. resource_hostable_agents = ( super(AZAwareWeightScheduler, self).select( plugin, context, resource_hostable_agents, resource_hosted_agents, len(resource_hostable_agents))) for agent in resource_hostable_agents: az_agent = agent['availability_zone'] hostable_az_agents[az_agent].append(agent) if az_agent not in num_az_agents: num_az_agents[az_agent] = 0 if num_agents_needed <= 0: return [] for agent in resource_hosted_agents: az_agent = agent['availability_zone'] if az_agent in num_az_agents: num_az_agents[az_agent] += 1 chosen_agents = [] while num_agents_needed > 0: # 'min' will stably output the first min value in the list. select_az = min(num_az_agents.items(), key=itemgetter(1))[0] # Select the agent in AZ with least weight. select_agent = hostable_az_agents[select_az][0] chosen_agents.append(select_agent) # Update the AZ-agents records. del hostable_az_agents[select_az][0] if not hostable_az_agents[select_az]: del num_az_agents[select_az] else: num_az_agents[select_az] += 1 num_agents_needed -= 1 return chosen_agents class DhcpFilter(base_resource_filter.BaseResourceFilter): def bind(self, context, agents, network_id): """Bind the network to the agents.""" # customize the bind logic bound_agents = agents[:] for agent in agents: # saving agent_id to use it after rollback to avoid # DetachedInstanceError agent_id = agent.id try: network.NetworkDhcpAgentBinding(context, dhcp_agent_id=agent_id, network_id=network_id).create() except exceptions.NeutronDbObjectDuplicateEntry: # it's totally ok, someone just did our job! bound_agents.remove(agent) LOG.info('Agent %s already present', agent_id) LOG.debug('Network %(network_id)s is scheduled to be ' 'hosted by DHCP agent %(agent_id)s', {'network_id': network_id, 'agent_id': agent_id}) super(DhcpFilter, self).bind(context, bound_agents, network_id) def filter_agents(self, plugin, context, network): """Return the agents that can host the network. This function returns a dictionary which has 3 keys. n_agents: The number of agents should be scheduled. If n_agents=0, all networks are already scheduled or no more agent can host the network. hostable_agents: A list of agents which can host the network. hosted_agents: A list of agents which already hosts the network. """ agents_dict = self._get_network_hostable_dhcp_agents( plugin, context, network) if not agents_dict['hostable_agents'] or agents_dict['n_agents'] <= 0: return {'n_agents': 0, 'hostable_agents': [], 'hosted_agents': agents_dict['hosted_agents']} return agents_dict def _filter_agents_with_network_access(self, plugin, context, network, hostable_agents): if 'candidate_hosts' in network: hostable_dhcp_hosts = network['candidate_hosts'] else: hostable_dhcp_hosts = plugin.filter_hosts_with_network_access( context, network['id'], [agent['host'] for agent in hostable_agents]) reachable_agents = [agent for agent in hostable_agents if agent['host'] in hostable_dhcp_hosts] return reachable_agents def _get_dhcp_agents_hosting_network(self, plugin, context, network): """Return dhcp agents hosting the given network or None if a given network is already hosted by enough number of agents. """ agents_per_network = cfg.CONF.dhcp_agents_per_network #TODO(gongysh) don't schedule the networks with only # subnets whose enable_dhcp is false with context.session.begin(subtransactions=True): network_hosted_agents = plugin.get_dhcp_agents_hosting_networks( context, [network['id']], hosts=network.get('candidate_hosts')) if len(network_hosted_agents) >= agents_per_network: LOG.debug('Network %s is already hosted by enough agents.', network['id']) return return network_hosted_agents def _get_active_agents(self, plugin, context, az_hints): """Return a list of active dhcp agents.""" with context.session.begin(subtransactions=True): filters = {'agent_type': [constants.AGENT_TYPE_DHCP], 'admin_state_up': [True]} if az_hints: filters['availability_zone'] = az_hints active_dhcp_agents = plugin.get_agent_objects( context, filters=filters) if not active_dhcp_agents: LOG.warning('No more DHCP agents') return [] return active_dhcp_agents def _get_network_hostable_dhcp_agents(self, plugin, context, network): """Provide information on hostable DHCP agents for network. The returned value includes the number of agents that will actually host the given network, a list of DHCP agents that can host the given network, and a list of DHCP agents currently hosting the network. """ hosted_agents = self._get_dhcp_agents_hosting_network(plugin, context, network) if hosted_agents is None: return {'n_agents': 0, 'hostable_agents': [], 'hosted_agents': []} n_agents = cfg.CONF.dhcp_agents_per_network - len(hosted_agents) az_hints = (network.get(az_def.AZ_HINTS) or cfg.CONF.default_availability_zones) active_dhcp_agents = self._get_active_agents(plugin, context, az_hints) hosted_agent_ids = [agent['id'] for agent in hosted_agents] if not active_dhcp_agents: return {'n_agents': 0, 'hostable_agents': [], 'hosted_agents': hosted_agents} hostable_dhcp_agents = [ agent for agent in active_dhcp_agents if agent.id not in hosted_agent_ids and plugin.is_eligible_agent( context, True, agent)] hostable_dhcp_agents = self._filter_agents_with_network_access( plugin, context, network, hostable_dhcp_agents) if not hostable_dhcp_agents: return {'n_agents': 0, 'hostable_agents': [], 'hosted_agents': hosted_agents} n_agents = min(len(hostable_dhcp_agents), n_agents) return {'n_agents': n_agents, 'hostable_agents': hostable_dhcp_agents, 'hosted_agents': hosted_agents} neutron-12.1.1/neutron/scheduler/__init__.py0000664000175000017500000000000013553660046021067 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/scheduler/base_scheduler.py0000664000175000017500000000552313553660047022320 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from operator import attrgetter import random import six @six.add_metaclass(abc.ABCMeta) class BaseScheduler(object): """The base scheduler (agnostic to resource type). Child classes of BaseScheduler must define the self.resource_filter to filter agents of particular type. """ resource_filter = None @abc.abstractmethod def select(self, plugin, context, resource_hostable_agents, resource_hosted_agents, num_agents_needed): """Return a subset of agents based on the specific scheduling logic.""" def schedule(self, plugin, context, resource): """Select and bind agents to a given resource.""" if not self.resource_filter: return # filter the agents that can host the resource filtered_agents_dict = self.resource_filter.filter_agents( plugin, context, resource) num_agents = filtered_agents_dict['n_agents'] hostable_agents = filtered_agents_dict['hostable_agents'] hosted_agents = filtered_agents_dict['hosted_agents'] chosen_agents = self.select(plugin, context, hostable_agents, hosted_agents, num_agents) # bind the resource to the agents self.resource_filter.bind(context, chosen_agents, resource['id']) return chosen_agents class BaseChanceScheduler(BaseScheduler): """Choose agents randomly.""" def __init__(self, resource_filter): self.resource_filter = resource_filter def select(self, plugin, context, resource_hostable_agents, resource_hosted_agents, num_agents_needed): chosen_agents = random.sample(resource_hostable_agents, num_agents_needed) return chosen_agents class BaseWeightScheduler(BaseScheduler): """Choose agents based on load.""" def __init__(self, resource_filter): self.resource_filter = resource_filter def select(self, plugin, context, resource_hostable_agents, resource_hosted_agents, num_agents_needed): chosen_agents = sorted(resource_hostable_agents, key=attrgetter('load'))[0:num_agents_needed] return chosen_agents neutron-12.1.1/neutron/__init__.py0000664000175000017500000000175513553660047017134 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import gettext from debtcollector import removals import six if six.PY2: gettext.install('neutron', unicode=1) else: gettext.install('neutron') # flake8: noqa six.moves.builtins.__dict__['_'] = removals.remove( message='Builtin _ translation function is deprecated in OpenStack; ' 'use the function from _i18n module for your project.')(_) neutron-12.1.1/neutron/pecan_wsgi/0000775000175000017500000000000013553660156017133 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/pecan_wsgi/app.py0000664000175000017500000000346213553660046020270 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pecan from neutron.pecan_wsgi.controllers import root from neutron.pecan_wsgi import hooks from neutron.pecan_wsgi import startup def versions_factory(global_config, **local_config): return pecan.make_app(root.RootController()) def v2_factory(global_config, **local_config): # Processing Order: # As request enters lower priority called before higher. # Response from controller is passed from higher priority to lower. app_hooks = [ hooks.UserFilterHook(), # priority 90 hooks.ContextHook(), # priority 95 hooks.ExceptionTranslationHook(), # priority 100 hooks.BodyValidationHook(), # priority 120 hooks.OwnershipValidationHook(), # priority 125 hooks.QuotaEnforcementHook(), # priority 130 hooks.NotifierHook(), # priority 135 hooks.QueryParametersHook(), # priority 139 hooks.PolicyHook(), # priority 140 ] app = pecan.make_app(root.V2Controller(), debug=False, force_canonical=False, hooks=app_hooks, guess_content_type_from_ext=True) startup.initialize_all() return app neutron-12.1.1/neutron/pecan_wsgi/constants.py0000664000175000017500000000132313553660046021516 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. ACTION_MAP = {'POST': 'create', 'PUT': 'update', 'GET': 'get', 'DELETE': 'delete'} neutron-12.1.1/neutron/pecan_wsgi/hooks/0000775000175000017500000000000013553660156020256 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/pecan_wsgi/hooks/quota_enforcement.py0000664000175000017500000000665713553660047024363 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from oslo_log import log as logging from pecan import hooks from neutron.common import exceptions from neutron.db import api as db_api from neutron import manager from neutron import quota from neutron.quota import resource_registry LOG = logging.getLogger(__name__) class QuotaEnforcementHook(hooks.PecanHook): priority = 130 def before(self, state): collection = state.request.context.get('collection') resource = state.request.context.get('resource') items = state.request.context.get('resources') if state.request.method != 'POST' or not resource or not items: return plugin = manager.NeutronManager.get_plugin_for_resource(collection) # Store requested resource amounts grouping them by tenant deltas = collections.Counter(map(lambda x: x['tenant_id'], items)) # Perform quota enforcement reservations = [] neutron_context = state.request.context.get('neutron_context') for (tenant_id, delta) in deltas.items(): try: reservation = quota.QUOTAS.make_reservation( neutron_context, tenant_id, {resource: delta}, plugin) LOG.debug("Made reservation on behalf of %(tenant_id)s " "for: %(delta)s", {'tenant_id': tenant_id, 'delta': {resource: delta}}) reservations.append(reservation) except exceptions.QuotaResourceUnknown as e: # Quotas cannot be enforced on this resource LOG.debug(e) # Save the reservations in the request context so that they can be # retrieved in the 'after' hook state.request.context['reservations'] = reservations def after(self, state): neutron_context = state.request.context.get('neutron_context') if not neutron_context: return collection = state.request.context.get('collection') resource = state.request.context.get('resource') if state.request.method == 'GET' and collection: # resync on list operations to preserve behavior of old API resource_registry.resync_resource( neutron_context, resource, neutron_context.tenant_id) # Commit reservation(s) reservations = state.request.context.get('reservations') or [] if not reservations and state.request.method != 'DELETE': return with db_api.context_manager.writer.using(neutron_context): # Commit the reservation(s) for reservation in reservations: quota.QUOTAS.commit_reservation( neutron_context, reservation.reservation_id) resource_registry.set_resources_dirty(neutron_context) neutron-12.1.1/neutron/pecan_wsgi/hooks/policy_enforcement.py0000664000175000017500000002702413553660047024520 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from oslo_log import log as logging from oslo_policy import policy as oslo_policy from oslo_utils import excutils from pecan import hooks import webob from neutron._i18n import _ from neutron.common import constants as const from neutron.extensions import quotasv2 from neutron import manager from neutron.pecan_wsgi import constants as pecan_constants from neutron.pecan_wsgi.controllers import quota from neutron.pecan_wsgi.hooks import utils from neutron import policy LOG = logging.getLogger(__name__) def _custom_getter(resource, resource_id): """Helper function to retrieve resources not served by any plugin.""" if resource == quotasv2.RESOURCE_NAME: return quota.get_tenant_quotas(resource_id)[quotasv2.RESOURCE_NAME] def fetch_resource(method, neutron_context, controller, collection, resource, resource_id, parent_id=None): field_list = [] if method == 'PUT': attrs = controller.resource_info if not attrs: # this isn't a request for a normal resource. it could be # an action like removing a network from a dhcp agent. # return None and assume the custom controller for this will # handle the necessary logic. return field_list = [name for (name, value) in attrs.items() if (value.get('required_by_policy') or value.get('primary_key') or 'default' not in value)] plugin = manager.NeutronManager.get_plugin_for_resource(collection) if plugin: if utils.is_member_action(controller): getter = controller.parent_controller.plugin_shower else: getter = controller.plugin_shower getter_args = [neutron_context, resource_id] if parent_id: getter_args.append(parent_id) return getter(*getter_args, fields=field_list) else: # Some legit resources, like quota, do not have a plugin yet. # Retrieving the original object is nevertheless important # for policy checks. return _custom_getter(resource, resource_id) class PolicyHook(hooks.PecanHook): priority = 140 def before(self, state): # This hook should be run only for PUT,POST and DELETE methods and for # requests targeting a neutron resource resources = state.request.context.get('resources', []) if state.request.method not in ('POST', 'PUT', 'DELETE'): return # As this routine will likely alter the resources, do a shallow copy resources_copy = resources[:] neutron_context = state.request.context.get('neutron_context') resource = state.request.context.get('resource') # If there is no resource for this request, don't bother running authZ # policies if not resource: return controller = utils.get_controller(state) if not controller or utils.is_member_action(controller): return collection = state.request.context.get('collection') needs_prefetch = (state.request.method == 'PUT' or state.request.method == 'DELETE') policy.init() action = controller.plugin_handlers[ pecan_constants.ACTION_MAP[state.request.method]] # NOTE(salv-orlando): As bulk updates are not supported, in case of PUT # requests there will be only a single item to process, and its # identifier would have been already retrieved by the lookup process; # in the case of DELETE requests there won't be any item to process in # the request body original_resources = [] if needs_prefetch: try: item = resources_copy.pop() except IndexError: # Ops... this was a delete after all! item = {} resource_id = state.request.context.get('resource_id') parent_id = state.request.context.get('parent_id') method = state.request.method resource_obj = fetch_resource(method, neutron_context, controller, collection, resource, resource_id, parent_id=parent_id) if resource_obj: original_resources.append(resource_obj) obj = copy.copy(resource_obj) obj.update(item) obj[const.ATTRIBUTES_TO_UPDATE] = list(item) # Put back the item in the list so that policies could be # enforced resources_copy.append(obj) # TODO(salv-orlando): as other hooks might need to prefetch resources, # store them in the request context. However, this should be done in a # separate hook which is conveniently called before all other hooks state.request.context['original_resources'] = original_resources for item in resources_copy: try: policy.enforce( neutron_context, action, item, pluralized=collection) except oslo_policy.PolicyNotAuthorized: with excutils.save_and_reraise_exception() as ctxt: # If a tenant is modifying it's own object, it's safe to # return a 403. Otherwise, pretend that it doesn't exist # to avoid giving away information. controller = utils.get_controller(state) s_action = controller.plugin_handlers[controller.SHOW] if not policy.check(neutron_context, s_action, item, pluralized=collection): ctxt.reraise = False msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) def after(self, state): neutron_context = state.request.context.get('neutron_context') resource = state.request.context.get('resource') collection = state.request.context.get('collection') controller = utils.get_controller(state) if not resource: # can't filter a resource we don't recognize return # NOTE(kevinbenton): extension listing isn't controlled by policy if resource == 'extension': return try: data = state.response.json except ValueError: return if state.request.method not in pecan_constants.ACTION_MAP: return if not data or (resource not in data and collection not in data): return policy.init() is_single = resource in data action_type = pecan_constants.ACTION_MAP[state.request.method] if action_type == 'get': action = controller.plugin_handlers[controller.SHOW] else: action = controller.plugin_handlers[action_type] key = resource if is_single else collection to_process = [data[resource]] if is_single else data[collection] # in the single case, we enforce which raises on violation # in the plural case, we just check so violating items are hidden policy_method = policy.enforce if is_single else policy.check plugin = manager.NeutronManager.get_plugin_for_resource(collection) try: resp = [self._get_filtered_item(state.request, controller, resource, collection, item) for item in to_process if (state.request.method != 'GET' or policy_method(neutron_context, action, item, plugin=plugin, pluralized=collection))] except oslo_policy.PolicyNotAuthorized: # This exception must be explicitly caught as the exception # translation hook won't be called if an error occurs in the # 'after' handler. Instead of raising an HTTPNotFound exception, # we have to set the status_code here to prevent the catch_errors # middleware from turning this into a 500. state.response.status_code = 404 return if is_single: resp = resp[0] state.response.json = {key: resp} def _get_filtered_item(self, request, controller, resource, collection, data): neutron_context = request.context.get('neutron_context') to_exclude = self._exclude_attributes_by_policy( neutron_context, controller, resource, collection, data) return self._filter_attributes(request, data, to_exclude) def _filter_attributes(self, request, data, fields_to_strip): # This routine will remove the fields that were requested to the # plugin for policy evaluation but were not specified in the # API request return dict(item for item in data.items() if item[0] not in fields_to_strip) def _exclude_attributes_by_policy(self, context, controller, resource, collection, data): """Identifies attributes to exclude according to authZ policies. Return a list of attribute names which should be stripped from the response returned to the user because the user is not authorized to see them. """ attributes_to_exclude = [] for attr_name in list(data): # TODO(amotoki): All attribute maps have tenant_id and # it determines excluded attributes based on tenant_id. # We need to migrate tenant_id to project_id later # as attr_info is referred to in various places and we need # to check all logs carefully. if attr_name == 'project_id': continue attr_data = controller.resource_info.get(attr_name) if attr_data and attr_data['is_visible']: if policy.check( context, # NOTE(kevinbenton): this used to reference a # _plugin_handlers dict, why? 'get_%s:%s' % (resource, attr_name), data, might_not_exist=True, pluralized=collection): # this attribute is visible, check next one continue # if the code reaches this point then either the policy check # failed or the attribute was not visible in the first place attributes_to_exclude.append(attr_name) # TODO(amotoki): As mentioned in the above TODO, # we treat project_id and tenant_id equivalently. # This should be migrated to project_id later. if attr_name == 'tenant_id': attributes_to_exclude.append('project_id') if attributes_to_exclude: LOG.debug("Attributes excluded by policy engine: %s", attributes_to_exclude) return attributes_to_exclude neutron-12.1.1/neutron/pecan_wsgi/hooks/utils.py0000664000175000017500000000221013553660046021761 0ustar zuulzuul00000000000000# Copyright (c) 2015 Taturiello Consulting, Meh. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.pecan_wsgi.controllers import resource from neutron.pecan_wsgi.controllers import utils as controller_utils def get_controller(state): if (state.arguments and state.arguments.args and isinstance(state.arguments.args[0], controller_utils.NeutronPecanController)): controller = state.arguments.args[0] return controller def is_member_action(controller): return isinstance(controller, resource.MemberActionController) neutron-12.1.1/neutron/pecan_wsgi/hooks/query_parameters.py0000664000175000017500000001445113553660047024224 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from pecan import hooks from neutron.api import api_common from neutron import manager from neutron.pecan_wsgi.hooks import policy_enforcement from neutron.pecan_wsgi.hooks import utils # TODO(blogan): ideally it'd be nice to get the pagination and sorting # helpers from the controller but since the controllers are # instantiated at startup and not on request, it would cause race # conditions because we need a new instantiation of a pagination # and sorting helper per request/response flow. As a result, we're forced to # pass them through the request context. def _get_pagination_helper(request, controller): if 'pagination_helper' in request.context: return request.context['pagination_helper'] if not controller.allow_pagination: helper = api_common.NoPaginationHelper(request, controller.primary_key) elif controller.native_pagination: helper = api_common.PaginationNativeHelper(request, controller.primary_key) else: helper = api_common.PaginationEmulatedHelper(request, controller.primary_key) request.context['pagination_helper'] = helper return helper def _get_sorting_helper(request, controller): if 'sorting_helper' in request.context: return request.context['sorting_helper'] if not controller.allow_sorting: helper = api_common.NoSortingHelper(request, controller.resource_info) elif controller.native_sorting: helper = api_common.SortingNativeHelper(request, controller.resource_info) else: helper = api_common.SortingEmulatedHelper(request, controller.resource_info) request.context['sorting_helper'] = helper return helper def _listify(thing): return thing if isinstance(thing, list) else [thing] def _set_fields(state, controller): params = state.request.params.mixed() fields = params.get('fields', []) # if only one fields query parameter is passed, pecan will not put # that parameter in a list, so we need to convert it into a list fields = _listify(fields) combined_fields, added_fields = controller.build_field_list(fields) state.request.context['query_params']['fields'] = combined_fields state.request.context['added_fields'] = added_fields return combined_fields, added_fields def _set_filters(state, controller): params = state.request.params.mixed() filters = api_common.get_filters_from_dict( {k: _listify(v) for k, v in params.items()}, controller.resource_info, skips=['fields', 'sort_key', 'sort_dir', 'limit', 'marker', 'page_reverse']) return filters class QueryParametersHook(hooks.PecanHook): # NOTE(blogan): needs to be run after the priority hook. after methods # are run in reverse priority order. priority = policy_enforcement.PolicyHook.priority - 1 def before(self, state): self._process_if_match_headers(state) state.request.context['query_params'] = {} if state.request.method != 'GET': return collection = state.request.context.get('collection') if not collection: return controller = utils.get_controller(state) combined_fields, added_fields = _set_fields(state, controller) filters = _set_filters(state, controller) query_params = {'fields': combined_fields, 'filters': filters} pagination_helper = _get_pagination_helper(state.request, controller) sorting_helper = _get_sorting_helper(state.request, controller) sorting_helper.update_args(query_params) sorting_helper.update_fields(query_params.get('fields', []), added_fields) pagination_helper.update_args(query_params) pagination_helper.update_fields(query_params.get('fields', []), added_fields) state.request.context['query_params'] = query_params def _process_if_match_headers(self, state): collection = state.request.context.get('collection') if not collection: return # add in if-match criterion to the context if present revision_number = api_common.check_request_for_revision_constraint( state.request) if revision_number is None: return state.request.context['neutron_context'].set_transaction_constraint( collection, state.request.context['resource_id'], revision_number) def after(self, state): resource = state.request.context.get('resource') collection = state.request.context.get('collection') # NOTE(blogan): don't paginate extension list or non-GET requests if (not resource or resource == 'extension' or state.request.method != 'GET'): return try: data = state.response.json except ValueError: return # Do not attempt to paginate if the body is not a list of entities if not data or resource in data or collection not in data: return controller = manager.NeutronManager.get_controller_for_resource( collection) sorting_helper = _get_sorting_helper(state.request, controller) pagination_helper = _get_pagination_helper(state.request, controller) obj_list = sorting_helper.sort(data[collection]) obj_list = pagination_helper.paginate(obj_list) resp_body = {collection: obj_list} pagination_links = pagination_helper.get_links(obj_list) if pagination_links: resp_body['_'.join([collection, 'links'])] = pagination_links state.response.json = resp_body neutron-12.1.1/neutron/pecan_wsgi/hooks/notifier.py0000664000175000017500000001115113553660047022445 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from oslo_log import log from pecan import hooks from neutron.common import rpc as n_rpc from neutron.pecan_wsgi import constants as pecan_constants from neutron.pecan_wsgi.hooks import utils LOG = log.getLogger(__name__) class NotifierHook(hooks.PecanHook): priority = 135 @property def _notifier(self): if not hasattr(self, '_notifier_inst'): self._notifier_inst = n_rpc.get_notifier('network') return self._notifier_inst def before(self, state): if state.request.method not in ('POST', 'PUT', 'DELETE'): return resource = state.request.context.get('resource') if not resource: return if utils.is_member_action(utils.get_controller(state)): return action = pecan_constants.ACTION_MAP.get(state.request.method) event = '%s.%s.start' % (resource, action) if action in ('create', 'update'): # notifier just gets plain old body without any treatment other # than the population of the object ID being operated on try: payload = state.request.json.copy() if not payload: return except ValueError: return if action == 'update': payload['id'] = state.request.context.get('resource_id') elif action == 'delete': resource_id = state.request.context.get('resource_id') payload = {resource + '_id': resource_id} self._notifier.info(state.request.context.get('neutron_context'), event, payload) def after(self, state): resource_name = state.request.context.get('resource') collection_name = state.request.context.get('collection') neutron_context = state.request.context.get('neutron_context') action = pecan_constants.ACTION_MAP.get(state.request.method) if not action or action not in ('create', 'update', 'delete'): return if utils.is_member_action(utils.get_controller(state)): return if not resource_name: LOG.debug("Skipping NotifierHook processing as there was no " "resource associated with the request") return if state.response.status_int > 300: LOG.debug("No notification will be sent due to unsuccessful " "status code: %s", state.response.status_int) return original = {} if (action in ('delete', 'update') and state.request.context.get('original_resources', [])): # We only need the original resource for updates and deletes original = state.request.context.get('original_resources')[0] if action == 'delete': # The object has been deleted, so we must notify the agent with the # data of the original object as the payload, but we do not need # to pass it in as the original result = {resource_name: original} original = {} else: if not state.response.body: result = {} else: result = state.response.json notifier_method = '%s.%s.end' % (resource_name, action) notifier_action = utils.get_controller(state).plugin_handlers[action] registry.publish(resource_name, events.BEFORE_RESPONSE, self, payload=events.APIEventPayload( neutron_context, notifier_method, notifier_action, request_body=state.request.body, states=(original, result,), collection_name=collection_name)) if action == 'delete': resource_id = state.request.context.get('resource_id') result[resource_name + '_id'] = resource_id self._notifier.info(neutron_context, notifier_method, result) neutron-12.1.1/neutron/pecan_wsgi/hooks/translation.py0000664000175000017500000000273113553660047023170 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import faults import oslo_i18n from oslo_log import log as logging from pecan import hooks from neutron.api import api_common LOG = logging.getLogger(__name__) class ExceptionTranslationHook(hooks.PecanHook): def on_error(self, state, e): language = None if state.request.accept_language: language = state.request.accept_language.best_match( oslo_i18n.get_available_languages('neutron')) exc = api_common.convert_exception_to_http_exc(e, faults.FAULT_MAP, language) if hasattr(exc, 'code') and 400 <= exc.code < 500: LOG.info('%(action)s failed (client error): %(exc)s', {'action': state.request.method, 'exc': exc}) else: LOG.exception('%s failed.', state.request.method) return exc neutron-12.1.1/neutron/pecan_wsgi/hooks/__init__.py0000664000175000017500000000304313553660046022365 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.pecan_wsgi.hooks import body_validation from neutron.pecan_wsgi.hooks import context from neutron.pecan_wsgi.hooks import notifier from neutron.pecan_wsgi.hooks import ownership_validation from neutron.pecan_wsgi.hooks import policy_enforcement from neutron.pecan_wsgi.hooks import query_parameters from neutron.pecan_wsgi.hooks import quota_enforcement from neutron.pecan_wsgi.hooks import translation from neutron.pecan_wsgi.hooks import userfilters ExceptionTranslationHook = translation.ExceptionTranslationHook ContextHook = context.ContextHook BodyValidationHook = body_validation.BodyValidationHook OwnershipValidationHook = ownership_validation.OwnershipValidationHook PolicyHook = policy_enforcement.PolicyHook QuotaEnforcementHook = quota_enforcement.QuotaEnforcementHook NotifierHook = notifier.NotifierHook QueryParametersHook = query_parameters.QueryParametersHook UserFilterHook = userfilters.UserFilterHook neutron-12.1.1/neutron/pecan_wsgi/hooks/ownership_validation.py0000664000175000017500000000412313553660046025056 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins import directory from pecan import hooks import webob from neutron._i18n import _ class OwnershipValidationHook(hooks.PecanHook): priority = 125 def before(self, state): if state.request.method != 'POST': return for item in state.request.context.get('resources', []): self._validate_network_tenant_ownership(state, item) def _validate_network_tenant_ownership(self, state, resource_item): # TODO(salvatore-orlando): consider whether this check can be folded # in the policy engine neutron_context = state.request.context.get('neutron_context') resource = state.request.context.get('resource') if (neutron_context.is_admin or neutron_context.is_advsvc or resource not in ('port', 'subnet')): return plugin = directory.get_plugin() network = plugin.get_network(neutron_context, resource_item['network_id']) # do not perform the check on shared networks if network.get('shared'): return network_owner = network['tenant_id'] if network_owner != resource_item['tenant_id']: msg = _("Tenant %(tenant_id)s not allowed to " "create %(resource)s on this network") raise webob.exc.HTTPForbidden(msg % { "tenant_id": resource_item['tenant_id'], "resource": resource, }) neutron-12.1.1/neutron/pecan_wsgi/hooks/context.py0000664000175000017500000000200413553660047022307 0ustar zuulzuul00000000000000# Copyright 2012 New Dream Network, LLC (DreamHost) # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import context from pecan import hooks class ContextHook(hooks.PecanHook): """Moves the request env's neutron.context into the requests context.""" priority = 95 def before(self, state): ctx = (state.request.environ.get('neutron.context') or context.get_admin_context()) state.request.context['neutron_context'] = ctx neutron-12.1.1/neutron/pecan_wsgi/hooks/userfilters.py0000664000175000017500000000337713553660046023207 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from pecan import hooks class UserFilterHook(hooks.PecanHook): # we do this at the very end to ensure user-defined filters # don't impact things like pagination and notification hooks priority = 90 def after(self, state): user_fields = state.request.params.getall('fields') if not user_fields: return try: data = state.response.json except ValueError: return resource = state.request.context.get('resource') collection = state.request.context.get('collection') if collection not in data and resource not in data: return is_single = resource in data key = resource if resource in data else collection if is_single: data[key] = self._filter_item( state.response.json[key], user_fields) else: data[key] = [ self._filter_item(i, user_fields) for i in state.response.json[key] ] state.response.json = data def _filter_item(self, item, fields): return { field: value for field, value in item.items() if field in fields } neutron-12.1.1/neutron/pecan_wsgi/hooks/body_validation.py0000664000175000017500000000521113553660046023774 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils from pecan import hooks import webob.exc from neutron._i18n import _ from neutron.api.v2 import base as v2_base from neutron.pecan_wsgi.hooks import utils class BodyValidationHook(hooks.PecanHook): priority = 120 def before(self, state): if state.request.method not in ('POST', 'PUT'): return resource = state.request.context.get('resource') collection = state.request.context.get('collection') neutron_context = state.request.context['neutron_context'] is_create = state.request.method == 'POST' if not resource: return if not state.request.body: return try: json_data = jsonutils.loads(state.request.body) if not isinstance(json_data, dict): raise ValueError() except ValueError: msg = _("Body contains invalid data") raise webob.exc.HTTPBadRequest(msg) # Raw data are consumed by member actions such as add_router_interface state.request.context['request_data'] = json_data if not (resource in json_data or collection in json_data): # there is no resource in the request. This can happen when a # member action is being processed or on agent scheduler operations return # Prepare data to be passed to the plugin from request body controller = utils.get_controller(state) data = v2_base.Controller.prepare_request_body( neutron_context, json_data, is_create, resource, controller.resource_info, allow_bulk=is_create) if collection in data: state.request.context['resources'] = [item[resource] for item in data[collection]] state.request.context['is_bulk'] = True else: state.request.context['resources'] = [data[resource]] state.request.context['is_bulk'] = False neutron-12.1.1/neutron/pecan_wsgi/__init__.py0000664000175000017500000000000013553660046021230 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/pecan_wsgi/controllers/0000775000175000017500000000000013553660156021501 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/pecan_wsgi/controllers/utils.py0000664000175000017500000004252213553660047023217 0ustar zuulzuul00000000000000# Copyright (c) 2015 Taturiello Consulting, Meh. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from collections import defaultdict import copy import functools from neutron_lib import constants from oslo_log import log as logging from oslo_utils import excutils import pecan from pecan import request from neutron._i18n import _ from neutron.api import api_common from neutron.api.v2 import attributes as api_attributes from neutron.db import api as db_api from neutron import manager from neutron_lib import exceptions # Utility functions for Pecan controllers. LOG = logging.getLogger(__name__) class Fakecode(object): co_varnames = () def _composed(*decorators): """Takes a list of decorators and returns a single decorator.""" def final_decorator(f): for d in decorators: # workaround for pecan bug that always assumes decorators # have a __code__ attr if not hasattr(d, '__code__'): setattr(d, '__code__', Fakecode()) f = d(f) return f return final_decorator def _protect_original_resources(f): """Wrapper to ensure that mutated resources are discarded on retries.""" @functools.wraps(f) def wrapped(*args, **kwargs): ctx = request.context if 'resources' in ctx: orig = ctx.get('protected_resources') if not orig: # this is the first call so we just take the whole reference ctx['protected_resources'] = ctx['resources'] # TODO(blogan): Once bug 157751 is fixed and released in # neutron-lib this memo will no longer be needed. This is just # quick way to not depend on a release of neutron-lib. # The version that has that bug fix will need to be updated in # neutron-lib. memo = {id(constants.ATTR_NOT_SPECIFIED): constants.ATTR_NOT_SPECIFIED} ctx['resources'] = copy.deepcopy(ctx['protected_resources'], memo=memo) return f(*args, **kwargs) return wrapped def _pecan_generator_wrapper(func, *args, **kwargs): """Helper function so we don't have to specify json for everything.""" kwargs.setdefault('content_type', 'application/json') kwargs.setdefault('template', 'json') return _composed(_protect_original_resources, db_api.retry_db_errors, func(*args, **kwargs)) def expose(*args, **kwargs): return _pecan_generator_wrapper(pecan.expose, *args, **kwargs) def when(index, *args, **kwargs): return _pecan_generator_wrapper(index.when, *args, **kwargs) def when_delete(index, *args, **kwargs): kwargs['method'] = 'DELETE' deco = _pecan_generator_wrapper(index.when, *args, **kwargs) return _composed(_set_del_code, deco) def _set_del_code(f): """Handle logic of disabling json templating engine and setting HTTP code. We return 204 on delete without content. However, pecan defaults empty responses with the json template engine to 'null', which is not empty content. This breaks connection re-use for some clients due to the inconsistency. So we need to detect when there is no response and disable the json templating engine. See https://github.com/pecan/pecan/issues/72 """ @functools.wraps(f) def wrapped(*args, **kwargs): f(*args, **kwargs) pecan.response.status = 204 pecan.override_template(None) # NOTE(kevinbenton): we are explicitly not returning the DELETE # response from the controller because that is the legacy Neutron # API behavior. return wrapped class NeutronPecanController(object): LIST = 'list' SHOW = 'show' CREATE = 'create' UPDATE = 'update' DELETE = 'delete' def __init__(self, collection, resource, plugin=None, resource_info=None, allow_pagination=None, allow_sorting=None, parent_resource=None, member_actions=None, collection_actions=None, item=None, action_status=None): # Ensure dashes are always replaced with underscores self.collection = collection and collection.replace('-', '_') self.resource = resource and resource.replace('-', '_') self._member_actions = member_actions or {} self._collection_actions = collection_actions or {} self._resource_info = resource_info self._plugin = plugin # Controllers for some resources that are not mapped to anything in # RESOURCE_ATTRIBUTE_MAP will not have anything in _resource_info if self.resource_info: self._mandatory_fields = set([field for (field, data) in self.resource_info.items() if data.get('required_by_policy')]) if 'tenant_id' in self._mandatory_fields: # ensure that project_id is queried in the database when # tenant_id is required self._mandatory_fields.add('project_id') else: self._mandatory_fields = set() self.allow_pagination = allow_pagination if self.allow_pagination is None: self.allow_pagination = True self.allow_sorting = allow_sorting if self.allow_sorting is None: self.allow_sorting = True self.native_pagination = api_common.is_native_pagination_supported( self.plugin) self.native_sorting = api_common.is_native_sorting_supported( self.plugin) if self.allow_pagination and self.native_pagination: if not self.native_sorting: raise exceptions.Invalid( _("Native pagination depends on native sorting") ) self.primary_key = self._get_primary_key() self.parent = parent_resource parent_resource = '_%s' % parent_resource if parent_resource else '' self._parent_id_name = ('%s_id' % self.parent if self.parent else None) self._plugin_handlers = { self.LIST: 'get%s_%s' % (parent_resource, self.collection), self.SHOW: 'get%s_%s' % (parent_resource, self.resource) } for action in [self.CREATE, self.UPDATE, self.DELETE]: self._plugin_handlers[action] = '%s%s_%s' % ( action, parent_resource, self.resource) self.item = item self.action_status = action_status or {} def _set_response_code(self, result, method_name): if method_name in self.action_status: pecan.response.status = self.action_status[method_name] else: pecan.response.status = 200 if result else 204 def build_field_list(self, request_fields): added_fields = [] combined_fields = [] req_fields_set = {f for f in request_fields if f} if req_fields_set: added_fields = self._mandatory_fields - req_fields_set combined_fields = req_fields_set | self._mandatory_fields # field sorting is to match old behavior of legacy API and to make # this drop-in compatible with the old API unit tests return sorted(combined_fields), list(added_fields) @property def plugin(self): if not self._plugin: self._plugin = manager.NeutronManager.get_plugin_for_resource( self.collection) return self._plugin @property def resource_info(self): if not self._resource_info: self._resource_info = api_attributes.get_collection_info( self.collection) return self._resource_info def _get_primary_key(self, default_primary_key='id'): if not self.resource_info: return default_primary_key for key, value in self.resource_info.items(): if value.get('primary_key', False): return key return default_primary_key @property def plugin_handlers(self): return self._plugin_handlers @property def plugin_lister(self): return getattr(self.plugin, self._plugin_handlers[self.LIST]) @property def plugin_shower(self): return getattr(self.plugin, self._plugin_handlers[self.SHOW]) @property def plugin_creator(self): return getattr(self.plugin, self._plugin_handlers[self.CREATE]) @property def plugin_bulk_creator(self): native = getattr(self.plugin, '%s_bulk' % self._plugin_handlers[self.CREATE], None) # NOTE(kevinbenton): this flag is just to make testing easier since we # don't have any in-tree plugins without native bulk support if getattr(self.plugin, '_FORCE_EMULATED_BULK', False) or not native: return self._emulated_bulk_creator return native def _emulated_bulk_creator(self, context, **kwargs): objs = [] body = kwargs[self.collection] try: for item in body[self.collection]: objs.append(self.plugin_creator(context, item)) return objs except Exception: with excutils.save_and_reraise_exception(): for obj in objs: try: self.plugin_deleter(context, obj['id']) except Exception: LOG.exception("Unable to undo bulk create for " "%(resource)s %(id)s", {'resource': self.collection, 'id': obj['id']}) @property def plugin_deleter(self): return getattr(self.plugin, self._plugin_handlers[self.DELETE]) @property def plugin_updater(self): return getattr(self.plugin, self._plugin_handlers[self.UPDATE]) class ShimRequest(object): def __init__(self, context): self.context = context def invert_dict(dictionary): inverted = defaultdict(list) for k, v in dictionary.items(): inverted[v].append(k) return inverted class ShimItemController(NeutronPecanController): def __init__(self, collection, resource, item, controller, collection_actions=None, member_actions=None, action_status=None): super(ShimItemController, self).__init__( collection, resource, collection_actions=collection_actions, member_actions=member_actions, item=item, action_status=action_status) self.controller = controller self.controller_delete = getattr(controller, 'delete', None) self.controller_update = getattr(controller, 'update', None) self.controller_show = getattr(controller, 'show', None) self.inverted_collection_actions = invert_dict( self._collection_actions) @expose(generic=True) def index(self): shim_request = ShimRequest(request.context['neutron_context']) kwargs = request.context['uri_identifiers'] if self.item in self.inverted_collection_actions['GET']: method = getattr(self.controller, self.item, None) # collection actions should not take an self.item because they are # essentially static items. result = method(shim_request, **kwargs) self._set_response_code(result, self.item) return result elif not self.controller_show: pecan.abort(405) else: result = self.controller_show(shim_request, self.item, **kwargs) self._set_response_code(result, 'show') return result @when_delete(index) def delete(self): if not self.controller_delete: pecan.abort(405) shim_request = ShimRequest(request.context['neutron_context']) uri_identifiers = request.context['uri_identifiers'] result = self.controller_delete(shim_request, self.item, **uri_identifiers) self._set_response_code(result, 'delete') return result @when(index, method='PUT') def update(self): if not self.controller_update: pecan.abort(405) pecan.response.status = self.action_status.get('update', 201) shim_request = ShimRequest(request.context['neutron_context']) kwargs = request.context['uri_identifiers'] try: kwargs['body'] = request.context['request_data'] except KeyError: pass result = self.controller_update(shim_request, self.item, **kwargs) self._set_response_code(result, 'update') return result @expose() def _lookup(self, resource, *remainder): request.context['resource'] = self.resource return ShimMemberActionController(self.collection, resource, self.item, self.controller, self._member_actions), remainder class ShimCollectionsController(NeutronPecanController): def __init__(self, collection, resource, controller, collection_actions=None, member_actions=None, collection_methods=None, action_status=None): collection_methods = collection_methods or {} super(ShimCollectionsController, self).__init__( collection, resource, member_actions=member_actions, collection_actions=collection_actions, action_status=action_status) self.controller = controller self.controller_index = getattr(controller, 'index', None) self.controller_create = getattr(controller, 'create', None) self.controller_update = getattr(controller, 'update', None) self.collection_methods = {} for action, method in collection_methods.items(): controller_method = getattr(controller, action, None) self.collection_methods[method] = ( controller_method, self.action_status.get(action, 200)) @expose(generic=True) def index(self): if (not self.controller_index and request.method not in self.collection_methods): pecan.abort(405) controller_method_status = self.collection_methods.get(request.method) status = None if controller_method_status: controller_method = controller_method_status[0] status = controller_method_status[1] else: controller_method = self.controller_index shim_request = ShimRequest(request.context['neutron_context']) uri_identifiers = request.context['uri_identifiers'] args = [shim_request] if request.method == 'PUT': args.append(request.context.get('request_data')) result = controller_method(*args, **uri_identifiers) if not status: self._set_response_code(result, 'index') else: pecan.response.status = status return result @when(index, method='POST') def create(self): if not self.controller_create: pecan.abort(405) shim_request = ShimRequest(request.context['neutron_context']) uri_identifiers = request.context['uri_identifiers'] result = self.controller_create(shim_request, request.context.get('request_data'), **uri_identifiers) self._set_response_code(result, 'create') return result @expose() def _lookup(self, item, *remainder): request.context['resource'] = self.resource request.context['resource_id'] = item return ( ShimItemController(self.collection, self.resource, item, self.controller, member_actions=self._member_actions, collection_actions=self._collection_actions, action_status=self.action_status), remainder ) class ShimMemberActionController(NeutronPecanController): def __init__(self, collection, resource, item, controller, member_actions): super(ShimMemberActionController, self).__init__( collection, resource, member_actions=member_actions, item=item) self.controller = controller self.inverted_member_actions = invert_dict(self._member_actions) @expose(generic=True) def index(self): if self.resource not in self.inverted_member_actions['GET']: pecan.abort(404) shim_request = ShimRequest(request.context['neutron_context']) uri_identifiers = request.context['uri_identifiers'] method = getattr(self.controller, self.resource) return method(shim_request, self.item, **uri_identifiers) class PecanResourceExtension(object): def __init__(self, collection, controller, plugin): self.collection = collection self.controller = controller self.plugin = plugin neutron-12.1.1/neutron/pecan_wsgi/controllers/quota.py0000664000175000017500000001324313553660047023206 0ustar zuulzuul00000000000000# Copyright (c) 2015 Taturiello Consulting, Meh. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import converters from neutron_lib.db import constants as db_const from neutron_lib import exceptions as n_exc from oslo_config import cfg from oslo_utils import importutils import pecan from pecan import request from neutron._i18n import _ from neutron.api.v2 import attributes from neutron.pecan_wsgi.controllers import utils from neutron.quota import resource_registry RESOURCE_NAME = "quota" TENANT_ID_ATTR = {'tenant_id': {'allow_post': False, 'allow_put': False, 'required_by_policy': True, 'validate': {'type:string': db_const.PROJECT_ID_FIELD_SIZE}, 'is_visible': True}} class QuotasController(utils.NeutronPecanController): def __init__(self): self._driver = importutils.import_class( cfg.CONF.QUOTAS.quota_driver ) super(QuotasController, self).__init__( "%ss" % RESOURCE_NAME, RESOURCE_NAME) def _check_admin(self, context, reason=_("Only admin can view or configure quota")): if not context.is_admin: raise n_exc.AdminRequired(reason=reason) @utils.expose() def _lookup(self, tenant_id, *remainder): return QuotaController(self._driver, tenant_id), remainder @utils.expose(generic=True) def index(self): neutron_context = request.context.get('neutron_context') # FIXME(salv-orlando): There shouldn't be any need to do this explicit # check. However some behaviours from the "old" extension have # been temporarily carried over here self._check_admin(neutron_context) # TODO(salv-orlando): proper plurals management return {self.collection: self._driver.get_all_quotas( neutron_context, resource_registry.get_all_resources())} @utils.when(index, method='POST') @utils.when(index, method='PUT') @utils.when(index, method='DELETE') def not_supported(self): pecan.abort(405) class QuotaController(utils.NeutronPecanController): def __init__(self, _driver, tenant_id): self._driver = _driver self._tenant_id = tenant_id super(QuotaController, self).__init__( "%ss" % RESOURCE_NAME, RESOURCE_NAME) # Ensure limits for all registered resources are returned attr_dict = attributes.RESOURCE_ATTRIBUTE_MAP[self.collection] for quota_resource in resource_registry.get_all_resources().keys(): attr_dict[quota_resource] = { 'allow_post': False, 'allow_put': True, 'convert_to': converters.convert_to_int, 'validate': { 'type:range': [-1, db_const.DB_INTEGER_MAX_VALUE]}, 'is_visible': True} # The quota resource must always declare a tenant_id attribute, # otherwise the attribute will be stripped off when generating the # response attr_dict.update(TENANT_ID_ATTR) @utils.expose(generic=True) def index(self): return get_tenant_quotas(self._tenant_id, self._driver) @utils.when(index, method='PUT') def put(self, *args, **kwargs): neutron_context = request.context.get('neutron_context') # For put requests there's always going to be a single element quota_data = request.context['resources'][0] for key, value in quota_data.items(): self._driver.update_quota_limit( neutron_context, self._tenant_id, key, value) return get_tenant_quotas(self._tenant_id, self._driver) @utils.when_delete(index) def delete(self): neutron_context = request.context.get('neutron_context') self._driver.delete_tenant_quota(neutron_context, self._tenant_id) @utils.when(index, method='POST') def not_supported(self): pecan.abort(405) def get_tenant_quotas(tenant_id, driver=None): if not driver: driver = importutils.import_class(cfg.CONF.QUOTAS.quota_driver) neutron_context = request.context.get('neutron_context') if tenant_id == 'tenant': # NOTE(salv-orlando): Read the following before the code in order # to avoid puking. # There is a weird undocumented behaviour of the Neutron quota API # as 'tenant' is used as an API action to return the identifier # of the tenant in the request context. This is used exclusively # for interaction with python-neutronclient and is a possibly # unnecessary 'whoami' API endpoint. Pending resolution of this # API issue, this controller will just treat the magic string # 'tenant' (and only that string) and return the response expected # by python-neutronclient return {'tenant': {'tenant_id': neutron_context.tenant_id}} tenant_quotas = driver.get_tenant_quotas( neutron_context, resource_registry.get_all_resources(), tenant_id) tenant_quotas['tenant_id'] = tenant_id return {RESOURCE_NAME: tenant_quotas} neutron-12.1.1/neutron/pecan_wsgi/controllers/extensions.py0000664000175000017500000000452713553660046024260 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pecan from neutron._i18n import _ from neutron.api import extensions from neutron.pecan_wsgi.controllers import utils class ExtensionsController(object): @utils.expose() def _lookup(self, alias, *remainder): return ExtensionController(alias), remainder @utils.expose(generic=True) def index(self): ext_mgr = extensions.PluginAwareExtensionManager.get_instance() exts = [extensions.ExtensionController._translate(ext) for ext in ext_mgr.extensions.values()] return {'extensions': exts} @utils.when(index, method='POST') @utils.when(index, method='PUT') @utils.when(index, method='DELETE') @utils.when(index, method='HEAD') @utils.when(index, method='PATCH') def not_supported(self): # NOTE(blogan): Normally we'd return 405 but the legacy extensions # controller returned 404. pecan.abort(404) class ExtensionController(object): def __init__(self, alias): self.alias = alias @utils.expose(generic=True) def index(self): ext_mgr = extensions.PluginAwareExtensionManager.get_instance() ext = ext_mgr.extensions.get(self.alias, None) if not ext: pecan.abort( 404, detail=_("Extension with alias %s " "does not exist") % self.alias) return {'extension': extensions.ExtensionController._translate(ext)} @utils.when(index, method='POST') @utils.when(index, method='PUT') @utils.when(index, method='DELETE') @utils.when(index, method='HEAD') @utils.when(index, method='PATCH') def not_supported(self): # NOTE(blogan): Normally we'd return 405 but the legacy extensions # controller returned 404. pecan.abort(404) neutron-12.1.1/neutron/pecan_wsgi/controllers/__init__.py0000664000175000017500000000140513553660046023610 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.pecan_wsgi.controllers import quota from neutron.pecan_wsgi.controllers import resource CollectionsController = resource.CollectionsController QuotasController = quota.QuotasController neutron-12.1.1/neutron/pecan_wsgi/controllers/root.py0000664000175000017500000001175413553660047023045 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # Copyright (c) 2015 Rackspace, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from oslo_log import log import pecan from pecan import request import six.moves.urllib.parse as urlparse from neutron.api.v2 import attributes from neutron.api.views import versions as versions_view from neutron import manager from neutron.pecan_wsgi.controllers import extensions as ext_ctrl from neutron.pecan_wsgi.controllers import utils CONF = cfg.CONF LOG = log.getLogger(__name__) _VERSION_INFO = {} def _load_version_info(version_info): assert version_info['id'] not in _VERSION_INFO _VERSION_INFO[version_info['id']] = version_info def _get_version_info(): return _VERSION_INFO.values() class RootController(object): @utils.expose(generic=True) def index(self): version_objs = [ { "id": "v2.0", "status": "CURRENT", }, ] builder = versions_view.get_view_builder(pecan.request) versions = [builder.build(version) for version in version_objs] return dict(versions=versions) @utils.when(index, method='HEAD') @utils.when(index, method='POST') @utils.when(index, method='PATCH') @utils.when(index, method='PUT') @utils.when(index, method='DELETE') def not_supported(self): pecan.abort(405) class V2Controller(object): # Same data structure as neutron.api.versions.Versions for API backward # compatibility version_info = { 'id': 'v2.0', 'status': 'CURRENT' } _load_version_info(version_info) # NOTE(blogan): Paste deploy handled the routing to the legacy extension # controller. If the extensions filter is removed from the api-paste.ini # then this controller will be routed to This means operators had # the ability to turn off the extensions controller via tha api-paste but # will not be able to turn it off with the pecan switch. extensions = ext_ctrl.ExtensionsController() @utils.expose(generic=True) def index(self): if not pecan.request.path_url.endswith('/'): pecan.abort(404) layout = [] for name, collection in attributes.CORE_RESOURCES.items(): href = urlparse.urljoin(pecan.request.path_url, collection) resource = {'name': name, 'collection': collection, 'links': [{'rel': 'self', 'href': href}]} layout.append(resource) return {'resources': layout} @utils.when(index, method='HEAD') @utils.when(index, method='POST') @utils.when(index, method='PATCH') @utils.when(index, method='PUT') @utils.when(index, method='DELETE') def not_supported(self): pecan.abort(405) @utils.expose() def _lookup(self, collection, *remainder): # if collection exists in the extension to service plugins map then # we are assuming that collection is the service plugin and # needs to be remapped. # Example: https://neutron.endpoint/v2.0/lbaas/loadbalancers if (remainder and manager.NeutronManager.get_resources_for_path_prefix( collection)): collection = remainder[0] remainder = remainder[1:] controller = manager.NeutronManager.get_controller_for_resource( collection) if not controller: LOG.warning("No controller found for: %s - returning response " "code 404", collection) pecan.abort(404) # Store resource and collection names in pecan request context so that # hooks can leverage them if necessary. The following code uses # attributes from the controller instance to ensure names have been # properly sanitized (eg: replacing dashes with underscores) request.context['resource'] = controller.resource request.context['collection'] = controller.collection # NOTE(blogan): initialize a dict to store the ids of the items walked # in the path for example: /networks/1234 would cause uri_identifiers # to contain: {'network_id': '1234'} # This is for backwards compatibility with legacy extensions that # defined their own controllers and expected kwargs to be passed in # with the uri_identifiers request.context['uri_identifiers'] = {} return controller, remainder neutron-12.1.1/neutron/pecan_wsgi/controllers/resource.py0000664000175000017500000002306413553660047023706 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging import pecan from pecan import request import webob from neutron._i18n import _ from neutron import manager from neutron.pecan_wsgi.controllers import utils LOG = logging.getLogger(__name__) class ItemController(utils.NeutronPecanController): def __init__(self, resource, item, plugin=None, resource_info=None, parent_resource=None, member_actions=None): super(ItemController, self).__init__(None, resource, plugin=plugin, resource_info=resource_info, parent_resource=parent_resource, member_actions=member_actions) self.item = item @utils.expose(generic=True) def index(self, *args, **kwargs): return self.get(*args, **kwargs) def get(self, *args, **kwargs): neutron_context = request.context['neutron_context'] getter_args = [neutron_context, self.item] # NOTE(tonytan4ever): This implicitly forces the getter method # uses the parent_id as the last argument, thus easy for future # refactoring if 'parent_id' in request.context: getter_args.append(request.context['parent_id']) fields = request.context['query_params'].get('fields') return {self.resource: self.plugin_shower(*getter_args, fields=fields)} @utils.when(index, method='HEAD') @utils.when(index, method='POST') @utils.when(index, method='PATCH') def not_supported(self): pecan.abort(405) @utils.when(index, method='PUT') def put(self, *args, **kwargs): neutron_context = request.context['neutron_context'] resources = request.context['resources'] # Bulk update is not supported, 'resources' always contains a single # elemenet data = {self.resource: resources[0]} updater_args = [neutron_context, self.item] if 'parent_id' in request.context: updater_args.append(request.context['parent_id']) updater_args.append(data) return {self.resource: self.plugin_updater(*updater_args)} @utils.when_delete(index) def delete(self): if request.body: msg = _("Request body is not supported in DELETE.") raise webob.exc.HTTPBadRequest(msg) neutron_context = request.context['neutron_context'] deleter_args = [neutron_context, self.item] if 'parent_id' in request.context: deleter_args.append(request.context['parent_id']) return self.plugin_deleter(*deleter_args) @utils.expose() def _lookup(self, collection, *remainder): request.context['collection'] = collection collection_path = '/'.join([self.resource, collection]) controller = manager.NeutronManager.get_controller_for_resource( collection_path) if not controller: if collection not in self._member_actions: LOG.warning("No controller found for: %s - returning" "response code 404", collection) pecan.abort(404) # collection is a member action, so we create a new controller # for it. method = self._member_actions[collection] kwargs = {'plugin': self.plugin, 'resource_info': self.resource_info} if method == 'PUT': kwargs['update_action'] = collection elif method == 'GET': kwargs['show_action'] = collection controller = MemberActionController( self.resource, self.item, self, **kwargs) else: request.context['parent_id'] = request.context['resource_id'] request.context['resource'] = controller.resource return controller, remainder class CollectionsController(utils.NeutronPecanController): item_controller_class = ItemController @utils.expose() def _lookup(self, item, *remainder): # Store resource identifier in request context request.context['resource_id'] = item uri_identifier = '%s_id' % self.resource request.context['uri_identifiers'][uri_identifier] = item return (self.item_controller_class( self.resource, item, resource_info=self.resource_info, # NOTE(tonytan4ever): item needs to share the same # parent as collection parent_resource=self.parent, member_actions=self._member_actions, plugin=self.plugin), remainder) @utils.expose(generic=True) def index(self, *args, **kwargs): return self.get(*args, **kwargs) def get(self, *args, **kwargs): # NOTE(blogan): these are set in the FieldsAndFiltersHoook query_params = request.context['query_params'] neutron_context = request.context['neutron_context'] lister_args = [neutron_context] if 'parent_id' in request.context: lister_args.append(request.context['parent_id']) return {self.collection: self.plugin_lister(*lister_args, **query_params)} @utils.when(index, method='HEAD') @utils.when(index, method='PATCH') @utils.when(index, method='PUT') @utils.when(index, method='DELETE') def not_supported(self): pecan.abort(405) @utils.when(index, method='POST') def post(self, *args, **kwargs): if 'resources' not in request.context: # user didn't specify any body, which is invalid for collections msg = (_("Unable to find '%s' in request body") % request.context['resource']) raise webob.exc.HTTPBadRequest(msg) resources = request.context['resources'] pecan.response.status = 201 return self.create(resources) def create(self, resources): if request.context['is_bulk']: # Bulk! creator = self.plugin_bulk_creator key = self.collection data = {key: [{self.resource: res} for res in resources]} creator_kwargs = {self.collection: data} else: creator = self.plugin_creator key = self.resource data = {key: resources[0]} creator_kwargs = {self.resource: data} neutron_context = request.context['neutron_context'] creator_args = [neutron_context] if 'parent_id' in request.context and self._parent_id_name: creator_kwargs[self._parent_id_name] = request.context['parent_id'] return {key: creator(*creator_args, **creator_kwargs)} class MemberActionController(ItemController): @property def plugin_shower(self): # NOTE(blogan): Do an explicit check for the _show_action because # pecan will see the plugin_shower property as a possible custom route # and try to evaluate it, which causes the code block to be executed. # If _show_action is None, getattr throws an exception and fails a # request. if self._show_action: return getattr(self.plugin, self._show_action) @property def plugin_updater(self): if self._update_action: return getattr(self.plugin, self._update_action) def __init__(self, resource, item, parent_controller, plugin=None, resource_info=None, show_action=None, update_action=None): super(MemberActionController, self).__init__( resource, item, plugin=plugin, resource_info=resource_info) self._show_action = show_action self._update_action = update_action self.parent_controller = parent_controller @utils.expose(generic=True) def index(self, *args, **kwargs): if not self._show_action: pecan.abort(405) neutron_context = request.context['neutron_context'] # NOTE(blogan): The legacy wsgi code did not pass fields to the plugin # on GET member actions. To maintain compatibility, we'll do the same. return self.plugin_shower(neutron_context, self.item) @utils.when(index, method='PUT') def put(self, *args, **kwargs): if not self._update_action: LOG.debug("Action %(action)s is not defined on resource " "%(resource)s", {'action': self._update_action, 'resource': self.resource}) pecan.abort(405) neutron_context = request.context['neutron_context'] LOG.debug("Processing member action %(action)s for resource " "%(resource)s identified by %(item)s", {'action': self._update_action, 'resource': self.resource, 'item': self.item}) return self.plugin_updater(neutron_context, self.item, request.context['request_data']) @utils.when(index, method='HEAD') @utils.when(index, method='POST') @utils.when(index, method='PATCH') @utils.when(index, method='DELETE') def not_supported(self): return super(MemberActionController, self).not_supported() neutron-12.1.1/neutron/pecan_wsgi/startup.py0000664000175000017500000001416613553660047021216 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins import directory from neutron.api import extensions from neutron.api.v2 import attributes from neutron.api.v2 import base from neutron import manager from neutron.pecan_wsgi.controllers import resource as res_ctrl from neutron.pecan_wsgi.controllers import utils from neutron import policy from neutron.quota import resource_registry # NOTE(blogan): This currently already exists in neutron.api.v2.router but # instead of importing that module and creating circular imports elsewhere, # it's easier to just copy it here. The likelihood of it needing to be changed # are slim to none. RESOURCES = {'network': 'networks', 'subnet': 'subnets', 'subnetpool': 'subnetpools', 'port': 'ports'} def initialize_all(): manager.init() ext_mgr = extensions.PluginAwareExtensionManager.get_instance() ext_mgr.extend_resources("2.0", attributes.RESOURCE_ATTRIBUTE_MAP) # At this stage we have a fully populated resource attribute map; # build Pecan controllers and routes for all core resources plugin = directory.get_plugin() for resource, collection in RESOURCES.items(): resource_registry.register_resource_by_name(resource) new_controller = res_ctrl.CollectionsController(collection, resource, plugin=plugin) manager.NeutronManager.set_controller_for_resource( collection, new_controller) manager.NeutronManager.set_plugin_for_resource(collection, plugin) pecanized_resources = ext_mgr.get_pecan_resources() for pec_res in pecanized_resources: manager.NeutronManager.set_controller_for_resource( pec_res.collection, pec_res.controller) manager.NeutronManager.set_plugin_for_resource( pec_res.collection, pec_res.plugin) # Now build Pecan Controllers and routes for all extensions resources = ext_mgr.get_resources() # Extensions controller is already defined, we don't need it. resources.pop(0) for ext_res in resources: path_prefix = ext_res.path_prefix.strip('/') collection = ext_res.collection # Retrieving the parent resource. It is expected the format of # the parent resource to be: # {'collection_name': 'name-of-collection', # 'member_name': 'name-of-resource'} # collection_name does not appear to be used in the legacy code # inside the controller logic, so we can assume we do not need it. parent = ext_res.parent or {} parent_resource = parent.get('member_name') collection_key = collection if parent_resource: collection_key = '/'.join([parent_resource, collection]) collection_actions = ext_res.collection_actions member_actions = ext_res.member_actions if manager.NeutronManager.get_controller_for_resource(collection_key): # This is a collection that already has a pecan controller, we # do not need to do anything else continue legacy_controller = getattr(ext_res.controller, 'controller', ext_res.controller) new_controller = None if isinstance(legacy_controller, base.Controller): resource = legacy_controller.resource plugin = legacy_controller.plugin attr_info = legacy_controller.attr_info member_actions = legacy_controller.member_actions pagination = legacy_controller.allow_pagination sorting = legacy_controller.allow_sorting # NOTE(blogan): legacy_controller and ext_res both can both have # member_actions. the member_actions for ext_res are strictly for # routing, while member_actions for legacy_controller are used for # handling the request once the routing has found the controller. # They're always the same so we will just use the ext_res # member_action. new_controller = res_ctrl.CollectionsController( collection, resource, resource_info=attr_info, parent_resource=parent_resource, member_actions=member_actions, plugin=plugin, allow_pagination=pagination, allow_sorting=sorting, collection_actions=collection_actions) # new_controller.collection has replaced hyphens with underscores manager.NeutronManager.set_plugin_for_resource( new_controller.collection, plugin) if path_prefix: manager.NeutronManager.add_resource_for_path_prefix( collection, path_prefix) else: new_controller = utils.ShimCollectionsController( collection, None, legacy_controller, collection_actions=collection_actions, member_actions=member_actions, action_status=ext_res.controller.action_status, collection_methods=ext_res.collection_methods) manager.NeutronManager.set_controller_for_resource( collection_key, new_controller) # Certain policy checks require that the extensions are loaded # and the RESOURCE_ATTRIBUTE_MAP populated before they can be # properly initialized. This can only be claimed with certainty # once this point in the code has been reached. In the event # that the policies have been initialized before this point, # calling reset will cause the next policy check to # re-initialize with all of the required data in place. policy.reset() neutron-12.1.1/neutron/api/0000775000175000017500000000000013553660156015565 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/views/0000775000175000017500000000000013553660156016722 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/views/__init__.py0000664000175000017500000000000013553660046021017 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/views/versions.py0000664000175000017500000000341013553660046021140 0ustar zuulzuul00000000000000# Copyright 2010-2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from neutron.api import api_common def get_view_builder(req): base_url = req.application_url return ViewBuilder(base_url) class ViewBuilder(object): def __init__(self, base_url): """Object initialization. :param base_url: url of the root wsgi application """ self.base_url = api_common.prepare_url(base_url) def build(self, version_data): """Generic method used to generate a version entity.""" version = { "id": version_data["id"], "status": version_data["status"], "links": self._build_links(version_data), } return version def _build_links(self, version_data): """Generate a container of links that refer to the provided version.""" href = self.generate_href(version_data["id"]) links = [ { "rel": "self", "href": href, }, ] return links def generate_href(self, version_number): """Create an url that refers to a specific version_number.""" return os.path.join(self.base_url, version_number, '') neutron-12.1.1/neutron/api/rpc/0000775000175000017500000000000013553660156016351 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/rpc/callbacks/0000775000175000017500000000000013553660156020270 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/rpc/callbacks/events.py0000664000175000017500000000125413553660046022146 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. CREATED = 'created' UPDATED = 'updated' DELETED = 'deleted' VALID = ( CREATED, UPDATED, DELETED ) neutron-12.1.1/neutron/api/rpc/callbacks/exceptions.py0000664000175000017500000000237713553660046023032 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions from neutron._i18n import _ class CallbackWrongResourceType(exceptions.NeutronException): message = _('Callback for %(resource_type)s returned wrong resource type') class CallbackNotFound(exceptions.NeutronException): message = _('Callback for %(resource_type)s not found') class CallbacksMaxLimitReached(exceptions.NeutronException): message = _("Cannot add multiple callbacks for %(resource_type)s") class NoAgentDbMixinImplemented(exceptions.NeutronException): message = _("RPC callbacks mechanism needs the implementation of " "AgentDbMixin in the plugin, as so far it's only designed " "to work with agents") neutron-12.1.1/neutron/api/rpc/callbacks/producer/0000775000175000017500000000000013553660156022113 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/rpc/callbacks/producer/registry.py0000664000175000017500000000367413553660047024346 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.api.rpc.callbacks import exceptions from neutron.api.rpc.callbacks import resource_manager from neutron.objects import base # TODO(ajo): consider adding locking: it's safe for eventlet but not # for other types of threading. def _get_manager(): return resource_manager.ProducerResourceCallbacksManager() def provide(callback, resource_type): """Register a callback as a producer for the resource type. This callback will be used to produce resources of corresponding type for interested parties. """ _get_manager().register(callback, resource_type) def unprovide(callback, resource_type): """Unregister a callback for corresponding resource type.""" _get_manager().unregister(callback, resource_type) def clear(): """Clear all callbacks.""" _get_manager().clear() def pull(resource_type, resource_id, **kwargs): """Get resource object that corresponds to resource id. The function will return an object that is provided by resource producer. :returns: NeutronObject """ callback = _get_manager().get_callback(resource_type) obj = callback(resource_type, resource_id, **kwargs) if obj: if (not isinstance(obj, base.NeutronObject) or resource_type != obj.obj_name()): raise exceptions.CallbackWrongResourceType( resource_type=resource_type) return obj neutron-12.1.1/neutron/api/rpc/callbacks/producer/__init__.py0000664000175000017500000000000013553660046024210 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/rpc/callbacks/consumer/0000775000175000017500000000000013553660156022123 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/rpc/callbacks/consumer/registry.py0000664000175000017500000000430313553660047024344 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import debtcollector from neutron.api.rpc.callbacks import resource_manager #TODO(ajo): consider adding locking to _get_manager, it's # safe for eventlet, but not for normal threading. def _get_manager(): return resource_manager.ConsumerResourceCallbacksManager() @debtcollector.removals.remove( message="This will be removed in the future. Please register callbacks " "using the 'register' method in this model and adjust the " "callback to accept the context and resource type as arguments.", version="Ocata" ) def subscribe(callback, resource_type): # temporary hack to differentiate between callback types until the # 'subscribe' method is removed callback.__dict__['_ACCEPTS_CONTEXT'] = False _get_manager().register(callback, resource_type) def register(callback, resource_type): # TODO(kevinbenton): remove this on debt collection callback.__dict__['_ACCEPTS_CONTEXT'] = True _get_manager().register(callback, resource_type) def unsubscribe(callback, resource_type): _get_manager().unregister(callback, resource_type) def push(context, resource_type, resource_list, event_type): """Push resource list into all registered callbacks for the event type.""" callbacks = _get_manager().get_callbacks(resource_type) for callback in callbacks: if callback._ACCEPTS_CONTEXT: callback(context, resource_type, resource_list, event_type) else: # backwards compat for callback listeners that don't take # context and resource_type callback(resource_list, event_type) def clear(): _get_manager().clear() neutron-12.1.1/neutron/api/rpc/callbacks/consumer/__init__.py0000664000175000017500000000000013553660046024220 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/rpc/callbacks/resource_manager.py0000664000175000017500000001115213553660046024161 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import collections from neutron_lib.callbacks import exceptions from oslo_log import log as logging import six from neutron.api.rpc.callbacks import exceptions as rpc_exc from neutron.api.rpc.callbacks import resources LOG = logging.getLogger(__name__) # TODO(QoS): split the registry/resources_rpc modules into two separate things: # one for pull and one for push APIs def _validate_resource_type(resource_type): if not resources.is_valid_resource_type(resource_type): raise exceptions.Invalid(element='resource', value=resource_type) @six.add_metaclass(abc.ABCMeta) class ResourceCallbacksManager(object): """A callback system that allows information providers in a loose manner. """ # This hook is to allow tests to get new objects for the class _singleton = True def __new__(cls, *args, **kwargs): if not cls._singleton: return super(ResourceCallbacksManager, cls).__new__(cls) if not hasattr(cls, '_instance'): cls._instance = super(ResourceCallbacksManager, cls).__new__(cls) return cls._instance @abc.abstractmethod def _add_callback(self, callback, resource_type): pass @abc.abstractmethod def _delete_callback(self, callback, resource_type): pass def register(self, callback, resource_type): """Register a callback for a resource type. :param callback: the callback. It must raise or return NeutronObject. :param resource_type: must be a valid resource type. """ LOG.debug("Registering callback for %s", resource_type) _validate_resource_type(resource_type) self._add_callback(callback, resource_type) def unregister(self, callback, resource_type): """Unregister callback from the registry. :param callback: the callback. :param resource_type: must be a valid resource type. """ LOG.debug("Unregistering callback for %s", resource_type) _validate_resource_type(resource_type) self._delete_callback(callback, resource_type) @abc.abstractmethod def clear(self): """Brings the manager to a clean state.""" def get_subscribed_types(self): return list(self._callbacks.keys()) class ProducerResourceCallbacksManager(ResourceCallbacksManager): _callbacks = dict() def _add_callback(self, callback, resource_type): if resource_type in self._callbacks: raise rpc_exc.CallbacksMaxLimitReached(resource_type=resource_type) self._callbacks[resource_type] = callback def _delete_callback(self, callback, resource_type): try: del self._callbacks[resource_type] except KeyError: raise rpc_exc.CallbackNotFound(resource_type=resource_type) def clear(self): self._callbacks = dict() def get_callback(self, resource_type): _validate_resource_type(resource_type) try: return self._callbacks[resource_type] except KeyError: raise rpc_exc.CallbackNotFound(resource_type=resource_type) class ConsumerResourceCallbacksManager(ResourceCallbacksManager): _callbacks = collections.defaultdict(set) def _add_callback(self, callback, resource_type): self._callbacks[resource_type].add(callback) def _delete_callback(self, callback, resource_type): try: self._callbacks[resource_type].remove(callback) if not self._callbacks[resource_type]: del self._callbacks[resource_type] except KeyError: raise rpc_exc.CallbackNotFound(resource_type=resource_type) def clear(self): self._callbacks = collections.defaultdict(set) def get_callbacks(self, resource_type): """Return the callback if found, None otherwise. :param resource_type: must be a valid resource type. """ _validate_resource_type(resource_type) callbacks = self._callbacks[resource_type] if not callbacks: raise rpc_exc.CallbackNotFound(resource_type=resource_type) return callbacks neutron-12.1.1/neutron/api/rpc/callbacks/version_manager.py0000664000175000017500000002511413553660047024023 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import copy import pprint import time from neutron_lib.plugins import directory from oslo_log import log as logging from oslo_utils import importutils from neutron.api.rpc.callbacks import exceptions LOG = logging.getLogger(__name__) VERSIONS_TTL = 60 # NOTE(mangelajo): if we import this globally we end up with a (very # long) circular dependency, this can be fixed if we # stop importing all exposed classes in # neutron.api.rpc.callbacks.resources and provide # a decorator to expose classes def _import_resources(): return importutils.import_module('neutron.api.rpc.callbacks.resources') def _import_agents_db(): return importutils.import_module('neutron.db.agents_db') AgentConsumer = collections.namedtuple('AgentConsumer', ['agent_type', 'host']) AgentConsumer.__repr__ = lambda self: '%s@%s' % self class ResourceConsumerTracker(object): """Class passed down to collect consumer's resource versions. This class is responsible for fetching the local versions of resources, and letting the called function register every consumer's resource version. This class is passed down to the plugin get_agents_resource_versions currently, as the only expected consumers are agents so far. Later on, this class can also be used to recalculate, for each resource type, the collection of versions that are local or known by one or more consumers. """ def __init__(self): # Initialize with the local (server) versions, as we always want # to send those. Agents, as they upgrade, will need the latest version, # and there is a corner case we'd not be covering otherwise: # 1) one or several neutron-servers get disconnected from rpc (while # running) # 2) a new agent comes up, with the latest version and it reports # 2 ways: # a) via status report (which will be stored in the database) # b) via fanout call to all neutron servers, this way, all of them # get their version set updated right away without the need to # re-fetch anything from the database. # 3) the neutron-servers get back online to the rpc bus, but they # lost the fanout message. # # TODO(mangelajo) To cover this case we may need a callback from oslo # messaging to get notified about disconnections/reconnections to the # rpc bus, invalidating the consumer version cache when we receive such # callback. self._versions = self._get_local_resource_versions() self._versions_by_consumer = collections.defaultdict(dict) self._needs_recalculation = False self.last_report = None def _get_local_resource_versions(self): resources = _import_resources() local_resource_versions = collections.defaultdict(set) for resource_type, version in ( resources.LOCAL_RESOURCE_VERSIONS.items()): local_resource_versions[resource_type].add(version) return local_resource_versions # TODO(mangelajo): add locking with _recalculate_versions if we ever # move out of green threads. def _set_version(self, consumer, resource_type, version): """Set or update a consumer resource type version.""" self._versions[resource_type].add(version) consumer_versions = self._versions_by_consumer[consumer] prev_version = consumer_versions.get(resource_type, None) if version: consumer_versions[resource_type] = version else: consumer_versions.pop(resource_type, None) if prev_version != version: # If a version got updated/changed in a consumer, we need to # recalculate the main dictionary of versions based on the # new _versions_by_consumer. # We defer the recalculation until every consumer version has # been set for all of its resource types. self._needs_recalculation = True LOG.debug("Version for resource type %(resource_type)s changed " "%(prev_version)s to %(version)s on " "consumer %(consumer)s", {'resource_type': resource_type, 'version': version, 'prev_version': prev_version, 'consumer': consumer}) def set_versions(self, consumer, versions): """Set or update an specific consumer resource types. :param consumer: should be an AgentConsumer object, with agent_type and host set. This acts as the unique ID for the agent. :param versions: should be a dictionary in the following format: {'QosPolicy': '1.1', 'SecurityGroup': '1.0', 'Port': '1.0'} """ for resource_type, resource_version in versions.items(): self._set_version(consumer, resource_type, resource_version) if versions: self._cleanup_removed_versions(consumer, versions) else: self._handle_no_set_versions(consumer) def _cleanup_removed_versions(self, consumer, versions): """Check if any version report has been removed, and cleanup.""" prev_resource_types = set( self._versions_by_consumer[consumer].keys()) cur_resource_types = set(versions.keys()) removed_resource_types = prev_resource_types - cur_resource_types if removed_resource_types: LOG.debug("Removing stale tracked versions: %s", removed_resource_types) for resource_type in removed_resource_types: self._set_version(consumer, resource_type, None) def _handle_no_set_versions(self, consumer): """Handle consumers reporting no versions.""" if self._versions_by_consumer[consumer]: self._needs_recalculation = True LOG.debug("Clearing versions for consumer %s", consumer) self._versions_by_consumer[consumer] = {} def get_resource_versions(self, resource_type): """Fetch the versions necessary to notify all consumers.""" if self._needs_recalculation: self._recalculate_versions() self._needs_recalculation = False return copy.copy(self._versions[resource_type]) def report(self): """Output debug information about the consumer versions.""" format = lambda versions: pprint.pformat(dict(versions), indent=4) debug_dict = {'pushed_versions': format(self._versions), 'consumer_versions': format(self._versions_by_consumer)} if self.last_report != debug_dict: self.last_report = debug_dict LOG.debug('Tracked resource versions report:\n' 'pushed versions:\n%(pushed_versions)s\n\n' 'consumer versions:\n%(consumer_versions)s\n', debug_dict) # TODO(mangelajo): Add locking if we ever move out of greenthreads. def _recalculate_versions(self): """Recalculate the _versions set. Re-fetch the local (server) versions and expand with consumers' versions. """ versions = self._get_local_resource_versions() for versions_dict in self._versions_by_consumer.values(): for res_type, res_version in versions_dict.items(): versions[res_type].add(res_version) self._versions = versions class CachedResourceConsumerTracker(object): """This class takes care of the caching logic of versions.""" def __init__(self): # This is TTL expiration time, 0 means it will be expired at start self._expires_at = 0 self._versions = ResourceConsumerTracker() def _update_consumer_versions(self): new_tracker = ResourceConsumerTracker() neutron_plugin = directory.get_plugin() agents_db = _import_agents_db() # If you use RPC callbacks, your plugin needs to implement # AgentsDbMixin so that we know which resource versions your # agents consume via RPC, please note that rpc_callbacks are # only designed to work with agents currently. if isinstance(neutron_plugin, agents_db.AgentDbMixin): neutron_plugin.get_agents_resource_versions(new_tracker) else: raise exceptions.NoAgentDbMixinImplemented() # preserve last report state so we don't duplicate logs on refresh new_tracker.last_report = self._versions.last_report self._versions = new_tracker self._versions.report() def _check_expiration(self): if time.time() > self._expires_at: self._update_consumer_versions() self._expires_at = time.time() + VERSIONS_TTL def get_resource_versions(self, resource_type): self._check_expiration() return self._versions.get_resource_versions(resource_type) def update_versions(self, consumer, resource_versions): self._versions.set_versions(consumer, resource_versions) def report(self): self._check_expiration() self._versions.report() _cached_version_tracker = None #NOTE(ajo): add locking if we ever stop using greenthreads def _get_cached_tracker(): global _cached_version_tracker if not _cached_version_tracker: _cached_version_tracker = CachedResourceConsumerTracker() return _cached_version_tracker def get_resource_versions(resource_type): """Return the set of versions expected by the consumers of a resource.""" return _get_cached_tracker().get_resource_versions(resource_type) def update_versions(consumer, resource_versions): """Update the resources' versions for a consumer id.""" _get_cached_tracker().update_versions(consumer, resource_versions) def report(): """Report resource versions in debug logs.""" _get_cached_tracker().report() neutron-12.1.1/neutron/api/rpc/callbacks/__init__.py0000664000175000017500000000000013553660046022365 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/rpc/callbacks/resources.py0000664000175000017500000000467613553660047022670 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron._i18n import _ from neutron.objects.logapi import logging_resource as log_object from neutron.objects import network from neutron.objects import ports from neutron.objects.qos import policy from neutron.objects import securitygroup from neutron.objects import subnet from neutron.objects import trunk # Supported types LOGGING_RESOURCE = log_object.Log.obj_name() TRUNK = trunk.Trunk.obj_name() QOS_POLICY = policy.QosPolicy.obj_name() SUBPORT = trunk.SubPort.obj_name() PORT = ports.Port.obj_name() NETWORK = network.Network.obj_name() SUBNET = subnet.Subnet.obj_name() SECURITYGROUP = securitygroup.SecurityGroup.obj_name() SECURITYGROUPRULE = securitygroup.SecurityGroupRule.obj_name() _VALID_CLS = ( policy.QosPolicy, trunk.Trunk, trunk.SubPort, ports.Port, subnet.Subnet, network.Network, securitygroup.SecurityGroup, securitygroup.SecurityGroupRule, log_object.Log, ) _TYPE_TO_CLS_MAP = {cls.obj_name(): cls for cls in _VALID_CLS} LOCAL_RESOURCE_VERSIONS = { resource_type: cls.VERSION for resource_type, cls in _TYPE_TO_CLS_MAP.items() } def get_resource_type(resource_cls): if not resource_cls: return None if not hasattr(resource_cls, 'obj_name'): return None return resource_cls.obj_name() def register_resource_class(resource_cls): resource_type = get_resource_type(resource_cls) if not resource_type: msg = _("cannot find resource type for %s class") % resource_cls raise ValueError(msg) if resource_type not in _TYPE_TO_CLS_MAP: _TYPE_TO_CLS_MAP[resource_type] = resource_cls if resource_type not in LOCAL_RESOURCE_VERSIONS: LOCAL_RESOURCE_VERSIONS[resource_type] = resource_cls.VERSION def is_valid_resource_type(resource_type): return resource_type in _TYPE_TO_CLS_MAP def get_resource_cls(resource_type): return _TYPE_TO_CLS_MAP.get(resource_type) neutron-12.1.1/neutron/api/rpc/__init__.py0000664000175000017500000000000013553660046020446 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/rpc/agentnotifiers/0000775000175000017500000000000013553660156021372 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/rpc/agentnotifiers/utils.py0000664000175000017500000000436713553660046023114 0ustar zuulzuul00000000000000# Copyright (c) 2016 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_log import log as logging import oslo_messaging from oslo_utils import excutils LOG = logging.getLogger(__name__) def _call_with_retry(max_attempts): """A wrapper to retry a function using rpc call in case of MessagingException. Retries the decorated function in case of MessagingException of some kind (a timeout, client send error etc). If maximum attempts are exceeded, the exception which occurred during last attempt is reraised. """ def wrapper(f): def func_wrapper(*args, **kwargs): # (ivasilevskaya) think of a more informative data to log action = '%(func)s' % {'func': getattr(f, '__name__', f)} for attempt in range(1, max_attempts + 1): try: return f(*args, **kwargs) except oslo_messaging.MessagingException: with excutils.save_and_reraise_exception( reraise=False) as ctxt: LOG.warning( 'Failed to execute %(action)s. %(attempt)d out' ' of %(max_attempts)d', {'attempt': attempt, 'max_attempts': max_attempts, 'action': action}) if attempt == max_attempts: ctxt.reraise = True return func_wrapper return wrapper def retry(func, max_attempts): """Adds the retry logic to original function and returns a partial. The returned partial can be called with the same arguments as the original function. """ return _call_with_retry(max_attempts)(func) neutron-12.1.1/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py0000664000175000017500000003712313553660047025542 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import copy import random from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.common import utils # Priorities - lower value is higher priority PRIORITY_NETWORK_CREATE = 0 PRIORITY_NETWORK_UPDATE = 1 PRIORITY_NETWORK_DELETE = 2 PRIORITY_SUBNET_UPDATE = 3 PRIORITY_SUBNET_DELETE = 4 # In order to improve port dhcp provisioning when nova concurrently create # multiple vms, I classify the port_create_end message to two levels, the # high-level message only cast to one agent, the low-level message cast to all # other agent. In this way, When there are a large number of ports that need to # be processed, we can dispatch the high priority message of port to different # agent, so that the processed port will not block other port's processing in # other dhcp agents. PRIORITY_PORT_CREATE_HIGH = 5 PRIORITY_PORT_CREATE_LOW = 6 PRIORITY_PORT_UPDATE = 7 PRIORITY_PORT_DELETE = 8 METHOD_PRIORITY_MAP = { 'network_create_end': PRIORITY_NETWORK_CREATE, 'network_update_end': PRIORITY_NETWORK_UPDATE, 'network_delete_end': PRIORITY_NETWORK_DELETE, 'subnet_create_end': PRIORITY_SUBNET_UPDATE, 'subnet_update_end': PRIORITY_SUBNET_UPDATE, 'subnet_delete_end': PRIORITY_SUBNET_DELETE, 'port_create_end': PRIORITY_PORT_CREATE_LOW, 'port_update_end': PRIORITY_PORT_UPDATE, 'port_delete_end': PRIORITY_PORT_DELETE } LOG = logging.getLogger(__name__) class DhcpAgentNotifyAPI(object): """API for plugin to notify DHCP agent. This class implements the client side of an rpc interface. The server side is neutron.agent.dhcp.agent.DhcpAgent. For more information about changing rpc interfaces, please see doc/source/contributor/internals/rpc_api.rst. """ # It seems dhcp agent does not support bulk operation VALID_RESOURCES = ['network', 'subnet', 'port'] VALID_METHOD_NAMES = ['network.create.end', 'network.update.end', 'network.delete.end', 'subnet.create.end', 'subnet.update.end', 'subnet.delete.end', 'port.create.end', 'port.update.end', 'port.delete.end'] def __init__(self, topic=topics.DHCP_AGENT, plugin=None): self._unsubscribed_resources = [] self._plugin = plugin target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) # register callbacks for router interface changes registry.subscribe(self._after_router_interface_created, resources.ROUTER_INTERFACE, events.AFTER_CREATE) registry.subscribe(self._after_router_interface_deleted, resources.ROUTER_INTERFACE, events.AFTER_DELETE) # register callbacks for events pertaining resources affecting DHCP callback_resources = ( resources.NETWORK, resources.NETWORKS, resources.PORT, resources.PORTS, resources.SUBNET, resources.SUBNETS, ) if not cfg.CONF.dhcp_agent_notification: return for resource in callback_resources: registry.subscribe(self._send_dhcp_notification, resource, events.BEFORE_RESPONSE) self.uses_native_notifications = {} for resource in (resources.NETWORK, resources.PORT, resources.SUBNET): self.uses_native_notifications[resource] = {'create': False, 'update': False, 'delete': False} registry.subscribe(self._native_event_send_dhcp_notification, resource, events.AFTER_CREATE) registry.subscribe(self._native_event_send_dhcp_notification, resource, events.AFTER_UPDATE) registry.subscribe(self._native_event_send_dhcp_notification, resource, events.AFTER_DELETE) @property def plugin(self): if self._plugin is None: self._plugin = directory.get_plugin() return self._plugin def _schedule_network(self, context, network, existing_agents): """Schedule the network to new agents :return: all agents associated with the network """ new_agents = self.plugin.schedule_network(context, network) or [] if new_agents: for agent in new_agents: self._cast_message( context, 'network_create_end', {'network': {'id': network['id']}, 'priority': PRIORITY_NETWORK_CREATE}, agent['host']) elif not existing_agents: LOG.warning('Unable to schedule network %s: no agents ' 'available; will retry on subsequent port ' 'and subnet creation events.', network['id']) return new_agents + existing_agents def _get_enabled_agents(self, context, network, agents, method, payload): """Get the list of agents who can provide services.""" if not agents: return [] network_id = network['id'] enabled_agents = agents if not cfg.CONF.enable_services_on_agents_with_admin_state_down: enabled_agents = [x for x in agents if x.admin_state_up] active_agents = [x for x in agents if x.is_active] len_enabled_agents = len(enabled_agents) len_active_agents = len(active_agents) if len_active_agents < len_enabled_agents: LOG.warning("Only %(active)d of %(total)d DHCP agents " "associated with network '%(net_id)s' " "are marked as active, so notifications " "may be sent to inactive agents.", {'active': len_active_agents, 'total': len_enabled_agents, 'net_id': network_id}) if not enabled_agents: num_ports = self.plugin.get_ports_count( context, {'network_id': [network_id]}) notification_required = ( num_ports > 0 and len(network['subnets']) >= 1) if notification_required: LOG.error("Will not send event %(method)s for network " "%(net_id)s: no agent available. Payload: " "%(payload)s", {'method': method, 'net_id': network_id, 'payload': payload}) return enabled_agents def _is_reserved_dhcp_port(self, port): return port.get('device_id') == constants.DEVICE_ID_RESERVED_DHCP_PORT def _notify_agents(self, context, method, payload, network_id): """Notify all the agents that are hosting the network.""" payload['priority'] = METHOD_PRIORITY_MAP.get(method) # fanout is required as we do not know who is "listening" no_agents = not utils.is_extension_supported( self.plugin, constants.DHCP_AGENT_SCHEDULER_EXT_ALIAS) fanout_required = method == 'network_delete_end' or no_agents # we do nothing on network creation because we want to give the # admin the chance to associate an agent to the network manually cast_required = method != 'network_create_end' if fanout_required: self._fanout_message(context, method, payload) elif cast_required: admin_ctx = (context if context.is_admin else context.elevated()) network = self.plugin.get_network(admin_ctx, network_id) if 'subnet' in payload and payload['subnet'].get('segment_id'): # if segment_id exists then the segment service plugin # must be loaded segment_plugin = directory.get_plugin('segments') segment = segment_plugin.get_segment( context, payload['subnet']['segment_id']) network['candidate_hosts'] = segment['hosts'] agents = self.plugin.get_dhcp_agents_hosting_networks( context, [network_id], hosts=network.get('candidate_hosts')) # schedule the network first, if needed schedule_required = ( method == 'subnet_create_end' or method == 'port_create_end' and not self._is_reserved_dhcp_port(payload['port'])) if schedule_required: agents = self._schedule_network(admin_ctx, network, agents) if not agents: LOG.debug("Network %s is not hosted by any dhcp agent", network_id) return enabled_agents = self._get_enabled_agents( context, network, agents, method, payload) if method == 'port_create_end': high_agent = enabled_agents.pop( random.randint(0, len(enabled_agents) - 1)) self._notify_high_priority_agent( context, copy.deepcopy(payload), high_agent) for agent in enabled_agents: self._cast_message( context, method, payload, agent.host, agent.topic) def _notify_high_priority_agent(self, context, payload, agent): payload['priority'] = PRIORITY_PORT_CREATE_HIGH self._cast_message(context, "port_create_end", payload, agent.host, agent.topic) def _cast_message(self, context, method, payload, host, topic=topics.DHCP_AGENT): """Cast the payload to the dhcp agent running on the host.""" cctxt = self.client.prepare(topic=topic, server=host) cctxt.cast(context, method, payload=payload) def _fanout_message(self, context, method, payload): """Fanout the payload to all dhcp agents.""" cctxt = self.client.prepare(fanout=True) cctxt.cast(context, method, payload=payload) def network_removed_from_agent(self, context, network_id, host): self._cast_message(context, 'network_delete_end', {'network_id': network_id, 'priority': PRIORITY_NETWORK_DELETE}, host) def network_added_to_agent(self, context, network_id, host): self._cast_message(context, 'network_create_end', {'network': {'id': network_id}, 'priority': PRIORITY_NETWORK_CREATE}, host) def agent_updated(self, context, admin_state_up, host): self._cast_message(context, 'agent_updated', {'admin_state_up': admin_state_up}, host) def _after_router_interface_created(self, resource, event, trigger, **kwargs): self._notify_agents(kwargs['context'], 'port_create_end', {'port': kwargs['port']}, kwargs['port']['network_id']) def _after_router_interface_deleted(self, resource, event, trigger, **kwargs): self._notify_agents(kwargs['context'], 'port_delete_end', {'port_id': kwargs['port']['id']}, kwargs['port']['network_id']) def _native_event_send_dhcp_notification(self, resource, event, trigger, context, **kwargs): action = event.replace('after_', '') # we unsubscribe the _send_dhcp_notification method now that we know # the loaded core plugin emits native resource events if resource not in self._unsubscribed_resources: self.uses_native_notifications[resource][action] = True if all(self.uses_native_notifications[resource].values()): # only unsubscribe the API level listener if we are # receiving all event types for this resource self._unsubscribed_resources.append(resource) registry.unsubscribe_by_resource(self._send_dhcp_notification, resource) method_name = '.'.join((resource, action, 'end')) payload = kwargs[resource] data = {resource: payload} if resource == resources.PORT: if self._only_status_changed(kwargs.get('original_port'), kwargs.get('port')): # don't waste time updating the DHCP agent for status updates return self.notify(context, data, method_name) def _only_status_changed(self, orig, new): # a status change will manifest as a bumped revision number, a new # updated_at timestamp, and a new status. If that's all that changed, # return True, else False if not orig or not new: return False if set(orig.keys()) != set(new.keys()): return False for k in orig.keys(): if k in ('status', 'updated_at', 'revision_number'): continue if orig[k] != new[k]: return False return True def _send_dhcp_notification(self, resource, event, trigger, payload=None): action = payload.action.split('_')[0] if (resource in self.uses_native_notifications and self.uses_native_notifications[resource][action]): return data = payload.latest_state if payload.collection_name and payload.collection_name in data: for body in data[payload.collection_name]: item = {resource: body} self.notify(payload.context, item, payload.method_name) else: self.notify(payload.context, data, payload.method_name) def notify(self, context, data, method_name): # data is {'key' : 'value'} with only one key if method_name not in self.VALID_METHOD_NAMES: return obj_type = list(data.keys())[0] if obj_type not in self.VALID_RESOURCES: return obj_value = data[obj_type] network_id = None if obj_type == 'network' and 'id' in obj_value: network_id = obj_value['id'] elif obj_type in ['port', 'subnet'] and 'network_id' in obj_value: network_id = obj_value['network_id'] if not network_id: return method_name = method_name.replace(".", "_") if method_name.endswith("_delete_end"): if 'id' in obj_value: payload = {obj_type + '_id': obj_value['id']} if obj_type != 'network': payload['network_id'] = network_id self._notify_agents(context, method_name, payload, network_id) else: self._notify_agents(context, method_name, data, network_id) neutron-12.1.1/neutron/api/rpc/agentnotifiers/__init__.py0000664000175000017500000000000013553660046023467 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/rpc/agentnotifiers/l3_rpc_agent_api.py0000664000175000017500000001655513553660047025150 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import random from neutron_lib import constants from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_log import log as logging import oslo_messaging from neutron.api.rpc.agentnotifiers import utils as ag_utils from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.common import utils LOG = logging.getLogger(__name__) # default messaging timeout is 60 sec, so 2 here is chosen to not block API # call for more than 2 minutes AGENT_NOTIFY_MAX_ATTEMPTS = 2 class L3AgentNotifyAPI(object): """API for plugin to notify L3 agent.""" def __init__(self, topic=topics.L3_AGENT): target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def _notification_host(self, context, method, host, use_call=False, **kwargs): """Notify the agent that is hosting the router.""" LOG.debug('Notify agent at %(host)s the message ' '%(method)s', {'host': host, 'method': method}) cctxt = self.client.prepare(server=host) rpc_method = (ag_utils.retry(cctxt.call, AGENT_NOTIFY_MAX_ATTEMPTS) if use_call else cctxt.cast) rpc_method(context, method, **kwargs) def _agent_notification(self, context, method, router_ids, operation, shuffle_agents): """Notify changed routers to hosting l3 agents.""" adminContext = context if context.is_admin else context.elevated() plugin = directory.get_plugin(plugin_constants.L3) for router_id in router_ids: hosts = plugin.get_hosts_to_notify(adminContext, router_id) if shuffle_agents: random.shuffle(hosts) for host in hosts: LOG.debug('Notify agent at %(topic)s.%(host)s the message ' '%(method)s', {'topic': topics.L3_AGENT, 'host': host, 'method': method}) cctxt = self.client.prepare(topic=topics.L3_AGENT, server=host, version='1.1') cctxt.cast(context, method, routers=[router_id]) def _agent_notification_arp(self, context, method, router_id, operation, data): """Notify arp details to l3 agents hosting router.""" if not router_id: return dvr_arptable = {'router_id': router_id, 'arp_table': data} LOG.debug('Fanout dvr_arptable update: %s', dvr_arptable) cctxt = self.client.prepare(fanout=True, version='1.2') cctxt.cast(context, method, payload=dvr_arptable) def _notification(self, context, method, router_ids, operation, shuffle_agents, schedule_routers=True): """Notify all the agents that are hosting the routers.""" plugin = directory.get_plugin(plugin_constants.L3) if not plugin: LOG.error('No plugin for L3 routing registered. Cannot notify ' 'agents with the message %s', method) return if utils.is_extension_supported( plugin, constants.L3_AGENT_SCHEDULER_EXT_ALIAS): adminContext = (context.is_admin and context or context.elevated()) if schedule_routers: plugin.schedule_routers(adminContext, router_ids) self._agent_notification( context, method, router_ids, operation, shuffle_agents) else: cctxt = self.client.prepare(fanout=True) cctxt.cast(context, method, routers=router_ids) def _notification_fanout(self, context, method, router_id=None, **kwargs): """Fanout the information to all L3 agents. This function will fanout the router_id or ext_net_id to the L3 Agents. """ ext_net_id = kwargs.get('ext_net_id') if router_id: kwargs['router_id'] = router_id LOG.debug('Fanout notify agent at %(topic)s the message ' '%(method)s on router %(router_id)s', {'topic': topics.L3_AGENT, 'method': method, 'router_id': router_id}) if ext_net_id: LOG.debug('Fanout notify agent at %(topic)s the message ' '%(method)s for external_network %(ext_net_id)s', {'topic': topics.L3_AGENT, 'method': method, 'ext_net_id': ext_net_id}) cctxt = self.client.prepare(fanout=True) cctxt.cast(context, method, **kwargs) def agent_updated(self, context, admin_state_up, host): self._notification_host(context, 'agent_updated', host, payload={'admin_state_up': admin_state_up}) def router_deleted(self, context, router_id): self._notification_fanout(context, 'router_deleted', router_id) def routers_updated(self, context, router_ids, operation=None, data=None, shuffle_agents=False, schedule_routers=True): if router_ids: self._notification(context, 'routers_updated', router_ids, operation, shuffle_agents, schedule_routers) def add_arp_entry(self, context, router_id, arp_table, operation=None): self._agent_notification_arp(context, 'add_arp_entry', router_id, operation, arp_table) def del_arp_entry(self, context, router_id, arp_table, operation=None): self._agent_notification_arp(context, 'del_arp_entry', router_id, operation, arp_table) def delete_fipnamespace_for_ext_net(self, context, ext_net_id): self._notification_fanout( context, 'fipnamespace_delete_on_ext_net', ext_net_id=ext_net_id) def router_removed_from_agent(self, context, router_id, host): self._notification_host(context, 'router_removed_from_agent', host, payload={'router_id': router_id}) def router_added_to_agent(self, context, router_ids, host): # need to use call here as we want to be sure agent received # notification and router will not be "lost". However using call() # itself is not a guarantee, calling code should handle exceptions and # retry self._notification_host(context, 'router_added_to_agent', host, use_call=True, payload=router_ids) def routers_updated_on_host(self, context, router_ids, host): self._notification_host(context, 'routers_updated', host, routers=router_ids) neutron-12.1.1/neutron/api/rpc/agentnotifiers/metering_rpc_agent_api.py0000664000175000017500000001144613553660047026436 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_log import log as logging import oslo_messaging from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.common import utils from neutron.db import agentschedulers_db LOG = logging.getLogger(__name__) class MeteringAgentNotifyAPI(object): """API for plugin to notify L3 metering agent.""" def __init__(self, topic=topics.METERING_AGENT): self.topic = topic target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def _agent_notification(self, context, method, routers): """Notify l3 metering agents hosted by l3 agent hosts.""" adminContext = context if context.is_admin else context.elevated() plugin = directory.get_plugin(plugin_constants.L3) l3_routers = {} state = agentschedulers_db.get_admin_state_up_filter() for router in routers: l3_agents = plugin.get_l3_agents_hosting_routers( adminContext, [router['id']], admin_state_up=state, active=True) for l3_agent in l3_agents: LOG.debug('Notify metering agent at %(topic)s.%(host)s ' 'the message %(method)s', {'topic': self.topic, 'host': l3_agent.host, 'method': method}) l3_router = l3_routers.get(l3_agent.host, []) l3_router.append(router) l3_routers[l3_agent.host] = l3_router for host, routers in l3_routers.items(): cctxt = self.client.prepare(server=host) cctxt.cast(context, method, routers=routers) def _notification_fanout(self, context, method, router_id): LOG.debug('Fanout notify metering agent at %(topic)s the message ' '%(method)s on router %(router_id)s', {'topic': self.topic, 'method': method, 'router_id': router_id}) cctxt = self.client.prepare(fanout=True) cctxt.cast(context, method, router_id=router_id) def _notification_host(self, context, method, host, **kwargs): """Notify the agent that is hosting the router.""" LOG.debug('Notify agent at %(host)s the message ' '%(method)s', {'host': host, 'method': method}) cctxt = self.client.prepare(server=host) cctxt.cast(context, method, **kwargs) def _notification(self, context, method, routers): """Notify all the agents that are hosting the routers.""" plugin = directory.get_plugin(plugin_constants.L3) if utils.is_extension_supported( plugin, constants.L3_AGENT_SCHEDULER_EXT_ALIAS): self._agent_notification(context, method, routers) else: cctxt = self.client.prepare(fanout=True) cctxt.cast(context, method, routers=routers) def router_deleted(self, context, router_id): self._notification_fanout(context, 'router_deleted', router_id) def routers_updated(self, context, routers): if routers: self._notification(context, 'routers_updated', routers) def update_metering_label_rules(self, context, routers): self._notification(context, 'update_metering_label_rules', routers) def add_metering_label_rule(self, context, routers): self._notification(context, 'add_metering_label_rule', routers) def remove_metering_label_rule(self, context, routers): self._notification(context, 'remove_metering_label_rule', routers) def add_metering_label(self, context, routers): self._notification(context, 'add_metering_label', routers) def remove_metering_label(self, context, routers): self._notification(context, 'remove_metering_label', routers) def routers_updated_on_host(self, context, router_ids, host): """Notify router updates to specific hosts hosting DVR routers.""" self._notification_host(context, 'routers_updated', host, routers=router_ids) neutron-12.1.1/neutron/api/rpc/handlers/0000775000175000017500000000000013553660156020151 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/rpc/handlers/securitygroups_rpc.py0000664000175000017500000003575713553660047024516 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.plugins import directory from neutron_lib.utils import net from oslo_log import log as logging import oslo_messaging from neutron.api.rpc.handlers import resources_rpc from neutron.common import constants from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.db import securitygroups_rpc_base as sg_rpc_base LOG = logging.getLogger(__name__) class SecurityGroupServerRpcApi(object): """RPC client for security group methods in the plugin. This class implements the client side of an rpc interface. This interface is used by agents to call security group related methods implemented on the plugin side. The other side of this interface is defined in SecurityGroupServerRpcCallback. For more information about changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. """ def __init__(self, topic): target = oslo_messaging.Target( topic=topic, version='1.0', namespace=constants.RPC_NAMESPACE_SECGROUP) self.client = n_rpc.get_client(target) def security_group_rules_for_devices(self, context, devices): LOG.debug("Get security group rules " "for devices via rpc %r", devices) cctxt = self.client.prepare(version='1.1') return cctxt.call(context, 'security_group_rules_for_devices', devices=devices) def security_group_info_for_devices(self, context, devices): LOG.debug("Get security group information for devices via rpc %r", devices) cctxt = self.client.prepare(version='1.2') return cctxt.call(context, 'security_group_info_for_devices', devices=devices) class SecurityGroupServerRpcCallback(object): """Callback for SecurityGroup agent RPC in plugin implementations. This class implements the server side of an rpc interface. The client side can be found in SecurityGroupServerRpcApi. For more information on changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. """ # API version history: # 1.1 - Initial version # 1.2 - security_group_info_for_devices introduced as an optimization # NOTE: target must not be overridden in subclasses # to keep RPC API version consistent across plugins. target = oslo_messaging.Target(version='1.2', namespace=constants.RPC_NAMESPACE_SECGROUP) @property def plugin(self): return directory.get_plugin() def _get_devices_info(self, context, devices): return dict( (port['id'], port) for port in self.plugin.get_ports_from_devices(context, devices) if port and not net.is_port_trusted(port) ) def security_group_rules_for_devices(self, context, **kwargs): """Callback method to return security group rules for each port. also convert remote_group_id rule to source_ip_prefix and dest_ip_prefix rule :params devices: list of devices :returns: port correspond to the devices with security group rules """ devices_info = kwargs.get('devices') ports = self._get_devices_info(context, devices_info) return self.plugin.security_group_rules_for_ports(context, ports) def security_group_info_for_devices(self, context, **kwargs): """Return security group information for requested devices. :params devices: list of devices :returns: sg_info{ 'security_groups': {sg_id: [rule1, rule2]} 'sg_member_ips': {sg_id: {'IPv4': set(), 'IPv6': set()}} 'devices': {device_id: {device_info}} } Note that sets are serialized into lists by rpc code. """ devices_info = kwargs.get('devices') ports = self._get_devices_info(context, devices_info) return self.plugin.security_group_info_for_ports(context, ports) class SecurityGroupAgentRpcApiMixin(object): """RPC client for security group methods to the agent. This class implements the client side of an rpc interface. This interface is used by plugins to call security group methods implemented on the agent side. The other side of this interface can be found in SecurityGroupAgentRpcCallbackMixin. For more information about changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. """ # history # 1.1 Support Security Group RPC SG_RPC_VERSION = "1.1" def _get_security_group_topic(self): return topics.get_topic_name(self.topic, topics.SECURITY_GROUP, topics.UPDATE) def security_groups_rule_updated(self, context, security_groups): """Notify rule updated security groups.""" if not security_groups: return cctxt = self.client.prepare(version=self.SG_RPC_VERSION, topic=self._get_security_group_topic(), fanout=True) cctxt.cast(context, 'security_groups_rule_updated', security_groups=security_groups) def security_groups_member_updated(self, context, security_groups): """Notify member updated security groups.""" if not security_groups: return cctxt = self.client.prepare(version=self.SG_RPC_VERSION, topic=self._get_security_group_topic(), fanout=True) cctxt.cast(context, 'security_groups_member_updated', security_groups=security_groups) class SecurityGroupAgentRpcCallbackMixin(object): """A mix-in that enable SecurityGroup support in agent implementations. This class implements the server side of an rpc interface. The client side can be found in SecurityGroupAgentRpcApiMixin. For more information on changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. The sg_agent reference implementation is available in neutron/agent """ # mix-in object should be have sg_agent sg_agent = None def _security_groups_agent_not_set(self): LOG.warning("Security group agent binding currently not set. " "This should be set by the end of the init " "process.") def security_groups_rule_updated(self, context, **kwargs): """Callback for security group rule update. :param security_groups: list of updated security_groups """ security_groups = kwargs.get('security_groups', []) LOG.debug("Security group rule updated on remote: %s", security_groups) if not self.sg_agent: return self._security_groups_agent_not_set() self.sg_agent.security_groups_rule_updated(security_groups) def security_groups_member_updated(self, context, **kwargs): """Callback for security group member update. :param security_groups: list of updated security_groups """ security_groups = kwargs.get('security_groups', []) LOG.debug("Security group member updated on remote: %s", security_groups) if not self.sg_agent: return self._security_groups_agent_not_set() self.sg_agent.security_groups_member_updated(security_groups) class SecurityGroupServerAPIShim(sg_rpc_base.SecurityGroupInfoAPIMixin): """Agent-side replacement for SecurityGroupServerRpcApi using local data. This provides the same methods as SecurityGroupServerRpcApi but it reads from the updates delivered to the push notifications cache rather than calling the server. """ def __init__(self, rcache): self.rcache = rcache registry.subscribe(self._clear_child_sg_rules, 'SecurityGroup', events.AFTER_DELETE) registry.subscribe(self._add_child_sg_rules, 'SecurityGroup', events.AFTER_UPDATE) # set this attr so agent can adjust the timeout of the client self.client = resources_rpc.ResourcesPullRpcApi().client def register_legacy_sg_notification_callbacks(self, sg_agent): self._sg_agent = sg_agent registry.subscribe(self._handle_sg_rule_delete, 'SecurityGroupRule', events.AFTER_DELETE) registry.subscribe(self._handle_sg_rule_update, 'SecurityGroupRule', events.AFTER_UPDATE) registry.subscribe(self._handle_sg_member_delete, 'Port', events.AFTER_DELETE) registry.subscribe(self._handle_sg_member_update, 'Port', events.AFTER_UPDATE) def security_group_info_for_devices(self, context, devices): ports = self._get_devices_info(context, devices) result = self.security_group_info_for_ports(context, ports) return result def security_group_rules_for_devices(self, context, devices): # this is the legacy method that should never be called since # security_group_info_for_devices will never throw an unsupported # error. raise NotImplementedError() def _add_child_sg_rules(self, rtype, event, trigger, context, updated, **kwargs): # whenever we receive a full security group, add all child rules # because the server won't emit events for the individual rules on # creation. for rule in updated.rules: self.rcache.record_resource_update(context, 'SecurityGroupRule', rule) def _clear_child_sg_rules(self, rtype, event, trigger, context, existing, **kwargs): if not existing: return # the server can delete an entire security group without notifying # about the security group rules. so we need to emulate a rule deletion # when a security group is removed. filters = {'security_group_id': (existing.id, )} for rule in self.rcache.get_resources('SecurityGroupRule', filters): self.rcache.record_resource_delete(context, 'SecurityGroupRule', rule.id) def _handle_sg_rule_delete(self, rtype, event, trigger, context, existing, **kwargs): if not existing: return sg_id = existing.security_group_id self._sg_agent.security_groups_rule_updated([sg_id]) def _handle_sg_rule_update(self, rtype, event, trigger, context, existing, updated, **kwargs): sg_id = updated.security_group_id self._sg_agent.security_groups_rule_updated([sg_id]) def _handle_sg_member_delete(self, rtype, event, trigger, context, existing, **kwargs): # received on port delete sgs = set(existing.security_group_ids) if existing else set() if sgs: self._sg_agent.security_groups_member_updated(sgs) def _handle_sg_member_update(self, rtype, event, trigger, context, existing, updated, changed_fields, **kwargs): # received on port update sgs = set(existing.security_group_ids) if existing else set() if not changed_fields.intersection({'security_group_ids', 'fixed_ips', 'allowed_address_pairs'}): # none of the relevant fields to SG calculations changed return sgs.update({sg_id for sg_id in updated.security_group_ids}) if sgs: self._sg_agent.security_groups_member_updated(sgs) def _get_devices_info(self, context, devices): # NOTE(kevinbenton): this format is required by the sg code, it is # defined in get_port_from_device and mimics # make_port_dict_with_security_groups in ML2 db result = {} for device in devices: ovo = self.rcache.get_resource_by_id('Port', device) if not ovo: continue port = ovo.to_dict() # the caller expects trusted ports to be excluded from the result if net.is_port_trusted(port): continue port['security_groups'] = list(ovo.security_group_ids) port['security_group_rules'] = [] port['security_group_source_groups'] = [] port['fixed_ips'] = [str(f['ip_address']) for f in port['fixed_ips']] # NOTE(kevinbenton): this id==device is only safe for OVS. a lookup # will be required for linux bridge and others that don't have the # full port UUID port['device'] = port['id'] port['port_security_enabled'] = getattr( ovo.security, 'port_security_enabled', True) result[device] = port return result def _select_ips_for_remote_group(self, context, remote_group_ids): if not remote_group_ids: return {} ips_by_group = {rg: set() for rg in remote_group_ids} filters = {'security_group_ids': tuple(remote_group_ids)} for p in self.rcache.get_resources('Port', filters): port_ips = [str(addr.ip_address) for addr in p.fixed_ips + p.allowed_address_pairs] for sg_id in p.security_group_ids: if sg_id in ips_by_group: ips_by_group[sg_id].update(set(port_ips)) return ips_by_group def _select_rules_for_ports(self, context, ports): if not ports: return [] results = [] sg_ids = set((sg_id for p in ports.values() for sg_id in p['security_group_ids'])) rules_by_sgid = collections.defaultdict(list) for sg_id in sg_ids: filters = {'security_group_id': (sg_id, )} for r in self.rcache.get_resources('SecurityGroupRule', filters): rules_by_sgid[r.security_group_id].append(r) for p in ports.values(): for sg_id in p['security_group_ids']: for rule in rules_by_sgid[sg_id]: results.append((p['id'], rule.to_dict())) return results def _select_sg_ids_for_ports(self, context, ports): sg_ids = set((sg_id for p in ports.values() for sg_id in p['security_group_ids'])) return [(sg_id, ) for sg_id in sg_ids] neutron-12.1.1/neutron/api/rpc/handlers/dhcp_rpc.py0000664000175000017500000003570313553660047022314 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import copy import itertools import operator from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import exceptions from neutron_lib.plugins import directory from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import log as logging import oslo_messaging from oslo_utils import excutils from neutron._i18n import _ from neutron.common import constants as n_const from neutron.common import utils from neutron.db import api as db_api from neutron.db import provisioning_blocks from neutron.extensions import segment as segment_ext from neutron.plugins.common import utils as p_utils from neutron.quota import resource_registry LOG = logging.getLogger(__name__) class DhcpRpcCallback(object): """DHCP agent RPC callback in plugin implementations. This class implements the server side of an rpc interface. The client side of this interface can be found in neutron.agent.dhcp.agent.DhcpPluginApi. For more information about changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. """ # API version history: # 1.0 - Initial version. # 1.1 - Added get_active_networks_info, create_dhcp_port, # and update_dhcp_port methods. # 1.2 - Removed get_dhcp_port. When removing a method (Making a # backwards incompatible change) you would normally bump the # major version. However, since the method was unused in the # RPC client for many releases, it should be OK to bump the # minor release instead and claim RPC compatibility with the # last few client versions. # 1.3 - Removed release_port_fixed_ip. It's not used by reference DHCP # agent since Juno, so similar rationale for not bumping the # major version as above applies here too. # 1.4 - Removed update_lease_expiration. It's not used by reference # DHCP agent since Juno, so similar rationale for not bumping the # major version as above applies here too. # 1.5 - Added dhcp_ready_on_ports. # 1.6 - Removed get_active_networks. It's not used by reference # DHCP agent since Havana, so similar rationale for not bumping # the major version as above applies here too. target = oslo_messaging.Target( namespace=n_const.RPC_NAMESPACE_DHCP_PLUGIN, version='1.6') def _get_active_networks(self, context, **kwargs): """Retrieve and return a list of the active networks.""" host = kwargs.get('host') plugin = directory.get_plugin() if utils.is_extension_supported( plugin, constants.DHCP_AGENT_SCHEDULER_EXT_ALIAS): if cfg.CONF.network_auto_schedule: plugin.auto_schedule_networks(context, host) nets = plugin.list_active_networks_on_active_dhcp_agent( context, host) else: filters = dict(admin_state_up=[True]) nets = plugin.get_networks(context, filters=filters) return nets def _port_action(self, plugin, context, port, action): """Perform port operations taking care of concurrency issues.""" try: if action == 'create_port': return p_utils.create_port(plugin, context, port) elif action == 'update_port': return plugin.update_port(context, port['id'], port) else: msg = _('Unrecognized action') raise exceptions.Invalid(message=msg) except (db_exc.DBReferenceError, exceptions.NetworkNotFound, exceptions.SubnetNotFound, exceptions.InvalidInput, exceptions.IpAddressGenerationFailure) as e: with excutils.save_and_reraise_exception(reraise=False) as ctxt: if isinstance(e, exceptions.IpAddressGenerationFailure): # Check if the subnet still exists and if it does not, # this is the reason why the ip address generation failed. # In any other unlikely event re-raise try: subnet_id = port['port']['fixed_ips'][0]['subnet_id'] plugin.get_subnet(context, subnet_id) except exceptions.SubnetNotFound: pass else: ctxt.reraise = True if ctxt.reraise: net_id = port['port']['network_id'] LOG.warning("Action %(action)s for network %(net_id)s " "could not complete successfully: " "%(reason)s", {"action": action, "net_id": net_id, 'reason': e}) def _group_by_network_id(self, res): grouped = {} keyfunc = operator.itemgetter('network_id') for net_id, values in itertools.groupby(sorted(res, key=keyfunc), keyfunc): grouped[net_id] = list(values) return grouped def get_active_networks_info(self, context, **kwargs): """Returns all the networks/subnets/ports in system.""" host = kwargs.get('host') LOG.debug('get_active_networks_info from %s', host) networks = self._get_active_networks(context, **kwargs) plugin = directory.get_plugin() filters = {'network_id': [network['id'] for network in networks]} ports = plugin.get_ports(context, filters=filters) # default is to filter subnets based on 'enable_dhcp' flag if kwargs.get('enable_dhcp_filter', True): filters['enable_dhcp'] = [True] # NOTE(kevinbenton): we sort these because the agent builds tags # based on position in the list and has to restart the process if # the order changes. subnets = sorted(plugin.get_subnets(context, filters=filters), key=operator.itemgetter('id')) # Handle the possibility that the dhcp agent(s) only has connectivity # inside a segment. If the segment service plugin is loaded and # there are active dhcp enabled subnets, then filter out the subnets # that are not on the host's segment. seg_plug = directory.get_plugin( segment_ext.SegmentPluginBase.get_plugin_type()) seg_subnets = [subnet for subnet in subnets if subnet.get('segment_id')] nonlocal_subnets = [] if seg_plug and seg_subnets: host_segment_ids = seg_plug.get_segments_by_hosts(context, [host]) # Gather the ids of all the subnets that are on a segment that # this host touches seg_subnet_ids = {subnet['id'] for subnet in seg_subnets if subnet['segment_id'] in host_segment_ids} # Gather the ids of all the networks that are routed routed_net_ids = {seg_subnet['network_id'] for seg_subnet in seg_subnets} # Remove the subnets with segments that are not in the same # segments as the host. Do this only for the networks that are # routed because we want non-routed networks to work as # before. nonlocal_subnets = [subnet for subnet in seg_subnets if subnet['id'] not in seg_subnet_ids] subnets = [subnet for subnet in subnets if subnet['network_id'] not in routed_net_ids or subnet['id'] in seg_subnet_ids] grouped_subnets = self._group_by_network_id(subnets) grouped_nonlocal_subnets = self._group_by_network_id(nonlocal_subnets) grouped_ports = self._group_by_network_id(ports) for network in networks: network['subnets'] = grouped_subnets.get(network['id'], []) network['non_local_subnets'] = ( grouped_nonlocal_subnets.get(network['id'], [])) network['ports'] = grouped_ports.get(network['id'], []) return networks def get_network_info(self, context, **kwargs): """Retrieve and return extended information about a network.""" network_id = kwargs.get('network_id') host = kwargs.get('host') LOG.debug('Network %(network_id)s requested from ' '%(host)s', {'network_id': network_id, 'host': host}) plugin = directory.get_plugin() try: network = plugin.get_network(context, network_id) except exceptions.NetworkNotFound: LOG.debug("Network %s could not be found, it might have " "been deleted concurrently.", network_id) return filters = dict(network_id=[network_id]) subnets = plugin.get_subnets(context, filters=filters) seg_plug = directory.get_plugin( segment_ext.SegmentPluginBase.get_plugin_type()) nonlocal_subnets = [] if seg_plug and subnets: seg_subnets = [subnet for subnet in subnets if subnet.get('segment_id')] # If there are no subnets with segments, then this is not a routed # network and no filtering should take place. if seg_subnets: segment_ids = seg_plug.get_segments_by_hosts(context, [host]) # There might be something to do if no segment_ids exist that # are mapped to this host. However, it seems that if this # host is not mapped to any segments and this is a routed # network, then this host shouldn't have even been scheduled # to. nonlocal_subnets = [subnet for subnet in seg_subnets if subnet['segment_id'] not in segment_ids] subnets = [subnet for subnet in seg_subnets if subnet['segment_id'] in segment_ids] # NOTE(kevinbenton): we sort these because the agent builds tags # based on position in the list and has to restart the process if # the order changes. network['subnets'] = sorted(subnets, key=operator.itemgetter('id')) network['non_local_subnets'] = sorted(nonlocal_subnets, key=operator.itemgetter('id')) network['ports'] = plugin.get_ports(context, filters=filters) return network @db_api.retry_db_errors def release_dhcp_port(self, context, **kwargs): """Release the port currently being used by a DHCP agent.""" host = kwargs.get('host') network_id = kwargs.get('network_id') device_id = kwargs.get('device_id') LOG.debug('DHCP port deletion for %(network_id)s request from ' '%(host)s', {'network_id': network_id, 'host': host}) plugin = directory.get_plugin() plugin.delete_ports_by_device_id(context, device_id, network_id) @oslo_messaging.expected_exceptions(exceptions.IpAddressGenerationFailure) @db_api.retry_db_errors @resource_registry.mark_resources_dirty def create_dhcp_port(self, context, **kwargs): """Create and return dhcp port information. If an expected failure occurs, a None port is returned. """ host = kwargs.get('host') # Note(pbondar): Create deep copy of port to prevent operating # on changed dict if RetryRequest is raised port = copy.deepcopy(kwargs.get('port')) LOG.debug('Create dhcp port %(port)s ' 'from %(host)s.', {'port': port, 'host': host}) port['port']['device_owner'] = constants.DEVICE_OWNER_DHCP port['port'][portbindings.HOST_ID] = host if 'mac_address' not in port['port']: port['port']['mac_address'] = constants.ATTR_NOT_SPECIFIED plugin = directory.get_plugin() return self._port_action(plugin, context, port, 'create_port') def _is_dhcp_agent_hosting_network(self, plugin, context, host, network_id): """Check whether a DHCP agent (host) is hosting a network.""" agents = plugin.get_dhcp_agents_hosting_networks(context, [network_id], hosts=[host]) return len(agents) != 0 @oslo_messaging.expected_exceptions(exceptions.NetworkNotFound) @oslo_messaging.expected_exceptions(exceptions.IpAddressGenerationFailure) @db_api.retry_db_errors def update_dhcp_port(self, context, **kwargs): """Update the dhcp port.""" host = kwargs.get('host') port = kwargs.get('port') port['id'] = kwargs.get('port_id') port['port'][portbindings.HOST_ID] = host plugin = directory.get_plugin() try: network_id = port['port']['network_id'] old_port = plugin.get_port(context, port['id']) if (old_port['device_id'] != constants.DEVICE_ID_RESERVED_DHCP_PORT and old_port['device_id'] != utils.get_dhcp_agent_device_id(network_id, host)): return if not self._is_dhcp_agent_hosting_network(plugin, context, host, network_id): LOG.warning("The DHCP agent on %(host)s does not host the " "network %(net_id)s.", {"host": host, "net_id": network_id}) raise exceptions.NetworkNotFound(net_id=network_id) LOG.debug('Update dhcp port %(port)s ' 'from %(host)s.', {'port': port, 'host': host}) return self._port_action(plugin, context, port, 'update_port') except exceptions.PortNotFound: LOG.debug('Host %(host)s tried to update port ' '%(port_id)s which no longer exists.', {'host': host, 'port_id': port['id']}) @db_api.retry_db_errors def dhcp_ready_on_ports(self, context, port_ids): for port_id in port_ids: provisioning_blocks.provisioning_complete( context, port_id, resources.PORT, provisioning_blocks.DHCP_ENTITY) neutron-12.1.1/neutron/api/rpc/handlers/resources_rpc.py0000664000175000017500000002661413553660047023411 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mellanox Technologies, Ltd # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from neutron_lib import exceptions from oslo_log import helpers as log_helpers from oslo_log import log as logging import oslo_messaging from neutron._i18n import _ from neutron.api.rpc.callbacks.consumer import registry as cons_registry from neutron.api.rpc.callbacks import exceptions as rpc_exc from neutron.api.rpc.callbacks.producer import registry as prod_registry from neutron.api.rpc.callbacks import resources from neutron.api.rpc.callbacks import version_manager from neutron.common import constants from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.objects import base as obj_base LOG = logging.getLogger(__name__) class ResourcesRpcError(exceptions.NeutronException): pass class InvalidResourceTypeClass(ResourcesRpcError): message = _("Invalid resource type %(resource_type)s") class ResourceNotFound(ResourcesRpcError): message = _("Resource %(resource_id)s of type %(resource_type)s " "not found") def _validate_resource_type(resource_type): if not resources.is_valid_resource_type(resource_type): raise InvalidResourceTypeClass(resource_type=resource_type) def _resource_to_class(resource_type): _validate_resource_type(resource_type) # we've already validated the resource type, so we are pretty sure the # class is there => no need to validate it specifically return resources.get_resource_cls(resource_type) def resource_type_versioned_topic(resource_type, version=None): """Return the topic for a resource type. If no version is provided, the latest version of the object will be used. """ _validate_resource_type(resource_type) cls = resources.get_resource_cls(resource_type) return topics.RESOURCE_TOPIC_PATTERN % {'resource_type': resource_type, 'version': version or cls.VERSION} class ResourcesPullRpcApi(object): """Agent-side RPC (stub) for agent-to-plugin interaction. This class implements the client side of an rpc interface. The server side can be found below: ResourcesPullRpcCallback. For more information on this RPC interface, see doc/source/devref/rpc_callbacks.rst. """ def __new__(cls): # make it a singleton if not hasattr(cls, '_instance'): cls._instance = super(ResourcesPullRpcApi, cls).__new__(cls) target = oslo_messaging.Target( topic=topics.PLUGIN, version='1.1', namespace=constants.RPC_NAMESPACE_RESOURCES) cls._instance.client = n_rpc.get_client(target) return cls._instance @log_helpers.log_method_call def pull(self, context, resource_type, resource_id): resource_type_cls = _resource_to_class(resource_type) cctxt = self.client.prepare() primitive = cctxt.call(context, 'pull', resource_type=resource_type, version=resource_type_cls.VERSION, resource_id=resource_id) if primitive is None: raise ResourceNotFound(resource_type=resource_type, resource_id=resource_id) return resource_type_cls.clean_obj_from_primitive(primitive) @log_helpers.log_method_call def bulk_pull(self, context, resource_type, filter_kwargs=None): resource_type_cls = _resource_to_class(resource_type) cctxt = self.client.prepare() primitives = cctxt.call(context, 'bulk_pull', resource_type=resource_type, version=resource_type_cls.VERSION, filter_kwargs=filter_kwargs) return [resource_type_cls.clean_obj_from_primitive(primitive) for primitive in primitives] class ResourcesPullRpcCallback(object): """Plugin-side RPC (implementation) for agent-to-plugin interaction. This class implements the server side of an rpc interface. The client side can be found above: ResourcesPullRpcApi. For more information on this RPC interface, see doc/source/devref/rpc_callbacks.rst. """ # History # 1.0 Initial version # 1.1 Added bulk_pull target = oslo_messaging.Target( version='1.1', namespace=constants.RPC_NAMESPACE_RESOURCES) @oslo_messaging.expected_exceptions(rpc_exc.CallbackNotFound) def pull(self, context, resource_type, version, resource_id): obj = prod_registry.pull(resource_type, resource_id, context=context) if obj: return obj.obj_to_primitive(target_version=version) @oslo_messaging.expected_exceptions(rpc_exc.CallbackNotFound) def bulk_pull(self, context, resource_type, version, filter_kwargs=None): filter_kwargs = filter_kwargs or {} resource_type_cls = _resource_to_class(resource_type) # TODO(kevinbenton): add in producer registry so producers can add # hooks to mangle these things like they can with 'pull'. return [obj.obj_to_primitive(target_version=version) for obj in resource_type_cls.get_objects(context, _pager=None, **filter_kwargs)] class ResourcesPushToServersRpcApi(object): """Publisher-side RPC (stub) for plugin-to-plugin fanout interaction. This class implements the client side of an rpc interface. The receiver side can be found below: ResourcesPushToServerRpcCallback. For more information on this RPC interface, see doc/source/devref/rpc_callbacks.rst. """ def __init__(self): target = oslo_messaging.Target( topic=topics.SERVER_RESOURCE_VERSIONS, version='1.0', namespace=constants.RPC_NAMESPACE_RESOURCES) self.client = n_rpc.get_client(target) @log_helpers.log_method_call def report_agent_resource_versions(self, context, agent_type, agent_host, version_map): """Fan out all the agent resource versions to other servers.""" cctxt = self.client.prepare(fanout=True) cctxt.cast(context, 'report_agent_resource_versions', agent_type=agent_type, agent_host=agent_host, version_map=version_map) class ResourcesPushToServerRpcCallback(object): """Receiver-side RPC (implementation) for plugin-to-plugin interaction. This class implements the receiver side of an rpc interface. The client side can be found above: ResourcePushToServerRpcApi. For more information on this RPC interface, see doc/source/devref/rpc_callbacks.rst. """ # History # 1.0 Initial version target = oslo_messaging.Target( version='1.0', namespace=constants.RPC_NAMESPACE_RESOURCES) @log_helpers.log_method_call def report_agent_resource_versions(self, context, agent_type, agent_host, version_map): consumer_id = version_manager.AgentConsumer(agent_type=agent_type, host=agent_host) version_manager.update_versions(consumer_id, version_map) class ResourcesPushRpcApi(object): """Plugin-side RPC for plugin-to-agents interaction. This interface is designed to push versioned object updates to interested agents using fanout topics. This class implements the caller side of an rpc interface. The receiver side can be found below: ResourcesPushRpcCallback. """ def __init__(self): target = oslo_messaging.Target( namespace=constants.RPC_NAMESPACE_RESOURCES) self.client = n_rpc.get_client(target) def _prepare_object_fanout_context(self, obj, resource_version, rpc_version): """Prepare fanout context, one topic per object type.""" obj_topic = resource_type_versioned_topic(obj.obj_name(), resource_version) return self.client.prepare(fanout=True, topic=obj_topic, version=rpc_version) @staticmethod def _classify_resources_by_type(resource_list): resources_by_type = collections.defaultdict(list) for resource in resource_list: resource_type = resources.get_resource_type(resource) resources_by_type[resource_type].append(resource) return resources_by_type def push(self, context, resource_list, event_type): """Push an event and list of resources to agents, batched per type. When a list of different resource types is passed to this method, the push will be sent as separate individual list pushes, one per resource type. """ resources_by_type = self._classify_resources_by_type(resource_list) LOG.debug( "Pushing event %s for resources: %s", event_type, {t: ["ID=%s,revision_number=%s" % ( getattr(obj, 'id', None), getattr(obj, 'revision_number', None)) for obj in resources_by_type[t]] for t in resources_by_type}) for resource_type, type_resources in resources_by_type.items(): self._push(context, resource_type, type_resources, event_type) def _push(self, context, resource_type, resource_list, event_type): """Push an event and list of resources of the same type to agents.""" _validate_resource_type(resource_type) for version in version_manager.get_resource_versions(resource_type): cctxt = self._prepare_object_fanout_context( resource_list[0], version, rpc_version='1.1') dehydrated_resources = [ resource.obj_to_primitive(target_version=version) for resource in resource_list] cctxt.cast(context, 'push', resource_list=dehydrated_resources, event_type=event_type) class ResourcesPushRpcCallback(object): """Agent-side RPC for plugin-to-agents interaction. This class implements the receiver for notification about versioned objects resource updates used by neutron.api.rpc.callbacks. You can find the caller side in ResourcesPushRpcApi. """ # History # 1.0 Initial version # 1.1 push method introduces resource_list support target = oslo_messaging.Target(version='1.1', namespace=constants.RPC_NAMESPACE_RESOURCES) @oslo_messaging.expected_exceptions(rpc_exc.CallbackNotFound) def push(self, context, **kwargs): """Push receiver, will always receive resources of the same type.""" resource_list = kwargs['resource_list'] event_type = kwargs['event_type'] resource_objs = [ obj_base.NeutronObject.clean_obj_from_primitive(resource) for resource in resource_list] resource_type = resources.get_resource_type(resource_objs[0]) cons_registry.push(context, resource_type, resource_objs, event_type) neutron-12.1.1/neutron/api/rpc/handlers/l3_rpc.py0000664000175000017500000003752713553660047021722 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib import context as neutron_context from neutron_lib import exceptions from neutron_lib.exceptions import l3 as l3_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from neutron.common import constants as n_const from neutron.common import utils from neutron.db import api as db_api LOG = logging.getLogger(__name__) class L3RpcCallback(object): """L3 agent RPC callback in plugin implementations.""" # 1.0 L3PluginApi BASE_RPC_API_VERSION # 1.1 Support update_floatingip_statuses # 1.2 Added methods for DVR support # 1.3 Added a method that returns the list of activated services # 1.4 Added L3 HA update_router_state. This method was later removed, # since it was unused. The RPC version was not changed # 1.5 Added update_ha_routers_states # 1.6 Added process_prefix_update to support IPv6 Prefix Delegation # 1.7 Added method delete_agent_gateway_port for DVR Routers # 1.8 Added address scope information # 1.9 Added get_router_ids # 1.10 Added update_all_ha_network_port_statuses target = oslo_messaging.Target(version='1.10') @property def plugin(self): if not hasattr(self, '_plugin'): self._plugin = directory.get_plugin() return self._plugin @property def l3plugin(self): if not hasattr(self, '_l3plugin'): self._l3plugin = directory.get_plugin(plugin_constants.L3) return self._l3plugin def update_all_ha_network_port_statuses(self, context, host): """Set HA network port to DOWN for HA routers hosted on This will update HA network port status to down for all HA routers hosted on . This is needed to avoid l3 agent spawning keepalived when l2 agent not yet wired the port. This can happen after a system reboot that has wiped out flows, etc and the L2 agent hasn't started up yet. The port will still be ACTIVE in the data model and the L3 agent will use that info to mistakenly think that L2 network is ready. By forcing into DOWN, we will require the L2 agent to essentially ack that the port is indeed ACTIVE by reacting to the port update and calling update_device_up. """ if not utils.is_extension_supported( self.plugin, constants.PORT_BINDING_EXT_ALIAS): return device_filter = { 'device_owner': [constants.DEVICE_OWNER_ROUTER_HA_INTF], 'status': [constants.PORT_STATUS_ACTIVE]} ports = self.plugin.get_ports(context, filters=device_filter) ha_ports = [p['id'] for p in ports if p.get(portbindings.HOST_ID) == host] if not ha_ports: return LOG.debug("L3 agent on host %(host)s requested for fullsync, so " "setting HA network ports %(ha_ports)s status to DOWN.", {"host": host, "ha_ports": ha_ports}) for p in ha_ports: self.plugin.update_port( context, p, {'port': {'status': constants.PORT_STATUS_DOWN}}) def get_router_ids(self, context, host): """Returns IDs of routers scheduled to l3 agent on This will autoschedule unhosted routers to l3 agent on and then return all ids of routers scheduled to it. """ if utils.is_extension_supported( self.l3plugin, constants.L3_AGENT_SCHEDULER_EXT_ALIAS): if cfg.CONF.router_auto_schedule: self.l3plugin.auto_schedule_routers(context, host) return self.l3plugin.list_router_ids_on_host(context, host) @db_api.retry_db_errors def sync_routers(self, context, **kwargs): """Sync routers according to filters to a specific agent. @param context: contain user information @param kwargs: host, router_ids @return: a list of routers with their interfaces and floating_ips """ router_ids = kwargs.get('router_ids') host = kwargs.get('host') context = neutron_context.get_admin_context() routers = self._routers_to_sync(context, router_ids, host) if utils.is_extension_supported( self.plugin, constants.PORT_BINDING_EXT_ALIAS): self._ensure_host_set_on_ports(context, host, routers) # refresh the data structure after ports are bound routers = self._routers_to_sync(context, router_ids, host) return routers def _routers_to_sync(self, context, router_ids, host=None): if utils.is_extension_supported( self.l3plugin, constants.L3_AGENT_SCHEDULER_EXT_ALIAS): routers = ( self.l3plugin.list_active_sync_routers_on_active_l3_agent( context, host, router_ids)) else: routers = self.l3plugin.get_sync_data(context, router_ids) return routers def _ensure_host_set_on_ports(self, context, host, routers): for router in routers: LOG.debug("Checking router: %(id)s for host: %(host)s", {'id': router['id'], 'host': host}) if router.get('gw_port') and router.get('distributed'): # '' is used to effectively clear binding of a gw port if not # bound (snat is not hosted on any l3 agent) gw_port_host = router.get('gw_port_host') or '' self._ensure_host_set_on_port(context, gw_port_host, router.get('gw_port'), router['id'], ha_router_port=router.get('ha')) for p in router.get(n_const.SNAT_ROUTER_INTF_KEY, []): self._ensure_host_set_on_port( context, gw_port_host, p, router['id'], ha_router_port=router.get('ha')) else: self._ensure_host_set_on_port( context, host, router.get('gw_port'), router['id'], ha_router_port=router.get('ha')) for interface in router.get(constants.INTERFACE_KEY, []): self._ensure_host_set_on_port( context, host, interface, router['id'], ha_router_port=router.get('ha')) interface = router.get(constants.HA_INTERFACE_KEY) if interface: self._ensure_host_set_on_port(context, host, interface, router['id']) def _ensure_host_set_on_port(self, context, host, port, router_id=None, ha_router_port=False): not_bound = port and port.get(portbindings.VIF_TYPE) in ( portbindings.VIF_TYPE_BINDING_FAILED, portbindings.VIF_TYPE_UNBOUND) if (port and host is not None and (port.get('device_owner') != constants.DEVICE_OWNER_DVR_INTERFACE and port.get(portbindings.HOST_ID) != host or not_bound)): # Ports owned by non-HA routers are bound again if they're # already bound but the router moved to another host. if not ha_router_port: # All ports, including ports created for SNAT'ing for # DVR are handled here try: LOG.debug("Updating router %(router)s port %(port)s " "binding host %(host)s", {"router": router_id, "port": port['id'], "host": host}) self.plugin.update_port( context, port['id'], {'port': {portbindings.HOST_ID: host}}) # updating port's host to pass actual info to l3 agent port[portbindings.HOST_ID] = host except exceptions.PortNotFound: LOG.debug("Port %(port)s not found while updating " "agent binding for router %(router)s.", {"port": port['id'], "router": router_id}) # Ports owned by HA routers should only be bound once, if # they are unbound. These ports are moved when an agent reports # that one of its routers moved to the active state. else: if not port.get(portbindings.HOST_ID): active_host = ( self.l3plugin.get_active_host_for_ha_router( context, router_id)) if active_host: host = active_host # If there is currently no active router instance (For # example it's a new router), the host that requested # the routers (Essentially a random host) will do. The # port binding will be corrected when an active is # elected. try: LOG.debug("Updating router %(router)s port %(port)s " "binding host %(host)s", {"router": router_id, "port": port['id'], "host": host}) self.plugin.update_port( context, port['id'], {'port': {portbindings.HOST_ID: host}}) except exceptions.PortNotFound: LOG.debug("Port %(port)s not found while updating " "agent binding for router %(router)s.", {"port": port['id'], "router": router_id}) elif (port and port.get('device_owner') == constants.DEVICE_OWNER_DVR_INTERFACE): # Ports that are DVR interfaces have multiple bindings (based on # of hosts on which DVR router interfaces are spawned). Such # bindings are created/updated here by invoking # update_distributed_port_binding self.plugin.update_distributed_port_binding(context, port['id'], {'port': {portbindings.HOST_ID: host, 'device_id': router_id} }) def get_external_network_id(self, context, **kwargs): """Get one external network id for l3 agent. l3 agent expects only one external network when it performs this query. """ context = neutron_context.get_admin_context() net_id = self.plugin.get_external_network_id(context) LOG.debug("External network ID returned to l3 agent: %s", net_id) return net_id def get_service_plugin_list(self, context, **kwargs): return directory.get_plugins().keys() @db_api.retry_db_errors def update_floatingip_statuses(self, context, router_id, fip_statuses): """Update operational status for a floating IP.""" with context.session.begin(subtransactions=True): for (floatingip_id, status) in fip_statuses.items(): LOG.debug("New status for floating IP %(floatingip_id)s: " "%(status)s", {'floatingip_id': floatingip_id, 'status': status}) try: self.l3plugin.update_floatingip_status(context, floatingip_id, status) except l3_exc.FloatingIPNotFound: LOG.debug("Floating IP: %s no longer present.", floatingip_id) # Find all floating IPs known to have been the given router # for which an update was not received. Set them DOWN mercilessly # This situation might occur for some asynchronous backends if # notifications were missed known_router_fips = self.l3plugin.get_floatingips( context, {'last_known_router_id': [router_id]}) # Consider only floating ips which were disassociated in the API # FIXME(salv-orlando): Filtering in code should be avoided. # the plugin should offer a way to specify a null filter fips_to_disable = (fip['id'] for fip in known_router_fips if not fip['router_id']) for fip_id in fips_to_disable: self.l3plugin.update_floatingip_status( context, fip_id, constants.FLOATINGIP_STATUS_DOWN) def get_ports_by_subnet(self, context, **kwargs): """DVR: RPC called by dvr-agent to get all ports for subnet.""" subnet_id = kwargs.get('subnet_id') LOG.debug("DVR: subnet_id: %s", subnet_id) filters = {'fixed_ips': {'subnet_id': [subnet_id]}} return self.plugin.get_ports(context, filters=filters) @db_api.retry_db_errors def get_agent_gateway_port(self, context, **kwargs): """Get Agent Gateway port for FIP. l3 agent expects an Agent Gateway Port to be returned for this query. """ network_id = kwargs.get('network_id') host = kwargs.get('host') admin_ctx = neutron_context.get_admin_context() agent_port = self.l3plugin.create_fip_agent_gw_port_if_not_exists( admin_ctx, network_id, host) self._ensure_host_set_on_port(admin_ctx, host, agent_port) LOG.debug('Agent Gateway port returned : %(agent_port)s with ' 'host %(host)s', {'agent_port': agent_port, 'host': host}) return agent_port @db_api.retry_db_errors def update_ha_routers_states(self, context, **kwargs): """Update states for HA routers. Get a map of router_id to its HA state on a host and update the DB. State must be in: ('active', 'standby'). """ states = kwargs.get('states') host = kwargs.get('host') LOG.debug('Updating HA routers states on host %s: %s', host, states) self.l3plugin.update_routers_states(context, states, host) def process_prefix_update(self, context, **kwargs): subnets = kwargs.get('subnets') updated_subnets = [] for subnet_id, prefix in subnets.items(): updated_subnets.append(self.plugin.update_subnet( context, subnet_id, {'subnet': {'cidr': prefix}})) return updated_subnets @db_api.retry_db_errors def delete_agent_gateway_port(self, context, **kwargs): """Delete Floatingip agent gateway port.""" network_id = kwargs.get('network_id') host = kwargs.get('host') admin_ctx = neutron_context.get_admin_context() self.l3plugin.delete_floatingip_agent_gateway_port( admin_ctx, host, network_id) neutron-12.1.1/neutron/api/rpc/handlers/__init__.py0000664000175000017500000000000013553660046022246 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/rpc/handlers/dvr_rpc.py0000664000175000017500000001262713553660047022171 0ustar zuulzuul00000000000000# Copyright 2014, Hewlett-Packard Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins import directory from oslo_log import helpers as log_helpers from oslo_log import log as logging import oslo_messaging from neutron.common import constants from neutron.common import rpc as n_rpc from neutron.common import topics LOG = logging.getLogger(__name__) class DVRServerRpcApi(object): """Agent-side RPC (stub) for agent-to-plugin interaction. This class implements the client side of an rpc interface. The server side can be found below: DVRServerRpcCallback. For more information on changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. """ # 1.0 Initial Version # 1.1 Support for passing 'fixed_ips' in get_subnet_for_dvr function. # Passing 'subnet" will be deprecated in the next release. def __init__(self, topic): target = oslo_messaging.Target(topic=topic, version='1.0', namespace=constants.RPC_NAMESPACE_DVR) self.client = n_rpc.get_client(target) @log_helpers.log_method_call def get_dvr_mac_address_by_host(self, context, host): cctxt = self.client.prepare() return cctxt.call(context, 'get_dvr_mac_address_by_host', host=host) @log_helpers.log_method_call def get_dvr_mac_address_list(self, context): cctxt = self.client.prepare() return cctxt.call(context, 'get_dvr_mac_address_list') @log_helpers.log_method_call def get_ports_on_host_by_subnet(self, context, host, subnet): """Get DVR serviced ports on given host and subnet.""" cctxt = self.client.prepare() return cctxt.call(context, 'get_ports_on_host_by_subnet', host=host, subnet=subnet) @log_helpers.log_method_call def get_subnet_for_dvr(self, context, subnet, fixed_ips): cctxt = self.client.prepare() return cctxt.call( context, 'get_subnet_for_dvr', subnet=subnet, fixed_ips=fixed_ips) class DVRServerRpcCallback(object): """Plugin-side RPC (implementation) for agent-to-plugin interaction. This class implements the server side of an rpc interface. The client side can be found above: DVRServerRpcApi. For more information on changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. """ # History # 1.0 Initial version # 1.1 Support for passing the 'fixed_ips" in get_subnet_for_dvr. # Passing subnet will be deprecated in the next release. target = oslo_messaging.Target(version='1.1', namespace=constants.RPC_NAMESPACE_DVR) @property def plugin(self): if not getattr(self, '_plugin', None): self._plugin = directory.get_plugin() return self._plugin def get_dvr_mac_address_list(self, context): return self.plugin.get_dvr_mac_address_list(context) def get_dvr_mac_address_by_host(self, context, **kwargs): host = kwargs.get('host') LOG.debug("DVR Agent requests mac_address for host %s", host) return self.plugin.get_dvr_mac_address_by_host(context, host) def get_ports_on_host_by_subnet(self, context, **kwargs): """Get DVR serviced ports for given host and subnet.""" host = kwargs.get('host') subnet = kwargs.get('subnet') LOG.debug("DVR Agent requests list of VM ports on host %s", host) return self.plugin.get_ports_on_host_by_subnet(context, host, subnet) def get_subnet_for_dvr(self, context, **kwargs): fixed_ips = kwargs.get('fixed_ips') subnet = kwargs.get('subnet') return self.plugin.get_subnet_for_dvr( context, subnet, fixed_ips=fixed_ips) class DVRAgentRpcApiMixin(object): """Plugin-side RPC (stub) for plugin-to-agent interaction.""" DVR_RPC_VERSION = "1.0" def _get_dvr_update_topic(self): return topics.get_topic_name(self.topic, topics.DVR, topics.UPDATE) def dvr_mac_address_update(self, context, dvr_macs): """Notify dvr mac address updates.""" if not dvr_macs: return cctxt = self.client.prepare(topic=self._get_dvr_update_topic(), version=self.DVR_RPC_VERSION, fanout=True) cctxt.cast(context, 'dvr_mac_address_update', dvr_macs=dvr_macs) class DVRAgentRpcCallbackMixin(object): """Agent-side RPC (implementation) for plugin-to-agent interaction.""" def dvr_mac_address_update(self, context, **kwargs): """Callback for dvr_mac_addresses update. :param dvr_macs: list of updated dvr_macs """ dvr_macs = kwargs.get('dvr_macs', []) LOG.debug("dvr_macs updated on remote: %s", dvr_macs) self.dvr_agent.dvr_mac_address_update(dvr_macs) neutron-12.1.1/neutron/api/rpc/handlers/metadata_rpc.py0000664000175000017500000000305113553660047023145 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.plugins import directory import oslo_messaging from neutron.common import constants class MetadataRpcCallback(object): """Metadata agent RPC callback in plugin implementations. This class implements the server side of an rpc interface used by the metadata service to make calls back into the Neutron plugin. The client side is defined in neutron.agent.metadata.agent.MetadataPluginAPI. For more information about changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. """ # 1.0 MetadataPluginAPI BASE_RPC_API_VERSION target = oslo_messaging.Target(version='1.0', namespace=constants.RPC_NAMESPACE_METADATA) @property def plugin(self): if not hasattr(self, '_plugin'): self._plugin = directory.get_plugin() return self._plugin def get_ports(self, context, filters): return self.plugin.get_ports(context, filters=filters) neutron-12.1.1/neutron/api/extensions.py0000664000175000017500000006176113553660047020350 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import imp import os from neutron_lib.api import extensions as api_extensions from neutron_lib.plugins import directory from oslo_config import cfg from oslo_log import log as logging from oslo_middleware import base import routes import webob.dec import webob.exc from neutron._i18n import _ from neutron.common import exceptions from neutron import extensions as core_extensions from neutron.plugins.common import constants as const from neutron.services import provider_configuration from neutron import wsgi LOG = logging.getLogger(__name__) EXTENSION_SUPPORTED_CHECK_MAP = {} _PLUGIN_AGNOSTIC_EXTENSIONS = set() def register_custom_supported_check(alias, f, plugin_agnostic=False): '''Register a custom function to determine if extension is supported. Consequent calls for the same alias replace the registered function. :param alias: API extension alias name :param f: custom check function that returns True if extension is supported :param plugin_agnostic: if False, don't require a plugin to claim support with supported_extension_aliases. If True, a plugin must claim the extension is supported. ''' EXTENSION_SUPPORTED_CHECK_MAP[alias] = f if plugin_agnostic: _PLUGIN_AGNOSTIC_EXTENSIONS.add(alias) class ActionExtensionController(wsgi.Controller): def __init__(self, application): self.application = application self.action_handlers = {} def add_action(self, action_name, handler): self.action_handlers[action_name] = handler def action(self, request, id): input_dict = self._deserialize(request.body, request.get_content_type()) for action_name, handler in self.action_handlers.items(): if action_name in input_dict: return handler(input_dict, request, id) # no action handler found (bump to downstream application) response = self.application return response class RequestExtensionController(wsgi.Controller): def __init__(self, application): self.application = application self.handlers = [] def add_handler(self, handler): self.handlers.append(handler) def process(self, request, *args, **kwargs): res = request.get_response(self.application) # currently request handlers are un-ordered for handler in self.handlers: response = handler(request, res) return response class ExtensionController(wsgi.Controller): def __init__(self, extension_manager): self.extension_manager = extension_manager @staticmethod def _translate(ext): ext_data = {} ext_data['name'] = ext.get_name() ext_data['alias'] = ext.get_alias() ext_data['description'] = ext.get_description() ext_data['updated'] = ext.get_updated() ext_data['links'] = [] # TODO(dprince): implement extension links return ext_data def index(self, request): extensions = [] for _alias, ext in self.extension_manager.extensions.items(): extensions.append(self._translate(ext)) return dict(extensions=extensions) def show(self, request, id): # NOTE(dprince): the extensions alias is used as the 'id' for show ext = self.extension_manager.extensions.get(id, None) if not ext: raise webob.exc.HTTPNotFound( _("Extension with alias %s does not exist") % id) return dict(extension=self._translate(ext)) def delete(self, request, id): msg = _('Resource not found.') raise webob.exc.HTTPNotFound(msg) def create(self, request): msg = _('Resource not found.') raise webob.exc.HTTPNotFound(msg) class ExtensionMiddleware(base.ConfigurableMiddleware): """Extensions middleware for WSGI.""" def __init__(self, application, ext_mgr=None): self.ext_mgr = (ext_mgr or ExtensionManager(get_extensions_path())) mapper = routes.Mapper() # extended resources for resource in self.ext_mgr.get_resources(): path_prefix = resource.path_prefix if resource.parent: path_prefix = (resource.path_prefix + "/%s/{%s_id}" % (resource.parent["collection_name"], resource.parent["member_name"])) LOG.debug('Extended resource: %s', resource.collection) for action, method in resource.collection_actions.items(): conditions = dict(method=[method]) path = "/%s/%s" % (resource.collection, action) with mapper.submapper(controller=resource.controller, action=action, path_prefix=path_prefix, conditions=conditions) as submap: submap.connect(path_prefix + path, path) submap.connect(path_prefix + path + "_format", "%s.:(format)" % path) for action, method in resource.collection_methods.items(): conditions = dict(method=[method]) path = "/%s" % resource.collection with mapper.submapper(controller=resource.controller, action=action, path_prefix=path_prefix, conditions=conditions) as submap: submap.connect(path_prefix + path, path) submap.connect(path_prefix + path + "_format", "%s.:(format)" % path) mapper.resource(resource.collection, resource.collection, controller=resource.controller, member=resource.member_actions, parent_resource=resource.parent, path_prefix=path_prefix) # extended actions action_controllers = self._action_ext_controllers(application, self.ext_mgr, mapper) for action in self.ext_mgr.get_actions(): LOG.debug('Extended action: %s', action.action_name) controller = action_controllers[action.collection] controller.add_action(action.action_name, action.handler) # extended requests req_controllers = self._request_ext_controllers(application, self.ext_mgr, mapper) for request_ext in self.ext_mgr.get_request_extensions(): LOG.debug('Extended request: %s', request_ext.key) controller = req_controllers[request_ext.key] controller.add_handler(request_ext.handler) self._router = routes.middleware.RoutesMiddleware(self._dispatch, mapper) super(ExtensionMiddleware, self).__init__(application) @classmethod def factory(cls, global_config, **local_config): """Paste factory.""" def _factory(app): return cls(app, global_config, **local_config) return _factory def _action_ext_controllers(self, application, ext_mgr, mapper): """Return a dict of ActionExtensionController-s by collection.""" action_controllers = {} for action in ext_mgr.get_actions(): if action.collection not in action_controllers.keys(): controller = ActionExtensionController(application) mapper.connect("/%s/:(id)/action.:(format)" % action.collection, action='action', controller=controller, conditions=dict(method=['POST'])) mapper.connect("/%s/:(id)/action" % action.collection, action='action', controller=controller, conditions=dict(method=['POST'])) action_controllers[action.collection] = controller return action_controllers def _request_ext_controllers(self, application, ext_mgr, mapper): """Returns a dict of RequestExtensionController-s by collection.""" request_ext_controllers = {} for req_ext in ext_mgr.get_request_extensions(): if req_ext.key not in request_ext_controllers.keys(): controller = RequestExtensionController(application) mapper.connect(req_ext.url_route + '.:(format)', action='process', controller=controller, conditions=req_ext.conditions) mapper.connect(req_ext.url_route, action='process', controller=controller, conditions=req_ext.conditions) request_ext_controllers[req_ext.key] = controller return request_ext_controllers @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): """Route the incoming request with router.""" req.environ['extended.app'] = self.application return self._router @staticmethod @webob.dec.wsgify(RequestClass=wsgi.Request) def _dispatch(req): """Dispatch the request. Returns the routed WSGI app's response or defers to the extended application. """ match = req.environ['wsgiorg.routing_args'][1] if not match: return req.environ['extended.app'] app = match['controller'] return app def plugin_aware_extension_middleware_factory(global_config, **local_config): """Paste factory.""" def _factory(app): ext_mgr = PluginAwareExtensionManager.get_instance() return ExtensionMiddleware(app, ext_mgr=ext_mgr) return _factory class ExtensionManager(object): """Load extensions from the configured extension path. See tests/unit/extensions/foxinsocks.py for an example extension implementation. """ def __init__(self, path): LOG.info('Initializing extension manager.') self.path = path self.extensions = {} self._load_all_extensions() def get_resources(self): """Returns a list of ResourceExtension objects.""" resources = [] resources.append(ResourceExtension('extensions', ExtensionController(self))) for ext in self.extensions.values(): resources.extend(ext.get_resources()) return resources def get_pecan_resources(self): """Returns a list of PecanResourceExtension objects.""" resources = [] for ext in self.extensions.values(): resources.extend(ext.get_pecan_resources()) return resources def get_actions(self): """Returns a list of ActionExtension objects.""" actions = [] for ext in self.extensions.values(): actions.extend(ext.get_actions()) return actions def get_request_extensions(self): """Returns a list of RequestExtension objects.""" request_exts = [] for ext in self.extensions.values(): request_exts.extend(ext.get_request_extensions()) return request_exts def extend_resources(self, version, attr_map): """Extend resources with additional resources or attributes. :param attr_map: the existing mapping from resource name to attrs definition. After this function, we will extend the attr_map if an extension wants to extend this map. """ processed_exts = {} exts_to_process = self.extensions.copy() check_optionals = True # Iterate until there are unprocessed extensions or if no progress # is made in a whole iteration while exts_to_process: processed_ext_count = len(processed_exts) for ext_name, ext in list(exts_to_process.items()): # Process extension only if all required extensions # have been processed already required_exts_set = set(ext.get_required_extensions()) if required_exts_set - set(processed_exts): continue optional_exts_set = set(ext.get_optional_extensions()) if check_optionals and optional_exts_set - set(processed_exts): continue extended_attrs = ext.get_extended_resources(version) for res, resource_attrs in extended_attrs.items(): res_to_update = attr_map.setdefault(res, {}) if self._is_sub_resource(res_to_update): # in the case of an existing sub-resource, we need to # update the parameters content rather than overwrite # it, and also keep the description of the parent # resource unmodified res_to_update['parameters'].update( resource_attrs['parameters']) else: res_to_update.update(resource_attrs) processed_exts[ext_name] = ext del exts_to_process[ext_name] if len(processed_exts) == processed_ext_count: # if we hit here, it means there are unsatisfied # dependencies. try again without optionals since optionals # are only necessary to set order if they are present. if check_optionals: check_optionals = False continue # Exit loop as no progress was made break if exts_to_process: unloadable_extensions = set(exts_to_process.keys()) LOG.error("Unable to process extensions (%s) because " "the configured plugins do not satisfy " "their requirements. Some features will not " "work as expected.", ', '.join(unloadable_extensions)) self._check_faulty_extensions(unloadable_extensions) # Extending extensions' attributes map. for ext in processed_exts.values(): ext.update_attributes_map(attr_map) def _is_sub_resource(self, resource): return ('parent' in resource and isinstance(resource['parent'], dict) and 'member_name' in resource['parent'] and 'parameters' in resource) def _check_faulty_extensions(self, faulty_extensions): """Raise for non-default faulty extensions. Gracefully fail for defective default extensions, which will be removed from the list of loaded extensions. """ default_extensions = set(const.DEFAULT_SERVICE_PLUGINS.values()) if not faulty_extensions <= default_extensions: raise exceptions.ExtensionsNotFound( extensions=list(faulty_extensions)) else: # Remove the faulty extensions so that they do not show during # ext-list for ext in faulty_extensions: try: del self.extensions[ext] except KeyError: pass def _check_extension(self, extension): """Checks for required methods in extension objects.""" try: LOG.debug('Ext name="%(name)s" alias="%(alias)s" ' 'description="%(desc)s" updated="%(updated)s"', {'name': extension.get_name(), 'alias': extension.get_alias(), 'desc': extension.get_description(), 'updated': extension.get_updated()}) except AttributeError: LOG.exception("Exception loading extension") return False return isinstance(extension, api_extensions.ExtensionDescriptor) def _load_all_extensions(self): """Load extensions from the configured path. The extension name is constructed from the module_name. If your extension module is named widgets.py, the extension class within that module should be 'Widgets'. See tests/unit/extensions/foxinsocks.py for an example extension implementation. """ for path in self.path.split(':'): if os.path.exists(path): self._load_all_extensions_from_path(path) else: LOG.error("Extension path '%s' doesn't exist!", path) def _load_all_extensions_from_path(self, path): # Sorting the extension list makes the order in which they # are loaded predictable across a cluster of load-balanced # Neutron Servers for f in sorted(os.listdir(path)): try: LOG.debug('Loading extension file: %s', f) mod_name, file_ext = os.path.splitext(os.path.split(f)[-1]) ext_path = os.path.join(path, f) if file_ext.lower() == '.py' and not mod_name.startswith('_'): mod = imp.load_source(mod_name, ext_path) ext_name = mod_name.capitalize() new_ext_class = getattr(mod, ext_name, None) if not new_ext_class: LOG.warning('Did not find expected name ' '"%(ext_name)s" in %(file)s', {'ext_name': ext_name, 'file': ext_path}) continue new_ext = new_ext_class() self.add_extension(new_ext) except Exception as exception: LOG.warning("Extension file %(f)s wasn't loaded due to " "%(exception)s", {'f': f, 'exception': exception}) def add_extension(self, ext): # Do nothing if the extension doesn't check out if not self._check_extension(ext): return alias = ext.get_alias() LOG.info('Loaded extension: %s', alias) if alias in self.extensions: raise exceptions.DuplicatedExtension(alias=alias) self.extensions[alias] = ext class PluginAwareExtensionManager(ExtensionManager): _instance = None def __init__(self, path, plugins): self.plugins = plugins super(PluginAwareExtensionManager, self).__init__(path) self.check_if_plugin_extensions_loaded() def _check_extension(self, extension): """Check if an extension is supported by any plugin.""" extension_is_valid = super(PluginAwareExtensionManager, self)._check_extension(extension) if not extension_is_valid: return False alias = extension.get_alias() if alias in EXTENSION_SUPPORTED_CHECK_MAP: return EXTENSION_SUPPORTED_CHECK_MAP[alias]() return (self._plugins_support(extension) and self._plugins_implement_interface(extension)) def _plugins_support(self, extension): alias = extension.get_alias() supports_extension = alias in self.get_supported_extension_aliases() if not supports_extension: LOG.info("Extension %s not supported by any of loaded " "plugins", alias) return supports_extension def _plugins_implement_interface(self, extension): if extension.get_plugin_interface() is None: return True for plugin in self.plugins.values(): if isinstance(plugin, extension.get_plugin_interface()): return True LOG.warning("Loaded plugins do not implement extension " "%s interface", extension.get_alias()) return False @classmethod def get_instance(cls): if cls._instance is None: service_plugins = directory.get_plugins() cls._instance = cls(get_extensions_path(service_plugins), service_plugins) return cls._instance def get_plugin_supported_extension_aliases(self, plugin): """Return extension aliases supported by a given plugin""" aliases = set() # we also check all classes that the plugins inherit to see if they # directly provide support for an extension for item in [plugin] + plugin.__class__.mro(): try: aliases |= set( getattr(item, "supported_extension_aliases", [])) except TypeError: # we land here if a class has a @property decorator for # supported extension aliases. They only work on objects. pass return aliases def get_supported_extension_aliases(self): """Gets extension aliases supported by all plugins.""" aliases = set() for plugin in self.plugins.values(): aliases |= self.get_plugin_supported_extension_aliases(plugin) aliases |= { alias for alias, func in EXTENSION_SUPPORTED_CHECK_MAP.items() if func() } return aliases @classmethod def clear_instance(cls): cls._instance = None def check_if_plugin_extensions_loaded(self): """Check if an extension supported by a plugin has been loaded.""" plugin_extensions = self.get_supported_extension_aliases() missing_aliases = plugin_extensions - set(self.extensions) missing_aliases -= _PLUGIN_AGNOSTIC_EXTENSIONS if missing_aliases: raise exceptions.ExtensionsNotFound( extensions=list(missing_aliases)) class RequestExtension(object): """Extend requests and responses of core Neutron OpenStack API controllers. Provide a way to add data to responses and handle custom request data that is sent to core Neutron OpenStack API controllers. """ def __init__(self, method, url_route, handler): self.url_route = url_route self.handler = handler self.conditions = dict(method=[method]) self.key = "%s-%s" % (method, url_route) class ActionExtension(object): """Add custom actions to core Neutron OpenStack API controllers.""" def __init__(self, collection, action_name, handler): self.collection = collection self.action_name = action_name self.handler = handler class ResourceExtension(object): """Add top level resources to the OpenStack API in Neutron.""" def __init__(self, collection, controller, parent=None, path_prefix="", collection_actions=None, member_actions=None, attr_map=None, collection_methods=None): collection_actions = collection_actions or {} collection_methods = collection_methods or {} member_actions = member_actions or {} attr_map = attr_map or {} self.collection = collection self.controller = controller self.parent = parent self.collection_actions = collection_actions self.collection_methods = collection_methods self.member_actions = member_actions self.path_prefix = path_prefix self.attr_map = attr_map # Returns the extension paths from a config entry and the __path__ # of neutron.extensions def get_extensions_path(service_plugins=None): paths = collections.OrderedDict() # Add Neutron core extensions paths[core_extensions.__path__[0]] = 1 if service_plugins: # Add Neutron *-aas extensions for plugin in service_plugins.values(): neutron_mod = provider_configuration.NeutronModule( plugin.__module__.split('.')[0]) try: paths[neutron_mod.module().extensions.__path__[0]] = 1 except AttributeError: # Occurs normally if module has no extensions sub-module pass # Add external/other plugins extensions if cfg.CONF.api_extensions_path: for path in cfg.CONF.api_extensions_path.split(":"): paths[path] = 1 LOG.debug("get_extension_paths = %s", paths) # Re-build the extension string path = ':'.join(paths) return path def append_api_extensions_path(paths): paths = list(set([cfg.CONF.api_extensions_path] + paths)) cfg.CONF.set_override('api_extensions_path', ':'.join([p for p in paths if p])) neutron-12.1.1/neutron/api/__init__.py0000664000175000017500000000000013553660046017662 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/v2/0000775000175000017500000000000013553660156016114 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/v2/base.py0000664000175000017500000010373413553660047017407 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import copy from neutron_lib.api import attributes from neutron_lib.api import faults from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import exceptions from oslo_log import log as logging from oslo_policy import policy as oslo_policy from oslo_utils import excutils import webob.exc from neutron._i18n import _ from neutron.api import api_common from neutron.api.v2 import resource as wsgi_resource from neutron.common import constants as n_const from neutron.common import exceptions as n_exc from neutron.common import rpc as n_rpc from neutron.db import api as db_api from neutron import policy from neutron import quota from neutron.quota import resource_registry LOG = logging.getLogger(__name__) class Controller(object): LIST = 'list' SHOW = 'show' CREATE = 'create' UPDATE = 'update' DELETE = 'delete' @property def plugin(self): return self._plugin @property def resource(self): return self._resource @property def attr_info(self): return self._attr_info @property def member_actions(self): return self._member_actions @property def allow_pagination(self): return self._allow_pagination @property def allow_sorting(self): return self._allow_sorting def _init_policy_attrs(self): """Create the list of attributes required by policy. If the attribute map contains a tenant_id policy, then include project_id to bring the resource into the brave new world. :return: sorted list of attributes required by policy """ policy_attrs = {name for (name, info) in self._attr_info.items() if info.get('required_by_policy')} if 'tenant_id' in policy_attrs: policy_attrs.add('project_id') # Could use list(), but sorted() makes testing easier. return sorted(policy_attrs) def __init__(self, plugin, collection, resource, attr_info, allow_bulk=False, member_actions=None, parent=None, allow_pagination=False, allow_sorting=False): if member_actions is None: member_actions = [] self._plugin = plugin self._collection = collection.replace('-', '_') self._resource = resource.replace('-', '_') self._attr_info = attr_info self._allow_bulk = allow_bulk self._allow_pagination = allow_pagination self._allow_sorting = allow_sorting self._native_bulk = self._is_native_bulk_supported() self._native_pagination = self._is_native_pagination_supported() self._native_sorting = self._is_native_sorting_supported() self._policy_attrs = self._init_policy_attrs() self._notifier = n_rpc.get_notifier('network') self._member_actions = member_actions self._primary_key = self._get_primary_key() if self._allow_pagination and self._native_pagination: # Native pagination need native sorting support if not self._native_sorting: raise exceptions.Invalid( _("Native pagination depend on native sorting") ) if not self._allow_sorting: LOG.info("Allow sorting is enabled because native " "pagination requires native sorting") self._allow_sorting = True self.parent = parent if parent: self._parent_id_name = '%s_id' % parent['member_name'] parent_part = '_%s' % parent['member_name'] else: self._parent_id_name = None parent_part = '' self._plugin_handlers = { self.LIST: 'get%s_%s' % (parent_part, self._collection), self.SHOW: 'get%s_%s' % (parent_part, self._resource) } for action in [self.CREATE, self.UPDATE, self.DELETE]: self._plugin_handlers[action] = '%s%s_%s' % (action, parent_part, self._resource) def _get_primary_key(self, default_primary_key='id'): for key, value in self._attr_info.items(): if value.get('primary_key', False): return key return default_primary_key def _is_native_bulk_supported(self): native_bulk_attr_name = ("_%s__native_bulk_support" % self._plugin.__class__.__name__) return getattr(self._plugin, native_bulk_attr_name, False) def _is_native_pagination_supported(self): return api_common.is_native_pagination_supported(self._plugin) def _is_native_sorting_supported(self): return api_common.is_native_sorting_supported(self._plugin) def _exclude_attributes_by_policy(self, context, data): """Identifies attributes to exclude according to authZ policies. Return a list of attribute names which should be stripped from the response returned to the user because the user is not authorized to see them. """ attributes_to_exclude = [] for attr_name in data.keys(): # TODO(amotoki): At now, all attribute maps have tenant_id and # determine excluded attributes based on tenant_id. # We need to migrate tenant_id to project_id later # as attr_info is referred to in various places and we need # to check all logis carefully. if attr_name == 'project_id': continue attr_data = self._attr_info.get(attr_name) if attr_data and attr_data['is_visible']: if policy.check( context, '%s:%s' % (self._plugin_handlers[self.SHOW], attr_name), data, might_not_exist=True, pluralized=self._collection): # this attribute is visible, check next one continue # if the code reaches this point then either the policy check # failed or the attribute was not visible in the first place attributes_to_exclude.append(attr_name) # TODO(amotoki): As mentioned in the above TODO, # we treat project_id and tenant_id equivalently. # This should be migrated to project_id in Ocata. if attr_name == 'tenant_id': attributes_to_exclude.append('project_id') return attributes_to_exclude def _view(self, context, data, fields_to_strip=None): """Build a view of an API resource. :param context: the neutron context :param data: the object for which a view is being created :param fields_to_strip: attributes to remove from the view :returns: a view of the object which includes only attributes visible according to API resource declaration and authZ policies. """ fields_to_strip = ((fields_to_strip or []) + self._exclude_attributes_by_policy(context, data)) return self._filter_attributes(data, fields_to_strip) def _filter_attributes(self, data, fields_to_strip=None): if not fields_to_strip: return data return dict(item for item in data.items() if (item[0] not in fields_to_strip)) def _do_field_list(self, original_fields): fields_to_add = None # don't do anything if fields were not specified in the request if original_fields: fields_to_add = [attr for attr in self._policy_attrs if attr not in original_fields] original_fields.extend(self._policy_attrs) return original_fields, fields_to_add def __getattr__(self, name): if name in self._member_actions: @db_api.retry_db_errors def _handle_action(request, id, **kwargs): arg_list = [request.context, id] # Ensure policy engine is initialized policy.init() # Fetch the resource and verify if the user can access it try: parent_id = kwargs.get(self._parent_id_name) resource = self._item(request, id, do_authz=True, field_list=None, parent_id=parent_id) except oslo_policy.PolicyNotAuthorized: msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) body = kwargs.pop('body', None) # Explicit comparison with None to distinguish from {} if body is not None: arg_list.append(body) # It is ok to raise a 403 because accessibility to the # object was checked earlier in this method policy.enforce(request.context, name, resource, pluralized=self._collection) ret_value = getattr(self._plugin, name)(*arg_list, **kwargs) # It is simply impossible to predict whether one of this # actions alters resource usage. For instance a tenant port # is created when a router interface is added. Therefore it is # important to mark as dirty resources whose counters have # been altered by this operation resource_registry.set_resources_dirty(request.context) return ret_value return _handle_action else: raise AttributeError() def _get_pagination_helper(self, request): if self._allow_pagination and self._native_pagination: return api_common.PaginationNativeHelper(request, self._primary_key) elif self._allow_pagination: return api_common.PaginationEmulatedHelper(request, self._primary_key) return api_common.NoPaginationHelper(request, self._primary_key) def _get_sorting_helper(self, request): if self._allow_sorting and self._native_sorting: return api_common.SortingNativeHelper(request, self._attr_info) elif self._allow_sorting: return api_common.SortingEmulatedHelper(request, self._attr_info) return api_common.NoSortingHelper(request, self._attr_info) def _items(self, request, do_authz=False, parent_id=None): """Retrieves and formats a list of elements of the requested entity.""" # NOTE(salvatore-orlando): The following ensures that fields which # are needed for authZ policy validation are not stripped away by the # plugin before returning. original_fields, fields_to_add = self._do_field_list( api_common.list_args(request, 'fields')) filters = api_common.get_filters(request, self._attr_info, ['fields', 'sort_key', 'sort_dir', 'limit', 'marker', 'page_reverse']) kwargs = {'filters': filters, 'fields': original_fields} sorting_helper = self._get_sorting_helper(request) pagination_helper = self._get_pagination_helper(request) sorting_helper.update_args(kwargs) sorting_helper.update_fields(original_fields, fields_to_add) pagination_helper.update_args(kwargs) pagination_helper.update_fields(original_fields, fields_to_add) if parent_id: kwargs[self._parent_id_name] = parent_id obj_getter = getattr(self._plugin, self._plugin_handlers[self.LIST]) obj_list = obj_getter(request.context, **kwargs) obj_list = sorting_helper.sort(obj_list) obj_list = pagination_helper.paginate(obj_list) # Check authz if do_authz: # FIXME(salvatore-orlando): obj_getter might return references to # other resources. Must check authZ on them too. # Omit items from list that should not be visible obj_list = [obj for obj in obj_list if policy.check(request.context, self._plugin_handlers[self.SHOW], obj, plugin=self._plugin, pluralized=self._collection)] # Use the first element in the list for discriminating which attributes # should be filtered out because of authZ policies # fields_to_add contains a list of attributes added for request policy # checks but that were not required by the user. They should be # therefore stripped fields_to_strip = fields_to_add or [] if obj_list: fields_to_strip += self._exclude_attributes_by_policy( request.context, obj_list[0]) collection = {self._collection: [self._filter_attributes(obj, fields_to_strip=fields_to_strip) for obj in obj_list]} pagination_links = pagination_helper.get_links(obj_list) if pagination_links: collection[self._collection + "_links"] = pagination_links # Synchronize usage trackers, if needed resource_registry.resync_resource( request.context, self._resource, request.context.tenant_id) return collection def _item(self, request, id, do_authz=False, field_list=None, parent_id=None): """Retrieves and formats a single element of the requested entity.""" kwargs = {'fields': field_list} action = self._plugin_handlers[self.SHOW] if parent_id: kwargs[self._parent_id_name] = parent_id obj_getter = getattr(self._plugin, action) obj = obj_getter(request.context, id, **kwargs) # Check authz # FIXME(salvatore-orlando): obj_getter might return references to # other resources. Must check authZ on them too. if do_authz: policy.enforce(request.context, action, obj, pluralized=self._collection) return obj @db_api.retry_db_errors def index(self, request, **kwargs): """Returns a list of the requested entity.""" parent_id = kwargs.get(self._parent_id_name) # Ensure policy engine is initialized policy.init() return self._items(request, True, parent_id) @db_api.retry_db_errors def show(self, request, id, **kwargs): """Returns detailed information about the requested entity.""" try: # NOTE(salvatore-orlando): The following ensures that fields # which are needed for authZ policy validation are not stripped # away by the plugin before returning. field_list, added_fields = self._do_field_list( api_common.list_args(request, "fields")) parent_id = kwargs.get(self._parent_id_name) # Ensure policy engine is initialized policy.init() return {self._resource: self._view(request.context, self._item(request, id, do_authz=True, field_list=field_list, parent_id=parent_id), fields_to_strip=added_fields)} except oslo_policy.PolicyNotAuthorized: # To avoid giving away information, pretend that it # doesn't exist msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) def _emulate_bulk_create(self, obj_creator, request, body, parent_id=None): objs = [] try: for item in body[self._collection]: kwargs = {self._resource: item} if parent_id: kwargs[self._parent_id_name] = parent_id fields_to_strip = self._exclude_attributes_by_policy( request.context, item) objs.append(self._filter_attributes( obj_creator(request.context, **kwargs), fields_to_strip=fields_to_strip)) return objs # Note(salvatore-orlando): broad catch as in theory a plugin # could raise any kind of exception except Exception: with excutils.save_and_reraise_exception(): for obj in objs: obj_deleter = getattr(self._plugin, self._plugin_handlers[self.DELETE]) try: kwargs = ({self._parent_id_name: parent_id} if parent_id else {}) obj_deleter(request.context, obj['id'], **kwargs) except Exception: # broad catch as our only purpose is to log the # exception LOG.exception("Unable to undo add for " "%(resource)s %(id)s", {'resource': self._resource, 'id': obj['id']}) # TODO(salvatore-orlando): The object being processed when the # plugin raised might have been created or not in the db. # We need a way for ensuring that if it has been created, # it is then deleted def create(self, request, body=None, **kwargs): self._notifier.info(request.context, self._resource + '.create.start', body) return self._create(request, body, **kwargs) @db_api.retry_db_errors def _create(self, request, body, **kwargs): """Creates a new instance of the requested entity.""" parent_id = kwargs.get(self._parent_id_name) body = Controller.prepare_request_body(request.context, body, True, self._resource, self._attr_info, allow_bulk=self._allow_bulk) action = self._plugin_handlers[self.CREATE] # Check authz if self._collection in body: # Have to account for bulk create items = body[self._collection] else: items = [body] # Ensure policy engine is initialized policy.init() # Store requested resource amounts grouping them by tenant # This won't work with multiple resources. However because of the # current structure of this controller there will hardly be more than # one resource for which reservations are being made request_deltas = collections.defaultdict(int) for item in items: self._validate_network_tenant_ownership(request, item[self._resource]) policy.enforce(request.context, action, item[self._resource], pluralized=self._collection) if 'tenant_id' not in item[self._resource]: # no tenant_id - no quota check continue tenant_id = item[self._resource]['tenant_id'] request_deltas[tenant_id] += 1 # Quota enforcement reservations = [] try: for (tenant, delta) in request_deltas.items(): reservation = quota.QUOTAS.make_reservation( request.context, tenant, {self._resource: delta}, self._plugin) reservations.append(reservation) except n_exc.QuotaResourceUnknown as e: # We don't want to quota this resource LOG.debug(e) def notify(create_result): # Ensure usage trackers for all resources affected by this API # operation are marked as dirty with db_api.context_manager.writer.using(request.context): # Commit the reservation(s) for reservation in reservations: quota.QUOTAS.commit_reservation( request.context, reservation.reservation_id) resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + '.create.end' self._notifier.info(request.context, notifier_method, create_result) registry.publish(self._resource, events.BEFORE_RESPONSE, self, payload=events.APIEventPayload( request.context, notifier_method, action, request_body=body, states=({}, create_result,), collection_name=self._collection)) return create_result def do_create(body, bulk=False, emulated=False): kwargs = {self._parent_id_name: parent_id} if parent_id else {} if bulk and not emulated: obj_creator = getattr(self._plugin, "%s_bulk" % action) else: obj_creator = getattr(self._plugin, action) try: if emulated: return self._emulate_bulk_create(obj_creator, request, body, parent_id) else: if self._collection in body: # This is weird but fixing it requires changes to the # plugin interface kwargs.update({self._collection: body}) else: kwargs.update({self._resource: body}) return obj_creator(request.context, **kwargs) except Exception: # In case of failure the plugin will always raise an # exception. Cancel the reservation with excutils.save_and_reraise_exception(): for reservation in reservations: quota.QUOTAS.cancel_reservation( request.context, reservation.reservation_id) if self._collection in body and self._native_bulk: # plugin does atomic bulk create operations objs = do_create(body, bulk=True) # Use first element of list to discriminate attributes which # should be removed because of authZ policies fields_to_strip = self._exclude_attributes_by_policy( request.context, objs[0]) return notify({self._collection: [self._filter_attributes( obj, fields_to_strip=fields_to_strip) for obj in objs]}) else: if self._collection in body: # Emulate atomic bulk behavior objs = do_create(body, bulk=True, emulated=True) return notify({self._collection: objs}) else: obj = do_create(body) return notify({self._resource: self._view(request.context, obj)}) def delete(self, request, id, **kwargs): """Deletes the specified entity.""" if request.body: msg = _('Request body is not supported in DELETE.') raise webob.exc.HTTPBadRequest(msg) self._notifier.info(request.context, self._resource + '.delete.start', {self._resource + '_id': id}) return self._delete(request, id, **kwargs) @db_api.retry_db_errors def _delete(self, request, id, **kwargs): action = self._plugin_handlers[self.DELETE] # Check authz policy.init() parent_id = kwargs.get(self._parent_id_name) obj = self._item(request, id, parent_id=parent_id) try: policy.enforce(request.context, action, obj, pluralized=self._collection) except oslo_policy.PolicyNotAuthorized: # To avoid giving away information, pretend that it # doesn't exist if policy does not authorize SHOW with excutils.save_and_reraise_exception() as ctxt: if not policy.check(request.context, self._plugin_handlers[self.SHOW], obj, pluralized=self._collection): ctxt.reraise = False msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) obj_deleter = getattr(self._plugin, action) obj_deleter(request.context, id, **kwargs) # A delete operation usually alters resource usage, so mark affected # usage trackers as dirty resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + '.delete.end' result = {self._resource: self._view(request.context, obj)} notifier_payload = {self._resource + '_id': id} notifier_payload.update(result) self._notifier.info(request.context, notifier_method, notifier_payload) registry.publish(self._resource, events.BEFORE_RESPONSE, self, payload=events.APIEventPayload( request.context, notifier_method, action, states=({}, obj, result,), collection_name=self._collection)) def update(self, request, id, body=None, **kwargs): """Updates the specified entity's attributes.""" try: payload = body.copy() except AttributeError: msg = _("Invalid format: %s") % request.body raise exceptions.BadRequest(resource='body', msg=msg) payload['id'] = id self._notifier.info(request.context, self._resource + '.update.start', payload) return self._update(request, id, body, **kwargs) @db_api.retry_db_errors def _update(self, request, id, body, **kwargs): body = Controller.prepare_request_body(request.context, body, False, self._resource, self._attr_info, allow_bulk=self._allow_bulk) action = self._plugin_handlers[self.UPDATE] # Load object to check authz # but pass only attributes in the original body and required # by the policy engine to the policy 'brain' field_list = [name for (name, value) in self._attr_info.items() if (value.get('required_by_policy') or value.get('primary_key') or 'default' not in value)] # Ensure policy engine is initialized policy.init() parent_id = kwargs.get(self._parent_id_name) orig_obj = self._item(request, id, field_list=field_list, parent_id=parent_id) orig_object_copy = copy.copy(orig_obj) orig_obj.update(body[self._resource]) # Make a list of attributes to be updated to inform the policy engine # which attributes are set explicitly so that it can distinguish them # from the ones that are set to their default values. orig_obj[n_const.ATTRIBUTES_TO_UPDATE] = body[self._resource].keys() try: policy.enforce(request.context, action, orig_obj, pluralized=self._collection) except oslo_policy.PolicyNotAuthorized: # To avoid giving away information, pretend that it # doesn't exist if policy does not authorize SHOW with excutils.save_and_reraise_exception() as ctxt: if not policy.check(request.context, self._plugin_handlers[self.SHOW], orig_obj, pluralized=self._collection): ctxt.reraise = False msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) obj_updater = getattr(self._plugin, action) kwargs = {self._resource: body} if parent_id: kwargs[self._parent_id_name] = parent_id obj = obj_updater(request.context, id, **kwargs) # Usually an update operation does not alter resource usage, but as # there might be side effects it might be worth checking for changes # in resource usage here as well (e.g: a tenant port is created when a # router interface is added) resource_registry.set_resources_dirty(request.context) result = {self._resource: self._view(request.context, obj)} notifier_method = self._resource + '.update.end' self._notifier.info(request.context, notifier_method, result) registry.publish(self._resource, events.BEFORE_RESPONSE, self, payload=events.APIEventPayload( request.context, notifier_method, action, request_body=body, states=(orig_object_copy, result,), collection_name=self._collection)) return result @staticmethod def prepare_request_body(context, body, is_create, resource, attr_info, allow_bulk=False): """Verifies required attributes are in request body. Also checking that an attribute is only specified if it is allowed for the given operation (create/update). Attribute with default values are considered to be optional. body argument must be the deserialized body. """ collection = resource + "s" if not body: raise webob.exc.HTTPBadRequest(_("Resource body required")) LOG.debug("Request body: %(body)s", {'body': body}) try: if collection in body: if not allow_bulk: raise webob.exc.HTTPBadRequest(_("Bulk operation " "not supported")) if not body[collection]: raise webob.exc.HTTPBadRequest(_("Resources required")) bulk_body = [ Controller.prepare_request_body( context, item if resource in item else {resource: item}, is_create, resource, attr_info, allow_bulk) for item in body[collection] ] return {collection: bulk_body} res_dict = body.get(resource) except (AttributeError, TypeError): msg = _("Body contains invalid data") raise webob.exc.HTTPBadRequest(msg) if res_dict is None: msg = _("Unable to find '%s' in request body") % resource raise webob.exc.HTTPBadRequest(msg) attr_ops = attributes.AttributeInfo(attr_info) attr_ops.populate_project_id(context, res_dict, is_create) attributes.populate_project_info(attr_info) attr_ops.verify_attributes(res_dict) if is_create: # POST attr_ops.fill_post_defaults( res_dict, exc_cls=webob.exc.HTTPBadRequest) else: # PUT for attr, attr_vals in attr_info.items(): if attr in res_dict and not attr_vals['allow_put']: msg = _("Cannot update read-only attribute %s") % attr raise webob.exc.HTTPBadRequest(msg) attr_ops.convert_values(res_dict, exc_cls=webob.exc.HTTPBadRequest) return body def _validate_network_tenant_ownership(self, request, resource_item): # TODO(salvatore-orlando): consider whether this check can be folded # in the policy engine if (request.context.is_admin or request.context.is_advsvc or self._resource not in ('port', 'subnet')): return network = self._plugin.get_network( request.context, resource_item['network_id']) # do not perform the check on shared networks if network.get('shared'): return network_owner = network['tenant_id'] if network_owner != resource_item['tenant_id']: # NOTE(kevinbenton): we raise a 404 to hide the existence of the # network from the tenant since they don't have access to it. msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) def create_resource(collection, resource, plugin, params, allow_bulk=False, member_actions=None, parent=None, allow_pagination=False, allow_sorting=False): controller = Controller(plugin, collection, resource, params, allow_bulk, member_actions=member_actions, parent=parent, allow_pagination=allow_pagination, allow_sorting=allow_sorting) return wsgi_resource.Resource(controller, faults.FAULT_MAP) neutron-12.1.1/neutron/api/v2/__init__.py0000664000175000017500000000000013553660046020211 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/api/v2/resource_helper.py0000664000175000017500000001026313553660046021654 0ustar zuulzuul00000000000000# (c) Copyright 2014 Cisco Systems Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins import constants from neutron_lib.plugins import directory from oslo_log import log as logging from neutron.api import extensions from neutron.api.v2 import base from neutron.quota import resource_registry LOG = logging.getLogger(__name__) def build_plural_mappings(special_mappings, resource_map): """Create plural to singular mapping for all resources. Allows for special mappings to be provided, for particular cases.. Otherwise, will strip off the last character for normal mappings, like routers -> router, unless the plural name ends with 'ies', in which case the singular form will end with a 'y' (e.g.: policy/policies) """ plural_mappings = {} for plural in resource_map: singular = special_mappings.get(plural) if not singular: if plural.endswith('ies'): singular = "%sy" % plural[:-3] else: singular = plural[:-1] plural_mappings[plural] = singular return plural_mappings def build_resource_info(plural_mappings, resource_map, which_service, action_map=None, register_quota=False, translate_name=False, allow_bulk=False): """Build resources for advanced services. Takes the resource information, and singular/plural mappings, and creates API resource objects for advanced services extensions. Will optionally translate underscores to dashes in resource names, register the resource, and accept action information for resources. :param plural_mappings: mappings between singular and plural forms :param resource_map: attribute map for the WSGI resources to create :param which_service: The name of the service for which the WSGI resources are being created. This name will be used to pass the appropriate plugin to the WSGI resource. It can be set to None or "CORE" to create WSGI resources for the core plugin :param action_map: custom resource actions :param register_quota: it can be set to True to register quotas for the resource(s) being created :param translate_name: replaces underscores with dashes :param allow_bulk: True if bulk create are allowed """ resources = [] if not which_service: which_service = constants.CORE if action_map is None: action_map = {} plugin = directory.get_plugin(which_service) path_prefix = getattr(plugin, "path_prefix", "") LOG.debug('Service %(service)s assigned prefix: %(prefix)s', {'service': which_service, 'prefix': path_prefix}) for collection_name in resource_map: resource_name = plural_mappings[collection_name] params = resource_map.get(collection_name, {}) if translate_name: collection_name = collection_name.replace('_', '-') if register_quota: resource_registry.register_resource_by_name(resource_name) member_actions = action_map.get(resource_name, {}) controller = base.create_resource( collection_name, resource_name, plugin, params, member_actions=member_actions, allow_bulk=allow_bulk, allow_pagination=True, allow_sorting=True) resource = extensions.ResourceExtension( collection_name, controller, path_prefix=path_prefix, member_actions=member_actions, attr_map=params) resources.append(resource) return resources neutron-12.1.1/neutron/api/v2/attributes.py0000664000175000017500000000334113553660047020654 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import attributes as attrs from neutron_lib.api.definitions import network as net_def from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import subnet as subnet_def from neutron_lib.api.definitions import subnetpool as subnetpool_def # Defining a constant to avoid repeating string literal in several modules SHARED = 'shared' # Define constants for base resource name CORE_RESOURCES = {net_def.RESOURCE_NAME: net_def.COLLECTION_NAME, subnet_def.RESOURCE_NAME: subnet_def.COLLECTION_NAME, subnetpool_def.RESOURCE_NAME: subnetpool_def.COLLECTION_NAME, port_def.RESOURCE_NAME: port_def.COLLECTION_NAME} RESOURCE_ATTRIBUTE_MAP = attrs.RESOURCES # Identify the attribute used by a resource to reference another resource RESOURCE_FOREIGN_KEYS = { net_def.COLLECTION_NAME: 'network_id' } def get_collection_info(collection): """Helper function to retrieve attribute info. :param collection: Collection or plural name of the resource """ return RESOURCE_ATTRIBUTE_MAP.get(collection) neutron-12.1.1/neutron/api/v2/router.py0000664000175000017500000000155713553660046020014 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron.pecan_wsgi import app as pecan_app def APIRouter(**local_config): return pecan_app.v2_factory(None, **local_config) def _factory(global_config, **local_config): return pecan_app.v2_factory(global_config, **local_config) setattr(APIRouter, 'factory', _factory) neutron-12.1.1/neutron/api/v2/resource.py0000664000175000017500000001244113553660046020315 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Utility methods for working with WSGI servers redux """ from oslo_log import log as logging import webob.dec import webob.exc from neutron.api import api_common from neutron.common import utils from neutron import wsgi LOG = logging.getLogger(__name__) class Request(wsgi.Request): pass def Resource(controller, faults=None, deserializers=None, serializers=None, action_status=None): """Represents an API entity resource and the associated serialization and deserialization logic """ default_deserializers = {'application/json': wsgi.JSONDeserializer()} default_serializers = {'application/json': wsgi.JSONDictSerializer()} format_types = {'json': 'application/json'} action_status = action_status or dict(create=201, delete=204) default_deserializers.update(deserializers or {}) default_serializers.update(serializers or {}) deserializers = default_deserializers serializers = default_serializers faults = faults or {} @webob.dec.wsgify(RequestClass=Request) def resource(request): route_args = request.environ.get('wsgiorg.routing_args') if route_args: args = route_args[1].copy() else: args = {} # NOTE(jkoelker) by now the controller is already found, remove # it from the args if it is in the matchdict args.pop('controller', None) fmt = args.pop('format', None) action = args.pop('action', None) content_type = format_types.get(fmt, request.best_match_content_type()) language = request.best_match_language() deserializer = deserializers.get(content_type) serializer = serializers.get(content_type) try: if request.body: args['body'] = deserializer.deserialize(request.body)['body'] # Routes library is dumb and cuts off everything after last dot (.) # as format. At the same time, it doesn't enforce format suffix, # which combined makes it impossible to pass a 'id' with dots # included (the last section after the last dot is lost). This is # important for some API extensions like tags where the id is # really a tag name that can contain special characters. # # To work around the Routes behaviour, we will attach the suffix # back to id if it's not one of supported formats (atm json only). # This of course won't work for the corner case of a tag name that # actually ends with '.json', but there seems to be no better way # to tackle it without breaking API backwards compatibility. if fmt is not None and fmt not in format_types: args['id'] = '.'.join([args['id'], fmt]) revision_number = api_common.check_request_for_revision_constraint( request) if revision_number is not None: request.context.set_transaction_constraint( controller._collection, args['id'], revision_number) method = getattr(controller, action) result = method(request=request, **args) except Exception as e: mapped_exc = api_common.convert_exception_to_http_exc(e, faults, language) if hasattr(mapped_exc, 'code') and 400 <= mapped_exc.code < 500: LOG.info('%(action)s failed (client error): %(exc)s', {'action': action, 'exc': mapped_exc}) else: LOG.exception('%(action)s failed: %(details)s', { 'action': action, 'details': utils.extract_exc_details(e), } ) raise mapped_exc status = action_status.get(action, 200) body = serializer.serialize(result) # NOTE(jkoelker) Comply with RFC2616 section 9.7 if status == 204: content_type = '' body = None return webob.Response(request=request, status=status, content_type=content_type, body=body) # NOTE(blogan): this is something that is needed for the transition to # pecan. This will allow the pecan code to have a handle on the controller # for an extension so it can reuse the code instead of forcing every # extension to rewrite the code for use with pecan. setattr(resource, 'controller', controller) setattr(resource, 'action_status', action_status) return resource neutron-12.1.1/neutron/api/versions.py0000664000175000017500000000200113553660047017777 0ustar zuulzuul00000000000000# Copyright 2011 Citrix Systems. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from debtcollector import removals from neutron.pecan_wsgi import app as pecan_app class Versions(object): @removals.remove(version="Queens", removal_version="Rocky", message="Use neutron.pecan_wsgi.app:versions_factory") @classmethod def factory(cls, global_config, **local_config): return pecan_app.versions_factory(global_config, **local_config) neutron-12.1.1/neutron/api/api_common.py0000664000175000017500000004221113553660047020257 0ustar zuulzuul00000000000000# Copyright 2011 Citrix System. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools from neutron_lib.db import model_base from neutron_lib import exceptions from oslo_config import cfg import oslo_i18n from oslo_log import log as logging from oslo_serialization import jsonutils from six.moves.urllib import parse from webob import exc from neutron._i18n import _ from neutron.api import extensions from neutron.common import constants from neutron import wsgi LOG = logging.getLogger(__name__) def ensure_if_match_supported(): """Raises exception if 'if-match' revision matching unsupported.""" if 'revision-if-match' in (extensions.PluginAwareExtensionManager. get_instance().extensions): return msg = _("This server does not support constraining operations based on " "revision numbers") raise exceptions.BadRequest(resource='if-match', msg=msg) def check_request_for_revision_constraint(request): """Parses, verifies, and returns a constraint from a request.""" revision_number = None for e in getattr(request.if_match, 'etags', []): if e.startswith('revision_number='): if revision_number is not None: msg = _("Multiple revision_number etags are not supported.") raise exceptions.BadRequest(resource='if-match', msg=msg) ensure_if_match_supported() try: revision_number = int(e.split('revision_number=')[1]) except ValueError: msg = _("Revision number etag must be in the format of " "revision_number=") raise exceptions.BadRequest(resource='if-match', msg=msg) return revision_number def get_filters(request, attr_info, skips=None): return get_filters_from_dict(request.GET.dict_of_lists(), attr_info, skips) def get_filters_from_dict(data, attr_info, skips=None): """Extracts the filters from a dict of query parameters. Returns a dict of lists for the filters: check=a&check=b&name=Bob& becomes: {'check': [u'a', u'b'], 'name': [u'Bob']} """ skips = skips or [] res = {} for key, values in data.items(): if key in skips or hasattr(model_base.BASEV2, key): continue values = [v for v in values if v] key_attr_info = attr_info.get(key, {}) if 'convert_list_to' in key_attr_info: values = key_attr_info['convert_list_to'](values) elif 'convert_to' in key_attr_info: convert_to = key_attr_info['convert_to'] values = [convert_to(v) for v in values] if values: res[key] = values return res def get_previous_link(request, items, id_key): params = request.GET.copy() params.pop('marker', None) if items: marker = items[0][id_key] params['marker'] = marker params['page_reverse'] = True return "%s?%s" % (prepare_url(request.path_url), parse.urlencode(params)) def get_next_link(request, items, id_key): params = request.GET.copy() params.pop('marker', None) if items: marker = items[-1][id_key] params['marker'] = marker params.pop('page_reverse', None) return "%s?%s" % (prepare_url(request.path_url), parse.urlencode(params)) def prepare_url(orig_url): """Takes a link and swaps in network_link_prefix if set.""" prefix = cfg.CONF.network_link_prefix # Copied directly from nova/api/openstack/common.py if not prefix: return orig_url url_parts = list(parse.urlsplit(orig_url)) prefix_parts = list(parse.urlsplit(prefix)) url_parts[0:2] = prefix_parts[0:2] url_parts[2] = prefix_parts[2] + url_parts[2] return parse.urlunsplit(url_parts).rstrip('/') def get_limit_and_marker(request): """Return marker, limit tuple from request. :param request: `wsgi.Request` possibly containing 'marker' and 'limit' GET variables. 'marker' is the id of the last element the client has seen, and 'limit' is the maximum number of items to return. If limit == 0, it means we needn't pagination, then return None. """ max_limit = _get_pagination_max_limit() limit = _get_limit_param(request) if max_limit > 0: limit = min(max_limit, limit) or max_limit if not limit: return None, None marker = request.GET.get('marker', None) return limit, marker def _get_pagination_max_limit(): max_limit = -1 if (cfg.CONF.pagination_max_limit.lower() != constants.PAGINATION_INFINITE): try: max_limit = int(cfg.CONF.pagination_max_limit) if max_limit == 0: raise ValueError() except ValueError: LOG.warning("Invalid value for pagination_max_limit: %s. It " "should be an integer greater to 0", cfg.CONF.pagination_max_limit) return max_limit def _get_limit_param(request): """Extract integer limit from request or fail.""" limit = request.GET.get('limit', 0) try: limit = int(limit) if limit >= 0: return limit except ValueError: pass msg = _("Limit must be an integer 0 or greater and not '%s'") % limit raise exceptions.BadRequest(resource='limit', msg=msg) def list_args(request, arg): """Extracts the list of arg from request.""" return [v for v in request.GET.getall(arg) if v] def get_sorts(request, attr_info): """Extract sort_key and sort_dir from request. Return as: [(key1, value1), (key2, value2)] """ sort_keys = list_args(request, "sort_key") sort_dirs = list_args(request, "sort_dir") if len(sort_keys) != len(sort_dirs): msg = _("The number of sort_keys and sort_dirs must be same") raise exc.HTTPBadRequest(explanation=msg) valid_dirs = [constants.SORT_DIRECTION_ASC, constants.SORT_DIRECTION_DESC] absent_keys = [x for x in sort_keys if x not in attr_info] if absent_keys: msg = _("%s is invalid attribute for sort_keys") % absent_keys raise exc.HTTPBadRequest(explanation=msg) invalid_dirs = [x for x in sort_dirs if x not in valid_dirs] if invalid_dirs: msg = (_("%(invalid_dirs)s is invalid value for sort_dirs, " "valid value is '%(asc)s' and '%(desc)s'") % {'invalid_dirs': invalid_dirs, 'asc': constants.SORT_DIRECTION_ASC, 'desc': constants.SORT_DIRECTION_DESC}) raise exc.HTTPBadRequest(explanation=msg) return list(zip(sort_keys, [x == constants.SORT_DIRECTION_ASC for x in sort_dirs])) def get_page_reverse(request): data = request.GET.get('page_reverse', 'False') return data.lower() == "true" def get_pagination_links(request, items, limit, marker, page_reverse, key="id"): key = key if key else 'id' links = [] if not limit: return links if not (len(items) < limit and not page_reverse): links.append({"rel": "next", "href": get_next_link(request, items, key)}) if not (len(items) < limit and page_reverse): links.append({"rel": "previous", "href": get_previous_link(request, items, key)}) return links def is_native_pagination_supported(plugin): native_pagination_attr_name = ("_%s__native_pagination_support" % plugin.__class__.__name__) return getattr(plugin, native_pagination_attr_name, False) def is_native_sorting_supported(plugin): native_sorting_attr_name = ("_%s__native_sorting_support" % plugin.__class__.__name__) return getattr(plugin, native_sorting_attr_name, False) class PaginationHelper(object): def __init__(self, request, primary_key='id'): self.request = request self.primary_key = primary_key def update_fields(self, original_fields, fields_to_add): pass def update_args(self, args): pass def paginate(self, items): return items def get_links(self, items): return {} class PaginationEmulatedHelper(PaginationHelper): def __init__(self, request, primary_key='id'): super(PaginationEmulatedHelper, self).__init__(request, primary_key) self.limit, self.marker = get_limit_and_marker(request) self.page_reverse = get_page_reverse(request) def update_fields(self, original_fields, fields_to_add): if not original_fields: return if self.primary_key not in original_fields: original_fields.append(self.primary_key) fields_to_add.append(self.primary_key) def paginate(self, items): if not self.limit: return items if not items: return [] # first, calculate the base index for pagination if self.marker: i = 0 for item in items: if item[self.primary_key] == self.marker: break i += 1 else: # if marker is not found, return nothing return [] else: i = len(items) if self.page_reverse else 0 if self.page_reverse: # don't wrap return items[max(i - self.limit, 0):i] else: if self.marker: # skip the matched marker i += 1 return items[i:i + self.limit] def get_links(self, items): return get_pagination_links( self.request, items, self.limit, self.marker, self.page_reverse, self.primary_key) class PaginationNativeHelper(PaginationEmulatedHelper): def update_args(self, args): if self.primary_key not in dict(args.get('sorts', [])).keys(): args.setdefault('sorts', []).append((self.primary_key, True)) args.update({'limit': self.limit, 'marker': self.marker, 'page_reverse': self.page_reverse}) def paginate(self, items): return items class NoPaginationHelper(PaginationHelper): pass class SortingHelper(object): def __init__(self, request, attr_info): pass def update_args(self, args): pass def update_fields(self, original_fields, fields_to_add): pass def sort(self, items): return items class SortingEmulatedHelper(SortingHelper): def __init__(self, request, attr_info): super(SortingEmulatedHelper, self).__init__(request, attr_info) self.sort_dict = get_sorts(request, attr_info) def update_fields(self, original_fields, fields_to_add): if not original_fields: return for key in dict(self.sort_dict).keys(): if key not in original_fields: original_fields.append(key) fields_to_add.append(key) def sort(self, items): def cmp_func(obj1, obj2): for key, direction in self.sort_dict: o1 = obj1[key] o2 = obj2[key] if o1 is None and o2 is None: ret = 0 elif o1 is None and o2 is not None: ret = -1 elif o1 is not None and o2 is None: ret = 1 else: ret = (o1 > o2) - (o1 < o2) if ret: return ret * (1 if direction else -1) return 0 return sorted(items, key=functools.cmp_to_key(cmp_func)) class SortingNativeHelper(SortingHelper): def __init__(self, request, attr_info): self.sort_dict = get_sorts(request, attr_info) def update_args(self, args): args['sorts'] = self.sort_dict class NoSortingHelper(SortingHelper): pass def convert_exception_to_http_exc(e, faults, language): serializer = wsgi.JSONDictSerializer() if isinstance(e, exceptions.MultipleExceptions): converted_exceptions = [ convert_exception_to_http_exc(inner, faults, language) for inner in e.inner_exceptions] # if no internal exceptions, will be handled as single exception if converted_exceptions: codes = {c.code for c in converted_exceptions} if len(codes) == 1: # all error codes are the same so we can maintain the code # and just concatenate the bodies joined_msg = "\n".join( (jsonutils.loads(c.body)['NeutronError']['message'] for c in converted_exceptions)) new_body = jsonutils.loads(converted_exceptions[0].body) new_body['NeutronError']['message'] = joined_msg converted_exceptions[0].body = serializer.serialize(new_body) return converted_exceptions[0] else: # multiple error types so we turn it into a Conflict with the # inner codes and bodies packed in new_exception = exceptions.Conflict() inner_error_strings = [] for c in converted_exceptions: c_body = jsonutils.loads(c.body) err = ('HTTP %s %s: %s' % ( c.code, c_body['NeutronError']['type'], c_body['NeutronError']['message'])) inner_error_strings.append(err) new_exception.msg = "\n".join(inner_error_strings) return convert_exception_to_http_exc( new_exception, faults, language) e = translate(e, language) body = serializer.serialize( {'NeutronError': get_exception_data(e)}) kwargs = {'body': body, 'content_type': 'application/json'} if isinstance(e, exc.HTTPException): # already an HTTP error, just update with content type and body e.body = body e.content_type = kwargs['content_type'] return e faults_tuple = tuple(faults.keys()) + (exceptions.NeutronException,) if isinstance(e, faults_tuple): for fault in faults: if isinstance(e, fault): mapped_exc = faults[fault] break else: mapped_exc = exc.HTTPInternalServerError return mapped_exc(**kwargs) if isinstance(e, NotImplementedError): # NOTE(armando-migliaccio): from a client standpoint # it makes sense to receive these errors, because # extensions may or may not be implemented by # the underlying plugin. So if something goes south, # because a plugin does not implement a feature, # returning 500 is definitely confusing. kwargs['body'] = serializer.serialize( {'NotImplementedError': get_exception_data(e)}) return exc.HTTPNotImplemented(**kwargs) # NOTE(jkoelker) Everything else is 500 # Do not expose details of 500 error to clients. msg = _('Request Failed: internal server error while ' 'processing your request.') msg = translate(msg, language) kwargs['body'] = serializer.serialize( {'NeutronError': get_exception_data(exc.HTTPInternalServerError(msg))}) return exc.HTTPInternalServerError(**kwargs) def get_exception_data(e): """Extract the information about an exception. Neutron client for the v2 API expects exceptions to have 'type', 'message' and 'detail' attributes.This information is extracted and converted into a dictionary. :param e: the exception to be reraised :returns: a structured dict with the exception data """ err_data = {'type': e.__class__.__name__, 'message': e, 'detail': ''} return err_data def translate(translatable, locale): """Translates the object to the given locale. If the object is an exception its translatable elements are translated in place, if the object is a translatable string it is translated and returned. Otherwise, the object is returned as-is. :param translatable: the object to be translated :param locale: the locale to translate to :returns: the translated object, or the object as-is if it was not translated """ localize = oslo_i18n.translate if isinstance(translatable, exceptions.NeutronException): translatable.msg = localize(translatable.msg, locale) elif isinstance(translatable, exc.HTTPError): translatable.detail = localize(translatable.detail, locale) elif isinstance(translatable, Exception): translatable.message = localize(translatable, locale) else: return localize(translatable, locale) return translatable neutron-12.1.1/neutron/core_extensions/0000775000175000017500000000000013553660156020223 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/core_extensions/qos.py0000664000175000017500000001131613553660047021400 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from neutron_lib.services.qos import constants as qos_consts from neutron.common import exceptions as n_exc from neutron.core_extensions import base from neutron.db import api as db_api from neutron.objects.qos import policy as policy_object class QosCoreResourceExtension(base.CoreResourceExtension): @property def plugin_loaded(self): if not hasattr(self, '_plugin_loaded'): self._plugin_loaded = ( plugin_constants.QOS in directory.get_plugins()) return self._plugin_loaded def _get_policy_obj(self, context, policy_id): obj = policy_object.QosPolicy.get_object(context, id=policy_id) if obj is None: raise n_exc.QosPolicyNotFound(policy_id=policy_id) return obj def _check_policy_change_permission(self, context, old_policy): """An existing policy can be modified only if one of the following is true: the policy's tenant is the context's tenant the policy is shared with the tenant Using is_accessible expresses these conditions. """ if not (policy_object.QosPolicy.is_accessible(context, old_policy)): raise n_exc.PolicyRemoveAuthorizationError(policy_id=old_policy.id) def _update_port_policy(self, context, port, port_changes): old_policy = policy_object.QosPolicy.get_port_policy( context.elevated(), port['id']) if old_policy: self._check_policy_change_permission(context, old_policy) old_policy.detach_port(port['id']) qos_policy_id = port_changes.get(qos_consts.QOS_POLICY_ID) if qos_policy_id is not None: policy = self._get_policy_obj(context, qos_policy_id) policy.attach_port(port['id']) port[qos_consts.QOS_POLICY_ID] = qos_policy_id def _create_network_policy(self, context, network, network_changes): qos_policy_id = network_changes.get(qos_consts.QOS_POLICY_ID) if not qos_policy_id: policy_obj = policy_object.QosPolicyDefault.get_object( context, project_id=network['project_id']) if policy_obj is not None: qos_policy_id = policy_obj.qos_policy_id if qos_policy_id is not None: policy = self._get_policy_obj(context, qos_policy_id) policy.attach_network(network['id']) network[qos_consts.QOS_POLICY_ID] = qos_policy_id def _update_network_policy(self, context, network, network_changes): old_policy = policy_object.QosPolicy.get_network_policy( context.elevated(), network['id']) if old_policy: self._check_policy_change_permission(context, old_policy) old_policy.detach_network(network['id']) qos_policy_id = network_changes.get(qos_consts.QOS_POLICY_ID) if qos_policy_id is not None: policy = self._get_policy_obj(context, qos_policy_id) policy.attach_network(network['id']) network[qos_consts.QOS_POLICY_ID] = qos_policy_id def _exec(self, method_name, context, kwargs): with db_api.autonested_transaction(context.session): return getattr(self, method_name)(context=context, **kwargs) def process_fields(self, context, resource_type, event_type, requested_resource, actual_resource): if (qos_consts.QOS_POLICY_ID in requested_resource and self.plugin_loaded): method_name = ('_%(event)s_%(resource)s_policy' % {'event': event_type, 'resource': resource_type}) self._exec(method_name, context, {resource_type: actual_resource, "%s_changes" % resource_type: requested_resource}) def extract_fields(self, resource_type, resource): if not self.plugin_loaded: return {} binding = resource['qos_policy_binding'] qos_policy_id = binding['policy_id'] if binding else None return {qos_consts.QOS_POLICY_ID: qos_policy_id} neutron-12.1.1/neutron/core_extensions/base.py0000664000175000017500000000323113553660046021504 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import six NETWORK = 'network' PORT = 'port' EVENT_CREATE = 'create' EVENT_UPDATE = 'update' CORE_RESOURCES = [NETWORK, PORT] @six.add_metaclass(abc.ABCMeta) class CoreResourceExtension(object): @abc.abstractmethod def process_fields(self, context, resource_type, event_type, requested_resource, actual_resource): """Process extension fields. :param context: neutron api request context :param resource_type: core resource type (one of CORE_RESOURCES) :param event_type: kind of event triggering this action (update, create) :param requested_resource: resource dict that contains extension fields :param actual_resource: actual resource dict known to plugin """ @abc.abstractmethod def extract_fields(self, resource_type, resource): """Extract extension fields. :param resource_type: core resource type (one of CORE_RESOURCES) :param resource: resource dict that contains extension fields """ neutron-12.1.1/neutron/core_extensions/__init__.py0000664000175000017500000000000013553660046022320 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/0000775000175000017500000000000013553660156016156 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/fake_notifier.py0000664000175000017500000000334413553660047021340 0ustar zuulzuul00000000000000# Copyright 2014 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import functools NOTIFICATIONS = [] def reset(): del NOTIFICATIONS[:] FakeMessage = collections.namedtuple('Message', ['publisher_id', 'priority', 'event_type', 'payload']) class FakeNotifier(object): def __init__(self, transport, publisher_id=None, driver=None, topics=None, serializer=None, retry=None): self.transport = transport self.publisher_id = publisher_id for priority in ('debug', 'info', 'warn', 'error', 'critical'): setattr(self, priority, functools.partial(self._notify, priority=priority.upper())) def prepare(self, publisher_id=None): if publisher_id is None: publisher_id = self.publisher_id return self.__class__(self.transport, publisher_id) def _notify(self, ctxt, event_type, payload, priority): msg = dict(publisher_id=self.publisher_id, priority=priority, event_type=event_type, payload=payload) NOTIFICATIONS.append(msg) neutron-12.1.1/neutron/tests/unit/0000775000175000017500000000000013553660156017135 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/0000775000175000017500000000000013553660157020761 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/auto_allocate/0000775000175000017500000000000013553660157023575 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/auto_allocate/test_db.py0000664000175000017500000002437513553660047025604 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock import testtools from neutron_lib import context from neutron_lib import exceptions as n_exc from oslo_db import exception as db_exc from oslo_utils import uuidutils from neutron.common import exceptions as c_exc from neutron.services.auto_allocate import db from neutron.services.auto_allocate import exceptions from neutron.tests.unit import testlib_api DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' class AutoAllocateTestCase(testlib_api.SqlTestCase): def setUp(self): super(AutoAllocateTestCase, self).setUp() self.setup_coreplugin(core_plugin=DB_PLUGIN_KLASS) self.ctx = context.get_admin_context() self.mixin = db.AutoAllocatedTopologyMixin() self.mixin._l3_plugin = mock.Mock() self.mixin._core_plugin = mock.Mock() def test__provision_external_connectivity_expected_cleanup(self): """Test that the right resources are cleaned up.""" subnets = [ {'id': 'subnet_foo_1', 'network_id': 'network_foo'}, {'id': 'subnet_foo_2', 'network_id': 'network_foo'}, ] with mock.patch.object(self.mixin, '_cleanup') as mock_cleanup: self.mixin.l3_plugin.create_router.return_value = ( {'id': 'router_foo'}) self.mixin.l3_plugin.add_router_interface.side_effect = ( n_exc.BadRequest(resource='router', msg='doh!')) self.assertRaises(exceptions.AutoAllocationFailure, self.mixin._provision_external_connectivity, self.ctx, 'ext_net_foo', subnets, 'tenant_foo') # expect no subnets to be unplugged mock_cleanup.assert_called_once_with( self.ctx, network_id='network_foo', router_id='router_foo', subnets=[]) def test__provision_external_connectivity_fail_expected_cleanup(self): """Test that the right resources are cleaned up.""" subnets = [ {'id': 'subnet_foo_1', 'network_id': 'network_foo'}, ] with mock.patch.object(self.mixin, '_cleanup') as mock_cleanup: self.mixin.l3_plugin.create_router.side_effect = ( n_exc.BadRequest(resource='router', msg='doh!')) self.assertRaises(exceptions.AutoAllocationFailure, self.mixin._provision_external_connectivity, self.ctx, 'ext_net_foo', subnets, 'tenant_foo') # expected router_id to be None mock_cleanup.assert_called_once_with( self.ctx, network_id='network_foo', router_id=None, subnets=[]) def test_get_auto_allocated_topology_dry_run_happy_path_for_kevin(self): with mock.patch.object(self.mixin, '_check_requirements') as f: self.mixin.get_auto_allocated_topology( self.ctx, mock.ANY, fields=['dry-run']) self.assertEqual(1, f.call_count) def test_get_auto_allocated_topology_dry_run_bad_input(self): self.assertRaises(n_exc.BadRequest, self.mixin.get_auto_allocated_topology, self.ctx, mock.ANY, fields=['foo']) def test__provision_tenant_private_network_handles_subnet_errors(self): network_id = uuidutils.generate_uuid() self.mixin._core_plugin.create_network.return_value = ( {'id': network_id}) self.mixin._core_plugin.create_subnet.side_effect = ( c_exc.SubnetAllocationError(reason='disaster')) with mock.patch.object(self.mixin, "_get_supported_subnetpools") as f,\ mock.patch.object(self.mixin, "_cleanup") as g: f.return_value = ( [{'ip_version': 4, "id": uuidutils.generate_uuid()}]) self.assertRaises(exceptions.AutoAllocationFailure, self.mixin._provision_tenant_private_network, self.ctx, 'foo_tenant') g.assert_called_once_with(self.ctx, network_id) def _test__build_topology(self, method, provisioning_exception): with mock.patch.object(self.mixin, method, side_effect=provisioning_exception), \ mock.patch.object(self.mixin, '_cleanup') as f: self.assertRaises(provisioning_exception.error, self.mixin._build_topology, self.ctx, mock.ANY, 'foo_net') f.assert_called_once_with( self.ctx, network_id=provisioning_exception.network_id, router_id=provisioning_exception.router_id, subnets=provisioning_exception.subnets ) def test__build_topology_provisioning_error_no_toplogy(self): provisioning_exception = exceptions.UnknownProvisioningError( db_exc.DBError) self._test__build_topology( '_provision_tenant_private_network', provisioning_exception) def test__build_topology_provisioning_error_network_only(self): provisioning_exception = exceptions.UnknownProvisioningError( Exception, network_id='foo') self._test__build_topology( '_provision_tenant_private_network', provisioning_exception) def test__build_topology_error_only_network_again(self): provisioning_exception = exceptions.UnknownProvisioningError( AttributeError, network_id='foo') with mock.patch.object(self.mixin, '_provision_tenant_private_network') as f: f.return_value = [{'network_id': 'foo'}] self._test__build_topology( '_provision_external_connectivity', provisioning_exception) def test__build_topology_error_network_with_router(self): provisioning_exception = exceptions.UnknownProvisioningError( KeyError, network_id='foo_n', router_id='foo_r') with mock.patch.object(self.mixin, '_provision_tenant_private_network') as f: f.return_value = [{'network_id': 'foo_n'}] self._test__build_topology( '_provision_external_connectivity', provisioning_exception) def test__build_topology_error_network_with_router_and_interfaces(self): provisioning_exception = exceptions.UnknownProvisioningError( db_exc.DBConnectionError, network_id='foo_n', router_id='foo_r', subnets=[{'id': 'foo_s'}]) with mock.patch.object(self.mixin, '_provision_tenant_private_network') as f,\ mock.patch.object(self.mixin, '_provision_external_connectivity') as g: f.return_value = [{'network_id': 'foo_n'}] g.return_value = {'id': 'foo_r'} self._test__build_topology( '_save', provisioning_exception) def test__save_with_provisioning_error(self): self.mixin._core_plugin.update_network.side_effect = Exception with testtools.ExpectedException( exceptions.UnknownProvisioningError) as e: self.mixin._save(self.ctx, 'foo_t', 'foo_n', 'foo_r', [{'id': 'foo_s'}]) self.assertEqual('foo_n', e.network_id) self.assertEqual('foo_r', e.router_id) self.assertEqual([{'id': 'foo_s'}], e.subnets) def test__provision_external_connectivity_with_provisioning_error(self): self.mixin._l3_plugin.create_router.side_effect = Exception with testtools.ExpectedException( exceptions.UnknownProvisioningError) as e: self.mixin._provision_external_connectivity( self.ctx, 'foo_default', [{'id': 'foo_s', 'network_id': 'foo_n'}], 'foo_tenant') self.assertEqual('foo_n', e.network_id) self.assertIsNone(e.router_id) self.assertIsNone(e.subnets) def test__provision_tenant_private_network_with_provisioning_error(self): self.mixin._core_plugin.create_network.side_effect = Exception with testtools.ExpectedException( exceptions.UnknownProvisioningError) as e: self.mixin._provision_tenant_private_network( self.ctx, 'foo_tenant') self.assertIsNone(e.network_id) def test__check_requirements_fail_on_missing_ext_net(self): self.assertRaises(exceptions.AutoAllocationFailure, self.mixin._check_requirements, self.ctx, 'foo_tenant') def test__check_requirements_fail_on_missing_pools(self): with mock.patch.object( self.mixin, '_get_default_external_network'),\ mock.patch.object( self.mixin, '_get_supported_subnetpools') as g: g.side_effect = n_exc.NotFound() self.assertRaises(exceptions.AutoAllocationFailure, self.mixin._check_requirements, self.ctx, 'foo_tenant') def test__check_requirements_happy_path_for_kevin(self): with mock.patch.object( self.mixin, '_get_default_external_network'),\ mock.patch.object( self.mixin, '_get_supported_subnetpools'): result = self.mixin._check_requirements(self.ctx, 'foo_tenant') expected = {'id': 'dry-run=pass', 'tenant_id': 'foo_tenant'} self.assertEqual(expected, result) def test__cleanup_handles_failures(self): notfound = n_exc.NotFound self.mixin._l3_plugin.remove_router_interface.side_effect = ( notfound) self.mixin._l3_plugin.delete_router.side_effect = ( notfound) self.mixin._core_plugin.delete_network.side_effect = ( notfound) self.mixin._cleanup(self.ctx, network_id=44, router_id=45, subnets=[{'id': 46}]) neutron-12.1.1/neutron/tests/unit/services/auto_allocate/__init__.py0000664000175000017500000000000013553660046025671 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/revisions/0000775000175000017500000000000013553660157023002 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/revisions/__init__.py0000664000175000017500000000000013553660046025076 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/revisions/test_revision_plugin.py0000664000175000017500000003202113553660047027623 0ustar zuulzuul00000000000000# All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import netaddr from neutron_lib import context as nctx from neutron_lib.plugins import constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_db import exception as db_exc from oslo_utils import uuidutils from sqlalchemy.orm import session as se from webob import exc from neutron.db import api as db_api from neutron.db import models_v2 from neutron.objects import ports as port_obj from neutron.tests.unit.plugins.ml2 import test_plugin class TestRevisionPlugin(test_plugin.Ml2PluginV2TestCase): l3_plugin = ('neutron.tests.unit.extensions.test_extraroute.' 'TestExtraRouteL3NatServicePlugin') _extension_drivers = ['qos'] def get_additional_service_plugins(self): p = super(TestRevisionPlugin, self).get_additional_service_plugins() p.update({'revision_plugin_name': 'revisions', 'qos_plugin_name': 'qos', 'tag_name': 'tag'}) return p def setUp(self): cfg.CONF.set_override('extension_drivers', self._extension_drivers, group='ml2') super(TestRevisionPlugin, self).setUp() self.cp = directory.get_plugin() self.l3p = directory.get_plugin(constants.L3) self._ctx = nctx.get_admin_context() self._tenant_id = uuidutils.generate_uuid() @property def ctx(self): # TODO(kevinbenton): return ctx without expire_all after switch to # enginefacade complete. We expire_all here because the switch to # the new engine facade is resulting in changes being spread over # other sessions so we can end up getting stale reads in the parent # session if objects remain in the identity map. if not self._ctx.session.is_active: self._ctx.session.expire_all() return self._ctx def test_handle_expired_object(self): rp = directory.get_plugin('revision_plugin') with self.port(): with self.ctx.session.begin(): ipal_objs = port_obj.IPAllocation.get_objects(self.ctx) if not ipal_objs: raise Exception("No IP allocations available.") ipal_obj = ipal_objs[0] # load port into our session port = self.ctx.session.query(models_v2.Port).one() # simulate concurrent delete in another session other_ctx = nctx.get_admin_context() other_ctx.session.delete( other_ctx.session.query(models_v2.Port).first() ) # expire the port so the revision bumping code will trigger a # lookup on its attributes and encounter an ObjectDeletedError self.ctx.session.expire(port) rp._bump_related_revisions(self.ctx.session, ipal_obj) def test_port_name_update_revises(self): with self.port() as port: rev = port['port']['revision_number'] new = {'port': {'name': 'seaweed'}} response = self._update('ports', port['port']['id'], new) new_rev = response['port']['revision_number'] self.assertGreater(new_rev, rev) def test_constrained_port_update(self): with self.port() as port: rev = port['port']['revision_number'] new = {'port': {'name': 'nigiri'}} for val in (rev - 1, rev + 1): # make sure off-by ones are rejected self._update('ports', port['port']['id'], new, headers={'If-Match': 'revision_number=%s' % val}, expected_code=exc.HTTPPreconditionFailed.code) after_attempt = self._show('ports', port['port']['id']) self.assertEqual(rev, after_attempt['port']['revision_number']) self.assertEqual(port['port']['name'], after_attempt['port']['name']) # correct revision should work self._update('ports', port['port']['id'], new, headers={'If-Match': 'revision_number=%s' % rev}) def test_constrained_port_delete(self): with self.port() as port: rev = port['port']['revision_number'] for val in (rev - 1, rev + 1): # make sure off-by ones are rejected self._delete('ports', port['port']['id'], headers={'If-Match': 'revision_number=%s' % val}, expected_code=exc.HTTPPreconditionFailed.code) # correct revision should work self._delete('ports', port['port']['id'], headers={'If-Match': 'revision_number=%s' % rev}) def test_constrained_port_update_handles_db_retries(self): # here we ensure all of the constraint handling logic persists # on retriable failures to commit caused by races with another # update with self.port() as port: rev = port['port']['revision_number'] new = {'port': {'name': 'nigiri'}} def concurrent_increment(s): db_api.sqla_remove(se.Session, 'before_commit', concurrent_increment) # slip in a concurrent update that will bump the revision plugin = directory.get_plugin() plugin.update_port(nctx.get_admin_context(), port['port']['id'], new) raise db_exc.DBDeadlock() db_api.sqla_listen(se.Session, 'before_commit', concurrent_increment) self._update('ports', port['port']['id'], new, headers={'If-Match': 'revision_number=%s' % rev}, expected_code=exc.HTTPPreconditionFailed.code) def test_port_ip_update_revises(self): with self.subnet() as subnet, self.port(subnet=subnet) as port: rev = port['port']['revision_number'] new = {'port': {'fixed_ips': port['port']['fixed_ips']}} # ensure adding an IP allocation updates the port next_ip = str(netaddr.IPAddress( new['port']['fixed_ips'][0]['ip_address']) + 1) new['port']['fixed_ips'].append({'ip_address': next_ip}) response = self._update('ports', port['port']['id'], new) self.assertEqual(2, len(response['port']['fixed_ips'])) new_rev = response['port']['revision_number'] self.assertGreater(new_rev, rev) # ensure deleting an IP allocation updates the port rev = new_rev new['port']['fixed_ips'].pop() response = self._update('ports', port['port']['id'], new) self.assertEqual(1, len(response['port']['fixed_ips'])) new_rev = response['port']['revision_number'] self.assertGreater(new_rev, rev) def test_security_group_rule_ops_bump_security_group(self): s = {'security_group': {'tenant_id': 'some_tenant', 'name': '', 'description': 's'}} sg = self.cp.create_security_group(self.ctx, s) s['security_group']['name'] = 'hello' updated = self.cp.update_security_group(self.ctx, sg['id'], s) self.assertGreater(updated['revision_number'], sg['revision_number']) # ensure rule changes bump parent SG r = {'security_group_rule': {'tenant_id': 'some_tenant', 'port_range_min': 80, 'protocol': 6, 'port_range_max': 90, 'remote_ip_prefix': '0.0.0.0/0', 'ethertype': 'IPv4', 'remote_group_id': None, 'direction': 'ingress', 'security_group_id': sg['id']}} rule = self.cp.create_security_group_rule(self.ctx, r) sg = updated updated = self.cp.get_security_group(self.ctx, sg['id']) self.assertGreater(updated['revision_number'], sg['revision_number']) self.cp.delete_security_group_rule(self.ctx, rule['id']) sg = updated updated = self.cp.get_security_group(self.ctx, sg['id']) self.assertGreater(updated['revision_number'], sg['revision_number']) def test_router_interface_ops_bump_router(self): r = {'router': {'name': 'myrouter', 'tenant_id': 'some_tenant', 'admin_state_up': True}} router = self.l3p.create_router(self.ctx, r) r['router']['name'] = 'yourrouter' updated = self.l3p.update_router(self.ctx, router['id'], r) self.assertGreater(updated['revision_number'], router['revision_number']) # add an intf and make sure it bumps rev with self.subnet(tenant_id='some_tenant', cidr='10.0.1.0/24') as s: interface_info = {'subnet_id': s['subnet']['id']} self.l3p.add_router_interface(self.ctx, router['id'], interface_info) router = updated updated = self.l3p.get_router(self.ctx, router['id']) self.assertGreater(updated['revision_number'], router['revision_number']) # Add a route and make sure it bumps revision number router = updated body = {'router': {'routes': [{'destination': '192.168.2.0/24', 'nexthop': '10.0.1.3'}]}} self.l3p.update_router(self.ctx, router['id'], body) updated = self.l3p.get_router(self.ctx, router['id']) self.assertGreater(updated['revision_number'], router['revision_number']) router = updated body['router']['routes'] = [] self.l3p.update_router(self.ctx, router['id'], body) updated = self.l3p.get_router(self.ctx, router['id']) self.assertGreater(updated['revision_number'], router['revision_number']) self.l3p.remove_router_interface(self.ctx, router['id'], interface_info) router = updated updated = self.l3p.get_router(self.ctx, router['id']) self.assertGreater(updated['revision_number'], router['revision_number']) def test_qos_policy_bump_port_revision(self): with self.port() as port: rev = port['port']['revision_number'] qos_plugin = directory.get_plugin('QOS') qos_policy = {'policy': {'id': uuidutils.generate_uuid(), 'name': "policy1", 'project_id': uuidutils.generate_uuid()}} qos_obj = qos_plugin.create_policy(self.ctx, qos_policy) data = {'port': {'qos_policy_id': qos_obj['id']}} response = self._update('ports', port['port']['id'], data) new_rev = response['port']['revision_number'] self.assertGreater(new_rev, rev) def test_qos_policy_bump_network_revision(self): with self.network() as network: rev = network['network']['revision_number'] qos_plugin = directory.get_plugin('QOS') qos_policy = {'policy': {'id': uuidutils.generate_uuid(), 'name': "policy1", 'project_id': uuidutils.generate_uuid()}} qos_obj = qos_plugin.create_policy(self.ctx, qos_policy) data = {'network': {'qos_policy_id': qos_obj['id']}} response = self._update('networks', network['network']['id'], data) new_rev = response['network']['revision_number'] self.assertGreater(new_rev, rev) def test_net_tag_bumps_net_revision(self): with self.network() as network: rev = network['network']['revision_number'] tag_plugin = directory.get_plugin('TAG') tag_plugin.update_tag(self.ctx, 'networks', network['network']['id'], 'mytag') updated = directory.get_plugin().get_network( self.ctx, network['network']['id']) self.assertGreater(updated['revision_number'], rev) tag_plugin.delete_tag(self.ctx, 'networks', network['network']['id'], 'mytag') rev = updated['revision_number'] updated = directory.get_plugin().get_network( self.ctx, network['network']['id']) self.assertGreater(updated['revision_number'], rev) neutron-12.1.1/neutron/tests/unit/services/qos/0000775000175000017500000000000013553660157021563 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/qos/drivers/0000775000175000017500000000000013553660157023241 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/qos/drivers/test_manager.py0000664000175000017500000003101213553660047026257 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import portbindings from neutron_lib import constants as lib_consts from neutron_lib import context from neutron_lib.services.qos import base as qos_driver_base from neutron_lib.services.qos import constants as qos_consts from oslo_utils import uuidutils from neutron.common import constants from neutron.common import exceptions from neutron.objects import ports as ports_object from neutron.objects.qos import rule as rule_object from neutron.services.qos.drivers import manager as driver_mgr from neutron.tests.unit.services.qos import base class TestQosDriversManagerBase(base.BaseQosTestCase): def setUp(self): super(TestQosDriversManagerBase, self).setUp() self.config_parse() self.setup_coreplugin(load_plugins=False) @staticmethod def _create_manager_with_drivers(drivers_details): for name, driver_details in drivers_details.items(): class QoSDriver(qos_driver_base.DriverBase): @property def is_loaded(self): return driver_details['is_loaded'] # the new ad-hoc driver will register on the QOS_PLUGIN registry QoSDriver(name, driver_details.get('vif_types', []), driver_details.get('vnic_types', []), driver_details.get('rules', [])) return driver_mgr.QosServiceDriverManager() class TestQosDriversManagerMulti(TestQosDriversManagerBase): """Test calls happen to all drivers""" def test_driver_manager_empty_with_no_drivers(self): driver_manager = self._create_manager_with_drivers({}) self.assertEqual(len(driver_manager._drivers), 0) def test_driver_manager_empty_with_no_loaded_drivers(self): driver_manager = self._create_manager_with_drivers( {'driver-A': {'is_loaded': False}}) self.assertEqual(len(driver_manager._drivers), 0) def test_driver_manager_with_one_loaded_driver(self): driver_manager = self._create_manager_with_drivers( {'driver-A': {'is_loaded': True}}) self.assertEqual(len(driver_manager._drivers), 1) def test_driver_manager_with_two_loaded_drivers(self): driver_manager = self._create_manager_with_drivers( {'driver-A': {'is_loaded': True}, 'driver-B': {'is_loaded': True}}) self.assertEqual(len(driver_manager._drivers), 2) class TestQoSDriversRulesValidations(TestQosDriversManagerBase): """Test validation of rules for port""" def setUp(self): super(TestQoSDriversRulesValidations, self).setUp() self.ctxt = context.Context('fake_user', 'fake_tenant') def _get_port(self, vif_type, vnic_type): port_id = uuidutils.generate_uuid() port_binding = ports_object.PortBinding( self.ctxt, port_id=port_id, vif_type=vif_type, vnic_type=vnic_type) return ports_object.Port( self.ctxt, id=uuidutils.generate_uuid(), binding=port_binding) def _test_validate_rule_for_port(self, port, expected_result): driver_manager = self._create_manager_with_drivers({ 'driver-A': { 'is_loaded': True, 'rules': { qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: { "min_kbps": {'type:values': None}, 'direction': { 'type:values': lib_consts.VALID_DIRECTIONS} } }, 'vif_types': [portbindings.VIF_TYPE_OVS], 'vnic_types': [portbindings.VNIC_NORMAL] } }) rule = rule_object.QosMinimumBandwidthRule( self.ctxt, id=uuidutils.generate_uuid()) is_rule_supported_mock = mock.Mock() if expected_result: is_rule_supported_mock.return_value = expected_result driver_manager._drivers[0].is_rule_supported = is_rule_supported_mock self.assertEqual(expected_result, driver_manager.validate_rule_for_port(rule, port)) if expected_result: is_rule_supported_mock.assert_called_once_with(rule) else: is_rule_supported_mock.assert_not_called() def test_validate_rule_for_port_rule_vif_type_supported(self): port = self._get_port( portbindings.VIF_TYPE_OVS, portbindings.VNIC_NORMAL) self._test_validate_rule_for_port( port, expected_result=True) def test_validate_rule_for_port_vif_type_not_supported(self): port = self._get_port( portbindings.VIF_TYPE_OTHER, portbindings.VNIC_NORMAL) self._test_validate_rule_for_port( port, expected_result=False) def test_validate_rule_for_port_unbound_vnic_type_supported(self): port = self._get_port( portbindings.VIF_TYPE_UNBOUND, portbindings.VNIC_NORMAL) self._test_validate_rule_for_port( port, expected_result=True) def test_validate_rule_for_port_unbound_vnic_type_not_supported(self): port = self._get_port( portbindings.VIF_TYPE_UNBOUND, portbindings.VNIC_BAREMETAL) self._test_validate_rule_for_port( port, expected_result=False) class TestQosDriversManagerRules(TestQosDriversManagerBase): """Test supported rules""" def test_available_rules_one_in_common(self): driver_manager = self._create_manager_with_drivers({ 'driver-A': { 'is_loaded': True, 'rules': { qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: { "max_kbps": {'type:values': None}, "max_burst_kbps": {'type:values': None} }, qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: { "min_kbps": {'type:values': None}, 'direction': { 'type:values': lib_consts.VALID_DIRECTIONS} } } }, 'driver-B': { 'is_loaded': True, 'rules': { qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: { "min_kbps": {'type:values': None}, 'direction': { 'type:values': lib_consts.VALID_DIRECTIONS} }, qos_consts.RULE_TYPE_DSCP_MARKING: { "dscp_mark": { 'type:values': lib_consts.VALID_DSCP_MARKS} } } } }) self.assertEqual(driver_manager.supported_rule_types, set([qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH])) def test_available_rules_no_rule_in_common(self): driver_manager = self._create_manager_with_drivers({ 'driver-A': { 'is_loaded': True, 'rules': { qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: { "max_kbps": {'type:values': None}, "max_burst_kbps": {'type:values': None} } } }, 'driver-B': { 'is_loaded': True, 'rules': { qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: { "min_kbps": {'type:values': None}, 'direction': { 'type:values': lib_consts.VALID_DIRECTIONS} }, qos_consts.RULE_TYPE_DSCP_MARKING: { "dscp_mark": { 'type:values': lib_consts.VALID_DSCP_MARKS} } } } }) self.assertEqual(driver_manager.supported_rule_types, set([])) def test__parse_parameter_values(self): range_parameter = {'type:range': [0, 10]} values_parameter = {'type:values': [1, 10, 100, 1000]} expected_parsed_range_parameter = {'start': 0, 'end': 10} expected_parsed_values_parameter = [1, 10, 100, 1000] parameter_values, parameter_type = ( driver_mgr.QosServiceDriverManager._parse_parameter_values( range_parameter)) self.assertEqual( expected_parsed_range_parameter, parameter_values) self.assertEqual( constants.VALUES_TYPE_RANGE, parameter_type) parameter_values, parameter_type = ( driver_mgr.QosServiceDriverManager._parse_parameter_values( values_parameter)) self.assertEqual( expected_parsed_values_parameter, parameter_values) self.assertEqual( constants.VALUES_TYPE_CHOICES, parameter_type) def test_supported_rule_type_details(self): driver_manager = self._create_manager_with_drivers({ 'driver-A': { 'is_loaded': True, 'rules': { qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: { "max_kbps": {'type:range': [0, 1000]}, "max_burst_kbps": {'type:range': [0, 1000]} } } }, 'driver-B': { 'is_loaded': True, 'rules': { qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: { "min_kbps": {'type:range': [0, 1000]}, 'direction': { 'type:values': lib_consts.VALID_DIRECTIONS} }, qos_consts.RULE_TYPE_DSCP_MARKING: { "dscp_mark": { 'type:values': lib_consts.VALID_DSCP_MARKS} } } } }) expected_rule_type_details = [{ 'name': 'driver-A', 'supported_parameters': [{ 'parameter_name': 'max_kbps', 'parameter_type': constants.VALUES_TYPE_RANGE, 'parameter_values': {'start': 0, 'end': 1000} }, { 'parameter_name': 'max_burst_kbps', 'parameter_type': constants.VALUES_TYPE_RANGE, 'parameter_values': {'start': 0, 'end': 1000} }] }] bandwidth_limit_details = driver_manager.supported_rule_type_details( qos_consts.RULE_TYPE_BANDWIDTH_LIMIT) self.assertEqual( len(expected_rule_type_details), len(bandwidth_limit_details)) self.assertEqual( expected_rule_type_details[0]['name'], bandwidth_limit_details[0]['name']) self.assertEqual( len(expected_rule_type_details[0]['supported_parameters']), len(bandwidth_limit_details[0]['supported_parameters']) ) for parameter in expected_rule_type_details[0]['supported_parameters']: self.assertIn( parameter, bandwidth_limit_details[0]['supported_parameters']) def test_supported_rule_type_details_no_drivers_loaded(self): driver_manager = self._create_manager_with_drivers({}) self.assertEqual( [], driver_manager.supported_rule_type_details( qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)) class TestQosDriversCalls(TestQosDriversManagerBase): """Test QoS driver calls""" def setUp(self): super(TestQosDriversCalls, self).setUp() self.driver_manager = self._create_manager_with_drivers( {'driver-A': {'is_loaded': True}}) def test_implemented_call_methods(self): for method in qos_consts.QOS_CALL_METHODS: with mock.patch.object(qos_driver_base.DriverBase, method) as \ method_fnc: context = mock.Mock() policy = mock.Mock() self.driver_manager.call(method, context, policy) method_fnc.assert_called_once_with(context, policy) def test_not_implemented_call_methods(self): self.assertRaises(exceptions.DriverCallError, self.driver_manager.call, 'wrong_method', mock.Mock(), mock.Mock()) neutron-12.1.1/neutron/tests/unit/services/qos/drivers/__init__.py0000664000175000017500000000000013553660046025335 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/qos/base.py0000664000175000017500000000304313553660047023045 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.api.rpc.callbacks.consumer import registry as cons_registry from neutron.api.rpc.callbacks.producer import registry as prod_registry from neutron.api.rpc.callbacks import resource_manager from neutron.tests.unit import testlib_api class BaseQosTestCase(testlib_api.SqlTestCase): def setUp(self): super(BaseQosTestCase, self).setUp() with mock.patch.object( resource_manager.ResourceCallbacksManager, '_singleton', new_callable=mock.PropertyMock(return_value=False)): self.cons_mgr = resource_manager.ConsumerResourceCallbacksManager() self.prod_mgr = resource_manager.ProducerResourceCallbacksManager() for mgr in (self.cons_mgr, self.prod_mgr): mgr.clear() mock.patch.object( cons_registry, '_get_manager', return_value=self.cons_mgr).start() mock.patch.object( prod_registry, '_get_manager', return_value=self.prod_mgr).start() neutron-12.1.1/neutron/tests/unit/services/qos/__init__.py0000664000175000017500000000000013553660046023657 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/qos/test_qos_plugin.py0000664000175000017500000013042313553660047025355 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import mock from neutron_lib.callbacks import events from neutron_lib import context from neutron_lib import exceptions as lib_exc from neutron_lib.plugins import constants as plugins_constants from neutron_lib.plugins import directory from neutron_lib.services.qos import constants as qos_consts from oslo_config import cfg from oslo_utils import uuidutils from neutron.common import constants from neutron.common import exceptions as n_exc from neutron import manager from neutron.objects import base as base_object from neutron.objects.qos import policy as policy_object from neutron.objects.qos import rule as rule_object from neutron.services.qos import qos_plugin from neutron.tests.unit.services.qos import base DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' class TestQosPlugin(base.BaseQosTestCase): def setUp(self): super(TestQosPlugin, self).setUp() self.setup_coreplugin(load_plugins=False) mock.patch('neutron.objects.db.api.create_object').start() mock.patch('neutron.objects.db.api.update_object').start() mock.patch('neutron.objects.db.api.delete_object').start() mock.patch('neutron.objects.db.api.get_object').start() _mock_qos_load_attr = mock.patch( 'neutron.objects.qos.policy.QosPolicy.obj_load_attr') self.mock_qos_load_attr = _mock_qos_load_attr.start() # We don't use real models as per mocks above. We also need to mock-out # methods that work with real data types mock.patch( 'neutron.objects.base.NeutronDbObject.modify_fields_from_db' ).start() mock.patch.object(policy_object.QosPolicy, 'unset_default').start() mock.patch.object(policy_object.QosPolicy, 'set_default').start() cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) cfg.CONF.set_override("service_plugins", ["qos"]) manager.init() self.qos_plugin = directory.get_plugin(plugins_constants.QOS) self.qos_plugin.driver_manager = mock.Mock() self.rpc_push = mock.patch('neutron.api.rpc.handlers.resources_rpc' '.ResourcesPushRpcApi.push').start() self.ctxt = context.Context('fake_user', 'fake_tenant') self.admin_ctxt = context.get_admin_context() mock.patch.object(self.ctxt.session, 'refresh').start() mock.patch.object(self.ctxt.session, 'expunge').start() self.policy_data = { 'policy': {'id': uuidutils.generate_uuid(), 'project_id': uuidutils.generate_uuid(), 'name': 'test-policy', 'description': 'Test policy description', 'shared': True, 'is_default': False}} self.rule_data = { 'bandwidth_limit_rule': {'id': uuidutils.generate_uuid(), 'max_kbps': 100, 'max_burst_kbps': 150}, 'dscp_marking_rule': {'id': uuidutils.generate_uuid(), 'dscp_mark': 16}, 'minimum_bandwidth_rule': { 'id': uuidutils.generate_uuid(), 'min_kbps': 10}} self.policy = policy_object.QosPolicy( self.ctxt, **self.policy_data['policy']) self.rule = rule_object.QosBandwidthLimitRule( self.ctxt, **self.rule_data['bandwidth_limit_rule']) self.dscp_rule = rule_object.QosDscpMarkingRule( self.ctxt, **self.rule_data['dscp_marking_rule']) self.min_rule = rule_object.QosMinimumBandwidthRule( self.ctxt, **self.rule_data['minimum_bandwidth_rule']) def _validate_driver_params(self, method_name, ctxt): call_args = self.qos_plugin.driver_manager.call.call_args[0] self.assertTrue(self.qos_plugin.driver_manager.call.called) self.assertEqual(call_args[0], method_name) self.assertEqual(call_args[1], ctxt) self.assertIsInstance(call_args[2], policy_object.QosPolicy) def test_get_ports_with_policy(self): network_ports = [ mock.MagicMock(qos_policy_id=None), mock.MagicMock(qos_policy_id=uuidutils.generate_uuid()), mock.MagicMock(qos_policy_id=None) ] ports = [ mock.MagicMock(qos_policy_id=self.policy.id), ] expected_network_ports = [ port for port in network_ports if port.qos_policy_id is None] expected_ports = ports + expected_network_ports with mock.patch( 'neutron.objects.ports.Port.get_objects', side_effect=[network_ports, ports] ), mock.patch.object( self.policy, "get_bound_networks" ), mock.patch.object( self.policy, "get_bound_ports" ): policy_ports = self.qos_plugin._get_ports_with_policy( self.ctxt, self.policy) self.assertEqual( len(expected_ports), len(policy_ports)) for port in expected_ports: self.assertIn(port, policy_ports) def _test_validate_create_port_callback(self, policy_id=None, network_policy_id=None): port_id = uuidutils.generate_uuid() kwargs = { "context": self.ctxt, "port": {"id": port_id} } port_mock = mock.MagicMock(id=port_id, qos_policy_id=policy_id) network_mock = mock.MagicMock( id=uuidutils.generate_uuid(), qos_policy_id=network_policy_id) policy_mock = mock.MagicMock(id=policy_id) admin_ctxt = mock.Mock() expected_policy_id = policy_id or network_policy_id with mock.patch( 'neutron.objects.ports.Port.get_object', return_value=port_mock ), mock.patch( 'neutron.objects.network.Network.get_object', return_value=network_mock ), mock.patch( 'neutron.objects.qos.policy.QosPolicy.get_object', return_value=policy_mock ) as get_policy, mock.patch.object( self.qos_plugin, "validate_policy_for_port" ) as validate_policy_for_port, mock.patch.object( self.ctxt, "elevated", return_value=admin_ctxt ): self.qos_plugin._validate_create_port_callback( "PORT", "precommit_create", "test_plugin", **kwargs) if policy_id or network_policy_id: get_policy.assert_called_once_with(admin_ctxt, id=expected_policy_id) validate_policy_for_port.assert_called_once_with(policy_mock, port_mock) else: get_policy.assert_not_called() validate_policy_for_port.assert_not_called() def test_validate_create_port_callback_policy_on_port(self): self._test_validate_create_port_callback( policy_id=uuidutils.generate_uuid()) def test_validate_create_port_callback_policy_on_port_and_network(self): self._test_validate_create_port_callback( policy_id=uuidutils.generate_uuid(), network_policy_id=uuidutils.generate_uuid()) def test_validate_create_port_callback_policy_on_network(self): self._test_validate_create_port_callback( network_policy_id=uuidutils.generate_uuid()) def test_validate_create_port_callback_no_policy(self): self._test_validate_create_port_callback() def _test_validate_update_port_callback(self, policy_id=None, original_policy_id=None): port_id = uuidutils.generate_uuid() kwargs = { "port": { "id": port_id, qos_consts.QOS_POLICY_ID: policy_id }, "original_port": { "id": port_id, qos_consts.QOS_POLICY_ID: original_policy_id } } port_mock = mock.MagicMock(id=port_id, qos_policy_id=policy_id) policy_mock = mock.MagicMock(id=policy_id) admin_ctxt = mock.Mock() with mock.patch( 'neutron.objects.ports.Port.get_object', return_value=port_mock ) as get_port, mock.patch( 'neutron.objects.qos.policy.QosPolicy.get_object', return_value=policy_mock ) as get_policy, mock.patch.object( self.qos_plugin, "validate_policy_for_port" ) as validate_policy_for_port, mock.patch.object( self.ctxt, "elevated", return_value=admin_ctxt ): self.qos_plugin._validate_update_port_callback( "PORT", "precommit_update", "test_plugin", payload=events.DBEventPayload( self.ctxt, desired_state=kwargs['port'], states=(kwargs['original_port'],))) if policy_id is None or policy_id == original_policy_id: get_port.assert_not_called() get_policy.assert_not_called() validate_policy_for_port.assert_not_called() else: get_port.assert_called_once_with(self.ctxt, id=port_id) get_policy.assert_called_once_with(admin_ctxt, id=policy_id) validate_policy_for_port.assert_called_once_with(policy_mock, port_mock) def test_validate_update_port_callback_policy_changed(self): self._test_validate_update_port_callback( policy_id=uuidutils.generate_uuid()) def test_validate_update_port_callback_policy_not_changed(self): policy_id = uuidutils.generate_uuid() self._test_validate_update_port_callback( policy_id=policy_id, original_policy_id=policy_id) def test_validate_update_port_callback_policy_removed(self): self._test_validate_update_port_callback( policy_id=None, original_policy_id=uuidutils.generate_uuid()) def _test_validate_update_network_callback(self, policy_id=None, original_policy_id=None): network_id = uuidutils.generate_uuid() kwargs = { "context": self.ctxt, "network": { "id": network_id, qos_consts.QOS_POLICY_ID: policy_id }, "original_network": { "id": network_id, qos_consts.QOS_POLICY_ID: original_policy_id } } port_mock_with_own_policy = mock.MagicMock( id=uuidutils.generate_uuid(), qos_policy_id=uuidutils.generate_uuid()) port_mock_without_own_policy = mock.MagicMock( id=uuidutils.generate_uuid(), qos_policy_id=None) ports = [port_mock_with_own_policy, port_mock_without_own_policy] policy_mock = mock.MagicMock(id=policy_id) admin_ctxt = mock.Mock() with mock.patch( 'neutron.objects.ports.Port.get_objects', return_value=ports ) as get_ports, mock.patch( 'neutron.objects.qos.policy.QosPolicy.get_object', return_value=policy_mock ) as get_policy, mock.patch.object( self.qos_plugin, "validate_policy_for_ports" ) as validate_policy_for_ports, mock.patch.object( self.ctxt, "elevated", return_value=admin_ctxt ): self.qos_plugin._validate_update_network_callback( "NETWORK", "precommit_update", "test_plugin", payload=events.DBEventPayload( self.ctxt, desired_state=kwargs['network'], states=(kwargs['original_network'],))) if policy_id is None or policy_id == original_policy_id: get_policy.assert_not_called() get_ports.assert_not_called() validate_policy_for_ports.assert_not_called() else: get_policy.assert_called_once_with(admin_ctxt, id=policy_id) get_ports.assert_called_once_with(self.ctxt, network_id=network_id) validate_policy_for_ports.assert_called_once_with( policy_mock, [port_mock_without_own_policy]) def test_validate_update_network_callback_policy_changed(self): self._test_validate_update_network_callback( policy_id=uuidutils.generate_uuid()) def test_validate_update_network_callback_policy_not_changed(self): policy_id = uuidutils.generate_uuid() self._test_validate_update_network_callback( policy_id=policy_id, original_policy_id=policy_id) def test_validate_update_network_callback_policy_removed(self): self._test_validate_update_network_callback( policy_id=None, original_policy_id=uuidutils.generate_uuid()) def test_validate_policy_for_port_rule_not_valid(self): port = {'id': uuidutils.generate_uuid()} with mock.patch.object( self.qos_plugin.driver_manager, "validate_rule_for_port", return_value=False ): self.policy.rules = [self.rule] self.assertRaises( n_exc.QosRuleNotSupported, self.qos_plugin.validate_policy_for_port, self.policy, port) def test_validate_policy_for_port_all_rules_valid(self): port = {'id': uuidutils.generate_uuid()} with mock.patch.object( self.qos_plugin.driver_manager, "validate_rule_for_port", return_value=True ): self.policy.rules = [self.rule] try: self.qos_plugin.validate_policy_for_port(self.policy, port) except n_exc.QosRuleNotSupported: self.fail("QosRuleNotSupported exception unexpectedly raised") @mock.patch( 'neutron.objects.rbac_db.RbacNeutronDbObjectMixin' '.create_rbac_policy') @mock.patch('neutron.objects.qos.policy.QosPolicy') def test_add_policy(self, mock_qos_policy, mock_create_rbac_policy): mock_manager = mock.Mock() mock_manager.attach_mock(mock_qos_policy, 'QosPolicy') mock_manager.attach_mock(self.qos_plugin.driver_manager, 'driver') mock_manager.reset_mock() self.qos_plugin.create_policy(self.ctxt, self.policy_data) policy_mock_call = mock.call.QosPolicy().create() create_precommit_mock_call = mock.call.driver.call( 'create_policy_precommit', self.ctxt, mock.ANY) create_mock_call = mock.call.driver.call( 'create_policy', self.ctxt, mock.ANY) self.assertTrue( mock_manager.mock_calls.index(policy_mock_call) < mock_manager.mock_calls.index(create_precommit_mock_call) < mock_manager.mock_calls.index(create_mock_call)) def test_add_policy_with_extra_tenant_keyword(self, *mocks): policy_id = uuidutils.generate_uuid() project_id = uuidutils.generate_uuid() tenant_policy = { 'policy': {'id': policy_id, 'project_id': project_id, 'tenant_id': project_id, 'name': 'test-policy', 'description': 'Test policy description', 'shared': True, 'is_default': False}} policy_details = {'id': policy_id, 'project_id': project_id, 'name': 'test-policy', 'description': 'Test policy description', 'shared': True, 'is_default': False} with mock.patch('neutron.objects.qos.policy.QosPolicy') as QosMocked: self.qos_plugin.create_policy(self.ctxt, tenant_policy) QosMocked.assert_called_once_with(self.ctxt, **policy_details) @mock.patch.object(policy_object.QosPolicy, "get_object") @mock.patch( 'neutron.objects.rbac_db.RbacNeutronDbObjectMixin' '.create_rbac_policy') @mock.patch.object(policy_object.QosPolicy, 'update') def test_update_policy(self, mock_qos_policy_update, mock_create_rbac_policy, mock_qos_policy_get): mock_qos_policy_get.return_value = self.policy mock_manager = mock.Mock() mock_manager.attach_mock(mock_qos_policy_update, 'update') mock_manager.attach_mock(self.qos_plugin.driver_manager, 'driver') mock_manager.reset_mock() fields = base_object.get_updatable_fields( policy_object.QosPolicy, self.policy_data['policy']) self.qos_plugin.update_policy( self.ctxt, self.policy.id, {'policy': fields}) self._validate_driver_params('update_policy', self.ctxt) policy_update_mock_call = mock.call.update() update_precommit_mock_call = mock.call.driver.call( 'update_policy_precommit', self.ctxt, mock.ANY) update_mock_call = mock.call.driver.call( 'update_policy', self.ctxt, mock.ANY) self.assertTrue( mock_manager.mock_calls.index(policy_update_mock_call) < mock_manager.mock_calls.index(update_precommit_mock_call) < mock_manager.mock_calls.index(update_mock_call)) @mock.patch('neutron.objects.db.api.get_object', return_value=None) @mock.patch.object(policy_object.QosPolicy, 'delete') def test_delete_policy(self, mock_qos_policy_delete, mock_api_get_policy): mock_manager = mock.Mock() mock_manager.attach_mock(mock_qos_policy_delete, 'delete') mock_manager.attach_mock(self.qos_plugin.driver_manager, 'driver') mock_manager.reset_mock() self.qos_plugin.delete_policy(self.ctxt, self.policy.id) self._validate_driver_params('delete_policy', self.ctxt) policy_delete_mock_call = mock.call.delete() delete_precommit_mock_call = mock.call.driver.call( 'delete_policy_precommit', self.ctxt, mock.ANY) delete_mock_call = mock.call.driver.call( 'delete_policy', self.ctxt, mock.ANY) self.assertTrue( mock_manager.mock_calls.index(policy_delete_mock_call) < mock_manager.mock_calls.index(delete_precommit_mock_call) < mock_manager.mock_calls.index(delete_mock_call)) @mock.patch.object(policy_object.QosPolicy, "get_object") @mock.patch.object(rule_object.QosBandwidthLimitRule, 'create') def test_create_policy_rule(self, mock_qos_rule_create, mock_qos_policy_get): _policy = copy.copy(self.policy) setattr(_policy, "rules", []) mock_qos_policy_get.return_value = _policy mock_manager = mock.Mock() mock_manager.attach_mock(mock_qos_rule_create, 'create') mock_manager.attach_mock(self.qos_plugin.driver_manager, 'driver') mock_manager.reset_mock() with mock.patch( 'neutron.objects.qos.qos_policy_validator' '.check_bandwidth_rule_conflict', return_value=None): self.qos_plugin.create_policy_bandwidth_limit_rule( self.ctxt, self.policy.id, self.rule_data) self._validate_driver_params('update_policy', self.ctxt) rule_create_mock_call = mock.call.create() update_precommit_mock_call = mock.call.driver.call( 'update_policy_precommit', self.ctxt, mock.ANY) update_mock_call = mock.call.driver.call( 'update_policy', self.ctxt, mock.ANY) self.assertTrue( mock_manager.mock_calls.index(rule_create_mock_call) < mock_manager.mock_calls.index(update_precommit_mock_call) < mock_manager.mock_calls.index(update_mock_call)) def test_create_policy_rule_check_rule_min_less_than_max(self): _policy = self._get_policy() setattr(_policy, "rules", [self.rule]) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy) as mock_qos_get_obj: self.qos_plugin.create_policy_minimum_bandwidth_rule( self.ctxt, _policy.id, self.rule_data) self._validate_driver_params('update_policy', self.ctxt) self.mock_qos_load_attr.assert_called_once_with('rules') mock_qos_get_obj.assert_called_once_with(self.ctxt, id=_policy.id) def test_create_policy_rule_check_rule_max_more_than_min(self): _policy = self._get_policy() setattr(_policy, "rules", [self.min_rule]) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy) as mock_qos_get_obj: self.qos_plugin.create_policy_bandwidth_limit_rule( self.ctxt, _policy.id, self.rule_data) self._validate_driver_params('update_policy', self.ctxt) self.mock_qos_load_attr.assert_called_once_with('rules') mock_qos_get_obj.assert_called_once_with(self.ctxt, id=_policy.id) def test_create_policy_rule_check_rule_bwlimit_less_than_minbw(self): _policy = self._get_policy() self.rule_data['bandwidth_limit_rule']['max_kbps'] = 1 setattr(_policy, "rules", [self.min_rule]) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy) as mock_qos_get_obj: self.assertRaises(n_exc.QoSRuleParameterConflict, self.qos_plugin.create_policy_bandwidth_limit_rule, self.ctxt, self.policy.id, self.rule_data) mock_qos_get_obj.assert_called_once_with(self.ctxt, id=_policy.id) def test_create_policy_rule_check_rule_minbw_gr_than_bwlimit(self): _policy = self._get_policy() self.rule_data['minimum_bandwidth_rule']['min_kbps'] = 1000000 setattr(_policy, "rules", [self.rule]) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy) as mock_qos_get_obj: self.assertRaises(n_exc.QoSRuleParameterConflict, self.qos_plugin.create_policy_minimum_bandwidth_rule, self.ctxt, self.policy.id, self.rule_data) mock_qos_get_obj.assert_called_once_with(self.ctxt, id=_policy.id) def test_create_policy_rule_duplicates(self): _policy = self._get_policy() setattr(_policy, "rules", [self.rule]) new_rule_data = { 'bandwidth_limit_rule': { 'max_kbps': 5000, 'direction': self.rule.direction } } with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy) as mock_qos_get_obj: self.assertRaises( n_exc.QoSRulesConflict, self.qos_plugin.create_policy_bandwidth_limit_rule, self.ctxt, _policy.id, new_rule_data) mock_qos_get_obj.assert_called_once_with(self.ctxt, id=_policy.id) @mock.patch.object(rule_object.QosBandwidthLimitRule, 'update') def test_update_policy_rule(self, mock_qos_rule_update): mock_manager = mock.Mock() mock_manager.attach_mock(mock_qos_rule_update, 'update') mock_manager.attach_mock(self.qos_plugin.driver_manager, 'driver') mock_manager.reset_mock() _policy = policy_object.QosPolicy( self.ctxt, **self.policy_data['policy']) setattr(_policy, "rules", [self.rule]) with mock.patch('neutron.objects.qos.rule.get_rules', return_value=[self.rule]), mock.patch( 'neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): self.rule_data['bandwidth_limit_rule']['max_kbps'] = 1 self.qos_plugin.update_policy_bandwidth_limit_rule( self.ctxt, self.rule.id, self.policy.id, self.rule_data) self._validate_driver_params('update_policy', self.ctxt) rule_update_mock_call = mock.call.update() update_precommit_mock_call = mock.call.driver.call( 'update_policy_precommit', self.ctxt, mock.ANY) update_mock_call = mock.call.driver.call( 'update_policy', self.ctxt, mock.ANY) self.assertTrue( mock_manager.mock_calls.index(rule_update_mock_call) < mock_manager.mock_calls.index(update_precommit_mock_call) < mock_manager.mock_calls.index(update_mock_call)) def test_update_policy_rule_check_rule_min_less_than_max(self): _policy = self._get_policy() setattr(_policy, "rules", [self.rule]) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): self.qos_plugin.update_policy_bandwidth_limit_rule( self.ctxt, self.rule.id, self.policy.id, self.rule_data) self.mock_qos_load_attr.assert_called_once_with('rules') self._validate_driver_params('update_policy', self.ctxt) rules = [self.rule, self.min_rule] setattr(_policy, "rules", rules) self.mock_qos_load_attr.reset_mock() with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): self.qos_plugin.update_policy_minimum_bandwidth_rule( self.ctxt, self.min_rule.id, self.policy.id, self.rule_data) self.mock_qos_load_attr.assert_called_once_with('rules') self._validate_driver_params('update_policy', self.ctxt) def test_update_policy_rule_check_rule_bwlimit_less_than_minbw(self): _policy = self._get_policy() setattr(_policy, "rules", [self.rule]) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): self.qos_plugin.update_policy_bandwidth_limit_rule( self.ctxt, self.rule.id, self.policy.id, self.rule_data) self.mock_qos_load_attr.assert_called_once_with('rules') self._validate_driver_params('update_policy', self.ctxt) self.rule_data['minimum_bandwidth_rule']['min_kbps'] = 1000 with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): self.assertRaises( n_exc.QoSRuleParameterConflict, self.qos_plugin.update_policy_minimum_bandwidth_rule, self.ctxt, self.min_rule.id, self.policy.id, self.rule_data) def test_update_policy_rule_check_rule_minbw_gr_than_bwlimit(self): _policy = self._get_policy() setattr(_policy, "rules", [self.min_rule]) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): self.qos_plugin.update_policy_minimum_bandwidth_rule( self.ctxt, self.min_rule.id, self.policy.id, self.rule_data) self.mock_qos_load_attr.assert_called_once_with('rules') self._validate_driver_params('update_policy', self.ctxt) self.rule_data['bandwidth_limit_rule']['max_kbps'] = 1 with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): self.assertRaises( n_exc.QoSRuleParameterConflict, self.qos_plugin.update_policy_bandwidth_limit_rule, self.ctxt, self.rule.id, self.policy.id, self.rule_data) def _get_policy(self): return policy_object.QosPolicy( self.ctxt, **self.policy_data['policy']) def test_update_policy_rule_bad_policy(self): _policy = policy_object.QosPolicy( self.ctxt, **self.policy_data['policy']) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): setattr(_policy, "rules", []) self.assertRaises( n_exc.QosRuleNotFound, self.qos_plugin.update_policy_bandwidth_limit_rule, self.ctxt, self.rule.id, self.policy.id, self.rule_data) @mock.patch.object(rule_object.QosBandwidthLimitRule, 'delete') def test_delete_policy_rule(self, mock_qos_rule_delete): mock_manager = mock.Mock() mock_manager.attach_mock(mock_qos_rule_delete, 'delete') mock_manager.attach_mock(self.qos_plugin.driver_manager, 'driver') mock_manager.reset_mock() _policy = policy_object.QosPolicy( self.ctxt, **self.policy_data['policy']) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): setattr(_policy, "rules", [self.rule]) self.qos_plugin.delete_policy_bandwidth_limit_rule( self.ctxt, self.rule.id, _policy.id) self._validate_driver_params('update_policy', self.ctxt) rule_delete_mock_call = mock.call.delete() update_precommit_mock_call = mock.call.driver.call( 'update_policy_precommit', self.ctxt, mock.ANY) update_mock_call = mock.call.driver.call( 'update_policy', self.ctxt, mock.ANY) self.assertTrue( mock_manager.mock_calls.index(rule_delete_mock_call) < mock_manager.mock_calls.index(update_precommit_mock_call) < mock_manager.mock_calls.index(update_mock_call)) def test_delete_policy_rule_bad_policy(self): _policy = policy_object.QosPolicy( self.ctxt, **self.policy_data['policy']) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): setattr(_policy, "rules", []) self.assertRaises( n_exc.QosRuleNotFound, self.qos_plugin.delete_policy_bandwidth_limit_rule, self.ctxt, self.rule.id, _policy.id) def test_get_policy_bandwidth_limit_rule(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=self.policy): with mock.patch('neutron.objects.qos.rule.' 'QosBandwidthLimitRule.' 'get_object') as get_object_mock: self.qos_plugin.get_policy_bandwidth_limit_rule( self.ctxt, self.rule.id, self.policy.id) get_object_mock.assert_called_once_with(self.ctxt, id=self.rule.id) def test_get_policy_bandwidth_limit_rules_for_policy(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=self.policy): with mock.patch('neutron.objects.qos.rule.' 'QosBandwidthLimitRule.' 'get_objects') as get_objects_mock: self.qos_plugin.get_policy_bandwidth_limit_rules( self.ctxt, self.policy.id) get_objects_mock.assert_called_once_with( self.ctxt, _pager=mock.ANY, qos_policy_id=self.policy.id) def test_get_policy_bandwidth_limit_rules_for_policy_with_filters(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=self.policy): with mock.patch('neutron.objects.qos.rule.' 'QosBandwidthLimitRule.' 'get_objects') as get_objects_mock: filters = {'filter': 'filter_id'} self.qos_plugin.get_policy_bandwidth_limit_rules( self.ctxt, self.policy.id, filters=filters) get_objects_mock.assert_called_once_with( self.ctxt, _pager=mock.ANY, qos_policy_id=self.policy.id, filter='filter_id') def test_get_policy_for_nonexistent_policy(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=None): self.assertRaises( n_exc.QosPolicyNotFound, self.qos_plugin.get_policy, self.ctxt, self.policy.id) def test_get_policy_bandwidth_limit_rule_for_nonexistent_policy(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=None): self.assertRaises( n_exc.QosPolicyNotFound, self.qos_plugin.get_policy_bandwidth_limit_rule, self.ctxt, self.rule.id, self.policy.id) def test_get_policy_bandwidth_limit_rules_for_nonexistent_policy(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=None): self.assertRaises( n_exc.QosPolicyNotFound, self.qos_plugin.get_policy_bandwidth_limit_rules, self.ctxt, self.policy.id) def test_create_policy_dscp_marking_rule(self): _policy = policy_object.QosPolicy( self.ctxt, **self.policy_data['policy']) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): setattr(_policy, "rules", [self.dscp_rule]) self.qos_plugin.create_policy_dscp_marking_rule( self.ctxt, self.policy.id, self.rule_data) self._validate_driver_params('update_policy', self.ctxt) def test_update_policy_dscp_marking_rule(self): _policy = policy_object.QosPolicy( self.ctxt, **self.policy_data['policy']) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): setattr(_policy, "rules", [self.dscp_rule]) self.qos_plugin.update_policy_dscp_marking_rule( self.ctxt, self.dscp_rule.id, self.policy.id, self.rule_data) self._validate_driver_params('update_policy', self.ctxt) def test_delete_policy_dscp_marking_rule(self): _policy = policy_object.QosPolicy( self.ctxt, **self.policy_data['policy']) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): setattr(_policy, "rules", [self.dscp_rule]) self.qos_plugin.delete_policy_dscp_marking_rule( self.ctxt, self.dscp_rule.id, self.policy.id) self._validate_driver_params('update_policy', self.ctxt) def test_get_policy_dscp_marking_rules(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=self.policy): with mock.patch('neutron.objects.qos.rule.' 'QosDscpMarkingRule.' 'get_objects') as get_objects_mock: self.qos_plugin.get_policy_dscp_marking_rules( self.ctxt, self.policy.id) get_objects_mock.assert_called_once_with( self.ctxt, _pager=mock.ANY, qos_policy_id=self.policy.id) def test_get_policy_dscp_marking_rules_for_policy_with_filters(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=self.policy): with mock.patch('neutron.objects.qos.rule.' 'QosDscpMarkingRule.' 'get_objects') as get_objects_mock: filters = {'filter': 'filter_id'} self.qos_plugin.get_policy_dscp_marking_rules( self.ctxt, self.policy.id, filters=filters) get_objects_mock.assert_called_once_with( self.ctxt, qos_policy_id=self.policy.id, _pager=mock.ANY, filter='filter_id') def test_get_policy_dscp_marking_rule_for_nonexistent_policy(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=None): self.assertRaises( n_exc.QosPolicyNotFound, self.qos_plugin.get_policy_dscp_marking_rule, self.ctxt, self.dscp_rule.id, self.policy.id) def test_get_policy_dscp_marking_rules_for_nonexistent_policy(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=None): self.assertRaises( n_exc.QosPolicyNotFound, self.qos_plugin.get_policy_dscp_marking_rules, self.ctxt, self.policy.id) def test_get_policy_minimum_bandwidth_rule(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=self.policy): with mock.patch('neutron.objects.qos.rule.' 'QosMinimumBandwidthRule.' 'get_object') as get_object_mock: self.qos_plugin.get_policy_minimum_bandwidth_rule( self.ctxt, self.rule.id, self.policy.id) get_object_mock.assert_called_once_with(self.ctxt, id=self.rule.id) def test_get_policy_minimum_bandwidth_rules_for_policy(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=self.policy): with mock.patch('neutron.objects.qos.rule.' 'QosMinimumBandwidthRule.' 'get_objects') as get_objects_mock: self.qos_plugin.get_policy_minimum_bandwidth_rules( self.ctxt, self.policy.id) get_objects_mock.assert_called_once_with( self.ctxt, _pager=mock.ANY, qos_policy_id=self.policy.id) def test_get_policy_minimum_bandwidth_rules_for_policy_with_filters(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=self.policy): with mock.patch('neutron.objects.qos.rule.' 'QosMinimumBandwidthRule.' 'get_objects') as get_objects_mock: filters = {'filter': 'filter_id'} self.qos_plugin.get_policy_minimum_bandwidth_rules( self.ctxt, self.policy.id, filters=filters) get_objects_mock.assert_called_once_with( self.ctxt, _pager=mock.ANY, qos_policy_id=self.policy.id, filter='filter_id') def test_get_policy_minimum_bandwidth_rule_for_nonexistent_policy(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=None): self.assertRaises( n_exc.QosPolicyNotFound, self.qos_plugin.get_policy_minimum_bandwidth_rule, self.ctxt, self.rule.id, self.policy.id) def test_get_policy_minimum_bandwidth_rules_for_nonexistent_policy(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=None): self.assertRaises( n_exc.QosPolicyNotFound, self.qos_plugin.get_policy_minimum_bandwidth_rules, self.ctxt, self.policy.id) def test_create_policy_rule_for_nonexistent_policy(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=None): self.assertRaises( n_exc.QosPolicyNotFound, self.qos_plugin.create_policy_bandwidth_limit_rule, self.ctxt, self.policy.id, self.rule_data) def test_update_policy_rule_for_nonexistent_policy(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=None): self.assertRaises( n_exc.QosPolicyNotFound, self.qos_plugin.update_policy_bandwidth_limit_rule, self.ctxt, self.rule.id, self.policy.id, self.rule_data) def test_delete_policy_rule_for_nonexistent_policy(self): with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=None): self.assertRaises( n_exc.QosPolicyNotFound, self.qos_plugin.delete_policy_bandwidth_limit_rule, self.ctxt, self.rule.id, self.policy.id) def test_verify_bad_method_call(self): self.assertRaises(AttributeError, getattr, self.qos_plugin, 'create_policy_bandwidth_limit_rules') def test_get_rule_type(self): admin_ctxt = context.get_admin_context() drivers_details = [{ 'name': 'fake-driver', 'supported_parameters': [{ 'parameter_name': 'max_kbps', 'parameter_type': constants.VALUES_TYPE_RANGE, 'parameter_range': {'start': 0, 'end': 100} }] }] with mock.patch.object( qos_plugin.QoSPlugin, "supported_rule_type_details", return_value=drivers_details ): rule_type_details = self.qos_plugin.get_rule_type( admin_ctxt, qos_consts.RULE_TYPE_BANDWIDTH_LIMIT) self.assertEqual( qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, rule_type_details['type']) self.assertEqual( drivers_details, rule_type_details['drivers']) def test_get_rule_type_as_user(self): self.assertRaises( lib_exc.NotAuthorized, self.qos_plugin.get_rule_type, self.ctxt, qos_consts.RULE_TYPE_BANDWIDTH_LIMIT) def test_get_rule_types(self): rule_types_mock = mock.PropertyMock( return_value=qos_consts.VALID_RULE_TYPES) filters = {'type': 'type_id'} with mock.patch.object(qos_plugin.QoSPlugin, 'supported_rule_types', new_callable=rule_types_mock): types = self.qos_plugin.get_rule_types(self.ctxt, filters=filters) self.assertEqual(sorted(qos_consts.VALID_RULE_TYPES), sorted(type_['type'] for type_ in types)) @mock.patch('neutron.objects.ports.Port') @mock.patch('neutron.objects.qos.policy.QosPolicy') def test_rule_notification_and_driver_ordering(self, qos_policy_mock, port_mock): rule_cls_mock = mock.Mock() rule_cls_mock.rule_type = 'fake' rule_actions = {'create': [self.ctxt, rule_cls_mock, self.policy.id, {'fake_rule': {}}], 'update': [self.ctxt, rule_cls_mock, self.rule.id, self.policy.id, {'fake_rule': {}}], 'delete': [self.ctxt, rule_cls_mock, self.rule.id, self.policy.id]} mock_manager = mock.Mock() mock_manager.attach_mock(qos_policy_mock, 'QosPolicy') mock_manager.attach_mock(port_mock, 'Port') mock_manager.attach_mock(rule_cls_mock, 'RuleCls') mock_manager.attach_mock(self.qos_plugin.driver_manager, 'driver') for action, arguments in rule_actions.items(): mock_manager.reset_mock() method = getattr(self.qos_plugin, "%s_policy_rule" % action) method(*arguments) # some actions get rule from policy get_rule_mock_call = getattr( mock.call.QosPolicy.get_object().get_rule_by_id(), action)() # some actions construct rule from class reference rule_mock_call = getattr(mock.call.RuleCls(), action)() driver_mock_call = mock.call.driver.call('update_policy', self.ctxt, mock.ANY) if rule_mock_call in mock_manager.mock_calls: action_index = mock_manager.mock_calls.index(rule_mock_call) else: action_index = mock_manager.mock_calls.index( get_rule_mock_call) self.assertTrue( action_index < mock_manager.mock_calls.index(driver_mock_call)) neutron-12.1.1/neutron/tests/unit/services/l3_router/0000775000175000017500000000000013553660157022677 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/l3_router/service_providers/0000775000175000017500000000000013553660157026434 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/l3_router/service_providers/test_driver_controller.py0000664000175000017500000002320013553660047033576 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import events from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions as lib_exc from neutron_lib.plugins import constants as p_cons from neutron_lib.plugins import directory from oslo_utils import uuidutils import testtools from neutron.services.l3_router.service_providers import driver_controller from neutron.services import provider_configuration from neutron.tests import base from neutron.tests.unit import testlib_api DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' class TestDriverController(testlib_api.SqlTestCase): def setUp(self): super(TestDriverController, self).setUp() self.setup_coreplugin(DB_PLUGIN_KLASS) self.fake_l3 = mock.Mock() self.dc = driver_controller.DriverController(self.fake_l3) self.fake_l3.l3_driver_controller = self.dc self.ctx = context.get_admin_context() def _return_provider_for_flavor(self, provider): self.dc._flavor_plugin_ref = mock.Mock() self.dc._flavor_plugin_ref.get_flavor.return_value = {'id': 'abc'} provider = {'provider': provider} self.dc._flavor_plugin_ref.get_flavor_next_provider.return_value = [ provider] def test_uses_scheduler(self): self._return_provider_for_flavor('dvrha') router_db = mock.Mock() flavor_id = uuidutils.generate_uuid() router_id = uuidutils.generate_uuid() router = dict(id=router_id, flavor_id=flavor_id) self.dc._set_router_provider('router', 'PRECOMMIT_CREATE', self, self.ctx, router, router_db) self.assertTrue(self.dc.uses_scheduler(self.ctx, router_id)) self.dc.drivers['dvrha'].use_integrated_agent_scheduler = False self.assertFalse(self.dc.uses_scheduler(self.ctx, router_id)) def test_driver_owns_router(self): self._return_provider_for_flavor('dvrha') router_db = mock.Mock() flavor_id = uuidutils.generate_uuid() r1 = uuidutils.generate_uuid() r2 = uuidutils.generate_uuid() router = dict(id=r1, flavor_id=flavor_id) self.dc._set_router_provider('router', 'PRECOMMIT_CREATE', self, self.ctx, router, router_db) self.assertTrue(self.dc.drivers['dvrha'].owns_router(self.ctx, r1)) self.assertFalse(self.dc.drivers['dvr'].owns_router(self.ctx, r1)) self.assertFalse(self.dc.drivers['dvr'].owns_router(self.ctx, r2)) self.assertFalse(self.dc.drivers['dvr'].owns_router(self.ctx, None)) def test__set_router_provider_flavor_specified(self): self._return_provider_for_flavor('dvrha') router_db = mock.Mock() flavor_id = uuidutils.generate_uuid() router_id = uuidutils.generate_uuid() router = dict(id=router_id, flavor_id=flavor_id) self.dc._set_router_provider('router', 'PRECOMMIT_CREATE', self, self.ctx, router, router_db) self.assertEqual(flavor_id, router_db.flavor_id) self.assertEqual(self.dc.drivers['dvrha'], self.dc.get_provider_for_router(self.ctx, router_id)) def test__update_router_provider_invalid(self): test_dc = driver_controller.DriverController(self.fake_l3) with mock.patch.object(test_dc, "get_provider_for_router"): with mock.patch.object( driver_controller, "_ensure_driver_supports_request") as _ensure: _ensure.side_effect = lib_exc.InvalidInput( error_message='message') self.assertRaises( lib_exc.InvalidInput, test_dc._update_router_provider, None, None, None, payload=events.DBEventPayload( None, request_body={'name': 'testname'}, states=({'flavor_id': 'old_fid'},))) def test__update_router_provider_with_flags(self): test_dc = driver_controller.DriverController(self.fake_l3) with mock.patch.object(test_dc, "get_provider_for_router"): with mock.patch.object( driver_controller, "_ensure_driver_supports_request") as _ensure: _ensure.side_effect = lib_exc.InvalidInput( error_message='message') with mock.patch( "neutron.services.l3_router.service_providers." "driver_controller.LOG.debug") as mock_log: self.assertRaises( lib_exc.InvalidInput, test_dc._update_router_provider, None, None, None, payload=events.DBEventPayload( None, request_body={'name': 'testname', 'distributed': False}, states=({'flavor_id': None, 'distributed': True, 'ha': False},))) # To validate that the 'ha' attribute of the router # stays unchanged from the previous state while # updating 'distributed' from True to False. mock_log.assert_any_call( "Get a provider driver handle based on the ha " "flag: %(ha_flag)s and distributed flag: " "%(distributed_flag)s", {'ha_flag': False, 'distributed_flag': False}) def test__set_router_provider_attr_lookups(self): # ensure correct drivers are looked up based on attrs router_id1 = uuidutils.generate_uuid() router_id2 = uuidutils.generate_uuid() router_id3 = uuidutils.generate_uuid() router_id4 = uuidutils.generate_uuid() router_id5 = uuidutils.generate_uuid() router_id6 = uuidutils.generate_uuid() router_id7 = uuidutils.generate_uuid() router_id8 = uuidutils.generate_uuid() router_id9 = uuidutils.generate_uuid() cases = [ ('dvrha', dict(id=router_id1, distributed=True, ha=True)), ('dvr', dict(id=router_id2, distributed=True, ha=False)), ('ha', dict(id=router_id3, distributed=False, ha=True)), ('single_node', dict(id=router_id4, distributed=False, ha=False)), ('ha', dict(id=router_id5, ha=True, distributed=constants.ATTR_NOT_SPECIFIED)), ('dvr', dict(id=router_id6, distributed=True, ha=constants.ATTR_NOT_SPECIFIED)), ('single_node', dict(id=router_id7, ha=False, distributed=constants.ATTR_NOT_SPECIFIED)), ('single_node', dict(id=router_id8, distributed=False, ha=constants.ATTR_NOT_SPECIFIED)), ('single_node', dict(id=router_id9, distributed=constants.ATTR_NOT_SPECIFIED, ha=constants.ATTR_NOT_SPECIFIED)), ] for driver, body in cases: self.dc._set_router_provider('router', 'PRECOMMIT_CREATE', self, self.ctx, body, mock.Mock()) self.assertEqual(self.dc.drivers[driver], self.dc.get_provider_for_router(self.ctx, body['id']), 'Expecting %s for body %s' % (driver, body)) def test__clear_router_provider(self): # ensure correct drivers are looked up based on attrs router_id1 = uuidutils.generate_uuid() body = dict(id=router_id1, distributed=True, ha=True) self.dc._set_router_provider('router', 'PRECOMMIT_CREATE', self, self.ctx, body, mock.Mock()) self.assertEqual(self.dc.drivers['dvrha'], self.dc.get_provider_for_router(self.ctx, body['id'])) self.dc._clear_router_provider('router', 'PRECOMMIT_DELETE', self, self.ctx, body['id']) with testtools.ExpectedException(ValueError): # if association was cleared, get_router will be called self.fake_l3.get_router.side_effect = ValueError self.dc.get_provider_for_router(self.ctx, body['id']) def test__flavor_plugin(self): directory.add_plugin(p_cons.FLAVORS, mock.Mock()) _dc = driver_controller.DriverController(self.fake_l3) self.assertEqual( directory.get_plugin(p_cons.FLAVORS), _dc._flavor_plugin) class Test_LegacyPlusProviderConfiguration(base.BaseTestCase): @mock.patch.object(provider_configuration.ProviderConfiguration, "add_provider") def test__update_router_provider_invalid(self, mock_method): mock_method.side_effect = lib_exc.Invalid(message='message') driver_controller._LegacyPlusProviderConfiguration() neutron-12.1.1/neutron/tests/unit/services/l3_router/service_providers/__init__.py0000664000175000017500000000000013553660046030530 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/l3_router/__init__.py0000664000175000017500000000000013553660046024773 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/l3_router/test_l3_router_plugin.py0000664000175000017500000000225013553660046027600 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron.services.l3_router import l3_router_plugin as lrp from neutron.tests import base class TestL3PluginDvrConditional(base.BaseTestCase): def _test_dvr_alias_exposed(self, enabled): cfg.CONF.set_override('enable_dvr', enabled) plugin = lrp.L3RouterPlugin() exposed = 'dvr' in plugin.supported_extension_aliases self.assertEqual(enabled, exposed) def test_dvr_alias_exposed_enabled(self): self._test_dvr_alias_exposed(enabled=True) def test_dvr_alias_exposed_disabled(self): self._test_dvr_alias_exposed(enabled=False) neutron-12.1.1/neutron/tests/unit/services/__init__.py0000664000175000017500000000000013553660046023055 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/metering/0000775000175000017500000000000013553660157022573 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/metering/test_metering_plugin.py0000664000175000017500000006066713553660047027411 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import metering as metering_apidef from neutron_lib import context from neutron_lib.plugins import constants from neutron_lib.plugins import directory from oslo_utils import uuidutils from neutron.api.rpc.agentnotifiers import metering_rpc_agent_api from neutron.common import utils from neutron.db import api as db_api from neutron.db.metering import metering_rpc from neutron.extensions import l3 as ext_l3 from neutron.extensions import metering as ext_metering from neutron.objects import agent as agent_obj from neutron.tests.common import helpers from neutron.tests import tools from neutron.tests.unit.db.metering import test_metering_db from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit.extensions import test_l3 _uuid = uuidutils.generate_uuid METERING_SERVICE_PLUGIN_KLASS = ( "neutron.services.metering." "metering_plugin.MeteringPlugin" ) class MeteringTestExtensionManager(object): def get_resources(self): l3_res = ext_l3.L3.get_resources() metering_res = ext_metering.Metering.get_resources() return l3_res + metering_res def get_actions(self): return [] def get_request_extensions(self): return [] # TODO(akamyshnikova):we need this temporary FakeContext class while Context # checking for existence of session attribute. class FakeContext(context.ContextBaseWithSession): def __init__(self, *args, **kwargs): super(FakeContext, self).__init__(*args, **kwargs) self._session = None @property def session(self): if self._session is None: self._session = db_api.get_writer_session() return self._session class TestMeteringPlugin(test_db_base_plugin_v2.NeutronDbPluginV2TestCase, test_l3.L3NatTestCaseMixin, test_metering_db.MeteringPluginDbTestCaseMixin): resource_prefix_map = dict( (k.replace('_', '-'), "/metering") for k in metering_apidef.RESOURCE_ATTRIBUTE_MAP.keys() ) def setUp(self): plugin = 'neutron.tests.unit.extensions.test_l3.TestL3NatIntPlugin' service_plugins = {'metering_plugin_name': METERING_SERVICE_PLUGIN_KLASS} ext_mgr = MeteringTestExtensionManager() super(TestMeteringPlugin, self).setUp(plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) self.uuid = '654f6b9d-0f36-4ae5-bd1b-01616794ca60' uuid = 'oslo_utils.uuidutils.generate_uuid' self.uuid_patch = mock.patch(uuid, return_value=self.uuid) self.mock_uuid = self.uuid_patch.start() self.tenant_id = 'a7e61382-47b8-4d40-bae3-f95981b5637b' self.ctx = FakeContext('', self.tenant_id, is_admin=True) self.context_patch = mock.patch('neutron_lib.context.Context', return_value=self.ctx) self.mock_context = self.context_patch.start() self.topic = 'metering_agent' add = ('neutron.api.rpc.agentnotifiers.' + 'metering_rpc_agent_api.MeteringAgentNotifyAPI' + '.add_metering_label') self.add_patch = mock.patch(add) self.mock_add = self.add_patch.start() remove = ('neutron.api.rpc.agentnotifiers.' + 'metering_rpc_agent_api.MeteringAgentNotifyAPI' + '.remove_metering_label') self.remove_patch = mock.patch(remove) self.mock_remove = self.remove_patch.start() update = ('neutron.api.rpc.agentnotifiers.' + 'metering_rpc_agent_api.MeteringAgentNotifyAPI' + '.update_metering_label_rules') self.update_patch = mock.patch(update) self.mock_update = self.update_patch.start() add_rule = ('neutron.api.rpc.agentnotifiers.' + 'metering_rpc_agent_api.MeteringAgentNotifyAPI' + '.add_metering_label_rule') self.add_rule_patch = mock.patch(add_rule) self.mock_add_rule = self.add_rule_patch.start() remove_rule = ('neutron.api.rpc.agentnotifiers.' + 'metering_rpc_agent_api.MeteringAgentNotifyAPI' + '.remove_metering_label_rule') self.remove_rule_patch = mock.patch(remove_rule) self.mock_remove_rule = self.remove_rule_patch.start() def test_routers_updated_on_host_rpc_call(self): router_test = { 'id': 'xyz', 'name': 'testrouter'} notify_host = ('neutron.api.rpc.agentnotifiers.' + 'metering_rpc_agent_api.MeteringAgentNotifyAPI' + '._notification_host') self.notify_patch = mock.patch(notify_host) self.mock_notify_host = self.notify_patch.start() metering_rpc_handle = metering_rpc_agent_api.MeteringAgentNotifyAPI() metering_rpc_handle.routers_updated_on_host( self.ctx, [router_test['id']], 'test_host') self.mock_notify_host.assert_called_with(self.ctx, 'routers_updated', 'test_host', routers=['xyz']) def test_add_metering_label_rpc_call(self): second_uuid = 'e27fe2df-376e-4ac7-ae13-92f050a21f84' expected = [{'status': 'ACTIVE', 'name': 'router1', 'gw_port_id': None, 'admin_state_up': True, 'distributed': False, 'tenant_id': self.tenant_id, '_metering_labels': [ {'rules': [], 'id': self.uuid}], 'id': self.uuid}] tenant_id_2 = '8a268a58-1610-4890-87e0-07abb8231206' self.mock_uuid.return_value = second_uuid with self.router(name='router2', tenant_id=tenant_id_2, set_context=True): self.mock_uuid.return_value = self.uuid with self.router(name='router1', tenant_id=self.tenant_id, set_context=True): with self.metering_label(tenant_id=self.tenant_id, set_context=True): self.mock_add.assert_called_with(self.ctx, expected) def test_add_metering_label_shared_rpc_call(self): second_uuid = 'e27fe2df-376e-4ac7-ae13-92f050a21f84' expected = [{'status': 'ACTIVE', 'name': 'router1', 'gw_port_id': None, 'admin_state_up': True, 'distributed': False, 'tenant_id': self.tenant_id, '_metering_labels': [ {'rules': [], 'id': self.uuid}, {'rules': [], 'id': second_uuid}], 'id': self.uuid}] tenant_id_2 = '8a268a58-1610-4890-87e0-07abb8231206' with self.router(name='router1', tenant_id=self.tenant_id, set_context=True): with self.metering_label(tenant_id=self.tenant_id, set_context=True): self.mock_uuid.return_value = second_uuid with self.metering_label(tenant_id=tenant_id_2, shared=True, set_context=True): self.mock_add.assert_called_with(self.ctx, expected) def test_remove_metering_label_rpc_call(self): expected = [{'status': 'ACTIVE', 'name': 'router1', 'gw_port_id': None, 'admin_state_up': True, 'distributed': False, 'tenant_id': self.tenant_id, '_metering_labels': [ {'rules': [], 'id': self.uuid}], 'id': self.uuid}] with self.router(tenant_id=self.tenant_id, set_context=True): with self.metering_label(tenant_id=self.tenant_id, set_context=True) as label: self.mock_add.assert_called_with(self.ctx, expected) self._delete('metering-labels', label['metering_label']['id']) self.mock_remove.assert_called_with(self.ctx, expected) def test_remove_one_metering_label_rpc_call(self): second_uuid = 'e27fe2df-376e-4ac7-ae13-92f050a21f84' expected_add = [{'status': 'ACTIVE', 'name': 'router1', 'gw_port_id': None, 'admin_state_up': True, 'distributed': False, 'tenant_id': self.tenant_id, '_metering_labels': [ {'rules': [], 'id': self.uuid}, {'rules': [], 'id': second_uuid}], 'id': self.uuid}] expected_remove = [{'status': 'ACTIVE', 'name': 'router1', 'gw_port_id': None, 'admin_state_up': True, 'distributed': False, 'tenant_id': self.tenant_id, '_metering_labels': [ {'rules': [], 'id': second_uuid}], 'id': self.uuid}] with self.router(tenant_id=self.tenant_id, set_context=True): with self.metering_label(tenant_id=self.tenant_id, set_context=True): self.mock_uuid.return_value = second_uuid with self.metering_label(tenant_id=self.tenant_id, set_context=True) as label: self.mock_add.assert_called_with(self.ctx, expected_add) self._delete('metering-labels', label['metering_label']['id']) self.mock_remove.assert_called_with(self.ctx, expected_remove) def test_add_and_remove_metering_label_rule_rpc_call(self): second_uuid = 'e27fe2df-376e-4ac7-ae13-92f050a21f84' expected_add = [{'status': 'ACTIVE', 'name': 'router1', 'gw_port_id': None, 'admin_state_up': True, 'distributed': False, 'tenant_id': self.tenant_id, '_metering_labels': [ {'rule': { 'remote_ip_prefix': utils.AuthenticIPNetwork( '10.0.0.0/24'), 'direction': 'ingress', 'metering_label_id': self.uuid, 'excluded': False, 'id': second_uuid}, 'id': self.uuid}], 'id': self.uuid}] expected_del = [{'status': 'ACTIVE', 'name': 'router1', 'gw_port_id': None, 'admin_state_up': True, 'distributed': False, 'tenant_id': self.tenant_id, '_metering_labels': [ {'rule': { 'remote_ip_prefix': utils.AuthenticIPNetwork( '10.0.0.0/24'), 'direction': 'ingress', 'metering_label_id': self.uuid, 'excluded': False, 'id': second_uuid}, 'id': self.uuid}], 'id': self.uuid}] with self.router(tenant_id=self.tenant_id, set_context=True): with self.metering_label(tenant_id=self.tenant_id, set_context=True) as label: l = label['metering_label'] self.mock_uuid.return_value = second_uuid with self.metering_label_rule(l['id']): self.mock_add_rule.assert_called_with(self.ctx, expected_add) self._delete('metering-label-rules', second_uuid) self.mock_remove_rule.assert_called_with(self.ctx, expected_del) def test_delete_metering_label_does_not_clear_router_tenant_id(self): tenant_id = '654f6b9d-0f36-4ae5-bd1b-01616794ca60' with self.metering_label(tenant_id=tenant_id) as metering_label: with self.router(tenant_id=tenant_id, set_context=True) as r: router = self._show('routers', r['router']['id']) self.assertEqual(tenant_id, router['router']['tenant_id']) metering_label_id = metering_label['metering_label']['id'] self._delete('metering-labels', metering_label_id, 204) router = self._show('routers', r['router']['id']) self.assertEqual(tenant_id, router['router']['tenant_id']) class TestMeteringPluginL3AgentScheduler( test_db_base_plugin_v2.NeutronDbPluginV2TestCase, test_l3.L3NatTestCaseMixin, test_metering_db.MeteringPluginDbTestCaseMixin): resource_prefix_map = dict( (k.replace('_', '-'), "/metering") for k in metering_apidef.RESOURCE_ATTRIBUTE_MAP.keys() ) def setUp(self, plugin_str=None, service_plugins=None, scheduler=None): if not plugin_str: plugin_str = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatIntAgentSchedulingPlugin') if not service_plugins: service_plugins = {'metering_plugin_name': METERING_SERVICE_PLUGIN_KLASS} if not scheduler: scheduler = plugin_str ext_mgr = MeteringTestExtensionManager() super(TestMeteringPluginL3AgentScheduler, self).setUp(plugin=plugin_str, ext_mgr=ext_mgr, service_plugins=service_plugins) self.uuid = '654f6b9d-0f36-4ae5-bd1b-01616794ca60' uuid = 'oslo_utils.uuidutils.generate_uuid' self.uuid_patch = mock.patch(uuid, return_value=self.uuid) self.mock_uuid = self.uuid_patch.start() self.tenant_id = 'a7e61382-47b8-4d40-bae3-f95981b5637b' self.ctx = FakeContext('', self.tenant_id, is_admin=True) self.context_patch = mock.patch('neutron_lib.context.Context', return_value=self.ctx) self.mock_context = self.context_patch.start() self.l3routers_patch = mock.patch(scheduler + '.get_l3_agents_hosting_routers') self.l3routers_mock = self.l3routers_patch.start() self.topic = 'metering_agent' add = ('neutron.api.rpc.agentnotifiers.' + 'metering_rpc_agent_api.MeteringAgentNotifyAPI' + '.add_metering_label') self.add_patch = mock.patch(add) self.mock_add = self.add_patch.start() remove = ('neutron.api.rpc.agentnotifiers.' + 'metering_rpc_agent_api.MeteringAgentNotifyAPI' + '.remove_metering_label') self.remove_patch = mock.patch(remove) self.mock_remove = self.remove_patch.start() def test_add_metering_label_rpc_call(self): second_uuid = 'e27fe2df-376e-4ac7-ae13-92f050a21f84' expected = [{'status': 'ACTIVE', 'name': 'router1', 'gw_port_id': None, 'admin_state_up': True, 'distributed': False, 'tenant_id': self.tenant_id, '_metering_labels': [ {'rules': [], 'id': second_uuid}], 'id': self.uuid}, {'status': 'ACTIVE', 'name': 'router2', 'gw_port_id': None, 'admin_state_up': True, 'distributed': False, 'tenant_id': self.tenant_id, '_metering_labels': [ {'rules': [], 'id': second_uuid}], 'id': second_uuid}] # bind each router to a specific agent agent1 = agent_obj.Agent(mock.ANY, host='agent1') agent2 = agent_obj.Agent(mock.ANY, host='agent2') agents = {self.uuid: agent1, second_uuid: agent2} def side_effect(context, routers, admin_state_up, active): return [agents[routers[0]]] self.l3routers_mock.side_effect = side_effect with self.router(name='router1', tenant_id=self.tenant_id, set_context=True): self.mock_uuid.return_value = second_uuid with self.router(name='router2', tenant_id=self.tenant_id, set_context=True): with self.metering_label(tenant_id=self.tenant_id, set_context=True): self.mock_add.assert_called_with( self.ctx, tools.UnorderedList(expected)) class TestMeteringPluginL3AgentSchedulerServicePlugin( TestMeteringPluginL3AgentScheduler): """Unit tests for the case where separate service plugin implements L3 routing. """ def setUp(self): l3_plugin = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatAgentSchedulingServicePlugin') service_plugins = {'metering_plugin_name': METERING_SERVICE_PLUGIN_KLASS, 'l3_plugin_name': l3_plugin} plugin_str = ('neutron.tests.unit.extensions.test_l3.' 'TestNoL3NatPlugin') super(TestMeteringPluginL3AgentSchedulerServicePlugin, self).setUp( plugin_str=plugin_str, service_plugins=service_plugins, scheduler=l3_plugin) class TestMeteringPluginRpcFromL3Agent( test_db_base_plugin_v2.NeutronDbPluginV2TestCase, test_l3.L3NatTestCaseMixin, test_metering_db.MeteringPluginDbTestCaseMixin): resource_prefix_map = dict( (k.replace('_', '-'), "/metering") for k in metering_apidef.RESOURCE_ATTRIBUTE_MAP ) def setUp(self): service_plugins = {'metering_plugin_name': METERING_SERVICE_PLUGIN_KLASS} plugin = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatIntAgentSchedulingPlugin') ext_mgr = MeteringTestExtensionManager() super(TestMeteringPluginRpcFromL3Agent, self).setUp(plugin=plugin, service_plugins=service_plugins, ext_mgr=ext_mgr) self.meter_plugin = directory.get_plugin(constants.METERING) self.tenant_id = 'admin_tenant_id' self.tenant_id_1 = 'tenant_id_1' self.tenant_id_2 = 'tenant_id_2' self.adminContext = context.get_admin_context() helpers.register_l3_agent(host='agent1') def test_get_sync_data_metering(self): with self.subnet() as subnet: s = subnet['subnet'] self._set_net_external(s['network_id']) with self.router(name='router1', subnet=subnet) as router: r = router['router'] self._add_external_gateway_to_router(r['id'], s['network_id']) with self.metering_label(tenant_id=r['tenant_id']): callbacks = metering_rpc.MeteringRpcCallbacks( self.meter_plugin) data = callbacks.get_sync_data_metering(self.adminContext, host='agent1') self.assertEqual('router1', data[0]['name']) helpers.register_l3_agent(host='agent2') data = callbacks.get_sync_data_metering(self.adminContext, host='agent2') self.assertFalse(data) self._remove_external_gateway_from_router( r['id'], s['network_id']) def test_get_sync_data_metering_shared(self): with self.router(name='router1', tenant_id=self.tenant_id_1): with self.router(name='router2', tenant_id=self.tenant_id_2): with self.metering_label(tenant_id=self.tenant_id, shared=True): callbacks = metering_rpc.MeteringRpcCallbacks( self.meter_plugin) data = callbacks.get_sync_data_metering(self.adminContext) routers = [router['name'] for router in data] self.assertIn('router1', routers) self.assertIn('router2', routers) def test_get_sync_data_metering_not_shared(self): with self.router(name='router1', tenant_id=self.tenant_id_1): with self.router(name='router2', tenant_id=self.tenant_id_2): with self.metering_label(tenant_id=self.tenant_id): callbacks = metering_rpc.MeteringRpcCallbacks( self.meter_plugin) data = callbacks.get_sync_data_metering(self.adminContext) routers = [router['name'] for router in data] self.assertEqual([], routers) def test_get_sync_data_metering_with_unscheduled_router(self): with self.subnet() as subnet: s = subnet['subnet'] self._set_net_external(s['network_id']) with self.router( name='router1', tenant_id=self.tenant_id ) as router1: self._add_external_gateway_to_router( router1['router']['id'], s['network_id']) with self.router(name='router2', tenant_id=self.tenant_id): with self.metering_label(tenant_id=self.tenant_id): callbacks = metering_rpc.MeteringRpcCallbacks( self.meter_plugin) data = callbacks.get_sync_data_metering( self.adminContext, host='agent1') self.assertEqual( set(['router1']), set([r['name'] for r in data])) self._remove_external_gateway_from_router( router1['router']['id'], s['network_id']) def test_get_sync_data_metering_with_inactive_router(self): with self.subnet() as subnet: s = subnet['subnet'] self._set_net_external(s['network_id']) with self.router( name='router1', tenant_id=self.tenant_id ) as router1: self._add_external_gateway_to_router( router1['router']['id'], s['network_id']) with self.router( name='router2', tenant_id=self.tenant_id, admin_state_up=False ) as router2: self._add_external_gateway_to_router( router2['router']['id'], s['network_id']) with self.metering_label(tenant_id=self.tenant_id): callbacks = metering_rpc.MeteringRpcCallbacks( self.meter_plugin) data = callbacks.get_sync_data_metering( self.adminContext, host='agent1') self.assertEqual( set(['router1']), set([r['name'] for r in data])) self._remove_external_gateway_from_router( router2['router']['id'], s['network_id']) self._remove_external_gateway_from_router( router1['router']['id'], s['network_id']) neutron-12.1.1/neutron/tests/unit/services/metering/agents/0000775000175000017500000000000013553660157024054 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/metering/agents/test_metering_agent.py0000664000175000017500000002503413553660047030457 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg from oslo_utils import fixture as utils_fixture from oslo_utils import timeutils from oslo_utils import uuidutils from neutron.conf.services import metering_agent as metering_agent_config from neutron.services.metering.agents import metering_agent from neutron.tests import base from neutron.tests import fake_notifier _uuid = uuidutils.generate_uuid TENANT_ID = _uuid() LABEL_ID = _uuid() ROUTERS = [{'status': 'ACTIVE', 'name': 'router1', 'gw_port_id': None, 'admin_state_up': True, 'tenant_id': TENANT_ID, '_metering_labels': [{'rules': [], 'id': LABEL_ID}], 'id': _uuid()}] ROUTERS_WITH_RULE = [{'status': 'ACTIVE', 'name': 'router1', 'gw_port_id': None, 'admin_state_up': True, 'tenant_id': TENANT_ID, '_metering_labels': [{'rule': {}, 'id': LABEL_ID}], 'id': _uuid()}] class TestMeteringOperations(base.BaseTestCase): def setUp(self): super(TestMeteringOperations, self).setUp() metering_agent_config.register_metering_agent_opts() self.noop_driver = ('neutron.services.metering.drivers.noop.' 'noop_driver.NoopMeteringDriver') cfg.CONF.set_override('driver', 'noop') cfg.CONF.set_override('measure_interval', 0) cfg.CONF.set_override('report_interval', 0) self.setup_notification_driver() metering_rpc = ('neutron.services.metering.agents.metering_agent.' 'MeteringPluginRpc._get_sync_data_metering') self.metering_rpc_patch = mock.patch(metering_rpc, return_value=[]) self.metering_rpc_patch.start() self.driver_patch = mock.patch(self.noop_driver, spec=True) self.driver_patch.start() loopingcall_patch = mock.patch( 'oslo_service.loopingcall.FixedIntervalLoopingCall') loopingcall_patch.start() self.agent = metering_agent.MeteringAgent('my agent', cfg.CONF) self.driver = self.agent.metering_driver def test_add_metering_label(self): self.agent.add_metering_label(None, ROUTERS) self.assertEqual(1, self.driver.add_metering_label.call_count) def test_remove_metering_label(self): self.agent.remove_metering_label(None, ROUTERS) self.assertEqual(1, self.driver.remove_metering_label.call_count) def test_update_metering_label_rule(self): self.agent.update_metering_label_rules(None, ROUTERS) self.assertEqual(1, self.driver.update_metering_label_rules.call_count) def test_add_metering_label_rule(self): self.agent.add_metering_label_rule(None, ROUTERS_WITH_RULE) self.assertEqual(1, self.driver.add_metering_label_rule.call_count) def test_remove_metering_label_rule(self): self.agent.remove_metering_label_rule(None, ROUTERS_WITH_RULE) self.assertEqual(1, self.driver.remove_metering_label_rule.call_count) def test_routers_updated(self): self.agent.routers_updated(None, ROUTERS) self.assertEqual(1, self.driver.update_routers.call_count) def test_get_traffic_counters(self): self.agent._get_traffic_counters(None, ROUTERS) self.assertEqual(1, self.driver.get_traffic_counters.call_count) def test_notification_report(self): self.agent.routers_updated(None, ROUTERS) self.driver.get_traffic_counters.return_value = {LABEL_ID: {'pkts': 88, 'bytes': 444}} self.agent._metering_loop() self.assertNotEqual(len(fake_notifier.NOTIFICATIONS), 0) for n in fake_notifier.NOTIFICATIONS: if n['event_type'] == 'l3.meter': break self.assertEqual('l3.meter', n['event_type']) payload = n['payload'] self.assertEqual(TENANT_ID, payload['tenant_id']) self.assertEqual(LABEL_ID, payload['label_id']) self.assertEqual(88, payload['pkts']) self.assertEqual(444, payload['bytes']) def test_notification_report_interval(self): measure_interval = 30 report_interval = 600 now = timeutils.utcnow() time_fixture = self.useFixture(utils_fixture.TimeFixture(now)) self.agent.routers_updated(None, ROUTERS) self.driver.get_traffic_counters.return_value = {LABEL_ID: {'pkts': 889, 'bytes': 4440}} cfg.CONF.set_override('measure_interval', measure_interval) cfg.CONF.set_override('report_interval', report_interval) for i in range(report_interval): self.agent._metering_loop() count = 0 if len(fake_notifier.NOTIFICATIONS) > 1: for n in fake_notifier.NOTIFICATIONS: if n['event_type'] == 'l3.meter': #skip the first notification because the time is 0 count += 1 if count > 1: break time_fixture.advance_time_seconds(measure_interval) self.assertEqual('l3.meter', n['event_type']) payload = n['payload'] self.assertEqual(TENANT_ID, payload['tenant_id']) self.assertEqual(LABEL_ID, payload['label_id']) self.assertLess((payload['time'] - report_interval), measure_interval, payload) interval = (payload['last_update'] - payload['first_update']) \ - report_interval self.assertLess(interval, measure_interval, payload) def test_router_deleted(self): label_id = _uuid() self.driver.get_traffic_counters = mock.MagicMock() self.driver.get_traffic_counters.return_value = {label_id: {'pkts': 44, 'bytes': 222}} self.agent._add_metering_info = mock.MagicMock() self.agent.routers_updated(None, ROUTERS) self.agent.router_deleted(None, ROUTERS[0]['id']) self.assertEqual(1, self.agent._add_metering_info.call_count) self.assertEqual(1, self.driver.remove_router.call_count) self.agent._add_metering_info.assert_called_with(label_id, 44, 222) @mock.patch('time.time') def _test_purge_metering_info(self, current_timestamp, is_empty, mock_time): mock_time.return_value = current_timestamp self.agent.metering_infos = {'fake': {'last_update': 1}} self.config(report_interval=1) self.agent._purge_metering_info() self.assertEqual(0 if is_empty else 1, len(self.agent.metering_infos)) self.assertEqual(1, mock_time.call_count) def test_purge_metering_info(self): # 1 < 2 - 1 -> False self._test_purge_metering_info(2, False) def test_purge_metering_info_delete(self): # 1 < 3 - 1 -> False self._test_purge_metering_info(3, True) @mock.patch('time.time') def _test_add_metering_info(self, expected_info, current_timestamp, mock_time): mock_time.return_value = current_timestamp actual_info = self.agent._add_metering_info('fake_label_id', 1, 1) self.assertEqual(1, len(self.agent.metering_infos)) self.assertEqual(expected_info, actual_info) self.assertEqual(expected_info, self.agent.metering_infos['fake_label_id']) self.assertEqual(1, mock_time.call_count) def test_add_metering_info_create(self): expected_info = {'bytes': 1, 'pkts': 1, 'time': 0, 'first_update': 1, 'last_update': 1} self._test_add_metering_info(expected_info, 1) def test_add_metering_info_update(self): expected_info = {'bytes': 1, 'pkts': 1, 'time': 0, 'first_update': 1, 'last_update': 1} self.agent.metering_infos = {'fake_label_id': expected_info} expected_info.update({'bytes': 2, 'pkts': 2, 'time': 1, 'last_update': 2}) self._test_add_metering_info(expected_info, 2) def test_metering_agent_host_value(self): expected_host = 'my agent' self.assertEqual(expected_host, self.agent.host) class TestMeteringDriver(base.BaseTestCase): def setUp(self): super(TestMeteringDriver, self).setUp() metering_agent_config.register_metering_agent_opts() cfg.CONF.set_override('driver', 'noop') self.agent = metering_agent.MeteringAgent('my agent', cfg.CONF) self.driver = mock.Mock() self.agent.metering_driver = self.driver def test_add_metering_label_with_bad_driver_impl(self): del self.driver.add_metering_label with mock.patch.object(metering_agent, 'LOG') as log: self.agent.add_metering_label(None, ROUTERS) log.exception.assert_called_with(mock.ANY, {'driver': 'noop', 'func': 'add_metering_label'}) def test_add_metering_label_runtime_error(self): self.driver.add_metering_label.side_effect = RuntimeError with mock.patch.object(metering_agent, 'LOG') as log: self.agent.add_metering_label(None, ROUTERS) log.exception.assert_called_with(mock.ANY, {'driver': 'noop', 'func': 'add_metering_label'}) def test_init_chain(self): with mock.patch('oslo_service.' 'periodic_task.PeriodicTasks.__init__') as init: metering_agent.MeteringAgent('my agent', cfg.CONF) init.assert_called_once_with(cfg.CONF) neutron-12.1.1/neutron/tests/unit/services/metering/agents/__init__.py0000664000175000017500000000000013553660046026150 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/metering/drivers/0000775000175000017500000000000013553660157024251 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/metering/drivers/test_iptables.py0000664000175000017500000010006113553660047027461 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import mock from oslo_config import cfg from neutron.services.metering.drivers.iptables import iptables_driver from neutron.tests import base TEST_ROUTERS = [ {'_metering_labels': [ {'id': 'c5df2fe5-c600-4a2a-b2f4-c0fb6df73c83', 'rules': [{ 'direction': 'ingress', 'excluded': False, 'id': '7f1a261f-2489-4ed1-870c-a62754501379', 'metering_label_id': 'c5df2fe5-c600-4a2a-b2f4-c0fb6df73c83', 'remote_ip_prefix': '10.0.0.0/24'}]}], 'admin_state_up': True, 'gw_port_id': '6d411f48-ecc7-45e0-9ece-3b5bdb54fcee', 'id': '473ec392-1711-44e3-b008-3251ccfc5099', 'name': 'router1', 'distributed': False, 'status': 'ACTIVE', 'tenant_id': '6c5f5d2a1fa2441e88e35422926f48e8'}, {'_metering_labels': [ {'id': 'eeef45da-c600-4a2a-b2f4-c0fb6df73c83', 'rules': [{ 'direction': 'egress', 'excluded': False, 'id': 'fa2441e8-2489-4ed1-870c-a62754501379', 'metering_label_id': 'eeef45da-c600-4a2a-b2f4-c0fb6df73c83', 'remote_ip_prefix': '20.0.0.0/24'}]}], 'admin_state_up': True, 'gw_port_id': '7d411f48-ecc7-45e0-9ece-3b5bdb54fcee', 'id': '373ec392-1711-44e3-b008-3251ccfc5099', 'name': 'router2', 'status': 'ACTIVE', 'distributed': False, 'tenant_id': '6c5f5d2a1fa2441e88e35422926f48e8'}, ] TEST_DVR_ROUTER = [ {'_metering_labels': [ {'id': 'c5df2fe5-c610-4a2a-b2f4-c0fb6df73c83', 'rules': [{ 'direction': 'ingress', 'excluded': False, 'id': '7f1a261f-2600-4ed1-870c-a62754501379', 'metering_label_id': 'c5df2fe5-c700-4a2a-b2f4-c0fb6df73c83', 'remote_ip_prefix': '10.0.0.0/24'}]}], 'admin_state_up': True, 'gw_port_id': '6d411f48-ecc7-45e0-9ece-3b5bdb54fcee', 'id': '473ec392-2711-44e3-b008-3251ccfc5099', 'name': 'router-test', 'distributed': True, 'status': 'ACTIVE', 'tenant_id': '6c5f5d2a1fa2441e88e35422926f48e8'}] TEST_ROUTERS_WITH_ONE_RULE = [ {'_metering_labels': [ {'id': 'c5df2fe5-c600-4a2a-b2f4-c0fb6df73c83', 'rule': { 'direction': 'ingress', 'excluded': False, 'id': '7f1a261f-2489-4ed1-870c-a62754501379', 'metering_label_id': 'c5df2fe5-c600-4a2a-b2f4-c0fb6df73c83', 'remote_ip_prefix': '30.0.0.0/24'}}], 'admin_state_up': True, 'gw_port_id': '6d411f48-ecc7-45e0-9ece-3b5bdb54fcee', 'id': '473ec392-1711-44e3-b008-3251ccfc5099', 'name': 'router1', 'status': 'ACTIVE', 'distributed': False, 'tenant_id': '6c5f5d2a1fa2441e88e35422926f48e8'}, {'_metering_labels': [ {'id': 'eeef45da-c600-4a2a-b2f4-c0fb6df73c83', 'rule': { 'direction': 'egress', 'excluded': False, 'id': 'fa2441e8-2489-4ed1-870c-a62754501379', 'metering_label_id': 'eeef45da-c600-4a2a-b2f4-c0fb6df73c83', 'remote_ip_prefix': '40.0.0.0/24'}}], 'admin_state_up': True, 'gw_port_id': '7d411f48-ecc7-45e0-9ece-3b5bdb54fcee', 'id': '373ec392-1711-44e3-b008-3251ccfc5099', 'name': 'router2', 'distributed': False, 'status': 'ACTIVE', 'tenant_id': '6c5f5d2a1fa2441e88e35422926f48e8'}, ] TEST_ROUTERS_WITH_NEW_LABEL = [ {'_metering_labels': [ {'id': 'e27fe2df-376e-4ac7-ae13-92f050a21f84', 'rule': { 'direction': 'ingress', 'excluded': False, 'id': '7f1a261f-2489-4ed1-870c-a62754501379', 'metering_label_id': 'e27fe2df-376e-4ac7-ae13-92f050a21f84', 'remote_ip_prefix': '50.0.0.0/24'}}], 'admin_state_up': True, 'gw_port_id': '6d411f48-ecc7-45e0-9ece-3b5bdb54fcee', 'id': '473ec392-1711-44e3-b008-3251ccfc5099', 'name': 'router1', 'status': 'ACTIVE', 'tenant_id': '6c5f5d2a1fa2441e88e35422926f48e8'}] class IptablesDriverTestCase(base.BaseTestCase): def setUp(self): super(IptablesDriverTestCase, self).setUp() self.utils_exec_p = mock.patch( 'neutron.agent.linux.utils.execute') self.utils_exec = self.utils_exec_p.start() self.iptables_cls_p = mock.patch( 'neutron.agent.linux.iptables_manager.IptablesManager') self.iptables_cls = self.iptables_cls_p.start() self.iptables_inst = mock.Mock() self.v4filter_inst = mock.Mock() self.v6filter_inst = mock.Mock() self.namespace_exists_p = mock.patch( 'neutron.agent.linux.ip_lib.network_namespace_exists') self.namespace_exists = self.namespace_exists_p.start() self.snat_ns_name_p = mock.patch( 'neutron.agent.l3.dvr_snat_ns.SnatNamespace.get_snat_ns_name') self.snat_ns_name = self.snat_ns_name_p.start() self.v4filter_inst.chains = [] self.v6filter_inst.chains = [] self.iptables_inst.ipv4 = {'filter': self.v4filter_inst} self.iptables_inst.ipv6 = {'filter': self.v6filter_inst} self.iptables_cls.return_value = self.iptables_inst cfg.CONF.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') self.metering = iptables_driver.IptablesMeteringDriver('metering', cfg.CONF) def test_create_stateless_iptables_manager(self): routers = TEST_ROUTERS[:1] self.namespace_exists.return_value = True self.metering.add_metering_label(None, routers) self.assertEqual(1, self.iptables_cls.call_count) self.iptables_cls.assert_called_with( binary_name=mock.ANY, namespace=mock.ANY, state_less=True, use_ipv6=mock.ANY) rm = iptables_driver.RouterWithMetering(self.metering.conf, routers[0]) self.assertTrue(rm.iptables_manager) self.assertIsNone(rm.snat_iptables_manager) def test_iptables_manager_never_create_with_no_valid_namespace(self): routers = TEST_ROUTERS[:1] self.namespace_exists.return_value = False self.metering.add_metering_label(None, routers) self.assertFalse(self.iptables_cls.called) rm = iptables_driver.RouterWithMetering(self.metering.conf, routers[0]) self.assertIsNone(rm.iptables_manager) self.assertIsNone(rm.snat_iptables_manager) def test_create_iptables_manager_for_distributed_routers(self): routers = TEST_DVR_ROUTER[:1] self.namespace_exists.return_value = True snat_ns_name = 'snat-' + routers[0]['id'] self.snat_ns_name.return_value = snat_ns_name self.metering.add_metering_label(None, routers) self.assertEqual(2, self.iptables_cls.call_count) rm = iptables_driver.RouterWithMetering(self.metering.conf, routers[0]) self.assertTrue(rm.iptables_manager) self.assertTrue(rm.snat_iptables_manager) def test_add_metering_label(self): routers = TEST_ROUTERS[:1] self.namespace_exists.return_value = True self.metering.add_metering_label(None, routers) calls = [mock.call.add_chain('neutron-meter-l-c5df2fe5-c60', wrap=False), mock.call.add_chain('neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-l-c5df2fe5-c60', '', wrap=False)] self.v4filter_inst.assert_has_calls(calls) def test_add_metering_label_dvr_routers(self): routers = TEST_DVR_ROUTER[:1] self.namespace_exists.return_value = True snat_ns_name = 'snat-' + routers[0]['id'] self.snat_ns_name.return_value = snat_ns_name self.metering._process_ns_specific_metering_label = mock.Mock() self.metering.add_metering_label(None, routers) rm = iptables_driver.RouterWithMetering(self.metering.conf, routers[0]) ext_dev, ext_snat_dev = self.metering.get_external_device_names(rm) self.assertEqual( 2, self.metering._process_ns_specific_metering_label.call_count) # check and validate the right device being passed based on the # namespace. self.assertEqual( self.metering._process_ns_specific_metering_label.mock_calls, [mock.call( routers[0], ext_dev, rm.iptables_manager), mock.call( routers[0], ext_snat_dev, rm.snat_iptables_manager)]) def test_add_metering_label_legacy_routers(self): routers = TEST_ROUTERS[:1] self.namespace_exists.return_value = True self.metering._process_ns_specific_metering_label = mock.Mock() self.metering.add_metering_label(None, routers) rm = iptables_driver.RouterWithMetering(self.metering.conf, routers[0]) ext_dev, _ = self.metering.get_external_device_names(rm) self.assertEqual( self.metering._process_ns_specific_metering_label.mock_calls, [mock.call(routers[0], ext_dev, rm.iptables_manager)]) def test_add_metering_label_when_no_namespace(self): routers = TEST_ROUTERS[:1] self.namespace_exists.return_value = False self.metering._process_metering_label = mock.Mock() self.metering.add_metering_label(None, routers) rm = iptables_driver.RouterWithMetering(self.metering.conf, routers[0]) self.assertIsNone(rm.iptables_manager) self.assertIsNone(rm.snat_iptables_manager) self.assertFalse(self.metering._process_metering_label.called) def test_process_metering_label_rules(self): self.namespace_exists.return_value = True self.metering.add_metering_label(None, TEST_ROUTERS) calls = [mock.call.add_chain('neutron-meter-l-c5df2fe5-c60', wrap=False), mock.call.add_chain('neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-l-c5df2fe5-c60', '', wrap=False), mock.call.add_rule('neutron-meter-r-c5df2fe5-c60', '-d 10.0.0.0/24 -i qg-6d411f48-ec' ' -j neutron-meter-l-c5df2fe5-c60', wrap=False, top=False), mock.call.add_chain('neutron-meter-l-eeef45da-c60', wrap=False), mock.call.add_chain('neutron-meter-r-eeef45da-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-eeef45da-c60', wrap=False), mock.call.add_rule('neutron-meter-l-eeef45da-c60', '', wrap=False), mock.call.add_rule('neutron-meter-r-eeef45da-c60', '-s 20.0.0.0/24 -o qg-7d411f48-ec' ' -j neutron-meter-l-eeef45da-c60', wrap=False, top=False)] self.v4filter_inst.assert_has_calls(calls) def test_process_metering_label_rules_with_no_gateway_router(self): routers = copy.deepcopy(TEST_ROUTERS) for router in routers: router['gw_port_id'] = None self.namespace_exists.return_value = True self.metering.add_metering_label(None, routers) calls = [mock.call.add_chain('neutron-meter-l-c5df2fe5-c60', wrap=False), mock.call.add_chain('neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-l-c5df2fe5-c60', '', wrap=False), mock.call.add_chain('neutron-meter-l-eeef45da-c60', wrap=False), mock.call.add_chain('neutron-meter-r-eeef45da-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-eeef45da-c60', wrap=False), mock.call.add_rule('neutron-meter-l-eeef45da-c60', '', wrap=False)] self.v4filter_inst.assert_has_calls(calls, any_order=False) def test_add_metering_label_with_rules(self): routers = copy.deepcopy(TEST_ROUTERS) routers[1]['_metering_labels'][0]['rules'][0].update({ 'direction': 'ingress', 'excluded': True, }) self.namespace_exists.return_value = True self.metering.add_metering_label(None, routers) calls = [mock.call.add_chain('neutron-meter-l-c5df2fe5-c60', wrap=False), mock.call.add_chain('neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-l-c5df2fe5-c60', '', wrap=False), mock.call.add_rule('neutron-meter-r-c5df2fe5-c60', '-d 10.0.0.0/24 -i qg-6d411f48-ec' ' -j neutron-meter-l-c5df2fe5-c60', wrap=False, top=False), mock.call.add_chain('neutron-meter-l-eeef45da-c60', wrap=False), mock.call.add_chain('neutron-meter-r-eeef45da-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-eeef45da-c60', wrap=False), mock.call.add_rule('neutron-meter-l-eeef45da-c60', '', wrap=False), mock.call.add_rule('neutron-meter-r-eeef45da-c60', '-d 20.0.0.0/24 -i qg-7d411f48-ec' ' -j RETURN', wrap=False, top=True)] self.v4filter_inst.assert_has_calls(calls) def test_update_metering_label_rules(self): routers = TEST_ROUTERS[:1] self.namespace_exists.return_value = True self.metering.add_metering_label(None, routers) updates = copy.deepcopy(routers) updates[0]['_metering_labels'][0]['rules'] = [{ 'direction': 'egress', 'excluded': True, 'id': '7f1a261f-2489-4ed1-870c-a62754501379', 'metering_label_id': 'c5df2fe5-c600-4a2a-b2f4-c0fb6df73c83', 'remote_ip_prefix': '10.0.0.0/24'}, {'direction': 'ingress', 'excluded': False, 'id': '6f1a261f-2489-4ed1-870c-a62754501379', 'metering_label_id': 'c5df2fe5-c600-4a2a-b2f4-c0fb6df73c83', 'remote_ip_prefix': '20.0.0.0/24'}] self.metering.update_metering_label_rules(None, updates) calls = [mock.call.add_chain('neutron-meter-l-c5df2fe5-c60', wrap=False), mock.call.add_chain('neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-l-c5df2fe5-c60', '', wrap=False), mock.call.add_rule('neutron-meter-r-c5df2fe5-c60', '-d 10.0.0.0/24 -i qg-6d411f48-ec' ' -j neutron-meter-l-c5df2fe5-c60', wrap=False, top=False), mock.call.empty_chain('neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-r-c5df2fe5-c60', '-s 10.0.0.0/24 -o qg-6d411f48-ec' ' -j RETURN', wrap=False, top=True), mock.call.add_rule('neutron-meter-r-c5df2fe5-c60', '-d 20.0.0.0/24 -i qg-6d411f48-ec -j ' 'neutron-meter-l-c5df2fe5-c60', wrap=False, top=False)] self.v4filter_inst.assert_has_calls(calls) def test_remove_metering_label_rule_in_update(self): routers = copy.deepcopy(TEST_ROUTERS[:1]) routers[0]['_metering_labels'][0]['rules'].append({ 'direction': 'ingress', 'excluded': False, 'id': 'aaaa261f-2489-4ed1-870c-a62754501379', 'metering_label_id': 'c5df2fe5-c600-4a2a-b2f4-c0fb6df73c83', 'remote_ip_prefix': '20.0.0.0/24', }) self.namespace_exists.return_value = True self.metering.add_metering_label(None, routers) del routers[0]['_metering_labels'][0]['rules'][1] self.metering.update_metering_label_rules(None, routers) calls = [mock.call.add_chain('neutron-meter-l-c5df2fe5-c60', wrap=False), mock.call.add_chain('neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-l-c5df2fe5-c60', '', wrap=False), mock.call.add_rule('neutron-meter-r-c5df2fe5-c60', '-d 10.0.0.0/24 -i qg-6d411f48-ec' ' -j neutron-meter-l-c5df2fe5-c60', wrap=False, top=False), mock.call.add_rule('neutron-meter-r-c5df2fe5-c60', '-d 20.0.0.0/24 -i qg-6d411f48-ec' ' -j neutron-meter-l-c5df2fe5-c60', wrap=False, top=False), mock.call.empty_chain('neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-r-c5df2fe5-c60', '-d 10.0.0.0/24 -i qg-6d411f48-ec' ' -j neutron-meter-l-c5df2fe5-c60', wrap=False, top=False)] self.v4filter_inst.assert_has_calls(calls) def test_add_metering_label_rule(self): new_routers_rules = TEST_ROUTERS_WITH_ONE_RULE self.metering.update_routers(None, TEST_ROUTERS) self.namespace_exists.return_value = True self.metering.add_metering_label_rule(None, new_routers_rules) calls = [ mock.call.add_rule('neutron-meter-r-c5df2fe5-c60', '-d 30.0.0.0/24 -i qg-6d411f48-ec' ' -j neutron-meter-l-c5df2fe5-c60', wrap=False, top=False), mock.call.add_rule('neutron-meter-r-eeef45da-c60', '-s 40.0.0.0/24 -o qg-7d411f48-ec' ' -j neutron-meter-l-eeef45da-c60', wrap=False, top=False), ] self.v4filter_inst.assert_has_calls(calls) def test_add_metering_label_rule_without_label(self): new_routers_rules = TEST_ROUTERS_WITH_NEW_LABEL # clear all the metering labels for r in TEST_ROUTERS: rm = iptables_driver.RouterWithMetering(self.metering.conf, r) rm.metering_labels = {} self.metering.update_routers(None, TEST_ROUTERS) self.metering.add_metering_label_rule(None, new_routers_rules) calls = [ mock.call.add_chain('neutron-meter-l-e27fe2df-376', wrap=False), mock.call.add_chain('neutron-meter-r-e27fe2df-376', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j neutron-meter-r-e27fe2df-376', wrap=False), mock.call.add_rule('neutron-meter-l-e27fe2df-376', '', wrap=False), mock.call.add_rule('neutron-meter-r-e27fe2df-376', '-d 50.0.0.0/24 ' '-i qg-6d411f48-ec ' '-j neutron-meter-l-e27fe2df-376', top=False, wrap=False) ] self.v4filter_inst.assert_has_calls(calls) def test_add_metering_label_rule_dvr_router(self): routers = TEST_DVR_ROUTER self.metering.update_routers(None, TEST_DVR_ROUTER) self.namespace_exists.return_value = True self.metering._process_metering_rule_action_based_on_ns = mock.Mock() self.metering.add_metering_label_rule(None, routers) rm = iptables_driver.RouterWithMetering(self.metering.conf, routers[0]) ext_dev, ext_snat_dev = self.metering.get_external_device_names(rm) self.assertEqual( 2, self.metering._process_metering_rule_action_based_on_ns.call_count) # check and validate the right device being passed based on the # namespace. self.assertEqual( self.metering._process_metering_rule_action_based_on_ns.mock_calls, [mock.call( routers[0], 'create', ext_dev, rm.iptables_manager), mock.call( routers[0], 'create', ext_snat_dev, rm.snat_iptables_manager)]) def test_remove_metering_label_rule_dvr_router(self): routers = TEST_DVR_ROUTER self.metering.update_routers(None, TEST_DVR_ROUTER) self.namespace_exists.return_value = True self.metering.add_metering_label_rule(None, routers) self.metering._process_metering_rule_action_based_on_ns = mock.Mock() self.metering.remove_metering_label_rule(None, routers) rm = iptables_driver.RouterWithMetering(self.metering.conf, routers[0]) ext_dev, ext_snat_dev = self.metering.get_external_device_names(rm) self.assertEqual( 2, self.metering._process_metering_rule_action_based_on_ns.call_count) # check and validate the right device being passed based on the # namespace. self.assertEqual( self.metering._process_metering_rule_action_based_on_ns.mock_calls, [mock.call( routers[0], 'delete', ext_dev, rm.iptables_manager), mock.call( routers[0], 'delete', ext_snat_dev, rm.snat_iptables_manager)]) def test_remove_metering_label_rule(self): new_routers_rules = TEST_ROUTERS_WITH_ONE_RULE self.metering.update_routers(None, TEST_ROUTERS) self.namespace_exists.return_value = True self.metering.add_metering_label_rule(None, new_routers_rules) self.metering.remove_metering_label_rule(None, new_routers_rules) calls = [ mock.call.remove_rule('neutron-meter-r-c5df2fe5-c60', '-d 30.0.0.0/24 -i qg-6d411f48-ec' ' -j neutron-meter-l-c5df2fe5-c60', wrap=False, top=False), mock.call.remove_rule('neutron-meter-r-eeef45da-c60', '-s 40.0.0.0/24 -o qg-7d411f48-ec' ' -j neutron-meter-l-eeef45da-c60', wrap=False, top=False) ] self.v4filter_inst.assert_has_calls(calls) def test_remove_metering_label(self): routers = TEST_ROUTERS[:1] self.namespace_exists.return_value = True self.metering.add_metering_label(None, routers) self.metering.remove_metering_label(None, routers) calls = [mock.call.add_chain('neutron-meter-l-c5df2fe5-c60', wrap=False), mock.call.add_chain('neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-l-c5df2fe5-c60', '', wrap=False), mock.call.add_rule('neutron-meter-r-c5df2fe5-c60', '-d 10.0.0.0/24 -i qg-6d411f48-ec' ' -j neutron-meter-l-c5df2fe5-c60', wrap=False, top=False), mock.call.remove_chain('neutron-meter-l-c5df2fe5-c60', wrap=False), mock.call.remove_chain('neutron-meter-r-c5df2fe5-c60', wrap=False)] self.v4filter_inst.assert_has_calls(calls) def test_remove_metering_label_with_dvr_routers(self): routers = TEST_DVR_ROUTER[:1] self.namespace_exists.return_value = True self.metering.add_metering_label(None, routers) self.metering._process_ns_specific_disassociate_metering_label = ( mock.Mock()) self.metering.remove_metering_label(None, routers) self.assertEqual( 2, (self.metering. _process_ns_specific_disassociate_metering_label.call_count)) def test_update_routers(self): routers = copy.deepcopy(TEST_ROUTERS) routers[1]['_metering_labels'][0]['rules'][0].update({ 'direction': 'ingress', 'excluded': True, }) self.namespace_exists.return_value = True self.metering.add_metering_label(None, routers) updates = copy.deepcopy(routers) updates[0]['gw_port_id'] = '587b63c1-22a3-40b3-9834-486d1fb215a5' self.metering.update_routers(None, updates) calls = [mock.call.add_chain('neutron-meter-l-c5df2fe5-c60', wrap=False), mock.call.add_chain('neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-l-c5df2fe5-c60', '', wrap=False), mock.call.add_rule('neutron-meter-r-c5df2fe5-c60', '-d 10.0.0.0/24 -i qg-6d411f48-ec' ' -j neutron-meter-l-c5df2fe5-c60', wrap=False, top=False), mock.call.add_chain('neutron-meter-l-eeef45da-c60', wrap=False), mock.call.add_chain('neutron-meter-r-eeef45da-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-eeef45da-c60', wrap=False), mock.call.add_rule('neutron-meter-l-eeef45da-c60', '', wrap=False), mock.call.add_rule('neutron-meter-r-eeef45da-c60', '-d 20.0.0.0/24 -i qg-7d411f48-ec' ' -j RETURN', wrap=False, top=True), mock.call.remove_chain('neutron-meter-l-c5df2fe5-c60', wrap=False), mock.call.remove_chain('neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_chain('neutron-meter-l-c5df2fe5-c60', wrap=False), mock.call.add_chain('neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-FORWARD', '-j ' 'neutron-meter-r-c5df2fe5-c60', wrap=False), mock.call.add_rule('neutron-meter-l-c5df2fe5-c60', '', wrap=False), mock.call.add_rule('neutron-meter-r-c5df2fe5-c60', '-d 10.0.0.0/24 -i qg-587b63c1-22' ' -j neutron-meter-l-c5df2fe5-c60', wrap=False, top=False)] self.v4filter_inst.assert_has_calls(calls) def test_update_routers_removal(self): routers = TEST_ROUTERS self.namespace_exists.return_value = True self.metering.add_metering_label(None, routers) # Remove router id '373ec392-1711-44e3-b008-3251ccfc5099' updates = TEST_ROUTERS[:1] self.metering.update_routers(None, updates) calls = [mock.call.remove_chain('neutron-meter-l-eeef45da-c60', wrap=False), mock.call.remove_chain('neutron-meter-r-eeef45da-c60', wrap=False)] self.v4filter_inst.assert_has_calls(calls) def test_get_traffic_counters_with_missing_chain(self): for r in TEST_ROUTERS: rm = iptables_driver.RouterWithMetering(self.metering.conf, r) rm.metering_labels = {r['_metering_labels'][0]['id']: 'fake'} self.metering.routers[r['id']] = rm mocked_method = self.iptables_cls.return_value.get_traffic_counters mocked_method.side_effect = [{'pkts': 1, 'bytes': 8}, RuntimeError('Failed to find the chain')] counters = self.metering.get_traffic_counters(None, TEST_ROUTERS) expected_label_id = TEST_ROUTERS[0]['_metering_labels'][0]['id'] self.assertIn(expected_label_id, counters) self.assertEqual(1, counters[expected_label_id]['pkts']) self.assertEqual(8, counters[expected_label_id]['bytes']) neutron-12.1.1/neutron/tests/unit/services/metering/drivers/__init__.py0000664000175000017500000000000013553660046026345 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/metering/__init__.py0000664000175000017500000000000013553660046024667 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/0000775000175000017500000000000013553660157022124 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/seg_types/0000775000175000017500000000000013553660157024126 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/seg_types/test_validators.py0000664000175000017500000000255313553660047027712 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron.services.trunk import constants from neutron.services.trunk.seg_types import validators from neutron.tests import base class ValidatorsTestCase(base.BaseTestCase): def test_add_validator_raises_keyerror_on_redefinition(self): self.assertRaises(KeyError, validators.add_validator, constants.VLAN, mock.ANY) def test_add_validator_add_new_type(self): validators.add_validator('foo', lambda: None) self.assertIn('foo', validators._supported) def test_get_validator(self): self.assertIsNotNone(validators.get_validator(constants.VLAN)) def test_get_validator_raises_keyerror_on_missing_validator(self): self.assertRaises(KeyError, validators.get_validator, 'my_random_seg_type') neutron-12.1.1/neutron/tests/unit/services/trunk/seg_types/__init__.py0000664000175000017500000000000013553660046026222 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/rpc/0000775000175000017500000000000013553660157022710 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/rpc/test_agent.py0000664000175000017500000000432113553660047025415 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg import oslo_messaging from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc from neutron.services.trunk.rpc import agent from neutron.tests import base class TrunkSkeletonTest(base.BaseTestCase): # TODO(fitoduarte): add more test to improve coverage of module @mock.patch("neutron.api.rpc.callbacks.resource_manager." "ConsumerResourceCallbacksManager.register") @mock.patch("neutron.common.rpc.get_server") def test___init__(self, mocked_get_server, mocked_register): test_obj = agent.TrunkSkeleton() self.assertEqual(2, mocked_register.call_count) calls = [mock.call(test_obj.handle_trunks, resources.TRUNK), mock.call(test_obj.handle_subports, resources.SUBPORT)] mocked_register.assert_has_calls(calls, any_order=True) # Test to see if the call to rpc.get_server has the correct # target and the correct endpoints topic = resources_rpc.resource_type_versioned_topic(resources.SUBPORT) subport_target = oslo_messaging.Target( topic=topic, server=cfg.CONF.host, fanout=True) topic = resources_rpc.resource_type_versioned_topic(resources.TRUNK) trunk_target = oslo_messaging.Target( topic=topic, server=cfg.CONF.host, fanout=True) calls = [mock.call(subport_target, mock.ANY), mock.call(trunk_target, mock.ANY)] mocked_get_server.assert_has_calls(calls, any_order=True) self.assertIn("ResourcesPushRpcCallback", str(mocked_get_server.call_args_list)) neutron-12.1.1/neutron/tests/unit/services/trunk/rpc/test_backend.py0000664000175000017500000000577513553660047025724 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import events from neutron_lib import fixture from neutron.api.rpc.callbacks import resource_manager from neutron.services.trunk import callbacks from neutron.services.trunk import constants as trunk_consts from neutron.services.trunk.rpc import backend from neutron.tests import base class ServerSideRpcBackendTest(base.BaseTestCase): # TODO(fitoduarte): add more test to improve coverage of module def setUp(self): super(ServerSideRpcBackendTest, self).setUp() self._mgr = mock.Mock() self.useFixture(fixture.CallbackRegistryFixture( callback_manager=self._mgr)) self.register_mock = mock.patch.object( resource_manager.ResourceCallbacksManager, "register").start() def test___init__(self,): test_obj = backend.ServerSideRpcBackend() calls = [mock.call(test_obj.process_event, trunk_consts.TRUNK, events.AFTER_CREATE), mock.call(test_obj.process_event, trunk_consts.TRUNK, events.AFTER_DELETE), mock.call(test_obj.process_event, trunk_consts.SUBPORTS, events.AFTER_CREATE), mock.call(test_obj.process_event, trunk_consts.SUBPORTS, events.AFTER_DELETE) ] self._mgr.subscribe.assert_has_calls(calls, any_order=True) def test_process_event(self): test_obj = backend.ServerSideRpcBackend() test_obj._stub = mock_stub = mock.Mock() trunk_plugin = mock.Mock() test_obj.process_event( trunk_consts.TRUNK, events.AFTER_CREATE, trunk_plugin, callbacks.TrunkPayload("context", "id", current_trunk="current_trunk")) test_obj.process_event( trunk_consts.TRUNK, events.AFTER_DELETE, trunk_plugin, callbacks.TrunkPayload("context", "id", original_trunk="original_trunk")) calls = [mock.call.trunk_created("context", "current_trunk"), mock.call.trunk_deleted("context", "original_trunk")] mock_stub.assert_has_calls(calls, any_order=False) neutron-12.1.1/neutron/tests/unit/services/trunk/rpc/__init__.py0000664000175000017500000000000013553660046025004 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/rpc/test_server.py0000664000175000017500000003241113553660047025626 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import portbindings from neutron_lib.plugins import directory from oslo_config import cfg import oslo_messaging from sqlalchemy.orm import exc from neutron.api.rpc.callbacks import events from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc from neutron.db import api as db_api from neutron.objects import trunk as trunk_obj from neutron.plugins.ml2 import plugin as ml2_plugin from neutron.services.trunk import constants from neutron.services.trunk import drivers from neutron.services.trunk import exceptions as trunk_exc from neutron.services.trunk import plugin as trunk_plugin from neutron.services.trunk.rpc import constants as rpc_consts from neutron.services.trunk.rpc import server from neutron.tests import base from neutron.tests.unit.plugins.ml2 import test_plugin class TrunkSkeletonTest(test_plugin.Ml2PluginV2TestCase): def setUp(self): super(TrunkSkeletonTest, self).setUp() self.mock_registry_provide = mock.patch( 'neutron.api.rpc.callbacks.producer.registry.provide').start() self.drivers_patch = mock.patch.object(drivers, 'register').start() self.mock_update_port = mock.patch.object(ml2_plugin.Ml2Plugin, 'update_port').start() self.compat_patch = mock.patch.object( trunk_plugin.TrunkPlugin, 'check_compatibility').start() self.trunk_plugin = trunk_plugin.TrunkPlugin() self.trunk_plugin.add_segmentation_type('vlan', lambda x: True) self.core_plugin = directory.get_plugin() def _create_test_trunk(self, port, subports=None): subports = subports if subports else [] trunk = {'port_id': port['port']['id'], 'tenant_id': 'test_tenant', 'sub_ports': subports } response = ( self.trunk_plugin.create_trunk(self.context, {'trunk': trunk})) return response @mock.patch("neutron.api.rpc.callbacks.resource_manager." "ResourceCallbacksManager.register") @mock.patch("neutron.common.rpc.get_server") def test___init__(self, mocked_get_server, mocked_registered): test_obj = server.TrunkSkeleton() self.mock_registry_provide.assert_called_with( server.trunk_by_port_provider, resources.TRUNK) trunk_target = oslo_messaging.Target(topic=rpc_consts.TRUNK_BASE_TOPIC, server=cfg.CONF.host, fanout=False) mocked_get_server.assert_called_with(trunk_target, [test_obj]) def test_update_subport_bindings(self): with self.port() as _parent_port: parent_port = _parent_port trunk = self._create_test_trunk(parent_port) port_data = {portbindings.HOST_ID: 'trunk_host_id'} self.core_plugin.update_port( self.context, parent_port['port']['id'], {'port': port_data}) subports = [] mock_return_vals = [] for vid in range(0, 3): with self.port() as new_port: new_port[portbindings.HOST_ID] = 'trunk_host_id' mock_return_vals.append(new_port) obj = trunk_obj.SubPort( context=self.context, trunk_id=trunk['id'], port_id=new_port['port']['id'], segmentation_type='vlan', segmentation_id=vid) subports.append(obj) self.mock_update_port.side_effect = mock_return_vals test_obj = server.TrunkSkeleton() test_obj._trunk_plugin = self.trunk_plugin test_obj._core_plugin = self.core_plugin updated_subports = test_obj.update_subport_bindings(self.context, subports=subports) trunk = trunk_obj.Trunk.get_object(self.context, id=trunk['id']) self.assertEqual(trunk.status, constants.BUILD_STATUS) self.assertIn(trunk.id, updated_subports) for port in updated_subports[trunk['id']]: self.assertEqual('trunk_host_id', port[portbindings.HOST_ID]) def test__handle_port_binding_binding_error(self): with self.port() as _trunk_port: trunk = self._create_test_trunk(_trunk_port) trunk_host = 'test-host' test_obj = server.TrunkSkeleton() self.mock_update_port.return_value = {portbindings.VIF_TYPE: portbindings.VIF_TYPE_BINDING_FAILED} self.assertRaises(trunk_exc.SubPortBindingError, test_obj._handle_port_binding, self.context, _trunk_port['port']['id'], trunk_obj.Trunk.get_object(self.context, id=trunk['id']), trunk_host) def test_udate_subport_bindings_error(self): with self.port() as _parent_port: parent_port = _parent_port trunk = self._create_test_trunk(parent_port) port_data = {portbindings.HOST_ID: 'trunk_host_id'} self.core_plugin.update_port( self.context, parent_port['port']['id'], {'port': port_data}) subports = [] for vid in range(0, 3): with self.port() as new_port: new_port[portbindings.HOST_ID] = 'trunk_host_id' obj = trunk_obj.SubPort( context=self.context, trunk_id=trunk['id'], port_id=new_port['port']['id'], segmentation_type='vlan', segmentation_id=vid) subports.append(obj) test_obj = server.TrunkSkeleton() test_obj._trunk_plugin = self.trunk_plugin test_obj._core_plugin = self.core_plugin self.mock_update_port.return_value = {portbindings.VIF_TYPE: portbindings.VIF_TYPE_BINDING_FAILED} updated_subports = test_obj.update_subport_bindings(self.context, subports=subports) trunk = trunk_obj.Trunk.get_object(self.context, id=trunk['id']) self.assertEqual(trunk.status, constants.ERROR_STATUS) self.assertEqual([], updated_subports[trunk.id]) def test_udate_subport_bindings_staledataerror(self): with self.port() as _parent_port: parent_port = _parent_port trunk = self._create_test_trunk(parent_port) port_data = {portbindings.HOST_ID: 'trunk_host_id'} self.core_plugin.update_port( self.context, parent_port['port']['id'], {'port': port_data}) subports = [] for vid in range(0, 3): with self.port() as new_port: new_port[portbindings.HOST_ID] = 'trunk_host_id' obj = trunk_obj.SubPort( context=self.context, trunk_id=trunk['id'], port_id=new_port['port']['id'], segmentation_type='vlan', segmentation_id=vid) subports.append(obj) test_obj = server.TrunkSkeleton() test_obj._trunk_plugin = self.trunk_plugin test_obj._core_plugin = self.core_plugin self.mock_update_port.return_value = {portbindings.VIF_TYPE: portbindings.VIF_TYPE_BINDING_FAILED} mock_trunk_obj = mock.Mock(port_id=parent_port['port']['id']) mock_trunk_obj.update.side_effect = exc.StaleDataError with mock.patch.object( trunk_obj.Trunk, 'get_object', return_value=mock_trunk_obj): self.assertRaises( exc.StaleDataError, test_obj.update_subport_bindings, self.context, subports=subports) self.assertEqual( db_api.MAX_RETRIES, mock_trunk_obj.update.call_count) def test_udate_subport_bindings_noretryerror(self): with self.port() as _parent_port: parent_port = _parent_port trunk = self._create_test_trunk(parent_port) port_data = {portbindings.HOST_ID: 'trunk_host_id'} self.core_plugin.update_port( self.context, parent_port['port']['id'], {'port': port_data}) subports = [] for vid in range(0, 3): with self.port() as new_port: new_port[portbindings.HOST_ID] = 'trunk_host_id' obj = trunk_obj.SubPort( context=self.context, trunk_id=trunk['id'], port_id=new_port['port']['id'], segmentation_type='vlan', segmentation_id=vid) subports.append(obj) test_obj = server.TrunkSkeleton() test_obj._trunk_plugin = self.trunk_plugin test_obj._core_plugin = self.core_plugin self.mock_update_port.return_value = {portbindings.VIF_TYPE: portbindings.VIF_TYPE_BINDING_FAILED} mock_trunk_obj = mock.Mock(port_id=parent_port['port']['id']) mock_trunk_obj.update.side_effect = KeyError with mock.patch.object( trunk_obj.Trunk, 'get_object', return_value=mock_trunk_obj): self.assertRaises( KeyError, test_obj.update_subport_bindings, self.context, subports=subports) self.assertEqual(1, mock_trunk_obj.update.call_count) def test_update_subport_bindings_exception(self): with self.port() as _parent_port: parent_port = _parent_port trunk = self._create_test_trunk(parent_port) port_data = {portbindings.HOST_ID: 'trunk_host_id'} self.core_plugin.update_port( self.context, parent_port['port']['id'], {'port': port_data}) subports = [] mock_return_vals = [] for vid in range(0, 3): with self.port() as new_port: new_port[portbindings.HOST_ID] = 'trunk_host_id' mock_return_vals.append(new_port) obj = trunk_obj.SubPort( context=self.context, trunk_id=trunk['id'], port_id=new_port['port']['id'], segmentation_type='vlan', segmentation_id=vid) subports.append(obj) self.mock_update_port.side_effect = Exception() test_obj = server.TrunkSkeleton() test_obj._trunk_plugin = self.trunk_plugin test_obj._core_plugin = self.core_plugin updated_subports = test_obj.update_subport_bindings(self.context, subports=subports) trunk = trunk_obj.Trunk.get_object(self.context, id=trunk['id']) self.assertEqual([], updated_subports.get(trunk.id)) self.assertEqual(constants.DEGRADED_STATUS, trunk.status) class TrunkStubTest(base.BaseTestCase): def setUp(self): super(TrunkStubTest, self).setUp() self.test_obj = server.TrunkStub() def test___init__(self): self.assertIsInstance(self.test_obj._resource_rpc, resources_rpc.ResourcesPushRpcApi) @mock.patch("neutron.api.rpc.handlers.resources_rpc.ResourcesPushRpcApi." "push") def test_trunk_created(self, mocked_push): m_context = mock.Mock() m_trunk = mock.Mock() self.test_obj.trunk_created(m_context, m_trunk) mocked_push.assert_called_with(m_context, [m_trunk], events.CREATED) @mock.patch("neutron.api.rpc.handlers.resources_rpc.ResourcesPushRpcApi." "push") def test_trunk_deleted(self, mocked_push): m_context = mock.Mock() m_trunk = mock.Mock() self.test_obj.trunk_deleted(m_context, m_trunk) mocked_push.assert_called_with(m_context, [m_trunk], events.DELETED) @mock.patch("neutron.api.rpc.handlers.resources_rpc.ResourcesPushRpcApi." "push") def test_subports_added(self, mocked_push): m_context = mock.Mock() m_subports = mock.Mock() self.test_obj.subports_added(m_context, m_subports) mocked_push.assert_called_with(m_context, m_subports, events.CREATED) @mock.patch("neutron.api.rpc.handlers.resources_rpc.ResourcesPushRpcApi." "push") def test_subports_deleted(self, mocked_push): m_context = mock.Mock() m_subports = mock.Mock() self.test_obj.subports_deleted(m_context, m_subports) mocked_push.assert_called_with(m_context, m_subports, events.DELETED) neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/0000775000175000017500000000000013553660157023602 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/linuxbridge/0000775000175000017500000000000013553660157026116 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/linuxbridge/test_driver.py0000664000175000017500000000324013553660046031016 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from oslo_config import cfg from neutron.services.trunk.drivers.linuxbridge import driver from neutron.tests import base class LinuxBridgeDriverTestCase(base.BaseTestCase): def test_driver_is_loaded(self): inst = driver.LinuxBridgeDriver.create() cfg.CONF.set_override('mechanism_drivers', ['a', 'b', 'linuxbridge'], group='ml2') self.assertTrue(inst.is_loaded) cfg.CONF.set_override('mechanism_drivers', ['a', 'b'], group='ml2') self.assertFalse(inst.is_loaded) cfg.CONF.set_override('core_plugin', 'my_foo_plugin') self.assertFalse(inst.is_loaded) def test_driver_properties(self): inst = driver.LinuxBridgeDriver.create() self.assertEqual(driver.NAME, inst.name) self.assertEqual(driver.SUPPORTED_INTERFACES, inst.interfaces) self.assertEqual(driver.SUPPORTED_SEGMENTATION_TYPES, inst.segmentation_types) self.assertEqual(constants.AGENT_TYPE_LINUXBRIDGE, inst.agent_type) self.assertTrue(inst.can_trunk_bound_port) neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/linuxbridge/agent/0000775000175000017500000000000013553660157027214 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/linuxbridge/agent/test_driver.py0000664000175000017500000002640713553660047032127 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock import oslo_messaging from oslo_utils import uuidutils import testtools from neutron.api.rpc.callbacks import events from neutron.api.rpc.handlers import resources_rpc from neutron.objects import trunk from neutron.services.trunk import constants as t_const from neutron.services.trunk.drivers.linuxbridge.agent import driver from neutron.services.trunk.drivers.linuxbridge.agent import trunk_plumber from neutron.tests import base class LinuxBridgeTrunkDriverTestCase(base.BaseTestCase): def setUp(self): super(LinuxBridgeTrunkDriverTestCase, self).setUp() self.plumber = mock.create_autospec(trunk_plumber.Plumber()) self.stub = mock.create_autospec(driver.trunk_rpc.TrunkStub()) self.tapi = mock.create_autospec(driver._TrunkAPI(self.stub)) self.lbd = driver.LinuxBridgeTrunkDriver(self.plumber, self.tapi) self.trunk = trunk.Trunk(id=uuidutils.generate_uuid(), port_id=uuidutils.generate_uuid(), project_id=uuidutils.generate_uuid()) self.subports = [trunk.SubPort(id=uuidutils.generate_uuid(), port_id=uuidutils.generate_uuid(), segmentation_type='vlan', trunk_id=self.trunk.id, segmentation_id=i) for i in range(20)] self.trunk.sub_ports = self.subports def test_handle_trunks_created(self): self._test_handle_trunks_wire_event(events.CREATED) def test_handle_trunks_updated(self): self._test_handle_trunks_wire_event(events.UPDATED) def _test_handle_trunks_wire_event(self, event): self.plumber.trunk_on_host.return_value = True self.lbd.handle_trunks(mock.Mock(), 'TRUNKS', [self.trunk], event) self.tapi.put_trunk.assert_called_once_with( self.trunk.port_id, self.trunk) self.tapi.bind_subports_to_host.assert_called_once_with( mock.ANY, self.trunk) self.assertFalse(self.plumber.delete_trunk_subports.called) def test_handle_trunks_deleted(self): self.lbd.handle_trunks(mock.Mock(), 'TRUNKS', [self.trunk], events.DELETED) self.tapi.put_trunk.assert_called_once_with( self.trunk.port_id, None) self.plumber.delete_trunk_subports.assert_called_once_with(self.trunk) def test_handle_subports_deleted(self): self.tapi.get_trunk_by_id.return_value = self.trunk self.lbd.handle_subports(mock.Mock(), 'TRUNKS', self.trunk.sub_ports, events.DELETED) self.assertEqual(20, len(self.tapi.delete_trunk_subport.mock_calls)) # should have tried to wire trunk at the end with state self.plumber.trunk_on_host.assert_called_once_with(self.trunk) def test_handle_subports_created(self): self.tapi.get_trunk_by_id.return_value = self.trunk self.lbd.handle_subports(mock.Mock(), 'TRUNKS', self.trunk.sub_ports, events.CREATED) self.assertEqual(20, len(self.tapi.put_trunk_subport.mock_calls)) # should have tried to wire trunk at the end with state self.plumber.trunk_on_host.assert_called_once_with(self.trunk) def test_agent_port_change_is_trunk(self): self.tapi.get_trunk.return_value = self.trunk self.lbd.agent_port_change('resource', 'event', 'trigger', 'context', {'port_id': self.trunk.port_id}) # should have tried to wire trunk self.plumber.trunk_on_host.assert_called_once_with(self.trunk) def test_agent_port_change_not_trunk(self): self.tapi.get_trunk.return_value = None self.tapi.get_trunk_for_subport.return_value = None other_port_id = uuidutils.generate_uuid() self.lbd.agent_port_change('resource', 'event', 'trigger', 'context', {'port_id': other_port_id}) self.plumber.delete_subports_by_port_id.assert_called_once_with( other_port_id) def test_agent_port_change_is_subport(self): self.tapi.get_trunk.return_value = None self.tapi.get_trunk_for_subport.return_value = self.trunk self.lbd.agent_port_change('resource', 'event', 'trigger', 'context', {'port_id': self.trunk.sub_ports[0].port_id, 'mac_address': 'mac_addr'}) self.plumber.delete_subports_by_port_id.assert_called_once_with( self.trunk.sub_ports[0].port_id) def test_wire_trunk_happy_path(self): self.lbd.wire_trunk('ctx', self.trunk) self.tapi.bind_subports_to_host.assert_called_once_with( 'ctx', self.trunk) self.plumber.ensure_trunk_subports.assert_called_once_with(self.trunk) self.tapi.set_trunk_status.assert_called_once_with( 'ctx', self.trunk, t_const.ACTIVE_STATUS) def test_wire_trunk_not_on_host(self): # trunk device not on host self.plumber.trunk_on_host.return_value = False self.lbd.wire_trunk('ctx', self.trunk) # don't bind and don't set status self.assertFalse(self.tapi.bind_subports_to_host.called) self.assertFalse(self.tapi.set_trunk_status.called) def test_wire_trunk_concurrent_removal(self): self.plumber.trunk_on_host.side_effect = [True, False] self.plumber.ensure_trunk_subports.side_effect = ValueError() self.lbd.wire_trunk('ctx', self.trunk) # we don't change status if port was just removed self.assertFalse(self.tapi.set_trunk_status.called) def test_wire_trunk_other_exception(self): self.plumber.ensure_trunk_subports.side_effect = ValueError() self.lbd.wire_trunk('ctx', self.trunk) # degraded due to dataplane failure self.tapi.set_trunk_status.assert_called_once_with( 'ctx', self.trunk, t_const.DEGRADED_STATUS) class TrunkAPITestCase(base.BaseTestCase): def setUp(self): super(TrunkAPITestCase, self).setUp() self.stub = mock.create_autospec(driver.trunk_rpc.TrunkStub()) self.tapi = driver._TrunkAPI(self.stub) self.trunk = trunk.Trunk(id=uuidutils.generate_uuid(), port_id=uuidutils.generate_uuid(), project_id=uuidutils.generate_uuid()) self.subports = [trunk.SubPort(id=uuidutils.generate_uuid(), port_id=uuidutils.generate_uuid(), segmentation_type='vlan', trunk_id=self.trunk.id, segmentation_id=i) for i in range(20)] self.trunk.sub_ports = self.subports self.stub.get_trunk_details.return_value = self.trunk def test_fetch_trunk(self): self.assertEqual(self.trunk, self.tapi._fetch_trunk('ctx', 'port')) self.stub.get_trunk_details.assert_called_once_with('ctx', 'port') def test_fetch_trunk_missing(self): self.stub.get_trunk_details.side_effect = ( resources_rpc.ResourceNotFound(resource_id='1', resource_type='1')) self.assertIsNone(self.tapi._fetch_trunk('ctx', 'port')) def test_fetch_trunk_plugin_disabled(self): self.stub.get_trunk_details.side_effect = ( oslo_messaging.RemoteError('CallbackNotFound')) self.assertIsNone(self.tapi._fetch_trunk('ctx', 'port')) def test_fetch_trunk_plugin_other_error(self): self.stub.get_trunk_details.side_effect = ( oslo_messaging.RemoteError('vacuum full')) with testtools.ExpectedException(oslo_messaging.RemoteError): self.tapi._fetch_trunk('ctx', 'port') def test_set_trunk_status(self): self.tapi.set_trunk_status('ctx', self.trunk, 'STATUS') self.stub.update_trunk_status.assert_called_once_with( 'ctx', self.trunk.id, 'STATUS') def test_bind_subports_to_host(self): self.tapi.bind_subports_to_host('ctx', self.trunk) self.stub.update_subport_bindings.assert_called_once_with( 'ctx', self.trunk.sub_ports) def test_put_trunk_subport_non_existent_trunk(self): # trunks not registered are ignored self.tapi.put_trunk_subport( 'non_trunk_id', self.trunk.sub_ports[0]) def test_get_trunk_by_id(self): self.tapi.put_trunk(self.trunk.port_id, self.trunk) self.assertEqual(self.trunk, self.tapi.get_trunk_by_id('ctx', self.trunk.id)) self.assertIsNone(self.tapi.get_trunk_by_id('ctx', 'other_id')) def test_put_trunk_subport(self): self.tapi.put_trunk(self.trunk.port_id, self.trunk) new = trunk.SubPort(id=uuidutils.generate_uuid(), port_id=uuidutils.generate_uuid(), segmentation_type='vlan', trunk_id=self.trunk.id, segmentation_id=1010) self.tapi.put_trunk_subport(self.trunk.id, new) subs = self.tapi.get_trunk('ctx', self.trunk.port_id).sub_ports self.assertEqual(21, len(subs)) self.assertEqual(new, subs[-1]) def test_delete_trunk_subport(self): self.tapi.put_trunk(self.trunk.port_id, self.trunk) sub = self.trunk.sub_ports[10] self.tapi.delete_trunk_subport(self.trunk.id, sub) subs = self.tapi.get_trunk('ctx', self.trunk.port_id).sub_ports self.assertNotIn(sub, subs) self.assertEqual(19, len(subs)) def test_get_trunk(self): self.tapi.put_trunk(self.trunk.port_id, self.trunk) self.assertEqual(self.trunk, self.tapi.get_trunk('ctx', self.trunk.port_id)) self.tapi.get_trunk('ctx', self.trunk.port_id) self.assertFalse(self.stub.get_trunk_details.called) def test_get_trunk_cache_miss(self): self.assertEqual(self.trunk, self.tapi.get_trunk('ctx', self.trunk.port_id)) self.tapi.get_trunk('ctx', self.trunk.port_id) self.assertEqual(1, len(self.stub.get_trunk_details.mock_calls)) def test_get_trunk_not_found(self): self.stub.get_trunk_details.side_effect = ( resources_rpc.ResourceNotFound(resource_id='1', resource_type='1')) self.assertIsNone(self.tapi.get_trunk('ctx', self.trunk.port_id)) self.tapi.get_trunk('ctx', self.trunk.port_id) self.assertEqual(1, len(self.stub.get_trunk_details.mock_calls)) def test_get_trunk_for_subport(self): self.tapi.put_trunk(self.trunk.port_id, self.trunk) t = self.tapi.get_trunk_for_subport( 'ctx', self.trunk.sub_ports[0].port_id) self.assertEqual(self.trunk, t) neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/linuxbridge/agent/__init__.py0000664000175000017500000000000013553660046031310 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/linuxbridge/agent/test_trunk_plumber.py0000664000175000017500000001643213553660047033522 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from oslo_utils import uuidutils from neutron.objects import trunk from neutron.services.trunk.drivers.linuxbridge.agent import trunk_plumber from neutron.tests import base class PlumberTestCase(base.BaseTestCase): def setUp(self): self.plumber = trunk_plumber.Plumber() self.get_tap_device_name = mock.patch.object( self.plumber, '_get_tap_device_name', return_value='devname').start() self.trunk = trunk.Trunk() self.trunk.port_id = uuidutils.generate_uuid() self.trunk.sub_ports = [] self.device_exists = mock.patch.object(trunk_plumber.ip_lib, 'device_exists').start() self.device_exists.return_value = True ipwrap = mock.patch.object(trunk_plumber.ip_lib, 'IPWrapper').start() ipwrap.return_value.netns.execute.return_value = IP_LINK_OUTPUT super(PlumberTestCase, self).setUp() def test_trunk_on_host(self): self.assertTrue(self.plumber.trunk_on_host(self.trunk)) self.device_exists.return_value = False self.assertFalse(self.plumber.trunk_on_host(self.trunk)) def test_ensure_trunk_subports(self): trunk_vals = set([('dev2', 23), ('dev3', 44), ('dev4', 45)]) existing_vals = set([('dev1', 21), ('dev2', 23), ('dev3', 45)]) mock.patch.object(self.plumber, '_get_subport_devs_and_vlans', return_value=trunk_vals).start() mock.patch.object(self.plumber, '_get_vlan_children', return_value=existing_vals).start() delete = mock.patch.object(self.plumber, '_safe_delete_device').start() create = mock.patch.object(self.plumber, '_create_vlan_subint').start() self.plumber.ensure_trunk_subports(self.trunk) # dev1 is gone and dev3 changed vlans delete.assert_has_calls([mock.call('dev3'), mock.call('dev1')], any_order=True) create.assert_has_calls([mock.call('devname', 'dev4', 45), mock.call('devname', 'dev3', 44)], any_order=True) def test_delete_trunk_subports(self): existing_vals = set([('dev1', 21), ('dev2', 23), ('dev3', 45)]) mock.patch.object(self.plumber, '_get_vlan_children', return_value=existing_vals).start() delete = mock.patch.object(self.plumber, '_safe_delete_device').start() self.plumber.delete_trunk_subports(self.trunk) delete.assert_has_calls([mock.call('dev3'), mock.call('dev2'), mock.call('dev1')], any_order=True) def test__get_vlan_children(self): expected = [('tap47198374-5a', 777), ('tap47198374-5b', 2), ('tap47198374-5c', 3)] self.assertEqual(set(expected), self.plumber._get_vlan_children('tap34786ac-28')) expected = [('tap39df7d39-c5', 99), ('tap39df7d44-b2', 904), ('tap11113d44-3f', 777)] self.assertEqual(set(expected), self.plumber._get_vlan_children('tapa962cfc7-9d')) # vlan sub-interface and non-trunk shouldn't have children self.assertEqual(set(), self.plumber._get_vlan_children('tap47198374-5c')) self.assertEqual(set(), self.plumber._get_vlan_children('br-int')) def test__iter_output_by_interface(self): iterator = trunk_plumber._iter_output_by_interface(IP_LINK_OUTPUT) names = [i.devname for i in iterator] expected = ['lo', 'eth0', 'bond0', 'ovs-system', 'br-ex', 'testb9cfb5d7', 'br-int', 'br-tun', 'tapa962cfc7-9d', 'tap39df7d39-c5', 'tap39df7d44-b2', 'tap11113d44-3f', 'tap34786ac-28', 'tap47198374-5a', 'tap47198374-5b', 'tap47198374-5c'] self.assertEqual(expected, names) IP_LINK_OUTPUT = """ 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 2: eth0: mtu 1500 qdisc pfifo_fast state UP mode DEFA link/ether 00:0c:29:10:68:04 brd ff:ff:ff:ff:ff:ff promiscuity 0 3: bond0: mtu 1500 qdisc noop state DOWN mode DEFAULT grou link/ether 5e:dc:86:6f:b7:19 brd ff:ff:ff:ff:ff:ff promiscuity 0 bond 4: ovs-system: mtu 1500 qdisc noop state DOWN mode DEFAULT group link/ether 5a:95:a1:b9:42:25 brd ff:ff:ff:ff:ff:ff promiscuity 1 5: br-ex: mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT gro link/ether be:cc:4f:f7:28:48 brd ff:ff:ff:ff:ff:ff promiscuity 1 6: testb9cfb5d7: mtu 1500 qdisc noqueue state UNKNOWN mode DEFA link/ether 82:90:49:84:32:47 brd ff:ff:ff:ff:ff:ff promiscuity 1 7: br-int: mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT gr link/ether 5a:5e:7d:02:7c:4d brd ff:ff:ff:ff:ff:ff promiscuity 1 8: br-tun: mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT gr link/ether 76:d8:a5:16:d7:4a brd ff:ff:ff:ff:ff:ff promiscuity 1 10: tapa962cfc7-9d: mtu 1500 qdisc noop state DOWN mode DEFAULT g link/ether 9a:31:1d:cc:b3:86 brd ff:ff:ff:ff:ff:ff promiscuity 0 tun 11: tap39df7d39-c5@tapa962cfc7-9d: mtu 1500 qdisc noop sta link/ether 9a:31:1d:cc:b3:86 brd ff:ff:ff:ff:ff:ff promiscuity 0 vlan protocol 802.1Q id 99 12: tap39df7d44-b2@tapa962cfc7-9d: mtu 1500 qdisc noop sta link/ether 9a:31:1d:cc:b3:86 brd ff:ff:ff:ff:ff:ff promiscuity 0 vlan protocol 802.1Q id 904 13: tap11113d44-3f@tapa962cfc7-9d: mtu 1500 qdisc noop sta link/ether 9a:31:1d:cc:b3:86 brd ff:ff:ff:ff:ff:ff promiscuity 0 vlan protocol 802.1Q id 777 14: tap34786ac-28: mtu 1500 qdisc noop state DOWN mode DEFAULT gr link/ether f6:07:9f:11:4c:dc brd ff:ff:ff:ff:ff:ff promiscuity 0 tun 15: tap47198374-5a@tap34786ac-28: mtu 1500 qdisc noop stat link/ether f6:07:9f:11:4c:dc brd ff:ff:ff:ff:ff:ff promiscuity 0 vlan protocol 802.1Q id 777 16: tap47198374-5b@tap34786ac-28: mtu 1500 qdisc noop stat link/ether f6:07:9f:11:4c:dc brd ff:ff:ff:ff:ff:ff promiscuity 0 vlan protocol 802.1Q id 2 17: tap47198374-5c@tap34786ac-28: mtu 1500 qdisc noop stat link/ether f6:07:9f:11:4c:dc brd ff:ff:ff:ff:ff:ff promiscuity 0 vlan protocol 802.1Q id 3 """ # noqa neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/linuxbridge/__init__.py0000664000175000017500000000000013553660046030212 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/openvswitch/0000775000175000017500000000000013553660157026153 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/openvswitch/test_driver.py0000664000175000017500000000603613553660046031061 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import constants from oslo_config import cfg from neutron.plugins.ml2.drivers.openvswitch.agent.common import ( constants as agent_consts) from neutron.services.trunk.drivers.openvswitch import driver from neutron.tests import base GEN_TRUNK_BR_NAME_PATCH = ( 'neutron.services.trunk.drivers.openvswitch.utils.gen_trunk_br_name') class OVSDriverTestCase(base.BaseTestCase): def test_driver_creation(self): ovs_driver = driver.OVSDriver.create() self.assertFalse(ovs_driver.is_loaded) self.assertEqual(driver.NAME, ovs_driver.name) self.assertEqual(driver.SUPPORTED_INTERFACES, ovs_driver.interfaces) self.assertEqual(driver.SUPPORTED_SEGMENTATION_TYPES, ovs_driver.segmentation_types) self.assertEqual(constants.AGENT_TYPE_OVS, ovs_driver.agent_type) self.assertFalse(ovs_driver.can_trunk_bound_port) self.assertTrue( ovs_driver.is_agent_compatible(constants.AGENT_TYPE_OVS)) self.assertTrue( ovs_driver.is_interface_compatible(driver.SUPPORTED_INTERFACES[0])) def test_driver_is_loaded(self): cfg.CONF.set_override('mechanism_drivers', 'openvswitch', group='ml2') ovs_driver = driver.OVSDriver.create() self.assertTrue(ovs_driver.is_loaded) def test_driver_is_not_loaded(self): cfg.CONF.set_override('core_plugin', 'my_foo_plugin') ovs_driver = driver.OVSDriver.create() self.assertFalse(ovs_driver.is_loaded) @mock.patch(GEN_TRUNK_BR_NAME_PATCH) def test_vif_details_bridge_name_handler_registration(self, mock_gen_br_name): driver.register() mock_gen_br_name.return_value = 'fake-trunk-br-name' test_trigger = mock.Mock() registry.publish(agent_consts.OVS_BRIDGE_NAME, events.BEFORE_READ, test_trigger, payload=events.EventPayload( None, metadata={ 'port': { 'trunk_details': { 'trunk_id': 'foo' } } })) test_trigger.assert_called_once_with('fake-trunk-br-name') neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/openvswitch/agent/0000775000175000017500000000000013553660157027251 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/openvswitch/agent/test_driver.py0000664000175000017500000001445013553660046032156 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import oslo_messaging from oslo_utils import uuidutils from neutron.api.rpc.callbacks import events from neutron.api.rpc.callbacks import resources from neutron.objects import trunk as trunk_obj from neutron.services.trunk.drivers.openvswitch.agent import driver from neutron.services.trunk.drivers.openvswitch.agent import ovsdb_handler from neutron.tests import base TRUNK_MANAGER = ('neutron.services.trunk.drivers.openvswitch.agent.' 'trunk_manager.TrunkManager') class OvsTrunkSkeletonTest(base.BaseTestCase): def setUp(self): super(OvsTrunkSkeletonTest, self).setUp() trunk_manager_cls_mock = mock.patch(TRUNK_MANAGER).start() self.trunk_manager = trunk_manager_cls_mock.return_value handler = ovsdb_handler.OVSDBHandler(self.trunk_manager) mock.patch.object(handler, 'trunk_rpc').start() mock.patch.object(handler, '_set_trunk_metadata').start() mock.patch.object( handler, 'manages_this_trunk', return_value=True).start() self.skeleton = driver.OVSTrunkSkeleton(handler) self.trunk_id = uuidutils.generate_uuid() self.subports = [ trunk_obj.SubPort( port_id=uuidutils.generate_uuid(), trunk_id=self.trunk_id, segmentation_type='foo', segmentation_id=i) for i in range(2)] @mock.patch("neutron.api.rpc.callbacks.resource_manager." "ConsumerResourceCallbacksManager.unregister") def test___init__(self, mocked_unregister): test_obj = driver.OVSTrunkSkeleton(mock.ANY) mocked_unregister.assert_called_with(test_obj.handle_trunks, resources.TRUNK) @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test_handle_subports_created(self, br): """Test handler calls into trunk manager for adding subports.""" def fake_update_subport_bindings(context, subports): return { self.trunk_id: [ {'id': subport.port_id, 'mac_address': "mac%d" % subport.segmentation_id} for subport in subports]} trunk_rpc = self.skeleton.ovsdb_handler.trunk_rpc trunk_rpc.update_subport_bindings.side_effect = ( fake_update_subport_bindings) self.skeleton.handle_subports(mock.Mock(), 'SUBPORTS', self.subports, events.CREATED) expected_calls = [ mock.call(subport.trunk_id, subport.port_id, mock.ANY, subport.segmentation_id) for subport in self.subports] self.trunk_manager.add_sub_port.assert_has_calls(expected_calls) @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test_handle_subports_deleted(self, br): """Test handler calls into trunk manager for deleting subports.""" self.skeleton.handle_subports(mock.Mock(), 'SUBPORTS', self.subports, events.DELETED) expected_calls = [ mock.call(subport.trunk_id, subport.port_id) for subport in self.subports] self.trunk_manager.remove_sub_port.assert_has_calls(expected_calls) def test_handle_subports_not_for_this_agent(self): with mock.patch.object(self.skeleton, 'ovsdb_handler') as handler_m: handler_m.manages_this_trunk.return_value = False self.skeleton.handle_subports(mock.Mock(), 'SUBPORTS', self.subports, mock.ANY) self.assertFalse(self.trunk_manager.wire_subports_for_trunk.called) self.assertFalse(self.trunk_manager.unwire_subports_for_trunk.called) def test_handle_subports_unknown_event(self): trunk_rpc = self.skeleton.ovsdb_handler.trunk_rpc # unknown events should be ignored and thus lead to no updates # and no trunk interactions. with mock.patch.object( self.skeleton.ovsdb_handler, 'wire_subports_for_trunk') as f,\ mock.patch.object( self.skeleton.ovsdb_handler, 'unwire_subports_for_trunk') as g: self.skeleton.handle_subports(mock.Mock(), 'SUBPORTS', self.subports, events.UPDATED) self.assertFalse(f.called) self.assertFalse(g.called) self.assertFalse(trunk_rpc.update_trunk_status.called) def test_handle_subports_trunk_rpc_error(self): trunk_rpc = self.skeleton.ovsdb_handler.trunk_rpc trunk_rpc.update_subport_bindings.side_effect = ( oslo_messaging.MessagingException) self.skeleton.handle_subports(mock.Mock(), 'SUBPORTS', self.subports, events.CREATED) self.assertTrue(trunk_rpc.update_subport_bindings.called) def _test_handle_subports_trunk_on_trunk_update(self, event): trunk_rpc = self.skeleton.ovsdb_handler.trunk_rpc self.skeleton.handle_subports(mock.Mock(), 'SUBPORTS', self.subports, event) # Make sure trunk state is reported to the server self.assertTrue(trunk_rpc.update_trunk_status.called) def test_handle_subports_created_trunk_on_trunk_update(self): with mock.patch.object( self.skeleton.ovsdb_handler, 'wire_subports_for_trunk'): self._test_handle_subports_trunk_on_trunk_update( events.CREATED) def test_handle_subports_deleted_trunk_on_trunk_update(self): with mock.patch.object( self.skeleton.ovsdb_handler, 'unwire_subports_for_trunk'): self._test_handle_subports_trunk_on_trunk_update( events.DELETED) neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/openvswitch/agent/test_ovsdb_handler.py0000664000175000017500000003623513553660047033503 0ustar zuulzuul00000000000000# Copyright (c) 2016 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import oslo_messaging from oslo_serialization import jsonutils from oslo_utils import uuidutils import testtools from neutron.api.rpc.handlers import resources_rpc from neutron.common import utils from neutron.objects import trunk as trunk_obj from neutron.services.trunk import constants from neutron.services.trunk.drivers.openvswitch.agent import exceptions from neutron.services.trunk.drivers.openvswitch.agent import ovsdb_handler from neutron.services.trunk.drivers.openvswitch.agent import trunk_manager from neutron.tests import base class TestLockOnBridgeName(base.BaseTestCase): def setUp(self): super(TestLockOnBridgeName, self).setUp() self.lock_mock = mock.patch('oslo_concurrency.lockutils.lock').start() self.method_called = False def test_positional_argument(self): @ovsdb_handler.lock_on_bridge_name(required_parameter='bridge_name') def testing_method(param, bridge_name, another): self.method_called = True testing_method('foo', 'bridge', 'bar') self.lock_mock.assert_called_once_with('bridge') self.assertTrue(self.method_called) def test_keyword_argument(self): @ovsdb_handler.lock_on_bridge_name(required_parameter='bridge_name') def testing_method(param, bridge_name, another): self.method_called = True testing_method('foo', another='baz', bridge_name='bridge') self.lock_mock.assert_called_once_with('bridge') self.assertTrue(self.method_called) def test_missing_argument(self): with testtools.ExpectedException(RuntimeError): @ovsdb_handler.lock_on_bridge_name( required_parameter='bridge_name') def testing_method(one, two): pass class TestIsTrunkServicePort(base.BaseTestCase): def test_with_bridge_name(self): observed = ovsdb_handler.is_trunk_service_port('tbr-foo') self.assertTrue(observed) def test_with_subport_patch_port_int_side(self): observed = ovsdb_handler.is_trunk_service_port('spi-foo') self.assertTrue(observed) def test_with_subport_patch_port_trunk_side(self): observed = ovsdb_handler.is_trunk_service_port('spt-foo') self.assertTrue(observed) def test_with_trunk_patch_port_int_side(self): observed = ovsdb_handler.is_trunk_service_port('tpi-foo') self.assertTrue(observed) def test_with_trunk_patch_port_trunk_side(self): observed = ovsdb_handler.is_trunk_service_port('tpt-foo') self.assertTrue(observed) def test_with_random_string(self): observed = ovsdb_handler.is_trunk_service_port('foo') self.assertFalse(observed) class TestBridgeHasInstancePort(base.BaseTestCase): def setUp(self): super(TestBridgeHasInstancePort, self).setUp() self.bridge = mock.Mock() self.present_interfaces = [] self.bridge.get_iface_name_list.return_value = self.present_interfaces def test_only_service_ports_on_bridge(self): """Test when only with patch ports and bridge name are on trunk bridge. """ self.present_interfaces.extend( ['tbr-foo', 'spt-foo', 'tpt-foo']) self.assertFalse(ovsdb_handler.bridge_has_instance_port(self.bridge)) def test_device_on_bridge(self): """Condition is True decause of foo device is present on bridge.""" self.present_interfaces.extend( ['tbr-foo', 'spt-foo', 'tpt-foo', 'foo']) self.assertTrue(ovsdb_handler.bridge_has_instance_port(self.bridge)) def test_ovsdb_error(self): self.bridge.get_iface_name_list.side_effect = RuntimeError self.assertFalse(ovsdb_handler.bridge_has_instance_port(self.bridge)) class TestOVSDBHandler(base.BaseTestCase): """Test that RPC or OVSDB failures do not cause crash.""" def setUp(self): super(TestOVSDBHandler, self).setUp() self.ovsdb_handler = ovsdb_handler.OVSDBHandler(mock.sentinel.manager) mock.patch.object(self.ovsdb_handler, 'trunk_rpc').start() mock.patch.object(self.ovsdb_handler, 'trunk_manager').start() self.trunk_manager = self.ovsdb_handler.trunk_manager self.trunk_id = uuidutils.generate_uuid() self.fake_subports = [ trunk_obj.SubPort( id=uuidutils.generate_uuid(), port_id=uuidutils.generate_uuid(), segmentation_id=1)] self.fake_port = { 'name': 'foo', 'external_ids': { 'trunk_id': 'trunk_id', 'subport_ids': jsonutils.dumps( [s.id for s in self.fake_subports]), } } self.subport_bindings = { 'trunk_id': [ {'id': subport.port_id, 'mac_address': 'mac'} for subport in self.fake_subports]} @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') @mock.patch.object(utils, 'wait_until_true', side_effect=utils.WaitTimeout) def test_handle_trunk_add_interface_wont_appear(self, wut, br): self.ovsdb_handler.handle_trunk_add('foo') self.assertTrue(self.trunk_manager.dispose_trunk.called) @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test_handle_trunk_add_rpc_failure(self, br): with mock.patch.object(self.ovsdb_handler, '_wire_trunk', side_effect=oslo_messaging.MessagingException): with mock.patch.object(ovsdb_handler, 'bridge_has_instance_port', return_value=True): self.ovsdb_handler.handle_trunk_add('foo') @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test_handle_trunk_add_ovsdb_failure(self, br): with mock.patch.object(self.ovsdb_handler, '_wire_trunk', side_effect=RuntimeError): with mock.patch.object(ovsdb_handler, 'bridge_has_instance_port', return_value=True): self.ovsdb_handler.handle_trunk_add('foo') @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test_handle_trunk_add_parent_port_not_found(self, br): with mock.patch.object(self.ovsdb_handler, '_get_parent_port', side_effect=exceptions.ParentPortNotFound): # do not wait the default timeout self.ovsdb_handler.timeout = 1 self.ovsdb_handler.handle_trunk_add('foo') @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test_handle_trunk_add_missing_bridge(self, br): br.return_value.bridge_exists.return_value = False with mock.patch.object( ovsdb_handler, 'bridge_has_instance_port') as has_port_mock: self.ovsdb_handler.handle_trunk_add('foo') self.assertFalse(has_port_mock.called) def test_handle_trunk_remove_trunk_manager_failure(self): with mock.patch.object(self.ovsdb_handler, '_get_trunk_metadata', side_effect=trunk_manager.TrunkManagerError(error='error')): with mock.patch.object(ovsdb_handler, 'bridge_has_instance_port', return_value=True): self.ovsdb_handler.handle_trunk_remove('foo', self.fake_port) @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test_handle_trunk_remove_rpc_failure(self, br): self.ovsdb_handler.trunk_rpc.update_trunk_status = ( oslo_messaging.MessagingException) self.ovsdb_handler.handle_trunk_remove('foo', self.fake_port) @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test_wire_subports_for_trunk_trunk_manager_failure(self, br): with mock.patch.object( self.ovsdb_handler, '_update_trunk_metadata') as f: trunk_rpc = self.ovsdb_handler.trunk_rpc trunk_rpc.update_subport_bindings.return_value = ( self.subport_bindings) self.trunk_manager.add_sub_port.side_effect = ( trunk_manager.TrunkManagerError(error='error')) status = self.ovsdb_handler.wire_subports_for_trunk( None, 'trunk_id', self.fake_subports) self.assertTrue(f.call_count) self.assertEqual(constants.DEGRADED_STATUS, status) @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test_wire_subports_for_trunk_ovsdb_failure(self, br): self.ovsdb_handler.trunk_rpc.update_subport_bindings.return_value = ( self.subport_bindings) with mock.patch.object(self.ovsdb_handler, '_set_trunk_metadata', side_effect=RuntimeError): status = self.ovsdb_handler.wire_subports_for_trunk( None, 'trunk_id', self.fake_subports) self.assertEqual(constants.DEGRADED_STATUS, status) @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test_unwire_subports_for_trunk_port_not_found(self, br): self.ovsdb_handler.trunk_rpc.update_subport_bindings.return_value = ( self.subport_bindings) with mock.patch.object(self.ovsdb_handler, '_update_trunk_metadata', side_effect=exceptions.ParentPortNotFound(bridge='foo_br')): status = self.ovsdb_handler.unwire_subports_for_trunk( 'trunk_id', ['subport_id']) self.assertEqual(constants.ACTIVE_STATUS, status) @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test_unwire_subports_for_trunk_trunk_manager_failure(self, br): with mock.patch.object( self.ovsdb_handler, '_update_trunk_metadata') as f: self.trunk_manager.remove_sub_port.side_effect = ( trunk_manager.TrunkManagerError(error='error')) status = self.ovsdb_handler.unwire_subports_for_trunk( 'foo_trunk_id', ['subport_id']) self.assertTrue(f.call_count) self.assertEqual(constants.DEGRADED_STATUS, status) def test__wire_trunk_get_trunk_details_failure(self): self.trunk_manager.get_port_uuid_from_external_ids.side_effect = ( trunk_manager.TrunkManagerError(error='error')) self.ovsdb_handler._wire_trunk(mock.Mock(), self.fake_port) def test__wire_trunk_trunk_not_associated(self): self.ovsdb_handler.trunk_rpc.get_trunk_details.side_effect = ( resources_rpc.ResourceNotFound( resource_id='id', resource_type='type')) self.ovsdb_handler._wire_trunk(mock.Mock(), self.fake_port) trunk_rpc = self.ovsdb_handler.trunk_rpc self.assertFalse(trunk_rpc.update_trunk_status.called) def test__wire_trunk_create_trunk_failure(self): self.trunk_manager.create_trunk.side_effect = ( trunk_manager.TrunkManagerError(error='error')) self.ovsdb_handler._wire_trunk(mock.Mock(), self.fake_port) trunk_rpc = self.ovsdb_handler.trunk_rpc trunk_rpc.update_trunk_status.assert_called_once_with( mock.ANY, mock.ANY, constants.ERROR_STATUS) def test__wire_trunk_rewire_trunk_failure(self): with mock.patch.object(self.ovsdb_handler, 'unwire_subports_for_trunk') as f,\ mock.patch.object(self.ovsdb_handler, 'get_connected_subports_for_trunk') as g: g.return_value = ['stale_port'] f.return_value = constants.DEGRADED_STATUS self.ovsdb_handler._wire_trunk( mock.Mock(), self.fake_port, rewire=True) trunk_rpc = self.ovsdb_handler.trunk_rpc trunk_rpc.update_trunk_status.assert_called_once_with( mock.ANY, mock.ANY, constants.DEGRADED_STATUS) def test__wire_trunk_report_trunk_called_on_wiring(self): with mock.patch.object(self.trunk_manager, 'create_trunk'),\ mock.patch.object(self.ovsdb_handler, 'wire_subports_for_trunk'): self.ovsdb_handler._wire_trunk(mock.Mock(), self.fake_port) trunk_rpc = self.ovsdb_handler.trunk_rpc self.assertTrue(trunk_rpc.update_trunk_status.called) @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test__set_trunk_metadata_with_None_params(self, br): mock_br = br.return_value with mock.patch.object( self.ovsdb_handler, "_get_parent_port", return_value={'name': 'foo', 'external_ids': {}}): self.ovsdb_handler._set_trunk_metadata(None, None, 'foo', []) mock_br.set_db_attribute.assert_called_once_with( 'Interface', 'foo', 'external_ids', {'bridge_name': mock.ANY, 'trunk_id': 'foo', 'subport_ids': '[]'}) def test__get_current_status_active(self): self.assertEqual(constants.ACTIVE_STATUS, self.ovsdb_handler._get_current_status([], [])) def test__get_current_status_degraded(self): self.assertEqual(constants.DEGRADED_STATUS, self.ovsdb_handler._get_current_status( [mock.ANY], [])) def _test__update_trunk_metadata_wire_flag(self, mock_br, wire, external_ids, subport_ids, expected_subport_ids): with mock.patch.object( self.ovsdb_handler, "_get_parent_port", return_value={'name': 'foo', 'external_ids': external_ids}): self.ovsdb_handler._update_trunk_metadata( None, None, 'foo', subport_ids, wire=wire) external_ids = mock_br.set_db_attribute.call_args[0][3] self.assertEqual(1, mock_br.set_db_attribute.call_count) self.assertEqual( sorted(expected_subport_ids), sorted(external_ids['subport_ids'])) @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test__update_trunk_metadata_wire(self, br): mock_br = br.return_value external_ids = { 'subport_ids': '["foo_subport_1"]' } subport_ids = ['foo_subport_2'] expected_subport_ids = '["foo_subport_1", "foo_subport_2"]' self._test__update_trunk_metadata_wire_flag( mock_br, True, external_ids, subport_ids, expected_subport_ids) @mock.patch('neutron.agent.common.ovs_lib.OVSBridge') def test__update_trunk_metadata_unwire(self, br): mock_br = br.return_value external_ids = { 'subport_ids': '["foo_subport_1", "foo_subport_2"]' } subport_ids = ['foo_subport_2'] expected_subport_ids = '["foo_subport_1"]' self._test__update_trunk_metadata_wire_flag( mock_br, False, external_ids, subport_ids, expected_subport_ids) neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/openvswitch/agent/__init__.py0000664000175000017500000000000013553660046031345 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/openvswitch/agent/test_trunk_manager.py0000664000175000017500000000414513553660046033520 0ustar zuulzuul00000000000000# Copyright (c) 2016 Red Hat # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib import mock import testtools from neutron.services.trunk.drivers.openvswitch.agent import trunk_manager from neutron.tests import base class TrunkManagerTestCase(base.BaseTestCase): """Tests are aimed to cover negative cases to make sure there is no typo in the logging. """ def setUp(self): super(TrunkManagerTestCase, self).setUp() self.trunk_manager = trunk_manager.TrunkManager(mock.sentinel.br_int) mock.patch.object(trunk_manager, 'TrunkBridge').start() @contextlib.contextmanager def _resource_fails(self, resource, method_name): with mock.patch.object(resource, method_name, side_effect=RuntimeError): with testtools.ExpectedException(trunk_manager.TrunkManagerError): yield def test_create_trunk_plug_fails(self): with self._resource_fails(trunk_manager.TrunkParentPort, 'plug'): self.trunk_manager.create_trunk(None, None, None) def test_remove_trunk_unplug_fails(self): with self._resource_fails(trunk_manager.TrunkParentPort, 'unplug'): self.trunk_manager.remove_trunk(None, None) def test_add_sub_port_plug_fails(self): with self._resource_fails(trunk_manager.SubPort, 'plug'): self.trunk_manager.add_sub_port(None, None, None, None) def test_remove_sub_port_unplug_fails(self): with self._resource_fails(trunk_manager.SubPort, 'unplug'): self.trunk_manager.remove_sub_port(None, None) neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/openvswitch/__init__.py0000664000175000017500000000000013553660046030247 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/drivers/__init__.py0000664000175000017500000000000013553660046025676 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/trunk/fakes.py0000664000175000017500000000272713553660046023574 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron.services.trunk.drivers import base class FakeDriver(base.DriverBase): @property def is_loaded(self): return True @classmethod def create(cls): return cls('foo_name', ('foo_intfs',), ('foo_seg_types',)) class FakeDriver2(base.DriverBase): @property def is_loaded(self): return True @classmethod def create(cls): return cls('foo_name2', ('foo_intf2',), ('foo_seg_types2',)) class FakeDriverCanTrunkBoundPort(base.DriverBase): @property def is_loaded(self): return True @classmethod def create(cls): return cls('foo_name3', ('foo_intfs',), ('foo_seg_types',), can_trunk_bound_port=True) class FakeDriverWithAgent(base.DriverBase): @property def is_loaded(self): return True @classmethod def create(cls): return cls('foo_name4', ('foo_intfs',), ('foo_seg_types',), "foo_type") neutron-12.1.1/neutron/tests/unit/services/trunk/test_rules.py0000664000175000017500000003606713553660047024701 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company, LP # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock import testtools from neutron_lib.api.definitions import trunk as trunk_api from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from neutron_lib.plugins.ml2 import api from oslo_utils import uuidutils from neutron.plugins.common import utils from neutron.services.trunk import constants from neutron.services.trunk import drivers from neutron.services.trunk import exceptions as trunk_exc from neutron.services.trunk import plugin as trunk_plugin from neutron.services.trunk import rules from neutron.services.trunk import utils as trunk_utils from neutron.tests import base from neutron.tests.unit.plugins.ml2 import test_plugin from neutron.tests.unit.services.trunk import fakes class SubPortsValidatorTestCase(base.BaseTestCase): def setUp(self): super(SubPortsValidatorTestCase, self).setUp() self.segmentation_types = {constants.VLAN: utils.is_valid_vlan_tag} self.context = mock.ANY mock.patch.object(rules.SubPortsValidator, '_get_port_mtu', return_value=None).start() mock.patch.object(rules.SubPortsValidator, '_prepare_subports', return_value=None).start() def test_validate_subport_subport_and_trunk_shared_port_id(self): shared_id = uuidutils.generate_uuid() validator = rules.SubPortsValidator( self.segmentation_types, [{'port_id': shared_id, 'segmentation_type': 'vlan', 'segmentation_id': 2}], shared_id) self.assertRaises(trunk_exc.ParentPortInUse, validator.validate, self.context) def test_validate_subport_invalid_vlan_id(self): validator = rules.SubPortsValidator( self.segmentation_types, [{'port_id': uuidutils.generate_uuid(), 'segmentation_type': 'vlan', 'segmentation_id': 5000}]) self.assertRaises(n_exc.InvalidInput, validator.validate, self.context) def test_validate_subport_vlan_id_not_an_int(self): validator = rules.SubPortsValidator( self.segmentation_types, [{'port_id': uuidutils.generate_uuid(), 'segmentation_type': 'vlan', 'segmentation_id': 'IamNotAnumber'}]) self.assertRaises(n_exc.InvalidInput, validator.validate, self.context) def test_validate_subport_valid_vlan_id_as_string(self): validator = rules.SubPortsValidator( self.segmentation_types, [{'port_id': uuidutils.generate_uuid(), 'segmentation_type': 'vlan', 'segmentation_id': '2'}]) with mock.patch.object(rules.TrunkPortValidator, 'validate') as f: validator.validate(self.context) f.assert_called_once_with(self.context, parent_port=False) def test_validate_subport_subport_invalid_segmentation_type(self): validator = rules.SubPortsValidator( self.segmentation_types, [{'port_id': uuidutils.generate_uuid(), 'segmentation_type': 'fake', 'segmentation_id': 100}]) self.assertRaises(n_exc.InvalidInput, validator.validate, self.context) def test_validate_subport_missing_segmentation_type(self): validator = rules.SubPortsValidator( self.segmentation_types, [{'port_id': uuidutils.generate_uuid(), 'segmentation_id': 100}]) self.assertRaises(n_exc.InvalidInput, validator.validate, self.context) def test_validate_subport_missing_segmentation_id(self): validator = rules.SubPortsValidator( self.segmentation_types, [{'port_id': uuidutils.generate_uuid(), 'segmentation_type': 'fake'}]) self.assertRaises(n_exc.InvalidInput, validator.validate, self.context) def test_validate_subport_missing_port_id(self): validator = rules.SubPortsValidator( self.segmentation_types, [{'segmentation_type': 'fake', 'segmentation_id': 100}]) self.assertRaises(n_exc.InvalidInput, validator.validate, self.context, basic_validation=True) class SubPortsValidatorPrepareTestCase(base.BaseTestCase): def setUp(self): super(SubPortsValidatorPrepareTestCase, self).setUp() self.segmentation_types = {constants.VLAN: utils.is_valid_vlan_tag} self.context = mock.ANY mock.patch.object(rules.SubPortsValidator, '_get_port_mtu', return_value=None).start() def test__prepare_subports_raise_no_provider_ext(self): validator = rules.SubPortsValidator( self.segmentation_types, [{'port_id': uuidutils.generate_uuid(), 'segmentation_type': 'inherit'}]) self.assertRaises(n_exc.InvalidInput, validator._prepare_subports, self.context) class SubPortsValidatorMtuSanityTestCase(test_plugin.Ml2PluginV2TestCase): def setUp(self): super(SubPortsValidatorMtuSanityTestCase, self).setUp() self.segmentation_types = {constants.VLAN: utils.is_valid_vlan_tag} def test_validate_subport_mtu_same_as_trunk(self): self._test_validate_subport_trunk_mtu(1500, 1500) def test_validate_subport_mtu_smaller_than_trunks(self): self._test_validate_subport_trunk_mtu(500, 1500) def test_validate_subport_mtu_greater_than_trunks(self): self._test_validate_subport_trunk_mtu(1500, 500) def test_validate_subport_mtu_unset_trunks_set(self): self._test_validate_subport_trunk_mtu(None, 500) def test_validate_subport_mtu_set_trunks_unset(self): self._test_validate_subport_trunk_mtu(500, None) def test_validate_subport_mtu_set_trunks_net_exception(self): self._test_validate_subport_trunk_mtu(1500, 'exc') def _test_validate_subport_trunk_mtu( self, subport_net_mtu, trunk_net_mtu): plugin = directory.get_plugin() orig_get_network = plugin.get_network orig_get_networks = plugin.get_networks def get_networks_adjust_mtu(*args, **kwargs): res = orig_get_networks(*args, **kwargs) res[0][api.MTU] = subport_net_mtu return res def get_network_adjust_mtu(*args, **kwargs): res = orig_get_network(*args, **kwargs) if res['name'] == 'net_trunk': if trunk_net_mtu == 'exc': raise n_exc.NetworkNotFound(net_id='net-id') res[api.MTU] = trunk_net_mtu elif res['name'] == 'net_subport': res[api.MTU] = subport_net_mtu return res with self.network('net_trunk') as trunk_net,\ self.subnet(network=trunk_net) as trunk_subnet,\ self.port(subnet=trunk_subnet) as trunk_port,\ self.network('net_subport') as subport_net,\ self.subnet(network=subport_net) as subport_subnet,\ self.port(subnet=subport_subnet) as subport,\ mock.patch.object(plugin, "get_network", side_effect=get_network_adjust_mtu),\ mock.patch.object(plugin, "get_networks", side_effect=get_networks_adjust_mtu): trunk = {'port_id': trunk_port['port']['id'], 'tenant_id': 'test_tenant', 'sub_ports': [{'port_id': subport['port']['id'], 'segmentation_type': 'vlan', 'segmentation_id': 2}]} validator = rules.SubPortsValidator( self.segmentation_types, trunk['sub_ports'], trunk['port_id']) if subport_net_mtu is None or trunk_net_mtu is None: validator.validate(self.context) elif subport_net_mtu == 'exc' or trunk_net_mtu == 'exc': validator.validate(self.context) elif subport_net_mtu <= trunk_net_mtu: validator.validate(self.context) else: self.assertRaises(trunk_exc.SubPortMtuGreaterThanTrunkPortMtu, validator.validate, self.context) class TrunkPortValidatorTestCase(test_plugin.Ml2PluginV2TestCase): def setUp(self): super(TrunkPortValidatorTestCase, self).setUp() self.drivers_patch = mock.patch.object(drivers, 'register').start() self.compat_patch = mock.patch.object( trunk_plugin.TrunkPlugin, 'check_compatibility').start() self.trunk_plugin = trunk_plugin.TrunkPlugin() self.trunk_plugin.add_segmentation_type(constants.VLAN, utils.is_valid_vlan_tag) def test_validate_port_parent_in_use_by_trunk(self): with self.port() as trunk_parent: trunk = {'port_id': trunk_parent['port']['id'], 'tenant_id': 'test_tenant', 'sub_ports': []} self.trunk_plugin.create_trunk( self.context, {trunk_api.ALIAS: trunk}) validator = rules.TrunkPortValidator(trunk_parent['port']['id']) self.assertRaises(trunk_exc.ParentPortInUse, validator.validate, self.context) def test_validate_port_id_in_use_by_unrelated_trunk(self): with self.port() as trunk_parent,\ self.port() as subport: trunk = {'port_id': trunk_parent['port']['id'], 'tenant_id': 'test_tenant', 'sub_ports': [{'port_id': subport['port']['id'], 'segmentation_type': 'vlan', 'segmentation_id': 2}]} self.trunk_plugin.create_trunk( self.context, {trunk_api.ALIAS: trunk}) validator = rules.TrunkPortValidator(subport['port']['id']) self.assertRaises(trunk_exc.TrunkPortInUse, validator.validate, self.context) def test_validate_port_has_binding_host(self): with self.port() as port: core_plugin = directory.get_plugin() port['port']['binding:host_id'] = 'host' core_plugin.update_port(self.context, port['port']['id'], port) validator = rules.TrunkPortValidator(port['port']['id']) self.assertTrue(validator.is_bound(self.context)) def test_validate_for_subport_calls_check(self): with self.port() as port: validator = rules.TrunkPortValidator(port['port']['id']) with mock.patch.object(validator, "check_not_in_use") as f: validator.validate(self.context, parent_port=False) f.assert_called_once_with(self.context) def test_validate_port_cannot_be_trunked_raises(self): with self.port() as port, \ mock.patch.object(rules.TrunkPortValidator, "can_be_trunked", return_value=False), \ testtools.ExpectedException(trunk_exc.ParentPortInUse): validator = rules.TrunkPortValidator(port['port']['id']) validator.validate(self.context) def test_can_be_trunked_returns_false(self): # need to trigger a driver registration fakes.FakeDriverCanTrunkBoundPort.create() self.trunk_plugin = trunk_plugin.TrunkPlugin() directory.add_plugin('trunk', self.trunk_plugin) with self.port() as port: core_plugin = directory.get_plugin() port['port']['binding:host_id'] = 'host' core_plugin.update_port(self.context, port['port']['id'], port) validator = rules.TrunkPortValidator(port['port']['id']) # port cannot be trunked because of binding mismatch self.assertFalse(validator.can_be_trunked(self.context)) def test_can_be_trunked_returns_true(self): # need to trigger a driver registration fakes.FakeDriverCanTrunkBoundPort.create() self.trunk_plugin = trunk_plugin.TrunkPlugin() directory.add_plugin('trunk', self.trunk_plugin) with self.port() as port, \ mock.patch.object(trunk_utils, "is_driver_compatible", return_value=True) as g: core_plugin = directory.get_plugin() port['port']['binding:host_id'] = 'host' core_plugin.update_port(self.context, port['port']['id'], port) validator = rules.TrunkPortValidator(port['port']['id']) self.assertTrue(validator.can_be_trunked(self.context)) self.assertTrue(g.call_count) def test_can_be_trunked_unbound_port(self): with self.port() as port: validator = rules.TrunkPortValidator(port['port']['id']) self.assertTrue(validator.can_be_trunked(self.context)) def test_can_be_trunked_raises_conflict(self): d1 = fakes.FakeDriver.create() d2 = fakes.FakeDriverWithAgent.create() self.trunk_plugin = trunk_plugin.TrunkPlugin() directory.add_plugin('trunk', self.trunk_plugin) self.trunk_plugin._drivers = [d1, d2] with self.port() as port, \ mock.patch.object(trunk_utils, "is_driver_compatible", return_value=True): core_plugin = directory.get_plugin() port['port']['binding:host_id'] = 'host' core_plugin.update_port(self.context, port['port']['id'], port) validator = rules.TrunkPortValidator(port['port']['id']) self.assertRaises( trunk_exc.TrunkPluginDriverConflict, validator.can_be_trunked, self.context) def test_check_not_in_use_pass(self): with self.port() as port: validator = rules.TrunkPortValidator(port['port']['id']) self.assertIsNone(validator.check_not_in_use( self.context)) def test_check_not_in_use_raises(self): with self.port() as port: core_plugin = directory.get_plugin() port['port']['device_id'] = 'foo_device_id' core_plugin.update_port(self.context, port['port']['id'], port) validator = rules.TrunkPortValidator(port['port']['id']) self.assertRaises(n_exc.PortInUse, validator.check_not_in_use, self.context) neutron-12.1.1/neutron/tests/unit/services/trunk/test_utils.py0000664000175000017500000000540013553660046024671 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron.services.trunk import utils from neutron.tests.unit.plugins.ml2 import test_plugin from neutron.tests.unit.services.trunk import fakes class UtilsTestCase(test_plugin.Ml2PluginV2TestCase): def test_get_agent_types_by_host_returns_empty(self): self.assertFalse( utils.get_agent_types_by_host( self.context, 'foo_host')) def test_get_agent_types_by_host_returns_agents(self): with mock.patch("neutron.db.agents_db.AgentDbMixin.get_agents") as f: f.return_value = [{'agent_type': 'foo_type'}] self.assertEqual( ['foo_type'], utils.get_agent_types_by_host( self.context, 'foo_host')) def _test_is_driver_compatible(self, driver, interface, agent_types): return utils.is_driver_compatible(self.context, driver, interface, agent_types) def test_is_driver_compatible(self): driver = fakes.FakeDriverWithAgent.create() self.assertTrue(self._test_is_driver_compatible( driver, 'foo_intfs', ['foo_type'])) def test_is_driver_compatible_agent_based_agent_mismatch(self): driver = fakes.FakeDriverWithAgent.create() self.assertFalse(self._test_is_driver_compatible( driver, 'foo_intfs', ['foo_type_unknown'])) def test_is_driver_incompatible_because_of_interface_mismatch(self): driver = fakes.FakeDriverWithAgent.create() self.assertFalse(self._test_is_driver_compatible( driver, 'not_my_interface', ['foo_type'])) def test_is_driver_compatible_agentless(self): driver = fakes.FakeDriver.create() self.assertTrue(self._test_is_driver_compatible( driver, 'foo_intfs', ['foo_type'])) def test_is_driver_compatible_multiple_drivers(self): driver1 = fakes.FakeDriverWithAgent.create() driver2 = fakes.FakeDriver2.create() self.assertTrue(self._test_is_driver_compatible( driver1, 'foo_intfs', ['foo_type'])) self.assertFalse(self._test_is_driver_compatible( driver2, 'foo_intfs', ['foo_type'])) neutron-12.1.1/neutron/tests/unit/services/trunk/test_plugin.py0000664000175000017500000004327213553660047025041 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company, LP # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib.plugins import directory import testtools from neutron.objects import trunk as trunk_objects from neutron.services.trunk import callbacks from neutron.services.trunk import constants from neutron.services.trunk import drivers from neutron.services.trunk import exceptions as trunk_exc from neutron.services.trunk import plugin as trunk_plugin from neutron.services.trunk.seg_types import validators from neutron.tests.unit.plugins.ml2 import test_plugin from neutron.tests.unit.services.trunk import fakes def create_subport_dict(port_id): return {'segmentation_type': 'vlan', 'segmentation_id': 123, 'port_id': port_id} def register_mock_callback(resource, event): callback = mock.Mock() registry.subscribe(callback, resource, event) return callback class TrunkPluginTestCase(test_plugin.Ml2PluginV2TestCase): def setUp(self): super(TrunkPluginTestCase, self).setUp() self.drivers_patch = mock.patch.object(drivers, 'register').start() self.compat_patch = mock.patch.object( trunk_plugin.TrunkPlugin, 'check_compatibility').start() self.trunk_plugin = trunk_plugin.TrunkPlugin() self.trunk_plugin.add_segmentation_type('vlan', lambda x: True) def _create_test_trunk(self, port, subports=None): subports = subports if subports else [] trunk = {'port_id': port['port']['id'], 'tenant_id': 'test_tenant', 'sub_ports': subports} response = ( self.trunk_plugin.create_trunk(self.context, {'trunk': trunk})) return response def _get_trunk_obj(self, trunk_id): return trunk_objects.Trunk.get_object(self.context, id=trunk_id) def _get_subport_obj(self, port_id): subports = trunk_objects.SubPort.get_objects( self.context, port_id=port_id) return subports[0] def _test_delete_port_raise_in_use(self, parent_port, child_port, port_id, exception): subport = create_subport_dict(child_port['port']['id']) self._create_test_trunk(parent_port, [subport]) core_plugin = directory.get_plugin() self.assertRaises(exception, core_plugin.delete_port, self.context, port_id) def test_delete_port_raise_in_use_by_trunk(self): with self.port() as parent_port, self.port() as child_port: self._test_delete_port_raise_in_use( parent_port, child_port, parent_port['port']['id'], trunk_exc.PortInUseAsTrunkParent) def test_delete_port_raise_in_use_by_subport(self): with self.port() as parent_port, self.port() as child_port: self._test_delete_port_raise_in_use( parent_port, child_port, child_port['port']['id'], trunk_exc.PortInUseAsSubPort) def test_delete_trunk_raise_in_use(self): with self.port() as port: trunk = self._create_test_trunk(port) core_plugin = directory.get_plugin() port['port']['binding:host_id'] = 'host' core_plugin.update_port(self.context, port['port']['id'], port) self.assertRaises(trunk_exc.TrunkInUse, self.trunk_plugin.delete_trunk, self.context, trunk['id']) def _test_trunk_create_notify(self, event): with self.port() as parent_port: callback = register_mock_callback(constants.TRUNK, event) trunk = self._create_test_trunk(parent_port) trunk_obj = self._get_trunk_obj(trunk['id']) payload = callbacks.TrunkPayload(self.context, trunk['id'], current_trunk=trunk_obj) callback.assert_called_once_with( constants.TRUNK, event, self.trunk_plugin, payload=payload) def test_create_trunk_notify_after_create(self): self._test_trunk_create_notify(events.AFTER_CREATE) def test_create_trunk_notify_precommit_create(self): self._test_trunk_create_notify(events.PRECOMMIT_CREATE) def _test_trunk_update_notify(self, event): with self.port() as parent_port: callback = register_mock_callback(constants.TRUNK, event) trunk = self._create_test_trunk(parent_port) orig_trunk_obj = self._get_trunk_obj(trunk['id']) trunk_req = {'trunk': {'name': 'foo'}} self.trunk_plugin.update_trunk(self.context, trunk['id'], trunk_req) trunk_obj = self._get_trunk_obj(trunk['id']) payload = callbacks.TrunkPayload(self.context, trunk['id'], original_trunk=orig_trunk_obj, current_trunk=trunk_obj) callback.assert_called_once_with( constants.TRUNK, event, self.trunk_plugin, payload=payload) def test_trunk_update_notify_after_update(self): self._test_trunk_update_notify(events.AFTER_UPDATE) def test_trunk_update_notify_precommit_update(self): # TODO(boden): refactor back into _test_trunk_update_notify # once all code uses neutron-lib payloads with self.port() as parent_port: callback = register_mock_callback( constants.TRUNK, events.PRECOMMIT_UPDATE) trunk = self._create_test_trunk(parent_port) orig_trunk_obj = self._get_trunk_obj(trunk['id']) trunk_req = {'trunk': {'name': 'foo'}} self.trunk_plugin.update_trunk(self.context, trunk['id'], trunk_req) trunk_obj = self._get_trunk_obj(trunk['id']) callback.assert_called_once_with( constants.TRUNK, events.PRECOMMIT_UPDATE, self.trunk_plugin, payload=mock.ANY) call_payload = callback.call_args[1]['payload'] self.assertEqual(orig_trunk_obj, call_payload.states[0]) self.assertEqual(trunk_obj, call_payload.desired_state) def _test_trunk_delete_notify(self, event): with self.port() as parent_port: callback = register_mock_callback(constants.TRUNK, event) trunk = self._create_test_trunk(parent_port) trunk_obj = self._get_trunk_obj(trunk['id']) self.trunk_plugin.delete_trunk(self.context, trunk['id']) payload = callbacks.TrunkPayload(self.context, trunk['id'], original_trunk=trunk_obj) callback.assert_called_once_with( constants.TRUNK, event, self.trunk_plugin, payload=payload) def test_delete_trunk_notify_after_delete(self): self._test_trunk_delete_notify(events.AFTER_DELETE) def test_delete_trunk_notify_precommit_delete(self): self._test_trunk_delete_notify(events.PRECOMMIT_DELETE) def _test_subport_action_empty_list_no_notify(self, event, subport_method): with self.port() as parent_port: trunk = self._create_test_trunk(parent_port) callback = register_mock_callback(constants.SUBPORTS, event) subport_method(self.context, trunk['id'], {'sub_ports': []}) callback.assert_not_called() def _test_add_subports_no_notification(self, event): self._test_subport_action_empty_list_no_notify( event, self.trunk_plugin.add_subports) def test_add_subports_notify_after_create_empty_list(self): self._test_add_subports_no_notification(events.AFTER_CREATE) def test_add_subports_notify_precommit_create_empty_list(self): self._test_add_subports_no_notification(events.PRECOMMIT_CREATE) def _test_remove_subports_no_notification(self, event): self._test_subport_action_empty_list_no_notify( event, self.trunk_plugin.remove_subports) def test_remove_subports_notify_after_delete_empty_list(self): self._test_remove_subports_no_notification(events.AFTER_DELETE) def test_remove_subports_notify_precommit_delete_empty_list(self): self._test_remove_subports_no_notification(events.PRECOMMIT_DELETE) def _test_add_subports_notify(self, event): with self.port() as parent_port, self.port() as child_port: trunk = self._create_test_trunk(parent_port) orig_trunk_obj = self._get_trunk_obj(trunk['id']) subport = create_subport_dict(child_port['port']['id']) callback = register_mock_callback(constants.SUBPORTS, event) self.trunk_plugin.add_subports( self.context, trunk['id'], {'sub_ports': [subport]}) trunk_obj = self._get_trunk_obj(trunk['id']) subport_obj = self._get_subport_obj(subport['port_id']) payload = callbacks.TrunkPayload(self.context, trunk['id'], current_trunk=trunk_obj, original_trunk=orig_trunk_obj, subports=[subport_obj]) callback.assert_called_once_with( constants.SUBPORTS, event, self.trunk_plugin, payload=payload) def test_add_subports_notify_after_create(self): self._test_add_subports_notify(events.AFTER_CREATE) def test_add_subports_notify_precommit_create(self): self._test_add_subports_notify(events.PRECOMMIT_CREATE) def _test_remove_subports_notify(self, event): with self.port() as parent_port, self.port() as child_port: subport = create_subport_dict(child_port['port']['id']) trunk = self._create_test_trunk(parent_port, [subport]) orig_trunk_obj = self._get_trunk_obj(trunk['id']) callback = register_mock_callback(constants.SUBPORTS, event) subport_obj = self._get_subport_obj(subport['port_id']) self.trunk_plugin.remove_subports( self.context, trunk['id'], {'sub_ports': [subport]}) trunk_obj = self._get_trunk_obj(trunk['id']) payload = callbacks.TrunkPayload(self.context, trunk['id'], current_trunk=trunk_obj, original_trunk=orig_trunk_obj, subports=[subport_obj]) callback.assert_called_once_with( constants.SUBPORTS, event, self.trunk_plugin, payload=payload) def test_remove_subports_notify_after_delete(self): self._test_remove_subports_notify(events.AFTER_DELETE) def test_remove_subports_notify_precommit_delete(self): self._test_remove_subports_notify(events.PRECOMMIT_DELETE) def test_create_trunk_in_down_state(self): with self.port() as port: trunk = self._create_test_trunk(port) self.assertEqual( constants.DOWN_STATUS, trunk['status']) def test_add_subports_trunk_in_error_state_raises(self): with self.port() as port, self.port() as subport: trunk = self._create_test_trunk(port) trunk_obj = self._get_trunk_obj(trunk['id']) trunk_obj.status = constants.ERROR_STATUS trunk_obj.update() s = create_subport_dict(subport['port']['id']) self.assertRaises(trunk_exc.TrunkInErrorState, self.trunk_plugin.add_subports, self.context, trunk['id'], {'sub_ports': [s]}) def test_add_subports_trunk_goes_to_down(self): with self.port() as port, self.port() as subport: trunk = self._create_test_trunk(port) trunk_obj = self._get_trunk_obj(trunk['id']) trunk_obj.status = constants.ACTIVE_STATUS trunk_obj.update() s = create_subport_dict(subport['port']['id']) trunk = self.trunk_plugin.add_subports( self.context, trunk['id'], {'sub_ports': [s]}) self.assertEqual(constants.DOWN_STATUS, trunk['status']) def test_remove_subports_trunk_goes_to_down(self): with self.port() as port, self.port() as subport: s = create_subport_dict(subport['port']['id']) trunk = self._create_test_trunk(port, [s]) trunk_obj = self._get_trunk_obj(trunk['id']) trunk_obj.status = constants.ACTIVE_STATUS trunk_obj.update() trunk = self.trunk_plugin.remove_subports( self.context, trunk['id'], {'sub_ports': [{'port_id': subport['port']['id']}]}) self.assertEqual(constants.DOWN_STATUS, trunk['status']) def test__trigger_trunk_status_change_vif_type_changed_unbound(self): callback = register_mock_callback(constants.TRUNK, events.AFTER_UPDATE) with self.port() as parent: parent[portbindings.VIF_TYPE] = portbindings.VIF_TYPE_UNBOUND original_port = {portbindings.VIF_TYPE: 'fakeviftype'} original_trunk, current_trunk = ( self._test__trigger_trunk_status_change( parent, original_port, constants.ACTIVE_STATUS, constants.DOWN_STATUS)) payload = callbacks.TrunkPayload(self.context, original_trunk['id'], original_trunk=original_trunk, current_trunk=current_trunk) callback.assert_called_once_with( constants.TRUNK, events.AFTER_UPDATE, self.trunk_plugin, payload=payload) def test__trigger_trunk_status_change_vif_type_unchanged(self): with self.port() as parent: parent[portbindings.VIF_TYPE] = 'fakeviftype' original_port = {portbindings.VIF_TYPE: 'fakeviftype'} self._test__trigger_trunk_status_change(parent, original_port, constants.ACTIVE_STATUS, constants.ACTIVE_STATUS) def test__trigger_trunk_status_change_vif_type_changed(self): with self.port() as parent: parent[portbindings.VIF_TYPE] = 'realviftype' original_port = {portbindings.VIF_TYPE: 'fakeviftype'} self._test__trigger_trunk_status_change(parent, original_port, constants.ACTIVE_STATUS, constants.ACTIVE_STATUS) def _test__trigger_trunk_status_change(self, new_parent, original_parent, initial_trunk_status, final_trunk_status): trunk = self._create_test_trunk(new_parent) trunk = self._get_trunk_obj(trunk['id']) trunk.update(status=initial_trunk_status) trunk_details = {'trunk_id': trunk.id} new_parent['trunk_details'] = trunk_details original_parent['trunk_details'] = trunk_details kwargs = {'context': self.context, 'port': new_parent, 'original_port': original_parent} self.trunk_plugin._trigger_trunk_status_change(resources.PORT, events.AFTER_UPDATE, None, **kwargs) current_trunk = self._get_trunk_obj(trunk.id) self.assertEqual(final_trunk_status, current_trunk.status) return trunk, current_trunk class TrunkPluginCompatDriversTestCase(test_plugin.Ml2PluginV2TestCase): def setUp(self): super(TrunkPluginCompatDriversTestCase, self).setUp() mock.patch.object(drivers, 'register').start() def test_plugin_fails_to_start_no_loaded_drivers(self): with testtools.ExpectedException( trunk_exc.IncompatibleTrunkPluginConfiguration): trunk_plugin.TrunkPlugin() def test_plugins_fails_to_start_seg_type_validator_not_found(self): fakes.FakeDriver.create() with mock.patch.object( validators, 'get_validator', side_effect=KeyError), \ testtools.ExpectedException( trunk_exc.SegmentationTypeValidatorNotFound): trunk_plugin.TrunkPlugin() def test_plugins_fails_to_start_conflicting_seg_types(self): fakes.FakeDriver.create() fakes.FakeDriver2.create() with testtools.ExpectedException( trunk_exc.IncompatibleDriverSegmentationTypes): trunk_plugin.TrunkPlugin() def test_plugin_with_fake_driver(self): with mock.patch.object(validators, 'get_validator', return_value={'foo_seg_types': mock.ANY}): fake_driver = fakes.FakeDriver.create() plugin = trunk_plugin.TrunkPlugin() self.assertTrue(fake_driver.is_loaded) self.assertEqual(set([]), plugin.supported_agent_types) self.assertEqual(set(['foo_intfs']), plugin.supported_interfaces) self.assertEqual([fake_driver], plugin.registered_drivers) neutron-12.1.1/neutron/tests/unit/services/trunk/__init__.py0000664000175000017500000000000013553660046024220 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/test_provider_configuration.py0000664000175000017500000002616413553660047027162 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import shutil import mock from neutron_lib import exceptions as n_exc from neutron_lib.plugins import constants from oslo_config import cfg from neutron import manager from neutron.services import provider_configuration as provconf from neutron.tests import base class ParseServiceProviderConfigurationTestCase(base.BaseTestCase): def setUp(self): super(ParseServiceProviderConfigurationTestCase, self).setUp() self.service_providers = mock.patch.object( provconf.NeutronModule, 'service_providers').start() def _set_override(self, service_providers): self.service_providers.return_value = service_providers def test_default_service_provider_configuration(self): providers = cfg.CONF.service_providers.service_provider self.assertEqual([], providers) def test_parse_single_service_provider_opt(self): self._set_override([constants.LOADBALANCER + ':lbaas:driver_path']) expected = {'service_type': constants.LOADBALANCER, 'name': 'lbaas', 'driver': 'driver_path', 'default': False} res = provconf.parse_service_provider_opt() self.assertEqual(1, len(res)) self.assertEqual([expected], res) def test_parse_single_default_service_provider_opt(self): self._set_override([constants.LOADBALANCER + ':lbaas:driver_path:default']) expected = {'service_type': constants.LOADBALANCER, 'name': 'lbaas', 'driver': 'driver_path', 'default': True} res = provconf.parse_service_provider_opt() self.assertEqual(1, len(res)) self.assertEqual([expected], res) def test_parse_multi_service_provider_opt(self): self._set_override([constants.LOADBALANCER + ':lbaas:driver_path', constants.LOADBALANCER + ':name1:path1', constants.LOADBALANCER + ':name2:path2:default']) res = provconf.parse_service_provider_opt() # This parsing crosses repos if additional projects are installed, # so check that at least what we expect is there; there may be more. self.assertGreaterEqual(len(res), 3) def test_parse_service_provider_invalid_format(self): self._set_override([constants.LOADBALANCER + ':lbaas:driver_path', 'svc_type:name1:path1:def']) self.assertRaises(n_exc.Invalid, provconf.parse_service_provider_opt) self._set_override([constants.LOADBALANCER + ':', 'svc_type:name1:path1:def']) self.assertRaises(n_exc.Invalid, provconf.parse_service_provider_opt) def test_parse_service_provider_name_too_long(self): name = 'a' * 256 self._set_override([constants.LOADBALANCER + ':' + name + ':driver_path', 'svc_type:name1:path1:def']) self.assertRaises(n_exc.Invalid, provconf.parse_service_provider_opt) class ProviderConfigurationTestCase(base.BaseTestCase): def setUp(self): super(ProviderConfigurationTestCase, self).setUp() self.service_providers = mock.patch.object( provconf.NeutronModule, 'service_providers').start() def _set_override(self, service_providers): self.service_providers.return_value = service_providers def test_ensure_driver_unique(self): pconf = provconf.ProviderConfiguration() pconf.providers[('svctype', 'name')] = {'driver': 'driver', 'default': True} self.assertRaises(n_exc.Invalid, pconf._ensure_driver_unique, 'driver') self.assertIsNone(pconf._ensure_driver_unique('another_driver1')) def test_ensure_default_unique(self): pconf = provconf.ProviderConfiguration() pconf.providers[('svctype', 'name')] = {'driver': 'driver', 'default': True} self.assertRaises(n_exc.Invalid, pconf._ensure_default_unique, 'svctype', True) self.assertIsNone(pconf._ensure_default_unique('svctype', False)) self.assertIsNone(pconf._ensure_default_unique('svctype1', True)) self.assertIsNone(pconf._ensure_default_unique('svctype1', False)) def test_add_provider(self): pconf = provconf.ProviderConfiguration() prov = {'service_type': constants.LOADBALANCER, 'name': 'name', 'driver': 'path', 'default': False} pconf.add_provider(prov) self.assertEqual(1, len(pconf.providers)) self.assertEqual([(constants.LOADBALANCER, 'name')], list(pconf.providers.keys())) self.assertEqual([{'driver': 'path', 'default': False}], list(pconf.providers.values())) def test_add_duplicate_provider(self): pconf = provconf.ProviderConfiguration() prov = {'service_type': constants.LOADBALANCER, 'name': 'name', 'driver': 'path', 'default': False} pconf.add_provider(prov) self.assertRaises(n_exc.Invalid, pconf.add_provider, prov) self.assertEqual(1, len(pconf.providers)) def test_get_service_providers(self): self._set_override([constants.LOADBALANCER + ':name:path', constants.LOADBALANCER + ':name2:path2', 'st2:name:driver:default', 'st3:name2:driver2:default']) provs = [{'service_type': constants.LOADBALANCER, 'name': 'name', 'driver': 'path', 'default': False}, {'service_type': constants.LOADBALANCER, 'name': 'name2', 'driver': 'path2', 'default': False}, {'service_type': 'st2', 'name': 'name', 'driver': 'driver', 'default': True }, {'service_type': 'st3', 'name': 'name2', 'driver': 'driver2', 'default': True}] pconf = provconf.ProviderConfiguration() for prov in provs: p = pconf.get_service_providers( filters={'name': [prov['name']], 'service_type': prov['service_type']} ) self.assertEqual([prov], p) def test_get_service_providers_with_fields(self): self._set_override([constants.LOADBALANCER + ":name:path", constants.LOADBALANCER + ":name2:path2"]) provs = [{'service_type': constants.LOADBALANCER, 'name': 'name', 'driver': 'path', 'default': False}, {'service_type': constants.LOADBALANCER, 'name': 'name2', 'driver': 'path2', 'default': False}] pconf = provconf.ProviderConfiguration() for prov in provs: p = pconf.get_service_providers( filters={'name': [prov['name']], 'service_type': prov['service_type']}, fields=['name'] ) self.assertEqual([{'name': prov['name']}], p) class GetProviderDriverClassTestCase(base.BaseTestCase): def test_get_provider_driver_class_hit(self): driver = 'ml2' expected = 'neutron.plugins.ml2.plugin.Ml2Plugin' actual = provconf.get_provider_driver_class( driver, namespace=manager.CORE_PLUGINS_NAMESPACE) self.assertEqual(expected, actual) def test_get_provider_driver_class_miss(self): retval = provconf.get_provider_driver_class('foo') self.assertEqual('foo', retval) class NeutronModuleTestCase(base.BaseTestCase): def test_can_parse_multi_opt_service_provider_from_conf_file(self): mod = provconf.NeutronModule('neutron_test') mod.ini(base.ETCDIR) self.assertEqual(['foo', 'bar'], mod.service_providers(), 'Expected two providers, only one read') class NeutronModuleConfigDirTestCase(base.BaseTestCase): def setup_config(self): self.config_parse(args=['--config-dir', base.ETCDIR]) def test_can_parse_multi_opt_service_provider_from_conf_dir(self): mod = provconf.NeutronModule('neutron_test') mod.ini() self.assertEqual(['foo', 'bar'], mod.service_providers()) class NeutronModuleMultiConfigDirTestCase(base.BaseTestCase): def setUp(self): self.tmpdir = self.get_default_temp_dir().path shutil.copyfile( os.path.join(base.ETCDIR, 'neutron_test2.conf.example'), os.path.join(self.tmpdir, 'neutron_test.conf')) super(NeutronModuleMultiConfigDirTestCase, self).setUp() def setup_config(self): self.config_parse(args=[ # NOTE(ihrachys): we expect the second directory to be checked '--config-dir', self.tmpdir, '--config-dir', base.ETCDIR ]) def test_read_configuration_from_all_matching_files(self): mod = provconf.NeutronModule('neutron_test') mod.ini() self.assertEqual(['zzz', 'foo', 'bar'], mod.service_providers()) class NeutronModuleMultiConfigFileTestCase(base.BaseTestCase): def setUp(self): self.tmpdir = self.get_default_temp_dir().path self.filepath1 = os.path.join(self.tmpdir, 'neutron_test.conf') self.filepath2 = os.path.join(base.ETCDIR, 'neutron_test.conf') shutil.copyfile( os.path.join(base.ETCDIR, 'neutron_test2.conf.example'), self.filepath1) super(NeutronModuleMultiConfigFileTestCase, self).setUp() def setup_config(self): self.config_parse(args=[ # NOTE(ihrachys): we expect both directories to be checked '--config-file', self.filepath1, '--config-file', self.filepath2 ]) def test_read_configuration_from_all_matching_files(self): mod = provconf.NeutronModule('neutron_test') mod.ini() self.assertEqual(['zzz', 'foo', 'bar'], mod.service_providers()) class NeutronModuleConfigNotParsedTestCase(base.DietTestCase): def setup_config(self): pass def test_ini_no_crash_if_config_files_not_parsed(self): mod = provconf.NeutronModule('neutron_test') mod.ini() neutron-12.1.1/neutron/tests/unit/services/logapi/0000775000175000017500000000000013553660157022234 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/logapi/rpc/0000775000175000017500000000000013553660157023020 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/logapi/rpc/__init__.py0000664000175000017500000000000013553660046025114 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/logapi/rpc/test_server.py0000664000175000017500000000737013553660047025744 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg import oslo_messaging from neutron.api.rpc.callbacks import events from neutron.api.rpc.handlers import resources_rpc from neutron.services.logapi.common import constants as log_const from neutron.services.logapi.rpc import server as server_rpc from neutron.tests import base class LoggingApiNotificationTestCase(base.BaseTestCase): def setUp(self): super(LoggingApiNotificationTestCase, self).setUp() self.test_obj = server_rpc.LoggingApiNotification() def test___init__(self): self.assertIsInstance(self.test_obj.notification_api, resources_rpc.ResourcesPushRpcApi) @mock.patch("neutron.api.rpc.handlers.resources_rpc.ResourcesPushRpcApi." "push") def test_create_log(self, mocked_push): m_context = mock.Mock() m_log_resource = mock.Mock() self.test_obj.create_log(m_context, m_log_resource) mocked_push.assert_called_with(m_context, [m_log_resource], events.CREATED) @mock.patch("neutron.api.rpc.handlers.resources_rpc.ResourcesPushRpcApi." "push") def test_update_log(self, mocked_push): m_context = mock.Mock() m_log_resource = mock.Mock() self.test_obj.update_log(m_context, m_log_resource) mocked_push.assert_called_with(m_context, [m_log_resource], events.UPDATED) @mock.patch("neutron.api.rpc.handlers.resources_rpc.ResourcesPushRpcApi." "push") def test_delete_log(self, mocked_push): m_context = mock.Mock() m_log_resource = mock.Mock() self.test_obj.delete_log(m_context, m_log_resource) mocked_push.assert_called_with(m_context, [m_log_resource], events.DELETED) class LoggingApiSkeletonTestCase(base.BaseTestCase): @mock.patch("neutron.common.rpc.get_server") def test___init__(self, mocked_get_server): test_obj = server_rpc.LoggingApiSkeleton() _target = oslo_messaging.Target( topic=log_const.LOGGING_PLUGIN, server=cfg.CONF.host, fanout=False) mocked_get_server.assert_called_with(_target, [test_obj]) @mock.patch("neutron.services.logapi.common.db_api." "get_sg_log_info_for_port") def test_get_sg_log_info_for_port(self, mock_callback): test_obj = server_rpc.LoggingApiSkeleton() m_context = mock.Mock() port_id = '123' test_obj.get_sg_log_info_for_port(m_context, port_id=port_id) mock_callback.assert_called_with(m_context, port_id) @mock.patch("neutron.services.logapi.common.db_api." "get_sg_log_info_for_log_resources") def test_get_sg_log_info_for_log_resources(self, mock_callback): test_obj = server_rpc.LoggingApiSkeleton() m_context = mock.Mock() log_resources = [mock.Mock()] test_obj.get_sg_log_info_for_log_resources(m_context, log_resources=log_resources) mock_callback.assert_called_with(m_context, log_resources) neutron-12.1.1/neutron/tests/unit/services/logapi/drivers/0000775000175000017500000000000013553660157023712 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/logapi/drivers/test_manager.py0000664000175000017500000001233113553660047026733 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.common import exceptions from neutron.services.logapi.common import constants as log_const from neutron.services.logapi.common import exceptions as log_exc from neutron.services.logapi.drivers import base as log_driver_base from neutron.services.logapi.drivers import manager as driver_mgr from neutron.tests.unit.services.logapi import base class TestGetParameter(base.BaseLogTestCase): def test__get_param_missing_parameter(self): kwargs = {'context': mock.sentinel.context} self.assertRaises(log_exc.LogapiDriverException, driver_mgr._get_param, args=[], kwargs=kwargs, name='log_obj', index=1) self.assertRaises(log_exc.LogapiDriverException, driver_mgr._get_param, args=[mock.sentinel.context], kwargs={}, name='log_obj', index=1) self.assertRaises(log_exc.LogapiDriverException, driver_mgr._get_param, args=[], kwargs={'log_obj': mock.sentinel.log_obj}, name='context', index=0) class TestLogDriversManagerBase(base.BaseLogTestCase): def setUp(self): super(TestLogDriversManagerBase, self).setUp() self.config_parse() self.setup_coreplugin(load_plugins=False) @staticmethod def _create_manager_with_drivers(drivers_details): for name, driver_details in drivers_details.items(): class LogDriver(log_driver_base.DriverBase): @property def is_loaded(self): return driver_details['is_loaded'] LogDriver(name, driver_details.get('vif_types', []), driver_details.get('vnic_types', []), driver_details.get('supported_logging_types', [])) return driver_mgr.LoggingServiceDriverManager() class TestLogDriversManagerMulti(TestLogDriversManagerBase): """Test calls happen to all drivers""" def test_driver_manager_empty_with_no_drivers(self): driver_manager = self._create_manager_with_drivers({}) self.assertEqual(0, len(driver_manager.drivers)) def test_driver_manager_empty_with_no_loaded_drivers(self): driver_manager = self._create_manager_with_drivers( {'driver-A': {'is_loaded': False}}) self.assertEqual(0, len(driver_manager.drivers)) def test_driver_manager_with_one_loaded_driver(self): driver_manager = self._create_manager_with_drivers( {'driver-A': {'is_loaded': True}}) self.assertEqual(1, len(driver_manager.drivers)) def test_driver_manager_with_two_loaded_drivers(self): driver_manager = self._create_manager_with_drivers( {'driver-A': {'is_loaded': True}, 'driver-B': {'is_loaded': True}}) self.assertEqual(2, len(driver_manager.drivers)) class TestLogDriversManagerLoggingTypes(TestLogDriversManagerBase): """Test supported logging types""" def test_available_logging_types(self): driver_manager = self._create_manager_with_drivers( {'driver-A': {'is_loaded': True, 'supported_logging_types': ['security_group']}, 'driver-B': {'is_loaded': True, 'supported_logging_types': ['security_group', 'firewall']} }) self.assertEqual(set(['security_group', 'firewall']), driver_manager.supported_logging_types) class TestLogDriversCalls(TestLogDriversManagerBase): """Test log driver calls""" def setUp(self): super(TestLogDriversCalls, self).setUp() self.driver_manager = self._create_manager_with_drivers( {'driver-A': {'is_loaded': True}}) def test_implemented_call_methods(self): for method in log_const.LOG_CALL_METHODS: with mock.patch.object(log_driver_base.DriverBase, method) as \ method_fnc: context = mock.sentinel.context log_obj = mock.sentinel.log_obj self.driver_manager.call( method, context=context, log_objs=[log_obj]) method_fnc.assert_called_once_with( context=context, log_objs=[log_obj]) def test_not_implemented_call_methods(self): context = mock.sentinel.context log_obj = mock.sentinel.log_obj self.assertRaises(exceptions.DriverCallError, self.driver_manager.call, 'wrong_method', context=context, log_objs=[log_obj]) neutron-12.1.1/neutron/tests/unit/services/logapi/drivers/test_base.py0000664000175000017500000000375713553660046026246 0ustar zuulzuul00000000000000# Copyright (C) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron.services.logapi.drivers import base as log_base_driver from neutron.tests import base SUPPORTED_LOGGING_TYPES = ['security_group'] class FakeDriver(log_base_driver.DriverBase): @staticmethod def create(): return FakeDriver( name='fake_driver', vif_types=[portbindings.VIF_TYPE_OVS], vnic_types=[portbindings.VNIC_NORMAL], supported_logging_types=SUPPORTED_LOGGING_TYPES, requires_rpc=False ) class TestDriverBase(base.BaseTestCase): def setUp(self): super(TestDriverBase, self).setUp() self.driver = FakeDriver.create() def test_is_vif_type_compatible(self): self.assertFalse( self.driver.is_vif_type_compatible(portbindings.VIF_TYPE_OTHER)) self.assertTrue( self.driver.is_vif_type_compatible(portbindings.VIF_TYPE_OVS)) def test_is_vnic_compatible(self): self.assertFalse( self.driver.is_vnic_compatible(portbindings.VNIC_BAREMETAL)) self.assertTrue( self.driver.is_vnic_compatible(portbindings.VNIC_NORMAL)) def test_is_logging_type_supported(self): self.assertTrue( self.driver.is_logging_type_supported('security_group')) self.assertFalse(self.driver.is_logging_type_supported('firewall')) neutron-12.1.1/neutron/tests/unit/services/logapi/drivers/openvswitch/0000775000175000017500000000000013553660157026263 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/logapi/drivers/openvswitch/test_ovs_firewall_log.py0000664000175000017500000003112713553660047033233 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants from oslo_config import cfg from oslo_utils import uuidutils from neutron.agent.common import ovs_lib from neutron.agent import firewall from neutron.common import constants as n_const from neutron.objects.logapi import logging_resource as log_object from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts from neutron.services.logapi.common import exceptions as log_exc from neutron.services.logapi.drivers.openvswitch \ import ovs_firewall_log as ovsfw_log from neutron.services.logapi.rpc import agent as agent_rpc from neutron.tests import base from neutron.tests import tools COOKIE_ID = uuidutils.generate_uuid() PORT_ID = uuidutils.generate_uuid() PROJECT_ID = uuidutils.generate_uuid() ACTION = tools.get_random_security_event() LOG_ID = uuidutils.generate_uuid() SG_ID = uuidutils.generate_uuid() REMOTE_SG_ID = uuidutils.generate_uuid() FakeSGLogInfo = [ { 'id': LOG_ID, 'ports_log': [{'port_id': PORT_ID, 'security_group_rules': [ {'ethertype': constants.IPv4, 'protocol': constants.PROTO_NAME_TCP, 'direction': firewall.INGRESS_DIRECTION, 'port_range_min': 123, 'port_range_max': 123, 'security_group_id': SG_ID}, {'ethertype': constants.IPv4, 'protocol': constants.PROTO_NAME_UDP, 'direction': firewall.EGRESS_DIRECTION, 'security_group_id': SG_ID}, {'ethertype': constants.IPv6, 'protocol': constants.PROTO_NAME_TCP, 'remote_group_id': REMOTE_SG_ID, 'direction': firewall.EGRESS_DIRECTION, 'security_group_id': SG_ID} ]}], 'event': 'ALL', 'project_id': PROJECT_ID, } ] def set_log_driver_config(ctrl_rate_limit, ctrl_burst_limit): cfg.CONF.set_override('rate_limit', ctrl_rate_limit, group='network_log') cfg.CONF.set_override('burst_limit', ctrl_burst_limit, group='network_log') class TestCookie(base.BaseTestCase): def setUp(self): super(TestCookie, self).setUp() self.cookie = ovsfw_log.Cookie(COOKIE_ID, PORT_ID, ACTION, PROJECT_ID) self.cookie.log_object_refs = set([LOG_ID]) def test_add_log_object_refs(self): new_log_id = uuidutils.generate_uuid() expected = set([LOG_ID, new_log_id]) self.cookie.add_log_obj_ref(new_log_id) self.assertEqual(expected, self.cookie.log_object_refs) def test_removed_log_object_ref(self): expected = set() self.cookie.remove_log_obj_ref(LOG_ID) self.assertEqual(expected, self.cookie.log_object_refs) def test_is_empty(self): self.cookie.remove_log_obj_ref(LOG_ID) result = self.cookie.is_empty self.assertTrue(result) class FakeOVSPort(object): def __init__(self, name, port, mac): self.port_name = name self.ofport = port self.vif_mac = mac class TestOVSFirewallLoggingDriver(base.BaseTestCase): def setUp(self): super(TestOVSFirewallLoggingDriver, self).setUp() mock_bridge = mock.patch.object( ovs_lib, 'OVSBridge', autospec=True).start() self.log_driver = ovsfw_log.OVSFirewallLoggingDriver(mock_bridge) resource_rpc_mock = mock.patch.object( agent_rpc, 'LoggingApiStub', autospec=True).start() self.log_driver.start_logapp = mock.Mock() self.log_driver.initialize(resource_rpc_mock) self.log_driver.SUPPORTED_LOGGING_TYPES = ['security_group'] self.mock_bridge = self.log_driver.int_br self.mock_bridge.reset_mock() self.fake_ovs_port = FakeOVSPort('port', 1, '00:00:00:00:00:00') self.mock_bridge.br.get_vif_port_by_id.return_value = \ self.fake_ovs_port log_data = { 'context': None, 'name': 'test1', 'id': LOG_ID, 'project_id': PROJECT_ID, 'event': 'ALL', 'resource_type': 'security_group' } self.log_resource = log_object.Log(**log_data) @property def port_ofport(self): return self.mock_bridge.br.get_vif_port_by_id.return_value.ofport @property def port_mac(self): return self.mock_bridge.br.get_vif_port_by_id.return_value.vif_mac def test_initialize_bridge(self): br = self.log_driver.initialize_bridge(self.mock_bridge) self.assertEqual(self.mock_bridge.deferred.return_value, br) def test_set_controller_rate_limit(self): set_log_driver_config(100, 25) self.log_driver.initialize_bridge(self.mock_bridge) expected_calls = [mock.call.set_controller_rate_limit(100), mock.call.set_controller_burst_limit(25)] self.mock_bridge.assert_has_calls(expected_calls) def test_generate_cookie(self): cookie_id = self.log_driver.generate_cookie( PORT_ID, ACTION, LOG_ID, PROJECT_ID) cookie = self.log_driver._get_cookie_by_id(cookie_id) self.assertIn(cookie, self.log_driver.cookies_table) def test__get_cookie_by_id_not_found(self): cookie_id = uuidutils.generate_uuid() cookie = ovsfw_log.Cookie(cookie_id=uuidutils.generate_uuid(), port=PORT_ID, action=ACTION, project=PROJECT_ID) self.log_driver.cookies_table = set([cookie]) self.assertRaises(log_exc.CookieNotFound, self.log_driver._get_cookie_by_id, cookie_id) def test_start_log_with_update_or_create_log_event(self): context = mock.Mock() log_data = {'log_resources': [self.log_resource]} self.log_driver.resource_rpc.get_sg_log_info_for_log_resources.\ return_value = FakeSGLogInfo self.log_driver.start_logging(context, **log_data) accept_cookie = self.log_driver._get_cookie(PORT_ID, 'ACCEPT') drop_cookie = self.log_driver._get_cookie(PORT_ID, 'DROP') conj_id = self.log_driver.conj_id_map.get_conj_id( SG_ID, REMOTE_SG_ID, firewall.EGRESS_DIRECTION, constants.IPv6) add_rules = [ # log ingress tcp port=123 mock.call( actions='controller', cookie=accept_cookie.id, reg5=self.port_ofport, dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_TCP, priority=77, table=ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE, tcp_dst='0x007b'), # log egress tcp6 mock.call( actions='resubmit(,%d),controller' % ( ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE), cookie=accept_cookie.id, reg5=self.port_ofport, dl_type="0x{:04x}".format(n_const.ETHERTYPE_IPV6), priority=70, reg7=conj_id + 1, table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE), # log egress udp mock.call( actions='resubmit(,%d),controller' % ( ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE), cookie=accept_cookie.id, reg5=self.port_ofport, dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_UDP, priority=77, table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE, ), # log drop mock.call( actions='controller', cookie=drop_cookie.id, priority=53, reg5=self.port_ofport, table=ovs_consts.DROPPED_TRAFFIC_TABLE, ) ] self.mock_bridge.br.add_flow.assert_has_calls( add_rules, any_order=True) def test_stop_log_with_delete_log_event(self): context = mock.Mock() log_data = {'log_resources': [self.log_resource]} self.log_driver.resource_rpc.get_sg_log_info_for_log_resources.\ return_value = FakeSGLogInfo self.log_driver.start_logging(context, **log_data) accept_cookie = self.log_driver._get_cookie(PORT_ID, 'ACCEPT') drop_cookie = self.log_driver._get_cookie(PORT_ID, 'DROP') self.mock_bridge.reset_mock() self.log_driver.stop_logging(context, **log_data) delete_rules = [ # delete drop flow mock.call( table=ovs_consts.DROPPED_TRAFFIC_TABLE, cookie=drop_cookie.id ), # delete accept flows mock.call( table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE, cookie=accept_cookie.id ), mock.call( table=ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE, cookie=accept_cookie.id ) ] self.mock_bridge.br.delete_flows.assert_has_calls( delete_rules, any_order=True) def test_start_log_with_add_port_event(self): context = mock.Mock() log_data = {'port_id': PORT_ID} self.log_driver.resource_rpc.get_sg_log_info_for_port.return_value = \ [ { 'id': uuidutils.generate_uuid(), 'ports_log': [{'port_id': PORT_ID, 'security_group_rules': [ {'ethertype': constants.IPv4, 'protocol': constants.PROTO_NAME_TCP, 'direction': firewall.INGRESS_DIRECTION, 'port_range_min': 123, 'port_range_max': 123, 'security_group_id': 456}]}], 'event': 'ACCEPT', 'project_id': PROJECT_ID, } ] self.log_driver.start_logging(context, **log_data) accept_cookie = self.log_driver._get_cookie(PORT_ID, 'ACCEPT') add_rules = [ # log ingress tcp port=123 mock.call( actions='controller', cookie=accept_cookie.id, reg5=self.port_ofport, dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_TCP, priority=77, table=ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE, tcp_dst='0x007b') ] self.mock_bridge.br.add_flow.assert_has_calls( add_rules, any_order=True) def test_stop_log_with_delete_port_event(self): context = mock.Mock() log_data = {'port_id': PORT_ID} # add port self.log_driver.resource_rpc.get_sg_log_info_for_port.return_value = \ FakeSGLogInfo self.log_driver.start_logging(context, **log_data) accept_cookie = self.log_driver._get_cookie(PORT_ID, 'ACCEPT') drop_cookie = self.log_driver._get_cookie(PORT_ID, 'DROP') self.mock_bridge.reset_mock() # delete port self.log_driver.stop_logging( context, port_id=PORT_ID) delete_rules = [ # delete accept flows mock.call( table=ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE, cookie=accept_cookie.id ), mock.call( table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE, cookie=accept_cookie.id ), # delete drop flow mock.call( table=ovs_consts.DROPPED_TRAFFIC_TABLE, cookie=drop_cookie.id ), ] self.mock_bridge.br.delete_flows.assert_has_calls( delete_rules, any_order=True) neutron-12.1.1/neutron/tests/unit/services/logapi/drivers/openvswitch/__init__.py0000664000175000017500000000000013553660046030357 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/logapi/drivers/__init__.py0000664000175000017500000000000013553660046026006 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/logapi/agent/0000775000175000017500000000000013553660157023332 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/logapi/agent/__init__.py0000664000175000017500000000000013553660046025426 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/logapi/agent/test_log_extension.py0000664000175000017500000001213313553660047027616 0ustar zuulzuul00000000000000# Copyright (C) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from oslo_utils import uuidutils from neutron.api.rpc.callbacks.consumer import registry from neutron.api.rpc.callbacks import events from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc from neutron.plugins.ml2.drivers.openvswitch.agent import ( ovs_agent_extension_api as ovs_ext_api) from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import ( ovs_bridge) from neutron.services.logapi.agent import log_extension as log_ext from neutron.tests import base class FakeLogDriver(log_ext.LoggingDriver): SUPPORTED_LOGGING_TYPES = ['security_group'] def initialize(self, resource_rpc, **kwargs): pass def start_logging(self, context, **kwargs): pass def stop_logging(self, context, **kwargs): pass class LoggingExtensionBaseTestCase(base.BaseTestCase): def setUp(self): super(LoggingExtensionBaseTestCase, self).setUp() conn_patcher = mock.patch( 'neutron.agent.ovsdb.impl_idl._connection') conn_patcher.start() self.agent_ext = log_ext.LoggingExtension() self.context = context.get_admin_context() self.connection = mock.Mock() agent_api = ovs_ext_api.OVSAgentExtensionAPI( ovs_bridge.OVSAgentBridge('br-int'), ovs_bridge.OVSAgentBridge('br-tun')) self.agent_ext.consume_api(agent_api) mock.patch( 'neutron.manager.NeutronManager.load_class_for_provider').start() class LoggingExtensionTestCase(LoggingExtensionBaseTestCase): def setUp(self): super(LoggingExtensionTestCase, self).setUp() self.agent_ext.initialize( self.connection, constants.EXTENSION_DRIVER_TYPE) self.log_driver = mock.Mock() log_driver_object = FakeLogDriver() self.log_driver.defer_apply.side_effect = log_driver_object.defer_apply self.agent_ext.log_driver = self.log_driver def _create_test_port_dict(self, device_owner): return {'port_id': uuidutils.generate_uuid(), 'device_owner': device_owner} def test__handle_notification_passes_update_events_enabled_log(self): log_obj = mock.Mock() log_obj.enabled = True self.agent_ext._handle_notification( self.context, 'log', [log_obj], events.UPDATED) self.assertTrue(self.log_driver.start_logging.called) def test__handle_notification_passes_update_events_disabled_log(self): log_obj = mock.Mock() log_obj.enabled = False self.agent_ext._handle_notification( self.context, 'log', [log_obj], events.UPDATED) self.assertTrue(self.log_driver.stop_logging.called) def test__handle_notification_passes_create_events(self): log_obj = mock.Mock() self.agent_ext._handle_notification( self.context, 'log', [log_obj], events.CREATED) self.assertTrue(self.log_driver.start_logging.called) def test__handle_notification_passes_delete_events(self): log_obj = mock.Mock() self.agent_ext._handle_notification( self.context, 'log', [log_obj], events.DELETED) self.assertTrue(self.log_driver.stop_logging.called) def test_handle_port_vm(self): port = self._create_test_port_dict(device_owner='compute:nova') self.agent_ext.handle_port(self.context, port) self.assertTrue(self.log_driver.start_logging.called) def test_handle_not_port_vm(self): port = self._create_test_port_dict( device_owner='network:router_interface') self.agent_ext.handle_port(self.context, port) self.assertFalse(self.log_driver.start_logging.called) class LoggingExtensionInitializeTestCase(LoggingExtensionBaseTestCase): @mock.patch.object(registry, 'register') @mock.patch.object(resources_rpc, 'ResourcesPushRpcCallback') def test_initialize_subscribed_to_rpc(self, rpc_mock, subscribe_mock): self.agent_ext.initialize( self.connection, constants.EXTENSION_DRIVER_TYPE) self.connection.create_consumer.assert_has_calls( [mock.call( resources_rpc.resource_type_versioned_topic(resource_type), [rpc_mock()], fanout=True) for resource_type in self.agent_ext.SUPPORTED_RESOURCE_TYPES] ) subscribe_mock.assert_called_with(mock.ANY, resources.LOGGING_RESOURCE) neutron-12.1.1/neutron/tests/unit/services/logapi/base.py0000664000175000017500000000304313553660047023516 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.api.rpc.callbacks.consumer import registry as cons_registry from neutron.api.rpc.callbacks.producer import registry as prod_registry from neutron.api.rpc.callbacks import resource_manager from neutron.tests.unit import testlib_api class BaseLogTestCase(testlib_api.SqlTestCase): def setUp(self): super(BaseLogTestCase, self).setUp() with mock.patch.object( resource_manager.ResourceCallbacksManager, '_singleton', new_callable=mock.PropertyMock(return_value=False)): self.cons_mgr = resource_manager.ConsumerResourceCallbacksManager() self.prod_mgr = resource_manager.ProducerResourceCallbacksManager() for mgr in (self.cons_mgr, self.prod_mgr): mgr.clear() mock.patch.object( cons_registry, '_get_manager', return_value=self.cons_mgr).start() mock.patch.object( prod_registry, '_get_manager', return_value=self.prod_mgr).start() neutron-12.1.1/neutron/tests/unit/services/logapi/test_logging_plugin.py0000664000175000017500000003547113553660047026661 0ustar zuulzuul00000000000000# Copyright (C) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from neutron_lib.plugins import constants as plugin_const from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import uuidutils from neutron import manager from neutron.objects.logapi import logging_resource as log_object from neutron.objects import ports from neutron.objects import securitygroup as sg_object from neutron.services.logapi.common import exceptions as log_exc from neutron.tests.unit.services.logapi import base DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' SUPPORTED_LOGGING_TYPES = ['security_group'] class TestLoggingPlugin(base.BaseLogTestCase): def setUp(self): super(TestLoggingPlugin, self).setUp() self.setup_coreplugin(load_plugins=False) mock.patch('neutron.objects.db.api.create_object').start() mock.patch('neutron.objects.db.api.update_object').start() mock.patch('neutron.objects.db.api.delete_object').start() mock.patch('neutron.objects.db.api.get_object').start() # We don't use real models as per mocks above. We also need to mock-out # methods that work with real data types mock.patch( 'neutron.objects.base.NeutronDbObject.modify_fields_from_db' ).start() cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) cfg.CONF.set_override("service_plugins", ["neutron.services.logapi.logging_plugin.LoggingPlugin"]) manager.init() self.log_plugin = directory.get_plugin(plugin_const.LOG_API) self.log_plugin.driver_manager = mock.Mock() log_types = mock.PropertyMock(return_value=SUPPORTED_LOGGING_TYPES) self.log_plugin.driver_manager.supported_logging_types = \ mock.patch('neutron.services.logapi.drivers.manager.' 'LoggingServiceDriverManager.supported_logging_types', new_callable=log_types).start() self.ctxt = context.Context('admin', 'fake_tenant') mock.patch.object(self.ctxt.session, 'refresh').start() mock.patch.object(self.ctxt.session, 'expunge').start() def test_get_logs(self): with mock.patch.object(log_object.Log, 'get_objects')\ as get_objects_mock: filters = {'filter': 'filter_id'} self.log_plugin.get_logs(self.ctxt, filters=filters) get_objects_mock.assert_called_once_with(self.ctxt, _pager=mock.ANY, filter='filter_id') def test_get_log_without_return_value(self): with mock.patch.object(log_object.Log, 'get_object', return_value=None): self.assertRaises( log_exc.LogResourceNotFound, self.log_plugin.get_log, self.ctxt, mock.ANY, ) def test_get_log_with_return_value(self): log_id = uuidutils.generate_uuid() with mock.patch.object(log_object.Log, 'get_object')\ as get_object_mock: self.log_plugin.get_log(self.ctxt, log_id) get_object_mock.assert_called_once_with(self.ctxt, id=log_id) @mock.patch('neutron.db._utils.model_query') def test_create_log_full_options(self, query_mock): log = {'log': {'resource_type': 'security_group', 'enabled': True, 'resource_id': uuidutils.generate_uuid(), 'target_id': uuidutils.generate_uuid()}} port = mock.Mock() new_log = mock.Mock() with mock.patch.object(sg_object.SecurityGroup, 'count', return_value=1): with mock.patch.object(ports.Port, 'get_object', return_value=port): with mock.patch('neutron.services.logapi.common.' 'validators.validate_log_type_for_port', return_value=True): with mock.patch('neutron.objects.logapi.' 'logging_resource.Log', return_value=new_log) as init_log_mock: self.log_plugin.create_log(self.ctxt, log) init_log_mock.assert_called_once_with( context=self.ctxt, **log['log']) self.assertTrue(new_log.create.called) calls = [ mock.call.call('create_log_precommit', self.ctxt, new_log), mock.call.call('create_log', self.ctxt, new_log) ] self.log_plugin.driver_manager.assert_has_calls(calls) def test_create_log_without_sg_resource(self): log = {'log': {'resource_type': 'security_group', 'enabled': True, 'target_id': uuidutils.generate_uuid()}} new_log = mock.Mock() new_log.enabled = True port = mock.Mock() with mock.patch.object(ports.Port, 'get_object', return_value=port): with mock.patch('neutron.services.logapi.common.' 'validators.validate_log_type_for_port', return_value=True): with mock.patch('neutron.objects.logapi.logging_resource.Log', return_value=new_log) as init_log_mock: self.log_plugin.create_log(self.ctxt, log) init_log_mock.assert_called_once_with( context=self.ctxt, **log['log']) self.assertTrue(new_log.create.called) calls = [ mock.call.call('create_log_precommit', self.ctxt, new_log), mock.call.call('create_log', self.ctxt, new_log) ] self.log_plugin.driver_manager.assert_has_calls(calls) def test_create_log_without_parent_resource(self): log = {'log': {'resource_type': 'security_group', 'enabled': True, 'resource_id': uuidutils.generate_uuid()}} new_log = mock.Mock() new_log.enabled = True with mock.patch.object(sg_object.SecurityGroup, 'count', return_value=1): with mock.patch('neutron.objects.logapi.logging_resource.Log', return_value=new_log) as init_log_mock: self.log_plugin.create_log(self.ctxt, log) init_log_mock.assert_called_once_with(context=self.ctxt, **log['log']) self.assertTrue(new_log.create.called) calls = [ mock.call.call('create_log_precommit', self.ctxt, new_log), mock.call.call('create_log', self.ctxt, new_log) ] self.log_plugin.driver_manager.assert_has_calls(calls) def test_create_log_without_target(self): log = {'log': {'resource_type': 'security_group', 'enabled': True, }} new_log = mock.Mock() new_log.enabled = True with mock.patch('neutron.objects.logapi.' 'logging_resource.Log', return_value=new_log) as init_log_mock: self.log_plugin.create_log(self.ctxt, log) init_log_mock.assert_called_once_with(context=self.ctxt, **log['log']) self.assertTrue(new_log.create.called) calls = [ mock.call.call('create_log_precommit', self.ctxt, new_log), mock.call.call('create_log', self.ctxt, new_log) ] self.log_plugin.driver_manager.assert_has_calls(calls) def test_create_log_nonexistent_sg_resource(self): log = {'log': {'resource_type': 'security_group', 'enabled': True, 'resource_id': uuidutils.generate_uuid()}} with mock.patch.object(sg_object.SecurityGroup, 'count', return_value=0): self.assertRaises( log_exc.ResourceNotFound, self.log_plugin.create_log, self.ctxt, log) def test_create_log_nonexistent_target(self): log = {'log': {'resource_type': 'security_group', 'enabled': True, 'target_id': uuidutils.generate_uuid()}} with mock.patch.object(ports.Port, 'get_object', return_value=None): self.assertRaises( log_exc.TargetResourceNotFound, self.log_plugin.create_log, self.ctxt, log) def test_create_log_not_bound_port(self): log = {'log': {'resource_type': 'security_group', 'enabled': True, 'resource_id': uuidutils.generate_uuid(), 'target_id': uuidutils.generate_uuid()}} port = mock.Mock() with mock.patch.object(sg_object.SecurityGroup, 'count', return_value=1): with mock.patch.object(ports.Port, 'get_object', return_value=port): with mock.patch('neutron.services.logapi.common.' 'validators.validate_log_type_for_port', return_value=True): self.assertRaises( log_exc.InvalidResourceConstraint, self.log_plugin.create_log, self.ctxt, log) def test_create_log_disabled(self): log_data = {'log': {'resource_type': 'security_group', 'enabled': False}} new_log = mock.Mock() new_log.enabled = False with mock.patch('neutron.objects.logapi.' 'logging_resource.Log', return_value=new_log) as init_log_mock: self.log_plugin.create_log(self.ctxt, log_data) init_log_mock.assert_called_once_with( context=self.ctxt, **log_data['log']) self.assertTrue(new_log.create.called) self.log_plugin.driver_manager.call.assert_not_called() def test_create_log_with_unsupported_logging_type(self): log = {'log': {'resource_type': 'fake_type', 'enabled': True}} self.assertRaises( log_exc.InvalidLogResourceType, self.log_plugin.create_log, self.ctxt, log) def test_create_log_with_unsupported_logging_type_on_port(self): log = {'log': {'resource_type': 'security_group', 'enabled': True, 'target_id': uuidutils.generate_uuid()}} port = mock.Mock() port.id = log['log']['target_id'] with mock.patch.object(ports.Port, 'get_object', return_value=port): with mock.patch('neutron.services.logapi.common.' 'validators.validate_log_type_for_port', return_value=False): self.assertRaises( log_exc.LoggingTypeNotSupported, self.log_plugin.create_log, self.ctxt, log) def test_update_log(self): log_data = {'log': {'enabled': True}} new_log = mock.Mock() new_log.id = uuidutils.generate_uuid() with mock.patch('neutron.objects.logapi.' 'logging_resource.Log', return_value=new_log) as update_log_mock: self.log_plugin.update_log(self.ctxt, new_log.id, log_data) update_log_mock.assert_called_once_with(self.ctxt, id=new_log.id) new_log.update_fields.assert_called_once_with(log_data['log'], reset_changes=True) self.assertTrue(new_log.update.called) calls = [ mock.call.call('update_log_precommit', self.ctxt, new_log), mock.call.call('update_log', self.ctxt, new_log) ] self.log_plugin.driver_manager.assert_has_calls(calls) def test_update_log_none_enabled(self): log_data = {'log': {}} new_log = mock.Mock() new_log.id = uuidutils.generate_uuid() with mock.patch('neutron.objects.logapi.' 'logging_resource.Log', return_value=new_log) as update_log_mock: self.log_plugin.update_log(self.ctxt, new_log.id, log_data) update_log_mock.assert_called_once_with(self.ctxt, id=new_log.id) new_log.update_fields.assert_called_once_with(log_data['log'], reset_changes=True) self.assertTrue(new_log.update.called) self.log_plugin.driver_manager.call.assert_not_called() def test_delete_log(self): delete_log = mock.Mock() delete_log.id = uuidutils.generate_uuid() with mock.patch.object(log_object.Log, 'get_object', return_value=delete_log) as delete_log_mock: self.log_plugin.delete_log(self.ctxt, delete_log.id) delete_log_mock.assert_called_once_with(self.ctxt, id=delete_log.id) self.assertTrue(delete_log.delete.called) calls = [ mock.call.call('delete_log_precommit', self.ctxt, delete_log), mock.call.call('delete_log', self.ctxt, delete_log) ] self.log_plugin.driver_manager.assert_has_calls(calls) def test_delete_nonexistent_log(self): with mock.patch.object(log_object.Log, 'get_object', return_value=None): self.assertRaises( log_exc.LogResourceNotFound, self.log_plugin.delete_log, self.ctxt, mock.ANY) neutron-12.1.1/neutron/tests/unit/services/logapi/__init__.py0000664000175000017500000000000013553660046024330 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/logapi/common/0000775000175000017500000000000013553660157023524 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/services/logapi/common/test_validators.py0000664000175000017500000001551113553660047027306 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import portbindings from neutron_lib import context from neutron_lib.plugins import constants as plugin_const from neutron_lib.plugins import directory from oslo_utils import uuidutils from sqlalchemy.orm import exc as orm_exc from neutron.objects import ports from neutron.objects import securitygroup as sg_object from neutron.services.logapi.common import exceptions as log_exc from neutron.services.logapi.common import validators from neutron.tests import base from neutron.tests.unit.services.logapi.drivers import ( test_manager as drv_mgr) class TestRequestValidations(base.BaseTestCase): """Test validation for a request""" def test_validate_request_resource_sg_not_exists(self): log_data = {'resource_type': 'security_group', 'resource_id': 'fake_sg_id'} with mock.patch.object(sg_object.SecurityGroup, 'count', return_value=0): self.assertRaises(log_exc.ResourceNotFound, validators.validate_request, mock.ANY, log_data) def test_validate_request_target_resource_port_not_exists(self): log_data = {'resource_type': 'security_group', 'target_id': 'fake_port_id'} with mock.patch.object(ports.Port, 'get_object', return_value=None): self.assertRaises(log_exc.TargetResourceNotFound, validators.validate_request, mock.ANY, log_data) def test_validate_request_log_type_not_supported_on_port(self): log_data = {'resource_type': 'security_group', 'target_id': 'fake_port_id'} with mock.patch.object(ports.Port, 'get_object', return_value=mock.ANY): with mock.patch.object(validators, 'validate_log_type_for_port', return_value=False): self.assertRaises(log_exc.LoggingTypeNotSupported, validators.validate_request, mock.ANY, log_data) def test_validate_request_invalid_resource_constraint(self): log_data = {'resource_type': 'security_group', 'resource_id': 'fake_sg_id', 'target_id': 'fake_port_id'} class FakeFiltered(object): def one(self): raise orm_exc.NoResultFound class FakeSGPortBinding(object): def filter_by(self, security_group_id, port_id): return FakeFiltered() with mock.patch.object( sg_object.SecurityGroup, 'count', return_value=1): with mock.patch.object( ports.Port, 'get_object', return_value=mock.ANY): with mock.patch.object(validators, 'validate_log_type_for_port', return_value=True): with mock.patch('neutron.db._utils.model_query', return_value=FakeSGPortBinding()): self.assertRaises( log_exc.InvalidResourceConstraint, validators.validate_request, mock.ANY, log_data) class TestLogDriversLoggingTypeValidations(drv_mgr.TestLogDriversManagerBase): """Test validation of logging type for a port""" def setUp(self): super(TestLogDriversLoggingTypeValidations, self).setUp() self.ctxt = context.Context('fake_user', 'fake_tenant') def _get_port(self, vif_type, vnic_type): port_id = uuidutils.generate_uuid() port_binding = ports.PortBinding( self.ctxt, port_id=port_id, vif_type=vif_type, vnic_type=vnic_type) return ports.Port( self.ctxt, id=uuidutils.generate_uuid(), binding=port_binding) def _test_validate_log_type_for_port(self, port, expected_result): driver_manager = self._create_manager_with_drivers({ 'driver-A': { 'is_loaded': True, 'supported_logging_types': ['security_group'], 'vif_types': [portbindings.VIF_TYPE_OVS], 'vnic_types': [portbindings.VNIC_NORMAL] } }) is_log_type_supported_mock = mock.Mock() if expected_result: is_log_type_supported_mock.return_value = expected_result log_driver = list(driver_manager.drivers)[0] log_driver.is_logging_type_supported = ( is_log_type_supported_mock ) class FakeLoggingPlugin(object): def __init__(self): self.driver_manager = driver_manager directory.add_plugin(plugin_const.LOG_API, FakeLoggingPlugin()) self.assertEqual( expected_result, validators.validate_log_type_for_port('security_group', port)) if expected_result: is_log_type_supported_mock.assert_called_once_with( 'security_group') else: is_log_type_supported_mock.assert_not_called() def test_validate_log_type_for_port_vif_type_supported(self): port = self._get_port( portbindings.VIF_TYPE_OVS, portbindings.VNIC_NORMAL) self._test_validate_log_type_for_port( port, expected_result=True) def test_validate_log_type_for_port_vif_type_not_supported(self): port = self._get_port( portbindings.VIF_TYPE_OTHER, portbindings.VNIC_NORMAL) self._test_validate_log_type_for_port( port, expected_result=False) def test_validate_log_type_for_port_unbound_vnic_type_supported(self): port = self._get_port( portbindings.VIF_TYPE_UNBOUND, portbindings.VNIC_NORMAL) self._test_validate_log_type_for_port( port, expected_result=True) def test_validate_log_type_for_port_unbound_vnic_type_not_supported(self): port = self._get_port( portbindings.VIF_TYPE_UNBOUND, portbindings.VNIC_BAREMETAL) self._test_validate_log_type_for_port( port, expected_result=False) neutron-12.1.1/neutron/tests/unit/services/logapi/common/test_db_api.py0000664000175000017500000002713513553660047026361 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as const from neutron_lib import context from oslo_utils import uuidutils from neutron.common import utils from neutron.objects.logapi import logging_resource as log_object from neutron.services.logapi.common import db_api from neutron.services.logapi.common import validators from neutron.services.logapi.rpc import server as server_rpc from neutron.tests.unit.extensions import test_securitygroup as test_sg def _create_log(tenant_id, resource_id=None, target_id=None, event='ALL', enabled=True,): log_data = { 'id': uuidutils.generate_uuid(), 'name': 'test', 'resource_type': 'security_group', 'project_id': tenant_id, 'event': event, 'enabled': enabled} if resource_id: log_data['resource_id'] = resource_id if target_id: log_data['target_id'] = target_id return log_object.Log(**log_data) class LoggingDBApiTestCase(test_sg.SecurityGroupDBTestCase): def setUp(self): super(LoggingDBApiTestCase, self).setUp() self.context = context.get_admin_context() self.sg_id, self.port_id, self.tenant_id = self._create_sg_and_port() def _create_sg_and_port(self): with self.network() as network, \ self.subnet(network), \ self.security_group() as sg: sg_id = sg['security_group']['id'] tenant_id = sg['security_group']['tenant_id'] res = self._create_port( self.fmt, network['network']['id'], security_groups=[sg_id]) ports_rest = self.deserialize(self.fmt, res) port_id = ports_rest['port']['id'] return sg_id, port_id, tenant_id def test_get_logs_bound_port(self): log = _create_log(target_id=self.port_id, tenant_id=self.tenant_id) with mock.patch.object(log_object.Log, 'get_objects', return_value=[log]): self.assertEqual( [log], db_api.get_logs_bound_port(self.context, self.port_id)) def test_get_logs_not_bound_port(self): fake_sg_id = uuidutils.generate_uuid() log = _create_log(resource_id=fake_sg_id, tenant_id=self.tenant_id) with mock.patch.object(log_object.Log, 'get_objects', return_value=[log]): self.assertEqual( [], db_api.get_logs_bound_port(self.context, self.port_id)) def test_get_logs_bound_sg(self): log = _create_log(resource_id=self.sg_id, tenant_id=self.tenant_id) with mock.patch.object(log_object.Log, 'get_objects', return_value=[log]): self.assertEqual( [log], db_api.get_logs_bound_sg(self.context, self.sg_id)) def test_get_logs_not_bound_sg(self): with self.network() as network, \ self.subnet(network), \ self.security_group() as sg: sg2_id = sg['security_group']['id'] res = self._create_port( self.fmt, network['network']['id'], security_groups=[sg2_id]) port2_id = self.deserialize(self.fmt, res)['port']['id'] log = _create_log(target_id=port2_id, tenant_id=self.tenant_id) with mock.patch.object(log_object.Log, 'get_objects', return_value=[log]): self.assertEqual( [], db_api.get_logs_bound_sg(self.context, self.sg_id)) def test__get_ports_being_logged(self): log1 = _create_log(target_id=self.port_id, tenant_id=self.tenant_id) log2 = _create_log(resource_id=self.sg_id, tenant_id=self.tenant_id) log3 = _create_log(target_id=self.port_id, resource_id=self.tenant_id, tenant_id=self.tenant_id) log4 = _create_log(tenant_id=self.tenant_id) with mock.patch.object( validators, 'validate_log_type_for_port', return_value=True): ports_log1 = db_api._get_ports_being_logged(self.context, log1) ports_log2 = db_api._get_ports_being_logged(self.context, log2) ports_log3 = db_api._get_ports_being_logged(self.context, log3) ports_log4 = db_api._get_ports_being_logged(self.context, log4) self.assertEqual([self.port_id], ports_log1) self.assertEqual([self.port_id], ports_log2) self.assertEqual([self.port_id], ports_log3) self.assertEqual([self.port_id], ports_log4) def test__get_ports_being_logged_not_supported_log_type(self): log = _create_log(tenant_id=self.tenant_id) with mock.patch.object( validators, 'validate_log_type_for_port', return_value=False): ports_log = db_api._get_ports_being_logged(self.context, log) self.assertEqual([], ports_log) class LoggingRpcCallbackTestCase(test_sg.SecurityGroupDBTestCase): def setUp(self): super(LoggingRpcCallbackTestCase, self).setUp() self.context = context.get_admin_context() self.rpc_callback = server_rpc.LoggingApiSkeleton() def test_get_sg_log_info_for_create_or_update_log(self): with self.network() as network, \ self.subnet(network), \ self.security_group() as sg: sg_id = sg['security_group']['id'] tenant_id = sg['security_group']['tenant_id'] rule1 = self._build_security_group_rule( sg_id, 'ingress', const.PROTO_NAME_TCP, '22', '22', ) rule2 = self._build_security_group_rule( sg_id, 'egress', const.PROTO_NAME_TCP, remote_ip_prefix='10.0.0.1', ) rules = { 'security_group_rules': [rule1['security_group_rule'], rule2['security_group_rule']]} self._create_security_group_rule(self.fmt, rules) res = self._create_port( self.fmt, network['network']['id'], security_groups=[sg_id]) ports_rest = self.deserialize(self.fmt, res) port_id = ports_rest['port']['id'] log = _create_log(resource_id=sg_id, tenant_id=tenant_id) with mock.patch.object(validators, 'validate_log_type_for_port', return_value=True): ports_log = ( self.rpc_callback.get_sg_log_info_for_log_resources( self.context, log_resources=[log]) ) expected = [{ 'event': log.event, 'id': log.id, 'ports_log': [{ 'port_id': port_id, 'security_group_rules': [ {'direction': 'egress', 'ethertype': u'IPv4', 'security_group_id': sg_id}, {'direction': 'egress', 'ethertype': u'IPv6', 'security_group_id': sg_id}, {'direction': 'ingress', 'ethertype': u'IPv4', 'port_range_max': 22, 'port_range_min': 22, 'protocol': u'tcp', 'security_group_id': sg_id}, {'direction': 'egress', 'ethertype': u'IPv4', 'protocol': u'tcp', 'dest_ip_prefix': utils.AuthenticIPNetwork('10.0.0.1/32'), 'security_group_id': sg_id}] }], 'project_id': tenant_id }] self.assertEqual(expected, ports_log) self._delete('ports', port_id) def test_get_sg_log_info_for_port_added_event(self): with self.network() as network, \ self.subnet(network), \ self.security_group() as sg: sg_id = sg['security_group']['id'] tenant_id = sg['security_group']['tenant_id'] rule1 = self._build_security_group_rule( sg_id, 'ingress', const.PROTO_NAME_TCP, '11', '13', remote_ip_prefix='10.0.0.1', ) rule2 = self._build_security_group_rule( sg_id, 'egress', const.PROTO_NAME_ICMP, ) rules = { 'security_group_rules': [rule1['security_group_rule'], rule2['security_group_rule']]} self._create_security_group_rule(self.fmt, rules) res = self._create_port( self.fmt, network['network']['id'], security_groups=[sg_id], tenant_id=tenant_id ) ports_rest = self.deserialize(self.fmt, res) port_id = ports_rest['port']['id'] log = _create_log(tenant_id=tenant_id) with mock.patch.object( log_object.Log, 'get_objects', return_value=[log]): with mock.patch.object( validators, 'validate_log_type_for_port', return_value=True): ports_log = ( self.rpc_callback.get_sg_log_info_for_port( self.context, port_id=port_id) ) expected = [{ 'event': log.event, 'id': log.id, 'ports_log': [{ 'port_id': port_id, 'security_group_rules': [ {'direction': 'egress', 'ethertype': u'IPv4', 'security_group_id': sg_id}, {'direction': 'egress', 'ethertype': u'IPv6', 'security_group_id': sg_id}, {'direction': 'ingress', 'ethertype': u'IPv4', 'port_range_max': 13, 'port_range_min': 11, 'protocol': u'tcp', 'source_ip_prefix': utils.AuthenticIPNetwork('10.0.0.1/32'), 'security_group_id': sg_id}, {'direction': 'egress', 'ethertype': u'IPv4', 'protocol': u'icmp', 'security_group_id': sg_id}] }], 'project_id': tenant_id }] self.assertEqual(expected, ports_log) self._delete('ports', port_id) neutron-12.1.1/neutron/tests/unit/services/logapi/common/__init__.py0000664000175000017500000000000013553660046025620 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/test_opts.py0000664000175000017500000000144613553660046021536 0ustar zuulzuul00000000000000# Copyright (c) 2016 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron import opts from neutron.tests import base class OptsTestCase(base.BaseTestCase): def test_list_sriov_agent_opts(self): self.assertEqual('sriov_nic', opts.list_sriov_agent_opts()[0][0]) neutron-12.1.1/neutron/tests/unit/test_policy.py0000664000175000017500000007116213553660047022053 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. """Test of Policy Engine For Neutron""" import mock from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_db import exception as db_exc from oslo_policy import fixture as op_fixture from oslo_policy import policy as oslo_policy from oslo_serialization import jsonutils from oslo_utils import importutils from oslo_utils import uuidutils import neutron from neutron.api.v2 import attributes from neutron.common import constants as n_const from neutron import policy from neutron.tests import base _uuid = uuidutils.generate_uuid class PolicyFileTestCase(base.BaseTestCase): def setUp(self): super(PolicyFileTestCase, self).setUp() self.context = context.Context('fake', 'fake', is_admin=False) self.target = {'tenant_id': 'fake'} def test_modified_policy_reloads(self): tmpfilename = self.get_temp_file_path('policy') action = "example:test" with open(tmpfilename, "w") as policyfile: policyfile.write("""{"example:test": ""}""") policy.refresh(policy_file=tmpfilename) policy.enforce(self.context, action, self.target) with open(tmpfilename, "w") as policyfile: policyfile.write("""{"example:test": "!"}""") policy.refresh(policy_file=tmpfilename) self.target = {'tenant_id': 'fake_tenant'} self.assertRaises(oslo_policy.PolicyNotAuthorized, policy.enforce, self.context, action, self.target) class PolicyTestCase(base.BaseTestCase): def setUp(self): super(PolicyTestCase, self).setUp() # NOTE(vish): preload rules to circumvent reloading from file rules = { "true": '@', "example:allowed": '@', "example:denied": '!', "example:get_http": "http:http://www.example.com", "example:my_file": "role:compute_admin or tenant_id:%(tenant_id)s", "example:early_and_fail": "! and @", "example:early_or_success": "@ or !", "example:lowercase_admin": "role:admin or role:sysadmin", "example:uppercase_admin": "role:ADMIN or role:sysadmin", } policy.refresh() # NOTE(vish): then overload underlying rules policy.set_rules(oslo_policy.Rules.from_dict(rules)) self.context = context.Context('fake', 'fake', roles=['member']) self.target = {} def test_enforce_nonexistent_action_throws(self): action = "example:noexist" self.assertRaises(oslo_policy.PolicyNotAuthorized, policy.enforce, self.context, action, self.target) def test_enforce_bad_action_throws(self): action = "example:denied" self.assertRaises(oslo_policy.PolicyNotAuthorized, policy.enforce, self.context, action, self.target) def test_check_bad_action_noraise(self): action = "example:denied" result = policy.check(self.context, action, self.target) self.assertFalse(result) def test_check_non_existent_action(self): action = "example:idonotexist" result_1 = policy.check(self.context, action, self.target) self.assertFalse(result_1) result_2 = policy.check(self.context, action, self.target, might_not_exist=True) self.assertTrue(result_2) def test_enforce_good_action(self): action = "example:allowed" result = policy.enforce(self.context, action, self.target) self.assertTrue(result) def test_enforce_http_true(self): self.useFixture(op_fixture.HttpCheckFixture()) action = "example:get_http" target = {} result = policy.enforce(self.context, action, target) self.assertTrue(result) def test_enforce_http_false(self): self.useFixture(op_fixture.HttpCheckFixture(False)) action = "example:get_http" target = {} self.assertRaises(oslo_policy.PolicyNotAuthorized, policy.enforce, self.context, action, target) def test_templatized_enforcement(self): target_mine = {'tenant_id': 'fake'} target_not_mine = {'tenant_id': 'another'} action = "example:my_file" policy.enforce(self.context, action, target_mine) self.assertRaises(oslo_policy.PolicyNotAuthorized, policy.enforce, self.context, action, target_not_mine) def test_early_AND_enforcement(self): action = "example:early_and_fail" self.assertRaises(oslo_policy.PolicyNotAuthorized, policy.enforce, self.context, action, self.target) def test_early_OR_enforcement(self): action = "example:early_or_success" policy.enforce(self.context, action, self.target) def test_ignore_case_role_check(self): lowercase_action = "example:lowercase_admin" uppercase_action = "example:uppercase_admin" # NOTE(dprince) we mix case in the Admin role here to ensure # case is ignored admin_context = context.Context('admin', 'fake', roles=['AdMiN']) policy.enforce(admin_context, lowercase_action, self.target) policy.enforce(admin_context, uppercase_action, self.target) class DefaultPolicyTestCase(base.BaseTestCase): def setUp(self): super(DefaultPolicyTestCase, self).setUp() tmpfilename = self.get_temp_file_path('policy.json') self.rules = { "default": '', "example:exist": '!', } with open(tmpfilename, "w") as policyfile: jsonutils.dump(self.rules, policyfile) policy.refresh(policy_file=tmpfilename) self.context = context.Context('fake', 'fake') def test_policy_called(self): self.assertRaises(oslo_policy.PolicyNotAuthorized, policy.enforce, self.context, "example:exist", {}) def test_not_found_policy_calls_default(self): policy.enforce(self.context, "example:noexist", {}) FAKE_RESOURCE_NAME = 'fake_resource' FAKE_SPECIAL_RESOURCE_NAME = 'fake_policy' FAKE_RESOURCES = {"%ss" % FAKE_RESOURCE_NAME: {'attr': {'allow_post': True, 'allow_put': True, 'is_visible': True, 'default': None, 'enforce_policy': True, 'validate': {'type:dict': {'sub_attr_1': {'type:string': None}, 'sub_attr_2': {'type:string': None}}} }, 'list_attr': {'allow_post': True, 'allow_put': True, 'is_visible': True, 'default': None, 'enforce_policy': True }}, # special plural name "%s" % FAKE_SPECIAL_RESOURCE_NAME.replace('y', 'ies'): {'attr': {'allow_post': True, 'allow_put': True, 'is_visible': True, 'default': None, 'enforce_policy': True, 'validate': {'type:dict': {'sub_attr_1': {'type:string': None}, 'sub_attr_2': {'type:string': None}}} }}} class NeutronPolicyTestCase(base.BaseTestCase): def fakepolicyinit(self, **kwargs): policy._ENFORCER = oslo_policy.Enforcer(cfg.CONF) policy._ENFORCER.set_rules(oslo_policy.Rules(self.rules)) def setUp(self): super(NeutronPolicyTestCase, self).setUp() # Add Fake resources to RESOURCE_ATTRIBUTE_MAP attributes.RESOURCE_ATTRIBUTE_MAP.update(FAKE_RESOURCES) self._set_rules() self.patcher = mock.patch.object(neutron.policy, 'init', new=self.fakepolicyinit) self.patcher.start() policy.refresh() self.addCleanup(policy.refresh) self.context = context.Context('fake', 'fake', roles=['user']) plugin_klass = importutils.import_class( "neutron.db.db_base_plugin_v2.NeutronDbPluginV2") directory.add_plugin(plugin_constants.CORE, plugin_klass()) def _set_rules(self, **kwargs): rules_dict = { "context_is_admin": "role:admin", "context_is_advsvc": "role:advsvc", "admin_or_network_owner": "rule:context_is_admin or " "tenant_id:%(network:tenant_id)s", "admin_or_owner": ("rule:context_is_admin or " "tenant_id:%(tenant_id)s"), "admin_only": "rule:context_is_admin", "regular_user": "role:user", "shared": "field:networks:shared=True", "external": "field:networks:router:external=True", "network_device": "field:port:device_owner=~^network:", "default": '@', "create_network": "rule:admin_or_owner", "create_network:shared": "rule:admin_only", "update_network": '@', "update_network:shared": "rule:admin_only", "get_network": "rule:admin_or_owner or rule:shared or " "rule:external or rule:context_is_advsvc", "create_subnet": "rule:admin_or_network_owner", "create_port:mac": "rule:admin_or_network_owner or " "rule:context_is_advsvc", "create_port:device_owner": "not rule:network_device", "create_port:fixed_ips": ( "rule:context_is_advsvc or rule:admin_or_network_owner or " "rule:shared"), "create_port:fixed_ips:ip_address": ( "rule:context_is_advsvc or rule:admin_or_network_owner"), "create_port:fixed_ips:subnet_id": ( "rule:context_is_advsvc or rule:admin_or_network_owner or " "rule:shared"), "update_port": "rule:admin_or_owner or rule:context_is_advsvc", "get_port": "rule:admin_or_owner or rule:context_is_advsvc", "delete_port": "rule:admin_or_owner or rule:context_is_advsvc", "create_fake_resource": "rule:admin_or_owner", "create_fake_resource:attr": "rule:admin_or_owner", "create_fake_resource:attr:sub_attr_1": "rule:admin_or_owner", "create_fake_resource:attr:sub_attr_2": "rule:admin_only", "create_fake_resource:list_attr": "rule:admin_only_or_owner", "create_fake_resource:list_attr:admin_element": "rule:admin_only", "create_fake_resource:list_attr:user_element": ( "rule:admin_or_owner"), "create_fake_policy:": "rule:admin_or_owner", } rules_dict.update(**kwargs) self.rules = oslo_policy.Rules.from_dict(rules_dict) def _test_action_on_attr(self, context, action, obj, attr, value, exception=None, **kwargs): action = "%s_%s" % (action, obj) target = {'tenant_id': 'the_owner', attr: value} if kwargs: target.update(kwargs) if exception: self.assertRaises(exception, policy.enforce, context, action, target) else: result = policy.enforce(context, action, target) self.assertTrue(result) def _test_nonadmin_action_on_attr(self, action, obj, attr, value, exception=None, **kwargs): user_context = context.Context('', "user", roles=['user']) self._test_action_on_attr(user_context, action, obj, attr, value, exception, **kwargs) def _test_advsvc_action_on_attr(self, action, obj, attr, value, exception=None, **kwargs): user_context = context.Context('', "user", roles=['user', 'advsvc']) self._test_action_on_attr(user_context, action, obj, attr, value, exception, **kwargs) def test_nonadmin_write_on_private_fails(self): self._test_nonadmin_action_on_attr( 'create', 'network', 'shared', False, oslo_policy.PolicyNotAuthorized) def test_nonadmin_read_on_private_fails(self): self._test_nonadmin_action_on_attr('get', 'network', 'shared', False, oslo_policy.PolicyNotAuthorized) def test_nonadmin_write_on_shared_fails(self): self._test_nonadmin_action_on_attr('create', 'network', 'shared', True, oslo_policy.PolicyNotAuthorized) def test_create_port_device_owner_regex(self): blocked_values = (constants.DEVICE_OWNER_NETWORK_PREFIX, 'network:abdef', constants.DEVICE_OWNER_DHCP, constants.DEVICE_OWNER_ROUTER_INTF) for val in blocked_values: self._test_advsvc_action_on_attr( 'create', 'port', 'device_owner', val, oslo_policy.PolicyNotAuthorized ) ok_values = ('network', 'networks', 'my_network:test', 'my_network:') for val in ok_values: self._test_advsvc_action_on_attr( 'create', 'port', 'device_owner', val ) def test_create_port_fixed_ips_on_shared_network(self): def fakegetnetwork(*args, **kwargs): return {'tenant_id': 'fake', 'shared': True} kwargs = {'network_id': _uuid()} with mock.patch.object(directory.get_plugin(), 'get_network', new=fakegetnetwork): self._test_nonadmin_action_on_attr( 'create', 'port', 'fixed_ips', [{'subnet_id': 'test-subnet-id'}], **kwargs) self._test_nonadmin_action_on_attr( 'create', 'port', 'fixed_ips', [{'ip_address': '1.2.3.4'}], exception=oslo_policy.PolicyNotAuthorized, **kwargs) def test_create_port_fixed_ips_on_nonshared_network(self): def fakegetnetwork(*args, **kwargs): return {'tenant_id': 'fake', 'shared': False} kwargs = {'network_id': _uuid()} with mock.patch.object(directory.get_plugin(), 'get_network', new=fakegetnetwork): self._test_nonadmin_action_on_attr( 'create', 'port', 'fixed_ips', [{'subnet_id': 'test-subnet-id'}], exception=oslo_policy.PolicyNotAuthorized, **kwargs) self._test_nonadmin_action_on_attr( 'create', 'port', 'fixed_ips', [{'ip_address': '1.2.3.4'}], exception=oslo_policy.PolicyNotAuthorized, **kwargs) def test_advsvc_get_network_works(self): self._test_advsvc_action_on_attr('get', 'network', 'shared', False) def test_advsvc_create_network_fails(self): self._test_advsvc_action_on_attr('create', 'network', 'shared', False, oslo_policy.PolicyNotAuthorized) def test_advsvc_create_port_works(self): self._test_advsvc_action_on_attr('create', 'port:mac', 'shared', False) def test_advsvc_get_port_works(self): self._test_advsvc_action_on_attr('get', 'port', 'shared', False) def test_advsvc_update_port_works(self): kwargs = {n_const.ATTRIBUTES_TO_UPDATE: ['shared']} self._test_advsvc_action_on_attr('update', 'port', 'shared', True, **kwargs) def test_advsvc_delete_port_works(self): self._test_advsvc_action_on_attr('delete', 'port', 'shared', False) def test_advsvc_create_subnet_fails(self): self._test_advsvc_action_on_attr('create', 'subnet', 'shared', False, oslo_policy.PolicyNotAuthorized) def test_nonadmin_read_on_shared_succeeds(self): self._test_nonadmin_action_on_attr('get', 'network', 'shared', True) def _test_enforce_adminonly_attribute(self, action, **kwargs): admin_context = context.get_admin_context() target = {'shared': True} if kwargs: target.update(kwargs) result = policy.enforce(admin_context, action, target) self.assertTrue(result) def test_enforce_adminonly_attribute_create(self): self._test_enforce_adminonly_attribute('create_network') def test_enforce_adminonly_attribute_update(self): kwargs = {n_const.ATTRIBUTES_TO_UPDATE: ['shared']} self._test_enforce_adminonly_attribute('update_network', **kwargs) def test_reset_adminonly_attr_to_default_fails(self): kwargs = {n_const.ATTRIBUTES_TO_UPDATE: ['shared']} self._test_nonadmin_action_on_attr( 'update', 'network', 'shared', False, oslo_policy.PolicyNotAuthorized, **kwargs) def test_enforce_adminonly_attribute_nonadminctx_returns_403(self): action = "create_network" target = {'shared': True, 'tenant_id': 'somebody_else'} self.assertRaises(oslo_policy.PolicyNotAuthorized, policy.enforce, self.context, action, target) def _test_build_subattribute_match_rule(self, validate_value): bk = FAKE_RESOURCES['%ss' % FAKE_RESOURCE_NAME]['attr']['validate'] FAKE_RESOURCES['%ss' % FAKE_RESOURCE_NAME]['attr']['validate'] = ( validate_value) action = "create_" + FAKE_RESOURCE_NAME target = {'tenant_id': 'fake', 'attr': {'sub_attr_1': 'x'}} self.assertFalse(policy._build_subattr_match_rule( 'attr', FAKE_RESOURCES['%ss' % FAKE_RESOURCE_NAME]['attr'], action, target)) FAKE_RESOURCES['%ss' % FAKE_RESOURCE_NAME]['attr']['validate'] = bk def test_build_subattribute_match_rule_empty_dict_validator(self): self._test_build_subattribute_match_rule({}) def test_build_subattribute_match_rule_wrong_validation_info(self): self._test_build_subattribute_match_rule( {'type:dict': 'wrong_stuff'}) def test_build_match_rule_special_pluralized(self): action = "create_" + FAKE_SPECIAL_RESOURCE_NAME pluralized = "create_fake_policies" target = {} result = policy._build_match_rule(action, target, pluralized) self.assertEqual("rule:" + action, str(result)) def test_build_match_rule_normal_pluralized_when_create(self): action = "create_" + FAKE_RESOURCE_NAME target = {} result = policy._build_match_rule(action, target, None) self.assertEqual("rule:" + action, str(result)) def test_build_match_rule_normal_pluralized_when_update(self): action = "update_" + FAKE_RESOURCE_NAME target = {} result = policy._build_match_rule(action, target, None) self.assertEqual("rule:" + action, str(result)) def test_enforce_subattribute(self): action = "create_" + FAKE_RESOURCE_NAME target = {'tenant_id': 'fake', 'attr': {'sub_attr_1': 'x'}} result = policy.enforce(self.context, action, target, None) self.assertTrue(result) def test_enforce_admin_only_subattribute(self): action = "create_" + FAKE_RESOURCE_NAME target = {'tenant_id': 'fake', 'attr': {'sub_attr_1': 'x', 'sub_attr_2': 'y'}} result = policy.enforce(context.get_admin_context(), action, target, None) self.assertTrue(result) def test_enforce_admin_only_subattribute_nonadminctx_returns_403(self): action = "create_" + FAKE_RESOURCE_NAME target = {'tenant_id': 'fake', 'attr': {'sub_attr_1': 'x', 'sub_attr_2': 'y'}} self.assertRaises(oslo_policy.PolicyNotAuthorized, policy.enforce, self.context, action, target, None) def test_enforce_regularuser_on_read(self): action = "get_network" target = {'shared': True, 'tenant_id': 'somebody_else'} result = policy.enforce(self.context, action, target) self.assertTrue(result) def test_enforce_tenant_id_check(self): # Trigger a policy with rule admin_or_owner action = "create_network" target = {'tenant_id': 'fake'} result = policy.enforce(self.context, action, target) self.assertTrue(result) def test_enforce_tenant_id_check_parent_resource(self): def fakegetnetwork(*args, **kwargs): return {'tenant_id': 'fake'} action = "create_port:mac" with mock.patch.object(directory.get_plugin(), 'get_network', new=fakegetnetwork): target = {'network_id': 'whatever'} result = policy.enforce(self.context, action, target) self.assertTrue(result) def test_enforce_plugin_failure(self): def fakegetnetwork(*args, **kwargs): raise NotImplementedError('Blast!') # the policy check and plugin method we use in this test are irrelevant # so long that we verify that, if *f* blows up, the behavior of the # policy engine to propagate the exception is preserved action = "create_port:mac" with mock.patch.object(directory.get_plugin(), 'get_network', new=fakegetnetwork): target = {'network_id': 'whatever'} self.assertRaises(NotImplementedError, policy.enforce, self.context, action, target) def test_enforce_subattribute_as_list(self): action = "create_" + FAKE_RESOURCE_NAME target = { 'tenant_id': 'fake', 'list_attr': [{'user_element': 'x'}]} result = policy.enforce(self.context, action, target, None) self.assertTrue(result) def test_enforce_subattribute_as_list_forbiden(self): action = "create_" + FAKE_RESOURCE_NAME target = { 'tenant_id': 'fake', 'list_attr': [{'admin_element': 'x'}]} self.assertRaises(oslo_policy.PolicyNotAuthorized, policy.enforce, self.context, action, target, None) def test_retryrequest_on_notfound(self): failure = exceptions.NetworkNotFound(net_id='whatever') action = "create_port:mac" with mock.patch.object(directory.get_plugin(), 'get_network', side_effect=failure): target = {'network_id': 'whatever'} try: policy.enforce(self.context, action, target) self.fail("Did not raise RetryRequest") except db_exc.RetryRequest as e: self.assertEqual(failure, e.inner_exc) def test_enforce_tenant_id_check_parent_resource_bw_compatibility(self): def fakegetnetwork(*args, **kwargs): return {'tenant_id': 'fake'} self._set_rules( admin_or_network_owner="role:admin or " "tenant_id:%(network_tenant_id)s") action = "create_port:mac" with mock.patch.object(directory.get_plugin(), 'get_network', new=fakegetnetwork): target = {'network_id': 'whatever'} result = policy.enforce(self.context, action, target) self.assertTrue(result) def test_tenant_id_check_no_target_field_raises(self): # Try and add a bad rule self.assertRaises( exceptions.PolicyInitError, oslo_policy.Rules.from_dict, {'test_policy': 'tenant_id:(wrong_stuff)'}) def test_tenant_id_check_caches_extracted_fields(self): plugin = directory.get_plugin() with mock.patch.object(plugin, 'get_network', return_value={'tenant_id': 'fake'}) as getter: action = "create_port:mac" for i in range(2): target = {'network_id': 'whatever'} policy.enforce(self.context, action, target) self.assertEqual(1, getter.call_count) def _test_enforce_tenant_id_raises(self, bad_rule): self._set_rules(admin_or_owner=bad_rule) # Trigger a policy with rule admin_or_owner action = "create_network" target = {'tenant_id': 'fake'} self.fakepolicyinit() self.assertRaises(exceptions.PolicyCheckError, policy.enforce, self.context, action, target) def test_enforce_tenant_id_check_malformed_target_field_raises(self): self._test_enforce_tenant_id_raises('tenant_id:%(malformed_field)s') def test_enforce_tenant_id_check_invalid_parent_resource_raises(self): self._test_enforce_tenant_id_raises('tenant_id:%(foobaz_tenant_id)s') def test_process_rules(self): action = "create_" + FAKE_RESOURCE_NAME # Construct RuleChecks for an action, attribute and subattribute match_rule = oslo_policy.RuleCheck('rule', action) attr_rule = oslo_policy.RuleCheck( 'rule', '%s:%ss' % (action, FAKE_RESOURCE_NAME)) sub_attr_rules = [oslo_policy.RuleCheck( 'rule', '%s:%s:%s' % (action, 'attr', 'sub_attr_1'))] # Build an AndCheck from the given RuleChecks # Make the checks nested to better check the recursion sub_attr_rules = oslo_policy.AndCheck(sub_attr_rules) attr_rule = oslo_policy.AndCheck( [attr_rule, sub_attr_rules]) match_rule = oslo_policy.AndCheck([match_rule, attr_rule]) # Assert that the rules are correctly extracted from the match_rule rules = policy._process_rules_list([], match_rule) self.assertEqual(['create_fake_resource', 'create_fake_resource:fake_resources', 'create_fake_resource:attr:sub_attr_1'], rules) @mock.patch.object(policy.LOG, 'isEnabledFor', return_value=True) @mock.patch.object(policy.LOG, 'debug') def test_log_rule_list(self, mock_debug, mock_is_e): policy.log_rule_list(oslo_policy.RuleCheck('rule', 'create_')) self.assertTrue(mock_is_e.called) self.assertTrue(mock_debug.called) def test__is_attribute_explicitly_set(self): action = 'create' attr = 'attr' target = {attr: 'valueA', 'tgt-tenant': 'tenantA'} resource = {attr: {'allow_post': True, 'allow_put': True, 'is_visible': True, 'enforce_policy': True, 'validate': {'type:string': 10}}} result = policy._is_attribute_explicitly_set( attr, resource, target, action) self.assertTrue(result) target = {'tgt-tenant': 'tenantA'} result = policy._is_attribute_explicitly_set( attr, resource, target, action) self.assertFalse(result) resource = {attr: {'allow_post': True, 'allow_put': True, 'is_visible': True, 'default': 'DfltValue', 'enforce_policy': True, 'validate': {'type:string': 10}}} result = policy._is_attribute_explicitly_set( attr, resource, target, action) self.assertFalse(result) target = {attr: 'DfltValue', 'tgt-tenant': 'tenantA'} result = policy._is_attribute_explicitly_set( attr, resource, target, action) self.assertFalse(result) target = {attr: constants.ATTR_NOT_SPECIFIED, 'tgt-tenant': 'tenantA'} result = policy._is_attribute_explicitly_set( attr, resource, target, action) self.assertFalse(result) neutron-12.1.1/neutron/tests/unit/test_auth.py0000664000175000017500000001163113553660047021510 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_middleware import request_id import webob from neutron import auth from neutron.tests import base class NeutronKeystoneContextTestCase(base.BaseTestCase): def setUp(self): super(NeutronKeystoneContextTestCase, self).setUp() @webob.dec.wsgify def fake_app(req): self.context = req.environ['neutron.context'] return webob.Response() self.context = None self.middleware = auth.NeutronKeystoneContext(fake_app) self.request = webob.Request.blank('/') self.request.headers['X_AUTH_TOKEN'] = 'testauthtoken' def test_no_user_id(self): self.request.headers['X_PROJECT_ID'] = 'testtenantid' response = self.request.get_response(self.middleware) self.assertEqual('401 Unauthorized', response.status) def test_with_user_id(self): self.request.headers['X_PROJECT_ID'] = 'testtenantid' self.request.headers['X_USER_ID'] = 'testuserid' response = self.request.get_response(self.middleware) self.assertEqual('200 OK', response.status) self.assertEqual('testuserid', self.context.user_id) self.assertEqual('testuserid', self.context.user) def test_with_tenant_id(self): self.request.headers['X_PROJECT_ID'] = 'testtenantid' self.request.headers['X_USER_ID'] = 'test_user_id' response = self.request.get_response(self.middleware) self.assertEqual('200 OK', response.status) self.assertEqual('testtenantid', self.context.tenant_id) self.assertEqual('testtenantid', self.context.tenant) def test_roles_no_admin(self): self.request.headers['X_PROJECT_ID'] = 'testtenantid' self.request.headers['X_USER_ID'] = 'testuserid' self.request.headers['X_ROLES'] = 'role1, role2 , role3,role4,role5' response = self.request.get_response(self.middleware) self.assertEqual('200 OK', response.status) self.assertEqual(['role1', 'role2', 'role3', 'role4', 'role5'], self.context.roles) self.assertFalse(self.context.is_admin) def test_roles_with_admin(self): self.request.headers['X_PROJECT_ID'] = 'testtenantid' self.request.headers['X_USER_ID'] = 'testuserid' self.request.headers['X_ROLES'] = ('role1, role2 , role3,role4,role5,' 'AdMiN') response = self.request.get_response(self.middleware) self.assertEqual('200 OK', response.status) self.assertEqual(['role1', 'role2', 'role3', 'role4', 'role5', 'AdMiN'], self.context.roles) self.assertTrue(self.context.is_admin) def test_with_user_tenant_name(self): self.request.headers['X_PROJECT_ID'] = 'testtenantid' self.request.headers['X_USER_ID'] = 'testuserid' self.request.headers['X_PROJECT_NAME'] = 'testtenantname' self.request.headers['X_USER_NAME'] = 'testusername' response = self.request.get_response(self.middleware) self.assertEqual('200 OK', response.status) self.assertEqual('testuserid', self.context.user_id) self.assertEqual('testusername', self.context.user_name) self.assertEqual('testtenantid', self.context.tenant_id) self.assertEqual('testtenantname', self.context.project_name) def test_request_id_extracted_from_env(self): req_id = 'dummy-request-id' self.request.headers['X_PROJECT_ID'] = 'testtenantid' self.request.headers['X_USER_ID'] = 'testuserid' self.request.environ[request_id.ENV_REQUEST_ID] = req_id self.request.get_response(self.middleware) self.assertEqual(req_id, self.context.request_id) def test_with_auth_token(self): self.request.headers['X_PROJECT_ID'] = 'testtenantid' self.request.headers['X_USER_ID'] = 'testuserid' response = self.request.get_response(self.middleware) self.assertEqual('200 OK', response.status) self.assertEqual('testauthtoken', self.context.auth_token) def test_without_auth_token(self): self.request.headers['X_PROJECT_ID'] = 'testtenantid' self.request.headers['X_USER_ID'] = 'testuserid' del self.request.headers['X_AUTH_TOKEN'] self.request.get_response(self.middleware) self.assertIsNone(self.context.auth_token) neutron-12.1.1/neutron/tests/unit/test_manager.py0000664000175000017500000002073613553660047022167 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import weakref import fixtures from neutron_lib.plugins import constants as lib_const from neutron_lib.plugins import directory from oslo_config import cfg from neutron import manager from neutron.plugins.common import constants from neutron.tests import base from neutron.tests.unit import dummy_plugin from neutron.tests.unit import testlib_api DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' class MultiServiceCorePlugin(object): supported_extension_aliases = ['lbaas', dummy_plugin.Dummy.get_alias()] class CorePluginWithAgentNotifiers(object): agent_notifiers = {'l3': 'l3_agent_notifier', 'dhcp': 'dhcp_agent_notifier'} class NeutronManagerTestCase(base.BaseTestCase): def setUp(self): ext_mapping = constants.EXT_TO_SERVICE_MAPPING if dummy_plugin.Dummy.get_alias() not in ext_mapping: ext_mapping[dummy_plugin.Dummy.get_alias()] = ( dummy_plugin.DUMMY_SERVICE_TYPE) super(NeutronManagerTestCase, self).setUp() self.config_parse() self.setup_coreplugin(load_plugins=False) self.useFixture( fixtures.MonkeyPatch('neutron.manager.NeutronManager._instance')) def tearDown(self): ext_mapping = constants.EXT_TO_SERVICE_MAPPING if dummy_plugin.Dummy.get_alias() in ext_mapping: del ext_mapping[dummy_plugin.Dummy.get_alias()] super(NeutronManagerTestCase, self).tearDown() def test_service_plugin_is_loaded(self): cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) cfg.CONF.set_override("service_plugins", ["neutron.tests.unit.dummy_plugin." "DummyServicePlugin"]) manager.init() plugin = directory.get_plugin(dummy_plugin.DUMMY_SERVICE_TYPE) self.assertIsInstance( plugin, dummy_plugin.DummyServicePlugin, "loaded plugin should be of type neutronDummyPlugin") def test_service_plugin_by_name_is_loaded(self): cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) cfg.CONF.set_override("service_plugins", [dummy_plugin.Dummy.get_alias()]) manager.init() plugin = directory.get_plugin(dummy_plugin.DUMMY_SERVICE_TYPE) self.assertIsInstance( plugin, dummy_plugin.DummyServicePlugin, "loaded plugin should be of type neutronDummyPlugin") def test_multiple_plugins_specified_for_service_type(self): cfg.CONF.set_override("service_plugins", ["neutron.tests.unit.dummy_plugin." "DummyServicePlugin", "neutron.tests.unit.dummy_plugin." "DummyServicePlugin"]) cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) e = self.assertRaises(ValueError, manager.NeutronManager.get_instance) self.assertIn(dummy_plugin.DUMMY_SERVICE_TYPE, str(e)) def test_multiple_plugins_by_name_specified_for_service_type(self): cfg.CONF.set_override("service_plugins", [dummy_plugin.Dummy.get_alias(), dummy_plugin.Dummy.get_alias()]) cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) self.assertRaises(ValueError, manager.NeutronManager.get_instance) def test_multiple_plugins_mixed_specified_for_service_type(self): cfg.CONF.set_override("service_plugins", ["neutron.tests.unit.dummy_plugin." "DummyServicePlugin", dummy_plugin.Dummy.get_alias()]) cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) self.assertRaises(ValueError, manager.NeutronManager.get_instance) def test_service_plugin_conflicts_with_core_plugin(self): cfg.CONF.set_override("service_plugins", ["neutron.tests.unit.dummy_plugin." "DummyServicePlugin"]) cfg.CONF.set_override("core_plugin", "neutron.tests.unit.test_manager." "MultiServiceCorePlugin") e = self.assertRaises(ValueError, manager.NeutronManager.get_instance) self.assertIn(dummy_plugin.DUMMY_SERVICE_TYPE, str(e)) def test_core_plugin_supports_services(self): cfg.CONF.set_override("core_plugin", "neutron.tests.unit.test_manager." "MultiServiceCorePlugin") manager.init() svc_plugins = directory.get_plugins() self.assertEqual(3, len(svc_plugins)) self.assertIn(lib_const.CORE, svc_plugins.keys()) self.assertIn(lib_const.LOADBALANCER, svc_plugins.keys()) self.assertIn(dummy_plugin.DUMMY_SERVICE_TYPE, svc_plugins.keys()) def test_load_default_service_plugins(self): self.patched_default_svc_plugins.return_value = { 'neutron.tests.unit.dummy_plugin.DummyServicePlugin': dummy_plugin.DUMMY_SERVICE_TYPE } cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) manager.init() svc_plugins = directory.get_plugins() self.assertIn(dummy_plugin.DUMMY_SERVICE_TYPE, svc_plugins) def test_post_plugin_validation(self): cfg.CONF.import_opt('dhcp_agents_per_network', 'neutron.db.agentschedulers_db') self.assertIsNone(manager.validate_post_plugin_load()) cfg.CONF.set_override('dhcp_agents_per_network', 2) self.assertIsNone(manager.validate_post_plugin_load()) cfg.CONF.set_override('dhcp_agents_per_network', 0) self.assertIsNotNone(manager.validate_post_plugin_load()) cfg.CONF.set_override('dhcp_agents_per_network', -1) self.assertIsNotNone(manager.validate_post_plugin_load()) def test_pre_plugin_validation(self): self.assertIsNotNone(manager.validate_pre_plugin_load()) cfg.CONF.set_override('core_plugin', 'dummy.plugin') self.assertIsNone(manager.validate_pre_plugin_load()) def test_manager_gathers_agent_notifiers_from_service_plugins(self): cfg.CONF.set_override("service_plugins", ["neutron.tests.unit.dummy_plugin." "DummyServicePlugin"]) cfg.CONF.set_override("core_plugin", "neutron.tests.unit.test_manager." "CorePluginWithAgentNotifiers") expected = {'l3': 'l3_agent_notifier', 'dhcp': 'dhcp_agent_notifier', dummy_plugin.Dummy.get_alias(): 'dummy_agent_notifier'} manager.init() core_plugin = directory.get_plugin() self.assertEqual(expected, core_plugin.agent_notifiers) def test_load_class_for_provider(self): manager.NeutronManager.load_class_for_provider( 'neutron.core_plugins', 'ml2') def test_load_class_for_provider_wrong_plugin(self): with testlib_api.ExpectedException(ImportError): manager.NeutronManager.load_class_for_provider( 'neutron.core_plugins', 'ml2XXXXXX') def test_get_service_plugin_by_path_prefix_3(self): cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) nm = manager.NeutronManager.get_instance() class pclass(object): def __init__(self, path_prefix): self.path_prefix = path_prefix x_plugin, y_plugin = pclass('xpa'), pclass('ypa') directory.add_plugin('x', x_plugin) directory.add_plugin('y', y_plugin) self.assertEqual(weakref.proxy(x_plugin), nm.get_service_plugin_by_path_prefix('xpa')) self.assertEqual(weakref.proxy(y_plugin), nm.get_service_plugin_by_path_prefix('ypa')) self.assertIsNone(nm.get_service_plugin_by_path_prefix('abc')) neutron-12.1.1/neutron/tests/unit/test_neutron_plugin_base_v2.py0000664000175000017500000000173613553660046025224 0ustar zuulzuul00000000000000# Copyright (c) 2017 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron import manager from neutron.tests import base class NeutronPluginBaseV2TestCase(base.BaseTestCase): def test_can_load_core_plugin_without_datastore(self): cfg.CONF.set_override("core_plugin", 'neutron.tests.unit.dummy_plugin.' 'DummyCorePluginWithoutDatastore') manager.init() neutron-12.1.1/neutron/tests/unit/objects/0000775000175000017500000000000013553660157020567 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/test_address_scope.py0000664000175000017500000000206013553660046025011 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import address_scope from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class AddressScopeIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = address_scope.AddressScope class AddressScopeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = address_scope.AddressScope neutron-12.1.1/neutron/tests/unit/objects/test_quota.py0000664000175000017500000001055213553660047023332 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime from oslo_utils import uuidutils from neutron.objects import quota from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class ResourceDeltaObjectIfaceTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = quota.ResourceDelta class ResourceDeltaDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = quota.ResourceDelta def setUp(self): super(ResourceDeltaDbObjectTestCase, self).setUp() for obj in self.obj_fields: self._create_test_reservation(res_id=obj['reservation_id']) def _create_test_reservation(self, res_id): self._reservation = quota.Reservation(self.context, id=res_id) self._reservation.create() class ReservationObjectIfaceTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = quota.Reservation class ReservationDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = quota.Reservation def _create_test_reservation(self, res=None, exp=None): res_id = uuidutils.generate_uuid() reservation = self._test_class(self.context, id=res_id, resource=res, expiration=exp) reservation.create() return reservation def test_delete_expired(self): dt = datetime.datetime.utcnow() resources = {'goals': 2, 'assists': 1} exp_date1 = datetime.datetime(2016, 3, 31, 14, 30) res1 = self._create_test_reservation(resources, exp_date1) exp_date2 = datetime.datetime(2015, 3, 31, 14, 30) res2 = self._create_test_reservation(resources, exp_date2) self.assertEqual(2, self._test_class.delete_expired( self.context, dt, None)) objs = self._test_class.get_objects(self.context, id=[res1.id, res2.id]) self.assertEqual([], objs) def test_reservation_synthetic_field(self): res = self._create_test_reservation() resource = 'test-res' res_delta = quota.ResourceDelta(self.context, resource=resource, reservation_id=res.id, amount='10') res_delta.create() obj = self._test_class.get_object(self.context, id=res.id) self.assertEqual(res_delta, obj.resource_deltas[0]) res_delta.delete() obj.update() # NOTE(manjeets) update on reservation should reflect # changes on synthetic field when it is deleted. obj = self._test_class.get_object(self.context, id=res.id) self.assertEqual([], obj.resource_deltas) class QuotaObjectIfaceTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = quota.Quota class QuotaDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = quota.Quota class QuotaUsageObjectIfaceTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = quota.QuotaUsage class QuotaUsageDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = quota.QuotaUsage def _test_get_object_dirty_protected(self, obj, dirty=True): obj.create() obj.dirty = dirty obj.update() new = self._test_class.get_object_dirty_protected( self.context, **obj._get_composite_keys()) self.assertEqual(obj, new) self.assertEqual(dirty, new.dirty) def test_get_object_dirty_protected(self): obj = self._make_object(self.obj_fields[0]) obj1 = self._make_object(self.obj_fields[1]) self._test_get_object_dirty_protected(obj, dirty=False) self._test_get_object_dirty_protected(obj1) neutron-12.1.1/neutron/tests/unit/objects/test_servicetype.py0000664000175000017500000000215013553660047024536 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import servicetype from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class ProviderResourceAssociationIfaceObjectTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = servicetype.ProviderResourceAssociation class ProviderResourceAssociationDbObjectTestCase( obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = servicetype.ProviderResourceAssociation neutron-12.1.1/neutron/tests/unit/objects/test_agent.py0000664000175000017500000000466113553660047023303 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import agent from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class AgentIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = agent.Agent class AgentDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = agent.Agent def test_configurations(self): obj = self.objs[0] obj.create() obj.configurations = {} obj.update() db_fields = obj.modify_fields_to_db(obj) self.assertEqual('', db_fields['configurations']) obj = agent.Agent.get_object(self.context, id=obj.id) self.assertEqual({}, obj.configurations) conf = {"tunnel_types": ["vxlan"], "tunneling_ip": "20.0.0.1", "bridge_mappings": {"phys_net1": "br-eth-1"}} obj.configurations = conf obj.update() obj = agent.Agent.get_object(self.context, id=obj.id) self.assertEqual(conf, obj.configurations) def test_resource_versions(self): obj = self.objs[0] versions = {'obj1': 'ver1', 'obj2': 1.1} obj.resource_versions = versions obj.create() obj = agent.Agent.get_object(self.context, id=obj.id) self.assertEqual(versions, obj.resource_versions) obj.resource_versions = {} obj.update() db_fields = obj.modify_fields_to_db(obj) self.assertIsNone(db_fields['resource_versions']) obj = agent.Agent.get_object(self.context, id=obj.id) self.assertIsNone(obj.resource_versions) obj.resource_versions = None obj.update() self.assertIsNone(obj.resource_versions) db_fields = obj.modify_fields_to_db(obj) self.assertIsNone(db_fields['resource_versions']) neutron-12.1.1/neutron/tests/unit/objects/test_ports.py0000664000175000017500000003133613553660047023353 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_utils import uuidutils import testscenarios from neutron.objects import base as obj_base from neutron.objects import network from neutron.objects import ports from neutron.objects.qos import binding from neutron.objects.qos import policy from neutron.tests import tools from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class SecurityGroupPortBindingIfaceObjTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = ports.SecurityGroupPortBinding class SecurityGroupPortBindingDbObjectTestCase( obj_test_base.BaseDbObjectTestCase): _test_class = ports.SecurityGroupPortBinding class BasePortBindingDbObjectTestCase(obj_test_base._BaseObjectTestCase, testlib_api.SqlTestCase): def setUp(self): super(BasePortBindingDbObjectTestCase, self).setUp() self.update_obj_fields( {'port_id': lambda: self._create_test_port_id()}) class PortBindingIfaceObjTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = ports.PortBinding class PortBindingDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, BasePortBindingDbObjectTestCase): _test_class = ports.PortBinding class DistributedPortBindingIfaceObjTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = ports.DistributedPortBinding class DistributedPortBindingDbObjectTestCase( obj_test_base.BaseDbObjectTestCase, BasePortBindingDbObjectTestCase): _test_class = ports.DistributedPortBinding # TODO(ihrachys): this test case copies some functions from the base module. # This is because we currently cannot inherit from the base class that contains # those functions, because that same class provides test cases that we don't # want to execute. Ideally, we would need to copy paste, but that would require # some significant refactoring in the base test classes. Leaving it for a # follow up. class PortBindingVifDetailsTestCase(testscenarios.WithScenarios, obj_test_base._BaseObjectTestCase, testlib_api.SqlTestCase): scenarios = [ (cls.__name__, {'_test_class': cls}) for cls in (ports.PortBinding, ports.DistributedPortBinding) ] def setUp(self): super(PortBindingVifDetailsTestCase, self).setUp() self._create_test_network() getter = lambda: self._create_port(network_id=self._network['id']).id self.update_obj_fields({'port_id': getter}) def _create_port(self, **port_attrs): attrs = {'project_id': uuidutils.generate_uuid(), 'admin_state_up': True, 'status': 'ACTIVE', 'device_id': 'fake_device', 'device_owner': 'fake_owner', 'mac_address': tools.get_random_EUI()} attrs.update(port_attrs) port = ports.Port(self.context, **attrs) port.create() return port def _create_test_network(self): self._network = network.Network(self.context, name='test-network1') self._network.create() def _make_object(self, fields): fields = obj_test_base.get_non_synthetic_fields( self._test_class, fields ) return self._test_class( self.context, **obj_test_base.remove_timestamps_from_fields( fields, self._test_class.fields)) def test_vif_details(self): vif_details = {'item1': 'val1', 'item2': 'val2'} obj = self._make_object(self.obj_fields[0]) obj.vif_details = vif_details obj.create() obj = self._test_class.get_object( self.context, **obj._get_composite_keys()) self.assertEqual(vif_details, obj.vif_details) vif_details['item1'] = 1.23 del vif_details['item2'] vif_details['item3'] = True obj.vif_details = vif_details obj.update() obj = self._test_class.get_object( self.context, **obj._get_composite_keys()) self.assertEqual(vif_details, obj.vif_details) obj.vif_details = None obj.update() # here the obj is reloaded from DB, # so we test if vif_details is still none self.assertIsNone(obj.vif_details) obj = self._test_class.get_object( self.context, **obj._get_composite_keys()) self.assertIsNone(obj.vif_details) def test_null_vif_details_in_db(self): # the null case for vif_details in our db model is an # empty string. add that here to simulate it correctly # in the tests kwargs = self.get_random_db_fields() kwargs['vif_details'] = '' db_obj = self._test_class.db_model(**kwargs) obj_fields = self._test_class.modify_fields_from_db(db_obj) obj = self._test_class(self.context, **obj_fields) self.assertIsNone(obj.vif_details) class IPAllocationIfaceObjTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = ports.IPAllocation class IPAllocationDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = ports.IPAllocation def setUp(self): super(IPAllocationDbObjectTestCase, self).setUp() network_id = self._create_test_network_id() port_id = self._create_test_port_id(network_id=network_id) self.update_obj_fields( {'port_id': port_id, 'network_id': network_id, 'subnet_id': lambda: self._create_test_subnet_id(network_id)}) class PortDNSIfaceObjTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = ports.PortDNS class PortDNSDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = ports.PortDNS def setUp(self): super(PortDNSDbObjectTestCase, self).setUp() self.update_obj_fields( {'port_id': lambda: self._create_test_port_id()}) class PortBindingLevelIfaceObjTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = ports.PortBindingLevel def setUp(self): super(PortBindingLevelIfaceObjTestCase, self).setUp() # for this object, the model contains segment_id but we expose it # through an ObjectField that is loaded without a relationship for obj in self.db_objs: obj['segment_id'] = None self.pager_map[self._test_class.obj_name()] = ( obj_base.Pager(sorts=[('port_id', True), ('level', True)])) self.pager_map[network.NetworkSegment.obj_name()] = ( obj_base.Pager( sorts=[('network_id', True), ('segment_index', True)])) class PortBindingLevelDbObjectTestCase( obj_test_base.BaseDbObjectTestCase): _test_class = ports.PortBindingLevel class PortIfaceObjTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = ports.Port def setUp(self): super(PortIfaceObjTestCase, self).setUp() self.pager_map[ports.PortBindingLevel.obj_name()] = ( obj_base.Pager(sorts=[('port_id', True), ('level', True)])) class PortDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = ports.Port def setUp(self): super(PortDbObjectTestCase, self).setUp() network_id = self._create_test_network_id() subnet_id = self._create_test_subnet_id(network_id) self.update_obj_fields( {'network_id': network_id, 'fixed_ips': {'subnet_id': subnet_id, 'network_id': network_id}}) def test_security_group_ids(self): groups = [] objs = [] for i in range(2): groups.append(self._create_test_security_group_id()) objs.append(self._make_object(self.obj_fields[i])) objs[i].security_group_ids = {groups[i]} objs[i].create() self.assertEqual([objs[0]], ports.Port.get_objects( self.context, security_group_ids=(groups[0], ))) self.assertEqual([objs[1]], ports.Port.get_objects( self.context, security_group_ids=(groups[1], ))) sg3_id = self._create_test_security_group_id() objs[0].security_group_ids = {sg3_id} objs[0].update() objs[0] = ports.Port.get_object(self.context, id=objs[0].id) self.assertEqual({sg3_id}, objs[0].security_group_ids) objs[0].security_group_ids = set() objs[0].update() objs[0] = ports.Port.get_object(self.context, id=objs[0].id) self.assertFalse(objs[0].security_group_ids) def test_security_group_ids_and_port_id(self): objs = [] group = self._create_test_security_group_id() for i in range(2): objs.append(self._make_object(self.obj_fields[i])) objs[i].security_group_ids = {group} objs[i].create() for i in range(2): self.assertEqual( [objs[i]], ports.Port.get_objects( self.context, id=(objs[i].id, ), security_group_ids=(group, ))) def test__attach_security_group(self): obj = self._make_object(self.obj_fields[0]) obj.create() sg_id = self._create_test_security_group_id() obj._attach_security_group(sg_id) obj = ports.Port.get_object(self.context, id=obj.id) self.assertIn(sg_id, obj.security_group_ids) sg2_id = self._create_test_security_group_id() obj._attach_security_group(sg2_id) obj = ports.Port.get_object(self.context, id=obj.id) self.assertIn(sg2_id, obj.security_group_ids) @mock.patch.object(policy.QosPolicy, 'unset_default') def test_qos_policy_id(self, *mocks): policy_obj = policy.QosPolicy(self.context) policy_obj.create() obj = self._make_object(self.obj_fields[0]) obj.qos_policy_id = policy_obj.id obj.create() obj = ports.Port.get_object(self.context, id=obj.id) self.assertEqual(policy_obj.id, obj.qos_policy_id) policy_obj2 = policy.QosPolicy(self.context) policy_obj2.create() obj.qos_policy_id = policy_obj2.id obj.update() obj = ports.Port.get_object(self.context, id=obj.id) self.assertEqual(policy_obj2.id, obj.qos_policy_id) obj.qos_policy_id = None obj.update() obj = ports.Port.get_object(self.context, id=obj.id) self.assertIsNone(obj.qos_policy_id) @mock.patch.object(policy.QosPolicy, 'unset_default') def test__attach_qos_policy(self, *mocks): obj = self._make_object(self.obj_fields[0]) obj.create() policy_obj = policy.QosPolicy(self.context) policy_obj.create() obj._attach_qos_policy(policy_obj.id) obj = ports.Port.get_object(self.context, id=obj.id) self.assertEqual(policy_obj.id, obj.qos_policy_id) qos_binding_obj = binding.QosPolicyPortBinding.get_object( self.context, port_id=obj.id) self.assertEqual(qos_binding_obj.policy_id, obj.qos_policy_id) old_policy_id = policy_obj.id policy_obj2 = policy.QosPolicy(self.context) policy_obj2.create() obj._attach_qos_policy(policy_obj2.id) obj = ports.Port.get_object(self.context, id=obj.id) self.assertEqual(policy_obj2.id, obj.qos_policy_id) qos_binding_obj2 = binding.QosPolicyPortBinding.get_object( self.context, port_id=obj.id) self.assertEqual(qos_binding_obj2.policy_id, obj.qos_policy_id) qos_binding_obj = binding.QosPolicyPortBinding.get_objects( self.context, policy_id=old_policy_id) self.assertEqual(0, len(qos_binding_obj)) def test_get_objects_queries_constant(self): self.skipTest( 'Port object loads segment info without relationships') def test_v1_1_to_v1_0_drops_data_plane_status(self): port_new = self._create_test_port() port_v1_0 = port_new.obj_to_primitive(target_version='1.0') self.assertNotIn('data_plane_status', port_v1_0['versioned_object.data']) neutron-12.1.1/neutron/tests/unit/objects/test_metering.py0000664000175000017500000000344013553660046024010 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import metering from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class MeteringLabelRuleObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = metering.MeteringLabelRule class MeteringLabelRuleDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = metering.MeteringLabelRule def _create_test_metering_label_id(self, **attrs): attrs = self.get_random_object_fields(metering.MeteringLabel) metering_label = metering.MeteringLabel(self.context, **attrs) metering_label.create() return metering_label.id def setUp(self): super(MeteringLabelRuleDbObjectTestCase, self).setUp() self.update_obj_fields( {'metering_label_id': self._create_test_metering_label_id}) class MeteringLabelObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = metering.MeteringLabel class MeteringLabelDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = metering.MeteringLabel neutron-12.1.1/neutron/tests/unit/objects/plugins/0000775000175000017500000000000013553660157022250 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/plugins/__init__.py0000664000175000017500000000000013553660046024344 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/plugins/ml2/0000775000175000017500000000000013553660157022742 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/plugins/ml2/test_vxlanallocation.py0000664000175000017500000000251613553660046027552 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects.plugins.ml2 import vxlanallocation as vxlan_obj from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api class VxlanAllocationIfaceObjTestCase(test_base.BaseObjectIfaceTestCase): _test_class = vxlan_obj.VxlanAllocation class VxlanAllocationDbObjTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = vxlan_obj.VxlanAllocation class VxlanEndpointIfaceObjTestCase(test_base.BaseObjectIfaceTestCase): _test_class = vxlan_obj.VxlanEndpoint class VxlanEndpointDbObjTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = vxlan_obj.VxlanEndpoint neutron-12.1.1/neutron/tests/unit/objects/plugins/ml2/__init__.py0000664000175000017500000000000013553660046025036 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/plugins/ml2/test_geneveallocation.py0000664000175000017500000000254713553660046027677 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects.plugins.ml2 import geneveallocation from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api class GeneveAllocationIfaceObjTestCase(test_base.BaseObjectIfaceTestCase): _test_class = geneveallocation.GeneveAllocation class GeneveAllocationDbObjTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = geneveallocation.GeneveAllocation class GeneveEndpointIfaceObjTestCase(test_base.BaseObjectIfaceTestCase): _test_class = geneveallocation.GeneveEndpoint class GeneveEndpointDbObjTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = geneveallocation.GeneveEndpoint neutron-12.1.1/neutron/tests/unit/objects/plugins/ml2/test_greallocation.py0000664000175000017500000000247513553660046027203 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects.plugins.ml2 import greallocation as gre_object from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api class GreAllocationIfaceObjTestCase(test_base.BaseObjectIfaceTestCase): _test_class = gre_object.GreAllocation class GreAllocationDbObjTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = gre_object.GreAllocation class GreEndpointIfaceObjTestCase(test_base.BaseObjectIfaceTestCase): _test_class = gre_object.GreEndpoint class GreEndpointDbObjTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = gre_object.GreEndpoint neutron-12.1.1/neutron/tests/unit/objects/plugins/ml2/test_flatallocation.py0000664000175000017500000000204713553660046027347 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects.plugins.ml2 import flatallocation from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api class FlatAllocationIfaceObjTestCase(test_base.BaseObjectIfaceTestCase): _test_class = flatallocation.FlatAllocation class FlatAllocationDbObjTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = flatallocation.FlatAllocation neutron-12.1.1/neutron/tests/unit/objects/plugins/ml2/test_vlanallocation.py0000664000175000017500000000204613553660046027360 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects.plugins.ml2 import vlanallocation from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api class VlanAllocationIfaceObjTestCase(test_base.BaseObjectIfaceTestCase): _test_class = vlanallocation.VlanAllocation class VlanAllocationDbObjTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = vlanallocation.VlanAllocation neutron-12.1.1/neutron/tests/unit/objects/test_l3agent.py0000664000175000017500000000263213553660046023535 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import l3agent from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api class RouterL3AgentBindingIfaceObjTestCase(test_base.BaseObjectIfaceTestCase): _test_class = l3agent.RouterL3AgentBinding class RouterL3AgentBindingDbObjTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = l3agent.RouterL3AgentBinding def setUp(self): super(RouterL3AgentBindingDbObjTestCase, self).setUp() router_id = self._create_test_router_id() index = iter(range(1, len(self.objs) + 2)) self.update_obj_fields( {'router_id': router_id, 'binding_index': lambda: next(index), 'l3_agent_id': lambda: self._create_test_agent_id()}) neutron-12.1.1/neutron/tests/unit/objects/test_common_types.py0000664000175000017500000002665413553660047024727 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import itertools from neutron_lib import constants as const from neutron_lib.db import constants as db_const from neutron_lib.utils import net from oslo_serialization import jsonutils from neutron.common import constants from neutron.objects import common_types from neutron.tests import base as test_base from neutron.tests import tools class TestField(object): def test_coerce_good_values(self): for in_val, out_val in self.coerce_good_values: self.assertEqual(out_val, self.field.coerce('obj', 'attr', in_val)) def test_coerce_bad_values(self): for in_val in self.coerce_bad_values: self.assertRaises((TypeError, ValueError), self.field.coerce, 'obj', 'attr', in_val) def test_to_primitive(self): for in_val, prim_val in self.to_primitive_values: self.assertEqual(prim_val, self.field.to_primitive('obj', 'attr', in_val)) def test_to_primitive_json_serializable(self): for in_val, _ in self.to_primitive_values: prim = self.field.to_primitive('obj', 'attr', in_val) jsencoded = jsonutils.dumps(prim) self.assertEqual(prim, jsonutils.loads(jsencoded)) def test_from_primitive(self): class ObjectLikeThing(object): _context = 'context' for prim_val, out_val in self.from_primitive_values: from_prim = self.field.from_primitive(ObjectLikeThing, 'attr', prim_val) self.assertEqual(out_val, from_prim) # ensure it's coercable for sanity self.field.coerce('obj', 'attr', from_prim) @abc.abstractmethod def test_stringify(self): '''This test should validate stringify() format for new field types.''' class IPV6ModeEnumFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(IPV6ModeEnumFieldTest, self).setUp() self.field = common_types.IPV6ModeEnumField() self.coerce_good_values = [(mode, mode) for mode in const.IPV6_MODES] self.coerce_bad_values = ['6', 4, 'type', 'slaacc'] self.to_primitive_values = self.coerce_good_values self.from_primitive_values = self.coerce_good_values def test_stringify(self): for in_val, out_val in self.coerce_good_values: self.assertEqual("'%s'" % in_val, self.field.stringify(in_val)) class DscpMarkFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(DscpMarkFieldTest, self).setUp() self.field = common_types.DscpMarkField() self.coerce_good_values = [(val, val) for val in const.VALID_DSCP_MARKS] self.coerce_bad_values = ['6', 'str', [], {}, object()] self.to_primitive_values = self.coerce_good_values self.from_primitive_values = self.coerce_good_values def test_stringify(self): for in_val, out_val in self.coerce_good_values: self.assertEqual("%s" % in_val, self.field.stringify(in_val)) class IPNetworkPrefixLenFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(IPNetworkPrefixLenFieldTest, self).setUp() self.field = common_types.IPNetworkPrefixLenField() self.coerce_good_values = [(x, x) for x in (0, 32, 128, 42)] self.coerce_bad_values = ['len', '1', 129, -1] self.to_primitive_values = self.coerce_good_values self.from_primitive_values = self.coerce_good_values def test_stringify(self): for in_val, out_val in self.coerce_good_values: self.assertEqual("%s" % in_val, self.field.stringify(in_val)) class MACAddressFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(MACAddressFieldTest, self).setUp() self.field = common_types.MACAddressField() mac1 = tools.get_random_EUI() mac2 = tools.get_random_EUI() self.coerce_good_values = [(mac1, mac1), (mac2, mac2)] self.coerce_bad_values = [ 'XXXX', 'ypp', 'g3:vvv', # the field type is strict and does not allow to pass strings, even # if they represent a valid MAC address net.get_random_mac('fe:16:3e:00:00:00'.split(':')), ] self.to_primitive_values = ((a1, str(a2)) for a1, a2 in self.coerce_good_values) self.from_primitive_values = ((a2, a1) for a1, a2 in self.to_primitive_values) def test_stringify(self): for in_val, out_val in self.coerce_good_values: self.assertEqual('%s' % in_val, self.field.stringify(in_val)) class IPNetworkFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(IPNetworkFieldTest, self).setUp() self.field = common_types.IPNetworkField() addrs = [ tools.get_random_ip_network(version=ip_version) for ip_version in constants.IP_ALLOWED_VERSIONS ] self.coerce_good_values = [(addr, addr) for addr in addrs] self.coerce_bad_values = [ 'ypp', 'g3:vvv', # the field type is strict and does not allow to pass strings, even # if they represent a valid IP network '10.0.0.0/24', ] self.to_primitive_values = ((a1, str(a2)) for a1, a2 in self.coerce_good_values) self.from_primitive_values = ((a2, a1) for a1, a2 in self.to_primitive_values) def test_stringify(self): for in_val, out_val in self.coerce_good_values: self.assertEqual('%s' % in_val, self.field.stringify(in_val)) class IPVersionEnumFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(IPVersionEnumFieldTest, self).setUp() self.field = common_types.IPVersionEnumField() self.coerce_good_values = [(val, val) for val in constants.IP_ALLOWED_VERSIONS] self.coerce_bad_values = [5, 0, -1, 'str'] self.to_primitive_values = self.coerce_good_values self.from_primitive_values = self.coerce_good_values def test_stringify(self): for in_val, out_val in self.coerce_good_values: self.assertEqual("%s" % in_val, self.field.stringify(in_val)) class FlowDirectionEnumFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(FlowDirectionEnumFieldTest, self).setUp() self.field = common_types.FlowDirectionEnumField() self.coerce_good_values = [(val, val) for val in const.VALID_DIRECTIONS] self.coerce_bad_values = ['test', '8', 10, []] self.to_primitive_values = self.coerce_good_values self.from_primitive_values = self.coerce_good_values def test_stringify(self): for in_val, out_val in self.coerce_good_values: self.assertEqual("'%s'" % in_val, self.field.stringify(in_val)) class DomainNameFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(DomainNameFieldTest, self).setUp() self.field = common_types.DomainNameField() self.coerce_good_values = [ (val, val) for val in ('www.google.com', 'hostname', '1abc.com') ] self.coerce_bad_values = ['x' * (db_const.FQDN_FIELD_SIZE + 1), 10, []] self.to_primitive_values = self.coerce_good_values self.from_primitive_values = self.coerce_good_values def test_stringify(self): for in_val, out_val in self.coerce_good_values: self.assertEqual("'%s'" % in_val, self.field.stringify(in_val)) class EtherTypeEnumFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(EtherTypeEnumFieldTest, self).setUp() self.field = common_types.EtherTypeEnumField() self.coerce_good_values = [(val, val) for val in constants.VALID_ETHERTYPES] self.coerce_bad_values = ['IpV4', 8, 'str', 'ipv6'] self.to_primitive_values = self.coerce_good_values self.from_primitive_values = self.coerce_good_values def test_stringify(self): for in_val, out_val in self.coerce_good_values: self.assertEqual("'%s'" % in_val, self.field.stringify(in_val)) class IpProtocolEnumFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(IpProtocolEnumFieldTest, self).setUp() self.field = common_types.IpProtocolEnumField() self.coerce_good_values = [ (val, val) for val in itertools.chain( const.IP_PROTOCOL_MAP.keys(), [str(v) for v in range(256)] ) ] self.coerce_bad_values = ['test', 'Udp', 256] self.to_primitive_values = self.coerce_good_values self.from_primitive_values = self.coerce_good_values def test_stringify(self): for in_val, out_val in self.coerce_good_values: self.assertEqual("'%s'" % in_val, self.field.stringify(in_val)) class UUIDFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(UUIDFieldTest, self).setUp() self.field = common_types.UUIDField() self.coerce_good_values = [ ('f1d9cb3f-c263-45d3-907c-d12a9ef1629e', 'f1d9cb3f-c263-45d3-907c-d12a9ef1629e'), ('7188f6637cbd4097a3b1d1bb7897c7c0', '7188f6637cbd4097a3b1d1bb7897c7c0')] self.coerce_bad_values = [ 'f1d9cb3f-c263-45d3-907c-d12a9ef16zzz', '7188f6637cbd4097a3b1d1bb7897'] self.to_primitive_values = self.coerce_good_values self.from_primitive_values = self.coerce_good_values def test_stringify(self): for in_val, out_val in self.coerce_good_values: self.assertEqual('%s' % in_val, self.field.stringify(in_val)) class DictOfMiscValuesFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(DictOfMiscValuesFieldTest, self).setUp() self.field = common_types.DictOfMiscValues test_dict_1 = {'a': True, 'b': 1.23, 'c': ['1', 1.23, True], 'd': {'aa': 'zz'}, 'e': '10.0.0.1'} test_dict_str = jsonutils.dumps(test_dict_1) self.coerce_good_values = [ (test_dict_1, test_dict_1), (test_dict_str, test_dict_1) ] self.coerce_bad_values = [str(test_dict_1), '{"a":}'] self.to_primitive_values = [ (test_dict_1, test_dict_str) ] self.from_primitive_values = [ (test_dict_str, test_dict_1) ] def test_stringify(self): for in_val, out_val in self.coerce_good_values: self.assertEqual(jsonutils.dumps(in_val), self.field.stringify(in_val)) neutron-12.1.1/neutron/tests/unit/objects/test_base.py0000664000175000017500000023642313553660047023122 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import copy import itertools import random import fixtures import mock import netaddr from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions as n_exc from neutron_lib.objects import exceptions as o_exc from neutron_lib.utils import helpers from oslo_db import exception as obj_exc from oslo_db.sqlalchemy import utils as db_utils from oslo_utils import uuidutils from oslo_versionedobjects import base as obj_base from oslo_versionedobjects import exception from oslo_versionedobjects import fields as obj_fields from sqlalchemy import orm import testtools from neutron.db import _model_query as model_query from neutron import objects from neutron.objects import agent from neutron.objects import base from neutron.objects import common_types from neutron.objects.db import api as obj_db_api from neutron.objects import flavor from neutron.objects.logapi import event_types from neutron.objects import network as net_obj from neutron.objects import ports from neutron.objects.qos import policy as qos_policy from neutron.objects import rbac_db from neutron.objects import router from neutron.objects import securitygroup from neutron.objects import stdattrs from neutron.objects import subnet from neutron.objects import utils as obj_utils from neutron.tests import base as test_base from neutron.tests import tools from neutron.tests.unit.db import test_db_base_plugin_v2 SQLALCHEMY_COMMIT = 'sqlalchemy.engine.Connection._commit_impl' SQLALCHEMY_CLOSE = 'sqlalchemy.engine.Connection.close' OBJECTS_BASE_OBJ_FROM_PRIMITIVE = ('oslo_versionedobjects.base.' 'VersionedObject.obj_from_primitive') TIMESTAMP_FIELDS = ['created_at', 'updated_at', 'revision_number'] class FakeModel(dict): pass class ObjectFieldsModel(dict): pass @base.NeutronObjectRegistry.register_if(False) class FakeSmallNeutronObject(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = ObjectFieldsModel primary_keys = ['field1'] foreign_keys = { 'FakeNeutronObjectCompositePrimaryKeyWithId': {'field1': 'id'}, 'FakeNeutronDbObject': {'field2': 'id'}, 'FakeNeutronObjectUniqueKey': {'field3': 'id'}, } fields = { 'field1': common_types.UUIDField(), 'field2': common_types.UUIDField(), 'field3': common_types.UUIDField(), } @base.NeutronObjectRegistry.register_if(False) class FakeSmallNeutronObjectWithMultipleParents(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = ObjectFieldsModel primary_keys = ['field1', 'field2'] foreign_keys = { 'FakeParent': {'field1': 'id'}, 'FakeParent2': {'field2': 'id'}, } fields = { 'field1': common_types.UUIDField(), 'field2': obj_fields.StringField(), } @base.NeutronObjectRegistry.register_if(False) class FakeParent(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = ObjectFieldsModel primary_keys = ['field1', 'field2'] fields = { 'id': common_types.UUIDField(), 'children': obj_fields.ListOfObjectsField( 'FakeSmallNeutronObjectWithMultipleParents', nullable=True) } synthetic_fields = ['children'] @base.NeutronObjectRegistry.register_if(False) class FakeWeirdKeySmallNeutronObject(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = ObjectFieldsModel primary_keys = ['field1'] foreign_keys = { 'FakeNeutronObjectNonStandardPrimaryKey': {'field1': 'weird_key'}, 'FakeNeutronObjectCompositePrimaryKey': {'field2': 'weird_key'}, } fields = { 'field1': common_types.UUIDField(), 'field2': obj_fields.StringField(), } class NeutronObjectRegistryFixture(fixtures.Fixture): """Use a NeutronObjectRegistry as a temp registry pattern fixture. It is fixture similar to oslo_versionedobjects.fixture.VersionedObjectRegistryFixture but it uses Neutron's base registry class """ def setUp(self): super(NeutronObjectRegistryFixture, self).setUp() self._base_test_obj_backup = copy.deepcopy( base.NeutronObjectRegistry._registry._obj_classes) self.addCleanup(self._restore_obj_registry) @staticmethod def register(cls_name): base.NeutronObjectRegistry.register(cls_name) def _restore_obj_registry(self): base.NeutronObjectRegistry._registry._obj_classes = \ self._base_test_obj_backup @base.NeutronObjectRegistry.register_if(False) class FakeNeutronDbObject(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel fields = { 'id': common_types.UUIDField(), 'field1': obj_fields.StringField(), 'obj_field': obj_fields.ObjectField('FakeSmallNeutronObject', nullable=True) } primary_keys = ['id'] fields_no_update = ['field1'] synthetic_fields = ['obj_field'] @base.NeutronObjectRegistry.register_if(False) class FakeNeutronObjectNonStandardPrimaryKey(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel primary_keys = ['weird_key'] fields = { 'weird_key': common_types.UUIDField(), 'field1': obj_fields.StringField(), 'obj_field': obj_fields.ListOfObjectsField( 'FakeWeirdKeySmallNeutronObject'), 'field2': obj_fields.StringField() } synthetic_fields = ['obj_field', 'field2'] @base.NeutronObjectRegistry.register_if(False) class FakeNeutronObjectCompositePrimaryKey(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel primary_keys = ['weird_key', 'field1'] fields = { 'weird_key': common_types.UUIDField(), 'field1': obj_fields.StringField(), 'obj_field': obj_fields.ListOfObjectsField( 'FakeWeirdKeySmallNeutronObject') } synthetic_fields = ['obj_field'] @base.NeutronObjectRegistry.register_if(False) class FakeNeutronObjectUniqueKey(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel primary_keys = ['id', 'id2'] unique_keys = [['unique_key'], ['id2']] fields = { 'id': common_types.UUIDField(), 'id2': common_types.UUIDField(), 'unique_key': obj_fields.StringField(), 'field1': obj_fields.StringField(), 'obj_field': obj_fields.ObjectField('FakeSmallNeutronObject', nullable=True) } synthetic_fields = ['obj_field'] @base.NeutronObjectRegistry.register_if(False) class FakeNeutronObjectRenamedField(base.NeutronDbObject): """ Testing renaming the parameter from DB to NeutronDbObject For tests: - db fields: id, field_db, field2 - object: id, field_ovo, field2 """ # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel primary_keys = ['id'] fields = { 'id': common_types.UUIDField(), 'field_ovo': obj_fields.StringField(), 'field2': obj_fields.StringField() } synthetic_fields = ['field2'] fields_need_translation = {'field_ovo': 'field_db'} @base.NeutronObjectRegistry.register_if(False) class FakeNeutronObjectCompositePrimaryKeyWithId(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel primary_keys = ['id', 'field1'] fields = { 'id': common_types.UUIDField(), 'field1': obj_fields.StringField(), 'obj_field': obj_fields.ListOfObjectsField('FakeSmallNeutronObject') } synthetic_fields = ['obj_field'] @base.NeutronObjectRegistry.register_if(False) class FakeNeutronObjectMultipleForeignKeys(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = ObjectFieldsModel foreign_keys = { 'FakeNeutronObjectSyntheticField': {'field1': 'id', 'field2': 'id'}, } fields = { 'field1': common_types.UUIDField(), 'field2': common_types.UUIDField(), } @base.NeutronObjectRegistry.register_if(False) class FakeNeutronObjectSyntheticField(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel fields = { 'id': common_types.UUIDField(), 'obj_field': obj_fields.ListOfObjectsField( 'FakeNeutronObjectMultipleForeignKeys') } synthetic_fields = ['obj_field'] @base.NeutronObjectRegistry.register_if(False) class FakeNeutronObjectSyntheticField2(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel fields = { 'id': common_types.UUIDField(), 'obj_field': obj_fields.ObjectField('FakeSmallNeutronObject') } synthetic_fields = ['obj_field'] @base.NeutronObjectRegistry.register_if(False) class FakeNeutronObjectWithProjectId(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(), 'field2': common_types.UUIDField(), } @base.NeutronObjectRegistry.register_if(False) class FakeNeutronObject(base.NeutronObject): # Version 1.0: Initial version VERSION = '1.0' fields = { 'id': common_types.UUIDField(), 'project_id': obj_fields.StringField(), 'field2': common_types.UUIDField(), } @classmethod def get_object(cls, context, **kwargs): if not hasattr(cls, '_obj'): cls._obj = FakeNeutronObject(id=uuidutils.generate_uuid(), project_id='fake-id', field2=uuidutils.generate_uuid()) return cls._obj @classmethod def get_objects(cls, context, _pager=None, count=1, **kwargs): return [ cls.get_object(context, **kwargs) for i in range(count) ] @base.NeutronObjectRegistry.register_if(False) class FakeNeutronObjectDictOfMiscValues(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel fields = { 'id': common_types.UUIDField(), 'dict_field': common_types.DictOfMiscValuesField(), } @base.NeutronObjectRegistry.register_if(False) class FakeNeutronObjectListOfDictOfMiscValues(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel fields = { 'id': common_types.UUIDField(), 'list_of_dicts_field': common_types.ListOfDictOfMiscValuesField(), } def get_random_dscp_mark(): return random.choice(constants.VALID_DSCP_MARKS) def get_list_of_random_networks(num=10): for i in range(5): res = [tools.get_random_ip_network() for i in range(num)] # make sure there are no duplicates if len(set(res)) == num: return res raise Exception('Failed to generate unique networks') def get_random_domain_name(): return '.'.join([ helpers.get_random_string(62)[:random.choice(range(63))] for i in range(4) ]) def get_random_dict_of_strings(): return { helpers.get_random_string(10): helpers.get_random_string(10) for i in range(10) } def get_random_dict(): return { helpers.get_random_string(6): helpers.get_random_string(6), helpers.get_random_string(6): tools.get_random_boolean(), helpers.get_random_string(6): tools.get_random_integer(), helpers.get_random_string(6): [ tools.get_random_integer(), helpers.get_random_string(6), tools.get_random_boolean(), ], helpers.get_random_string(6): { helpers.get_random_string(6): helpers.get_random_string(6) } } def get_random_dicts_list(): return [get_random_dict() for _ in range(5)] def get_set_of_random_uuids(): return { uuidutils.generate_uuid() for i in range(10) } # NOTE: The keys in this dictionary have alphabetic order. FIELD_TYPE_VALUE_GENERATOR_MAP = { common_types.DictOfMiscValuesField: get_random_dict, common_types.ListOfDictOfMiscValuesField: get_random_dicts_list, common_types.DomainNameField: get_random_domain_name, common_types.DscpMarkField: get_random_dscp_mark, common_types.EtherTypeEnumField: tools.get_random_ether_type, common_types.FloatingIPStatusEnumField: tools.get_random_floatingip_status, common_types.FlowDirectionEnumField: tools.get_random_flow_direction, common_types.HARouterEnumField: tools.get_random_ha_states, common_types.IpamAllocationStatusEnumField: tools.get_random_ipam_status, common_types.IPNetworkField: tools.get_random_ip_network, common_types.IPNetworkPrefixLenField: tools.get_random_prefixlen, common_types.IPV6ModeEnumField: tools.get_random_ipv6_mode, common_types.IPVersionEnumField: tools.get_random_ip_version, common_types.IpProtocolEnumField: tools.get_random_ip_protocol, common_types.ListOfIPNetworksField: get_list_of_random_networks, common_types.ListOfObjectsField: lambda: [], common_types.MACAddressField: tools.get_random_EUI, common_types.PortBindingStatusEnumField: tools.get_random_port_binding_statuses, common_types.PortRangeField: tools.get_random_port, common_types.PortRangeWith0Field: lambda: tools.get_random_port(0), common_types.RouterStatusEnumField: tools.get_random_router_status, common_types.SetOfUUIDsField: get_set_of_random_uuids, common_types.UUIDField: uuidutils.generate_uuid, common_types.VlanIdRangeField: tools.get_random_vlan, event_types.SecurityEventField: tools.get_random_security_event, obj_fields.BooleanField: tools.get_random_boolean, obj_fields.DateTimeField: tools.get_random_datetime, obj_fields.DictOfStringsField: get_random_dict_of_strings, obj_fields.IPAddressField: tools.get_random_ip_address, obj_fields.IntegerField: tools.get_random_integer, obj_fields.ListOfObjectsField: lambda: [], obj_fields.ListOfStringsField: tools.get_random_string_list, obj_fields.ObjectField: lambda: None, obj_fields.StringField: lambda: helpers.get_random_string(10), } def get_obj_persistent_fields(obj): return {field: getattr(obj, field) for field in obj.fields if field not in obj.synthetic_fields if field in obj} def get_value(generator, version): if 'version' in generator.__code__.co_varnames: return generator(version=version) return generator() def remove_timestamps_from_fields(obj_fields, cls_fields): obj_fields_result = obj_fields.copy() for ts_field in TIMESTAMP_FIELDS: if ts_field in cls_fields.keys() and cls_fields[ts_field].nullable: obj_fields_result.pop(ts_field) return obj_fields_result def get_non_synthetic_fields(objclass, obj_fields): return {field: value for field, value in obj_fields.items() if not objclass.is_synthetic(field)} class _BaseObjectTestCase(object): _test_class = FakeNeutronDbObject CORE_PLUGIN = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' def setUp(self): super(_BaseObjectTestCase, self).setUp() # TODO(ihrachys): revisit plugin setup once we decouple # neutron.objects.db.api from core plugin instance self.setup_coreplugin(self.CORE_PLUGIN) # make sure all objects are loaded and registered in the registry objects.register_objects() self.context = context.get_admin_context() self._unique_tracker = collections.defaultdict(set) self.locked_obj_fields = collections.defaultdict(set) self.db_objs = [ self._test_class.db_model(**self.get_random_db_fields()) for _ in range(3) ] # TODO(ihrachys) remove obj_fields since they duplicate self.objs self.obj_fields = [self._test_class.modify_fields_from_db(db_obj) for db_obj in self.db_objs] self.objs = [ self._test_class(self.context, **fields) for fields in self.obj_fields ] invalid_fields = ( set(self._test_class.synthetic_fields).union(set(TIMESTAMP_FIELDS)) ) valid_field = [f for f in self._test_class.fields if f not in invalid_fields][0] self.valid_field_filter = {valid_field: self.obj_fields[-1][valid_field]} self.obj_registry = self.useFixture( NeutronObjectRegistryFixture()) self.obj_registry.register(FakeSmallNeutronObject) self.obj_registry.register(FakeWeirdKeySmallNeutronObject) self.obj_registry.register(FakeNeutronObjectMultipleForeignKeys) synthetic_obj_fields = self.get_random_db_fields( FakeSmallNeutronObject) self.model_map = { self._test_class.db_model: self.db_objs, ObjectFieldsModel: [ObjectFieldsModel(**synthetic_obj_fields)]} def _get_random_update_fields(self): return self.get_updatable_fields( self.get_random_object_fields(self._test_class)) def get_random_object_fields(self, obj_cls=None): obj_cls = obj_cls or self._test_class fields = {} ip_version = tools.get_random_ip_version() for field, field_obj in obj_cls.fields.items(): if field not in obj_cls.synthetic_fields: generator = FIELD_TYPE_VALUE_GENERATOR_MAP[type(field_obj)] fields[field] = get_value(generator, ip_version) for k, v in self.locked_obj_fields.items(): if k in fields: fields[k] = v for keys in obj_cls.unique_keys: keytup = tuple(keys) unique_values = tuple(fields[k] for k in keytup) if unique_values in self._unique_tracker[keytup]: # if you get a recursion depth error here, it means # your random generator didn't generate unique values return self.get_random_object_fields(obj_cls) self._unique_tracker[keytup].add(unique_values) return fields def get_random_db_fields(self, obj_cls=None): obj_cls = obj_cls or self._test_class return obj_cls.modify_fields_to_db( self.get_random_object_fields(obj_cls)) def update_obj_fields(self, values_dict, db_objs=None, obj_fields=None, objs=None): '''Update values for test objects with specific values. The default behaviour is using random values for all fields of test objects. Sometimes it's not practical, for example, when some fields, often those referencing other objects, require non-random values (None or UUIDs of valid objects). If that's the case, a test subclass may call the method to override some field values for test objects. Receives a single ``values_dict`` dict argument where keys are names of test class fields, and values are either actual values for the keys, or callables that will be used to generate different values for each test object. Note: if a value is a dict itself, the method will recursively update corresponding embedded objects. ''' # TODO(ihrachys) make the method update db_objs to keep generated test # objects unique despite new locked fields for k, v in values_dict.items(): for db_obj, fields, obj in zip( db_objs or self.db_objs, obj_fields or self.obj_fields, objs or self.objs): val = v() if callable(v) else v db_obj_key = obj.fields_need_translation.get(k, k) if isinstance(val, collections.Mapping): self.update_obj_fields( val, db_obj[db_obj_key], fields[k], obj[k]) else: db_obj[db_obj_key] = val fields[k] = val obj[k] = val if k in self.valid_field_filter: self.valid_field_filter[k] = val self.locked_obj_fields[k] = v() if callable(v) else v @classmethod def generate_object_keys(cls, obj_cls, field_names=None): if field_names is None: field_names = obj_cls.primary_keys keys = {} for field in field_names: field_obj = obj_cls.fields[field] generator = FIELD_TYPE_VALUE_GENERATOR_MAP[type(field_obj)] keys[field] = generator() return keys def get_updatable_fields(self, fields): return base.get_updatable_fields(self._test_class, fields) @classmethod def _is_test_class(cls, obj): return isinstance(obj, cls._test_class) def fake_get_objects(self, obj_cls, context, **kwargs): return self.model_map[obj_cls.db_model] def _get_object_synthetic_fields(self, objclass): return [field for field in objclass.synthetic_fields if objclass.is_object_field(field)] def _get_ovo_object_class(self, objclass, field): try: name = objclass.fields[field].objname return base.NeutronObjectRegistry.obj_classes().get(name)[0] except TypeError: # NOTE(korzen) some synthetic fields are not handled by # this method, for example the ones that have subclasses, see # QosRule return class BaseObjectIfaceTestCase(_BaseObjectTestCase, test_base.BaseTestCase): def setUp(self): super(BaseObjectIfaceTestCase, self).setUp() self.model_map = collections.defaultdict(list) self.model_map[self._test_class.db_model] = self.db_objs self.pager_map = collections.defaultdict(lambda: None) # don't validate refresh and expunge in tests that don't touch database # because otherwise it will fail due to db models not being injected # into active session in the first place mock.patch.object(self.context.session, 'refresh').start() mock.patch.object(self.context.session, 'expunge').start() self.get_objects_mock = mock.patch.object( obj_db_api, 'get_objects', side_effect=self.fake_get_objects).start() self.get_object_mock = mock.patch.object( obj_db_api, 'get_object', side_effect=self.fake_get_object).start() # NOTE(ihrachys): for matters of basic object behaviour validation, # mock out rbac code accessing database. There are separate tests that # cover RBAC, per object type. if self._test_class.rbac_db_cls is not None: if getattr(self._test_class.rbac_db_cls, 'db_model', None): mock.patch.object( rbac_db.RbacNeutronDbObjectMixin, 'is_shared_with_tenant', return_value=False).start() mock.patch.object( rbac_db.RbacNeutronDbObjectMixin, 'get_shared_with_tenant').start() def fake_get_object(self, context, model, **kwargs): objs = self.model_map[model] if not objs: return None return [obj for obj in objs if obj['id'] == kwargs['id']][0] def fake_get_objects(self, obj_cls, context, **kwargs): return self.model_map[obj_cls.db_model] # TODO(ihrachys) document the intent of all common test cases in docstrings def test_get_object(self): with mock.patch.object( obj_db_api, 'get_object', return_value=self.db_objs[0]) as get_object_mock: with mock.patch.object(obj_db_api, 'get_objects', side_effect=self.fake_get_objects): obj_keys = self.generate_object_keys(self._test_class) obj = self._test_class.get_object(self.context, **obj_keys) self.assertTrue(self._is_test_class(obj)) self._check_equal(self.objs[0], obj) get_object_mock.assert_called_once_with( self._test_class, self.context, **self._test_class.modify_fields_to_db(obj_keys)) def test_get_object_missing_object(self): with mock.patch.object(obj_db_api, 'get_object', return_value=None): obj_keys = self.generate_object_keys(self._test_class) obj = self._test_class.get_object(self.context, **obj_keys) self.assertIsNone(obj) def test_get_object_missing_primary_key(self): non_unique_fields = (set(self._test_class.fields.keys()) - set(self._test_class.primary_keys) - set(itertools.chain.from_iterable( self._test_class.unique_keys))) obj_keys = self.generate_object_keys(self._test_class, non_unique_fields) exception = self.assertRaises(o_exc.NeutronPrimaryKeyMissing, self._test_class.get_object, self.context, **obj_keys) self.assertIn(self._test_class.__name__, str(exception)) def test_get_object_unique_key(self): if not self._test_class.unique_keys: self.skipTest('No unique keys found in test class %r' % self._test_class) for unique_keys in self._test_class.unique_keys: with mock.patch.object(obj_db_api, 'get_object', return_value=self.db_objs[0]) \ as get_object_mock: with mock.patch.object(obj_db_api, 'get_objects', side_effect=self.fake_get_objects): obj_keys = self.generate_object_keys(self._test_class, unique_keys) obj = self._test_class.get_object(self.context, **obj_keys) self.assertTrue(self._is_test_class(obj)) self._check_equal(self.objs[0], obj) get_object_mock.assert_called_once_with( self._test_class, mock.ANY, **self._test_class.modify_fields_to_db(obj_keys)) def _get_synthetic_fields_get_objects_calls(self, db_objs): mock_calls = [] for db_obj in db_objs: for field in self._test_class.synthetic_fields: if self._test_class.is_object_field(field): obj_class = self._get_ovo_object_class(self._test_class, field) filter_kwargs = { obj_class.fields_need_translation.get(k, k): db_obj[v] for k, v in obj_class.foreign_keys.get( self._test_class.__name__).items() } mock_calls.append( mock.call( obj_class, self.context, _pager=self.pager_map[obj_class.obj_name()], **filter_kwargs)) return mock_calls def test_get_objects(self): '''Test that get_objects fetches data from database.''' with mock.patch.object( obj_db_api, 'get_objects', side_effect=self.fake_get_objects) as get_objects_mock: objs = self._test_class.get_objects(self.context) self.assertItemsEqual( [get_obj_persistent_fields(obj) for obj in self.objs], [get_obj_persistent_fields(obj) for obj in objs]) get_objects_mock.assert_any_call( self._test_class, self.context, _pager=self.pager_map[self._test_class.obj_name()] ) def test_get_objects_valid_fields(self): '''Test that a valid filter does not raise an error.''' with mock.patch.object( obj_db_api, 'get_objects', side_effect=self.fake_get_objects): self._test_class.get_objects(self.context, **self.valid_field_filter) def test_get_objects_mixed_fields(self): synthetic_fields = ( set(self._test_class.synthetic_fields) - self._test_class.extra_filter_names ) if not synthetic_fields: self.skipTest('No synthetic fields that are not extra filters ' 'found in test class %r' % self._test_class) filters = copy.copy(self.valid_field_filter) filters[synthetic_fields.pop()] = 'xxx' with mock.patch.object(obj_db_api, 'get_objects', return_value=self.db_objs): self.assertRaises(n_exc.InvalidInput, self._test_class.get_objects, self.context, **filters) def test_get_objects_synthetic_fields_not_extra_filters(self): synthetic_fields = ( set(self._test_class.synthetic_fields) - self._test_class.extra_filter_names ) if not synthetic_fields: self.skipTest('No synthetic fields that are not extra filters ' 'found in test class %r' % self._test_class) with mock.patch.object(obj_db_api, 'get_objects', side_effect=self.fake_get_objects): self.assertRaises(n_exc.InvalidInput, self._test_class.get_objects, self.context, **{synthetic_fields.pop(): 'xxx'}) def test_get_objects_invalid_fields(self): with mock.patch.object(obj_db_api, 'get_objects', side_effect=self.fake_get_objects): self.assertRaises(n_exc.InvalidInput, self._test_class.get_objects, self.context, fake_field='xxx') def test_get_objects_without_validate_filters(self): with mock.patch.object( obj_db_api, 'get_objects', side_effect=self.fake_get_objects): objs = self._test_class.get_objects(self.context, validate_filters=False, unknown_filter='value') self.assertItemsEqual( [get_obj_persistent_fields(obj) for obj in self.objs], [get_obj_persistent_fields(obj) for obj in objs]) @mock.patch.object(obj_db_api, 'update_object', return_value={}) @mock.patch.object(obj_db_api, 'update_objects', return_value=0) def test_update_objects_valid_fields(self, *mocks): '''Test that a valid filter does not raise an error.''' self._test_class.update_objects( self.context, {}, **self.valid_field_filter) def test_update_objects_invalid_fields(self): with mock.patch.object(obj_db_api, 'update_objects'): self.assertRaises(n_exc.InvalidInput, self._test_class.update_objects, self.context, {}, fake_field='xxx') @mock.patch.object(obj_db_api, 'update_objects') @mock.patch.object(obj_db_api, 'update_object', return_value={}) def test_update_objects_without_validate_filters(self, *mocks): self._test_class.update_objects( self.context, {'unknown_filter': 'new_value'}, validate_filters=False, unknown_filter='value') def _prep_string_field(self): self.filter_string_field = None # find the first string field to use as string matching filter for field in self.obj_fields[0]: if isinstance(field, obj_fields.StringField): self.filter_string_field = field break if self.filter_string_field is None: self.skipTest('There is no string field in this object') def test_get_objects_with_string_matching_filters_contains(self): self._prep_string_field() filter_dict_contains = { self.filter_string_field: obj_utils.StringContains( "random_thing")} with mock.patch.object( obj_db_api, 'get_objects', side_effect=self.fake_get_objects): res = self._test_class.get_objects(self.context, **filter_dict_contains) self.assertEqual([], res) def test_get_objects_with_string_matching_filters_starts(self): self._prep_string_field() filter_dict_starts = { self.filter_string_field: obj_utils.StringStarts( "random_thing") } with mock.patch.object( obj_db_api, 'get_objects', side_effect=self.fake_get_objects): res = self._test_class.get_objects(self.context, **filter_dict_starts) self.assertEqual([], res) def test_get_objects_with_string_matching_filters_ends(self): self._prep_string_field() filter_dict_ends = { self.filter_string_field: obj_utils.StringEnds( "random_thing") } with mock.patch.object( obj_db_api, 'get_objects', side_effect=self.fake_get_objects): res = self._test_class.get_objects(self.context, **filter_dict_ends) self.assertEqual([], res) def test_delete_objects(self): '''Test that delete_objects calls to underlying db_api.''' with mock.patch.object( obj_db_api, 'delete_objects', return_value=0 ) as delete_objects_mock: self.assertEqual(0, self._test_class.delete_objects(self.context)) delete_objects_mock.assert_any_call( self._test_class, self.context) def test_delete_objects_valid_fields(self): '''Test that a valid filter does not raise an error.''' with mock.patch.object(obj_db_api, 'delete_objects', return_value=0): self._test_class.delete_objects(self.context, **self.valid_field_filter) def test_delete_objects_invalid_fields(self): with mock.patch.object(obj_db_api, 'delete_objects'): self.assertRaises(n_exc.InvalidInput, self._test_class.delete_objects, self.context, fake_field='xxx') def test_delete_objects_without_validate_filters(self): with mock.patch.object( obj_db_api, 'delete_objects'): self._test_class.delete_objects(self.context, validate_filters=False, unknown_filter='value') def test_count(self): if not isinstance(self._test_class, base.NeutronDbObject): self.skipTest('Class %s does not inherit from NeutronDbObject' % self._test_class) expected = 10 with mock.patch.object(obj_db_api, 'count', return_value=expected): self.assertEqual(expected, self._test_class.count(self.context)) def test_count_invalid_fields(self): self.assertRaises(n_exc.InvalidInput, self._test_class.count, self.context, fake_field='xxx') def _check_equal(self, expected, observed): self.assertItemsEqual(get_obj_persistent_fields(expected), get_obj_persistent_fields(observed)) def test_count_validate_filters_false(self): if not isinstance(self._test_class, base.NeutronDbObject): self.skipTest('Class %s does not inherit from NeutronDbObject' % self._test_class) expected = 10 with mock.patch.object(obj_db_api, 'count', return_value=expected): self.assertEqual(expected, self._test_class.count(self.context, validate_filters=False, fake_field='xxx')) # Adding delete_objects mock because some objects are using delete_objects # while calling create(), Port for example @mock.patch.object(obj_db_api, 'delete_objects') def test_create(self, *mocks): with mock.patch.object(obj_db_api, 'create_object', return_value=self.db_objs[0]) as create_mock: with mock.patch.object(obj_db_api, 'get_objects', side_effect=self.fake_get_objects): obj = self._test_class(self.context, **self.obj_fields[0]) self._check_equal(self.objs[0], obj) obj.create() self._check_equal(self.objs[0], obj) create_mock.assert_called_once_with( obj, self.context, self._test_class.modify_fields_to_db( get_obj_persistent_fields(self.objs[0]))) # Adding delete_objects mock because some objects are using delete_objects # while calling create(), Port for example @mock.patch.object(obj_db_api, 'delete_objects') def test_create_updates_from_db_object(self, *mocks): with mock.patch.object(obj_db_api, 'create_object', return_value=self.db_objs[0]): with mock.patch.object(obj_db_api, 'get_objects', side_effect=self.fake_get_objects): self.objs[1].create() self._check_equal(self.objs[0], self.objs[1]) # Adding delete_objects mock because some objects are using delete_objects # while calling create(), Port for example @mock.patch.object(obj_db_api, 'delete_objects') def test_create_duplicates(self, delete_object): with mock.patch.object(obj_db_api, 'create_object', side_effect=obj_exc.DBDuplicateEntry): obj = self._test_class(self.context, **self.obj_fields[0]) self.assertRaises(o_exc.NeutronDbObjectDuplicateEntry, obj.create) def test_update_fields(self): if not self._test_class.primary_keys: self.skipTest( 'Test class %r has no primary keys' % self._test_class) with mock.patch.object(obj_base.VersionedObject, 'obj_reset_changes'): expected = self._test_class(self.context, **self.obj_fields[0]) for key, val in self.obj_fields[1].items(): if key not in expected.fields_no_update: setattr(expected, key, val) observed = self._test_class(self.context, **self.obj_fields[0]) observed.update_fields(self.obj_fields[1], reset_changes=True) self.assertEqual(expected, observed) self.assertTrue(observed.obj_reset_changes.called) with mock.patch.object(obj_base.VersionedObject, 'obj_reset_changes'): obj = self._test_class(self.context, **self.obj_fields[0]) obj.update_fields(self.obj_fields[1]) self.assertFalse(obj.obj_reset_changes.called) def test_extra_fields(self): if not len(self._test_class.obj_extra_fields): self.skipTest( 'Test class %r has no obj_extra_fields' % self._test_class) obj = self._test_class(self.context, **self.obj_fields[0]) for field in self._test_class.obj_extra_fields: # field is accessible and cannot be set by any value getattr(obj, field) self.assertIn(field, obj.to_dict().keys()) self.assertRaises(AttributeError, setattr, obj, field, "1") def test_to_dict_makes_primitive_field_value(self): obj = self._test_class(self.context, **self.obj_fields[0]) dict_ = obj.to_dict() for k, v in dict_.items(): if k not in obj.fields: continue field = obj.fields[k] self.assertEqual(v, field.to_primitive(obj, k, getattr(obj, k))) def test_to_dict_with_unset_project_id(self): if 'project_id' not in self._test_class.fields: self.skipTest( 'Test class %r has no project_id in fields' % self._test_class) obj_data = copy.copy(self.obj_fields[0]) obj_data.pop('project_id') obj = self._test_class(self.context, **obj_data) dict_ = obj.to_dict() self.assertNotIn('project_id', dict_) self.assertNotIn('tenant_id', dict_) def test_fields_no_update(self): obj = self._test_class(self.context, **self.obj_fields[0]) for field in self._test_class.fields_no_update: self.assertTrue(hasattr(obj, field)) def test_get_tenant_id(self): if not hasattr(self._test_class, 'project_id'): self.skipTest( 'Test class %r has no project_id field' % self._test_class) obj = self._test_class(self.context, **self.obj_fields[0]) project_id = self.obj_fields[0]['project_id'] self.assertEqual(project_id, obj.tenant_id) # Adding delete_objects mock because some objects are using delete_objects # while calling update(), Port for example @mock.patch.object(obj_db_api, 'delete_objects') @mock.patch.object(obj_db_api, 'update_object') def test_update_changes(self, update_mock, del_mock): fields_to_update = self.get_updatable_fields( self._test_class.modify_fields_from_db(self.db_objs[0])) if not fields_to_update: self.skipTest('No updatable fields found in test class %r' % self._test_class) with mock.patch.object(base.NeutronDbObject, '_get_changed_persistent_fields', return_value=fields_to_update): with mock.patch.object(obj_db_api, 'get_objects', side_effect=self.fake_get_objects): obj = self._test_class(self.context, **self.obj_fields[0]) # get new values and fix keys update_mock.return_value = self.db_objs[1] fixed_keys = self._test_class.modify_fields_to_db( obj._get_composite_keys()) for key, value in fixed_keys.items(): update_mock.return_value[key] = value obj.update() update_mock.assert_called_once_with( obj, self.context, self._test_class.modify_fields_to_db(fields_to_update), **fixed_keys) @mock.patch.object(base.NeutronDbObject, '_get_changed_persistent_fields', return_value={'a': 'a', 'b': 'b', 'c': 'c'}) def test_update_changes_forbidden(self, *mocks): with mock.patch.object( self._test_class, 'fields_no_update', new_callable=mock.PropertyMock(return_value=['a', 'c']), create=True): obj = self._test_class(self.context, **self.obj_fields[0]) self.assertRaises(o_exc.NeutronObjectUpdateForbidden, obj.update) # Adding delete_objects mock because some objects are using delete_objects # while calling update(), Port and Network for example @mock.patch.object(obj_db_api, 'delete_objects') def test_update_updates_from_db_object(self, *mocks): with mock.patch.object(obj_db_api, 'update_object', return_value=self.db_objs[0]): with mock.patch.object(obj_db_api, 'get_objects', side_effect=self.fake_get_objects): obj = self._test_class(self.context, **self.obj_fields[1]) fields_to_update = self.get_updatable_fields( self.obj_fields[1]) if not fields_to_update: self.skipTest('No updatable fields found in test ' 'class %r' % self._test_class) with mock.patch.object(base.NeutronDbObject, '_get_changed_persistent_fields', return_value=fields_to_update): with mock.patch.object( obj_db_api, 'get_objects', side_effect=self.fake_get_objects): obj.update() self._check_equal(self.objs[0], obj) @mock.patch.object(obj_db_api, 'delete_object') def test_delete(self, delete_mock): obj = self._test_class(self.context, **self.obj_fields[0]) self._check_equal(self.objs[0], obj) obj.delete() self._check_equal(self.objs[0], obj) delete_mock.assert_called_once_with( obj, self.context, **self._test_class.modify_fields_to_db(obj._get_composite_keys())) @mock.patch(OBJECTS_BASE_OBJ_FROM_PRIMITIVE) def test_clean_obj_from_primitive(self, get_prim_m): expected_obj = get_prim_m.return_value observed_obj = self._test_class.clean_obj_from_primitive('foo', 'bar') self.assertIs(expected_obj, observed_obj) self.assertTrue(observed_obj.obj_reset_changes.called) def test_update_primary_key_forbidden_fail(self): obj = self._test_class(self.context, **self.obj_fields[0]) obj.obj_reset_changes() if not self._test_class.primary_keys: self.skipTest( 'All non-updatable fields found in test class %r ' 'are primary keys' % self._test_class) for key, val in self.obj_fields[0].items(): if key in self._test_class.primary_keys: setattr(obj, key, val) self.assertRaises(o_exc.NeutronObjectUpdateForbidden, obj.update) def test_to_dict_synthetic_fields(self): cls_ = self._test_class object_fields = self._get_object_synthetic_fields(cls_) if not object_fields: self.skipTest( 'No object fields found in test class %r' % cls_) for field in object_fields: obj = cls_(self.context, **self.obj_fields[0]) objclass = self._get_ovo_object_class(cls_, field) if not objclass: continue child = objclass( self.context, **objclass.modify_fields_from_db( self.get_random_db_fields(obj_cls=objclass)) ) child_dict = child.to_dict() if (isinstance(cls_.fields[field], obj_fields.ListOfObjectsField) or isinstance(cls_.fields[field], common_types.ListOfObjectsField)): setattr(obj, field, [child]) dict_ = obj.to_dict() self.assertEqual([child_dict], dict_[field]) else: setattr(obj, field, child) dict_ = obj.to_dict() self.assertEqual(child_dict, dict_[field]) def test_get_objects_pager_is_passed_through(self): with mock.patch.object(obj_db_api, 'get_objects') as get_objects: pager = base.Pager() self._test_class.get_objects(self.context, _pager=pager) get_objects.assert_called_once_with( self._test_class, mock.ANY, _pager=pager) class BaseDbObjectNonStandardPrimaryKeyTestCase(BaseObjectIfaceTestCase): _test_class = FakeNeutronObjectNonStandardPrimaryKey class BaseDbObjectCompositePrimaryKeyTestCase(BaseObjectIfaceTestCase): _test_class = FakeNeutronObjectCompositePrimaryKey class BaseDbObjectUniqueKeysTestCase(BaseObjectIfaceTestCase): _test_class = FakeNeutronObjectUniqueKey class UniqueKeysTestCase(test_base.BaseTestCase): def test_class_creation(self): m_get_unique_keys = mock.patch.object(db_utils, 'get_unique_keys') with m_get_unique_keys as get_unique_keys: get_unique_keys.return_value = [['field1'], ['field2', 'db_field3']] @base.NeutronObjectRegistry.register_if(False) class UniqueKeysTestObject(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel primary_keys = ['id'] fields = { 'id': common_types.UUIDField(), 'field1': common_types.UUIDField(), 'field2': common_types.UUIDField(), 'field3': common_types.UUIDField(), } fields_need_translation = {'field3': 'db_field3'} expected = {('field1',), ('field2', 'field3')} observed = {tuple(sorted(key)) for key in UniqueKeysTestObject.unique_keys} self.assertEqual(expected, observed) class NeutronObjectCountTestCase(test_base.BaseTestCase): def test_count(self): expected = 10 self.assertEqual( expected, FakeNeutronObject.count(None, count=expected)) class BaseDbObjectCompositePrimaryKeyWithIdTestCase(BaseObjectIfaceTestCase): _test_class = FakeNeutronObjectCompositePrimaryKeyWithId class BaseDbObjectRenamedFieldTestCase(BaseObjectIfaceTestCase): _test_class = FakeNeutronObjectRenamedField class BaseObjectIfaceWithProjectIdTestCase(BaseObjectIfaceTestCase): _test_class = FakeNeutronObjectWithProjectId def test_update_fields_using_tenant_id(self): obj = self._test_class(self.context, **self.obj_fields[0]) obj.obj_reset_changes() tenant_id = obj['tenant_id'] new_obj_fields = dict() new_obj_fields['tenant_id'] = uuidutils.generate_uuid() new_obj_fields['field2'] = uuidutils.generate_uuid() obj.update_fields(new_obj_fields) self.assertEqual(set(['field2']), obj.obj_what_changed()) self.assertEqual(tenant_id, obj.project_id) def test_tenant_id_filter_added_when_project_id_present(self): self._test_class.get_objects( self.context, tenant_id=self.obj_fields[0]['project_id']) class BaseDbObjectMultipleForeignKeysTestCase(_BaseObjectTestCase, test_base.BaseTestCase): _test_class = FakeNeutronObjectSyntheticField def test_load_synthetic_db_fields_with_multiple_foreign_keys(self): obj = self._test_class(self.context, **self.obj_fields[0]) self.assertRaises(o_exc.NeutronSyntheticFieldMultipleForeignKeys, obj.load_synthetic_db_fields) class BaseDbObjectForeignKeysNotFoundTestCase(_BaseObjectTestCase, test_base.BaseTestCase): _test_class = FakeNeutronObjectSyntheticField2 def test_load_foreign_keys_not_belong_class(self): obj = self._test_class(self.context, **self.obj_fields[0]) self.assertRaises(o_exc.NeutronSyntheticFieldsForeignKeysNotFound, obj.load_synthetic_db_fields) class BaseDbObjectMultipleParentsForForeignKeysTestCase( _BaseObjectTestCase, test_base.BaseTestCase): _test_class = FakeParent def test_load_synthetic_db_fields_with_multiple_parents(self): child_cls = FakeSmallNeutronObjectWithMultipleParents self.obj_registry.register(child_cls) self.obj_registry.register(FakeParent) obj = self._test_class(self.context, **self.obj_fields[0]) fake_children = [ child_cls( self.context, **child_cls.modify_fields_from_db( self.get_random_db_fields(obj_cls=child_cls)) ) for _ in range(5) ] with mock.patch.object(child_cls, 'get_objects', return_value=fake_children) as get_objects: obj.load_synthetic_db_fields() get_objects.assert_called_once_with(self.context, field1=obj.id) self.assertEqual(fake_children, obj.children) class BaseObjectIfaceDictMiscValuesTestCase(_BaseObjectTestCase, test_base.BaseTestCase): _test_class = FakeNeutronObjectDictOfMiscValues def test_dict_of_misc_values(self): obj_id = uuidutils.generate_uuid() float_value = 1.23 misc_list = [True, float_value] obj_dict = { 'bool': True, 'float': float_value, 'misc_list': misc_list } obj = self._test_class(self.context, id=obj_id, dict_field=obj_dict) self.assertTrue(obj.dict_field['bool']) self.assertEqual(float_value, obj.dict_field['float']) self.assertEqual(misc_list, obj.dict_field['misc_list']) class BaseObjectIfaceListDictMiscValuesTestCase(_BaseObjectTestCase, test_base.BaseTestCase): _test_class = FakeNeutronObjectListOfDictOfMiscValues def test_list_of_dict_of_misc_values(self): obj_id = uuidutils.generate_uuid() float_value = 1.23 misc_list = [True, float_value] obj_dict = { 'bool': True, 'float': float_value, 'misc_list': misc_list } obj = self._test_class( self.context, id=obj_id, list_of_dicts_field=[obj_dict]) self.assertEqual(1, len(obj.list_of_dicts_field)) self.assertTrue(obj.list_of_dicts_field[0]['bool']) self.assertEqual(float_value, obj.list_of_dicts_field[0]['float']) self.assertEqual(misc_list, obj.list_of_dicts_field[0]['misc_list']) class BaseDbObjectTestCase(_BaseObjectTestCase, test_db_base_plugin_v2.DbOperationBoundMixin): def setUp(self): super(BaseDbObjectTestCase, self).setUp() synthetic_fields = self._get_object_synthetic_fields(self._test_class) for synth_field in synthetic_fields: objclass = self._get_ovo_object_class(self._test_class, synth_field) if not objclass: continue for db_obj in self.db_objs: objclass_fields = self.get_random_db_fields(objclass) if isinstance(self._test_class.fields[synth_field], obj_fields.ObjectField): db_obj[synth_field] = objclass.db_model(**objclass_fields) else: db_obj[synth_field] = [ objclass.db_model(**objclass_fields) ] def _create_test_network(self, name='test-network1', network_id=None): network_id = (uuidutils.generate_uuid() if network_id is None else network_id) _network = net_obj.Network(self.context, name=name, id=network_id) _network.create() return _network def _create_test_network_id(self): return self._create_test_network( "test-network-%s" % helpers.get_random_string(4)).id def _create_external_network_id(self): test_network_id = self._create_test_network_id() ext_net = net_obj.ExternalNetwork(self.context, network_id=test_network_id) ext_net.create() return ext_net.network_id def _create_test_fip_id(self, fip_id=None): fake_fip = '172.23.3.0' ext_net_id = self._create_external_network_id() values = { 'floating_ip_address': netaddr.IPAddress(fake_fip), 'floating_network_id': ext_net_id, 'floating_port_id': self._create_test_port_id( network_id=ext_net_id) } if fip_id: values['id'] = fip_id fip_obj = router.FloatingIP(self.context, **values) fip_obj.create() return fip_obj.id def _create_test_subnet_id(self, network_id=None): if not network_id: network_id = self._create_test_network_id() test_subnet = { 'project_id': uuidutils.generate_uuid(), 'name': 'test-subnet1', 'network_id': network_id, 'ip_version': 4, 'cidr': netaddr.IPNetwork('10.0.0.0/24'), 'gateway_ip': '10.0.0.1', 'enable_dhcp': 1, 'ipv6_ra_mode': None, 'ipv6_address_mode': None } subnet_obj = subnet.Subnet(self.context, **test_subnet) subnet_obj.create() return subnet_obj.id def _create_test_port_id(self, **port_attrs): return self._create_test_port(**port_attrs)['id'] def _create_test_port(self, **port_attrs): if 'network_id' not in port_attrs: port_attrs['network_id'] = self._create_test_network_id() if not hasattr(self, '_mac_address_generator'): self._mac_address_generator = ( netaddr.EUI(":".join(["%02x" % i] * 6)) for i in itertools.count() ) if not hasattr(self, '_port_name_generator'): self._port_name_generator = ("test-port%d" % i for i in itertools.count(1)) attrs = {'project_id': uuidutils.generate_uuid(), 'admin_state_up': True, 'status': 'ACTIVE', 'device_id': 'fake_device', 'device_owner': 'fake_owner'} attrs.update(port_attrs) if 'name' not in attrs: attrs['name'] = next(self._port_name_generator) if 'mac_address' not in attrs: attrs['mac_address'] = next(self._mac_address_generator) port = ports.Port(self.context, **attrs) port.create() return port def _create_test_segment_id(self, network_id=None): attr = self.get_random_object_fields(net_obj.NetworkSegment) attr['network_id'] = network_id or self._create_test_network_id() segment = net_obj.NetworkSegment(self.context, **attr) segment.create() return segment.id def _create_test_router_id(self): attrs = { 'name': 'test_router', } self._router = router.Router(self.context, **attrs) self._router.create() return self._router['id'] def _create_test_security_group_id(self): sg_fields = self.get_random_object_fields(securitygroup.SecurityGroup) _securitygroup = securitygroup.SecurityGroup( self.context, **sg_fields) _securitygroup.create() return _securitygroup.id def _create_test_agent_id(self): attrs = self.get_random_object_fields(obj_cls=agent.Agent) _agent = agent.Agent(self.context, **attrs) _agent.create() return _agent['id'] def _create_test_standard_attribute_id(self): attrs = { 'resource_type': helpers.get_random_string(4), 'revision_number': tools.get_random_integer() } return obj_db_api.create_object( stdattrs.StandardAttribute, self.context, attrs, populate_id=False)['id'] def _create_test_flavor_id(self): attrs = self.get_random_object_fields(obj_cls=flavor.Flavor) flavor_obj = flavor.Flavor(self.context, **attrs) flavor_obj.create() return flavor_obj.id def _create_test_service_profile_id(self): attrs = self.get_random_object_fields(obj_cls=flavor.ServiceProfile) service_profile_obj = flavor.ServiceProfile(self.context, **attrs) service_profile_obj.create() return service_profile_obj.id def _create_test_qos_policy(self, **qos_policy_attrs): _qos_policy = qos_policy.QosPolicy(self.context, **qos_policy_attrs) _qos_policy.create() return _qos_policy def test_get_standard_attr_id(self): if not self._test_class.has_standard_attributes(): self.skipTest( 'No standard attributes found in test class %r' % self._test_class) obj = self._make_object(self.obj_fields[0]) obj.create() model = self.context.session.query(obj.db_model).filter_by( **obj._get_composite_keys()).one() retrieved_obj = self._test_class.get_object( self.context, **obj._get_composite_keys()) self.assertIsNotNone(retrieved_obj.standard_attr_id) self.assertEqual( model.standard_attr_id, retrieved_obj.standard_attr_id) def _make_object(self, fields): fields = get_non_synthetic_fields(self._test_class, fields) return self._test_class(self.context, **remove_timestamps_from_fields( fields, self._test_class.fields)) def test_downgrade_to_1_0(self): for obj in self.objs: try: obj.obj_to_primitive(target_version='1.0') except exception.IncompatibleObjectVersion: # the only exception we should allow is IncompatibleVersion pass def test_get_object_create_update_delete(self): # Timestamps can't be initialized and multiple objects may use standard # attributes so we need to remove timestamps when creating objects obj = self._make_object(self.obj_fields[0]) obj.create() new = self._test_class.get_object(self.context, **obj._get_composite_keys()) self.assertEqual(obj, new) obj = new for key, val in self.get_updatable_fields(self.obj_fields[1]).items(): setattr(obj, key, val) obj.update() new = self._test_class.get_object(self.context, **obj._get_composite_keys()) self.assertEqual(obj, new) obj = new new.delete() new = self._test_class.get_object(self.context, **obj._get_composite_keys()) self.assertIsNone(new) def test_update_non_existent_object_raises_not_found(self): obj = self._make_object(self.obj_fields[0]) obj.obj_reset_changes() fields_to_update = self.get_updatable_fields(self.obj_fields[0]) if not fields_to_update: self.skipTest('No updatable fields found in test class %r' % self._test_class) for key, val in fields_to_update.items(): setattr(obj, key, val) self.assertRaises(n_exc.ObjectNotFound, obj.update) def test_delete_non_existent_object_raises_not_found(self): obj = self._make_object(self.obj_fields[0]) self.assertRaises(n_exc.ObjectNotFound, obj.delete) @mock.patch(SQLALCHEMY_COMMIT) def test_create_single_transaction(self, mock_commit): obj = self._make_object(self.obj_fields[0]) obj.create() self.assertEqual(1, mock_commit.call_count) def test_update_single_transaction(self): obj = self._make_object(self.obj_fields[0]) obj.create() fields_to_update = self.get_updatable_fields(self.obj_fields[1]) if not fields_to_update: self.skipTest('No updatable fields found in test class %r' % self._test_class) for key, val in fields_to_update.items(): setattr(obj, key, val) with mock.patch(SQLALCHEMY_COMMIT) as mock_commit: obj.update() self.assertEqual(1, mock_commit.call_count) def test_delete_single_transaction(self): obj = self._make_object(self.obj_fields[0]) obj.create() with mock.patch(SQLALCHEMY_COMMIT) as mock_commit: obj.delete() self.assertEqual(1, mock_commit.call_count) def _get_ro_txn_exit_func_name(self): # for old engine facade, we didn't have distinction between r/o and r/w # transactions and so we always call commit even for getters when the # old facade is used return ( SQLALCHEMY_CLOSE if self._test_class.new_facade else SQLALCHEMY_COMMIT) def test_get_objects_single_transaction(self): with mock.patch(self._get_ro_txn_exit_func_name()) as mock_exit: self._test_class.get_objects(self.context) self.assertEqual(1, mock_exit.call_count) def test_get_object_single_transaction(self): obj = self._make_object(self.obj_fields[0]) obj.create() with mock.patch(self._get_ro_txn_exit_func_name()) as mock_exit: obj = self._test_class.get_object(self.context, **obj._get_composite_keys()) self.assertEqual(1, mock_exit.call_count) def test_get_objects_supports_extra_filtername(self): self.filtered_args = None def foo_filter(query, filters): self.filtered_args = filters return query self.obj_registry.register(self._test_class) model_query.register_hook( self._test_class.db_model, 'foo_filter', query_hook=None, filter_hook=None, result_filters=foo_filter) base.register_filter_hook_on_model(self._test_class.db_model, 'foo') self._test_class.get_objects(self.context, foo=42) self.assertEqual({'foo': [42]}, self.filtered_args) def test_filtering_by_fields(self): obj = self._make_object(self.obj_fields[0]) obj.create() for field in get_obj_persistent_fields(obj): if not isinstance(obj[field], list): filters = {field: [obj[field]]} else: filters = {field: obj[field]} new = self._test_class.get_objects(self.context, **filters) self.assertItemsEqual( [obj._get_composite_keys()], [obj_._get_composite_keys() for obj_ in new], 'Filtering by %s failed.' % field) def _get_non_synth_fields(self, objclass, db_attrs): fields = objclass.modify_fields_from_db(db_attrs) fields = remove_timestamps_from_fields(fields, objclass.fields) fields = get_non_synthetic_fields(objclass, fields) return fields def _create_object_with_synthetic_fields(self, db_obj): cls_ = self._test_class object_fields = self._get_object_synthetic_fields(cls_) # create base object obj = cls_(self.context, **self._get_non_synth_fields(cls_, db_obj)) obj.create() # create objects that are going to be loaded into the base object # through synthetic fields for field in object_fields: objclass = self._get_ovo_object_class(cls_, field) if not objclass: continue # check that the stored database model does not have non-empty # relationships dbattr = obj.fields_need_translation.get(field, field) # Skipping empty relationships for the following reasons: # 1) db_obj have the related object loaded - In this case we do not # have to create the related objects and the loop can continue. # 2) when the related objects are not loaded - In this # case they need to be created because of the foreign key # relationships. But we still need to check whether the # relationships are loaded or not. That is achieved by the # assertTrue statement after retrieving the dbattr in # this method. if getattr(obj.db_obj, dbattr, None): continue if isinstance(cls_.fields[field], obj_fields.ObjectField): objclass_fields = self._get_non_synth_fields(objclass, db_obj[field]) else: objclass_fields = self._get_non_synth_fields(objclass, db_obj[field][0]) # make sure children point to the base object foreign_keys = objclass.foreign_keys.get(obj.__class__.__name__) for local_field, foreign_key in foreign_keys.items(): objclass_fields[local_field] = obj.get(foreign_key) synth_field_obj = objclass(self.context, **objclass_fields) synth_field_obj.create() # reload the parent object under test obj = cls_.get_object(self.context, **obj._get_composite_keys()) # check that the stored database model now has filled relationships dbattr = obj.fields_need_translation.get(field, field) self.assertTrue(getattr(obj.db_obj, dbattr, None)) # reset the object so that we can compare it to other clean objects obj.obj_reset_changes([field]) return obj def _test_get_with_synthetic_fields(self, getter): object_fields = self._get_object_synthetic_fields(self._test_class) if not object_fields: self.skipTest( 'No synthetic object fields found ' 'in test class %r' % self._test_class ) obj = self._create_object_with_synthetic_fields(self.db_objs[0]) listed_obj = getter(self.context, **obj._get_composite_keys()) self.assertTrue(listed_obj) self.assertEqual(obj, listed_obj) def test_get_object_with_synthetic_fields(self): self._test_get_with_synthetic_fields(self._test_class.get_object) def test_get_objects_with_synthetic_fields(self): def getter(*args, **kwargs): objs = self._test_class.get_objects(*args, **kwargs) self.assertEqual(1, len(objs)) return objs[0] self._test_get_with_synthetic_fields(getter) # NOTE(korzen) _list method is used in neutron.tests.db.unit.db. # test_db_base_plugin_v2.DbOperationBoundMixin in _list_and_count_queries() # This is used in test_subnet for asserting that number of queries is # constant. It can be used also for port and network objects when ready. def _list(self, resource, neutron_context): cls_ = resource return cls_.get_objects(neutron_context) @test_base.unstable_test("bug 1775220") def test_get_objects_queries_constant(self): iter_db_obj = iter(self.db_objs) def _create(): return self._create_object_with_synthetic_fields(next(iter_db_obj)) self._assert_object_list_queries_constant(_create, self._test_class) def test_count(self): for fields in self.obj_fields: self._make_object(fields).create() self.assertEqual( len(self.obj_fields), self._test_class.count(self.context)) def test_count_validate_filters_false(self): for fields in self.obj_fields: self._make_object(fields).create() self.assertEqual( len(self.obj_fields), self._test_class.count(self.context, validate_filters=False, fake_filter='xxx')) def test_count_invalid_filters(self): for fields in self.obj_fields: self._make_object(fields).create() self.assertRaises(n_exc.InvalidInput, self._test_class.count, self.context, fake_field='xxx') def test_objects_exist(self): for fields in self.obj_fields: self._make_object(fields).create() self.assertTrue(self._test_class.objects_exist(self.context)) def test_objects_exist_false(self): self.assertFalse(self._test_class.objects_exist(self.context)) def test_objects_exist_validate_filters(self): self.assertRaises(n_exc.InvalidInput, self._test_class.objects_exist, self.context, fake_field='xxx') def test_objects_exist_validate_filters_false(self): for fields in self.obj_fields: self._make_object(fields).create() self.assertTrue(self._test_class.objects_exist( self.context, validate_filters=False, fake_filter='xxx')) def test_update_object(self): fields_to_update = self.get_updatable_fields( self.obj_fields[1]) if not fields_to_update: self.skipTest('No updatable fields found in test ' 'class %r' % self._test_class) for fields in self.obj_fields: self._make_object(fields).create() obj = self._test_class.get_objects( self.context, **self.valid_field_filter) for k, v in self.valid_field_filter.items(): self.assertEqual(v, obj[0][k]) new_values = self._get_random_update_fields() keys = self.objs[0]._get_composite_keys() updated_obj = self._test_class.update_object( self.context, new_values, **keys) # Check the correctness of the updated object for k, v in new_values.items(): self.assertEqual(v, updated_obj[k]) def test_update_objects(self): fields_to_update = self.get_updatable_fields( self.obj_fields[1]) if not fields_to_update: self.skipTest('No updatable fields found in test ' 'class %r' % self._test_class) for fields in self.obj_fields: self._make_object(fields).create() objs = self._test_class.get_objects( self.context, **self.valid_field_filter) for k, v in self.valid_field_filter.items(): self.assertEqual(v, objs[0][k]) count = self._test_class.update_objects( self.context, {}, **self.valid_field_filter) # we haven't updated anything, but got the number of matching records self.assertEqual(len(objs), count) # and the request hasn't changed the number of matching records new_objs = self._test_class.get_objects( self.context, **self.valid_field_filter) self.assertEqual(len(objs), len(new_objs)) # now update an object with new values new_values = self._get_random_update_fields() keys = self.objs[0]._get_composite_keys() count_updated = self._test_class.update_objects( self.context, new_values, **keys) self.assertEqual(1, count_updated) new_filter = keys.copy() new_filter.update(new_values) # check that we can fetch using new values new_objs = self._test_class.get_objects( self.context, **new_filter) self.assertEqual(1, len(new_objs)) def test_update_objects_nothing_to_update(self): fields_to_update = self.get_updatable_fields( self.obj_fields[1]) if not fields_to_update: self.skipTest('No updatable fields found in test ' 'class %r' % self._test_class) self.assertEqual( 0, self._test_class.update_objects(self.context, {})) def test_delete_objects(self): for fields in self.obj_fields: self._make_object(fields).create() objs = self._test_class.get_objects( self.context, **self.valid_field_filter) for k, v in self.valid_field_filter.items(): self.assertEqual(v, objs[0][k]) count = self._test_class.delete_objects( self.context, **self.valid_field_filter) self.assertEqual(len(objs), count) new_objs = self._test_class.get_objects(self.context) self.assertEqual(len(self.obj_fields) - len(objs), len(new_objs)) for obj in new_objs: for k, v in self.valid_field_filter.items(): self.assertNotEqual(v, obj[k]) def test_delete_objects_nothing_to_delete(self): self.assertEqual( 0, self._test_class.delete_objects(self.context)) def test_db_obj(self): obj = self._make_object(self.obj_fields[0]) self.assertIsNone(obj.db_obj) obj.create() self.assertIsNotNone(obj.db_obj) fields_to_update = self.get_updatable_fields(self.obj_fields[1]) if fields_to_update: old_fields = {} for key, val in fields_to_update.items(): db_model_attr = ( obj.fields_need_translation.get(key, key)) old_fields[db_model_attr] = obj.db_obj[db_model_attr] setattr(obj, key, val) obj.update() self.assertIsNotNone(obj.db_obj) for k, v in obj.modify_fields_to_db(fields_to_update).items(): if isinstance(obj.db_obj[k], orm.dynamic.AppenderQuery): self.assertIsInstance(v, list) else: self.assertEqual(v, obj.db_obj[k], '%s attribute differs' % k) obj.delete() self.assertIsNone(obj.db_obj) class UniqueObjectBase(test_base.BaseTestCase): def setUp(self): super(UniqueObjectBase, self).setUp() obj_registry = self.useFixture( NeutronObjectRegistryFixture()) self.db_model = FakeModel class RegisteredObject(base.NeutronDbObject): db_model = self.db_model self.registered_object = RegisteredObject obj_registry.register(self.registered_object) class GetObjectClassByModelTestCase(UniqueObjectBase): def setUp(self): super(GetObjectClassByModelTestCase, self).setUp() self.not_registered_object = FakeSmallNeutronObject def test_object_found_by_model(self): found_obj = base.get_object_class_by_model( self.registered_object.db_model) self.assertIs(self.registered_object, found_obj) def test_not_registed_object_raises_exception(self): with testtools.ExpectedException(o_exc.NeutronDbObjectNotFoundByModel): base.get_object_class_by_model(self.not_registered_object.db_model) class RegisterFilterHookOnModelTestCase(UniqueObjectBase): def test_filtername_is_added(self): filter_name = 'foo' self.assertNotIn( filter_name, self.registered_object.extra_filter_names) base.register_filter_hook_on_model( FakeNeutronDbObject.db_model, filter_name) self.assertIn(filter_name, self.registered_object.extra_filter_names) class PagerTestCase(test_base.BaseTestCase): def test_comparison(self): pager = base.Pager(sorts=[('order', True)]) pager2 = base.Pager(sorts=[('order', True)]) self.assertEqual(pager, pager2) pager3 = base.Pager() self.assertNotEqual(pager, pager3) class OperationOnStringAndJsonTestCase(test_base.BaseTestCase): def test_load_empty_string_to_json(self): for field_val in ['', None]: for default_val in [None, {}]: res = base.NeutronDbObject.load_json_from_str(field_val, default_val) self.assertEqual(res, default_val) def test_dump_field_to_string(self): for field_val in [{}, None]: for default_val in ['', None]: res = base.NeutronDbObject.filter_to_json_str(field_val, default_val) self.assertEqual(default_val, res) class NeutronObjectValidatorTestCase(test_base.BaseTestCase): def test_load_wrong_synthetic_fields(self): try: @obj_base.VersionedObjectRegistry.register_if(False) class FakeNeutronObjectSyntheticFieldWrong(base.NeutronDbObject): # Version 1.0: Initial version VERSION = '1.0' db_model = FakeModel fields = { 'id': common_types.UUIDField(), 'obj_field': common_types.UUIDField() } synthetic_fields = ['obj_field', 'wrong_synthetic_field_name'] except o_exc.NeutronObjectValidatorException as exc: self.assertIn('wrong_synthetic_field_name', str(exc)) neutron-12.1.1/neutron/tests/unit/objects/test_ipam.py0000664000175000017500000000510013553660047023120 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import ipam from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class IpamSubnetObjectIfaceTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = ipam.IpamSubnet class IpamSubnetDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = ipam.IpamSubnet def setUp(self): super(IpamSubnetDbObjectTestCase, self).setUp() self.update_obj_fields( {'neutron_subnet_id': lambda: self._create_test_subnet_id()}) class IpamAllocationPoolObjectIfaceTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = ipam.IpamAllocationPool class IpamAllocationPoolDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = ipam.IpamAllocationPool def setUp(self): super(IpamAllocationPoolDbObjectTestCase, self).setUp() self._create_test_ipam_subnet() self.update_obj_fields({'ipam_subnet_id': self._ipam_subnet['id']}) def _create_test_ipam_subnet(self): attrs = self.get_random_object_fields(obj_cls=ipam.IpamSubnet) self._ipam_subnet = ipam.IpamSubnet(self.context, **attrs) self._ipam_subnet.create() class IpamAllocationObjectIfaceTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = ipam.IpamAllocation class IpamAllocationDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = ipam.IpamAllocation def setUp(self): super(IpamAllocationDbObjectTestCase, self).setUp() self._create_test_ipam_subnet() self.update_obj_fields({'ipam_subnet_id': self._ipam_subnet['id']}) def _create_test_ipam_subnet(self): attrs = self.get_random_object_fields(obj_cls=ipam.IpamSubnet) self._ipam_subnet = ipam.IpamSubnet(self.context, **attrs) self._ipam_subnet.create() neutron-12.1.1/neutron/tests/unit/objects/extensions/0000775000175000017500000000000013553660157022766 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/extensions/__init__.py0000664000175000017500000000000013553660047025063 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/extensions/test_standardattributes.py0000664000175000017500000000346013553660047030307 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base from oslo_versionedobjects import fields as obj_fields import sqlalchemy as sa from neutron.db import standard_attr from neutron.objects import base as objects_base from neutron.objects import common_types from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api class FakeDbModelWithStandardAttributes( standard_attr.HasStandardAttributes, model_base.BASEV2): id = sa.Column(sa.String(36), primary_key=True, nullable=False) item = sa.Column(sa.String(64)) api_collections = [] collection_resource_map = {} tag_support = False @objects_base.NeutronObjectRegistry.register_if(False) class FakeObjectWithStandardAttributes(objects_base.NeutronDbObject): VERSION = '1.0' db_model = FakeDbModelWithStandardAttributes fields = { 'id': common_types.UUIDField(), 'item': obj_fields.StringField(), } class HasStandardAttributesDbTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = FakeObjectWithStandardAttributes class HasStandardAttributesTestCase(test_base.BaseObjectIfaceTestCase): _test_class = FakeObjectWithStandardAttributes neutron-12.1.1/neutron/tests/unit/objects/test_utils.py0000664000175000017500000000337413553660047023345 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.common import exceptions from neutron.objects import utils from neutron.tests import base as test_base class TestConvertFilters(test_base.BaseTestCase): def test_convert_filters_no_tenant_id(self): kwargs = { 'filter%d' % i: 'value%d' % i for i in range(0, 10) } self.assertEqual(kwargs, utils.convert_filters(**kwargs)) def test_convert_filters_tenant_id(self): expected_project_id = 'fake-tenant-id' kwargs = { 'filter%d' % i: 'value%d' % i for i in range(0, 10) } expected = kwargs.copy() expected['project_id'] = expected_project_id self.assertEqual( expected, utils.convert_filters(tenant_id=expected_project_id, **kwargs) ) def test_convert_filters_tenant_id_and_project_id_raises(self): kwargs = { 'filter%d' % i: 'value%d' % i for i in range(0, 10) } kwargs['tenant_id'] = 'fake-tenant-id' kwargs['project_id'] = 'fake-tenant-id' self.assertRaises( exceptions.TenantIdProjectIdFilterConflict, utils.convert_filters, **kwargs ) neutron-12.1.1/neutron/tests/unit/objects/qos/0000775000175000017500000000000013553660157021371 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/qos/test_policy.py0000664000175000017500000005534513553660047024313 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as n_const from neutron_lib.services.qos import constants as qos_consts from oslo_utils import uuidutils from oslo_versionedobjects import exception import testtools from neutron.common import exceptions as n_exc from neutron.objects.db import api as db_api from neutron.objects import network as net_obj from neutron.objects import ports as port_obj from neutron.objects.qos import binding from neutron.objects.qos import policy from neutron.objects.qos import rule from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api RULE_OBJ_CLS = { qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: rule.QosBandwidthLimitRule, qos_consts.RULE_TYPE_DSCP_MARKING: rule.QosDscpMarkingRule, qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: rule.QosMinimumBandwidthRule, } # TODO(ihrachys): add tests for QosPolicyRBAC class QosPolicyObjectTestCase(test_base.BaseObjectIfaceTestCase): _test_class = policy.QosPolicy def setUp(self): super(QosPolicyObjectTestCase, self).setUp() mock.patch.object(policy.QosPolicy, 'get_default').start() # qos_policy_ids will be incorrect, but we don't care in this test self.db_qos_bandwidth_rules = [ self.get_random_db_fields(rule.QosBandwidthLimitRule) for _ in range(3)] self.db_qos_dscp_rules = [ self.get_random_db_fields(rule.QosDscpMarkingRule) for _ in range(3)] self.db_qos_minimum_bandwidth_rules = [ self.get_random_db_fields(rule.QosMinimumBandwidthRule) for _ in range(3)] self.model_map.update({ self._test_class.db_model: self.db_objs, binding.QosPolicyPortBinding.db_model: [], binding.QosPolicyNetworkBinding.db_model: [], rule.QosBandwidthLimitRule.db_model: self.db_qos_bandwidth_rules, rule.QosDscpMarkingRule.db_model: self.db_qos_dscp_rules, rule.QosMinimumBandwidthRule.db_model: self.db_qos_minimum_bandwidth_rules}) # TODO(ihrachys): stop overriding those test cases, instead base test cases # should be expanded if there are missing bits there to support QoS objects def test_get_objects(self): admin_context = self.context.elevated() with mock.patch.object(self.context, 'elevated', return_value=admin_context) as context_mock: objs = self._test_class.get_objects(self.context) context_mock.assert_called_once_with() self.get_objects_mock.assert_any_call( self._test_class, admin_context, _pager=None) self.assertItemsEqual( [test_base.get_obj_persistent_fields(obj) for obj in self.objs], [test_base.get_obj_persistent_fields(obj) for obj in objs]) def test_get_objects_valid_fields(self): admin_context = self.context.elevated() with mock.patch.object( db_api, 'get_objects', return_value=[self.db_objs[0]]) as get_objects_mock: with mock.patch.object( self.context, 'elevated', return_value=admin_context) as context_mock: objs = self._test_class.get_objects( self.context, **self.valid_field_filter) context_mock.assert_called_once_with() get_objects_mock.assert_any_call( self._test_class, admin_context, _pager=None, **self.valid_field_filter) self._check_equal(self.objs[0], objs[0]) def test_get_object(self): admin_context = self.context.elevated() with mock.patch.object(db_api, 'get_object', return_value=self.db_objs[0]) as get_object_mock, \ mock.patch.object(self.context, 'elevated', return_value=admin_context) as context_mock: obj = self._test_class.get_object(self.context, id='fake_id') self.assertTrue(self._is_test_class(obj)) self._check_equal(self.objs[0], obj) context_mock.assert_called_once_with() get_object_mock.assert_called_once_with( self._test_class, admin_context, id='fake_id') def test_to_dict_makes_primitive_field_value(self): # is_shared_with_tenant requires DB with mock.patch.object(self._test_class, 'is_shared_with_tenant', return_value=False): (super(QosPolicyObjectTestCase, self). test_to_dict_makes_primitive_field_value()) class QosPolicyDbObjectTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = policy.QosPolicy def setUp(self): super(QosPolicyDbObjectTestCase, self).setUp() self._network_id = self._create_test_network_id() self._port = self._create_test_port(network_id=self._network_id) def _create_test_policy(self): self.objs[0].create() return self.objs[0] def _create_test_policy_with_rules(self, rule_type, reload_rules=False, bwlimit_direction=None): policy_obj = self._create_test_policy() rules = [] for obj_cls in (RULE_OBJ_CLS.get(rule_type) for rule_type in rule_type): rule_fields = self.get_random_object_fields(obj_cls=obj_cls) rule_fields['qos_policy_id'] = policy_obj.id if (obj_cls.rule_type == qos_consts.RULE_TYPE_BANDWIDTH_LIMIT and bwlimit_direction is not None): rule_fields['direction'] = bwlimit_direction rule_obj = obj_cls(self.context, **rule_fields) rule_obj.create() rules.append(rule_obj) if reload_rules: policy_obj.obj_load_attr('rules') return policy_obj, rules def test_attach_network_get_network_policy(self): obj = self._create_test_policy() policy_obj = policy.QosPolicy.get_network_policy(self.context, self._network_id) self.assertIsNone(policy_obj) # Now attach policy and repeat obj.attach_network(self._network_id) policy_obj = policy.QosPolicy.get_network_policy(self.context, self._network_id) self.assertEqual(obj, policy_obj) def test_attach_network_nonexistent_network(self): obj = self._create_test_policy() self.assertRaises(n_exc.NetworkQosBindingError, obj.attach_network, uuidutils.generate_uuid()) def test_attach_network_get_policy_network(self): obj = self._create_test_policy() obj.attach_network(self._network_id) networks = obj.get_bound_networks() self.assertEqual(1, len(networks)) self.assertEqual(self._network_id, networks[0]) def test_attach_and_get_multiple_policy_networks(self): net1_id = self._network_id net2 = net_obj.Network(self.context, name='test-network2') net2.create() net2_id = net2['id'] obj = self._create_test_policy() obj.attach_network(net1_id) obj.attach_network(net2_id) networks = obj.get_bound_networks() self.assertEqual(2, len(networks)) self.assertIn(net1_id, networks) self.assertIn(net2_id, networks) def test_attach_port_nonexistent_port(self): obj = self._create_test_policy() self.assertRaises(n_exc.PortQosBindingError, obj.attach_port, uuidutils.generate_uuid()) def test_attach_network_nonexistent_policy(self): policy_obj = self._make_object(self.obj_fields[0]) self.assertRaises(n_exc.NetworkQosBindingError, policy_obj.attach_network, self._network_id) def test_attach_port_nonexistent_policy(self): policy_obj = self._make_object(self.obj_fields[0]) self.assertRaises(n_exc.PortQosBindingError, policy_obj.attach_port, self._port['id']) def test_attach_port_get_port_policy(self): obj = self._create_test_policy() policy_obj = policy.QosPolicy.get_network_policy(self.context, self._network_id) self.assertIsNone(policy_obj) # Now attach policy and repeat obj.attach_port(self._port['id']) policy_obj = policy.QosPolicy.get_port_policy(self.context, self._port['id']) self.assertEqual(obj, policy_obj) def test_attach_and_get_multiple_policy_ports(self): port1_id = self._port['id'] port2 = db_api.create_object(port_obj.Port, self.context, {'tenant_id': 'fake_tenant_id', 'name': 'test-port2', 'network_id': self._network_id, 'mac_address': 'fake_mac2', 'admin_state_up': True, 'status': 'ACTIVE', 'device_id': 'fake_device', 'device_owner': 'fake_owner'}) port2_id = port2['id'] obj = self._create_test_policy() obj.attach_port(port1_id) obj.attach_port(port2_id) ports = obj.get_bound_ports() self.assertEqual(2, len(ports)) self.assertIn(port1_id, ports) self.assertIn(port2_id, ports) def test_attach_port_get_policy_port(self): obj = self._create_test_policy() obj.attach_port(self._port['id']) ports = obj.get_bound_ports() self.assertEqual(1, len(ports)) self.assertEqual(self._port['id'], ports[0]) def test_detach_port(self): obj = self._create_test_policy() obj.attach_port(self._port['id']) obj.detach_port(self._port['id']) policy_obj = policy.QosPolicy.get_port_policy(self.context, self._port['id']) self.assertIsNone(policy_obj) def test_detach_network(self): obj = self._create_test_policy() obj.attach_network(self._network_id) obj.detach_network(self._network_id) policy_obj = policy.QosPolicy.get_network_policy(self.context, self._network_id) self.assertIsNone(policy_obj) def test_detach_port_nonexistent_port(self): obj = self._create_test_policy() self.assertRaises(n_exc.PortQosBindingNotFound, obj.detach_port, 'non-existent-port') def test_detach_network_nonexistent_network(self): obj = self._create_test_policy() self.assertRaises(n_exc.NetworkQosBindingNotFound, obj.detach_network, 'non-existent-port') def test_detach_port_nonexistent_policy(self): policy_obj = self._make_object(self.obj_fields[0]) self.assertRaises(n_exc.PortQosBindingNotFound, policy_obj.detach_port, self._port['id']) def test_detach_network_nonexistent_policy(self): policy_obj = self._make_object(self.obj_fields[0]) self.assertRaises(n_exc.NetworkQosBindingNotFound, policy_obj.detach_network, self._network_id) @mock.patch.object(policy.QosPolicyDefault, 'create') def test_set_default_no_default_policy_exists(self, mock_default_create): obj = self._create_test_policy() with mock.patch.object(obj, 'get_default', return_value=None): obj.set_default() mock_default_create.assert_called_once_with() def test_set_default_default_policy_exists(self): obj = self._create_test_policy() with mock.patch.object(obj, 'get_default', return_value=mock.Mock()): self.assertRaises(n_exc.QoSPolicyDefaultAlreadyExists, obj.set_default) def test_set_default_is_default_policy(self): obj = self._create_test_policy() with mock.patch.object(obj, 'get_default', return_value=obj.id), \ mock.patch.object(obj, 'set_default'): obj.set_default() @mock.patch.object(policy.QosPolicyDefault, 'get_object') @mock.patch.object(policy.QosPolicyDefault, 'delete') def test_unset_default_default_policy_exists(self, mock_default_delete, mock_default_get): obj = self._create_test_policy() with mock.patch.object(obj, 'get_default', return_value=obj.id): mock_default_get.return_value = policy.QosPolicyDefault() obj.unset_default() mock_default_get.assert_called_once_with(obj.obj_context, project_id=obj.project_id) mock_default_delete.assert_called_once_with() def test_unset_default_no_default_policy_exists(self): obj = self._create_test_policy() with mock.patch.object(obj, 'get_default', return_value=None): obj.unset_default() def test_synthetic_rule_fields(self): policy_obj, rule_obj = self._create_test_policy_with_rules( [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT]) policy_obj = policy.QosPolicy.get_object(self.context, id=policy_obj.id) self.assertEqual(rule_obj, policy_obj.rules) def test_get_object_fetches_rules_non_lazily(self): policy_obj, rule_obj = self._create_test_policy_with_rules( [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT]) policy_obj = policy.QosPolicy.get_object(self.context, id=policy_obj.id) self.assertEqual(rule_obj, policy_obj.rules) primitive = policy_obj.obj_to_primitive() self.assertNotEqual([], (primitive['versioned_object.data']['rules'])) def test_to_dict_returns_rules_as_dicts(self): policy_obj, rule_obj = self._create_test_policy_with_rules( [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT]) policy_obj = policy.QosPolicy.get_object(self.context, id=policy_obj.id) obj_dict = policy_obj.to_dict() rule_dict = rule_obj[0].to_dict() # first make sure that to_dict() is still sane and does not return # objects for obj in (rule_dict, obj_dict): self.assertIsInstance(obj, dict) self.assertEqual(rule_dict, obj_dict['rules'][0]) def test_shared_default(self): obj = self._make_object(self.obj_fields[0]) self.assertFalse(obj.shared) def test_delete_not_allowed_if_policy_in_use_by_port(self): obj = self._create_test_policy() obj.attach_port(self._port['id']) self.assertRaises(n_exc.QosPolicyInUse, obj.delete) obj.detach_port(self._port['id']) obj.delete() def test_delete_not_allowed_if_policy_in_use_by_network(self): obj = self._create_test_policy() obj.attach_network(self._network_id) self.assertRaises(n_exc.QosPolicyInUse, obj.delete) obj.detach_network(self._network_id) obj.delete() def test_reload_rules_reloads_rules(self): policy_obj, rule_obj = self._create_test_policy_with_rules( [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT]) self.assertEqual([], policy_obj.rules) policy_obj._reload_rules() self.assertEqual(rule_obj, policy_obj.rules) def test_reload_is_default(self): policy_obj = self._create_test_policy() self.assertFalse(policy_obj.is_default) policy_obj.set_default() policy_obj._reload_is_default() self.assertTrue(policy_obj.is_default) def test_get_bound_tenant_ids_returns_set_of_tenant_ids(self): obj = self._create_test_policy() obj.attach_port(self._port['id']) ids = self._test_class.get_bound_tenant_ids(self.context, obj['id']) self.assertEqual(ids.pop(), self._port.project_id) self.assertEqual(len(ids), 0) obj.detach_port(self._port['id']) obj.delete() @staticmethod def _policy_through_version(obj, version): primitive = obj.obj_to_primitive(target_version=version) return policy.QosPolicy.clean_obj_from_primitive(primitive) def test_object_version(self): policy_obj, rule_objs = self._create_test_policy_with_rules( RULE_OBJ_CLS.keys(), reload_rules=True) policy_obj_v1_5 = self._policy_through_version( policy_obj, policy.QosPolicy.VERSION) for rule_obj in rule_objs: self.assertIn(rule_obj, policy_obj_v1_5.rules) def test_object_version_degradation_1_3_to_1_2_null_description(self): policy_obj = self._create_test_policy() policy_obj.description = None with testtools.ExpectedException(exception.IncompatibleObjectVersion): policy_obj.obj_to_primitive('1.2') def test_object_version_degradation_to_1_0(self): #NOTE(mangelajo): we should not check .VERSION, since that's the # local version on the class definition policy_obj, rule_objs = self._create_test_policy_with_rules( [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, qos_consts.RULE_TYPE_DSCP_MARKING, qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH], reload_rules=True, bwlimit_direction=n_const.EGRESS_DIRECTION) policy_obj_v1_0 = self._policy_through_version(policy_obj, '1.0') self.assertIn(rule_objs[0], policy_obj_v1_0.rules) self.assertNotIn(rule_objs[1], policy_obj_v1_0.rules) self.assertNotIn(rule_objs[2], policy_obj_v1_0.rules) def test_object_version_degradation_1_2_to_1_1(self): #NOTE(mangelajo): we should not check .VERSION, since that's the # local version on the class definition policy_obj, rule_objs = self._create_test_policy_with_rules( [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, qos_consts.RULE_TYPE_DSCP_MARKING, qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH], reload_rules=True, bwlimit_direction=n_const.EGRESS_DIRECTION) policy_obj_v1_1 = self._policy_through_version(policy_obj, '1.1') self.assertIn(rule_objs[0], policy_obj_v1_1.rules) self.assertIn(rule_objs[1], policy_obj_v1_1.rules) self.assertNotIn(rule_objs[2], policy_obj_v1_1.rules) def test_object_version_degradation_1_3_to_1_2(self): #NOTE(mangelajo): we should not check .VERSION, since that's the # local version on the class definition policy_obj, rule_objs = self._create_test_policy_with_rules( [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, qos_consts.RULE_TYPE_DSCP_MARKING, qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH], reload_rules=True, bwlimit_direction=n_const.EGRESS_DIRECTION) policy_obj_v1_2 = self._policy_through_version(policy_obj, '1.2') self.assertIn(rule_objs[0], policy_obj_v1_2.rules) self.assertIn(rule_objs[1], policy_obj_v1_2.rules) self.assertIn(rule_objs[2], policy_obj_v1_2.rules) def test_v1_4_to_v1_3_drops_project_id(self): policy_new = self._create_test_policy() policy_v1_3 = policy_new.obj_to_primitive(target_version='1.3') self.assertNotIn('project_id', policy_v1_3['versioned_object.data']) self.assertIn('tenant_id', policy_v1_3['versioned_object.data']) def test_object_version_degradation_1_5_to_1_4_ingress_direction(self): policy_obj, rule_objs = self._create_test_policy_with_rules( [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, qos_consts.RULE_TYPE_DSCP_MARKING, qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH], reload_rules=True, bwlimit_direction=n_const.INGRESS_DIRECTION) policy_obj_v1_4 = self._policy_through_version(policy_obj, '1.4') self.assertNotIn(rule_objs[0], policy_obj_v1_4.rules) self.assertIn(rule_objs[1], policy_obj_v1_4.rules) self.assertIn(rule_objs[2], policy_obj_v1_4.rules) def test_object_version_degradation_1_5_to_1_4_egress_direction(self): policy_obj, rule_objs = self._create_test_policy_with_rules( [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, qos_consts.RULE_TYPE_DSCP_MARKING, qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH], reload_rules=True, bwlimit_direction=n_const.EGRESS_DIRECTION) policy_obj_v1_4 = self._policy_through_version(policy_obj, '1.4') self.assertIn(rule_objs[0], policy_obj_v1_4.rules) self.assertIn(rule_objs[1], policy_obj_v1_4.rules) self.assertIn(rule_objs[2], policy_obj_v1_4.rules) def test_v1_6_to_v1_5_drops_is_default(self): policy_new = self._create_test_policy() policy_v1_5 = policy_new.obj_to_primitive(target_version='1.5') self.assertNotIn('is_default', policy_v1_5['versioned_object.data']) @mock.patch.object(policy.QosPolicy, 'unset_default') def test_filter_by_shared(self, *mocks): project_id = uuidutils.generate_uuid() policy_obj = policy.QosPolicy( self.context, name='shared-policy', shared=True, project_id=project_id, is_default=False) policy_obj.create() policy_obj = policy.QosPolicy( self.context, name='private-policy', shared=False, project_id=project_id) policy_obj.create() shared_policies = policy.QosPolicy.get_objects( self.context, shared=True) self.assertEqual(1, len(shared_policies)) self.assertEqual('shared-policy', shared_policies[0].name) private_policies = policy.QosPolicy.get_objects( self.context, shared=False) self.assertEqual(1, len(private_policies)) self.assertEqual('private-policy', private_policies[0].name) def test_get_objects_queries_constant(self): # NOTE(korzen) QoSPolicy is using extra queries to reload rules. # QoSPolicy currently cannot be loaded using constant queries number. # It can be reworked in follow-up patch. pass class QosPolicyDefaultObjectTestCase(test_base.BaseObjectIfaceTestCase): _test_class = policy.QosPolicyDefault neutron-12.1.1/neutron/tests/unit/objects/qos/test_rule_type.py0000664000175000017500000001120413553660047025006 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # rule types are so different from other objects that we don't base the test # class on the common base class for all objects import mock from neutron_lib import constants as lib_consts from neutron_lib.db import constants as db_consts from neutron_lib.services.qos import constants as qos_consts from oslo_config import cfg from neutron.common import constants from neutron import manager from neutron.objects.qos import rule_type from neutron.services.qos import qos_plugin from neutron.tests import base as test_base DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' DRIVER_SUPPORTED_PARAMETERS = [ { 'parameter_name': qos_consts.MAX_KBPS, 'parameter_type': constants.VALUES_TYPE_RANGE, 'parameter_values': {"start": 0, "end": db_consts.DB_INTEGER_MAX_VALUE} }, { 'parameter_name': qos_consts.MAX_BURST, 'parameter_type': constants.VALUES_TYPE_RANGE, 'parameter_values': {"start": 0, "end": db_consts.DB_INTEGER_MAX_VALUE} }, { 'parameter_name': qos_consts.DIRECTION, 'parameter_type': constants.VALUES_TYPE_CHOICES, 'parameter_values': lib_consts.VALID_DIRECTIONS } ] class QosRuleTypeObjectTestCase(test_base.BaseTestCase): def setUp(self): super(QosRuleTypeObjectTestCase, self).setUp() self.config_parse() self.setup_coreplugin(load_plugins=False) cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) cfg.CONF.set_override("service_plugins", ["qos"]) manager.init() def test_get_object(self): driver_details = { 'name': "backend_driver", 'supported_parameters': DRIVER_SUPPORTED_PARAMETERS } with mock.patch.object( qos_plugin.QoSPlugin, 'supported_rule_type_details', return_value=[driver_details] ): rule_type_details = rule_type.QosRuleType.get_object( qos_consts.RULE_TYPE_BANDWIDTH_LIMIT) self.assertEqual( driver_details['name'], rule_type_details.drivers[0].name) self.assertEqual( driver_details['supported_parameters'], rule_type_details.drivers[0].supported_parameters) self.assertEqual(1, len(rule_type_details.drivers)) self.assertEqual( qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, rule_type_details.type) def test_get_objects(self): rule_types_mock = mock.PropertyMock( return_value=set(qos_consts.VALID_RULE_TYPES)) with mock.patch.object(qos_plugin.QoSPlugin, 'supported_rule_types', new_callable=rule_types_mock): types = rule_type.QosRuleType.get_objects() self.assertEqual(sorted(qos_consts.VALID_RULE_TYPES), sorted(type_['type'] for type_ in types)) def test_wrong_type(self): self.assertRaises(ValueError, rule_type.QosRuleType, type='bad_type') @staticmethod def _policy_through_version(obj, version): primitive = obj.obj_to_primitive(target_version=version) return rule_type.QosRuleType.clean_obj_from_primitive(primitive) def test_object_version(self): qos_rule_type = rule_type.QosRuleType() rule_type_v1_1 = self._policy_through_version(qos_rule_type, '1.1') self.assertIn(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, tuple(rule_type_v1_1.fields['type'].AUTO_TYPE. _valid_values)) self.assertIn(qos_consts.RULE_TYPE_DSCP_MARKING, tuple(rule_type_v1_1.fields['type'].AUTO_TYPE. _valid_values)) def test_object_version_degradation_1_3_to_1_2(self): drivers_obj = rule_type.QosRuleTypeDriver( name="backend_driver", supported_parameters=[{}] ) qos_rule_type = rule_type.QosRuleType( type=qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, drivers=[drivers_obj]) rule_type_v1_2 = self._policy_through_version(qos_rule_type, '1.2') self.assertNotIn("drivers", rule_type_v1_2) self.assertIn("type", rule_type_v1_2) neutron-12.1.1/neutron/tests/unit/objects/qos/test_rule.py0000664000175000017500000002544713553660047023763 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron_lib.services.qos import constants as qos_consts from oslo_utils import uuidutils from oslo_versionedobjects import exception from neutron.objects.qos import policy from neutron.objects.qos import rule from neutron.tests import base as neutron_test_base from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api POLICY_ID_A = 'policy-id-a' POLICY_ID_B = 'policy-id-b' DEVICE_OWNER_COMPUTE = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' class QosRuleObjectTestCase(neutron_test_base.BaseTestCase): def _test_should_apply_to_port(self, rule_policy_id, port_policy_id, device_owner, expected_result): test_rule = rule.QosRule(qos_policy_id=rule_policy_id) port = {qos_consts.QOS_POLICY_ID: port_policy_id, 'device_owner': device_owner} self.assertEqual(expected_result, test_rule.should_apply_to_port(port)) def test_should_apply_to_port_with_network_port_and_net_policy(self): self._test_should_apply_to_port( rule_policy_id=POLICY_ID_B, port_policy_id=POLICY_ID_A, device_owner=constants.DEVICE_OWNER_ROUTER_INTF, expected_result=False) def test_should_apply_to_port_with_network_port_and_only_net_policy(self): self._test_should_apply_to_port( rule_policy_id=POLICY_ID_B, port_policy_id=None, device_owner=constants.DEVICE_OWNER_ROUTER_INTF, expected_result=False) def test_should_apply_to_port_with_network_port_and_port_policy(self): self._test_should_apply_to_port( rule_policy_id=POLICY_ID_A, port_policy_id=POLICY_ID_A, device_owner=constants.DEVICE_OWNER_ROUTER_INTF, expected_result=True) def test_should_apply_to_port_with_compute_port_and_net_policy(self): # NOTE(ralonsoh): in this case the port has a port QoS policy; the # network QoS policy can't be applied. self._test_should_apply_to_port( rule_policy_id=POLICY_ID_B, port_policy_id=POLICY_ID_A, device_owner=DEVICE_OWNER_COMPUTE, expected_result=False) def test_should_apply_to_port_with_compute_port_and_only_net_policy(self): self._test_should_apply_to_port( rule_policy_id=POLICY_ID_B, port_policy_id=None, device_owner=DEVICE_OWNER_COMPUTE, expected_result=True) def test_should_apply_to_port_with_compute_port_and_port_policy(self): self._test_should_apply_to_port( rule_policy_id=POLICY_ID_A, port_policy_id=POLICY_ID_A, device_owner=DEVICE_OWNER_COMPUTE, expected_result=True) def test_should_apply_to_port_with_router_gw_port_and_net_policy(self): self._test_should_apply_to_port( rule_policy_id=POLICY_ID_B, port_policy_id=POLICY_ID_A, device_owner=constants.DEVICE_OWNER_ROUTER_GW, expected_result=False) def test_should_apply_to_port_with_router_gw_port_and_port_policy(self): self._test_should_apply_to_port( rule_policy_id=POLICY_ID_A, port_policy_id=POLICY_ID_A, device_owner=constants.DEVICE_OWNER_ROUTER_GW, expected_result=True) def test_should_apply_to_port_with_agent_gw_port_and_net_policy(self): self._test_should_apply_to_port( rule_policy_id=POLICY_ID_B, port_policy_id=POLICY_ID_A, device_owner=constants.DEVICE_OWNER_AGENT_GW, expected_result=False) def test_should_apply_to_port_with_agent_gw_port_and_port_policy(self): self._test_should_apply_to_port( rule_policy_id=POLICY_ID_A, port_policy_id=POLICY_ID_A, device_owner=constants.DEVICE_OWNER_AGENT_GW, expected_result=True) class QosBandwidthLimitRuleObjectTestCase(test_base.BaseObjectIfaceTestCase): _test_class = rule.QosBandwidthLimitRule def test_to_dict_returns_type(self): obj = rule.QosBandwidthLimitRule(self.context, **self.db_objs[0]) dict_ = obj.to_dict() self.assertEqual(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT, dict_['type']) def test_bandwidth_limit_object_version_degradation(self): self.db_objs[0]['direction'] = constants.EGRESS_DIRECTION rule_obj = rule.QosBandwidthLimitRule(self.context, **self.db_objs[0]) primitive_rule = rule_obj.obj_to_primitive('1.2') self.assertNotIn( "direction", primitive_rule['versioned_object.data'].keys()) self.assertEqual( self.db_objs[0]['max_kbps'], primitive_rule['versioned_object.data']['max_kbps']) self.assertEqual( self.db_objs[0]['max_burst_kbps'], primitive_rule['versioned_object.data']['max_burst_kbps']) self.db_objs[0]['direction'] = constants.INGRESS_DIRECTION rule_obj = rule.QosBandwidthLimitRule(self.context, **self.db_objs[0]) self.assertRaises( exception.IncompatibleObjectVersion, rule_obj.obj_to_primitive, '1.2') def test_duplicate_rules(self): policy_id = uuidutils.generate_uuid() ingress_rule_1 = rule.QosBandwidthLimitRule( self.context, qos_policy_id=policy_id, max_kbps=1000, max_burst=500, direction=constants.INGRESS_DIRECTION) ingress_rule_2 = rule.QosBandwidthLimitRule( self.context, qos_policy_id=policy_id, max_kbps=2000, max_burst=500, direction=constants.INGRESS_DIRECTION) egress_rule = rule.QosBandwidthLimitRule( self.context, qos_policy_id=policy_id, max_kbps=1000, max_burst=500, direction=constants.EGRESS_DIRECTION) dscp_rule = rule.QosDscpMarkingRule( self.context, qos_policy_id=policy_id, dscp_mark=16) self.assertTrue(ingress_rule_1.duplicates(ingress_rule_2)) self.assertFalse(ingress_rule_1.duplicates(egress_rule)) self.assertFalse(ingress_rule_1.duplicates(dscp_rule)) class QosBandwidthLimitRuleDbObjectTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = rule.QosBandwidthLimitRule def setUp(self): super(QosBandwidthLimitRuleDbObjectTestCase, self).setUp() # Prepare policy to be able to insert a rule for obj in self.db_objs: generated_qos_policy_id = obj['qos_policy_id'] policy_obj = policy.QosPolicy(self.context, id=generated_qos_policy_id, project_id=uuidutils.generate_uuid()) policy_obj.create() class QosDscpMarkingRuleObjectTestCase(test_base.BaseObjectIfaceTestCase): _test_class = rule.QosDscpMarkingRule def test_dscp_object_version_degradation(self): dscp_rule = rule.QosDscpMarkingRule() self.assertRaises(exception.IncompatibleObjectVersion, dscp_rule.obj_to_primitive, '1.0') def test_duplicate_rules(self): policy_id = uuidutils.generate_uuid() dscp_rule_1 = rule.QosDscpMarkingRule( self.context, qos_policy_id=policy_id, dscp_mark=16) dscp_rule_2 = rule.QosDscpMarkingRule( self.context, qos_policy_id=policy_id, dscp_mark=32) bw_limit_rule = rule.QosBandwidthLimitRule( self.context, qos_policy_id=policy_id, max_kbps=1000, max_burst=500, direction=constants.EGRESS_DIRECTION) self.assertTrue(dscp_rule_1.duplicates(dscp_rule_2)) self.assertFalse(dscp_rule_1.duplicates(bw_limit_rule)) class QosDscpMarkingRuleDbObjectTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = rule.QosDscpMarkingRule def setUp(self): super(QosDscpMarkingRuleDbObjectTestCase, self).setUp() # Prepare policy to be able to insert a rule for obj in self.db_objs: generated_qos_policy_id = obj['qos_policy_id'] policy_obj = policy.QosPolicy(self.context, id=generated_qos_policy_id, project_id=uuidutils.generate_uuid()) policy_obj.create() class QosMinimumBandwidthRuleObjectTestCase(test_base.BaseObjectIfaceTestCase): _test_class = rule.QosMinimumBandwidthRule def test_min_bw_object_version_degradation(self): min_bw_rule = rule.QosMinimumBandwidthRule() for version in ['1.0', '1.1']: self.assertRaises(exception.IncompatibleObjectVersion, min_bw_rule.obj_to_primitive, version) def test_duplicate_rules(self): policy_id = uuidutils.generate_uuid() ingress_rule_1 = rule.QosMinimumBandwidthRule( self.context, qos_policy_id=policy_id, min_kbps=1000, direction=constants.INGRESS_DIRECTION) ingress_rule_2 = rule.QosMinimumBandwidthRule( self.context, qos_policy_id=policy_id, min_kbps=2000, direction=constants.INGRESS_DIRECTION) egress_rule = rule.QosMinimumBandwidthRule( self.context, qos_policy_id=policy_id, min_kbps=1000, direction=constants.EGRESS_DIRECTION) dscp_rule = rule.QosDscpMarkingRule( self.context, qos_policy_id=policy_id, dscp_mark=16) self.assertTrue(ingress_rule_1.duplicates(ingress_rule_2)) self.assertFalse(ingress_rule_1.duplicates(egress_rule)) self.assertFalse(ingress_rule_1.duplicates(dscp_rule)) class QosMinimumBandwidthRuleDbObjectTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = rule.QosMinimumBandwidthRule def setUp(self): super(QosMinimumBandwidthRuleDbObjectTestCase, self).setUp() # Prepare policy to be able to insert a rule for obj in self.db_objs: generated_qos_policy_id = obj['qos_policy_id'] policy_obj = policy.QosPolicy(self.context, id=generated_qos_policy_id, project_id=uuidutils.generate_uuid()) policy_obj.create() neutron-12.1.1/neutron/tests/unit/objects/qos/__init__.py0000664000175000017500000000000013553660046023465 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/qos/test_binding.py0000664000175000017500000000500613553660047024413 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects.qos import binding from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api class QosPolicyPortBindingObjectTestCase(test_base.BaseObjectIfaceTestCase): _test_class = binding.QosPolicyPortBinding class QosPolicyPortBindingDbObjectTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = binding.QosPolicyPortBinding def setUp(self): super(QosPolicyPortBindingDbObjectTestCase, self).setUp() network_id = self._create_test_network_id() for db_obj in self.db_objs: self._create_test_qos_policy(id=db_obj['policy_id']) self._create_test_port(network_id=network_id, id=db_obj['port_id']) class QosPolicyNetworkBindingObjectTestCase(test_base.BaseObjectIfaceTestCase): _test_class = binding.QosPolicyNetworkBinding class QosPolicyNetworkBindingDbObjectTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = binding.QosPolicyNetworkBinding def setUp(self): super(QosPolicyNetworkBindingDbObjectTestCase, self).setUp() for db_obj in self.db_objs: self._create_test_qos_policy(id=db_obj['policy_id']) self._create_test_network(network_id=db_obj['network_id']) class QosPolicyFloatingIPBindingObjectTestCase( test_base.BaseObjectIfaceTestCase): _test_class = binding.QosPolicyFloatingIPBinding class QosPolicyFloatingIPBindingDbObjectTestCase( test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = binding.QosPolicyFloatingIPBinding def setUp(self): super(QosPolicyFloatingIPBindingDbObjectTestCase, self).setUp() for db_obj in self.db_objs: self._create_test_qos_policy(id=db_obj['policy_id']) self._create_test_fip_id(fip_id=db_obj['fip_id']) neutron-12.1.1/neutron/tests/unit/objects/test_objects.py0000664000175000017500000001550313553660047023633 0ustar zuulzuul00000000000000# Copyright 2015 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import pprint from oslo_versionedobjects import fixture from neutron import objects from neutron.objects import base from neutron.tests import base as test_base # NOTE: The hashes in this list should only be changed if they come with a # corresponding version bump in the affected objects. Please keep the list in # alphabetic order. object_data = { 'AddressScope': '1.0-dd0dfdb67775892d3adc090e28e43bd8', 'Agent': '1.0-7106cb40117a8d1f042545796ed8787d', 'AllowedAddressPair': '1.0-9f9186b6f952fbf31d257b0458b852c0', 'AutoAllocatedTopology': '1.0-74642e58c53bf3610dc224c59f81b242', 'DefaultSecurityGroup': '1.0-971520cb2e0ec06d747885a0cf78347f', 'DistributedPortBinding': '1.0-39c0d17b281991dcb66716fee5a8bef2', 'DNSNameServer': '1.0-bf87a85327e2d812d1666ede99d9918b', 'ExternalNetwork': '1.0-53d885e033cb931f9bb3bdd6bbe3f0ce', 'DVRMacAddress': '1.0-d3c61a8338d20da74db2364d4d6554f2', 'ExtraDhcpOpt': '1.0-632f689cbeb36328995a7aed1d0a78d3', 'FlatAllocation': '1.0-bf666f24f4642b047eeca62311fbcb41', 'Flavor': '1.0-82194de5c9aafce08e8527bb7977f5c6', 'FlavorServiceProfileBinding': '1.0-a2c8731e16cefdac4571f80abf1f8930', 'FloatingIP': '1.0-0205cc99ec79e8089d641ed1b565ddae', 'FloatingIPDNS': '1.0-ee3db848500fa1825235f701828c06d5', 'GeneveAllocation': '1.0-d5f76e8eac60a778914d61dd8e23e90f', 'GeneveEndpoint': '1.0-040f026996b5952e2ae4ccd40ac61ca6', 'GreAllocation': '1.0-9ee1bbc4d999bea84c99425484b11ac5', 'GreEndpoint': '1.0-040f026996b5952e2ae4ccd40ac61ca6', 'IPAllocation': '1.0-47251b4c6d45c3b5feb0297fe5c461f2', 'IPAllocationPool': '1.0-371016a6480ed0b4299319cb46d9215d', 'IpamAllocation': '1.0-ace65431abd0a7be84cc4a5f32d034a3', 'IpamAllocationPool': '1.0-c4fa1460ed1b176022ede7af7d1510d5', 'IpamSubnet': '1.0-713de401682a70f34891e13af645fa08', 'L3HARouterAgentPortBinding': '1.0-d1d7ee13f35d56d7e225def980612ee5', 'L3HARouterNetwork': '1.0-87acea732853f699580179a94d2baf91', 'L3HARouterVRIdAllocation': '1.0-37502aebdbeadc4f9e3bd5e9da714ab9', 'MeteringLabel': '1.0-cc4b620a3425222447cbe459f62de533', 'MeteringLabelRule': '1.0-b5c5717e7bab8d1af1623156012a5842', 'Log': '1.0-6391351c0f34ed34375a19202f361d24', 'Network': '1.0-f2f6308f79731a767b92b26b0f4f3849', 'NetworkDhcpAgentBinding': '1.0-6eeceb5fb4335cd65a305016deb41c68', 'NetworkDNSDomain': '1.0-420db7910294608534c1e2e30d6d8319', 'NetworkPortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3', 'NetworkRBAC': '1.0-c8a67f39809c5a3c8c7f26f2f2c620b2', 'NetworkSegment': '1.0-57b7f2960971e3b95ded20cbc59244a8', 'Port': '1.1-5bf48d12a7bf7f5b7a319e8003b437a5', 'PortBinding': '1.0-3306deeaa6deb01e33af06777d48d578', 'PortBindingLevel': '1.0-de66a4c61a083b8f34319fa9dde5b060', 'PortDataPlaneStatus': '1.0-25be74bda46c749653a10357676c0ab2', 'PortDNS': '1.1-c5ca2dc172bdd5fafee3fc986d1d7023', 'PortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3', 'ProviderResourceAssociation': '1.0-05ab2d5a3017e5ce9dd381328f285f34', 'ProvisioningBlock': '1.0-c19d6d05bfa8143533471c1296066125', 'QosBandwidthLimitRule': '1.3-51b662b12a8d1dfa89288d826c6d26d3', 'QosDscpMarkingRule': '1.3-0313c6554b34fd10c753cb63d638256c', 'QosMinimumBandwidthRule': '1.3-314c3419f4799067cc31cc319080adff', 'QosPolicyRBAC': '1.0-c8a67f39809c5a3c8c7f26f2f2c620b2', 'QosRuleType': '1.3-7286188edeb3a0386f9cf7979b9700fc', 'QosRuleTypeDriver': '1.0-7d8cb9f0ef661ac03700eae97118e3db', 'QosPolicy': '1.7-4adb0cde3102c10d8970ec9487fd7fe7', 'QosPolicyDefault': '1.0-59e5060eedb1f06dd0935a244d27d11c', 'QosPolicyFloatingIPBinding': '1.0-5625df4205a18778cd6aa40f99be024e', 'QosPolicyNetworkBinding': '1.0-df53a1e0f675aab8d27a1ccfed38dc42', 'QosPolicyPortBinding': '1.0-66cb364ac99aa64523ade07f9f868ea6', 'Quota': '1.0-6bb6a0f1bd5d66a2134ffa1a61873097', 'QuotaUsage': '1.0-6fbf820368681aac7c5d664662605cf9', 'Reservation': '1.0-49929fef8e82051660342eed51b48f2a', 'ResourceDelta': '1.0-a980b37e0a52618b5af8db29af18be76', 'Route': '1.0-a9883a63b416126f9e345523ec09483b', 'Router': '1.0-adb984d9b73aa11566d40abbeb790df1', 'RouterExtraAttributes': '1.0-ef8d61ae2864f0ec9af0ab7939cab318', 'RouterL3AgentBinding': '1.0-c5ba6c95e3a4c1236a55f490cd67da82', 'RouterPort': '1.0-c8c8f499bcdd59186fcd83f323106908', 'RouterRoute': '1.0-07fc5337c801fb8c6ccfbcc5afb45907', 'SecurityGroup': '1.0-e26b90c409b31fd2e3c6fcec402ac0b9', 'SecurityGroupPortBinding': '1.0-6879d5c0af80396ef5a72934b6a6ef20', 'SecurityGroupRule': '1.0-e9b8dace9d48b936c62ad40fe1f339d5', 'SegmentHostMapping': '1.0-521597cf82ead26217c3bd10738f00f0', 'ServiceProfile': '1.0-9beafc9e7d081b8258f3c5cb66ac5eed', 'StandardAttribute': '1.0-617d4f46524c4ce734a6fc1cc0ac6a0b', 'Subnet': '1.0-927155c1fdd5a615cbcb981dda97bce4', 'SubnetPool': '1.0-a0e03895d1a6e7b9d4ab7b0ca13c3867', 'SubnetPoolPrefix': '1.0-13c15144135eb869faa4a76dc3ee3b6c', 'SubnetServiceType': '1.0-05ae4cdb2a9026a697b143926a1add8c', 'SubPort': '1.0-72c8471068db1f0491b5480fe49b52bb', 'Tag': '1.0-1a0d20379920ffa3cebfd3e016d2f7a0', 'Trunk': '1.1-aa3922b39e37fbb89886c2ee8715cf49', 'VlanAllocation': '1.0-72636c1b7d5c8eef987bd09666e64f3e', 'VxlanAllocation': '1.0-934638cd32d00f81d6fbf93c8eb5755a', 'VxlanEndpoint': '1.0-40522eafdcf838758711dfa886cbdb2e', } class TestObjectVersions(test_base.BaseTestCase): def setUp(self): super(TestObjectVersions, self).setUp() # NOTE(ihrachys): seed registry with all objects under neutron.objects # before validating the hashes objects.register_objects() def test_versions(self): checker = fixture.ObjectVersionChecker( base.NeutronObjectRegistry.obj_classes()) fingerprints = checker.get_hashes() if os.getenv('GENERATE_HASHES'): with open('object_hashes.txt', 'w') as hashes_file: hashes_file.write(pprint.pformat(fingerprints)) expected, actual = checker.test_hashes(object_data) self.assertEqual(expected, actual, 'Some objects have changed; please make sure the ' 'versions have been bumped, and then update their ' 'hashes in the object_data map in this test module.') neutron-12.1.1/neutron/tests/unit/objects/test_trunk.py0000664000175000017500000001615113553660047023345 0ustar zuulzuul00000000000000# Copyright (c) 2016 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools import mock from neutron_lib import exceptions as n_exc from oslo_db import exception as obj_exc from oslo_utils import uuidutils from neutron.objects.db import api as obj_db_api from neutron.objects import trunk as t_obj from neutron.services.trunk import constants from neutron.services.trunk import exceptions as t_exc from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api class SubPortObjectTestCase(test_base.BaseObjectIfaceTestCase): _test_class = t_obj.SubPort def test_create_duplicates(self): with mock.patch.object(obj_db_api, 'create_object', side_effect=obj_exc.DBDuplicateEntry): obj = self._test_class(self.context, **self.obj_fields[0]) self.assertRaises(t_exc.DuplicateSubPort, obj.create) class SubPortDbObjectTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = t_obj.SubPort def setUp(self): super(SubPortDbObjectTestCase, self).setUp() self._network_id = self._create_test_network_id() for obj in self.obj_fields: self._create_test_port( id=obj['port_id'], network_id=self._network_id) self._create_trunk(trunk_id=obj['trunk_id']) def _create_trunk(self, trunk_id): port_id = self._create_test_port_id(network_id=self._network_id) trunk = t_obj.Trunk(self.context, id=trunk_id, port_id=port_id) trunk.create() def test_create_port_not_found(self): obj = self.obj_fields[0] obj['port_id'] = uuidutils.generate_uuid() sub_port = self._make_object(obj) self.assertRaises(n_exc.PortNotFound, sub_port.create) def test_create_trunk_not_found(self): obj = self.obj_fields[0] obj['trunk_id'] = uuidutils.generate_uuid() sub_port = self._make_object(obj) self.assertRaises(t_exc.TrunkNotFound, sub_port.create) class TrunkObjectTestCase(test_base.BaseObjectIfaceTestCase): _test_class = t_obj.Trunk class TrunkDbObjectTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = t_obj.Trunk def setUp(self): super(TrunkDbObjectTestCase, self).setUp() self._network_id = self._create_test_network_id() sub_ports = [] for obj in self.db_objs: sub_ports.extend(obj['sub_ports']) for obj in itertools.chain(self.obj_fields, sub_ports): self._create_test_port( id=obj['port_id'], network_id=self._network_id) def test_create_port_not_found(self): obj = self.obj_fields[0] obj['port_id'] = uuidutils.generate_uuid() trunk = self._make_object(obj) self.assertRaises(n_exc.PortNotFound, trunk.create) def _test_create_trunk_with_subports(self, port_id, vids): project_id = uuidutils.generate_uuid() sub_ports = [] for vid in vids: vid_port_id = self._create_test_port_id( network_id=self._network_id) sub_ports.append(t_obj.SubPort( self.context, port_id=vid_port_id, segmentation_type='vlan', segmentation_id=vid)) trunk = t_obj.Trunk( self.context, port_id=port_id, sub_ports=sub_ports, project_id=project_id) trunk.create() self.assertEqual(sub_ports, trunk.sub_ports) return trunk def test_create_with_sub_ports(self): trunk = self._test_create_trunk_with_subports( self.db_objs[0]['port_id'], [1, 2]) def _as_tuple(sub_port): return (sub_port['port_id'], sub_port['segmentation_type'], sub_port['segmentation_id']) expected = {_as_tuple(port) for port in trunk.sub_ports} sub_ports = t_obj.SubPort.get_objects(self.context, trunk_id=trunk.id) self.assertEqual(expected, {_as_tuple(port) for port in sub_ports}) def test_get_object_includes_correct_subports(self): trunk1_vids = [1, 2, 3] trunk2_vids = [4, 5, 6] port_id1 = self.db_objs[0]['port_id'] trunk1 = self._test_create_trunk_with_subports(port_id1, trunk1_vids) port_id2 = uuidutils.generate_uuid() self._create_test_port( id=port_id2, network_id=self._network_id) self._test_create_trunk_with_subports(port_id2, trunk2_vids) listed_trunk1 = t_obj.Trunk.get_object( self.context, id=trunk1.id, port_id=port_id1 ) self.assertEqual( set(trunk1_vids), {sp.segmentation_id for sp in listed_trunk1.sub_ports} ) def test_update_multiple_fields(self): trunk = t_obj.Trunk(context=self.context, admin_state_up=False, port_id=self.db_objs[0]['port_id'], status=constants.DOWN_STATUS) trunk.create() fields = {'admin_state_up': True, 'status': constants.ACTIVE_STATUS} trunk.update(**fields) trunk = t_obj.Trunk.get_object(self.context, id=trunk.id) self._assert_trunk_attrs(trunk, **fields) def _assert_trunk_attrs(self, trunk, **kwargs): """Check the values passed in kwargs match the values of the trunk""" for k in trunk.fields: if k in kwargs: self.assertEqual(kwargs[k], trunk[k]) def test_v1_1_to_v1_0_drops_project_id(self): trunk_new = self._test_create_trunk_with_subports( self.db_objs[0]['port_id'], [1, 2]) trunk_v1_0 = trunk_new.obj_to_primitive(target_version='1.0') self.assertNotIn('project_id', trunk_v1_0['versioned_object.data']) self.assertIn('tenant_id', trunk_v1_0['versioned_object.data']) def test_get_objects_tenant_id(self): trunk = t_obj.Trunk(context=self.context, project_id='faketenant', port_id=self.db_objs[0]['port_id']) trunk.create() self.assertIsNotNone( t_obj.Trunk.get_objects(self.context, tenant_id='faketenant')) def test_get_objects_both_tenant_and_project_ids(self): trunk = t_obj.Trunk(context=self.context, project_id='faketenant', port_id=self.db_objs[0]['port_id']) trunk.create() self.assertIsNotNone( t_obj.Trunk.get_objects( self.context, tenant_id='faketenant', project_id='faketenant')) neutron-12.1.1/neutron/tests/unit/objects/test_floatingip.py0000664000175000017500000000237413553660046024337 0ustar zuulzuul00000000000000# Copyright 2016 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import floatingip from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class FloatingIPDNSIfaceObjectTestcase( obj_test_base.BaseObjectIfaceTestCase): _test_class = floatingip.FloatingIPDNS class FloatingIPDNSDbObjectTestcase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = floatingip.FloatingIPDNS def setUp(self): super(FloatingIPDNSDbObjectTestcase, self).setUp() self.update_obj_fields( {'floatingip_id': lambda: self._create_test_fip_id()}) neutron-12.1.1/neutron/tests/unit/objects/test_router.py0000664000175000017500000000712413553660047023522 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import router from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class RouterRouteIfaceObjectTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = router.RouterRoute class RouterRouteDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = router.RouterRoute def setUp(self): super(RouterRouteDbObjectTestCase, self).setUp() self.update_obj_fields( {'router_id': lambda: self._create_test_router_id()}) class RouterExtraAttrsIfaceObjTestCase(obj_test_base. BaseObjectIfaceTestCase): _test_class = router.RouterExtraAttributes class RouterExtraAttrsDbObjTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = router.RouterExtraAttributes def setUp(self): super(RouterExtraAttrsDbObjTestCase, self).setUp() self.update_obj_fields( {'router_id': lambda: self._create_test_router_id()}) class RouterIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = router.Router class RouterDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = router.Router def setUp(self): super(RouterDbObjectTestCase, self).setUp() self.update_obj_fields( {'gw_port_id': lambda: self._create_test_port_id(), 'flavor_id': lambda: self._create_test_flavor_id()}) class RouterPortIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = router.RouterPort class RouterPortDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = router.RouterPort def setUp(self): super(RouterPortDbObjectTestCase, self).setUp() self.update_obj_fields( {'router_id': lambda: self._create_test_router_id(), 'port_id': lambda: self._create_test_port_id()}) class DVRMacAddressIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = router.DVRMacAddress class DVRMacAddressDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = router.DVRMacAddress class FloatingIPIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = router.FloatingIP class FloatingIPDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = router.FloatingIP def setUp(self): super(FloatingIPDbObjectTestCase, self).setUp() self.update_obj_fields( {'floating_port_id': lambda: self._create_test_port_id(), 'fixed_port_id': lambda: self._create_test_port_id(), 'router_id': lambda: self._create_test_router_id()}) neutron-12.1.1/neutron/tests/unit/objects/port/0000775000175000017500000000000013553660157021553 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/port/extensions/0000775000175000017500000000000013553660157023752 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/port/extensions/test_port_security.py0000664000175000017500000000250313553660046030273 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects.port.extensions import port_security from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class PortSecurityIfaceObjTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = port_security.PortSecurity class PortSecurityDbObjTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = port_security.PortSecurity def setUp(self): super(PortSecurityDbObjTestCase, self).setUp() network_id = self._create_test_network_id() for obj in self.db_objs: self._create_test_port( id=obj['port_id'], network_id=network_id) neutron-12.1.1/neutron/tests/unit/objects/port/extensions/test_extra_dhcp_opt.py0000664000175000017500000000230713553660046030365 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects.port.extensions import extra_dhcp_opt from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class ExtraDhcpOptIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = extra_dhcp_opt.ExtraDhcpOpt class ExtraDhcpOptDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = extra_dhcp_opt.ExtraDhcpOpt def setUp(self): super(ExtraDhcpOptDbObjectTestCase, self).setUp() self.update_obj_fields( {'port_id': lambda: self._create_test_port_id()}) neutron-12.1.1/neutron/tests/unit/objects/port/extensions/test_allowedaddresspairs.py0000664000175000017500000000246513553660047031424 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects.port.extensions import allowedaddresspairs from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class AllowedAddrPairsIfaceObjTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = allowedaddresspairs.AllowedAddressPair #TODO(mhickey): Add common base db test class specifically for port extensions class AllowedAddrPairsDbObjTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = allowedaddresspairs.AllowedAddressPair def setUp(self): super(AllowedAddrPairsDbObjTestCase, self).setUp() self.update_obj_fields( {'port_id': lambda: self._create_test_port_id()}) neutron-12.1.1/neutron/tests/unit/objects/port/extensions/__init__.py0000664000175000017500000000000013553660046026046 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/port/extensions/test_data_plane_status.py0000664000175000017500000000255313553660046031060 0ustar zuulzuul00000000000000# Copyright (c) 2017 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects.port.extensions import data_plane_status from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class DataPlaneStatusIfaceObjTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = data_plane_status.PortDataPlaneStatus class DataPlaneStatusDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = data_plane_status.PortDataPlaneStatus def setUp(self): super(DataPlaneStatusDbObjectTestCase, self).setUp() net = self._create_test_network() getter = lambda: self._create_test_port(network_id=net.id).id self.update_obj_fields({'port_id': getter}) neutron-12.1.1/neutron/tests/unit/objects/port/__init__.py0000664000175000017500000000000013553660046023647 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/__init__.py0000664000175000017500000000000013553660046022663 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/test_subnetpool.py0000664000175000017500000000573713553660047024404 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_utils import uuidutils from neutron.objects import subnetpool from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class SubnetPoolTestMixin(object): def _create_test_subnetpool(self): obj = subnetpool.SubnetPool( self.context, id=uuidutils.generate_uuid(), ip_version=4, default_prefixlen=24, min_prefixlen=0, max_prefixlen=32, shared=False) obj.create() return obj class SubnetPoolIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = subnetpool.SubnetPool class SubnetPoolDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase, SubnetPoolTestMixin): _test_class = subnetpool.SubnetPool def test_subnetpool_prefixes(self): pool = self._create_test_subnetpool() prefixes = obj_test_base.get_list_of_random_networks() pool.prefixes = prefixes pool.update() new_pool = self._test_class.get_object(self.context, id=pool.id) self.assertItemsEqual(prefixes, new_pool.prefixes) prefixes.pop() pool.prefixes = prefixes pool.update() new_pool = self._test_class.get_object(self.context, id=pool.id) self.assertItemsEqual(prefixes, new_pool.prefixes) def test_get_objects_queries_constant(self): # TODO(korzen) SubnetPool is using SubnetPoolPrefix object to reload # prefixes, which costs extra SQL query each time reload_prefixes # are called in get_object(s). SubnetPool has defined relationship # for SubnetPoolPrefixes, so it should be possible to reuse side loaded # values fo this. To be reworked in follow-up patch. pass class SubnetPoolPrefixIfaceObjectTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = subnetpool.SubnetPoolPrefix class SubnetPoolPrefixDbObjectTestCase( obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase, SubnetPoolTestMixin): _test_class = subnetpool.SubnetPoolPrefix def setUp(self): super(SubnetPoolPrefixDbObjectTestCase, self).setUp() self.update_obj_fields( {'subnetpool_id': lambda: self._create_test_subnetpool().id}) neutron-12.1.1/neutron/tests/unit/objects/test_tag.py0000664000175000017500000000225113553660046022750 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import tag from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class TagIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = tag.Tag class TagDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = tag.Tag def setUp(self): super(TagDbObjectTestCase, self).setUp() self.update_obj_fields( { 'standard_attr_id': lambda: self._create_test_standard_attribute_id() }) neutron-12.1.1/neutron/tests/unit/objects/test_network.py0000664000175000017500000002106613553660047023674 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.objects import base as obj_base from neutron.objects import network from neutron.objects.qos import binding from neutron.objects.qos import policy from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api # TODO(ihrachys): add tests for NetworkRBAC class NetworkDhcpAgentBindingObjectIfaceTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = network.NetworkDhcpAgentBinding class NetworkDhcpAgentBindingDbObjectTestCase( obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = network.NetworkDhcpAgentBinding def setUp(self): super(NetworkDhcpAgentBindingDbObjectTestCase, self).setUp() self._network = self._create_test_network() self.update_obj_fields( {'network_id': self._network.id, 'dhcp_agent_id': lambda: self._create_test_agent_id()}) class NetworkPortSecurityIfaceObjTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = network.NetworkPortSecurity class NetworkPortSecurityDbObjTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = network.NetworkPortSecurity def setUp(self): super(NetworkPortSecurityDbObjTestCase, self).setUp() self.update_obj_fields({'id': lambda: self._create_test_network_id()}) class NetworkSegmentIfaceObjTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = network.NetworkSegment def setUp(self): super(NetworkSegmentIfaceObjTestCase, self).setUp() # TODO(ihrachys): we should not need to duplicate that in every single # place, instead we should move the default pager into the base class # attribute and pull it from there for testing matters. Leaving it for # a follow up. self.pager_map[self._test_class.obj_name()] = ( obj_base.Pager( sorts=[('network_id', True), ('segment_index', True)])) class NetworkSegmentDbObjTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = network.NetworkSegment def setUp(self): super(NetworkSegmentDbObjTestCase, self).setUp() self.update_obj_fields( {'network_id': lambda: self._create_test_network_id()}) def test_hosts(self): hosts = ['host1', 'host2'] obj = self._make_object(self.obj_fields[0]) obj.hosts = hosts obj.create() obj = network.NetworkSegment.get_object(self.context, id=obj.id) self.assertEqual(hosts, obj.hosts) obj.hosts = ['host3'] obj.update() obj = network.NetworkSegment.get_object(self.context, id=obj.id) self.assertEqual(['host3'], obj.hosts) obj.hosts = None obj.update() obj = network.NetworkSegment.get_object(self.context, id=obj.id) self.assertFalse(obj.hosts) class NetworkObjectIfaceTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = network.Network def setUp(self): super(NetworkObjectIfaceTestCase, self).setUp() self.pager_map[network.NetworkSegment.obj_name()] = ( obj_base.Pager( sorts=[('network_id', True), ('segment_index', True)])) class NetworkDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = network.Network @mock.patch.object(policy.QosPolicy, 'unset_default') def test_qos_policy_id(self, *mocks): policy_obj = policy.QosPolicy(self.context) policy_obj.create() obj = self._make_object(self.obj_fields[0]) obj.qos_policy_id = policy_obj.id obj.create() obj = network.Network.get_object(self.context, id=obj.id) self.assertEqual(policy_obj.id, obj.qos_policy_id) policy_obj2 = policy.QosPolicy(self.context) policy_obj2.create() obj.qos_policy_id = policy_obj2.id obj.update() obj = network.Network.get_object(self.context, id=obj.id) self.assertEqual(policy_obj2.id, obj.qos_policy_id) obj.qos_policy_id = None obj.update() obj = network.Network.get_object(self.context, id=obj.id) self.assertIsNone(obj.qos_policy_id) @mock.patch.object(policy.QosPolicy, 'unset_default') def test__attach_qos_policy(self, *mocks): obj = self._make_object(self.obj_fields[0]) obj.create() policy_obj = policy.QosPolicy(self.context) policy_obj.create() obj._attach_qos_policy(policy_obj.id) obj = network.Network.get_object(self.context, id=obj.id) self.assertEqual(policy_obj.id, obj.qos_policy_id) qos_binding_obj = binding.QosPolicyNetworkBinding.get_object( self.context, network_id=obj.id) self.assertEqual(qos_binding_obj.policy_id, obj.qos_policy_id) old_policy_id = policy_obj.id policy_obj2 = policy.QosPolicy(self.context) policy_obj2.create() obj._attach_qos_policy(policy_obj2.id) obj = network.Network.get_object(self.context, id=obj.id) self.assertEqual(policy_obj2.id, obj.qos_policy_id) qos_binding_obj2 = binding.QosPolicyNetworkBinding.get_object( self.context, network_id=obj.id) self.assertEqual(qos_binding_obj2.policy_id, obj.qos_policy_id) qos_binding_obj = binding.QosPolicyNetworkBinding.get_objects( self.context, policy_id=old_policy_id) self.assertEqual(0, len(qos_binding_obj)) def test_dns_domain(self): obj = self._make_object(self.obj_fields[0]) obj.dns_domain = 'foo.com' obj.create() obj = network.Network.get_object(self.context, id=obj.id) self.assertEqual('foo.com', obj.dns_domain) obj.dns_domain = 'bar.com' obj.update() obj = network.Network.get_object(self.context, id=obj.id) self.assertEqual('bar.com', obj.dns_domain) obj.dns_domain = None obj.update() obj = network.Network.get_object(self.context, id=obj.id) self.assertIsNone(obj.dns_domain) def test__set_dns_domain(self): obj = self._make_object(self.obj_fields[0]) obj.create() obj._set_dns_domain('foo.com') obj = network.Network.get_object(self.context, id=obj.id) self.assertEqual('foo.com', obj.dns_domain) obj._set_dns_domain('bar.com') obj = network.Network.get_object(self.context, id=obj.id) self.assertEqual('bar.com', obj.dns_domain) class SegmentHostMappingIfaceObjectTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = network.SegmentHostMapping class SegmentHostMappingDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = network.SegmentHostMapping def setUp(self): super(SegmentHostMappingDbObjectTestCase, self).setUp() self.update_obj_fields( {'segment_id': lambda: self._create_test_segment_id()}) class NetworkDNSDomainIfaceObjectTestcase( obj_test_base.BaseObjectIfaceTestCase): _test_class = network.NetworkDNSDomain class NetworkDNSDomainDbObjectTestcase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = network.NetworkDNSDomain def setUp(self): super(NetworkDNSDomainDbObjectTestcase, self).setUp() self.update_obj_fields( {'network_id': lambda: self._create_test_network_id()}) class ExternalNetworkIfaceObjectTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = network.ExternalNetwork class ExternalNetworkDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = network.ExternalNetwork def setUp(self): super(ExternalNetworkDbObjectTestCase, self).setUp() self.update_obj_fields( {'network_id': lambda: self._create_test_network_id()}) neutron-12.1.1/neutron/tests/unit/objects/db/0000775000175000017500000000000013553660157021154 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/db/test_api.py0000664000175000017500000001731213553660047023340 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import mock from neutron_lib import context from neutron_lib import exceptions as n_exc from neutron.db import _model_query as model_query from neutron.objects import base from neutron.objects.db import api from neutron.objects import network from neutron.objects import utils as obj_utils from neutron.tests import base as test_base from neutron.tests.unit import testlib_api PLUGIN_NAME = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' class FakeModel(object): def __init__(self, *args, **kwargs): pass class FakeObj(base.NeutronDbObject): db_model = FakeModel class GetObjectsTestCase(test_base.BaseTestCase): def setUp(self): super(GetObjectsTestCase, self).setUp() # TODO(ihrachys): revisit plugin setup once we decouple # objects.db.objects.api from core plugin instance self.setup_coreplugin(PLUGIN_NAME) def test_get_objects_pass_marker_obj_when_limit_and_marker_passed(self): ctxt = context.get_admin_context() marker = mock.sentinel.marker limit = mock.sentinel.limit pager = base.Pager(marker=marker, limit=limit) with mock.patch.object( model_query, 'get_collection') as get_collection: with mock.patch.object(api, 'get_object') as get_object: api.get_objects(FakeObj, ctxt, _pager=pager) get_object.assert_called_with(FakeObj, ctxt, id=marker) get_collection.assert_called_with( ctxt, FakeObj.db_model, dict_func=None, filters={}, limit=limit, marker_obj=get_object.return_value) class CreateObjectTestCase(test_base.BaseTestCase): def test_populate_id(self, populate_id=True): ctxt = context.get_admin_context() values = {'x': 1, 'y': 2, 'z': 3} with mock.patch.object(FakeObj, 'db_model') as db_model_mock: with mock.patch.object(ctxt.__class__, 'session'): api.create_object(FakeObj, ctxt, values, populate_id=populate_id) expected = copy.copy(values) if populate_id: expected['id'] = mock.ANY db_model_mock.assert_called_with(**expected) def test_populate_id_False(self): self.test_populate_id(populate_id=False) class CRUDScenarioTestCase(testlib_api.SqlTestCase): CORE_PLUGIN = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' def setUp(self): super(CRUDScenarioTestCase, self).setUp() # TODO(ihrachys): revisit plugin setup once we decouple # neutron.objects.db.api from core plugin instance self.setup_coreplugin(self.CORE_PLUGIN) # NOTE(ihrachys): nothing specific to networks in this test case, but # we needed to pick some real object, so we picked the network. Any # other object would work as well for our needs here. self.obj_cls = network.Network self.ctxt = context.get_admin_context() def test_get_object_with_None_value_in_filters(self): obj = api.create_object(self.obj_cls, self.ctxt, {'name': 'foo'}) new_obj = api.get_object( self.obj_cls, self.ctxt, name='foo', status=None) self.assertEqual(obj, new_obj) def test_get_objects_with_None_value_in_filters(self): obj = api.create_object(self.obj_cls, self.ctxt, {'name': 'foo'}) new_objs = api.get_objects( self.obj_cls, self.ctxt, name='foo', status=None) self.assertEqual(obj, new_objs[0]) def test_get_objects_with_string_matching_filters_contains(self): obj1 = api.create_object( self.obj_cls, self.ctxt, {'name': 'obj_con_1'}) obj2 = api.create_object( self.obj_cls, self.ctxt, {'name': 'obj_con_2'}) obj3 = api.create_object( self.obj_cls, self.ctxt, {'name': 'obj_3'}) objs = api.get_objects( self.obj_cls, self.ctxt, name=obj_utils.StringContains('con')) self.assertEqual(2, len(objs)) self.assertIn(obj1, objs) self.assertIn(obj2, objs) self.assertNotIn(obj3, objs) def test_get_objects_with_string_matching_filters_starts(self): obj1 = api.create_object(self.obj_cls, self.ctxt, {'name': 'pre_obj1'}) obj2 = api.create_object(self.obj_cls, self.ctxt, {'name': 'pre_obj2'}) obj3 = api.create_object(self.obj_cls, self.ctxt, {'name': 'obj_3'}) objs = api.get_objects( self.obj_cls, self.ctxt, name=obj_utils.StringStarts('pre')) self.assertEqual(2, len(objs)) self.assertIn(obj1, objs) self.assertIn(obj2, objs) self.assertNotIn(obj3, objs) def test_get_objects_with_string_matching_filters_ends(self): obj1 = api.create_object(self.obj_cls, self.ctxt, {'name': 'obj1_end'}) obj2 = api.create_object(self.obj_cls, self.ctxt, {'name': 'obj2_end'}) obj3 = api.create_object(self.obj_cls, self.ctxt, {'name': 'obj_3'}) objs = api.get_objects( self.obj_cls, self.ctxt, name=obj_utils.StringEnds('end')) self.assertEqual(2, len(objs)) self.assertIn(obj1, objs) self.assertIn(obj2, objs) self.assertNotIn(obj3, objs) def test_get_object_create_update_delete(self): obj = api.create_object(self.obj_cls, self.ctxt, {'name': 'foo'}) new_obj = api.get_object(self.obj_cls, self.ctxt, id=obj.id) self.assertEqual(obj, new_obj) obj = new_obj api.update_object(self.obj_cls, self.ctxt, {'name': 'bar'}, id=obj.id) new_obj = api.get_object(self.obj_cls, self.ctxt, id=obj.id) self.assertEqual(obj, new_obj) obj = new_obj api.delete_object(self.obj_cls, self.ctxt, id=obj.id) new_obj = api.get_object(self.obj_cls, self.ctxt, id=obj.id) self.assertIsNone(new_obj) # delete_object raises an exception on missing object self.assertRaises( n_exc.ObjectNotFound, api.delete_object, self.obj_cls, self.ctxt, id=obj.id) # but delete_objects does not not api.delete_objects(self.obj_cls, self.ctxt, id=obj.id) def test_delete_objects_removes_all_matching_objects(self): # create some objects with identical description for i in range(10): api.create_object( self.obj_cls, self.ctxt, {'name': 'foo%d' % i, 'description': 'bar'}) # create some more objects with a different description descriptions = set() for i in range(10, 20): desc = 'bar%d' % i descriptions.add(desc) api.create_object( self.obj_cls, self.ctxt, {'name': 'foo%d' % i, 'description': desc}) # make sure that all objects are in the database self.assertEqual(20, api.count(self.obj_cls, self.ctxt)) # now delete just those with the 'bar' description api.delete_objects(self.obj_cls, self.ctxt, description='bar') # check that half of objects are gone, and remaining have expected # descriptions objs = api.get_objects(self.obj_cls, self.ctxt) self.assertEqual(10, len(objs)) self.assertEqual( descriptions, {obj.description for obj in objs}) neutron-12.1.1/neutron/tests/unit/objects/db/__init__.py0000664000175000017500000000000013553660046023250 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/test_provisioning_blocks.py0000664000175000017500000000244413553660047026265 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import provisioning_blocks from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class ProvisioningBlockIfaceObjectTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = provisioning_blocks.ProvisioningBlock class ProvisioningBlockDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = provisioning_blocks.ProvisioningBlock def setUp(self): super(ProvisioningBlockDbObjectTestCase, self).setUp() self.update_obj_fields( { 'standard_attr_id': lambda: self._create_test_standard_attribute_id() }) neutron-12.1.1/neutron/tests/unit/objects/test_rbac_db.py0000664000175000017500000003461513553660047023563 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import events from neutron_lib import context as n_context from neutron_lib.db import model_base from neutron_lib import exceptions as n_exc from oslo_versionedobjects import fields as obj_fields import sqlalchemy as sa from neutron.db import rbac_db_models from neutron.extensions import rbac as ext_rbac from neutron.objects import base from neutron.objects import common_types from neutron.objects.db import api as obj_db_api from neutron.objects import rbac_db from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api class FakeDbModel(dict): pass class FakeRbacModel(rbac_db_models.RBACColumns, model_base.BASEV2): object_id = sa.Column(sa.String(36), nullable=False) object_type = 'fake_rbac_object' def get_valid_actions(self): return (rbac_db_models.ACCESS_SHARED,) @base.NeutronObjectRegistry.register_if(False) class FakeNeutronRbacObject(base.NeutronDbObject): VERSION = '1.0' db_model = FakeRbacModel fields = { 'object_id': obj_fields.StringField(), 'target_tenant': obj_fields.StringField(), 'action': obj_fields.StringField(), } @base.NeutronObjectRegistry.register_if(False) class FakeNeutronDbObject(rbac_db.NeutronRbacObject): # Version 1.0: Initial version VERSION = '1.0' rbac_db_cls = FakeNeutronRbacObject db_model = FakeDbModel fields = { 'id': common_types.UUIDField(), 'field1': obj_fields.StringField(), 'field2': obj_fields.StringField(), 'shared': obj_fields.BooleanField(default=False), } fields_no_update = ['id'] synthetic_fields = ['field2'] def get_bound_tenant_ids(cls, context, policy_id): pass class RbacNeutronDbObjectTestCase(test_base.BaseObjectIfaceTestCase, testlib_api.SqlTestCase): _test_class = FakeNeutronDbObject def setUp(self): super(RbacNeutronDbObjectTestCase, self).setUp() FakeNeutronDbObject.update_post = mock.Mock() @mock.patch.object(_test_class.rbac_db_cls, 'db_model') def test_get_tenants_with_shared_access_to_db_obj_return_tenant_ids( self, *mocks): ctx = mock.Mock() fake_ids = {'tenant_id_' + str(i) for i in range(10)} ctx.session.query.return_value.filter.return_value = [ (fake_id,) for fake_id in fake_ids] ret_ids = self._test_class._get_tenants_with_shared_access_to_db_obj( ctx, 'fake_db_obj_id') self.assertEqual(fake_ids, ret_ids) def test_is_accessible_for_admin(self): ctx = mock.Mock(is_admin=True, tenant_id='we_dont_care') self.assertTrue(self._test_class.is_accessible(ctx, None)) def test_is_accessible_for_db_object_owner(self): ctx = mock.Mock(is_admin=False, tenant_id='db_object_owner') db_obj = mock.Mock(tenant_id=ctx.tenant_id) self.assertTrue(self._test_class.is_accessible(ctx, db_obj)) @mock.patch.object(_test_class, 'is_shared_with_tenant', return_value=True) def test_is_accessible_if_shared_with_tenant(self, mock_is_shared): ctx = mock.Mock(is_admin=False, tenant_id='db_object_shareholder') db_obj = mock.Mock(tenant_id='db_object_owner') self.assertTrue(self._test_class.is_accessible(ctx, db_obj)) mock_is_shared.assert_called_once_with( mock.ANY, db_obj.id, ctx.tenant_id) @mock.patch.object(_test_class, 'is_shared_with_tenant', return_value=False) def test_is_accessible_fails_for_unauthorized_tenant(self, mock_is_shared): ctx = mock.Mock(is_admin=False, tenant_id='Billy_the_kid') db_obj = mock.Mock(tenant_id='db_object_owner') self.assertFalse(self._test_class.is_accessible(ctx, db_obj)) mock_is_shared.assert_called_once_with( mock.ANY, db_obj.id, ctx.tenant_id) def _rbac_policy_generate_change_events(self, resource, trigger, context, object_type, policy, event_list): for event in event_list: self._test_class.validate_rbac_policy_change( resource, event, trigger, context, object_type, policy) @mock.patch.object(_test_class, 'validate_rbac_policy_update') def test_validate_rbac_policy_change_handles_only_object_type( self, mock_validate_rbac_update): self._rbac_policy_generate_change_events( resource=None, trigger='dummy_trigger', context=None, object_type='dummy_object_type', policy=None, event_list=(events.BEFORE_CREATE, events.BEFORE_UPDATE, events.BEFORE_DELETE)) mock_validate_rbac_update.assert_not_called() @mock.patch.object(_test_class, 'validate_rbac_policy_update') @mock.patch.object(obj_db_api, 'get_object', return_value={'tenant_id': 'tyrion_lannister'}) def test_validate_rbac_policy_change_allowed_for_admin_or_owner( self, mock_get_object, mock_validate_update): context = mock.Mock(is_admin=True, tenant_id='db_obj_owner_id') self._rbac_policy_generate_change_events( resource=None, trigger='dummy_trigger', context=context, object_type=self._test_class.rbac_db_cls.db_model.object_type, policy={'object_id': 'fake_object_id'}, event_list=(events.BEFORE_CREATE, events.BEFORE_UPDATE)) self.assertTrue(self._test_class.validate_rbac_policy_update.called) @mock.patch.object(_test_class, 'validate_rbac_policy_update') @mock.patch.object(obj_db_api, 'get_object', return_value={'tenant_id': 'king_beyond_the_wall'}) def test_validate_rbac_policy_change_forbidden_for_outsiders( self, mock_get_object, mock_validate_update): context = mock.Mock(is_admin=False, tenant_id='db_obj_owner_id') self.assertRaises( n_exc.InvalidInput, self._rbac_policy_generate_change_events, resource=mock.Mock(), trigger='dummy_trigger', context=context, object_type=self._test_class.rbac_db_cls.db_model.object_type, policy={'object_id': 'fake_object_id'}, event_list=(events.BEFORE_CREATE, events.BEFORE_UPDATE)) self.assertFalse(mock_validate_update.called) @mock.patch.object(_test_class, '_validate_rbac_policy_delete') def _test_validate_rbac_policy_delete_handles_policy( self, policy, mock_validate_delete): self._test_class.validate_rbac_policy_delete( resource=mock.Mock(), event=events.BEFORE_DELETE, trigger='dummy_trigger', context=n_context.get_admin_context(), object_type=self._test_class.rbac_db_cls.db_model.object_type, policy=policy) mock_validate_delete.assert_not_called() def test_validate_rbac_policy_delete_handles_shared_action(self): self._test_validate_rbac_policy_delete_handles_policy( {'action': 'unknown_action'}) @mock.patch.object(obj_db_api, 'get_object') def test_validate_rbac_policy_delete_skips_db_object_owner(self, mock_get_object): policy = {'action': rbac_db_models.ACCESS_SHARED, 'target_tenant': 'fake_tenant_id', 'object_id': 'fake_obj_id', 'tenant_id': 'fake_tenant_id'} mock_get_object.return_value.tenant_id = policy['target_tenant'] self._test_validate_rbac_policy_delete_handles_policy(policy) @mock.patch.object(obj_db_api, 'get_object') @mock.patch.object(_test_class, 'get_bound_tenant_ids', return_value='tenant_id_shared_with') def test_validate_rbac_policy_delete_fails_single_tenant_and_in_use( self, get_bound_tenant_ids_mock, mock_get_object): policy = {'action': rbac_db_models.ACCESS_SHARED, 'target_tenant': 'tenant_id_shared_with', 'tenant_id': 'object_owner_tenant_id', 'object_id': 'fake_obj_id'} context = mock.Mock() with mock.patch.object( self._test_class, '_get_db_obj_rbac_entries') as target_tenants_mock: filter_mock = target_tenants_mock.return_value.filter filter_mock.return_value.count.return_value = 0 self.assertRaises( ext_rbac.RbacPolicyInUse, self._test_class.validate_rbac_policy_delete, resource=None, event=events.BEFORE_DELETE, trigger='dummy_trigger', context=context, object_type=self._test_class.rbac_db_cls.db_model.object_type, policy=policy) def test_validate_rbac_policy_delete_not_bound_tenant_success(self): context = mock.Mock() with mock.patch.object( self._test_class, 'get_bound_tenant_ids', return_value={'fake_tid2', 'fake_tid3'}), \ mock.patch.object(self._test_class, '_get_db_obj_rbac_entries') as get_rbac_entries_mock, \ mock.patch.object( self._test_class, '_get_tenants_with_shared_access_to_db_obj') as sh_tids: get_rbac_entries_mock.filter.return_value.count.return_value = 0 self._test_class._validate_rbac_policy_delete( context=context, obj_id='fake_obj_id', target_tenant='fake_tid1') sh_tids.assert_not_called() @mock.patch.object(_test_class, '_get_db_obj_rbac_entries') @mock.patch.object(_test_class, '_get_tenants_with_shared_access_to_db_obj', return_value=['some_other_tenant']) @mock.patch.object(_test_class, 'get_bound_tenant_ids', return_value={'fake_id1'}) def test_validate_rbac_policy_delete_fails_single_used_wildcarded( self, get_bound_tenant_ids_mock, mock_tenants_with_shared_access, _get_db_obj_rbac_entries_mock): policy = {'action': rbac_db_models.ACCESS_SHARED, 'target_tenant': '*', 'tenant_id': 'object_owner_tenant_id', 'object_id': 'fake_obj_id'} context = mock.Mock() with mock.patch.object(obj_db_api, 'get_object'): self.assertRaises( ext_rbac.RbacPolicyInUse, self._test_class.validate_rbac_policy_delete, resource=mock.Mock(), event=events.BEFORE_DELETE, trigger='dummy_trigger', context=context, object_type=self._test_class.rbac_db_cls.db_model.object_type, policy=policy) @mock.patch.object(_test_class, 'attach_rbac') @mock.patch.object(obj_db_api, 'get_object', return_value=['fake_rbac_policy']) @mock.patch.object(_test_class, '_validate_rbac_policy_delete') def test_update_shared_avoid_duplicate_update( self, mock_validate_delete, get_object_mock, attach_rbac_mock): obj_id = 'fake_obj_id' obj = self._test_class(mock.Mock()) obj.update_shared(is_shared_new=True, obj_id=obj_id) get_object_mock.assert_called_with( obj.rbac_db_cls, mock.ANY, object_id=obj_id, target_tenant='*', action=rbac_db_models.ACCESS_SHARED) self.assertFalse(mock_validate_delete.called) self.assertFalse(attach_rbac_mock.called) @mock.patch.object(_test_class, 'attach_rbac') @mock.patch.object(obj_db_api, 'get_object', return_value=[]) @mock.patch.object(_test_class, '_validate_rbac_policy_delete') def test_update_shared_wildcard( self, mock_validate_delete, get_object_mock, attach_rbac_mock): obj_id = 'fake_obj_id' test_neutron_obj = self._test_class(mock.Mock()) test_neutron_obj.update_shared(is_shared_new=True, obj_id=obj_id) get_object_mock.assert_called_with( test_neutron_obj.rbac_db_cls, mock.ANY, object_id=obj_id, target_tenant='*', action=rbac_db_models.ACCESS_SHARED) attach_rbac_mock.assert_called_with( obj_id, test_neutron_obj.obj_context.tenant_id) def test_shared_field_false_without_context(self): test_neutron_obj = self._test_class() self.assertFalse(test_neutron_obj.to_dict()['shared']) @mock.patch.object(_test_class, 'attach_rbac') @mock.patch.object(obj_db_api, 'get_object', return_value=['fake_rbac_policy']) @mock.patch.object(_test_class, '_validate_rbac_policy_delete') def test_update_shared_remove_wildcard_sharing( self, mock_validate_delete, get_object_mock, attach_rbac_mock): obj_id = 'fake_obj_id' obj = self._test_class(mock.Mock()) obj.update_shared(is_shared_new=False, obj_id=obj_id) get_object_mock.assert_called_with( obj.rbac_db_cls, mock.ANY, object_id=obj_id, target_tenant='*', action=rbac_db_models.ACCESS_SHARED) self.assertFalse(attach_rbac_mock.attach_rbac.called) mock_validate_delete.assert_called_with(mock.ANY, obj_id, '*') @mock.patch.object(_test_class, 'create_rbac_policy') def test_attach_rbac_returns_type(self, create_rbac_mock): obj_id = 'fake_obj_id' tenant_id = 'fake_tenant_id' target_tenant = 'fake_target_tenant' self._test_class(mock.Mock()).attach_rbac(obj_id, tenant_id, target_tenant) rbac_pol = create_rbac_mock.call_args_list[0][0][1]['rbac_policy'] self.assertEqual(rbac_pol['object_id'], obj_id) self.assertEqual(rbac_pol['target_tenant'], target_tenant) self.assertEqual(rbac_pol['action'], rbac_db_models.ACCESS_SHARED) self.assertEqual(rbac_pol['object_type'], self._test_class.rbac_db_cls.db_model.object_type) neutron-12.1.1/neutron/tests/unit/objects/test_subnet.py0000664000175000017500000002362013553660047023501 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from oslo_utils import uuidutils from neutron.db import rbac_db_models from neutron.objects import base as obj_base from neutron.objects.db import api as obj_db_api from neutron.objects import network as net_obj from neutron.objects import rbac_db from neutron.objects import subnet from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class IPAllocationPoolObjectIfaceTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = subnet.IPAllocationPool class IPAllocationPoolDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = subnet.IPAllocationPool def setUp(self): super(IPAllocationPoolDbObjectTestCase, self).setUp() self.update_obj_fields( {'subnet_id': lambda: self._create_test_subnet_id()}) class DNSNameServerObjectIfaceTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = subnet.DNSNameServer def setUp(self): super(DNSNameServerObjectIfaceTestCase, self).setUp() self.pager_map[self._test_class.obj_name()] = ( obj_base.Pager(sorts=[('order', True)])) class DNSNameServerDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = subnet.DNSNameServer def setUp(self): super(DNSNameServerDbObjectTestCase, self).setUp() self._subnet_id = self._create_test_subnet_id() self.update_obj_fields({'subnet_id': self._subnet_id}) def _create_dnsnameservers(self): for obj in self.obj_fields: dns = self._make_object(obj) dns.create() def test_get_objects_sort_by_order_asc(self): self._create_dnsnameservers() objs = self._test_class.get_objects(self.context) fields_sorted = sorted([obj['order'] for obj in self.obj_fields]) self.assertEqual(fields_sorted, [obj.order for obj in objs]) def test_get_objects_sort_by_order_desc(self): self._create_dnsnameservers() pager = obj_base.Pager(sorts=[('order', False)]) objs = self._test_class.get_objects(self.context, _pager=pager, subnet_id=self._subnet_id) fields_sorted = sorted([obj['order'] for obj in self.obj_fields], reverse=True) self.assertEqual(fields_sorted, [obj.order for obj in objs]) def test_get_objects_sort_by_address_asc_using_pager(self): self._create_dnsnameservers() pager = obj_base.Pager(sorts=[('address', True)]) objs = self._test_class.get_objects(self.context, _pager=pager) fields_sorted = sorted([obj['address'] for obj in self.obj_fields]) self.assertEqual(fields_sorted, [obj.address for obj in objs]) class RouteObjectIfaceTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = subnet.Route class RouteDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = subnet.Route def setUp(self): super(RouteDbObjectTestCase, self).setUp() self.update_obj_fields( {'subnet_id': lambda: self._create_test_subnet_id()}) class SubnetServiceTypeObjectIfaceTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = subnet.SubnetServiceType class SubnetServiceTypeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = subnet.SubnetServiceType def setUp(self): super(SubnetServiceTypeDbObjectTestCase, self).setUp() self.update_obj_fields( {'subnet_id': lambda: self._create_test_subnet_id()}) class SubnetObjectIfaceTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = subnet.Subnet def setUp(self): super(SubnetObjectIfaceTestCase, self).setUp() self.pager_map[subnet.DNSNameServer.obj_name()] = ( obj_base.Pager(sorts=[('order', True)])) # Base class will mock those out only when rbac_db_model is set for the # object. Since subnets don't have their own models but only derive # shared value from networks, we need to unconditionally mock those # entry points out here, otherwise they will trigger database access, # which is not allowed in 'Iface' test classes. mock.patch.object( rbac_db.RbacNeutronDbObjectMixin, 'is_shared_with_tenant', return_value=False).start() mock.patch.object( rbac_db.RbacNeutronDbObjectMixin, 'get_shared_with_tenant').start() class SubnetDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = subnet.Subnet def setUp(self): super(SubnetDbObjectTestCase, self).setUp() network_id = self._create_test_network_id() self.update_obj_fields( {'network_id': network_id, 'segment_id': lambda: self._create_test_segment_id(network_id)}) def test_get_dns_nameservers_in_order(self): obj = self._make_object(self.obj_fields[0]) obj.create() dns_nameservers = [(2, '1.2.3.4'), (1, '5.6.7.8'), (4, '7.7.7.7')] for order, address in dns_nameservers: dns = subnet.DNSNameServer(self.context, order=order, address=address, subnet_id=obj.id) dns.create() new = self._test_class.get_object(self.context, id=obj.id) self.assertEqual(1, new.dns_nameservers[0].order) self.assertEqual(2, new.dns_nameservers[1].order) self.assertEqual(4, new.dns_nameservers[-1].order) def _create_shared_network_rbac_entry(self, network): attrs = { 'object_id': network['id'], 'target_tenant': '*', 'action': rbac_db_models.ACCESS_SHARED } obj_db_api.create_object(net_obj.NetworkRBAC, self.context, attrs) def test_get_subnet_shared_true(self): network = self._create_test_network() self._create_shared_network_rbac_entry(network) subnet_data = dict(self.obj_fields[0]) subnet_data['network_id'] = network['id'] obj = self._make_object(subnet_data) # check if shared will be load by 'obj_load_attr' and using extra query # by RbacNeutronDbObjectMixin get_shared_with_tenant self.assertTrue(obj.shared) obj.create() # here the shared should be load by is_network_shared self.assertTrue(obj.shared) new = self._test_class.get_object(self.context, **obj._get_composite_keys()) # again, the shared should be load by is_network_shared self.assertTrue(new.shared) def test_filter_by_shared(self): network = self._create_test_network() self._create_shared_network_rbac_entry(network) subnet_data = dict(self.obj_fields[0]) subnet_data['network_id'] = network['id'] obj = self._make_object(subnet_data) obj.create() result = self._test_class.get_objects(self.context, shared=True) self.assertEqual(obj, result[0]) def test_get_shared_subnet_with_another_tenant(self): network_shared = self._create_test_network() self._create_shared_network_rbac_entry(network_shared) subnet_data = dict(self.obj_fields[0]) subnet_data['network_id'] = network_shared['id'] shared_subnet = self._make_object(subnet_data) shared_subnet.create() priv_subnet = self._make_object(self.obj_fields[1]) priv_subnet.create() # Situation here: # - we have one network with a subnet that are private # - shared network with its subnet # creating new context, user should have access to one shared network all_subnets = self._test_class.get_objects(self.context) self.assertEqual(2, len(all_subnets)) # access with new tenant_id, should be able to access to one subnet new_ctx = context.Context('', uuidutils.generate_uuid()) public_subnets = self._test_class.get_objects(new_ctx) self.assertEqual([shared_subnet], public_subnets) # test get_object to fetch the private and then the shared subnet fetched_private_subnet = self._test_class.get_object(new_ctx, id=priv_subnet.id) self.assertIsNone(fetched_private_subnet) fetched_public_subnet = ( self._test_class.get_object(new_ctx, id=shared_subnet.id)) self.assertEqual(shared_subnet, fetched_public_subnet) def test_get_service_types(self): obj = self._make_object(self.obj_fields[0]) obj.create() service_type_obj = subnet.SubnetServiceType( self.context, subnet_id=obj.id, service_type='dhcp-agent') service_type_obj.create() listed_obj = subnet.Subnet.get_object(self.context, id=obj.id) self.assertEqual([service_type_obj.service_type], listed_obj.service_types) # Try to load the service_types by obj_load_attr obj1 = self._make_object(self.obj_fields[0]) self.assertEqual([service_type_obj.service_type], obj1.service_types) neutron-12.1.1/neutron/tests/unit/objects/test_flavor.py0000664000175000017500000000456513553660047023501 0ustar zuulzuul00000000000000# Copyright 2016 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import flavor from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class FlavorServiceProfileBindingIfaceObjectTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = flavor.FlavorServiceProfileBinding class FlavorServiceProfileBindingDbObjectTestCase( obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = flavor.FlavorServiceProfileBinding def setUp(self): super(FlavorServiceProfileBindingDbObjectTestCase, self).setUp() self.update_obj_fields( {'flavor_id': lambda: self._create_test_flavor_id(), 'service_profile_id': lambda: self._create_test_service_profile_id()}) class ServiceProfileIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = flavor.ServiceProfile class ServiceProfileDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = flavor.ServiceProfile def test_get_objects_queries_constant(self): # FIXME(electrocucaracha): There are no lazy loading for flavors # relationship in ServiceProfile model db disable this UT to avoid # failing pass class FlavorIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase): _test_class = flavor.Flavor class FlavorDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = flavor.Flavor def test_get_objects_queries_constant(self): # FIXME(electrocucaracha): There are no lazy loading for # service_profiles relationship in Flavor model db disable this UT to # avoid failing pass neutron-12.1.1/neutron/tests/unit/objects/test_auto_allocate.py0000664000175000017500000000253313553660047025015 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import auto_allocate from neutron.tests.unit.objects import test_base as obj_test_base from neutron.tests.unit import testlib_api class AutoAllocateTopologyIfaceObjectTestCase( obj_test_base.BaseObjectIfaceTestCase): _test_class = auto_allocate.AutoAllocatedTopology class AutoAllocateTopologyDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = auto_allocate.AutoAllocatedTopology def setUp(self): super(AutoAllocateTopologyDbObjectTestCase, self).setUp() self.update_obj_fields({ 'network_id': lambda: self._create_test_network_id(), 'router_id': lambda: self._create_test_router_id()}) neutron-12.1.1/neutron/tests/unit/objects/test_securitygroup.py0000664000175000017500000001340313553660047025123 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import securitygroup from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api class SecurityGroupIfaceObjTestCase(test_base.BaseObjectIfaceTestCase): _test_class = securitygroup.SecurityGroup class SecurityGroupDbObjTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = securitygroup.SecurityGroup def setUp(self): super(SecurityGroupDbObjTestCase, self).setUp() # TODO(ihrachys): consider refactoring base test class to set None for # all nullable fields for db_obj in self.db_objs: for rule in db_obj['rules']: # we either make it null, or create remote groups for each rule # generated; we picked the former here rule['remote_group_id'] = None def test_is_default_True(self): fields = self.obj_fields[0].copy() sg_obj = self._make_object(fields) sg_obj.is_default = True sg_obj.create() default_sg_obj = securitygroup.DefaultSecurityGroup.get_object( self.context, project_id=sg_obj.project_id, security_group_id=sg_obj.id) self.assertIsNotNone(default_sg_obj) sg_obj = securitygroup.SecurityGroup.get_object( self.context, id=sg_obj.id, project_id=sg_obj.project_id ) self.assertTrue(sg_obj.is_default) def test_is_default_False(self): fields = self.obj_fields[0].copy() sg_obj = self._make_object(fields) sg_obj.is_default = False sg_obj.create() default_sg_obj = securitygroup.DefaultSecurityGroup.get_object( self.context, project_id=sg_obj.project_id, security_group_id=sg_obj.id) self.assertIsNone(default_sg_obj) sg_obj = securitygroup.SecurityGroup.get_object( self.context, id=sg_obj.id, project_id=sg_obj.project_id ) self.assertFalse(sg_obj.is_default) def test_get_object_filter_by_is_default(self): fields = self.obj_fields[0].copy() sg_obj = self._make_object(fields) sg_obj.is_default = True sg_obj.create() listed_obj = securitygroup.SecurityGroup.get_object( self.context, id=sg_obj.id, project_id=sg_obj.project_id, is_default=True ) self.assertIsNotNone(listed_obj) self.assertEqual(sg_obj, listed_obj) def test_get_objects_queries_constant(self): # TODO(electrocucaracha) SecurityGroup is using SecurityGroupRule # object to reload rules, which costs extra SQL query each time # is_default field is loaded as part of get_object(s). SecurityGroup # has defined relationship for SecurityGroupRules, so it should be # possible to reuse side loaded values fo this. To be reworked in # follow-up patch. pass def test_get_object_no_synth(self): fields = self.obj_fields[0].copy() sg_obj = self._make_object(fields) sg_obj.is_default = True sg_obj.create() listed_obj = securitygroup.SecurityGroup.get_object( self.context, fields=['id', 'name'], id=sg_obj.id, project_id=sg_obj.project_id ) self.assertIsNotNone(listed_obj) self.assertEqual(len(sg_obj.rules), 0) self.assertIsNone(listed_obj.rules) def test_get_objects_no_synth(self): fields = self.obj_fields[0].copy() sg_obj = self._make_object(fields) sg_obj.is_default = True sg_obj.create() listed_objs = securitygroup.SecurityGroup.get_objects( self.context, fields=['id', 'name'], id=sg_obj.id, project_id=sg_obj.project_id ) self.assertEqual(len(listed_objs), 1) self.assertEqual(len(sg_obj.rules), 0) self.assertIsNone(listed_objs[0].rules) class DefaultSecurityGroupIfaceObjTestCase(test_base.BaseObjectIfaceTestCase): _test_class = securitygroup.DefaultSecurityGroup class DefaultSecurityGroupDbObjTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = securitygroup.DefaultSecurityGroup def setUp(self): super(DefaultSecurityGroupDbObjTestCase, self).setUp() self.update_obj_fields( { 'security_group_id': lambda: self._create_test_security_group_id() }) class SecurityGroupRuleIfaceObjTestCase(test_base.BaseObjectIfaceTestCase): _test_class = securitygroup.SecurityGroupRule class SecurityGroupRuleDbObjTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = securitygroup.SecurityGroupRule def setUp(self): super(SecurityGroupRuleDbObjTestCase, self).setUp() self.update_obj_fields( { 'security_group_id': lambda: self._create_test_security_group_id(), 'remote_group_id': lambda: self._create_test_security_group_id() }) neutron-12.1.1/neutron/tests/unit/objects/logapi/0000775000175000017500000000000013553660157022042 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/logapi/__init__.py0000664000175000017500000000000013553660046024136 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/objects/logapi/test_logging_resource.py0000664000175000017500000000701013553660046027003 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_utils import uuidutils from neutron.objects.logapi import logging_resource as log_res from neutron.objects import securitygroup from neutron.tests.unit.objects import test_base from neutron.tests.unit import testlib_api class LogObjectTestCase(test_base.BaseObjectIfaceTestCase): _test_class = log_res.Log class LogDBObjectTestCase(test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = log_res.Log def setUp(self): super(LogDBObjectTestCase, self).setUp() self._network_id = self._create_test_network_id() self._port_id = self._create_test_port_id(network_id=self._network_id) self._security_group = self._create_test_security_group() self.update_obj_fields({'resource_id': self._security_group['id'], 'target_id': self._port_id}) def _create_test_security_group(self): sg_fields = self.get_random_object_fields(securitygroup.SecurityGroup) sg_obj = securitygroup.SecurityGroup(self.context, **sg_fields) return sg_obj def test_create_sg_log_with_secgroup(self): sg = self._create_test_security_group() sg_log = log_res.Log(context=self.context, id=uuidutils.generate_uuid(), name='test-create', resource_type='security_group', resource_id=sg.id, enabled=False) sg_log.create() self.assertEqual(sg.id, sg_log.resource_id) def test_create_sg_log_with_port(self): port_id = self._create_test_port_id(network_id=self._network_id) sg_log = log_res.Log(context=self.context, id=uuidutils.generate_uuid(), name='test-create', resource_type='security_group', target_id=port_id, enabled=False) sg_log.create() self.assertEqual(port_id, sg_log.target_id) def test_update_multiple_log_fields(self): sg_log = log_res.Log(context=self.context, id=uuidutils.generate_uuid(), name='test-create', description='test-description', resource_type='security_group', enabled=False) sg_log.create() fields = {'name': 'test-update', 'description': 'test-update-descr', 'enabled': True} sg_log.update_fields(fields) sg_log.update() new_sg_log = log_res.Log.get_object(self.context, id=sg_log.id) self._assert_attrs(new_sg_log, **fields) def _assert_attrs(self, sg_log, **kwargs): """Check the values passed in kwargs match the values of the sg log""" for k in sg_log.fields: if k in kwargs: self.assertEqual(kwargs[k], sg_log[k]) neutron-12.1.1/neutron/tests/unit/objects/test_l3_hamode.py0000664000175000017500000000504713553660047024037 0ustar zuulzuul00000000000000# Copyright (c) 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.objects import l3_hamode from neutron.tests.unit.objects import test_base as base from neutron.tests.unit import testlib_api class L3HARouterAgentPortBindingIfaceObjectTestCase( base.BaseObjectIfaceTestCase): _test_class = l3_hamode.L3HARouterAgentPortBinding class L3HARouterAgentPortBindingDbObjectTestCase(base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = l3_hamode.L3HARouterAgentPortBinding def setUp(self): super(L3HARouterAgentPortBindingDbObjectTestCase, self).setUp() _network_id = self._create_test_network_id() def get_port(): return self._create_test_port_id(network_id=_network_id) self.update_obj_fields({'port_id': get_port, 'router_id': self._create_test_router_id, 'l3_agent_id': self._create_test_agent_id}) class L3HARouterNetworkIfaceObjectTestCase(base.BaseObjectIfaceTestCase): _test_class = l3_hamode.L3HARouterNetwork class L3HARouterNetworkDbObjectTestCase(base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = l3_hamode.L3HARouterNetwork def setUp(self): super(L3HARouterNetworkDbObjectTestCase, self).setUp() network = self._create_test_network() self.update_obj_fields({'network_id': network.id}) class L3HARouterVRIdAllocationIfaceObjectTestCase( base.BaseObjectIfaceTestCase): _test_class = l3_hamode.L3HARouterVRIdAllocation class L3HARouterVRIdAllocationDbObjectTestCase(base.BaseDbObjectTestCase, testlib_api.SqlTestCase): _test_class = l3_hamode.L3HARouterVRIdAllocation def setUp(self): super(L3HARouterVRIdAllocationDbObjectTestCase, self).setUp() self.update_obj_fields( {'network_id': lambda: self._create_test_network().id}) neutron-12.1.1/neutron/tests/unit/plugins/0000775000175000017500000000000013553660157020617 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/__init__.py0000664000175000017500000000000013553660046022713 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/0000775000175000017500000000000013553660157021311 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/0000775000175000017500000000000013553660157022767 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/test_type_geneve.py0000664000175000017500000000454513553660047026720 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as p_const from neutron.plugins.ml2.drivers import type_geneve from neutron.tests.unit.plugins.ml2.drivers import base_type_tunnel from neutron.tests.unit.plugins.ml2 import test_rpc from neutron.tests.unit import testlib_api TUNNEL_IP_ONE = "10.10.10.77" TUNNEL_IP_TWO = "10.10.10.78" HOST_ONE = 'fake_host_one1' HOST_TWO = 'fake_host_two2' class GeneveTypeTest(base_type_tunnel.TunnelTypeTestMixin, testlib_api.SqlTestCase): DRIVER_CLASS = type_geneve.GeneveTypeDriver TYPE = p_const.TYPE_GENEVE def test_get_endpoints(self): self.driver.add_endpoint(TUNNEL_IP_ONE, HOST_ONE) self.driver.add_endpoint(TUNNEL_IP_TWO, HOST_TWO) endpoints = self.driver.get_endpoints() for endpoint in endpoints: if endpoint['ip_address'] == TUNNEL_IP_ONE: self.assertEqual(HOST_ONE, endpoint['host']) elif endpoint['ip_address'] == TUNNEL_IP_TWO: self.assertEqual(HOST_TWO, endpoint['host']) class GeneveTypeMultiRangeTest(base_type_tunnel.TunnelTypeMultiRangeTestMixin, testlib_api.SqlTestCase): DRIVER_CLASS = type_geneve.GeneveTypeDriver class GeneveTypeRpcCallbackTest(base_type_tunnel.TunnelRpcCallbackTestMixin, test_rpc.RpcCallbacksTestCase, testlib_api.SqlTestCase): DRIVER_CLASS = type_geneve.GeneveTypeDriver TYPE = p_const.TYPE_GENEVE class GeneveTypeTunnelMTUTest(base_type_tunnel.TunnelTypeMTUTestMixin, testlib_api.SqlTestCase): DRIVER_CLASS = type_geneve.GeneveTypeDriver TYPE = p_const.TYPE_GENEVE ENCAP_OVERHEAD = p_const.GENEVE_ENCAP_MIN_OVERHEAD neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mechanism_test.py0000664000175000017500000002510013553660047026340 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants as const from neutron_lib.plugins.ml2 import api class TestMechanismDriver(api.MechanismDriver): """Test mechanism driver for testing mechanism driver api.""" def initialize(self): self.bound_ports = set() def _check_network_context(self, context, original_expected): assert(isinstance(context, api.NetworkContext)) assert(isinstance(context.current, dict)) assert(context.current['id'] is not None) if original_expected: assert(isinstance(context.original, dict)) assert(context.current['id'] == context.original['id']) else: assert(not context.original) def create_network_precommit(self, context): self._check_network_context(context, False) def create_network_postcommit(self, context): self._check_network_context(context, False) def update_network_precommit(self, context): self._check_network_context(context, True) def update_network_postcommit(self, context): self._check_network_context(context, True) def delete_network_precommit(self, context): self._check_network_context(context, False) def delete_network_postcommit(self, context): self._check_network_context(context, False) def _check_subnet_context(self, context, original_expected): assert(isinstance(context, api.SubnetContext)) assert(isinstance(context.current, dict)) assert(context.current['id'] is not None) if original_expected: assert(isinstance(context.original, dict)) assert(context.current['id'] == context.original['id']) else: assert(not context.original) network_context = context.network assert(isinstance(network_context, api.NetworkContext)) self._check_network_context(network_context, False) def create_subnet_precommit(self, context): self._check_subnet_context(context, False) def create_subnet_postcommit(self, context): self._check_subnet_context(context, False) def update_subnet_precommit(self, context): self._check_subnet_context(context, True) def update_subnet_postcommit(self, context): self._check_subnet_context(context, True) def delete_subnet_precommit(self, context): self._check_subnet_context(context, False) def delete_subnet_postcommit(self, context): self._check_subnet_context(context, False) def _check_port_context(self, context, original_expected): assert(isinstance(context, api.PortContext)) self._check_port_info(context.current, context.host, context.vif_type, context.vif_details) if context.vif_type in (portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_BINDING_FAILED): if (context.segments_to_bind and context.segments_to_bind[0][api.NETWORK_TYPE] == 'vlan'): # Partially bound. self._check_bound(context.binding_levels, context.top_bound_segment, context.bottom_bound_segment) else: self._check_unbound(context.binding_levels, context.top_bound_segment, context.bottom_bound_segment) assert((context.current['id'], context.host) not in self.bound_ports) else: self._check_bound(context.binding_levels, context.top_bound_segment, context.bottom_bound_segment) assert((context.current['id'], context.host) in self.bound_ports) if original_expected: self._check_port_info(context.original, context.original_host, context.original_vif_type, context.original_vif_details) assert(context.current['id'] == context.original['id']) if (context.original_vif_type in (portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_BINDING_FAILED)): self._check_unbound(context.original_binding_levels, context.original_top_bound_segment, context.original_bottom_bound_segment) else: self._check_bound(context.original_binding_levels, context.original_top_bound_segment, context.original_bottom_bound_segment) else: assert(context.original is None) assert(context.original_host is None) assert(context.original_vif_type is None) assert(context.original_vif_details is None) assert(context.original_status is None) self._check_unbound(context.original_binding_levels, context.original_top_bound_segment, context.original_bottom_bound_segment) network_context = context.network assert(isinstance(network_context, api.NetworkContext)) self._check_network_context(network_context, False) def _check_port_info(self, port, host, vif_type, vif_details): assert(isinstance(port, dict)) assert(port['id'] is not None) assert(vif_type in (portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_BINDING_FAILED, portbindings.VIF_TYPE_DISTRIBUTED, portbindings.VIF_TYPE_OVS, portbindings.VIF_TYPE_BRIDGE)) if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE: assert(port[portbindings.HOST_ID] == '') assert(port[portbindings.VIF_TYPE] == portbindings.VIF_TYPE_DISTRIBUTED) assert(port[portbindings.VIF_DETAILS] == {}) else: assert(port[portbindings.HOST_ID] == host) assert(port[portbindings.VIF_TYPE] != portbindings.VIF_TYPE_DISTRIBUTED) assert(port[portbindings.VIF_TYPE] == vif_type) assert(isinstance(vif_details, dict)) assert(port[portbindings.VIF_DETAILS] == vif_details) def _check_unbound(self, levels, top_segment, bottom_segment): assert(levels is None) assert(top_segment is None) assert(bottom_segment is None) def _check_bound(self, levels, top_segment, bottom_segment): assert(isinstance(levels, list)) top_level = levels[0] assert(isinstance(top_level, dict)) assert(isinstance(top_segment, dict)) assert(top_segment == top_level[api.BOUND_SEGMENT]) assert('test' == top_level[api.BOUND_DRIVER]) bottom_level = levels[-1] assert(isinstance(bottom_level, dict)) assert(isinstance(bottom_segment, dict)) assert(bottom_segment == bottom_level[api.BOUND_SEGMENT]) assert('test' == bottom_level[api.BOUND_DRIVER]) def create_port_precommit(self, context): self._check_port_context(context, False) def create_port_postcommit(self, context): self._check_port_context(context, False) def update_port_precommit(self, context): if ((context.original_top_bound_segment and not context.top_bound_segment) or (context.host == "host-fail")): self.bound_ports.remove((context.original['id'], context.original_host)) self._check_port_context(context, True) def update_port_postcommit(self, context): self._check_port_context(context, True) def delete_port_precommit(self, context): self._check_port_context(context, False) def delete_port_postcommit(self, context): self._check_port_context(context, False) def bind_port(self, context): self._check_port_context(context, False) host = context.host segment = context.segments_to_bind[0] segment_id = segment[api.ID] if host == "host-ovs-no_filter": context.set_binding(segment_id, portbindings.VIF_TYPE_OVS, {portbindings.CAP_PORT_FILTER: False}) self.bound_ports.add((context.current['id'], host)) elif host == "host-bridge-filter": context.set_binding(segment_id, portbindings.VIF_TYPE_BRIDGE, {portbindings.CAP_PORT_FILTER: True}) self.bound_ports.add((context.current['id'], host)) elif host == "host-ovs-filter-active": context.set_binding(segment_id, portbindings.VIF_TYPE_OVS, {portbindings.CAP_PORT_FILTER: True}, status=const.PORT_STATUS_ACTIVE) self.bound_ports.add((context.current['id'], host)) elif host == "host-hierarchical": segment_type = segment[api.NETWORK_TYPE] if segment_type == 'local': next_segment = context.allocate_dynamic_segment( {api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'physnet1'} ) context.continue_binding(segment_id, [next_segment]) elif segment_type == 'vlan': context.set_binding(segment_id, portbindings.VIF_TYPE_OVS, {portbindings.CAP_PORT_FILTER: False}) self.bound_ports.add((context.current['id'], host)) elif host == "host-fail": context.set_binding(None, portbindings.VIF_TYPE_BINDING_FAILED, {portbindings.CAP_PORT_FILTER: False}) self.bound_ports.add((context.current['id'], host)) def filter_hosts_with_segment_access( self, context, segments, candidate_hosts, agent_getter): return set() neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/0000775000175000017500000000000013553660157025303 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/0000775000175000017500000000000013553660157026401 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_age0000664000175000017500000014554413553660047034300 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import sys import mock from neutron_lib import constants from oslo_config import cfg from neutron.agent.linux import bridge_lib from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.common import exceptions from neutron.plugins.ml2.drivers.agent import _agent_manager_base as amb from neutron.plugins.ml2.drivers.linuxbridge.agent.common \ import constants as lconst from neutron.plugins.ml2.drivers.linuxbridge.agent \ import linuxbridge_neutron_agent from neutron.tests import base LOCAL_IP = '192.168.0.33' LOCAL_IPV6 = '2001:db8:1::33' VXLAN_GROUPV6 = 'ff05::/120' PORT_1 = 'abcdef01-12ddssdfds-fdsfsd' DEVICE_1 = 'tapabcdef01-12' NETWORK_ID = '57653b20-ed5b-4ed0-a31d-06f84e3fd909' BRIDGE_MAPPING_VALUE = 'br-eth2' BRIDGE_MAPPINGS = {'physnet0': BRIDGE_MAPPING_VALUE} INTERFACE_MAPPINGS = {'physnet1': 'eth1'} FAKE_DEFAULT_DEV = mock.Mock() FAKE_DEFAULT_DEV.name = 'eth1' PORT_DATA = { "port_id": PORT_1, "device": DEVICE_1 } class FakeIpLinkCommand(object): def set_up(self): pass class FakeIpDevice(object): def __init__(self): self.link = FakeIpLinkCommand() def disable_ipv6(self): pass def get_linuxbridge_manager(bridge_mappings, interface_mappings): with mock.patch.object(ip_lib.IPWrapper, 'get_device_by_ip', return_value=FAKE_DEFAULT_DEV),\ mock.patch.object(ip_lib, 'device_exists', return_value=True),\ mock.patch.object(linuxbridge_neutron_agent.LinuxBridgeManager, 'check_vxlan_support'): cfg.CONF.set_override('local_ip', LOCAL_IP, 'VXLAN') return linuxbridge_neutron_agent.LinuxBridgeManager( bridge_mappings, interface_mappings) class TestLinuxBridge(base.BaseTestCase): def setUp(self): super(TestLinuxBridge, self).setUp() self.linux_bridge = get_linuxbridge_manager( BRIDGE_MAPPINGS, INTERFACE_MAPPINGS) def test_ensure_physical_in_bridge_invalid(self): result = self.linux_bridge.ensure_physical_in_bridge( 'network_id', constants.TYPE_VLAN, 'physnetx', 7) self.assertFalse(result) def test_ensure_physical_in_bridge_flat(self): with mock.patch.object(self.linux_bridge, 'ensure_flat_bridge') as flat_bridge_func: self.linux_bridge.ensure_physical_in_bridge( 'network_id', constants.TYPE_FLAT, 'physnet1', None) self.assertTrue(flat_bridge_func.called) def test_ensure_physical_in_bridge_vlan(self): with mock.patch.object(self.linux_bridge, 'ensure_vlan_bridge') as vlan_bridge_func: self.linux_bridge.ensure_physical_in_bridge( 'network_id', constants.TYPE_VLAN, 'physnet1', 7) self.assertTrue(vlan_bridge_func.called) def test_ensure_physical_in_bridge_vxlan(self): self.linux_bridge.vxlan_mode = lconst.VXLAN_UCAST with mock.patch.object(self.linux_bridge, 'ensure_vxlan_bridge') as vxlan_bridge_func: self.linux_bridge.ensure_physical_in_bridge( 'network_id', 'vxlan', 'physnet1', 7) self.assertTrue(vxlan_bridge_func.called) class TestLinuxBridgeManager(base.BaseTestCase): def setUp(self): super(TestLinuxBridgeManager, self).setUp() self.lbm = get_linuxbridge_manager( BRIDGE_MAPPINGS, INTERFACE_MAPPINGS) def test_local_ip_validation_with_valid_ip(self): with mock.patch.object(ip_lib.IPWrapper, 'get_device_by_ip', return_value=FAKE_DEFAULT_DEV): self.lbm.local_ip = LOCAL_IP result = self.lbm.get_local_ip_device() self.assertEqual(FAKE_DEFAULT_DEV, result) def test_local_ip_validation_with_invalid_ip(self): with mock.patch.object(ip_lib.IPWrapper, 'get_device_by_ip', return_value=None),\ mock.patch.object(sys, 'exit') as exit,\ mock.patch.object(linuxbridge_neutron_agent.LOG, 'error') as log: self.lbm.local_ip = LOCAL_IP self.lbm.get_local_ip_device() self.assertEqual(1, log.call_count) exit.assert_called_once_with(1) def _test_vxlan_group_validation(self, bad_local_ip, bad_vxlan_group): with mock.patch.object(ip_lib.IPWrapper, 'get_device_by_ip', return_value=FAKE_DEFAULT_DEV),\ mock.patch.object(sys, 'exit') as exit,\ mock.patch.object(linuxbridge_neutron_agent.LOG, 'error') as log: self.lbm.local_ip = bad_local_ip cfg.CONF.set_override('vxlan_group', bad_vxlan_group, 'VXLAN') self.lbm.validate_vxlan_group_with_local_ip() self.assertEqual(1, log.call_count) exit.assert_called_once_with(1) def test_vxlan_group_validation_with_mismatched_local_ip(self): self._test_vxlan_group_validation(LOCAL_IP, VXLAN_GROUPV6) def test_vxlan_group_validation_with_unicast_group(self): self._test_vxlan_group_validation(LOCAL_IP, '240.0.0.0') def test_vxlan_group_validation_with_invalid_cidr(self): self._test_vxlan_group_validation(LOCAL_IP, '224.0.0.1/') def test_vxlan_group_validation_with_v6_unicast_group(self): self._test_vxlan_group_validation(LOCAL_IPV6, '2001:db8::') def test_get_existing_bridge_name(self): phy_net = 'physnet0' self.assertEqual('br-eth2', self.lbm.bridge_mappings.get(phy_net)) phy_net = '' self.assertIsNone(self.lbm.bridge_mappings.get(phy_net)) def test_get_bridge_name(self): nw_id = "123456789101112" self.assertEqual("brq" + nw_id[0:11], self.lbm.get_bridge_name(nw_id)) nw_id = "" self.assertEqual("brq", self.lbm.get_bridge_name(nw_id)) def test_get_subinterface_name_backwards_compatibility(self): self.assertEqual("abcdefghijklm.1", self.lbm.get_subinterface_name("abcdefghijklm", "1")) self.assertEqual("abcdefghijkl.11", self.lbm.get_subinterface_name("abcdefghijkl", "11")) self.assertEqual("abcdefghij.1111", self.lbm.get_subinterface_name("abcdefghij", "1111")) def test_get_subinterface_name_advanced(self): """Ensure the same hash is used for long interface names. If the generated vlan device name would be too long, make sure that everything before the '.' is equal. This might be helpful when debugging problems. """ max_device_name = "abcdefghijklmno" vlan_dev_name1 = self.lbm.get_subinterface_name(max_device_name, "1") vlan_dev_name2 = self.lbm.get_subinterface_name(max_device_name, "1111") self.assertEqual(vlan_dev_name1.partition(".")[0], vlan_dev_name2.partition(".")[0]) def test_get_tap_device_name(self): if_id = "123456789101112" self.assertEqual(constants.TAP_DEVICE_PREFIX + if_id[0:11], self.lbm.get_tap_device_name(if_id)) if_id = "" self.assertEqual(constants.TAP_DEVICE_PREFIX, self.lbm.get_tap_device_name(if_id)) def test_get_vxlan_device_name(self): vn_id = constants.MAX_VXLAN_VNI self.assertEqual("vxlan-" + str(vn_id), self.lbm.get_vxlan_device_name(vn_id)) self.assertIsNone(self.lbm.get_vxlan_device_name(vn_id + 1)) def test_get_vxlan_group(self): cfg.CONF.set_override('vxlan_group', '239.1.2.3/24', 'VXLAN') vn_id = constants.MAX_VXLAN_VNI self.assertEqual('239.1.2.255', self.lbm.get_vxlan_group(vn_id)) vn_id = 256 self.assertEqual('239.1.2.0', self.lbm.get_vxlan_group(vn_id)) vn_id = 257 self.assertEqual('239.1.2.1', self.lbm.get_vxlan_group(vn_id)) def test_get_vxlan_group_with_multicast_address(self): cfg.CONF.set_override('vxlan_group', '239.1.2.3/32', 'VXLAN') cfg.CONF.set_override('multicast_ranges', ('224.0.0.10:300:315', '225.0.0.15:400:600'), 'VXLAN') vn_id = 300 self.assertEqual('224.0.0.10', self.lbm.get_vxlan_group(vn_id)) vn_id = 500 self.assertEqual('225.0.0.15', self.lbm.get_vxlan_group(vn_id)) vn_id = 315 self.assertEqual('224.0.0.10', self.lbm.get_vxlan_group(vn_id)) vn_id = 4000 # outside of range should fallback to group self.assertEqual('239.1.2.3', self.lbm.get_vxlan_group(vn_id)) def test__is_valid_multicast_range(self): bad_ranges = ['224.0.0.10:330:315', 'x:100:200', '10.0.0.1:100:200', '224.0.0.10:100', '224.0.0.10:100:200:300'] for r in bad_ranges: self.assertFalse(self.lbm._is_valid_multicast_range(r), 'range %s should have been invalid' % r) good_ranges = ['224.0.0.10:315:330', '224.0.0.0:315:315'] for r in good_ranges: self.assertTrue(self.lbm._is_valid_multicast_range(r), 'range %s should have been valid' % r) # v4 ranges are bad when a v6 local_ip is present self.lbm.local_ip = '2000::1' for r in good_ranges: self.assertFalse(self.lbm._is_valid_multicast_range(r), 'range %s should have been invalid' % r) def test__match_multicast_range(self): cfg.CONF.set_override('multicast_ranges', ('224.0.0.10:300:315', '225.0.0.15:400:600'), 'VXLAN') self.assertEqual('224.0.0.10', self.lbm._match_multicast_range(307)) self.assertEqual('225.0.0.15', self.lbm._match_multicast_range(407)) self.assertIsNone(self.lbm._match_multicast_range(399)) def test_get_vxlan_group_with_ipv6(self): cfg.CONF.set_override('local_ip', LOCAL_IPV6, 'VXLAN') self.lbm.local_ip = LOCAL_IPV6 cfg.CONF.set_override('vxlan_group', VXLAN_GROUPV6, 'VXLAN') vn_id = constants.MAX_VXLAN_VNI self.assertEqual('ff05::ff', self.lbm.get_vxlan_group(vn_id)) vn_id = 256 self.assertEqual('ff05::', self.lbm.get_vxlan_group(vn_id)) vn_id = 257 self.assertEqual('ff05::1', self.lbm.get_vxlan_group(vn_id)) def test_get_deletable_bridges(self): br_list = ["br-int", "brq1", "brq2", "brq-user"] expected = set(br_list[1:3]) lbm = get_linuxbridge_manager( bridge_mappings={"physnet0": "brq-user"}, interface_mappings={}) with mock.patch.object( bridge_lib, 'get_bridge_names', return_value=br_list): self.assertEqual(expected, lbm.get_deletable_bridges()) def test_get_tap_devices_count(self): with mock.patch.object( bridge_lib.BridgeDevice, 'get_interfaces') as get_ifs_fn: get_ifs_fn.return_value = ['tap2101', 'eth0.100', 'vxlan-1000'] self.assertEqual(1, self.lbm.get_tap_devices_count('br0')) def test_get_interface_details(self): with mock.patch.object(ip_lib.IpAddrCommand, 'list') as list_fn,\ mock.patch.object(ip_lib.IpRouteCommand, 'get_gateway') as getgw_fn: gwdict = dict(gateway='1.1.1.1') getgw_fn.return_value = gwdict ipdict = dict(cidr='1.1.1.1/24', broadcast='1.1.1.255', scope='global', ip_version=4, dynamic=False) list_fn.return_value = ipdict ret = self.lbm.get_interface_details("eth0", 4) self.assertTrue(list_fn.called) self.assertTrue(getgw_fn.called) self.assertEqual(ret, (ipdict, gwdict)) def test_ensure_flat_bridge(self): with mock.patch.object(self.lbm, 'ensure_bridge') as ens: self.assertEqual( "eth0", self.lbm.ensure_flat_bridge("123", None, "eth0")) ens.assert_called_once_with("brq123", "eth0") def test_ensure_flat_bridge_with_existed_brq(self): with mock.patch.object(self.lbm, 'ensure_bridge') as ens: ens.return_value = "br-eth2" self.assertEqual("br-eth2", self.lbm.ensure_flat_bridge("123", "br-eth2", None)) ens.assert_called_with("br-eth2") def test_ensure_vlan_bridge(self): with mock.patch.object(self.lbm, 'ensure_vlan') as ens_vl_fn,\ mock.patch.object(self.lbm, 'ensure_bridge') as ens: ens_vl_fn.return_value = "eth0.1" self.assertEqual("eth0.1", self.lbm.ensure_vlan_bridge("123", None, "eth0", "1")) ens.assert_called_with("brq123", "eth0.1") self.assertEqual("eth0.1", self.lbm.ensure_vlan_bridge("123", None, "eth0", "1")) ens.assert_called_with("brq123", "eth0.1") def test_ensure_vlan_bridge_with_existed_brq(self): with mock.patch.object(self.lbm, 'ensure_vlan') as ens_vl_fn,\ mock.patch.object(self.lbm, 'ensure_bridge') as ens: ens_vl_fn.return_value = None ens.return_value = "br-eth2" self.assertEqual("br-eth2", self.lbm.ensure_vlan_bridge("123", "br-eth2", None, None)) ens.assert_called_with("br-eth2") def test_ensure_local_bridge(self): with mock.patch.object(self.lbm, 'ensure_bridge') as ens_fn: self.lbm.ensure_local_bridge("54321", None) ens_fn.assert_called_once_with("brq54321") def test_ensure_local_bridge_with_existed_brq(self): with mock.patch.object(self.lbm, 'ensure_bridge') as ens_fn: ens_fn.return_value = "br-eth2" self.lbm.ensure_local_bridge("54321", 'br-eth2') ens_fn.assert_called_once_with("br-eth2") def test_ensure_vlan(self): with mock.patch.object(ip_lib, 'device_exists') as de_fn: de_fn.return_value = True self.assertEqual("eth0.1", self.lbm.ensure_vlan("eth0", "1")) de_fn.return_value = False vlan_dev = FakeIpDevice() with mock.patch.object(vlan_dev, 'disable_ipv6') as dv6_fn,\ mock.patch.object(self.lbm.ip, 'add_vlan', return_value=vlan_dev) as add_vlan_fn: retval = self.lbm.ensure_vlan("eth0", "1") self.assertEqual("eth0.1", retval) add_vlan_fn.assert_called_with('eth0.1', 'eth0', '1') dv6_fn.assert_called_once_with() def test_ensure_vxlan(self, expected_proxy=False): seg_id = "12345678" self.lbm.local_int = 'eth0' self.lbm.vxlan_mode = lconst.VXLAN_MCAST with mock.patch.object(ip_lib, 'device_exists') as de_fn: de_fn.return_value = True self.assertEqual("vxlan-" + seg_id, self.lbm.ensure_vxlan(seg_id)) de_fn.return_value = False vxlan_dev = FakeIpDevice() with mock.patch.object(vxlan_dev, 'disable_ipv6') as dv6_fn,\ mock.patch.object(self.lbm.ip, 'add_vxlan', return_value=vxlan_dev) as add_vxlan_fn: retval = self.lbm.ensure_vxlan(seg_id) self.assertEqual("vxlan-" + seg_id, retval) add_vxlan_fn.assert_called_with("vxlan-" + seg_id, seg_id, group="224.0.0.1", srcport=(0, 0), dstport=None, ttl=None, dev=self.lbm.local_int) dv6_fn.assert_called_once_with() cfg.CONF.set_override('l2_population', 'True', 'VXLAN') self.assertEqual("vxlan-" + seg_id, self.lbm.ensure_vxlan(seg_id)) add_vxlan_fn.assert_called_with("vxlan-" + seg_id, seg_id, group="224.0.0.1", srcport=(0, 0), dstport=None, ttl=None, dev=self.lbm.local_int, proxy=expected_proxy) def test_ensure_vxlan_arp_responder_enabled(self): cfg.CONF.set_override('arp_responder', True, 'VXLAN') self.test_ensure_vxlan(expected_proxy=True) def test_ensure_vxlan_dscp_inherit_set(self): cfg.CONF.set_override('dscp_inherit', 'True', 'AGENT') seg_id = "12345678" self.lbm.local_int = 'eth0' self.lbm.vxlan_mode = lconst.VXLAN_MCAST with mock.patch.object(ip_lib, 'device_exists', return_value=False): vxlan_dev = FakeIpDevice() with mock.patch.object(vxlan_dev, 'disable_ipv6') as dv6_fn,\ mock.patch.object(self.lbm.ip, 'add_vxlan', return_value=vxlan_dev) as add_vxlan_fn: self.assertEqual("vxlan-" + seg_id, self.lbm.ensure_vxlan(seg_id)) add_vxlan_fn.assert_called_with("vxlan-" + seg_id, seg_id, group="224.0.0.1", srcport=(0, 0), dstport=None, ttl=None, tos='inherit', dev=self.lbm.local_int) dv6_fn.assert_called_once_with() def test__update_interface_ip_details(self): gwdict = dict(gateway='1.1.1.1', metric=50) ipdict = dict(cidr='1.1.1.1/24', broadcast='1.1.1.255', scope='global', ip_version=4, dynamic=False) with mock.patch.object(ip_lib.IpAddrCommand, 'add') as add_fn,\ mock.patch.object(ip_lib.IpAddrCommand, 'delete') as del_fn,\ mock.patch.object(ip_lib.IpAddrCommand, 'list') as list_fn: # 'list' actually returns a dict, but we're only simulating # whether the device exists or not list_fn.side_effect = [True, False] self.lbm._update_interface_ip_details("br0", "eth0", [ipdict], None) self.assertFalse(add_fn.called) self.assertTrue(del_fn.called) add_fn.reset_mock() del_fn.reset_mock() self.lbm._update_interface_ip_details("br0", "eth0", [ipdict], None) self.assertTrue(add_fn.called) self.assertTrue(del_fn.called) with mock.patch.object(ip_lib.IpRouteCommand, 'add_gateway') as addgw_fn,\ mock.patch.object(ip_lib.IpRouteCommand, 'delete_gateway') as delgw_fn: self.lbm._update_interface_ip_details("br0", "eth0", None, gwdict) self.assertTrue(addgw_fn.called) self.assertTrue(delgw_fn.called) def test_bridge_exists_and_ensure_up(self): ip_lib_mock = mock.Mock() with mock.patch.object(ip_lib, 'IPDevice', return_value=ip_lib_mock): # device exists self.assertTrue(self.lbm._bridge_exists_and_ensure_up("br0")) self.assertTrue(ip_lib_mock.link.set_up.called) # device doesn't exists ip_lib_mock.link.set_up.side_effect = RuntimeError self.assertFalse(self.lbm._bridge_exists_and_ensure_up("br0")) def test_ensure_bridge(self): bridge_device = mock.Mock() bridge_device_old = mock.Mock() with mock.patch.object(self.lbm, '_bridge_exists_and_ensure_up') as de_fn,\ mock.patch.object(bridge_lib, "BridgeDevice", return_value=bridge_device) as br_fn,\ mock.patch.object(self.lbm, 'update_interface_ip_details') as upd_fn,\ mock.patch.object(bridge_lib, 'is_bridged_interface'),\ mock.patch.object(bridge_lib.BridgeDevice, 'get_interface_bridge') as get_if_br_fn: de_fn.return_value = False br_fn.addbr.return_value = bridge_device bridge_device.setfd.return_value = False bridge_device.disable_stp.return_value = False bridge_device.disable_ipv6.return_value = False bridge_device.link.set_up.return_value = False self.assertEqual("br0", self.lbm.ensure_bridge("br0", None)) bridge_device.owns_interface.return_value = False self.lbm.ensure_bridge("br0", "eth0") upd_fn.assert_called_with("br0", "eth0") bridge_device.owns_interface.assert_called_with("eth0") de_fn.return_value = True bridge_device.delif.side_effect = Exception() self.lbm.ensure_bridge("br0", "eth0") bridge_device.owns_interface.assert_called_with("eth0") de_fn.return_value = True bridge_device.owns_interface.return_value = False get_if_br_fn.return_value = bridge_device_old bridge_device.addif.reset_mock() self.lbm.ensure_bridge("br0", "eth0") bridge_device_old.delif.assert_called_once_with('eth0') bridge_device.addif.assert_called_once_with('eth0') def test_ensure_physical_in_bridge(self): self.assertFalse( self.lbm.ensure_physical_in_bridge("123", constants.TYPE_VLAN, "phys", "1") ) with mock.patch.object(self.lbm, "ensure_flat_bridge") as flbr_fn: self.assertTrue( self.lbm.ensure_physical_in_bridge("123", constants.TYPE_FLAT, "physnet1", None) ) self.assertTrue(flbr_fn.called) with mock.patch.object(self.lbm, "ensure_vlan_bridge") as vlbr_fn: self.assertTrue( self.lbm.ensure_physical_in_bridge("123", constants.TYPE_VLAN, "physnet1", "1") ) self.assertTrue(vlbr_fn.called) with mock.patch.object(self.lbm, "ensure_vxlan_bridge") as vlbr_fn: self.lbm.vxlan_mode = lconst.VXLAN_MCAST self.assertTrue( self.lbm.ensure_physical_in_bridge("123", constants.TYPE_VXLAN, "physnet1", "1") ) self.assertTrue(vlbr_fn.called) def test_ensure_physical_in_bridge_with_existed_brq(self): with mock.patch.object(linuxbridge_neutron_agent.LOG, 'error') as log: self.lbm.ensure_physical_in_bridge("123", constants.TYPE_FLAT, "physnet9", "1") self.assertEqual(1, log.call_count) @mock.patch.object(ip_lib, "device_exists", return_value=False) def test_add_tap_interface_with_interface_disappearing(self, exists): with mock.patch.object(self.lbm, "_add_tap_interface", side_effect=RuntimeError("No such dev")): self.assertFalse(self.lbm.add_tap_interface("123", constants.TYPE_VLAN, "physnet1", None, "tap1", "foo", None)) @mock.patch.object(ip_lib, "device_exists", return_value=True) def test_add_tap_interface_with_other_error(self, exists): with mock.patch.object(self.lbm, "_add_tap_interface", side_effect=RuntimeError("No more fuel")): self.assertRaises(RuntimeError, self.lbm.add_tap_interface, "123", constants.TYPE_VLAN, "physnet1", None, "tap1", "foo", None) def test_add_tap_interface_owner_compute(self): with mock.patch.object(ip_lib, "device_exists"): with mock.patch.object(self.lbm, "ensure_local_bridge"): self.assertTrue(self.lbm.add_tap_interface( "123", constants.TYPE_LOCAL, "physnet1", None, "tap1", "compute:1", None)) def _test_add_tap_interface(self, dev_owner_prefix): with mock.patch.object(ip_lib, "device_exists") as de_fn: de_fn.return_value = False self.assertFalse( self.lbm.add_tap_interface("123", constants.TYPE_VLAN, "physnet1", "1", "tap1", dev_owner_prefix, None)) de_fn.return_value = True bridge_device = mock.Mock() with mock.patch.object(self.lbm, "ensure_local_bridge") as en_fn,\ mock.patch.object(bridge_lib, "BridgeDevice", return_value=bridge_device), \ mock.patch.object(self.lbm, '_set_tap_mtu') as set_tap, \ mock.patch.object(bridge_lib.BridgeDevice, "get_interface_bridge") as get_br: bridge_device.addif.retun_value = False get_br.return_value = True self.assertTrue(self.lbm.add_tap_interface( "123", constants.TYPE_LOCAL, "physnet1", None, "tap1", dev_owner_prefix, None)) en_fn.assert_called_with("123", "brq123") self.lbm.bridge_mappings = {"physnet1": "brq999"} self.assertTrue(self.lbm.add_tap_interface( "123", constants.TYPE_LOCAL, "physnet1", None, "tap1", dev_owner_prefix, 8765)) set_tap.assert_called_with('tap1', 8765) en_fn.assert_called_with("123", "brq999") get_br.return_value = False bridge_device.addif.retun_value = True self.assertFalse(self.lbm.add_tap_interface( "123", constants.TYPE_LOCAL, "physnet1", None, "tap1", dev_owner_prefix, None)) with mock.patch.object(self.lbm, "ensure_physical_in_bridge") as ens_fn: ens_fn.return_value = False self.assertFalse(self.lbm.add_tap_interface( "123", constants.TYPE_VLAN, "physnet1", "1", "tap1", dev_owner_prefix, None)) def test_add_tap_interface_owner_network(self): self._test_add_tap_interface(constants.DEVICE_OWNER_NETWORK_PREFIX) def test_add_tap_interface_owner_neutron(self): self._test_add_tap_interface(constants.DEVICE_OWNER_NEUTRON_PREFIX) def test_plug_interface(self): segment = amb.NetworkSegment( constants.TYPE_VLAN, "physnet-1", "1", 1777) with mock.patch.object(self.lbm, "add_tap_interface") as add_tap: self.lbm.plug_interface("123", segment, "tap234", constants.DEVICE_OWNER_NETWORK_PREFIX) add_tap.assert_called_with("123", constants.TYPE_VLAN, "physnet-1", "1", "tap234", constants.DEVICE_OWNER_NETWORK_PREFIX, 1777) def test_delete_bridge(self): with mock.patch.object(ip_lib.IPDevice, "exists") as de_fn,\ mock.patch.object(ip_lib, "IpLinkCommand") as link_cmd,\ mock.patch.object(bridge_lib.BridgeDevice, "get_interfaces") as getif_fn,\ mock.patch.object(self.lbm, "remove_interface"),\ mock.patch.object(self.lbm, "update_interface_ip_details") as updif_fn,\ mock.patch.object(self.lbm, "delete_interface") as delif_fn: de_fn.return_value = False self.lbm.delete_bridge("br0") self.assertFalse(getif_fn.called) de_fn.return_value = True getif_fn.return_value = ["eth0", "eth1", "vxlan-1002"] link_cmd.set_down.return_value = False self.lbm.delete_bridge("br0") updif_fn.assert_called_with("eth1", "br0") delif_fn.assert_called_with("vxlan-1002") def test_delete_bridge_not_exist(self): self.lbm.interface_mappings.update({}) bridge_device = mock.Mock() with mock.patch.object(bridge_lib, "BridgeDevice", return_value=bridge_device): bridge_device.exists.side_effect = [True, False] bridge_device.get_interfaces.return_value = [] bridge_device.link.set_down.side_effect = RuntimeError self.lbm.delete_bridge("br0") self.assertEqual(2, bridge_device.exists.call_count) bridge_device.exists.side_effect = [True, True] self.assertRaises(RuntimeError, self.lbm.delete_bridge, "br0") def test_delete_bridge_with_ip(self): bridge_device = mock.Mock() with mock.patch.object(ip_lib, "device_exists") as de_fn,\ mock.patch.object(self.lbm, "remove_interface"),\ mock.patch.object(self.lbm, "update_interface_ip_details") as updif_fn,\ mock.patch.object(self.lbm, "delete_interface") as del_interface,\ mock.patch.object(bridge_lib, "BridgeDevice", return_value=bridge_device): de_fn.return_value = True updif_fn.return_value = True bridge_device.get_interfaces.return_value = ["eth0", "eth1.1"] bridge_device.link.set_down.return_value = False self.lbm.delete_bridge("br0") updif_fn.assert_called_with("eth1.1", "br0") self.assertFalse(del_interface.called) def test_delete_bridge_no_ip(self): bridge_device = mock.Mock() with mock.patch.object(ip_lib, "device_exists") as de_fn,\ mock.patch.object(self.lbm, "remove_interface"),\ mock.patch.object(self.lbm, "get_interface_details") as if_det_fn,\ mock.patch.object(self.lbm, "_update_interface_ip_details") as updif_fn,\ mock.patch.object(self.lbm, "delete_interface") as del_interface,\ mock.patch.object(bridge_lib, "BridgeDevice", return_value=bridge_device): de_fn.return_value = True bridge_device.get_interfaces.return_value = ["eth0", "eth1.1"] bridge_device.link.set_down.return_value = False if_det_fn.return_value = ([], None) self.lbm.delete_bridge("br0") del_interface.assert_called_with("eth1.1") self.assertFalse(updif_fn.called) def test_delete_bridge_no_int_mappings(self): lbm = get_linuxbridge_manager( bridge_mappings={}, interface_mappings={}) with mock.patch.object(ip_lib.IPDevice, "exists") as de_fn,\ mock.patch.object(ip_lib, "IpLinkCommand") as link_cmd,\ mock.patch.object(bridge_lib.BridgeDevice, "get_interfaces") as getif_fn,\ mock.patch.object(lbm, "remove_interface"),\ mock.patch.object(lbm, "delete_interface") as del_interface: de_fn.return_value = False lbm.delete_bridge("br0") self.assertFalse(getif_fn.called) de_fn.return_value = True getif_fn.return_value = ["vxlan-1002"] link_cmd.set_down.return_value = False lbm.delete_bridge("br0") del_interface.assert_called_with("vxlan-1002") def test_delete_bridge_with_physical_vlan(self): self.lbm.interface_mappings.update({"physnet2": "eth1.4000"}) bridge_device = mock.Mock() with mock.patch.object(ip_lib, "device_exists") as de_fn,\ mock.patch.object(self.lbm, "remove_interface"),\ mock.patch.object(self.lbm, "update_interface_ip_details") as updif_fn,\ mock.patch.object(self.lbm, "delete_interface") as del_int,\ mock.patch.object(bridge_lib, "BridgeDevice", return_value=bridge_device): de_fn.return_value = True bridge_device.get_interfaces.return_value = ["eth1.1", "eth1.4000"] updif_fn.return_value = False bridge_device.link.set_down.return_value = False self.lbm.delete_bridge("br0") del_int.assert_called_once_with("eth1.1") def test_remove_interface(self): with mock.patch.object(ip_lib.IPDevice, "exists") as de_fn,\ mock.patch.object(bridge_lib.BridgeDevice, 'owns_interface') as owns_fn,\ mock.patch.object(bridge_lib.BridgeDevice, "delif") as delif_fn: de_fn.return_value = False self.assertFalse(self.lbm.remove_interface("br0", "eth0")) self.assertFalse(owns_fn.called) de_fn.return_value = True owns_fn.return_value = False self.assertTrue(self.lbm.remove_interface("br0", "eth0")) delif_fn.return_value = False self.assertTrue(self.lbm.remove_interface("br0", "eth0")) def test_remove_interface_not_on_bridge(self): bridge_device = mock.Mock() with mock.patch.object(bridge_lib, "BridgeDevice", return_value=bridge_device): bridge_device.exists.return_value = True bridge_device.delif.side_effect = RuntimeError bridge_device.owns_interface.side_effect = [True, False] self.lbm.remove_interface("br0", 'tap0') self.assertEqual(2, bridge_device.owns_interface.call_count) bridge_device.owns_interface.side_effect = [True, True] self.assertRaises(RuntimeError, self.lbm.remove_interface, "br0", 'tap0') def test_delete_interface(self): with mock.patch.object(ip_lib.IPDevice, "exists") as de_fn,\ mock.patch.object(ip_lib.IpLinkCommand, "set_down") as down_fn,\ mock.patch.object(ip_lib.IpLinkCommand, "delete") as delete_fn: de_fn.return_value = False self.lbm.delete_interface("eth1.1") self.assertFalse(down_fn.called) self.assertFalse(delete_fn.called) de_fn.return_value = True self.lbm.delete_interface("eth1.1") self.assertTrue(down_fn.called) self.assertTrue(delete_fn.called) def _check_vxlan_support(self, expected, vxlan_ucast_supported, vxlan_mcast_supported): with mock.patch.object(self.lbm, 'vxlan_ucast_supported', return_value=vxlan_ucast_supported),\ mock.patch.object(self.lbm, 'vxlan_mcast_supported', return_value=vxlan_mcast_supported): if expected == lconst.VXLAN_NONE: self.assertRaises(exceptions.VxlanNetworkUnsupported, self.lbm.check_vxlan_support) self.assertEqual(expected, self.lbm.vxlan_mode) else: self.lbm.check_vxlan_support() self.assertEqual(expected, self.lbm.vxlan_mode) def test_check_vxlan_support(self): self._check_vxlan_support(expected=lconst.VXLAN_UCAST, vxlan_ucast_supported=True, vxlan_mcast_supported=True) self._check_vxlan_support(expected=lconst.VXLAN_MCAST, vxlan_ucast_supported=False, vxlan_mcast_supported=True) self._check_vxlan_support(expected=lconst.VXLAN_NONE, vxlan_ucast_supported=False, vxlan_mcast_supported=False) self._check_vxlan_support(expected=lconst.VXLAN_NONE, vxlan_ucast_supported=False, vxlan_mcast_supported=False) def _check_vxlan_ucast_supported( self, expected, l2_population, iproute_arg_supported, fdb_append): cfg.CONF.set_override('l2_population', l2_population, 'VXLAN') with mock.patch.object(ip_lib, 'device_exists', return_value=False),\ mock.patch.object(ip_lib, 'vxlan_in_use', return_value=False),\ mock.patch.object(self.lbm, 'delete_interface', return_value=None),\ mock.patch.object(self.lbm, 'ensure_vxlan', return_value=None),\ mock.patch.object( utils, 'execute', side_effect=None if fdb_append else RuntimeError()),\ mock.patch.object(ip_lib, 'iproute_arg_supported', return_value=iproute_arg_supported): self.assertEqual(expected, self.lbm.vxlan_ucast_supported()) def test_vxlan_ucast_supported(self): self._check_vxlan_ucast_supported( expected=False, l2_population=False, iproute_arg_supported=True, fdb_append=True) self._check_vxlan_ucast_supported( expected=False, l2_population=True, iproute_arg_supported=False, fdb_append=True) self._check_vxlan_ucast_supported( expected=False, l2_population=True, iproute_arg_supported=True, fdb_append=False) self._check_vxlan_ucast_supported( expected=True, l2_population=True, iproute_arg_supported=True, fdb_append=True) def _check_vxlan_mcast_supported( self, expected, vxlan_group, iproute_arg_supported): cfg.CONF.set_override('vxlan_group', vxlan_group, 'VXLAN') with mock.patch.object( ip_lib, 'iproute_arg_supported', return_value=iproute_arg_supported): self.assertEqual(expected, self.lbm.vxlan_mcast_supported()) def test_vxlan_mcast_supported(self): self._check_vxlan_mcast_supported( expected=False, vxlan_group='', iproute_arg_supported=True) self._check_vxlan_mcast_supported( expected=False, vxlan_group='224.0.0.1', iproute_arg_supported=False) self._check_vxlan_mcast_supported( expected=True, vxlan_group='224.0.0.1', iproute_arg_supported=True) def _test_ensure_port_admin_state(self, admin_state): port_id = 'fake_id' with mock.patch.object(ip_lib, 'IPDevice') as dev_mock: self.lbm.ensure_port_admin_state(port_id, admin_state) tap_name = self.lbm.get_tap_device_name(port_id) self.assertEqual(admin_state, dev_mock(tap_name).link.set_up.called) self.assertNotEqual(admin_state, dev_mock(tap_name).link.set_down.called) def test_ensure_port_admin_state_up(self): self._test_ensure_port_admin_state(True) def test_ensure_port_admin_state_down(self): self._test_ensure_port_admin_state(False) def test_get_agent_id_bridge_mappings(self): lbm = get_linuxbridge_manager(BRIDGE_MAPPINGS, INTERFACE_MAPPINGS) with mock.patch.object(ip_lib, "get_device_mac", return_value='16:63:69:10:a0:59') as mock_gim: agent_id = lbm.get_agent_id() self.assertEqual("lb16636910a059", agent_id) mock_gim.assert_called_with(BRIDGE_MAPPING_VALUE) def test_get_agent_id_no_bridge_mappings(self): devices_mock = [ mock.MagicMock(), mock.MagicMock() ] devices_mock[0].name = "eth1" devices_mock[1].name = "eth2" bridge_mappings = {} lbm = get_linuxbridge_manager(bridge_mappings, INTERFACE_MAPPINGS) with mock.patch.object(ip_lib.IPWrapper, 'get_devices', return_value=devices_mock), \ mock.patch.object( ip_lib, "get_device_mac", side_effect=[None, '16:63:69:10:a0:59']) as mock_gim: agent_id = lbm.get_agent_id() self.assertEqual("lb16636910a059", agent_id) mock_gim.assert_has_calls([mock.call("eth1"), mock.call("eth2")]) class TestLinuxBridgeRpcCallbacks(base.BaseTestCase): def setUp(self): super(TestLinuxBridgeRpcCallbacks, self).setUp() class FakeLBAgent(object): def __init__(self): self.agent_id = 1 self.mgr = get_linuxbridge_manager( BRIDGE_MAPPINGS, INTERFACE_MAPPINGS) self.mgr.vxlan_mode = lconst.VXLAN_UCAST self.network_ports = collections.defaultdict(list) self.lb_rpc = linuxbridge_neutron_agent.LinuxBridgeRpcCallbacks( object(), FakeLBAgent(), object() ) segment = mock.Mock() segment.network_type = 'vxlan' segment.segmentation_id = 1 self.lb_rpc.network_map['net_id'] = segment def test_network_delete_mapped_net(self): mock_net = mock.Mock() mock_net.physical_network = None self._test_network_delete({NETWORK_ID: mock_net}) def test_network_delete_unmapped_net(self): self._test_network_delete({}) def _test_network_delete(self, net_map): self.lb_rpc.network_map = net_map with mock.patch.object(self.lb_rpc.agent.mgr, "get_bridge_name") as get_br_fn,\ mock.patch.object(self.lb_rpc.agent.mgr, "delete_bridge") as del_fn: get_br_fn.return_value = "br0" self.lb_rpc.network_delete("anycontext", network_id=NETWORK_ID) get_br_fn.assert_called_with(NETWORK_ID) del_fn.assert_called_with("br0") def test_port_update(self): port = {'id': PORT_1} self.lb_rpc.port_update(context=None, port=port) self.assertEqual(set([DEVICE_1]), self.lb_rpc.updated_devices) def test_network_update(self): updated_network = {'id': NETWORK_ID} self.lb_rpc.agent.network_ports = { NETWORK_ID: [PORT_DATA] } self.lb_rpc.network_update(context=None, network=updated_network) self.assertEqual(set([DEVICE_1]), self.lb_rpc.updated_devices) def test_network_delete_with_existed_brq(self): mock_net = mock.Mock() mock_net.physical_network = 'physnet0' self.lb_rpc.network_map = {'123': mock_net} with mock.patch.object(linuxbridge_neutron_agent.LOG, 'info') as log,\ mock.patch.object(self.lb_rpc.agent.mgr, "delete_bridge") as del_fn: self.lb_rpc.network_delete("anycontext", network_id="123") self.assertEqual(0, del_fn.call_count) self.assertEqual(1, log.call_count) def _test_fdb_add(self, proxy_enabled=False): fdb_entries = {'net_id': {'ports': {'agent_ip': [constants.FLOODING_ENTRY, ['port_mac', 'port_ip']]}, 'network_type': 'vxlan', 'segment_id': 1}} with mock.patch.object(utils, 'execute', return_value='') as execute_fn, \ mock.patch.object(ip_lib, 'add_neigh_entry', return_value='') as add_fn: self.lb_rpc.fdb_add(None, fdb_entries) expected = [ mock.call(['bridge', 'fdb', 'show', 'dev', 'vxlan-1'], run_as_root=True), mock.call(['bridge', 'fdb', 'add', constants.FLOODING_ENTRY[0], 'dev', 'vxlan-1', 'dst', 'agent_ip'], run_as_root=True, check_exit_code=False), mock.call(['bridge', 'fdb', 'replace', 'port_mac', 'dev', 'vxlan-1', 'dst', 'agent_ip'], run_as_root=True, check_exit_code=False), ] execute_fn.assert_has_calls(expected) if proxy_enabled: add_fn.assert_called_with('port_ip', 'port_mac', 'vxlan-1') else: add_fn.assert_not_called() def test_fdb_add(self): self._test_fdb_add(proxy_enabled=False) def test_fdb_add_with_arp_responder(self): cfg.CONF.set_override('arp_responder', True, 'VXLAN') self._test_fdb_add(proxy_enabled=True) def test_fdb_ignore(self): fdb_entries = {'net_id': {'ports': {LOCAL_IP: [constants.FLOODING_ENTRY, ['port_mac', 'port_ip']]}, 'network_type': 'vxlan', 'segment_id': 1}} with mock.patch.object(utils, 'execute', return_value='') as execute_fn: self.lb_rpc.fdb_add(None, fdb_entries) self.lb_rpc.fdb_remove(None, fdb_entries) self.assertFalse(execute_fn.called) fdb_entries = {'other_net_id': {'ports': {'192.168.0.67': [constants.FLOODING_ENTRY, ['port_mac', 'port_ip']]}, 'network_type': 'vxlan', 'segment_id': 1}} with mock.patch.object(utils, 'execute', return_value='') as execute_fn: self.lb_rpc.fdb_add(None, fdb_entries) self.lb_rpc.fdb_remove(None, fdb_entries) self.assertFalse(execute_fn.called) def _test_fdb_remove(self, proxy_enabled=False): fdb_entries = {'net_id': {'ports': {'agent_ip': [constants.FLOODING_ENTRY, ['port_mac', 'port_ip']]}, 'network_type': 'vxlan', 'segment_id': 1}} with mock.patch.object(utils, 'execute', return_value='') as execute_fn, \ mock.patch.object(ip_lib, 'delete_neigh_entry', return_value='') as del_fn: self.lb_rpc.fdb_remove(None, fdb_entries) expected = [ mock.call(['bridge', 'fdb', 'delete', constants.FLOODING_ENTRY[0], 'dev', 'vxlan-1', 'dst', 'agent_ip'], run_as_root=True, check_exit_code=False), mock.call(['bridge', 'fdb', 'delete', 'port_mac', 'dev', 'vxlan-1', 'dst', 'agent_ip'], run_as_root=True, check_exit_code=False), ] execute_fn.assert_has_calls(expected) if proxy_enabled: del_fn.assert_called_with('port_ip', 'port_mac', 'vxlan-1') else: del_fn.assert_not_called() def test_fdb_remove(self): self._test_fdb_remove(proxy_enabled=False) def test_fdb_remove_with_arp_responder(self): cfg.CONF.set_override('arp_responder', True, 'VXLAN') self._test_fdb_remove(proxy_enabled=True) def _test_fdb_update_chg_ip(self, proxy_enabled=False): fdb_entries = {'chg_ip': {'net_id': {'agent_ip': {'before': [['port_mac', 'port_ip_1']], 'after': [['port_mac', 'port_ip_2']]}}}} with mock.patch.object(ip_lib, 'add_neigh_entry', return_value='') as add_fn, \ mock.patch.object(ip_lib, 'delete_neigh_entry', return_value='') as del_fn: self.lb_rpc.fdb_update(None, fdb_entries) if proxy_enabled: del_fn.assert_called_with('port_ip_1', 'port_mac', 'vxlan-1') add_fn.assert_called_with('port_ip_2', 'port_mac', 'vxlan-1') else: del_fn.assert_not_called() add_fn.assert_not_called() def test_fdb_update_chg_ip(self): self._test_fdb_update_chg_ip(proxy_enabled=False) def test_fdb_update_chg_ip_with_arp_responder(self): cfg.CONF.set_override('arp_responder', True, 'VXLAN') self._test_fdb_update_chg_ip(proxy_enabled=True) def test_fdb_update_chg_ip_empty_lists(self): fdb_entries = {'chg_ip': {'net_id': {'agent_ip': {}}}} self.lb_rpc.fdb_update(None, fdb_entries) ././@LongLink0000000000000000000000000000016000000000000011212 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_agent_extension_api.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_agent_exten0000664000175000017500000000230713553660046034257 0ustar zuulzuul00000000000000# Copyright 2017 OVH SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.plugins.ml2.drivers.linuxbridge.agent import \ linuxbridge_agent_extension_api as ext_api from neutron.tests import base class TestLinuxbridgeAgentExtensionAPI(base.BaseTestCase): def setUp(self): super(TestLinuxbridgeAgentExtensionAPI, self).setUp() self.iptables_manager = mock.Mock() self.extension_api = ext_api.LinuxbridgeAgentExtensionAPI( self.iptables_manager) def test_get_iptables_manager(self): self.assertEqual(self.iptables_manager, self.extension_api.get_iptables_manager()) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/__init__.py0000664000175000017500000000000013553660046030475 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_arp_protect.py0000664000175000017500000001735713553660046032346 0ustar zuulzuul00000000000000# Copyright (c) 2018 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib import constants from neutron.agent.common import utils from neutron.plugins.ml2.drivers.linuxbridge.agent import arp_protect from neutron.tests import base VIF = 'vif_tap0' PORT_NO_SEC = {'port_security_enabled': False} PORT_TRUSTED = {'device_owner': constants.DEVICE_OWNER_ROUTER_GW} PORT = {'fixed_ips': [{'ip_address': '10.1.1.1'}], 'device_owner': 'nobody', 'mac_address': '00:11:22:33:44:55'} PORT_ADDR_PAIR = {'fixed_ips': [{'ip_address': '10.1.1.1'}], 'device_owner': 'nobody', 'mac_address': '00:11:22:33:44:55', 'allowed_address_pairs': [ {'mac_address': '00:11:22:33:44:66', 'ip_address': '10.1.1.2'}]} class TestLinuxBridgeARPSpoofing(base.BaseTestCase): def setUp(self): super(TestLinuxBridgeARPSpoofing, self).setUp() self.execute = mock.patch.object(utils, "execute").start() @mock.patch.object(arp_protect, "delete_arp_spoofing_protection") def test_port_no_security(self, dasp): arp_protect.setup_arp_spoofing_protection(VIF, PORT_NO_SEC) dasp.assert_called_with([VIF]) @mock.patch.object(arp_protect, "delete_arp_spoofing_protection") def test_port_trusted(self, dasp): arp_protect.setup_arp_spoofing_protection(VIF, PORT_TRUSTED) dasp.assert_called_with([VIF]) def _test_port_add_arp_spoofing(self, vif, port): mac_addresses = {port['mac_address']} ip_addresses = {p['ip_address'] for p in port['fixed_ips']} if port.get('allowed_address_pairs'): mac_addresses |= {p['mac_address'] for p in port['allowed_address_pairs']} ip_addresses |= {p['ip_address'] for p in port['allowed_address_pairs']} spoof_chain = arp_protect.SPOOF_CHAIN_PREFIX + vif mac_chain = arp_protect.MAC_CHAIN_PREFIX + vif expected = [ mock.call(['ebtables', '-t', 'nat', '--concurrent', '-L'], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), mock.ANY, mock.ANY, mock.call(['ebtables', '-t', 'nat', '--concurrent', '-N', 'neutronMAC-%s' % vif, '-P', 'DROP'], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), mock.ANY, mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A', 'PREROUTING', '-i', vif, '-j', mac_chain], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A', mac_chain, '-i', vif, '--among-src', '%s' % ','.join(sorted(mac_addresses)), '-j', 'RETURN'], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), mock.ANY, mock.ANY, mock.call(['ebtables', '-t', 'nat', '--concurrent', '-N', spoof_chain, '-P', 'DROP'], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), mock.call(['ebtables', '-t', 'nat', '--concurrent', '-F', spoof_chain], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), ] for addr in sorted(ip_addresses): expected.extend([ mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A', spoof_chain, '-p', 'ARP', '--arp-ip-src', addr, '-j', 'ACCEPT'], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), ]) expected.extend([ mock.ANY, mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A', 'PREROUTING', '-i', vif, '-j', spoof_chain, '-p', 'ARP'], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), ]) arp_protect.setup_arp_spoofing_protection(vif, port) self.execute.assert_has_calls(expected) def test_port_add_arp_spoofing(self): self._test_port_add_arp_spoofing(VIF, PORT) def test_port_add_arp_spoofing_addr_pair(self): self._test_port_add_arp_spoofing(VIF, PORT_ADDR_PAIR) @mock.patch.object(arp_protect, "chain_exists", return_value=True) @mock.patch.object(arp_protect, "vif_jump_present", return_value=True) def test_port_delete_arp_spoofing(self, ce, vjp): spoof_chain = arp_protect.SPOOF_CHAIN_PREFIX + VIF mac_chain = arp_protect.MAC_CHAIN_PREFIX + VIF expected = [ mock.call(['ebtables', '-t', 'nat', '--concurrent', '-L'], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), mock.ANY, mock.call(['ebtables', '-t', 'nat', '--concurrent', '-D', 'PREROUTING', '-i', VIF, '-j', spoof_chain, '-p', 'ARP'], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), mock.call(['ebtables', '-t', 'nat', '--concurrent', '-X', spoof_chain], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), mock.ANY, mock.call(['ebtables', '-t', 'nat', '--concurrent', '-X', mac_chain], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), mock.call(['ebtables', '-t', 'filter', '--concurrent', '-L'], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), mock.ANY, mock.call(['ebtables', '-t', 'filter', '--concurrent', '-D', 'FORWARD', '-i', VIF, '-j', spoof_chain, '-p', 'ARP'], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), mock.call(['ebtables', '-t', 'filter', '--concurrent', '-X', spoof_chain], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), mock.ANY, mock.call(['ebtables', '-t', 'filter', '--concurrent', '-X', mac_chain], check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True), ] arp_protect.delete_arp_spoofing_protection([VIF]) self.execute.assert_has_calls(expected) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/extension_drivers/0000775000175000017500000000000013553660157032153 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/extension_drivers/test_qos_driver.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/extension_drivers/test_qos_d0000664000175000017500000003005213553660047034240 0ustar zuulzuul00000000000000# Copyright 2016 OVH SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants from oslo_config import cfg from oslo_utils import uuidutils from neutron.agent.linux import tc_lib from neutron.objects.qos import rule from neutron.plugins.ml2.drivers.linuxbridge.agent.common import config # noqa from neutron.plugins.ml2.drivers.linuxbridge.agent.extension_drivers import ( qos_driver) from neutron.tests import base TEST_LATENCY_VALUE = 100 DSCP_VALUE = 32 class QosLinuxbridgeAgentDriverTestCase(base.BaseTestCase): def setUp(self): super(QosLinuxbridgeAgentDriverTestCase, self).setUp() cfg.CONF.set_override("tbf_latency", TEST_LATENCY_VALUE, "QOS") self.qos_driver = qos_driver.QosLinuxbridgeAgentDriver() self.qos_driver.initialize() self.rule_egress_bw_limit = self._create_bw_limit_rule_obj( constants.EGRESS_DIRECTION) self.rule_ingress_bw_limit = self._create_bw_limit_rule_obj( constants.INGRESS_DIRECTION) self.rule_dscp_marking = self._create_dscp_marking_rule_obj() self.port = self._create_fake_port(uuidutils.generate_uuid()) def _create_bw_limit_rule_obj(self, direction): rule_obj = rule.QosBandwidthLimitRule() rule_obj.id = uuidutils.generate_uuid() rule_obj.max_kbps = 2 rule_obj.max_burst_kbps = 200 rule_obj.direction = direction rule_obj.obj_reset_changes() return rule_obj def _create_dscp_marking_rule_obj(self): rule_obj = rule.QosDscpMarkingRule() rule_obj.id = uuidutils.generate_uuid() rule_obj.dscp_mark = DSCP_VALUE rule_obj.obj_reset_changes() return rule_obj def _create_fake_port(self, policy_id): return {'qos_policy_id': policy_id, 'network_qos_policy_id': None, 'device': 'fake_tap'} def _dscp_mark_chain_name(self, device): return "qos-o%s" % device[3:] def _dscp_postrouting_rule(self, device): return ("-m physdev --physdev-in %s --physdev-is-bridged " "-j $qos-o%s") % (device, device[3:]) def _dscp_rule(self, dscp_mark_value): return "-j DSCP --set-dscp %s" % format(dscp_mark_value, '#04x') def _dscp_rule_tag(self, device): return "dscp-%s" % device def test_initialize_iptables_manager_passed_through_api(self): iptables_manager = mock.Mock() qos_drv = qos_driver.QosLinuxbridgeAgentDriver() with mock.patch.object( qos_drv, "agent_api" ) as agent_api, mock.patch( "neutron.agent.linux.iptables_manager.IptablesManager" ) as IptablesManager: agent_api.get_iptables_manager.return_value = ( iptables_manager) qos_drv.initialize() self.assertEqual(iptables_manager, qos_drv.iptables_manager) self.assertNotEqual(IptablesManager(), qos_drv.iptables_manager) iptables_manager.initialize_mangle_table.assert_called_once_with() def test_initialize_iptables_manager_not_passed_through_api(self): qos_drv = qos_driver.QosLinuxbridgeAgentDriver() with mock.patch.object( qos_drv, "agent_api" ) as agent_api, mock.patch( "neutron.agent.linux.iptables_manager.IptablesManager" ) as IptablesManager: agent_api.get_iptables_manager.return_value = None qos_drv.initialize() self.assertEqual(IptablesManager(), qos_drv.iptables_manager) IptablesManager().initialize_mangle_table.assert_called_once_with() def test_initialize_iptables_manager_no_agent_api(self): qos_drv = qos_driver.QosLinuxbridgeAgentDriver() with mock.patch( "neutron.agent.linux.iptables_manager.IptablesManager" ) as IptablesManager: qos_driver.agent_api = None qos_drv.initialize() self.assertEqual(IptablesManager(), qos_drv.iptables_manager) IptablesManager().initialize_mangle_table.assert_called_once_with() def test_create_egress_bandwidth_limit(self): with mock.patch.object( tc_lib.TcCommand, "set_filters_bw_limit" ) as set_filters_bw_limit, mock.patch.object( tc_lib.TcCommand, "set_tbf_bw_limit" ) as set_tbf_limit: self.qos_driver.create_bandwidth_limit(self.port, self.rule_egress_bw_limit) set_filters_bw_limit.assert_called_once_with( self.rule_egress_bw_limit.max_kbps, self.rule_egress_bw_limit.max_burst_kbps, ) set_tbf_limit.assert_not_called() def test_create_ingress_bandwidth_limit(self): with mock.patch.object( tc_lib.TcCommand, "set_filters_bw_limit" ) as set_filters_bw_limit, mock.patch.object( tc_lib.TcCommand, "set_tbf_bw_limit" ) as set_tbf_limit: self.qos_driver.create_bandwidth_limit(self.port, self.rule_ingress_bw_limit) set_filters_bw_limit.assert_not_called() set_tbf_limit.assert_called_once_with( self.rule_ingress_bw_limit.max_kbps, self.rule_ingress_bw_limit.max_burst_kbps, TEST_LATENCY_VALUE ) def test_update_egress_bandwidth_limit(self): with mock.patch.object( tc_lib.TcCommand, "update_filters_bw_limit" ) as update_filters_bw_limit, mock.patch.object( tc_lib.TcCommand, "update_tbf_bw_limit" ) as update_tbf_bw_limit: self.qos_driver.update_bandwidth_limit(self.port, self.rule_egress_bw_limit) update_filters_bw_limit.assert_called_once_with( self.rule_egress_bw_limit.max_kbps, self.rule_egress_bw_limit.max_burst_kbps, ) update_tbf_bw_limit.assert_not_called() def test_update_ingress_bandwidth_limit(self): with mock.patch.object( tc_lib.TcCommand, "update_filters_bw_limit" ) as update_filters_bw_limit, mock.patch.object( tc_lib.TcCommand, "update_tbf_bw_limit" ) as update_tbf_bw_limit: self.qos_driver.update_bandwidth_limit(self.port, self.rule_ingress_bw_limit) update_filters_bw_limit.assert_not_called() update_tbf_bw_limit.assert_called_once_with( self.rule_egress_bw_limit.max_kbps, self.rule_egress_bw_limit.max_burst_kbps, TEST_LATENCY_VALUE ) def test_delete_bandwidth_limit(self): with mock.patch.object( tc_lib.TcCommand, "delete_filters_bw_limit" ) as delete_filters_bw_limit: self.qos_driver.delete_bandwidth_limit(self.port) delete_filters_bw_limit.assert_called_once_with() def test_delete_ingress_bandwidth_limit(self): with mock.patch.object( tc_lib.TcCommand, "delete_tbf_bw_limit" ) as delete_tbf_bw_limit: self.qos_driver.delete_bandwidth_limit_ingress(self.port) delete_tbf_bw_limit.assert_called_once_with() def test_create_dscp_marking(self): expected_calls = [ mock.call.add_chain( self._dscp_mark_chain_name(self.port['device'])), mock.call.add_rule( "POSTROUTING", self._dscp_postrouting_rule(self.port['device'])), mock.call.add_rule( self._dscp_mark_chain_name(self.port['device']), self._dscp_rule(DSCP_VALUE), tag=self._dscp_rule_tag(self.port['device']) ) ] with mock.patch.object( self.qos_driver, "iptables_manager") as iptables_manager: iptables_manager.ip4['mangle'] = mock.Mock() iptables_manager.ip6['mangle'] = mock.Mock() self.qos_driver.create_dscp_marking( self.port, self.rule_dscp_marking) iptables_manager.ipv4['mangle'].assert_has_calls(expected_calls) iptables_manager.ipv6['mangle'].assert_has_calls(expected_calls) def test_update_dscp_marking(self): expected_calls = [ mock.call.clear_rules_by_tag( self._dscp_rule_tag(self.port['device'])), mock.call.add_chain( self._dscp_mark_chain_name(self.port['device'])), mock.call.add_rule( "POSTROUTING", self._dscp_postrouting_rule(self.port['device'])), mock.call.add_rule( self._dscp_mark_chain_name(self.port['device']), self._dscp_rule(DSCP_VALUE), tag=self._dscp_rule_tag(self.port['device']) ) ] with mock.patch.object( self.qos_driver, "iptables_manager") as iptables_manager: iptables_manager.ip4['mangle'] = mock.Mock() iptables_manager.ip6['mangle'] = mock.Mock() self.qos_driver.update_dscp_marking( self.port, self.rule_dscp_marking) iptables_manager.ipv4['mangle'].assert_has_calls(expected_calls) iptables_manager.ipv6['mangle'].assert_has_calls(expected_calls) def test_delete_dscp_marking_chain_empty(self): dscp_chain_name = self._dscp_mark_chain_name(self.port['device']) expected_calls = [ mock.call.clear_rules_by_tag( self._dscp_rule_tag(self.port['device'])), mock.call.remove_chain( dscp_chain_name), mock.call.remove_rule( "POSTROUTING", self._dscp_postrouting_rule(self.port['device'])) ] with mock.patch.object( self.qos_driver, "iptables_manager") as iptables_manager: iptables_manager.ip4['mangle'] = mock.Mock() iptables_manager.ip6['mangle'] = mock.Mock() iptables_manager.get_chain = mock.Mock(return_value=[]) self.qos_driver.delete_dscp_marking(self.port) iptables_manager.ipv4['mangle'].assert_has_calls(expected_calls) iptables_manager.ipv6['mangle'].assert_has_calls(expected_calls) iptables_manager.get_chain.assert_has_calls([ mock.call("mangle", dscp_chain_name, ip_version=4), mock.call("mangle", dscp_chain_name, ip_version=6) ]) def test_delete_dscp_marking_chain_not_empty(self): dscp_chain_name = self._dscp_mark_chain_name(self.port['device']) expected_calls = [ mock.call.clear_rules_by_tag( self._dscp_rule_tag(self.port['device'])), ] with mock.patch.object( self.qos_driver, "iptables_manager") as iptables_manager: iptables_manager.ip4['mangle'] = mock.Mock() iptables_manager.ip6['mangle'] = mock.Mock() iptables_manager.get_chain = mock.Mock( return_value=["some other rule"]) self.qos_driver.delete_dscp_marking(self.port) iptables_manager.ipv4['mangle'].assert_has_calls(expected_calls) iptables_manager.ipv6['mangle'].assert_has_calls(expected_calls) iptables_manager.get_chain.assert_has_calls([ mock.call("mangle", dscp_chain_name, ip_version=4), mock.call("mangle", dscp_chain_name, ip_version=6) ]) iptables_manager.ipv4['mangle'].remove_chain.assert_not_called() iptables_manager.ipv4['mangle'].remove_rule.assert_not_called() ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/extension_drivers/__init__.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/extension_drivers/__init__.p0000664000175000017500000000000013553660046034056 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/__init__.py0000664000175000017500000000000013553660046027377 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/mech_driver/0000775000175000017500000000000013553660157027572 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/mech_driver/__init__.py0000664000175000017500000000000013553660046031666 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/mech_driver/test_mech_linuxbridge.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/mech_driver/test_mech_linuxbridge.0000664000175000017500000000565313553660046034150 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron.plugins.ml2.drivers.linuxbridge.mech_driver \ import mech_linuxbridge from neutron.tests.unit.plugins.ml2 import _test_mech_agent as base class LinuxbridgeMechanismBaseTestCase(base.AgentMechanismBaseTestCase): VIF_TYPE = portbindings.VIF_TYPE_BRIDGE CAP_PORT_FILTER = True AGENT_TYPE = constants.AGENT_TYPE_LINUXBRIDGE GOOD_MAPPINGS = {'fake_physical_network': 'fake_interface'} GOOD_TUNNEL_TYPES = ['gre', 'vxlan'] GOOD_CONFIGS = {'interface_mappings': GOOD_MAPPINGS, 'tunnel_types': GOOD_TUNNEL_TYPES} BAD_MAPPINGS = {'wrong_physical_network': 'wrong_interface'} BAD_TUNNEL_TYPES = ['bad_tunnel_type'] BAD_CONFIGS = {'interface_mappings': BAD_MAPPINGS, 'tunnel_types': BAD_TUNNEL_TYPES} AGENTS = [{'alive': True, 'configurations': GOOD_CONFIGS, 'host': 'host'}] AGENTS_DEAD = [{'alive': False, 'configurations': GOOD_CONFIGS, 'host': 'dead_host'}] AGENTS_BAD = [{'alive': False, 'configurations': GOOD_CONFIGS, 'host': 'bad_host_1'}, {'alive': True, 'configurations': BAD_CONFIGS, 'host': 'bad_host_2'}] def setUp(self): super(LinuxbridgeMechanismBaseTestCase, self).setUp() self.driver = mech_linuxbridge.LinuxbridgeMechanismDriver() self.driver.initialize() class LinuxbridgeMechanismGenericTestCase(LinuxbridgeMechanismBaseTestCase, base.AgentMechanismGenericTestCase): pass class LinuxbridgeMechanismLocalTestCase(LinuxbridgeMechanismBaseTestCase, base.AgentMechanismLocalTestCase): pass class LinuxbridgeMechanismFlatTestCase(LinuxbridgeMechanismBaseTestCase, base.AgentMechanismFlatTestCase): pass class LinuxbridgeMechanismVlanTestCase(LinuxbridgeMechanismBaseTestCase, base.AgentMechanismVlanTestCase): pass class LinuxbridgeMechanismGreTestCase(LinuxbridgeMechanismBaseTestCase, base.AgentMechanismGreTestCase): pass neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/test_type_vlan.py0000664000175000017500000003503113553660047026401 0ustar zuulzuul00000000000000# Copyright (c) 2014 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as p_const from neutron_lib import context from neutron_lib import exceptions as exc from neutron_lib.plugins.ml2 import api from oslo_config import cfg from testtools import matchers from neutron.db import api as db_api from neutron.objects.plugins.ml2 import vlanallocation as vlan_alloc_obj from neutron.plugins.common import utils as plugin_utils from neutron.plugins.ml2.drivers import type_vlan from neutron.tests.unit import testlib_api PROVIDER_NET = 'phys_net1' TENANT_NET = 'phys_net2' VLAN_MIN = 200 VLAN_MAX = 209 NETWORK_VLAN_RANGES = [PROVIDER_NET, "%s:%s:%s" % (TENANT_NET, VLAN_MIN, VLAN_MAX)] UPDATED_VLAN_RANGES = { PROVIDER_NET: [], TENANT_NET: [(VLAN_MIN + 5, VLAN_MAX + 5)], } EMPTY_VLAN_RANGES = { PROVIDER_NET: [] } CORE_PLUGIN = 'ml2' class VlanTypeTest(testlib_api.SqlTestCase): def setUp(self): super(VlanTypeTest, self).setUp() cfg.CONF.set_override('network_vlan_ranges', NETWORK_VLAN_RANGES, group='ml2_type_vlan') self.network_vlan_ranges = plugin_utils.parse_network_vlan_ranges( NETWORK_VLAN_RANGES) self.driver = type_vlan.VlanTypeDriver() self.driver._sync_vlan_allocations() self.context = context.Context() self.driver.physnet_mtus = [] self.setup_coreplugin(CORE_PLUGIN) def test_parse_network_exception_handling(self): with mock.patch.object(plugin_utils, 'parse_network_vlan_ranges') as parse_ranges: parse_ranges.side_effect = Exception('any exception') self.assertRaises(SystemExit, self.driver._parse_network_vlan_ranges) @db_api.context_manager.reader def _get_allocation(self, context, segment): return vlan_alloc_obj.VlanAllocation.get_object( context, physical_network=segment[api.PHYSICAL_NETWORK], vlan_id=segment[api.SEGMENTATION_ID]) def test_partial_segment_is_partial_segment(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN} self.assertTrue(self.driver.is_partial_segment(segment)) def test_specific_segment_is_not_partial_segment(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: PROVIDER_NET, api.SEGMENTATION_ID: 1} self.assertFalse(self.driver.is_partial_segment(segment)) def test_validate_provider_segment(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: PROVIDER_NET, api.SEGMENTATION_ID: 1} self.assertIsNone(self.driver.validate_provider_segment(segment)) def test_validate_provider_segment_without_segmentation_id(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: TENANT_NET} self.driver.validate_provider_segment(segment) def test_validate_provider_segment_without_physical_network(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN} self.driver.validate_provider_segment(segment) def test_validate_provider_segment_no_phys_network_seg_id_0(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.SEGMENTATION_ID: 0} self.assertRaises(exc.InvalidInput, self.driver.validate_provider_segment, segment) def test_validate_provider_segment_with_missing_physical_network(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.SEGMENTATION_ID: 1} self.assertRaises(exc.InvalidInput, self.driver.validate_provider_segment, segment) def test_validate_provider_segment_with_invalid_physical_network(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: 'other_phys_net', api.SEGMENTATION_ID: 1} self.assertRaises(exc.InvalidInput, self.driver.validate_provider_segment, segment) def test_validate_provider_segment_with_invalid_segmentation_id(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: PROVIDER_NET} segmentation_ids = [ p_const.MIN_VLAN_TAG - 1, p_const.MAX_VLAN_TAG + 1] for segmentation_id in segmentation_ids: segment[api.SEGMENTATION_ID] = segmentation_id self.assertRaises(exc.InvalidInput, self.driver.validate_provider_segment, segment) def test_validate_provider_segment_with_invalid_input(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: PROVIDER_NET, api.SEGMENTATION_ID: 1, 'invalid': 1} self.assertRaises(exc.InvalidInput, self.driver.validate_provider_segment, segment) def test_validate_provider_segment_with_physical_network_only(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: PROVIDER_NET} self.assertRaises(exc.InvalidInput, self.driver.validate_provider_segment, segment) def test_sync_vlan_allocations(self): def check_in_ranges(network_vlan_ranges): vlan_min, vlan_max = network_vlan_ranges[TENANT_NET][0] segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: TENANT_NET} segment[api.SEGMENTATION_ID] = vlan_min - 1 self.assertIsNone( self._get_allocation(self.context, segment)) segment[api.SEGMENTATION_ID] = vlan_max + 1 self.assertIsNone( self._get_allocation(self.context, segment)) segment[api.SEGMENTATION_ID] = vlan_min self.assertFalse( self._get_allocation(self.context, segment).allocated) segment[api.SEGMENTATION_ID] = vlan_max self.assertFalse( self._get_allocation(self.context, segment).allocated) check_in_ranges(self.network_vlan_ranges) self.driver.network_vlan_ranges = UPDATED_VLAN_RANGES self.driver._sync_vlan_allocations() check_in_ranges(UPDATED_VLAN_RANGES) self.driver.network_vlan_ranges = EMPTY_VLAN_RANGES self.driver._sync_vlan_allocations() vlan_min, vlan_max = UPDATED_VLAN_RANGES[TENANT_NET][0] segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: TENANT_NET} segment[api.SEGMENTATION_ID] = vlan_min self.assertIsNone( self._get_allocation(self.context, segment)) segment[api.SEGMENTATION_ID] = vlan_max self.assertIsNone( self._get_allocation(self.context, segment)) def test_reserve_provider_segment(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: PROVIDER_NET, api.SEGMENTATION_ID: 101} alloc = self._get_allocation(self.context, segment) self.assertIsNone(alloc) observed = self.driver.reserve_provider_segment(self.context, segment) alloc = self._get_allocation(self.context, observed) self.assertTrue(alloc.allocated) def test_reserve_provider_segment_already_allocated(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: PROVIDER_NET, api.SEGMENTATION_ID: 101} observed = self.driver.reserve_provider_segment(self.context, segment) self.assertRaises(exc.VlanIdInUse, self.driver.reserve_provider_segment, self.context, observed) def test_reserve_provider_segment_in_tenant_pools(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: TENANT_NET, api.SEGMENTATION_ID: VLAN_MIN} alloc = self._get_allocation(self.context, segment) self.assertFalse(alloc.allocated) observed = self.driver.reserve_provider_segment(self.context, segment) alloc = self._get_allocation(self.context, observed) self.assertTrue(alloc.allocated) def test_reserve_provider_segment_without_segmentation_id(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: TENANT_NET} observed = self.driver.reserve_provider_segment(self.context, segment) alloc = self._get_allocation(self.context, observed) self.assertTrue(alloc.allocated) vlan_id = observed[api.SEGMENTATION_ID] self.assertThat(vlan_id, matchers.GreaterThan(VLAN_MIN - 1)) self.assertThat(vlan_id, matchers.LessThan(VLAN_MAX + 1)) def test_reserve_provider_segment_without_physical_network(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN} observed = self.driver.reserve_provider_segment(self.context, segment) alloc = self._get_allocation(self.context, observed) self.assertTrue(alloc.allocated) vlan_id = observed[api.SEGMENTATION_ID] self.assertThat(vlan_id, matchers.GreaterThan(VLAN_MIN - 1)) self.assertThat(vlan_id, matchers.LessThan(VLAN_MAX + 1)) self.assertEqual(TENANT_NET, observed[api.PHYSICAL_NETWORK]) def test_reserve_provider_segment_all_allocateds(self): for __ in range(VLAN_MIN, VLAN_MAX + 1): self.driver.allocate_tenant_segment(self.context) segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN} self.assertRaises(exc.NoNetworkAvailable, self.driver.reserve_provider_segment, self.context, segment) def test_get_mtu(self): cfg.CONF.set_override('global_physnet_mtu', 1475) cfg.CONF.set_override('path_mtu', 1400, group='ml2') self.driver.physnet_mtus = {'physnet1': 1450, 'physnet2': 1400} self.assertEqual(1450, self.driver.get_mtu('physnet1')) cfg.CONF.set_override('global_physnet_mtu', 1375) cfg.CONF.set_override('path_mtu', 1400, group='ml2') self.driver.physnet_mtus = {'physnet1': 1450, 'physnet2': 1400} self.assertEqual(1375, self.driver.get_mtu('physnet1')) cfg.CONF.set_override('global_physnet_mtu', 0) cfg.CONF.set_override('path_mtu', 1400, group='ml2') self.driver.physnet_mtus = {'physnet1': 1450, 'physnet2': 1400} self.assertEqual(1450, self.driver.get_mtu('physnet1')) cfg.CONF.set_override('global_physnet_mtu', 0) cfg.CONF.set_override('path_mtu', 0, group='ml2') self.driver.physnet_mtus = {} self.assertEqual(0, self.driver.get_mtu('physnet1')) def test_allocate_tenant_segment(self): for __ in range(VLAN_MIN, VLAN_MAX + 1): segment = self.driver.allocate_tenant_segment(self.context) alloc = self._get_allocation(self.context, segment) self.assertTrue(alloc.allocated) vlan_id = segment[api.SEGMENTATION_ID] self.assertThat(vlan_id, matchers.GreaterThan(VLAN_MIN - 1)) self.assertThat(vlan_id, matchers.LessThan(VLAN_MAX + 1)) self.assertEqual(TENANT_NET, segment[api.PHYSICAL_NETWORK]) def test_allocate_tenant_segment_no_available(self): for __ in range(VLAN_MIN, VLAN_MAX + 1): self.driver.allocate_tenant_segment(self.context) segment = self.driver.allocate_tenant_segment(self.context) self.assertIsNone(segment) def test_release_segment(self): segment = self.driver.allocate_tenant_segment(self.context) self.driver.release_segment(self.context, segment) alloc = self._get_allocation(self.context, segment) self.assertFalse(alloc.allocated) def test_release_segment_unallocated(self): segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, api.PHYSICAL_NETWORK: PROVIDER_NET, api.SEGMENTATION_ID: 101} with mock.patch.object(type_vlan.LOG, 'warning') as log_warn: self.driver.release_segment(self.context, segment) log_warn.assert_called_once_with( "No vlan_id %(vlan_id)s found on physical network " "%(physical_network)s", {'vlan_id': 101, 'physical_network': PROVIDER_NET}) class VlanTypeAllocationTest(testlib_api.SqlTestCase): def test_allocate_tenant_segment_in_order_of_config(self): ranges = NETWORK_VLAN_RANGES + ['phys_net3:20:30'] cfg.CONF.set_override('network_vlan_ranges', ranges, group='ml2_type_vlan') driver = type_vlan.VlanTypeDriver() driver.physnet_mtus = [] driver._sync_vlan_allocations() # swap config order from DB order after sync has happened to # ensure config order is followed and not DB order cfg.CONF.set_override('network_vlan_ranges', list(reversed(ranges)), group='ml2_type_vlan') driver._parse_network_vlan_ranges() ctx = context.Context() for vlan in range(11): # all of physnet3 should be exhausted first self.assertEqual( {'network_type': 'vlan', 'physical_network': 'phys_net3', 'segmentation_id': mock.ANY, 'mtu': 1500}, driver.allocate_tenant_segment(ctx)) for vlan in range(10): # then physnet2 self.assertEqual( {'network_type': 'vlan', 'physical_network': 'phys_net2', 'segmentation_id': mock.ANY, 'mtu': 1500}, driver.allocate_tenant_segment(ctx)) # then nothing self.assertFalse(driver.allocate_tenant_segment(ctx)) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/base_type_tunnel.py0000664000175000017500000004427613553660047026714 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation, all rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib import constants as p_const from neutron_lib import context from neutron_lib import exceptions as exc from neutron_lib.plugins.ml2 import api from oslo_config import cfg from six import moves import testtools from testtools import matchers from neutron.plugins.ml2.drivers import type_tunnel TUNNEL_IP_ONE = "10.10.10.10" TUNNEL_IP_TWO = "10.10.10.20" TUNNEL_IPV6_ONE = "2001:db8:1::10" HOST_ONE = 'fake_host_one' HOST_TWO = 'fake_host_two' TUN_MIN = 100 TUN_MAX = 109 TUNNEL_RANGES = [(TUN_MIN, TUN_MAX)] UPDATED_TUNNEL_RANGES = [(TUN_MIN + 5, TUN_MAX + 5)] class TunnelTypeTestMixin(object): DRIVER_CLASS = None TYPE = None def setUp(self): super(TunnelTypeTestMixin, self).setUp() self.driver = self.DRIVER_CLASS() self.driver.tunnel_ranges = TUNNEL_RANGES self.driver.sync_allocations() self.context = context.Context() def test_tunnel_type(self): self.assertEqual(self.TYPE, self.driver.get_type()) def test_validate_provider_segment(self): segment = {api.NETWORK_TYPE: self.TYPE, api.PHYSICAL_NETWORK: 'phys_net', api.SEGMENTATION_ID: None} with testtools.ExpectedException(exc.InvalidInput): self.driver.validate_provider_segment(segment) segment[api.PHYSICAL_NETWORK] = None self.driver.validate_provider_segment(segment) segment[api.SEGMENTATION_ID] = 1 self.driver.validate_provider_segment(segment) def test_sync_tunnel_allocations(self): self.assertIsNone( self.driver.get_allocation(self.context, (TUN_MIN - 1))) self.assertFalse( self.driver.get_allocation(self.context, (TUN_MIN)).allocated) self.assertFalse( self.driver.get_allocation(self.context, (TUN_MIN + 1)).allocated) self.assertFalse( self.driver.get_allocation(self.context, (TUN_MAX - 1)).allocated) self.assertFalse( self.driver.get_allocation(self.context, (TUN_MAX)).allocated) self.assertIsNone( self.driver.get_allocation(self.context, (TUN_MAX + 1))) self.driver.tunnel_ranges = UPDATED_TUNNEL_RANGES self.driver.sync_allocations() self.assertIsNone( self.driver.get_allocation(self.context, (TUN_MIN + 5 - 1))) self.assertFalse( self.driver.get_allocation(self.context, (TUN_MIN + 5)).allocated) self.assertFalse( self.driver.get_allocation(self.context, (TUN_MIN + 5 + 1)).allocated) self.assertFalse( self.driver.get_allocation(self.context, (TUN_MAX + 5 - 1)).allocated) self.assertFalse( self.driver.get_allocation(self.context, (TUN_MAX + 5)).allocated) self.assertIsNone( self.driver.get_allocation(self.context, (TUN_MAX + 5 + 1))) def _test_sync_allocations_and_allocated(self, tunnel_id): segment = {api.NETWORK_TYPE: self.TYPE, api.PHYSICAL_NETWORK: None, api.SEGMENTATION_ID: tunnel_id} self.driver.reserve_provider_segment(self.context, segment) self.driver.tunnel_ranges = UPDATED_TUNNEL_RANGES self.driver.sync_allocations() self.assertTrue( self.driver.get_allocation(self.context, tunnel_id).allocated) def test_sync_allocations_and_allocated_in_initial_range(self): self._test_sync_allocations_and_allocated(TUN_MIN + 2) def test_sync_allocations_and_allocated_in_final_range(self): self._test_sync_allocations_and_allocated(TUN_MAX + 2) def test_sync_allocations_no_op(self): def verify_no_chunk(iterable, chunk_size): # no segment removed/added self.assertEqual(0, len(list(iterable))) return [] with mock.patch.object( type_tunnel, 'chunks', side_effect=verify_no_chunk) as chunks: self.driver.sync_allocations() self.assertEqual(2, len(chunks.mock_calls)) def test_partial_segment_is_partial_segment(self): segment = {api.NETWORK_TYPE: self.TYPE, api.PHYSICAL_NETWORK: None, api.SEGMENTATION_ID: None} self.assertTrue(self.driver.is_partial_segment(segment)) def test_specific_segment_is_not_partial_segment(self): segment = {api.NETWORK_TYPE: self.TYPE, api.PHYSICAL_NETWORK: None, api.SEGMENTATION_ID: 101} self.assertFalse(self.driver.is_partial_segment(segment)) def test_reserve_provider_segment_full_specs(self): segment = {api.NETWORK_TYPE: self.TYPE, api.PHYSICAL_NETWORK: None, api.SEGMENTATION_ID: 101} observed = self.driver.reserve_provider_segment(self.context, segment) alloc = self.driver.get_allocation(self.context, observed[api.SEGMENTATION_ID]) self.assertTrue(alloc.allocated) with testtools.ExpectedException(exc.TunnelIdInUse): self.driver.reserve_provider_segment(self.context, segment) self.driver.release_segment(self.context, segment) alloc = self.driver.get_allocation(self.context, observed[api.SEGMENTATION_ID]) self.assertFalse(alloc.allocated) segment[api.SEGMENTATION_ID] = 1000 observed = self.driver.reserve_provider_segment(self.context, segment) alloc = self.driver.get_allocation(self.context, observed[api.SEGMENTATION_ID]) self.assertTrue(alloc.allocated) self.driver.release_segment(self.context, segment) alloc = self.driver.get_allocation(self.context, observed[api.SEGMENTATION_ID]) self.assertIsNone(alloc) def test_reserve_provider_segment(self): tunnel_ids = set() specs = {api.NETWORK_TYPE: self.TYPE, api.PHYSICAL_NETWORK: 'None', api.SEGMENTATION_ID: None} for x in moves.range(TUN_MIN, TUN_MAX + 1): segment = self.driver.reserve_provider_segment(self.context, specs) self.assertEqual(self.TYPE, segment[api.NETWORK_TYPE]) self.assertThat(segment[api.SEGMENTATION_ID], matchers.GreaterThan(TUN_MIN - 1)) self.assertThat(segment[api.SEGMENTATION_ID], matchers.LessThan(TUN_MAX + 1)) tunnel_ids.add(segment[api.SEGMENTATION_ID]) with testtools.ExpectedException(exc.NoNetworkAvailable): segment = self.driver.reserve_provider_segment(self.context, specs) segment = {api.NETWORK_TYPE: self.TYPE, api.PHYSICAL_NETWORK: 'None', api.SEGMENTATION_ID: tunnel_ids.pop()} self.driver.release_segment(self.context, segment) segment = self.driver.reserve_provider_segment(self.context, specs) self.assertThat(segment[api.SEGMENTATION_ID], matchers.GreaterThan(TUN_MIN - 1)) self.assertThat(segment[api.SEGMENTATION_ID], matchers.LessThan(TUN_MAX + 1)) tunnel_ids.add(segment[api.SEGMENTATION_ID]) for tunnel_id in tunnel_ids: segment[api.SEGMENTATION_ID] = tunnel_id self.driver.release_segment(self.context, segment) def test_allocate_tenant_segment(self): tunnel_ids = set() for x in moves.range(TUN_MIN, TUN_MAX + 1): segment = self.driver.allocate_tenant_segment(self.context) self.assertThat(segment[api.SEGMENTATION_ID], matchers.GreaterThan(TUN_MIN - 1)) self.assertThat(segment[api.SEGMENTATION_ID], matchers.LessThan(TUN_MAX + 1)) tunnel_ids.add(segment[api.SEGMENTATION_ID]) segment = self.driver.allocate_tenant_segment(self.context) self.assertIsNone(segment) segment = {api.NETWORK_TYPE: self.TYPE, api.PHYSICAL_NETWORK: 'None', api.SEGMENTATION_ID: tunnel_ids.pop()} self.driver.release_segment(self.context, segment) segment = self.driver.allocate_tenant_segment(self.context) self.assertThat(segment[api.SEGMENTATION_ID], matchers.GreaterThan(TUN_MIN - 1)) self.assertThat(segment[api.SEGMENTATION_ID], matchers.LessThan(TUN_MAX + 1)) tunnel_ids.add(segment[api.SEGMENTATION_ID]) for tunnel_id in tunnel_ids: segment[api.SEGMENTATION_ID] = tunnel_id self.driver.release_segment(self.context, segment) def add_endpoint(self, ip=TUNNEL_IP_ONE, host=HOST_ONE): return self.driver.add_endpoint(ip, host) def test_add_endpoint(self): endpoint = self.add_endpoint() self.assertEqual(TUNNEL_IP_ONE, endpoint.ip_address) self.assertEqual(HOST_ONE, endpoint.host) return endpoint def test_add_endpoint_for_existing_tunnel_ip(self): self.add_endpoint() with mock.patch.object(type_tunnel.LOG, 'warning') as log_warn: self.add_endpoint() log_warn.assert_called_once_with(mock.ANY, TUNNEL_IP_ONE) def test_get_endpoint_by_host(self): self.add_endpoint() host_endpoint = self.driver.get_endpoint_by_host(HOST_ONE) self.assertEqual(TUNNEL_IP_ONE, host_endpoint.ip_address) return host_endpoint def test_get_endpoint_by_host_for_not_existing_host(self): ip_endpoint = self.driver.get_endpoint_by_host(HOST_TWO) self.assertIsNone(ip_endpoint) def test_get_endpoint_by_ip(self): self.add_endpoint() ip_endpoint = self.driver.get_endpoint_by_ip(TUNNEL_IP_ONE) self.assertEqual(HOST_ONE, ip_endpoint.host) return ip_endpoint def test_get_endpoint_by_ip_for_not_existing_tunnel_ip(self): ip_endpoint = self.driver.get_endpoint_by_ip(TUNNEL_IP_TWO) self.assertIsNone(ip_endpoint) def test_delete_endpoint(self): self.add_endpoint() self.assertIsNone(self.driver.delete_endpoint(TUNNEL_IP_ONE)) # Get all the endpoints and verify its empty endpoints = self.driver.get_endpoints() self.assertNotIn(TUNNEL_IP_ONE, endpoints) class TunnelTypeMultiRangeTestMixin(object): DRIVER_CLASS = None TUN_MIN0 = 100 TUN_MAX0 = 101 TUN_MIN1 = 200 TUN_MAX1 = 201 TUNNEL_MULTI_RANGES = [(TUN_MIN0, TUN_MAX0), (TUN_MIN1, TUN_MAX1)] def setUp(self): super(TunnelTypeMultiRangeTestMixin, self).setUp() self.driver = self.DRIVER_CLASS() self.driver.tunnel_ranges = self.TUNNEL_MULTI_RANGES self.driver.sync_allocations() self.context = context.Context() def test_release_segment(self): segments = [self.driver.allocate_tenant_segment(self.context) for i in range(4)] # Release them in random order. No special meaning. for i in (0, 2, 1, 3): self.driver.release_segment(self.context, segments[i]) for key in (self.TUN_MIN0, self.TUN_MAX0, self.TUN_MIN1, self.TUN_MAX1): alloc = self.driver.get_allocation(self.context, key) self.assertFalse(alloc.allocated) class TunnelRpcCallbackTestMixin(object): DRIVER_CLASS = None TYPE = None def setUp(self): super(TunnelRpcCallbackTestMixin, self).setUp() self.driver = self.DRIVER_CLASS() def _test_tunnel_sync(self, kwargs, delete_tunnel=False): with mock.patch.object(self.notifier, 'tunnel_update') as tunnel_update,\ mock.patch.object(self.notifier, 'tunnel_delete') as tunnel_delete: details = self.callbacks.tunnel_sync('fake_context', **kwargs) tunnels = details['tunnels'] for tunnel in tunnels: self.assertEqual(kwargs['tunnel_ip'], tunnel['ip_address']) self.assertEqual(kwargs['host'], tunnel['host']) self.assertTrue(tunnel_update.called) if delete_tunnel: self.assertTrue(tunnel_delete.called) else: self.assertFalse(tunnel_delete.called) def _test_tunnel_sync_raises(self, kwargs): with mock.patch.object(self.notifier, 'tunnel_update') as tunnel_update,\ mock.patch.object(self.notifier, 'tunnel_delete') as tunnel_delete: self.assertRaises(exc.InvalidInput, self.callbacks.tunnel_sync, 'fake_context', **kwargs) self.assertFalse(tunnel_update.called) self.assertFalse(tunnel_delete.called) def test_tunnel_sync_called_without_host_passed(self): kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE, 'host': None} self._test_tunnel_sync(kwargs) def test_tunnel_sync_called_with_host_passed_for_existing_tunnel_ip(self): self.driver.add_endpoint(TUNNEL_IP_ONE, None) kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE, 'host': HOST_ONE} self._test_tunnel_sync(kwargs) def test_tunnel_sync_called_with_host_passed(self): kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE, 'host': HOST_ONE} self._test_tunnel_sync(kwargs) def test_tunnel_sync_called_with_host_passed_ipv6(self): cfg.CONF.set_override('overlay_ip_version', 6, group='ml2') kwargs = {'tunnel_ip': TUNNEL_IPV6_ONE, 'tunnel_type': self.TYPE, 'host': HOST_ONE} self._test_tunnel_sync(kwargs) def test_tunnel_sync_called_for_existing_endpoint(self): self.driver.add_endpoint(TUNNEL_IP_ONE, HOST_ONE) kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE, 'host': HOST_ONE} self._test_tunnel_sync(kwargs) def test_tunnel_sync_called_for_existing_host_with_tunnel_ip_changed(self): self.driver.add_endpoint(TUNNEL_IP_ONE, HOST_ONE) kwargs = {'tunnel_ip': TUNNEL_IP_TWO, 'tunnel_type': self.TYPE, 'host': HOST_ONE} self._test_tunnel_sync(kwargs, True) def test_tunnel_sync_called_with_used_tunnel_ip_host_roaming(self): self.driver.add_endpoint(TUNNEL_IP_ONE, HOST_ONE) kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE, 'host': HOST_TWO} self._test_tunnel_sync(kwargs, False) def test_tunnel_sync_called_with_used_tunnel_ip_roaming_case_two(self): self.driver.add_endpoint(TUNNEL_IP_ONE, None) self.driver.add_endpoint(TUNNEL_IP_TWO, HOST_TWO) kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE, 'host': HOST_TWO} self._test_tunnel_sync(kwargs, False) def test_tunnel_sync_called_without_tunnel_ip(self): kwargs = {'tunnel_type': self.TYPE, 'host': None} self._test_tunnel_sync_raises(kwargs) def test_tunnel_sync_called_without_tunnel_type(self): kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'host': None} self._test_tunnel_sync_raises(kwargs) def test_tunnel_sync_called_with_tunnel_overlay_mismatch(self): cfg.CONF.set_override('overlay_ip_version', 6, group='ml2') kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE, 'host': HOST_ONE} self._test_tunnel_sync_raises(kwargs) def test_tunnel_sync_called_with_tunnel_overlay_mismatch_ipv6(self): cfg.CONF.set_override('overlay_ip_version', 4, group='ml2') kwargs = {'tunnel_ip': TUNNEL_IPV6_ONE, 'tunnel_type': self.TYPE, 'host': HOST_ONE} self._test_tunnel_sync_raises(kwargs) class TunnelTypeMTUTestMixin(object): DRIVER_CLASS = None TYPE = None ENCAP_OVERHEAD = 0 def setUp(self): super(TunnelTypeMTUTestMixin, self).setUp() self.driver = self.DRIVER_CLASS() def _test_get_mtu(self, ip_version): cfg.CONF.set_override('overlay_ip_version', ip_version, group='ml2') ip_header_length = p_const.IP_HEADER_LENGTH[ip_version] cfg.CONF.set_override('global_physnet_mtu', 1500) cfg.CONF.set_override('path_mtu', 1475, group='ml2') self.driver.physnet_mtus = {'physnet1': 1450, 'physnet2': 1400} self.assertEqual(1475 - self.ENCAP_OVERHEAD - ip_header_length, self.driver.get_mtu('physnet1')) cfg.CONF.set_override('global_physnet_mtu', 1450) cfg.CONF.set_override('path_mtu', 1475, group='ml2') self.driver.physnet_mtus = {'physnet1': 1400, 'physnet2': 1425} self.assertEqual(1450 - self.ENCAP_OVERHEAD - ip_header_length, self.driver.get_mtu('physnet1')) cfg.CONF.set_override('global_physnet_mtu', 0) cfg.CONF.set_override('path_mtu', 1450, group='ml2') self.driver.physnet_mtus = {'physnet1': 1425, 'physnet2': 1400} self.assertEqual(1450 - self.ENCAP_OVERHEAD - ip_header_length, self.driver.get_mtu('physnet1')) cfg.CONF.set_override('global_physnet_mtu', 0) cfg.CONF.set_override('path_mtu', 0, group='ml2') self.driver.physnet_mtus = {} self.assertEqual(0, self.driver.get_mtu('physnet1')) def test_get_mtu_ipv4(self): self._test_get_mtu(4) def test_get_mtu_ipv6(self): self._test_get_mtu(6) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/test_type_local.py0000664000175000017500000000521113553660046026527 0ustar zuulzuul00000000000000# Copyright (c) 2014 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as p_const from neutron_lib import exceptions as exc from neutron_lib.plugins.ml2 import api from neutron.plugins.ml2.drivers import type_local from neutron.tests import base class LocalTypeTest(base.BaseTestCase): def setUp(self): super(LocalTypeTest, self).setUp() self.driver = type_local.LocalTypeDriver() self.context = None def test_is_partial_segment(self): segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL} self.assertFalse(self.driver.is_partial_segment(segment)) def test_validate_provider_segment(self): segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL} self.driver.validate_provider_segment(segment) def test_validate_provider_segment_with_unallowed_physical_network(self): segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL, api.PHYSICAL_NETWORK: 'phys_net'} self.assertRaises(exc.InvalidInput, self.driver.validate_provider_segment, segment) def test_validate_provider_segment_with_unallowed_segmentation_id(self): segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL, api.SEGMENTATION_ID: 2} self.assertRaises(exc.InvalidInput, self.driver.validate_provider_segment, segment) def test_reserve_provider_segment(self): segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL} observed = self.driver.reserve_provider_segment(self.context, segment) self.assertEqual(segment, observed) def test_release_provider_segment(self): segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL} observed = self.driver.reserve_provider_segment(self.context, segment) self.driver.release_segment(self.context, observed) def test_allocate_tenant_segment(self): expected = {api.NETWORK_TYPE: p_const.TYPE_LOCAL} observed = self.driver.allocate_tenant_segment(self.context) self.assertEqual(expected, observed) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/l2pop/0000775000175000017500000000000013553660157024023 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/0000775000175000017500000000000013553660157026301 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/l2population_rpc_base.py0000664000175000017500000001340013553660046033134 0ustar zuulzuul00000000000000# Copyright (C) 2014 VA Linux Systems Japan K.K. # Copyright (C) 2014 Fumihiko Kakuma # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import mock from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc from neutron.plugins.ml2.drivers.l2pop.rpc_manager import l2population_rpc from neutron.tests import base from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent import \ test_vlanmanager class FakeNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin): def fdb_add(self, context, fdb_entries): pass def fdb_remove(self, context, fdb_entries): pass def add_fdb_flow(self, br, port_info, remote_ip, lvm, ofport): pass def del_fdb_flow(self, br, port_info, remote_ip, lvm, ofport): pass def setup_tunnel_port(self, br, remote_ip, network_type): pass def cleanup_tunnel_port(self, br, tun_ofport, tunnel_type): pass def setup_entry_for_arp_reply(self, br, action, local_vid, mac_address, ip_address): pass class TestL2populationRpcCallBackTunnelMixinBase(base.BaseTestCase): def setUp(self): super(TestL2populationRpcCallBackTunnelMixinBase, self).setUp() self.vlan_manager = self.useFixture( test_vlanmanager.LocalVlanManagerFixture()).manager self.fakeagent = FakeNeutronAgent() self.fakebr = mock.Mock() Port = collections.namedtuple('Port', 'ip, ofport') LVM = collections.namedtuple( 'LVM', 'net, vlan, phys, segid, mac, ip, vif, port') self.local_ip = '127.0.0.1' self.type_gre = 'gre' self.ports = [Port(ip='10.1.0.1', ofport='ofport1'), Port(ip='10.1.0.2', ofport='ofport2'), Port(ip='10.1.0.3', ofport='ofport3')] self.ofports = { self.type_gre: { self.ports[0].ip: self.ports[0].ofport, self.ports[1].ip: self.ports[1].ofport, self.ports[2].ip: self.ports[2].ofport, } } self.lvms = [LVM(net='net1', vlan=1, phys='phys1', segid='tun1', mac='mac1', ip='1.1.1.1', vif='vifid1', port='port1'), LVM(net='net2', vlan=2, phys='phys2', segid='tun2', mac='mac2', ip='2.2.2.2', vif='vifid2', port='port2'), LVM(net='net3', vlan=3, phys='phys3', segid='tun3', mac='mac3', ip='3.3.3.3', vif='vifid3', port='port3')] self.agent_ports = { self.ports[0].ip: [(self.lvms[0].mac, self.lvms[0].ip)], self.ports[1].ip: [(self.lvms[1].mac, self.lvms[1].ip)], self.ports[2].ip: [(self.lvms[2].mac, self.lvms[2].ip)], } self.fdb_entries1 = { self.lvms[0].net: { 'network_type': self.type_gre, 'segment_id': self.lvms[0].segid, 'ports': { self.local_ip: [], self.ports[0].ip: [(self.lvms[0].mac, self.lvms[0].ip)]}, }, self.lvms[1].net: { 'network_type': self.type_gre, 'segment_id': self.lvms[1].segid, 'ports': { self.local_ip: [], self.ports[1].ip: [(self.lvms[1].mac, self.lvms[1].ip)]}, }, self.lvms[2].net: { 'network_type': self.type_gre, 'segment_id': self.lvms[2].segid, 'ports': { self.local_ip: [], self.ports[2].ip: [(self.lvms[2].mac, self.lvms[2].ip)]}, }, } for i in range(3): self.vlan_manager.add( self.lvms[i].net, self.lvms[i].vlan, self.type_gre, self.lvms[i].phys, self.lvms[i].segid, {self.lvms[i].vif: self.lvms[i].port}) setattr(self, 'lvm%d' % i, self.vlan_manager.get(self.lvms[i].net)) self.upd_fdb_entry1_val = { self.lvms[0].net: { self.ports[0].ip: { 'before': [l2pop_rpc.PortInfo(self.lvms[0].mac, self.lvms[0].ip)], 'after': [l2pop_rpc.PortInfo(self.lvms[1].mac, self.lvms[1].ip)], }, self.ports[1].ip: { 'before': [l2pop_rpc.PortInfo(self.lvms[0].mac, self.lvms[0].ip)], 'after': [l2pop_rpc.PortInfo(self.lvms[1].mac, self.lvms[1].ip)], }, }, self.lvms[1].net: { self.ports[2].ip: { 'before': [l2pop_rpc.PortInfo(self.lvms[0].mac, self.lvms[0].ip)], 'after': [l2pop_rpc.PortInfo(self.lvms[2].mac, self.lvms[2].ip)], }, }, } self.upd_fdb_entry1 = {'chg_ip': self.upd_fdb_entry1_val} def _tunnel_port_lookup(self, network_type, remote_ip): return self.ofports[network_type].get(remote_ip) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/__init__.py0000664000175000017500000000000013553660046030375 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/test_l2population_rpc.py0000664000175000017500000003005013553660047033202 0ustar zuulzuul00000000000000# Copyright (C) 2014 VA Linux Systems Japan K.K. # Copyright (C) 2014 Fumihiko Kakuma # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as n_const from neutron.tests.unit.plugins.ml2.drivers.l2pop.rpc_manager \ import l2population_rpc_base class TestL2populationRpcCallBackTunnelMixin( l2population_rpc_base.TestL2populationRpcCallBackTunnelMixinBase): def test_get_agent_ports_no_data(self): # Make sure vlan manager has no mappings that were added in setUp() self.vlan_manager.mapping = {} self.assertFalse( list(self.fakeagent.get_agent_ports(self.fdb_entries1))) def test_get_agent_ports_non_existence_key_in_lvm(self): results = {} self.vlan_manager.pop(self.lvms[1].net) for lvm, agent_ports in self.fakeagent.get_agent_ports( self.fdb_entries1): results[lvm] = agent_ports expected = { self.lvm0: { self.ports[0].ip: [(self.lvms[0].mac, self.lvms[0].ip)], self.local_ip: []}, self.lvm2: { self.ports[2].ip: [(self.lvms[2].mac, self.lvms[2].ip)], self.local_ip: []}, } self.assertEqual(expected, results) def test_get_agent_ports_no_agent_ports(self): results = {} self.fdb_entries1[self.lvms[1].net]['ports'] = {} for lvm, agent_ports in self.fakeagent.get_agent_ports( self.fdb_entries1): results[lvm] = agent_ports expected = { self.lvm0: { self.ports[0].ip: [(self.lvms[0].mac, self.lvms[0].ip)], self.local_ip: []}, self.lvm1: {}, self.lvm2: { self.ports[2].ip: [(self.lvms[2].mac, self.lvms[2].ip)], self.local_ip: []}, } self.assertEqual(expected, results) def test_fdb_add_tun(self): with mock.patch.object(self.fakeagent, 'setup_tunnel_port'),\ mock.patch.object(self.fakeagent, 'add_fdb_flow' ) as mock_add_fdb_flow: self.fakeagent.fdb_add_tun('context', self.fakebr, self.lvm0, self.agent_ports, self._tunnel_port_lookup) expected = [ mock.call(self.fakebr, (self.lvms[0].mac, self.lvms[0].ip), self.ports[0].ip, self.lvm0, self.ports[0].ofport), mock.call(self.fakebr, (self.lvms[1].mac, self.lvms[1].ip), self.ports[1].ip, self.lvm0, self.ports[1].ofport), mock.call(self.fakebr, (self.lvms[2].mac, self.lvms[2].ip), self.ports[2].ip, self.lvm0, self.ports[2].ofport), ] self.assertEqual(sorted(expected), sorted(mock_add_fdb_flow.call_args_list)) def test_fdb_add_tun_non_existence_key_in_ofports(self): ofport = self.lvm0.network_type + '0a0a0a0a' del self.ofports[self.type_gre][self.ports[1].ip] with mock.patch.object(self.fakeagent, 'setup_tunnel_port', return_value=ofport ) as mock_setup_tunnel_port,\ mock.patch.object(self.fakeagent, 'add_fdb_flow' ) as mock_add_fdb_flow: self.fakeagent.fdb_add_tun('context', self.fakebr, self.lvm0, self.agent_ports, self._tunnel_port_lookup) mock_setup_tunnel_port.assert_called_once_with( self.fakebr, self.ports[1].ip, self.lvm0.network_type) expected = [ mock.call(self.fakebr, (self.lvms[0].mac, self.lvms[0].ip), self.ports[0].ip, self.lvm0, self.ports[0].ofport), mock.call(self.fakebr, (self.lvms[1].mac, self.lvms[1].ip), self.ports[1].ip, self.lvm0, ofport), mock.call(self.fakebr, (self.lvms[2].mac, self.lvms[2].ip), self.ports[2].ip, self.lvm0, self.ports[2].ofport), ] self.assertEqual(sorted(expected), sorted(mock_add_fdb_flow.call_args_list)) def test_fdb_add_tun_unavailable_ofport(self): del self.ofports[self.type_gre][self.ports[1].ip] with mock.patch.object(self.fakeagent, 'setup_tunnel_port', return_value=0 ) as mock_setup_tunnel_port,\ mock.patch.object(self.fakeagent, 'add_fdb_flow' ) as mock_add_fdb_flow: self.fakeagent.fdb_add_tun('context', self.fakebr, self.lvm0, self.agent_ports, self._tunnel_port_lookup) mock_setup_tunnel_port.assert_called_once_with( self.fakebr, self.ports[1].ip, self.lvm0.network_type) expected = [ mock.call(self.fakebr, (self.lvms[0].mac, self.lvms[0].ip), self.ports[0].ip, self.lvm0, self.ports[0].ofport), mock.call(self.fakebr, (self.lvms[2].mac, self.lvms[2].ip), self.ports[2].ip, self.lvm0, self.ports[2].ofport), ] self.assertEqual(sorted(expected), sorted(mock_add_fdb_flow.call_args_list)) def test_fdb_remove_tun(self): with mock.patch.object( self.fakeagent, 'del_fdb_flow') as mock_del_fdb_flow: self.fakeagent.fdb_remove_tun('context', self.fakebr, self.lvm0, self.agent_ports, self._tunnel_port_lookup) expected = [ mock.call(self.fakebr, (self.lvms[0].mac, self.lvms[0].ip), self.ports[0].ip, self.lvm0, self.ports[0].ofport), mock.call(self.fakebr, (self.lvms[1].mac, self.lvms[1].ip), self.ports[1].ip, self.lvm0, self.ports[1].ofport), mock.call(self.fakebr, (self.lvms[2].mac, self.lvms[2].ip), self.ports[2].ip, self.lvm0, self.ports[2].ofport), ] self.assertEqual(sorted(expected), sorted(mock_del_fdb_flow.call_args_list)) def test_fdb_remove_tun_flooding_entry(self): self.agent_ports[self.ports[1].ip] = [n_const.FLOODING_ENTRY] with mock.patch.object(self.fakeagent, 'del_fdb_flow' ) as mock_del_fdb_flow,\ mock.patch.object(self.fakeagent, 'cleanup_tunnel_port' ) as mock_cleanup_tunnel_port: self.fakeagent.fdb_remove_tun('context', self.fakebr, self.lvm0, self.agent_ports, self._tunnel_port_lookup) expected = [ mock.call(self.fakebr, (self.lvms[0].mac, self.lvms[0].ip), self.ports[0].ip, self.lvm0, self.ports[0].ofport), mock.call(self.fakebr, (n_const.FLOODING_ENTRY[0], n_const.FLOODING_ENTRY[1]), self.ports[1].ip, self.lvm0, self.ports[1].ofport), mock.call(self.fakebr, (self.lvms[2].mac, self.lvms[2].ip), self.ports[2].ip, self.lvm0, self.ports[2].ofport), ] self.assertEqual(sorted(expected), sorted(mock_del_fdb_flow.call_args_list)) mock_cleanup_tunnel_port.assert_called_once_with( self.fakebr, self.ports[1].ofport, self.lvm0.network_type) def test_fdb_remove_tun_non_existence_key_in_ofports(self): del self.ofports[self.type_gre][self.ports[1].ip] with mock.patch.object( self.fakeagent, 'del_fdb_flow') as mock_del_fdb_flow: self.fakeagent.fdb_remove_tun('context', self.fakebr, self.lvm0, self.agent_ports, self._tunnel_port_lookup) expected = [ mock.call(self.fakebr, (self.lvms[0].mac, self.lvms[0].ip), self.ports[0].ip, self.lvm0, self.ports[0].ofport), mock.call(self.fakebr, (self.lvms[2].mac, self.lvms[2].ip), self.ports[2].ip, self.lvm0, self.ports[2].ofport), ] self.assertEqual(sorted(expected), sorted(mock_del_fdb_flow.call_args_list)) def test_fdb_update(self): fake__fdb_chg_ip = mock.Mock() self.fakeagent._fdb_chg_ip = fake__fdb_chg_ip self.fakeagent.fdb_update('context', self.upd_fdb_entry1) fake__fdb_chg_ip.assert_called_once_with( 'context', self.upd_fdb_entry1_val) def test_fdb_update_non_existence_method(self): self.assertRaises(NotImplementedError, self.fakeagent.fdb_update, 'context', self.upd_fdb_entry1) def test__fdb_chg_ip(self): with mock.patch.object( self.fakeagent, 'setup_entry_for_arp_reply') as m_setup_entry_for_arp_reply: self.fakeagent.fdb_chg_ip_tun('context', self.fakebr, self.upd_fdb_entry1_val, self.local_ip) expected = [ mock.call(self.fakebr, 'remove', self.lvm0.vlan, self.lvms[0].mac, self.lvms[0].ip), mock.call(self.fakebr, 'add', self.lvm0.vlan, self.lvms[1].mac, self.lvms[1].ip), mock.call(self.fakebr, 'remove', self.lvm0.vlan, self.lvms[0].mac, self.lvms[0].ip), mock.call(self.fakebr, 'add', self.lvm0.vlan, self.lvms[1].mac, self.lvms[1].ip), mock.call(self.fakebr, 'remove', self.lvm1.vlan, self.lvms[0].mac, self.lvms[0].ip), mock.call(self.fakebr, 'add', self.lvm1.vlan, self.lvms[2].mac, self.lvms[2].ip), ] m_setup_entry_for_arp_reply.assert_has_calls(expected, any_order=True) def test__fdb_chg_ip_no_lvm(self): m_setup_entry_for_arp_reply = mock.Mock() self.fakeagent.setup_entry_for_arp_reply = m_setup_entry_for_arp_reply self.fakeagent.fdb_chg_ip_tun( 'context', self.fakebr, self.upd_fdb_entry1, self.local_ip) self.assertFalse(m_setup_entry_for_arp_reply.call_count) def test__fdb_chg_ip_ip_is_local_ip(self): upd_fdb_entry_val = { self.lvms[0].net: { self.local_ip: { 'before': [(self.lvms[0].mac, self.lvms[0].ip)], 'after': [(self.lvms[1].mac, self.lvms[1].ip)], }, }, } m_setup_entry_for_arp_reply = mock.Mock() self.fakeagent.setup_entry_for_arp_reply = m_setup_entry_for_arp_reply self.fakeagent.fdb_chg_ip_tun('context', self.fakebr, upd_fdb_entry_val, self.local_ip) self.assertFalse(m_setup_entry_for_arp_reply.call_count) def test_fdb_chg_ip_tun_empty_before_after(self): upd_fdb_entry_val = { self.lvms[0].net: { self.local_ip: {}, }, } m_setup_entry_for_arp_reply = mock.Mock() self.fakeagent.setup_entry_for_arp_reply = m_setup_entry_for_arp_reply # passing non-local ip self.fakeagent.fdb_chg_ip_tun('context', self.fakebr, upd_fdb_entry_val, "8.8.8.8") self.assertFalse(m_setup_entry_for_arp_reply.call_count) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/l2pop/test_mech_driver.py0000664000175000017500000021177213553660047027733 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import mock from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import provider_net as pnet from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_serialization import jsonutils import testtools from neutron.common import constants as n_const from neutron.common import topics from neutron.db import agents_db from neutron.db import common_db_mixin from neutron.db import l3_agentschedulers_db from neutron.db import l3_hamode_db from neutron.plugins.ml2 import driver_context from neutron.plugins.ml2.drivers.l2pop import db as l2pop_db from neutron.plugins.ml2.drivers.l2pop import mech_driver as l2pop_mech_driver from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc from neutron.plugins.ml2.drivers.l2pop.rpc_manager import l2population_rpc from neutron.plugins.ml2 import managers from neutron.plugins.ml2 import models from neutron.plugins.ml2 import rpc from neutron.scheduler import l3_agent_scheduler from neutron.tests import base from neutron.tests.common import helpers from neutron.tests.unit.plugins.ml2 import test_plugin HOST = 'my_l2_host' HOST_2 = HOST + '_2' HOST_3 = HOST + '_3' HOST_4 = HOST + '_4' HOST_5 = HOST + '_5' TEST_ROUTER_ID = 'router_id' NOTIFIER = 'neutron.plugins.ml2.rpc.AgentNotifierApi' DEVICE_OWNER_COMPUTE = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' DEVICE_OWNER_ROUTER_HA_INTF = constants.DEVICE_OWNER_ROUTER_HA_INTF + 'fake' class FakeL3PluginWithAgents(common_db_mixin.CommonDbMixin, l3_hamode_db.L3_HA_NAT_db_mixin, l3_agentschedulers_db.L3AgentSchedulerDbMixin, agents_db.AgentDbMixin): pass class TestL2PopulationRpcTestCase(test_plugin.Ml2PluginV2TestCase): _mechanism_drivers = ['openvswitch', 'fake_agent', 'l2population'] def setUp(self): super(TestL2PopulationRpcTestCase, self).setUp() self.adminContext = context.get_admin_context() self.type_manager = managers.TypeManager() self.notifier = rpc.AgentNotifierApi(topics.AGENT) self.callbacks = rpc.RpcCallbacks(self.notifier, self.type_manager) net_arg = {pnet.NETWORK_TYPE: 'vxlan', pnet.SEGMENTATION_ID: '1'} self._network = self._make_network(self.fmt, 'net1', True, arg_list=(pnet.NETWORK_TYPE, pnet.SEGMENTATION_ID,), **net_arg) net_arg = {pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: '2'} self._network2 = self._make_network(self.fmt, 'net2', True, arg_list=(pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK, pnet.SEGMENTATION_ID,), **net_arg) net_arg = {pnet.NETWORK_TYPE: 'flat', pnet.PHYSICAL_NETWORK: 'noagent'} self._network3 = self._make_network(self.fmt, 'net3', True, arg_list=(pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK,), **net_arg) notifier_patch = mock.patch(NOTIFIER) notifier_patch.start() self.fanout_topic = topics.get_topic_name(topics.AGENT, topics.L2POPULATION, topics.UPDATE) fanout = ('neutron.plugins.ml2.drivers.l2pop.rpc.' 'L2populationAgentNotifyAPI._notification_fanout') fanout_patch = mock.patch(fanout) self.mock_fanout = fanout_patch.start() cast = ('neutron.plugins.ml2.drivers.l2pop.rpc.' 'L2populationAgentNotifyAPI._notification_host') cast_patch = mock.patch(cast) self.mock_cast = cast_patch.start() uptime = ('neutron.plugins.ml2.drivers.l2pop.db.get_agent_uptime') uptime_patch = mock.patch(uptime, return_value=190) uptime_patch.start() def _setup_l3(self): notif_p = mock.patch.object(l3_hamode_db.L3_HA_NAT_db_mixin, '_notify_router_updated') self.notif_m = notif_p.start() self.plugin = FakeL3PluginWithAgents() self._register_ml2_agents() self._register_l3_agents() def _register_l3_agents(self): self.agent1 = helpers.register_l3_agent(host=HOST) self.agent2 = helpers.register_l3_agent(host=HOST_2) def _register_ml2_agents(self): helpers.register_ovs_agent(host=HOST, tunneling_ip='20.0.0.1') helpers.register_ovs_agent(host=HOST_2, tunneling_ip='20.0.0.2') helpers.register_ovs_agent(host=HOST_3, tunneling_ip='20.0.0.3', tunnel_types=[]) helpers.register_ovs_agent(host=HOST_4, tunneling_ip='20.0.0.4') helpers.register_ovs_agent(host=HOST_5, tunneling_ip='20.0.0.5', binary='neutron-fake-agent', tunnel_types=[], interface_mappings={'physnet1': 'eth9'}, agent_type=constants.AGENT_TYPE_OFA, l2pop_network_types=['vlan']) def test_port_info_compare(self): # An assumption the code makes is that PortInfo compares equal to # equivalent regular tuples. self.assertEqual(("mac", "ip"), l2pop_rpc.PortInfo("mac", "ip")) flooding_entry = l2pop_rpc.PortInfo(*constants.FLOODING_ENTRY) self.assertEqual(constants.FLOODING_ENTRY, flooding_entry) def test__unmarshall_fdb_entries(self): entries = {'foouuid': { 'segment_id': 1001, 'ports': {'192.168.0.10': [['00:00:00:00:00:00', '0.0.0.0'], ['fa:16:3e:ff:8c:0f', '10.0.0.6']]}, 'network_type': 'vxlan'}} entries['chg_ip'] = { 'foouuid': { '192.168.0.1': {'before': [['fa:16:3e:ff:8c:0f', '10.0.0.6']], 'after': [['fa:16:3e:ff:8c:0f', '10.0.0.7']]}, '192.168.0.2': {'before': [['fa:16:3e:ff:8c:0e', '10.0.0.8']]} }, 'foouuid2': { '192.168.0.1': {'before': [['ff:16:3e:ff:8c:0e', '1.0.0.8']]} } } mixin = l2population_rpc.L2populationRpcCallBackMixin entries = mixin._unmarshall_fdb_entries(entries) port_info_list = entries['foouuid']['ports']['192.168.0.10'] # Check that the lists have been properly converted to PortInfo self.assertIsInstance(port_info_list[0], l2pop_rpc.PortInfo) self.assertIsInstance(port_info_list[1], l2pop_rpc.PortInfo) self.assertEqual(('00:00:00:00:00:00', '0.0.0.0'), port_info_list[0]) self.assertEqual(('fa:16:3e:ff:8c:0f', '10.0.0.6'), port_info_list[1]) agt1 = entries['chg_ip']['foouuid']['192.168.0.1'] self.assertIsInstance(agt1['before'][0], l2pop_rpc.PortInfo) self.assertIsInstance(agt1['after'][0], l2pop_rpc.PortInfo) self.assertEqual(('fa:16:3e:ff:8c:0f', '10.0.0.6'), agt1['before'][0]) self.assertEqual(('fa:16:3e:ff:8c:0f', '10.0.0.7'), agt1['after'][0]) agt1_net2 = entries['chg_ip']['foouuid2']['192.168.0.1'] self.assertEqual(('ff:16:3e:ff:8c:0e', '1.0.0.8'), agt1_net2['before'][0]) self.assertIsInstance(agt1_net2['before'][0], l2pop_rpc.PortInfo) agt2 = entries['chg_ip']['foouuid']['192.168.0.2'] self.assertIsInstance(agt2['before'][0], l2pop_rpc.PortInfo) self.assertEqual(('fa:16:3e:ff:8c:0e', '10.0.0.8'), agt2['before'][0]) def test_portinfo_marshalled_as_list(self): entry = ['fa:16:3e:ff:8c:0f', '10.0.0.6'] payload = {'netuuid': {'ports': {'1': [l2pop_rpc.PortInfo(*entry)]}}} result = jsonutils.loads(jsonutils.dumps(payload)) self.assertEqual(entry, result['netuuid']['ports']['1'][0]) def _create_router(self, ha=True, tenant_id='tenant1', distributed=None, ctx=None): if ctx is None: ctx = self.adminContext ctx.tenant_id = tenant_id router = {'name': TEST_ROUTER_ID, 'admin_state_up': True, 'tenant_id': ctx.tenant_id} if ha is not None: router['ha'] = ha if distributed is not None: router['distributed'] = distributed return self.plugin.create_router(ctx, {'router': router}) def _bind_router(self, router_id, tenant_id): scheduler = l3_agent_scheduler.ChanceScheduler() filters = {'agent_type': [constants.AGENT_TYPE_L3]} agents_object = self.plugin.get_agent_objects( self.adminContext, filters=filters) for agent_obj in agents_object: scheduler.create_ha_port_and_bind( self.plugin, self.adminContext, router_id, tenant_id, agent_obj) self._bind_ha_network_ports(router_id) def _bind_ha_network_ports(self, router_id): port_bindings = self.plugin.get_ha_router_port_bindings( self.adminContext, [router_id]) plugin = directory.get_plugin() for port_binding in port_bindings: filters = {'id': [port_binding.port_id]} port = plugin.get_ports(self.adminContext, filters=filters)[0] if port_binding.l3_agent_id == self.agent1['id']: port[portbindings.HOST_ID] = self.agent1['host'] else: port[portbindings.HOST_ID] = self.agent2['host'] plugin.update_port(self.adminContext, port['id'], {port_def.RESOURCE_NAME: port}) def _get_first_interface(self, net_id, router): plugin = directory.get_plugin() if router['distributed']: device_filter = {'device_id': [router['id']], 'device_owner': [constants.DEVICE_OWNER_DVR_INTERFACE]} else: device_filter = {'device_id': [router['id']], 'device_owner': [constants.DEVICE_OWNER_HA_REPLICATED_INT]} ports = plugin.get_ports(self.adminContext, filters=device_filter) if ports: return ports[0] def _add_router_interface(self, subnet, router, host): interface_info = {'subnet_id': subnet['id']} self.plugin.add_router_interface(self.adminContext, router['id'], interface_info) self.plugin.update_routers_states( self.adminContext, {router['id']: n_const.HA_ROUTER_STATE_ACTIVE}, host) port = self._get_first_interface(subnet['network_id'], router) self.mock_cast.reset_mock() self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=host, device=port['id'], host=host) return port def _create_ha_router(self): self._setup_l3() router = self._create_router() self._bind_router(router['id'], router['tenant_id']) return router def _create_dvr_router(self): self._setup_l3() router = self._create_router(distributed=True) self._bind_router(router['id'], router['tenant_id']) return router def _verify_remove_fdb(self, expected, agent_id, device, host=None): self.mock_fanout.reset_mock() self.callbacks.update_device_down(self.adminContext, agent_id=host, device=device, host=host) self.mock_fanout.assert_called_with( mock.ANY, 'remove_fdb_entries', expected) def test_other_agents_get_flood_entries_for_ha_agents(self): # First HA router port is added on HOST and HOST2, then network port # is added on HOST4. # HOST4 should get flood entries for HOST1 and HOST2 router = self._create_ha_router() directory.add_plugin(plugin_constants.L3, self.plugin) with self.subnet(network=self._network, enable_dhcp=False) as snet: subnet = snet['subnet'] port = self._add_router_interface(subnet, router, HOST) host_arg = {portbindings.HOST_ID: HOST_4, 'admin_state_up': True} with self.port(subnet=snet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: p1 = port1['port'] device1 = 'tap' + p1['id'] self.mock_cast.reset_mock() self.mock_fanout.reset_mock() self.callbacks.update_device_up( self.adminContext, agent_id=HOST_4, device=device1) cast_expected = { port['network_id']: { 'ports': {'20.0.0.1': [constants.FLOODING_ENTRY], '20.0.0.2': [constants.FLOODING_ENTRY]}, 'network_type': 'vxlan', 'segment_id': 1}} self.assertEqual(1, self.mock_cast.call_count) self.mock_cast.assert_called_with( mock.ANY, 'add_fdb_entries', cast_expected, HOST_4) def test_delete_ha_port(self): # First network port is added on HOST, and then HA router port # is added on HOST and HOST2. # Remove_fdb should carry flood entry of only HOST2 and not HOST router = self._create_ha_router() directory.add_plugin(plugin_constants.L3, self.plugin) with self.subnet(network=self._network, enable_dhcp=False) as snet: host_arg = {portbindings.HOST_ID: HOST, 'admin_state_up': True} with self.port(subnet=snet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: p1 = port1['port'] device1 = 'tap' + p1['id'] self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device1) subnet = snet['subnet'] port = self._add_router_interface(subnet, router, HOST) expected = {port['network_id']: {'ports': {'20.0.0.2': [constants.FLOODING_ENTRY]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_fanout.reset_mock() interface_info = {'subnet_id': subnet['id']} self.plugin.remove_router_interface(self.adminContext, router['id'], interface_info) self.mock_fanout.assert_called_with( mock.ANY, 'remove_fdb_entries', expected) def _test_ovs_agent_restarted_with_dvr_port( self, agent_boot_timeout=True, agent_restarted=False): plugin = directory.get_plugin() router = self._create_dvr_router() with mock.patch.object(l2pop_mech_driver.L2populationMechanismDriver, 'agent_restarted', return_value=agent_boot_timeout): with self.subnet(network=self._network, enable_dhcp=False) as snet: with self.port( subnet=snet, device_owner=constants.DEVICE_OWNER_DVR_INTERFACE)\ as port: port_id = port['port']['id'] plugin.update_distributed_port_binding(self.adminContext, port_id, {'port': {portbindings.HOST_ID: HOST_4, 'device_id': router['id']}}) port = self._show('ports', port_id) self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED, port['port'][portbindings.VIF_TYPE]) self.callbacks.update_device_up( self.adminContext, agent_id=HOST_4, device=port_id, host=HOST_4, agent_restarted=agent_restarted) fanout_expected = {port['port']['network_id']: { 'network_type': u'vxlan', 'ports': { u'20.0.0.4': [('00:00:00:00:00:00', '0.0.0.0')]}, 'segment_id': 1}} self.mock_fanout.assert_called_with(mock.ANY, 'add_fdb_entries', fanout_expected) def test_ovs_agent_restarted_with_dvr_port_boot_config_timeout(self): self._test_ovs_agent_restarted_with_dvr_port() def test_ovs_agent_restarted_with_dvr_port_rpc_send_timeout(self): self._test_ovs_agent_restarted_with_dvr_port( agent_boot_timeout=False, agent_restarted=True) def test_ha_agents_with_dvr_rtr_does_not_get_other_fdb(self): router = self._create_dvr_router() directory.add_plugin(plugin_constants.L3, self.plugin) with self.subnet(network=self._network, enable_dhcp=False) as snet: host_arg = {portbindings.HOST_ID: HOST_4, 'admin_state_up': True} with self.port(subnet=snet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: p1 = port1['port'] device1 = 'tap' + p1['id'] self.callbacks.update_device_up( self.adminContext, agent_id=HOST_4, device=device1) subnet = snet['subnet'] port = self._add_router_interface(subnet, router, HOST) self.mock_cast.assert_not_called() self.mock_fanout.assert_not_called() self.mock_cast.reset_mock() self.mock_fanout.reset_mock() self.callbacks.update_device_up( self.adminContext, agent_id=HOST_2, device=port['id'], host=HOST_2) self.mock_cast.assert_not_called() self.mock_fanout.assert_not_called() def test_ha_agents_get_other_fdb(self): # First network port is added on HOST4, then HA router port is # added on HOST and HOST2. # Both HA agents should create tunnels to HOST4 and among themselves. # Both HA agents should be notified to other agents. router = self._create_ha_router() directory.add_plugin(plugin_constants.L3, self.plugin) with self.subnet(network=self._network, enable_dhcp=False) as snet: host_arg = {portbindings.HOST_ID: HOST_4, 'admin_state_up': True} with self.port(subnet=snet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: p1 = port1['port'] device1 = 'tap' + p1['id'] self.callbacks.update_device_up( self.adminContext, agent_id=HOST_4, device=device1) p1_ips = [p['ip_address'] for p in p1['fixed_ips']] subnet = snet['subnet'] port = self._add_router_interface(subnet, router, HOST) fanout_expected = {port['network_id']: { 'ports': {'20.0.0.1': [constants.FLOODING_ENTRY]}, 'network_type': 'vxlan', 'segment_id': 1}} cast_expected_host = {port['network_id']: { 'ports': { '20.0.0.4': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo(p1['mac_address'], p1_ips[0])], '20.0.0.2': [constants.FLOODING_ENTRY]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_cast.assert_called_with( mock.ANY, 'add_fdb_entries', cast_expected_host, HOST) self.mock_fanout.assert_called_with( mock.ANY, 'add_fdb_entries', fanout_expected) self.mock_cast.reset_mock() self.mock_fanout.reset_mock() self.callbacks.update_device_up( self.adminContext, agent_id=HOST_2, device=port['id'], host=HOST_2) cast_expected_host2 = {port['network_id']: { 'ports': { '20.0.0.4': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo(p1['mac_address'], p1_ips[0])], '20.0.0.1': [constants.FLOODING_ENTRY]}, 'network_type': 'vxlan', 'segment_id': 1}} fanout_expected = {port['network_id']: { 'ports': {'20.0.0.2': [constants.FLOODING_ENTRY]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_cast.assert_called_with( mock.ANY, 'add_fdb_entries', cast_expected_host2, HOST_2) self.mock_fanout.assert_called_with( mock.ANY, 'add_fdb_entries', fanout_expected) def test_fdb_add_called(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: with self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), **host_arg): p1 = port1['port'] device = 'tap' + p1['id'] self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device) p1_ips = [p['ip_address'] for p in p1['fixed_ips']] expected = {p1['network_id']: {'ports': {'20.0.0.1': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( p1['mac_address'], p1_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_fanout.assert_called_with( mock.ANY, 'add_fdb_entries', expected) def test_fdb_add_not_called_type_local(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST + '_3'} with self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: with self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), **host_arg): p1 = port1['port'] device = 'tap' + p1['id'] self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device) self.assertFalse(self.mock_fanout.called) def test_fdb_add_called_for_l2pop_network_types(self): self._register_ml2_agents() host = HOST + '_5' with self.subnet(network=self._network2) as subnet: host_arg = {portbindings.HOST_ID: host} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: with self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), **host_arg): p1 = port1['port'] device = 'tap' + p1['id'] self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=host, device=device) p1_ips = [p['ip_address'] for p in p1['fixed_ips']] expected = {p1['network_id']: {'ports': {'20.0.0.5': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( p1['mac_address'], p1_ips[0])]}, 'network_type': 'vlan', 'segment_id': 2}} self.mock_fanout.assert_called_with( mock.ANY, 'add_fdb_entries', expected) def test_fdb_called_for_active_ports(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: host_arg = {portbindings.HOST_ID: HOST + '_2'} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg): p1 = port1['port'] device1 = 'tap' + p1['id'] self.mock_cast.reset_mock() self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device1) p1_ips = [p['ip_address'] for p in p1['fixed_ips']] self.assertFalse(self.mock_cast.called) expected2 = {p1['network_id']: {'ports': {'20.0.0.1': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( p1['mac_address'], p1_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_fanout.assert_called_with( mock.ANY, 'add_fdb_entries', expected2) def test_fdb_add_two_agents(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST, 'admin_state_up': True} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID, 'admin_state_up',), **host_arg) as port1: host_arg = {portbindings.HOST_ID: HOST + '_2', 'admin_state_up': True} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID, 'admin_state_up',), **host_arg) as port2: p1 = port1['port'] p2 = port2['port'] device1 = 'tap' + p1['id'] device2 = 'tap' + p2['id'] self.mock_cast.reset_mock() self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=HOST + '_2', device=device2) self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device1) p1_ips = [p['ip_address'] for p in p1['fixed_ips']] p2_ips = [p['ip_address'] for p in p2['fixed_ips']] expected1 = {p1['network_id']: {'ports': {'20.0.0.2': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( p2['mac_address'], p2_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_cast.assert_called_with(mock.ANY, 'add_fdb_entries', expected1, HOST) expected2 = {p1['network_id']: {'ports': {'20.0.0.1': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( p1['mac_address'], p1_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_fanout.assert_called_with( mock.ANY, 'add_fdb_entries', expected2) def test_fdb_add_called_two_networks(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST + '_2'} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: with self.subnet(cidr='10.1.0.0/24') as subnet2: with self.port(subnet=subnet2, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg): host_arg = {portbindings.HOST_ID: HOST} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port3: p1 = port1['port'] p3 = port3['port'] device1 = 'tap' + p1['id'] device3 = 'tap' + p3['id'] self.mock_cast.reset_mock() self.mock_fanout.reset_mock() self.callbacks.update_device_up( self.adminContext, agent_id=HOST + '_2', device=device1) self.callbacks.update_device_up( self.adminContext, agent_id=HOST, device=device3) p1_ips = [p['ip_address'] for p in p1['fixed_ips']] expected1 = {p1['network_id']: {'ports': {'20.0.0.2': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( p1['mac_address'], p1_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_cast.assert_called_with( mock.ANY, 'add_fdb_entries', expected1, HOST) p3_ips = [p['ip_address'] for p in p3['fixed_ips']] expected2 = {p1['network_id']: {'ports': {'20.0.0.1': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( p3['mac_address'], p3_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_fanout.assert_called_with( mock.ANY, 'add_fdb_entries', expected2) def test_fdb_add_called_dualstack(self): self._register_ml2_agents() host_arg = {portbindings.HOST_ID: HOST, 'admin_state_up': True} with self.subnet(self._network) as subnet,\ self.subnet( self._network, cidr='2001:db8::/64', ip_version=6, gateway_ip='fe80::1', ipv6_address_mode=constants.IPV6_SLAAC) as subnet2: with self.port( subnet, fixed_ips=[{'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet2['subnet']['id']}], device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg ) as port: p1 = port['port'] device = 'tap' + p1['id'] self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device) p1_ips = [p['ip_address'] for p in p1['fixed_ips']] expected = {p1['network_id']: {'ports': {'20.0.0.1': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( p1['mac_address'], p1_ips[0]), l2pop_rpc.PortInfo( p1['mac_address'], p1_ips[1])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_fanout.assert_called_with( mock.ANY, 'add_fdb_entries', expected) def test_update_port_up_two_active_ports(self): '''The test will check that even with 2 active ports on the host, agent will be provided with the whole list of fdb entries. Bug 1789846 ''' self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST} # 2 ports on host 1 with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port2: # 1 port on another host to have fdb entree to update # agent on host 1 host_arg = {portbindings.HOST_ID: HOST + '_2'} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port3: p1 = port1['port'] p2 = port2['port'] p3 = port3['port'] # only ACTIVE ports count plugin = directory.get_plugin() p2['status'] = 'ACTIVE' plugin.update_port(self.adminContext, p2['id'], port2) p3['status'] = 'ACTIVE' plugin.update_port(self.adminContext, p3['id'], port3) self.mock_cast.reset_mock() p1['status'] = 'ACTIVE' plugin.update_port(self.adminContext, p1['id'], port1) # agent on host 1 should be updated with entry from # another host expected = {p3['network_id']: {'ports': {'20.0.0.2': [ constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( p3['mac_address'], p3['fixed_ips'][0]['ip_address'])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_cast.assert_called_once_with( mock.ANY, 'add_fdb_entries', expected, HOST) def test_update_port_down(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port2: p2 = port2['port'] device2 = 'tap' + p2['id'] self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device2) p1 = port1['port'] device1 = 'tap' + p1['id'] self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device1) self.mock_fanout.reset_mock() self.callbacks.update_device_down(self.adminContext, agent_id=HOST, device=device2) p2_ips = [p['ip_address'] for p in p2['fixed_ips']] expected = {p2['network_id']: {'ports': {'20.0.0.1': [l2pop_rpc.PortInfo( p2['mac_address'], p2_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_fanout.assert_called_with( mock.ANY, 'remove_fdb_entries', expected) def test_update_port_down_last_port_up(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg): with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port2: p2 = port2['port'] device2 = 'tap' + p2['id'] self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device2) self.callbacks.update_device_down(self.adminContext, agent_id=HOST, device=device2) p2_ips = [p['ip_address'] for p in p2['fixed_ips']] expected = {p2['network_id']: {'ports': {'20.0.0.1': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( p2['mac_address'], p2_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_fanout.assert_called_with( mock.ANY, 'remove_fdb_entries', expected) def test_update_port_down_ha_router_port(self): router = self._create_ha_router() directory.add_plugin(plugin_constants.L3, self.plugin) with self.subnet(network=self._network, enable_dhcp=False) as snet: subnet = snet['subnet'] router_port = self._add_router_interface(subnet, router, HOST) router_port_device = 'tap' + router_port['id'] host_arg = {portbindings.HOST_ID: HOST_4, 'admin_state_up': True} with self.port(subnet=snet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: p1 = port1['port'] device1 = 'tap' + p1['id'] self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device1) self.mock_fanout.reset_mock() self.callbacks.update_device_down(self.adminContext, agent_id=HOST, device=router_port_device, host=HOST) router_port_ips = [ p['ip_address'] for p in router_port['fixed_ips']] expected = { router_port['network_id']: { 'ports': { '20.0.0.1': [ l2pop_rpc.PortInfo(router_port['mac_address'], router_port_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_fanout.assert_called_with( mock.ANY, 'remove_fdb_entries', expected) def test_delete_port(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port: p1 = port['port'] device = 'tap' + p1['id'] self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device) with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port2: p2 = port2['port'] device1 = 'tap' + p2['id'] self.mock_fanout.reset_mock() self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device1) self._delete('ports', port2['port']['id']) p2_ips = [p['ip_address'] for p in p2['fixed_ips']] expected = {p2['network_id']: {'ports': {'20.0.0.1': [l2pop_rpc.PortInfo( p2['mac_address'], p2_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_fanout.assert_any_call( mock.ANY, 'remove_fdb_entries', expected) def test_delete_port_last_port_up(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg): with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port: p1 = port['port'] device = 'tap' + p1['id'] self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device) self._delete('ports', port['port']['id']) p1_ips = [p['ip_address'] for p in p1['fixed_ips']] expected = {p1['network_id']: {'ports': {'20.0.0.1': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( p1['mac_address'], p1_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_fanout.assert_any_call( mock.ANY, 'remove_fdb_entries', expected) def test_mac_addr_changed(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST + '_5'} with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: p1 = port1['port'] p1_ip = p1['fixed_ips'][0]['ip_address'] self.mock_fanout.reset_mock() device = 'tap' + p1['id'] old_mac = p1['mac_address'] mac = old_mac.split(':') mac[5] = '01' if mac[5] != '01' else '00' new_mac = ':'.join(mac) data = {'port': {'mac_address': new_mac, portbindings.HOST_ID: HOST}} req = self.new_update_request('ports', data, p1['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertIn('port', res) self.assertEqual(new_mac, res['port']['mac_address']) # port was not bound before, so no fdb call expected yet self.assertFalse(self.mock_fanout.called) self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device) self.assertEqual(1, self.mock_fanout.call_count) add_expected = { p1['network_id']: { 'segment_id': 1, 'network_type': 'vxlan', 'ports': { '20.0.0.1': [ l2pop_rpc.PortInfo('00:00:00:00:00:00', '0.0.0.0'), l2pop_rpc.PortInfo(new_mac, p1_ip) ] } } } self.mock_fanout.assert_called_with( mock.ANY, 'add_fdb_entries', add_expected) def test_fixed_ips_changed(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST} fixed_ips = [{'subnet_id': subnet['subnet']['id'], 'ip_address': '10.0.0.2'}] with self.port(subnet=subnet, cidr='10.0.0.0/24', device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), fixed_ips=fixed_ips, **host_arg) as port1: p1 = port1['port'] device = 'tap' + p1['id'] self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device) self.mock_fanout.reset_mock() data = {'port': {'fixed_ips': [{'ip_address': '10.0.0.2'}, {'ip_address': '10.0.0.10'}]}} req = self.new_update_request('ports', data, p1['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) ips = res['port']['fixed_ips'] self.assertEqual(2, len(ips)) add_expected = {'chg_ip': {p1['network_id']: {'20.0.0.1': {'after': [(p1['mac_address'], '10.0.0.10')]}}}} self.mock_fanout.assert_any_call( mock.ANY, 'update_fdb_entries', add_expected) self.mock_fanout.reset_mock() data = {'port': {'fixed_ips': [{'ip_address': '10.0.0.2'}, {'ip_address': '10.0.0.16'}]}} req = self.new_update_request('ports', data, p1['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) ips = res['port']['fixed_ips'] self.assertEqual(2, len(ips)) upd_expected = {'chg_ip': {p1['network_id']: {'20.0.0.1': {'before': [(p1['mac_address'], '10.0.0.10')], 'after': [(p1['mac_address'], '10.0.0.16')]}}}} self.mock_fanout.assert_any_call( mock.ANY, 'update_fdb_entries', upd_expected) self.mock_fanout.reset_mock() data = {'port': {'fixed_ips': [{'ip_address': '10.0.0.16'}]}} req = self.new_update_request('ports', data, p1['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) ips = res['port']['fixed_ips'] self.assertEqual(1, len(ips)) del_expected = {'chg_ip': {p1['network_id']: {'20.0.0.1': {'before': [(p1['mac_address'], '10.0.0.2')]}}}} self.mock_fanout.assert_any_call( mock.ANY, 'update_fdb_entries', del_expected) def test_no_fdb_updates_without_port_updates(self): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST} with self.port(subnet=subnet, cidr='10.0.0.0/24', device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: p1 = port1['port'] device = 'tap' + p1['id'] self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device) p1['status'] = 'ACTIVE' self.mock_fanout.reset_mock() plugin = directory.get_plugin() plugin.update_port(self.adminContext, p1['id'], port1) self.assertFalse(self.mock_fanout.called) def test_get_device_details_port_id(self): self._register_ml2_agents() host_arg = {portbindings.HOST_ID: HOST} with self.port(arg_list=(portbindings.HOST_ID,), **host_arg) as port: port_id = port['port']['id'] # ensure various formats all result in correct port_id formats = ['tap' + port_id[0:8], port_id, port['port']['mac_address']] for device in formats: details = self.callbacks.get_device_details( self.adminContext, device=device, agent_id=HOST_2) self.assertEqual(port_id, details['port_id']) def _update_and_check_portbinding(self, port_id, host_id): data = {'port': {portbindings.HOST_ID: host_id}} req = self.new_update_request('ports', data, port_id) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(host_id, res['port'][portbindings.HOST_ID]) def _test_host_changed(self, twice): self._register_ml2_agents() with self.subnet(network=self._network) as subnet: host_arg = {portbindings.HOST_ID: HOST} with self.port(subnet=subnet, cidr='10.0.0.0/24', device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID,), **host_arg) as port1: tunnel_ip = '20.0.0.1' p1 = port1['port'] device1 = 'tap' + p1['id'] self.callbacks.update_device_up( self.adminContext, agent_id=HOST, device=device1) if twice: tunnel_ip = '20.0.0.4' self._update_and_check_portbinding(p1['id'], HOST_4) self.callbacks.update_device_up(self.adminContext, agent_id=HOST_4, device=device1) self.mock_fanout.reset_mock() self._update_and_check_portbinding(p1['id'], HOST_2) p1_ips = [p['ip_address'] for p in p1['fixed_ips']] expected = {p1['network_id']: {'ports': {tunnel_ip: [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( p1['mac_address'], p1_ips[0])]}, 'network_type': 'vxlan', 'segment_id': 1}} self.mock_fanout.assert_called_with( mock.ANY, 'remove_fdb_entries', expected) def test_host_changed(self): self._test_host_changed(twice=False) def test_host_changed_twice(self): self._test_host_changed(twice=True) def test_delete_port_no_fdb_entries_with_ha_port(self): l2pop_mech = l2pop_mech_driver.L2populationMechanismDriver() l2pop_mech.L2PopulationAgentNotify = mock.Mock() l2pop_mech.rpc_ctx = mock.Mock() port = {'device_owner': l2pop_db.HA_ROUTER_PORTS[0]} context = mock.Mock() context.current = port with mock.patch.object(l2pop_mech, '_get_agent_fdb', return_value=None) as upd_port_down,\ mock.patch.object(l2pop_mech.L2PopulationAgentNotify, 'remove_fdb_entries'): l2pop_mech.delete_port_postcommit(context) self.assertTrue(upd_port_down.called) def test_delete_port_invokes_update_device_down(self): l2pop_mech = l2pop_mech_driver.L2populationMechanismDriver() l2pop_mech.L2PopulationAgentNotify = mock.Mock() l2pop_mech.rpc_ctx = mock.Mock() port = {'device_owner': ''} context = mock.Mock() context.current = port with mock.patch.object(l2pop_mech, '_get_agent_fdb', return_value=None) as upd_port_down,\ mock.patch.object(l2pop_mech.L2PopulationAgentNotify, 'remove_fdb_entries'): l2pop_mech.delete_port_postcommit(context) self.assertTrue(upd_port_down.called) def test_delete_unbound_port(self): self._test_delete_port_handles_agentless_host_id(None) def test_delete_port_bound_to_agentless_host(self): self._test_delete_port_handles_agentless_host_id('test') def _test_delete_port_handles_agentless_host_id(self, host): l2pop_mech = l2pop_mech_driver.L2populationMechanismDriver() l2pop_mech.initialize() with self.port() as port: port['port'][portbindings.HOST_ID] = host bindings = [models.PortBindingLevel()] port_context = driver_context.PortContext( self.driver, self.context, port['port'], self.driver.get_network( self.context, port['port']['network_id']), models.PortBinding(), bindings) # The point is to provide coverage and to assert that no exceptions # are raised. l2pop_mech.delete_port_postcommit(port_context) def test_delete_dvr_snat_port_fdb_entries(self): l2pop_mech = l2pop_mech_driver.L2populationMechanismDriver() l2pop_mech.initialize() self._setup_l3() with self.subnet(network=self._network, enable_dhcp=False) as snet: host_arg = {portbindings.HOST_ID: HOST, 'admin_state_up': True} with self.port(subnet=snet, device_owner=constants.DEVICE_OWNER_ROUTER_SNAT, arg_list=(portbindings.HOST_ID,), **host_arg) as p: device = 'tap' + p['port']['id'] self.callbacks.update_device_up(self.adminContext, agent_id=HOST, device=device) self.mock_fanout.reset_mock() p['port'][portbindings.HOST_ID] = HOST bindings = [models.PortBindingLevel()] port_context = driver_context.PortContext( self.driver, self.context, p['port'], self.driver.get_network( self.context, p['port']['network_id']), models.PortBinding(), bindings) fdbs = { p['port']['network_id']: { 'segment_id': 'fakeid', 'ports': {}, } } mock.patch.object( l2pop_mech, '_get_agent_fdb', return_value=fdbs).start() # The point is to provide coverage and to assert that # no exceptions are raised. l2pop_mech.delete_port_postcommit(port_context) def test_fixed_ips_change_unbound_port_no_rpc(self): l2pop_mech = l2pop_mech_driver.L2populationMechanismDriver() l2pop_mech.initialize() l2pop_mech.L2populationAgentNotify = mock.Mock() with self.port() as port: port_context = driver_context.PortContext( self.driver, self.context, port['port'], self.driver.get_network( self.context, port['port']['network_id']), models.PortBinding(), None) l2pop_mech._fixed_ips_changed( port_context, None, port['port'], (set(['10.0.0.1']), set())) # There's no need to send an RPC update if the IP address for an # unbound port changed. self.assertFalse( l2pop_mech.L2populationAgentNotify.update_fdb_entries.called) class TestL2PopulationMechDriver(base.BaseTestCase): def _test_get_tunnels(self, agent_ip, exclude_host=True): mech_driver = l2pop_mech_driver.L2populationMechanismDriver() agent = mock.Mock() agent.host = HOST network_ports = ((None, agent),) with mock.patch.object(l2pop_db, 'get_agent_ip', return_value=agent_ip): excluded_host = HOST + '-EXCLUDE' if exclude_host else HOST return mech_driver._get_tunnels(network_ports, excluded_host) def test_get_tunnels(self): tunnels = self._test_get_tunnels('20.0.0.1') self.assertIn('20.0.0.1', tunnels) def test_get_tunnels_no_ip(self): tunnels = self._test_get_tunnels(None) self.assertEqual(0, len(tunnels)) def test_get_tunnels_dont_exclude_host(self): tunnels = self._test_get_tunnels(None, exclude_host=False) self.assertEqual(0, len(tunnels)) def _test_create_agent_fdb(self, fdb_network_ports, agent_ips): mech_driver = l2pop_mech_driver.L2populationMechanismDriver() tunnel_network_ports, tunnel_agent = ( self._mock_network_ports(HOST + '1', [None])) agent_ips[tunnel_agent] = '10.0.0.1' def agent_ip_side_effect(agent): return agent_ips[agent] with mock.patch.object(l2pop_db, 'get_agent_ip', side_effect=agent_ip_side_effect),\ mock.patch.object(l2pop_db, 'get_nondistributed_active_network_ports', return_value=fdb_network_ports),\ mock.patch.object(l2pop_db, 'get_distributed_active_network_ports', return_value=tunnel_network_ports): agent = mock.Mock() agent.host = HOST segment = {'segmentation_id': 1, 'network_type': 'vxlan'} return mech_driver._create_agent_fdb(context, agent, segment, 'network_id') def _mock_network_ports(self, host_name, bindings): agent = mock.Mock() agent.host = host_name return [(binding, agent) for binding in bindings], agent def test_create_agent_fdb(self): binding = mock.Mock() binding.port = {'mac_address': '00:00:DE:AD:BE:EF', 'fixed_ips': [{'ip_address': '1.1.1.1'}]} fdb_network_ports, fdb_agent = ( self._mock_network_ports(HOST + '2', [binding])) agent_ips = {fdb_agent: '20.0.0.1'} agent_fdb = self._test_create_agent_fdb(fdb_network_ports, agent_ips) result = agent_fdb['network_id'] expected_result = {'segment_id': 1, 'network_type': 'vxlan', 'ports': {'10.0.0.1': [constants.FLOODING_ENTRY], '20.0.0.1': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( mac_address='00:00:DE:AD:BE:EF', ip_address='1.1.1.1')]}} self.assertEqual(expected_result, result) def test_create_agent_fdb_only_tunnels(self): agent_fdb = self._test_create_agent_fdb([], {}) result = agent_fdb['network_id'] expected_result = {'segment_id': 1, 'network_type': 'vxlan', 'ports': {'10.0.0.1': [constants.FLOODING_ENTRY]}} self.assertEqual(expected_result, result) def test_create_agent_fdb_concurrent_port_deletion(self): binding = mock.Mock() binding.port = {'mac_address': '00:00:DE:AD:BE:EF', 'fixed_ips': [{'ip_address': '1.1.1.1'}]} binding2 = mock.Mock() # the port was deleted binding2.port = None fdb_network_ports, fdb_agent = ( self._mock_network_ports(HOST + '2', [binding, binding2])) agent_ips = {fdb_agent: '20.0.0.1'} agent_fdb = self._test_create_agent_fdb(fdb_network_ports, agent_ips) result = agent_fdb['network_id'] expected_result = {'segment_id': 1, 'network_type': 'vxlan', 'ports': {'10.0.0.1': [constants.FLOODING_ENTRY], '20.0.0.1': [constants.FLOODING_ENTRY, l2pop_rpc.PortInfo( mac_address='00:00:DE:AD:BE:EF', ip_address='1.1.1.1')]}} self.assertEqual(expected_result, result) def test_update_port_precommit_mac_address_changed_raises(self): port = {'status': u'ACTIVE', 'device_owner': DEVICE_OWNER_COMPUTE, 'mac_address': u'12:34:56:78:4b:0e', 'id': u'1'} original_port = port.copy() original_port['mac_address'] = u'12:34:56:78:4b:0f' with mock.patch.object(driver_context.segments_db, 'get_network_segments'): ctx = driver_context.PortContext(mock.Mock(), mock.Mock(), port, mock.MagicMock(), models.PortBinding(), [models.PortBindingLevel()], original_port=original_port) mech_driver = l2pop_mech_driver.L2populationMechanismDriver() with testtools.ExpectedException(exceptions.InvalidInput): mech_driver.update_port_precommit(ctx) def test_agent_restarted(self): mech_driver = l2pop_mech_driver.L2populationMechanismDriver() ctx = mock.Mock() ctx.host = "__host1__" ctx._plugin_context = {} agent = mock.Mock() agent.started_at = datetime.datetime(2018, 5, 25, 15, 51, 20) agent.heartbeat_timestamp = datetime.datetime(2018, 5, 25, 15, 51, 50) with mock.patch.object(l2pop_db, 'get_agent_by_host', return_value=agent): res = mech_driver.agent_restarted(ctx) self.assertTrue(res) agent.heartbeat_timestamp = datetime.datetime(2018, 5, 25, 15, 58, 30) with mock.patch.object(l2pop_db, 'get_agent_by_host', return_value=agent): res = mech_driver.agent_restarted(ctx) self.assertFalse(res) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/l2pop/test_db.py0000664000175000017500000003036413553660047026025 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib import context from neutron_lib.utils import net from oslo_utils import uuidutils from neutron.common import constants as n_const from neutron.db.models import l3 as l3_models from neutron.db import models_v2 from neutron.objects import l3_hamode from neutron.objects import network as network_obj from neutron.objects import router as l3_objs from neutron.plugins.ml2.drivers.l2pop import db as l2pop_db from neutron.plugins.ml2 import models from neutron.tests.common import helpers from neutron.tests import tools from neutron.tests.unit import testlib_api HOST = helpers.HOST HOST_2 = 'HOST_2' HOST_3 = 'HOST_3' HOST_2_TUNNELING_IP = '20.0.0.2' HOST_3_TUNNELING_IP = '20.0.0.3' TEST_ROUTER_ID = uuidutils.generate_uuid() TEST_NETWORK_ID = uuidutils.generate_uuid() TEST_HA_NETWORK_ID = uuidutils.generate_uuid() PLUGIN_NAME = 'ml2' class TestL2PopulationDBTestCase(testlib_api.SqlTestCase): def setUp(self): super(TestL2PopulationDBTestCase, self).setUp() self.setup_coreplugin(PLUGIN_NAME) self.ctx = context.get_admin_context() self._create_network() def _create_network(self, network_id=TEST_NETWORK_ID): network_obj.Network(self.ctx, id=network_id).create() def _create_router(self, distributed=True, ha=False): with self.ctx.session.begin(subtransactions=True): self.ctx.session.add(l3_models.Router(id=TEST_ROUTER_ID)) l3_objs.RouterExtraAttributes( self.ctx, router_id=TEST_ROUTER_ID, distributed=distributed, ha=ha).create() def _create_ha_router(self, distributed=False): helpers.register_l3_agent(HOST_2) helpers.register_ovs_agent(HOST_2, tunneling_ip=HOST_2_TUNNELING_IP) # Register l3 agent on host3, which doesn't host any HA router. # Tests should test that host3 is not a HA agent host. helpers.register_l3_agent(HOST_3) helpers.register_ovs_agent(HOST_3, tunneling_ip=HOST_3_TUNNELING_IP) with self.ctx.session.begin(subtransactions=True): network_obj.Network(self.ctx, id=TEST_HA_NETWORK_ID).create() self._create_router(distributed=distributed, ha=True) for state, host in [(n_const.HA_ROUTER_STATE_ACTIVE, HOST), (n_const.HA_ROUTER_STATE_STANDBY, HOST_2)]: self._setup_port_binding( network_id=TEST_HA_NETWORK_ID, device_owner=constants.DEVICE_OWNER_ROUTER_HA_INTF, device_id=TEST_ROUTER_ID, host_state=state, host=host) def get_l3_agent_by_host(self, agent_host): plugin = helpers.FakePlugin() return plugin._get_agent_by_type_and_host( self.ctx, constants.AGENT_TYPE_L3, agent_host) def test_get_agent_by_host(self): helpers.register_l3_agent() helpers.register_dhcp_agent() helpers.register_ovs_agent() agent = l2pop_db.get_agent_by_host( self.ctx, helpers.HOST) self.assertEqual(constants.AGENT_TYPE_OVS, agent.agent_type) def test_get_agent_by_host_no_candidate(self): helpers.register_l3_agent() helpers.register_dhcp_agent() agent = l2pop_db.get_agent_by_host( self.ctx, helpers.HOST) self.assertIsNone(agent) def _setup_port_binding(self, **kwargs): with self.ctx.session.begin(subtransactions=True): mac = net.get_random_mac('fa:16:3e:00:00:00'.split(':')) port_id = uuidutils.generate_uuid() network_id = kwargs.get('network_id', TEST_NETWORK_ID) device_owner = kwargs.get('device_owner', '') device_id = kwargs.get('device_id', '') host = kwargs.get('host', helpers.HOST) self.ctx.session.add(models_v2.Port( id=port_id, network_id=network_id, mac_address=mac, admin_state_up=True, status=constants.PORT_STATUS_ACTIVE, device_id=device_id, device_owner=device_owner)) port_binding_cls = models.PortBinding binding_kwarg = {'port_id': port_id, 'host': host, 'vif_type': portbindings.VIF_TYPE_UNBOUND, 'vnic_type': portbindings.VNIC_NORMAL} if device_owner == constants.DEVICE_OWNER_DVR_INTERFACE: port_binding_cls = models.DistributedPortBinding binding_kwarg['router_id'] = TEST_ROUTER_ID binding_kwarg['status'] = constants.PORT_STATUS_DOWN self.ctx.session.add(port_binding_cls(**binding_kwarg)) if network_id == TEST_HA_NETWORK_ID: agent = self.get_l3_agent_by_host(host) l3_hamode.L3HARouterAgentPortBinding( self.ctx, port_id=port_id, router_id=device_id, l3_agent_id=agent['id'], state=kwargs.get( 'host_state', n_const.HA_ROUTER_STATE_ACTIVE)).create() def test_get_distributed_active_network_ports(self): self._setup_port_binding( device_owner=constants.DEVICE_OWNER_DVR_INTERFACE) # Register a L2 agent + A bunch of other agents on the same host helpers.register_l3_agent() helpers.register_dhcp_agent() helpers.register_ovs_agent() tunnel_network_ports = l2pop_db.get_distributed_active_network_ports( self.ctx, TEST_NETWORK_ID) self.assertEqual(1, len(tunnel_network_ports)) _, agent = tunnel_network_ports[0] self.assertEqual(constants.AGENT_TYPE_OVS, agent.agent_type) def test_get_distributed_active_network_ports_no_candidate(self): self._setup_port_binding( device_owner=constants.DEVICE_OWNER_DVR_INTERFACE) # Register a bunch of non-L2 agents on the same host helpers.register_l3_agent() helpers.register_dhcp_agent() tunnel_network_ports = l2pop_db.get_distributed_active_network_ports( self.ctx, TEST_NETWORK_ID) self.assertEqual(0, len(tunnel_network_ports)) def test_get_nondistributed_active_network_ports(self): self._setup_port_binding(dvr=False) # Register a L2 agent + A bunch of other agents on the same host helpers.register_l3_agent() helpers.register_dhcp_agent() helpers.register_ovs_agent() fdb_network_ports = l2pop_db.get_nondistributed_active_network_ports( self.ctx, TEST_NETWORK_ID) self.assertEqual(1, len(fdb_network_ports)) _, agent = fdb_network_ports[0] self.assertEqual(constants.AGENT_TYPE_OVS, agent.agent_type) def test_get_nondistributed_active_network_ports_no_candidate(self): self._setup_port_binding(dvr=False) # Register a bunch of non-L2 agents on the same host helpers.register_l3_agent() helpers.register_dhcp_agent() fdb_network_ports = l2pop_db.get_nondistributed_active_network_ports( self.ctx, TEST_NETWORK_ID) self.assertEqual(0, len(fdb_network_ports)) def test__get_ha_router_interface_ids_with_ha_dvr_snat_port(self): helpers.register_dhcp_agent() helpers.register_l3_agent() helpers.register_ovs_agent() self._create_ha_router() self._setup_port_binding( device_owner=constants.DEVICE_OWNER_ROUTER_SNAT, device_id=TEST_ROUTER_ID) ha_iface_ids = l2pop_db._get_ha_router_interface_ids( self.ctx, TEST_NETWORK_ID) self.assertEqual(1, len(list(ha_iface_ids))) def test__get_ha_router_interface_ids_with_ha_replicated_port(self): helpers.register_dhcp_agent() helpers.register_l3_agent() helpers.register_ovs_agent() self._create_ha_router() self._setup_port_binding( device_owner=constants.DEVICE_OWNER_HA_REPLICATED_INT, device_id=TEST_ROUTER_ID) ha_iface_ids = l2pop_db._get_ha_router_interface_ids( self.ctx, TEST_NETWORK_ID) self.assertEqual(1, len(list(ha_iface_ids))) def test__get_ha_router_interface_ids_with_no_ha_port(self): self._create_router() self._setup_port_binding( device_owner=constants.DEVICE_OWNER_ROUTER_SNAT, device_id=TEST_ROUTER_ID) ha_iface_ids = l2pop_db._get_ha_router_interface_ids( self.ctx, TEST_NETWORK_ID) self.assertEqual(0, len(list(ha_iface_ids))) def test_active_network_ports_with_dvr_snat_port(self): # Test to get agent hosting dvr snat port helpers.register_l3_agent() helpers.register_dhcp_agent() helpers.register_ovs_agent() # create DVR router self._create_router() # setup DVR snat port self._setup_port_binding( device_owner=constants.DEVICE_OWNER_ROUTER_SNAT, device_id=TEST_ROUTER_ID) helpers.register_dhcp_agent() fdb_network_ports = l2pop_db.get_nondistributed_active_network_ports( self.ctx, TEST_NETWORK_ID) self.assertEqual(1, len(fdb_network_ports)) def test_active_network_ports_with_ha_dvr_snat_port(self): # test to get HA agents hosting HA+DVR snat port helpers.register_dhcp_agent() helpers.register_l3_agent() helpers.register_ovs_agent() # create HA+DVR router self._create_ha_router() # setup HA snat port self._setup_port_binding( device_owner=constants.DEVICE_OWNER_ROUTER_SNAT, device_id=TEST_ROUTER_ID) fdb_network_ports = l2pop_db.get_nondistributed_active_network_ports( self.ctx, TEST_NETWORK_ID) self.assertEqual(0, len(fdb_network_ports)) ha_ports = l2pop_db.get_ha_active_network_ports( self.ctx, TEST_NETWORK_ID) self.assertEqual(2, len(ha_ports)) def test_active_port_count_with_dvr_snat_port(self): helpers.register_l3_agent() helpers.register_dhcp_agent() helpers.register_ovs_agent() self._create_router() self._setup_port_binding( device_owner=constants.DEVICE_OWNER_ROUTER_SNAT, device_id=TEST_ROUTER_ID) helpers.register_dhcp_agent() port_count = l2pop_db.get_agent_network_active_port_count( self.ctx, HOST, TEST_NETWORK_ID) self.assertEqual(1, port_count) port_count = l2pop_db.get_agent_network_active_port_count( self.ctx, HOST_2, TEST_NETWORK_ID) self.assertEqual(0, port_count) def test_active_port_count_with_ha_dvr_snat_port(self): helpers.register_dhcp_agent() helpers.register_l3_agent() helpers.register_ovs_agent() self._create_ha_router() self._setup_port_binding( device_owner=constants.DEVICE_OWNER_ROUTER_SNAT, device_id=TEST_ROUTER_ID) port_count = l2pop_db.get_agent_network_active_port_count( self.ctx, HOST, TEST_NETWORK_ID) self.assertEqual(1, port_count) port_count = l2pop_db.get_agent_network_active_port_count( self.ctx, HOST_2, TEST_NETWORK_ID) self.assertEqual(1, port_count) def test_get_ha_agents_by_router_id(self): helpers.register_dhcp_agent() helpers.register_l3_agent() helpers.register_ovs_agent() self._create_ha_router() self._setup_port_binding( device_owner=constants.DEVICE_OWNER_ROUTER_SNAT, device_id=TEST_ROUTER_ID) agents = l2pop_db.get_ha_agents_by_router_id( self.ctx, TEST_ROUTER_ID) ha_agents = [agent.host for agent in agents] self.assertEqual(tools.UnorderedList([HOST, HOST_2]), ha_agents) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/l2pop/__init__.py0000664000175000017500000000000013553660046026117 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/agent/0000775000175000017500000000000013553660157024065 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/agent/test_capabilities.py0000664000175000017500000000340213553660047030124 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import events from neutron_lib import fixture from neutron.plugins.ml2.drivers.agent import capabilities from neutron.tests import base class CapabilitiesTest(base.BaseTestCase): def setUp(self): super(CapabilitiesTest, self).setUp() self._mgr = mock.Mock() self.useFixture(fixture.CallbackRegistryFixture( callback_manager=self._mgr)) def test_notify_init_event(self): mock_agent_type = mock.Mock() mock_agent = mock.Mock() capabilities.notify_init_event(mock_agent_type, mock_agent) self._mgr.publish.assert_called_with(mock_agent_type, events.AFTER_INIT, mock_agent, payload=None) def test_register(self): mock_callback = mock.Mock() mock_agent_type = mock.Mock() capabilities.register(mock_callback, mock_agent_type) self._mgr.subscribe.assert_called_with(mock_callback, mock_agent_type, events.AFTER_INIT) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/agent/test__agent_manager_base.py0000664000175000017500000000335713553660046031424 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.plugins.ml2.drivers.agent import _agent_manager_base as amb from neutron.tests import base class RPCCallBackImpl(amb.CommonAgentManagerRpcCallBackBase): def security_groups_rule_updated(self, context, **kwargs): pass def security_groups_member_updated(self, context, **kwargs): pass class Test_CommonAgentManagerRpcCallBackBase(base.BaseTestCase): def setUp(self): super(Test_CommonAgentManagerRpcCallBackBase, self).setUp() self.rpc_callbacks = RPCCallBackImpl(None, None, None) def test_get_and_clear_updated_devices(self): updated_devices = ['tap1', 'tap2'] self.rpc_callbacks.updated_devices = updated_devices self.assertEqual(updated_devices, self.rpc_callbacks.get_and_clear_updated_devices()) self.assertEqual(set(), self.rpc_callbacks.updated_devices) def test_add_network(self): segment = amb.NetworkSegment('vlan', 'physnet1', 100) network_id = "foo" self.rpc_callbacks.add_network(network_id, segment) self.assertEqual(segment, self.rpc_callbacks.network_map[network_id]) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/agent/test__common_agent.py0000664000175000017500000006170413553660047030311 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.agent import constants as agent_consts from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from oslo_config import cfg import testtools from neutron.agent.linux import bridge_lib from neutron.plugins.ml2.drivers.agent import _agent_manager_base as amb from neutron.plugins.ml2.drivers.agent import _common_agent as ca from neutron.tests import base LOCAL_IP = '192.168.0.33' LOCAL_IPV6 = '2001:db8:1::33' VXLAN_GROUPV6 = 'ff05::/120' PORT_1 = 'abcdef01-12ddssdfds-fdsfsd' DEVICE_1 = 'tapabcdef01-12' NETWORK_ID = '57653b20-ed5b-4ed0-a31d-06f84e3fd909' BRIDGE_MAPPING_VALUE = 'br-eth2' BRIDGE_MAPPINGS = {'physnet0': BRIDGE_MAPPING_VALUE} INTERFACE_MAPPINGS = {'physnet1': 'eth1'} FAKE_DEFAULT_DEV = mock.Mock() FAKE_DEFAULT_DEV.name = 'eth1' PORT_DATA = { "port_id": PORT_1, "device": DEVICE_1 } class TestCommonAgentLoop(base.BaseTestCase): def setUp(self): super(TestCommonAgentLoop, self).setUp() # disable setting up periodic state reporting cfg.CONF.set_override('report_interval', 0, 'AGENT') cfg.CONF.set_default('firewall_driver', 'neutron.agent.firewall.NoopFirewallDriver', group='SECURITYGROUP') cfg.CONF.set_override('local_ip', LOCAL_IP, 'VXLAN') self.get_bridge_names_p = mock.patch.object(bridge_lib, 'get_bridge_names') self.get_bridge_names = self.get_bridge_names_p.start() self.get_bridge_names.return_value = ["br-int", "brq1"] manager = mock.Mock() manager.get_all_devices.return_value = [] manager.get_agent_configurations.return_value = {} manager.get_rpc_consumers.return_value = [] with mock.patch.object(ca.CommonAgentLoop, '_validate_manager_class'),\ mock.patch.object(ca.CommonAgentLoop, '_validate_rpc_endpoints'): self.agent = ca.CommonAgentLoop(manager, 0, 10, 'fake_agent', 'foo-binary') with mock.patch.object(self.agent, "daemon_loop"): self.agent.start() def test_treat_devices_removed_notify(self): handler = mock.Mock() registry.subscribe(handler, resources.PORT_DEVICE, events.AFTER_DELETE) devices = [DEVICE_1] self.agent.treat_devices_removed(devices) handler.assert_called_once_with(mock.ANY, mock.ANY, self.agent, context=mock.ANY, device=DEVICE_1, port_id=mock.ANY) def test_treat_devices_added_updated_notify(self): handler = mock.Mock() registry.subscribe(handler, resources.PORT_DEVICE, events.AFTER_UPDATE) agent = self.agent mock_details = {'device': 'dev123', 'port_id': 'port123', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'physical_network': 'physnet1', 'device_owner': 'horse'} agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] agent.mgr = mock.Mock() agent.mgr.plug_interface.return_value = True agent.treat_devices_added_updated(set(['dev123'])) handler.assert_called_once_with(mock.ANY, mock.ANY, self.agent, context=mock.ANY, device_details=mock_details) def test_treat_devices_removed_with_existed_device(self): agent = self.agent agent.mgr.ensure_port_admin_state = mock.Mock() devices = [DEVICE_1] agent.network_ports[NETWORK_ID].append(PORT_DATA) with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd,\ mock.patch.object(agent.sg_agent, "remove_devices_filter") as fn_rdf,\ mock.patch.object(agent.ext_manager, "delete_port") as ext_mgr_delete_port: fn_udd.return_value = {'device': DEVICE_1, 'exists': True} with mock.patch.object(ca.LOG, 'info') as log: resync = agent.treat_devices_removed(devices) self.assertEqual(2, log.call_count) self.assertFalse(resync) self.assertTrue(fn_udd.called) self.assertTrue(fn_rdf.called) self.assertTrue(ext_mgr_delete_port.called) self.assertNotIn(PORT_DATA, agent.network_ports[NETWORK_ID]) def test_treat_devices_removed_with_not_existed_device(self): agent = self.agent devices = [DEVICE_1] agent.network_ports[NETWORK_ID].append(PORT_DATA) with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd,\ mock.patch.object(agent.sg_agent, "remove_devices_filter") as fn_rdf,\ mock.patch.object(agent.ext_manager, "delete_port") as ext_mgr_delete_port: fn_udd.return_value = {'device': DEVICE_1, 'exists': False} with mock.patch.object(ca.LOG, 'debug') as log: resync = agent.treat_devices_removed(devices) self.assertEqual(1, log.call_count) self.assertFalse(resync) self.assertTrue(fn_udd.called) self.assertTrue(fn_rdf.called) self.assertTrue(ext_mgr_delete_port.called) self.assertNotIn(PORT_DATA, agent.network_ports[NETWORK_ID]) def test_treat_devices_removed_failed(self): agent = self.agent devices = [DEVICE_1] agent.network_ports[NETWORK_ID].append(PORT_DATA) with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd,\ mock.patch.object(agent.sg_agent, "remove_devices_filter") as fn_rdf,\ mock.patch.object(agent.ext_manager, "delete_port") as ext_mgr_delete_port: fn_udd.side_effect = Exception() resync = agent.treat_devices_removed(devices) self.assertTrue(resync) self.assertTrue(fn_udd.called) self.assertTrue(fn_rdf.called) self.assertTrue(ext_mgr_delete_port.called) self.assertNotIn(PORT_DATA, agent.network_ports[NETWORK_ID]) def test_treat_devices_removed_failed_extension(self): agent = self.agent devices = [DEVICE_1] agent.network_ports[NETWORK_ID].append(PORT_DATA) with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd,\ mock.patch.object(agent.sg_agent, "remove_devices_filter") as fn_rdf,\ mock.patch.object(agent.ext_manager, "delete_port") as ext_mgr_delete_port: ext_mgr_delete_port.side_effect = Exception() resync = agent.treat_devices_removed(devices) self.assertTrue(resync) self.assertTrue(fn_udd.called) self.assertTrue(fn_rdf.called) self.assertTrue(ext_mgr_delete_port.called) self.assertNotIn(PORT_DATA, agent.network_ports[NETWORK_ID]) def test_treat_devices_removed_delete_arp_spoofing(self): agent = self.agent agent._ensure_port_admin_state = mock.Mock() devices = [DEVICE_1] with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd,\ mock.patch.object(agent.sg_agent, "remove_devices_filter"): fn_udd.return_value = {'device': DEVICE_1, 'exists': True} with mock.patch.object(agent.mgr, 'delete_arp_spoofing_protection') as de_arp: agent.treat_devices_removed(devices) de_arp.assert_called_with(devices) def test__get_devices_locally_modified(self): new_ts = {1: 1000, 2: 2000, 3: 3000} old_ts = {1: 10, 2: 2000, 4: 900} # 3 and 4 are not returned because 3 is a new device and 4 is a # removed device self.assertEqual( set([1]), self.agent._get_devices_locally_modified(new_ts, old_ts)) def _test_scan_devices(self, previous, updated, fake_current, expected, sync, fake_ts_current=None): self.agent.mgr = mock.Mock() self.agent.mgr.get_all_devices.return_value = fake_current self.agent.mgr.get_devices_modified_timestamps.return_value = ( fake_ts_current or {}) self.agent.rpc_callbacks.get_and_clear_updated_devices.return_value =\ updated results = self.agent.scan_devices(previous, sync) self.assertEqual(expected, results) def test_scan_devices_no_changes(self): previous = {'current': set([1, 2]), 'updated': set(), 'added': set(), 'removed': set(), 'timestamps': {}} fake_current = set([1, 2]) updated = set() expected = {'current': set([1, 2]), 'updated': set(), 'added': set(), 'removed': set(), 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=False) def test_scan_devices_timestamp_triggers_updated(self): previous = {'current': set([1, 2]), 'updated': set(), 'added': set(), 'removed': set(), 'timestamps': {2: 600}} fake_current = set([1, 2]) updated = set() expected = {'current': set([1, 2]), 'updated': set([2]), 'added': set(), 'removed': set(), 'timestamps': {2: 1000}} self._test_scan_devices(previous, updated, fake_current, expected, sync=False, fake_ts_current={2: 1000}) def test_scan_devices_added_removed(self): previous = {'current': set([1, 2]), 'updated': set(), 'added': set(), 'removed': set(), 'timestamps': {}} fake_current = set([2, 3]) updated = set() expected = {'current': set([2, 3]), 'updated': set(), 'added': set([3]), 'removed': set([1]), 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=False) def test_scan_devices_removed_retried_on_sync(self): previous = {'current': set([2, 3]), 'updated': set(), 'added': set(), 'removed': set([1]), 'timestamps': {}} fake_current = set([2, 3]) updated = set() expected = {'current': set([2, 3]), 'updated': set(), 'added': set([2, 3]), 'removed': set([1]), 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=True) def test_scan_devices_vanished_removed_on_sync(self): previous = {'current': set([2, 3]), 'updated': set(), 'added': set(), 'removed': set([1]), 'timestamps': {}} # Device 2 disappeared. fake_current = set([3]) updated = set() # Device 1 should be retried. expected = {'current': set([3]), 'updated': set(), 'added': set([3]), 'removed': set([1, 2]), 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=True) def test_scan_devices_updated(self): previous = {'current': set([1, 2]), 'updated': set(), 'added': set(), 'removed': set(), 'timestamps': {}} fake_current = set([1, 2]) updated = set([1]) expected = {'current': set([1, 2]), 'updated': set([1]), 'added': set(), 'removed': set(), 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=False) def test_scan_devices_updated_non_existing(self): previous = {'current': set([1, 2]), 'updated': set(), 'added': set(), 'removed': set(), 'timestamps': {}} fake_current = set([1, 2]) updated = set([3]) expected = {'current': set([1, 2]), 'updated': set(), 'added': set(), 'removed': set(), 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=False) def test_scan_devices_updated_deleted_concurrently(self): previous = { 'current': set([1, 2]), 'updated': set(), 'added': set(), 'removed': set(), 'timestamps': {} } # Device 2 disappeared. fake_current = set([1]) # Device 2 got an concurrent update via network_update updated = set([2]) expected = { 'current': set([1]), 'updated': set(), 'added': set(), 'removed': set([2]), 'timestamps': {} } self._test_scan_devices( previous, updated, fake_current, expected, sync=False ) def test_scan_devices_updated_on_sync(self): previous = {'current': set([1, 2]), 'updated': set([1]), 'added': set(), 'removed': set(), 'timestamps': {}} fake_current = set([1, 2]) updated = set([2]) expected = {'current': set([1, 2]), 'updated': set([1, 2]), 'added': set([1, 2]), 'removed': set(), 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=True) def test_scan_devices_with_delete_arp_protection(self): previous = None fake_current = set([1, 2]) updated = set() expected = {'current': set([1, 2]), 'updated': set(), 'added': set([1, 2]), 'removed': set(), 'timestamps': {}} self._test_scan_devices(previous, updated, fake_current, expected, sync=False) self.agent.mgr.delete_unreferenced_arp_protection.assert_called_with( fake_current) def test_process_network_devices(self): agent = self.agent device_info = {'current': set(), 'added': set(['tap3', 'tap4']), 'updated': set(['tap2', 'tap3']), 'removed': set(['tap1'])} agent.sg_agent.setup_port_filters = mock.Mock() agent.treat_devices_added_updated = mock.Mock(return_value=False) agent.treat_devices_removed = mock.Mock(return_value=False) agent.process_network_devices(device_info) agent.sg_agent.setup_port_filters.assert_called_with( device_info['added'], device_info['updated']) agent.treat_devices_added_updated.assert_called_with(set(['tap2', 'tap3', 'tap4'])) agent.treat_devices_removed.assert_called_with(set(['tap1'])) def test_treat_devices_added_updated_no_local_interface(self): agent = self.agent mock_details = {'device': 'dev123', 'port_id': 'port123', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'physical_network': 'physnet1', 'device_owner': constants.DEVICE_OWNER_NETWORK_PREFIX} agent.ext_manager = mock.Mock() agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] agent.mgr = mock.Mock() agent.mgr.plug_interface.return_value = False agent.mgr.ensure_port_admin_state = mock.Mock() agent.treat_devices_added_updated(set(['tap1'])) self.assertFalse(agent.mgr.ensure_port_admin_state.called) def test_treat_devices_added_updated_admin_state_up_true(self): agent = self.agent mock_details = {'device': 'dev123', 'port_id': 'port123', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'physical_network': 'physnet1', 'device_owner': constants.DEVICE_OWNER_NETWORK_PREFIX} mock_port_data = { 'port_id': mock_details['port_id'], 'device': mock_details['device'] } agent.ext_manager = mock.Mock() agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] agent.mgr = mock.Mock() agent.mgr.plug_interface.return_value = True agent.mgr.ensure_port_admin_state = mock.Mock() mock_segment = amb.NetworkSegment(mock_details['network_type'], mock_details['physical_network'], mock_details['segmentation_id']) with mock.patch('neutron.plugins.ml2.drivers.agent.' '_agent_manager_base.NetworkSegment', return_value=mock_segment): resync_needed = agent.treat_devices_added_updated(set(['tap1'])) self.assertFalse(resync_needed) agent.rpc_callbacks.add_network.assert_called_with('net123', mock_segment) agent.mgr.plug_interface.assert_called_with( 'net123', mock_segment, 'dev123', constants.DEVICE_OWNER_NETWORK_PREFIX) self.assertTrue(agent.plugin_rpc.update_device_up.called) self.assertTrue(agent.ext_manager.handle_port.called) self.assertIn(mock_port_data, agent.network_ports[ mock_details['network_id']] ) def test_treat_devices_added_updated_setup_arp_protection(self): agent = self.agent mock_details = {'device': 'dev123', 'port_id': 'port123', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'physical_network': 'physnet1', 'device_owner': constants.DEVICE_OWNER_NETWORK_PREFIX} agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] agent.mgr = mock.Mock() agent.mgr.plug_interface.return_value = True with mock.patch.object(agent.mgr, 'setup_arp_spoofing_protection') as set_arp: agent.treat_devices_added_updated(set(['tap1'])) set_arp.assert_called_with(mock_details['device'], mock_details) def test__process_device_if_exists_missing_intf(self): mock_details = {'device': 'dev123', 'port_id': 'port123', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'physical_network': 'physnet1', 'device_owner': constants.DEVICE_OWNER_NETWORK_PREFIX} self.agent.mgr = mock.Mock() self.agent.mgr.get_all_devices.return_value = [] self.agent.mgr.plug_interface.side_effect = RuntimeError() self.agent._process_device_if_exists(mock_details) def test__process_device_if_exists_error(self): mock_details = {'device': 'dev123', 'port_id': 'port123', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'physical_network': 'physnet1', 'device_owner': constants.DEVICE_OWNER_NETWORK_PREFIX} self.agent.mgr = mock.Mock() self.agent.mgr.get_all_devices.return_value = ['dev123'] self.agent.mgr.plug_interface.side_effect = RuntimeError() with testtools.ExpectedException(RuntimeError): # device exists so it should raise self.agent._process_device_if_exists(mock_details) def test_set_rpc_timeout(self): self.agent.stop() for rpc_client in (self.agent.plugin_rpc.client, self.agent.sg_plugin_rpc.client, self.agent.state_rpc.client): self.assertEqual(cfg.CONF.AGENT.quitting_rpc_timeout, rpc_client.timeout) def test_set_rpc_timeout_no_value(self): self.agent.quitting_rpc_timeout = None with mock.patch.object(self.agent, 'set_rpc_timeout') as mock_set_rpc: self.agent.stop() self.assertFalse(mock_set_rpc.called) def test_report_state_revived(self): with mock.patch.object(self.agent.state_rpc, "report_state") as report_st: report_st.return_value = agent_consts.AGENT_REVIVED self.agent._report_state() self.assertTrue(self.agent.fullsync) def test_update_network_ports(self): port_1_data = PORT_DATA NETWORK_2_ID = 'fake_second_network' port_2_data = { 'port_id': 'fake_port_2', 'device': 'fake_port_2_device_name' } self.agent.network_ports[NETWORK_ID].append( port_1_data ) self.agent.network_ports[NETWORK_ID].append( port_2_data ) #check update port: self.agent._update_network_ports( NETWORK_2_ID, port_2_data['port_id'], port_2_data['device'] ) self.assertNotIn(port_2_data, self.agent.network_ports[NETWORK_ID]) self.assertIn(port_2_data, self.agent.network_ports[NETWORK_2_ID]) def test_clean_network_ports(self): port_1_data = PORT_DATA port_2_data = { 'port_id': 'fake_port_2', 'device': 'fake_port_2_device_name' } self.agent.network_ports[NETWORK_ID].append( port_1_data ) self.agent.network_ports[NETWORK_ID].append( port_2_data ) #check removing port from network when other ports are still there: cleaned_port_id = self.agent._clean_network_ports(DEVICE_1) self.assertIn(NETWORK_ID, self.agent.network_ports.keys()) self.assertNotIn(port_1_data, self.agent.network_ports[NETWORK_ID]) self.assertIn(port_2_data, self.agent.network_ports[NETWORK_ID]) self.assertEqual(PORT_1, cleaned_port_id) #and now remove last port from network: cleaned_port_id = self.agent._clean_network_ports( port_2_data['device'] ) self.assertNotIn(NETWORK_ID, self.agent.network_ports.keys()) self.assertEqual(port_2_data['port_id'], cleaned_port_id) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/agent/__init__.py0000664000175000017500000000000013553660046026161 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/macvtap/0000775000175000017500000000000013553660157024422 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/macvtap/agent/0000775000175000017500000000000013553660157025520 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/macvtap/agent/test_macvtap_neutron_agent.py0000664000175000017500000002552113553660047033517 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import sys import mock from neutron_lib.utils import helpers from oslo_config import cfg from oslo_service import service from neutron.agent.linux import ip_lib from neutron.common import config as common_config from neutron.common import topics from neutron.plugins.ml2.drivers.agent import _agent_manager_base as amb from neutron.plugins.ml2.drivers.macvtap.agent import macvtap_neutron_agent from neutron.plugins.ml2.drivers.macvtap import macvtap_common from neutron.tests import base INTERFACE_MAPPINGS = {'physnet1': 'eth1'} NETWORK_ID = 'net-id123' NETWORK_SEGMENT_VLAN = amb.NetworkSegment('vlan', 'physnet1', 1) NETWORK_SEGMENT_FLAT = amb.NetworkSegment('flat', 'physnet1', None) class TestMacvtapRPCCallbacks(base.BaseTestCase): def setUp(self): super(TestMacvtapRPCCallbacks, self).setUp() agent = mock.Mock() agent.mgr = mock.Mock() agent.mgr.interface_mappings = INTERFACE_MAPPINGS self.rpc = macvtap_neutron_agent.MacvtapRPCCallBack(mock.Mock(), agent, mock.Mock()) def test_network_delete_vlan(self): self.rpc.network_map = {NETWORK_ID: NETWORK_SEGMENT_VLAN} with mock.patch.object(ip_lib.IpLinkCommand, 'delete') as mock_del,\ mock.patch.object(macvtap_common, 'get_vlan_device_name', return_value='vlan1'),\ mock.patch.object(ip_lib.IPDevice, 'exists', return_value=True): self.rpc.network_delete("anycontext", network_id=NETWORK_ID) self.assertTrue(mock_del.called) def test_network_delete_flat(self): self.rpc.network_map = {NETWORK_ID: NETWORK_SEGMENT_FLAT} with mock.patch.object(ip_lib.IpLinkCommand, 'delete') as mock_del: self.rpc.network_delete( "anycontext", network_id=NETWORK_SEGMENT_FLAT.segmentation_id) self.assertFalse(mock_del.called) def test_port_update(self): port = {'id': 'port-id123', 'mac_address': 'mac1'} self.rpc.port_update(context=None, port=port) self.assertEqual(set(['mac1']), self.rpc.updated_devices) class TestMacvtapManager(base.BaseTestCase): def setUp(self): super(TestMacvtapManager, self).setUp() with mock.patch.object(ip_lib, 'device_exists', return_value=True): self.mgr = macvtap_neutron_agent.MacvtapManager(INTERFACE_MAPPINGS) def test_validate_interface_mappings_dev_exists(self): good_mapping = {'physnet1': 'eth1', 'physnet2': 'eth2'} self.mgr.interface_mappings = good_mapping with mock.patch.object(ip_lib, 'device_exists', return_value=True)\ as mock_de: self.mgr.validate_interface_mappings() mock_de.assert_any_call('eth1') mock_de.assert_any_call('eth2') self.assertEqual(2, mock_de.call_count) def test_validate_interface_mappings_dev_not_exists(self): bad_mapping = {'physnet1': 'foo'} self.mgr.interface_mappings = bad_mapping with mock.patch.object(ip_lib, 'device_exists', return_value=False)\ as mock_de, mock.patch.object(sys, 'exit') as mock_exit: self.mgr.validate_interface_mappings() mock_de.assert_called_with('foo') mock_exit.assert_called_once_with(1) def _test_ensure_port_admin_state(self, admin_state): dev = 'macvtap1' mac = 'mac1' self.mgr.mac_device_name_mappings = {mac: dev} with mock.patch.object(ip_lib, 'IPDevice') as mock_ip_dev: self.mgr.ensure_port_admin_state(mac, admin_state) self.assertEqual(admin_state, mock_ip_dev(dev).link.set_up.called) self.assertNotEqual(admin_state, mock_ip_dev(dev).link.set_down.called) def test_ensure_port_admin_state_up(self): self._test_ensure_port_admin_state(True) def test_ensure_port_admin_state_down(self): self._test_ensure_port_admin_state(False) def test_get_all_devices(self): listing = ['foo', 'macvtap0', 'macvtap1', 'bar'] # set some mac mappings to make sure they are cleaned up self.mgr.mac_device_name_mappings = {'foo': 'bar'} with mock.patch.object(os, 'listdir', return_value=listing)\ as mock_ld,\ mock.patch.object(ip_lib, 'get_device_mac') as mock_gdn: mock_gdn.side_effect = ['mac0', 'mac1'] result = self.mgr.get_all_devices() mock_ld.assert_called_once_with(macvtap_neutron_agent.MACVTAP_FS) self.assertEqual(set(['mac0', 'mac1']), result) self.assertEqual({'mac0': 'macvtap0', 'mac1': 'macvtap1'}, self.mgr.mac_device_name_mappings) def test_get_agent_configurations(self): expected = {'interface_mappings': INTERFACE_MAPPINGS} self.assertEqual(expected, self.mgr.get_agent_configurations()) def test_get_agent_id_ok(self): mock_devices = [ip_lib.IPDevice('macvtap1')] with mock.patch.object(ip_lib.IPWrapper, 'get_devices', return_value=mock_devices),\ mock.patch.object(ip_lib, 'get_device_mac', return_value='foo:bar'): self.assertEqual('macvtapfoobar', self.mgr.get_agent_id()) def test_get_agent_id_fail(self): mock_devices = [] with mock.patch.object(ip_lib.IPWrapper, 'get_devices', return_value=mock_devices),\ mock.patch.object(sys, 'exit') as mock_exit: self.mgr.get_agent_id() mock_exit.assert_called_once_with(1) def test_get_extension_driver_type(self): self.assertEqual('macvtap', self.mgr.get_extension_driver_type()) def test_get_rpc_callbacks(self): context = mock.Mock() agent = mock.Mock() sg_agent = mock.Mock() obj = self.mgr.get_rpc_callbacks(context, agent, sg_agent) self.assertIsInstance(obj, macvtap_neutron_agent.MacvtapRPCCallBack) def test_get_rpc_consumers(self): consumers = [[topics.PORT, topics.UPDATE], [topics.NETWORK, topics.DELETE], [topics.SECURITY_GROUP, topics.UPDATE]] self.assertEqual(consumers, self.mgr.get_rpc_consumers()) def test_plug_interface(self): self.mgr.mac_device_name_mappings['mac1'] = 'macvtap0' with mock.patch.object(ip_lib.IpLinkCommand, 'set_allmulticast_on')\ as mock_sao: self.mgr.plug_interface('network_id', 'network_segment', 'mac1', 'device_owner') self.assertTrue(mock_sao.called) class TestMacvtapMain(base.BaseTestCase): def test_parse_interface_mappings_good(self): cfg.CONF.set_override('physical_interface_mappings', 'good_mapping', 'macvtap') with mock.patch.object(helpers, 'parse_mappings', return_value=INTERFACE_MAPPINGS): mappings = macvtap_neutron_agent.parse_interface_mappings() self.assertEqual(INTERFACE_MAPPINGS, mappings) def test_parse_interface_mappings_bad(self): cfg.CONF.set_override('physical_interface_mappings', 'bad_mapping', 'macvtap') with mock.patch.object(helpers, 'parse_mappings', side_effect=ValueError('bad mapping')),\ mock.patch.object(sys, 'exit') as mock_exit: macvtap_neutron_agent.parse_interface_mappings() mock_exit.assert_called_with(1) def test_parse_interface_mappings_no_mapping(self): with mock.patch.object(sys, 'exit') as mock_exit: macvtap_neutron_agent.parse_interface_mappings() mock_exit.assert_called_with(1) def test_validate_firewall_driver_noop_long(self): cfg.CONF.set_override('firewall_driver', 'neutron.agent.firewall.NoopFirewallDriver', 'SECURITYGROUP') macvtap_neutron_agent.validate_firewall_driver() def test_validate_firewall_driver_noop(self): cfg.CONF.set_override('firewall_driver', 'noop', 'SECURITYGROUP') macvtap_neutron_agent.validate_firewall_driver() def test_validate_firewall_driver_other(self): cfg.CONF.set_override('firewall_driver', 'foo', 'SECURITYGROUP') with mock.patch.object(sys, 'exit')as mock_exit: macvtap_neutron_agent.validate_firewall_driver() mock_exit.assert_called_with(1) def test_main(self): cfg.CONF.set_override('quitting_rpc_timeout', 1, 'AGENT') cfg.CONF.set_override('polling_interval', 2, 'AGENT') mock_manager_return = mock.Mock(spec=amb.CommonAgentManagerBase) mock_launch_return = mock.Mock() with mock.patch.object(common_config, 'init'),\ mock.patch.object(common_config, 'setup_logging'),\ mock.patch.object(service, 'launch', return_value=mock_launch_return) as mock_launch,\ mock.patch.object(macvtap_neutron_agent, 'parse_interface_mappings', return_value=INTERFACE_MAPPINGS) as mock_pim,\ mock.patch.object(macvtap_neutron_agent, 'validate_firewall_driver') as mock_vfd,\ mock.patch('neutron.plugins.ml2.drivers.agent._common_agent.' 'CommonAgentLoop') as mock_loop,\ mock.patch('neutron.plugins.ml2.drivers.macvtap.agent.' 'macvtap_neutron_agent.MacvtapManager', return_value=mock_manager_return) as mock_manager: macvtap_neutron_agent.main() self.assertTrue(mock_vfd.called) self.assertTrue(mock_pim.called) mock_manager.assert_called_with(INTERFACE_MAPPINGS) mock_loop.assert_called_with(mock_manager_return, 2, 1, 'Macvtap agent', 'neutron-macvtap-agent') self.assertTrue(mock_launch.called) self.assertTrue(mock_launch_return.wait.called) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/macvtap/agent/__init__.py0000664000175000017500000000000013553660046027614 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/macvtap/test_macvtap_common.py0000664000175000017500000000463713553660046031045 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import hashlib import mock from neutron.plugins.ml2.drivers.macvtap import macvtap_common as m_common from neutron.tests import base MOCKED_HASH = "MOCKEDHASH" class MockSHA(object): def hexdigest(self): return MOCKED_HASH class MacvtapCommonTestCase(base.BaseTestCase): @mock.patch.object(hashlib, 'sha1', return_value=MockSHA()) def test_get_vlan_device_name(self, mocked_hash): # only the first six chars of the hash are being used in the algorithm hash_used = MOCKED_HASH[0:6] self.assertEqual('10charrrrr.1', m_common.get_vlan_device_name('10charrrrr', "1")) self.assertEqual('11ch' + hash_used + '.1', m_common.get_vlan_device_name('11charrrrrr', "1")) self.assertEqual('14ch' + hash_used + '.1', m_common.get_vlan_device_name('14charrrrrrrrr', "1")) self.assertEqual('14ch' + hash_used + '.1111', m_common.get_vlan_device_name('14charrrrrrrrr', "1111")) def test_get_vlan_subinterface_name_advanced(self): """Ensure the same hash is used for long interface names. If the generated vlan device name would be too long, make sure that everything before the '.' is equal. This might be helpful when debugging problems. """ max_device_name = "15charrrrrrrrrr" vlan_dev_name1 = m_common.get_vlan_device_name(max_device_name, "1") vlan_dev_name2 = m_common.get_vlan_device_name(max_device_name, "1111") self.assertEqual(vlan_dev_name1.partition(".")[0], vlan_dev_name2.partition(".")[0]) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/macvtap/__init__.py0000664000175000017500000000000013553660046026516 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/macvtap/mech_driver/0000775000175000017500000000000013553660157026711 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/macvtap/mech_driver/test_mech_macvtap.py0000664000175000017500000001571213553660046032754 0ustar zuulzuul00000000000000# Copyright (c) 2015 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib.plugins.ml2 import api from neutron.plugins.ml2.drivers.macvtap.mech_driver import mech_macvtap from neutron.tests.unit.plugins.ml2 import _test_mech_agent as base class MacvtapMechanismBaseTestCase(base.AgentMechanismBaseTestCase): VIF_TYPE = portbindings.VIF_TYPE_MACVTAP CAP_PORT_FILTER = False AGENT_TYPE = constants.AGENT_TYPE_MACVTAP GOOD_MAPPINGS = {'fake_physical_network': 'fake_if'} GOOD_CONFIGS = {'interface_mappings': GOOD_MAPPINGS} BAD_MAPPINGS = {'wrong_physical_network': 'wrong_if'} BAD_CONFIGS = {'interface_mappings': BAD_MAPPINGS} AGENT = {'alive': True, 'configurations': GOOD_CONFIGS, 'host': 'host'} AGENTS = [AGENT] AGENTS_DEAD = [{'alive': False, 'configurations': GOOD_CONFIGS, 'host': 'dead_host'}] AGENTS_BAD = [{'alive': False, 'configurations': GOOD_CONFIGS, 'host': 'bad_host_1'}, {'alive': True, 'configurations': BAD_CONFIGS, 'host': 'bad_host_2'}] def setUp(self): super(MacvtapMechanismBaseTestCase, self).setUp() self.driver = mech_macvtap.MacvtapMechanismDriver() self.driver.initialize() class MacvtapMechanismGenericTestCase(MacvtapMechanismBaseTestCase, base.AgentMechanismGenericTestCase): pass class MacvtapMechanismMigrationTestCase(object): # MIGRATION_SEGMENT must be overridden for the specific type being tested MIGRATION_SEGMENT = None MIGRATION_SEGMENTS = [MIGRATION_SEGMENT] def test__is_live_migration_true(self): original = {"binding:profile": {"migrating_to": "host"}} self._test__is_live_migration(True, original) def test__is_live_migration_false(self): self._test__is_live_migration(False, {}) def test__is_live_migration_false_None_original(self): self._test__is_live_migration(False, None) def _test__is_live_migration(self, expected, original): context = base.FakePortContext(self.AGENT_TYPE, self.AGENTS, self.MIGRATION_SEGMENTS, vnic_type=self.VNIC_TYPE, original=original) self.assertEqual(expected, self.driver._is_live_migration(context)) def _test_try_to_bind_segment_for_agent_migration(self, expected, original): context = base.FakePortContext(self.AGENT_TYPE, self.AGENTS, self.MIGRATION_SEGMENTS, vnic_type=self.VNIC_TYPE, original=original) result = self.driver.try_to_bind_segment_for_agent( context, self.MIGRATION_SEGMENT, self.AGENT) self.assertEqual(expected, result) def test_try_to_bind_segment_for_agent_migration_abort(self): original = {"binding:profile": {"migrating_to": "host"}, "binding:vif_details": {"macvtap_source": "bad_source"}, "binding:host_id": "source_host"} self._test_try_to_bind_segment_for_agent_migration(False, original) def test_try_to_bind_segment_for_agent_migration_ok(self): macvtap_src = "fake_if" seg_id = self.MIGRATION_SEGMENT.get(api.SEGMENTATION_ID) if seg_id: # In the vlan case, macvtap source name ends with .vlan_id macvtap_src += "." + str(seg_id) original = {"binding:profile": {"migrating_to": "host"}, "binding:vif_details": {"macvtap_source": macvtap_src}, "binding:host_id": "source_host"} self._test_try_to_bind_segment_for_agent_migration(True, original) class MacvtapMechanismFlatTestCase(MacvtapMechanismBaseTestCase, base.AgentMechanismFlatTestCase, MacvtapMechanismMigrationTestCase): MIGRATION_SEGMENT = {api.ID: 'flat_segment_id', api.NETWORK_TYPE: 'flat', api.PHYSICAL_NETWORK: 'fake_physical_network'} def test_type_flat_vif_details(self): context = base.FakePortContext(self.AGENT_TYPE, self.AGENTS, self.FLAT_SEGMENTS, vnic_type=self.VNIC_TYPE) self.driver.bind_port(context) vif_details = context._bound_vif_details self.assertIsNone(vif_details.get(portbindings.VIF_DETAILS_VLAN)) self.assertEqual("bridge", vif_details.get( portbindings.VIF_DETAILS_MACVTAP_MODE)) self.assertEqual("fake_if", vif_details.get( portbindings.VIF_DETAILS_PHYSICAL_INTERFACE)) self.assertEqual("fake_if", vif_details.get( portbindings.VIF_DETAILS_MACVTAP_SOURCE)) class MacvtapMechanismVlanTestCase(MacvtapMechanismBaseTestCase, base.AgentMechanismVlanTestCase, MacvtapMechanismMigrationTestCase): MIGRATION_SEGMENT = {api.ID: 'vlan_segment_id', api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'fake_physical_network', api.SEGMENTATION_ID: 1234} def test_type_vlan_vif_details(self): context = base.FakePortContext(self.AGENT_TYPE, self.AGENTS, self.VLAN_SEGMENTS, vnic_type=self.VNIC_TYPE) self.driver.bind_port(context) vif_details = context._bound_vif_details self.assertEqual(1234, vif_details.get(portbindings.VIF_DETAILS_VLAN)) self.assertEqual("bridge", vif_details.get( portbindings.VIF_DETAILS_MACVTAP_MODE)) self.assertEqual("fake_if", vif_details.get( portbindings.VIF_DETAILS_PHYSICAL_INTERFACE)) self.assertEqual("fake_if.1234", vif_details.get( portbindings.VIF_DETAILS_MACVTAP_SOURCE)) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/macvtap/mech_driver/__init__.py0000664000175000017500000000000013553660046031005 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/test_type_gre.py0000664000175000017500000000461013553660047026215 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as p_const from neutron.plugins.ml2.drivers import type_gre from neutron.tests.unit.plugins.ml2.drivers import base_type_tunnel from neutron.tests.unit.plugins.ml2 import test_rpc from neutron.tests.unit import testlib_api TUNNEL_IP_ONE = "10.10.10.10" TUNNEL_IP_TWO = "10.10.10.20" HOST_ONE = 'fake_host_one' HOST_TWO = 'fake_host_two' class GreTypeTest(base_type_tunnel.TunnelTypeTestMixin, testlib_api.SqlTestCase): DRIVER_MODULE = type_gre DRIVER_CLASS = type_gre.GreTypeDriver TYPE = p_const.TYPE_GRE def test_get_endpoints(self): self.add_endpoint() self.add_endpoint( base_type_tunnel.TUNNEL_IP_TWO, base_type_tunnel.HOST_TWO) endpoints = self.driver.get_endpoints() for endpoint in endpoints: if endpoint['ip_address'] == base_type_tunnel.TUNNEL_IP_ONE: self.assertEqual(base_type_tunnel.HOST_ONE, endpoint['host']) elif endpoint['ip_address'] == base_type_tunnel.TUNNEL_IP_TWO: self.assertEqual(base_type_tunnel.HOST_TWO, endpoint['host']) class GreTypeMultiRangeTest(base_type_tunnel.TunnelTypeMultiRangeTestMixin, testlib_api.SqlTestCase): DRIVER_CLASS = type_gre.GreTypeDriver class GreTypeRpcCallbackTest(base_type_tunnel.TunnelRpcCallbackTestMixin, test_rpc.RpcCallbacksTestCase, testlib_api.SqlTestCase): DRIVER_CLASS = type_gre.GreTypeDriver TYPE = p_const.TYPE_GRE class GreTypeTunnelMTUTest(base_type_tunnel.TunnelTypeMTUTestMixin, testlib_api.SqlTestCase): DRIVER_CLASS = type_gre.GreTypeDriver TYPE = p_const.TYPE_GRE ENCAP_OVERHEAD = p_const.GRE_ENCAP_OVERHEAD neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/0000775000175000017500000000000013553660157025340 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/0000775000175000017500000000000013553660157026436 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/fake_oflib.py0000664000175000017500000001200013553660047031060 0ustar zuulzuul00000000000000# Copyright (C) 2014 VA Linux Systems Japan K.K. # Copyright (C) 2014 Fumihiko Kakuma # Copyright (C) 2014 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock class _Eq(object): def __eq__(self, other): return repr(self) == repr(other) def __ne__(self, other): return not self.__eq__(other) class _Value(_Eq): def __or__(self, b): return _Op('|', self, b) def __ror__(self, a): return _Op('|', a, self) class _SimpleValue(_Value): def __init__(self, name): self.name = name def __repr__(self): return self.name class _Op(_Value): def __init__(self, op, a, b): self.op = op self.a = a self.b = b def __repr__(self): return '%s%s%s' % (self.a, self.op, self.b) def _mkcls(name): class Cls(_Eq): _name = name def __init__(self, *args, **kwargs): self._args = args self._kwargs = kwargs self._hist = [] def __getattr__(self, name): return self._kwargs[name] def __repr__(self): args = list(map(repr, self._args)) kwargs = sorted(['%s=%s' % (x, y) for x, y in self._kwargs.items()]) return '%s(%s)' % (self._name, ', '.join(args + kwargs)) return Cls class _Mod(object): _cls_cache = {} def __init__(self, name): self._name = name def __getattr__(self, name): fullname = '%s.%s' % (self._name, name) if '_' in name: # constants are named like OFPxxx_yyy_zzz return _SimpleValue(fullname) try: return self._cls_cache[fullname] except KeyError: pass cls = _mkcls(fullname) self._cls_cache[fullname] = cls return cls def __repr__(self): return 'Mod(%s)' % (self._name,) def patch_fake_oflib_of(): ryu_mod = mock.Mock() ryu_base_mod = ryu_mod.base ryu_exc_mod = ryu_mod.exception ryu_ctrl_mod = ryu_mod.controller handler = _Mod('ryu.controller.handler') handler.set_ev_cls = mock.Mock() ofp_event = _Mod('ryu.controller.ofp_event') ryu_ctrl_mod.handler = handler ryu_ctrl_mod.ofp_event = ofp_event ryu_lib_mod = ryu_mod.lib ryu_lib_hub = ryu_lib_mod.hub ryu_packet_mod = ryu_lib_mod.packet packet = _Mod('ryu.lib.packet.packet') arp = _Mod('ryu.lib.packet.arp') ethernet = _Mod('ryu.lib.packet.ethernet') ether_types = _Mod('ryu.lib.packet.ether_types') in_proto = _Mod('ryu.lib.packet.in_proto') icmpv6 = _Mod('ryu.lib.packet.icmpv6') vlan = _Mod('ryu.lib.packet.vlan') ryu_packet_mod.packet = packet packet.Packet = mock.Mock() ryu_packet_mod.arp = arp ryu_packet_mod.ethernet = ethernet ryu_packet_mod.ether_types = ether_types ryu_packet_mod.icmpv6 = icmpv6 ryu_packet_mod.in_proto = in_proto ryu_packet_mod.vlan = vlan ryu_ofproto_mod = ryu_mod.ofproto ofp = _Mod('ryu.ofproto.ofproto_v1_3') ofpp = _Mod('ryu.ofproto.ofproto_v1_3_parser') ryu_ofproto_mod.ofproto_v1_3 = ofp ryu_ofproto_mod.ofproto_v1_3_parser = ofpp ryu_app_mod = ryu_mod.app ryu_app_ofctl_mod = ryu_app_mod.ofctl ryu_ofctl_api = ryu_app_ofctl_mod.api modules = {'ryu': ryu_mod, 'ryu.base': ryu_base_mod, 'ryu.controller': ryu_ctrl_mod, 'ryu.controller.handler': handler, 'ryu.controller.handler.set_ev_cls': handler.set_ev_cls, 'ryu.controller.ofp_event': ofp_event, 'ryu.exception': ryu_exc_mod, 'ryu.lib': ryu_lib_mod, 'ryu.lib.hub': ryu_lib_hub, 'ryu.lib.packet': ryu_packet_mod, 'ryu.lib.packet.packet': packet, 'ryu.lib.packet.packet.Packet': packet.Packet, 'ryu.lib.packet.arp': arp, 'ryu.lib.packet.ethernet': ethernet, 'ryu.lib.packet.ether_types': ether_types, 'ryu.lib.packet.icmpv6': icmpv6, 'ryu.lib.packet.in_proto': in_proto, 'ryu.lib.packet.vlan': vlan, 'ryu.ofproto': ryu_ofproto_mod, 'ryu.ofproto.ofproto_v1_3': ofp, 'ryu.ofproto.ofproto_v1_3_parser': ofpp, 'ryu.app': ryu_app_mod, 'ryu.app.ofctl': ryu_app_ofctl_mod, 'ryu.app.ofctl.api': ryu_ofctl_api} return mock.patch.dict('sys.modules', modules) ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_agent_extension_api.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_agent_extension_api0000664000175000017500000001704613553660047034340 0ustar zuulzuul00000000000000# Copyright 2012 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import mock from neutron.plugins.ml2.drivers.openvswitch.agent \ import ovs_agent_extension_api as ovs_ext_agt from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \ .openflow.native import ovs_bridge_test_base as native_ovs_bridge_test_base from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \ import ovs_test_base class TestOVSAgentExtensionAPI(ovs_test_base.OVSOFCtlTestBase): def setUp(self): super(TestOVSAgentExtensionAPI, self).setUp() self.br_int = self.br_int_cls("br-int") self.br_tun = self.br_tun_cls("br-tun") def _test_bridge(self, orig_bridge, new_bridge): self.assertIsNotNone(new_bridge) self.assertEqual(orig_bridge.br_name, new_bridge.br_name) self.assertIn(new_bridge._default_cookie, orig_bridge.reserved_cookies) self.assertNotEqual(orig_bridge._default_cookie, new_bridge._default_cookie) def test_request_int_br(self): agent_extension_api = ovs_ext_agt.OVSAgentExtensionAPI(self.br_int, self.br_tun) new_int_br = agent_extension_api.request_int_br() self._test_bridge(self.br_int, new_int_br) def test_request_tun_br(self): agent_extension_api = ovs_ext_agt.OVSAgentExtensionAPI(self.br_int, self.br_tun) new_tun_br = agent_extension_api.request_tun_br() self._test_bridge(self.br_tun, new_tun_br) def test_request_tun_br_tunneling_disabled(self): agent_extension_api = ovs_ext_agt.OVSAgentExtensionAPI(self.br_int, None) self.assertIsNone(agent_extension_api.request_tun_br()) class TestOVSCookieBridgeOFCtl(ovs_test_base.OVSOFCtlTestBase): def setUp(self): super(TestOVSCookieBridgeOFCtl, self).setUp() self.bridge = self.br_int_cls("br-int") mock.patch.object(self.bridge, "run_ofctl").start() self.tested_bridge = ovs_ext_agt.OVSCookieBridge(self.bridge) # mocking do_action_flows does not work, because this method is # later wrapped by the cookie bridge code, and six.wraps apparently # can't wrap a mock, so we mock deeper self.mock_build_flow_expr_str = mock.patch( 'neutron.agent.common.ovs_lib._build_flow_expr_str', return_value="").start() def test_cookie(self): self.assertNotEqual(self.bridge._default_cookie, self.tested_bridge._default_cookie) def test_reserved(self): self.assertIn(self.tested_bridge._default_cookie, self.bridge.reserved_cookies) def assert_mock_build_flow_expr_str_call(self, action, kwargs_list, strict=False): self.mock_build_flow_expr_str.assert_called_once_with( kwargs_list[0], action, strict ) def test_add_flow_without_cookie(self): self.tested_bridge.add_flow(in_port=1, actions="output:2") self.assert_mock_build_flow_expr_str_call( 'add', [{"in_port": 1, "actions": "output:2", "cookie": self.tested_bridge._default_cookie}] ) def test_mod_flow_without_cookie(self): self.tested_bridge.mod_flow(in_port=1, actions="output:2") self.assert_mock_build_flow_expr_str_call( 'mod', [{"in_port": 1, "actions": "output:2", "cookie": self.tested_bridge._default_cookie}] ) def test_del_flows_without_cookie(self): self.tested_bridge.delete_flows(in_port=1) self.assert_mock_build_flow_expr_str_call( 'del', [{"in_port": 1, "cookie": str(self.tested_bridge._default_cookie) + '/-1'}] ) def test_add_flow_with_cookie(self): self.tested_bridge.add_flow(cookie=1234, in_port=1, actions="output:2") self.assert_mock_build_flow_expr_str_call( 'add', [{"in_port": 1, "actions": "output:2", "cookie": 1234}] ) def test_mod_flow_with_cookie(self): self.tested_bridge.mod_flow(cookie='1234', in_port=1, actions="output:2") self.assert_mock_build_flow_expr_str_call( 'mod', [{"in_port": 1, "actions": "output:2", "cookie": "1234"}] ) def test_del_flows_with_cookie(self): self.tested_bridge.delete_flows(cookie=1234, in_port=1) self.assert_mock_build_flow_expr_str_call( 'del', [{"in_port": 1, "cookie": "1234/-1"}] ) def test_mod_flow_with_cookie_mask(self): self.tested_bridge.mod_flow(cookie='1234/3', in_port=1, actions="output:2") self.assert_mock_build_flow_expr_str_call( 'mod', [{"in_port": 1, "actions": "output:2", "cookie": str(1234) + '/3'}] ) def test_del_flows_with_cookie_mask(self): self.tested_bridge.delete_flows(cookie='1234/7', in_port=1) self.assert_mock_build_flow_expr_str_call( 'del', [{"in_port": 1, "cookie": str(1234) + '/7'}] ) def test_install_drop(self): self.tested_bridge.install_drop() self.assert_mock_build_flow_expr_str_call( 'add', [{"table": 0, "priority": 0, "actions": "drop", "cookie": self.tested_bridge._default_cookie}] ) class TestOVSCookieBridgeRyu(native_ovs_bridge_test_base.OVSBridgeTestBase): def setUp(self): super(TestOVSCookieBridgeRyu, self).setUp() self.setup_bridge_mock('br-int', self.br_int_cls) self.tested_bridge = ovs_ext_agt.OVSCookieBridge(self.br) def test_cookie(self): self.assertNotEqual(self.br._default_cookie, self.tested_bridge._default_cookie) def test_reserved(self): self.assertIn(self.tested_bridge._default_cookie, self.br.reserved_cookies) def test_install_drop(self): priority = 99 in_port = 666 self.tested_bridge.install_drop(priority=priority, in_port=in_port) (dp, ofp, ofpp) = self._get_dp() expected = [ mock.call._send_msg( ofpp.OFPFlowMod( dp, # this is the interesting part of the check: cookie=self.tested_bridge._default_cookie, instructions=[], match=ofpp.OFPMatch(in_port=in_port), priority=priority, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/0000775000175000017500000000000013553660157030267 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/test_br_cookie.py0000664000175000017500000000542113553660047033634 0ustar zuulzuul00000000000000# Copyright 2016 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.agent.common import ovs_lib from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import ovs_bridge from neutron.tests import base class TestBRCookieOpenflow(base.BaseTestCase): def setUp(self): super(TestBRCookieOpenflow, self).setUp() conn_patcher = mock.patch( 'neutron.agent.ovsdb.impl_idl._connection') conn_patcher.start() self.addCleanup(conn_patcher.stop) self.br = ovs_bridge.OVSAgentBridge('br-int') def test_reserved_cookies(self): def_cookie = self.br.default_cookie self.assertIn(def_cookie, self.br.reserved_cookies) def test_request_cookie(self): default_cookie = self.br.default_cookie requested_cookie = self.br.request_cookie() self.assertEqual(default_cookie, self.br.default_cookie) self.assertIn(default_cookie, self.br.reserved_cookies) self.assertIn(requested_cookie, self.br.reserved_cookies) def test_unset_cookie(self): requested_cookie = self.br.request_cookie() self.assertIn(requested_cookie, self.br.reserved_cookies) self.br.unset_cookie(requested_cookie) self.assertNotIn(requested_cookie, self.br.reserved_cookies) def test_set_agent_uuid_stamp(self): self.br = ovs_bridge.OVSAgentBridge('br-int') def_cookie = self.br.default_cookie new_cookie = ovs_lib.generate_random_cookie() self.br.set_agent_uuid_stamp(new_cookie) self.assertEqual(new_cookie, self.br.default_cookie) self.assertIn(new_cookie, self.br.reserved_cookies) self.assertNotIn(def_cookie, self.br.reserved_cookies) def test_set_agent_uuid_stamp_with_reserved_cookie(self): self.br = ovs_bridge.OVSAgentBridge('br-int') def_cookie = self.br.default_cookie new_cookie = self.br.request_cookie() self.br.set_agent_uuid_stamp(new_cookie) self.assertEqual(new_cookie, self.br.default_cookie) self.assertIn(new_cookie, self.br.reserved_cookies) self.assertNotIn(def_cookie, self.br.reserved_cookies) self.assertEqual(set([new_cookie]), self.br.reserved_cookies) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/__init__.py0000664000175000017500000000000013553660046032363 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/0000775000175000017500000000000013553660157032265 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016300000000000011215 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ovs_bridge_test_base.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/ovs_bridg0000664000175000017500000001565513553660047034200 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \ import ovs_test_base call = mock.call # short hand class OVSBridgeTestBase(ovs_test_base.OVSOFCtlTestBase): def setup_bridge_mock(self, name, cls): self.br = cls(name) mock_add_flow = mock.patch.object(self.br, 'add_flow').start() mock_mod_flow = mock.patch.object(self.br, 'mod_flow').start() mock_delete_flows = mock.patch.object(self.br, 'delete_flows').start() self.mock = mock.Mock() self.mock.attach_mock(mock_add_flow, 'add_flow') self.mock.attach_mock(mock_mod_flow, 'mod_flow') self.mock.attach_mock(mock_delete_flows, 'delete_flows') def test_drop_port(self): in_port = 2345 self.br.drop_port(in_port=in_port) expected = [ call.add_flow(priority=2, table=0, actions='drop', in_port=in_port), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_goto(self): dest_table_id = 123 priority = 99 in_port = 666 self.br.install_goto(dest_table_id=dest_table_id, priority=priority, in_port=in_port) expected = [ call.add_flow(priority=priority, table=0, actions='resubmit(,%s)' % dest_table_id, in_port=in_port), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_drop(self): priority = 99 in_port = 666 self.br.install_drop(priority=priority, in_port=in_port) expected = [ call.add_flow(priority=priority, table=0, actions='drop', in_port=in_port), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_normal(self): priority = 99 in_port = 666 self.br.install_normal(priority=priority, in_port=in_port) expected = [ call.add_flow(priority=priority, table=0, actions='normal', in_port=in_port), ] self.assertEqual(expected, self.mock.mock_calls) def test_dump_flows_for_table(self): table = 23 with mock.patch.object(self.br, 'run_ofctl') as run_ofctl: self.br.dump_flows(table) run_ofctl.assert_has_calls([mock.call("dump-flows", mock.ANY)]) def test_dump_all_flows(self): with mock.patch.object(self.br, 'run_ofctl') as run_ofctl: self.br.dump_flows_all_tables() run_ofctl.assert_has_calls([mock.call("dump-flows", [])]) class OVSDVRProcessTestMixin(object): def test_install_dvr_process_ipv4(self): vlan_tag = 999 gateway_ip = '192.0.2.1' self.br.install_dvr_process_ipv4(vlan_tag=vlan_tag, gateway_ip=gateway_ip) expected = [ call.add_flow(table=self.dvr_process_table_id, proto='arp', nw_dst=gateway_ip, actions='drop', priority=3, dl_vlan=vlan_tag), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_dvr_process_ipv4(self): vlan_tag = 999 gateway_ip = '192.0.2.1' self.br.delete_dvr_process_ipv4(vlan_tag=vlan_tag, gateway_ip=gateway_ip) expected = [ call.delete_flows(table=self.dvr_process_table_id, dl_vlan=vlan_tag, proto='arp', nw_dst=gateway_ip), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_dvr_process_ipv6(self): vlan_tag = 999 gateway_mac = '08:60:6e:7f:74:e7' self.br.install_dvr_process_ipv6(vlan_tag=vlan_tag, gateway_mac=gateway_mac) expected = [ call.add_flow(table=self.dvr_process_table_id, proto='icmp6', dl_src=gateway_mac, actions='drop', priority=3, dl_vlan=vlan_tag, icmp_type=constants.ICMPV6_TYPE_RA), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_dvr_process_ipv6(self): vlan_tag = 999 gateway_mac = '08:60:6e:7f:74:e7' self.br.delete_dvr_process_ipv6(vlan_tag=vlan_tag, gateway_mac=gateway_mac) expected = [ call.delete_flows(table=self.dvr_process_table_id, dl_vlan=vlan_tag, dl_src=gateway_mac, proto='icmp6', icmp_type=constants.ICMPV6_TYPE_RA), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_dvr_process(self): vlan_tag = 999 vif_mac = '00:0e:0c:5e:95:d0' dvr_mac_address = 'f2:0b:a4:5b:b2:ab' self.br.install_dvr_process(vlan_tag=vlan_tag, vif_mac=vif_mac, dvr_mac_address=dvr_mac_address) expected = [ call.add_flow(priority=2, table=self.dvr_process_table_id, dl_dst=vif_mac, dl_vlan=vlan_tag, actions='drop'), call.add_flow(priority=1, table=self.dvr_process_table_id, dl_vlan=vlan_tag, dl_src=vif_mac, actions='mod_dl_src:%(mac)s,resubmit(,%(next)s)' % { 'mac': dvr_mac_address, 'next': self.dvr_process_next_table_id, }), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_dvr_process(self): vlan_tag = 999 vif_mac = '00:0e:0c:5e:95:d0' self.br.delete_dvr_process(vlan_tag=vlan_tag, vif_mac=vif_mac) expected = [ call.delete_flows(table=self.dvr_process_table_id, dl_dst=vif_mac, dl_vlan=vlan_tag), call.delete_flows(table=self.dvr_process_table_id, dl_vlan=vlan_tag, dl_src=vif_mac), ] self.assertEqual(expected, self.mock.mock_calls) ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/__init__.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/__init__.0000664000175000017500000000000013553660047034011 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_phys.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_p0000664000175000017500000000704113553660047034171 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import neutron.plugins.ml2.drivers.openvswitch.agent.common.constants \ as ovs_const from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent.\ openflow.ovs_ofctl import ovs_bridge_test_base call = mock.call # short hand class OVSPhysicalBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase, ovs_bridge_test_base.OVSDVRProcessTestMixin): dvr_process_table_id = ovs_const.DVR_PROCESS_VLAN dvr_process_next_table_id = ovs_const.LOCAL_VLAN_TRANSLATION def setUp(self): super(OVSPhysicalBridgeTest, self).setUp() self.setup_bridge_mock('br-phys', self.br_phys_cls) def test_setup_default_table(self): self.br.setup_default_table() expected = [ call.add_flow(priority=0, table=0, actions='normal'), ] self.assertEqual(expected, self.mock.mock_calls) def test_provision_local_vlan(self): port = 999 lvid = 888 segmentation_id = 777 distributed = False self.br.provision_local_vlan(port=port, lvid=lvid, segmentation_id=segmentation_id, distributed=distributed) expected = [ call.add_flow(priority=4, table=0, dl_vlan=lvid, in_port=port, actions='mod_vlan_vid:%s,normal' % segmentation_id), ] self.assertEqual(expected, self.mock.mock_calls) def test_provision_local_vlan_novlan(self): port = 999 lvid = 888 segmentation_id = None distributed = False self.br.provision_local_vlan(port=port, lvid=lvid, segmentation_id=segmentation_id, distributed=distributed) expected = [ call.add_flow(priority=4, table=0, dl_vlan=lvid, in_port=port, actions='strip_vlan,normal') ] self.assertEqual(expected, self.mock.mock_calls) def test_reclaim_local_vlan(self): port = 999 lvid = 888 self.br.reclaim_local_vlan(port=port, lvid=lvid) expected = [ call.delete_flows(dl_vlan=lvid, in_port=port), ] self.assertEqual(expected, self.mock.mock_calls) def test_add_dvr_mac_vlan(self): mac = '00:02:b3:13:fe:3d' port = 8888 self.br.add_dvr_mac_vlan(mac=mac, port=port) expected = [ call.add_flow(priority=2, table=3, dl_src=mac, actions='output:%s' % port), ] self.assertEqual(expected, self.mock.mock_calls) def test_remove_dvr_mac_vlan(self): mac = '00:02:b3:13:fe:3d' self.br.remove_dvr_mac_vlan(mac=mac) expected = [ call.delete_flows(dl_src=mac, table=3), ] self.assertEqual(expected, self.mock.mock_calls) ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_tun.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_t0000664000175000017500000003300213553660047034171 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import netaddr import neutron.plugins.ml2.drivers.openvswitch.agent.common.constants \ as ovs_const from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent.\ openflow.ovs_ofctl import ovs_bridge_test_base call = mock.call # short hand class OVSTunnelBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase, ovs_bridge_test_base.OVSDVRProcessTestMixin): dvr_process_table_id = ovs_const.DVR_PROCESS dvr_process_next_table_id = ovs_const.PATCH_LV_TO_TUN def setUp(self): super(OVSTunnelBridgeTest, self).setUp() self.setup_bridge_mock('br-tun', self.br_tun_cls) self.stamp = self.br.default_cookie def test_setup_default_table(self): patch_int_ofport = 5555 mock_do_action_flows = mock.patch.object(self.br, 'do_action_flows').start() self.mock.attach_mock(mock_do_action_flows, 'do_action_flows') self.br.setup_default_table(patch_int_ofport=patch_int_ofport, arp_responder_enabled=False) flow_args = [{'priority': 1, 'in_port': patch_int_ofport, 'actions': 'resubmit(,2)'}, {'priority': 0, 'actions': 'drop'}, {'priority': 0, 'table': 2, 'dl_dst': '00:00:00:00:00:00/01:00:00:00:00:00', 'actions': 'resubmit(,20)'}, {'priority': 0, 'table': 2, 'dl_dst': '01:00:00:00:00:00/01:00:00:00:00:00', 'actions': 'resubmit(,22)'}, {'priority': 0, 'table': 3, 'actions': 'drop'}, {'priority': 0, 'table': 4, 'actions': 'drop'}, {'priority': 0, 'table': 6, 'actions': 'drop'}, {'priority': 1, 'table': 10, 'actions': 'learn(cookie=' + str(self.stamp) + ',table=20,priority=1,hard_timeout=300,' 'NXM_OF_VLAN_TCI[0..11],' 'NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],' 'load:0->NXM_OF_VLAN_TCI[],' 'load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],' 'output:NXM_OF_IN_PORT[]),' 'output:%s' % patch_int_ofport}, {'priority': 0, 'table': 20, 'actions': 'resubmit(,22)'} ] expected = [call.do_action_flows('add', flow_args, False), call.add_flow(priority=0, table=22, actions='drop')] self.assertEqual(expected, self.mock.mock_calls) def test_setup_default_table_arp_responder_enabled(self): patch_int_ofport = 5555 mock_do_action_flows = mock.patch.object(self.br, 'do_action_flows').start() self.mock.attach_mock(mock_do_action_flows, 'do_action_flows') self.br.setup_default_table(patch_int_ofport=patch_int_ofport, arp_responder_enabled=True) flow_args = [{'priority': 1, 'in_port': patch_int_ofport, 'actions': 'resubmit(,2)'}, {'priority': 0, 'actions': 'drop'}, {'priority': 1, 'table': 2, 'dl_dst': 'ff:ff:ff:ff:ff:ff', 'actions': 'resubmit(,21)', 'proto': 'arp'}, {'priority': 0, 'table': 2, 'dl_dst': '00:00:00:00:00:00/01:00:00:00:00:00', 'actions': 'resubmit(,20)'}, {'priority': 0, 'table': 2, 'dl_dst': '01:00:00:00:00:00/01:00:00:00:00:00', 'actions': 'resubmit(,22)'}, {'priority': 0, 'table': 3, 'actions': 'drop'}, {'priority': 0, 'table': 4, 'actions': 'drop'}, {'priority': 0, 'table': 6, 'actions': 'drop'}, {'priority': 1, 'table': 10, 'actions': 'learn(cookie=' + str(self.stamp) + ',table=20,priority=1,hard_timeout=300,' 'NXM_OF_VLAN_TCI[0..11],' 'NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],' 'load:0->NXM_OF_VLAN_TCI[],' 'load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],' 'output:NXM_OF_IN_PORT[]),' 'output:%s' % patch_int_ofport}, {'priority': 0, 'table': 20, 'actions': 'resubmit(,22)'}, {'priority': 0, 'table': 21, 'actions': 'resubmit(,22)'} ] expected = [call.do_action_flows('add', flow_args, False), call.add_flow(priority=0, table=22, actions='drop')] self.assertEqual(expected, self.mock.mock_calls) def test_provision_local_vlan(self): network_type = 'vxlan' lvid = 888 segmentation_id = 777 distributed = False self.br.provision_local_vlan(network_type=network_type, lvid=lvid, segmentation_id=segmentation_id, distributed=distributed) expected = [ call.add_flow(priority=1, tun_id=segmentation_id, actions='mod_vlan_vid:%s,resubmit(,10)' % lvid, table=4), ] self.assertEqual(expected, self.mock.mock_calls) def test_reclaim_local_vlan(self): network_type = 'vxlan' segmentation_id = 777 self.br.reclaim_local_vlan(network_type=network_type, segmentation_id=segmentation_id) expected = [ call.delete_flows(tun_id=segmentation_id, table=4), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_flood_to_tun(self): vlan = 3333 tun_id = 2222 ports = [11, 44, 22, 33] self.br.install_flood_to_tun(vlan=vlan, tun_id=tun_id, ports=ports) expected = [ call.mod_flow(table=22, dl_vlan=vlan, actions='strip_vlan,set_tunnel:%(tun)s,' 'output:%(ports)s' % { 'tun': tun_id, 'ports': ','.join(map(str, ports)), }), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_flood_to_tun(self): vlan = 3333 self.br.delete_flood_to_tun(vlan=vlan) expected = [ call.delete_flows(table=22, dl_vlan=vlan), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_unicast_to_tun(self): vlan = 3333 port = 55 mac = '08:60:6e:7f:74:e7' tun_id = 2222 self.br.install_unicast_to_tun(vlan=vlan, tun_id=tun_id, port=port, mac=mac) expected = [ call.add_flow(priority=2, table=20, dl_dst=mac, dl_vlan=vlan, actions='strip_vlan,set_tunnel:%(tun)s,' 'output:%(port)s' % { 'tun': tun_id, 'port': port, }), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_unicast_to_tun(self): vlan = 3333 mac = '08:60:6e:7f:74:e7' self.br.delete_unicast_to_tun(vlan=vlan, mac=mac) expected = [ call.delete_flows(table=20, dl_dst=mac, dl_vlan=vlan), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_unicast_to_tun_without_mac(self): vlan = 3333 mac = None self.br.delete_unicast_to_tun(vlan=vlan, mac=mac) expected = [ call.delete_flows(table=20, dl_vlan=vlan), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_arp_responder(self): vlan = 3333 ip = '192.0.2.1' mac = '08:60:6e:7f:74:e7' self.br.install_arp_responder(vlan=vlan, ip=ip, mac=mac) expected = [ call.add_flow(proto='arp', nw_dst=ip, actions='move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],' 'mod_dl_src:%(mac)s,load:0x2->NXM_OF_ARP_OP[],' 'move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],' 'move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],' 'load:%(mac)#x->NXM_NX_ARP_SHA[],' 'load:%(ip)#x->NXM_OF_ARP_SPA[],in_port' % { 'mac': netaddr.EUI(mac, dialect=netaddr.mac_unix), 'ip': netaddr.IPAddress(ip), }, priority=1, table=21, dl_vlan=vlan), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_arp_responder(self): vlan = 3333 ip = '192.0.2.1' self.br.delete_arp_responder(vlan=vlan, ip=ip) expected = [ call.delete_flows(table=21, dl_vlan=vlan, proto='arp', nw_dst=ip), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_arp_responder_without_ip(self): vlan = 3333 ip = None self.br.delete_arp_responder(vlan=vlan, ip=ip) expected = [ call.delete_flows(table=21, dl_vlan=vlan, proto='arp'), ] self.assertEqual(expected, self.mock.mock_calls) def test_setup_tunnel_port(self): network_type = 'vxlan' port = 11111 self.br.setup_tunnel_port(network_type=network_type, port=port) expected = [ call.add_flow(priority=1, in_port=port, actions='resubmit(,4)'), ] self.assertEqual(expected, self.mock.mock_calls) def test_cleanup_tunnel_port(self): port = 11111 self.br.cleanup_tunnel_port(port=port) expected = [ call.delete_flows(in_port=port), ] self.assertEqual(expected, self.mock.mock_calls) def test_add_dvr_mac_tun(self): mac = '00:02:b3:13:fe:3d' port = 8888 self.br.add_dvr_mac_tun(mac=mac, port=port) expected = [ call.add_flow(priority=1, table=9, dl_src=mac, actions='output:%s' % port), ] self.assertEqual(expected, self.mock.mock_calls) def test_remove_dvr_mac_tun(self): mac = '00:02:b3:13:fe:3d' self.br.remove_dvr_mac_tun(mac=mac) expected = [ call.delete_flows(dl_src=mac, table=9), ] self.assertEqual(expected, self.mock.mock_calls) def _mock_add_tunnel_port(self, deferred_br=False): port_name = 'fake_port' remote_ip = '192.168.1.3' local_ip = '192.168.1.2' tunnel_type = 'vxlan' vxlan_udp_port = '4789' dont_fragment = True if deferred_br: with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.add_port', return_value=9999) as add_port, \ self.br.deferred() as deferred_br: ofport = deferred_br.add_tunnel_port(port_name, remote_ip, local_ip, tunnel_type, vxlan_udp_port, dont_fragment) else: with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.add_port', return_value=9999) as add_port: ofport = self.br.add_tunnel_port(port_name, remote_ip, local_ip, tunnel_type, vxlan_udp_port, dont_fragment) self.assertEqual(9999, ofport) self.assertEqual(1, add_port.call_count) self.assertEqual(port_name, add_port.call_args[0][0]) def _mock_delete_port(self, deferred_br=False): port_name = 'fake_port' if deferred_br: with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'delete_port') as delete_port, \ self.br.deferred() as deferred_br: deferred_br.delete_port(port_name) else: with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'delete_port') as delete_port: self.br.delete_port(port_name) self.assertEqual([call(port_name)], delete_port.mock_calls) def test_add_tunnel_port(self): self._mock_add_tunnel_port() def test_delete_port(self): self._mock_delete_port() def test_deferred_br_add_tunnel_port(self): self._mock_add_tunnel_port(True) def test_deferred_br_delete_port(self): self._mock_delete_port(True) ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_int.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/ovs_ofctl/test_br_i0000664000175000017500000002503513553660047034165 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as const from neutron.common import constants as n_const from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent.\ openflow.ovs_ofctl import ovs_bridge_test_base call = mock.call # short hand class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase): def setUp(self): super(OVSIntegrationBridgeTest, self).setUp() self.setup_bridge_mock('br-int', self.br_int_cls) def test_setup_default_table(self): self.br.setup_default_table() expected = [ call.add_flow(priority=0, table=23, actions='drop'), call.add_flow(priority=0, table=0, actions='resubmit(,60)'), call.add_flow(priority=3, table=60, actions='normal'), call.add_flow(priority=0, table=24, actions='drop'), call.add_flow(actions='drop', dl_vlan=4095, priority=65535, table=0) ] self.assertEqual(expected, self.mock.mock_calls) def test_provision_local_vlan(self): port = 999 lvid = 888 segmentation_id = 777 self.br.provision_local_vlan(port=port, lvid=lvid, segmentation_id=segmentation_id) expected = [ call.add_flow(priority=3, dl_vlan=segmentation_id, in_port=port, actions='mod_vlan_vid:%s,resubmit(,60)' % lvid), ] self.assertEqual(expected, self.mock.mock_calls) def test_provision_local_vlan_novlan(self): port = 999 lvid = 888 segmentation_id = None self.br.provision_local_vlan(port=port, lvid=lvid, segmentation_id=segmentation_id) expected = [ call.add_flow(priority=3, dl_vlan=0xffff, in_port=port, actions='mod_vlan_vid:%s,resubmit(,60)' % lvid), ] self.assertEqual(expected, self.mock.mock_calls) def test_reclaim_local_vlan(self): port = 999 segmentation_id = 777 self.br.reclaim_local_vlan(port=port, segmentation_id=segmentation_id) expected = [ call.delete_flows(dl_vlan=segmentation_id, in_port=port), ] self.assertEqual(expected, self.mock.mock_calls) def test_reclaim_local_vlan_novlan(self): port = 999 segmentation_id = None self.br.reclaim_local_vlan(port=port, segmentation_id=segmentation_id) expected = [ call.delete_flows(dl_vlan=0xffff, in_port=port), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_dvr_to_src_mac(self): network_type = 'vxlan' vlan_tag = 1111 gateway_mac = '08:60:6e:7f:74:e7' dst_mac = '00:02:b3:13:fe:3d' dst_port = 6666 self.br.install_dvr_to_src_mac(network_type=network_type, vlan_tag=vlan_tag, gateway_mac=gateway_mac, dst_mac=dst_mac, dst_port=dst_port) expected = [ call.add_flow(priority=4, table=1, dl_dst=dst_mac, dl_vlan=vlan_tag, actions='mod_dl_src:%(mac)s,resubmit(,60)' % { 'mac': gateway_mac, }), call.add_flow(priority=4, table=60, dl_dst=dst_mac, dl_vlan=vlan_tag, actions='strip_vlan,output:%(port)s' % { 'port': dst_port, }), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_dvr_to_src_mac(self): network_type = 'vxlan' vlan_tag = 1111 dst_mac = '00:02:b3:13:fe:3d' self.br.delete_dvr_to_src_mac(network_type=network_type, vlan_tag=vlan_tag, dst_mac=dst_mac) expected = [ call.delete_flows( strict=True, priority=4, table=1, dl_dst=dst_mac, dl_vlan=vlan_tag), call.delete_flows( strict=True, priority=4, table=60, dl_dst=dst_mac, dl_vlan=vlan_tag), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_dvr_to_src_mac_vlan(self): network_type = 'vlan' vlan_tag = 1111 gateway_mac = '08:60:6e:7f:74:e7' dst_mac = '00:02:b3:13:fe:3d' dst_port = 6666 self.br.install_dvr_to_src_mac(network_type=network_type, vlan_tag=vlan_tag, gateway_mac=gateway_mac, dst_mac=dst_mac, dst_port=dst_port) expected = [ call.add_flow(priority=4, table=2, dl_dst=dst_mac, dl_vlan=vlan_tag, actions='mod_dl_src:%(mac)s,resubmit(,60)' % { 'mac': gateway_mac, }), call.add_flow(priority=4, table=60, dl_dst=dst_mac, dl_vlan=vlan_tag, actions='strip_vlan,output:%(port)s' % { 'port': dst_port, }), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_dvr_to_src_mac_vlan(self): network_type = 'vlan' vlan_tag = 1111 dst_mac = '00:02:b3:13:fe:3d' self.br.delete_dvr_to_src_mac(network_type=network_type, vlan_tag=vlan_tag, dst_mac=dst_mac) expected = [ call.delete_flows( strict=True, priority=4, table=2, dl_dst=dst_mac, dl_vlan=vlan_tag), call.delete_flows( strict=True, priority=4, table=60, dl_dst=dst_mac, dl_vlan=vlan_tag), ] self.assertEqual(expected, self.mock.mock_calls) def test_add_dvr_mac_vlan(self): mac = '00:02:b3:13:fe:3d' port = 8888 self.br.add_dvr_mac_vlan(mac=mac, port=port) expected = [ call.add_flow(priority=4, table=0, actions='resubmit(,2)', dl_src=mac, in_port=port), ] self.assertEqual(expected, self.mock.mock_calls) def test_remove_dvr_mac_vlan(self): mac = '00:02:b3:13:fe:3d' self.br.remove_dvr_mac_vlan(mac=mac) expected = [ call.delete_flows(dl_src=mac, table=0), ] self.assertEqual(expected, self.mock.mock_calls) def test_add_dvr_mac_tun(self): mac = '00:02:b3:13:fe:3d' port = 8888 self.br.add_dvr_mac_tun(mac=mac, port=port) expected = [ call.add_flow(priority=2, table=0, actions='resubmit(,1)', dl_src=mac, in_port=port), ] self.assertEqual(expected, self.mock.mock_calls) def test_remove_dvr_mac_tun(self): mac = '00:02:b3:13:fe:3d' port = 8888 self.br.remove_dvr_mac_tun(mac=mac, port=port) expected = [ call.delete_flows(dl_src=mac, table=0, in_port=port), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_icmpv6_na_spoofing_protection(self): port = 8888 ip_addresses = ['2001:db8::1', 'fdf8:f53b:82e4::1/128'] self.br.install_icmpv6_na_spoofing_protection(port, ip_addresses) expected = [ call.add_flow(dl_type=n_const.ETHERTYPE_IPV6, actions='resubmit(,60)', icmp_type=const.ICMPV6_TYPE_NA, nw_proto=const.PROTO_NUM_IPV6_ICMP, nd_target='2001:db8::1', priority=2, table=24, in_port=8888), call.add_flow(dl_type=n_const.ETHERTYPE_IPV6, actions='resubmit(,60)', icmp_type=const.ICMPV6_TYPE_NA, nw_proto=const.PROTO_NUM_IPV6_ICMP, nd_target='fdf8:f53b:82e4::1/128', priority=2, table=24, in_port=8888), call.add_flow(dl_type=n_const.ETHERTYPE_IPV6, icmp_type=const.ICMPV6_TYPE_NA, nw_proto=const.PROTO_NUM_IPV6_ICMP, priority=10, table=0, in_port=8888, actions='resubmit(,24)') ] self.assertEqual(expected, self.mock.mock_calls) def test_install_arp_spoofing_protection(self): port = 8888 ip_addresses = ['192.0.2.1', '192.0.2.2/32'] self.br.install_arp_spoofing_protection(port, ip_addresses) expected = [ call.add_flow(proto='arp', actions='resubmit(,25)', arp_spa='192.0.2.1', priority=2, table=24, in_port=8888), call.add_flow(proto='arp', actions='resubmit(,25)', arp_spa='192.0.2.2/32', priority=2, table=24, in_port=8888), call.add_flow(priority=10, table=0, in_port=8888, actions='resubmit(,24)', proto='arp') ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_arp_spoofing_protection(self): port = 8888 self.br.delete_arp_spoofing_protection(port) expected = [ call.delete_flows(table=0, in_port=8888, proto='arp'), call.delete_flows(table=0, in_port=8888, icmp_type=const.ICMPV6_TYPE_NA, nw_proto=const.PROTO_NUM_IPV6_ICMP), call.delete_flows(table=24, in_port=8888), ] self.assertEqual(expected, self.mock.mock_calls) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/0000775000175000017500000000000013553660157031555 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016000000000000011212 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/ovs_bridge_test_base.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/ovs_bridge_t0000664000175000017500000002552213553660047034152 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_utils import importutils from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \ import ovs_test_base call = mock.call # short hand class OVSBridgeTestBase(ovs_test_base.OVSRyuTestBase): _ARP_MODULE = 'ryu.lib.packet.arp' _ETHER_TYPES_MODULE = 'ryu.lib.packet.ether_types' _ICMPV6_MODULE = 'ryu.lib.packet.icmpv6' _IN_PROTO_MODULE = 'ryu.lib.packet.in_proto' _OFP_MODULE = 'ryu.ofproto.ofproto_v1_3' _OFPP_MODULE = 'ryu.ofproto.ofproto_v1_3_parser' def setup_bridge_mock(self, name, cls): self.br = cls(name) self.stamp = self.br.default_cookie self.dp = mock.Mock() self.ofp = importutils.import_module(self._OFP_MODULE) self.ofpp = importutils.import_module(self._OFPP_MODULE) self.arp = importutils.import_module(self._ARP_MODULE) self.ether_types = importutils.import_module(self._ETHER_TYPES_MODULE) self.icmpv6 = importutils.import_module(self._ICMPV6_MODULE) self.in_proto = importutils.import_module(self._IN_PROTO_MODULE) mock.patch.object(self.br, '_get_dp', autospec=True, return_value=self._get_dp()).start() mock__send_msg = mock.patch.object(self.br, '_send_msg').start() mock_delete_flows = mock.patch.object(self.br, 'uninstall_flows').start() self.mock = mock.Mock() self.mock.attach_mock(mock__send_msg, '_send_msg') self.mock.attach_mock(mock_delete_flows, 'uninstall_flows') def _get_dp(self): return self.dp, self.ofp, self.ofpp def test_drop_port(self): in_port = 2345 self.br.drop_port(in_port=in_port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg( ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(in_port=in_port), priority=2, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_goto(self): dest_table_id = 123 priority = 99 in_port = 666 self.br.install_goto(dest_table_id=dest_table_id, priority=priority, in_port=in_port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg( ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionGotoTable(table_id=dest_table_id), ], match=ofpp.OFPMatch(in_port=in_port), priority=priority, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_drop(self): priority = 99 in_port = 666 self.br.install_drop(priority=priority, in_port=in_port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg( ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(in_port=in_port), priority=priority, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_normal(self): priority = 99 in_port = 666 self.br.install_normal(priority=priority, in_port=in_port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg( ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0) ]), ], match=ofpp.OFPMatch(in_port=in_port), priority=priority, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test__cidr_to_ryu(self): f = self.br._cidr_to_ryu self.assertEqual('192.168.0.1', f('192.168.0.1')) self.assertEqual('192.168.0.1', f('192.168.0.1/32')) self.assertEqual(('192.168.0.0', '255.255.255.0'), f('192.168.0.0/24')) def test__setup_controllers__out_of_band(self): cfg = mock.MagicMock() cfg.OVS.of_listen_address = "" cfg.OVS.of_listen_port = "" m_add_protocols = mock.patch.object(self.br, 'add_protocols') m_set_controller = mock.patch.object(self.br, 'set_controller') m_set_probe = mock.patch.object(self.br, 'set_controllers_inactivity_probe') m_set_ccm = mock.patch.object(self.br, 'set_controllers_connection_mode') with m_set_ccm as set_ccm: with m_set_controller, m_add_protocols, m_set_probe: self.br.setup_controllers(cfg) set_ccm.assert_called_once_with("out-of-band") class OVSDVRProcessTestMixin(object): def test_install_dvr_process_ipv4(self): vlan_tag = 999 gateway_ip = '192.0.2.1' self.br.install_dvr_process_ipv4(vlan_tag=vlan_tag, gateway_ip=gateway_ip) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_ARP, arp_tpa=gateway_ip, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT), priority=3, table_id=self.dvr_process_table_id)), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_dvr_process_ipv4(self): vlan_tag = 999 gateway_ip = '192.0.2.1' self.br.delete_dvr_process_ipv4(vlan_tag=vlan_tag, gateway_ip=gateway_ip) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows(table_id=self.dvr_process_table_id, match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_ARP, arp_tpa=gateway_ip, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT)), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_dvr_process_ipv6(self): vlan_tag = 999 gateway_mac = '08:60:6e:7f:74:e7' self.br.install_dvr_process_ipv6(vlan_tag=vlan_tag, gateway_mac=gateway_mac) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch( eth_src=gateway_mac, eth_type=self.ether_types.ETH_TYPE_IPV6, icmpv6_type=self.icmpv6.ND_ROUTER_ADVERT, ip_proto=self.in_proto.IPPROTO_ICMPV6, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT), priority=3, table_id=self.dvr_process_table_id)), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_dvr_process_ipv6(self): vlan_tag = 999 gateway_mac = '08:60:6e:7f:74:e7' self.br.delete_dvr_process_ipv6(vlan_tag=vlan_tag, gateway_mac=gateway_mac) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows(table_id=self.dvr_process_table_id, match=ofpp.OFPMatch( eth_src=gateway_mac, eth_type=self.ether_types.ETH_TYPE_IPV6, icmpv6_type=self.icmpv6.ND_ROUTER_ADVERT, ip_proto=self.in_proto.IPPROTO_ICMPV6, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT)), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_dvr_process(self): vlan_tag = 999 vif_mac = '00:0e:0c:5e:95:d0' dvr_mac_address = 'f2:0b:a4:5b:b2:ab' self.br.install_dvr_process(vlan_tag=vlan_tag, vif_mac=vif_mac, dvr_mac_address=dvr_mac_address) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch( eth_dst=vif_mac, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT), priority=2, table_id=self.dvr_process_table_id)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionSetField(eth_src=dvr_mac_address), ]), ofpp.OFPInstructionGotoTable( table_id=self.dvr_process_next_table_id), ], match=ofpp.OFPMatch( eth_src=vif_mac, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT), priority=1, table_id=self.dvr_process_table_id)), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_dvr_process(self): vlan_tag = 999 vif_mac = '00:0e:0c:5e:95:d0' self.br.delete_dvr_process(vlan_tag=vlan_tag, vif_mac=vif_mac) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows(table_id=self.dvr_process_table_id, match=ofpp.OFPMatch( eth_dst=vif_mac, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT)), call.uninstall_flows(table_id=self.dvr_process_table_id, match=ofpp.OFPMatch( eth_src=vif_mac, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT)), ] self.assertEqual(expected, self.mock.mock_calls) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/__init__.py0000664000175000017500000000000013553660046033651 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys0000664000175000017500000001300513553660047034202 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import neutron.plugins.ml2.drivers.openvswitch.agent.common.constants \ as ovs_const from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import ovs_bridge_test_base call = mock.call # short hand class OVSPhysicalBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase, ovs_bridge_test_base.OVSDVRProcessTestMixin): dvr_process_table_id = ovs_const.DVR_PROCESS_VLAN dvr_process_next_table_id = ovs_const.LOCAL_VLAN_TRANSLATION def setUp(self): conn_patcher = mock.patch( 'neutron.agent.ovsdb.impl_idl._connection') conn_patcher.start() super(OVSPhysicalBridgeTest, self).setUp() self.addCleanup(conn_patcher.stop) self.setup_bridge_mock('br-phys', self.br_phys_cls) self.stamp = self.br.default_cookie def test_setup_default_table(self): self.br.setup_default_table() (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0), ]), ], match=ofpp.OFPMatch(), priority=0, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test_provision_local_vlan(self): port = 999 lvid = 888 segmentation_id = 777 distributed = False self.br.provision_local_vlan(port=port, lvid=lvid, segmentation_id=segmentation_id, distributed=distributed) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionSetField( vlan_vid=segmentation_id | ofp.OFPVID_PRESENT), ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0), ]), ], match=ofpp.OFPMatch( in_port=port, vlan_vid=lvid | ofp.OFPVID_PRESENT), priority=4, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test_provision_local_vlan_novlan(self): port = 999 lvid = 888 segmentation_id = None distributed = False self.br.provision_local_vlan(port=port, lvid=lvid, segmentation_id=segmentation_id, distributed=distributed) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionPopVlan(), ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0), ]), ], match=ofpp.OFPMatch( in_port=port, vlan_vid=lvid | ofp.OFPVID_PRESENT), priority=4, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test_reclaim_local_vlan(self): port = 999 lvid = 888 self.br.reclaim_local_vlan(port=port, lvid=lvid) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows( match=ofpp.OFPMatch( in_port=port, vlan_vid=lvid | ofp.OFPVID_PRESENT)), ] self.assertEqual(expected, self.mock.mock_calls) def test_add_dvr_mac_vlan(self): mac = '00:02:b3:13:fe:3d' port = 8888 self.br.add_dvr_mac_vlan(mac=mac, port=port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionOutput(port, 0), ]), ], match=ofpp.OFPMatch(eth_src=mac), priority=2, table_id=3)), ] self.assertEqual(expected, self.mock.mock_calls) def test_remove_dvr_mac_vlan(self): mac = '00:02:b3:13:fe:3d' self.br.remove_dvr_mac_vlan(mac=mac) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows(eth_src=mac, table_id=3), ] self.assertEqual(expected, self.mock.mock_calls) ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_tun.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_tun.0000664000175000017500000005006213553660047034107 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import neutron.plugins.ml2.drivers.openvswitch.agent.common.constants \ as ovs_const from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import ovs_bridge_test_base call = mock.call # short hand class OVSTunnelBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase, ovs_bridge_test_base.OVSDVRProcessTestMixin): dvr_process_table_id = ovs_const.DVR_PROCESS dvr_process_next_table_id = ovs_const.PATCH_LV_TO_TUN def setUp(self): conn_patcher = mock.patch( 'neutron.agent.ovsdb.impl_idl._connection') conn_patcher.start() super(OVSTunnelBridgeTest, self).setUp() # NOTE(ivasilevskaya) The behaviour of oslotest.base.addCleanup() # according to https://review.openstack.org/#/c/119201/4 guarantees # that all started mocks will be stopped even without direct call to # patcher.stop(). # If any individual mocks should be stopped by other than default # mechanism, their cleanup has to be added after # oslotest.BaseTestCase.setUp() not to be included in the stopall set # that will be cleaned up by mock.patch.stopall. This way the mock # won't be attempted to be stopped twice. self.addCleanup(conn_patcher.stop) self.setup_bridge_mock('br-tun', self.br_tun_cls) self.stamp = self.br.default_cookie def test_setup_default_table(self): patch_int_ofport = 5555 arp_responder_enabled = False self.br.setup_default_table(patch_int_ofport=patch_int_ofport, arp_responder_enabled=arp_responder_enabled) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ofpp.OFPInstructionGotoTable(table_id=2)], match=ofpp.OFPMatch(in_port=patch_int_ofport), priority=1, table_id=0)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(), priority=0, table_id=0)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ofpp.OFPInstructionGotoTable(table_id=20)], match=ofpp.OFPMatch( eth_dst=('00:00:00:00:00:00', '01:00:00:00:00:00')), priority=0, table_id=2)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ofpp.OFPInstructionGotoTable(table_id=22)], match=ofpp.OFPMatch( eth_dst=('01:00:00:00:00:00', '01:00:00:00:00:00')), priority=0, table_id=2)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(), priority=0, table_id=3)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(), priority=0, table_id=4)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(), priority=0, table_id=6)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.NXActionLearn( cookie=self.stamp, hard_timeout=300, priority=1, specs=[ ofpp.NXFlowSpecMatch( dst=('vlan_tci', 0), n_bits=12, src=('vlan_tci', 0)), ofpp.NXFlowSpecMatch( dst=('eth_dst', 0), n_bits=48, src=('eth_src', 0)), ofpp.NXFlowSpecLoad( dst=('vlan_tci', 0), n_bits=16, src=0), ofpp.NXFlowSpecLoad( dst=('tunnel_id', 0), n_bits=64, src=('tunnel_id', 0)), ofpp.NXFlowSpecOutput( dst='', n_bits=32, src=('in_port', 0)), ], table_id=20), ofpp.OFPActionOutput(patch_int_ofport, 0), ]), ], match=ofpp.OFPMatch(), priority=1, table_id=10)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ofpp.OFPInstructionGotoTable(table_id=22)], match=ofpp.OFPMatch(), priority=0, table_id=20)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(), priority=0, table_id=22)) ] self.assertEqual(expected, self.mock.mock_calls) def test_setup_default_table_arp_responder_enabled(self): patch_int_ofport = 5555 arp_responder_enabled = True self.br.setup_default_table(patch_int_ofport=patch_int_ofport, arp_responder_enabled=arp_responder_enabled) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ofpp.OFPInstructionGotoTable(table_id=2)], match=ofpp.OFPMatch(in_port=patch_int_ofport), priority=1, table_id=0)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(), priority=0, table_id=0)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ofpp.OFPInstructionGotoTable(table_id=21)], match=ofpp.OFPMatch( eth_dst='ff:ff:ff:ff:ff:ff', eth_type=self.ether_types.ETH_TYPE_ARP), priority=1, table_id=2)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ofpp.OFPInstructionGotoTable(table_id=20)], match=ofpp.OFPMatch( eth_dst=('00:00:00:00:00:00', '01:00:00:00:00:00')), priority=0, table_id=2)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ofpp.OFPInstructionGotoTable(table_id=22)], match=ofpp.OFPMatch( eth_dst=('01:00:00:00:00:00', '01:00:00:00:00:00')), priority=0, table_id=2)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(), priority=0, table_id=3)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(), priority=0, table_id=4)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(), priority=0, table_id=6)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.NXActionLearn( cookie=self.stamp, hard_timeout=300, priority=1, specs=[ ofpp.NXFlowSpecMatch( dst=('vlan_tci', 0), n_bits=12, src=('vlan_tci', 0)), ofpp.NXFlowSpecMatch( dst=('eth_dst', 0), n_bits=48, src=('eth_src', 0)), ofpp.NXFlowSpecLoad( dst=('vlan_tci', 0), n_bits=16, src=0), ofpp.NXFlowSpecLoad( dst=('tunnel_id', 0), n_bits=64, src=('tunnel_id', 0)), ofpp.NXFlowSpecOutput( dst='', n_bits=32, src=('in_port', 0)), ], table_id=20), ofpp.OFPActionOutput(patch_int_ofport, 0), ]), ], match=ofpp.OFPMatch(), priority=1, table_id=10)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ofpp.OFPInstructionGotoTable(table_id=22)], match=ofpp.OFPMatch(), priority=0, table_id=20)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ofpp.OFPInstructionGotoTable(table_id=22)], match=ofpp.OFPMatch(), priority=0, table_id=21)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(), priority=0, table_id=22)) ] self.assertEqual(expected, self.mock.mock_calls) def test_provision_local_vlan(self): network_type = 'vxlan' lvid = 888 segmentation_id = 777 distributed = False self.br.provision_local_vlan(network_type=network_type, lvid=lvid, segmentation_id=segmentation_id, distributed=distributed) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionPushVlan(), ofpp.OFPActionSetField( vlan_vid=lvid | ofp.OFPVID_PRESENT) ]), ofpp.OFPInstructionGotoTable(table_id=10), ], match=ofpp.OFPMatch(tunnel_id=segmentation_id), priority=1, table_id=4)), ] self.assertEqual(expected, self.mock.mock_calls) def test_reclaim_local_vlan(self): network_type = 'vxlan' segmentation_id = 777 self.br.reclaim_local_vlan(network_type=network_type, segmentation_id=segmentation_id) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows( table_id=4, match=ofpp.OFPMatch(tunnel_id=segmentation_id)), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_flood_to_tun(self): vlan = 3333 tun_id = 2222 ports = [11, 44, 22, 33] self.br.install_flood_to_tun(vlan=vlan, tun_id=tun_id, ports=ports) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionPopVlan(), ofpp.OFPActionSetField(tunnel_id=tun_id), ] + [ofpp.OFPActionOutput(p, 0) for p in ports]), ], match=ofpp.OFPMatch(vlan_vid=vlan | ofp.OFPVID_PRESENT), priority=1, table_id=22)), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_flood_to_tun(self): vlan = 3333 self.br.delete_flood_to_tun(vlan=vlan) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows(table_id=22, match=ofpp.OFPMatch(vlan_vid=vlan | ofp.OFPVID_PRESENT)), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_unicast_to_tun(self): vlan = 3333 port = 55 mac = '08:60:6e:7f:74:e7' tun_id = 2222 self.br.install_unicast_to_tun(vlan=vlan, tun_id=tun_id, port=port, mac=mac) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionPopVlan(), ofpp.OFPActionSetField(tunnel_id=tun_id), ofpp.OFPActionOutput(port, 0), ]), ], match=ofpp.OFPMatch( eth_dst=mac, vlan_vid=vlan | ofp.OFPVID_PRESENT), priority=2, table_id=20)), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_unicast_to_tun(self): vlan = 3333 mac = '08:60:6e:7f:74:e7' self.br.delete_unicast_to_tun(vlan=vlan, mac=mac) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows(table_id=20, match=ofpp.OFPMatch( eth_dst=mac, vlan_vid=vlan | ofp.OFPVID_PRESENT)), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_unicast_to_tun_without_mac(self): vlan = 3333 mac = None self.br.delete_unicast_to_tun(vlan=vlan, mac=mac) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows(table_id=20, match=ofpp.OFPMatch(vlan_vid=vlan | ofp.OFPVID_PRESENT)), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_arp_responder(self): vlan = 3333 ip = '192.0.2.1' mac = '08:60:6e:7f:74:e7' self.br.install_arp_responder(vlan=vlan, ip=ip, mac=mac) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionSetField(arp_op=self.arp.ARP_REPLY), ofpp.NXActionRegMove( dst_field='arp_tha', n_bits=48, src_field='arp_sha'), ofpp.NXActionRegMove( dst_field='arp_tpa', n_bits=32, src_field='arp_spa'), ofpp.OFPActionSetField(arp_sha=mac), ofpp.OFPActionSetField(arp_spa=ip), ofpp.NXActionRegMove(src_field='eth_src', dst_field='eth_dst', n_bits=48), ofpp.OFPActionSetField(eth_src=mac), ofpp.OFPActionOutput(ofp.OFPP_IN_PORT, 0), ]), ], match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_ARP, arp_tpa=ip, vlan_vid=vlan | ofp.OFPVID_PRESENT), priority=1, table_id=21)), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_arp_responder(self): vlan = 3333 ip = '192.0.2.1' self.br.delete_arp_responder(vlan=vlan, ip=ip) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows( match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_ARP, arp_tpa=ip, vlan_vid=vlan | ofp.OFPVID_PRESENT), table_id=21), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_arp_responder_without_ip(self): vlan = 3333 ip = None self.br.delete_arp_responder(vlan=vlan, ip=ip) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows( match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_ARP, vlan_vid=vlan | ofp.OFPVID_PRESENT), table_id=21), ] self.assertEqual(expected, self.mock.mock_calls) def test_setup_tunnel_port(self): network_type = 'vxlan' port = 11111 self.br.setup_tunnel_port(network_type=network_type, port=port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionGotoTable(table_id=4), ], match=ofpp.OFPMatch(in_port=port), priority=1, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test_cleanup_tunnel_port(self): port = 11111 self.br.cleanup_tunnel_port(port=port) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows(in_port=port), ] self.assertEqual(expected, self.mock.mock_calls) def test_add_dvr_mac_tun(self): mac = '00:02:b3:13:fe:3d' port = 8888 self.br.add_dvr_mac_tun(mac=mac, port=port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionOutput(port, 0), ]), ], match=ofpp.OFPMatch(eth_src=mac), priority=1, table_id=9)), ] self.assertEqual(expected, self.mock.mock_calls) def test_remove_dvr_mac_tun(self): mac = '00:02:b3:13:fe:3d' self.br.remove_dvr_mac_tun(mac=mac) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows(eth_src=mac, table_id=9), ] self.assertEqual(expected, self.mock.mock_calls) ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.0000664000175000017500000004153013553660047034073 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import ovs_bridge_test_base call = mock.call # short hand class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase): def setUp(self): super(OVSIntegrationBridgeTest, self).setUp() self.setup_bridge_mock('br-int', self.br_int_cls) self.stamp = self.br.default_cookie def test_setup_default_table(self): self.br.setup_default_table() (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(), priority=0, table_id=23)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionGotoTable(table_id=60), ], match=ofpp.OFPMatch(), priority=0, table_id=0)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions( ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0) ]), ], match=ofpp.OFPMatch(), priority=3, table_id=60)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(), priority=0, table_id=24)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[], match=ofpp.OFPMatch(vlan_vid=4095), priority=65535, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test_provision_local_vlan(self): port = 999 lvid = 888 segmentation_id = 777 self.br.provision_local_vlan(port=port, lvid=lvid, segmentation_id=segmentation_id) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionSetField( vlan_vid=lvid | ofp.OFPVID_PRESENT), ]), ofpp.OFPInstructionGotoTable(table_id=60), ], match=ofpp.OFPMatch( in_port=port, vlan_vid=segmentation_id | ofp.OFPVID_PRESENT), priority=3, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test_provision_local_vlan_novlan(self): port = 999 lvid = 888 segmentation_id = None self.br.provision_local_vlan(port=port, lvid=lvid, segmentation_id=segmentation_id) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionPushVlan(), ofpp.OFPActionSetField( vlan_vid=lvid | ofp.OFPVID_PRESENT), ]), ofpp.OFPInstructionGotoTable(table_id=60), ], match=ofpp.OFPMatch( in_port=port, vlan_vid=ofp.OFPVID_NONE), priority=3, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test_reclaim_local_vlan(self): port = 999 segmentation_id = 777 self.br.reclaim_local_vlan(port=port, segmentation_id=segmentation_id) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows( match=ofpp.OFPMatch( in_port=port, vlan_vid=segmentation_id | ofp.OFPVID_PRESENT)), ] self.assertEqual(expected, self.mock.mock_calls) def test_reclaim_local_vlan_novlan(self): port = 999 segmentation_id = None self.br.reclaim_local_vlan(port=port, segmentation_id=segmentation_id) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows( match=ofpp.OFPMatch( in_port=port, vlan_vid=ofp.OFPVID_NONE)), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_dvr_to_src_mac(self): network_type = 'vxlan' vlan_tag = 1111 gateway_mac = '08:60:6e:7f:74:e7' dst_mac = '00:02:b3:13:fe:3d' dst_port = 6666 self.br.install_dvr_to_src_mac(network_type=network_type, vlan_tag=vlan_tag, gateway_mac=gateway_mac, dst_mac=dst_mac, dst_port=dst_port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionSetField(eth_src=gateway_mac), ]), ofpp.OFPInstructionGotoTable(table_id=60), ], match=ofpp.OFPMatch( eth_dst=dst_mac, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT), priority=4, table_id=1)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionPopVlan(), ofpp.OFPActionOutput(6666, 0), ]), ], match=ofpp.OFPMatch( eth_dst=dst_mac, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT), priority=4, table_id=60)), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_dvr_to_src_mac(self): network_type = 'vxlan' vlan_tag = 1111 dst_mac = '00:02:b3:13:fe:3d' self.br.delete_dvr_to_src_mac(network_type=network_type, vlan_tag=vlan_tag, dst_mac=dst_mac) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows( strict=True, priority=4, table_id=1, match=ofpp.OFPMatch( eth_dst=dst_mac, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT)), call.uninstall_flows( strict=True, priority=4, table_id=60, match=ofpp.OFPMatch( eth_dst=dst_mac, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT)), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_dvr_to_src_mac_vlan(self): network_type = 'vlan' vlan_tag = 1111 gateway_mac = '08:60:6e:7f:74:e7' dst_mac = '00:02:b3:13:fe:3d' dst_port = 6666 self.br.install_dvr_to_src_mac(network_type=network_type, vlan_tag=vlan_tag, gateway_mac=gateway_mac, dst_mac=dst_mac, dst_port=dst_port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionSetField(eth_src=gateway_mac), ]), ofpp.OFPInstructionGotoTable(table_id=60), ], match=ofpp.OFPMatch( eth_dst=dst_mac, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT), priority=4, table_id=2)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [ ofpp.OFPActionPopVlan(), ofpp.OFPActionOutput(dst_port, 0), ]), ], match=ofpp.OFPMatch( eth_dst=dst_mac, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT), priority=4, table_id=60)), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_dvr_to_src_mac_vlan(self): network_type = 'vlan' vlan_tag = 1111 dst_mac = '00:02:b3:13:fe:3d' self.br.delete_dvr_to_src_mac(network_type=network_type, vlan_tag=vlan_tag, dst_mac=dst_mac) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows( strict=True, priority=4, table_id=2, match=ofpp.OFPMatch( eth_dst=dst_mac, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT)), call.uninstall_flows( strict=True, priority=4, table_id=60, match=ofpp.OFPMatch( eth_dst=dst_mac, vlan_vid=vlan_tag | ofp.OFPVID_PRESENT)), ] self.assertEqual(expected, self.mock.mock_calls) def test_add_dvr_mac_vlan(self): mac = '00:02:b3:13:fe:3d' port = 8888 self.br.add_dvr_mac_vlan(mac=mac, port=port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionGotoTable(table_id=2), ], match=ofpp.OFPMatch( eth_src=mac, in_port=port), priority=4, table_id=0)) ] self.assertEqual(expected, self.mock.mock_calls) def test_remove_dvr_mac_vlan(self): mac = '00:02:b3:13:fe:3d' self.br.remove_dvr_mac_vlan(mac=mac) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows(eth_src=mac, table_id=0), ] self.assertEqual(expected, self.mock.mock_calls) def test_add_dvr_mac_tun(self): mac = '00:02:b3:13:fe:3d' port = 8888 self.br.add_dvr_mac_tun(mac=mac, port=port) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionGotoTable(table_id=1), ], match=ofpp.OFPMatch( eth_src=mac, in_port=port), priority=2, table_id=0)) ] self.assertEqual(expected, self.mock.mock_calls) def test_remove_dvr_mac_tun(self): mac = '00:02:b3:13:fe:3d' port = 8888 self.br.remove_dvr_mac_tun(mac=mac, port=port) expected = [ call.uninstall_flows(eth_src=mac, in_port=port, table_id=0), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_icmpv6_na_spoofing_protection(self): port = 8888 ip_addresses = ['2001:db8::1', 'fdf8:f53b:82e4::1/128'] self.br.install_icmpv6_na_spoofing_protection(port, ip_addresses) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionGotoTable(table_id=60), ], match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_IPV6, icmpv6_type=self.icmpv6.ND_NEIGHBOR_ADVERT, ip_proto=self.in_proto.IPPROTO_ICMPV6, ipv6_nd_target='2001:db8::1', in_port=8888, ), priority=2, table_id=24)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionGotoTable(table_id=60), ], match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_IPV6, icmpv6_type=self.icmpv6.ND_NEIGHBOR_ADVERT, ip_proto=self.in_proto.IPPROTO_ICMPV6, ipv6_nd_target='fdf8:f53b:82e4::1', in_port=8888, ), priority=2, table_id=24)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionGotoTable(table_id=24), ], match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_IPV6, icmpv6_type=self.icmpv6.ND_NEIGHBOR_ADVERT, ip_proto=self.in_proto.IPPROTO_ICMPV6, in_port=8888, ), priority=10, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test_install_arp_spoofing_protection(self): port = 8888 ip_addresses = ['192.0.2.1', '192.0.2.2/32'] self.br.install_arp_spoofing_protection(port, ip_addresses) (dp, ofp, ofpp) = self._get_dp() expected = [ call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionGotoTable(table_id=25), ], match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_ARP, arp_spa='192.0.2.1', in_port=8888, ), priority=2, table_id=24)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionGotoTable(table_id=25), ], match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_ARP, arp_spa='192.0.2.2', in_port=8888 ), priority=2, table_id=24)), call._send_msg(ofpp.OFPFlowMod(dp, cookie=self.stamp, instructions=[ ofpp.OFPInstructionGotoTable(table_id=24), ], match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_ARP, in_port=8888, ), priority=10, table_id=0)), ] self.assertEqual(expected, self.mock.mock_calls) def test_delete_arp_spoofing_protection(self): port = 8888 self.br.delete_arp_spoofing_protection(port) (dp, ofp, ofpp) = self._get_dp() expected = [ call.uninstall_flows(table_id=0, match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_ARP, in_port=8888)), call.uninstall_flows(table_id=0, match=ofpp.OFPMatch( eth_type=self.ether_types.ETH_TYPE_IPV6, icmpv6_type=self.icmpv6.ND_NEIGHBOR_ADVERT, in_port=8888, ip_proto=self.in_proto.IPPROTO_ICMPV6)), call.uninstall_flows(table_id=24, in_port=port), ] self.assertEqual(expected, self.mock.mock_calls) ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_ovs_bridge.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_ovs_bri0000664000175000017500000000426013553660047034202 0ustar zuulzuul00000000000000# Copyright (c) 2016 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.agent.common import ovs_lib from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native \ import ofswitch from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \ import ovs_test_base DPID = "0003e9" class OVSAgentBridgeTestCase(ovs_test_base.OVSRyuTestBase): def test__get_dp(self): mock.patch.object( ovs_lib.OVSBridge, 'get_datapath_id', return_value=DPID).start() mock.patch.object( ofswitch.OpenFlowSwitchMixin, "_get_dp_by_dpid", side_effect=RuntimeError).start() br = self.br_int_cls('br-int') br._cached_dpid = int(DPID, 16) # make sure it correctly raises RuntimeError, not UnboundLocalError as # in LP https://bugs.launchpad.net/neutron/+bug/1588042 self.assertRaises(RuntimeError, br._get_dp) def test_get_datapath_no_data_returned(self): def _mock_db_get_val(tb, rec, col): if tb == 'Bridge': return [] mock.patch.object(ovs_lib.OVSBridge, 'db_get_val', side_effect=_mock_db_get_val).start() br = self.br_int_cls('br-int') # make sure that in case of any misconfiguration when no datapath is # found a proper exception, not a TypeError is raised self.assertRaises(RuntimeError, br._get_dp) def test__get_dp_when_get_datapath_id_returns_None(self): br = self.br_int_cls('br-int') with mock.patch.object(br, 'get_datapath_id', return_value=None): self.assertRaises(RuntimeError, br._get_dp) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_capabilities.py0000664000175000017500000000257513553660047033376 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import events from neutron_lib import fixture from neutron.plugins.ml2.drivers.openvswitch.agent import ovs_capabilities from neutron.services.trunk.drivers.openvswitch.agent import driver from neutron.tests import base from neutron_lib import constants class CapabilitiesTest(base.BaseTestCase): def setUp(self): super(CapabilitiesTest, self).setUp() self._mgr = mock.Mock() self.useFixture(fixture.CallbackRegistryFixture( callback_manager=self._mgr)) def test_register(self): ovs_capabilities.register() self._mgr.subscribe.assert_called_with(driver.init_handler, constants.AGENT_TYPE_OVS, events.AFTER_INIT) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_vlanmanager.py0000664000175000017500000001121013553660047032333 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import fixtures import testtools from neutron.plugins.ml2.drivers.openvswitch.agent import vlanmanager from neutron.tests import base class LocalVlanManagerFixture(fixtures.Fixture): def _setUp(self): super(LocalVlanManagerFixture, self)._setUp() self.vlan_manager = vlanmanager.LocalVlanManager() self.addCleanup(self.restore_manager) # Remove _instance attribute from VlanManager in order to not obtain a # singleton del vlanmanager.LocalVlanManager._instance self.manager = vlanmanager.LocalVlanManager() def restore_manager(self): vlanmanager.LocalVlanManager._instance = self.vlan_manager class TestLocalVLANMapping(base.BaseTestCase): def test___eq___equal(self): mapping1 = vlanmanager.LocalVLANMapping(1, 2, 3, 4, 5) mapping2 = vlanmanager.LocalVLANMapping(1, 2, 3, 4, 5) self.assertEqual(mapping1, mapping2) def test___eq___different(self): mapping1 = vlanmanager.LocalVLANMapping(1, 2, 3, 4, 5) mapping2 = vlanmanager.LocalVLANMapping(1, 2, 4, 4, 5) self.assertNotEqual(mapping1, mapping2) def test___eq___different_type(self): mapping = vlanmanager.LocalVLANMapping(1, 2, 3, 4, 5) self.assertNotEqual(mapping, "foo") class TestLocalVlanManager(base.BaseTestCase): def setUp(self): super(TestLocalVlanManager, self).setUp() self.vlan_manager = self.useFixture(LocalVlanManagerFixture()).manager def test_is_singleton(self): self.vlan_manager.add(1, None, None, None, None) new_vlan_manager = vlanmanager.LocalVlanManager() self.assertIs(new_vlan_manager, self.vlan_manager) self.assertItemsEqual(new_vlan_manager.mapping, self.vlan_manager.mapping) def test_in_operator_on_key(self): self.vlan_manager.add(1, None, None, None, None) self.assertIn(1, self.vlan_manager) self.assertNotIn(2, self.vlan_manager) def test_iterator_returns_vlan_mappings(self): created_vlans = [] for val in range(3): self.vlan_manager.add(val, val, val, val, val) created_vlans.append(self.vlan_manager.get(val)) self.assertItemsEqual(created_vlans, list(self.vlan_manager)) def test_get_net_uuid_existing(self): port_id = 'port-id' vlan_data = (2, 3, 4, 5, {port_id: 'port'}) net_id = 1 self.vlan_manager.add(net_id, *vlan_data) obtained_net_id = self.vlan_manager.get_net_uuid(port_id) self.assertEqual(net_id, obtained_net_id) def test_get_net_uuid_non_existing_raises_exception(self): vlan_data = (1, 2, 3, 4, 5, {'port_id': 'port'}) self.vlan_manager.add(*vlan_data) with testtools.ExpectedException(vlanmanager.VifIdNotFound): self.vlan_manager.get_net_uuid('non-existing-port') def test_add_and_get(self): vlan_data = (2, 3, 4, 5, 6) expected_vlan_mapping = vlanmanager.LocalVLANMapping(*vlan_data) self.vlan_manager.add(1, *vlan_data) vlan_mapping = self.vlan_manager.get(1) self.assertEqual(expected_vlan_mapping, vlan_mapping) def test_add_existing_raises_exception(self): vlan_data = (2, 3, 4, 5, 6) self.vlan_manager.add(1, *vlan_data) with testtools.ExpectedException(vlanmanager.MappingAlreadyExists): self.vlan_manager.add(1, *vlan_data) def test_get_non_existing_raises_keyerror(self): with testtools.ExpectedException(vlanmanager.MappingNotFound): self.vlan_manager.get(1) def test_pop(self): vlan_data = (2, 3, 4, 5, 6) expected_vlan_mapping = vlanmanager.LocalVLANMapping(*vlan_data) self.vlan_manager.add(1, *vlan_data) vlan_mapping = self.vlan_manager.pop(1) self.assertEqual(expected_vlan_mapping, vlan_mapping) self.assertFalse(self.vlan_manager.mapping) def test_pop_non_existing_raises_exception(self): with testtools.ExpectedException(vlanmanager.MappingNotFound): self.vlan_manager.pop(1) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py0000664000175000017500000052233513553660047033616 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys import time import mock from neutron_lib.agent import constants as agent_consts from neutron_lib import constants as n_const from oslo_config import cfg from oslo_log import log import oslo_messaging import testtools from neutron._i18n import _ from neutron.agent.common import ovs_lib from neutron.agent.common import utils from neutron.agent.linux import async_process from neutron.agent.linux import ip_lib from neutron.common import rpc as n_rpc from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent import ovs_neutron_agent \ as ovs_agent from neutron.tests import base from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \ import ovs_test_base from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \ import test_vlanmanager NOTIFIER = 'neutron.plugins.ml2.rpc.AgentNotifierApi' PULLAPI = 'neutron.api.rpc.handlers.resources_rpc.ResourcesPullRpcApi' OVS_LINUX_KERN_VERS_WITHOUT_VXLAN = "3.12.0" FAKE_MAC = '00:11:22:33:44:55' FAKE_IP1 = '10.0.0.1' FAKE_IP2 = '10.0.0.2' FAKE_IP6 = '2001:db8:42:42::10' TEST_PORT_ID1 = 'port-id-1' TEST_PORT_ID2 = 'port-id-2' TEST_PORT_ID3 = 'port-id-3' TEST_NETWORK_ID1 = 'net-id-1' TEST_NETWORK_ID2 = 'net-id-2' DEVICE_OWNER_COMPUTE = n_const.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' class FakeVif(object): ofport = 99 port_name = 'name' vif_mac = 'aa:bb:cc:11:22:33' class MockFixedIntervalLoopingCall(object): def __init__(self, f): self.f = f def start(self, interval=0): self.f() class ValidateTunnelTypes(ovs_test_base.OVSAgentConfigTestBase): def setUp(self): super(ValidateTunnelTypes, self).setUp() self.mock_validate_local_ip = mock.patch.object( self.mod_agent, 'validate_local_ip').start() def test_validate_tunnel_types_succeeds(self): cfg.CONF.set_override('local_ip', '10.10.10.10', group='OVS') cfg.CONF.set_override('tunnel_types', [n_const.TYPE_GRE], group='AGENT') self.mod_agent.validate_tunnel_config(cfg.CONF.AGENT.tunnel_types, cfg.CONF.OVS.local_ip) self.mock_validate_local_ip.assert_called_once_with('10.10.10.10') def test_validate_tunnel_types_fails_for_invalid_tunnel_type(self): cfg.CONF.set_override('local_ip', '10.10.10.10', group='OVS') cfg.CONF.set_override('tunnel_types', ['foobar'], group='AGENT') with testtools.ExpectedException(SystemExit): self.mod_agent.validate_tunnel_config(cfg.CONF.AGENT.tunnel_types, cfg.CONF.OVS.local_ip) class TestOvsNeutronAgent(object): def setUp(self): super(TestOvsNeutronAgent, self).setUp() self.useFixture(test_vlanmanager.LocalVlanManagerFixture()) mock.patch(PULLAPI).start() notifier_p = mock.patch(NOTIFIER) notifier_cls = notifier_p.start() self.notifier = mock.Mock() notifier_cls.return_value = self.notifier systemd_patch = mock.patch('oslo_service.systemd.notify_once') self.systemd_notify = systemd_patch.start() cfg.CONF.set_default('firewall_driver', 'neutron.agent.firewall.NoopFirewallDriver', group='SECURITYGROUP') cfg.CONF.set_default('quitting_rpc_timeout', 10, 'AGENT') cfg.CONF.set_default('local_ip', '127.0.0.1', 'OVS') mock.patch( 'neutron.agent.ovsdb.native.helpers.enable_connection_uri').start() mock.patch( 'neutron.agent.common.ovs_lib.OVSBridge.get_ports_attributes', return_value=[]).start() mock.patch('neutron.agent.common.ovs_lib.BaseOVS.config', new_callable=mock.PropertyMock, return_value={}).start() mock.patch('neutron.agent.ovsdb.impl_idl._connection').start() self.agent = self._make_agent() self.agent.sg_agent = mock.Mock() def _make_agent(self): with mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_integration_br'),\ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_ancillary_bridges', return_value=[]),\ mock.patch('neutron.agent.linux.ip_lib.get_device_mac', return_value='00:00:00:00:00:01'),\ mock.patch( 'neutron.agent.common.ovs_lib.BaseOVS.get_bridges'),\ mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall', new=MockFixedIntervalLoopingCall),\ mock.patch( 'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports', return_value=[]): ext_manager = mock.Mock() agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(), ext_manager, cfg.CONF) agent.tun_br = self.br_tun_cls(br_name='br-tun') return agent def _mock_port_bound(self, ofport=None, new_local_vlan=None, old_local_vlan=None, db_get_val=None): port = mock.Mock() port.ofport = ofport net_uuid = 'my-net-uuid' fixed_ips = [{'subnet_id': 'my-subnet-uuid', 'ip_address': '1.1.1.1'}] if old_local_vlan is not None: self.agent.vlan_manager.add( net_uuid, old_local_vlan, None, None, None) with mock.patch.object(self.agent, 'int_br', autospec=True) as int_br: int_br.db_get_val.return_value = db_get_val int_br.set_db_attribute.return_value = True needs_binding = self.agent.port_bound( port, net_uuid, 'local', None, None, fixed_ips, DEVICE_OWNER_COMPUTE, False) if db_get_val is None: self.assertEqual(0, int_br.set_db_attribute.call_count) self.assertFalse(needs_binding) else: vlan_mapping = {'net_uuid': net_uuid, 'network_type': 'local', 'physical_network': 'None'} int_br.set_db_attribute.assert_called_once_with( "Port", mock.ANY, "other_config", vlan_mapping) self.assertTrue(needs_binding) def test_setup_physical_bridges_during_agent_initialization(self): with mock.patch.object( self.mod_agent.OVSNeutronAgent, 'setup_physical_bridges') as setup_physical_bridges,\ mock.patch.object( self.mod_agent.OVSNeutronAgent, 'setup_rpc') as setup_rpc: setup_rpc.side_effect = oslo_messaging.MessagingException( "Test communication failure") try: self._make_agent() except oslo_messaging.MessagingException: pass setup_physical_bridges.assert_called_once_with(mock.ANY) def test_datapath_type_system(self): # verify kernel datapath is default expected = constants.OVS_DATAPATH_SYSTEM self.assertEqual(expected, self.agent.int_br.datapath_type) def test_datapath_type_netdev(self): with mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_integration_br'), \ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_ancillary_bridges', return_value=[]), \ mock.patch('neutron.agent.linux.ip_lib.get_device_mac', return_value='00:00:00:00:00:01'), \ mock.patch( 'neutron.agent.common.ovs_lib.BaseOVS.get_bridges'), \ mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall', new=MockFixedIntervalLoopingCall), \ mock.patch( 'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports', return_value=[]), \ mock.patch('neutron.agent.common.ovs_lib.BaseOVS.config', new_callable=mock.PropertyMock, return_value={'datapath_types': ['netdev']}): # validate setting non default datapath expected = constants.OVS_DATAPATH_NETDEV cfg.CONF.set_override('datapath_type', expected, group='OVS') ext_manager = mock.Mock() self.agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(), ext_manager, cfg.CONF) self.assertEqual(expected, self.agent.int_br.datapath_type) def test_agent_type_ovs(self): # verify agent_type is default expected = n_const.AGENT_TYPE_OVS self.assertEqual(expected, self.agent.agent_state['agent_type']) def test_agent_available_local_vlans(self): expected = [n_const.MIN_VLAN_TAG, n_const.MIN_VLAN_TAG + 1, n_const.MAX_VLAN_TAG - 1, n_const.MAX_VLAN_TAG] exception = [n_const.MIN_VLAN_TAG - 1, n_const.MAX_VLAN_TAG + 1, n_const.MAX_VLAN_TAG + 2] available_vlan = self.agent.available_local_vlans for tag in expected: self.assertIn(tag, available_vlan) for tag in exception: self.assertNotIn(tag, available_vlan) def test_agent_type_alt(self): with mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_integration_br'),\ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_ancillary_bridges', return_value=[]), \ mock.patch('neutron.agent.linux.ip_lib.get_device_mac', return_value='00:00:00:00:00:01'), \ mock.patch( 'neutron.agent.common.ovs_lib.BaseOVS.get_bridges'), \ mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall', new=MockFixedIntervalLoopingCall), \ mock.patch( 'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports', return_value=[]): # validate setting non default agent_type expected = 'alt agent type' cfg.CONF.set_override('agent_type', expected, group='AGENT') ext_manager = mock.Mock() self.agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(), ext_manager, cfg.CONF) self.assertEqual(expected, self.agent.agent_state['agent_type']) def _test_restore_local_vlan_maps(self, tag, segmentation_id='1'): port = mock.Mock() port.port_name = 'fake_port' net_uuid = 'fake_network_id' local_vlan_map = {'net_uuid': net_uuid, 'network_type': 'vlan', 'physical_network': 'fake_network'} if segmentation_id is not None: local_vlan_map['segmentation_id'] = segmentation_id # this is for the call inside get_vif_ports() get_interfaces = [{'name': port.port_name, 'ofport': '1', 'external_ids': { 'iface-id': '1', 'attached-mac': 'mac1'}}, {'name': 'invalid', 'ofport': ovs_lib.INVALID_OFPORT, 'external_ids': { 'iface-id': '2', 'attached-mac': 'mac2'}}, {'name': 'unassigned', 'ofport': ovs_lib.UNASSIGNED_OFPORT, 'external_ids': { 'iface-id': '3', 'attached-mac': 'mac3'}}] # this is for the call inside _restore_local_vlan_map() get_ports = [{'name': port.port_name, 'other_config': local_vlan_map, 'tag': tag}] with mock.patch.object(self.agent.int_br, 'get_ports_attributes', side_effect=[get_interfaces, get_ports]) as gpa: self.agent._restore_local_vlan_map() expected_hints = {} if tag: expected_hints[net_uuid] = tag self.assertEqual(expected_hints, self.agent._local_vlan_hints) # make sure invalid and unassigned ports were skipped gpa.assert_has_calls([ mock.call('Interface', columns=mock.ANY, if_exists=True), mock.call('Port', columns=mock.ANY, ports=['fake_port']) ]) def test_restore_local_vlan_map_with_device_has_tag(self): self._test_restore_local_vlan_maps(2) def test_restore_local_vlan_map_with_device_no_tag(self): self._test_restore_local_vlan_maps([]) def test_restore_local_vlan_map_no_segmentation_id(self): self._test_restore_local_vlan_maps(2, segmentation_id=None) def test_restore_local_vlan_map_segmentation_id_compat(self): self._test_restore_local_vlan_maps(2, segmentation_id='None') def test_check_agent_configurations_for_dvr_raises(self): self.agent.enable_distributed_routing = True self.agent.enable_tunneling = True self.agent.l2_pop = False self.assertRaises(ValueError, self.agent._check_agent_configurations) def test_check_agent_configurations_for_dvr(self): self.agent.enable_distributed_routing = True self.agent.enable_tunneling = True self.agent.l2_pop = True self.assertIsNone(self.agent._check_agent_configurations()) def test_check_agent_configurations_for_dvr_with_vlan(self): self.agent.enable_distributed_routing = True self.agent.enable_tunneling = False self.agent.l2_pop = False self.assertIsNone(self.agent._check_agent_configurations()) def test_port_bound_deletes_flows_for_valid_ofport(self): self._mock_port_bound(ofport=1, new_local_vlan=1, db_get_val={}) def test_port_bound_ignores_flows_for_invalid_ofport(self): self._mock_port_bound(ofport=-1, new_local_vlan=1, db_get_val={}) def test_port_bound_does_not_rewire_if_already_bound(self): self._mock_port_bound( ofport=-1, new_local_vlan=1, old_local_vlan=1, db_get_val={}) def test_port_bound_not_found(self): self._mock_port_bound(ofport=1, new_local_vlan=1, db_get_val=None) def _test_port_dead(self, cur_tag=None): port = mock.Mock() port.ofport = 1 with mock.patch.object(self.agent, 'int_br') as int_br: int_br.db_get_val.return_value = cur_tag self.agent.port_dead(port) if cur_tag is None or cur_tag == constants.DEAD_VLAN_TAG: self.assertFalse(int_br.set_db_attribute.called) self.assertFalse(int_br.drop_port.called) else: int_br.assert_has_calls([ mock.call.set_db_attribute("Port", mock.ANY, "tag", constants.DEAD_VLAN_TAG, log_errors=True), mock.call.drop_port(in_port=port.ofport), ]) def test_port_dead(self): self._test_port_dead() def test_port_dead_with_port_already_dead(self): self._test_port_dead(constants.DEAD_VLAN_TAG) def test_port_dead_with_valid_tag(self): self._test_port_dead(cur_tag=1) def mock_scan_ports(self, vif_port_set=None, registered_ports=None, updated_ports=None, port_tags_dict=None, sync=False): if port_tags_dict is None: # Because empty dicts evaluate as False. port_tags_dict = {} with mock.patch.object(self.agent.int_br, 'get_vif_port_set', return_value=vif_port_set),\ mock.patch.object(self.agent.int_br, 'get_port_tag_dict', return_value=port_tags_dict): return self.agent.scan_ports(registered_ports, sync, updated_ports) def test_scan_ports_returns_current_only_for_unchanged_ports(self): vif_port_set = set([1, 3]) registered_ports = set([1, 3]) expected = {'current': vif_port_set, 'added': set(), 'removed': set()} actual = self.mock_scan_ports(vif_port_set, registered_ports) self.assertEqual(expected, actual) def test_scan_ports_returns_port_changes(self): vif_port_set = set([1, 3]) registered_ports = set([1, 2]) expected = dict(current=vif_port_set, added=set([3]), removed=set([2])) actual = self.mock_scan_ports(vif_port_set, registered_ports) self.assertEqual(expected, actual) def test_scan_ports_returns_port_changes_with_sync(self): vif_port_set = set([1, 3]) registered_ports = set([1, 2]) expected = dict(current=vif_port_set, added=vif_port_set, removed=set([2])) actual = self.mock_scan_ports(vif_port_set, registered_ports, sync=True) self.assertEqual(expected, actual) def _test_scan_ports_with_updated_ports(self, updated_ports): vif_port_set = set([1, 3, 4]) registered_ports = set([1, 2, 4]) expected = dict(current=vif_port_set, added=set([3]), removed=set([2]), updated=set([4])) actual = self.mock_scan_ports(vif_port_set, registered_ports, updated_ports) self.assertEqual(expected, actual) def test_scan_ports_finds_known_updated_ports(self): self._test_scan_ports_with_updated_ports(set([4])) def test_scan_ports_ignores_unknown_updated_ports(self): # the port '5' was not seen on current ports. Hence it has either # never been wired or already removed and should be ignored self._test_scan_ports_with_updated_ports(set([4, 5])) def test_scan_ports_ignores_updated_port_if_removed(self): vif_port_set = set([1, 3]) registered_ports = set([1, 2]) updated_ports = set([1, 2]) expected = dict(current=vif_port_set, added=set([3]), removed=set([2]), updated=set([1])) actual = self.mock_scan_ports(vif_port_set, registered_ports, updated_ports) self.assertEqual(expected, actual) def test_scan_ports_no_vif_changes_returns_updated_port_only(self): vif_port_set = set([1, 2, 3]) registered_ports = set([1, 2, 3]) updated_ports = set([2]) expected = dict(current=vif_port_set, updated=set([2]), added=set(), removed=set()) actual = self.mock_scan_ports(vif_port_set, registered_ports, updated_ports) self.assertEqual(expected, actual) def _test_process_ports_events(self, events, registered_ports, ancillary_ports, expected_ports, expected_ancillary, updated_ports=None, ): with mock.patch.object(self.agent, 'check_changed_vlans', return_value=set()): devices_not_ready_yet = set() failed_devices = {'added': set(), 'removed': set()} failed_ancillary_devices = { 'added': set(), 'removed': set()} actual = self.agent.process_ports_events( events, registered_ports, ancillary_ports, devices_not_ready_yet, failed_devices, failed_ancillary_devices, updated_ports) self.assertEqual( (expected_ports, expected_ancillary, devices_not_ready_yet), actual) def test_process_ports_events_port_removed_and_added(self): port_id = 'f6f104bd-37c7-4f7b-9d70-53a6bb42728f' events = { 'removed': [{'ofport': 1, 'external_ids': {'iface-id': port_id, 'attached-mac': 'fa:16:3e:f6:1b:fb'}, 'name': 'qvof6f104bd-37'}], 'added': [{'ofport': 2, 'external_ids': {'iface-id': port_id, 'attached-mac': 'fa:16:3e:f6:1b:fb'}, 'name': 'qvof6f104bd-37'}] } registered_ports = {port_id} expected_ancillary = dict(current=set(), added=set(), removed=set()) # port was removed and then added expected_ports = dict(current={port_id}, added={port_id}, removed=set()) with mock.patch.object(ovs_lib.BaseOVS, "port_exists", return_value=True): self._test_process_ports_events(events.copy(), registered_ports, set(), expected_ports, expected_ancillary) # port was added and then removed expected_ports = dict(current=set(), added=set(), removed={port_id}) with mock.patch.object(ovs_lib.BaseOVS, "port_exists", return_value=False): self._test_process_ports_events(events.copy(), registered_ports, set(), expected_ports, expected_ancillary) def test_process_ports_events_returns_current_for_unchanged_ports(self): events = {'added': [], 'removed': []} registered_ports = {1, 3} ancillary_ports = {2, 5} expected_ports = {'current': registered_ports, 'added': set(), 'removed': set()} expected_ancillary = {'current': ancillary_ports, 'added': set(), 'removed': set()} self._test_process_ports_events(events, registered_ports, ancillary_ports, expected_ports, expected_ancillary) def test_process_port_events_no_vif_changes_return_updated_port_only(self): events = {'added': [], 'removed': []} registered_ports = {1, 2, 3} updated_ports = {2} expected_ports = dict(current=registered_ports, updated={2}, added=set(), removed=set()) expected_ancillary = dict(current=set(), added=set(), removed=set()) self._test_process_ports_events(events, registered_ports, set(), expected_ports, expected_ancillary, updated_ports) def test_process_port_events_ignores_removed_port_if_never_added(self): events = {'added': [], 'removed': [{'name': 'port2', 'ofport': 2, 'external_ids': {'attached-mac': 'test-mac'}}]} registered_ports = {1} expected_ports = dict(current=registered_ports, added=set(), removed=set()) expected_ancillary = dict(current=set(), added=set(), removed=set()) devices_not_ready_yet = set() with mock.patch.object(self.agent.int_br, 'portid_from_external_ids', side_effect=[2]), \ mock.patch.object(self.agent, 'check_changed_vlans', return_value=set()): failed_devices = {'added': set(), 'removed': set()} failed_ancillary_devices = { 'added': set(), 'removed': set()} ports_not_ready_yet = set() actual = self.agent.process_ports_events( events, registered_ports, set(), ports_not_ready_yet, failed_devices, failed_ancillary_devices) self.assertEqual( (expected_ports, expected_ancillary, devices_not_ready_yet), actual) def test_process_port_events_port_not_ready_yet(self): events = {'added': [{'name': 'port5', 'ofport': [], 'external_ids': {'attached-mac': 'test-mac'}}], 'removed': []} old_devices_not_ready = {'port4'} registered_ports = set([1, 2, 3]) expected_ports = dict(current=set([1, 2, 3, 4]), added=set([4]), removed=set()) self.agent.ancillary_brs = [] expected_ancillary = dict(current=set(), added=set(), removed=set()) with mock.patch.object(self.agent.int_br, 'portid_from_external_ids', side_effect=[5, 4]), \ mock.patch.object(self.agent, 'check_changed_vlans', return_value=set()), \ mock.patch.object(self.agent.int_br, 'get_ports_attributes', return_value=[{'name': 'port4', 'ofport': 4, 'external_ids': { 'attached-mac': 'mac4'}}]): expected_devices_not_ready = {'port5'} failed_devices = {'added': set(), 'removed': set()} failed_ancillary_devices = { 'added': set(), 'removed': set()} actual = self.agent.process_ports_events( events, registered_ports, set(), old_devices_not_ready, failed_devices, failed_ancillary_devices) self.assertEqual( (expected_ports, expected_ancillary, expected_devices_not_ready), actual) def _test_process_port_events_with_updated_ports(self, updated_ports): events = {'added': [{'name': 'port3', 'ofport': 3, 'external_ids': {'attached-mac': 'test-mac'}}, {'name': 'qg-port2', 'ofport': 6, 'external_ids': {'attached-mac': 'test-mac'}}], 'removed': [{'name': 'port2', 'ofport': 2, 'external_ids': {'attached-mac': 'test-mac'}}, {'name': 'qg-port1', 'ofport': 5, 'external_ids': {'attached-mac': 'test-mac'}}]} registered_ports = {1, 2, 4} ancillary_ports = {5, 8} expected_ports = dict(current={1, 3, 4}, added={3}, removed={2}) if updated_ports: expected_ports['updated'] = updated_ports expected_ancillary = dict(current={6, 8}, added={6}, removed={5}) ancillary_bridge = mock.Mock() ancillary_bridge.get_vif_port_set.return_value = {5, 6, 8} self.agent.ancillary_brs = [ancillary_bridge] with mock.patch.object(self.agent.int_br, 'portid_from_external_ids', side_effect=[3, 6, 2, 5]), \ mock.patch.object(self.agent, 'check_changed_vlans', return_value=set()): devices_not_ready_yet = set() failed_devices = {'added': set(), 'removed': set()} failed_ancillary_devices = { 'added': set(), 'removed': set()} actual = self.agent.process_ports_events( events, registered_ports, ancillary_ports, devices_not_ready_yet, failed_devices, failed_ancillary_devices, updated_ports) self.assertEqual( (expected_ports, expected_ancillary, devices_not_ready_yet), actual) def test_process_port_events_returns_port_changes(self): self._test_process_port_events_with_updated_ports(set()) def test_process_port_events_finds_known_updated_ports(self): self._test_process_port_events_with_updated_ports({4}) def test_process_port_events_ignores_unknown_updated_ports(self): # the port '10' was not seen on current ports. Hence it has either # never been wired or already removed and should be ignored self._test_process_port_events_with_updated_ports({4, 10}) def test_process_port_events_ignores_updated_port_if_removed(self): self._test_process_port_events_with_updated_ports({4, 5}) def test_update_ports_returns_changed_vlan(self): br = self.br_int_cls('br-int') mac = "ca:fe:de:ad:be:ef" port = ovs_lib.VifPort(1, 1, 1, mac, br) self.agent.vlan_manager.add( '1', 1, '1', None, 1, {port.vif_id: port}) vif_port_set = set([1, 3]) registered_ports = set([1, 2]) port_tags_dict = {1: []} expected = dict( added=set([3]), current=vif_port_set, removed=set([2]), updated=set([1]) ) with mock.patch.object(self.agent, 'tun_br', autospec=True), \ mock.patch.object(self.agent.plugin_rpc, 'update_device_list') as upd_l: actual = self.mock_scan_ports( vif_port_set, registered_ports, port_tags_dict=port_tags_dict) self.assertEqual(expected, actual) upd_l.assert_called_once_with(mock.ANY, [], set([1]), self.agent.agent_id, self.agent.conf.host) def test_update_retries_map_and_remove_devs_not_to_retry(self): failed_devices_retries_map = { 'device_not_to_retry': constants.MAX_DEVICE_RETRIES, 'device_to_retry': 2, 'ancillary_not_to_retry': constants.MAX_DEVICE_RETRIES, 'ancillary_to_retry': 1} failed_devices = { 'added': set(['device_not_to_retry']), 'removed': set(['device_to_retry', 'new_device'])} failed_ancillary_devices = {'added': set(['ancillary_to_retry']), 'removed': set(['ancillary_not_to_retry'])} expected_failed_devices_retries_map = { 'device_to_retry': 3, 'new_device': 1, 'ancillary_to_retry': 2} (new_failed_devices_retries_map, devices_not_to_retry, ancillary_devices_not_t_retry) = self.agent._get_devices_not_to_retry( failed_devices, failed_ancillary_devices, failed_devices_retries_map) self.agent._remove_devices_not_to_retry( failed_devices, failed_ancillary_devices, devices_not_to_retry, ancillary_devices_not_t_retry) self.assertIn('device_to_retry', failed_devices['removed']) self.assertNotIn('device_not_to_retry', failed_devices['added']) self.assertEqual( expected_failed_devices_retries_map, new_failed_devices_retries_map) def test_add_port_tag_info(self): lvm = mock.Mock() lvm.vlan = 1 self.agent.vlan_manager.mapping["net1"] = lvm ovs_db_list = [{'name': 'tap1', 'tag': [], 'other_config': {'segmentation_id': '1'}}, {'name': 'tap2', 'tag': [], 'other_config': {}}, {'name': 'tap3', 'tag': [], 'other_config': None}] vif_port1 = mock.Mock() vif_port1.port_name = 'tap1' vif_port2 = mock.Mock() vif_port2.port_name = 'tap2' vif_port3 = mock.Mock() vif_port3.port_name = 'tap3' port_details = [ {'network_id': 'net1', 'vif_port': vif_port1}, {'network_id': 'net1', 'vif_port': vif_port2}, {'network_id': 'net1', 'vif_port': vif_port3}] with mock.patch.object(self.agent, 'int_br') as int_br: int_br.get_ports_attributes.return_value = ovs_db_list self.agent._add_port_tag_info(port_details) set_db_attribute_calls = \ [mock.call.set_db_attribute("Port", "tap1", "other_config", {"segmentation_id": "1", "tag": "1"}), mock.call.set_db_attribute("Port", "tap2", "other_config", {"tag": "1"}), mock.call.set_db_attribute("Port", "tap3", "other_config", {"tag": "1"})] int_br.assert_has_calls(set_db_attribute_calls, any_order=True) def test_add_port_tag_info_with_tagged_ports(self): lvm = mock.Mock() lvm.vlan = 1 self.agent.vlan_manager.mapping["net1"] = lvm ovs_db_list1 = [{'name': 'tap1', 'tag': 1, 'other_config': {'segmentation_id': '1', 'tag': '1'}}] ovs_db_list2 = [{'name': 'tap2', 'tag': 2, 'other_config': {'segmentation_id': '1', 'tag': '1'}}, {'name': 'tap3', 'tag': 1, 'other_config': {'segmentation_id': '2', 'tag': '2'}}] vif_port1 = mock.Mock() vif_port1.port_name = 'tap1' vif_port2 = mock.Mock() vif_port2.port_name = 'tap2' vif_port2.ofport = 7 vif_port3 = mock.Mock() vif_port3.port_name = 'tap3' vif_port3.ofport = 8 port_details1 = [{'network_id': 'net1', 'vif_port': vif_port1}] port_details2 = [{'network_id': 'net1', 'vif_port': vif_port2}, {'network_id': 'net1', 'vif_port': vif_port3}] with mock.patch.object(self.agent, 'int_br') as int_br: int_br.get_ports_attributes.return_value = ovs_db_list1 self.agent._add_port_tag_info(port_details1) int_br.set_db_attribute.assert_not_called() # Reset mock to check port with changed tag int_br.reset_mock() int_br.get_ports_attributes.return_value = ovs_db_list2 self.agent._add_port_tag_info(port_details2) expected_calls = \ [mock.call.set_db_attribute("Port", "tap2", "other_config", {'segmentation_id': '1', 'tag': '1'}), mock.call.uninstall_flows(in_port=7), mock.call.set_db_attribute("Port", "tap3", "other_config", {'segmentation_id': '2', 'tag': '1'}), mock.call.uninstall_flows(in_port=8)] int_br.assert_has_calls(expected_calls) def test_bind_devices(self): devices_up = ['tap1'] devices_down = ['tap2'] self.agent.vlan_manager.mapping["net1"] = mock.Mock() ovs_db_list = [{'name': 'tap1', 'tag': []}, {'name': 'tap2', 'tag': []}] vif_port1 = mock.Mock() vif_port1.port_name = 'tap1' vif_port2 = mock.Mock() vif_port2.port_name = 'tap2' port_details = [ {'network_id': 'net1', 'vif_port': vif_port1, 'device': devices_up[0], 'device_owner': 'network:dhcp', 'admin_state_up': True}, {'network_id': 'net1', 'vif_port': vif_port2, 'device': devices_down[0], 'device_owner': 'network:dhcp', 'admin_state_up': False}] with mock.patch.object( self.agent.plugin_rpc, 'update_device_list', return_value={'devices_up': devices_up, 'devices_down': devices_down, 'failed_devices_up': [], 'failed_devices_down': []}) as update_devices, \ mock.patch.object(self.agent, 'int_br') as int_br: int_br.get_ports_attributes.return_value = ovs_db_list self.agent._bind_devices(port_details) update_devices.assert_called_once_with(mock.ANY, devices_up, devices_down, mock.ANY, mock.ANY, agent_restarted=True) def _test_arp_spoofing(self, enable_prevent_arp_spoofing): self.agent.prevent_arp_spoofing = enable_prevent_arp_spoofing ovs_db_list = [{'name': 'fake_device', 'tag': []}] self.agent.vlan_manager.add('fake_network', 1, None, None, 1) vif_port = mock.Mock() vif_port.port_name = 'fake_device' vif_port.ofport = 1 need_binding_ports = [{'network_id': 'fake_network', 'vif_port': vif_port, 'device': 'fake_device', 'admin_state_up': True}] with mock.patch.object( self.agent.plugin_rpc, 'update_device_list', return_value={'devices_up': [], 'devices_down': [], 'failed_devices_up': [], 'failed_devices_down': []}), \ mock.patch.object(self.agent, 'int_br') as int_br, \ mock.patch.object( self.agent, 'setup_arp_spoofing_protection') as setup_arp: int_br.get_ports_attributes.return_value = ovs_db_list self.agent._bind_devices(need_binding_ports) self.assertEqual(enable_prevent_arp_spoofing, setup_arp.called) def test_setup_arp_spoofing_protection_enable(self): self._test_arp_spoofing(True) def test_setup_arp_spoofing_protection_disabled(self): self._test_arp_spoofing(False) def _mock_treat_devices_added_updated(self, details, port, func_name): """Mock treat devices added or updated. :param details: the details to return for the device :param port: the port that get_vif_port_by_id should return :param func_name: the function that should be called :returns: whether the named function was called """ with mock.patch.object(self.agent.plugin_rpc, 'get_devices_details_list_and_failed_devices', return_value={'devices': [details], 'failed_devices': []}),\ mock.patch.object(self.agent.int_br, 'get_vifs_by_ids', return_value={details['device']: port}),\ mock.patch.object(self.agent.plugin_rpc, 'update_device_list', return_value={'devices_up': [], 'devices_down': details, 'failed_devices_up': [], 'failed_devices_down': []}),\ mock.patch.object(self.agent.int_br, 'get_port_tag_dict', return_value={}),\ mock.patch.object(self.agent, func_name) as func: skip_devs, need_bound_devices, _ = ( self.agent.treat_devices_added_or_updated([], False)) # The function should not raise self.assertFalse(skip_devs) return func.called def test_treat_devices_added_updated_ignores_invalid_ofport(self): port = mock.Mock() port.ofport = -1 self.assertFalse(self._mock_treat_devices_added_updated( mock.MagicMock(), port, 'port_dead')) def test_treat_devices_added_updated_marks_unknown_port_as_dead(self): port = mock.Mock() port.ofport = 1 self.assertTrue(self._mock_treat_devices_added_updated( mock.MagicMock(), port, 'port_dead')) def test_treat_devices_added_does_not_process_missing_port(self): with mock.patch.object( self.agent.plugin_rpc, 'get_devices_details_list_and_failed_devices') as get_dev_fn,\ mock.patch.object(self.agent.int_br, 'get_vif_port_by_id', return_value=None): self.assertFalse(get_dev_fn.called) def test_treat_devices_added_updated_updates_known_port(self): details = mock.MagicMock() details.__contains__.side_effect = lambda x: True self.assertTrue(self._mock_treat_devices_added_updated( details, mock.Mock(), 'treat_vif_port')) def test_treat_devices_added_updated_sends_vif_port_into_extension_manager( self, *args): details = mock.MagicMock() details.__contains__.side_effect = lambda x: True port = mock.MagicMock() def fake_handle_port(context, port): self.assertIn('vif_port', port) with mock.patch.object(self.agent.plugin_rpc, 'get_devices_details_list_and_failed_devices', return_value={'devices': [details], 'failed_devices': []}),\ mock.patch.object(self.agent.ext_manager, 'handle_port', new=fake_handle_port),\ mock.patch.object(self.agent.int_br, 'get_vifs_by_ids', return_value={details['device']: port}),\ mock.patch.object(self.agent, 'treat_vif_port', return_value=False): self.agent.treat_devices_added_or_updated([], False) def test_treat_devices_added_updated_skips_if_port_not_found(self): dev_mock = mock.MagicMock() dev_mock.__getitem__.return_value = 'the_skipped_one' with mock.patch.object(self.agent.plugin_rpc, 'get_devices_details_list_and_failed_devices', return_value={'devices': [dev_mock], 'failed_devices': []}),\ mock.patch.object(self.agent.int_br, 'get_port_tag_dict', return_value={}),\ mock.patch.object(self.agent.int_br, 'get_vifs_by_ids', return_value={}),\ mock.patch.object(self.agent.ext_manager, "delete_port") as ext_mgr_delete_port,\ mock.patch.object(self.agent, 'treat_vif_port') as treat_vif_port: skip_devs = self.agent.treat_devices_added_or_updated([], False) # The function should return False for resync and no device # processed self.assertEqual((['the_skipped_one'], [], set()), skip_devs) ext_mgr_delete_port.assert_called_once_with( self.agent.context, {'port_id': 'the_skipped_one'}) self.assertFalse(treat_vif_port.called) def test_treat_devices_added_failed_devices(self): dev_mock = 'the_failed_one' with mock.patch.object(self.agent.plugin_rpc, 'get_devices_details_list_and_failed_devices', return_value={'devices': [], 'failed_devices': [dev_mock]}),\ mock.patch.object(self.agent.int_br, 'get_vifs_by_ids', return_value={}),\ mock.patch.object(self.agent, 'treat_vif_port') as treat_vif_port: failed_devices = {'added': set(), 'removed': set()} (_, _, failed_devices['added']) = ( self.agent.treat_devices_added_or_updated([], False)) # The function should return False for resync and no device # processed self.assertEqual(set([dev_mock]), failed_devices.get('added')) self.assertFalse(treat_vif_port.called) def test_treat_devices_added_updated_put_port_down(self): fake_details_dict = {'admin_state_up': False, 'port_id': 'xxx', 'device': 'xxx', 'network_id': 'yyy', 'physical_network': 'foo', 'segmentation_id': 'bar', 'network_type': 'baz', 'fixed_ips': [{'subnet_id': 'my-subnet-uuid', 'ip_address': '1.1.1.1'}], 'device_owner': DEVICE_OWNER_COMPUTE } with mock.patch.object(self.agent.plugin_rpc, 'get_devices_details_list_and_failed_devices', return_value={'devices': [fake_details_dict], 'failed_devices': []}),\ mock.patch.object(self.agent.int_br, 'get_vifs_by_ids', return_value={'xxx': mock.MagicMock()}),\ mock.patch.object(self.agent.int_br, 'get_port_tag_dict', return_value={}),\ mock.patch.object(self.agent, 'treat_vif_port') as treat_vif_port: skip_devs, need_bound_devices, _ = ( self.agent.treat_devices_added_or_updated([], False)) # The function should return False for resync self.assertFalse(skip_devs) self.assertTrue(treat_vif_port.called) def _mock_treat_devices_removed(self, port_exists): details = dict(exists=port_exists) with mock.patch.object(self.agent.plugin_rpc, 'update_device_list', return_value={'devices_up': [], 'devices_down': details, 'failed_devices_up': [], 'failed_devices_down': []}): with mock.patch.object(self.agent, 'port_unbound') as port_unbound: self.assertFalse(self.agent.treat_devices_removed([{}])) self.assertTrue(port_unbound.called) def test_treat_devices_removed_unbinds_port(self): self._mock_treat_devices_removed(True) def test_treat_devices_removed_ignores_missing_port(self): self._mock_treat_devices_removed(False) def test_treat_devices_removed_failed_devices(self): dev_mock = 'the_failed_one' with mock.patch.object(self.agent.plugin_rpc, 'update_device_list', return_value={'devices_up': [], 'devices_down': [], 'failed_devices_up': [], 'failed_devices_down': [ dev_mock]}): failed_devices = {'added': set(), 'removed': set()} failed_devices['removed'] = self.agent.treat_devices_removed([{}]) self.assertEqual(set([dev_mock]), failed_devices.get('removed')) def test_treat_devices_removed_ext_delete_port(self): port_id = 'fake-id' m_delete = mock.patch.object(self.agent.ext_manager, 'delete_port') m_rpc = mock.patch.object(self.agent.plugin_rpc, 'update_device_list', return_value={'devices_up': [], 'devices_down': [], 'failed_devices_up': [], 'failed_devices_down': []}) m_unbound = mock.patch.object(self.agent, 'port_unbound') with m_delete as delete, m_rpc, m_unbound: self.agent.treat_devices_removed([port_id]) delete.assert_called_with(mock.ANY, {'port_id': port_id}) def test_treat_vif_port_shut_down_port(self): details = mock.MagicMock() vif_port = type('vif_port', (object,), { "vif_id": "12", "iface-id": "407a79e0-e0be-4b7d-92a6-513b2161011b", "vif_mac": "fa:16:3e:68:46:7b", "port_name": "qr-407a79e0-e0", "ofport": -1, "bridge_name": "br-int"}) with mock.patch.object( self.agent.plugin_rpc, 'update_device_down' ) as update_device_down, mock.patch.object( self.agent, "port_dead" ) as port_dead: port_needs_binding = self.agent.treat_vif_port( vif_port, details['port_id'], details['network_id'], details['network_type'], details['physical_network'], details['segmentation_id'], False, details['fixed_ips'], details['device_owner'], False) self.assertFalse(port_needs_binding) port_dead.assert_called_once_with(vif_port) update_device_down.assert_called_once_with( self.agent.context, details['port_id'], self.agent.agent_id, self.agent.conf.host) def test_bind_port_with_missing_network(self): vif_port = mock.Mock() vif_port.name.return_value = 'port' self.agent._bind_devices([{'network_id': 'non-existent', 'vif_port': vif_port}]) def _test_process_network_ports(self, port_info, skipped_devices=None): failed_devices = {'added': set(), 'removed': set()} skipped_devices = skipped_devices or [] added_devices = port_info.get('added', set()) with mock.patch.object(self.agent.sg_agent, "setup_port_filters") as setup_port_filters,\ mock.patch.object( self.agent, "treat_devices_added_or_updated", return_value=( skipped_devices, [], failed_devices['added'])) as device_added_updated,\ mock.patch.object(self.agent.int_br, "get_ports_attributes", return_value=[]),\ mock.patch.object(self.agent, "treat_devices_removed", return_value=( failed_devices[ 'removed'])) as device_removed,\ mock.patch.object(self.agent, "treat_devices_skipped", return_value=( skipped_devices)) as device_skipped: self.assertEqual( failed_devices, self.agent.process_network_ports(port_info, False)) setup_port_filters.assert_called_once_with( added_devices - set(skipped_devices), port_info.get('updated', set())) devices_added_updated = (added_devices | port_info.get('updated', set())) if devices_added_updated: device_added_updated.assert_called_once_with( devices_added_updated, False) if port_info.get('removed', set()): device_removed.assert_called_once_with(port_info['removed']) if skipped_devices: device_skipped.assert_called_once_with(set(skipped_devices)) def test_process_network_ports(self): self._test_process_network_ports( {'current': set(['tap0']), 'removed': set(['eth0']), 'added': set(['eth1'])}) def test_process_network_port_with_updated_ports(self): self._test_process_network_ports( {'current': set(['tap0', 'tap1']), 'updated': set(['tap1', 'eth1']), 'removed': set(['eth0']), 'added': set(['eth1'])}) def test_process_network_port_with_skipped_ports(self): port_info = {'current': set(['tap0', 'tap1']), 'removed': set(['eth0']), 'added': set(['eth1', 'eth2'])} self._test_process_network_ports(port_info, skipped_devices=['eth1']) def test_process_network_port_with_empty_port(self): self._test_process_network_ports({}) def test_hybrid_plug_flag_based_on_firewall(self): cfg.CONF.set_default( 'firewall_driver', 'neutron.agent.firewall.NoopFirewallDriver', group='SECURITYGROUP') agt = self._make_agent() self.assertFalse(agt.agent_state['configurations']['ovs_hybrid_plug']) cfg.CONF.set_default( 'firewall_driver', 'neutron.agent.linux.openvswitch_firewall.OVSFirewallDriver', group='SECURITYGROUP') with mock.patch('neutron.agent.linux.openvswitch_firewall.' 'OVSFirewallDriver.initialize_bridge'): agt = self._make_agent() self.assertFalse(agt.agent_state['configurations']['ovs_hybrid_plug']) cfg.CONF.set_default( 'firewall_driver', 'neutron.agent.linux.iptables_firewall.' 'OVSHybridIptablesFirewallDriver', group='SECURITYGROUP') with mock.patch('neutron.agent.linux.ip_conntrack.' 'IpConntrackManager._populate_initial_zone_map'): agt = self._make_agent() self.assertTrue(agt.agent_state['configurations']['ovs_hybrid_plug']) def test_report_state(self): with mock.patch.object(self.agent.state_rpc, "report_state") as report_st: self.agent.int_br_device_count = 5 self.systemd_notify.assert_not_called() self.agent._report_state() report_st.assert_called_with(self.agent.context, self.agent.agent_state, True) self.systemd_notify.assert_called_once_with() self.systemd_notify.reset_mock() # agent keeps sending "start_flag" while iter 0 not completed self.assertIn("start_flag", self.agent.agent_state) self.assertEqual( self.agent.agent_state["configurations"]["devices"], self.agent.int_br_device_count ) self.agent._report_state() report_st.assert_called_with(self.agent.context, self.agent.agent_state, True) self.systemd_notify.assert_not_called() def test_report_state_fail(self): with mock.patch.object(self.agent.state_rpc, "report_state") as report_st: report_st.side_effect = Exception() self.agent._report_state() report_st.assert_called_with(self.agent.context, self.agent.agent_state, True) self.agent._report_state() report_st.assert_called_with(self.agent.context, self.agent.agent_state, True) self.systemd_notify.assert_not_called() def test_report_state_revived(self): with mock.patch.object(self.agent.state_rpc, "report_state") as report_st: report_st.return_value = agent_consts.AGENT_REVIVED self.agent._report_state() self.assertTrue(self.agent.fullsync) def test_port_update(self): port = {"id": TEST_PORT_ID1, "network_id": TEST_NETWORK_ID1, "admin_state_up": False} self.agent.port_update("unused_context", port=port, network_type="vlan", segmentation_id="1", physical_network="physnet") self.assertEqual(set([TEST_PORT_ID1]), self.agent.updated_ports) def test_port_delete_after_update(self): """Make sure a port is not marked for delete and update.""" port = {'id': TEST_PORT_ID1} self.agent.port_update(context=None, port=port) self.agent.port_delete(context=None, port_id=port['id']) self.assertEqual(set(), self.agent.updated_ports) self.assertEqual(set([port['id']]), self.agent.deleted_ports) def test_process_deleted_ports_cleans_network_ports(self): self.agent._update_port_network(TEST_PORT_ID1, TEST_NETWORK_ID1) self.agent.port_delete(context=None, port_id=TEST_PORT_ID1) self.agent.sg_agent = mock.Mock() self.agent.int_br = mock.Mock() self.agent.process_deleted_ports(port_info={}) self.assertEqual(set(), self.agent.network_ports[TEST_NETWORK_ID1]) def test_network_update(self): """Network update marks port for update. """ network = {'id': TEST_NETWORK_ID1} port = {'id': TEST_PORT_ID1, 'network_id': network['id']} self.agent._update_port_network(port['id'], port['network_id']) self.agent.network_update(context=None, network=network) self.assertEqual(set([port['id']]), self.agent.updated_ports) def test_network_update_outoforder(self): """Network update arrives later than port_delete. But the main agent loop still didn't process the ports, so we ensure the port is not marked for update. """ network = {'id': TEST_NETWORK_ID1} port = {'id': TEST_PORT_ID1, 'network_id': network['id']} self.agent._update_port_network(port['id'], port['network_id']) self.agent.port_delete(context=None, port_id=port['id']) self.agent.network_update(context=None, network=network) self.assertEqual(set(), self.agent.updated_ports) def test_update_port_network(self): """Ensure ports are associated and moved across networks correctly.""" self.agent._update_port_network(TEST_PORT_ID1, TEST_NETWORK_ID1) self.agent._update_port_network(TEST_PORT_ID2, TEST_NETWORK_ID1) self.agent._update_port_network(TEST_PORT_ID3, TEST_NETWORK_ID2) self.agent._update_port_network(TEST_PORT_ID1, TEST_NETWORK_ID2) self.assertEqual(set([TEST_PORT_ID2]), self.agent.network_ports[TEST_NETWORK_ID1]) self.assertEqual(set([TEST_PORT_ID1, TEST_PORT_ID3]), self.agent.network_ports[TEST_NETWORK_ID2]) def test_port_delete(self): vif = FakeVif() with mock.patch.object(self.agent, 'int_br') as int_br: int_br.get_vif_by_port_id.return_value = vif.port_name int_br.get_vif_port_by_id.return_value = vif self.agent.port_delete("unused_context", port_id='id') self.agent.process_deleted_ports(port_info={}) # the main things we care about are that it gets put in the # dead vlan and gets blocked int_br.set_db_attribute.assert_any_call( 'Port', vif.port_name, 'tag', constants.DEAD_VLAN_TAG, log_errors=False) int_br.drop_port.assert_called_once_with(in_port=vif.ofport) def test_port_delete_removed_port(self): with mock.patch.object(self.agent, 'int_br') as int_br: self.agent.port_delete("unused_context", port_id='id') # if it was removed from the bridge, we shouldn't be processing it self.agent.process_deleted_ports(port_info={'removed': {'id', }}) self.assertFalse(int_br.set_db_attribute.called) self.assertFalse(int_br.drop_port.called) def _test_setup_physical_bridges(self, port_exists=False): with mock.patch.object(ip_lib.IPDevice, "exists") as devex_fn,\ mock.patch.object(sys, "exit"),\ mock.patch.object(self.agent, 'br_phys_cls') as phys_br_cls,\ mock.patch.object(self.agent, 'int_br') as int_br,\ mock.patch.object(self.agent, '_check_bridge_datapath_id'),\ mock.patch.object(ovs_lib.BaseOVS, 'get_bridges'): devex_fn.return_value = True parent = mock.MagicMock() phys_br = phys_br_cls() parent.attach_mock(phys_br_cls, 'phys_br_cls') parent.attach_mock(phys_br, 'phys_br') parent.attach_mock(int_br, 'int_br') if port_exists: phys_br.get_port_ofport.return_value = "phy_ofport" int_br.get_port_ofport.return_value = "int_ofport" else: phys_br.add_patch_port.return_value = "phy_ofport" int_br.add_patch_port.return_value = "int_ofport" phys_br.port_exists.return_value = port_exists int_br.port_exists.return_value = port_exists self.agent.setup_physical_bridges({"physnet1": "br-eth"}) expected_calls = [ mock.call.phys_br_cls('br-eth'), mock.call.phys_br.create(), mock.call.phys_br.set_secure_mode(), mock.call.phys_br.setup_controllers(mock.ANY), mock.call.phys_br.setup_default_table(), mock.call.int_br.db_get_val('Interface', 'int-br-eth', 'type', log_errors=False), # Have to use __getattr__ here to avoid mock._Call.__eq__ # method being called mock.call.int_br.db_get_val().__getattr__('__eq__')('veth'), mock.call.int_br.port_exists('int-br-eth'), ] if port_exists: expected_calls += [ mock.call.int_br.get_port_ofport('int-br-eth'), ] else: expected_calls += [ mock.call.int_br.add_patch_port( 'int-br-eth', constants.NONEXISTENT_PEER), ] expected_calls += [ mock.call.phys_br.port_exists('phy-br-eth'), ] if port_exists: expected_calls += [ mock.call.phys_br.get_port_ofport('phy-br-eth'), ] else: expected_calls += [ mock.call.phys_br.add_patch_port( 'phy-br-eth', constants.NONEXISTENT_PEER), ] expected_calls += [ mock.call.int_br.drop_port(in_port='int_ofport'), mock.call.phys_br.drop_port(in_port='phy_ofport'), mock.call.int_br.set_db_attribute('Interface', 'int-br-eth', 'options', {'peer': 'phy-br-eth'}), mock.call.phys_br.set_db_attribute('Interface', 'phy-br-eth', 'options', {'peer': 'int-br-eth'}), ] parent.assert_has_calls(expected_calls) self.assertEqual("int_ofport", self.agent.int_ofports["physnet1"]) self.assertEqual("phy_ofport", self.agent.phys_ofports["physnet1"]) def test_setup_physical_bridges(self): self._test_setup_physical_bridges() def test_setup_physical_bridges_port_exists(self): self._test_setup_physical_bridges(port_exists=True) def test_setup_physical_bridges_using_veth_interconnection(self): self.agent.use_veth_interconnection = True with mock.patch.object(ip_lib.IPDevice, "exists") as devex_fn,\ mock.patch.object(sys, "exit"),\ mock.patch.object(utils, "execute") as utilsexec_fn,\ mock.patch.object(self.agent, 'br_phys_cls') as phys_br_cls,\ mock.patch.object(self.agent, 'int_br') as int_br,\ mock.patch.object(self.agent, '_check_bridge_datapath_id'),\ mock.patch.object(ip_lib.IPWrapper, "add_veth") as addveth_fn,\ mock.patch.object(ip_lib.IpLinkCommand, "delete") as linkdel_fn,\ mock.patch.object(ip_lib.IpLinkCommand, "set_up"),\ mock.patch.object(ip_lib.IpLinkCommand, "set_mtu"),\ mock.patch.object(ovs_lib.BaseOVS, "get_bridges") as get_br_fn: devex_fn.return_value = True parent = mock.MagicMock() parent.attach_mock(utilsexec_fn, 'utils_execute') parent.attach_mock(linkdel_fn, 'link_delete') parent.attach_mock(addveth_fn, 'add_veth') addveth_fn.return_value = (ip_lib.IPDevice("int-br-eth1"), ip_lib.IPDevice("phy-br-eth1")) phys_br = phys_br_cls() phys_br.add_port.return_value = "phys_veth_ofport" int_br.add_port.return_value = "int_veth_ofport" get_br_fn.return_value = ["br-eth"] self.agent.setup_physical_bridges({"physnet1": "br-eth"}) expected_calls = [mock.call.link_delete(), mock.call.utils_execute(['udevadm', 'settle', '--timeout=10']), mock.call.add_veth('int-br-eth', 'phy-br-eth')] parent.assert_has_calls(expected_calls, any_order=False) self.assertEqual("int_veth_ofport", self.agent.int_ofports["physnet1"]) self.assertEqual("phys_veth_ofport", self.agent.phys_ofports["physnet1"]) int_br.add_port.assert_called_with("int-br-eth") phys_br.add_port.assert_called_with("phy-br-eth") def _test_setup_physical_bridges_change_from_veth_to_patch_conf( self, port_exists=False): with mock.patch.object(sys, "exit"),\ mock.patch.object(self.agent, 'br_phys_cls') as phys_br_cls,\ mock.patch.object(self.agent, 'int_br') as int_br,\ mock.patch.object(self.agent.int_br, 'db_get_val', return_value='veth'), \ mock.patch.object(self.agent, '_check_bridge_datapath_id'), \ mock.patch.object(ovs_lib.BaseOVS, 'get_bridges'): phys_br = phys_br_cls() parent = mock.MagicMock() parent.attach_mock(phys_br_cls, 'phys_br_cls') parent.attach_mock(phys_br, 'phys_br') parent.attach_mock(int_br, 'int_br') if port_exists: phys_br.get_port_ofport.return_value = "phy_ofport" int_br.get_port_ofport.return_value = "int_ofport" else: phys_br.add_patch_port.return_value = "phy_ofport" int_br.add_patch_port.return_value = "int_ofport" phys_br.port_exists.return_value = port_exists int_br.port_exists.return_value = port_exists self.agent.setup_physical_bridges({"physnet1": "br-eth"}) expected_calls = [ mock.call.phys_br_cls('br-eth'), mock.call.phys_br.create(), mock.call.phys_br.set_secure_mode(), mock.call.phys_br.setup_controllers(mock.ANY), mock.call.phys_br.setup_default_table(), mock.call.int_br.delete_port('int-br-eth'), mock.call.phys_br.delete_port('phy-br-eth'), mock.call.int_br.port_exists('int-br-eth'), ] if port_exists: expected_calls += [ mock.call.int_br.get_port_ofport('int-br-eth'), ] else: expected_calls += [ mock.call.int_br.add_patch_port( 'int-br-eth', constants.NONEXISTENT_PEER), ] expected_calls += [ mock.call.phys_br.port_exists('phy-br-eth'), ] if port_exists: expected_calls += [ mock.call.phys_br.get_port_ofport('phy-br-eth'), ] else: expected_calls += [ mock.call.phys_br.add_patch_port( 'phy-br-eth', constants.NONEXISTENT_PEER), ] expected_calls += [ mock.call.int_br.drop_port(in_port='int_ofport'), mock.call.phys_br.drop_port(in_port='phy_ofport'), mock.call.int_br.set_db_attribute('Interface', 'int-br-eth', 'options', {'peer': 'phy-br-eth'}), mock.call.phys_br.set_db_attribute('Interface', 'phy-br-eth', 'options', {'peer': 'int-br-eth'}), ] parent.assert_has_calls(expected_calls) self.assertEqual("int_ofport", self.agent.int_ofports["physnet1"]) self.assertEqual("phy_ofport", self.agent.phys_ofports["physnet1"]) def test_setup_physical_bridges_change_from_veth_to_patch_conf(self): self._test_setup_physical_bridges_change_from_veth_to_patch_conf() def test_setup_physical_bridges_change_from_veth_to_patch_conf_port_exists( self): self._test_setup_physical_bridges_change_from_veth_to_patch_conf( port_exists=True) def test_setup_tunnel_br(self): self.tun_br = mock.Mock() with mock.patch.object(self.agent.int_br, "add_patch_port", return_value=1) as int_patch_port,\ mock.patch.object(self.agent.tun_br, "add_patch_port", return_value=1) as tun_patch_port,\ mock.patch.object(self.agent.tun_br, 'bridge_exists', return_value=False),\ mock.patch.object(self.agent.tun_br, 'create') as create_tun,\ mock.patch.object(self.agent.tun_br, 'setup_controllers') as setup_controllers,\ mock.patch.object(self.agent.tun_br, 'port_exists', return_value=False),\ mock.patch.object(self.agent.int_br, 'port_exists', return_value=False),\ mock.patch.object(sys, "exit"): self.agent.setup_tunnel_br(None) self.agent.setup_tunnel_br() self.assertTrue(create_tun.called) self.assertTrue(setup_controllers.called) self.assertTrue(int_patch_port.called) self.assertTrue(tun_patch_port.called) def test_setup_tunnel_br_ports_exits_drop_flows(self): cfg.CONF.set_override('drop_flows_on_start', True, 'AGENT') with mock.patch.object(self.agent.tun_br, 'port_exists', return_value=True),\ mock.patch.object(self.agent, 'tun_br'),\ mock.patch.object(self.agent.int_br, 'port_exists', return_value=True),\ mock.patch.object(self.agent.tun_br, 'setup_controllers'),\ mock.patch.object(self.agent, 'patch_tun_ofport', new=2),\ mock.patch.object(self.agent, 'patch_int_ofport', new=2),\ mock.patch.object(self.agent.tun_br, 'uninstall_flows') as delete,\ mock.patch.object(self.agent.int_br, "add_patch_port") as int_patch_port,\ mock.patch.object(self.agent.tun_br, "add_patch_port") as tun_patch_port,\ mock.patch.object(sys, "exit"): self.agent.setup_tunnel_br(None) self.agent.setup_tunnel_br() self.assertFalse(int_patch_port.called) self.assertFalse(tun_patch_port.called) self.assertTrue(delete.called) def test_setup_tunnel_port(self): self.agent.tun_br = mock.Mock() self.agent.l2_pop = False self.agent.udp_vxlan_port = 8472 self.agent.tun_br_ofports['vxlan'] = {} self.agent.local_ip = '2.3.4.5' with mock.patch.object(self.agent.tun_br, "add_tunnel_port", return_value='6') as add_tun_port_fn,\ mock.patch.object(self.agent.tun_br, "add_flow"): self.agent._setup_tunnel_port(self.agent.tun_br, 'portname', '1.2.3.4', 'vxlan') self.assertTrue(add_tun_port_fn.called) def test_port_unbound(self): with mock.patch.object(self.agent, "reclaim_local_vlan") as reclvl_fn: self.agent.enable_tunneling = True lvm = mock.Mock() lvm.network_type = "gre" lvm.vif_ports = {"vif1": mock.Mock()} self.agent.vlan_manager.mapping["netuid12345"] = lvm self.agent.port_unbound("vif1", "netuid12345") self.assertTrue(reclvl_fn.called) lvm.vif_ports = {} self.agent.port_unbound("vif1", "netuid12345") self.assertEqual(2, reclvl_fn.call_count) lvm.vif_ports = {"vif1": mock.Mock()} self.agent.port_unbound("vif3", "netuid12345") self.assertEqual(2, reclvl_fn.call_count) def _prepare_l2_pop_ofports(self): lvm1 = mock.Mock() lvm1.network_type = 'gre' lvm1.vlan = 'vlan1' lvm1.segmentation_id = 'seg1' lvm1.tun_ofports = set(['1']) lvm2 = mock.Mock() lvm2.network_type = 'gre' lvm2.vlan = 'vlan2' lvm2.segmentation_id = 'seg2' lvm2.tun_ofports = set(['1', '2']) self.agent.vlan_manager.mapping = {'net1': lvm1, 'net2': lvm2} self.agent.tun_br_ofports = {'gre': {'1.1.1.1': '1', '2.2.2.2': '2'}} self.agent.arp_responder_enabled = True def test_fdb_ignore_network(self): self._prepare_l2_pop_ofports() fdb_entry = {'net3': {}} with mock.patch.object(self.agent.tun_br, 'add_flow') as add_flow_fn,\ mock.patch.object(self.agent.tun_br, 'uninstall_flows') as del_flow_fn,\ mock.patch.object(self.agent, '_setup_tunnel_port') as add_tun_fn,\ mock.patch.object(self.agent, 'cleanup_tunnel_port') as clean_tun_fn: self.agent.fdb_add(None, fdb_entry) self.assertFalse(add_flow_fn.called) self.assertFalse(add_tun_fn.called) self.agent.fdb_remove(None, fdb_entry) self.assertFalse(del_flow_fn.called) self.assertFalse(clean_tun_fn.called) def test_fdb_ignore_self(self): self._prepare_l2_pop_ofports() self.agent.local_ip = 'agent_ip' fdb_entry = {'net2': {'network_type': 'gre', 'segment_id': 'tun2', 'ports': {'agent_ip': [l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1), n_const.FLOODING_ENTRY]}}} with mock.patch.object(self.agent.tun_br, "deferred") as defer_fn: self.agent.fdb_add(None, fdb_entry) self.assertFalse(defer_fn.called) self.agent.fdb_remove(None, fdb_entry) self.assertFalse(defer_fn.called) def test_fdb_add_flows(self): self._prepare_l2_pop_ofports() fdb_entry = {'net1': {'network_type': 'gre', 'segment_id': 'tun1', 'ports': {'2.2.2.2': [l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1), n_const.FLOODING_ENTRY]}}} with mock.patch.object(self.agent, 'tun_br', autospec=True) as tun_br,\ mock.patch.object(self.agent, '_setup_tunnel_port', autospec=True) as add_tun_fn: self.agent.fdb_add(None, fdb_entry) self.assertFalse(add_tun_fn.called) deferred_br_call = mock.call.deferred().__enter__() expected_calls = [ deferred_br_call.install_arp_responder('vlan1', FAKE_IP1, FAKE_MAC), deferred_br_call.install_unicast_to_tun('vlan1', 'seg1', '2', FAKE_MAC), deferred_br_call.install_flood_to_tun('vlan1', 'seg1', set(['1', '2'])), ] tun_br.assert_has_calls(expected_calls) def test_fdb_del_flows(self): self._prepare_l2_pop_ofports() fdb_entry = {'net2': {'network_type': 'gre', 'segment_id': 'tun2', 'ports': {'2.2.2.2': [l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1), n_const.FLOODING_ENTRY]}}} with mock.patch.object(self.agent, 'tun_br', autospec=True) as br_tun: self.agent.fdb_remove(None, fdb_entry) deferred_br_call = mock.call.deferred().__enter__() expected_calls = [ mock.call.deferred(), mock.call.deferred().__enter__(), deferred_br_call.delete_arp_responder('vlan2', FAKE_IP1), deferred_br_call.delete_unicast_to_tun('vlan2', FAKE_MAC), deferred_br_call.install_flood_to_tun('vlan2', 'seg2', set(['1'])), deferred_br_call.delete_port('gre-02020202'), deferred_br_call.cleanup_tunnel_port('2'), mock.call.deferred().__exit__(None, None, None), ] br_tun.assert_has_calls(expected_calls) def test_fdb_add_port(self): self._prepare_l2_pop_ofports() fdb_entry = {'net1': {'network_type': 'gre', 'segment_id': 'tun1', 'ports': {'1.1.1.1': [l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1)]}}} with mock.patch.object(self.agent, 'tun_br', autospec=True) as tun_br,\ mock.patch.object(self.agent, '_setup_tunnel_port') as add_tun_fn: self.agent.fdb_add(None, fdb_entry) self.assertFalse(add_tun_fn.called) fdb_entry['net1']['ports']['10.10.10.10'] = [ l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1)] self.agent.fdb_add(None, fdb_entry) deferred_br = tun_br.deferred().__enter__() add_tun_fn.assert_called_with( deferred_br, 'gre-0a0a0a0a', '10.10.10.10', 'gre') def test_fdb_del_port(self): self._prepare_l2_pop_ofports() fdb_entry = {'net2': {'network_type': 'gre', 'segment_id': 'tun2', 'ports': {'2.2.2.2': [n_const.FLOODING_ENTRY]}}} with mock.patch.object(self.agent.tun_br, 'deferred') as defer_fn,\ mock.patch.object(self.agent.tun_br, 'delete_port') as delete_port_fn: self.agent.fdb_remove(None, fdb_entry) deferred_br = defer_fn().__enter__() deferred_br.delete_port.assert_called_once_with('gre-02020202') self.assertFalse(delete_port_fn.called) def test_fdb_update_chg_ip(self): self._prepare_l2_pop_ofports() fdb_entries = {'chg_ip': {'net1': {'agent_ip': {'before': [l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1)], 'after': [l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP2)]}}}} with mock.patch.object(self.agent.tun_br, 'deferred') as deferred_fn: self.agent.fdb_update(None, fdb_entries) deferred_br = deferred_fn().__enter__() deferred_br.assert_has_calls([ mock.call.install_arp_responder('vlan1', FAKE_IP2, FAKE_MAC), mock.call.delete_arp_responder('vlan1', FAKE_IP1) ]) def test_del_fdb_flow_idempotency(self): lvm = mock.Mock() lvm.network_type = 'gre' lvm.vlan = 'vlan1' lvm.segmentation_id = 'seg1' lvm.tun_ofports = set(['1', '2']) with mock.patch.object(self.agent.tun_br, 'mod_flow') as mod_flow_fn,\ mock.patch.object(self.agent.tun_br, 'uninstall_flows') as uninstall_flows_fn: self.agent.del_fdb_flow(self.agent.tun_br, n_const.FLOODING_ENTRY, '1.1.1.1', lvm, '3') self.assertFalse(mod_flow_fn.called) self.assertFalse(uninstall_flows_fn.called) def test_recl_lv_port_to_preserve(self): self._prepare_l2_pop_ofports() self.agent.l2_pop = True self.agent.enable_tunneling = True with mock.patch.object(self.agent, 'tun_br', autospec=True) as tun_br: self.agent.reclaim_local_vlan('net1') self.assertFalse(tun_br.cleanup_tunnel_port.called) def test_recl_lv_port_to_remove(self): self._prepare_l2_pop_ofports() self.agent.l2_pop = True self.agent.enable_tunneling = True with mock.patch.object(self.agent, 'tun_br', autospec=True) as tun_br: self.agent.reclaim_local_vlan('net2') tun_br.delete_port.assert_called_once_with('gre-02020202') def _test_ext_br_recreated(self, setup_bridges_side_effect): bridge_mappings = {'physnet0': 'br-ex0', 'physnet1': 'br-ex1'} ex_br_mocks = [mock.Mock(br_name='br-ex0'), mock.Mock(br_name='br-ex1')] phys_bridges = {'physnet0': ex_br_mocks[0], 'physnet1': ex_br_mocks[1]}, bm_mock = mock.Mock() bridges_added = ['br-ex0'] expected_added_bridges = ( bridges_added if setup_bridges_side_effect else []) with mock.patch( 'neutron.agent.linux.ovsdb_monitor.get_bridges_monitor', return_value=bm_mock),\ mock.patch.object( self.agent, 'check_ovs_status', return_value=constants.OVS_NORMAL),\ mock.patch.object( self.agent, '_agent_has_updates', side_effect=TypeError('loop exit')),\ mock.patch.dict( self.agent.bridge_mappings, bridge_mappings, clear=True),\ mock.patch.dict( self.agent.phys_brs, phys_bridges, clear=True),\ mock.patch.object( self.agent, 'setup_physical_bridges') as setup_physical_bridges: bm_mock.bridges_added = bridges_added setup_physical_bridges.side_effect = setup_bridges_side_effect try: self.agent.rpc_loop(polling_manager=mock.Mock(), bridges_monitor=bm_mock) except TypeError: pass # Setup bridges should be called once even if it will raise Runtime # Error because there is raised TypeError in _agent_has_updates to stop # agent after first loop iteration setup_physical_bridges.assert_called_once_with({'physnet0': 'br-ex0'}) self.assertEqual(expected_added_bridges, self.agent.added_bridges) def test_ext_br_recreated(self): self._test_ext_br_recreated(setup_bridges_side_effect=None) def test_ext_br_recreated_fail_setup_physical_bridge(self): self._test_ext_br_recreated(setup_bridges_side_effect=RuntimeError) def test_daemon_loop_uses_polling_manager(self): ex_br_mock = mock.Mock(br_name="br-ex0") with mock.patch( 'neutron.agent.common.polling.get_polling_manager' ) as mock_get_pm, mock.patch( 'neutron.agent.linux.ovsdb_monitor.get_bridges_monitor' ) as mock_get_bm, mock.patch.object( self.agent, 'rpc_loop' ) as mock_loop, mock.patch.dict( self.agent.phys_brs, {'physnet0': ex_br_mock}, clear=True): self.agent.daemon_loop() mock_get_pm.assert_called_with(True, constants.DEFAULT_OVSDBMON_RESPAWN) mock_get_bm.assert_called_once_with( ['br-ex0'], constants.DEFAULT_OVSDBMON_RESPAWN) mock_loop.assert_called_once_with( polling_manager=mock.ANY, bridges_monitor=mock.ANY) def test_setup_tunnel_port_invalid_ofport(self): remote_ip = '1.2.3.4' with mock.patch.object( self.agent.tun_br, 'add_tunnel_port', return_value=ovs_lib.INVALID_OFPORT) as add_tunnel_port_fn,\ mock.patch.object(self.mod_agent.LOG, 'error') as log_error_fn: self.agent.local_ip = '1.2.3.4' ofport = self.agent._setup_tunnel_port( self.agent.tun_br, 'gre-1', remote_ip, n_const.TYPE_GRE) add_tunnel_port_fn.assert_called_once_with( 'gre-1', remote_ip, self.agent.local_ip, n_const.TYPE_GRE, self.agent.vxlan_udp_port, self.agent.dont_fragment, self.agent.tunnel_csum, self.agent.tos) log_error_fn.assert_called_once_with( _("Failed to set-up %(type)s tunnel port to %(ip)s"), {'type': n_const.TYPE_GRE, 'ip': remote_ip}) self.assertEqual(0, ofport) def test_setup_tunnel_port_invalid_address_mismatch(self): remote_ip = '2001:db8::2' with mock.patch.object(self.mod_agent.LOG, 'error') as log_error_fn: self.agent.local_ip = '1.2.3.4' ofport = self.agent._setup_tunnel_port( self.agent.tun_br, 'gre-1', remote_ip, n_const.TYPE_GRE) log_error_fn.assert_called_once_with( _("IP version mismatch, cannot create tunnel: " "local_ip=%(lip)s remote_ip=%(rip)s"), {'lip': self.agent.local_ip, 'rip': remote_ip}) self.assertEqual(0, ofport) def test_setup_tunnel_port_invalid_netaddr_exception(self): remote_ip = '2001:db8::2' with mock.patch.object(self.mod_agent.LOG, 'error') as log_error_fn: self.agent.local_ip = '1.2.3.4.5' ofport = self.agent._setup_tunnel_port( self.agent.tun_br, 'gre-1', remote_ip, n_const.TYPE_GRE) log_error_fn.assert_called_once_with( _("Invalid local or remote IP, cannot create tunnel: " "local_ip=%(lip)s remote_ip=%(rip)s"), {'lip': self.agent.local_ip, 'rip': remote_ip}) self.assertEqual(0, ofport) def test_setup_tunnel_port_error_negative_df_disabled(self): remote_ip = '1.2.3.4' with mock.patch.object( self.agent.tun_br, 'add_tunnel_port', return_value=ovs_lib.INVALID_OFPORT) as add_tunnel_port_fn,\ mock.patch.object(self.mod_agent.LOG, 'error') as log_error_fn: self.agent.dont_fragment = False self.agent.tunnel_csum = False self.agent.local_ip = '2.3.4.5' ofport = self.agent._setup_tunnel_port( self.agent.tun_br, 'gre-1', remote_ip, n_const.TYPE_GRE) add_tunnel_port_fn.assert_called_once_with( 'gre-1', remote_ip, self.agent.local_ip, n_const.TYPE_GRE, self.agent.vxlan_udp_port, self.agent.dont_fragment, self.agent.tunnel_csum, self.agent.tos) log_error_fn.assert_called_once_with( _("Failed to set-up %(type)s tunnel port to %(ip)s"), {'type': n_const.TYPE_GRE, 'ip': remote_ip}) self.assertEqual(0, ofport) def test_setup_tunnel_port_error_negative_tunnel_csum(self): remote_ip = '1.2.3.4' with mock.patch.object( self.agent.tun_br, 'add_tunnel_port', return_value=ovs_lib.INVALID_OFPORT) as add_tunnel_port_fn,\ mock.patch.object(self.mod_agent.LOG, 'error') as log_error_fn: self.agent.dont_fragment = True self.agent.tunnel_csum = True self.agent.local_ip = '2.3.4.5' ofport = self.agent._setup_tunnel_port( self.agent.tun_br, 'gre-1', remote_ip, n_const.TYPE_GRE) add_tunnel_port_fn.assert_called_once_with( 'gre-1', remote_ip, self.agent.local_ip, n_const.TYPE_GRE, self.agent.vxlan_udp_port, self.agent.dont_fragment, self.agent.tunnel_csum, self.agent.tos) log_error_fn.assert_called_once_with( _("Failed to set-up %(type)s tunnel port to %(ip)s"), {'type': n_const.TYPE_GRE, 'ip': remote_ip}) self.assertEqual(0, ofport) def test_setup_tunnel_port_error_negative_tos_inherit(self): remote_ip = '1.2.3.4' with mock.patch.object( self.agent.tun_br, 'add_tunnel_port', return_value=ovs_lib.INVALID_OFPORT) as add_tunnel_port_fn,\ mock.patch.object(self.mod_agent.LOG, 'error') as log_error_fn: self.agent.tos = 'inherit' self.agent.local_ip = '2.3.4.5' ofport = self.agent._setup_tunnel_port( self.agent.tun_br, 'gre-1', remote_ip, n_const.TYPE_GRE) add_tunnel_port_fn.assert_called_once_with( 'gre-1', remote_ip, self.agent.local_ip, n_const.TYPE_GRE, self.agent.vxlan_udp_port, self.agent.dont_fragment, self.agent.tunnel_csum, self.agent.tos) log_error_fn.assert_called_once_with( _("Failed to set-up %(type)s tunnel port to %(ip)s"), {'type': n_const.TYPE_GRE, 'ip': remote_ip}) self.assertEqual(0, ofport) def test_tunnel_sync_with_ml2_plugin(self): fake_tunnel_details = {'tunnels': [{'ip_address': '100.101.31.15'}]} with mock.patch.object(self.agent.plugin_rpc, 'tunnel_sync', return_value=fake_tunnel_details),\ mock.patch.object( self.agent, '_setup_tunnel_port') as _setup_tunnel_port_fn,\ mock.patch.object(self.agent, 'cleanup_stale_flows') as cleanup: self.agent.tunnel_types = ['vxlan'] self.agent.tunnel_sync() expected_calls = [mock.call(self.agent.tun_br, 'vxlan-64651f0f', '100.101.31.15', 'vxlan')] _setup_tunnel_port_fn.assert_has_calls(expected_calls) self.assertEqual([], cleanup.mock_calls) def test_tunnel_sync_invalid_ip_address(self): fake_tunnel_details = {'tunnels': [{'ip_address': '300.300.300.300'}, {'ip_address': '100.100.100.100'}]} with mock.patch.object(self.agent.plugin_rpc, 'tunnel_sync', return_value=fake_tunnel_details),\ mock.patch.object( self.agent, '_setup_tunnel_port') as _setup_tunnel_port_fn,\ mock.patch.object(self.agent, 'cleanup_stale_flows') as cleanup: self.agent.tunnel_types = ['vxlan'] self.agent.tunnel_sync() _setup_tunnel_port_fn.assert_called_once_with(self.agent.tun_br, 'vxlan-64646464', '100.100.100.100', 'vxlan') self.assertEqual([], cleanup.mock_calls) def test_tunnel_sync_setup_tunnel_flood_flow_once(self): fake_tunnel_details = {'tunnels': [{'ip_address': '200.200.200.200'}, {'ip_address': '100.100.100.100'}]} with mock.patch.object(self.agent.plugin_rpc, 'tunnel_sync', return_value=fake_tunnel_details),\ mock.patch.object( self.agent, '_setup_tunnel_port') as _setup_tunnel_port_fn,\ mock.patch.object( self.agent, '_setup_tunnel_flood_flow') as _setup_tunnel_flood_flow: self.agent.tunnel_types = ['vxlan'] self.agent.tunnel_sync() expected_calls = [mock.call(self.agent.tun_br, 'vxlan-c8c8c8c8', '200.200.200.200', 'vxlan'), mock.call(self.agent.tun_br, 'vxlan-64646464', '100.100.100.100', 'vxlan')] _setup_tunnel_port_fn.assert_has_calls(expected_calls) _setup_tunnel_flood_flow.assert_called_once_with(self.agent.tun_br, 'vxlan') def test_tunnel_update(self): kwargs = {'tunnel_ip': '10.10.10.10', 'tunnel_type': 'gre'} self.agent._setup_tunnel_port = mock.Mock() self.agent.enable_tunneling = True self.agent.tunnel_types = ['gre'] self.agent.l2_pop = False self.agent.tunnel_update(context=None, **kwargs) expected_calls = [ mock.call(self.agent.tun_br, 'gre-0a0a0a0a', '10.10.10.10', 'gre')] self.agent._setup_tunnel_port.assert_has_calls(expected_calls) def test_tunnel_delete(self): kwargs = {'tunnel_ip': '10.10.10.10', 'tunnel_type': 'gre'} self.agent.enable_tunneling = True self.agent.tunnel_types = ['gre'] self.agent.tun_br_ofports = {'gre': {'10.10.10.10': '1'}} with mock.patch.object( self.agent, 'cleanup_tunnel_port' ) as clean_tun_fn: self.agent.tunnel_delete(context=None, **kwargs) self.assertTrue(clean_tun_fn.called) def test_reset_tunnel_ofports(self): tunnel_handles = self.agent.tun_br_ofports self.agent.tun_br_ofports = {'gre': {'10.10.10.10': '1'}} self.agent._reset_tunnel_ofports() self.assertEqual(self.agent.tun_br_ofports, tunnel_handles) def _test_ovs_status(self, *args): reply2 = {'current': set(['tap0']), 'added': set(['tap2']), 'removed': set([])} reply3 = {'current': set(['tap2']), 'added': set([]), 'removed': set(['tap0'])} reply_ancillary = {'current': set([]), 'added': set([]), 'removed': set([])} self.agent.enable_tunneling = True with mock.patch.object(async_process.AsyncProcess, "_spawn"),\ mock.patch.object(async_process.AsyncProcess, "start"),\ mock.patch.object(async_process.AsyncProcess, "is_active", return_value=True),\ mock.patch.object(async_process.AsyncProcess, "stop"),\ mock.patch.object(log.KeywordArgumentAdapter, 'exception') as log_exception,\ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'process_ports_events') as process_p_events,\ mock.patch.object( self.mod_agent.OVSNeutronAgent, 'process_network_ports') as process_network_ports,\ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'check_ovs_status') as check_ovs_status,\ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_integration_br') as setup_int_br,\ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_physical_bridges') as setup_phys_br,\ mock.patch.object(time, 'sleep'),\ mock.patch.object( self.mod_agent.OVSNeutronAgent, 'update_stale_ofport_rules') as update_stale, \ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'cleanup_stale_flows') as cleanup, \ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_tunnel_br') as setup_tunnel_br,\ mock.patch.object( self.mod_agent.OVSNeutronAgent, 'setup_tunnel_br_flows') as setup_tunnel_br_flows,\ mock.patch.object( self.mod_agent.OVSNeutronAgent, '_reset_tunnel_ofports') as reset_tunnel_ofports,\ mock.patch.object(self.agent.state_rpc, 'report_state') as report_st: log_exception.side_effect = Exception( 'Fake exception to get out of the loop') devices_not_ready = set() process_p_events.side_effect = [(reply2, reply_ancillary, devices_not_ready), (reply3, reply_ancillary, devices_not_ready)] failed_devices = {'added': set(), 'removed': set()} failed_ancillary_devices = {'added': set(), 'removed': set()} process_network_ports.side_effect = [ failed_devices, Exception('Fake exception to get out of the loop')] check_ovs_status.side_effect = args if self.agent.enable_tunneling: self.agent.agent_state.pop("start_flag") try: self.agent.daemon_loop() except Exception: pass process_p_events.assert_has_calls([ mock.call({'removed': [], 'added': []}, set(), set(), set(), failed_devices, failed_ancillary_devices, set()), mock.call({'removed': [], 'added': []}, set(['tap0']), set(), set(), failed_devices, failed_ancillary_devices, set()) ]) process_network_ports.assert_has_calls([ mock.call(reply2, False), mock.call(reply3, True) ]) cleanup.assert_called_once_with() self.assertTrue(update_stale.called) # Verify the OVS restart we triggered in the loop # re-setup the bridges setup_int_br.assert_has_calls([mock.call()]) setup_phys_br.assert_has_calls([mock.call({})]) # Ensure that tunnel handles are reset and bridge # and flows reconfigured. self.assertTrue(reset_tunnel_ofports.called) self.assertTrue(setup_tunnel_br_flows.called) self.assertTrue(setup_tunnel_br.called) if self.agent.enable_tunneling: self.agent.agent_state['start_flag'] = True report_st.assert_called_once_with( self.agent.context, self.agent.agent_state, True) def test_ovs_status(self): self._test_ovs_status(constants.OVS_NORMAL, constants.OVS_DEAD, constants.OVS_RESTARTED) # OVS will not DEAD in some exception, like DBConnectionError. self._test_ovs_status(constants.OVS_NORMAL, constants.OVS_RESTARTED) def test_rpc_loop_fail_to_process_network_ports_keep_flows(self): with mock.patch.object(async_process.AsyncProcess, "_spawn"),\ mock.patch.object(async_process.AsyncProcess, "start"),\ mock.patch.object(async_process.AsyncProcess, "is_active", return_value=True),\ mock.patch.object(async_process.AsyncProcess, "stop"),\ mock.patch.object( self.mod_agent.OVSNeutronAgent, 'process_network_ports') as process_network_ports,\ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'check_ovs_status') as check_ovs_status,\ mock.patch.object(time, 'sleep'),\ mock.patch.object( self.mod_agent.OVSNeutronAgent, 'update_stale_ofport_rules') as update_stale, \ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'cleanup_stale_flows') as cleanup,\ mock.patch.object( self.mod_agent.OVSNeutronAgent, '_check_and_handle_signal') as check_and_handle_signal: process_network_ports.side_effect = Exception("Trigger resync") check_ovs_status.return_value = constants.OVS_NORMAL check_and_handle_signal.side_effect = [True, False] self.agent.daemon_loop() self.assertTrue(update_stale.called) self.assertFalse(cleanup.called) def test_set_rpc_timeout(self): with mock.patch.object( n_rpc.BackingOffClient, 'set_max_timeout') as smt: self.agent._handle_sigterm(None, None) for rpc_client in (self.agent.plugin_rpc.client, self.agent.sg_plugin_rpc.client, self.agent.dvr_plugin_rpc.client, self.agent.state_rpc.client): smt.assert_called_with(10) def test_set_rpc_timeout_no_value(self): self.agent.quitting_rpc_timeout = None with mock.patch.object(self.agent, 'set_rpc_timeout') as mock_set_rpc: self.agent._handle_sigterm(None, None) self.assertFalse(mock_set_rpc.called) def test_arp_spoofing_network_port(self): int_br = mock.create_autospec(self.agent.int_br) self.agent.setup_arp_spoofing_protection( int_br, FakeVif(), {'device_owner': n_const.DEVICE_OWNER_ROUTER_INTF}) self.assertTrue(int_br.delete_arp_spoofing_protection.called) self.assertFalse(int_br.install_arp_spoofing_protection.called) def test_arp_spoofing_port_security_disabled(self): int_br = mock.create_autospec(self.agent.int_br) self.agent.setup_arp_spoofing_protection( int_br, FakeVif(), {'port_security_enabled': False}) self.assertTrue(int_br.delete_arp_spoofing_protection.called) self.assertFalse(int_br.install_arp_spoofing_protection.called) def test_arp_spoofing_basic_rule_setup(self): vif = FakeVif() fake_details = {'fixed_ips': [], 'device_owner': 'nobody'} self.agent.prevent_arp_spoofing = True int_br = mock.create_autospec(self.agent.int_br) self.agent.setup_arp_spoofing_protection(int_br, vif, fake_details) self.assertEqual( [mock.call(port=vif.ofport)], int_br.delete_arp_spoofing_allow_rules.mock_calls) self.assertEqual( [mock.call(ip_addresses=set(), port=vif.ofport)], int_br.install_arp_spoofing_protection.mock_calls) def test_arp_spoofing_basic_rule_setup_fixed_ipv6(self): vif = FakeVif() fake_details = {'fixed_ips': [{'ip_address': 'fdf8:f53b:82e4::1'}], 'device_owner': 'nobody'} self.agent.prevent_arp_spoofing = True br = mock.create_autospec(self.agent.int_br) self.agent.setup_arp_spoofing_protection(br, vif, fake_details) self.assertEqual( [mock.call(port=vif.ofport)], br.delete_arp_spoofing_allow_rules.mock_calls) self.assertTrue(br.install_icmpv6_na_spoofing_protection.called) def test_arp_spoofing_fixed_and_allowed_addresses(self): vif = FakeVif() fake_details = { 'device_owner': 'nobody', 'fixed_ips': [{'ip_address': '192.168.44.100'}, {'ip_address': '192.168.44.101'}], 'allowed_address_pairs': [{'ip_address': '192.168.44.102/32'}, {'ip_address': '192.168.44.103/32'}] } self.agent.prevent_arp_spoofing = True int_br = mock.create_autospec(self.agent.int_br) self.agent.setup_arp_spoofing_protection(int_br, vif, fake_details) # make sure all addresses are allowed addresses = {'192.168.44.100', '192.168.44.101', '192.168.44.102/32', '192.168.44.103/32'} self.assertEqual( [mock.call(port=vif.ofport, ip_addresses=addresses)], int_br.install_arp_spoofing_protection.mock_calls) def test_arp_spoofing_fixed_and_allowed_addresses_ipv6(self): vif = FakeVif() fake_details = { 'device_owner': 'nobody', 'fixed_ips': [{'ip_address': '2001:db8::1'}, {'ip_address': '2001:db8::2'}], 'allowed_address_pairs': [{'ip_address': '2001:db8::200', 'mac_address': 'aa:22:33:44:55:66'}] } self.agent.prevent_arp_spoofing = True int_br = mock.create_autospec(self.agent.int_br) self.agent.setup_arp_spoofing_protection(int_br, vif, fake_details) # make sure all addresses are allowed including ipv6 LLAs addresses = {'2001:db8::1', '2001:db8::2', '2001:db8::200', 'fe80::a822:33ff:fe44:5566', 'fe80::a8bb:ccff:fe11:2233'} self.assertEqual( [mock.call(port=vif.ofport, ip_addresses=addresses)], int_br.install_icmpv6_na_spoofing_protection.mock_calls) def test__get_ofport_moves(self): previous = {'port1': 1, 'port2': 2} current = {'port1': 5, 'port2': 2} # we expect it to tell us port1 moved expected = ['port1'] self.assertEqual(expected, self.agent._get_ofport_moves(current, previous)) def test_update_stale_ofport_rules_clears_old(self): self.agent.prevent_arp_spoofing = True self.agent.vifname_to_ofport_map = {'port1': 1, 'port2': 2} self.agent.int_br = mock.Mock() # simulate port1 was removed newmap = {'port2': 2} self.agent.int_br.get_vif_port_to_ofport_map.return_value = newmap self.agent.update_stale_ofport_rules() # rules matching port 1 should have been deleted self.assertEqual( [mock.call(port=1)], self.agent.int_br.delete_arp_spoofing_protection.mock_calls) # make sure the state was updated with the new map self.assertEqual(newmap, self.agent.vifname_to_ofport_map) def test_update_stale_ofport_rules_treats_moved(self): self.agent.prevent_arp_spoofing = True self.agent.vifname_to_ofport_map = {'port1': 1, 'port2': 2} self.agent.treat_devices_added_or_updated = mock.Mock() self.agent.int_br = mock.Mock() # simulate port1 was moved newmap = {'port2': 2, 'port1': 90} self.agent.int_br.get_vif_port_to_ofport_map.return_value = newmap ofport_changed_ports = self.agent.update_stale_ofport_rules() self.assertEqual(['port1'], ofport_changed_ports) def test_update_stale_ofport_rules_removes_drop_flow(self): self.agent.prevent_arp_spoofing = False self.agent.vifname_to_ofport_map = {'port1': 1, 'port2': 2} self.agent.int_br = mock.Mock() # simulate port1 was removed newmap = {'port2': 2} self.agent.int_br.get_vif_port_to_ofport_map.return_value = newmap self.agent.update_stale_ofport_rules() # drop flow rule matching port 1 should have been deleted ofport_changed_ports = self.agent.update_stale_ofport_rules() expected = [ mock.call(in_port=1) ] self.assertEqual(expected, self.agent.int_br.uninstall_flows.mock_calls) self.assertEqual(newmap, self.agent.vifname_to_ofport_map) self.assertFalse( self.agent.int_br.delete_arp_spoofing_protection.called) self.assertEqual([], ofport_changed_ports) def test__setup_tunnel_port_while_new_mapping_is_added(self): """ Test that _setup_tunnel_port doesn't fail if new vlan mapping is added in a different coroutine while iterating over existing mappings. See bug 1449944 for more info. """ def add_new_vlan_mapping(*args, **kwargs): self.agent.vlan_manager.add('bar', 1, 2, 3, 4) bridge = mock.Mock() tunnel_type = 'vxlan' self.agent.tun_br_ofports = {tunnel_type: dict()} self.agent.l2_pop = False self.agent.vlan_manager.add('foo', 4, tunnel_type, 2, 1) self.agent.local_ip = '2.3.4.5' bridge.install_flood_to_tun.side_effect = add_new_vlan_mapping self.agent._setup_tunnel_port(bridge, 1, '1.2.3.4', tunnel_type=tunnel_type) self.agent._setup_tunnel_flood_flow(bridge, tunnel_type) self.assertIn('bar', self.agent.vlan_manager) def test_setup_entry_for_arp_reply_ignores_ipv6_addresses(self): self.agent.arp_responder_enabled = True ip = '2001:db8::1' br = mock.Mock() self.agent.setup_entry_for_arp_reply( br, 'add', mock.Mock(), mock.Mock(), ip) self.assertFalse(br.install_arp_responder.called) def test__check_bridge_datapath_id(self): datapath_id = u'0000622486fa3f42' datapath_ids_set = set() for i in range(5): dpid = format((i << 48) + int(datapath_id, 16), '0x').zfill(16) bridge = mock.Mock() bridge.br_name = 'bridge_%s' % i bridge.get_datapath_id = mock.Mock(return_value=datapath_id) self.agent._check_bridge_datapath_id(bridge, datapath_ids_set) self.assertEqual(i + 1, len(datapath_ids_set)) self.assertIn(dpid, datapath_ids_set) if i == 0: bridge.set_datapath_id.assert_not_called() else: bridge.set_datapath_id.assert_called_once_with(dpid) class TestOvsNeutronAgentOFCtl(TestOvsNeutronAgent, ovs_test_base.OVSOFCtlTestBase): def test_cleanup_stale_flows(self): with mock.patch.object(self.agent.int_br, 'dump_flows_all_tables') as dump_flows,\ mock.patch.object(self.agent.int_br, 'delete_flows') as del_flow: self.agent.int_br.set_agent_uuid_stamp(1234) dump_flows.return_value = [ 'cookie=0x4d2, duration=50.156s, table=0,actions=drop', 'cookie=0x4321, duration=54.143s, table=2, priority=0', 'cookie=0x2345, duration=50.125s, table=2, priority=0', 'cookie=0x4d2, duration=52.112s, table=3, actions=drop', ] self.agent.iter_num = 3 self.agent.cleanup_stale_flows() expected = [ mock.call(cookie='0x4321/-1', table='2'), mock.call(cookie='0x2345/-1', table='2'), ] self.assertEqual(expected, del_flow.mock_calls) class TestOvsNeutronAgentRyu(TestOvsNeutronAgent, ovs_test_base.OVSRyuTestBase): def test_cleanup_stale_flows(self): uint64_max = (1 << 64) - 1 with mock.patch.object(self.agent.int_br, 'dump_flows') as dump_flows,\ mock.patch.object(self.agent.int_br, 'uninstall_flows') as uninstall_flows: self.agent.int_br.set_agent_uuid_stamp(1234) fake_flows = [ # mock ryu.ofproto.ofproto_v1_3_parser.OFPFlowStats mock.Mock(cookie=1234, table_id=0), mock.Mock(cookie=17185, table_id=2), mock.Mock(cookie=9029, table_id=2), mock.Mock(cookie=1234, table_id=3), ] dump_flows.return_value = fake_flows self.agent.iter_num = 3 self.agent.cleanup_stale_flows() dump_flows_expected = [ mock.call(tid) for tid in constants.INT_BR_ALL_TABLES] dump_flows.assert_has_calls(dump_flows_expected) expected = [mock.call(cookie=17185, cookie_mask=uint64_max), mock.call(cookie=9029, cookie_mask=uint64_max)] uninstall_flows.assert_has_calls(expected, any_order=True) self.assertEqual(len(constants.INT_BR_ALL_TABLES) * len(expected), len(uninstall_flows.mock_calls)) class AncillaryBridgesTest(object): def setUp(self): super(AncillaryBridgesTest, self).setUp() conn_patcher = mock.patch( 'neutron.agent.ovsdb.impl_idl._connection') conn_patcher.start() self.addCleanup(conn_patcher.stop) mock.patch(PULLAPI).start() notifier_p = mock.patch(NOTIFIER) notifier_cls = notifier_p.start() self.notifier = mock.Mock() notifier_cls.return_value = self.notifier cfg.CONF.set_default('firewall_driver', 'neutron.agent.firewall.NoopFirewallDriver', group='SECURITYGROUP') cfg.CONF.set_override('report_interval', 0, 'AGENT') mock.patch('neutron.agent.common.ovs_lib.BaseOVS.config', new_callable=mock.PropertyMock, return_value={}).start() def _test_ancillary_bridges(self, bridges, ancillary): device_ids = ancillary[:] def pullup_side_effect(*args, **kwargs): # Check that the device_id exists, if it does return it # if it does not return None try: device_ids.remove(args[0]) return args[0] except Exception: return None with mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_integration_br'),\ mock.patch('neutron.agent.linux.ip_lib.get_device_mac', return_value='00:00:00:00:00:01'),\ mock.patch('neutron.agent.common.ovs_lib.BaseOVS.get_bridges', return_value=bridges),\ mock.patch('neutron.agent.common.ovs_lib.BaseOVS.' 'get_bridge_external_bridge_id', side_effect=pullup_side_effect),\ mock.patch( 'neutron.agent.common.ovs_lib.OVSBridge.' 'get_ports_attributes', return_value=[]),\ mock.patch( 'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports', return_value=[]): ext_manager = mock.Mock() self.agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(), ext_manager, cfg.CONF) self.assertEqual(len(ancillary), len(self.agent.ancillary_brs)) if ancillary: bridges = [br.br_name for br in self.agent.ancillary_brs] for br in ancillary: self.assertIn(br, bridges) def test_ancillary_bridges_single(self): bridges = ['br-int', 'br-ex'] self._test_ancillary_bridges(bridges, ['br-ex']) def test_ancillary_bridges_none(self): bridges = ['br-int'] self._test_ancillary_bridges(bridges, []) def test_ancillary_bridges_multiple(self): bridges = ['br-int', 'br-ex1', 'br-ex2'] self._test_ancillary_bridges(bridges, ['br-ex1', 'br-ex2']) def mock_scan_ancillary_ports(self, vif_port_set=None, registered_ports=None, sync=False): bridges = ['br-int', 'br-ex'] ancillary = ['br-ex'] with mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_integration_br'), \ mock.patch.object(self.mod_agent.OVSNeutronAgent, '_restore_local_vlan_map'), \ mock.patch('neutron.agent.common.ovs_lib.BaseOVS.get_bridges', return_value=bridges), \ mock.patch('neutron.agent.common.ovs_lib.BaseOVS.' 'get_bridge_external_bridge_id', side_effect=ancillary), \ mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_port_set', return_value=vif_port_set): ext_manager = mock.Mock() self.agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(), ext_manager, cfg.CONF) return self.agent.scan_ancillary_ports(registered_ports, sync) def test_scan_ancillary_ports_returns_cur_only_for_unchanged_ports(self): vif_port_set = set([1, 2]) registered_ports = set([1, 2]) expected = dict(current=vif_port_set, added=set(), removed=set()) actual = self.mock_scan_ancillary_ports(vif_port_set, registered_ports) self.assertEqual(expected, actual) def test_scan_ancillary_ports_returns_port_changes(self): vif_port_set = set([1, 3]) registered_ports = set([1, 2]) expected = dict(current=vif_port_set, added=set([3]), removed=set([2])) actual = self.mock_scan_ancillary_ports(vif_port_set, registered_ports) self.assertEqual(expected, actual) def test_scan_ancillary_ports_returns_port_changes_with_sync(self): vif_port_set = set([1, 3]) registered_ports = set([1, 2]) expected = dict(current=vif_port_set, added=vif_port_set, removed=set([2])) actual = self.mock_scan_ancillary_ports(vif_port_set, registered_ports, sync=True) self.assertEqual(expected, actual) class AncillaryBridgesTestOFCtl(AncillaryBridgesTest, ovs_test_base.OVSOFCtlTestBase): pass class AncillaryBridgesTestRyu(AncillaryBridgesTest, ovs_test_base.OVSRyuTestBase): pass class TestOvsDvrNeutronAgent(object): def setUp(self): super(TestOvsDvrNeutronAgent, self).setUp() mock.patch(PULLAPI).start() notifier_p = mock.patch(NOTIFIER) notifier_cls = notifier_p.start() self.notifier = mock.Mock() notifier_cls.return_value = self.notifier cfg.CONF.set_default('firewall_driver', 'neutron.agent.firewall.NoopFirewallDriver', group='SECURITYGROUP') mock.patch('neutron.agent.common.ovs_lib.BaseOVS.config', new_callable=mock.PropertyMock, return_value={}).start() mock.patch('neutron.agent.ovsdb.impl_idl._connection').start() with mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_integration_br'),\ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'setup_ancillary_bridges', return_value=[]),\ mock.patch('neutron.agent.linux.ip_lib.get_device_mac', return_value='00:00:00:00:00:01'),\ mock.patch( 'neutron.agent.common.ovs_lib.BaseOVS.get_bridges'),\ mock.patch('oslo_service.loopingcall.' 'FixedIntervalLoopingCall', new=MockFixedIntervalLoopingCall),\ mock.patch( 'neutron.agent.common.ovs_lib.OVSBridge.' 'get_ports_attributes', return_value=[]),\ mock.patch( 'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports', return_value=[]): ext_manager = mock.Mock() self.agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(), ext_manager, cfg.CONF) self.agent.tun_br = self.br_tun_cls(br_name='br-tun') self.agent.sg_agent = mock.Mock() def _setup_for_dvr_test(self): self._port = mock.Mock() self._port.ofport = 10 self._port.vif_id = "1234-5678-90" self._physical_network = 'physeth1' self._old_local_vlan = None self._segmentation_id = 2001 self.agent.enable_distributed_routing = True self.agent.enable_tunneling = True self.agent.patch_tun_ofport = 1 self.agent.patch_int_ofport = 2 self.agent.dvr_agent.local_ports = {} self.agent.vlan_manager = self.useFixture( test_vlanmanager.LocalVlanManagerFixture()).manager self.agent.dvr_agent.enable_distributed_routing = True self.agent.dvr_agent.enable_tunneling = True self.agent.dvr_agent.patch_tun_ofport = 1 self.agent.dvr_agent.patch_int_ofport = 2 self.agent.dvr_agent.tun_br = mock.Mock() self.agent.dvr_agent.phys_brs[self._physical_network] = mock.Mock() self.agent.dvr_agent.bridge_mappings = {self._physical_network: 'br-eth1'} self.agent.dvr_agent.int_ofports[self._physical_network] = 30 self.agent.dvr_agent.phys_ofports[self._physical_network] = 40 self.agent.dvr_agent.local_dvr_map = {} self.agent.dvr_agent.registered_dvr_macs = set() self.agent.dvr_agent.dvr_mac_address = 'aa:22:33:44:55:66' self._net_uuid = 'my-net-uuid' self._fixed_ips = [{'subnet_id': 'my-subnet-uuid', 'ip_address': '1.1.1.1'}] self._compute_port = mock.Mock() self._compute_port.ofport = 20 self._compute_port.vif_id = "1234-5678-91" self._compute_fixed_ips = [{'subnet_id': 'my-subnet-uuid', 'ip_address': '1.1.1.3'}] @staticmethod def _expected_port_bound(port, lvid, is_dvr=True): resp = [ mock.call.db_get_val('Port', port.port_name, 'other_config'), mock.call.set_db_attribute('Port', port.port_name, 'other_config', mock.ANY), ] if is_dvr: resp = [mock.call.get_vifs_by_ids([])] + resp return resp def _expected_install_dvr_process(self, lvid, port, ip_version, gateway_ip): if ip_version == 4: ipvx_calls = [ mock.call.install_dvr_process_ipv4( vlan_tag=lvid, gateway_ip=gateway_ip), ] else: ipvx_calls = [ mock.call.install_dvr_process_ipv6( vlan_tag=lvid, gateway_mac=port.vif_mac), ] return ipvx_calls + [ mock.call.install_dvr_process( vlan_tag=lvid, dvr_mac_address=self.agent.dvr_agent.dvr_mac_address, vif_mac=port.vif_mac, ), ] def _test_port_bound_for_dvr_on_vlan_network(self, device_owner, ip_version=4): self._setup_for_dvr_test() if ip_version == 4: gateway_ip = '1.1.1.10' cidr = '1.1.1.0/24' else: gateway_ip = '2001:100::1' cidr = '2001:100::0/64' self._port.vif_mac = 'aa:bb:cc:11:22:33' gateway_mac = 'aa:bb:cc:66:66:66' self._compute_port.vif_mac = '77:88:99:00:11:22' physical_network = self._physical_network segmentation_id = self._segmentation_id network_type = n_const.TYPE_VLAN int_br = mock.create_autospec(self.agent.int_br) tun_br = mock.create_autospec(self.agent.tun_br) phys_br = mock.create_autospec(self.br_phys_cls('br-phys')) int_br.set_db_attribute.return_value = True int_br.db_get_val.return_value = {} with mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={'gateway_ip': gateway_ip, 'cidr': cidr, 'ip_version': ip_version, 'gateway_mac': gateway_mac}),\ mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_ports_on_host_by_subnet', return_value=[]),\ mock.patch.object(self.agent.dvr_agent.int_br, 'get_vif_port_by_id', return_value=self._port),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.dict(self.agent.phys_brs, {physical_network: phys_br}),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br),\ mock.patch.dict(self.agent.dvr_agent.phys_brs, {physical_network: phys_br}): self.agent.port_bound( self._port, self._net_uuid, network_type, physical_network, segmentation_id, self._fixed_ips, n_const.DEVICE_OWNER_DVR_INTERFACE, False) phy_ofp = self.agent.dvr_agent.phys_ofports[physical_network] int_ofp = self.agent.dvr_agent.int_ofports[physical_network] lvid = self.agent.vlan_manager.get(self._net_uuid).vlan expected_on_phys_br = [ mock.call.provision_local_vlan( port=phy_ofp, lvid=lvid, segmentation_id=segmentation_id, distributed=True, ), ] + self._expected_install_dvr_process( port=self._port, lvid=lvid, ip_version=ip_version, gateway_ip=self._fixed_ips[0]['ip_address']) expected_on_int_br = [ mock.call.provision_local_vlan( port=int_ofp, lvid=lvid, segmentation_id=segmentation_id, ), ] + self._expected_port_bound(self._port, lvid) self.assertEqual(expected_on_int_br, int_br.mock_calls) self.assertEqual([], tun_br.mock_calls) self.assertEqual(expected_on_phys_br, phys_br.mock_calls) int_br.reset_mock() tun_br.reset_mock() phys_br.reset_mock() self.agent.port_bound(self._compute_port, self._net_uuid, network_type, physical_network, segmentation_id, self._compute_fixed_ips, device_owner, False) expected_on_int_br = [ mock.call.install_dvr_to_src_mac( network_type=network_type, gateway_mac=gateway_mac, dst_mac=self._compute_port.vif_mac, dst_port=self._compute_port.ofport, vlan_tag=segmentation_id, ), ] + self._expected_port_bound(self._compute_port, lvid, False) self.assertEqual(expected_on_int_br, int_br.mock_calls) self.assertFalse([], tun_br.mock_calls) self.assertFalse([], phys_br.mock_calls) def _test_port_bound_for_dvr_on_vxlan_network(self, device_owner, ip_version=4): self._setup_for_dvr_test() if ip_version == 4: gateway_ip = '1.1.1.1' cidr = '1.1.1.0/24' else: gateway_ip = '2001:100::1' cidr = '2001:100::0/64' network_type = n_const.TYPE_VXLAN self._port.vif_mac = gateway_mac = 'aa:bb:cc:11:22:33' self._compute_port.vif_mac = '77:88:99:00:11:22' physical_network = self._physical_network segmentation_id = self._segmentation_id int_br = mock.create_autospec(self.agent.int_br) tun_br = mock.create_autospec(self.agent.tun_br) phys_br = mock.create_autospec(self.br_phys_cls('br-phys')) int_br.set_db_attribute.return_value = True int_br.db_get_val.return_value = {} with mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={'gateway_ip': gateway_ip, 'cidr': cidr, 'ip_version': ip_version, 'gateway_mac': gateway_mac}),\ mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_ports_on_host_by_subnet', return_value=[]),\ mock.patch.object(self.agent.dvr_agent.int_br, 'get_vif_port_by_id', return_value=self._port),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.dict(self.agent.phys_brs, {physical_network: phys_br}),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br),\ mock.patch.dict(self.agent.dvr_agent.phys_brs, {physical_network: phys_br}): self.agent.port_bound( self._port, self._net_uuid, network_type, physical_network, segmentation_id, self._fixed_ips, n_const.DEVICE_OWNER_DVR_INTERFACE, False) lvid = self.agent.vlan_manager.get(self._net_uuid).vlan expected_on_int_br = self._expected_port_bound( self._port, lvid) expected_on_tun_br = [ mock.call.provision_local_vlan( network_type=network_type, segmentation_id=segmentation_id, lvid=lvid, distributed=True), ] + self._expected_install_dvr_process( port=self._port, lvid=lvid, ip_version=ip_version, gateway_ip=gateway_ip) self.assertEqual(expected_on_int_br, int_br.mock_calls) self.assertEqual(expected_on_tun_br, tun_br.mock_calls) self.assertEqual([], phys_br.mock_calls) int_br.reset_mock() tun_br.reset_mock() phys_br.reset_mock() self.agent.port_bound(self._compute_port, self._net_uuid, network_type, physical_network, segmentation_id, self._compute_fixed_ips, device_owner, False) expected_on_int_br = [ mock.call.install_dvr_to_src_mac( network_type=network_type, gateway_mac=gateway_mac, dst_mac=self._compute_port.vif_mac, dst_port=self._compute_port.ofport, vlan_tag=lvid, ), ] + self._expected_port_bound(self._compute_port, lvid, False) self.assertEqual(expected_on_int_br, int_br.mock_calls) self.assertEqual([], tun_br.mock_calls) self.assertEqual([], phys_br.mock_calls) def test_port_bound_for_dvr_with_compute_ports(self): self._test_port_bound_for_dvr_on_vlan_network( device_owner=DEVICE_OWNER_COMPUTE) self._test_port_bound_for_dvr_on_vlan_network( device_owner=DEVICE_OWNER_COMPUTE, ip_version=6) self._test_port_bound_for_dvr_on_vxlan_network( device_owner=DEVICE_OWNER_COMPUTE) self._test_port_bound_for_dvr_on_vxlan_network( device_owner=DEVICE_OWNER_COMPUTE, ip_version=6) def test_port_bound_for_dvr_with_lbaas_vip_ports(self): self._test_port_bound_for_dvr_on_vlan_network( device_owner=n_const.DEVICE_OWNER_LOADBALANCER) self._test_port_bound_for_dvr_on_vlan_network( device_owner=n_const.DEVICE_OWNER_LOADBALANCER, ip_version=6) self._test_port_bound_for_dvr_on_vxlan_network( device_owner=n_const.DEVICE_OWNER_LOADBALANCER) self._test_port_bound_for_dvr_on_vxlan_network( device_owner=n_const.DEVICE_OWNER_LOADBALANCER, ip_version=6) def test_port_bound_for_dvr_with_lbaasv2_vip_ports(self): self._test_port_bound_for_dvr_on_vlan_network( device_owner=n_const.DEVICE_OWNER_LOADBALANCERV2) self._test_port_bound_for_dvr_on_vlan_network( device_owner=n_const.DEVICE_OWNER_LOADBALANCERV2, ip_version=6) self._test_port_bound_for_dvr_on_vxlan_network( device_owner=n_const.DEVICE_OWNER_LOADBALANCERV2) self._test_port_bound_for_dvr_on_vxlan_network( device_owner=n_const.DEVICE_OWNER_LOADBALANCERV2, ip_version=6) def test_port_bound_for_dvr_with_dhcp_ports(self): self._test_port_bound_for_dvr_on_vlan_network( device_owner=n_const.DEVICE_OWNER_DHCP) self._test_port_bound_for_dvr_on_vlan_network( device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6) self._test_port_bound_for_dvr_on_vxlan_network( device_owner=n_const.DEVICE_OWNER_DHCP) self._test_port_bound_for_dvr_on_vxlan_network( device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6) def test_port_bound_for_dvr_with_csnat_ports(self): self._setup_for_dvr_test() int_br, tun_br = self._port_bound_for_dvr_with_csnat_ports() lvid = self.agent.vlan_manager.get(self._net_uuid).vlan expected_on_int_br = [ mock.call.install_dvr_to_src_mac( network_type='vxlan', gateway_mac='aa:bb:cc:11:22:33', dst_mac=self._port.vif_mac, dst_port=self._port.ofport, vlan_tag=lvid, ), ] + self._expected_port_bound(self._port, lvid, is_dvr=False) self.assertEqual(expected_on_int_br, int_br.mock_calls) expected_on_tun_br = [ mock.call.provision_local_vlan( network_type='vxlan', lvid=lvid, segmentation_id=None, distributed=True, ), ] self.assertEqual(expected_on_tun_br, tun_br.mock_calls) def test_port_bound_for_dvr_with_csnat_port_without_passing_fixed_ip(self): self._setup_for_dvr_test() int_br = mock.create_autospec(self.agent.int_br) tun_br = mock.create_autospec(self.agent.tun_br) int_br.set_db_attribute.return_value = True int_br.db_get_val.return_value = {} with mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr') as mock_getsub,\ mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_ports_on_host_by_subnet', return_value=[]),\ mock.patch.object(self.agent.dvr_agent.int_br, 'get_vif_port_by_id', return_value=self._port),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', None, None, self._fixed_ips, n_const.DEVICE_OWNER_ROUTER_SNAT, False) mock_getsub.assert_called_with( self.agent.context, mock.ANY, fixed_ips=None) def test_port_bound_for_dvr_with_csnat_ports_ofport_change(self): self._setup_for_dvr_test() self._port_bound_for_dvr_with_csnat_ports() # simulate a replug self._port.ofport = 12 int_br, tun_br = self._port_bound_for_dvr_with_csnat_ports() lvid = self.agent.vlan_manager.get(self._net_uuid).vlan expected_on_int_br = [ mock.call.delete_dvr_to_src_mac( network_type='vxlan', dst_mac=self._port.vif_mac, vlan_tag=lvid, ), mock.call.install_dvr_to_src_mac( network_type='vxlan', gateway_mac='aa:bb:cc:11:22:33', dst_mac=self._port.vif_mac, dst_port=self._port.ofport, vlan_tag=lvid, ), ] + self._expected_port_bound(self._port, lvid, is_dvr=False) self.assertEqual(expected_on_int_br, int_br.mock_calls) # a local vlan was already provisioned so there should be no new # calls to tunbr self.assertEqual([], tun_br.mock_calls) # make sure ofport was updated self.assertEqual(12, self.agent.dvr_agent.local_ports[self._port.vif_id].ofport) def _port_bound_for_dvr_with_csnat_ports(self): int_br = mock.create_autospec(self.agent.int_br) tun_br = mock.create_autospec(self.agent.tun_br) int_br.set_db_attribute.return_value = True int_br.db_get_val.return_value = {} with mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={'gateway_ip': '1.1.1.1', 'cidr': '1.1.1.0/24', 'ip_version': 4, 'gateway_mac': 'aa:bb:cc:11:22:33'}),\ mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_ports_on_host_by_subnet', return_value=[]),\ mock.patch.object(self.agent.dvr_agent.int_br, 'get_vif_port_by_id', return_value=self._port),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', None, None, self._fixed_ips, n_const.DEVICE_OWNER_ROUTER_SNAT, False) return int_br, tun_br def test_port_bound_for_dvr_with_csnat_ports_without_subnet(self): self._setup_for_dvr_test() int_br = mock.create_autospec(self.agent.int_br) tun_br = mock.create_autospec(self.agent.tun_br) # get_subnet_for_dvr RPC returns {} on error with mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={}),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', None, None, self._fixed_ips, n_const.DEVICE_OWNER_ROUTER_SNAT, False) self.assertFalse(int_br.install_dvr_to_src_mac.called) def test_treat_devices_removed_for_dvr_interface(self): self._test_treat_devices_removed_for_dvr_interface() self._test_treat_devices_removed_for_dvr_interface(ip_version=6) self._test_treat_devices_removed_for_dvr_interface(network_type='vlan') self._test_treat_devices_removed_for_dvr_interface(ip_version=6, network_type='vlan') def _test_treat_devices_removed_for_dvr_interface( self, ip_version=4, network_type='vxlan'): self._setup_for_dvr_test() if ip_version == 4: gateway_ip = '1.1.1.1' cidr = '1.1.1.0/24' else: gateway_ip = '2001:100::1' cidr = '2001:100::0/64' gateway_mac = 'aa:bb:cc:11:22:33' int_br = mock.create_autospec(self.agent.int_br) tun_br = mock.create_autospec(self.agent.tun_br) int_br.set_db_attribute.return_value = True int_br.db_get_val.return_value = {} with mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={'gateway_ip': gateway_ip, 'cidr': cidr, 'ip_version': ip_version, 'gateway_mac': gateway_mac}),\ mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_ports_on_host_by_subnet', return_value=[]),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br),\ mock.patch.object(self.agent.dvr_agent.int_br, 'get_vif_port_by_id', return_value=self._port): if network_type == 'vlan': self.agent.port_bound(self._port, self._net_uuid, network_type, self._physical_network, self._segmentation_id, self._compute_fixed_ips, n_const.DEVICE_OWNER_DVR_INTERFACE, False) else: self.agent.port_bound( self._port, self._net_uuid, 'vxlan', None, None, self._fixed_ips, n_const.DEVICE_OWNER_DVR_INTERFACE, False) lvid = self.agent.vlan_manager.get(self._net_uuid).vlan self.assertEqual(self._expected_port_bound(self._port, lvid), int_br.mock_calls) expected_on_tun_br = [ mock.call.provision_local_vlan(network_type='vxlan', lvid=lvid, segmentation_id=None, distributed=True), ] + self._expected_install_dvr_process( port=self._port, lvid=lvid, ip_version=ip_version, gateway_ip=gateway_ip) self.assertEqual(expected_on_tun_br, tun_br.mock_calls) int_br.reset_mock() tun_br.reset_mock() phys_br = mock.create_autospec(self.br_phys_cls('br-phys')) with mock.patch.object(self.agent, 'reclaim_local_vlan'),\ mock.patch.object(self.agent.plugin_rpc, 'update_device_list', return_value={ 'devices_up': [], 'devices_down': [self._port.vif_id], 'failed_devices_up': [], 'failed_devices_down': []}),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.dict(self.agent.phys_brs, {self._physical_network: phys_br}),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br),\ mock.patch.dict(self.agent.dvr_agent.phys_brs, {self._physical_network: phys_br}): failed_devices = {'added': set(), 'removed': set()} failed_devices['removed'] = self.agent.treat_devices_removed( [self._port.vif_id]) lvid = self.agent.vlan_manager.get(self._net_uuid).vlan if ip_version == 4: expected = [ mock.call.delete_dvr_process_ipv4( vlan_tag=lvid, gateway_ip=gateway_ip), ] else: expected = [ mock.call.delete_dvr_process_ipv6( vlan_tag=lvid, gateway_mac=gateway_mac), ] expected.extend([ mock.call.delete_dvr_process( vlan_tag=lvid, vif_mac=self._port.vif_mac), ]) if network_type == 'vlan': self.assertEqual([], int_br.mock_calls) self.assertEqual([], tun_br.mock_calls) self.assertEqual(expected, phys_br.mock_calls) self.assertEqual({}, self.agent.dvr_agent.local_ports) else: self.assertEqual([], int_br.mock_calls) self.assertEqual(expected, tun_br.mock_calls) self.assertEqual([], phys_br.mock_calls) def _test_treat_devices_removed_for_dvr(self, device_owner, ip_version=4): self._setup_for_dvr_test() if ip_version == 4: gateway_ip = '1.1.1.1' cidr = '1.1.1.0/24' else: gateway_ip = '2001:100::1' cidr = '2001:100::0/64' gateway_mac = 'aa:bb:cc:11:22:33' int_br = mock.create_autospec(self.agent.int_br) tun_br = mock.create_autospec(self.agent.tun_br) int_br.set_db_attribute.return_value = True int_br.db_get_val.return_value = {} with mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={'gateway_ip': gateway_ip, 'cidr': cidr, 'ip_version': ip_version, 'gateway_mac': gateway_mac}),\ mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_ports_on_host_by_subnet', return_value=[]),\ mock.patch.object(self.agent.dvr_agent.int_br, 'get_vif_port_by_id', return_value=self._port),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', None, None, self._fixed_ips, n_const.DEVICE_OWNER_DVR_INTERFACE, False) lvid = self.agent.vlan_manager.get(self._net_uuid).vlan self.assertEqual( self._expected_port_bound(self._port, lvid), int_br.mock_calls) expected_on_tun_br = [ mock.call.provision_local_vlan( network_type='vxlan', segmentation_id=None, lvid=lvid, distributed=True), ] + self._expected_install_dvr_process( port=self._port, lvid=lvid, ip_version=ip_version, gateway_ip=gateway_ip) self.assertEqual(expected_on_tun_br, tun_br.mock_calls) int_br.reset_mock() tun_br.reset_mock() self.agent.port_bound(self._compute_port, self._net_uuid, 'vxlan', None, None, self._compute_fixed_ips, device_owner, False) self.assertEqual( [ mock.call.install_dvr_to_src_mac( network_type='vxlan', gateway_mac='aa:bb:cc:11:22:33', dst_mac=self._compute_port.vif_mac, dst_port=self._compute_port.ofport, vlan_tag=lvid, ), ] + self._expected_port_bound(self._compute_port, lvid, False), int_br.mock_calls) self.assertEqual([], tun_br.mock_calls) int_br.reset_mock() tun_br.reset_mock() with mock.patch.object(self.agent, 'reclaim_local_vlan'),\ mock.patch.object(self.agent.plugin_rpc, 'update_device_list', return_value={ 'devices_up': [], 'devices_down': [ self._compute_port.vif_id], 'failed_devices_up': [], 'failed_devices_down': []}),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br): failed_devices = {'added': set(), 'removed': set()} failed_devices['removed'] = self.agent.treat_devices_removed( [self._compute_port.vif_id]) int_br.assert_has_calls([ mock.call.delete_dvr_to_src_mac( network_type='vxlan', vlan_tag=lvid, dst_mac=self._compute_port.vif_mac, ), ]) self.assertEqual([], tun_br.mock_calls) def test_treat_devices_removed_for_dvr_with_compute_ports(self): self._test_treat_devices_removed_for_dvr( device_owner=DEVICE_OWNER_COMPUTE) self._test_treat_devices_removed_for_dvr( device_owner=DEVICE_OWNER_COMPUTE, ip_version=6) def test_treat_devices_removed_for_dvr_with_lbaas_vip_ports(self): self._test_treat_devices_removed_for_dvr( device_owner=n_const.DEVICE_OWNER_LOADBALANCER) self._test_treat_devices_removed_for_dvr( device_owner=n_const.DEVICE_OWNER_LOADBALANCER, ip_version=6) def test_treat_devices_removed_for_dvr_with_lbaasv2_vip_ports(self): self._test_treat_devices_removed_for_dvr( device_owner=n_const.DEVICE_OWNER_LOADBALANCERV2) self._test_treat_devices_removed_for_dvr( device_owner=n_const.DEVICE_OWNER_LOADBALANCERV2, ip_version=6) def test_treat_devices_removed_for_dvr_with_dhcp_ports(self): self._test_treat_devices_removed_for_dvr( device_owner=n_const.DEVICE_OWNER_DHCP) self._test_treat_devices_removed_for_dvr( device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6) def test_treat_devices_removed_for_dvr_csnat_port(self): self._setup_for_dvr_test() gateway_mac = 'aa:bb:cc:11:22:33' int_br = mock.create_autospec(self.agent.int_br) tun_br = mock.create_autospec(self.agent.tun_br) int_br.set_db_attribute.return_value = True int_br.db_get_val.return_value = {} with mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={'gateway_ip': '1.1.1.1', 'cidr': '1.1.1.0/24', 'ip_version': 4, 'gateway_mac': gateway_mac}),\ mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_ports_on_host_by_subnet', return_value=[]),\ mock.patch.object(self.agent.dvr_agent.int_br, 'get_vif_port_by_id', return_value=self._port),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', None, None, self._fixed_ips, n_const.DEVICE_OWNER_ROUTER_SNAT, False) lvid = self.agent.vlan_manager.get(self._net_uuid).vlan expected_on_int_br = [ mock.call.install_dvr_to_src_mac( network_type='vxlan', gateway_mac=gateway_mac, dst_mac=self._port.vif_mac, dst_port=self._port.ofport, vlan_tag=lvid, ), ] + self._expected_port_bound(self._port, lvid, is_dvr=False) self.assertEqual(expected_on_int_br, int_br.mock_calls) expected_on_tun_br = [ mock.call.provision_local_vlan( network_type='vxlan', lvid=lvid, segmentation_id=None, distributed=True, ), ] self.assertEqual(expected_on_tun_br, tun_br.mock_calls) int_br.reset_mock() tun_br.reset_mock() with mock.patch.object(self.agent, 'reclaim_local_vlan'),\ mock.patch.object(self.agent.plugin_rpc, 'update_device_list', return_value={ 'devices_up': [], 'devices_down': [self._port.vif_id], 'failed_devices_up': [], 'failed_devices_down': []}),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br): failed_devices = {'added': set(), 'removed': set()} failed_devices['removed'] = self.agent.treat_devices_removed( [self._port.vif_id]) expected_on_int_br = [ mock.call.delete_dvr_to_src_mac( network_type='vxlan', dst_mac=self._port.vif_mac, vlan_tag=lvid, ), ] self.assertEqual(expected_on_int_br, int_br.mock_calls) expected_on_tun_br = [] self.assertEqual(expected_on_tun_br, tun_br.mock_calls) def test_setup_dvr_flows_on_int_br(self): self._setup_for_dvr_test() int_br = mock.create_autospec(self.agent.int_br) tun_br = mock.create_autospec(self.agent.tun_br) with mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br),\ mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_dvr_mac_address_list', return_value=[{'host': 'cn1', 'mac_address': 'aa-bb-cc-dd-ee-ff'}, {'host': 'cn2', 'mac_address': '11-22-33-44-55-66'}]): self.agent.dvr_agent.setup_dvr_flows_on_integ_br() self.assertTrue(self.agent.dvr_agent.in_distributed_mode()) physical_networks = list( self.agent.dvr_agent.bridge_mappings.keys()) ioport = self.agent.dvr_agent.int_ofports[physical_networks[0]] expected_on_int_br = [ # setup_dvr_flows_on_integ_br mock.call.setup_canary_table(), mock.call.install_drop(table_id=constants.DVR_TO_SRC_MAC, priority=1), mock.call.install_drop(table_id=constants.DVR_TO_SRC_MAC_VLAN, priority=1), mock.call.install_drop(table_id=constants.LOCAL_SWITCHING, priority=2, in_port=ioport), ] self.assertEqual(expected_on_int_br, int_br.mock_calls) self.assertEqual([], tun_br.mock_calls) def test_get_dvr_mac_address(self): self._setup_for_dvr_test() self.agent.dvr_agent.dvr_mac_address = None with mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_dvr_mac_address_by_host', return_value={'host': 'cn1', 'mac_address': 'aa-22-33-44-55-66'}): self.agent.dvr_agent.get_dvr_mac_address() self.assertEqual('aa:22:33:44:55:66', self.agent.dvr_agent.dvr_mac_address) self.assertTrue(self.agent.dvr_agent.in_distributed_mode()) def test_get_dvr_mac_address_exception(self): self._setup_for_dvr_test() self.agent.dvr_agent.dvr_mac_address = None int_br = mock.create_autospec(self.agent.int_br) with mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_dvr_mac_address_by_host', side_effect=oslo_messaging.RemoteError),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br): with testtools.ExpectedException(SystemExit): self.agent.dvr_agent.get_dvr_mac_address() self.assertIsNone(self.agent.dvr_agent.dvr_mac_address) self.assertFalse(self.agent.dvr_agent.in_distributed_mode()) def test_get_dvr_mac_address_retried(self): valid_entry = {'host': 'cn1', 'mac_address': 'aa-22-33-44-55-66'} raise_timeout = oslo_messaging.MessagingTimeout() # Raise a timeout the first 2 times it calls get_dvr_mac_address() self._setup_for_dvr_test() self.agent.dvr_agent.dvr_mac_address = None with mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_dvr_mac_address_by_host', side_effect=(raise_timeout, raise_timeout, valid_entry)): self.agent.dvr_agent.get_dvr_mac_address() self.assertEqual('aa:22:33:44:55:66', self.agent.dvr_agent.dvr_mac_address) self.assertTrue(self.agent.dvr_agent.in_distributed_mode()) self.assertEqual(self.agent.dvr_agent.plugin_rpc. get_dvr_mac_address_by_host.call_count, 3) def test_get_dvr_mac_address_retried_max(self): raise_timeout = oslo_messaging.MessagingTimeout() # Raise a timeout every time until we give up, currently 5 tries self._setup_for_dvr_test() self.agent.dvr_agent.dvr_mac_address = None int_br = mock.create_autospec(self.agent.int_br) with mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_dvr_mac_address_by_host', side_effect=raise_timeout),\ mock.patch.object(utils, "execute"),\ mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br): with testtools.ExpectedException(SystemExit): self.agent.dvr_agent.get_dvr_mac_address() self.assertIsNone(self.agent.dvr_agent.dvr_mac_address) self.assertFalse(self.agent.dvr_agent.in_distributed_mode()) self.assertEqual(self.agent.dvr_agent.plugin_rpc. get_dvr_mac_address_by_host.call_count, 5) def test_dvr_mac_address_update(self): self._setup_for_dvr_test() newhost = 'cn2' newmac = 'aa:bb:cc:dd:ee:ff' int_br = mock.create_autospec(self.agent.int_br) tun_br = mock.create_autospec(self.agent.tun_br) phys_br = mock.create_autospec(self.br_phys_cls('br-phys')) physical_network = 'physeth1' with mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.dict(self.agent.phys_brs, {physical_network: phys_br}),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br),\ mock.patch.dict(self.agent.dvr_agent.phys_brs, {physical_network: phys_br}): self.agent.dvr_agent.\ dvr_mac_address_update( dvr_macs=[{'host': newhost, 'mac_address': newmac}]) expected_on_int_br = [ mock.call.add_dvr_mac_vlan( mac=newmac, port=self.agent.int_ofports[physical_network]), mock.call.add_dvr_mac_tun( mac=newmac, port=self.agent.patch_tun_ofport), ] expected_on_tun_br = [ mock.call.add_dvr_mac_tun( mac=newmac, port=self.agent.patch_int_ofport), ] expected_on_phys_br = [ mock.call.add_dvr_mac_vlan( mac=newmac, port=self.agent.phys_ofports[physical_network]), ] self.assertEqual(expected_on_int_br, int_br.mock_calls) self.assertEqual(expected_on_tun_br, tun_br.mock_calls) self.assertEqual(expected_on_phys_br, phys_br.mock_calls) int_br.reset_mock() tun_br.reset_mock() phys_br.reset_mock() with mock.patch.object(self.agent, 'int_br', new=int_br),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.dict(self.agent.phys_brs, {physical_network: phys_br}),\ mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\ mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br),\ mock.patch.dict(self.agent.dvr_agent.phys_brs, {physical_network: phys_br}): self.agent.dvr_agent.dvr_mac_address_update(dvr_macs=[]) expected_on_int_br = [ mock.call.remove_dvr_mac_vlan( mac=newmac), mock.call.remove_dvr_mac_tun( mac=newmac, port=self.agent.patch_tun_ofport), ] expected_on_tun_br = [ mock.call.remove_dvr_mac_tun( mac=newmac), ] expected_on_phys_br = [ mock.call.remove_dvr_mac_vlan( mac=newmac), ] self.assertEqual(expected_on_int_br, int_br.mock_calls) self.assertEqual(expected_on_tun_br, tun_br.mock_calls) self.assertEqual(expected_on_phys_br, phys_br.mock_calls) def test_ovs_restart(self): self._setup_for_dvr_test() reset_methods = ( 'reset_ovs_parameters', 'reset_dvr_parameters', 'setup_dvr_flows_on_integ_br', 'setup_dvr_flows_on_tun_br', 'setup_dvr_flows_on_phys_br', 'setup_dvr_mac_flows_on_all_brs') reset_mocks = [mock.patch.object(self.agent.dvr_agent, method).start() for method in reset_methods] tun_br = mock.create_autospec(self.agent.tun_br) with mock.patch.object(self.agent, 'check_ovs_status', return_value=constants.OVS_RESTARTED),\ mock.patch.object(self.agent, '_agent_has_updates', side_effect=TypeError('loop exit')),\ mock.patch.object(self.agent, 'tun_br', new=tun_br),\ mock.patch.object(self.agent, 'setup_physical_bridges'),\ mock.patch.object(self.agent, 'setup_integration_br'),\ mock.patch.object(self.agent, 'setup_tunnel_br'),\ mock.patch.object(self.agent, 'state_rpc'): try: self.agent.rpc_loop(polling_manager=mock.Mock()) except TypeError: pass self.assertTrue(all([x.called for x in reset_mocks])) def test_rpc_loop_survives_error_in_check_canary_table(self): with mock.patch.object(self.agent.int_br, 'check_canary_table', side_effect=TypeError('borked')),\ mock.patch.object(self.agent, '_check_and_handle_signal', side_effect=[True, False]): self.agent.rpc_loop(polling_manager=mock.Mock()) def _test_scan_ports_failure(self, scan_method_name): with mock.patch.object(self.agent, 'check_ovs_status', return_value=constants.OVS_RESTARTED),\ mock.patch.object(self.agent, scan_method_name, side_effect=TypeError('broken')),\ mock.patch.object(self.agent, '_agent_has_updates', return_value=True),\ mock.patch.object(self.agent, '_check_and_handle_signal', side_effect=[True, False]),\ mock.patch.object(self.agent, 'setup_physical_bridges'),\ mock.patch.object(self.agent, 'setup_integration_br'),\ mock.patch.object(self.agent, 'state_rpc'): # block RPC calls and bridge calls self.agent.rpc_loop(polling_manager=mock.Mock()) def test_scan_ports_failure(self): self._test_scan_ports_failure('scan_ports') def test_scan_ancillary_ports_failure(self): with mock.patch.object(self.agent, 'scan_ports'): with mock.patch.object(self.agent, 'update_stale_ofport_rules'): self.agent.ancillary_brs = mock.Mock() self._test_scan_ports_failure('scan_ancillary_ports') class TestOvsDvrNeutronAgentOFCtl(TestOvsDvrNeutronAgent, ovs_test_base.OVSOFCtlTestBase): pass class TestOvsDvrNeutronAgentRyu(TestOvsDvrNeutronAgent, ovs_test_base.OVSRyuTestBase): pass class TestValidateTunnelLocalIP(base.BaseTestCase): def test_validate_local_ip_with_valid_ip(self): mock_get_device_by_ip = mock.patch.object( ip_lib.IPWrapper, 'get_device_by_ip').start() ovs_agent.validate_local_ip(FAKE_IP1) mock_get_device_by_ip.assert_called_once_with(FAKE_IP1) def test_validate_local_ip_with_valid_ipv6(self): mock_get_device_by_ip = mock.patch.object( ip_lib.IPWrapper, 'get_device_by_ip').start() ovs_agent.validate_local_ip(FAKE_IP6) mock_get_device_by_ip.assert_called_once_with(FAKE_IP6) def test_validate_local_ip_with_none_ip(self): with testtools.ExpectedException(SystemExit): ovs_agent.validate_local_ip(None) def test_validate_local_ip_with_invalid_ip(self): mock_get_device_by_ip = mock.patch.object( ip_lib.IPWrapper, 'get_device_by_ip').start() mock_get_device_by_ip.return_value = None with testtools.ExpectedException(SystemExit): ovs_agent.validate_local_ip(FAKE_IP1) mock_get_device_by_ip.assert_called_once_with(FAKE_IP1) def test_validate_local_ip_with_invalid_ipv6(self): mock_get_device_by_ip = mock.patch.object( ip_lib.IPWrapper, 'get_device_by_ip').start() mock_get_device_by_ip.return_value = None with testtools.ExpectedException(SystemExit): ovs_agent.validate_local_ip(FAKE_IP6) mock_get_device_by_ip.assert_called_once_with(FAKE_IP6) class TestOvsAgentTunnelName(base.BaseTestCase): def test_get_tunnel_hash_invalid_address(self): hashlen = n_const.DEVICE_NAME_MAX_LEN self.assertIsNone( ovs_agent.OVSNeutronAgent.get_tunnel_hash('a.b.c.d', hashlen)) def test_get_tunnel_name_vxlan(self): self.assertEqual( 'vxlan-7f000002', ovs_agent.OVSNeutronAgent.get_tunnel_name( 'vxlan', '127.0.0.1', '127.0.0.2')) def test_get_tunnel_name_gre(self): self.assertEqual( 'gre-7f000002', ovs_agent.OVSNeutronAgent.get_tunnel_name( 'gre', '127.0.0.1', '127.0.0.2')) def test_get_tunnel_name_vxlan_ipv6(self): self.assertEqual( 'vxlan-pehtjzksi', ovs_agent.OVSNeutronAgent.get_tunnel_name( 'vxlan', '2001:db8::1', '2001:db8::2')) def test_get_tunnel_name_gre_ipv6(self): self.assertEqual( 'gre-pehtjzksiqr', ovs_agent.OVSNeutronAgent.get_tunnel_name( 'gre', '2001:db8::1', '2001:db8::2')) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py0000664000175000017500000007415213553660047032252 0ustar zuulzuul00000000000000# Copyright 2012 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import time import mock from neutron_lib import constants as n_const from oslo_config import cfg from oslo_log import log import six from neutron.agent.common import ip_lib from neutron.agent.common import ovs_lib from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \ import ovs_test_base from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \ import test_vlanmanager def nonzero(f): if six.PY3: return f.__bool__() else: return f.__nonzero__() # Useful global dummy variables. NET_UUID = '3faeebfe-5d37-11e1-a64b-000c29d5f0a7' LS_ID = 420 LV_ID = 42 LV_IDS = [42, 43] VIF_ID = '404deaec-5d37-11e1-a64b-000c29d5f0a8' VIF_MAC = '3c:09:24:1e:78:23' OFPORT_NUM = 1 VIF_PORT = ovs_lib.VifPort('port', OFPORT_NUM, VIF_ID, VIF_MAC, 'switch') VIF_PORTS = {VIF_ID: VIF_PORT} FIXED_IPS = [{'subnet_id': 'my-subnet-uuid', 'ip_address': '1.1.1.1'}] VM_DEVICE_OWNER = n_const.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' TUN_OFPORTS = {n_const.TYPE_GRE: {'ip1': '11', 'ip2': '12'}} BCAST_MAC = "01:00:00:00:00:00/01:00:00:00:00:00" UCAST_MAC = "00:00:00:00:00:00/01:00:00:00:00:00" class DummyPort(object): def __init__(self, interface_id): self.interface_id = interface_id class DummyVlanBinding(object): def __init__(self, network_id, vlan_id): self.network_id = network_id self.vlan_id = vlan_id class TunnelTest(object): USE_VETH_INTERCONNECTION = False VETH_MTU = None def setUp(self): super(TunnelTest, self).setUp() self.useFixture(test_vlanmanager.LocalVlanManagerFixture()) conn_patcher = mock.patch( 'neutron.agent.ovsdb.impl_idl._connection') conn_patcher.start() mock.patch( 'neutron.api.rpc.handlers.resources_rpc.ResourcesPullRpcApi' ).start() self.addCleanup(conn_patcher.stop) cfg.CONF.set_default('firewall_driver', 'neutron.agent.firewall.NoopFirewallDriver', group='SECURITYGROUP') cfg.CONF.set_override('report_interval', 0, 'AGENT') self.INT_BRIDGE = 'integration_bridge' self.TUN_BRIDGE = 'tunnel_bridge' self.MAP_TUN_BRIDGE = 'tun_br_map' self.AUX_BRIDGE = 'ancillary_bridge' self.NET_MAPPING = ['net1:%s' % self.MAP_TUN_BRIDGE] self.INT_OFPORT = 11111 self.TUN_OFPORT = 22222 self.MAP_TUN_INT_OFPORT = 33333 self.MAP_TUN_PHY_OFPORT = 44444 self.LVM_DATA = ( LV_ID, 'gre', None, LS_ID, VIF_PORTS) self.LVM_FLAT_DATA = ( LV_ID, 'flat', 'net1', LS_ID, VIF_PORTS) self.LVM_VLAN_DATA = ( LV_ID, 'vlan', 'net1', LS_ID, VIF_PORTS) self.inta = mock.Mock() self.intb = mock.Mock() mock.patch.object(ovs_lib.BaseOVS, 'config', new_callable=mock.PropertyMock, return_value={}).start() mock.patch('neutron.agent.ovsdb.impl_idl._connection').start() self.ovs_bridges = { self.INT_BRIDGE: mock.create_autospec( self.br_int_cls('br-int')), self.TUN_BRIDGE: mock.create_autospec( self.br_tun_cls('br-tun')), self.MAP_TUN_BRIDGE: mock.create_autospec( self.br_phys_cls('br-phys')), self.AUX_BRIDGE: mock.create_autospec( ovs_lib.OVSBridge('br-aux')), } self.ovs_int_ofports = { 'patch-tun': self.TUN_OFPORT, 'int-%s' % self.MAP_TUN_BRIDGE: self.MAP_TUN_INT_OFPORT } def lookup_br(br_name, *args, **kwargs): return self.ovs_bridges[br_name] self.mock_int_bridge_cls = mock.patch(self._BR_INT_CLASS, autospec=True).start() self.mock_int_bridge_cls.side_effect = lookup_br self.mock_phys_bridge_cls = mock.patch(self._BR_PHYS_CLASS, autospec=True).start() self.mock_phys_bridge_cls.side_effect = lookup_br self.mock_tun_bridge_cls = mock.patch(self._BR_TUN_CLASS, autospec=True).start() self.mock_tun_bridge_cls.side_effect = lookup_br self.mock_aux_bridge_cls = mock.patch( 'neutron.agent.common.ovs_lib.OVSBridge', autospec=True).start() self.mock_aux_bridge_cls.side_effect = lookup_br self.mock_int_bridge = self.ovs_bridges[self.INT_BRIDGE] self.mock_int_bridge.add_port.return_value = self.MAP_TUN_INT_OFPORT self.mock_int_bridge.add_patch_port.side_effect = ( lambda tap, peer: self.ovs_int_ofports[tap]) self.mock_int_bridge.port_exists.return_value = False self.mock_int_bridge.get_vif_ports.return_value = [] self.mock_int_bridge.get_ports_attributes.return_value = [] self.mock_int_bridge.db_get_val.return_value = {} self.mock_map_tun_bridge = self.ovs_bridges[self.MAP_TUN_BRIDGE] self.mock_map_tun_bridge.br_name = self.MAP_TUN_BRIDGE self.mock_map_tun_bridge.add_port.return_value = ( self.MAP_TUN_PHY_OFPORT) self.mock_map_tun_bridge.add_patch_port.return_value = ( self.MAP_TUN_PHY_OFPORT) self.mock_map_tun_bridge.port_exists.return_value = False self.mock_tun_bridge = self.ovs_bridges[self.TUN_BRIDGE] self.mock_tun_bridge.add_port.return_value = self.INT_OFPORT self.mock_tun_bridge.add_patch_port.return_value = self.INT_OFPORT self.ipdevice = mock.patch.object(ip_lib, 'IPDevice').start() self.ipwrapper = mock.patch.object(ip_lib, 'IPWrapper').start() add_veth = self.ipwrapper.return_value.add_veth add_veth.return_value = [self.inta, self.intb] self.get_bridges = mock.patch.object(ovs_lib.BaseOVS, 'get_bridges').start() self.get_bridges.return_value = [self.INT_BRIDGE, self.TUN_BRIDGE, self.MAP_TUN_BRIDGE, self.AUX_BRIDGE] self.get_bridge_external_bridge_id = mock.patch.object( ovs_lib.BaseOVS, 'get_bridge_external_bridge_id').start() self.get_bridge_external_bridge_id.side_effect = ( lambda bridge, log_errors: bridge if bridge in self.ovs_bridges else None) self.execute = mock.patch('neutron.agent.common.utils.execute').start() self.mock_check_bridge_datapath_id = mock.patch.object( self.mod_agent.OVSNeutronAgent, '_check_bridge_datapath_id').start() self._define_expected_calls() def _define_expected_calls(self, arp_responder=False): self.mock_int_bridge_cls_expected = [ mock.call(self.INT_BRIDGE, datapath_type=mock.ANY), ] self.mock_phys_bridge_cls_expected = [ mock.call(self.MAP_TUN_BRIDGE, datapath_type=mock.ANY), ] self.mock_tun_bridge_cls_expected = [ mock.call(self.TUN_BRIDGE, datapath_type=mock.ANY), ] self.mock_int_bridge = self.ovs_bridges[self.INT_BRIDGE] self.mock_int_bridge_expected = [ mock.call.create(), mock.call.set_secure_mode(), mock.call.setup_controllers(mock.ANY), mock.call.setup_default_table(), ] self.mock_map_tun_bridge_expected = [ mock.call.create(), mock.call.set_secure_mode(), mock.call.setup_controllers(mock.ANY), mock.call.setup_default_table(), mock.call.port_exists('phy-%s' % self.MAP_TUN_BRIDGE), mock.call.add_patch_port('phy-%s' % self.MAP_TUN_BRIDGE, constants.NONEXISTENT_PEER), ] self.mock_int_bridge_expected += [ mock.call.db_get_val('Interface', 'int-%s' % self.MAP_TUN_BRIDGE, 'type', log_errors=False), mock.call.port_exists('int-%s' % self.MAP_TUN_BRIDGE), mock.call.add_patch_port('int-%s' % self.MAP_TUN_BRIDGE, constants.NONEXISTENT_PEER), ] self.mock_int_bridge_expected += [ mock.call.drop_port(in_port=self.MAP_TUN_INT_OFPORT), mock.call.set_db_attribute( 'Interface', 'int-%s' % self.MAP_TUN_BRIDGE, 'options', {'peer': 'phy-%s' % self.MAP_TUN_BRIDGE}), ] self.mock_map_tun_bridge_expected += [ mock.call.drop_port(in_port=self.MAP_TUN_PHY_OFPORT), mock.call.set_db_attribute( 'Interface', 'phy-%s' % self.MAP_TUN_BRIDGE, 'options', {'peer': 'int-%s' % self.MAP_TUN_BRIDGE}), ] self.mock_aux_bridge = self.ovs_bridges[self.AUX_BRIDGE] self.mock_aux_bridge_expected = [ ] self.mock_tun_bridge_expected = [ mock.call.create(secure_mode=True), mock.call.setup_controllers(mock.ANY), mock.call.port_exists('patch-int'), nonzero(mock.call.port_exists()), mock.call.add_patch_port('patch-int', 'patch-tun'), ] self.mock_int_bridge_expected += [ mock.call.port_exists('patch-tun'), mock.call.add_patch_port('patch-tun', 'patch-int'), ] self.mock_int_bridge_expected += [ mock.call.get_vif_ports((ovs_lib.INVALID_OFPORT, ovs_lib.UNASSIGNED_OFPORT)), mock.call.get_ports_attributes( 'Port', columns=['name', 'other_config', 'tag'], ports=[]) ] self.mock_tun_bridge_expected += [ mock.call.setup_default_table(self.INT_OFPORT, arp_responder), ] self.ipdevice_expected = [] self.ipwrapper_expected = [mock.call()] self.get_bridges_expected = [mock.call(), mock.call()] self.inta_expected = [] self.intb_expected = [] self.execute_expected = [] def _build_agent(self, **config_opts_agent): """Configure and initialize OVS agent. :param config_opts_agent: a dict with options to override the default values for the AGENT group. """ bridge_classes = { 'br_int': self.mock_int_bridge_cls, 'br_phys': self.mock_phys_bridge_cls, 'br_tun': self.mock_tun_bridge_cls, } cfg.CONF.set_override('integration_bridge', self.INT_BRIDGE, 'OVS') cfg.CONF.set_override('tunnel_bridge', self.TUN_BRIDGE, 'OVS') cfg.CONF.set_override('local_ip', '10.0.0.1', 'OVS') cfg.CONF.set_override('bridge_mappings', self.NET_MAPPING, 'OVS') cfg.CONF.set_override('polling_interval', 2, 'AGENT') cfg.CONF.set_override('tunnel_types', ['gre'], 'AGENT') cfg.CONF.set_override('veth_mtu', self.VETH_MTU, 'AGENT') cfg.CONF.set_override('minimize_polling', False, 'AGENT') cfg.CONF.set_override('use_veth_interconnection', self.USE_VETH_INTERCONNECTION, 'OVS') for k, v in config_opts_agent.items(): cfg.CONF.set_override(k, v, 'AGENT') ext_mgr = mock.Mock() return self.mod_agent.OVSNeutronAgent( bridge_classes, ext_mgr, cfg.CONF) def _verify_mock_call(self, mock_obj, expected): mock_obj.assert_has_calls(expected) self.assertEqual(expected, mock_obj.mock_calls) def _verify_mock_calls(self): self._verify_mock_call(self.mock_int_bridge_cls, self.mock_int_bridge_cls_expected) self._verify_mock_call(self.mock_tun_bridge_cls, self.mock_tun_bridge_cls_expected) self._verify_mock_call(self.mock_phys_bridge_cls, self.mock_phys_bridge_cls_expected) self._verify_mock_call(self.mock_int_bridge, self.mock_int_bridge_expected) self._verify_mock_call(self.mock_map_tun_bridge, self.mock_map_tun_bridge_expected) self._verify_mock_call(self.mock_tun_bridge, self.mock_tun_bridge_expected) self._verify_mock_call(self.mock_aux_bridge, self.mock_aux_bridge_expected) self._verify_mock_call(self.ipdevice, self.ipdevice_expected) self._verify_mock_call(self.ipwrapper, self.ipwrapper_expected) self._verify_mock_call(self.get_bridges, self.get_bridges_expected) self._verify_mock_call(self.inta, self.inta_expected) self._verify_mock_call(self.intb, self.intb_expected) self._verify_mock_call(self.execute, self.execute_expected) def test_construct(self): agent = self._build_agent() self.assertEqual(agent.agent_id, 'ovs-agent-%s' % cfg.CONF.host) self._verify_mock_calls() # TODO(ethuleau): Initially, local ARP responder is be dependent to the # ML2 l2 population mechanism driver. # The next two tests use l2_pop flag to test ARP responder def test_construct_with_arp_responder(self): self._build_agent(l2_population=True, arp_responder=True) self._define_expected_calls(True) self._verify_mock_calls() def test_construct_without_arp_responder(self): self._build_agent(l2_population=False, arp_responder=True) self._verify_mock_calls() def test_construct_vxlan(self): self._build_agent(tunnel_types=['vxlan']) self._verify_mock_calls() def test_provision_local_vlan(self): ofports = list(TUN_OFPORTS[n_const.TYPE_GRE].values()) self.mock_tun_bridge_expected += [ mock.call.install_flood_to_tun(LV_ID, LS_ID, ofports), mock.call.provision_local_vlan( network_type=n_const.TYPE_GRE, lvid=LV_ID, segmentation_id=LS_ID), ] a = self._build_agent() a.available_local_vlans = set([LV_ID]) a.tun_br_ofports = TUN_OFPORTS a.provision_local_vlan(NET_UUID, n_const.TYPE_GRE, None, LS_ID) self._verify_mock_calls() def test_provision_local_vlan_flat(self): self.mock_map_tun_bridge_expected.append( mock.call.provision_local_vlan( port=self.MAP_TUN_PHY_OFPORT, lvid=LV_ID, segmentation_id=None, distributed=False)) self.mock_int_bridge_expected.append( mock.call.provision_local_vlan( port=self.INT_OFPORT, lvid=LV_ID, segmentation_id=None)) a = self._build_agent() a.available_local_vlans = set([LV_ID]) a.phys_brs['net1'] = self.mock_map_tun_bridge a.phys_ofports['net1'] = self.MAP_TUN_PHY_OFPORT a.int_ofports['net1'] = self.INT_OFPORT a.provision_local_vlan(NET_UUID, n_const.TYPE_FLAT, 'net1', LS_ID) self._verify_mock_calls() def test_provision_local_vlan_flat_fail(self): a = self._build_agent() a.provision_local_vlan(NET_UUID, n_const.TYPE_FLAT, 'net2', LS_ID) self._verify_mock_calls() def test_provision_local_vlan_vlan(self): self.mock_map_tun_bridge_expected.append( mock.call.provision_local_vlan( port=self.MAP_TUN_PHY_OFPORT, lvid=LV_ID, segmentation_id=LS_ID, distributed=False)) self.mock_int_bridge_expected.append( mock.call.provision_local_vlan( port=self.INT_OFPORT, lvid=LV_ID, segmentation_id=LS_ID)) a = self._build_agent() a.available_local_vlans = set([LV_ID]) a.phys_brs['net1'] = self.mock_map_tun_bridge a.phys_ofports['net1'] = self.MAP_TUN_PHY_OFPORT a.int_ofports['net1'] = self.INT_OFPORT a.provision_local_vlan(NET_UUID, n_const.TYPE_VLAN, 'net1', LS_ID) self._verify_mock_calls() def test_provision_local_vlan_vlan_fail(self): a = self._build_agent() a.provision_local_vlan(NET_UUID, n_const.TYPE_VLAN, 'net2', LS_ID) self._verify_mock_calls() def test_reclaim_local_vlan(self): self.mock_tun_bridge_expected += [ mock.call.reclaim_local_vlan(network_type='gre', segmentation_id=LS_ID), mock.call.delete_flood_to_tun(LV_ID), mock.call.delete_unicast_to_tun(LV_ID, None), mock.call.delete_arp_responder(LV_ID, None), ] a = self._build_agent() a.available_local_vlans = set() a.vlan_manager.add(NET_UUID, *self.LVM_DATA) a.reclaim_local_vlan(NET_UUID) self.assertIn(self.LVM_DATA[0], a.available_local_vlans) self._verify_mock_calls() def test_reclaim_local_vlan_flat(self): self.mock_map_tun_bridge_expected.append( mock.call.reclaim_local_vlan( port=self.MAP_TUN_PHY_OFPORT, lvid=self.LVM_FLAT_DATA[0])) self.mock_int_bridge_expected.append( mock.call.reclaim_local_vlan( port=self.INT_OFPORT, segmentation_id=None)) a = self._build_agent() a.phys_brs['net1'] = self.mock_map_tun_bridge a.phys_ofports['net1'] = self.MAP_TUN_PHY_OFPORT a.int_ofports['net1'] = self.INT_OFPORT a.available_local_vlans = set() a.vlan_manager.add(NET_UUID, *self.LVM_FLAT_DATA) a.reclaim_local_vlan(NET_UUID) self.assertIn(self.LVM_FLAT_DATA[0], a.available_local_vlans) self._verify_mock_calls() def test_reclaim_local_vlan_vlan(self): self.mock_map_tun_bridge_expected.append( mock.call.reclaim_local_vlan( port=self.MAP_TUN_PHY_OFPORT, lvid=self.LVM_VLAN_DATA[0])) self.mock_int_bridge_expected.append( mock.call.reclaim_local_vlan( port=self.INT_OFPORT, segmentation_id=LS_ID)) a = self._build_agent() a.phys_brs['net1'] = self.mock_map_tun_bridge a.phys_ofports['net1'] = self.MAP_TUN_PHY_OFPORT a.int_ofports['net1'] = self.INT_OFPORT a.available_local_vlans = set() a.vlan_manager.add(NET_UUID, *self.LVM_VLAN_DATA) a.reclaim_local_vlan(NET_UUID) self.assertIn(self.LVM_VLAN_DATA[0], a.available_local_vlans) self._verify_mock_calls() def test_port_bound(self): vlan_mapping = {'segmentation_id': str(LS_ID), 'physical_network': 'None', 'net_uuid': NET_UUID, 'network_type': 'gre'} self.mock_int_bridge_expected += [ mock.call.db_get_val('Port', 'port', 'other_config'), mock.call.set_db_attribute('Port', VIF_PORT.port_name, 'other_config', vlan_mapping)] a = self._build_agent() a.vlan_manager.add(NET_UUID, *self.LVM_DATA) a.local_dvr_map = {} self.ovs_bridges[self.INT_BRIDGE].db_get_val.return_value = {} a.port_bound(VIF_PORT, NET_UUID, 'gre', None, LS_ID, FIXED_IPS, VM_DEVICE_OWNER, False) self._verify_mock_calls() def test_port_unbound(self): with mock.patch.object(self.mod_agent.OVSNeutronAgent, 'reclaim_local_vlan') as reclaim_local_vlan: a = self._build_agent() a.vlan_manager.add(NET_UUID, *self.LVM_DATA) a.port_unbound(VIF_ID, NET_UUID) reclaim_local_vlan.assert_called_once_with(NET_UUID) self._verify_mock_calls() def test_port_dead(self): self.mock_int_bridge_expected += [ mock.call.db_get_val('Port', VIF_PORT.port_name, 'tag', log_errors=True), mock.call.set_db_attribute( 'Port', VIF_PORT.port_name, 'tag', constants.DEAD_VLAN_TAG, log_errors=True), mock.call.drop_port(in_port=VIF_PORT.ofport), ] a = self._build_agent() a.available_local_vlans = set([LV_ID]) a.vlan_manager.add(NET_UUID, *self.LVM_DATA) self.ovs_bridges[self.INT_BRIDGE].db_get_val.return_value = mock.Mock() a.port_dead(VIF_PORT) self._verify_mock_calls() def test_tunnel_update(self): tunnel_port = '9999' self.mock_tun_bridge.add_tunnel_port.return_value = tunnel_port self.mock_tun_bridge_expected += [ mock.call.add_tunnel_port('gre-0a000a01', '10.0.10.1', '10.0.0.1', 'gre', 4789, True, False, None), mock.call.setup_tunnel_port('gre', tunnel_port), ] a = self._build_agent() a.tunnel_update( mock.sentinel.ctx, tunnel_ip='10.0.10.1', tunnel_type=n_const.TYPE_GRE) self._verify_mock_calls() def test_tunnel_update_self(self): a = self._build_agent() a.tunnel_update( mock.sentinel.ctx, tunnel_ip='10.0.0.1') self._verify_mock_calls() def test_daemon_loop(self): reply_ge_1 = {'added': [{'name': 'tap0', 'ofport': 3, 'external_ids': { 'attached-mac': 'test_mac'}}], 'removed': []} reply_ge_2 = {'added': [], 'removed': [{'name': 'tap0', 'ofport': 3, 'external_ids': { 'attached-mac': 'test_mac'}}]} reply_pe_1 = {'current': set(['tap0']), 'added': set(['tap0']), 'removed': set([])} reply_pe_2 = {'current': set([]), 'added': set([]), 'removed': set(['tap0'])} reply_ancillary = {'current': set([]), 'added': set([]), 'removed': set([])} self.mock_int_bridge_expected += [ mock.call.check_canary_table(), mock.call.cleanup_flows(), mock.call.check_canary_table() ] self.mock_map_tun_bridge_expected += [ mock.call.cleanup_flows(), ] self.mock_tun_bridge_expected += [ mock.call.cleanup_flows() ] # No cleanup is expected on ancillary bridge self.ovs_bridges[self.INT_BRIDGE].check_canary_table.return_value = \ constants.OVS_NORMAL with mock.patch.object(log.KeywordArgumentAdapter, 'exception') as log_exception,\ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'process_ports_events') as process_p_events,\ mock.patch.object( self.mod_agent.OVSNeutronAgent, 'process_network_ports') as process_network_ports,\ mock.patch.object(self.mod_agent.OVSNeutronAgent, 'tunnel_sync'),\ mock.patch.object(time, 'sleep'),\ mock.patch.object( self.mod_agent.OVSNeutronAgent, 'update_stale_ofport_rules') as update_stale: log_exception.side_effect = Exception( 'Fake exception to get out of the loop') update_stale.return_value = [] devices_not_ready = set() process_p_events.side_effect = [ (reply_pe_1, reply_ancillary, devices_not_ready), (reply_pe_2, reply_ancillary, devices_not_ready)] interface_polling = mock.Mock() interface_polling.get_events.side_effect = [reply_ge_1, reply_ge_2] failed_devices = {'removed': set([]), 'added': set([])} failed_ancillary_devices = {'removed': set([]), 'added': set([])} process_network_ports.side_effect = [ failed_devices, Exception('Fake exception to get out of the loop')] n_agent = self._build_agent() # Hack to test loop # We start method and expect it will raise after 2nd loop # If something goes wrong, assert_has_calls below will catch it try: n_agent.rpc_loop(interface_polling) except Exception: pass # FIXME(salv-orlando): There should not be assertions on log # messages log_exception.assert_called_once_with( "Error while processing VIF ports") process_p_events.assert_has_calls([ mock.call(reply_ge_1, set(), set(), devices_not_ready, failed_devices, failed_ancillary_devices, set()), mock.call(reply_ge_2, set(['tap0']), set(), devices_not_ready, failed_devices, failed_ancillary_devices, set()) ]) process_network_ports.assert_has_calls([ mock.call({'current': set(['tap0']), 'removed': set([]), 'added': set(['tap0'])}, False), ]) self.assertTrue(update_stale.called) self._verify_mock_calls() class TunnelTestOFCtl(TunnelTest, ovs_test_base.OVSOFCtlTestBase): pass class TunnelTestRyu(TunnelTest, ovs_test_base.OVSRyuTestBase): pass class TunnelTestUseVethInterco(TunnelTest): USE_VETH_INTERCONNECTION = True def _define_expected_calls(self, arp_responder=False): self.mock_int_bridge_cls_expected = [ mock.call(self.INT_BRIDGE, datapath_type=mock.ANY), ] self.mock_phys_bridge_cls_expected = [ mock.call(self.MAP_TUN_BRIDGE, datapath_type=mock.ANY), ] self.mock_tun_bridge_cls_expected = [ mock.call(self.TUN_BRIDGE, datapath_type=mock.ANY), ] self.mock_int_bridge_expected = [ mock.call.create(), mock.call.set_secure_mode(), mock.call.setup_controllers(mock.ANY), mock.call.setup_default_table(), ] self.mock_map_tun_bridge_expected = [ mock.call.create(), mock.call.set_secure_mode(), mock.call.setup_controllers(mock.ANY), mock.call.setup_default_table(), mock.call.add_port('phy-%s' % self.MAP_TUN_BRIDGE), ] self.mock_int_bridge_expected += [ mock.call.db_get_val('Interface', 'int-%s' % self.MAP_TUN_BRIDGE, 'type', log_errors=False), mock.call.add_port('int-%s' % self.MAP_TUN_BRIDGE) ] self.mock_int_bridge_expected += [ mock.call.drop_port(in_port=self.MAP_TUN_INT_OFPORT), ] self.mock_map_tun_bridge_expected += [ mock.call.drop_port(in_port=self.MAP_TUN_PHY_OFPORT), ] self.mock_aux_bridge = self.ovs_bridges[self.AUX_BRIDGE] self.mock_aux_bridge_expected = [ ] self.mock_tun_bridge_expected = [ mock.call.create(secure_mode=True), mock.call.setup_controllers(mock.ANY), mock.call.port_exists('patch-int'), nonzero(mock.call.port_exists()), mock.call.add_patch_port('patch-int', 'patch-tun'), ] self.mock_int_bridge_expected += [ mock.call.port_exists('patch-tun'), mock.call.add_patch_port('patch-tun', 'patch-int') ] self.mock_int_bridge_expected += [ mock.call.get_vif_ports((ovs_lib.INVALID_OFPORT, ovs_lib.UNASSIGNED_OFPORT)), mock.call.get_ports_attributes( 'Port', columns=['name', 'other_config', 'tag'], ports=[]) ] self.mock_tun_bridge_expected += [ mock.call.setup_default_table(self.INT_OFPORT, arp_responder), ] self.ipdevice_expected = [ mock.call('int-%s' % self.MAP_TUN_BRIDGE), mock.call().exists(), nonzero(mock.call().exists()), mock.call().link.delete() ] self.ipwrapper_expected = [ mock.call(), mock.call().add_veth('int-%s' % self.MAP_TUN_BRIDGE, 'phy-%s' % self.MAP_TUN_BRIDGE) ] self.get_bridges_expected = [mock.call(), mock.call()] self.inta_expected = [mock.call.link.set_up()] self.intb_expected = [mock.call.link.set_up()] self.execute_expected = [mock.call(['udevadm', 'settle', '--timeout=10'])] class TunnelTestUseVethIntercoOFCtl(TunnelTestUseVethInterco, ovs_test_base.OVSOFCtlTestBase): pass class TunnelTestUseVethIntercoRyu(TunnelTestUseVethInterco, ovs_test_base.OVSRyuTestBase): pass class TunnelTestWithMTU(TunnelTestUseVethInterco): VETH_MTU = 1500 def _define_expected_calls(self, arp_responder=False): super(TunnelTestWithMTU, self)._define_expected_calls(arp_responder) self.inta_expected.append(mock.call.link.set_mtu(self.VETH_MTU)) self.intb_expected.append(mock.call.link.set_mtu(self.VETH_MTU)) class TunnelTestWithMTUOFCtl(TunnelTestWithMTU, ovs_test_base.OVSOFCtlTestBase): pass class TunnelTestWithMTURyu(TunnelTestWithMTU, ovs_test_base.OVSRyuTestBase): pass neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/__init__.py0000664000175000017500000000000013553660046030532 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/ovs_test_base.py0000664000175000017500000000622213553660047031650 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014 Fumihiko Kakuma # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import mock from oslo_utils import importutils from neutron.tests import base from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \ import fake_oflib _AGENT_PACKAGE = 'neutron.plugins.ml2.drivers.openvswitch.agent' _AGENT_NAME = _AGENT_PACKAGE + '.ovs_neutron_agent' _DVR_AGENT_NAME = ('neutron.plugins.ml2.drivers.openvswitch.agent.' 'ovs_dvr_neutron_agent') class OVSAgentConfigTestBase(base.BaseTestCase): def setUp(self): super(OVSAgentConfigTestBase, self).setUp() self.mod_agent = importutils.import_module(_AGENT_NAME) self.mod_dvr_agent = importutils.import_module(_DVR_AGENT_NAME) class OVSAgentTestBase(OVSAgentConfigTestBase): def setUp(self): super(OVSAgentTestBase, self).setUp() conn_patcher = mock.patch( 'neutron.agent.ovsdb.impl_idl._connection') conn_patcher.start() self.addCleanup(conn_patcher.stop) self.br_int_cls = importutils.import_class(self._BR_INT_CLASS) self.br_phys_cls = importutils.import_class(self._BR_PHYS_CLASS) self.br_tun_cls = importutils.import_class(self._BR_TUN_CLASS) def _bridge_classes(self): return { 'br_int': self.br_int_cls, 'br_phys': self.br_phys_cls, 'br_tun': self.br_tun_cls, } class OVSOFCtlTestBase(OVSAgentTestBase): _DRIVER_PACKAGE = _AGENT_PACKAGE + '.openflow.ovs_ofctl' _BR_INT_CLASS = _DRIVER_PACKAGE + '.br_int.OVSIntegrationBridge' _BR_TUN_CLASS = _DRIVER_PACKAGE + '.br_tun.OVSTunnelBridge' _BR_PHYS_CLASS = _DRIVER_PACKAGE + '.br_phys.OVSPhysicalBridge' class OVSRyuTestBase(OVSAgentTestBase): _DRIVER_PACKAGE = _AGENT_PACKAGE + '.openflow.native' _BR_INT_CLASS = _DRIVER_PACKAGE + '.br_int.OVSIntegrationBridge' _BR_TUN_CLASS = _DRIVER_PACKAGE + '.br_tun.OVSTunnelBridge' _BR_PHYS_CLASS = _DRIVER_PACKAGE + '.br_phys.OVSPhysicalBridge' def setUp(self): self.fake_oflib_of = fake_oflib.patch_fake_oflib_of() self.fake_oflib_of.start() self.addCleanup(self.fake_oflib_of.stop) super(OVSRyuTestBase, self).setUp() ryu_app = mock.Mock() self.br_int_cls = functools.partial(self.br_int_cls, ryu_app=ryu_app) self.br_phys_cls = functools.partial(self.br_phys_cls, ryu_app=ryu_app) self.br_tun_cls = functools.partial(self.br_tun_cls, ryu_app=ryu_app) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/0000775000175000017500000000000013553660157032210 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_driver.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_d0000664000175000017500000001762213553660047034305 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import mock from neutron_lib import constants from neutron_lib import context from oslo_utils import uuidutils from neutron.objects.qos import policy from neutron.objects.qos import rule from neutron.plugins.ml2.drivers.openvswitch.agent import ( ovs_agent_extension_api as ovs_ext_api) from neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers import ( qos_driver) from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import ( ovs_bridge) from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent import ( ovs_test_base) class QosOVSAgentDriverTestCase(ovs_test_base.OVSAgentConfigTestBase): def setUp(self): super(QosOVSAgentDriverTestCase, self).setUp() conn_patcher = mock.patch( 'neutron.agent.ovsdb.impl_idl._connection') conn_patcher.start() self.addCleanup(conn_patcher.stop) self.context = context.get_admin_context() self.qos_driver = qos_driver.QosOVSAgentDriver() self.agent_api = ovs_ext_api.OVSAgentExtensionAPI( ovs_bridge.OVSAgentBridge('br-int'), ovs_bridge.OVSAgentBridge('br-tun')) self.qos_driver.consume_api(self.agent_api) self.qos_driver.initialize() self.qos_driver.br_int = mock.Mock() self.qos_driver.br_int.get_egress_bw_limit_for_port = mock.Mock( return_value=(1000, 10)) self.get_egress = self.qos_driver.br_int.get_egress_bw_limit_for_port self.get_ingress = self.qos_driver.br_int.get_ingress_bw_limit_for_port self.qos_driver.br_int.dump_flows_for = mock.Mock(return_value=None) self.qos_driver.br_int.del_egress_bw_limit_for_port = mock.Mock() self.delete_egress = ( self.qos_driver.br_int.delete_egress_bw_limit_for_port) self.delete_ingress = ( self.qos_driver.br_int.delete_ingress_bw_limit_for_port) self.create_egress = ( self.qos_driver.br_int.create_egress_bw_limit_for_port) self.update_ingress = ( self.qos_driver.br_int.update_ingress_bw_limit_for_port) self.rules = [ self._create_bw_limit_rule_obj(constants.EGRESS_DIRECTION), self._create_bw_limit_rule_obj(constants.INGRESS_DIRECTION), self._create_dscp_marking_rule_obj()] self.qos_policy = self._create_qos_policy_obj(self.rules) self.port = self._create_fake_port(self.qos_policy.id) def _create_bw_limit_rule_obj(self, direction): rule_obj = rule.QosBandwidthLimitRule() rule_obj.id = uuidutils.generate_uuid() rule_obj.max_kbps = 2 rule_obj.max_burst_kbps = 200 rule_obj.direction = direction rule_obj.obj_reset_changes() return rule_obj def _create_dscp_marking_rule_obj(self): rule_obj = rule.QosDscpMarkingRule() rule_obj.id = uuidutils.generate_uuid() rule_obj.dscp_mark = 32 rule_obj.obj_reset_changes() return rule_obj def _create_qos_policy_obj(self, rules): policy_dict = {'id': uuidutils.generate_uuid(), 'project_id': uuidutils.generate_uuid(), 'name': 'test', 'description': 'test', 'shared': False, 'rules': rules} policy_obj = policy.QosPolicy(self.context, **policy_dict) policy_obj.obj_reset_changes() for policy_rule in policy_obj.rules: policy_rule.qos_policy_id = policy_obj.id policy_rule.obj_reset_changes() return policy_obj def _create_fake_port(self, policy_id): self.port_name = 'fakeport' class FakeVifPort(object): port_name = self.port_name ofport = 111 return {'vif_port': FakeVifPort(), 'qos_policy_id': policy_id, 'network_qos_policy_id': None, 'port_id': uuidutils.generate_uuid(), 'device_owner': uuidutils.generate_uuid()} def test_create_new_rules(self): self.qos_driver.br_int.get_egress_bw_limit_for_port = mock.Mock( return_value=(None, None)) self.qos_driver.br_int.get_ingress_bw_limit_for_port = mock.Mock( return_value=(None, None)) self.qos_driver.create(self.port, self.qos_policy) self.assertEqual(0, self.delete_egress.call_count) self.assertEqual(0, self.delete_ingress.call_count) self.create_egress.assert_called_once_with( self.port_name, self.rules[0].max_kbps, self.rules[0].max_burst_kbps) self.update_ingress.assert_called_once_with( self.port_name, self.rules[1].max_kbps, self.rules[1].max_burst_kbps) self._assert_dscp_rule_create_updated() def test_create_existing_rules(self): self.qos_driver.create(self.port, self.qos_policy) self._assert_rules_create_updated() self._assert_dscp_rule_create_updated() def test_update_rules(self): self.qos_driver.update(self.port, self.qos_policy) self._assert_rules_create_updated() self._assert_dscp_rule_create_updated() def test_update_rules_no_vif_port(self): port = copy.copy(self.port) port.pop("vif_port") self.qos_driver.update(port, self.qos_policy) self.create_egress.assert_not_called() self.update_ingress.assert_not_called() def _test_delete_rules(self, qos_policy): self.qos_driver.br_int.get_ingress_bw_limit_for_port = mock.Mock( return_value=(self.rules[1].max_kbps, self.rules[1].max_burst_kbps)) self.qos_driver.create(self.port, qos_policy) self.qos_driver.delete(self.port, qos_policy) self.delete_egress.assert_called_once_with(self.port_name) self.delete_ingress.assert_called_once_with(self.port_name) def _test_delete_rules_no_policy(self): self.qos_driver.br_int.get_ingress_bw_limit_for_port = mock.Mock( return_value=(self.rules[1].max_kbps, self.rules[1].max_burst_kbps)) self.qos_driver.delete(self.port) self.delete_egress.assert_called_once_with(self.port_name) self.delete_ingress.assert_called_once_with(self.port_name) def test_delete_rules(self): self._test_delete_rules(self.qos_policy) def test_delete_rules_no_policy(self): self._test_delete_rules_no_policy() def test_delete_rules_no_vif_port(self): port = copy.copy(self.port) port.pop("vif_port") self.qos_driver.delete(port, self.qos_policy) self.delete_egress.assert_not_called() self.delete_ingress.assert_not_called() def _assert_rules_create_updated(self): self.create_egress.assert_called_once_with( self.port_name, self.rules[0].max_kbps, self.rules[0].max_burst_kbps) self.update_ingress.assert_called_once_with( self.port_name, self.rules[1].max_kbps, self.rules[1].max_burst_kbps) def _assert_dscp_rule_create_updated(self): # Assert add_flow is the last call self.assertEqual( 'add_flow', self.qos_driver.br_int.method_calls[-1][0]) self.qos_driver.br_int.add_flow.assert_called_once_with( actions='mod_nw_tos:128,load:55->NXM_NX_REG2[0..5],resubmit(,0)', in_port=mock.ANY, priority=65535, reg2=0, table=0) ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/__init__.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/__init__.p0000664000175000017500000000000013553660046034113 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/__init__.py0000664000175000017500000000000013553660046027434 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/mech_driver/0000775000175000017500000000000013553660157027627 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/mech_driver/__init__.py0000664000175000017500000000000013553660046031723 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/mech_driver/test_mech_openvswitch.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/openvswitch/mech_driver/test_mech_openvswitch.0000664000175000017500000002477013553660047034244 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import constants from neutron_lib.plugins.ml2 import api from oslo_config import cfg from neutron.plugins.ml2.drivers.openvswitch.agent.common import ( constants as a_const) from neutron.plugins.ml2.drivers.openvswitch.mech_driver import ( mech_openvswitch) from neutron.tests.unit.plugins.ml2 import _test_mech_agent as base class OpenvswitchMechanismBaseTestCase(base.AgentMechanismBaseTestCase): VIF_TYPE = portbindings.VIF_TYPE_OVS VIF_DETAILS = {portbindings.OVS_DATAPATH_TYPE: 'system', portbindings.CAP_PORT_FILTER: True, portbindings.OVS_HYBRID_PLUG: True} AGENT_TYPE = constants.AGENT_TYPE_OVS GOOD_MAPPINGS = {'fake_physical_network': 'fake_bridge'} GOOD_TUNNEL_TYPES = ['gre', 'vxlan'] GOOD_CONFIGS = {'bridge_mappings': GOOD_MAPPINGS, 'tunnel_types': GOOD_TUNNEL_TYPES} BAD_MAPPINGS = {'wrong_physical_network': 'wrong_bridge'} BAD_TUNNEL_TYPES = ['bad_tunnel_type'] BAD_CONFIGS = {'bridge_mappings': BAD_MAPPINGS, 'tunnel_types': BAD_TUNNEL_TYPES} AGENTS = [{'alive': True, 'configurations': GOOD_CONFIGS, 'host': 'host'}] AGENTS_DEAD = [{'alive': False, 'configurations': GOOD_CONFIGS, 'host': 'dead_host'}] AGENTS_BAD = [{'alive': False, 'configurations': GOOD_CONFIGS, 'host': 'bad_host_1'}, {'alive': True, 'configurations': BAD_CONFIGS, 'host': 'bad_host_2'}] def setUp(self): super(OpenvswitchMechanismBaseTestCase, self).setUp() cfg.CONF.set_override('firewall_driver', 'iptables_hybrid', 'SECURITYGROUP') self.driver = mech_openvswitch.OpenvswitchMechanismDriver() self.driver.initialize() def test__set_bridge_name_notify(self): def fake_callback(resource, event, trigger, payload=None): trigger('fake-br-name') registry.subscribe(fake_callback, a_const.OVS_BRIDGE_NAME, events.BEFORE_READ) fake_vif_details = {} self.driver._set_bridge_name('foo', fake_vif_details) self.assertEqual( 'fake-br-name', fake_vif_details.get(portbindings.VIF_DETAILS_BRIDGE_NAME, '')) class OpenvswitchMechanismSGDisabledBaseTestCase( OpenvswitchMechanismBaseTestCase): VIF_DETAILS = {portbindings.OVS_DATAPATH_TYPE: 'system', portbindings.CAP_PORT_FILTER: False, portbindings.OVS_HYBRID_PLUG: False} def setUp(self): cfg.CONF.set_override('enable_security_group', False, group='SECURITYGROUP') super(OpenvswitchMechanismSGDisabledBaseTestCase, self).setUp() class OpenvswitchMechanismHybridPlugTestCase(OpenvswitchMechanismBaseTestCase): def _make_port_ctx(self, agents): segments = [{api.ID: 'local_segment_id', api.NETWORK_TYPE: 'local'}] return base.FakePortContext(self.AGENT_TYPE, agents, segments, vnic_type=self.VNIC_TYPE) def test_backward_compat_with_unreporting_agent(self): hybrid = portbindings.OVS_HYBRID_PLUG # agent didn't report so it should be hybrid based on server config context = self._make_port_ctx(self.AGENTS) self.driver.bind_port(context) self.assertTrue(context._bound_vif_details[hybrid]) self.driver.vif_details[hybrid] = False context = self._make_port_ctx(self.AGENTS) self.driver.bind_port(context) self.assertFalse(context._bound_vif_details[hybrid]) def test_hybrid_plug_true_if_agent_requests(self): hybrid = portbindings.OVS_HYBRID_PLUG # set server side default to false and ensure that hybrid becomes # true if requested by the agent self.driver.vif_details[hybrid] = False agents = [{'alive': True, 'configurations': {hybrid: True}, 'host': 'host'}] context = self._make_port_ctx(agents) self.driver.bind_port(context) self.assertTrue(context._bound_vif_details[hybrid]) def test_hybrid_plug_false_if_agent_requests(self): hybrid = portbindings.OVS_HYBRID_PLUG # set server side default to true and ensure that hybrid becomes # false if requested by the agent self.driver.vif_details[hybrid] = True agents = [{'alive': True, 'configurations': {hybrid: False}, 'host': 'host'}] context = self._make_port_ctx(agents) self.driver.bind_port(context) self.assertFalse(context._bound_vif_details[hybrid]) class OpenvswitchMechanismGenericTestCase(OpenvswitchMechanismBaseTestCase, base.AgentMechanismGenericTestCase): pass class OpenvswitchMechanismLocalTestCase(OpenvswitchMechanismBaseTestCase, base.AgentMechanismLocalTestCase): pass class OpenvswitchMechanismFlatTestCase(OpenvswitchMechanismBaseTestCase, base.AgentMechanismFlatTestCase): pass class OpenvswitchMechanismVlanTestCase(OpenvswitchMechanismBaseTestCase, base.AgentMechanismVlanTestCase): pass class OpenvswitchMechanismGreTestCase(OpenvswitchMechanismBaseTestCase, base.AgentMechanismGreTestCase): pass class OpenvswitchMechanismSGDisabledLocalTestCase( OpenvswitchMechanismSGDisabledBaseTestCase, base.AgentMechanismLocalTestCase): pass class OpenvswitchMechanismFirewallUndefinedTestCase( OpenvswitchMechanismBaseTestCase, base.AgentMechanismLocalTestCase): VIF_DETAILS = {portbindings.OVS_DATAPATH_TYPE: 'system', portbindings.CAP_PORT_FILTER: True, portbindings.OVS_HYBRID_PLUG: True} def setUp(self): # this simple test case just ensures backward compatibility where # the server has no firewall driver configured, which should result # in hybrid plugging. super(OpenvswitchMechanismFirewallUndefinedTestCase, self).setUp() cfg.CONF.set_override('firewall_driver', '', 'SECURITYGROUP') self.driver = mech_openvswitch.OpenvswitchMechanismDriver() self.driver.initialize() class OpenvswitchMechanismDPDKTestCase(OpenvswitchMechanismBaseTestCase): GOOD_MAPPINGS = {'fake_physical_network': 'fake_bridge'} GOOD_TUNNEL_TYPES = ['gre', 'vxlan'] VHOST_CONFIGS = {'bridge_mappings': GOOD_MAPPINGS, 'tunnel_types': GOOD_TUNNEL_TYPES, 'datapath_type': a_const.OVS_DATAPATH_NETDEV, 'ovs_capabilities': { 'iface_types': [a_const.OVS_DPDK_VHOST_USER]}} VHOST_SERVER_CONFIGS = {'bridge_mappings': GOOD_MAPPINGS, 'tunnel_types': GOOD_TUNNEL_TYPES, 'datapath_type': a_const.OVS_DATAPATH_NETDEV, 'ovs_capabilities': { 'iface_types': [a_const.OVS_DPDK_VHOST_USER_CLIENT]}} SYSTEM_CONFIGS = {'bridge_mappings': GOOD_MAPPINGS, 'tunnel_types': GOOD_TUNNEL_TYPES, 'datapath_type': a_const.OVS_DATAPATH_SYSTEM, 'ovs_capabilities': {'iface_types': []}} AGENT = {'alive': True, 'configurations': VHOST_CONFIGS, 'host': 'host'} AGENT_SERVER = {'alive': True, 'configurations': VHOST_SERVER_CONFIGS, 'host': 'host'} AGENT_SYSTEM = {'alive': True, 'configurations': SYSTEM_CONFIGS, 'host': 'host'} def test_get_vhost_mode(self): ifaces = [] result = self.driver.get_vhost_mode(ifaces) self.assertEqual(portbindings.VHOST_USER_MODE_CLIENT, result) ifaces = [a_const.OVS_DPDK_VHOST_USER] result = self.driver.get_vhost_mode(ifaces) self.assertEqual(portbindings.VHOST_USER_MODE_CLIENT, result) ifaces = [a_const.OVS_DPDK_VHOST_USER_CLIENT] result = self.driver.get_vhost_mode(ifaces) self.assertEqual(portbindings.VHOST_USER_MODE_SERVER, result) def test_get_vif_type(self): result = self.driver.get_vif_type(None, self.AGENT, None) self.assertEqual(portbindings.VIF_TYPE_VHOST_USER, result) result = self.driver.get_vif_type(None, self.AGENT_SERVER, None) self.assertEqual(portbindings.VIF_TYPE_VHOST_USER, result) result = self.driver.get_vif_type(None, self.AGENT_SYSTEM, None) self.assertEqual(portbindings.VIF_TYPE_OVS, result) class OpenvswitchMechanismSRIOVTestCase(OpenvswitchMechanismBaseTestCase): def _make_port_ctx(self, agents, profile=None): segments = [{api.ID: 'local_segment_id', api.NETWORK_TYPE: 'local'}] return base.FakePortContext(self.AGENT_TYPE, agents, segments, vnic_type=portbindings.VNIC_DIRECT, profile=profile) @mock.patch('neutron.plugins.ml2.drivers.mech_agent.' 'SimpleAgentMechanismDriverBase.bind_port') def test_bind_port_sriov_legacy(self, mocked_bind_port): context = self._make_port_ctx(self.AGENTS) self.driver.bind_port(context) mocked_bind_port.assert_not_called() @mock.patch('neutron.plugins.ml2.drivers.mech_agent.' 'SimpleAgentMechanismDriverBase.bind_port') def test_bind_port_sriov_switchdev(self, mocked_bind_port): profile = {'capabilities': ['switchdev']} context = self._make_port_ctx(self.AGENTS, profile=profile) self.driver.bind_port(context) mocked_bind_port.assert_called() neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/test_type_vxlan.py0000664000175000017500000000653313553660047026576 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as p_const from neutron.plugins.ml2.drivers import type_vxlan from neutron.tests.unit.plugins.ml2.drivers import base_type_tunnel from neutron.tests.unit.plugins.ml2 import test_rpc from neutron.tests.unit import testlib_api VXLAN_UDP_PORT_ONE = 9999 VXLAN_UDP_PORT_TWO = 8888 class VxlanTypeTest(base_type_tunnel.TunnelTypeTestMixin, testlib_api.SqlTestCase): DRIVER_MODULE = type_vxlan DRIVER_CLASS = type_vxlan.VxlanTypeDriver TYPE = p_const.TYPE_VXLAN def add_endpoint(self, ip=base_type_tunnel.TUNNEL_IP_ONE, host=base_type_tunnel.HOST_ONE): if ip == base_type_tunnel.TUNNEL_IP_ONE: port = VXLAN_UDP_PORT_ONE else: port = VXLAN_UDP_PORT_TWO return self.driver.add_endpoint(ip, host, port) def test_add_endpoint(self): endpoint = super(VxlanTypeTest, self).test_add_endpoint() self.assertEqual(VXLAN_UDP_PORT_ONE, endpoint.udp_port) def test_get_endpoint_by_host(self): endpoint = super(VxlanTypeTest, self).test_get_endpoint_by_host() self.assertEqual(VXLAN_UDP_PORT_ONE, endpoint.udp_port) def test_get_endpoint_by_ip(self): endpoint = super(VxlanTypeTest, self).test_get_endpoint_by_ip() self.assertEqual(VXLAN_UDP_PORT_ONE, endpoint.udp_port) def test_get_endpoints(self): self.add_endpoint() self.add_endpoint(base_type_tunnel.TUNNEL_IP_TWO, base_type_tunnel.HOST_TWO) endpoints = self.driver.get_endpoints() for endpoint in endpoints: if endpoint['ip_address'] == base_type_tunnel.TUNNEL_IP_ONE: self.assertEqual(VXLAN_UDP_PORT_ONE, endpoint['udp_port']) self.assertEqual(base_type_tunnel.HOST_ONE, endpoint['host']) elif endpoint['ip_address'] == base_type_tunnel.TUNNEL_IP_TWO: self.assertEqual(VXLAN_UDP_PORT_TWO, endpoint['udp_port']) self.assertEqual(base_type_tunnel.HOST_TWO, endpoint['host']) class VxlanTypeMultiRangeTest(base_type_tunnel.TunnelTypeMultiRangeTestMixin, testlib_api.SqlTestCase): DRIVER_CLASS = type_vxlan.VxlanTypeDriver class VxlanTypeRpcCallbackTest(base_type_tunnel.TunnelRpcCallbackTestMixin, test_rpc.RpcCallbacksTestCase, testlib_api.SqlTestCase): DRIVER_CLASS = type_vxlan.VxlanTypeDriver TYPE = p_const.TYPE_VXLAN class VxlanTypeTunnelMTUTest(base_type_tunnel.TunnelTypeMTUTestMixin, testlib_api.SqlTestCase): DRIVER_CLASS = type_vxlan.VxlanTypeDriver TYPE = p_const.TYPE_VXLAN ENCAP_OVERHEAD = p_const.VXLAN_ENCAP_OVERHEAD neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/__init__.py0000664000175000017500000000000013553660046025063 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/0000775000175000017500000000000013553660157025125 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/0000775000175000017500000000000013553660157026223 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py0000664000175000017500000002070613553660047031240 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron.agent.linux import ip_link_support from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ import exceptions as exc from neutron.plugins.ml2.drivers.mech_sriov.agent import pci_lib from neutron.tests import base class TestPciLib(base.BaseTestCase): DEV_NAME = "p7p1" VF_INDEX = 1 VF_INDEX_DISABLE = 0 PF_LINK_SHOW = ('122: p7p1: mtu 1500 qdisc noop' ' state DOWN mode DEFAULT group default qlen 1000') PF_MAC = ' link/ether f4:52:14:2a:3e:c0 brd ff:ff:ff:ff:ff:ff' VF_0_LINK_SHOW = (' vf 0 MAC fa:16:3e:b4:81:ac, vlan 4095, spoof' ' checking off, link-state disable') VF_1_LINK_SHOW = (' vf 1 MAC 00:00:00:00:00:11, vlan 4095, spoof' ' checking off, link-state enable') VF_2_LINK_SHOW = (' vf 2 MAC fa:16:3e:68:4e:79, vlan 4095, spoof' ' checking off, link-state enable') VF_LINK_SHOW = '\n'.join((PF_LINK_SHOW, PF_MAC, VF_0_LINK_SHOW, VF_1_LINK_SHOW, VF_2_LINK_SHOW)) MACVTAP_LINK_SHOW = ('63: macvtap1@enp129s0f1: mtu ' '1500 qdisc noop state DOWN mode DEFAULT group ' 'default qlen 500 link/ether 4a:9b:6d:de:65:b5 brd ' 'ff:ff:ff:ff:ff:ff') MACVTAP_LINK_SHOW2 = ('64: macvtap2@p1p2_1: mtu ' '1500 qdisc noop state DOWN mode DEFAULT group ' 'default qlen 500 link/ether 4a:9b:6d:de:65:b5 brd ' 'ff:ff:ff:ff:ff:ff') IP_LINK_SHOW_WITH_MACVTAP = '\n'.join((VF_LINK_SHOW, MACVTAP_LINK_SHOW)) IP_LINK_SHOW_WITH_MACVTAP2 = '\n'.join((VF_LINK_SHOW, MACVTAP_LINK_SHOW2)) MAC_MAPPING = { 0: "fa:16:3e:b4:81:ac", 1: "00:00:00:00:00:11", 2: "fa:16:3e:68:4e:79", } def setUp(self): super(TestPciLib, self).setUp() self.pci_wrapper = pci_lib.PciDeviceIPWrapper(self.DEV_NAME) def test_get_assigned_macs(self): with mock.patch.object(self.pci_wrapper, "_as_root") as mock_as_root: mock_as_root.return_value = self.VF_LINK_SHOW result = self.pci_wrapper.get_assigned_macs([self.VF_INDEX]) self.assertEqual( {self.VF_INDEX: self.MAC_MAPPING[self.VF_INDEX]}, result) def test_get_assigned_macs_fail(self): with mock.patch.object(self.pci_wrapper, "_as_root") as mock_as_root: mock_as_root.side_effect = Exception() self.assertRaises(exc.IpCommandDeviceError, self.pci_wrapper.get_assigned_macs, [self.VF_INDEX]) def test_get_vf_state_enable(self): with mock.patch.object(self.pci_wrapper, "_as_root") as mock_as_root: mock_as_root.return_value = self.VF_LINK_SHOW result = self.pci_wrapper.get_vf_state(self.VF_INDEX) self.assertTrue(result) def test_get_vf_state_disable(self): with mock.patch.object(self.pci_wrapper, "_as_root") as mock_as_root: mock_as_root.return_value = self.VF_LINK_SHOW result = self.pci_wrapper.get_vf_state(self.VF_INDEX_DISABLE) self.assertFalse(result) def test_get_vf_state_fail(self): with mock.patch.object(self.pci_wrapper, "_as_root") as mock_as_root: mock_as_root.side_effect = Exception() self.assertRaises(exc.IpCommandDeviceError, self.pci_wrapper.get_vf_state, self.VF_INDEX) def test_set_vf_state(self): with mock.patch.object(self.pci_wrapper, "_as_root"): result = self.pci_wrapper.set_vf_state(self.VF_INDEX, True) self.assertIsNone(result) def test_set_vf_state_fail(self): with mock.patch.object(self.pci_wrapper, "_as_root") as mock_as_root: mock_as_root.side_effect = Exception() self.assertRaises(exc.IpCommandDeviceError, self.pci_wrapper.set_vf_state, self.VF_INDEX, True) def test_set_vf_spoofcheck(self): with mock.patch.object(self.pci_wrapper, "_as_root"): result = self.pci_wrapper.set_vf_spoofcheck(self.VF_INDEX, True) self.assertIsNone(result) def test_set_vf_spoofcheck_fail(self): with mock.patch.object(self.pci_wrapper, "_as_root") as mock_as_root: mock_as_root.side_effect = Exception() self.assertRaises(exc.IpCommandDeviceError, self.pci_wrapper.set_vf_spoofcheck, self.VF_INDEX, True) def _set_vf_rate(self, rate, passed=True): if passed: with mock.patch.object(self.pci_wrapper, "_as_root") \ as mock_as_root: result = self.pci_wrapper.set_vf_rate( self.VF_INDEX, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 1000) self.assertIsNone(result) mock_as_root.assert_called_once_with( [], "link", ("set", self.DEV_NAME, "vf", str(self.VF_INDEX), "rate", '1000')) else: with mock.patch.object(self.pci_wrapper, "_as_root", side_effect=Exception()): self.assertRaises(exc.IpCommandDeviceError, self.pci_wrapper.set_vf_rate, self.VF_INDEX, rate, 1000) def test_set_vf_rate_max_rate(self): self._set_vf_rate( ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE) def test_set_vf_rate_max_rate_fail(self): self._set_vf_rate('rate', passed=False) def test_set_vf_rate_min_tx_rate(self): self._set_vf_rate( ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE) def test_set_vf_rate_min_tx_rate_fail(self): self._set_vf_rate( ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE, passed=False) def test_set_vf_state_not_supported(self): with mock.patch.object(self.pci_wrapper, "_as_root") as mock_as_root: mock_as_root.side_effect = Exception( pci_lib.PciDeviceIPWrapper.IP_LINK_OP_NOT_SUPPORTED) self.assertRaises(exc.IpCommandOperationNotSupportedError, self.pci_wrapper.set_vf_state, self.VF_INDEX, state=True) def test_is_macvtap_assigned(self): self.assertTrue(pci_lib.PciDeviceIPWrapper.is_macvtap_assigned( 'enp129s0f1', self.IP_LINK_SHOW_WITH_MACVTAP)) def test_is_macvtap_assigned_interface_with_underscore(self): self.assertTrue(pci_lib.PciDeviceIPWrapper.is_macvtap_assigned( 'p1p2_1', self.IP_LINK_SHOW_WITH_MACVTAP2)) def test_is_macvtap_assigned_not_assigned(self): self.assertFalse(pci_lib.PciDeviceIPWrapper.is_macvtap_assigned( 'enp129s0f2', self.IP_LINK_SHOW_WITH_MACVTAP)) def test_link_show_command_failed(self): with mock.patch.object(pci_lib.PciDeviceIPWrapper, "_as_root") as mock_as_root: mock_as_root.side_effect = Exception() self.assertRaises(exc.IpCommandError, self.pci_wrapper.link_show) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/__init__.py0000664000175000017500000000000013553660046030317 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/common/0000775000175000017500000000000013553660157027513 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/common/test_config.py0000664000175000017500000001316313553660046032372 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.utils import helpers from oslo_config import cfg from neutron.conf.plugins.ml2.drivers.mech_sriov import agent_common \ as agent_common_config from neutron.plugins.ml2.drivers.mech_sriov.agent.common import config from neutron.plugins.ml2.drivers.mech_sriov.agent \ import sriov_nic_agent as agent from neutron.tests import base class TestSriovAgentConfig(base.BaseTestCase): EXCLUDE_DEVICES_LIST = ['p7p1:0000:07:00.1;0000:07:00.2', 'p3p1:0000:04:00.3'] EXCLUDE_DEVICES_LIST_INVALID = ['p7p2:0000:07:00.1;0000:07:00.2'] EXCLUDE_DEVICES_WITH_SPACES_LIST = ['p7p1: 0000:07:00.1 ; 0000:07:00.2', 'p3p1:0000:04:00.3 '] EXCLUDE_DEVICES_WITH_SPACES_ERROR = ['p7p1', 'p3p1:0000:04:00.3 '] EXCLUDE_DEVICES = {'p7p1': set(['0000:07:00.1', '0000:07:00.2']), 'p3p1': set(['0000:04:00.3'])} DEVICE_MAPPING_LIST = ['physnet7:p7p1', 'physnet3:p3p1'] DEVICE_MAPPING_WITH_ERROR_LIST = ['physnet7', 'physnet3:p3p1'] DEVICE_MAPPING_WITH_SPACES_LIST = ['physnet7 : p7p1', 'physnet3 : p3p1 '] DEVICE_MAPPING = {'physnet7': ['p7p1'], 'physnet3': ['p3p1']} def test_defaults(self): self.assertEqual(agent_common_config.DEFAULT_DEVICE_MAPPINGS, cfg.CONF.SRIOV_NIC.physical_device_mappings) self.assertEqual(agent_common_config.DEFAULT_EXCLUDE_DEVICES, cfg.CONF.SRIOV_NIC.exclude_devices) self.assertEqual(2, cfg.CONF.AGENT.polling_interval) def test_device_mappings(self): cfg.CONF.set_override('physical_device_mappings', self.DEVICE_MAPPING_LIST, 'SRIOV_NIC') device_mappings = helpers.parse_mappings( cfg.CONF.SRIOV_NIC.physical_device_mappings, unique_keys=False) self.assertEqual(self.DEVICE_MAPPING, device_mappings) def test_device_mappings_with_error(self): cfg.CONF.set_override('physical_device_mappings', self.DEVICE_MAPPING_WITH_ERROR_LIST, 'SRIOV_NIC') self.assertRaises(ValueError, helpers.parse_mappings, cfg.CONF.SRIOV_NIC.physical_device_mappings, unique_keys=False) def test_device_mappings_with_spaces(self): cfg.CONF.set_override('physical_device_mappings', self.DEVICE_MAPPING_WITH_SPACES_LIST, 'SRIOV_NIC') device_mappings = helpers.parse_mappings( cfg.CONF.SRIOV_NIC.physical_device_mappings, unique_keys=False) self.assertEqual(self.DEVICE_MAPPING, device_mappings) def test_exclude_devices(self): cfg.CONF.set_override('exclude_devices', self.EXCLUDE_DEVICES_LIST, 'SRIOV_NIC') exclude_devices = config.parse_exclude_devices( cfg.CONF.SRIOV_NIC.exclude_devices) self.assertEqual(self.EXCLUDE_DEVICES, exclude_devices) def test_exclude_devices_with_spaces(self): cfg.CONF.set_override('exclude_devices', self.EXCLUDE_DEVICES_WITH_SPACES_LIST, 'SRIOV_NIC') exclude_devices = config.parse_exclude_devices( cfg.CONF.SRIOV_NIC.exclude_devices) self.assertEqual(self.EXCLUDE_DEVICES, exclude_devices) def test_exclude_devices_with_error(self): cfg.CONF.set_override('exclude_devices', self.EXCLUDE_DEVICES_WITH_SPACES_ERROR, 'SRIOV_NIC') self.assertRaises(ValueError, config.parse_exclude_devices, cfg.CONF.SRIOV_NIC.exclude_devices) def test_validate_config_ok(self): cfg.CONF.set_override('physical_device_mappings', self.DEVICE_MAPPING_LIST, 'SRIOV_NIC') cfg.CONF.set_override('exclude_devices', self.EXCLUDE_DEVICES_LIST, 'SRIOV_NIC') config_parser = agent.SriovNicAgentConfigParser() config_parser.parse() device_mappings = config_parser.device_mappings exclude_devices = config_parser.exclude_devices self.assertEqual(self.EXCLUDE_DEVICES, exclude_devices) self.assertEqual(self.DEVICE_MAPPING, device_mappings) def test_validate_config_fail(self): cfg.CONF.set_override('physical_device_mappings', self.DEVICE_MAPPING_LIST, 'SRIOV_NIC') cfg.CONF.set_override('exclude_devices', self.EXCLUDE_DEVICES_LIST_INVALID, 'SRIOV_NIC') config_parser = agent.SriovNicAgentConfigParser() self.assertRaises(ValueError, config_parser.parse) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/common/__init__.py0000664000175000017500000000000013553660046031607 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py0000664000175000017500000006533413553660047033016 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib.api.definitions import portbindings from oslo_config import cfg from oslo_utils import uuidutils from neutron.agent.l2 import l2_agent_extensions_manager as l2_ext_manager from neutron.agent import rpc as agent_rpc from neutron.plugins.ml2.drivers.mech_sriov.agent.common import config # noqa from neutron.plugins.ml2.drivers.mech_sriov.agent.common import exceptions from neutron.plugins.ml2.drivers.mech_sriov.agent import sriov_nic_agent from neutron.tests import base DEVICE_MAC = '11:22:33:44:55:66' PCI_SLOT = "0000:06:00.1" class TestSriovAgent(base.BaseTestCase): def setUp(self): super(TestSriovAgent, self).setUp() # disable setting up periodic state reporting cfg.CONF.set_override('report_interval', 0, 'AGENT') cfg.CONF.set_default('firewall_driver', 'neutron.agent.firewall.NoopFirewallDriver', group='SECURITYGROUP') cfg.CONF.set_default('enable_security_group', False, group='SECURITYGROUP') class MockFixedIntervalLoopingCall(object): def __init__(self, f): self.f = f def start(self, interval=0): self.f() mock.patch('oslo_service.loopingcall.' 'FixedIntervalLoopingCall', new=MockFixedIntervalLoopingCall) self.agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.eswitch_manager" ".ESwitchManager.get_assigned_devices_info", return_value=set()) @mock.patch.object(agent_rpc.PluginReportStateAPI, 'report_state') def test_cached_device_count_report_state(self, report_state, get_dev): self.agent._report_state() agent_conf = self.agent.agent_state['configurations'] # ensure devices aren't calculated until first scan_devices call self.assertNotIn('devices', agent_conf) self.agent.scan_devices(set(), set()) self.assertEqual(0, agent_conf['devices']) # ensure report_state doesn't call get_dev get_dev.reset_mock() get_dev.return_value = set(['dev1', 'dev2']) self.agent._report_state() self.assertEqual(0, agent_conf['devices']) # after a device scan, conf should bump to 2 self.agent.scan_devices(set(), set()) self.assertEqual(2, agent_conf['devices']) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.get_assigned_macs", return_value=[(DEVICE_MAC, PCI_SLOT)]) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True) def test_treat_devices_removed_with_existed_device(self, *args): agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0) devices = [(DEVICE_MAC, PCI_SLOT)] with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd: fn_udd.return_value = {'device': DEVICE_MAC, 'exists': True} resync = agent.treat_devices_removed(devices) self.assertFalse(resync) self.assertTrue(fn_udd.called) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.get_assigned_macs", return_value=[(DEVICE_MAC, PCI_SLOT)]) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True) def test_treat_devices_removed_with_not_existed_device(self, *args): agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0) devices = [(DEVICE_MAC, PCI_SLOT)] with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd: fn_udd.return_value = {'device': DEVICE_MAC, 'exists': False} with mock.patch.object(sriov_nic_agent.LOG, 'debug') as log: resync = agent.treat_devices_removed(devices) self.assertEqual(1, log.call_count) self.assertFalse(resync) self.assertTrue(fn_udd.called) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.get_assigned_macs", return_value=[(DEVICE_MAC, PCI_SLOT)]) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True) def test_treat_devices_removed_failed(self, *args): agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0) devices = [(DEVICE_MAC, PCI_SLOT)] with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd: fn_udd.side_effect = Exception() with mock.patch.object(sriov_nic_agent.LOG, 'debug') as log: resync = agent.treat_devices_removed(devices) self.assertEqual(1, log.call_count) self.assertTrue(resync) self.assertTrue(fn_udd.called) def mock_scan_devices(self, expected, mock_current, registered_devices, updated_devices): self.agent.eswitch_mgr = mock.Mock() self.agent.eswitch_mgr.get_assigned_devices_info.return_value = ( mock_current) results = self.agent.scan_devices(registered_devices, updated_devices) self.assertEqual(expected, results) def test_scan_devices_returns_empty_sets(self): registered = set() updated = set() mock_current = set() expected = {'current': set(), 'updated': set(), 'added': set(), 'removed': set()} self.mock_scan_devices(expected, mock_current, registered, updated) def test_scan_devices_no_changes(self): registered = set(['1', '2']) updated = set() mock_current = set(['1', '2']) expected = {'current': set(['1', '2']), 'updated': set(), 'added': set(), 'removed': set()} self.mock_scan_devices(expected, mock_current, registered, updated) def test_scan_devices_new_and_removed(self): registered = set(['1', '2']) updated = set() mock_current = set(['2', '3']) expected = {'current': set(['2', '3']), 'updated': set(), 'added': set(['3']), 'removed': set(['1'])} self.mock_scan_devices(expected, mock_current, registered, updated) def test_scan_devices_updated_and_removed(self): registered = set(['1', '2']) # '1' is in removed and updated tuple updated = set(['1']) mock_current = set(['2', '3']) expected = {'current': set(['2', '3']), 'updated': set(), 'added': set(['3']), 'removed': set(['1'])} self.mock_scan_devices(expected, mock_current, registered, updated) def test_scan_devices_new_updates(self): registered = set(['1']) updated = set(['2']) mock_current = set(['1', '2']) expected = {'current': set(['1', '2']), 'updated': set(['2']), 'added': set(['2']), 'removed': set()} self.mock_scan_devices(expected, mock_current, registered, updated) def test_scan_devices_updated_missing(self): registered = set(['1']) updated = set(['2']) mock_current = set(['1']) expected = {'current': set(['1']), 'updated': set(), 'added': set(), 'removed': set()} self.mock_scan_devices(expected, mock_current, registered, updated) def test_process_network_devices(self): agent = self.agent device_info = {'current': set(), 'added': set(['mac3', 'mac4']), 'updated': set(['mac2', 'mac3']), 'removed': set(['mac1'])} agent.sg_agent.prepare_devices_filter = mock.Mock() agent.sg_agent.refresh_firewall = mock.Mock() agent.treat_devices_added_updated = mock.Mock(return_value=False) agent.treat_devices_removed = mock.Mock(return_value=False) agent.process_network_devices(device_info) agent.sg_agent.prepare_devices_filter.assert_called_with( set(['mac3', 'mac4'])) self.assertTrue(agent.sg_agent.refresh_firewall.called) agent.treat_devices_added_updated.assert_called_with(set(['mac2', 'mac3', 'mac4'])) agent.treat_devices_removed.assert_called_with(set(['mac1'])) def test_treat_devices_added_updated_and_removed(self): agent = self.agent MAC1 = 'aa:bb:cc:dd:ee:ff' SLOT1 = '1:2:3.0' MAC2 = 'aa:bb:cc:dd:ee:fe' SLOT2 = '1:3:3.0' mac_pci_slot_device1 = (MAC1, SLOT1) mac_pci_slot_device2 = (MAC2, SLOT2) mock_device1_details = {'device': MAC1, 'port_id': 'port123', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'profile': {'pci_slot': SLOT1}, 'physical_network': 'physnet1', 'port_security_enabled': False} mock_device2_details = {'device': MAC2, 'port_id': 'port124', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'profile': {'pci_slot': SLOT2}, 'physical_network': 'physnet1', 'port_security_enabled': False} agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = ( [mock_device1_details]) agent.treat_devices_added_updated(set([MAC1])) self.assertEqual({'net123': [{'port_id': 'port123', 'device': mac_pci_slot_device1}]}, agent.network_ports) agent.plugin_rpc.get_devices_details_list.return_value = ( [mock_device2_details]) # add the second device and check the network_ports dict agent.treat_devices_added_updated(set([MAC2])) self.assertEqual( {'net123': [{'port_id': 'port123', 'device': mac_pci_slot_device1}, {'port_id': 'port124', 'device': mac_pci_slot_device2}]}, agent.network_ports) with mock.patch.object(agent.plugin_rpc, "update_device_down"): agent.treat_devices_removed([mac_pci_slot_device2]) # remove the second device and check the network_ports dict self.assertEqual({'net123': [{'port_id': 'port123', 'device': mac_pci_slot_device1}]}, agent.network_ports) def test_treat_devices_added_updated_admin_state_up_true(self): agent = self.agent mock_details = {'device': 'aa:bb:cc:dd:ee:ff', 'port_id': 'port123', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'profile': {'pci_slot': '1:2:3.0'}, 'physical_network': 'physnet1', 'port_security_enabled': False} agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = True agent.set_device_state = mock.Mock() agent.set_device_spoofcheck = mock.Mock() resync_needed = agent.treat_devices_added_updated( set(['aa:bb:cc:dd:ee:ff'])) self.assertFalse(resync_needed) agent.eswitch_mgr.device_exists.assert_called_with('aa:bb:cc:dd:ee:ff', '1:2:3.0') agent.eswitch_mgr.set_device_state.assert_called_with( 'aa:bb:cc:dd:ee:ff', '1:2:3.0', True) agent.eswitch_mgr.set_device_spoofcheck.assert_called_with( 'aa:bb:cc:dd:ee:ff', '1:2:3.0', False) agent.plugin_rpc.update_device_list.assert_called_once_with( mock.ANY, set(['aa:bb:cc:dd:ee:ff']), set(), mock.ANY, mock.ANY) def test_treat_devices_added_updated_multiple_admin_state_up_true(self): agent = self.agent mock_details = [{'device': 'aa:bb:cc:dd:ee:ff', 'port_id': 'port123', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'profile': {'pci_slot': '1:2:3.0'}, 'physical_network': 'physnet1', 'port_security_enabled': False}, {'device': '11:22:33:44:55:66', 'port_id': 'port321', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'profile': {'pci_slot': '1:2:3.0'}, 'physical_network': 'physnet1', 'port_security_enabled': False}] agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = mock_details agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = True agent.set_device_state = mock.Mock() agent.set_device_spoofcheck = mock.Mock() resync_needed = agent.treat_devices_added_updated( set(['aa:bb:cc:dd:ee:ff', '11:22:33:44:55:66'])) self.assertFalse(resync_needed) calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0'), mock.call('11:22:33:44:55:66', '1:2:3.0')] agent.eswitch_mgr.device_exists.assert_has_calls(calls, any_order=True) calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0', True), mock.call('11:22:33:44:55:66', '1:2:3.0', True)] agent.eswitch_mgr.set_device_state.assert_has_calls(calls, any_order=True) calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0', False), mock.call('11:22:33:44:55:66', '1:2:3.0', False)] agent.eswitch_mgr.set_device_spoofcheck.assert_has_calls(calls, any_order=True) agent.plugin_rpc.update_device_list.assert_called_once_with( mock.ANY, set(['aa:bb:cc:dd:ee:ff', '11:22:33:44:55:66']), set(), mock.ANY, mock.ANY) def test_treat_devices_added_updated_multiple_admin_states(self): agent = self.agent mock_details = [{'device': 'aa:bb:cc:dd:ee:ff', 'port_id': 'port123', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'profile': {'pci_slot': '1:2:3.0'}, 'physical_network': 'physnet1', 'port_security_enabled': False}, {'device': '11:22:33:44:55:66', 'port_id': 'port321', 'network_id': 'net123', 'admin_state_up': False, 'network_type': 'vlan', 'segmentation_id': 100, 'profile': {'pci_slot': '1:2:3.0'}, 'physical_network': 'physnet1', 'port_security_enabled': False}] agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = mock_details agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = True agent.set_device_state = mock.Mock() agent.set_device_spoofcheck = mock.Mock() resync_needed = agent.treat_devices_added_updated( set(['aa:bb:cc:dd:ee:ff', '11:22:33:44:55:66'])) self.assertFalse(resync_needed) calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0'), mock.call('11:22:33:44:55:66', '1:2:3.0')] agent.eswitch_mgr.device_exists.assert_has_calls(calls, any_order=True) calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0', True), mock.call('11:22:33:44:55:66', '1:2:3.0', False)] agent.eswitch_mgr.set_device_state.assert_has_calls(calls, any_order=True) calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0', False), mock.call('11:22:33:44:55:66', '1:2:3.0', False)] agent.eswitch_mgr.set_device_spoofcheck.assert_has_calls(calls, any_order=True) agent.plugin_rpc.update_device_list.assert_called_once_with( mock.ANY, set(['aa:bb:cc:dd:ee:ff']), set(['11:22:33:44:55:66']), mock.ANY, mock.ANY) def test_treat_device_ip_link_state_not_supported(self): agent = self.agent agent.plugin_rpc = mock.Mock() agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = True agent.eswitch_mgr.set_device_state.side_effect = ( exceptions.IpCommandOperationNotSupportedError( dev_name='aa:bb:cc:dd:ee:ff')) self.assertTrue(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0', admin_state_up=True)) def test_treat_device_set_device_state_exception(self): agent = self.agent agent.plugin_rpc = mock.Mock() agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = True agent.eswitch_mgr.set_device_state.side_effect = ( exceptions.SriovNicError()) self.assertFalse(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0', admin_state_up=True)) def test_treat_device_no_device_found(self): agent = self.agent agent.plugin_rpc = mock.Mock() agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = False self.assertFalse(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0', admin_state_up=True)) def test_treat_devices_added_updated_admin_state_up_false(self): agent = self.agent mock_details = {'device': 'aa:bb:cc:dd:ee:ff', 'port_id': 'port123', 'network_id': 'net123', 'admin_state_up': False, 'network_type': 'vlan', 'segmentation_id': 100, 'profile': {'pci_slot': '1:2:3.0'}, 'physical_network': 'physnet1'} agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] agent.remove_port_binding = mock.Mock() agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = True resync_needed = agent.treat_devices_added_updated( set(['aa:bb:cc:dd:ee:ff'])) self.assertFalse(resync_needed) agent.plugin_rpc.update_device_list.assert_called_once_with( mock.ANY, set(), set(['aa:bb:cc:dd:ee:ff']), mock.ANY, mock.ANY) def test_treat_devices_added_updated_no_device_found(self): agent = self.agent mock_details = {'device': 'aa:bb:cc:dd:ee:ff', 'port_id': 'port123', 'network_id': 'net123', 'admin_state_up': True, 'network_type': 'vlan', 'segmentation_id': 100, 'profile': {'pci_slot': '1:2:3.0'}, 'physical_network': 'physnet1'} agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] agent.remove_port_binding = mock.Mock() agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = False resync_needed = agent.treat_devices_added_updated( set(['aa:bb:cc:dd:ee:ff'])) self.assertTrue(resync_needed) self.assertFalse(agent.plugin_rpc.update_device_up.called) def test_update_and_clean_network_ports(self): network_id1 = 'network_id1' network_id2 = 'network_id2' port_id1 = 'port_id1' port_id2 = 'port_id2' mac_slot_1 = ('mac1', 'slot1') mac_slot_2 = ('mac2', 'slot2') self.agent.network_ports[network_id1] = [{'port_id': port_id1, 'device': mac_slot_1}, {'port_id': port_id2, 'device': mac_slot_2}] self.agent._update_network_ports(network_id2, port_id1, mac_slot_1) self.assertEqual({network_id1: [{'port_id': port_id2, 'device': mac_slot_2}], network_id2: [ {'port_id': port_id1, 'device': mac_slot_1}]}, self.agent.network_ports) cleaned_port_id = self.agent._clean_network_ports(mac_slot_1) self.assertEqual(cleaned_port_id, port_id1) self.assertEqual({network_id1: [{'port_id': port_id2, 'device': mac_slot_2}]}, self.agent.network_ports) cleaned_port_id = self.agent._clean_network_ports(mac_slot_2) self.assertEqual({}, self.agent.network_ports) class FakeAgent(object): def __init__(self): self.updated_devices = set() class TestSriovNicSwitchRpcCallbacks(base.BaseTestCase): def setUp(self): super(TestSriovNicSwitchRpcCallbacks, self).setUp() self.context = object() self.agent = FakeAgent() sg_agent = object() self.sriov_rpc_callback = sriov_nic_agent.SriovNicSwitchRpcCallbacks( self.context, self.agent, sg_agent) def _create_fake_port(self): return {'id': uuidutils.generate_uuid(), portbindings.PROFILE: {'pci_slot': PCI_SLOT}, 'mac_address': DEVICE_MAC} def test_port_update_with_pci_slot(self): port = self._create_fake_port() kwargs = {'context': self.context, 'port': port} self.sriov_rpc_callback.port_update(**kwargs) self.assertEqual(set([(DEVICE_MAC, PCI_SLOT)]), self.agent.updated_devices) def test_port_update_with_vnic_physical_direct(self): port = self._create_fake_port() port[portbindings.VNIC_TYPE] = portbindings.VNIC_DIRECT_PHYSICAL kwargs = {'context': self.context, 'port': port} self.sriov_rpc_callback.port_update(**kwargs) self.assertEqual(set(), self.agent.updated_devices) def test_port_update_without_pci_slot(self): port = self._create_fake_port() port[portbindings.PROFILE] = None kwargs = {'context': self.context, 'port': port} self.sriov_rpc_callback.port_update(**kwargs) self.assertEqual(set(), self.agent.updated_devices) def test_network_update(self): TEST_NETWORK_ID1 = "n1" TEST_NETWORK_ID2 = "n2" TEST_PORT_ID1 = 'p1' TEST_PORT_ID2 = 'p2' network1 = {'id': TEST_NETWORK_ID1} port1 = {'id': TEST_PORT_ID1, 'network_id': TEST_NETWORK_ID1} port2 = {'id': TEST_PORT_ID2, 'network_id': TEST_NETWORK_ID2} self.agent.network_ports = { TEST_NETWORK_ID1: [{'port_id': port1['id'], 'device': ('mac1', 'slot1')}], TEST_NETWORK_ID2: [{'port_id': port2['id'], 'device': ('mac2', 'slot2')}]} kwargs = {'context': self.context, 'network': network1} self.sriov_rpc_callback.network_update(**kwargs) self.assertEqual(set([('mac1', 'slot1')]), self.agent.updated_devices) class TestSRIOVAgentExtensionConfig(base.BaseTestCase): def setUp(self): super(TestSRIOVAgentExtensionConfig, self).setUp() l2_ext_manager.register_opts(cfg.CONF) # disable setting up periodic state reporting cfg.CONF.set_override('report_interval', 0, group='AGENT') cfg.CONF.set_override('extensions', ['qos'], group='agent') @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.eswitch_manager" ".ESwitchManager.get_assigned_devices_info", return_value=[]) def test_report_loaded_extension(self, *args): with mock.patch.object(agent_rpc.PluginReportStateAPI, 'report_state') as mock_report_state: agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0) agent._report_state() mock_report_state.assert_called_with( agent.context, agent.agent_state) self.assertEqual( ['qos'], agent.agent_state['configurations']['extensions']) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/0000775000175000017500000000000013553660157031775 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/test_qos_driver.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/test_qos_dr0000664000175000017500000001427513553660046034254 0ustar zuulzuul00000000000000# Copyright 2015 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib import constants from neutron_lib import context from neutron_lib.services.qos import constants as qos_consts from oslo_utils import uuidutils from neutron.objects.qos import policy from neutron.objects.qos import rule from neutron.plugins.ml2.drivers.mech_sriov.agent.common import exceptions from neutron.plugins.ml2.drivers.mech_sriov.agent.extension_drivers import ( qos_driver) from neutron.tests import base class QosSRIOVAgentDriverTestCase(base.BaseTestCase): ASSIGNED_MAC = '00:00:00:00:00:66' PCI_SLOT = '0000:06:00.1' def setUp(self): super(QosSRIOVAgentDriverTestCase, self).setUp() self.context = context.get_admin_context() self.qos_driver = qos_driver.QosSRIOVAgentDriver() self.qos_driver.initialize() self.qos_driver.eswitch_mgr = mock.Mock() self.qos_driver.eswitch_mgr.set_device_max_rate = mock.Mock() self.qos_driver.eswitch_mgr.set_device_min_tx_rate = mock.Mock() self.qos_driver.eswitch_mgr.clear_max_rate = mock.Mock() self.qos_driver.eswitch_mgr.clear_min_tx_rate = mock.Mock() self.max_rate_mock = self.qos_driver.eswitch_mgr.set_device_max_rate self.min_tx_rate_mock = \ self.qos_driver.eswitch_mgr.set_device_min_tx_rate self.clear_max_rate_mock = self.qos_driver.eswitch_mgr.clear_max_rate self.clear_min_tx_rate_mock = \ self.qos_driver.eswitch_mgr.clear_min_tx_rate self.rule = self._create_bw_limit_rule_obj() self.rule_min_tx_rate = self._create_minimum_bandwidth_rule_obj() self.qos_policy = self._create_qos_policy_obj([self.rule]) self.qos_policy_min_tx_rate = self._create_qos_policy_obj( [self.rule_min_tx_rate]) self.port = self._create_fake_port(self.qos_policy.id) self.port_min = self._create_fake_port(self.qos_policy_min_tx_rate.id) def _create_bw_limit_rule_obj(self): rule_obj = rule.QosBandwidthLimitRule() rule_obj.id = uuidutils.generate_uuid() rule_obj.max_kbps = 2 rule_obj.max_burst_kbps = 200 rule_obj.obj_reset_changes() return rule_obj def _create_minimum_bandwidth_rule_obj(self): rule_obj = rule.QosMinimumBandwidthRule() rule_obj.id = uuidutils.generate_uuid() rule_obj.min_kbps = 200 rule_obj.direction = constants.EGRESS_DIRECTION rule_obj.obj_reset_changes() return rule_obj def _create_qos_policy_obj(self, rules): policy_dict = {'id': uuidutils.generate_uuid(), 'project_id': uuidutils.generate_uuid(), 'name': 'test', 'description': 'test', 'shared': False, 'rules': rules} policy_obj = policy.QosPolicy(self.context, **policy_dict) policy_obj.obj_reset_changes() for policy_rule in policy_obj.rules: policy_rule.qos_policy_id = policy_obj.id policy_rule.obj_reset_changes() return policy_obj def _create_fake_port(self, qos_policy_id): return {'port_id': uuidutils.generate_uuid(), 'profile': {'pci_slot': self.PCI_SLOT}, 'device': self.ASSIGNED_MAC, qos_consts.QOS_POLICY_ID: qos_policy_id, 'device_owner': uuidutils.generate_uuid()} def test_create_rule(self): self.qos_driver.create(self.port, self.qos_policy) self.max_rate_mock.assert_called_once_with( self.ASSIGNED_MAC, self.PCI_SLOT, self.rule.max_kbps) def test_update_rule(self): self.qos_driver.update(self.port, self.qos_policy) self.max_rate_mock.assert_called_once_with( self.ASSIGNED_MAC, self.PCI_SLOT, self.rule.max_kbps) def test_delete_rules_on_assigned_vf(self): self.qos_driver.delete(self.port, self.qos_policy) self.max_rate_mock.assert_called_once_with( self.ASSIGNED_MAC, self.PCI_SLOT, 0) def test_delete_rules_on_released_vf(self): del self.port['device_owner'] self.qos_driver.delete(self.port, self.qos_policy) self.clear_max_rate_mock.assert_called_once_with(self.PCI_SLOT) def test__set_vf_max_rate_captures_sriov_failure(self): self.max_rate_mock.side_effect = exceptions.SriovNicError() self.qos_driver._set_vf_max_rate(self.ASSIGNED_MAC, self.PCI_SLOT) def test__set_vf_max_rate_unknown_device(self): with mock.patch.object(self.qos_driver.eswitch_mgr, 'device_exists', return_value=False): self.qos_driver._set_vf_max_rate(self.ASSIGNED_MAC, self.PCI_SLOT) self.assertFalse(self.max_rate_mock.called) def test_create_minimum_bandwidth(self): self.qos_driver.create(self.port_min, self.qos_policy_min_tx_rate) self.min_tx_rate_mock.assert_called_once_with( self.ASSIGNED_MAC, self.PCI_SLOT, self.rule_min_tx_rate.min_kbps) def test_update_minimum_bandwidth(self): self.qos_driver.update(self.port_min, self.qos_policy_min_tx_rate) self.min_tx_rate_mock.assert_called_once_with( self.ASSIGNED_MAC, self.PCI_SLOT, self.rule_min_tx_rate.min_kbps) def test_delete_minimum_bandwidth_on_assigned_vf(self): self.qos_driver.delete(self.port_min, self.qos_policy_min_tx_rate) self.min_tx_rate_mock.assert_called_once_with( self.ASSIGNED_MAC, self.PCI_SLOT, 0) def test_delete_minimum_bandwidth_on_released_vf(self): del self.port_min['device_owner'] self.qos_driver.delete(self.port_min, self.qos_policy_min_tx_rate) self.clear_min_tx_rate_mock.assert_called_once_with(self.PCI_SLOT) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/__init__.py0000664000175000017500000000000013553660046034071 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py0000664000175000017500000011101713553660047032773 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import os import mock from neutron.agent.linux import ip_link_support from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ import exceptions as exc from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm from neutron.tests import base class TestCreateESwitchManager(base.BaseTestCase): SCANNED_DEVICES = [('0000:06:00.1', 0), ('0000:06:00.2', 1), ('0000:06:00.3', 2)] @staticmethod def cleanup(): if hasattr(esm.ESwitchManager, '_instance'): del esm.ESwitchManager._instance def test_create_eswitch_mgr_fail(self): device_mappings = {'physnet1': ['p6p1']} with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.scan_vf_devices", side_effect=exc.InvalidDeviceError( dev_name="p6p1", reason="device" " not found")),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.pf_device_exists", return_value=True), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True): eswitch_mgr = esm.ESwitchManager() self.addCleanup(self.cleanup) self.assertRaises(exc.InvalidDeviceError, eswitch_mgr.discover_devices, device_mappings, None) def test_create_eswitch_mgr_ok(self): device_mappings = {'physnet1': ['p6p1']} with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.scan_vf_devices", return_value=self.SCANNED_DEVICES),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.pf_device_exists", return_value=True), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True): eswitch_mgr = esm.ESwitchManager() self.addCleanup(self.cleanup) eswitch_mgr.discover_devices(device_mappings, None) class TestESwitchManagerApi(base.BaseTestCase): SCANNED_DEVICES = [('0000:06:00.1', 0), ('0000:06:00.2', 1), ('0000:06:00.3', 2)] ASSIGNED_MAC = '00:00:00:00:00:66' PCI_SLOT = '0000:06:00.1' WRONG_MAC = '00:00:00:00:00:67' WRONG_PCI = "0000:06:00.6" MAX_RATE = ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE MIN_RATE = ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE def setUp(self): super(TestESwitchManagerApi, self).setUp() device_mappings = {'physnet1': ['p6p1']} self.eswitch_mgr = esm.ESwitchManager() self.addCleanup(self.cleanup) self._set_eswitch_manager(self.eswitch_mgr, device_mappings) @staticmethod def cleanup(): if hasattr(esm.ESwitchManager, '_instance'): del esm.ESwitchManager._instance def _set_eswitch_manager(self, eswitch_mgr, device_mappings): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.scan_vf_devices", return_value=self.SCANNED_DEVICES), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.pf_device_exists", return_value=True), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True): eswitch_mgr.discover_devices(device_mappings, None) def test_discover_devices_with_device(self): device_mappings = {'physnet1': ['p6p1', 'p6p2']} with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.pf_device_exists", return_value=True), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.ESwitchManager._create_emb_switch", ) as emb_switch: self.eswitch_mgr.discover_devices(device_mappings, None) self.assertTrue(emb_switch.called) def test_discover_devices_without_device(self): device_mappings = {'physnet1': ['p6p1', 'p6p2']} with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.pf_device_exists", return_value=False), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.ESwitchManager._create_emb_switch", ) as emb_switch: self.eswitch_mgr.discover_devices(device_mappings, None) self.assertFalse(emb_switch.called) def test_get_assigned_devices_info(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_assigned_devices_info", return_value=[(self.ASSIGNED_MAC, self.PCI_SLOT)]),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "pci_lib.PciDeviceIPWrapper.link_show", return_value=''): result = self.eswitch_mgr.get_assigned_devices_info() self.assertIn(self.ASSIGNED_MAC, list(result)[0]) self.assertIn(self.PCI_SLOT, list(result)[0]) def test_get_assigned_devices_info_multiple_nics_for_physnet(self): device_mappings = {'physnet1': ['p6p1', 'p6p2']} devices_info = { 'p6p1': [(self.ASSIGNED_MAC, self.PCI_SLOT)], 'p6p2': [(self.WRONG_MAC, self.WRONG_PCI)], } def get_assigned_devices_info(self): return devices_info[self.dev_name] self._set_eswitch_manager(self.eswitch_mgr, device_mappings) with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_assigned_devices_info", side_effect=get_assigned_devices_info, autospec=True): result = self.eswitch_mgr.get_assigned_devices_info() self.assertIn(devices_info['p6p1'][0], list(result)) self.assertIn(devices_info['p6p2'][0], list(result)) def test_get_device_status_true(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_pci_device", return_value=self.ASSIGNED_MAC),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_device_state", return_value=True): result = self.eswitch_mgr.get_device_state(self.ASSIGNED_MAC, self.PCI_SLOT) self.assertTrue(result) def test_get_device_status_false(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_pci_device", return_value=self.ASSIGNED_MAC),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_device_state", return_value=False): result = self.eswitch_mgr.get_device_state(self.ASSIGNED_MAC, self.PCI_SLOT) self.assertFalse(result) def test_get_device_status_mismatch(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_pci_device", return_value=self.ASSIGNED_MAC),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_device_state", return_value=True): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.LOG.warning") as log_mock: result = self.eswitch_mgr.get_device_state(self.WRONG_MAC, self.PCI_SLOT) log_mock.assert_called_with('device pci mismatch: ' '%(device_mac)s - %(pci_slot)s', {'pci_slot': self.PCI_SLOT, 'device_mac': self.WRONG_MAC}) self.assertFalse(result) def test_set_device_status(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_pci_device", return_value=self.ASSIGNED_MAC),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.set_device_state"): self.eswitch_mgr.set_device_state(self.ASSIGNED_MAC, self.PCI_SLOT, True) def test_set_device_max_rate(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_pci_device", return_value=self.ASSIGNED_MAC) as get_pci_mock,\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.set_device_rate")\ as set_device_rate_mock: self.eswitch_mgr.set_device_max_rate(self.ASSIGNED_MAC, self.PCI_SLOT, 1000) get_pci_mock.assert_called_once_with(self.PCI_SLOT) set_device_rate_mock.assert_called_once_with( self.PCI_SLOT, self.MAX_RATE, 1000) def test_set_device_min_tx_rate(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_pci_device", return_value=self.ASSIGNED_MAC) as get_pci_mock,\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.set_device_rate")\ as set_device_rate_mock: self.eswitch_mgr.set_device_min_tx_rate(self.ASSIGNED_MAC, self.PCI_SLOT, 1000) get_pci_mock.assert_called_once_with(self.PCI_SLOT) set_device_rate_mock.assert_called_once_with( self.PCI_SLOT, self.MIN_RATE, 1000) def test_set_device_status_mismatch(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_pci_device", return_value=self.ASSIGNED_MAC),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.set_device_state"): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.LOG.warning") as log_mock: self.eswitch_mgr.set_device_state(self.WRONG_MAC, self.PCI_SLOT, True) log_mock.assert_called_with('device pci mismatch: ' '%(device_mac)s - %(pci_slot)s', {'pci_slot': self.PCI_SLOT, 'device_mac': self.WRONG_MAC}) def _mock_device_exists(self, pci_slot, mac_address, expected_result): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_pci_device", return_value=self.ASSIGNED_MAC): result = self.eswitch_mgr.device_exists(mac_address, pci_slot) self.assertEqual(expected_result, result) def test_device_exists_true(self): self._mock_device_exists(self.PCI_SLOT, self.ASSIGNED_MAC, True) def test_device_exists_false(self): self._mock_device_exists(self.WRONG_PCI, self.WRONG_MAC, False) def test_device_exists_mismatch(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.EmbSwitch.get_pci_device", return_value=self.ASSIGNED_MAC): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.LOG.warning") as log_mock: result = self.eswitch_mgr.device_exists(self.WRONG_MAC, self.PCI_SLOT) log_mock.assert_called_with('device pci mismatch: ' '%(device_mac)s - %(pci_slot)s', {'pci_slot': self.PCI_SLOT, 'device_mac': self.WRONG_MAC}) self.assertFalse(result) def test_clear_max_rate(self): with mock.patch('neutron.plugins.ml2.drivers.mech_sriov.agent.' 'eswitch_manager.ESwitchManager._clear_rate') \ as clear_rate_mock: self.eswitch_mgr.clear_max_rate(self.PCI_SLOT) clear_rate_mock.assert_called_once_with(self.PCI_SLOT, self.MAX_RATE) def test_clear_min_tx_rate(self): with mock.patch('neutron.plugins.ml2.drivers.mech_sriov.agent.' 'eswitch_manager.ESwitchManager._clear_rate') \ as clear_rate_mock: self.eswitch_mgr.clear_min_tx_rate(self.PCI_SLOT) clear_rate_mock.assert_called_once_with(self.PCI_SLOT, self.MIN_RATE) def test_process_emb_switch_without_device(self): device_mappings = {'physnet1': ['p6p1', 'p6p2']} phys_net = 'physnet1' dev_name = 'p6p1' self._set_eswitch_manager(self.eswitch_mgr, device_mappings) with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.pf_device_exists", return_value=False), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.ESwitchManager._create_emb_switch", ) as emb_switch: self.eswitch_mgr._process_emb_switch_map(phys_net, dev_name, {}) self.assertFalse(emb_switch.called) def test_process_emb_switch_with_device(self): device_mappings = {'physnet1': ['p6p1', 'p6p2']} phys_net = 'physnet1' dev_name = 'p6p3' self._set_eswitch_manager(self.eswitch_mgr, device_mappings) with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.pf_device_exists", return_value=True), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.ESwitchManager._create_emb_switch", ) as emb_switch: self.eswitch_mgr._process_emb_switch_map(phys_net, dev_name, {}) self.assertTrue(emb_switch.called) def _test_clear_rate(self, rate_type, pci_slot, passed, mac_address): with mock.patch('neutron.plugins.ml2.drivers.mech_sriov.agent.' 'eswitch_manager.EmbSwitch.set_device_rate') \ as set_rate_mock, \ mock.patch('neutron.plugins.ml2.drivers.mech_sriov.agent.' 'pci_lib.PciDeviceIPWrapper.get_assigned_macs', return_value=mac_address), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "pci_lib.PciDeviceIPWrapper.link_show", return_value=''), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.pf_device_exists", return_value=True): self.eswitch_mgr._clear_rate(pci_slot, rate_type) if passed: set_rate_mock.assert_called_once_with(pci_slot, rate_type, 0) else: self.assertFalse(set_rate_mock.called) def test_clear_rate_max_rate_existing_pci_slot(self): self._test_clear_rate(self.MAX_RATE, self.PCI_SLOT, passed=True, mac_address={}) def test_clear_rate_max_rate_exist_and_assigned_pci(self): self._test_clear_rate(self.MAX_RATE, self.PCI_SLOT, passed=False, mac_address={0: self.ASSIGNED_MAC}) def test_clear_rate_max_rate_nonexisting_pci_slot(self): self._test_clear_rate(self.MAX_RATE, self.WRONG_PCI, passed=False, mac_address={}) def test_clear_rate_min_tx_rate_existing_pci_slot(self): self._test_clear_rate(self.MIN_RATE, self.PCI_SLOT, passed=True, mac_address={}) def test_clear_rate_min_tx_rate_exist_and_assigned_pci(self): self._test_clear_rate(self.MIN_RATE, self.PCI_SLOT, passed=False, mac_address={0: self.ASSIGNED_MAC}) def test_clear_rate_min_tx_rate_nonexisting_pci_slot(self): self._test_clear_rate(self.MIN_RATE, self.WRONG_PCI, passed=False, mac_address={}) def test_create_emb_switch(self): DEVICES = [('0000:04:00.1', 0), ('0000:04:00.2', 1)] with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.scan_vf_devices", side_effect=[[], DEVICES]), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.get_numvfs", return_value=2): physnet = 'test_create_emb_switch' self.assertNotIn(physnet, self.eswitch_mgr.emb_switches_map) # first time device will not be added as no VFs returned self.eswitch_mgr._create_emb_switch(physnet, 'dev1', []) self.assertNotIn(physnet, self.eswitch_mgr.emb_switches_map) self.assertEqual({'dev1'}, self.eswitch_mgr.skipped_devices) # second time device should be added with 2 VFs self.eswitch_mgr._create_emb_switch(physnet, 'dev1', []) self.assertIn(physnet, self.eswitch_mgr.emb_switches_map) self.assertEqual(set(), self.eswitch_mgr.skipped_devices) self.assertIn('0000:04:00.1', self.eswitch_mgr.pci_slot_map) self.assertIn('0000:04:00.2', self.eswitch_mgr.pci_slot_map) def test_create_emb_switch_zero_vfs(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.scan_vf_devices", return_value=[]), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.get_numvfs", return_value=0): physnet = 'test_create_emb_switch' self.assertNotIn(physnet, self.eswitch_mgr.emb_switches_map) # first time device will not be added self.eswitch_mgr._create_emb_switch(physnet, 'dev1', []) self.assertNotIn(physnet, self.eswitch_mgr.emb_switches_map) self.assertEqual({'dev1'}, self.eswitch_mgr.skipped_devices) # second time device should be added with 0 VFs self.eswitch_mgr._create_emb_switch(physnet, 'dev1', []) self.assertIn(physnet, self.eswitch_mgr.emb_switches_map) self.assertEqual(set(), self.eswitch_mgr.skipped_devices) class TestEmbSwitch(base.BaseTestCase): DEV_NAME = "eth2" PHYS_NET = "default" ASSIGNED_MAC = '00:00:00:00:00:66' PCI_SLOT = "0000:06:00.1" WRONG_PCI_SLOT = "0000:06:00.4" SCANNED_DEVICES = [('0000:06:00.1', 0), ('0000:06:00.2', 1), ('0000:06:00.3', 2)] VF_TO_MAC_MAPPING = {0: '00:00:00:00:00:11', 1: '00:00:00:00:00:22', 2: '00:00:00:00:00:33'} EXPECTED_MAC_TO_PCI = { '00:00:00:00:00:11': '0000:06:00.1', '00:00:00:00:00:22': '0000:06:00.2', '00:00:00:00:00:33': '0000:06:00.3'} def setUp(self): super(TestEmbSwitch, self).setUp() exclude_devices = set() with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.scan_vf_devices", return_value=self.SCANNED_DEVICES): self.emb_switch = esm.EmbSwitch(self.DEV_NAME, exclude_devices) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.scan_vf_devices", return_value=[(PCI_SLOT, 0)]) def test_get_assigned_devices_info(self, *args): emb_switch = esm.EmbSwitch(self.DEV_NAME, ()) with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.get_assigned_macs", return_value={0: self.ASSIGNED_MAC}),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "pci_lib.PciDeviceIPWrapper.link_show", return_value=''): result = emb_switch.get_assigned_devices_info() self.assertIn(self.ASSIGNED_MAC, list(result)[0]) self.assertIn(self.PCI_SLOT, list(result)[0]) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.scan_vf_devices", return_value=SCANNED_DEVICES) def test_get_assigned_devices_info_multiple_slots(self, *args): emb_switch = esm.EmbSwitch(self.DEV_NAME, ()) with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.get_assigned_macs", return_value=self.VF_TO_MAC_MAPPING),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "pci_lib.PciDeviceIPWrapper.link_show", return_value=''): devices_info = emb_switch.get_assigned_devices_info() for device_info in devices_info: mac = device_info[0] pci_slot = device_info[1] self.assertEqual( self.EXPECTED_MAC_TO_PCI[mac], pci_slot) def test_get_assigned_devices_empty(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=False), \ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "pci_lib.PciDeviceIPWrapper.link_show", return_value=''): result = self.emb_switch.get_assigned_devices_info() self.assertFalse(result) def test_get_device_state_ok(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.get_vf_state", return_value=False): result = self.emb_switch.get_device_state(self.PCI_SLOT) self.assertFalse(result) def test_get_device_state_fail(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.get_vf_state", return_value=False): self.assertRaises(exc.InvalidPciSlotError, self.emb_switch.get_device_state, self.WRONG_PCI_SLOT) def test_set_device_state_ok(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.set_vf_state"): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "pci_lib.LOG.warning") as log_mock: self.emb_switch.set_device_state(self.PCI_SLOT, True) self.assertEqual(0, log_mock.call_count) def test_set_device_state_fail(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.set_vf_state"): self.assertRaises(exc.InvalidPciSlotError, self.emb_switch.set_device_state, self.WRONG_PCI_SLOT, True) def test_set_device_spoofcheck_ok(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.set_vf_spoofcheck") as \ set_vf_spoofcheck_mock: self.emb_switch.set_device_spoofcheck(self.PCI_SLOT, True) self.assertTrue(set_vf_spoofcheck_mock.called) def test_set_device_spoofcheck_fail(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.set_vf_spoofcheck"): self.assertRaises(exc.InvalidPciSlotError, self.emb_switch.set_device_spoofcheck, self.WRONG_PCI_SLOT, True) def test_set_device_rate_ok(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock: self.emb_switch.set_device_rate( self.PCI_SLOT, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2000) pci_lib_mock.assert_called_with( 0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2) def test_set_device_max_rate_ok2(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock: self.emb_switch.set_device_rate( self.PCI_SLOT, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 99) pci_lib_mock.assert_called_with( 0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 1) def test_set_device_max_rate_rounded_ok(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock: self.emb_switch.set_device_rate( self.PCI_SLOT, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2001) pci_lib_mock.assert_called_with( 0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2) def test_set_device_max_rate_rounded_ok2(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock: self.emb_switch.set_device_rate( self.PCI_SLOT, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2499) pci_lib_mock.assert_called_with( 0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2) def test_set_device_max_rate_rounded_ok3(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock: self.emb_switch.set_device_rate( self.PCI_SLOT, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2500) pci_lib_mock.assert_called_with( 0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 3) def test_set_device_max_rate_disable(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock: self.emb_switch.set_device_rate( self.PCI_SLOT, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 0) pci_lib_mock.assert_called_with( 0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 0) def test_set_device_max_rate_fail(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.set_vf_rate"): self.assertRaises( exc.InvalidPciSlotError, self.emb_switch.set_device_rate, self.WRONG_PCI_SLOT, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 1000) def test_get_pci_device(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.get_assigned_macs", return_value={0: self.ASSIGNED_MAC}),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "pci_lib.PciDeviceIPWrapper.link_show", return_value=''): result = self.emb_switch.get_pci_device(self.PCI_SLOT) self.assertEqual(self.ASSIGNED_MAC, result) def test_get_pci_device_fail(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.get_assigned_macs", return_value=[self.ASSIGNED_MAC]),\ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True): result = self.emb_switch.get_pci_device(self.WRONG_PCI_SLOT) self.assertIsNone(result) def test_get_pci_list(self): result = self.emb_switch.get_pci_slot_list() self.assertEqual([tup[0] for tup in self.SCANNED_DEVICES], sorted(result)) class TestPciOsWrapper(base.BaseTestCase): DEV_NAME = "p7p1" VF_INDEX = 1 DIR_CONTENTS = [ "mlx4_port1", "virtfn0", "virtfn1", "virtfn2" ] DIR_CONTENTS_NO_MATCH = [ "mlx4_port1", "mlx4_port1" ] LINKS = { "virtfn0": "../0000:04:00.1", "virtfn1": "../0000:04:00.2", "virtfn2": "../0000:04:00.3" } PCI_SLOTS = [ ('0000:04:00.1', 0), ('0000:04:00.2', 1), ('0000:04:00.3', 2) ] def test_scan_vf_devices(self): def _get_link(file_path): file_name = os.path.basename(file_path) return self.LINKS[file_name] with mock.patch("os.path.isdir", return_value=True),\ mock.patch("os.listdir", return_value=self.DIR_CONTENTS),\ mock.patch("os.path.islink", return_value=True),\ mock.patch("os.readlink", side_effect=_get_link): result = esm.PciOsWrapper.scan_vf_devices(self.DEV_NAME) self.assertEqual(self.PCI_SLOTS, result) def test_scan_vf_devices_no_dir(self): with mock.patch("os.path.isdir", return_value=False): self.assertRaises(exc.InvalidDeviceError, esm.PciOsWrapper.scan_vf_devices, self.DEV_NAME) def test_scan_vf_devices_no_content(self): with mock.patch("os.path.isdir", return_value=True),\ mock.patch("os.listdir", return_value=[]): self.assertEqual([], esm.PciOsWrapper.scan_vf_devices(self.DEV_NAME)) def test_scan_vf_devices_no_match(self): with mock.patch("os.path.isdir", return_value=True),\ mock.patch("os.listdir", return_value=self.DIR_CONTENTS_NO_MATCH): self.assertEqual([], esm.PciOsWrapper.scan_vf_devices(self.DEV_NAME)) @mock.patch("os.listdir", side_effect=OSError()) def test_is_assigned_vf_true(self, *args): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." "eswitch_manager.PciOsWrapper.pf_device_exists", return_value=True): self.assertTrue(esm.PciOsWrapper.is_assigned_vf( self.DEV_NAME, self.VF_INDEX, '')) @mock.patch("os.path.exists", return_value=True) @mock.patch("os.listdir", return_value=[DEV_NAME, "eth1"]) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.is_macvtap_assigned", return_value=False) def test_is_assigned_vf_false(self, *args): self.assertFalse(esm.PciOsWrapper.is_assigned_vf( self.DEV_NAME, self.VF_INDEX, '')) @mock.patch("os.path.exists", return_value=True) @mock.patch("os.listdir", return_value=["eth0", "eth1"]) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.is_macvtap_assigned", return_value=True) def test_is_assigned_vf_macvtap( self, mock_is_macvtap_assigned, *args): esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME, self.VF_INDEX, '') mock_is_macvtap_assigned.called_with(self.VF_INDEX, "eth0") @mock.patch("os.path.exists", return_value=True) @mock.patch("os.listdir", side_effect=OSError()) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." "PciDeviceIPWrapper.is_macvtap_assigned") def test_is_assigned_vf_macvtap_failure( self, mock_is_macvtap_assigned, *args): esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME, self.VF_INDEX, '') self.assertFalse(mock_is_macvtap_assigned.called) @mock.patch("os.path.exists", return_value=False) @mock.patch("os.listdir", return_value=["eth0", "eth1"]) def test_is_assigned_vf_pf_disappeared(self, list_dir_mock, *args): self.assertFalse(esm.PciOsWrapper.is_assigned_vf( self.DEV_NAME, self.VF_INDEX, '')) self.assertFalse(list_dir_mock.called) def test_pf_device_exists_with_no_dir(self): with mock.patch("os.path.isdir", return_value=False): self.assertFalse(esm.PciOsWrapper.pf_device_exists('p6p1')) def test_pf_device_exists_with_dir(self): with mock.patch("os.path.isdir", return_value=True): self.assertTrue(esm.PciOsWrapper.pf_device_exists('p6p1')) def test_get_numvfs(self): with mock.patch("six.moves.builtins.open", mock.mock_open(read_data="63")) as mock_open: self.assertEqual(63, esm.PciOsWrapper.get_numvfs('dev1')) mock_open.assert_called_once_with( esm.PciOsWrapper.NUMVFS_PATH % 'dev1') def test_get_numvfs_no_file(self): with mock.patch("six.moves.builtins.open", side_effect=IOError()) as mock_open: self.assertEqual(-1, esm.PciOsWrapper.get_numvfs('dev1')) mock_open.assert_called_once_with( esm.PciOsWrapper.NUMVFS_PATH % 'dev1') neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/__init__.py0000664000175000017500000000000013553660046027221 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/mech_driver/0000775000175000017500000000000013553660157027414 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/mech_driver/__init__.py0000664000175000017500000000000013553660046031510 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/mech_driver/test_mech_sriov_nic_switch.pyneutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/mech_driver/test_mech_sriov_nic_swi0000664000175000017500000002071713553660047034254 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib.plugins.ml2 import api import testtools from neutron.plugins.ml2.drivers.mech_sriov.mech_driver \ import exceptions as exc from neutron.plugins.ml2.drivers.mech_sriov.mech_driver import mech_driver from neutron.tests.unit.plugins.ml2 import _test_mech_agent as base class TestFakePortContext(base.FakePortContext): def __init__(self, agent_type, agents, segments, vnic_type=portbindings.VNIC_NORMAL, profile=None): super(TestFakePortContext, self).__init__(agent_type, agents, segments, vnic_type=vnic_type, profile=profile) def set_binding(self, segment_id, vif_type, vif_details, state): self._bound_segment_id = segment_id self._bound_vif_type = vif_type self._bound_vif_details = vif_details self._bound_state = state class SriovNicSwitchMechanismBaseTestCase(base.AgentMechanismBaseTestCase): VIF_TYPE = portbindings.VIF_TYPE_HW_VEB CAP_PORT_FILTER = False AGENT_TYPE = constants.AGENT_TYPE_NIC_SWITCH VLAN_SEGMENTS = base.AgentMechanismVlanTestCase.VLAN_SEGMENTS GOOD_MAPPINGS = {'fake_physical_network': ['fake_device']} GOOD_CONFIGS = {'device_mappings': GOOD_MAPPINGS} BAD_MAPPINGS = {'wrong_physical_network': ['wrong_device']} BAD_CONFIGS = {'device_mappings': BAD_MAPPINGS} AGENTS = [{'alive': True, 'configurations': GOOD_CONFIGS}] AGENTS_DEAD = [{'alive': False, 'configurations': GOOD_CONFIGS}] AGENTS_BAD = [{'alive': False, 'configurations': GOOD_CONFIGS}, {'alive': True, 'configurations': BAD_CONFIGS}] def setUp(self): super(SriovNicSwitchMechanismBaseTestCase, self).setUp() self.driver = mech_driver.SriovNicSwitchMechanismDriver() self.driver.initialize() class SriovSwitchMechGenericTestCase(SriovNicSwitchMechanismBaseTestCase, base.AgentMechanismGenericTestCase): def test_check_segment(self): """Validate the check_segment call.""" segment = {'api.NETWORK_TYPE': ""} segment[api.NETWORK_TYPE] = constants.TYPE_VLAN self.assertTrue(self.driver.check_segment_for_agent(segment)) # Validate a network type not currently supported segment[api.NETWORK_TYPE] = constants.TYPE_GRE self.assertFalse(self.driver.check_segment_for_agent(segment)) def test_check_segment_allows_supported_network_types(self): for network_type in self.driver.get_allowed_network_types(agent=None): segment = {api.NETWORK_TYPE: network_type} self.assertTrue(self.driver.check_segment_for_agent(segment)) class SriovMechVlanTestCase(SriovNicSwitchMechanismBaseTestCase, base.AgentMechanismBaseTestCase): VLAN_SEGMENTS = [{api.ID: 'unknown_segment_id', api.NETWORK_TYPE: 'no_such_type'}, {api.ID: 'vlan_segment_id', api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'fake_physical_network', api.SEGMENTATION_ID: 1234}] def test_type_vlan(self): context = TestFakePortContext(self.AGENT_TYPE, self.AGENTS, self.VLAN_SEGMENTS, portbindings.VNIC_DIRECT) self.driver.bind_port(context) self._check_bound(context, self.VLAN_SEGMENTS[1]) def test_type_vlan_bad(self): context = TestFakePortContext(self.AGENT_TYPE, self.AGENTS_BAD, self.VLAN_SEGMENTS, portbindings.VNIC_DIRECT) self.driver.bind_port(context) self._check_unbound(context) class SriovSwitchMechVnicTypeTestCase(SriovNicSwitchMechanismBaseTestCase): def _check_vif_type_for_vnic_type(self, vnic_type, expected_vif_type): context = TestFakePortContext(self.AGENT_TYPE, self.AGENTS, self.VLAN_SEGMENTS, vnic_type) self.driver.bind_port(context) self.assertEqual(expected_vif_type, context._bound_vif_type) vlan = int(context._bound_vif_details[portbindings.VIF_DETAILS_VLAN]) self.assertEqual(1234, vlan) def test_vnic_type_direct(self): self._check_vif_type_for_vnic_type(portbindings.VNIC_DIRECT, portbindings.VIF_TYPE_HW_VEB) def test_vnic_type_macvtap(self): self._check_vif_type_for_vnic_type(portbindings.VNIC_MACVTAP, portbindings.VIF_TYPE_HW_VEB) def test_vnic_type_direct_physical(self): self._check_vif_type_for_vnic_type(portbindings.VNIC_DIRECT_PHYSICAL, portbindings.VIF_TYPE_HOSTDEV_PHY) @mock.patch.object(mech_driver.SriovNicSwitchMechanismDriver, 'try_to_bind_segment_for_agent') def test_vnic_type_direct_with_switchdev_cap(self, mocked_bind_segment): profile = {'capabilities': ['switchdev']} context = TestFakePortContext(self.AGENT_TYPE, self.AGENTS, self.VLAN_SEGMENTS, portbindings.VNIC_DIRECT, profile) self.driver.bind_port(context) mocked_bind_segment.assert_not_called() class SriovSwitchMechVifDetailsTestCase(SriovNicSwitchMechanismBaseTestCase): VLAN_SEGMENTS = [{api.ID: 'vlan_segment_id', api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'fake_physical_network', api.SEGMENTATION_ID: 1234}] def test_vif_details_contains_vlan_id(self): context = TestFakePortContext(self.AGENT_TYPE, self.AGENTS, self.VLAN_SEGMENTS, portbindings.VNIC_DIRECT) self.driver.bind_port(context) vif_details = context._bound_vif_details self.assertIsNotNone(vif_details) vlan_id = int(vif_details.get(portbindings.VIF_DETAILS_VLAN)) self.assertEqual(1234, vlan_id) def test_get_vif_details_for_flat_network(self): segment = {api.NETWORK_TYPE: constants.TYPE_FLAT} vif_details = self.driver._get_vif_details(segment) vlan_id = vif_details[portbindings.VIF_DETAILS_VLAN] self.assertEqual('0', vlan_id) def test_get_vif_details_unsupported_net(self): segment = {api.NETWORK_TYPE: 'foo'} with testtools.ExpectedException(exc.SriovUnsupportedNetworkType): self.driver._get_vif_details(segment) def test_get_vif_details_with_agent(self): context = TestFakePortContext(self.AGENT_TYPE, self.AGENTS, self.VLAN_SEGMENTS, portbindings.VNIC_DIRECT) self.driver.bind_port(context) self.assertEqual(constants.PORT_STATUS_DOWN, context._bound_state) def test_get_vif_details_with_agent_direct_physical(self): context = TestFakePortContext(self.AGENT_TYPE, self.AGENTS, self.VLAN_SEGMENTS, portbindings.VNIC_DIRECT_PHYSICAL) self.driver.bind_port(context) self.assertEqual(constants.PORT_STATUS_ACTIVE, context._bound_state) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/test_helpers.py0000664000175000017500000001347213553660047026047 0ustar zuulzuul00000000000000# Copyright (c) 2014 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from oslo_db import exception as exc from sqlalchemy.orm import query from neutron.plugins.ml2.drivers import type_vlan from neutron.tests.unit import testlib_api TENANT_NET = 'phys_net2' VLAN_MIN = 200 VLAN_MAX = 209 VLAN_OUTSIDE = 100 NETWORK_VLAN_RANGES = { TENANT_NET: [(VLAN_MIN, VLAN_MAX)], } class HelpersTest(testlib_api.SqlTestCase): def setUp(self): super(HelpersTest, self).setUp() self.driver = type_vlan.VlanTypeDriver() self.driver.network_vlan_ranges = NETWORK_VLAN_RANGES self.driver._sync_vlan_allocations() self.context = context.get_admin_context() def check_raw_segment(self, expected, observed): for key, value in expected.items(): self.assertEqual(value, observed[key]) def test_primary_keys(self): self.assertEqual(set(['physical_network', 'vlan_id']), self.driver.primary_keys) def test_allocate_specific_unallocated_segment_in_pools(self): expected = dict(physical_network=TENANT_NET, vlan_id=VLAN_MIN) observed = self.driver.allocate_fully_specified_segment(self.context, **expected) self.check_raw_segment(expected, observed) def test_allocate_specific_allocated_segment_in_pools(self): raw_segment = dict(physical_network=TENANT_NET, vlan_id=VLAN_MIN) self.driver.allocate_fully_specified_segment(self.context, **raw_segment) observed = self.driver.allocate_fully_specified_segment(self.context, **raw_segment) self.assertIsNone(observed) def test_allocate_specific_finally_allocated_segment_in_pools(self): # Test case: allocate a specific unallocated segment in pools but # the segment is allocated concurrently between select and update raw_segment = dict(physical_network=TENANT_NET, vlan_id=VLAN_MIN) with mock.patch.object(query.Query, 'update', return_value=0): observed = self.driver.allocate_fully_specified_segment( self.context, **raw_segment) self.assertIsNone(observed) def test_allocate_specific_unallocated_segment_outside_pools(self): expected = dict(physical_network=TENANT_NET, vlan_id=VLAN_OUTSIDE) observed = self.driver.allocate_fully_specified_segment(self.context, **expected) self.check_raw_segment(expected, observed) def test_allocate_specific_allocated_segment_outside_pools(self): raw_segment = dict(physical_network=TENANT_NET, vlan_id=VLAN_OUTSIDE) self.driver.allocate_fully_specified_segment(self.context, **raw_segment) observed = self.driver.allocate_fully_specified_segment(self.context, **raw_segment) self.assertIsNone(observed) def test_allocate_specific_finally_unallocated_segment_outside_pools(self): # Test case: allocate a specific allocated segment in pools but # the segment is concurrently unallocated after select or update expected = dict(physical_network=TENANT_NET, vlan_id=VLAN_MIN) with mock.patch.object(self.driver.model, 'save'): observed = self.driver.allocate_fully_specified_segment( self.context, **expected) self.check_raw_segment(expected, observed) def test_allocate_partial_segment_without_filters(self): expected = dict(physical_network=TENANT_NET) observed = self.driver.allocate_partially_specified_segment( self.context) self.check_raw_segment(expected, observed) def test_allocate_partial_segment_with_filter(self): expected = dict(physical_network=TENANT_NET) observed = self.driver.allocate_partially_specified_segment( self.context, **expected) self.check_raw_segment(expected, observed) def test_allocate_partial_segment_no_resource_available(self): for i in range(VLAN_MIN, VLAN_MAX + 1): self.driver.allocate_partially_specified_segment(self.context) observed = self.driver.allocate_partially_specified_segment( self.context) self.assertIsNone(observed) def test_allocate_partial_segment_outside_pools(self): raw_segment = dict(physical_network='other_phys_net') observed = self.driver.allocate_partially_specified_segment( self.context, **raw_segment) self.assertIsNone(observed) def test_allocate_partial_segment_first_attempt_fails(self): expected = dict(physical_network=TENANT_NET) with mock.patch.object(query.Query, 'update', side_effect=[0, 1]): self.assertRaises( exc.RetryRequest, self.driver.allocate_partially_specified_segment, self.context, **expected) observed = self.driver.allocate_partially_specified_segment( self.context, **expected) self.check_raw_segment(expected, observed) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_fake_agent.py0000664000175000017500000000500613553660047026420 0ustar zuulzuul00000000000000# Copyright (C) 2014,2015 VA Linux Systems Japan K.K. # Copyright (C) 2014 Fumihiko Kakuma # Copyright (C) 2014,2015 YAMAMOTO Takashi # All Rights Reserved. # # Based on openvswitch mechanism driver. # # Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron.agent import securitygroups_rpc from neutron.plugins.ml2.drivers import mech_agent class FakeAgentMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """ML2 mechanism driver for testing. This is a ML2 mechanism driver used by UTs in test_l2population. This driver implements minimum requirements for L2pop mech driver. As there are some agent-based mechanism drivers and OVS agent mech driver is not the only one to support L2pop, it is useful to test L2pop with multiple drivers like this to check the minimum requirements. NOTE(yamamoto): This is a modified copy of ofagent mechanism driver as of writing this. There's no need to keep this synced with the "real" ofagent mechansim driver or its agent. """ def __init__(self): sg_enabled = securitygroups_rpc.is_firewall_enabled() vif_details = {portbindings.CAP_PORT_FILTER: sg_enabled, portbindings.OVS_HYBRID_PLUG: sg_enabled} super(FakeAgentMechanismDriver, self).__init__( # NOTE(yamamoto): l2pop driver has a hardcoded list of # supported agent types. constants.AGENT_TYPE_OFA, portbindings.VIF_TYPE_OVS, vif_details) def get_allowed_network_types(self, agent): return (agent['configurations'].get('tunnel_types', []) + [constants.TYPE_LOCAL, constants.TYPE_FLAT, constants.TYPE_VLAN]) def get_mappings(self, agent): return dict(agent['configurations'].get('interface_mappings', {})) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/ext_test.py0000664000175000017500000002274313553660046025205 0ustar zuulzuul00000000000000# Copyright 2015 Intel Corporation. # Copyright 2015 Isaku Yamahata # # All Rights Reserved. # # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import validators from neutron_lib import constants from neutron_lib.db import model_base from neutron_lib.plugins.ml2 import api import oslo_db.sqlalchemy.session import sqlalchemy as sa from sqlalchemy import orm from neutron.api import extensions from neutron.db import models_v2 from neutron.objects import subnet as subnet_obj from neutron.tests.unit.plugins.ml2 import extensions as test_extensions class TestExtensionDriverBase(api.ExtensionDriver): _supported_extension_aliases = 'fake_extension' def initialize(self): extensions.append_api_extensions_path(test_extensions.__path__) @property def extension_alias(self): return self._supported_extension_aliases class TestExtensionDriver(TestExtensionDriverBase): def initialize(self): super(TestExtensionDriver, self).initialize() # keep track of values self.val_by_id = {} def _check_create(self, session, data, result): assert(isinstance(session, oslo_db.sqlalchemy.session.Session)) assert(isinstance(data, dict)) assert('id' not in data) assert(isinstance(result, dict)) assert(result['id'] is not None) def _check_update(self, session, data, result): assert(isinstance(session, oslo_db.sqlalchemy.session.Session)) assert(isinstance(data, dict)) assert(isinstance(result, dict)) assert(result['id'] is not None) def _check_extend(self, session, result, entry, expected_db_entry_class, expected_obj_entry_class=None): # TODO(slaweq): After converting all code to use Subnet OVO, # expected_db_entry_class can be removed as only OVO object # should be expected here assert(isinstance(session, oslo_db.sqlalchemy.session.Session)) assert(isinstance(result, dict)) assert(result['id'] is not None) if expected_obj_entry_class: assert(isinstance(entry, expected_db_entry_class) or isinstance(entry, expected_obj_entry_class)) else: assert(isinstance(entry, expected_db_entry_class)) assert(entry.id == result['id']) def _store_change(self, result, data, field): if field in data and data[field] != constants.ATTR_NOT_SPECIFIED: self.val_by_id[result['id']] = data[field] elif result['id'] not in self.val_by_id: self.val_by_id[result['id']] = 'default_%s' % field def process_create_network(self, plugin_context, data, result): session = plugin_context.session self._check_create(session, data, result) self._store_change(result, data, 'network_extension') result['network_extension'] = self.val_by_id[result['id']] def process_update_network(self, plugin_context, data, result): session = plugin_context.session self._check_update(session, data, result) self._store_change(result, data, 'network_extension') result['network_extension'] = self.val_by_id[result['id']] def extend_network_dict(self, session, net_db, result): self._check_extend(session, result, net_db, models_v2.Network) result['network_extension'] = self.val_by_id.get(result['id']) def process_create_subnet(self, plugin_context, data, result): session = plugin_context.session self._check_create(session, data, result) self._store_change(result, data, 'subnet_extension') result['subnet_extension'] = self.val_by_id[result['id']] def process_update_subnet(self, plugin_context, data, result): session = plugin_context.session self._check_update(session, data, result) self._store_change(result, data, 'subnet_extension') result['subnet_extension'] = self.val_by_id[result['id']] def extend_subnet_dict(self, session, subnet_db, result): self._check_extend( session, result, subnet_db, expected_db_entry_class=models_v2.Subnet, expected_obj_entry_class=subnet_obj.Subnet) result['subnet_extension'] = self.val_by_id.get(result['id']) def process_create_port(self, plugin_context, data, result): session = plugin_context.session self._check_create(session, data, result) self._store_change(result, data, 'port_extension') result['port_extension'] = self.val_by_id[result['id']] def process_update_port(self, plugin_context, data, result): session = plugin_context.session self._check_update(session, data, result) self._store_change(result, data, 'port_extension') result['port_extension'] = self.val_by_id[result['id']] def extend_port_dict(self, session, port_db, result): self._check_extend(session, result, port_db, models_v2.Port) result['port_extension'] = self.val_by_id.get(result['id']) class TestNetworkExtension(model_base.BASEV2): network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', ondelete="CASCADE"), primary_key=True) value = sa.Column(sa.String(64)) network = orm.relationship( models_v2.Network, backref=orm.backref('extension', cascade='delete', uselist=False, lazy='joined')) class TestSubnetExtension(model_base.BASEV2): subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id', ondelete="CASCADE"), primary_key=True) value = sa.Column(sa.String(64)) subnet = orm.relationship( models_v2.Subnet, backref=orm.backref('extension', cascade='delete', uselist=False, lazy='joined')) class TestPortExtension(model_base.BASEV2): port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True) value = sa.Column(sa.String(64)) port = orm.relationship( models_v2.Port, backref=orm.backref('extension', cascade='delete', uselist=False, lazy='joined')) class TestDBExtensionDriver(TestExtensionDriverBase): def _get_value(self, data, key): value = data[key] if not validators.is_attr_set(value): value = '' return value def process_create_network(self, plugin_context, data, result): session = plugin_context.session value = self._get_value(data, 'network_extension') record = TestNetworkExtension(network_id=result['id'], value=value) session.add(record) result['network_extension'] = value def process_update_network(self, plugin_context, data, result): session = plugin_context.session record = (session.query(TestNetworkExtension). filter_by(network_id=result['id']).one()) value = data.get('network_extension') if value and value != record.value: record.value = value result['network_extension'] = record.value def extend_network_dict(self, session, net_db, result): result['network_extension'] = net_db.extension.value def process_create_subnet(self, plugin_context, data, result): session = plugin_context.session value = self._get_value(data, 'subnet_extension') record = TestSubnetExtension(subnet_id=result['id'], value=value) session.add(record) result['subnet_extension'] = value def process_update_subnet(self, plugin_context, data, result): session = plugin_context.session record = (session.query(TestSubnetExtension). filter_by(subnet_id=result['id']).one()) value = data.get('subnet_extension') if value and value != record.value: record.value = value result['subnet_extension'] = record.value def extend_subnet_dict(self, session, subnet_db, result): value = subnet_db.extension.value if subnet_db.extension else '' result['subnet_extension'] = value def process_create_port(self, plugin_context, data, result): session = plugin_context.session value = self._get_value(data, 'port_extension') record = TestPortExtension(port_id=result['id'], value=value) session.add(record) result['port_extension'] = value def process_update_port(self, plugin_context, data, result): session = plugin_context.session record = (session.query(TestPortExtension). filter_by(port_id=result['id']).one()) value = data.get('port_extension') if value and value != record.value: record.value = value result['port_extension'] = record.value def extend_port_dict(self, session, port_db, result): value = port_db.extension.value if port_db.extension else '' result['port_extension'] = value neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/test_type_flat.py0000664000175000017500000001641713553660047026376 0ustar zuulzuul00000000000000# Copyright (c) 2014 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as p_const from neutron_lib import context from neutron_lib import exceptions as exc from neutron_lib.plugins.ml2 import api from oslo_config import cfg from neutron.common import exceptions as n_exc from neutron.objects.plugins.ml2 import flatallocation as flat_obj from neutron.plugins.ml2.drivers import type_flat from neutron.tests import base from neutron.tests.unit import testlib_api FLAT_NETWORKS = ['flat_net1', 'flat_net2'] CORE_PLUGIN = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' class FlatTypeTest(testlib_api.SqlTestCase): def setUp(self): super(FlatTypeTest, self).setUp() self.setup_coreplugin(CORE_PLUGIN) cfg.CONF.set_override('flat_networks', FLAT_NETWORKS, group='ml2_type_flat') self.driver = type_flat.FlatTypeDriver() self.context = context.Context() self.driver.physnet_mtus = [] def _get_allocation(self, context, segment): return flat_obj.FlatAllocation.get_object( context, physical_network=segment[api.PHYSICAL_NETWORK]) def test_is_partial_segment(self): segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, api.PHYSICAL_NETWORK: 'flat_net1'} self.assertFalse(self.driver.is_partial_segment(segment)) def test_validate_provider_segment(self): segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, api.PHYSICAL_NETWORK: 'flat_net1'} self.driver.validate_provider_segment(segment) def test_validate_provider_phynet_name(self): self.driver._parse_networks([]) segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, api.PHYSICAL_NETWORK: 'flat_net1'} self.assertRaises(exc.InvalidInput, self.driver.validate_provider_segment, segment=segment) def test_validate_provider_phynet_name_multiple(self): self.driver._parse_networks(['flat_net1', 'flat_net2']) segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, api.PHYSICAL_NETWORK: 'flat_net1'} self.driver.validate_provider_segment(segment) segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, api.PHYSICAL_NETWORK: 'flat_net2'} self.driver.validate_provider_segment(segment) def test_validate_provider_segment_without_physnet_restriction(self): self.driver._parse_networks('*') segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, api.PHYSICAL_NETWORK: 'other_flat_net'} self.driver.validate_provider_segment(segment) def test_validate_provider_segment_with_missing_physical_network(self): segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT} self.assertRaises(exc.InvalidInput, self.driver.validate_provider_segment, segment) def test_validate_provider_segment_with_unsupported_physical_network(self): segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, api.PHYSICAL_NETWORK: 'other_flat_net'} self.assertRaises(exc.InvalidInput, self.driver.validate_provider_segment, segment) def test_validate_provider_segment_with_unallowed_segmentation_id(self): segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, api.PHYSICAL_NETWORK: 'flat_net1', api.SEGMENTATION_ID: 1234} self.assertRaises(exc.InvalidInput, self.driver.validate_provider_segment, segment) def test_reserve_provider_segment(self): segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, api.PHYSICAL_NETWORK: 'flat_net1'} observed = self.driver.reserve_provider_segment(self.context, segment) alloc = self._get_allocation(self.context, observed) self.assertEqual(segment[api.PHYSICAL_NETWORK], alloc.physical_network) def test_release_segment(self): segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, api.PHYSICAL_NETWORK: 'flat_net1'} self.driver.reserve_provider_segment(self.context, segment) self.driver.release_segment(self.context, segment) alloc = self._get_allocation(self.context, segment) self.assertIsNone(alloc) def test_reserve_provider_segment_already_reserved(self): segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, api.PHYSICAL_NETWORK: 'flat_net1'} self.driver.reserve_provider_segment(self.context, segment) self.assertRaises(n_exc.FlatNetworkInUse, self.driver.reserve_provider_segment, self.context, segment) def test_allocate_tenant_segment(self): observed = self.driver.allocate_tenant_segment(self.context) self.assertIsNone(observed) def test_get_mtu(self): cfg.CONF.set_override('global_physnet_mtu', 1475) cfg.CONF.set_override('path_mtu', 1400, group='ml2') self.driver.physnet_mtus = {'physnet1': 1450, 'physnet2': 1400} self.assertEqual(1450, self.driver.get_mtu('physnet1')) cfg.CONF.set_override('global_physnet_mtu', 1375) cfg.CONF.set_override('path_mtu', 1400, group='ml2') self.driver.physnet_mtus = {'physnet1': 1450, 'physnet2': 1400} self.assertEqual(1375, self.driver.get_mtu('physnet1')) cfg.CONF.set_override('global_physnet_mtu', 0) cfg.CONF.set_override('path_mtu', 1425, group='ml2') self.driver.physnet_mtus = {'physnet1': 1450, 'physnet2': 1400} self.assertEqual(1400, self.driver.get_mtu('physnet2')) cfg.CONF.set_override('global_physnet_mtu', 0) cfg.CONF.set_override('path_mtu', 0, group='ml2') self.driver.physnet_mtus = {} self.assertEqual(0, self.driver.get_mtu('physnet1')) def test_parse_physical_network_mtus(self): cfg.CONF.set_override( 'physical_network_mtus', ['physnet1:1500', 'physnet2:1500', 'physnet3:9000'], group='ml2') driver = type_flat.FlatTypeDriver() self.assertEqual('1500', driver.physnet_mtus['physnet1']) self.assertEqual('1500', driver.physnet_mtus['physnet2']) self.assertEqual('9000', driver.physnet_mtus['physnet3']) class FlatTypeDefaultTest(base.BaseTestCase): def setUp(self): super(FlatTypeDefaultTest, self).setUp() self.driver = type_flat.FlatTypeDriver() self.driver.physnet_mtus = [] def test_validate_provider_segment_default(self): segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, api.PHYSICAL_NETWORK: 'other_flat_net'} self.driver.validate_provider_segment(segment) neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mechanism_logger.py0000664000175000017500000001321613553660046026644 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins.ml2 import api from oslo_log import log LOG = log.getLogger(__name__) class LoggerMechanismDriver(api.MechanismDriver): """Mechanism driver that logs all calls and parameters made. Generally used for testing and debugging. """ def initialize(self): pass def _log_network_call(self, method_name, context): LOG.info("%(method)s called with network settings %(current)s " "(original settings %(original)s) and " "network segments %(segments)s", {'method': method_name, 'current': context.current, 'original': context.original, 'segments': context.network_segments}) def create_network_precommit(self, context): self._log_network_call("create_network_precommit", context) def create_network_postcommit(self, context): self._log_network_call("create_network_postcommit", context) def update_network_precommit(self, context): self._log_network_call("update_network_precommit", context) def update_network_postcommit(self, context): self._log_network_call("update_network_postcommit", context) def delete_network_precommit(self, context): self._log_network_call("delete_network_precommit", context) def delete_network_postcommit(self, context): self._log_network_call("delete_network_postcommit", context) def check_vlan_transparency(self, context): self._log_network_call("check_vlan_transparency", context) return True def _log_subnet_call(self, method_name, context): LOG.info("%(method)s called with subnet settings %(current)s " "(original settings %(original)s)", {'method': method_name, 'current': context.current, 'original': context.original}) def create_subnet_precommit(self, context): self._log_subnet_call("create_subnet_precommit", context) def create_subnet_postcommit(self, context): self._log_subnet_call("create_subnet_postcommit", context) def update_subnet_precommit(self, context): self._log_subnet_call("update_subnet_precommit", context) def update_subnet_postcommit(self, context): self._log_subnet_call("update_subnet_postcommit", context) def delete_subnet_precommit(self, context): self._log_subnet_call("delete_subnet_precommit", context) def delete_subnet_postcommit(self, context): self._log_subnet_call("delete_subnet_postcommit", context) def _log_port_call(self, method_name, context): network_context = context.network LOG.info("%(method)s called with port settings %(current)s " "(original settings %(original)s) " "host %(host)s " "(original host %(original_host)s) " "vif type %(vif_type)s " "(original vif type %(original_vif_type)s) " "vif details %(vif_details)s " "(original vif details %(original_vif_details)s) " "binding levels %(levels)s " "(original binding levels %(original_levels)s) " "on network %(network)s " "with segments to bind %(segments_to_bind)s", {'method': method_name, 'current': context.current, 'original': context.original, 'host': context.host, 'original_host': context.original_host, 'vif_type': context.vif_type, 'original_vif_type': context.original_vif_type, 'vif_details': context.vif_details, 'original_vif_details': context.original_vif_details, 'levels': context.binding_levels, 'original_levels': context.original_binding_levels, 'network': network_context.current, 'segments_to_bind': context.segments_to_bind}) def create_port_precommit(self, context): self._log_port_call("create_port_precommit", context) def create_port_postcommit(self, context): self._log_port_call("create_port_postcommit", context) def update_port_precommit(self, context): self._log_port_call("update_port_precommit", context) def update_port_postcommit(self, context): self._log_port_call("update_port_postcommit", context) def delete_port_precommit(self, context): self._log_port_call("delete_port_precommit", context) def delete_port_postcommit(self, context): self._log_port_call("delete_port_postcommit", context) def bind_port(self, context): self._log_port_call("bind_port", context) def filter_hosts_with_segment_access( self, context, segments, candidate_hosts, agent_getter): LOG.info("filter_hosts_with_segment_access called with segments " "%(segments)s, candidate hosts %(hosts)s ", {'segments': segments, 'hosts': candidate_hosts}) return set() neutron-12.1.1/neutron/tests/unit/plugins/ml2/drivers/mech_faulty_agent.py0000664000175000017500000000243213553660046027015 0ustar zuulzuul00000000000000# All Rights Reserved. # # Based on openvswitch mechanism driver. # # Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.plugins.ml2.drivers import mech_agent class FaultyAgentMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """ML2 mechanism driver for testing of handlers for faulty drivers The purpose of this class is to test the ml2 plugin manager handlers for on_load_failure_callback parameter provided by the stevedore.named.NamedExtensionManager class. """ def __init__(self): raise Exception("Using a faulty driver for testing purposes.") def get_allowed_network_types(self, agent): pass def get_mappings(self, agent): pass neutron-12.1.1/neutron/tests/unit/plugins/ml2/test_agent_scheduler.py0000664000175000017500000000251313553660047026055 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.tests.unit.db import test_agentschedulers_db from neutron.tests.unit.plugins.ml2 import test_plugin class Ml2AgentSchedulerTestCase( test_agentschedulers_db.OvsAgentSchedulerTestCase): plugin_str = test_plugin.PLUGIN_NAME l3_plugin = ('neutron.services.l3_router.' 'l3_router_plugin.L3RouterPlugin') class Ml2L3AgentNotifierTestCase( test_agentschedulers_db.OvsL3AgentNotifierTestCase): plugin_str = test_plugin.PLUGIN_NAME l3_plugin = ('neutron.services.l3_router.' 'l3_router_plugin.L3RouterPlugin') class Ml2DhcpAgentNotifierTestCase( test_agentschedulers_db.OvsDhcpAgentNotifierTestCase): plugin_str = test_plugin.PLUGIN_NAME neutron-12.1.1/neutron/tests/unit/plugins/ml2/test_rpc.py0000664000175000017500000005325413553660047023515 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Unit Tests for ml2 rpc """ import collections import mock from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from neutron_lib.services.qos import constants as qos_consts from oslo_config import cfg from oslo_context import context as oslo_context from sqlalchemy.orm import exc from neutron.agent import rpc as agent_rpc from neutron.common import topics from neutron.db import provisioning_blocks from neutron.plugins.ml2 import db as ml2_db from neutron.plugins.ml2.drivers import type_tunnel from neutron.plugins.ml2 import managers from neutron.plugins.ml2 import rpc as plugin_rpc from neutron.tests import base cfg.CONF.import_group('ml2', 'neutron.conf.plugins.ml2.config') class RpcCallbacksTestCase(base.BaseTestCase): def setUp(self): super(RpcCallbacksTestCase, self).setUp() self.type_manager = managers.TypeManager() self.notifier = plugin_rpc.AgentNotifierApi(topics.AGENT) self.callbacks = plugin_rpc.RpcCallbacks(self.notifier, self.type_manager) self.plugin = mock.MagicMock() directory.add_plugin(plugin_constants.CORE, self.plugin) def _test_update_device_up(self, host=None): kwargs = { 'agent_id': 'foo_agent', 'device': 'foo_device', 'host': host } with mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin' '._device_to_port_id'),\ mock.patch.object(self.callbacks, 'notify_l2pop_port_wiring'): with mock.patch('neutron.db.provisioning_blocks.' 'provisioning_complete') as pc: self.callbacks.update_device_up(mock.Mock(), **kwargs) return pc def test_update_device_up_notify(self): notify = self._test_update_device_up() notify.assert_called_once_with(mock.ANY, mock.ANY, resources.PORT, provisioning_blocks.L2_AGENT_ENTITY) def test_update_device_up_notify_not_sent_with_port_not_found(self): self.plugin.port_bound_to_host.return_value = False notify = self._test_update_device_up('host') self.assertFalse(notify.call_count) def test_get_device_details_without_port_context(self): self.plugin.get_bound_port_context.return_value = None self.assertEqual( {'device': 'fake_device'}, self.callbacks.get_device_details(mock.Mock(), device='fake_device')) def test_get_device_details_port_context_without_bounded_segment(self): self.plugin.get_bound_port_context().bottom_bound_segment = None self.assertEqual( {'device': 'fake_device'}, self.callbacks.get_device_details(mock.Mock(), device='fake_device')) def test_get_device_details_port_status_equal_new_status(self): port = collections.defaultdict(lambda: 'fake') self.plugin.get_bound_port_context().current = port self.plugin.port_bound_to_host = port for admin_state_up in (True, False): new_status = (constants.PORT_STATUS_BUILD if admin_state_up else constants.PORT_STATUS_DOWN) for status in (constants.PORT_STATUS_ACTIVE, constants.PORT_STATUS_BUILD, constants.PORT_STATUS_DOWN, constants.PORT_STATUS_ERROR): port['admin_state_up'] = admin_state_up port['status'] = status self.plugin.update_port_status.reset_mock() self.callbacks.get_device_details(mock.Mock()) self.assertEqual(status == new_status, not self.plugin.update_port_status.called) def test_get_device_details_caching(self): port = collections.defaultdict(lambda: 'fake_port') cached_networks = {} self.plugin.get_bound_port_context().current = port self.plugin.get_bound_port_context().network.current = ( {"id": "fake_network"}) self.callbacks.get_device_details(mock.Mock(), host='fake_host', cached_networks=cached_networks) self.assertIn('fake_port', cached_networks) def test_get_device_details_wrong_host(self): port = collections.defaultdict(lambda: 'fake') port_context = self.plugin.get_bound_port_context() port_context.current = port port_context.host = 'fake' self.plugin.update_port_status.reset_mock() self.callbacks.get_device_details(mock.Mock(), host='fake_host') self.assertFalse(self.plugin.update_port_status.called) def test_get_device_details_port_no_host(self): port = collections.defaultdict(lambda: 'fake') port_context = self.plugin.get_bound_port_context() port_context.current = port self.plugin.update_port_status.reset_mock() self.callbacks.get_device_details(mock.Mock()) self.assertTrue(self.plugin.update_port_status.called) def test_get_device_details_qos_policy_id_none(self): port = collections.defaultdict(lambda: 'fake_port') self.plugin.get_bound_port_context().current = port self.plugin.get_bound_port_context().network._network = ( {"id": "fake_network"}) res = self.callbacks.get_device_details(mock.Mock(), host='fake') self.assertIsNone(res['qos_policy_id']) def test_get_device_details_network_qos_policy_id(self): port = collections.defaultdict(lambda: 'fake_port') self.plugin.get_bound_port_context().current = port self.plugin.get_bound_port_context().network._network = ( {"id": "fake_network", qos_consts.QOS_POLICY_ID: 'test-policy-id'}) res = self.callbacks.get_device_details(mock.Mock(), host='fake') self.assertEqual('test-policy-id', res['network_qos_policy_id']) def test_get_device_details_qos_policy_id_from_port(self): port = collections.defaultdict( lambda: 'fake_port', {qos_consts.QOS_POLICY_ID: 'test-port-policy-id'}) self.plugin.get_bound_port_context().current = port self.plugin.get_bound_port_context().network._network = ( {"id": "fake_network", qos_consts.QOS_POLICY_ID: 'test-net-policy-id'}) res = self.callbacks.get_device_details(mock.Mock(), host='fake') self.assertEqual('test-port-policy-id', res['qos_policy_id']) def _test_get_devices_list(self, callback, side_effect, expected): devices = [1, 2, 3, 4, 5] kwargs = {'host': 'fake_host', 'agent_id': 'fake_agent_id'} with mock.patch.object(self.callbacks, '_get_device_details', side_effect=side_effect) as f: res = callback('fake_context', devices=devices, **kwargs) self.assertEqual(expected, res) self.assertEqual(len(devices), f.call_count) calls = [mock.call('fake_context', device=i, port_context=mock.ANY, **kwargs) for i in devices] f.assert_has_calls(calls) def test_get_devices_details_list(self): results = [{'device': [v]} for v in [1, 2, 3, 4, 5]] expected = results callback = self.callbacks.get_devices_details_list self._test_get_devices_list(callback, results, expected) def test_get_devices_details_list_with_empty_devices(self): with mock.patch.object(self.callbacks, 'get_device_details') as f: res = self.callbacks.get_devices_details_list('fake_context') self.assertFalse(f.called) self.assertEqual([], res) def test_get_devices_details_list_and_failed_devices(self): devices = [1, 2, 3, 4, 5] expected = {'devices': devices, 'failed_devices': []} callback = ( self.callbacks.get_devices_details_list_and_failed_devices) self._test_get_devices_list(callback, devices, expected) def test_get_devices_details_list_and_failed_devices_failures(self): devices = [1, Exception('testdevice'), 3, Exception('testdevice'), 5] expected = {'devices': [1, 3, 5], 'failed_devices': [2, 4]} callback = ( self.callbacks.get_devices_details_list_and_failed_devices) self._test_get_devices_list(callback, devices, expected) def test_get_devices_details_list_and_failed_devices_empty_dev(self): with mock.patch.object(self.callbacks, 'get_device_details') as f: res = self.callbacks.get_devices_details_list_and_failed_devices( 'fake_context') self.assertFalse(f.called) self.assertEqual({'devices': [], 'failed_devices': []}, res) def _test_update_device_not_bound_to_host(self, func): self.plugin.port_bound_to_host.return_value = False self.callbacks.notify_l2pop_port_wiring = mock.Mock() self.plugin._device_to_port_id.return_value = 'fake_port_id' res = func(mock.Mock(), device='fake_device', host='fake_host') self.plugin.port_bound_to_host.assert_called_once_with(mock.ANY, 'fake_port_id', 'fake_host') return res def test_update_device_up_with_device_not_bound_to_host(self): with mock.patch.object(ml2_db, 'get_port') as ml2_db_get_port: self.assertIsNone(self._test_update_device_not_bound_to_host( self.callbacks.update_device_up)) port = ml2_db_get_port.return_value (self.plugin.nova_notifier.notify_port_active_direct. assert_called_once_with(port)) def test_update_device_down_with_device_not_bound_to_host(self): self.assertEqual( {'device': 'fake_device', 'exists': True}, self._test_update_device_not_bound_to_host( self.callbacks.update_device_down)) def test_update_device_down_call_update_port_status(self): self.plugin.update_port_status.return_value = False self.callbacks.notify_l2pop_port_wiring = mock.Mock() self.plugin._device_to_port_id.return_value = 'fake_port_id' self.assertEqual( {'device': 'fake_device', 'exists': False}, self.callbacks.update_device_down(mock.Mock(), device='fake_device', host='fake_host')) self.plugin.update_port_status.assert_called_once_with( mock.ANY, 'fake_port_id', constants.PORT_STATUS_DOWN, 'fake_host') def test_notify_l2pop_port_wiring_non_dvr_port(self): port = {'device_owner': constants.DEVICE_OWNER_COMPUTE_PREFIX} l2pop_driver = ( self.plugin.mechanism_manager.mech_drivers.get.return_value) with mock.patch.object(ml2_db, 'get_port') as ml2_db_get_port: ml2_db_get_port.return_value = port self.callbacks.notify_l2pop_port_wiring( 'port_id', mock.Mock(), 'DOWN', 'host', agent_restarted=None) self.assertFalse(l2pop_driver.obj.agent_restarted.called) def test_update_device_down_call_update_port_status_failed(self): self.plugin.update_port_status.side_effect = exc.StaleDataError self.assertEqual({'device': 'fake_device', 'exists': False}, self.callbacks.update_device_down( mock.Mock(), device='fake_device')) def _test_update_device_list(self, devices_up_side_effect, devices_down_side_effect, expected): devices_up = [1, 2, 3] devices_down = [4, 5] kwargs = {'host': 'fake_host', 'agent_id': 'fake_agent_id'} with mock.patch.object(self.callbacks, 'update_device_up', side_effect=devices_up_side_effect) as f_up, \ mock.patch.object(self.callbacks, 'update_device_down', side_effect=devices_down_side_effect) as f_down: res = self.callbacks.update_device_list( 'fake_context', devices_up=devices_up, devices_down=devices_down, **kwargs) self.assertEqual(expected, res) self.assertEqual(len(devices_up), f_up.call_count) self.assertEqual(len(devices_down), f_down.call_count) def test_update_device_list_no_failure(self): devices_up_side_effect = [1, 2, 3] devices_down_side_effect = [ {'device': 4, 'exists': True}, {'device': 5, 'exists': True}] expected = {'devices_up': devices_up_side_effect, 'failed_devices_up': [], 'devices_down': [{'device': 4, 'exists': True}, {'device': 5, 'exists': True}], 'failed_devices_down': []} self._test_update_device_list(devices_up_side_effect, devices_down_side_effect, expected) def test_update_device_list_failed_devices(self): devices_up_side_effect = [1, Exception('testdevice'), 3] devices_down_side_effect = [{'device': 4, 'exists': True}, Exception('testdevice')] expected = {'devices_up': [1, 3], 'failed_devices_up': [2], 'devices_down': [{'device': 4, 'exists': True}], 'failed_devices_down': [5]} self._test_update_device_list(devices_up_side_effect, devices_down_side_effect, expected) def test_update_device_list_empty_devices(self): expected = {'devices_up': [], 'failed_devices_up': [], 'devices_down': [], 'failed_devices_down': []} kwargs = {'host': 'fake_host', 'agent_id': 'fake_agent_id'} res = self.callbacks.update_device_list( 'fake_context', devices_up=[], devices_down=[], **kwargs) self.assertEqual(expected, res) class RpcApiTestCase(base.BaseTestCase): def _test_rpc_api(self, rpcapi, topic, method, rpc_method, **kwargs): if method == "update_device_list": expected = {'devices_up': [], 'failed_devices_up': [], 'devices_down': [], 'failed_devices_down': []} else: expected = 'foo' ctxt = oslo_context.RequestContext(user='fake_user', tenant='fake_project') expected_retval = expected if rpc_method == 'call' else None expected_version = kwargs.pop('version', None) fanout = kwargs.pop('fanout', False) with mock.patch.object(rpcapi.client, rpc_method) as rpc_mock,\ mock.patch.object(rpcapi.client, 'prepare') as prepare_mock: prepare_mock.return_value = rpcapi.client rpc_mock.return_value = expected_retval retval = getattr(rpcapi, method)(ctxt, **kwargs) prepare_args = {} if expected_version: prepare_args['version'] = expected_version if fanout: prepare_args['fanout'] = fanout if topic: prepare_args['topic'] = topic prepare_mock.assert_called_once_with(**prepare_args) self.assertEqual(retval, expected_retval) rpc_mock.assert_called_once_with(ctxt, method, **kwargs) def test_delete_network(self): rpcapi = plugin_rpc.AgentNotifierApi(topics.AGENT) self._test_rpc_api( rpcapi, topics.get_topic_name(topics.AGENT, topics.NETWORK, topics.DELETE), 'network_delete', rpc_method='cast', fanout=True, network_id='fake_request_spec') def test_port_update(self): rpcapi = plugin_rpc.AgentNotifierApi(topics.AGENT) self._test_rpc_api( rpcapi, topics.get_topic_name(topics.AGENT, topics.PORT, topics.UPDATE), 'port_update', rpc_method='cast', fanout=True, port='fake_port', network_type='fake_network_type', segmentation_id='fake_segmentation_id', physical_network='fake_physical_network') def test_port_delete(self): rpcapi = plugin_rpc.AgentNotifierApi(topics.AGENT) self._test_rpc_api( rpcapi, topics.get_topic_name(topics.AGENT, topics.PORT, topics.DELETE), 'port_delete', rpc_method='cast', fanout=True, port_id='fake_port') def test_tunnel_update(self): rpcapi = plugin_rpc.AgentNotifierApi(topics.AGENT) self._test_rpc_api( rpcapi, topics.get_topic_name(topics.AGENT, type_tunnel.TUNNEL, topics.UPDATE), 'tunnel_update', rpc_method='cast', fanout=True, tunnel_ip='fake_ip', tunnel_type='gre') def test_tunnel_delete(self): rpcapi = plugin_rpc.AgentNotifierApi(topics.AGENT) self._test_rpc_api( rpcapi, topics.get_topic_name(topics.AGENT, type_tunnel.TUNNEL, topics.DELETE), 'tunnel_delete', rpc_method='cast', fanout=True, tunnel_ip='fake_ip', tunnel_type='gre') def test_device_details(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) self._test_rpc_api(rpcapi, None, 'get_device_details', rpc_method='call', device='fake_device', agent_id='fake_agent_id', host='fake_host') def test_devices_details_list(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) self._test_rpc_api(rpcapi, None, 'get_devices_details_list', rpc_method='call', devices=['fake_device1', 'fake_device2'], agent_id='fake_agent_id', host='fake_host', version='1.3') def test_update_device_down(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) self._test_rpc_api(rpcapi, None, 'update_device_down', rpc_method='call', device='fake_device', agent_id='fake_agent_id', host='fake_host') def test_tunnel_sync(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) self._test_rpc_api(rpcapi, None, 'tunnel_sync', rpc_method='call', tunnel_ip='fake_tunnel_ip', tunnel_type=None, host='fake_host', version='1.4') def test_update_device_up(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) self._test_rpc_api(rpcapi, None, 'update_device_up', rpc_method='call', device='fake_device', agent_id='fake_agent_id', host='fake_host') def test_update_device_list(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) self._test_rpc_api(rpcapi, None, 'update_device_list', rpc_method='call', devices_up=['fake_device1', 'fake_device2'], devices_down=['fake_device3', 'fake_device4'], agent_id='fake_agent_id', host='fake_host', agent_restarted=False, version='1.5') def test_get_devices_details_list_and_failed_devices(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) self._test_rpc_api(rpcapi, None, 'get_devices_details_list_and_failed_devices', rpc_method='call', devices=['fake_device1', 'fake_device2'], agent_id='fake_agent_id', host='fake_host', version='1.5') def test_devices_details_list_and_failed_devices(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) self._test_rpc_api(rpcapi, None, 'get_devices_details_list_and_failed_devices', rpc_method='call', devices=['fake_device1', 'fake_device2'], agent_id='fake_agent_id', host='fake_host', version='1.5') neutron-12.1.1/neutron/tests/unit/plugins/ml2/base.py0000664000175000017500000000336013553660046022574 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins import constants from neutron_lib.plugins import directory from neutron.tests.unit.plugins.ml2 import test_plugin class ML2TestFramework(test_plugin.Ml2PluginV2TestCase): l3_plugin = ('neutron.services.l3_router.l3_router_plugin.' 'L3RouterPlugin') _mechanism_drivers = ['openvswitch'] def get_additional_service_plugins(self): p = super(ML2TestFramework, self).get_additional_service_plugins() p.update({'flavors_plugin_name': 'neutron.services.flavors.' 'flavors_plugin.FlavorsPlugin'}) return p def setUp(self): super(ML2TestFramework, self).setUp() self.core_plugin = directory.get_plugin() self.l3_plugin = directory.get_plugin(constants.L3) def _create_router(self, distributed=False, ha=False, admin_state_up=True): return self.l3_plugin.create_router( self.context, {'router': {'name': 'router', 'admin_state_up': admin_state_up, 'tenant_id': self._tenant_id, 'ha': ha, 'distributed': distributed}}) neutron-12.1.1/neutron/tests/unit/plugins/ml2/extensions/0000775000175000017500000000000013553660157023510 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/extensions/test_port_security.py0000664000175000017500000000311613553660046030032 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import port_security as psec from neutron.plugins.ml2.extensions import port_security from neutron.tests.unit.plugins.ml2 import test_plugin class TestML2ExtensionPortSecurity(test_plugin.Ml2PluginV2TestCase): def _test_extend_dict_no_port_security(self, func): """Test extend_*_dict won't crash if port_security item is None.""" for db_data in ({'port_security': None, 'name': 'net1'}, {}): response_data = {} session = mock.Mock() driver = port_security.PortSecurityExtensionDriver() getattr(driver, func)(session, db_data, response_data) self.assertTrue(response_data[psec.PORTSECURITY]) def test_extend_port_dict_no_port_security(self): self._test_extend_dict_no_port_security('extend_port_dict') def test_extend_network_dict_no_port_security(self): self._test_extend_dict_no_port_security('extend_network_dict') neutron-12.1.1/neutron/tests/unit/plugins/ml2/extensions/__init__.py0000664000175000017500000000000013553660046025604 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/extensions/fake_extension.py0000664000175000017500000000413513553660046027064 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import extensions from neutron_lib import constants from neutron._i18n import _ EXTENDED_ATTRIBUTES_2_0 = { 'networks': { 'network_extension': {'allow_post': True, 'allow_put': True, 'default': constants.ATTR_NOT_SPECIFIED, 'is_visible': True, 'enforce_policy': True}, }, 'subnets': { 'subnet_extension': {'allow_post': True, 'allow_put': True, 'default': constants.ATTR_NOT_SPECIFIED, 'is_visible': True, 'enforce_policy': True}, }, 'ports': { 'port_extension': {'allow_post': True, 'allow_put': True, 'default': constants.ATTR_NOT_SPECIFIED, 'is_visible': True, 'enforce_policy': True}, }, } class Fake_extension(extensions.ExtensionDescriptor): @classmethod def get_name(cls): return "ML2 fake extension" @classmethod def get_alias(cls): return "fake_extension" @classmethod def get_description(cls): return _("Adds test attributes to core resources.") @classmethod def get_updated(cls): return "2014-07-16T10:00:00-00:00" def get_extended_resources(self, version): if version == "2.0": return EXTENDED_ATTRIBUTES_2_0 else: return {} neutron-12.1.1/neutron/tests/unit/plugins/ml2/extensions/test_dns_integration.py0000664000175000017500000013064413553660047030316 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneauth1 import loading from keystoneauth1 import session import mock import netaddr from neutron_lib.api.definitions import dns as dns_apidef from neutron_lib.api.definitions import provider_net as pnet from neutron_lib import constants from neutron_lib import context from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import uuidutils import testtools from neutron.objects import ports as port_obj from neutron.plugins.ml2.extensions import dns_integration from neutron.services.externaldns.drivers.designate import driver from neutron.tests.unit.plugins.ml2 import test_plugin mock_client = mock.Mock() mock_admin_client = mock.Mock() mock_config = {'return_value': (mock_client, mock_admin_client)} DNSNAME = 'port-dns-name' DNSDOMAIN = 'domain.com.' PORTDNSDOMAIN = 'port-dns-domain.com.' NEWDNSNAME = 'new-port-dns-name' NEWPORTDNSDOMAIN = 'new-port-dns-domain.com.' V4UUID = 'v4_uuid' V6UUID = 'v6_uuid' @mock.patch( 'neutron.services.externaldns.drivers.designate.driver.get_clients', **mock_config) class DNSIntegrationTestCase(test_plugin.Ml2PluginV2TestCase): _extension_drivers = ['dns'] _domain = DNSDOMAIN def setUp(self): cfg.CONF.set_override('extension_drivers', self._extension_drivers, group='ml2') cfg.CONF.set_override('external_dns_driver', 'designate') mock_client.reset_mock() mock_admin_client.reset_mock() super(DNSIntegrationTestCase, self).setUp() dns_integration.DNS_DRIVER = None dns_integration.subscribe() self.plugin = directory.get_plugin() cfg.CONF.set_override('dns_domain', self._domain) def _create_port_for_test(self, provider_net=True, dns_domain=True, dns_name=True, ipv4=True, ipv6=True, dns_domain_port=False): net_kwargs = {} if provider_net: net_kwargs = { 'arg_list': (pnet.NETWORK_TYPE, pnet.SEGMENTATION_ID,), pnet.NETWORK_TYPE: 'vxlan', pnet.SEGMENTATION_ID: '2016', } if dns_domain: net_kwargs[dns_apidef.DNSDOMAIN] = DNSDOMAIN net_kwargs['arg_list'] = \ net_kwargs.get('arg_list', ()) + (dns_apidef.DNSDOMAIN,) res = self._create_network(self.fmt, 'test_network', True, **net_kwargs) network = self.deserialize(self.fmt, res) if ipv4: cidr = '10.0.0.0/24' self._create_subnet_for_test(network['network']['id'], cidr) if ipv6: cidr = 'fd3d:bdd4:da60::/64' self._create_subnet_for_test(network['network']['id'], cidr) port_kwargs = {} if dns_name: port_kwargs = { 'arg_list': (dns_apidef.DNSNAME,), dns_apidef.DNSNAME: DNSNAME } if dns_domain_port: port_kwargs[dns_apidef.DNSDOMAIN] = PORTDNSDOMAIN port_kwargs['arg_list'] = (port_kwargs.get('arg_list', ()) + (dns_apidef.DNSDOMAIN,)) res = self._create_port('json', network['network']['id'], **port_kwargs) self.assertEqual(201, res.status_int) port = self.deserialize(self.fmt, res)['port'] ctx = context.get_admin_context() dns_data_db = port_obj.PortDNS.get_object(ctx, port_id=port['id']) return port, dns_data_db def _create_subnet_for_test(self, network_id, cidr): ip_net = netaddr.IPNetwork(cidr) # initialize the allocation_pool to the lower half of the subnet subnet_size = ip_net.last - ip_net.first subnet_mid_point = ip_net.first + int(subnet_size / 2) start, end = (netaddr.IPAddress(ip_net.first + 2), netaddr.IPAddress(subnet_mid_point)) allocation_pools = [{'start': str(start), 'end': str(end)}] return self._create_subnet(self.fmt, network_id, str(ip_net), ip_version=ip_net.ip.version, allocation_pools=allocation_pools) def _update_port_for_test(self, port, new_dns_name=NEWDNSNAME, new_dns_domain=None, **kwargs): mock_client.reset_mock() ip_addresses = [netaddr.IPAddress(ip['ip_address']) for ip in port['fixed_ips']] records_v4 = [ip for ip in ip_addresses if ip.version == 4] records_v6 = [ip for ip in ip_addresses if ip.version == 6] recordsets = [] if records_v4: recordsets.append({'id': V4UUID, 'records': records_v4}) if records_v6: recordsets.append({'id': V6UUID, 'records': records_v6}) mock_client.recordsets.list.return_value = recordsets mock_admin_client.reset_mock() body = {} if new_dns_name is not None: body['dns_name'] = new_dns_name if new_dns_domain is not None: body[dns_apidef.DNSDOMAIN] = new_dns_domain body.update(kwargs) data = {'port': body} req = self.new_update_request('ports', data, port['id']) res = req.get_response(self.api) self.assertEqual(200, res.status_int) port = self.deserialize(self.fmt, res)['port'] ctx = context.get_admin_context() dns_data_db = port_obj.PortDNS.get_object(ctx, port_id=port['id']) return port, dns_data_db def _verify_port_dns(self, port, dns_data_db, dns_name=True, dns_domain=True, ptr_zones=True, delete_records=False, provider_net=True, dns_driver=True, original_ips=None, current_dns_name=DNSNAME, previous_dns_name='', dns_domain_port=False, current_dns_domain=DNSDOMAIN, previous_dns_domain=DNSDOMAIN): if dns_name: self.assertEqual(current_dns_name, port[dns_apidef.DNSNAME]) if dns_domain_port: self.assertTrue(port[dns_apidef.DNSDOMAIN]) is_there_dns_domain = dns_domain or dns_domain_port if dns_name and is_there_dns_domain and provider_net and dns_driver: self.assertEqual(current_dns_name, dns_data_db['current_dns_name']) self.assertEqual(previous_dns_name, dns_data_db['previous_dns_name']) if current_dns_name: self.assertEqual(current_dns_domain, dns_data_db['current_dns_domain']) else: self.assertFalse(dns_data_db['current_dns_domain']) records_v4 = [ip['ip_address'] for ip in port['fixed_ips'] if netaddr.IPAddress(ip['ip_address']).version == 4] records_v6 = [ip['ip_address'] for ip in port['fixed_ips'] if netaddr.IPAddress(ip['ip_address']).version == 6] expected = [] expected_delete = [] if records_v4: if current_dns_name: expected.append( mock.call(current_dns_domain, current_dns_name, 'A', records_v4)) if delete_records: expected_delete.append(mock.call(previous_dns_domain, V4UUID)) if records_v6: if current_dns_name: expected.append( mock.call(current_dns_domain, current_dns_name, 'AAAA', records_v6)) if delete_records: expected_delete.append(mock.call(previous_dns_domain, V6UUID)) mock_client.recordsets.create.assert_has_calls(expected, any_order=True) self.assertEqual( len(mock_client.recordsets.create.call_args_list), len(expected)) mock_client.recordsets.delete.assert_has_calls(expected_delete, any_order=True) self.assertEqual( len(mock_client.recordsets.delete.call_args_list), len(expected_delete)) expected = [] expected_delete = [] if ptr_zones: records = records_v4 + records_v6 recordset_name = '%s.%s' % (current_dns_name, current_dns_domain) for record in records: in_addr_name = netaddr.IPAddress(record).reverse_dns in_addr_zone_name = self._get_in_addr_zone_name( in_addr_name) if current_dns_name: expected.append(mock.call(in_addr_zone_name, in_addr_name, 'PTR', [recordset_name])) if delete_records and not original_ips: expected_delete.append(mock.call(in_addr_zone_name, in_addr_name)) if delete_records and original_ips: for record in original_ips: in_addr_name = netaddr.IPAddress(record).reverse_dns in_addr_zone_name = self._get_in_addr_zone_name( in_addr_name) expected_delete.append(mock.call(in_addr_zone_name, in_addr_name)) mock_admin_client.recordsets.create.assert_has_calls( expected, any_order=True) self.assertEqual( len(mock_admin_client.recordsets.create.call_args_list), len(expected)) mock_admin_client.recordsets.delete.assert_has_calls( expected_delete, any_order=True) self.assertEqual( len(mock_admin_client.recordsets.delete.call_args_list), len(expected_delete)) else: if not dns_name: self.assertEqual('', port[dns_apidef.DNSNAME]) if not (dns_name or dns_domain_port): self.assertIsNone(dns_data_db) self.assertFalse(mock_client.recordsets.create.call_args_list) self.assertFalse( mock_admin_client.recordsets.create.call_args_list) self.assertFalse(mock_client.recordsets.delete.call_args_list) self.assertFalse( mock_admin_client.recordsets.delete.call_args_list) def _get_in_addr_zone_name(self, in_addr_name): units = self._get_bytes_or_nybles_to_skip(in_addr_name) return '.'.join(in_addr_name.split('.')[int(units):]) def _get_bytes_or_nybles_to_skip(self, in_addr_name): if 'in-addr.arpa' in in_addr_name: return (( constants.IPv4_BITS - cfg.CONF.designate.ipv4_ptr_zone_prefix_size) / 8) return (constants.IPv6_BITS - cfg.CONF.designate.ipv6_ptr_zone_prefix_size) / 4 def test_create_port(self, *mocks): port, dns_data_db = self._create_port_for_test() self._verify_port_dns(port, dns_data_db) def test_create_port_tenant_network(self, *mocks): port, dns_data_db = self._create_port_for_test(provider_net=False) self._verify_port_dns(port, dns_data_db, provider_net=False) def test_create_port_no_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_name=False) self._verify_port_dns(port, dns_data_db, dns_name=False) def test_create_port_no_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_domain=False) self._verify_port_dns(port, dns_data_db, dns_domain=False) def test_create_port_no_dns_driver(self, *mocks): cfg.CONF.set_override('external_dns_driver', '') port, dns_data_db = self._create_port_for_test() self._verify_port_dns(port, dns_data_db, dns_driver=False) def test_create_port_no_ipv6(self, *mocks): port, dns_data_db = self._create_port_for_test(ipv6=False) self._verify_port_dns(port, dns_data_db) def test_create_port_no_ipv4(self, *mocks): port, dns_data_db = self._create_port_for_test(ipv4=False) self._verify_port_dns(port, dns_data_db) def test_create_port_no_ptr_zones(self, *mocks): cfg.CONF.set_override( 'allow_reverse_dns_lookup', False, group='designate') port, dns_data_db = self._create_port_for_test() self._verify_port_dns(port, dns_data_db, ptr_zones=False) cfg.CONF.set_override('allow_reverse_dns_lookup', True, group='designate') def test_update_port(self, *mocks): port, dns_data_db = self._create_port_for_test() port, dns_data_db = self._update_port_for_test(port) self._verify_port_dns(port, dns_data_db, delete_records=True, current_dns_name=NEWDNSNAME, previous_dns_name=DNSNAME) def test_update_port_with_current_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test() port, dns_data_db = self._update_port_for_test(port, new_dns_name=DNSNAME) self.assertEqual(DNSNAME, dns_data_db['current_dns_name']) self.assertEqual(DNSDOMAIN, dns_data_db['current_dns_domain']) self.assertEqual('', dns_data_db['previous_dns_name']) self.assertEqual('', dns_data_db['previous_dns_domain']) self.assertFalse(mock_client.recordsets.create.call_args_list) self.assertFalse( mock_admin_client.recordsets.create.call_args_list) self.assertFalse(mock_client.recordsets.delete.call_args_list) self.assertFalse( mock_admin_client.recordsets.delete.call_args_list) def test_update_port_tenant_network(self, *mocks): port, dns_data_db = self._create_port_for_test(provider_net=False) port, dns_data_db = self._update_port_for_test(port) self._verify_port_dns(port, dns_data_db, delete_records=True, current_dns_name=NEWDNSNAME, previous_dns_name=DNSNAME, provider_net=False) def test_update_port_no_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_domain=False) port, dns_data_db = self._update_port_for_test(port) self._verify_port_dns(port, dns_data_db, delete_records=True, current_dns_name=NEWDNSNAME, previous_dns_name=DNSNAME, dns_domain=False) def test_update_port_add_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_name=False) port, dns_data_db = self._update_port_for_test(port) self._verify_port_dns(port, dns_data_db, delete_records=False, current_dns_name=NEWDNSNAME, previous_dns_name='') def test_update_port_clear_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test() port, dns_data_db = self._update_port_for_test(port, new_dns_name='') self._verify_port_dns(port, dns_data_db, delete_records=True, current_dns_name='', previous_dns_name=DNSNAME) def test_update_port_non_dns_name_attribute(self, *mocks): port, dns_data_db = self._create_port_for_test() port_name = 'port_name' kwargs = {'name': port_name} port, dns_data_db = self._update_port_for_test(port, new_dns_name=None, **kwargs) self.assertEqual(DNSNAME, dns_data_db['current_dns_name']) self.assertEqual(DNSDOMAIN, dns_data_db['current_dns_domain']) self.assertEqual('', dns_data_db['previous_dns_name']) self.assertEqual('', dns_data_db['previous_dns_domain']) self.assertFalse(mock_client.recordsets.create.call_args_list) self.assertFalse( mock_admin_client.recordsets.create.call_args_list) self.assertFalse(mock_client.recordsets.delete.call_args_list) self.assertFalse( mock_admin_client.recordsets.delete.call_args_list) self.assertEqual(port_name, port['name']) def _compute_new_fixed_ips(self, port): new_fixed_ips = [ {'subnet_id': ip['subnet_id'], 'ip_address': str(netaddr.IPAddress(ip['ip_address']) + 1)} for ip in port['fixed_ips'] ] return {'fixed_ips': new_fixed_ips} def test_update_port_fixed_ips(self, *mocks): port, dns_data_db = self._create_port_for_test() original_ips = [ip['ip_address'] for ip in port['fixed_ips']] kwargs = self._compute_new_fixed_ips(port) port, dns_data_db = self._update_port_for_test(port, new_dns_name=None, **kwargs) self._verify_port_dns(port, dns_data_db, delete_records=True, current_dns_name=DNSNAME, previous_dns_name=DNSNAME, original_ips=original_ips) def test_update_port_fixed_ips_with_new_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test() original_ips = [ip['ip_address'] for ip in port['fixed_ips']] kwargs = self._compute_new_fixed_ips(port) port, dns_data_db = self._update_port_for_test(port, new_dns_name=NEWDNSNAME, **kwargs) self._verify_port_dns(port, dns_data_db, delete_records=True, current_dns_name=NEWDNSNAME, previous_dns_name=DNSNAME, original_ips=original_ips) def test_update_port_fixed_ips_with_current_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test() original_ips = [ip['ip_address'] for ip in port['fixed_ips']] kwargs = self._compute_new_fixed_ips(port) port, dns_data_db = self._update_port_for_test(port, new_dns_name=DNSNAME, **kwargs) self._verify_port_dns(port, dns_data_db, delete_records=True, current_dns_name=DNSNAME, previous_dns_name=DNSNAME, original_ips=original_ips) def test_update_port_fixed_ips_clearing_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test() original_ips = [ip['ip_address'] for ip in port['fixed_ips']] kwargs = self._compute_new_fixed_ips(port) port, dns_data_db = self._update_port_for_test(port, new_dns_name='', **kwargs) self._verify_port_dns(port, dns_data_db, delete_records=True, current_dns_name='', previous_dns_name=DNSNAME, original_ips=original_ips) def _assert_update_fixed_ips_no_effect_after_clearing_dns_attribute( self, dns_data_db, dns_data_db_1, dns_data_db_2): self.assertEqual('', dns_data_db_2['current_dns_name']) self.assertEqual('', dns_data_db_2['current_dns_domain']) self.assertEqual(dns_data_db_1['current_dns_name'], dns_data_db_2['current_dns_name']) self.assertEqual(dns_data_db_1['current_dns_domain'], dns_data_db_2['current_dns_domain']) self.assertEqual(dns_data_db['current_dns_name'], dns_data_db_1['previous_dns_name']) self.assertEqual(dns_data_db['current_dns_domain'], dns_data_db_1['previous_dns_domain']) self.assertFalse(dns_data_db_2['previous_dns_name']) self.assertFalse(dns_data_db_2['previous_dns_domain']) self.assertFalse(mock_client.recordsets.create.call_args_list) self.assertFalse( mock_admin_client.recordsets.create.call_args_list) self.assertFalse(mock_client.recordsets.delete.call_args_list) self.assertFalse( mock_admin_client.recordsets.delete.call_args_list) def test_update_fixed_ips_no_effect_after_clearing_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test() port, dns_data_db_1 = self._update_port_for_test(port, new_dns_name='') kwargs = self._compute_new_fixed_ips(port) mock_client.reset_mock() mock_admin_client.reset_mock() port, dns_data_db_2 = self._update_port_for_test(port, new_dns_name='', **kwargs) self._assert_update_fixed_ips_no_effect_after_clearing_dns_attribute( dns_data_db, dns_data_db_1, dns_data_db_2) def test_create_port_dns_name_field_missing(self, *mocks): res = self._create_network(self.fmt, 'test_network', True) net = self.deserialize(self.fmt, res)['network'] cidr = '10.0.0.0/24' self._create_subnet_for_test(net['id'], cidr) port_request = { 'port': { 'network_id': net['id'], 'tenant_id': net['tenant_id'], 'name': 'mugsie', 'admin_state_up': True, 'device_id': '', 'device_owner': '', 'fixed_ips': '' } } self.plugin.create_port(self.context, port_request) def test_dns_driver_loaded_after_server_restart(self, *mocks): dns_integration.DNS_DRIVER = None port, dns_data_db = self._create_port_for_test() self._verify_port_dns(port, dns_data_db) class DNSIntegrationTestCaseDefaultDomain(DNSIntegrationTestCase): _domain = 'openstacklocal.' def _generate_dns_assignment(self, port): fqdn = [] for ip in port['fixed_ips']: hostname = 'host-%s' % ip['ip_address'].replace( '.', '-').replace(':', '-') fqdn.append('%s.%s' % (hostname, self._domain)) return set(fqdn) def _verify_port_dns(self, port, dns_data_db, dns_name=True, dns_domain=True, ptr_zones=True, delete_records=False, provider_net=True, dns_driver=True, original_ips=None, current_dns_name=DNSNAME, previous_dns_name=''): self.assertEqual('', port[dns_apidef.DNSNAME]) fqdn_set = self._generate_dns_assignment(port) port_fqdn_set = set([each['fqdn'] for each in port['dns_assignment']]) self.assertEqual(fqdn_set, port_fqdn_set) self.assertIsNone(dns_data_db, "dns data should be none") self.assertFalse(mock_client.recordsets.create.call_args_list) self.assertFalse( mock_admin_client.recordsets.create.call_args_list) self.assertFalse(mock_client.recordsets.delete.call_args_list) self.assertFalse( mock_admin_client.recordsets.delete.call_args_list) def test_update_fixed_ips_no_effect_after_clearing_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test() port, dns_data_db_1 = self._update_port_for_test(port, new_dns_name='') kwargs = {'fixed_ips': []} for ip in port['fixed_ips']: kwargs['fixed_ips'].append( {'subnet_id': ip['subnet_id'], 'ip_address': str(netaddr.IPAddress(ip['ip_address']) + 1)}) mock_client.reset_mock() mock_admin_client.reset_mock() port, dns_data_db_2 = self._update_port_for_test(port, new_dns_name='', **kwargs) self._verify_port_dns(port, dns_data_db_2) def test_update_port_non_dns_name_attribute(self, *mocks): port, dns_data_db = self._create_port_for_test() port_name = 'port_name' kwargs = {'name': port_name} port, dns_data_db = self._update_port_for_test(port, new_dns_name=None, **kwargs) self._verify_port_dns(port, dns_data_db) def test_update_port_with_current_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test() port, dns_data_db = self._update_port_for_test(port, new_dns_name=DNSNAME) self._verify_port_dns(port, dns_data_db) @mock.patch( 'neutron.services.externaldns.drivers.designate.driver.get_clients', **mock_config) class DNSDomainPortsTestCase(DNSIntegrationTestCase): _extension_drivers = ['dns_domain_ports'] def test_create_port_net_dns_domain_port_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test( dns_domain_port=True) self._verify_port_dns(port, dns_data_db, dns_domain_port=True, current_dns_domain=PORTDNSDOMAIN) def test_create_port_no_net_dns_domain_port_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test( dns_domain=False, dns_domain_port=True) self._verify_port_dns(port, dns_data_db, dns_domain=False, dns_domain_port=True, current_dns_domain=PORTDNSDOMAIN) def test_create_port_no_net_dns_domain_no_port_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_domain=False) self._verify_port_dns(port, dns_data_db, dns_domain=False) def test_create_port_port_dns_domain_no_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_domain=False, dns_domain_port=True, dns_name=False) self._verify_port_dns(port, dns_data_db, dns_name=False, dns_domain=False, dns_domain_port=True) self.assertEqual(PORTDNSDOMAIN, dns_data_db[dns_apidef.DNSDOMAIN]) self.assertEqual(PORTDNSDOMAIN, port[dns_apidef.DNSDOMAIN]) def test_update_port_replace_port_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test( dns_domain_port=True) port, dns_data_db = self._update_port_for_test( port, new_dns_name=None, new_dns_domain=NEWPORTDNSDOMAIN) self._verify_port_dns(port, dns_data_db, delete_records=True, current_dns_name=DNSNAME, previous_dns_name=DNSNAME, current_dns_domain=NEWPORTDNSDOMAIN, previous_dns_domain=PORTDNSDOMAIN) def test_update_port_replace_network_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test() port, dns_data_db = self._update_port_for_test( port, new_dns_name=None, new_dns_domain=PORTDNSDOMAIN) self._verify_port_dns(port, dns_data_db, delete_records=True, current_dns_name=DNSNAME, previous_dns_name=DNSNAME, current_dns_domain=PORTDNSDOMAIN) def test_update_port_add_dns_domain_no_net_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_domain=False) port, dns_data_db = self._update_port_for_test( port, new_dns_name=None, new_dns_domain=PORTDNSDOMAIN) self._verify_port_dns(port, dns_data_db, current_dns_name=DNSNAME, current_dns_domain=PORTDNSDOMAIN, previous_dns_domain='') def test_update_port_add_dns_name_port_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_domain=False, dns_domain_port=True, dns_name=False) port, dns_data_db = self._update_port_for_test(port) self._verify_port_dns(port, dns_data_db, current_dns_name=NEWDNSNAME, current_dns_domain=PORTDNSDOMAIN, previous_dns_domain='') def test_update_port_add_port_dns_domain_port_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_domain=False) port, dns_data_db = self._update_port_for_test( port, new_dns_name=None, new_dns_domain=PORTDNSDOMAIN) self._verify_port_dns(port, dns_data_db, current_dns_name=DNSNAME, current_dns_domain=PORTDNSDOMAIN, previous_dns_domain='') def test_update_port_add_port_dns_domain_add_port_dns_name(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_name=False, dns_domain=False) port, dns_data_db = self._update_port_for_test( port, new_dns_domain=NEWPORTDNSDOMAIN) self._verify_port_dns(port, dns_data_db, current_dns_name=NEWDNSNAME, current_dns_domain=NEWPORTDNSDOMAIN, previous_dns_domain='') def test_update_port_clear_port_dns_domain_no_network_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_domain_port=True, dns_domain=False) port, dns_data_db = self._update_port_for_test(port, new_dns_domain='', new_dns_name=None) self.assertFalse(dns_data_db['current_dns_name']) self.assertFalse(dns_data_db['current_dns_domain']) self.assertEqual(DNSNAME, dns_data_db['previous_dns_name']) self.assertEqual(PORTDNSDOMAIN, dns_data_db['previous_dns_domain']) self.assertEqual(DNSNAME, dns_data_db[dns_apidef.DNSNAME]) self.assertFalse(dns_data_db[dns_apidef.DNSDOMAIN]) self.assertEqual(DNSNAME, port[dns_apidef.DNSNAME]) self.assertFalse(port[dns_apidef.DNSDOMAIN]) self.assertFalse(mock_client.recordsets.create.call_args_list) self.assertFalse(mock_admin_client.recordsets.create.call_args_list) self.assertEqual(2, mock_client.recordsets.delete.call_count) self.assertEqual( 2, len(mock_admin_client.recordsets.delete.call_args_list)) def test_update_port_clear_port_dns_domain_network_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_domain_port=True) port, dns_data_db = self._update_port_for_test(port, new_dns_domain='', new_dns_name=None) self._verify_port_dns(port, dns_data_db, delete_records=True, current_dns_name=DNSNAME, previous_dns_name=DNSNAME, previous_dns_domain=PORTDNSDOMAIN) def _assert_no_external_dns_service_calls(self, port, dns_data_db, dns_name=DNSNAME, dns_domain=PORTDNSDOMAIN): if dns_data_db: self.assertFalse(dns_data_db['current_dns_name']) self.assertFalse(dns_data_db['current_dns_domain']) self.assertFalse(dns_data_db['previous_dns_name']) self.assertFalse(dns_data_db['previous_dns_domain']) self.assertEqual(dns_name, dns_data_db[dns_apidef.DNSNAME]) self.assertEqual(dns_domain, dns_data_db[dns_apidef.DNSDOMAIN]) self.assertEqual(dns_name, port[dns_apidef.DNSNAME]) self.assertEqual(dns_domain, port[dns_apidef.DNSDOMAIN]) self.assertFalse(mock_client.recordsets.create.call_args_list) self.assertFalse( mock_admin_client.recordsets.create.call_args_list) self.assertFalse(mock_client.recordsets.delete.call_args_list) self.assertFalse( mock_admin_client.recordsets.delete.call_args_list) def test_create_port_dns_name_dns_domain_no_provider_net(self, *mocks): port, dns_data_db = self._create_port_for_test(provider_net=False, dns_domain_port=True) self.assertIsNotNone(dns_data_db) self._assert_no_external_dns_service_calls(port, dns_data_db) def test_create_port_no_dns_name_dns_domain_no_provider_net(self, *mocks): port, dns_data_db = self._create_port_for_test(provider_net=False, dns_name=False, dns_domain_port=True) self.assertIsNotNone(dns_data_db) self._assert_no_external_dns_service_calls(port, dns_data_db, dns_name='') def test_create_port_dns_name_no_dns_domain_no_provider_net(self, *mocks): port, dns_data_db = self._create_port_for_test(provider_net=False) self.assertIsNotNone(dns_data_db) self._assert_no_external_dns_service_calls(port, dns_data_db, dns_domain='') def test_create_port_no_dns_name_no_dns_domain_no_provider_net(self, *mocks): port, dns_data_db = self._create_port_for_test(provider_net=False, dns_name=False) self.assertIsNone(dns_data_db) self._assert_no_external_dns_service_calls(port, dns_data_db, dns_name='', dns_domain='') def test_update_port_add_dns_name_add_dns_domain_no_provider_net(self, *mocks): port, dns_data_db = self._create_port_for_test(provider_net=False, dns_name=False) self.assertIsNone(dns_data_db) port, dns_data_db = self._update_port_for_test( port, new_dns_domain=PORTDNSDOMAIN, new_dns_name=DNSNAME) self.assertIsNotNone(dns_data_db) self._assert_no_external_dns_service_calls(port, dns_data_db) def test_update_port_add_dns_domain_no_provider_net(self, *mocks): port, dns_data_db = self._create_port_for_test(provider_net=False) self.assertIsNotNone(dns_data_db) port, dns_data_db = self._update_port_for_test( port, new_dns_domain=PORTDNSDOMAIN, new_dns_name=None) self.assertIsNotNone(dns_data_db) self._assert_no_external_dns_service_calls(port, dns_data_db) def test_update_port_fixed_ips_with_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test( dns_domain_port=True) original_ips = [ip['ip_address'] for ip in port['fixed_ips']] kwargs = self._compute_new_fixed_ips(port) port, dns_data_db = self._update_port_for_test(port, new_dns_name=None, **kwargs) self._verify_port_dns(port, dns_data_db, delete_records=True, current_dns_name=DNSNAME, previous_dns_name=DNSNAME, current_dns_domain=PORTDNSDOMAIN, previous_dns_domain=PORTDNSDOMAIN, original_ips=original_ips) def test_update_fixed_ips_no_effect_after_clearing_dns_domain(self, *mocks): port, dns_data_db = self._create_port_for_test(dns_domain_port=True, dns_domain=False) port, dns_data_db_1 = self._update_port_for_test(port, new_dns_domain='', new_dns_name=None) kwargs = self._compute_new_fixed_ips(port) mock_client.reset_mock() mock_admin_client.reset_mock() port, dns_data_db_2 = self._update_port_for_test(port, new_dns_name=None, **kwargs) self._assert_update_fixed_ips_no_effect_after_clearing_dns_attribute( dns_data_db, dns_data_db_1, dns_data_db_2) class TestDesignateClientKeystoneV2(testtools.TestCase): """Test case for designate clients """ TEST_URL = 'http://127.0.0.1:9001/v2' TEST_ADMIN_USERNAME = uuidutils.generate_uuid(dashed=False) TEST_ADMIN_PASSWORD = uuidutils.generate_uuid(dashed=False) TEST_ADMIN_TENANT_NAME = uuidutils.generate_uuid(dashed=False) TEST_ADMIN_TENANT_ID = uuidutils.generate_uuid(dashed=False) TEST_ADMIN_AUTH_URL = 'http://127.0.0.1:35357/v2.0' TEST_CA_CERT = uuidutils.generate_uuid(dashed=False) TEST_CONTEXT = mock.Mock() TEST_CONTEXT.auth_token = uuidutils.generate_uuid(dashed=False) def setUp(self): super(TestDesignateClientKeystoneV2, self).setUp() cfg.CONF.set_override('url', self.TEST_URL, group='designate') cfg.CONF.set_override('admin_username', self.TEST_ADMIN_USERNAME, group='designate') cfg.CONF.set_override('admin_password', self.TEST_ADMIN_PASSWORD, group='designate') cfg.CONF.set_override('admin_auth_url', self.TEST_ADMIN_AUTH_URL, group='designate') cfg.CONF.set_override('admin_tenant_id', self.TEST_ADMIN_TENANT_ID, group='designate') cfg.CONF.set_override('admin_tenant_name', self.TEST_ADMIN_TENANT_NAME, group='designate') # enforce session recalculation mock.patch.object(driver, '_SESSION', new=None).start() self.driver_session = ( mock.patch.object(session, 'Session').start()) self.load_auth = ( mock.patch.object(driver.loading, 'load_auth_from_conf_options').start()) self.password = ( mock.patch.object(driver.password, 'Password').start()) def test_insecure_client(self): cfg.CONF.set_override('insecure', True, group='designate') driver.get_clients(self.TEST_CONTEXT) self.driver_session.assert_called_with(cert=None, timeout=None, verify=False) def test_secure_client(self): cfg.CONF.set_override('insecure', False, group='designate') cfg.CONF.set_override('cafile', self.TEST_CA_CERT, group='designate') driver.get_clients(self.TEST_CONTEXT) self.driver_session.assert_called_with(cert=None, timeout=None, verify=self.TEST_CA_CERT) def test_auth_type_not_defined(self): driver.get_clients(self.TEST_CONTEXT) self.load_auth.assert_not_called() self.password.assert_called_with( auth_url=self.TEST_ADMIN_AUTH_URL, password=self.TEST_ADMIN_PASSWORD, tenant_id=self.TEST_ADMIN_TENANT_ID, tenant_name=self.TEST_ADMIN_TENANT_NAME, username=self.TEST_ADMIN_USERNAME) class TestDesignateClientKeystoneV3(testtools.TestCase): """Test case for designate clients """ TEST_URL = 'http://127.0.0.1:9001/v2' TEST_ADMIN_USERNAME = uuidutils.generate_uuid(dashed=False) TEST_ADMIN_PASSWORD = uuidutils.generate_uuid(dashed=False) TEST_ADMIN_USER_DOMAIN_ID = 'Default' TEST_ADMIN_PROJECT_ID = uuidutils.generate_uuid(dashed=False) TEST_ADMIN_PROJECT_DOMAIN_ID = 'Default' TEST_ADMIN_AUTH_URL = 'http://127.0.0.1:35357/v3' TEST_CA_CERT = uuidutils.generate_uuid(dashed=False) TEST_CONTEXT = mock.Mock() TEST_CONTEXT.auth_token = uuidutils.generate_uuid(dashed=False) def setUp(self): super(TestDesignateClientKeystoneV3, self).setUp() # Register the Password auth plugin options, # so we can use CONF.set_override password_option = loading.get_auth_plugin_conf_options('password') cfg.CONF.register_opts(password_option, group='designate') self.addCleanup( cfg.CONF.unregister_opts, password_option, group='designate') cfg.CONF.set_override('url', self.TEST_URL, group='designate') cfg.CONF.set_override('auth_type', 'password', group='designate') cfg.CONF.set_override('username', self.TEST_ADMIN_USERNAME, group='designate') cfg.CONF.set_override('password', self.TEST_ADMIN_PASSWORD, group='designate') cfg.CONF.set_override('user_domain_id', self.TEST_ADMIN_USER_DOMAIN_ID, group='designate') cfg.CONF.set_override('project_domain_id', self.TEST_ADMIN_PROJECT_DOMAIN_ID, group='designate') cfg.CONF.set_override('auth_url', self.TEST_ADMIN_AUTH_URL, group='designate') # enforce session recalculation mock.patch.object(driver, '_SESSION', new=None).start() self.driver_session = ( mock.patch.object(session, 'Session').start()) self.load_auth = ( mock.patch.object(driver.loading, 'load_auth_from_conf_options').start()) self.password = ( mock.patch.object(driver.password, 'Password').start()) def test_insecure_client(self): cfg.CONF.set_override('insecure', True, group='designate') driver.get_clients(self.TEST_CONTEXT) self.driver_session.assert_called_with(cert=None, timeout=None, verify=False) def test_secure_client(self): cfg.CONF.set_override('insecure', False, group='designate') cfg.CONF.set_override('cafile', self.TEST_CA_CERT, group='designate') driver.get_clients(self.TEST_CONTEXT) self.driver_session.assert_called_with(cert=None, timeout=None, verify=self.TEST_CA_CERT) def test_auth_type_password(self): driver.get_clients(self.TEST_CONTEXT) self.load_auth.assert_called_with(cfg.CONF, 'designate') self.password.assert_not_called() neutron-12.1.1/neutron/tests/unit/plugins/ml2/extensions/test_data_plane_status.py0000664000175000017500000000535713553660046030623 0ustar zuulzuul00000000000000# Copyright (c) 2017 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import data_plane_status as dps_lib from neutron_lib.api.definitions import port as port_def from neutron_lib import constants from neutron_lib import context from neutron_lib.plugins import directory from oslo_config import cfg from neutron.plugins.ml2.extensions import data_plane_status from neutron.tests.unit.plugins.ml2 import test_plugin class DataPlaneStatusSML2ExtDriverTestCase(test_plugin.Ml2PluginV2TestCase): _extension_drivers = ['data_plane_status'] def setUp(self): cfg.CONF.set_override('extension_drivers', self._extension_drivers, group='ml2') super(DataPlaneStatusSML2ExtDriverTestCase, self).setUp() self.plugin = directory.get_plugin() def test_extend_port_dict_no_data_plane_status(self): for db_data in ({'data_plane_status': None}, {}): response_data = {} session = mock.Mock() driver = data_plane_status.DataPlaneStatusExtensionDriver() driver.extend_port_dict(session, db_data, response_data) self.assertIsNone(response_data['data_plane_status']) def test_show_port_has_data_plane_status(self): with self.port() as port: req = self.new_show_request(port_def.COLLECTION_NAME, port['port']['id'], self.fmt) p = self.deserialize(self.fmt, req.get_response(self.api)) self.assertIsNone(p['port'][dps_lib.DATA_PLANE_STATUS]) def test_port_update_data_plane_status(self): with self.port() as port: admin_ctx = context.get_admin_context() p = {'port': {dps_lib.DATA_PLANE_STATUS: constants.ACTIVE}} self.plugin.update_port(admin_ctx, port['port']['id'], p) req = self.new_show_request( port_def.COLLECTION_NAME, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(res['port'][dps_lib.DATA_PLANE_STATUS], constants.ACTIVE) neutron-12.1.1/neutron/tests/unit/plugins/ml2/_test_mech_agent.py0000664000175000017500000002530613553660047025157 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib.plugins.ml2 import api from neutron.tests import base NETWORK_ID = "fake_network" PORT_ID = "fake_port" class FakeNetworkContext(api.NetworkContext): def __init__(self, segments): self._network_segments = segments @property def current(self): return {'id': NETWORK_ID} @property def original(self): return None @property def network_segments(self): return self._network_segments class FakePortContext(api.PortContext): def __init__(self, agent_type, agents, segments, vnic_type=portbindings.VNIC_NORMAL, original=None, profile=None): self._agent_type = agent_type self._agents = agents self._network_context = FakeNetworkContext(segments) self._bound_vnic_type = vnic_type self._bound_profile = profile self._bound_segment_id = None self._bound_vif_type = None self._bound_vif_details = None self._original = original self._binding_levels = [] @property def current(self): return {'id': PORT_ID, portbindings.VNIC_TYPE: self._bound_vnic_type, portbindings.PROFILE: self._bound_profile} @property def original(self): return self._original @property def status(self): return 'DOWN' @property def original_status(self): return None @property def network(self): return self._network_context def _prepare_to_bind(self, segments_to_bind): self._segments_to_bind = segments_to_bind self._new_bound_segment = None self._next_segments_to_bind = None def _push_binding_level(self, binding_level): self._binding_levels.append(binding_level) def _pop_binding_level(self): return self._binding_levels.pop() @property def binding_levels(self): if self._binding_levels: return [{ api.BOUND_DRIVER: level.driver, api.BOUND_SEGMENT: self._expand_segment(level.segment_id) } for level in self._binding_levels] @property def original_binding_levels(self): return None @property def top_bound_segment(self): if self._binding_levels: return self._expand_segment(self._binding_levels[0].segment_id) @property def original_top_bound_segment(self): return None @property def bottom_bound_segment(self): if self._binding_levels: return self._expand_segment(self._binding_levels[-1].segment_id) @property def original_bottom_bound_segment(self): return None def _expand_segment(self, segment_id): for segment in self._network_context.network_segments: if segment[api.ID] == self._bound_segment_id: return segment @property def host(self): return '' @property def original_host(self): return None @property def vif_type(self): return portbindings.UNBOUND @property def original_vif_type(self): return portbindings.UNBOUND @property def vif_details(self): return None @property def original_vif_details(self): return None @property def segments_to_bind(self): return self._network_context.network_segments def host_agents(self, agent_type): if agent_type == self._agent_type: return self._agents else: return [] def set_binding(self, segment_id, vif_type, vif_details): self._bound_segment_id = segment_id self._bound_vif_type = vif_type self._bound_vif_details = vif_details def continue_binding(self, segment_id, next_segments_to_bind): pass def allocate_dynamic_segment(self, segment): pass def release_dynamic_segment(self, segment_id): pass class AgentMechanismBaseTestCase(base.BaseTestCase): # The following must be overridden for the specific mechanism # driver being tested: VIF_TYPE = None VIF_DETAILS = None AGENT_TYPE = None AGENTS = None AGENTS_DEAD = None AGENTS_BAD = None VNIC_TYPE = portbindings.VNIC_NORMAL def _check_unbound(self, context): self.assertIsNone(context._bound_segment_id) self.assertIsNone(context._bound_vif_type) self.assertIsNone(context._bound_vif_details) def _check_bound(self, context, segment): self.assertEqual(context._bound_segment_id, segment[api.ID]) self.assertEqual(context._bound_vif_type, self.VIF_TYPE) vif_details = context._bound_vif_details self.assertIsNotNone(vif_details) # NOTE(r-mibu): The following five lines are just for backward # compatibility. In this class, HAS_PORT_FILTER has been replaced # by VIF_DETAILS which can be set expected vif_details to check, # but all replacement of HAS_PORT_FILTER in successor has not been # completed. if self.VIF_DETAILS is None: expected = getattr(self, 'CAP_PORT_FILTER', None) port_filter = vif_details[portbindings.CAP_PORT_FILTER] self.assertEqual(expected, port_filter) return self.assertEqual(self.VIF_DETAILS, vif_details) class AgentMechanismGenericTestCase(AgentMechanismBaseTestCase): UNKNOWN_TYPE_SEGMENTS = [{api.ID: 'unknown_segment_id', api.NETWORK_TYPE: 'no_such_type', api.NETWORK_ID: 'fake_network_id'}] def test_unknown_type(self): context = FakePortContext(self.AGENT_TYPE, self.AGENTS, self.UNKNOWN_TYPE_SEGMENTS, vnic_type=self.VNIC_TYPE) self.driver.bind_port(context) self._check_unbound(context) class AgentMechanismLocalTestCase(AgentMechanismBaseTestCase): LOCAL_SEGMENTS = [{api.ID: 'unknown_segment_id', api.NETWORK_TYPE: 'no_such_type', api.NETWORK_ID: 'fake_network_id'}, {api.ID: 'local_segment_id', api.NETWORK_TYPE: 'local', api.NETWORK_ID: 'fake_network_id'}] def test_type_local(self): context = FakePortContext(self.AGENT_TYPE, self.AGENTS, self.LOCAL_SEGMENTS, vnic_type=self.VNIC_TYPE) self.driver.bind_port(context) self._check_bound(context, self.LOCAL_SEGMENTS[1]) def test_type_local_dead(self): context = FakePortContext(self.AGENT_TYPE, self.AGENTS_DEAD, self.LOCAL_SEGMENTS, vnic_type=self.VNIC_TYPE) self.driver.bind_port(context) self._check_unbound(context) class AgentMechanismFlatTestCase(AgentMechanismBaseTestCase): FLAT_SEGMENTS = [{api.ID: 'unknown_segment_id', api.NETWORK_TYPE: 'no_such_type', api.NETWORK_ID: 'fake_network_id'}, {api.ID: 'flat_segment_id', api.NETWORK_TYPE: 'flat', api.PHYSICAL_NETWORK: 'fake_physical_network', api.NETWORK_ID: 'fake_network_id'}] def test_type_flat(self): context = FakePortContext(self.AGENT_TYPE, self.AGENTS, self.FLAT_SEGMENTS, vnic_type=self.VNIC_TYPE) self.driver.bind_port(context) self._check_bound(context, self.FLAT_SEGMENTS[1]) def test_type_flat_bad(self): context = FakePortContext(self.AGENT_TYPE, self.AGENTS_BAD, self.FLAT_SEGMENTS, vnic_type=self.VNIC_TYPE) self.driver.bind_port(context) self._check_unbound(context) class AgentMechanismVlanTestCase(AgentMechanismBaseTestCase): VLAN_SEGMENTS = [{api.ID: 'unknown_segment_id', api.NETWORK_TYPE: 'no_such_type', api.NETWORK_ID: 'fake_network_id'}, {api.ID: 'vlan_segment_id', api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'fake_physical_network', api.SEGMENTATION_ID: 1234, api.NETWORK_ID: 'fake_network_id'}] def test_type_vlan(self): context = FakePortContext(self.AGENT_TYPE, self.AGENTS, self.VLAN_SEGMENTS, vnic_type=self.VNIC_TYPE) self.driver.bind_port(context) self._check_bound(context, self.VLAN_SEGMENTS[1]) def test_type_vlan_bad(self): context = FakePortContext(self.AGENT_TYPE, self.AGENTS_BAD, self.VLAN_SEGMENTS, vnic_type=self.VNIC_TYPE) self.driver.bind_port(context) self._check_unbound(context) class AgentMechanismGreTestCase(AgentMechanismBaseTestCase): GRE_SEGMENTS = [{api.ID: 'unknown_segment_id', api.NETWORK_TYPE: 'no_such_type', api.NETWORK_ID: 'fake_network_id'}, {api.ID: 'gre_segment_id', api.NETWORK_TYPE: 'gre', api.SEGMENTATION_ID: 1234, api.NETWORK_ID: 'fake_network_id'}] def test_type_gre(self): context = FakePortContext(self.AGENT_TYPE, self.AGENTS, self.GRE_SEGMENTS) self.driver.bind_port(context) self._check_bound(context, self.GRE_SEGMENTS[1]) def test_type_gre_bad(self): context = FakePortContext(self.AGENT_TYPE, self.AGENTS_BAD, self.GRE_SEGMENTS) self.driver.bind_port(context) self._check_unbound(context) neutron-12.1.1/neutron/tests/unit/plugins/ml2/test_extension_driver_api.py0000664000175000017500000003112613553660047027143 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import uuidutils from neutron.tests.unit.plugins.ml2.drivers import ext_test from neutron.tests.unit.plugins.ml2 import test_plugin class ExtensionDriverTestCase(test_plugin.Ml2PluginV2TestCase): _extension_drivers = ['test'] def setUp(self): cfg.CONF.set_override('extension_drivers', self._extension_drivers, group='ml2') super(ExtensionDriverTestCase, self).setUp() self._plugin = directory.get_plugin() self._ctxt = context.get_admin_context() def _verify_network_create(self, code, exc_reason): tenant_id = uuidutils.generate_uuid() data = {'network': {'name': 'net1', 'tenant_id': tenant_id}} req = self.new_create_request('networks', data) res = req.get_response(self.api) self.assertEqual(code, res.status_int) network = self.deserialize(self.fmt, res) if exc_reason: self.assertEqual(exc_reason, network['NeutronError']['type']) return (network, tenant_id) def _verify_network_update(self, network, code, exc_reason): net_id = network['network']['id'] new_name = 'a_brand_new_name' data = {'network': {'name': new_name}} req = self.new_update_request('networks', data, net_id) res = req.get_response(self.api) self.assertEqual(code, res.status_int) error = self.deserialize(self.fmt, res) self.assertEqual(exc_reason, error['NeutronError']['type']) def test_faulty_process_create(self): with mock.patch.object(ext_test.TestExtensionDriver, 'process_create_network', side_effect=TypeError): net, tenant_id = self._verify_network_create(500, 'HTTPInternalServerError') # Verify the operation is rolled back query_params = "tenant_id=%s" % tenant_id nets = self._list('networks', query_params=query_params) self.assertFalse(nets['networks']) def test_faulty_process_update(self): with mock.patch.object(ext_test.TestExtensionDriver, 'process_update_network', side_effect=TypeError): network, tid = self._verify_network_create(201, None) self._verify_network_update(network, 500, 'HTTPInternalServerError') def test_faulty_extend_dict(self): with mock.patch.object(ext_test.TestExtensionDriver, 'extend_network_dict', side_effect=[None, None, TypeError]): network, tid = self._verify_network_create(201, None) self._verify_network_update(network, 400, 'ExtensionDriverError') def test_network_attr(self): with self.network() as network: # Test create network ent = network['network'].get('network_extension') self.assertIsNotNone(ent) # Test list networks res = self._list('networks') val = res['networks'][0].get('network_extension') self.assertEqual('default_network_extension', val) # Test network update data = {'network': {'network_extension': 'Test_Network_Extension_Update'}} res = self._update('networks', network['network']['id'], data) val = res['network'].get('network_extension') self.assertEqual('Test_Network_Extension_Update', val) def test_subnet_attr(self): with self.subnet() as subnet: # Test create subnet ent = subnet['subnet'].get('subnet_extension') self.assertIsNotNone(ent) # Test list subnets res = self._list('subnets') val = res['subnets'][0].get('subnet_extension') self.assertEqual('default_subnet_extension', val) # Test subnet update data = {'subnet': {'subnet_extension': 'Test_Subnet_Extension_Update'}} res = self._update('subnets', subnet['subnet']['id'], data) val = res['subnet'].get('subnet_extension') self.assertEqual('Test_Subnet_Extension_Update', val) def test_port_attr(self): with self.port() as port: # Test create port ent = port['port'].get('port_extension') self.assertIsNotNone(ent) # Test list ports res = self._list('ports') val = res['ports'][0].get('port_extension') self.assertEqual('default_port_extension', val) # Test port update data = {'port': {'port_extension': 'Test_Port_Extension_Update'}} res = self._update('ports', port['port']['id'], data) val = res['port'].get('port_extension') self.assertEqual('Test_Port_Extension_Update', val) def test_extend_network_dict(self): with mock.patch.object(ext_test.TestExtensionDriver, 'process_update_network') as ext_update_net,\ mock.patch.object(ext_test.TestExtensionDriver, 'extend_network_dict') as ext_net_dict,\ self.network() as network: net_id = network['network']['id'] net_data = {'network': {'id': net_id}} self._plugin.update_network(self._ctxt, net_id, net_data) self.assertTrue(ext_update_net.called) self.assertTrue(ext_net_dict.called) def test_extend_subnet_dict(self): with mock.patch.object(ext_test.TestExtensionDriver, 'process_update_subnet') as ext_update_subnet,\ mock.patch.object(ext_test.TestExtensionDriver, 'extend_subnet_dict') as ext_subnet_dict,\ self.subnet() as subnet: subnet_id = subnet['subnet']['id'] subnet_data = {'subnet': {'id': subnet_id}} self._plugin.update_subnet(self._ctxt, subnet_id, subnet_data) self.assertTrue(ext_update_subnet.called) self.assertTrue(ext_subnet_dict.called) def test_extend_port_dict(self): with mock.patch.object(ext_test.TestExtensionDriver, 'process_update_port') as ext_update_port,\ mock.patch.object(ext_test.TestExtensionDriver, 'extend_port_dict') as ext_port_dict,\ self.port() as port: port_id = port['port']['id'] port_data = {'port': {'id': port_id}} self._plugin.update_port(self._ctxt, port_id, port_data) self.assertTrue(ext_update_port.called) self.assertTrue(ext_port_dict.called) class DBExtensionDriverTestCase(test_plugin.Ml2PluginV2TestCase): _extension_drivers = ['testdb'] def setUp(self): cfg.CONF.set_override('extension_drivers', self._extension_drivers, group='ml2') super(DBExtensionDriverTestCase, self).setUp() self._plugin = directory.get_plugin() self._ctxt = context.get_admin_context() def test_network_attr(self): with self.network() as network: # Test create with default value. net_id = network['network']['id'] val = network['network']['network_extension'] self.assertEqual("", val) res = self._show('networks', net_id) val = res['network']['network_extension'] self.assertEqual("", val) # Test list. res = self._list('networks') val = res['networks'][0]['network_extension'] self.assertEqual("", val) # Test create with explicit value. res = self._create_network(self.fmt, 'test-network', True, arg_list=('network_extension', ), network_extension="abc") network = self.deserialize(self.fmt, res) net_id = network['network']['id'] val = network['network']['network_extension'] self.assertEqual("abc", val) res = self._show('networks', net_id) val = res['network']['network_extension'] self.assertEqual("abc", val) # Test update. data = {'network': {'network_extension': "def"}} res = self._update('networks', net_id, data) val = res['network']['network_extension'] self.assertEqual("def", val) res = self._show('networks', net_id) val = res['network']['network_extension'] self.assertEqual("def", val) def test_subnet_attr(self): with self.subnet() as subnet: # Test create with default value. net_id = subnet['subnet']['id'] val = subnet['subnet']['subnet_extension'] self.assertEqual("", val) res = self._show('subnets', net_id) val = res['subnet']['subnet_extension'] self.assertEqual("", val) # Test list. res = self._list('subnets') val = res['subnets'][0]['subnet_extension'] self.assertEqual("", val) with self.network() as network: # Test create with explicit value. data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.1.0.0/24', 'ip_version': '4', 'tenant_id': self._tenant_id, 'subnet_extension': 'abc'}} req = self.new_create_request('subnets', data, self.fmt) res = req.get_response(self.api) subnet = self.deserialize(self.fmt, res) subnet_id = subnet['subnet']['id'] val = subnet['subnet']['subnet_extension'] self.assertEqual("abc", val) res = self._show('subnets', subnet_id) val = res['subnet']['subnet_extension'] self.assertEqual("abc", val) # Test update. data = {'subnet': {'subnet_extension': "def"}} res = self._update('subnets', subnet_id, data) val = res['subnet']['subnet_extension'] self.assertEqual("def", val) res = self._show('subnets', subnet_id) val = res['subnet']['subnet_extension'] self.assertEqual("def", val) def test_port_attr(self): with self.port() as port: # Test create with default value. net_id = port['port']['id'] val = port['port']['port_extension'] self.assertEqual("", val) res = self._show('ports', net_id) val = res['port']['port_extension'] self.assertEqual("", val) # Test list. res = self._list('ports') val = res['ports'][0]['port_extension'] self.assertEqual("", val) with self.network() as network: # Test create with explicit value. res = self._create_port(self.fmt, network['network']['id'], arg_list=('port_extension', ), port_extension="abc") port = self.deserialize(self.fmt, res) port_id = port['port']['id'] val = port['port']['port_extension'] self.assertEqual("abc", val) res = self._show('ports', port_id) val = res['port']['port_extension'] self.assertEqual("abc", val) # Test update. data = {'port': {'port_extension': "def"}} res = self._update('ports', port_id, data) val = res['port']['port_extension'] self.assertEqual("def", val) res = self._show('ports', port_id) val = res['port']['port_extension'] self.assertEqual("def", val) neutron-12.1.1/neutron/tests/unit/plugins/ml2/test_ext_portsecurity.py0000664000175000017500000000540113553660046026353 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import port_security as psec from neutron_lib import context from neutron_lib.plugins import directory from oslo_config import cfg from neutron.tests.unit.extensions import test_portsecurity as test_psec from neutron.tests.unit.plugins.ml2 import test_plugin class PSExtDriverTestCase(test_plugin.Ml2PluginV2TestCase, test_psec.TestPortSecurity): _extension_drivers = ['port_security'] def setUp(self): cfg.CONF.set_override('extension_drivers', self._extension_drivers, group='ml2') super(PSExtDriverTestCase, self).setUp() def test_create_net_port_security_default(self): _core_plugin = directory.get_plugin() admin_ctx = context.get_admin_context() args = {'network': {'name': 'test', 'tenant_id': '', 'shared': False, 'admin_state_up': True, 'status': 'ACTIVE'}} try: network = _core_plugin.create_network(admin_ctx, args) _value = network[psec.PORTSECURITY] finally: if network: _core_plugin.delete_network(admin_ctx, network['id']) self.assertEqual(psec.DEFAULT_PORT_SECURITY, _value) def test_create_port_with_secgroup_none_and_port_security_false(self): if self._skip_security_group: self.skipTest("Plugin does not support security groups") with self.network() as net: with self.subnet(network=net): res = self._create_port('json', net['network']['id'], arg_list=('security_groups', 'port_security_enabled'), security_groups=[], port_security_enabled=False) self.assertEqual(201, res.status_int) port = self.deserialize('json', res) self.assertFalse(port['port'][psec.PORTSECURITY]) self.assertEqual([], port['port']['security_groups']) neutron-12.1.1/neutron/tests/unit/plugins/ml2/test_db.py0000664000175000017500000004475313553660047023322 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation, all rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import warnings import mock import netaddr from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib import context from neutron_lib.plugins.ml2 import api from oslo_utils import uuidutils from sqlalchemy.orm import exc from sqlalchemy.orm import query from neutron.db import api as db_api from neutron.db import db_base_plugin_v2 from neutron.db.models import l3 as l3_models from neutron.db import models_v2 from neutron.db import segments_db from neutron.objects import network as network_obj from neutron.objects import ports as port_obj from neutron.plugins.ml2 import db as ml2_db from neutron.plugins.ml2 import models from neutron.tests.unit import testlib_api PLUGIN_NAME = 'ml2' class Ml2DBTestCase(testlib_api.SqlTestCase): def setUp(self): super(Ml2DBTestCase, self).setUp() self.ctx = context.get_admin_context() self.setup_coreplugin(PLUGIN_NAME) def _setup_neutron_network(self, network_id): network_obj.Network(self.ctx, id=network_id).create() def _setup_neutron_port(self, network_id, port_id): mac_address = db_base_plugin_v2.NeutronDbPluginV2._generate_mac() port = port_obj.Port(self.ctx, id=port_id, network_id=network_id, mac_address=netaddr.EUI(mac_address), admin_state_up=True, status='DOWN', device_id='', device_owner='') port.create() return port def _setup_neutron_portbinding(self, port_id, vif_type, host): with db_api.context_manager.writer.using(self.ctx): self.ctx.session.add(models.PortBinding(port_id=port_id, vif_type=vif_type, host=host)) @staticmethod def _sort_segments(segments): return sorted(segments, key=lambda d: d['segmentation_id']) def _create_segments(self, segments, is_seg_dynamic=False, network_id=uuidutils.generate_uuid()): self._setup_neutron_network(network_id) for segment in segments: segments_db.add_network_segment( self.ctx, network_id, segment, is_dynamic=is_seg_dynamic) segment['network_id'] = network_id net_segments = segments_db.get_network_segments( self.ctx, network_id, filter_dynamic=is_seg_dynamic) net_segments = self._sort_segments(net_segments) for segment_index, segment in enumerate(segments): self.assertEqual(segment, net_segments[segment_index]) return net_segments def test_network_segments_for_provider_network(self): segment = {api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'physnet1', api.SEGMENTATION_ID: 1} self._create_segments([segment]) def test_network_segments_is_dynamic_true(self): segment = {api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'physnet1', api.SEGMENTATION_ID: 1} self._create_segments([segment], is_seg_dynamic=True) def test_network_segments_for_multiprovider_network(self): segments = [{api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'physnet1', api.SEGMENTATION_ID: 1}, {api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'physnet1', api.SEGMENTATION_ID: 2}] self._create_segments(segments) def test_get_networks_segments(self): net_id1 = uuidutils.generate_uuid() net_id2 = uuidutils.generate_uuid() segments1 = [{api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'physnet1', api.SEGMENTATION_ID: 1}, {api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'physnet1', api.SEGMENTATION_ID: 2}] segments2 = [{api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'physnet1', api.SEGMENTATION_ID: 3}, {api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'physnet1', api.SEGMENTATION_ID: 4}] net1segs = self._create_segments(segments1, network_id=net_id1) net2segs = self._create_segments(segments2, network_id=net_id2) segs = segments_db.get_networks_segments( self.ctx, [net_id1, net_id2]) self.assertEqual(net1segs, self._sort_segments(segs[net_id1])) self.assertEqual(net2segs, self._sort_segments(segs[net_id2])) def test_get_networks_segments_no_segments(self): net_id1 = uuidutils.generate_uuid() net_id2 = uuidutils.generate_uuid() self._create_segments([], network_id=net_id1) self._create_segments([], network_id=net_id2) segs = segments_db.get_networks_segments( self.ctx, [net_id1, net_id2]) self.assertEqual([], segs[net_id1]) self.assertEqual([], segs[net_id2]) def test_get_segment_by_id(self): segment = {api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'physnet1', api.SEGMENTATION_ID: 1} net_segment = self._create_segments([segment])[0] segment_uuid = net_segment[api.ID] net_segment = segments_db.get_segment_by_id(self.ctx, segment_uuid) self.assertEqual(segment, net_segment) def test_get_segment_by_id_result_not_found(self): segment_uuid = uuidutils.generate_uuid() net_segment = segments_db.get_segment_by_id(self.ctx, segment_uuid) self.assertIsNone(net_segment) def test_delete_network_segment(self): segment = {api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'physnet1', api.SEGMENTATION_ID: 1} net_segment = self._create_segments([segment])[0] segment_uuid = net_segment[api.ID] segments_db.delete_network_segment(self.ctx, segment_uuid) # Get segment and verify its empty net_segment = segments_db.get_segment_by_id(self.ctx, segment_uuid) self.assertIsNone(net_segment) def test_get_dynamic_segment(self): net_id = uuidutils.generate_uuid() segment1 = {api.NETWORK_TYPE: 'vlan', api.PHYSICAL_NETWORK: 'physnet1', api.SEGMENTATION_ID: 1} self._create_segments( [segment1], is_seg_dynamic=True, network_id=net_id) segs1 = segments_db.get_dynamic_segment( self.ctx, net_id) self.assertEqual('vlan', segs1[api.NETWORK_TYPE]) self.assertEqual('physnet1', segs1[api.PHYSICAL_NETWORK]) self.assertEqual(1, segs1[api.SEGMENTATION_ID]) segs2 = segments_db.get_dynamic_segment( self.ctx, net_id, physical_network='physnet1') self.assertEqual('vlan', segs2[api.NETWORK_TYPE]) self.assertEqual('physnet1', segs2[api.PHYSICAL_NETWORK]) self.assertEqual(1, segs2[api.SEGMENTATION_ID]) segs3 = segments_db.get_dynamic_segment( self.ctx, net_id, segmentation_id=1) self.assertEqual('vlan', segs3[api.NETWORK_TYPE]) self.assertEqual('physnet1', segs3[api.PHYSICAL_NETWORK]) self.assertEqual(1, segs3[api.SEGMENTATION_ID]) def test_add_port_binding(self): network_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() self._setup_neutron_network(network_id) self._setup_neutron_port(network_id, port_id) port = ml2_db.add_port_binding(self.ctx, port_id) self.assertEqual(port_id, port.port_id) self.assertEqual(portbindings.VIF_TYPE_UNBOUND, port.vif_type) def test_get_port_binding_host(self): network_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() host = 'fake_host' vif_type = portbindings.VIF_TYPE_UNBOUND self._setup_neutron_network(network_id) self._setup_neutron_port(network_id, port_id) self._setup_neutron_portbinding(port_id, vif_type, host) port_host = ml2_db.get_port_binding_host(self.ctx, port_id) self.assertEqual(host, port_host) def test_get_port_binding_host_multiple_results_found(self): network_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() port_id_one = uuidutils.generate_uuid() port_id_two = uuidutils.generate_uuid() # NOTE(manjeets) to check startswith testcase we # need port ids with same prefix port_id_one = port_id[:8] + port_id_one[8:] port_id_two = port_id[:8] + port_id_two[8:] host = 'fake_host' vif_type = portbindings.VIF_TYPE_UNBOUND self._setup_neutron_network(network_id) self._setup_neutron_port(network_id, port_id_one) self._setup_neutron_portbinding(port_id_one, vif_type, host) self._setup_neutron_port(network_id, port_id_two) self._setup_neutron_portbinding(port_id_two, vif_type, host) port_host = ml2_db.get_port_binding_host(self.ctx, port_id[:8]) self.assertIsNone(port_host) def test_get_port_binding_host_result_not_found(self): port_id = uuidutils.generate_uuid() port_host = ml2_db.get_port_binding_host(self.ctx, port_id) self.assertIsNone(port_host) def test_get_port(self): network_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() self._setup_neutron_network(network_id) self._setup_neutron_port(network_id, port_id) port = ml2_db.get_port(self.ctx, port_id) self.assertEqual(port_id, port.id) def test_get_port_multiple_results_found(self): with mock.patch( 'sqlalchemy.orm.query.Query.one', side_effect=exc.MultipleResultsFound): port = ml2_db.get_port(self.ctx, 'unused') self.assertIsNone(port) def test_get_port_result_not_found(self): port_id = uuidutils.generate_uuid() port = ml2_db.get_port(self.ctx, port_id) self.assertIsNone(port) def test_get_port_from_device_mac(self): network_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() self._setup_neutron_network(network_id) port = self._setup_neutron_port(network_id, port_id) observed_port = ml2_db.get_port_from_device_mac(self.ctx, port['mac_address']) self.assertEqual(port_id, observed_port.id) class Ml2DvrDBTestCase(testlib_api.SqlTestCase): def setUp(self): super(Ml2DvrDBTestCase, self).setUp() self.ctx = context.get_admin_context() self.setup_coreplugin(PLUGIN_NAME) def _setup_neutron_network(self, network_id, port_ids): with db_api.context_manager.writer.using(self.ctx): network_obj.Network(self.ctx, id=network_id).create() ports = [] for port_id in port_ids: mac_address = (db_base_plugin_v2.NeutronDbPluginV2. _generate_mac()) port = port_obj.Port(self.ctx, id=port_id, network_id=network_id, mac_address=netaddr.EUI(mac_address), admin_state_up=True, status='ACTIVE', device_id='', device_owner='') port.create() ports.append(port) return ports def _setup_neutron_router(self): with self.ctx.session.begin(subtransactions=True): router = l3_models.Router() self.ctx.session.add(router) return router def _setup_distributed_binding(self, network_id, port_id, router_id, host_id): with db_api.context_manager.writer.using(self.ctx): record = models.DistributedPortBinding( port_id=port_id, host=host_id, router_id=router_id, vif_type=portbindings.VIF_TYPE_UNBOUND, vnic_type=portbindings.VNIC_NORMAL, status='DOWN') self.ctx.session.add(record) return record def test_ensure_distributed_port_binding_deals_with_db_duplicate(self): network_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() router_id = 'foo_router_id' host_id = 'foo_host_id' self._setup_neutron_network(network_id, [port_id]) self._setup_distributed_binding(network_id, port_id, router_id, host_id) with mock.patch.object(query.Query, 'first') as query_first: query_first.return_value = [] with mock.patch.object(ml2_db.LOG, 'debug') as log_trace: binding = ml2_db.ensure_distributed_port_binding( self.ctx, port_id, host_id, router_id) self.assertTrue(query_first.called) self.assertTrue(log_trace.called) self.assertEqual(port_id, binding.port_id) def test_ensure_distributed_port_binding(self): network_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() self._setup_neutron_network(network_id, [port_id]) router = self._setup_neutron_router() ml2_db.ensure_distributed_port_binding( self.ctx, port_id, 'foo_host', router.id) expected = (self.ctx.session.query(models.DistributedPortBinding). filter_by(port_id=port_id).one()) self.assertEqual(port_id, expected.port_id) def test_ensure_distributed_port_binding_multiple_bindings(self): network_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() self._setup_neutron_network(network_id, [port_id]) router = self._setup_neutron_router() ml2_db.ensure_distributed_port_binding( self.ctx, port_id, 'foo_host_1', router.id) ml2_db.ensure_distributed_port_binding( self.ctx, port_id, 'foo_host_2', router.id) bindings = (self.ctx.session.query(models.DistributedPortBinding). filter_by(port_id=port_id).all()) self.assertEqual(2, len(bindings)) def test_delete_distributed_port_binding_if_stale(self): network_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() self._setup_neutron_network(network_id, [port_id]) binding = self._setup_distributed_binding( network_id, port_id, None, 'foo_host_id') ml2_db.delete_distributed_port_binding_if_stale(self.ctx, binding) count = (self.ctx.session.query(models.DistributedPortBinding). filter_by(port_id=binding.port_id).count()) self.assertFalse(count) def test_get_distributed_port_binding_by_host_not_found(self): port = ml2_db.get_distributed_port_binding_by_host( self.ctx, 'foo_port_id', 'foo_host_id') self.assertIsNone(port) def test_get_distributed_port_bindings_not_found(self): port = ml2_db.get_distributed_port_bindings(self.ctx, 'foo_port_id') self.assertFalse(len(port)) def test_get_distributed_port_bindings(self): network_id = uuidutils.generate_uuid() port_id_1 = uuidutils.generate_uuid() port_id_2 = uuidutils.generate_uuid() self._setup_neutron_network(network_id, [port_id_1, port_id_2]) router = self._setup_neutron_router() self._setup_distributed_binding( network_id, port_id_1, router.id, 'foo_host_id_1') self._setup_distributed_binding( network_id, port_id_1, router.id, 'foo_host_id_2') ports = ml2_db.get_distributed_port_bindings(self.ctx, port_id_1) self.assertEqual(2, len(ports)) def test_distributed_port_binding_deleted_by_port_deletion(self): network_id = uuidutils.generate_uuid() network_obj.Network(self.ctx, id=network_id).create() with db_api.context_manager.writer.using(self.ctx): device_owner = constants.DEVICE_OWNER_DVR_INTERFACE port = models_v2.Port( id='port_id', network_id=network_id, mac_address='00:11:22:33:44:55', admin_state_up=True, status=constants.PORT_STATUS_ACTIVE, device_id='device_id', device_owner=device_owner) self.ctx.session.add(port) binding_kwarg = { 'port_id': 'port_id', 'host': 'host', 'vif_type': portbindings.VIF_TYPE_UNBOUND, 'vnic_type': portbindings.VNIC_NORMAL, 'router_id': 'router_id', 'status': constants.PORT_STATUS_DOWN } self.ctx.session.add(models.DistributedPortBinding( **binding_kwarg)) binding_kwarg['host'] = 'another-host' self.ctx.session.add(models.DistributedPortBinding( **binding_kwarg)) with warnings.catch_warnings(record=True) as warning_list: with db_api.context_manager.writer.using(self.ctx): self.ctx.session.delete(port) self.assertEqual( [], warning_list, 'Warnings: %s' % ';'.join([str(w) for w in warning_list])) ports = ml2_db.get_distributed_port_bindings(self.ctx, 'port_id') self.assertEqual(0, len(ports)) neutron-12.1.1/neutron/tests/unit/plugins/ml2/test_plugin.py0000664000175000017500000043075113553660047024230 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import fixtures import mock import netaddr from neutron_lib.agent import constants as agent_consts from neutron_lib.api.definitions import availability_zone as az_def from neutron_lib.api.definitions import external_net as extnet_apidef from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import provider_net as pnet from neutron_lib.callbacks import events from neutron_lib.callbacks import exceptions as c_exc from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions as exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from neutron_lib.plugins.ml2 import api as driver_api from oslo_config import cfg from oslo_db import exception as db_exc from oslo_utils import uuidutils import testtools import webob from neutron._i18n import _ from neutron.common import utils from neutron.db import agents_db from neutron.db import api as db_api from neutron.db import models_v2 from neutron.db import provisioning_blocks from neutron.db import segments_db from neutron.extensions import multiprovidernet as mpnet from neutron.objects import base as base_obj from neutron.objects import ports as port_obj from neutron.objects import router as l3_obj from neutron.plugins.ml2.common import exceptions as ml2_exc from neutron.plugins.ml2 import db as ml2_db from neutron.plugins.ml2 import driver_context from neutron.plugins.ml2.drivers import type_vlan from neutron.plugins.ml2 import managers from neutron.plugins.ml2 import models from neutron.plugins.ml2 import plugin as ml2_plugin from neutron.services.revisions import revision_plugin from neutron.services.segments import db as segments_plugin_db from neutron.services.segments import plugin as segments_plugin from neutron.tests.common import helpers from neutron.tests.unit import _test_extension_portbindings as test_bindings from neutron.tests.unit.agent import test_securitygroups_rpc as test_sg_rpc from neutron.tests.unit.db import test_allowedaddresspairs_db as test_pair from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin from neutron.tests.unit.db import test_ipam_pluggable_backend as test_ipam from neutron.tests.unit.extensions import test_extra_dhcp_opt as test_dhcpopts from neutron.tests.unit.plugins.ml2.drivers import mechanism_logger as \ mech_logger from neutron.tests.unit.plugins.ml2.drivers import mechanism_test as mech_test cfg.CONF.import_opt('network_vlan_ranges', 'neutron.plugins.ml2.drivers.type_vlan', group='ml2_type_vlan') PLUGIN_NAME = 'ml2' DEVICE_OWNER_COMPUTE = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' HOST = 'fake_host' TEST_ROUTER_ID = 'router_id' # TODO(marun) - Move to somewhere common for reuse class PluginConfFixture(fixtures.Fixture): """Plugin configuration shared across the unit and functional tests.""" def __init__(self, plugin_name, parent_setup=None): super(PluginConfFixture, self).__init__() self.plugin_name = plugin_name self.parent_setup = parent_setup def _setUp(self): if self.parent_setup: self.parent_setup() class Ml2ConfFixture(PluginConfFixture): def __init__(self, parent_setup=None): super(Ml2ConfFixture, self).__init__(PLUGIN_NAME, parent_setup) class Ml2PluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase): _mechanism_drivers = ['logger', 'test'] l3_plugin = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatServicePlugin') def get_additional_service_plugins(self): """Subclasses can return a dictionary of service plugins to load.""" return {} def setup_parent(self): """Perform parent setup with the common plugin configuration class.""" service_plugins = {'l3_plugin_name': self.l3_plugin} service_plugins.update(self.get_additional_service_plugins()) # Ensure that the parent setup can be called without arguments # by the common configuration setUp. parent_setup = functools.partial( super(Ml2PluginV2TestCase, self).setUp, plugin=PLUGIN_NAME, service_plugins=service_plugins, ) self.useFixture(Ml2ConfFixture(parent_setup)) self.port_create_status = 'DOWN' def setUp(self): self.ovo_push_interface_p = mock.patch( 'neutron.plugins.ml2.ovo_rpc.OVOServerRpcInterface') self.ovo_push_interface_p.start() # Enable the test mechanism driver to ensure that # we can successfully call through to all mechanism # driver apis. cfg.CONF.set_override('mechanism_drivers', self._mechanism_drivers, group='ml2') self.physnet = 'physnet1' self.vlan_range = '1:100' self.vlan_range2 = '200:300' self.physnet2 = 'physnet2' self.phys_vrange = ':'.join([self.physnet, self.vlan_range]) self.phys2_vrange = ':'.join([self.physnet2, self.vlan_range2]) cfg.CONF.set_override('network_vlan_ranges', [self.phys_vrange, self.phys2_vrange], group='ml2_type_vlan') self.setup_parent() self.driver = directory.get_plugin() self.context = context.get_admin_context() class TestMl2BulkToggleWithoutBulkless(Ml2PluginV2TestCase): _mechanism_drivers = ['logger', 'test'] def test_bulk_enabled_with_bulk_drivers(self): self.assertFalse(self._skip_native_bulk) class TestMl2BasicGet(test_plugin.TestBasicGet, Ml2PluginV2TestCase): pass class TestMl2V2HTTPResponse(test_plugin.TestV2HTTPResponse, Ml2PluginV2TestCase): pass class TestMl2NetworksV2(test_plugin.TestNetworksV2, Ml2PluginV2TestCase): def setUp(self, plugin=None): super(TestMl2NetworksV2, self).setUp() # provider networks self.pnets = [{'name': 'net1', pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1, 'tenant_id': 'tenant_one'}, {'name': 'net2', pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet2', pnet.SEGMENTATION_ID: 210, 'tenant_id': 'tenant_one'}, {'name': 'net3', pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet2', pnet.SEGMENTATION_ID: 220, 'tenant_id': 'tenant_one'} ] # multiprovider networks self.mp_nets = [{'name': 'net4', mpnet.SEGMENTS: [{pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet2', pnet.SEGMENTATION_ID: 1}, {pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet2', pnet.SEGMENTATION_ID: 202}], 'tenant_id': 'tenant_one'} ] self.nets = self.mp_nets + self.pnets def test_network_after_create_callback(self): after_create = mock.Mock() registry.subscribe(after_create, resources.NETWORK, events.AFTER_CREATE) with self.network() as n: after_create.assert_called_once_with( resources.NETWORK, events.AFTER_CREATE, mock.ANY, context=mock.ANY, network=mock.ANY) kwargs = after_create.mock_calls[0][2] self.assertEqual(n['network']['id'], kwargs['network']['id']) def test_network_precommit_create_callback(self): precommit_create = mock.Mock() registry.subscribe(precommit_create, resources.NETWORK, events.PRECOMMIT_CREATE) with self.network(): precommit_create.assert_called_once_with( resources.NETWORK, events.PRECOMMIT_CREATE, mock.ANY, context=mock.ANY, network=mock.ANY, request=mock.ANY) def test_network_precommit_create_callback_aborts(self): precommit_create = mock.Mock() registry.subscribe(precommit_create, resources.NETWORK, events.PRECOMMIT_CREATE) precommit_create.side_effect = exc.InvalidInput(error_message='x') data = {'network': {'tenant_id': 'sometenant', 'name': 'dummy', 'admin_state_up': True, 'shared': False}} req = self.new_create_request('networks', data) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_network_precommit_update_includes_req(self): precommit_update = mock.Mock() registry.subscribe(precommit_update, resources.NETWORK, events.PRECOMMIT_UPDATE) with self.network() as n: data = {'network': {'name': 'updated'}} req = self.new_update_request('networks', data, n['network']['id']) self.deserialize(self.fmt, req.get_response(self.api)) precommit_update.assert_called_once_with( resources.NETWORK, events.PRECOMMIT_UPDATE, mock.ANY, payload=mock.ANY) self.assertEqual( 'updated', precommit_update.call_args[1]['payload'].desired_state['name']) def test_network_after_update_callback(self): after_update = mock.Mock() registry.subscribe(after_update, resources.NETWORK, events.AFTER_UPDATE) with self.network() as n: data = {'network': {'name': 'updated'}} req = self.new_update_request('networks', data, n['network']['id']) self.deserialize(self.fmt, req.get_response(self.api)) after_update.assert_called_once_with( resources.NETWORK, events.AFTER_UPDATE, mock.ANY, context=mock.ANY, network=mock.ANY, original_network=mock.ANY) kwargs = after_update.mock_calls[0][2] self.assertEqual(n['network']['name'], kwargs['original_network']['name']) self.assertEqual('updated', kwargs['network']['name']) def test_network_after_delete_callback(self): after_delete = mock.Mock() registry.subscribe(after_delete, resources.NETWORK, events.AFTER_DELETE) with self.network() as n: req = self.new_delete_request('networks', n['network']['id']) req.get_response(self.api) after_delete.assert_called_once_with( resources.NETWORK, events.AFTER_DELETE, mock.ANY, context=mock.ANY, network=mock.ANY) kwargs = after_delete.mock_calls[0][2] self.assertEqual(n['network']['id'], kwargs['network']['id']) def test_bulk_network_before_and_after_events_outside_of_txn(self): # capture session states during each before and after event before = [] after = [] b_func = lambda *a, **k: before.append(k['context'].session.is_active) a_func = lambda *a, **k: after.append(k['context'].session.is_active) registry.subscribe(b_func, resources.NETWORK, events.BEFORE_CREATE) registry.subscribe(a_func, resources.NETWORK, events.AFTER_CREATE) data = [{'tenant_id': self._tenant_id}] * 4 self._create_bulk_from_list( self.fmt, 'network', data, context=context.get_admin_context()) # ensure events captured self.assertTrue(before) self.assertTrue(after) # ensure session was closed for all self.assertFalse(any(before)) self.assertFalse(any(after)) def _create_and_verify_networks(self, networks): for net_idx, net in enumerate(networks): # create req = self.new_create_request('networks', {'network': net}) # verify network = self.deserialize(self.fmt, req.get_response(self.api))['network'] if mpnet.SEGMENTS not in net: for k, v in net.items(): self.assertEqual(net[k], network[k]) self.assertNotIn(mpnet.SEGMENTS, network) else: segments = network[mpnet.SEGMENTS] expected_segments = net[mpnet.SEGMENTS] self.assertEqual(len(expected_segments), len(segments)) for expected, actual in zip(expected_segments, segments): self.assertEqual(expected, actual) def _lookup_network_by_segmentation_id(self, seg_id, num_expected_nets): params_str = "%s=%s" % (pnet.SEGMENTATION_ID, seg_id) net_req = self.new_list_request('networks', None, params=params_str) networks = self.deserialize(self.fmt, net_req.get_response(self.api)) if num_expected_nets: self.assertIsNotNone(networks) self.assertEqual(num_expected_nets, len(networks['networks'])) else: self.assertIsNone(networks) return networks def test_list_networks_with_segmentation_id(self): self._create_and_verify_networks(self.pnets) # verify we can find the network that we expect lookup_vlan_id = 1 expected_net = [n for n in self.pnets if n[pnet.SEGMENTATION_ID] == lookup_vlan_id].pop() networks = self._lookup_network_by_segmentation_id(lookup_vlan_id, 1) # verify all provider attributes network = networks['networks'][0] for attr in pnet.ATTRIBUTES: self.assertEqual(expected_net[attr], network[attr]) def test_list_mpnetworks_with_segmentation_id(self): self._create_and_verify_networks(self.nets) # get all networks with seg_id=1 (including multisegment networks) lookup_vlan_id = 1 networks = self._lookup_network_by_segmentation_id(lookup_vlan_id, 2) # get the mpnet networks = [n for n in networks['networks'] if mpnet.SEGMENTS in n] network = networks.pop() # verify attributes of the looked up item segments = network[mpnet.SEGMENTS] expected_segments = self.mp_nets[0][mpnet.SEGMENTS] self.assertEqual(len(expected_segments), len(segments)) for expected, actual in zip(expected_segments, segments): self.assertEqual(expected, actual) def test_create_network_segment_allocation_fails(self): plugin = directory.get_plugin() mock.patch.object(db_api._retry_db_errors, 'max_retries', new=2).start() with mock.patch.object( plugin.type_manager, 'create_network_segments', side_effect=db_exc.RetryRequest(ValueError()) ) as f: data = {'network': {'tenant_id': 'sometenant', 'name': 'dummy', 'admin_state_up': True, 'shared': False}} req = self.new_create_request('networks', data) res = req.get_response(self.api) self.assertEqual(500, res.status_int) # 1 + retry count self.assertEqual(3, f.call_count) class TestExternalNetwork(Ml2PluginV2TestCase): def _create_external_network(self): data = {'network': {'name': 'net1', 'router:external': 'True', 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) network = self.deserialize(self.fmt, network_req.get_response(self.api)) return network def test_external_network_type_none(self): cfg.CONF.set_default('external_network_type', None, group='ml2') network = self._create_external_network() # For external network, expected network type to be # tenant_network_types which is by default 'local'. self.assertEqual(constants.TYPE_LOCAL, network['network'][pnet.NETWORK_TYPE]) # No physical network specified, expected 'None'. self.assertIsNone(network['network'][pnet.PHYSICAL_NETWORK]) # External network will not have a segmentation id. self.assertIsNone(network['network'][pnet.SEGMENTATION_ID]) # External network will not have multiple segments. self.assertNotIn(mpnet.SEGMENTS, network['network']) def test_external_network_type_vlan(self): cfg.CONF.set_default('external_network_type', constants.TYPE_VLAN, group='ml2') network = self._create_external_network() # For external network, expected network type to be 'vlan'. self.assertEqual(constants.TYPE_VLAN, network['network'][pnet.NETWORK_TYPE]) # Physical network is expected. self.assertIsNotNone(network['network'][pnet.PHYSICAL_NETWORK]) # External network will have a segmentation id. self.assertIsNotNone(network['network'][pnet.SEGMENTATION_ID]) # External network will not have multiple segments. self.assertNotIn(mpnet.SEGMENTS, network['network']) class TestMl2NetworksWithVlanTransparencyBase(TestMl2NetworksV2): data = {'network': {'name': 'net1', mpnet.SEGMENTS: [{pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1'}], 'tenant_id': 'tenant_one', 'vlan_transparent': 'True'}} def setUp(self, plugin=None): cfg.CONF.set_override('vlan_transparent', True) super(TestMl2NetworksWithVlanTransparencyBase, self).setUp(plugin) class TestMl2NetworksWithVlanTransparency( TestMl2NetworksWithVlanTransparencyBase): _mechanism_drivers = ['test'] def test_create_network_vlan_transparent_fail(self): with mock.patch.object(mech_test.TestMechanismDriver, 'check_vlan_transparency', return_value=False): network_req = self.new_create_request('networks', self.data) res = network_req.get_response(self.api) self.assertEqual(500, res.status_int) error_result = self.deserialize(self.fmt, res)['NeutronError'] self.assertEqual("VlanTransparencyDriverError", error_result['type']) def test_create_network_vlan_transparent(self): with mock.patch.object(mech_test.TestMechanismDriver, 'check_vlan_transparency', return_value=True): network_req = self.new_create_request('networks', self.data) res = network_req.get_response(self.api) self.assertEqual(201, res.status_int) network = self.deserialize(self.fmt, res)['network'] self.assertIn('vlan_transparent', network) class TestMl2NetworksWithVlanTransparencyAndMTU( TestMl2NetworksWithVlanTransparencyBase): _mechanism_drivers = ['test'] def test_create_network_vlan_transparent_and_mtu(self): with mock.patch.object(mech_test.TestMechanismDriver, 'check_vlan_transparency', return_value=True): cfg.CONF.set_override('path_mtu', 1000, group='ml2') cfg.CONF.set_override('global_physnet_mtu', 1000) network_req = self.new_create_request('networks', self.data) res = network_req.get_response(self.api) self.assertEqual(201, res.status_int) network = self.deserialize(self.fmt, res)['network'] self.assertEqual(1000, network['mtu']) self.assertIn('vlan_transparent', network) self.assertTrue(network['vlan_transparent']) self.assertTrue(network['vlan_transparent']) class TestMl2NetworksWithAvailabilityZone(TestMl2NetworksV2): def test_create_network_availability_zone(self): az_hints = ['az1', 'az2'] data = {'network': {'name': 'net1', az_def.AZ_HINTS: az_hints, 'tenant_id': 'tenant_one'}} with mock.patch.object(agents_db.AgentAvailabilityZoneMixin, 'validate_availability_zones'): network_req = self.new_create_request('networks', data) res = network_req.get_response(self.api) self.assertEqual(201, res.status_int) network = self.deserialize(self.fmt, res)['network'] self.assertEqual(az_hints, network[az_def.AZ_HINTS]) class TestMl2SubnetsV2(test_plugin.TestSubnetsV2, Ml2PluginV2TestCase): def test_subnet_after_create_callback(self): after_create = mock.Mock() registry.subscribe(after_create, resources.SUBNET, events.AFTER_CREATE) with self.subnet() as s: after_create.assert_called_once_with( resources.SUBNET, events.AFTER_CREATE, mock.ANY, context=mock.ANY, subnet=mock.ANY) kwargs = after_create.mock_calls[0][2] self.assertEqual(s['subnet']['id'], kwargs['subnet']['id']) def test_port_update_subnetnotfound(self): with self.network() as n: with self.subnet(network=n, cidr='1.1.1.0/24') as s1,\ self.subnet(network=n, cidr='1.1.2.0/24') as s2,\ self.subnet(network=n, cidr='1.1.3.0/24') as s3: fixed_ips = [{'subnet_id': s1['subnet']['id']}, {'subnet_id': s2['subnet']['id']}, {'subnet_id': s3['subnet']['id']}] with self.port(subnet=s1, fixed_ips=fixed_ips, device_owner=constants.DEVICE_OWNER_DHCP) as p: plugin = directory.get_plugin() orig_update = plugin.update_port def delete_before_update(ctx, *args, **kwargs): # swap back out with original so only called once plugin.update_port = orig_update # delete s2 in the middle of s1 port_update plugin.delete_subnet(ctx, s2['subnet']['id']) return plugin.update_port(ctx, *args, **kwargs) plugin.update_port = delete_before_update req = self.new_delete_request('subnets', s1['subnet']['id']) res = req.get_response(self.api) self.assertEqual(204, res.status_int) # ensure port only has 1 IP on s3 port = self._show('ports', p['port']['id'])['port'] self.assertEqual(1, len(port['fixed_ips'])) self.assertEqual(s3['subnet']['id'], port['fixed_ips'][0]['subnet_id']) def test_subnet_after_update_callback(self): after_update = mock.Mock() registry.subscribe(after_update, resources.SUBNET, events.AFTER_UPDATE) with self.subnet() as s: data = {'subnet': {'name': 'updated'}} req = self.new_update_request('subnets', data, s['subnet']['id']) self.deserialize(self.fmt, req.get_response(self.api)) after_update.assert_called_once_with( resources.SUBNET, events.AFTER_UPDATE, mock.ANY, context=mock.ANY, subnet=mock.ANY, original_subnet=mock.ANY) kwargs = after_update.mock_calls[0][2] self.assertEqual(s['subnet']['name'], kwargs['original_subnet']['name']) self.assertEqual('updated', kwargs['subnet']['name']) def test_subnet_after_delete_callback(self): after_delete = mock.Mock() registry.subscribe(after_delete, resources.SUBNET, events.AFTER_DELETE) with self.subnet() as s: req = self.new_delete_request('subnets', s['subnet']['id']) req.get_response(self.api) after_delete.assert_called_once_with( resources.SUBNET, events.AFTER_DELETE, mock.ANY, context=mock.ANY, subnet=mock.ANY) kwargs = after_delete.mock_calls[0][2] self.assertEqual(s['subnet']['id'], kwargs['subnet']['id']) def test_delete_subnet_race_with_dhcp_port_creation(self): with self.network() as network: with self.subnet(network=network) as subnet: subnet_id = subnet['subnet']['id'] attempt = [0] def create_dhcp_port(*args, **kwargs): """A method to emulate race condition. Adds dhcp port in the middle of subnet delete """ if attempt[0] > 0: return False attempt[0] += 1 data = {'port': {'network_id': network['network']['id'], 'tenant_id': network['network']['tenant_id'], 'name': 'port1', 'admin_state_up': 1, 'device_id': '', 'device_owner': constants.DEVICE_OWNER_DHCP, 'fixed_ips': [{'subnet_id': subnet_id}]}} plugin = directory.get_plugin() plugin.create_port(context.get_admin_context(), data) # we mock _subnet_check_ip_allocations with method # that creates DHCP port 'in the middle' of subnet_delete # causing retry this way subnet is deleted on the # second attempt registry.subscribe(create_dhcp_port, resources.SUBNET, events.PRECOMMIT_DELETE) req = self.new_delete_request('subnets', subnet_id) res = req.get_response(self.api) self.assertEqual(204, res.status_int) self.assertEqual(1, attempt[0]) def test_create_subnet_check_mtu_in_mech_context(self): plugin = directory.get_plugin() plugin.mechanism_manager.create_subnet_precommit = mock.Mock() net_arg = {pnet.NETWORK_TYPE: 'vxlan', pnet.SEGMENTATION_ID: '1'} network = self._make_network(self.fmt, 'net1', True, arg_list=(pnet.NETWORK_TYPE, pnet.SEGMENTATION_ID,), **net_arg) with self.subnet(network=network): mock_subnet_pre = plugin.mechanism_manager.create_subnet_precommit observerd_mech_context = mock_subnet_pre.call_args_list[0][0][0] self.assertEqual(network['network']['mtu'], observerd_mech_context.network.current['mtu']) class TestMl2DbOperationBounds(test_plugin.DbOperationBoundMixin, Ml2PluginV2TestCase): """Test cases to assert constant query count for list operations. These test cases assert that an increase in the number of objects does not result in an increase of the number of db operations. All database lookups during a list operation should be performed in bulk so the number of queries required for 2 objects instead of 1 should stay the same. """ def setUp(self): super(TestMl2DbOperationBounds, self).setUp() self.kwargs = self.get_api_kwargs() def make_network(self): return self._make_network(self.fmt, 'name', True, **self.kwargs) def make_subnet(self): net = self.make_network() setattr(self, '_subnet_count', getattr(self, '_subnet_count', 0) + 1) cidr = '1.%s.0.0/24' % self._subnet_count return self._make_subnet(self.fmt, net, None, cidr, **self.kwargs) def make_port(self): net = self.make_network() return self._make_port(self.fmt, net['network']['id'], **self.kwargs) def test_network_list_queries_constant(self): self._assert_object_list_queries_constant(self.make_network, 'networks') def test_subnet_list_queries_constant(self): self._assert_object_list_queries_constant(self.make_subnet, 'subnets') def test_port_list_queries_constant(self): self._assert_object_list_queries_constant(self.make_port, 'ports') self._assert_object_list_queries_constant(self.make_port, 'ports', filters=['device_id']) self._assert_object_list_queries_constant(self.make_port, 'ports', filters=['device_id', 'device_owner']) self._assert_object_list_queries_constant(self.make_port, 'ports', filters=['tenant_id', 'name', 'device_id']) class TestMl2DbOperationBoundsTenant(TestMl2DbOperationBounds): admin = False class TestMl2DbOperationBoundsTenantRbac(TestMl2DbOperationBoundsTenant): def make_port_in_shared_network(self): context_ = self._get_context() # create shared network owned by the tenant; we use direct driver call # because default policy does not allow users to create shared networks net = self.driver.create_network( context.get_admin_context(), {'network': {'name': 'net1', 'tenant_id': context_.tenant, 'admin_state_up': True, 'shared': True}}) # create port that belongs to another tenant return self._make_port( self.fmt, net['id'], set_context=True, tenant_id='fake_tenant') def test_port_list_in_shared_network_queries_constant(self): self._assert_object_list_queries_constant( self.make_port_in_shared_network, 'ports') class TestMl2RevivedAgentsBindPorts(Ml2PluginV2TestCase): _mechanism_drivers = ['openvswitch', 'logger'] def _test__retry_binding_revived_agents(self, event, agent_status, admin_state_up, agent_type, ports, should_bind_ports): plugin = directory.get_plugin() context = mock.Mock() agent = { 'agent_status': agent_status, 'admin_state_up': admin_state_up, 'agent_type': agent_type} host = "test_host" for port in ports: port.binding = mock.MagicMock( vif_type=portbindings.VIF_TYPE_BINDING_FAILED, host=host) with mock.patch( 'neutron.objects.ports.Port.get_ports_by_binding_type_and_host', return_value=ports ) as get_ports_by_binding_type_and_host, mock.patch.object( plugin, 'get_network', return_value=mock.Mock() ) as get_network, mock.patch( 'neutron.plugins.ml2.db.get_binding_levels', return_value=None ) as get_binding_levels, mock.patch( 'neutron.plugins.ml2.driver_context.PortContext' ) as port_context, mock.patch.object( plugin, '_bind_port_if_needed' ) as bind_port_if_needed: plugin._retry_binding_revived_agents( resources.AGENT, event, plugin, **{'context': context, 'host': host, 'agent': agent}) if (agent_status == agent_consts.AGENT_ALIVE or not admin_state_up or agent_type not in plugin._rebind_on_revive_agent_types): get_ports_by_binding_type_and_host.assert_not_called() else: get_ports_by_binding_type_and_host.assert_called_once_with( context, portbindings.VIF_TYPE_BINDING_FAILED, host) if should_bind_ports: get_network_expected_calls = [ mock.call(context, port.network_id) for port in ports] get_network.assert_has_calls(get_network_expected_calls) get_binding_levels_expected_calls = [ mock.call(context, port.id, host) for port in ports] get_binding_levels.assert_has_calls( get_binding_levels_expected_calls) bind_port_if_needed.assert_called_once_with(port_context()) else: get_network.assert_not_called() get_binding_levels.assert_not_called() bind_port_if_needed.assert_not_called() def test__retry_binding_revived_agents(self): port = mock.MagicMock( id=uuidutils.generate_uuid()) self._test__retry_binding_revived_agents( events.AFTER_UPDATE, agent_consts.AGENT_REVIVED, True, constants.AGENT_TYPE_OVS, [port], should_bind_ports=True) def test__retry_binding_revived_agents_no_binding_failed_ports(self): self._test__retry_binding_revived_agents( events.AFTER_UPDATE, agent_consts.AGENT_REVIVED, True, constants.AGENT_TYPE_OVS, [], should_bind_ports=False) def test__retry_binding_revived_agents_alive_agent(self): port = mock.MagicMock( id=uuidutils.generate_uuid()) self._test__retry_binding_revived_agents( events.AFTER_UPDATE, agent_consts.AGENT_ALIVE, True, constants.AGENT_TYPE_OVS, [port], should_bind_ports=False) def test__retry_binding_revived_agents_not_binding_agent(self): port = mock.MagicMock( id=uuidutils.generate_uuid()) self._test__retry_binding_revived_agents( events.AFTER_UPDATE, agent_consts.AGENT_REVIVED, True, "Other agent which don't support binding", [port], should_bind_ports=False) def test__retry_binding_revived_agents_agent_admin_state_down(self): port = mock.MagicMock( id=uuidutils.generate_uuid()) self._test__retry_binding_revived_agents( events.AFTER_UPDATE, agent_consts.AGENT_REVIVED, False, constants.AGENT_TYPE_OVS, [port], should_bind_ports=False) class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase): def test__port_provisioned_with_blocks(self): plugin = directory.get_plugin() ups = mock.patch.object(plugin, 'update_port_status').start() with self.port() as port: mock.patch('neutron.plugins.ml2.plugin.db.get_port').start() provisioning_blocks.add_provisioning_component( self.context, port['port']['id'], 'port', 'DHCP') plugin._port_provisioned('port', 'evt', 'trigger', self.context, port['port']['id']) self.assertFalse(ups.called) def test__port_provisioned_no_binding(self): plugin = directory.get_plugin() with self.network() as net: net_id = net['network']['id'] port_id = 'fake_id' port_db = models_v2.Port( id=port_id, tenant_id='tenant', network_id=net_id, mac_address='08:00:01:02:03:04', admin_state_up=True, status='ACTIVE', device_id='vm_id', device_owner=DEVICE_OWNER_COMPUTE ) with db_api.context_manager.writer.using(self.context): self.context.session.add(port_db) self.assertIsNone(plugin._port_provisioned('port', 'evt', 'trigger', self.context, port_id)) def test__port_provisioned_port_admin_state_down(self): plugin = directory.get_plugin() ups = mock.patch.object(plugin, 'update_port_status').start() port_id = 'fake_port_id' binding = mock.Mock(vif_type=portbindings.VIF_TYPE_OVS) port = mock.Mock( id=port_id, admin_state_up=False, port_binding=binding) with mock.patch('neutron.plugins.ml2.plugin.db.get_port', return_value=port): plugin._port_provisioned('port', 'evt', 'trigger', self.context, port_id) self.assertFalse(ups.called) def test_port_after_create_outside_transaction(self): self.tx_open = True receive = lambda *a, **k: setattr(self, 'tx_open', k['context'].session.is_active) registry.subscribe(receive, resources.PORT, events.AFTER_CREATE) with self.port(): self.assertFalse(self.tx_open) def test_port_after_update_outside_transaction(self): self.tx_open = True receive = lambda *a, **k: setattr(self, 'tx_open', k['context'].session.is_active) with self.port() as p: registry.subscribe(receive, resources.PORT, events.AFTER_UPDATE) self._update('ports', p['port']['id'], {'port': {'name': 'update'}}) self.assertFalse(self.tx_open) def test_port_after_delete_outside_transaction(self): self.tx_open = True receive = lambda *a, **k: setattr(self, 'tx_open', k['context'].session.is_active) with self.port() as p: registry.subscribe(receive, resources.PORT, events.AFTER_DELETE) self._delete('ports', p['port']['id']) self.assertFalse(self.tx_open) def test_bulk_ports_before_and_after_events_outside_of_txn(self): with self.network() as n: pass # capture session states during each before and after event before = [] after = [] b_func = lambda *a, **k: before.append(k['context'].session.is_active) a_func = lambda *a, **k: after.append(k['context'].session.is_active) registry.subscribe(b_func, resources.PORT, events.BEFORE_CREATE) registry.subscribe(a_func, resources.PORT, events.AFTER_CREATE) data = [{'tenant_id': self._tenant_id, 'network_id': n['network']['id']}] * 4 self._create_bulk_from_list( self.fmt, 'port', data, context=context.get_admin_context()) # ensure events captured self.assertTrue(before) self.assertTrue(after) # ensure session was closed for all self.assertFalse(any(before)) self.assertFalse(any(after)) def test_create_router_port_and_fail_create_postcommit(self): with mock.patch.object(managers.MechanismManager, 'create_port_postcommit', side_effect=ml2_exc.MechanismDriverError( method='create_port_postcommit')): l3_plugin = directory.get_plugin(plugin_constants.L3) data = {'router': {'name': 'router', 'admin_state_up': True, 'tenant_id': 'fake_tenant'}} r = l3_plugin.create_router(self.context, data) with self.subnet() as s: data = {'subnet_id': s['subnet']['id']} self.assertRaises(ml2_exc.MechanismDriverError, l3_plugin.add_router_interface, self.context, r['id'], data) res_ports = self._list('ports')['ports'] self.assertEqual([], res_ports) def test_create_router_port_and_fail_bind_port_if_needed(self): with mock.patch.object(ml2_plugin.Ml2Plugin, '_bind_port_if_needed', side_effect=ml2_exc.MechanismDriverError( method='_bind_port_if_needed')): l3_plugin = directory.get_plugin(plugin_constants.L3) data = {'router': {'name': 'router', 'admin_state_up': True, 'tenant_id': 'fake_tenant'}} r = l3_plugin.create_router(self.context, data) with self.subnet() as s: data = {'subnet_id': s['subnet']['id']} self.assertRaises(ml2_exc.MechanismDriverError, l3_plugin.add_router_interface, self.context, r['id'], data) res_ports = self._list('ports')['ports'] self.assertEqual([], res_ports) def test_update_port_status_build(self): with self.port() as port: self.assertEqual('DOWN', port['port']['status']) self.assertEqual('DOWN', self.port_create_status) def test_notify_port_updated_for_status_change(self): ctx = context.get_admin_context() plugin = directory.get_plugin() with self.port() as port: with mock.patch.object(self.plugin, '_notify_port_updated') as notify_mock: port['port']['status'] = constants.PORT_STATUS_ACTIVE plugin.update_port(ctx, port['port']['id'], port) self.assertTrue(notify_mock.called) def test_update_port_status_short_id(self): ctx = context.get_admin_context() plugin = directory.get_plugin() with self.port() as port: with mock.patch.object(ml2_db, 'get_binding_levels', return_value=[]) as mock_gbl: port_id = port['port']['id'] short_id = port_id[:11] plugin.update_port_status(ctx, short_id, 'UP') mock_gbl.assert_called_once_with(mock.ANY, port_id, mock.ANY) def test_update_port_with_empty_data(self): ctx = context.get_admin_context() plugin = directory.get_plugin() with self.port() as port: port_id = port['port']['id'] new_port = plugin.update_port(ctx, port_id, {"port": {}}) self.assertEqual(port["port"], new_port) def _add_fake_dhcp_agent(self): agent = mock.Mock() plugin = directory.get_plugin() self.get_dhcp_mock = mock.patch.object( plugin, 'get_dhcp_agents_hosting_networks', return_value=[agent]).start() def test_dhcp_provisioning_blocks_inserted_on_create_with_agents(self): self._add_fake_dhcp_agent() with mock.patch.object(provisioning_blocks, 'add_provisioning_component') as ap: with self.port(): self.assertTrue(ap.called) def test_dhcp_provisioning_blocks_skipped_on_create_with_no_dhcp(self): self._add_fake_dhcp_agent() with self.subnet(enable_dhcp=False) as subnet: with mock.patch.object(provisioning_blocks, 'add_provisioning_component') as ap: with self.port(subnet=subnet): self.assertFalse(ap.called) def _test_dhcp_provisioning_blocks_inserted_on_update(self, update_dict, expected_block): ctx = context.get_admin_context() plugin = directory.get_plugin() self._add_fake_dhcp_agent() with self.port() as port: with mock.patch.object(provisioning_blocks, 'add_provisioning_component') as ap: port['port'].update(update_dict) plugin.update_port(ctx, port['port']['id'], port) self.assertEqual(expected_block, ap.called) def test_dhcp_provisioning_blocks_not_inserted_on_no_addr_change(self): update = {'binding:host_id': 'newhost'} self._test_dhcp_provisioning_blocks_inserted_on_update(update, False) def test_dhcp_provisioning_blocks_inserted_on_addr_change(self): update = {'binding:host_id': 'newhost', 'mac_address': '11:22:33:44:55:66'} self._test_dhcp_provisioning_blocks_inserted_on_update(update, True) def test_dhcp_provisioning_blocks_removed_without_dhcp_agents(self): with mock.patch.object(provisioning_blocks, 'remove_provisioning_component') as cp: with self.port(): self.assertTrue(cp.called) def test_create_update_get_port_same_fixed_ips_order(self): ctx = context.get_admin_context() plugin = directory.get_plugin() initial_fixed_ips = [{'ip_address': '10.0.0.5'}, {'ip_address': '10.0.0.7'}, {'ip_address': '10.0.0.6'}] with self.port(fixed_ips=initial_fixed_ips) as port: show = plugin.get_port(ctx, port['port']['id']) self.assertEqual(port['port']['fixed_ips'], show['fixed_ips']) new_fixed_ips = list(reversed(initial_fixed_ips)) port['port']['fixed_ips'] = new_fixed_ips updated = plugin.update_port(ctx, port['port']['id'], port) self.assertEqual(show['fixed_ips'], updated['fixed_ips']) updated = plugin.get_port(ctx, port['port']['id']) self.assertEqual(show['fixed_ips'], updated['fixed_ips']) def test_update_port_fixed_ip_changed(self): ctx = context.get_admin_context() plugin = directory.get_plugin() fixed_ip_data = [{'ip_address': '10.0.0.4'}] with self.port(fixed_ips=fixed_ip_data) as port,\ mock.patch.object( plugin.notifier, 'security_groups_member_updated') as sg_member_update: port['port']['fixed_ips'][0]['ip_address'] = '10.0.0.3' plugin.update_port(ctx, port['port']['id'], port) self.assertTrue(sg_member_update.called) def test_update_port_status_with_network(self): registry.clear() # don't care about callback behavior ctx = context.get_admin_context() plugin = directory.get_plugin() with self.port() as port: net = plugin.get_network(ctx, port['port']['network_id']) with mock.patch.object(plugin, 'get_networks') as get_nets: plugin.update_port_status(ctx, port['port']['id'], 'UP', network=net) self.assertFalse(get_nets.called) def test_update_port_mac(self): self.check_update_port_mac( host_arg={portbindings.HOST_ID: HOST}, arg_list=(portbindings.HOST_ID,)) def test_update_non_existent_port(self): ctx = context.get_admin_context() plugin = directory.get_plugin() data = {'port': {'admin_state_up': False}} self.assertRaises(exc.PortNotFound, plugin.update_port, ctx, 'invalid-uuid', data) def test_delete_non_existent_port(self): ctx = context.get_admin_context() plugin = directory.get_plugin() with mock.patch.object(ml2_plugin.LOG, 'debug') as log_debug: plugin.delete_port(ctx, 'invalid-uuid', l3_port_check=False) log_debug.assert_has_calls([ mock.call(_("Deleting port %s"), 'invalid-uuid'), mock.call(_("The port '%s' was deleted"), 'invalid-uuid') ]) def test_l3_cleanup_on_net_delete(self): l3plugin = directory.get_plugin(plugin_constants.L3) kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} with self.network(**kwargs) as n: with self.subnet(network=n, cidr='200.0.0.0/22'): l3plugin.create_floatingip( context.get_admin_context(), {'floatingip': {'floating_network_id': n['network']['id'], 'tenant_id': n['network']['tenant_id'], 'dns_name': '', 'dns_domain': ''}} ) self._delete('networks', n['network']['id']) flips = l3plugin.get_floatingips(context.get_admin_context()) self.assertFalse(flips) def test_create_ports_bulk_port_binding_failure(self): ctx = context.get_admin_context() with self.network() as net: plugin = directory.get_plugin() with mock.patch.object(plugin, '_bind_port_if_needed', side_effect=ml2_exc.MechanismDriverError( method='create_port_bulk')) as _bind_port_if_needed: res = self._create_port_bulk(self.fmt, 2, net['network']['id'], 'test', True, context=ctx) self.assertTrue(_bind_port_if_needed.called) # We expect a 500 as we injected a fault in the plugin self._validate_behavior_on_bulk_failure( res, 'ports', webob.exc.HTTPServerError.code) def test_create_ports_bulk_with_sec_grp(self): ctx = context.get_admin_context() plugin = directory.get_plugin() with self.network() as net,\ mock.patch.object(plugin.notifier, 'security_groups_member_updated') as m_upd: res = self._create_port_bulk(self.fmt, 3, net['network']['id'], 'test', True, context=ctx) ports = self.deserialize(self.fmt, res) used_sg = ports['ports'][0]['security_groups'] m_upd.assert_has_calls( [mock.call(ctx, [sg]) for sg in used_sg], any_order=True) def test_create_ports_bulk_with_sec_grp_member_provider_update(self): ctx = context.get_admin_context() plugin = directory.get_plugin() with self.network() as net,\ mock.patch.object(plugin.notifier, 'security_groups_member_updated') as m_upd: net_id = net['network']['id'] data = [{ 'network_id': net_id, 'tenant_id': self._tenant_id }, { 'network_id': net_id, 'tenant_id': self._tenant_id, 'device_owner': constants.DEVICE_OWNER_DHCP } ] res = self._create_bulk_from_list(self.fmt, 'port', data, context=ctx) ports = self.deserialize(self.fmt, res) used_sg = ports['ports'][0]['security_groups'] m_upd.assert_called_once_with(ctx, used_sg) m_upd.reset_mock() data[0]['device_owner'] = constants.DEVICE_OWNER_DHCP self._create_bulk_from_list(self.fmt, 'port', data, context=ctx) self.assertFalse(m_upd.called) def test_create_ports_bulk_with_sec_grp_provider_update_ipv6(self): ctx = context.get_admin_context() plugin = directory.get_plugin() fake_prefix = '2001:db8::/64' fake_gateway = 'fe80::1' with self.network() as net: with self.subnet(net, gateway_ip=fake_gateway, cidr=fake_prefix, ip_version=6) as snet_v6,\ mock.patch.object( plugin.notifier, 'security_groups_member_updated') as m_upd: net_id = net['network']['id'] data = [{ 'network_id': net_id, 'tenant_id': self._tenant_id, 'fixed_ips': [{'subnet_id': snet_v6['subnet']['id']}], 'device_owner': constants.DEVICE_OWNER_ROUTER_INTF } ] self._create_bulk_from_list(self.fmt, 'port', data, context=ctx) self.assertFalse(m_upd.called) def test_delete_port_no_notify_in_disassociate_floatingips(self): ctx = context.get_admin_context() plugin = directory.get_plugin() l3plugin = directory.get_plugin(plugin_constants.L3) with self.port() as port,\ mock.patch.object( l3plugin, 'disassociate_floatingips') as disassociate_floatingips,\ mock.patch.object(registry, 'notify') as notify: port_id = port['port']['id'] plugin.delete_port(ctx, port_id) # check that no notification was requested while under # transaction disassociate_floatingips.assert_has_calls([ mock.call(ctx, port_id, do_notify=False) ]) # check that notifier was still triggered self.assertTrue(notify.call_counts) def test_registry_notify_before_after_port_binding(self): plugin = directory.get_plugin() ctx = context.get_admin_context() b_update_events = [] a_update_events = [] b_receiver = lambda *a, **k: b_update_events.append(k) a_receiver = lambda *a, **k: a_update_events.append(k['port']) registry.subscribe(b_receiver, resources.PORT, events.BEFORE_UPDATE) registry.subscribe(a_receiver, resources.PORT, events.AFTER_UPDATE) with self.port() as p: port = {'port': {'binding:host_id': 'newhost'}} plugin.update_port(ctx, p['port']['id'], port) # updating in the host should result in two AFTER_UPDATE events. # one to change the host_id, the second to commit a binding self.assertEqual(2, len(b_update_events)) self.assertEqual({'context': ctx, 'port': {'binding:host_id': 'newhost'}, 'original_port': mock.ANY}, b_update_events[0]) self.assertIn('orig_binding', b_update_events[1]) self.assertIn('new_binding', b_update_events[1]) self.assertDictContainsSubset({'context': ctx}, b_update_events[1]) self.assertDictContainsSubset({ 'admin_state_up': True, 'binding:host_id': 'newhost', 'binding:vif_type': 'unbound', 'binding:vnic_type': u'normal', 'status': 'DOWN'}, b_update_events[1]['port']) self.assertEqual('newhost', a_update_events[0]['binding:host_id']) self.assertEqual('unbound', a_update_events[0]['binding:vif_type']) self.assertEqual('newhost', a_update_events[1]['binding:host_id']) self.assertNotEqual('unbound', a_update_events[1]['binding:vif_type']) def test_check_if_compute_port_serviced_by_dvr(self): self.assertTrue(utils.is_dvr_serviced(DEVICE_OWNER_COMPUTE)) def test_check_if_lbaas_vip_port_serviced_by_dvr(self): self.assertTrue(utils.is_dvr_serviced( constants.DEVICE_OWNER_LOADBALANCER)) def test_check_if_lbaasv2_vip_port_serviced_by_dvr(self): self.assertTrue(utils.is_dvr_serviced( constants.DEVICE_OWNER_LOADBALANCERV2)) def test_check_if_dhcp_port_serviced_by_dvr(self): self.assertTrue(utils.is_dvr_serviced(constants.DEVICE_OWNER_DHCP)) def test_check_if_port_not_serviced_by_dvr(self): self.assertFalse(utils.is_dvr_serviced( constants.DEVICE_OWNER_ROUTER_INTF)) def test_disassociate_floatingips_do_notify_returns_nothing(self): ctx = context.get_admin_context() l3plugin = directory.get_plugin(plugin_constants.L3) with self.port() as port: port_id = port['port']['id'] # check that nothing is returned when notifications are handled # by the called method self.assertIsNone(l3plugin.disassociate_floatingips(ctx, port_id)) def test_create_port_tolerates_db_deadlock(self): plugin = directory.get_plugin() with self.network() as net: with self.subnet(network=net) as subnet: _orig = plugin._get_port self._failed = False def fail_once(*args, **kwargs): if not self._failed: self._failed = True raise db_exc.DBDeadlock() return _orig(*args, **kwargs) with mock.patch.object(plugin, '_get_port', side_effect=fail_once) as get_port_mock: port_kwargs = {portbindings.HOST_ID: 'host1', 'subnet': subnet, 'device_id': 'deadlocktest'} with self.port(arg_list=(portbindings.HOST_ID,), **port_kwargs) as port: self.assertTrue(port['port']['id']) self.assertTrue(get_port_mock.called) # make sure that we didn't create more than one port on # the retry query_params = "network_id=%s" % net['network']['id'] query_params += "&device_id=%s" % 'deadlocktest' ports = self._list('ports', query_params=query_params) self.assertEqual(1, len(ports['ports'])) def test_delete_port_tolerates_db_deadlock(self): ctx = context.get_admin_context() plugin = directory.get_plugin() with self.port() as port: port_db = plugin._get_port(ctx, port['port']['id']) with mock.patch.object(plugin, '_get_port') as gp: gp.side_effect = [db_exc.DBDeadlock] + [port_db] * 3 req = self.new_delete_request('ports', port['port']['id']) res = req.get_response(self.api) self.assertEqual(204, res.status_int) self.assertGreater(gp.call_count, 1) self.assertRaises( exc.PortNotFound, plugin.get_port, ctx, port['port']['id']) def test_port_create_resillient_to_duplicate_records(self): def make_port(): with self.port(): pass self._test_operation_resillient_to_ipallocation_failure(make_port) def test_port_update_resillient_to_duplicate_records(self): cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.8'}] with self.subnet(cidr=cidr, allocation_pools=allocation_pools) as subnet: with self.port(subnet=subnet) as p: data = {'port': {'fixed_ips': [{'ip_address': '10.0.0.9'}]}} req = self.new_update_request('ports', data, p['port']['id']) def do_request(): self.assertEqual(200, req.get_response(self.api).status_int) self._test_operation_resillient_to_ipallocation_failure( do_request) def _test_operation_resillient_to_ipallocation_failure(self, func): class IPAllocationsGrenade(object): insert_ip_called = False except_raised = False def execute(self, con, curs, stmt, *args, **kwargs): if 'INSERT INTO ipallocations' in stmt: self.insert_ip_called = True def commit(self, con): # we blow up on commit to simulate another thread/server # stealing our IP before our transaction was done if self.insert_ip_called and not self.except_raised: self.except_raised = True raise db_exc.DBDuplicateEntry() listener = IPAllocationsGrenade() engine = db_api.context_manager.writer.get_engine() db_api.sqla_listen(engine, 'before_cursor_execute', listener.execute) db_api.sqla_listen(engine, 'commit', listener.commit) func() # make sure that the grenade went off during the commit self.assertTrue(listener.except_raised) def test_list_ports_filtered_by_fixed_ip_substring(self): # for this test we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) with self.port() as port1, self.port(): fixed_ips = port1['port']['fixed_ips'][0] query_params = """ fixed_ips=ip_address_substr%%3D%s&fixed_ips=subnet_id%%3D%s """.strip() % (fixed_ips['ip_address'][:-1], fixed_ips['subnet_id']) self._test_list_resources('port', [port1], query_params=query_params) query_params = """ fixed_ips=ip_address_substr%%3D%s&fixed_ips=subnet_id%%3D%s """.strip() % (fixed_ips['ip_address'][1:], fixed_ips['subnet_id']) self._test_list_resources('port', [port1], query_params=query_params) query_params = """ fixed_ips=ip_address_substr%%3D%s&fixed_ips=subnet_id%%3D%s """.strip() % ('192.168.', fixed_ips['subnet_id']) self._test_list_resources('port', [], query_params=query_params) def test_list_ports_filtered_by_fixed_ip_substring_dual_stack(self): with self.subnet() as subnet: # Get a IPv4 and IPv6 address tenant_id = subnet['subnet']['tenant_id'] net_id = subnet['subnet']['network_id'] res = self._create_subnet( self.fmt, tenant_id=tenant_id, net_id=net_id, cidr='2607:f0d0:1002:51::/124', ip_version=6, gateway_ip=constants.ATTR_NOT_SPECIFIED) subnet2 = self.deserialize(self.fmt, res) kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet2['subnet']['id']}]} res = self._create_port(self.fmt, net_id=net_id, **kwargs) port1 = self.deserialize(self.fmt, res) res = self._create_port(self.fmt, net_id=net_id, **kwargs) port2 = self.deserialize(self.fmt, res) fixed_ips = port1['port']['fixed_ips'] self.assertEqual(2, len(fixed_ips)) query_params = """ fixed_ips=ip_address_substr%%3D%s&fixed_ips=ip_address%%3D%s """.strip() % (fixed_ips[0]['ip_address'][:-1], fixed_ips[1]['ip_address']) self._test_list_resources('port', [port1], query_params=query_params) query_params = """ fixed_ips=ip_address_substr%%3D%s&fixed_ips=ip_address%%3D%s """.strip() % ('192.168.', fixed_ips[1]['ip_address']) self._test_list_resources('port', [], query_params=query_params) self._delete('ports', port1['port']['id']) self._delete('ports', port2['port']['id']) class TestMl2PortsV2WithRevisionPlugin(Ml2PluginV2TestCase): def setUp(self): super(TestMl2PortsV2WithRevisionPlugin, self).setUp() self.revision_plugin = revision_plugin.RevisionPlugin() def test_update_port_status_bumps_revision(self): ctx = context.get_admin_context() plugin = directory.get_plugin() host_arg = {portbindings.HOST_ID: HOST} with self.port(arg_list=(portbindings.HOST_ID,), **host_arg) as port: port = plugin.get_port(ctx, port['port']['id']) updated_ports = [] receiver = lambda *a, **k: updated_ports.append(k['port']) registry.subscribe(receiver, resources.PORT, events.AFTER_UPDATE) plugin.update_port_status( ctx, port['id'], constants.PORT_STATUS_ACTIVE, host=HOST) self.assertGreater(updated_ports[0]['revision_number'], port['revision_number']) def test_bind_port_bumps_revision(self): updated_ports = [] created_ports = [] ureceiver = lambda *a, **k: updated_ports.append(k['port']) creceiver = lambda *a, **k: created_ports.append(k['port']) registry.subscribe(ureceiver, resources.PORT, events.AFTER_UPDATE) registry.subscribe(creceiver, resources.PORT, events.AFTER_CREATE) host_arg = {portbindings.HOST_ID: HOST} with self.port(arg_list=(portbindings.HOST_ID,), **host_arg): self.assertGreater(updated_ports[0]['revision_number'], created_ports[0]['revision_number']) def test_update_port_status_dvr_port_no_update_on_same_status(self): ctx = context.get_admin_context() plugin = directory.get_plugin() # enable subscription for events p_update_receiver = mock.Mock() registry.subscribe(p_update_receiver, resources.PORT, events.AFTER_UPDATE) host_arg = {portbindings.HOST_ID: HOST} with self.port(device_owner=constants.DEVICE_OWNER_DVR_INTERFACE, device_id=TEST_ROUTER_ID, arg_list=(portbindings.HOST_ID,), **host_arg) as port: ml2_db.ensure_distributed_port_binding(ctx, port['port']['id'], HOST) p_update_receiver.reset_mock() plugin.update_port_status( ctx, port['port']['id'], constants.PORT_STATUS_ACTIVE, host=HOST) self.assertTrue(p_update_receiver.called) after_1 = plugin.get_port(ctx, port['port']['id']) p_update_receiver.reset_mock() plugin.update_port_status( ctx, port['port']['id'], constants.PORT_STATUS_ACTIVE, host=HOST) self.assertFalse(p_update_receiver.called) after_2 = plugin.get_port(ctx, port['port']['id']) self.assertEqual(after_1['revision_number'], after_2['revision_number']) class TestMl2PortsV2WithL3(test_plugin.TestPortsV2, Ml2PluginV2TestCase): """For testing methods that require the L3 service plugin.""" l3_plugin = 'neutron.services.l3_router.l3_router_plugin.L3RouterPlugin' def get_additional_service_plugins(self): return {'flavors': 'flavors'} def test_update_port_status_notify_port_event_after_update(self): ctx = context.get_admin_context() plugin = directory.get_plugin() l3plugin = directory.get_plugin(plugin_constants.L3) host_arg = {portbindings.HOST_ID: HOST} with mock.patch.object(l3plugin.l3_rpc_notifier, 'routers_updated_on_host') as mock_updated: with self.port(device_owner=constants.DEVICE_OWNER_ROUTER_HA_INTF, device_id=TEST_ROUTER_ID, arg_list=(portbindings.HOST_ID,), **host_arg) as port: plugin.update_port_status( ctx, port['port']['id'], constants.PORT_STATUS_ACTIVE, host=HOST) mock_updated.assert_called_once_with( mock.ANY, [TEST_ROUTER_ID], HOST) class TestMl2PluginOnly(Ml2PluginV2TestCase): """For testing methods that don't call drivers""" def test__verify_service_plugins_requirements(self): plugin = directory.get_plugin() with mock.patch.dict(ml2_plugin.SERVICE_PLUGINS_REQUIRED_DRIVERS, {self.l3_plugin: self._mechanism_drivers}),\ mock.patch.object(plugin.extension_manager, 'names', return_value=self._mechanism_drivers): plugin._verify_service_plugins_requirements() def test__verify_service_plugins_requirements_missing_driver(self): plugin = directory.get_plugin() with mock.patch.dict(ml2_plugin.SERVICE_PLUGINS_REQUIRED_DRIVERS, {self.l3_plugin: ['test_required_driver']}),\ mock.patch.object(plugin.extension_manager, 'names', return_value=self._mechanism_drivers): self.assertRaises( ml2_exc.ExtensionDriverNotFound, plugin._verify_service_plugins_requirements ) def _test_check_mac_update_allowed(self, vif_type, expect_change=True): plugin = directory.get_plugin() port = {'mac_address': "fake_mac", 'id': "fake_id"} if expect_change: new_attrs = {"mac_address": "dummy_mac"} else: new_attrs = {"mac_address": port['mac_address']} binding = mock.Mock() binding.vif_type = vif_type mac_changed = plugin._check_mac_update_allowed(port, new_attrs, binding) self.assertEqual(expect_change, mac_changed) def test_check_mac_update_allowed_if_no_mac_change(self): self._test_check_mac_update_allowed(portbindings.VIF_TYPE_UNBOUND, expect_change=False) def test_check_mac_update_allowed_unless_bound(self): with testtools.ExpectedException(exc.PortBound): self._test_check_mac_update_allowed(portbindings.VIF_TYPE_OVS) def _test_reset_mac_for_direct_physical(self, direct_physical=True, unbinding=True): plugin = directory.get_plugin() port = {'device_id': '123', 'device_owner': 'compute:nova'} new_attrs = ({'device_id': '', 'device_owner': ''} if unbinding else {'name': 'new'}) binding = mock.Mock() binding.vnic_type = ( portbindings.VNIC_DIRECT_PHYSICAL if direct_physical else portbindings.VNIC_NORMAL) new_mac = plugin._reset_mac_for_direct_physical( port, new_attrs, binding) if direct_physical and unbinding: self.assertTrue(new_mac) self.assertIsNotNone(new_attrs.get('mac_address')) else: self.assertFalse(new_mac) self.assertIsNone(new_attrs.get('mac_address')) def test_reset_mac_for_direct_physical(self): self._test_reset_mac_for_direct_physical() def test_reset_mac_for_direct_physical_not_physycal(self): self._test_reset_mac_for_direct_physical(False, True) def test_reset_mac_for_direct_physical_no_unbinding(self): self._test_reset_mac_for_direct_physical(True, False) def test_reset_mac_for_direct_physical_no_unbinding_not_physical(self): self._test_reset_mac_for_direct_physical(False, False) def test__device_to_port_id_prefix_names(self): input_output = [('sg-abcdefg', 'abcdefg'), ('tap123456', '123456'), ('qvo567890', '567890')] for device, expected in input_output: self.assertEqual(expected, ml2_plugin.Ml2Plugin._device_to_port_id( self.context, device)) def test__device_to_port_id_mac_address(self): with self.port() as p: mac = p['port']['mac_address'] port_id = p['port']['id'] self.assertEqual(port_id, ml2_plugin.Ml2Plugin._device_to_port_id( self.context, mac)) def test__device_to_port_id_not_uuid_not_mac(self): dev = '1234567' self.assertEqual(dev, ml2_plugin.Ml2Plugin._device_to_port_id( self.context, dev)) def test__device_to_port_id_UUID(self): port_id = uuidutils.generate_uuid() self.assertEqual(port_id, ml2_plugin.Ml2Plugin._device_to_port_id( self.context, port_id)) class Test_GetNetworkMtu(Ml2PluginV2TestCase): def test_get_mtu_with_physical_net(self): plugin = directory.get_plugin() mock_type_driver = mock.MagicMock() plugin.type_manager.drivers['driver1'] = mock.Mock() plugin.type_manager.drivers['driver1'].obj = mock_type_driver net = { 'name': 'net1', 'network_type': 'driver1', 'physical_network': 'physnet1', } plugin._get_network_mtu(net) mock_type_driver.get_mtu.assert_called_once_with('physnet1') def _register_type_driver_with_mtu(self, driver, mtu): plugin = directory.get_plugin() class FakeDriver(object): def get_mtu(self, physical_network=None): return mtu def validate_provider_segment(self, segment): pass def is_partial_segment(self, segment): return False driver_mock = mock.Mock() driver_mock.obj = FakeDriver() plugin.type_manager.drivers[driver] = driver_mock def test_single_segment(self): plugin = directory.get_plugin() self._register_type_driver_with_mtu('driver1', 1400) net = { 'name': 'net1', mpnet.SEGMENTS: [ { 'network_type': 'driver1', 'physical_network': 'physnet1' }, ] } self.assertEqual(1400, plugin._get_network_mtu(net)) def test_multiple_segments_returns_minimal_mtu(self): plugin = directory.get_plugin() self._register_type_driver_with_mtu('driver1', 1400) self._register_type_driver_with_mtu('driver2', 1300) net = { 'name': 'net1', mpnet.SEGMENTS: [ { 'network_type': 'driver1', 'physical_network': 'physnet1' }, { 'network_type': 'driver2', 'physical_network': 'physnet2' }, ] } self.assertEqual(1300, plugin._get_network_mtu(net)) def test_no_segments(self): plugin = directory.get_plugin() self._register_type_driver_with_mtu('driver1', 1400) net = { 'name': 'net1', 'network_type': 'driver1', 'physical_network': 'physnet1', } self.assertEqual(1400, plugin._get_network_mtu(net)) def test_get_mtu_None_returns_0(self): plugin = directory.get_plugin() self._register_type_driver_with_mtu('driver1', None) net = { 'name': 'net1', 'network_type': 'driver1', 'physical_network': 'physnet1', } self.assertEqual(1500, plugin._get_network_mtu(net)) def test_unknown_segment_type_ignored(self): plugin = directory.get_plugin() self._register_type_driver_with_mtu('driver1', None) self._register_type_driver_with_mtu('driver2', 1300) net = { 'name': 'net1', mpnet.SEGMENTS: [ { 'network_type': 'driver1', 'physical_network': 'physnet1' }, { 'network_type': 'driver2', 'physical_network': 'physnet2' }, ] } self.assertEqual(1300, plugin._get_network_mtu(net)) class TestMl2DvrPortsV2(TestMl2PortsV2): def setUp(self): super(TestMl2DvrPortsV2, self).setUp() extensions = ['router', constants.L3_AGENT_SCHEDULER_EXT_ALIAS, constants.L3_DISTRIBUTED_EXT_ALIAS] self.plugin = directory.get_plugin() self.l3plugin = mock.Mock() type(self.l3plugin).supported_extension_aliases = ( mock.PropertyMock(return_value=extensions)) def test_delete_port_notifies_l3_plugin(self, floating_ip=False): directory.add_plugin(plugin_constants.L3, self.l3plugin) ns_to_delete = {'host': 'myhost', 'agent_id': 'vm_l3_agent', 'router_id': 'my_router'} router_ids = set() call_count_total = 3 if floating_ip: router_ids.add(ns_to_delete['router_id']) with self.port() as port,\ mock.patch.object(registry, 'notify') as notify,\ mock.patch.object(self.l3plugin, 'disassociate_floatingips', return_value=router_ids): port_id = port['port']['id'] self.plugin.delete_port(self.context, port_id) self.assertEqual(call_count_total, notify.call_count) # needed for a full match in the assertion below port['port']['extra_dhcp_opts'] = [] expected = [mock.call(resources.PORT, events.BEFORE_DELETE, mock.ANY, context=self.context, port_id=port['port']['id'], port_check=True), mock.call(resources.PORT, events.PRECOMMIT_DELETE, mock.ANY, network=mock.ANY, bind=mock.ANY, port=port['port'], port_db=mock.ANY, context=self.context, levels=mock.ANY, id=mock.ANY, bindings=mock.ANY), mock.call(resources.PORT, events.AFTER_DELETE, mock.ANY, context=self.context, port=port['port'], router_ids=router_ids)] notify.assert_has_calls(expected) def test_delete_port_with_floatingip_notifies_l3_plugin(self): self.test_delete_port_notifies_l3_plugin(floating_ip=True) def test_concurrent_csnat_port_delete(self): plugin = directory.get_plugin(plugin_constants.L3) r = plugin.create_router( self.context, {'router': {'name': 'router', 'admin_state_up': True, 'tenant_id': 'fake_tenant'}}) with self.subnet() as s: p = plugin.add_router_interface(self.context, r['id'], {'subnet_id': s['subnet']['id']}) # lie to turn the port into an SNAT interface with db_api.context_manager.reader.using(self.context): pager = base_obj.Pager(limit=1) rp = l3_obj.RouterPort.get_objects( self.context, _pager=pager, port_id=p['port_id']) rp[0].port_type = constants.DEVICE_OWNER_ROUTER_SNAT rp[0].update() # take the port away before csnat gets a chance to delete it # to simulate a concurrent delete orig_get_ports = plugin._core_plugin.get_ports def get_ports_with_delete_first(*args, **kwargs): plugin._core_plugin.delete_port(self.context, p['port_id'], l3_port_check=False) return orig_get_ports(*args, **kwargs) plugin._core_plugin.get_ports = get_ports_with_delete_first # This should be able to handle a concurrent delete without raising # an exception router = plugin._get_router(self.context, r['id']) plugin.delete_csnat_router_interface_ports(self.context, router) class TestMl2PortBinding(Ml2PluginV2TestCase, test_bindings.PortBindingsTestCase): # Test case does not set binding:host_id, so ml2 does not attempt # to bind port VIF_TYPE = portbindings.VIF_TYPE_UNBOUND HAS_PORT_FILTER = False ENABLE_SG = True FIREWALL_DRIVER = test_sg_rpc.FIREWALL_HYBRID_DRIVER def setUp(self, firewall_driver=None): test_sg_rpc.set_firewall_driver(self.FIREWALL_DRIVER) cfg.CONF.set_override( 'enable_security_group', self.ENABLE_SG, group='SECURITYGROUP') super(TestMl2PortBinding, self).setUp() def _check_port_binding_profile(self, port, profile=None): self.assertIn('id', port) self.assertIn(portbindings.PROFILE, port) value = port[portbindings.PROFILE] self.assertEqual(profile or {}, value) def test_create_port_binding_profile(self): self._test_create_port_binding_profile({'a': 1, 'b': 2}) def test_update_port_binding_profile(self): self._test_update_port_binding_profile({'c': 3}) def test_create_port_binding_profile_too_big(self): s = 'x' * 5000 profile_arg = {portbindings.PROFILE: {'d': s}} try: with self.port(expected_res_status=400, arg_list=(portbindings.PROFILE,), **profile_arg): pass except webob.exc.HTTPClientError: pass def test_remove_port_binding_profile(self): profile = {'e': 5} profile_arg = {portbindings.PROFILE: profile} with self.port(arg_list=(portbindings.PROFILE,), **profile_arg) as port: self._check_port_binding_profile(port['port'], profile) port_id = port['port']['id'] profile_arg = {portbindings.PROFILE: None} port = self._update('ports', port_id, {'port': profile_arg})['port'] self._check_port_binding_profile(port) port = self._show('ports', port_id)['port'] self._check_port_binding_profile(port) def test_return_on_concurrent_delete_and_binding(self): # create a port and delete it so we have an expired mechanism context with self.port() as port: plugin = directory.get_plugin() binding = plugin._get_port(self.context, port['port']['id']).port_binding binding['host'] = 'test' mech_context = driver_context.PortContext( plugin, self.context, port['port'], plugin.get_network(self.context, port['port']['network_id']), binding, None) side = exc.PortNotFound(port_id=port['port']['id']) with mock.patch.object(plugin, '_get_port', side_effect=side) as gp_mock,\ mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.' '_make_port_dict') as mpd_mock: plugin._bind_port_if_needed(mech_context) # called during deletion to get port self.assertTrue(gp_mock.mock_calls) # should have returned before calling _make_port_dict self.assertFalse(mpd_mock.mock_calls) def _create_port_and_bound_context(self, port_vif_type, bound_vif_type): with self.port() as port: plugin = directory.get_plugin() binding = plugin._get_port( self.context, port['port']['id']).port_binding binding['host'] = 'fake_host' binding['vif_type'] = port_vif_type # Generates port context to be used before the bind. port_context = driver_context.PortContext( plugin, self.context, port['port'], plugin.get_network(self.context, port['port']['network_id']), binding, None) bound_context = mock.MagicMock() # Bound context is how port_context is expected to look # after _bind_port. bound_context.vif_type = bound_vif_type return plugin, port_context, bound_context def test__attempt_binding(self): # Simulate a successful binding for vif_type unbound # and keep the same binding state for other vif types. vif_types = [(portbindings.VIF_TYPE_BINDING_FAILED, portbindings.VIF_TYPE_BINDING_FAILED), (portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_OVS), (portbindings.VIF_TYPE_OVS, portbindings.VIF_TYPE_OVS)] for port_vif_type, bound_vif_type in vif_types: plugin, port_context, bound_context = ( self._create_port_and_bound_context(port_vif_type, bound_vif_type)) with mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin._bind_port', return_value=bound_context) as bd_mock: context, need_notify, try_again = (plugin._attempt_binding( port_context, False)) expected_need_notify = port_vif_type not in ( portbindings.VIF_TYPE_BINDING_FAILED, portbindings.VIF_TYPE_OVS) if bound_vif_type == portbindings.VIF_TYPE_BINDING_FAILED: expected_vif_type = port_vif_type expected_try_again = True expected_bd_mock_called = True else: expected_vif_type = portbindings.VIF_TYPE_OVS expected_try_again = False expected_bd_mock_called = (port_vif_type == portbindings.VIF_TYPE_UNBOUND) self.assertEqual(expected_need_notify, need_notify) self.assertEqual(expected_vif_type, context.vif_type) self.assertEqual(expected_try_again, try_again) self.assertEqual(expected_bd_mock_called, bd_mock.called) def test__bind_port_if_needed_early_exit_on_no_segments(self): with self.network() as n: ctx = context.get_admin_context() seg_plugin = segments_plugin.Plugin.get_instance() seg = seg_plugin.get_segments(ctx)[0] seg_plugin.delete_segment(ctx, seg['id']) plugin = directory.get_plugin() mech_context = driver_context.PortContext( plugin, ctx, None, plugin.get_network(self.context, n['network']['id']), models.PortBinding(), None) with mock.patch.object(plugin, '_attempt_binding') as ab: plugin._bind_port_if_needed(mech_context) self.assertFalse(ab.called) def test__attempt_binding_retries(self): # Simulate cases of both successful and failed binding states for # vif_type unbound vif_types = [(portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_BINDING_FAILED), (portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_OVS)] for port_vif_type, bound_vif_type in vif_types: plugin, port_context, bound_context = ( self._create_port_and_bound_context(port_vif_type, bound_vif_type)) with mock.patch( 'neutron.plugins.ml2.plugin.Ml2Plugin._bind_port', return_value=bound_context),\ mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin._commit_' 'port_binding', return_value=(bound_context, True, False)),\ mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.' '_attempt_binding', side_effect=plugin._attempt_binding) as at_mock: plugin._bind_port_if_needed(port_context) if bound_vif_type == portbindings.VIF_TYPE_BINDING_FAILED: # An unsuccessful binding attempt should be retried # MAX_BIND_TRIES amount of times. self.assertEqual(ml2_plugin.MAX_BIND_TRIES, at_mock.call_count) else: # Successful binding should only be attempted once. self.assertEqual(1, at_mock.call_count) def test__bind_port_if_needed_concurrent_calls(self): port_vif_type = portbindings.VIF_TYPE_UNBOUND bound_vif_type = portbindings.VIF_TYPE_OVS plugin, port_context, bound_context = ( self._create_port_and_bound_context(port_vif_type, bound_vif_type)) bound_context._binding_levels = [mock.Mock( port_id="port_id", level=0, driver='fake_agent', segment_id="11111111-2222-3333-4444-555555555555")] # let _commit_port_binding replace the PortContext with a new instance # which does not have any binding levels set to simulate the concurrent # port binding operations fail with mock.patch( 'neutron.plugins.ml2.plugin.Ml2Plugin._bind_port', return_value=bound_context),\ mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.' '_notify_port_updated') as npu_mock,\ mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.' '_attempt_binding', side_effect=plugin._attempt_binding) as ab_mock,\ mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.' '_commit_port_binding', return_value=( mock.MagicMock(), True, True)) as cpb_mock: ret_context = plugin._bind_port_if_needed(port_context, allow_notify=True) # _attempt_binding will return without doing anything during # the second iteration since _should_bind_port returns False self.assertEqual(2, ab_mock.call_count) self.assertEqual(1, cpb_mock.call_count) # _notify_port_updated will still be called though it does # nothing due to the missing binding levels npu_mock.assert_called_once_with(ret_context) def test__commit_port_binding_populating_with_binding_levels(self): port_vif_type = portbindings.VIF_TYPE_OVS bound_vif_type = portbindings.VIF_TYPE_OVS plugin, port_context, bound_context = ( self._create_port_and_bound_context(port_vif_type, bound_vif_type)) db_portbinding = port_obj.PortBindingLevel( self.context, port_id=uuidutils.generate_uuid(), level=0, driver='fake_agent', segment_id="11111111-2222-3333-4444-555555555555") bound_context.network.current = {'id': 'net_id'} with mock.patch.object(ml2_db, 'get_binding_levels', return_value=[db_portbinding]),\ mock.patch.object(driver_context.PortContext, '_push_binding_level') as pbl_mock: plugin._commit_port_binding( port_context, bound_context, True, False) pbl_mock.assert_called_once_with(db_portbinding) def test_port_binding_profile_not_changed(self): profile = {'e': 5} profile_arg = {portbindings.PROFILE: profile} with self.port(arg_list=(portbindings.PROFILE,), **profile_arg) as port: self._check_port_binding_profile(port['port'], profile) port_id = port['port']['id'] state_arg = {'admin_state_up': True} port = self._update('ports', port_id, {'port': state_arg})['port'] self._check_port_binding_profile(port, profile) port = self._show('ports', port_id)['port'] self._check_port_binding_profile(port, profile) def test_update_port_binding_host_id_none(self): with self.port() as port: plugin = directory.get_plugin() binding = plugin._get_port( self.context, port['port']['id']).port_binding with self.context.session.begin(subtransactions=True): binding.host = 'test' mech_context = driver_context.PortContext( plugin, self.context, port['port'], plugin.get_network(self.context, port['port']['network_id']), binding, None) with mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.' '_update_port_dict_binding') as update_mock: attrs = {portbindings.HOST_ID: None} self.assertEqual('test', binding.host) with self.context.session.begin(subtransactions=True): plugin._process_port_binding(mech_context, attrs) self.assertTrue(update_mock.mock_calls) self.assertEqual('', binding.host) def test_update_port_binding_host_id_not_changed(self): with self.port() as port: plugin = directory.get_plugin() binding = plugin._get_port( self.context, port['port']['id']).port_binding binding['host'] = 'test' mech_context = driver_context.PortContext( plugin, self.context, port['port'], plugin.get_network(self.context, port['port']['network_id']), binding, None) with mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.' '_update_port_dict_binding') as update_mock: attrs = {portbindings.PROFILE: {'e': 5}} plugin._process_port_binding(mech_context, attrs) self.assertTrue(update_mock.mock_calls) self.assertEqual('test', binding.host) def test_process_distributed_port_binding_update_router_id(self): host_id = 'host' binding = models.DistributedPortBinding( port_id='port_id', host=host_id, router_id='old_router_id', vif_type=portbindings.VIF_TYPE_OVS, vnic_type=portbindings.VNIC_NORMAL, status=constants.PORT_STATUS_DOWN) plugin = directory.get_plugin() mock_network = {'id': 'net_id'} mock_port = {'id': 'port_id'} ctxt = context.get_admin_context() new_router_id = 'new_router' attrs = {'device_id': new_router_id, portbindings.HOST_ID: host_id} with mock.patch.object(plugin, '_update_port_dict_binding'): with mock.patch.object(segments_db, 'get_network_segments', return_value=[]): mech_context = driver_context.PortContext( self, ctxt, mock_port, mock_network, binding, None) plugin._process_distributed_port_binding(mech_context, ctxt, attrs) self.assertEqual(new_router_id, mech_context._binding.router_id) self.assertEqual(host_id, mech_context._binding.host) def test_update_distributed_port_binding_on_concurrent_port_delete(self): plugin = directory.get_plugin() with self.port() as port: port = { 'id': port['port']['id'], portbindings.HOST_ID: 'foo_host', } exc = db_exc.DBReferenceError('', '', '', '') with mock.patch.object(ml2_db, 'ensure_distributed_port_binding', side_effect=exc): res = plugin.update_distributed_port_binding( self.context, port['id'], {'port': port}) self.assertIsNone(res) def test_update_distributed_port_binding_on_non_existent_port(self): plugin = directory.get_plugin() port = { 'id': 'foo_port_id', portbindings.HOST_ID: 'foo_host', } with mock.patch.object( ml2_db, 'ensure_distributed_port_binding') as mock_dist: plugin.update_distributed_port_binding( self.context, 'foo_port_id', {'port': port}) self.assertFalse(mock_dist.called) def test__bind_port_original_port_set(self): plugin = directory.get_plugin() plugin.mechanism_manager = mock.Mock() mock_port = {'id': 'port_id'} context = mock.Mock() context.network.current = {'id': 'net_id'} context.original = mock_port with mock.patch.object(plugin, '_update_port_dict_binding'), \ mock.patch.object(segments_db, 'get_network_segments', return_value=[]): new_context = plugin._bind_port(context) self.assertEqual(mock_port, new_context.original) self.assertFalse(new_context == context) class TestMl2PortBindingNoSG(TestMl2PortBinding): HAS_PORT_FILTER = False ENABLE_SG = False FIREWALL_DRIVER = test_sg_rpc.FIREWALL_NOOP_DRIVER class TestMl2PortBindingHost(Ml2PluginV2TestCase, test_bindings.PortBindingsHostTestCaseMixin): pass class TestMl2PortBindingVnicType(Ml2PluginV2TestCase, test_bindings.PortBindingsVnicTestCaseMixin): pass class TestMultiSegmentNetworks(Ml2PluginV2TestCase): def setUp(self, plugin=None): super(TestMultiSegmentNetworks, self).setUp() def test_allocate_dynamic_segment(self): data = {'network': {'name': 'net1', 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) network = self.deserialize(self.fmt, network_req.get_response(self.api)) segment = {driver_api.NETWORK_TYPE: 'vlan', driver_api.PHYSICAL_NETWORK: 'physnet1'} network_id = network['network']['id'] self.driver.type_manager.allocate_dynamic_segment( self.context, network_id, segment) dynamic_segment = segments_db.get_dynamic_segment( self.context, network_id, 'physnet1') self.assertEqual('vlan', dynamic_segment[driver_api.NETWORK_TYPE]) self.assertEqual('physnet1', dynamic_segment[driver_api.PHYSICAL_NETWORK]) self.assertGreater(dynamic_segment[driver_api.SEGMENTATION_ID], 0) segment2 = {driver_api.NETWORK_TYPE: 'vlan', driver_api.SEGMENTATION_ID: 1234, driver_api.PHYSICAL_NETWORK: 'physnet3'} self.driver.type_manager.allocate_dynamic_segment( self.context, network_id, segment2) dynamic_segment = segments_db.get_dynamic_segment( self.context, network_id, segmentation_id='1234') self.assertEqual('vlan', dynamic_segment[driver_api.NETWORK_TYPE]) self.assertEqual('physnet3', dynamic_segment[driver_api.PHYSICAL_NETWORK]) self.assertEqual(dynamic_segment[driver_api.SEGMENTATION_ID], 1234) def test_allocate_dynamic_segment_multiple_physnets(self): data = {'network': {'name': 'net1', 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) network = self.deserialize(self.fmt, network_req.get_response(self.api)) segment = {driver_api.NETWORK_TYPE: 'vlan', driver_api.PHYSICAL_NETWORK: 'physnet1'} network_id = network['network']['id'] self.driver.type_manager.allocate_dynamic_segment( self.context, network_id, segment) dynamic_segment = segments_db.get_dynamic_segment( self.context, network_id, 'physnet1') self.assertEqual('vlan', dynamic_segment[driver_api.NETWORK_TYPE]) self.assertEqual('physnet1', dynamic_segment[driver_api.PHYSICAL_NETWORK]) dynamic_segmentation_id = dynamic_segment[driver_api.SEGMENTATION_ID] self.assertGreater(dynamic_segmentation_id, 0) dynamic_segment1 = segments_db.get_dynamic_segment( self.context, network_id, 'physnet1') dynamic_segment1_id = dynamic_segment1[driver_api.SEGMENTATION_ID] self.assertEqual(dynamic_segmentation_id, dynamic_segment1_id) segment2 = {driver_api.NETWORK_TYPE: 'vlan', driver_api.PHYSICAL_NETWORK: 'physnet2'} self.driver.type_manager.allocate_dynamic_segment( self.context, network_id, segment2) dynamic_segment2 = segments_db.get_dynamic_segment( self.context, network_id, 'physnet2') dynamic_segmentation2_id = dynamic_segment2[driver_api.SEGMENTATION_ID] self.assertNotEqual(dynamic_segmentation_id, dynamic_segmentation2_id) def test_allocate_release_dynamic_segment(self): data = {'network': {'name': 'net1', 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) network = self.deserialize(self.fmt, network_req.get_response(self.api)) segment = {driver_api.NETWORK_TYPE: 'vlan', driver_api.PHYSICAL_NETWORK: 'physnet1'} network_id = network['network']['id'] self.driver.type_manager.allocate_dynamic_segment( self.context, network_id, segment) dynamic_segment = segments_db.get_dynamic_segment( self.context, network_id, 'physnet1') self.assertEqual('vlan', dynamic_segment[driver_api.NETWORK_TYPE]) self.assertEqual('physnet1', dynamic_segment[driver_api.PHYSICAL_NETWORK]) dynamic_segmentation_id = dynamic_segment[driver_api.SEGMENTATION_ID] self.assertGreater(dynamic_segmentation_id, 0) self.driver.type_manager.release_dynamic_segment( self.context, dynamic_segment[driver_api.ID]) self.assertIsNone(segments_db.get_dynamic_segment( self.context, network_id, 'physnet1')) def test_create_network_provider(self): data = {'network': {'name': 'net1', pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1, 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) network = self.deserialize(self.fmt, network_req.get_response(self.api)) self.assertEqual('vlan', network['network'][pnet.NETWORK_TYPE]) self.assertEqual('physnet1', network['network'][pnet.PHYSICAL_NETWORK]) self.assertEqual(1, network['network'][pnet.SEGMENTATION_ID]) self.assertNotIn(mpnet.SEGMENTS, network['network']) def test_create_network_single_multiprovider(self): data = {'network': {'name': 'net1', mpnet.SEGMENTS: [{pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1}], 'tenant_id': 'tenant_one'}} net_req = self.new_create_request('networks', data) network = self.deserialize(self.fmt, net_req.get_response(self.api)) self.assertEqual('vlan', network['network'][pnet.NETWORK_TYPE]) self.assertEqual('physnet1', network['network'][pnet.PHYSICAL_NETWORK]) self.assertEqual(1, network['network'][pnet.SEGMENTATION_ID]) self.assertNotIn(mpnet.SEGMENTS, network['network']) # Tests get_network() net_req = self.new_show_request('networks', network['network']['id']) network = self.deserialize(self.fmt, net_req.get_response(self.api)) self.assertEqual('vlan', network['network'][pnet.NETWORK_TYPE]) self.assertEqual('physnet1', network['network'][pnet.PHYSICAL_NETWORK]) self.assertEqual(1, network['network'][pnet.SEGMENTATION_ID]) self.assertNotIn(mpnet.SEGMENTS, network['network']) def test_create_network_multiprovider(self): data = {'network': {'name': 'net1', mpnet.SEGMENTS: [{pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1}, {pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 2}], 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) network = self.deserialize(self.fmt, network_req.get_response(self.api)) segments = network['network'][mpnet.SEGMENTS] for segment_index, segment in enumerate(data['network'] [mpnet.SEGMENTS]): for field in [pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK, pnet.SEGMENTATION_ID]: self.assertEqual(segment.get(field), segments[segment_index][field]) # Tests get_network() net_req = self.new_show_request('networks', network['network']['id']) network = self.deserialize(self.fmt, net_req.get_response(self.api)) segments = network['network'][mpnet.SEGMENTS] for segment_index, segment in enumerate(data['network'] [mpnet.SEGMENTS]): for field in [pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK, pnet.SEGMENTATION_ID]: self.assertEqual(segment.get(field), segments[segment_index][field]) def test_create_network_with_provider_and_multiprovider_fail(self): data = {'network': {'name': 'net1', mpnet.SEGMENTS: [{pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1}], pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1, 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) res = network_req.get_response(self.api) self.assertEqual(400, res.status_int) def test_create_network_duplicate_full_segments(self): data = {'network': {'name': 'net1', mpnet.SEGMENTS: [{pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1}, {pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1}], 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) res = network_req.get_response(self.api) self.assertEqual(400, res.status_int) def test_create_network_duplicate_partial_segments(self): data = {'network': {'name': 'net1', mpnet.SEGMENTS: [{pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1'}, {pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1'}], 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) res = network_req.get_response(self.api) self.assertEqual(201, res.status_int) def test_release_network_segments(self): data = {'network': {'name': 'net1', 'admin_state_up': True, 'shared': False, pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1, 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) res = network_req.get_response(self.api) network = self.deserialize(self.fmt, res) network_id = network['network']['id'] segment = {driver_api.NETWORK_TYPE: 'vlan', driver_api.PHYSICAL_NETWORK: 'physnet2'} self.driver.type_manager.allocate_dynamic_segment( self.context, network_id, segment) dynamic_segment = segments_db.get_dynamic_segment( self.context, network_id, 'physnet2') self.assertEqual('vlan', dynamic_segment[driver_api.NETWORK_TYPE]) self.assertEqual('physnet2', dynamic_segment[driver_api.PHYSICAL_NETWORK]) self.assertGreater(dynamic_segment[driver_api.SEGMENTATION_ID], 0) with mock.patch.object(type_vlan.VlanTypeDriver, 'release_segment') as rs: segments_plugin_db.subscribe() req = self.new_delete_request('networks', network_id) res = req.get_response(self.api) self.assertEqual(2, rs.call_count) self.assertEqual([], segments_db.get_network_segments( self.context, network_id)) self.assertIsNone(segments_db.get_dynamic_segment( self.context, network_id, 'physnet2')) def test_release_segment_no_type_driver(self): data = {'network': {'name': 'net1', 'admin_state_up': True, 'shared': False, pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1, 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) res = network_req.get_response(self.api) network = self.deserialize(self.fmt, res) network_id = network['network']['id'] segment = {driver_api.NETWORK_TYPE: 'faketype', driver_api.PHYSICAL_NETWORK: 'physnet1', driver_api.ID: 1} with mock.patch('neutron.plugins.ml2.managers.LOG') as log: with mock.patch('neutron.plugins.ml2.managers.segments_db') as db: db.get_network_segments.return_value = (segment,) self.driver.type_manager.release_network_segments( self.context, network_id) log.error.assert_called_once_with( "Failed to release segment '%s' because " "network type is not supported.", segment) def test_create_provider_fail(self): segment = {pnet.NETWORK_TYPE: None, pnet.PHYSICAL_NETWORK: 'phys_net', pnet.SEGMENTATION_ID: None} with testtools.ExpectedException(exc.InvalidInput): self.driver.type_manager._process_provider_create(segment) def test_create_network_plugin(self): data = {'network': {'name': 'net1', 'admin_state_up': True, 'shared': False, pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1, 'tenant_id': 'tenant_one'}} def raise_mechanism_exc(*args, **kwargs): raise ml2_exc.MechanismDriverError( method='create_network_postcommit') with mock.patch('neutron.plugins.ml2.managers.MechanismManager.' 'create_network_precommit', new=raise_mechanism_exc): with testtools.ExpectedException(ml2_exc.MechanismDriverError): self.driver.create_network(self.context, data) def test_extend_dictionary_no_segments(self): network = dict(name='net_no_segment', id='5', tenant_id='tenant_one') self.driver.type_manager.extend_network_dict_provider(self.context, network) self.assertIsNone(network[pnet.NETWORK_TYPE]) self.assertIsNone(network[pnet.PHYSICAL_NETWORK]) self.assertIsNone(network[pnet.SEGMENTATION_ID]) class TestMl2AllowedAddressPairs(Ml2PluginV2TestCase, test_pair.TestAllowedAddressPairs): _extension_drivers = ['port_security'] def setUp(self, plugin=None): cfg.CONF.set_override('extension_drivers', self._extension_drivers, group='ml2') super(test_pair.TestAllowedAddressPairs, self).setUp( plugin=PLUGIN_NAME) class TestMl2PortSecurity(Ml2PluginV2TestCase): def setUp(self): cfg.CONF.set_override('extension_drivers', ['port_security'], group='ml2') cfg.CONF.set_override('enable_security_group', False, group='SECURITYGROUP') super(TestMl2PortSecurity, self).setUp() def test_port_update_without_security_groups(self): with self.port() as port: plugin = directory.get_plugin() ctx = context.get_admin_context() self.assertTrue(port['port']['port_security_enabled']) updated_port = plugin.update_port( ctx, port['port']['id'], {'port': {'port_security_enabled': False}}) self.assertFalse(updated_port['port_security_enabled']) class TestMl2HostsNetworkAccess(Ml2PluginV2TestCase): _mechanism_drivers = ['openvswitch', 'logger'] def setUp(self): super(TestMl2HostsNetworkAccess, self).setUp() helpers.register_ovs_agent( host='host1', bridge_mappings={'physnet1': 'br-eth-1'}) helpers.register_ovs_agent( host='host2', bridge_mappings={'physnet2': 'br-eth-2'}) helpers.register_ovs_agent( host='host3', bridge_mappings={'physnet3': 'br-eth-3'}) self.dhcp_agent1 = helpers.register_dhcp_agent( host='host1') self.dhcp_agent2 = helpers.register_dhcp_agent( host='host2') self.dhcp_agent3 = helpers.register_dhcp_agent( host='host3') self.dhcp_hosts = {'host1', 'host2', 'host3'} def test_filter_hosts_with_network_access(self): net = self.driver.create_network( self.context, {'network': {'name': 'net1', pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1, 'tenant_id': 'tenant_one', 'admin_state_up': True, 'shared': True}}) observeds = self.driver.filter_hosts_with_network_access( self.context, net['id'], self.dhcp_hosts) self.assertEqual({self.dhcp_agent1.host}, observeds) def test_filter_hosts_with_network_access_multi_segments(self): net = self.driver.create_network( self.context, {'network': {'name': 'net1', mpnet.SEGMENTS: [ {pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet1', pnet.SEGMENTATION_ID: 1}, {pnet.NETWORK_TYPE: 'vlan', pnet.PHYSICAL_NETWORK: 'physnet2', pnet.SEGMENTATION_ID: 2}], 'tenant_id': 'tenant_one', 'admin_state_up': True, 'shared': True}}) expecteds = {self.dhcp_agent1.host, self.dhcp_agent2.host} observeds = self.driver.filter_hosts_with_network_access( self.context, net['id'], self.dhcp_hosts) self.assertEqual(expecteds, observeds) def test_filter_hosts_with_network_access_not_supported(self): self.driver.mechanism_manager.host_filtering_supported = False observeds = self.driver.filter_hosts_with_network_access( self.context, 'fake_id', self.dhcp_hosts) self.assertEqual(self.dhcp_hosts, observeds) class DHCPOptsTestCase(test_dhcpopts.TestExtraDhcpOpt): def setUp(self, plugin=None): super(DHCPOptsTestCase, self).setUp(plugin=PLUGIN_NAME) class Ml2PluginV2FaultyDriverTestCase(test_plugin.NeutronDbPluginV2TestCase): def setUp(self): # Enable the test mechanism driver to ensure that # we can successfully call through to all mechanism # driver apis. cfg.CONF.set_override('mechanism_drivers', ['test', 'logger'], group='ml2') super(Ml2PluginV2FaultyDriverTestCase, self).setUp(PLUGIN_NAME) self.port_create_status = 'DOWN' class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase): def test_create_network_faulty(self): err_msg = "Some errors" with mock.patch.object(mech_test.TestMechanismDriver, 'create_network_postcommit', side_effect=(exc.InvalidInput( error_message=err_msg))): tenant_id = uuidutils.generate_uuid() data = {'network': {'name': 'net1', 'tenant_id': tenant_id}} req = self.new_create_request('networks', data) res = req.get_response(self.api) self.assertEqual(400, res.status_int) error = self.deserialize(self.fmt, res) self.assertEqual('InvalidInput', error['NeutronError']['type']) # Check the client can see the root cause of error. self.assertIn(err_msg, error['NeutronError']['message']) query_params = "tenant_id=%s" % tenant_id nets = self._list('networks', query_params=query_params) self.assertFalse(nets['networks']) def test_delete_network_faulty(self): with mock.patch.object(mech_test.TestMechanismDriver, 'delete_network_postcommit', side_effect=ml2_exc.MechanismDriverError): with mock.patch.object(mech_logger.LoggerMechanismDriver, 'delete_network_postcommit') as dnp: data = {'network': {'name': 'net1', 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) network_res = network_req.get_response(self.api) self.assertEqual(201, network_res.status_int) network = self.deserialize(self.fmt, network_res) net_id = network['network']['id'] req = self.new_delete_request('networks', net_id) res = req.get_response(self.api) self.assertEqual(204, res.status_int) # Test if other mechanism driver was called self.assertTrue(dnp.called) self._show('networks', net_id, expected_code=webob.exc.HTTPNotFound.code) def test_update_network_faulty(self): err_msg = "Some errors" with mock.patch.object(mech_test.TestMechanismDriver, 'update_network_postcommit', side_effect=(exc.InvalidInput( error_message=err_msg))): with mock.patch.object(mech_logger.LoggerMechanismDriver, 'update_network_postcommit') as unp: data = {'network': {'name': 'net1', 'tenant_id': 'tenant_one'}} network_req = self.new_create_request('networks', data) network_res = network_req.get_response(self.api) self.assertEqual(201, network_res.status_int) network = self.deserialize(self.fmt, network_res) net_id = network['network']['id'] new_name = 'a_brand_new_name' data = {'network': {'name': new_name}} req = self.new_update_request('networks', data, net_id) res = req.get_response(self.api) self.assertEqual(400, res.status_int) error = self.deserialize(self.fmt, res) self.assertEqual('InvalidInput', error['NeutronError']['type']) # Check the client can see the root cause of error. self.assertIn(err_msg, error['NeutronError']['message']) # Test if other mechanism driver was called self.assertTrue(unp.called) net = self._show('networks', net_id) self.assertEqual(new_name, net['network']['name']) self._delete('networks', net_id) def test_create_subnet_faulty(self): err_msg = "Some errors" with mock.patch.object(mech_test.TestMechanismDriver, 'create_subnet_postcommit', side_effect=(exc.InvalidInput( error_message=err_msg))): with self.network() as network: net_id = network['network']['id'] data = {'subnet': {'network_id': net_id, 'cidr': '10.0.20.0/24', 'ip_version': '4', 'name': 'subnet1', 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.20.1'}} req = self.new_create_request('subnets', data) res = req.get_response(self.api) self.assertEqual(400, res.status_int) error = self.deserialize(self.fmt, res) self.assertEqual('InvalidInput', error['NeutronError']['type']) # Check the client can see the root cause of error. self.assertIn(err_msg, error['NeutronError']['message']) query_params = "network_id=%s" % net_id subnets = self._list('subnets', query_params=query_params) self.assertFalse(subnets['subnets']) def test_delete_subnet_faulty(self): with mock.patch.object(mech_test.TestMechanismDriver, 'delete_subnet_postcommit', side_effect=ml2_exc.MechanismDriverError): with mock.patch.object(mech_logger.LoggerMechanismDriver, 'delete_subnet_postcommit') as dsp: with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.20.0/24', 'ip_version': '4', 'name': 'subnet1', 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.20.1'}} subnet_req = self.new_create_request('subnets', data) subnet_res = subnet_req.get_response(self.api) self.assertEqual(201, subnet_res.status_int) subnet = self.deserialize(self.fmt, subnet_res) subnet_id = subnet['subnet']['id'] req = self.new_delete_request('subnets', subnet_id) res = req.get_response(self.api) self.assertEqual(204, res.status_int) # Test if other mechanism driver was called self.assertTrue(dsp.called) self._show('subnets', subnet_id, expected_code=webob.exc.HTTPNotFound.code) def test_update_subnet_faulty(self): err_msg = "Some errors" with mock.patch.object(mech_test.TestMechanismDriver, 'update_subnet_postcommit', side_effect=(exc.InvalidInput( error_message=err_msg))): with mock.patch.object(mech_logger.LoggerMechanismDriver, 'update_subnet_postcommit') as usp: with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.20.0/24', 'ip_version': '4', 'name': 'subnet1', 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.20.1'}} subnet_req = self.new_create_request('subnets', data) subnet_res = subnet_req.get_response(self.api) self.assertEqual(201, subnet_res.status_int) subnet = self.deserialize(self.fmt, subnet_res) subnet_id = subnet['subnet']['id'] new_name = 'a_brand_new_name' data = {'subnet': {'name': new_name}} req = self.new_update_request('subnets', data, subnet_id) res = req.get_response(self.api) self.assertEqual(400, res.status_int) error = self.deserialize(self.fmt, res) self.assertEqual('InvalidInput', error['NeutronError']['type']) # Check the client can see the root cause of error. self.assertIn(err_msg, error['NeutronError']['message']) # Test if other mechanism driver was called self.assertTrue(usp.called) subnet = self._show('subnets', subnet_id) self.assertEqual(new_name, subnet['subnet']['name']) self._delete('subnets', subnet['subnet']['id']) def test_create_port_faulty(self): err_msg = "Some errors" with mock.patch.object(mech_test.TestMechanismDriver, 'create_port_postcommit', side_effect=(exc.InvalidInput( error_message=err_msg))): with self.network() as network: net_id = network['network']['id'] data = {'port': {'network_id': net_id, 'tenant_id': network['network']['tenant_id'], 'name': 'port1', 'admin_state_up': 1, 'fixed_ips': []}} req = self.new_create_request('ports', data) res = req.get_response(self.api) self.assertEqual(400, res.status_int) error = self.deserialize(self.fmt, res) self.assertEqual('InvalidInput', error['NeutronError']['type']) # Check the client can see the root cause of error. self.assertIn(err_msg, error['NeutronError']['message']) query_params = "network_id=%s" % net_id ports = self._list('ports', query_params=query_params) self.assertFalse(ports['ports']) def test_update_port_faulty(self): with mock.patch.object(mech_test.TestMechanismDriver, 'update_port_postcommit', side_effect=ml2_exc.MechanismDriverError): with mock.patch.object(mech_logger.LoggerMechanismDriver, 'update_port_postcommit') as upp: with self.network() as network: data = {'port': {'network_id': network['network']['id'], 'tenant_id': network['network']['tenant_id'], 'name': 'port1', 'admin_state_up': 1, 'fixed_ips': []}} port_req = self.new_create_request('ports', data) port_res = port_req.get_response(self.api) self.assertEqual(201, port_res.status_int) port = self.deserialize(self.fmt, port_res) port_id = port['port']['id'] new_name = 'a_brand_new_name' data = {'port': {'name': new_name}} req = self.new_update_request('ports', data, port_id) res = req.get_response(self.api) self.assertEqual(200, res.status_int) # Test if other mechanism driver was called self.assertTrue(upp.called) port = self._show('ports', port_id) self.assertEqual(new_name, port['port']['name']) self._delete('ports', port['port']['id']) def test_update_distributed_router_interface_port(self): """Test validate distributed router interface update succeeds.""" host_id = 'host' binding = models.DistributedPortBinding( port_id='port_id', host=host_id, router_id='old_router_id', vif_type=portbindings.VIF_TYPE_OVS, vnic_type=portbindings.VNIC_NORMAL, status=constants.PORT_STATUS_DOWN) with mock.patch.object( mech_test.TestMechanismDriver, 'update_port_postcommit', side_effect=ml2_exc.MechanismDriverError) as port_post,\ mock.patch.object( mech_test.TestMechanismDriver, 'update_port_precommit') as port_pre,\ mock.patch.object( ml2_db, 'get_distributed_port_bindings') as dist_bindings: dist_bindings.return_value = [binding] port_pre.return_value = True with self.network() as network: with self.subnet(network=network) as subnet: subnet_id = subnet['subnet']['id'] data = {'port': { 'network_id': network['network']['id'], 'tenant_id': network['network']['tenant_id'], 'name': 'port1', 'device_owner': constants.DEVICE_OWNER_DVR_INTERFACE, 'admin_state_up': 1, 'fixed_ips': [{'subnet_id': subnet_id}]}} port_req = self.new_create_request('ports', data) port_res = port_req.get_response(self.api) self.assertEqual(201, port_res.status_int) port = self.deserialize(self.fmt, port_res) port_id = port['port']['id'] new_name = 'a_brand_new_name' data = {'port': {'name': new_name}} req = self.new_update_request('ports', data, port_id) res = req.get_response(self.api) self.assertEqual(200, res.status_int) self.assertTrue(dist_bindings.called) self.assertTrue(port_pre.called) self.assertTrue(port_post.called) port = self._show('ports', port_id) self.assertEqual(new_name, port['port']['name']) class TestML2PluggableIPAM(test_ipam.UseIpamMixin, TestMl2SubnetsV2): def test_create_subnet_delete_subnet_call_ipam_driver(self): driver = 'neutron.ipam.drivers.neutrondb_ipam.driver.NeutronDbPool' gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' with mock.patch(driver) as driver_mock: request = mock.Mock() request.subnet_id = uuidutils.generate_uuid() request.subnet_cidr = netaddr.IPNetwork(cidr) request.allocation_pools = [] request.gateway_ip = netaddr.IPAddress(gateway_ip) request.tenant_id = uuidutils.generate_uuid() ipam_subnet = mock.Mock() ipam_subnet.get_details.return_value = request driver_mock().allocate_subnet.return_value = ipam_subnet self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr) driver_mock().allocate_subnet.assert_called_with(mock.ANY) driver_mock().remove_subnet.assert_called_with(request.subnet_id) def test_delete_subnet_deallocates_slaac_correctly(self): driver = 'neutron.ipam.drivers.neutrondb_ipam.driver.NeutronDbPool' with self.network() as network: with self.subnet(network=network, cidr='2001:100::0/64', ip_version=6, ipv6_ra_mode=constants.IPV6_SLAAC) as subnet: with self.port(subnet=subnet) as port: with mock.patch(driver) as driver_mock: # Validate that deletion of SLAAC allocation happens # via IPAM interface, i.e. ipam_subnet.deallocate is # called prior to subnet deletiong from db. self._delete('subnets', subnet['subnet']['id']) dealloc = driver_mock().get_subnet().deallocate dealloc.assert_called_with( port['port']['fixed_ips'][0]['ip_address']) driver_mock().remove_subnet.assert_called_with( subnet['subnet']['id']) class TestTransactionGuard(Ml2PluginV2TestCase): def test_delete_network_guard(self): plugin = directory.get_plugin() ctx = context.get_admin_context() with db_api.context_manager.writer.using(ctx): with testtools.ExpectedException(RuntimeError): plugin.delete_network(ctx, 'id') def test_delete_subnet_guard(self): plugin = directory.get_plugin() ctx = context.get_admin_context() with db_api.context_manager.writer.using(ctx): with testtools.ExpectedException(RuntimeError): plugin.delete_subnet(ctx, 'id') class TestML2Segments(Ml2PluginV2TestCase): def _reserve_segment(self, network, seg_id=None): segment = {'id': 'fake_id', 'network_id': network['network']['id'], 'tenant_id': network['network']['tenant_id'], driver_api.NETWORK_TYPE: 'vlan', driver_api.PHYSICAL_NETWORK: self.physnet} if seg_id: segment[driver_api.SEGMENTATION_ID] = seg_id self.driver._handle_segment_change( mock.ANY, events.PRECOMMIT_CREATE, segments_plugin.Plugin(), self.context, segment) if seg_id: # Assert it is not changed self.assertEqual(seg_id, segment[driver_api.SEGMENTATION_ID]) else: self.assertTrue(segment[driver_api.SEGMENTATION_ID] > 0) return segment def test_reserve_segment_success_with_partial_segment(self): with self.network() as network: self._reserve_segment(network) def test_reserve_segment_fail_with_duplicate_param(self): with self.network() as network: self._reserve_segment(network, 10) self.assertRaises( exc.VlanIdInUse, self._reserve_segment, network, 10) def test_create_network_mtu_on_precommit(self): with mock.patch.object(mech_test.TestMechanismDriver, 'create_network_precommit') as bmp: with mock.patch.object( self.driver, '_get_network_mtu') as mtu: mtu.return_value = 1100 with self.network() as network: self.assertIn('mtu', network['network']) all_args = bmp.call_args_list mech_context = all_args[0][0][0] self.assertEqual(1100, mech_context.__dict__['_network']['mtu']) def test_provider_info_update_network(self): with self.network() as network: network_id = network['network']['id'] plugin = directory.get_plugin() updated_network = plugin.update_network( self.context, network_id, {'network': {'name': 'test-net'}}) self.assertIn('provider:network_type', updated_network) self.assertIn('provider:physical_network', updated_network) self.assertIn('provider:segmentation_id', updated_network) def test_reserve_segment_update_network_mtu(self): with self.network() as network: network_id = network['network']['id'] with mock.patch.object( self.driver, '_get_network_mtu') as mtu: mtu.return_value = 100 self._reserve_segment(network) updated_network = self.driver.get_network(self.context, network_id) self.assertEqual(100, updated_network[driver_api.MTU]) mtu.return_value = 200 self._reserve_segment(network) updated_network = self.driver.get_network(self.context, network_id) self.assertEqual(200, updated_network[driver_api.MTU]) def _test_nofity_mechanism_manager(self, event): seg1 = {driver_api.NETWORK_TYPE: 'vlan', driver_api.PHYSICAL_NETWORK: self.physnet, driver_api.SEGMENTATION_ID: 1000} seg2 = {driver_api.NETWORK_TYPE: 'vlan', driver_api.PHYSICAL_NETWORK: self.physnet, driver_api.SEGMENTATION_ID: 1001} seg3 = {driver_api.NETWORK_TYPE: 'vlan', driver_api.PHYSICAL_NETWORK: self.physnet, driver_api.SEGMENTATION_ID: 1002} with self.network() as network: network = network['network'] for stale_seg in segments_db.get_network_segments(self.context, network['id']): segments_db.delete_network_segment(self.context, stale_seg['id']) for seg in [seg1, seg2, seg3]: seg['network_id'] = network['id'] segments_db.add_network_segment(self.context, network['id'], seg) self.net_context = None def record_network_context(net_context): self.net_context = net_context with mock.patch.object(managers.MechanismManager, 'update_network_precommit', side_effect=record_network_context): self.driver._handle_segment_change( mock.ANY, event, segments_plugin.Plugin(), self.context, seg1) # Make sure the mechanism manager can get the right amount of # segments of network self.assertEqual(3, len(self.net_context.current[mpnet.SEGMENTS])) def test_reserve_segment_nofity_mechanism_manager(self): self._test_nofity_mechanism_manager(events.PRECOMMIT_CREATE) def test_release_segment(self): with self.network() as network: segment = self._reserve_segment(network, 10) segment['network_id'] = network['network']['id'] self.driver._handle_segment_change( mock.ANY, events.PRECOMMIT_DELETE, mock.ANY, self.context, segment) # Check that the segment_id is not reserved segment = self._reserve_segment( network, segment[driver_api.SEGMENTATION_ID]) def test_release_segment_nofity_mechanism_manager(self): self._test_nofity_mechanism_manager(events.PRECOMMIT_DELETE) def test_prevent_delete_segment_with_tenant_port(self): fake_owner_compute = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' ml2_db.subscribe() plugin = directory.get_plugin() with self.port(device_owner=fake_owner_compute) as port: # add writer here to make sure that the following operations are # performed in the same session with db_api.context_manager.writer.using(self.context): binding = plugin._get_port( self.context, port['port']['id']).port_binding binding['host'] = 'host-ovs-no_filter' mech_context = driver_context.PortContext( plugin, self.context, port['port'], plugin.get_network(self.context, port['port']['network_id']), binding, None) plugin._bind_port_if_needed(mech_context) segment = segments_db.get_network_segments( self.context, port['port']['network_id'])[0] segment['network_id'] = port['port']['network_id'] self.assertRaises(c_exc.CallbackFailure, registry.notify, resources.SEGMENT, events.BEFORE_DELETE, mock.ANY, context=self.context, segment=segment) exist_port = self._show('ports', port['port']['id']) self.assertEqual(port['port']['id'], exist_port['port']['id']) neutron-12.1.1/neutron/tests/unit/plugins/ml2/__init__.py0000664000175000017500000000000013553660046023405 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/ml2/test_managers.py0000664000175000017500000001276713553660047024532 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.plugins.ml2 import api from oslo_config import cfg from oslo_db import exception as db_exc from neutron.plugins.ml2.common import exceptions as ml2_exc from neutron.plugins.ml2 import managers from neutron.tests import base from neutron.tests.unit.plugins.ml2._test_mech_agent import FakePortContext from neutron.tests.unit.plugins.ml2.drivers import mech_fake_agent from neutron.tests.unit.plugins.ml2.drivers import mechanism_test class TestManagers(base.BaseTestCase): def setUp(self): super(TestManagers, self).setUp() self.segment_id = 49 self.segments_to_bind = [{api.ID: "id1", 'network_type': 'vlan', 'physical_network': 'public', api.SEGMENTATION_ID: self.segment_id}] self.context = FakePortContext(None, None, self.segments_to_bind) self.context._binding = mock.Mock() self.context._binding_levels = [] self.context._new_bound_segment = self.segment_id self.context._next_segments_to_bind = None def test__check_driver_to_bind(self): cfg.CONF.set_override('mechanism_drivers', ['fake_agent'], group='ml2') manager = managers.MechanismManager() with mock.patch.object(mech_fake_agent.FakeAgentMechanismDriver, 'bind_port') as bind_port: manager._bind_port_level(self.context, 0, self.segments_to_bind) self.assertEqual(1, bind_port.call_count) def test__check_driver_to_bind2(self): cfg.CONF.set_override('mechanism_drivers', ['fake_agent'], group='ml2') manager = managers.MechanismManager() self.context._binding_levels = [mock.Mock(port_id="port_id", level=0, driver='fake_agent', segment_id=self.segment_id)] with mock.patch.object(mech_fake_agent.FakeAgentMechanismDriver, 'bind_port') as bind_port: manager._bind_port_level(self.context, 0, self.segments_to_bind) self.assertEqual(0, bind_port.call_count) @mock.patch.object(managers.LOG, 'critical') @mock.patch.object(managers.MechanismManager, '_driver_not_loaded') def test__driver_not_found(self, mock_not_loaded, mock_log): cfg.CONF.set_override('mechanism_drivers', ['invalidmech'], group='ml2') self.assertRaises(SystemExit, managers.MechanismManager) mock_not_loaded.assert_not_called() mock_log.assert_called_once_with("The following mechanism drivers " "were not found: %s" % set(['invalidmech'])) @mock.patch.object(managers.LOG, 'critical') @mock.patch.object(managers.MechanismManager, '_driver_not_found') def test__driver_not_loaded(self, mock_not_found, mock_log): cfg.CONF.set_override('mechanism_drivers', ['faulty_agent'], group='ml2') self.assertRaises(SystemExit, managers.MechanismManager) mock_log.assert_called_once_with(u"The '%(entrypoint)s' entrypoint " "could not be loaded for the " "following reason: '%(reason)s'.", {'entrypoint': mock.ANY, 'reason': mock.ANY}) class TestMechManager(base.BaseTestCase): def setUp(self): cfg.CONF.set_override('mechanism_drivers', ['test'], group='ml2') super(TestMechManager, self).setUp() self._manager = managers.MechanismManager() def _check_precommit(self, resource, operation): meth_name = "%s_%s_precommit" % (operation, resource) method = getattr(self._manager, meth_name) fake_ctxt = mock.Mock() fake_ctxt.current = {} with mock.patch.object(mechanism_test.TestMechanismDriver, meth_name, side_effect=db_exc.DBDeadlock()): self.assertRaises(db_exc.DBDeadlock, method, fake_ctxt) with mock.patch.object(mechanism_test.TestMechanismDriver, meth_name, side_effect=RuntimeError()): self.assertRaises(ml2_exc.MechanismDriverError, method, fake_ctxt) def _check_resource(self, resource): self._check_precommit(resource, 'create') self._check_precommit(resource, 'update') self._check_precommit(resource, 'delete') def test_network_precommit(self): self._check_resource('network') def test_subnet_precommit(self): self._check_resource('subnet') def test_port_precommit(self): self._check_resource('port') neutron-12.1.1/neutron/tests/unit/plugins/ml2/test_port_binding.py0000664000175000017500000003532013553660047025401 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import portbindings from neutron_lib import constants as const from neutron_lib import context from neutron_lib.plugins import directory from oslo_config import cfg from oslo_serialization import jsonutils from neutron.conf.plugins.ml2.drivers import driver_type from neutron.plugins.ml2 import driver_context from neutron.plugins.ml2 import models as ml2_models from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase): def setUp(self): # Enable the test mechanism driver to ensure that # we can successfully call through to all mechanism # driver apis. cfg.CONF.set_override('mechanism_drivers', ['logger', 'test'], 'ml2') # NOTE(dasm): ml2_type_vlan requires to be registered before used. # This piece was refactored and removed from .config, so it causes # a problem, when tests are executed with pdb. # There is no problem when tests are running without debugger. driver_type.register_ml2_drivers_vlan_opts() cfg.CONF.set_override('network_vlan_ranges', ['physnet1:1000:1099'], group='ml2_type_vlan') super(PortBindingTestCase, self).setUp('ml2') self.port_create_status = 'DOWN' self.plugin = directory.get_plugin() self.plugin.start_rpc_listeners() def _check_response(self, port, vif_type, has_port_filter, bound, status): self.assertEqual(vif_type, port[portbindings.VIF_TYPE]) vif_details = port[portbindings.VIF_DETAILS] port_status = port['status'] if bound: # TODO(rkukura): Replace with new VIF security details self.assertEqual(has_port_filter, vif_details[portbindings.CAP_PORT_FILTER]) self.assertEqual(status or 'DOWN', port_status) else: self.assertEqual('DOWN', port_status) def _test_port_binding(self, host, vif_type, has_port_filter, bound, status=None, network_type='local'): mac_address = 'aa:aa:aa:aa:aa:aa' host_arg = {portbindings.HOST_ID: host, 'mac_address': mac_address} with self.port(name='name', arg_list=(portbindings.HOST_ID,), **host_arg) as port: self._check_response(port['port'], vif_type, has_port_filter, bound, status) port_id = port['port']['id'] neutron_context = context.get_admin_context() details = self.plugin.endpoints[0].get_device_details( neutron_context, agent_id="theAgentId", device=port_id) if bound: self.assertEqual(network_type, details['network_type']) self.assertEqual(mac_address, details['mac_address']) else: self.assertNotIn('network_type', details) self.assertNotIn('mac_address', details) def test_unbound(self): self._test_port_binding("", portbindings.VIF_TYPE_UNBOUND, False, False) def test_binding_failed(self): self._test_port_binding("host-fail", portbindings.VIF_TYPE_BINDING_FAILED, False, False) def test_binding_no_filter(self): self._test_port_binding("host-ovs-no_filter", portbindings.VIF_TYPE_OVS, False, True) def test_binding_filter(self): self._test_port_binding("host-bridge-filter", portbindings.VIF_TYPE_BRIDGE, True, True) def test_binding_status_active(self): self._test_port_binding("host-ovs-filter-active", portbindings.VIF_TYPE_OVS, True, True, 'ACTIVE') def test_update_port_binding_no_binding(self): ctx = context.get_admin_context() with self.port(name='name') as port: # emulating concurrent binding deletion with ctx.session.begin(): for item in (ctx.session.query(ml2_models.PortBinding). filter_by(port_id=port['port']['id'])): ctx.session.delete(item) self.assertIsNone( self.plugin.get_bound_port_context(ctx, port['port']['id'])) def test_hierarchical_binding(self): self._test_port_binding("host-hierarchical", portbindings.VIF_TYPE_OVS, False, True, network_type='vlan') def test_get_bound_port_context_cache_hit(self): ctx = context.get_admin_context() with self.port(name='name') as port: cached_network_id = port['port']['network_id'] some_network = {'id': cached_network_id} cached_networks = {cached_network_id: some_network} self.plugin.get_network = mock.Mock(return_value=some_network) self.plugin.get_bound_port_context(ctx, port['port']['id'], cached_networks=cached_networks) self.assertFalse(self.plugin.get_network.called) def _test_update_port_binding(self, host, new_host=None): with mock.patch.object(self.plugin, '_notify_port_updated') as notify_mock: host_arg = {portbindings.HOST_ID: host} update_body = {'name': 'test_update'} if new_host is not None: update_body[portbindings.HOST_ID] = new_host with self.port(name='name', arg_list=(portbindings.HOST_ID,), **host_arg) as port: neutron_context = context.get_admin_context() updated_port = self._update('ports', port['port']['id'], {'port': update_body}, neutron_context=neutron_context) port_data = updated_port['port'] if new_host is not None: self.assertEqual(new_host, port_data[portbindings.HOST_ID]) else: self.assertEqual(host, port_data[portbindings.HOST_ID]) if new_host is not None and new_host != host: notify_mock.assert_called_once_with(mock.ANY) else: self.assertFalse(notify_mock.called) def test_update_with_new_host_binding_notifies_agent(self): self._test_update_port_binding('host-ovs-no_filter', 'host-bridge-filter') def test_update_with_same_host_binding_does_not_notify(self): self._test_update_port_binding('host-ovs-no_filter', 'host-ovs-no_filter') def test_update_without_binding_does_not_notify(self): self._test_update_port_binding('host-ovs-no_filter') def testt_update_from_empty_to_host_binding_notifies_agent(self): self._test_update_port_binding('', 'host-ovs-no_filter') def test_update_from_host_to_empty_binding_notifies_agent(self): self._test_update_port_binding('host-ovs-no_filter', '') def test_process_binding_port_host_id_changed(self): ctx = context.get_admin_context() plugin = directory.get_plugin() host_id = {portbindings.HOST_ID: 'host1'} with self.port(**host_id) as port: # Since the port is DOWN at first # It's necessary to make its status ACTIVE for this test plugin.update_port_status(ctx, port['port']['id'], const.PORT_STATUS_ACTIVE) attrs = port['port'] attrs['status'] = const.PORT_STATUS_ACTIVE original_port = attrs.copy() attrs['binding:host_id'] = 'host2' updated_port = attrs.copy() network = {'id': attrs['network_id']} binding = ml2_models.PortBinding( port_id=original_port['id'], host=original_port['binding:host_id'], vnic_type=original_port['binding:vnic_type'], profile=jsonutils.dumps(original_port['binding:profile']), vif_type=original_port['binding:vif_type'], vif_details=original_port['binding:vif_details']) levels = [] mech_context = driver_context.PortContext( plugin, ctx, updated_port, network, binding, levels, original_port=original_port) plugin._process_port_binding(mech_context, port['port']) self.assertEqual(const.PORT_STATUS_DOWN, updated_port['status']) port_dict = plugin.get_port(ctx, port['port']['id']) self.assertEqual(const.PORT_STATUS_DOWN, port_dict['status']) def test_distributed_binding(self): ctx = context.get_admin_context() with self.port(device_owner=const.DEVICE_OWNER_DVR_INTERFACE) as port: port_id = port['port']['id'] # Verify port's VIF type and status. self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED, port['port'][portbindings.VIF_TYPE]) self.assertEqual('DOWN', port['port']['status']) # Update port to bind for a host. self.plugin.update_distributed_port_binding(ctx, port_id, {'port': {portbindings.HOST_ID: 'host-ovs-no_filter', 'device_id': 'router1'}}) # Get port and verify VIF type and status unchanged. port = self._show('ports', port_id) self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED, port['port'][portbindings.VIF_TYPE]) self.assertEqual('DOWN', port['port']['status']) # Get and verify binding details for host details = self.plugin.endpoints[0].get_device_details( ctx, agent_id="theAgentId", device=port_id, host='host-ovs-no_filter') self.assertEqual('local', details['network_type']) # Get port and verify VIF type and changed status. port = self._show('ports', port_id) self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED, port['port'][portbindings.VIF_TYPE]) self.assertEqual('BUILD', port['port']['status']) # Mark device up. self.plugin.endpoints[0].update_device_up( ctx, agent_id="theAgentId", device=port_id, host='host-ovs-no_filter') # Get port and verify VIF type and changed status. port = self._show('ports', port_id) self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED, port['port'][portbindings.VIF_TYPE]) self.assertEqual('ACTIVE', port['port']['status']) # Mark device down. self.plugin.endpoints[0].update_device_down( ctx, agent_id="theAgentId", device=port_id, host='host-ovs-no_filter') # Get port and verify VIF type and changed status. port = self._show('ports', port_id) self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED, port['port'][portbindings.VIF_TYPE]) self.assertEqual('DOWN', port['port']['status']) def test_distributed_binding_multi_host_status(self): ctx = context.get_admin_context() with self.port(device_owner=const.DEVICE_OWNER_DVR_INTERFACE) as port: port_id = port['port']['id'] # Update port to bind for 1st host. self.plugin.update_distributed_port_binding(ctx, port_id, {'port': {portbindings.HOST_ID: 'host-ovs-no_filter', 'device_id': 'router1'}}) # Mark 1st device up. self.plugin.endpoints[0].update_device_up( ctx, agent_id="theAgentId", device=port_id, host='host-ovs-no_filter') # Get port and verify status is ACTIVE. port = self._show('ports', port_id) self.assertEqual('ACTIVE', port['port']['status']) # Update port to bind for a 2nd host. self.plugin.update_distributed_port_binding(ctx, port_id, {'port': {portbindings.HOST_ID: 'host-bridge-filter', 'device_id': 'router1'}}) # Mark 2nd device up. self.plugin.endpoints[0].update_device_up( ctx, agent_id="the2ndAgentId", device=port_id, host='host-bridge-filter') # Get port and verify status unchanged. port = self._show('ports', port_id) self.assertEqual('ACTIVE', port['port']['status']) # Mark 1st device down. self.plugin.endpoints[0].update_device_down( ctx, agent_id="theAgentId", device=port_id, host='host-ovs-no_filter') # Get port and verify status unchanged. port = self._show('ports', port_id) self.assertEqual('ACTIVE', port['port']['status']) # Mark 2nd device down. self.plugin.endpoints[0].update_device_down( ctx, agent_id="the2ndAgentId", device=port_id, host='host-bridge-filter') # Get port and verify status is DOWN. port = self._show('ports', port_id) self.assertEqual('DOWN', port['port']['status']) def test_distributed_binding_update_unbound_host(self): ctx = context.get_admin_context() with self.port(device_owner=const.DEVICE_OWNER_DVR_INTERFACE) as port: port_id = port['port']['id'] # Mark device up without first binding on host. self.plugin.endpoints[0].update_device_up( ctx, agent_id="theAgentId", device=port_id, host='host-ovs-no_filter') # Get port and verify status is still DOWN. port = self._show('ports', port_id) self.assertEqual('DOWN', port['port']['status']) neutron-12.1.1/neutron/tests/unit/plugins/ml2/test_ovo_rpc.py0000664000175000017500000001135213553660046024370 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from neutron_lib.plugins import directory from neutron.objects import network from neutron.objects import securitygroup from neutron.objects import subnet from neutron.plugins.ml2 import ovo_rpc from neutron.tests.unit.plugins.ml2 import test_plugin class OVOServerRpcInterfaceTestCase(test_plugin.Ml2PluginV2TestCase): def setUp(self): super(OVOServerRpcInterfaceTestCase, self).setUp() self.plugin = directory.get_plugin() self.ctx = context.get_admin_context() self.received = [] receive = lambda s, ctx, obs, evt: self.received.append((obs[0], evt)) mock.patch('neutron.api.rpc.handlers.resources_rpc.' 'ResourcesPushRpcApi.push', new=receive).start() # base case blocks the handler self.ovo_push_interface_p.stop() self.plugin.ovo_notifier = ovo_rpc.OVOServerRpcInterface() def _assert_object_received(self, ovotype, oid=None, event=None): self.plugin.ovo_notifier.wait() for obj, evt in self.received: if isinstance(obj, ovotype): if (obj.id == oid or not oid) and (not event or event == evt): return obj self.fail("Could not find OVO %s with ID %s in %s" % (ovotype, oid, self.received)) def test_network_lifecycle(self): with self.network() as n: self._assert_object_received(network.Network, n['network']['id'], 'updated') self.plugin.delete_network(self.ctx, n['network']['id']) self._assert_object_received(network.Network, n['network']['id'], 'deleted') def test_subnet_lifecycle(self): with self.subnet() as s: self._assert_object_received(subnet.Subnet, s['subnet']['id'], 'updated') self.plugin.delete_subnet(self.ctx, s['subnet']['id']) self._assert_object_received(subnet.Subnet, s['subnet']['id'], 'deleted') def test_securitygroup_and_rule_lifecycle(self): # making a network makes a default security group with self.network() as n: sg = self._assert_object_received(securitygroup.SecurityGroup, event='updated') self.assertEqual(sg.tenant_id, n['network']['tenant_id']) sgr = self.plugin.create_security_group_rule(self.ctx, {'security_group_rule': {'security_group_id': sg.id, 'tenant_id': sg.tenant_id, 'port_range_min': None, 'port_range_max': None, 'remote_ip_prefix': None, 'remote_group_id': None, 'protocol': None, 'direction': None, 'ethertype': 'IPv4'}}) self._assert_object_received( securitygroup.SecurityGroupRule, sgr['id'], 'updated') self.plugin.delete_security_group_rule(self.ctx, sgr['id']) self._assert_object_received( securitygroup.SecurityGroupRule, sgr['id'], 'deleted') self.plugin.delete_security_group(self.ctx, sg.id) self._assert_object_received(securitygroup.SecurityGroup, sg.id, 'deleted') def test_transaction_state_error_doesnt_notify(self): # running in a transaction should cause it to skip notification since # fresh reads aren't possible. with self.ctx.session.begin(): self.plugin.create_security_group( self.ctx, {'security_group': {'tenant_id': 'test', 'description': 'desc', 'name': 'test'}}) self.assertEqual([], self.received) neutron-12.1.1/neutron/tests/unit/plugins/ml2/test_tracked_resources.py0000664000175000017500000003617513553660046026442 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from neutron_lib import fixture from oslo_utils import uuidutils from neutron.db.quota import api as quota_db_api from neutron.tests.unit.api import test_extensions from neutron.tests.unit.extensions import test_l3 from neutron.tests.unit.extensions import test_securitygroup from neutron.tests.unit.plugins.ml2 import base as ml2_base from neutron.tests.unit.plugins.ml2 import test_plugin class SgTestCaseWrapper(test_securitygroup.SecurityGroupDBTestCase): # This wrapper class enables Ml2PluginV2TestCase to correctly call the # setup method in SecurityGroupDBTestCase which does not accept the # service_plugins keyword parameter. def setUp(self, plugin, **kwargs): super(SgTestCaseWrapper, self).setUp(plugin) class BaseTestTrackedResources(test_plugin.Ml2PluginV2TestCase, SgTestCaseWrapper): def setUp(self): self.ctx = context.get_admin_context() super(BaseTestTrackedResources, self).setUp() self._tenant_id = uuidutils.generate_uuid() def _test_init(self, resource_name): quota_db_api.set_quota_usage( self.ctx, resource_name, self._tenant_id) class BaseTestEventHandler(object): def setUp(self): # Prevent noise from default security group operations def_sec_group_patch = mock.patch( 'neutron.db.securitygroups_db.SecurityGroupDbMixin.' '_ensure_default_security_group') def_sec_group_patch.start() get_sec_group_port_patch = mock.patch( 'neutron.db.securitygroups_db.SecurityGroupDbMixin.' '_get_security_groups_on_port') get_sec_group_port_patch.start() handler_patch = mock.patch( 'neutron.quota.resource.TrackedResource._db_event_handler') self.handler_mock = handler_patch.start() super(BaseTestEventHandler, self).setUp() def _verify_event_handler_calls(self, data, expected_call_count=1): if not hasattr(data, '__iter__') or isinstance(data, dict): data = [data] self.assertEqual(expected_call_count, self.handler_mock.call_count) call_idx = -1 for item in data: if item: model = self.handler_mock.call_args_list[call_idx][0][-1] self.assertEqual(model['id'], item['id']) self.assertEqual(model['tenant_id'], item['tenant_id']) call_idx = call_idx - 1 class TestTrackedResourcesEventHandler(BaseTestEventHandler, BaseTestTrackedResources): def test_create_delete_network_triggers_event(self): self._test_init('network') net = self._make_network('json', 'meh', True)['network'] self._verify_event_handler_calls(net) self._delete('networks', net['id']) self._verify_event_handler_calls(net, expected_call_count=2) def test_create_delete_port_triggers_event(self): self._test_init('port') net = self._make_network('json', 'meh', True)['network'] port = self._make_port('json', net['id'])['port'] # Expecting 2 calls - 1 for the network, 1 for the port self._verify_event_handler_calls(port, expected_call_count=2) self._delete('ports', port['id']) self._verify_event_handler_calls(port, expected_call_count=3) def test_create_delete_subnet_triggers_event(self): self._test_init('subnet') net = self._make_network('json', 'meh', True) subnet = self._make_subnet('json', net, '10.0.0.1', '10.0.0.0/24')['subnet'] # Expecting 2 calls - 1 for the network, 1 for the subnet self._verify_event_handler_calls([subnet, net['network']], expected_call_count=2) self._delete('subnets', subnet['id']) self._verify_event_handler_calls(subnet, expected_call_count=3) def test_create_delete_network_with_subnet_triggers_event(self): self._test_init('network') self._test_init('subnet') net = self._make_network('json', 'meh', True) subnet = self._make_subnet('json', net, '10.0.0.1', '10.0.0.0/24')['subnet'] # Expecting 2 calls - 1 for the network, 1 for the subnet self._verify_event_handler_calls([subnet, net['network']], expected_call_count=2) self._delete('networks', net['network']['id']) # Expecting 2 more calls - 1 for the network, 1 for the subnet self._verify_event_handler_calls([net['network'], subnet], expected_call_count=4) def test_create_delete_subnetpool_triggers_event(self): self._test_init('subnetpool') pool = self._make_subnetpool('json', ['10.0.0.0/8'], name='meh', tenant_id=self._tenant_id)['subnetpool'] self._verify_event_handler_calls(pool) self._delete('subnetpools', pool['id']) self._verify_event_handler_calls(pool, expected_call_count=2) def test_create_delete_securitygroup_triggers_event(self): self._test_init('security_group') sec_group = self._make_security_group( 'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group'] # When a security group is created it also creates 2 rules, therefore # there will be three calls and we need to verify the first self._verify_event_handler_calls([None, None, sec_group], expected_call_count=3) self._delete('security-groups', sec_group['id']) # When a security group is deleted it also removes the 2 rules # generated upon creation self._verify_event_handler_calls(sec_group, expected_call_count=6) def test_create_delete_securitygrouprule_triggers_event(self): self._test_init('security_group_rule') sec_group = self._make_security_group( 'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group'] rule_req = self._build_security_group_rule( sec_group['id'], 'ingress', 'TCP', tenant_id=self._tenant_id) sec_group_rule = self._make_security_group_rule( 'json', rule_req)['security_group_rule'] # When a security group is created it also creates 2 rules, therefore # there will be four calls in total to the event handler self._verify_event_handler_calls(sec_group_rule, expected_call_count=4) self._delete('security-group-rules', sec_group_rule['id']) self._verify_event_handler_calls(sec_group_rule, expected_call_count=5) class TestL3ResourcesEventHandler(BaseTestEventHandler, ml2_base.ML2TestFramework, test_l3.L3NatTestCaseMixin): def setUp(self): super(TestL3ResourcesEventHandler, self).setUp() self.useFixture(fixture.APIDefinitionFixture()) ext_mgr = test_l3.L3TestExtensionManager() self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) def test_create_delete_floating_ip_triggers_event(self): net = self._make_network('json', 'meh', True) subnet = self._make_subnet('json', net, '14.0.0.1', '14.0.0.0/24')['subnet'] self._set_net_external(subnet['network_id']) floatingip = self._make_floatingip('json', subnet['network_id']) internal_port = self._show( 'ports', floatingip['floatingip']['port_id'])['ports'][0] # When a floatingip is created it also creates port, therefore # there will be four calls in total to the event handler self._verify_event_handler_calls(floatingip['floatingip'], expected_call_count=4) self._delete('floatingips', floatingip['floatingip']['id']) # Expecting 2 more calls - 1 for the port, 1 for the floatingip self._verify_event_handler_calls( [internal_port, floatingip['floatingip']], expected_call_count=6) class TestTrackedResources(BaseTestTrackedResources): def _verify_dirty_bit(self, resource_name, expected_value=True): usage = quota_db_api.get_quota_usage_by_resource_and_tenant( self.ctx, resource_name, self._tenant_id) self.assertEqual(expected_value, usage.dirty) def test_create_delete_network_marks_dirty(self): self._test_init('network') net = self._make_network('json', 'meh', True)['network'] self._verify_dirty_bit('network') # Clear the dirty bit quota_db_api.set_quota_usage_dirty( self.ctx, 'network', self._tenant_id, dirty=False) self._delete('networks', net['id']) self._verify_dirty_bit('network') def test_list_networks_clears_dirty(self): self._test_init('network') net = self._make_network('json', 'meh', True)['network'] self.ctx.tenant_id = net['tenant_id'] self._list('networks', neutron_context=self.ctx) self._verify_dirty_bit('network', expected_value=False) def test_create_delete_port_marks_dirty(self): self._test_init('port') net = self._make_network('json', 'meh', True)['network'] port = self._make_port('json', net['id'])['port'] self._verify_dirty_bit('port') # Clear the dirty bit quota_db_api.set_quota_usage_dirty( self.ctx, 'port', self._tenant_id, dirty=False) self._delete('ports', port['id']) self._verify_dirty_bit('port') def test_list_ports_clears_dirty(self): self._test_init('port') net = self._make_network('json', 'meh', True)['network'] port = self._make_port('json', net['id'])['port'] self.ctx.tenant_id = port['tenant_id'] self._list('ports', neutron_context=self.ctx) self._verify_dirty_bit('port', expected_value=False) def test_create_delete_subnet_marks_dirty(self): self._test_init('subnet') net = self._make_network('json', 'meh', True) subnet = self._make_subnet('json', net, '10.0.0.1', '10.0.0.0/24')['subnet'] self._verify_dirty_bit('subnet') # Clear the dirty bit quota_db_api.set_quota_usage_dirty( self.ctx, 'subnet', self._tenant_id, dirty=False) self._delete('subnets', subnet['id']) self._verify_dirty_bit('subnet') def test_create_delete_network_with_subnet_marks_dirty(self): self._test_init('network') self._test_init('subnet') net = self._make_network('json', 'meh', True) self._make_subnet('json', net, '10.0.0.1', '10.0.0.0/24')['subnet'] self._verify_dirty_bit('subnet') # Clear the dirty bit quota_db_api.set_quota_usage_dirty( self.ctx, 'subnet', self._tenant_id, dirty=False) self._delete('networks', net['network']['id']) self._verify_dirty_bit('network') self._verify_dirty_bit('subnet') def test_list_subnets_clears_dirty(self): self._test_init('subnet') net = self._make_network('json', 'meh', True) subnet = self._make_subnet('json', net, '10.0.0.1', '10.0.0.0/24')['subnet'] self.ctx.tenant_id = subnet['tenant_id'] self._list('subnets', neutron_context=self.ctx) self._verify_dirty_bit('subnet', expected_value=False) def test_create_delete_subnetpool_marks_dirty(self): self._test_init('subnetpool') pool = self._make_subnetpool('json', ['10.0.0.0/8'], name='meh', tenant_id=self._tenant_id)['subnetpool'] self._verify_dirty_bit('subnetpool') # Clear the dirty bit quota_db_api.set_quota_usage_dirty( self.ctx, 'subnetpool', self._tenant_id, dirty=False) self._delete('subnetpools', pool['id']) self._verify_dirty_bit('subnetpool') def test_list_subnetpools_clears_dirty(self): self._test_init('subnetpool') pool = self._make_subnetpool('json', ['10.0.0.0/8'], name='meh', tenant_id=self._tenant_id)['subnetpool'] self.ctx.tenant_id = pool['tenant_id'] self._list('subnetpools', neutron_context=self.ctx) self._verify_dirty_bit('subnetpool', expected_value=False) def test_create_delete_securitygroup_marks_dirty(self): self._test_init('security_group') sec_group = self._make_security_group( 'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group'] self._verify_dirty_bit('security_group') # Clear the dirty bit quota_db_api.set_quota_usage_dirty( self.ctx, 'security_group', self._tenant_id, dirty=False) self._delete('security-groups', sec_group['id']) self._verify_dirty_bit('security_group') def test_list_securitygroups_clears_dirty(self): self._test_init('security_group') self._make_security_group( 'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group'] self.ctx.tenant_id = self._tenant_id self._list('security-groups', neutron_context=self.ctx) self._verify_dirty_bit('security_group', expected_value=False) def test_create_delete_securitygrouprule_marks_dirty(self): self._test_init('security_group_rule') sec_group = self._make_security_group( 'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group'] rule_req = self._build_security_group_rule( sec_group['id'], 'ingress', 'TCP', tenant_id=self._tenant_id) sec_group_rule = self._make_security_group_rule( 'json', rule_req)['security_group_rule'] self._verify_dirty_bit('security_group_rule') # Clear the dirty bit quota_db_api.set_quota_usage_dirty( self.ctx, 'security_group_rule', self._tenant_id, dirty=False) self._delete('security-group-rules', sec_group_rule['id']) self._verify_dirty_bit('security_group_rule') def test_list_securitygrouprules_clears_dirty(self): self._test_init('security_group_rule') self._make_security_group( 'json', 'meh', 'meh', tenant_id=self._tenant_id)['security_group'] # As the security group create operation also creates 2 security group # rules there is no need to explicitly create any rule self.ctx.tenant_id = self._tenant_id self._list('security-group-rules', neutron_context=self.ctx) self._verify_dirty_bit('security_group_rule', expected_value=False) neutron-12.1.1/neutron/tests/unit/plugins/ml2/test_driver_context.py0000664000175000017500000001026413553660046025761 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron.plugins.ml2 import driver_context from neutron.plugins.ml2 import models from neutron.tests import base class TestPortContext(base.BaseTestCase): # REVISIT(rkukura): These was originally for DvrPortContext tests, # but DvrPortContext functionality has been folded into the # regular PortContext class. Tests for non-DVR-specific # functionality are needed here as well. def test_host(self): plugin = mock.Mock() plugin_context = mock.Mock() network = mock.MagicMock() binding = models.PortBinding() port = {'device_owner': constants.DEVICE_OWNER_DVR_INTERFACE} binding.host = 'foohost' with mock.patch.object(driver_context.segments_db, 'get_network_segments'): ctx = driver_context.PortContext(plugin, plugin_context, port, network, binding, None) self.assertEqual('foohost', ctx.host) def test_host_super(self): plugin = mock.Mock() plugin_context = mock.Mock() network = mock.MagicMock() binding = models.PortBinding() port = {'device_owner': constants.DEVICE_OWNER_COMPUTE_PREFIX, portbindings.HOST_ID: 'host'} binding.host = 'foohost' with mock.patch.object(driver_context.segments_db, 'get_network_segments'): ctx = driver_context.PortContext(plugin, plugin_context, port, network, binding, None) self.assertEqual('host', ctx.host) def test_status(self): plugin = mock.Mock() plugin_context = mock.Mock() network = mock.MagicMock() binding = models.PortBinding() port = {'device_owner': constants.DEVICE_OWNER_DVR_INTERFACE} binding.status = 'foostatus' with mock.patch.object(driver_context.segments_db, 'get_network_segments'): ctx = driver_context.PortContext(plugin, plugin_context, port, network, binding, None) self.assertEqual('foostatus', ctx.status) def test_status_super(self): plugin = mock.Mock() plugin_context = mock.Mock() network = mock.MagicMock() binding = models.PortBinding() port = {'device_owner': constants.DEVICE_OWNER_COMPUTE_PREFIX, 'status': 'status'} binding.status = 'foostatus' with mock.patch.object(driver_context.segments_db, 'get_network_segments'): ctx = driver_context.PortContext(plugin, plugin_context, port, network, binding, None) self.assertEqual('status', ctx.status) neutron-12.1.1/neutron/tests/unit/plugins/ml2/test_security_group.py0000664000175000017500000001650013553660047026005 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # Copyright 2013, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import math import mock from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as const from neutron_lib import context from neutron_lib import fixture from neutron_lib.plugins import directory from neutron.extensions import securitygroup as ext_sg from neutron.tests.unit.agent import test_securitygroups_rpc as test_sg_rpc from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit.extensions import test_securitygroup as test_sg NOTIFIER = 'neutron.plugins.ml2.rpc.AgentNotifierApi' class Ml2SecurityGroupsTestCase(test_sg.SecurityGroupDBTestCase): def setUp(self, plugin=None): test_sg_rpc.set_firewall_driver(test_sg_rpc.FIREWALL_HYBRID_DRIVER) notifier_p = mock.patch(NOTIFIER) notifier_cls = notifier_p.start() self.notifier = mock.Mock() notifier_cls.return_value = self.notifier self.useFixture(fixture.APIDefinitionFixture()) super(Ml2SecurityGroupsTestCase, self).setUp('ml2') class TestMl2SecurityGroups(Ml2SecurityGroupsTestCase, test_sg.TestSecurityGroups, test_sg_rpc.SGNotificationTestMixin): def setUp(self): super(TestMl2SecurityGroups, self).setUp() self.ctx = context.get_admin_context() plugin = directory.get_plugin() plugin.start_rpc_listeners() def _make_port_with_new_sec_group(self, net_id): sg = self._make_security_group(self.fmt, 'name', 'desc') port = self._make_port( self.fmt, net_id, security_groups=[sg['security_group']['id']]) return port['port'] def _make_port_without_sec_group(self, net_id): port = self._make_port( self.fmt, net_id, security_groups=[]) return port['port'] def test_security_group_get_ports_from_devices(self): with self.network() as n: with self.subnet(n): orig_ports = [ self._make_port_with_new_sec_group(n['network']['id']), self._make_port_with_new_sec_group(n['network']['id']), self._make_port_without_sec_group(n['network']['id']) ] plugin = directory.get_plugin() # should match full ID and starting chars ports = plugin.get_ports_from_devices(self.ctx, [orig_ports[0]['id'], orig_ports[1]['id'][0:8], orig_ports[2]['id']]) self.assertEqual(len(orig_ports), len(ports)) for port_dict in ports: p = next(p for p in orig_ports if p['id'] == port_dict['id']) self.assertEqual(p['id'], port_dict['id']) self.assertEqual(p['security_groups'], port_dict[ext_sg.SECURITYGROUPS]) self.assertEqual([], port_dict['security_group_rules']) self.assertEqual([p['fixed_ips'][0]['ip_address']], port_dict['fixed_ips']) self._delete('ports', p['id']) def test_security_group_get_ports_from_devices_with_bad_id(self): plugin = directory.get_plugin() ports = plugin.get_ports_from_devices(self.ctx, ['bad_device_id']) self.assertFalse(ports) def test_security_group_no_db_calls_with_no_ports(self): plugin = directory.get_plugin() with mock.patch( 'neutron.plugins.ml2.db.get_sg_ids_grouped_by_port' ) as get_mock: self.assertFalse(plugin.get_ports_from_devices(self.ctx, [])) self.assertFalse(get_mock.called) def test_large_port_count_broken_into_parts(self): plugin = directory.get_plugin() max_ports_per_query = 5 ports_to_query = 73 for max_ports_per_query in (1, 2, 5, 7, 9, 31): with mock.patch('neutron.plugins.ml2.db.MAX_PORTS_PER_QUERY', new=max_ports_per_query),\ mock.patch( 'neutron.plugins.ml2.db.get_sg_ids_grouped_by_port', return_value={}) as get_mock: plugin.get_ports_from_devices(self.ctx, ['%s%s' % (const.TAP_DEVICE_PREFIX, i) for i in range(ports_to_query)]) all_call_args = [x[1][1] for x in get_mock.mock_calls] last_call_args = all_call_args.pop() # all but last should be getting MAX_PORTS_PER_QUERY ports self.assertTrue( all(map(lambda x: len(x) == max_ports_per_query, all_call_args)) ) remaining = ports_to_query % max_ports_per_query if remaining: self.assertEqual(remaining, len(last_call_args)) # should be broken into ceil(total/MAX_PORTS_PER_QUERY) calls self.assertEqual( math.ceil(ports_to_query / float(max_ports_per_query)), get_mock.call_count ) def test_full_uuids_skip_port_id_lookup(self): plugin = directory.get_plugin() # when full UUIDs are provided, the _or statement should only # have one matching 'IN' criteria for all of the IDs with mock.patch('neutron.plugins.ml2.db.or_') as or_mock,\ mock.patch('sqlalchemy.orm.Session.query') as qmock: fmock = qmock.return_value.outerjoin.return_value.filter # return no ports to exit the method early since we are mocking # the query fmock.return_value = [] plugin.get_ports_from_devices(self.ctx, [test_base._uuid(), test_base._uuid()]) # the or_ function should only have one argument or_mock.assert_called_once_with(mock.ANY) def test_security_groups_created_outside_transaction(self): def record_after_state(r, e, t, context, *args, **kwargs): self.was_active = context.session.is_active registry.subscribe(record_after_state, resources.SECURITY_GROUP, events.AFTER_CREATE) with self.subnet() as s: self.assertFalse(self.was_active) self._delete( 'security-groups', self._list('security-groups')['security_groups'][0]['id']) with self.port(subnet=s): self.assertFalse(self.was_active) class TestMl2SGServerRpcCallBack( Ml2SecurityGroupsTestCase, test_sg_rpc.SGServerRpcCallBackTestCase): pass neutron-12.1.1/neutron/tests/unit/plugins/common/0000775000175000017500000000000013553660157022107 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/plugins/common/test_utils.py0000664000175000017500000001214713553660047024663 0ustar zuulzuul00000000000000# Copyright (c) 2015 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import hashlib import mock from neutron_lib import constants from neutron_lib import exceptions import testtools from neutron.db import l3_db from neutron.plugins.common import utils from neutron.tests import base LONG_NAME1 = "A_REALLY_LONG_INTERFACE_NAME1" LONG_NAME2 = "A_REALLY_LONG_INTERFACE_NAME2" SHORT_NAME = "SHORT" MOCKED_HASH = "mockedhash" class MockSHA(object): def hexdigest(self): return MOCKED_HASH class TestUtils(base.BaseTestCase): @mock.patch.object(hashlib, 'sha1', return_value=MockSHA()) def test_get_interface_name(self, mock_sha1): prefix = "pre-" prefix_long = "long_prefix" prefix_exceeds_max_dev_len = "much_too_long_prefix" hash_used = MOCKED_HASH[0:6] self.assertEqual("A_REALLY_" + hash_used, utils.get_interface_name(LONG_NAME1)) self.assertEqual("SHORT", utils.get_interface_name(SHORT_NAME)) self.assertEqual("pre-A_REA" + hash_used, utils.get_interface_name(LONG_NAME1, prefix=prefix)) self.assertEqual("pre-SHORT", utils.get_interface_name(SHORT_NAME, prefix=prefix)) # len(prefix) > max_device_len - len(hash_used) self.assertRaises(ValueError, utils.get_interface_name, SHORT_NAME, prefix_long) # len(prefix) > max_device_len self.assertRaises(ValueError, utils.get_interface_name, SHORT_NAME, prefix=prefix_exceeds_max_dev_len) def test_get_interface_uniqueness(self): prefix = "prefix-" if_prefix1 = utils.get_interface_name(LONG_NAME1, prefix=prefix) if_prefix2 = utils.get_interface_name(LONG_NAME2, prefix=prefix) self.assertNotEqual(if_prefix1, if_prefix2) @mock.patch.object(hashlib, 'sha1', return_value=MockSHA()) def test_get_interface_max_len(self, mock_sha1): self.assertEqual(constants.DEVICE_NAME_MAX_LEN, len(utils.get_interface_name(LONG_NAME1))) self.assertEqual(10, len(utils.get_interface_name(LONG_NAME1, max_len=10))) self.assertEqual(12, len(utils.get_interface_name(LONG_NAME1, prefix="pre-", max_len=12))) def test_delete_port_on_error(self): core_plugin, context = mock.Mock(), mock.Mock() port_id = 'pid' with testtools.ExpectedException(ValueError): with utils.delete_port_on_error(core_plugin, context, port_id): raise ValueError() core_plugin.delete_port.assert_called_once_with(context, port_id, l3_port_check=False) def test_delete_port_on_error_fail_port_delete(self): core_plugin, context = mock.Mock(), mock.Mock() core_plugin.delete_port.side_effect = TypeError() port_id = 'pid' with testtools.ExpectedException(ValueError): with utils.delete_port_on_error(core_plugin, context, port_id): raise ValueError() core_plugin.delete_port.assert_called_once_with(context, port_id, l3_port_check=False) def test_delete_port_on_error_port_does_not_exist(self): core_plugin, context = mock.Mock(), mock.Mock() port_id = 'pid' core_plugin.delete_port.side_effect = exceptions.PortNotFound( port_id=port_id) with testtools.ExpectedException(exceptions.PortNotFound): with utils.delete_port_on_error(core_plugin, context, port_id): raise exceptions.PortNotFound(port_id=port_id) core_plugin.delete_port.assert_called_once_with(context, port_id, l3_port_check=False) @mock.patch.object(l3_db.L3_NAT_dbonly_mixin, '_check_router_port') def test_update_port_on_error(self, mock_check): core_plugin, context = mock.Mock(), mock.Mock() port = mock_check.return_value = {'device_owner': 'xxxxxxxx'} revert_value = {'device_id': '', 'device_owner': port['device_owner']} with testtools.ExpectedException(ValueError): with utils.update_port_on_error(core_plugin, context, 1, revert_value): raise ValueError() core_plugin.update_port.assert_called_once_with( context, 1, {'port': revert_value}) neutron-12.1.1/neutron/tests/unit/plugins/common/__init__.py0000664000175000017500000000000013553660047024204 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/_test_extension_portbindings.py0000664000175000017500000004153713553660047025514 0ustar zuulzuul00000000000000# Copyright 2013 NEC Corporation # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import context from neutron_lib.plugins import directory from oslo_config import cfg from six.moves import http_client as httplib from webob import exc from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit import dummy_plugin class PortBindingsTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): # VIF_TYPE must be overridden according to plugin vif_type VIF_TYPE = portbindings.VIF_TYPE_OTHER # VIF_DETAILS must be overridden according to plugin vif_details VIF_DETAILS = None def _check_response_portbindings(self, port): self.assertEqual(port[portbindings.VIF_TYPE], self.VIF_TYPE) # REVISIT(rkukura): Consider reworking tests to enable ML2 to bind if self.VIF_TYPE not in [portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_BINDING_FAILED]: # NOTE(r-mibu): The following six lines are just for backward # compatibility. In this class, HAS_PORT_FILTER has been replaced # by VIF_DETAILS which can be set expected vif_details to check, # but all replacement of HAS_PORT_FILTER in successor has not been # completed. if self.VIF_DETAILS is None: expected = getattr(self, 'HAS_PORT_FILTER', False) vif_details = port[portbindings.VIF_DETAILS] port_filter = vif_details[portbindings.CAP_PORT_FILTER] self.assertEqual(expected, port_filter) return self.assertEqual(self.VIF_DETAILS, port[portbindings.VIF_DETAILS]) def _check_response_no_portbindings(self, port): self.assertIn('status', port) self.assertNotIn(portbindings.VIF_TYPE, port) self.assertNotIn(portbindings.VIF_DETAILS, port) def _get_non_admin_context(self): return context.Context(user_id=None, tenant_id=self._tenant_id, is_admin=False) def test_port_vif_details(self): with self.port(name='name') as port: port_id = port['port']['id'] # Check a response of create_port self._check_response_portbindings(port['port']) # Check a response of get_port ctx = context.get_admin_context() port = self._show('ports', port_id, neutron_context=ctx)['port'] self._check_response_portbindings(port) # By default user is admin - now test non admin user ctx = self._get_non_admin_context() non_admin_port = self._show( 'ports', port_id, neutron_context=ctx)['port'] self._check_response_no_portbindings(non_admin_port) def test_ports_vif_details(self): plugin = directory.get_plugin() cfg.CONF.set_default('allow_overlapping_ips', True) with self.port(), self.port(): ctx = context.get_admin_context() ports = plugin.get_ports(ctx) self.assertEqual(len(ports), 2) for port in ports: self._check_response_portbindings(port) # By default user is admin - now test non admin user ctx = self._get_non_admin_context() ports = self._list('ports', neutron_context=ctx)['ports'] self.assertEqual(len(ports), 2) for non_admin_port in ports: self._check_response_no_portbindings(non_admin_port) def _check_port_binding_profile(self, port, profile=None): # For plugins which does not use binding:profile attr # we just check an operation for the port succeed. self.assertIn('id', port) def _test_create_port_binding_profile(self, profile): profile_arg = {portbindings.PROFILE: profile} with self.port(arg_list=(portbindings.PROFILE,), **profile_arg) as port: port_id = port['port']['id'] self._check_port_binding_profile(port['port'], profile) port = self._show('ports', port_id) self._check_port_binding_profile(port['port'], profile) def test_create_port_binding_profile_none(self): self._test_create_port_binding_profile(None) def test_create_port_binding_profile_with_empty_dict(self): self._test_create_port_binding_profile({}) def _test_update_port_binding_profile(self, profile): profile_arg = {portbindings.PROFILE: profile} with self.port() as port: self._check_port_binding_profile(port['port']) port_id = port['port']['id'] ctx = context.get_admin_context() port = self._update('ports', port_id, {'port': profile_arg}, neutron_context=ctx)['port'] self._check_port_binding_profile(port, profile) port = self._show('ports', port_id)['port'] self._check_port_binding_profile(port, profile) def test_update_port_binding_profile_none(self): self._test_update_port_binding_profile(None) def test_update_port_binding_profile_with_empty_dict(self): self._test_update_port_binding_profile({}) def test_port_create_portinfo_non_admin(self): profile_arg = {portbindings.PROFILE: {dummy_plugin.RESOURCE_NAME: dummy_plugin.RESOURCE_NAME}} with self.network(set_context=True, tenant_id='test') as net1: with self.subnet(network=net1) as subnet1: # succeed without binding:profile with self.port(subnet=subnet1, set_context=True, tenant_id='test'): pass # fail with binding:profile try: with self.port(subnet=subnet1, expected_res_status=403, arg_list=(portbindings.PROFILE,), set_context=True, tenant_id='test', **profile_arg): pass except exc.HTTPClientError: pass def test_port_update_portinfo_non_admin(self): profile_arg = {portbindings.PROFILE: {dummy_plugin.RESOURCE_NAME: dummy_plugin.RESOURCE_NAME}} with self.network() as net1: with self.subnet(network=net1) as subnet1: with self.port(subnet=subnet1) as port: # By default user is admin - now test non admin user port_id = port['port']['id'] ctx = self._get_non_admin_context() port = self._update('ports', port_id, {'port': profile_arg}, expected_code=exc.HTTPForbidden.code, neutron_context=ctx) class PortBindingsHostTestCaseMixin(object): fmt = 'json' hostname = 'testhost' def _check_response_portbindings_host(self, port): self.assertEqual(port[portbindings.HOST_ID], self.hostname) def _check_response_no_portbindings_host(self, port): self.assertIn('status', port) self.assertNotIn(portbindings.HOST_ID, port) def test_port_vif_non_admin(self): with self.network(set_context=True, tenant_id='test') as net1: with self.subnet(network=net1) as subnet1: host_arg = {portbindings.HOST_ID: self.hostname} try: with self.port(subnet=subnet1, expected_res_status=403, arg_list=(portbindings.HOST_ID,), set_context=True, tenant_id='test', **host_arg): pass except exc.HTTPClientError: pass def test_port_vif_host(self): host_arg = {portbindings.HOST_ID: self.hostname} with self.port(name='name', arg_list=(portbindings.HOST_ID,), **host_arg) as port: port_id = port['port']['id'] # Check a response of create_port self._check_response_portbindings_host(port['port']) # Check a response of get_port ctx = context.get_admin_context() port = self._show('ports', port_id, neutron_context=ctx)['port'] self._check_response_portbindings_host(port) # By default user is admin - now test non admin user ctx = context.Context(user_id=None, tenant_id=self._tenant_id, is_admin=False) non_admin_port = self._show( 'ports', port_id, neutron_context=ctx)['port'] self._check_response_no_portbindings_host(non_admin_port) def test_ports_vif_host(self): cfg.CONF.set_default('allow_overlapping_ips', True) host_arg = {portbindings.HOST_ID: self.hostname} with self.port(name='name1', arg_list=(portbindings.HOST_ID,), **host_arg), self.port(name='name2'): ctx = context.get_admin_context() ports = self._list('ports', neutron_context=ctx)['ports'] self.assertEqual(2, len(ports)) for port in ports: if port['name'] == 'name1': self._check_response_portbindings_host(port) else: self.assertFalse(port[portbindings.HOST_ID]) # By default user is admin - now test non admin user ctx = context.Context(user_id=None, tenant_id=self._tenant_id, is_admin=False) ports = self._list('ports', neutron_context=ctx)['ports'] self.assertEqual(2, len(ports)) for non_admin_port in ports: self._check_response_no_portbindings_host(non_admin_port) def test_ports_vif_host_update(self): cfg.CONF.set_default('allow_overlapping_ips', True) host_arg = {portbindings.HOST_ID: self.hostname} with self.port(name='name1', arg_list=(portbindings.HOST_ID,), **host_arg) as port1, self.port(name='name2') as port2: data = {'port': {portbindings.HOST_ID: 'testhosttemp'}} req = self.new_update_request('ports', data, port1['port']['id']) req.get_response(self.api) req = self.new_update_request('ports', data, port2['port']['id']) ctx = context.get_admin_context() req.get_response(self.api) ports = self._list('ports', neutron_context=ctx)['ports'] self.assertEqual(2, len(ports)) for port in ports: self.assertEqual('testhosttemp', port[portbindings.HOST_ID]) def test_ports_vif_non_host_update(self): host_arg = {portbindings.HOST_ID: self.hostname} with self.port(name='name', arg_list=(portbindings.HOST_ID,), **host_arg) as port: data = {'port': {'admin_state_up': False}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(port['port'][portbindings.HOST_ID], res['port'][portbindings.HOST_ID]) def test_ports_vif_non_host_update_when_host_null(self): with self.port() as port: data = {'port': {'admin_state_up': False}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(port['port'][portbindings.HOST_ID], res['port'][portbindings.HOST_ID]) def test_ports_vif_host_list(self): cfg.CONF.set_default('allow_overlapping_ips', True) host_arg = {portbindings.HOST_ID: self.hostname} with self.port(name='name1', arg_list=(portbindings.HOST_ID,), **host_arg) as port1,\ self.port(name='name2'),\ self.port(name='name3', arg_list=(portbindings.HOST_ID,), **host_arg) as port3: self._test_list_resources( 'port', (port1, port3), query_params='%s=%s' % (portbindings.HOST_ID, self.hostname)) class PortBindingsVnicTestCaseMixin(object): fmt = 'json' vnic_type = portbindings.VNIC_NORMAL def _check_response_portbindings_vnic_type(self, port): self.assertIn('status', port) self.assertEqual(port[portbindings.VNIC_TYPE], self.vnic_type) def test_port_vnic_type_non_admin(self): with self.network(set_context=True, tenant_id='test') as net1: with self.subnet(network=net1) as subnet1: vnic_arg = {portbindings.VNIC_TYPE: self.vnic_type} with self.port(subnet=subnet1, expected_res_status=httplib.CREATED, arg_list=(portbindings.VNIC_TYPE,), set_context=True, tenant_id='test', **vnic_arg) as port: # Check a response of create_port self._check_response_portbindings_vnic_type(port['port']) def test_port_vnic_type(self): vnic_arg = {portbindings.VNIC_TYPE: self.vnic_type} with self.port(name='name', arg_list=(portbindings.VNIC_TYPE,), **vnic_arg) as port: port_id = port['port']['id'] # Check a response of create_port self._check_response_portbindings_vnic_type(port['port']) # Check a response of get_port ctx = context.get_admin_context() port = self._show('ports', port_id, neutron_context=ctx)['port'] self._check_response_portbindings_vnic_type(port) # By default user is admin - now test non admin user ctx = context.Context(user_id=None, tenant_id=self._tenant_id, is_admin=False) non_admin_port = self._show( 'ports', port_id, neutron_context=ctx)['port'] self._check_response_portbindings_vnic_type(non_admin_port) def test_ports_vnic_type(self): cfg.CONF.set_default('allow_overlapping_ips', True) vnic_arg = {portbindings.VNIC_TYPE: self.vnic_type} with self.port(name='name1', arg_list=(portbindings.VNIC_TYPE,), **vnic_arg), self.port(name='name2'): ctx = context.get_admin_context() ports = self._list('ports', neutron_context=ctx)['ports'] self.assertEqual(2, len(ports)) for port in ports: if port['name'] == 'name1': self._check_response_portbindings_vnic_type(port) else: self.assertEqual(portbindings.VNIC_NORMAL, port[portbindings.VNIC_TYPE]) # By default user is admin - now test non admin user ctx = context.Context(user_id=None, tenant_id=self._tenant_id, is_admin=False) ports = self._list('ports', neutron_context=ctx)['ports'] self.assertEqual(2, len(ports)) for non_admin_port in ports: self._check_response_portbindings_vnic_type(non_admin_port) def test_ports_vnic_type_list(self): cfg.CONF.set_default('allow_overlapping_ips', True) vnic_arg = {portbindings.VNIC_TYPE: self.vnic_type} with self.port(name='name1', arg_list=(portbindings.VNIC_TYPE,), **vnic_arg) as port1,\ self.port(name='name2') as port2,\ self.port(name='name3', arg_list=(portbindings.VNIC_TYPE,), **vnic_arg) as port3: self._test_list_resources( 'port', (port1, port2, port3), query_params='%s=%s' % (portbindings.VNIC_TYPE, self.vnic_type)) neutron-12.1.1/neutron/tests/unit/test_wsgi.py0000664000175000017500000006271413553660047021530 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import socket import ssl import mock from neutron_lib import exceptions as exception from oslo_config import cfg import six.moves.urllib.request as urlrequest import testtools import webob import webob.exc from neutron.common import exceptions as n_exc from neutron.common import ipv6_utils from neutron.tests import base from neutron.tests.common import helpers from neutron import wsgi CONF = cfg.CONF TEST_VAR_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'var')) def open_no_proxy(*args, **kwargs): # NOTE(jamespage): # Deal with more secure certification chain verification # introduced in python 2.7.9 under PEP-0476 # https://github.com/python/peps/blob/master/pep-0476.txt if hasattr(ssl, "_create_unverified_context"): opener = urlrequest.build_opener( urlrequest.ProxyHandler({}), urlrequest.HTTPSHandler(context=ssl._create_unverified_context()) ) else: opener = urlrequest.build_opener(urlrequest.ProxyHandler({})) return opener.open(*args, **kwargs) class TestServiceBase(base.BaseTestCase): """Service tests base.""" @mock.patch("neutron.policy.refresh") @mock.patch("neutron.common.config.setup_logging") def _test_reset(self, worker_service, setup_logging_mock, refresh_mock): worker_service.reset() setup_logging_mock.assert_called_once_with() refresh_mock.assert_called_once_with() class TestWorkerService(TestServiceBase): """WorkerService tests.""" @mock.patch('neutron.db.api.context_manager.get_legacy_facade') def test_start_withoutdb_call(self, apimock): _service = mock.Mock() _service.pool.spawn.return_value = None _app = mock.Mock() workerservice = wsgi.WorkerService(_service, _app) workerservice.start() self.assertFalse(apimock.called) def test_reset(self): _service = mock.Mock() _app = mock.Mock() worker_service = wsgi.WorkerService(_service, _app) self._test_reset(worker_service) class TestWSGIServer(base.BaseTestCase): """WSGI server tests.""" def test_start_random_port(self): server = wsgi.Server("test_random_port") server.start(None, 0, host="127.0.0.1") self.assertNotEqual(0, server.port) server.stop() server.wait() @mock.patch('oslo_service.service.ProcessLauncher') def test_start_multiple_workers(self, ProcessLauncher): launcher = ProcessLauncher.return_value server = wsgi.Server("test_multiple_processes") server.start(None, 0, host="127.0.0.1", workers=2) launcher.launch_service.assert_called_once_with(mock.ANY, workers=2) server.stop() launcher.stop.assert_called_once_with() server.wait() launcher.wait.assert_called_once_with() @testtools.skipIf( not ipv6_utils.is_enabled_and_bind_by_default(), 'IPv6 support disabled on host') def test_start_random_port_with_ipv6(self): server = wsgi.Server("test_random_port") server.start(None, 0, host="::1") self.assertEqual("::1", server.host) self.assertNotEqual(0, server.port) server.stop() server.wait() def test_ipv6_listen_called_with_scope(self): server = wsgi.Server("test_app") with mock.patch.object(wsgi.eventlet, 'listen') as mock_listen: with mock.patch.object(socket, 'getaddrinfo') as mock_get_addr: mock_get_addr.return_value = [ (socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP, '', ('fe80::204:acff:fe96:da87%eth0', 1234, 0, 2)) ] with mock.patch.object(server, 'pool') as mock_pool: server.start(None, 1234, host="fe80::204:acff:fe96:da87%eth0") mock_get_addr.assert_called_once_with( "fe80::204:acff:fe96:da87%eth0", 1234, socket.AF_UNSPEC, socket.SOCK_STREAM ) mock_listen.assert_called_once_with( ('fe80::204:acff:fe96:da87%eth0', 1234, 0, 2), family=socket.AF_INET6, backlog=cfg.CONF.backlog ) mock_pool.spawn.assert_has_calls([ mock.call( server._run, None, mock_listen.return_value.dup.return_value) ]) def test_app(self): greetings = b'Hello, World!!!' def hello_world(env, start_response): if env['PATH_INFO'] != '/': start_response('404 Not Found', [('Content-Type', 'text/plain')]) return ['Not Found\r\n'] start_response('200 OK', [('Content-Type', 'text/plain')]) return [greetings] server = wsgi.Server("test_app") server.start(hello_world, 0, host="127.0.0.1") response = open_no_proxy('http://127.0.0.1:%d/' % server.port) self.assertEqual(greetings, response.read()) server.stop() def test_disable_ssl(self): CONF.set_default('use_ssl', True) greetings = 'Hello, World!!!' def hello_world(env, start_response): if env['PATH_INFO'] != '/': start_response('404 Not Found', [('Content-Type', 'text/plain')]) return ['Not Found\r\n'] start_response('200 OK', [('Content-Type', 'text/plain')]) return [greetings] server = wsgi.Server("test_app", disable_ssl=True) server.start(hello_world, 0, host="127.0.0.1") response = open_no_proxy('http://127.0.0.1:%d/' % server.port) self.assertEqual(greetings.encode('utf-8'), response.read()) server.stop() @mock.patch.object(wsgi, 'eventlet') def test__run(self, eventlet_mock): server = wsgi.Server('test') server._run("app", "socket") eventlet_mock.wsgi.server.assert_called_once_with( 'socket', 'app', max_size=server.num_threads, log=mock.ANY, keepalive=CONF.wsgi_keep_alive, log_format=CONF.wsgi_log_format, socket_timeout=server.client_socket_timeout ) class SerializerTest(base.BaseTestCase): def test_serialize_unknown_content_type(self): """Verify that exception InvalidContentType is raised.""" input_dict = {'servers': {'test': 'pass'}} content_type = 'application/unknown' serializer = wsgi.Serializer() self.assertRaises( exception.InvalidContentType, serializer.serialize, input_dict, content_type) def test_get_deserialize_handler_unknown_content_type(self): """Verify that exception InvalidContentType is raised.""" content_type = 'application/unknown' serializer = wsgi.Serializer() self.assertRaises( exception.InvalidContentType, serializer.get_deserialize_handler, content_type) def test_serialize_content_type_json(self): """Test serialize with content type json.""" input_data = {'servers': ['test=pass']} content_type = 'application/json' serializer = wsgi.Serializer() result = serializer.serialize(input_data, content_type) self.assertEqual(b'{"servers": ["test=pass"]}', result) def test_deserialize_raise_bad_request(self): """Test serialize verifies that exception is raises.""" content_type = 'application/unknown' data_string = 'test' serializer = wsgi.Serializer() self.assertRaises( webob.exc.HTTPBadRequest, serializer.deserialize, data_string, content_type) def test_deserialize_json_content_type(self): """Test Serializer.deserialize with content type json.""" content_type = 'application/json' data_string = '{"servers": ["test=pass"]}' serializer = wsgi.Serializer() result = serializer.deserialize(data_string, content_type) self.assertEqual({'body': {u'servers': [u'test=pass']}}, result) class RequestDeserializerTest(testtools.TestCase): def setUp(self): super(RequestDeserializerTest, self).setUp() class JSONDeserializer(object): def deserialize(self, data, action='default'): return 'pew_json' self.body_deserializers = {'application/json': JSONDeserializer()} self.deserializer = wsgi.RequestDeserializer(self.body_deserializers) def test_get_deserializer(self): """Test RequestDeserializer.get_body_deserializer.""" expected_json_serializer = self.deserializer.get_body_deserializer( 'application/json') self.assertEqual( expected_json_serializer, self.body_deserializers['application/json']) def test_get_expected_content_type(self): """Test RequestDeserializer.get_expected_content_type.""" request = wsgi.Request.blank('/') request.headers['Accept'] = 'application/json' self.assertEqual('application/json', self.deserializer.get_expected_content_type(request)) def test_get_action_args(self): """Test RequestDeserializer.get_action_args.""" env = { 'wsgiorg.routing_args': [None, { 'controller': None, 'format': None, 'action': 'update', 'id': 12}]} expected = {'action': 'update', 'id': 12} self.assertEqual(expected, self.deserializer.get_action_args(env)) def test_deserialize(self): """Test RequestDeserializer.deserialize.""" with mock.patch.object( self.deserializer, 'get_action_args') as mock_method: mock_method.return_value = {'action': 'create'} request = wsgi.Request.blank('/') request.headers['Accept'] = 'application/json' deserialized = self.deserializer.deserialize(request) expected = ('create', {}, 'application/json') self.assertEqual(expected, deserialized) def test_get_body_deserializer_unknown_content_type(self): """Verify that exception InvalidContentType is raised.""" content_type = 'application/unknown' deserializer = wsgi.RequestDeserializer() self.assertRaises( exception.InvalidContentType, deserializer.get_body_deserializer, content_type) class ResponseSerializerTest(testtools.TestCase): def setUp(self): super(ResponseSerializerTest, self).setUp() class JSONSerializer(object): def serialize(self, data, action='default'): return b'pew_json' class HeadersSerializer(object): def serialize(self, response, data, action): response.status_int = 404 self.body_serializers = {'application/json': JSONSerializer()} self.serializer = wsgi.ResponseSerializer( self.body_serializers, HeadersSerializer()) def test_serialize_unknown_content_type(self): """Verify that exception InvalidContentType is raised.""" self.assertRaises( exception.InvalidContentType, self.serializer.serialize, {}, 'application/unknown') def test_get_body_serializer(self): """Verify that exception InvalidContentType is raised.""" self.assertRaises( exception.InvalidContentType, self.serializer.get_body_serializer, 'application/unknown') def test_get_serializer(self): """Test ResponseSerializer.get_body_serializer.""" content_type = 'application/json' self.assertEqual(self.body_serializers[content_type], self.serializer.get_body_serializer(content_type)) def test_serialize_json_response(self): response = self.serializer.serialize({}, 'application/json') self.assertEqual('application/json', response.headers['Content-Type']) self.assertEqual(b'pew_json', response.body) self.assertEqual(404, response.status_int) def test_serialize_response_None(self): response = self.serializer.serialize( None, 'application/json') self.assertEqual('application/json', response.headers['Content-Type']) self.assertEqual(b'', response.body) self.assertEqual(404, response.status_int) class RequestTest(base.BaseTestCase): def test_content_type_missing(self): request = wsgi.Request.blank('/tests/123', method='POST') request.body = b"" self.assertIsNone(request.get_content_type()) def test_content_type_unsupported(self): request = wsgi.Request.blank('/tests/123', method='POST') request.headers["Content-Type"] = "text/html" request.body = b"fake
" self.assertIsNone(request.get_content_type()) def test_content_type_with_charset(self): request = wsgi.Request.blank('/tests/123') request.headers["Content-Type"] = "application/json; charset=UTF-8" result = request.get_content_type() self.assertEqual("application/json", result) def test_content_type_with_given_content_types(self): request = wsgi.Request.blank('/tests/123') request.headers["Content-Type"] = "application/new-type;" self.assertIsNone(request.get_content_type()) def test_content_type_from_accept(self): request = wsgi.Request.blank('/tests/123') request.headers["Accept"] = "application/json" result = request.best_match_content_type() self.assertEqual("application/json", result) request = wsgi.Request.blank('/tests/123') request.headers["Accept"] = ("application/json; q=0.3") result = request.best_match_content_type() self.assertEqual("application/json", result) def test_content_type_from_query_extension(self): request = wsgi.Request.blank('/tests/123.json') result = request.best_match_content_type() self.assertEqual("application/json", result) request = wsgi.Request.blank('/tests/123.invalid') result = request.best_match_content_type() self.assertEqual("application/json", result) def test_content_type_accept_and_query_extension(self): request = wsgi.Request.blank('/tests/123.json') request.headers["Accept"] = "application/json" result = request.best_match_content_type() self.assertEqual("application/json", result) def test_content_type_accept_default(self): request = wsgi.Request.blank('/tests/123.unsupported') request.headers["Accept"] = "application/unsupported1" result = request.best_match_content_type() self.assertEqual("application/json", result) def test_content_type_accept_with_given_content_types(self): request = wsgi.Request.blank('/tests/123') request.headers["Accept"] = "application/new_type" result = request.best_match_content_type() self.assertEqual("application/json", result) class ActionDispatcherTest(base.BaseTestCase): def test_dispatch(self): """Test ActionDispatcher.dispatch.""" serializer = wsgi.ActionDispatcher() serializer.create = lambda x: x self.assertEqual('pants', serializer.dispatch('pants', action='create')) def test_dispatch_action_None(self): """Test ActionDispatcher.dispatch with none action.""" serializer = wsgi.ActionDispatcher() serializer.create = lambda x: x + ' pants' serializer.default = lambda x: x + ' trousers' self.assertEqual('Two trousers', serializer.dispatch('Two', action=None)) def test_dispatch_default(self): serializer = wsgi.ActionDispatcher() serializer.create = lambda x: x + ' pants' serializer.default = lambda x: x + ' trousers' self.assertEqual('Two trousers', serializer.dispatch('Two', action='update')) class ResponseHeadersSerializerTest(base.BaseTestCase): def test_default(self): serializer = wsgi.ResponseHeaderSerializer() response = webob.Response() serializer.serialize(response, {'v': '123'}, 'fake') self.assertEqual(200, response.status_int) def test_custom(self): class Serializer(wsgi.ResponseHeaderSerializer): def update(self, response, data): response.status_int = 404 response.headers['X-Custom-Header'] = data['v'] serializer = Serializer() response = webob.Response() serializer.serialize(response, {'v': '123'}, 'update') self.assertEqual(404, response.status_int) self.assertEqual('123', response.headers['X-Custom-Header']) class DictSerializerTest(base.BaseTestCase): def test_dispatch_default(self): serializer = wsgi.DictSerializer() self.assertEqual('', serializer.serialize({}, 'NonExistentAction')) class JSONDictSerializerTest(base.BaseTestCase): def test_json(self): input_dict = dict(servers=dict(a=(2, 3))) expected_json = b'{"servers":{"a":[2,3]}}' serializer = wsgi.JSONDictSerializer() result = serializer.serialize(input_dict) result = result.replace(b'\n', b'').replace(b' ', b'') self.assertEqual(expected_json, result) # The tested behaviour is only meant to be witnessed in Python 2, so it is # OK to skip this test with Python 3. @helpers.requires_py2 def test_json_with_utf8(self): input_dict = dict(servers=dict(a=(2, '\xe7\xbd\x91\xe7\xbb\x9c'))) expected_json = b'{"servers":{"a":[2,"\\u7f51\\u7edc"]}}' serializer = wsgi.JSONDictSerializer() result = serializer.serialize(input_dict) result = result.replace(b'\n', b'').replace(b' ', b'') self.assertEqual(expected_json, result) def test_json_with_unicode(self): input_dict = dict(servers=dict(a=(2, u'\u7f51\u7edc'))) expected_json = b'{"servers":{"a":[2,"\\u7f51\\u7edc"]}}' serializer = wsgi.JSONDictSerializer() result = serializer.serialize(input_dict) result = result.replace(b'\n', b'').replace(b' ', b'') self.assertEqual(expected_json, result) class TextDeserializerTest(base.BaseTestCase): def test_dispatch_default(self): deserializer = wsgi.TextDeserializer() self.assertEqual({}, deserializer.deserialize({}, 'update')) class JSONDeserializerTest(base.BaseTestCase): def test_json(self): data = """{"a": { "a1": "1", "a2": "2", "bs": ["1", "2", "3", {"c": {"c1": "1"}}], "d": {"e": "1"}, "f": "1"}}""" as_dict = { 'body': { 'a': { 'a1': '1', 'a2': '2', 'bs': ['1', '2', '3', {'c': {'c1': '1'}}], 'd': {'e': '1'}, 'f': '1'}}} deserializer = wsgi.JSONDeserializer() self.assertEqual(as_dict, deserializer.deserialize(data)) def test_default_raise_Malformed_Exception(self): """Test JsonDeserializer.default. Test verifies JsonDeserializer.default raises exception MalformedRequestBody correctly. """ data_string = "" deserializer = wsgi.JSONDeserializer() self.assertRaises( n_exc.MalformedRequestBody, deserializer.default, data_string) def test_json_with_utf8(self): data = b'{"a": "\xe7\xbd\x91\xe7\xbb\x9c"}' as_dict = {'body': {'a': u'\u7f51\u7edc'}} deserializer = wsgi.JSONDeserializer() self.assertEqual(as_dict, deserializer.deserialize(data)) def test_json_with_unicode(self): data = b'{"a": "\u7f51\u7edc"}' as_dict = {'body': {'a': u'\u7f51\u7edc'}} deserializer = wsgi.JSONDeserializer() self.assertEqual(as_dict, deserializer.deserialize(data)) class RequestHeadersDeserializerTest(base.BaseTestCase): def test_default(self): deserializer = wsgi.RequestHeadersDeserializer() req = wsgi.Request.blank('/') self.assertEqual({}, deserializer.deserialize(req, 'nonExistent')) def test_custom(self): class Deserializer(wsgi.RequestHeadersDeserializer): def update(self, request): return {'a': request.headers['X-Custom-Header']} deserializer = Deserializer() req = wsgi.Request.blank('/') req.headers['X-Custom-Header'] = 'b' self.assertEqual({'a': 'b'}, deserializer.deserialize(req, 'update')) class ResourceTest(base.BaseTestCase): @staticmethod def my_fault_body_function(): return 'off' class Controller(object): def index(self, request, index=None): return index def test_dispatch(self): resource = wsgi.Resource(self.Controller(), self.my_fault_body_function) actual = resource.dispatch( resource.controller, 'index', action_args={'index': 'off'}) expected = 'off' self.assertEqual(expected, actual) def test_dispatch_unknown_controller_action(self): resource = wsgi.Resource(self.Controller(), self.my_fault_body_function) self.assertRaises( AttributeError, resource.dispatch, resource.controller, 'create', {}) def test_malformed_request_body_throws_bad_request(self): resource = wsgi.Resource(None, self.my_fault_body_function) request = wsgi.Request.blank( "/", body=b"{mal:formed", method='POST', headers={'Content-Type': "application/json"}) response = resource(request) self.assertEqual(400, response.status_int) def test_wrong_content_type_throws_unsupported_media_type_error(self): resource = wsgi.Resource(None, self.my_fault_body_function) request = wsgi.Request.blank( "/", body=b"{some:json}", method='POST', headers={'Content-Type': "xxx"}) response = resource(request) self.assertEqual(400, response.status_int) def test_wrong_content_type_server_error(self): resource = wsgi.Resource(None, self.my_fault_body_function) request = wsgi.Request.blank( "/", method='POST', headers={'Content-Type': "unknow"}) response = resource(request) self.assertEqual(500, response.status_int) def test_call_resource_class_bad_request(self): class FakeRequest(object): def __init__(self): self.url = 'http://where.no' self.environ = 'environ' self.body = 'body' def method(self): pass def best_match_content_type(self): return 'best_match_content_type' resource = wsgi.Resource(self.Controller(), self.my_fault_body_function) request = FakeRequest() result = resource(request) self.assertEqual(400, result.status_int) def test_type_error(self): resource = wsgi.Resource(self.Controller(), self.my_fault_body_function) request = wsgi.Request.blank( "/", method='POST', headers={'Content-Type': "json"}) response = resource.dispatch( request, action='index', action_args='test') self.assertEqual(400, response.status_int) def test_call_resource_class_internal_error(self): class FakeRequest(object): def __init__(self): self.url = 'http://where.no' self.environ = 'environ' self.body = '{"Content-Type": "json"}' def method(self): pass def best_match_content_type(self): return 'application/json' resource = wsgi.Resource(self.Controller(), self.my_fault_body_function) request = FakeRequest() result = resource(request) self.assertEqual(500, result.status_int) class FaultTest(base.BaseTestCase): def test_call_fault(self): class MyException(object): status_int = 415 explanation = 'test' my_exceptions = MyException() my_fault = wsgi.Fault(exception=my_exceptions) request = wsgi.Request.blank( "/", method='POST', headers={'Content-Type': "unknow"}) response = my_fault(request) self.assertEqual(415, response.status_int) neutron-12.1.1/neutron/tests/unit/agent/0000775000175000017500000000000013553660156020233 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/windows/0000775000175000017500000000000013553660156021725 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/windows/test_ip_lib.py0000664000175000017500000001134713553660046024600 0ustar zuulzuul00000000000000# Copyright 2016 Cloudbase Solutions. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import netifaces from neutron.agent.windows import ip_lib from neutron.tests import base class TestIpWrapper(base.BaseTestCase): def test_get_device_by_ip_no_ip(self): ret = ip_lib.IPWrapper().get_device_by_ip(None) self.assertIsNone(ret) @mock.patch.object(ip_lib.IPWrapper, 'get_devices') def test_get_device_by_ip(self, mock_get_devices): mock_dev1 = mock.MagicMock() mock_dev2 = mock.MagicMock() mock_dev1.device_has_ip.return_value = False mock_dev2.device_has_ip.return_value = True mock_get_devices.return_value = [mock_dev1, mock_dev2] ret = ip_lib.IPWrapper().get_device_by_ip('fake_ip') self.assertEqual(mock_dev2, ret) @mock.patch('netifaces.interfaces') def test_get_devices(self, mock_interfaces): mock_interfaces.return_value = [mock.sentinel.dev1, mock.sentinel.dev2] ret = ip_lib.IPWrapper().get_devices() self.assertEqual(mock.sentinel.dev1, ret[0].name) self.assertEqual(mock.sentinel.dev2, ret[1].name) @mock.patch('netifaces.interfaces') def test_get_devices_error(self, mock_interfaces): mock_interfaces.side_effect = OSError ret = ip_lib.IPWrapper().get_devices() self.assertEqual([], ret) class TestIpDevice(base.BaseTestCase): @mock.patch('netifaces.ifaddresses') def test_read_ifaddresses(self, mock_netifaces): mock_address = {'addr': mock.sentinel.fake_addr} mock_netifaces.return_value = {netifaces.AF_INET: [mock_address]} ret = ip_lib.IPDevice("fake_dev").read_ifaddresses() self.assertTrue(ret) @mock.patch('netifaces.ifaddresses') def test_read_ifaddresses_no_ip(self, mock_netifaces): mock_netifaces.return_value = {} ret = ip_lib.IPDevice("fake_dev").read_ifaddresses() self.assertFalse(ret) @mock.patch('netifaces.ifaddresses') def test_read_ifaddresses_ip_error(self, mock_netifaces): mock_netifaces.side_effect = OSError ret = ip_lib.IPDevice("fake_dev").read_ifaddresses() self.assertFalse(ret) @mock.patch('netifaces.ifaddresses') def test_read_faddresses_not_found(self, mock_netifaces): mock_netifaces.side_effect = ValueError ret = ip_lib.IPDevice("fake_dev").read_ifaddresses() self.assertFalse(ret) def test_device_has_ip(self): mock_address = {'addr': mock.sentinel.fake_addr} ip_device = ip_lib.IPDevice("fake_dev") with mock.patch.object(ip_device, "read_ifaddresses", return_value=( {netifaces.AF_INET: [mock_address]})): ret = ip_device.device_has_ip(mock.sentinel.fake_addr) self.assertTrue(ret) def test_device_has_ip_false(self): ip_device = ip_lib.IPDevice("fake_dev") with mock.patch.object(ip_device, "read_ifaddresses", return_value={}): ret = ip_device.device_has_ip(mock.sentinel.fake_addr) self.assertFalse(ret) def test_device_has_ip_error(self): ip_device = ip_lib.IPDevice("fake_dev") with mock.patch.object(ip_device, "read_ifaddresses", return_value=None): ret = ip_device.device_has_ip(mock.sentinel.fake_addr) self.assertFalse(ret) class TestIPLink(base.BaseTestCase): def setUp(self): super(TestIPLink, self).setUp() parent = ip_lib.IPDevice("fake_dev") self.ip_link = ip_lib.IPLink(parent) self.ip_link._parent.read_ifaddresses = mock.Mock() def test_address(self): mock_address = {'addr': mock.sentinel.fake_addr} self.ip_link._parent.read_ifaddresses.return_value = { netifaces.AF_LINK: [mock_address]} self.assertEqual([mock_address['addr']], self.ip_link.address) def test_address_no_address(self): self.ip_link._parent.read_ifaddresses.return_value = { netifaces.AF_LINK: []} self.assertEqual([], self.ip_link.address) def test_address_error(self): self.ip_link._parent.read_ifaddresses.return_value = None self.assertFalse(self.ip_link.address) neutron-12.1.1/neutron/tests/unit/agent/windows/test_utils.py0000664000175000017500000001751513553660047024506 0ustar zuulzuul00000000000000# Copyright 2018 Cloudbase Solutions. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import io import ddt import eventlet from eventlet import tpool import mock import six from neutron.agent.windows import utils from neutron.common import exceptions from neutron.tests import base @ddt.ddt class WindowsUtilsTestCase(base.BaseTestCase): @mock.patch('os.environ', {mock.sentinel.key0: mock.sentinel.val0}) @mock.patch.object(utils.subprocess, 'Popen') @mock.patch.object(tpool, 'Proxy') @mock.patch.object(eventlet, 'getcurrent') def test_create_process(self, mock_get_current_gt, mock_tpool_proxy, mock_popen): cmd = ['fake_cmd'] popen_obj, ret_cmd = utils.create_process( cmd, run_as_root=mock.sentinel.run_as_root, addl_env={mock.sentinel.key1: mock.sentinel.val1}, tpool_proxy=True) exp_env = {mock.sentinel.key0: mock.sentinel.val0, mock.sentinel.key1: mock.sentinel.val1} mock_popen.assert_called_once_with( cmd, shell=False, stdin=utils.subprocess.PIPE, stdout=utils.subprocess.PIPE, stderr=utils.subprocess.PIPE, env=exp_env, preexec_fn=None, close_fds=False) file_type = getattr(six.moves.builtins, 'file', io.IOBase) mock_tpool_proxy.assert_called_once_with( mock_popen.return_value, autowrap=(file_type, )) self.assertEqual(mock_tpool_proxy.return_value, popen_obj) self.assertEqual(ret_cmd, cmd) @ddt.data({}, {'pid': None}, {'process_exists': True}) @ddt.unpack @mock.patch.object(utils, 'wmi', create=True) def test_get_wmi_process(self, mock_wmi, pid=mock.sentinel.pid, process_exists=False): mock_conn = mock_wmi.WMI.return_value if not pid: exp_process = None elif process_exists: exp_process = mock.sentinel.wmi_obj mock_conn.Win32_Process.return_value = [exp_process] else: exp_process = None mock_conn.Win32_Process.return_value = [] wmi_obj = utils._get_wmi_process(pid) self.assertEqual(exp_process, wmi_obj) if pid: mock_conn.Win32_Process.assert_called_once_with(ProcessId=pid) @ddt.data(True, False) @mock.patch.object(utils, '_get_wmi_process') def test_kill_process(self, process_exists, mock_get_process): if not process_exists: mock_get_process.return_value = None utils.kill_process(mock.sentinel.pid, mock.sentinel.signal, run_as_root=False) mock_get_process.assert_called_once_with(mock.sentinel.pid) if process_exists: mock_get_process.return_value.Terminate.assert_called_once_with() @ddt.data(True, False) @mock.patch.object(utils, '_get_wmi_process') def test_kill_process_exception(self, process_still_running, mock_get_process): mock_process = mock.Mock() mock_process.Terminate.side_effect = OSError mock_get_process.side_effect = [ mock_process, mock_process if process_still_running else None] if process_still_running: self.assertRaises(OSError, utils.kill_process, mock.sentinel.pid, mock.sentinel.signal) else: utils.kill_process(mock.sentinel.pid, mock.sentinel.signal) @ddt.data({'return_stder': True}, {'returncode': 1, 'check_exit_code': False, 'log_fail_as_error': True}, {'returncode': 1, 'log_fail_as_error': True, 'extra_ok_codes': [1]}, {'returncode': 1, 'log_fail_as_error': True, 'exp_fail': True}) @ddt.unpack @mock.patch.object(utils, 'create_process') @mock.patch.object(utils, 'avoid_blocking_call') def test_execute(self, mock_avoid_blocking_call, mock_create_process, returncode=0, check_exit_code=True, return_stder=True, log_fail_as_error=True, extra_ok_codes=None, exp_fail=False): fake_stdin = 'fake_stdin' fake_stdout = 'fake_stdout' fake_stderr = 'fake_stderr' mock_popen = mock.Mock() mock_popen.communicate.return_value = fake_stdout, fake_stderr mock_popen.returncode = returncode mock_create_process.return_value = mock_popen, mock.sentinel.cmd mock_avoid_blocking_call.side_effect = ( lambda func, *args, **kwargs: func(*args, **kwargs)) args = (mock.sentinel.cmd, fake_stdin, mock.sentinel.env, check_exit_code, return_stder, log_fail_as_error, extra_ok_codes) if exp_fail: self.assertRaises(exceptions.ProcessExecutionError, utils.execute, *args) else: ret_val = utils.execute(*args) if return_stder: exp_ret_val = (fake_stdout, fake_stderr) else: exp_ret_val = fake_stdout self.assertEqual(exp_ret_val, ret_val) mock_create_process.assert_called_once_with( mock.sentinel.cmd, addl_env=mock.sentinel.env, tpool_proxy=False) mock_avoid_blocking_call.assert_called_once_with( mock_popen.communicate, six.b(fake_stdin)) mock_popen.communicate.assert_called_once_with(six.b(fake_stdin)) mock_popen.stdin.close.assert_called_once_with() def test_get_root_helper_child_pid(self): pid = utils.get_root_helper_child_pid( mock.sentinel.pid, mock.sentinel.exp_cmd, run_as_root=False) self.assertEqual(str(mock.sentinel.pid), pid) @ddt.data(True, False) @mock.patch.object(utils, '_get_wmi_process') def test_process_is_running(self, process_running, mock_get_process): mock_get_process.return_value = ( mock.sentinel.wmi_obj if process_running else None) self.assertEqual(process_running, utils.process_is_running(mock.sentinel.pid)) mock_get_process.assert_called_once_with(mock.sentinel.pid) @ddt.data({}, {'process_running': False}, {'command_matches': False}) @ddt.unpack @mock.patch.object(utils, '_get_wmi_process') def test_pid_invoked_with_cmdline(self, mock_get_process, process_running=True, command_matches=False): exp_cmd = 'exp_cmd' mock_process = mock.Mock() mock_get_process.return_value = ( mock_process if process_running else None) mock_process.CommandLine = ( exp_cmd if command_matches else 'unexpected_cmd') exp_result = process_running and command_matches result = utils.pid_invoked_with_cmdline(mock.sentinel.pid, [exp_cmd]) self.assertEqual(exp_result, result) mock_get_process.assert_called_once_with(mock.sentinel.pid) neutron-12.1.1/neutron/tests/unit/agent/windows/__init__.py0000664000175000017500000000000013553660046024022 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/linux/0000775000175000017500000000000013553660156021372 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/linux/openvswitch_firewall/0000775000175000017500000000000013553660156025630 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/linux/openvswitch_firewall/test_iptables.py0000664000175000017500000001207713553660046031051 0ustar zuulzuul00000000000000# Copyright 2017 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.agent.linux import iptables_firewall from neutron.agent.linux.openvswitch_firewall import iptables from neutron.tests import base class TestHelper(base.BaseTestCase): def setUp(self): super(TestHelper, self).setUp() self.helper = iptables.Helper(mock.Mock()) mock.patch.object(iptables_firewall, 'cfg').start() mock.patch('neutron.agent.linux.ip_conntrack.get_conntrack').start() def test_get_hybrid_ports(self): present_ports = ['tap1234', 'qvo-1234', 'tap9876', 'qvo-fghfhfh'] self.helper.int_br.get_port_name_list.return_value = present_ports expected_hybrid_ports = ['qvo-1234', 'qvo-fghfhfh'] observed = self.helper.get_hybrid_ports() self.assertItemsEqual(expected_hybrid_ports, observed) def test_has_not_been_cleaned_no_value(self): other_config = {'foo': 'bar'} self.helper.int_br.db_get_val.return_value = other_config self.assertTrue(self.helper.has_not_been_cleaned) def test_has_not_been_cleaned_true(self): other_config = {'foo': 'bar', iptables.Helper.CLEANED_METADATA: 'true'} self.helper.int_br.db_get_val.return_value = other_config self.assertFalse(self.helper.has_not_been_cleaned) def test_has_not_been_cleaned_false(self): other_config = {'foo': 'bar', iptables.Helper.CLEANED_METADATA: 'false'} self.helper.int_br.db_get_val.return_value = other_config self.assertTrue(self.helper.has_not_been_cleaned) def test_load_driver_if_needed_no_hybrid_ports(self): self.helper.int_br.get_port_name_list.return_value = [ 'tap1234', 'tap9876'] self.helper.load_driver_if_needed() self.assertIsNone(self.helper.iptables_driver) def test_load_driver_if_needed_hybrid_ports_cleaned(self): """If was cleaned, driver shouldn't be loaded.""" self.helper.int_br.get_port_name_list.return_value = [ 'tap1234', 'qvo-1234', 'tap9876', 'qvo-fghfhfh'] self.helper.int_br.db_get_val.return_value = { 'foo': 'bar', iptables.Helper.CLEANED_METADATA: 'true'} self.helper.load_driver_if_needed() self.assertIsNone(self.helper.iptables_driver) def test_load_driver_if_needed_hybrid_ports_not_cleaned(self): """If hasn't been cleaned, driver should be loaded.""" self.helper.int_br.get_port_name_list.return_value = [ 'tap1234', 'qvo-1234', 'tap9876', 'qvo-fghfhfh'] self.helper.int_br.db_get_val.return_value = {'foo': 'bar'} self.helper.load_driver_if_needed() self.assertIsNotNone(self.helper.iptables_driver) def test_get_iptables_driver_instance_has_correct_instance(self): instance = iptables.get_iptables_driver_instance() self.assertIsInstance( instance, iptables_firewall.OVSHybridIptablesFirewallDriver) def test_cleanup_port_last_port_marks_cleaned(self): self.helper.iptables_driver = mock.Mock() self.helper.hybrid_ports = {'qvoport'} with mock.patch.object(self.helper, 'mark_as_cleaned') as mock_mark: self.helper.cleanup_port({'device': 'port'}) self.assertIsNone(self.helper.iptables_driver) self.assertTrue(mock_mark.called) def test_cleanup_port_existing_ports(self): self.helper.iptables_driver = mock.Mock() self.helper.hybrid_ports = {'qvoport', 'qvoanother'} with mock.patch.object(self.helper, 'mark_as_cleaned') as mock_mark: self.helper.cleanup_port({'device': 'port'}) self.assertIsNotNone(self.helper.iptables_driver) self.assertFalse(mock_mark.called) def test_cleanup_port_unknown(self): self.helper.iptables_driver = mock.Mock() self.helper.hybrid_ports = {'qvoanother'} self.helper.cleanup_port({'device': 'port'}) self.assertFalse(self.helper.iptables_driver.remove_port_filter.called) class TestHybridIptablesHelper(base.BaseTestCase): def test_overloaded_remove_conntrack(self): with mock.patch.object(iptables_firewall.IptablesFirewallDriver, '_remove_conntrack_entries_from_port_deleted') as rcefpd, \ mock.patch("neutron.agent.linux.ip_conntrack.IpConntrackManager" "._populate_initial_zone_map"): firewall = iptables.get_iptables_driver_instance() firewall._remove_conntrack_entries_from_port_deleted(None) rcefpd.assert_not_called() neutron-12.1.1/neutron/tests/unit/agent/linux/openvswitch_firewall/test_rules.py0000664000175000017500000005171513553660047030403 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants from neutron.agent import firewall from neutron.agent.linux.openvswitch_firewall import constants as ovsfw_consts from neutron.agent.linux.openvswitch_firewall import firewall as ovsfw from neutron.agent.linux.openvswitch_firewall import rules from neutron.common import constants as n_const from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts from neutron.tests import base TESTING_VLAN_TAG = 1 class TestIsValidPrefix(base.BaseTestCase): def test_valid_prefix_ipv4(self): is_valid = rules.is_valid_prefix('10.0.0.0/0') self.assertTrue(is_valid) def test_invalid_prefix_ipv4(self): is_valid = rules.is_valid_prefix('0.0.0.0/0') self.assertFalse(is_valid) def test_valid_prefix_ipv6(self): is_valid = rules.is_valid_prefix('ffff::0/0') self.assertTrue(is_valid) def test_invalid_prefix_ipv6(self): is_valid = rules.is_valid_prefix('0000:0::0/0') self.assertFalse(is_valid) is_valid = rules.is_valid_prefix('::0/0') self.assertFalse(is_valid) is_valid = rules.is_valid_prefix('::/0') self.assertFalse(is_valid) class TestCreateFlowsFromRuleAndPort(base.BaseTestCase): def setUp(self): super(TestCreateFlowsFromRuleAndPort, self).setUp() ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00') ovs_port.ofport = 1 port_dict = {'device': 'port_id'} self.port = ovsfw.OFPort( port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG) self.create_flows_mock = mock.patch.object( rules, 'create_protocol_flows').start() @property def passed_flow_template(self): return self.create_flows_mock.call_args[0][1] def _test_create_flows_from_rule_and_port_helper( self, rule, expected_template): rules.create_flows_from_rule_and_port(rule, self.port) self.assertEqual(expected_template, self.passed_flow_template) def test_create_flows_from_rule_and_port_no_ip_ipv4(self): rule = { 'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, } expected_template = { 'priority': 74, 'dl_type': n_const.ETHERTYPE_IP, 'reg_port': self.port.ofport, } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_src_and_dst_ipv4(self): rule = { 'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, 'source_ip_prefix': '192.168.0.0/24', 'dest_ip_prefix': '10.0.0.1/32', } expected_template = { 'priority': 74, 'dl_type': n_const.ETHERTYPE_IP, 'reg_port': self.port.ofport, 'nw_src': '192.168.0.0/24', 'nw_dst': '10.0.0.1/32', } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_src_and_dst_with_zero_ipv4(self): rule = { 'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, 'source_ip_prefix': '192.168.0.0/24', 'dest_ip_prefix': '0.0.0.0/0', } expected_template = { 'priority': 74, 'dl_type': n_const.ETHERTYPE_IP, 'reg_port': self.port.ofport, 'nw_src': '192.168.0.0/24', } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_no_ip_ipv6(self): rule = { 'ethertype': constants.IPv6, 'direction': firewall.INGRESS_DIRECTION, } expected_template = { 'priority': 74, 'dl_type': n_const.ETHERTYPE_IPV6, 'reg_port': self.port.ofport, } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_src_and_dst_ipv6(self): rule = { 'ethertype': constants.IPv6, 'direction': firewall.INGRESS_DIRECTION, 'source_ip_prefix': '2001:db8:bbbb::1/64', 'dest_ip_prefix': '2001:db8:aaaa::1/64', } expected_template = { 'priority': 74, 'dl_type': n_const.ETHERTYPE_IPV6, 'reg_port': self.port.ofport, 'ipv6_src': '2001:db8:bbbb::1/64', 'ipv6_dst': '2001:db8:aaaa::1/64', } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_src_and_dst_with_zero_ipv6(self): rule = { 'ethertype': constants.IPv6, 'direction': firewall.INGRESS_DIRECTION, 'source_ip_prefix': '2001:db8:bbbb::1/64', 'dest_ip_prefix': '::/0', } expected_template = { 'priority': 74, 'dl_type': n_const.ETHERTYPE_IPV6, 'reg_port': self.port.ofport, 'ipv6_src': '2001:db8:bbbb::1/64', } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) class TestCreateProtocolFlows(base.BaseTestCase): def setUp(self): super(TestCreateProtocolFlows, self).setUp() ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00') ovs_port.ofport = 1 port_dict = {'device': 'port_id'} self.port = ovsfw.OFPort( port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG) def _test_create_protocol_flows_helper(self, direction, rule, expected_flows): flow_template = {'some_settings': 'foo'} for flow in expected_flows: flow.update(flow_template) flows = rules.create_protocol_flows( direction, flow_template, self.port, rule) self.assertEqual(expected_flows, flows) def test_create_protocol_flows_ingress(self): rule = {'protocol': constants.PROTO_NUM_TCP} expected_flows = [{ 'table': ovs_consts.RULES_INGRESS_TABLE, 'actions': 'output:1', 'nw_proto': constants.PROTO_NUM_TCP, }] self._test_create_protocol_flows_helper( firewall.INGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_egress(self): rule = {'protocol': constants.PROTO_NUM_TCP} expected_flows = [{ 'table': ovs_consts.RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( ovs_consts.ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_TCP, }] self._test_create_protocol_flows_helper( firewall.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_no_protocol(self): rule = {} expected_flows = [{ 'table': ovs_consts.RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( ovs_consts.ACCEPT_OR_INGRESS_TABLE), }] self._test_create_protocol_flows_helper( firewall.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_icmp6(self): rule = {'ethertype': constants.IPv6, 'protocol': constants.PROTO_NUM_IPV6_ICMP} expected_flows = [{ 'table': ovs_consts.RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( ovs_consts.ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_IPV6_ICMP, }] self._test_create_protocol_flows_helper( firewall.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_port_range(self): rule = {'ethertype': constants.IPv4, 'protocol': constants.PROTO_NUM_TCP, 'port_range_min': 22, 'port_range_max': 23} expected_flows = [{ 'table': ovs_consts.RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( ovs_consts.ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_TCP, 'tcp_dst': '0x0016/0xfffe' }] self._test_create_protocol_flows_helper( firewall.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_icmp(self): rule = {'ethertype': constants.IPv4, 'protocol': constants.PROTO_NUM_ICMP, 'port_range_min': 0} expected_flows = [{ 'table': ovs_consts.RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( ovs_consts.ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_ICMP, 'icmp_type': 0 }] self._test_create_protocol_flows_helper( firewall.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_ipv6_icmp(self): rule = {'ethertype': constants.IPv6, 'protocol': constants.PROTO_NUM_IPV6_ICMP, 'port_range_min': 5, 'port_range_max': 0} expected_flows = [{ 'table': ovs_consts.RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( ovs_consts.ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_IPV6_ICMP, 'icmp_type': 5, 'icmp_code': 0, }] self._test_create_protocol_flows_helper( firewall.EGRESS_DIRECTION, rule, expected_flows) class TestCreatePortRangeFlows(base.BaseTestCase): def _test_create_port_range_flows_helper(self, expected_flows, rule): flow_template = {'some_settings': 'foo'} for flow in expected_flows: flow.update(flow_template) port_range_flows = rules.create_port_range_flows(flow_template, rule) self.assertEqual(expected_flows, port_range_flows) def test_create_port_range_flows_with_source_and_destination(self): rule = { 'protocol': constants.PROTO_NUM_TCP, 'source_port_range_min': 123, 'source_port_range_max': 124, 'port_range_min': 10, 'port_range_max': 11, } expected_flows = [ {'tcp_src': '0x007b', 'tcp_dst': '0x000a/0xfffe'}, {'tcp_src': '0x007c', 'tcp_dst': '0x000a/0xfffe'}, ] self._test_create_port_range_flows_helper(expected_flows, rule) def test_create_port_range_flows_with_source(self): rule = { 'protocol': constants.PROTO_NUM_TCP, 'source_port_range_min': 123, 'source_port_range_max': 124, } expected_flows = [ {'tcp_src': '0x007b'}, {'tcp_src': '0x007c'}, ] self._test_create_port_range_flows_helper(expected_flows, rule) def test_create_port_range_flows_with_destination(self): rule = { 'protocol': constants.PROTO_NUM_TCP, 'port_range_min': 10, 'port_range_max': 11, } expected_flows = [ {'tcp_dst': '0x000a/0xfffe'}, ] self._test_create_port_range_flows_helper(expected_flows, rule) def test_create_port_range_flows_without_port_range(self): rule = { 'protocol': constants.PROTO_NUM_TCP, } expected_flows = [] self._test_create_port_range_flows_helper(expected_flows, rule) def test_create_port_range_with_icmp_protocol(self): # NOTE: such call is prevented by create_protocols_flows rule = { 'protocol': constants.PROTO_NUM_ICMP, 'port_range_min': 10, 'port_range_max': 11, } expected_flows = [] self._test_create_port_range_flows_helper(expected_flows, rule) class TestCreateFlowsForIpAddress(base.BaseTestCase): def _generate_conjuncion_actions(self, conj_ids, offset): return ','.join( ["conjunction(%d,1/2)" % (c + offset) for c in conj_ids]) def test_create_flows_for_ip_address_egress(self): expected_template = { 'table': ovs_consts.RULES_EGRESS_TABLE, 'priority': 72, 'dl_type': n_const.ETHERTYPE_IP, 'reg_net': 0x123, 'nw_dst': '192.168.0.1/32' } conj_ids = [12, 20] flows = rules.create_flows_for_ip_address( '192.168.0.1', firewall.EGRESS_DIRECTION, constants.IPv4, 0x123, conj_ids) self.assertEqual(2, len(flows)) self.assertEqual(ovsfw_consts.OF_STATE_ESTABLISHED_NOT_REPLY, flows[0]['ct_state']) self.assertEqual(ovsfw_consts.OF_STATE_NEW_NOT_ESTABLISHED, flows[1]['ct_state']) for i in range(2): self.assertEqual(self._generate_conjuncion_actions(conj_ids, i), flows[i]['actions']) for f in flows: del f['actions'] del f['ct_state'] self.assertEqual(expected_template, f) class TestCreateConjFlows(base.BaseTestCase): def test_create_conj_flows(self): ovs_port = mock.Mock(ofport=1, vif_mac='00:00:00:00:00:00') port_dict = {'device': 'port_id'} port = ovsfw.OFPort( port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG) conj_id = 1234 expected_template = { 'table': ovs_consts.RULES_INGRESS_TABLE, 'dl_type': n_const.ETHERTYPE_IPV6, 'priority': 71, 'conj_id': conj_id, 'reg_port': port.ofport } flows = rules.create_conj_flows(port, conj_id, firewall.INGRESS_DIRECTION, constants.IPv6) self.assertEqual(ovsfw_consts.OF_STATE_ESTABLISHED_NOT_REPLY, flows[0]['ct_state']) self.assertEqual(ovsfw_consts.OF_STATE_NEW_NOT_ESTABLISHED, flows[1]['ct_state']) self.assertEqual("output:{:d}".format(port.ofport), flows[0]['actions']) self.assertEqual("ct(commit,zone=NXM_NX_REG{:d}[0..15]),{:s}," "resubmit(,{:d})".format( ovsfw_consts.REG_NET, flows[0]['actions'], ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE), flows[1]['actions']) for f in flows: del f['actions'] del f['ct_state'] self.assertEqual(expected_template, f) expected_template['conj_id'] += 1 class TestMergeRules(base.BaseTestCase): def setUp(self): super(TestMergeRules, self).setUp() self.rule_tmpl = [('direction', 'ingress'), ('ethertype', 'IPv4'), ('protocol', 6)] def _test_merge_port_ranges_helper(self, expected, result): """Take a list of (port_range_min, port_range_max, conj_ids) and an output from rules.merge_port_ranges and check if they are identical, ignoring the other rule fields. """ self.assertEqual(len(expected), len(result)) for (range_min, range_max, conj_ids), result1 in zip( expected, result): self.assertEqual(range_min, result1[0].get('port_range_min')) self.assertEqual(range_max, result1[0].get('port_range_max')) self.assertEqual(conj_ids, set(result1[1])) def test__assert_mergeable_rules(self): self.assertRaises(RuntimeError, rules._assert_mergeable_rules, [({'direction': 'ingress', 'ethertype': 'IPv4', 'protocol': 1}, 8), ({'direction': 'ingress', 'ethertype': 'IPv6'}, 16)]) def test_merge_common_rules_single(self): rule_conj_tuple = ({'direction': 'egress', 'ethertype': 'IPv4', 'protocol': 1}, 8) result = rules.merge_common_rules([rule_conj_tuple]) self.assertEqual([(rule_conj_tuple[0], [rule_conj_tuple[1]])], result) def test_merge_common_rules(self): rule_conj_list = [({'direction': 'ingress', 'ethertype': 'IPv4', 'protocol': 1}, 8), ({'direction': 'ingress', 'ethertype': 'IPv4', 'protocol': 1, 'port_range_min': 3}, 16), ({'direction': 'ingress', 'ethertype': 'IPv4', 'protocol': 1, 'port_range_min': 3, 'port_range_max': 0}, 40), ({'direction': 'ingress', 'ethertype': 'IPv4', 'protocol': 1}, 24)] result = rules.merge_common_rules(rule_conj_list) self.assertItemsEqual( [({'direction': 'ingress', 'ethertype': 'IPv4', 'protocol': 1}, [8, 24]), ({'direction': 'ingress', 'ethertype': 'IPv4', 'protocol': 1, 'port_range_min': 3}, [16]), ({'direction': 'ingress', 'ethertype': 'IPv4', 'protocol': 1, 'port_range_min': 3, 'port_range_max': 0}, [40])], result) def test_merge_port_ranges_overlapping(self): result = rules.merge_port_ranges( [(dict([('port_range_min', 20), ('port_range_max', 30)] + self.rule_tmpl), 6), (dict([('port_range_min', 30), ('port_range_max', 40)] + self.rule_tmpl), 14), (dict([('port_range_min', 35), ('port_range_max', 40)] + self.rule_tmpl), 22), (dict([('port_range_min', 20), ('port_range_max', 20)] + self.rule_tmpl), 30)]) self._test_merge_port_ranges_helper([ # port_range_min, port_range_max, conj_ids (20, 20, {6, 30}), (21, 29, {6}), (30, 30, {6, 14}), (31, 34, {14}), (35, 40, {14, 22})], result) def test_merge_port_ranges_no_port_ranges(self): result = rules.merge_port_ranges( [(dict(self.rule_tmpl), 10), (dict(self.rule_tmpl), 12), (dict([('port_range_min', 30), ('port_range_max', 40)] + self.rule_tmpl), 4)]) self._test_merge_port_ranges_helper([ (1, 29, {10, 12}), (30, 40, {10, 12, 4}), (41, 65535, {10, 12})], result) def test_merge_port_ranges_no_port_ranges_same_conj_id(self): result = rules.merge_port_ranges( [(dict(self.rule_tmpl), 10), (dict(self.rule_tmpl), 12), (dict([('port_range_min', 30), ('port_range_max', 30)] + self.rule_tmpl), 10)]) self._test_merge_port_ranges_helper([ (None, None, {10, 12})], result) def test_merge_port_ranges_nonoverlapping(self): result = rules.merge_port_ranges( [(dict([('port_range_min', 30), ('port_range_max', 40)] + self.rule_tmpl), 32), (dict([('port_range_min', 100), ('port_range_max', 140)] + self.rule_tmpl), 40)]) self._test_merge_port_ranges_helper( [(30, 40, {32}), (100, 140, {40})], result) class TestFlowPriority(base.BaseTestCase): def test_flow_priority_offset(self): self.assertEqual(0, rules.flow_priority_offset( {'foo': 'bar', 'remote_group_id': 'hoge'})) self.assertEqual(4, rules.flow_priority_offset({'foo': 'bar'})) self.assertEqual(5, rules.flow_priority_offset( {'protocol': constants.PROTO_NUM_ICMP})) self.assertEqual(7, rules.flow_priority_offset( {'protocol': constants.PROTO_NUM_TCP})) self.assertEqual(6, rules.flow_priority_offset( {'protocol': constants.PROTO_NUM_ICMP, 'port_range_min': 0})) self.assertEqual(7, rules.flow_priority_offset( {'protocol': constants.PROTO_NUM_IPV6_ICMP, 'port_range_min': 0, 'port_range_max': 0})) neutron-12.1.1/neutron/tests/unit/agent/linux/openvswitch_firewall/__init__.py0000664000175000017500000000000013553660046027725 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py0000664000175000017500000010223113553660047031044 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import events as callbacks_events from neutron_lib.callbacks import registry as callbacks_registry from neutron_lib.callbacks import resources as callbacks_resources from neutron_lib import constants import testtools from neutron.agent.common import ovs_lib from neutron.agent.common import utils from neutron.agent import firewall from neutron.agent.linux.openvswitch_firewall import constants as ovsfw_consts from neutron.agent.linux.openvswitch_firewall import exceptions from neutron.agent.linux.openvswitch_firewall import firewall as ovsfw from neutron.common import constants as n_const from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import ovs_bridge from neutron.tests import base TESTING_VLAN_TAG = 1 def create_ofport(port_dict): ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00', ofport=1, port_name="port-name") return ovsfw.OFPort(port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG) class TestCreateRegNumbers(base.BaseTestCase): def test_no_registers_defined(self): flow = {'foo': 'bar'} ovsfw.create_reg_numbers(flow) self.assertEqual({'foo': 'bar'}, flow) def test_all_registers_defined(self): flow = {'foo': 'bar', 'reg_port': 1, 'reg_net': 2, 'reg_remote_group': 3} expected_flow = {'foo': 'bar', 'reg{:d}'.format(ovsfw_consts.REG_PORT): 1, 'reg{:d}'.format(ovsfw_consts.REG_NET): 2, 'reg{:d}'.format(ovsfw_consts.REG_REMOTE_GROUP): 3} ovsfw.create_reg_numbers(flow) self.assertEqual(expected_flow, flow) class TestSecurityGroup(base.BaseTestCase): def setUp(self): super(TestSecurityGroup, self).setUp() self.sg = ovsfw.SecurityGroup('123') self.sg.members = {'type': [1, 2, 3, 4]} def test_update_rules_split(self): rules = [ {'foo': 'bar', 'rule': 'all'}, {'bar': 'foo'}, {'remote_group_id': '123456', 'foo': 'bar'}] expected_raw_rules = [{'foo': 'bar', 'rule': 'all'}, {'bar': 'foo'}] expected_remote_rules = [{'remote_group_id': '123456', 'foo': 'bar'}] self.sg.update_rules(rules) self.assertEqual(expected_raw_rules, self.sg.raw_rules) self.assertEqual(expected_remote_rules, self.sg.remote_rules) def test_update_rules_protocols(self): rules = [ {'foo': 'bar', 'protocol': constants.PROTO_NAME_ICMP, 'ethertype': constants.IPv4}, {'foo': 'bar', 'protocol': constants.PROTO_NAME_ICMP, 'ethertype': constants.IPv6}, {'foo': 'bar', 'protocol': constants.PROTO_NAME_IPV6_ICMP_LEGACY, 'ethertype': constants.IPv6}, {'foo': 'bar', 'protocol': constants.PROTO_NAME_TCP}, {'foo': 'bar', 'protocol': '94'}, {'foo': 'bar', 'protocol': 'baz'}, {'foo': 'no_proto'}] self.sg.update_rules(rules) self.assertEqual({'foo': 'no_proto'}, self.sg.raw_rules.pop()) protos = [rule['protocol'] for rule in self.sg.raw_rules] self.assertEqual([constants.PROTO_NUM_ICMP, constants.PROTO_NUM_IPV6_ICMP, constants.PROTO_NUM_IPV6_ICMP, constants.PROTO_NUM_TCP, 94, 'baz'], protos) def test_get_ethertype_filtered_addresses(self): addresses = self.sg.get_ethertype_filtered_addresses('type') expected_addresses = [1, 2, 3, 4] self.assertEqual(expected_addresses, addresses) class TestOFPort(base.BaseTestCase): def setUp(self): super(TestOFPort, self).setUp() self.ipv4_addresses = ['10.0.0.1', '192.168.0.1'] self.ipv6_addresses = ['fe80::f816:3eff:fe2e:1'] port_dict = {'device': 1, 'fixed_ips': self.ipv4_addresses + self.ipv6_addresses} self.port = create_ofport(port_dict) def test_ipv4_address(self): ipv4_addresses = self.port.ipv4_addresses self.assertEqual(self.ipv4_addresses, ipv4_addresses) def test_ipv6_address(self): ipv6_addresses = self.port.ipv6_addresses self.assertEqual(self.ipv6_addresses, ipv6_addresses) def test__get_allowed_pairs(self): port = { 'allowed_address_pairs': [ {'mac_address': 'foo', 'ip_address': '10.0.0.1'}, {'mac_address': 'bar', 'ip_address': '192.168.0.1'}, {'mac_address': 'qux', 'ip_address': '169.254.0.0/16'}, {'mac_address': 'baz', 'ip_address': '2003::f'}, ]} allowed_pairs_v4 = ovsfw.OFPort._get_allowed_pairs(port, version=4) allowed_pairs_v6 = ovsfw.OFPort._get_allowed_pairs(port, version=6) expected_aap_v4 = {('foo', '10.0.0.1'), ('bar', '192.168.0.1'), ('qux', '169.254.0.0/16')} expected_aap_v6 = {('baz', '2003::f')} self.assertEqual(expected_aap_v4, allowed_pairs_v4) self.assertEqual(expected_aap_v6, allowed_pairs_v6) def test__get_allowed_pairs_empty(self): port = {} allowed_pairs = ovsfw.OFPort._get_allowed_pairs(port, version=4) self.assertFalse(allowed_pairs) def test_update(self): old_port_dict = self.port.neutron_port_dict new_port_dict = old_port_dict.copy() added_ips = [1, 2, 3] new_port_dict.update({ 'fixed_ips': added_ips, 'allowed_address_pairs': [ {'mac_address': '00:00:00:00:00:01', 'ip_address': '192.168.0.1'}, {'mac_address': '00:00:00:00:00:01', 'ip_address': '2003::f'}], }) self.port.update(new_port_dict) self.assertEqual(new_port_dict, self.port.neutron_port_dict) self.assertIsNot(new_port_dict, self.port.neutron_port_dict) self.assertEqual(added_ips, self.port.fixed_ips) self.assertEqual({('00:00:00:00:00:01', '192.168.0.1')}, self.port.allowed_pairs_v4) self.assertIn(('00:00:00:00:00:01', '2003::f'), self.port.allowed_pairs_v6) class TestSGPortMap(base.BaseTestCase): def setUp(self): super(TestSGPortMap, self).setUp() self.map = ovsfw.SGPortMap() def test_get_or_create_sg_existing_sg(self): self.map.sec_groups['id'] = mock.sentinel sg = self.map.get_or_create_sg('id') self.assertIs(mock.sentinel, sg) def test_get_or_create_sg_nonexisting_sg(self): with mock.patch.object(ovsfw, 'SecurityGroup') as sg_mock: sg = self.map.get_or_create_sg('id') self.assertEqual(sg_mock.return_value, sg) def _check_port(self, port_id, expected_sg_ids): port = self.map.ports[port_id] expected_sgs = [self.map.sec_groups[sg_id] for sg_id in expected_sg_ids] self.assertEqual(port.sec_groups, expected_sgs) def _check_sg(self, sg_id, expected_port_ids): sg = self.map.sec_groups[sg_id] expected_ports = {self.map.ports[port_id] for port_id in expected_port_ids} self.assertEqual(sg.ports, expected_ports) def _create_ports_and_sgroups(self): sg_1 = ovsfw.SecurityGroup(1) sg_2 = ovsfw.SecurityGroup(2) sg_3 = ovsfw.SecurityGroup(3) port_a = create_ofport({'device': 'a'}) port_b = create_ofport({'device': 'b'}) self.map.ports = {'a': port_a, 'b': port_b} self.map.sec_groups = {1: sg_1, 2: sg_2, 3: sg_3} port_a.sec_groups = [sg_1, sg_2] port_b.sec_groups = [sg_2, sg_3] sg_1.ports = {port_a} sg_2.ports = {port_a, port_b} sg_3.ports = {port_b} def test_create_port(self): port = create_ofport({'device': 'a'}) sec_groups = ['1', '2'] port_dict = {'security_groups': sec_groups} self.map.create_port(port, port_dict) self._check_port('a', sec_groups) self._check_sg('1', ['a']) self._check_sg('2', ['a']) def test_update_port_sg_added(self): self._create_ports_and_sgroups() port_dict = {'security_groups': [1, 2, 3]} self.map.update_port(self.map.ports['b'], port_dict) self._check_port('a', [1, 2]) self._check_port('b', [1, 2, 3]) self._check_sg(1, ['a', 'b']) self._check_sg(2, ['a', 'b']) self._check_sg(3, ['b']) def test_update_port_sg_removed(self): self._create_ports_and_sgroups() port_dict = {'security_groups': [1]} self.map.update_port(self.map.ports['b'], port_dict) self._check_port('a', [1, 2]) self._check_port('b', [1]) self._check_sg(1, ['a', 'b']) self._check_sg(2, ['a']) self._check_sg(3, []) def test_remove_port(self): self._create_ports_and_sgroups() self.map.remove_port(self.map.ports['a']) self._check_port('b', [2, 3]) self._check_sg(1, []) self._check_sg(2, ['b']) self._check_sg(3, ['b']) self.assertNotIn('a', self.map.ports) def test_update_rules(self): """Just make sure it doesn't crash""" self.map.update_rules(1, []) def test_update_members(self): """Just make sure we doesn't crash""" self.map.update_members(1, []) class TestConjIdMap(base.BaseTestCase): def setUp(self): super(TestConjIdMap, self).setUp() self.conj_id_map = ovsfw.ConjIdMap() def test_get_conj_id(self): allocated = [] for direction in [firewall.EGRESS_DIRECTION, firewall.INGRESS_DIRECTION]: id_ = self.conj_id_map.get_conj_id( 'sg', 'remote', direction, constants.IPv4) allocated.append(id_) self.assertEqual(len(set(allocated)), 2) self.assertEqual(len(self.conj_id_map.id_map), 2) self.assertEqual(self.conj_id_map.get_conj_id( 'sg', 'remote', firewall.EGRESS_DIRECTION, constants.IPv4), allocated[0]) def test_get_conj_id_invalid(self): self.assertRaises(ValueError, self.conj_id_map.get_conj_id, 'sg', 'remote', 'invalid-direction', constants.IPv6) def test_delete_sg(self): test_data = [('sg1', 'sg1'), ('sg1', 'sg2')] ids = [] for sg_id, remote_sg_id in test_data: ids.append(self.conj_id_map.get_conj_id( sg_id, remote_sg_id, firewall.INGRESS_DIRECTION, constants.IPv6)) result = self.conj_id_map.delete_sg('sg1') self.assertIn(('sg1', ids[0]), result) self.assertIn(('sg2', ids[1]), result) self.assertFalse(self.conj_id_map.id_map) reallocated = self.conj_id_map.get_conj_id( 'sg-foo', 'sg-foo', firewall.INGRESS_DIRECTION, constants.IPv6) self.assertIn(reallocated, ids) class TestConjIPFlowManager(base.BaseTestCase): def setUp(self): super(TestConjIPFlowManager, self).setUp() self.driver = mock.Mock() self.manager = ovsfw.ConjIPFlowManager(self.driver) self.vlan_tag = 100 self.conj_id = 16 def test_update_flows_for_vlan(self): remote_group = self.driver.sg_port_map.get_sg.return_value remote_group.get_ethertype_filtered_addresses.return_value = [ '10.22.3.4'] with mock.patch.object(self.manager.conj_id_map, 'get_conj_id') as get_conj_id_mock: get_conj_id_mock.return_value = self.conj_id self.manager.add(self.vlan_tag, 'sg', 'remote_id', firewall.INGRESS_DIRECTION, constants.IPv4, 0) self.manager.add(self.vlan_tag, 'sg', 'remote_id', firewall.INGRESS_DIRECTION, constants.IPv4, 3) self.manager.update_flows_for_vlan(self.vlan_tag) self.assertEqual(self.driver._add_flow.call_args_list, [mock.call(actions='conjunction(16,1/2)', ct_state='+est-rel-rpl', dl_type=2048, nw_src='10.22.3.4/32', priority=70, reg_net=self.vlan_tag, table=82), mock.call(actions='conjunction(17,1/2)', ct_state='+new-est', dl_type=2048, nw_src='10.22.3.4/32', priority=70, reg_net=self.vlan_tag, table=82), mock.call(actions='conjunction(22,1/2)', ct_state='+est-rel-rpl', dl_type=2048, nw_src='10.22.3.4/32', priority=73, reg_net=self.vlan_tag, table=82), mock.call(actions='conjunction(23,1/2)', ct_state='+new-est', dl_type=2048, nw_src='10.22.3.4/32', priority=73, reg_net=self.vlan_tag, table=82)]) def test_sg_removed(self): with mock.patch.object(self.manager.conj_id_map, 'get_conj_id') as get_id_mock, \ mock.patch.object(self.manager.conj_id_map, 'delete_sg') as delete_sg_mock: get_id_mock.return_value = self.conj_id delete_sg_mock.return_value = [('remote_id', self.conj_id)] self.manager.add(self.vlan_tag, 'sg', 'remote_id', firewall.INGRESS_DIRECTION, constants.IPv4, 0) self.manager.flow_state[self.vlan_tag][( firewall.INGRESS_DIRECTION, constants.IPv4)] = { '10.22.3.4': [self.conj_id]} self.manager.sg_removed('sg') self.driver._add_flow.assert_not_called() self.driver.delete_flows_for_ip_addresses.assert_called_once_with( {'10.22.3.4'}, firewall.INGRESS_DIRECTION, constants.IPv4, self.vlan_tag) class FakeOVSPort(object): def __init__(self, name, port, mac): self.port_name = name self.ofport = port self.vif_mac = mac class TestOVSFirewallDriver(base.BaseTestCase): def setUp(self): super(TestOVSFirewallDriver, self).setUp() mock_bridge = mock.patch.object( ovs_lib, 'OVSBridge', autospec=True).start() self.firewall = ovsfw.OVSFirewallDriver(mock_bridge) self.mock_bridge = self.firewall.int_br self.mock_bridge.reset_mock() self.fake_ovs_port = FakeOVSPort('port', 1, '00:00:00:00:00:00') self.mock_bridge.br.get_vif_port_by_id.return_value = \ self.fake_ovs_port def _prepare_security_group(self): security_group_rules = [ {'ethertype': constants.IPv4, 'protocol': constants.PROTO_NAME_TCP, 'direction': firewall.INGRESS_DIRECTION, 'port_range_min': 123, 'port_range_max': 123}] self.firewall.update_security_group_rules(1, security_group_rules) security_group_rules = [ {'ethertype': constants.IPv4, 'protocol': constants.PROTO_NAME_UDP, 'direction': firewall.EGRESS_DIRECTION}, {'ethertype': constants.IPv6, 'protocol': constants.PROTO_NAME_TCP, 'remote_group_id': 2, 'direction': firewall.EGRESS_DIRECTION}] self.firewall.update_security_group_rules(2, security_group_rules) @property def port_ofport(self): return self.mock_bridge.br.get_vif_port_by_id.return_value.ofport @property def port_mac(self): return self.mock_bridge.br.get_vif_port_by_id.return_value.vif_mac def test_callbacks_registered(self): with mock.patch.object(callbacks_registry, "subscribe") as subscribe: firewall = ovsfw.OVSFirewallDriver(mock.MagicMock()) subscribe.assert_called_once_with( firewall._init_firewall_callback, callbacks_resources.AGENT, callbacks_events.OVS_RESTARTED) def test_initialize_bridge(self): br = self.firewall.initialize_bridge(self.mock_bridge) self.assertEqual(br, self.mock_bridge.deferred.return_value) def test__add_flow_dl_type_formatted_to_string(self): dl_type = 0x0800 self.firewall._add_flow(dl_type=dl_type) def test__add_flow_registers_are_replaced(self): self.firewall._add_flow(in_port=1, reg_port=1, reg_net=2) expected_calls = {'in_port': 1, 'reg{:d}'.format(ovsfw_consts.REG_PORT): 1, 'reg{:d}'.format(ovsfw_consts.REG_NET): 2} self.mock_bridge.br.add_flow.assert_called_once_with( **expected_calls) def test__drop_all_unmatched_flows(self): self.firewall._drop_all_unmatched_flows() expected_calls = [ mock.call(actions='drop', priority=0, table=ovs_consts.BASE_EGRESS_TABLE), mock.call(actions='drop', priority=0, table=ovs_consts.RULES_EGRESS_TABLE), mock.call(actions='drop', priority=0, table=ovs_consts.ACCEPT_OR_INGRESS_TABLE), mock.call(actions='drop', priority=0, table=ovs_consts.BASE_INGRESS_TABLE), mock.call(actions='drop', priority=0, table=ovs_consts.RULES_INGRESS_TABLE)] actual_calls = self.firewall.int_br.br.add_flow.call_args_list self.assertEqual(expected_calls, actual_calls) def test_get_or_create_ofport_non_existing(self): port_dict = { 'device': 'port-id', 'security_groups': [123, 456]} port = self.firewall.get_or_create_ofport(port_dict) sg1, sg2 = sorted( self.firewall.sg_port_map.sec_groups.values(), key=lambda x: x.id) self.assertIn(port, self.firewall.sg_port_map.ports.values()) self.assertEqual( sorted(port.sec_groups, key=lambda x: x.id), [sg1, sg2]) self.assertIn(port, sg1.ports) self.assertIn(port, sg2.ports) def test_get_or_create_ofport_existing(self): port_dict = { 'device': 'port-id', 'security_groups': [123, 456]} of_port = create_ofport(port_dict) self.firewall.sg_port_map.ports[of_port.id] = of_port port = self.firewall.get_or_create_ofport(port_dict) sg1, sg2 = sorted( self.firewall.sg_port_map.sec_groups.values(), key=lambda x: x.id) self.assertIs(of_port, port) self.assertIn(port, self.firewall.sg_port_map.ports.values()) self.assertEqual( sorted(port.sec_groups, key=lambda x: x.id), [sg1, sg2]) self.assertIn(port, sg1.ports) self.assertIn(port, sg2.ports) def test_get_or_create_ofport_changed(self): port_dict = { 'device': 'port-id', 'security_groups': [123, 456]} of_port = create_ofport(port_dict) self.firewall.sg_port_map.ports[of_port.id] = of_port fake_ovs_port = FakeOVSPort('port', 2, '00:00:00:00:00:00') self.mock_bridge.br.get_vif_port_by_id.return_value = \ fake_ovs_port port = self.firewall.get_or_create_ofport(port_dict) self.assertIn(of_port.id, self.firewall.sg_port_map.ports.keys()) self.assertEqual(port.ofport, 2) def test_get_or_create_ofport_missing(self): port_dict = { 'device': 'port-id', 'security_groups': [123, 456]} self.mock_bridge.br.get_vif_port_by_id.return_value = None with testtools.ExpectedException(exceptions.OVSFWPortNotFound): self.firewall.get_or_create_ofport(port_dict) def test_get_or_create_ofport_missing_nocreate(self): port_dict = { 'device': 'port-id', 'security_groups': [123, 456]} self.mock_bridge.br.get_vif_port_by_id.return_value = None self.assertIsNone(self.firewall.get_ofport(port_dict)) self.assertFalse(self.mock_bridge.br.get_vif_port_by_id.called) def test_is_port_managed_managed_port(self): port_dict = {'device': 'port-id'} self.firewall.sg_port_map.ports[port_dict['device']] = object() is_managed = self.firewall.is_port_managed(port_dict) self.assertTrue(is_managed) def test_is_port_managed_not_managed_port(self): port_dict = {'device': 'port-id'} is_managed = self.firewall.is_port_managed(port_dict) self.assertFalse(is_managed) def test_prepare_port_filter(self): port_dict = {'device': 'port-id', 'security_groups': [1], 'fixed_ips': ["10.0.0.1"]} self._prepare_security_group() self.firewall.prepare_port_filter(port_dict) exp_egress_classifier = mock.call( actions='set_field:{:d}->reg5,set_field:{:d}->reg6,' 'resubmit(,{:d})'.format( self.port_ofport, TESTING_VLAN_TAG, ovs_consts.BASE_EGRESS_TABLE), in_port=self.port_ofport, priority=100, table=ovs_consts.TRANSIENT_TABLE) exp_ingress_classifier = mock.call( actions='set_field:{:d}->reg5,set_field:{:d}->reg6,' 'strip_vlan,resubmit(,{:d})'.format( self.port_ofport, TESTING_VLAN_TAG, ovs_consts.BASE_INGRESS_TABLE), dl_dst=self.port_mac, dl_vlan='0x%x' % TESTING_VLAN_TAG, priority=90, table=ovs_consts.TRANSIENT_TABLE) filter_rule = mock.call( actions='ct(commit,zone=NXM_NX_REG6[0..15]),' 'output:{:d},resubmit(,{:d})'.format( self.port_ofport, ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE), dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_TCP, priority=77, reg5=self.port_ofport, ct_state=ovsfw_consts.OF_STATE_NEW_NOT_ESTABLISHED, table=ovs_consts.RULES_INGRESS_TABLE, tcp_dst='0x007b') calls = self.mock_bridge.br.add_flow.call_args_list for call in exp_ingress_classifier, exp_egress_classifier, filter_rule: self.assertIn(call, calls) def test_prepare_port_filter_port_security_disabled(self): port_dict = {'device': 'port-id', 'security_groups': [1], 'port_security_enabled': False} self._prepare_security_group() with mock.patch.object( self.firewall, 'initialize_port_flows') as m_init_flows: self.firewall.prepare_port_filter(port_dict) self.assertFalse(m_init_flows.called) def test_prepare_port_filter_initialized_port(self): port_dict = {'device': 'port-id', 'security_groups': [1]} self._prepare_security_group() self.firewall.prepare_port_filter(port_dict) self.assertFalse(self.mock_bridge.br.delete_flows.called) self.firewall.prepare_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) def test_update_port_filter(self): port_dict = {'device': 'port-id', 'security_groups': [1]} self._prepare_security_group() self.firewall.prepare_port_filter(port_dict) port_dict['security_groups'] = [2] self.mock_bridge.reset_mock() self.firewall.update_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) conj_id = self.firewall.conj_ip_manager.conj_id_map.get_conj_id( 2, 2, firewall.EGRESS_DIRECTION, constants.IPv6) filter_rules = [mock.call( actions='resubmit(,{:d})'.format( ovs_consts.ACCEPT_OR_INGRESS_TABLE), dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_UDP, priority=77, ct_state=ovsfw_consts.OF_STATE_NEW_NOT_ESTABLISHED, reg5=self.port_ofport, table=ovs_consts.RULES_EGRESS_TABLE), mock.call( actions='conjunction({:d},2/2)'.format(conj_id + 6), ct_state=ovsfw_consts.OF_STATE_ESTABLISHED_NOT_REPLY, dl_type=mock.ANY, nw_proto=6, priority=73, reg5=self.port_ofport, table=ovs_consts.RULES_EGRESS_TABLE)] self.mock_bridge.br.add_flow.assert_has_calls( filter_rules, any_order=True) def test_update_port_filter_create_new_port_if_not_present(self): port_dict = {'device': 'port-id', 'security_groups': [1]} self._prepare_security_group() with mock.patch.object( self.firewall, 'prepare_port_filter' ) as prepare_mock, mock.patch.object( self.firewall, 'initialize_port_flows' ) as initialize_port_flows_mock, mock.patch.object( self.firewall, 'add_flows_from_rules' ) as add_flows_from_rules_mock: self.firewall.update_port_filter(port_dict) self.assertFalse(prepare_mock.called) self.assertFalse(self.mock_bridge.br.delete_flows.called) self.assertTrue(initialize_port_flows_mock.called) self.assertTrue(add_flows_from_rules_mock.called) def test_update_port_filter_port_security_disabled(self): port_dict = {'device': 'port-id', 'security_groups': [1]} self._prepare_security_group() self.firewall.prepare_port_filter(port_dict) port_dict['port_security_enabled'] = False self.firewall.update_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) def test_update_port_filter_applies_added_flows(self): """Check flows are applied right after _set_flows is called.""" port_dict = {'device': 'port-id', 'security_groups': [1]} self._prepare_security_group() self.firewall.prepare_port_filter(port_dict) with self.firewall.defer_apply(): self.firewall.update_port_filter(port_dict) self.assertEqual(2, self.mock_bridge.apply_flows.call_count) def test_remove_port_filter(self): port_dict = {'device': 'port-id', 'security_groups': [1]} self._prepare_security_group() self.firewall.prepare_port_filter(port_dict) self.firewall.remove_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) self.assertIn(1, self.firewall.sg_to_delete) def test_remove_port_filter_port_security_disabled(self): port_dict = {'device': 'port-id', 'security_groups': [1]} self.firewall.remove_port_filter(port_dict) self.assertFalse(self.mock_bridge.br.delete_flows.called) def test_update_security_group_rules(self): """Just make sure it doesn't crash""" new_rules = [ {'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_ICMP}, {'ethertype': constants.IPv4, 'direction': firewall.EGRESS_DIRECTION, 'remote_group_id': 2}] self.firewall.update_security_group_rules(1, new_rules) def test_update_security_group_members(self): """Just make sure it doesn't crash""" new_members = {constants.IPv4: [1, 2, 3, 4]} self.firewall.update_security_group_members(2, new_members) def test__cleanup_stale_sg(self): self._prepare_security_group() self.firewall.sg_to_delete = {1} with mock.patch.object(self.firewall.conj_ip_manager, 'sg_removed') as sg_removed_mock,\ mock.patch.object(self.firewall.sg_port_map, 'delete_sg') as delete_sg_mock: self.firewall._cleanup_stale_sg() sg_removed_mock.assert_called_once_with(1) delete_sg_mock.assert_called_once_with(1) def test_get_ovs_port(self): ovs_port = self.firewall.get_ovs_port('port_id') self.assertEqual(self.fake_ovs_port, ovs_port) def test_get_ovs_port_non_existent(self): self.mock_bridge.br.get_vif_port_by_id.return_value = None with testtools.ExpectedException(exceptions.OVSFWPortNotFound): self.firewall.get_ovs_port('port_id') def test__initialize_egress_no_port_security_sends_to_egress(self): self.mock_bridge.br.db_get_val.return_value = {'tag': TESTING_VLAN_TAG} self.firewall._initialize_egress_no_port_security('port_id') expected_call = mock.call( table=ovs_consts.TRANSIENT_TABLE, priority=100, in_port=self.fake_ovs_port.ofport, actions='set_field:%d->reg%d,' 'set_field:%d->reg%d,' 'resubmit(,%d)' % ( self.fake_ovs_port.ofport, ovsfw_consts.REG_PORT, TESTING_VLAN_TAG, ovsfw_consts.REG_NET, ovs_consts.ACCEPT_OR_INGRESS_TABLE) ) calls = self.mock_bridge.br.add_flow.call_args_list self.assertIn(expected_call, calls) def test__initialize_egress_no_port_security_no_tag(self): self.mock_bridge.br.db_get_val.return_value = {} self.firewall._initialize_egress_no_port_security('port_id') self.assertFalse(self.mock_bridge.br.add_flow.called) def test__remove_egress_no_port_security_deletes_flow(self): self.mock_bridge.br.db_get_val.return_value = {'tag': TESTING_VLAN_TAG} self.firewall.sg_port_map.unfiltered['port_id'] = 1 self.firewall._remove_egress_no_port_security('port_id') expected_call = mock.call( table=ovs_consts.TRANSIENT_TABLE, in_port=self.fake_ovs_port.ofport, ) calls = self.mock_bridge.br.delete_flows.call_args_list self.assertIn(expected_call, calls) def test__remove_egress_no_port_security_non_existing_port(self): with testtools.ExpectedException(exceptions.OVSFWPortNotHandled): self.firewall._remove_egress_no_port_security('foo') def test_process_trusted_ports_caches_port_id(self): self.firewall.process_trusted_ports(['port_id']) self.assertIn('port_id', self.firewall.sg_port_map.unfiltered) def test_process_trusted_ports_port_not_found(self): """Check that exception is not propagated outside.""" self.mock_bridge.br.get_vif_port_by_id.return_value = None self.firewall.process_trusted_ports(['port_id']) # Processing should have failed so port is not cached self.assertNotIn('port_id', self.firewall.sg_port_map.unfiltered) def test_remove_trusted_ports_clears_cached_port_id(self): self.firewall.sg_port_map.unfiltered['port_id'] = 1 self.firewall.remove_trusted_ports(['port_id']) self.assertNotIn('port_id', self.firewall.sg_port_map.unfiltered) def test_remove_trusted_ports_not_managed_port(self): """Check that exception is not propagated outside.""" self.firewall.remove_trusted_ports(['port_id']) class TestCookieContext(base.BaseTestCase): def setUp(self): super(TestCookieContext, self).setUp() # Don't attempt to connect to ovsdb mock.patch('neutron.agent.ovsdb.api.from_config').start() # Don't trigger iptables -> ovsfw migration mock.patch( 'neutron.agent.linux.openvswitch_firewall.iptables.Helper').start() self.execute = mock.patch.object( utils, "execute", spec=utils.execute).start() bridge = ovs_bridge.OVSAgentBridge('foo') mock.patch.object( ovsfw.OVSFirewallDriver, 'initialize_bridge', return_value=bridge.deferred( full_ordered=True, use_bundle=True)).start() self.firewall = ovsfw.OVSFirewallDriver(bridge) # Remove calls from firewall initialization self.execute.reset_mock() def test_cookie_is_different_in_context(self): default_cookie = self.firewall.int_br.br.default_cookie with self.firewall.update_cookie_context(): self.firewall._add_flow(actions='drop') update_cookie = self.firewall._update_cookie self.firewall._add_flow(actions='drop') expected_calls = [ mock.call( mock.ANY, process_input='hard_timeout=0,idle_timeout=0,priority=1,' 'cookie=%d,actions=drop' % cookie, run_as_root=mock.ANY, ) for cookie in (update_cookie, default_cookie) ] self.execute.assert_has_calls(expected_calls) def test_context_cookie_is_not_left_as_used(self): with self.firewall.update_cookie_context(): update_cookie = self.firewall._update_cookie self.assertNotIn( update_cookie, self.firewall.int_br.br._reserved_cookies) neutron-12.1.1/neutron/tests/unit/agent/linux/test_ip_monitor.py0000664000175000017500000000301013553660047025153 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.linux import ip_monitor from neutron.tests import base class TestIPMonitorEvent(base.BaseTestCase): def test_from_text_parses_added_line(self): event = ip_monitor.IPMonitorEvent.from_text( '3: wlp3s0 inet 192.168.3.59/24 brd 192.168.3.255 ' 'scope global dynamic wlp3s0\ valid_lft 300sec ' 'preferred_lft 300sec') self.assertEqual('wlp3s0', event.interface) self.assertTrue(event.added) self.assertEqual('192.168.3.59/24', event.cidr) def test_from_text_parses_deleted_line(self): event = ip_monitor.IPMonitorEvent.from_text( 'Deleted 1: lo inet 127.0.0.2/8 scope host secondary lo\'' ' valid_lft forever preferred_lft forever') self.assertEqual('lo', event.interface) self.assertFalse(event.added) self.assertEqual('127.0.0.2/8', event.cidr) neutron-12.1.1/neutron/tests/unit/agent/linux/test_pd.py0000664000175000017500000001065313553660047023412 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.agent.l3 import dvr_edge_router from neutron.agent.l3 import dvr_local_router from neutron.agent.l3 import legacy_router from neutron.agent.linux import pd from neutron.tests import base as tests_base class FakeRouter(object): def __init__(self, router_id): self.router_id = router_id class TestPrefixDelegation(tests_base.DietTestCase): def test_remove_router(self): l3_agent = mock.Mock() router_id = 1 l3_agent.pd.routers = {router_id: pd.get_router_entry(None, True)} pd.remove_router(None, None, l3_agent, router=FakeRouter(router_id)) self.assertTrue(l3_agent.pd.delete_router_pd.called) self.assertEqual({}, l3_agent.pd.routers) def _test_add_update_pd(self, l3_agent, router, ns_name): # add entry pd.add_router(None, None, l3_agent, router=router) pd_router = l3_agent.pd.routers.get(router.router_id) self.assertEqual(ns_name, pd_router.get('ns_name')) # clear namespace name, update entry pd_router['ns_name'] = None pd.update_router(None, None, l3_agent, router=router) pd_router = l3_agent.pd.routers.get(router.router_id) self.assertEqual(ns_name, pd_router.get('ns_name')) def test_add_update_dvr_edge_router(self): l3_agent = mock.Mock() l3_agent.pd.routers = {} router_id = '1' ri = dvr_edge_router.DvrEdgeRouter(l3_agent, 'host', router_id, mock.Mock(), mock.Mock(), mock.Mock()) ns_name = ri.snat_namespace.name self._test_add_update_pd(l3_agent, ri, ns_name) def test_add_update_dvr_local_router(self): l3_agent = mock.Mock() l3_agent.pd.routers = {} router_id = '1' ri = dvr_local_router.DvrLocalRouter(l3_agent, 'host', router_id, mock.Mock(), mock.Mock(), mock.Mock()) ns_name = ri.ns_name self._test_add_update_pd(l3_agent, ri, ns_name) def test_add_update_legacy_router(self): l3_agent = mock.Mock() l3_agent.pd.routers = {} router_id = '1' ri = legacy_router.LegacyRouter(l3_agent, router_id, mock.Mock(), mock.Mock(), mock.Mock()) ns_name = ri.ns_name self._test_add_update_pd(l3_agent, ri, ns_name) def test_update_no_router_exception(self): l3_agent = mock.Mock() l3_agent.pd.routers = {} router = mock.Mock() router.router_id = '1' with mock.patch.object(pd.LOG, 'exception') as log: pd.update_router(None, None, l3_agent, router=router) self.assertTrue(log.called) def test_remove_stale_ri_ifname(self): pd_info_1 = mock.Mock() pd_info_1.ri_ifname = 'STALE' pd_info_2 = mock.Mock() pd_info_2.ri_ifname = 'NOT_STALE' router = { 'subnets': { 'FAKE_SUBNET_ID1': pd_info_1, 'FAKE_SUBNET_ID2': pd_info_2}} class FakePD(pd.PrefixDelegation): def __init__(self, router): self.routers = {'FAKE_ROUTER_ID': router} fake_pd = FakePD(router) fake_pd._delete_pd = mock.Mock() fake_pd.remove_stale_ri_ifname('FAKE_ROUTER_ID', 'STALE') fake_pd._delete_pd.assert_called_with(router, pd_info_1) self.assertEqual(len(router['subnets'].keys()), 1) neutron-12.1.1/neutron/tests/unit/agent/linux/test_interface.py0000664000175000017500000006641513553660047024756 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants from neutron.agent.common import ovs_lib from neutron.agent.linux import interface from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.conf.agent import common as config from neutron.tests import base class BaseChild(interface.LinuxInterfaceDriver): def plug_new(*args): pass def unplug(*args): pass class FakeNetwork(object): id = '12345678-1234-5678-90ab-ba0987654321' class FakeSubnet(object): cidr = '192.168.1.1/24' class FakeAllocation(object): subnet = FakeSubnet() ip_address = '192.168.1.2' ip_version = 4 class FakePort(object): id = 'abcdef01-1234-5678-90ab-ba0987654321' fixed_ips = [FakeAllocation] device_id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' network = FakeNetwork() network_id = network.id class TestBase(base.BaseTestCase): def setUp(self): super(TestBase, self).setUp() self.conf = config.setup_conf() config.register_interface_opts(self.conf) self.ip_dev_p = mock.patch.object(ip_lib, 'IPDevice') self.ip_dev = self.ip_dev_p.start() self.ip_p = mock.patch.object(ip_lib, 'IPWrapper') self.ip = self.ip_p.start() self.device_exists_p = mock.patch.object(ip_lib, 'device_exists') self.device_exists = self.device_exists_p.start() class TestABCDriver(TestBase): def setUp(self): super(TestABCDriver, self).setUp() mock_link_addr = mock.PropertyMock(return_value='aa:bb:cc:dd:ee:ff') type(self.ip_dev().link).address = mock_link_addr def test_get_device_name(self): bc = BaseChild(self.conf) device_name = bc.get_device_name(FakePort()) self.assertEqual('tapabcdef01-12', device_name) def test_init_router_port(self): addresses = [dict(scope='global', dynamic=False, cidr='172.16.77.240/24')] self.ip_dev().addr.list = mock.Mock(return_value=addresses) self.ip_dev().route.list_onlink_routes.return_value = [] bc = BaseChild(self.conf) ns = '12345678-1234-5678-90ab-ba0987654321' bc.init_router_port('tap0', ['192.168.1.2/24'], namespace=ns, extra_subnets=[{'cidr': '172.20.0.0/24'}]) self.ip_dev.assert_has_calls( [mock.call('tap0', namespace=ns), mock.call().addr.list(), mock.call().addr.delete('172.16.77.240/24'), mock.call().addr.add('192.168.1.2/24'), mock.call('tap0', namespace=ns), mock.call().route.list_onlink_routes(constants.IP_VERSION_4), mock.call().route.list_onlink_routes(constants.IP_VERSION_6), mock.call().route.add_onlink_route('172.20.0.0/24')]) def test_init_router_port_delete_onlink_routes(self): addresses = [dict(scope='global', dynamic=False, cidr='172.16.77.240/24')] self.ip_dev().addr.list = mock.Mock(return_value=addresses) self.ip_dev().route.list_onlink_routes.return_value = [ {'cidr': '172.20.0.0/24'}] bc = BaseChild(self.conf) ns = '12345678-1234-5678-90ab-ba0987654321' bc.init_router_port('tap0', ['192.168.1.2/24'], namespace=ns) self.ip_dev.assert_has_calls( [mock.call().route.list_onlink_routes(constants.IP_VERSION_4), mock.call().route.list_onlink_routes(constants.IP_VERSION_6), mock.call().route.delete_onlink_route('172.20.0.0/24')]) def test_l3_init_with_preserve(self): addresses = [dict(scope='global', dynamic=False, cidr='192.168.1.3/32')] self.ip_dev().addr.list = mock.Mock(return_value=addresses) bc = BaseChild(self.conf) ns = '12345678-1234-5678-90ab-ba0987654321' bc.init_l3('tap0', ['192.168.1.2/24'], namespace=ns, preserve_ips=['192.168.1.3/32']) self.ip_dev.assert_has_calls( [mock.call('tap0', namespace=ns), mock.call().addr.list(), mock.call().addr.add('192.168.1.2/24')]) self.assertFalse(self.ip_dev().addr.delete.called) self.assertFalse(self.ip_dev().delete_addr_and_conntrack_state.called) def _test_l3_init_clean_connections(self, clean_connections): addresses = [ dict(scope='global', dynamic=False, cidr='10.0.0.1/24'), dict(scope='global', dynamic=False, cidr='10.0.0.3/32')] self.ip_dev().addr.list = mock.Mock(return_value=addresses) bc = BaseChild(self.conf) ns = '12345678-1234-5678-90ab-ba0987654321' bc.init_l3('tap0', ['10.0.0.1/24'], namespace=ns, clean_connections=clean_connections) delete = self.ip_dev().delete_addr_and_conntrack_state if clean_connections: delete.assert_called_once_with('10.0.0.3/32') else: self.assertFalse(delete.called) def test_l3_init_with_clean_connections(self): self._test_l3_init_clean_connections(True) def test_l3_init_without_clean_connections(self): self._test_l3_init_clean_connections(False) def test_init_router_port_ipv6_with_gw_ip(self): addresses = [dict(scope='global', dynamic=False, cidr='2001:db8:a::123/64')] self.ip_dev().addr.list = mock.Mock(return_value=addresses) self.ip_dev().route.list_onlink_routes.return_value = [] bc = BaseChild(self.conf) ns = '12345678-1234-5678-90ab-ba0987654321' new_cidr = '2001:db8:a::124/64' kwargs = {'namespace': ns, 'extra_subnets': [{'cidr': '2001:db8:b::/64'}]} bc.init_router_port('tap0', [new_cidr], **kwargs) expected_calls = ( [mock.call('tap0', namespace=ns), mock.call().addr.list(), mock.call().addr.delete('2001:db8:a::123/64'), mock.call().addr.add('2001:db8:a::124/64')]) expected_calls += ( [mock.call('tap0', namespace=ns), mock.call().route.list_onlink_routes(constants.IP_VERSION_4), mock.call().route.list_onlink_routes(constants.IP_VERSION_6), mock.call().route.add_onlink_route('2001:db8:b::/64')]) self.ip_dev.assert_has_calls(expected_calls) def test_init_router_port_ext_gw_with_dual_stack(self): old_addrs = [dict(ip_version=4, scope='global', dynamic=False, cidr='172.16.77.240/24'), dict(ip_version=6, scope='global', dynamic=False, cidr='2001:db8:a::123/64')] self.ip_dev().addr.list = mock.Mock(return_value=old_addrs) self.ip_dev().route.list_onlink_routes.return_value = [] bc = BaseChild(self.conf) ns = '12345678-1234-5678-90ab-ba0987654321' new_cidrs = ['192.168.1.2/24', '2001:db8:a::124/64'] bc.init_router_port('tap0', new_cidrs, namespace=ns, extra_subnets=[{'cidr': '172.20.0.0/24'}]) self.ip_dev.assert_has_calls( [mock.call('tap0', namespace=ns), mock.call().addr.list(), mock.call().addr.add('192.168.1.2/24'), mock.call().addr.add('2001:db8:a::124/64'), mock.call().addr.delete('172.16.77.240/24'), mock.call().addr.delete('2001:db8:a::123/64'), mock.call().route.list_onlink_routes(constants.IP_VERSION_4), mock.call().route.list_onlink_routes(constants.IP_VERSION_6), mock.call().route.add_onlink_route('172.20.0.0/24')], any_order=True) def test_init_router_port_with_ipv6_delete_onlink_routes(self): addresses = [dict(scope='global', dynamic=False, cidr='2001:db8:a::123/64')] route = '2001:db8:a::/64' self.ip_dev().addr.list = mock.Mock(return_value=addresses) self.ip_dev().route.list_onlink_routes.return_value = [{'cidr': route}] bc = BaseChild(self.conf) ns = '12345678-1234-5678-90ab-ba0987654321' bc.init_router_port('tap0', ['2001:db8:a::124/64'], namespace=ns) self.ip_dev.assert_has_calls( [mock.call().route.list_onlink_routes(constants.IP_VERSION_4), mock.call().route.list_onlink_routes(constants.IP_VERSION_6), mock.call().route.delete_onlink_route(route)]) def test_l3_init_with_duplicated_ipv6(self): addresses = [dict(scope='global', dynamic=False, cidr='2001:db8:a::123/64')] self.ip_dev().addr.list = mock.Mock(return_value=addresses) bc = BaseChild(self.conf) ns = '12345678-1234-5678-90ab-ba0987654321' bc.init_l3('tap0', ['2001:db8:a::123/64'], namespace=ns) self.assertFalse(self.ip_dev().addr.add.called) def test_l3_init_with_duplicated_ipv6_uncompact(self): addresses = [dict(scope='global', dynamic=False, cidr='2001:db8:a::123/64')] self.ip_dev().addr.list = mock.Mock(return_value=addresses) bc = BaseChild(self.conf) ns = '12345678-1234-5678-90ab-ba0987654321' bc.init_l3('tap0', ['2001:db8:a:0000:0000:0000:0000:0123/64'], namespace=ns) self.assertFalse(self.ip_dev().addr.add.called) def test_l3_init_with_duplicated_ipv6_dynamic(self): device_name = 'tap0' cidr = '2001:db8:a::123/64' ns = '12345678-1234-5678-90ab-ba0987654321' addresses = [dict(scope='global', dynamic=True, cidr=cidr)] self.ip_dev().addr.list = mock.Mock(return_value=addresses) bc = BaseChild(self.conf) bc.init_l3(device_name, [cidr], namespace=ns) self.ip_dev.assert_has_calls( [mock.call(device_name, namespace=ns), mock.call().addr.list(), mock.call().addr.delete(cidr), mock.call().addr.add(cidr)]) def test_l3_init_with_duplicated_ipv6_lla(self): device_name = 'tap0' cidr = 'fe80::a8bb:ccff:fedd:eeff/64' ns = '12345678-1234-5678-90ab-ba0987654321' addresses = [dict(scope='link', dynamic=False, cidr=cidr)] self.ip_dev().addr.list = mock.Mock(return_value=addresses) bc = BaseChild(self.conf) bc.init_l3(device_name, [cidr], namespace=ns) self.ip_dev.assert_has_calls( [mock.call(device_name, namespace=ns), mock.call().addr.list()]) # The above assert won't verify there were no extra calls right # after list() self.assertFalse(self.ip_dev().addr.add.called) def test_l3_init_with_not_present_ipv6_lla(self): device_name = 'tap0' cidr = 'fe80::a8bb:ccff:fedd:eeff/64' ns = '12345678-1234-5678-90ab-ba0987654321' self.ip_dev().addr.list = mock.Mock(return_value=[]) bc = BaseChild(self.conf) bc.init_l3(device_name, [cidr], namespace=ns) self.ip_dev.assert_has_calls( [mock.call(device_name, namespace=ns), mock.call().addr.list(), mock.call().addr.add(cidr)]) def test_add_ipv6_addr(self): device_name = 'tap0' cidr = '2001:db8::/64' ns = '12345678-1234-5678-90ab-ba0987654321' bc = BaseChild(self.conf) bc.add_ipv6_addr(device_name, cidr, ns) self.ip_dev.assert_has_calls( [mock.call(device_name, namespace=ns), mock.call().addr.add(cidr, 'global')]) def test_delete_ipv6_addr(self): device_name = 'tap0' cidr = '2001:db8::/64' ns = '12345678-1234-5678-90ab-ba0987654321' bc = BaseChild(self.conf) bc.delete_ipv6_addr(device_name, cidr, ns) self.ip_dev.assert_has_calls( [mock.call(device_name, namespace=ns), mock.call().delete_addr_and_conntrack_state(cidr)]) def test_delete_ipv6_addr_with_prefix(self): device_name = 'tap0' prefix = '2001:db8::/48' in_cidr = '2001:db8::/64' out_cidr = '2001:db7::/64' ns = '12345678-1234-5678-90ab-ba0987654321' in_addresses = [dict(scope='global', dynamic=False, cidr=in_cidr)] out_addresses = [dict(scope='global', dynamic=False, cidr=out_cidr)] # Initially set the address list to be empty self.ip_dev().addr.list = mock.Mock(return_value=[]) bc = BaseChild(self.conf) # Call delete_v6addr_with_prefix when the address list is empty bc.delete_ipv6_addr_with_prefix(device_name, prefix, ns) # Assert that delete isn't called self.assertFalse(self.ip_dev().delete_addr_and_conntrack_state.called) # Set the address list to contain only an address outside of the range # of the given prefix self.ip_dev().addr.list = mock.Mock(return_value=out_addresses) bc.delete_ipv6_addr_with_prefix(device_name, prefix, ns) # Assert that delete isn't called self.assertFalse(self.ip_dev().delete_addr_and_conntrack_state.called) # Set the address list to contain only an address inside of the range # of the given prefix self.ip_dev().addr.list = mock.Mock(return_value=in_addresses) bc.delete_ipv6_addr_with_prefix(device_name, prefix, ns) # Assert that delete is called self.ip_dev.assert_has_calls( [mock.call(device_name, namespace=ns), mock.call().addr.list(scope='global', filters=['permanent']), mock.call().delete_addr_and_conntrack_state(in_cidr)]) def test_get_ipv6_llas(self): ns = '12345678-1234-5678-90ab-ba0987654321' addresses = [dict(scope='link', dynamic=False, cidr='fe80:cafe::/64')] self.ip_dev().addr.list = mock.Mock(return_value=addresses) device_name = self.ip_dev().name bc = BaseChild(self.conf) llas = bc.get_ipv6_llas(device_name, ns) self.assertEqual(addresses, llas) self.ip_dev.assert_has_calls( [mock.call(device_name, namespace=ns), mock.call().addr.list(scope='link', ip_version=6)]) def test_set_mtu_logs_once(self): bc = BaseChild(self.conf) with mock.patch('neutron.agent.linux.interface.LOG.warning') as log: bc.set_mtu('dev', 9999) log.assert_called_once_with(mock.ANY) class TestOVSInterfaceDriver(TestBase): def test_get_device_name(self): br = interface.OVSInterfaceDriver(self.conf) device_name = br.get_device_name(FakePort()) self.assertEqual('tapabcdef01-12', device_name) def test_plug_no_ns(self): self._test_plug() def test_plug_with_ns(self): self._test_plug(namespace='01234567-1234-1234-99') def test_plug_alt_bridge(self): self._test_plug(bridge='br-foo') def test_plug_configured_bridge(self): br = 'br-v' self.conf.set_override('ovs_use_veth', False) self.conf.set_override('ovs_integration_bridge', br) self.assertEqual(self.conf.ovs_integration_bridge, br) def device_exists(dev, namespace=None): return dev == br ovs = interface.OVSInterfaceDriver(self.conf) with mock.patch.object(ovs, '_ovs_add_port') as add_port: self.device_exists.side_effect = device_exists ovs.plug('01234567-1234-1234-99', 'port-1234', 'tap0', 'aa:bb:cc:dd:ee:ff', bridge=None, namespace=None) add_port.assert_called_once_with('br-v', 'tap0', 'port-1234', 'aa:bb:cc:dd:ee:ff', internal=True) def _test_plug(self, bridge=None, namespace=None): with mock.patch('neutron.agent.ovsdb.impl_idl._connection'): if not bridge: bridge = 'br-int' def device_exists(dev, namespace=None): return dev == bridge with mock.patch.object(ovs_lib.OVSBridge, 'replace_port') as replace: ovs = interface.OVSInterfaceDriver(self.conf) self.device_exists.side_effect = device_exists link = self.ip.return_value.device.return_value.link link.set_address.side_effect = (RuntimeError, None) ovs.plug('01234567-1234-1234-99', 'port-1234', 'tap0', 'aa:bb:cc:dd:ee:ff', bridge=bridge, namespace=namespace, mtu=9000) replace.assert_called_once_with( 'tap0', ('type', 'internal'), ('external_ids', { 'iface-id': 'port-1234', 'iface-status': 'active', 'attached-mac': 'aa:bb:cc:dd:ee:ff'})) expected = [ mock.call(), mock.call().device('tap0'), mock.call().device().link.set_address('aa:bb:cc:dd:ee:ff'), mock.call().device().link.set_address('aa:bb:cc:dd:ee:ff')] if namespace: expected.extend( [mock.call().ensure_namespace(namespace), mock.call().ensure_namespace().add_device_to_namespace( mock.ANY)]) expected.extend([ mock.call(namespace=namespace), mock.call().device('tap0'), mock.call().device().link.set_mtu(9000), mock.call().device().link.set_up(), ]) self.ip.assert_has_calls(expected) def test_unplug(self, bridge=None): if not bridge: bridge = 'br-int' with mock.patch('neutron.agent.common.ovs_lib.OVSBridge') as ovs_br: ovs = interface.OVSInterfaceDriver(self.conf) ovs.unplug('tap0') ovs_br.assert_has_calls([mock.call(bridge), mock.call().delete_port('tap0')]) class TestOVSInterfaceDriverWithVeth(TestOVSInterfaceDriver): def setUp(self): super(TestOVSInterfaceDriverWithVeth, self).setUp() self.conf.set_override('ovs_use_veth', True) def test_get_device_name(self): br = interface.OVSInterfaceDriver(self.conf) device_name = br.get_device_name(FakePort()) self.assertEqual('ns-abcdef01-12', device_name) def test_plug_with_prefix(self): self._test_plug(devname='qr-0', prefix='qr-') def _test_plug(self, devname=None, bridge=None, namespace=None, prefix=None): with mock.patch('neutron.agent.ovsdb.impl_idl._connection'): if not devname: devname = 'ns-0' if not bridge: bridge = 'br-int' def device_exists(dev, namespace=None): return dev == bridge ovs = interface.OVSInterfaceDriver(self.conf) self.device_exists.side_effect = device_exists root_dev = mock.Mock() ns_dev = mock.Mock() self.ip().add_veth = mock.Mock(return_value=(root_dev, ns_dev)) mock.patch.object( interface, '_get_veth', return_value=(root_dev, ns_dev)).start() expected = [mock.call(), mock.call().add_veth('tap0', devname, namespace2=namespace)] with mock.patch.object(ovs_lib.OVSBridge, 'replace_port') as replace: ovs.plug('01234567-1234-1234-99', 'port-1234', devname, 'aa:bb:cc:dd:ee:ff', bridge=bridge, namespace=namespace, prefix=prefix, mtu=9000) replace.assert_called_once_with( 'tap0', ('external_ids', { 'iface-id': 'port-1234', 'iface-status': 'active', 'attached-mac': 'aa:bb:cc:dd:ee:ff'})) ns_dev.assert_has_calls( [mock.call.link.set_address('aa:bb:cc:dd:ee:ff')]) ns_dev.assert_has_calls([mock.call.link.set_mtu(9000)]) root_dev.assert_has_calls([mock.call.link.set_mtu(9000)]) self.ip.assert_has_calls(expected) root_dev.assert_has_calls([mock.call.link.set_up()]) ns_dev.assert_has_calls([mock.call.link.set_up()]) def test_unplug(self, bridge=None): if not bridge: bridge = 'br-int' with mock.patch('neutron.agent.common.ovs_lib.OVSBridge') as ovs_br: ovs = interface.OVSInterfaceDriver(self.conf) ovs.unplug('ns-0', bridge=bridge) ovs_br.assert_has_calls([mock.call(bridge), mock.call().delete_port('tap0')]) self.ip_dev.assert_has_calls([mock.call('ns-0', namespace=None), mock.call().link.delete()]) class TestBridgeInterfaceDriver(TestBase): def test_get_device_name(self): br = interface.BridgeInterfaceDriver(self.conf) device_name = br.get_device_name(FakePort()) self.assertEqual('ns-abcdef01-12', device_name) def test_plug_no_ns(self): self._test_plug() def test_plug_with_ns(self): self._test_plug(namespace='01234567-1234-1234-99') def _test_plug(self, namespace=None): def device_exists(device, namespace=None): return device.startswith('brq') root_veth = mock.Mock() ns_veth = mock.Mock() self.ip().add_veth = mock.Mock(return_value=(root_veth, ns_veth)) mock.patch.object( interface, '_get_veth', return_value=(root_veth, ns_veth)).start() self.device_exists.side_effect = device_exists br = interface.BridgeInterfaceDriver(self.conf) mac_address = 'aa:bb:cc:dd:ee:ff' br.plug('01234567-1234-1234-99', 'port-1234', 'ns-0', mac_address, namespace=namespace, mtu=9000) ip_calls = [mock.call(), mock.call().add_veth('tap0', 'ns-0', namespace2=namespace)] ns_veth.assert_has_calls([mock.call.link.set_address(mac_address)]) ns_veth.assert_has_calls([mock.call.link.set_mtu(9000)]) root_veth.assert_has_calls([mock.call.link.set_mtu(9000)]) self.ip.assert_has_calls(ip_calls) root_veth.assert_has_calls([mock.call.link.set_up()]) ns_veth.assert_has_calls([mock.call.link.set_up()]) def test_plug_dev_exists(self): self.device_exists.return_value = True with mock.patch('neutron.agent.linux.interface.LOG.info') as log: br = interface.BridgeInterfaceDriver(self.conf) br.plug('01234567-1234-1234-99', 'port-1234', 'tap0', 'aa:bb:cc:dd:ee:ff') self.assertFalse(self.ip_dev.called) self.assertEqual(log.call_count, 1) def test_unplug_no_device(self): self.device_exists.return_value = False self.ip_dev().link.delete.side_effect = RuntimeError with mock.patch('neutron.agent.linux.interface.LOG') as log: br = interface.BridgeInterfaceDriver(self.conf) br.unplug('tap0') [mock.call(), mock.call('tap0'), mock.call().link.delete()] self.assertEqual(log.error.call_count, 1) def test_unplug(self): self.device_exists.return_value = True with mock.patch('neutron.agent.linux.interface.LOG.debug') as log: br = interface.BridgeInterfaceDriver(self.conf) br.unplug('tap0') self.assertEqual(log.call_count, 1) self.ip_dev.assert_has_calls([mock.call('tap0', namespace=None), mock.call().link.delete()]) class TestIVSInterfaceDriver(TestBase): def test_get_device_name(self): br = interface.IVSInterfaceDriver(self.conf) device_name = br.get_device_name(FakePort()) self.assertEqual('ns-abcdef01-12', device_name) def test_plug_with_prefix(self): self._test_plug(devname='qr-0', prefix='qr-') def _test_plug(self, devname=None, namespace=None, prefix=None): if not devname: devname = 'ns-0' def device_exists(dev, namespace=None): return dev == 'indigo' ivs = interface.IVSInterfaceDriver(self.conf) self.device_exists.side_effect = device_exists root_dev = mock.Mock() _ns_dev = mock.Mock() ns_dev = mock.Mock() self.ip().add_veth = mock.Mock(return_value=(root_dev, _ns_dev)) self.ip().device = mock.Mock(return_value=(ns_dev)) expected = [mock.call(), mock.call().add_veth('tap0', devname), mock.call().device(devname)] ivsctl_cmd = ['ivs-ctl', 'add-port', 'tap0'] with mock.patch.object(utils, 'execute') as execute: ivs.plug('01234567-1234-1234-99', 'port-1234', devname, 'aa:bb:cc:dd:ee:ff', namespace=namespace, prefix=prefix, mtu=9000) execute.assert_called_once_with(ivsctl_cmd, run_as_root=True) ns_dev.assert_has_calls( [mock.call.link.set_address('aa:bb:cc:dd:ee:ff')]) ns_dev.assert_has_calls([mock.call.link.set_mtu(9000)]) root_dev.assert_has_calls([mock.call.link.set_mtu(9000)]) if namespace: expected.extend( [mock.call().ensure_namespace(namespace), mock.call().ensure_namespace().add_device_to_namespace( mock.ANY)]) self.ip.assert_has_calls(expected) root_dev.assert_has_calls([mock.call.link.set_up()]) ns_dev.assert_has_calls([mock.call.link.set_up()]) def test_plug_namespace(self): self._test_plug(namespace='mynamespace') def test_unplug(self): ivs = interface.IVSInterfaceDriver(self.conf) ivsctl_cmd = ['ivs-ctl', 'del-port', 'tap0'] with mock.patch.object(utils, 'execute') as execute: ivs.unplug('ns-0') execute.assert_called_once_with(ivsctl_cmd, run_as_root=True) self.ip_dev.assert_has_calls([mock.call('ns-0', namespace=None), mock.call().link.delete()]) neutron-12.1.1/neutron/tests/unit/agent/linux/test_external_process.py0000664000175000017500000003164613553660047026374 0ustar zuulzuul00000000000000# Copyright 2012 New Dream Network, LLC (DreamHost) # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os.path import mock from oslo_utils import fileutils import psutil from neutron.agent.linux import external_process as ep from neutron.tests import base from neutron.tests import tools TEST_UUID = 'test-uuid' TEST_SERVICE = 'testsvc' TEST_PID = 1234 TEST_CMDLINE = 'python foo --router_id=%s' class BaseTestProcessMonitor(base.BaseTestCase): def setUp(self): super(BaseTestProcessMonitor, self).setUp() self.log_patch = mock.patch("neutron.agent.linux.external_process." "LOG.error") self.error_log = self.log_patch.start() self.spawn_patch = mock.patch("eventlet.spawn") self.eventlent_spawn = self.spawn_patch.start() # create a default process monitor self.create_child_process_monitor('respawn') def create_child_process_monitor(self, action): conf = mock.Mock() conf.AGENT.check_child_processes_action = action conf.AGENT.check_child_processes = True self.pmonitor = ep.ProcessMonitor( config=conf, resource_type='test') def get_monitored_process(self, uuid, service=None): monitored_process = mock.Mock() self.pmonitor.register(uuid=uuid, service_name=service, monitored_process=monitored_process) return monitored_process class TestProcessMonitor(BaseTestProcessMonitor): def test_error_logged(self): pm = self.get_monitored_process(TEST_UUID) pm.active = False self.pmonitor._check_child_processes() self.assertTrue(self.error_log.called) def test_exit_handler(self): self.create_child_process_monitor('exit') pm = self.get_monitored_process(TEST_UUID) pm.active = False with mock.patch.object(ep.ProcessMonitor, '_exit_handler') as exit_handler: self.pmonitor._check_child_processes() exit_handler.assert_called_once_with(TEST_UUID, None) def test_register(self): pm = self.get_monitored_process(TEST_UUID) self.assertEqual(len(self.pmonitor._monitored_processes), 1) self.assertIn(pm, self.pmonitor._monitored_processes.values()) def test_register_same_service_twice(self): self.get_monitored_process(TEST_UUID) self.get_monitored_process(TEST_UUID) self.assertEqual(len(self.pmonitor._monitored_processes), 1) def test_register_different_service_types(self): self.get_monitored_process(TEST_UUID) self.get_monitored_process(TEST_UUID, TEST_SERVICE) self.assertEqual(len(self.pmonitor._monitored_processes), 2) def test_unregister(self): self.get_monitored_process(TEST_UUID) self.pmonitor.unregister(TEST_UUID, None) self.assertEqual(len(self.pmonitor._monitored_processes), 0) def test_unregister_unknown_process(self): self.pmonitor.unregister(TEST_UUID, None) self.assertEqual(len(self.pmonitor._monitored_processes), 0) class TestProcessManager(base.BaseTestCase): def setUp(self): super(TestProcessManager, self).setUp() self.execute_p = mock.patch('neutron.agent.common.utils.execute') self.execute = self.execute_p.start() self.delete_if_exists = mock.patch( 'oslo_utils.fileutils.delete_if_exists').start() self.ensure_dir = mock.patch.object( fileutils, 'ensure_tree').start() self.conf = mock.Mock() self.conf.external_pids = '/var/path' def test_processmanager_ensures_pid_dir(self): pid_file = os.path.join(self.conf.external_pids, 'pid') ep.ProcessManager(self.conf, 'uuid', pid_file=pid_file) self.ensure_dir.assert_called_once_with(self.conf.external_pids, mode=0o755) def test_enable_no_namespace(self): callback = mock.Mock() callback.return_value = ['the', 'cmd'] with mock.patch.object(ep.ProcessManager, 'get_pid_file_name') as name: name.return_value = 'pidfile' with mock.patch.object(ep.ProcessManager, 'active') as active: active.__get__ = mock.Mock(return_value=False) manager = ep.ProcessManager(self.conf, 'uuid') manager.enable(callback) callback.assert_called_once_with('pidfile') self.execute.assert_called_once_with(['the', 'cmd'], check_exit_code=True, extra_ok_codes=None, run_as_root=False, log_fail_as_error=True) def test_enable_with_namespace(self): callback = mock.Mock() callback.return_value = ['the', 'cmd'] with mock.patch.object(ep.ProcessManager, 'get_pid_file_name') as name: name.return_value = 'pidfile' with mock.patch.object(ep.ProcessManager, 'active') as active: active.__get__ = mock.Mock(return_value=False) manager = ep.ProcessManager(self.conf, 'uuid', namespace='ns') with mock.patch.object(ep, 'ip_lib') as ip_lib: manager.enable(callback) callback.assert_called_once_with('pidfile') ip_lib.assert_has_calls([ mock.call.IPWrapper(namespace='ns'), mock.call.IPWrapper().netns.execute( ['the', 'cmd'], addl_env=None, run_as_root=True)]) def test_enable_with_namespace_process_active(self): callback = mock.Mock() callback.return_value = ['the', 'cmd'] with mock.patch.object(ep.ProcessManager, 'active') as active: active.__get__ = mock.Mock(return_value=True) manager = ep.ProcessManager(self.conf, 'uuid', namespace='ns') with mock.patch.object(ep, 'ip_lib'): manager.enable(callback) self.assertFalse(callback.called) def test_reload_cfg_without_custom_reload_callback(self): with mock.patch.object(ep.ProcessManager, 'disable') as disable: manager = ep.ProcessManager(self.conf, 'uuid', namespace='ns') manager.reload_cfg() disable.assert_called_once_with('HUP') def test_reload_cfg_with_custom_reload_callback(self): reload_callback = mock.sentinel.callback with mock.patch.object(ep.ProcessManager, 'disable') as disable: manager = ep.ProcessManager( self.conf, 'uuid', namespace='ns', custom_reload_callback=reload_callback) manager.reload_cfg() disable.assert_called_once_with(get_stop_command=reload_callback) def test_disable_get_stop_command(self): cmd = ['the', 'cmd'] reload_callback = mock.Mock(return_value=cmd) with mock.patch.object(ep.ProcessManager, 'pid', mock.PropertyMock(return_value=4)): with mock.patch.object(ep.ProcessManager, 'active', mock.PropertyMock(return_value=True)): manager = ep.ProcessManager( self.conf, 'uuid', custom_reload_callback=reload_callback) manager.disable( get_stop_command=manager.custom_reload_callback) self.assertIn(cmd, self.execute.call_args[0]) def test_disable_no_namespace(self): with mock.patch.object(ep.ProcessManager, 'pid') as pid: pid.__get__ = mock.Mock(return_value=4) with mock.patch.object(ep.ProcessManager, 'active') as active: active.__get__ = mock.Mock(return_value=True) manager = ep.ProcessManager(self.conf, 'uuid') with mock.patch.object(ep, 'utils') as utils: manager.disable() utils.assert_has_calls([ mock.call.execute(['kill', '-9', 4], run_as_root=False)]) def test_disable_namespace(self): with mock.patch.object(ep.ProcessManager, 'pid') as pid: pid.__get__ = mock.Mock(return_value=4) with mock.patch.object(ep.ProcessManager, 'active') as active: active.__get__ = mock.Mock(return_value=True) manager = ep.ProcessManager(self.conf, 'uuid', namespace='ns') with mock.patch.object(ep, 'utils') as utils: manager.disable() utils.assert_has_calls([ mock.call.execute(['kill', '-9', 4], run_as_root=True)]) def test_disable_not_active(self): with mock.patch.object(ep.ProcessManager, 'pid') as pid: pid.__get__ = mock.Mock(return_value=4) with mock.patch.object(ep.ProcessManager, 'active') as active: active.__get__ = mock.Mock(return_value=False) with mock.patch.object(ep.LOG, 'debug') as debug: manager = ep.ProcessManager(self.conf, 'uuid') manager.disable() debug.assert_called_once_with(mock.ANY, mock.ANY) def test_disable_no_pid(self): with mock.patch.object(ep.ProcessManager, 'pid') as pid: pid.__get__ = mock.Mock(return_value=None) with mock.patch.object(ep.ProcessManager, 'active') as active: active.__get__ = mock.Mock(return_value=False) with mock.patch.object(ep.LOG, 'debug') as debug: manager = ep.ProcessManager(self.conf, 'uuid') manager.disable() debug.assert_called_once_with(mock.ANY, mock.ANY) def test_get_pid_file_name_default(self): manager = ep.ProcessManager(self.conf, 'uuid') retval = manager.get_pid_file_name() self.assertEqual(retval, '/var/path/uuid.pid') def test_pid(self): self.useFixture(tools.OpenFixture('/var/path/uuid.pid', '5')) manager = ep.ProcessManager(self.conf, 'uuid') self.assertEqual(manager.pid, 5) def test_pid_no_an_int(self): self.useFixture(tools.OpenFixture('/var/path/uuid.pid', 'foo')) manager = ep.ProcessManager(self.conf, 'uuid') self.assertIsNone(manager.pid) def test_pid_invalid_file(self): with mock.patch.object(ep.ProcessManager, 'get_pid_file_name') as name: name.return_value = '.doesnotexist/pid' manager = ep.ProcessManager(self.conf, 'uuid') self.assertIsNone(manager.pid) def test_active(self): with mock.patch.object(ep.ProcessManager, 'cmdline') as cmdline: cmdline.__get__ = mock.Mock( return_value=TEST_CMDLINE % 'uuid') manager = ep.ProcessManager(self.conf, 'uuid') self.assertTrue(manager.active) def test_active_none(self): with mock.patch.object(ep.ProcessManager, 'cmdline') as cmdline: cmdline.__get__ = mock.Mock(return_value=None) manager = ep.ProcessManager(self.conf, 'uuid') self.assertFalse(manager.active) def test_active_cmd_mismatch(self): with mock.patch.object(ep.ProcessManager, 'cmdline') as cmdline: cmdline.__get__ = mock.Mock( return_value=TEST_CMDLINE % 'anotherid') manager = ep.ProcessManager(self.conf, 'uuid') self.assertFalse(manager.active) def test_cmdline(self): with mock.patch.object(psutil, 'Process') as proc: proc().cmdline.return_value = (TEST_CMDLINE % 'uuid').split(' ') with mock.patch.object(ep.ProcessManager, 'pid') as pid: pid.__get__ = mock.Mock(return_value=4) manager = ep.ProcessManager(self.conf, 'uuid') self.assertEqual(TEST_CMDLINE % 'uuid', manager.cmdline) proc().cmdline.assert_called_once_with() def test_cmdline_none(self): with mock.patch.object(psutil, 'Process') as proc: proc.side_effect = psutil.NoSuchProcess(4) with mock.patch.object(ep.ProcessManager, 'pid') as pid: pid.__get__ = mock.Mock(return_value=4) manager = ep.ProcessManager(self.conf, 'uuid') self.assertIsNone(manager.cmdline) proc.assert_called_once_with(4) neutron-12.1.1/neutron/tests/unit/agent/linux/test_iptables_manager.py0000664000175000017500000015612513553660047026311 0ustar zuulzuul00000000000000# Copyright 2012 Locaweb. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import sys import fixtures import mock from oslo_config import cfg import testtools from neutron._i18n import _ from neutron.agent.linux import iptables_comments as ic from neutron.agent.linux import iptables_manager from neutron.agent.linux import utils as linux_utils from neutron.common import constants from neutron.common import exceptions as n_exc from neutron.tests import base from neutron.tests import tools IPTABLES_ARG = {'bn': iptables_manager.binary_name, 'snat_out_comment': ic.SNAT_OUT, 'filter_rules': '', 'mark': constants.ROUTER_MARK_MASK} NAT_TEMPLATE = ('# Generated by iptables_manager\n' '*nat\n' ':OUTPUT - [0:0]\n' ':POSTROUTING - [0:0]\n' ':PREROUTING - [0:0]\n' ':neutron-postrouting-bottom - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-POSTROUTING - [0:0]\n' ':%(bn)s-PREROUTING - [0:0]\n' ':%(bn)s-float-snat - [0:0]\n' ':%(bn)s-snat - [0:0]\n' '-I OUTPUT 1 -j %(bn)s-OUTPUT\n' '-I POSTROUTING 1 -j %(bn)s-POSTROUTING\n' '-I POSTROUTING 2 -j neutron-postrouting-bottom\n' '-I PREROUTING 1 -j %(bn)s-PREROUTING\n' '-I neutron-postrouting-bottom 1 -j %(bn)s-snat\n' '-I %(bn)s-snat 1 -j ' '%(bn)s-float-snat\n' 'COMMIT\n' '# Completed by iptables_manager\n') NAT_DUMP = NAT_TEMPLATE % IPTABLES_ARG FILTER_TEMPLATE = ('# Generated by iptables_manager\n' '*filter\n' ':FORWARD - [0:0]\n' ':INPUT - [0:0]\n' ':OUTPUT - [0:0]\n' ':neutron-filter-top - [0:0]\n' ':%(bn)s-FORWARD - [0:0]\n' ':%(bn)s-INPUT - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-local - [0:0]\n' '-I FORWARD 1 -j neutron-filter-top\n' '-I FORWARD 2 -j %(bn)s-FORWARD\n' '-I INPUT 1 -j %(bn)s-INPUT\n' '-I OUTPUT 1 -j neutron-filter-top\n' '-I OUTPUT 2 -j %(bn)s-OUTPUT\n' '-I neutron-filter-top 1 -j %(bn)s-local\n' 'COMMIT\n' '# Completed by iptables_manager\n') FILTER_DUMP = FILTER_TEMPLATE % IPTABLES_ARG FILTER_WITH_RULES_TEMPLATE = ( '# Generated by iptables_manager\n' '*filter\n' ':FORWARD - [0:0]\n' ':INPUT - [0:0]\n' ':OUTPUT - [0:0]\n' ':neutron-filter-top - [0:0]\n' ':%(bn)s-FORWARD - [0:0]\n' ':%(bn)s-INPUT - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-filter - [0:0]\n' ':%(bn)s-local - [0:0]\n' '-I FORWARD 1 -j neutron-filter-top\n' '-I FORWARD 2 -j %(bn)s-FORWARD\n' '-I INPUT 1 -j %(bn)s-INPUT\n' '-I OUTPUT 1 -j neutron-filter-top\n' '-I OUTPUT 2 -j %(bn)s-OUTPUT\n' '-I neutron-filter-top 1 -j %(bn)s-local\n' '%(filter_rules)s' 'COMMIT\n' '# Completed by iptables_manager\n') COMMENTED_NAT_DUMP = ( '# Generated by iptables_manager\n' '*nat\n' ':OUTPUT - [0:0]\n' ':POSTROUTING - [0:0]\n' ':PREROUTING - [0:0]\n' ':neutron-postrouting-bottom - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-POSTROUTING - [0:0]\n' ':%(bn)s-PREROUTING - [0:0]\n' ':%(bn)s-float-snat - [0:0]\n' ':%(bn)s-snat - [0:0]\n' '-I OUTPUT 1 -j %(bn)s-OUTPUT\n' '-I POSTROUTING 1 -j %(bn)s-POSTROUTING\n' '-I POSTROUTING 2 -j neutron-postrouting-bottom\n' '-I PREROUTING 1 -j %(bn)s-PREROUTING\n' '-I neutron-postrouting-bottom 1 ' '-m comment --comment "%(snat_out_comment)s" -j %(bn)s-snat\n' '-I %(bn)s-snat 1 -j ' '%(bn)s-float-snat\n' 'COMMIT\n' '# Completed by iptables_manager\n' % IPTABLES_ARG) TRAFFIC_COUNTERS_DUMP = ( 'Chain OUTPUT (policy ACCEPT 400 packets, 65901 bytes)\n' ' pkts bytes target prot opt in out source' ' destination \n' ' 400 65901 chain1 all -- * * 0.0.0.0/0' ' 0.0.0.0/0 \n' ' 400 65901 chain2 all -- * * 0.0.0.0/0' ' 0.0.0.0/0 \n') FILTER_RESTORE_DUMP = ('# Generated by iptables_manager\n' '*filter\n' ':FORWARD - [0:0]\n' ':INPUT - [0:0]\n' ':OUTPUT - [0:0]\n' ':neutron-filter-top - [0:0]\n' ':%(bn)s-FORWARD - [0:0]\n' ':%(bn)s-INPUT - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-test-filter - [0:0]\n' ':%(bn)s-local - [0:0]\n' '-A FORWARD -j neutron-filter-top\n' '-A FORWARD -j %(bn)s-FORWARD\n' '-A INPUT -j %(bn)s-INPUT\n' '-A OUTPUT -j neutron-filter-top\n' '-A OUTPUT -j %(bn)s-OUTPUT\n' '-A neutron-filter-top -j %(bn)s-local\n' '%(filter_rules)s' 'COMMIT\n' '# Completed by iptables_manager\n') NAT_RESTORE_TEMPLATE = ('# Generated by iptables_manager\n' '*nat\n' ':OUTPUT - [0:0]\n' ':POSTROUTING - [0:0]\n' ':PREROUTING - [0:0]\n' ':neutron-postrouting-bottom - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-POSTROUTING - [0:0]\n' ':%(bn)s-PREROUTING - [0:0]\n' ':%(bn)s-float-snat - [0:0]\n' ':%(bn)s-snat - [0:0]\n' '-A OUTPUT -j %(bn)s-OUTPUT\n' '-A POSTROUTING -j %(bn)s-POSTROUTING\n' '-A POSTROUTING -j neutron-postrouting-bottom\n' '-A PREROUTING -j %(bn)s-PREROUTING\n' '-A neutron-postrouting-bottom -j %(bn)s-snat\n' '-A %(bn)s-snat -j ' '%(bn)s-float-snat\n' 'COMMIT\n' '# Completed by iptables_manager\n') NAT_RESTORE_DUMP = NAT_RESTORE_TEMPLATE % IPTABLES_ARG class IptablesTestCase(base.BaseTestCase): def test_get_binary_name_in_unittest(self): # Corresponds to sys.argv content when running python -m unittest class with mock.patch('sys.argv', ['python -m unittest', 'class']): binary_name = iptables_manager.get_binary_name() self.assertEqual('python_-m_unitte', binary_name) class IptablesCommentsTestCase(base.BaseTestCase): def setUp(self): super(IptablesCommentsTestCase, self).setUp() cfg.CONF.set_override('comment_iptables_rules', True, 'AGENT') self.iptables = iptables_manager.IptablesManager() self.execute = mock.patch.object(self.iptables, "execute").start() def test_comments_short_enough(self): for attr in dir(ic): if not attr.startswith('__') and len(getattr(ic, attr)) > 255: self.fail("Iptables comment %s is longer than 255 characters." % attr) def test_reordering_of_jump_rule_comments(self): # jump at the start self.assertEqual( '-m comment --comment "aloha" -j sg-chain', iptables_manager.comment_rule('-j sg-chain', 'aloha')) # jump in the middle self.assertEqual( '-s source -m comment --comment "aloha" -j sg-chain', iptables_manager.comment_rule('-s source -j sg-chain', 'aloha')) # no jump rule self.assertEqual( '-s source -m comment --comment "aloha"', iptables_manager.comment_rule('-s source', 'aloha')) def test_add_filter_rule(self): iptables_args = {} iptables_args.update(IPTABLES_ARG) filter_rules = ('-I %(bn)s-INPUT 1 -s 0/0 -d 192.168.0.2 -j ' '%(bn)s-filter\n-I %(bn)s-filter 1 -j DROP\n' % iptables_args) iptables_args['filter_rules'] = filter_rules filter_dump_mod = FILTER_WITH_RULES_TEMPLATE % iptables_args raw_dump = _generate_raw_dump(IPTABLES_ARG) mangle_dump = _generate_mangle_dump(IPTABLES_ARG) expected_calls_and_values = [ (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(filter_dump_mod + mangle_dump + COMMENTED_NAT_DUMP + raw_dump), run_as_root=True, log_fail_as_error=False), None), (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(FILTER_DUMP + mangle_dump + COMMENTED_NAT_DUMP + raw_dump), run_as_root=True, log_fail_as_error=False), None), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) self.iptables.ipv4['filter'].add_chain('filter') self.iptables.ipv4['filter'].add_rule('filter', '-j DROP') self.iptables.ipv4['filter'].add_rule('INPUT', '-s 0/0 -d 192.168.0.2 -j' ' %(bn)s-filter' % IPTABLES_ARG) self.iptables.apply() self.iptables.ipv4['filter'].remove_rule('filter', '-j DROP') self.iptables.ipv4['filter'].remove_rule('INPUT', '-s 0/0 -d 192.168.0.2 -j' ' %(bn)s-filter' % IPTABLES_ARG) self.iptables.ipv4['filter'].remove_chain('filter') self.iptables.apply() tools.verify_mock_calls(self.execute, expected_calls_and_values) def _generate_mangle_dump(iptables_args): return ('# Generated by iptables_manager\n' '*mangle\n' ':FORWARD - [0:0]\n' ':INPUT - [0:0]\n' ':OUTPUT - [0:0]\n' ':POSTROUTING - [0:0]\n' ':PREROUTING - [0:0]\n' ':%(bn)s-FORWARD - [0:0]\n' ':%(bn)s-INPUT - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-POSTROUTING - [0:0]\n' ':%(bn)s-PREROUTING - [0:0]\n' ':%(bn)s-mark - [0:0]\n' '-I FORWARD 1 -j %(bn)s-FORWARD\n' '-I INPUT 1 -j %(bn)s-INPUT\n' '-I OUTPUT 1 -j %(bn)s-OUTPUT\n' '-I POSTROUTING 1 -j %(bn)s-POSTROUTING\n' '-I PREROUTING 1 -j %(bn)s-PREROUTING\n' '-I %(bn)s-PREROUTING 1 -j %(bn)s-mark\n' 'COMMIT\n' '# Completed by iptables_manager\n' % iptables_args) def _generate_mangle_dump_v6(iptables_args): return ('# Generated by iptables_manager\n' '*mangle\n' ':FORWARD - [0:0]\n' ':INPUT - [0:0]\n' ':OUTPUT - [0:0]\n' ':POSTROUTING - [0:0]\n' ':PREROUTING - [0:0]\n' ':%(bn)s-FORWARD - [0:0]\n' ':%(bn)s-INPUT - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-POSTROUTING - [0:0]\n' ':%(bn)s-PREROUTING - [0:0]\n' '-I FORWARD 1 -j %(bn)s-FORWARD\n' '-I INPUT 1 -j %(bn)s-INPUT\n' '-I OUTPUT 1 -j %(bn)s-OUTPUT\n' '-I POSTROUTING 1 -j %(bn)s-POSTROUTING\n' '-I PREROUTING 1 -j %(bn)s-PREROUTING\n' 'COMMIT\n' '# Completed by iptables_manager\n' % iptables_args) def _generate_raw_dump(iptables_args): return ('# Generated by iptables_manager\n' '*raw\n' ':OUTPUT - [0:0]\n' ':PREROUTING - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-PREROUTING - [0:0]\n' '-I OUTPUT 1 -j %(bn)s-OUTPUT\n' '-I PREROUTING 1 -j %(bn)s-PREROUTING\n' 'COMMIT\n' '# Completed by iptables_manager\n' % iptables_args) def _generate_mangle_restore_dump(iptables_args): return ('# Generated by iptables_manager\n' '*mangle\n' ':FORWARD - [0:0]\n' ':INPUT - [0:0]\n' ':OUTPUT - [0:0]\n' ':POSTROUTING - [0:0]\n' ':PREROUTING - [0:0]\n' ':%(bn)s-FORWARD - [0:0]\n' ':%(bn)s-INPUT - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-POSTROUTING - [0:0]\n' ':%(bn)s-PREROUTING - [0:0]\n' ':%(bn)s-mark - [0:0]\n' '-A FORWARD -j %(bn)s-FORWARD\n' '-A INPUT -j %(bn)s-INPUT\n' '-A OUTPUT -j %(bn)s-OUTPUT\n' '-A POSTROUTING -j %(bn)s-POSTROUTING\n' '-A PREROUTING -j %(bn)s-PREROUTING\n' '-A %(bn)s-PREROUTING -j %(bn)s-mark\n' 'COMMIT\n' '# Completed by iptables_manager\n' % iptables_args) def _generate_raw_restore_dump(iptables_args): return ('# Generated by iptables_manager\n' '*raw\n' ':OUTPUT - [0:0]\n' ':PREROUTING - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-PREROUTING - [0:0]\n' '-A OUTPUT -j %(bn)s-OUTPUT\n' '-A PREROUTING -j %(bn)s-PREROUTING\n' 'COMMIT\n' '# Completed by iptables_manager\n' % iptables_args) MANGLE_DUMP = _generate_mangle_dump(IPTABLES_ARG) MANGLE_DUMP_V6 = _generate_mangle_dump_v6(IPTABLES_ARG) RAW_DUMP = _generate_raw_dump(IPTABLES_ARG) MANGLE_RESTORE_DUMP = _generate_mangle_restore_dump(IPTABLES_ARG) RAW_RESTORE_DUMP = _generate_raw_restore_dump(IPTABLES_ARG) class IptablesFixture(fixtures.Fixture): def _setUp(self): # We MUST save and restore use_table_lock because it is a class # attribute and could change state in some tests, which can cause # the other iptables_manager test cases to randomly fail due to # race conditions. self.use_table_lock = iptables_manager.IptablesManager.use_table_lock iptables_manager.IptablesManager.use_table_lock = False self.addCleanup(self._reset) def _reset(self): iptables_manager.IptablesManager.use_table_lock = self.use_table_lock class IptablesManagerStateFulTestCase(base.BaseTestCase): def setUp(self): super(IptablesManagerStateFulTestCase, self).setUp() cfg.CONF.set_override('comment_iptables_rules', False, 'AGENT') cfg.CONF.set_override('report_interval', 30, 'AGENT') self.execute = mock.patch.object(linux_utils, "execute").start() self.iptables = iptables_manager.IptablesManager() self.useFixture(IptablesFixture()) def test_binary_name(self): expected = os.path.basename(sys.argv[0])[:16] self.assertEqual(expected, iptables_manager.binary_name) def test_get_chain_name(self): name = '0123456789' * 5 # 28 chars is the maximum length of iptables chain name. self.assertEqual(iptables_manager.get_chain_name(name, wrap=False), name[:28]) # 11 chars is the maximum length of chain name of iptable_manager # if binary_name is prepended. self.assertEqual(iptables_manager.get_chain_name(name, wrap=True), name[:11]) def test_defer_apply_with_exception(self): self.iptables._apply = mock.Mock(side_effect=Exception) with testtools.ExpectedException(n_exc.IpTablesApplyException): with self.iptables.defer_apply(): pass def _extend_with_ip6tables_filter(self, expected_calls, filter_dump): expected_calls.insert(2, ( mock.call(['ip6tables-save'], run_as_root=True), '')) expected_calls.insert(3, ( mock.call(['ip6tables-restore', '-n'], process_input=filter_dump, run_as_root=True, log_fail_as_error=False), None)) expected_calls.extend([ (mock.call(['ip6tables-save'], run_as_root=True), ''), (mock.call(['ip6tables-restore', '-n'], process_input=filter_dump, run_as_root=True, log_fail_as_error=False), None)]) def _test_add_and_remove_chain_custom_binary_name_helper(self, use_ipv6): bn = ("xbcdef" * 5) self.iptables = iptables_manager.IptablesManager( binary_name=bn, use_ipv6=use_ipv6) self.execute = mock.patch.object(self.iptables, "execute").start() iptables_args = {'bn': bn[:16], 'filter_rules': ''} filter_dump = FILTER_WITH_RULES_TEMPLATE % iptables_args filter_dump_ipv6 = FILTER_TEMPLATE % iptables_args filter_dump_mod = filter_dump nat_dump = NAT_TEMPLATE % iptables_args raw_dump = _generate_raw_dump(iptables_args) mangle_dump = _generate_mangle_dump(iptables_args) expected_calls_and_values = [ (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(filter_dump_mod + mangle_dump + nat_dump + raw_dump), run_as_root=True, log_fail_as_error=False), None), (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(filter_dump + mangle_dump + nat_dump + raw_dump), run_as_root=True, log_fail_as_error=False), None), ] if use_ipv6: mangle_dump_v6 = _generate_mangle_dump_v6(iptables_args) self._extend_with_ip6tables_filter( expected_calls_and_values, filter_dump_ipv6 + mangle_dump_v6 + raw_dump) tools.setup_mock_calls(self.execute, expected_calls_and_values) self.iptables.ipv4['filter'].add_chain('filter') self.iptables.apply() self.iptables.ipv4['filter'].empty_chain('filter') self.iptables.apply() tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_add_and_remove_chain_custom_binary_name(self): self._test_add_and_remove_chain_custom_binary_name_helper(False) def test_add_and_remove_chain_custom_binary_name_with_ipv6(self): self._test_add_and_remove_chain_custom_binary_name_helper(True) def _test_empty_chain_custom_binary_name_helper(self, use_ipv6): bn = ("xbcdef" * 5)[:16] self.iptables = iptables_manager.IptablesManager( binary_name=bn, use_ipv6=use_ipv6) self.execute = mock.patch.object(self.iptables, "execute").start() iptables_args = {'bn': bn} filter_dump = FILTER_TEMPLATE % iptables_args filter_rules = ('-I %(bn)s-filter 1 -s 0/0 -d 192.168.0.2\n' % iptables_args) iptables_args['filter_rules'] = filter_rules filter_dump_mod = FILTER_WITH_RULES_TEMPLATE % iptables_args nat_dump = NAT_TEMPLATE % iptables_args raw_dump = _generate_raw_dump(iptables_args) mangle_dump = _generate_mangle_dump(iptables_args) expected_calls_and_values = [ (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(filter_dump_mod + mangle_dump + nat_dump + raw_dump), run_as_root=True, log_fail_as_error=False), None), (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(filter_dump + mangle_dump + nat_dump + raw_dump), run_as_root=True, log_fail_as_error=False), None), ] if use_ipv6: mangle_dump_v6 = _generate_mangle_dump_v6(iptables_args) self._extend_with_ip6tables_filter( expected_calls_and_values, filter_dump + mangle_dump_v6 + raw_dump) tools.setup_mock_calls(self.execute, expected_calls_and_values) self.iptables.ipv4['filter'].add_chain('filter') self.iptables.ipv4['filter'].add_rule('filter', '-s 0/0 -d 192.168.0.2') self.iptables.apply() self.iptables.ipv4['filter'].remove_chain('filter') self.iptables.apply() tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_empty_chain_custom_binary_name(self): self._test_empty_chain_custom_binary_name_helper(False) def test_empty_chain_custom_binary_name_with_ipv6(self): self._test_empty_chain_custom_binary_name_helper(True) def _test_add_and_remove_chain_helper(self, use_ipv6): self.iptables = iptables_manager.IptablesManager( use_ipv6=use_ipv6) self.execute = mock.patch.object(self.iptables, "execute").start() filter_dump_mod = FILTER_WITH_RULES_TEMPLATE % IPTABLES_ARG expected_calls_and_values = [ (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(filter_dump_mod + MANGLE_DUMP + NAT_DUMP + RAW_DUMP), run_as_root=True, log_fail_as_error=False), None), (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(FILTER_DUMP + MANGLE_DUMP + NAT_DUMP + RAW_DUMP), run_as_root=True, log_fail_as_error=False), None), ] if use_ipv6: self._extend_with_ip6tables_filter( expected_calls_and_values, FILTER_DUMP + MANGLE_DUMP_V6 + RAW_DUMP) tools.setup_mock_calls(self.execute, expected_calls_and_values) self.iptables.ipv4['filter'].add_chain('filter') self.iptables.apply() self.iptables.ipv4['filter'].remove_chain('filter') self.iptables.apply() tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_add_and_remove_chain(self): self._test_add_and_remove_chain_helper(False) def test_add_and_remove_chain_with_ipv6(self): self._test_add_and_remove_chain_helper(True) def _test_add_filter_rule_helper(self, use_ipv6): self.iptables = iptables_manager.IptablesManager( use_ipv6=use_ipv6) self.execute = mock.patch.object(self.iptables, "execute").start() iptables_args = {} iptables_args.update(IPTABLES_ARG) filter_rules = ('-I %(bn)s-INPUT 1 -s 0/0 -d 192.168.0.2 -j ' '%(bn)s-filter\n-I %(bn)s-filter 1 -j DROP\n' % iptables_args) iptables_args['filter_rules'] = filter_rules filter_dump_mod = FILTER_WITH_RULES_TEMPLATE % iptables_args raw_dump = RAW_DUMP % IPTABLES_ARG expected_calls_and_values = [ (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(filter_dump_mod + MANGLE_DUMP + NAT_DUMP + RAW_DUMP), run_as_root=True, log_fail_as_error=False), None), (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(FILTER_DUMP + MANGLE_DUMP + NAT_DUMP + RAW_DUMP), run_as_root=True, log_fail_as_error=False), None), ] if use_ipv6: self._extend_with_ip6tables_filter( expected_calls_and_values, FILTER_DUMP + MANGLE_DUMP_V6 + raw_dump) tools.setup_mock_calls(self.execute, expected_calls_and_values) self.iptables.ipv4['filter'].add_chain('filter') self.iptables.ipv4['filter'].add_rule('filter', '-j DROP') self.iptables.ipv4['filter'].add_rule('INPUT', '-s 0/0 -d 192.168.0.2 -j' ' %(bn)s-filter' % IPTABLES_ARG) self.iptables.apply() self.iptables.ipv4['filter'].remove_rule('filter', '-j DROP') self.iptables.ipv4['filter'].remove_rule('INPUT', '-s 0/0 -d 192.168.0.2 -j' ' %(bn)s-filter' % IPTABLES_ARG) self.iptables.ipv4['filter'].remove_chain('filter') self.iptables.apply() tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_add_filter_rule(self): self._test_add_filter_rule_helper(False) def test_add_filter_rule_with_ipv6(self): self._test_add_filter_rule_helper(True) def _test_rule_with_wrap_target_helper(self, use_ipv6): self.iptables = iptables_manager.IptablesManager( use_ipv6=use_ipv6) self.execute = mock.patch.object(self.iptables, "execute").start() name = '0123456789' * 5 wrap = "%s-%s" % (iptables_manager.binary_name, iptables_manager.get_chain_name(name)) iptables_args = {'bn': iptables_manager.binary_name, 'wrap': wrap} filter_dump_mod = ('# Generated by iptables_manager\n' '*filter\n' ':FORWARD - [0:0]\n' ':INPUT - [0:0]\n' ':OUTPUT - [0:0]\n' ':neutron-filter-top - [0:0]\n' ':%(wrap)s - [0:0]\n' ':%(bn)s-FORWARD - [0:0]\n' ':%(bn)s-INPUT - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-local - [0:0]\n' '-I FORWARD 1 -j neutron-filter-top\n' '-I FORWARD 2 -j %(bn)s-FORWARD\n' '-I INPUT 1 -j %(bn)s-INPUT\n' '-I OUTPUT 1 -j neutron-filter-top\n' '-I OUTPUT 2 -j %(bn)s-OUTPUT\n' '-I neutron-filter-top 1 -j %(bn)s-local\n' '-I %(bn)s-INPUT 1 -s 0/0 -d 192.168.0.2 -j ' '%(wrap)s\n' 'COMMIT\n' '# Completed by iptables_manager\n' % iptables_args) raw_dump = RAW_DUMP % IPTABLES_ARG expected_calls_and_values = [ (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(filter_dump_mod + MANGLE_DUMP + NAT_DUMP + RAW_DUMP), run_as_root=True, log_fail_as_error=False), None), (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(FILTER_DUMP + MANGLE_DUMP + NAT_DUMP + RAW_DUMP), run_as_root=True, log_fail_as_error=False), None), ] if use_ipv6: self._extend_with_ip6tables_filter( expected_calls_and_values, FILTER_DUMP + MANGLE_DUMP_V6 + raw_dump) tools.setup_mock_calls(self.execute, expected_calls_and_values) self.iptables.ipv4['filter'].add_chain(name) self.iptables.ipv4['filter'].add_rule('INPUT', '-s 0/0 -d 192.168.0.2 -j' ' $%s' % name) self.iptables.apply() self.iptables.ipv4['filter'].remove_rule('INPUT', '-s 0/0 -d 192.168.0.2 -j' ' $%s' % name) self.iptables.ipv4['filter'].remove_chain(name) self.iptables.apply() tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_rule_with_wrap_target(self): self._test_rule_with_wrap_target_helper(False) def test_rule_with_wrap_target_with_ipv6(self): self._test_rule_with_wrap_target_helper(True) def _test_add_mangle_rule_helper(self, use_ipv6): self.iptables = iptables_manager.IptablesManager( use_ipv6=use_ipv6) self.execute = mock.patch.object(self.iptables, "execute").start() mangle_dump_mod = ( '# Generated by iptables_manager\n' '*mangle\n' ':FORWARD - [0:0]\n' ':INPUT - [0:0]\n' ':OUTPUT - [0:0]\n' ':POSTROUTING - [0:0]\n' ':PREROUTING - [0:0]\n' ':%(bn)s-FORWARD - [0:0]\n' ':%(bn)s-INPUT - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-POSTROUTING - [0:0]\n' ':%(bn)s-PREROUTING - [0:0]\n' ':%(bn)s-mangle - [0:0]\n' ':%(bn)s-mark - [0:0]\n' '-I FORWARD 1 -j %(bn)s-FORWARD\n' '-I INPUT 1 -j %(bn)s-INPUT\n' '-I OUTPUT 1 -j %(bn)s-OUTPUT\n' '-I POSTROUTING 1 -j %(bn)s-POSTROUTING\n' '-I PREROUTING 1 -j %(bn)s-PREROUTING\n' '-I %(bn)s-PREROUTING 1 -j %(bn)s-mark\n' '-I %(bn)s-PREROUTING 2 -j MARK --set-xmark 0x1/%(mark)s\n' 'COMMIT\n' '# Completed by iptables_manager\n' % IPTABLES_ARG) expected_calls_and_values = [ (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(FILTER_DUMP + mangle_dump_mod + NAT_DUMP + RAW_DUMP), run_as_root=True, log_fail_as_error=False), None), (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(FILTER_DUMP + MANGLE_DUMP + NAT_DUMP + RAW_DUMP), run_as_root=True, log_fail_as_error=False), None), ] if use_ipv6: self._extend_with_ip6tables_filter( expected_calls_and_values, FILTER_DUMP + MANGLE_DUMP_V6 + RAW_DUMP) tools.setup_mock_calls(self.execute, expected_calls_and_values) self.iptables.ipv4['mangle'].add_chain('mangle') self.iptables.ipv4['mangle'].add_rule( 'PREROUTING', '-j MARK --set-xmark 0x1/%s' % constants.ROUTER_MARK_MASK) self.iptables.apply() self.iptables.ipv4['mangle'].remove_rule( 'PREROUTING', '-j MARK --set-xmark 0x1/%s' % constants.ROUTER_MARK_MASK) self.iptables.ipv4['mangle'].remove_chain('mangle') self.iptables.apply() tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_add_mangle_rule(self): self._test_add_mangle_rule_helper(False) def test_add_mangle_rule_with_ipv6(self): self._test_add_mangle_rule_helper(True) def _test_add_nat_rule_helper(self, use_ipv6): self.iptables = iptables_manager.IptablesManager( use_ipv6=use_ipv6) self.execute = mock.patch.object(self.iptables, "execute").start() nat_dump = NAT_TEMPLATE % IPTABLES_ARG nat_dump_mod = ('# Generated by iptables_manager\n' '*nat\n' ':OUTPUT - [0:0]\n' ':POSTROUTING - [0:0]\n' ':PREROUTING - [0:0]\n' ':neutron-postrouting-bottom - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-POSTROUTING - [0:0]\n' ':%(bn)s-PREROUTING - [0:0]\n' ':%(bn)s-float-snat - [0:0]\n' ':%(bn)s-nat - [0:0]\n' ':%(bn)s-snat - [0:0]\n' '-I OUTPUT 1 -j %(bn)s-OUTPUT\n' '-I POSTROUTING 1 -j %(bn)s-POSTROUTING\n' '-I POSTROUTING 2 -j neutron-postrouting-bottom\n' '-I PREROUTING 1 -j %(bn)s-PREROUTING\n' '-I neutron-postrouting-bottom 1 -j %(bn)s-snat\n' '-I %(bn)s-PREROUTING 1 -d 192.168.0.3 -j ' '%(bn)s-nat\n' '-I %(bn)s-nat 1 -p tcp --dport 8080 -j ' 'REDIRECT --to-port 80\n' '-I %(bn)s-snat 1 -j %(bn)s-float-snat\n' 'COMMIT\n' '# Completed by iptables_manager\n' % IPTABLES_ARG) raw_dump = RAW_DUMP % IPTABLES_ARG expected_calls_and_values = [ (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(FILTER_DUMP + MANGLE_DUMP + nat_dump_mod + RAW_DUMP), run_as_root=True, log_fail_as_error=False), None), (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(FILTER_DUMP + MANGLE_DUMP + nat_dump + RAW_DUMP), run_as_root=True, log_fail_as_error=False), None), ] if use_ipv6: self._extend_with_ip6tables_filter( expected_calls_and_values, FILTER_DUMP + MANGLE_DUMP_V6 + raw_dump) tools.setup_mock_calls(self.execute, expected_calls_and_values) self.iptables.ipv4['nat'].add_chain('nat') self.iptables.ipv4['nat'].add_rule('PREROUTING', '-d 192.168.0.3 -j ' '%(bn)s-nat' % IPTABLES_ARG) self.iptables.ipv4['nat'].add_rule('nat', '-p tcp --dport 8080' + ' -j REDIRECT --to-port 80') self.iptables.apply() self.iptables.ipv4['nat'].remove_rule('nat', '-p tcp --dport 8080 -j' ' REDIRECT --to-port 80') self.iptables.ipv4['nat'].remove_rule('PREROUTING', '-d 192.168.0.3 -j ' '%(bn)s-nat' % IPTABLES_ARG) self.iptables.ipv4['nat'].remove_chain('nat') self.iptables.apply() tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_add_nat_rule(self): self._test_add_nat_rule_helper(False) def test_add_nat_rule_with_ipv6(self): self._test_add_nat_rule_helper(True) def _test_add_raw_rule_helper(self, use_ipv6): self.iptables = iptables_manager.IptablesManager( use_ipv6=use_ipv6) self.execute = mock.patch.object(self.iptables, "execute").start() raw_dump_mod = ('# Generated by iptables_manager\n' '*raw\n' ':OUTPUT - [0:0]\n' ':PREROUTING - [0:0]\n' ':%(bn)s-OUTPUT - [0:0]\n' ':%(bn)s-PREROUTING - [0:0]\n' ':%(bn)s-raw - [0:0]\n' '-I OUTPUT 1 -j %(bn)s-OUTPUT\n' '-I PREROUTING 1 -j %(bn)s-PREROUTING\n' '-I %(bn)s-PREROUTING 1 -j CT --notrack\n' 'COMMIT\n' '# Completed by iptables_manager\n' % IPTABLES_ARG) expected_calls_and_values = [ (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(FILTER_DUMP + MANGLE_DUMP + NAT_DUMP + raw_dump_mod), run_as_root=True, log_fail_as_error=False), None), (mock.call(['iptables-save'], run_as_root=True), ''), (mock.call(['iptables-restore', '-n'], process_input=(FILTER_DUMP + MANGLE_DUMP + NAT_DUMP + RAW_DUMP), run_as_root=True, log_fail_as_error=False), None), ] if use_ipv6: self._extend_with_ip6tables_filter( expected_calls_and_values, FILTER_DUMP + MANGLE_DUMP_V6 + RAW_DUMP) tools.setup_mock_calls(self.execute, expected_calls_and_values) self.iptables.ipv4['raw'].add_chain('raw') self.iptables.ipv4['raw'].add_rule('PREROUTING', '-j CT --notrack') self.iptables.apply() self.iptables.ipv4['raw'].remove_rule('PREROUTING', '-j CT --notrack') self.iptables.ipv4['raw'].remove_chain('raw') self.iptables.apply() tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_add_raw_rule(self): self._test_add_raw_rule_helper(False) def test_add_raw_rule_with_ipv6(self): self._test_add_raw_rule_helper(True) def test_add_rule_to_a_nonexistent_chain(self): self.assertRaises(LookupError, self.iptables.ipv4['filter'].add_rule, 'nonexistent', '-j DROP') def test_remove_nonexistent_chain(self): with mock.patch.object(iptables_manager, "LOG") as log: self.iptables.ipv4['filter'].remove_chain('nonexistent') log.debug.assert_called_once_with( 'Attempted to remove chain %s which does not exist', 'nonexistent') def test_remove_nonexistent_rule(self): with mock.patch.object(iptables_manager, "LOG") as log: self.iptables.ipv4['filter'].remove_rule('nonexistent', '-j DROP') log.warning.assert_called_once_with( 'Tried to remove rule that was not there: ' '%(chain)r %(rule)r %(wrap)r %(top)r', {'wrap': True, 'top': False, 'rule': '-j DROP', 'chain': 'nonexistent'}) def test_iptables__apply_synchronized_no_namespace(self): self.execute.side_effect = RuntimeError # no namespace set so should raise self.assertRaises(RuntimeError, self.iptables._apply_synchronized) self.iptables.namespace = 'test' with mock.patch('neutron.agent.linux.ip_lib.network_namespace_exists', return_value=True): self.assertRaises(RuntimeError, self.iptables._apply_synchronized) with mock.patch('neutron.agent.linux.ip_lib.network_namespace_exists', return_value=False): self.assertEqual([], self.iptables._apply_synchronized()) def test_iptables_failure_with_no_failing_line_number(self): with mock.patch.object(iptables_manager, "LOG") as log: # generate Runtime errors on iptables-restore calls def iptables_restore_failer(*args, **kwargs): if 'iptables-restore' in args[0]: self.input_lines = kwargs['process_input'].split('\n') # don't provide a specific failure message so all lines # are logged raise RuntimeError() return FILTER_DUMP self.execute.side_effect = iptables_restore_failer # _apply_synchronized calls iptables-restore so it should raise # a RuntimeError self.assertRaises(RuntimeError, self.iptables._apply_synchronized) # The RuntimeError should have triggered a log of the input to the # process that it failed to execute. Verify by comparing the log # call to the 'process_input' arg given to the failed iptables-restore # call. # Failure without a specific line number in the error should cause # all lines to be logged with numbers. logged = ['%7d. %s' % (n, l) for n, l in enumerate(self.input_lines, 1)] log.error.assert_called_once_with(_( 'IPTablesManager.apply failed to apply the ' 'following set of iptables rules:\n%s'), '\n'.join(logged) ) def test_iptables_failure(self): with mock.patch.object(iptables_manager, "LOG") as log: # generate Runtime errors on iptables-restore calls def iptables_restore_failer(*args, **kwargs): if 'iptables-restore' in args[0]: self.input_lines = kwargs['process_input'].split('\n') # pretend line 11 failed msg = ("Exit code: 1\nStdout: ''\n" "Stderr: 'iptables-restore: line 11 failed\n'") raise n_exc.ProcessExecutionError( msg, iptables_manager.XTABLES_RESOURCE_PROBLEM_CODE) return FILTER_DUMP self.execute.side_effect = iptables_restore_failer # _apply_synchronized calls iptables-restore so it should raise # a RuntimeError self.assertRaises(RuntimeError, self.iptables._apply_synchronized) # check that we tried with -w when the first attempt failed self.execute.assert_has_calls( [mock.call(['iptables-restore', '-n'], process_input=mock.ANY, run_as_root=True, log_fail_as_error=False), mock.call(['iptables-restore', '-n', '-w', '10', '-W', iptables_manager.XLOCK_WAIT_INTERVAL], process_input=mock.ANY, run_as_root=True)]) # The RuntimeError should have triggered a log of the input to the # process that it failed to execute. Verify by comparing the log # call to the 'process_input' arg given to the failed iptables-restore # call. # Line 11 of the input was marked as failing so lines (11 - context) # to (11 + context) should be logged ctx = iptables_manager.IPTABLES_ERROR_LINES_OF_CONTEXT log_start = max(0, 11 - ctx) log_end = 11 + ctx logged = ['%7d. %s' % (n, l) for n, l in enumerate(self.input_lines[log_start:log_end], log_start + 1)] log.error.assert_called_once_with(_( 'IPTablesManager.apply failed to apply the ' 'following set of iptables rules:\n%s'), '\n'.join(logged) ) def test_iptables_use_table_lock(self): # Under normal operation, if we do call iptables-restore with a -w # and it succeeds, the next call will only use -w. PE_error = n_exc.ProcessExecutionError( "", iptables_manager.XTABLES_RESOURCE_PROBLEM_CODE) self.execute.side_effect = [FILTER_DUMP, PE_error, None, FILTER_DUMP, None, FILTER_DUMP, None] self.iptables._apply_synchronized() self.assertEqual(3, self.execute.call_count) self.execute.assert_has_calls( [mock.call(['iptables-save'], run_as_root=True), mock.call(['iptables-restore', '-n'], process_input=mock.ANY, run_as_root=True, log_fail_as_error=False), mock.call(['iptables-restore', '-n', '-w', '10', '-W', iptables_manager.XLOCK_WAIT_INTERVAL], process_input=mock.ANY, run_as_root=True)]) self.execute.reset_mock() self.iptables._apply_synchronized() self.assertEqual(2, self.execute.call_count) self.execute.assert_has_calls( [mock.call(['iptables-save'], run_as_root=True), mock.call(['iptables-restore', '-n', '-w', '10', '-W', iptables_manager.XLOCK_WAIT_INTERVAL], process_input=mock.ANY, run_as_root=True)]) # Another instance of the class should behave similarly now self.execute.reset_mock() iptm = iptables_manager.IptablesManager() iptm._apply_synchronized() self.assertEqual(2, self.execute.call_count) self.execute.assert_has_calls( [mock.call(['iptables-save'], run_as_root=True), mock.call(['iptables-restore', '-n', '-w', '10', '-W', iptables_manager.XLOCK_WAIT_INTERVAL], process_input=mock.ANY, run_as_root=True)]) def test_get_traffic_counters_chain_notexists(self): with mock.patch.object(iptables_manager, "LOG") as log: acc = self.iptables.get_traffic_counters('chain1') self.assertIsNone(acc) self.assertEqual(0, self.execute.call_count) log.warning.assert_called_once_with( 'Attempted to get traffic counters of chain %s which ' 'does not exist', 'chain1') def _test_get_traffic_counters_helper(self, use_ipv6): self.iptables = iptables_manager.IptablesManager( use_ipv6=use_ipv6) self.execute = mock.patch.object(self.iptables, "execute").start() exp_packets = 800 exp_bytes = 131802 expected_calls_and_values = [ (mock.call(['iptables', '-t', 'filter', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10'], run_as_root=True), TRAFFIC_COUNTERS_DUMP), (mock.call(['iptables', '-t', 'raw', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10'], run_as_root=True), ''), (mock.call(['iptables', '-t', 'mangle', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10'], run_as_root=True), ''), (mock.call(['iptables', '-t', 'nat', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10'], run_as_root=True), ''), ] if use_ipv6: expected_calls_and_values.append( (mock.call(['ip6tables', '-t', 'raw', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10'], run_as_root=True), '')) expected_calls_and_values.append( (mock.call(['ip6tables', '-t', 'filter', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10'], run_as_root=True), TRAFFIC_COUNTERS_DUMP)) expected_calls_and_values.append( (mock.call(['ip6tables', '-t', 'mangle', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10'], run_as_root=True), '')) exp_packets *= 2 exp_bytes *= 2 tools.setup_mock_calls(self.execute, expected_calls_and_values) acc = self.iptables.get_traffic_counters('OUTPUT') self.assertEqual(acc['pkts'], exp_packets) self.assertEqual(acc['bytes'], exp_bytes) tools.verify_mock_calls(self.execute, expected_calls_and_values, any_order=True) def test_get_traffic_counters(self): self._test_get_traffic_counters_helper(False) def test_get_traffic_counters_with_ipv6(self): self._test_get_traffic_counters_helper(True) def _test_get_traffic_counters_with_zero_helper(self, use_ipv6): self.iptables = iptables_manager.IptablesManager( use_ipv6=use_ipv6) self.execute = mock.patch.object(self.iptables, "execute").start() exp_packets = 800 exp_bytes = 131802 expected_calls_and_values = [ (mock.call(['iptables', '-t', 'filter', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10', '-Z'], run_as_root=True), TRAFFIC_COUNTERS_DUMP), (mock.call(['iptables', '-t', 'raw', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10', '-Z'], run_as_root=True), ''), (mock.call(['iptables', '-t', 'mangle', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10', '-Z'], run_as_root=True), ''), (mock.call(['iptables', '-t', 'nat', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10', '-Z'], run_as_root=True), '') ] if use_ipv6: expected_calls_and_values.append( (mock.call(['ip6tables', '-t', 'raw', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10', '-Z'], run_as_root=True), '')) expected_calls_and_values.append( (mock.call(['ip6tables', '-t', 'filter', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10', '-Z'], run_as_root=True), TRAFFIC_COUNTERS_DUMP)) expected_calls_and_values.append( (mock.call(['ip6tables', '-t', 'mangle', '-L', 'OUTPUT', '-n', '-v', '-x', '-w', '10', '-Z'], run_as_root=True), '')) exp_packets *= 2 exp_bytes *= 2 tools.setup_mock_calls(self.execute, expected_calls_and_values) acc = self.iptables.get_traffic_counters('OUTPUT', zero=True) self.assertEqual(acc['pkts'], exp_packets) self.assertEqual(acc['bytes'], exp_bytes) tools.verify_mock_calls(self.execute, expected_calls_and_values, any_order=True) def test_get_traffic_counters_with_zero(self): self._test_get_traffic_counters_with_zero_helper(False) def test_get_traffic_counters_with_zero_with_ipv6(self): self._test_get_traffic_counters_with_zero_helper(True) def test_add_blank_rule(self): self.iptables = iptables_manager.IptablesManager( use_ipv6=False) self.execute = mock.patch.object(self.iptables, "execute").start() iptables_args = {} iptables_args.update(IPTABLES_ARG) filter_rules = ('-A %(bn)s-test-filter\n' % iptables_args) iptables_args['filter_rules'] = filter_rules filter_dump_mod = FILTER_RESTORE_DUMP % iptables_args expected_calls_and_values = [ (mock.call(['iptables-save'], run_as_root=True), (filter_dump_mod + MANGLE_RESTORE_DUMP + NAT_RESTORE_DUMP + RAW_RESTORE_DUMP)), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) self.iptables.ipv4['filter'].add_chain('test-filter') self.iptables.ipv4['filter'].add_rule('test-filter', '') self.iptables.apply() tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_add_rule_exchanged_interface_and_ip(self): self.iptables = iptables_manager.IptablesManager( use_ipv6=False) self.execute = mock.patch.object(self.iptables, "execute").start() iptables_args = {} iptables_args.update(IPTABLES_ARG) filter_rules = ('-A %(bn)s-test-filter -d 192.168.0.2 -i tap-xxx ' '-j ACCEPT\n' % iptables_args) iptables_args['filter_rules'] = filter_rules filter_dump_mod = FILTER_RESTORE_DUMP % iptables_args RESTORE_INPUT = ('# Generated by iptables_manager\n' '*filter\n' '-D run.py-test-filter 1\n' '-I run.py-test-filter 1 ' '-i tap-xxx -d 192.168.0.2 -j ACCEPT\n' 'COMMIT\n' '# Completed by iptables_manager\n' % IPTABLES_ARG) expected_calls_and_values = [ (mock.call(['iptables-save'], run_as_root=True), (filter_dump_mod + MANGLE_RESTORE_DUMP + NAT_RESTORE_DUMP + RAW_RESTORE_DUMP)), (mock.call(['iptables-restore', '-n'], process_input=RESTORE_INPUT, run_as_root=True, log_fail_as_error=False), None), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) self.iptables.ipv4['filter'].add_chain('test-filter') self.iptables.ipv4['filter'].add_rule('test-filter', '-i tap-xxx -d 192.168.0.2 ' '-j ACCEPT') self.iptables.apply() tools.verify_mock_calls(self.execute, expected_calls_and_values) class IptablesManagerStateLessTestCase(base.BaseTestCase): def setUp(self): super(IptablesManagerStateLessTestCase, self).setUp() cfg.CONF.set_override('comment_iptables_rules', False, 'AGENT') self.iptables = (iptables_manager.IptablesManager(state_less=True)) def test_nat_not_found(self): self.assertNotIn('nat', self.iptables.ipv4) def test_mangle_not_found(self): self.assertNotIn('mangle', self.iptables.ipv4) def test_initialize_mangle_table(self): iptables = iptables_manager.IptablesManager(state_less=True) iptables.initialize_mangle_table() self.assertIn('mangle', iptables.ipv4) self.assertNotIn('nat', iptables.ipv4) def test_initialize_nat_table(self): iptables = iptables_manager.IptablesManager(state_less=True) iptables.initialize_nat_table() self.assertIn('nat', iptables.ipv4) self.assertNotIn('mangle', iptables.ipv4) neutron-12.1.1/neutron/tests/unit/agent/linux/test_iptables_firewall.py0000664000175000017500000031754313553660047026507 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import mock from neutron_lib import constants from oslo_config import cfg import testtools from neutron.agent import firewall from neutron.agent.linux import ip_conntrack from neutron.agent.linux import ipset_manager from neutron.agent.linux import iptables_comments as ic from neutron.agent.linux import iptables_firewall from neutron.common import exceptions as n_exc from neutron.common import utils from neutron.conf.agent import common as agent_config from neutron.conf.agent import securitygroups_rpc as security_config from neutron.tests import base from neutron.tests.unit.api.v2 import test_base _uuid = test_base._uuid #TODO(mangelajo): replace all 'IPv4', 'IPv6' to constants FAKE_PREFIX = {'IPv4': '10.0.0.0/24', 'IPv6': 'fe80::/48'} FAKE_IP = {'IPv4': '10.0.0.1', 'IPv6': 'fe80::1'} #TODO(mangelajo): replace all '*_sgid' strings for the constants FAKE_SGID = 'fake_sgid' OTHER_SGID = 'other_sgid' _IPv6 = constants.IPv6 _IPv4 = constants.IPv4 RAW_TABLE_OUTPUT = """ # Generated by iptables-save v1.4.21 on Fri Jul 31 16:13:28 2015 *raw :PREROUTING ACCEPT [11561:3470468] :OUTPUT ACCEPT [11504:4064044] :neutron-openvswi-OUTPUT - [0:0] :neutron-openvswi-PREROUTING - [0:0] -A PREROUTING -j neutron-openvswi-PREROUTING -A OUTPUT -j neutron-openvswi-OUTPUT -A neutron-openvswi-PREROUTING -m physdev --physdev-in qvbe804433b-61 -j CT --zone 4097 -A neutron-openvswi-PREROUTING -m physdev --physdev-in tape804433b-61 -j CT --zone 4097 -A neutron-openvswi-PREROUTING -m physdev --physdev-in qvb95c24827-02 -j CT --zone 4098 -A neutron-openvswi-PREROUTING -m physdev --physdev-in tap95c24827-02 -j CT --zone 4098 -A neutron-openvswi-PREROUTING -m physdev --physdev-in qvb61634509-31 -j CT --zone 4098 -A neutron-openvswi-PREROUTING -m physdev --physdev-in tap61634509-31 -j CT --zone 4098 -A neutron-openvswi-PREROUTING -m physdev --physdev-in qvb8f46cf18-12 -j CT --zone 4105 -A neutron-openvswi-PREROUTING -m physdev --physdev-in tap8f46cf18-12 -j CT --zone 4105 COMMIT # Completed on Fri Jul 31 16:13:28 2015 """ # noqa class BaseIptablesFirewallTestCase(base.BaseTestCase): def setUp(self): super(BaseIptablesFirewallTestCase, self).setUp() mock.patch('eventlet.spawn_n').start() security_config.register_securitygroups_opts() agent_config.register_root_helper(cfg.CONF) cfg.CONF.set_override('comment_iptables_rules', False, 'AGENT') self.utils_exec_p = mock.patch( 'neutron.agent.linux.utils.execute') self.utils_exec = self.utils_exec_p.start() self.iptables_cls_p = mock.patch( 'neutron.agent.linux.iptables_manager.IptablesManager') iptables_cls = self.iptables_cls_p.start() self.iptables_inst = mock.Mock() self.v4filter_inst = mock.Mock() self.v6filter_inst = mock.Mock() self.iptables_inst.ipv4 = {'filter': self.v4filter_inst, 'raw': self.v4filter_inst } self.iptables_inst.ipv6 = {'filter': self.v6filter_inst, 'raw': self.v6filter_inst } iptables_cls.return_value = self.iptables_inst self.iptables_inst.get_rules_for_table.return_value = ( RAW_TABLE_OUTPUT.splitlines()) self.firewall = iptables_firewall.IptablesFirewallDriver() self.firewall.iptables = self.iptables_inst # don't mess with sysctl knobs in unit tests self.firewall._enabled_netfilter_for_bridges = True # initial data has 1, 2, and 9 in use, see RAW_TABLE_OUTPUT above. self._dev_zone_map = {'61634509-31': 4098, '8f46cf18-12': 4105, '95c24827-02': 4098, 'e804433b-61': 4097} get_rules_for_table_func = lambda x: RAW_TABLE_OUTPUT.split('\n') filtered_ports = {port_id: self._fake_port() for port_id in self._dev_zone_map} self.firewall.ipconntrack = ip_conntrack.IpConntrackManager( get_rules_for_table_func, filtered_ports=filtered_ports, unfiltered_ports=dict()) def _fake_port(self): return {'device': 'tapfake_dev', 'mac_address': 'ff:ff:ff:ff:ff:ff', 'network_id': 'fake_net', 'fixed_ips': [FAKE_IP['IPv4'], FAKE_IP['IPv6']]} class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): def test_prepare_port_filter_with_no_sg(self): port = self._fake_port() self.firewall.prepare_port_filter(port) calls = [mock.call.add_chain('sg-fallback'), mock.call.add_rule( 'sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP), mock.call.add_chain('sg-chain'), mock.call.add_rule('PREROUTING', mock.ANY, # zone set comment=None), mock.call.add_rule('PREROUTING', mock.ANY, # zone set comment=None), mock.call.add_rule('PREROUTING', mock.ANY, # zone set comment=None), mock.call.add_chain('ifake_dev'), mock.call.add_rule('FORWARD', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j $sg-chain', top=True, comment=ic.VM_INT_SG), mock.call.add_rule('sg-chain', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j $ifake_dev', top=False, comment=ic.SG_TO_VM_SG), mock.call.add_rule( 'ifake_dev', '-m state --state RELATED,ESTABLISHED -j RETURN', top=False, comment=None), mock.call.add_rule( 'ifake_dev', '-m state --state INVALID -j DROP', top=False, comment=None), mock.call.add_rule('ifake_dev', '-j $sg-fallback', top=False, comment=None), mock.call.add_chain('ofake_dev'), mock.call.add_rule('FORWARD', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $sg-chain', top=True, comment=ic.VM_INT_SG), mock.call.add_rule('sg-chain', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $ofake_dev', top=False, comment=ic.SG_TO_VM_SG), mock.call.add_rule('INPUT', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $ofake_dev', top=False, comment=ic.INPUT_TO_SG), mock.call.add_chain('sfake_dev'), mock.call.add_rule( 'sfake_dev', '-s 10.0.0.1/32 -m mac --mac-source FF:FF:FF:FF:FF:FF ' '-j RETURN', comment=ic.PAIR_ALLOW), mock.call.add_rule( 'sfake_dev', '-j DROP', comment=ic.PAIR_DROP), mock.call.add_rule( 'ofake_dev', '-s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ' '--sport 68 --dport 67 -j RETURN', top=False, comment=None), mock.call.add_rule('ofake_dev', '-j $sfake_dev', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 68 --dport 67 -j RETURN', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 67 --dport 68 -j DROP', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-m state --state RELATED,ESTABLISHED -j RETURN', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-m state --state INVALID -j DROP', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-j $sg-fallback', top=False, comment=None), mock.call.add_rule('sg-chain', '-j ACCEPT')] self.v4filter_inst.assert_has_calls(calls) def test_filter_ipv4_ingress(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress'} ingress = mock.call.add_rule('ifake_dev', '-j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'source_ip_prefix': prefix} ingress = mock.call.add_rule( 'ifake_dev', '-s %s -j RETURN' % prefix, top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_tcp(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'tcp'} ingress = mock.call.add_rule( 'ifake_dev', '-p tcp -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_tcp_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'tcp', 'source_ip_prefix': prefix} ingress = mock.call.add_rule('ifake_dev', '-s %s -p tcp -j RETURN' % prefix, top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_icmp(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'icmp'} ingress = mock.call.add_rule('ifake_dev', '-p icmp -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_icmp_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'icmp', 'source_ip_prefix': prefix} ingress = mock.call.add_rule( 'ifake_dev', '-s %s -p icmp -j RETURN' % prefix, top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_tcp_port(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'tcp', 'port_range_min': 10, 'port_range_max': 10} ingress = mock.call.add_rule('ifake_dev', '-p tcp -m tcp --dport 10 -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_bad_vrrp_with_dport(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'vrrp', 'port_range_min': 10, 'port_range_max': 10} # Dest port isn't support with VRRP, so don't send it # down to iptables. ingress = mock.call.add_rule('ifake_dev', '-p vrrp -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_tcp_port_by_num(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': '6', 'port_range_min': 10, 'port_range_max': 10} ingress = mock.call.add_rule('ifake_dev', '-p tcp -m tcp --dport 10 -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_tcp_mport(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'tcp', 'port_range_min': 10, 'port_range_max': 100} ingress = mock.call.add_rule( 'ifake_dev', '-p tcp -m tcp -m multiport --dports 10:100 -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_tcp_mport_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'tcp', 'port_range_min': 10, 'port_range_max': 100, 'source_ip_prefix': prefix} ingress = mock.call.add_rule( 'ifake_dev', '-s %s -p tcp -m tcp -m multiport --dports 10:100 ' '-j RETURN' % prefix, top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_udp(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'udp'} ingress = mock.call.add_rule( 'ifake_dev', '-p udp -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_udp_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'udp', 'source_ip_prefix': prefix} ingress = mock.call.add_rule('ifake_dev', '-s %s -p udp -j RETURN' % prefix, top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_udp_port(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'udp', 'port_range_min': 10, 'port_range_max': 10} ingress = mock.call.add_rule('ifake_dev', '-p udp -m udp --dport 10 -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_udp_mport(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'udp', 'port_range_min': 10, 'port_range_max': 100} ingress = mock.call.add_rule( 'ifake_dev', '-p udp -m udp -m multiport --dports 10:100 -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_udp_mport_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'udp', 'port_range_min': 10, 'port_range_max': 100, 'source_ip_prefix': prefix} ingress = mock.call.add_rule( 'ifake_dev', '-s %s -p udp -m udp -m multiport --dports 10:100 ' '-j RETURN' % prefix, top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_dccp_port(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'dccp', 'port_range_min': 10, 'port_range_max': 10} ingress = mock.call.add_rule('ifake_dev', '-p dccp -m dccp --dport 10 -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_sctp_port(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'sctp', 'port_range_min': 10, 'port_range_max': 10} ingress = mock.call.add_rule('ifake_dev', '-p sctp -m sctp --dport 10 -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_protocol_blank(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': ''} ingress = mock.call.add_rule('ifake_dev', '-j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_protocol_zero(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': '0'} ingress = mock.call.add_rule('ifake_dev', '-j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_protocol_encap(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'encap'} ingress = mock.call.add_rule('ifake_dev', '-p encap -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_protocol_encap_by_num(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': '98'} ingress = mock.call.add_rule('ifake_dev', '-p encap -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_ingress_protocol_999_local(self): # There is no protocol 999, so let's return a mapping # that says there is and make sure the rule is created # using the name and not the number. rule = {'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': '999'} ingress = mock.call.add_rule('ifake_dev', '-p fooproto -j RETURN', top=False, comment=None) egress = None with mock.patch.object(self.firewall, '_local_protocol_name_map') as lpnm: lpnm.return_value = {'999': 'fooproto'} self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress(self): rule = {'ethertype': 'IPv4', 'direction': 'egress'} egress = mock.call.add_rule('ofake_dev', '-j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_dest_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'egress', 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_source_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'egress', 'source_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-s %s -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_tcp(self): rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'tcp'} egress = mock.call.add_rule( 'ofake_dev', '-p tcp -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_tcp_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'tcp', 'dest_ip_prefix': prefix} egress = mock.call.add_rule('ofake_dev', '-d %s -p tcp -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_icmp(self): rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'icmp'} egress = mock.call.add_rule('ofake_dev', '-p icmp -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_icmp_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'icmp', 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p icmp -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_icmp_type(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'icmp', 'port_range_min': 8, 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p icmp -m icmp --icmp-type 8 -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_icmp_type_name(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'icmp', 'port_range_min': 'echo-request', 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p icmp -m icmp --icmp-type echo-request ' '-j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_icmp_type_code(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'icmp', 'port_range_min': 8, 'port_range_max': 0, 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p icmp -m icmp --icmp-type 8/0 -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_icmp_type_code_protocol_num(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': '1', 'port_range_min': 8, 'port_range_max': 0, 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p icmp -m icmp --icmp-type 8/0 -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_tcp_port(self): rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'tcp', 'port_range_min': 10, 'port_range_max': 10} egress = mock.call.add_rule('ofake_dev', '-p tcp -m tcp --dport 10 -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_tcp_mport(self): rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'tcp', 'port_range_min': 10, 'port_range_max': 100} egress = mock.call.add_rule( 'ofake_dev', '-p tcp -m tcp -m multiport --dports 10:100 -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_tcp_mport_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'tcp', 'port_range_min': 10, 'port_range_max': 100, 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p tcp -m tcp -m multiport --dports 10:100 ' '-j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_udp(self): rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'udp'} egress = mock.call.add_rule( 'ofake_dev', '-p udp -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_udp_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'udp', 'dest_ip_prefix': prefix} egress = mock.call.add_rule('ofake_dev', '-d %s -p udp -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_udp_port(self): rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'udp', 'port_range_min': 10, 'port_range_max': 10} egress = mock.call.add_rule('ofake_dev', '-p udp -m udp --dport 10 -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_udp_mport(self): rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'udp', 'port_range_min': 10, 'port_range_max': 100} egress = mock.call.add_rule( 'ofake_dev', '-p udp -m udp -m multiport --dports 10:100 -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv4_egress_udp_mport_prefix(self): prefix = FAKE_PREFIX['IPv4'] rule = {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'udp', 'port_range_min': 10, 'port_range_max': 100, 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p udp -m udp -m multiport --dports 10:100 ' '-j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress(self): rule = {'ethertype': 'IPv6', 'direction': 'ingress'} ingress = mock.call.add_rule('ifake_dev', '-j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress_prefix(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'source_ip_prefix': prefix} ingress = mock.call.add_rule( 'ifake_dev', '-s %s -j RETURN' % prefix, top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress_tcp(self): rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'protocol': 'tcp'} ingress = mock.call.add_rule( 'ifake_dev', '-p tcp -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress_tcp_prefix(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'protocol': 'tcp', 'source_ip_prefix': prefix} ingress = mock.call.add_rule('ifake_dev', '-s %s -p tcp -j RETURN' % prefix, top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress_tcp_port(self): rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'protocol': 'tcp', 'port_range_min': 10, 'port_range_max': 10} ingress = mock.call.add_rule('ifake_dev', '-p tcp -m tcp --dport 10 -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress_icmp(self): rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'protocol': 'icmp'} ingress = mock.call.add_rule( 'ifake_dev', '-p ipv6-icmp -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress_icmp_prefix(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'protocol': 'icmp', 'source_ip_prefix': prefix} ingress = mock.call.add_rule( 'ifake_dev', '-s %s -p ipv6-icmp -j RETURN' % prefix, top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress_tcp_mport(self): rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'protocol': 'tcp', 'port_range_min': 10, 'port_range_max': 100} ingress = mock.call.add_rule( 'ifake_dev', '-p tcp -m tcp -m multiport --dports 10:100 -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def _test_filter_ingress_tcp_min_port_0(self, ethertype): rule = {'ethertype': ethertype, 'direction': 'ingress', 'protocol': 'tcp', 'port_range_min': 0, 'port_range_max': 100} ingress = mock.call.add_rule( 'ifake_dev', '-p tcp -m tcp -m multiport --dports 0:100 -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ingress_tcp_min_port_0_for_ipv4(self): self._test_filter_ingress_tcp_min_port_0('IPv4') def test_filter_ingress_tcp_min_port_0_for_ipv6(self): self._test_filter_ingress_tcp_min_port_0('IPv6') def test_filter_ipv6_ingress_tcp_mport_prefix(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'protocol': 'tcp', 'port_range_min': 10, 'port_range_max': 100, 'source_ip_prefix': prefix} ingress = mock.call.add_rule( 'ifake_dev', '-s %s -p tcp -m tcp -m multiport --dports 10:100 ' '-j RETURN' % prefix, top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress_udp(self): rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'protocol': 'udp'} ingress = mock.call.add_rule( 'ifake_dev', '-p udp -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress_udp_prefix(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'protocol': 'udp', 'source_ip_prefix': prefix} ingress = mock.call.add_rule('ifake_dev', '-s %s -p udp -j RETURN' % prefix, top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress_udp_port(self): rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'protocol': 'udp', 'port_range_min': 10, 'port_range_max': 10} ingress = mock.call.add_rule('ifake_dev', '-p udp -m udp --dport 10 -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress_udp_mport(self): rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'protocol': 'udp', 'port_range_min': 10, 'port_range_max': 100} ingress = mock.call.add_rule( 'ifake_dev', '-p udp -m udp -m multiport --dports 10:100 -j RETURN', top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_ingress_udp_mport_prefix(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'ingress', 'protocol': 'udp', 'port_range_min': 10, 'port_range_max': 100, 'source_ip_prefix': prefix} ingress = mock.call.add_rule( 'ifake_dev', '-s %s -p udp -m udp -m multiport --dports 10:100 ' '-j RETURN' % prefix, top=False, comment=None) egress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress(self): rule = {'ethertype': 'IPv6', 'direction': 'egress'} egress = mock.call.add_rule('ofake_dev', '-j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_prefix(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'egress', 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_tcp(self): rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'tcp'} egress = mock.call.add_rule( 'ofake_dev', '-p tcp -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_tcp_prefix(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'tcp', 'dest_ip_prefix': prefix} egress = mock.call.add_rule('ofake_dev', '-d %s -p tcp -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_icmp(self): rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'icmp'} egress = mock.call.add_rule( 'ofake_dev', '-p ipv6-icmp -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_icmp_prefix(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'icmp', 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p ipv6-icmp -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_icmp_type(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'icmp', 'port_range_min': 8, 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p ipv6-icmp -m icmp6 --icmpv6-type 8 -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_icmp_type_name(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'icmp', 'port_range_min': 'echo-request', 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p ipv6-icmp -m icmp6 --icmpv6-type echo-request ' '-j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_icmp_type_code(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'icmp', 'port_range_min': 8, 'port_range_max': 0, 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p ipv6-icmp -m icmp6 --icmpv6-type 8/0 -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_icmp_type_code_protocol_num(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': '58', 'port_range_min': 8, 'port_range_max': 0, 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p ipv6-icmp -m icmp6 --icmpv6-type 8/0 -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_icmp_type_code_protocol_legacy_name(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'icmpv6', 'port_range_min': 8, 'port_range_max': 0, 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p ipv6-icmp -m icmp6 --icmpv6-type 8/0 -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_tcp_port(self): rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'tcp', 'port_range_min': 10, 'port_range_max': 10} egress = mock.call.add_rule('ofake_dev', '-p tcp -m tcp --dport 10 -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_tcp_mport(self): rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'tcp', 'port_range_min': 10, 'port_range_max': 100} egress = mock.call.add_rule( 'ofake_dev', '-p tcp -m tcp -m multiport --dports 10:100 -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_tcp_mport_prefix(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'tcp', 'port_range_min': 10, 'port_range_max': 100, 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p tcp -m tcp -m multiport --dports 10:100 ' '-j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_udp(self): rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'udp'} egress = mock.call.add_rule( 'ofake_dev', '-p udp -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_udp_prefix(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'udp', 'dest_ip_prefix': prefix} egress = mock.call.add_rule('ofake_dev', '-d %s -p udp -j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_udp_port(self): rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'udp', 'port_range_min': 10, 'port_range_max': 10} egress = mock.call.add_rule('ofake_dev', '-p udp -m udp --dport 10 -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_udp_mport(self): rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'udp', 'port_range_min': 10, 'port_range_max': 100} egress = mock.call.add_rule( 'ofake_dev', '-p udp -m udp -m multiport --dports 10:100 -j RETURN', top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def test_filter_ipv6_egress_udp_mport_prefix(self): prefix = FAKE_PREFIX['IPv6'] rule = {'ethertype': 'IPv6', 'direction': 'egress', 'protocol': 'udp', 'port_range_min': 10, 'port_range_max': 100, 'dest_ip_prefix': prefix} egress = mock.call.add_rule( 'ofake_dev', '-d %s -p udp -m udp -m multiport --dports 10:100 ' '-j RETURN' % prefix, top=False, comment=None) ingress = None self._test_prepare_port_filter(rule, ingress, egress) def _test_process_trusted_ports(self, configured): port = self._fake_port() port['id'] = 'tapfake_dev' calls = [ mock.call.add_chain('sg-fallback'), mock.call.add_rule('sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP)] if configured: self.firewall.trusted_ports.append(port['id']) else: calls.append( mock.call.add_rule('FORWARD', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j ACCEPT', top=False, comment=ic.TRUSTED_ACCEPT)) calls.append( mock.call.add_rule('FORWARD', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j ACCEPT', top=False, comment=ic.TRUSTED_ACCEPT)) self.firewall.process_trusted_ports([port['id']]) for filter_inst in [self.v4filter_inst, self.v6filter_inst]: comb = zip(calls, filter_inst.mock_calls) for (l, r) in comb: self.assertEqual(l, r) filter_inst.assert_has_calls(calls) self.assertIn(port['id'], self.firewall.trusted_ports) def test_process_trusted_ports(self): self._test_process_trusted_ports(False) def test_process_trusted_ports_already_configured(self): self._test_process_trusted_ports(True) def _test_remove_trusted_ports(self, configured): port = self._fake_port() port['id'] = 'tapfake_dev' calls = [ mock.call.add_chain('sg-fallback'), mock.call.add_rule('sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP)] if configured: self.firewall.trusted_ports.append(port['id']) calls.append( mock.call.remove_rule('FORWARD', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j ACCEPT')) calls.append( mock.call.remove_rule('FORWARD', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j ACCEPT')) self.firewall.remove_trusted_ports([port['id']]) for filter_inst in [self.v4filter_inst, self.v6filter_inst]: comb = zip(calls, filter_inst.mock_calls) for (l, r) in comb: self.assertEqual(l, r) filter_inst.assert_has_calls(calls) self.assertNotIn(port['id'], self.firewall.trusted_ports) def test_remove_trusted_ports(self): self._test_remove_trusted_ports(True) def test_process_remove_ports_not_configured(self): self._test_remove_trusted_ports(False) def _test_prepare_port_filter(self, rule, ingress_expected_call=None, egress_expected_call=None): port = self._fake_port() ethertype = rule['ethertype'] prefix = utils.ip_to_cidr(FAKE_IP[ethertype]) filter_inst = self.v4filter_inst dhcp_rule = [mock.call.add_rule( 'ofake_dev', '-s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ' '--sport 68 --dport 67 -j RETURN', top=False, comment=None)] if ethertype == 'IPv6': filter_inst = self.v6filter_inst dhcp_rule = [mock.call.add_rule('ofake_dev', '-s ::/128 -d ff02::/16 ' '-p ipv6-icmp -m icmp6 ' '--icmpv6-type %s -j RETURN' % icmp6_type, top=False, comment=None) for icmp6_type in constants.ICMPV6_ALLOWED_UNSPEC_ADDR_TYPES] sg = [rule] port['security_group_rules'] = sg self.firewall.prepare_port_filter(port) calls = [mock.call.add_chain('sg-fallback'), mock.call.add_rule( 'sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP), mock.call.add_chain('sg-chain'), mock.call.add_rule('PREROUTING', mock.ANY, # zone set comment=None), mock.call.add_rule('PREROUTING', mock.ANY, # zone set comment=None), mock.call.add_rule('PREROUTING', mock.ANY, # zone set comment=None), mock.call.add_chain('ifake_dev'), mock.call.add_rule('FORWARD', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j $sg-chain', top=True, comment=ic.VM_INT_SG), mock.call.add_rule('sg-chain', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j $ifake_dev', top=False, comment=ic.SG_TO_VM_SG) ] if ethertype == 'IPv6': for icmp6_type in firewall.ICMPV6_ALLOWED_INGRESS_TYPES: calls.append( mock.call.add_rule('ifake_dev', '-p ipv6-icmp -m icmp6 --icmpv6-type ' '%s -j RETURN' % icmp6_type, top=False, comment=None)) calls += [ mock.call.add_rule( 'ifake_dev', '-m state --state RELATED,ESTABLISHED -j RETURN', top=False, comment=None ) ] if ingress_expected_call: calls.append(ingress_expected_call) calls += [mock.call.add_rule( 'ifake_dev', '-m state --state INVALID -j DROP', top=False, comment=None), mock.call.add_rule('ifake_dev', '-j $sg-fallback', top=False, comment=None), mock.call.add_chain('ofake_dev'), mock.call.add_rule('FORWARD', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $sg-chain', top=True, comment=ic.VM_INT_SG), mock.call.add_rule('sg-chain', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $ofake_dev', top=False, comment=ic.SG_TO_VM_SG), mock.call.add_rule('INPUT', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $ofake_dev', top=False, comment=ic.INPUT_TO_SG), mock.call.add_chain('sfake_dev'), mock.call.add_rule( 'sfake_dev', '-s %s -m mac --mac-source FF:FF:FF:FF:FF:FF -j RETURN' % prefix, comment=ic.PAIR_ALLOW)] if ethertype == 'IPv6': calls.append(mock.call.add_rule('sfake_dev', '-s fe80::fdff:ffff:feff:ffff/128 -m mac ' '--mac-source FF:FF:FF:FF:FF:FF -j RETURN', comment=ic.PAIR_ALLOW)) calls.append(mock.call.add_rule('sfake_dev', '-j DROP', comment=ic.PAIR_DROP)) calls += dhcp_rule calls.append(mock.call.add_rule('ofake_dev', '-j $sfake_dev', top=False, comment=None)) if ethertype == 'IPv4': calls.append(mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 68 --dport 67 -j RETURN', top=False, comment=None)) calls.append(mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 67 --dport 68 -j DROP', top=False, comment=None)) if ethertype == 'IPv6': calls.append(mock.call.add_rule('ofake_dev', '-p ipv6-icmp -m icmp6 ' '--icmpv6-type %s -j DROP' % constants.ICMPV6_TYPE_RA, top=False, comment=None)) calls.append(mock.call.add_rule('ofake_dev', '-p ipv6-icmp -j RETURN', top=False, comment=None)) calls.append(mock.call.add_rule('ofake_dev', '-p udp -m udp ' '--sport 546 --dport 547 ' '-j RETURN', top=False, comment=None)) calls.append(mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 547 --dport 546 -j DROP', top=False, comment=None)) calls += [ mock.call.add_rule( 'ofake_dev', '-m state --state RELATED,ESTABLISHED -j RETURN', top=False, comment=None), ] if egress_expected_call: calls.append(egress_expected_call) calls += [mock.call.add_rule( 'ofake_dev', '-m state --state INVALID -j DROP', top=False, comment=None), mock.call.add_rule('ofake_dev', '-j $sg-fallback', top=False, comment=None), mock.call.add_rule('sg-chain', '-j ACCEPT')] comb = zip(calls, filter_inst.mock_calls) for (l, r) in comb: self.assertEqual(l, r) filter_inst.assert_has_calls(calls) def _test_remove_conntrack_entries(self, ethertype, protocol, direction, ct_zone): port = self._fake_port() port['security_groups'] = 'fake_sg_id' self.firewall.filtered_ports[port['device']] = port self.firewall.updated_rule_sg_ids = set(['fake_sg_id']) self.firewall.sg_rules['fake_sg_id'] = [ {'direction': direction, 'ethertype': ethertype, 'protocol': protocol}] with mock.patch.dict(self.firewall.ipconntrack._device_zone_map, {port['network_id']: ct_zone}): self.firewall.filter_defer_apply_on() self.firewall.sg_rules['fake_sg_id'] = [] self.firewall.filter_defer_apply_off() if not ct_zone: self.assertFalse(self.utils_exec.called) return # process conntrack updates in the queue while not self.firewall.ipconntrack._queue.empty(): self.firewall.ipconntrack._process_queue() cmd = ['conntrack', '-D'] if protocol: cmd.extend(['-p', protocol]) if ethertype == 'IPv4': cmd.extend(['-f', 'ipv4']) if direction == 'ingress': cmd.extend(['-d', '10.0.0.1']) else: cmd.extend(['-s', '10.0.0.1']) else: cmd.extend(['-f', 'ipv6']) if direction == 'ingress': cmd.extend(['-d', 'fe80::1']) else: cmd.extend(['-s', 'fe80::1']) cmd.extend(['-w', ct_zone]) calls = [ mock.call(cmd, run_as_root=True, check_exit_code=True, extra_ok_codes=[1])] self.utils_exec.assert_has_calls(calls) def test_remove_conntrack_entries_for_delete_rule_ipv4(self): for direction in ['ingress', 'egress']: for pro in [None, 'tcp', 'icmp', 'udp']: self._test_remove_conntrack_entries( 'IPv4', pro, direction, ct_zone=10) def test_remove_conntrack_entries_for_delete_rule_ipv4_no_ct_zone(self): for direction in ['ingress', 'egress']: for pro in [None, 'tcp', 'icmp', 'udp']: self._test_remove_conntrack_entries( 'IPv4', pro, direction, ct_zone=None) def test_remove_conntrack_entries_for_delete_rule_ipv6(self): for direction in ['ingress', 'egress']: for pro in [None, 'tcp', 'icmp', 'udp']: self._test_remove_conntrack_entries( 'IPv6', pro, direction, ct_zone=10) def test_remove_conntrack_entries_for_delete_rule_ipv6_no_ct_zone(self): for direction in ['ingress', 'egress']: for pro in [None, 'tcp', 'icmp', 'udp']: self._test_remove_conntrack_entries( 'IPv6', pro, direction, ct_zone=None) def test_remove_conntrack_entries_for_port_sec_group_change(self): self._test_remove_conntrack_entries_for_port_sec_group_change( ct_zone=10) def test_remove_conntrack_entries_for_port_sec_group_change_no_ct_zone( self): self._test_remove_conntrack_entries_for_port_sec_group_change( ct_zone=None) def _get_expected_conntrack_calls(self, ips, ct_zone): expected_calls = [] for ip_item in ips: proto = ip_item[0] ip = ip_item[1] for direction in ['-d', '-s']: cmd = ['conntrack', '-D', '-f', proto, direction, ip] if ct_zone: cmd.extend(['-w', ct_zone]) expected_calls.append( mock.call(cmd, run_as_root=True, check_exit_code=True, extra_ok_codes=[1])) return expected_calls def _test_remove_conntrack_entries_for_port_sec_group_change(self, ct_zone): port = self._fake_port() port['security_groups'] = ['fake_sg_id'] self.firewall.filtered_ports[port['device']] = port self.firewall.updated_sg_members = set(['tapfake_dev']) with mock.patch.dict(self.firewall.ipconntrack._device_zone_map, {port['network_id']: ct_zone}): self.firewall.filter_defer_apply_on() new_port = copy.deepcopy(port) new_port['security_groups'] = ['fake_sg_id2'] self.firewall.filtered_ports[port['device']] = new_port self.firewall.filter_defer_apply_off() if not ct_zone: self.assertFalse(self.utils_exec.called) return # process conntrack updates in the queue while not self.firewall.ipconntrack._queue.empty(): self.firewall.ipconntrack._process_queue() calls = self._get_expected_conntrack_calls( [('ipv4', '10.0.0.1'), ('ipv6', 'fe80::1')], ct_zone) self.utils_exec.assert_has_calls(calls) def test_remove_conntrack_entries_for_sg_member_changed_ipv4(self): for direction in ['ingress', 'egress']: self._test_remove_conntrack_entries_sg_member_changed( 'IPv4', direction, ct_zone=10) def test_remove_conntrack_entries_for_sg_member_changed_ipv4_no_ct_zone( self): for direction in ['ingress', 'egress']: self._test_remove_conntrack_entries_sg_member_changed( 'IPv4', direction, ct_zone=None) def test_remove_conntrack_entries_for_sg_member_changed_ipv6(self): for direction in ['ingress', 'egress']: self._test_remove_conntrack_entries_sg_member_changed( 'IPv6', direction, ct_zone=10) def test_remove_conntrack_entries_for_sg_member_changed_ipv6_no_ct_zone( self): for direction in ['ingress', 'egress']: self._test_remove_conntrack_entries_sg_member_changed( 'IPv6', direction, ct_zone=None) def _test_remove_conntrack_entries_sg_member_changed(self, ethertype, direction, ct_zone): port = self._fake_port() port['security_groups'] = ['fake_sg_id'] port['security_group_source_groups'] = ['fake_sg_id2'] port['security_group_rules'] = [{'security_group_id': 'fake_sg_id', 'direction': direction, 'remote_group_id': 'fake_sg_id2', 'ethertype': ethertype}] self.firewall.filtered_ports = {port['device']: port} if ethertype == "IPv4": ethertype = "ipv4" members_add = {'IPv4': ['10.0.0.2', '10.0.0.3']} members_after_delete = {'IPv4': ['10.0.0.3']} else: ethertype = "ipv6" members_add = {'IPv6': ['fe80::2', 'fe80::3']} members_after_delete = {'IPv6': ['fe80::3']} with mock.patch.dict(self.firewall.ipconntrack._device_zone_map, {port['network_id']: ct_zone}): # add ['10.0.0.2', '10.0.0.3'] or ['fe80::2', 'fe80::3'] self.firewall.security_group_updated('sg_member', ['fake_sg_id2']) self.firewall.update_security_group_members( 'fake_sg_id2', members_add) # delete '10.0.0.2' or 'fe80::2' self.firewall.security_group_updated('sg_member', ['fake_sg_id2']) self.firewall.update_security_group_members( 'fake_sg_id2', members_after_delete) # check conntrack deletion from '10.0.0.1' to '10.0.0.2' or # from 'fe80::1' to 'fe80::2' ips = {"ipv4": ['10.0.0.1', '10.0.0.2'], "ipv6": ['fe80::1', 'fe80::2']} calls = [] # process conntrack updates in the queue while not self.firewall.ipconntrack._queue.empty(): self.firewall.ipconntrack._process_queue() for direction in ['ingress', 'egress']: direction = '-d' if direction == 'ingress' else '-s' remote_ip_direction = '-s' if direction == '-d' else '-d' conntrack_cmd = ['conntrack', '-D', '-f', ethertype, direction, ips[ethertype][0]] if not ct_zone: continue conntrack_cmd.extend(['-w', 10]) conntrack_cmd.extend([remote_ip_direction, ips[ethertype][1]]) calls.append(mock.call(conntrack_cmd, run_as_root=True, check_exit_code=True, extra_ok_codes=[1])) self.utils_exec.assert_has_calls(calls) def test_user_sg_rules_deduped_before_call_to_iptables_manager(self): port = self._fake_port() port['security_group_rules'] = [{'ethertype': 'IPv4', 'direction': 'ingress'}] * 2 self.firewall.prepare_port_filter(port) rules = [''.join(c[1]) for c in self.v4filter_inst.add_rule.mock_calls] self.assertEqual(len(set(rules)), len(rules)) def test_update_delete_port_filter(self): port = self._fake_port() port['security_group_rules'] = [{'ethertype': 'IPv4', 'direction': 'ingress'}] self.firewall.prepare_port_filter(port) port['security_group_rules'] = [{'ethertype': 'IPv4', 'direction': 'egress'}] self.firewall.update_port_filter(port) self.firewall.update_port_filter({'device': 'no-exist-device'}) self.firewall.remove_port_filter(port) self.firewall.remove_port_filter({'device': 'no-exist-device'}) calls = [mock.call.add_chain('sg-fallback'), mock.call.add_rule( 'sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP), mock.call.add_chain('sg-chain'), mock.call.add_rule('PREROUTING', mock.ANY, comment=None), # zone set mock.call.add_rule('PREROUTING', mock.ANY, comment=None), # zone set mock.call.add_rule('PREROUTING', mock.ANY, comment=None), # zone set mock.call.add_chain('ifake_dev'), mock.call.add_rule( 'FORWARD', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j $sg-chain', top=True, comment=ic.VM_INT_SG), mock.call.add_rule( 'sg-chain', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j $ifake_dev', top=False, comment=ic.SG_TO_VM_SG), mock.call.add_rule( 'ifake_dev', '-m state --state RELATED,ESTABLISHED -j RETURN', top=False, comment=None), mock.call.add_rule('ifake_dev', '-j RETURN', top=False, comment=None), mock.call.add_rule( 'ifake_dev', '-m state --state INVALID -j DROP', top=False, comment=None), mock.call.add_rule('ifake_dev', '-j $sg-fallback', top=False, comment=None), mock.call.add_chain('ofake_dev'), mock.call.add_rule( 'FORWARD', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $sg-chain', top=True, comment=ic.VM_INT_SG), mock.call.add_rule( 'sg-chain', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $ofake_dev', top=False, comment=ic.SG_TO_VM_SG), mock.call.add_rule( 'INPUT', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $ofake_dev', top=False, comment=ic.INPUT_TO_SG), mock.call.add_chain('sfake_dev'), mock.call.add_rule( 'sfake_dev', '-s 10.0.0.1/32 -m mac --mac-source FF:FF:FF:FF:FF:FF ' '-j RETURN', comment=ic.PAIR_ALLOW), mock.call.add_rule( 'sfake_dev', '-j DROP', comment=ic.PAIR_DROP), mock.call.add_rule( 'ofake_dev', '-s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ' '--sport 68 --dport 67 -j RETURN', top=False, comment=None), mock.call.add_rule('ofake_dev', '-j $sfake_dev', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 68 --dport 67 -j RETURN', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 67 --dport 68 -j DROP', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-m state --state RELATED,ESTABLISHED -j RETURN', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-m state --state INVALID -j DROP', top=False, comment=None), mock.call.add_rule('ofake_dev', '-j $sg-fallback', top=False, comment=None), mock.call.add_rule('sg-chain', '-j ACCEPT'), mock.call.remove_chain('ifake_dev'), mock.call.remove_chain('ofake_dev'), mock.call.remove_chain('sfake_dev'), mock.call.remove_rule('PREROUTING', mock.ANY), # zone set mock.call.remove_rule('PREROUTING', mock.ANY), # zone set mock.call.remove_rule('PREROUTING', mock.ANY), # zone set mock.call.remove_chain('sg-chain'), mock.call.add_chain('sg-chain'), mock.call.add_rule('PREROUTING', mock.ANY, comment=None), # zone set mock.call.add_rule('PREROUTING', mock.ANY, comment=None), # zone set mock.call.add_rule('PREROUTING', mock.ANY, comment=None), # zone set mock.call.add_chain('ifake_dev'), mock.call.add_rule( 'FORWARD', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j $sg-chain', top=True, comment=ic.VM_INT_SG), mock.call.add_rule( 'sg-chain', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j $ifake_dev', top=False, comment=ic.SG_TO_VM_SG), mock.call.add_rule( 'ifake_dev', '-m state --state RELATED,ESTABLISHED -j RETURN', top=False, comment=None), mock.call.add_rule( 'ifake_dev', '-m state --state INVALID -j DROP', top=False, comment=None), mock.call.add_rule('ifake_dev', '-j $sg-fallback', top=False, comment=None), mock.call.add_chain('ofake_dev'), mock.call.add_rule( 'FORWARD', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $sg-chain', top=True, comment=ic.VM_INT_SG), mock.call.add_rule( 'sg-chain', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $ofake_dev', top=False, comment=ic.SG_TO_VM_SG), mock.call.add_rule( 'INPUT', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $ofake_dev', top=False, comment=ic.INPUT_TO_SG), mock.call.add_chain('sfake_dev'), mock.call.add_rule( 'sfake_dev', '-s 10.0.0.1/32 -m mac --mac-source FF:FF:FF:FF:FF:FF ' '-j RETURN', comment=ic.PAIR_ALLOW), mock.call.add_rule( 'sfake_dev', '-j DROP', comment=ic.PAIR_DROP), mock.call.add_rule( 'ofake_dev', '-s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ' '--sport 68 --dport 67 -j RETURN', top=False, comment=None), mock.call.add_rule('ofake_dev', '-j $sfake_dev', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 68 --dport 67 -j RETURN', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 67 --dport 68 -j DROP', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-m state --state RELATED,ESTABLISHED -j RETURN', top=False, comment=None), mock.call.add_rule('ofake_dev', '-j RETURN', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-m state --state INVALID -j DROP', top=False, comment=None), mock.call.add_rule('ofake_dev', '-j $sg-fallback', top=False, comment=None), mock.call.add_rule('sg-chain', '-j ACCEPT'), mock.call.remove_chain('ifake_dev'), mock.call.remove_chain('ofake_dev'), mock.call.remove_chain('sfake_dev'), mock.call.remove_rule('PREROUTING', mock.ANY), # zone set mock.call.remove_rule('PREROUTING', mock.ANY), # zone set mock.call.remove_rule('PREROUTING', mock.ANY), # zone set mock.call.remove_chain('sg-chain'), mock.call.add_chain('sg-chain')] self.v4filter_inst.assert_has_calls(calls) def test_delete_conntrack_from_delete_port(self): self._test_delete_conntrack_from_delete_port(ct_zone=10) def test_delete_conntrack_from_delete_port_no_ct_zone(self): self._test_delete_conntrack_from_delete_port(ct_zone=None) def _test_delete_conntrack_from_delete_port(self, ct_zone): port = self._fake_port() port['security_groups'] = ['fake_sg_id'] self.firewall.filtered_ports = {'tapfake_dev': port} self.firewall.devices_with_updated_sg_members['fake_sg_id2' ] = ['tapfake_dev'] new_port = copy.deepcopy(port) new_port['security_groups'] = ['fake_sg_id2'] new_port['device'] = ['tapfake_dev2'] new_port['fixed_ips'] = ['10.0.0.2', 'fe80::2'] self.firewall.sg_members['fake_sg_id2'] = {'IPv4': ['10.0.0.2'], 'IPv6': ['fe80::2']} mock.patch.object(self.firewall.ipconntrack, 'get_device_zone', return_value=ct_zone).start() self.firewall.remove_port_filter(port) if not ct_zone: self.assertFalse(self.utils_exec.called) return # process conntrack updates in the queue while not self.firewall.ipconntrack._queue.empty(): self.firewall.ipconntrack._process_queue() calls = self._get_expected_conntrack_calls( [('ipv4', '10.0.0.1'), ('ipv6', 'fe80::1')], ct_zone) self.utils_exec.assert_has_calls(calls) def test_remove_unknown_port(self): port = self._fake_port() self.firewall.remove_port_filter(port) # checking no exception occurs self.assertFalse(self.v4filter_inst.called) def test_defer_apply(self): with self.firewall.defer_apply(): pass self.iptables_inst.assert_has_calls([mock.call.defer_apply_on(), mock.call.defer_apply_off()]) def test_filter_defer_with_exception(self): try: with self.firewall.defer_apply(): raise Exception("same exception") except Exception: pass self.iptables_inst.assert_has_calls([mock.call.defer_apply_on(), mock.call.defer_apply_off()]) def _mock_chain_applies(self): class CopyingMock(mock.MagicMock): """Copies arguments so mutable arguments can be asserted on. Copied verbatim from unittest.mock documentation. """ def __call__(self, *args, **kwargs): args = copy.deepcopy(args) kwargs = copy.deepcopy(kwargs) return super(CopyingMock, self).__call__(*args, **kwargs) # Need to use CopyingMock because _{setup,remove}_chains_apply are # usually called with that's modified between calls (i.e., # self.firewall.filtered_ports). chain_applies = CopyingMock() self.firewall._setup_chains_apply = chain_applies.setup self.firewall._remove_chains_apply = chain_applies.remove return chain_applies def test_mock_chain_applies(self): chain_applies = self._mock_chain_applies() port_prepare = {'device': 'd1', 'mac_address': 'prepare', 'network_id': 'fake_net'} port_update = {'device': 'd1', 'mac_address': 'update', 'network_id': 'fake_net'} self.firewall.prepare_port_filter(port_prepare) self.firewall.update_port_filter(port_update) self.firewall.remove_port_filter(port_update) chain_applies.assert_has_calls([ mock.call.setup({'d1': port_prepare}, {}), mock.call.remove({'d1': port_prepare}, {}), mock.call.setup({'d1': port_update}, {}), mock.call.remove({'d1': port_update}, {}), mock.call.setup({}, {})]) def test_defer_chain_apply_need_pre_defer_copy(self): chain_applies = self._mock_chain_applies() port = self._fake_port() device2port = {port['device']: port} self.firewall.prepare_port_filter(port) with self.firewall.defer_apply(): self.firewall.remove_port_filter(port) chain_applies.assert_has_calls([mock.call.setup(device2port, {}), mock.call.remove(device2port, {}), mock.call.setup({}, {})]) def test_defer_chain_apply_coalesce_simple(self): chain_applies = self._mock_chain_applies() port = self._fake_port() with self.firewall.defer_apply(): self.firewall.prepare_port_filter(port) self.firewall.update_port_filter(port) self.firewall.remove_port_filter(port) chain_applies.assert_has_calls([mock.call.remove({}, {}), mock.call.setup({}, {})]) def test_defer_chain_apply_coalesce_multiple_ports(self): chain_applies = self._mock_chain_applies() port1 = {'device': 'd1', 'mac_address': 'mac1', 'network_id': 'net1'} port2 = {'device': 'd2', 'mac_address': 'mac2', 'network_id': 'net1'} device2port = {'d1': port1, 'd2': port2} with self.firewall.defer_apply(): self.firewall.prepare_port_filter(port1) self.firewall.prepare_port_filter(port2) chain_applies.assert_has_calls([mock.call.remove({}, {}), mock.call.setup(device2port, {})]) def test_ip_spoofing_filter_with_multiple_ips(self): port = {'device': 'tapfake_dev', 'mac_address': 'ff:ff:ff:ff:ff:ff', 'network_id': 'fake_net', 'fixed_ips': ['10.0.0.1', 'fe80::1', '10.0.0.2']} self.firewall.prepare_port_filter(port) calls = [mock.call.add_chain('sg-fallback'), mock.call.add_rule( 'sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP), mock.call.add_chain('sg-chain'), mock.call.add_rule('PREROUTING', mock.ANY, # zone set comment=None), mock.call.add_rule('PREROUTING', mock.ANY, # zone set comment=None), mock.call.add_rule('PREROUTING', mock.ANY, # zone set comment=None), mock.call.add_chain('ifake_dev'), mock.call.add_rule('FORWARD', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j $sg-chain', top=True, comment=ic.VM_INT_SG), mock.call.add_rule('sg-chain', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j $ifake_dev', top=False, comment=ic.SG_TO_VM_SG), mock.call.add_rule( 'ifake_dev', '-m state --state RELATED,ESTABLISHED -j RETURN', top=False, comment=None), mock.call.add_rule( 'ifake_dev', '-m state --state INVALID -j DROP', top=False, comment=None), mock.call.add_rule('ifake_dev', '-j $sg-fallback', top=False, comment=None), mock.call.add_chain('ofake_dev'), mock.call.add_rule('FORWARD', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $sg-chain', top=True, comment=ic.VM_INT_SG), mock.call.add_rule('sg-chain', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $ofake_dev', top=False, comment=ic.SG_TO_VM_SG), mock.call.add_rule('INPUT', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $ofake_dev', top=False, comment=ic.INPUT_TO_SG), mock.call.add_chain('sfake_dev'), mock.call.add_rule( 'sfake_dev', '-s 10.0.0.1/32 -m mac --mac-source FF:FF:FF:FF:FF:FF ' '-j RETURN', comment=ic.PAIR_ALLOW), mock.call.add_rule( 'sfake_dev', '-s 10.0.0.2/32 -m mac --mac-source FF:FF:FF:FF:FF:FF ' '-j RETURN', comment=ic.PAIR_ALLOW), mock.call.add_rule( 'sfake_dev', '-j DROP', comment=ic.PAIR_DROP), mock.call.add_rule( 'ofake_dev', '-s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ' '--sport 68 --dport 67 -j RETURN', top=False, comment=None), mock.call.add_rule('ofake_dev', '-j $sfake_dev', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 68 --dport 67 -j RETURN', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 67 --dport 68 -j DROP', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-m state --state RELATED,ESTABLISHED -j RETURN', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-m state --state INVALID -j DROP', top=False, comment=None), mock.call.add_rule('ofake_dev', '-j $sg-fallback', top=False, comment=None), mock.call.add_rule('sg-chain', '-j ACCEPT')] self.v4filter_inst.assert_has_calls(calls) def test_ip_spoofing_no_fixed_ips(self): port = {'device': 'tapfake_dev', 'mac_address': 'ff:ff:ff:ff:ff:ff', 'network_id': 'fake_net', 'fixed_ips': []} self.firewall.prepare_port_filter(port) calls = [mock.call.add_chain('sg-fallback'), mock.call.add_rule( 'sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP), mock.call.add_chain('sg-chain'), mock.call.add_rule('PREROUTING', mock.ANY, # zone set comment=None), mock.call.add_rule('PREROUTING', mock.ANY, # zone set comment=None), mock.call.add_rule('PREROUTING', mock.ANY, # zone set comment=None), mock.call.add_chain('ifake_dev'), mock.call.add_rule('FORWARD', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j $sg-chain', top=True, comment=ic.VM_INT_SG), mock.call.add_rule('sg-chain', '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged -j $ifake_dev', top=False, comment=ic.SG_TO_VM_SG), mock.call.add_rule( 'ifake_dev', '-m state --state RELATED,ESTABLISHED -j RETURN', top=False, comment=None), mock.call.add_rule( 'ifake_dev', '-m state --state INVALID -j DROP', top=False, comment=None), mock.call.add_rule('ifake_dev', '-j $sg-fallback', top=False, comment=None), mock.call.add_chain('ofake_dev'), mock.call.add_rule('FORWARD', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $sg-chain', top=True, comment=ic.VM_INT_SG), mock.call.add_rule('sg-chain', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $ofake_dev', top=False, comment=ic.SG_TO_VM_SG), mock.call.add_rule('INPUT', '-m physdev --physdev-in tapfake_dev ' '--physdev-is-bridged -j $ofake_dev', top=False, comment=ic.INPUT_TO_SG), mock.call.add_chain('sfake_dev'), mock.call.add_rule( 'sfake_dev', '-m mac --mac-source FF:FF:FF:FF:FF:FF -j RETURN', comment=ic.PAIR_ALLOW), mock.call.add_rule( 'sfake_dev', '-j DROP', comment=ic.PAIR_DROP), mock.call.add_rule( 'ofake_dev', '-s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp ' '--sport 68 --dport 67 -j RETURN', top=False, comment=None), mock.call.add_rule('ofake_dev', '-j $sfake_dev', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 68 --dport 67 -j RETURN', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-p udp -m udp --sport 67 --dport 68 -j DROP', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-m state --state RELATED,ESTABLISHED -j RETURN', top=False, comment=None), mock.call.add_rule( 'ofake_dev', '-m state --state INVALID -j DROP', top=False, comment=None), mock.call.add_rule('ofake_dev', '-j $sg-fallback', top=False, comment=None), mock.call.add_rule('sg-chain', '-j ACCEPT')] self.v4filter_inst.assert_has_calls(calls) class IptablesFirewallEnhancedIpsetTestCase(BaseIptablesFirewallTestCase): def setUp(self): super(IptablesFirewallEnhancedIpsetTestCase, self).setUp() self.firewall.ipset = mock.Mock() self.firewall.ipset.get_name.side_effect = ( ipset_manager.IpsetManager.get_name) self.firewall.ipset.set_name_exists.return_value = True self.firewall.ipset.set_members = mock.Mock(return_value=([], [])) def _fake_port(self, sg_id=FAKE_SGID): return {'device': 'tapfake_dev', 'mac_address': 'ff:ff:ff:ff:ff:ff', 'network_id': 'fake_net', 'fixed_ips': [FAKE_IP['IPv4'], FAKE_IP['IPv6']], 'security_groups': [sg_id], 'security_group_source_groups': [sg_id]} def _fake_sg_rule_for_ethertype(self, ethertype, remote_group): return {'direction': 'ingress', 'remote_group_id': remote_group, 'ethertype': ethertype} def _fake_sg_rules(self, sg_id=FAKE_SGID, remote_groups=None): remote_groups = remote_groups or {_IPv4: [FAKE_SGID], _IPv6: [FAKE_SGID]} rules = [] for ip_version, remote_group_list in remote_groups.items(): for remote_group in remote_group_list: rules.append(self._fake_sg_rule_for_ethertype(ip_version, remote_group)) return {sg_id: rules} def _fake_sg_members(self, sg_ids=None): return {sg_id: copy.copy(FAKE_IP) for sg_id in (sg_ids or [FAKE_SGID])} def test_update_security_group_members(self): sg_members = {'IPv4': ['10.0.0.1', '10.0.0.2'], 'IPv6': ['fe80::1']} self.firewall.update_security_group_members('fake_sgid', sg_members) calls = [ mock.call.set_members('fake_sgid', 'IPv4', ['10.0.0.1', '10.0.0.2']), mock.call.set_members('fake_sgid', 'IPv6', ['fe80::1']) ] self.firewall.ipset.assert_has_calls(calls, any_order=True) def _setup_fake_firewall_members_and_rules(self, firewall): firewall.sg_rules = self._fake_sg_rules() firewall.pre_sg_rules = self._fake_sg_rules() firewall.sg_members = self._fake_sg_members() firewall.pre_sg_members = firewall.sg_members def _prepare_rules_and_members_for_removal(self): self._setup_fake_firewall_members_and_rules(self.firewall) self.firewall.pre_sg_members[OTHER_SGID] = ( self.firewall.pre_sg_members[FAKE_SGID]) def test_determine_remote_sgs_to_remove(self): self._prepare_rules_and_members_for_removal() ports = [self._fake_port()] self.assertEqual( {_IPv4: set([OTHER_SGID]), _IPv6: set([OTHER_SGID])}, self.firewall._determine_remote_sgs_to_remove(ports)) def test_determine_remote_sgs_to_remove_ipv6_unreferenced(self): self._prepare_rules_and_members_for_removal() ports = [self._fake_port()] self.firewall.sg_rules = self._fake_sg_rules( remote_groups={_IPv4: [OTHER_SGID, FAKE_SGID], _IPv6: [FAKE_SGID]}) self.assertEqual( {_IPv4: set(), _IPv6: set([OTHER_SGID])}, self.firewall._determine_remote_sgs_to_remove(ports)) def test_get_remote_sg_ids_by_ipversion(self): self.firewall.sg_rules = self._fake_sg_rules( remote_groups={_IPv4: [FAKE_SGID], _IPv6: [OTHER_SGID]}) ports = [self._fake_port()] self.assertEqual( {_IPv4: set([FAKE_SGID]), _IPv6: set([OTHER_SGID])}, self.firewall._get_remote_sg_ids_sets_by_ipversion(ports)) def test_get_remote_sg_ids(self): self.firewall.sg_rules = self._fake_sg_rules( remote_groups={_IPv4: [FAKE_SGID, FAKE_SGID, FAKE_SGID], _IPv6: [OTHER_SGID, OTHER_SGID, OTHER_SGID]}) port = self._fake_port() self.assertEqual( {_IPv4: set([FAKE_SGID]), _IPv6: set([OTHER_SGID])}, self.firewall._get_remote_sg_ids(port)) def test_determine_sg_rules_to_remove(self): self.firewall.pre_sg_rules = self._fake_sg_rules(sg_id=OTHER_SGID) ports = [self._fake_port()] self.assertEqual(set([OTHER_SGID]), self.firewall._determine_sg_rules_to_remove(ports)) def test_get_sg_ids_set_for_ports(self): sg_ids = set([FAKE_SGID, OTHER_SGID]) ports = [self._fake_port(sg_id) for sg_id in sg_ids] self.assertEqual(sg_ids, self.firewall._get_sg_ids_set_for_ports(ports)) def test_remove_sg_members(self): self.firewall.sg_members = self._fake_sg_members([FAKE_SGID, OTHER_SGID]) remote_sgs_to_remove = {_IPv4: set([FAKE_SGID]), _IPv6: set([FAKE_SGID, OTHER_SGID])} self.firewall._remove_sg_members(remote_sgs_to_remove) self.assertIn(OTHER_SGID, self.firewall.sg_members) self.assertNotIn(FAKE_SGID, self.firewall.sg_members) def test_remove_unused_security_group_info_clears_unused_rules(self): self._setup_fake_firewall_members_and_rules(self.firewall) self.firewall.prepare_port_filter(self._fake_port()) # create another SG which won't be referenced by any filtered port fake_sg_rules = self.firewall.sg_rules['fake_sgid'] self.firewall.pre_sg_rules[OTHER_SGID] = fake_sg_rules self.firewall.sg_rules[OTHER_SGID] = fake_sg_rules # call the cleanup function, and check the unused sg_rules are out self.firewall._remove_unused_security_group_info() self.assertNotIn(OTHER_SGID, self.firewall.sg_rules) def test_remove_unused_security_group_info(self): self.firewall.sg_members = {OTHER_SGID: {_IPv4: [], _IPv6: []}} self.firewall.pre_sg_members = self.firewall.sg_members self.firewall.sg_rules = self._fake_sg_rules( remote_groups={_IPv4: [FAKE_SGID], _IPv6: [FAKE_SGID]}) self.firewall.pre_sg_rules = self.firewall.sg_rules port = self._fake_port() self.firewall.filtered_ports['tapfake_dev'] = port self.firewall._remove_unused_security_group_info() self.assertNotIn(OTHER_SGID, self.firewall.sg_members) def test_not_remove_used_security_group_info(self): self.firewall.sg_members = {OTHER_SGID: {_IPv4: [], _IPv6: []}} self.firewall.pre_sg_members = self.firewall.sg_members self.firewall.sg_rules = self._fake_sg_rules( remote_groups={_IPv4: [OTHER_SGID], _IPv6: [OTHER_SGID]}) self.firewall.pre_sg_rules = self.firewall.sg_rules port = self._fake_port() self.firewall.filtered_ports['tapfake_dev'] = port self.firewall._remove_unused_security_group_info() self.assertIn(OTHER_SGID, self.firewall.sg_members) def test_remove_all_unused_info(self): self._setup_fake_firewall_members_and_rules(self.firewall) self.firewall.filtered_ports = {} self.firewall._remove_unused_security_group_info() self.assertFalse(self.firewall.sg_members) self.assertFalse(self.firewall.sg_rules) def test_single_fallback_accept_rule(self): p1, p2 = self._fake_port(), self._fake_port() self.firewall._setup_chains_apply(dict(p1=p1, p2=p2), {}) v4_adds = self.firewall.iptables.ipv4['filter'].add_rule.mock_calls v6_adds = self.firewall.iptables.ipv6['filter'].add_rule.mock_calls sg_chain_v4_accept = [call for call in v4_adds if call == mock.call('sg-chain', '-j ACCEPT')] sg_chain_v6_accept = [call for call in v6_adds if call == mock.call('sg-chain', '-j ACCEPT')] self.assertEqual(1, len(sg_chain_v4_accept)) self.assertEqual(1, len(sg_chain_v6_accept)) def test_remove_port_filter_with_destroy_ipset_chain(self): self.firewall.sg_rules = self._fake_sg_rules() port = self._fake_port() self.firewall.pre_sg_members = {'fake_sgid': { 'IPv4': [], 'IPv6': []}} sg_members = {'IPv4': ['10.0.0.1'], 'IPv6': ['fe80::1']} self.firewall.update_security_group_members('fake_sgid', sg_members) self.firewall.prepare_port_filter(port) self.firewall.filter_defer_apply_on() self.firewall.sg_members = {'fake_sgid': { 'IPv4': [], 'IPv6': []}} self.firewall.pre_sg_members = {'fake_sgid': { 'IPv4': ['10.0.0.1'], 'IPv6': ['fe80::1']}} self.firewall.remove_port_filter(port) self.firewall.filter_defer_apply_off() calls = [ mock.call.set_members('fake_sgid', 'IPv4', ['10.0.0.1']), mock.call.set_members('fake_sgid', 'IPv6', ['fe80::1']), mock.call.get_name('fake_sgid', 'IPv4'), mock.call.set_name_exists('NIPv4fake_sgid'), mock.call.get_name('fake_sgid', 'IPv6'), mock.call.set_name_exists('NIPv6fake_sgid'), mock.call.destroy('fake_sgid', 'IPv4'), mock.call.destroy('fake_sgid', 'IPv6')] self.firewall.ipset.assert_has_calls(calls, any_order=True) def test_filter_defer_apply_off_with_sg_only_ipv6_rule(self): self.firewall.sg_rules = self._fake_sg_rules() self.firewall.pre_sg_rules = self._fake_sg_rules() self.firewall.ipset_chains = {'IPv4fake_sgid': ['10.0.0.2'], 'IPv6fake_sgid': ['fe80::1']} self.firewall.sg_members = {'fake_sgid': { 'IPv4': ['10.0.0.2'], 'IPv6': ['fe80::1']}} self.firewall.pre_sg_members = {'fake_sgid': { 'IPv4': ['10.0.0.2'], 'IPv6': ['fe80::1']}} self.firewall.sg_rules['fake_sgid'].remove( {'direction': 'ingress', 'remote_group_id': 'fake_sgid', 'ethertype': 'IPv4'}) self.firewall.sg_rules.update() self.firewall._defer_apply = True port = self._fake_port() self.firewall.filtered_ports['tapfake_dev'] = port self.firewall._pre_defer_filtered_ports = {} self.firewall._pre_defer_unfiltered_ports = {} self.firewall.filter_defer_apply_off() calls = [mock.call.destroy('fake_sgid', 'IPv4')] self.firewall.ipset.assert_has_calls(calls, True) def test_sg_rule_expansion_with_remote_ips(self): other_ips = ['10.0.0.2', '10.0.0.3', '10.0.0.4'] self.firewall.sg_members = {'fake_sgid': { 'IPv4': [FAKE_IP['IPv4']] + other_ips, 'IPv6': [FAKE_IP['IPv6']]}} port = self._fake_port() rule = self._fake_sg_rule_for_ethertype(_IPv4, FAKE_SGID) rules = self.firewall._expand_sg_rule_with_remote_ips( rule, port, 'ingress') self.assertEqual(list(rules), [dict(list(rule.items()) + [('source_ip_prefix', '%s/32' % ip)]) for ip in other_ips]) def test_build_ipv4v6_mac_ip_list(self): mac_oth = 'ffff-ff0f-ffff' mac_unix = 'FF:FF:FF:0F:FF:FF' ipv4 = FAKE_IP['IPv4'] ipv6 = FAKE_IP['IPv6'] fake_ipv4_pair = [] fake_ipv4_pair.append((mac_unix, ipv4)) fake_ipv6_pair = [] fake_ipv6_pair.append((mac_unix, ipv6)) fake_ipv6_pair.append((mac_unix, 'fe80::fdff:ffff:fe0f:ffff')) mac_ipv4_pairs = [] mac_ipv6_pairs = [] self.firewall._build_ipv4v6_mac_ip_list(mac_oth, ipv4, mac_ipv4_pairs, mac_ipv6_pairs) self.assertEqual(fake_ipv4_pair, mac_ipv4_pairs) self.firewall._build_ipv4v6_mac_ip_list(mac_oth, ipv6, mac_ipv4_pairs, mac_ipv6_pairs) self.assertEqual(fake_ipv6_pair, mac_ipv6_pairs) # ensure that LLA is not added again for another v6 addr ipv62 = 'fe81::1' self.firewall._build_ipv4v6_mac_ip_list(mac_oth, ipv62, mac_ipv4_pairs, mac_ipv6_pairs) fake_ipv6_pair.append((mac_unix, ipv62)) self.assertEqual(fake_ipv6_pair, mac_ipv6_pairs) class OVSHybridIptablesFirewallTestCase(BaseIptablesFirewallTestCase): def test__populate_initial_zone_map(self): self.assertEqual(self._dev_zone_map, self.firewall.ipconntrack._device_zone_map) def test__generate_device_zone(self): # initial data has 4097, 4098, and 4105 in use. # we fill from top up first. self.assertEqual(4106, self.firewall.ipconntrack._generate_device_zone('test')) # once it's maxed out, it scans for gaps self.firewall.ipconntrack._device_zone_map['someport'] = ( ip_conntrack.MAX_CONNTRACK_ZONES) for i in range(4099, 4105): self.assertEqual(i, self.firewall.ipconntrack._generate_device_zone(i)) # 4105 and 4106 are taken so next should be 4107 self.assertEqual(4107, self.firewall.ipconntrack._generate_device_zone('p11')) # take out zone 4097 and make sure it's selected self.firewall.ipconntrack._device_zone_map.pop('e804433b-61') self.assertEqual(4097, self.firewall.ipconntrack._generate_device_zone('p1')) # fill it up and then make sure an extra throws an error for i in range(ip_conntrack.ZONE_START, ip_conntrack.MAX_CONNTRACK_ZONES): self.firewall.ipconntrack._device_zone_map['dev-%s' % i] = i with testtools.ExpectedException(n_exc.CTZoneExhaustedError): self.firewall.ipconntrack._find_open_zone() # with it full, try again, this should trigger a cleanup # and return 4097 self.assertEqual(ip_conntrack.ZONE_START, self.firewall.ipconntrack._generate_device_zone('p12')) self.assertEqual({'p12': ip_conntrack.ZONE_START}, self.firewall.ipconntrack._device_zone_map) def test_get_device_zone(self): dev = {'device': 'tap1234', 'network_id': '12345678901234567'} # initial data has 4097, 4098, and 4105 in use. self.assertEqual(4106, self.firewall.ipconntrack.get_device_zone(dev)) # should have been truncated to 11 chars self._dev_zone_map.update({'12345678901': 4106}) self.assertEqual(self._dev_zone_map, self.firewall.ipconntrack._device_zone_map) def test_multiple_firewall_with_common_conntrack(self): self.firewall1 = iptables_firewall.OVSHybridIptablesFirewallDriver() self.firewall2 = iptables_firewall.OVSHybridIptablesFirewallDriver() self.assertEqual(id(self.firewall1.ipconntrack), id(self.firewall2.ipconntrack)) neutron-12.1.1/neutron/tests/unit/agent/linux/test_bridge_lib.py0000664000175000017500000001056613553660047025074 0ustar zuulzuul00000000000000# Copyright 2015 Intel Corporation. # Copyright 2015 Isaku Yamahata # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.agent.linux import bridge_lib from neutron.tests import base class BridgeLibTest(base.BaseTestCase): """A test suite to exercise the bridge libraries """ _NAMESPACE = 'test-namespace' _BR_NAME = 'test-br' _IF_NAME = 'test-if' def setUp(self): super(BridgeLibTest, self).setUp() mock.patch( 'neutron.common.ipv6_utils.is_enabled_and_bind_by_default', return_value=True).start() ip_wrapper = mock.patch('neutron.agent.linux.ip_lib.IPWrapper').start() self.execute = ip_wrapper.return_value.netns.execute def _verify_bridge_mock(self, cmd): self.execute.assert_called_once_with(cmd, run_as_root=True) self.execute.reset_mock() def _verify_bridge_sysctl_mock(self, cmd): self.execute.assert_called_once_with(cmd, run_as_root=True, log_fail_as_error=True) self.execute.reset_mock() def test_is_bridged_interface(self): exists = lambda path: path == "/sys/class/net/tapOK/brport" with mock.patch('os.path.exists', side_effect=exists): self.assertTrue(bridge_lib.is_bridged_interface("tapOK")) self.assertFalse(bridge_lib.is_bridged_interface("tapKO")) def test_get_interface_bridge(self): with mock.patch('os.readlink', side_effect=["prefix/br0", OSError()]): br = bridge_lib.BridgeDevice.get_interface_bridge('tap0') self.assertIsInstance(br, bridge_lib.BridgeDevice) self.assertEqual("br0", br.name) br = bridge_lib.BridgeDevice.get_interface_bridge('tap0') self.assertIsNone(br) def _test_br(self, namespace=None): br = bridge_lib.BridgeDevice.addbr(self._BR_NAME, namespace) self.assertEqual(namespace, br.namespace) self._verify_bridge_mock(['brctl', 'addbr', self._BR_NAME]) br.setfd(0) self._verify_bridge_mock(['brctl', 'setfd', self._BR_NAME, '0']) br.disable_stp() self._verify_bridge_mock(['brctl', 'stp', self._BR_NAME, 'off']) br.disable_ipv6() cmd = 'net.ipv6.conf.%s.disable_ipv6=1' % self._BR_NAME self._verify_bridge_sysctl_mock(['sysctl', '-w', cmd]) br.addif(self._IF_NAME) self._verify_bridge_mock( ['brctl', 'addif', self._BR_NAME, self._IF_NAME]) br.delif(self._IF_NAME) self._verify_bridge_mock( ['brctl', 'delif', self._BR_NAME, self._IF_NAME]) br.delbr() self._verify_bridge_mock(['brctl', 'delbr', self._BR_NAME]) def test_addbr_with_namespace(self): self._test_br(self._NAMESPACE) def test_addbr_without_namespace(self): self._test_br() def test_addbr_exists(self): self.execute.side_effect = RuntimeError() with mock.patch.object(bridge_lib.BridgeDevice, 'exists', return_value=True): bridge_lib.BridgeDevice.addbr(self._BR_NAME) bridge_lib.BridgeDevice.addbr(self._BR_NAME) def test_owns_interface(self): br = bridge_lib.BridgeDevice('br-int') exists = lambda path: path == "/sys/class/net/br-int/brif/abc" with mock.patch('os.path.exists', side_effect=exists): self.assertTrue(br.owns_interface("abc")) self.assertFalse(br.owns_interface("def")) def test_get_interfaces(self): br = bridge_lib.BridgeDevice('br-int') interfaces = ["tap1", "tap2"] with mock.patch('os.listdir', side_effect=[interfaces, OSError()]): self.assertEqual(interfaces, br.get_interfaces()) self.assertEqual([], br.get_interfaces()) neutron-12.1.1/neutron/tests/unit/agent/linux/test_ip_link_support.py0000664000175000017500000001630413553660046026226 0ustar zuulzuul00000000000000# Copyright 2014 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron.agent.linux import ip_link_support as ip_link from neutron.tests import base class TestIpLinkSupport(base.BaseTestCase): IP_LINK_HELP = """Usage: ip link add [link DEV] [ name ] NAME [ txqueuelen PACKETS ] [ address LLADDR ] [ broadcast LLADDR ] [ mtu MTU ] [index IDX ] [ numtxqueues QUEUE_COUNT ] [ numrxqueues QUEUE_COUNT ] type TYPE [ ARGS ] ip link delete DEV type TYPE [ ARGS ] ip link set { dev DEVICE | group DEVGROUP } [ { up | down } ] [ arp { on | off } ] [ dynamic { on | off } ] [ multicast { on | off } ] [ allmulticast { on | off } ] [ promisc { on | off } ] [ trailers { on | off } ] [ txqueuelen PACKETS ] [ name NEWNAME ] [ address LLADDR ] [ broadcast LLADDR ] [ mtu MTU ] [ netns PID ] [ netns NAME ] [ alias NAME ] [ vf NUM [ mac LLADDR ] [ vlan VLANID [ qos VLAN-QOS ] ] [ rate TXRATE ] ] [ spoofchk { on | off} ] ] [ state { auto | enable | disable} ] ] [ master DEVICE ] [ nomaster ] ip link show [ DEVICE | group GROUP ] [up] TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap | can | bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | gre | gretap | ip6gre | ip6gretap | vti } """ IP_LINK_HELP_NO_STATE = """Usage: ip link add link DEV [ name ] NAME [ txqueuelen PACKETS ] [ address LLADDR ] [ broadcast LLADDR ] [ mtu MTU ] type TYPE [ ARGS ] ip link delete DEV type TYPE [ ARGS ] ip link set DEVICE [ { up | down } ] [ arp { on | off } ] [ dynamic { on | off } ] [ multicast { on | off } ] [ allmulticast { on | off } ] [ promisc { on | off } ] [ trailers { on | off } ] [ txqueuelen PACKETS ] [ name NEWNAME ] [ address LLADDR ] [ broadcast LLADDR ] [ mtu MTU ] [ netns PID ] [ alias NAME ] [ vf NUM [ mac LLADDR ] [ vlan VLANID [ qos VLAN-QOS ] ] [ rate TXRATE ] ] ip link show [ DEVICE ] TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can } """ IP_LINK_HELP_NO_SPOOFCHK = IP_LINK_HELP_NO_STATE IP_LINK_HELP_NO_VF = """Usage: ip link set DEVICE { up | down | arp { on | off } | dynamic { on | off } | multicast { on | off } | allmulticast { on | off } | promisc { on | off } | trailers { on | off } | txqueuelen PACKETS | name NEWNAME | address LLADDR | broadcast LLADDR | mtu MTU } ip link show [ DEVICE ] """ def _test_capability(self, capability, subcapability=None, expected=True, stdout="", stderr=""): with mock.patch("neutron.agent.linux.utils.execute") as mock_exec: mock_exec.return_value = (stdout, stderr) vf_section = ip_link.IpLinkSupport.get_vf_mgmt_section() capable = ip_link.IpLinkSupport.vf_mgmt_capability_supported( vf_section, capability, subcapability) self.assertEqual(expected, capable) mock_exec.assert_called_once_with(['ip', 'link', 'help'], check_exit_code=False, return_stderr=True, log_fail_as_error=False) def test_vf_mgmt(self): self._test_capability( ip_link.IpLinkConstants.IP_LINK_CAPABILITY_STATE, stderr=self.IP_LINK_HELP) def test_execute_with_stdout(self): self._test_capability( ip_link.IpLinkConstants.IP_LINK_CAPABILITY_STATE, stdout=self.IP_LINK_HELP) def test_vf_mgmt_no_state(self): self._test_capability( ip_link.IpLinkConstants.IP_LINK_CAPABILITY_STATE, expected=False, stderr=self.IP_LINK_HELP_NO_STATE) def test_vf_mgmt_no_spoofchk(self): self._test_capability( ip_link.IpLinkConstants.IP_LINK_CAPABILITY_SPOOFCHK, expected=False, stderr=self.IP_LINK_HELP_NO_SPOOFCHK) def test_vf_mgmt_no_vf(self): self._test_capability( ip_link.IpLinkConstants.IP_LINK_CAPABILITY_STATE, expected=False, stderr=self.IP_LINK_HELP_NO_VF) def test_vf_mgmt_unknown_capability(self): self._test_capability( "state1", expected=False, stderr=self.IP_LINK_HELP) def test_vf_mgmt_sub_capability(self): self._test_capability( ip_link.IpLinkConstants.IP_LINK_CAPABILITY_VLAN, ip_link.IpLinkConstants.IP_LINK_SUB_CAPABILITY_QOS, stderr=self.IP_LINK_HELP) def test_vf_mgmt_sub_capability_mismatch(self): self._test_capability( ip_link.IpLinkConstants.IP_LINK_CAPABILITY_STATE, ip_link.IpLinkConstants.IP_LINK_SUB_CAPABILITY_QOS, expected=False, stderr=self.IP_LINK_HELP) def test_vf_mgmt_sub_capability_invalid(self): self._test_capability( ip_link.IpLinkConstants.IP_LINK_CAPABILITY_VLAN, "qos1", expected=False, stderr=self.IP_LINK_HELP) def test_vf_mgmt_error(self): with mock.patch("neutron.agent.linux.utils.execute") as mock_exec: mock_exec.side_effect = Exception() self.assertRaises( ip_link.UnsupportedIpLinkCommand, ip_link.IpLinkSupport.get_vf_mgmt_section) neutron-12.1.1/neutron/tests/unit/agent/linux/test_ip_lib.py0000664000175000017500000022300313553660047024240 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import errno import socket import mock import netaddr from neutron_lib import exceptions import pyroute2 from pyroute2.netlink.rtnl import ndmsg from pyroute2 import NetlinkError import testtools from neutron.agent.common import utils # noqa from neutron.agent.linux import ip_lib from neutron.common import exceptions as n_exc from neutron import privileged from neutron.privileged.agent.linux import ip_lib as priv_lib from neutron.tests import base NETNS_SAMPLE = [ '12345678-1234-5678-abcd-1234567890ab', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'cccccccc-cccc-cccc-cccc-cccccccccccc'] LINK_SAMPLE = [ '1: lo: mtu 16436 qdisc noqueue state UNKNOWN \\' 'link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0', '2: eth0: mtu 1500 qdisc mq state UP ' 'qlen 1000\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff' '\ alias openvswitch', '3: br-int: mtu 1500 qdisc noop state DOWN ' '\ link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff promiscuity 0', '4: gw-ddc717df-49: mtu 1500 qdisc noop ' 'state DOWN \ link/ether fe:dc:ba:fe:dc:ba brd ff:ff:ff:ff:ff:ff ' 'promiscuity 0', '5: foo:foo: mtu 1500 qdisc mq state ' 'UP qlen 1000\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff ' 'promiscuity 0', '6: foo@foo: mtu 1500 qdisc mq state ' 'UP qlen 1000\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff ' 'promiscuity 0', '7: foo:foo@foo: mtu 1500 qdisc mq ' 'state UP qlen 1000' '\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff promiscuity 0', '8: foo@foo:foo: mtu 1500 qdisc mq ' 'state UP qlen 1000' '\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff promiscuity 0', '9: bar.9@eth0: mtu 1500 qdisc ' ' noqueue master brq0b24798c-07 state UP mode DEFAULT' '\ link/ether ab:04:49:b6:ab:a0 brd ff:ff:ff:ff:ff:ff promiscuity 0' '\ vlan protocol 802.1q id 9 ', '10: bar@eth0: mtu 1500 qdisc ' ' noqueue master brq0b24798c-07 state UP mode DEFAULT' '\ link/ether ab:04:49:b6:ab:a0 brd ff:ff:ff:ff:ff:ff promiscuity 0' '\ vlan protocol 802.1Q id 10 ', '11: bar:bar@eth0: mtu 1500 qdisc mq ' 'state UP qlen 1000' '\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff promiscuity 0' '\ vlan id 11 ', '12: bar@bar@eth0: mtu 1500 qdisc mq ' 'state UP qlen 1000' '\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff promiscuity 0' '\ vlan id 12 ', '13: bar:bar@bar@eth0: mtu 1500 ' 'qdisc mq state UP qlen 1000' '\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff promiscuity 0' '\ vlan protocol 802.1q id 13 ', '14: bar@bar:bar@eth0: mtu 1500 ' 'qdisc mq state UP qlen 1000' '\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff promiscuity 0' '\ vlan protocol 802.1Q id 14 '] ADDR_SAMPLE = (""" 2: eth0: mtu 1500 qdisc mq state UP qlen 1000 link/ether dd:cc:aa:b9:76:ce brd ff:ff:ff:ff:ff:ff inet 172.16.77.240/24 brd 172.16.77.255 scope global eth0 inet6 2001:470:9:1224:5595:dd51:6ba2:e788/64 scope global temporary dynamic valid_lft 14187sec preferred_lft 3387sec inet6 fe80::3023:39ff:febc:22ae/64 scope link tentative valid_lft forever preferred_lft forever inet6 fe80::3023:39ff:febc:22af/64 scope link tentative dadfailed valid_lft forever preferred_lft forever inet6 2001:470:9:1224:fd91:272:581e:3a32/64 scope global temporary """ """deprecated dynamic valid_lft 14187sec preferred_lft 0sec inet6 2001:470:9:1224:4508:b885:5fb:740b/64 scope global temporary """ """deprecated dynamic valid_lft 14187sec preferred_lft 0sec inet6 2001:470:9:1224:dfcc:aaff:feb9:76ce/64 scope global dynamic valid_lft 14187sec preferred_lft 3387sec inet6 fe80::dfcc:aaff:feb9:76ce/64 scope link valid_lft forever preferred_lft forever """) ADDR_SAMPLE2 = (""" 2: eth0: mtu 1500 qdisc mq state UP qlen 1000 link/ether dd:cc:aa:b9:76:ce brd ff:ff:ff:ff:ff:ff inet 172.16.77.240/24 scope global eth0 inet6 2001:470:9:1224:5595:dd51:6ba2:e788/64 scope global temporary dynamic valid_lft 14187sec preferred_lft 3387sec inet6 fe80::3023:39ff:febc:22ae/64 scope link tentative valid_lft forever preferred_lft forever inet6 fe80::3023:39ff:febc:22af/64 scope link tentative dadfailed valid_lft forever preferred_lft forever inet6 2001:470:9:1224:fd91:272:581e:3a32/64 scope global temporary """ """deprecated dynamic valid_lft 14187sec preferred_lft 0sec inet6 2001:470:9:1224:4508:b885:5fb:740b/64 scope global temporary """ """deprecated dynamic valid_lft 14187sec preferred_lft 0sec inet6 2001:470:9:1224:dfcc:aaff:feb9:76ce/64 scope global dynamic valid_lft 14187sec preferred_lft 3387sec inet6 fe80::dfcc:aaff:feb9:76ce/64 scope link valid_lft forever preferred_lft forever """) ADDR_SAMPLE3 = (""" 2: eth0@NONE: mtu 1500 qdisc mq state UP link/ether dd:cc:aa:b9:76:ce brd ff:ff:ff:ff:ff:ff inet 172.16.77.240/24 brd 172.16.77.255 scope global eth0 """) GATEWAY_SAMPLE1 = (""" default via 10.35.19.254 metric 100 10.35.16.0/22 proto kernel scope link src 10.35.17.97 """) GATEWAY_SAMPLE2 = (""" default via 10.35.19.254 metric 100 """) GATEWAY_SAMPLE3 = (""" 10.35.16.0/22 proto kernel scope link src 10.35.17.97 """) GATEWAY_SAMPLE4 = (""" default via 10.35.19.254 """) GATEWAY_SAMPLE5 = (""" default via 192.168.99.1 proto static """) GATEWAY_SAMPLE6 = (""" default via 192.168.99.1 proto static metric 100 """) GATEWAY_SAMPLE7 = (""" default dev qg-31cd36 metric 1 """) IPv6_GATEWAY_SAMPLE1 = (""" default via 2001:470:9:1224:4508:b885:5fb:740b metric 100 2001:db8::/64 proto kernel scope link src 2001:470:9:1224:dfcc:aaff:feb9:76ce """) IPv6_GATEWAY_SAMPLE2 = (""" default via 2001:470:9:1224:4508:b885:5fb:740b metric 100 """) IPv6_GATEWAY_SAMPLE3 = (""" 2001:db8::/64 proto kernel scope link src 2001:470:9:1224:dfcc:aaff:feb9:76ce """) IPv6_GATEWAY_SAMPLE4 = (""" default via fe80::dfcc:aaff:feb9:76ce """) IPv6_GATEWAY_SAMPLE5 = (""" default via 2001:470:9:1224:4508:b885:5fb:740b metric 1024 """) DEVICE_ROUTE_SAMPLE = ("10.0.0.0/24 scope link src 10.0.0.2") SUBNET_SAMPLE1 = ("10.0.0.0/24 dev qr-23380d11-d2 scope link src 10.0.0.1\n" "10.0.0.0/24 dev tap1d7888a7-10 scope link src 10.0.0.2") SUBNET_SAMPLE2 = ("10.0.0.0/24 dev tap1d7888a7-10 scope link src 10.0.0.2\n" "10.0.0.0/24 dev qr-23380d11-d2 scope link src 10.0.0.1") RULE_V4_SAMPLE = (""" 0: from all lookup local 32766: from all lookup main 32767: from all lookup default 101: from 192.168.45.100 lookup 2 """) RULE_V6_SAMPLE = (""" 0: from all lookup local 32766: from all lookup main 32767: from all lookup default 201: from 2001:db8::1 lookup 3 """) class TestSubProcessBase(base.BaseTestCase): def setUp(self): super(TestSubProcessBase, self).setUp() self.execute_p = mock.patch('neutron.agent.common.utils.execute') self.execute = self.execute_p.start() def test_execute_wrapper(self): base = ip_lib.SubProcessBase() base._execute(['o'], 'link', ('list',), run_as_root=True) self.execute.assert_called_once_with(['ip', '-o', 'link', 'list'], run_as_root=True, log_fail_as_error=True) def test_execute_wrapper_int_options(self): base = ip_lib.SubProcessBase() base._execute([4], 'link', ('list',)) self.execute.assert_called_once_with(['ip', '-4', 'link', 'list'], run_as_root=False, log_fail_as_error=True) def test_execute_wrapper_no_options(self): base = ip_lib.SubProcessBase() base._execute([], 'link', ('list',)) self.execute.assert_called_once_with(['ip', 'link', 'list'], run_as_root=False, log_fail_as_error=True) def test_run_no_namespace(self): base = ip_lib.SubProcessBase() base._run([], 'link', ('list',)) self.execute.assert_called_once_with(['ip', 'link', 'list'], run_as_root=False, log_fail_as_error=True) def test_run_namespace(self): base = ip_lib.SubProcessBase(namespace='ns') base._run([], 'link', ('list',)) self.execute.assert_called_once_with(['ip', 'netns', 'exec', 'ns', 'ip', 'link', 'list'], run_as_root=True, log_fail_as_error=True) def test_as_root_namespace(self): base = ip_lib.SubProcessBase(namespace='ns') base._as_root([], 'link', ('list',)) self.execute.assert_called_once_with(['ip', 'netns', 'exec', 'ns', 'ip', 'link', 'list'], run_as_root=True, log_fail_as_error=True) class TestIpWrapper(base.BaseTestCase): def setUp(self): super(TestIpWrapper, self).setUp() self.execute_p = mock.patch.object(ip_lib.IPWrapper, '_execute') self.execute = self.execute_p.start() @mock.patch('os.path.islink') @mock.patch('os.listdir', return_value=['lo']) def test_get_devices(self, mocked_listdir, mocked_islink): retval = ip_lib.IPWrapper().get_devices() mocked_islink.assert_called_once_with('/sys/class/net/lo') self.assertEqual([], retval) @mock.patch('neutron.agent.common.utils.execute') def test_get_devices_namespaces(self, mocked_execute): fake_str = mock.Mock() fake_str.split.return_value = ['lo'] mocked_execute.return_value = fake_str retval = ip_lib.IPWrapper(namespace='foo').get_devices() mocked_execute.assert_called_once_with( ['ip', 'netns', 'exec', 'foo', 'find', '/sys/class/net', '-maxdepth', '1', '-type', 'l', '-printf', '%f '], run_as_root=True, log_fail_as_error=True) self.assertTrue(fake_str.split.called) self.assertEqual([], retval) @mock.patch('neutron.agent.common.utils.execute') def test_get_devices_namespaces_ns_not_exists(self, mocked_execute): mocked_execute.side_effect = RuntimeError( "Cannot open network namespace") with mock.patch.object(ip_lib.IpNetnsCommand, 'exists', return_value=False): retval = ip_lib.IPWrapper(namespace='foo').get_devices() self.assertEqual([], retval) @mock.patch('neutron.agent.common.utils.execute') def test_get_devices_namespaces_ns_exists(self, mocked_execute): mocked_execute.side_effect = RuntimeError( "Cannot open network namespace") with mock.patch.object(ip_lib.IpNetnsCommand, 'exists', return_value=True): self.assertRaises(RuntimeError, ip_lib.IPWrapper(namespace='foo').get_devices) @mock.patch('neutron.agent.common.utils.execute') def test_get_devices_exclude_loopback_and_gre(self, mocked_execute): device_name = 'somedevice' mocked_execute.return_value = 'lo gre0 gretap0 ' + device_name devices = ip_lib.IPWrapper(namespace='foo').get_devices( exclude_loopback=True, exclude_gre_devices=True) somedevice = devices.pop() self.assertEqual(device_name, somedevice.name) self.assertFalse(devices) @mock.patch.object(pyroute2.netns, 'listnetns') @mock.patch.object(priv_lib, 'list_netns') def test_get_namespaces_non_root(self, priv_listnetns, listnetns): self.config(group='AGENT', use_helper_for_ns_read=False) listnetns.return_value = NETNS_SAMPLE retval = ip_lib.list_network_namespaces() self.assertEqual(retval, ['12345678-1234-5678-abcd-1234567890ab', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'cccccccc-cccc-cccc-cccc-cccccccccccc']) self.assertEqual(1, listnetns.call_count) self.assertFalse(priv_listnetns.called) @mock.patch.object(pyroute2.netns, 'listnetns') @mock.patch.object(priv_lib, 'list_netns') def test_get_namespaces_root(self, priv_listnetns, listnetns): self.config(group='AGENT', use_helper_for_ns_read=True) priv_listnetns.return_value = NETNS_SAMPLE retval = ip_lib.list_network_namespaces() self.assertEqual(retval, ['12345678-1234-5678-abcd-1234567890ab', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'cccccccc-cccc-cccc-cccc-cccccccccccc']) self.assertEqual(1, priv_listnetns.call_count) self.assertFalse(listnetns.called) def test_add_tuntap(self): ip_lib.IPWrapper().add_tuntap('tap0') self.execute.assert_called_once_with([], 'tuntap', ('add', 'tap0', 'mode', 'tap'), run_as_root=True, namespace=None) def test_add_veth(self): ip_lib.IPWrapper().add_veth('tap0', 'tap1') self.execute.assert_called_once_with([], 'link', ('add', 'tap0', 'type', 'veth', 'peer', 'name', 'tap1'), run_as_root=True, namespace=None) def test_add_macvtap(self): ip_lib.IPWrapper().add_macvtap('macvtap0', 'eth0', 'bridge') self.execute.assert_called_once_with([], 'link', ('add', 'link', 'eth0', 'name', 'macvtap0', 'type', 'macvtap', 'mode', 'bridge'), run_as_root=True, namespace=None) def test_del_veth(self): ip_lib.IPWrapper().del_veth('fpr-1234') self.execute.assert_called_once_with([], 'link', ('del', 'fpr-1234'), run_as_root=True, namespace=None) def test_add_veth_with_namespaces(self): ns2 = 'ns2' with mock.patch.object(ip_lib.IPWrapper, 'ensure_namespace') as en: ip_lib.IPWrapper().add_veth('tap0', 'tap1', namespace2=ns2) en.assert_has_calls([mock.call(ns2)]) self.execute.assert_called_once_with([], 'link', ('add', 'tap0', 'type', 'veth', 'peer', 'name', 'tap1', 'netns', ns2), run_as_root=True, namespace=None) def test_add_dummy(self): ip_lib.IPWrapper().add_dummy('dummy0') self.execute.assert_called_once_with([], 'link', ('add', 'dummy0', 'type', 'dummy'), run_as_root=True, namespace=None) def test_get_device(self): dev = ip_lib.IPWrapper(namespace='ns').device('eth0') self.assertEqual(dev.namespace, 'ns') self.assertEqual(dev.name, 'eth0') @mock.patch.object(priv_lib, 'create_netns') def test_ensure_namespace(self, create): with mock.patch.object(ip_lib, 'IPDevice') as ip_dev: ip = ip_lib.IPWrapper() with mock.patch.object(ip.netns, 'exists') as ns_exists: with mock.patch('neutron.agent.common.utils.execute'): ns_exists.return_value = False ip.ensure_namespace('ns') create.assert_called_once_with('ns') ns_exists.assert_called_once_with('ns') ip_dev.assert_has_calls([mock.call('lo', namespace='ns'), mock.call().link.set_up()]) def test_ensure_namespace_existing(self): with mock.patch.object(ip_lib, 'IpNetnsCommand') as ip_ns_cmd: ip_ns_cmd.exists.return_value = True ns = ip_lib.IPWrapper().ensure_namespace('ns') self.assertFalse(self.execute.called) self.assertEqual(ns.namespace, 'ns') def test_namespace_is_empty_no_devices(self): ip = ip_lib.IPWrapper(namespace='ns') with mock.patch.object(ip, 'get_devices') as get_devices: get_devices.return_value = [] self.assertTrue(ip.namespace_is_empty()) self.assertTrue(get_devices.called) def test_namespace_is_empty(self): ip = ip_lib.IPWrapper(namespace='ns') with mock.patch.object(ip, 'get_devices') as get_devices: get_devices.return_value = [mock.Mock()] self.assertFalse(ip.namespace_is_empty()) self.assertTrue(get_devices.called) def test_garbage_collect_namespace_does_not_exist(self): with mock.patch.object(ip_lib, 'IpNetnsCommand') as ip_ns_cmd_cls: ip_ns_cmd_cls.return_value.exists.return_value = False ip = ip_lib.IPWrapper(namespace='ns') with mock.patch.object(ip, 'namespace_is_empty') as mock_is_empty: self.assertFalse(ip.garbage_collect_namespace()) ip_ns_cmd_cls.assert_has_calls([mock.call().exists('ns')]) self.assertNotIn(mock.call().delete('ns'), ip_ns_cmd_cls.return_value.mock_calls) self.assertEqual([], mock_is_empty.mock_calls) def test_garbage_collect_namespace_existing_empty_ns(self): with mock.patch.object(ip_lib, 'IpNetnsCommand') as ip_ns_cmd_cls: ip_ns_cmd_cls.return_value.exists.return_value = True ip = ip_lib.IPWrapper(namespace='ns') with mock.patch.object(ip, 'namespace_is_empty') as mock_is_empty: mock_is_empty.return_value = True self.assertTrue(ip.garbage_collect_namespace()) mock_is_empty.assert_called_once_with() expected = [mock.call().exists('ns'), mock.call().delete('ns')] ip_ns_cmd_cls.assert_has_calls(expected) def test_garbage_collect_namespace_existing_not_empty(self): lo_device = mock.Mock() lo_device.name = 'lo' tap_device = mock.Mock() tap_device.name = 'tap1' with mock.patch.object(ip_lib, 'IpNetnsCommand') as ip_ns_cmd_cls: ip_ns_cmd_cls.return_value.exists.return_value = True ip = ip_lib.IPWrapper(namespace='ns') with mock.patch.object(ip, 'namespace_is_empty') as mock_is_empty: mock_is_empty.return_value = False self.assertFalse(ip.garbage_collect_namespace()) mock_is_empty.assert_called_once_with() expected = [mock.call(ip), mock.call().exists('ns')] self.assertEqual(ip_ns_cmd_cls.mock_calls, expected) self.assertNotIn(mock.call().delete('ns'), ip_ns_cmd_cls.mock_calls) def test_add_vlan(self): retval = ip_lib.IPWrapper().add_vlan('eth0.1', 'eth0', '1') self.assertIsInstance(retval, ip_lib.IPDevice) self.assertEqual(retval.name, 'eth0.1') self.execute.assert_called_once_with([], 'link', ['add', 'link', 'eth0', 'name', 'eth0.1', 'type', 'vlan', 'id', '1'], run_as_root=True, namespace=None) def test_add_vxlan_valid_srcport_length(self): retval = ip_lib.IPWrapper().add_vxlan('vxlan0', 'vni0', group='group0', dev='dev0', ttl='ttl0', tos='tos0', local='local0', proxy=True, srcport=(1, 2)) self.assertIsInstance(retval, ip_lib.IPDevice) self.assertEqual(retval.name, 'vxlan0') self.execute.assert_called_once_with([], 'link', ['add', 'vxlan0', 'type', 'vxlan', 'id', 'vni0', 'group', 'group0', 'dev', 'dev0', 'ttl', 'ttl0', 'tos', 'tos0', 'local', 'local0', 'proxy', 'srcport', '1', '2'], run_as_root=True, namespace=None) def test_add_vxlan_invalid_srcport_length(self): wrapper = ip_lib.IPWrapper() self.assertRaises(n_exc.NetworkVxlanPortRangeError, wrapper.add_vxlan, 'vxlan0', 'vni0', group='group0', dev='dev0', ttl='ttl0', tos='tos0', local='local0', proxy=True, srcport=('1', '2', '3')) def test_add_vxlan_invalid_srcport_range(self): wrapper = ip_lib.IPWrapper() self.assertRaises(n_exc.NetworkVxlanPortRangeError, wrapper.add_vxlan, 'vxlan0', 'vni0', group='group0', dev='dev0', ttl='ttl0', tos='tos0', local='local0', proxy=True, srcport=(2000, 1000)) def test_add_vxlan_dstport(self): retval = ip_lib.IPWrapper().add_vxlan('vxlan0', 'vni0', group='group0', dev='dev0', ttl='ttl0', tos='tos0', local='local0', proxy=True, srcport=(1, 2), dstport=4789) self.assertIsInstance(retval, ip_lib.IPDevice) self.assertEqual(retval.name, 'vxlan0') self.execute.assert_called_once_with([], 'link', ['add', 'vxlan0', 'type', 'vxlan', 'id', 'vni0', 'group', 'group0', 'dev', 'dev0', 'ttl', 'ttl0', 'tos', 'tos0', 'local', 'local0', 'proxy', 'srcport', '1', '2', 'dstport', '4789'], run_as_root=True, namespace=None) def test_add_device_to_namespace(self): dev = mock.Mock() ip_lib.IPWrapper(namespace='ns').add_device_to_namespace(dev) dev.assert_has_calls([mock.call.link.set_netns('ns')]) def test_add_device_to_namespace_is_none(self): dev = mock.Mock() ip_lib.IPWrapper().add_device_to_namespace(dev) self.assertEqual([], dev.mock_calls) class TestIPDevice(base.BaseTestCase): def test_eq_same_name(self): dev1 = ip_lib.IPDevice('tap0') dev2 = ip_lib.IPDevice('tap0') self.assertEqual(dev1, dev2) def test_eq_diff_name(self): dev1 = ip_lib.IPDevice('tap0') dev2 = ip_lib.IPDevice('tap1') self.assertNotEqual(dev1, dev2) def test_eq_same_namespace(self): dev1 = ip_lib.IPDevice('tap0', 'ns1') dev2 = ip_lib.IPDevice('tap0', 'ns1') self.assertEqual(dev1, dev2) def test_eq_diff_namespace(self): dev1 = ip_lib.IPDevice('tap0', namespace='ns1') dev2 = ip_lib.IPDevice('tap0', namespace='ns2') self.assertNotEqual(dev1, dev2) def test_eq_other_is_none(self): dev1 = ip_lib.IPDevice('tap0', namespace='ns1') self.assertIsNotNone(dev1) def test_str(self): self.assertEqual(str(ip_lib.IPDevice('tap0')), 'tap0') class TestIPCommandBase(base.BaseTestCase): def setUp(self): super(TestIPCommandBase, self).setUp() self.ip = mock.Mock() self.ip.namespace = 'namespace' self.ip_cmd = ip_lib.IpCommandBase(self.ip) self.ip_cmd.COMMAND = 'foo' def test_run(self): self.ip_cmd._run([], ('link', 'show')) self.ip.assert_has_calls([mock.call._run([], 'foo', ('link', 'show'))]) def test_run_with_options(self): self.ip_cmd._run(['o'], ('link')) self.ip.assert_has_calls([mock.call._run(['o'], 'foo', ('link'))]) def test_as_root_namespace_false(self): self.ip_cmd._as_root([], ('link')) self.ip.assert_has_calls( [mock.call._as_root([], 'foo', ('link'), use_root_namespace=False)]) def test_as_root_namespace_true(self): self.ip_cmd._as_root([], ('link'), use_root_namespace=True) self.ip.assert_has_calls( [mock.call._as_root([], 'foo', ('link'), use_root_namespace=True)]) def test_as_root_namespace_true_with_options(self): self.ip_cmd._as_root('o', 'link', use_root_namespace=True) self.ip.assert_has_calls( [mock.call._as_root('o', 'foo', ('link'), use_root_namespace=True)]) class TestIPDeviceCommandBase(base.BaseTestCase): def setUp(self): super(TestIPDeviceCommandBase, self).setUp() self.ip_dev = mock.Mock() self.ip_dev.name = 'eth0' self.ip_dev._execute = mock.Mock(return_value='executed') self.ip_cmd = ip_lib.IpDeviceCommandBase(self.ip_dev) self.ip_cmd.COMMAND = 'foo' def test_name_property(self): self.assertEqual(self.ip_cmd.name, 'eth0') class TestIPCmdBase(base.BaseTestCase): def setUp(self): super(TestIPCmdBase, self).setUp() self.parent = mock.Mock() self.parent.name = 'eth0' def _assert_call(self, options, args): self.parent._run.assert_has_calls([ mock.call(options, self.command, args)]) def _assert_sudo(self, options, args, use_root_namespace=False): self.parent._as_root.assert_has_calls( [mock.call(options, self.command, args, use_root_namespace=use_root_namespace)]) class TestIpRuleCommand(TestIPCmdBase): def setUp(self): super(TestIpRuleCommand, self).setUp() self.parent._as_root.return_value = '' self.command = 'rule' self.rule_cmd = ip_lib.IpRuleCommand(self.parent) def _test_add_rule(self, ip, table, priority): ip_version = netaddr.IPNetwork(ip).version self.rule_cmd.add(ip, table=table, priority=priority) self._assert_sudo([ip_version], (['show'])) self._assert_sudo([ip_version], ('add', 'from', ip, 'priority', str(priority), 'table', str(table), 'type', 'unicast')) def _test_add_rule_exists(self, ip, table, priority, output): self.parent._as_root.return_value = output ip_version = netaddr.IPNetwork(ip).version self.rule_cmd.add(ip, table=table, priority=priority) self._assert_sudo([ip_version], (['show'])) def _test_delete_rule(self, ip, table, priority): ip_version = netaddr.IPNetwork(ip).version self.rule_cmd.delete(ip, table=table, priority=priority) self._assert_sudo([ip_version], ('del', 'from', ip, 'priority', str(priority), 'table', str(table), 'type', 'unicast')) def test__parse_line(self): def test(ip_version, line, expected): actual = self.rule_cmd._parse_line(ip_version, line) self.assertEqual(expected, actual) test(4, "4030201:\tfrom 1.2.3.4/24 lookup 10203040", {'from': '1.2.3.4/24', 'table': '10203040', 'type': 'unicast', 'priority': '4030201'}) test(6, "1024: from all iif qg-c43b1928-48 lookup noscope", {'priority': '1024', 'from': '::/0', 'type': 'unicast', 'iif': 'qg-c43b1928-48', 'table': 'noscope'}) def test__make_canonical_all_v4(self): actual = self.rule_cmd._make_canonical(4, {'from': 'all'}) self.assertEqual({'from': '0.0.0.0/0', 'type': 'unicast'}, actual) def test__make_canonical_all_v6(self): actual = self.rule_cmd._make_canonical(6, {'from': 'all'}) self.assertEqual({'from': '::/0', 'type': 'unicast'}, actual) def test__make_canonical_lookup(self): actual = self.rule_cmd._make_canonical(6, {'lookup': 'table'}) self.assertEqual({'table': 'table', 'type': 'unicast'}, actual) def test__make_canonical_iif(self): actual = self.rule_cmd._make_canonical(6, {'iif': 'iface_name'}) self.assertEqual({'iif': 'iface_name', 'type': 'unicast'}, actual) def test__make_canonical_fwmark(self): actual = self.rule_cmd._make_canonical(6, {'fwmark': '0x400'}) self.assertEqual({'fwmark': '0x400/0xffffffff', 'type': 'unicast'}, actual) def test__make_canonical_fwmark_with_mask(self): actual = self.rule_cmd._make_canonical(6, {'fwmark': '0x400/0x00ff'}) self.assertEqual({'fwmark': '0x400/0xff', 'type': 'unicast'}, actual) def test__make_canonical_fwmark_integer(self): actual = self.rule_cmd._make_canonical(6, {'fwmark': 0x400}) self.assertEqual({'fwmark': '0x400/0xffffffff', 'type': 'unicast'}, actual) def test__make_canonical_fwmark_iterable(self): actual = self.rule_cmd._make_canonical(6, {'fwmark': (0x400, 0xffff)}) self.assertEqual({'fwmark': '0x400/0xffff', 'type': 'unicast'}, actual) def test_add_rule_v4(self): self._test_add_rule('192.168.45.100', 2, 100) def test_add_rule_v4_exists(self): self._test_add_rule_exists('192.168.45.100', 2, 101, RULE_V4_SAMPLE) def test_add_rule_v6(self): self._test_add_rule('2001:db8::1', 3, 200) def test_add_rule_v6_exists(self): self._test_add_rule_exists('2001:db8::1', 3, 201, RULE_V6_SAMPLE) def test_delete_rule_v4(self): self._test_delete_rule('192.168.45.100', 2, 100) def test_delete_rule_v6(self): self._test_delete_rule('2001:db8::1', 3, 200) class TestIpLinkCommand(TestIPCmdBase): def setUp(self): super(TestIpLinkCommand, self).setUp() self.parent._run.return_value = LINK_SAMPLE[1] self.command = 'link' self.link_cmd = ip_lib.IpLinkCommand(self.parent) def test_set_address(self): self.link_cmd.set_address('aa:bb:cc:dd:ee:ff') self._assert_sudo([], ('set', 'eth0', 'address', 'aa:bb:cc:dd:ee:ff')) def test_set_allmulticast_on(self): self.link_cmd.set_allmulticast_on() self._assert_sudo([], ('set', 'eth0', 'allmulticast', 'on')) def test_set_mtu(self): self.link_cmd.set_mtu(1500) self._assert_sudo([], ('set', 'eth0', 'mtu', 1500)) def test_set_up(self): observed = self.link_cmd.set_up() self.assertEqual(self.parent._as_root.return_value, observed) self._assert_sudo([], ('set', 'eth0', 'up')) def test_set_down(self): observed = self.link_cmd.set_down() self.assertEqual(self.parent._as_root.return_value, observed) self._assert_sudo([], ('set', 'eth0', 'down')) def test_set_netns(self): self.link_cmd.set_netns('foo') self._assert_sudo([], ('set', 'eth0', 'netns', 'foo')) self.assertEqual(self.parent.namespace, 'foo') def test_set_name(self): self.link_cmd.set_name('tap1') self._assert_sudo([], ('set', 'eth0', 'name', 'tap1')) self.assertEqual(self.parent.name, 'tap1') def test_set_alias(self): self.link_cmd.set_alias('openvswitch') self._assert_sudo([], ('set', 'eth0', 'alias', 'openvswitch')) def test_delete(self): self.link_cmd.delete() self._assert_sudo([], ('delete', 'eth0')) def test_address_property(self): self.parent._execute = mock.Mock(return_value=LINK_SAMPLE[1]) self.assertEqual(self.link_cmd.address, 'cc:dd:ee:ff:ab:cd') def test_mtu_property(self): self.parent._execute = mock.Mock(return_value=LINK_SAMPLE[1]) self.assertEqual(self.link_cmd.mtu, 1500) def test_qdisc_property(self): self.parent._execute = mock.Mock(return_value=LINK_SAMPLE[1]) self.assertEqual(self.link_cmd.qdisc, 'mq') def test_qlen_property(self): self.parent._execute = mock.Mock(return_value=LINK_SAMPLE[1]) self.assertEqual(self.link_cmd.qlen, 1000) def test_alias_property(self): self.parent._execute = mock.Mock(return_value=LINK_SAMPLE[1]) self.assertEqual(self.link_cmd.alias, 'openvswitch') def test_state_property(self): self.parent._execute = mock.Mock(return_value=LINK_SAMPLE[1]) self.assertEqual(self.link_cmd.state, 'UP') def test_settings_property(self): expected = {'mtu': 1500, 'qlen': 1000, 'state': 'UP', 'qdisc': 'mq', 'brd': 'ff:ff:ff:ff:ff:ff', 'link/ether': 'cc:dd:ee:ff:ab:cd', 'alias': 'openvswitch'} self.parent._execute = mock.Mock(return_value=LINK_SAMPLE[1]) self.assertEqual(self.link_cmd.attributes, expected) self._assert_call(['o'], ('show', 'eth0')) class TestIpAddrCommand(TestIPCmdBase): def setUp(self): super(TestIpAddrCommand, self).setUp() self.parent.name = 'tap0' self.command = 'addr' self.addr_cmd = ip_lib.IpAddrCommand(self.parent) def test_add_address(self): self.addr_cmd.add('192.168.45.100/24') self._assert_sudo([4], ('add', '192.168.45.100/24', 'scope', 'global', 'dev', 'tap0', 'brd', '192.168.45.255')) def test_add_address_scoped(self): self.addr_cmd.add('192.168.45.100/24', scope='link') self._assert_sudo([4], ('add', '192.168.45.100/24', 'scope', 'link', 'dev', 'tap0', 'brd', '192.168.45.255')) def test_add_address_no_broadcast(self): self.addr_cmd.add('192.168.45.100/24', add_broadcast=False) self._assert_sudo([4], ('add', '192.168.45.100/24', 'scope', 'global', 'dev', 'tap0')) def test_del_address(self): self.addr_cmd.delete('192.168.45.100/24') self._assert_sudo([4], ('del', '192.168.45.100/24', 'dev', 'tap0')) def test_flush(self): self.addr_cmd.flush(6) self._assert_sudo([6], ('flush', 'tap0')) def test_list(self): expected = [ dict(name='eth0', scope='global', dadfailed=False, tentative=False, dynamic=False, cidr='172.16.77.240/24'), dict(name='eth0', scope='global', dadfailed=False, tentative=False, dynamic=True, cidr='2001:470:9:1224:5595:dd51:6ba2:e788/64'), dict(name='eth0', scope='link', dadfailed=False, tentative=True, dynamic=False, cidr='fe80::3023:39ff:febc:22ae/64'), dict(name='eth0', scope='link', dadfailed=True, tentative=True, dynamic=False, cidr='fe80::3023:39ff:febc:22af/64'), dict(name='eth0', scope='global', dadfailed=False, tentative=False, dynamic=True, cidr='2001:470:9:1224:fd91:272:581e:3a32/64'), dict(name='eth0', scope='global', dadfailed=False, tentative=False, dynamic=True, cidr='2001:470:9:1224:4508:b885:5fb:740b/64'), dict(name='eth0', scope='global', dadfailed=False, tentative=False, dynamic=True, cidr='2001:470:9:1224:dfcc:aaff:feb9:76ce/64'), dict(name='eth0', scope='link', dadfailed=False, tentative=False, dynamic=False, cidr='fe80::dfcc:aaff:feb9:76ce/64')] test_cases = [ADDR_SAMPLE, ADDR_SAMPLE2] for test_case in test_cases: self.parent._run = mock.Mock(return_value=test_case) self.assertEqual(expected, self.addr_cmd.list()) self._assert_call([], ('show', 'tap0')) def test_wait_until_address_ready(self): self.parent._run.return_value = ADDR_SAMPLE # this address is not tentative or failed so it should return self.assertIsNone(self.addr_cmd.wait_until_address_ready( '2001:470:9:1224:fd91:272:581e:3a32')) def test_wait_until_address_ready_non_existent_address(self): self.addr_cmd.list = mock.Mock(return_value=[]) with testtools.ExpectedException(ip_lib.AddressNotReady): self.addr_cmd.wait_until_address_ready('abcd::1234') def test_wait_until_address_ready_timeout(self): tentative_address = 'fe80::3023:39ff:febc:22ae' self.addr_cmd.list = mock.Mock(return_value=[ dict(scope='link', dadfailed=False, tentative=True, dynamic=False, cidr=tentative_address + '/64')]) with testtools.ExpectedException(ip_lib.AddressNotReady): self.addr_cmd.wait_until_address_ready(tentative_address, wait_time=1) def test_list_filtered(self): expected = [ dict(name='eth0', scope='global', tentative=False, dadfailed=False, dynamic=False, cidr='172.16.77.240/24')] test_cases = [ADDR_SAMPLE, ADDR_SAMPLE2] for test_case in test_cases: output = '\n'.join(test_case.split('\n')[0:4]) self.parent._run.return_value = output self.assertEqual(self.addr_cmd.list('global', filters=['permanent']), expected) self._assert_call([], ('show', 'tap0', 'permanent', 'scope', 'global')) def test_get_devices_with_ip(self): # This can only verify that get_devices_with_ip() returns a dict # with the correct entry, it doesn't actually test that it only # returns items filtered by the arguments since it isn't calling # /sbin/ip at all. self.parent._run.return_value = ADDR_SAMPLE3 devices = self.addr_cmd.get_devices_with_ip(to='172.16.77.240/24') self.assertEqual(1, len(devices)) expected = {'cidr': '172.16.77.240/24', 'dadfailed': False, 'dynamic': False, 'name': 'eth0', 'scope': 'global', 'tentative': False} self.assertEqual(expected, devices[0]) class TestIpRouteCommand(TestIPCmdBase): def setUp(self): super(TestIpRouteCommand, self).setUp() self.parent.name = 'eth0' self.command = 'route' self.route_cmd = ip_lib.IpRouteCommand(self.parent) self.ip_version = 4 self.table = 14 self.metric = 100 self.cidr = '192.168.45.100/24' self.ip = '10.0.0.1' self.gateway = '192.168.45.100' self.test_cases = [{'sample': GATEWAY_SAMPLE1, 'expected': {'gateway': '10.35.19.254', 'metric': 100}}, {'sample': GATEWAY_SAMPLE2, 'expected': {'gateway': '10.35.19.254', 'metric': 100}}, {'sample': GATEWAY_SAMPLE3, 'expected': None}, {'sample': GATEWAY_SAMPLE4, 'expected': {'gateway': '10.35.19.254'}}, {'sample': GATEWAY_SAMPLE5, 'expected': {'gateway': '192.168.99.1'}}, {'sample': GATEWAY_SAMPLE6, 'expected': {'gateway': '192.168.99.1', 'metric': 100}}, {'sample': GATEWAY_SAMPLE7, 'expected': {'metric': 1}}] def test_add_gateway(self): self.route_cmd.add_gateway(self.gateway, self.metric, self.table) self._assert_sudo([self.ip_version], ('replace', 'default', 'via', self.gateway, 'metric', self.metric, 'dev', self.parent.name, 'table', self.table)) def test_add_gateway_subtable(self): self.route_cmd.table(self.table).add_gateway(self.gateway, self.metric) self._assert_sudo([self.ip_version], ('replace', 'default', 'via', self.gateway, 'metric', self.metric, 'dev', self.parent.name, 'table', self.table)) def test_del_gateway_success(self): self.route_cmd.delete_gateway(self.gateway, table=self.table) self._assert_sudo([self.ip_version], ('del', 'default', 'via', self.gateway, 'dev', self.parent.name, 'table', self.table)) def test_del_gateway_success_subtable(self): self.route_cmd.table(table=self.table).delete_gateway(self.gateway) self._assert_sudo([self.ip_version], ('del', 'default', 'via', self.gateway, 'dev', self.parent.name, 'table', self.table)) def test_del_gateway_cannot_find_device(self): self.parent._as_root.side_effect = RuntimeError("Cannot find device") exc = self.assertRaises(exceptions.DeviceNotFoundError, self.route_cmd.delete_gateway, self.gateway, table=self.table) self.assertIn(self.parent.name, str(exc)) def test_del_gateway_other_error(self): self.parent._as_root.side_effect = RuntimeError() self.assertRaises(RuntimeError, self.route_cmd.delete_gateway, self.gateway, table=self.table) def test_get_gateway(self): for test_case in self.test_cases: self.parent._run = mock.Mock(return_value=test_case['sample']) self.assertEqual(self.route_cmd.get_gateway(), test_case['expected']) def test_flush_route_table(self): self.route_cmd.flush(self.ip_version, self.table) self._assert_sudo([self.ip_version], ('flush', 'table', self.table)) def test_add_route(self): self.route_cmd.add_route(self.cidr, self.ip, self.table) self._assert_sudo([self.ip_version], ('replace', self.cidr, 'via', self.ip, 'dev', self.parent.name, 'table', self.table)) def test_add_route_no_via(self): self.route_cmd.add_route(self.cidr, table=self.table) self._assert_sudo([self.ip_version], ('replace', self.cidr, 'dev', self.parent.name, 'table', self.table)) def test_add_route_with_scope(self): self.route_cmd.add_route(self.cidr, scope='link') self._assert_sudo([self.ip_version], ('replace', self.cidr, 'dev', self.parent.name, 'scope', 'link')) def test_add_route_no_device(self): self.parent._as_root.side_effect = RuntimeError("Cannot find device") self.assertRaises(exceptions.DeviceNotFoundError, self.route_cmd.add_route, self.cidr, self.ip, self.table) def test_delete_route(self): self.route_cmd.delete_route(self.cidr, self.ip, self.table) self._assert_sudo([self.ip_version], ('del', self.cidr, 'via', self.ip, 'dev', self.parent.name, 'table', self.table)) def test_delete_route_no_via(self): self.route_cmd.delete_route(self.cidr, table=self.table) self._assert_sudo([self.ip_version], ('del', self.cidr, 'dev', self.parent.name, 'table', self.table)) def test_delete_route_with_scope(self): self.route_cmd.delete_route(self.cidr, scope='link') self._assert_sudo([self.ip_version], ('del', self.cidr, 'dev', self.parent.name, 'scope', 'link')) def test_delete_route_no_device(self): self.parent._as_root.side_effect = RuntimeError("Cannot find device") self.assertRaises(exceptions.DeviceNotFoundError, self.route_cmd.delete_route, self.cidr, self.ip, self.table) def test_list_routes(self): self.parent._run.return_value = ( "default via 172.124.4.1 dev eth0 metric 100\n" "10.0.0.0/22 dev eth0 scope link\n" "172.24.4.0/24 dev eth0 proto kernel src 172.24.4.2\n") routes = self.route_cmd.table(self.table).list_routes(self.ip_version) self.assertEqual([{'cidr': '0.0.0.0/0', 'dev': 'eth0', 'metric': '100', 'table': 14, 'via': '172.124.4.1'}, {'cidr': '10.0.0.0/22', 'dev': 'eth0', 'scope': 'link', 'table': 14}, {'cidr': '172.24.4.0/24', 'dev': 'eth0', 'proto': 'kernel', 'src': '172.24.4.2', 'table': 14}], routes) def test_list_onlink_routes_subtable(self): self.parent._run.return_value = ( "10.0.0.0/22\n" "172.24.4.0/24 proto kernel src 172.24.4.2\n") routes = self.route_cmd.table(self.table).list_onlink_routes( self.ip_version) self.assertEqual(['10.0.0.0/22'], [r['cidr'] for r in routes]) self._assert_call([self.ip_version], ('list', 'dev', self.parent.name, 'table', self.table, 'scope', 'link')) def test_add_onlink_route_subtable(self): self.route_cmd.table(self.table).add_onlink_route(self.cidr) self._assert_sudo([self.ip_version], ('replace', self.cidr, 'dev', self.parent.name, 'table', self.table, 'scope', 'link')) def test_delete_onlink_route_subtable(self): self.route_cmd.table(self.table).delete_onlink_route(self.cidr) self._assert_sudo([self.ip_version], ('del', self.cidr, 'dev', self.parent.name, 'table', self.table, 'scope', 'link')) class TestIPv6IpRouteCommand(TestIpRouteCommand): def setUp(self): super(TestIPv6IpRouteCommand, self).setUp() self.ip_version = 6 self.cidr = '2001:db8::/64' self.ip = '2001:db8::100' self.gateway = '2001:db8::1' self.test_cases = [{'sample': IPv6_GATEWAY_SAMPLE1, 'expected': {'gateway': '2001:470:9:1224:4508:b885:5fb:740b', 'metric': 100}}, {'sample': IPv6_GATEWAY_SAMPLE2, 'expected': {'gateway': '2001:470:9:1224:4508:b885:5fb:740b', 'metric': 100}}, {'sample': IPv6_GATEWAY_SAMPLE3, 'expected': None}, {'sample': IPv6_GATEWAY_SAMPLE4, 'expected': {'gateway': 'fe80::dfcc:aaff:feb9:76ce'}}, {'sample': IPv6_GATEWAY_SAMPLE5, 'expected': {'gateway': '2001:470:9:1224:4508:b885:5fb:740b', 'metric': 1024}}] def test_list_routes(self): self.parent._run.return_value = ( "default via 2001:db8::1 dev eth0 metric 100\n" "2001:db8::/64 dev eth0 proto kernel src 2001:db8::2\n") routes = self.route_cmd.table(self.table).list_routes(self.ip_version) self.assertEqual([{'cidr': '::/0', 'dev': 'eth0', 'metric': '100', 'table': 14, 'via': '2001:db8::1'}, {'cidr': '2001:db8::/64', 'dev': 'eth0', 'proto': 'kernel', 'src': '2001:db8::2', 'table': 14}], routes) class TestIPRoute(TestIpRouteCommand): """Leverage existing tests for IpRouteCommand for IPRoute This test leverages the tests written for IpRouteCommand. The difference is that the 'dev' argument should not be passed for each of the commands. So, this test removes the dev argument from the expected arguments in each assert. """ def setUp(self): super(TestIPRoute, self).setUp() self.parent = ip_lib.IPRoute() self.parent._run = mock.Mock() self.parent._as_root = mock.Mock() self.route_cmd = self.parent.route self.check_dev_args = False def _remove_dev_args(self, args): def args_without_dev(): previous = None for arg in args: if 'dev' not in (arg, previous): yield arg previous = arg return tuple(arg for arg in args_without_dev()) def _assert_call(self, options, args): if not self.check_dev_args: args = self._remove_dev_args(args) super(TestIPRoute, self)._assert_call(options, args) def _assert_sudo(self, options, args, use_root_namespace=False): if not self.check_dev_args: args = self._remove_dev_args(args) super(TestIPRoute, self)._assert_sudo(options, args) def test_del_gateway_cannot_find_device(self): # This test doesn't make sense for this case since dev won't be passed pass class TestIpNetnsCommand(TestIPCmdBase): def setUp(self): super(TestIpNetnsCommand, self).setUp() self.command = 'netns' self.netns_cmd = ip_lib.IpNetnsCommand(self.parent) @mock.patch.object(priv_lib, 'create_netns') def test_add_namespace(self, create): with mock.patch('neutron.agent.common.utils.execute') as execute: ns = self.netns_cmd.add('ns') create.assert_called_once_with('ns') self.assertEqual(ns.namespace, 'ns') execute.assert_called_once_with( ['ip', 'netns', 'exec', 'ns', 'sysctl', '-w', 'net.ipv4.conf.all.promote_secondaries=1'], run_as_root=True, check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True) @mock.patch.object(priv_lib, 'remove_netns') def test_delete_namespace(self, remove): self.netns_cmd.delete('ns') remove.assert_called_once_with('ns') @mock.patch.object(pyroute2.netns, 'listnetns') @mock.patch.object(priv_lib, 'list_netns') def test_namespace_exists_use_helper(self, priv_listnetns, listnetns): self.config(group='AGENT', use_helper_for_ns_read=True) priv_listnetns.return_value = NETNS_SAMPLE # need another instance to avoid mocking netns_cmd = ip_lib.IpNetnsCommand(ip_lib.SubProcessBase()) self.assertTrue( netns_cmd.exists('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb')) self.assertEqual(1, priv_listnetns.call_count) self.assertFalse(listnetns.called) @mock.patch.object(pyroute2.netns, 'listnetns') @mock.patch.object(priv_lib, 'list_netns') def test_namespace_does_not_exist_no_helper(self, priv_listnetns, listnetns): self.config(group='AGENT', use_helper_for_ns_read=False) listnetns.return_value = NETNS_SAMPLE # need another instance to avoid mocking netns_cmd = ip_lib.IpNetnsCommand(ip_lib.SubProcessBase()) self.assertFalse( netns_cmd.exists('bbbbbbbb-1111-2222-3333-bbbbbbbbbbbb')) self.assertEqual(1, listnetns.call_count) self.assertFalse(priv_listnetns.called) def test_execute(self): self.parent.namespace = 'ns' with mock.patch('neutron.agent.common.utils.execute') as execute: self.netns_cmd.execute(['ip', 'link', 'list']) execute.assert_called_once_with(['ip', 'netns', 'exec', 'ns', 'ip', 'link', 'list'], run_as_root=True, check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True) def test_execute_env_var_prepend(self): self.parent.namespace = 'ns' with mock.patch('neutron.agent.common.utils.execute') as execute: env = dict(FOO=1, BAR=2) self.netns_cmd.execute(['ip', 'link', 'list'], env) execute.assert_called_once_with( ['ip', 'netns', 'exec', 'ns', 'env'] + ['%s=%s' % (k, v) for k, v in env.items()] + ['ip', 'link', 'list'], run_as_root=True, check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True) def test_execute_nosudo_with_no_namespace(self): with mock.patch('neutron.agent.common.utils.execute') as execute: self.parent.namespace = None self.netns_cmd.execute(['test']) execute.assert_called_once_with(['test'], check_exit_code=True, extra_ok_codes=None, run_as_root=False, log_fail_as_error=True) class TestDeviceExists(base.BaseTestCase): def test_device_exists(self): with mock.patch('neutron.agent.common.utils.execute') as execute: execute.return_value = LINK_SAMPLE[1] self.assertTrue(ip_lib.device_exists('eth0')) execute.assert_called_once_with( ['ip', '-o', 'link', 'show', 'eth0'], run_as_root=False, log_fail_as_error=False) def test_device_exists_reset_fail(self): device = ip_lib.IPDevice('eth0') device.set_log_fail_as_error(True) with mock.patch.object(ip_lib.IPDevice, '_execute') as _execute: _execute.return_value = LINK_SAMPLE[1] self.assertTrue(device.exists()) self.assertTrue(device.get_log_fail_as_error()) def test_device_does_not_exist(self): with mock.patch.object(ip_lib.IPDevice, '_execute') as _execute: _execute.return_value = '' _execute.side_effect = RuntimeError self.assertFalse(ip_lib.device_exists('eth0')) def test_ensure_device_is_ready(self): ip_lib_mock = mock.Mock() with mock.patch.object(ip_lib, 'IPDevice', return_value=ip_lib_mock): self.assertTrue(ip_lib.ensure_device_is_ready("eth0")) self.assertTrue(ip_lib_mock.link.set_up.called) ip_lib_mock.reset_mock() # device doesn't exists ip_lib_mock.link.set_up.side_effect = RuntimeError self.assertFalse(ip_lib.ensure_device_is_ready("eth0")) def test_ensure_device_is_ready_no_link_address(self): with mock.patch.object(ip_lib.IPDevice, '_execute') as _execute: # Use lo, it has no MAC address _execute.return_value = LINK_SAMPLE[0] self.assertFalse(ip_lib.ensure_device_is_ready("lo")) class TestGetRoutingTable(base.BaseTestCase): ip_db_interfaces = { 1: { 'family': 0, 'txqlen': 0, 'ipdb_scope': 'system', 'index': 1, 'operstate': 'DOWN', 'num_tx_queues': 1, 'group': 0, 'carrier_changes': 0, 'ipaddr': [], 'neighbours': [], 'ifname': 'lo', 'promiscuity': 0, 'linkmode': 0, 'broadcast': '00:00:00:00:00:00', 'address': '00:00:00:00:00:00', 'vlans': [], 'ipdb_priority': 0, 'qdisc': 'noop', 'mtu': 65536, 'num_rx_queues': 1, 'carrier': 1, 'flags': 8, 'ifi_type': 772, 'ports': [] }, 2: { 'family': 0, 'txqlen': 500, 'ipdb_scope': 'system', 'index': 2, 'operstate': 'DOWN', 'num_tx_queues': 1, 'group': 0, 'carrier_changes': 1, 'ipaddr': ['1111:1111:1111:1111::3/64', '10.0.0.3/24'], 'neighbours': [], 'ifname': 'tap-1', 'promiscuity': 0, 'linkmode': 0, 'broadcast': 'ff:ff:ff:ff:ff:ff', 'address': 'b6:d5:f6:a8:2e:62', 'vlans': [], 'ipdb_priority': 0, 'kind': 'tun', 'qdisc': 'fq_codel', 'mtu': 1500, 'num_rx_queues': 1, 'carrier': 0, 'flags': 4099, 'ifi_type': 1, 'ports': [] }, 'tap-1': { 'family': 0, 'txqlen': 500, 'ipdb_scope': 'system', 'index': 2, 'operstate': 'DOWN', 'num_tx_queues': 1, 'group': 0, 'carrier_changes': 1, 'ipaddr': ['1111:1111:1111:1111::3/64', '10.0.0.3/24'], 'neighbours': [], 'ifname': 'tap-1', 'promiscuity': 0, 'linkmode': 0, 'broadcast': 'ff:ff:ff:ff:ff:ff', 'address': 'b6:d5:f6:a8:2e:62', 'vlans': [], 'ipdb_priority': 0, 'kind': 'tun', 'qdisc': 'fq_codel', 'mtu': 1500, 'num_rx_queues': 1, 'carrier': 0, 'flags': 4099, 'ifi_type': 1, 'ports': [] }, 'lo': { 'family': 0, 'txqlen': 0, 'ipdb_scope': 'system', 'index': 1, 'operstate': 'DOWN', 'num_tx_queues': 1, 'group': 0, 'carrier_changes': 0, 'ipaddr': [], 'neighbours': [], 'ifname': 'lo', 'promiscuity': 0, 'linkmode': 0, 'broadcast': '00:00:00:00:00:00', 'address': '00:00:00:00:00:00', 'vlans': [], 'ipdb_priority': 0, 'qdisc': 'noop', 'mtu': 65536, 'num_rx_queues': 1, 'carrier': 1, 'flags': 8, 'ifi_type': 772, 'ports': [] } } ip_db_routes = [ { 'oif': 2, 'dst_len': 24, 'family': 2, 'proto': 3, 'tos': 0, 'dst': '10.0.1.0/24', 'flags': 16, 'ipdb_priority': 0, 'metrics': {}, 'scope': 0, 'encap': {}, 'src_len': 0, 'table': 254, 'multipath': [], 'type': 1, 'gateway': '10.0.0.1', 'ipdb_scope': 'system' }, { 'oif': 2, 'type': 1, 'dst_len': 24, 'family': 2, 'proto': 2, 'tos': 0, 'dst': '10.0.0.0/24', 'ipdb_priority': 0, 'metrics': {}, 'flags': 16, 'encap': {}, 'src_len': 0, 'table': 254, 'multipath': [], 'prefsrc': '10.0.0.3', 'scope': 253, 'ipdb_scope': 'system' }, { 'oif': 2, 'dst_len': 0, 'family': 2, 'proto': 3, 'tos': 0, 'dst': 'default', 'flags': 16, 'ipdb_priority': 0, 'metrics': {}, 'scope': 0, 'encap': {}, 'src_len': 0, 'table': 254, 'multipath': [], 'type': 1, 'gateway': '10.0.0.2', 'ipdb_scope': 'system' }, { 'metrics': {}, 'oif': 2, 'dst_len': 64, 'family': socket.AF_INET6, 'proto': 2, 'tos': 0, 'dst': '1111:1111:1111:1111::/64', 'pref': '00', 'ipdb_priority': 0, 'priority': 256, 'flags': 0, 'encap': {}, 'src_len': 0, 'table': 254, 'multipath': [], 'type': 1, 'scope': 0, 'ipdb_scope': 'system' }, { 'metrics': {}, 'oif': 2, 'dst_len': 64, 'family': socket.AF_INET6, 'proto': 3, 'tos': 0, 'dst': '1111:1111:1111:1112::/64', 'pref': '00', 'flags': 0, 'ipdb_priority': 0, 'priority': 1024, 'scope': 0, 'encap': {}, 'src_len': 0, 'table': 254, 'multipath': [], 'type': 1, 'gateway': '1111:1111:1111:1111::1', 'ipdb_scope': 'system' } ] def setUp(self): super(TestGetRoutingTable, self).setUp() self.addCleanup(privileged.default.set_client_mode, True) privileged.default.set_client_mode(False) @mock.patch.object(pyroute2, 'IPDB') @mock.patch.object(pyroute2, 'NetNS') def test_get_routing_table_nonexistent_namespace(self, mock_netns, mock_ip_db): mock_netns.side_effect = OSError(errno.ENOENT, None) with testtools.ExpectedException(ip_lib.NetworkNamespaceNotFound): ip_lib.get_routing_table(4, 'ns') @mock.patch.object(pyroute2, 'IPDB') @mock.patch.object(pyroute2, 'NetNS') def test_get_routing_table_other_error(self, mock_netns, mock_ip_db): expected_exception = OSError(errno.EACCES, None) mock_netns.side_effect = expected_exception with testtools.ExpectedException(expected_exception.__class__): ip_lib.get_routing_table(4, 'ns') @mock.patch.object(pyroute2, 'IPDB') @mock.patch.object(pyroute2, 'NetNS') def _test_get_routing_table(self, version, ip_db_routes, expected, mock_netns, mock_ip_db): mock_ip_db_instance = mock_ip_db.return_value mock_ip_db_enter = mock_ip_db_instance.__enter__.return_value mock_ip_db_enter.interfaces = self.ip_db_interfaces mock_ip_db_enter.routes = ip_db_routes self.assertEqual(expected, ip_lib.get_routing_table(version)) def test_get_routing_table_4(self): expected = [{'destination': '10.0.1.0/24', 'nexthop': '10.0.0.1', 'device': 'tap-1', 'scope': 'universe'}, {'destination': '10.0.0.0/24', 'nexthop': None, 'device': 'tap-1', 'scope': 'link'}, {'destination': 'default', 'nexthop': '10.0.0.2', 'device': 'tap-1', 'scope': 'universe'}] self._test_get_routing_table(4, self.ip_db_routes, expected) def test_get_routing_table_6(self): expected = [{'destination': '1111:1111:1111:1111::/64', 'nexthop': None, 'device': 'tap-1', 'scope': 'universe'}, {'destination': '1111:1111:1111:1112::/64', 'nexthop': '1111:1111:1111:1111::1', 'device': 'tap-1', 'scope': 'universe'}] self._test_get_routing_table(6, self.ip_db_routes, expected) class TestIpNeighCommand(TestIPCmdBase): def setUp(self): super(TestIpNeighCommand, self).setUp() self.parent.name = 'tap0' self.command = 'neigh' self.neigh_cmd = ip_lib.IpNeighCommand(self.parent) self.addCleanup(privileged.default.set_client_mode, True) privileged.default.set_client_mode(False) @mock.patch.object(pyroute2, 'NetNS') def test_add_entry(self, mock_netns): mock_netns_instance = mock_netns.return_value mock_netns_enter = mock_netns_instance.__enter__.return_value mock_netns_enter.link_lookup.return_value = [1] self.neigh_cmd.add('192.168.45.100', 'cc:dd:ee:ff:ab:cd') mock_netns_enter.link_lookup.assert_called_once_with(ifname='tap0') mock_netns_enter.neigh.assert_called_once_with( 'replace', dst='192.168.45.100', lladdr='cc:dd:ee:ff:ab:cd', family=2, ifindex=1, state=ndmsg.states['permanent']) @mock.patch.object(pyroute2, 'NetNS') def test_add_entry_nonexistent_namespace(self, mock_netns): mock_netns.side_effect = OSError(errno.ENOENT, None) with testtools.ExpectedException(ip_lib.NetworkNamespaceNotFound): self.neigh_cmd.add('192.168.45.100', 'cc:dd:ee:ff:ab:cd') @mock.patch.object(pyroute2, 'NetNS') def test_add_entry_other_error(self, mock_netns): expected_exception = OSError(errno.EACCES, None) mock_netns.side_effect = expected_exception with testtools.ExpectedException(expected_exception.__class__): self.neigh_cmd.add('192.168.45.100', 'cc:dd:ee:ff:ab:cd') @mock.patch.object(pyroute2, 'NetNS') def test_delete_entry(self, mock_netns): mock_netns_instance = mock_netns.return_value mock_netns_enter = mock_netns_instance.__enter__.return_value mock_netns_enter.link_lookup.return_value = [1] self.neigh_cmd.delete('192.168.45.100', 'cc:dd:ee:ff:ab:cd') mock_netns_enter.link_lookup.assert_called_once_with(ifname='tap0') mock_netns_enter.neigh.assert_called_once_with( 'delete', dst='192.168.45.100', lladdr='cc:dd:ee:ff:ab:cd', family=2, ifindex=1) @mock.patch.object(priv_lib, '_run_iproute') def test_delete_entry_not_exist(self, mock_run_iproute): # trying to delete a non-existent entry shouldn't raise an error mock_run_iproute.side_effect = NetlinkError(errno.ENOENT, None) self.neigh_cmd.delete('192.168.45.100', 'cc:dd:ee:ff:ab:cd') @mock.patch.object(pyroute2, 'NetNS') def test_dump_entries(self, mock_netns): mock_netns_instance = mock_netns.return_value mock_netns_enter = mock_netns_instance.__enter__.return_value mock_netns_enter.link_lookup.return_value = [1] self.neigh_cmd.dump(4) mock_netns_enter.link_lookup.assert_called_once_with(ifname='tap0') mock_netns_enter.neigh.assert_called_once_with( 'dump', family=2, ifindex=1) def test_flush(self): self.neigh_cmd.flush(4, '192.168.0.1') self._assert_sudo([4], ('flush', 'to', '192.168.0.1')) class TestArpPing(TestIPCmdBase): @mock.patch.object(ip_lib, 'IPWrapper') @mock.patch('eventlet.spawn_n') def test_send_ipv4_addr_adv_notif(self, spawn_n, mIPWrapper): spawn_n.side_effect = lambda f: f() ARPING_COUNT = 3 address = '20.0.0.1' ip_lib.send_ip_addr_adv_notif(mock.sentinel.ns_name, mock.sentinel.iface_name, address, ARPING_COUNT) self.assertTrue(spawn_n.called) mIPWrapper.assert_has_calls([ mock.call(namespace=mock.sentinel.ns_name), mock.call().netns.execute(mock.ANY, extra_ok_codes=[1]), mock.call().netns.execute(mock.ANY, extra_ok_codes=[1]), mock.call().netns.execute(mock.ANY, extra_ok_codes=[1, 2]), mock.call().netns.execute(mock.ANY, extra_ok_codes=[1, 2]), mock.call().netns.execute(mock.ANY, extra_ok_codes=[1, 2]), mock.call().netns.execute(mock.ANY, extra_ok_codes=[1, 2])]) ip_wrapper = mIPWrapper(namespace=mock.sentinel.ns_name) # Just test that arping is called with the right arguments for arg in ('-A', '-U'): arping_cmd = ['arping', arg, '-I', mock.sentinel.iface_name, '-c', 1, '-w', mock.ANY, address] ip_wrapper.netns.execute.assert_any_call(arping_cmd, extra_ok_codes=mock.ANY) @mock.patch.object(ip_lib, 'IPWrapper') @mock.patch('eventlet.spawn_n') def test_send_ipv4_addr_adv_notif_nodev(self, spawn_n, mIPWrapper): spawn_n.side_effect = lambda f: f() ip_wrapper = mIPWrapper(namespace=mock.sentinel.ns_name) ip_wrapper.netns.execute.side_effect = RuntimeError ARPING_COUNT = 3 address = '20.0.0.1' with mock.patch.object(ip_lib, 'device_exists_with_ips_and_mac', return_value=False): ip_lib.send_ip_addr_adv_notif(mock.sentinel.ns_name, mock.sentinel.iface_name, address, ARPING_COUNT) # should return early with a single call when ENODEV mIPWrapper.assert_has_calls([ mock.call(namespace=mock.sentinel.ns_name), mock.call().netns.execute(mock.ANY, extra_ok_codes=[1]) ] * 1) @mock.patch('eventlet.spawn_n') def test_no_ipv6_addr_notif(self, spawn_n): ipv6_addr = 'fd00::1' ip_lib.send_ip_addr_adv_notif(mock.sentinel.ns_name, mock.sentinel.iface_name, ipv6_addr, 3) self.assertFalse(spawn_n.called) class TestAddNamespaceToCmd(base.BaseTestCase): def test_add_namespace_to_cmd_with_namespace(self): cmd = ['ping', '8.8.8.8'] self.assertEqual(['ip', 'netns', 'exec', 'tmp'] + cmd, ip_lib.add_namespace_to_cmd(cmd, 'tmp')) def test_add_namespace_to_cmd_without_namespace(self): cmd = ['ping', '8.8.8.8'] self.assertEqual(cmd, ip_lib.add_namespace_to_cmd(cmd, None)) class TestSetIpNonlocalBindForHaNamespace(base.BaseTestCase): def test_setting_failure(self): """Make sure message is formatted correctly.""" with mock.patch.object(ip_lib, 'set_ip_nonlocal_bind', return_value=1): ip_lib.set_ip_nonlocal_bind_for_namespace('foo', value=1) class TestSysctl(base.BaseTestCase): def setUp(self): super(TestSysctl, self).setUp() self.execute_p = mock.patch.object(ip_lib.IpNetnsCommand, 'execute') self.execute = self.execute_p.start() def test_disable_ipv6_when_ipv6_globally_enabled(self): dev = ip_lib.IPDevice('tap0', 'ns1') with mock.patch.object(ip_lib.ipv6_utils, 'is_enabled_and_bind_by_default', return_value=True): dev.disable_ipv6() self.execute.assert_called_once_with( ['sysctl', '-w', 'net.ipv6.conf.tap0.disable_ipv6=1'], log_fail_as_error=True, run_as_root=True) def test_disable_ipv6_when_ipv6_globally_disabled(self): dev = ip_lib.IPDevice('tap0', 'ns1') with mock.patch.object(ip_lib.ipv6_utils, 'is_enabled_and_bind_by_default', return_value=False): dev.disable_ipv6() self.assertFalse(self.execute.called) neutron-12.1.1/neutron/tests/unit/agent/linux/test_utils.py0000664000175000017500000005645413553660047024160 0ustar zuulzuul00000000000000# Copyright 2012, VMware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import signal import socket import mock import six import testtools from oslo_config import cfg import oslo_i18n from neutron.agent.linux import utils from neutron.common import exceptions as n_exc from neutron.tests import base from neutron.tests.common import helpers from neutron.tests import tools _marker = object() class AgentUtilsExecuteTest(base.BaseTestCase): def setUp(self): super(AgentUtilsExecuteTest, self).setUp() self.test_file = self.get_temp_file_path('test_execute.tmp') open(self.test_file, 'w').close() self.process = mock.patch('eventlet.green.subprocess.Popen').start() self.process.return_value.returncode = 0 self.mock_popen = self.process.return_value.communicate def test_xenapi_root_helper(self): token = utils.xenapi_root_helper.ROOT_HELPER_DAEMON_TOKEN self.config(group='AGENT', root_helper_daemon=token) with mock.patch( 'neutron.agent.linux.utils.xenapi_root_helper.XenAPIClient')\ as mock_xenapi_class: mock_client = mock_xenapi_class.return_value cmd_client = utils.RootwrapDaemonHelper.get_client() self.assertEqual(cmd_client, mock_client) def test_without_helper(self): expected = "%s\n" % self.test_file self.mock_popen.return_value = [expected, ""] result = utils.execute(["ls", self.test_file]) self.assertEqual(result, expected) def test_with_helper(self): expected = "ls %s\n" % self.test_file self.mock_popen.return_value = [expected, ""] self.config(group='AGENT', root_helper='echo') result = utils.execute(["ls", self.test_file], run_as_root=True) self.assertEqual(result, expected) @mock.patch.object(utils.RootwrapDaemonHelper, 'get_client') def test_with_helper_exception(self, get_client): client_inst = mock.Mock() client_inst.execute.side_effect = RuntimeError get_client.return_value = client_inst self.config(group='AGENT', root_helper_daemon='echo') with mock.patch.object(utils, 'LOG') as log: self.assertRaises(RuntimeError, utils.execute, ['ls'], run_as_root=True) self.assertTrue(log.error.called) def test_stderr_true(self): expected = "%s\n" % self.test_file self.mock_popen.return_value = [expected, ""] out = utils.execute(["ls", self.test_file], return_stderr=True) self.assertIsInstance(out, tuple) self.assertEqual(out, (expected, "")) def test_check_exit_code(self): self.mock_popen.return_value = ["", ""] stdout = utils.execute(["ls", self.test_file[:-1]], check_exit_code=False) self.assertEqual("", stdout) def test_execute_raises(self): self.mock_popen.side_effect = RuntimeError self.assertRaises(RuntimeError, utils.execute, ["ls", self.test_file[:-1]]) def test_process_input(self): expected = "%s\n" % self.test_file[:-1] self.mock_popen.return_value = [expected, ""] result = utils.execute(["cat"], process_input="%s\n" % self.test_file[:-1]) self.assertEqual(result, expected) def test_with_addl_env(self): expected = "%s\n" % self.test_file self.mock_popen.return_value = [expected, ""] result = utils.execute(["ls", self.test_file], addl_env={'foo': 'bar'}) self.assertEqual(result, expected) def test_return_code_log_error_raise_runtime(self): self.mock_popen.return_value = ('', '') self.process.return_value.returncode = 1 with mock.patch.object(utils, 'LOG') as log: self.assertRaises(RuntimeError, utils.execute, ['ls']) self.assertTrue(log.error.called) def test_return_code_log_error_no_raise_runtime(self): self.mock_popen.return_value = ('', '') self.process.return_value.returncode = 1 with mock.patch.object(utils, 'LOG') as log: utils.execute(['ls'], check_exit_code=False) self.assertTrue(log.error.called) def test_return_code_log_debug(self): self.mock_popen.return_value = ('', '') with mock.patch.object(utils, 'LOG') as log: utils.execute(['ls']) self.assertTrue(log.debug.called) def test_return_code_log_error_change_locale(self): ja_output = 'std_out in Japanese' ja_error = 'std_err in Japanese' ja_message_out = oslo_i18n._message.Message(ja_output) ja_message_err = oslo_i18n._message.Message(ja_error) ja_translate_out = oslo_i18n._translate.translate(ja_message_out, 'ja') ja_translate_err = oslo_i18n._translate.translate(ja_message_err, 'ja') self.mock_popen.return_value = (ja_translate_out, ja_translate_err) self.process.return_value.returncode = 1 with mock.patch.object(utils, 'LOG') as log: utils.execute(['ls'], check_exit_code=False) self.assertIn(ja_translate_out, str(log.error.call_args_list)) self.assertIn(ja_translate_err, str(log.error.call_args_list)) def test_return_code_raise_runtime_do_not_log_fail_as_error(self): self.mock_popen.return_value = ('', '') self.process.return_value.returncode = 1 with mock.patch.object(utils, 'LOG') as log: self.assertRaises(n_exc.ProcessExecutionError, utils.execute, ['ls'], log_fail_as_error=False) self.assertFalse(log.error.called) def test_encode_process_input(self): str_idata = "%s\n" % self.test_file[:-1] str_odata = "%s\n" % self.test_file if six.PY3: bytes_idata = str_idata.encode(encoding='utf-8') bytes_odata = str_odata.encode(encoding='utf-8') self.mock_popen.return_value = [bytes_odata, b''] result = utils.execute(['cat'], process_input=str_idata) self.mock_popen.assert_called_once_with(bytes_idata) else: self.mock_popen.return_value = [str_odata, ''] result = utils.execute(['cat'], process_input=str_idata) self.mock_popen.assert_called_once_with(str_idata) self.assertEqual(str_odata, result) def test_return_str_data(self): str_data = "%s\n" % self.test_file self.mock_popen.return_value = [str_data, ''] result = utils.execute(['ls', self.test_file], return_stderr=True) self.assertEqual((str_data, ''), result) @helpers.requires_py3 def test_surrogateescape_in_decoding_out_data(self): bytes_err_data = b'\xed\xa0\xbd' err_data = bytes_err_data.decode('utf-8', 'surrogateescape') out_data = "%s\n" % self.test_file bytes_out_data = out_data.encode(encoding='utf-8') self.mock_popen.return_value = [bytes_out_data, bytes_err_data] result = utils.execute(['ls', self.test_file], return_stderr=True) self.assertEqual((out_data, err_data), result) class AgentUtilsExecuteEncodeTest(base.BaseTestCase): def setUp(self): super(AgentUtilsExecuteEncodeTest, self).setUp() self.test_file = self.get_temp_file_path('test_execute.tmp') open(self.test_file, 'w').close() def test_decode_return_data(self): str_data = "%s\n" % self.test_file result = utils.execute(['ls', self.test_file], return_stderr=True) self.assertEqual((str_data, ''), result) class TestFindParentPid(base.BaseTestCase): def setUp(self): super(TestFindParentPid, self).setUp() self.m_execute = mock.patch.object(utils, 'execute').start() def test_returns_none_for_no_valid_pid(self): self.m_execute.side_effect = n_exc.ProcessExecutionError('', returncode=1) self.assertIsNone(utils.find_parent_pid(-1)) def test_returns_parent_id_for_good_ouput(self): self.m_execute.return_value = '123 \n' self.assertEqual(utils.find_parent_pid(-1), '123') def test_raises_exception_returncode_0(self): with testtools.ExpectedException(n_exc.ProcessExecutionError): self.m_execute.side_effect = \ n_exc.ProcessExecutionError('', returncode=0) utils.find_parent_pid(-1) def test_raises_unknown_exception(self): with testtools.ExpectedException(RuntimeError): self.m_execute.side_effect = RuntimeError() utils.find_parent_pid(-1) class TestFindForkTopParent(base.BaseTestCase): def _test_find_fork_top_parent(self, expected=_marker, find_parent_pid_retvals=None, pid_invoked_with_cmdline_retvals=None): def _find_parent_pid(x): if find_parent_pid_retvals: return find_parent_pid_retvals.pop(0) pid_invoked_with_cmdline = {} if pid_invoked_with_cmdline_retvals: pid_invoked_with_cmdline['side_effect'] = ( pid_invoked_with_cmdline_retvals) else: pid_invoked_with_cmdline['return_value'] = False with mock.patch.object(utils, 'find_parent_pid', side_effect=_find_parent_pid), \ mock.patch.object(utils, 'pid_invoked_with_cmdline', **pid_invoked_with_cmdline): actual = utils.find_fork_top_parent(_marker) self.assertEqual(expected, actual) def test_returns_own_pid_no_parent(self): self._test_find_fork_top_parent() def test_returns_own_pid_nofork(self): self._test_find_fork_top_parent(find_parent_pid_retvals=['2', '3']) def test_returns_first_parent_pid_fork(self): self._test_find_fork_top_parent( expected='2', find_parent_pid_retvals=['2', '3', '4'], pid_invoked_with_cmdline_retvals=[True, False, False]) def test_returns_top_parent_pid_fork(self): self._test_find_fork_top_parent( expected='4', find_parent_pid_retvals=['2', '3', '4'], pid_invoked_with_cmdline_retvals=[True, True, True]) class TestKillProcess(base.BaseTestCase): def _test_kill_process(self, pid, raise_exception=False, kill_signal=signal.SIGKILL, pid_killed=True): if raise_exception: exc = n_exc.ProcessExecutionError('', returncode=0) else: exc = None with mock.patch.object(utils, 'execute', side_effect=exc) as mock_execute: with mock.patch.object(utils, 'process_is_running', return_value=not pid_killed): utils.kill_process(pid, kill_signal, run_as_root=True) mock_execute.assert_called_with(['kill', '-%d' % kill_signal, pid], run_as_root=True) def test_kill_process_returns_none_for_valid_pid(self): self._test_kill_process('1') def test_kill_process_returns_none_for_stale_pid(self): self._test_kill_process('1', raise_exception=True) def test_kill_process_raises_exception_for_execute_exception(self): with testtools.ExpectedException(n_exc.ProcessExecutionError): # Simulate that the process is running after trying to kill due to # any reason such as, for example, Permission denied self._test_kill_process('1', raise_exception=True, pid_killed=False) def test_kill_process_with_different_signal(self): self._test_kill_process('1', kill_signal=signal.SIGTERM) class TestGetCmdlineFromPid(base.BaseTestCase): def setUp(self): super(TestGetCmdlineFromPid, self).setUp() self.pid = 34 self.process_is_running_mock = mock.patch.object( utils, "process_is_running").start() def _test_cmdline(self, process, expected_cmd): self.process_is_running_mock.return_value = True mock_open = self.useFixture( tools.OpenFixture('/proc/%s/cmdline' % self.pid, process) ).mock_open cmdline = utils.get_cmdline_from_pid(self.pid) mock_open.assert_called_once_with('/proc/%s/cmdline' % self.pid, 'r') self.assertEqual(expected_cmd, cmdline) def test_cmdline_separated_with_null_char(self): process_cmd = "python3\0test-binary\0test option\0" expected_cmdline = ["python3", "test-binary", "test option"] self._test_cmdline(process_cmd, expected_cmdline) def test_cmdline_separated_with_space_char(self): process_cmd = "python3 test-binary test option\0" expected_cmdline = ["python3", "test-binary", "test", "option"] self._test_cmdline(process_cmd, expected_cmdline) def test_no_process_running(self): self.process_is_running_mock.return_value = False mock_open = self.useFixture( tools.OpenFixture('/proc/%s/cmdline' % self.pid) ).mock_open cmdline = utils.get_cmdline_from_pid(self.pid) mock_open.assert_not_called() self.assertEqual([], cmdline) class TestFindChildPids(base.BaseTestCase): def test_returns_empty_list_for_exit_code_1(self): with mock.patch.object(utils, 'execute', side_effect=n_exc.ProcessExecutionError( '', returncode=1)): self.assertEqual([], utils.find_child_pids(-1)) def test_returns_empty_list_for_no_output(self): with mock.patch.object(utils, 'execute', return_value=''): self.assertEqual([], utils.find_child_pids(-1)) def test_returns_list_of_child_process_ids_for_good_ouput(self): with mock.patch.object(utils, 'execute', return_value=' 123 \n 185\n'): self.assertEqual(utils.find_child_pids(-1), ['123', '185']) def test_returns_list_of_child_process_ids_recursively(self): with mock.patch.object(utils, 'execute', side_effect=[' 123 \n 185\n', ' 40 \n', '\n', '41\n', '\n']): actual = utils.find_child_pids(-1, True) self.assertEqual(actual, ['123', '185', '40', '41']) def test_raises_unknown_exception(self): with testtools.ExpectedException(RuntimeError): with mock.patch.object(utils, 'execute', side_effect=RuntimeError()): utils.find_child_pids(-1) class TestGetRoothelperChildPid(base.BaseTestCase): def _test_get_root_helper_child_pid(self, expected=_marker, run_as_root=False, pids=None, cmds=None): def _find_child_pids(x): if not pids: return [] pids.pop(0) return pids mock_pid = object() pid_invoked_with_cmdline = {} if cmds: pid_invoked_with_cmdline['side_effect'] = cmds else: pid_invoked_with_cmdline['return_value'] = False with mock.patch.object(utils, 'find_child_pids', side_effect=_find_child_pids), \ mock.patch.object(utils, 'pid_invoked_with_cmdline', **pid_invoked_with_cmdline): actual = utils.get_root_helper_child_pid( mock_pid, mock.ANY, run_as_root) if expected is _marker: expected = str(mock_pid) self.assertEqual(expected, actual) def test_returns_process_pid_not_root(self): self._test_get_root_helper_child_pid() def test_returns_child_pid_as_root(self): self._test_get_root_helper_child_pid(expected='2', pids=['1', '2'], run_as_root=True, cmds=[True]) def test_returns_last_child_pid_as_root(self): self._test_get_root_helper_child_pid(expected='3', pids=['1', '2', '3'], run_as_root=True, cmds=[False, True]) def test_returns_first_non_root_helper_child(self): self._test_get_root_helper_child_pid( expected='2', pids=['1', '2', '3'], run_as_root=True, cmds=[True, False]) def test_returns_none_as_root(self): self._test_get_root_helper_child_pid(expected=None, run_as_root=True) class TestPathUtilities(base.BaseTestCase): def test_remove_abs_path(self): self.assertEqual(['ping', '8.8.8.8'], utils.remove_abs_path(['/usr/bin/ping', '8.8.8.8'])) def test_cmd_matches_expected_matches_abs_path(self): cmd = ['/bar/../foo'] self.assertTrue(utils.cmd_matches_expected(cmd, cmd)) def test_cmd_matches_expected_matches_script(self): self.assertTrue(utils.cmd_matches_expected(['python', 'script'], ['script'])) def test_cmd_matches_expected_doesnt_match(self): self.assertFalse(utils.cmd_matches_expected('foo', 'bar')) class FakeUser(object): def __init__(self, name): self.pw_name = name class FakeGroup(object): def __init__(self, name): self.gr_name = name class TestBaseOSUtils(base.BaseTestCase): EUID = 123 EUNAME = 'user' EGID = 456 EGNAME = 'group' @mock.patch('os.geteuid', return_value=EUID) @mock.patch('pwd.getpwuid', return_value=FakeUser(EUNAME)) def test_is_effective_user_id(self, getpwuid, geteuid): self.assertTrue(utils.is_effective_user(self.EUID)) geteuid.assert_called_once_with() self.assertFalse(getpwuid.called) @mock.patch('os.geteuid', return_value=EUID) @mock.patch('pwd.getpwuid', return_value=FakeUser(EUNAME)) def test_is_effective_user_str_id(self, getpwuid, geteuid): self.assertTrue(utils.is_effective_user(str(self.EUID))) geteuid.assert_called_once_with() self.assertFalse(getpwuid.called) @mock.patch('os.geteuid', return_value=EUID) @mock.patch('pwd.getpwuid', return_value=FakeUser(EUNAME)) def test_is_effective_user_name(self, getpwuid, geteuid): self.assertTrue(utils.is_effective_user(self.EUNAME)) geteuid.assert_called_once_with() getpwuid.assert_called_once_with(self.EUID) @mock.patch('os.geteuid', return_value=EUID) @mock.patch('pwd.getpwuid', return_value=FakeUser(EUNAME)) def test_is_not_effective_user(self, getpwuid, geteuid): self.assertFalse(utils.is_effective_user('wrong')) geteuid.assert_called_once_with() getpwuid.assert_called_once_with(self.EUID) @mock.patch('os.getegid', return_value=EGID) @mock.patch('grp.getgrgid', return_value=FakeGroup(EGNAME)) def test_is_effective_group_id(self, getgrgid, getegid): self.assertTrue(utils.is_effective_group(self.EGID)) getegid.assert_called_once_with() self.assertFalse(getgrgid.called) @mock.patch('os.getegid', return_value=EGID) @mock.patch('grp.getgrgid', return_value=FakeGroup(EGNAME)) def test_is_effective_group_str_id(self, getgrgid, getegid): self.assertTrue(utils.is_effective_group(str(self.EGID))) getegid.assert_called_once_with() self.assertFalse(getgrgid.called) @mock.patch('os.getegid', return_value=EGID) @mock.patch('grp.getgrgid', return_value=FakeGroup(EGNAME)) def test_is_effective_group_name(self, getgrgid, getegid): self.assertTrue(utils.is_effective_group(self.EGNAME)) getegid.assert_called_once_with() getgrgid.assert_called_once_with(self.EGID) @mock.patch('os.getegid', return_value=EGID) @mock.patch('grp.getgrgid', return_value=FakeGroup(EGNAME)) def test_is_not_effective_group(self, getgrgid, getegid): self.assertFalse(utils.is_effective_group('wrong')) getegid.assert_called_once_with() getgrgid.assert_called_once_with(self.EGID) class TestUnixDomainHttpConnection(base.BaseTestCase): def test_connect(self): with mock.patch.object(utils, 'cfg') as cfg: cfg.CONF.metadata_proxy_socket = '/the/path' with mock.patch('socket.socket') as socket_create: conn = utils.UnixDomainHTTPConnection('169.254.169.254', timeout=3) conn.connect() socket_create.assert_has_calls([ mock.call(socket.AF_UNIX, socket.SOCK_STREAM), mock.call().settimeout(3), mock.call().connect('/the/path')] ) self.assertEqual(conn.timeout, 3) class TestUnixDomainHttpProtocol(base.BaseTestCase): def test_init_empty_client(self): for addr in ('', b''): u = utils.UnixDomainHttpProtocol(mock.Mock(), addr, mock.Mock()) self.assertEqual(u.client_address, ('', 0)) def test_init_with_client(self): u = utils.UnixDomainHttpProtocol(mock.Mock(), 'foo', mock.Mock()) self.assertEqual(u.client_address, 'foo') class TestUnixDomainWSGIServer(base.BaseTestCase): def setUp(self): super(TestUnixDomainWSGIServer, self).setUp() self.eventlet_p = mock.patch.object(utils, 'eventlet') self.eventlet = self.eventlet_p.start() def test_start(self): self.server = utils.UnixDomainWSGIServer('test') mock_app = mock.Mock() with mock.patch.object(self.server, '_launch') as launcher: self.server.start(mock_app, '/the/path', workers=5, backlog=128) self.eventlet.assert_has_calls([ mock.call.listen( '/the/path', family=socket.AF_UNIX, backlog=128 )] ) launcher.assert_called_once_with(mock_app, workers=5) def test_run(self): self.server = utils.UnixDomainWSGIServer('test') self.server._run('app', 'sock') self.eventlet.wsgi.server.assert_called_once_with( 'sock', 'app', protocol=utils.UnixDomainHttpProtocol, log=mock.ANY, log_format=cfg.CONF.wsgi_log_format, max_size=self.server.num_threads ) def test_num_threads(self): num_threads = 8 self.server = utils.UnixDomainWSGIServer('test', num_threads=num_threads) self.server._run('app', 'sock') self.eventlet.wsgi.server.assert_called_once_with( 'sock', 'app', protocol=utils.UnixDomainHttpProtocol, log=mock.ANY, log_format=cfg.CONF.wsgi_log_format, max_size=num_threads ) neutron-12.1.1/neutron/tests/unit/agent/linux/failing_process.py0000664000175000017500000000137513553660046025117 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import sys def main(): filename = sys.argv[1] if not os.path.exists(filename): sys.exit(1) if __name__ == '__main__': main() neutron-12.1.1/neutron/tests/unit/agent/linux/test_async_process.py0000664000175000017500000003303513553660047025661 0ustar zuulzuul00000000000000# Copyright 2013 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import signal import eventlet.event from eventlet.green import subprocess import eventlet.queue import mock import testtools from neutron.agent.common import utils from neutron.agent.linux import async_process from neutron.tests import base from neutron.tests.unit.agent.linux import failing_process class TestAsyncProcess(base.BaseTestCase): def setUp(self): super(TestAsyncProcess, self).setUp() self.proc = async_process.AsyncProcess(['fake']) def test_construtor_raises_exception_for_negative_respawn_interval(self): with testtools.ExpectedException(ValueError): async_process.AsyncProcess(['fake'], respawn_interval=-1) def test__spawn(self): expected_process = 'Foo' proc = self.proc with mock.patch.object(utils, 'create_process') as mock_create_process: mock_create_process.return_value = [expected_process, None] with mock.patch('eventlet.spawn') as mock_spawn: proc._spawn() self.assertTrue(self.proc._is_running) self.assertIsInstance(proc._kill_event, eventlet.event.Event) self.assertEqual(proc._process, expected_process) mock_spawn.assert_has_calls([ mock.call(proc._watch_process, proc._read_stdout, proc._kill_event), mock.call(proc._watch_process, proc._read_stderr, proc._kill_event), ]) self.assertEqual(len(proc._watchers), 2) def test__pid_none(self): pid = 1 self.proc._pid = None with mock.patch.object(self.proc, '_process') as _process: with mock.patch.object(utils, 'get_root_helper_child_pid') as func: func.return_value = pid self.assertEqual(self.proc.pid, pid) func.assert_called_once_with(_process.pid, ['fake'], run_as_root=False) self.assertEqual(self.proc._pid, pid) def test__pid_not_none(self): self.proc._pid = 1 with mock.patch.object(self.proc, '_process'),\ mock.patch.object(utils, 'get_root_helper_child_pid') as func: self.assertEqual(self.proc.pid, 1) func.assert_not_called() def test__handle_process_error_kills_with_respawn(self): with mock.patch.object(self.proc, '_kill') as kill: self.proc._handle_process_error() kill.assert_has_calls([mock.call(signal.SIGKILL)]) def test__handle_process_error_kills_without_respawn(self): self.proc.respawn_interval = 1 with mock.patch.object(self.proc, '_kill') as kill: with mock.patch.object(self.proc, '_spawn') as spawn: with mock.patch('eventlet.sleep') as sleep: self.proc._handle_process_error() kill.assert_has_calls([mock.call(signal.SIGKILL)]) sleep.assert_has_calls([mock.call(self.proc.respawn_interval)]) spawn.assert_called_once_with() def test__handle_process_error_no_crash_if_started(self): self.proc._is_running = True with mock.patch.object(self.proc, '_kill'): with mock.patch.object(self.proc, '_spawn') as mock_spawn: self.proc._handle_process_error() mock_spawn.assert_not_called() def _watch_process_exception(self): raise Exception('Error!') def _test__watch_process(self, callback, kill_event): self.proc._is_running = True self.proc._kill_event = kill_event # Ensure the test times out eventually if the watcher loops endlessly with self.assert_max_execution_time(): with mock.patch.object(self.proc, '_handle_process_error') as func: self.proc._watch_process(callback, kill_event) if not kill_event.ready(): func.assert_called_once_with() def test__watch_process_exits_on_callback_failure(self): self._test__watch_process(lambda: None, eventlet.event.Event()) def test__watch_process_exits_on_exception(self): self._test__watch_process(self._watch_process_exception, eventlet.event.Event()) with mock.patch.object(self.proc, '_handle_process_error') as func: self.proc._watch_process(self._watch_process_exception, self.proc._kill_event) func.assert_not_called() def test__watch_process_exits_on_sent_kill_event(self): kill_event = eventlet.event.Event() kill_event.send() self._test__watch_process(None, kill_event) def _test_read_output_queues_and_returns_result(self, output): queue = eventlet.queue.LightQueue() mock_stream = mock.Mock() with mock.patch.object(mock_stream, 'readline') as mock_readline: mock_readline.return_value = output result = self.proc._read(mock_stream, queue) if output: self.assertEqual(output, result) self.assertEqual(output, queue.get_nowait()) else: self.assertFalse(result) self.assertTrue(queue.empty()) def test__read_queues_and_returns_output(self): self._test_read_output_queues_and_returns_result('foo') def test__read_returns_none_for_missing_output(self): self._test_read_output_queues_and_returns_result('') def test_start_raises_exception_if_process_already_started(self): self.proc._is_running = True with testtools.ExpectedException(async_process.AsyncProcessException): self.proc.start() def test_start_invokes__spawn(self): with mock.patch.object(self.proc, '_spawn') as mock_start: self.proc.start() mock_start.assert_called_once_with() def test__iter_queue_returns_empty_list_for_empty_queue(self): result = list(self.proc._iter_queue(eventlet.queue.LightQueue(), False)) self.assertEqual([], result) def test__iter_queue_returns_queued_data(self): queue = eventlet.queue.LightQueue() queue.put('foo') result = list(self.proc._iter_queue(queue, False)) self.assertEqual(result, ['foo']) def _test_iter_output_calls_iter_queue_on_output_queue(self, output_type): expected_value = 'foo' with mock.patch.object(self.proc, '_iter_queue') as mock_iter_queue: mock_iter_queue.return_value = expected_value target_func = getattr(self.proc, 'iter_%s' % output_type, None) value = target_func() self.assertEqual(value, expected_value) queue = getattr(self.proc, '_%s_lines' % output_type, None) mock_iter_queue.assert_called_with(queue, False) def test_iter_stdout(self): self._test_iter_output_calls_iter_queue_on_output_queue('stdout') def test_iter_stderr(self): self._test_iter_output_calls_iter_queue_on_output_queue('stderr') def test__kill_targets_process_for_pid(self): pid = 1 with mock.patch.object(self.proc, '_kill_event' ) as mock_kill_event,\ mock.patch.object(utils, 'get_root_helper_child_pid', return_value=pid),\ mock.patch.object(self.proc, '_kill_process_and_wait' ) as mock_kill_process_and_wait,\ mock.patch.object(self.proc, '_process'): self.proc._kill(signal.SIGKILL) self.assertIsNone(self.proc._kill_event) self.assertFalse(self.proc._is_running) self.assertIsNone(self.proc._pid) mock_kill_event.send.assert_called_once_with() if pid: mock_kill_process_and_wait.assert_called_once_with( pid, signal.SIGKILL, None) def _test__kill_process_and_wait(self, pid, expected, exception_message=None, kill_signal=signal.SIGKILL): self.proc.run_as_root = True if exception_message: exc = RuntimeError(exception_message) else: exc = None with mock.patch.object(utils, 'kill_process', side_effect=exc) as mock_kill_process: actual = self.proc._kill_process(pid, kill_signal) self.assertEqual(expected, actual) mock_kill_process.assert_called_with(pid, kill_signal, self.proc.run_as_root) def test__kill_process_and_wait_returns_true_for_valid_pid(self): self._test__kill_process_and_wait('1', True) def test__kill_process_and_wait_returns_false_for_execute_exception(self): self._test__kill_process_and_wait('1', False, 'Invalid') def test_kill_process_and_wait_with_different_signal(self): self._test__kill_process_and_wait( '1', True, kill_signal=signal.SIGTERM) def test__kill_process_timeout_reached(self): self.proc.run_as_root = True kill_timeout = 5 pid = '1' with mock.patch.object(utils, 'kill_process') as mock_kill_process, \ mock.patch.object(self.proc, '_process') as process_mock: process_mock.wait.side_effect = subprocess.TimeoutExpired( self.proc.cmd, kill_timeout) self.assertTrue( self.proc._kill_process_and_wait( pid, signal.SIGTERM, kill_timeout)) process_mock.wait.assert_called_once_with(kill_timeout) mock_kill_process.assert_has_calls([ mock.call(pid, signal.SIGTERM, self.proc.run_as_root), mock.call(pid, signal.SIGKILL, self.proc.run_as_root)]) def test_stop_calls_kill_with_provided_signal_number(self): self.proc._is_running = True with mock.patch.object(self.proc, '_kill') as mock_kill: self.proc.stop(kill_signal=signal.SIGTERM) mock_kill.assert_called_once_with(signal.SIGTERM, None) def test_stop_raises_exception_if_already_started(self): with testtools.ExpectedException(async_process.AsyncProcessException): self.proc.stop() def test_cmd(self): for expected, cmd in (('ls -l file', ['ls', '-l', 'file']), ('fake', ['fake'])): proc = async_process.AsyncProcess(cmd) self.assertEqual(expected, proc.cmd) class TestAsyncProcessLogging(base.BaseTestCase): def setUp(self): super(TestAsyncProcessLogging, self).setUp() self.log_mock = mock.patch.object(async_process, 'LOG').start() def _test__read_stdout_logging(self, enable): proc = async_process.AsyncProcess(['fakecmd'], log_output=enable) with mock.patch.object(proc, '_read', return_value='fakedata'),\ mock.patch.object(proc, '_process'): proc._read_stdout() self.assertEqual(enable, self.log_mock.debug.called) def _test__read_stderr_logging(self, enable): proc = async_process.AsyncProcess(['fake'], log_output=enable) with mock.patch.object(proc, '_read', return_value='fakedata'),\ mock.patch.object(proc, '_process'): proc._read_stderr() self.assertEqual(enable, self.log_mock.error.called) def test__read_stdout_logging_enabled(self): self._test__read_stdout_logging(enable=True) def test__read_stdout_logging_disabled(self): self._test__read_stdout_logging(enable=False) def test__read_stderr_logging_enabled(self): self._test__read_stderr_logging(enable=True) def test__read_stderr_logging_disabled(self): self._test__read_stderr_logging(enable=False) class TestAsyncProcessDieOnError(base.BaseTestCase): def test__read_stderr_returns_none_on_error(self): proc = async_process.AsyncProcess(['fakecmd'], die_on_error=True) with mock.patch.object(proc, '_read', return_value='fakedata'),\ mock.patch.object(proc, '_process'): self.assertIsNone(proc._read_stderr()) class TestFailingAsyncProcess(base.BaseTestCase): def setUp(self): super(TestFailingAsyncProcess, self).setUp() path = self.get_temp_file_path('async.tmp', self.get_new_temp_dir()) self.process = async_process.AsyncProcess(['python', failing_process.__file__, path], respawn_interval=0) def test_failing_async_process_handle_error_once(self): with mock.patch.object(self.process, '_handle_process_error')\ as handle_error_mock: self.process.start() self.process._process.wait() # Wait for the monitor process to complete for thread in self.process._watchers: thread.wait() self.assertEqual(1, handle_error_mock.call_count) neutron-12.1.1/neutron/tests/unit/agent/linux/test_tc_lib.py0000664000175000017500000002261013553660047024237 0ustar zuulzuul00000000000000# Copyright 2016 OVH SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.services.qos import constants as qos_consts from neutron.agent.linux import tc_lib from neutron.common import constants from neutron.common import utils from neutron.tests import base DEVICE_NAME = "tap_device" KERNEL_HZ_VALUE = 1000 BW_LIMIT = 2000 # [kbps] BURST = 100 # [kbit] LATENCY = 50 # [ms] TC_QDISC_OUTPUT = ( 'qdisc tbf 8011: root refcnt 2 rate %(bw)skbit burst %(burst)skbit ' 'lat 50.0ms \n') % {'bw': BW_LIMIT, 'burst': BURST} TC_FILTERS_OUTPUT = ( 'filter protocol all pref 49152 u32 \nfilter protocol all pref ' '49152 u32 fh 800: ht divisor 1 \nfilter protocol all pref 49152 u32 fh ' '800::800 order 2048 key ht 800 \n match 00000000/00000000 at 0\n ' 'police 0x1e rate %(bw)skbit burst %(burst)skbit mtu 2Kb action \n' 'drop overhead 0b \n ref 1 bind 1' ) % {'bw': BW_LIMIT, 'burst': BURST} class BaseUnitConversionTest(object): def test_convert_to_kilobits_bare_value(self): value = "1000" expected_value = 8 # kbit self.assertEqual( expected_value, tc_lib.convert_to_kilobits(value, self.base_unit) ) def test_convert_to_kilobits_bytes_value(self): value = "1000b" expected_value = 8 # kbit self.assertEqual( expected_value, tc_lib.convert_to_kilobits(value, self.base_unit) ) def test_convert_to_kilobits_bits_value(self): value = "1000bit" expected_value = utils.bits_to_kilobits(1000, self.base_unit) self.assertEqual( expected_value, tc_lib.convert_to_kilobits(value, self.base_unit) ) def test_convert_to_kilobits_megabytes_value(self): value = "1m" expected_value = utils.bits_to_kilobits( self.base_unit ** 2 * 8, self.base_unit) self.assertEqual( expected_value, tc_lib.convert_to_kilobits(value, self.base_unit) ) def test_convert_to_kilobits_megabits_value(self): value = "1mbit" expected_value = utils.bits_to_kilobits( self.base_unit ** 2, self.base_unit) self.assertEqual( expected_value, tc_lib.convert_to_kilobits(value, self.base_unit) ) def test_convert_to_bytes_wrong_unit(self): value = "1Zbit" self.assertRaises( tc_lib.InvalidUnit, tc_lib.convert_to_kilobits, value, self.base_unit ) class TestSIUnitConversions(BaseUnitConversionTest, base.BaseTestCase): base_unit = constants.SI_BASE class TestIECUnitConversions(BaseUnitConversionTest, base.BaseTestCase): base_unit = constants.IEC_BASE class TestTcCommand(base.BaseTestCase): def setUp(self): super(TestTcCommand, self).setUp() self.tc = tc_lib.TcCommand(DEVICE_NAME, KERNEL_HZ_VALUE) self.bw_limit = "%s%s" % (BW_LIMIT, tc_lib.BW_LIMIT_UNIT) self.burst = "%s%s" % (BURST, tc_lib.BURST_UNIT) self.latency = "%s%s" % (LATENCY, tc_lib.LATENCY_UNIT) self.execute = mock.patch('neutron.agent.common.utils.execute').start() def test_check_kernel_hz_lower_then_zero(self): self.assertRaises( tc_lib.InvalidKernelHzValue, tc_lib.TcCommand, DEVICE_NAME, 0 ) self.assertRaises( tc_lib.InvalidKernelHzValue, tc_lib.TcCommand, DEVICE_NAME, -100 ) def test_get_filters_bw_limits(self): self.execute.return_value = TC_FILTERS_OUTPUT bw_limit, burst_limit = self.tc.get_filters_bw_limits() self.assertEqual(BW_LIMIT, bw_limit) self.assertEqual(BURST, burst_limit) def test_get_filters_bw_limits_when_output_not_match(self): output = ( "Some different " "output from command:" "tc filters show dev XXX parent ffff:" ) self.execute.return_value = output bw_limit, burst_limit = self.tc.get_filters_bw_limits() self.assertIsNone(bw_limit) self.assertIsNone(burst_limit) def test_get_filters_bw_limits_when_wrong_units(self): output = TC_FILTERS_OUTPUT.replace("kbit", "Xbit") self.execute.return_value = output self.assertRaises(tc_lib.InvalidUnit, self.tc.get_filters_bw_limits) def test_get_tbf_bw_limits(self): self.execute.return_value = TC_QDISC_OUTPUT bw_limit, burst_limit = self.tc.get_tbf_bw_limits() self.assertEqual(BW_LIMIT, bw_limit) self.assertEqual(BURST, burst_limit) def test_get_tbf_bw_limits_when_wrong_qdisc(self): output = TC_QDISC_OUTPUT.replace("tbf", "different_qdisc") self.execute.return_value = output bw_limit, burst_limit = self.tc.get_tbf_bw_limits() self.assertIsNone(bw_limit) self.assertIsNone(burst_limit) def test_get_tbf_bw_limits_when_wrong_units(self): output = TC_QDISC_OUTPUT.replace("kbit", "Xbit") self.execute.return_value = output self.assertRaises(tc_lib.InvalidUnit, self.tc.get_tbf_bw_limits) def test_set_tbf_bw_limit(self): self.tc.set_tbf_bw_limit(BW_LIMIT, BURST, LATENCY) self.execute.assert_called_once_with( ["tc", "qdisc", "replace", "dev", DEVICE_NAME, "root", "tbf", "rate", self.bw_limit, "latency", self.latency, "burst", self.burst], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None ) def test_update_filters_bw_limit(self): self.tc.update_filters_bw_limit(BW_LIMIT, BURST) self.execute.assert_has_calls([ mock.call( ["tc", "qdisc", "del", "dev", DEVICE_NAME, "ingress"], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=[1, 2] ), mock.call( ['tc', 'qdisc', 'add', 'dev', DEVICE_NAME, "ingress", "handle", tc_lib.INGRESS_QDISC_ID], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None ), mock.call( ['tc', 'filter', 'add', 'dev', DEVICE_NAME, 'parent', tc_lib.INGRESS_QDISC_ID, 'protocol', 'all', 'prio', '49', 'basic', 'police', 'rate', self.bw_limit, 'burst', self.burst, 'mtu', tc_lib.MAX_MTU_VALUE, 'drop'], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None )] ) def test_update_tbf_bw_limit(self): self.tc.update_tbf_bw_limit(BW_LIMIT, BURST, LATENCY) self.execute.assert_called_once_with( ["tc", "qdisc", "replace", "dev", DEVICE_NAME, "root", "tbf", "rate", self.bw_limit, "latency", self.latency, "burst", self.burst], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None ) def test_delete_filters_bw_limit(self): self.tc.delete_filters_bw_limit() self.execute.assert_called_once_with( ["tc", "qdisc", "del", "dev", DEVICE_NAME, "ingress"], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=[1, 2] ) def test_delete_tbf_bw_limit(self): self.tc.delete_tbf_bw_limit() self.execute.assert_called_once_with( ["tc", "qdisc", "del", "dev", DEVICE_NAME, "root"], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=[1, 2] ) def test_get_ingress_qdisc_burst_value_burst_not_none(self): self.assertEqual( BURST, self.tc.get_ingress_qdisc_burst_value(BW_LIMIT, BURST) ) def test_get_ingress_qdisc_burst_no_burst_value_given(self): expected_burst = BW_LIMIT * qos_consts.DEFAULT_BURST_RATE self.assertEqual( expected_burst, self.tc.get_ingress_qdisc_burst_value(BW_LIMIT, None) ) def test_get_ingress_qdisc_burst_burst_value_zero(self): expected_burst = BW_LIMIT * qos_consts.DEFAULT_BURST_RATE self.assertEqual( expected_burst, self.tc.get_ingress_qdisc_burst_value(BW_LIMIT, 0) ) def test__get_tbf_burst_value_when_burst_bigger_then_minimal(self): result = self.tc._get_tbf_burst_value(BW_LIMIT, BURST) self.assertEqual(BURST, result) def test__get_tbf_burst_value_when_burst_smaller_then_minimal(self): result = self.tc._get_tbf_burst_value(BW_LIMIT, 0) self.assertEqual(2, result) neutron-12.1.1/neutron/tests/unit/agent/linux/__init__.py0000664000175000017500000000000013553660046023467 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/linux/test_daemon.py0000664000175000017500000002676113553660047024261 0ustar zuulzuul00000000000000# # Copyright 2012 New Dream Network, LLC (DreamHost) # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from logging import handlers import os import sys import mock import testtools from neutron.agent.linux import daemon from neutron.common import exceptions from neutron.tests import base from neutron.tests import tools FAKE_FD = 8 class FakeEntry(object): def __init__(self, name, value): setattr(self, name, value) class TestUnwatchLog(base.BaseTestCase): def test_unwatch_log(self): temp_file_name = self.get_temp_file_path('unwatch_log_temp_file_name') stream_handler = logging.StreamHandler() logger = logging.Logger('fake') logger.addHandler(stream_handler) logger.addHandler(handlers.WatchedFileHandler(temp_file_name)) with mock.patch('logging.getLogger', return_value=logger): daemon.unwatch_log() self.assertEqual(2, len(logger.handlers)) logger.handlers.remove(stream_handler) observed = logger.handlers[0] self.assertEqual(logging.FileHandler, type(observed)) self.assertEqual(temp_file_name, observed.baseFilename) class TestPrivileges(base.BaseTestCase): def test_setuid_with_name(self): with mock.patch('pwd.getpwnam', return_value=FakeEntry('pw_uid', 123)): with mock.patch('os.setuid') as setuid_mock: daemon.setuid('user') setuid_mock.assert_called_once_with(123) def test_setuid_with_id(self): with mock.patch('os.setuid') as setuid_mock: daemon.setuid('321') setuid_mock.assert_called_once_with(321) def test_setuid_fails(self): with mock.patch('os.setuid', side_effect=OSError()): with mock.patch.object(daemon.LOG, 'critical') as log_critical: self.assertRaises(exceptions.FailToDropPrivilegesExit, daemon.setuid, '321') log_critical.assert_called_once_with(mock.ANY) def test_setgid_with_name(self): with mock.patch('grp.getgrnam', return_value=FakeEntry('gr_gid', 123)): with mock.patch('os.setgid') as setgid_mock: daemon.setgid('group') setgid_mock.assert_called_once_with(123) def test_setgid_with_id(self): with mock.patch('os.setgid') as setgid_mock: daemon.setgid('321') setgid_mock.assert_called_once_with(321) def test_setgid_fails(self): with mock.patch('os.setgid', side_effect=OSError()): with mock.patch.object(daemon.LOG, 'critical') as log_critical: self.assertRaises(exceptions.FailToDropPrivilegesExit, daemon.setgid, '321') log_critical.assert_called_once_with(mock.ANY) @mock.patch.object(os, 'setgroups') @mock.patch.object(daemon, 'setgid') @mock.patch.object(daemon, 'setuid') def test_drop_no_privileges(self, mock_setuid, mock_setgid, mock_setgroups): daemon.drop_privileges() for cursor in (mock_setuid, mock_setgid, mock_setgroups): self.assertFalse(cursor.called) @mock.patch.object(os, 'geteuid', return_value=0) @mock.patch.object(os, 'setgroups') @mock.patch.object(daemon, 'setgid') @mock.patch.object(daemon, 'setuid') def _test_drop_privileges(self, setuid, setgid, setgroups, geteuid, user=None, group=None): daemon.drop_privileges(user=user, group=group) if user: setuid.assert_called_once_with(user) else: self.assertFalse(setuid.called) if group: setgroups.assert_called_once_with([]) setgid.assert_called_once_with(group) else: self.assertFalse(setgroups.called) self.assertFalse(setgid.called) def test_drop_user_privileges(self): self._test_drop_privileges(user='user') def test_drop_uid_privileges(self): self._test_drop_privileges(user='321') def test_drop_group_privileges(self): self._test_drop_privileges(group='group') def test_drop_gid_privileges(self): self._test_drop_privileges(group='654') def test_drop_privileges_without_root_permissions(self): with mock.patch('os.geteuid', return_value=1): with mock.patch.object(daemon.LOG, 'critical') as log_critical: self.assertRaises(exceptions.FailToDropPrivilegesExit, daemon.drop_privileges, 'user') log_critical.assert_called_once_with(mock.ANY) class TestPidfile(base.BaseTestCase): def setUp(self): super(TestPidfile, self).setUp() self.os_p = mock.patch.object(daemon, 'os') self.os = self.os_p.start() self.os.open.return_value = FAKE_FD self.fcntl_p = mock.patch.object(daemon, 'fcntl') self.fcntl = self.fcntl_p.start() self.fcntl.flock.return_value = 0 def test_init(self): self.os.O_CREAT = os.O_CREAT self.os.O_RDWR = os.O_RDWR daemon.Pidfile('thefile', 'python') self.os.open.assert_called_once_with('thefile', os.O_CREAT | os.O_RDWR) self.fcntl.flock.assert_called_once_with(FAKE_FD, self.fcntl.LOCK_EX | self.fcntl.LOCK_NB) def test_init_open_fail(self): self.os.open.side_effect = IOError with mock.patch.object(daemon.sys, 'stderr'): with testtools.ExpectedException(SystemExit): daemon.Pidfile('thefile', 'python') sys.assert_has_calls([ mock.call.stderr.write(mock.ANY), mock.call.exit(1)] ) def test_unlock(self): p = daemon.Pidfile('thefile', 'python') p.unlock() self.fcntl.flock.assert_has_calls([ mock.call(FAKE_FD, self.fcntl.LOCK_EX | self.fcntl.LOCK_NB), mock.call(FAKE_FD, self.fcntl.LOCK_UN)] ) def test_write(self): p = daemon.Pidfile('thefile', 'python') p.write(34) self.os.assert_has_calls([ mock.call.ftruncate(FAKE_FD, 0), mock.call.write(FAKE_FD, b'34'), mock.call.fsync(FAKE_FD)] ) def test_read(self): self.os.read.return_value = '34' p = daemon.Pidfile('thefile', 'python') self.assertEqual(34, p.read()) def test_is_running(self): mock_open = self.useFixture( tools.OpenFixture('/proc/34/cmdline', 'python')).mock_open p = daemon.Pidfile('thefile', 'python') with mock.patch.object(p, 'read') as read: read.return_value = 34 self.assertTrue(p.is_running()) mock_open.assert_called_once_with('/proc/34/cmdline', 'r') def test_is_running_uuid_true(self): mock_open = self.useFixture( tools.OpenFixture('/proc/34/cmdline', 'python 1234')).mock_open p = daemon.Pidfile('thefile', 'python', uuid='1234') with mock.patch.object(p, 'read') as read: read.return_value = 34 self.assertTrue(p.is_running()) mock_open.assert_called_once_with('/proc/34/cmdline', 'r') def test_is_running_uuid_false(self): mock_open = self.useFixture( tools.OpenFixture('/proc/34/cmdline', 'python 1234')).mock_open p = daemon.Pidfile('thefile', 'python', uuid='6789') with mock.patch.object(p, 'read') as read: read.return_value = 34 self.assertFalse(p.is_running()) mock_open.assert_called_once_with('/proc/34/cmdline', 'r') class TestDaemon(base.BaseTestCase): def setUp(self): super(TestDaemon, self).setUp() self.os_p = mock.patch.object(daemon, 'os') self.os = self.os_p.start() self.pidfile_p = mock.patch.object(daemon, 'Pidfile') self.pidfile = self.pidfile_p.start() def test_init(self): d = daemon.Daemon('pidfile') self.assertEqual(d.procname, 'python') def test_init_nopidfile(self): d = daemon.Daemon(pidfile=None) self.assertEqual(d.procname, 'python') self.assertFalse(self.pidfile.called) def test_fork_parent(self): self.os.fork.return_value = 1 d = daemon.Daemon('pidfile') d._fork() self.os._exit.assert_called_once_with(mock.ANY) def test_fork_child(self): self.os.fork.return_value = 0 d = daemon.Daemon('pidfile') self.assertIsNone(d._fork()) def test_fork_error(self): self.os.fork.side_effect = OSError(1) with mock.patch.object(daemon.sys, 'stderr'): with testtools.ExpectedException(SystemExit): d = daemon.Daemon('pidfile', 'stdin') d._fork() def test_daemonize(self): self.os.devnull = '/dev/null' d = daemon.Daemon('pidfile') with mock.patch.object(d, '_fork') as fork: with mock.patch.object(daemon, 'atexit') as atexit: with mock.patch.object(daemon, 'signal') as signal: signal.SIGTERM = 15 with mock.patch.object(daemon, 'sys') as sys: sys.stdin.fileno.return_value = 0 sys.stdout.fileno.return_value = 1 sys.stderr.fileno.return_value = 2 d.daemonize() signal.signal.assert_called_once_with(15, d.handle_sigterm) atexit.register.assert_called_once_with(d.delete_pid) fork.assert_has_calls([mock.call(), mock.call()]) self.os.assert_has_calls([ mock.call.chdir('/'), mock.call.setsid(), mock.call.umask(0), mock.call.dup2(mock.ANY, 0), mock.call.dup2(mock.ANY, 1), mock.call.dup2(mock.ANY, 2), mock.call.getpid()] ) def test_delete_pid(self): self.pidfile.return_value.__str__.return_value = 'pidfile' d = daemon.Daemon('pidfile') d.delete_pid() self.os.remove.assert_called_once_with('pidfile') def test_handle_sigterm(self): d = daemon.Daemon('pidfile') with mock.patch.object(daemon, 'sys') as sys: d.handle_sigterm(15, 1234) sys.exit.assert_called_once_with(0) def test_start(self): self.pidfile.return_value.is_running.return_value = False d = daemon.Daemon('pidfile') with mock.patch.object(d, 'daemonize') as daemonize: with mock.patch.object(d, 'run') as run: d.start() run.assert_called_once_with() daemonize.assert_called_once_with() def test_start_running(self): self.pidfile.return_value.is_running.return_value = True d = daemon.Daemon('pidfile') with mock.patch.object(daemon.sys, 'stderr'): with mock.patch.object(d, 'daemonize') as daemonize: with testtools.ExpectedException(SystemExit): d.start() self.assertFalse(daemonize.called) neutron-12.1.1/neutron/tests/unit/agent/linux/test_ipset_manager.py0000664000175000017500000001447713553660046025634 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron.agent.linux import ipset_manager from neutron.tests import base TEST_SET_ID = 'fake_sgid' ETHERTYPE = 'IPv4' TEST_SET_NAME = ipset_manager.IpsetManager.get_name(TEST_SET_ID, ETHERTYPE) TEST_SET_NAME_NEW = TEST_SET_NAME + ipset_manager.SWAP_SUFFIX FAKE_IPS = ['10.0.0.1', '10.0.0.2', '10.0.0.3', '10.0.0.4', '10.0.0.5', '10.0.0.6'] class BaseIpsetManagerTest(base.BaseTestCase): def setUp(self): super(BaseIpsetManagerTest, self).setUp() self.ipset = ipset_manager.IpsetManager() self.execute = mock.patch.object(self.ipset, "execute").start() self.expected_calls = [] self.expect_create() self.force_sorted_get_set_ips() def force_sorted_get_set_ips(self): """Force sorted responses by self.ipset._get_new/deleted_set_ips. _get_new/deleted_set_ips use internally sets and return randomly ordered responses. This method ensures sorted responses from them in order to guarantee call order in self.ipset.set_members. """ original_get_new_set_ips = self.ipset._get_new_set_ips original_get_deleted_set_ips = self.ipset._get_deleted_set_ips def sorted_get_new_set_ips(set_name, expected_ips): unsorted = original_get_new_set_ips(set_name, expected_ips) return sorted(unsorted) def sorted_get_deleted_set_ips(set_name, expected_ips): unsorted = original_get_deleted_set_ips(set_name, expected_ips) return sorted(unsorted) mock.patch.object(self.ipset, '_get_new_set_ips', side_effect=sorted_get_new_set_ips).start() mock.patch.object(self.ipset, '_get_deleted_set_ips', side_effect=sorted_get_deleted_set_ips).start() def verify_mock_calls(self): self.execute.assert_has_calls(self.expected_calls, any_order=False) def expect_set(self, addresses): temp_input = ['create %s hash:net family inet' % TEST_SET_NAME_NEW] temp_input.extend('add %s %s' % (TEST_SET_NAME_NEW, ip) for ip in self.ipset._sanitize_addresses(addresses)) input = '\n'.join(temp_input) self.expected_calls.extend([ mock.call(['ipset', 'restore', '-exist'], process_input=input, run_as_root=True, check_exit_code=True), mock.call(['ipset', 'swap', TEST_SET_NAME_NEW, TEST_SET_NAME], process_input=None, run_as_root=True, check_exit_code=True), mock.call(['ipset', 'destroy', TEST_SET_NAME_NEW], process_input=None, run_as_root=True, check_exit_code=False)]) def expect_add(self, addresses): self.expected_calls.extend( mock.call(['ipset', 'add', '-exist', TEST_SET_NAME, ip], process_input=None, run_as_root=True, check_exit_code=True) for ip in self.ipset._sanitize_addresses(addresses)) def expect_del(self, addresses): self.expected_calls.extend( mock.call(['ipset', 'del', TEST_SET_NAME, ip], process_input=None, run_as_root=True, check_exit_code=False) for ip in self.ipset._sanitize_addresses(addresses)) def expect_create(self): self.expected_calls.append( mock.call(['ipset', 'create', '-exist', TEST_SET_NAME, 'hash:net', 'family', 'inet'], process_input=None, run_as_root=True, check_exit_code=True)) def expect_destroy(self): self.expected_calls.append( mock.call(['ipset', 'destroy', TEST_SET_NAME], process_input=None, run_as_root=True, check_exit_code=False)) def add_first_ip(self): self.expect_set([FAKE_IPS[0]]) self.ipset.set_members(TEST_SET_ID, ETHERTYPE, [FAKE_IPS[0]]) def add_all_ips(self): self.expect_set(FAKE_IPS) self.ipset.set_members(TEST_SET_ID, ETHERTYPE, FAKE_IPS) class IpsetManagerTestCase(BaseIpsetManagerTest): def test_set_name_exists(self): self.add_first_ip() self.assertTrue(self.ipset.set_name_exists('N' + ETHERTYPE + TEST_SET_ID)) def test_set_members_with_first_add_member(self): self.add_first_ip() self.verify_mock_calls() def test_set_members_adding_less_than_5(self): self.add_first_ip() self.expect_add(FAKE_IPS[1:5]) self.ipset.set_members(TEST_SET_ID, ETHERTYPE, FAKE_IPS[0:5]) self.verify_mock_calls() def test_set_members_deleting_less_than_5(self): self.add_all_ips() self.expect_del(FAKE_IPS[3:]) self.ipset.set_members(TEST_SET_ID, ETHERTYPE, FAKE_IPS[0:3]) self.verify_mock_calls() def test_set_members_adding_more_than_5(self): self.add_first_ip() self.expect_set(FAKE_IPS) self.ipset.set_members(TEST_SET_ID, ETHERTYPE, FAKE_IPS) self.verify_mock_calls() def test_set_members_adding_all_zero_ipv4(self): self.expect_set(['0.0.0.0/0']) self.ipset.set_members(TEST_SET_ID, ETHERTYPE, ['0.0.0.0/0']) self.verify_mock_calls() def test_set_members_adding_all_zero_ipv6(self): self.expect_set(['::/0']) self.ipset.set_members(TEST_SET_ID, ETHERTYPE, ['::/0']) self.verify_mock_calls() def test_destroy(self): self.add_first_ip() self.expect_destroy() self.ipset.destroy(TEST_SET_ID, ETHERTYPE) self.verify_mock_calls() neutron-12.1.1/neutron/tests/unit/agent/linux/test_dhcp.py0000664000175000017500000036761213553660047023737 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import mock import netaddr from neutron_lib.api.definitions import extra_dhcp_opt as edo_ext from neutron_lib import constants from oslo_config import cfg import oslo_messaging from oslo_utils import fileutils import testtools from neutron.agent.linux import dhcp from neutron.common import exceptions as n_exc from neutron.conf.agent import common as config from neutron.conf.agent import dhcp as dhcp_config from neutron.conf import common as base_config from neutron.tests import base from neutron.tests import tools class FakeIPAllocation(object): def __init__(self, address, subnet_id=None): self.ip_address = address self.subnet_id = subnet_id class FakeDNSAssignment(object): def __init__(self, ip_address, dns_name='', domain='openstacklocal'): if dns_name: self.hostname = dns_name else: self.hostname = 'host-%s' % ip_address.replace( '.', '-').replace(':', '-') self.ip_address = ip_address self.fqdn = self.hostname if domain: self.fqdn = '%s.%s.' % (self.hostname, domain) class DhcpOpt(object): def __init__(self, **kwargs): self.__dict__.update(ip_version=4) self.__dict__.update(kwargs) def __str__(self): return str(self.__dict__) # A base class where class attributes can also be accessed by treating # an instance as a dict. class Dictable(object): def __getitem__(self, k): return self.__class__.__dict__.get(k) class FakeDhcpPort(object): def __init__(self): self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa' self.admin_state_up = True self.device_owner = constants.DEVICE_OWNER_DHCP self.fixed_ips = [ FakeIPAllocation('192.168.0.1', 'dddddddd-dddd-dddd-dddd-dddddddddddd')] self.mac_address = '00:00:80:aa:bb:ee' self.device_id = 'fake_dhcp_port' self.extra_dhcp_opts = [] class FakeReservedPort(object): def __init__(self, id='reserved-aaaa-aaaa-aaaa-aaaaaaaaaaa'): self.admin_state_up = True self.device_owner = constants.DEVICE_OWNER_DHCP self.fixed_ips = [ FakeIPAllocation('192.168.0.6', 'dddddddd-dddd-dddd-dddd-dddddddddddd'), FakeIPAllocation('fdca:3ba5:a17a:4ba3::2', 'ffffffff-ffff-ffff-ffff-ffffffffffff')] self.mac_address = '00:00:80:aa:bb:ee' self.device_id = constants.DEVICE_ID_RESERVED_DHCP_PORT self.extra_dhcp_opts = [] self.id = id class FakePort1(object): def __init__(self, domain='openstacklocal'): self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' self.admin_state_up = True self.device_owner = 'foo1' self.fixed_ips = [ FakeIPAllocation('192.168.0.2', 'dddddddd-dddd-dddd-dddd-dddddddddddd')] self.mac_address = '00:00:80:aa:bb:cc' self.device_id = 'fake_port1' self.extra_dhcp_opts = [] self.dns_assignment = [FakeDNSAssignment('192.168.0.2', domain=domain)] class FakePort2(object): def __init__(self): self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' self.admin_state_up = False self.device_owner = 'foo2' self.fixed_ips = [ FakeIPAllocation('192.168.0.3', 'dddddddd-dddd-dddd-dddd-dddddddddddd')] self.mac_address = '00:00:f3:aa:bb:cc' self.device_id = 'fake_port2' self.dns_assignment = [FakeDNSAssignment('192.168.0.3')] self.extra_dhcp_opts = [] class FakePort3(object): def __init__(self): self.id = '44444444-4444-4444-4444-444444444444' self.admin_state_up = True self.device_owner = 'foo3' self.fixed_ips = [ FakeIPAllocation('192.168.0.4', 'dddddddd-dddd-dddd-dddd-dddddddddddd'), FakeIPAllocation('192.168.1.2', 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')] self.dns_assignment = [FakeDNSAssignment('192.168.0.4'), FakeDNSAssignment('192.168.1.2')] self.mac_address = '00:00:0f:aa:bb:cc' self.device_id = 'fake_port3' self.extra_dhcp_opts = [] class FakePort4(object): def __init__(self): self.id = 'gggggggg-gggg-gggg-gggg-gggggggggggg' self.admin_state_up = False self.device_owner = 'foo3' self.fixed_ips = [ FakeIPAllocation('192.168.0.4', 'dddddddd-dddd-dddd-dddd-dddddddddddd'), FakeIPAllocation('ffda:3ba5:a17a:4ba3:0216:3eff:fec2:771d', 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')] self.dns_assignment = [ FakeDNSAssignment('192.168.0.4'), FakeDNSAssignment('ffda:3ba5:a17a:4ba3:0216:3eff:fec2:771d')] self.mac_address = '00:16:3E:C2:77:1D' self.device_id = 'fake_port4' self.extra_dhcp_opts = [] class FakePort5(object): def __init__(self): self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeee' self.admin_state_up = True self.device_owner = 'foo5' self.fixed_ips = [ FakeIPAllocation('192.168.0.5', 'dddddddd-dddd-dddd-dddd-dddddddddddd')] self.dns_assignment = [FakeDNSAssignment('192.168.0.5')] self.mac_address = '00:00:0f:aa:bb:55' self.device_id = 'fake_port5' self.extra_dhcp_opts = [ DhcpOpt(opt_name=edo_ext.DHCP_OPT_CLIENT_ID, opt_value='test5')] class FakePort6(object): def __init__(self): self.id = 'ccccccccc-cccc-cccc-cccc-ccccccccc' self.admin_state_up = True self.device_owner = 'foo6' self.fixed_ips = [ FakeIPAllocation('192.168.0.6', 'dddddddd-dddd-dddd-dddd-dddddddddddd')] self.dns_assignment = [FakeDNSAssignment('192.168.0.6')] self.mac_address = '00:00:0f:aa:bb:66' self.device_id = 'fake_port6' self.extra_dhcp_opts = [ DhcpOpt(opt_name=edo_ext.DHCP_OPT_CLIENT_ID, opt_value='test6', ip_version=4), DhcpOpt(opt_name='dns-server', opt_value='123.123.123.45', ip_version=4)] class FakeV6Port(object): def __init__(self, domain='openstacklocal'): self.id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh' self.admin_state_up = True self.device_owner = 'foo3' self.fixed_ips = [ FakeIPAllocation('fdca:3ba5:a17a:4ba3::2', 'ffffffff-ffff-ffff-ffff-ffffffffffff')] self.mac_address = '00:00:f3:aa:bb:cc' self.device_id = 'fake_port6' self.extra_dhcp_opts = [] self.dns_assignment = [FakeDNSAssignment('fdca:3ba5:a17a:4ba3::2', domain=domain)] class FakeV6PortExtraOpt(object): def __init__(self): self.id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh' self.admin_state_up = True self.device_owner = 'foo3' self.fixed_ips = [ FakeIPAllocation('ffea:3ba5:a17a:4ba3:0216:3eff:fec2:771d', 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')] self.dns_assignment = [ FakeDNSAssignment('ffea:3ba5:a17a:4ba3:0216:3eff:fec2:771d')] self.mac_address = '00:16:3e:c2:77:1d' self.device_id = 'fake_port6' self.extra_dhcp_opts = [ DhcpOpt(opt_name='dns-server', opt_value='ffea:3ba5:a17a:4ba3::100', ip_version=6)] class FakeDualPortWithV6ExtraOpt(object): def __init__(self): self.id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh' self.admin_state_up = True self.device_owner = 'foo3' self.fixed_ips = [ FakeIPAllocation('192.168.0.3', 'dddddddd-dddd-dddd-dddd-dddddddddddd'), FakeIPAllocation('ffea:3ba5:a17a:4ba3:0216:3eff:fec2:771d', 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')] self.dns_assignment = [ FakeDNSAssignment('192.168.0.3'), FakeDNSAssignment('ffea:3ba5:a17a:4ba3:0216:3eff:fec2:771d')] self.mac_address = '00:16:3e:c2:77:1d' self.device_id = 'fake_port6' self.extra_dhcp_opts = [ DhcpOpt(opt_name='dns-server', opt_value='ffea:3ba5:a17a:4ba3::100', ip_version=6)] class FakeDualPort(object): def __init__(self, domain='openstacklocal'): self.id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh' self.admin_state_up = True self.device_owner = 'foo3' self.fixed_ips = [ FakeIPAllocation('192.168.0.3', 'dddddddd-dddd-dddd-dddd-dddddddddddd'), FakeIPAllocation('fdca:3ba5:a17a:4ba3::3', 'ffffffff-ffff-ffff-ffff-ffffffffffff')] self.mac_address = '00:00:0f:aa:bb:cc' self.device_id = 'fake_dual_port' self.extra_dhcp_opts = [] self.dns_assignment = [FakeDNSAssignment('192.168.0.3', domain=domain), FakeDNSAssignment('fdca:3ba5:a17a:4ba3::3', domain=domain)] class FakeRouterPort(object): def __init__(self, dev_owner=constants.DEVICE_OWNER_ROUTER_INTF, ip_address='192.168.0.1', domain='openstacklocal'): self.id = 'rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr' self.admin_state_up = True self.device_owner = constants.DEVICE_OWNER_ROUTER_INTF self.mac_address = '00:00:0f:rr:rr:rr' self.device_id = 'fake_router_port' self.dns_assignment = [] self.extra_dhcp_opts = [] self.device_owner = dev_owner self.fixed_ips = [FakeIPAllocation( ip_address, 'dddddddd-dddd-dddd-dddd-dddddddddddd')] self.dns_assignment = [FakeDNSAssignment(ip.ip_address, domain=domain) for ip in self.fixed_ips] class FakeRouterPortNoDHCP(object): def __init__(self, dev_owner=constants.DEVICE_OWNER_ROUTER_INTF, ip_address='192.168.0.1', domain='openstacklocal'): self.id = 'ssssssss-ssss-ssss-ssss-ssssssssssss' self.admin_state_up = True self.device_owner = constants.DEVICE_OWNER_ROUTER_INTF self.mac_address = '00:00:0f:rr:rr:rr' self.device_id = 'fake_router_port_no_dhcp' self.dns_assignment = [] self.extra_dhcp_opts = [] self.device_owner = dev_owner self.fixed_ips = [FakeIPAllocation( ip_address, 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')] self.dns_assignment = [FakeDNSAssignment(ip.ip_address, domain=domain) for ip in self.fixed_ips] class FakeRouterPort2(object): def __init__(self): self.id = 'rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr' self.admin_state_up = True self.device_owner = constants.DEVICE_OWNER_ROUTER_INTF self.fixed_ips = [ FakeIPAllocation('192.168.1.1', 'dddddddd-dddd-dddd-dddd-dddddddddddd')] self.dns_assignment = [FakeDNSAssignment('192.168.1.1')] self.mac_address = '00:00:0f:rr:rr:r2' self.device_id = 'fake_router_port2' self.extra_dhcp_opts = [] class FakeRouterPortSegmentID(object): def __init__(self): self.id = 'qqqqqqqq-qqqq-qqqq-qqqq-qqqqqqqqqqqq' self.admin_state_up = True self.device_owner = constants.DEVICE_OWNER_ROUTER_INTF self.fixed_ips = [ FakeIPAllocation('192.168.2.1', 'iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii')] self.dns_assignment = [FakeDNSAssignment('192.168.2.1')] self.mac_address = '00:00:0f:rr:rr:r3' self.device_id = 'fake_router_port3' self.extra_dhcp_opts = [] class FakePortMultipleAgents1(object): def __init__(self): self.id = 'rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr' self.admin_state_up = True self.device_owner = constants.DEVICE_OWNER_DHCP self.fixed_ips = [ FakeIPAllocation('192.168.0.5', 'dddddddd-dddd-dddd-dddd-dddddddddddd')] self.dns_assignment = [FakeDNSAssignment('192.168.0.5')] self.mac_address = '00:00:0f:dd:dd:dd' self.device_id = 'fake_multiple_agents_port' self.extra_dhcp_opts = [] class FakePortMultipleAgents2(object): def __init__(self): self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' self.admin_state_up = True self.device_owner = constants.DEVICE_OWNER_DHCP self.fixed_ips = [ FakeIPAllocation('192.168.0.6', 'dddddddd-dddd-dddd-dddd-dddddddddddd')] self.dns_assignment = [FakeDNSAssignment('192.168.0.6')] self.mac_address = '00:00:0f:ee:ee:ee' self.device_id = 'fake_multiple_agents_port2' self.extra_dhcp_opts = [] class FakePortWithClientIdNum(object): def __init__(self): self.extra_dhcp_opts = [ DhcpOpt(opt_name=dhcp.DHCP_OPT_CLIENT_ID_NUM, opt_value='test_client_id_num')] class FakeV4HostRoute(object): def __init__(self): self.destination = '20.0.0.1/24' self.nexthop = '20.0.0.1' class FakeV4HostRouteGateway(object): def __init__(self): self.destination = constants.IPv4_ANY self.nexthop = '10.0.0.1' class FakeV6HostRoute(object): def __init__(self): self.destination = '2001:0200:feed:7ac0::/64' self.nexthop = '2001:0200:feed:7ac0::1' class FakeV4Subnet(Dictable): def __init__(self): self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' self.ip_version = 4 self.cidr = '192.168.0.0/24' self.gateway_ip = '192.168.0.1' self.enable_dhcp = True self.host_routes = [FakeV4HostRoute()] self.dns_nameservers = ['8.8.8.8'] class FakeV4Subnet2(FakeV4Subnet): def __init__(self): super(FakeV4Subnet2, self).__init__() self.cidr = '192.168.1.0/24' self.gateway_ip = '192.168.1.1' self.host_routes = [] class FakeV4SubnetSegmentID(FakeV4Subnet): def __init__(self): super(FakeV4SubnetSegmentID, self).__init__() self.id = 'iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii' self.cidr = '192.168.2.0/24' self.gateway_ip = '192.168.2.1' self.host_routes = [] self.segment_id = 1 class FakeV4SubnetSegmentID2(FakeV4Subnet): def __init__(self): super(FakeV4SubnetSegmentID2, self).__init__() self.id = 'jjjjjjjj-jjjj-jjjj-jjjj-jjjjjjjjjjjj' self.host_routes = [] self.segment_id = 2 class FakeV4MetadataSubnet(FakeV4Subnet): def __init__(self): super(FakeV4MetadataSubnet, self).__init__() self.cidr = '169.254.169.254/30' self.gateway_ip = '169.254.169.253' self.host_routes = [] self.dns_nameservers = [] class FakeV4SubnetGatewayRoute(FakeV4Subnet): def __init__(self): super(FakeV4SubnetGatewayRoute, self).__init__() self.host_routes = [FakeV4HostRouteGateway()] class FakeV4SubnetMultipleAgentsWithoutDnsProvided(FakeV4Subnet): def __init__(self): super(FakeV4SubnetMultipleAgentsWithoutDnsProvided, self).__init__() self.dns_nameservers = [] self.host_routes = [] class FakeV4SubnetAgentWithManyDnsProvided(FakeV4Subnet): def __init__(self): super(FakeV4SubnetAgentWithManyDnsProvided, self).__init__() self.dns_nameservers = ['2.2.2.2', '9.9.9.9', '1.1.1.1', '3.3.3.3'] self.host_routes = [] class FakeV4SubnetAgentWithNoDnsProvided(FakeV4Subnet): def __init__(self): super(FakeV4SubnetAgentWithNoDnsProvided, self).__init__() self.dns_nameservers = ['0.0.0.0'] self.host_routes = [] class FakeV4MultipleAgentsWithoutDnsProvided(object): def __init__(self): self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' self.subnets = [FakeV4SubnetMultipleAgentsWithoutDnsProvided()] self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(), FakePortMultipleAgents1(), FakePortMultipleAgents2()] self.namespace = 'qdhcp-ns' class FakeV4AgentWithoutDnsProvided(object): def __init__(self): self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' self.subnets = [FakeV4SubnetMultipleAgentsWithoutDnsProvided()] self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(), FakePortMultipleAgents1()] self.namespace = 'qdhcp-ns' class FakeV4AgentWithManyDnsProvided(object): def __init__(self): self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' self.subnets = [FakeV4SubnetAgentWithManyDnsProvided()] self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(), FakePortMultipleAgents1()] self.namespace = 'qdhcp-ns' class FakeV4AgentWithNoDnsProvided(object): def __init__(self): self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' self.subnets = [FakeV4SubnetAgentWithNoDnsProvided()] self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(), FakePortMultipleAgents1()] self.namespace = 'qdhcp-ns' class FakeV4SubnetMultipleAgentsWithDnsProvided(FakeV4Subnet): def __init__(self): super(FakeV4SubnetMultipleAgentsWithDnsProvided, self).__init__() self.host_routes = [] class FakeV4MultipleAgentsWithDnsProvided(object): def __init__(self): self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' self.subnets = [FakeV4SubnetMultipleAgentsWithDnsProvided()] self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(), FakePortMultipleAgents1(), FakePortMultipleAgents2()] self.namespace = 'qdhcp-ns' class FakeV6Subnet(object): def __init__(self): self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' self.ip_version = 6 self.cidr = 'fdca:3ba5:a17a:4ba3::/64' self.gateway_ip = 'fdca:3ba5:a17a:4ba3::1' self.enable_dhcp = True self.host_routes = [FakeV6HostRoute()] self.dns_nameservers = ['2001:0200:feed:7ac0::1'] self.ipv6_ra_mode = None self.ipv6_address_mode = None class FakeV4SubnetNoDHCP(object): def __init__(self): self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' self.ip_version = 4 self.cidr = '192.168.1.0/24' self.gateway_ip = '192.168.1.1' self.enable_dhcp = False self.host_routes = [] self.dns_nameservers = [] class FakeV6SubnetDHCPStateful(Dictable): def __init__(self): self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' self.ip_version = 6 self.cidr = 'fdca:3ba5:a17a:4ba3::/64' self.gateway_ip = 'fdca:3ba5:a17a:4ba3::1' self.enable_dhcp = True self.host_routes = [FakeV6HostRoute()] self.dns_nameservers = ['2001:0200:feed:7ac0::1'] self.ipv6_ra_mode = None self.ipv6_address_mode = constants.DHCPV6_STATEFUL class FakeV6SubnetSlaac(object): def __init__(self): self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' self.ip_version = 6 self.cidr = 'ffda:3ba5:a17a:4ba3::/64' self.gateway_ip = 'ffda:3ba5:a17a:4ba3::1' self.enable_dhcp = True self.host_routes = [FakeV6HostRoute()] self.ipv6_address_mode = constants.IPV6_SLAAC self.ipv6_ra_mode = None class FakeV6SubnetStateless(object): def __init__(self): self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' self.ip_version = 6 self.cidr = 'ffea:3ba5:a17a:4ba3::/64' self.gateway_ip = 'ffea:3ba5:a17a:4ba3::1' self.enable_dhcp = True self.dns_nameservers = [] self.host_routes = [] self.ipv6_address_mode = constants.DHCPV6_STATELESS self.ipv6_ra_mode = None class FakeV6SubnetStatelessNoDnsProvided(object): def __init__(self): self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' self.ip_version = 6 self.cidr = 'ffea:3ba5:a17a:4ba3::/64' self.gateway_ip = 'ffea:3ba5:a17a:4ba3::1' self.enable_dhcp = True self.dns_nameservers = ['::'] self.host_routes = [] self.ipv6_address_mode = constants.DHCPV6_STATELESS self.ipv6_ra_mode = None class FakeV6SubnetStatelessBadPrefixLength(object): def __init__(self): self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' self.ip_version = 6 self.cidr = 'ffeb:3ba5:a17a:4ba3::/56' self.gateway_ip = 'ffeb:3ba5:a17a:4ba3::1' self.enable_dhcp = True self.dns_nameservers = [] self.host_routes = [] self.ipv6_address_mode = constants.DHCPV6_STATELESS self.ipv6_ra_mode = None class FakeV4SubnetNoGateway(FakeV4Subnet): def __init__(self): super(FakeV4SubnetNoGateway, self).__init__() self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' self.cidr = '192.168.1.0/24' self.gateway_ip = None self.enable_dhcp = True self.host_routes = [] self.dns_nameservers = [] class FakeV4SubnetNoRouter(FakeV4Subnet): def __init__(self): super(FakeV4SubnetNoRouter, self).__init__() self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' self.cidr = '192.168.1.0/24' self.gateway_ip = '192.168.1.1' self.host_routes = [] self.dns_nameservers = [] class FakeV4Network(object): def __init__(self): self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' self.subnets = [FakeV4Subnet()] self.ports = [FakePort1()] self.namespace = 'qdhcp-ns' class FakeV4NetworkClientId(object): def __init__(self): self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' self.subnets = [FakeV4Subnet()] self.ports = [FakePort1(), FakePort5(), FakePort6()] self.namespace = 'qdhcp-ns' class FakeV4NetworkClientIdNum(object): def __init__(self): self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' self.subnets = [FakeV4Subnet()] self.ports = [FakePortWithClientIdNum()] self.namespace = 'qdhcp-ns' class FakeV6Network(object): def __init__(self): self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' self.subnets = [FakeV6Subnet()] self.ports = [FakePort2()] self.namespace = 'qdhcp-ns' class FakeDualNetwork(object): def __init__(self, domain='openstacklocal'): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4Subnet(), FakeV6SubnetDHCPStateful()] self.namespace = 'qdhcp-ns' self.ports = [FakePort1(domain=domain), FakeV6Port(domain=domain), FakeDualPort(domain=domain), FakeRouterPort(domain=domain)] class FakeDeviceManagerNetwork(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4Subnet(), FakeV6SubnetDHCPStateful()] self.ports = [FakePort1(), FakeV6Port(), FakeDualPort(), FakeRouterPort()] self.namespace = 'qdhcp-ns' class FakeDualNetworkReserved(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4Subnet(), FakeV6SubnetDHCPStateful()] self.ports = [FakePort1(), FakeV6Port(), FakeDualPort(), FakeRouterPort(), FakeReservedPort()] self.namespace = 'qdhcp-ns' class FakeDualNetworkReserved2(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4Subnet(), FakeV6SubnetDHCPStateful()] self.ports = [FakePort1(), FakeV6Port(), FakeDualPort(), FakeRouterPort(), FakeReservedPort(), FakeReservedPort(id='reserved-2')] self.namespace = 'qdhcp-ns' class FakeNetworkDhcpPort(object): def __init__(self): self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' self.subnets = [FakeV4Subnet()] self.ports = [FakePort1(), FakeDhcpPort()] self.namespace = 'qdhcp-ns' class FakeDualNetworkGatewayRoute(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4SubnetGatewayRoute(), FakeV6SubnetDHCPStateful()] self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()] self.namespace = 'qdhcp-ns' class FakeDualNetworkSingleDHCP(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4Subnet(), FakeV4SubnetNoDHCP()] self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()] self.namespace = 'qdhcp-ns' class FakeDualNetworkSingleDHCPBothAttaced(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' # dhcp-agent actually can't get the subnet with dhcp disabled self.subnets = [FakeV4Subnet()] self.ports = [FakePort1(), FakeRouterPortNoDHCP(), FakeRouterPort()] self.namespace = 'qdhcp-ns' class FakeDualNetworkDualDHCP(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4Subnet(), FakeV4Subnet2()] self.ports = [FakePort1(), FakeRouterPort(), FakeRouterPort2()] self.namespace = 'qdhcp-ns' class FakeDualNetworkDualDHCPOnLinkSubnetRoutesDisabled(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4Subnet(), FakeV4SubnetSegmentID()] self.ports = [FakePort1(), FakeRouterPort(), FakeRouterPortSegmentID()] self.namespace = 'qdhcp-ns' class FakeNonLocalSubnets(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4SubnetSegmentID2()] self.non_local_subnets = [FakeV4SubnetSegmentID()] self.ports = [FakePort1(), FakeRouterPort(), FakeRouterPortSegmentID()] self.namespace = 'qdhcp-ns' class FakeDualNetworkTriDHCPOneOnLinkSubnetRoute(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4Subnet(), FakeV4Subnet2(), FakeV4SubnetSegmentID()] self.ports = [FakePort1(), FakeRouterPort(), FakeRouterPort2(), FakeRouterPortSegmentID()] self.namespace = 'qdhcp-ns' class FakeV4NoGatewayNetwork(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4SubnetNoGateway()] self.ports = [FakePort1()] class FakeV4NetworkNoRouter(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4SubnetNoRouter()] self.ports = [FakePort1()] class FakeV4MetadataNetwork(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4MetadataSubnet()] self.ports = [FakeRouterPort(ip_address='169.254.169.253')] class FakeV4NetworkDistRouter(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4Subnet()] self.ports = [FakePort1(), FakeRouterPort( dev_owner=constants.DEVICE_OWNER_DVR_INTERFACE)] class FakeDualV4Pxe3Ports(object): def __init__(self, port_detail="portsSame"): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4Subnet(), FakeV4SubnetNoDHCP()] self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()] self.namespace = 'qdhcp-ns' if port_detail == "portsSame": self.ports[0].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] self.ports[1].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.1.3'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.1.2'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux2.0')] self.ports[2].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.1.3'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.1.2'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux3.0')] else: self.ports[0].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.2'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] self.ports[1].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.5'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.5'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux2.0')] self.ports[2].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.7'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.7'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux3.0')] class FakeV4NetworkPxe2Ports(object): def __init__(self, port_detail="portsSame"): self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' self.subnets = [FakeV4Subnet()] self.ports = [FakePort1(), FakePort2(), FakeRouterPort()] self.namespace = 'qdhcp-ns' if port_detail == "portsSame": self.ports[0].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] self.ports[1].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] else: self.ports[0].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] self.ports[1].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.5'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.5'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] class FakeV4NetworkPxe3Ports(object): def __init__(self, port_detail="portsSame"): self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' self.subnets = [FakeV4Subnet()] self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()] self.namespace = 'qdhcp-ns' if port_detail == "portsSame": self.ports[0].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] self.ports[1].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.1.3'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.1.2'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] self.ports[2].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.1.3'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.1.2'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] else: self.ports[0].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] self.ports[1].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.5'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.5'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux2.0')] self.ports[2].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.7'), DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.7'), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux3.0')] class FakeV6NetworkPxePort(object): def __init__(self): self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' self.subnets = [FakeV6SubnetDHCPStateful()] self.ports = [FakeV6Port()] self.namespace = 'qdhcp-ns' self.ports[0].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='2001:192:168::1', ip_version=6), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0', ip_version=6)] class FakeV6NetworkPxePortWrongOptVersion(object): def __init__(self): self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' self.subnets = [FakeV6SubnetDHCPStateful()] self.ports = [FakeV6Port()] self.namespace = 'qdhcp-ns' self.ports[0].extra_dhcp_opts = [ DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.7', ip_version=4), DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0', ip_version=6)] class FakeDualStackNetworkSingleDHCP(object): def __init__(self): self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' self.subnets = [FakeV4Subnet(), FakeV6SubnetSlaac()] self.ports = [FakePort1(), FakePort4(), FakeRouterPort()] class FakeDualStackNetworkingSingleDHCPTags(object): def __init__(self): self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' self.subnets = [FakeV4Subnet(), FakeV6SubnetSlaac()] self.ports = [FakePort1(), FakePort4(), FakeRouterPort()] for port in self.ports: port.extra_dhcp_opts = [ DhcpOpt(opt_name='tag:ipxe,bootfile-name', opt_value='pxelinux.0')] class FakeV4NetworkMultipleTags(object): def __init__(self): self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' self.subnets = [FakeV4Subnet()] self.ports = [FakePort1(), FakeRouterPort()] self.namespace = 'qdhcp-ns' self.ports[0].extra_dhcp_opts = [ DhcpOpt(opt_name='tag:ipxe,bootfile-name', opt_value='pxelinux.0')] class FakeV6NetworkStatelessDHCP(object): def __init__(self): self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' self.subnets = [FakeV6SubnetStateless()] self.ports = [FakeV6PortExtraOpt()] self.namespace = 'qdhcp-ns' class FakeV6NetworkStatelessDHCPNoDnsProvided(object): def __init__(self): self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' self.subnets = [FakeV6SubnetStatelessNoDnsProvided()] self.ports = [FakeV6Port()] self.namespace = 'qdhcp-ns' class FakeV6NetworkStatelessDHCPBadPrefixLength(object): def __init__(self): self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' self.subnets = [FakeV6SubnetStatelessBadPrefixLength()] self.ports = [FakeV6PortExtraOpt()] self.namespace = 'qdhcp-ns' class FakeNetworkWithV6SatelessAndV4DHCPSubnets(object): def __init__(self): self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' self.subnets = [FakeV6SubnetStateless(), FakeV4Subnet()] self.ports = [FakeDualPortWithV6ExtraOpt(), FakeRouterPort()] self.namespace = 'qdhcp-ns' class LocalChild(dhcp.DhcpLocalProcess): PORTS = {4: [4], 6: [6]} def __init__(self, *args, **kwargs): self.process_monitor = mock.Mock() kwargs['process_monitor'] = self.process_monitor super(LocalChild, self).__init__(*args, **kwargs) self.called = [] def reload_allocations(self): self.called.append('reload') def restart(self): self.called.append('restart') def spawn_process(self): self.called.append('spawn') class TestConfBase(base.BaseTestCase): def setUp(self): super(TestConfBase, self).setUp() self.conf = config.setup_conf() self.conf.register_opts(base_config.core_opts) self.conf.register_opts(dhcp_config.DHCP_OPTS) self.conf.register_opts(dhcp_config.DNSMASQ_OPTS) config.register_external_process_opts(self.conf) config.register_interface_driver_opts_helper(self.conf) class TestBase(TestConfBase): def setUp(self): super(TestBase, self).setUp() instance = mock.patch("neutron.agent.linux.dhcp.DeviceManager") self.mock_mgr = instance.start() self.conf.register_opt(cfg.BoolOpt('enable_isolated_metadata', default=True)) self.conf.register_opt(cfg.BoolOpt("force_metadata", default=False)) self.conf.register_opt(cfg.BoolOpt('enable_metadata_network', default=False)) self.config_parse(self.conf) self.conf.set_override('state_path', '') self.replace_p = mock.patch('neutron_lib.utils.file.replace_file') self.execute_p = mock.patch('neutron.agent.common.utils.execute') mock.patch('neutron.agent.linux.utils.execute').start() self.safe = self.replace_p.start() self.execute = self.execute_p.start() self.makedirs = mock.patch('os.makedirs').start() self.rmtree = mock.patch('shutil.rmtree').start() self.external_process = mock.patch( 'neutron.agent.linux.external_process.ProcessManager').start() self.mock_mgr.return_value.driver.bridged = True class TestDhcpBase(TestBase): def test_existing_dhcp_networks_abstract_error(self): self.assertRaises(NotImplementedError, dhcp.DhcpBase.existing_dhcp_networks, None) def test_check_version_abstract_error(self): self.assertRaises(NotImplementedError, dhcp.DhcpBase.check_version) def test_base_abc_error(self): self.assertRaises(TypeError, dhcp.DhcpBase, None) def test_restart(self): class SubClass(dhcp.DhcpBase): def __init__(self): dhcp.DhcpBase.__init__(self, cfg.CONF, FakeV4Network(), mock.Mock(), None) self.called = [] def enable(self): self.called.append('enable') def disable(self, retain_port=False, block=False): self.called.append('disable %s %s' % (retain_port, block)) def reload_allocations(self): pass @property def active(self): return True c = SubClass() c.restart() self.assertEqual(c.called, ['disable True True', 'enable']) class TestDhcpLocalProcess(TestBase): def test_get_conf_file_name(self): tpl = '/dhcp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/dev' lp = LocalChild(self.conf, FakeV4Network()) self.assertEqual(lp.get_conf_file_name('dev'), tpl) @mock.patch.object(fileutils, 'ensure_tree') def test_ensure_dir_called(self, ensure_dir): LocalChild(self.conf, FakeV4Network()) ensure_dir.assert_called_once_with( '/dhcp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', mode=0o755) def test_enable_already_active(self): with mock.patch.object(LocalChild, 'active') as patched: patched.__get__ = mock.Mock(return_value=True) lp = LocalChild(self.conf, FakeV4Network()) lp.enable() self.assertEqual(lp.called, ['restart']) self.assertFalse(self.mock_mgr.return_value.setup.called) @mock.patch.object(fileutils, 'ensure_tree') def test_enable(self, ensure_dir): attrs_to_mock = dict( [(a, mock.DEFAULT) for a in ['active', 'interface_name', 'spawn_process']] ) with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks: mocks['active'].__get__ = mock.Mock(return_value=False) mocks['interface_name'].__set__ = mock.Mock() mocks['spawn_process'].side_effect = [ n_exc.ProcessExecutionError( returncode=2, message="Test dnsmasq start failed"), None] lp = LocalChild(self.conf, FakeDualNetwork()) lp.enable() self.mock_mgr.assert_has_calls( [mock.call(self.conf, None), mock.call().setup(mock.ANY)]) self.assertEqual(2, mocks['interface_name'].__set__.call_count) ensure_dir.assert_has_calls([ mock.call( '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc', mode=0o755), mock.call( '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc', mode=0o755)]) def _assert_disabled(self, lp): self.assertTrue(lp.process_monitor.unregister.called) self.assertTrue(self.external_process().disable.called) @mock.patch('neutron.agent.linux.ip_lib.network_namespace_exists') def test_disable_not_active(self, namespace_exists): namespace_exists.return_value = False attrs_to_mock = dict([(a, mock.DEFAULT) for a in ['active', 'interface_name']]) with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks: mocks['active'].__get__ = mock.Mock(return_value=False) mocks['interface_name'].__get__ = mock.Mock(return_value='tap0') network = FakeDualNetwork() lp = LocalChild(self.conf, network) lp.device_manager = mock.Mock() lp.disable() lp.device_manager.destroy.assert_called_once_with( network, 'tap0') self._assert_disabled(lp) def test_disable_retain_port(self): attrs_to_mock = dict([(a, mock.DEFAULT) for a in ['active', 'interface_name']]) network = FakeDualNetwork() with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks: mocks['active'].__get__ = mock.Mock(return_value=True) mocks['interface_name'].__get__ = mock.Mock(return_value='tap0') lp = LocalChild(self.conf, network) lp.disable(retain_port=True) self._assert_disabled(lp) @mock.patch('neutron.agent.linux.ip_lib.network_namespace_exists') def test_disable(self, namespace_exists): namespace_exists.return_value = True attrs_to_mock = {'active': mock.DEFAULT} with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks: mocks['active'].__get__ = mock.Mock(return_value=False) lp = LocalChild(self.conf, FakeDualNetwork()) with mock.patch('neutron.agent.linux.ip_lib.' 'delete_network_namespace') as delete_ns: lp.disable() self._assert_disabled(lp) delete_ns.assert_called_with('qdhcp-ns') @mock.patch('neutron.agent.linux.ip_lib.network_namespace_exists') def test_disable_config_dir_removed_after_destroy(self, namespace_exists): namespace_exists.return_value = True parent = mock.MagicMock() parent.attach_mock(self.rmtree, 'rmtree') parent.attach_mock(self.mock_mgr, 'DeviceManager') lp = LocalChild(self.conf, FakeDualNetwork()) with mock.patch('neutron.agent.linux.ip_lib.' 'delete_network_namespace') as delete_ns: lp.disable(retain_port=False) expected = [mock.call.DeviceManager().destroy(mock.ANY, mock.ANY), mock.call.rmtree(mock.ANY, ignore_errors=True)] parent.assert_has_calls(expected) delete_ns.assert_called_with('qdhcp-ns') def test_get_interface_name(self): net = FakeDualNetwork() path = '/dhcp/%s/interface' % net.id self.useFixture(tools.OpenFixture(path, 'tap0')) lp = LocalChild(self.conf, net) self.assertEqual(lp.interface_name, 'tap0') def test_set_interface_name(self): with mock.patch('neutron_lib.utils.file.replace_file') as replace: lp = LocalChild(self.conf, FakeDualNetwork()) with mock.patch.object(lp, 'get_conf_file_name') as conf_file: conf_file.return_value = '/interface' lp.interface_name = 'tap0' conf_file.assert_called_once_with('interface') replace.assert_called_once_with(mock.ANY, 'tap0') class TestDnsmasq(TestBase): def _get_dnsmasq(self, network, process_monitor=None): process_monitor = process_monitor or mock.Mock() return dhcp.Dnsmasq(self.conf, network, process_monitor=process_monitor) def _test_spawn(self, extra_options, network=FakeDualNetwork(), max_leases=16777216, lease_duration=86400, has_static=True, no_resolv='--no-resolv', has_stateless=True, dhcp_t1=0, dhcp_t2=0, bridged=True): def mock_get_conf_file_name(kind): return '/dhcp/%s/%s' % (network.id, kind) # if you need to change this path here, think twice, # that means pid files will move around, breaking upgrades # or backwards-compatibility expected_pid_file = '/dhcp/%s/pid' % network.id expected = [ 'dnsmasq', '--no-hosts', no_resolv, '--pid-file=%s' % expected_pid_file, '--dhcp-hostsfile=/dhcp/%s/host' % network.id, '--addn-hosts=/dhcp/%s/addn_hosts' % network.id, '--dhcp-optsfile=/dhcp/%s/opts' % network.id, '--dhcp-leasefile=/dhcp/%s/leases' % network.id, '--dhcp-match=set:ipxe,175', '--local-service', '--bind-dynamic', ] if not bridged: expected += [ '--bridge-interface=tap0,tap*' ] seconds = '' if lease_duration == -1: lease_duration = 'infinite' else: seconds = 's' if has_static: prefix = '--dhcp-range=set:tag%d,%s,static,%s,%s%s' prefix6 = '--dhcp-range=set:tag%d,%s,static,%s,%s%s' elif has_stateless: prefix = '--dhcp-range=set:tag%d,%s,%s,%s%s' prefix6 = '--dhcp-range=set:tag%d,%s,%s,%s%s' possible_leases = 0 for i, s in enumerate(network.subnets): if (s.ip_version != 6 or s.ipv6_address_mode == constants.DHCPV6_STATEFUL): if s.ip_version == 4: expected.extend([prefix % ( i, s.cidr.split('/')[0], netaddr.IPNetwork(s.cidr).netmask, lease_duration, seconds)]) else: expected.extend([prefix6 % ( i, s.cidr.split('/')[0], s.cidr.split('/')[1], lease_duration, seconds)]) possible_leases += netaddr.IPNetwork(s.cidr).size if hasattr(network, 'mtu'): expected.append( '--dhcp-option-force=option:mtu,%s' % network.mtu) expected.append('--dhcp-lease-max=%d' % min( possible_leases, max_leases)) if dhcp_t1: expected.append('--dhcp-option-force=option:T1,%ds' % dhcp_t1) if dhcp_t2: expected.append('--dhcp-option-force=option:T2,%ds' % dhcp_t2) expected.extend(extra_options) self.execute.return_value = ('', '') attrs_to_mock = dict( [(a, mock.DEFAULT) for a in ['_output_opts_file', 'get_conf_file_name', 'interface_name']] ) test_pm = mock.Mock() with mock.patch.multiple(dhcp.Dnsmasq, **attrs_to_mock) as mocks: mocks['get_conf_file_name'].side_effect = mock_get_conf_file_name mocks['_output_opts_file'].return_value = ( '/dhcp/%s/opts' % network.id ) mocks['interface_name'].__get__ = mock.Mock(return_value='tap0') dm = self._get_dnsmasq(network, test_pm) dm.spawn_process() self.assertTrue(mocks['_output_opts_file'].called) self.assertTrue(test_pm.register.called) self.external_process().enable.assert_called_once_with( reload_cfg=False) call_kwargs = self.external_process.mock_calls[0][2] cmd_callback = call_kwargs['default_cmd_callback'] result_cmd = cmd_callback(expected_pid_file) self.assertEqual(expected, result_cmd) def test_spawn(self): self._test_spawn(['--conf-file=', '--domain=openstacklocal']) def test_spawn_not_bridged(self): self.mock_mgr.return_value.driver.bridged = False self._test_spawn(['--conf-file=', '--domain=openstacklocal'], bridged=False) def test_spawn_infinite_lease_duration(self): self.conf.set_override('dhcp_lease_duration', -1) self._test_spawn(['--conf-file=', '--domain=openstacklocal'], FakeDualNetwork(), 16777216, -1) def test_spawn_cfg_config_file(self): self.conf.set_override('dnsmasq_config_file', '/foo') self._test_spawn(['--conf-file=/foo', '--domain=openstacklocal']) def test_spawn_no_dns_domain(self): (exp_host_name, exp_host_data, exp_addn_name, exp_addn_data) = self._test_no_dns_domain_alloc_data self.conf.set_override('dns_domain', '') network = FakeDualNetwork(domain=self.conf.dns_domain) self._test_spawn(['--conf-file='], network=network) self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data), mock.call(exp_addn_name, exp_addn_data)]) def test_spawn_no_dhcp_range(self): network = FakeV6Network() subnet = FakeV6SubnetSlaac() network.subnets = [subnet] self._test_spawn(['--conf-file=', '--domain=openstacklocal'], network, has_static=False) def test_spawn_no_dhcp_range_bad_prefix_length(self): network = FakeV6NetworkStatelessDHCPBadPrefixLength() subnet = FakeV6SubnetStatelessBadPrefixLength() network.subnets = [subnet] self._test_spawn(['--conf-file=', '--domain=openstacklocal'], network, has_static=False, has_stateless=False) def test_spawn_cfg_dns_server(self): self.conf.set_override('dnsmasq_dns_servers', ['8.8.8.8']) self._test_spawn(['--conf-file=', '--server=8.8.8.8', '--domain=openstacklocal']) def test_spawn_cfg_multiple_dns_server(self): self.conf.set_override('dnsmasq_dns_servers', ['8.8.8.8', '9.9.9.9']) self._test_spawn(['--conf-file=', '--server=8.8.8.8', '--server=9.9.9.9', '--domain=openstacklocal']) def test_spawn_cfg_enable_dnsmasq_log(self): self.conf.set_override('dnsmasq_base_log_dir', '/tmp') network = FakeV4Network() dhcp_dns_log = \ '/tmp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/dhcp_dns_log' self._test_spawn(['--conf-file=', '--domain=openstacklocal', '--log-queries', '--log-dhcp', ('--log-facility=%s' % dhcp_dns_log)], network) self.makedirs.assert_called_with(os.path.join('/tmp', network.id)) def test_spawn_cfg_with_local_resolv(self): self.conf.set_override('dnsmasq_local_resolv', True) self._test_spawn(['--conf-file=', '--domain=openstacklocal'], no_resolv='') def test_spawn_cfg_with_local_resolv_overridden(self): self.conf.set_override('dnsmasq_local_resolv', True) self.conf.set_override('dnsmasq_dns_servers', ['8.8.8.8']) self._test_spawn(['--conf-file=', '--server=8.8.8.8', '--domain=openstacklocal']) def test_spawn_max_leases_is_smaller_than_cap(self): self._test_spawn( ['--conf-file=', '--domain=openstacklocal'], network=FakeV4Network(), max_leases=256) def test_spawn_cfg_broadcast(self): self.conf.set_override('dhcp_broadcast_reply', True) self._test_spawn(['--conf-file=', '--domain=openstacklocal', '--dhcp-broadcast']) def test_spawn_cfg_advertise_mtu(self): network = FakeV4Network() network.mtu = 1500 self._test_spawn(['--conf-file=', '--domain=openstacklocal'], network) def test_spawn_cfg_advertise_mtu_plugin_doesnt_pass_mtu_value(self): network = FakeV4Network() self._test_spawn(['--conf-file=', '--domain=openstacklocal'], network) def test_spawn_cfg_with_dhcp_timers(self): self.conf.set_override('dhcp_renewal_time', 30) self.conf.set_override('dhcp_rebinding_time', 100) self._test_spawn(['--conf-file=', '--domain=openstacklocal'], dhcp_t1=30, dhcp_t2=100) def _test_output_init_lease_file(self, timestamp): expected = [ '00:00:80:aa:bb:cc 192.168.0.2 * *', '00:00:0f:aa:bb:cc 192.168.0.3 * *', '00:00:0f:rr:rr:rr 192.168.0.1 * *\n'] expected = "\n".join(['%s %s' % (timestamp, l) for l in expected]) with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: conf_fn.return_value = '/foo/leases' dm = self._get_dnsmasq(FakeDualNetwork()) dm._output_init_lease_file() self.safe.assert_called_once_with('/foo/leases', expected) @mock.patch('time.time') def test_output_init_lease_file(self, tmock): self.conf.set_override('dhcp_lease_duration', 500) tmock.return_value = 1000000 # lease duration should be added to current time timestamp = 1000000 + 500 self._test_output_init_lease_file(timestamp) def test_output_init_lease_file_infinite_duration(self): self.conf.set_override('dhcp_lease_duration', -1) # when duration is infinite, lease db timestamp should be 0 timestamp = 0 self._test_output_init_lease_file(timestamp) def _test_output_opts_file(self, expected, network, ipm_retval=None): with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: conf_fn.return_value = '/foo/opts' dm = self._get_dnsmasq(network) if ipm_retval: with mock.patch.object( dm, '_make_subnet_interface_ip_map') as ipm: ipm.return_value = ipm_retval dm._output_opts_file() self.assertTrue(ipm.called) else: dm._output_opts_file() self.safe.assert_called_once_with('/foo/opts', expected) def test_output_opts_file(self): fake_v6 = '2001:0200:feed:7ac0::1' expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1\n' 'tag:tag1,option6:dns-server,%s\n' 'tag:tag1,option6:domain-search,openstacklocal').lstrip() % ( '[' + fake_v6 + ']') self._test_output_opts_file(expected, FakeDualNetwork()) def test_output_opts_file_gateway_route(self): fake_v6 = '2001:0200:feed:7ac0::1' expected = ('tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,' '192.168.0.1\ntag:tag0,249,169.254.169.254/32,' '192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1\n' 'tag:tag1,option6:dns-server,%s\n' 'tag:tag1,option6:domain-search,' 'openstacklocal').lstrip() % ('[' + fake_v6 + ']') self._test_output_opts_file(expected, FakeDualNetworkGatewayRoute()) def test_output_opts_file_multiple_agents_without_dns_provided(self): expected = ('tag:tag0,option:classless-static-route,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,' '192.168.0.1\ntag:tag0,option:router,192.168.0.1\n' 'tag:tag0,option:dns-server,192.168.0.5,' '192.168.0.6').lstrip() self._test_output_opts_file(expected, FakeV4MultipleAgentsWithoutDnsProvided()) def test_output_opts_file_agent_dns_provided(self): expected = ('tag:tag0,option:classless-static-route,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,' '192.168.0.1\ntag:tag0,option:router,192.168.0.1' ).lstrip() self._test_output_opts_file(expected, FakeV4AgentWithoutDnsProvided()) def test_output_opts_file_agent_with_many_dns_provided(self): expected = ('tag:tag0,' 'option:dns-server,2.2.2.2,9.9.9.9,1.1.1.1,3.3.3.3\n' 'tag:tag0,option:classless-static-route,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,' '192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1').lstrip() self._test_output_opts_file(expected, FakeV4AgentWithManyDnsProvided()) def test_output_opts_file_agent_with_no_dns_provided(self): expected = ('tag:tag0,' 'option:dns-server\n' 'tag:tag0,option:classless-static-route,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,' '192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1').lstrip() self._test_output_opts_file(expected, FakeV4AgentWithNoDnsProvided()) def test_output_opts_file_multiple_agents_with_dns_provided(self): expected = ('tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,' '192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1').lstrip() self._test_output_opts_file(expected, FakeV4MultipleAgentsWithDnsProvided()) def test_output_opts_file_single_dhcp(self): expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,' '192.168.1.0/24,0.0.0.0,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,192.168.1.0/24,0.0.0.0,' '0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1').lstrip() self._test_output_opts_file(expected, FakeDualNetworkSingleDHCP()) def test_output_opts_file_single_dhcp_both_not_isolated(self): expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1').lstrip() self._test_output_opts_file(expected, FakeDualNetworkSingleDHCPBothAttaced()) def test_output_opts_file_dual_dhcp_rfc3442(self): expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,' '192.168.1.0/24,0.0.0.0,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,192.168.1.0/24,0.0.0.0,' '0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1\n' 'tag:tag1,option:dns-server,8.8.8.8\n' 'tag:tag1,option:classless-static-route,' '169.254.169.254/32,192.168.1.1,' '192.168.0.0/24,0.0.0.0,0.0.0.0/0,192.168.1.1\n' 'tag:tag1,249,169.254.169.254/32,192.168.1.1,' '192.168.0.0/24,0.0.0.0,0.0.0.0/0,192.168.1.1\n' 'tag:tag1,option:router,192.168.1.1').lstrip() self._test_output_opts_file(expected, FakeDualNetworkDualDHCP()) def test_output_opts_file_dual_dhcp_rfc3442_no_on_link_subnet_routes(self): expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1\n' 'tag:tag1,option:dns-server,8.8.8.8\n' 'tag:tag1,option:classless-static-route,' '169.254.169.254/32,192.168.2.1,0.0.0.0/0,192.168.2.1\n' 'tag:tag1,249,169.254.169.254/32,192.168.2.1,' '0.0.0.0/0,192.168.2.1\n' 'tag:tag1,option:router,192.168.2.1').lstrip() self._test_output_opts_file(expected, FakeDualNetworkDualDHCPOnLinkSubnetRoutesDisabled()) def test_output_opts_file_dual_dhcp_rfc3442_one_on_link_subnet_route(self): expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,' '192.168.1.0/24,0.0.0.0,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,192.168.1.0/24,0.0.0.0,' '0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1\n' 'tag:tag1,option:dns-server,8.8.8.8\n' 'tag:tag1,option:classless-static-route,' '169.254.169.254/32,192.168.1.1,' '192.168.0.0/24,0.0.0.0,0.0.0.0/0,192.168.1.1\n' 'tag:tag1,249,169.254.169.254/32,192.168.1.1,' '192.168.0.0/24,0.0.0.0,0.0.0.0/0,192.168.1.1\n' 'tag:tag1,option:router,192.168.1.1\n' 'tag:tag2,option:dns-server,8.8.8.8\n' 'tag:tag2,option:classless-static-route,' '169.254.169.254/32,192.168.2.1,0.0.0.0/0,192.168.2.1\n' 'tag:tag2,249,169.254.169.254/32,192.168.2.1,' '0.0.0.0/0,192.168.2.1\n' 'tag:tag2,option:router,192.168.2.1').lstrip() self._test_output_opts_file(expected, FakeDualNetworkTriDHCPOneOnLinkSubnetRoute()) def test_output_opts_file_no_gateway(self): expected = ( 'tag:tag0,option:classless-static-route,' '169.254.169.254/32,192.168.1.1\n' 'tag:tag0,249,169.254.169.254/32,192.168.1.1\n' 'tag:tag0,option:router').lstrip() ipm_retval = {FakeV4SubnetNoGateway().id: '192.168.1.1'} self._test_output_opts_file(expected, FakeV4NoGatewayNetwork(), ipm_retval=ipm_retval) def test_non_local_subnets(self): expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,169.254.169.254/32,192.168.0.1,' '0.0.0.0/0,192.168.0.1\ntag:tag0,option:router,192.168.0.1\n' 'tag:tag1,option:dns-server,8.8.8.8\n' 'tag:tag1,option:classless-static-route,' '169.254.169.254/32,192.168.2.1,0.0.0.0/0,192.168.2.1\n' 'tag:tag1,249,169.254.169.254/32,192.168.2.1,' '0.0.0.0/0,192.168.2.1\n' 'tag:tag1,option:router,192.168.2.1').lstrip() ipm_retval = {FakeV4SubnetSegmentID2().id: '192.168.0.1'} self._test_output_opts_file(expected, FakeNonLocalSubnets(), ipm_retval=ipm_retval) def test_output_opts_file_no_neutron_router_on_subnet(self): expected = ( 'tag:tag0,option:classless-static-route,' '169.254.169.254/32,192.168.1.2,0.0.0.0/0,192.168.1.1\n' 'tag:tag0,249,169.254.169.254/32,192.168.1.2,' '0.0.0.0/0,192.168.1.1\n' 'tag:tag0,option:router,192.168.1.1').lstrip() ipm_retval = {FakeV4SubnetNoRouter().id: '192.168.1.2'} self._test_output_opts_file(expected, FakeV4NetworkNoRouter(), ipm_retval=ipm_retval) def test_output_opts_file_dist_neutron_router_on_subnet(self): expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1').lstrip() ipm_retval = {FakeV4Subnet().id: '192.168.0.1'} self._test_output_opts_file(expected, FakeV4NetworkDistRouter(), ipm_retval=ipm_retval) def test_output_opts_file_pxe_2port_1net(self): expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1\n' 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' 'option:tftp-server,192.168.0.3\n' 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' 'option:server-ip-address,192.168.0.2\n' 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' 'option:bootfile-name,pxelinux.0\n' 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,' 'option:tftp-server,192.168.0.3\n' 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,' 'option:server-ip-address,192.168.0.2\n' 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,' 'option:bootfile-name,pxelinux.0').lstrip() self._test_output_opts_file(expected, FakeV4NetworkPxe2Ports()) def test_output_opts_file_pxe_2port_1net_diff_details(self): expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1\n' 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' 'option:tftp-server,192.168.0.3\n' 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' 'option:server-ip-address,192.168.0.2\n' 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' 'option:bootfile-name,pxelinux.0\n' 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,' 'option:tftp-server,192.168.0.5\n' 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,' 'option:server-ip-address,192.168.0.5\n' 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,' 'option:bootfile-name,pxelinux.0').lstrip() self._test_output_opts_file(expected, FakeV4NetworkPxe2Ports("portsDiff")) def test_output_opts_file_pxe_3port_2net(self): expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,' '192.168.1.0/24,0.0.0.0,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,192.168.1.0/24,0.0.0.0,' '0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1\n' 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' 'option:tftp-server,192.168.0.3\n' 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' 'option:server-ip-address,192.168.0.2\n' 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' 'option:bootfile-name,pxelinux.0\n' 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,' 'option:tftp-server,192.168.1.3\n' 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,' 'option:server-ip-address,192.168.1.2\n' 'tag:ffffffff-ffff-ffff-ffff-ffffffffffff,' 'option:bootfile-name,pxelinux2.0\n' 'tag:44444444-4444-4444-4444-444444444444,' 'option:tftp-server,192.168.1.3\n' 'tag:44444444-4444-4444-4444-444444444444,' 'option:server-ip-address,192.168.1.2\n' 'tag:44444444-4444-4444-4444-444444444444,' 'option:bootfile-name,pxelinux3.0').lstrip() self._test_output_opts_file(expected, FakeDualV4Pxe3Ports()) def test_output_opts_file_multiple_tags(self): expected = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1\n' 'tag:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' 'tag:ipxe,option:bootfile-name,pxelinux.0') expected = expected.lstrip() with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: conf_fn.return_value = '/foo/opts' dm = self._get_dnsmasq(FakeV4NetworkMultipleTags()) dm._output_opts_file() self.safe.assert_called_once_with('/foo/opts', expected) @mock.patch('neutron.agent.linux.dhcp.Dnsmasq.get_conf_file_name', return_value='/foo/opts') def test_output_opts_file_pxe_ipv6_port_with_ipv6_opt(self, mock_get_conf_fn): expected = ( 'tag:tag0,option6:dns-server,[2001:0200:feed:7ac0::1]\n' 'tag:tag0,option6:domain-search,openstacklocal\n' 'tag:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' 'option6:tftp-server,2001:192:168::1\n' 'tag:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' 'option6:bootfile-name,pxelinux.0') expected = expected.lstrip() dm = self._get_dnsmasq(FakeV6NetworkPxePort()) dm._output_opts_file() self.safe.assert_called_once_with('/foo/opts', expected) @mock.patch('neutron.agent.linux.dhcp.Dnsmasq.get_conf_file_name', return_value='/foo/opts') def test_output_opts_file_pxe_ipv6_port_with_ipv4_opt(self, mock_get_conf_fn): expected = ( 'tag:tag0,option6:dns-server,[2001:0200:feed:7ac0::1]\n' 'tag:tag0,option6:domain-search,openstacklocal\n' 'tag:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' 'option6:bootfile-name,pxelinux.0') expected = expected.lstrip() dm = self._get_dnsmasq(FakeV6NetworkPxePortWrongOptVersion()) dm._output_opts_file() self.safe.assert_called_once_with('/foo/opts', expected) def test_output_opts_file_ipv6_address_mode_unset(self): fake_v6 = '2001:0200:feed:7ac0::1' expected = ( 'tag:tag0,option6:dns-server,%s\n' 'tag:tag0,option6:domain-search,openstacklocal').lstrip() % ( '[' + fake_v6 + ']') self._test_output_opts_file(expected, FakeV6Network()) def test_output_opts_file_ipv6_address_force_metadata(self): fake_v6 = '2001:0200:feed:7ac0::1' expected = ( 'tag:tag0,option6:dns-server,%s\n' 'tag:tag0,option6:domain-search,openstacklocal').lstrip() % ( '[' + fake_v6 + ']') self.conf.force_metadata = True self._test_output_opts_file(expected, FakeV6Network()) @property def _test_no_dns_domain_alloc_data(self): exp_host_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host' exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2,' '192.168.0.2\n' '00:00:f3:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--2,' '[fdca:3ba5:a17a:4ba3::2]\n' '00:00:0f:aa:bb:cc,host-192-168-0-3,' '192.168.0.3\n' '00:00:0f:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--3,' '[fdca:3ba5:a17a:4ba3::3]\n' '00:00:0f:rr:rr:rr,host-192-168-0-1,' '192.168.0.1\n').lstrip() exp_addn_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/addn_hosts' exp_addn_data = ( '192.168.0.2\t' 'host-192-168-0-2 host-192-168-0-2\n' 'fdca:3ba5:a17a:4ba3::2\t' 'host-fdca-3ba5-a17a-4ba3--2 ' 'host-fdca-3ba5-a17a-4ba3--2\n' '192.168.0.3\thost-192-168-0-3 ' 'host-192-168-0-3\n' 'fdca:3ba5:a17a:4ba3::3\t' 'host-fdca-3ba5-a17a-4ba3--3 ' 'host-fdca-3ba5-a17a-4ba3--3\n' '192.168.0.1\t' 'host-192-168-0-1 ' 'host-192-168-0-1\n' ).lstrip() return (exp_host_name, exp_host_data, exp_addn_name, exp_addn_data) @property def _test_reload_allocation_data(self): exp_host_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host' exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2.openstacklocal.,' '192.168.0.2\n' '00:00:f3:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--2.' 'openstacklocal.,[fdca:3ba5:a17a:4ba3::2]\n' '00:00:0f:aa:bb:cc,host-192-168-0-3.openstacklocal.,' '192.168.0.3\n' '00:00:0f:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--3.' 'openstacklocal.,[fdca:3ba5:a17a:4ba3::3]\n' '00:00:0f:rr:rr:rr,host-192-168-0-1.openstacklocal.,' '192.168.0.1\n').lstrip() exp_addn_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/addn_hosts' exp_addn_data = ( '192.168.0.2\t' 'host-192-168-0-2.openstacklocal. host-192-168-0-2\n' 'fdca:3ba5:a17a:4ba3::2\t' 'host-fdca-3ba5-a17a-4ba3--2.openstacklocal. ' 'host-fdca-3ba5-a17a-4ba3--2\n' '192.168.0.3\thost-192-168-0-3.openstacklocal. ' 'host-192-168-0-3\n' 'fdca:3ba5:a17a:4ba3::3\t' 'host-fdca-3ba5-a17a-4ba3--3.openstacklocal. ' 'host-fdca-3ba5-a17a-4ba3--3\n' '192.168.0.1\t' 'host-192-168-0-1.openstacklocal. ' 'host-192-168-0-1\n' ).lstrip() exp_opt_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts' fake_v6 = '2001:0200:feed:7ac0::1' exp_opt_data = ( 'tag:tag0,option:dns-server,8.8.8.8\n' 'tag:tag0,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,249,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag0,option:router,192.168.0.1\n' 'tag:tag1,option6:dns-server,%s\n' 'tag:tag1,option6:domain-search,openstacklocal').lstrip() % ( '[' + fake_v6 + ']') return (exp_host_name, exp_host_data, exp_addn_name, exp_addn_data, exp_opt_name, exp_opt_data,) def test_reload_allocations_no_interface(self): net = FakeDualNetwork() ipath = '/dhcp/%s/interface' % net.id self.useFixture(tools.OpenFixture(ipath)) test_pm = mock.Mock() dm = self._get_dnsmasq(net, test_pm) dm.reload_allocations() self.assertFalse(test_pm.register.called) def test_reload_allocations(self): (exp_host_name, exp_host_data, exp_addn_name, exp_addn_data, exp_opt_name, exp_opt_data,) = self._test_reload_allocation_data net = FakeDualNetwork() hpath = '/dhcp/%s/host' % net.id ipath = '/dhcp/%s/interface' % net.id self.useFixture(tools.OpenFixture(hpath)) self.useFixture(tools.OpenFixture(ipath, 'tapdancingmice')) test_pm = mock.Mock() dm = self._get_dnsmasq(net, test_pm) dm.reload_allocations() self.assertTrue(test_pm.register.called) self.external_process().enable.assert_called_once_with( reload_cfg=True) self.safe.assert_has_calls([ mock.call(exp_host_name, exp_host_data), mock.call(exp_addn_name, exp_addn_data), mock.call(exp_opt_name, exp_opt_data), ]) def test_release_unused_leases(self): dnsmasq = self._get_dnsmasq(FakeDualNetwork()) ip1 = '192.168.1.2' mac1 = '00:00:80:aa:bb:cc' ip2 = '192.168.1.3' mac2 = '00:00:80:cc:bb:aa' ip3 = '0001:0002:0003:0004:0005:0006:0007:0008' mac3 = '00:00:80:bb:aa:cc' old_leases = {(ip1, mac1, None), (ip2, mac2, None), (ip3, mac3, None)} dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases) # Because the lease release code could fire multiple times, the # second read of the lease file must not have the entries that # would have been released. dnsmasq._read_leases_file_leases = mock.Mock( side_effect=[{ip1: {'iaid': mac1, 'client_id': 'client_id', 'server_id': 'server_id'}, ip2: {'iaid': mac2, 'client_id': 'client_id', 'server_id': 'server_id'}, ip3: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'} }, {}]) dnsmasq._output_hosts_file = mock.Mock() dnsmasq._release_lease = mock.Mock() dnsmasq.network.ports = [] dnsmasq.device_manager.unplug = mock.Mock() dnsmasq._release_unused_leases() dnsmasq._release_lease.assert_has_calls([mock.call(mac1, ip1, constants.IP_VERSION_4, None, 'server_id', mac1), mock.call(mac2, ip2, constants.IP_VERSION_4, None, 'server_id', mac2), mock.call(mac3, ip3, constants.IP_VERSION_6, 'client_id', 'server_id', 0xff), ], any_order=True) def test_release_for_ipv6_lease(self): dnsmasq = self._get_dnsmasq(FakeDualNetwork()) ip1 = 'fdca:3ba5:a17a::1' mac1 = '00:00:80:aa:bb:cc' ip2 = '192.168.1.3' mac2 = '00:00:80:cc:bb:aa' old_leases = set([(ip1, mac1, 'client_id'), (ip2, mac2, None)]) dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases) # Because the lease release code could fire multiple times, the # second read of the lease file must not have the entries that # would have been released. dnsmasq._read_leases_file_leases = mock.Mock( side_effect=[{ip1: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'}, ip2: {'iaid': mac2, 'client_id': None, 'server_id': 'server_id'} }, {}]) ipw = mock.patch( 'neutron.agent.linux.ip_lib.IpNetnsCommand.execute').start() dnsmasq._release_unused_leases() # Verify that dhcp_release is called both for ipv4 and ipv6 addresses. self.assertEqual(2, ipw.call_count) ipw.assert_has_calls([mock.call(['dhcp_release6', '--iface', None, '--ip', ip1, '--client-id', 'client_id', '--server-id', 'server_id', '--iaid', 0xff], run_as_root=True)]) ipw.assert_has_calls([mock.call(['dhcp_release', None, ip2, mac2], run_as_root=True), ]) def test_release_for_ipv6_lease_no_dhcp_release6(self): dnsmasq = self._get_dnsmasq(FakeDualNetwork()) ip1 = 'fdca:3ba5:a17a::1' mac1 = '00:00:80:aa:bb:cc' old_leases = set([(ip1, mac1, None)]) dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases) dnsmasq._read_leases_file_leases = mock.Mock( return_value={'fdca:3ba5:a17a::1': {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'} }) ipw = mock.patch( 'neutron.agent.linux.ip_lib.IpNetnsCommand.execute').start() dnsmasq._IS_DHCP_RELEASE6_SUPPORTED = False dnsmasq._release_unused_leases() # Verify that dhcp_release6 is not called when it is not present ipw.assert_not_called() def test_release_unused_leases_with_dhcp_port(self): dnsmasq = self._get_dnsmasq(FakeNetworkDhcpPort()) ip1 = '192.168.1.2' mac1 = '00:00:80:aa:bb:cc' ip2 = '192.168.1.3' mac2 = '00:00:80:cc:bb:aa' ip6 = '2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d' old_leases = set([(ip1, mac1, None), (ip2, mac2, None)]) dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases) dnsmasq._read_leases_file_leases = mock.Mock( return_value={ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'} }) dnsmasq._output_hosts_file = mock.Mock() dnsmasq._release_lease = mock.Mock() dnsmasq.device_manager.get_device_id = mock.Mock( return_value='fake_dhcp_port') dnsmasq._release_unused_leases() self.assertFalse( dnsmasq.device_manager.unplug.called) self.assertFalse( dnsmasq.device_manager.driver.unplug.called) def test_release_unused_leases_with_client_id(self): dnsmasq = self._get_dnsmasq(FakeDualNetwork()) ip1 = '192.168.1.2' mac1 = '00:00:80:aa:bb:cc' client_id1 = 'client1' ip2 = '192.168.1.3' mac2 = '00:00:80:cc:bb:aa' client_id2 = 'client2' ip6 = '2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d' old_leases = set([(ip1, mac1, client_id1), (ip2, mac2, client_id2)]) dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases) # Because the lease release code could fire multiple times, the # second read of the lease file must not have the entries that # would have been released. dnsmasq._read_leases_file_leases = mock.Mock( side_effect=[{ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'}, ip1: {'iaid': mac1, 'client_id': client_id1, 'server_id': 'server_id'}, ip2: {'iaid': mac2, 'client_id': client_id2, 'server_id': 'server_id'} }, {ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'} }]) dnsmasq._output_hosts_file = mock.Mock() dnsmasq._release_lease = mock.Mock() dnsmasq.network.ports = [] dnsmasq._release_unused_leases() dnsmasq._release_lease.assert_has_calls( [mock.call(mac1, ip1, constants.IP_VERSION_4, client_id1, 'server_id', mac1), mock.call(mac2, ip2, constants.IP_VERSION_4, client_id2, 'server_id', mac2)], any_order=True) def test_release_unused_leases_one_lease(self): dnsmasq = self._get_dnsmasq(FakeDualNetwork()) ip1 = '192.168.0.2' mac1 = '00:00:80:aa:bb:cc' ip2 = '192.168.0.3' mac2 = '00:00:80:cc:bb:aa' ip6 = '2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d' old_leases = set([(ip1, mac1, None), (ip2, mac2, None)]) dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases) # Because the lease release code could fire multiple times, the # second read of the lease file must not have the entries that # would have been released. dnsmasq._read_leases_file_leases = mock.Mock( side_effect=[{ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'}, ip2: {'iaid': mac2, 'client_id': None, 'server_id': 'server_id'} }, {ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'} }]) dnsmasq._output_hosts_file = mock.Mock() dnsmasq._release_lease = mock.Mock() dnsmasq.network.ports = [FakePort1()] dnsmasq._release_unused_leases() dnsmasq._release_lease.assert_called_once_with( mac2, ip2, constants.IP_VERSION_4, None, 'server_id', mac2) def test_release_unused_leases_one_lease_with_client_id(self): dnsmasq = self._get_dnsmasq(FakeDualNetwork()) ip1 = '192.168.0.2' mac1 = '00:00:80:aa:bb:cc' client_id1 = 'client1' ip2 = '192.168.0.5' mac2 = '00:00:0f:aa:bb:55' client_id2 = 'test5' ip6 = '2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d' old_leases = set([(ip1, mac1, client_id1), (ip2, mac2, client_id2)]) dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases) dnsmasq._output_hosts_file = mock.Mock() # Because the lease release code could fire multiple times, the # second read of the lease file must not have the entries that # would have been released. dnsmasq._read_leases_file_leases = mock.Mock( side_effect=[{ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'}, ip1: {'iaid': mac1, 'client_id': client_id1, 'server_id': 'server_id'} }, {ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'} }]) dnsmasq._release_lease = mock.Mock() dnsmasq.network.ports = [FakePort5()] dnsmasq._release_unused_leases() dnsmasq._release_lease.assert_called_once_with( mac1, ip1, constants.IP_VERSION_4, client_id1, 'server_id', mac1) def test_release_unused_leases_one_lease_with_client_id_none(self): dnsmasq = self._get_dnsmasq(FakeDualNetwork()) ip1 = '192.168.0.2' mac1 = '00:00:80:aa:bb:cc' client_id1 = 'client1' ip2 = '192.168.0.4' mac2 = '00:16:3E:C2:77:1D' client_id2 = 'test4' ip6 = '2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d' old_leases = set([(ip1, mac1, client_id1), (ip2, mac2, None)]) dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases) dnsmasq._output_hosts_file = mock.Mock() # Because the lease release code could fire multiple times, the # second read of the lease file must not have the entries that # would have been released. dnsmasq._read_leases_file_leases = mock.Mock( side_effect=[{ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'}, ip1: {'iaid': mac1, 'client_id': client_id1, 'server_id': 'server_id'}, ip2: {'iaid': mac2, 'client_id': client_id2, 'server_id': 'server_id'} }, {ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'}, ip2: {'iaid': mac2, 'client_id': client_id2, 'server_id': 'server_id'} }]) dnsmasq._release_lease = mock.Mock() dnsmasq.network.ports = [FakePort4()] dnsmasq._release_unused_leases() dnsmasq._release_lease.assert_called_once_with( mac1, ip1, constants.IP_VERSION_4, client_id1, 'server_id', mac1) def test_release_unused_leases_one_lease_from_leases_file(self): # leases file has a stale entry that is not in the host file dnsmasq = self._get_dnsmasq(FakeDualNetwork()) ip1 = '192.168.0.2' mac1 = '00:00:80:aa:bb:cc' ip2 = '192.168.0.3' mac2 = '00:00:80:cc:bb:aa' ip6 = '2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d' old_leases = set([(ip1, mac1, None)]) dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases) # Because the lease release code could fire multiple times, the # second read of the lease file must not have the entries that # would have been released. dnsmasq._read_leases_file_leases = mock.Mock( side_effect=[{ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'}, ip2: {'iaid': mac2, 'client_id': None, 'server_id': 'server_id'} }, {ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'} }]) dnsmasq._output_hosts_file = mock.Mock() dnsmasq._release_lease = mock.Mock() dnsmasq.network.ports = [FakePort1()] dnsmasq._release_unused_leases() dnsmasq._release_lease.assert_called_once_with( mac2, ip2, constants.IP_VERSION_4, None, 'server_id', mac2) @mock.patch.object(dhcp.LOG, 'warn') def _test_release_unused_leases_one_lease_mult_times(self, mock_log_warn, removed): # Simulate a dhcp_release failure where the lease remains in the # lease file despite multiple dhcp_release calls dnsmasq = self._get_dnsmasq(FakeDualNetwork()) ip1 = '192.168.0.2' mac1 = '00:00:80:aa:bb:cc' ip2 = '192.168.0.3' mac2 = '00:00:80:cc:bb:aa' ip6 = '2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d' old_leases = set([(ip1, mac1, None), (ip2, mac2, None)]) dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases) # Because the lease release code could fire multiple times, the # second and subsequent reads of the lease file must have the # entries that were not released. side_effect = [{ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'}, ip2: {'iaid': mac2, 'client_id': None, 'server_id': 'server_id'} }, {ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'}, ip2: {'iaid': mac2, 'client_id': None, 'server_id': 'server_id'} }, {ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'}, ip2: {'iaid': mac2, 'client_id': None, 'server_id': 'server_id'} }] # entry did/didn't go away after final dhcp_release try if not removed: side_effect.append( {ip6: {'iaid': 0xff, 'client_id': 'client_id', 'server_id': 'server_id'}, ip2: {'iaid': mac2, 'client_id': None, 'server_id': 'server_id'} }) else: side_effect.append({}) dnsmasq._read_leases_file_leases = mock.Mock(side_effect=side_effect) dnsmasq._output_hosts_file = mock.Mock() dnsmasq._release_lease = mock.Mock() dnsmasq.network.ports = [FakePort1()] dnsmasq._release_unused_leases() self.assertEqual(dhcp.DHCP_RELEASE_TRIES, dnsmasq._release_lease.call_count) self.assertEqual(dhcp.DHCP_RELEASE_TRIES + 1, dnsmasq._read_leases_file_leases.call_count) if not removed: self.assertTrue(mock_log_warn.called) def test_release_unused_leases_one_lease_mult_times_not_removed(self): self._test_release_unused_leases_one_lease_mult_times(False) def test_release_unused_leases_one_lease_mult_times_removed(self): self._test_release_unused_leases_one_lease_mult_times(True) def test_read_hosts_file_leases(self): filename = '/path/to/file' lines = ["00:00:80:aa:bb:cc,inst-name,192.168.0.1", "00:00:80:aa:bb:cc,inst-name,[fdca:3ba5:a17a::1]"] mock_open = self.useFixture( tools.OpenFixture(filename, '\n'.join(lines))).mock_open dnsmasq = self._get_dnsmasq(FakeDualNetwork()) leases = dnsmasq._read_hosts_file_leases(filename) self.assertEqual(set([("192.168.0.1", "00:00:80:aa:bb:cc", None), ("fdca:3ba5:a17a::1", "00:00:80:aa:bb:cc", None)]), leases) mock_open.assert_called_once_with(filename) def test_read_hosts_file_leases_with_client_id(self): filename = '/path/to/file' lines = ["00:00:80:aa:bb:cc,id:client1,inst-name,192.168.0.1", "00:00:80:aa:bb:cc,id:client2,inst-name," "[fdca:3ba5:a17a::1]"] mock_open = self.useFixture( tools.OpenFixture(filename, '\n'.join(lines))).mock_open dnsmasq = self._get_dnsmasq(FakeDualNetwork()) leases = dnsmasq._read_hosts_file_leases(filename) self.assertEqual(set([("192.168.0.1", "00:00:80:aa:bb:cc", 'client1'), ("fdca:3ba5:a17a::1", "00:00:80:aa:bb:cc", 'client2')]), leases) mock_open.assert_called_once_with(filename) def test_read_hosts_file_leases_with_stateless_IPv6_tag(self): filename = self.get_temp_file_path('leases') with open(filename, "w") as leasesfile: lines = [ "00:00:80:aa:bb:cc,id:client1,inst-name,192.168.0.1\n", "00:00:80:aa:bb:cc,set:ccccccccc-cccc-cccc-cccc-cccccccc\n", "00:00:80:aa:bb:cc,id:client2,inst-name,[fdca:3ba5:a17a::1]\n"] for line in lines: leasesfile.write(line) dnsmasq = self._get_dnsmasq(FakeDualNetwork()) leases = dnsmasq._read_hosts_file_leases(filename) self.assertEqual(set([("192.168.0.1", "00:00:80:aa:bb:cc", 'client1'), ("fdca:3ba5:a17a::1", "00:00:80:aa:bb:cc", 'client2')]), leases) def _test_read_leases_file_leases(self, ip_version, add_bad_line=False): filename = '/path/to/file' lines = [ "1472673289 aa:bb:cc:00:00:02 192.168.1.2 host-192-168-1-2 *", "1472673289 aa:bb:cc:00:00:03 192.168.1.3 host-192-168-1-3 *", "1472673289 aa:bb:cc:00:00:04 192.168.1.4 host-192-168-1-4 *", "duid 00:01:00:01:02:03:04:05:06:07:08:09:0a:0b", "1472597740 1044800001 [2001:DB8::a] host-2001-db8--a " "00:04:4a:d0:d2:34:19:2b:49:08:84:e8:34:bd:0c:dc:b9:3b", "1472597823 1044800002 [2001:DB8::b] host-2001-db8--b " "00:04:ce:96:53:3d:f2:c2:4c:4c:81:7d:db:c9:8d:d2:74:22:3b:0a", "1472599048 1044800003 [2001:DB8::c] host-2001-db8--c " "00:04:4f:f0:cd:ca:5e:77:41:bc:9d:7f:5c:33:31:37:5d:80:77:b4" ] bad_line = '1472673289 aa:bb:cc:00:00:05 192.168.1.5 host-192.168-1-5' if add_bad_line: lines.append(bad_line) mock_open = self.useFixture( tools.OpenFixture(filename, '\n'.join(lines))).mock_open dnsmasq = self._get_dnsmasq(FakeDualNetwork()) with mock.patch('os.path.exists', return_value=True), \ mock.patch.object(dhcp.LOG, 'warning') as mock_log_warn: leases = dnsmasq._read_leases_file_leases(filename, ip_version) server_id = '00:01:00:01:02:03:04:05:06:07:08:09:0a:0b' entry1 = {'iaid': '1044800001', 'client_id': '00:04:4a:d0:d2:34:19:2b:49:08:84:' 'e8:34:bd:0c:dc:b9:3b', 'server_id': server_id } entry2 = {'iaid': '1044800002', 'client_id': '00:04:ce:96:53:3d:f2:c2:4c:4c:81:' '7d:db:c9:8d:d2:74:22:3b:0a', 'server_id': server_id } entry3 = {'iaid': '1044800003', 'client_id': '00:04:4f:f0:cd:ca:5e:77:41:bc:9d:' '7f:5c:33:31:37:5d:80:77:b4', 'server_id': server_id } v6_expected = {'2001:DB8::a': entry1, '2001:DB8::b': entry2, '2001:DB8::c': entry3 } entry4 = {'iaid': 'aa:bb:cc:00:00:02', 'client_id': '*', 'server_id': None } entry5 = {'iaid': 'aa:bb:cc:00:00:03', 'client_id': '*', 'server_id': None } entry6 = {'iaid': 'aa:bb:cc:00:00:04', 'client_id': '*', 'server_id': None } v4_expected = {'192.168.1.2': entry4, '192.168.1.3': entry5, '192.168.1.4': entry6 } expected = {} if not ip_version or ip_version == constants.IP_VERSION_6: expected.update(v6_expected) if not ip_version or ip_version == constants.IP_VERSION_4: expected.update(v4_expected) mock_open.assert_called_once_with(filename) self.assertEqual(expected, leases) if add_bad_line: self.assertTrue(mock_log_warn.called) def test_read_v6_leases_file_leases(self): self._test_read_leases_file_leases(constants.IP_VERSION_6) def test_read_v4_leases_file_leases(self): self._test_read_leases_file_leases(constants.IP_VERSION_4) def test_read_all_leases_file_leases(self): self._test_read_leases_file_leases(None) def test_read_all_leases_file_leases_with_bad_line(self): self._test_read_leases_file_leases(None, True) def test_make_subnet_interface_ip_map(self): with mock.patch('neutron.agent.linux.ip_lib.IPDevice') as ip_dev: ip_dev.return_value.addr.list.return_value = [ {'cidr': '192.168.0.1/24'} ] dm = self._get_dnsmasq(FakeDualNetwork()) self.assertEqual( dm._make_subnet_interface_ip_map(), {FakeV4Subnet().id: '192.168.0.1'} ) def test_remove_config_files(self): net = FakeV4Network() path = '/opt/data/neutron/dhcp' self.conf.dhcp_confs = path lp = LocalChild(self.conf, net) lp._remove_config_files() self.rmtree.assert_called_once_with(os.path.join(path, net.id), ignore_errors=True) def test_existing_dhcp_networks(self): path = '/opt/data/neutron/dhcp' self.conf.dhcp_confs = path cases = { # network_uuid --> is_dhcp_alive? 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa': True, 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb': False, 'not_uuid_like_name': True } def active_fake(self, instance, cls): return cases[instance.network.id] with mock.patch('os.listdir') as mock_listdir: with mock.patch.object(dhcp.Dnsmasq, 'active') as mock_active: mock_active.__get__ = active_fake mock_listdir.return_value = list(cases) result = dhcp.Dnsmasq.existing_dhcp_networks(self.conf) mock_listdir.assert_called_once_with(path) self.assertItemsEqual(['aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'], result) def test__output_hosts_file_log_only_twice(self): dm = self._get_dnsmasq(FakeDualStackNetworkSingleDHCP()) with mock.patch.object(dhcp, 'LOG') as logger: logger.process.return_value = ('fake_message', {}) dm._output_hosts_file() # The method logs twice, at the start of and the end. There should be # no other logs, no matter how many hosts there are to dump in the # file. self.assertEqual(2, len(logger.method_calls)) def test_only_populates_dhcp_enabled_subnets(self): exp_host_name = '/dhcp/eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee/host' exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2.openstacklocal.,' '192.168.0.2\n' '00:16:3E:C2:77:1D,host-192-168-0-4.openstacklocal.,' '192.168.0.4\n' '00:00:0f:rr:rr:rr,host-192-168-0-1.openstacklocal.,' '192.168.0.1\n').lstrip() dm = self._get_dnsmasq(FakeDualStackNetworkSingleDHCP()) dm._output_hosts_file() self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data)]) def test_only_populates_dhcp_client_id(self): exp_host_name = '/dhcp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/host' exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2.openstacklocal.,' '192.168.0.2\n' '00:00:0f:aa:bb:55,id:test5,' 'host-192-168-0-5.openstacklocal.,' '192.168.0.5\n' '00:00:0f:aa:bb:66,id:test6,' 'host-192-168-0-6.openstacklocal.,192.168.0.6,' 'set:ccccccccc-cccc-cccc-cccc-ccccccccc\n').lstrip() dm = self._get_dnsmasq(FakeV4NetworkClientId()) dm._output_hosts_file() self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data)]) def test_only_populates_dhcp_enabled_subnet_on_a_network(self): exp_host_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host' exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2.openstacklocal.,' '192.168.0.2\n' '00:00:f3:aa:bb:cc,host-192-168-0-3.openstacklocal.,' '192.168.0.3\n' '00:00:0f:aa:bb:cc,host-192-168-0-4.openstacklocal.,' '192.168.0.4\n' '00:00:0f:rr:rr:rr,host-192-168-0-1.openstacklocal.,' '192.168.0.1\n').lstrip() dm = self._get_dnsmasq(FakeDualNetworkSingleDHCP()) dm._output_hosts_file() self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data)]) def test_host_and_opts_file_on_stateless_dhcpv6_network(self): exp_host_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/host' exp_host_data = ('00:16:3e:c2:77:1d,' 'set:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh\n').lstrip() exp_opt_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/opts' exp_opt_data = ('tag:tag0,option6:domain-search,openstacklocal\n' 'tag:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' 'option6:dns-server,ffea:3ba5:a17a:4ba3::100').lstrip() dm = self._get_dnsmasq(FakeV6NetworkStatelessDHCP()) dm._output_hosts_file() dm._output_opts_file() self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data), mock.call(exp_opt_name, exp_opt_data)]) def test_host_and_opts_file_on_stateless_dhcpv6_network_no_dns(self): exp_host_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/host' exp_opt_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/opts' exp_opt_data = ('tag:tag0,option6:dns-server\n' 'tag:tag0,' 'option6:domain-search,openstacklocal').lstrip() dm = self._get_dnsmasq(FakeV6NetworkStatelessDHCPNoDnsProvided()) dm._output_hosts_file() dm._output_opts_file() self.safe.assert_has_calls([mock.call(exp_host_name, ''), mock.call(exp_opt_name, exp_opt_data)]) def test_host_file_on_net_with_v6_slaac_and_v4(self): exp_host_name = '/dhcp/eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee/host' exp_host_data = ( '00:00:80:aa:bb:cc,host-192-168-0-2.openstacklocal.,192.168.0.2,' 'set:eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee\n' '00:16:3E:C2:77:1D,host-192-168-0-4.openstacklocal.,192.168.0.4,' 'set:gggggggg-gggg-gggg-gggg-gggggggggggg\n00:00:0f:rr:rr:rr,' 'host-192-168-0-1.openstacklocal.,192.168.0.1,' 'set:rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr\n').lstrip() dm = self._get_dnsmasq(FakeDualStackNetworkingSingleDHCPTags()) dm._output_hosts_file() self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data)]) def test_host_and_opts_file_on_net_with_V6_stateless_and_V4_subnets( self): exp_host_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/host' exp_host_data = ( '00:16:3e:c2:77:1d,set:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh\n' '00:16:3e:c2:77:1d,host-192-168-0-3.openstacklocal.,' '192.168.0.3,set:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh\n' '00:00:0f:rr:rr:rr,' 'host-192-168-0-1.openstacklocal.,192.168.0.1\n').lstrip() exp_opt_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/opts' exp_opt_data = ( 'tag:tag0,option6:domain-search,openstacklocal\n' 'tag:tag1,option:dns-server,8.8.8.8\n' 'tag:tag1,option:classless-static-route,20.0.0.1/24,20.0.0.1,' '169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag1,249,20.0.0.1/24,20.0.0.1,169.254.169.254/32,' '192.168.0.1,0.0.0.0/0,192.168.0.1\n' 'tag:tag1,option:router,192.168.0.1\n' 'tag:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' 'option6:dns-server,ffea:3ba5:a17a:4ba3::100').lstrip() dm = self._get_dnsmasq(FakeNetworkWithV6SatelessAndV4DHCPSubnets()) dm._output_hosts_file() dm._output_opts_file() self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data), mock.call(exp_opt_name, exp_opt_data)]) def test_has_metadata_subnet_returns_true(self): self.assertTrue(dhcp.Dnsmasq.has_metadata_subnet( [FakeV4MetadataSubnet()])) def test_has_metadata_subnet_returns_false(self): self.assertFalse(dhcp.Dnsmasq.has_metadata_subnet( [FakeV4Subnet()])) def test_should_enable_metadata_isolated_network_returns_true(self): self.assertTrue(dhcp.Dnsmasq.should_enable_metadata( self.conf, FakeV4NetworkNoRouter())) def test_should_enable_metadata_non_isolated_network_returns_false(self): self.assertFalse(dhcp.Dnsmasq.should_enable_metadata( self.conf, FakeV4NetworkDistRouter())) def test_should_enable_metadata_isolated_meta_disabled_returns_false(self): self.conf.set_override('enable_isolated_metadata', False) self.assertFalse(dhcp.Dnsmasq.should_enable_metadata( self.conf, FakeV4MetadataNetwork())) def test_should_enable_metadata_with_metadata_network_returns_true(self): self.conf.set_override('enable_metadata_network', True) self.assertTrue(dhcp.Dnsmasq.should_enable_metadata( self.conf, FakeV4MetadataNetwork())) def test_should_force_metadata_returns_true(self): self.conf.set_override("force_metadata", True) self.assertTrue(dhcp.Dnsmasq.should_enable_metadata( self.conf, FakeDualNetworkDualDHCP())) def _test__generate_opts_per_subnet_helper( self, config_opts, expected_mdt_ip, network_class=FakeNetworkDhcpPort): for key, value in config_opts.items(): self.conf.set_override(key, value) dm = self._get_dnsmasq(network_class()) with mock.patch('neutron.agent.linux.ip_lib.IPDevice') as ipdev_mock: list_addr = ipdev_mock.return_value.addr.list list_addr.return_value = [{'cidr': alloc.ip_address + '/24'} for alloc in FakeDhcpPort().fixed_ips] options, idx_map = dm._generate_opts_per_subnet() contains_metadata_ip = any(['%s/32' % dhcp.METADATA_DEFAULT_IP in line for line in options]) self.assertEqual(expected_mdt_ip, contains_metadata_ip) def test__generate_opts_per_subnet_no_metadata(self): config = {'enable_isolated_metadata': False, 'force_metadata': False} self._test__generate_opts_per_subnet_helper(config, False) def test__generate_opts_per_subnet_isolated_metadata_with_router(self): config = {'enable_isolated_metadata': True, 'force_metadata': False} self._test__generate_opts_per_subnet_helper(config, True) def test__generate_opts_per_subnet_forced_metadata(self): config = {'enable_isolated_metadata': False, 'force_metadata': True} self._test__generate_opts_per_subnet_helper(config, True) def test__generate_opts_per_subnet_forced_metadata_non_local_subnet(self): config = {'enable_isolated_metadata': False, 'force_metadata': True} self._test__generate_opts_per_subnet_helper( config, True, network_class=FakeNonLocalSubnets) def test_client_id_num(self): dm = self._get_dnsmasq(FakeV4NetworkClientIdNum()) self.assertEqual('test_client_id_num', dm._get_client_id(FakePortWithClientIdNum())) class TestDeviceManager(TestConfBase): def setUp(self): super(TestDeviceManager, self).setUp() ip_lib_patcher = mock.patch('neutron.agent.linux.dhcp.ip_lib') load_interface_driver_patcher = mock.patch( 'neutron.agent.linux.dhcp.agent_common_utils.' 'load_interface_driver') self.mock_ip_lib = ip_lib_patcher.start() self.mock_load_interface_driver = load_interface_driver_patcher.start() def _test_setup(self, load_interface_driver, ip_lib, use_gateway_ips): with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: # Create DeviceManager. self.conf.register_opt(cfg.BoolOpt('enable_isolated_metadata', default=False)) self.conf.register_opt(cfg.BoolOpt('force_metadata', default=False)) plugin = mock.Mock() device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.return_value = None mgr = dhcp.DeviceManager(self.conf, plugin) load_interface_driver.assert_called_with(self.conf) # Setup with no existing DHCP port - expect a new DHCP port to # be created. network = FakeDeviceManagerNetwork() network.tenant_id = 'Tenant A' def mock_create(dict): port = dhcp.DictModel(dict['port']) port.id = 'abcd-123456789' port.mac_address = '00-12-34-56-78-90' port.fixed_ips = [ dhcp.DictModel({'subnet_id': ip['subnet_id'], 'ip_address': 'unique-IP-address'}) for ip in port.fixed_ips ] # server rudely gave us an extra address we didn't ask for port.fixed_ips.append(dhcp.DictModel( {'subnet_id': 'ffffffff-6666-6666-6666-ffffffffffff', 'ip_address': '2003::f816:3eff:fe45:e893'})) return port plugin.create_dhcp_port.side_effect = mock_create mgr.driver.get_device_name.return_value = 'ns-XXX' mgr.driver.use_gateway_ips = use_gateway_ips ip_lib.ensure_device_is_ready.return_value = True mgr.setup(network) plugin.create_dhcp_port.assert_called_with(mock.ANY) mgr.driver.init_l3.assert_called_with('ns-XXX', mock.ANY, namespace='qdhcp-ns') cidrs = set(mgr.driver.init_l3.call_args[0][1]) if use_gateway_ips: self.assertEqual(cidrs, set(['%s/%s' % (s.gateway_ip, s.cidr.split('/')[1]) for s in network.subnets])) else: self.assertEqual(cidrs, set(['unique-IP-address/24', 'unique-IP-address/64'])) # Now call setup again. This time we go through the existing # port code path, and the driver's init_l3 method is called # again. plugin.create_dhcp_port.reset_mock() mgr.driver.init_l3.reset_mock() mgr.setup(network) mgr.driver.init_l3.assert_called_with('ns-XXX', mock.ANY, namespace='qdhcp-ns') cidrs = set(mgr.driver.init_l3.call_args[0][1]) if use_gateway_ips: self.assertEqual(cidrs, set(['%s/%s' % (s.gateway_ip, s.cidr.split('/')[1]) for s in network.subnets])) else: self.assertEqual(cidrs, set(['unique-IP-address/24', 'unique-IP-address/64'])) self.assertFalse(plugin.create_dhcp_port.called) def test_setup_device_manager_dhcp_port_without_gateway_ips(self): self._test_setup(self.mock_load_interface_driver, self.mock_ip_lib, use_gateway_ips=False) def test_setup_device_manager_dhcp_port_with_gateway_ips(self): self._test_setup(self.mock_load_interface_driver, self.mock_ip_lib, use_gateway_ips=True) def _test_setup_reserved(self, enable_isolated_metadata=False, force_metadata=False): with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: # Create DeviceManager. self.conf.register_opt( cfg.BoolOpt('enable_isolated_metadata', default=enable_isolated_metadata)) self.conf.register_opt( cfg.BoolOpt('force_metadata', default=force_metadata)) plugin = mock.Mock() device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.return_value = None mgr = dhcp.DeviceManager(self.conf, plugin) self.mock_load_interface_driver.assert_called_with(self.conf) # Setup with a reserved DHCP port. network = FakeDualNetworkReserved() network.tenant_id = 'Tenant A' reserved_port = network.ports[-1] def mock_update(port_id, dict): port = reserved_port port.network_id = dict['port']['network_id'] port.device_id = dict['port']['device_id'] return port plugin.update_dhcp_port.side_effect = mock_update mgr.driver.get_device_name.return_value = 'ns-XXX' mgr.driver.use_gateway_ips = False self.mock_ip_lib.ensure_device_is_ready.return_value = True mgr.setup(network) plugin.update_dhcp_port.assert_called_with(reserved_port.id, mock.ANY) expect_ips = ['192.168.0.6/24', 'fdca:3ba5:a17a:4ba3::2/64'] if enable_isolated_metadata or force_metadata: expect_ips.append(dhcp.METADATA_DEFAULT_CIDR) mgr.driver.init_l3.assert_called_with('ns-XXX', expect_ips, namespace='qdhcp-ns') def test_setup_reserved_and_disable_metadata(self): """Test reserved port case of DeviceManager's DHCP port setup logic which metadata disabled. """ self._test_setup_reserved() def test_setup_reserved_with_isolated_metadata_enable(self): """Test reserved port case of DeviceManager's DHCP port setup logic which isolated_ metadata enabled. """ self._test_setup_reserved(enable_isolated_metadata=True) def test_setup_reserved_with_force_metadata_enable(self): """Test reserved port case of DeviceManager's DHCP port setup logic which force_metadata enabled. """ self._test_setup_reserved(force_metadata=True) def test_setup_reserved_and_enable_metadata(self): """Test reserved port case of DeviceManager's DHCP port setup logic which both isolated_metadata and force_metadata enabled. """ self._test_setup_reserved(enable_isolated_metadata=True, force_metadata=True) def test_setup_reserved_2(self): """Test scenario where a network has two reserved ports, and update_dhcp_port fails for the first of those. """ with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: # Create DeviceManager. self.conf.register_opt( cfg.BoolOpt('enable_isolated_metadata', default=False)) self.conf.register_opt( cfg.BoolOpt('force_metadata', default=False)) plugin = mock.Mock() device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.return_value = None mgr = dhcp.DeviceManager(self.conf, plugin) self.mock_load_interface_driver.assert_called_with(self.conf) # Setup with a reserved DHCP port. network = FakeDualNetworkReserved2() network.tenant_id = 'Tenant A' reserved_port_1 = network.ports[-2] reserved_port_2 = network.ports[-1] def mock_update(port_id, dict): if port_id == reserved_port_1.id: return None port = reserved_port_2 port.network_id = dict['port']['network_id'] port.device_id = dict['port']['device_id'] return port plugin.update_dhcp_port.side_effect = mock_update mgr.driver.get_device_name.return_value = 'ns-XXX' mgr.driver.use_gateway_ips = False self.mock_ip_lib.ensure_device_is_ready.return_value = True mgr.setup(network) plugin.update_dhcp_port.assert_called_with(reserved_port_2.id, mock.ANY) mgr.driver.init_l3.assert_called_with( 'ns-XXX', ['192.168.0.6/24', 'fdca:3ba5:a17a:4ba3::2/64'], namespace='qdhcp-ns') def test__setup_reserved_dhcp_port_with_fake_remote_error(self): """Test scenario where a fake_network has two reserved ports, and update_dhcp_port fails for the first of those with a RemoteError. """ # Setup with a reserved DHCP port. fake_network = FakeDualNetworkReserved2() fake_network.tenant_id = 'Tenant A' reserved_port_2 = fake_network.ports[-1] mock_plugin = mock.Mock() dh = dhcp.DeviceManager(cfg.CONF, mock_plugin) messaging_error = oslo_messaging.RemoteError( exc_type='FakeRemoteError') mock_plugin.update_dhcp_port.side_effect = [messaging_error, reserved_port_2] with testtools.ExpectedException(oslo_messaging.RemoteError): dh.setup_dhcp_port(fake_network) class TestDictModel(base.BaseTestCase): def test_string_representation_port(self): port = dhcp.DictModel({'id': 'id', 'network_id': 'net_id'}) self.assertEqual('id=id, network_id=net_id', str(port)) def test_string_representation_network(self): net = dhcp.DictModel({'id': 'id', 'name': 'myname'}) self.assertEqual('id=id, name=myname', str(net)) neutron-12.1.1/neutron/tests/unit/agent/linux/test_ip_conntrack.py0000664000175000017500000000306213553660046025454 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron.agent.linux import ip_conntrack from neutron.tests import base class IPConntrackTestCase(base.BaseTestCase): def setUp(self): super(IPConntrackTestCase, self).setUp() self.execute = mock.Mock() self.filtered_port = {} self.unfiltered_port = {} self.mgr = ip_conntrack.IpConntrackManager( self._get_rule_for_table, self.filtered_port, self.unfiltered_port, self.execute, zone_per_port=True) def _get_rule_for_table(self, table): return ['test --physdev-in tapdevice -j CT --zone 100'] def test_delete_conntrack_state_dedupes(self): rule = {'ethertype': 'IPv4', 'direction': 'ingress'} dev_info = {'device': 'tapdevice', 'fixed_ips': ['1.2.3.4']} dev_info_list = [dev_info for _ in range(10)] self.mgr._delete_conntrack_state(dev_info_list, rule) self.assertEqual(1, len(self.execute.mock_calls)) neutron-12.1.1/neutron/tests/unit/agent/linux/test_ovsdb_monitor.py0000664000175000017500000000726313553660047025676 0ustar zuulzuul00000000000000# Copyright 2013 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.agent.common import ovs_lib from neutron.agent.linux import ovsdb_monitor from neutron.agent.ovsdb.native import helpers from neutron.tests import base class TestOvsdbMonitor(base.BaseTestCase): def setUp(self): super(TestOvsdbMonitor, self).setUp() mock.patch.object(helpers, 'enable_connection_uri').start() def test___init__(self): ovsdb_monitor.OvsdbMonitor('Interface') def test___init___with_columns(self): columns = ['col1', 'col2'] with mock.patch( 'neutron.agent.linux.async_process.AsyncProcess.__init__') as init: ovsdb_monitor.OvsdbMonitor('Interface', columns=columns) cmd = init.call_args_list[0][0][0] self.assertEqual('col1,col2', cmd[-1]) def test___init___with_format(self): with mock.patch( 'neutron.agent.linux.async_process.AsyncProcess.__init__') as init: ovsdb_monitor.OvsdbMonitor('Interface', format='blob') cmd = init.call_args_list[0][0][0] self.assertEqual('--format=blob', cmd[-1]) def test__init__with_connection_columns(self): conn_info = 'tcp:10.10.10.10:6640' columns = ['col1', 'col2'] with mock.patch( 'neutron.agent.linux.async_process.AsyncProcess.__init__') as init: ovsdb_monitor.OvsdbMonitor('Interface', columns=columns, ovsdb_connection=conn_info) cmd_all = init.call_args_list[0][0][0] cmd_expect = ['ovsdb-client', 'monitor', 'tcp:10.10.10.10:6640', 'Interface', 'col1,col2'] self.assertEqual(cmd_expect, cmd_all) class TestSimpleInterfaceMonitor(base.BaseTestCase): def setUp(self): super(TestSimpleInterfaceMonitor, self).setUp() self.monitor = ovsdb_monitor.SimpleInterfaceMonitor() def test_has_updates_is_false_if_active_with_no_output(self): target = ('neutron.agent.linux.ovsdb_monitor.SimpleInterfaceMonitor' '.is_active') with mock.patch(target, return_value=True): self.assertFalse(self.monitor.has_updates) def test_has_updates_after_calling_get_events_is_false(self): with mock.patch.object( self.monitor, 'process_events') as process_events: self.monitor.new_events = {'added': ['foo'], 'removed': ['foo1']} self.assertTrue(self.monitor.has_updates) self.monitor.get_events() self.assertTrue(process_events.called) self.assertFalse(self.monitor.has_updates) def process_event_unassigned_of_port(self): output = '{"data":[["e040fbec-0579-4990-8324-d338da33ae88","insert",' output += '"m50",["set",[]],["map",[]]]],"headings":["row","action",' output += '"name","ofport","external_ids"]}' with mock.patch.object( self.monitor, 'iter_stdout', return_value=[output]): self.monitor.process_events() self.assertEqual(self.monitor.new_events['added'][0]['ofport'], ovs_lib.UNASSIGNED_OFPORT) neutron-12.1.1/neutron/tests/unit/agent/linux/test_keepalived.py0000664000175000017500000004020713553660047025116 0ustar zuulzuul00000000000000# Copyright (C) 2014 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import os import textwrap import mock from neutron_lib import constants as n_consts import testtools from neutron.agent.linux import keepalived from neutron.tests import base # Keepalived user guide: # http://www.keepalived.org/pdf/UserGuide.pdf KEEPALIVED_GLOBAL_CONFIG = textwrap.dedent("""\ global_defs { notification_email_from %(email_from)s router_id %(router_id)s }""") % dict( email_from=keepalived.KEEPALIVED_EMAIL_FROM, router_id=keepalived.KEEPALIVED_ROUTER_ID) VRRP_ID = 1 VRRP_INTERVAL = 5 class KeepalivedGetFreeRangeTestCase(base.BaseTestCase): def test_get_free_range(self): free_range = keepalived.get_free_range( parent_range='169.254.0.0/16', excluded_ranges=['169.254.0.0/24', '169.254.1.0/24', '169.254.2.0/24'], size=24) self.assertEqual('169.254.3.0/24', free_range) def test_get_free_range_without_excluded(self): free_range = keepalived.get_free_range( parent_range='169.254.0.0/16', excluded_ranges=[], size=20) self.assertEqual('169.254.0.0/20', free_range) def test_get_free_range_excluded_out_of_parent(self): free_range = keepalived.get_free_range( parent_range='169.254.0.0/16', excluded_ranges=['255.255.255.0/24'], size=24) self.assertEqual('169.254.0.0/24', free_range) def test_get_free_range_not_found(self): tiny_parent_range = '192.168.1.0/24' huge_size = 8 with testtools.ExpectedException(ValueError): keepalived.get_free_range( parent_range=tiny_parent_range, excluded_ranges=[], size=huge_size) class KeepalivedConfBaseMixin(object): def _get_config(self): config = keepalived.KeepalivedConf() instance1 = keepalived.KeepalivedInstance('MASTER', 'eth0', 1, ['169.254.192.0/18'], advert_int=5) instance1.set_authentication('AH', 'pass123') instance1.track_interfaces.append("eth0") vip_address1 = keepalived.KeepalivedVipAddress('192.168.1.0/24', 'eth1') vip_address2 = keepalived.KeepalivedVipAddress('192.168.2.0/24', 'eth2') vip_address3 = keepalived.KeepalivedVipAddress('192.168.3.0/24', 'eth2') vip_address_ex = keepalived.KeepalivedVipAddress('192.168.55.0/24', 'eth10') instance1.vips.append(vip_address1) instance1.vips.append(vip_address2) instance1.vips.append(vip_address3) instance1.vips.append(vip_address_ex) virtual_route = keepalived.KeepalivedVirtualRoute(n_consts.IPv4_ANY, "192.168.1.1", "eth1") instance1.virtual_routes.gateway_routes = [virtual_route] instance2 = keepalived.KeepalivedInstance('MASTER', 'eth4', 2, ['169.254.192.0/18'], mcast_src_ip='224.0.0.1') instance2.track_interfaces.append("eth4") vip_address1 = keepalived.KeepalivedVipAddress('192.168.3.0/24', 'eth6') instance2.vips.append(vip_address1) instance2.vips.append(vip_address2) instance2.vips.append(vip_address_ex) config.add_instance(instance1) config.add_instance(instance2) return config class KeepalivedConfTestCase(base.BaseTestCase, KeepalivedConfBaseMixin): expected = KEEPALIVED_GLOBAL_CONFIG + textwrap.dedent(""" vrrp_instance VR_1 { state MASTER interface eth0 virtual_router_id 1 priority 50 garp_master_delay 60 advert_int 5 authentication { auth_type AH auth_pass pass123 } track_interface { eth0 } virtual_ipaddress { 169.254.0.1/24 dev eth0 } virtual_ipaddress_excluded { 192.168.1.0/24 dev eth1 192.168.2.0/24 dev eth2 192.168.3.0/24 dev eth2 192.168.55.0/24 dev eth10 } virtual_routes { 0.0.0.0/0 via 192.168.1.1 dev eth1 } } vrrp_instance VR_2 { state MASTER interface eth4 virtual_router_id 2 priority 50 garp_master_delay 60 mcast_src_ip 224.0.0.1 track_interface { eth4 } virtual_ipaddress { 169.254.0.2/24 dev eth4 } virtual_ipaddress_excluded { 192.168.2.0/24 dev eth2 192.168.3.0/24 dev eth6 192.168.55.0/24 dev eth10 } }""") def test_config_generation(self): config = self._get_config() self.assertEqual(self.expected, config.get_config_str()) def test_config_with_reset(self): config = self._get_config() self.assertEqual(self.expected, config.get_config_str()) config.reset() self.assertEqual(KEEPALIVED_GLOBAL_CONFIG, config.get_config_str()) def test_get_existing_vip_ip_addresses_returns_list(self): config = self._get_config() instance = config.get_instance(1) current_vips = sorted(instance.get_existing_vip_ip_addresses('eth2')) self.assertEqual(['192.168.2.0/24', '192.168.3.0/24'], current_vips) class KeepalivedStateExceptionTestCase(base.BaseTestCase): def test_state_exception(self): invalid_vrrp_state = 'a seal walks' self.assertRaises(keepalived.InvalidInstanceStateException, keepalived.KeepalivedInstance, invalid_vrrp_state, 'eth0', 33, ['169.254.192.0/18']) invalid_auth_type = 'into a club' instance = keepalived.KeepalivedInstance('MASTER', 'eth0', 1, ['169.254.192.0/18']) self.assertRaises(keepalived.InvalidAuthenticationTypeException, instance.set_authentication, invalid_auth_type, 'some_password') class KeepalivedInstanceRoutesTestCase(base.BaseTestCase): @classmethod def _get_instance_routes(cls): routes = keepalived.KeepalivedInstanceRoutes() default_gw_eth0 = keepalived.KeepalivedVirtualRoute( '0.0.0.0/0', '1.0.0.254', 'eth0') default_gw_eth1 = keepalived.KeepalivedVirtualRoute( '::/0', 'fe80::3e97:eff:fe26:3bfa/64', 'eth1') routes.gateway_routes = [default_gw_eth0, default_gw_eth1] extra_routes = [ keepalived.KeepalivedVirtualRoute('10.0.0.0/8', '1.0.0.1'), keepalived.KeepalivedVirtualRoute('20.0.0.0/8', '2.0.0.2')] routes.extra_routes = extra_routes extra_subnets = [ keepalived.KeepalivedVirtualRoute( '30.0.0.0/8', None, 'eth0', scope='link')] routes.extra_subnets = extra_subnets return routes def test_routes(self): routes = self._get_instance_routes() self.assertEqual(len(routes.routes), 5) def test_remove_routes_on_interface(self): routes = self._get_instance_routes() routes.remove_routes_on_interface('eth0') self.assertEqual(len(routes.routes), 3) routes.remove_routes_on_interface('eth1') self.assertEqual(len(routes.routes), 2) def test_build_config(self): expected = """ virtual_routes { 0.0.0.0/0 via 1.0.0.254 dev eth0 ::/0 via fe80::3e97:eff:fe26:3bfa/64 dev eth1 10.0.0.0/8 via 1.0.0.1 20.0.0.0/8 via 2.0.0.2 30.0.0.0/8 dev eth0 scope link }""" routes = self._get_instance_routes() self.assertEqual(expected, '\n'.join(routes.build_config())) class KeepalivedInstanceTestCase(base.BaseTestCase, KeepalivedConfBaseMixin): def test_get_primary_vip(self): instance = keepalived.KeepalivedInstance('MASTER', 'ha0', 42, ['169.254.192.0/18']) self.assertEqual('169.254.0.42/24', instance.get_primary_vip()) def test_remove_addresses_by_interface(self): config = self._get_config() instance = config.get_instance(1) instance.remove_vips_vroutes_by_interface('eth2') instance.remove_vips_vroutes_by_interface('eth10') expected = KEEPALIVED_GLOBAL_CONFIG + textwrap.dedent(""" vrrp_instance VR_1 { state MASTER interface eth0 virtual_router_id 1 priority 50 garp_master_delay 60 advert_int 5 authentication { auth_type AH auth_pass pass123 } track_interface { eth0 } virtual_ipaddress { 169.254.0.1/24 dev eth0 } virtual_ipaddress_excluded { 192.168.1.0/24 dev eth1 } virtual_routes { 0.0.0.0/0 via 192.168.1.1 dev eth1 } } vrrp_instance VR_2 { state MASTER interface eth4 virtual_router_id 2 priority 50 garp_master_delay 60 mcast_src_ip 224.0.0.1 track_interface { eth4 } virtual_ipaddress { 169.254.0.2/24 dev eth4 } virtual_ipaddress_excluded { 192.168.2.0/24 dev eth2 192.168.3.0/24 dev eth6 192.168.55.0/24 dev eth10 } }""") self.assertEqual(expected, config.get_config_str()) def test_build_config_no_vips(self): expected = textwrap.dedent("""\ vrrp_instance VR_1 { state MASTER interface eth0 virtual_router_id 1 priority 50 garp_master_delay 60 virtual_ipaddress { 169.254.0.1/24 dev eth0 } }""") instance = keepalived.KeepalivedInstance( 'MASTER', 'eth0', VRRP_ID, ['169.254.192.0/18']) self.assertEqual(expected, os.linesep.join(instance.build_config())) def test_build_config_no_vips_track_script(self): expected = """ vrrp_script ha_health_check_1 { script "/etc/ha_confs/qrouter-x/ha_check_script_1.sh" interval 5 fall 2 rise 2 } vrrp_instance VR_1 { state MASTER interface eth0 virtual_router_id 1 priority 50 garp_master_delay 60 virtual_ipaddress { 169.254.0.1/24 dev eth0 } }""" instance = keepalived.KeepalivedInstance( 'MASTER', 'eth0', VRRP_ID, ['169.254.192.0/18']) instance.track_script = keepalived.KeepalivedTrackScript( VRRP_INTERVAL, '/etc/ha_confs/qrouter-x', VRRP_ID) self.assertEqual(expected, '\n'.join(instance.build_config())) class KeepalivedVipAddressTestCase(base.BaseTestCase): def test_vip_with_scope(self): vip = keepalived.KeepalivedVipAddress('fe80::3e97:eff:fe26:3bfa/64', 'eth1', 'link') self.assertEqual('fe80::3e97:eff:fe26:3bfa/64 dev eth1 scope link', vip.build_config()) def test_add_vip_idempotent(self): instance = keepalived.KeepalivedInstance('MASTER', 'eth0', 1, ['169.254.192.0/18']) instance.add_vip('192.168.222.1/32', 'eth11', None) instance.add_vip('192.168.222.1/32', 'eth12', 'link') self.assertEqual(1, len(instance.vips)) class KeepalivedVirtualRouteTestCase(base.BaseTestCase): def test_virtual_route_with_dev(self): route = keepalived.KeepalivedVirtualRoute(n_consts.IPv4_ANY, '1.2.3.4', 'eth0') self.assertEqual('0.0.0.0/0 via 1.2.3.4 dev eth0', route.build_config()) def test_virtual_route_without_dev(self): route = keepalived.KeepalivedVirtualRoute('50.0.0.0/8', '1.2.3.4') self.assertEqual('50.0.0.0/8 via 1.2.3.4', route.build_config()) class KeepalivedTrackScriptTestCase(base.BaseTestCase): def test_build_config_preamble(self): exp_conf = [ '', 'vrrp_script ha_health_check_1 {', ' script "/etc/ha_confs/qrouter-x/ha_check_script_1.sh"', ' interval 5', ' fall 2', ' rise 2', '}', ''] ts = keepalived.KeepalivedTrackScript( VRRP_INTERVAL, '/etc/ha_confs/qrouter-x', VRRP_ID) self.assertEqual(exp_conf, ts.build_config_preamble()) def test_get_config_str(self): ts = keepalived.KeepalivedTrackScript( VRRP_INTERVAL, '/etc/ha_confs/qrouter-x', VRRP_ID) ts.routes = [ keepalived.KeepalivedVirtualRoute('12.0.0.0/24', '10.0.0.1'), ] self.assertEqual(''' track_script { ha_health_check_1 }''', ts.get_config_str()) def test_get_script_str(self): ts = keepalived.KeepalivedTrackScript( VRRP_INTERVAL, '/etc/ha_confs/qrouter-x', VRRP_ID) ts.routes = [ keepalived.KeepalivedVirtualRoute('12.0.0.0/24', '10.0.0.1'), ] ts.vips = [ keepalived.KeepalivedVipAddress('192.168.0.3/18', 'ha-xxx'), ] self.assertEqual("""#!/bin/bash -eu ip a | grep 192.168.0.3 || exit 0 ping -c 1 -w 1 10.0.0.1 1>/dev/null || exit 1""", ts._get_script_str()) def test_get_script_str_no_routes(self): ts = keepalived.KeepalivedTrackScript( VRRP_INTERVAL, '/etc/ha_confs/qrouter-x', VRRP_ID) self.assertEqual('#!/bin/bash -eu\n', ts._get_script_str()) def test_write_check_script(self): conf_dir = '/etc/ha_confs/qrouter-x' ts = keepalived.KeepalivedTrackScript(VRRP_INTERVAL, conf_dir, VRRP_ID) ts.routes = [ keepalived.KeepalivedVirtualRoute('12.0.0.0/24', '10.0.0.1'), keepalived.KeepalivedVirtualRoute('2001:db8::1', '2001:db8::1'), ] with mock.patch.object(keepalived, 'file_utils') as patched_utils: ts.write_check_script() patched_utils.replace_file.assert_called_with( os.path.join(conf_dir, 'ha_check_script_1.sh'), """#!/bin/bash -eu ping -c 1 -w 1 10.0.0.1 1>/dev/null || exit 1 ping6 -c 1 -w 1 2001:db8::1 1>/dev/null || exit 1""", 0o520 ) def test_write_check_script_no_routes(self): conf_dir = '/etc/ha_confs/qrouter-x' ts = keepalived.KeepalivedTrackScript( VRRP_INTERVAL, conf_dir, VRRP_ID) with mock.patch.object(keepalived, 'file_utils') as patched_utils: ts.write_check_script() patched_utils.replace_file.assert_not_called() neutron-12.1.1/neutron/tests/unit/agent/linux/test_xenapi_root_helper.py0000664000175000017500000000664513553660046026702 0ustar zuulzuul00000000000000# Copyright 2016 Citrix System. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg from oslo_rootwrap import cmd as oslo_rootwrap_cmd from neutron.agent.linux import xenapi_root_helper as helper from neutron.conf.agent import xenapi_conf from neutron.tests import base class TestXenapiRootHelper(base.BaseTestCase): def _get_fake_xenapi_client(self): class FakeXenapiClient(helper.XenAPIClient): def __init__(self): self._session = mock.MagicMock() return FakeXenapiClient() def setUp(self): super(TestXenapiRootHelper, self).setUp() conf = cfg.CONF xenapi_conf.register_xenapi_opts(conf) def test_get_return_code_unauthourized(self): failure_details = [helper.XENAPI_PLUGIN_FAILURE_ID, 'run_command', 'PluginError', helper.MSG_UNAUTHORIZED] xenapi_client = self._get_fake_xenapi_client() rc = xenapi_client._get_return_code(failure_details) self.assertEqual(oslo_rootwrap_cmd.RC_UNAUTHORIZED, rc) def test_get_return_code_noexecfound(self): failure_details = [helper.XENAPI_PLUGIN_FAILURE_ID, 'run_command', 'PluginError', helper.MSG_NOT_FOUND] xenapi_client = self._get_fake_xenapi_client() rc = xenapi_client._get_return_code(failure_details) self.assertEqual(oslo_rootwrap_cmd.RC_NOEXECFOUND, rc) def test_get_return_code_unknown_error(self): failure_details = [helper.XENAPI_PLUGIN_FAILURE_ID, 'run_command', 'PluginError', 'Any unknown error'] xenapi_client = self._get_fake_xenapi_client() rc = xenapi_client._get_return_code(failure_details) self.assertEqual(helper.RC_UNKNOWN_XENAPI_ERROR, rc) def test_execute(self): cmd = ["ovs-vsctl", "list-ports", "xapi2"] expect_cmd_args = {'cmd': '["ovs-vsctl", "list-ports", "xapi2"]', 'cmd_input': 'null'} raw_result = '{"returncode": 0, "err": "", "out": "vif158.2"}' with mock.patch.object(helper.XenAPIClient, "_call_plugin", return_value=raw_result) as mock_call_plugin: xenapi_client = self._get_fake_xenapi_client() rc, out, err = xenapi_client.execute(cmd) mock_call_plugin.assert_called_once_with( 'netwrap.py', 'run_command', expect_cmd_args) self.assertEqual(0, rc) self.assertEqual("vif158.2", out) self.assertEqual("", err) def test_execute_nocommand(self): cmd = [] xenapi_client = self._get_fake_xenapi_client() rc, out, err = xenapi_client.execute(cmd) self.assertEqual(oslo_rootwrap_cmd.RC_NOCOMMAND, rc) neutron-12.1.1/neutron/tests/unit/agent/linux/test_polling.py0000664000175000017500000000540213553660047024447 0ustar zuulzuul00000000000000# Copyright 2013 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.agent.common import base_polling from neutron.agent.linux import polling from neutron.agent.ovsdb.native import helpers from neutron.tests import base class TestGetPollingManager(base.BaseTestCase): def setUp(self): super(TestGetPollingManager, self).setUp() mock.patch.object(helpers, 'enable_connection_uri').start() def test_return_always_poll_by_default(self): with polling.get_polling_manager() as pm: self.assertEqual(pm.__class__, base_polling.AlwaysPoll) def test_manage_polling_minimizer(self): mock_target = 'neutron.agent.linux.polling.InterfacePollingMinimizer' with mock.patch('%s.start' % mock_target) as mock_start: with mock.patch('%s.stop' % mock_target) as mock_stop: with polling.get_polling_manager(minimize_polling=True) as pm: self.assertEqual(pm.__class__, polling.InterfacePollingMinimizer) mock_stop.assert_has_calls([mock.call()]) mock_start.assert_has_calls([mock.call()]) class TestInterfacePollingMinimizer(base.BaseTestCase): def setUp(self): super(TestInterfacePollingMinimizer, self).setUp() mock.patch.object(helpers, 'enable_connection_uri').start() self.pm = polling.InterfacePollingMinimizer() def test_start_calls_monitor_start(self): with mock.patch.object(self.pm._monitor, 'start') as mock_start: self.pm.start() mock_start.assert_called_with(block=True) def test_stop_calls_monitor_stop(self): with mock.patch.object(self.pm._monitor, 'stop') as mock_stop: self.pm.stop() mock_stop.assert_called_with() def mock_has_updates(self, return_value): target = ('neutron.agent.linux.ovsdb_monitor.SimpleInterfaceMonitor' '.has_updates') return mock.patch( target, new_callable=mock.PropertyMock(return_value=return_value), ) def test__is_polling_required_returns_when_updates_are_present(self): with self.mock_has_updates(True): self.assertTrue(self.pm._is_polling_required()) neutron-12.1.1/neutron/tests/unit/agent/linux/test_l3_tc_lib.py0000664000175000017500000004345613553660047024650 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants from neutron.agent.linux import l3_tc_lib as tc_lib from neutron.common import exceptions from neutron.tests import base FLOATING_IP_DEVICE_NAME = "qg-device_rfp" FLOATING_IP_ROUTER_NAMESPACE = "qrouter-namespace_snat-namespace" FLOATING_IP_1 = "172.16.5.146" FLOATING_IP_2 = "172.16.10.105" FILETER_ID_1 = "800::800" FILETER_ID_2 = "800::801" TC_INGRESS_FILTERS = ( 'filter protocol ip u32 \n' 'filter protocol ip u32 fh 800: ht divisor 1 \n' 'filter protocol ip u32 fh %(filter_id1)s order 2048 key ' 'ht 800 bkt 0 ' 'flowid :1 (rule hit 0 success 0)\n' ' match IP dst %(fip1)s/32 (success 0 ) \n' ' police 0x3 rate 3000Kbit burst 3Mb mtu 64Kb action drop overhead 0b \n' 'ref 1 bind 1\n' '\n' ' Sent 111 bytes 222 pkts (dropped 0, overlimits 0) \n' 'filter protocol ip u32 fh %(filter_id2)s order 2049 key ' 'ht 800 bkt 0 ' 'flowid :1 (rule hit 0 success 0)\n' ' match IP dst %(fip2)s/32 (success 0 ) \n' ' police 0x1b rate 22000Kbit burst 22Mb mtu 64Kb action drop ' 'overhead 0b \n' 'ref 1 bind 1\n' '\n' ' Sent 111 bytes 222 pkts (dropped 0, overlimits 0)\n') % { "filter_id1": FILETER_ID_1, "fip1": FLOATING_IP_1, "filter_id2": FILETER_ID_2, "fip2": FLOATING_IP_2} TC_INGRESS_FILTERS_DUP = TC_INGRESS_FILTERS + ( 'filter protocol ip u32 fh %(filter_id2)s order 2049 key ' 'ht 800 bkt 0 ' 'flowid :1 (rule hit 0 success 0)\n' ' match IP dst %(fip2)s/32 (success 0 ) \n' ' police 0x1b rate 22000Kbit burst 22Mb mtu 64Kb action drop ' 'overhead 0b \n' 'ref 1 bind 1\n' '\n' ' Sent 111 bytes 222 pkts (dropped 0, overlimits 0)\n') % { "filter_id2": FILETER_ID_2, "fip2": FLOATING_IP_2} TC_EGRESS_FILTERS = ( 'filter protocol ip u32 \n' 'filter protocol ip u32 fh 800: ht divisor 1 \n' 'filter protocol ip u32 fh %(filter_id1)s order 2048 key ' 'ht 800 bkt 0 ' 'flowid :1 (rule hit 0 success 0)\n' ' match IP src %(fip1)s/32 (success 0 ) \n' ' police 0x4 rate 3000Kbit burst 3Mb mtu 64Kb action drop overhead 0b \n' 'ref 1 bind 1\n' '\n' ' Sent 111 bytes 222 pkts (dropped 0, overlimits 0) \n' 'filter protocol ip u32 fh %(filter_id2)s order 2049 key ' 'ht 800 bkt 0 ' 'flowid :1 (rule hit 0 success 0)\n' ' match IP src %(fip2)s/32 (success 0 ) \n' ' police 0x1c rate 22000Kbit burst 22Mb mtu 64Kb action drop ' 'overhead 0b \n' 'ref 1 bind 1\n' '\n' ' Sent 111 bytes 222 pkts (dropped 0, overlimits 0)\n') % { "filter_id1": FILETER_ID_1, "fip1": FLOATING_IP_1, "filter_id2": FILETER_ID_2, "fip2": FLOATING_IP_2} FILTERS_IDS = {constants.INGRESS_DIRECTION: TC_INGRESS_FILTERS, constants.EGRESS_DIRECTION: TC_EGRESS_FILTERS} INGRESS_QSIC_ID = "ffff:" EGRESS_QDISC_ID = "1:" QDISC_IDS = {constants.INGRESS_DIRECTION: INGRESS_QSIC_ID, constants.EGRESS_DIRECTION: EGRESS_QDISC_ID} TC_QDISCS = ( 'qdisc htb %(egress)s root refcnt 2 r2q 10 default 0 ' 'direct_packets_stat 6\n' 'qdisc ingress %(ingress)s parent ffff:fff1 ----------------\n') % { "egress": EGRESS_QDISC_ID, "ingress": INGRESS_QSIC_ID} class TestFloatingIPTcCommandBase(base.BaseTestCase): def setUp(self): super(TestFloatingIPTcCommandBase, self).setUp() self.tc = tc_lib.FloatingIPTcCommandBase( FLOATING_IP_DEVICE_NAME, namespace=FLOATING_IP_ROUTER_NAMESPACE) self.execute = mock.patch('neutron.agent.common.utils.execute').start() def test__get_qdiscs(self): self.tc._get_qdiscs() self.execute.assert_called_once_with( ['ip', 'netns', 'exec', FLOATING_IP_ROUTER_NAMESPACE, 'tc', 'qdisc', 'show', 'dev', FLOATING_IP_DEVICE_NAME], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None ) def test__get_qdisc_id_for_filter(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_qdiscs') as get_qdiscs: get_qdiscs.return_value = TC_QDISCS q1 = self.tc._get_qdisc_id_for_filter(constants.INGRESS_DIRECTION) self.assertEqual(INGRESS_QSIC_ID, q1) q2 = self.tc._get_qdisc_id_for_filter(constants.EGRESS_DIRECTION) self.assertEqual(EGRESS_QDISC_ID, q2) def test__add_qdisc(self): self.tc._add_qdisc(constants.INGRESS_DIRECTION) self.execute.assert_called_with( ['ip', 'netns', 'exec', FLOATING_IP_ROUTER_NAMESPACE, 'tc', 'qdisc', 'add', 'dev', FLOATING_IP_DEVICE_NAME, 'ingress'], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None ) self.tc._add_qdisc(constants.EGRESS_DIRECTION) self.execute.assert_called_with( ['ip', 'netns', 'exec', FLOATING_IP_ROUTER_NAMESPACE, 'tc', 'qdisc', 'add', 'dev', FLOATING_IP_DEVICE_NAME] + ['root', 'handle', '1:', 'htb'], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None ) def test__get_filters(self): self.tc._get_filters(INGRESS_QSIC_ID) self.execute.assert_called_with( ['ip', 'netns', 'exec', FLOATING_IP_ROUTER_NAMESPACE, 'tc', '-p', '-s', '-d', 'filter', 'show', 'dev', FLOATING_IP_DEVICE_NAME, 'parent', INGRESS_QSIC_ID, 'prio', 1], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None ) def test__get_filterid_for_ip(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_filters') as get_filters: get_filters.return_value = TC_EGRESS_FILTERS f_id = self.tc._get_filterid_for_ip(INGRESS_QSIC_ID, FLOATING_IP_1) self.assertEqual(FILETER_ID_1, f_id) def test__get_filterid_for_ip_no_output(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_filters') as get_filters: get_filters.return_value = "" self.assertRaises(exceptions.FilterIDForIPNotFound, self.tc._get_filterid_for_ip, INGRESS_QSIC_ID, FLOATING_IP_1) def test__get_filterid_for_ip_duplicated(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_filters') as get_filters: get_filters.return_value = TC_INGRESS_FILTERS_DUP self.assertRaises(exceptions.MultipleFilterIDForIPFound, self.tc._get_filterid_for_ip, INGRESS_QSIC_ID, FLOATING_IP_2) def test__get_filterid_for_ip_not_found(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_filters') as get_filters: get_filters.return_value = TC_EGRESS_FILTERS self.assertRaises(exceptions.FilterIDForIPNotFound, self.tc._get_filterid_for_ip, INGRESS_QSIC_ID, "1.1.1.1") def test__del_filter_by_id(self): self.tc._del_filter_by_id(INGRESS_QSIC_ID, FLOATING_IP_1) self.execute.assert_called_once_with( ['ip', 'netns', 'exec', FLOATING_IP_ROUTER_NAMESPACE, 'tc', 'filter', 'del', 'dev', FLOATING_IP_DEVICE_NAME, 'parent', INGRESS_QSIC_ID, 'prio', 1, 'handle', FLOATING_IP_1, 'u32'], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None ) def test__get_qdisc_filters(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_filters') as get_filters: get_filters.return_value = TC_EGRESS_FILTERS f_ids = self.tc._get_qdisc_filters(INGRESS_QSIC_ID) self.assertEqual([FILETER_ID_1, FILETER_ID_2], f_ids) def test__get_qdisc_filters_no_output(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_filters') as get_filters: get_filters.return_value = "" f_ids = self.tc._get_qdisc_filters(INGRESS_QSIC_ID) self.assertEqual(0, len(f_ids)) def test__add_filter(self): protocol = ['protocol', 'ip'] prio = ['prio', 1] match = ['u32', 'match', 'ip', 'dst', FLOATING_IP_1] police = ['police', 'rate', '1kbit', 'burst', '1kbit', 'mtu', '64kb', 'drop', 'flowid', ':1'] args = protocol + prio + match + police cmd = ['tc', 'filter', 'add', 'dev', FLOATING_IP_DEVICE_NAME, 'parent', INGRESS_QSIC_ID] + args self.tc._add_filter(INGRESS_QSIC_ID, constants.INGRESS_DIRECTION, FLOATING_IP_1, 1, 1) self.execute.assert_called_once_with( ['ip', 'netns', 'exec', FLOATING_IP_ROUTER_NAMESPACE] + cmd, run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None ) def test__get_or_create_qdisc(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_qdisc_id_for_filter') as get_disc1: get_disc1.return_value = None with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_add_qdisc'): with mock.patch.object( tc_lib.FloatingIPTcCommandBase, '_get_qdisc_id_for_filter') as get_disc2: get_disc2.return_value = INGRESS_QSIC_ID qdisc_id = self.tc._get_or_create_qdisc( constants.INGRESS_DIRECTION) self.assertEqual(INGRESS_QSIC_ID, qdisc_id) def test__get_or_create_qdisc_failed(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_qdisc_id_for_filter') as get_disc1: get_disc1.return_value = None with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_add_qdisc'): with mock.patch.object( tc_lib.FloatingIPTcCommandBase, '_get_qdisc_id_for_filter') as get_disc2: get_disc2.return_value = None self.assertRaises(exceptions.FailedToAddQdiscToDevice, self.tc._get_or_create_qdisc, constants.INGRESS_DIRECTION) class TestFloatingIPTcCommand(base.BaseTestCase): def setUp(self): super(TestFloatingIPTcCommand, self).setUp() self.tc = tc_lib.FloatingIPTcCommand( FLOATING_IP_DEVICE_NAME, namespace=FLOATING_IP_ROUTER_NAMESPACE) self.execute = mock.patch('neutron.agent.common.utils.execute').start() def test_clear_all_filters(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_qdisc_id_for_filter') as get_disc: get_disc.return_value = EGRESS_QDISC_ID with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_filters') as get_filters: get_filters.return_value = TC_EGRESS_FILTERS self.tc.clear_all_filters(constants.EGRESS_DIRECTION) self.assertEqual(2, self.execute.call_count) def test_set_ip_rate_limit_filter_existed(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_qdisc_id_for_filter') as get_disc: get_disc.return_value = EGRESS_QDISC_ID with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_filterid_for_ip') as get_filter: get_filter.return_value = FILETER_ID_1 with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_del_filter_by_id') as del_filter: with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_add_filter') as add_filter: ip = "111.111.111.111" self.tc.set_ip_rate_limit(constants.EGRESS_DIRECTION, ip, 1, 1) del_filter.assert_called_once_with( EGRESS_QDISC_ID, FILETER_ID_1) add_filter.assert_called_once_with( EGRESS_QDISC_ID, constants.EGRESS_DIRECTION, ip, 1, 1) def test_set_ip_rate_limit_no_qdisc(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_qdisc_id_for_filter') as get_disc: get_disc.return_value = None with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_add_qdisc'): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_filters') as get_filters: get_filters.return_value = TC_INGRESS_FILTERS get_disc.return_value = INGRESS_QSIC_ID ip = "111.111.111.111" self.tc.set_ip_rate_limit(constants.INGRESS_DIRECTION, ip, 1, 1) protocol = ['protocol', 'ip'] prio = ['prio', 1] _match = 'dst' match = ['u32', 'match', 'ip', _match, ip] police = ['police', 'rate', '1kbit', 'burst', '1kbit', 'mtu', '64kb', 'drop', 'flowid', ':1'] args = protocol + prio + match + police self.execute.assert_called_once_with( ['ip', 'netns', 'exec', FLOATING_IP_ROUTER_NAMESPACE, 'tc', 'filter', 'add', 'dev', FLOATING_IP_DEVICE_NAME, 'parent', INGRESS_QSIC_ID] + args, run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None ) def test_clear_ip_rate_limit(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_qdisc_id_for_filter') as get_disc: get_disc.return_value = EGRESS_QDISC_ID with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_filterid_for_ip') as get_filter_id: get_filter_id.return_value = FILETER_ID_1 self.tc.clear_ip_rate_limit(constants.EGRESS_DIRECTION, FLOATING_IP_1) self.execute.assert_called_once_with( ['ip', 'netns', 'exec', FLOATING_IP_ROUTER_NAMESPACE, 'tc', 'filter', 'del', 'dev', FLOATING_IP_DEVICE_NAME, 'parent', EGRESS_QDISC_ID, 'prio', 1, 'handle', FILETER_ID_1, 'u32'], run_as_root=True, check_exit_code=True, log_fail_as_error=True, extra_ok_codes=None ) def test_get_filter_id_for_ip(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_qdisc_id_for_filter') as get_disc: get_disc.return_value = EGRESS_QDISC_ID with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_filterid_for_ip') as get_filter_id: self.tc.get_filter_id_for_ip(constants.EGRESS_DIRECTION, '8.8.8.8') get_filter_id.assert_called_once_with(EGRESS_QDISC_ID, '8.8.8.8') def test_get_existing_filter_ids(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_qdisc_id_for_filter') as get_disc: get_disc.return_value = EGRESS_QDISC_ID with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_qdisc_filters') as get_filter_ids: self.tc.get_existing_filter_ids(constants.EGRESS_DIRECTION) get_filter_ids.assert_called_once_with(EGRESS_QDISC_ID) def test_delete_filter_ids(self): with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_get_qdisc_id_for_filter') as get_disc: get_disc.return_value = EGRESS_QDISC_ID with mock.patch.object(tc_lib.FloatingIPTcCommandBase, '_del_filter_by_id') as del_filter_id: self.tc.delete_filter_ids(constants.EGRESS_DIRECTION, [FILETER_ID_1, FILETER_ID_2]) del_filter_id.assert_has_calls( [mock.call(EGRESS_QDISC_ID, FILETER_ID_1), mock.call(EGRESS_QDISC_ID, FILETER_ID_2)]) neutron-12.1.1/neutron/tests/unit/agent/test_agent_extensions_manager.py0000664000175000017500000000313213553660046026710 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg from neutron.agent import agent_extensions_manager as ext_manager from neutron.conf.agent import agent_extensions_manager as ext_manager_config from neutron.tests import base class TestAgentExtensionsManager(base.BaseTestCase): def setUp(self): super(TestAgentExtensionsManager, self).setUp() mock.patch('neutron.agent.l2.extensions.qos.QosAgentExtension', autospec=True).start() conf = cfg.CONF ext_manager_config.register_agent_ext_manager_opts() cfg.CONF.set_override('extensions', ['qos'], 'agent') namespace = 'neutron.agent.l2.extensions' self.manager = ext_manager.AgentExtensionsManager(conf, namespace) def _get_extension(self): return self.manager.extensions[0].obj def test_initialize(self): connection = object() self.manager.initialize(connection, 'fake_driver_type') ext = self._get_extension() ext.initialize.assert_called_once_with(connection, 'fake_driver_type') neutron-12.1.1/neutron/tests/unit/agent/metadata/0000775000175000017500000000000013553660156022013 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/metadata/test_agent.py0000664000175000017500000005004413553660047024524 0ustar zuulzuul00000000000000# Copyright 2012 New Dream Network, LLC (DreamHost) # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as n_const import testtools import webob from oslo_config import cfg from oslo_config import fixture as config_fixture from oslo_utils import fileutils from neutron.agent.linux import utils as agent_utils from neutron.agent.metadata import agent from neutron.agent import metadata_agent from neutron.common import cache_utils as cache from neutron.common import utils from neutron.conf.agent.metadata import config as meta_conf from neutron.tests import base class ConfFixture(config_fixture.Config): def setUp(self): super(ConfFixture, self).setUp() meta_conf.register_meta_conf_opts( meta_conf.METADATA_PROXY_HANDLER_OPTS, self.conf) self.config(auth_ca_cert=None, nova_metadata_host='9.9.9.9', nova_metadata_port=8775, metadata_proxy_shared_secret='secret', nova_metadata_protocol='http', nova_metadata_insecure=True, nova_client_cert='nova_cert', nova_client_priv_key='nova_priv_key') cache.register_oslo_configs(self.conf) class NewCacheConfFixture(ConfFixture): def setUp(self): super(NewCacheConfFixture, self).setUp() self.config( group='cache', enabled=True, backend='oslo_cache.dict', expiration_time=5) class TestMetadataProxyHandlerBase(base.BaseTestCase): fake_conf = cfg.CONF fake_conf_fixture = ConfFixture(fake_conf) def setUp(self): super(TestMetadataProxyHandlerBase, self).setUp() self.useFixture(self.fake_conf_fixture) self.log_p = mock.patch.object(agent, 'LOG') self.log = self.log_p.start() self.handler = agent.MetadataProxyHandler(self.fake_conf) self.handler.plugin_rpc = mock.Mock() self.handler.context = mock.Mock() class TestMetadataProxyHandlerRpc(TestMetadataProxyHandlerBase): def test_get_port_filters(self): router_id = 'test_router_id' ip = '1.2.3.4' networks = ('net_id1', 'net_id2') expected = {'device_id': [router_id], 'device_owner': n_const.ROUTER_INTERFACE_OWNERS, 'network_id': networks, 'fixed_ips': {'ip_address': [ip]}} actual = self.handler._get_port_filters(router_id, ip, networks) self.assertEqual(expected, actual) def test_get_router_networks(self): router_id = 'router-id' expected = ('network_id1', 'network_id2') ports = [{'network_id': 'network_id1', 'something': 42}, {'network_id': 'network_id2', 'something_else': 32}] self.handler.plugin_rpc.get_ports.return_value = ports networks = self.handler._get_router_networks(router_id) self.assertEqual(expected, networks) def test_get_ports_for_remote_address(self): ip = '1.1.1.1' networks = ('network_id1', 'network_id2') expected = [{'port_id': 'port_id1'}, {'port_id': 'port_id2'}] self.handler.plugin_rpc.get_ports.return_value = expected ports = self.handler._get_ports_for_remote_address(ip, networks) self.assertEqual(expected, ports) class _TestMetadataProxyHandlerCacheMixin(object): def test_call(self): req = mock.Mock() with mock.patch.object(self.handler, '_get_instance_and_tenant_id') as get_ids: get_ids.return_value = ('instance_id', 'tenant_id') with mock.patch.object(self.handler, '_proxy_request') as proxy: proxy.return_value = 'value' retval = self.handler(req) self.assertEqual(retval, 'value') def test_call_no_instance_match(self): req = mock.Mock() with mock.patch.object(self.handler, '_get_instance_and_tenant_id') as get_ids: get_ids.return_value = None, None retval = self.handler(req) self.assertIsInstance(retval, webob.exc.HTTPNotFound) def test_call_internal_server_error(self): req = mock.Mock() with mock.patch.object(self.handler, '_get_instance_and_tenant_id') as get_ids: get_ids.side_effect = Exception retval = self.handler(req) self.assertIsInstance(retval, webob.exc.HTTPInternalServerError) self.assertEqual(len(self.log.mock_calls), 2) def test_get_router_networks(self): router_id = 'router-id' expected = ('network_id1', 'network_id2') ports = [{'network_id': 'network_id1', 'something': 42}, {'network_id': 'network_id2', 'something_else': 32}] mock_get_ports = self.handler.plugin_rpc.get_ports mock_get_ports.return_value = ports networks = self.handler._get_router_networks(router_id) mock_get_ports.assert_called_once_with( mock.ANY, {'device_id': [router_id], 'device_owner': n_const.ROUTER_INTERFACE_OWNERS}) self.assertEqual(expected, networks) def _test_get_router_networks_twice_helper(self): router_id = 'router-id' ports = [{'network_id': 'network_id1', 'something': 42}] expected_networks = ('network_id1',) with mock.patch( 'oslo_utils.timeutils.utcnow_ts', return_value=0): mock_get_ports = self.handler.plugin_rpc.get_ports mock_get_ports.return_value = ports networks = self.handler._get_router_networks(router_id) mock_get_ports.assert_called_once_with( mock.ANY, {'device_id': [router_id], 'device_owner': n_const.ROUTER_INTERFACE_OWNERS}) self.assertEqual(expected_networks, networks) networks = self.handler._get_router_networks(router_id) def test_get_router_networks_twice(self): self._test_get_router_networks_twice_helper() self.assertEqual( 1, self.handler.plugin_rpc.get_ports.call_count) def _get_ports_for_remote_address_cache_hit_helper(self): remote_address = 'remote_address' networks = ('net1', 'net2') mock_get_ports = self.handler.plugin_rpc.get_ports mock_get_ports.return_value = [{'network_id': 'net1', 'something': 42}] self.handler._get_ports_for_remote_address(remote_address, networks) mock_get_ports.assert_called_once_with( mock.ANY, {'network_id': networks, 'fixed_ips': {'ip_address': [remote_address]}} ) self.assertEqual(1, mock_get_ports.call_count) self.handler._get_ports_for_remote_address(remote_address, networks) def test_get_ports_for_remote_address_cache_hit(self): self._get_ports_for_remote_address_cache_hit_helper() self.assertEqual( 1, self.handler.plugin_rpc.get_ports.call_count) def test_get_ports_network_id(self): network_id = 'network-id' router_id = 'router-id' remote_address = 'remote-address' expected = ['port1'] networks = (network_id,) with mock.patch.object(self.handler, '_get_ports_for_remote_address' ) as mock_get_ip_addr,\ mock.patch.object(self.handler, '_get_router_networks' ) as mock_get_router_networks: mock_get_ip_addr.return_value = expected ports = self.handler._get_ports(remote_address, network_id, router_id) mock_get_ip_addr.assert_called_once_with(remote_address, networks) self.assertFalse(mock_get_router_networks.called) self.assertEqual(expected, ports) def test_get_ports_router_id(self): router_id = 'router-id' remote_address = 'remote-address' expected = ['port1'] networks = ('network1', 'network2') with mock.patch.object(self.handler, '_get_ports_for_remote_address', return_value=expected ) as mock_get_ip_addr,\ mock.patch.object(self.handler, '_get_router_networks', return_value=networks ) as mock_get_router_networks: ports = self.handler._get_ports(remote_address, router_id=router_id) mock_get_router_networks.assert_called_once_with(router_id) mock_get_ip_addr.assert_called_once_with(remote_address, networks) self.assertEqual(expected, ports) def test_get_ports_no_id(self): self.assertRaises(TypeError, self.handler._get_ports, 'remote_address') def _get_instance_and_tenant_id_helper(self, headers, list_ports_retval, networks=None, router_id=None): remote_address = '192.168.1.1' headers['X-Forwarded-For'] = remote_address req = mock.Mock(headers=headers) def mock_get_ports(*args, **kwargs): return list_ports_retval.pop(0) self.handler.plugin_rpc.get_ports.side_effect = mock_get_ports instance_id, tenant_id = self.handler._get_instance_and_tenant_id(req) expected = [] if router_id: expected.append( mock.call( mock.ANY, {'device_id': [router_id], 'device_owner': n_const.ROUTER_INTERFACE_OWNERS} ) ) expected.append( mock.call( mock.ANY, {'network_id': networks, 'fixed_ips': {'ip_address': ['192.168.1.1']}} ) ) self.handler.plugin_rpc.get_ports.assert_has_calls(expected) return (instance_id, tenant_id) def test_get_instance_id_router_id(self): router_id = 'the_id' headers = { 'X-Neutron-Router-ID': router_id } networks = ('net1', 'net2') ports = [ [{'network_id': 'net1'}, {'network_id': 'net2'}], [{'device_id': 'device_id', 'tenant_id': 'tenant_id', 'network_id': 'net1'}] ] self.assertEqual( self._get_instance_and_tenant_id_helper(headers, ports, networks=networks, router_id=router_id), ('device_id', 'tenant_id') ) def test_get_instance_id_router_id_no_match(self): router_id = 'the_id' headers = { 'X-Neutron-Router-ID': router_id } networks = ('net1', 'net2') ports = [ [{'network_id': 'net1'}, {'network_id': 'net2'}], [] ] self.assertEqual( self._get_instance_and_tenant_id_helper(headers, ports, networks=networks, router_id=router_id), (None, None) ) def test_get_instance_id_network_id(self): network_id = 'the_id' headers = { 'X-Neutron-Network-ID': network_id } ports = [ [{'device_id': 'device_id', 'tenant_id': 'tenant_id', 'network_id': 'the_id'}] ] self.assertEqual( self._get_instance_and_tenant_id_helper(headers, ports, networks=('the_id',)), ('device_id', 'tenant_id') ) def test_get_instance_id_network_id_no_match(self): network_id = 'the_id' headers = { 'X-Neutron-Network-ID': network_id } ports = [[]] self.assertEqual( self._get_instance_and_tenant_id_helper(headers, ports, networks=('the_id',)), (None, None) ) def _proxy_request_test_helper(self, response_code=200, method='GET'): hdrs = {'X-Forwarded-For': '8.8.8.8'} body = 'body' req = mock.Mock(path_info='/the_path', query_string='', headers=hdrs, method=method, body=body) resp = mock.MagicMock(status_code=response_code) resp.status.__str__.side_effect = AttributeError resp.content = 'content' req.response = resp with mock.patch.object(self.handler, '_sign_instance_id') as sign: sign.return_value = 'signed' with mock.patch('requests.request') as mock_request: resp.headers = {'content-type': 'text/plain'} mock_request.return_value = resp retval = self.handler._proxy_request('the_id', 'tenant_id', req) mock_request.assert_called_once_with( method=method, url='http://9.9.9.9:8775/the_path', headers={ 'X-Forwarded-For': '8.8.8.8', 'X-Instance-ID-Signature': 'signed', 'X-Instance-ID': 'the_id', 'X-Tenant-ID': 'tenant_id' }, data=body, cert=(self.fake_conf.nova_client_cert, self.fake_conf.nova_client_priv_key), verify=False) return retval def test_proxy_request_post(self): response = self._proxy_request_test_helper(method='POST') self.assertEqual(response.content_type, "text/plain") self.assertEqual(response.body, 'content') def test_proxy_request_200(self): response = self._proxy_request_test_helper(200) self.assertEqual(response.content_type, "text/plain") self.assertEqual(response.body, 'content') def test_proxy_request_400(self): self.assertIsInstance(self._proxy_request_test_helper(400), webob.exc.HTTPBadRequest) def test_proxy_request_403(self): self.assertIsInstance(self._proxy_request_test_helper(403), webob.exc.HTTPForbidden) def test_proxy_request_404(self): self.assertIsInstance(self._proxy_request_test_helper(404), webob.exc.HTTPNotFound) def test_proxy_request_409(self): self.assertIsInstance(self._proxy_request_test_helper(409), webob.exc.HTTPConflict) def test_proxy_request_500(self): self.assertIsInstance(self._proxy_request_test_helper(500), webob.exc.HTTPInternalServerError) def test_proxy_request_other_code(self): with testtools.ExpectedException(Exception): self._proxy_request_test_helper(302) def test_sign_instance_id(self): self.assertEqual( self.handler._sign_instance_id('foo'), '773ba44693c7553d6ee20f61ea5d2757a9a4f4a44d2841ae4e95b52e4cd62db4' ) class TestMetadataProxyHandlerNewCache(TestMetadataProxyHandlerBase, _TestMetadataProxyHandlerCacheMixin): fake_conf = cfg.CONF fake_conf_fixture = NewCacheConfFixture(fake_conf) class TestUnixDomainMetadataProxy(base.BaseTestCase): def setUp(self): super(TestUnixDomainMetadataProxy, self).setUp() self.cfg_p = mock.patch.object(agent, 'cfg') self.cfg = self.cfg_p.start() looping_call_p = mock.patch( 'oslo_service.loopingcall.FixedIntervalLoopingCall') self.looping_mock = looping_call_p.start() self.cfg.CONF.metadata_proxy_socket = '/the/path' self.cfg.CONF.metadata_workers = 0 self.cfg.CONF.metadata_backlog = 128 self.cfg.CONF.metadata_proxy_socket_mode = meta_conf.USER_MODE @mock.patch.object(fileutils, 'ensure_tree') def test_init_doesnot_exists(self, ensure_dir): agent.UnixDomainMetadataProxy(mock.Mock()) ensure_dir.assert_called_once_with('/the', mode=0o755) def test_init_exists(self): with mock.patch('os.path.isdir') as isdir: with mock.patch('os.unlink') as unlink: isdir.return_value = True agent.UnixDomainMetadataProxy(mock.Mock()) unlink.assert_called_once_with('/the/path') def test_init_exists_unlink_no_file(self): with mock.patch('os.path.isdir') as isdir: with mock.patch('os.unlink') as unlink: with mock.patch('os.path.exists') as exists: isdir.return_value = True exists.return_value = False unlink.side_effect = OSError agent.UnixDomainMetadataProxy(mock.Mock()) unlink.assert_called_once_with('/the/path') def test_init_exists_unlink_fails_file_still_exists(self): with mock.patch('os.path.isdir') as isdir: with mock.patch('os.unlink') as unlink: with mock.patch('os.path.exists') as exists: isdir.return_value = True exists.return_value = True unlink.side_effect = OSError with testtools.ExpectedException(OSError): agent.UnixDomainMetadataProxy(mock.Mock()) unlink.assert_called_once_with('/the/path') @mock.patch.object(agent, 'MetadataProxyHandler') @mock.patch.object(agent_utils, 'UnixDomainWSGIServer') @mock.patch.object(fileutils, 'ensure_tree') def test_run(self, ensure_dir, server, handler): p = agent.UnixDomainMetadataProxy(self.cfg.CONF) p.run() ensure_dir.assert_called_once_with('/the', mode=0o755) server.assert_has_calls([ mock.call('neutron-metadata-agent'), mock.call().start(handler.return_value, '/the/path', workers=0, backlog=128, mode=0o644), mock.call().wait()] ) self.looping_mock.assert_called_once_with(p._report_state) self.looping_mock.return_value.start.assert_called_once_with( interval=mock.ANY) def test_main(self): with mock.patch.object(agent, 'UnixDomainMetadataProxy') as proxy: with mock.patch.object(metadata_agent, 'config') as config: with mock.patch.object(metadata_agent, 'cfg') as cfg: with mock.patch.object(utils, 'cfg'): metadata_agent.main() self.assertTrue(config.setup_logging.called) proxy.assert_has_calls([ mock.call(cfg.CONF), mock.call().run()] ) def test_report_state(self): with mock.patch('neutron.agent.rpc.PluginReportStateAPI') as state_api: with mock.patch('os.makedirs'): proxy = agent.UnixDomainMetadataProxy(self.cfg.CONF) proxy._init_state_reporting() self.assertTrue(proxy.agent_state['start_flag']) proxy._report_state() self.assertNotIn('start_flag', proxy.agent_state) state_api_inst = state_api.return_value state_api_inst.report_state.assert_called_once_with( proxy.context, proxy.agent_state, use_call=True) neutron-12.1.1/neutron/tests/unit/agent/metadata/test_driver.py0000664000175000017500000002267313553660047024730 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import mock from oslo_config import cfg from oslo_utils import uuidutils from neutron.agent.l3 import agent as l3_agent from neutron.agent.l3 import router_info from neutron.agent.linux import iptables_manager from neutron.agent.metadata import driver as metadata_driver from neutron.common import constants from neutron.conf.agent import common as agent_config from neutron.conf.agent.l3 import config as l3_config from neutron.conf.agent.l3 import ha as ha_conf from neutron.conf.agent.metadata import config as meta_conf from neutron.tests import base from neutron.tests import tools from neutron.tests.unit.agent.linux import test_utils _uuid = uuidutils.generate_uuid class TestMetadataDriverRules(base.BaseTestCase): def test_metadata_nat_rules(self): rules = ('PREROUTING', '-d 169.254.169.254/32 -i qr-+ ' '-p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9697') self.assertEqual( [rules], metadata_driver.MetadataDriver.metadata_nat_rules(9697)) def test_metadata_filter_rules(self): rules = [('INPUT', '-m mark --mark 0x1/%s -j ACCEPT' % constants.ROUTER_MARK_MASK), ('INPUT', '-p tcp -m tcp --dport 9697 -j DROP')] self.assertEqual( rules, metadata_driver.MetadataDriver.metadata_filter_rules(9697, '0x1')) class TestMetadataDriverProcess(base.BaseTestCase): EUNAME = 'neutron' EGNAME = 'neutron' METADATA_DEFAULT_IP = '169.254.169.254' METADATA_PORT = 8080 METADATA_SOCKET = '/socket/path' PIDFILE = 'pidfile' def setUp(self): super(TestMetadataDriverProcess, self).setUp() mock.patch('eventlet.spawn').start() agent_config.register_interface_driver_opts_helper(cfg.CONF) cfg.CONF.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') mock.patch('neutron.agent.l3.agent.L3PluginApi').start() mock.patch('neutron.agent.l3.ha.AgentMixin' '._init_ha_conf_path').start() l3_config.register_l3_agent_config_opts(l3_config.OPTS, cfg.CONF) ha_conf.register_l3_agent_ha_opts() meta_conf.register_meta_conf_opts(meta_conf.SHARED_OPTS, cfg.CONF) def test_after_router_updated_called_on_agent_process_update(self): with mock.patch.object(metadata_driver, 'after_router_updated') as f,\ mock.patch.object(router_info.RouterInfo, 'process'): agent = l3_agent.L3NATAgent('localhost') router_id = _uuid() router = {'id': router_id} ri = router_info.RouterInfo(mock.Mock(), router_id, router, agent.conf, mock.ANY) agent.router_info[router_id] = ri agent._process_updated_router(router) f.assert_called_once_with( 'router', 'after_update', agent, router=ri) def test_after_router_updated_should_not_call_add_metadata_rules(self): with mock.patch.object(iptables_manager.IptablesTable, 'add_rule') as f,\ mock.patch.object(iptables_manager.IptablesManager, 'apply'),\ mock.patch.object(metadata_driver.MetadataDriver, 'spawn_monitored_metadata_proxy'),\ mock.patch.object(router_info.RouterInfo, 'process'): agent = l3_agent.L3NATAgent('localhost') router_id = _uuid() router = {'id': router_id} ri = router_info.RouterInfo(mock.Mock(), router_id, router, agent.conf, mock.ANY) agent.router_info[router_id] = ri f.reset_mock() agent._process_updated_router(router) f.assert_not_called() def test_spawn_metadata_proxy(self): router_id = _uuid() router_ns = 'qrouter-%s' % router_id ip_class_path = 'neutron.agent.linux.ip_lib.IPWrapper' cfg.CONF.set_override('metadata_proxy_user', self.EUNAME) cfg.CONF.set_override('metadata_proxy_group', self.EGNAME) cfg.CONF.set_override('metadata_proxy_socket', self.METADATA_SOCKET) cfg.CONF.set_override('debug', True) agent = l3_agent.L3NATAgent('localhost') with mock.patch(ip_class_path) as ip_mock,\ mock.patch( 'neutron.agent.linux.external_process.' 'ProcessManager.get_pid_file_name', return_value=self.PIDFILE),\ mock.patch('pwd.getpwnam', return_value=test_utils.FakeUser(self.EUNAME)),\ mock.patch('grp.getgrnam', return_value=test_utils.FakeGroup(self.EGNAME)),\ mock.patch('os.makedirs'): cfg_file = os.path.join( metadata_driver.HaproxyConfigurator.get_config_path( agent.conf.state_path), "%s.conf" % router_id) mock_open = self.useFixture( tools.OpenFixture(cfg_file)).mock_open agent.metadata_driver.spawn_monitored_metadata_proxy( agent.process_monitor, router_ns, self.METADATA_PORT, agent.conf, bind_address=self.METADATA_DEFAULT_IP, router_id=router_id) netns_execute_args = [ 'haproxy', '-f', cfg_file] log_tag = ("haproxy-" + metadata_driver.METADATA_SERVICE_NAME + "-" + router_id) cfg_contents = metadata_driver._HAPROXY_CONFIG_TEMPLATE % { 'user': self.EUNAME, 'group': self.EGNAME, 'host': self.METADATA_DEFAULT_IP, 'port': self.METADATA_PORT, 'unix_socket_path': self.METADATA_SOCKET, 'res_type': 'Router', 'res_id': router_id, 'pidfile': self.PIDFILE, 'log_level': 'debug', 'log_tag': log_tag} mock_open.assert_has_calls([ mock.call(cfg_file, 'w'), mock.call().write(cfg_contents)], any_order=True) ip_mock.assert_has_calls([ mock.call(namespace=router_ns), mock.call().netns.execute(netns_execute_args, addl_env=None, run_as_root=True) ]) def test_create_config_file_wrong_user(self): with mock.patch('pwd.getpwnam', side_effect=KeyError): config = metadata_driver.HaproxyConfigurator(_uuid(), mock.ANY, mock.ANY, mock.ANY, mock.ANY, self.EUNAME, self.EGNAME, mock.ANY, mock.ANY) self.assertRaises(metadata_driver.InvalidUserOrGroupException, config.create_config_file) def test_create_config_file_wrong_group(self): with mock.patch('grp.getgrnam', side_effect=KeyError),\ mock.patch('pwd.getpwnam', return_value=test_utils.FakeUser(self.EUNAME)): config = metadata_driver.HaproxyConfigurator(_uuid(), mock.ANY, mock.ANY, mock.ANY, mock.ANY, self.EUNAME, self.EGNAME, mock.ANY, mock.ANY) self.assertRaises(metadata_driver.InvalidUserOrGroupException, config.create_config_file) def test__migrate_python_ns_metadata_proxy_if_needed(self): agent = l3_agent.L3NATAgent('localhost') with mock.patch( 'neutron.agent.linux.external_process.ProcessManager')\ as mock_pm: mock_pm.cmdline = ( 'python neutron-ns-metadata-proxy') (agent.metadata_driver ._migrate_python_ns_metadata_proxy_if_needed(mock_pm)) mock_pm.disable.assert_called_once_with() def test__migrate_python_ns_metadata_proxy_if_needed_not_called(self): agent = l3_agent.L3NATAgent('localhost') with mock.patch( 'neutron.agent.linux.external_process.ProcessManager')\ as mock_pm: mock_pm.cmdline = ( 'haproxy -f foo.cfg') (agent.metadata_driver ._migrate_python_ns_metadata_proxy_if_needed(mock_pm)) mock_pm.disable.assert_not_called() neutron-12.1.1/neutron/tests/unit/agent/metadata/__init__.py0000664000175000017500000000000013553660046024110 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/test_rpc.py0000664000175000017500000001627513553660047022442 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import mock from oslo_context import context as oslo_context from neutron.agent import rpc from neutron.tests import base class AgentRPCPluginApi(base.BaseTestCase): def _test_rpc_call(self, method): agent = rpc.PluginApi('fake_topic') ctxt = oslo_context.RequestContext(user='fake_user', tenant='fake_project') expect_val = 'foo' with mock.patch.object(agent.client, 'call') as mock_call,\ mock.patch.object(agent.client, 'prepare') as mock_prepare: mock_prepare.return_value = agent.client mock_call.return_value = expect_val func_obj = getattr(agent, method) if method == 'tunnel_sync': actual_val = func_obj(ctxt, 'fake_tunnel_ip') else: actual_val = func_obj(ctxt, 'fake_device', 'fake_agent_id') self.assertEqual(actual_val, expect_val) def test_get_device_details(self): self._test_rpc_call('get_device_details') def test_get_devices_details_list(self): self._test_rpc_call('get_devices_details_list') def test_update_device_down(self): self._test_rpc_call('update_device_down') def test_tunnel_sync(self): self._test_rpc_call('tunnel_sync') class AgentPluginReportState(base.BaseTestCase): def test_plugin_report_state_use_call(self): topic = 'test' reportStateAPI = rpc.PluginReportStateAPI(topic) expected_agent_state = {'agent': 'test'} with mock.patch.object(reportStateAPI.client, 'call') as mock_call, \ mock.patch.object(reportStateAPI.client, 'cast'), \ mock.patch.object(reportStateAPI.client, 'prepare' ) as mock_prepare: mock_prepare.return_value = reportStateAPI.client ctxt = oslo_context.RequestContext(user='fake_user', tenant='fake_project') reportStateAPI.report_state(ctxt, expected_agent_state, use_call=True) self.assertEqual(mock_call.call_args[0][0], ctxt) self.assertEqual(mock_call.call_args[0][1], 'report_state') self.assertEqual(mock_call.call_args[1]['agent_state'], {'agent_state': expected_agent_state}) self.assertIsInstance(mock_call.call_args[1]['time'], str) def test_plugin_report_state_cast(self): topic = 'test' reportStateAPI = rpc.PluginReportStateAPI(topic) expected_agent_state = {'agent': 'test'} with mock.patch.object(reportStateAPI.client, 'call'), \ mock.patch.object(reportStateAPI.client, 'cast' ) as mock_cast, \ mock.patch.object(reportStateAPI.client, 'prepare' ) as mock_prepare: mock_prepare.return_value = reportStateAPI.client ctxt = oslo_context.RequestContext(user='fake_user', tenant='fake_project') reportStateAPI.report_state(ctxt, expected_agent_state) self.assertEqual(mock_cast.call_args[0][0], ctxt) self.assertEqual(mock_cast.call_args[0][1], 'report_state') self.assertEqual(mock_cast.call_args[1]['agent_state'], {'agent_state': expected_agent_state}) self.assertIsInstance(mock_cast.call_args[1]['time'], str) def test_plugin_report_state_microsecond_is_0(self): topic = 'test' expected_time = datetime.datetime(2015, 7, 27, 15, 33, 30, 0) expected_time_str = '2015-07-27T15:33:30.000000' expected_agent_state = {'agent': 'test'} with mock.patch('neutron.agent.rpc.datetime') as mock_datetime: reportStateAPI = rpc.PluginReportStateAPI(topic) mock_datetime.utcnow.return_value = expected_time with mock.patch.object(reportStateAPI.client, 'call'), \ mock.patch.object(reportStateAPI.client, 'cast' ) as mock_cast, \ mock.patch.object(reportStateAPI.client, 'prepare' ) as mock_prepare: mock_prepare.return_value = reportStateAPI.client ctxt = oslo_context.RequestContext(user='fake_user', tenant='fake_project') reportStateAPI.report_state(ctxt, expected_agent_state) self.assertEqual(expected_time_str, mock_cast.call_args[1]['time']) class AgentRPCMethods(base.BaseTestCase): def _test_create_consumers( self, endpoints, method, expected, topics, listen): call_to_patch = 'neutron.common.rpc.create_connection' with mock.patch(call_to_patch) as create_connection: rpc.create_consumers( endpoints, method, topics, start_listening=listen) create_connection.assert_has_calls(expected) def test_create_consumers_start_listening(self): endpoints = [mock.Mock()] expected = [ mock.call(), mock.call().create_consumer('foo-topic-op', endpoints, fanout=True), mock.call().consume_in_threads() ] method = 'foo' topics = [('topic', 'op')] self._test_create_consumers( endpoints, method, expected, topics, True) def test_create_consumers_do_not_listen(self): endpoints = [mock.Mock()] expected = [ mock.call(), mock.call().create_consumer('foo-topic-op', endpoints, fanout=True), ] method = 'foo' topics = [('topic', 'op')] self._test_create_consumers( endpoints, method, expected, topics, False) def test_create_consumers_with_node_name(self): endpoints = [mock.Mock()] expected = [ mock.call(), mock.call().create_consumer('foo-topic-op', endpoints, fanout=True), mock.call().create_consumer('foo-topic-op.node1', endpoints, fanout=False), mock.call().consume_in_threads() ] call_to_patch = 'neutron.common.rpc.create_connection' with mock.patch(call_to_patch) as create_connection: rpc.create_consumers(endpoints, 'foo', [('topic', 'op', 'node1')]) create_connection.assert_has_calls(expected) neutron-12.1.1/neutron/tests/unit/agent/__init__.py0000664000175000017500000000000013553660046022330 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/ovsdb/0000775000175000017500000000000013553660156021350 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/ovsdb/test_impl_idl.py0000664000175000017500000000301013553660046024542 0ustar zuulzuul00000000000000# Copyright (c) 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import testtools from ovsdbapp import exceptions from neutron.agent.ovsdb import impl_idl from neutron.tests import base class TransactionTestCase(base.BaseTestCase): def test_commit_raises_exception_on_timeout(self): transaction = impl_idl.NeutronOVSDBTransaction(mock.sentinel, mock.Mock(), 1) with self.assert_max_execution_time(10): with testtools.ExpectedException(exceptions.TimeoutException): transaction.commit() def test_post_commit_does_not_raise_exception(self): with mock.patch.object(impl_idl.NeutronOVSDBTransaction, "do_post_commit", side_effect=Exception): transaction = impl_idl.NeutronOVSDBTransaction(mock.sentinel, mock.Mock(), 0) transaction.post_commit(mock.Mock()) neutron-12.1.1/neutron/tests/unit/agent/ovsdb/__init__.py0000664000175000017500000000000013553660046023445 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/ovsdb/native/0000775000175000017500000000000013553660156022636 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/ovsdb/native/__init__.py0000664000175000017500000000000013553660046024733 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/ovsdb/native/test_connection.py0000664000175000017500000001140713553660047026410 0ustar zuulzuul00000000000000# Copyright 2015, Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from ovsdbapp.backend.ovs_idl import connection from ovsdbapp.backend.ovs_idl import idlutils from neutron.agent.ovsdb.native import connection as native_conn from neutron.agent.ovsdb.native import exceptions as ovsdb_exc from neutron.agent.ovsdb.native import helpers from neutron.tests import base SSL_KEY_FILE = '/tmp/dummy.pem' SSL_CERT_FILE = '/tmp/dummy.crt' SSL_CA_FILE = '/tmp/ca.crt' class TestOVSNativeConnection(base.BaseTestCase): @mock.patch.object(connection, 'threading') @mock.patch.object(idlutils, 'wait_for_change') @mock.patch.object(native_conn, 'idl') @mock.patch.object(helpers, 'enable_connection_uri') @mock.patch.object(idlutils, 'get_schema_helper') def test_do_get_schema_helper_retry(self, mock_get_schema_helper, mock_enable_conn, mock_idl, mock_wait_for_change, mock_threading): mock_helper = mock.Mock() # raise until 3rd retry attempt mock_get_schema_helper.side_effect = [Exception(), Exception(), mock_helper] try: conn = connection.Connection(idl_factory=native_conn.idl_factory, timeout=mock.Mock()) except TypeError: conn = connection.Connection(idl=native_conn.idl_factory(), timeout=mock.Mock()) conn.start() self.assertEqual(3, len(mock_get_schema_helper.mock_calls)) mock_helper.register_all.assert_called_once_with() @mock.patch.object(native_conn, 'Stream') @mock.patch.object(connection, 'threading') @mock.patch.object(native_conn, 'idl') @mock.patch.object(idlutils, 'get_schema_helper') @mock.patch.object(native_conn, 'os') @mock.patch.object(native_conn, 'cfg') def test_ssl_connection(self, mock_cfg, mock_os, mock_get_schema_helper, mock_idl, mock_threading, mock_stream): mock_os.path.isfile.return_value = True mock_cfg.CONF.OVS.ovsdb_connection = 'ssl:127.0.0.1:6640' mock_cfg.CONF.OVS.ssl_key_file = SSL_KEY_FILE mock_cfg.CONF.OVS.ssl_cert_file = SSL_CERT_FILE mock_cfg.CONF.OVS.ssl_ca_cert_file = SSL_CA_FILE conn = connection.Connection(idl=native_conn.idl_factory(), timeout=1) conn.start() mock_stream.ssl_set_private_key_file.assert_called_once_with( SSL_KEY_FILE ) mock_stream.ssl_set_certificate_file.assert_called_once_with( SSL_CERT_FILE ) mock_stream.ssl_set_ca_cert_file.assert_called_once_with( SSL_CA_FILE ) @mock.patch.object(native_conn, 'Stream') @mock.patch.object(connection, 'threading') @mock.patch.object(native_conn, 'idl') @mock.patch.object(idlutils, 'get_schema_helper') @mock.patch.object(native_conn, 'cfg') def test_ssl_conn_file_missing(self, mock_cfg, mock_get_schema_helper, mock_idl, mock_threading, mock_stream): mock_cfg.CONF.OVS.ovsdb_connection = 'ssl:127.0.0.1:6640' mock_cfg.CONF.OVS.ssl_key_file = SSL_KEY_FILE mock_cfg.CONF.OVS.ssl_cert_file = SSL_CERT_FILE mock_cfg.CONF.OVS.ssl_ca_cert_file = SSL_CA_FILE self.assertRaises(ovsdb_exc.OvsdbSslConfigNotFound, native_conn.idl_factory) @mock.patch.object(native_conn, 'Stream') @mock.patch.object(connection, 'threading') @mock.patch.object(native_conn, 'idl') @mock.patch.object(idlutils, 'get_schema_helper') @mock.patch.object(native_conn, 'cfg') def test_ssl_conn_cfg_missing(self, mock_cfg, mock_get_schema_helper, mock_idl, mock_threading, mock_stream): mock_cfg.CONF.OVS.ovsdb_connection = 'ssl:127.0.0.1:6640' mock_cfg.CONF.OVS.ssl_key_file = None mock_cfg.CONF.OVS.ssl_cert_file = None mock_cfg.CONF.OVS.ssl_ca_cert_file = None self.assertRaises(ovsdb_exc.OvsdbSslRequiredOptError, native_conn.idl_factory) neutron-12.1.1/neutron/tests/unit/agent/ovsdb/native/test_helpers.py0000664000175000017500000000244713553660047025717 0ustar zuulzuul00000000000000# Copyright 2015, Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.agent.ovsdb.native import helpers from neutron.tests import base CONNECTION_TO_MANAGER_URI_MAP = ( ('unix:/path/to/file', 'punix:/path/to/file'), ('tcp:127.0.0.1:6640', 'ptcp:6640:127.0.0.1'), ('ssl:192.168.1.1:8080', 'pssl:8080:192.168.1.1')) class TestOVSNativeHelpers(base.BaseTestCase): def setUp(self): super(TestOVSNativeHelpers, self).setUp() self.execute = mock.patch('neutron.agent.common.utils.execute').start() def test__connection_to_manager_uri(self): for conn_uri, expected in CONNECTION_TO_MANAGER_URI_MAP: self.assertEqual(expected, helpers._connection_to_manager_uri(conn_uri)) neutron-12.1.1/neutron/tests/unit/agent/dhcp/0000775000175000017500000000000013553660156021151 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/dhcp/test_agent.py0000664000175000017500000030002713553660047023661 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import copy import sys import uuid import eventlet import mock from neutron_lib.agent import constants as agent_consts from neutron_lib import constants as const from neutron_lib import exceptions from oslo_config import cfg import oslo_messaging import testtools from neutron.agent.dhcp import agent as dhcp_agent from neutron.agent import dhcp_agent as entry from neutron.agent.linux import dhcp from neutron.agent.linux import interface from neutron.agent.metadata import driver as metadata_driver from neutron.common import config as common_config from neutron.common import constants as n_const from neutron.common import utils from neutron.conf.agent import common as config from neutron.conf.agent import dhcp as dhcp_config from neutron.tests import base HOSTNAME = 'hostname' dev_man = dhcp.DeviceManager rpc_api = dhcp_agent.DhcpPluginApi DEVICE_MANAGER = '%s.%s' % (dev_man.__module__, dev_man.__name__) DHCP_PLUGIN = '%s.%s' % (rpc_api.__module__, rpc_api.__name__) FAKE_NETWORK_UUID = '12345678-1234-5678-1234567890ab' FAKE_NETWORK_DHCP_NS = "qdhcp-%s" % FAKE_NETWORK_UUID FAKE_TENANT_ID = 'aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa' FAKE_PRIORITY = 6 fake_subnet1_allocation_pools = dhcp.DictModel(dict(id='', start='172.9.9.2', end='172.9.9.254')) fake_subnet1 = dhcp.DictModel(dict(id='bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbb', network_id=FAKE_NETWORK_UUID, cidr='172.9.9.0/24', enable_dhcp=True, name='', tenant_id=FAKE_TENANT_ID, gateway_ip='172.9.9.1', host_routes=[], dns_nameservers=[], ip_version=4, ipv6_ra_mode=None, ipv6_address_mode=None, allocation_pools=fake_subnet1_allocation_pools)) fake_subnet2_allocation_pools = dhcp.DictModel(dict(id='', start='172.9.8.2', end='172.9.8.254')) fake_subnet2 = dhcp.DictModel(dict(id='dddddddd-dddd-dddd-dddddddddddd', network_id=FAKE_NETWORK_UUID, cidr='172.9.8.0/24', enable_dhcp=False, name='', tenant_id=FAKE_TENANT_ID, gateway_ip='172.9.8.1', host_routes=[], dns_nameservers=[], ip_version=4, allocation_pools=fake_subnet2_allocation_pools)) fake_subnet3 = dhcp.DictModel(dict(id='bbbbbbbb-1111-2222-bbbbbbbbbbbb', network_id=FAKE_NETWORK_UUID, cidr='192.168.1.1/24', enable_dhcp=True, ip_version=4)) fake_ipv6_subnet = dhcp.DictModel(dict(id='bbbbbbbb-1111-2222-bbbbbbbbbbbb', network_id=FAKE_NETWORK_UUID, cidr='2001:0db8::0/64', enable_dhcp=True, tenant_id=FAKE_TENANT_ID, gateway_ip='2001:0db8::1', ip_version=6, ipv6_ra_mode='slaac', ipv6_address_mode=None)) fake_meta_subnet = dhcp.DictModel(dict(id='bbbbbbbb-1111-2222-bbbbbbbbbbbb', network_id=FAKE_NETWORK_UUID, cidr='169.254.169.252/30', gateway_ip='169.254.169.253', enable_dhcp=True, ip_version=4)) fake_fixed_ip1 = dhcp.DictModel(dict(id='', subnet_id=fake_subnet1.id, ip_address='172.9.9.9')) fake_fixed_ip_subnet2 = dhcp.DictModel(dict(id='', subnet_id=fake_subnet2.id, ip_address='172.9.8.9')) fake_fixed_ip2 = dhcp.DictModel(dict(id='', subnet_id=fake_subnet1.id, ip_address='172.9.9.10')) fake_fixed_ipv6 = dhcp.DictModel(dict(id='', subnet_id=fake_ipv6_subnet.id, ip_address='2001:db8::a8bb:ccff:fedd:ee99')) fake_meta_fixed_ip = dhcp.DictModel(dict(id='', subnet=fake_meta_subnet, ip_address='169.254.169.254')) fake_allocation_pool_subnet1 = dhcp.DictModel(dict(id='', start='172.9.9.2', end='172.9.9.254')) fake_port1 = dhcp.DictModel(dict(id='12345678-1234-aaaa-1234567890ab', device_id='dhcp-12345678-1234-aaaa-1234567890ab', device_owner='', allocation_pools=fake_subnet1_allocation_pools, mac_address='aa:bb:cc:dd:ee:ff', network_id=FAKE_NETWORK_UUID, fixed_ips=[fake_fixed_ip1])) fake_dhcp_port = dhcp.DictModel(dict(id='12345678-1234-aaaa-123456789022', device_id='dhcp-12345678-1234-aaaa-123456789022', device_owner=const.DEVICE_OWNER_DHCP, allocation_pools=fake_subnet1_allocation_pools, mac_address='aa:bb:cc:dd:ee:22', network_id=FAKE_NETWORK_UUID, fixed_ips=[fake_fixed_ip2])) fake_port2 = dhcp.DictModel(dict(id='12345678-1234-aaaa-123456789000', device_id='dhcp-12345678-1234-aaaa-123456789000', device_owner='', mac_address='aa:bb:cc:dd:ee:99', network_id=FAKE_NETWORK_UUID, revision_number=77, fixed_ips=[fake_fixed_ip2])) fake_ipv6_port = dhcp.DictModel(dict(id='12345678-1234-aaaa-123456789000', device_owner='', mac_address='aa:bb:cc:dd:ee:99', network_id=FAKE_NETWORK_UUID, fixed_ips=[fake_fixed_ipv6])) fake_meta_port = dhcp.DictModel(dict(id='12345678-1234-aaaa-1234567890ab', mac_address='aa:bb:cc:dd:ee:ff', network_id=FAKE_NETWORK_UUID, device_owner=const.DEVICE_OWNER_ROUTER_INTF, device_id='forzanapoli', fixed_ips=[fake_meta_fixed_ip])) fake_meta_dvr_port = dhcp.DictModel(fake_meta_port.copy()) fake_meta_dvr_port.device_owner = const.DEVICE_OWNER_DVR_INTERFACE fake_dist_port = dhcp.DictModel(dict(id='12345678-1234-aaaa-1234567890ab', mac_address='aa:bb:cc:dd:ee:ff', network_id=FAKE_NETWORK_UUID, device_owner=const.DEVICE_OWNER_DVR_INTERFACE, device_id='forzanapoli', fixed_ips=[fake_meta_fixed_ip])) fake_network = dhcp.NetModel(dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, admin_state_up=True, subnets=[fake_subnet1, fake_subnet2], ports=[fake_port1])) fake_network_ipv6 = dhcp.NetModel(dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, admin_state_up=True, subnets=[fake_ipv6_subnet], ports=[fake_ipv6_port])) fake_network_ipv6_ipv4 = dhcp.NetModel(dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, admin_state_up=True, subnets=[fake_ipv6_subnet, fake_subnet1], ports=[fake_port1])) isolated_network = dhcp.NetModel( dict( id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, admin_state_up=True, subnets=[fake_subnet1], ports=[fake_port1])) nonisolated_dist_network = dhcp.NetModel( dict( id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, admin_state_up=True, subnets=[fake_subnet1], ports=[fake_port1, fake_port2])) empty_network = dhcp.NetModel( dict( id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, admin_state_up=True, subnets=[fake_subnet1], ports=[])) fake_meta_network = dhcp.NetModel( dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, admin_state_up=True, subnets=[fake_meta_subnet], ports=[fake_meta_port])) fake_meta_dvr_network = dhcp.NetModel(fake_meta_network.copy()) fake_meta_dvr_network.ports = [fake_meta_dvr_port] fake_dist_network = dhcp.NetModel( dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, admin_state_up=True, subnets=[fake_meta_subnet], ports=[fake_meta_port, fake_dist_port])) fake_down_network = dhcp.NetModel( dict(id='12345678-dddd-dddd-1234567890ab', tenant_id=FAKE_TENANT_ID, admin_state_up=False, subnets=[], ports=[])) class TestDhcpAgent(base.BaseTestCase): METADATA_DEFAULT_IP = dhcp.METADATA_DEFAULT_IP def setUp(self): super(TestDhcpAgent, self).setUp() entry.register_options(cfg.CONF) cfg.CONF.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') # disable setting up periodic state reporting cfg.CONF.set_override('report_interval', 0, 'AGENT') self.driver_cls_p = mock.patch( 'neutron.agent.dhcp.agent.importutils.import_class') self.driver = mock.Mock(name='driver') self.driver.existing_dhcp_networks.return_value = [] self.driver_cls = self.driver_cls_p.start() self.driver_cls.return_value = self.driver self.mock_makedirs_p = mock.patch("os.makedirs") self.mock_makedirs = self.mock_makedirs_p.start() self.mock_create_metadata_proxy_cfg = mock.patch( "neutron.agent.metadata.driver.HaproxyConfigurator") self.mock_create_metadata_proxy_cfg.start() self.mock_ip_wrapper_p = mock.patch("neutron.agent.linux.ip_lib." "IPWrapper") self.mock_ip_wrapper = self.mock_ip_wrapper_p.start() def test_init_host(self): dhcp = dhcp_agent.DhcpAgent(HOSTNAME) with mock.patch.object(dhcp, 'sync_state') as sync_state: dhcp.init_host() sync_state.assert_called_once_with() def test_dhcp_agent_manager(self): state_rpc_str = 'neutron.agent.rpc.PluginReportStateAPI' # sync_state is needed for this test cfg.CONF.set_override('report_interval', 1, 'AGENT') mock_start_ready = mock.patch.object( dhcp_agent.DhcpAgentWithStateReport, 'start_ready_ports_loop', autospec=True).start() with mock.patch.object(dhcp_agent.DhcpAgentWithStateReport, 'periodic_resync', autospec=True) as mock_periodic_resync: with mock.patch(state_rpc_str) as state_rpc: with mock.patch.object(sys, 'argv') as sys_argv: sys_argv.return_value = [ 'dhcp', '--config-file', base.etcdir('neutron.conf')] cfg.CONF.register_opts(dhcp_config.DHCP_AGENT_OPTS) config.register_interface_driver_opts_helper(cfg.CONF) config.register_agent_state_opts_helper(cfg.CONF) config.register_interface_opts(cfg.CONF) common_config.init(sys.argv[1:]) agent_mgr = dhcp_agent.DhcpAgentWithStateReport( 'testhost') eventlet.greenthread.sleep(1) agent_mgr.after_start() mock_periodic_resync.assert_called_once_with(agent_mgr) mock_start_ready.assert_called_once_with(agent_mgr) state_rpc.assert_has_calls( [mock.call(mock.ANY), mock.call().report_state(mock.ANY, mock.ANY, mock.ANY)]) def test_run_completes_single_pass(self): with mock.patch(DEVICE_MANAGER): dhcp = dhcp_agent.DhcpAgent(HOSTNAME) attrs_to_mock = dict( [(a, mock.DEFAULT) for a in ['periodic_resync', 'start_ready_ports_loop', '_process_loop']]) with mock.patch.multiple(dhcp, **attrs_to_mock) as mocks: with mock.patch.object(dhcp_agent.eventlet, 'spawn_n') as spawn_n: dhcp.run() mocks['periodic_resync'].assert_called_once_with() mocks['start_ready_ports_loop'].assert_called_once_with() spawn_n.assert_called_once_with(mocks['_process_loop']) def test_call_driver(self): network = mock.Mock() network.id = '1' dhcp = dhcp_agent.DhcpAgent(cfg.CONF) self.assertTrue(dhcp.call_driver('foo', network)) self.driver.assert_called_once_with(cfg.CONF, mock.ANY, mock.ANY, mock.ANY, mock.ANY) def _test_call_driver_failure(self, exc=None, trace_level='exception', expected_sync=True): network = mock.Mock() network.id = '1' self.driver.return_value.foo.side_effect = exc or Exception dhcp = dhcp_agent.DhcpAgent(HOSTNAME) with mock.patch.object(dhcp, 'schedule_resync') as schedule_resync: self.assertIsNone(dhcp.call_driver('foo', network)) self.driver.assert_called_once_with(cfg.CONF, mock.ANY, mock.ANY, mock.ANY, mock.ANY) self.assertEqual(expected_sync, schedule_resync.called) def test_call_driver_ip_address_generation_failure(self): error = oslo_messaging.RemoteError( exc_type='IpAddressGenerationFailure') self._test_call_driver_failure(exc=error, expected_sync=False) def test_call_driver_failure(self): self._test_call_driver_failure() def test_call_driver_remote_error_net_not_found(self): self._test_call_driver_failure( exc=oslo_messaging.RemoteError(exc_type='NetworkNotFound'), trace_level='warning') def test_call_driver_network_not_found(self): self._test_call_driver_failure( exc=exceptions.NetworkNotFound(net_id='1'), trace_level='warning') def test_call_driver_conflict(self): self._test_call_driver_failure( exc=exceptions.Conflict(), trace_level='warning', expected_sync=False) def _test_sync_state_helper(self, known_net_ids, active_net_ids): active_networks = set(mock.Mock(id=netid) for netid in active_net_ids) with mock.patch(DHCP_PLUGIN) as plug: mock_plugin = mock.Mock() mock_plugin.get_active_networks_info.return_value = active_networks plug.return_value = mock_plugin dhcp = dhcp_agent.DhcpAgent(HOSTNAME) attrs_to_mock = dict([(a, mock.DEFAULT) for a in ['disable_dhcp_helper', 'cache', 'safe_configure_dhcp_for_network']]) with mock.patch.multiple(dhcp, **attrs_to_mock) as mocks: mocks['cache'].get_network_ids.return_value = known_net_ids mocks['cache'].get_port_ids.return_value = range(4) dhcp.sync_state() diff = set(known_net_ids) - set(active_net_ids) exp_disable = [mock.call(net_id) for net_id in diff] mocks['cache'].assert_has_calls([mock.call.get_network_ids()]) mocks['disable_dhcp_helper'].assert_has_calls(exp_disable) self.assertEqual(set(range(4)), dhcp.dhcp_ready_ports) def test_sync_state_initial(self): self._test_sync_state_helper([], ['a']) def test_sync_state_same(self): self._test_sync_state_helper(['a'], ['a']) def test_sync_state_disabled_net(self): self._test_sync_state_helper(['b'], ['a']) def test_sync_state_waitall(self): with mock.patch.object(dhcp_agent.eventlet.GreenPool, 'waitall') as w: active_net_ids = ['1', '2', '3', '4', '5'] known_net_ids = ['1', '2', '3', '4', '5'] self._test_sync_state_helper(known_net_ids, active_net_ids) w.assert_called_once_with() def test_sync_state_for_all_networks_plugin_error(self): with mock.patch(DHCP_PLUGIN) as plug: mock_plugin = mock.Mock() mock_plugin.get_active_networks_info.side_effect = Exception plug.return_value = mock_plugin with mock.patch.object(dhcp_agent.LOG, 'exception') as log: dhcp = dhcp_agent.DhcpAgent(HOSTNAME) with mock.patch.object(dhcp, 'schedule_resync') as schedule_resync: dhcp.sync_state() self.assertTrue(log.called) self.assertTrue(schedule_resync.called) def test_sync_state_for_one_network_plugin_error(self): with mock.patch(DHCP_PLUGIN) as plug: mock_plugin = mock.Mock() exc = Exception() mock_plugin.get_active_networks_info.side_effect = exc plug.return_value = mock_plugin with mock.patch.object(dhcp_agent.LOG, 'exception') as log: dhcp = dhcp_agent.DhcpAgent(HOSTNAME) with mock.patch.object(dhcp, 'schedule_resync') as schedule_resync: dhcp.sync_state(['foo_network']) self.assertTrue(log.called) schedule_resync.assert_called_with(exc, 'foo_network') def test_periodic_resync(self): dhcp = dhcp_agent.DhcpAgent(HOSTNAME) with mock.patch.object(dhcp_agent.eventlet, 'spawn') as spawn: dhcp.periodic_resync() spawn.assert_called_once_with(dhcp._periodic_resync_helper) def test_start_ready_ports_loop(self): dhcp = dhcp_agent.DhcpAgent(HOSTNAME) with mock.patch.object(dhcp_agent.eventlet, 'spawn') as spawn: dhcp.start_ready_ports_loop() spawn.assert_called_once_with(dhcp._dhcp_ready_ports_loop) def test__dhcp_ready_ports_doesnt_log_exception_on_timeout(self): dhcp = dhcp_agent.DhcpAgent(HOSTNAME) dhcp.dhcp_ready_ports = set(range(4)) with mock.patch.object(dhcp.plugin_rpc, 'dhcp_ready_on_ports', side_effect=oslo_messaging.MessagingTimeout): # exit after 2 iterations with mock.patch.object(dhcp_agent.eventlet, 'sleep', side_effect=[0, 0, RuntimeError]): with mock.patch.object(dhcp_agent.LOG, 'exception') as lex: with testtools.ExpectedException(RuntimeError): dhcp._dhcp_ready_ports_loop() self.assertFalse(lex.called) def test__dhcp_ready_ports_loop(self): dhcp = dhcp_agent.DhcpAgent(HOSTNAME) dhcp.dhcp_ready_ports = set(range(4)) with mock.patch.object(dhcp.plugin_rpc, 'dhcp_ready_on_ports', side_effect=[RuntimeError, 0]) as ready: # exit after 2 iterations with mock.patch.object(dhcp_agent.eventlet, 'sleep', side_effect=[0, 0, RuntimeError]): with testtools.ExpectedException(RuntimeError): dhcp._dhcp_ready_ports_loop() # should have been called with all ports again after the failure ready.assert_has_calls([mock.call(set(range(4)))] * 2) def test_dhcp_ready_ports_loop_with_limit_ports_per_call(self): dhcp = dhcp_agent.DhcpAgent(HOSTNAME) sync_max = dhcp_agent.DHCP_READY_PORTS_SYNC_MAX port_count = sync_max + 1 dhcp.dhcp_ready_ports = set(range(port_count)) with mock.patch.object(dhcp.plugin_rpc, 'dhcp_ready_on_ports') as ready: # exit after 2 iterations with mock.patch.object(dhcp_agent.eventlet, 'sleep', side_effect=[0, 0, RuntimeError]): with testtools.ExpectedException(RuntimeError): dhcp._dhcp_ready_ports_loop() # all ports should have been processed self.assertEqual(set(), dhcp.dhcp_ready_ports) # two calls are expected, one with DHCP_READY_PORTS_SYNC_MAX ports, # second one with one port self.assertEqual(2, ready.call_count) self.assertEqual(sync_max, len(ready.call_args_list[0][0][0])) self.assertEqual(1, len(ready.call_args_list[1][0][0])) # all ports need to be ready ports_ready = (ready.call_args_list[0][0][0] | ready.call_args_list[1][0][0]) self.assertEqual(set(range(port_count)), ports_ready) def test_dhcp_ready_ports_updates_after_enable_dhcp(self): dhcp = dhcp_agent.DhcpAgent(HOSTNAME) self.assertEqual(set(), dhcp.dhcp_ready_ports) dhcp.configure_dhcp_for_network(fake_network) self.assertEqual({fake_port1.id}, dhcp.dhcp_ready_ports) def test_dhcp_metadata_destroy(self): cfg.CONF.set_override('force_metadata', True) cfg.CONF.set_override('enable_isolated_metadata', False) with mock.patch.object(metadata_driver, 'MetadataDriver') as md_cls: dhcp = dhcp_agent.DhcpAgent(HOSTNAME) dhcp.configure_dhcp_for_network(fake_network) md_cls.spawn_monitored_metadata_proxy.assert_called_once_with( mock.ANY, mock.ANY, mock.ANY, mock.ANY, bind_address=self.METADATA_DEFAULT_IP, network_id=fake_network.id) dhcp.disable_dhcp_helper(fake_network.id) md_cls.destroy_monitored_metadata_proxy.assert_called_once_with( mock.ANY, fake_network.id, mock.ANY, fake_network.namespace) def test_report_state_revival_logic(self): dhcp = dhcp_agent.DhcpAgentWithStateReport(HOSTNAME) with mock.patch.object(dhcp.state_rpc, 'report_state') as report_state,\ mock.patch.object(dhcp, "run"): report_state.return_value = agent_consts.AGENT_ALIVE dhcp._report_state() self.assertEqual({}, dhcp.needs_resync_reasons) report_state.return_value = agent_consts.AGENT_REVIVED dhcp._report_state() self.assertEqual(dhcp.needs_resync_reasons[None], ['Agent has just been revived']) def test_periodic_resync_helper(self): with mock.patch.object(dhcp_agent.eventlet, 'sleep') as sleep: dhcp = dhcp_agent.DhcpAgent(HOSTNAME) resync_reasons = collections.OrderedDict( (('a', 'reason1'), ('b', 'reason2'))) dhcp.needs_resync_reasons = resync_reasons with mock.patch.object(dhcp, 'sync_state') as sync_state: sync_state.side_effect = RuntimeError with testtools.ExpectedException(RuntimeError): dhcp._periodic_resync_helper() sync_state.assert_called_once_with(resync_reasons.keys()) sleep.assert_called_once_with(dhcp.conf.resync_interval) self.assertEqual(0, len(dhcp.needs_resync_reasons)) def test_populate_cache_on_start_without_active_networks_support(self): # emul dhcp driver that doesn't support retrieving of active networks self.driver.existing_dhcp_networks.side_effect = NotImplementedError with mock.patch.object(dhcp_agent.LOG, 'debug') as log: dhcp = dhcp_agent.DhcpAgent(HOSTNAME) self.driver.existing_dhcp_networks.assert_called_once_with( dhcp.conf, ) self.assertFalse(dhcp.cache.get_network_ids()) self.assertTrue(log.called) def test_populate_cache_on_start(self): networks = ['aaa', 'bbb'] self.driver.existing_dhcp_networks.return_value = networks dhcp = dhcp_agent.DhcpAgent(HOSTNAME) self.driver.existing_dhcp_networks.assert_called_once_with( dhcp.conf, ) self.assertEqual(set(networks), set(dhcp.cache.get_network_ids())) def test_none_interface_driver(self): cfg.CONF.set_override('interface_driver', None) self.assertRaises(SystemExit, dhcp.DeviceManager, cfg.CONF, None) def test_nonexistent_interface_driver(self): # Temporarily turn off mock, so could use the real import_class # to import interface_driver. self.driver_cls_p.stop() self.addCleanup(self.driver_cls_p.start) cfg.CONF.set_override('interface_driver', 'foo.bar') self.assertRaises(SystemExit, dhcp.DeviceManager, cfg.CONF, None) class TestLogArgs(base.BaseTestCase): def test_log_args_without_log_dir_and_file(self): conf_dict = {'debug': True, 'log_dir': None, 'log_file': None, 'use_syslog': True, 'syslog_log_facility': 'LOG_USER'} conf = dhcp.DictModel(conf_dict) expected_args = ['--debug', '--use-syslog', '--syslog-log-facility=LOG_USER'] args = config.get_log_args(conf, 'log_file_name') self.assertEqual(expected_args, args) def test_log_args_without_log_file(self): conf_dict = {'debug': True, 'log_dir': '/etc/tests', 'log_file': None, 'use_syslog': False, 'syslog_log_facility': 'LOG_USER'} conf = dhcp.DictModel(conf_dict) expected_args = ['--debug', '--log-file=log_file_name', '--log-dir=/etc/tests'] args = config.get_log_args(conf, 'log_file_name') self.assertEqual(expected_args, args) def test_log_args_with_log_dir_and_file(self): conf_dict = {'debug': True, 'log_dir': '/etc/tests', 'log_file': 'tests/filelog', 'use_syslog': False, 'syslog_log_facility': 'LOG_USER'} conf = dhcp.DictModel(conf_dict) expected_args = ['--debug', '--log-file=log_file_name', '--log-dir=/etc/tests/tests'] args = config.get_log_args(conf, 'log_file_name') self.assertEqual(expected_args, args) def test_log_args_without_log_dir(self): conf_dict = {'debug': True, 'log_file': 'tests/filelog', 'log_dir': None, 'use_syslog': False, 'syslog_log_facility': 'LOG_USER'} conf = dhcp.DictModel(conf_dict) expected_args = ['--debug', '--log-file=log_file_name', '--log-dir=tests'] args = config.get_log_args(conf, 'log_file_name') self.assertEqual(expected_args, args) def test_log_args_with_filelog_and_syslog(self): conf_dict = {'debug': True, 'log_file': 'tests/filelog', 'log_dir': '/etc/tests', 'use_syslog': True, 'syslog_log_facility': 'LOG_USER'} conf = dhcp.DictModel(conf_dict) expected_args = ['--debug', '--log-file=log_file_name', '--log-dir=/etc/tests/tests'] args = config.get_log_args(conf, 'log_file_name') self.assertEqual(expected_args, args) class TestDhcpAgentEventHandler(base.BaseTestCase): def setUp(self): super(TestDhcpAgentEventHandler, self).setUp() config.register_interface_driver_opts_helper(cfg.CONF) cfg.CONF.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') entry.register_options(cfg.CONF) # register all dhcp cfg options self.plugin_p = mock.patch(DHCP_PLUGIN) plugin_cls = self.plugin_p.start() self.plugin = mock.Mock() plugin_cls.return_value = self.plugin self.cache_p = mock.patch('neutron.agent.dhcp.agent.NetworkCache') cache_cls = self.cache_p.start() self.cache = mock.Mock() self.cache.is_port_message_stale.return_value = False cache_cls.return_value = self.cache self.mock_makedirs_p = mock.patch("os.makedirs") self.mock_makedirs = self.mock_makedirs_p.start() self.mock_init_p = mock.patch('neutron.agent.dhcp.agent.' 'DhcpAgent._populate_networks_cache') self.mock_init = self.mock_init_p.start() self.dhcp = dhcp_agent.DhcpAgent(HOSTNAME) self.call_driver_p = mock.patch.object(self.dhcp, 'call_driver') self.call_driver = self.call_driver_p.start() self.schedule_resync_p = mock.patch.object(self.dhcp, 'schedule_resync') self.schedule_resync = self.schedule_resync_p.start() self.external_process_p = mock.patch( 'neutron.agent.linux.external_process.ProcessManager' ) self.external_process = self.external_process_p.start() def _process_manager_constructor_call(self, ns=FAKE_NETWORK_DHCP_NS): return mock.call(conf=cfg.CONF, uuid=FAKE_NETWORK_UUID, namespace=ns, default_cmd_callback=mock.ANY) def _enable_dhcp_helper(self, network, enable_isolated_metadata=False, is_isolated_network=False): self.dhcp._process_monitor = mock.Mock() if enable_isolated_metadata: cfg.CONF.set_override('enable_isolated_metadata', True) self.plugin.get_network_info.return_value = network self.dhcp.enable_dhcp_helper(network.id) self.plugin.assert_has_calls([ mock.call.get_network_info(network.id)]) self.call_driver.assert_called_once_with('enable', network) self.cache.assert_has_calls([mock.call.put(network)]) if is_isolated_network and enable_isolated_metadata: self.external_process.assert_has_calls([ self._process_manager_constructor_call(), mock.call().enable()], any_order=True) else: self.external_process.assert_has_calls([ self._process_manager_constructor_call(), mock.call().disable()]) def test_enable_dhcp_helper_enable_metadata_isolated_network(self): self._enable_dhcp_helper(isolated_network, enable_isolated_metadata=True, is_isolated_network=True) def test_enable_dhcp_helper_enable_metadata_no_gateway(self): isolated_network_no_gateway = copy.deepcopy(isolated_network) isolated_network_no_gateway.subnets[0].gateway_ip = None self._enable_dhcp_helper(isolated_network_no_gateway, enable_isolated_metadata=True, is_isolated_network=True) def test_enable_dhcp_helper_enable_metadata_nonisolated_network(self): nonisolated_network = copy.deepcopy(isolated_network) nonisolated_network.ports[0].device_owner = ( const.DEVICE_OWNER_ROUTER_INTF) nonisolated_network.ports[0].fixed_ips[0].ip_address = '172.9.9.1' self._enable_dhcp_helper(nonisolated_network, enable_isolated_metadata=True, is_isolated_network=False) def test_enable_dhcp_helper_enable_metadata_nonisolated_dist_network(self): nonisolated_dvr_network = copy.deepcopy(nonisolated_dist_network) nonisolated_dvr_network.ports[0].device_owner = ( const.DEVICE_OWNER_ROUTER_INTF) nonisolated_dvr_network.ports[0].fixed_ips[0].ip_address = '172.9.9.1' nonisolated_dvr_network.ports[1].device_owner = ( const.DEVICE_OWNER_DVR_INTERFACE) nonisolated_dvr_network.ports[1].fixed_ips[0].ip_address = '172.9.9.1' self._enable_dhcp_helper(nonisolated_dvr_network, enable_isolated_metadata=True, is_isolated_network=False) def test_enable_dhcp_helper_enable_metadata_empty_network(self): self._enable_dhcp_helper(empty_network, enable_isolated_metadata=True, is_isolated_network=True) def test_enable_dhcp_helper_enable_metadata_ipv6_ipv4_network(self): self._enable_dhcp_helper(fake_network_ipv6_ipv4, enable_isolated_metadata=True, is_isolated_network=True) def test_enable_dhcp_helper_driver_failure_ipv6_ipv4_network(self): self.plugin.get_network_info.return_value = fake_network_ipv6_ipv4 self.call_driver.return_value = False cfg.CONF.set_override('enable_isolated_metadata', True) with mock.patch.object( self.dhcp, 'enable_isolated_metadata_proxy') as enable_metadata: self.dhcp.enable_dhcp_helper(fake_network_ipv6_ipv4.id) self.plugin.assert_has_calls( [mock.call.get_network_info(fake_network_ipv6_ipv4.id)]) self.call_driver.assert_called_once_with('enable', fake_network_ipv6_ipv4) self.assertFalse(self.cache.called) self.assertFalse(enable_metadata.called) self.assertFalse(self.external_process.called) def test_enable_dhcp_helper(self): self._enable_dhcp_helper(fake_network) def test_enable_dhcp_helper_ipv6_network(self): self._enable_dhcp_helper(fake_network_ipv6) def test_enable_dhcp_helper_down_network(self): self.plugin.get_network_info.return_value = fake_down_network self.dhcp.enable_dhcp_helper(fake_down_network.id) self.plugin.assert_has_calls( [mock.call.get_network_info(fake_down_network.id)]) self.assertFalse(self.call_driver.called) self.assertFalse(self.cache.called) self.assertFalse(self.external_process.called) def test_enable_dhcp_helper_network_none(self): self.plugin.get_network_info.return_value = None self.dhcp.enable_dhcp_helper('fake_id') self.plugin.assert_has_calls( [mock.call.get_network_info('fake_id')]) self.assertFalse(self.call_driver.called) self.assertFalse(self.dhcp.schedule_resync.called) def test_enable_dhcp_helper_exception_during_rpc(self): self.plugin.get_network_info.side_effect = Exception with mock.patch.object(dhcp_agent.LOG, 'exception') as log: self.dhcp.enable_dhcp_helper(fake_network.id) self.plugin.assert_has_calls( [mock.call.get_network_info(fake_network.id)]) self.assertFalse(self.call_driver.called) self.assertTrue(log.called) self.assertTrue(self.schedule_resync.called) self.assertFalse(self.cache.called) self.assertFalse(self.external_process.called) def test_enable_dhcp_helper_driver_failure(self): self.plugin.get_network_info.return_value = fake_network self.call_driver.return_value = False cfg.CONF.set_override('enable_isolated_metadata', True) self.dhcp.enable_dhcp_helper(fake_network.id) self.plugin.assert_has_calls( [mock.call.get_network_info(fake_network.id)]) self.call_driver.assert_called_once_with('enable', fake_network) self.assertFalse(self.cache.called) self.assertFalse(self.external_process.called) def _disable_dhcp_helper_known_network(self, isolated_metadata=False): if isolated_metadata: cfg.CONF.set_override('enable_isolated_metadata', True) self.cache.get_network_by_id.return_value = fake_network self.dhcp.disable_dhcp_helper(fake_network.id) self.cache.assert_has_calls( [mock.call.get_network_by_id(fake_network.id)]) self.call_driver.assert_called_once_with('disable', fake_network) self.external_process.assert_has_calls([ self._process_manager_constructor_call(), mock.call().disable()]) def test_disable_dhcp_helper_known_network_isolated_metadata(self): self._disable_dhcp_helper_known_network(isolated_metadata=True) def test_disable_dhcp_helper_known_network(self): self._disable_dhcp_helper_known_network() def test_disable_dhcp_helper_unknown_network(self): self.cache.get_network_by_id.return_value = None self.dhcp.disable_dhcp_helper('abcdef') self.cache.assert_has_calls( [mock.call.get_network_by_id('abcdef')]) self.assertEqual(0, self.call_driver.call_count) self.assertFalse(self.external_process.called) def _disable_dhcp_helper_driver_failure(self, isolated_metadata=False): if isolated_metadata: cfg.CONF.set_override('enable_isolated_metadata', True) self.cache.get_network_by_id.return_value = fake_network self.call_driver.return_value = False self.dhcp.disable_dhcp_helper(fake_network.id) self.cache.assert_has_calls( [mock.call.get_network_by_id(fake_network.id)]) self.call_driver.assert_called_once_with('disable', fake_network) self.cache.assert_has_calls( [mock.call.get_network_by_id(fake_network.id)]) self.external_process.assert_has_calls([ self._process_manager_constructor_call(), mock.call().disable() ]) def test_disable_dhcp_helper_driver_failure_isolated_metadata(self): self._disable_dhcp_helper_driver_failure(isolated_metadata=True) def test_disable_dhcp_helper_driver_failure(self): self._disable_dhcp_helper_driver_failure() def test_enable_isolated_metadata_proxy(self): self.dhcp._process_monitor = mock.Mock() self.dhcp.enable_isolated_metadata_proxy(fake_network) self.external_process.assert_has_calls([ self._process_manager_constructor_call(), mock.call().enable() ], any_order=True) def test_disable_isolated_metadata_proxy(self): method_path = ('neutron.agent.metadata.driver.MetadataDriver' '.destroy_monitored_metadata_proxy') with mock.patch(method_path) as destroy: self.dhcp.disable_isolated_metadata_proxy(fake_network) destroy.assert_called_once_with(self.dhcp._process_monitor, fake_network.id, cfg.CONF, fake_network.namespace) def _test_enable_isolated_metadata_proxy(self, network): cfg.CONF.set_override('enable_metadata_network', True) cfg.CONF.set_override('debug', True) cfg.CONF.set_override('log_file', 'test.log') method_path = ('neutron.agent.metadata.driver.MetadataDriver' '.spawn_monitored_metadata_proxy') with mock.patch(method_path) as spawn: self.dhcp.enable_isolated_metadata_proxy(network) metadata_ip = dhcp.METADATA_DEFAULT_IP spawn.assert_called_once_with(self.dhcp._process_monitor, network.namespace, dhcp.METADATA_PORT, cfg.CONF, bind_address=metadata_ip, router_id='forzanapoli') def test_enable_isolated_metadata_proxy_with_metadata_network(self): self._test_enable_isolated_metadata_proxy(fake_meta_network) def test_enable_isolated_metadata_proxy_with_metadata_network_dvr(self): self._test_enable_isolated_metadata_proxy(fake_meta_dvr_network) def test_enable_isolated_metadata_proxy_with_dist_network(self): self._test_enable_isolated_metadata_proxy(fake_dist_network) def _test_disable_isolated_metadata_proxy(self, network): cfg.CONF.set_override('enable_metadata_network', True) method_path = ('neutron.agent.metadata.driver.MetadataDriver' '.destroy_monitored_metadata_proxy') with mock.patch(method_path) as destroy: self.dhcp.enable_isolated_metadata_proxy(network) self.dhcp.disable_isolated_metadata_proxy(network) destroy.assert_called_once_with(self.dhcp._process_monitor, 'forzanapoli', cfg.CONF, network.namespace) def test_disable_isolated_metadata_proxy_with_metadata_network(self): self._test_disable_isolated_metadata_proxy(fake_meta_network) def test_disable_isolated_metadata_proxy_with_metadata_network_dvr(self): self._test_disable_isolated_metadata_proxy(fake_meta_dvr_network) def test_disable_isolated_metadata_proxy_with_dist_network(self): self._test_disable_isolated_metadata_proxy(fake_dist_network) def test_network_create_end(self): payload = dict(network=dict(id=fake_network.id), priority=FAKE_PRIORITY) with mock.patch.object(self.dhcp, 'enable_dhcp_helper') as enable: self.dhcp.network_create_end(None, payload) self.dhcp._process_resource_update() enable.assert_called_once_with(fake_network.id) def test_network_update_end_admin_state_up(self): payload = dict(network=dict(id=fake_network.id, admin_state_up=True), priority=FAKE_PRIORITY) with mock.patch.object(self.dhcp, 'enable_dhcp_helper') as enable: self.dhcp.network_update_end(None, payload) self.dhcp._process_resource_update() enable.assert_called_once_with(fake_network.id) def test_network_update_end_admin_state_down(self): payload = dict(network=dict(id=fake_network.id, admin_state_up=False), priority=FAKE_PRIORITY) with mock.patch.object(self.dhcp, 'disable_dhcp_helper') as disable: self.dhcp.network_update_end(None, payload) self.dhcp._process_resource_update() disable.assert_called_once_with(fake_network.id) def test_network_delete_end(self): payload = dict(network_id=fake_network.id, priority=FAKE_PRIORITY) with mock.patch.object(self.dhcp, 'disable_dhcp_helper') as disable: self.dhcp.network_delete_end(None, payload) self.dhcp._process_resource_update() disable.assert_called_once_with(fake_network.id) def test_refresh_dhcp_helper_no_dhcp_enabled_networks(self): network = dhcp.NetModel(dict(id='net-id', tenant_id=FAKE_TENANT_ID, admin_state_up=True, subnets=[], ports=[])) self.cache.get_network_by_id.return_value = network self.plugin.get_network_info.return_value = network with mock.patch.object(self.dhcp, 'disable_dhcp_helper') as disable: self.dhcp.refresh_dhcp_helper(network.id) disable.assert_called_once_with(network.id) self.assertFalse(self.cache.called) self.assertFalse(self.call_driver.called) self.cache.assert_has_calls( [mock.call.get_network_by_id('net-id')]) def test_refresh_dhcp_helper_exception_during_rpc(self): network = dhcp.NetModel(dict(id='net-id', tenant_id=FAKE_TENANT_ID, admin_state_up=True, subnets=[], ports=[])) self.cache.get_network_by_id.return_value = network self.plugin.get_network_info.side_effect = Exception with mock.patch.object(dhcp_agent.LOG, 'exception') as log: self.dhcp.refresh_dhcp_helper(network.id) self.assertFalse(self.call_driver.called) self.cache.assert_has_calls( [mock.call.get_network_by_id('net-id')]) self.assertTrue(log.called) self.assertTrue(self.dhcp.schedule_resync.called) def test_subnet_create_end(self): # We should call reload_allocations when subnet's enable_dhcp # attribute isn't True. payload = dict(subnet=dhcp.DictModel( dict(network_id=fake_network.id, enable_dhcp=False, cidr='99.99.99.0/24', ip_version=4)), priority=FAKE_PRIORITY) self.cache.get_network_by_id.return_value = fake_network new_net = copy.deepcopy(fake_network) new_net.subnets.append(payload['subnet']) self.plugin.get_network_info.return_value = new_net self.dhcp.subnet_create_end(None, payload) self.dhcp._process_resource_update() self.cache.assert_has_calls([mock.call.put(new_net)]) self.call_driver.assert_called_once_with('reload_allocations', new_net) # We should call restart when subnet's enable_dhcp attribute is True. self.call_driver.reset_mock() payload = dict(subnet=dhcp.DictModel( dict(network_id=fake_network.id, enable_dhcp=True, cidr='99.99.88.0/24', ip_version=4)), priority=FAKE_PRIORITY) new_net = copy.deepcopy(fake_network) new_net.subnets.append(payload['subnet']) self.plugin.get_network_info.return_value = new_net self.dhcp.subnet_create_end(None, payload) self.dhcp._process_resource_update() self.cache.assert_has_calls([mock.call.put(new_net)]) self.call_driver.assert_called_once_with('restart', new_net) def test_subnet_update_end(self): payload = dict(subnet=dict(network_id=fake_network.id), priority=FAKE_PRIORITY) self.cache.get_network_by_id.return_value = fake_network self.plugin.get_network_info.return_value = fake_network self.dhcp.subnet_update_end(None, payload) self.dhcp._process_resource_update() self.cache.assert_has_calls([mock.call.put(fake_network)]) self.call_driver.assert_called_once_with('reload_allocations', fake_network) # ensure all ports flagged as ready self.assertEqual({p.id for p in fake_network.ports}, self.dhcp.dhcp_ready_ports) def test_subnet_update_dhcp(self): payload = dict(subnet=dict(network_id=fake_network.id), priority=FAKE_PRIORITY) self.cache.get_network_by_id.return_value = fake_network new_net = copy.deepcopy(fake_network) new_subnet1 = copy.deepcopy(fake_subnet1) new_subnet2 = copy.deepcopy(fake_subnet2) new_subnet2.enable_dhcp = True new_net.subnets = [new_subnet1, new_subnet2] self.plugin.get_network_info.return_value = new_net self.dhcp.subnet_update_end(None, payload) self.dhcp._process_resource_update() self.call_driver.assert_called_once_with('restart', new_net) self.call_driver.reset_mock() self.cache.get_network_by_id.return_value = new_net new_net2 = copy.deepcopy(new_net) new_subnet1 = copy.deepcopy(new_subnet1) new_subnet1.enable_dhcp = False new_subnet2 = copy.deepcopy(new_subnet2) new_net2.subnets = [new_subnet1, new_subnet2] self.plugin.get_network_info.return_value = new_net2 self.dhcp.subnet_update_end(None, payload) self.dhcp._process_resource_update() self.call_driver.assert_called_once_with('restart', new_net2) def test_subnet_update_end_restart(self): new_state = dhcp.NetModel(dict(id=fake_network.id, tenant_id=fake_network.tenant_id, admin_state_up=True, subnets=[fake_subnet1, fake_subnet3], ports=[fake_port1])) payload = dict(subnet=dict(network_id=fake_network.id), priority=FAKE_PRIORITY) self.cache.get_network_by_id.return_value = fake_network self.plugin.get_network_info.return_value = new_state self.dhcp.subnet_update_end(None, payload) self.dhcp._process_resource_update() self.cache.assert_has_calls([mock.call.put(new_state)]) self.call_driver.assert_called_once_with('restart', new_state) def test_subnet_delete_end_no_network_id(self): prev_state = dhcp.NetModel(dict(id=fake_network.id, tenant_id=fake_network.tenant_id, admin_state_up=True, subnets=[fake_subnet1, fake_subnet3], ports=[fake_port1])) payload = dict(subnet_id=fake_subnet1.id, priority=FAKE_PRIORITY) self.cache.get_network_by_subnet_id.return_value = prev_state self.cache.get_network_by_id.return_value = prev_state self.plugin.get_network_info.return_value = fake_network self.dhcp.subnet_delete_end(None, payload) self.dhcp._process_resource_update() self.cache.assert_has_calls([ mock.call.get_network_by_subnet_id( 'bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbb'), mock.call.get_network_by_subnet_id( 'bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbb'), mock.call.get_network_by_id('12345678-1234-5678-1234567890ab'), mock.call.put(fake_network)]) self.call_driver.assert_called_once_with('restart', fake_network) def test_subnet_update_end_delete_payload(self): prev_state = dhcp.NetModel(dict(id=fake_network.id, tenant_id=fake_network.tenant_id, admin_state_up=True, subnets=[fake_subnet1, fake_subnet3], ports=[fake_port1])) payload = dict(subnet_id=fake_subnet1.id, network_id=fake_network.id, priority=FAKE_PRIORITY) self.cache.get_network_by_subnet_id.return_value = prev_state self.cache.get_network_by_id.return_value = prev_state self.plugin.get_network_info.return_value = fake_network self.dhcp.subnet_delete_end(None, payload) self.dhcp._process_resource_update() self.cache.assert_has_calls([ mock.call.get_network_by_subnet_id( 'bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbb'), mock.call.get_network_by_id(FAKE_NETWORK_UUID), mock.call.put(fake_network)]) self.call_driver.assert_called_once_with('restart', fake_network) def test_port_update_end(self): self.reload_allocations_p = mock.patch.object(self.dhcp, 'reload_allocations') self.reload_allocations = self.reload_allocations_p.start() payload = dict(port=copy.deepcopy(fake_port2)) self.cache.get_network_by_id.return_value = fake_network self.dhcp.port_update_end(None, payload) self.dhcp._process_resource_update() self.reload_allocations.assert_called_once_with(fake_port2, fake_network) def test_reload_allocations(self): self.cache.get_port_by_id.return_value = fake_port2 with mock.patch.object( self.dhcp, 'update_isolated_metadata_proxy') as ump: self.dhcp.reload_allocations(fake_port2, fake_network) self.cache.assert_has_calls([mock.call.put_port(mock.ANY)]) self.call_driver.assert_called_once_with('reload_allocations', fake_network) self.assertTrue(ump.called) def test_port_create_end(self): self.reload_allocations_p = mock.patch.object(self.dhcp, 'reload_allocations') self.reload_allocations = self.reload_allocations_p.start() payload = dict(port=copy.deepcopy(fake_port2)) self.cache.get_network_by_id.return_value = fake_network self.dhcp.port_create_end(None, payload) self.dhcp._process_resource_update() self.reload_allocations.assert_called_once_with(fake_port2, fake_network) def test_port_create_end_no_resync_if_same_port_already_in_cache(self): self.reload_allocations_p = mock.patch.object(self.dhcp, 'reload_allocations') self.reload_allocations = self.reload_allocations_p.start() payload = dict(port=copy.deepcopy(fake_port2)) cached_port = copy.deepcopy(fake_port2) new_fake_network = copy.deepcopy(fake_network) new_fake_network.ports = [cached_port] self.cache.get_network_by_id.return_value = new_fake_network self.dhcp.port_create_end(None, payload) self.dhcp._process_resource_update() self.reload_allocations.assert_called_once_with(fake_port2, new_fake_network) self.schedule_resync.assert_not_called() def test_port_update_change_ip_on_port(self): payload = dict(port=fake_port1, priority=FAKE_PRIORITY) self.cache.get_network_by_id.return_value = fake_network updated_fake_port1 = copy.deepcopy(fake_port1) updated_fake_port1.fixed_ips[0].ip_address = '172.9.9.99' self.cache.get_port_by_id.return_value = updated_fake_port1 with mock.patch.object( self.dhcp, 'update_isolated_metadata_proxy') as ump: self.dhcp.port_update_end(None, payload) self.dhcp._process_resource_update() self.cache.assert_has_calls( [mock.call.get_network_by_id(fake_port1.network_id), mock.call.put_port(mock.ANY)]) self.call_driver.assert_has_calls( [mock.call.call_driver('reload_allocations', fake_network)]) self.assertTrue(ump.called) def test_port_update_change_subnet_on_dhcp_agents_port(self): self.cache.get_network_by_id.return_value = fake_network self.cache.get_port_by_id.return_value = fake_port1 payload = dict(port=copy.deepcopy(fake_port1), priority=FAKE_PRIORITY) device_id = utils.get_dhcp_agent_device_id( payload['port']['network_id'], self.dhcp.conf.host) payload['port']['fixed_ips'][0]['subnet_id'] = '77777-7777' payload['port']['device_id'] = device_id self.dhcp.port_update_end(None, payload) self.dhcp._process_resource_update() self.assertFalse(self.call_driver.called) def test_port_update_change_ip_on_dhcp_agents_port(self): self.cache.get_network_by_id.return_value = fake_network self.cache.get_port_by_id.return_value = fake_port1 payload = dict(port=copy.deepcopy(fake_port1), priority=FAKE_PRIORITY) device_id = utils.get_dhcp_agent_device_id( payload['port']['network_id'], self.dhcp.conf.host) payload['port']['fixed_ips'][0]['ip_address'] = '172.9.9.99' payload['port']['device_id'] = device_id self.dhcp.port_update_end(None, payload) self.dhcp._process_resource_update() self.call_driver.assert_has_calls( [mock.call.call_driver('restart', fake_network)]) def test_port_update_change_ip_on_dhcp_agents_port_cache_miss(self): self.cache.get_network_by_id.return_value = fake_network self.cache.get_port_by_id.return_value = None payload = dict(port=copy.deepcopy(fake_port1), priority=FAKE_PRIORITY) device_id = utils.get_dhcp_agent_device_id( payload['port']['network_id'], self.dhcp.conf.host) payload['port']['fixed_ips'][0]['ip_address'] = '172.9.9.99' payload['port']['device_id'] = device_id self.dhcp.port_update_end(None, payload) self.dhcp._process_resource_update() self.schedule_resync.assert_called_once_with(mock.ANY, fake_port1.network_id) def test_port_create_duplicate_ip_on_dhcp_agents_same_network(self): self.cache.get_network_by_id.return_value = fake_network payload = dict(port=copy.deepcopy(fake_port2)) duplicate_ip = fake_port1['fixed_ips'][0]['ip_address'] payload['port']['fixed_ips'][0]['ip_address'] = duplicate_ip self.dhcp.port_create_end(None, payload) self.dhcp._process_resource_update() self.schedule_resync.assert_called_once_with(mock.ANY, fake_port2.network_id) def test_port_update_on_dhcp_agents_port_no_ip_change(self): self.cache.get_network_by_id.return_value = fake_network self.cache.get_port_by_id.return_value = fake_port1 payload = dict(port=fake_port1, priority=FAKE_PRIORITY) device_id = utils.get_dhcp_agent_device_id( payload['port']['network_id'], self.dhcp.conf.host) payload['port']['device_id'] = device_id self.dhcp.port_update_end(None, payload) self.dhcp._process_resource_update() self.call_driver.assert_has_calls( [mock.call.call_driver('reload_allocations', fake_network)]) def test_port_delete_end_no_network_id(self): payload = dict(port_id=fake_port2.id, priority=FAKE_PRIORITY) self.cache.get_network_by_id.return_value = fake_network self.cache.get_port_by_id.return_value = fake_port2 with mock.patch.object( self.dhcp, 'update_isolated_metadata_proxy') as ump: self.dhcp.port_delete_end(None, payload) self.dhcp._process_resource_update() self.cache.assert_has_calls( [mock.call.get_port_by_id(fake_port2.id), mock.call.get_port_by_id(fake_port2.id), mock.call.deleted_ports.add(fake_port2.id), mock.call.get_network_by_id(fake_network.id), mock.call.remove_port(fake_port2)]) self.call_driver.assert_has_calls( [mock.call.call_driver('reload_allocations', fake_network)]) self.assertTrue(ump.called) def test_port_delete_end(self): payload = dict(port_id=fake_port2.id, network_id=fake_network.id, priority=FAKE_PRIORITY) self.cache.get_network_by_id.return_value = fake_network self.cache.get_port_by_id.return_value = fake_port2 with mock.patch.object( self.dhcp, 'update_isolated_metadata_proxy') as ump: self.dhcp.port_delete_end(None, payload) self.dhcp._process_resource_update() self.cache.assert_has_calls( [mock.call.get_port_by_id(fake_port2.id), mock.call.deleted_ports.add(fake_port2.id), mock.call.get_network_by_id(fake_network.id), mock.call.remove_port(fake_port2)]) self.call_driver.assert_has_calls( [mock.call.call_driver('reload_allocations', fake_network)]) self.assertTrue(ump.called) def test_port_delete_end_unknown_port(self): payload = dict(port_id='unknown', network_id='unknown', priority=FAKE_PRIORITY) self.cache.get_port_by_id.return_value = None self.dhcp.port_delete_end(None, payload) self.dhcp._process_resource_update() self.cache.assert_has_calls([mock.call.get_port_by_id('unknown')]) self.assertEqual(self.call_driver.call_count, 0) def test_port_delete_end_agents_port(self): port = dhcp.DictModel(copy.deepcopy(fake_port1)) device_id = utils.get_dhcp_agent_device_id( port.network_id, self.dhcp.conf.host) port['device_id'] = device_id self.cache.get_network_by_id.return_value = fake_network self.cache.get_port_by_id.return_value = port self.dhcp.port_delete_end(None, {'port_id': port.id, 'network_id': fake_network.id, 'priority': FAKE_PRIORITY}) self.dhcp._process_resource_update() self.call_driver.assert_has_calls( [mock.call.call_driver('disable', fake_network)]) class TestDhcpPluginApiProxy(base.BaseTestCase): def _test_dhcp_api(self, method, **kwargs): proxy = dhcp_agent.DhcpPluginApi('foo', host='foo') with mock.patch.object(proxy.client, 'call') as rpc_mock,\ mock.patch.object(proxy.client, 'prepare') as prepare_mock: prepare_mock.return_value = proxy.client rpc_mock.return_value = kwargs.pop('return_value', []) prepare_args = {} if 'version' in kwargs: prepare_args['version'] = kwargs.pop('version') retval = getattr(proxy, method)(**kwargs) self.assertEqual(retval, rpc_mock.return_value) prepare_mock.assert_called_once_with(**prepare_args) kwargs['host'] = proxy.host rpc_mock.assert_called_once_with(mock.ANY, method, **kwargs) def test_get_active_networks_info(self): self._test_dhcp_api('get_active_networks_info', version='1.1') def test_get_network_info(self): self._test_dhcp_api('get_network_info', network_id='fake_id', return_value=None) def test_create_dhcp_port(self): self._test_dhcp_api('create_dhcp_port', port='fake_port', return_value=None, version='1.1') def test_update_dhcp_port(self): self._test_dhcp_api('update_dhcp_port', port_id='fake_id', port='fake_port', return_value=None, version='1.1') def test_release_dhcp_port(self): self._test_dhcp_api('release_dhcp_port', network_id='fake_id', device_id='fake_id_2') class TestNetworkCache(base.BaseTestCase): def test_update_of_deleted_port_ignored(self): nc = dhcp_agent.NetworkCache() nc.put(fake_network) nc.deleted_ports.add(fake_port2['id']) self.assertTrue(nc.is_port_message_stale(fake_port2)) def test_stale_update_ignored(self): nc = dhcp_agent.NetworkCache() nc.put(fake_network) nc.put_port(fake_port2) stale = copy.copy(fake_port2) stale['revision_number'] = 2 self.assertTrue(nc.is_port_message_stale(stale)) def test_put_network(self): nc = dhcp_agent.NetworkCache() nc.put(fake_network) self.assertEqual(nc.cache, {fake_network.id: fake_network}) self.assertEqual(nc.subnet_lookup, {fake_subnet1.id: fake_network.id, fake_subnet2.id: fake_network.id}) self.assertEqual(nc.port_lookup, {fake_port1.id: fake_network.id}) def test_put_network_existing(self): prev_network_info = mock.Mock() nc = dhcp_agent.NetworkCache() with mock.patch.object(nc, 'remove') as remove: nc.cache[fake_network.id] = prev_network_info nc.put(fake_network) remove.assert_called_once_with(prev_network_info) self.assertEqual(nc.cache, {fake_network.id: fake_network}) self.assertEqual(nc.subnet_lookup, {fake_subnet1.id: fake_network.id, fake_subnet2.id: fake_network.id}) self.assertEqual(nc.port_lookup, {fake_port1.id: fake_network.id}) def test_remove_network(self): nc = dhcp_agent.NetworkCache() nc.cache = {fake_network.id: fake_network} nc.subnet_lookup = {fake_subnet1.id: fake_network.id, fake_subnet2.id: fake_network.id} nc.port_lookup = {fake_port1.id: fake_network.id} nc.remove(fake_network) self.assertEqual(0, len(nc.cache)) self.assertEqual(0, len(nc.subnet_lookup)) self.assertEqual(0, len(nc.port_lookup)) def test_get_network_by_id(self): nc = dhcp_agent.NetworkCache() nc.put(fake_network) self.assertEqual(nc.get_network_by_id(fake_network.id), fake_network) def test_get_network_ids(self): nc = dhcp_agent.NetworkCache() nc.put(fake_network) self.assertEqual(list(nc.get_network_ids()), [fake_network.id]) def test_get_network_by_subnet_id(self): nc = dhcp_agent.NetworkCache() nc.put(fake_network) self.assertEqual(nc.get_network_by_subnet_id(fake_subnet1.id), fake_network) def test_get_network_by_port_id(self): nc = dhcp_agent.NetworkCache() nc.put(fake_network) self.assertEqual(nc.get_network_by_port_id(fake_port1.id), fake_network) def test_get_port_ids(self): fake_net = dhcp.NetModel( dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, subnets=[fake_subnet1], ports=[fake_port1])) nc = dhcp_agent.NetworkCache() nc.put(fake_net) nc.put_port(fake_port2) self.assertEqual(set([fake_port1['id'], fake_port2['id']]), set(nc.get_port_ids())) def test_get_port_ids_limited_nets(self): fake_net = dhcp.NetModel( dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, subnets=[fake_subnet1], ports=[fake_port1])) fake_port2 = copy.deepcopy(fake_port1) fake_port2['id'] = 'fp2' fake_port2['network_id'] = '12345678-1234-5678-1234567890ac' fake_net2 = dhcp.NetModel( dict(id='12345678-1234-5678-1234567890ac', tenant_id=FAKE_TENANT_ID, subnets=[fake_subnet1], ports=[fake_port2])) nc = dhcp_agent.NetworkCache() nc.put(fake_net) nc.put(fake_net2) self.assertEqual(set([fake_port1['id']]), set(nc.get_port_ids([fake_net.id, 'net2']))) self.assertEqual(set(), set(nc.get_port_ids(['net2']))) self.assertEqual(set([fake_port2['id']]), set(nc.get_port_ids([fake_port2.network_id, 'net2']))) def test_put_port(self): fake_net = dhcp.NetModel( dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, subnets=[fake_subnet1], ports=[fake_port1])) nc = dhcp_agent.NetworkCache() nc.put(fake_net) nc.put_port(fake_port2) self.assertEqual(2, len(nc.port_lookup)) self.assertIn(fake_port2, fake_net.ports) def test_put_port_existing(self): fake_net = dhcp.NetModel( dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, subnets=[fake_subnet1], ports=[fake_port1, fake_port2])) nc = dhcp_agent.NetworkCache() nc.put(fake_net) nc.put_port(fake_port2) self.assertEqual(2, len(nc.port_lookup)) self.assertIn(fake_port2, fake_net.ports) def test_remove_port_existing(self): fake_net = dhcp.NetModel( dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID, subnets=[fake_subnet1], ports=[fake_port1, fake_port2])) nc = dhcp_agent.NetworkCache() nc.put(fake_net) nc.remove_port(fake_port2) self.assertEqual(1, len(nc.port_lookup)) self.assertNotIn(fake_port2, fake_net.ports) def test_get_port_by_id(self): nc = dhcp_agent.NetworkCache() nc.put(fake_network) self.assertEqual(nc.get_port_by_id(fake_port1.id), fake_port1) class FakePort1(object): def __init__(self): self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' class FakePort2(object): def __init__(self): self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' class FakeV4Subnet(object): def __init__(self): self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' self.ip_version = 4 self.cidr = '192.168.0.0/24' self.gateway_ip = '192.168.0.1' self.enable_dhcp = True class FakeV6Subnet(object): def __init__(self): self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' self.ip_version = 6 self.cidr = '2001:db8:0:1::/64' self.gateway_ip = '2001:db8:0:1::1' self.enable_dhcp = True class FakeV4SubnetOutsideGateway(FakeV4Subnet): def __init__(self): super(FakeV4SubnetOutsideGateway, self).__init__() self.gateway_ip = '192.168.1.1' class FakeV6SubnetOutsideGateway(FakeV6Subnet): def __init__(self): super(FakeV6SubnetOutsideGateway, self).__init__() self.gateway_ip = '2001:db8:1:1::1' class FakeV4SubnetNoGateway(object): def __init__(self): self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' self.ip_version = 4 self.cidr = '192.168.1.0/24' self.gateway_ip = None self.enable_dhcp = True class FakeV6SubnetNoGateway(object): def __init__(self): self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' self.ip_version = 6 self.cidr = '2001:db8:1:0::/64' self.gateway_ip = None self.enable_dhcp = True class FakeV4Network(object): def __init__(self): self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' self.subnets = [FakeV4Subnet()] self.ports = [FakePort1()] self.namespace = 'qdhcp-aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' class FakeDualNetwork(object): def __init__(self): self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' self.subnets = [FakeV4Subnet(), FakeV6Subnet()] self.ports = [FakePort1(), FakePort2()] self.namespace = 'qdhcp-dddddddd-dddd-dddd-dddd-dddddddddddd' class FakeV4NetworkOutsideGateway(FakeV4Network): def __init__(self): super(FakeV4NetworkOutsideGateway, self).__init__() self.subnets = [FakeV4SubnetOutsideGateway()] class FakeDualNetworkOutsideGateway(FakeDualNetwork): def __init__(self): super(FakeDualNetworkOutsideGateway, self).__init__() self.subnets = [FakeV4SubnetOutsideGateway(), FakeV6SubnetOutsideGateway()] class FakeDualNetworkNoSubnet(object): def __init__(self): self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' self.subnets = [] self.ports = [] class FakeDualNetworkNoGateway(object): def __init__(self): self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' self.subnets = [FakeV4SubnetNoGateway(), FakeV6SubnetNoGateway()] self.ports = [FakePort1(), FakePort2()] class TestDeviceManager(base.BaseTestCase): def setUp(self): super(TestDeviceManager, self).setUp() config.register_interface_driver_opts_helper(cfg.CONF) cfg.CONF.register_opts(dhcp_config.DHCP_AGENT_OPTS) cfg.CONF.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') cfg.CONF.set_override('enable_isolated_metadata', True) self.ensure_device_is_ready_p = mock.patch( 'neutron.agent.linux.ip_lib.ensure_device_is_ready') self.ensure_device_is_ready = (self.ensure_device_is_ready_p.start()) self.dvr_cls_p = mock.patch('neutron.agent.linux.interface.NullDriver') self.iproute_cls_p = mock.patch('neutron.agent.linux.' 'ip_lib.IpRouteCommand') driver_cls = self.dvr_cls_p.start() iproute_cls = self.iproute_cls_p.start() self.mock_driver = mock.MagicMock() self.mock_driver.DEV_NAME_LEN = ( interface.LinuxInterfaceDriver.DEV_NAME_LEN) self.mock_driver.use_gateway_ips = False self.mock_iproute = mock.MagicMock() driver_cls.return_value = self.mock_driver iproute_cls.return_value = self.mock_iproute iptables_cls_p = mock.patch( 'neutron.agent.linux.iptables_manager.IptablesManager') iptables_cls = iptables_cls_p.start() self.iptables_inst = mock.Mock() iptables_cls.return_value = self.iptables_inst self.mangle_inst_v4 = mock.Mock() self.iptables_inst.ipv4 = {'mangle': self.mangle_inst_v4} self.mangle_inst_v6 = mock.Mock() self.iptables_inst.ipv6 = {'mangle': self.mangle_inst_v6} self.mock_ip_wrapper_p = mock.patch("neutron.agent.linux.ip_lib." "IPWrapper") self.mock_ip_wrapper = self.mock_ip_wrapper_p.start() self.mock_ipv6_enabled_p = mock.patch('neutron.common.ipv6_utils.' 'is_enabled_and_bind_by_default') self.mock_ipv6_enabled = self.mock_ipv6_enabled_p.start() self.mock_ipv6_enabled.return_value = True def _test_setup_helper(self, device_is_ready, ipv6_enabled=True, net=None, port=None): net = net or fake_network port = port or fake_port1 plugin = mock.Mock() plugin.create_dhcp_port.return_value = port or fake_port1 self.ensure_device_is_ready.return_value = device_is_ready self.mock_driver.get_device_name.return_value = 'tap12345678-12' dh = dhcp.DeviceManager(cfg.CONF, plugin) dh._set_default_route = mock.Mock() dh._cleanup_stale_devices = mock.Mock() interface_name = dh.setup(net) self.assertEqual('tap12345678-12', interface_name) plugin.assert_has_calls([ mock.call.create_dhcp_port( {'port': {'name': '', 'admin_state_up': True, 'network_id': net.id, 'tenant_id': net.tenant_id, 'fixed_ips': [{'subnet_id': port.fixed_ips[0].subnet_id}], 'device_id': mock.ANY}})]) if port == fake_ipv6_port: expected_ips = ['2001:db8::a8bb:ccff:fedd:ee99/64', '169.254.169.254/16'] else: expected_ips = ['172.9.9.9/24', '169.254.169.254/16'] expected = [mock.call.get_device_name(port)] if ipv6_enabled: expected.append( mock.call.configure_ipv6_ra(net.namespace, 'default', 0)) if not device_is_ready: expected.append(mock.call.plug(net.id, port.id, 'tap12345678-12', 'aa:bb:cc:dd:ee:ff', namespace=net.namespace, mtu=None)) expected.append(mock.call.init_l3( 'tap12345678-12', expected_ips, namespace=net.namespace)) self.mock_driver.assert_has_calls(expected) dh._set_default_route.assert_called_once_with(net, 'tap12345678-12') def test_setup(self): cfg.CONF.set_override('enable_metadata_network', False) self._test_setup_helper(False) cfg.CONF.set_override('enable_metadata_network', True) self._test_setup_helper(False) def test_setup_without_ipv6_enabled(self): # NOTE(mjozefcz): This test checks if IPv6 RA is *not* # configured when host doesn't support IPv6. self.mock_ipv6_enabled.return_value = False self._test_setup_helper(False, ipv6_enabled=False) def test_setup_calls_fill_dhcp_udp_checksums_v4(self): self._test_setup_helper(False) rule = ('-p udp -m udp --dport %d -j CHECKSUM --checksum-fill' % const.DHCP_RESPONSE_PORT) expected = [mock.call.add_rule('POSTROUTING', rule)] self.mangle_inst_v4.assert_has_calls(expected) def test_setup_calls_fill_dhcp_udp_checksums_v6(self): self._test_setup_helper(False) rule = ('-p udp -m udp --dport %d -j CHECKSUM --checksum-fill' % n_const.DHCPV6_CLIENT_PORT) expected = [mock.call.add_rule('POSTROUTING', rule)] self.mangle_inst_v6.assert_has_calls(expected) def test_setup_dhcp_port_doesnt_orphan_devices(self): with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: plugin = mock.Mock() device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.return_value = None net = copy.deepcopy(fake_network) plugin.create_dhcp_port.side_effect = exceptions.Conflict() dh = dhcp.DeviceManager(cfg.CONF, plugin) clean = mock.patch.object(dh, '_cleanup_stale_devices').start() with testtools.ExpectedException(exceptions.Conflict): dh.setup(net) clean.assert_called_once_with(net, dhcp_port=None) def test_setup_create_dhcp_port(self): with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: plugin = mock.Mock() device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.return_value = None net = copy.deepcopy(fake_network) plugin.create_dhcp_port.return_value = fake_dhcp_port dh = dhcp.DeviceManager(cfg.CONF, plugin) dh.setup(net) plugin.assert_has_calls([ mock.call.create_dhcp_port( {'port': {'name': '', 'admin_state_up': True, 'network_id': net.id, 'tenant_id': net.tenant_id, 'fixed_ips': [{'subnet_id': fake_dhcp_port.fixed_ips[0].subnet_id}], 'device_id': mock.ANY}})]) self.assertIn(fake_dhcp_port, net.ports) def test_setup_plug_exception(self): plugin = mock.Mock() plugin.create_dhcp_port.return_value = fake_dhcp_port self.ensure_device_is_ready.return_value = False self.mock_driver.get_device_name.return_value = 'tap12345678-12' dh = dhcp.DeviceManager(cfg.CONF, plugin) dh._set_default_route = mock.Mock() dh._cleanup_stale_devices = mock.Mock() dh.driver = mock.Mock() dh.driver.plug.side_effect = OSError() net = copy.deepcopy(fake_network) self.assertRaises(OSError, dh.setup, net) dh.driver.unplug.assert_called_once_with(mock.ANY, namespace=net.namespace) plugin.release_dhcp_port.assert_called_once_with( net.id, mock.ANY) def test_setup_ipv6(self): self._test_setup_helper(True, net=fake_network_ipv6, port=fake_ipv6_port) def test_setup_device_is_ready(self): self._test_setup_helper(True) def test_create_dhcp_port_raise_conflict(self): plugin = mock.Mock() dh = dhcp.DeviceManager(cfg.CONF, plugin) plugin.create_dhcp_port.return_value = None self.assertRaises(exceptions.Conflict, dh.setup_dhcp_port, fake_network) def test_create_dhcp_port_create_new(self): plugin = mock.Mock() dh = dhcp.DeviceManager(cfg.CONF, plugin) plugin.create_dhcp_port.return_value = fake_network.ports[0] dh.setup_dhcp_port(fake_network) plugin.assert_has_calls([ mock.call.create_dhcp_port( {'port': {'name': '', 'admin_state_up': True, 'network_id': fake_network.id, 'tenant_id': fake_network.tenant_id, 'fixed_ips': [{'subnet_id': fake_fixed_ip1.subnet_id}], 'device_id': mock.ANY}})]) def test_create_dhcp_port_update_add_subnet_bug_1627480(self): # this can go away once bug/1627480 is fixed plugin = mock.Mock() dh = dhcp.DeviceManager(cfg.CONF, plugin) fake_network_copy = copy.deepcopy(fake_network) fake_network_copy.ports[0].device_id = dh.get_device_id(fake_network) fake_network_copy.subnets[1].enable_dhcp = True plugin.update_dhcp_port.return_value = fake_network.ports[0] with testtools.ExpectedException(exceptions.SubnetMismatchForPort): dh.setup_dhcp_port(fake_network_copy) def test_create_dhcp_port_update_add_subnet(self): plugin = mock.Mock() dh = dhcp.DeviceManager(cfg.CONF, plugin) fake_network_copy = copy.deepcopy(fake_network) fake_network_copy.ports[0].device_id = dh.get_device_id(fake_network) fake_network_copy.subnets[1].enable_dhcp = True updated_port = copy.deepcopy(fake_network_copy.ports[0]) updated_port.fixed_ips.append(fake_fixed_ip_subnet2) plugin.update_dhcp_port.return_value = updated_port dh.setup_dhcp_port(fake_network_copy) port_body = {'port': { 'network_id': fake_network.id, 'fixed_ips': [{'subnet_id': fake_fixed_ip1.subnet_id, 'ip_address': fake_fixed_ip1.ip_address}, {'subnet_id': fake_subnet2.id}]}} plugin.assert_has_calls([ mock.call.update_dhcp_port(fake_network_copy.ports[0].id, port_body)]) def test_update_dhcp_port_raises_conflict(self): plugin = mock.Mock() dh = dhcp.DeviceManager(cfg.CONF, plugin) fake_network_copy = copy.deepcopy(fake_network) fake_network_copy.ports[0].device_id = dh.get_device_id(fake_network) fake_network_copy.subnets[1].enable_dhcp = True plugin.update_dhcp_port.return_value = None self.assertRaises(exceptions.Conflict, dh.setup_dhcp_port, fake_network_copy) def test_create_dhcp_port_no_update_or_create(self): plugin = mock.Mock() dh = dhcp.DeviceManager(cfg.CONF, plugin) fake_network_copy = copy.deepcopy(fake_network) fake_network_copy.ports[0].device_id = dh.get_device_id(fake_network) dh.setup_dhcp_port(fake_network_copy) self.assertFalse(plugin.setup_dhcp_port.called) self.assertFalse(plugin.update_dhcp_port.called) def test_setup_dhcp_port_with_non_enable_dhcp_subnet(self): plugin = mock.Mock() dh = dhcp.DeviceManager(cfg.CONF, plugin) fake_network_copy = copy.deepcopy(fake_network) fake_network_copy.ports[0].device_id = dh.get_device_id(fake_network) plugin.update_dhcp_port.return_value = fake_port1 self.assertEqual(fake_subnet1.id, dh.setup_dhcp_port(fake_network_copy).fixed_ips[0].subnet_id) def test_destroy(self): fake_net = dhcp.NetModel( dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID)) with mock.patch('neutron.agent.linux.interface.NullDriver') as dvr_cls: mock_driver = mock.MagicMock() mock_driver.get_device_name.return_value = 'tap12345678-12' dvr_cls.return_value = mock_driver plugin = mock.Mock() dh = dhcp.DeviceManager(cfg.CONF, plugin) dh.destroy(fake_net, 'tap12345678-12') dvr_cls.assert_called_once_with(cfg.CONF) mock_driver.assert_has_calls( [mock.call.unplug('tap12345678-12', namespace='qdhcp-' + fake_net.id)]) plugin.assert_has_calls( [mock.call.release_dhcp_port(fake_net.id, mock.ANY)]) def test_destroy_with_none(self): fake_net = dhcp.NetModel( dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID)) with mock.patch('neutron.agent.linux.interface.NullDriver') as dvr_cls: mock_driver = mock.MagicMock() mock_driver.get_device_name.return_value = 'tap12345678-12' dvr_cls.return_value = mock_driver plugin = mock.Mock() dh = dhcp.DeviceManager(cfg.CONF, plugin) dh.destroy(fake_net, None) dvr_cls.assert_called_once_with(cfg.CONF) plugin.assert_has_calls( [mock.call.release_dhcp_port(fake_net.id, mock.ANY)]) self.assertFalse(mock_driver.called) def test_get_interface_name(self): fake_net = dhcp.NetModel( dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID)) fake_port = dhcp.DictModel( dict(id='12345678-1234-aaaa-1234567890ab', mac_address='aa:bb:cc:dd:ee:ff')) with mock.patch('neutron.agent.linux.interface.NullDriver') as dvr_cls: mock_driver = mock.MagicMock() mock_driver.get_device_name.return_value = 'tap12345678-12' dvr_cls.return_value = mock_driver plugin = mock.Mock() dh = dhcp.DeviceManager(cfg.CONF, plugin) dh.get_interface_name(fake_net, fake_port) dvr_cls.assert_called_once_with(cfg.CONF) mock_driver.assert_has_calls( [mock.call.get_device_name(fake_port)]) self.assertEqual(0, len(plugin.mock_calls)) def test_get_device_id(self): fake_net = dhcp.NetModel( dict(id=FAKE_NETWORK_UUID, tenant_id=FAKE_TENANT_ID)) expected = ('dhcp1ae5f96c-c527-5079-82ea-371a01645457-12345678-1234-' '5678-1234567890ab') # the DHCP port name only contains the hostname and not the domain name local_hostname = cfg.CONF.host.split('.')[0] with mock.patch('uuid.uuid5') as uuid5: uuid5.return_value = '1ae5f96c-c527-5079-82ea-371a01645457' dh = dhcp.DeviceManager(cfg.CONF, None) self.assertEqual(expected, dh.get_device_id(fake_net)) uuid5.assert_called_once_with(uuid.NAMESPACE_DNS, local_hostname) def test_update(self): # Try with namespaces and no metadata network cfg.CONF.set_override('enable_metadata_network', False) dh = dhcp.DeviceManager(cfg.CONF, None) dh._set_default_route = mock.Mock() network = mock.Mock() dh.update(network, 'ns-12345678-12') dh._set_default_route.assert_called_once_with(network, 'ns-12345678-12') # Meta data network enabled, don't interfere with its gateway. cfg.CONF.set_override('enable_metadata_network', True) dh = dhcp.DeviceManager(cfg.CONF, None) dh._set_default_route = mock.Mock() dh.update(FakeV4Network(), 'ns-12345678-12') self.assertTrue(dh._set_default_route.called) def test_set_default_route(self): dh = dhcp.DeviceManager(cfg.CONF, None) with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.return_value = None # Basic one subnet with gateway. network = FakeV4Network() dh._set_default_route(network, 'tap-name') self.assertEqual(2, device.route.get_gateway.call_count) self.assertFalse(device.route.delete_gateway.called) device.route.add_gateway.assert_called_once_with('192.168.0.1') def test_set_default_route_outside_subnet(self): dh = dhcp.DeviceManager(cfg.CONF, None) with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.return_value = None # Basic one subnet with gateway outside the subnet. network = FakeV4NetworkOutsideGateway() dh._set_default_route(network, 'tap-name') self.assertEqual(2, device.route.get_gateway.call_count) self.assertFalse(device.route.delete_gateway.called) device.route.add_route.assert_called_once_with('192.168.1.1', scope='link') device.route.add_gateway.assert_called_once_with('192.168.1.1') def test_set_default_route_no_subnet(self): dh = dhcp.DeviceManager(cfg.CONF, None) with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.return_value = None network = FakeDualNetworkNoSubnet() network.namespace = 'qdhcp-1234' dh._set_default_route(network, 'tap-name') self.assertEqual(2, device.route.get_gateway.call_count) self.assertFalse(device.route.delete_gateway.called) self.assertFalse(device.route.add_gateway.called) def test_set_default_route_no_subnet_delete_gateway(self): dh = dhcp.DeviceManager(cfg.CONF, None) v4_gateway = '192.168.0.1' v6_gateway = '2001:db8:0:1::1' expected = [mock.call(v4_gateway), mock.call(v6_gateway)] with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.side_effect = [ dict(gateway=v4_gateway), dict(gateway=v6_gateway)] network = FakeDualNetworkNoSubnet() network.namespace = 'qdhcp-1234' dh._set_default_route(network, 'tap-name') self.assertEqual(2, device.route.get_gateway.call_count) self.assertEqual(2, device.route.delete_gateway.call_count) device.route.delete_gateway.assert_has_calls(expected) self.assertFalse(device.route.add_gateway.called) def test_set_default_route_no_gateway(self): dh = dhcp.DeviceManager(cfg.CONF, None) v4_gateway = '192.168.0.1' v6_gateway = '2001:db8:0:1::1' expected = [mock.call(v4_gateway), mock.call(v6_gateway)] with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.side_effect = [ dict(gateway=v4_gateway), dict(gateway=v6_gateway)] network = FakeDualNetworkNoGateway() network.namespace = 'qdhcp-1234' dh._set_default_route(network, 'tap-name') self.assertEqual(2, device.route.get_gateway.call_count) self.assertEqual(2, device.route.delete_gateway.call_count) device.route.delete_gateway.assert_has_calls(expected) self.assertFalse(device.route.add_gateway.called) def test_set_default_route_do_nothing(self): dh = dhcp.DeviceManager(cfg.CONF, None) v4_gateway = '192.168.0.1' v6_gateway = '2001:db8:0:1::1' with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.side_effect = [ dict(gateway=v4_gateway), dict(gateway=v6_gateway)] network = FakeDualNetwork() dh._set_default_route(network, 'tap-name') self.assertEqual(2, device.route.get_gateway.call_count) self.assertFalse(device.route.delete_gateway.called) self.assertFalse(device.route.add_gateway.called) def test_set_default_route_change_gateway(self): dh = dhcp.DeviceManager(cfg.CONF, None) v4_gateway = '192.168.0.1' old_v4_gateway = '192.168.0.2' v6_gateway = '2001:db8:0:1::1' old_v6_gateway = '2001:db8:0:1::2' expected = [mock.call(v4_gateway), mock.call(v6_gateway)] with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.side_effect = [ dict(gateway=old_v4_gateway), dict(gateway=old_v6_gateway)] network = FakeDualNetwork() dh._set_default_route(network, 'tap-name') self.assertEqual(2, device.route.get_gateway.call_count) self.assertFalse(device.route.delete_gateway.called) device.route.add_gateway.assert_has_calls(expected) def test_set_default_route_change_gateway_outside_subnet(self): dh = dhcp.DeviceManager(cfg.CONF, None) v4_gateway = '192.168.1.1' old_v4_gateway = '192.168.2.1' v6_gateway = '2001:db8:1:1::1' old_v6_gateway = '2001:db8:2:0::1' add_route_expected = [mock.call(v4_gateway, scope='link'), mock.call(v6_gateway, scope='link')] add_gw_expected = [mock.call(v4_gateway), mock.call(v6_gateway)] with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: device = mock.Mock() mock_IPDevice.return_value = device device.route.list_onlink_routes.side_effect = [ [{'cidr': old_v4_gateway}], []] device.route.get_gateway.side_effect = [ dict(gateway=old_v4_gateway), dict(gateway=old_v6_gateway)] network = FakeDualNetworkOutsideGateway() dh._set_default_route(network, 'tap-name') self.assertEqual(2, device.route.get_gateway.call_count) self.assertEqual(2, device.route.list_onlink_routes.call_count) self.assertFalse(device.route.delete_gateway.called) device.route.delete_route.assert_called_once_with(old_v4_gateway, scope='link') device.route.add_route.assert_has_calls(add_route_expected) device.route.add_gateway.assert_has_calls(add_gw_expected) def test_set_default_route_two_subnets(self): # Try two subnets. Should set gateway from the first. dh = dhcp.DeviceManager(cfg.CONF, None) v4_gateway = '192.168.1.1' v6_gateway = '2001:db8:1:1::1' expected = [mock.call(v4_gateway), mock.call(v6_gateway)] with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: device = mock.Mock() mock_IPDevice.return_value = device device.route.get_gateway.return_value = None network = FakeDualNetwork() subnet2 = FakeV4Subnet() subnet2.gateway_ip = v4_gateway subnet3 = FakeV6Subnet() subnet3.gateway_ip = v6_gateway network.subnets = [subnet2, FakeV4Subnet(), subnet3, FakeV6Subnet()] dh._set_default_route(network, 'tap-name') self.assertEqual(2, device.route.get_gateway.call_count) self.assertFalse(device.route.delete_gateway.called) device.route.add_gateway.assert_has_calls(expected) class TestDictModel(base.BaseTestCase): def test_basic_dict(self): d = dict(a=1, b=2) m = dhcp.DictModel(d) self.assertEqual(1, m.a) self.assertEqual(2, m.b) def test_dict_has_sub_dict(self): d = dict(a=dict(b=2)) m = dhcp.DictModel(d) self.assertEqual(2, m.a.b) def test_dict_contains_list(self): d = dict(a=[1, 2]) m = dhcp.DictModel(d) self.assertEqual([1, 2], m.a) def test_dict_contains_list_of_dicts(self): d = dict(a=[dict(b=2), dict(c=3)]) m = dhcp.DictModel(d) self.assertEqual(2, m.a[0].b) self.assertEqual(3, m.a[1].c) neutron-12.1.1/neutron/tests/unit/agent/dhcp/__init__.py0000664000175000017500000000000013553660046023246 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/common/0000775000175000017500000000000013553660156021523 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/common/test_utils.py0000664000175000017500000000704413553660047024300 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.agent.common import utils from neutron.agent.linux import interface from neutron.conf.agent import common as config from neutron.tests import base from neutron.tests.unit import testlib_api class TestLoadInterfaceDriver(base.BaseTestCase): def setUp(self): super(TestLoadInterfaceDriver, self).setUp() self.conf = config.setup_conf() config.register_interface_opts(self.conf) config.register_interface_driver_opts_helper(self.conf) def test_load_interface_driver_not_set(self): with testlib_api.ExpectedException(SystemExit): utils.load_interface_driver(self.conf) def test_load_interface_driver_wrong_driver(self): self.conf.set_override('interface_driver', 'neutron.NonExistentDriver') with testlib_api.ExpectedException(SystemExit): utils.load_interface_driver(self.conf) def test_load_interface_driver_does_not_consume_irrelevant_errors(self): self.conf.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') with mock.patch('oslo_utils.importutils.import_class', side_effect=RuntimeError()): with testlib_api.ExpectedException(RuntimeError): utils.load_interface_driver(self.conf) def test_load_interface_driver_success(self): self.conf.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') self.assertIsInstance(utils.load_interface_driver(self.conf), interface.NullDriver) def test_load_null_interface_driver_success(self): self.conf.set_override('interface_driver', 'null') self.assertIsInstance(utils.load_interface_driver(self.conf), interface.NullDriver) def test_load_ivs_interface_driver_success(self): self.conf.set_override('interface_driver', 'ivs') self.assertIsInstance(utils.load_interface_driver(self.conf), interface.IVSInterfaceDriver) def test_load_linuxbridge_interface_driver_success(self): self.conf.set_override('interface_driver', 'linuxbridge') self.assertIsInstance(utils.load_interface_driver(self.conf), interface.BridgeInterfaceDriver) def test_load_ovs_interface_driver_success(self): self.conf.set_override('interface_driver', 'openvswitch') self.assertIsInstance(utils.load_interface_driver(self.conf), interface.OVSInterfaceDriver) def test_load_interface_driver_as_alias_wrong_driver(self): self.conf.set_override('interface_driver', 'openvswitchXX') with testlib_api.ExpectedException(SystemExit): utils.load_interface_driver(self.conf) neutron-12.1.1/neutron/tests/unit/agent/common/test_resource_processing_queue.py0000664000175000017500000001033013553660046030416 0ustar zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import datetime from oslo_utils import uuidutils from neutron.agent.common import resource_processing_queue as queue from neutron.tests import base _uuid = uuidutils.generate_uuid FAKE_ID = _uuid() FAKE_ID_2 = _uuid() PRIORITY_RPC = 0 class TestExclusiveResourceProcessor(base.BaseTestCase): def test_i_am_master(self): master = queue.ExclusiveResourceProcessor(FAKE_ID) not_master = queue.ExclusiveResourceProcessor(FAKE_ID) master_2 = queue.ExclusiveResourceProcessor(FAKE_ID_2) not_master_2 = queue.ExclusiveResourceProcessor(FAKE_ID_2) self.assertTrue(master._i_am_master()) self.assertFalse(not_master._i_am_master()) self.assertTrue(master_2._i_am_master()) self.assertFalse(not_master_2._i_am_master()) master.__exit__(None, None, None) master_2.__exit__(None, None, None) def test_master(self): master = queue.ExclusiveResourceProcessor(FAKE_ID) not_master = queue.ExclusiveResourceProcessor(FAKE_ID) master_2 = queue.ExclusiveResourceProcessor(FAKE_ID_2) not_master_2 = queue.ExclusiveResourceProcessor(FAKE_ID_2) self.assertEqual(master, master._master) self.assertEqual(master, not_master._master) self.assertEqual(master_2, master_2._master) self.assertEqual(master_2, not_master_2._master) master.__exit__(None, None, None) master_2.__exit__(None, None, None) def test__enter__(self): self.assertNotIn(FAKE_ID, queue.ExclusiveResourceProcessor._masters) master = queue.ExclusiveResourceProcessor(FAKE_ID) master.__enter__() self.assertIn(FAKE_ID, queue.ExclusiveResourceProcessor._masters) master.__exit__(None, None, None) def test__exit__(self): master = queue.ExclusiveResourceProcessor(FAKE_ID) not_master = queue.ExclusiveResourceProcessor(FAKE_ID) master.__enter__() self.assertIn(FAKE_ID, queue.ExclusiveResourceProcessor._masters) not_master.__enter__() not_master.__exit__(None, None, None) self.assertIn(FAKE_ID, queue.ExclusiveResourceProcessor._masters) master.__exit__(None, None, None) self.assertNotIn(FAKE_ID, queue.ExclusiveResourceProcessor._masters) def test_data_fetched_since(self): master = queue.ExclusiveResourceProcessor(FAKE_ID) self.assertEqual(datetime.datetime.min, master._get_resource_data_timestamp()) ts1 = datetime.datetime.utcnow() - datetime.timedelta(seconds=10) ts2 = datetime.datetime.utcnow() master.fetched_and_processed(ts2) self.assertEqual(ts2, master._get_resource_data_timestamp()) master.fetched_and_processed(ts1) self.assertEqual(ts2, master._get_resource_data_timestamp()) master.__exit__(None, None, None) def test_updates(self): master = queue.ExclusiveResourceProcessor(FAKE_ID) not_master = queue.ExclusiveResourceProcessor(FAKE_ID) master.queue_update(queue.ResourceUpdate(FAKE_ID, 0)) not_master.queue_update(queue.ResourceUpdate(FAKE_ID, 0)) for update in not_master.updates(): raise Exception("Only the master should process a resource") self.assertEqual(2, len([i for i in master.updates()])) def test_hit_retry_limit(self): tries = 1 rpqueue = queue.ResourceProcessingQueue() update = queue.ResourceUpdate(FAKE_ID, PRIORITY_RPC, tries=tries) rpqueue.add(update) self.assertFalse(update.hit_retry_limit()) rpqueue.add(update) self.assertTrue(update.hit_retry_limit()) neutron-12.1.1/neutron/tests/unit/agent/common/__init__.py0000664000175000017500000000000013553660046023620 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/common/test_ovs_lib.py0000664000175000017500000014222713553660047024600 0ustar zuulzuul00000000000000# Copyright 2012, VMware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import mock from neutron_lib import constants from neutron_lib import exceptions from oslo_serialization import jsonutils from oslo_utils import uuidutils import tenacity import testtools from neutron.agent.common import ovs_lib from neutron.agent.common import utils from neutron.conf.agent import common as config from neutron.plugins.ml2.drivers.openvswitch.agent.common \ import constants as p_const from neutron.tests import base from neutron.tests import tools OVS_LINUX_KERN_VERS_WITHOUT_VXLAN = "3.12.0" # some test data for get_vif_port_to_ofport_map that exhibited bug 1444269 OVSLIST_WITH_UNSET_PORT = ( '{"data":[["patch-tun",["map",[]],1],["tap2ab72a72-44",["map",[["attached-' 'mac","fa:16:3e:b0:f8:38"],["iface-id","2ab72a72-4407-4ef3-806a-b2172f3e4d' 'c7"],["iface-status","active"]]],2],["tap6b108774-15",["map",[["attached-' 'mac","fa:16:3e:02:f5:91"],["iface-id","6b108774-1559-45e9-a7c3-b714f11722' 'cf"],["iface-status","active"]]],["set",[]]]],"headings":["name","externa' 'l_ids","ofport"]}') class OFCTLParamListMatcher(object): def _parse(self, params): actions_pos = params.find('actions') return set(params[:actions_pos].split(',')), params[actions_pos:] def __init__(self, params): self.expected = self._parse(params) def __eq__(self, other): return self.expected == self._parse(other) def __str__(self): return 'ovs-ofctl parameters: %s, "%s"' % self.expected __repr__ = __str__ class StringSetMatcher(object): """A helper object for unordered CSV strings Will compare equal if both strings, when read as a comma-separated set of values, represent the same set. Example: "a,b,45" == "b,45,a" """ def __init__(self, string, separator=','): self.separator = separator self.set = set(string.split(self.separator)) def __eq__(self, other): return self.set == set(other.split(self.separator)) def __ne__(self, other): return self.set != set(other.split(self.separator)) def __repr__(self): sep = '' if self.separator == ',' else " on %s" % self.separator return '' % (self.set, sep) def vsctl_only(f): # NOTE(ivasilevskaya) as long as some tests rely heavily on mocking # direct vsctl commands, need to ensure that ovsdb_interface = 'vsctl' # TODO(ivasilevskaya) introduce alternative tests for native interface? def wrapper(*args, **kwargs): config.cfg.CONF.set_override("ovsdb_interface", "vsctl", group="OVS") return f(*args, **kwargs) return wrapper class OVS_Lib_Test(base.BaseTestCase): """A test suite to exercise the OVS libraries shared by Neutron agents. Note: these tests do not actually execute ovs-* utilities, and thus can run on any system. That does, however, limit their scope. """ @vsctl_only def setUp(self): super(OVS_Lib_Test, self).setUp() self.BR_NAME = "br-int" self.br = ovs_lib.OVSBridge(self.BR_NAME) self.execute = mock.patch.object( utils, "execute", spec=utils.execute).start() @property def TO(self): return "--timeout=%s" % self.br.vsctl_timeout def _vsctl_args(self, *args): cmd = ['ovs-vsctl', self.TO, '--oneline', '--format=json', '--'] cmd += args return cmd def _vsctl_mock(self, *args): cmd = self._vsctl_args(*args) return mock.call(cmd, run_as_root=True, log_fail_as_error=False) def _verify_vsctl_mock(self, *args): cmd = self._vsctl_args(*args) self.execute.assert_called_once_with(cmd, run_as_root=True, log_fail_as_error=False) def test_vifport(self): """Create and stringify vif port, confirm no exceptions.""" pname = "vif1.0" ofport = 5 vif_id = uuidutils.generate_uuid() mac = "ca:fe:de:ad:be:ef" # test __init__ port = ovs_lib.VifPort(pname, ofport, vif_id, mac, self.br) self.assertEqual(port.port_name, pname) self.assertEqual(port.ofport, ofport) self.assertEqual(port.vif_id, vif_id) self.assertEqual(port.vif_mac, mac) self.assertEqual(port.switch.br_name, self.BR_NAME) # test __str__ str(port) def _build_timeout_opt(self, exp_timeout): return "--timeout=%d" % exp_timeout if exp_timeout else self.TO def test_add_flow(self): ofport = "99" vid = 4000 lsw_id = 18 cidr = '192.168.1.0/24' flow_dict_1 = collections.OrderedDict([ ('cookie', 1234), ('priority', 2), ('dl_src', 'ca:fe:de:ad:be:ef'), ('actions', 'strip_vlan,output:0')]) flow_dict_2 = collections.OrderedDict([ ('cookie', 1254), ('priority', 1), ('actions', 'normal')]) flow_dict_3 = collections.OrderedDict([ ('cookie', 1257), ('priority', 2), ('actions', 'drop')]) flow_dict_4 = collections.OrderedDict([ ('cookie', 1274), ('priority', 2), ('in_port', ofport), ('actions', 'drop')]) flow_dict_5 = collections.OrderedDict([ ('cookie', 1284), ('priority', 4), ('in_port', ofport), ('dl_vlan', vid), ('actions', "strip_vlan,set_tunnel:%s,normal" % (lsw_id))]) flow_dict_6 = collections.OrderedDict([ ('cookie', 1754), ('priority', 3), ('tun_id', lsw_id), ('actions', "mod_vlan_vid:%s,output:%s" % (vid, ofport))]) flow_dict_7 = collections.OrderedDict([ ('cookie', 1256), ('priority', 4), ('nw_src', cidr), ('proto', 'arp'), ('actions', 'drop')]) self.br.add_flow(**flow_dict_1) self.br.add_flow(**flow_dict_2) self.br.add_flow(**flow_dict_3) self.br.add_flow(**flow_dict_4) self.br.add_flow(**flow_dict_5) self.br.add_flow(**flow_dict_6) self.br.add_flow(**flow_dict_7) expected_calls = [ self._ofctl_mock("add-flows", self.BR_NAME, '-', process_input=OFCTLParamListMatcher( "hard_timeout=0,idle_timeout=0,cookie=1234," "priority=2,dl_src=ca:fe:de:ad:be:ef," "actions=strip_vlan,output:0")), self._ofctl_mock("add-flows", self.BR_NAME, '-', process_input=OFCTLParamListMatcher( "hard_timeout=0,idle_timeout=0,cookie=1254," "priority=1,actions=normal")), self._ofctl_mock("add-flows", self.BR_NAME, '-', process_input=OFCTLParamListMatcher( "hard_timeout=0,idle_timeout=0,cookie=1257," "priority=2,actions=drop")), self._ofctl_mock("add-flows", self.BR_NAME, '-', process_input=OFCTLParamListMatcher( "hard_timeout=0,idle_timeout=0,cookie=1274," "priority=2,in_port=%s,actions=drop" % ofport )), self._ofctl_mock("add-flows", self.BR_NAME, '-', process_input=OFCTLParamListMatcher( "hard_timeout=0,idle_timeout=0,cookie=1284," "priority=4,dl_vlan=%s,in_port=%s," "actions=strip_vlan,set_tunnel:%s,normal" % (vid, ofport, lsw_id))), self._ofctl_mock("add-flows", self.BR_NAME, '-', process_input=OFCTLParamListMatcher( "hard_timeout=0,idle_timeout=0,cookie=1754," "priority=3," "tun_id=%s,actions=mod_vlan_vid:%s,output:%s" % (lsw_id, vid, ofport))), self._ofctl_mock("add-flows", self.BR_NAME, '-', process_input=OFCTLParamListMatcher( "hard_timeout=0,idle_timeout=0,cookie=1256," "priority=4,nw_src=%s,arp,actions=drop" % cidr)), ] self.execute.assert_has_calls(expected_calls) def _ofctl_args(self, cmd, *args): cmd = ['ovs-ofctl', cmd, '-O', self.br._highest_protocol_needed] cmd += args return cmd def _ofctl_mock(self, cmd, *args, **kwargs): cmd = self._ofctl_args(cmd, *args) return mock.call(cmd, run_as_root=True, **kwargs) def _verify_ofctl_mock(self, cmd, *args, **kwargs): cmd = self._ofctl_args(cmd, *args) return self.execute.assert_called_once_with(cmd, run_as_root=True, **kwargs) def test_add_flow_timeout_set(self): flow_dict = collections.OrderedDict([ ('cookie', 1234), ('priority', 1), ('hard_timeout', 1000), ('idle_timeout', 2000), ('actions', 'normal')]) self.br.add_flow(**flow_dict) self._verify_ofctl_mock( "add-flows", self.BR_NAME, '-', process_input="hard_timeout=1000,idle_timeout=2000," "priority=1,cookie=1234,actions=normal") def test_add_flow_default_priority(self): flow_dict = collections.OrderedDict([('actions', 'normal'), ('cookie', 1234)]) self.br.add_flow(**flow_dict) self._verify_ofctl_mock( "add-flows", self.BR_NAME, '-', process_input="hard_timeout=0,idle_timeout=0,priority=1," "cookie=1234,actions=normal") def _test_get_port_ofport(self, ofport, expected_result): pname = "tap99" self.br.vsctl_timeout = 0 # Don't waste precious time retrying self.execute.return_value = self._encode_ovs_json( ['ofport'], [[ofport]]) self.assertEqual(self.br.get_port_ofport(pname), expected_result) self._verify_vsctl_mock("--columns=ofport", "list", "Interface", pname) def test_get_port_ofport_succeeds_for_valid_ofport(self): self._test_get_port_ofport(6, 6) def test_get_port_ofport_returns_invalid_ofport_for_non_int(self): self._test_get_port_ofport([], ovs_lib.INVALID_OFPORT) def test_get_port_ofport_returns_invalid_for_invalid(self): self._test_get_port_ofport(ovs_lib.INVALID_OFPORT, ovs_lib.INVALID_OFPORT) def test_get_port_mac(self): pname = "tap99" self.br.vsctl_timeout = 0 # Don't waste precious time retrying self.execute.return_value = self._encode_ovs_json( ['mac_in_use'], [['00:01:02:03:04:05']]) expected_result = '00:01:02:03:04:05' self.assertEqual(self.br.get_port_mac(pname), expected_result) def test_default_datapath(self): # verify kernel datapath is default expected = p_const.OVS_DATAPATH_SYSTEM self.assertEqual(expected, self.br.datapath_type) def test_non_default_datapath(self): expected = p_const.OVS_DATAPATH_NETDEV self.br = ovs_lib.OVSBridge(self.BR_NAME, datapath_type=expected) br2 = self.br.add_bridge('another-br', datapath_type=expected) self.assertEqual(expected, self.br.datapath_type) self.assertEqual(expected, br2.datapath_type) def test_count_flows(self): self.execute.return_value = 'ignore\nflow-1\n' # counts the number of flows as total lines of output - 2 self.assertEqual(self.br.count_flows(), 1) self._verify_ofctl_mock("dump-flows", self.BR_NAME, process_input=None) def test_delete_flow(self): ofport = 5 lsw_id = 40 vid = 39 self.br.delete_flows(in_port=ofport) self.br.delete_flows(tun_id=lsw_id) self.br.delete_flows(dl_vlan=vid) self.br.delete_flows() cookie_spec = "cookie=%s/-1" % self.br._default_cookie expected_calls = [ self._ofctl_mock("del-flows", self.BR_NAME, '-', process_input=StringSetMatcher( "%s,in_port=%d" % (cookie_spec, ofport))), self._ofctl_mock("del-flows", self.BR_NAME, '-', process_input=StringSetMatcher( "%s,tun_id=%s" % (cookie_spec, lsw_id))), self._ofctl_mock("del-flows", self.BR_NAME, '-', process_input=StringSetMatcher( "%s,dl_vlan=%s" % (cookie_spec, vid))), self._ofctl_mock("del-flows", self.BR_NAME, '-', process_input="%s" % cookie_spec), ] self.execute.assert_has_calls(expected_calls) def test_delete_flows_cookie_nomask(self): self.br.delete_flows(cookie=42) self.execute.assert_has_calls([ self._ofctl_mock("del-flows", self.BR_NAME, '-', process_input="cookie=42/-1"), ]) def test_do_action_flows_delete_flows(self): # test what the deferred bridge implementation calls, in the case of a # delete_flows(cookie=ovs_lib.COOKIE_ANY) among calls to # delete_flows(foo=bar) self.br.do_action_flows('del', [{'in_port': 5}, {'cookie': ovs_lib.COOKIE_ANY}]) expected_calls = [ self._ofctl_mock("del-flows", self.BR_NAME, process_input=None), ] self.execute.assert_has_calls(expected_calls) def test_delete_flows_any_cookie(self): self.br.delete_flows(in_port=5, cookie=ovs_lib.COOKIE_ANY) self.br.delete_flows(cookie=ovs_lib.COOKIE_ANY) expected_calls = [ self._ofctl_mock("del-flows", self.BR_NAME, '-', process_input="in_port=5"), self._ofctl_mock("del-flows", self.BR_NAME, process_input=None), ] self.execute.assert_has_calls(expected_calls) def test_mod_delete_flows_strict(self): self.br.delete_flows(in_port=5, priority=1, strict=True) self.br.mod_flow(in_port=5, priority=1, strict=True, actions='drop') cookie_spec = "cookie=%s" % self.br._default_cookie expected_calls = [ self._ofctl_mock("del-flows", self.BR_NAME, '--strict', '-', process_input=StringSetMatcher( "%s/-1,in_port=5,priority=1" % cookie_spec)), self._ofctl_mock("mod-flows", self.BR_NAME, '--strict', '-', process_input=StringSetMatcher( "%s,in_port=5,priority=1,actions=drop" % cookie_spec)), ] self.execute.assert_has_calls(expected_calls) def test_mod_delete_flows_priority_without_strict(self): self.assertRaises(exceptions.InvalidInput, self.br.delete_flows, in_port=5, priority=1) def test_mod_delete_flows_mixed_strict(self): deferred_br = self.br.deferred() deferred_br.delete_flows(in_port=5) deferred_br.delete_flows(in_port=5, priority=1, strict=True) self.assertRaises(exceptions.InvalidInput, deferred_br.apply_flows) def test_dump_flows(self): table = 23 nxst_flow = "NXST_FLOW reply (xid=0x4):" flows = "\n".join([" cookie=0x0, duration=18042.514s, table=0, " "n_packets=6, n_bytes=468, " "priority=2,in_port=1 actions=drop", " cookie=0x0, duration=18027.562s, table=0, " "n_packets=0, n_bytes=0, " "priority=3,in_port=1,dl_vlan=100 " "actions=mod_vlan_vid:1,NORMAL", " cookie=0x0, duration=18044.351s, table=0, " "n_packets=9, n_bytes=594, priority=1 " "actions=NORMAL", " cookie=0x0, " "duration=18044.211s, table=23, n_packets=0, " "n_bytes=0, priority=0 actions=drop"]) flow_args = '\n'.join([nxst_flow, flows]) run_ofctl = mock.patch.object(self.br, 'run_ofctl').start() run_ofctl.side_effect = [flow_args] retflows = self.br.dump_flows_for_table(table) self.assertEqual(flows, retflows) def test_dump_flows_ovs_dead(self): table = 23 run_ofctl = mock.patch.object(self.br, 'run_ofctl').start() run_ofctl.side_effect = [''] retflows = self.br.dump_flows_for_table(table) self.assertIsNone(retflows) def test_mod_flow_with_priority_set(self): params = {'in_port': '1', 'priority': '1'} self.assertRaises(exceptions.InvalidInput, self.br.mod_flow, **params) def test_mod_flow_no_actions_set(self): params = {'in_port': '1'} self.assertRaises(exceptions.InvalidInput, self.br.mod_flow, **params) def test_ofctl_of_version_use_highest(self): self.br.add_flow(in_port=1, actions="drop") self.execute.assert_has_calls([ mock.call(['ovs-ofctl', 'add-flows', '-O', p_const.OPENFLOW10, mock.ANY, '-'], process_input=mock.ANY, run_as_root=mock.ANY) ]) self.br.use_at_least_protocol(p_const.OPENFLOW12) self.execute.reset_mock() self.br.add_flow(in_port=1, actions="drop") self.execute.assert_has_calls([ mock.call(['ovs-ofctl', 'add-flows', '-O', p_const.OPENFLOW12, mock.ANY, '-'], process_input=mock.ANY, run_as_root=mock.ANY), ]) def test_ofctl_of_version_keep_highest(self): self.br.use_at_least_protocol(p_const.OPENFLOW13) self.br.use_at_least_protocol(p_const.OPENFLOW12) self.execute.reset_mock() self.br.add_flow(in_port=1, actions="drop") self.execute.assert_has_calls([ mock.call(['ovs-ofctl', 'add-flows', '-O', p_const.OPENFLOW13, mock.ANY, '-'], process_input=mock.ANY, run_as_root=mock.ANY), ]) def test_ofctl_of_version_use_unknown(self): with testtools.ExpectedException(Exception): self.br.use_at_least_protocol("OpenFlow42") def test_run_ofctl_retry_on_socket_error(self): err = RuntimeError('failed to connect to socket') self.execute.side_effect = [err] * 5 with mock.patch('time.sleep') as sleep: self.br.run_ofctl('add-flows', []) self.assertEqual(5, sleep.call_count) self.assertEqual(6, self.execute.call_count) # a regular exception fails right away self.execute.side_effect = RuntimeError('garbage') self.execute.reset_mock() with mock.patch('time.sleep') as sleep: self.br.run_ofctl('add-flows', []) self.assertEqual(0, sleep.call_count) self.assertEqual(1, self.execute.call_count) def test_add_tunnel_port(self): pname = "tap99" local_ip = "1.1.1.1" remote_ip = "9.9.9.9" ofport = 6 command = ["--may-exist", "add-port", self.BR_NAME, pname] command.extend(["--", "set", "Interface", pname]) command.extend(["type=gre", "options:df_default=true", "options:remote_ip=" + remote_ip, "options:local_ip=" + local_ip, "options:in_key=flow", "options:out_key=flow", "options:egress_pkt_mark=0"]) # Each element is a tuple of (expected mock call, return_value) expected_calls_and_values = [ (self._vsctl_mock(*command), None), (self._vsctl_mock("--columns=ofport", "list", "Interface", pname), self._encode_ovs_json(['ofport'], [[ofport]])), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) self.assertEqual( self.br.add_tunnel_port(pname, remote_ip, local_ip), ofport) tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_add_vxlan_fragmented_tunnel_port(self): pname = "tap99" local_ip = "1.1.1.1" remote_ip = "9.9.9.9" ofport = 6 vxlan_udp_port = "9999" dont_fragment = False command = ["--may-exist", "add-port", self.BR_NAME, pname] command.extend(["--", "set", "Interface", pname]) command.extend(["type=" + constants.TYPE_VXLAN, "options:dst_port=" + vxlan_udp_port, "options:df_default=false", "options:remote_ip=" + remote_ip, "options:local_ip=" + local_ip, "options:in_key=flow", "options:out_key=flow", "options:egress_pkt_mark=0"]) # Each element is a tuple of (expected mock call, return_value) expected_calls_and_values = [ (self._vsctl_mock(*command), None), (self._vsctl_mock("--columns=ofport", "list", "Interface", pname), self._encode_ovs_json(['ofport'], [[ofport]])), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) self.assertEqual( self.br.add_tunnel_port(pname, remote_ip, local_ip, constants.TYPE_VXLAN, vxlan_udp_port, dont_fragment), ofport) tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_add_vxlan_csum_tunnel_port(self): pname = "tap99" local_ip = "1.1.1.1" remote_ip = "9.9.9.9" ofport = 6 vxlan_udp_port = "9999" dont_fragment = True tunnel_csum = True command = ["--may-exist", "add-port", self.BR_NAME, pname] command.extend(["--", "set", "Interface", pname]) command.extend(["type=" + constants.TYPE_VXLAN, "options:dst_port=" + vxlan_udp_port, "options:df_default=true", "options:remote_ip=" + remote_ip, "options:local_ip=" + local_ip, "options:in_key=flow", "options:out_key=flow", "options:egress_pkt_mark=0", "options:csum=true"]) # Each element is a tuple of (expected mock call, return_value) expected_calls_and_values = [ (self._vsctl_mock(*command), None), (self._vsctl_mock("--columns=ofport", "list", "Interface", pname), self._encode_ovs_json(['ofport'], [[ofport]])), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) self.assertEqual( self.br.add_tunnel_port(pname, remote_ip, local_ip, constants.TYPE_VXLAN, vxlan_udp_port, dont_fragment, tunnel_csum), ofport) tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_add_vxlan_tos_tunnel_port(self): pname = "tap99" local_ip = "1.1.1.1" remote_ip = "9.9.9.9" ofport = 6 vxlan_udp_port = "9999" dont_fragment = True tunnel_csum = False tos = 8 command = ["--may-exist", "add-port", self.BR_NAME, pname] command.extend(["--", "set", "Interface", pname]) command.extend(["type=" + constants.TYPE_VXLAN, "options:dst_port=" + vxlan_udp_port, "options:df_default=true", "options:remote_ip=" + remote_ip, "options:local_ip=" + local_ip, "options:in_key=flow", "options:out_key=flow", "options:egress_pkt_mark=0", "options:tos=" + str(tos)]) # Each element is a tuple of (expected mock call, return_value) expected_calls_and_values = [ (self._vsctl_mock(*command), None), (self._vsctl_mock("--columns=ofport", "list", "Interface", pname), self._encode_ovs_json(['ofport'], [[ofport]])), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) self.assertEqual( self.br.add_tunnel_port(pname, remote_ip, local_ip, constants.TYPE_VXLAN, vxlan_udp_port, dont_fragment, tunnel_csum, tos), ofport) tools.verify_mock_calls(self.execute, expected_calls_and_values) def _encode_ovs_json(self, headings, data): # See man ovs-vsctl(8) for the encoding details. r = {"data": [], "headings": headings} for row in data: ovs_row = [] r["data"].append(ovs_row) for cell in row: if isinstance(cell, (str, int, list)): ovs_row.append(cell) elif isinstance(cell, dict): ovs_row.append(["map", cell.items()]) elif isinstance(cell, set): ovs_row.append(["set", cell]) else: raise TypeError('%r not int, str, list, set or dict' % type(cell)) return jsonutils.dumps(r) def test_get_vif_port_to_ofport_map(self): self.execute.return_value = OVSLIST_WITH_UNSET_PORT results = self.br.get_vif_port_to_ofport_map() expected = {'2ab72a72-4407-4ef3-806a-b2172f3e4dc7': 2, 'patch-tun': 1} self.assertEqual(expected, results) def test_get_vif_ports(self): pname = "tap99" ofport = 6 vif_id = uuidutils.generate_uuid() mac = "ca:fe:de:ad:be:ef" id_field = 'iface-id' external_ids = {"attached-mac": mac, id_field: vif_id} self.br.get_ports_attributes = mock.Mock(return_value=[{ 'name': pname, 'ofport': ofport, 'external_ids': external_ids}]) ports = self.br.get_vif_ports() self.assertEqual(1, len(ports)) self.assertEqual(ports[0].port_name, pname) self.assertEqual(ports[0].ofport, ofport) self.assertEqual(ports[0].vif_id, vif_id) self.assertEqual(ports[0].vif_mac, mac) self.assertEqual(ports[0].switch.br_name, self.BR_NAME) self.br.get_ports_attributes.assert_called_once_with( 'Interface', columns=['name', 'external_ids', 'ofport'], if_exists=True) def test_get_vif_port_set(self): id_key = 'iface-id' headings = ['name', 'external_ids', 'ofport'] data = [ # A vif port on this bridge: ['tap99', {id_key: 'tap99id', 'attached-mac': 'tap99mac'}, 1], # A vif port on this bridge not yet configured ['tap98', {id_key: 'tap98id', 'attached-mac': 'tap98mac'}, []], # Another vif port on this bridge not yet configured ['tap97', {id_key: 'tap97id', 'attached-mac': 'tap97mac'}, ['set', []]], # Non-vif port on this bridge: ['bogus', {}, 2], ] # Each element is a tuple of (expected mock call, return_value) expected_calls_and_values = [ (self._vsctl_mock("list-ports", self.BR_NAME), 'tap99\\ntun22'), (self._vsctl_mock("--if-exists", "--columns=name,external_ids,ofport", "list", "Interface", 'tap99', 'tun22'), self._encode_ovs_json(headings, data)), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) port_set = self.br.get_vif_port_set() self.assertEqual(set(['tap99id']), port_set) tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_get_vif_ports_list_ports_error(self): expected_calls_and_values = [ (self._vsctl_mock("list-ports", self.BR_NAME), RuntimeError()), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) self.assertRaises(RuntimeError, self.br.get_vif_ports) tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_get_vif_port_set_list_ports_error(self): expected_calls_and_values = [ (self._vsctl_mock("list-ports", self.BR_NAME), RuntimeError()), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) self.assertRaises(RuntimeError, self.br.get_vif_port_set) tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_get_vif_port_set_list_interface_error(self): expected_calls_and_values = [ (self._vsctl_mock("list-ports", self.BR_NAME), 'tap99\n'), (self._vsctl_mock("--if-exists", "--columns=name,external_ids,ofport", "list", "Interface", "tap99"), RuntimeError()), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) self.assertRaises(RuntimeError, self.br.get_vif_port_set) tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_get_port_tag_dict(self): headings = ['name', 'tag'] data = [ ['int-br-eth2', set()], ['patch-tun', set()], ['qr-76d9e6b6-21', 1], ['tapce5318ff-78', 1], ['tape1400310-e6', 1], ] # Each element is a tuple of (expected mock call, return_value) expected_calls_and_values = [ (self._vsctl_mock("list-ports", self.BR_NAME), '\\n'.join((iface for iface, tag in data))), (self._vsctl_mock("--columns=name,tag", "list", "Port"), self._encode_ovs_json(headings, data)), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) port_tags = self.br.get_port_tag_dict() self.assertEqual( port_tags, {u'int-br-eth2': [], u'patch-tun': [], u'qr-76d9e6b6-21': 1, u'tapce5318ff-78': 1, u'tape1400310-e6': 1} ) def test_clear_db_attribute(self): pname = "tap77" self.br.clear_db_attribute("Port", pname, "tag") self._verify_vsctl_mock("clear", "Port", pname, "tag") def _test_iface_to_br(self, exp_timeout=None): iface = 'tap0' br = 'br-int' if exp_timeout: self.br.vsctl_timeout = exp_timeout self.execute.return_value = 'br-int' self.assertEqual(self.br.get_bridge_for_iface(iface), br) self._verify_vsctl_mock("iface-to-br", iface) def test_iface_to_br(self): self._test_iface_to_br() def test_iface_to_br_non_default_timeout(self): new_timeout = 5 self._test_iface_to_br(new_timeout) def test_iface_to_br_handles_ovs_vsctl_exception(self): iface = 'tap0' self.execute.side_effect = Exception self.assertIsNone(self.br.get_bridge_for_iface(iface)) self._verify_vsctl_mock("iface-to-br", iface) def test_delete_all_ports(self): with mock.patch.object(self.br, 'get_port_name_list', return_value=['port1']) as get_port: with mock.patch.object(self.br, 'delete_port') as delete_port: self.br.delete_ports(all_ports=True) get_port.assert_called_once_with() delete_port.assert_called_once_with('port1') def test_delete_neutron_ports(self): port1 = ovs_lib.VifPort('tap1234', 1, uuidutils.generate_uuid(), 'ca:fe:de:ad:be:ef', 'br') port2 = ovs_lib.VifPort('tap5678', 2, uuidutils.generate_uuid(), 'ca:ee:de:ad:be:ef', 'br') with mock.patch.object(self.br, 'get_vif_ports', return_value=[port1, port2]) as get_ports: with mock.patch.object(self.br, 'delete_port') as delete_port: self.br.delete_ports(all_ports=False) get_ports.assert_called_once_with() delete_port.assert_has_calls([ mock.call('tap1234'), mock.call('tap5678') ]) def test_delete_neutron_ports_list_error(self): expected_calls_and_values = [ (self._vsctl_mock("list-ports", self.BR_NAME), RuntimeError()), ] tools.setup_mock_calls(self.execute, expected_calls_and_values) self.assertRaises(RuntimeError, self.br.delete_ports, all_ports=False) tools.verify_mock_calls(self.execute, expected_calls_and_values) def test_get_bridges_not_default_timeout(self): bridges = ['br-int', 'br-ex'] self.br.vsctl_timeout = 5 self.execute.return_value = 'br-int\\nbr-ex\n' self.assertEqual(self.br.get_bridges(), bridges) self._verify_vsctl_mock("list-br") def test_get_local_port_mac_succeeds(self): with mock.patch('neutron.agent.linux.ip_lib.IpLinkCommand', return_value=mock.Mock(address='foo')): self.assertEqual('foo', self.br.get_local_port_mac()) def test_get_local_port_mac_raises_exception_for_missing_mac(self): with mock.patch('neutron.agent.linux.ip_lib.IpLinkCommand', return_value=mock.Mock(address=None)): with testtools.ExpectedException(Exception): self.br.get_local_port_mac() def test_delete_egress_bw_limit_for_port(self): with mock.patch.object( self.br, "_set_egress_bw_limit_for_port" ) as set_egress_mock, mock.patch.object( self.br, "port_exists", return_value=True ) as port_exists_mock: self.br.delete_egress_bw_limit_for_port("test_port") port_exists_mock.assert_called_once_with("test_port") set_egress_mock.assert_called_once_with("test_port", 0, 0) def test_delete_egress_bw_limit_for_port_port_not_exists(self): with mock.patch.object( self.br, "_set_egress_bw_limit_for_port" ) as set_egress_mock, mock.patch.object( self.br, "port_exists", return_value=False ) as port_exists_mock: self.br.delete_egress_bw_limit_for_port("test_port") port_exists_mock.assert_called_once_with("test_port") set_egress_mock.assert_not_called() def test_get_vifs_by_ids(self): db_list_res = [ {'name': 'qvo1', 'ofport': 1, 'external_ids': {'iface-id': 'pid1', 'attached-mac': '11'}}, {'name': 'qvo2', 'ofport': 2, 'external_ids': {'iface-id': 'pid2', 'attached-mac': '22'}}, {'name': 'qvo4', 'ofport': -1, 'external_ids': {'iface-id': 'pid4', 'attached-mac': '44'}}, ] self.br.get_ports_attributes = mock.Mock(return_value=db_list_res) self.br.ovsdb = mock.Mock() self.br.ovsdb.list_ports.return_value.execute.return_value = [ 'qvo1', 'qvo2', 'qvo4'] by_id = self.br.get_vifs_by_ids(['pid1', 'pid2', 'pid3', 'pid4']) # pid3 isn't on bridge and pid4 doesn't have a valid ofport self.assertIsNone(by_id['pid3']) self.assertIsNone(by_id['pid4']) self.assertEqual('pid1', by_id['pid1'].vif_id) self.assertEqual('qvo1', by_id['pid1'].port_name) self.assertEqual(1, by_id['pid1'].ofport) self.assertEqual('pid2', by_id['pid2'].vif_id) self.assertEqual('qvo2', by_id['pid2'].port_name) self.assertEqual(2, by_id['pid2'].ofport) self.br.get_ports_attributes.assert_has_calls( [mock.call('Interface', columns=['name', 'external_ids', 'ofport'], if_exists=True)]) def _test_get_vif_port_by_id(self, iface_id, data, br_name=None, extra_calls_and_values=None): headings = ['external_ids', 'name', 'ofport'] # Each element is a tuple of (expected mock call, return_value) expected_calls_and_values = [ (self._vsctl_mock("--columns=external_ids,name,ofport", "find", "Interface", 'external_ids:iface-id=%s' % iface_id, 'external_ids:attached-mac!=""'), self._encode_ovs_json(headings, data))] if data: if not br_name: br_name = self.BR_NAME # Only the last information list in 'data' is used, so if more # than one vif is described in data, the rest must be declared # in the argument 'expected_calls_and_values'. if extra_calls_and_values: expected_calls_and_values.extend(extra_calls_and_values) expected_calls_and_values.append( (self._vsctl_mock("iface-to-br", data[-1][headings.index('name')]), br_name)) tools.setup_mock_calls(self.execute, expected_calls_and_values) vif_port = self.br.get_vif_port_by_id(iface_id) tools.verify_mock_calls(self.execute, expected_calls_and_values) return vif_port def _assert_vif_port(self, vif_port, ofport=None, mac=None): if not ofport or ofport == -1 or not mac: self.assertIsNone(vif_port, "Got %s" % vif_port) return self.assertEqual('tap99id', vif_port.vif_id) self.assertEqual(mac, vif_port.vif_mac) self.assertEqual('tap99', vif_port.port_name) self.assertEqual(ofport, vif_port.ofport) def _test_get_vif_port_by_id_with_data(self, ofport=None, mac=None): external_ids = [["iface-id", "tap99id"], ["iface-status", "active"], ["attached-mac", mac]] data = [[["map", external_ids], "tap99", ofport if ofport else ["set", []]]] vif_port = self._test_get_vif_port_by_id('tap99id', data) self._assert_vif_port(vif_port, ofport, mac) def test_get_vif_by_port_id_with_ofport(self): self._test_get_vif_port_by_id_with_data( ofport=1, mac="aa:bb:cc:dd:ee:ff") def test_get_vif_by_port_id_without_ofport(self): self._test_get_vif_port_by_id_with_data(mac="aa:bb:cc:dd:ee:ff") def test_get_vif_by_port_id_with_invalid_ofport(self): self._test_get_vif_port_by_id_with_data( ofport=-1, mac="aa:bb:cc:dd:ee:ff") def test_get_vif_by_port_id_with_no_data(self): self.assertIsNone(self._test_get_vif_port_by_id('whatever', [])) def test_get_vif_by_port_id_different_bridge(self): external_ids = [["iface-id", "tap99id"], ["iface-status", "active"]] data = [[["map", external_ids], "tap99", 1]] self.assertIsNone(self._test_get_vif_port_by_id('tap99id', data, "br-ext")) def test_get_vif_by_port_id_multiple_vifs(self): external_ids = [["iface-id", "tap99id"], ["iface-status", "active"], ["attached-mac", "de:ad:be:ef:13:37"]] data = [[["map", external_ids], "dummytap", 1], [["map", external_ids], "tap99", 1337]] extra_calls_and_values = [ (self._vsctl_mock("iface-to-br", "dummytap"), "br-ext")] vif_port = self._test_get_vif_port_by_id( 'tap99id', data, extra_calls_and_values=extra_calls_and_values) self._assert_vif_port(vif_port, ofport=1337, mac="de:ad:be:ef:13:37") def test_get_port_ofport_retry(self): with mock.patch.object( self.br, 'db_get_val', side_effect=[[], [], [], [], 1]): self.assertEqual(1, self.br._get_port_val('1', 'ofport')) def test_get_port_ofport_retry_fails(self): # reduce timeout for faster execution self.br.vsctl_timeout = 1 # after 7 calls the retry will timeout and raise with mock.patch.object( self.br, 'db_get_val', side_effect=[[] for _ in range(7)]): self.assertRaises(tenacity.RetryError, self.br._get_port_val, '1', 'ofport') def test_get_port_external_ids_retry(self): external_ids = [["iface-id", "tap99id"], ["iface-status", "active"], ["attached-mac", "de:ad:be:ef:13:37"]] with mock.patch.object( self.br, 'db_get_val', side_effect=[[], [], [], [], external_ids]): self.assertEqual(external_ids, self.br._get_port_val('1', 'external_ids')) def test_get_port_external_ids_retry_fails(self): # reduce timeout for faster execution self.br.vsctl_timeout = 1 # after 7 calls the retry will timeout and raise with mock.patch.object( self.br, 'db_get_val', side_effect=[[] for _ in range(7)]): self.assertRaises(tenacity.RetryError, self.br._get_port_val, '1', 'external_ids') def test_set_controller_rate_limit(self): with mock.patch.object( self.br, "set_controller_field" ) as set_ctrl_field_mock: self.br.set_controller_rate_limit(200) set_ctrl_field_mock.assert_called_once_with( 'controller_rate_limit', 200) def test_set_controller_rate_limit_with_value_less_than_min(self): with mock.patch.object( self.br, "set_controller_field" ) as set_ctrl_field_mock: self.br.set_controller_rate_limit(50) set_ctrl_field_mock.assert_called_once_with( 'controller_rate_limit', ovs_lib.CTRL_RATE_LIMIT_MIN) def test_set_controller_burst_limit(self): with mock.patch.object( self.br, "set_controller_field" ) as set_ctrl_field_mock: self.br.set_controller_burst_limit(100) set_ctrl_field_mock.assert_called_once_with( 'controller_burst_limit', 100) def test_set_controller_burst_limit_with_value_less_than_min(self): with mock.patch.object( self.br, "set_controller_field" ) as set_ctrl_field_mock: self.br.set_controller_burst_limit(10) set_ctrl_field_mock.assert_called_once_with( 'controller_burst_limit', ovs_lib.CTRL_BURST_LIMIT_MIN) class TestDeferredOVSBridge(base.BaseTestCase): def setUp(self): super(TestDeferredOVSBridge, self).setUp() self.br = mock.Mock() self.mocked_do_action_flows = mock.patch.object( self.br, 'do_action_flows').start() self.add_flow_dict1 = dict(in_port=11, actions='drop') self.add_flow_dict2 = dict(in_port=12, actions='drop') self.mod_flow_dict1 = dict(in_port=21, actions='drop') self.mod_flow_dict2 = dict(in_port=22, actions='drop') self.del_flow_dict1 = dict(in_port=31) self.del_flow_dict2 = dict(in_port=32) def test_right_allowed_passthroughs(self): expected_passthroughs = ('add_port', 'add_tunnel_port', 'delete_port') self.assertEqual(expected_passthroughs, ovs_lib.DeferredOVSBridge.ALLOWED_PASSTHROUGHS) def _verify_mock_call(self, expected_calls): self.mocked_do_action_flows.assert_has_calls(expected_calls) self.assertEqual(len(expected_calls), len(self.mocked_do_action_flows.mock_calls)) def test_apply_on_exit(self): expected_calls = [ mock.call('add', [self.add_flow_dict1], False), mock.call('mod', [self.mod_flow_dict1], False), mock.call('del', [self.del_flow_dict1], False), ] with ovs_lib.DeferredOVSBridge(self.br) as deferred_br: deferred_br.add_flow(**self.add_flow_dict1) deferred_br.mod_flow(**self.mod_flow_dict1) deferred_br.delete_flows(**self.del_flow_dict1) self._verify_mock_call([]) self._verify_mock_call(expected_calls) def test_apply_on_exit_with_errors(self): try: with ovs_lib.DeferredOVSBridge(self.br) as deferred_br: deferred_br.add_flow(**self.add_flow_dict1) deferred_br.mod_flow(**self.mod_flow_dict1) deferred_br.delete_flows(**self.del_flow_dict1) raise Exception() except Exception: self._verify_mock_call([]) else: self.fail('Exception would be reraised') def test_apply(self): expected_calls = [ mock.call('add', [self.add_flow_dict1], False), mock.call('mod', [self.mod_flow_dict1], False), mock.call('del', [self.del_flow_dict1], False), ] with ovs_lib.DeferredOVSBridge(self.br) as deferred_br: deferred_br.add_flow(**self.add_flow_dict1) deferred_br.mod_flow(**self.mod_flow_dict1) deferred_br.delete_flows(**self.del_flow_dict1) self._verify_mock_call([]) deferred_br.apply_flows() self._verify_mock_call(expected_calls) self._verify_mock_call(expected_calls) def test_apply_order(self): expected_calls = [ mock.call( 'del', [self.del_flow_dict1, self.del_flow_dict2], False), mock.call( 'mod', [self.mod_flow_dict1, self.mod_flow_dict2], False), mock.call( 'add', [self.add_flow_dict1, self.add_flow_dict2], False), ] order = 'del', 'mod', 'add' with ovs_lib.DeferredOVSBridge(self.br, order=order) as deferred_br: deferred_br.add_flow(**self.add_flow_dict1) deferred_br.mod_flow(**self.mod_flow_dict1) deferred_br.delete_flows(**self.del_flow_dict1) deferred_br.delete_flows(**self.del_flow_dict2) deferred_br.add_flow(**self.add_flow_dict2) deferred_br.mod_flow(**self.mod_flow_dict2) self._verify_mock_call(expected_calls) def test_apply_full_ordered(self): expected_calls = [ mock.call('add', [self.add_flow_dict1], False), mock.call('mod', [self.mod_flow_dict1], False), mock.call( 'del', [self.del_flow_dict1, self.del_flow_dict2], False), mock.call('add', [self.add_flow_dict2], False), mock.call('mod', [self.mod_flow_dict2], False), ] with ovs_lib.DeferredOVSBridge(self.br, full_ordered=True) as deferred_br: deferred_br.add_flow(**self.add_flow_dict1) deferred_br.mod_flow(**self.mod_flow_dict1) deferred_br.delete_flows(**self.del_flow_dict1) deferred_br.delete_flows(**self.del_flow_dict2) deferred_br.add_flow(**self.add_flow_dict2) deferred_br.mod_flow(**self.mod_flow_dict2) self._verify_mock_call(expected_calls) def test_getattr_unallowed_attr(self): with ovs_lib.DeferredOVSBridge(self.br) as deferred_br: self.assertEqual(self.br.add_port, deferred_br.add_port) def test_getattr_unallowed_attr_failure(self): with ovs_lib.DeferredOVSBridge(self.br) as deferred_br: self.assertRaises(AttributeError, getattr, deferred_br, 'failure') @vsctl_only def test_default_cookie(self): self.br = ovs_lib.OVSBridge("br-tun") uuid_stamp1 = self.br.default_cookie self.assertEqual(uuid_stamp1, self.br.default_cookie) @vsctl_only def test_cookie_passed_to_addmod(self): self.br = ovs_lib.OVSBridge("br-tun") stamp = str(self.br.default_cookie) expected_calls = [ mock.call('add-flows', ['-'], 'hard_timeout=0,idle_timeout=0,priority=1,' 'cookie=' + stamp + ',actions=drop'), mock.call('mod-flows', ['-'], 'cookie=' + stamp + ',actions=drop') ] with mock.patch.object(self.br, 'run_ofctl') as f: with ovs_lib.DeferredOVSBridge(self.br) as deferred_br: deferred_br.add_flow(actions='drop') deferred_br.mod_flow(actions='drop') f.assert_has_calls(expected_calls) @vsctl_only def test_add_flow_with_bundle(self): br = ovs_lib.OVSBridge("foo") deferred = br.deferred(use_bundle=True) with mock.patch.object(utils, "execute", spec=utils.execute) as mexec: deferred.add_flow(in_port=1, actions='drop') deferred.apply_flows() self.assertIn('--bundle', mexec.call_args[0][0]) neutron-12.1.1/neutron/tests/unit/agent/common/test_polling.py0000664000175000017500000000520613553660047024602 0ustar zuulzuul00000000000000# Copyright 2013 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.agent.common import base_polling as polling from neutron.tests import base class TestBasePollingManager(base.BaseTestCase): def setUp(self): super(TestBasePollingManager, self).setUp() self.pm = polling.BasePollingManager() def test__is_polling_required_should_not_be_implemented(self): self.assertRaises(NotImplementedError, self.pm._is_polling_required) def test_force_polling_sets_interval_attribute(self): self.assertFalse(self.pm._force_polling) self.pm.force_polling() self.assertTrue(self.pm._force_polling) def test_polling_completed_sets_interval_attribute(self): self.pm._polling_completed = False self.pm.polling_completed() self.assertTrue(self.pm._polling_completed) def mock_is_polling_required(self, return_value): return mock.patch.object(self.pm, '_is_polling_required', return_value=return_value) def test_is_polling_required_returns_true_when_forced(self): with self.mock_is_polling_required(False): self.pm.force_polling() self.assertTrue(self.pm.is_polling_required) self.assertFalse(self.pm._force_polling) def test_is_polling_required_returns_true_when_polling_not_completed(self): with self.mock_is_polling_required(False): self.pm._polling_completed = False self.assertTrue(self.pm.is_polling_required) def test_is_polling_required_returns_true_when_updates_are_present(self): with self.mock_is_polling_required(True): self.assertTrue(self.pm.is_polling_required) self.assertFalse(self.pm._polling_completed) def test_is_polling_required_returns_false_for_no_updates(self): with self.mock_is_polling_required(False): self.assertFalse(self.pm.is_polling_required) class TestAlwaysPoll(base.BaseTestCase): def test_is_polling_required_always_returns_true(self): pm = polling.AlwaysPoll() self.assertTrue(pm.is_polling_required) neutron-12.1.1/neutron/tests/unit/agent/l2/0000775000175000017500000000000013553660156020550 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/l2/extensions/0000775000175000017500000000000013553660156022747 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/l2/extensions/__init__.py0000664000175000017500000000000013553660046025044 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/l2/extensions/test_qos.py0000664000175000017500000005743213553660047025174 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mellanox Technologies, Ltd # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as common_constants from neutron_lib import context from neutron_lib.db import constants as db_consts from neutron_lib.services.qos import constants as qos_consts from oslo_utils import uuidutils from neutron.agent.l2.extensions import qos from neutron.agent.l2.extensions import qos_linux from neutron.api.rpc.callbacks.consumer import registry from neutron.api.rpc.callbacks import events from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc from neutron import manager from neutron.objects.qos import policy from neutron.objects.qos import rule from neutron.plugins.ml2.drivers.openvswitch.agent import ( ovs_agent_extension_api as ovs_ext_api) from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import ( ovs_bridge) from neutron.tests import base BASE_TEST_POLICY = {'context': None, 'name': 'test1', 'id': uuidutils.generate_uuid()} TEST_POLICY = policy.QosPolicy(**BASE_TEST_POLICY) TEST_POLICY_DESCR = policy.QosPolicy(description='fake_descr', **BASE_TEST_POLICY) TEST_POLICY2 = policy.QosPolicy(context=None, name='test2', id=uuidutils.generate_uuid()) TEST_PORT = {'port_id': 'test_port_id', 'qos_policy_id': TEST_POLICY.id} TEST_PORT2 = {'port_id': 'test_port_id_2', 'qos_policy_id': TEST_POLICY2.id} FAKE_RULE_ID = uuidutils.generate_uuid() FAKE_RULE_ID_2 = uuidutils.generate_uuid() REALLY_FAKE_RULE_ID = uuidutils.generate_uuid() class FakeDriver(qos_linux.QosLinuxAgentDriver): SUPPORTED_RULES = { qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: { qos_consts.MAX_KBPS: { 'type:range': [0, db_consts.DB_INTEGER_MAX_VALUE]}, qos_consts.MAX_BURST: { 'type:range': [0, db_consts.DB_INTEGER_MAX_VALUE]}, qos_consts.DIRECTION: { 'type:values': [common_constants.EGRESS_DIRECTION, common_constants.INGRESS_DIRECTION]} }, } def __init__(self): super(FakeDriver, self).__init__() self.create_bandwidth_limit = mock.Mock() self.update_bandwidth_limit = mock.Mock() self.delete_bandwidth_limit = mock.Mock() self.delete_bandwidth_limit_ingress = mock.Mock() def initialize(self): pass class QosFakeRule(rule.QosRule): rule_type = 'fake_type' class QosAgentDriverTestCase(base.BaseTestCase): def setUp(self): super(QosAgentDriverTestCase, self).setUp() self.driver = FakeDriver() self.policy = TEST_POLICY self.egress_bandwidth_limit_rule = ( rule.QosBandwidthLimitRule( context=None, id=FAKE_RULE_ID, qos_policy_id=self.policy.id, max_kbps=100, max_burst_kbps=200, direction=common_constants.EGRESS_DIRECTION)) self.ingress_bandwidth_limit_rule = ( rule.QosBandwidthLimitRule( context=None, id=FAKE_RULE_ID_2, qos_policy_id=self.policy.id, max_kbps=100, max_burst_kbps=200, direction=common_constants.INGRESS_DIRECTION)) self.policy.rules = [self.egress_bandwidth_limit_rule, self.ingress_bandwidth_limit_rule] self.port = {'qos_policy_id': None, 'network_qos_policy_id': None, 'device_owner': 'random-device-owner'} self.fake_rule = QosFakeRule(context=None, id=REALLY_FAKE_RULE_ID, qos_policy_id=self.policy.id) def test_create(self): self.driver.create(self.port, self.policy) self.driver.create_bandwidth_limit.assert_has_calls([ mock.call(self.port, self.egress_bandwidth_limit_rule), mock.call(self.port, self.ingress_bandwidth_limit_rule) ]) def test_update(self): self.driver.update(self.port, self.policy) self.driver.update_bandwidth_limit.assert_has_calls([ mock.call(self.port, self.egress_bandwidth_limit_rule), mock.call(self.port, self.ingress_bandwidth_limit_rule) ]) def test_delete(self): self.driver.delete(self.port, self.policy) self.driver.delete_bandwidth_limit.assert_called_with(self.port) self.driver.delete_bandwidth_limit_ingress.assert_called_with( self.port) def test_delete_no_policy(self): self.driver.delete(self.port, qos_policy=None) self.driver.delete_bandwidth_limit.assert_called_with(self.port) self.driver.delete_bandwidth_limit_ingress.assert_called_with( self.port) def test__iterate_rules_with_unknown_rule_type(self): self.policy.rules.append(self.fake_rule) rules = list(self.driver._iterate_rules(self.policy.rules)) self.assertEqual(2, len(rules)) self.assertIsInstance(rules[0], rule.QosBandwidthLimitRule) self.assertIsInstance(rules[1], rule.QosBandwidthLimitRule) def test__handle_update_create_rules_checks_should_apply_to_port(self): self.egress_bandwidth_limit_rule.should_apply_to_port = mock.Mock( return_value=False) self.ingress_bandwidth_limit_rule.should_apply_to_port = mock.Mock( return_value=False) self.driver.create(self.port, self.policy) self.assertFalse(self.driver.create_bandwidth_limit.called) self.egress_bandwidth_limit_rule.should_apply_to_port = mock.Mock( return_value=True) self.ingress_bandwidth_limit_rule.should_apply_to_port = mock.Mock( return_value=True) self.driver.create(self.port, self.policy) self.assertTrue(self.driver.create_bandwidth_limit.called) def test__get_max_burst_value(self): rule = self.egress_bandwidth_limit_rule rule.max_burst_kbps = 0 expected_burst = rule.max_kbps * qos_consts.DEFAULT_BURST_RATE self.assertEqual( expected_burst, self.driver._get_egress_burst_value(rule) ) def test__rule_type_has_ingress_direction(self): self.assertTrue( self.driver._rule_type_has_ingress_direction( qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)) # Should return False for rule type other than # RULE_TYPE_BANDWIDTH_LIMIT supported_rules = { qos_consts.RULE_TYPE_DSCP_MARKING: { qos_consts.DSCP_MARK: { 'type:values': common_constants.VALID_DSCP_MARKS} } } with mock.patch.dict(self.driver.SUPPORTED_RULES, supported_rules): self.assertFalse( self.driver._rule_type_has_ingress_direction( qos_consts.RULE_TYPE_DSCP_MARKING)) # Should return False for rule type RULE_TYPE_BANDWIDTH_LIMIT but # without INGRESS_DIRECTION in supported values supported_rules = { qos_consts.RULE_TYPE_BANDWIDTH_LIMIT: { 'type:values': [common_constants.EGRESS_DIRECTION] } } with mock.patch.dict(self.driver.SUPPORTED_RULES, supported_rules): self.assertFalse( self.driver._rule_type_has_ingress_direction( qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)) def test__rule_is_ingress_direction(self): self.assertFalse( self.driver._rule_is_ingress_direction( self.egress_bandwidth_limit_rule)) self.assertFalse( self.driver._rule_is_ingress_direction( self.fake_rule)) self.assertTrue( self.driver._rule_is_ingress_direction( self.ingress_bandwidth_limit_rule)) class QosExtensionBaseTestCase(base.BaseTestCase): def setUp(self): super(QosExtensionBaseTestCase, self).setUp() conn_patcher = mock.patch( 'neutron.agent.ovsdb.impl_idl._connection') conn_patcher.start() self.addCleanup(conn_patcher.stop) self.qos_ext = qos.QosAgentExtension() self.context = context.get_admin_context() self.connection = mock.Mock() self.agent_api = ovs_ext_api.OVSAgentExtensionAPI( ovs_bridge.OVSAgentBridge('br-int'), ovs_bridge.OVSAgentBridge('br-tun')) self.qos_ext.consume_api(self.agent_api) # Don't rely on used driver mock.patch.object( manager.NeutronManager, 'load_class_for_provider', return_value=lambda: mock.Mock( spec=qos_linux.QosLinuxAgentDriver)).start() setattr(TEST_POLICY, 'rules', []) class QosExtensionRpcTestCase(QosExtensionBaseTestCase): def setUp(self): super(QosExtensionRpcTestCase, self).setUp() self.qos_ext.initialize( self.connection, constants.EXTENSION_DRIVER_TYPE) self.pull_mock = mock.patch.object( self.qos_ext.resource_rpc, 'pull', return_value=TEST_POLICY).start() def _create_test_port_dict(self, qos_policy_id=None): return {'port_id': uuidutils.generate_uuid(), 'qos_policy_id': qos_policy_id or TEST_POLICY.id} def test_handle_port_with_no_policy(self): port = self._create_test_port_dict() del port['qos_policy_id'] self.qos_ext._process_reset_port = mock.Mock() self.qos_ext.handle_port(self.context, port) self.qos_ext._process_reset_port.assert_called_with(port) def test_handle_unknown_port(self): port = self._create_test_port_dict() qos_policy_id = port['qos_policy_id'] port_id = port['port_id'] TEST_POLICY.rules = [rule.QosBandwidthLimitRule( context=None, id=FAKE_RULE_ID, qos_policy_id=TEST_POLICY.id, max_kbps=100, max_burst_kbps=200, direction=common_constants.EGRESS_DIRECTION)] self.qos_ext.handle_port(self.context, port) # we make sure the underlying qos driver is called with the # right parameters self.qos_ext.qos_driver.create.assert_called_once_with( port, TEST_POLICY) self.assertEqual(port, self.qos_ext.policy_map.qos_policy_ports[qos_policy_id][port_id]) self.assertIn(port_id, self.qos_ext.policy_map.port_policies) self.assertEqual(TEST_POLICY, self.qos_ext.policy_map.known_policies[qos_policy_id]) def test_handle_unknown_port_with_no_rules(self): test_policy_with_rules = {'context': None, 'name': 'test1', 'id': uuidutils.generate_uuid()} test_policy = policy.QosPolicy(**test_policy_with_rules) test_policy.rules = [] port = self._create_test_port_dict(test_policy.id) qos_policy_id = port['qos_policy_id'] port_id = port['port_id'] self.pull_mock.return_value = test_policy self.qos_ext.handle_port(self.context, port) # we make sure the underlying qos driver is called with the # right parameters self.qos_ext.qos_driver.delete.assert_called_once_with(port) self.assertEqual(port, self.qos_ext.policy_map.qos_policy_ports[qos_policy_id][port_id]) self.assertIn(port_id, self.qos_ext.policy_map.port_policies) self.assertEqual(test_policy, self.qos_ext.policy_map.known_policies[qos_policy_id]) def test_handle_known_port(self): port_obj1 = self._create_test_port_dict() port_obj2 = dict(port_obj1) self.qos_ext.handle_port(self.context, port_obj1) self.qos_ext.qos_driver.reset_mock() self.qos_ext.handle_port(self.context, port_obj2) self.assertFalse(self.qos_ext.qos_driver.create.called) def test_handle_known_port_change_policy_id(self): port = self._create_test_port_dict() self.qos_ext.handle_port(self.context, port) self.qos_ext.resource_rpc.pull.reset_mock() test_policy_with_rules = {'context': None, 'name': 'test1', 'id': uuidutils.generate_uuid()} test_policy = policy.QosPolicy(**test_policy_with_rules) self.pull_mock.return_value = test_policy port['qos_policy_id'] = test_policy.id self.qos_ext.handle_port(self.context, port) self.pull_mock.assert_called_once_with( self.context, resources.QOS_POLICY, port['qos_policy_id']) def test_handle_diff_ports_same_policy_id(self): port_obj1 = self._create_test_port_dict() port_obj2 = self._create_test_port_dict() self.qos_ext.handle_port(self.context, port_obj1) self.pull_mock.assert_called_once_with( self.context, resources.QOS_POLICY, port_obj1['qos_policy_id']) self.assertIsNotNone( self.qos_ext.policy_map.get_port_policy(port_obj1)) self.assertIsNone( self.qos_ext.policy_map.get_port_policy(port_obj2)) self.qos_ext.resource_rpc.pull.reset_mock() self.qos_ext.handle_port(self.context, port_obj2) self.assertFalse(self.pull_mock.called) self.assertIsNotNone( self.qos_ext.policy_map.get_port_policy(port_obj2)) self.assertEqual( self.qos_ext.policy_map.get_port_policy(port_obj1), self.qos_ext.policy_map.get_port_policy(port_obj2)) def test_delete_known_port(self): port = self._create_test_port_dict() self.qos_ext.handle_port(self.context, port) self.qos_ext.qos_driver.reset_mock() self.qos_ext.delete_port(self.context, port) self.qos_ext.qos_driver.delete.assert_called_with(port) self.assertIsNone(self.qos_ext.policy_map.get_port_policy(port)) def test_delete_unknown_port(self): port = self._create_test_port_dict() self.qos_ext.delete_port(self.context, port) self.assertTrue(self.qos_ext.qos_driver.delete.called) self.assertIsNone(self.qos_ext.policy_map.get_port_policy(port)) def test__handle_notification_ignores_all_event_types_except_updated(self): with mock.patch.object( self.qos_ext, '_process_update_policy') as update_mock: for event_type in set(events.VALID) - {events.UPDATED}: self.qos_ext._handle_notification(mock.Mock(), 'QOS', object(), event_type) self.assertFalse(update_mock.called) def test__handle_notification_passes_update_events(self): with mock.patch.object( self.qos_ext, '_process_update_policy') as update_mock: policy_obj = mock.Mock() self.qos_ext._handle_notification(mock.Mock(), 'QOS', [policy_obj], events.UPDATED) update_mock.assert_called_with(policy_obj) def test__process_update_policy(self): port1 = self._create_test_port_dict(qos_policy_id=TEST_POLICY.id) port2 = self._create_test_port_dict(qos_policy_id=TEST_POLICY2.id) self.qos_ext.policy_map.set_port_policy(port1, TEST_POLICY) self.qos_ext.policy_map.set_port_policy(port2, TEST_POLICY2) self.qos_ext._policy_rules_modified = mock.Mock(return_value=True) policy_obj = mock.Mock() policy_obj.id = port1['qos_policy_id'] self.qos_ext._process_update_policy(policy_obj) self.qos_ext.qos_driver.update.assert_called_with(port1, policy_obj) self.qos_ext.qos_driver.update.reset_mock() policy_obj.id = port2['qos_policy_id'] self.qos_ext._process_update_policy(policy_obj) self.qos_ext.qos_driver.update.assert_called_with(port2, policy_obj) def test__process_update_policy_descr_not_propagated_into_driver(self): port = self._create_test_port_dict(qos_policy_id=TEST_POLICY.id) self.qos_ext.policy_map.set_port_policy(port, TEST_POLICY) self.qos_ext._policy_rules_modified = mock.Mock(return_value=False) self.qos_ext._process_update_policy(TEST_POLICY_DESCR) self.qos_ext._policy_rules_modified.assert_called_with(TEST_POLICY, TEST_POLICY_DESCR) self.assertFalse(self.qos_ext.qos_driver.delete.called) self.assertFalse(self.qos_ext.qos_driver.update.called) self.assertEqual(TEST_POLICY_DESCR, self.qos_ext.policy_map.get_policy(TEST_POLICY.id)) def test__process_update_policy_not_known(self): self.qos_ext._policy_rules_modified = mock.Mock() self.qos_ext._process_update_policy(TEST_POLICY_DESCR) self.assertFalse(self.qos_ext._policy_rules_modified.called) self.assertFalse(self.qos_ext.qos_driver.delete.called) self.assertFalse(self.qos_ext.qos_driver.update.called) self.assertIsNone(self.qos_ext.policy_map.get_policy( TEST_POLICY_DESCR.id)) def test__process_reset_port(self): port1 = self._create_test_port_dict(qos_policy_id=TEST_POLICY.id) port2 = self._create_test_port_dict(qos_policy_id=TEST_POLICY2.id) self.qos_ext.policy_map.set_port_policy(port1, TEST_POLICY) self.qos_ext.policy_map.set_port_policy(port2, TEST_POLICY2) self.qos_ext._process_reset_port(port1) self.qos_ext.qos_driver.delete.assert_called_with(port1) self.assertIsNone(self.qos_ext.policy_map.get_port_policy(port1)) self.assertIsNotNone(self.qos_ext.policy_map.get_port_policy(port2)) self.qos_ext.qos_driver.delete.reset_mock() self.qos_ext._process_reset_port(port2) self.qos_ext.qos_driver.delete.assert_called_with(port2) self.assertIsNone(self.qos_ext.policy_map.get_port_policy(port2)) class QosExtensionInitializeTestCase(QosExtensionBaseTestCase): @mock.patch.object(registry, 'register') @mock.patch.object(resources_rpc, 'ResourcesPushRpcCallback') def test_initialize_subscribed_to_rpc(self, rpc_mock, subscribe_mock): self.qos_ext.initialize( self.connection, constants.EXTENSION_DRIVER_TYPE) self.connection.create_consumer.assert_has_calls( [mock.call( resources_rpc.resource_type_versioned_topic(resource_type), [rpc_mock()], fanout=True) for resource_type in self.qos_ext.SUPPORTED_RESOURCE_TYPES] ) subscribe_mock.assert_called_with(mock.ANY, resources.QOS_POLICY) class QosExtensionReflushRulesTestCase(QosExtensionBaseTestCase): def setUp(self): super(QosExtensionReflushRulesTestCase, self).setUp() self.qos_ext.initialize( self.connection, constants.EXTENSION_DRIVER_TYPE) self.pull_mock = mock.patch.object( self.qos_ext.resource_rpc, 'pull', return_value=TEST_POLICY).start() self.policy = policy.QosPolicy(**BASE_TEST_POLICY) self.rule = ( rule.QosBandwidthLimitRule(context=None, id=FAKE_RULE_ID, qos_policy_id=self.policy.id, max_kbps=100, max_burst_kbps=10)) self.policy.rules = [self.rule] self.port = {'port_id': uuidutils.generate_uuid(), 'qos_policy_id': TEST_POLICY.id} self.new_policy = policy.QosPolicy(description='descr', **BASE_TEST_POLICY) def test_is_reflush_required_change_policy_descr(self): self.qos_ext.policy_map.set_port_policy(self.port, self.policy) self.new_policy.rules = [self.rule] self.assertFalse(self.qos_ext._policy_rules_modified(self.policy, self.new_policy)) def test_is_reflush_required_change_policy_rule(self): self.qos_ext.policy_map.set_port_policy(self.port, self.policy) updated_rule = (rule.QosBandwidthLimitRule(context=None, id=FAKE_RULE_ID, qos_policy_id=self.policy.id, max_kbps=200, max_burst_kbps=20)) self.new_policy.rules = [updated_rule] self.assertTrue(self.qos_ext._policy_rules_modified(self.policy, self.new_policy)) def test_is_reflush_required_remove_rules(self): self.qos_ext.policy_map.set_port_policy(self.port, self.policy) self.new_policy.rules = [] self.assertTrue(self.qos_ext._policy_rules_modified(self.policy, self.new_policy)) def test_is_reflush_required_add_rules(self): self.qos_ext.policy_map.set_port_policy(self.port, self.policy) self.new_policy.rules = [self.rule] fake_rule = QosFakeRule(context=None, id=REALLY_FAKE_RULE_ID, qos_policy_id=self.policy.id) self.new_policy.rules.append(fake_rule) self.assertTrue(self.qos_ext._policy_rules_modified(self.policy, self.new_policy)) class PortPolicyMapTestCase(base.BaseTestCase): def setUp(self): super(PortPolicyMapTestCase, self).setUp() self.policy_map = qos.PortPolicyMap() def test_update_policy(self): self.policy_map.update_policy(TEST_POLICY) self.assertEqual(TEST_POLICY, self.policy_map.known_policies[TEST_POLICY.id]) def _set_ports(self): self.policy_map.set_port_policy(TEST_PORT, TEST_POLICY) self.policy_map.set_port_policy(TEST_PORT2, TEST_POLICY2) def test_set_port_policy(self): self._set_ports() self.assertEqual(TEST_POLICY, self.policy_map.known_policies[TEST_POLICY.id]) self.assertIn(TEST_PORT['port_id'], self.policy_map.qos_policy_ports[TEST_POLICY.id]) def test_get_port_policy(self): self._set_ports() self.assertEqual(TEST_POLICY, self.policy_map.get_port_policy(TEST_PORT)) self.assertEqual(TEST_POLICY2, self.policy_map.get_port_policy(TEST_PORT2)) def test_get_ports(self): self._set_ports() self.assertEqual([TEST_PORT], list(self.policy_map.get_ports(TEST_POLICY))) self.assertEqual([TEST_PORT2], list(self.policy_map.get_ports(TEST_POLICY2))) def test_clean_by_port(self): self._set_ports() self.policy_map.clean_by_port(TEST_PORT) self.assertNotIn(TEST_POLICY.id, self.policy_map.known_policies) self.assertNotIn(TEST_PORT['port_id'], self.policy_map.port_policies) self.assertIn(TEST_POLICY2.id, self.policy_map.known_policies) def test_clean_by_port_for_unknown_port(self): self.policy_map._clean_policy_info = mock.Mock() self.policy_map.clean_by_port(TEST_PORT) self.policy_map._clean_policy_info.assert_not_called() def test_has_policy_changed(self): self._set_ports() self.assertTrue( self.policy_map.has_policy_changed(TEST_PORT, 'a_new_policy_id')) self.assertFalse( self.policy_map.has_policy_changed(TEST_PORT, TEST_POLICY.id)) neutron-12.1.1/neutron/tests/unit/agent/l2/extensions/test_fdb_population.py0000664000175000017500000002161013553660047027364 0ustar zuulzuul00000000000000# Copyright (c) 2016 Mellanox Technologies, Ltd # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import mock from neutron_lib import constants from neutron_lib.utils import helpers from oslo_config import cfg import six from neutron.agent.l2.extensions.fdb_population import ( FdbPopulationAgentExtension) from neutron.plugins.ml2.drivers.linuxbridge.agent.common import ( constants as linux_bridge_constants) from neutron.plugins.ml2.drivers.openvswitch.agent.common import ( constants as ovs_constants) from neutron.tests import base class FdbPopulationExtensionTestCase(base.BaseTestCase): UPDATE_MSG = {u'device_owner': constants.DEVICE_OWNER_ROUTER_INTF, u'physical_network': u'physnet1', u'mac_address': u'fa:16:3e:ba:bc:21', u'port_id': u'17ceda02-43e1-48d8-beb6-35885b20cae6'} DELETE_MSG = {u'port_id': u'17ceda02-43e1-48d8-beb6-35885b20cae6'} FDB_TABLE = ("aa:aa:aa:aa:aa:aa self permanent\n" "bb:bb:bb:bb:bb:bb self permanent") def setUp(self): super(FdbPopulationExtensionTestCase, self).setUp() cfg.CONF.set_override('shared_physical_device_mappings', ['physnet1:p1p1'], 'FDB') self.DEVICE = self._get_existing_device() def _get_existing_device(self): device_mappings = helpers.parse_mappings( cfg.CONF.FDB.shared_physical_device_mappings, unique_keys=False) DEVICES = six.next(six.itervalues(device_mappings)) return DEVICES[0] def _get_fdb_extension(self, mock_execute, fdb_table): mock_execute.return_value = fdb_table fdb_pop = FdbPopulationAgentExtension() fdb_pop.initialize(None, ovs_constants.EXTENSION_DRIVER_TYPE) return fdb_pop @mock.patch('neutron.agent.linux.utils.execute') def test_initialize(self, mock_execute): fdb_extension = FdbPopulationAgentExtension() fdb_extension.initialize(None, ovs_constants.EXTENSION_DRIVER_TYPE) fdb_extension.initialize(None, linux_bridge_constants.EXTENSION_DRIVER_TYPE) @mock.patch('neutron.agent.linux.utils.execute') def test_initialize_invalid_agent(self, mock_execute): fdb_extension = FdbPopulationAgentExtension() self.assertRaises(SystemExit, fdb_extension.initialize, None, 'sriov') @mock.patch('neutron.agent.linux.utils.execute') def test_construct_empty_fdb_table(self, mock_execute): self._get_fdb_extension(mock_execute, fdb_table='') cmd = ['bridge', 'fdb', 'show', 'dev', self.DEVICE] mock_execute.assert_called_once_with(cmd, run_as_root=True) @mock.patch('neutron.agent.linux.utils.execute') def test_construct_existing_fdb_table(self, mock_execute): fdb_extension = self._get_fdb_extension(mock_execute, fdb_table=self.FDB_TABLE) cmd = ['bridge', 'fdb', 'show', 'dev', self.DEVICE] mock_execute.assert_called_once_with(cmd, run_as_root=True) updated_macs_for_device = ( fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE)) macs = [line.split()[0] for line in self.FDB_TABLE.split('\n')] for mac in macs: self.assertIn(mac, updated_macs_for_device) @mock.patch('neutron.agent.linux.utils.execute') def test_update_port_add_rule(self, mock_execute): fdb_extension = self._get_fdb_extension(mock_execute, self.FDB_TABLE) mock_execute.reset_mock() fdb_extension.handle_port(context=None, details=self.UPDATE_MSG) cmd = ['bridge', 'fdb', 'add', self.UPDATE_MSG['mac_address'], 'dev', self.DEVICE] mock_execute.assert_called_once_with(cmd, run_as_root=True) updated_macs_for_device = ( fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE)) mac = self.UPDATE_MSG['mac_address'] self.assertIn(mac, updated_macs_for_device) @mock.patch('neutron.agent.linux.utils.execute') def test_update_port_changed_mac(self, mock_execute): fdb_extension = self._get_fdb_extension(mock_execute, self.FDB_TABLE) mock_execute.reset_mock() mac = self.UPDATE_MSG['mac_address'] updated_mac = 'fa:16:3e:ba:bc:33' commands = [] fdb_extension.handle_port(context=None, details=self.UPDATE_MSG) commands.append(['bridge', 'fdb', 'add', mac, 'dev', self.DEVICE]) self.UPDATE_MSG['mac_address'] = updated_mac fdb_extension.handle_port(context=None, details=self.UPDATE_MSG) commands.append(['bridge', 'fdb', 'delete', mac, 'dev', self.DEVICE]) commands.append(['bridge', 'fdb', 'add', updated_mac, 'dev', self.DEVICE]) calls = [] for cmd in commands: calls.append(mock.call(cmd, run_as_root=True)) mock_execute.assert_has_calls(calls) updated_macs_for_device = ( fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE)) self.assertIn(updated_mac, updated_macs_for_device) self.assertNotIn(mac, updated_macs_for_device) @mock.patch('neutron.agent.linux.utils.execute') def test_unpermitted_device_owner(self, mock_execute): fdb_extension = self._get_fdb_extension(mock_execute, '') mock_execute.reset_mock() details = copy.deepcopy(self.UPDATE_MSG) details['device_owner'] = constants.DEVICE_OWNER_LOADBALANCER fdb_extension.handle_port(context=None, details=details) self.assertFalse(mock_execute.called) updated_macs_for_device = ( fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE)) mac = self.UPDATE_MSG['mac_address'] self.assertNotIn(mac, updated_macs_for_device) @mock.patch('neutron.agent.linux.utils.execute') def test_catch_init_exception(self, mock_execute): mock_execute.side_effect = RuntimeError fdb_extension = self._get_fdb_extension(mock_execute, '') updated_macs_for_device = ( fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE)) self.assertIsNone(updated_macs_for_device) @mock.patch('neutron.agent.linux.utils.execute') def test_catch_update_port_exception(self, mock_execute): fdb_extension = self._get_fdb_extension(mock_execute, '') mock_execute.side_effect = RuntimeError fdb_extension.handle_port(context=None, details=self.UPDATE_MSG) updated_macs_for_device = ( fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE)) mac = self.UPDATE_MSG['mac_address'] self.assertNotIn(mac, updated_macs_for_device) @mock.patch('neutron.agent.linux.utils.execute') def test_catch_delete_port_exception(self, mock_execute): fdb_extension = self._get_fdb_extension(mock_execute, '') fdb_extension.handle_port(context=None, details=self.UPDATE_MSG) mock_execute.side_effect = RuntimeError fdb_extension.delete_port(context=None, details=self.DELETE_MSG) updated_macs_for_device = ( fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE)) mac = self.UPDATE_MSG['mac_address'] self.assertIn(mac, updated_macs_for_device) @mock.patch('neutron.agent.linux.utils.execute') def test_delete_port(self, mock_execute): fdb_extension = self._get_fdb_extension(mock_execute, '') fdb_extension.handle_port(context=None, details=self.UPDATE_MSG) mock_execute.reset_mock() fdb_extension.delete_port(context=None, details=self.DELETE_MSG) cmd = ['bridge', 'fdb', 'delete', self.UPDATE_MSG['mac_address'], 'dev', self.DEVICE] mock_execute.assert_called_once_with(cmd, run_as_root=True) @mock.patch('neutron.agent.linux.utils.execute') def test_multiple_devices(self, mock_execute): cfg.CONF.set_override('shared_physical_device_mappings', ['physnet1:p1p1', 'physnet1:p2p2'], 'FDB') fdb_extension = self._get_fdb_extension(mock_execute, '') fdb_extension.handle_port(context=None, details=self.UPDATE_MSG) mac = self.UPDATE_MSG['mac_address'] calls = [] cmd = ['bridge', 'fdb', 'add', mac, 'dev', 'p1p1'] calls.append(mock.call(cmd, run_as_root=True)) cmd = ['bridge', 'fdb', 'add', mac, 'dev', 'p2p2'] calls.append(mock.call(cmd, run_as_root=True)) mock_execute.assert_has_calls(calls, any_order=True) neutron-12.1.1/neutron/tests/unit/agent/l2/__init__.py0000664000175000017500000000000013553660046022645 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/l2/test_l2_agent_extensions_manager.py0000664000175000017500000000363513553660046027632 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg from neutron.agent.l2 import l2_agent_extensions_manager as l2_ext_manager from neutron.tests import base class TestL2AgentExtensionsManager(base.BaseTestCase): def setUp(self): super(TestL2AgentExtensionsManager, self).setUp() mock.patch('neutron.agent.l2.extensions.qos.QosAgentExtension', autospec=True).start() conf = cfg.CONF l2_ext_manager.register_opts(conf) cfg.CONF.set_override('extensions', ['qos'], 'agent') self.manager = l2_ext_manager.L2AgentExtensionsManager(conf) def _get_extension(self): return self.manager.extensions[0].obj def test_initialize(self): connection = object() self.manager.initialize(connection, 'fake_driver_type') ext = self._get_extension() ext.initialize.assert_called_once_with(connection, 'fake_driver_type') def test_handle_port(self): context = object() data = object() self.manager.handle_port(context, data) ext = self._get_extension() ext.handle_port.assert_called_once_with(context, data) def test_delete_port(self): context = object() data = object() self.manager.delete_port(context, data) ext = self._get_extension() ext.delete_port.assert_called_once_with(context, data) neutron-12.1.1/neutron/tests/unit/agent/test_securitygroups_rpc.py0000664000175000017500000044467713553660047025644 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import contextlib import mock import netaddr from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef from neutron_lib import constants as const from neutron_lib import context from neutron_lib.plugins import directory from oslo_config import cfg import oslo_messaging from testtools import matchers import webob.exc from neutron.agent import firewall as firewall_base from neutron.agent.linux import ip_conntrack from neutron.agent.linux import iptables_manager from neutron.agent import securitygroups_rpc as sg_rpc from neutron.api.rpc.handlers import securitygroups_rpc from neutron.common import rpc as n_rpc from neutron.db import securitygroups_rpc_base as sg_db_rpc from neutron.extensions import securitygroup as ext_sg from neutron.tests import base from neutron.tests import tools from neutron.tests.unit.extensions import test_securitygroup as test_sg FAKE_PREFIX = {const.IPv4: '10.0.0.0/24', const.IPv6: '2001:db8::/64'} FAKE_IP = {const.IPv4: '10.0.0.1', const.IPv6: 'fe80::1', 'IPv6_GLOBAL': '2001:db8::1', 'IPv6_LLA': 'fe80::123', 'IPv6_DHCP': '2001:db8::3'} TEST_PLUGIN_CLASS = ('neutron.tests.unit.agent.test_securitygroups_rpc.' 'SecurityGroupRpcTestPlugin') FIREWALL_BASE_PACKAGE = 'neutron.agent.linux.iptables_firewall.' FIREWALL_IPTABLES_DRIVER = FIREWALL_BASE_PACKAGE + 'IptablesFirewallDriver' FIREWALL_HYBRID_DRIVER = (FIREWALL_BASE_PACKAGE + 'OVSHybridIptablesFirewallDriver') FIREWALL_NOOP_DRIVER = 'neutron.agent.firewall.NoopFirewallDriver' def ingress_address_assignment_rules(port): rules = [] v4_addrs = [ip['ip_address'] for ip in port['port']['fixed_ips'] if netaddr.IPNetwork(ip['ip_address']).version == 4] v6_addrs = [ip['ip_address'] for ip in port['port']['fixed_ips'] if netaddr.IPNetwork(ip['ip_address']).version == 6] if v6_addrs: rules.append({'direction': 'ingress', 'ethertype': 'IPv6', 'protocol': 'ipv6-icmp', 'source_port_range_min': 134}) for dest in v4_addrs + ['255.255.255.255']: rules.append({'direction': 'ingress', 'ethertype': 'IPv4', 'port_range_max': 68, 'port_range_min': 68, 'protocol': 'udp', 'source_port_range_max': 67, 'source_port_range_min': 67, 'dest_ip_prefix': '%s/32' % dest}) for dest in v6_addrs: rules.append({'direction': 'ingress', 'ethertype': 'IPv6', 'port_range_max': 546, 'port_range_min': 546, 'protocol': 'udp', 'source_port_range_max': 547, 'source_port_range_min': 547, 'dest_ip_prefix': '%s/128' % dest}) for dest in ['fe80::/64']: rules.append({'direction': 'ingress', 'ethertype': 'IPv6', 'port_range_max': 546, 'port_range_min': 546, 'protocol': 'udp', 'source_port_range_max': 547, 'source_port_range_min': 547, 'dest_ip_prefix': '%s' % dest}) return rules def set_enable_security_groups(enabled): cfg.CONF.set_override('enable_security_group', enabled, group='SECURITYGROUP') def set_firewall_driver(firewall_driver): cfg.CONF.set_override('firewall_driver', firewall_driver, group='SECURITYGROUP') class FakeFirewallDriver(firewall_base.FirewallDriver): """Fake FirewallDriver FirewallDriver is base class for other types of drivers. To be able to use it in tests, it's needed to overwrite all abstract methods. """ def prepare_port_filter(self, port): raise NotImplementedError() def update_port_filter(self, port): raise NotImplementedError() class SecurityGroupRpcTestPlugin(test_sg.SecurityGroupTestPlugin, sg_db_rpc.SecurityGroupServerRpcMixin): def __init__(self): super(SecurityGroupRpcTestPlugin, self).__init__() self.notifier = mock.Mock() self.devices = {} def create_port(self, context, port): result = super(SecurityGroupRpcTestPlugin, self).create_port(context, port) self.devices[result['id']] = result self.notify_security_groups_member_updated(context, result) return result def update_port(self, context, id, port): original_port = self.get_port(context, id) updated_port = super(SecurityGroupRpcTestPlugin, self).update_port(context, id, port) self.devices[id] = updated_port self.update_security_group_on_port( context, id, port, original_port, updated_port) return updated_port def delete_port(self, context, id): port = self.get_port(context, id) super(SecurityGroupRpcTestPlugin, self).delete_port(context, id) self.notify_security_groups_member_updated(context, port) del self.devices[id] def get_port_from_device(self, context, device): device = self.devices.get(device) if device: device['security_group_rules'] = [] device['security_group_source_groups'] = [] device['fixed_ips'] = [ip['ip_address'] for ip in device['fixed_ips']] return device class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase): def setUp(self, plugin=None): plugin = plugin or TEST_PLUGIN_CLASS set_firewall_driver(FIREWALL_NOOP_DRIVER) super(SGServerRpcCallBackTestCase, self).setUp(plugin) self.notifier = directory.get_plugin().notifier self.rpc = securitygroups_rpc.SecurityGroupServerRpcCallback() def _test_security_group_port(self, device_owner, gw_ip, cidr, ip_version, ip_address): with self.network() as net: with self.subnet(net, gateway_ip=gw_ip, cidr=cidr, ip_version=ip_version) as subnet: kwargs = { 'fixed_ips': [{'subnet_id': subnet['subnet']['id'], 'ip_address': ip_address}]} if device_owner: kwargs['device_owner'] = device_owner res = self._create_port( self.fmt, net['network']['id'], **kwargs) res = self.deserialize(self.fmt, res) port_id = res['port']['id'] if device_owner in const.ROUTER_INTERFACE_OWNERS: data = {'port': {'fixed_ips': []}} req = self.new_update_request('ports', data, port_id) res = self.deserialize(self.fmt, req.get_response(self.api)) self._delete('ports', port_id) def _test_sg_rules_for_devices_ipv4_ingress_port_range( self, min_port, max_port): fake_prefix = FAKE_PREFIX[const.IPv4] with self.network() as n,\ self.subnet(n),\ self.security_group() as sg1: sg1_id = sg1['security_group']['id'] rule1 = self._build_security_group_rule( sg1_id, 'ingress', const.PROTO_NAME_TCP, str(min_port), str(max_port)) rule2 = self._build_security_group_rule( sg1_id, 'ingress', const.PROTO_NAME_TCP, '23', '23', fake_prefix) rules = { 'security_group_rules': [rule1['security_group_rule'], rule2['security_group_rule']]} res = self._create_security_group_rule(self.fmt, rules) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) res1 = self._create_port( self.fmt, n['network']['id'], security_groups=[sg1_id]) ports_rest1 = self.deserialize(self.fmt, res1) port_id1 = ports_rest1['port']['id'] self.rpc.devices = {port_id1: ports_rest1['port']} devices = [port_id1, 'no_exist_device'] ctx = context.get_admin_context() ports_rpc = self.rpc.security_group_rules_for_devices( ctx, devices=devices) port_rpc = ports_rpc[port_id1] expected = [{'direction': 'egress', 'ethertype': const.IPv4, 'security_group_id': sg1_id}, {'direction': 'egress', 'ethertype': const.IPv6, 'security_group_id': sg1_id}, {'direction': 'ingress', 'protocol': const.PROTO_NAME_TCP, 'ethertype': const.IPv4, 'port_range_max': max_port, 'security_group_id': sg1_id, 'port_range_min': min_port}, {'direction': 'ingress', 'protocol': const.PROTO_NAME_TCP, 'ethertype': const.IPv4, 'port_range_max': 23, 'security_group_id': sg1_id, 'port_range_min': 23, 'source_ip_prefix': fake_prefix}, ] + ingress_address_assignment_rules(ports_rest1) self.assertEqual(port_rpc['security_group_rules'], expected) self._delete('ports', port_id1) def test_sg_rules_for_devices_ipv4_ingress_port_range_min_port_1(self): self._test_sg_rules_for_devices_ipv4_ingress_port_range(1, 10) def test_security_group_info_for_ports_with_no_rules(self): with self.network() as n,\ self.subnet(n),\ self.security_group() as sg: sg_id = sg['security_group']['id'] self._delete_default_security_group_egress_rules(sg_id) res = self._create_port( self.fmt, n['network']['id'], security_groups=[sg_id]) ports_rest = self.deserialize(self.fmt, res) port_id = ports_rest['port']['id'] self.rpc.devices = {port_id: ports_rest['port']} devices = [port_id] ctx = context.get_admin_context() sg_info = self.rpc.security_group_info_for_devices( ctx, devices=devices) expected = {sg_id: []} self.assertEqual(expected, sg_info['security_groups']) self._delete('ports', port_id) @contextlib.contextmanager def _port_with_addr_pairs_and_security_group(self): plugin_obj = directory.get_plugin() if ('allowed-address-pairs' not in plugin_obj.supported_extension_aliases): self.skipTest("Test depends on allowed-address-pairs extension") fake_prefix = FAKE_PREFIX['IPv4'] with self.network() as n,\ self.subnet(n),\ self.security_group() as sg1: sg1_id = sg1['security_group']['id'] rule1 = self._build_security_group_rule( sg1_id, 'ingress', 'tcp', '22', '22', remote_group_id=sg1_id) rule2 = self._build_security_group_rule( sg1_id, 'ingress', 'tcp', '23', '23', fake_prefix) rules = { 'security_group_rules': [rule1['security_group_rule'], rule2['security_group_rule']]} res = self._create_security_group_rule(self.fmt, rules) self.deserialize(self.fmt, res) self.assertEqual(res.status_int, 201) address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.1.0/24'}, {'mac_address': '00:00:00:00:00:01', 'ip_address': '11.0.0.1'}] res1 = self._create_port( self.fmt, n['network']['id'], security_groups=[sg1_id], arg_list=(addr_apidef.ADDRESS_PAIRS,), allowed_address_pairs=address_pairs) yield self.deserialize(self.fmt, res1) def test_security_group_info_for_devices_ipv4_addr_pair(self): with self._port_with_addr_pairs_and_security_group() as port: port_id = port['port']['id'] sg_id = port['port']['security_groups'][0] devices = [port_id, 'no_exist_device'] ctx = context.get_admin_context() # verify that address pairs are included in remote SG IPs sg_member_ips = self.rpc.security_group_info_for_devices( ctx, devices=devices)['sg_member_ips'] expected_member_ips = [ '10.0.1.0/24', '11.0.0.1', port['port']['fixed_ips'][0]['ip_address']] self.assertEqual(sorted(expected_member_ips), sorted(sg_member_ips[sg_id]['IPv4'])) self._delete('ports', port_id) def test_security_group_rules_for_devices_ipv4_ingress_addr_pair(self): fake_prefix = FAKE_PREFIX[const.IPv4] with self._port_with_addr_pairs_and_security_group() as port: port_id = port['port']['id'] sg_id = port['port']['security_groups'][0] devices = [port_id, 'no_exist_device'] ctx = context.get_admin_context() ports_rpc = self.rpc.security_group_rules_for_devices( ctx, devices=devices) port_rpc = ports_rpc[port_id] expected = [{'direction': 'egress', 'ethertype': 'IPv4', 'security_group_id': sg_id}, {'direction': 'egress', 'ethertype': 'IPv6', 'security_group_id': sg_id}, {'direction': 'ingress', 'protocol': 'tcp', 'ethertype': 'IPv4', 'port_range_max': 22, 'remote_group_id': sg_id, 'security_group_id': sg_id, 'source_ip_prefix': '11.0.0.1/32', 'port_range_min': 22}, {'direction': 'ingress', 'protocol': 'tcp', 'ethertype': 'IPv4', 'port_range_max': 22, 'remote_group_id': sg_id, 'security_group_id': sg_id, 'source_ip_prefix': '10.0.1.0/24', 'port_range_min': 22}, {'direction': 'ingress', 'protocol': 'tcp', 'ethertype': 'IPv4', 'port_range_max': 23, 'security_group_id': sg_id, 'port_range_min': 23, 'source_ip_prefix': fake_prefix}, ] + ingress_address_assignment_rules(port) expected = tools.UnorderedList(expected) self.assertEqual(expected, port_rpc['security_group_rules']) self.assertEqual(port['port']['allowed_address_pairs'], port_rpc['allowed_address_pairs']) self._delete('ports', port_id) def test_security_group_rules_for_devices_ipv4_egress(self): fake_prefix = FAKE_PREFIX[const.IPv4] with self.network() as n,\ self.subnet(n),\ self.security_group() as sg1: sg1_id = sg1['security_group']['id'] rule1 = self._build_security_group_rule( sg1_id, 'egress', const.PROTO_NAME_TCP, '22', '22') rule2 = self._build_security_group_rule( sg1_id, 'egress', const.PROTO_NAME_UDP, '23', '23', fake_prefix) rules = { 'security_group_rules': [rule1['security_group_rule'], rule2['security_group_rule']]} res = self._create_security_group_rule(self.fmt, rules) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) res1 = self._create_port( self.fmt, n['network']['id'], security_groups=[sg1_id]) ports_rest1 = self.deserialize(self.fmt, res1) port_id1 = ports_rest1['port']['id'] self.rpc.devices = {port_id1: ports_rest1['port']} devices = [port_id1, 'no_exist_device'] ctx = context.get_admin_context() ports_rpc = self.rpc.security_group_rules_for_devices( ctx, devices=devices) port_rpc = ports_rpc[port_id1] expected = [{'direction': 'egress', 'ethertype': const.IPv4, 'security_group_id': sg1_id}, {'direction': 'egress', 'ethertype': const.IPv6, 'security_group_id': sg1_id}, {'direction': 'egress', 'protocol': const.PROTO_NAME_TCP, 'ethertype': const.IPv4, 'port_range_max': 22, 'security_group_id': sg1_id, 'port_range_min': 22}, {'direction': 'egress', 'protocol': const.PROTO_NAME_UDP, 'ethertype': const.IPv4, 'port_range_max': 23, 'security_group_id': sg1_id, 'port_range_min': 23, 'dest_ip_prefix': fake_prefix}, ] + ingress_address_assignment_rules(ports_rest1) self.assertEqual(port_rpc['security_group_rules'], expected) self._delete('ports', port_id1) def test_security_group_rules_for_devices_ipv4_source_group(self): with self.network() as n,\ self.subnet(n),\ self.security_group() as sg1,\ self.security_group() as sg2: sg1_id = sg1['security_group']['id'] sg2_id = sg2['security_group']['id'] rule1 = self._build_security_group_rule( sg1_id, 'ingress', const.PROTO_NAME_TCP, '24', '25', remote_group_id=sg2['security_group']['id']) rules = { 'security_group_rules': [rule1['security_group_rule']]} res = self._create_security_group_rule(self.fmt, rules) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) res1 = self._create_port( self.fmt, n['network']['id'], security_groups=[sg1_id, sg2_id]) ports_rest1 = self.deserialize(self.fmt, res1) port_id1 = ports_rest1['port']['id'] self.rpc.devices = {port_id1: ports_rest1['port']} devices = [port_id1, 'no_exist_device'] res2 = self._create_port( self.fmt, n['network']['id'], security_groups=[sg2_id]) ports_rest2 = self.deserialize(self.fmt, res2) port_id2 = ports_rest2['port']['id'] port_fixed_ip2 = ports_rest2['port']['fixed_ips'][0]['ip_address'] ctx = context.get_admin_context() ports_rpc = self.rpc.security_group_rules_for_devices( ctx, devices=devices) port_rpc = ports_rpc[port_id1] expected = [{'direction': 'egress', 'ethertype': const.IPv4, 'security_group_id': sg1_id}, {'direction': 'egress', 'ethertype': const.IPv6, 'security_group_id': sg1_id}, {'direction': 'egress', 'ethertype': const.IPv4, 'security_group_id': sg2_id}, {'direction': 'egress', 'ethertype': const.IPv6, 'security_group_id': sg2_id}, {'direction': u'ingress', 'source_ip_prefix': port_fixed_ip2 + '/32', 'protocol': const.PROTO_NAME_TCP, 'ethertype': const.IPv4, 'port_range_max': 25, 'port_range_min': 24, 'remote_group_id': sg2_id, 'security_group_id': sg1_id}, ] + ingress_address_assignment_rules(ports_rest1) self.assertEqual(expected, port_rpc['security_group_rules']) self._delete('ports', port_id1) self._delete('ports', port_id2) def test_security_group_info_for_devices_ipv4_source_group(self): with self.network() as n,\ self.subnet(n),\ self.security_group() as sg1,\ self.security_group() as sg2: sg1_id = sg1['security_group']['id'] sg2_id = sg2['security_group']['id'] rule1 = self._build_security_group_rule( sg1_id, 'ingress', const.PROTO_NAME_TCP, '24', '25', remote_group_id=sg2['security_group']['id']) rules = { 'security_group_rules': [rule1['security_group_rule']]} res = self._create_security_group_rule(self.fmt, rules) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) res1 = self._create_port( self.fmt, n['network']['id'], security_groups=[sg1_id]) ports_rest1 = self.deserialize(self.fmt, res1) port_id1 = ports_rest1['port']['id'] self.rpc.devices = {port_id1: ports_rest1['port']} devices = [port_id1, 'no_exist_device'] res2 = self._create_port( self.fmt, n['network']['id'], security_groups=[sg2_id]) ports_rest2 = self.deserialize(self.fmt, res2) port_id2 = ports_rest2['port']['id'] port_ip2 = ports_rest2['port']['fixed_ips'][0]['ip_address'] ctx = context.get_admin_context() ports_rpc = self.rpc.security_group_info_for_devices( ctx, devices=devices) expected = { 'security_groups': {sg1_id: [ {'direction': 'egress', 'ethertype': const.IPv4}, {'direction': 'egress', 'ethertype': const.IPv6}, {'direction': u'ingress', 'protocol': const.PROTO_NAME_TCP, 'ethertype': const.IPv4, 'port_range_max': 25, 'port_range_min': 24, 'remote_group_id': sg2_id} ]}, 'sg_member_ips': {sg2_id: { 'IPv4': set([port_ip2]), 'IPv6': set(), }} } self.assertEqual(expected['security_groups'], ports_rpc['security_groups']) self.assertEqual(expected['sg_member_ips'][sg2_id]['IPv4'], ports_rpc['sg_member_ips'][sg2_id]['IPv4']) self._delete('ports', port_id1) self._delete('ports', port_id2) def test_security_group_rules_for_devices_ipv6_ingress(self): fake_prefix = FAKE_PREFIX[const.IPv6] fake_gateway = FAKE_IP[const.IPv6] with self.network() as n,\ self.subnet(n, gateway_ip=fake_gateway, cidr=fake_prefix, ip_version=6 ) as subnet_v6,\ self.security_group() as sg1: sg1_id = sg1['security_group']['id'] rule1 = self._build_security_group_rule( sg1_id, 'ingress', const.PROTO_NAME_TCP, '22', '22', ethertype=const.IPv6) rule2 = self._build_security_group_rule( sg1_id, 'ingress', const.PROTO_NAME_UDP, '23', '23', fake_prefix, ethertype=const.IPv6) rules = { 'security_group_rules': [rule1['security_group_rule'], rule2['security_group_rule']]} res = self._create_security_group_rule(self.fmt, rules) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) self._create_port( self.fmt, n['network']['id'], fixed_ips=[{'subnet_id': subnet_v6['subnet']['id'], 'ip_address': FAKE_IP['IPv6_DHCP']}], device_owner=const.DEVICE_OWNER_DHCP, security_groups=[sg1_id]) res1 = self._create_port( self.fmt, n['network']['id'], fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}], security_groups=[sg1_id]) ports_rest1 = self.deserialize(self.fmt, res1) port_id1 = ports_rest1['port']['id'] self.rpc.devices = {port_id1: ports_rest1['port']} devices = [port_id1, 'no_exist_device'] ctx = context.get_admin_context() ports_rpc = self.rpc.security_group_rules_for_devices( ctx, devices=devices) port_rpc = ports_rpc[port_id1] source_port, dest_port, ethertype = sg_db_rpc.DHCP_RULE_PORT[6] expected = [{'direction': 'egress', 'ethertype': const.IPv4, 'security_group_id': sg1_id}, {'direction': 'egress', 'ethertype': const.IPv6, 'security_group_id': sg1_id}, {'direction': 'ingress', 'protocol': const.PROTO_NAME_TCP, 'ethertype': const.IPv6, 'port_range_max': 22, 'security_group_id': sg1_id, 'port_range_min': 22}, {'direction': 'ingress', 'protocol': const.PROTO_NAME_UDP, 'ethertype': const.IPv6, 'port_range_max': 23, 'security_group_id': sg1_id, 'port_range_min': 23, 'source_ip_prefix': fake_prefix}, ] + ingress_address_assignment_rules(ports_rest1) self.assertEqual(port_rpc['security_group_rules'], expected) self._delete('ports', port_id1) def test_security_group_info_for_devices_only_ipv6_rule(self): with self.network() as n,\ self.subnet(n),\ self.security_group() as sg1: sg1_id = sg1['security_group']['id'] rule1 = self._build_security_group_rule( sg1_id, 'ingress', const.PROTO_NAME_TCP, '22', '22', remote_group_id=sg1_id, ethertype=const.IPv6) rules = { 'security_group_rules': [rule1['security_group_rule']]} self._make_security_group_rule(self.fmt, rules) res1 = self._create_port( self.fmt, n['network']['id'], security_groups=[sg1_id]) ports_rest1 = self.deserialize(self.fmt, res1) port_id1 = ports_rest1['port']['id'] self.rpc.devices = {port_id1: ports_rest1['port']} devices = [port_id1, 'no_exist_device'] ctx = context.get_admin_context() ports_rpc = self.rpc.security_group_info_for_devices( ctx, devices=devices) expected = { 'security_groups': {sg1_id: [ {'direction': 'egress', 'ethertype': const.IPv4}, {'direction': 'egress', 'ethertype': const.IPv6}, {'direction': u'ingress', 'protocol': const.PROTO_NAME_TCP, 'ethertype': const.IPv6, 'port_range_max': 22, 'port_range_min': 22, 'remote_group_id': sg1_id} ]}, 'sg_member_ips': {sg1_id: { 'IPv6': set(), }} } self.assertEqual(expected['security_groups'], ports_rpc['security_groups']) self.assertEqual(expected['sg_member_ips'][sg1_id]['IPv6'], ports_rpc['sg_member_ips'][sg1_id]['IPv6']) self._delete('ports', port_id1) def test_security_group_rules_for_devices_ipv6_egress(self): fake_prefix = FAKE_PREFIX[const.IPv6] fake_gateway = FAKE_IP[const.IPv6] with self.network() as n,\ self.subnet(n, gateway_ip=fake_gateway, cidr=fake_prefix, ip_version=6 ) as subnet_v6,\ self.security_group() as sg1: sg1_id = sg1['security_group']['id'] rule1 = self._build_security_group_rule( sg1_id, 'egress', const.PROTO_NAME_TCP, '22', '22', ethertype=const.IPv6) rule2 = self._build_security_group_rule( sg1_id, 'egress', const.PROTO_NAME_UDP, '23', '23', fake_prefix, ethertype=const.IPv6) rules = { 'security_group_rules': [rule1['security_group_rule'], rule2['security_group_rule']]} self._make_security_group_rule(self.fmt, rules) ports_rest1 = self._make_port( self.fmt, n['network']['id'], fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}], security_groups=[sg1_id]) port_id1 = ports_rest1['port']['id'] self.rpc.devices = {port_id1: ports_rest1['port']} devices = [port_id1, 'no_exist_device'] ctx = context.get_admin_context() ports_rpc = self.rpc.security_group_rules_for_devices( ctx, devices=devices) port_rpc = ports_rpc[port_id1] expected = [{'direction': 'egress', 'ethertype': const.IPv4, 'security_group_id': sg1_id}, {'direction': 'egress', 'ethertype': const.IPv6, 'security_group_id': sg1_id}, {'direction': 'egress', 'protocol': const.PROTO_NAME_TCP, 'ethertype': const.IPv6, 'port_range_max': 22, 'security_group_id': sg1_id, 'port_range_min': 22}, {'direction': 'egress', 'protocol': const.PROTO_NAME_UDP, 'ethertype': const.IPv6, 'port_range_max': 23, 'security_group_id': sg1_id, 'port_range_min': 23, 'dest_ip_prefix': fake_prefix}, ] + ingress_address_assignment_rules(ports_rest1) self.assertEqual(port_rpc['security_group_rules'], expected) self._delete('ports', port_id1) def test_security_group_rules_for_devices_ipv6_source_group(self): fake_prefix = FAKE_PREFIX[const.IPv6] fake_gateway = FAKE_IP[const.IPv6] with self.network() as n,\ self.subnet(n, gateway_ip=fake_gateway, cidr=fake_prefix, ip_version=6 ) as subnet_v6,\ self.security_group() as sg1,\ self.security_group() as sg2: sg1_id = sg1['security_group']['id'] sg2_id = sg2['security_group']['id'] rule1 = self._build_security_group_rule( sg1_id, 'ingress', const.PROTO_NAME_TCP, '24', '25', ethertype=const.IPv6, remote_group_id=sg2['security_group']['id']) rules = { 'security_group_rules': [rule1['security_group_rule']]} self._make_security_group_rule(self.fmt, rules) ports_rest1 = self._make_port( self.fmt, n['network']['id'], fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}], security_groups=[sg1_id, sg2_id]) port_id1 = ports_rest1['port']['id'] self.rpc.devices = {port_id1: ports_rest1['port']} devices = [port_id1, 'no_exist_device'] ports_rest2 = self._make_port( self.fmt, n['network']['id'], fixed_ips=[{'subnet_id': subnet_v6['subnet']['id']}], security_groups=[sg2_id]) port_id2 = ports_rest2['port']['id'] port_ip2 = ports_rest2['port']['fixed_ips'][0]['ip_address'] ctx = context.get_admin_context() ports_rpc = self.rpc.security_group_rules_for_devices( ctx, devices=devices) port_rpc = ports_rpc[port_id1] expected = [{'direction': 'egress', 'ethertype': const.IPv4, 'security_group_id': sg1_id}, {'direction': 'egress', 'ethertype': const.IPv6, 'security_group_id': sg1_id}, {'direction': 'egress', 'ethertype': const.IPv4, 'security_group_id': sg2_id}, {'direction': 'egress', 'ethertype': const.IPv6, 'security_group_id': sg2_id}, {'direction': 'ingress', 'source_ip_prefix': port_ip2 + '/128', 'protocol': const.PROTO_NAME_TCP, 'ethertype': const.IPv6, 'port_range_max': 25, 'port_range_min': 24, 'remote_group_id': sg2_id, 'security_group_id': sg1_id}, ] + ingress_address_assignment_rules(ports_rest1) self.assertEqual(expected, port_rpc['security_group_rules']) self._delete('ports', port_id1) self._delete('ports', port_id2) class SecurityGroupAgentRpcTestCaseForNoneDriver(base.BaseTestCase): def test_init_firewall_with_none_driver(self): set_enable_security_groups(False) agent = sg_rpc.SecurityGroupAgentRpc( context=None, plugin_rpc=mock.Mock()) self.assertEqual(agent.firewall.__class__.__name__, 'NoopFirewallDriver') def test_get_trusted_devices(self): agent = sg_rpc.SecurityGroupAgentRpc( context=None, plugin_rpc=mock.Mock()) device_ids = ['port_1_id', 'tap_2', 'tap_3', 'port_4_id'] devices = { 'port_1_id': {'device': 'tap_1'}, 'port_3_id': {'device': 'tap_3'}, } trusted_devices = agent._get_trusted_devices( device_ids, devices) self.assertEqual(['tap_2', 'port_4_id'], trusted_devices) class BaseSecurityGroupAgentRpcTestCase(base.BaseTestCase): def setUp(self, defer_refresh_firewall=False): super(BaseSecurityGroupAgentRpcTestCase, self).setUp() set_firewall_driver(FIREWALL_NOOP_DRIVER) self.agent = sg_rpc.SecurityGroupAgentRpc( context=None, plugin_rpc=mock.Mock(), defer_refresh_firewall=defer_refresh_firewall) mock.patch('neutron.agent.linux.iptables_manager').start() self.default_firewall = self.agent.firewall self.firewall = mock.Mock() firewall_object = FakeFirewallDriver() self.firewall.defer_apply.side_effect = firewall_object.defer_apply self.agent.firewall = self.firewall self.fake_device = {'device': 'fake_device', 'network_id': 'fake_net', 'security_groups': ['fake_sgid1', 'fake_sgid2'], 'security_group_source_groups': ['fake_sgid2'], 'security_group_rules': [{'security_group_id': 'fake_sgid1', 'remote_group_id': 'fake_sgid2'}]} self.firewall.ports = {'fake_device': self.fake_device} self.firewall.security_group_updated = mock.Mock() class SecurityGroupAgentRpcTestCase(BaseSecurityGroupAgentRpcTestCase): def setUp(self, defer_refresh_firewall=False): super(SecurityGroupAgentRpcTestCase, self).setUp( defer_refresh_firewall) rpc = self.agent.plugin_rpc rpc.security_group_info_for_devices.side_effect = ( oslo_messaging.UnsupportedVersion('1.2')) rpc.security_group_rules_for_devices.return_value = ( self.firewall.ports) def test_prepare_and_remove_devices_filter(self): self.agent.prepare_devices_filter(['fake_device']) self.agent.remove_devices_filter(['fake_device']) # ignore device which is not filtered self.firewall.assert_has_calls([mock.call.defer_apply(), mock.call.prepare_port_filter( self.fake_device), mock.call.process_trusted_ports([]), mock.call.defer_apply(), mock.call.remove_port_filter( self.fake_device), ]) def test_prepare_devices_filter_with_noopfirewall(self): self.agent.firewall = self.default_firewall self.agent.plugin_rpc.security_group_info_for_devices = mock.Mock() self.agent.plugin_rpc.security_group_rules_for_devices = mock.Mock() self.agent.prepare_devices_filter(['fake_device']) self.assertFalse(self.agent.plugin_rpc. security_group_info_for_devices.called) self.assertFalse(self.agent.plugin_rpc. security_group_rules_for_devices.called) def test_prepare_devices_filter_with_firewall_disabled(self): cfg.CONF.set_override('enable_security_group', False, 'SECURITYGROUP') self.agent.plugin_rpc.security_group_info_for_devices = mock.Mock() self.agent.plugin_rpc.security_group_rules_for_devices = mock.Mock() self.agent.prepare_devices_filter(['fake_device']) self.assertFalse(self.agent.plugin_rpc. security_group_info_for_devices.called) self.assertFalse(self.agent.plugin_rpc. security_group_rules_for_devices.called) def test_prepare_devices_filter_with_trusted_ports(self): devices_to_filter = {k: {'device': k} for k in range(4, 8)} all_devices = range(10) expected_devices = [0, 1, 2, 3, 8, 9] self.agent._use_enhanced_rpc = True with mock.patch.object( self.agent.plugin_rpc, 'security_group_info_for_devices', return_value={ 'devices': devices_to_filter, 'security_groups': {}, 'sg_member_ips': {}}): with mock.patch.object( self.agent.firewall, 'process_trusted_ports') as m_process: self.agent.prepare_devices_filter(all_devices) m_process.assert_called_once_with(expected_devices) def test_remove_devices_filter_with_trusted_ports(self): all_devices = range(10) firewall_managed_ports = {k: k for k in range(4, 8)} trusted_port_ids = [0, 1, 2, 3, 8, 9] with mock.patch.object(self.agent, 'firewall') as mock_firewall: mock_firewall.ports = firewall_managed_ports self.agent.remove_devices_filter(all_devices) mock_firewall.remove_port_filter.assert_has_calls( [mock.call(i) for i in firewall_managed_ports.keys()]) mock_firewall.remove_trusted_ports( [mock.call([i]) for i in trusted_port_ids]) def test_security_groups_rule_updated(self): self.agent.refresh_firewall = mock.Mock() self.agent.prepare_devices_filter(['fake_port_id']) self.agent.security_groups_rule_updated(['fake_sgid1', 'fake_sgid3']) self.agent.refresh_firewall.assert_has_calls( [mock.call.refresh_firewall([self.fake_device['device']])]) self.assertFalse(self.firewall.security_group_updated.called) def test_security_groups_rule_not_updated(self): self.agent.refresh_firewall = mock.Mock() self.agent.prepare_devices_filter(['fake_port_id']) self.agent.security_groups_rule_updated(['fake_sgid3', 'fake_sgid4']) self.assertFalse(self.agent.refresh_firewall.called) self.assertFalse(self.firewall.security_group_updated.called) def test_security_groups_member_updated(self): self.agent.refresh_firewall = mock.Mock() self.agent.prepare_devices_filter(['fake_port_id']) self.agent.security_groups_member_updated(['fake_sgid2', 'fake_sgid3']) self.agent.refresh_firewall.assert_has_calls( [mock.call.refresh_firewall([self.fake_device['device']])]) self.assertFalse(self.firewall.security_group_updated.called) def test_security_groups_member_not_updated(self): self.agent.refresh_firewall = mock.Mock() self.agent.prepare_devices_filter(['fake_port_id']) self.agent.security_groups_member_updated(['fake_sgid3', 'fake_sgid4']) self.assertFalse(self.agent.refresh_firewall.called) self.assertFalse(self.firewall.security_group_updated.called) def test_refresh_firewall(self): self.agent.prepare_devices_filter(['fake_port_id']) self.agent.refresh_firewall() calls = [mock.call.defer_apply(), mock.call.prepare_port_filter(self.fake_device), mock.call.process_trusted_ports(['fake_port_id']), mock.call.defer_apply(), mock.call.update_port_filter(self.fake_device), mock.call.process_trusted_ports([])] self.firewall.assert_has_calls(calls) def test_refresh_firewall_devices(self): self.agent.prepare_devices_filter(['fake_port_id']) self.agent.refresh_firewall([self.fake_device['device']]) calls = [mock.call.defer_apply(), mock.call.prepare_port_filter(self.fake_device), mock.call.process_trusted_ports(['fake_port_id']), mock.call.defer_apply(), mock.call.update_port_filter(self.fake_device), mock.call.process_trusted_ports([])] self.firewall.assert_has_calls(calls) def test_refresh_firewall_none(self): self.agent.refresh_firewall([]) self.assertFalse(self.firewall.called) def test_refresh_firewall_with_firewall_disabled(self): cfg.CONF.set_override('enable_security_group', False, 'SECURITYGROUP') self.agent.plugin_rpc.security_group_info_for_devices = mock.Mock() self.agent.plugin_rpc.security_group_rules_for_devices = mock.Mock() self.agent.firewall.defer_apply = mock.Mock() self.agent.refresh_firewall([self.fake_device['device']]) self.assertFalse(self.agent.plugin_rpc. security_group_info_for_devices.called) self.assertFalse(self.agent.plugin_rpc. security_group_rules_for_devices.called) self.assertFalse(self.agent.firewall.defer_apply.called) def test_refresh_firewall_with_noopfirewall(self): self.agent.firewall = self.default_firewall self.agent.plugin_rpc.security_group_info_for_devices = mock.Mock() self.agent.plugin_rpc.security_group_rules_for_devices = mock.Mock() self.agent.firewall.defer_apply = mock.Mock() self.agent.refresh_firewall([self.fake_device]) self.assertFalse(self.agent.plugin_rpc. security_group_info_for_devices.called) self.assertFalse(self.agent.plugin_rpc. security_group_rules_for_devices.called) self.assertFalse(self.agent.firewall.defer_apply.called) class SecurityGroupAgentEnhancedRpcTestCase( BaseSecurityGroupAgentRpcTestCase): def setUp(self, defer_refresh_firewall=False): super(SecurityGroupAgentEnhancedRpcTestCase, self).setUp( defer_refresh_firewall=defer_refresh_firewall) fake_sg_info = { 'security_groups': collections.OrderedDict([ ('fake_sgid2', []), ('fake_sgid1', [{'remote_group_id': 'fake_sgid2'}])]), 'sg_member_ips': {'fake_sgid2': {'IPv4': [], 'IPv6': []}}, 'devices': self.firewall.ports} self.agent.plugin_rpc.security_group_info_for_devices.return_value = ( fake_sg_info) def test_prepare_and_remove_devices_filter_enhanced_rpc(self): self.agent.prepare_devices_filter(['fake_device']) self.agent.remove_devices_filter(['fake_device']) # these two mocks are too long, just use tmp_mock to replace them tmp_mock1 = mock.call.update_security_group_rules( 'fake_sgid1', [{'remote_group_id': 'fake_sgid2'}]) tmp_mock2 = mock.call.update_security_group_members( 'fake_sgid2', {'IPv4': [], 'IPv6': []}) # ignore device which is not filtered self.firewall.assert_has_calls([mock.call.defer_apply(), mock.call.update_security_group_rules( 'fake_sgid2', []), tmp_mock1, tmp_mock2, mock.call.prepare_port_filter( self.fake_device), mock.call.process_trusted_ports([]), mock.call.defer_apply(), mock.call.remove_port_filter( self.fake_device), ], any_order=True) def test_security_groups_rule_updated_enhanced_rpc(self): sg_list = ['fake_sgid1', 'fake_sgid3'] self.agent.refresh_firewall = mock.Mock() self.agent.prepare_devices_filter(['fake_port_id']) self.agent.security_groups_rule_updated(sg_list) self.agent.refresh_firewall.assert_called_once_with( [self.fake_device['device']]) self.firewall.security_group_updated.assert_called_once_with( 'sg_rule', set(sg_list)) def test_security_groups_rule_not_updated_enhanced_rpc(self): self.agent.refresh_firewall = mock.Mock() self.agent.prepare_devices_filter(['fake_port_id']) self.agent.security_groups_rule_updated(['fake_sgid3', 'fake_sgid4']) self.assertFalse(self.agent.refresh_firewall.called) self.assertFalse(self.firewall.security_group_updated.called) def test_security_groups_member_updated_enhanced_rpc(self): sg_list = ['fake_sgid2', 'fake_sgid3'] self.agent.refresh_firewall = mock.Mock() self.agent.prepare_devices_filter(['fake_port_id']) self.agent.security_groups_member_updated(sg_list) self.agent.refresh_firewall.assert_called_once_with( [self.fake_device['device']]) self.firewall.security_group_updated.assert_called_once_with( 'sg_member', set(sg_list)) def test_security_groups_member_not_updated_enhanced_rpc(self): self.agent.refresh_firewall = mock.Mock() self.agent.prepare_devices_filter(['fake_port_id']) self.agent.security_groups_member_updated( ['fake_sgid3', 'fake_sgid4']) self.assertFalse(self.agent.refresh_firewall.called) self.assertFalse(self.firewall.security_group_updated.called) def test_refresh_firewall_enhanced_rpc(self): self.agent.prepare_devices_filter(['fake_port_id']) self.agent.refresh_firewall() calls = [mock.call.defer_apply(), mock.call.update_security_group_rules('fake_sgid2', []), mock.call.update_security_group_rules( 'fake_sgid1', [{'remote_group_id': 'fake_sgid2'}]), mock.call.update_security_group_members( 'fake_sgid2', {'IPv4': [], 'IPv6': []}), mock.call.prepare_port_filter(self.fake_device), mock.call.process_trusted_ports(['fake_port_id']), mock.call.defer_apply(), mock.call.update_security_group_rules('fake_sgid2', []), mock.call.update_security_group_rules( 'fake_sgid1', [{'remote_group_id': 'fake_sgid2'}]), mock.call.update_security_group_members( 'fake_sgid2', {'IPv4': [], 'IPv6': []}), mock.call.update_port_filter(self.fake_device), mock.call.process_trusted_ports([])] self.firewall.assert_has_calls(calls, any_order=True) def test_refresh_firewall_devices_enhanced_rpc(self): self.agent.prepare_devices_filter(['fake_device']) self.agent.refresh_firewall([self.fake_device['device']]) calls = [mock.call.defer_apply(), mock.call.update_security_group_rules('fake_sgid2', []), mock.call.update_security_group_rules('fake_sgid1', [ {'remote_group_id': 'fake_sgid2'}]), mock.call.update_security_group_members('fake_sgid2', { 'IPv4': [], 'IPv6': [] }), mock.call.prepare_port_filter(self.fake_device), mock.call.process_trusted_ports([]), mock.call.defer_apply(), mock.call.update_security_group_rules('fake_sgid2', []), mock.call.update_security_group_rules('fake_sgid1', [ {'remote_group_id': 'fake_sgid2'}]), mock.call.update_security_group_members('fake_sgid2', { 'IPv4': [], 'IPv6': []}), mock.call.update_port_filter(self.fake_device), mock.call.process_trusted_ports([]), ] self.firewall.assert_has_calls(calls, any_order=True) def test_refresh_firewall_none_enhanced_rpc(self): self.agent.refresh_firewall([]) self.assertFalse(self.firewall.called) class SecurityGroupAgentRpcWithDeferredRefreshTestCase( SecurityGroupAgentRpcTestCase): def setUp(self): super(SecurityGroupAgentRpcWithDeferredRefreshTestCase, self).setUp( defer_refresh_firewall=True) @contextlib.contextmanager def add_fake_device(self, device, sec_groups, source_sec_groups=None): fake_device = {'device': device, 'security_groups': sec_groups, 'security_group_source_groups': source_sec_groups or [], 'security_group_rules': [{'security_group_id': 'fake_sgid1', 'remote_group_id': 'fake_sgid2'}]} self.firewall.ports[device] = fake_device yield del self.firewall.ports[device] def test_security_groups_rule_updated(self): self.agent.security_groups_rule_updated(['fake_sgid1', 'fake_sgid3']) self.assertIn('fake_device', self.agent.devices_to_refilter) self.assertFalse(self.firewall.security_group_updated.called) def test_multiple_security_groups_rule_updated_same_port(self): with self.add_fake_device(device='fake_device_2', sec_groups=['fake_sgidX']): self.agent.refresh_firewall = mock.Mock() self.agent.security_groups_rule_updated(['fake_sgid1']) self.agent.security_groups_rule_updated(['fake_sgid2']) self.assertIn('fake_device', self.agent.devices_to_refilter) self.assertNotIn('fake_device_2', self.agent.devices_to_refilter) self.assertFalse(self.firewall.security_group_updated.called) def test_security_groups_rule_updated_multiple_ports(self): with self.add_fake_device(device='fake_device_2', sec_groups=['fake_sgid2']): self.agent.refresh_firewall = mock.Mock() self.agent.security_groups_rule_updated(['fake_sgid1', 'fake_sgid2']) self.assertIn('fake_device', self.agent.devices_to_refilter) self.assertIn('fake_device_2', self.agent.devices_to_refilter) self.assertFalse(self.firewall.security_group_updated.called) def test_multiple_security_groups_rule_updated_multiple_ports(self): with self.add_fake_device(device='fake_device_2', sec_groups=['fake_sgid2']): self.agent.refresh_firewall = mock.Mock() self.agent.security_groups_rule_updated(['fake_sgid1']) self.agent.security_groups_rule_updated(['fake_sgid2']) self.assertIn('fake_device', self.agent.devices_to_refilter) self.assertIn('fake_device_2', self.agent.devices_to_refilter) self.assertFalse(self.firewall.security_group_updated.called) def test_security_groups_member_updated(self): self.agent.security_groups_member_updated(['fake_sgid2', 'fake_sgid3']) self.assertIn('fake_device', self.agent.devices_to_refilter) self.assertFalse(self.firewall.security_group_updated.called) def test_multiple_security_groups_member_updated_same_port(self): with self.add_fake_device(device='fake_device_2', sec_groups=['fake_sgid1', 'fake_sgid1B'], source_sec_groups=['fake_sgidX']): self.agent.refresh_firewall = mock.Mock() self.agent.security_groups_member_updated(['fake_sgid1', 'fake_sgid3']) self.agent.security_groups_member_updated(['fake_sgid2', 'fake_sgid3']) self.assertIn('fake_device', self.agent.devices_to_refilter) self.assertNotIn('fake_device_2', self.agent.devices_to_refilter) self.assertFalse(self.firewall.security_group_updated.called) def test_security_groups_member_updated_multiple_ports(self): with self.add_fake_device(device='fake_device_2', sec_groups=['fake_sgid1', 'fake_sgid1B'], source_sec_groups=['fake_sgid2']): self.agent.security_groups_member_updated(['fake_sgid2']) self.assertIn('fake_device', self.agent.devices_to_refilter) self.assertIn('fake_device_2', self.agent.devices_to_refilter) self.assertFalse(self.firewall.security_group_updated.called) def test_multiple_security_groups_member_updated_multiple_ports(self): with self.add_fake_device(device='fake_device_2', sec_groups=['fake_sgid1', 'fake_sgid1B'], source_sec_groups=['fake_sgid1B']): self.agent.security_groups_member_updated(['fake_sgid1B']) self.agent.security_groups_member_updated(['fake_sgid2']) self.assertIn('fake_device', self.agent.devices_to_refilter) self.assertIn('fake_device_2', self.agent.devices_to_refilter) self.assertFalse(self.firewall.security_group_updated.called) def test_setup_port_filters_new_ports_only(self): self.agent.prepare_devices_filter = mock.Mock() self.agent.refresh_firewall = mock.Mock() self.agent.devices_to_refilter = set() self.agent.global_refresh_firewall = False self.agent.setup_port_filters(set(['fake_new_device']), set()) self.assertFalse(self.agent.devices_to_refilter) self.assertFalse(self.agent.global_refresh_firewall) self.agent.prepare_devices_filter.assert_called_once_with( set(['fake_new_device'])) self.assertFalse(self.agent.refresh_firewall.called) self.assertFalse(self.firewall.security_group_updated.called) def test_setup_port_filters_updated_ports_only(self): self.agent.prepare_devices_filter = mock.Mock() self.agent.refresh_firewall = mock.Mock() self.agent.devices_to_refilter = set() self.agent.global_refresh_firewall = False self.agent.setup_port_filters(set(), set(['fake_updated_device'])) self.assertFalse(self.agent.devices_to_refilter) self.assertFalse(self.agent.global_refresh_firewall) self.agent.refresh_firewall.assert_called_once_with( set(['fake_updated_device'])) self.assertFalse(self.agent.prepare_devices_filter.called) self.assertFalse(self.firewall.security_group_updated.called) def test_setup_port_filter_new_and_updated_ports(self): self.agent.prepare_devices_filter = mock.Mock() self.agent.refresh_firewall = mock.Mock() self.agent.devices_to_refilter = set() self.agent.global_refresh_firewall = False self.agent.setup_port_filters(set(['fake_new_device']), set(['fake_updated_device'])) self.assertFalse(self.agent.devices_to_refilter) self.assertFalse(self.agent.global_refresh_firewall) self.agent.prepare_devices_filter.assert_called_once_with( set(['fake_new_device'])) self.agent.refresh_firewall.assert_called_once_with( set(['fake_updated_device'])) self.assertFalse(self.firewall.security_group_updated.called) def test_setup_port_filters_sg_updates_only(self): self.agent.prepare_devices_filter = mock.Mock() self.agent.refresh_firewall = mock.Mock() self.agent.devices_to_refilter = set(['fake_device']) self.agent.global_refresh_firewall = False self.agent.setup_port_filters(set(), set()) self.assertFalse(self.agent.devices_to_refilter) self.assertFalse(self.agent.global_refresh_firewall) self.agent.refresh_firewall.assert_called_once_with( set(['fake_device'])) self.assertFalse(self.agent.prepare_devices_filter.called) self.assertFalse(self.firewall.security_group_updated.called) def test_setup_port_filters_sg_updates_and_new_ports(self): self.agent.prepare_devices_filter = mock.Mock() self.agent.refresh_firewall = mock.Mock() self.agent.devices_to_refilter = set(['fake_device']) self.agent.global_refresh_firewall = False self.agent.setup_port_filters(set(['fake_new_device']), set()) self.assertFalse(self.agent.devices_to_refilter) self.assertFalse(self.agent.global_refresh_firewall) self.agent.prepare_devices_filter.assert_called_once_with( set(['fake_new_device'])) self.agent.refresh_firewall.assert_called_once_with( set(['fake_device'])) self.assertFalse(self.firewall.security_group_updated.called) def _test_prepare_devices_filter(self, devices): # simulate an RPC arriving and calling _security_group_updated() self.agent.devices_to_refilter |= set(['fake_new_device']) def test_setup_port_filters_new_port_and_rpc(self): # Make sure that if an RPC arrives and adds a device to # devices_to_refilter while we are in setup_port_filters() # that it is not cleared, and will be processed later. self.agent.prepare_devices_filter = self._test_prepare_devices_filter self.agent.refresh_firewall = mock.Mock() self.agent.devices_to_refilter = set(['new_device', 'fake_device']) self.agent.global_refresh_firewall = False self.agent.setup_port_filters(set(['new_device']), set()) self.assertEqual(self.agent.devices_to_refilter, set(['fake_new_device'])) self.assertFalse(self.agent.global_refresh_firewall) self.agent.refresh_firewall.assert_called_once_with( set(['fake_device'])) self.assertFalse(self.firewall.security_group_updated.called) def test_setup_port_filters_sg_updates_and_updated_ports(self): self.agent.prepare_devices_filter = mock.Mock() self.agent.refresh_firewall = mock.Mock() self.agent.devices_to_refilter = set(['fake_device', 'fake_device_2']) self.agent.global_refresh_firewall = False self.agent.setup_port_filters( set(), set(['fake_device', 'fake_updated_device'])) self.assertFalse(self.agent.devices_to_refilter) self.assertFalse(self.agent.global_refresh_firewall) self.agent.refresh_firewall.assert_called_once_with( set(['fake_device', 'fake_device_2', 'fake_updated_device'])) self.assertFalse(self.agent.prepare_devices_filter.called) self.assertFalse(self.firewall.security_group_updated.called) def test_setup_port_filters_all_updates(self): self.agent.prepare_devices_filter = mock.Mock() self.agent.refresh_firewall = mock.Mock() self.agent.devices_to_refilter = set(['fake_device', 'fake_device_2']) self.agent.global_refresh_firewall = False self.agent.setup_port_filters( set(['fake_new_device']), set(['fake_device', 'fake_updated_device'])) self.assertFalse(self.agent.devices_to_refilter) self.assertFalse(self.agent.global_refresh_firewall) self.agent.prepare_devices_filter.assert_called_once_with( set(['fake_new_device'])) self.agent.refresh_firewall.assert_called_once_with( set(['fake_device', 'fake_device_2', 'fake_updated_device'])) self.assertFalse(self.firewall.security_group_updated.called) def test_setup_port_filters_no_update(self): self.agent.prepare_devices_filter = mock.Mock() self.agent.refresh_firewall = mock.Mock() self.agent.devices_to_refilter = set() self.agent.global_refresh_firewall = False self.agent.setup_port_filters(set(), set()) self.assertFalse(self.agent.devices_to_refilter) self.assertFalse(self.agent.global_refresh_firewall) self.assertFalse(self.agent.refresh_firewall.called) self.assertFalse(self.agent.prepare_devices_filter.called) self.assertFalse(self.firewall.security_group_updated.called) def test_setup_port_filters_with_global_refresh(self): self.agent.prepare_devices_filter = mock.Mock() self.agent.refresh_firewall = mock.Mock() self.agent.devices_to_refilter = set() self.agent.global_refresh_firewall = True self.agent.setup_port_filters(set(), set()) self.assertFalse(self.agent.devices_to_refilter) self.assertFalse(self.agent.global_refresh_firewall) self.agent.refresh_firewall.assert_called_once_with() self.assertFalse(self.agent.prepare_devices_filter.called) self.assertFalse(self.firewall.security_group_updated.called) class FakeSGNotifierAPI(securitygroups_rpc.SecurityGroupAgentRpcApiMixin): def __init__(self): self.topic = 'fake' target = oslo_messaging.Target(topic=self.topic, version='1.0') self.client = n_rpc.get_client(target) class SecurityGroupAgentRpcApiTestCase(base.BaseTestCase): def setUp(self): super(SecurityGroupAgentRpcApiTestCase, self).setUp() self.notifier = FakeSGNotifierAPI() self.mock_prepare = mock.patch.object(self.notifier.client, 'prepare', return_value=self.notifier.client).start() self.mock_cast = mock.patch.object(self.notifier.client, 'cast').start() def test_security_groups_rule_updated(self): self.notifier.security_groups_rule_updated( None, security_groups=['fake_sgid']) self.mock_cast.assert_has_calls( [mock.call(None, 'security_groups_rule_updated', security_groups=['fake_sgid'])]) def test_security_groups_member_updated(self): self.notifier.security_groups_member_updated( None, security_groups=['fake_sgid']) self.mock_cast.assert_has_calls( [mock.call(None, 'security_groups_member_updated', security_groups=['fake_sgid'])]) def test_security_groups_rule_not_updated(self): self.notifier.security_groups_rule_updated( None, security_groups=[]) self.assertFalse(self.mock_cast.called) def test_security_groups_member_not_updated(self): self.notifier.security_groups_member_updated( None, security_groups=[]) self.assertFalse(self.mock_cast.called) #Note(nati) bn -> binary_name # id -> device_id PHYSDEV_MOD = '-m physdev' PHYSDEV_IS_BRIDGED = '--physdev-is-bridged' IPTABLES_ARG = {'bn': iptables_manager.binary_name, 'physdev_mod': PHYSDEV_MOD, 'physdev_is_bridged': PHYSDEV_IS_BRIDGED} CHAINS_MANGLE = 'FORWARD|INPUT|OUTPUT|POSTROUTING|PREROUTING|mark' IPTABLES_ARG['chains'] = CHAINS_MANGLE CHAINS_MANGLE_V6 = 'FORWARD|INPUT|OUTPUT|POSTROUTING|PREROUTING' IPTABLES_ARG['chains'] = CHAINS_MANGLE_V6 CHAINS_NAT = 'OUTPUT|POSTROUTING|PREROUTING|float-snat|snat' IPTABLES_ARG['port1'] = 'port1' IPTABLES_ARG['port2'] = 'port2' IPTABLES_ARG['port3'] = 'port3' IPTABLES_ARG['mac1'] = '12:34:56:78:9A:BC' IPTABLES_ARG['mac2'] = '12:34:56:78:9A:BD' IPTABLES_ARG['ip1'] = '10.0.0.3/32' IPTABLES_ARG['ip2'] = '10.0.0.4/32' IPTABLES_ARG['chains'] = CHAINS_NAT IPTABLES_RAW_DEFAULT = """# Generated by iptables_manager *raw :OUTPUT - [0:0] :PREROUTING - [0:0] :%(bn)s-OUTPUT - [0:0] :%(bn)s-PREROUTING - [0:0] -I OUTPUT 1 -j %(bn)s-OUTPUT -I PREROUTING 1 -j %(bn)s-PREROUTING COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_RAW_BRIDGE_NET_1 = """# Generated by iptables_manager *raw :OUTPUT - [0:0] :PREROUTING - [0:0] :%(bn)s-OUTPUT - [0:0] :%(bn)s-PREROUTING - [0:0] -I OUTPUT 1 -j %(bn)s-OUTPUT -I PREROUTING 1 -j %(bn)s-PREROUTING -I %(bn)s-PREROUTING 1 -m physdev --physdev-in brqfakenet1 \ -m comment --comment "Set zone for port1" -j CT --zone 4097 -I %(bn)s-PREROUTING 2 -i brqfakenet1 \ -m comment --comment "Set zone for port1" -j CT --zone 4097 -I %(bn)s-PREROUTING 3 -m physdev --physdev-in tap_port1 \ -m comment --comment "Set zone for port1" -j CT --zone 4097 COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_RAW_BRIDGE_NET_2 = """# Generated by iptables_manager *raw :OUTPUT - [0:0] :PREROUTING - [0:0] :%(bn)s-OUTPUT - [0:0] :%(bn)s-PREROUTING - [0:0] -I OUTPUT 1 -j %(bn)s-OUTPUT -I PREROUTING 1 -j %(bn)s-PREROUTING -I %(bn)s-PREROUTING 1 -m physdev --physdev-in brqfakenet1 \ -m comment --comment "Set zone for port1" -j CT --zone 4097 -I %(bn)s-PREROUTING 2 -i brqfakenet1 \ -m comment --comment "Set zone for port1" -j CT --zone 4097 -I %(bn)s-PREROUTING 3 -m physdev --physdev-in tap_port1 \ -m comment --comment "Set zone for port1" -j CT --zone 4097 -I %(bn)s-PREROUTING 4 -m physdev --physdev-in brqfakenet2 \ -m comment --comment "Set zone for port2" -j CT --zone 4098 -I %(bn)s-PREROUTING 5 -i brqfakenet2 \ -m comment --comment "Set zone for port2" -j CT --zone 4098 -I %(bn)s-PREROUTING 6 -m physdev --physdev-in tap_port2 \ -m comment --comment "Set zone for port2" -j CT --zone 4098 COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_RAW_DEVICE_1 = """# Generated by iptables_manager *raw :OUTPUT - [0:0] :PREROUTING - [0:0] :%(bn)s-OUTPUT - [0:0] :%(bn)s-PREROUTING - [0:0] -I OUTPUT 1 -j %(bn)s-OUTPUT -I PREROUTING 1 -j %(bn)s-PREROUTING -I %(bn)s-PREROUTING 1 -m physdev --physdev-in qvbtap_port1 \ -m comment --comment "Set zone for %(port1)s" -j CT --zone 4097 -I %(bn)s-PREROUTING 2 -i qvbtap_port1 \ -m comment --comment "Set zone for %(port1)s" -j CT --zone 4097 -I %(bn)s-PREROUTING 3 -m physdev --physdev-in tap_port1 \ -m comment --comment "Set zone for %(port1)s" -j CT --zone 4097 COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_RAW_DEVICE_2 = """# Generated by iptables_manager *raw :OUTPUT - [0:0] :PREROUTING - [0:0] :%(bn)s-OUTPUT - [0:0] :%(bn)s-PREROUTING - [0:0] -I OUTPUT 1 -j %(bn)s-OUTPUT -I PREROUTING 1 -j %(bn)s-PREROUTING -I %(bn)s-PREROUTING 1 -m physdev --physdev-in qvbtap_%(port1)s \ -m comment --comment "Set zone for %(port1)s" -j CT --zone 4097 -I %(bn)s-PREROUTING 2 -i qvbtap_%(port1)s \ -m comment --comment "Set zone for %(port1)s" -j CT --zone 4097 -I %(bn)s-PREROUTING 3 -m physdev --physdev-in tap_%(port1)s \ -m comment --comment "Set zone for %(port1)s" -j CT --zone 4097 -I %(bn)s-PREROUTING 4 -m physdev --physdev-in qvbtap_%(port2)s \ -m comment --comment "Set zone for %(port2)s" -j CT --zone 4098 -I %(bn)s-PREROUTING 5 -i qvbtap_%(port2)s \ -m comment --comment "Set zone for %(port2)s" -j CT --zone 4098 -I %(bn)s-PREROUTING 6 -m physdev --physdev-in tap_%(port2)s \ -m comment --comment "Set zone for %(port2)s" -j CT --zone 4098 COMMIT # Completed by iptables_manager """ % IPTABLES_ARG CHAINS_RAW = 'OUTPUT|PREROUTING' IPTABLES_ARG['chains'] = CHAINS_RAW IPTABLES_RAW = """# Generated by iptables_manager *raw :OUTPUT - [0:0] :PREROUTING - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I OUTPUT 1 -j %(bn)s-OUTPUT -I PREROUTING 1 -j %(bn)s-PREROUTING COMMIT # Completed by iptables_manager """ % IPTABLES_ARG CHAINS_EMPTY = 'FORWARD|INPUT|OUTPUT|local|sg-chain|sg-fallback' CHAINS_1 = CHAINS_EMPTY + '|i_port1|o_port1|s_port1' CHAINS_2 = CHAINS_1 + '|i_port2|o_port2|s_port2' IPTABLES_ARG['chains'] = CHAINS_1 IPSET_FILTER_1 = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-o_port1 -I %(bn)s-i_port1 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_port1 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_port1 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_port1 4 -m set --match-set NIPv4security_group1 src -j \ RETURN -I %(bn)s-i_port1 5 -m state --state INVALID -j DROP -I %(bn)s-i_port1 6 -j %(bn)s-sg-fallback -I %(bn)s-o_port1 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_port1 2 -j %(bn)s-s_port1 -I %(bn)s-o_port1 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_port1 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_port1 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_port1 6 -j RETURN -I %(bn)s-o_port1 7 -m state --state INVALID -j DROP -I %(bn)s-o_port1 8 -j %(bn)s-sg-fallback -I %(bn)s-s_port1 1 -s 10.0.0.3/32 -m mac --mac-source 12:34:56:78:9A:BC \ -j RETURN -I %(bn)s-s_port1 2 -j DROP -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-i_port1 -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-o_port1 -I %(bn)s-sg-chain 3 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_FILTER_1 = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-o_port1 -I %(bn)s-i_port1 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_port1 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_port1 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_port1 4 -m state --state INVALID -j DROP -I %(bn)s-i_port1 5 -j %(bn)s-sg-fallback -I %(bn)s-o_port1 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_port1 2 -j %(bn)s-s_port1 -I %(bn)s-o_port1 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_port1 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_port1 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_port1 6 -j RETURN -I %(bn)s-o_port1 7 -m state --state INVALID -j DROP -I %(bn)s-o_port1 8 -j %(bn)s-sg-fallback -I %(bn)s-s_port1 1 -s 10.0.0.3/32 -m mac --mac-source 12:34:56:78:9A:BC \ -j RETURN -I %(bn)s-s_port1 2 -j DROP -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-i_port1 -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-o_port1 -I %(bn)s-sg-chain 3 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_FILTER_1_2 = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-o_port1 -I %(bn)s-i_port1 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_port1 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_port1 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_port1 4 -s 10.0.0.4/32 -j RETURN -I %(bn)s-i_port1 5 -m state --state INVALID -j DROP -I %(bn)s-i_port1 6 -j %(bn)s-sg-fallback -I %(bn)s-o_port1 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_port1 2 -j %(bn)s-s_port1 -I %(bn)s-o_port1 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_port1 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_port1 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_port1 6 -j RETURN -I %(bn)s-o_port1 7 -m state --state INVALID -j DROP -I %(bn)s-o_port1 8 -j %(bn)s-sg-fallback -I %(bn)s-s_port1 1 -s 10.0.0.3/32 -m mac --mac-source 12:34:56:78:9A:BC \ -j RETURN -I %(bn)s-s_port1 2 -j DROP -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-i_port1 -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-o_port1 -I %(bn)s-sg-chain 3 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_ARG['chains'] = CHAINS_2 IPSET_FILTER_2 = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-INPUT 2 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-i_%(port1)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port1)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port1)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port1)s 4 -m set --match-set NIPv4security_group1 src -j RETURN -I %(bn)s-i_%(port1)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port1)s 6 -j %(bn)s-sg-fallback -I %(bn)s-i_%(port2)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port2)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port2)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port2)s 4 -m set --match-set NIPv4security_group1 src -j RETURN -I %(bn)s-i_%(port2)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 6 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s -I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port1)s 6 -j RETURN -I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s -I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port2)s 6 -j RETURN -I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN -I %(bn)s-s_%(port2)s 2 -j DROP -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port1)s -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-sg-chain 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port2)s -I %(bn)s-sg-chain 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-sg-chain 5 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPSET_FILTER_2_TRUSTED = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 5 %(physdev_mod)s --physdev-INGRESS tap_%(port3)s \ %(physdev_is_bridged)s -j ACCEPT -I %(bn)s-FORWARD 6 %(physdev_mod)s --physdev-EGRESS tap_%(port3)s \ %(physdev_is_bridged)s -j ACCEPT -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-INPUT 2 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-i_%(port1)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port1)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port1)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port1)s 4 -m set --match-set NIPv4security_group1 src -j RETURN -I %(bn)s-i_%(port1)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port1)s 6 -j %(bn)s-sg-fallback -I %(bn)s-i_%(port2)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port2)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port2)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port2)s 4 -m set --match-set NIPv4security_group1 src -j RETURN -I %(bn)s-i_%(port2)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 6 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s -I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port1)s 6 -j RETURN -I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s -I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port2)s 6 -j RETURN -I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN -I %(bn)s-s_%(port2)s 2 -j DROP -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port1)s -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-sg-chain 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port2)s -I %(bn)s-sg-chain 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-sg-chain 5 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPSET_FILTER_2_3_TRUSTED = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 5 %(physdev_mod)s --physdev-INGRESS tap_%(port3)s \ %(physdev_is_bridged)s -j ACCEPT -I %(bn)s-FORWARD 6 %(physdev_mod)s --physdev-EGRESS tap_%(port3)s \ %(physdev_is_bridged)s -j ACCEPT -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-INPUT 2 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-i_%(port1)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port1)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port1)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port1)s 4 -m set --match-set NIPv4security_group1 src -j RETURN -I %(bn)s-i_%(port1)s 5 -p icmp -j RETURN -I %(bn)s-i_%(port1)s 6 -m state --state INVALID -j DROP -I %(bn)s-i_%(port1)s 7 -j %(bn)s-sg-fallback -I %(bn)s-i_%(port2)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port2)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port2)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port2)s 4 -m set --match-set NIPv4security_group1 src -j RETURN -I %(bn)s-i_%(port2)s 5 -p icmp -j RETURN -I %(bn)s-i_%(port2)s 6 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 7 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s -I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port1)s 6 -j RETURN -I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s -I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port2)s 6 -j RETURN -I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN -I %(bn)s-s_%(port2)s 2 -j DROP -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port1)s -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-sg-chain 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port2)s -I %(bn)s-sg-chain 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-sg-chain 5 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_FILTER_2 = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-INPUT 2 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-i_%(port1)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port1)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port1)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port1)s 4 -s %(ip2)s -j RETURN -I %(bn)s-i_%(port1)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port1)s 6 -j %(bn)s-sg-fallback -I %(bn)s-i_%(port2)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port2)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port2)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port2)s 4 -s %(ip1)s -j RETURN -I %(bn)s-i_%(port2)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 6 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s -I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port1)s 6 -j RETURN -I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s -I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port2)s 6 -j RETURN -I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN -I %(bn)s-s_%(port2)s 2 -j DROP -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port1)s -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-sg-chain 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port2)s -I %(bn)s-sg-chain 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-sg-chain 5 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_FILTER_2_TRUSTED = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 5 %(physdev_mod)s --physdev-INGRESS tap_%(port3)s \ %(physdev_is_bridged)s -j ACCEPT -I %(bn)s-FORWARD 6 %(physdev_mod)s --physdev-EGRESS tap_%(port3)s \ %(physdev_is_bridged)s -j ACCEPT -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-INPUT 2 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-i_%(port1)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port1)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port1)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port1)s 4 -s %(ip2)s -j RETURN -I %(bn)s-i_%(port1)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port1)s 6 -j %(bn)s-sg-fallback -I %(bn)s-i_%(port2)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port2)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port2)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port2)s 4 -s %(ip1)s -j RETURN -I %(bn)s-i_%(port2)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 6 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s -I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port1)s 6 -j RETURN -I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s -I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port2)s 6 -j RETURN -I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN -I %(bn)s-s_%(port2)s 2 -j DROP -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port1)s -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-sg-chain 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port2)s -I %(bn)s-sg-chain 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-sg-chain 5 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_FILTER_2_2 = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-INPUT 2 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-i_%(port1)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port1)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port1)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port1)s 4 -m state --state INVALID -j DROP -I %(bn)s-i_%(port1)s 5 -j %(bn)s-sg-fallback -I %(bn)s-i_%(port2)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port2)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port2)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port2)s 4 -s %(ip1)s -j RETURN -I %(bn)s-i_%(port2)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 6 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s -I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port1)s 6 -j RETURN -I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s -I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port2)s 6 -j RETURN -I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN -I %(bn)s-s_%(port2)s 2 -j DROP -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port1)s -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-sg-chain 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port2)s -I %(bn)s-sg-chain 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-sg-chain 5 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_FILTER_2_3 = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-INPUT 2 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-i_%(port1)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port1)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port1)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port1)s 4 -s %(ip2)s -j RETURN -I %(bn)s-i_%(port1)s 5 -p icmp -j RETURN -I %(bn)s-i_%(port1)s 6 -m state --state INVALID -j DROP -I %(bn)s-i_%(port1)s 7 -j %(bn)s-sg-fallback -I %(bn)s-i_%(port2)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port2)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port2)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port2)s 4 -s %(ip1)s -j RETURN -I %(bn)s-i_%(port2)s 5 -p icmp -j RETURN -I %(bn)s-i_%(port2)s 6 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 7 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s -I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port1)s 6 -j RETURN -I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s -I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port2)s 6 -j RETURN -I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN -I %(bn)s-s_%(port2)s 2 -j DROP -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port1)s -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-sg-chain 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port2)s -I %(bn)s-sg-chain 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-sg-chain 5 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_FILTER_2_3_TRUSTED = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 5 %(physdev_mod)s --physdev-INGRESS tap_%(port3)s \ %(physdev_is_bridged)s -j ACCEPT -I %(bn)s-FORWARD 6 %(physdev_mod)s --physdev-EGRESS tap_%(port3)s \ %(physdev_is_bridged)s -j ACCEPT -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-INPUT 2 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-i_%(port1)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port1)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port1)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port1)s 4 -s %(ip2)s -j RETURN -I %(bn)s-i_%(port1)s 5 -p icmp -j RETURN -I %(bn)s-i_%(port1)s 6 -m state --state INVALID -j DROP -I %(bn)s-i_%(port1)s 7 -j %(bn)s-sg-fallback -I %(bn)s-i_%(port2)s 1 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port2)s 2 -s 10.0.0.2/32 -p udp -m udp --sport 67 \ --dport 68 -j RETURN -I %(bn)s-i_%(port2)s 3 -p tcp -m tcp --dport 22 -j RETURN -I %(bn)s-i_%(port2)s 4 -s %(ip1)s -j RETURN -I %(bn)s-i_%(port2)s 5 -p icmp -j RETURN -I %(bn)s-i_%(port2)s 6 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 7 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port1)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 2 -j %(bn)s-s_%(port1)s -I %(bn)s-o_%(port1)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port1)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port1)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port1)s 6 -j RETURN -I %(bn)s-o_%(port1)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port1)s 8 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port2)s 1 -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp \ --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 2 -j %(bn)s-s_%(port2)s -I %(bn)s-o_%(port2)s 3 -p udp -m udp --sport 68 --dport 67 -j RETURN -I %(bn)s-o_%(port2)s 4 -p udp -m udp --sport 67 --dport 68 -j DROP -I %(bn)s-o_%(port2)s 5 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port2)s 6 -j RETURN -I %(bn)s-o_%(port2)s 7 -m state --state INVALID -j DROP -I %(bn)s-o_%(port2)s 8 -j %(bn)s-sg-fallback -I %(bn)s-s_%(port1)s 1 -s %(ip1)s -m mac --mac-source %(mac1)s -j RETURN -I %(bn)s-s_%(port1)s 2 -j DROP -I %(bn)s-s_%(port2)s 1 -s %(ip2)s -m mac --mac-source %(mac2)s -j RETURN -I %(bn)s-s_%(port2)s 2 -j DROP -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port1)s -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-sg-chain 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port2)s -I %(bn)s-sg-chain 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-sg-chain 5 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_ARG['chains'] = CHAINS_EMPTY IPTABLES_FILTER_EMPTY = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-sg-chain 1 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_ARG['chains'] = CHAINS_1 IPTABLES_FILTER_V6_1 = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-o_port1 -I %(bn)s-i_port1 1 -p ipv6-icmp -m icmp6 --icmpv6-type 130 -j RETURN -I %(bn)s-i_port1 2 -p ipv6-icmp -m icmp6 --icmpv6-type 135 -j RETURN -I %(bn)s-i_port1 3 -p ipv6-icmp -m icmp6 --icmpv6-type 136 -j RETURN -I %(bn)s-i_port1 4 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_port1 5 -m state --state INVALID -j DROP -I %(bn)s-i_port1 6 -j %(bn)s-sg-fallback -I %(bn)s-o_port1 1 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 131 -j RETURN -I %(bn)s-o_port1 2 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 135 -j RETURN -I %(bn)s-o_port1 3 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 143 -j RETURN -I %(bn)s-o_port1 4 -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j DROP -I %(bn)s-o_port1 5 -p ipv6-icmp -j RETURN -I %(bn)s-o_port1 6 -p udp -m udp --sport 546 --dport 547 -j RETURN -I %(bn)s-o_port1 7 -p udp -m udp --sport 547 --dport 546 -j DROP -I %(bn)s-o_port1 8 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_port1 9 -m state --state INVALID -j DROP -I %(bn)s-o_port1 10 -j %(bn)s-sg-fallback -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-i_port1 -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_port1 \ %(physdev_is_bridged)s -j %(bn)s-o_port1 -I %(bn)s-sg-chain 3 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_ARG['chains'] = CHAINS_2 IPTABLES_FILTER_V6_2 = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-INPUT 2 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-i_%(port1)s 1 -p ipv6-icmp -m icmp6 --icmpv6-type 130 -j RETURN -I %(bn)s-i_%(port1)s 2 -p ipv6-icmp -m icmp6 --icmpv6-type 135 -j RETURN -I %(bn)s-i_%(port1)s 3 -p ipv6-icmp -m icmp6 --icmpv6-type 136 -j RETURN -I %(bn)s-i_%(port1)s 4 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port1)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port1)s 6 -j %(bn)s-sg-fallback -I %(bn)s-i_%(port2)s 1 -p ipv6-icmp -m icmp6 --icmpv6-type 130 -j RETURN -I %(bn)s-i_%(port2)s 2 -p ipv6-icmp -m icmp6 --icmpv6-type 135 -j RETURN -I %(bn)s-i_%(port2)s 3 -p ipv6-icmp -m icmp6 --icmpv6-type 136 -j RETURN -I %(bn)s-i_%(port2)s 4 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port2)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 6 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port1)s 1 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 131 -j RETURN -I %(bn)s-o_%(port1)s 2 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 135 -j RETURN -I %(bn)s-o_%(port1)s 3 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 143 -j RETURN -I %(bn)s-o_%(port1)s 4 -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j DROP -I %(bn)s-o_%(port1)s 5 -p ipv6-icmp -j RETURN -I %(bn)s-o_%(port1)s 6 -p udp -m udp --sport 546 --dport 547 -j RETURN -I %(bn)s-o_%(port1)s 7 -p udp -m udp --sport 547 --dport 546 -j DROP -I %(bn)s-o_%(port1)s 8 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port1)s 9 -m state --state INVALID -j DROP -I %(bn)s-o_%(port1)s 10 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port2)s 1 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 131 -j RETURN -I %(bn)s-o_%(port2)s 2 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 135 -j RETURN -I %(bn)s-o_%(port2)s 3 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 143 -j RETURN -I %(bn)s-o_%(port2)s 4 -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j DROP -I %(bn)s-o_%(port2)s 5 -p ipv6-icmp -j RETURN -I %(bn)s-o_%(port2)s 6 -p udp -m udp --sport 546 --dport 547 -j RETURN -I %(bn)s-o_%(port2)s 7 -p udp -m udp --sport 547 --dport 546 -j DROP -I %(bn)s-o_%(port2)s 8 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port2)s 9 -m state --state INVALID -j DROP -I %(bn)s-o_%(port2)s 10 -j %(bn)s-sg-fallback -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port1)s -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-sg-chain 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port2)s -I %(bn)s-sg-chain 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-sg-chain 5 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_FILTER_V6_2_TRUSTED = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-FORWARD 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-sg-chain -I %(bn)s-FORWARD 5 %(physdev_mod)s --physdev-INGRESS tap_%(port3)s \ %(physdev_is_bridged)s -j ACCEPT -I %(bn)s-FORWARD 6 %(physdev_mod)s --physdev-EGRESS tap_%(port3)s \ %(physdev_is_bridged)s -j ACCEPT -I %(bn)s-INPUT 1 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-INPUT 2 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-i_%(port1)s 1 -p ipv6-icmp -m icmp6 --icmpv6-type 130 -j RETURN -I %(bn)s-i_%(port1)s 2 -p ipv6-icmp -m icmp6 --icmpv6-type 135 -j RETURN -I %(bn)s-i_%(port1)s 3 -p ipv6-icmp -m icmp6 --icmpv6-type 136 -j RETURN -I %(bn)s-i_%(port1)s 4 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port1)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port1)s 6 -j %(bn)s-sg-fallback -I %(bn)s-i_%(port2)s 1 -p ipv6-icmp -m icmp6 --icmpv6-type 130 -j RETURN -I %(bn)s-i_%(port2)s 2 -p ipv6-icmp -m icmp6 --icmpv6-type 135 -j RETURN -I %(bn)s-i_%(port2)s 3 -p ipv6-icmp -m icmp6 --icmpv6-type 136 -j RETURN -I %(bn)s-i_%(port2)s 4 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-i_%(port2)s 5 -m state --state INVALID -j DROP -I %(bn)s-i_%(port2)s 6 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port1)s 1 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 131 -j RETURN -I %(bn)s-o_%(port1)s 2 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 135 -j RETURN -I %(bn)s-o_%(port1)s 3 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 143 -j RETURN -I %(bn)s-o_%(port1)s 4 -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j DROP -I %(bn)s-o_%(port1)s 5 -p ipv6-icmp -j RETURN -I %(bn)s-o_%(port1)s 6 -p udp -m udp --sport 546 --dport 547 -j RETURN -I %(bn)s-o_%(port1)s 7 -p udp -m udp --sport 547 --dport 546 -j DROP -I %(bn)s-o_%(port1)s 8 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port1)s 9 -m state --state INVALID -j DROP -I %(bn)s-o_%(port1)s 10 -j %(bn)s-sg-fallback -I %(bn)s-o_%(port2)s 1 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 131 -j RETURN -I %(bn)s-o_%(port2)s 2 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 135 -j RETURN -I %(bn)s-o_%(port2)s 3 -s ::/128 -d ff02::/16 -p ipv6-icmp -m icmp6 \ --icmpv6-type 143 -j RETURN -I %(bn)s-o_%(port2)s 4 -p ipv6-icmp -m icmp6 --icmpv6-type 134 -j DROP -I %(bn)s-o_%(port2)s 5 -p ipv6-icmp -j RETURN -I %(bn)s-o_%(port2)s 6 -p udp -m udp --sport 546 --dport 547 -j RETURN -I %(bn)s-o_%(port2)s 7 -p udp -m udp --sport 547 --dport 546 -j DROP -I %(bn)s-o_%(port2)s 8 -m state --state RELATED,ESTABLISHED -j RETURN -I %(bn)s-o_%(port2)s 9 -m state --state INVALID -j DROP -I %(bn)s-o_%(port2)s 10 -j %(bn)s-sg-fallback -I %(bn)s-sg-chain 1 %(physdev_mod)s --physdev-INGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port1)s -I %(bn)s-sg-chain 2 %(physdev_mod)s --physdev-EGRESS tap_%(port1)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port1)s -I %(bn)s-sg-chain 3 %(physdev_mod)s --physdev-INGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-i_%(port2)s -I %(bn)s-sg-chain 4 %(physdev_mod)s --physdev-EGRESS tap_%(port2)s \ %(physdev_is_bridged)s -j %(bn)s-o_%(port2)s -I %(bn)s-sg-chain 5 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG IPTABLES_ARG['chains'] = CHAINS_EMPTY IPTABLES_FILTER_V6_EMPTY = """# Generated by iptables_manager *filter :FORWARD - [0:0] :INPUT - [0:0] :OUTPUT - [0:0] :neutron-filter-top - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] :%(bn)s-(%(chains)s) - [0:0] -I FORWARD 1 -j neutron-filter-top -I FORWARD 2 -j %(bn)s-FORWARD -I INPUT 1 -j %(bn)s-INPUT -I OUTPUT 1 -j neutron-filter-top -I OUTPUT 2 -j %(bn)s-OUTPUT -I neutron-filter-top 1 -j %(bn)s-local -I %(bn)s-sg-chain 1 -j ACCEPT -I %(bn)s-sg-fallback 1 -j DROP COMMIT # Completed by iptables_manager """ % IPTABLES_ARG class TestSecurityGroupAgentWithIptables(base.BaseTestCase): FIREWALL_DRIVER = FIREWALL_IPTABLES_DRIVER PHYSDEV_INGRESS = 'physdev-out' PHYSDEV_EGRESS = 'physdev-in' def setUp(self, defer_refresh_firewall=False, test_rpc_v1_1=True): clear_mgrs = lambda: ip_conntrack.CONTRACK_MGRS.clear() self.addCleanup(clear_mgrs) clear_mgrs() # clear before start in case other tests didn't clean up super(TestSecurityGroupAgentWithIptables, self).setUp() set_firewall_driver(self.FIREWALL_DRIVER) cfg.CONF.set_override('enable_ipset', False, group='SECURITYGROUP') cfg.CONF.set_override('comment_iptables_rules', False, group='AGENT') self.utils_exec = mock.patch( 'neutron.agent.linux.utils.execute').start() self.rpc = mock.Mock() self._init_agent(defer_refresh_firewall) if test_rpc_v1_1: self.rpc.security_group_info_for_devices.side_effect = ( oslo_messaging.UnsupportedVersion('1.2')) self.iptables = self.agent.firewall.iptables self.ipconntrack = self.agent.firewall.ipconntrack # TODO(jlibosva) Get rid of mocking iptables execute and mock out # firewall instead self.iptables.use_ipv6 = True self.iptables_execute = mock.patch.object(self.iptables, "execute").start() self.iptables_execute_return_values = [] self.expected_call_count = 0 self.expected_calls = [] self.expected_process_inputs = [] self.iptables_execute.side_effect = self.iptables_execute_return_values rule1 = [{'direction': 'ingress', 'protocol': const.PROTO_NAME_UDP, 'ethertype': const.IPv4, 'source_ip_prefix': '10.0.0.2/32', 'source_port_range_min': 67, 'source_port_range_max': 67, 'port_range_min': 68, 'port_range_max': 68}, {'direction': 'ingress', 'protocol': const.PROTO_NAME_TCP, 'ethertype': const.IPv4, 'port_range_min': 22, 'port_range_max': 22}, {'direction': 'egress', 'ethertype': const.IPv4}] rule2 = rule1[:] rule2 += [{'direction': 'ingress', 'source_ip_prefix': '10.0.0.4/32', 'ethertype': const.IPv4}] rule3 = rule2[:] rule3 += [{'direction': 'ingress', 'protocol': const.PROTO_NAME_ICMP, 'ethertype': const.IPv4}] rule4 = rule1[:] rule4 += [{'direction': 'ingress', 'source_ip_prefix': '10.0.0.3/32', 'ethertype': const.IPv4}] rule5 = rule4[:] rule5 += [{'direction': 'ingress', 'protocol': const.PROTO_NAME_ICMP, 'ethertype': const.IPv4}] self.devices1 = {'tap_port1': self._device('tap_port1', '10.0.0.3/32', '12:34:56:78:9a:bc', rule1)} self.devices2 = collections.OrderedDict([ ('tap_port1', self._device('tap_port1', '10.0.0.3/32', '12:34:56:78:9a:bc', rule2)), ('tap_port2', self._device('tap_port2', '10.0.0.4/32', '12:34:56:78:9a:bd', rule4)) ]) self.devices3 = collections.OrderedDict([ ('tap_port1', self._device('tap_port1', '10.0.0.3/32', '12:34:56:78:9a:bc', rule3)), ('tap_port2', self._device('tap_port2', '10.0.0.4/32', '12:34:56:78:9a:bd', rule5)) ]) self.agent.firewall.security_group_updated = mock.Mock() @staticmethod def _enforce_order_in_firewall(firewall): # for the sake of the test, eliminate any order randomness: # it helps to match iptables output against regexps consistently for attr in ('filtered_ports', 'unfiltered_ports'): setattr(firewall, attr, collections.OrderedDict()) def _init_agent(self, defer_refresh_firewall): self.agent = sg_rpc.SecurityGroupAgentRpc( context=None, plugin_rpc=self.rpc, defer_refresh_firewall=defer_refresh_firewall) self._enforce_order_in_firewall(self.agent.firewall) # don't mess with sysctl knobs in unit tests self.agent.firewall._enabled_netfilter_for_bridges = True def _device(self, device, ip, mac_address, rule): return {'device': device, 'network_id': 'fakenet%s' % device[-1:], 'fixed_ips': [ip], 'mac_address': mac_address, 'security_groups': ['security_group1'], 'security_group_rules': rule, 'security_group_source_groups': [ 'security_group1']} def _regex(self, value): value = value.replace('physdev-INGRESS', self.PHYSDEV_INGRESS) value = value.replace('physdev-EGRESS', self.PHYSDEV_EGRESS) value = value.replace('\n', '\\n') value = value.replace('[', r'\[') value = value.replace(']', r'\]') value = value.replace('*', r'\*') return value def _register_mock_call(self, *args, **kwargs): return_value = kwargs.pop('return_value', None) self.iptables_execute_return_values.append(return_value) has_process_input = 'process_input' in kwargs process_input = kwargs.get('process_input') self.expected_process_inputs.append((has_process_input, process_input)) if has_process_input: kwargs['process_input'] = mock.ANY self.expected_calls.append(mock.call(*args, **kwargs)) self.expected_call_count += 1 def _verify_mock_calls(self, exp_fw_sg_updated_call=False): self.assertEqual(self.expected_call_count, self.iptables_execute.call_count) self.iptables_execute.assert_has_calls(self.expected_calls) for i, expected in enumerate(self.expected_process_inputs): check, expected_regex = expected if not check: continue # The second or later arguments of self.iptables.execute # are keyword parameter, so keyword argument is extracted by [1] kwargs = self.iptables_execute.call_args_list[i][1] self.assertThat(kwargs['process_input'], matchers.MatchesRegex(expected_regex)) self.assertEqual(exp_fw_sg_updated_call, self.agent.firewall.security_group_updated.called) def _replay_iptables(self, v4_filter, v6_filter, raw): self._register_mock_call( ['iptables-save'], run_as_root=True, return_value='') self._register_mock_call( ['iptables-restore', '-n'], process_input=self._regex(v4_filter + raw), run_as_root=True, log_fail_as_error=False, return_value='') self._register_mock_call( ['ip6tables-save'], run_as_root=True, return_value='') self._register_mock_call( ['ip6tables-restore', '-n'], process_input=self._regex(v6_filter + raw), run_as_root=True, log_fail_as_error=False, return_value='') def test_prepare_remove_port(self): self.ipconntrack._device_zone_map = {} self.rpc.security_group_rules_for_devices.return_value = self.devices1 self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.agent.remove_devices_filter(['tap_port1']) self._verify_mock_calls() def test_security_group_member_updated(self): self.rpc.security_group_rules_for_devices.return_value = self.devices1 self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_1_2, IPTABLES_FILTER_V6_1, IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPTABLES_FILTER_2_2, IPTABLES_FILTER_V6_2, IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.rpc.security_group_rules_for_devices.return_value = self.devices2 self.agent.security_groups_member_updated(['security_group1']) self.agent.prepare_devices_filter(['tap_port2']) self.rpc.security_group_rules_for_devices.return_value = self.devices1 self.agent.security_groups_member_updated(['security_group1']) self.agent.remove_devices_filter(['tap_port2']) self.agent.remove_devices_filter(['tap_port1']) self._verify_mock_calls() def test_security_group_rule_updated(self): self.rpc.security_group_rules_for_devices.return_value = self.devices2 self._replay_iptables( IPTABLES_FILTER_2_TRUSTED, IPTABLES_FILTER_V6_2_TRUSTED, IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables( IPTABLES_FILTER_2_3_TRUSTED, IPTABLES_FILTER_V6_2_TRUSTED, IPTABLES_RAW_BRIDGE_NET_2) self.agent.prepare_devices_filter(['tap_port1', 'tap_port3']) self.rpc.security_group_rules_for_devices.return_value = self.devices3 self.agent.security_groups_rule_updated(['security_group1']) self._verify_mock_calls() class TestSecurityGroupAgentEnhancedRpcWithIptables( TestSecurityGroupAgentWithIptables): def setUp(self, defer_refresh_firewall=False): super(TestSecurityGroupAgentEnhancedRpcWithIptables, self).setUp( defer_refresh_firewall=defer_refresh_firewall, test_rpc_v1_1=False) self.sg_info = self.rpc.security_group_info_for_devices rule1 = [{'direction': 'ingress', 'protocol': const.PROTO_NAME_UDP, 'ethertype': const.IPv4, 'source_ip_prefix': '10.0.0.2/32', 'source_port_range_min': 67, 'source_port_range_max': 67, 'port_range_min': 68, 'port_range_max': 68}, {'direction': 'ingress', 'protocol': const.PROTO_NAME_TCP, 'ethertype': const.IPv4, 'port_range_min': 22, 'port_range_max': 22}, {'direction': 'egress', 'ethertype': const.IPv4}, {'direction': 'ingress', 'remote_group_id': 'security_group1', 'ethertype': const.IPv4}] rule2 = rule1[:] rule2 += [{'direction': 'ingress', 'protocol': const.PROTO_NAME_ICMP, 'ethertype': const.IPv4}] devices_info1 = {'tap_port1': self._device('tap_port1', '10.0.0.3/32', '12:34:56:78:9a:bc', [])} self.devices_info1 = {'security_groups': {'security_group1': rule1}, 'sg_member_ips': { 'security_group1': { 'IPv4': ['10.0.0.3/32'], 'IPv6': []}}, 'devices': devices_info1} devices_info2 = collections.OrderedDict([ ('tap_port1', self._device('tap_port1', '10.0.0.3/32', '12:34:56:78:9a:bc', [])), ('tap_port2', self._device('tap_port2', '10.0.0.4/32', '12:34:56:78:9a:bd', [])) ]) self.devices_info2 = {'security_groups': {'security_group1': rule1}, 'sg_member_ips': { 'security_group1': { 'IPv4': ['10.0.0.3/32', '10.0.0.4/32'], 'IPv6': []}}, 'devices': devices_info2} self.devices_info3 = {'security_groups': {'security_group1': rule2}, 'sg_member_ips': { 'security_group1': { 'IPv4': ['10.0.0.3/32', '10.0.0.4/32'], 'IPv6': []}}, 'devices': devices_info2} def test_prepare_remove_port(self): self.ipconntrack._device_zone_map = {} self.sg_info.return_value = self.devices_info1 self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.agent.remove_devices_filter(['tap_port1']) self._verify_mock_calls() def test_security_group_member_updated(self): self.sg_info.return_value = self.devices_info1 self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_1_2, IPTABLES_FILTER_V6_1, IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPTABLES_FILTER_2_2, IPTABLES_FILTER_V6_2, IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.sg_info.return_value = self.devices_info2 self.agent.security_groups_member_updated(['security_group1']) self.agent.prepare_devices_filter(['tap_port2']) self.sg_info.return_value = self.devices_info1 self.agent.security_groups_member_updated(['security_group1']) self.agent.remove_devices_filter(['tap_port2']) self.agent.remove_devices_filter(['tap_port1']) self._verify_mock_calls(True) self.assertEqual( 2, self.agent.firewall.security_group_updated.call_count) def test_security_group_rule_updated(self): self.sg_info.return_value = self.devices_info2 self._replay_iptables( IPTABLES_FILTER_2_TRUSTED, IPTABLES_FILTER_V6_2_TRUSTED, IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables( IPTABLES_FILTER_2_3_TRUSTED, IPTABLES_FILTER_V6_2_TRUSTED, IPTABLES_RAW_BRIDGE_NET_2) self.agent.prepare_devices_filter(['tap_port1', 'tap_port3']) self.sg_info.return_value = self.devices_info3 self.agent.security_groups_rule_updated(['security_group1']) self._verify_mock_calls(True) self.agent.firewall.security_group_updated.assert_called_with( 'sg_rule', set(['security_group1'])) class TestSecurityGroupAgentEnhancedIpsetWithIptables( TestSecurityGroupAgentEnhancedRpcWithIptables): def setUp(self, defer_refresh_firewall=False): super(TestSecurityGroupAgentEnhancedIpsetWithIptables, self).setUp( defer_refresh_firewall) self.agent.firewall.enable_ipset = True self.ipset = self.agent.firewall.ipset self.ipset_execute = mock.patch.object(self.ipset, "execute").start() def test_prepare_remove_port(self): self.ipconntrack._device_zone_map = {} self.sg_info.return_value = self.devices_info1 self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.agent.remove_devices_filter(['tap_port1']) self._verify_mock_calls() def test_security_group_member_updated(self): self.sg_info.return_value = self.devices_info1 self.ipset._get_new_set_ips = mock.Mock(return_value=['10.0.0.3']) self.ipset._get_deleted_set_ips = mock.Mock(return_value=[]) self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2, IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2, IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_BRIDGE_NET_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.sg_info.return_value = self.devices_info2 self.agent.security_groups_member_updated(['security_group1']) self.agent.prepare_devices_filter(['tap_port2']) self.sg_info.return_value = self.devices_info1 self.agent.security_groups_member_updated(['security_group1']) self.agent.remove_devices_filter(['tap_port2']) self.agent.remove_devices_filter(['tap_port1']) self._verify_mock_calls(True) self.assertEqual( 2, self.agent.firewall.security_group_updated.call_count) def test_security_group_rule_updated(self): self.ipset._get_new_set_ips = mock.Mock(return_value=['10.0.0.3']) self.ipset._get_deleted_set_ips = mock.Mock(return_value=[]) self.sg_info.return_value = self.devices_info2 self._replay_iptables( IPSET_FILTER_2_TRUSTED, IPTABLES_FILTER_V6_2_TRUSTED, IPTABLES_RAW_BRIDGE_NET_2) self._replay_iptables( IPSET_FILTER_2_3_TRUSTED, IPTABLES_FILTER_V6_2_TRUSTED, IPTABLES_RAW_BRIDGE_NET_2) self.agent.prepare_devices_filter(['tap_port1', 'tap_port3']) self.sg_info.return_value = self.devices_info3 self.agent.security_groups_rule_updated(['security_group1']) self._verify_mock_calls(True) self.agent.firewall.security_group_updated.assert_called_with( 'sg_rule', set(['security_group1'])) class SGNotificationTestMixin(object): def test_security_group_rule_updated(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: with self.security_group(name, description): security_group_id = sg['security_group']['id'] rule = self._build_security_group_rule( security_group_id, direction='ingress', proto=const.PROTO_NAME_TCP) security_group_rule = self._make_security_group_rule(self.fmt, rule) self._delete('security-group-rules', security_group_rule['security_group_rule']['id']) self.notifier.assert_has_calls( [mock.call.security_groups_rule_updated(mock.ANY, [security_group_id]), mock.call.security_groups_rule_updated(mock.ANY, [security_group_id])]) def test_security_group_member_updated(self): with self.network() as n: with self.subnet(n): with self.security_group() as sg: security_group_id = sg['security_group']['id'] res = self._create_port(self.fmt, n['network']['id']) port = self.deserialize(self.fmt, res) data = {'port': {'fixed_ips': port['port']['fixed_ips'], 'name': port['port']['name'], ext_sg.SECURITYGROUPS: [security_group_id]}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(res['port'][ext_sg.SECURITYGROUPS][0], security_group_id) self._delete('ports', port['port']['id']) self.notifier.assert_has_calls( [mock.call.security_groups_member_updated( mock.ANY, [mock.ANY])]) class TestSecurityGroupAgentWithOVSIptables( TestSecurityGroupAgentWithIptables): FIREWALL_DRIVER = FIREWALL_HYBRID_DRIVER def setUp(self, defer_refresh_firewall=False, test_rpc_v1_1=True): super(TestSecurityGroupAgentWithOVSIptables, self).setUp( defer_refresh_firewall, test_rpc_v1_1) def _init_agent(self, defer_refresh_firewall): self.agent = sg_rpc.SecurityGroupAgentRpc( context=None, plugin_rpc=self.rpc, defer_refresh_firewall=defer_refresh_firewall) self._enforce_order_in_firewall(self.agent.firewall) # don't mess with sysctl knobs in unit tests self.agent.firewall._enabled_netfilter_for_bridges = True def test_prepare_remove_port(self): self.ipconntrack._device_zone_map = {} self.rpc.security_group_rules_for_devices.return_value = self.devices1 self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_DEVICE_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.agent.remove_devices_filter(['tap_port1']) self._verify_mock_calls() def test_security_group_member_updated(self): self.ipconntrack._device_zone_map = {} self.rpc.security_group_rules_for_devices.return_value = self.devices1 self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_DEVICE_1) self._replay_iptables(IPTABLES_FILTER_1_2, IPTABLES_FILTER_V6_1, IPTABLES_RAW_DEVICE_1) self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, IPTABLES_RAW_DEVICE_2) self._replay_iptables(IPTABLES_FILTER_2_2, IPTABLES_FILTER_V6_2, IPTABLES_RAW_DEVICE_2) self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, IPTABLES_RAW_DEVICE_1) self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.rpc.security_group_rules_for_devices.return_value = self.devices2 self.agent.security_groups_member_updated(['security_group1']) self.agent.prepare_devices_filter(['tap_port2']) self.rpc.security_group_rules_for_devices.return_value = self.devices1 self.agent.security_groups_member_updated(['security_group1']) self.agent.remove_devices_filter(['tap_port2']) self.agent.remove_devices_filter(['tap_port1']) self._verify_mock_calls() def test_security_group_rule_updated(self): self.ipconntrack._device_zone_map = {} self.rpc.security_group_rules_for_devices.return_value = self.devices2 self._replay_iptables( IPTABLES_FILTER_2_TRUSTED, IPTABLES_FILTER_V6_2_TRUSTED, IPTABLES_RAW_DEVICE_2) self._replay_iptables( IPTABLES_FILTER_2_3_TRUSTED, IPTABLES_FILTER_V6_2_TRUSTED, IPTABLES_RAW_DEVICE_2) self.agent.prepare_devices_filter(['tap_port1', 'tap_port3']) self.rpc.security_group_rules_for_devices.return_value = self.devices3 self.agent.security_groups_rule_updated(['security_group1']) self._verify_mock_calls() def _regex(self, value): #Note(nati): tap is prefixed on the device # in the OVSHybridIptablesFirewallDriver value = value.replace('tap_port', 'taptap_port') value = value.replace('qvbtaptap_port', 'qvbtap_port') value = value.replace('o_port', 'otap_port') value = value.replace('i_port', 'itap_port') value = value.replace('s_port', 'stap_port') return super( TestSecurityGroupAgentWithOVSIptables, self)._regex(value) class TestSecurityGroupExtensionControl(base.BaseTestCase): def test_disable_security_group_extension_by_config(self): set_enable_security_groups(False) exp_aliases = ['dummy1', 'dummy2'] ext_aliases = ['dummy1', 'security-group', 'dummy2'] sg_rpc.disable_security_group_extension_by_config(ext_aliases) self.assertEqual(ext_aliases, exp_aliases) def test_enable_security_group_extension_by_config(self): set_enable_security_groups(True) exp_aliases = ['dummy1', 'security-group', 'dummy2'] ext_aliases = ['dummy1', 'security-group', 'dummy2'] sg_rpc.disable_security_group_extension_by_config(ext_aliases) self.assertEqual(ext_aliases, exp_aliases) neutron-12.1.1/neutron/tests/unit/agent/l3/0000775000175000017500000000000013553660156020551 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/l3/test_agent.py0000664000175000017500000054525513553660047023277 0ustar zuulzuul00000000000000# Copyright 2012 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from itertools import chain as iter_chain from itertools import combinations as iter_combinations import eventlet import fixtures import mock import netaddr from neutron_lib.agent import constants as agent_consts from neutron_lib.api.definitions import portbindings from neutron_lib import constants as lib_constants from neutron_lib import exceptions as exc from neutron_lib.plugins import constants as plugin_constants from oslo_config import cfg from oslo_log import log import oslo_messaging from oslo_utils import timeutils from oslo_utils import uuidutils from testtools import matchers from neutron.agent.common import resource_processing_queue from neutron.agent.l3 import agent as l3_agent from neutron.agent.l3 import dvr_edge_router as dvr_router from neutron.agent.l3 import dvr_router_base from neutron.agent.l3 import dvr_snat_ns from neutron.agent.l3 import ha_router from neutron.agent.l3 import legacy_router from neutron.agent.l3 import link_local_allocator as lla from neutron.agent.l3 import namespace_manager from neutron.agent.l3 import namespaces from neutron.agent.l3 import router_info as l3router from neutron.agent.linux import dibbler from neutron.agent.linux import interface from neutron.agent.linux import iptables_manager from neutron.agent.linux import pd from neutron.agent.linux import ra from neutron.agent.metadata import driver as metadata_driver from neutron.agent import rpc as agent_rpc from neutron.common import constants as n_const from neutron.common import exceptions as n_exc from neutron.conf.agent import common as agent_config from neutron.conf.agent.l3 import config as l3_config from neutron.conf.agent.l3 import ha as ha_conf from neutron.conf import common as base_config from neutron.tests import base from neutron.tests.common import l3_test_common from neutron.tests.unit.agent.linux.test_utils import FakeUser _uuid = uuidutils.generate_uuid HOSTNAME = 'myhost' FAKE_ID = _uuid() FAKE_ID_2 = _uuid() FIP_PRI = 32768 class BasicRouterOperationsFramework(base.BaseTestCase): def setUp(self): super(BasicRouterOperationsFramework, self).setUp() mock.patch('eventlet.spawn').start() self.conf = agent_config.setup_conf() self.conf.register_opts(base_config.core_opts) log.register_options(self.conf) self.conf.register_opts(agent_config.AGENT_STATE_OPTS, 'AGENT') l3_config.register_l3_agent_config_opts(l3_config.OPTS, self.conf) ha_conf.register_l3_agent_ha_opts(self.conf) agent_config.register_interface_driver_opts_helper(self.conf) agent_config.register_process_monitor_opts(self.conf) agent_config.register_availability_zone_opts_helper(self.conf) agent_config.register_interface_opts(self.conf) agent_config.register_external_process_opts(self.conf) agent_config.register_pd_opts(self.conf) agent_config.register_ra_opts(self.conf) self.conf.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') self.conf.set_override('state_path', cfg.CONF.state_path) self.conf.set_override('pd_dhcp_driver', '') self.device_exists_p = mock.patch( 'neutron.agent.linux.ip_lib.device_exists') self.device_exists = self.device_exists_p.start() self.list_network_namespaces_p = mock.patch( 'neutron.agent.linux.ip_lib.list_network_namespaces') self.list_network_namespaces = self.list_network_namespaces_p.start() self.ensure_dir = mock.patch( 'oslo_utils.fileutils.ensure_tree').start() mock.patch('neutron.agent.linux.keepalived.KeepalivedManager' '.get_full_config_file_path').start() self.utils_exec_p = mock.patch( 'neutron.agent.linux.utils.execute') self.utils_exec = self.utils_exec_p.start() self.utils_replace_file_p = mock.patch( 'neutron_lib.utils.file.replace_file') self.utils_replace_file = self.utils_replace_file_p.start() self.external_process_p = mock.patch( 'neutron.agent.linux.external_process.ProcessManager') self.external_process = self.external_process_p.start() self.process_monitor = mock.patch( 'neutron.agent.linux.external_process.ProcessMonitor').start() self.send_adv_notif_p = mock.patch( 'neutron.agent.linux.ip_lib.send_ip_addr_adv_notif') self.send_adv_notif = self.send_adv_notif_p.start() self.dvr_cls_p = mock.patch('neutron.agent.linux.interface.NullDriver') driver_cls = self.dvr_cls_p.start() self.mock_driver = mock.MagicMock() self.mock_driver.DEV_NAME_LEN = ( interface.LinuxInterfaceDriver.DEV_NAME_LEN) driver_cls.return_value = self.mock_driver self.ip_cls_p = mock.patch('neutron.agent.linux.ip_lib.IPWrapper') ip_cls = self.ip_cls_p.start() self.mock_ip = mock.MagicMock() ip_cls.return_value = self.mock_ip ip_rule = mock.patch('neutron.agent.linux.ip_lib.IPRule').start() self.mock_rule = mock.MagicMock() ip_rule.return_value = self.mock_rule ip_dev = mock.patch('neutron.agent.linux.ip_lib.IPDevice').start() self.mock_ip_dev = mock.MagicMock() ip_dev.return_value = self.mock_ip_dev self.l3pluginApi_cls_p = mock.patch( 'neutron.agent.l3.agent.L3PluginApi') l3pluginApi_cls = self.l3pluginApi_cls_p.start() self.plugin_api = mock.MagicMock() l3pluginApi_cls.return_value = self.plugin_api self.looping_call_p = mock.patch( 'oslo_service.loopingcall.FixedIntervalLoopingCall') self.looping_call_p.start() subnet_id_1 = _uuid() subnet_id_2 = _uuid() self.snat_ports = [{'subnets': [{'cidr': '152.2.0.0/16', 'gateway_ip': '152.2.0.1', 'id': subnet_id_1}], 'mtu': 1500, 'network_id': _uuid(), 'device_owner': lib_constants.DEVICE_OWNER_ROUTER_SNAT, 'mac_address': 'fa:16:3e:80:8d:80', 'fixed_ips': [{'subnet_id': subnet_id_1, 'ip_address': '152.2.0.13', 'prefixlen': 16}], 'id': _uuid(), 'device_id': _uuid()}, {'subnets': [{'cidr': '152.10.0.0/16', 'gateway_ip': '152.10.0.1', 'id': subnet_id_2}], 'mtu': 1450, 'network_id': _uuid(), 'device_owner': lib_constants.DEVICE_OWNER_ROUTER_SNAT, 'mac_address': 'fa:16:3e:80:8d:80', 'fixed_ips': [{'subnet_id': subnet_id_2, 'ip_address': '152.10.0.13', 'prefixlen': 16}], 'id': _uuid(), 'device_id': _uuid()}] self.ri_kwargs = {'agent_conf': self.conf, 'interface_driver': self.mock_driver} def _process_router_instance_for_agent(self, agent, ri, router): ri.router = router if not ri.radvd: ri.radvd = ra.DaemonMonitor(router['id'], ri.ns_name, agent.process_monitor, ri.get_internal_device_name, self.conf) ri.process() class IptablesFixture(fixtures.Fixture): def _setUp(self): # We MUST save and restore random_fully because it is a class # attribute and could change state in some tests, which can cause # the other router test cases to randomly fail due to race conditions. self.random_fully = iptables_manager.IptablesManager.random_fully iptables_manager.IptablesManager.random_fully = True self.addCleanup(self._reset) def _reset(self): iptables_manager.IptablesManager.random_fully = self.random_fully class TestBasicRouterOperations(BasicRouterOperationsFramework): def setUp(self): super(TestBasicRouterOperations, self).setUp() self.useFixture(IptablesFixture()) def test_request_id_changes(self): a = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.assertNotEqual(a.context.request_id, a.context.request_id) self.useFixture(IptablesFixture()) def test_init_ha_conf(self): with mock.patch('os.path.dirname', return_value='/etc/ha/'): l3_agent.L3NATAgent(HOSTNAME, self.conf) self.ensure_dir.assert_called_once_with('/etc/ha/', mode=0o755) def test_enqueue_state_change_router_not_found(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) non_existent_router = 42 # Make sure the exceptional code path has coverage agent.enqueue_state_change(non_existent_router, 'master') def test_enqueue_state_change_metadata_disable(self): self.conf.set_override('enable_metadata_proxy', False) agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = mock.Mock() router_info = mock.MagicMock() agent.router_info[router.id] = router_info agent._update_metadata_proxy = mock.Mock() agent.enqueue_state_change(router.id, 'master') self.assertFalse(agent._update_metadata_proxy.call_count) def test_enqueue_state_change_l3_extension(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = mock.Mock() router_info = mock.MagicMock() agent.router_info[router.id] = router_info agent.l3_ext_manager.ha_state_change = mock.Mock() agent.enqueue_state_change(router.id, 'master') agent.l3_ext_manager.ha_state_change.assert_called_once_with( agent.context, {'router_id': router.id, 'state': 'master'}) def test_enqueue_state_change_router_active_ha(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = {'distributed': False} router_info = mock.MagicMock(router=router) with mock.patch.object( agent.metadata_driver, 'spawn_monitored_metadata_proxy' ) as spawn_metadata_proxy, mock.patch.object( agent.metadata_driver, 'destroy_monitored_metadata_proxy' ) as destroy_metadata_proxy: agent._update_metadata_proxy(router_info, "router_id", "master") spawn_metadata_proxy.assert_called() destroy_metadata_proxy.assert_not_called() def test_enqueue_state_change_router_standby_ha(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = {'distributed': False} router_info = mock.MagicMock(router=router) with mock.patch.object( agent.metadata_driver, 'spawn_monitored_metadata_proxy' ) as spawn_metadata_proxy, mock.patch.object( agent.metadata_driver, 'destroy_monitored_metadata_proxy' ) as destroy_metadata_proxy: agent._update_metadata_proxy(router_info, "router_id", "standby") spawn_metadata_proxy.assert_not_called() destroy_metadata_proxy.assert_called() def test_enqueue_state_change_router_standby_ha_dvr(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = {'distributed': True} router_info = mock.MagicMock(router=router) with mock.patch.object( agent.metadata_driver, 'spawn_monitored_metadata_proxy' ) as spawn_metadata_proxy, mock.patch.object( agent.metadata_driver, 'destroy_monitored_metadata_proxy' ) as destroy_metadata_proxy: agent._update_metadata_proxy(router_info, "router_id", "standby") spawn_metadata_proxy.assert_called() destroy_metadata_proxy.assert_not_called() def _test__configure_ipv6_params_helper(self, state, gw_port_id): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router_info = l3router.RouterInfo(agent, _uuid(), {}, **self.ri_kwargs) if gw_port_id: router_info.ex_gw_port = {'id': gw_port_id} expected_forwarding_state = state == 'master' with mock.patch.object( router_info.driver, "configure_ipv6_forwarding" ) as configure_ipv6_forwarding, mock.patch.object( router_info, "_configure_ipv6_params_on_gw" ) as configure_ipv6_on_gw: agent._configure_ipv6_params(router_info, state) if state == 'master': configure_ipv6_forwarding.assert_called_once_with( router_info.ns_name, 'all', expected_forwarding_state) else: configure_ipv6_forwarding.assert_not_called() if gw_port_id: interface_name = router_info.get_external_device_name( router_info.ex_gw_port['id']) configure_ipv6_on_gw.assert_called_once_with( router_info.ex_gw_port, router_info.ns_name, interface_name, expected_forwarding_state) else: configure_ipv6_on_gw.assert_not_called() def test__configure_ipv6_params_master(self): self._test__configure_ipv6_params_helper('master', gw_port_id=_uuid()) def test__configure_ipv6_params_backup(self): self._test__configure_ipv6_params_helper('backup', gw_port_id=_uuid()) def test__configure_ipv6_params_master_no_gw_port(self): self._test__configure_ipv6_params_helper('master', gw_port_id=None) def test__configure_ipv6_params_backup_no_gw_port(self): self._test__configure_ipv6_params_helper('backup', gw_port_id=None) def test_check_ha_state_for_router_master_standby(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = mock.Mock() router.id = '1234' router_info = mock.MagicMock() agent.router_info[router.id] = router_info router_info.ha_state = 'master' with mock.patch.object(agent.state_change_notifier, 'queue_event') as queue_event: agent.check_ha_state_for_router(router.id, n_const.HA_ROUTER_STATE_STANDBY) queue_event.assert_called_once_with((router.id, 'master')) def test_check_ha_state_for_router_standby_standby(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = mock.Mock() router.id = '1234' router_info = mock.MagicMock() agent.router_info[router.id] = router_info router_info.ha_state = 'backup' with mock.patch.object(agent.state_change_notifier, 'queue_event') as queue_event: agent.check_ha_state_for_router(router.id, n_const.HA_ROUTER_STATE_STANDBY) queue_event.assert_not_called() def test_periodic_sync_routers_task_raise_exception(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.plugin_api.get_router_ids.return_value = ['fake_id'] self.plugin_api.get_routers.side_effect = ValueError self.assertRaises(ValueError, agent.periodic_sync_routers_task, agent.context) self.assertTrue(agent.fullsync) def test_l3_initial_report_state_done(self): with mock.patch.object(l3_agent.L3NATAgentWithStateReport, 'periodic_sync_routers_task'),\ mock.patch.object(agent_rpc.PluginReportStateAPI, 'report_state') as report_state,\ mock.patch.object(eventlet, 'spawn_n'): agent = l3_agent.L3NATAgentWithStateReport(host=HOSTNAME, conf=self.conf) self.assertTrue(agent.agent_state['start_flag']) agent.after_start() report_state.assert_called_once_with(agent.context, agent.agent_state, True) self.assertIsNone(agent.agent_state.get('start_flag')) def test_report_state_revival_logic(self): with mock.patch.object(agent_rpc.PluginReportStateAPI, 'report_state') as report_state: agent = l3_agent.L3NATAgentWithStateReport(host=HOSTNAME, conf=self.conf) report_state.return_value = agent_consts.AGENT_REVIVED agent._report_state() self.assertTrue(agent.fullsync) agent.fullsync = False report_state.return_value = agent_consts.AGENT_ALIVE agent._report_state() self.assertFalse(agent.fullsync) def test_periodic_sync_routers_task_call_clean_stale_namespaces(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.plugin_api.get_routers.return_value = [] agent.periodic_sync_routers_task(agent.context) self.assertFalse(agent.namespaces_manager._clean_stale) def test_periodic_sync_routers_task_call_ensure_snat_cleanup(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.conf.agent_mode = 'dvr_snat' dvr_ha_router = {'id': _uuid(), 'external_gateway_info': {}, 'routes': [], 'distributed': True, 'ha': True} dvr_router = {'id': _uuid(), 'external_gateway_info': {}, 'routes': [], 'distributed': True, 'ha': False} routers = [dvr_router, dvr_ha_router] self.plugin_api.get_router_ids.return_value = [r['id'] for r in routers] self.plugin_api.get_routers.return_value = routers with mock.patch.object(namespace_manager.NamespaceManager, 'ensure_snat_cleanup') as ensure_snat_cleanup: agent.periodic_sync_routers_task(agent.context) ensure_snat_cleanup.assert_called_once_with(dvr_router['id']) def test_periodic_sync_routers_task_call_clean_stale_meta_proxies(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) stale_router_ids = [_uuid(), _uuid()] active_routers = [{'id': _uuid()}, {'id': _uuid()}] self.plugin_api.get_router_ids.return_value = [r['id'] for r in active_routers] self.plugin_api.get_routers.return_value = active_routers namespace_list = [namespaces.NS_PREFIX + r_id for r_id in stale_router_ids] namespace_list += [namespaces.NS_PREFIX + r['id'] for r in active_routers] self.list_network_namespaces.return_value = namespace_list driver = metadata_driver.MetadataDriver with mock.patch.object( driver, 'destroy_monitored_metadata_proxy') as destroy_proxy: agent.periodic_sync_routers_task(agent.context) expected_calls = [ mock.call( mock.ANY, r_id, agent.conf, namespaces.NS_PREFIX + r_id) for r_id in stale_router_ids] self.assertEqual(len(stale_router_ids), destroy_proxy.call_count) destroy_proxy.assert_has_calls(expected_calls, any_order=True) def test_router_info_create(self): id = _uuid() agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(agent, id, {}, **self.ri_kwargs) self.assertTrue(ri.ns_name.endswith(id)) def test_router_info_create_with_router(self): ns_id = _uuid() subnet_id = _uuid() ex_gw_port = {'id': _uuid(), 'network_id': _uuid(), 'fixed_ips': [{'ip_address': '19.4.4.4', 'prefixlen': 24, 'subnet_id': subnet_id}], 'subnets': [{'id': subnet_id, 'cidr': '19.4.4.0/24', 'gateway_ip': '19.4.4.1'}]} router = { 'id': _uuid(), 'enable_snat': True, 'routes': [], 'gw_port': ex_gw_port} agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(agent, ns_id, router, **self.ri_kwargs) self.assertTrue(ri.ns_name.endswith(ns_id)) self.assertEqual(router, ri.router) def test_agent_create(self): l3_agent.L3NATAgent(HOSTNAME, self.conf) def _test_internal_network_action(self, action): router = l3_test_common.prepare_router_data(num_internal_ports=2) router_id = router['id'] agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(agent, router_id, router, **self.ri_kwargs) port = {'network_id': _uuid(), 'id': _uuid(), 'mac_address': 'ca:fe:de:ad:be:ef', 'mtu': 1500, 'fixed_ips': [{'subnet_id': _uuid(), 'ip_address': '99.0.1.9', 'prefixlen': 24}]} interface_name = ri.get_internal_device_name(port['id']) if action == 'add': self.device_exists.return_value = False ri.internal_network_added(port) self.assertEqual(1, self.mock_driver.plug.call_count) self.assertEqual(1, self.mock_driver.init_router_port.call_count) self.send_adv_notif.assert_called_once_with(ri.ns_name, interface_name, '99.0.1.9') elif action == 'remove': self.device_exists.return_value = True ri.internal_network_removed(port) self.assertEqual(1, self.mock_driver.unplug.call_count) else: raise Exception("Invalid action %s" % action) @staticmethod def _fixed_ip_cidr(fixed_ip): return '%s/%s' % (fixed_ip['ip_address'], fixed_ip['prefixlen']) def _test_internal_network_action_dist(self, action, scope_match=False): router = l3_test_common.prepare_router_data(num_internal_ports=2) agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) subnet_id = _uuid() port = {'network_id': _uuid(), 'id': _uuid(), 'mac_address': 'ca:fe:de:ad:be:ef', 'mtu': 1500, 'fixed_ips': [{'subnet_id': subnet_id, 'ip_address': '99.0.1.9', 'prefixlen': 24}], 'subnets': [{'id': subnet_id}]} ri.router['gw_port_host'] = HOSTNAME agent.host = HOSTNAME agent.conf.agent_mode = 'dvr_snat' sn_port = {'fixed_ips': [{'ip_address': '20.0.0.31', 'subnet_id': _uuid()}], 'subnets': [{'gateway_ip': '20.0.0.1'}], 'extra_subnets': [{'cidr': '172.16.0.0/24'}], 'id': _uuid(), 'network_id': _uuid(), 'mtu': 1500, 'mac_address': 'ca:fe:de:ad:be:ef'} ex_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', 'prefixlen': 24, 'subnet_id': _uuid()}], 'subnets': [{'gateway_ip': '20.0.0.1'}], 'extra_subnets': [{'cidr': '172.16.0.0/24'}], 'id': _uuid(), portbindings.HOST_ID: HOSTNAME, 'network_id': _uuid(), 'mtu': 1500, 'mac_address': 'ca:fe:de:ad:be:ef'} ri.snat_ports = sn_port ri.ex_gw_port = ex_gw_port ri.snat_namespace = mock.Mock() if scope_match: ri._check_if_address_scopes_match = mock.Mock(return_value=True) else: ri._check_if_address_scopes_match = mock.Mock(return_value=False) if action == 'add': self.device_exists.return_value = False ri.get_snat_port_for_internal_port = mock.Mock( return_value=sn_port) ri._snat_redirect_add = mock.Mock() ri._set_subnet_arp_info = mock.Mock() ri._internal_network_added = mock.Mock() ri._set_subnet_arp_info = mock.Mock() ri._port_has_ipv6_subnet = mock.Mock(return_value=False) ri._add_interface_routing_rule_to_router_ns = mock.Mock() ri._add_interface_route_to_fip_ns = mock.Mock() ri.internal_network_added(port) self.assertEqual(2, ri._internal_network_added.call_count) ri._set_subnet_arp_info.assert_called_once_with(subnet_id) ri._internal_network_added.assert_called_with( dvr_snat_ns.SnatNamespace.get_snat_ns_name(ri.router['id']), sn_port['network_id'], sn_port['id'], sn_port['fixed_ips'], sn_port['mac_address'], ri._get_snat_int_device_name(sn_port['id']), lib_constants.SNAT_INT_DEV_PREFIX, mtu=1500) self.assertTrue(ri._check_if_address_scopes_match.called) if scope_match: self.assertTrue( ri._add_interface_routing_rule_to_router_ns.called) self.assertTrue( ri._add_interface_route_to_fip_ns.called) self.assertEqual(0, ri._snat_redirect_add.call_count) else: self.assertFalse( ri._add_interface_routing_rule_to_router_ns.called) self.assertFalse( ri._add_interface_route_to_fip_ns.called) self.assertEqual(1, ri._snat_redirect_add.call_count) elif action == 'remove': self.device_exists.return_value = False ri.get_snat_port_for_internal_port = mock.Mock( return_value=sn_port) ri._delete_arp_cache_for_internal_port = mock.Mock() ri._snat_redirect_modify = mock.Mock() ri._port_has_ipv6_subnet = mock.Mock(return_value=False) ri._delete_interface_routing_rule_in_router_ns = mock.Mock() ri._delete_interface_route_in_fip_ns = mock.Mock() ri.internal_network_removed(port) self.assertEqual( 1, ri._delete_arp_cache_for_internal_port.call_count) self.assertTrue(ri._check_if_address_scopes_match.called) if scope_match: self.assertFalse(ri._snat_redirect_modify.called) self.assertTrue( ri._delete_interface_routing_rule_in_router_ns.called) self.assertTrue( ri._delete_interface_route_in_fip_ns.called) else: ri._snat_redirect_modify.assert_called_with( sn_port, port, ri.get_internal_device_name(port['id']), is_add=False) self.assertFalse( ri._delete_interface_routing_rule_in_router_ns.called) self.assertFalse( ri._delete_interface_route_in_fip_ns.called) def test_agent_add_internal_network(self): self._test_internal_network_action('add') def test_agent_add_internal_network_dist(self): self._test_internal_network_action_dist('add') def test_agent_add_internal_network_dist_with_addr_scope_match(self): self._test_internal_network_action_dist('add', scope_match=True) def test_agent_remove_internal_network(self): self._test_internal_network_action('remove') def test_agent_remove_internal_network_dist_with_addr_scope_mismatch(self): self._test_internal_network_action_dist('remove', scope_match=True) def test_agent_remove_internal_network_dist(self): self._test_internal_network_action_dist('remove') def _add_external_gateway(self, ri, router, ex_gw_port, interface_name, use_fake_fip=False, no_subnet=False, no_sub_gw=None, dual_stack=False): self.device_exists.return_value = False if no_sub_gw is None: no_sub_gw = [] if use_fake_fip: fake_fip = {'floatingips': [{'id': _uuid(), 'floating_ip_address': '192.168.1.34', 'fixed_ip_address': '192.168.0.1', 'port_id': _uuid()}]} router[lib_constants.FLOATINGIP_KEY] = fake_fip['floatingips'] ri.external_gateway_added(ex_gw_port, interface_name) if not router.get('distributed'): self.assertEqual(1, self.mock_driver.plug.call_count) self.assertEqual(1, self.mock_driver.init_router_port.call_count) if no_subnet and not dual_stack: self.assertEqual(0, self.send_adv_notif.call_count) ip_cidrs = [] kwargs = {'preserve_ips': [], 'namespace': 'qrouter-' + router['id'], 'extra_subnets': [], 'clean_connections': True} else: exp_arp_calls = [mock.call(ri.ns_name, interface_name, '20.0.0.30')] if dual_stack and not no_sub_gw: exp_arp_calls += [mock.call(ri.ns_name, interface_name, '2001:192:168:100::2')] self.send_adv_notif.assert_has_calls(exp_arp_calls) ip_cidrs = ['20.0.0.30/24'] if dual_stack: if not no_sub_gw: ip_cidrs.append('2001:192:168:100::2/64') kwargs = {'preserve_ips': ['192.168.1.34/32'], 'namespace': 'qrouter-' + router['id'], 'extra_subnets': [{'cidr': '172.16.0.0/24'}], 'clean_connections': True} self.mock_driver.init_router_port.assert_called_with( interface_name, ip_cidrs, **kwargs) else: ri._create_dvr_gateway.assert_called_once_with( ex_gw_port, interface_name) def _set_ri_kwargs(self, agent, router_id, router): self.ri_kwargs['agent'] = agent self.ri_kwargs['router_id'] = router_id self.ri_kwargs['router'] = router def _test_external_gateway_action(self, action, router, dual_stack=False): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ex_net_id = _uuid() sn_port = self.snat_ports[1] # Special setup for dvr routers if router.get('distributed'): agent.conf.agent_mode = 'dvr_snat' agent.host = HOSTNAME self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ri._create_dvr_gateway = mock.Mock() ri.get_snat_interfaces = mock.Mock(return_value=self.snat_ports) ri.snat_ports = self.snat_ports ri._create_snat_namespace() ri.fip_ns = agent.get_fip_ns(ex_net_id) ri.internal_ports = self.snat_ports else: ri = l3router.RouterInfo( agent, router['id'], router, **self.ri_kwargs) ri.use_ipv6 = False subnet_id = _uuid() fixed_ips = [{'subnet_id': subnet_id, 'ip_address': '20.0.0.30', 'prefixlen': 24}] subnets = [{'id': subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}] if dual_stack: ri.use_ipv6 = True subnet_id_v6 = _uuid() fixed_ips.append({'subnet_id': subnet_id_v6, 'ip_address': '2001:192:168:100::2', 'prefixlen': 64}) subnets.append({'id': subnet_id_v6, 'cidr': '2001:192:168:100::/64', 'gateway_ip': '2001:192:168:100::1'}) ex_gw_port = {'fixed_ips': fixed_ips, 'subnets': subnets, 'extra_subnets': [{'cidr': '172.16.0.0/24'}], 'id': _uuid(), 'network_id': ex_net_id, 'mtu': 1500, 'mac_address': 'ca:fe:de:ad:be:ef'} ex_gw_port_no_sub = {'fixed_ips': [], 'id': _uuid(), 'network_id': ex_net_id, 'mtu': 1500, 'mac_address': 'ca:fe:de:ad:be:ef'} interface_name = ri.get_external_device_name(ex_gw_port['id']) if action == 'add': self._add_external_gateway(ri, router, ex_gw_port, interface_name, use_fake_fip=True, dual_stack=dual_stack) elif action == 'add_no_sub': ri.use_ipv6 = True self._add_external_gateway(ri, router, ex_gw_port_no_sub, interface_name, no_subnet=True) elif action == 'add_no_sub_v6_gw': ri.use_ipv6 = True self.conf.set_override('ipv6_gateway', 'fe80::f816:3eff:fe2e:1') if dual_stack: use_fake_fip = True # Remove v6 entries del ex_gw_port['fixed_ips'][-1] del ex_gw_port['subnets'][-1] else: use_fake_fip = False ex_gw_port = ex_gw_port_no_sub self._add_external_gateway(ri, router, ex_gw_port, interface_name, no_subnet=True, no_sub_gw='fe80::f816:3eff:fe2e:1', use_fake_fip=use_fake_fip, dual_stack=dual_stack) elif action == 'remove': self.device_exists.return_value = True ri.get_snat_port_for_internal_port = mock.Mock( return_value=sn_port) ri._snat_redirect_remove = mock.Mock() if router.get('distributed'): ri.snat_iptables_manager = iptables_manager.IptablesManager( namespace=ri.snat_namespace.name, use_ipv6=ri.use_ipv6) ri.fip_ns.delete_rtr_2_fip_link = mock.Mock() ri.router['gw_port'] = "" ri.external_gateway_removed(ex_gw_port, interface_name) if not router.get('distributed'): self.mock_driver.unplug.assert_called_once_with( interface_name, bridge=agent.conf.external_network_bridge, namespace=mock.ANY, prefix=mock.ANY) else: ri._snat_redirect_remove.assert_called_with( sn_port, sn_port, ri.get_internal_device_name(sn_port['id'])) ri.get_snat_port_for_internal_port.assert_called_with( mock.ANY, ri.snat_ports) self.assertTrue(ri.fip_ns.delete_rtr_2_fip_link.called) else: raise Exception("Invalid action %s" % action) def _test_external_gateway_updated(self, dual_stack=False): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(num_internal_ports=2) ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.use_ipv6 = False interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test( self, ri, dual_stack=dual_stack) fake_fip = {'floatingips': [{'id': _uuid(), 'floating_ip_address': '192.168.1.34', 'fixed_ip_address': '192.168.0.1', 'port_id': _uuid()}]} router[lib_constants.FLOATINGIP_KEY] = fake_fip['floatingips'] ri.external_gateway_updated(ex_gw_port, interface_name) self.assertEqual(1, self.mock_driver.plug.call_count) self.assertEqual(1, self.mock_driver.init_router_port.call_count) exp_arp_calls = [mock.call(ri.ns_name, interface_name, '20.0.0.30')] if dual_stack: ri.use_ipv6 = True exp_arp_calls += [mock.call(ri.ns_name, interface_name, '2001:192:168:100::2')] self.send_adv_notif.assert_has_calls(exp_arp_calls) ip_cidrs = ['20.0.0.30/24'] gateway_ips = ['20.0.0.1'] if dual_stack: ip_cidrs.append('2001:192:168:100::2/64') gateway_ips.append('2001:192:168:100::1') kwargs = {'preserve_ips': ['192.168.1.34/32'], 'namespace': 'qrouter-' + router['id'], 'extra_subnets': [{'cidr': '172.16.0.0/24'}], 'clean_connections': True} self.mock_driver.init_router_port.assert_called_with(interface_name, ip_cidrs, **kwargs) def test_external_gateway_updated(self): self._test_external_gateway_updated() def test_external_gateway_updated_dual_stack(self): self._test_external_gateway_updated(dual_stack=True) def test_external_gateway_updated_dvr(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.conf.agent_mode = 'dvr_snat' agent.host = HOSTNAME router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True router['gw_port_host'] = HOSTNAME self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ri._create_dvr_gateway = mock.Mock() ri.get_snat_interfaces = mock.Mock(return_value=self.snat_ports) ri.snat_ports = self.snat_ports ri._create_snat_namespace() ex_net_id = _uuid() ri.fip_ns = agent.get_fip_ns(ex_net_id) ri.internal_ports = self.snat_ports ri.use_ipv6 = False interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test( self, ri) fake_fip = {'floatingips': [{'id': _uuid(), 'floating_ip_address': '192.168.1.34', 'fixed_ip_address': '192.168.0.1', 'port_id': _uuid(), 'dvr_snat_bound': True}]} router[lib_constants.FLOATINGIP_KEY] = fake_fip['floatingips'] ri.external_gateway_updated(ex_gw_port, interface_name) self.assertEqual(1, self.mock_driver.plug.call_count) self.assertEqual(1, self.mock_driver.init_router_port.call_count) exp_arp_calls = [mock.call(ri.snat_namespace.name, interface_name, '20.0.0.30')] self.send_adv_notif.assert_has_calls(exp_arp_calls) ip_cidrs = ['20.0.0.30/24'] kwargs = {'preserve_ips': ['192.168.1.34/32'], 'namespace': ri.snat_namespace.name, 'extra_subnets': [{'cidr': '172.16.0.0/24'}], 'clean_connections': True} self.mock_driver.init_router_port.assert_called_with(interface_name, ip_cidrs, **kwargs) def test_dvr_edge_router_init_for_snat_namespace_object(self): router = {'id': _uuid()} self._set_ri_kwargs(mock.Mock(), router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) # Make sure that ri.snat_namespace object is created when the # router is initialized, and that it's name matches the gw # namespace name self.assertIsNotNone(ri.snat_namespace) self.assertEqual(ri.snat_namespace.name, ri.get_gw_ns_name()) def test_ext_gw_updated_calling_snat_ns_delete_if_gw_port_host_none( self): """Test to check the impact of snat_namespace object. This function specifically checks the impact of the snat namespace object value on external_gateway_removed for deleting snat_namespace when the gw_port_host mismatches or none. """ router = l3_test_common.prepare_router_data(num_internal_ports=2) self._set_ri_kwargs(mock.Mock(), router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) with mock.patch.object(dvr_snat_ns.SnatNamespace, 'delete') as snat_ns_delete: interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test( self, ri) router['gw_port_host'] = '' ri._snat_redirect_remove = mock.Mock() ri.external_gateway_updated(ex_gw_port, interface_name) if router['gw_port_host'] != ri.host: self.assertFalse(ri._snat_redirect_remove.called) self.assertEqual(1, snat_ns_delete.call_count) @mock.patch.object(namespaces.Namespace, 'delete') def test_snat_ns_delete_not_called_when_snat_namespace_does_not_exist( self, mock_ns_del): """Test to check the impact of snat_namespace object. This function specifically checks the impact of the snat namespace object initialization without the actual creation of snat_namespace. When deletes are issued to the snat namespace based on the snat namespace object existence, it should be checking for the valid namespace existence before it tries to delete. """ router = l3_test_common.prepare_router_data(num_internal_ports=2) self._set_ri_kwargs(mock.Mock(), router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) # Make sure we set a return value to emulate the non existence # of the namespace. self.mock_ip.netns.exists.return_value = False self.assertIsNotNone(ri.snat_namespace) interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test(self, ri) ri._external_gateway_removed = mock.Mock() ri.external_gateway_removed(ex_gw_port, interface_name) self.assertFalse(mock_ns_del.called) def _test_ext_gw_updated_dvr_edge_router(self, host_match, snat_hosted_before=True): """ Helper to test external gw update for edge router on dvr_snat agent :param host_match: True if new gw host should be the same as agent host :param snat_hosted_before: True if agent has already been hosting snat for the router """ router = l3_test_common.prepare_router_data(num_internal_ports=2) self._set_ri_kwargs(mock.Mock(), router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) if snat_hosted_before: ri._create_snat_namespace() snat_ns_name = ri.snat_namespace.name interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test(self, ri) ri._external_gateway_added = mock.Mock() router['gw_port_host'] = ri.host if host_match else (ri.host + 'foo') ri.external_gateway_updated(ex_gw_port, interface_name) if not host_match: self.assertFalse(ri._external_gateway_added.called) if snat_hosted_before: # host mismatch means that snat was rescheduled to another # agent, hence need to verify that gw port was unplugged and # snat namespace was deleted self.mock_driver.unplug.assert_called_with( interface_name, bridge=self.conf.external_network_bridge, namespace=snat_ns_name, prefix=namespaces.EXTERNAL_DEV_PREFIX) else: if not snat_hosted_before: self.assertIsNotNone(ri.snat_namespace) self.assertTrue(ri._external_gateway_added.called) def test_ext_gw_updated_dvr_edge_router(self): self._test_ext_gw_updated_dvr_edge_router(host_match=True) def test_ext_gw_updated_dvr_edge_router_host_mismatch(self): self._test_ext_gw_updated_dvr_edge_router(host_match=False) def test_ext_gw_updated_dvr_edge_router_snat_rescheduled(self): self._test_ext_gw_updated_dvr_edge_router(host_match=True, snat_hosted_before=False) def test_agent_add_external_gateway(self): router = l3_test_common.prepare_router_data(num_internal_ports=2) self._test_external_gateway_action('add', router) def test_agent_add_external_gateway_dual_stack(self): router = l3_test_common.prepare_router_data(num_internal_ports=2) self._test_external_gateway_action('add', router, dual_stack=True) def test_agent_add_external_gateway_dist(self): router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True router['gw_port_host'] = HOSTNAME self._test_external_gateway_action('add', router) def test_agent_add_external_gateway_dist_dual_stack(self): router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True router['gw_port_host'] = HOSTNAME self._test_external_gateway_action('add', router, dual_stack=True) def test_agent_add_external_gateway_no_subnet(self): router = l3_test_common.prepare_router_data(num_internal_ports=2, v6_ext_gw_with_sub=False) self._test_external_gateway_action('add_no_sub', router) def test_agent_add_external_gateway_no_subnet_with_ipv6_gw(self): router = l3_test_common.prepare_router_data(num_internal_ports=2, v6_ext_gw_with_sub=False) self._test_external_gateway_action('add_no_sub_v6_gw', router) def test_agent_add_external_gateway_dual_stack_no_subnet_w_ipv6_gw(self): router = l3_test_common.prepare_router_data(num_internal_ports=2, v6_ext_gw_with_sub=False) self._test_external_gateway_action('add_no_sub_v6_gw', router, dual_stack=True) def test_agent_remove_external_gateway(self): router = l3_test_common.prepare_router_data(num_internal_ports=2) self._test_external_gateway_action('remove', router) def test_agent_remove_external_gateway_dual_stack(self): router = l3_test_common.prepare_router_data(num_internal_ports=2) self._test_external_gateway_action('remove', router, dual_stack=True) def test_agent_remove_external_gateway_dist(self): router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True router['gw_port_host'] = HOSTNAME self._test_external_gateway_action('remove', router) def test_agent_remove_external_gateway_dist_dual_stack(self): router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True router['gw_port_host'] = HOSTNAME self._test_external_gateway_action('remove', router, dual_stack=True) def _verify_snat_mangle_rules(self, nat_rules, mangle_rules, router, random_fully, negate=False): interfaces = router[lib_constants.INTERFACE_KEY] source_cidrs = [] for iface in interfaces: for subnet in iface['subnets']: prefix = subnet['cidr'].split('/')[1] source_cidr = "%s/%s" % (iface['fixed_ips'][0]['ip_address'], prefix) source_cidrs.append(source_cidr) source_nat_ip = router['gw_port']['fixed_ips'][0]['ip_address'] interface_name = ('qg-%s' % router['gw_port']['id'])[:14] mask_rule = ('-m mark ! --mark 0x2/%s -m conntrack --ctstate DNAT ' '-j SNAT --to-source %s' % (n_const.ROUTER_MARK_MASK, source_nat_ip)) snat_rule = ('-o %s -j SNAT --to-source %s' % (interface_name, source_nat_ip)) if random_fully: mask_rule += ' --random-fully' snat_rule += ' --random-fully' expected_rules = [ '! -i %s ! -o %s -m conntrack ! --ctstate DNAT -j ACCEPT' % (interface_name, interface_name), mask_rule, snat_rule] for r in nat_rules: if negate: self.assertNotIn(r.rule, expected_rules) else: self.assertIn(r.rule, expected_rules) expected_rules = [ '-i %s -j MARK --set-xmark 0x2/%s' % (interface_name, n_const.ROUTER_MARK_MASK), '-o %s -m connmark --mark 0x0/%s -j CONNMARK ' '--save-mark --nfmask %s --ctmask %s' % (interface_name, l3router.ADDRESS_SCOPE_MARK_MASK, l3router.ADDRESS_SCOPE_MARK_MASK, l3router.ADDRESS_SCOPE_MARK_MASK)] for r in mangle_rules: if negate: self.assertNotIn(r.rule, expected_rules) else: self.assertIn(r.rule, expected_rules) @mock.patch.object(dvr_router_base.LOG, 'error') def test_get_snat_port_for_internal_port(self, log_error): router = l3_test_common.prepare_router_data(num_internal_ports=4) self._set_ri_kwargs(mock.Mock(), router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) test_port = { 'mac_address': '00:12:23:34:45:56', 'fixed_ips': [{'subnet_id': l3_test_common.get_subnet_id( router[lib_constants.INTERFACE_KEY][0]), 'ip_address': '101.12.13.14'}]} internal_ports = ri.router.get(lib_constants.INTERFACE_KEY, []) # test valid case with mock.patch.object(ri, 'get_snat_interfaces') as get_interfaces: get_interfaces.return_value = [test_port] res_port = ri.get_snat_port_for_internal_port(internal_ports[0]) self.assertEqual(test_port, res_port) # test invalid case test_port['fixed_ips'][0]['subnet_id'] = 1234 res_ip = ri.get_snat_port_for_internal_port(internal_ports[0]) self.assertNotEqual(test_port, res_ip) self.assertIsNone(res_ip) self.assertTrue(log_error.called) @mock.patch.object(dvr_router_base.LOG, 'error') def test_get_snat_port_for_internal_port_ipv6_same_port(self, log_error): router = l3_test_common.prepare_router_data(ip_version=4, enable_snat=True, num_internal_ports=1) ri = dvr_router.DvrEdgeRouter(mock.sentinel.agent, HOSTNAME, router['id'], router, **self.ri_kwargs) # Add two additional IPv6 prefixes on the same interface l3_test_common.router_append_interface(router, count=2, ip_version=6, same_port=True) internal_ports = ri.router.get(lib_constants.INTERFACE_KEY, []) with mock.patch.object(ri, 'get_snat_interfaces') as get_interfaces: get_interfaces.return_value = internal_ports # get the second internal interface in the list res_port = ri.get_snat_port_for_internal_port(internal_ports[1]) self.assertEqual(internal_ports[1], res_port) # tweak the first subnet_id, should still find port based # on second subnet_id test_port = copy.deepcopy(res_port) test_port['fixed_ips'][0]['subnet_id'] = 1234 res_ip = ri.get_snat_port_for_internal_port(test_port) self.assertEqual(internal_ports[1], res_ip) # tweak the second subnet_id, shouldn't match now test_port['fixed_ips'][1]['subnet_id'] = 1234 res_ip = ri.get_snat_port_for_internal_port(test_port) self.assertIsNone(res_ip) self.assertTrue(log_error.called) def test_process_cent_router(self): router = l3_test_common.prepare_router_data() agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) self._test_process_router(ri, agent) def test_process_dist_router(self): router = l3_test_common.prepare_router_data() agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ri.snat_iptables_manager = iptables_manager.IptablesManager( namespace=ri.snat_namespace.name, use_ipv6=ri.use_ipv6) subnet_id = l3_test_common.get_subnet_id( router[lib_constants.INTERFACE_KEY][0]) ri.router['distributed'] = True ri.router['_snat_router_interfaces'] = [{ 'fixed_ips': [{'subnet_id': subnet_id, 'ip_address': '1.2.3.4'}]}] ri.router['gw_port_host'] = None self._test_process_router(ri, agent, is_dvr_edge=True) def _test_process_router(self, ri, agent, is_dvr_edge=False): router = ri.router agent.host = HOSTNAME fake_fip_id = 'fake_fip_id' ri.create_dvr_external_gateway_on_agent = mock.Mock() ri.process_floating_ip_addresses = mock.Mock() ri.process_floating_ip_nat_rules = mock.Mock() ri.process_floating_ip_nat_rules_for_centralized_floatingip = ( mock.Mock()) ri.process_floating_ip_addresses.return_value = { fake_fip_id: 'ACTIVE'} ri.external_gateway_added = mock.Mock() ri.external_gateway_updated = mock.Mock() ri.process_address_scope = mock.Mock() fake_floatingips1 = {'floatingips': [ {'id': fake_fip_id, 'floating_ip_address': '8.8.8.8', 'fixed_ip_address': '7.7.7.7', 'port_id': _uuid(), 'host': HOSTNAME}]} ri.process() ri.process_floating_ip_addresses.assert_called_with(mock.ANY) ri.process_floating_ip_addresses.reset_mock() if not is_dvr_edge: ri.process_floating_ip_nat_rules.assert_called_with() ri.process_floating_ip_nat_rules.reset_mock() elif ri.router.get('gw_port_host') == agent.host: ri.process_floating_ip_nat_rules_for_centralized_floatingip. \ assert_called_with() ri.process_floating_ip_nat_rules_for_centralized_floatingip. \ reset_mock() ri.external_gateway_added.reset_mock() # remap floating IP to a new fixed ip fake_floatingips2 = copy.deepcopy(fake_floatingips1) fake_floatingips2['floatingips'][0]['fixed_ip_address'] = '7.7.7.8' router[lib_constants.FLOATINGIP_KEY] = fake_floatingips2['floatingips'] ri.process() ri.process_floating_ip_addresses.assert_called_with(mock.ANY) ri.process_floating_ip_addresses.reset_mock() if not is_dvr_edge: ri.process_floating_ip_nat_rules.assert_called_with() ri.process_floating_ip_nat_rules.reset_mock() elif ri.router.get('gw_port_host') == agent.host: ri.process_floating_ip_nat_rules_for_centralized_floatingip. \ assert_called_with() ri.process_floating_ip_nat_rules_for_centralized_floatingip. \ reset_mock() self.assertEqual(0, ri.external_gateway_added.call_count) self.assertEqual(0, ri.external_gateway_updated.call_count) ri.external_gateway_added.reset_mock() ri.external_gateway_updated.reset_mock() # change the ex_gw_port a bit to test gateway update new_gw_port = copy.deepcopy(ri.router['gw_port']) ri.router['gw_port'] = new_gw_port old_ip = (netaddr.IPAddress(ri.router['gw_port'] ['fixed_ips'][0]['ip_address'])) ri.router['gw_port']['fixed_ips'][0]['ip_address'] = str(old_ip + 1) ri.process() ri.process_floating_ip_addresses.reset_mock() ri.process_floating_ip_nat_rules.reset_mock() self.assertEqual(0, ri.external_gateway_added.call_count) self.assertEqual(1, ri.external_gateway_updated.call_count) # remove just the floating ips del router[lib_constants.FLOATINGIP_KEY] ri.process() ri.process_floating_ip_addresses.assert_called_with(mock.ANY) ri.process_floating_ip_addresses.reset_mock() if not is_dvr_edge: ri.process_floating_ip_nat_rules.assert_called_with() ri.process_floating_ip_nat_rules.reset_mock() elif ri.router.get('gw_port_host') == agent.host: ri.process_floating_ip_nat_rules_for_centralized_floatingip. \ assert_called_with() ri.process_floating_ip_nat_rules_for_centralized_floatingip. \ reset_mock() # now no ports so state is torn down del router[lib_constants.INTERFACE_KEY] del router['gw_port'] ri.process() self.assertEqual(1, self.send_adv_notif.call_count) distributed = ri.router.get('distributed', False) self.assertEqual(distributed, ri.process_floating_ip_addresses.called) self.assertEqual(distributed, ri.process_floating_ip_nat_rules.called) def _test_process_floating_ip_addresses_add(self, ri, agent): floating_ips = ri.get_floating_ips() fip_id = floating_ips[0]['id'] device = self.mock_ip_dev device.addr.list.return_value = [] ri.iptables_manager.ipv4['nat'] = mock.MagicMock() ex_gw_port = {'id': _uuid(), 'network_id': mock.sentinel.ext_net_id} ri.add_floating_ip = mock.Mock( return_value=lib_constants.FLOATINGIP_STATUS_ACTIVE) with mock.patch.object(lla.LinkLocalAllocator, '_write'): if ri.router['distributed']: ri.fip_ns = agent.get_fip_ns(ex_gw_port['network_id']) ri.create_dvr_external_gateway_on_agent(ex_gw_port) fip_statuses = ri.process_floating_ip_addresses( mock.sentinel.interface_name) self.assertEqual({fip_id: lib_constants.FLOATINGIP_STATUS_ACTIVE}, fip_statuses) ri.add_floating_ip.assert_called_once_with( floating_ips[0], mock.sentinel.interface_name, device) @mock.patch.object(lla.LinkLocalAllocator, '_write') def test_create_dvr_fip_interfaces_if_fipnamespace_exist(self, lla_write): fake_network_id = _uuid() subnet_id = _uuid() fake_floatingips = {'floatingips': [ {'id': _uuid(), 'floating_ip_address': '20.0.0.3', 'fixed_ip_address': '192.168.0.1', 'floating_network_id': _uuid(), 'port_id': _uuid(), 'host': HOSTNAME}]} agent_gateway_port = ( [{'fixed_ips': [ {'ip_address': '20.0.0.30', 'prefixlen': 24, 'subnet_id': subnet_id}], 'subnets': [ {'id': subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}], 'id': _uuid(), 'network_id': fake_network_id, 'mtu': 1500, 'mac_address': 'ca:fe:de:ad:be:ef'}] ) router = l3_test_common.prepare_router_data(enable_snat=True) router[lib_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] router[n_const.FLOATINGIP_AGENT_INTF_KEY] = agent_gateway_port router['distributed'] = True agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ext_gw_port = ri.router.get('gw_port') ri.fip_ns = agent.get_fip_ns(ext_gw_port['network_id']) agent.process_router_add = mock.Mock() ri.fip_ns.create_rtr_2_fip_link = mock.Mock() with mock.patch.object(ri, 'get_floating_ips') as fips, \ mock.patch.object(ri.fip_ns, 'create') as create_fip, \ mock.patch.object(ri, 'get_floating_agent_gw_interface' ) as fip_gw_port: fips.return_value = fake_floatingips fip_gw_port.return_value = agent_gateway_port[0] ri.create_dvr_external_gateway_on_agent(ext_gw_port) ri.connect_rtr_2_fip() ri._get_floatingips_bound_to_host = mock.Mock( return_value=True) self.assertTrue(fip_gw_port.called) self.assertTrue(create_fip.called) self.assertEqual(agent_gateway_port[0], ri.fip_ns.agent_gateway_port) self.assertTrue(ri.rtr_fip_connect) # Now let us associate the fip to the router ri.floating_ip_added_dist(fips, "192.168.0.1/32") # Now let us disassociate the fip from the router ri.floating_ip_removed_dist("192.168.0.1/32") # Calling create_dvr_external_gateway_interfaces again to make # sure that the fip namespace create is not called again. # If the create is not called again, that would contain # the duplicate rules configuration in the fip namespace. ri.create_dvr_external_gateway_on_agent(ext_gw_port) self.assertTrue(fip_gw_port.called) create_fip.assert_called_once_with() self.assertEqual(1, ri.fip_ns.create_rtr_2_fip_link.call_count) @mock.patch.object(lla.LinkLocalAllocator, '_write') def test_floating_ip_not_configured_if_no_host_or_dest_host( self, lla_write): fake_network_id = _uuid() subnet_id = _uuid() fake_floatingips = {'floatingips': [ {'id': _uuid(), 'floating_ip_address': '20.0.0.3', 'fixed_ip_address': '192.168.0.1', 'floating_network_id': _uuid(), 'port_id': _uuid()}]} agent_gateway_port = ( [{'fixed_ips': [ {'ip_address': '20.0.0.30', 'prefixlen': 24, 'subnet_id': subnet_id}], 'subnets': [ {'id': subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}], 'id': _uuid(), 'network_id': fake_network_id, 'mac_address': 'ca:fe:de:ad:be:ef'}] ) router = l3_test_common.prepare_router_data(enable_snat=True) router[lib_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] router[n_const.FLOATINGIP_AGENT_INTF_KEY] = agent_gateway_port router['distributed'] = True agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ext_gw_port = ri.router.get('gw_port') ri.fip_ns = agent.get_fip_ns(ext_gw_port['network_id']) agent.process_router_add = mock.Mock() ri.fip_ns.create_rtr_2_fip_link = mock.Mock() with mock.patch.object(ri, 'get_floating_ips') as fips, \ mock.patch.object(ri, 'get_floating_agent_gw_interface' ) as fip_gw_port, \ mock.patch.object(ri, '_add_floating_ip_rule') as add_rule, \ mock.patch.object(ri.fip_ns, 'create') as create_fip: fips.return_value = fake_floatingips fip_gw_port.return_value = agent_gateway_port[0] ri.create_dvr_external_gateway_on_agent(ext_gw_port) ri.connect_rtr_2_fip() self.assertTrue(fip_gw_port.called) self.assertTrue(create_fip.called) self.assertEqual(agent_gateway_port[0], ri.fip_ns.agent_gateway_port) self.assertTrue(ri.rtr_fip_connect) # Now let us associate the fip to the router status = ri.floating_ip_added_dist(fips, "192.168.0.1/32") self.assertIsNone(status) self.assertEqual(0, self.send_adv_notif.call_count) self.assertFalse(add_rule.called) @mock.patch.object(lla.LinkLocalAllocator, '_write') def test_floating_ip_centralized(self, lla_write): fake_network_id = _uuid() subnet_id = _uuid() fake_floatingips = {'floatingips': [ {'id': _uuid(), 'floating_ip_address': '20.0.0.3', 'fixed_ip_address': '192.168.0.1', 'floating_network_id': _uuid(), 'port_id': _uuid(), 'dvr_snat_bound': True, 'host': None}, {'id': _uuid(), 'floating_ip_address': '20.0.0.4', 'fixed_ip_address': '192.168.0.2', 'floating_network_id': _uuid(), 'port_id': _uuid(), 'dvr_snat_bound': True, 'host': None}]} agent_gateway_port = ( [{'fixed_ips': [ {'ip_address': '20.0.0.30', 'prefixlen': 24, 'subnet_id': subnet_id}], 'subnets': [ {'id': subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}], 'id': _uuid(), 'network_id': fake_network_id, 'mac_address': 'ca:fe:de:ad:be:ef'}] ) router = l3_test_common.prepare_router_data(enable_snat=True) router[lib_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] router[n_const.FLOATINGIP_AGENT_INTF_KEY] = agent_gateway_port router['distributed'] = True agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ext_gw_port = ri.router.get('gw_port') ri.fip_ns = agent.get_fip_ns(ext_gw_port['network_id']) agent.process_router_add = mock.Mock() ri.fip_ns.create_rtr_2_fip_link = mock.Mock() with mock.patch.object(ri, 'get_floating_ips') as fips, \ mock.patch.object(ri, 'add_centralized_floatingip') as add_fip, \ mock.patch.object(ri, 'get_centralized_fip_cidr_set' ) as get_fip_cidrs, \ mock.patch.object(ri, 'get_floating_agent_gw_interface' ) as fip_gw_port, \ mock.patch.object(ri.fip_ns, 'create') as create_fip, \ mock.patch.object(ri, 'remove_centralized_floatingip') as rem_fip: fips.return_value = fake_floatingips fip_gw_port.return_value = agent_gateway_port[0] add_fip.return_value = lib_constants.FLOATINGIP_STATUS_ACTIVE ri.create_dvr_external_gateway_on_agent(ext_gw_port) ri.connect_rtr_2_fip() self.assertTrue(fip_gw_port.called) self.assertTrue(create_fip.called) self.assertEqual(agent_gateway_port[0], ri.fip_ns.agent_gateway_port) self.assertTrue(ri.rtr_fip_connect) # Now let us associate the fip to the router status = ri.floating_ip_added_dist(fips, "192.168.0.1/32") add_fip.assert_called_once_with(fips, "192.168.0.1/32") self.assertEqual(lib_constants.FLOATINGIP_STATUS_ACTIVE, status) # Now let us add the second fip status = ri.floating_ip_added_dist(fips, "192.168.0.2/32") self.assertEqual(lib_constants.FLOATINGIP_STATUS_ACTIVE, status) device = mock.Mock() get_fip_cidrs.return_value = set( ["192.168.0.2/32", "192.168.0.1/32"]) self.assertEqual(set(["192.168.0.2/32", "192.168.0.1/32"]), ri.get_router_cidrs(device)) ri.floating_ip_removed_dist("192.168.0.1/32") rem_fip.assert_called_once_with("192.168.0.1/32") self.assertTrue(get_fip_cidrs.called) get_fip_cidrs.return_value = set(["192.168.0.2/32"]) self.assertEqual(set(["192.168.0.2/32"]), ri.get_router_cidrs(device)) @mock.patch.object(lla.LinkLocalAllocator, '_write') def test_create_dvr_fip_interfaces_for_late_binding(self, lla_write): fake_network_id = _uuid() fake_subnet_id = _uuid() fake_floatingips = {'floatingips': [ {'id': _uuid(), 'floating_ip_address': '20.0.0.3', 'fixed_ip_address': '192.168.0.1', 'floating_network_id': _uuid(), 'port_id': _uuid(), 'host': HOSTNAME}]} agent_gateway_port = ( {'fixed_ips': [ {'ip_address': '20.0.0.30', 'prefixlen': 24, 'subnet_id': fake_subnet_id}], 'subnets': [ {'id': fake_subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}], 'id': _uuid(), 'network_id': fake_network_id, 'mtu': 1500, 'mac_address': 'ca:fe:de:ad:be:ef'} ) router = l3_test_common.prepare_router_data(enable_snat=True) router[lib_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] router[n_const.FLOATINGIP_AGENT_INTF_KEY] = [] router['distributed'] = True agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ext_gw_port = ri.router.get('gw_port') ri.fip_ns = agent.get_fip_ns(ext_gw_port['network_id']) ri.fip_ns.subscribe = mock.Mock() with mock.patch.object(agent.plugin_rpc, 'get_agent_gateway_port') as fip_gw_port: fip_gw_port.return_value = agent_gateway_port ri.create_dvr_external_gateway_on_agent(ext_gw_port) ri.connect_rtr_2_fip() self.assertTrue(fip_gw_port.called) self.assertTrue(ri.rtr_fip_connect) self.assertEqual(agent_gateway_port, ri.fip_ns.agent_gateway_port) @mock.patch.object(lla.LinkLocalAllocator, '_write') def test_create_dvr_fip_interfaces(self, lla_write): fake_network_id = _uuid() subnet_id = _uuid() fake_floatingips = {'floatingips': [ {'id': _uuid(), 'floating_ip_address': '20.0.0.3', 'fixed_ip_address': '192.168.0.1', 'floating_network_id': _uuid(), 'port_id': _uuid(), 'host': HOSTNAME}]} agent_gateway_port = ( [{'fixed_ips': [ {'ip_address': '20.0.0.30', 'prefixlen': 24, 'subnet_id': subnet_id}], 'subnets': [ {'id': subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}], 'id': _uuid(), 'network_id': fake_network_id, 'mtu': 1500, 'mac_address': 'ca:fe:de:ad:be:ef'}] ) router = l3_test_common.prepare_router_data(enable_snat=True) router[lib_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] router[n_const.FLOATINGIP_AGENT_INTF_KEY] = agent_gateway_port router['distributed'] = True agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ext_gw_port = ri.router.get('gw_port') ri.fip_ns = agent.get_fip_ns(ext_gw_port['network_id']) ri.fip_ns.subscribe = mock.Mock() ri.fip_ns.agent_router_gateway = mock.Mock() agent.process_router_add = mock.Mock() with mock.patch.object( ri, 'get_floating_agent_gw_interface') as fip_gw_port: fip_gw_port.return_value = agent_gateway_port[0] ri.create_dvr_external_gateway_on_agent(ext_gw_port) ri.connect_rtr_2_fip() ri._get_floatingips_bound_to_host = mock.Mock( return_value=True) self.assertTrue(fip_gw_port.called) self.assertEqual(agent_gateway_port[0], ri.fip_ns.agent_gateway_port) self.assertTrue(ri.rtr_fip_connect) self.assertTrue(ri.rtr_fip_subnet) @mock.patch.object(lla.LinkLocalAllocator, '_write') def test_create_dvr_fip_interfaces_for_restart_l3agent_case(self, lla_write): fake_floatingips = {'floatingips': [ {'id': _uuid(), 'floating_ip_address': '20.0.0.3', 'fixed_ip_address': '192.168.0.1', 'floating_network_id': _uuid(), 'port_id': _uuid(), 'host': HOSTNAME}]} agent_gateway_port = ( [{'fixed_ips': [ {'ip_address': '20.0.0.30', 'prefixlen': 24, 'subnet_id': 'subnet_id'}], 'subnets': [ {'id': 'subnet_id', 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}], 'id': _uuid(), 'network_id': 'fake_network_id', 'mtu': 1500, 'mac_address': 'ca:fe:de:ad:be:ef'}] ) router = l3_test_common.prepare_router_data(enable_snat=True) router[lib_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] router[n_const.FLOATINGIP_AGENT_INTF_KEY] = agent_gateway_port router['distributed'] = True agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ext_gw_port = ri.router.get('gw_port') ri.fip_ns = agent.get_fip_ns(ext_gw_port['network_id']) ri.fip_ns.subscribe = mock.Mock(return_value=True) ri.fip_ns.agent_router_gateway = mock.Mock() ri.rtr_fip_subnet = None with mock.patch.object( ri, 'get_floating_agent_gw_interface') as fip_gw_port: fip_gw_port.return_value = agent_gateway_port[0] ri.create_dvr_external_gateway_on_agent(ext_gw_port) ri.connect_rtr_2_fip() ri._get_floatingips_bound_to_host = mock.Mock( return_value=True) self.assertTrue(fip_gw_port.called) self.assertEqual(agent_gateway_port[0], ri.fip_ns.agent_gateway_port) self.assertTrue(ri.rtr_fip_subnet) self.assertTrue(ri.rtr_fip_connect) def test_process_router_cent_floating_ip_add(self): fake_floatingips = {'floatingips': [ {'id': _uuid(), 'floating_ip_address': '15.1.2.3', 'fixed_ip_address': '192.168.0.1', 'status': 'DOWN', 'floating_network_id': _uuid(), 'port_id': _uuid(), 'host': HOSTNAME}]} router = l3_test_common.prepare_router_data(enable_snat=True) router[lib_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.iptables_manager.ipv4['nat'] = mock.MagicMock() ri.get_external_device_name = mock.Mock(return_value='exgw') self._test_process_floating_ip_addresses_add(ri, agent) def _test_process_router_snat_disabled(self, random_fully): iptables_manager.IptablesManager.random_fully = random_fully agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(enable_snat=True) ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # Process with NAT ri.process() orig_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:] orig_mangle_rules = ri.iptables_manager.ipv4['mangle'].rules[:] # Reprocess without NAT router['enable_snat'] = False # Reassign the router object to RouterInfo ri.router = router ri.process() # For some reason set logic does not work well with # IpTablesRule instances nat_rules_delta = [r for r in orig_nat_rules if r not in ri.iptables_manager.ipv4['nat'].rules] self.assertEqual(1, len(nat_rules_delta)) mangle_rules_delta = [ r for r in orig_mangle_rules if r not in ri.iptables_manager.ipv4['mangle'].rules] self.assertEqual(1, len(mangle_rules_delta)) self._verify_snat_mangle_rules(nat_rules_delta, mangle_rules_delta, router, random_fully) self.assertEqual(1, self.send_adv_notif.call_count) def test_process_router_snat_disabled_random_fully(self): self._test_process_router_snat_disabled(True) def test_process_router_snat_disabled_random_fully_false(self): self._test_process_router_snat_disabled(False) def _test_process_router_snat_enabled(self, random_fully): iptables_manager.IptablesManager.random_fully = random_fully agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(enable_snat=False) ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # Process without NAT ri.process() orig_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:] orig_mangle_rules = ri.iptables_manager.ipv4['mangle'].rules[:] # Reprocess with NAT router['enable_snat'] = True # Reassign the router object to RouterInfo ri.router = router ri.process() # For some reason set logic does not work well with # IpTablesRule instances nat_rules_delta = [r for r in ri.iptables_manager.ipv4['nat'].rules if r not in orig_nat_rules] self.assertEqual(1, len(nat_rules_delta)) mangle_rules_delta = [ r for r in ri.iptables_manager.ipv4['mangle'].rules if r not in orig_mangle_rules] self.assertEqual(1, len(mangle_rules_delta)) self._verify_snat_mangle_rules(nat_rules_delta, mangle_rules_delta, router, random_fully) self.assertEqual(1, self.send_adv_notif.call_count) def test_process_router_snat_enabled_random_fully(self): self._test_process_router_snat_enabled(True) def test_process_router_snat_enabled_random_fully_false(self): self._test_process_router_snat_enabled(False) def _test_update_routing_table(self, is_snat_host=True): router = l3_test_common.prepare_router_data() uuid = router['id'] s_netns = 'snat-' + uuid q_netns = 'qrouter-' + uuid fake_route1 = {'destination': '135.207.0.0/16', 'nexthop': '19.4.4.200'} calls = [mock.call('replace', fake_route1, q_netns)] agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, uuid, router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ri._update_routing_table = mock.Mock() with mock.patch.object(ri, '_is_this_snat_host') as snat_host: snat_host.return_value = is_snat_host ri.update_routing_table('replace', fake_route1) if is_snat_host: ri._update_routing_table('replace', fake_route1, s_netns) calls += [mock.call('replace', fake_route1, s_netns)] ri._update_routing_table.assert_has_calls(calls, any_order=True) def test_process_update_snat_routing_table(self): self._test_update_routing_table() def test_process_not_update_snat_routing_table(self): self._test_update_routing_table(is_snat_host=False) def test_process_router_interface_added(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # Process with NAT ri.process() # Add an interface and reprocess l3_test_common.router_append_interface(router) # Reassign the router object to RouterInfo ri.router = router ri.process() # send_ip_addr_adv_notif is called both times process is called self.assertEqual(2, self.send_adv_notif.call_count) def _test_process_ipv6_only_or_dual_stack_gw(self, dual_stack=False): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(ip_version=6, dual_stack=dual_stack) # Get NAT rules without the gw_port gw_port = router['gw_port'] router['gw_port'] = None ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() self._process_router_instance_for_agent(agent, ri, router) orig_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:] # Get NAT rules with the gw_port router['gw_port'] = gw_port ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) p = ri.external_gateway_nat_fip_rules s = ri.external_gateway_nat_snat_rules attrs_to_mock = dict( [(a, mock.DEFAULT) for a in ['external_gateway_nat_fip_rules', 'external_gateway_nat_snat_rules']] ) with mock.patch.multiple(ri, **attrs_to_mock) as mocks: mocks['external_gateway_nat_fip_rules'].side_effect = p mocks['external_gateway_nat_snat_rules'].side_effect = s self._process_router_instance_for_agent(agent, ri, router) new_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:] # NAT rules should only change for dual_stack operation if dual_stack: self.assertTrue( mocks['external_gateway_nat_fip_rules'].called) self.assertTrue( mocks['external_gateway_nat_snat_rules'].called) self.assertNotEqual(orig_nat_rules, new_nat_rules) else: self.assertFalse( mocks['external_gateway_nat_fip_rules'].called) self.assertFalse( mocks['external_gateway_nat_snat_rules'].called) self.assertEqual(orig_nat_rules, new_nat_rules) def test_process_ipv6_only_gw(self): self._test_process_ipv6_only_or_dual_stack_gw() def test_process_dual_stack_gw(self): self._test_process_ipv6_only_or_dual_stack_gw(dual_stack=True) def _process_router_ipv6_interface_added( self, router, ra_mode=None, addr_mode=None): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # Process with NAT ri.process() orig_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:] # Add an IPv6 interface and reprocess l3_test_common.router_append_interface(router, count=1, ip_version=6, ra_mode=ra_mode, addr_mode=addr_mode) # Reassign the router object to RouterInfo self._process_router_instance_for_agent(agent, ri, router) # IPv4 NAT rules should not be changed by adding an IPv6 interface nat_rules_delta = [r for r in ri.iptables_manager.ipv4['nat'].rules if r not in orig_nat_rules] self.assertFalse(nat_rules_delta) return ri def _radvd_expected_call_external_process(self, ri, enable=True): expected_calls = [mock.call(uuid=ri.router['id'], service='radvd', default_cmd_callback=mock.ANY, namespace=ri.ns_name, conf=mock.ANY, run_as_root=True)] if enable: expected_calls.append(mock.call().enable(reload_cfg=True)) else: expected_calls.append(mock.call().disable()) return expected_calls def _process_router_ipv6_subnet_added(self, router, ipv6_subnet_modes=None, dns_nameservers=None, network_mtu=0): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) agent.external_gateway_added = mock.Mock() self._process_router_instance_for_agent(agent, ri, router) # Add an IPv6 interface with len(ipv6_subnet_modes) subnets # and reprocess l3_test_common.router_append_subnet( router, count=len(ipv6_subnet_modes), ip_version=6, ipv6_subnet_modes=ipv6_subnet_modes, dns_nameservers=dns_nameservers, network_mtu=network_mtu) # Reassign the router object to RouterInfo self._process_router_instance_for_agent(agent, ri, router) return ri def _assert_ri_process_enabled(self, ri): """Verify that process was enabled for a router instance.""" expected_calls = self._radvd_expected_call_external_process(ri) self.assertEqual(expected_calls, self.external_process.mock_calls) def _assert_ri_process_disabled(self, ri): """Verify that process was disabled for a router instance.""" expected_calls = self._radvd_expected_call_external_process(ri, False) self.assertEqual(expected_calls, self.external_process.mock_calls) def test_process_router_ipv6_interface_added(self): router = l3_test_common.prepare_router_data() ri = self._process_router_ipv6_interface_added(router) self._assert_ri_process_enabled(ri) # Expect radvd configured without prefix self.assertNotIn('prefix', self.utils_replace_file.call_args[0][1]) def test_process_router_ipv6_slaac_interface_added(self): router = l3_test_common.prepare_router_data() ri = self._process_router_ipv6_interface_added( router, ra_mode=lib_constants.IPV6_SLAAC) self._assert_ri_process_enabled(ri) # Expect radvd configured with prefix radvd_config_str = self.utils_replace_file.call_args[0][1] self.assertIn('prefix', radvd_config_str) self.assertIn('AdvAutonomous on', radvd_config_str) def test_process_router_ipv6_dhcpv6_stateful_interface_added(self): router = l3_test_common.prepare_router_data() ri = self._process_router_ipv6_interface_added( router, ra_mode=lib_constants.DHCPV6_STATEFUL) self._assert_ri_process_enabled(ri) # Expect radvd configured with prefix radvd_config_str = self.utils_replace_file.call_args[0][1] self.assertIn('prefix', radvd_config_str) self.assertIn('AdvAutonomous off', radvd_config_str) def test_process_router_ipv6_subnets_added(self): router = l3_test_common.prepare_router_data() ri = self._process_router_ipv6_subnet_added(router, ipv6_subnet_modes=[ {'ra_mode': lib_constants.IPV6_SLAAC, 'address_mode': lib_constants.IPV6_SLAAC}, {'ra_mode': lib_constants.DHCPV6_STATELESS, 'address_mode': lib_constants.DHCPV6_STATELESS}, {'ra_mode': lib_constants.DHCPV6_STATEFUL, 'address_mode': lib_constants.DHCPV6_STATEFUL}]) self._assert_ri_process_enabled(ri) radvd_config_str = self.utils_replace_file.call_args[0][1] # Assert we have a prefix from IPV6_SLAAC and a prefix from # DHCPV6_STATELESS on one interface self.assertEqual(3, radvd_config_str.count("prefix")) self.assertEqual(1, radvd_config_str.count("interface")) self.assertEqual(2, radvd_config_str.count("AdvAutonomous on")) self.assertEqual(1, radvd_config_str.count("AdvAutonomous off")) def test_process_router_ipv6_subnets_added_to_existing_port(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) agent.external_gateway_added = mock.Mock() self._process_router_instance_for_agent(agent, ri, router) # Add the first subnet on a new interface l3_test_common.router_append_subnet( router, count=1, ip_version=6, ipv6_subnet_modes=[ {'ra_mode': lib_constants.IPV6_SLAAC, 'address_mode': lib_constants.IPV6_SLAAC}]) self._process_router_instance_for_agent(agent, ri, router) self._assert_ri_process_enabled(ri) radvd_config = self.utils_replace_file.call_args[0][1].split() self.assertEqual(1, len(ri.internal_ports[1]['subnets'])) self.assertEqual(1, len(ri.internal_ports[1]['fixed_ips'])) self.assertEqual(1, radvd_config.count("prefix")) self.assertEqual(1, radvd_config.count("interface")) # Reset mocks to verify radvd enabled and configured correctly # after second subnet added to interface self.external_process.reset_mock() self.utils_replace_file.reset_mock() # Add the second subnet on the same interface interface_id = router[lib_constants.INTERFACE_KEY][1]['id'] l3_test_common.router_append_subnet( router, count=1, ip_version=6, ipv6_subnet_modes=[ {'ra_mode': lib_constants.IPV6_SLAAC, 'address_mode': lib_constants.IPV6_SLAAC}], interface_id=interface_id) self._process_router_instance_for_agent(agent, ri, router) # radvd should have been enabled again and the interface # should have two prefixes self._assert_ri_process_enabled(ri) radvd_config = self.utils_replace_file.call_args[0][1].split() self.assertEqual(2, len(ri.internal_ports[1]['subnets'])) self.assertEqual(2, len(ri.internal_ports[1]['fixed_ips'])) self.assertEqual(2, radvd_config.count("prefix")) self.assertEqual(1, radvd_config.count("interface")) def test_process_router_ipv6v4_interface_added(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # Process with NAT ri.process() # Add an IPv4 and IPv6 interface and reprocess l3_test_common.router_append_interface(router, count=1, ip_version=4) l3_test_common.router_append_interface(router, count=1, ip_version=6) # Reassign the router object to RouterInfo self._process_router_instance_for_agent(agent, ri, router) self._assert_ri_process_enabled(ri) def test_process_router_interface_removed(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(num_internal_ports=2) ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # Process with NAT ri.process() # Add an interface and reprocess del router[lib_constants.INTERFACE_KEY][1] # Reassign the router object to RouterInfo ri.router = router ri.process() # send_ip_addr_adv_notif is called both times process is called self.assertEqual(2, self.send_adv_notif.call_count) def test_process_router_ipv6_interface_removed(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() self._process_router_instance_for_agent(agent, ri, router) # Add an IPv6 interface and reprocess l3_test_common.router_append_interface(router, count=1, ip_version=6) self._process_router_instance_for_agent(agent, ri, router) self._assert_ri_process_enabled(ri) # Reset the calls so we can check for disable radvd self.external_process.reset_mock() self.process_monitor.reset_mock() # Remove the IPv6 interface and reprocess del router[lib_constants.INTERFACE_KEY][1] self._process_router_instance_for_agent(agent, ri, router) self._assert_ri_process_disabled(ri) def test_process_router_ipv6_subnet_removed(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) agent.external_gateway_added = mock.Mock() self._process_router_instance_for_agent(agent, ri, router) # Add an IPv6 interface with two subnets and reprocess l3_test_common.router_append_subnet( router, count=2, ip_version=6, ipv6_subnet_modes=([{'ra_mode': lib_constants.IPV6_SLAAC, 'address_mode': lib_constants.IPV6_SLAAC}] * 2)) self._process_router_instance_for_agent(agent, ri, router) self._assert_ri_process_enabled(ri) # Reset mocks to check for modified radvd config self.utils_replace_file.reset_mock() self.external_process.reset_mock() # Remove one subnet from the interface and reprocess interfaces = copy.deepcopy(router[lib_constants.INTERFACE_KEY]) del interfaces[1]['subnets'][0] del interfaces[1]['fixed_ips'][0] router[lib_constants.INTERFACE_KEY] = interfaces self._process_router_instance_for_agent(agent, ri, router) # Assert radvd was enabled again and that we only have one # prefix on the interface self._assert_ri_process_enabled(ri) radvd_config = self.utils_replace_file.call_args[0][1].split() self.assertEqual(1, len(ri.internal_ports[1]['subnets'])) self.assertEqual(1, len(ri.internal_ports[1]['fixed_ips'])) self.assertEqual(1, radvd_config.count("interface")) self.assertEqual(1, radvd_config.count("prefix")) def test_process_router_internal_network_added_unexpected_error(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() with mock.patch.object( ri, 'internal_network_added') as internal_network_added: # raise RuntimeError to simulate that an unexpected exception # occurs internal_network_added.side_effect = RuntimeError self.assertRaises(RuntimeError, ri.process) self.assertNotIn( router[lib_constants.INTERFACE_KEY][0], ri.internal_ports) # The unexpected exception has been fixed manually internal_network_added.side_effect = None # periodic_sync_routers_task finds out that _rpc_loop failed to # process the router last time, it will retry in the next run. ri.process() # We were able to add the port to ri.internal_ports self.assertIn( router[lib_constants.INTERFACE_KEY][0], ri.internal_ports) def test_process_router_internal_network_removed_unexpected_error(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # add an internal port ri.process() with mock.patch.object( ri, 'internal_network_removed') as internal_net_removed: # raise RuntimeError to simulate that an unexpected exception # occurs internal_net_removed.side_effect = RuntimeError ri.internal_ports[0]['admin_state_up'] = False # The above port is set to down state, remove it. self.assertRaises(RuntimeError, ri.process) self.assertIn( router[lib_constants.INTERFACE_KEY][0], ri.internal_ports) # The unexpected exception has been fixed manually internal_net_removed.side_effect = None # periodic_sync_routers_task finds out that _rpc_loop failed to # process the router last time, it will retry in the next run. ri.process() # We were able to remove the port from ri.internal_ports self.assertNotIn( router[lib_constants.INTERFACE_KEY][0], ri.internal_ports) def test_process_router_floatingip_nochange(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(num_internal_ports=1) fip1 = {'id': _uuid(), 'floating_ip_address': '8.8.8.8', 'fixed_ip_address': '7.7.7.7', 'status': 'ACTIVE', 'port_id': router[lib_constants.INTERFACE_KEY][0]['id']} fip2 = copy.copy(fip1) fip2.update({'id': _uuid(), 'status': 'DOWN', 'floating_ip_address': '9.9.9.9'}) router[lib_constants.FLOATINGIP_KEY] = [fip1, fip2] ri = legacy_router.LegacyRouter(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() with mock.patch.object( agent.plugin_rpc, 'update_floatingip_statuses' ) as mock_update_fip_status,\ mock.patch.object( ri, 'get_centralized_fip_cidr_set') as cent_cidrs,\ mock.patch.object(ri, 'get_router_cidrs') as mock_get_cidrs: cent_cidrs.return_value = set() mock_get_cidrs.return_value = set( [fip1['floating_ip_address'] + '/32']) ri.process() # make sure only the one that wasn't in existing cidrs was sent mock_update_fip_status.assert_called_once_with( mock.ANY, ri.router_id, {fip2['id']: 'ACTIVE'}) @mock.patch.object(l3_agent.LOG, 'exception') def _retrigger_initialize(self, log_exception, delete_fail=False): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = {'id': _uuid(), 'external_gateway_info': {'network_id': 'aaa'}} self.plugin_api.get_routers.return_value = [router] update = resource_processing_queue.ResourceUpdate( router['id'], resource_processing_queue.PRIORITY_SYNC_ROUTERS_TASK, resource=router, timestamp=timeutils.utcnow()) agent._queue.add(update) ri = legacy_router.LegacyRouter(agent, router['id'], router, **self.ri_kwargs) calls = [mock.call('Error while initializing router %s', router['id'])] if delete_fail: # if delete fails, then also retrigger initialize ri.delete = mock.Mock(side_effect=RuntimeError()) calls.append( mock.call('Error while deleting router %s', router['id'])) else: ri.delete = mock.Mock() calls.append( mock.call('Failed to process compatible router: %s' % router['id'])) ri.process = mock.Mock() ri.initialize = mock.Mock(side_effect=RuntimeError()) agent._create_router = mock.Mock(return_value=ri) agent._process_router_update() log_exception.assert_has_calls(calls) ri.initialize.side_effect = None agent._process_router_update() self.assertTrue(ri.delete.called) self.assertEqual(2, ri.initialize.call_count) self.assertEqual(2, agent._create_router.call_count) self.assertEqual(1, ri.process.call_count) self.assertIn(ri.router_id, agent.router_info) def test_initialize_fail_retrigger_initialize(self): self._retrigger_initialize() def test_initialize_and_delete_fail_retrigger_initialize(self): self._retrigger_initialize(delete_fail=True) def test_process_router_floatingip_status_update_if_processed(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(num_internal_ports=1) fip1 = {'id': _uuid(), 'floating_ip_address': '8.8.8.8', 'fixed_ip_address': '7.7.7.7', 'status': 'ACTIVE', 'port_id': router[lib_constants.INTERFACE_KEY][0]['id']} fip2 = copy.copy(fip1) fip2.update({'id': _uuid(), 'status': 'DOWN', }) router[lib_constants.FLOATINGIP_KEY] = [fip1, fip2] ri = legacy_router.LegacyRouter(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() ri.iptables_manager.ipv4['nat'] = mock.MagicMock() with mock.patch.object( agent.plugin_rpc, 'update_floatingip_statuses' ) as mock_update_fip_status,\ mock.patch.object( ri, 'get_centralized_fip_cidr_set') as cent_cidrs,\ mock.patch.object(ri, 'get_router_cidrs') as mock_get_cidrs: mock_get_cidrs.return_value = set() cent_cidrs.return_value = set() ri.process() # make sure both was sent since not existed in existing cidrs mock_update_fip_status.assert_called_once_with( mock.ANY, ri.router_id, {fip1['id']: 'ACTIVE', fip2['id']: 'ACTIVE'}) def test_process_router_floatingip_disabled(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) with mock.patch.object( agent.plugin_rpc, 'update_floatingip_statuses') as mock_update_fip_status: fip_id = _uuid() router = l3_test_common.prepare_router_data(num_internal_ports=1) router[lib_constants.FLOATINGIP_KEY] = [ {'id': fip_id, 'floating_ip_address': '8.8.8.8', 'fixed_ip_address': '7.7.7.7', 'status': 'DOWN', 'port_id': router[lib_constants.INTERFACE_KEY][0]['id']}] ri = legacy_router.LegacyRouter(agent, router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() ri.process() # Assess the call for putting the floating IP up was performed mock_update_fip_status.assert_called_once_with( mock.ANY, ri.router_id, {fip_id: lib_constants.FLOATINGIP_STATUS_ACTIVE}) mock_update_fip_status.reset_mock() # Process the router again, this time without floating IPs router[lib_constants.FLOATINGIP_KEY] = [] ri.router = router ri.process() # Assess the call for putting the floating IP up was performed mock_update_fip_status.assert_called_once_with( mock.ANY, ri.router_id, {fip_id: lib_constants.FLOATINGIP_STATUS_DOWN}) def test_process_router_floatingip_exception(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) with mock.patch.object( agent.plugin_rpc, 'update_floatingip_statuses') as mock_update_fip_status: fip_id = _uuid() router = l3_test_common.prepare_router_data(num_internal_ports=1) router[lib_constants.FLOATINGIP_KEY] = [ {'id': fip_id, 'floating_ip_address': '8.8.8.8', 'fixed_ip_address': '7.7.7.7', 'port_id': router[lib_constants.INTERFACE_KEY][0]['id']}] ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.process_floating_ip_addresses = mock.Mock( side_effect=RuntimeError) ri.external_gateway_added = mock.Mock() ri.process() # Assess the call for putting the floating IP into Error # was performed mock_update_fip_status.assert_called_once_with( mock.ANY, ri.router_id, {fip_id: lib_constants.FLOATINGIP_STATUS_ERROR}) def test_process_external_iptables_exception(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) with mock.patch.object( agent.plugin_rpc, 'update_floatingip_statuses') as mock_update_fip_status: fip_id = _uuid() router = l3_test_common.prepare_router_data(num_internal_ports=1) router[lib_constants.FLOATINGIP_KEY] = [ {'id': fip_id, 'floating_ip_address': '8.8.8.8', 'fixed_ip_address': '7.7.7.7', 'port_id': router[lib_constants.INTERFACE_KEY][0]['id']}] ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) ri.iptables_manager._apply = mock.Mock(side_effect=Exception) ri.process_external() # Assess the call for putting the floating IP into Error # was performed mock_update_fip_status.assert_called_once_with( mock.ANY, ri.router_id, {fip_id: lib_constants.FLOATINGIP_STATUS_ERROR}) self.assertEqual(1, ri.iptables_manager._apply.call_count) def test_handle_router_snat_rules_distributed_without_snat_manager(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, 'foo_router_id', {}) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ri.iptables_manager = mock.MagicMock() ri._is_this_snat_host = mock.Mock(return_value=True) ri.get_ex_gw_port = mock.Mock(return_value=None) ri._handle_router_snat_rules(None, mock.ANY) self.assertIsNone(ri.snat_iptables_manager) self.assertFalse(ri.iptables_manager.called) def test_handle_router_snat_rules_add_back_jump(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(agent, _uuid(), {}, **self.ri_kwargs) ri.iptables_manager = mock.MagicMock() port = {'fixed_ips': [{'ip_address': '192.168.1.4'}]} ri._handle_router_snat_rules(port, "iface") nat = ri.iptables_manager.ipv4['nat'] nat.empty_chain.assert_any_call('snat') nat.add_rule.assert_any_call('snat', '-j $float-snat') for call in nat.mock_calls: name, args, kwargs = call if name == 'add_rule': self.assertEqual(('snat', '-j $float-snat'), args) self.assertEqual({}, kwargs) break def test_handle_router_snat_rules_add_rules(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(agent, _uuid(), {}, **self.ri_kwargs) ex_gw_port = {'fixed_ips': [{'ip_address': '192.168.1.4'}]} ri.router = {'distributed': False} ri._handle_router_snat_rules(ex_gw_port, "iface") nat_rules = list(map(str, ri.iptables_manager.ipv4['nat'].rules)) wrap_name = ri.iptables_manager.wrap_name jump_float_rule = "-A %s-snat -j %s-float-snat" % (wrap_name, wrap_name) snat_rule1 = ("-A %s-snat -o iface -j SNAT --to-source %s " "--random-fully") % ( wrap_name, ex_gw_port['fixed_ips'][0]['ip_address']) snat_rule2 = ("-A %s-snat -m mark ! --mark 0x2/%s " "-m conntrack --ctstate DNAT " "-j SNAT --to-source %s --random-fully") % ( wrap_name, n_const.ROUTER_MARK_MASK, ex_gw_port['fixed_ips'][0]['ip_address']) self.assertIn(jump_float_rule, nat_rules) self.assertIn(snat_rule1, nat_rules) self.assertIn(snat_rule2, nat_rules) self.assertThat(nat_rules.index(jump_float_rule), matchers.LessThan(nat_rules.index(snat_rule1))) mangle_rules = list(map(str, ri.iptables_manager.ipv4['mangle'].rules)) mangle_rule = ("-A %s-mark -i iface " "-j MARK --set-xmark 0x2/%s" % (wrap_name, n_const.ROUTER_MARK_MASK)) self.assertIn(mangle_rule, mangle_rules) def test_process_router_delete_stale_internal_devices(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) stale_devlist = [l3_test_common.FakeDev('qr-a1b2c3d4-e5'), l3_test_common.FakeDev('qr-b2c3d4e5-f6')] stale_devnames = [dev.name for dev in stale_devlist] get_devices_return = [] get_devices_return.extend(stale_devlist) self.mock_ip.get_devices.return_value = get_devices_return router = l3_test_common.prepare_router_data(enable_snat=True, num_internal_ports=1) ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) internal_ports = ri.router.get(lib_constants.INTERFACE_KEY, []) self.assertEqual(1, len(internal_ports)) internal_port = internal_ports[0] with mock.patch.object(ri, 'internal_network_removed' ) as internal_network_removed,\ mock.patch.object(ri, 'internal_network_added' ) as internal_network_added,\ mock.patch.object(ri, 'external_gateway_removed' ) as external_gateway_removed,\ mock.patch.object(ri, 'external_gateway_added' ) as external_gateway_added: ri.process() self.assertEqual(1, external_gateway_added.call_count) self.assertFalse(external_gateway_removed.called) self.assertFalse(internal_network_removed.called) internal_network_added.assert_called_once_with(internal_port) self.assertEqual(len(stale_devnames), self.mock_driver.unplug.call_count) calls = [mock.call(stale_devname, namespace=ri.ns_name, prefix=namespaces.INTERNAL_DEV_PREFIX) for stale_devname in stale_devnames] self.mock_driver.unplug.assert_has_calls(calls, any_order=True) def test_process_router_delete_stale_external_devices(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) stale_devlist = [l3_test_common.FakeDev('qg-a1b2c3d4-e5')] stale_devnames = [dev.name for dev in stale_devlist] router = l3_test_common.prepare_router_data(enable_snat=True, num_internal_ports=1) del router['gw_port'] ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) self.mock_ip.get_devices.return_value = stale_devlist ri.process() self.mock_driver.unplug.assert_called_with( stale_devnames[0], bridge="", namespace=ri.ns_name, prefix=namespaces.EXTERNAL_DEV_PREFIX) def test_process_dvr_router_delete_stale_external_devices(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) stale_devlist = [l3_test_common.FakeDev('qg-a1b2c3d4-e5')] stale_devnames = [dev.name for dev in stale_devlist] router = l3_test_common.prepare_router_data(enable_snat=True, num_internal_ports=1) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ri.snat_iptables_manager = iptables_manager.IptablesManager( namespace=ri.snat_namespace.name, use_ipv6=ri.use_ipv6) self.mock_ip.get_devices.return_value = stale_devlist ri.process() self.mock_driver.unplug.assert_called_with( stale_devnames[0], bridge=agent.conf.external_network_bridge, namespace=ri.snat_namespace.name, prefix=namespaces.EXTERNAL_DEV_PREFIX) def test_process_dvr_router_delete_stale_external_devices_no_snat_ns(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(enable_gw=False, num_internal_ports=1) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) self.mock_ip.netns.exists.return_value = False ri._delete_stale_external_devices('qg-a1b2c3d4-e5') self.assertFalse(self.mock_ip.get_devices.called) def test_router_deleted(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent._queue = mock.Mock() agent.router_deleted(None, FAKE_ID) self.assertEqual(1, agent._queue.add.call_count) def test_routers_updated(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent._queue = mock.Mock() agent.routers_updated(None, [FAKE_ID]) self.assertEqual(1, agent._queue.add.call_count) def test_removed_from_agent(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent._queue = mock.Mock() agent.router_removed_from_agent(None, {'router_id': FAKE_ID}) self.assertEqual(1, agent._queue.add.call_count) def test_added_to_agent(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent._queue = mock.Mock() agent.router_added_to_agent(None, [FAKE_ID]) self.assertEqual(1, agent._queue.add.call_count) def test_network_update_not_called(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent._queue = mock.Mock() network = {'id': _uuid()} agent.network_update(None, network=network) self.assertFalse(agent._queue.add.called) def test_network_update(self): router = l3_test_common.prepare_router_data(num_internal_ports=2) agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent._process_added_router(router) ri = l3router.RouterInfo(agent, router['id'], router, **self.ri_kwargs) internal_ports = ri.router.get(lib_constants.INTERFACE_KEY, []) network_id = internal_ports[0]['network_id'] agent._queue = mock.Mock() network = {'id': network_id} agent.network_update(None, network=network) self.assertEqual(1, agent._queue.add.call_count) def test_create_router_namespace(self): self.mock_ip.ensure_namespace.return_value = self.mock_ip agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ns = namespaces.Namespace( 'qrouter-bar', self.conf, agent.driver, agent.use_ipv6) ns.create() calls = [mock.call(['sysctl', '-w', 'net.ipv4.ip_forward=1']), mock.call(['sysctl', '-w', 'net.ipv4.conf.all.arp_ignore=1']), mock.call( ['sysctl', '-w', 'net.ipv4.conf.all.arp_announce=2'])] if agent.use_ipv6: calls.append(mock.call( ['sysctl', '-w', 'net.ipv6.conf.all.forwarding=1'])) self.mock_ip.netns.execute.assert_has_calls(calls) def test_destroy_namespace(self): namespace = 'qrouter-bar' self.list_network_namespaces.return_value = [namespace] self.mock_ip.get_devices.return_value = [ l3_test_common.FakeDev('qr-aaaa'), l3_test_common.FakeDev('rfp-aaaa')] agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ns = namespaces.RouterNamespace( 'bar', self.conf, agent.driver, agent.use_ipv6) ns.create() ns.delete() self.mock_driver.unplug.assert_called_once_with('qr-aaaa', prefix='qr-', namespace='qrouter' '-bar') self.mock_ip.del_veth.assert_called_once_with('rfp-aaaa') def test_destroy_router_namespace(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ns = namespaces.Namespace( 'qrouter-bar', self.conf, agent.driver, agent.use_ipv6) ns.create() ns.delete() self.mock_ip.netns.delete.assert_called_once_with("qrouter-bar") def test_destroy_snat_namespace(self): namespace = 'snat-bar' self.list_network_namespaces.return_value = [namespace] self.mock_ip.get_devices.return_value = [ l3_test_common.FakeDev('qg-aaaa'), l3_test_common.FakeDev('sg-aaaa')] agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ns = dvr_snat_ns.SnatNamespace( 'bar', self.conf, agent.driver, agent.use_ipv6) ns.create() ns.delete() calls = [mock.call('qg-aaaa', bridge=agent.conf.external_network_bridge, namespace=namespace, prefix=namespaces.EXTERNAL_DEV_PREFIX), mock.call('sg-aaaa', namespace=namespace, prefix=lib_constants.SNAT_INT_DEV_PREFIX)] self.mock_driver.unplug.assert_has_calls(calls, any_order=True) def _configure_metadata_proxy(self, enableflag=True): if not enableflag: self.conf.set_override('enable_metadata_proxy', False) agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router_id = _uuid() router = {'id': router_id, 'external_gateway_info': {}, 'routes': [], 'distributed': False} driver = metadata_driver.MetadataDriver with mock.patch.object( driver, 'destroy_monitored_metadata_proxy') as destroy_proxy: with mock.patch.object( driver, 'spawn_monitored_metadata_proxy') as spawn_proxy: agent._process_added_router(router) if enableflag: spawn_proxy.assert_called_with( mock.ANY, mock.ANY, self.conf.metadata_port, mock.ANY, router_id=router_id ) else: self.assertFalse(spawn_proxy.call_count) agent._router_removed(router_id) if enableflag: destroy_proxy.assert_called_with(mock.ANY, router_id, mock.ANY, 'qrouter-' + router_id) else: self.assertFalse(destroy_proxy.call_count) def test_enable_metadata_proxy(self): self._configure_metadata_proxy() def test_disable_metadata_proxy_spawn(self): self._configure_metadata_proxy(enableflag=False) def _test_process_routers_update_rpc_timeout(self, ext_net_call=False, ext_net_call_failed=False): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.fullsync = False agent._process_router_if_compatible = mock.Mock() router_id = _uuid() router = {'id': router_id, 'external_gateway_info': {'network_id': 'aaa'}} self.plugin_api.get_routers.return_value = [router] if ext_net_call_failed: agent._process_router_if_compatible.side_effect = ( oslo_messaging.MessagingTimeout) agent._queue = mock.Mock() agent._resync_router = mock.Mock() update = mock.Mock() update.id = router_id update.resource = None agent._queue.each_update_to_next_resource.side_effect = [ [(None, update)]] agent._process_router_update() self.assertFalse(agent.fullsync) self.assertEqual(ext_net_call, agent._process_router_if_compatible.called) agent._resync_router.assert_called_with(update) def test_process_routers_update_rpc_timeout_on_get_routers(self): self.plugin_api.get_routers.side_effect = ( oslo_messaging.MessagingTimeout) self._test_process_routers_update_rpc_timeout() def test_process_routers_update_resyncs_failed_router(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router_id = _uuid() router = {'id': router_id} # Attempting to configure the router will fail agent._process_router_if_compatible = mock.MagicMock() agent._process_router_if_compatible.side_effect = RuntimeError() # Queue an update from a full sync update = resource_processing_queue.ResourceUpdate( router_id, resource_processing_queue.PRIORITY_SYNC_ROUTERS_TASK, resource=router, timestamp=timeutils.utcnow()) agent._queue.add(update) agent._process_router_update() # The update contained the router object, get_routers won't be called self.assertFalse(agent.plugin_rpc.get_routers.called) # The update failed, assert that get_routers was called agent._process_router_update() self.assertTrue(agent.plugin_rpc.get_routers.called) def test_process_routers_update_rpc_timeout_on_get_ext_net(self): self._test_process_routers_update_rpc_timeout(ext_net_call=True, ext_net_call_failed=True) @mock.patch.object(pd, 'remove_router') def _test_process_routers_update_router_deleted(self, remove_router, error=False): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent._queue = mock.Mock() update = mock.Mock() update.resource = None update.action = 1 # ROUTER_DELETED router_info = mock.MagicMock() agent.router_info[update.id] = router_info router_processor = mock.Mock() agent._queue.each_update_to_next_resource.side_effect = [ [(router_processor, update)]] agent._resync_router = mock.Mock() if error: agent._safe_router_removed = mock.Mock() agent._safe_router_removed.return_value = False agent._process_router_update() if error: self.assertFalse(router_processor.fetched_and_processed.called) agent._resync_router.assert_called_with(update) self.assertFalse(remove_router.called) else: router_info.delete.assert_called_once_with() self.assertFalse(agent.router_info) self.assertFalse(agent._resync_router.called) router_processor.fetched_and_processed.assert_called_once_with( update.timestamp) self.assertTrue(remove_router.called) def test_process_routers_update_router_deleted_success(self): self._test_process_routers_update_router_deleted() def test_process_routers_update_router_deleted_error(self): self._test_process_routers_update_router_deleted(error=True) def test_process_routers_if_compatible(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = {'id': _uuid()} related_router = {'id': _uuid()} routers = [router, related_router] self.plugin_api.get_routers.return_value = routers update = resource_processing_queue.ResourceUpdate( router['id'], resource_processing_queue.PRIORITY_RPC, resource=router) events_queue = [] def add_mock(update): events_queue.append(update) agent._queue = mock.Mock() agent._queue.add.side_effect = add_mock with mock.patch.object( agent, "_process_router_if_compatible" ) as process_router_if_compatible, mock.patch.object( agent, "_safe_router_removed" ) as safe_router_removed: self.assertTrue( agent._process_routers_if_compatible(routers, update)) process_router_if_compatible.assert_called_once_with( router) safe_router_removed.assert_not_called() self.assertEqual(1, len(events_queue)) self.assertEqual(related_router['id'], events_queue[0].id) self.assertEqual(resource_processing_queue.PRIORITY_RELATED_ROUTER, events_queue[0].priority) def test_process_routers_if_compatible_router_not_compatible(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = {'id': _uuid()} agent.router_info = [router['id']] self.plugin_api.get_routers.return_value = [router] update = resource_processing_queue.ResourceUpdate( router['id'], resource_processing_queue.PRIORITY_RPC, resource=router) with mock.patch.object( agent, "_process_router_if_compatible", side_effect=n_exc.RouterNotCompatibleWithAgent( router_id=router['id']) ) as process_router_if_compatible, mock.patch.object( agent, "_safe_router_removed" ) as safe_router_removed: self.assertTrue( agent._process_routers_if_compatible([router], update)) process_router_if_compatible.assert_called_once_with( router) safe_router_removed.assert_called_once_with(router['id']) def test_process_dvr_routers_ha_on_update_when_router_unbound(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.conf.agent_mode = 'dvr_snat' router = mock.Mock() router.id = '1234' router.distributed = True router.ha = True router_info = mock.MagicMock() agent.router_info[router.id] = router_info updated_router = {'id': '1234', 'distributed': True, 'ha': True, 'external_gateway_info': {}, 'routes': [], 'admin_state_up': True} self.plugin_api.get_routers.return_value = [updated_router] update = resource_processing_queue.ResourceUpdate( updated_router['id'], resource_processing_queue.PRIORITY_RPC, resource=updated_router) with mock.patch.object(agent, "_safe_router_removed" ) as router_remove,\ mock.patch.object(agent, "_process_added_router" ) as add_router: agent._process_routers_if_compatible([updated_router], update) router_remove.assert_called_once_with(updated_router['id']) add_router.assert_called_once_with(updated_router) def test_process_dvr_routers_ha_on_update_without_ha_interface(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.conf.agent_mode = 'dvr_snat' router = mock.Mock() router.id = '1234' router.distributed = True router._ha_interface = True router.ha = True router_info = mock.MagicMock() agent.router_info[router.id] = router_info updated_router = {'id': '1234', 'distributed': True, 'ha': True, 'external_gateway_info': {}, 'routes': [], 'admin_state_up': True} self.plugin_api.get_routers.return_value = [updated_router] update = resource_processing_queue.ResourceUpdate( updated_router['id'], resource_processing_queue.PRIORITY_RPC, resource=updated_router) with mock.patch.object(agent, "_safe_router_removed" ) as router_remove,\ mock.patch.object(agent, "_process_added_router" ) as add_router: agent._process_routers_if_compatible([updated_router], update) router_remove.assert_called_once_with(updated_router['id']) add_router.assert_called_once_with(updated_router) def test_process_routers_if_compatible_error(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = {'id': _uuid()} self.plugin_api.get_routers.return_value = [router] update = resource_processing_queue.ResourceUpdate( router['id'], resource_processing_queue.PRIORITY_RPC, resource=router) with mock.patch.object( agent, "_process_router_if_compatible", side_effect=Exception( "Test failure during _process_routers_if_compatible") ) as process_router_if_compatible, mock.patch.object( agent, "_safe_router_removed" ) as safe_router_removed: self.assertFalse( agent._process_routers_if_compatible([router], update)) process_router_if_compatible.assert_called_once_with( router) safe_router_removed.assert_not_called() def test_process_ha_dvr_router_if_compatible_no_ha_interface(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.conf.agent_mode = 'dvr_snat' router = {'id': _uuid(), 'distributed': True, 'ha': True, 'external_gateway_info': {}, 'routes': [], 'admin_state_up': True} with mock.patch.object(agent, 'check_ha_state_for_router') as chsfr: agent._process_router_if_compatible(router) self.assertIn(router['id'], agent.router_info) self.assertFalse(chsfr.called) def test_process_router_if_compatible_with_no_ext_net_in_conf(self): self.conf.set_override('external_network_bridge', 'br-ex') agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.plugin_api.get_external_network_id.return_value = 'aaa' router = {'id': _uuid(), 'routes': [], 'admin_state_up': True, 'external_gateway_info': {'network_id': 'aaa'}} agent._process_router_if_compatible(router) self.assertIn(router['id'], agent.router_info) self.plugin_api.get_external_network_id.assert_called_with( agent.context) def test_process_router_if_compatible_with_cached_ext_net(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.plugin_api.get_external_network_id.return_value = 'aaa' agent.target_ex_net_id = 'aaa' router = {'id': _uuid(), 'routes': [], 'admin_state_up': True, 'external_gateway_info': {'network_id': 'aaa'}} agent._process_router_if_compatible(router) self.assertIn(router['id'], agent.router_info) self.assertFalse(self.plugin_api.get_external_network_id.called) def test_process_router_if_compatible_with_stale_cached_ext_net(self): self.conf.set_override('external_network_bridge', 'br-ex') agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.plugin_api.get_external_network_id.return_value = 'aaa' agent.target_ex_net_id = 'bbb' router = {'id': _uuid(), 'routes': [], 'admin_state_up': True, 'external_gateway_info': {'network_id': 'aaa'}} agent._process_router_if_compatible(router) self.assertIn(router['id'], agent.router_info) self.plugin_api.get_external_network_id.assert_called_with( agent.context) def test_process_router_if_compatible_w_no_ext_net_and_2_net_plugin(self): self.conf.set_override('external_network_bridge', 'br-ex') agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = {'id': _uuid(), 'routes': [], 'admin_state_up': True, 'external_gateway_info': {'network_id': 'aaa'}} agent.router_info = {} self.plugin_api.get_external_network_id.side_effect = ( exc.TooManyExternalNetworks()) self.assertRaises(exc.TooManyExternalNetworks, agent._process_router_if_compatible, router) self.assertNotIn(router['id'], agent.router_info) def test_process_router_if_compatible_with_ext_net_in_conf(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.plugin_api.get_external_network_id.return_value = 'aaa' router = {'id': _uuid(), 'routes': [], 'admin_state_up': True, 'external_gateway_info': {'network_id': 'bbb'}} agent.router_info = {} self.conf.set_override('gateway_external_network_id', 'aaa') self.assertRaises(n_exc.RouterNotCompatibleWithAgent, agent._process_router_if_compatible, router) self.assertNotIn(router['id'], agent.router_info) def test_process_router_if_compatible_with_no_bridge_no_ext_net(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.plugin_api.get_external_network_id.return_value = 'aaa' router = {'id': _uuid(), 'routes': [], 'admin_state_up': True, 'external_gateway_info': {'network_id': 'aaa'}} agent.router_info = {} agent._process_router_if_compatible(router) self.assertIn(router['id'], agent.router_info) def test_nonexistent_interface_driver(self): self.conf.set_override('interface_driver', None) self.assertRaises(SystemExit, l3_agent.L3NATAgent, HOSTNAME, self.conf) self.conf.set_override('interface_driver', 'wrong.driver') self.assertRaises(SystemExit, l3_agent.L3NATAgent, HOSTNAME, self.conf) @mock.patch.object(namespaces.RouterNamespace, 'delete') @mock.patch.object(dvr_snat_ns.SnatNamespace, 'delete') def _cleanup_namespace_test(self, stale_namespace_list, router_list, other_namespaces, mock_snat_ns, mock_router_ns): good_namespace_list = [namespaces.NS_PREFIX + r['id'] for r in router_list] good_namespace_list += [dvr_snat_ns.SNAT_NS_PREFIX + r['id'] for r in router_list] self.list_network_namespaces.return_value = (stale_namespace_list + good_namespace_list + other_namespaces) agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.assertTrue(agent.namespaces_manager._clean_stale) pm = self.external_process.return_value pm.reset_mock() with agent.namespaces_manager as ns_manager: for r in router_list: ns_manager.keep_router(r['id']) qrouters = [n for n in stale_namespace_list if n.startswith(namespaces.NS_PREFIX)] self.assertEqual(len(qrouters), mock_router_ns.call_count) self.assertEqual( len(stale_namespace_list) - len(qrouters), mock_snat_ns.call_count) self.assertFalse(agent.namespaces_manager._clean_stale) def test_cleanup_namespace(self): stale_namespaces = [namespaces.NS_PREFIX + 'foo', namespaces.NS_PREFIX + 'bar', dvr_snat_ns.SNAT_NS_PREFIX + 'foo'] other_namespaces = ['unknown'] self._cleanup_namespace_test(stale_namespaces, [], other_namespaces) def test_cleanup_namespace_with_registered_router_ids(self): stale_namespaces = [namespaces.NS_PREFIX + 'cccc', namespaces.NS_PREFIX + 'eeeee', dvr_snat_ns.SNAT_NS_PREFIX + 'fffff'] router_list = [{'id': 'foo', 'distributed': False}, {'id': 'aaaa', 'distributed': False}] other_namespaces = ['qdhcp-aabbcc', 'unknown'] self._cleanup_namespace_test(stale_namespaces, router_list, other_namespaces) def test_create_dvr_gateway(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data() self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) port_id = _uuid() subnet_id = _uuid() dvr_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', 'prefixlen': 24, 'subnet_id': subnet_id}], 'subnets': [{'id': subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}], 'id': port_id, 'network_id': _uuid(), 'mtu': 1500, 'mac_address': 'ca:fe:de:ad:be:ef'} interface_name = ri._get_snat_int_device_name(port_id) self.device_exists.return_value = False with mock.patch.object(ri, 'get_snat_interfaces') as get_interfaces: get_interfaces.return_value = self.snat_ports ri._create_dvr_gateway(dvr_gw_port, interface_name) # check 2 internal ports are plugged # check 1 ext-gw-port is plugged self.assertEqual(3, self.mock_driver.plug.call_count) self.assertEqual(3, self.mock_driver.init_router_port.call_count) def test_process_address_scope(self): router = l3_test_common.prepare_router_data() router['distributed'] = True router['gw_port_host'] = HOSTNAME agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ri.get_ex_gw_port = mock.Mock(return_value=None) # Make sure the code doesn't crash if ri.snat_iptables_manager is None. ri.process_address_scope() with mock.patch.object(ri, '_add_address_scope_mark') as mocked_func: ri.snat_iptables_manager = iptables_manager.IptablesManager( namespace=mock.ANY, use_ipv6=False) ri.snat_iptables_manager.defer_apply_off = mock.Mock() ri.process_address_scope() self.assertEqual(2, mocked_func.call_count) def test_get_service_plugin_list(self): service_plugins = [plugin_constants.L3] self.plugin_api.get_service_plugin_list.return_value = service_plugins agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.assertEqual(service_plugins, agent.neutron_service_plugins) self.assertTrue(self.plugin_api.get_service_plugin_list.called) def test_get_service_plugin_list_retried(self): raise_timeout = oslo_messaging.MessagingTimeout() # Raise a timeout the first 2 times it calls # get_service_plugin_list then return a empty tuple self.plugin_api.get_service_plugin_list.side_effect = ( raise_timeout, tuple() ) agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.assertEqual(tuple(), agent.neutron_service_plugins) def test_external_gateway_removed_ext_gw_port_no_fip_ns(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.conf.agent_mode = 'dvr_snat' router = l3_test_common.prepare_router_data(num_internal_ports=2) router['gw_port_host'] = HOSTNAME self.mock_driver.unplug.reset_mock() external_net_id = router['gw_port']['network_id'] self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ri.remove_floating_ip = mock.Mock() agent._fetch_external_net_id = mock.Mock(return_value=external_net_id) ri.ex_gw_port = ri.router['gw_port'] del ri.router['gw_port'] ri.external_gateway_added( ri.ex_gw_port, ri.get_external_device_name(ri.ex_gw_port['id'])) ri.fip_ns = None nat = ri.iptables_manager.ipv4['nat'] nat.clear_rules_by_tag = mock.Mock() nat.add_rule = mock.Mock() ri.snat_namespace = mock.Mock() ri.external_gateway_removed( ri.ex_gw_port, ri.get_external_device_name(ri.ex_gw_port['id'])) self.assertFalse(ri.remove_floating_ip.called) @mock.patch('os.geteuid', return_value='490') @mock.patch('pwd.getpwuid', return_value=FakeUser('neutron')) def test_spawn_radvd(self, geteuid, getpwuid): router = l3_test_common.prepare_router_data(ip_version=6) conffile = '/fake/radvd.conf' pidfile = '/fake/radvd.pid' agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) # we don't want the whole process manager to be mocked to be # able to catch execute() calls self.external_process_p.stop() self.ip_cls_p.stop() get_conf_file_name = 'neutron.agent.linux.utils.get_conf_file_name' get_pid_file_name = ('neutron.agent.linux.external_process.' 'ProcessManager.get_pid_file_name') utils_execute = 'neutron.agent.common.utils.execute' mock.patch(get_conf_file_name).start().return_value = conffile mock.patch(get_pid_file_name).start().return_value = pidfile execute = mock.patch(utils_execute).start() radvd = ra.DaemonMonitor( router['id'], namespaces.RouterNamespace._get_ns_name(router['id']), agent.process_monitor, l3_test_common.FakeDev, self.conf) radvd.enable(router['_interfaces']) cmd = execute.call_args[0][0] self.assertIn('radvd', cmd) _join = lambda *args: ' '.join(args) cmd = _join(*cmd) self.assertIn(_join('-C', conffile), cmd) self.assertIn(_join('-p', pidfile), cmd) self.assertIn(_join('-u', 'neutron'), cmd) self.assertIn(_join('-m', 'syslog'), cmd) def test_generate_radvd_mtu_conf(self): router = l3_test_common.prepare_router_data() ipv6_subnet_modes = [{'ra_mode': lib_constants.IPV6_SLAAC, 'address_mode': lib_constants.IPV6_SLAAC}] network_mtu = '1446' ri = self._process_router_ipv6_subnet_added(router, ipv6_subnet_modes, None, network_mtu) # Verify that MTU is advertised expected = "AdvLinkMTU 1446" ri.radvd._generate_radvd_conf(router[lib_constants.INTERFACE_KEY]) self.assertIn(expected, self.utils_replace_file.call_args[0][1]) def test_generate_radvd_conf_other_and_managed_flag(self): # expected = {ra_mode: (AdvOtherConfigFlag, AdvManagedFlag), ...} expected = {lib_constants.IPV6_SLAAC: (False, False), lib_constants.DHCPV6_STATELESS: (True, False), lib_constants.DHCPV6_STATEFUL: (False, True)} modes = [lib_constants.IPV6_SLAAC, lib_constants.DHCPV6_STATELESS, lib_constants.DHCPV6_STATEFUL] mode_combos = list(iter_chain(*[[list(combo) for combo in iter_combinations(modes, i)] for i in range(1, len(modes) + 1)])) for mode_list in mode_combos: ipv6_subnet_modes = [{'ra_mode': mode, 'address_mode': mode} for mode in mode_list] router = l3_test_common.prepare_router_data() ri = self._process_router_ipv6_subnet_added(router, ipv6_subnet_modes) ri.radvd._generate_radvd_conf(router[lib_constants.INTERFACE_KEY]) def assertFlag(flag): return (self.assertIn if flag else self.assertNotIn) other_flag, managed_flag = ( any(expected[mode][0] for mode in mode_list), any(expected[mode][1] for mode in mode_list)) assertFlag(other_flag)('AdvOtherConfigFlag on;', self.utils_replace_file.call_args[0][1]) assertFlag(managed_flag)('AdvManagedFlag on;', self.utils_replace_file.call_args[0][1]) def test_generate_radvd_intervals(self): self.conf.set_override('min_rtr_adv_interval', 22) self.conf.set_override('max_rtr_adv_interval', 66) router = l3_test_common.prepare_router_data() ipv6_subnet_modes = [{'ra_mode': lib_constants.IPV6_SLAAC, 'address_mode': lib_constants.IPV6_SLAAC}] ri = self._process_router_ipv6_subnet_added(router, ipv6_subnet_modes) ri.radvd._generate_radvd_conf(router[lib_constants.INTERFACE_KEY]) self.assertIn("MinRtrAdvInterval 22", self.utils_replace_file.call_args[0][1]) self.assertIn("MaxRtrAdvInterval 66", self.utils_replace_file.call_args[0][1]) def test_generate_radvd_rdnss_conf(self): router = l3_test_common.prepare_router_data() ipv6_subnet_modes = [{'ra_mode': lib_constants.IPV6_SLAAC, 'address_mode': lib_constants.IPV6_SLAAC}] dns_list = ['fd01:1::100', 'fd01:1::200', 'fd01::300', 'fd01::400'] ri = self._process_router_ipv6_subnet_added(router, ipv6_subnet_modes, dns_nameservers=dns_list) ri.radvd._generate_radvd_conf(router[lib_constants.INTERFACE_KEY]) # Verify that radvd configuration file includes RDNSS entries expected = "RDNSS " for dns in dns_list[0:ra.MAX_RDNSS_ENTRIES]: expected += "%s " % dns self.assertIn(expected, self.utils_replace_file.call_args[0][1]) def _pd_expected_call_external_process(self, requestor, ri, enable=True, ha=False): expected_calls = [] if enable: expected_calls.append(mock.call(uuid=requestor, service='dibbler', default_cmd_callback=mock.ANY, namespace=ri.ns_name, conf=mock.ANY, pid_file=mock.ANY)) expected_calls.append(mock.call().enable(reload_cfg=False)) else: expected_calls.append(mock.call(uuid=requestor, service='dibbler', namespace=ri.ns_name, conf=mock.ANY, pid_file=mock.ANY)) # in the HA switchover case, disable is called without arguments if ha: expected_calls.append(mock.call().disable()) else: expected_calls.append(mock.call().disable( get_stop_command=mock.ANY)) return expected_calls def _pd_setup_agent_router(self, enable_ha=False): router = l3_test_common.prepare_router_data() agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent._router_added(router['id'], router) # Make sure radvd monitor is created ri = agent.router_info[router['id']] ri.iptables_manager.ipv6['mangle'] = mock.MagicMock() ri._process_pd_iptables_rules = mock.MagicMock() if not ri.radvd: ri.radvd = ra.DaemonMonitor(router['id'], ri.ns_name, agent.process_monitor, ri.get_internal_device_name, self.conf) if enable_ha: agent.pd.routers[router['id']]['master'] = False return agent, router, ri def _pd_remove_gw_interface(self, intfs, agent, ri): expected_pd_update = {} expected_calls = [] for intf in intfs: requestor_id = self._pd_get_requestor_id(intf, ri) expected_calls += (self._pd_expected_call_external_process( requestor_id, ri, False)) for subnet in intf['subnets']: expected_pd_update[subnet['id']] = ( lib_constants.PROVISIONAL_IPV6_PD_PREFIX) # Implement the prefix update notifier # Keep track of the updated prefix self.pd_update = {} def pd_notifier(context, prefix_update): self.pd_update = prefix_update for subnet_id, prefix in prefix_update.items(): for intf in intfs: for subnet in intf['subnets']: if subnet['id'] == subnet_id: # Update the prefix subnet['cidr'] = prefix break # Remove the gateway interface agent.pd.notifier = pd_notifier agent.pd.remove_gw_interface(ri.router['id']) self._pd_assert_dibbler_calls(expected_calls, self.external_process.mock_calls[-len(expected_calls):]) self.assertEqual(expected_pd_update, self.pd_update) def _pd_remove_interfaces(self, intfs, agent, ri): expected_pd_update = [] expected_calls = [] for intf in intfs: # Remove the router interface ri.router[lib_constants.INTERFACE_KEY].remove(intf) requestor_id = self._pd_get_requestor_id(intf, ri) expected_calls += (self._pd_expected_call_external_process( requestor_id, ri, False)) for subnet in intf['subnets']: expected_pd_update += [{subnet['id']: lib_constants.PROVISIONAL_IPV6_PD_PREFIX}] # Implement the prefix update notifier # Keep track of the updated prefix self.pd_update = [] def pd_notifier(context, prefix_update): self.pd_update.append(prefix_update) for intf in intfs: for subnet in intf['subnets']: if subnet['id'] in prefix_update: # Update the prefix subnet['cidr'] = prefix_update[subnet['id']] # Process the router for removed interfaces agent.pd.notifier = pd_notifier ri.process() # The number of external process calls takes radvd into account. # This is because there is no ipv6 interface any more after removing # the interfaces, and radvd will be killed because of that self._pd_assert_dibbler_calls(expected_calls, self.external_process.mock_calls[-len(expected_calls) - 2:]) self._pd_assert_radvd_calls(ri, False) self.assertEqual(expected_pd_update, self.pd_update) def _pd_get_requestor_id(self, intf, ri): ifname = ri.get_internal_device_name(intf['id']) for subnet in intf['subnets']: return dibbler.PDDibbler(ri.router['id'], subnet['id'], ifname).requestor_id def _pd_assert_dibbler_calls(self, expected, actual): '''Check the external process calls for dibbler are expected in the case of multiple pd-enabled router ports, the exact sequence of these calls are not deterministic. It's known, though, that each external_process call is followed with either an enable() or disable() ''' num_ext_calls = len(expected) // 2 expected_ext_calls = [] actual_ext_calls = [] expected_action_calls = [] actual_action_calls = [] for c in range(num_ext_calls): expected_ext_calls.append(expected[c * 2]) actual_ext_calls.append(actual[c * 2]) expected_action_calls.append(expected[c * 2 + 1]) actual_action_calls.append(actual[c * 2 + 1]) self.assertEqual(expected_action_calls, actual_action_calls) for exp in expected_ext_calls: for act in actual_ext_calls: if exp == act: break else: msg = "Unexpected dibbler external process call." self.fail(msg) def _pd_assert_radvd_calls(self, ri, enable=True): exp_calls = self._radvd_expected_call_external_process(ri, enable) self.assertEqual(exp_calls, self.external_process.mock_calls[-len(exp_calls):]) def _pd_assert_update_subnet_calls(self, router_id, intfs, mock_pd_update_subnet): for intf in intfs: mock_pd_update_subnet.assert_any_call(router_id, intf['subnets'][0]['id'], intf['subnets'][0]['cidr']) def _pd_get_prefixes(self, agent, ri, existing_intfs, new_intfs, mock_get_prefix): # First generate the prefixes that will be used for each interface prefixes = {} expected_pd_update = {} expected_calls = [] last_prefix = '' for ifno, intf in enumerate(existing_intfs + new_intfs): requestor_id = self._pd_get_requestor_id(intf, ri) prefixes[requestor_id] = "2001:db8:%d::/64" % ifno last_prefix = prefixes[requestor_id] if intf in new_intfs: subnet_id = (intf['subnets'][0]['id'] if intf['subnets'] else None) expected_pd_update[subnet_id] = prefixes[requestor_id] expected_calls += ( self._pd_expected_call_external_process(requestor_id, ri)) # Implement the prefix update notifier # Keep track of the updated prefix self.pd_update = {} def pd_notifier(context, prefix_update): self.pd_update = prefix_update for subnet_id, prefix in prefix_update.items(): gateway_ip = '%s1' % netaddr.IPNetwork(prefix).network for intf in new_intfs: for fip in intf['fixed_ips']: if fip['subnet_id'] == subnet_id: fip['ip_address'] = gateway_ip for subnet in intf['subnets']: if subnet['id'] == subnet_id: # Update the prefix subnet['cidr'] = prefix subnet['gateway_ip'] = gateway_ip break # Start the dibbler client agent.pd.notifier = pd_notifier agent.pd.process_prefix_update() # Get the prefix and check that the neutron server is notified def get_prefix(pdo): key = '%s:%s:%s' % (pdo.router_id, pdo.subnet_id, pdo.ri_ifname) return prefixes[key] mock_get_prefix.side_effect = get_prefix agent.pd.process_prefix_update() # Make sure that the updated prefixes are expected self._pd_assert_dibbler_calls(expected_calls, self.external_process.mock_calls[-len(expected_calls):]) self.assertEqual(expected_pd_update, self.pd_update) return last_prefix def _pd_verify_update_results(self, ri, pd_intfs, mock_pd_update_subnet): # verify router port initialized for intf in pd_intfs: self.mock_driver.init_router_port.assert_any_call( ri.get_internal_device_name(intf['id']), ip_cidrs=l3router.common_utils.fixed_ip_cidrs( intf['fixed_ips']), namespace=ri.ns_name) # verify that subnet is updated in PD self._pd_assert_update_subnet_calls(ri.router['id'], pd_intfs, mock_pd_update_subnet) # Check that radvd is started self._pd_assert_radvd_calls(ri) def _pd_add_gw_interface(self, agent, ri): gw_ifname = ri.get_external_device_name(ri.router['gw_port']['id']) agent.pd.add_gw_interface(ri.router['id'], gw_ifname) @mock.patch.object(pd.PrefixDelegation, 'update_subnet') @mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True) @mock.patch.object(dibbler.os, 'getpid', return_value=1234) @mock.patch.object(pd.PrefixDelegation, '_is_lla_active', return_value=True) @mock.patch.object(dibbler.os, 'chmod') @mock.patch.object(dibbler.shutil, 'rmtree') @mock.patch.object(pd.PrefixDelegation, '_get_sync_data') def test_pd_have_subnet(self, mock1, mock2, mock3, mock4, mock_getpid, mock_get_prefix, mock_pd_update_subnet): '''Add one pd-enabled subnet that has already been assigned ''' prefix = '2001:db8:10::/64' # Initial setup agent, router, ri = self._pd_setup_agent_router() # Create one pd-enabled subnet and add router interface l3_test_common.router_append_pd_enabled_subnet(router, prefix=prefix) ri.process() pd_intfs = l3_test_common.get_assigned_pd_interfaces(router) subnet_id = pd_intfs[0]['subnets'][0]['id'] # Check that _process_pd_iptables_rules() is called correctly self.assertEqual({subnet_id: prefix}, ri.pd_subnets) ri._process_pd_iptables_rules.assert_called_once_with(prefix, subnet_id) @mock.patch.object(pd.PrefixDelegation, 'update_subnet') @mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True) @mock.patch.object(dibbler.os, 'getpid', return_value=1234) @mock.patch.object(pd.PrefixDelegation, '_is_lla_active', return_value=True) @mock.patch.object(dibbler.os, 'chmod') @mock.patch.object(dibbler.shutil, 'rmtree') @mock.patch.object(pd.PrefixDelegation, '_get_sync_data') def test_pd_add_remove_subnet(self, mock1, mock2, mock3, mock4, mock_getpid, mock_get_prefix, mock_pd_update_subnet): '''Add and remove one pd-enabled subnet Remove the interface by deleting it from the router ''' # Initial setup agent, router, ri = self._pd_setup_agent_router() # Create one pd-enabled subnet and add router interface l3_test_common.router_append_pd_enabled_subnet(router) ri.process() # Provisional PD prefix on startup, so nothing cached self.assertEqual({}, ri.pd_subnets) # No client should be started since there is no gateway port self.assertFalse(self.external_process.call_count) self.assertFalse(mock_get_prefix.call_count) # Add the gateway interface self._pd_add_gw_interface(agent, ri) update_router = copy.deepcopy(router) pd_intfs = l3_test_common.get_unassigned_pd_interfaces(update_router) subnet_id = pd_intfs[0]['subnets'][0]['id'] # Get one prefix prefix = self._pd_get_prefixes(agent, ri, [], pd_intfs, mock_get_prefix) # Update the router with the new prefix ri.router = update_router ri.process() self._pd_verify_update_results(ri, pd_intfs, mock_pd_update_subnet) # Check that _process_pd_iptables_rules() is called correctly self.assertEqual({subnet_id: prefix}, ri.pd_subnets) ri._process_pd_iptables_rules.assert_called_once_with(prefix, subnet_id) # Now remove the interface self._pd_remove_interfaces(pd_intfs, agent, ri) self.assertEqual({}, ri.pd_subnets) @mock.patch.object(pd.PrefixDelegation, 'update_subnet') @mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True) @mock.patch.object(dibbler.os, 'getpid', return_value=1234) @mock.patch.object(pd.PrefixDelegation, '_is_lla_active', return_value=True) @mock.patch.object(dibbler.os, 'chmod') @mock.patch.object(dibbler.shutil, 'rmtree') @mock.patch.object(pd.PrefixDelegation, '_get_sync_data') def test_pd_remove_gateway(self, mock1, mock2, mock3, mock4, mock_getpid, mock_get_prefix, mock_pd_update_subnet): '''Add one pd-enabled subnet and remove the gateway port Remove the gateway port and check the prefix is removed ''' # Initial setup agent, router, ri = self._pd_setup_agent_router() # Create one pd-enabled subnet and add router interface l3_test_common.router_append_pd_enabled_subnet(router) ri.process() # Add the gateway interface self._pd_add_gw_interface(agent, ri) update_router = copy.deepcopy(router) pd_intfs = l3_test_common.get_unassigned_pd_interfaces(update_router) # Get one prefix self._pd_get_prefixes(agent, ri, [], pd_intfs, mock_get_prefix) # Update the router with the new prefix ri.router = update_router ri.process() self._pd_verify_update_results(ri, pd_intfs, mock_pd_update_subnet) # Now remove the gw interface self._pd_remove_gw_interface(pd_intfs, agent, ri) @mock.patch.object(pd.PrefixDelegation, 'update_subnet') @mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True) @mock.patch.object(dibbler.os, 'getpid', return_value=1234) @mock.patch.object(pd.PrefixDelegation, '_is_lla_active', return_value=True) @mock.patch.object(dibbler.os, 'chmod') @mock.patch.object(dibbler.shutil, 'rmtree') @mock.patch.object(pd.PrefixDelegation, '_get_sync_data') def test_pd_add_remove_2_subnets(self, mock1, mock2, mock3, mock4, mock_getpid, mock_get_prefix, mock_pd_update_subnet): '''Add and remove two pd-enabled subnets Remove the interfaces by deleting them from the router ''' # Initial setup agent, router, ri = self._pd_setup_agent_router() # Create 2 pd-enabled subnets and add router interfaces l3_test_common.router_append_pd_enabled_subnet(router, count=2) ri.process() # No client should be started self.assertFalse(self.external_process.call_count) self.assertFalse(mock_get_prefix.call_count) # Add the gateway interface self._pd_add_gw_interface(agent, ri) update_router = copy.deepcopy(router) pd_intfs = l3_test_common.get_unassigned_pd_interfaces(update_router) # Get prefixes self._pd_get_prefixes(agent, ri, [], pd_intfs, mock_get_prefix) # Update the router with the new prefix ri.router = update_router ri.process() self._pd_verify_update_results(ri, pd_intfs, mock_pd_update_subnet) # Now remove the interface self._pd_remove_interfaces(pd_intfs, agent, ri) @mock.patch.object(pd.PrefixDelegation, 'update_subnet') @mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True) @mock.patch.object(dibbler.os, 'getpid', return_value=1234) @mock.patch.object(pd.PrefixDelegation, '_is_lla_active', return_value=True) @mock.patch.object(dibbler.os, 'chmod') @mock.patch.object(dibbler.shutil, 'rmtree') @mock.patch.object(pd.PrefixDelegation, '_get_sync_data') def test_pd_remove_gateway_2_subnets(self, mock1, mock2, mock3, mock4, mock_getpid, mock_get_prefix, mock_pd_update_subnet): '''Add one pd-enabled subnet, followed by adding another one Remove the gateway port and check the prefix is removed ''' # Initial setup agent, router, ri = self._pd_setup_agent_router() # Add the gateway interface self._pd_add_gw_interface(agent, ri) # Create 1 pd-enabled subnet and add router interface l3_test_common.router_append_pd_enabled_subnet(router, count=1) ri.process() update_router = copy.deepcopy(router) pd_intfs = l3_test_common.get_unassigned_pd_interfaces(update_router) # Get prefixes self._pd_get_prefixes(agent, ri, [], pd_intfs, mock_get_prefix) # Update the router with the new prefix ri.router = update_router ri.process() self._pd_verify_update_results(ri, pd_intfs, mock_pd_update_subnet) # Now add another interface # Create one pd-enabled subnet and add router interface l3_test_common.router_append_pd_enabled_subnet(update_router, count=1) ri.process() update_router_2 = copy.deepcopy(update_router) pd_intfs1 = l3_test_common.get_unassigned_pd_interfaces( update_router_2) # Get prefixes self._pd_get_prefixes(agent, ri, pd_intfs, pd_intfs1, mock_get_prefix) # Update the router with the new prefix ri.router = update_router_2 ri.process() self._pd_verify_update_results(ri, pd_intfs1, mock_pd_update_subnet) # Now remove the gw interface self._pd_remove_gw_interface(pd_intfs + pd_intfs1, agent, ri) @mock.patch.object(l3router.RouterInfo, 'enable_radvd') @mock.patch.object(pd.PrefixDelegation, '_add_lla') @mock.patch.object(pd.PrefixDelegation, 'update_subnet') @mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True) @mock.patch.object(dibbler.os, 'getpid', return_value=1234) @mock.patch.object(pd.PrefixDelegation, '_is_lla_active', return_value=True) @mock.patch.object(dibbler.os, 'chmod') @mock.patch.object(dibbler.shutil, 'rmtree') @mock.patch.object(pd.PrefixDelegation, '_get_sync_data') def test_pd_ha_standby(self, mock1, mock2, mock3, mock4, mock_getpid, mock_get_prefix, mock_pd_update_subnet, mock_add_lla, mock_enable_radvd): '''Test HA in the standby router The intent is to test the PD code with HA. To avoid unnecessary complexities, use the regular router. ''' # Initial setup agent, router, ri = self._pd_setup_agent_router(enable_ha=True) # Create one pd-enabled subnet and add router interface l3_test_common.router_append_pd_enabled_subnet(router) self._pd_add_gw_interface(agent, ri) ri.process() self.assertFalse(mock_add_lla.called) # No client should be started since it's standby router agent.pd.process_prefix_update() self.assertFalse(self.external_process.called) self.assertFalse(mock_get_prefix.called) update_router = copy.deepcopy(router) pd_intfs = l3_test_common.assign_prefix_for_pd_interfaces( update_router) # Update the router with the new prefix ri.router = update_router ri.process() self._pd_assert_update_subnet_calls(router['id'], pd_intfs, mock_pd_update_subnet) # No client should be started since it's standby router agent.pd.process_prefix_update() self.assertFalse(self.external_process.called) self.assertFalse(mock_get_prefix.called) @mock.patch.object(pd.PrefixDelegation, '_add_lla') @mock.patch.object(pd.PrefixDelegation, 'update_subnet') @mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True) @mock.patch.object(dibbler.os, 'getpid', return_value=1234) @mock.patch.object(pd.PrefixDelegation, '_is_lla_active', return_value=True) @mock.patch.object(dibbler.os, 'chmod') @mock.patch.object(dibbler.shutil, 'rmtree') @mock.patch.object(pd.PrefixDelegation, '_get_sync_data') def test_pd_ha_active(self, mock1, mock2, mock3, mock4, mock_getpid, mock_get_prefix, mock_pd_update_subnet, mock_add_lla): '''Test HA in the active router The intent is to test the PD code with HA. To avoid unnecessary complexities, use the regular router. ''' # Initial setup agent, router, ri = self._pd_setup_agent_router(enable_ha=True) # Create one pd-enabled subnet and add router interface l3_test_common.router_append_pd_enabled_subnet(router) self._pd_add_gw_interface(agent, ri) ri.process() self.assertFalse(mock_add_lla.called) # No client should be started since it's standby router agent.pd.process_prefix_update() self.assertFalse(self.external_process.called) self.assertFalse(mock_get_prefix.called) update_router = copy.deepcopy(router) pd_intfs = l3_test_common.get_unassigned_pd_interfaces(update_router) # Turn the router to be active agent.pd.process_ha_state(router['id'], True) # Get prefixes self._pd_get_prefixes(agent, ri, [], pd_intfs, mock_get_prefix) # Update the router with the new prefix ri.router = update_router ri.process() self._pd_verify_update_results(ri, pd_intfs, mock_pd_update_subnet) @mock.patch.object(pd.PrefixDelegation, 'update_subnet') @mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True) @mock.patch.object(dibbler.os, 'getpid', return_value=1234) @mock.patch.object(pd.PrefixDelegation, '_is_lla_active', return_value=True) @mock.patch.object(dibbler.os, 'chmod') @mock.patch.object(dibbler.shutil, 'rmtree') @mock.patch.object(pd.PrefixDelegation, '_get_sync_data') def test_pd_ha_switchover(self, mock1, mock2, mock3, mock4, mock_getpid, mock_get_prefix, mock_pd_update_subnet): '''Test HA in the active router The intent is to test the PD code with HA. To avoid unnecessary complexities, use the regular router. ''' # Initial setup agent, router, ri = self._pd_setup_agent_router(enable_ha=True) # Turn the router to be active agent.pd.process_ha_state(router['id'], True) # Create one pd-enabled subnet and add router interface l3_test_common.router_append_pd_enabled_subnet(router) self._pd_add_gw_interface(agent, ri) ri.process() update_router = copy.deepcopy(router) pd_intfs = l3_test_common.get_unassigned_pd_interfaces(update_router) # Get prefixes self._pd_get_prefixes(agent, ri, [], pd_intfs, mock_get_prefix) # Update the router with the new prefix ri.router = update_router ri.process() self._pd_verify_update_results(ri, pd_intfs, mock_pd_update_subnet) # Turn the router to be standby agent.pd.process_ha_state(router['id'], False) expected_calls = [] for intf in pd_intfs: requestor_id = self._pd_get_requestor_id(intf, ri) expected_calls += (self._pd_expected_call_external_process( requestor_id, ri, False, ha=True)) self._pd_assert_dibbler_calls(expected_calls, self.external_process.mock_calls[-len(expected_calls):]) @mock.patch.object(dibbler.os, 'chmod') def test_pd_generate_dibbler_conf(self, mock_chmod): pddib = dibbler.PDDibbler("router_id", "subnet-id", "ifname") pddib._generate_dibbler_conf("ex_gw_ifname", "fe80::f816:3eff:fef5:a04e", None) expected = 'bind-to-address fe80::f816:3eff:fef5:a04e\n'\ '# ask for address\n \n pd 1\n \n}' self.assertIn(expected, self.utils_replace_file.call_args[0][1]) pddib._generate_dibbler_conf("ex_gw_ifname", "fe80::f816:3eff:fef5:a04e", "2001:db8:2c50:2026::/64") expected = 'bind-to-address fe80::f816:3eff:fef5:a04e\n'\ '# ask for address\n \n pd 1 '\ '{\n prefix 2001:db8:2c50:2026::/64\n }\n \n}' self.assertIn(expected, self.utils_replace_file.call_args[0][1]) def _verify_address_scopes_iptables_rule(self, mock_iptables_manager): filter_calls = [mock.call.add_chain('scope'), mock.call.add_rule('FORWARD', '-j $scope')] v6_mangle_calls = [mock.call.add_chain('scope'), mock.call.add_rule('PREROUTING', '-j $scope'), mock.call.add_rule( 'PREROUTING', '-m connmark ! --mark 0x0/0xffff0000 ' '-j CONNMARK --restore-mark ' '--nfmask 0xffff0000 --ctmask 0xffff0000')] v4_mangle_calls = (v6_mangle_calls + [mock.call.add_chain('floatingip'), mock.call.add_chain('float-snat'), mock.call.add_rule('PREROUTING', '-j $floatingip'), mock.call.add_rule( 'PREROUTING', '-d 169.254.169.254/32 -i %(interface_name)s ' '-p tcp -m tcp --dport 80 ' '-j MARK --set-xmark %(value)s/%(mask)s' % {'interface_name': namespaces.INTERNAL_DEV_PREFIX + '+', 'value': self.conf.metadata_access_mark, 'mask': n_const.ROUTER_MARK_MASK}), mock.call.add_rule( 'float-snat', '-m connmark --mark 0x0/0xffff0000 ' '-j CONNMARK --save-mark ' '--nfmask 0xffff0000 --ctmask 0xffff0000')]) mock_iptables_manager.ipv4['filter'].assert_has_calls(filter_calls) mock_iptables_manager.ipv6['filter'].assert_has_calls(filter_calls) mock_iptables_manager.ipv4['mangle'].assert_has_calls(v4_mangle_calls, any_order=True) mock_iptables_manager.ipv6['mangle'].assert_has_calls(v6_mangle_calls, any_order=True) def test_initialize_address_scope_iptables_rules(self): id = _uuid() agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) with mock.patch('neutron.agent.linux.iptables_manager.' 'IptablesManager'): ri = l3router.RouterInfo(agent, id, {}, **self.ri_kwargs) self._verify_address_scopes_iptables_rule(ri.iptables_manager) def test_initialize_address_scope_iptables_rules_dvr(self): router = l3_test_common.prepare_router_data() with mock.patch('neutron.agent.linux.iptables_manager.' 'IptablesManager'): self._set_ri_kwargs(mock.Mock(), router['id'], router) ri = dvr_router.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) self._verify_address_scopes_iptables_rule(ri.iptables_manager) interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test( self, ri) router['gw_port_host'] = ri.host ri._external_gateway_added = mock.Mock() ri._create_dvr_gateway(ex_gw_port, interface_name) self._verify_address_scopes_iptables_rule( ri.snat_iptables_manager) def _verify_metadata_iptables_rule(self, mock_iptables_manager): v4_mangle_calls = ([mock.call.add_rule( 'PREROUTING', '-d 169.254.169.254/32 -i %(interface_name)s ' '-p tcp -m tcp --dport 80 ' '-j MARK --set-xmark %(value)s/%(mask)s' % {'interface_name': namespaces.INTERNAL_DEV_PREFIX + '+', 'value': self.conf.metadata_access_mark, 'mask': n_const.ROUTER_MARK_MASK})]) mock_iptables_manager.ipv4['mangle'].assert_has_calls(v4_mangle_calls, any_order=True) def test_initialize_metadata_iptables_rules(self): id = _uuid() agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) with mock.patch('neutron.agent.linux.iptables_manager.' 'IptablesManager'): ri = l3router.RouterInfo(agent, id, {}, **self.ri_kwargs) self._verify_metadata_iptables_rule(ri.iptables_manager) @mock.patch.object(l3router.RouterInfo, 'delete') @mock.patch.object(ha_router.HaRouter, 'destroy_state_change_monitor') def test_delete_ha_router_initialize_fails(self, mock_dscm, mock_delete): router = l3_test_common.prepare_router_data(enable_ha=True) router[lib_constants.HA_INTERFACE_KEY] = None agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) # an early failure of an HA router initiailization shouldn't try # and cleanup a state change monitor process that was never spawned. # Cannot use self.assertRaises(Exception, ...) as that causes an H202 # pep8 failure. try: agent._router_added(router['id'], router) raise Exception("agent._router_added() should have raised an " "exception") except Exception: pass self.assertTrue(mock_delete.called) self.assertFalse(mock_dscm.called) neutron-12.1.1/neutron/tests/unit/agent/l3/test_dvr_local_router.py0000664000175000017500000012020513553660047025526 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import portbindings from neutron_lib import constants as lib_constants from oslo_config import cfg from oslo_log import log from oslo_utils import uuidutils from neutron.agent.l3 import agent as l3_agent from neutron.agent.l3 import dvr_edge_ha_router as dvr_edge_ha_rtr from neutron.agent.l3 import dvr_edge_router as dvr_edge_rtr from neutron.agent.l3 import dvr_local_router as dvr_router from neutron.agent.l3 import link_local_allocator as lla from neutron.agent.l3 import router_info from neutron.agent.linux import interface from neutron.agent.linux import ip_lib from neutron.common import constants as n_const from neutron.common import utils as common_utils from neutron.conf.agent import common as agent_config from neutron.conf.agent.l3 import config as l3_config from neutron.conf.agent.l3 import ha as ha_conf from neutron.conf import common as base_config from neutron.tests import base from neutron.tests.common import l3_test_common _uuid = uuidutils.generate_uuid FIP_PRI = 32768 HOSTNAME = 'myhost' class TestDvrRouterOperations(base.BaseTestCase): def setUp(self): super(TestDvrRouterOperations, self).setUp() mock.patch('eventlet.spawn').start() self.conf = agent_config.setup_conf() self.conf.register_opts(base_config.core_opts) log.register_options(self.conf) self.conf.register_opts(agent_config.AGENT_STATE_OPTS, 'AGENT') l3_config.register_l3_agent_config_opts(l3_config.OPTS, self.conf) ha_conf.register_l3_agent_ha_opts(self.conf) agent_config.register_interface_driver_opts_helper(self.conf) agent_config.register_process_monitor_opts(self.conf) agent_config.register_interface_opts(self.conf) agent_config.register_external_process_opts(self.conf) self.conf.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') self.conf.set_override('state_path', cfg.CONF.state_path) self.device_exists_p = mock.patch( 'neutron.agent.linux.ip_lib.device_exists') self.device_exists = self.device_exists_p.start() self.ensure_dir = mock.patch( 'oslo_utils.fileutils.ensure_tree').start() mock.patch('neutron.agent.linux.keepalived.KeepalivedManager' '.get_full_config_file_path').start() self.utils_exec_p = mock.patch( 'neutron.agent.linux.utils.execute') self.utils_exec = self.utils_exec_p.start() self.utils_replace_file_p = mock.patch( 'neutron_lib.utils.file.replace_file') self.utils_replace_file = self.utils_replace_file_p.start() self.external_process_p = mock.patch( 'neutron.agent.linux.external_process.ProcessManager') self.external_process = self.external_process_p.start() self.process_monitor = mock.patch( 'neutron.agent.linux.external_process.ProcessMonitor').start() self.send_adv_notif_p = mock.patch( 'neutron.agent.linux.ip_lib.send_ip_addr_adv_notif') self.send_adv_notif = self.send_adv_notif_p.start() self.dvr_cls_p = mock.patch('neutron.agent.linux.interface.NullDriver') driver_cls = self.dvr_cls_p.start() self.mock_driver = mock.MagicMock() self.mock_driver.DEV_NAME_LEN = ( interface.LinuxInterfaceDriver.DEV_NAME_LEN) driver_cls.return_value = self.mock_driver self.ip_cls_p = mock.patch('neutron.agent.linux.ip_lib.IPWrapper') ip_cls = self.ip_cls_p.start() self.mock_ip = mock.MagicMock() ip_cls.return_value = self.mock_ip ip_rule = mock.patch('neutron.agent.linux.ip_lib.IPRule').start() self.mock_rule = mock.MagicMock() ip_rule.return_value = self.mock_rule ip_dev = mock.patch('neutron.agent.linux.ip_lib.IPDevice').start() self.mock_ip_dev = mock.MagicMock() ip_dev.return_value = self.mock_ip_dev self.l3pluginApi_cls_p = mock.patch( 'neutron.agent.l3.agent.L3PluginApi') l3pluginApi_cls = self.l3pluginApi_cls_p.start() self.plugin_api = mock.MagicMock() l3pluginApi_cls.return_value = self.plugin_api self.looping_call_p = mock.patch( 'oslo_service.loopingcall.FixedIntervalLoopingCall') self.looping_call_p.start() subnet_id_1 = _uuid() subnet_id_2 = _uuid() self.snat_ports = [{'subnets': [{'cidr': '152.2.0.0/16', 'gateway_ip': '152.2.0.1', 'id': subnet_id_1}], 'network_id': _uuid(), 'device_owner': lib_constants.DEVICE_OWNER_ROUTER_SNAT, 'mac_address': 'fa:16:3e:80:8d:80', 'fixed_ips': [{'subnet_id': subnet_id_1, 'ip_address': '152.2.0.13', 'prefixlen': 16}], 'id': _uuid(), 'device_id': _uuid()}, {'subnets': [{'cidr': '152.10.0.0/16', 'gateway_ip': '152.10.0.1', 'id': subnet_id_2}], 'network_id': _uuid(), 'device_owner': lib_constants.DEVICE_OWNER_ROUTER_SNAT, 'mac_address': 'fa:16:3e:80:8d:80', 'fixed_ips': [{'subnet_id': subnet_id_2, 'ip_address': '152.10.0.13', 'prefixlen': 16}], 'id': _uuid(), 'device_id': _uuid()}] self.ri_kwargs = {'agent_conf': self.conf, 'interface_driver': self.mock_driver} def _create_router(self, router=None, **kwargs): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.router_id = _uuid() if not router: router = mock.MagicMock() kwargs['agent'] = agent kwargs['router_id'] = self.router_id kwargs['router'] = router kwargs['agent_conf'] = self.conf kwargs['interface_driver'] = mock.Mock() return dvr_router.DvrLocalRouter(HOSTNAME, **kwargs) def _set_ri_kwargs(self, agent, router_id, router): self.ri_kwargs['agent'] = agent self.ri_kwargs['router_id'] = router_id self.ri_kwargs['router'] = router def test_gw_ns_name(self): ri = self._create_router() self.assertEqual(ri.ns_name, ri.get_gw_ns_name()) def test_create_dvr_fip_interfaces_update(self): ri = self._create_router() fip_agent_port = {'subnets': []} ri.get_floating_agent_gw_interface = mock.Mock( return_value=fip_agent_port) ri.get_floating_ips = mock.Mock(return_value=True) ri.fip_ns = mock.Mock() ri.fip_ns.subscribe.return_value = False ri.rtr_fip_connect = True ex_gw_port = {'network_id': 'fake_net_id'} ri.create_dvr_external_gateway_on_agent(ex_gw_port) ri.fip_ns.create_or_update_gateway_port.assert_called_once_with( fip_agent_port) def test_create_dvr_fip_interfaces_with_matching_address_scope(self): self._setup_create_dvr_fip_interfaces_for_setting_routing_rules( address_scopes_match=True) def test_create_dvr_fip_interfaces_with_address_scope_mismatch(self): self._setup_create_dvr_fip_interfaces_for_setting_routing_rules() def _setup_create_dvr_fip_interfaces_for_setting_routing_rules( self, address_scopes_match=False): ri = self._create_router() ri.get_floating_agent_gw_interface = mock.Mock() ri.fip_ns = mock.Mock() ri._add_interface_routing_rule_to_router_ns = mock.Mock() ri._add_interface_route_to_fip_ns = mock.Mock() ri.fip_ns._create_rtr_2_fip_link = mock.Mock() ri.internal_ports = ['moke_port_1', 'moke_port_2'] if address_scopes_match: ri._check_if_address_scopes_match = mock.Mock( return_value=True) else: ri._check_if_address_scopes_match = mock.Mock( return_value=False) ri.rtr_fip_connect = False ex_gw_port = {'network_id': 'fake_net_id'} ri.create_dvr_external_gateway_on_agent(ex_gw_port) ri._check_rtr_2_fip_connect = mock.Mock() ri.connect_rtr_2_fip() self.assertTrue(ri._check_if_address_scopes_match.called) if address_scopes_match: self.assertTrue( ri.fip_ns.create_rtr_2_fip_link.called) self.assertTrue( ri._add_interface_routing_rule_to_router_ns.called) self.assertTrue( ri._add_interface_route_to_fip_ns.called) else: self.assertFalse( ri._add_interface_routing_rule_to_router_ns.called) self.assertFalse( ri._add_interface_route_to_fip_ns.called) self.assertTrue( ri.fip_ns.create_rtr_2_fip_link.called) def test_get_floating_ips_dvr(self): router = mock.MagicMock() router.get.return_value = [{'host': HOSTNAME}, {'host': mock.sentinel.otherhost}] ri = self._create_router(router) fips = ri.get_floating_ips() self.assertEqual( [{'host': HOSTNAME}, {'host': mock.sentinel.otherhost}], fips) def test_floating_forward_rules_no_fip_ns(self): router = mock.MagicMock() router.get.return_value = [{'host': HOSTNAME}, {'host': mock.sentinel.otherhost}] fip = {'id': _uuid()} ri = self._create_router(router) self.assertFalse(ri.floating_forward_rules(fip)) def test_floating_forward_rules(self): router = mock.MagicMock() router.get.return_value = [{'host': HOSTNAME}, {'host': mock.sentinel.otherhost}] ri = self._create_router(router) floating_ip = '15.1.2.3' rtr_2_fip_name = 'fake_router' fixed_ip = '192.168.0.1' fip = {'id': _uuid(), 'fixed_ip_address': '192.168.0.1', 'floating_ip_address': '15.1.2.3'} instance = mock.Mock() instance.get_rtr_ext_device_name = mock.Mock( return_value=rtr_2_fip_name) ri.fip_ns = instance dnat_from_floatingip_to_fixedip = ( 'PREROUTING', '-d %s/32 -i %s -j DNAT --to-destination %s' % ( floating_ip, rtr_2_fip_name, fixed_ip)) to_source = '-s %s/32 -j SNAT --to-source %s' % (fixed_ip, floating_ip) if ri.iptables_manager.random_fully: to_source += ' --random-fully' snat_from_fixedip_to_floatingip = ('float-snat', to_source) actual = ri.floating_forward_rules(fip) expected = [dnat_from_floatingip_to_fixedip, snat_from_fixedip_to_floatingip] self.assertEqual(expected, actual) def test_floating_mangle_rules_no_fip_ns(self): router = mock.MagicMock() router.get.return_value = [{'host': HOSTNAME}, {'host': mock.sentinel.otherhost}] ri = self._create_router(router) floating_ip = mock.Mock() fixed_ip = mock.Mock() internal_mark = mock.Mock() self.assertFalse(ri.floating_mangle_rules(floating_ip, fixed_ip, internal_mark)) def test_floating_mangle_rules(self): router = mock.MagicMock() router.get.return_value = [{'host': HOSTNAME}, {'host': mock.sentinel.otherhost}] ri = self._create_router(router) floating_ip = '15.1.2.3' fixed_ip = '192.168.0.1' internal_mark = 'fake_mark' rtr_2_fip_name = 'fake_router' instance = mock.Mock() instance.get_rtr_ext_device_name = mock.Mock( return_value=rtr_2_fip_name) ri.fip_ns = instance mark_traffic_to_floating_ip = ( 'floatingip', '-d %s/32 -i %s -j MARK --set-xmark %s' % ( floating_ip, rtr_2_fip_name, internal_mark)) mark_traffic_from_fixed_ip = ( 'FORWARD', '-s %s/32 -j $float-snat' % fixed_ip) actual = ri.floating_mangle_rules(floating_ip, fixed_ip, internal_mark) expected = [mark_traffic_to_floating_ip, mark_traffic_from_fixed_ip] self.assertEqual(expected, actual) @mock.patch.object(ip_lib, 'send_ip_addr_adv_notif') @mock.patch.object(ip_lib, 'IPDevice') @mock.patch.object(ip_lib, 'IPRule') def test_floating_ip_added_dist(self, mIPRule, mIPDevice, mock_adv_notif): router = mock.MagicMock() ri = self._create_router(router) ri.ex_gw_port = ri.router['gw_port'] ext_net_id = _uuid() subnet_id = _uuid() agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', 'prefixlen': 24, 'subnet_id': subnet_id}], 'subnets': [{'id': subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}], 'id': _uuid(), 'network_id': ext_net_id, 'mac_address': 'ca:fe:de:ad:be:ef'} fip = {'id': _uuid(), 'host': HOSTNAME, 'floating_ip_address': '15.1.2.3', 'fixed_ip_address': '192.168.0.1', 'floating_network_id': ext_net_id, 'port_id': _uuid()} ri.fip_ns = mock.Mock() ri.fip_ns.agent_gateway_port = agent_gw_port ri.create_dvr_external_gateway_on_agent(ri.ex_gw_port) ri._check_rtr_2_fip_connect = mock.Mock() ri.connect_rtr_2_fip() self.assertTrue(ri.rtr_fip_connect) ri.fip_ns.allocate_rule_priority.return_value = FIP_PRI subnet = lla.LinkLocalAddressPair('169.254.30.42/31') ri.rtr_fip_subnet = subnet ri.fip_ns.local_subnets = mock.Mock() ri.fip_ns.local_subnets.allocate.return_value = subnet ip_cidr = common_utils.ip_to_cidr(fip['floating_ip_address']) ri.floating_ip_added_dist(fip, ip_cidr) mIPRule().rule.add.assert_called_with(ip='192.168.0.1', table=16, priority=FIP_PRI) ri.fip_ns.local_subnets.allocate.assert_not_called() # Validate that fip_ns.local_subnets is called when # ri.rtr_fip_subnet is None ri.rtr_fip_subnet = None ri.floating_ip_added_dist(fip, ip_cidr) mIPRule().rule.add.assert_called_with(ip='192.168.0.1', table=16, priority=FIP_PRI) ri.fip_ns.local_subnets.allocate.assert_called_once_with(ri.router_id) # TODO(mrsmith): add more asserts @mock.patch.object(ip_lib, 'IPWrapper') @mock.patch.object(ip_lib, 'IPDevice') @mock.patch.object(ip_lib, 'IPRule') def test_floating_ip_removed_dist(self, mIPRule, mIPDevice, mIPWrapper): router = mock.MagicMock() ri = self._create_router(router) ri.ex_gw_port = ri.router['gw_port'] subnet_id = _uuid() fixed_ip = '20.0.0.30' agent_gw_port = {'fixed_ips': [{'ip_address': fixed_ip, 'prefixlen': 24, 'subnet_id': subnet_id}], 'subnets': [{'id': subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}], 'id': _uuid(), 'network_id': _uuid(), 'mac_address': 'ca:fe:de:ad:be:ef'} fip_cidr = '11.22.33.44/24' ri.fip_ns = mock.Mock() ri.fip_ns.get_name.return_value = 'fip_ns_name' ri.floating_ips_dict['11.22.33.44'] = (fixed_ip, FIP_PRI) ri.fip_2_rtr = '11.22.33.42' ri.rtr_2_fip = '11.22.33.40' ri.fip_ns.agent_gateway_port = agent_gw_port s = lla.LinkLocalAddressPair('169.254.30.42/31') ri.rtr_fip_subnet = s ri.fip_ns.local_subnets = mock.Mock() ri.floating_ip_removed_dist(fip_cidr) mIPRule().rule.delete.assert_called_with( ip=fixed_ip, table=16, priority=FIP_PRI) mIPDevice().route.delete_route.assert_called_with(fip_cidr, str(s.ip)) ri.fip_ns.local_subnets.allocate.assert_not_called() @mock.patch.object(ip_lib, 'IPRule') def test_floating_ip_moved_dist(self, mIPRule): router = mock.MagicMock() ri = self._create_router(router) floating_ip_address = '15.1.2.3' fixed_ip = '192.168.0.1' fip = {'floating_ip_address': floating_ip_address, 'fixed_ip_address': fixed_ip} ri.floating_ips_dict['15.1.2.3'] = (fixed_ip, FIP_PRI) ri.fip_ns = mock.Mock() ri.fip_ns.allocate_rule_priority.return_value = FIP_PRI ri.floating_ip_moved_dist(fip) mIPRule().rule.delete.assert_called_once_with( ip=fixed_ip, table=16, priority=FIP_PRI) ri.fip_ns.deallocate_rule_priority.assert_called_once_with( floating_ip_address) ri.fip_ns.allocate_rule_priority.assert_called_once_with( floating_ip_address) mIPRule().rule.add.assert_called_with(ip=fixed_ip, table=16, priority=FIP_PRI) def _test_add_floating_ip(self, ri, fip, is_failure=False): if not is_failure: ri.floating_ip_added_dist = mock.Mock( return_value=lib_constants.FLOATINGIP_STATUS_ACTIVE) else: ri.floating_ip_added_dist = mock.Mock( return_value=lib_constants.FLOATINGIP_STATUS_ERROR) result = ri.add_floating_ip(fip, mock.sentinel.interface_name, mock.sentinel.device) ri.floating_ip_added_dist.assert_called_once_with( fip, mock.ANY) return result def test_add_floating_ip(self): ri = self._create_router(mock.MagicMock()) ip = '15.1.2.3' fip = {'floating_ip_address': ip} result = self._test_add_floating_ip(ri, fip) ri.floating_ip_added_dist.assert_called_once_with(fip, ip + '/32') self.assertEqual(lib_constants.FLOATINGIP_STATUS_ACTIVE, result) def test_add_floating_ip_failure(self): ri = self._create_router(mock.MagicMock()) ip = '15.1.2.3' fip = {'floating_ip_address': ip} result = self._test_add_floating_ip(ri, fip, True) ri.floating_ip_added_dist.assert_called_once_with(fip, ip + '/32') self.assertEqual(lib_constants.FLOATINGIP_STATUS_ERROR, result) @mock.patch.object(router_info.RouterInfo, 'remove_floating_ip') def test_remove_floating_ip(self, super_remove_floating_ip): ri = self._create_router(mock.MagicMock()) ri.floating_ip_removed_dist = mock.Mock() ri.remove_floating_ip(mock.sentinel.device, mock.sentinel.ip_cidr) self.assertFalse(super_remove_floating_ip.called) ri.floating_ip_removed_dist.assert_called_once_with( mock.sentinel.ip_cidr) def test__get_internal_port(self): ri = self._create_router() port = {'fixed_ips': [{'subnet_id': mock.sentinel.subnet_id}]} router_ports = [port] ri.router.get.return_value = router_ports self.assertEqual(port, ri._get_internal_port(mock.sentinel.subnet_id)) def test__get_internal_port_not_found(self): ri = self._create_router() port = {'fixed_ips': [{'subnet_id': mock.sentinel.subnet_id}]} router_ports = [port] ri.router.get.return_value = router_ports self.assertIsNone(ri._get_internal_port(mock.sentinel.subnet_id2)) def test__get_snat_idx_ipv4(self): ip_cidr = '101.12.13.00/24' ri = self._create_router(mock.MagicMock()) snat_idx = ri._get_snat_idx(ip_cidr) # 0x650C0D00 is numerical value of 101.12.13.00 self.assertEqual(0x650C0D00, snat_idx) def test__get_snat_idx_ipv6(self): ip_cidr = '2620:0:a03:e100::/64' ri = self._create_router(mock.MagicMock()) snat_idx = ri._get_snat_idx(ip_cidr) # 0x3D345705 is 30 bit xor folded crc32 of the ip_cidr self.assertEqual(0x3D345705, snat_idx) def test__get_snat_idx_ipv6_below_32768(self): ip_cidr = 'd488::/30' # crc32 of this ip_cidr is 0x1BD7 ri = self._create_router(mock.MagicMock()) snat_idx = ri._get_snat_idx(ip_cidr) # 0x1BD7 + 0x3FFFFFFF = 0x40001BD6 self.assertEqual(0x40001BD6, snat_idx) def test__set_subnet_arp_info(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrLocalRouter(HOSTNAME, **self.ri_kwargs) ports = ri.router.get(lib_constants.INTERFACE_KEY, []) subnet_id = l3_test_common.get_subnet_id(ports[0]) test_ports = [{'mac_address': '00:11:22:33:44:55', 'device_owner': lib_constants.DEVICE_OWNER_DHCP, 'fixed_ips': [{'ip_address': '1.2.3.4', 'prefixlen': 24, 'subnet_id': subnet_id}]}, {'mac_address': '11:22:33:44:55:66', 'device_owner': lib_constants.DEVICE_OWNER_LOADBALANCER, 'fixed_ips': [{'ip_address': '1.2.3.5', 'prefixlen': 24, 'subnet_id': subnet_id}]}, {'mac_address': '22:33:44:55:66:77', 'device_owner': lib_constants.DEVICE_OWNER_LOADBALANCERV2, 'fixed_ips': [{'ip_address': '1.2.3.6', 'prefixlen': 24, 'subnet_id': subnet_id}]}] self.plugin_api.get_ports_by_subnet.return_value = test_ports # Test basic case ports[0]['subnets'] = [{'id': subnet_id, 'cidr': '1.2.3.0/24'}] with mock.patch.object(ri, '_process_arp_cache_for_internal_port') as parp: ri._set_subnet_arp_info(subnet_id) self.assertEqual(1, parp.call_count) self.mock_ip_dev.neigh.add.assert_called_once_with( '1.2.3.4', '00:11:22:33:44:55') # Test negative case router['distributed'] = False ri._set_subnet_arp_info(subnet_id) self.mock_ip_dev.neigh.add.never_called() def test_add_arp_entry(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True subnet_id = l3_test_common.get_subnet_id( router[lib_constants.INTERFACE_KEY][0]) arp_table = {'ip_address': '1.7.23.11', 'mac_address': '00:11:22:33:44:55', 'subnet_id': subnet_id} payload = {'arp_table': arp_table, 'router_id': router['id']} agent._router_added(router['id'], router) agent.add_arp_entry(None, payload) agent.router_deleted(None, router['id']) self.mock_ip_dev.neigh.add.assert_called_once_with( '1.7.23.11', '00:11:22:33:44:55') def test_add_arp_entry_no_routerinfo(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(num_internal_ports=2) subnet_id = l3_test_common.get_subnet_id( router[lib_constants.INTERFACE_KEY][0]) arp_table = {'ip_address': '1.7.23.11', 'mac_address': '00:11:22:33:44:55', 'subnet_id': subnet_id} payload = {'arp_table': arp_table, 'router_id': router['id']} agent.add_arp_entry(None, payload) def test__update_arp_entry_with_no_subnet(self): self._set_ri_kwargs(mock.sentinel.agent, 'foo_router_id', {'distributed': True, 'gw_port_host': HOSTNAME}) ri = dvr_router.DvrLocalRouter(HOSTNAME, **self.ri_kwargs) with mock.patch.object(l3_agent.ip_lib, 'IPDevice') as f: ri._update_arp_entry(mock.ANY, mock.ANY, 'foo_subnet_id', 'add') self.assertFalse(f.call_count) def _setup_test_for_arp_entry_cache(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrLocalRouter(HOSTNAME, **self.ri_kwargs) subnet_id = l3_test_common.get_subnet_id( ri.router[lib_constants.INTERFACE_KEY][0]) return ri, subnet_id def test__update_arp_entry_calls_arp_cache_with_no_device(self): ri, subnet_id = self._setup_test_for_arp_entry_cache() state = True with mock.patch.object(l3_agent.ip_lib, 'IPDevice') as rtrdev,\ mock.patch.object(ri, '_cache_arp_entry') as arp_cache: rtrdev.return_value.exists.return_value = False state = ri._update_arp_entry( mock.ANY, mock.ANY, subnet_id, 'add') self.assertFalse(state) self.assertTrue(arp_cache.called) arp_cache.assert_called_once_with(mock.ANY, mock.ANY, subnet_id, 'add') self.assertFalse(rtrdev.neigh.add.called) def test__process_arp_cache_for_internal_port(self): ri, subnet_id = self._setup_test_for_arp_entry_cache() ri._cache_arp_entry('1.7.23.11', '00:11:22:33:44:55', subnet_id, 'add') self.assertEqual(1, len(ri._pending_arp_set)) with mock.patch.object(ri, '_update_arp_entry') as update_arp: update_arp.return_value = True ri._process_arp_cache_for_internal_port(subnet_id) self.assertEqual(0, len(ri._pending_arp_set)) def test__delete_arp_cache_for_internal_port(self): ri, subnet_id = self._setup_test_for_arp_entry_cache() ri._cache_arp_entry('1.7.23.11', '00:11:22:33:44:55', subnet_id, 'add') self.assertEqual(1, len(ri._pending_arp_set)) ri._delete_arp_cache_for_internal_port(subnet_id) self.assertEqual(0, len(ri._pending_arp_set)) def test_del_arp_entry(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True subnet_id = l3_test_common.get_subnet_id( router[lib_constants.INTERFACE_KEY][0]) arp_table = {'ip_address': '1.5.25.15', 'mac_address': '00:44:33:22:11:55', 'subnet_id': subnet_id} payload = {'arp_table': arp_table, 'router_id': router['id']} agent._router_added(router['id'], router) # first add the entry agent.add_arp_entry(None, payload) # now delete it agent.del_arp_entry(None, payload) self.mock_ip_dev.neigh.delete.assert_called_once_with( '1.5.25.15', '00:44:33:22:11:55') agent.router_deleted(None, router['id']) def test_get_floating_agent_gw_interfaces(self): fake_network_id = _uuid() subnet_id = _uuid() agent_gateway_port = ( [{'fixed_ips': [{'ip_address': '20.0.0.30', 'prefixlen': 24, 'subnet_id': subnet_id}], 'subnets': [{'id': subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}], 'id': _uuid(), portbindings.HOST_ID: 'myhost', 'device_owner': lib_constants.DEVICE_OWNER_AGENT_GW, 'network_id': fake_network_id, 'mac_address': 'ca:fe:de:ad:be:ef'}] ) router = l3_test_common.prepare_router_data(enable_snat=True) router[n_const.FLOATINGIP_AGENT_INTF_KEY] = agent_gateway_port router['distributed'] = True agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrLocalRouter(HOSTNAME, **self.ri_kwargs) self.assertEqual( agent_gateway_port[0], ri.get_floating_agent_gw_interface(fake_network_id)) def test_process_router_dist_floating_ip_add(self): fake_floatingips = {'floatingips': [ {'id': _uuid(), 'host': HOSTNAME, 'floating_ip_address': '15.1.2.3', 'fixed_ip_address': '192.168.0.1', 'floating_network_id': mock.sentinel.ext_net_id, 'port_id': _uuid()}, {'id': _uuid(), 'host': 'some-other-host', 'floating_ip_address': '15.1.2.4', 'fixed_ip_address': '192.168.0.10', 'floating_network_id': mock.sentinel.ext_net_id, 'port_id': _uuid()}]} router = l3_test_common.prepare_router_data(enable_snat=True) router[lib_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] router['distributed'] = True agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrLocalRouter(HOSTNAME, **self.ri_kwargs) ri.iptables_manager.ipv4['nat'] = mock.MagicMock() fip_ns = agent.get_fip_ns(mock.sentinel.ext_net_id) subnet_id = _uuid() fip_ns.agent_gateway_port = ( {'fixed_ips': [{'ip_address': '20.0.0.30', 'subnet_id': subnet_id}], 'subnets': [{'id': subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}], 'id': _uuid(), 'network_id': _uuid(), 'mac_address': 'ca:fe:de:ad:be:ef'} ) def _test_ext_gw_updated_dvr_agent_mode(self, host, agent_mode, expected_call_count): router = l3_test_common.prepare_router_data(num_internal_ports=2) agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self._set_ri_kwargs(agent, router['id'], router) ri = dvr_router.DvrLocalRouter(HOSTNAME, **self.ri_kwargs) interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test(self, ri) ri._external_gateway_added = mock.Mock() # test agent mode = dvr (compute node) router['gw_port_host'] = host agent.conf.agent_mode = agent_mode ri.external_gateway_updated(ex_gw_port, interface_name) # no gateway should be added on dvr node self.assertEqual(expected_call_count, ri._external_gateway_added.call_count) def test_ext_gw_updated_dvr_agent_mode(self): # no gateway should be added on dvr node self._test_ext_gw_updated_dvr_agent_mode('any-foo', 'dvr', 0) def test_ext_gw_updated_dvr_agent_mode_host(self): # no gateway should be added on dvr node self._test_ext_gw_updated_dvr_agent_mode(HOSTNAME, 'dvr', 0) def test_external_gateway_removed_ext_gw_port_and_fip(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.conf.agent_mode = lib_constants.L3_AGENT_MODE_DVR_SNAT router = l3_test_common.prepare_router_data(num_internal_ports=2) router['gw_port_host'] = HOSTNAME self.mock_driver.unplug.reset_mock() external_net_id = router['gw_port']['network_id'] self._set_ri_kwargs(agent, router['id'], router) ri = dvr_edge_rtr.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ri.remove_floating_ip = mock.Mock() agent._fetch_external_net_id = mock.Mock(return_value=external_net_id) ri.ex_gw_port = ri.router['gw_port'] del ri.router['gw_port'] ri.external_gateway_added( ri.ex_gw_port, ri.get_external_device_name(ri.ex_gw_port['id'])) ri.fip_ns = None nat = ri.iptables_manager.ipv4['nat'] nat.clear_rules_by_tag = mock.Mock() nat.add_rule = mock.Mock() ri.fip_ns = agent.get_fip_ns(external_net_id) subnet_id = _uuid() ri.fip_ns.agent_gateway_port = { 'fixed_ips': [{ 'ip_address': '20.0.0.30', 'prefixlen': 24, 'subnet_id': subnet_id }], 'subnets': [{'id': subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}], 'id': _uuid(), 'network_id': external_net_id, 'mac_address': 'ca:fe:de:ad:be:ef'} vm_floating_ip = '19.4.4.2' ri.floating_ips_dict[vm_floating_ip] = FIP_PRI ri.rtr_fip_subnet = ri.fip_ns.local_subnets.allocate(ri.router_id) _, fip_to_rtr = ri.rtr_fip_subnet.get_pair() self.mock_ip.get_devices.return_value = [ l3_test_common.FakeDev(ri.fip_ns.get_ext_device_name(_uuid()))] ri.get_router_cidrs = mock.Mock( return_value={vm_floating_ip + '/32', '19.4.4.1/24'}) self.device_exists.return_value = True ri.external_gateway_removed( ri.ex_gw_port, ri.get_external_device_name(ri.ex_gw_port['id'])) ri.remove_floating_ip.assert_called_once_with(self.mock_ip_dev, '19.4.4.2/32') def test_get_router_cidrs_no_fip_ns(self): router = mock.MagicMock() router.get.return_value = [{'host': HOSTNAME}, {'host': mock.sentinel.otherhost}] ri = self._create_router(router) device = mock.Mock() self.assertFalse(ri.get_router_cidrs(device)) def test_get_router_cidrs_no_device_exists(self): router = mock.MagicMock() router.get.return_value = [{'host': HOSTNAME}, {'host': mock.sentinel.otherhost}] ri = self._create_router(router) fake_fip_ns = mock.Mock(return_value=True) fake_fip_ns.get_name = mock.Mock(return_value=None) fake_fip_ns.get_int_device_name = mock.Mock(return_value=None) ri.fip_ns = fake_fip_ns device = mock.Mock() device.exists = mock.Mock(return_value=False) with mock.patch.object(ip_lib, 'IPDevice', return_value=device): self.assertFalse(ri.get_router_cidrs(device)) @mock.patch.object(router_info.RouterInfo, '_add_snat_rules') @mock.patch.object(dvr_router.DvrLocalRouter, '_handle_router_snat_rules') def test_handle_snat_rule_for_centralized_fip( self, _add_snat_rules, _handle_router_snat_rules): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.conf.agent_mode = lib_constants.L3_AGENT_MODE_DVR_SNAT self.mock_driver.unplug.reset_mock() router = l3_test_common.prepare_router_data(enable_floating_ip=True) router['gw_port_host'] = HOSTNAME self._set_ri_kwargs(agent, router['id'], router) ri = dvr_edge_rtr.DvrEdgeRouter(HOSTNAME, **self.ri_kwargs) ri.snat_iptables_manager = mock.MagicMock() ipv4_nat = ri.snat_iptables_manager.ipv4['nat'] interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test(self, ri) ri._handle_router_snat_rules(ex_gw_port, interface_name) ipv4_nat.add_rule.assert_called_once_with('snat', '-j $float-snat') @mock.patch.object(dvr_edge_rtr.DvrEdgeRouter, 'add_centralized_floatingip') def test_add_centralized_floatingip_dvr_ha( self, super_add_centralized_floatingip): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.conf.agent_mode = lib_constants.L3_AGENT_MODE_DVR_SNAT router = l3_test_common.prepare_router_data( num_internal_ports=2, enable_ha=True) router['gw_port_host'] = HOSTNAME self.mock_driver.unplug.reset_mock() self._set_ri_kwargs(agent, router['id'], router) fip = {'id': _uuid()} fip_cidr = '11.22.33.44/24' ri = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, [], **self.ri_kwargs) ri.is_router_master = mock.Mock(return_value=False) ri._add_vip = mock.Mock() interface_name = ri.get_snat_external_device_interface_name( ri.get_ex_gw_port()) ri.add_centralized_floatingip(fip, fip_cidr) ri._add_vip.assert_called_once_with(fip_cidr, interface_name) super_add_centralized_floatingip.assert_not_called() router[lib_constants.HA_INTERFACE_KEY]['status'] = 'DOWN' self._set_ri_kwargs(agent, router['id'], router) ri_1 = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, [], **self.ri_kwargs) ri_1.is_router_master = mock.Mock(return_value=True) ri_1._add_vip = mock.Mock() interface_name = ri_1.get_snat_external_device_interface_name( ri_1.get_ex_gw_port()) ri_1.add_centralized_floatingip(fip, fip_cidr) ri_1._add_vip.assert_called_once_with(fip_cidr, interface_name) super_add_centralized_floatingip.assert_not_called() router[lib_constants.HA_INTERFACE_KEY]['status'] = 'ACTIVE' self._set_ri_kwargs(agent, router['id'], router) ri_2 = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, [], **self.ri_kwargs) ri_2.is_router_master = mock.Mock(return_value=True) ri_2._add_vip = mock.Mock() interface_name = ri_2.get_snat_external_device_interface_name( ri_2.get_ex_gw_port()) ri_2.add_centralized_floatingip(fip, fip_cidr) ri_2._add_vip.assert_called_once_with(fip_cidr, interface_name) super_add_centralized_floatingip.assert_called_once_with(fip, fip_cidr) @mock.patch.object(dvr_edge_rtr.DvrEdgeRouter, 'remove_centralized_floatingip') def test_remove_centralized_floatingip(self, super_remove_centralized_floatingip): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.conf.agent_mode = lib_constants.L3_AGENT_MODE_DVR_SNAT router = l3_test_common.prepare_router_data(num_internal_ports=2) router['gw_port_host'] = HOSTNAME self.mock_driver.unplug.reset_mock() self._set_ri_kwargs(agent, router['id'], router) fip_cidr = '11.22.33.44/24' ri = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, [], **self.ri_kwargs) ri.is_router_master = mock.Mock(return_value=False) ri._remove_vip = mock.Mock() ri.remove_centralized_floatingip(fip_cidr) ri._remove_vip.assert_called_once_with(fip_cidr) super_remove_centralized_floatingip.assert_not_called() ri1 = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, [], **self.ri_kwargs) ri1.is_router_master = mock.Mock(return_value=True) ri1._remove_vip = mock.Mock() ri1.remove_centralized_floatingip(fip_cidr) ri1._remove_vip.assert_called_once_with(fip_cidr) super_remove_centralized_floatingip.assert_called_once_with(fip_cidr) neutron-12.1.1/neutron/tests/unit/agent/l3/test_l3_agent_extension_api.py0000664000175000017500000001120113553660047026575 0ustar zuulzuul00000000000000# Copyright 2016 Comcast # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import mock from oslo_utils import uuidutils from neutron.agent.l3 import l3_agent_extension_api as l3_agent_api from neutron.agent.l3 import router_info from neutron.agent.linux import ip_lib from neutron.conf.agent import common as config from neutron.conf.agent.l3 import config as l3_config from neutron.tests import base class TestL3AgentExtensionApi(base.BaseTestCase): def _prepare_router_data(self, ports=None): self.router_id = uuidutils.generate_uuid() self.project_id = uuidutils.generate_uuid() self.conf = config.setup_conf() l3_config.register_l3_agent_config_opts(l3_config.OPTS, self.conf) ri_kwargs = {'router': {'id': self.router_id, 'project_id': self.project_id}, 'agent_conf': self.conf, 'interface_driver': mock.ANY, 'use_ipv6': mock.ANY} ri = router_info.RouterInfo(mock.Mock(), self.router_id, **ri_kwargs) ri.internal_ports = ports return {ri.router_id: ri}, ri def test_get_router_hosting_port_for_router_not_in_ns(self): port_ids = [1, 2] ports = [{'id': pid} for pid in port_ids] router_info, ri = self._prepare_router_data(ports) with mock.patch.object(ip_lib, 'list_network_namespaces') as mock_list_netns: mock_list_netns.return_value = [] api_object = l3_agent_api.L3AgentExtensionAPI(router_info) router = api_object.get_router_hosting_port(port_ids[0]) mock_list_netns.assert_called_once_with() self.assertFalse(router) def test_get_router_hosting_port_for_router_in_ns(self): port_ids = [1, 2] ports = [{'id': pid} for pid in port_ids] router_info, ri = self._prepare_router_data(ports) with mock.patch.object(ip_lib, 'list_network_namespaces') as mock_list_netns: mock_list_netns.return_value = [ri.ns_name] api_object = l3_agent_api.L3AgentExtensionAPI(router_info) router = api_object.get_router_hosting_port(port_ids[0]) self.assertEqual(ri, router) def test_get_routers_in_project(self): router_info, ri = self._prepare_router_data() with mock.patch.object(ip_lib, 'list_network_namespaces') as mock_list_netns: mock_list_netns.return_value = [ri.ns_name] api_object = l3_agent_api.L3AgentExtensionAPI(router_info) routers = api_object.get_routers_in_project(self.project_id) self.assertEqual([ri], routers) def test_is_router_in_namespace_for_in_ns(self): router_info, ri = self._prepare_router_data() with mock.patch.object(ip_lib, 'list_network_namespaces') as mock_list_netns: mock_list_netns.return_value = [ri.ns_name] api_object = l3_agent_api.L3AgentExtensionAPI(router_info) router_in_ns = api_object.is_router_in_namespace(ri.router_id) self.assertTrue(router_in_ns) def test_is_router_in_namespace_for_not_in_ns(self): router_info, ri = self._prepare_router_data() with mock.patch.object(ip_lib, 'list_network_namespaces') as mock_list_netns: mock_list_netns.return_value = [uuidutils.generate_uuid()] api_object = l3_agent_api.L3AgentExtensionAPI(router_info) router_in_ns = api_object.is_router_in_namespace(ri.router_id) self.assertFalse(router_in_ns) def test_get_router_info(self): router_info, ri = self._prepare_router_data() api_object = l3_agent_api.L3AgentExtensionAPI(router_info) self.assertEqual(ri, api_object.get_router_info(self.router_id)) def test_get_router_info_nonexistent(self): router_info, ri = self._prepare_router_data() api_object = l3_agent_api.L3AgentExtensionAPI(router_info) self.assertIsNone( api_object.get_router_info(uuidutils.generate_uuid())) neutron-12.1.1/neutron/tests/unit/agent/l3/test_legacy_router.py0000664000175000017500000001060113553660046025022 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as lib_constants from oslo_utils import uuidutils from neutron.agent.l3 import legacy_router from neutron.agent.linux import ip_lib from neutron.tests import base _uuid = uuidutils.generate_uuid class BasicRouterTestCaseFramework(base.BaseTestCase): def _create_router(self, router=None, **kwargs): if not router: router = mock.MagicMock() self.agent_conf = mock.Mock() self.driver = mock.Mock() self.router_id = _uuid() return legacy_router.LegacyRouter(mock.Mock(), self.router_id, router, self.agent_conf, self.driver, **kwargs) class TestBasicRouterOperations(BasicRouterTestCaseFramework): def test_remove_floating_ip(self): ri = self._create_router(mock.MagicMock()) device = mock.Mock() cidr = '15.1.2.3/32' ri.remove_floating_ip(device, cidr) device.delete_addr_and_conntrack_state.assert_called_once_with(cidr) def test_remove_external_gateway_ip(self): ri = self._create_router(mock.MagicMock()) device = mock.Mock() cidr = '172.16.0.0/24' ri.remove_external_gateway_ip(device, cidr) device.delete_addr_and_conntrack_state.assert_called_once_with(cidr) @mock.patch.object(ip_lib, 'IPDevice') def test_remove_multiple_external_gateway_ips(self, IPDevice): ri = self._create_router(mock.MagicMock()) IPDevice.return_value = device = mock.Mock() gw_ip_pri = '172.16.5.110' gw_ip_sec = '172.16.5.111' gw_ip6_pri = '2001:db8::1' gw_ip6_sec = '2001:db8::2' v4_prefixlen = 24 v6_prefixlen = 64 ex_gw_port = {'fixed_ips': [ {'ip_address': gw_ip_pri, 'prefixlen': v4_prefixlen}, {'ip_address': gw_ip_sec}, {'ip_address': gw_ip6_pri, 'prefixlen': v6_prefixlen}, {'ip_address': gw_ip6_sec}]} ri.external_gateway_removed(ex_gw_port, "qg-fake-name") cidr_pri = '%s/%s' % (gw_ip_pri, v4_prefixlen) cidr_sec = '%s/%s' % (gw_ip_sec, lib_constants.IPv4_BITS) cidr_v6 = '%s/%s' % (gw_ip6_pri, v6_prefixlen) cidr_v6_sec = '%s/%s' % (gw_ip6_sec, lib_constants.IPv6_BITS) device.delete_addr_and_conntrack_state.assert_has_calls( [mock.call(cidr_pri), mock.call(cidr_sec), mock.call(cidr_v6), mock.call(cidr_v6_sec)]) @mock.patch.object(ip_lib, 'send_ip_addr_adv_notif') class TestAddFloatingIpWithMockGarp(BasicRouterTestCaseFramework): def test_add_floating_ip(self, send_ip_addr_adv_notif): ri = self._create_router() ri._add_fip_addr_to_device = mock.Mock(return_value=True) ip = '15.1.2.3' result = ri.add_floating_ip({'floating_ip_address': ip}, mock.sentinel.interface_name, mock.sentinel.device) ip_lib.send_ip_addr_adv_notif.assert_called_once_with( ri.ns_name, mock.sentinel.interface_name, ip) self.assertEqual(lib_constants.FLOATINGIP_STATUS_ACTIVE, result) def test_add_floating_ip_error(self, send_ip_addr_adv_notif): ri = self._create_router() ri._add_fip_addr_to_device = mock.Mock(return_value=False) result = ri.add_floating_ip({'floating_ip_address': '15.1.2.3'}, mock.sentinel.interface_name, mock.sentinel.device) self.assertFalse(ip_lib.send_ip_addr_adv_notif.called) self.assertEqual(lib_constants.FLOATINGIP_STATUS_ERROR, result) neutron-12.1.1/neutron/tests/unit/agent/l3/test_ha_router.py0000664000175000017500000001337313553660047024160 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import signal import mock from neutron_lib import constants as n_consts from oslo_utils import uuidutils from neutron.agent.l3 import ha_router from neutron.agent.l3 import router_info from neutron.tests import base _uuid = uuidutils.generate_uuid class TestBasicRouterOperations(base.BaseTestCase): def _create_router(self, router=None, **kwargs): if not router: router = mock.MagicMock() self.agent_conf = mock.Mock() self.router_id = _uuid() return ha_router.HaRouter(mock.sentinel.enqueue_state, mock.sentinel.agent, self.router_id, router, self.agent_conf, mock.sentinel.driver, **kwargs) def test_get_router_cidrs_returns_ha_cidrs(self): ri = self._create_router() device = mock.MagicMock() device.name.return_value = 'eth2' addresses = ['15.1.2.2/24', '15.1.2.3/32'] ri._get_cidrs_from_keepalived = mock.MagicMock(return_value=addresses) self.assertEqual(set(addresses), ri.get_router_cidrs(device)) def test__add_default_gw_virtual_route(self): ri = self._create_router() mock_instance = mock.Mock() mock_instance.virtual_routes.gateway_routes = [] ri._get_keepalived_instance = mock.Mock(return_value=mock_instance) subnets = [{'id': _uuid(), 'cidr': '20.0.0.0/24', 'gateway_ip': None}] ex_gw_port = {'fixed_ips': [], 'subnets': subnets, 'extra_subnets': [], 'id': _uuid(), 'network_id': _uuid(), 'mac_address': 'ca:fe:de:ad:be:ef'} # Make sure no exceptional code ri._add_default_gw_virtual_route(ex_gw_port, 'qg-abc') self.assertEqual(0, len(mock_instance.virtual_routes.gateway_routes)) subnets.append({'id': _uuid(), 'cidr': '30.0.0.0/24', 'gateway_ip': '30.0.0.1'}) ri._add_default_gw_virtual_route(ex_gw_port, 'qg-abc') self.assertEqual(1, len(mock_instance.virtual_routes.gateway_routes)) subnets[1]['gateway_ip'] = None ri._add_default_gw_virtual_route(ex_gw_port, 'qg-abc') self.assertEqual(0, len(mock_instance.virtual_routes.gateway_routes)) @mock.patch.object(router_info.RouterInfo, 'remove_floating_ip') def test_remove_floating_ip(self, super_remove_floating_ip): ri = self._create_router(mock.MagicMock()) mock_instance = mock.Mock() ri._get_keepalived_instance = mock.Mock(return_value=mock_instance) device = mock.Mock() fip_cidr = '15.1.2.3/32' ri.remove_floating_ip(device, fip_cidr) self.assertTrue(super_remove_floating_ip.called) def test_destroy_state_change_monitor_ok(self): ri = self._create_router(mock.MagicMock()) # need a port for destroy_state_change_monitor() to call PM code ri.ha_port = {'id': _uuid()} with mock.patch.object(ri, '_get_state_change_monitor_process_manager')\ as m_get_state: mock_pm = m_get_state.return_value mock_pm.active = False ri.destroy_state_change_monitor(mock_pm) mock_pm.disable.assert_called_once_with( sig=str(int(signal.SIGTERM))) def test_destroy_state_change_monitor_force(self): ri = self._create_router(mock.MagicMock()) # need a port for destroy_state_change_monitor() to call PM code ri.ha_port = {'id': _uuid()} with mock.patch.object(ri, '_get_state_change_monitor_process_manager')\ as m_get_state: mock_pm = m_get_state.return_value mock_pm.active = False with mock.patch.object(ha_router, 'SIGTERM_TIMEOUT', 0): ri.destroy_state_change_monitor(mock_pm) calls = ["sig='str(%d)'" % signal.SIGTERM, "sig='str(%d)'" % signal.SIGKILL] mock_pm.disable.has_calls(calls) def test_set_ha_port(self): ri = self._create_router() self.assertIsNone(ri.ha_port) ri.router = {} ri.set_ha_port() self.assertIsNone(ri.ha_port) # HA_INTERFACE_KEY from None to some value ri.router = {n_consts.HA_INTERFACE_KEY: {"id": _uuid(), "status": "DOWN"}} ri.set_ha_port() self.assertIsNotNone(ri.ha_port) self.assertEqual('DOWN', ri.ha_port["status"]) # HA port state change ri.router = {n_consts.HA_INTERFACE_KEY: {"id": _uuid(), "status": "ACTIVE"}} ri.set_ha_port() self.assertIsNotNone(ri.ha_port) self.assertEqual('ACTIVE', ri.ha_port["status"]) ri.router = {} ri.set_ha_port() # neutron server return empty HA_INTERFACE_KEY, but # agent side router info should remain the original value. self.assertIsNotNone(ri.ha_port) neutron-12.1.1/neutron/tests/unit/agent/l3/test_fip_rule_priority_allocator.py0000664000175000017500000000435413553660046027774 0ustar zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.l3 import fip_rule_priority_allocator as frpa from neutron.tests import base class TestFipPriority(base.BaseTestCase): def test__init__(self): test_pr = frpa.FipPriority(10) self.assertEqual(10, test_pr.index) def test__repr__(self): test_pr = frpa.FipPriority(20) self.assertEqual("20", str(test_pr)) def test__eq__(self): left_pr = frpa.FipPriority(10) right_pr = frpa.FipPriority(10) other_pr = frpa.FipPriority(20) self.assertEqual(left_pr, right_pr) self.assertNotEqual(left_pr, other_pr) self.assertNotEqual(right_pr, other_pr) def test__hash__(self): left_pr = frpa.FipPriority(10) right_pr = frpa.FipPriority(10) other_pr = frpa.FipPriority(20) self.assertEqual(hash(left_pr), hash(right_pr)) self.assertNotEqual(hash(left_pr), hash(other_pr)) self.assertNotEqual(hash(other_pr), hash(right_pr)) class TestFipRulePriorityAllocator(base.BaseTestCase): def setUp(self): super(TestFipRulePriorityAllocator, self).setUp() self.priority_rule_start = 100 self.priority_rule_end = 200 self.data_store_path = '/data_store_path_test' def test__init__(self): _frpa = frpa.FipRulePriorityAllocator(self.data_store_path, self.priority_rule_start, self.priority_rule_end) self.assertEqual(self.data_store_path, _frpa.state_file) self.assertEqual(frpa.FipPriority, _frpa.ItemClass) self.assertEqual(100, len(_frpa.pool)) neutron-12.1.1/neutron/tests/unit/agent/l3/test_dvr_snat_ns.py0000664000175000017500000000413613553660047024505 0ustar zuulzuul00000000000000# Copyright (c) 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg from oslo_utils import uuidutils from neutron.agent.common import utils from neutron.agent.l3 import dvr_snat_ns from neutron.agent.linux import ip_lib from neutron.tests import base _uuid = uuidutils.generate_uuid class TestDvrSnatNs(base.BaseTestCase): def setUp(self): super(TestDvrSnatNs, self).setUp() self.conf = mock.Mock() self.conf.state_path = cfg.CONF.state_path self.driver = mock.Mock() self.driver.DEV_NAME_LEN = 14 self.router_id = _uuid() self.snat_ns = dvr_snat_ns.SnatNamespace(self.router_id, self.conf, self.driver, use_ipv6=False) @mock.patch.object(utils, 'execute') @mock.patch.object(ip_lib, 'create_network_namespace') @mock.patch.object(ip_lib, 'network_namespace_exists') def test_create(self, exists, create, execute): exists.return_value = False self.snat_ns.create() netns_cmd = ['ip', 'netns', 'exec', self.snat_ns.name] loose_cmd = ['sysctl', '-w', 'net.netfilter.nf_conntrack_tcp_loose=0'] expected = [mock.call(netns_cmd + loose_cmd, check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True)] create.assert_called_once_with(self.snat_ns.name) execute.assert_has_calls(expected) neutron-12.1.1/neutron/tests/unit/agent/l3/test_dvr_fip_ns.py0000664000175000017500000003774213553660047024327 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import mock from oslo_config import cfg from oslo_utils import uuidutils from neutron.agent.common import utils from neutron.agent.l3 import dvr_fip_ns from neutron.agent.l3 import link_local_allocator as lla from neutron.agent.l3 import router_info from neutron.agent.linux import ip_lib from neutron.agent.linux import iptables_manager from neutron.common import exceptions as n_exc from neutron.common import utils as n_utils from neutron.tests import base _uuid = uuidutils.generate_uuid class TestDvrFipNs(base.BaseTestCase): def setUp(self): super(TestDvrFipNs, self).setUp() self.conf = mock.Mock() self.conf.state_path = cfg.CONF.state_path self.driver = mock.Mock() self.driver.DEV_NAME_LEN = 14 self.net_id = _uuid() self.fip_ns = dvr_fip_ns.FipNamespace(self.net_id, self.conf, self.driver, use_ipv6=True) def test_subscribe(self): is_first = self.fip_ns.subscribe(mock.sentinel.external_net_id) self.assertTrue(is_first) def test_subscribe_not_first(self): self.fip_ns.subscribe(mock.sentinel.external_net_id) is_first = self.fip_ns.subscribe(mock.sentinel.external_net_id2) self.assertFalse(is_first) def test_unsubscribe(self): self.fip_ns.subscribe(mock.sentinel.external_net_id) is_last = self.fip_ns.unsubscribe(mock.sentinel.external_net_id) self.assertTrue(is_last) def test_unsubscribe_not_last(self): self.fip_ns.subscribe(mock.sentinel.external_net_id) self.fip_ns.subscribe(mock.sentinel.external_net_id2) is_last = self.fip_ns.unsubscribe(mock.sentinel.external_net_id2) self.assertFalse(is_last) def test_allocate_rule_priority(self): pr = self.fip_ns.allocate_rule_priority('20.0.0.30') self.assertIn('20.0.0.30', self.fip_ns._rule_priorities.allocations) self.assertNotIn(pr, self.fip_ns._rule_priorities.pool) def test_deallocate_rule_priority(self): pr = self.fip_ns.allocate_rule_priority('20.0.0.30') self.fip_ns.deallocate_rule_priority('20.0.0.30') self.assertNotIn('20.0.0.30', self.fip_ns._rule_priorities.allocations) self.assertIn(pr, self.fip_ns._rule_priorities.pool) def _get_agent_gw_port(self): v4_subnet_id = _uuid() v6_subnet_id = _uuid() agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', 'prefixlen': 24, 'subnet_id': v4_subnet_id}, {'ip_address': 'cafe:dead:beef::3', 'prefixlen': 64, 'subnet_id': v6_subnet_id}], 'subnets': [{'id': v4_subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}, {'id': v6_subnet_id, 'cidr': 'cafe:dead:beef::/64', 'gateway_ip': 'cafe:dead:beef::1'}], 'id': _uuid(), 'network_id': self.net_id, 'mac_address': 'ca:fe:de:ad:be:ef'} return agent_gw_port @mock.patch.object(ip_lib, 'IPWrapper') @mock.patch.object(ip_lib, 'device_exists') @mock.patch.object(dvr_fip_ns.FipNamespace, 'create') def test_create_gateway_port(self, fip_create, device_exists, ip_wrapper): agent_gw_port = self._get_agent_gw_port() device_exists.return_value = False with mock.patch.object(self.fip_ns.driver, 'set_onlink_routes') as \ mock_set_onlink_routes: self.fip_ns.create_or_update_gateway_port(agent_gw_port) self.assertTrue(fip_create.called) self.assertEqual(1, self.driver.plug.call_count) ext_net_bridge = self.conf.external_network_bridge if ext_net_bridge: self.assertEqual(1, self.driver.remove_vlan_tag.call_count) self.assertEqual(1, self.driver.init_l3.call_count) interface_name = self.fip_ns.get_ext_device_name(agent_gw_port['id']) gw_cidrs = [sn['cidr'] for sn in agent_gw_port['subnets'] if sn.get('cidr')] mock_set_onlink_routes.assert_called_once_with( interface_name, self.fip_ns.name, [], preserve_ips=gw_cidrs, is_ipv6=False) @mock.patch.object(ip_lib, 'IPDevice') @mock.patch.object(ip_lib, 'send_ip_addr_adv_notif') @mock.patch.object(dvr_fip_ns.FipNamespace, 'subscribe') @mock.patch.object(dvr_fip_ns.FipNamespace, '_add_default_gateway_for_fip') def test_update_gateway_port( self, def_gw, fip_sub, send_adv_notif, IPDevice): fip_sub.return_value = False self.fip_ns._check_for_gateway_ip_change = mock.Mock(return_value=True) agent_gw_port = self._get_agent_gw_port() interface_name = self.fip_ns.get_ext_device_name(agent_gw_port['id']) self.fip_ns.agent_gateway_port = agent_gw_port with mock.patch.object(self.fip_ns.driver, 'set_onlink_routes'): self.fip_ns.create_or_update_gateway_port(agent_gw_port) expected = [ mock.call(self.fip_ns.get_name(), interface_name, agent_gw_port['fixed_ips'][0]['ip_address']), mock.call(self.fip_ns.get_name(), interface_name, agent_gw_port['fixed_ips'][1]['ip_address'])] send_adv_notif.assert_has_calls(expected) self.assertTrue(def_gw.called) @mock.patch.object(ip_lib.IPDevice, 'exists') @mock.patch.object(dvr_fip_ns.FipNamespace, 'subscribe') @mock.patch.object(dvr_fip_ns.FipNamespace, 'delete') @mock.patch.object(dvr_fip_ns.FipNamespace, 'unsubscribe') def test_update_gateway_port_raises_exception( self, fip_unsub, fip_delete, fip_sub, exists): agent_gw_port = self._get_agent_gw_port() self.fip_ns._create_gateway_port = mock.Mock() self.fip_ns.create_or_update_gateway_port(agent_gw_port) exists.return_value = False fip_sub.return_value = False self.fip_ns._check_for_gateway_ip_change = mock.Mock(return_value=True) self.fip_ns.agent_gateway_port = agent_gw_port self.assertRaises(n_exc.FloatingIpSetupException, self.fip_ns.create_or_update_gateway_port, agent_gw_port) self.assertTrue(fip_unsub.called) self.assertTrue(fip_delete.called) @mock.patch.object(ip_lib, 'IPDevice') @mock.patch.object(ip_lib, 'send_ip_addr_adv_notif') @mock.patch.object(dvr_fip_ns.FipNamespace, 'subscribe') @mock.patch.object(dvr_fip_ns.FipNamespace, '_add_default_gateway_for_fip') def test_update_gateway_port_gateway_outside_subnet_added( self, def_gw, fip_sub, send_adv_notif, IPDevice): fip_sub.return_value = False self.fip_ns.agent_gateway_port = None agent_gw_port = self._get_agent_gw_port() agent_gw_port['subnets'][0]['gateway_ip'] = '20.0.1.1' self.fip_ns._check_for_gateway_ip_change = mock.Mock(return_value=True) self.fip_ns.agent_gateway_port = agent_gw_port with mock.patch.object(self.fip_ns.driver, 'set_onlink_routes'): self.fip_ns.create_or_update_gateway_port(agent_gw_port) IPDevice().route.add_route.assert_called_once_with('20.0.1.1', scope='link') self.assertTrue(def_gw.called) def test_check_gateway_ip_changed_no_change(self): agent_gw_port = self._get_agent_gw_port() self.fip_ns.agent_gateway_port = copy.deepcopy(agent_gw_port) agent_gw_port['mac_address'] = 'aa:bb:cc:dd:ee:ff' self.assertFalse(self.fip_ns._check_for_gateway_ip_change( agent_gw_port)) def test_check_gateway_ip_changed_v4(self): agent_gw_port = self._get_agent_gw_port() self.fip_ns.agent_gateway_port = copy.deepcopy(agent_gw_port) agent_gw_port['subnets'][0]['gateway_ip'] = '20.0.0.2' self.assertTrue(self.fip_ns._check_for_gateway_ip_change( agent_gw_port)) def test_check_gateway_ip_changed_v6(self): agent_gw_port = self._get_agent_gw_port() self.fip_ns.agent_gateway_port = copy.deepcopy(agent_gw_port) agent_gw_port['subnets'][1]['gateway_ip'] = 'cafe:dead:beef::2' self.assertTrue(self.fip_ns._check_for_gateway_ip_change( agent_gw_port)) @mock.patch.object(iptables_manager, 'IptablesManager') @mock.patch.object(utils, 'execute') @mock.patch.object(ip_lib.IpNetnsCommand, 'exists') def _test_create(self, old_kernel, exists, execute, IPTables): exists.return_value = True # There are up to six sysctl calls - two to enable forwarding, # two for arp_ignore and arp_announce, and two for ip_nonlocal_bind execute.side_effect = [None, None, None, None, RuntimeError if old_kernel else None, None] self.fip_ns._iptables_manager = IPTables() self.fip_ns.create() ns_name = self.fip_ns.get_name() netns_cmd = ['ip', 'netns', 'exec', ns_name] bind_cmd = ['sysctl', '-w', 'net.ipv4.ip_nonlocal_bind=1'] expected = [mock.call(netns_cmd + bind_cmd, check_exit_code=True, extra_ok_codes=None, log_fail_as_error=False, run_as_root=True)] if old_kernel: expected.append(mock.call(bind_cmd, check_exit_code=True, extra_ok_codes=None, log_fail_as_error=True, run_as_root=True)) execute.assert_has_calls(expected) def test_create_old_kernel(self): self._test_create(True) def test_create_new_kernel(self): self._test_create(False) @mock.patch.object(ip_lib, 'IPWrapper') def test_destroy(self, IPWrapper): ip_wrapper = IPWrapper() dev1 = mock.Mock() dev1.name = 'fpr-aaaa' dev2 = mock.Mock() dev2.name = 'fg-aaaa' ip_wrapper.get_devices.return_value = [dev1, dev2] with mock.patch.object(self.fip_ns.ip_wrapper_root.netns, 'delete') as delete,\ mock.patch.object(self.fip_ns.ip_wrapper_root.netns, 'exists', return_value=True) as exists: self.fip_ns.delete() exists.assert_called_once_with(self.fip_ns.name) delete.assert_called_once_with(self.fip_ns.name) ext_net_bridge = self.conf.external_network_bridge ns_name = self.fip_ns.get_name() self.driver.unplug.assert_called_once_with('fg-aaaa', bridge=ext_net_bridge, prefix='fg-', namespace=ns_name) ip_wrapper.del_veth.assert_called_once_with('fpr-aaaa') def test_destroy_no_namespace(self): with mock.patch.object(self.fip_ns.ip_wrapper_root.netns, 'delete') as delete,\ mock.patch.object(self.fip_ns.ip_wrapper_root.netns, 'exists', return_value=False) as exists: self.fip_ns.delete() exists.assert_called_once_with(self.fip_ns.name) self.assertFalse(delete.called) @mock.patch.object(ip_lib, 'IPWrapper') @mock.patch.object(ip_lib, 'IPDevice') def _test_create_rtr_2_fip_link(self, dev_exists, addr_exists, IPDevice, IPWrapper): ri = mock.Mock() ri.router_id = _uuid() ri.rtr_fip_subnet = None ri.ns_name = mock.sentinel.router_ns ri.get_ex_gw_port.return_value = {'mtu': 2000} rtr_2_fip_name = self.fip_ns.get_rtr_ext_device_name(ri.router_id) fip_2_rtr_name = self.fip_ns.get_int_device_name(ri.router_id) fip_ns_name = self.fip_ns.get_name() self.fip_ns.local_subnets = allocator = mock.Mock() pair = lla.LinkLocalAddressPair('169.254.31.28/31') allocator.allocate.return_value = pair addr_pair = pair.get_pair() ip_wrapper = IPWrapper() ip_wrapper.add_veth.return_value = (IPDevice(), IPDevice()) device = IPDevice() device.exists.return_value = dev_exists device.addr.list.return_value = addr_exists ri._get_snat_idx = mock.Mock() self.fip_ns._add_rtr_ext_route_rule_to_route_table = mock.Mock() self.fip_ns.create_rtr_2_fip_link(ri) if not dev_exists: ip_wrapper.add_veth.assert_called_with(rtr_2_fip_name, fip_2_rtr_name, fip_ns_name) self.assertEqual(2, device.link.set_up.call_count) device.link.set_mtu.assert_called_with(2000) self.assertEqual(2, device.link.set_mtu.call_count) if not addr_exists: expected = [mock.call(str(addr_pair[0]), add_broadcast=False), mock.call(str(addr_pair[1]), add_broadcast=False)] device.addr.add.assert_has_calls(expected) self.assertEqual(2, device.addr.add.call_count) expected = [mock.call(n_utils.cidr_to_ip(addr_pair[1]), mock.ANY), mock.call(n_utils.cidr_to_ip(addr_pair[0]), mock.ANY)] device.neigh.add.assert_has_calls(expected) self.assertEqual(2, device.neigh.add.call_count) device.route.add_gateway.assert_called_once_with( '169.254.31.29', table=16) self.assertTrue( self.fip_ns._add_rtr_ext_route_rule_to_route_table.called) def test_create_rtr_2_fip_link(self): self._test_create_rtr_2_fip_link(False, False) def test_create_rtr_2_fip_link_already_exists(self): self._test_create_rtr_2_fip_link(True, False) def test_create_rtr_2_fip_link_and_addr_already_exist(self): self._test_create_rtr_2_fip_link(True, True) @mock.patch.object(router_info.RouterInfo, 'get_router_cidrs') @mock.patch.object(ip_lib, 'IPDevice') def _test_scan_fip_ports(self, ri, ip_list, stale_list, IPDevice, get_router_cidrs): IPDevice.return_value = device = mock.Mock() device.exists.return_value = True ri.get_router_cidrs.return_value = ip_list get_router_cidrs.return_value = stale_list self.fip_ns.get_rtr_ext_device_name = mock.Mock( return_value=mock.sentinel.rtr_ext_device_name) self.fip_ns.scan_fip_ports(ri) if stale_list: device.delete_addr_and_conntrack_state.assert_called_once_with( stale_list[0]) def test_scan_fip_ports_restart_fips(self): ri = mock.Mock() ri.floating_ips_dict = {} ip_list = [{'cidr': '111.2.3.4'}, {'cidr': '111.2.3.5'}] stale_list = ['111.2.3.7/32'] self._test_scan_fip_ports(ri, ip_list, stale_list) self.assertTrue(ri.rtr_fip_connect) def test_scan_fip_ports_restart_none(self): ri = mock.Mock() ri.floating_ips_dict = {} ri.rtr_fip_connect = False self._test_scan_fip_ports(ri, [], []) self.assertFalse(ri.rtr_fip_connect) neutron-12.1.1/neutron/tests/unit/agent/l3/extensions/0000775000175000017500000000000013553660156022750 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/l3/extensions/__init__.py0000664000175000017500000000000013553660046025045 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/l3/extensions/test_fip_qos.py0000664000175000017500000004603113553660047026024 0ustar zuulzuul00000000000000# Copyright 2017 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as lib_const from neutron_lib import context from neutron_lib.services.qos import constants as qos_consts from oslo_utils import uuidutils from neutron.agent.l3 import agent as l3_agent from neutron.agent.l3.extensions import fip_qos from neutron.agent.l3 import l3_agent_extension_api as l3_ext_api from neutron.agent.l3 import router_info as l3router from neutron.api.rpc.callbacks.consumer import registry from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc from neutron.objects.qos import policy from neutron.objects.qos import rule from neutron.tests import base from neutron.tests.unit.agent.l3 import test_agent _uuid = uuidutils.generate_uuid TEST_POLICY = policy.QosPolicy(context=None, name='test1', id=_uuid()) TEST_POLICY2 = policy.QosPolicy(context=None, name='test2', id=_uuid()) TEST_QOS_FIP = "3.3.3.3" TEST_FIP = "1.1.1.1" TEST_FIP2 = "2.2.2.2" HOSTNAME = 'myhost' class QosExtensionBaseTestCase(test_agent.BasicRouterOperationsFramework): def setUp(self): super(QosExtensionBaseTestCase, self).setUp() self.fip_qos_ext = fip_qos.FipQosAgentExtension() self.context = context.get_admin_context() self.connection = mock.Mock() self.policy = policy.QosPolicy(context=None, name='test1', id=_uuid()) self.ingress_rule = ( rule.QosBandwidthLimitRule(context=None, id=_uuid(), qos_policy_id=self.policy.id, max_kbps=1111, max_burst_kbps=2222, direction=lib_const.INGRESS_DIRECTION)) self.egress_rule = ( rule.QosBandwidthLimitRule(context=None, id=_uuid(), qos_policy_id=self.policy.id, max_kbps=3333, max_burst_kbps=4444, direction=lib_const.EGRESS_DIRECTION)) self.policy.rules = [self.ingress_rule, self.egress_rule] self.new_ingress_rule = ( rule.QosBandwidthLimitRule(context=None, id=_uuid(), qos_policy_id=self.policy.id, max_kbps=5555, max_burst_kbps=6666, direction=lib_const.INGRESS_DIRECTION)) self.ingress_rule_only_has_max_kbps = ( rule.QosBandwidthLimitRule(context=None, id=_uuid(), qos_policy_id=self.policy.id, max_kbps=5555, max_burst_kbps=0, direction=lib_const.INGRESS_DIRECTION)) self.policy2 = policy.QosPolicy(context=None, name='test2', id=_uuid()) self.policy2.rules = [self.ingress_rule] self.policy3 = policy.QosPolicy(context=None, name='test3', id=_uuid()) self.policy3.rules = [self.egress_rule] self.policy4 = policy.QosPolicy(context=None, name='test4', id=_uuid()) self.dscp = rule.QosDscpMarkingRule(context=None, id=_uuid(), qos_policy_id=self.policy4.id, dscp_mark=32) self.dscp.obj_reset_changes() self.policy4.rules = [self.dscp] self.qos_policies = {self.policy.id: self.policy, self.policy2.id: self.policy2, self.policy3.id: self.policy3, self.policy4.id: self.policy4} self.agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.ex_gw_port = {'id': _uuid()} self.fip = {'id': _uuid(), 'floating_ip_address': TEST_QOS_FIP, 'fixed_ip_address': '192.168.0.1', 'floating_network_id': _uuid(), 'port_id': _uuid(), 'host': HOSTNAME, 'qos_policy_id': self.policy.id} self.router = {'id': _uuid(), 'gw_port': self.ex_gw_port, 'ha': False, 'distributed': False, lib_const.FLOATINGIP_KEY: [self.fip]} self.router_info = l3router.RouterInfo(self.agent, _uuid(), self.router, **self.ri_kwargs) self.router_info.ex_gw_port = self.ex_gw_port self.agent.router_info[self.router['id']] = self.router_info def _mock_get_router_info(router_id): return self.router_info self.get_router_info = mock.patch( 'neutron.agent.l3.l3_agent_extension_api.' 'L3AgentExtensionAPI.get_router_info').start() self.get_router_info.side_effect = _mock_get_router_info self.agent_api = l3_ext_api.L3AgentExtensionAPI(None) self.fip_qos_ext.consume_api(self.agent_api) class FipQosExtensionInitializeTestCase(QosExtensionBaseTestCase): @mock.patch.object(registry, 'register') @mock.patch.object(resources_rpc, 'ResourcesPushRpcCallback') def test_initialize_subscribed_to_rpc(self, rpc_mock, subscribe_mock): call_to_patch = 'neutron.common.rpc.create_connection' with mock.patch(call_to_patch, return_value=self.connection) as create_connection: self.fip_qos_ext.initialize( self.connection, lib_const.L3_AGENT_MODE) create_connection.assert_has_calls([mock.call()]) self.connection.create_consumer.assert_has_calls( [mock.call( resources_rpc.resource_type_versioned_topic( resources.QOS_POLICY), [rpc_mock()], fanout=True)] ) subscribe_mock.assert_called_with(mock.ANY, resources.QOS_POLICY) class FipQosExtensionTestCase(QosExtensionBaseTestCase): def setUp(self): super(FipQosExtensionTestCase, self).setUp() self.fip_qos_ext.initialize( self.connection, lib_const.L3_AGENT_MODE) self._set_pull_mock() def _set_pull_mock(self): def _pull_mock(context, resource_type, resource_id): return self.qos_policies[resource_id] self.pull = mock.patch( 'neutron.api.rpc.handlers.resources_rpc.' 'ResourcesPullRpcApi.pull').start() self.pull.side_effect = _pull_mock def _test_new_fip_add(self, func): tc_wrapper = mock.Mock() with mock.patch.object(self.fip_qos_ext, '_get_tc_wrapper', return_value=tc_wrapper): func(self.context, self.router) tc_wrapper.set_ip_rate_limit.assert_has_calls( [mock.call(lib_const.INGRESS_DIRECTION, TEST_QOS_FIP, 1111, 2222), mock.call(lib_const.EGRESS_DIRECTION, TEST_QOS_FIP, 3333, 4444)], any_order=True) def test_add_router(self): self._test_new_fip_add(self.fip_qos_ext.add_router) def test_update_router(self): self._test_new_fip_add(self.fip_qos_ext.update_router) def test_update_router_fip_policy_changed(self): tc_wrapper = mock.Mock() with mock.patch.object(self.fip_qos_ext, '_get_tc_wrapper', return_value=tc_wrapper): self.fip_qos_ext.update_router(self.context, self.router) tc_wrapper.set_ip_rate_limit.assert_has_calls( [mock.call(lib_const.INGRESS_DIRECTION, TEST_QOS_FIP, 1111, 2222), mock.call(lib_const.EGRESS_DIRECTION, TEST_QOS_FIP, 3333, 4444)], any_order=True) # the policy of floating IP has been changed to # which only has one egress rule self.fip[qos_consts.QOS_POLICY_ID] = self.policy3.id self.fip_qos_ext.update_router(self.context, self.router) tc_wrapper.clear_ip_rate_limit.assert_has_calls( [mock.call(lib_const.INGRESS_DIRECTION, TEST_QOS_FIP)]) def test_update_router_fip_policy_changed_to_none(self): tc_wrapper = mock.Mock() with mock.patch.object(self.fip_qos_ext, '_get_tc_wrapper', return_value=tc_wrapper): self.fip_qos_ext.update_router(self.context, self.router) tc_wrapper.set_ip_rate_limit.assert_has_calls( [mock.call(lib_const.INGRESS_DIRECTION, TEST_QOS_FIP, 1111, 2222), mock.call(lib_const.EGRESS_DIRECTION, TEST_QOS_FIP, 3333, 4444)], any_order=True) # floating IP remove the qos_policy bonding self.fip[qos_consts.QOS_POLICY_ID] = None self.fip_qos_ext.update_router(self.context, self.router) tc_wrapper.clear_ip_rate_limit.assert_has_calls( [mock.call(lib_const.INGRESS_DIRECTION, TEST_QOS_FIP), mock.call(lib_const.EGRESS_DIRECTION, TEST_QOS_FIP)], any_order=True) def test__process_update_policy(self): tc_wrapper = mock.Mock() with mock.patch.object(self.fip_qos_ext, '_get_tc_wrapper', return_value=tc_wrapper): self.fip_qos_ext.update_router(self.context, self.router) tc_wrapper.set_ip_rate_limit.assert_has_calls( [mock.call(lib_const.INGRESS_DIRECTION, TEST_QOS_FIP, 1111, 2222), mock.call(lib_const.EGRESS_DIRECTION, TEST_QOS_FIP, 3333, 4444)], any_order=True) # the rules of floating IP policy has been changed self.fip_qos_ext._policy_rules_modified = mock.Mock( return_value=True) self.policy.rules = [self.new_ingress_rule, self.egress_rule] self.fip_qos_ext._process_update_policy(self.policy) tc_wrapper.set_ip_rate_limit.assert_has_calls( [mock.call(lib_const.INGRESS_DIRECTION, TEST_QOS_FIP, 5555, 6666)]) def _test_qos_policy_scenarios(self, fip_removed=True, qos_rules_removed=False): tc_wrapper = mock.Mock() with mock.patch.object(self.fip_qos_ext, '_get_tc_wrapper', return_value=tc_wrapper): self.fip_qos_ext.update_router(self.context, self.router) tc_wrapper.set_ip_rate_limit.assert_has_calls( [mock.call(lib_const.INGRESS_DIRECTION, TEST_QOS_FIP, 1111, 2222), mock.call(lib_const.EGRESS_DIRECTION, TEST_QOS_FIP, 3333, 4444)], any_order=True) if fip_removed: # floating IP dissociated, then it does not belong to # this router self.router[lib_const.FLOATINGIP_KEY] = [] if qos_rules_removed: self.policy.rules = [] self.fip_qos_ext.update_router(self.context, self.router) tc_wrapper.clear_ip_rate_limit.assert_has_calls( [mock.call(lib_const.INGRESS_DIRECTION, TEST_QOS_FIP), mock.call(lib_const.EGRESS_DIRECTION, TEST_QOS_FIP)], any_order=True) def test_update_router_fip_removed(self): self._test_qos_policy_scenarios() def test_fip_qos_changed_to_none(self): self._test_qos_policy_scenarios(qos_rules_removed=True) def _test_only_one_direction_rule(self, func, policy, direction): tc_wrapper = mock.Mock() with mock.patch.object( self.fip_qos_ext.resource_rpc, 'pull', return_value=policy): with mock.patch.object(self.fip_qos_ext, '_get_tc_wrapper', return_value=tc_wrapper): func(self.context, self.router) if direction == lib_const.INGRESS_DIRECTION: calls = [mock.call(lib_const.INGRESS_DIRECTION, TEST_QOS_FIP, 1111, 2222)] else: calls = [mock.call(lib_const.EGRESS_DIRECTION, TEST_QOS_FIP, 3333, 4444)] tc_wrapper.set_ip_rate_limit.assert_has_calls(calls) def test_add_router_only_ingress(self): self._test_only_one_direction_rule(self.fip_qos_ext.add_router, self.policy2, lib_const.INGRESS_DIRECTION) def test_add_router_only_egress(self): self._test_only_one_direction_rule(self.fip_qos_ext.add_router, self.policy3, lib_const.EGRESS_DIRECTION) def test_update_router_only_ingress(self): self._test_only_one_direction_rule(self.fip_qos_ext.add_router, self.policy2, lib_const.INGRESS_DIRECTION) def test_update_router_only_egress(self): self._test_only_one_direction_rule(self.fip_qos_ext.add_router, self.policy3, lib_const.EGRESS_DIRECTION) def test_rule_only_has_max_kbps(self): tc_wrapper = mock.Mock() with mock.patch.object(self.fip_qos_ext, '_get_tc_wrapper', return_value=tc_wrapper): self.fip_qos_ext.update_router(self.context, self.router) tc_wrapper.set_ip_rate_limit.assert_has_calls( [mock.call(lib_const.INGRESS_DIRECTION, TEST_QOS_FIP, 1111, 2222), mock.call(lib_const.EGRESS_DIRECTION, TEST_QOS_FIP, 3333, 4444)], any_order=True) # policy ingress rule changed to only has one max_kbps value self.policy.rules = [self.ingress_rule_only_has_max_kbps, self.egress_rule] self.fip_qos_ext.update_router(self.context, self.router) tc_wrapper.set_ip_rate_limit.assert_has_calls( [mock.call(lib_const.INGRESS_DIRECTION, TEST_QOS_FIP, 5555, 0)]) def test_qos_policy_has_no_bandwidth_limit_rule(self): tc_wrapper = mock.Mock() with mock.patch.object(self.fip_qos_ext, '_get_tc_wrapper', return_value=tc_wrapper): self.fip['qos_policy_id'] = self.policy4.id self.fip_qos_ext.add_router(self.context, self.router) tc_wrapper.set_ip_rate_limit.assert_not_called() def _test_process_ip_rates(self, with_cache): rates = {'egress': {'rate': 333, 'burst': 444}, 'ingress': {'rate': 111, 'burst': 222}} fip = '123.123.123.123' device = mock.Mock() tc_wrapper = mock.Mock() with mock.patch.object( self.fip_qos_ext, '_get_tc_wrapper', return_value=tc_wrapper) as get_tc_wrapper: with mock.patch.object( self.fip_qos_ext, 'process_ip_rate_limit') as process_ip: self.fip_qos_ext.process_ip_rates( fip, device, rates, with_cache=with_cache) if with_cache: self.assertEqual(2, process_ip.call_count) else: self.assertEqual(2, get_tc_wrapper.call_count) self.assertEqual( 2, tc_wrapper.set_ip_rate_limit.call_count) def test_process_ip_rates_with_cache(self): self._test_process_ip_rates(with_cache=True) def test_process_ip_rates_without_cache(self): self._test_process_ip_rates(with_cache=False) class RouterFipRateLimitMapsTestCase(base.BaseTestCase): def setUp(self): super(RouterFipRateLimitMapsTestCase, self).setUp() self.policy_map = fip_qos.RouterFipRateLimitMaps() def test_update_policy(self): self.policy_map.update_policy(TEST_POLICY) self.assertEqual(TEST_POLICY, self.policy_map.known_policies[TEST_POLICY.id]) def _set_fips(self): self.policy_map.set_fip_policy(TEST_FIP, TEST_POLICY) self.policy_map.set_fip_policy(TEST_FIP2, TEST_POLICY2) def test_set_fip_policy(self): self._set_fips() self.assertEqual(TEST_POLICY, self.policy_map.known_policies[TEST_POLICY.id]) self.assertIn(TEST_FIP, self.policy_map.qos_policy_fips[TEST_POLICY.id]) def test_get_fip_policy(self): self._set_fips() self.assertEqual(TEST_POLICY, self.policy_map.get_fip_policy(TEST_FIP)) self.assertEqual(TEST_POLICY2, self.policy_map.get_fip_policy(TEST_FIP2)) def test_get_fips(self): self._set_fips() self.assertEqual([TEST_FIP], list(self.policy_map.get_fips(TEST_POLICY))) self.assertEqual([TEST_FIP2], list(self.policy_map.get_fips(TEST_POLICY2))) def test_clean_by_fip(self): self._set_fips() self.policy_map.clean_by_fip(TEST_FIP) self.assertNotIn(TEST_POLICY.id, self.policy_map.known_policies) self.assertNotIn(TEST_FIP, self.policy_map.fip_policies) self.assertIn(TEST_POLICY2.id, self.policy_map.known_policies) def test_clean_by_fip_for_unknown_fip(self): self.policy_map._clean_policy_info = mock.Mock() self.policy_map.clean_by_fip(TEST_FIP) self.policy_map._clean_policy_info.assert_not_called() def test_find_fip_router_id(self): router_id = _uuid() self.policy_map.router_floating_ips[router_id] = set([TEST_FIP, TEST_FIP2]) self.assertIsNone(self.policy_map.find_fip_router_id("8.8.8.8")) self.assertEqual(router_id, self.policy_map.find_fip_router_id(TEST_FIP)) neutron-12.1.1/neutron/tests/unit/agent/l3/test_link_local_allocator.py0000664000175000017500000000213013553660046026323 0ustar zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron.agent.l3 import link_local_allocator as lla from neutron.tests import base class TestLinkLocalAddrAllocator(base.BaseTestCase): def setUp(self): super(TestLinkLocalAddrAllocator, self).setUp() self.subnet = netaddr.IPNetwork('169.254.31.0/24') def test__init__(self): a = lla.LinkLocalAllocator('/file', self.subnet.cidr) self.assertEqual('/file', a.state_file) self.assertEqual({}, a.allocations) neutron-12.1.1/neutron/tests/unit/agent/l3/test_item_allocator.py0000664000175000017500000001321613553660046025161 0ustar zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.agent.l3 import item_allocator as ia from neutron.tests import base class TestObject(object): def __init__(self, value): super(TestObject, self).__init__() self._value = value def __str__(self): return str(self._value) class TestItemAllocator(base.BaseTestCase): def test__init__(self): test_pool = set(TestObject(s) for s in range(32768, 40000)) with mock.patch.object(ia.ItemAllocator, '_write') as write: a = ia.ItemAllocator('/file', TestObject, test_pool) test_object = a.allocate('test') self.assertIn('test', a.allocations) self.assertIn(test_object, a.allocations.values()) self.assertNotIn(test_object, a.pool) self.assertTrue(write.called) def test__init__readfile(self): test_pool = set(TestObject(s) for s in range(32768, 40000)) with mock.patch.object(ia.ItemAllocator, '_read') as read: read.return_value = ["da873ca2,10\n"] a = ia.ItemAllocator('/file', TestObject, test_pool) self.assertIn('da873ca2', a.remembered) self.assertEqual({}, a.allocations) def test__init__readfile_error(self): test_pool = set(TestObject(s) for s in range(32768, 40000)) with mock.patch.object(ia.ItemAllocator, '_read') as read,\ mock.patch.object(ia.ItemAllocator, '_write') as write: read.return_value = ["da873ca2,10\n", "corrupt_entry_no_delimiter\n", "42c9daf7,11\n"] a = ia.ItemAllocator('/file', TestObject, test_pool) self.assertIn('da873ca2', a.remembered) self.assertIn('42c9daf7', a.remembered) self.assertNotIn('corrupt_entry_no_delimiter', a.remembered) self.assertEqual({}, a.allocations) self.assertTrue(write.called) def test_allocate_and_lookup(self): test_pool = set([TestObject(33000), TestObject(33001)]) a = ia.ItemAllocator('/file', TestObject, test_pool) with mock.patch.object(ia.ItemAllocator, '_write') as write: test_object = a.allocate('test') # a lookup should find the same object lookup_object = a.lookup('test') self.assertIn('test', a.allocations) self.assertIn(test_object, a.allocations.values()) self.assertNotIn(test_object, a.pool) self.assertTrue(write.called) self.assertEqual(test_object, lookup_object) def test_allocate_repeated_call_with_same_key(self): test_pool = set([TestObject(33000), TestObject(33001), TestObject(33002), TestObject(33003), TestObject(33004), TestObject(33005)]) a = ia.ItemAllocator('/file', TestObject, test_pool) with mock.patch.object(ia.ItemAllocator, '_write'): test_object = a.allocate('test') test_object1 = a.allocate('test') test_object2 = a.allocate('test') test_object3 = a.allocate('test1') # same value for same key on repeated calls self.assertEqual(test_object, test_object1) self.assertEqual(test_object1, test_object2) # values for different keys should be diffent self.assertNotEqual(test_object, test_object3) def test_allocate_from_file(self): test_pool = set([TestObject(33000), TestObject(33001)]) with mock.patch.object(ia.ItemAllocator, '_read') as read: read.return_value = ["deadbeef,33000\n"] a = ia.ItemAllocator('/file', TestObject, test_pool) with mock.patch.object(ia.ItemAllocator, '_write') as write: t_obj = a.allocate('deadbeef') self.assertEqual('33000', t_obj._value) self.assertIn('deadbeef', a.allocations) self.assertIn(t_obj, a.allocations.values()) self.assertNotIn(33000, a.pool) self.assertFalse(write.called) def test_allocate_exhausted_pool(self): test_pool = set([TestObject(33000)]) with mock.patch.object(ia.ItemAllocator, '_read') as read: read.return_value = ["deadbeef,33000\n"] a = ia.ItemAllocator('/file', TestObject, test_pool) with mock.patch.object(ia.ItemAllocator, '_write') as write: allocation = a.allocate('abcdef12') self.assertNotIn('deadbeef', a.allocations) self.assertNotIn(allocation, a.pool) self.assertTrue(write.called) def test_release(self): test_pool = set([TestObject(33000), TestObject(33001)]) with mock.patch.object(ia.ItemAllocator, '_write') as write: a = ia.ItemAllocator('/file', TestObject, test_pool) allocation = a.allocate('deadbeef') write.reset_mock() a.release('deadbeef') # Just try to release the item again to see if it # throws any error a.release('deadbeef') self.assertNotIn('deadbeef', a.allocations) self.assertIn(allocation, a.pool) self.assertEqual({}, a.allocations) write.assert_called_once_with([]) neutron-12.1.1/neutron/tests/unit/agent/l3/__init__.py0000664000175000017500000000000013553660046022646 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/agent/l3/test_namespace_manager.py0000664000175000017500000001122113553660046025603 0ustar zuulzuul00000000000000# Copyright (c) 2015 Rackspace # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_utils import uuidutils from neutron.agent.l3 import dvr_fip_ns from neutron.agent.l3 import dvr_snat_ns from neutron.agent.l3 import namespace_manager from neutron.agent.l3 import namespaces from neutron.agent.linux import ip_lib from neutron.tests import base _uuid = uuidutils.generate_uuid class NamespaceManagerTestCaseFramework(base.BaseTestCase): def _create_namespace_manager(self): self.agent_conf = mock.Mock() self.driver = mock.Mock() return namespace_manager.NamespaceManager(self.agent_conf, self.driver) class TestNamespaceManager(NamespaceManagerTestCaseFramework): def setUp(self): super(TestNamespaceManager, self).setUp() self.ns_manager = self._create_namespace_manager() def test_get_prefix_and_id(self): router_id = _uuid() ns_prefix, ns_id = self.ns_manager.get_prefix_and_id( namespaces.NS_PREFIX + router_id) self.assertEqual(namespaces.NS_PREFIX, ns_prefix) self.assertEqual(router_id, ns_id) ns_prefix, ns_id = self.ns_manager.get_prefix_and_id( dvr_snat_ns.SNAT_NS_PREFIX + router_id) self.assertEqual(dvr_snat_ns.SNAT_NS_PREFIX, ns_prefix) self.assertEqual(router_id, ns_id) ns_name = 'dhcp-' + router_id self.assertIsNone(self.ns_manager.get_prefix_and_id(ns_name)) def test_is_managed(self): router_id = _uuid() router_ns_name = namespaces.NS_PREFIX + router_id self.assertTrue(self.ns_manager.is_managed(router_ns_name)) router_ns_name = dvr_snat_ns.SNAT_NS_PREFIX + router_id self.assertTrue(self.ns_manager.is_managed(router_ns_name)) ext_net_id = _uuid() router_ns_name = dvr_fip_ns.FIP_NS_PREFIX + ext_net_id self.assertTrue(self.ns_manager.is_managed(router_ns_name)) self.assertFalse(self.ns_manager.is_managed('dhcp-' + router_id)) def test_list_all(self): ns_names = [namespaces.NS_PREFIX + _uuid(), dvr_snat_ns.SNAT_NS_PREFIX + _uuid(), dvr_fip_ns.FIP_NS_PREFIX + _uuid(), 'dhcp-' + _uuid(), ] # Test the normal path with mock.patch.object(ip_lib, 'list_network_namespaces', return_value=ns_names): retrieved_ns_names = self.ns_manager.list_all() self.assertEqual(len(ns_names) - 1, len(retrieved_ns_names)) for i in range(len(retrieved_ns_names)): self.assertIn(ns_names[i], retrieved_ns_names) self.assertNotIn(ns_names[-1], retrieved_ns_names) # Test path where list_network_namespaces() raises exception with mock.patch.object(ip_lib, 'list_network_namespaces', side_effect=RuntimeError): retrieved_ns_names = self.ns_manager.list_all() self.assertFalse(retrieved_ns_names) def test_ensure_snat_cleanup(self): router_id = _uuid() with mock.patch.object(self.ns_manager, '_cleanup') as mock_cleanup: self.ns_manager.ensure_snat_cleanup(router_id) mock_cleanup.assert_called_once_with(dvr_snat_ns.SNAT_NS_PREFIX, router_id) def test_ensure_router_cleanup(self): router_id = _uuid() ns_names = [namespaces.NS_PREFIX + _uuid() for _ in range(5)] ns_names += [dvr_snat_ns.SNAT_NS_PREFIX + _uuid() for _ in range(5)] ns_names += [namespaces.NS_PREFIX + router_id, dvr_snat_ns.SNAT_NS_PREFIX + router_id] with mock.patch.object(ip_lib, 'list_network_namespaces', return_value=ns_names), \ mock.patch.object(self.ns_manager, '_cleanup') as mock_cleanup: self.ns_manager.ensure_router_cleanup(router_id) expected = [mock.call(namespaces.NS_PREFIX, router_id), mock.call(dvr_snat_ns.SNAT_NS_PREFIX, router_id)] mock_cleanup.assert_has_calls(expected, any_order=True) self.assertEqual(2, mock_cleanup.call_count) neutron-12.1.1/neutron/tests/unit/agent/l3/test_router_info.py0000664000175000017500000005334113553660047024522 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as lib_constants from oslo_utils import uuidutils from neutron.agent.l3 import router_info from neutron.agent.linux import ip_lib from neutron.common import exceptions as n_exc from neutron.conf.agent import common as config from neutron.conf.agent.l3 import config as l3_config from neutron.tests import base _uuid = uuidutils.generate_uuid class TestRouterInfo(base.BaseTestCase): def setUp(self): super(TestRouterInfo, self).setUp() conf = config.setup_conf() l3_config.register_l3_agent_config_opts(l3_config.OPTS, conf) self.ip_cls_p = mock.patch('neutron.agent.linux.ip_lib.IPWrapper') ip_cls = self.ip_cls_p.start() self.mock_ip = mock.MagicMock() ip_cls.return_value = self.mock_ip self.ri_kwargs = {'agent_conf': conf, 'interface_driver': mock.sentinel.interface_driver} def _check_agent_method_called(self, calls): self.mock_ip.netns.execute.assert_has_calls( [mock.call(call, check_exit_code=False) for call in calls], any_order=True) def test_routing_table_update(self): ri = router_info.RouterInfo(mock.Mock(), _uuid(), {}, **self.ri_kwargs) ri.router = {} fake_route1 = {'destination': '135.207.0.0/16', 'nexthop': '1.2.3.4'} fake_route2 = {'destination': '135.207.111.111/32', 'nexthop': '1.2.3.4'} ri.update_routing_table('replace', fake_route1) expected = [['ip', 'route', 'replace', 'to', '135.207.0.0/16', 'via', '1.2.3.4']] self._check_agent_method_called(expected) ri.update_routing_table('delete', fake_route1) expected = [['ip', 'route', 'delete', 'to', '135.207.0.0/16', 'via', '1.2.3.4']] self._check_agent_method_called(expected) ri.update_routing_table('replace', fake_route2) expected = [['ip', 'route', 'replace', 'to', '135.207.111.111/32', 'via', '1.2.3.4']] self._check_agent_method_called(expected) ri.update_routing_table('delete', fake_route2) expected = [['ip', 'route', 'delete', 'to', '135.207.111.111/32', 'via', '1.2.3.4']] self._check_agent_method_called(expected) def test_update_routing_table(self): # Just verify the correct namespace was used in the call uuid = _uuid() netns = 'qrouter-' + uuid fake_route1 = {'destination': '135.207.0.0/16', 'nexthop': '1.2.3.4'} ri = router_info.RouterInfo(mock.Mock(), uuid, {'id': uuid}, **self.ri_kwargs) ri._update_routing_table = mock.Mock() ri.update_routing_table('replace', fake_route1) ri._update_routing_table.assert_called_once_with('replace', fake_route1, netns) def test_routes_updated(self): ri = router_info.RouterInfo(mock.Mock(), _uuid(), {}, **self.ri_kwargs) ri.router = {} fake_old_routes = [] fake_new_routes = [{'destination': "110.100.31.0/24", 'nexthop': "10.100.10.30"}, {'destination': "110.100.30.0/24", 'nexthop': "10.100.10.30"}] ri.routes = fake_old_routes ri.router['routes'] = fake_new_routes ri.routes_updated(fake_old_routes, fake_new_routes) expected = [['ip', 'route', 'replace', 'to', '110.100.30.0/24', 'via', '10.100.10.30'], ['ip', 'route', 'replace', 'to', '110.100.31.0/24', 'via', '10.100.10.30']] self._check_agent_method_called(expected) ri.routes = fake_new_routes fake_new_routes = [{'destination': "110.100.30.0/24", 'nexthop': "10.100.10.30"}] ri.router['routes'] = fake_new_routes ri.routes_updated(ri.routes, fake_new_routes) expected = [['ip', 'route', 'delete', 'to', '110.100.31.0/24', 'via', '10.100.10.30']] self._check_agent_method_called(expected) fake_new_routes = [] ri.router['routes'] = fake_new_routes ri.routes_updated(ri.routes, fake_new_routes) expected = [['ip', 'route', 'delete', 'to', '110.100.30.0/24', 'via', '10.100.10.30']] self._check_agent_method_called(expected) def test__process_pd_iptables_rules(self): subnet_id = _uuid() ex_gw_port = {'id': _uuid()} prefix = '2001:db8:cafe::/64' ri = router_info.RouterInfo(mock.Mock(), _uuid(), {}, **self.ri_kwargs) ipv6_mangle = ri.iptables_manager.ipv6['mangle'] = mock.MagicMock() ri.get_ex_gw_port = mock.Mock(return_value=ex_gw_port) ri.get_external_device_name = mock.Mock(return_value='fake_device') ri.get_address_scope_mark_mask = mock.Mock(return_value='fake_mark') ri._process_pd_iptables_rules(prefix, subnet_id) mangle_rule = '-d %s ' % prefix mangle_rule += ri.address_scope_mangle_rule('fake_device', 'fake_mark') ipv6_mangle.add_rule.assert_called_once_with( 'scope', mangle_rule, tag='prefix_delegation_%s' % subnet_id) def test_add_ports_address_scope_iptables(self): ri = router_info.RouterInfo(mock.Mock(), _uuid(), {}, **self.ri_kwargs) port = { 'id': _uuid(), 'fixed_ips': [{'ip_address': '172.9.9.9'}], 'address_scopes': {lib_constants.IP_VERSION_4: '1234'} } ipv4_mangle = ri.iptables_manager.ipv4['mangle'] = mock.MagicMock() ri.get_address_scope_mark_mask = mock.Mock(return_value='fake_mark') ri.get_internal_device_name = mock.Mock(return_value='fake_device') ri.rt_tables_manager = mock.MagicMock() ri.process_external_port_address_scope_routing = mock.Mock() ri.process_floating_ip_address_scope_rules = mock.Mock() ri.iptables_manager._apply = mock.Mock() ri.router[lib_constants.INTERFACE_KEY] = [port] ri.process_address_scope() ipv4_mangle.add_rule.assert_called_once_with( 'scope', ri.address_scope_mangle_rule('fake_device', 'fake_mark')) def test_address_scope_mark_ids_handling(self): mark_ids = set(range(router_info.ADDRESS_SCOPE_MARK_ID_MIN, router_info.ADDRESS_SCOPE_MARK_ID_MAX)) ri = router_info.RouterInfo(mock.Mock(), _uuid(), {}, **self.ri_kwargs) # first mark id is used for the default address scope scope_to_mark_id = {router_info.DEFAULT_ADDRESS_SCOPE: mark_ids.pop()} self.assertEqual(scope_to_mark_id, ri._address_scope_to_mark_id) self.assertEqual(mark_ids, ri.available_mark_ids) # new id should be used for new address scope ri.get_address_scope_mark_mask('new_scope') scope_to_mark_id['new_scope'] = mark_ids.pop() self.assertEqual(scope_to_mark_id, ri._address_scope_to_mark_id) self.assertEqual(mark_ids, ri.available_mark_ids) # new router should have it's own mark ids set new_mark_ids = set(range(router_info.ADDRESS_SCOPE_MARK_ID_MIN, router_info.ADDRESS_SCOPE_MARK_ID_MAX)) new_ri = router_info.RouterInfo(mock.Mock(), _uuid(), {}, **self.ri_kwargs) new_mark_ids.pop() self.assertEqual(new_mark_ids, new_ri.available_mark_ids) self.assertTrue(ri.available_mark_ids != new_ri.available_mark_ids) def test_process_delete(self): ri = router_info.RouterInfo(mock.Mock(), _uuid(), {}, **self.ri_kwargs) ri.router = {'id': _uuid()} with mock.patch.object(ri, '_process_internal_ports') as p_i_p,\ mock.patch.object(ri, '_process_external_on_delete') as p_e_o_d: self.mock_ip.netns.exists.return_value = False ri.process_delete() self.assertFalse(p_i_p.called) self.assertFalse(p_e_o_d.called) p_i_p.reset_mock() p_e_o_d.reset_mock() self.mock_ip.netns.exists.return_value = True ri.process_delete() p_i_p.assert_called_once_with() p_e_o_d.assert_called_once_with() def test__update_internal_ports_cache(self): ri = router_info.RouterInfo(mock.Mock(), _uuid(), {}, **self.ri_kwargs) ri.internal_ports = [ {'id': 'port-id-1', 'mtu': 1500}, {'id': 'port-id-2', 'mtu': 2000}] initial_internal_ports = ri.internal_ports[:] # Test add new element to the cache new_port = {'id': 'new-port-id', 'mtu': 1500} ri._update_internal_ports_cache(new_port) self.assertEqual( initial_internal_ports + [new_port], ri.internal_ports) # Test update existing port in cache updated_port = new_port.copy() updated_port['mtu'] = 2500 ri._update_internal_ports_cache(updated_port) self.assertEqual( initial_internal_ports + [updated_port], ri.internal_ports) class BasicRouterTestCaseFramework(base.BaseTestCase): def _create_router(self, router=None, **kwargs): if not router: router = mock.MagicMock() self.agent_conf = mock.Mock() self.router_id = _uuid() return router_info.RouterInfo(mock.Mock(), self.router_id, router, self.agent_conf, mock.sentinel.interface_driver, **kwargs) class TestBasicRouterOperations(BasicRouterTestCaseFramework): def test_get_floating_ips(self): router = mock.MagicMock() router.get.return_value = [mock.sentinel.floating_ip] ri = self._create_router(router) fips = ri.get_floating_ips() self.assertEqual([mock.sentinel.floating_ip], fips) def test_process_floating_ip_nat_rules(self): ri = self._create_router() fips = [{'fixed_ip_address': mock.sentinel.ip, 'floating_ip_address': mock.sentinel.fip}] ri.get_floating_ips = mock.Mock(return_value=fips) ri.iptables_manager = mock.MagicMock() ipv4_nat = ri.iptables_manager.ipv4['nat'] ri.floating_forward_rules = mock.Mock( return_value=[(mock.sentinel.chain, mock.sentinel.rule)]) ri.process_floating_ip_nat_rules() # Be sure that the rules are cleared first and apply is called last self.assertEqual(mock.call.clear_rules_by_tag('floating_ip'), ipv4_nat.mock_calls[0]) self.assertEqual(mock.call.apply(), ri.iptables_manager.mock_calls[-1]) # Be sure that add_rule is called somewhere in the middle ipv4_nat.add_rule.assert_called_once_with(mock.sentinel.chain, mock.sentinel.rule, tag='floating_ip') def test_process_floating_ip_nat_rules_removed(self): ri = self._create_router() ri.get_floating_ips = mock.Mock(return_value=[]) ri.iptables_manager = mock.MagicMock() ipv4_nat = ri.iptables_manager.ipv4['nat'] ri.process_floating_ip_nat_rules() # Be sure that the rules are cleared first and apply is called last self.assertEqual(mock.call.clear_rules_by_tag('floating_ip'), ipv4_nat.mock_calls[0]) self.assertEqual(mock.call.apply(), ri.iptables_manager.mock_calls[-1]) # Be sure that add_rule is called somewhere in the middle self.assertFalse(ipv4_nat.add_rule.called) def test_process_floating_ip_address_scope_rules_diff_scopes(self): ri = self._create_router() fips = [{'fixed_ip_address': mock.sentinel.ip, 'floating_ip_address': mock.sentinel.fip, 'fixed_ip_address_scope': 'scope1'}] ri.get_floating_ips = mock.Mock(return_value=fips) ri._get_external_address_scope = mock.Mock(return_value='scope2') ipv4_mangle = ri.iptables_manager.ipv4['mangle'] = mock.MagicMock() ri.floating_mangle_rules = mock.Mock( return_value=[(mock.sentinel.chain1, mock.sentinel.rule1)]) ri.get_external_device_name = mock.Mock() ri.process_floating_ip_address_scope_rules() # Be sure that the rules are cleared first self.assertEqual(mock.call.clear_rules_by_tag('floating_ip'), ipv4_mangle.mock_calls[0]) # Be sure that add_rule is called somewhere in the middle self.assertEqual(1, ipv4_mangle.add_rule.call_count) self.assertEqual(mock.call.add_rule(mock.sentinel.chain1, mock.sentinel.rule1, tag='floating_ip'), ipv4_mangle.mock_calls[1]) def test_process_floating_ip_address_scope_rules_same_scopes(self): ri = self._create_router() fips = [{'fixed_ip_address': mock.sentinel.ip, 'floating_ip_address': mock.sentinel.fip, 'fixed_ip_address_scope': 'scope1'}] ri.get_floating_ips = mock.Mock(return_value=fips) ri._get_external_address_scope = mock.Mock(return_value='scope1') ipv4_mangle = ri.iptables_manager.ipv4['mangle'] = mock.MagicMock() ri.process_floating_ip_address_scope_rules() # Be sure that the rules are cleared first self.assertEqual(mock.call.clear_rules_by_tag('floating_ip'), ipv4_mangle.mock_calls[0]) # Be sure that add_rule is not called somewhere in the middle self.assertFalse(ipv4_mangle.add_rule.called) def test_process_floating_ip_mangle_rules_removed(self): ri = self._create_router() ri.get_floating_ips = mock.Mock(return_value=[]) ipv4_mangle = ri.iptables_manager.ipv4['mangle'] = mock.MagicMock() ri.process_floating_ip_address_scope_rules() # Be sure that the rules are cleared first self.assertEqual(mock.call.clear_rules_by_tag('floating_ip'), ipv4_mangle.mock_calls[0]) # Be sure that add_rule is not called somewhere in the middle self.assertFalse(ipv4_mangle.add_rule.called) def _test_add_fip_addr_to_device_error(self, device): ri = self._create_router() ip = '15.1.2.3' result = ri._add_fip_addr_to_device( {'id': mock.sentinel.id, 'floating_ip_address': ip}, device) device.addr.add.assert_called_with(ip + '/32') return result def test__add_fip_addr_to_device(self): result = self._test_add_fip_addr_to_device_error(mock.Mock()) self.assertTrue(result) def test__add_fip_addr_to_device_error(self): device = mock.Mock() device.addr.add.side_effect = RuntimeError result = self._test_add_fip_addr_to_device_error(device) self.assertFalse(result) def test_process_snat_dnat_for_fip(self): ri = self._create_router() ri.process_floating_ip_nat_rules = mock.Mock(side_effect=Exception) self.assertRaises(n_exc.FloatingIpSetupException, ri.process_snat_dnat_for_fip) ri.process_floating_ip_nat_rules.assert_called_once_with() def test_put_fips_in_error_state(self): ri = self._create_router() ri.router = mock.Mock() ri.router.get.return_value = [{'id': mock.sentinel.id1}, {'id': mock.sentinel.id2}] statuses = ri.put_fips_in_error_state() expected = [{mock.sentinel.id1: lib_constants.FLOATINGIP_STATUS_ERROR, mock.sentinel.id2: lib_constants.FLOATINGIP_STATUS_ERROR}] self.assertNotEqual(expected, statuses) def test_configure_fip_addresses(self): ri = self._create_router() ri.process_floating_ip_addresses = mock.Mock( side_effect=Exception) self.assertRaises(n_exc.FloatingIpSetupException, ri.configure_fip_addresses, mock.sentinel.interface_name) ri.process_floating_ip_addresses.assert_called_once_with( mock.sentinel.interface_name) def test_get_router_cidrs_returns_cidrs(self): ri = self._create_router() addresses = ['15.1.2.2/24', '15.1.2.3/32'] device = mock.MagicMock() device.addr.list.return_value = [{'cidr': addresses[0]}, {'cidr': addresses[1]}] self.assertEqual(set(addresses), ri.get_router_cidrs(device)) @mock.patch.object(ip_lib, 'IPDevice') class TestFloatingIpWithMockDevice(BasicRouterTestCaseFramework): def test_process_floating_ip_addresses_remap(self, IPDevice): fip_id = _uuid() fip = { 'id': fip_id, 'port_id': _uuid(), 'floating_ip_address': '15.1.2.3', 'fixed_ip_address': '192.168.0.2', 'status': lib_constants.FLOATINGIP_STATUS_DOWN } IPDevice.return_value = device = mock.Mock() device.addr.list.return_value = [{'cidr': '15.1.2.3/32'}] ri = self._create_router() ri.get_floating_ips = mock.Mock(return_value=[fip]) fip_statuses = ri.process_floating_ip_addresses( mock.sentinel.interface_name) self.assertEqual({fip_id: lib_constants.FLOATINGIP_STATUS_ACTIVE}, fip_statuses) self.assertFalse(device.addr.add.called) self.assertFalse(device.addr.delete.called) def test_process_router_with_disabled_floating_ip(self, IPDevice): fip_id = _uuid() fip = { 'id': fip_id, 'port_id': _uuid(), 'floating_ip_address': '15.1.2.3', 'fixed_ip_address': '192.168.0.2' } ri = self._create_router() ri.floating_ips = [fip] ri.get_floating_ips = mock.Mock(return_value=[]) fip_statuses = ri.process_floating_ip_addresses( mock.sentinel.interface_name) self.assertIsNone(fip_statuses.get(fip_id)) def test_process_router_floating_ip_with_device_add_error(self, IPDevice): IPDevice.return_value = device = mock.Mock(side_effect=RuntimeError) device.addr.list.return_value = [] fip_id = _uuid() fip = { 'id': fip_id, 'port_id': _uuid(), 'floating_ip_address': '15.1.2.3', 'fixed_ip_address': '192.168.0.2', 'status': 'DOWN' } ri = self._create_router() ri.add_floating_ip = mock.Mock( return_value=lib_constants.FLOATINGIP_STATUS_ERROR) ri.get_floating_ips = mock.Mock(return_value=[fip]) fip_statuses = ri.process_floating_ip_addresses( mock.sentinel.interface_name) self.assertEqual({fip_id: lib_constants.FLOATINGIP_STATUS_ERROR}, fip_statuses) # TODO(mrsmith): refactor for DVR cases def test_process_floating_ip_addresses_remove(self, IPDevice): IPDevice.return_value = device = mock.Mock() device.addr.list.return_value = [{'cidr': '15.1.2.3/32'}] ri = self._create_router() ri.remove_floating_ip = mock.Mock() ri.router.get = mock.Mock(return_value=[]) fip_statuses = ri.process_floating_ip_addresses( mock.sentinel.interface_name) self.assertEqual({}, fip_statuses) ri.remove_floating_ip.assert_called_once_with(device, '15.1.2.3/32') def test_process_floating_ip_reassignment(self, IPDevice): IPDevice.return_value = device = mock.Mock() device.addr.list.return_value = [{'cidr': '15.1.2.3/32'}] fip_id = _uuid() fip = { 'id': fip_id, 'port_id': _uuid(), 'floating_ip_address': '15.1.2.3', 'fixed_ip_address': '192.168.0.3', 'status': 'DOWN' } ri = self._create_router() ri.get_floating_ips = mock.Mock(return_value=[fip]) ri.move_floating_ip = mock.Mock() ri.fip_map = {'15.1.2.3': '192.168.0.2'} ri.process_floating_ip_addresses(mock.sentinel.interface_name) ri.move_floating_ip.assert_called_once_with(fip) def test_process_floating_ip_addresses_gw_secondary_ip_not_removed( self, IPDevice): IPDevice.return_value = device = mock.Mock() device.addr.list.return_value = [{'cidr': '1.1.1.1/16'}, {'cidr': '2.2.2.2/32'}, {'cidr': '3.3.3.3/32'}, {'cidr': '4.4.4.4/32'}] ri = self._create_router() ri.get_floating_ips = mock.Mock(return_value=[ {'id': _uuid(), 'floating_ip_address': '3.3.3.3', 'status': 'DOWN'}]) ri.add_floating_ip = mock.Mock() ri.get_ex_gw_port = mock.Mock(return_value={ "fixed_ips": [{"ip_address": "1.1.1.1"}, {"ip_address": "2.2.2.2"}]}) ri.remove_floating_ip = mock.Mock() ri.process_floating_ip_addresses("qg-fake-device") ri.remove_floating_ip.assert_called_once_with(device, '4.4.4.4/32') neutron-12.1.1/neutron/tests/unit/agent/test_resource_cache.py0000664000175000017500000002212513553660046024616 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import context from neutron.agent import resource_cache from neutron.api.rpc.callbacks import events as events_rpc from neutron.tests import base class OVOLikeThing(object): def __init__(self, id, revision_number=10, **kwargs): self.id = id self.fields = ['id', 'revision_number'] self.revision_number = revision_number for k, v in kwargs.items(): self.fields.append(k) setattr(self, k, v) def to_dict(self): return {f: getattr(self, f) for f in self.fields} def get(self, k): return getattr(self, k, None) class RemoteResourceCacheTestCase(base.BaseTestCase): def setUp(self): super(RemoteResourceCacheTestCase, self).setUp() rtypes = ['duck', 'goose'] self.goose = OVOLikeThing(1) self.duck = OVOLikeThing(2) self.ctx = context.get_admin_context() self.rcache = resource_cache.RemoteResourceCache(rtypes) self._pullmock = mock.patch.object(self.rcache, '_puller').start() def test_get_resource_by_id(self): self.rcache.record_resource_update(self.ctx, 'goose', self.goose) self.assertEqual(self.goose, self.rcache.get_resource_by_id('goose', 1)) self.assertIsNone(self.rcache.get_resource_by_id('goose', 2)) def test__flood_cache_for_query_pulls_once(self): resources = [OVOLikeThing(66), OVOLikeThing(67)] received_kw = [] receiver = lambda *a, **k: received_kw.append(k) registry.subscribe(receiver, 'goose', events.AFTER_UPDATE) self._pullmock.bulk_pull.side_effect = [ resources, [resources[0]], [resources[1]], [resources[1]] ] self.rcache._flood_cache_for_query('goose', id=(66, 67), name=('a', 'b')) self._pullmock.bulk_pull.assert_called_once_with( mock.ANY, 'goose', filter_kwargs={'id': (66, 67), 'name': ('a', 'b')}) self._pullmock.bulk_pull.reset_mock() self.rcache._flood_cache_for_query('goose', id=(66, ), name=('a', )) self.assertFalse(self._pullmock.called) self.rcache._flood_cache_for_query('goose', id=(67, ), name=('b', )) self.assertFalse(self._pullmock.called) # querying by just ID should trigger a new call since ID+name is a more # specific query self.rcache._flood_cache_for_query('goose', id=(67, )) self._pullmock.bulk_pull.assert_called_once_with( mock.ANY, 'goose', filter_kwargs={'id': (67, )}) self.assertItemsEqual( resources, [rec['updated'] for rec in received_kw]) def test_bulk_pull_doesnt_wipe_out_newer_data(self): self.rcache.record_resource_update( self.ctx, 'goose', OVOLikeThing(1, revision_number=5)) updated = OVOLikeThing(1) updated.revision_number = 1 # older revision number self._pullmock.bulk_pull.return_value = [updated] self.rcache._flood_cache_for_query('goose', id=(1,),) self.assertEqual( 5, self.rcache.get_resource_by_id('goose', 1).revision_number) def test_get_resources(self): geese = [OVOLikeThing(3, size='large'), OVOLikeThing(5, size='medium'), OVOLikeThing(4, size='large'), OVOLikeThing(6, size='small')] for goose in geese: self.rcache.record_resource_update(self.ctx, 'goose', goose) is_large = {'size': ('large', )} is_small = {'size': ('small', )} self.assertItemsEqual([geese[0], geese[2]], self.rcache.get_resources('goose', is_large)) self.assertItemsEqual([geese[3]], self.rcache.get_resources('goose', is_small)) def test_match_resources_with_func(self): geese = [OVOLikeThing(3, size='large'), OVOLikeThing(5, size='medium'), OVOLikeThing(4, size='xlarge'), OVOLikeThing(6, size='small')] for goose in geese: self.rcache.record_resource_update(self.ctx, 'goose', goose) has_large = lambda o: 'large' in o.size self.assertItemsEqual([geese[0], geese[2]], self.rcache.match_resources_with_func('goose', has_large)) def test__is_stale(self): goose = OVOLikeThing(3, size='large') self.rcache.record_resource_update(self.ctx, 'goose', goose) # same revision id is not considered stale updated = OVOLikeThing(3, size='large') self.assertFalse(self.rcache._is_stale('goose', updated)) updated.revision_number = 0 self.assertTrue(self.rcache._is_stale('goose', updated)) updated.revision_number = 200 self.assertFalse(self.rcache._is_stale('goose', updated)) # once deleted, all updates are stale self.rcache.record_resource_delete(self.ctx, 'goose', 3) self.assertTrue(self.rcache._is_stale('goose', updated)) def test_record_resource_update(self): received_kw = [] receiver = lambda *a, **k: received_kw.append(k) registry.subscribe(receiver, 'goose', events.AFTER_UPDATE) self.rcache.record_resource_update(self.ctx, 'goose', OVOLikeThing(3, size='large')) self.assertEqual(1, len(received_kw)) self.assertIsNone(received_kw[0]['existing']) # another update with no changed fields results in no callback self.rcache.record_resource_update(self.ctx, 'goose', OVOLikeThing(3, size='large', revision_number=100)) self.assertEqual(1, len(received_kw)) self.rcache.record_resource_update(self.ctx, 'goose', OVOLikeThing(3, size='small', revision_number=101)) self.assertEqual(2, len(received_kw)) self.assertEqual('large', received_kw[1]['existing'].size) self.assertEqual('small', received_kw[1]['updated'].size) self.assertEqual(set(['size']), received_kw[1]['changed_fields']) def test_record_resource_delete(self): received_kw = [] receiver = lambda *a, **k: received_kw.append(k) registry.subscribe(receiver, 'goose', events.AFTER_DELETE) self.rcache.record_resource_update(self.ctx, 'goose', OVOLikeThing(3, size='large')) self.rcache.record_resource_delete(self.ctx, 'goose', 3) self.assertEqual(1, len(received_kw)) self.assertEqual(3, received_kw[0]['existing'].id) self.assertEqual(3, received_kw[0]['resource_id']) # deletes of non-existing cache items are still honored self.rcache.record_resource_delete(self.ctx, 'goose', 4) self.assertEqual(2, len(received_kw)) self.assertIsNone(received_kw[1]['existing']) self.assertEqual(4, received_kw[1]['resource_id']) def test_record_resource_delete_ignores_dups(self): received_kw = [] receiver = lambda *a, **k: received_kw.append(k) registry.subscribe(receiver, 'goose', events.AFTER_DELETE) self.rcache.record_resource_delete(self.ctx, 'goose', 3) self.assertEqual(1, len(received_kw)) self.rcache.record_resource_delete(self.ctx, 'goose', 4) self.assertEqual(2, len(received_kw)) self.rcache.record_resource_delete(self.ctx, 'goose', 3) self.assertEqual(2, len(received_kw)) def test_resource_change_handler(self): with mock.patch.object(resource_cache.RemoteResourceWatcher, '_init_rpc_listeners'): watch = resource_cache.RemoteResourceWatcher(self.rcache) geese = [OVOLikeThing(3, size='large'), OVOLikeThing(5, size='medium'), OVOLikeThing(4, size='large'), OVOLikeThing(6, size='small')] watch.resource_change_handler(self.ctx, 'goose', geese, events_rpc.UPDATED) for goose in geese: self.assertEqual(goose, self.rcache.get_resource_by_id('goose', goose.id)) watch.resource_change_handler(self.ctx, 'goose', geese, events_rpc.DELETED) for goose in geese: self.assertIsNone( self.rcache.get_resource_by_id('goose', goose.id)) neutron-12.1.1/neutron/tests/unit/testlib_api.py0000664000175000017500000003242413553660047022012 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import fixtures import six import testresources import testscenarios import testtools from neutron_lib.db import model_base from oslo_config import cfg from oslo_db import exception as oslodb_exception from oslo_db.sqlalchemy import enginefacade from oslo_db.sqlalchemy import provision from oslo_db.sqlalchemy import session from neutron.db import api as db_api from neutron.db.migration import cli as migration # Import all data models from neutron.db.migration.models import head # noqa from neutron.tests import base from neutron import wsgi class ExpectedException(testtools.ExpectedException): def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): if super(ExpectedException, self).__exit__(exc_type, exc_value, traceback): self.exception = exc_value return True return False def create_request(path, body, content_type, method='GET', query_string=None, context=None, headers=None): headers = headers or {} if query_string: url = "%s?%s" % (path, query_string) else: url = path req = wsgi.Request.blank(url) req.method = method req.headers = {} req.headers['Accept'] = content_type req.headers.update(headers) if isinstance(body, six.text_type): req.body = body.encode() else: req.body = body if context: req.environ['neutron.context'] = context return req class SqlFixture(fixtures.Fixture): """Base of a fixture which can create a schema and delete from its tables. """ @classmethod def _generate_schema(cls, engine): model_base.BASEV2.metadata.create_all(engine) def _delete_from_schema(self, engine): with engine.begin() as conn: for table in reversed( model_base.BASEV2.metadata.sorted_tables): conn.execute(table.delete()) def _init_resources(self): raise NotImplementedError() def _setUp(self): self._init_resources() # check if the fixtures failed to get # an engine. The test setUp() itself should also be checking # this and raising skipTest. if not hasattr(self, 'engine'): return engine = self.engine self.addCleanup(lambda: self._delete_from_schema(engine)) self.sessionmaker = session.get_maker(engine) _restore_factory = db_api.context_manager._root_factory self.enginefacade_factory = enginefacade._TestTransactionFactory( self.engine, self.sessionmaker, from_factory=_restore_factory, apply_global=False) db_api.context_manager._root_factory = self.enginefacade_factory engine = db_api.context_manager.writer.get_engine() self.addCleanup( lambda: setattr( db_api.context_manager, "_root_factory", _restore_factory)) self.useFixture(EnableSQLiteFKsFixture(engine)) class EnableSQLiteFKsFixture(fixtures.Fixture): """Turn SQLite PRAGMA foreign keys on and off for tests. FIXME(zzzeek): figure out some way to get oslo.db test_base to honor oslo_db.engines.create_engine() arguments like sqlite_fks as well as handling that it needs to be turned off during drops. """ def __init__(self, engine): self.engine = engine def _setUp(self): if self.engine.name == 'sqlite': self.engine.execute("PRAGMA foreign_keys=ON") def disable_fks(): with self.engine.connect() as conn: conn.connection.rollback() conn.execute("PRAGMA foreign_keys=OFF") self.addCleanup(disable_fks) class StaticSqlFixture(SqlFixture): """Fixture which keeps a single sqlite memory database at the global scope. """ _GLOBAL_RESOURCES = False @classmethod def _init_resources(cls): # this is a classlevel version of what testresources # does w/ the resources attribute as well as the # setUpResources() step (which requires a test instance, that # SqlFixture does not have). Because this is a SQLite memory # database, we don't actually tear it down, so we can keep # it running throughout all tests. if cls._GLOBAL_RESOURCES: return else: cls._GLOBAL_RESOURCES = True cls.schema_resource = provision.SchemaResource( provision.DatabaseResource("sqlite", db_api.context_manager), cls._generate_schema, teardown=False) dependency_resources = {} for name, resource in cls.schema_resource.resources: dependency_resources[name] = resource.getResource() cls.schema_resource.make(dependency_resources) cls.engine = dependency_resources['database'].engine class StaticSqlFixtureNoSchema(SqlFixture): """Fixture which keeps a single sqlite memory database at the global scope """ _GLOBAL_RESOURCES = False @classmethod def _init_resources(cls): if cls._GLOBAL_RESOURCES: return else: cls._GLOBAL_RESOURCES = True cls.database_resource = provision.DatabaseResource( "sqlite", db_api.context_manager) dependency_resources = {} for name, resource in cls.database_resource.resources: dependency_resources[name] = resource.getResource() cls.engine = dependency_resources['backend'].engine def _delete_from_schema(self, engine): pass class OpportunisticSqlFixture(SqlFixture): """Fixture which uses testresources with oslo_db provisioning to check for available backends and optimize test runs. Requires that the test itself implement the resources attribute. """ DRIVER = 'sqlite' def __init__(self, test): super(OpportunisticSqlFixture, self).__init__() self.test = test @classmethod def _generate_schema_w_migrations(cls, engine): alembic_configs = migration.get_alembic_configs() with engine.connect() as conn: for alembic_config in alembic_configs: alembic_config.attributes['connection'] = conn alembic_config.neutron_config = cfg.CONF alembic_config.neutron_config.set_override( 'connection', str(engine.url), group='database') migration.do_alembic_command( alembic_config, 'upgrade', 'heads') def _delete_from_schema(self, engine): if self.test.BUILD_SCHEMA: super(OpportunisticSqlFixture, self)._delete_from_schema(engine) def _init_resources(self): testresources.setUpResources( self.test, self.test.resources, testresources._get_result()) self.addCleanup( testresources.tearDownResources, self.test, self.test.resources, testresources._get_result() ) # unfortunately, fixtures won't let us call a skip() from # here. So the test has to check this also. # see https://github.com/testing-cabal/fixtures/issues/31 if hasattr(self.test, 'db'): self.engine = self.test.engine = self.test.db.engine @classmethod def resources_collection(cls, test): # reimplement current oslo.db code. # FIXME(zzzeek) The patterns here are up in the air enough # that I think keeping this totally separate will give us the # most leverage in being able to fix oslo.db in an upcoming # release, then port neutron back to the working version. driver = test.DRIVER if driver not in test._database_resources: try: test._database_resources[driver] = \ provision.DatabaseResource(driver) except oslodb_exception.BackendNotAvailable: test._database_resources[driver] = None database_resource = test._database_resources[driver] if database_resource is None: return [] key = (driver, None) if test.BUILD_SCHEMA: if key not in test._schema_resources: test._schema_resources[key] = provision.SchemaResource( database_resource, cls._generate_schema_w_migrations if test.BUILD_WITH_MIGRATIONS else cls._generate_schema, teardown=False) schema_resource = test._schema_resources[key] return [ ('schema', schema_resource), ('db', database_resource) ] else: return [ ('db', database_resource) ] class BaseSqlTestCase(object): BUILD_SCHEMA = True def setUp(self): super(BaseSqlTestCase, self).setUp() self._setup_database_fixtures() def _setup_database_fixtures(self): if self.BUILD_SCHEMA: fixture = StaticSqlFixture() else: fixture = StaticSqlFixtureNoSchema() self.useFixture(fixture) self.engine = fixture.engine class SqlTestCaseLight(BaseSqlTestCase, base.DietTestCase): """All SQL taste, zero plugin/rpc sugar""" class SqlTestCase(BaseSqlTestCase, base.BaseTestCase): """regular sql test""" class OpportunisticDBTestMixin(object): """Mixin that converts a BaseSqlTestCase to use the OpportunisticSqlFixture. """ SKIP_ON_UNAVAILABLE_DB = not base.bool_from_env('OS_FAIL_ON_MISSING_DEPS') FIXTURE = OpportunisticSqlFixture BUILD_WITH_MIGRATIONS = False def _setup_database_fixtures(self): self.useFixture(self.FIXTURE(self)) if not hasattr(self, 'db'): msg = "backend '%s' unavailable" % self.DRIVER if self.SKIP_ON_UNAVAILABLE_DB: self.skip(msg) else: self.fail(msg) _schema_resources = {} _database_resources = {} @property def resources(self): """this attribute is used by testresources for optimized sorting of tests. This is the big requirement that allows testresources to sort tests such that database "resources" can be kept open for many tests at once. IMO(zzzeek) "sorting" should not be needed; only that necessary resources stay open as long as they are needed (or long enough to reduce overhead). testresources would be improved to not depend on custom, incompatible-with-pytest "suite classes", fixture information leaking out of the Fixture classes themselves, and exotic sorting schemes for something that can nearly always be handled "good enough" with unittest-standard setupclass/setupmodule schemes. """ return self.FIXTURE.resources_collection(self) class MySQLTestCaseMixin(OpportunisticDBTestMixin): """Mixin that turns any BaseSqlTestCase into a MySQL test suite. If the MySQL db is unavailable then this test is skipped, unless OS_FAIL_ON_MISSING_DEPS is enabled. """ DRIVER = "mysql" class PostgreSQLTestCaseMixin(OpportunisticDBTestMixin): """Mixin that turns any BaseSqlTestCase into a PostgresSQL test suite. If the PostgreSQL db is unavailable then this test is skipped, unless OS_FAIL_ON_MISSING_DEPS is enabled. """ DRIVER = "postgresql" def module_load_tests(loader, found_tests, pattern): """Apply OptimisingTestSuite on a per-module basis. FIXME(zzzeek): oslo.db provides this but the contract that "pattern" should be None no longer seems to behave as it used to at the module level, so this function needs to be added in this form. """ result = testresources.OptimisingTestSuite() found_tests = testscenarios.load_tests_apply_scenarios( loader, found_tests, pattern) result.addTest(found_tests) return result class WebTestCase(SqlTestCase): fmt = 'json' def setUp(self): super(WebTestCase, self).setUp() json_deserializer = wsgi.JSONDeserializer() self._deserializers = { 'application/json': json_deserializer, } def deserialize(self, response): ctype = 'application/%s' % self.fmt data = self._deserializers[ctype].deserialize(response.body)['body'] return data def serialize(self, data): ctype = 'application/%s' % self.fmt result = wsgi.Serializer().serialize(data, ctype) return result class SubDictMatch(object): def __init__(self, sub_dict): self.sub_dict = sub_dict def __eq__(self, super_dict): return all(item in super_dict.items() for item in self.sub_dict.items()) neutron-12.1.1/neutron/tests/unit/ipam/0000775000175000017500000000000013553660157020064 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/ipam/drivers/0000775000175000017500000000000013553660157021542 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/ipam/drivers/neutrondb_ipam/0000775000175000017500000000000013553660157024550 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/ipam/drivers/neutrondb_ipam/test_driver.py0000664000175000017500000004607613553660047027467 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import netaddr from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from oslo_utils import uuidutils from neutron.common import constants as n_const from neutron.ipam.drivers.neutrondb_ipam import driver from neutron.ipam import exceptions as ipam_exc from neutron.ipam import requests as ipam_req from neutron.objects import ipam as ipam_obj from neutron.tests.unit.db import test_db_base_plugin_v2 as test_db_plugin from neutron.tests.unit import testlib_api def convert_firstip_to_ipaddress(range_item): return netaddr.IPAddress(range_item['first_ip']) class TestNeutronDbIpamMixin(object): def _create_network(self, plugin, ctx, shared=False): network = {'network': {'name': 'net', 'shared': shared, 'admin_state_up': True, 'tenant_id': self._tenant_id}} created_network = plugin.create_network(ctx, network) return (created_network, created_network['id']) def _create_subnet(self, plugin, ctx, network_id, cidr, ip_version=4, v6_address_mode=constants.ATTR_NOT_SPECIFIED, allocation_pools=constants.ATTR_NOT_SPECIFIED): subnet = {'subnet': {'name': 'sub', 'cidr': cidr, 'ip_version': ip_version, 'gateway_ip': constants.ATTR_NOT_SPECIFIED, 'allocation_pools': allocation_pools, 'enable_dhcp': True, 'dns_nameservers': constants.ATTR_NOT_SPECIFIED, 'host_routes': constants.ATTR_NOT_SPECIFIED, 'ipv6_address_mode': v6_address_mode, 'ipv6_ra_mode': constants.ATTR_NOT_SPECIFIED, 'network_id': network_id, 'tenant_id': self._tenant_id}} return plugin.create_subnet(ctx, subnet) class TestNeutronDbIpamPool(testlib_api.SqlTestCase, TestNeutronDbIpamMixin): """Test case for the Neutron's DB IPAM driver subnet pool interface.""" def setUp(self): super(TestNeutronDbIpamPool, self).setUp() self._tenant_id = 'test-tenant' # Configure plugin for tests self.setup_coreplugin(test_db_plugin.DB_PLUGIN_KLASS) # Prepare environment for tests self.plugin = directory.get_plugin() self.ctx = context.get_admin_context() self.network, self.net_id = self._create_network(self.plugin, self.ctx) # Allocate IPAM driver self.ipam_pool = driver.NeutronDbPool(None, self.ctx) def _verify_ipam_subnet_details(self, ipam_subnet, cidr=None, tenant_id=None, gateway_ip=None, allocation_pools=None): ipam_subnet_details = ipam_subnet.get_details() gateway_ip_address = None cidr_ip_network = None if gateway_ip: gateway_ip_address = netaddr.IPAddress(gateway_ip) if cidr: cidr_ip_network = netaddr.IPNetwork(cidr) self.assertEqual(tenant_id, ipam_subnet_details.tenant_id) self.assertEqual(gateway_ip_address, ipam_subnet_details.gateway_ip) self.assertEqual(cidr_ip_network, ipam_subnet_details.subnet_cidr) self.assertEqual(allocation_pools, ipam_subnet_details.allocation_pools) def test_allocate_ipam_subnet_no_neutron_subnet_id(self): cidr = '10.0.0.0/24' allocation_pools = [netaddr.IPRange('10.0.0.100', '10.0.0.150'), netaddr.IPRange('10.0.0.200', '10.0.0.250')] subnet_req = ipam_req.SpecificSubnetRequest( self._tenant_id, None, cidr, allocation_pools=allocation_pools, gateway_ip='10.0.0.101') ipam_subnet = self.ipam_pool.allocate_subnet(subnet_req) self._verify_ipam_subnet_details(ipam_subnet, cidr, self._tenant_id, '10.0.0.101', allocation_pools) def _prepare_specific_subnet_request(self, cidr): subnet = self._create_subnet( self.plugin, self.ctx, self.net_id, cidr) subnet_req = ipam_req.SpecificSubnetRequest( self._tenant_id, subnet['id'], cidr, gateway_ip=subnet['gateway_ip']) return subnet, subnet_req def test_allocate_ipam_subnet_with_neutron_subnet_id(self): cidr = '10.0.0.0/24' subnet, subnet_req = self._prepare_specific_subnet_request(cidr) ipam_subnet = self.ipam_pool.allocate_subnet(subnet_req) self._verify_ipam_subnet_details( ipam_subnet, cidr, self._tenant_id, subnet['gateway_ip'], [netaddr.IPRange('10.0.0.2', '10.0.0.254')]) def test_allocate_any_subnet_fails(self): self.assertRaises( ipam_exc.InvalidSubnetRequestType, self.ipam_pool.allocate_subnet, ipam_req.AnySubnetRequest(self._tenant_id, 'meh', constants.IPv4, 24)) def _test_update_subnet_pools(self, allocation_pools, expected_pools=None): if expected_pools is None: expected_pools = allocation_pools cidr = '10.0.0.0/24' subnet, subnet_req = self._prepare_specific_subnet_request(cidr) self.ipam_pool.allocate_subnet(subnet_req) update_subnet_req = ipam_req.SpecificSubnetRequest( self._tenant_id, subnet['id'], cidr, gateway_ip=subnet['gateway_ip'], allocation_pools=allocation_pools) self.ipam_pool.update_subnet(update_subnet_req) ipam_subnet = self.ipam_pool.get_subnet(subnet['id']) self._verify_ipam_subnet_details( ipam_subnet, cidr, self._tenant_id, subnet['gateway_ip'], expected_pools) def test_update_subnet_pools(self): allocation_pools = [netaddr.IPRange('10.0.0.100', '10.0.0.150'), netaddr.IPRange('10.0.0.200', '10.0.0.250')] self._test_update_subnet_pools(allocation_pools) def test_update_subnet_pools_with_blank_pools(self): allocation_pools = [] self._test_update_subnet_pools(allocation_pools) def test_update_subnet_pools_with_none_pools(self): allocation_pools = None expected_pools = [netaddr.IPRange('10.0.0.2', '10.0.0.254')] # Pools should not be changed on update self._test_update_subnet_pools(allocation_pools, expected_pools=expected_pools) def test_get_subnet(self): cidr = '10.0.0.0/24' subnet, subnet_req = self._prepare_specific_subnet_request(cidr) self.ipam_pool.allocate_subnet(subnet_req) # Retrieve the subnet ipam_subnet = self.ipam_pool.get_subnet(subnet['id']) self._verify_ipam_subnet_details( ipam_subnet, cidr, self._tenant_id, subnet['gateway_ip'], [netaddr.IPRange('10.0.0.2', '10.0.0.254')]) def test_get_non_existing_subnet_fails(self): self.assertRaises(n_exc.SubnetNotFound, self.ipam_pool.get_subnet, 'boo') def test_remove_ipam_subnet(self): cidr = '10.0.0.0/24' subnet, subnet_req = self._prepare_specific_subnet_request(cidr) self.ipam_pool.allocate_subnet(subnet_req) # Remove ipam subnet by neutron subnet id self.ipam_pool.remove_subnet(subnet['id']) def test_remove_non_existent_subnet_fails(self): self.assertRaises(n_exc.SubnetNotFound, self.ipam_pool.remove_subnet, 'non-existent-id') def test_get_details_for_invalid_subnet_id_fails(self): cidr = '10.0.0.0/24' non_existent_id = uuidutils.generate_uuid() subnet_req = ipam_req.SpecificSubnetRequest( self._tenant_id, non_existent_id, cidr) self.ipam_pool.allocate_subnet(subnet_req) # Neutron subnet does not exist, so get_subnet should fail self.assertRaises(n_exc.SubnetNotFound, self.ipam_pool.get_subnet, 'non-existent-id') class TestNeutronDbIpamSubnet(testlib_api.SqlTestCase, TestNeutronDbIpamMixin): """Test case for Subnet interface for Neutron's DB IPAM driver. This test case exercises the reference IPAM driver. Even if it loads a plugin, the unit tests in this class do not exercise it at all; they simply perform white box testing on the IPAM driver. The plugin is exclusively used to create the neutron objects on which the IPAM driver will operate. """ def _create_and_allocate_ipam_subnet( self, cidr, allocation_pools=constants.ATTR_NOT_SPECIFIED, ip_version=4, v6_auto_address=False, tenant_id=None): v6_address_mode = constants.ATTR_NOT_SPECIFIED if v6_auto_address: # set ip version to 6 regardless of what's been passed to the # method ip_version = 6 v6_address_mode = n_const.IPV6_SLAAC subnet = self._create_subnet( self.plugin, self.ctx, self.net_id, cidr, ip_version=ip_version, allocation_pools=allocation_pools, v6_address_mode=v6_address_mode) # Build netaddr.IPRanges from allocation pools since IPAM SubnetRequest # objects are strongly typed allocation_pool_ranges = [netaddr.IPRange( pool['start'], pool['end']) for pool in subnet['allocation_pools']] subnet_req = ipam_req.SpecificSubnetRequest( tenant_id, subnet['id'], cidr, gateway_ip=subnet['gateway_ip'], allocation_pools=allocation_pool_ranges) ipam_subnet = self.ipam_pool.allocate_subnet(subnet_req) return ipam_subnet, subnet def setUp(self): super(TestNeutronDbIpamSubnet, self).setUp() self._tenant_id = 'test-tenant' # Configure plugin for tests self.setup_coreplugin(test_db_plugin.DB_PLUGIN_KLASS) # Prepare environment for tests self.plugin = directory.get_plugin() self.ctx = context.get_admin_context() self.network, self.net_id = self._create_network(self.plugin, self.ctx) # Allocate IPAM driver self.ipam_pool = driver.NeutronDbPool(None, self.ctx) def test__verify_ip_succeeds(self): cidr = '10.0.0.0/24' ipam_subnet = self._create_and_allocate_ipam_subnet(cidr)[0] ipam_subnet._verify_ip(self.ctx, '10.0.0.2') def test__verify_ip_not_in_subnet_fails(self): cidr = '10.0.0.0/24' ipam_subnet = self._create_and_allocate_ipam_subnet(cidr)[0] self.assertRaises(ipam_exc.InvalidIpForSubnet, ipam_subnet._verify_ip, self.ctx, '192.168.0.2') def test__verify_ip_bcast_and_network_fail(self): cidr = '10.0.0.0/24' ipam_subnet = self._create_and_allocate_ipam_subnet(cidr)[0] self.assertRaises(ipam_exc.InvalidIpForSubnet, ipam_subnet._verify_ip, self.ctx, '10.0.0.255') self.assertRaises(ipam_exc.InvalidIpForSubnet, ipam_subnet._verify_ip, self.ctx, '10.0.0.0') def _allocate_address(self, cidr, ip_version, address_request): ipam_subnet = self._create_and_allocate_ipam_subnet( cidr, ip_version=ip_version)[0] return ipam_subnet.allocate(address_request) def test_allocate_any_v4_address_succeeds(self): self._test_allocate_any_address_succeeds('10.0.0.0/24', 4) def test_allocate_any_v6_address_succeeds(self): self._test_allocate_any_address_succeeds('fde3:abcd:4321:1::/64', 6) def _test_allocate_any_address_succeeds(self, subnet_cidr, ip_version): ip_address = self._allocate_address( subnet_cidr, ip_version, ipam_req.AnyAddressRequest) self.assertIn(netaddr.IPAddress(ip_address), netaddr.IPSet(netaddr.IPNetwork(subnet_cidr))) def test_allocate_specific_v4_address_succeeds(self): ip_address = self._allocate_address( '10.0.0.0/24', 4, ipam_req.SpecificAddressRequest('10.0.0.33')) self.assertEqual('10.0.0.33', ip_address) def test_allocate_specific_v6_address_succeeds(self): ip_address = self._allocate_address( 'fde3:abcd:4321:1::/64', 6, ipam_req.SpecificAddressRequest('fde3:abcd:4321:1::33')) self.assertEqual('fde3:abcd:4321:1::33', ip_address) def test_allocate_specific_v4_address_out_of_range_fails(self): self.assertRaises(ipam_exc.InvalidIpForSubnet, self._allocate_address, '10.0.0.0/24', 4, ipam_req.SpecificAddressRequest('192.168.0.1')) def test_allocate_specific_v6_address_out_of_range_fails(self): self.assertRaises(ipam_exc.InvalidIpForSubnet, self._allocate_address, 'fde3:abcd:4321:1::/64', 6, ipam_req.SpecificAddressRequest( 'fde3:abcd:eeee:1::33')) def test_allocate_specific_address_in_use_fails(self): ipam_subnet = self._create_and_allocate_ipam_subnet( 'fde3:abcd:4321:1::/64', ip_version=6)[0] addr_req = ipam_req.SpecificAddressRequest('fde3:abcd:4321:1::33') ipam_subnet.allocate(addr_req) self.assertRaises(ipam_exc.IpAddressAlreadyAllocated, ipam_subnet.allocate, addr_req) def test_allocate_any_address_exhausted_pools_fails(self): # Same as above, the ranges will be recalculated always ipam_subnet = self._create_and_allocate_ipam_subnet( '192.168.0.0/30', ip_version=4)[0] ipam_subnet.allocate(ipam_req.AnyAddressRequest) # The second address generation request on a /30 for v4 net must fail self.assertRaises(ipam_exc.IpAddressGenerationFailure, ipam_subnet.allocate, ipam_req.AnyAddressRequest) def _test_deallocate_address(self, cidr, ip_version): ipam_subnet = self._create_and_allocate_ipam_subnet( cidr, ip_version=ip_version)[0] ip_address = ipam_subnet.allocate(ipam_req.AnyAddressRequest) ipam_subnet.deallocate(ip_address) def test_deallocate_v4_address(self): self._test_deallocate_address('10.0.0.0/24', 4) def test_deallocate_v6_address(self): # This test does not really exercise any different code path wrt # test_deallocate_v4_address. It is provided for completeness and for # future proofing in case v6-specific logic will be added. self._test_deallocate_address('fde3:abcd:4321:1::/64', 6) def test_allocate_unallocated_address_fails(self): ipam_subnet = self._create_and_allocate_ipam_subnet( '10.0.0.0/24', ip_version=4)[0] self.assertRaises(ipam_exc.IpAddressAllocationNotFound, ipam_subnet.deallocate, '10.0.0.2') def test_allocate_all_pool_addresses_triggers_range_recalculation(self): # This test instead might be made to pass, but for the wrong reasons! pass def test_allocate_subnet_for_non_existent_subnet_pass(self): # This test should pass because ipam subnet is no longer # have foreign key relationship with neutron subnet. # Creating ipam subnet before neutron subnet is a valid case. tenant_id = uuidutils.generate_uuid() subnet_id = uuidutils.generate_uuid() subnet_req = ipam_req.SpecificSubnetRequest( tenant_id, subnet_id, '192.168.0.0/24') self.ipam_pool.allocate_subnet(subnet_req) def test_update_allocation_pools_with_no_pool_change(self): cidr = '10.0.0.0/24' ipam_subnet = self._create_and_allocate_ipam_subnet( cidr)[0] ipam_subnet.subnet_manager.delete_allocation_pools = mock.Mock() ipam_subnet.create_allocation_pools = mock.Mock() alloc_pools = [netaddr.IPRange('10.0.0.2', '10.0.0.254')] # Make sure allocation pools recreation does not happen in case of # unchanged allocation pools ipam_subnet.update_allocation_pools(alloc_pools, cidr) self.assertFalse( ipam_subnet.subnet_manager.delete_allocation_pools.called) self.assertFalse(ipam_subnet.create_allocation_pools.called) def _test__no_pool_changes(self, new_pools): id = uuidutils.generate_uuid() ipam_subnet = driver.NeutronDbSubnet(id, self.ctx) pools = [ipam_obj.IpamAllocationPool(self.ctx, ipam_subnet_id=id, first_ip='192.168.10.20', last_ip='192.168.10.41'), ipam_obj.IpamAllocationPool(self.ctx, ipam_subnet_id=id, first_ip='192.168.10.50', last_ip='192.168.10.60')] ipam_subnet.subnet_manager.list_pools = mock.Mock(return_value=pools) return ipam_subnet._no_pool_changes(self.ctx, new_pools) def test__no_pool_changes_negative(self): pool_list = [[netaddr.IPRange('192.168.10.2', '192.168.10.254')], [netaddr.IPRange('192.168.10.20', '192.168.10.41')], [netaddr.IPRange('192.168.10.20', '192.168.10.41'), netaddr.IPRange('192.168.10.51', '192.168.10.60')]] for pools in pool_list: self.assertFalse(self._test__no_pool_changes(pools)) def test__no_pool_changes_positive(self): pools = [netaddr.IPRange('192.168.10.20', '192.168.10.41'), netaddr.IPRange('192.168.10.50', '192.168.10.60')] self.assertTrue(self._test__no_pool_changes(pools)) neutron-12.1.1/neutron/tests/unit/ipam/drivers/neutrondb_ipam/test_db_api.py0000664000175000017500000001117613553660046027402 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import context from oslo_utils import uuidutils from neutron.ipam.drivers.neutrondb_ipam import db_api from neutron.objects import ipam as ipam_obj from neutron.tests.unit import testlib_api CORE_PLUGIN = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' class TestIpamSubnetManager(testlib_api.SqlTestCase): """Test case for SubnetManager DB helper class""" def setUp(self): super(TestIpamSubnetManager, self).setUp() self.setup_coreplugin(core_plugin=CORE_PLUGIN) self.ctx = context.get_admin_context() self.neutron_subnet_id = uuidutils.generate_uuid() self.ipam_subnet_id = uuidutils.generate_uuid() self.subnet_ip = '1.2.3.4' self.single_pool = ('1.2.3.4', '1.2.3.10') self.multi_pool = (('1.2.3.2', '1.2.3.12'), ('1.2.3.15', '1.2.3.24')) self.subnet_manager = db_api.IpamSubnetManager(self.ipam_subnet_id, self.neutron_subnet_id) self.subnet_manager_id = self.subnet_manager.create(self.ctx) self.ctx.session.flush() def test_create(self): self.assertEqual(self.ipam_subnet_id, self.subnet_manager_id) subnet_count = ipam_obj.IpamSubnet.count( self.ctx, id=self.ipam_subnet_id) self.assertEqual(1, subnet_count) def test_remove(self): count = db_api.IpamSubnetManager.delete(self.ctx, self.neutron_subnet_id) self.assertEqual(1, count) subnet_exists = ipam_obj.IpamSubnet.objects_exist( self.ctx, id=self.ipam_subnet_id) self.assertFalse(subnet_exists) def test_remove_non_existent_subnet(self): count = db_api.IpamSubnetManager.delete(self.ctx, 'non-existent') self.assertEqual(0, count) def _validate_ips(self, pools, db_pool): self.assertTrue( any(pool == (str(db_pool.first_ip), str(db_pool.last_ip)) for pool in pools)) def test_create_pool(self): self.subnet_manager.create_pool(self.ctx, self.single_pool[0], self.single_pool[1]) ipam_pools = ipam_obj.IpamAllocationPool.get_objects( self.ctx, ipam_subnet_id=self.ipam_subnet_id) self._validate_ips([self.single_pool], ipam_pools[0]) def test_check_unique_allocation(self): self.assertTrue(self.subnet_manager.check_unique_allocation( self.ctx, self.subnet_ip)) def test_check_unique_allocation_negative(self): self.subnet_manager.create_allocation(self.ctx, self.subnet_ip) self.assertFalse(self.subnet_manager.check_unique_allocation( self.ctx, self.subnet_ip)) def test_list_allocations(self): ips = ['1.2.3.4', '1.2.3.6', '1.2.3.7'] for ip in ips: self.subnet_manager.create_allocation(self.ctx, ip) allocs = self.subnet_manager.list_allocations(self.ctx) self.assertEqual(len(ips), len(allocs)) for allocation in allocs: self.assertIn(str(allocation.ip_address), ips) def _test_create_allocation(self): self.subnet_manager.create_allocation(self.ctx, self.subnet_ip) alloc = ipam_obj.IpamAllocation.get_objects( self.ctx, ipam_subnet_id=self.ipam_subnet_id) self.assertEqual(1, len(alloc)) self.assertEqual(self.subnet_ip, str(alloc[0].ip_address)) return alloc def test_create_allocation(self): self._test_create_allocation() def test_delete_allocation(self): allocs = self._test_create_allocation() self.subnet_manager.delete_allocation(self.ctx, allocs[0].ip_address) alloc_exists = ipam_obj.IpamAllocation.objects_exist( self.ctx, ipam_subnet_id=self.ipam_subnet_id) self.assertFalse(alloc_exists) neutron-12.1.1/neutron/tests/unit/ipam/drivers/neutrondb_ipam/__init__.py0000664000175000017500000000000013553660046026644 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/ipam/drivers/__init__.py0000664000175000017500000000000013553660046023636 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/ipam/test_utils.py0000664000175000017500000000726613553660047022646 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron.ipam import utils from neutron.tests import base class TestIpamUtils(base.BaseTestCase): def test_check_subnet_ip_v4_network(self): self.assertFalse(utils.check_subnet_ip('1.1.1.0/24', '1.1.1.0')) def test_check_subnet_ip_v4_broadcast(self): self.assertFalse(utils.check_subnet_ip('1.1.1.0/24', '1.1.1.255')) def test_check_subnet_ip_v4_valid(self): self.assertTrue(utils.check_subnet_ip('1.1.1.0/24', '1.1.1.1')) self.assertTrue(utils.check_subnet_ip('1.1.1.0/24', '1.1.1.254')) def test_check_subnet_ip_v6_network(self): self.assertTrue(utils.check_subnet_ip('F111::0/64', 'F111::0')) def test_check_subnet_ip_v6_valid(self): self.assertTrue(utils.check_subnet_ip('F111::0/64', 'F111::1')) self.assertTrue(utils.check_subnet_ip('F111::0/64', 'F111::FFFF:FFFF:FFFF:FFFF')) def test_generate_pools_v4_nogateway(self): cidr = '192.168.0.0/24' expected = [netaddr.IPRange('192.168.0.1', '192.168.0.254')] self.assertEqual(expected, utils.generate_pools(cidr, None)) def test_generate_pools_v4_gateway_first(self): cidr = '192.168.0.0/24' gateway = '192.168.0.1' expected = [netaddr.IPRange('192.168.0.2', '192.168.0.254')] self.assertEqual(expected, utils.generate_pools(cidr, gateway)) def test_generate_pools_v4_gateway_last(self): cidr = '192.168.0.0/24' gateway = '192.168.0.254' expected = [netaddr.IPRange('192.168.0.1', '192.168.0.253')] self.assertEqual(expected, utils.generate_pools(cidr, gateway)) def test_generate_pools_v4_32(self): # 32 is special because it should have 1 usable address cidr = '192.168.0.0/32' expected = [netaddr.IPRange('192.168.0.0', '192.168.0.0')] self.assertEqual(expected, utils.generate_pools(cidr, None)) def test_generate_pools_v4_31(self): cidr = '192.168.0.0/31' expected = [] self.assertEqual(expected, utils.generate_pools(cidr, None)) def test_generate_pools_v4_gateway_middle(self): cidr = '192.168.0.0/24' gateway = '192.168.0.128' expected = [netaddr.IPRange('192.168.0.1', '192.168.0.127'), netaddr.IPRange('192.168.0.129', '192.168.0.254')] self.assertEqual(expected, utils.generate_pools(cidr, gateway)) def test_generate_pools_v6_nogateway(self): # other than the difference in the last address, the rest of the # logic is the same as v4 so we only need one test cidr = 'F111::0/64' expected = [netaddr.IPRange('F111::1', 'F111::FFFF:FFFF:FFFF:FFFF')] self.assertEqual(expected, utils.generate_pools(cidr, None)) def test_generate_pools_v6_empty(self): # We want to be sure the range will begin and end with an IPv6 # address, even if an ambiguous ::/64 cidr is given. cidr = '::/64' expected = [netaddr.IPRange('::1', '::FFFF:FFFF:FFFF:FFFF')] self.assertEqual(expected, utils.generate_pools(cidr, None)) neutron-12.1.1/neutron/tests/unit/ipam/__init__.py0000664000175000017500000000000013553660046022160 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/ipam/test_requests.py0000664000175000017500000003770313553660047023360 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import netaddr from neutron_lib import constants from neutron_lib import context from oslo_config import cfg from oslo_utils import netutils from oslo_utils import uuidutils from neutron.ipam import driver from neutron.ipam import exceptions as ipam_exc from neutron.ipam import requests as ipam_req from neutron import manager from neutron.tests import base from neutron.tests.unit.ipam import fake_driver FAKE_IPAM_CLASS = 'neutron.tests.unit.ipam.fake_driver.FakeDriver' class IpamSubnetRequestTestCase(base.BaseTestCase): def setUp(self): super(IpamSubnetRequestTestCase, self).setUp() self.tenant_id = uuidutils.generate_uuid() self.subnet_id = uuidutils.generate_uuid() class TestIpamSubnetRequests(IpamSubnetRequestTestCase): def test_subnet_request(self): pool = ipam_req.SubnetRequest(self.tenant_id, self.subnet_id) self.assertEqual(self.tenant_id, pool.tenant_id) self.assertEqual(self.subnet_id, pool.subnet_id) self.assertIsNone(pool.gateway_ip) self.assertIsNone(pool.allocation_pools) def test_subnet_request_gateway(self): request = ipam_req.SubnetRequest(self.tenant_id, self.subnet_id, gateway_ip='1.2.3.1') self.assertEqual('1.2.3.1', str(request.gateway_ip)) def test_subnet_request_bad_gateway(self): self.assertRaises(netaddr.core.AddrFormatError, ipam_req.SubnetRequest, self.tenant_id, self.subnet_id, gateway_ip='1.2.3.') def test_subnet_request_with_range(self): allocation_pools = [netaddr.IPRange('1.2.3.4', '1.2.3.5'), netaddr.IPRange('1.2.3.7', '1.2.3.9')] request = ipam_req.SubnetRequest(self.tenant_id, self.subnet_id, allocation_pools=allocation_pools) self.assertEqual(allocation_pools, request.allocation_pools) def test_subnet_request_range_not_list(self): self.assertRaises(TypeError, ipam_req.SubnetRequest, self.tenant_id, self.subnet_id, allocation_pools=1) def test_subnet_request_bad_range(self): self.assertRaises(TypeError, ipam_req.SubnetRequest, self.tenant_id, self.subnet_id, allocation_pools=['1.2.3.4']) def test_subnet_request_different_versions(self): pools = [netaddr.IPRange('0.0.0.1', '0.0.0.2'), netaddr.IPRange('::1', '::2')] self.assertRaises(ValueError, ipam_req.SubnetRequest, self.tenant_id, self.subnet_id, allocation_pools=pools) def test_subnet_request_overlap(self): pools = [netaddr.IPRange('0.0.0.10', '0.0.0.20'), netaddr.IPRange('0.0.0.8', '0.0.0.10')] self.assertRaises(ValueError, ipam_req.SubnetRequest, self.tenant_id, self.subnet_id, allocation_pools=pools) class TestIpamAnySubnetRequest(IpamSubnetRequestTestCase): def test_subnet_request(self): request = ipam_req.AnySubnetRequest(self.tenant_id, self.subnet_id, constants.IPv4, 24, gateway_ip='0.0.0.1') self.assertEqual(24, request.prefixlen) def test_subnet_request_bad_prefix_type(self): self.assertRaises(netaddr.core.AddrFormatError, ipam_req.AnySubnetRequest, self.tenant_id, self.subnet_id, constants.IPv4, 'A') def test_subnet_request_bad_prefix(self): self.assertRaises(netaddr.core.AddrFormatError, ipam_req.AnySubnetRequest, self.tenant_id, self.subnet_id, constants.IPv4, 33) self.assertRaises(netaddr.core.AddrFormatError, ipam_req.AnySubnetRequest, self.tenant_id, self.subnet_id, constants.IPv6, 129) def test_subnet_request_gateway(self): request = ipam_req.AnySubnetRequest(self.tenant_id, self.subnet_id, constants.IPv6, 64, gateway_ip='2000::1') self.assertEqual(netaddr.IPAddress('2000::1'), request.gateway_ip) def test_subnet_request_allocation_pool_wrong_version(self): pools = [netaddr.IPRange('0.0.0.4', '0.0.0.5')] self.assertRaises(ipam_exc.IpamValueInvalid, ipam_req.AnySubnetRequest, self.tenant_id, self.subnet_id, constants.IPv6, 64, allocation_pools=pools) def test_subnet_request_allocation_pool_not_in_net(self): pools = [netaddr.IPRange('0.0.0.64', '0.0.0.128')] self.assertRaises(ipam_exc.IpamValueInvalid, ipam_req.AnySubnetRequest, self.tenant_id, self.subnet_id, constants.IPv4, 25, allocation_pools=pools) class TestIpamSpecificSubnetRequest(IpamSubnetRequestTestCase): def test_subnet_request(self): request = ipam_req.SpecificSubnetRequest(self.tenant_id, self.subnet_id, '1.2.3.0/24', gateway_ip='1.2.3.1') self.assertEqual(24, request.prefixlen) self.assertEqual(netaddr.IPAddress('1.2.3.1'), request.gateway_ip) self.assertEqual(netaddr.IPNetwork('1.2.3.0/24'), request.subnet_cidr) def test_subnet_request_gateway(self): request = ipam_req.SpecificSubnetRequest(self.tenant_id, self.subnet_id, '2001::1', gateway_ip='2000::1') self.assertEqual(netaddr.IPAddress('2000::1'), request.gateway_ip) class TestAddressRequest(base.BaseTestCase): # This class doesn't test much. At least running through all of the # constructors may shake out some trivial bugs. EUI64 = ipam_req.AutomaticAddressRequest.EUI64 def test_specific_address_ipv6(self): request = ipam_req.SpecificAddressRequest('2000::45') self.assertEqual(netaddr.IPAddress('2000::45'), request.address) def test_specific_address_ipv4(self): request = ipam_req.SpecificAddressRequest('1.2.3.32') self.assertEqual(netaddr.IPAddress('1.2.3.32'), request.address) def test_any_address(self): ipam_req.AnyAddressRequest() def test_automatic_address_request_eui64(self): subnet_cidr = '2607:f0d0:1002:51::/64' port_mac = 'aa:bb:cc:dd:ee:ff' eui_addr = str(netutils.get_ipv6_addr_by_EUI64(subnet_cidr, port_mac)) request = ipam_req.AutomaticAddressRequest( address_type=self.EUI64, prefix=subnet_cidr, mac=port_mac) self.assertEqual(request.address, netaddr.IPAddress(eui_addr)) def test_automatic_address_request_invalid_address_type_raises(self): self.assertRaises(ipam_exc.InvalidAddressType, ipam_req.AutomaticAddressRequest, address_type='kaboom') def test_automatic_address_request_eui64_no_mac_raises(self): self.assertRaises(ipam_exc.AddressCalculationFailure, ipam_req.AutomaticAddressRequest, address_type=self.EUI64, prefix='meh') def test_automatic_address_request_eui64_alien_param_raises(self): self.assertRaises(ipam_exc.AddressCalculationFailure, ipam_req.AutomaticAddressRequest, address_type=self.EUI64, mac='meh', alien='et', prefix='meh') class TestIpamDriverLoader(base.BaseTestCase): def setUp(self): super(TestIpamDriverLoader, self).setUp() self.ctx = context.get_admin_context() def _verify_fake_ipam_driver_is_loaded(self, driver_name): mgr = manager.NeutronManager ipam_driver = mgr.load_class_for_provider('neutron.ipam_drivers', driver_name) self.assertEqual( fake_driver.FakeDriver, ipam_driver, "loaded ipam driver should be FakeDriver") def _verify_import_error_is_generated(self, driver_name): mgr = manager.NeutronManager self.assertRaises(ImportError, mgr.load_class_for_provider, 'neutron.ipam_drivers', driver_name) def test_ipam_driver_is_loaded_by_class(self): self._verify_fake_ipam_driver_is_loaded(FAKE_IPAM_CLASS) def test_ipam_driver_is_loaded_by_name(self): self._verify_fake_ipam_driver_is_loaded('fake') def test_ipam_driver_raises_import_error(self): self._verify_import_error_is_generated( 'neutron.tests.unit.ipam_req.SomeNonExistentClass') def test_ipam_driver_raises_import_error_for_none(self): self._verify_import_error_is_generated(None) def _load_ipam_driver(self, driver_name, subnet_pool_id): cfg.CONF.set_override("ipam_driver", driver_name) return driver.Pool.get_instance(subnet_pool_id, self.ctx) def test_ipam_driver_is_loaded_from_ipam_driver_config_value(self): ipam_driver = self._load_ipam_driver('fake', None) self.assertIsInstance( ipam_driver, fake_driver.FakeDriver, "loaded ipam driver should be of type FakeDriver") @mock.patch(FAKE_IPAM_CLASS) def test_ipam_driver_is_loaded_with_subnet_pool_id(self, ipam_mock): subnet_pool_id = 'SomePoolID' self._load_ipam_driver('fake', subnet_pool_id) ipam_mock.assert_called_once_with(subnet_pool_id, self.ctx) class TestAddressRequestFactory(base.BaseTestCase): def test_specific_address_request_is_loaded(self): for address in ('10.12.0.15', 'fffe::1'): ip = {'ip_address': address} port = {'device_owner': constants.DEVICE_OWNER_COMPUTE_PREFIX + 'None'} self.assertIsInstance( ipam_req.AddressRequestFactory.get_request(None, port, ip), ipam_req.SpecificAddressRequest) def test_any_address_request_is_loaded(self): for addr in [None, '']: ip = {'ip_address': addr} port = {'device_owner': constants.DEVICE_OWNER_COMPUTE_PREFIX + 'None'} self.assertIsInstance( ipam_req.AddressRequestFactory.get_request(None, port, ip), ipam_req.AnyAddressRequest) def test_automatic_address_request_is_loaded(self): ip = {'mac': '6c:62:6d:de:cf:49', 'subnet_cidr': '2001:470:abcd::/64', 'eui64_address': True} port = {'device_owner': constants.DEVICE_OWNER_COMPUTE_PREFIX + 'None'} self.assertIsInstance( ipam_req.AddressRequestFactory.get_request(None, port, ip), ipam_req.AutomaticAddressRequest) def test_prefernext_address_request_on_dhcp_port(self): ip = {} port = {'device_owner': constants.DEVICE_OWNER_DHCP} self.assertIsInstance( ipam_req.AddressRequestFactory.get_request(None, port, ip), ipam_req.PreferNextAddressRequest) class TestSubnetRequestFactory(IpamSubnetRequestTestCase): def _build_subnet_dict(self, id=None, cidr='192.168.1.0/24', prefixlen=8, ip_version=4): subnet = {'cidr': cidr, 'prefixlen': prefixlen, 'ip_version': ip_version, 'tenant_id': self.tenant_id, 'gateway_ip': None, 'allocation_pools': None, 'id': id or self.subnet_id} subnetpool = {'ip_version': ip_version, 'default_prefixlen': prefixlen} return subnet, subnetpool def test_specific_subnet_request_is_loaded(self): addresses = [ '10.12.0.15/24', '10.12.0.0/24', 'fffe::1/64', 'fffe::/64'] for address in addresses: subnet, subnetpool = self._build_subnet_dict(cidr=address) self.assertIsInstance( ipam_req.SubnetRequestFactory.get_request(None, subnet, subnetpool), ipam_req.SpecificSubnetRequest) def test_any_address_request_is_loaded_for_ipv4(self): subnet, subnetpool = self._build_subnet_dict(cidr=None, ip_version=4) self.assertIsInstance( ipam_req.SubnetRequestFactory.get_request(None, subnet, subnetpool), ipam_req.AnySubnetRequest) def test_any_address_request_is_loaded_for_ipv6(self): subnet, subnetpool = self._build_subnet_dict(cidr=None, ip_version=6) self.assertIsInstance( ipam_req.SubnetRequestFactory.get_request(None, subnet, subnetpool), ipam_req.AnySubnetRequest) def test_args_are_passed_to_specific_request(self): subnet, subnetpool = self._build_subnet_dict() request = ipam_req.SubnetRequestFactory.get_request(None, subnet, subnetpool) self.assertIsInstance(request, ipam_req.SpecificSubnetRequest) self.assertEqual(self.tenant_id, request.tenant_id) self.assertEqual(self.subnet_id, request.subnet_id) self.assertIsNone(request.gateway_ip) self.assertIsNone(request.allocation_pools) class TestGetRequestFactory(base.BaseTestCase): def setUp(self): super(TestGetRequestFactory, self).setUp() cfg.CONF.set_override('ipam_driver', 'fake') self.driver = driver.Pool.get_instance(None, None) def test_get_subnet_request_factory(self): self.assertEqual( self.driver.get_subnet_request_factory(), ipam_req.SubnetRequestFactory) def test_get_address_request_factory(self): self.assertEqual( self.driver.get_address_request_factory(), ipam_req.AddressRequestFactory) neutron-12.1.1/neutron/tests/unit/ipam/test_subnet_alloc.py0000664000175000017500000002405513553660047024153 0ustar zuulzuul00000000000000# Copyright (c) 2015 Hewlett-Packard Development Company, L.P. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import netaddr from neutron_lib import constants from neutron_lib import context from neutron_lib.plugins import directory from oslo_config import cfg from oslo_db import exception as db_exc from oslo_utils import uuidutils from neutron.common import exceptions as n_exc from neutron.db import api as db_api from neutron.ipam import requests as ipam_req from neutron.ipam import subnet_alloc from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit import testlib_api class TestSubnetAllocation(testlib_api.SqlTestCase): def setUp(self): super(TestSubnetAllocation, self).setUp() self._tenant_id = 'test-tenant' self.setup_coreplugin(test_db_base_plugin_v2.DB_PLUGIN_KLASS) self.plugin = directory.get_plugin() self.ctx = context.get_admin_context() cfg.CONF.set_override('allow_overlapping_ips', True) def _create_subnet_pool(self, plugin, ctx, name, prefix_list, min_prefixlen, ip_version, max_prefixlen=constants.ATTR_NOT_SPECIFIED, default_prefixlen=constants.ATTR_NOT_SPECIFIED, default_quota=constants.ATTR_NOT_SPECIFIED, shared=False, is_default=False): subnetpool = {'subnetpool': {'name': name, 'tenant_id': self._tenant_id, 'prefixes': prefix_list, 'min_prefixlen': min_prefixlen, 'max_prefixlen': max_prefixlen, 'default_prefixlen': default_prefixlen, 'shared': shared, 'is_default': is_default, 'default_quota': default_quota}} return plugin.create_subnetpool(ctx, subnetpool) def _get_subnetpool(self, ctx, plugin, id): return plugin.get_subnetpool(ctx, id) def test_allocate_any_subnet(self): prefix_list = ['10.1.0.0/16', '192.168.1.0/24'] sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', prefix_list, 21, 4) sp = self.plugin._get_subnetpool(self.ctx, sp['id']) with db_api.context_manager.writer.using(self.ctx): sa = subnet_alloc.SubnetAllocator(sp, self.ctx) req = ipam_req.AnySubnetRequest(self._tenant_id, uuidutils.generate_uuid(), constants.IPv4, 21) res = sa.allocate_subnet(req) detail = res.get_details() prefix_set = netaddr.IPSet(iterable=prefix_list) allocated_set = netaddr.IPSet(iterable=[detail.subnet_cidr]) self.assertTrue(allocated_set.issubset(prefix_set)) self.assertEqual(21, detail.prefixlen) def test_allocate_specific_subnet(self): sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', ['10.1.0.0/16', '192.168.1.0/24'], 21, 4) with db_api.context_manager.writer.using(self.ctx): sp = self.plugin._get_subnetpool(self.ctx, sp['id']) sa = subnet_alloc.SubnetAllocator(sp, self.ctx) req = ipam_req.SpecificSubnetRequest(self._tenant_id, uuidutils.generate_uuid(), '10.1.2.0/24') res = sa.allocate_subnet(req) detail = res.get_details() sp = self._get_subnetpool(self.ctx, self.plugin, sp['id']) self.assertEqual('10.1.2.0/24', str(detail.subnet_cidr)) self.assertEqual(24, detail.prefixlen) def test_insufficient_prefix_space_for_any_allocation(self): sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', ['10.1.1.0/24', '192.168.1.0/24'], 21, 4) sp = self.plugin._get_subnetpool(self.ctx, sp['id']) sa = subnet_alloc.SubnetAllocator(sp, self.ctx) req = ipam_req.AnySubnetRequest(self._tenant_id, uuidutils.generate_uuid(), constants.IPv4, 21) self.assertRaises(n_exc.SubnetAllocationError, sa.allocate_subnet, req) def test_insufficient_prefix_space_for_specific_allocation(self): sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', ['10.1.0.0/24'], 21, 4) sp = self.plugin._get_subnetpool(self.ctx, sp['id']) sa = subnet_alloc.SubnetAllocator(sp, self.ctx) req = ipam_req.SpecificSubnetRequest(self._tenant_id, uuidutils.generate_uuid(), '10.1.0.0/21') self.assertRaises(n_exc.SubnetAllocationError, sa.allocate_subnet, req) def test_allocate_any_subnet_gateway(self): sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', ['10.1.0.0/16', '192.168.1.0/24'], 21, 4) sp = self.plugin._get_subnetpool(self.ctx, sp['id']) with db_api.context_manager.writer.using(self.ctx): sa = subnet_alloc.SubnetAllocator(sp, self.ctx) req = ipam_req.AnySubnetRequest(self._tenant_id, uuidutils.generate_uuid(), constants.IPv4, 21) res = sa.allocate_subnet(req) detail = res.get_details() self.assertEqual(detail.gateway_ip, detail.subnet_cidr.network + 1) def test_allocate_specific_subnet_specific_gateway(self): sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', ['10.1.0.0/16', '192.168.1.0/24'], 21, 4) sp = self.plugin._get_subnetpool(self.ctx, sp['id']) with db_api.context_manager.writer.using(self.ctx): sa = subnet_alloc.SubnetAllocator(sp, self.ctx) req = ipam_req.SpecificSubnetRequest(self._tenant_id, uuidutils.generate_uuid(), '10.1.2.0/24', gateway_ip='10.1.2.254') res = sa.allocate_subnet(req) detail = res.get_details() self.assertEqual(netaddr.IPAddress('10.1.2.254'), detail.gateway_ip) def test_allocate_specific_ipv6_subnet_specific_gateway(self): # Same scenario as described in bug #1466322 sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', ['2210::/64'], 64, 6) sp = self.plugin._get_subnetpool(self.ctx, sp['id']) with db_api.context_manager.writer.using(self.ctx): sa = subnet_alloc.SubnetAllocator(sp, self.ctx) req = ipam_req.SpecificSubnetRequest(self._tenant_id, uuidutils.generate_uuid(), '2210::/64', '2210::ffff:ffff:ffff:ffff') res = sa.allocate_subnet(req) detail = res.get_details() self.assertEqual(netaddr.IPAddress('2210::ffff:ffff:ffff:ffff'), detail.gateway_ip) def test__allocation_value_for_tenant_no_allocations(self): sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', ['10.1.0.0/16', '192.168.1.0/24'], 21, 4) sa = subnet_alloc.SubnetAllocator(sp, self.ctx) value = sa._allocations_used_by_tenant(32) self.assertEqual(0, value) def test_subnetpool_default_quota_exceeded(self): sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', ['fe80::/48'], 48, 6, default_quota=1) sp = self.plugin._get_subnetpool(self.ctx, sp['id']) sa = subnet_alloc.SubnetAllocator(sp, self.ctx) req = ipam_req.SpecificSubnetRequest(self._tenant_id, uuidutils.generate_uuid(), 'fe80::/63') self.assertRaises(n_exc.SubnetPoolQuotaExceeded, sa.allocate_subnet, req) def test_subnetpool_concurrent_allocation_exception(self): sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', ['fe80::/48'], 48, 6, default_quota=1) sp = self.plugin._get_subnetpool(self.ctx, sp['id']) sa = subnet_alloc.SubnetAllocator(sp, self.ctx) req = ipam_req.SpecificSubnetRequest(self._tenant_id, uuidutils.generate_uuid(), 'fe80::/63') with mock.patch("sqlalchemy.orm.query.Query.update", return_value=0): self.assertRaises(db_exc.RetryRequest, sa.allocate_subnet, req) neutron-12.1.1/neutron/tests/unit/ipam/fake_driver.py0000664000175000017500000000220013553660046022706 0ustar zuulzuul00000000000000# Copyright (c) 2015 Infoblox Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.ipam import driver class FakeDriver(driver.Pool): """Fake IPAM driver for tests only Just implement IPAM Driver interface without any functionality inside """ def allocate_subnet(self, subnet): return driver.Subnet() def get_subnet(self, cidr): return driver.Subnet() def get_allocator(self, subnet_ids): return driver.SubnetGroup() def update_subnet(self, request): return driver.Subnet() def remove_subnet(self, cidr): pass neutron-12.1.1/neutron/tests/unit/quota/0000775000175000017500000000000013553660157020267 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/quota/test_resource_registry.py0000664000175000017500000001762113553660046025463 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from oslo_config import cfg import testtools from neutron.quota import resource from neutron.quota import resource_registry from neutron.tests import base from neutron.tests.unit import quota as test_quota class TestResourceRegistry(base.DietTestCase): def setUp(self): super(TestResourceRegistry, self).setUp() self.registry = resource_registry.ResourceRegistry.get_instance() # clean up the registry at every test self.registry.unregister_resources() def test_set_tracked_resource_new_resource(self): self.registry.set_tracked_resource('meh', test_quota.MehModel) self.assertEqual(test_quota.MehModel, self.registry._tracked_resource_mappings['meh']) def test_set_tracked_resource_existing_with_override(self): self.test_set_tracked_resource_new_resource() self.registry.set_tracked_resource('meh', test_quota.OtherMehModel, override=True) # Override is set to True, the model class should change self.assertEqual(test_quota.OtherMehModel, self.registry._tracked_resource_mappings['meh']) def test_set_tracked_resource_existing_no_override(self): self.test_set_tracked_resource_new_resource() self.registry.set_tracked_resource('meh', test_quota.OtherMehModel) # Override is set to false, the model class should not change self.assertEqual(test_quota.MehModel, self.registry._tracked_resource_mappings['meh']) def _test_register_resource_by_name(self, resource_name, expected_type): self.assertNotIn(resource_name, self.registry._resources) self.registry.register_resource_by_name(resource_name) self.assertIn(resource_name, self.registry._resources) self.assertIsInstance(self.registry.get_resource(resource_name), expected_type) def test_register_resource_by_name_tracked(self): self.test_set_tracked_resource_new_resource() self._test_register_resource_by_name('meh', resource.TrackedResource) def test_register_resource_by_name_not_tracked(self): self._test_register_resource_by_name('meh', resource.CountableResource) def test_tracked_resource_error_if_already_registered_as_untracked(self): self.registry.register_resource_by_name('meh') with testtools.ExpectedException(RuntimeError): self.registry.set_tracked_resource('meh', test_quota.MehModel) # ensure unregister works self.registry.unregister_resources() def test_register_resource_by_name_with_tracking_disabled_by_config(self): cfg.CONF.set_override('track_quota_usage', False, group='QUOTAS') # DietTestCase does not automatically cleans configuration overrides self.addCleanup(cfg.CONF.reset) self.registry.set_tracked_resource('meh', test_quota.MehModel) self.assertNotIn( 'meh', self.registry._tracked_resource_mappings) self._test_register_resource_by_name('meh', resource.CountableResource) class TestAuxiliaryFunctions(base.DietTestCase): def setUp(self): super(TestAuxiliaryFunctions, self).setUp() self.registry = resource_registry.ResourceRegistry.get_instance() # clean up the registry at every test self.registry.unregister_resources() def test_resync_tracking_disabled(self): cfg.CONF.set_override('track_quota_usage', False, group='QUOTAS') # DietTestCase does not automatically cleans configuration overrides self.addCleanup(cfg.CONF.reset) with mock.patch('neutron.quota.resource.' 'TrackedResource.resync') as mock_resync: self.registry.set_tracked_resource('meh', test_quota.MehModel) self.registry.register_resource_by_name('meh') resource_registry.resync_resource(mock.ANY, 'meh', 'tenant_id') self.assertEqual(0, mock_resync.call_count) def test_resync_tracked_resource(self): with mock.patch('neutron.quota.resource.' 'TrackedResource.resync') as mock_resync: self.registry.set_tracked_resource('meh', test_quota.MehModel) self.registry.register_resource_by_name('meh') resource_registry.resync_resource(mock.ANY, 'meh', 'tenant_id') mock_resync.assert_called_once_with(mock.ANY, 'tenant_id') def test_resync_non_tracked_resource(self): with mock.patch('neutron.quota.resource.' 'TrackedResource.resync') as mock_resync: self.registry.register_resource_by_name('meh') resource_registry.resync_resource(mock.ANY, 'meh', 'tenant_id') self.assertEqual(0, mock_resync.call_count) def test_set_resources_dirty_invoked_with_tracking_disabled(self): cfg.CONF.set_override('track_quota_usage', False, group='QUOTAS') # DietTestCase does not automatically cleans configuration overrides self.addCleanup(cfg.CONF.reset) with mock.patch('neutron.quota.resource.' 'TrackedResource.mark_dirty') as mock_mark_dirty: self.registry.set_tracked_resource('meh', test_quota.MehModel) self.registry.register_resource_by_name('meh') resource_registry.set_resources_dirty(mock.ANY) self.assertEqual(0, mock_mark_dirty.call_count) def test_set_resources_dirty_no_dirty_resource(self): ctx = context.Context('user_id', 'tenant_id', is_admin=False, is_advsvc=False) with mock.patch('neutron.quota.resource.' 'TrackedResource.mark_dirty') as mock_mark_dirty: self.registry.set_tracked_resource('meh', test_quota.MehModel) self.registry.register_resource_by_name('meh') res = self.registry.get_resource('meh') # This ensures dirty is false res._dirty_tenants.clear() resource_registry.set_resources_dirty(ctx) self.assertEqual(0, mock_mark_dirty.call_count) def test_set_resources_dirty_no_tracked_resource(self): ctx = context.Context('user_id', 'tenant_id', is_admin=False, is_advsvc=False) with mock.patch('neutron.quota.resource.' 'TrackedResource.mark_dirty') as mock_mark_dirty: self.registry.register_resource_by_name('meh') resource_registry.set_resources_dirty(ctx) self.assertEqual(0, mock_mark_dirty.call_count) def test_set_resources_dirty(self): ctx = context.Context('user_id', 'tenant_id', is_admin=False, is_advsvc=False) with mock.patch('neutron.quota.resource.' 'TrackedResource.mark_dirty') as mock_mark_dirty: self.registry.set_tracked_resource('meh', test_quota.MehModel) self.registry.register_resource_by_name('meh') res = self.registry.get_resource('meh') # This ensures dirty is true res._dirty_tenants.add('tenant_id') resource_registry.set_resources_dirty(ctx) mock_mark_dirty.assert_called_once_with(ctx) neutron-12.1.1/neutron/tests/unit/quota/test_resource.py0000664000175000017500000003666713553660047023547 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from neutron_lib.plugins import constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import uuidutils import testtools from neutron.db import api as db_api from neutron.db.quota import api as quota_api from neutron.quota import resource from neutron.tests import base from neutron.tests.unit import quota as test_quota from neutron.tests.unit import testlib_api DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' meh_quota_flag = 'quota_meh' meh_quota_opts = [cfg.IntOpt(meh_quota_flag, default=99)] class TestResource(base.DietTestCase): """Unit tests for neutron.quota.resource.BaseResource""" def test_create_resource_without_plural_name(self): res = resource.BaseResource('foo', None) self.assertEqual('foos', res.plural_name) res = resource.BaseResource('foy', None) self.assertEqual('foies', res.plural_name) def test_create_resource_with_plural_name(self): res = resource.BaseResource('foo', None, plural_name='foopsies') self.assertEqual('foopsies', res.plural_name) def test_resource_default_value(self): res = resource.BaseResource('foo', 'foo_quota') with mock.patch('oslo_config.cfg.CONF') as mock_cfg: mock_cfg.QUOTAS.foo_quota = 99 self.assertEqual(99, res.default) def test_resource_negative_default_value(self): res = resource.BaseResource('foo', 'foo_quota') with mock.patch('oslo_config.cfg.CONF') as mock_cfg: mock_cfg.QUOTAS.foo_quota = -99 self.assertEqual(-1, res.default) class TestTrackedResource(testlib_api.SqlTestCase): def _add_data(self, tenant_id=None): session = db_api.get_writer_session() with session.begin(): tenant_id = tenant_id or self.tenant_id session.add(test_quota.MehModel( meh='meh_%s' % uuidutils.generate_uuid(), tenant_id=tenant_id)) session.add(test_quota.MehModel( meh='meh_%s' % uuidutils.generate_uuid(), tenant_id=tenant_id)) def _delete_data(self): session = db_api.get_writer_session() with session.begin(): query = session.query(test_quota.MehModel).filter_by( tenant_id=self.tenant_id) for item in query: session.delete(item) def _update_data(self): session = db_api.get_writer_session() with session.begin(): query = session.query(test_quota.MehModel).filter_by( tenant_id=self.tenant_id) for item in query: item['meh'] = 'meh-%s' % item['meh'] session.add(item) def setUp(self): super(TestTrackedResource, self).setUp() self.setup_coreplugin(DB_PLUGIN_KLASS) self.resource = 'meh' self.other_resource = 'othermeh' self.tenant_id = 'meh' self.context = context.Context( user_id='', tenant_id=self.tenant_id, is_admin=False) def _create_resource(self): res = resource.TrackedResource( self.resource, test_quota.MehModel, meh_quota_flag) res.register_events() return res def _create_other_resource(self): res = resource.TrackedResource( self.other_resource, test_quota.OtherMehModel, meh_quota_flag) res.register_events() return res def test_bulk_delete_protection(self): self._create_resource() with testtools.ExpectedException(RuntimeError): ctx = context.get_admin_context() ctx.session.query(test_quota.MehModel).delete() def test_count_first_call_with_dirty_false(self): quota_api.set_quota_usage( self.context, self.resource, self.tenant_id, in_use=1) res = self._create_resource() self._add_data() # explicitly set dirty flag to False quota_api.set_all_quota_usage_dirty( self.context, self.resource, dirty=False) # Expect correct count to be returned anyway since the first call to # count() always resyncs with the db self.assertEqual(2, res.count(self.context, None, self.tenant_id)) def test_count_reserved(self): res = self._create_resource() quota_api.create_reservation(self.context, self.tenant_id, {res.name: 1}) self.assertEqual(1, res.count_reserved(self.context, self.tenant_id)) def test_count_used_first_call_with_dirty_false(self): quota_api.set_quota_usage( self.context, self.resource, self.tenant_id, in_use=1) res = self._create_resource() self._add_data() # explicitly set dirty flag to False quota_api.set_all_quota_usage_dirty( self.context, self.resource, dirty=False) # Expect correct count_used to be returned # anyway since the first call to # count_used() always resyncs with the db self.assertEqual(2, res.count_used(self.context, self.tenant_id)) def _test_count(self): res = self._create_resource() quota_api.set_quota_usage( self.context, res.name, self.tenant_id, in_use=0) self._add_data() return res def test_count_with_dirty_false(self): res = self._test_count() res.count(self.context, None, self.tenant_id) # At this stage count has been invoked, and the dirty flag should be # false. Another invocation of count should not query the model class set_quota = 'neutron.db.quota.api.set_quota_usage' with mock.patch(set_quota) as mock_set_quota: self.assertEqual(0, mock_set_quota.call_count) self.assertEqual(2, res.count(self.context, None, self.tenant_id)) def test_count_used_with_dirty_false(self): res = self._test_count() res.count_used(self.context, self.tenant_id) # At this stage count_used has been invoked, # and the dirty flag should be false. Another invocation # of count_used should not query the model class set_quota = 'neutron.db.quota.api.set_quota_usage' with mock.patch(set_quota) as mock_set_quota: self.assertEqual(0, mock_set_quota.call_count) self.assertEqual(2, res.count_used(self.context, self.tenant_id)) def test_count_with_dirty_true_resync(self): res = self._test_count() # Expect correct count to be returned, which also implies # set_quota_usage has been invoked with the correct parameters self.assertEqual(2, res.count(self.context, None, self.tenant_id, resync_usage=True)) def test_count_used_with_dirty_true_resync(self): res = self._test_count() # Expect correct count_used to be returned, which also implies # set_quota_usage has been invoked with the correct parameters self.assertEqual(2, res.count_used(self.context, self.tenant_id, resync_usage=True)) def test_count_with_dirty_true_resync_calls_set_quota_usage(self): res = self._test_count() set_quota_usage = 'neutron.db.quota.api.set_quota_usage' with mock.patch(set_quota_usage) as mock_set_quota_usage: quota_api.set_quota_usage_dirty(self.context, self.resource, self.tenant_id) res.count(self.context, None, self.tenant_id, resync_usage=True) mock_set_quota_usage.assert_called_once_with( self.context, self.resource, self.tenant_id, in_use=2) def test_count_used_with_dirty_true_resync_calls_set_quota_usage(self): res = self._test_count() set_quota_usage = 'neutron.db.quota.api.set_quota_usage' with mock.patch(set_quota_usage) as mock_set_quota_usage: quota_api.set_quota_usage_dirty(self.context, self.resource, self.tenant_id) res.count_used(self.context, self.tenant_id, resync_usage=True) mock_set_quota_usage.assert_called_once_with( self.context, self.resource, self.tenant_id, in_use=2) def test_count_with_dirty_true_no_usage_info(self): res = self._create_resource() self._add_data() # Invoke count without having usage info in DB - Expect correct # count to be returned self.assertEqual(2, res.count(self.context, None, self.tenant_id)) def test_count_used_with_dirty_true_no_usage_info(self): res = self._create_resource() self._add_data() # Invoke count_used without having usage info in DB - Expect correct # count_used to be returned self.assertEqual(2, res.count_used(self.context, self.tenant_id)) def test_count_with_dirty_true_no_usage_info_calls_set_quota_usage(self): res = self._create_resource() self._add_data() set_quota_usage = 'neutron.db.quota.api.set_quota_usage' with mock.patch(set_quota_usage) as mock_set_quota_usage: quota_api.set_quota_usage_dirty(self.context, self.resource, self.tenant_id) res.count(self.context, None, self.tenant_id, resync_usage=True) mock_set_quota_usage.assert_called_once_with( self.context, self.resource, self.tenant_id, in_use=2) def test_count_used_with_dirty_true_no_usage_info_calls_set_quota_usage( self): res = self._create_resource() self._add_data() set_quota_usage = 'neutron.db.quota.api.set_quota_usage' with mock.patch(set_quota_usage) as mock_set_quota_usage: quota_api.set_quota_usage_dirty(self.context, self.resource, self.tenant_id) res.count_used(self.context, self.tenant_id, resync_usage=True) mock_set_quota_usage.assert_called_once_with( self.context, self.resource, self.tenant_id, in_use=2) def test_add_delete_data_triggers_event(self): res = self._create_resource() other_res = self._create_other_resource() # Validate dirty tenants since mock does not work well with SQLAlchemy # event handlers. self._add_data() self._add_data('someone_else') self.assertEqual(2, len(res._dirty_tenants)) # Also, the dirty flag should not be set for other resources self.assertEqual(0, len(other_res._dirty_tenants)) self.assertIn(self.tenant_id, res._dirty_tenants) self.assertIn('someone_else', res._dirty_tenants) def test_delete_data_triggers_event(self): res = self._create_resource() self._add_data() self._add_data('someone_else') # Artificially clear _dirty_tenants res._dirty_tenants.clear() self._delete_data() # We did not delete "someone_else", so expect only a single dirty # tenant self.assertEqual(1, len(res._dirty_tenants)) self.assertIn(self.tenant_id, res._dirty_tenants) def test_update_does_not_trigger_event(self): res = self._create_resource() self._add_data() self._add_data('someone_else') # Artificially clear _dirty_tenants res._dirty_tenants.clear() self._update_data() self.assertEqual(0, len(res._dirty_tenants)) def test_mark_dirty(self): res = self._create_resource() self._add_data() self._add_data('someone_else') set_quota_usage = 'neutron.db.quota.api.set_quota_usage_dirty' with mock.patch(set_quota_usage) as mock_set_quota_usage: res.mark_dirty(self.context) self.assertEqual(2, mock_set_quota_usage.call_count) mock_set_quota_usage.assert_any_call( self.context, self.resource, self.tenant_id) mock_set_quota_usage.assert_any_call( self.context, self.resource, 'someone_else') def test_mark_dirty_no_dirty_tenant(self): res = self._create_resource() set_quota_usage = 'neutron.db.quota.api.set_quota_usage_dirty' with mock.patch(set_quota_usage) as mock_set_quota_usage: res.mark_dirty(self.context) self.assertFalse(mock_set_quota_usage.call_count) def test_resync(self): res = self._create_resource() self._add_data() res.mark_dirty(self.context) # self.tenant_id now is out of sync set_quota_usage = 'neutron.db.quota.api.set_quota_usage' with mock.patch(set_quota_usage) as mock_set_quota_usage: res.resync(self.context, self.tenant_id) # and now it should be in sync self.assertNotIn(self.tenant_id, res._out_of_sync_tenants) mock_set_quota_usage.assert_called_once_with( self.context, self.resource, self.tenant_id, in_use=2) class Test_CountResource(base.BaseTestCase): def test_all_plugins_checked(self): plugin1 = mock.Mock() plugin2 = mock.Mock() plugins = {'plugin1': plugin1, 'plugin2': plugin2} for name, plugin in plugins.items(): plugin.get_floatingips_count.side_effect = NotImplementedError plugin.get_floatingips.side_effect = NotImplementedError directory.add_plugin(name, plugin) context = mock.Mock() collection_name = 'floatingips' tenant_id = 'fakeid' self.assertRaises( NotImplementedError, resource._count_resource, context, collection_name, tenant_id) for plugin in plugins.values(): for func in (plugin.get_floatingips_count, plugin.get_floatingips): func.assert_called_with( context, filters={'tenant_id': [tenant_id]}) def test_core_plugin_checked_first(self): plugin1 = mock.Mock() plugin2 = mock.Mock() plugin1.get_floatingips_count.side_effect = NotImplementedError plugin1.get_floatingips.side_effect = NotImplementedError directory.add_plugin('plugin1', plugin1) plugin2.get_floatingips_count.return_value = 10 directory.add_plugin(constants.CORE, plugin2) context = mock.Mock() collection_name = 'floatingips' tenant_id = 'fakeid' self.assertEqual( 10, resource._count_resource(context, collection_name, tenant_id)) neutron-12.1.1/neutron/tests/unit/quota/__init__.py0000664000175000017500000000171413553660046022400 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa # Model classes for test resources class MehModel(model_base.BASEV2, model_base.HasProject): meh = sa.Column(sa.String(8), primary_key=True) class OtherMehModel(model_base.BASEV2, model_base.HasProject): othermeh = sa.Column(sa.String(8), primary_key=True) neutron-12.1.1/neutron/tests/unit/extensions/0000775000175000017500000000000013553660157021335 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/extensions/test_address_scope.py0000664000175000017500000006257613553660047025602 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib import mock import netaddr from neutron_lib.api.definitions import address_scope as apidef from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context import webob.exc from neutron.db import address_scope_db from neutron.db import db_base_plugin_v2 from neutron.extensions import address_scope as ext_address_scope from neutron.tests.unit.db import test_db_base_plugin_v2 DB_PLUGIN_KLASS = ('neutron.tests.unit.extensions.test_address_scope.' 'AddressScopeTestPlugin') class AddressScopeTestExtensionManager(object): def get_resources(self): return ext_address_scope.Address_scope.get_resources() def get_actions(self): return [] def get_request_extensions(self): return [] class AddressScopeTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def _create_address_scope(self, fmt, ip_version=constants.IP_VERSION_4, expected_res_status=None, admin=False, **kwargs): address_scope = {'address_scope': {}} address_scope['address_scope']['ip_version'] = ip_version for k, v in kwargs.items(): address_scope['address_scope'][k] = str(v) address_scope_req = self.new_create_request('address-scopes', address_scope, fmt) if not admin: neutron_context = context.Context('', kwargs.get('tenant_id', self._tenant_id)) address_scope_req.environ['neutron.context'] = neutron_context address_scope_res = address_scope_req.get_response(self.ext_api) if expected_res_status: self.assertEqual(expected_res_status, address_scope_res.status_int) return address_scope_res def _make_address_scope(self, fmt, ip_version, admin=False, **kwargs): res = self._create_address_scope(fmt, ip_version, admin=admin, **kwargs) if res.status_int >= webob.exc.HTTPClientError.code: raise webob.exc.HTTPClientError(code=res.status_int) return self.deserialize(fmt, res) @contextlib.contextmanager def address_scope(self, ip_version=constants.IP_VERSION_4, admin=False, **kwargs): addr_scope = self._make_address_scope(self.fmt, ip_version, admin, **kwargs) yield addr_scope def _test_create_address_scope(self, ip_version=constants.IP_VERSION_4, admin=False, expected=None, **kwargs): keys = kwargs.copy() keys.setdefault('tenant_id', self._tenant_id) with self.address_scope(ip_version, admin=admin, **keys) as addr_scope: keys['ip_version'] = ip_version self._validate_resource(addr_scope, keys, 'address_scope') if expected: self._compare_resource(addr_scope, expected, 'address_scope') return addr_scope def _test_update_address_scope(self, addr_scope_id, data, admin=False, expected=None, tenant_id=None): update_req = self.new_update_request( 'address-scopes', data, addr_scope_id) if not admin: neutron_context = context.Context('', tenant_id or self._tenant_id) update_req.environ['neutron.context'] = neutron_context update_res = update_req.get_response(self.ext_api) if expected: addr_scope = self.deserialize(self.fmt, update_res) self._compare_resource(addr_scope, expected, 'address_scope') return addr_scope return update_res class AddressScopeTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, address_scope_db.AddressScopeDbMixin): __native_pagination_support = True __native_sorting_support = True supported_extension_aliases = ["address-scope"] class TestAddressScope(AddressScopeTestCase): def setUp(self): plugin = DB_PLUGIN_KLASS ext_mgr = AddressScopeTestExtensionManager() super(TestAddressScope, self).setUp(plugin=plugin, ext_mgr=ext_mgr) def test_create_address_scope_ipv4(self): expected_addr_scope = {'name': 'foo-address-scope', 'tenant_id': self._tenant_id, 'shared': False, 'ip_version': constants.IP_VERSION_4} self._test_create_address_scope(name='foo-address-scope', expected=expected_addr_scope) def test_create_address_scope_ipv6(self): expected_addr_scope = {'name': 'foo-address-scope', 'tenant_id': self._tenant_id, 'shared': False, 'ip_version': constants.IP_VERSION_6} self._test_create_address_scope(constants.IP_VERSION_6, name='foo-address-scope', expected=expected_addr_scope) def test_create_address_scope_empty_name(self): expected_addr_scope = {'name': '', 'tenant_id': self._tenant_id, 'shared': False} self._test_create_address_scope(name='', expected=expected_addr_scope) # no name specified self._test_create_address_scope(expected=expected_addr_scope) def test_create_address_scope_shared_admin(self): expected_addr_scope = {'name': 'foo-address-scope', 'shared': True} self._test_create_address_scope(name='foo-address-scope', admin=True, shared=True, expected=expected_addr_scope) def test_created_address_scope_shared_non_admin(self): res = self._create_address_scope(self.fmt, name='foo-address-scope', tenant_id=self._tenant_id, admin=False, shared=True) self.assertEqual(webob.exc.HTTPForbidden.code, res.status_int) def test_created_address_scope_specify_id(self): res = self._create_address_scope(self.fmt, name='foo-address-scope', id='foo-id') self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_delete_address_scope(self): with self.address_scope(name='foo-address-scope') as addr_scope: self._delete('address-scopes', addr_scope['address_scope']['id']) self._show('address-scopes', addr_scope['address_scope']['id'], expected_code=webob.exc.HTTPNotFound.code) def test_update_address_scope(self): addr_scope = self._test_create_address_scope(name='foo-address-scope') data = {'address_scope': {'name': 'bar-address-scope'}} self._test_update_address_scope(addr_scope['address_scope']['id'], data, expected=data['address_scope']) def test_update_address_scope_shared_true_admin(self): addr_scope = self._test_create_address_scope(name='foo-address-scope') data = {'address_scope': {'shared': True}} self._test_update_address_scope(addr_scope['address_scope']['id'], data, admin=True, expected=data['address_scope']) def test_update_address_scope_shared_true_non_admin(self): addr_scope = self._test_create_address_scope(name='foo-address-scope') data = {'address_scope': {'shared': True}} res = self._test_update_address_scope( addr_scope['address_scope']['id'], data, admin=False) self.assertEqual(webob.exc.HTTPForbidden.code, res.status_int) def test_update_address_scope_shared_false_admin(self): addr_scope = self._test_create_address_scope(name='foo-address-scope', admin=True, shared=True) data = {'address_scope': {'shared': False}} res = self._test_update_address_scope( addr_scope['address_scope']['id'], data, admin=True) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_get_address_scope(self): addr_scope = self._test_create_address_scope(name='foo-address-scope') req = self.new_show_request('address-scopes', addr_scope['address_scope']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(addr_scope['address_scope']['id'], res['address_scope']['id']) def test_get_address_scope_different_tenants_not_shared(self): addr_scope = self._test_create_address_scope(name='foo-address-scope') req = self.new_show_request('address-scopes', addr_scope['address_scope']['id']) neutron_context = context.Context('', 'not-the-owner') req.environ['neutron.context'] = neutron_context res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_get_address_scope_different_tenants_shared(self): addr_scope = self._test_create_address_scope(name='foo-address-scope', shared=True, admin=True) req = self.new_show_request('address-scopes', addr_scope['address_scope']['id']) neutron_context = context.Context('', 'test-tenant-2') req.environ['neutron.context'] = neutron_context res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(addr_scope['address_scope']['id'], res['address_scope']['id']) def test_list_address_scopes(self): self._test_create_address_scope(name='foo-address-scope') self._test_create_address_scope(constants.IP_VERSION_6, name='bar-address-scope') res = self._list('address-scopes') self.assertEqual(2, len(res['address_scopes'])) def test_list_address_scopes_different_tenants_shared(self): self._test_create_address_scope(name='foo-address-scope', shared=True, admin=True) admin_res = self._list('address-scopes') mortal_res = self._list( 'address-scopes', neutron_context=context.Context('', 'not-the-owner')) self.assertEqual(1, len(admin_res['address_scopes'])) self.assertEqual(1, len(mortal_res['address_scopes'])) def test_list_address_scopes_different_tenants_not_shared(self): self._test_create_address_scope(constants.IP_VERSION_6, name='foo-address-scope') admin_res = self._list('address-scopes') mortal_res = self._list( 'address-scopes', neutron_context=context.Context('', 'not-the-owner')) self.assertEqual(1, len(admin_res['address_scopes'])) self.assertEqual(0, len(mortal_res['address_scopes'])) class TestSubnetPoolsWithAddressScopes(AddressScopeTestCase): def setUp(self): plugin = DB_PLUGIN_KLASS ext_mgr = AddressScopeTestExtensionManager() super(TestSubnetPoolsWithAddressScopes, self).setUp(plugin=plugin, ext_mgr=ext_mgr) def _test_create_subnetpool(self, prefixes, expected=None, admin=False, **kwargs): keys = kwargs.copy() keys.setdefault('tenant_id', self._tenant_id) with self.subnetpool(prefixes, admin, **keys) as subnetpool: self._validate_resource(subnetpool, keys, 'subnetpool') if expected: self._compare_resource(subnetpool, expected, 'subnetpool') return subnetpool def test_create_subnetpool_associate_address_scope(self): with self.address_scope(name='foo-address-scope') as addr_scope: address_scope_id = addr_scope['address_scope']['id'] subnet = netaddr.IPNetwork('10.10.10.0/24') expected = {'address_scope_id': address_scope_id} self._test_create_subnetpool([subnet.cidr], expected=expected, name='foo-subnetpool', min_prefixlen='21', address_scope_id=address_scope_id) def test_create_subnetpool_associate_invalid_address_scope(self): self.assertRaises( webob.exc.HTTPClientError, self._test_create_subnetpool, [], min_prefixlen='21', address_scope_id='foo-addr-scope-id') def test_create_subnetpool_assoc_address_scope_with_prefix_intersect(self): with self.address_scope(name='foo-address-scope') as addr_scope: address_scope_id = addr_scope['address_scope']['id'] subnet = netaddr.IPNetwork('10.10.10.0/24') expected = {'address_scope_id': address_scope_id} self._test_create_subnetpool([subnet.cidr], expected=expected, name='foo-subnetpool', min_prefixlen='21', address_scope_id=address_scope_id) overlap_subnet = netaddr.IPNetwork('10.10.10.10/24') self.assertRaises( webob.exc.HTTPClientError, self._test_create_subnetpool, [overlap_subnet.cidr], min_prefixlen='21', address_scope_id=address_scope_id) def test_update_subnetpool_associate_address_scope(self): subnet = netaddr.IPNetwork('10.10.10.0/24') initial_subnetpool = self._test_create_subnetpool([subnet.cidr], name='foo-sp', min_prefixlen='21') with self.address_scope(name='foo-address-scope') as addr_scope: address_scope_id = addr_scope['address_scope']['id'] data = {'subnetpool': {'address_scope_id': address_scope_id}} req = self.new_update_request( 'subnetpools', data, initial_subnetpool['subnetpool']['id']) api = self._api_for_resource('subnetpools') res = self.deserialize(self.fmt, req.get_response(api)) self._compare_resource(res, data['subnetpool'], 'subnetpool') def test_update_subnetpool_associate_invalid_address_scope(self): subnet = netaddr.IPNetwork('10.10.10.0/24') initial_subnetpool = self._test_create_subnetpool([subnet.cidr], name='foo-sp', min_prefixlen='21') data = {'subnetpool': {'address_scope_id': 'foo-addr-scope-id'}} req = self.new_update_request( 'subnetpools', data, initial_subnetpool['subnetpool']['id']) api = self._api_for_resource('subnetpools') res = req.get_response(api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnetpool_disassociate_address_scope(self): with self.address_scope(name='foo-address-scope') as addr_scope: address_scope_id = addr_scope['address_scope']['id'] subnet = netaddr.IPNetwork('10.10.10.0/24') expected = {'address_scope_id': address_scope_id} initial_subnetpool = self._test_create_subnetpool( [subnet.cidr], expected=expected, name='foo-sp', min_prefixlen='21', address_scope_id=address_scope_id) data = {'subnetpool': {'address_scope_id': None}} req = self.new_update_request( 'subnetpools', data, initial_subnetpool['subnetpool']['id']) api = self._api_for_resource('subnetpools') res = self.deserialize(self.fmt, req.get_response(api)) self._compare_resource(res, data['subnetpool'], 'subnetpool') def test_update_subnetpool_associate_another_address_scope(self): with self.address_scope(name='foo-address-scope') as addr_scope: address_scope_id = addr_scope['address_scope']['id'] subnet = netaddr.IPNetwork('10.10.10.0/24') expected = {'address_scope_id': address_scope_id} initial_subnetpool = self._test_create_subnetpool( [subnet.cidr], expected=expected, name='foo-sp', min_prefixlen='21', address_scope_id=address_scope_id) with self.address_scope(name='foo-address-scope') as other_a_s: other_a_s_id = other_a_s['address_scope']['id'] update_data = {'subnetpool': {'address_scope_id': other_a_s_id}} req = self.new_update_request( 'subnetpools', update_data, initial_subnetpool['subnetpool']['id']) api = self._api_for_resource('subnetpools') res = self.deserialize(self.fmt, req.get_response(api)) self._compare_resource(res, update_data['subnetpool'], 'subnetpool') def _test_update_subnetpool_address_scope_notify(self, as_change=True): with self.address_scope(name='foo-address-scope') as addr_scope: foo_as_id = addr_scope['address_scope']['id'] subnet = netaddr.IPNetwork('10.10.10.0/24') initial_subnetpool = self._test_create_subnetpool( [subnet.cidr], name='foo-sp', min_prefixlen='21', address_scope_id=foo_as_id) subnetpool_id = initial_subnetpool['subnetpool']['id'] with self.address_scope(name='bar-address-scope') as other_as, \ self.network() as network: data = {'subnet': { 'network_id': network['network']['id'], 'subnetpool_id': subnetpool_id, 'prefixlen': 24, 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) subnet = self.deserialize(self.fmt, req.get_response(self.api)) with mock.patch.object(registry, 'notify') as notify: plugin = db_base_plugin_v2.NeutronDbPluginV2() plugin.is_address_scope_owned_by_tenant = mock.Mock( return_value=True) plugin._validate_address_scope_id = mock.Mock() ctx = context.get_admin_context() bar_as_id = other_as['address_scope']['id'] data = {'subnetpool': { 'name': 'bar-sp'}} if as_change: data['subnetpool']['address_scope_id'] = bar_as_id updated_sp = plugin.update_subnetpool( ctx, subnetpool_id, data) self.assertEqual('bar-sp', updated_sp['name']) if as_change: self.assertEqual(bar_as_id, updated_sp['address_scope_id']) notify.assert_called_once_with( resources.SUBNETPOOL_ADDRESS_SCOPE, events.AFTER_UPDATE, plugin.update_subnetpool, context=ctx, subnetpool_id=subnetpool_id) else: self.assertEqual(foo_as_id, updated_sp['address_scope_id']) self.assertFalse(notify.called) def test_update_subnetpool_address_scope_notify(self): self._test_update_subnetpool_address_scope_notify() def test_not_update_subnetpool_address_scope_not_notify(self): self._test_update_subnetpool_address_scope_notify(False) def test_network_create_contain_address_scope_attr(self): with self.network() as network: result = self._show('networks', network['network']['id']) keys = [apidef.IPV4_ADDRESS_SCOPE, apidef.IPV6_ADDRESS_SCOPE] for k in keys: # Correlated address scopes should initially be None self.assertIsNone(result['network'][k]) def test_correlate_network_with_address_scope(self): with self.address_scope(name='v4-as') as v4_addr_scope, \ self.address_scope( name='v6-as', ip_version=constants.IP_VERSION_6) as v6_addr_scope, \ self.network() as network: v4_as_id = v4_addr_scope['address_scope']['id'] subnet = netaddr.IPNetwork('10.10.10.0/24') v4_subnetpool = self._test_create_subnetpool( [subnet.cidr], name='v4-sp', min_prefixlen='24', address_scope_id=v4_as_id) v4_subnetpool_id = v4_subnetpool['subnetpool']['id'] v6_as_id = v6_addr_scope['address_scope']['id'] subnet = netaddr.IPNetwork('fd5c:6ee1:c7ae::/64') v6_subnetpool = self._test_create_subnetpool( [subnet.cidr], name='v6-sp', min_prefixlen='64', address_scope_id=v6_as_id) v6_subnetpool_id = v6_subnetpool['subnetpool']['id'] data = {'subnet': { 'network_id': network['network']['id'], 'subnetpool_id': v4_subnetpool_id, 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) self.deserialize(self.fmt, req.get_response(self.api)) data['subnet']['subnetpool_id'] = v6_subnetpool_id data['subnet']['ip_version'] = 6 req = self.new_create_request('subnets', data) self.deserialize(self.fmt, req.get_response(self.api)) result = self._show('networks', network['network']['id']) self.assertEqual( v4_as_id, result['network'][apidef.IPV4_ADDRESS_SCOPE]) self.assertEqual( v6_as_id, result['network'][apidef.IPV6_ADDRESS_SCOPE]) def test_delete_address_scope_in_use(self): with self.address_scope(name='foo-address-scope') as addr_scope: address_scope_id = addr_scope['address_scope']['id'] subnet = netaddr.IPNetwork('10.10.10.0/24') expected = {'address_scope_id': address_scope_id} self._test_create_subnetpool([subnet.cidr], expected=expected, name='foo-subnetpool', min_prefixlen='21', address_scope_id=address_scope_id) self._delete('address-scopes', address_scope_id, expected_code=webob.exc.HTTPConflict.code) def test_add_subnetpool_address_scope_wrong_address_family(self): with self.address_scope(constants.IP_VERSION_6, name='foo-address-scope') as addr_scope: address_scope_id = addr_scope['address_scope']['id'] subnet = netaddr.IPNetwork('10.10.10.0/24') self.assertRaises(webob.exc.HTTPClientError, self._test_create_subnetpool, [subnet.cidr], name='foo-subnetpool', min_prefixlen='21', address_scope_id=address_scope_id) def test_update_subnetpool_associate_address_scope_wrong_family(self): with self.address_scope(constants.IP_VERSION_6, name='foo-address-scope') as addr_scope: address_scope_id = addr_scope['address_scope']['id'] subnet = netaddr.IPNetwork('2001:db8::/64') expected = {'address_scope_id': address_scope_id} initial_subnetpool = self._test_create_subnetpool( [subnet.cidr], expected=expected, name='foo-sp', min_prefixlen='64', address_scope_id=address_scope_id) with self.address_scope(name='foo-address-scope') as other_a_s: other_a_s_id = other_a_s['address_scope']['id'] update_data = {'subnetpool': {'address_scope_id': other_a_s_id}} req = self.new_update_request( 'subnetpools', update_data, initial_subnetpool['subnetpool']['id']) api = self._api_for_resource('subnetpools') res = req.get_response(api) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) neutron-12.1.1/neutron/tests/unit/extensions/test_router_availability_zone.py0000664000175000017500000000717113553660047030057 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.plugins import constants from neutron.db.availability_zone import router as router_az_db from neutron.db import common_db_mixin from neutron.db import l3_agentschedulers_db from neutron.db import l3_db from neutron.extensions import l3 from neutron.tests.unit.extensions import test_availability_zone as test_az from neutron.tests.unit.extensions import test_l3 class AZL3ExtensionManager(test_az.AZExtensionManager): def get_resources(self): return (super(AZL3ExtensionManager, self).get_resources() + l3.L3.get_resources()) class AZRouterTestPlugin(common_db_mixin.CommonDbMixin, l3_db.L3_NAT_db_mixin, router_az_db.RouterAvailabilityZoneMixin, l3_agentschedulers_db.AZL3AgentSchedulerDbMixin): supported_extension_aliases = ["router", "l3_agent_scheduler", "router_availability_zone"] @classmethod def get_plugin_type(cls): return constants.L3 def get_plugin_description(self): return "L3 Routing Service Plugin for testing" class TestAZRouterCase(test_az.AZTestCommon, test_l3.L3NatTestCaseMixin): def setUp(self): plugin = ('neutron.tests.unit.extensions.' 'test_availability_zone.AZTestPlugin') l3_plugin = ('neutron.tests.unit.extensions.' 'test_router_availability_zone.AZRouterTestPlugin') service_plugins = {'l3_plugin_name': l3_plugin} ext_mgr = AZL3ExtensionManager() super(TestAZRouterCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) def test_create_router_with_az(self): self._register_azs() az_hints = ['nova2'] with self.router(availability_zone_hints=az_hints) as router: res = self._show('routers', router['router']['id']) self.assertItemsEqual(az_hints, res['router']['availability_zone_hints']) def test_create_router_with_azs(self): self._register_azs() az_hints = ['nova2', 'nova3'] with self.router(availability_zone_hints=az_hints) as router: res = self._show('routers', router['router']['id']) self.assertItemsEqual(az_hints, res['router']['availability_zone_hints']) def test_create_router_without_az(self): with self.router() as router: res = self._show('routers', router['router']['id']) self.assertEqual([], res['router']['availability_zone_hints']) def test_create_router_with_empty_az(self): with self.router(availability_zone_hints=[]) as router: res = self._show('routers', router['router']['id']) self.assertEqual([], res['router']['availability_zone_hints']) def test_create_router_with_none_existing_az(self): res = self._create_router(self.fmt, 'tenant_id', availability_zone_hints=['nova4']) self.assertEqual(404, res.status_int) neutron-12.1.1/neutron/tests/unit/extensions/test_servicetype.py0000664000175000017500000002455113553660047025315 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from neutron_lib import exceptions as n_exc from neutron_lib.plugins import constants from oslo_config import cfg from oslo_utils import uuidutils import webob.exc as webexc import webtest from neutron.api import extensions from neutron.db.models import servicetype as st_model from neutron.db import servicetype_db as st_db from neutron.extensions import servicetype from neutron.services import provider_configuration as provconf from neutron.tests.unit.api import test_extensions from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit import dummy_plugin as dp from neutron.tests.unit import testlib_api _uuid = test_base._uuid _get_path = test_base._get_path PLUGIN_NAME = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' class ServiceTypeManagerTestCase(testlib_api.SqlTestCase): def setUp(self): self.service_providers = mock.patch.object( provconf.NeutronModule, 'service_providers').start() super(ServiceTypeManagerTestCase, self).setUp() self.ctx = context.get_admin_context() self.setup_coreplugin(PLUGIN_NAME) def _set_override(self, service_providers): self.service_providers.return_value = service_providers st_db.ServiceTypeManager._instance = None self.manager = st_db.ServiceTypeManager.get_instance() for provider in service_providers: self.manager.add_provider_configuration( provider.split(':')[0], provconf.ProviderConfiguration()) def test_service_provider_driver_not_unique(self): self._set_override([constants.LOADBALANCER + ':lbaas:driver']) prov = {'service_type': constants.LOADBALANCER, 'name': 'name2', 'driver': 'driver', 'default': False} self.assertRaises( n_exc.Invalid, self.manager.config['LOADBALANCER'].add_provider, prov) def test_get_service_providers(self): """Test that get_service_providers filters correctly.""" self._set_override( [constants.LOADBALANCER + ':lbaas:driver_path1', constants.FIREWALL + ':fwaas:driver_path2']) ctx = context.get_admin_context() res = self.manager.get_service_providers( ctx, filters=dict(service_type=[constants.LOADBALANCER]) ) self.assertEqual(1, len(res)) res = self.manager.get_service_providers( ctx, filters=dict(service_type=[constants.FIREWALL]) ) self.assertEqual(1, len(res)) def test_multiple_default_providers_specified_for_service(self): self.assertRaises( n_exc.Invalid, self._set_override, [constants.LOADBALANCER + ':lbaas1:driver_path:default', constants.LOADBALANCER + ':lbaas2:driver_path:default']) def test_get_default_provider(self): self._set_override([constants.LOADBALANCER + ':lbaas1:driver_path:default', dp.DUMMY_SERVICE_TYPE + ':lbaas2:driver_path2']) # can pass None as a context p = self.manager.get_default_service_provider(None, constants.LOADBALANCER) self.assertEqual({'service_type': constants.LOADBALANCER, 'name': 'lbaas1', 'driver': 'driver_path', 'default': True}, p) self.assertRaises( provconf.DefaultServiceProviderNotFound, self.manager.get_default_service_provider, None, dp.DUMMY_SERVICE_TYPE ) def test_get_provider_names_by_resource_ids(self): self._set_override([dp.DUMMY_SERVICE_TYPE + ':dummy1:driver_path', dp.DUMMY_SERVICE_TYPE + ':dummy2:driver_path2']) ctx = context.get_admin_context() test_data = [{'provider_name': 'dummy1', 'resource_id': uuidutils.generate_uuid()}, {'provider_name': 'dummy1', 'resource_id': uuidutils.generate_uuid()}, {'provider_name': 'dummy2', 'resource_id': uuidutils.generate_uuid()}] self.manager.add_resource_association(ctx, dp.DUMMY_SERVICE_TYPE, **test_data[0]) self.manager.add_resource_association(ctx, dp.DUMMY_SERVICE_TYPE, **test_data[1]) self.manager.add_resource_association(ctx, dp.DUMMY_SERVICE_TYPE, **test_data[2]) names_by_id = self.manager.get_provider_names_by_resource_ids( ctx, [td['resource_id'] for td in test_data]) # unmatched IDs will be excluded from the result self.assertEqual({td['resource_id']: td['provider_name'] for td in test_data}, names_by_id) def test_add_resource_association(self): self._set_override([constants.LOADBALANCER + ':lbaas1:driver_path:default', dp.DUMMY_SERVICE_TYPE + ':lbaas2:driver_path2']) ctx = context.get_admin_context() self.manager.add_resource_association(ctx, constants.LOADBALANCER, 'lbaas1', uuidutils.generate_uuid()) self.assertEqual(ctx.session. query(st_model.ProviderResourceAssociation).count(), 1) assoc = ctx.session.query(st_model.ProviderResourceAssociation).one() ctx.session.delete(assoc) def test_invalid_resource_association(self): self._set_override([constants.LOADBALANCER + ':lbaas1:driver_path:default', dp.DUMMY_SERVICE_TYPE + ':lbaas2:driver_path2']) ctx = context.get_admin_context() self.assertRaises(provconf.ServiceProviderNotFound, self.manager.add_resource_association, ctx, 'BLABLA_svc', 'name', '123-123') class TestServiceTypeExtensionManager(object): """Mock extensions manager.""" def get_resources(self): return (servicetype.Servicetype.get_resources() + dp.Dummy.get_resources()) def get_actions(self): return [] def get_request_extensions(self): return [] class ServiceTypeExtensionTestCaseBase(testlib_api.WebTestCase): fmt = 'json' def setUp(self): # This is needed because otherwise a failure will occur due to # nonexisting core_plugin self.setup_coreplugin(test_db_base_plugin_v2.DB_PLUGIN_KLASS) cfg.CONF.set_override('service_plugins', ["%s.%s" % (dp.__name__, dp.DummyServicePlugin.__name__)]) # Ensure existing ExtensionManager is not used extensions.PluginAwareExtensionManager._instance = None ext_mgr = TestServiceTypeExtensionManager() self.ext_mdw = test_extensions.setup_extensions_middleware(ext_mgr) self.api = webtest.TestApp(self.ext_mdw) self.resource_name = servicetype.RESOURCE_NAME.replace('-', '_') super(ServiceTypeExtensionTestCaseBase, self).setUp() class ServiceTypeExtensionTestCase(ServiceTypeExtensionTestCaseBase): def setUp(self): self._patcher = mock.patch( "neutron.db.servicetype_db.ServiceTypeManager", autospec=True) self.mock_mgr = self._patcher.start() self.mock_mgr.get_instance.return_value = self.mock_mgr.return_value super(ServiceTypeExtensionTestCase, self).setUp() def test_service_provider_list(self): instance = self.mock_mgr.return_value res = self.api.get(_get_path('service-providers', fmt=self.fmt)) instance.get_service_providers.assert_called_with(mock.ANY, filters={}, fields=[]) self.assertEqual(webexc.HTTPOk.code, res.status_int) class ServiceTypeManagerExtTestCase(ServiceTypeExtensionTestCaseBase): """Tests ServiceTypemanager as a public API.""" def setUp(self): self.service_providers = mock.patch.object( provconf.NeutronModule, 'service_providers').start() service_providers = [ constants.LOADBALANCER + ':lbaas:driver_path', dp.DUMMY_SERVICE_TYPE + ':dummy:dummy_dr' ] self.service_providers.return_value = service_providers # Blank out service type manager instance st_db.ServiceTypeManager._instance = None self.manager = st_db.ServiceTypeManager.get_instance() for provider in service_providers: self.manager.add_provider_configuration( provider.split(':')[0], provconf.ProviderConfiguration()) super(ServiceTypeManagerExtTestCase, self).setUp() def _list_service_providers(self): return self.api.get(_get_path('service-providers', fmt=self.fmt)) def test_list_service_providers(self): res = self._list_service_providers() self.assertEqual(webexc.HTTPOk.code, res.status_int) data = self.deserialize(res) self.assertIn('service_providers', data) self.assertGreaterEqual(len(data['service_providers']), 2) neutron-12.1.1/neutron/tests/unit/extensions/test_agent.py0000664000175000017500000001473613553660047024055 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from datetime import datetime import time from neutron_lib import constants from neutron_lib import context from oslo_config import cfg from oslo_utils import uuidutils from webob import exc from neutron.db import agents_db from neutron.db import db_base_plugin_v2 from neutron.extensions import agent from neutron.tests.common import helpers from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit.db import test_db_base_plugin_v2 _uuid = uuidutils.generate_uuid _get_path = test_base._get_path L3_HOSTA = 'hosta' DHCP_HOSTA = 'hosta' L3_HOSTB = 'hostb' DHCP_HOSTC = 'hostc' LBAAS_HOSTA = 'hosta' LBAAS_HOSTB = 'hostb' class AgentTestExtensionManager(object): def get_resources(self): return agent.Agent.get_resources() def get_actions(self): return [] def get_request_extensions(self): return [] # This plugin class is just for testing class TestAgentPlugin(db_base_plugin_v2.NeutronDbPluginV2, agents_db.AgentDbMixin): supported_extension_aliases = ["agent"] class AgentDBTestMixIn(object): def _list_agents(self, expected_res_status=None, neutron_context=None, query_string=None): agent_res = self._list('agents', neutron_context=neutron_context, query_params=query_string) if expected_res_status: self.assertEqual(expected_res_status, agent_res.status_int) return agent_res def _register_agent_states(self, lbaas_agents=False): """Register two L3 agents and two DHCP agents.""" l3_hosta = helpers._get_l3_agent_dict( L3_HOSTA, constants.L3_AGENT_MODE_LEGACY) l3_hostb = helpers._get_l3_agent_dict( L3_HOSTB, constants.L3_AGENT_MODE_LEGACY) dhcp_hosta = helpers._get_dhcp_agent_dict(DHCP_HOSTA) dhcp_hostc = helpers._get_dhcp_agent_dict(DHCP_HOSTC) helpers.register_l3_agent(host=L3_HOSTA) helpers.register_l3_agent(host=L3_HOSTB) helpers.register_dhcp_agent(host=DHCP_HOSTA) helpers.register_dhcp_agent(host=DHCP_HOSTC) res = [l3_hosta, l3_hostb, dhcp_hosta, dhcp_hostc] if lbaas_agents: lbaas_hosta = { 'binary': 'neutron-loadbalancer-agent', 'host': LBAAS_HOSTA, 'topic': 'LOADBALANCER_AGENT', 'configurations': {'device_drivers': ['haproxy_ns']}, 'agent_type': constants.AGENT_TYPE_LOADBALANCER} lbaas_hostb = copy.deepcopy(lbaas_hosta) lbaas_hostb['host'] = LBAAS_HOSTB callback = agents_db.AgentExtRpcCallback() callback.report_state( self.adminContext, agent_state={'agent_state': lbaas_hosta}, time=datetime.utcnow().strftime(constants.ISO8601_TIME_FORMAT)) callback.report_state( self.adminContext, agent_state={'agent_state': lbaas_hostb}, time=datetime.utcnow().strftime(constants.ISO8601_TIME_FORMAT)) res += [lbaas_hosta, lbaas_hostb] return res def _register_dvr_agents(self): dvr_snat_agent = helpers.register_l3_agent( host=L3_HOSTA, agent_mode=constants.L3_AGENT_MODE_DVR_SNAT) dvr_agent = helpers.register_l3_agent( host=L3_HOSTB, agent_mode=constants.L3_AGENT_MODE_DVR) return [dvr_snat_agent, dvr_agent] def _register_l3_agent(self, host): helpers.register_l3_agent(host) class AgentDBTestCase(AgentDBTestMixIn, test_db_base_plugin_v2.NeutronDbPluginV2TestCase): fmt = 'json' def setUp(self): plugin = 'neutron.tests.unit.extensions.test_agent.TestAgentPlugin' # for these tests we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) ext_mgr = AgentTestExtensionManager() super(AgentDBTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr) self.adminContext = context.get_admin_context() def test_create_agent(self): data = {'agent': {}} _req = self.new_create_request('agents', data, self.fmt) _req.environ['neutron.context'] = context.Context( '', 'tenant_id') res = _req.get_response(self.ext_api) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_list_agent(self): agents = self._register_agent_states() res = self._list('agents') self.assertEqual(len(agents), len(res['agents'])) def test_show_agent(self): self._register_agent_states() agents = self._list_agents( query_string='binary=neutron-l3-agent') self.assertEqual(2, len(agents['agents'])) agent = self._show('agents', agents['agents'][0]['id']) self.assertEqual('neutron-l3-agent', agent['agent']['binary']) def test_update_agent(self): self._register_agent_states() agents = self._list_agents( query_string='binary=neutron-l3-agent&host=' + L3_HOSTB) self.assertEqual(1, len(agents['agents'])) com_id = agents['agents'][0]['id'] agent = self._show('agents', com_id) new_agent = {} new_agent['agent'] = {} new_agent['agent']['admin_state_up'] = False new_agent['agent']['description'] = 'description' self._update('agents', com_id, new_agent) agent = self._show('agents', com_id) self.assertFalse(agent['agent']['admin_state_up']) self.assertEqual('description', agent['agent']['description']) def test_dead_agent(self): cfg.CONF.set_override('agent_down_time', 1) self._register_agent_states() time.sleep(1.5) agents = self._list_agents( query_string='binary=neutron-l3-agent&host=' + L3_HOSTB) self.assertFalse(agents['agents'][0]['alive']) neutron-12.1.1/neutron/tests/unit/extensions/test_default_subnetpools.py0000664000175000017500000002050413553660047027026 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron_lib.api.definitions import default_subnetpools as api_def from neutron_lib import constants from oslo_config import cfg import webob.exc from neutron.db import db_base_plugin_v2 from neutron.extensions import default_subnetpools from neutron.tests.unit.db import test_db_base_plugin_v2 class DefaultSubnetpoolsExtensionManager(object): def get_resources(self): return [] def get_actions(self): return [] def get_request_extensions(self): return [] def get_extended_resources(self, version): extension = default_subnetpools.Default_subnetpools() return extension.get_extended_resources(version) class DefaultSubnetpoolsExtensionTestPlugin( db_base_plugin_v2.NeutronDbPluginV2): """Test plugin to mixin the default subnet pools extension. """ supported_extension_aliases = [api_def.ALIAS, "subnet_allocation"] class DefaultSubnetpoolsExtensionTestCase( test_db_base_plugin_v2.NeutronDbPluginV2TestCase): """Test API extension default_subnetpools attributes. """ def setUp(self): plugin = ('neutron.tests.unit.extensions.test_default_subnetpools.' + 'DefaultSubnetpoolsExtensionTestPlugin') ext_mgr = DefaultSubnetpoolsExtensionManager() super(DefaultSubnetpoolsExtensionTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr) def _create_subnet_using_default_subnetpool( self, network_id, tenant_id, ip_version=4, **kwargs): data = {'subnet': { 'network_id': network_id, 'ip_version': str(ip_version), 'tenant_id': tenant_id, 'use_default_subnetpool': True}} data['subnet'].update(kwargs) subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) return self.deserialize(self.fmt, res)['subnet'] def _update_subnetpool(self, subnetpool_id, **data): update_req = self.new_update_request( 'subnetpools', {'subnetpool': data}, subnetpool_id) res = update_req.get_response(self.api) return self.deserialize(self.fmt, res)['subnetpool'] def test_create_subnet_only_ip_version_v4(self): with self.network() as network: tenant_id = network['network']['tenant_id'] subnetpool_prefix = '10.0.0.0/8' with self.subnetpool(prefixes=[subnetpool_prefix], admin=True, name="My subnet pool", tenant_id=tenant_id, min_prefixlen='25', is_default=True) as subnetpool: subnetpool_id = subnetpool['subnetpool']['id'] subnet = self._create_subnet_using_default_subnetpool( network['network']['id'], tenant_id, prefixlen='27') ip_net = netaddr.IPNetwork(subnet['cidr']) self.assertIn(ip_net, netaddr.IPNetwork(subnetpool_prefix)) self.assertEqual(27, ip_net.prefixlen) self.assertEqual(subnetpool_id, subnet['subnetpool_id']) def test_convert_subnetpool_to_default_subnetpool(self): with self.network() as network: tenant_id = network['network']['tenant_id'] subnetpool_prefix = '10.0.0.0/8' with self.subnetpool(prefixes=[subnetpool_prefix], admin=True, name="My subnet pool", tenant_id=tenant_id, min_prefixlen='25', is_default=False) as subnetpool: self.assertFalse(subnetpool['subnetpool']['is_default']) subnetpool_id = subnetpool['subnetpool']['id'] updated_subnetpool = self._update_subnetpool( subnetpool_id, is_default=True) self.assertTrue(updated_subnetpool['is_default']) subnet = self._create_subnet_using_default_subnetpool( network['network']['id'], tenant_id) ip_net = netaddr.IPNetwork(subnet['cidr']) self.assertIn(ip_net, netaddr.IPNetwork(subnetpool_prefix)) self.assertEqual(subnetpool_id, subnet['subnetpool_id']) def test_convert_default_subnetpool_to_non_default(self): with self.network() as network: tenant_id = network['network']['tenant_id'] subnetpool_prefix = '10.0.0.0/8' with self.subnetpool(prefixes=[subnetpool_prefix], admin=True, name="My subnet pool", tenant_id=tenant_id, min_prefixlen='25', is_default=True) as subnetpool: self.assertTrue(subnetpool['subnetpool']['is_default']) subnetpool_id = subnetpool['subnetpool']['id'] updated_subnetpool = self._update_subnetpool( subnetpool_id, is_default=False) self.assertFalse(updated_subnetpool['is_default']) def test_create_subnet_only_ip_version_v6(self): # this test mirrors its v4 counterpart with self.network() as network: tenant_id = network['network']['tenant_id'] subnetpool_prefix = '2000::/56' with self.subnetpool(prefixes=[subnetpool_prefix], admin=True, name="My ipv6 subnet pool", tenant_id=tenant_id, min_prefixlen='64', is_default=True) as subnetpool: subnetpool_id = subnetpool['subnetpool']['id'] cfg.CONF.set_override('ipv6_pd_enabled', False) subnet = self._create_subnet_using_default_subnetpool( network['network']['id'], tenant_id, ip_version=6) self.assertEqual(subnetpool_id, subnet['subnetpool_id']) ip_net = netaddr.IPNetwork(subnet['cidr']) self.assertIn(ip_net, netaddr.IPNetwork(subnetpool_prefix)) self.assertEqual(64, ip_net.prefixlen) def _test_create_subnet_V6_pd_modes(self, ra_addr_mode, expect_fail=False): cfg.CONF.set_override('ipv6_pd_enabled', True) with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'ip_version': '6', 'tenant_id': network['network']['tenant_id'], 'use_default_subnetpool': True}} if ra_addr_mode: data['subnet']['ipv6_ra_mode'] = ra_addr_mode data['subnet']['ipv6_address_mode'] = ra_addr_mode subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) if expect_fail: self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) else: subnet = self.deserialize(self.fmt, res)['subnet'] self.assertEqual(constants.IPV6_PD_POOL_ID, subnet['subnetpool_id']) def test_create_subnet_V6_pd_slaac(self): self._test_create_subnet_V6_pd_modes('slaac') def test_create_subnet_V6_pd_stateless(self): self._test_create_subnet_V6_pd_modes('dhcpv6-stateless') def test_create_subnet_V6_pd_statefull(self): self._test_create_subnet_V6_pd_modes('dhcpv6-statefull', expect_fail=True) def test_create_subnet_V6_pd_no_mode(self): self._test_create_subnet_V6_pd_modes(None, expect_fail=True) neutron-12.1.1/neutron/tests/unit/extensions/v2attributes.py0000664000175000017500000000275013553660046024346 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.api import extensions EXTENDED_ATTRIBUTES_2_0 = { 'networks': { 'v2attrs:something': {'allow_post': False, 'allow_put': False, 'is_visible': True}, 'v2attrs:something_else': {'allow_post': True, 'allow_put': False, 'is_visible': False}, } } class V2attributes(extensions.ExtensionDescriptor): def get_name(self): return "V2 Extended Attributes Example" def get_alias(self): return "v2attrs" def get_description(self): return "Demonstrates extended attributes on V2 core resources" def get_updated(self): return "2012-07-18T10:00:00-00:00" def get_extended_resources(self, version): if version == "2.0": return EXTENDED_ATTRIBUTES_2_0 else: return {} neutron-12.1.1/neutron/tests/unit/extensions/test_availability_zone.py0000664000175000017500000001362013553660047026453 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import availability_zone as az_def from neutron_lib import context from neutron_lib.exceptions import availability_zone as az_exc from neutron.db import agents_db from neutron.db import db_base_plugin_v2 from neutron.extensions import agent from neutron.extensions import availability_zone as az_ext from neutron.tests.common import helpers from neutron.tests.unit.db import test_db_base_plugin_v2 class AZExtensionManager(object): def get_resources(self): agent.Agent().update_attributes_map(az_def.RESOURCE_ATTRIBUTE_MAP) return (az_ext.Availability_zone.get_resources() + agent.Agent.get_resources()) def get_actions(self): return [] def get_request_extensions(self): return [] class AZTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, agents_db.AgentDbMixin): supported_extension_aliases = ["agent", "availability_zone"] class AZTestCommon(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def _register_azs(self): self.agent1 = helpers.register_dhcp_agent(host='host1', az='nova1') self.agent2 = helpers.register_dhcp_agent(host='host2', az='nova2') self.agent3 = helpers.register_l3_agent(host='host2', az='nova2') self.agent4 = helpers.register_l3_agent(host='host3', az='nova3') self.agent5 = helpers.register_l3_agent(host='host4', az='nova2') class TestAZAgentCase(AZTestCommon): def setUp(self): plugin = ('neutron.tests.unit.extensions.' 'test_availability_zone.AZTestPlugin') ext_mgr = AZExtensionManager() super(TestAZAgentCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr) def test_list_availability_zones(self): self._register_azs() helpers.set_agent_admin_state(self.agent3['id'], admin_state_up=False) helpers.set_agent_admin_state(self.agent4['id'], admin_state_up=False) expected = [ {'name': 'nova1', 'resource': 'network', 'state': 'available'}, {'name': 'nova2', 'resource': 'network', 'state': 'available'}, {'name': 'nova2', 'resource': 'router', 'state': 'available'}, {'name': 'nova3', 'resource': 'router', 'state': 'unavailable'}] res = self._list('availability_zones') azs = res['availability_zones'] self.assertItemsEqual(expected, azs) # list with filters res = self._list('availability_zones', query_params="availability_zone=nova1") azs = res['availability_zones'] self.assertItemsEqual(expected[:1], azs) # not admin case ctx = context.Context('', 'noadmin') res = self._list('availability_zones', neutron_context=ctx) azs = res['availability_zones'] self.assertItemsEqual(expected, azs) def test_list_agent_with_az(self): helpers.register_dhcp_agent(host='host1', az='nova1') res = self._list('agents') self.assertEqual('nova1', res['agents'][0]['availability_zone']) def test_validate_availability_zones(self): self._register_azs() ctx = context.Context('', 'tenant_id') self.plugin.validate_availability_zones(ctx, 'network', ['nova1', 'nova2']) self.plugin.validate_availability_zones(ctx, 'router', ['nova2', 'nova3']) self.assertRaises(az_exc.AvailabilityZoneNotFound, self.plugin.validate_availability_zones, ctx, 'router', ['nova1']) class TestAZNetworkCase(AZTestCommon): def setUp(self): ext_mgr = AZExtensionManager() super(TestAZNetworkCase, self).setUp(plugin='ml2', ext_mgr=ext_mgr) def test_availability_zones_in_create_response(self): with self.network() as net: self.assertIn('availability_zone_hints', net['network']) self.assertIn('availability_zones', net['network']) def test_create_network_with_az(self): self._register_azs() az_hints = ['nova1'] with self.network(availability_zone_hints=az_hints) as net: res = self._show('networks', net['network']['id']) self.assertItemsEqual(az_hints, res['network']['availability_zone_hints']) def test_create_network_with_azs(self): self._register_azs() az_hints = ['nova1', 'nova2'] with self.network(availability_zone_hints=az_hints) as net: res = self._show('networks', net['network']['id']) self.assertItemsEqual(az_hints, res['network']['availability_zone_hints']) def test_create_network_without_az(self): with self.network() as net: res = self._show('networks', net['network']['id']) self.assertEqual([], res['network']['availability_zone_hints']) def test_create_network_with_empty_az(self): with self.network(availability_zone_hints=[]) as net: res = self._show('networks', net['network']['id']) self.assertEqual([], res['network']['availability_zone_hints']) def test_create_network_with_not_exist_az(self): res = self._create_network(self.fmt, 'net', True, availability_zone_hints=['nova3']) self.assertEqual(404, res.status_int) neutron-12.1.1/neutron/tests/unit/extensions/test_extra_dhcp_opt.py0000664000175000017500000003462513553660047025761 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import copy from neutron_lib.api.definitions import extra_dhcp_opt as edo_ext import webob.exc from neutron.db import db_base_plugin_v2 from neutron.db import extradhcpopt_db as edo_db from neutron.tests.unit.db import test_db_base_plugin_v2 DB_PLUGIN_KLASS = ( 'neutron.tests.unit.extensions.test_extra_dhcp_opt.ExtraDhcpOptTestPlugin') class ExtraDhcpOptTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, edo_db.ExtraDhcpOptMixin): """Test plugin that implements necessary calls on create/delete port for associating ports with extra dhcp options. """ supported_extension_aliases = ["extra_dhcp_opt"] def create_port(self, context, port): with context.session.begin(subtransactions=True): edos = port['port'].get(edo_ext.EXTRADHCPOPTS, []) new_port = super(ExtraDhcpOptTestPlugin, self).create_port( context, port) self._process_port_create_extra_dhcp_opts(context, new_port, edos) return new_port def update_port(self, context, id, port): with context.session.begin(subtransactions=True): rtn_port = super(ExtraDhcpOptTestPlugin, self).update_port( context, id, port) self._update_extra_dhcp_opts_on_port(context, id, port, rtn_port) return rtn_port class ExtraDhcpOptDBTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def setUp(self, plugin=DB_PLUGIN_KLASS): super(ExtraDhcpOptDBTestCase, self).setUp(plugin=plugin) class TestExtraDhcpOpt(ExtraDhcpOptDBTestCase): def _check_opts(self, expected, returned): self.assertEqual(len(expected), len(returned)) for opt in returned: name = opt['opt_name'] for exp in expected: if (name == exp['opt_name'] and opt['ip_version'] == exp.get( 'ip_version', 4)): val = exp['opt_value'] break self.assertEqual(val, opt['opt_value']) def test_create_port_with_extradhcpopts(self): opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'}, {'opt_name': 'server-ip-address', 'opt_value': '123.123.123.456'}, {'opt_name': 'tftp-server', 'opt_value': '123.123.123.123'}] params = {edo_ext.EXTRADHCPOPTS: opt_list, 'arg_list': (edo_ext.EXTRADHCPOPTS,)} with self.port(**params) as port: self._check_opts(opt_list, port['port'][edo_ext.EXTRADHCPOPTS]) def test_create_port_with_none_extradhcpopts(self): opt_list = [{'opt_name': 'bootfile-name', 'opt_value': None}, {'opt_name': 'server-ip-address', 'opt_value': '123.123.123.456'}, {'opt_name': 'tftp-server', 'opt_value': '123.123.123.123'}] expected = [{'opt_name': 'server-ip-address', 'opt_value': '123.123.123.456'}, {'opt_name': 'tftp-server', 'opt_value': '123.123.123.123'}] params = {edo_ext.EXTRADHCPOPTS: opt_list, 'arg_list': (edo_ext.EXTRADHCPOPTS,)} with self.port(**params) as port: self._check_opts(expected, port['port'][edo_ext.EXTRADHCPOPTS]) def test_create_port_with_empty_router_extradhcpopts(self): opt_list = [{'opt_name': 'router', 'opt_value': ''}, {'opt_name': 'server-ip-address', 'opt_value': '123.123.123.456'}, {'opt_name': 'tftp-server', 'opt_value': '123.123.123.123'}] params = {edo_ext.EXTRADHCPOPTS: opt_list, 'arg_list': (edo_ext.EXTRADHCPOPTS,)} with self.port(**params) as port: self._check_opts(opt_list, port['port'][edo_ext.EXTRADHCPOPTS]) def test_create_port_with_extradhcpopts_ipv4_opt_version(self): opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0', 'ip_version': 4}, {'opt_name': 'server-ip-address', 'opt_value': '123.123.123.456', 'ip_version': 4}, {'opt_name': 'tftp-server', 'opt_value': '123.123.123.123', 'ip_version': 4}] params = {edo_ext.EXTRADHCPOPTS: opt_list, 'arg_list': (edo_ext.EXTRADHCPOPTS,)} with self.port(**params) as port: self._check_opts(opt_list, port['port'][edo_ext.EXTRADHCPOPTS]) def test_create_port_with_extradhcpopts_ipv6_opt_version(self): opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0', 'ip_version': 6}, {'opt_name': 'tftp-server', 'opt_value': '2001:192:168::1', 'ip_version': 6}] params = {edo_ext.EXTRADHCPOPTS: opt_list, 'arg_list': (edo_ext.EXTRADHCPOPTS,)} with self.port(**params) as port: self._check_opts(opt_list, port['port'][edo_ext.EXTRADHCPOPTS]) def _test_update_port_with_extradhcpopts(self, opt_list, upd_opts, expected_opts): params = {edo_ext.EXTRADHCPOPTS: opt_list, 'arg_list': (edo_ext.EXTRADHCPOPTS,)} with self.port(**params) as port: update_port = {'port': {edo_ext.EXTRADHCPOPTS: upd_opts}} req = self.new_update_request('ports', update_port, port['port']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, res.status_int) port = self.deserialize('json', res) self._check_opts(expected_opts, port['port'][edo_ext.EXTRADHCPOPTS]) def test_update_port_with_extradhcpopts_with_same(self): opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'}, {'opt_name': 'tftp-server', 'opt_value': '123.123.123.123'}, {'opt_name': 'server-ip-address', 'opt_value': '123.123.123.456'}] upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': 'changeme.0'}] expected_opts = opt_list[:] for i in expected_opts: if i['opt_name'] == upd_opts[0]['opt_name']: i['opt_value'] = upd_opts[0]['opt_value'] break self._test_update_port_with_extradhcpopts(opt_list, upd_opts, expected_opts) def test_update_port_with_additional_extradhcpopt(self): opt_list = [{'opt_name': 'tftp-server', 'opt_value': '123.123.123.123'}, {'opt_name': 'server-ip-address', 'opt_value': '123.123.123.456'}] upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': 'changeme.0'}] expected_opts = copy.deepcopy(opt_list) expected_opts.append(upd_opts[0]) self._test_update_port_with_extradhcpopts(opt_list, upd_opts, expected_opts) def test_update_port_with_extradhcpopts(self): opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'}, {'opt_name': 'tftp-server', 'opt_value': '123.123.123.123'}, {'opt_name': 'server-ip-address', 'opt_value': '123.123.123.456'}] upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': 'changeme.0'}] expected_opts = copy.deepcopy(opt_list) for i in expected_opts: if i['opt_name'] == upd_opts[0]['opt_name']: i['opt_value'] = upd_opts[0]['opt_value'] break self._test_update_port_with_extradhcpopts(opt_list, upd_opts, expected_opts) def test_update_port_with_extradhcpopt_delete(self): opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'}, {'opt_name': 'tftp-server', 'opt_value': '123.123.123.123'}, {'opt_name': 'server-ip-address', 'opt_value': '123.123.123.456'}] upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': None}] expected_opts = [] expected_opts = [opt for opt in opt_list if opt['opt_name'] != 'bootfile-name'] self._test_update_port_with_extradhcpopts(opt_list, upd_opts, expected_opts) def test_update_port_without_extradhcpopt_delete(self): opt_list = [] upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': None}] expected_opts = [] self._test_update_port_with_extradhcpopts(opt_list, upd_opts, expected_opts) def test_update_port_adding_extradhcpopts(self): opt_list = [] upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'}, {'opt_name': 'tftp-server', 'opt_value': '123.123.123.123'}, {'opt_name': 'server-ip-address', 'opt_value': '123.123.123.456'}] expected_opts = copy.deepcopy(upd_opts) self._test_update_port_with_extradhcpopts(opt_list, upd_opts, expected_opts) def test_update_port_with_blank_string_extradhcpopt(self): opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'}, {'opt_name': 'tftp-server', 'opt_value': '123.123.123.123'}, {'opt_name': 'server-ip-address', 'opt_value': '123.123.123.456'}] upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': ' '}] params = {edo_ext.EXTRADHCPOPTS: opt_list, 'arg_list': (edo_ext.EXTRADHCPOPTS,)} with self.port(**params) as port: update_port = {'port': {edo_ext.EXTRADHCPOPTS: upd_opts}} req = self.new_update_request('ports', update_port, port['port']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_update_port_with_blank_name_extradhcpopt(self): opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'}, {'opt_name': 'tftp-server', 'opt_value': '123.123.123.123'}, {'opt_name': 'server-ip-address', 'opt_value': '123.123.123.456'}] upd_opts = [{'opt_name': ' ', 'opt_value': 'pxelinux.0'}] params = {edo_ext.EXTRADHCPOPTS: opt_list, 'arg_list': (edo_ext.EXTRADHCPOPTS,)} with self.port(**params) as port: update_port = {'port': {edo_ext.EXTRADHCPOPTS: upd_opts}} req = self.new_update_request('ports', update_port, port['port']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_update_port_with_blank_router_extradhcpopt(self): opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0', 'ip_version': 4}, {'opt_name': 'tftp-server', 'opt_value': '123.123.123.123', 'ip_version': 4}, {'opt_name': 'router', 'opt_value': '123.123.123.1', 'ip_version': 4}] upd_opts = [{'opt_name': 'router', 'opt_value': '', 'ip_version': 4}] expected_opts = copy.deepcopy(opt_list) for i in expected_opts: if i['opt_name'] == upd_opts[0]['opt_name']: i['opt_value'] = upd_opts[0]['opt_value'] break self._test_update_port_with_extradhcpopts(opt_list, upd_opts, expected_opts) def test_update_port_with_extradhcpopts_ipv6_change_value(self): opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0', 'ip_version': 6}, {'opt_name': 'tftp-server', 'opt_value': '2001:192:168::1', 'ip_version': 6}] upd_opts = [{'opt_name': 'tftp-server', 'opt_value': '2001:192:168::2', 'ip_version': 6}] expected_opts = copy.deepcopy(opt_list) for i in expected_opts: if i['opt_name'] == upd_opts[0]['opt_name']: i['opt_value'] = upd_opts[0]['opt_value'] break self._test_update_port_with_extradhcpopts(opt_list, upd_opts, expected_opts) def test_update_port_with_extradhcpopts_add_another_ver_opt(self): opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0', 'ip_version': 6}, {'opt_name': 'tftp-server', 'opt_value': '2001:192:168::1', 'ip_version': 6}] upd_opts = [{'opt_name': 'tftp-server', 'opt_value': '123.123.123.123', 'ip_version': 4}] expected_opts = copy.deepcopy(opt_list) expected_opts.extend(upd_opts) self._test_update_port_with_extradhcpopts(opt_list, upd_opts, expected_opts) neutron-12.1.1/neutron/tests/unit/extensions/base.py0000664000175000017500000001124213553660047022617 0ustar zuulzuul00000000000000# Copyright 2014 Intel Corporation. # Copyright 2014 Isaku Yamahata # # All Rights Reserved. # # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import fixture from oslo_config import cfg from oslo_utils import uuidutils from webob import exc import webtest from neutron.api import extensions from neutron import manager from neutron import quota from neutron.tests.unit.api import test_extensions from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit import testlib_api CORE_PLUGIN = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' class ExtensionTestCase(testlib_api.WebTestCase): # TODO(boden): phase out resource_attribute_map def _setUpExtension(self, plugin, service_type, resource_attribute_map, extension_class, resource_prefix, plural_mappings=None, translate_resource_name=False, allow_pagination=False, allow_sorting=False, supported_extension_aliases=None, use_quota=False, ): self._resource_prefix = resource_prefix self._plural_mappings = plural_mappings or {} self._translate_resource_name = translate_resource_name # Ensure existing ExtensionManager is not used extensions.PluginAwareExtensionManager._instance = None self.useFixture(fixture.APIDefinitionFixture()) # Create the default configurations self.config_parse() core_plugin = CORE_PLUGIN if service_type else plugin self.setup_coreplugin(core_plugin, load_plugins=False) if service_type: cfg.CONF.set_override('service_plugins', [plugin]) self._plugin_patcher = mock.patch(plugin, autospec=True) self.plugin = self._plugin_patcher.start() instance = self.plugin.return_value if service_type: instance.get_plugin_type.return_value = service_type manager.init() if supported_extension_aliases is not None: instance.supported_extension_aliases = supported_extension_aliases if allow_pagination: # instance.__native_pagination_support = True native_pagination_attr_name = ("_%s__native_pagination_support" % instance.__class__.__name__) setattr(instance, native_pagination_attr_name, True) if allow_sorting: # instance.__native_sorting_support = True native_sorting_attr_name = ("_%s__native_sorting_support" % instance.__class__.__name__) setattr(instance, native_sorting_attr_name, True) if use_quota: quota.QUOTAS._driver = None cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', group='QUOTAS') setattr(instance, 'path_prefix', resource_prefix) class ExtensionTestExtensionManager(object): def get_resources(self): return extension_class.get_resources() def get_actions(self): return [] def get_request_extensions(self): return [] ext_mgr = ExtensionTestExtensionManager() self.ext_mdw = test_extensions.setup_extensions_middleware(ext_mgr) self.api = webtest.TestApp(self.ext_mdw) def _test_entity_delete(self, entity): """Does the entity deletion based on naming convention.""" entity_id = uuidutils.generate_uuid() path = self._resource_prefix + '/' if self._resource_prefix else '' path += self._plural_mappings.get(entity, entity + 's') if self._translate_resource_name: path = path.replace('_', '-') res = self.api.delete( test_base._get_path(path, id=entity_id, fmt=self.fmt)) delete_entity = getattr(self.plugin.return_value, "delete_" + entity) delete_entity.assert_called_with(mock.ANY, entity_id) self.assertEqual(exc.HTTPNoContent.code, res.status_int) neutron-12.1.1/neutron/tests/unit/extensions/test_qos_fip.py0000664000175000017500000002474313553660047024416 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from neutron_lib import context from neutron_lib.services.qos import constants as qos_consts from oslo_config import cfg from oslo_utils import uuidutils from neutron.common import exceptions as n_exception from neutron.conf.db import extraroute_db from neutron.db import l3_fip_qos from neutron.extensions import l3 from neutron.extensions import qos_fip from neutron.objects.qos import policy from neutron.tests.unit.extensions import test_l3 class FloatingIPQoSTestExtensionManager(object): def get_resources(self): return l3.L3.get_resources() def get_actions(self): return [] def get_request_extensions(self): return [] class TestFloatingIPQoSIntPlugin( test_l3.TestL3NatIntPlugin, l3_fip_qos.FloatingQoSDbMixin): supported_extension_aliases = ["external-net", "router", qos_fip.FIP_QOS_ALIAS] class TestFloatingIPQoSL3NatServicePlugin( test_l3.TestL3NatServicePlugin, l3_fip_qos.FloatingQoSDbMixin): supported_extension_aliases = ["router", qos_fip.FIP_QOS_ALIAS] class FloatingIPQoSDBTestCaseBase(object): def test_create_fip_with_qos_policy_id(self): ctx = context.get_admin_context() policy_obj = policy.QosPolicy(ctx, id=uuidutils.generate_uuid(), project_id='tenant', name='pol1', rules=[]) policy_obj.create() with self.subnet(cidr='11.0.0.0/24') as s: network_id = s['subnet']['network_id'] self._set_net_external(network_id) fip = self._make_floatingip( self.fmt, network_id, qos_policy_id=policy_obj.id) self.assertEqual(policy_obj.id, fip['floatingip'][qos_consts.QOS_POLICY_ID]) def test_fip_has_qos_policy_id_remove_policy(self): ctx = context.get_admin_context() policy_obj = policy.QosPolicy(ctx, id=uuidutils.generate_uuid(), project_id='tenant', name='pol1', rules=[]) policy_obj.create() with self.subnet(cidr='11.0.0.0/24') as s: network_id = s['subnet']['network_id'] self._set_net_external(network_id) fip = self._make_floatingip( self.fmt, network_id, qos_policy_id=policy_obj.id) self.assertEqual(policy_obj.id, fip['floatingip'][qos_consts.QOS_POLICY_ID]) self.assertRaises(n_exception.QosPolicyInUse, policy_obj.delete) def test_floatingip_update_qos_policy_id(self): ctx = context.get_admin_context() policy_obj_1 = policy.QosPolicy(ctx, id=uuidutils.generate_uuid(), project_id='tenant', name='pol2', rules=[]) policy_obj_1.create() policy_obj_2 = policy.QosPolicy(ctx, id=uuidutils.generate_uuid(), project_id='tenant', name='pol3', rules=[]) policy_obj_2.create() with self.subnet(cidr='11.0.0.0/24') as s: network_id = s['subnet']['network_id'] self._set_net_external(network_id) fip = self._make_floatingip( self.fmt, network_id, qos_policy_id=policy_obj_1.id) self.assertEqual(policy_obj_1.id, fip['floatingip'][qos_consts.QOS_POLICY_ID]) body = self._show('floatingips', fip['floatingip']['id']) self.assertEqual(policy_obj_1.id, body['floatingip'][qos_consts.QOS_POLICY_ID]) body = self._update( 'floatingips', fip['floatingip']['id'], {'floatingip': {qos_consts.QOS_POLICY_ID: policy_obj_2.id}}) self.assertEqual(policy_obj_2.id, body['floatingip'][qos_consts.QOS_POLICY_ID]) def test_floatingip_adding_qos_policy_id_by_update(self): ctx = context.get_admin_context() policy_obj = policy.QosPolicy(ctx, id=uuidutils.generate_uuid(), project_id='tenant', name='pol4', rules=[]) policy_obj.create() with self.subnet(cidr='11.0.0.0/24') as s: network_id = s['subnet']['network_id'] self._set_net_external(network_id) fip = self._make_floatingip( self.fmt, network_id) self.assertIsNone(fip['floatingip'].get(qos_consts.QOS_POLICY_ID)) body = self._update( 'floatingips', fip['floatingip']['id'], {'floatingip': {qos_consts.QOS_POLICY_ID: policy_obj.id}}) body = self._show('floatingips', body['floatingip']['id']) self.assertEqual(policy_obj.id, body['floatingip'][qos_consts.QOS_POLICY_ID]) def test_floatingip_remove_qos_policy_id(self): ctx = context.get_admin_context() policy_obj = policy.QosPolicy(ctx, id=uuidutils.generate_uuid(), project_id='tenant', name='pol5', rules=[]) policy_obj.create() with self.subnet(cidr='11.0.0.0/24') as s: network_id = s['subnet']['network_id'] self._set_net_external(network_id) fip = self._make_floatingip( self.fmt, network_id, qos_policy_id=policy_obj.id) self.assertEqual(policy_obj.id, fip['floatingip'][qos_consts.QOS_POLICY_ID]) self._update( 'floatingips', fip['floatingip']['id'], {'floatingip': {qos_consts.QOS_POLICY_ID: None}}) body = self._show('floatingips', fip['floatingip']['id']) self.assertIsNone( body['floatingip'].get(qos_consts.QOS_POLICY_ID)) def test_floatingip_update_change_nothing(self): ctx = context.get_admin_context() policy_obj = policy.QosPolicy(ctx, id=uuidutils.generate_uuid(), project_id='tenant', name='pol2', rules=[]) policy_obj.create() with self.subnet(cidr='11.0.0.0/24') as s: network_id = s['subnet']['network_id'] self._set_net_external(network_id) fip = self._make_floatingip( self.fmt, network_id) self.assertIsNone(fip['floatingip'].get(qos_consts.QOS_POLICY_ID)) # Updating policy_id from None to None body = self._update( 'floatingips', fip['floatingip']['id'], {'floatingip': {qos_consts.QOS_POLICY_ID: None}}) self.assertIsNone( body['floatingip'].get(qos_consts.QOS_POLICY_ID)) body = self._show('floatingips', fip['floatingip']['id']) self.assertIsNone( body['floatingip'].get(qos_consts.QOS_POLICY_ID)) body = self._update( 'floatingips', fip['floatingip']['id'], {'floatingip': {qos_consts.QOS_POLICY_ID: policy_obj.id}}) self.assertEqual(policy_obj.id, body['floatingip'][qos_consts.QOS_POLICY_ID]) # Updating again with same policy_id body = self._update( 'floatingips', fip['floatingip']['id'], {'floatingip': {qos_consts.QOS_POLICY_ID: policy_obj.id}}) self.assertEqual(policy_obj.id, body['floatingip'][qos_consts.QOS_POLICY_ID]) class FloatingIPQoSDBIntTestCase(test_l3.L3BaseForIntTests, test_l3.L3NatTestCaseMixin, FloatingIPQoSDBTestCaseBase): def setUp(self, plugin=None): if not plugin: plugin = ('neutron.tests.unit.extensions.test_qos_fip.' 'TestFloatingIPQoSIntPlugin') service_plugins = {'qos': 'neutron.services.qos.qos_plugin.QoSPlugin'} extraroute_db.register_db_extraroute_opts() # for these tests we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) cfg.CONF.set_default('max_routes', 3) ext_mgr = FloatingIPQoSTestExtensionManager() super(test_l3.L3BaseForIntTests, self).setUp( plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) self.setup_notification_driver() class FloatingIPQoSDBSepTestCase(test_l3.L3BaseForSepTests, test_l3.L3NatTestCaseMixin, FloatingIPQoSDBTestCaseBase): def setUp(self): # the plugin without L3 support plugin = 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin' # the L3 service plugin l3_plugin = ('neutron.tests.unit.extensions.test_qos_fip.' 'TestFloatingIPQoSL3NatServicePlugin') service_plugins = {'l3_plugin_name': l3_plugin, 'qos': 'neutron.services.qos.qos_plugin.QoSPlugin'} extraroute_db.register_db_extraroute_opts() # for these tests we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) cfg.CONF.set_default('max_routes', 3) ext_mgr = FloatingIPQoSTestExtensionManager() super(test_l3.L3BaseForSepTests, self).setUp( plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) self.setup_notification_driver() neutron-12.1.1/neutron/tests/unit/extensions/test_providernet.py0000664000175000017500000001612313553660047025310 0ustar zuulzuul00000000000000# Copyright 2013 VMware # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import provider_net from neutron_lib import context from neutron_lib import fixture from neutron_lib.plugins import constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import uuidutils from webob import exc as web_exc import webtest from neutron.api import extensions from neutron.api.v2 import router from neutron.extensions import providernet as pnet from neutron import quota from neutron.tests.unit.api import test_extensions from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit import testlib_api class ProviderExtensionManager(object): def get_resources(self): return [] def get_actions(self): return [] def get_request_extensions(self): return [] def get_extended_resources(self, version): return pnet.Providernet().get_extended_resources(version) class ProvidernetExtensionTestCase(testlib_api.WebTestCase): fmt = 'json' def setUp(self): super(ProvidernetExtensionTestCase, self).setUp() plugin = 'neutron.neutron_plugin_base_v2.NeutronPluginBaseV2' # Ensure existing ExtensionManager is not used extensions.PluginAwareExtensionManager._instance = None self.useFixture(fixture.APIDefinitionFixture()) # Update the plugin and extensions path self.setup_coreplugin(plugin, load_plugins=False) self._plugin_patcher = mock.patch(plugin, autospec=True) self.plugin = self._plugin_patcher.start() # Ensure Quota checks never fail because of mock instance = self.plugin.return_value instance.get_networks_count.return_value = 1 # Register mock plugin and enable the 'provider' extension instance.supported_extension_aliases = ["provider"] directory.add_plugin(constants.CORE, instance) ext_mgr = ProviderExtensionManager() self.ext_mdw = test_extensions.setup_extensions_middleware(ext_mgr) self.addCleanup(self._plugin_patcher.stop) self.api = webtest.TestApp(router.APIRouter()) quota.QUOTAS._driver = None cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', group='QUOTAS') def _prepare_net_data(self): return {'name': 'net1', provider_net.NETWORK_TYPE: 'sometype', provider_net.PHYSICAL_NETWORK: 'physnet', provider_net.SEGMENTATION_ID: 666} def _put_network_with_provider_attrs(self, ctx, expect_errors=False): data = self._prepare_net_data() env = {'neutron.context': ctx} instance = self.plugin.return_value instance.get_network.return_value = {'tenant_id': ctx.tenant_id, 'shared': False} net_id = uuidutils.generate_uuid() res = self.api.put(test_base._get_path('networks', id=net_id, fmt=self.fmt), self.serialize({'network': data}), extra_environ=env, expect_errors=expect_errors) return res, data, net_id def _post_network_with_provider_attrs(self, ctx, expect_errors=False): data = self._prepare_net_data() env = {'neutron.context': ctx} res = self.api.post(test_base._get_path('networks', fmt=self.fmt), self.serialize({'network': data}), content_type='application/' + self.fmt, extra_environ=env, expect_errors=expect_errors) return res, data def _post_network_with_bad_provider_attrs(self, ctx, bad_data, expect_errors=False): data = self._prepare_net_data() data.update(bad_data) env = {'neutron.context': ctx} res = self.api.post(test_base._get_path('networks', fmt=self.fmt), self.serialize({'network': data}), content_type='application/' + self.fmt, extra_environ=env, expect_errors=expect_errors) return res, data def test_network_create_with_provider_attrs(self): ctx = context.get_admin_context() tenant_id = 'an_admin' ctx.tenant_id = tenant_id res, data = self._post_network_with_provider_attrs(ctx) instance = self.plugin.return_value exp_input = {'network': data} exp_input['network'].update({'admin_state_up': True, 'tenant_id': tenant_id, 'project_id': tenant_id, 'shared': False}) instance.create_network.assert_called_with(mock.ANY, network=exp_input) self.assertEqual(web_exc.HTTPCreated.code, res.status_int) def test_network_create_with_bad_provider_attrs_400(self): ctx = context.get_admin_context() ctx.tenant_id = 'an_admin' bad_data = {provider_net.SEGMENTATION_ID: "abc"} res, _1 = self._post_network_with_bad_provider_attrs(ctx, bad_data, True) self.assertEqual(web_exc.HTTPBadRequest.code, res.status_int) def test_network_update_with_provider_attrs(self): ctx = context.get_admin_context() ctx.tenant_id = 'an_admin' res, data, net_id = self._put_network_with_provider_attrs(ctx) instance = self.plugin.return_value exp_input = {'network': data} instance.update_network.assert_called_with(mock.ANY, net_id, network=exp_input) self.assertEqual(web_exc.HTTPOk.code, res.status_int) def test_network_create_with_provider_attrs_noadmin_returns_403(self): tenant_id = 'no_admin' ctx = context.Context('', tenant_id, is_admin=False) res, _1 = self._post_network_with_provider_attrs(ctx, True) self.assertEqual(web_exc.HTTPForbidden.code, res.status_int) def test_network_update_with_provider_attrs_noadmin_returns_403(self): tenant_id = 'no_admin' ctx = context.Context('', tenant_id, is_admin=False) res, _1, _2 = self._put_network_with_provider_attrs(ctx, True) self.assertEqual(web_exc.HTTPForbidden.code, res.status_int) neutron-12.1.1/neutron/tests/unit/extensions/test_l3_ext_gw_mode.py0000664000175000017500000005511013553660047025645 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import mock import netaddr from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib import constants from neutron_lib import context as nctx from neutron_lib.plugins import directory from oslo_config import cfg from oslo_db import exception as db_exc from oslo_serialization import jsonutils from oslo_utils import uuidutils import testscenarios from webob import exc from neutron.common import utils from neutron.db import api as db_api from neutron.db import l3_db from neutron.db import l3_gwmode_db from neutron.db.models import l3 as l3_models from neutron.extensions import l3 from neutron.objects import network as net_obj from neutron.objects import ports as port_obj from neutron.objects import router as l3_obj from neutron.objects import subnet as subnet_obj from neutron.tests import base from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit.extensions import test_l3 from neutron.tests.unit import testlib_api _uuid = uuidutils.generate_uuid FAKE_GW_PORT_ID = _uuid() FAKE_GW_PORT_MAC = 'aa:bb:cc:dd:ee:ff' FAKE_FIP_EXT_PORT_ID = _uuid() FAKE_FIP_EXT_PORT_MAC = '11:22:33:44:55:66' FAKE_FIP_INT_PORT_ID = _uuid() FAKE_FIP_INT_PORT_MAC = 'aa:aa:aa:aa:aa:aa' FAKE_ROUTER_PORT_ID = _uuid() FAKE_ROUTER_PORT_MAC = 'bb:bb:bb:bb:bb:bb' class TestExtensionManager(object): def get_resources(self): return l3.L3.get_resources() def get_actions(self): return [] def get_request_extensions(self): return [] # A simple class for making a concrete class out of the mixin # for the case of a plugin that integrates l3 routing. class TestDbIntPlugin(test_l3.TestL3NatIntPlugin, l3_gwmode_db.L3_NAT_db_mixin): supported_extension_aliases = ["external-net", "router", "ext-gw-mode"] # A simple class for making a concrete class out of the mixin # for the case of a l3 router service plugin class TestDbSepPlugin(test_l3.TestL3NatServicePlugin, l3_gwmode_db.L3_NAT_db_mixin): supported_extension_aliases = ["router", "ext-gw-mode"] class TestGetEnableSnat(testscenarios.WithScenarios, base.BaseTestCase): scenarios = [ ('enabled', {'enable_snat_by_default': True}), ('disabled', {'enable_snat_by_default': False})] def setUp(self): super(TestGetEnableSnat, self).setUp() self.config(enable_snat_by_default=self.enable_snat_by_default) def _test_get_enable_snat(self, expected, info): observed = l3_gwmode_db.L3_NAT_dbonly_mixin._get_enable_snat(info) self.assertEqual(expected, observed) def test_get_enable_snat_without_gw_info(self): self._test_get_enable_snat(self.enable_snat_by_default, {}) def test_get_enable_snat_without_enable_snat(self): info = {'network_id': _uuid()} self._test_get_enable_snat(self.enable_snat_by_default, info) def test_get_enable_snat_with_snat_enabled(self): self._test_get_enable_snat(True, {'enable_snat': True}) def test_get_enable_snat_with_snat_disabled(self): self._test_get_enable_snat(False, {'enable_snat': False}) class TestL3GwModeMixin(testlib_api.SqlTestCase): def setUp(self): super(TestL3GwModeMixin, self).setUp() plugin = __name__ + '.' + TestDbIntPlugin.__name__ self.setup_coreplugin(plugin) self.target_object = TestDbIntPlugin() # Patch the context ctx_patcher = mock.patch('neutron_lib.context', autospec=True) mock_context = ctx_patcher.start() self.context = mock_context.get_admin_context() # This ensure also calls to elevated work in unit tests self.context.elevated.return_value = self.context self.context.session = db_api.get_writer_session() # Create sample data for tests self.ext_net_id = _uuid() self.int_net_id = _uuid() self.int_sub_id = _uuid() self.tenant_id = 'the_tenant' self.network = net_obj.Network( self.context, id=self.ext_net_id, project_id=self.tenant_id, admin_state_up=True, status=constants.NET_STATUS_ACTIVE) self.net_ext = net_obj.ExternalNetwork( self.context, network_id=self.ext_net_id) self.network.create() self.net_ext.create() self.router = l3_models.Router( id=_uuid(), name=None, tenant_id=self.tenant_id, admin_state_up=True, status=constants.NET_STATUS_ACTIVE, enable_snat=True, gw_port_id=None) self.context.session.add(self.router) self.context.session.flush() self.router_gw_port = port_obj.Port( self.context, id=FAKE_GW_PORT_ID, project_id=self.tenant_id, device_id=self.router.id, device_owner=l3_db.DEVICE_OWNER_ROUTER_GW, admin_state_up=True, status=constants.PORT_STATUS_ACTIVE, mac_address=netaddr.EUI(FAKE_GW_PORT_MAC), network_id=self.ext_net_id) self.router_gw_port.create() self.router.gw_port_id = self.router_gw_port.id self.context.session.add(self.router) self.context.session.flush() self.fip_ext_port = port_obj.Port( self.context, id=FAKE_FIP_EXT_PORT_ID, project_id=self.tenant_id, admin_state_up=True, device_id=self.router.id, device_owner=l3_db.DEVICE_OWNER_FLOATINGIP, status=constants.PORT_STATUS_ACTIVE, mac_address=netaddr.EUI(FAKE_FIP_EXT_PORT_MAC), network_id=self.ext_net_id) self.fip_ext_port.create() self.context.session.flush() self.int_net = net_obj.Network( self.context, id=self.int_net_id, project_id=self.tenant_id, admin_state_up=True, status=constants.NET_STATUS_ACTIVE) self.int_sub = subnet_obj.Subnet(self.context, id=self.int_sub_id, project_id=self.tenant_id, ip_version=4, cidr=utils.AuthenticIPNetwork('3.3.3.0/24'), gateway_ip=netaddr.IPAddress('3.3.3.1'), network_id=self.int_net_id) self.router_port = port_obj.Port( self.context, id=FAKE_ROUTER_PORT_ID, project_id=self.tenant_id, admin_state_up=True, device_id=self.router.id, device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF, status=constants.PORT_STATUS_ACTIVE, mac_address=netaddr.EUI(FAKE_ROUTER_PORT_MAC), network_id=self.int_net_id) self.router_port_ip_info = port_obj.IPAllocation(self.context, port_id=self.router_port.id, network_id=self.int_net.id, subnet_id=self.int_sub_id, ip_address='3.3.3.1') self.int_net.create() self.int_sub.create() self.router_port.create() self.router_port_ip_info.create() self.context.session.flush() self.fip_int_port = port_obj.Port( self.context, id=FAKE_FIP_INT_PORT_ID, project_id=self.tenant_id, admin_state_up=True, device_id='something', device_owner=constants.DEVICE_OWNER_COMPUTE_PREFIX + 'nova', status=constants.PORT_STATUS_ACTIVE, mac_address=netaddr.EUI(FAKE_FIP_INT_PORT_MAC), network_id=self.int_net_id) self.fip_int_ip_info = port_obj.IPAllocation(self.context, port_id=self.fip_int_port.id, network_id=self.int_net.id, subnet_id=self.int_sub_id, ip_address='3.3.3.3') self.fip = l3_obj.FloatingIP( self.context, id=_uuid(), floating_ip_address=netaddr.IPAddress('1.1.1.2'), floating_network_id=self.ext_net_id, floating_port_id=FAKE_FIP_EXT_PORT_ID, fixed_port_id=None, fixed_ip_address=None, router_id=None) self.fip_int_port.create() self.fip_int_ip_info.create() self.fip.create() self.context.session.flush() self.context.session.expire_all() self.fip_request = {'port_id': FAKE_FIP_INT_PORT_ID, 'tenant_id': self.tenant_id} def _get_gwports_dict(self, gw_ports): return dict((gw_port['id'], gw_port) for gw_port in gw_ports) def _reset_ext_gw(self): # Reset external gateway self.router.gw_port_id = None self.context.session.add(self.router) self.context.session.flush() def _test_update_router_gw(self, current_enable_snat, gw_info=None, expected_enable_snat=True): if not current_enable_snat: previous_gw_info = {'network_id': self.ext_net_id, 'enable_snat': current_enable_snat} self.target_object._update_router_gw_info( self.context, self.router.id, previous_gw_info) self.target_object._update_router_gw_info( self.context, self.router.id, gw_info) router = self.target_object._get_router( self.context, self.router.id) try: self.assertEqual(FAKE_GW_PORT_ID, router.gw_port.id) self.assertEqual(netaddr.EUI(FAKE_GW_PORT_MAC), router.gw_port.mac_address) except AttributeError: self.assertIsNone(router.gw_port) self.assertEqual(expected_enable_snat, router.enable_snat) def test_update_router_gw_with_gw_info_none(self): self._test_update_router_gw(current_enable_snat=True) def test_update_router_gw_without_info_and_snat_disabled_previously(self): self._test_update_router_gw(current_enable_snat=False) def test_update_router_gw_with_network_only(self): info = {'network_id': self.ext_net_id} self._test_update_router_gw(current_enable_snat=True, gw_info=info) def test_update_router_gw_with_network_and_snat_disabled_previously(self): info = {'network_id': self.ext_net_id} self._test_update_router_gw(current_enable_snat=False, gw_info=info) def test_update_router_gw_with_snat_disabled(self): info = {'network_id': self.ext_net_id, 'enable_snat': False} self._test_update_router_gw( current_enable_snat=True, gw_info=info, expected_enable_snat=False) def test_update_router_gw_with_snat_enabled(self): info = {'network_id': self.ext_net_id, 'enable_snat': True} self._test_update_router_gw(current_enable_snat=False, gw_info=info) def test_make_router_dict_no_ext_gw(self): self._reset_ext_gw() router_dict = self.target_object._make_router_dict(self.router) self.assertIsNone(router_dict[l3_apidef.EXTERNAL_GW_INFO]) def test_make_router_dict_with_ext_gw(self): router_dict = self.target_object._make_router_dict(self.router) self.assertEqual({'network_id': self.ext_net_id, 'enable_snat': True, 'external_fixed_ips': []}, router_dict[l3_apidef.EXTERNAL_GW_INFO]) def test_make_router_dict_with_ext_gw_snat_disabled(self): self.router.enable_snat = False router_dict = self.target_object._make_router_dict(self.router) self.assertEqual({'network_id': self.ext_net_id, 'enable_snat': False, 'external_fixed_ips': []}, router_dict[l3_apidef.EXTERNAL_GW_INFO]) def test_build_routers_list_no_ext_gw(self): self._reset_ext_gw() router_dict = self.target_object._make_router_dict(self.router) routers = self.target_object._build_routers_list(self.context, [router_dict], []) self.assertEqual(1, len(routers)) router = routers[0] self.assertIsNone(router.get('gw_port')) self.assertIsNone(router.get('enable_snat')) def test_build_routers_list_with_ext_gw(self): router_dict = self.target_object._make_router_dict(self.router) routers = self.target_object._build_routers_list( self.context, [router_dict], self._get_gwports_dict([self.router.gw_port])) self.assertEqual(1, len(routers)) router = routers[0] self.assertIsNotNone(router.get('gw_port')) self.assertEqual(FAKE_GW_PORT_ID, router['gw_port']['id']) self.assertTrue(router.get('enable_snat')) def test_build_routers_list_with_ext_gw_snat_disabled(self): self.router.enable_snat = False router_dict = self.target_object._make_router_dict(self.router) routers = self.target_object._build_routers_list( self.context, [router_dict], self._get_gwports_dict([self.router.gw_port])) self.assertEqual(1, len(routers)) router = routers[0] self.assertIsNotNone(router.get('gw_port')) self.assertEqual(FAKE_GW_PORT_ID, router['gw_port']['id']) self.assertFalse(router.get('enable_snat')) def test_build_routers_list_with_gw_port_mismatch(self): router_dict = self.target_object._make_router_dict(self.router) routers = self.target_object._build_routers_list( self.context, [router_dict], {}) self.assertEqual(1, len(routers)) router = routers[0] self.assertIsNone(router.get('gw_port')) self.assertIsNone(router.get('enable_snat')) class ExtGwModeIntTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase, test_l3.L3NatTestCaseMixin): def setUp(self, plugin=None, svc_plugins=None, ext_mgr=None): plugin = plugin or ( 'neutron.tests.unit.extensions.test_l3_ext_gw_mode.' 'TestDbIntPlugin') # for these tests we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) ext_mgr = ext_mgr or TestExtensionManager() super(ExtGwModeIntTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr, service_plugins=svc_plugins) def _set_router_external_gateway(self, router_id, network_id, snat_enabled=None, expected_code=exc.HTTPOk.code, neutron_context=None): ext_gw_info = {'network_id': network_id} # Need to set enable_snat also if snat_enabled == False if snat_enabled is not None: ext_gw_info['enable_snat'] = snat_enabled return self._update('routers', router_id, {'router': {'external_gateway_info': ext_gw_info}}, expected_code=expected_code, neutron_context=neutron_context) def test_router_gateway_set_fail_after_port_create(self): with self.router() as r, self.subnet() as s: ext_net_id = s['subnet']['network_id'] self._set_net_external(ext_net_id) plugin = directory.get_plugin() with mock.patch.object(plugin, '_get_port', side_effect=ValueError()): self._set_router_external_gateway(r['router']['id'], ext_net_id, expected_code=500) ports = [p for p in plugin.get_ports(nctx.get_admin_context()) if p['device_owner'] == l3_db.DEVICE_OWNER_ROUTER_GW] self.assertFalse(ports) def test_router_gateway_set_retry(self): with self.router() as r, self.subnet() as s: ext_net_id = s['subnet']['network_id'] self._set_net_external(ext_net_id) with mock.patch.object( l3_db.L3_NAT_dbonly_mixin, '_validate_gw_info', side_effect=[db_exc.RetryRequest(None), ext_net_id]): self._set_router_external_gateway(r['router']['id'], ext_net_id) res = self._show('routers', r['router']['id'])['router'] self.assertEqual(ext_net_id, res['external_gateway_info']['network_id']) def test_router_create_with_gwinfo_invalid_ext_ip(self): with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) ext_info = { 'network_id': s['subnet']['network_id'], 'external_fixed_ips': [{'ip_address': '10.0.0.'}] } error_code = exc.HTTPBadRequest.code res = self._create_router( self.fmt, _uuid(), arg_list=('external_gateway_info',), external_gateway_info=ext_info, expected_code=error_code ) msg = ("Invalid input for external_gateway_info. " "Reason: '10.0.0.' is not a valid IP address.") body = jsonutils.loads(res.body) self.assertEqual(msg, body['NeutronError']['message']) def test_router_create_show_no_ext_gwinfo(self): name = 'router1' tenant_id = _uuid() expected_value = [('name', name), ('tenant_id', tenant_id), ('admin_state_up', True), ('status', 'ACTIVE'), ('external_gateway_info', None)] with self.router(name=name, admin_state_up=True, tenant_id=tenant_id) as router: res = self._show('routers', router['router']['id']) for k, v in expected_value: self.assertEqual(res['router'][k], v) def _test_router_create_show_ext_gwinfo(self, snat_input_value, snat_expected_value): name = 'router1' tenant_id = _uuid() with self.subnet() as s: ext_net_id = s['subnet']['network_id'] self._set_net_external(ext_net_id) input_value = {'network_id': ext_net_id} if snat_input_value in (True, False): input_value['enable_snat'] = snat_input_value expected_value = [('name', name), ('tenant_id', tenant_id), ('admin_state_up', True), ('status', 'ACTIVE'), ('external_gateway_info', {'network_id': ext_net_id, 'enable_snat': snat_expected_value, 'external_fixed_ips': [{ 'ip_address': mock.ANY, 'subnet_id': s['subnet']['id']}]})] with self.router( name=name, admin_state_up=True, tenant_id=tenant_id, external_gateway_info=input_value) as router: res = self._show('routers', router['router']['id']) for k, v in expected_value: self.assertEqual(res['router'][k], v) def test_router_create_show_ext_gwinfo_default(self): self._test_router_create_show_ext_gwinfo(None, True) def test_router_create_show_ext_gwinfo_with_snat_enabled(self): self._test_router_create_show_ext_gwinfo(True, True) def test_router_create_show_ext_gwinfo_with_snat_disabled(self): self._test_router_create_show_ext_gwinfo(False, False) def _test_router_update_ext_gwinfo(self, snat_input_value, snat_expected_value=False, expected_http_code=exc.HTTPOk.code): with self.router() as r: with self.subnet() as s: try: ext_net_id = s['subnet']['network_id'] self._set_net_external(ext_net_id) self._set_router_external_gateway( r['router']['id'], ext_net_id, snat_enabled=snat_input_value, expected_code=expected_http_code) if expected_http_code != exc.HTTPOk.code: return body = self._show('routers', r['router']['id']) res_gw_info = body['router']['external_gateway_info'] self.assertEqual(ext_net_id, res_gw_info['network_id']) self.assertEqual(snat_expected_value, res_gw_info['enable_snat']) finally: self._remove_external_gateway_from_router( r['router']['id'], ext_net_id) def test_router_update_ext_gwinfo_default(self): self._test_router_update_ext_gwinfo(None, True) def test_router_update_ext_gwinfo_with_snat_enabled(self): self._test_router_update_ext_gwinfo(True, True) def test_router_update_ext_gwinfo_with_snat_disabled(self): self._test_router_update_ext_gwinfo(False, False) def test_router_update_ext_gwinfo_with_invalid_snat_setting(self): self._test_router_update_ext_gwinfo( 'xxx', None, expected_http_code=exc.HTTPBadRequest.code) class ExtGwModeSepTestCase(ExtGwModeIntTestCase): def setUp(self, plugin=None): # Store l3 resource attribute map as it will be updated self._l3_attribute_map_bk = {} for item in l3_apidef.RESOURCE_ATTRIBUTE_MAP: self._l3_attribute_map_bk[item] = ( l3_apidef.RESOURCE_ATTRIBUTE_MAP[item].copy()) plugin = plugin or ( 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin') # the L3 service plugin l3_plugin = ('neutron.tests.unit.extensions.test_l3_ext_gw_mode.' 'TestDbSepPlugin') svc_plugins = {'l3_plugin_name': l3_plugin} # for these tests we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) super(ExtGwModeSepTestCase, self).setUp(plugin=plugin, svc_plugins=svc_plugins) neutron-12.1.1/neutron/tests/unit/extensions/test_dns.py0000664000175000017500000006060713553660047023541 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import math import netaddr from neutron_lib import constants from neutron_lib import context from neutron_lib.db import constants as db_const from neutron_lib.plugins import directory from oslo_config import cfg from neutron.common import utils from neutron.db import db_base_plugin_v2 from neutron.extensions import dns from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit.plugins.ml2 import test_plugin class DnsExtensionManager(object): def get_resources(self): return [] def get_actions(self): return [] def get_request_extensions(self): return [] def get_extended_resources(self, version): return dns.Dns().get_extended_resources(version) class DnsExtensionTestPlugin(db_base_plugin_v2.NeutronDbPluginV2): """Test plugin to mixin the DNS Integration extensions. """ supported_extension_aliases = ["dns-integration", "router"] class DnsExtensionTestCase(test_plugin.Ml2PluginV2TestCase): """Test API extension dns attributes. """ _extension_drivers = ['dns'] def setUp(self): cfg.CONF.set_override('extension_drivers', self._extension_drivers, group='ml2') super(DnsExtensionTestCase, self).setUp() def _create_network(self, fmt, name, admin_state_up, arg_list=None, set_context=False, tenant_id=None, **kwargs): new_arg_list = ('dns_domain',) if arg_list is not None: new_arg_list = arg_list + new_arg_list return super(DnsExtensionTestCase, self)._create_network(fmt, name, admin_state_up, arg_list=new_arg_list, set_context=set_context, tenant_id=tenant_id, **kwargs) def _create_port(self, fmt, net_id, expected_res_status=None, arg_list=None, set_context=False, tenant_id=None, **kwargs): tenant_id = tenant_id or self._tenant_id data = {'port': {'network_id': net_id, 'tenant_id': tenant_id}} for arg in (('admin_state_up', 'device_id', 'mac_address', 'name', 'fixed_ips', 'tenant_id', 'device_owner', 'security_groups', 'dns_name') + (arg_list or ())): # Arg must be present if arg in kwargs: data['port'][arg] = kwargs[arg] # create a dhcp port device id if one hasn't been supplied if ('device_owner' in kwargs and kwargs['device_owner'] == constants.DEVICE_OWNER_DHCP and 'host' in kwargs and 'device_id' not in kwargs): device_id = utils.get_dhcp_agent_device_id(net_id, kwargs['host']) data['port']['device_id'] = device_id port_req = self.new_create_request('ports', data, fmt) if set_context and tenant_id: # create a specific auth context for this request port_req.environ['neutron.context'] = context.Context( '', tenant_id) port_res = port_req.get_response(self.api) if expected_res_status: self.assertEqual(expected_res_status, port_res.status_int) return port_res def _test_list_resources(self, resource, items, neutron_context=None, query_params=None): res = self._list('%ss' % resource, neutron_context=neutron_context, query_params=query_params) resource = resource.replace('-', '_') self.assertItemsEqual([i['id'] for i in res['%ss' % resource]], [i[resource]['id'] for i in items]) return res def test_create_port_json(self): keys = [('admin_state_up', True), ('status', self.port_create_status)] with self.port(name='myname') as port: for k, v in keys: self.assertEqual(port['port'][k], v) self.assertIn('mac_address', port['port']) ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) subnet_db = directory.get_plugin().get_subnet( context.get_admin_context(), ips[0]['subnet_id']) self.assertIn(netaddr.IPAddress(ips[0]['ip_address']), netaddr.IPSet(netaddr.IPNetwork(subnet_db['cidr']))) self.assertEqual('myname', port['port']['name']) self._verify_dns_assigment(port['port'], ips_list=[ips[0]['ip_address']]) def test_list_ports(self): # for this test we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) with self.port() as v1, self.port() as v2, self.port() as v3: ports = (v1, v2, v3) res = self._test_list_resources('port', ports) for port in res['ports']: self._verify_dns_assigment( port, ips_list=[port['fixed_ips'][0]['ip_address']]) def test_show_port(self): with self.port() as port: req = self.new_show_request('ports', port['port']['id'], self.fmt) sport = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(port['port']['id'], sport['port']['id']) self._verify_dns_assigment( sport['port'], ips_list=[sport['port']['fixed_ips'][0]['ip_address']]) def test_update_port_non_default_dns_domain_with_dns_name(self): with self.port() as port: port_ip = port['port']['fixed_ips'][0]['ip_address'] cfg.CONF.set_override('dns_domain', 'example.com') data = {'port': {'admin_state_up': False, 'dns_name': 'vm1'}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['port']['admin_state_up'], res['port']['admin_state_up']) self._verify_dns_assigment(res['port'], ips_list=[port_ip], dns_name='vm1') def test_update_port_default_dns_domain_with_dns_name(self): with self.port() as port: port_ip = port['port']['fixed_ips'][0]['ip_address'] data = {'port': {'admin_state_up': False, 'dns_name': 'vm1'}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['port']['admin_state_up'], res['port']['admin_state_up']) self._verify_dns_assigment(res['port'], ips_list=[port_ip]) def _verify_dns_assigment(self, port, ips_list=None, exp_ips_ipv4=0, exp_ips_ipv6=0, ipv4_cidrs=None, ipv6_cidrs=None, dns_name=''): ips_list = ips_list or [] ipv4_cidrs = ipv4_cidrs or [] ipv6_cidrs = ipv6_cidrs or [] self.assertEqual(dns_name, port['dns_name']) dns_assignment = port['dns_assignment'] if ips_list: self.assertEqual(len(dns_assignment), len(ips_list)) ips_set = set(ips_list) else: self.assertEqual(len(dns_assignment), exp_ips_ipv4 + exp_ips_ipv6) ipv4_count = 0 ipv6_count = 0 subnets_v4 = [netaddr.IPNetwork(cidr) for cidr in ipv4_cidrs] subnets_v6 = [netaddr.IPNetwork(cidr) for cidr in ipv6_cidrs] request_dns_name, request_fqdn = self._get_request_hostname_and_fqdn( dns_name) for assignment in dns_assignment: if ips_list: self.assertIn(assignment['ip_address'], ips_set) ips_set.remove(assignment['ip_address']) else: ip = netaddr.IPAddress(assignment['ip_address']) if ip.version == 4: self.assertTrue(self._verify_ip_in_subnet(ip, subnets_v4)) ipv4_count += 1 else: self.assertTrue(self._verify_ip_in_subnet(ip, subnets_v6)) ipv6_count += 1 hostname, fqdn = self._get_hostname_and_fqdn(request_dns_name, request_fqdn, assignment) self.assertEqual(assignment['hostname'], hostname) self.assertEqual(assignment['fqdn'], fqdn) if ips_list: self.assertFalse(ips_set) else: self.assertEqual(ipv4_count, exp_ips_ipv4) self.assertEqual(ipv6_count, exp_ips_ipv6) def _get_dns_domain(self): if not cfg.CONF.dns_domain: return '' if cfg.CONF.dns_domain.endswith('.'): return cfg.CONF.dns_domain return '%s.' % cfg.CONF.dns_domain def _get_request_hostname_and_fqdn(self, dns_name): request_dns_name = '' request_fqdn = '' dns_domain = self._get_dns_domain() if dns_name and dns_domain and dns_domain != 'openstacklocal.': request_dns_name = dns_name request_fqdn = request_dns_name if not request_dns_name.endswith('.'): request_fqdn = '%s.%s' % (dns_name, dns_domain) return request_dns_name, request_fqdn def _get_hostname_and_fqdn(self, request_dns_name, request_fqdn, assignment): dns_domain = self._get_dns_domain() if request_dns_name: hostname = request_dns_name fqdn = request_fqdn else: hostname = 'host-%s' % assignment['ip_address'].replace( '.', '-').replace(':', '-') fqdn = hostname if dns_domain: fqdn = '%s.%s' % (hostname, dns_domain) return hostname, fqdn def _verify_ip_in_subnet(self, ip, subnets_list): for subnet in subnets_list: if ip in subnet: return True return False def test_update_port_update_ip(self): """Test update of port IP. Check that a configured IP 10.0.0.2 is replaced by 10.0.0.10. """ with self.subnet() as subnet: fixed_ip_data = [{'ip_address': '10.0.0.2'}] with self.port(subnet=subnet, fixed_ips=fixed_ip_data) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) data = {'port': {'fixed_ips': [{'subnet_id': subnet['subnet']['id'], 'ip_address': "10.0.0.10"}]}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) ips = res['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(ips[0]['ip_address'], '10.0.0.10') self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) self._verify_dns_assigment(res['port'], ips_list=['10.0.0.10']) def test_update_port_update_ip_address_only(self): with self.subnet() as subnet: fixed_ip_data = [{'ip_address': '10.0.0.2'}] with self.port(subnet=subnet, fixed_ips=fixed_ip_data) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) data = {'port': {'fixed_ips': [{'subnet_id': subnet['subnet']['id'], 'ip_address': "10.0.0.10"}, {'ip_address': "10.0.0.2"}]}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) ips = res['port']['fixed_ips'] self.assertEqual(2, len(ips)) self.assertIn({'ip_address': '10.0.0.2', 'subnet_id': subnet['subnet']['id']}, ips) self.assertIn({'ip_address': '10.0.0.10', 'subnet_id': subnet['subnet']['id']}, ips) self._verify_dns_assigment(res['port'], ips_list=['10.0.0.10', '10.0.0.2']) def test_create_port_with_multiple_ipv4_and_ipv6_subnets(self): res = self._test_create_port_with_multiple_ipv4_and_ipv6_subnets() self.assertEqual(201, res.status_code) def test_create_port_multiple_v4_v6_subnets_pqdn_and_dns_domain_no_period( self): cfg.CONF.set_override('dns_domain', 'example.com') res = self._test_create_port_with_multiple_ipv4_and_ipv6_subnets( dns_name='vm1') self.assertEqual(201, res.status_code) def test_create_port_multiple_v4_v6_subnets_pqdn_and_dns_domain_period( self): cfg.CONF.set_override('dns_domain', 'example.com.') res = self._test_create_port_with_multiple_ipv4_and_ipv6_subnets( dns_name='vm1') self.assertEqual(201, res.status_code) def test_create_port_multiple_v4_v6_subnets_pqdn_and_no_dns_domain( self): cfg.CONF.set_override('dns_domain', '') res = self._test_create_port_with_multiple_ipv4_and_ipv6_subnets() self.assertEqual(201, res.status_code) def test_create_port_multiple_v4_v6_subnets_fqdn_and_dns_domain_no_period( self): cfg.CONF.set_override('dns_domain', 'example.com') res = self._test_create_port_with_multiple_ipv4_and_ipv6_subnets( dns_name='vm1.example.com.') self.assertEqual(201, res.status_code) def test_create_port_multiple_v4_v6_subnets_fqdn_and_dns_domain_period( self): cfg.CONF.set_override('dns_domain', 'example.com.') res = self._test_create_port_with_multiple_ipv4_and_ipv6_subnets( dns_name='vm1.example.com.') self.assertEqual(201, res.status_code) def test_create_port_multiple_v4_v6_subnets_fqdn_default_domain_period( self): cfg.CONF.set_override('dns_domain', 'openstacklocal.') res = self._test_create_port_with_multiple_ipv4_and_ipv6_subnets() self.assertEqual(201, res.status_code) def test_create_port_multiple_v4_v6_subnets_bad_fqdn_and_dns_domain( self): cfg.CONF.set_override('dns_domain', 'example.com') res = self._test_create_port_with_multiple_ipv4_and_ipv6_subnets( dns_name='vm1.bad-domain.com.') self.assertEqual(400, res.status_code) expected_error = ('The dns_name passed is a FQDN. Its higher level ' 'labels must be equal to the dns_domain option in ' 'neutron.conf') self.assertIn(expected_error, res.text) def test_create_port_multiple_v4_v6_subnets_bad_pqdn_and_dns_domain( self): cfg.CONF.set_override('dns_domain', 'example.com') num_labels = int( math.floor(db_const.FQDN_FIELD_SIZE / constants.DNS_LABEL_MAX_LEN)) filler_len = int( math.floor(db_const.FQDN_FIELD_SIZE % constants.DNS_LABEL_MAX_LEN)) dns_name = (('a' * (constants.DNS_LABEL_MAX_LEN - 1) + '.') * num_labels + 'a' * filler_len) res = self._test_create_port_with_multiple_ipv4_and_ipv6_subnets( dns_name=dns_name) self.assertEqual(400, res.status_code) expected_error = ("When the two are concatenated to form a FQDN " "(with a '.' at the end), the resulting length " "exceeds the maximum size") self.assertIn(expected_error, res.text) def _test_create_port_with_multiple_ipv4_and_ipv6_subnets(self, dns_name=''): """Test port create with multiple IPv4, IPv6 DHCP/SLAAC subnets.""" res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) sub_dicts = [ {'gateway': '10.0.0.1', 'cidr': '10.0.0.0/24', 'ip_version': 4, 'ra_addr_mode': None}, {'gateway': '10.0.1.1', 'cidr': '10.0.1.0/24', 'ip_version': 4, 'ra_addr_mode': None}, {'gateway': 'fe80::1', 'cidr': 'fe80::/64', 'ip_version': 6, 'ra_addr_mode': constants.IPV6_SLAAC}, {'gateway': 'fe81::1', 'cidr': 'fe81::/64', 'ip_version': 6, 'ra_addr_mode': constants.IPV6_SLAAC}, {'gateway': 'fe82::1', 'cidr': 'fe82::/64', 'ip_version': 6, 'ra_addr_mode': constants.DHCPV6_STATEFUL}, {'gateway': 'fe83::1', 'cidr': 'fe83::/64', 'ip_version': 6, 'ra_addr_mode': constants.DHCPV6_STATEFUL}] subnets = {} for sub_dict in sub_dicts: subnet = self._make_subnet( self.fmt, network, gateway=sub_dict['gateway'], cidr=sub_dict['cidr'], ip_version=sub_dict['ip_version'], ipv6_ra_mode=sub_dict['ra_addr_mode'], ipv6_address_mode=sub_dict['ra_addr_mode']) subnets[subnet['subnet']['id']] = sub_dict res = self._create_port(self.fmt, net_id=network['network']['id'], dns_name=dns_name) if res.status_code != 201: return res port = self.deserialize(self.fmt, res) # Since the create port request was made without a list of fixed IPs, # the port should be associated with addresses for one of the # IPv4 subnets, one of the DHCPv6 subnets, and both of the IPv6 # SLAAC subnets. self.assertEqual(4, len(port['port']['fixed_ips'])) addr_mode_count = {None: 0, constants.DHCPV6_STATEFUL: 0, constants.IPV6_SLAAC: 0} for fixed_ip in port['port']['fixed_ips']: subnet_id = fixed_ip['subnet_id'] if subnet_id in subnets: addr_mode_count[subnets[subnet_id]['ra_addr_mode']] += 1 self.assertEqual(1, addr_mode_count[None]) self.assertEqual(1, addr_mode_count[constants.DHCPV6_STATEFUL]) self.assertEqual(2, addr_mode_count[constants.IPV6_SLAAC]) self._verify_dns_assigment(port['port'], exp_ips_ipv4=1, exp_ips_ipv6=3, ipv4_cidrs=[sub_dicts[0]['cidr'], sub_dicts[1]['cidr']], ipv6_cidrs=[sub_dicts[2]['cidr'], sub_dicts[3]['cidr'], sub_dicts[4]['cidr'], sub_dicts[5]['cidr']], dns_name=dns_name) return res def test_api_extension_validation_with_bad_dns_names(self): num_labels = int( math.floor(db_const.FQDN_FIELD_SIZE / constants.DNS_LABEL_MAX_LEN)) filler_len = int( math.floor(db_const.FQDN_FIELD_SIZE % constants.DNS_LABEL_MAX_LEN)) dns_names = [555, '\f\n\r', '.', '-vm01', '_vm01', 'vm01-', '-vm01.test1', 'vm01.-test1', 'vm01._test1', 'vm01.test1-', 'vm01.te$t1', 'vm0#1.test1.', 'vm01.123.', '-' + 'a' * constants.DNS_LABEL_MAX_LEN, 'a' * (constants.DNS_LABEL_MAX_LEN + 1), ('a' * (constants.DNS_LABEL_MAX_LEN - 1) + '.') * num_labels + 'a' * (filler_len + 1)] res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) sub_dict = {'gateway': '10.0.0.1', 'cidr': '10.0.0.0/24', 'ip_version': 4, 'ra_addr_mode': None} self._make_subnet(self.fmt, network, gateway=sub_dict['gateway'], cidr=sub_dict['cidr'], ip_version=sub_dict['ip_version'], ipv6_ra_mode=sub_dict['ra_addr_mode'], ipv6_address_mode=sub_dict['ra_addr_mode']) for dns_name in dns_names: res = self._create_port(self.fmt, net_id=network['network']['id'], dns_name=dns_name) self.assertEqual(400, res.status_code) error_message = res.json['NeutronError']['message'] is_expected_message = ( 'cannot be converted to lowercase string' in error_message or 'not a valid PQDN or FQDN. Reason:' in error_message or 'must be string type' in error_message) self.assertTrue(is_expected_message) def test_api_extension_validation_with_good_dns_names(self): cfg.CONF.set_override('dns_domain', 'example.com') higher_labels_len = len('example.com.') num_labels = int( math.floor((db_const.FQDN_FIELD_SIZE - higher_labels_len) / constants.DNS_LABEL_MAX_LEN)) filler_len = int( math.floor((db_const.FQDN_FIELD_SIZE - higher_labels_len) % constants.DNS_LABEL_MAX_LEN)) dns_names = ['', 'www.1000.com', 'vM01', 'vm01.example.com.', '8vm01', 'vm-01.example.com.', 'vm01.test', 'vm01.test.example.com.', 'vm01.test-100', 'vm01.test-100.example.com.', 'a' * constants.DNS_LABEL_MAX_LEN, ('a' * constants.DNS_LABEL_MAX_LEN) + '.example.com.', ('a' * (constants.DNS_LABEL_MAX_LEN - 1) + '.') * num_labels + 'a' * (filler_len - 1)] res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) sub_dict = {'gateway': '10.0.0.1', 'cidr': '10.0.0.0/24', 'ip_version': 4, 'ra_addr_mode': None} self._make_subnet(self.fmt, network, gateway=sub_dict['gateway'], cidr=sub_dict['cidr'], ip_version=sub_dict['ip_version'], ipv6_ra_mode=sub_dict['ra_addr_mode'], ipv6_address_mode=sub_dict['ra_addr_mode']) for dns_name in dns_names: res = self._create_port(self.fmt, net_id=network['network']['id'], dns_name=dns_name) self.assertEqual(201, res.status_code) class DnsExtensionTestNetworkDnsDomain( test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def setUp(self): plugin = ('neutron.tests.unit.extensions.test_dns.' + 'DnsExtensionTestPlugin') ext_mgr = DnsExtensionManager() super(DnsExtensionTestNetworkDnsDomain, self).setUp( plugin=plugin, ext_mgr=ext_mgr) def test_update_network_dns_domain(self): with self.network() as network: data = {'network': {'dns_domain': 'my-domain.org.'}} req = self.new_update_request('networks', data, network['network']['id']) res = req.get_response(self.api) self.assertEqual(200, res.status_code) self.assertNotIn('dns_domain', self.deserialize(self.fmt, res)['network']) neutron-12.1.1/neutron/tests/unit/extensions/foxinsocks.py0000664000175000017500000000720213553660047024074 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.api import extensions as api_extensions from neutron_lib.services import base from oslo_serialization import jsonutils import six from neutron.api import extensions from neutron import wsgi class FoxInSocksController(wsgi.Controller): def index(self, request): return "Try to say this Mr. Knox, sir..." @six.add_metaclass(abc.ABCMeta) class FoxInSocksPluginInterface(base.ServicePluginBase): @abc.abstractmethod def method_to_support_foxnsox_extension(self): pass class Foxinsocks(api_extensions.ExtensionDescriptor): def __init__(self): pass def get_plugin_interface(self): return FoxInSocksPluginInterface def get_name(self): return "Fox In Socks" def get_alias(self): return "FOXNSOX" def get_description(self): return "The Fox In Socks Extension" def get_updated(self): return "2011-01-22T13:25:27-06:00" def get_resources(self): resources = [] resource = extensions.ResourceExtension('foxnsocks', FoxInSocksController()) resources.append(resource) return resources def get_actions(self): return [extensions.ActionExtension('dummy_resources', 'FOXNSOX:add_tweedle', self._add_tweedle_handler), extensions.ActionExtension('dummy_resources', 'FOXNSOX:delete_tweedle', self._delete_tweedle_handler)] def get_request_extensions(self): request_exts = [] def _goose_handler(req, res): #NOTE: This only handles JSON responses. # You can use content type header to test for XML. data = jsonutils.loads(res.body) data['FOXNSOX:googoose'] = req.GET.get('chewing') res.body = jsonutils.dump_as_bytes(data) return res req_ext1 = extensions.RequestExtension('GET', '/dummy_resources/:(id)', _goose_handler) request_exts.append(req_ext1) def _bands_handler(req, res): #NOTE: This only handles JSON responses. # You can use content type header to test for XML. data = jsonutils.loads(res.body) data['FOXNSOX:big_bands'] = 'Pig Bands!' res.body = jsonutils.dump_as_bytes(data) return res req_ext2 = extensions.RequestExtension('GET', '/dummy_resources/:(id)', _bands_handler) request_exts.append(req_ext2) return request_exts def _add_tweedle_handler(self, input_dict, req, id): return "Tweedle {0} Added.".format( input_dict['FOXNSOX:add_tweedle']['name']) def _delete_tweedle_handler(self, input_dict, req, id): return "Tweedle {0} Deleted.".format( input_dict['FOXNSOX:delete_tweedle']['name']) neutron-12.1.1/neutron/tests/unit/extensions/test_l3.py0000664000175000017500000064626313553660047023303 0ustar zuulzuul00000000000000# Copyright 2012 VMware, Inc. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import contextlib import copy import mock import netaddr from neutron_lib.api.definitions import external_net as extnet_apidef from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import exceptions from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as lib_constants from neutron_lib import context from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import l3 as l3_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import importutils from oslo_utils import uuidutils from sqlalchemy import orm import testtools from webob import exc from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api from neutron.api.rpc.handlers import l3_rpc from neutron.db import _resource_extend as resource_extend from neutron.db import common_db_mixin from neutron.db import db_base_plugin_v2 from neutron.db import dns_db from neutron.db import external_net_db from neutron.db import l3_agentschedulers_db from neutron.db import l3_attrs_db from neutron.db import l3_db from neutron.db import l3_dvr_db from neutron.db import l3_dvrscheduler_db from neutron.db import l3_hamode_db from neutron.db.models import l3 as l3_models from neutron.db import models_v2 from neutron.extensions import l3 from neutron.services.revisions import revision_plugin from neutron.tests import base from neutron.tests.common import helpers from neutron.tests import fake_notifier from neutron.tests.unit.api import test_extensions from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit.extensions import base as test_extensions_base from neutron.tests.unit.extensions import test_agent from neutron.tests.unit.plugins.ml2 import base as ml2_base from neutron.tests.unit import testlib_api _uuid = uuidutils.generate_uuid _get_path = test_base._get_path DEVICE_OWNER_COMPUTE = lib_constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' class L3TestExtensionManager(object): def get_resources(self): return l3.L3.get_resources() def get_actions(self): return [] def get_request_extensions(self): return [] class L3NatExtensionTestCase(test_extensions_base.ExtensionTestCase): fmt = 'json' def setUp(self): super(L3NatExtensionTestCase, self).setUp() self._setUpExtension( 'neutron.services.l3_router.l3_router_plugin.L3RouterPlugin', plugin_constants.L3, {}, l3.L3, '', allow_pagination=True, allow_sorting=True, supported_extension_aliases=['router'], use_quota=True) def test_router_create(self): router_id = _uuid() tenant_id = _uuid() data = {'router': {'name': 'router1', 'admin_state_up': True, 'tenant_id': tenant_id, 'project_id': tenant_id, 'external_gateway_info': None}} return_value = copy.deepcopy(data['router']) return_value.update({'status': "ACTIVE", 'id': router_id}) instance = self.plugin.return_value instance.create_router.return_value = return_value instance.get_routers_count.return_value = 0 res = self.api.post(_get_path('routers', fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt) instance.create_router.assert_called_with(mock.ANY, router=data) self.assertEqual(exc.HTTPCreated.code, res.status_int) res = self.deserialize(res) self.assertIn('router', res) router = res['router'] self.assertEqual(router_id, router['id']) self.assertEqual("ACTIVE", router['status']) self.assertTrue(router['admin_state_up']) def test_router_list(self): router_id = _uuid() return_value = [{'name': 'router1', 'admin_state_up': True, 'tenant_id': _uuid(), 'id': router_id}] instance = self.plugin.return_value instance.get_routers.return_value = return_value res = self.api.get(_get_path('routers', fmt=self.fmt)) instance.get_routers.assert_called_with(mock.ANY, fields=mock.ANY, filters=mock.ANY, sorts=mock.ANY, limit=mock.ANY, marker=mock.ANY, page_reverse=mock.ANY) self.assertEqual(exc.HTTPOk.code, res.status_int) res = self.deserialize(res) self.assertIn('routers', res) self.assertEqual(1, len(res['routers'])) self.assertEqual(router_id, res['routers'][0]['id']) def test_router_update(self): router_id = _uuid() update_data = {'router': {'admin_state_up': False}} return_value = {'name': 'router1', 'admin_state_up': False, 'tenant_id': _uuid(), 'status': "ACTIVE", 'id': router_id} instance = self.plugin.return_value instance.update_router.return_value = return_value res = self.api.put(_get_path('routers', id=router_id, fmt=self.fmt), self.serialize(update_data)) instance.update_router.assert_called_with(mock.ANY, router_id, router=update_data) self.assertEqual(exc.HTTPOk.code, res.status_int) res = self.deserialize(res) self.assertIn('router', res) router = res['router'] self.assertEqual(router_id, router['id']) self.assertEqual("ACTIVE", router['status']) self.assertFalse(router['admin_state_up']) def test_router_get(self): router_id = _uuid() return_value = {'name': 'router1', 'admin_state_up': False, 'tenant_id': _uuid(), 'status': "ACTIVE", 'id': router_id} instance = self.plugin.return_value instance.get_router.return_value = return_value res = self.api.get(_get_path('routers', id=router_id, fmt=self.fmt)) instance.get_router.assert_called_with(mock.ANY, router_id, fields=mock.ANY) self.assertEqual(exc.HTTPOk.code, res.status_int) res = self.deserialize(res) self.assertIn('router', res) router = res['router'] self.assertEqual(router_id, router['id']) self.assertEqual("ACTIVE", router['status']) self.assertFalse(router['admin_state_up']) def test_router_delete(self): router_id = _uuid() res = self.api.delete(_get_path('routers', id=router_id)) instance = self.plugin.return_value instance.delete_router.assert_called_with(mock.ANY, router_id) self.assertEqual(exc.HTTPNoContent.code, res.status_int) def test_router_add_interface(self): router_id = _uuid() subnet_id = _uuid() port_id = _uuid() interface_data = {'subnet_id': subnet_id} return_value = copy.deepcopy(interface_data) return_value['port_id'] = port_id instance = self.plugin.return_value instance.add_router_interface.return_value = return_value path = _get_path('routers', id=router_id, action="add_router_interface", fmt=self.fmt) res = self.api.put(path, self.serialize(interface_data)) instance.add_router_interface.assert_called_with(mock.ANY, router_id, interface_data) self.assertEqual(exc.HTTPOk.code, res.status_int) res = self.deserialize(res) self.assertIn('port_id', res) self.assertEqual(port_id, res['port_id']) self.assertEqual(subnet_id, res['subnet_id']) def test_router_add_interface_empty_body(self): router_id = _uuid() instance = self.plugin.return_value path = _get_path('routers', id=router_id, action="add_router_interface", fmt=self.fmt) res = self.api.put(path) self.assertEqual(exc.HTTPOk.code, res.status_int) instance.add_router_interface.assert_called_with(mock.ANY, router_id) # This base plugin class is for tests. class TestL3NatBasePlugin(db_base_plugin_v2.NeutronDbPluginV2, external_net_db.External_net_db_mixin): __native_pagination_support = True __native_sorting_support = True def create_network(self, context, network): session = context.session with session.begin(subtransactions=True): net = super(TestL3NatBasePlugin, self).create_network(context, network) self._process_l3_create(context, net, network['network']) return net def update_network(self, context, id, network): session = context.session with session.begin(subtransactions=True): net = super(TestL3NatBasePlugin, self).update_network(context, id, network) self._process_l3_update(context, net, network['network']) return net def delete_port(self, context, id, l3_port_check=True): plugin = directory.get_plugin(plugin_constants.L3) if plugin: if l3_port_check: plugin.prevent_l3_port_deletion(context, id) plugin.disassociate_floatingips(context, id) return super(TestL3NatBasePlugin, self).delete_port(context, id) # This plugin class is for tests with plugin that integrates L3. class TestL3NatIntPlugin(TestL3NatBasePlugin, l3_db.L3_NAT_db_mixin, dns_db.DNSDbMixin): __native_pagination_support = True __native_sorting_support = True supported_extension_aliases = ["external-net", "router", "dns-integration"] # This plugin class is for tests with plugin that integrates L3 and L3 agent # scheduling. class TestL3NatIntAgentSchedulingPlugin(TestL3NatIntPlugin, l3_agentschedulers_db. L3AgentSchedulerDbMixin, l3_hamode_db.L3_HA_NAT_db_mixin): supported_extension_aliases = ["external-net", "router", "l3_agent_scheduler"] router_scheduler = importutils.import_object( cfg.CONF.router_scheduler_driver) # This plugin class is for tests with plugin not supporting L3. class TestNoL3NatPlugin(TestL3NatBasePlugin): __native_pagination_support = True __native_sorting_support = True supported_extension_aliases = ["external-net"] # A L3 routing service plugin class for tests with plugins that # delegate away L3 routing functionality class TestL3NatServicePlugin(common_db_mixin.CommonDbMixin, l3_dvr_db.L3_NAT_with_dvr_db_mixin, l3_db.L3_NAT_db_mixin, dns_db.DNSDbMixin): __native_pagination_support = True __native_sorting_support = True supported_extension_aliases = ["router", "dns-integration"] @classmethod def get_plugin_type(cls): return plugin_constants.L3 def get_plugin_description(self): return "L3 Routing Service Plugin for testing" # A L3 routing with L3 agent scheduling service plugin class for tests with # plugins that delegate away L3 routing functionality class TestL3NatAgentSchedulingServicePlugin(TestL3NatServicePlugin, l3_dvrscheduler_db. L3_DVRsch_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin): supported_extension_aliases = ["router", "l3_agent_scheduler"] def __init__(self): super(TestL3NatAgentSchedulingServicePlugin, self).__init__() self.router_scheduler = importutils.import_object( cfg.CONF.router_scheduler_driver) self.agent_notifiers.update( {lib_constants.AGENT_TYPE_L3: l3_rpc_agent_api.L3AgentNotifyAPI()}) class L3NatTestCaseMixin(object): def _create_router(self, fmt, tenant_id, name=None, admin_state_up=None, set_context=False, arg_list=None, **kwargs): tenant_id = tenant_id or _uuid() data = {'router': {'tenant_id': tenant_id}} if name: data['router']['name'] = name if admin_state_up is not None: data['router']['admin_state_up'] = admin_state_up for arg in (('admin_state_up', 'tenant_id', 'availability_zone_hints') + (arg_list or ())): # Arg must be present and not empty if arg in kwargs: data['router'][arg] = kwargs[arg] router_req = self.new_create_request('routers', data, fmt) if set_context and tenant_id: # create a specific auth context for this request router_req.environ['neutron.context'] = context.Context( '', tenant_id) return router_req.get_response(self.ext_api) def _make_router(self, fmt, tenant_id, name=None, admin_state_up=None, external_gateway_info=None, set_context=False, arg_list=None, **kwargs): if external_gateway_info: arg_list = ('external_gateway_info', ) + (arg_list or ()) res = self._create_router(fmt, tenant_id, name, admin_state_up, set_context, arg_list=arg_list, external_gateway_info=external_gateway_info, **kwargs) return self.deserialize(fmt, res) def _add_external_gateway_to_router(self, router_id, network_id, expected_code=exc.HTTPOk.code, neutron_context=None, ext_ips=None): ext_ips = ext_ips or [] body = {'router': {'external_gateway_info': {'network_id': network_id}}} if ext_ips: body['router']['external_gateway_info'][ 'external_fixed_ips'] = ext_ips return self._update('routers', router_id, body, expected_code=expected_code, neutron_context=neutron_context) def _remove_external_gateway_from_router(self, router_id, network_id, expected_code=exc.HTTPOk.code, external_gw_info=None): return self._update('routers', router_id, {'router': {'external_gateway_info': external_gw_info}}, expected_code=expected_code) def _router_interface_action(self, action, router_id, subnet_id, port_id, expected_code=exc.HTTPOk.code, expected_body=None, tenant_id=None, msg=None): interface_data = {} if subnet_id is not None: interface_data.update({'subnet_id': subnet_id}) if port_id is not None: interface_data.update({'port_id': port_id}) req = self.new_action_request('routers', interface_data, router_id, "%s_router_interface" % action) # if tenant_id was specified, create a tenant context for this request if tenant_id: req.environ['neutron.context'] = context.Context( '', tenant_id) res = req.get_response(self.ext_api) self.assertEqual(expected_code, res.status_int, msg) response = self.deserialize(self.fmt, res) if expected_body: self.assertEqual(expected_body, response, msg) return response @contextlib.contextmanager def router(self, name='router1', admin_state_up=True, fmt=None, tenant_id=None, external_gateway_info=None, set_context=False, **kwargs): router = self._make_router(fmt or self.fmt, tenant_id, name, admin_state_up, external_gateway_info, set_context, **kwargs) yield router def _set_net_external(self, net_id): self._update('networks', net_id, {'network': {extnet_apidef.EXTERNAL: True}}) def _create_floatingip(self, fmt, network_id, port_id=None, fixed_ip=None, set_context=False, floating_ip=None, subnet_id=None, tenant_id=None, **kwargs): tenant_id = tenant_id or self._tenant_id data = {'floatingip': {'floating_network_id': network_id, 'tenant_id': tenant_id}} if port_id: data['floatingip']['port_id'] = port_id if fixed_ip: data['floatingip']['fixed_ip_address'] = fixed_ip if floating_ip: data['floatingip']['floating_ip_address'] = floating_ip if subnet_id: data['floatingip']['subnet_id'] = subnet_id data['floatingip'].update(kwargs) floatingip_req = self.new_create_request('floatingips', data, fmt) if set_context and tenant_id: # create a specific auth context for this request floatingip_req.environ['neutron.context'] = context.Context( '', tenant_id) return floatingip_req.get_response(self.ext_api) def _make_floatingip(self, fmt, network_id, port_id=None, fixed_ip=None, set_context=False, tenant_id=None, floating_ip=None, http_status=exc.HTTPCreated.code, **kwargs): res = self._create_floatingip(fmt, network_id, port_id, fixed_ip, set_context, floating_ip, tenant_id=tenant_id, **kwargs) self.assertEqual(http_status, res.status_int) return self.deserialize(fmt, res) def _validate_floating_ip(self, fip): body = self._list('floatingips') self.assertEqual(1, len(body['floatingips'])) self.assertEqual(body['floatingips'][0]['id'], fip['floatingip']['id']) body = self._show('floatingips', fip['floatingip']['id']) self.assertEqual(body['floatingip']['id'], fip['floatingip']['id']) @contextlib.contextmanager def floatingip_with_assoc(self, port_id=None, fmt=None, fixed_ip=None, public_cidr='11.0.0.0/24', set_context=False, tenant_id=None, **kwargs): with self.subnet(cidr=public_cidr, set_context=set_context, tenant_id=tenant_id) as public_sub: self._set_net_external(public_sub['subnet']['network_id']) private_port = None if port_id: private_port = self._show('ports', port_id) with test_db_base_plugin_v2.optional_ctx( private_port, self.port, set_context=set_context, tenant_id=tenant_id) as private_port: with self.router(set_context=set_context, tenant_id=tenant_id) as r: sid = private_port['port']['fixed_ips'][0]['subnet_id'] private_sub = {'subnet': {'id': sid}} floatingip = None self._add_external_gateway_to_router( r['router']['id'], public_sub['subnet']['network_id']) self._router_interface_action( 'add', r['router']['id'], private_sub['subnet']['id'], None) floatingip = self._make_floatingip( fmt or self.fmt, public_sub['subnet']['network_id'], port_id=private_port['port']['id'], fixed_ip=fixed_ip, tenant_id=tenant_id, set_context=set_context, **kwargs) yield floatingip if floatingip: self._delete('floatingips', floatingip['floatingip']['id']) @contextlib.contextmanager def floatingip_no_assoc_with_public_sub( self, private_sub, fmt=None, set_context=False, public_sub=None, **kwargs): self._set_net_external(public_sub['subnet']['network_id']) with self.router() as r: floatingip = None self._add_external_gateway_to_router( r['router']['id'], public_sub['subnet']['network_id']) self._router_interface_action('add', r['router']['id'], private_sub['subnet']['id'], None) floatingip = self._make_floatingip( fmt or self.fmt, public_sub['subnet']['network_id'], set_context=set_context, **kwargs) yield floatingip, r if floatingip: self._delete('floatingips', floatingip['floatingip']['id']) @contextlib.contextmanager def floatingip_no_assoc(self, private_sub, fmt=None, set_context=False, **kwargs): with self.subnet(cidr='12.0.0.0/24') as public_sub: with self.floatingip_no_assoc_with_public_sub( private_sub, fmt, set_context, public_sub, **kwargs) as (f, r): # Yield only the floating ip object yield f class ExtraAttributesMixinTestCase(testlib_api.SqlTestCase): def setUp(self): super(ExtraAttributesMixinTestCase, self).setUp() self.mixin = l3_attrs_db.ExtraAttributesMixin() directory.add_plugin(plugin_constants.L3, self.mixin) self.ctx = context.get_admin_context() self.router = l3_models.Router() with self.ctx.session.begin(): self.ctx.session.add(self.router) def _get_default_api_values(self): return {k: v.get('transform_from_db', lambda x: x)(v['default']) for k, v in l3_attrs_db.get_attr_info().items()} def test_set_extra_attr_key_bad(self): with testtools.ExpectedException(RuntimeError): self.mixin.set_extra_attr_value(self.ctx, self.router, 'bad', 'value') def test__extend_extra_router_dict_defaults(self): rdict = {} self.mixin._extend_extra_router_dict(rdict, self.router) self.assertEqual(self._get_default_api_values(), rdict) def test_set_attrs_and_extend(self): self.mixin.set_extra_attr_value(self.ctx, self.router, 'ha_vr_id', 99) self.mixin.set_extra_attr_value(self.ctx, self.router, 'availability_zone_hints', ['x', 'y', 'z']) expected = self._get_default_api_values() expected.update({'ha_vr_id': 99, 'availability_zone_hints': ['x', 'y', 'z']}) rdict = {} self.mixin._extend_extra_router_dict(rdict, self.router) self.assertEqual(expected, rdict) self.mixin.set_extra_attr_value(self.ctx, self.router, 'availability_zone_hints', ['z', 'y', 'z']) expected['availability_zone_hints'] = ['z', 'y', 'z'] self.mixin._extend_extra_router_dict(rdict, self.router) self.assertEqual(expected, rdict) class L3NatTestCaseBase(L3NatTestCaseMixin): def test_router_create(self): name = 'router1' tenant_id = _uuid() expected_value = [('name', name), ('tenant_id', tenant_id), ('admin_state_up', True), ('status', 'ACTIVE'), ('external_gateway_info', None)] with self.router(name='router1', admin_state_up=True, tenant_id=tenant_id) as router: for k, v in expected_value: self.assertEqual(router['router'][k], v) def test_router_create_call_extensions(self): self.extension_called = False def _extend_router_dict_test_attr(*args, **kwargs): self.extension_called = True resource_extend.register_funcs( l3_apidef.ROUTERS, [_extend_router_dict_test_attr]) self.assertFalse(self.extension_called) with self.router(): self.assertTrue(self.extension_called) def test_router_create_with_gwinfo(self): with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) data = {'router': {'tenant_id': _uuid()}} data['router']['name'] = 'router1' data['router']['external_gateway_info'] = { 'network_id': s['subnet']['network_id']} router_req = self.new_create_request('routers', data, self.fmt) res = router_req.get_response(self.ext_api) router = self.deserialize(self.fmt, res) self.assertEqual( s['subnet']['network_id'], router['router']['external_gateway_info']['network_id']) def test_router_create_with_gwinfo_ext_ip(self): with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) ext_info = { 'network_id': s['subnet']['network_id'], 'external_fixed_ips': [{'ip_address': '10.0.0.99'}] } res = self._create_router( self.fmt, _uuid(), arg_list=('external_gateway_info',), external_gateway_info=ext_info ) router = self.deserialize(self.fmt, res) self.assertEqual( [{'ip_address': '10.0.0.99', 'subnet_id': s['subnet']['id']}], router['router']['external_gateway_info'][ 'external_fixed_ips']) def test_router_create_with_gwinfo_ext_ip_subnet(self): with self.network() as n: with self.subnet(network=n) as v1,\ self.subnet(network=n, cidr='1.0.0.0/24') as v2,\ self.subnet(network=n, cidr='2.0.0.0/24') as v3: subnets = (v1, v2, v3) self._set_net_external(n['network']['id']) for s in subnets: ext_info = { 'network_id': n['network']['id'], 'external_fixed_ips': [ {'subnet_id': s['subnet']['id']}] } res = self._create_router( self.fmt, _uuid(), arg_list=('external_gateway_info',), external_gateway_info=ext_info ) router = self.deserialize(self.fmt, res) ext_ips = router['router']['external_gateway_info'][ 'external_fixed_ips'] self.assertEqual( [{'subnet_id': s['subnet']['id'], 'ip_address': mock.ANY}], ext_ips) def test_router_create_with_gwinfo_ext_ip_non_admin(self): with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) ext_info = { 'network_id': s['subnet']['network_id'], 'external_fixed_ips': [{'ip_address': '10.0.0.99'}] } res = self._create_router( self.fmt, _uuid(), arg_list=('external_gateway_info',), set_context=True, external_gateway_info=ext_info ) self.assertEqual(exc.HTTPForbidden.code, res.status_int) def test_create_routers_native_quotas(self): tenant_id = _uuid() quota = 1 cfg.CONF.set_override('quota_router', quota, group='QUOTAS') res = self._create_router(self.fmt, tenant_id) self.assertEqual(exc.HTTPCreated.code, res.status_int) res = self._create_router(self.fmt, tenant_id) self.assertEqual(exc.HTTPConflict.code, res.status_int) def test_router_list(self): with self.router() as v1, self.router() as v2, self.router() as v3: routers = (v1, v2, v3) self._test_list_resources('router', routers) def test_router_list_with_parameters(self): with self.router(name='router1') as router1,\ self.router(name='router2') as router2: query_params = 'name=router1' self._test_list_resources('router', [router1], query_params=query_params) query_params = 'name=router2' self._test_list_resources('router', [router2], query_params=query_params) query_params = 'name=router3' self._test_list_resources('router', [], query_params=query_params) def test_router_list_with_sort(self): with self.router(name='router1') as router1,\ self.router(name='router2') as router2,\ self.router(name='router3') as router3: self._test_list_with_sort('router', (router3, router2, router1), [('name', 'desc')]) def test_router_list_with_pagination(self): with self.router(name='router1') as router1,\ self.router(name='router2') as router2,\ self.router(name='router3') as router3: self._test_list_with_pagination('router', (router1, router2, router3), ('name', 'asc'), 2, 2) def test_router_list_with_pagination_reverse(self): with self.router(name='router1') as router1,\ self.router(name='router2') as router2,\ self.router(name='router3') as router3: self._test_list_with_pagination_reverse('router', (router1, router2, router3), ('name', 'asc'), 2, 2) def test_router_update(self): rname1 = "yourrouter" rname2 = "nachorouter" with self.router(name=rname1) as r: body = self._show('routers', r['router']['id']) self.assertEqual(body['router']['name'], rname1) body = self._update('routers', r['router']['id'], {'router': {'name': rname2}}) body = self._show('routers', r['router']['id']) self.assertEqual(body['router']['name'], rname2) def test_router_update_gateway(self): with self.router() as r: with self.subnet() as s1: with self.subnet() as s2: self._set_net_external(s1['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s1['subnet']['network_id']) body = self._show('routers', r['router']['id']) net_id = (body['router'] ['external_gateway_info']['network_id']) self.assertEqual(net_id, s1['subnet']['network_id']) self._set_net_external(s2['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s2['subnet']['network_id']) body = self._show('routers', r['router']['id']) net_id = (body['router'] ['external_gateway_info']['network_id']) self.assertEqual(net_id, s2['subnet']['network_id']) # Validate that we can clear the gateway with # an empty dict, in any other case, we fall back # on None as default value self._remove_external_gateway_from_router( r['router']['id'], s2['subnet']['network_id'], external_gw_info={}) def test_router_update_gateway_with_external_ip_used_by_gw(self): with self.router() as r: with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s['subnet']['network_id'], ext_ips=[{'ip_address': s['subnet']['gateway_ip']}], expected_code=exc.HTTPBadRequest.code) def test_router_update_gateway_with_invalid_external_ip(self): with self.router() as r: with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s['subnet']['network_id'], ext_ips=[{'ip_address': '99.99.99.99'}], expected_code=exc.HTTPBadRequest.code) def test_router_update_gateway_with_invalid_external_subnet(self): with self.subnet() as s1,\ self.subnet(cidr='1.0.0.0/24') as s2,\ self.router() as r: self._set_net_external(s1['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s1['subnet']['network_id'], # this subnet is not on the same network so this should fail ext_ips=[{'subnet_id': s2['subnet']['id']}], expected_code=exc.HTTPBadRequest.code) def test_router_update_gateway_with_different_external_subnet(self): with self.network() as n: with self.subnet(network=n) as s1,\ self.subnet(network=n, cidr='1.0.0.0/24') as s2,\ self.router() as r: self._set_net_external(n['network']['id']) res1 = self._add_external_gateway_to_router( r['router']['id'], n['network']['id'], ext_ips=[{'subnet_id': s1['subnet']['id']}]) res2 = self._add_external_gateway_to_router( r['router']['id'], n['network']['id'], ext_ips=[{'subnet_id': s2['subnet']['id']}]) fip1 = res1['router']['external_gateway_info']['external_fixed_ips'][0] fip2 = res2['router']['external_gateway_info']['external_fixed_ips'][0] self.assertEqual(s1['subnet']['id'], fip1['subnet_id']) self.assertEqual(s2['subnet']['id'], fip2['subnet_id']) self.assertNotEqual(fip1['subnet_id'], fip2['subnet_id']) self.assertNotEqual(fip1['ip_address'], fip2['ip_address']) def test_router_update_gateway_with_existed_floatingip(self): with self.subnet() as subnet: self._set_net_external(subnet['subnet']['network_id']) with self.floatingip_with_assoc() as fip: self._add_external_gateway_to_router( fip['floatingip']['router_id'], subnet['subnet']['network_id'], expected_code=exc.HTTPConflict.code) def test_router_update_gateway_to_empty_with_existed_floatingip(self): with self.floatingip_with_assoc() as fip: self._remove_external_gateway_from_router( fip['floatingip']['router_id'], None, expected_code=exc.HTTPConflict.code) def test_router_update_gateway_add_multiple_prefixes_ipv6(self): with self.network() as n: with self.subnet(network=n) as s1, \ self.subnet(network=n, ip_version=6, cidr='2001:db8::/32') \ as s2, (self.router()) as r: self._set_net_external(n['network']['id']) res1 = self._add_external_gateway_to_router( r['router']['id'], n['network']['id'], ext_ips=[{'subnet_id': s1['subnet']['id']}]) fip1 = (res1['router']['external_gateway_info'] ['external_fixed_ips'][0]) self.assertEqual(s1['subnet']['id'], fip1['subnet_id']) res2 = self._add_external_gateway_to_router( r['router']['id'], n['network']['id'], ext_ips=[{'ip_address': fip1['ip_address'], 'subnet_id': s1['subnet']['id']}, {'subnet_id': s2['subnet']['id']}]) self.assertEqual(fip1, res2['router']['external_gateway_info'] ['external_fixed_ips'][0]) fip2 = (res2['router']['external_gateway_info'] ['external_fixed_ips'][1]) self.assertEqual(s2['subnet']['id'], fip2['subnet_id']) self.assertNotEqual(fip1['subnet_id'], fip2['subnet_id']) self.assertNotEqual(fip1['ip_address'], fip2['ip_address']) def test_router_concurrent_delete_upon_subnet_create(self): with self.network() as n: with self.subnet(network=n) as s1, self.router() as r: self._set_net_external(n['network']['id']) self._add_external_gateway_to_router( r['router']['id'], n['network']['id'], ext_ips=[{'subnet_id': s1['subnet']['id']}]) plugin = directory.get_plugin(plugin_constants.L3) mock.patch.object( plugin, 'update_router', side_effect=l3_exc.RouterNotFound(router_id='1')).start() # ensure the router disappearing doesn't interfere with subnet # creation self._create_subnet(self.fmt, net_id=n['network']['id'], ip_version=6, cidr='2001:db8::/32', expected_res_status=(exc.HTTPCreated.code)) def test_router_update_gateway_upon_subnet_create_ipv6(self): with self.network() as n: with self.subnet(network=n) as s1, self.router() as r: self._set_net_external(n['network']['id']) res1 = self._add_external_gateway_to_router( r['router']['id'], n['network']['id'], ext_ips=[{'subnet_id': s1['subnet']['id']}]) fip1 = (res1['router']['external_gateway_info'] ['external_fixed_ips'][0]) sres = self._create_subnet(self.fmt, net_id=n['network']['id'], ip_version=6, cidr='2001:db8::/32', expected_res_status=( exc.HTTPCreated.code)) s2 = self.deserialize(self.fmt, sres) res2 = self._show('routers', r['router']['id']) self.assertEqual(fip1, res2['router']['external_gateway_info'] ['external_fixed_ips'][0]) fip2 = (res2['router']['external_gateway_info'] ['external_fixed_ips'][1]) self.assertEqual(s2['subnet']['id'], fip2['subnet_id']) self.assertNotEqual(fip1['subnet_id'], fip2['subnet_id']) self.assertNotEqual(fip1['ip_address'], fip2['ip_address']) def test_router_update_gateway_upon_subnet_create_max_ips_ipv6(self): """Create subnet should not cause excess fixed IPs on router gw If a router gateway port has the maximum of one IPv4 and one IPv6 fixed, create subnet should not add any more IP addresses to the port (unless this is the subnet is a SLAAC/DHCPv6-stateless subnet in which case the addresses are added automatically) """ with self.router() as r, self.network() as n: with self.subnet(cidr='10.0.0.0/24', network=n) as s1, ( self.subnet(ip_version=6, cidr='2001:db8::/64', network=n)) as s2: self._set_net_external(n['network']['id']) self._add_external_gateway_to_router( r['router']['id'], n['network']['id'], ext_ips=[{'subnet_id': s1['subnet']['id']}, {'subnet_id': s2['subnet']['id']}], expected_code=exc.HTTPOk.code) res1 = self._show('routers', r['router']['id']) original_fips = (res1['router']['external_gateway_info'] ['external_fixed_ips']) # Add another IPv4 subnet - a fip SHOULD NOT be added # to the external gateway port as it already has a v4 address self._create_subnet(self.fmt, net_id=n['network']['id'], cidr='10.0.1.0/24') res2 = self._show('routers', r['router']['id']) self.assertEqual(original_fips, res2['router']['external_gateway_info'] ['external_fixed_ips']) # Add a SLAAC subnet - a fip from this subnet SHOULD be added # to the external gateway port s3 = self.deserialize(self.fmt, self._create_subnet(self.fmt, net_id=n['network']['id'], ip_version=6, cidr='2001:db8:1::/64', ipv6_ra_mode=lib_constants.IPV6_SLAAC, ipv6_address_mode=lib_constants.IPV6_SLAAC)) res3 = self._show('routers', r['router']['id']) fips = (res3['router']['external_gateway_info'] ['external_fixed_ips']) fip_subnet_ids = [fip['subnet_id'] for fip in fips] self.assertIn(s1['subnet']['id'], fip_subnet_ids) self.assertIn(s2['subnet']['id'], fip_subnet_ids) self.assertIn(s3['subnet']['id'], fip_subnet_ids) self._remove_external_gateway_from_router( r['router']['id'], n['network']['id']) def _test_router_add_interface_subnet(self, router, subnet, msg=None): exp_notifications = ['router.create.start', 'router.create.end', 'network.create.start', 'network.create.end', 'subnet.create.start', 'subnet.create.end', 'router.interface.create', 'router.interface.delete'] body = self._router_interface_action('add', router['router']['id'], subnet['subnet']['id'], None) self.assertIn('port_id', body, msg) # fetch port and confirm device_id r_port_id = body['port_id'] port = self._show('ports', r_port_id) self.assertEqual(port['port']['device_id'], router['router']['id'], msg) self._router_interface_action('remove', router['router']['id'], subnet['subnet']['id'], None) self._show('ports', r_port_id, expected_code=exc.HTTPNotFound.code) self.assertEqual( set(exp_notifications), set(n['event_type'] for n in fake_notifier.NOTIFICATIONS), msg) for n in fake_notifier.NOTIFICATIONS: if n['event_type'].startswith('router.interface.'): payload = n['payload']['router_interface'] self.assertIn('id', payload) self.assertEqual(payload['id'], router['router']['id']) self.assertIn('tenant_id', payload) rtid = router['router']['tenant_id'] # tolerate subnet tenant deliberately set to '' in the # nsx metadata access case self.assertIn(payload['tenant_id'], [rtid, ''], msg) def test_router_add_interface_bad_values(self): with self.router() as r: exp_code = exc.HTTPBadRequest.code self._router_interface_action('add', r['router']['id'], False, None, expected_code=exp_code) self._router_interface_action('add', r['router']['id'], None, False, expected_code=exp_code) def test_router_add_interface_subnet(self): fake_notifier.reset() with self.router() as r: with self.network() as n: with self.subnet(network=n) as s: self._test_router_add_interface_subnet(r, s) def test_router_delete_race_with_interface_add(self): # this test depends on protection from the revision plugin so # we have to initialize it revision_plugin.RevisionPlugin() with self.router() as r, self.subnet() as s: def jam_in_interface(*args, **kwargs): self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None) # unsubscribe now that the evil is done registry.unsubscribe(jam_in_interface, resources.ROUTER, events.PRECOMMIT_DELETE) registry.subscribe(jam_in_interface, resources.ROUTER, events.PRECOMMIT_DELETE) self._delete('routers', r['router']['id'], expected_code=exc.HTTPConflict.code) def test_router_add_interface_ipv6_subnet(self): """Test router-interface-add for valid ipv6 subnets. Verify the valid use-cases of an IPv6 subnet where we are allowed to associate to the Neutron Router are successful. """ slaac = lib_constants.IPV6_SLAAC stateful = lib_constants.DHCPV6_STATEFUL stateless = lib_constants.DHCPV6_STATELESS use_cases = [{'msg': 'IPv6 Subnet Modes (slaac, none)', 'ra_mode': slaac, 'address_mode': None}, {'msg': 'IPv6 Subnet Modes (none, none)', 'ra_mode': None, 'address_mode': None}, {'msg': 'IPv6 Subnet Modes (dhcpv6-stateful, none)', 'ra_mode': stateful, 'address_mode': None}, {'msg': 'IPv6 Subnet Modes (dhcpv6-stateless, none)', 'ra_mode': stateless, 'address_mode': None}, {'msg': 'IPv6 Subnet Modes (slaac, slaac)', 'ra_mode': slaac, 'address_mode': slaac}, {'msg': 'IPv6 Subnet Modes (dhcpv6-stateful,' 'dhcpv6-stateful)', 'ra_mode': stateful, 'address_mode': stateful}, {'msg': 'IPv6 Subnet Modes (dhcpv6-stateless,' 'dhcpv6-stateless)', 'ra_mode': stateless, 'address_mode': stateless}] for uc in use_cases: fake_notifier.reset() with self.router() as r, self.network() as n: with self.subnet(network=n, cidr='fd00::1/64', gateway_ip='fd00::1', ip_version=6, ipv6_ra_mode=uc['ra_mode'], ipv6_address_mode=uc['address_mode']) as s: self._test_router_add_interface_subnet(r, s, uc['msg']) def test_router_add_interface_multiple_ipv4_subnets(self): """Test router-interface-add for multiple ipv4 subnets. Verify that adding multiple ipv4 subnets from the same network to a router places them all on different router interfaces. """ with self.router() as r, self.network() as n: with self.subnet(network=n, cidr='10.0.0.0/24') as s1, ( self.subnet(network=n, cidr='10.0.1.0/24')) as s2: body = self._router_interface_action('add', r['router']['id'], s1['subnet']['id'], None) pid1 = body['port_id'] body = self._router_interface_action('add', r['router']['id'], s2['subnet']['id'], None) pid2 = body['port_id'] self.assertNotEqual(pid1, pid2) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) self._router_interface_action('remove', r['router']['id'], s2['subnet']['id'], None) def test_router_add_interface_multiple_ipv6_subnets_same_net(self): """Test router-interface-add for multiple ipv6 subnets on a network. Verify that adding multiple ipv6 subnets from the same network to a router places them all on the same router interface. """ with self.router() as r, self.network() as n: with (self.subnet(network=n, cidr='fd00::1/64', ip_version=6) ) as s1, self.subnet(network=n, cidr='fd01::1/64', ip_version=6) as s2: body = self._router_interface_action('add', r['router']['id'], s1['subnet']['id'], None) pid1 = body['port_id'] body = self._router_interface_action('add', r['router']['id'], s2['subnet']['id'], None) pid2 = body['port_id'] self.assertEqual(pid1, pid2) port = self._show('ports', pid1) self.assertEqual(2, len(port['port']['fixed_ips'])) port_subnet_ids = [fip['subnet_id'] for fip in port['port']['fixed_ips']] self.assertIn(s1['subnet']['id'], port_subnet_ids) self.assertIn(s2['subnet']['id'], port_subnet_ids) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) self._router_interface_action('remove', r['router']['id'], s2['subnet']['id'], None) def test_router_add_interface_multiple_ipv6_subnets_different_net(self): """Test router-interface-add for ipv6 subnets on different networks. Verify that adding multiple ipv6 subnets from different networks to a router places them on different router interfaces. """ with self.router() as r, self.network() as n1, self.network() as n2: with (self.subnet(network=n1, cidr='fd00::1/64', ip_version=6) ) as s1, self.subnet(network=n2, cidr='fd01::1/64', ip_version=6) as s2: body = self._router_interface_action('add', r['router']['id'], s1['subnet']['id'], None) pid1 = body['port_id'] body = self._router_interface_action('add', r['router']['id'], s2['subnet']['id'], None) pid2 = body['port_id'] self.assertNotEqual(pid1, pid2) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) self._router_interface_action('remove', r['router']['id'], s2['subnet']['id'], None) def test_router_add_iface_ipv6_ext_ra_subnet_returns_400(self): """Test router-interface-add for in-valid ipv6 subnets. Verify that an appropriate error message is displayed when an IPv6 subnet configured to use an external_router for Router Advertisements (i.e., ipv6_ra_mode is None and ipv6_address_mode is not None) is attempted to associate with a Neutron Router. """ use_cases = [{'msg': 'IPv6 Subnet Modes (none, slaac)', 'ra_mode': None, 'address_mode': lib_constants.IPV6_SLAAC}, {'msg': 'IPv6 Subnet Modes (none, dhcpv6-stateful)', 'ra_mode': None, 'address_mode': lib_constants.DHCPV6_STATEFUL}, {'msg': 'IPv6 Subnet Modes (none, dhcpv6-stateless)', 'ra_mode': None, 'address_mode': lib_constants.DHCPV6_STATELESS}] for uc in use_cases: with self.router() as r, self.network() as n: with self.subnet(network=n, cidr='fd00::1/64', gateway_ip='fd00::1', ip_version=6, ipv6_ra_mode=uc['ra_mode'], ipv6_address_mode=uc['address_mode']) as s: exp_code = exc.HTTPBadRequest.code self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None, expected_code=exp_code, msg=uc['msg']) def test_router_add_interface_ipv6_subnet_without_gateway_ip(self): with self.router() as r: with self.subnet(ip_version=6, cidr='fe80::/64', gateway_ip=None) as s: error_code = exc.HTTPBadRequest.code self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None, expected_code=error_code) def test_router_add_interface_subnet_with_bad_tenant_returns_404(self): tenant_id = _uuid() with self.router(tenant_id=tenant_id, set_context=True) as r: with self.network(tenant_id=tenant_id, set_context=True) as n: with self.subnet(network=n, set_context=True) as s: err_code = exc.HTTPNotFound.code self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None, expected_code=err_code, tenant_id='bad_tenant') body = self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None) self.assertIn('port_id', body) self._router_interface_action('remove', r['router']['id'], s['subnet']['id'], None, expected_code=err_code, tenant_id='bad_tenant') def test_router_add_interface_by_subnet_other_tenant_subnet_returns_400( self): router_tenant_id = _uuid() with self.router(tenant_id=router_tenant_id, set_context=True) as r: with self.network(shared=True) as n: with self.subnet(network=n) as s: err_code = exc.HTTPBadRequest.code self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None, expected_code=err_code, tenant_id=router_tenant_id) def _test_router_add_interface_by_port_allocation_pool( self, out_of_pool=False, router_action_as_admin=False, expected_code=exc.HTTPOk.code): router_tenant_id = _uuid() with self.router(tenant_id=router_tenant_id, set_context=True) as r: with self.network(shared=True) as n: with self.subnet(network=n) as s1, ( self.subnet(network=n, cidr='fd00::/64', ip_version=6)) as s2, ( self.subnet(network=n, cidr='fd01::/64', ip_version=6)) as s3: fixed_ips = [{'subnet_id': s1['subnet']['id']}, {'subnet_id': s2['subnet']['id']}, {'subnet_id': s3['subnet']['id']}] if out_of_pool: fixed_ips[1] = {'subnet_id': s2['subnet']['id'], 'ip_address': s2['subnet']['gateway_ip']} with self.port(subnet=s1, fixed_ips=fixed_ips, tenant_id=router_tenant_id) as p: kwargs = {'expected_code': expected_code} if not router_action_as_admin: kwargs['tenant_id'] = router_tenant_id self._router_interface_action( 'add', r['router']['id'], None, p['port']['id'], **kwargs) def test_router_add_interface_by_port_other_tenant_address_in_pool( self): self._test_router_add_interface_by_port_allocation_pool() def test_router_add_interface_by_port_other_tenant_address_out_of_pool( self): self._test_router_add_interface_by_port_allocation_pool( out_of_pool=True, expected_code=exc.HTTPBadRequest.code) def test_router_add_interface_by_port_admin_address_out_of_pool( self): self._test_router_add_interface_by_port_allocation_pool( out_of_pool=True, router_action_as_admin=True) def test_router_add_interface_subnet_with_port_from_other_tenant(self): tenant_id = _uuid() other_tenant_id = _uuid() with self.router(tenant_id=tenant_id) as r,\ self.network(tenant_id=tenant_id) as n1,\ self.network(tenant_id=other_tenant_id) as n2: with self.subnet(network=n1, cidr='10.0.0.0/24') as s1,\ self.subnet(network=n2, cidr='10.1.0.0/24') as s2: body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) self.assertIn('port_id', body) self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None, tenant_id=tenant_id) self.assertIn('port_id', body) def test_router_add_interface_port(self): orig_update_port = self.plugin.update_port with self.router() as r, ( self.port()) as p, ( mock.patch.object(self.plugin, 'update_port')) as update_port: update_port.side_effect = orig_update_port body = self._router_interface_action('add', r['router']['id'], None, p['port']['id']) self.assertIn('port_id', body) self.assertEqual(p['port']['id'], body['port_id']) expected_port_update = { 'device_owner': lib_constants.DEVICE_OWNER_ROUTER_INTF, 'device_id': r['router']['id']} update_port.assert_any_call( mock.ANY, p['port']['id'], {'port': expected_port_update}) # fetch port and confirm device_id body = self._show('ports', p['port']['id']) self.assertEqual(r['router']['id'], body['port']['device_id']) # clean-up self._router_interface_action('remove', r['router']['id'], None, p['port']['id']) def test_router_add_interface_delete_port_after_failure(self): with self.router() as r, self.subnet(enable_dhcp=False) as s: plugin = directory.get_plugin() # inject a failure in the update port that happens at the end # to ensure the port gets deleted with mock.patch.object( plugin, 'update_port', side_effect=n_exc.InvalidInput(error_message='x')): self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None, exc.HTTPBadRequest.code) self.assertFalse(plugin.get_ports(context.get_admin_context())) def test_router_add_interface_dup_port(self): '''This tests that if multiple routers add one port as their interfaces. Only the first router's interface would be added to this port. All the later requests would return exceptions. ''' with self.router() as r1, self.router() as r2, self.network() as n: with self.subnet(network=n) as s: with self.port(subnet=s) as p: self._router_interface_action('add', r1['router']['id'], None, p['port']['id']) # mock out the sequential check plugin = 'neutron.db.l3_db.L3_NAT_dbonly_mixin' check_p = mock.patch(plugin + '._check_router_port', port_id=p['port']['id'], device_id=r2['router']['id'], return_value=p['port']) checkport = check_p.start() # do regular checkport after first skip checkport.side_effect = check_p.stop() self._router_interface_action('add', r2['router']['id'], None, p['port']['id'], exc.HTTPConflict.code) # clean-up self._router_interface_action('remove', r1['router']['id'], None, p['port']['id']) def _assert_body_port_id_and_update_port(self, body, mock_update_port, port_id, device_id): self.assertNotIn('port_id', body) expected_port_update_before_update = { 'device_owner': lib_constants.DEVICE_OWNER_ROUTER_INTF, 'device_id': device_id} expected_port_update_after_fail = { 'device_owner': '', 'device_id': ''} mock_update_port.assert_has_calls( [mock.call( mock.ANY, port_id, {'port': expected_port_update_before_update}), mock.call( mock.ANY, port_id, {'port': expected_port_update_after_fail})], any_order=False) # fetch port and confirm device_id and device_owner body = self._show('ports', port_id) self.assertEqual('', body['port']['device_owner']) self.assertEqual('', body['port']['device_id']) def test_router_add_interface_multiple_ipv4_subnet_port_returns_400(self): """Test adding router port with multiple IPv4 subnets fails. Multiple IPv4 subnets are not allowed on a single router port. Ensure that adding a port with multiple IPv4 subnets to a router fails. """ with self.network() as n, self.router() as r: with self.subnet(network=n, cidr='10.0.0.0/24') as s1, ( self.subnet(network=n, cidr='10.0.1.0/24')) as s2: fixed_ips = [{'subnet_id': s1['subnet']['id']}, {'subnet_id': s2['subnet']['id']}] orig_update_port = self.plugin.update_port with self.port(subnet=s1, fixed_ips=fixed_ips) as p, ( mock.patch.object(self.plugin, 'update_port')) as update_port: update_port.side_effect = orig_update_port exp_code = exc.HTTPBadRequest.code body = self._router_interface_action( 'add', r['router']['id'], None, p['port']['id'], expected_code=exp_code) self._assert_body_port_id_and_update_port( body, update_port, p['port']['id'], r['router']['id']) def test_router_add_interface_ipv6_port_existing_network_returns_400(self): """Ensure unique IPv6 router ports per network id. Adding a router port containing one or more IPv6 subnets with the same network id as an existing router port should fail. This is so there is no ambiguity regarding on which port to add an IPv6 subnet when executing router-interface-add with a subnet and no port. """ with self.network() as n, self.router() as r: with self.subnet(network=n, cidr='fd00::/64', ip_version=6) as s1, ( self.subnet(network=n, cidr='fd01::/64', ip_version=6)) as s2: orig_update_port = self.plugin.update_port with self.port(subnet=s1) as p, ( mock.patch.object(self.plugin, 'update_port')) as update_port: update_port.side_effect = orig_update_port self._router_interface_action('add', r['router']['id'], s2['subnet']['id'], None) exp_code = exc.HTTPBadRequest.code body = self._router_interface_action( 'add', r['router']['id'], None, p['port']['id'], expected_code=exp_code) self._assert_body_port_id_and_update_port( body, update_port, p['port']['id'], r['router']['id']) self._router_interface_action('remove', r['router']['id'], s2['subnet']['id'], None) def test_router_add_interface_multiple_ipv6_subnet_port(self): """A port with multiple IPv6 subnets can be added to a router Create a port with multiple associated IPv6 subnets and attach it to a router. The action should succeed. """ with self.network() as n, self.router() as r: with self.subnet(network=n, cidr='fd00::/64', ip_version=6) as s1, ( self.subnet(network=n, cidr='fd01::/64', ip_version=6)) as s2: fixed_ips = [{'subnet_id': s1['subnet']['id']}, {'subnet_id': s2['subnet']['id']}] with self.port(subnet=s1, fixed_ips=fixed_ips) as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) self._router_interface_action('remove', r['router']['id'], None, p['port']['id']) def test_router_add_interface_empty_port_and_subnet_ids(self): with self.router() as r: self._router_interface_action('add', r['router']['id'], None, None, expected_code=exc. HTTPBadRequest.code) def test_router_add_interface_port_bad_tenant_returns_404(self): tenant_id = _uuid() with self.router(tenant_id=tenant_id, set_context=True) as r: with self.network(tenant_id=tenant_id, set_context=True) as n: with self.subnet(tenant_id=tenant_id, network=n, set_context=True) as s: with self.port(tenant_id=tenant_id, subnet=s, set_context=True) as p: err_code = exc.HTTPNotFound.code self._router_interface_action('add', r['router']['id'], None, p['port']['id'], expected_code=err_code, tenant_id='bad_tenant') self._router_interface_action('add', r['router']['id'], None, p['port']['id'], tenant_id=tenant_id) # clean-up should fail as well self._router_interface_action('remove', r['router']['id'], None, p['port']['id'], expected_code=err_code, tenant_id='bad_tenant') def test_router_add_interface_port_without_ips(self): with self.network() as network, self.router() as r: # Create a router port without ips p = self._make_port(self.fmt, network['network']['id'], device_owner=lib_constants.DEVICE_OWNER_ROUTER_INTF) err_code = exc.HTTPBadRequest.code self._router_interface_action('add', r['router']['id'], None, p['port']['id'], expected_code=err_code) def test_router_add_interface_dup_subnet1_returns_400(self): with self.router() as r: with self.subnet() as s: self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None) self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None, expected_code=exc. HTTPBadRequest.code) def test_router_add_interface_dup_subnet2_returns_400(self): with self.router() as r: with self.subnet() as s1, self.subnet(cidr='1.0.0.0/24') as s2: with self.port(subnet=s1) as p1, self.port(subnet=s2) as p2: orig_update_port = self.plugin.update_port with self.port(subnet=s1) as p3, ( mock.patch.object(self.plugin, 'update_port')) as update_port: update_port.side_effect = orig_update_port for p in [p1, p2]: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) body = self._router_interface_action( 'add', r['router']['id'], None, p3['port']['id'], expected_code=exc.HTTPBadRequest.code) self._assert_body_port_id_and_update_port( body, update_port, p3['port']['id'], r['router']['id']) def test_router_add_interface_overlapped_cidr_returns_400(self): with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s1, self.subnet( cidr='10.0.2.0/24') as s2: self._router_interface_action('add', r['router']['id'], s1['subnet']['id'], None) self._router_interface_action('add', r['router']['id'], s2['subnet']['id'], None) def try_overlapped_cidr(cidr): with self.subnet(cidr=cidr) as s3: self._router_interface_action('add', r['router']['id'], s3['subnet']['id'], None, expected_code=exc. HTTPBadRequest.code) # another subnet with same cidr try_overlapped_cidr('10.0.1.0/24') try_overlapped_cidr('10.0.2.0/24') # another subnet with overlapped cidr including s1 try_overlapped_cidr('10.0.0.0/16') # another subnet with overlapped cidr including s2 try_overlapped_cidr('10.0.2.128/28') def test_router_add_interface_no_data_returns_400(self): with self.router() as r: self._router_interface_action('add', r['router']['id'], None, None, expected_code=exc. HTTPBadRequest.code) def test_router_add_interface_with_both_ids_returns_400(self): with self.router() as r: with self.subnet() as s: with self.port(subnet=s) as p: self._router_interface_action('add', r['router']['id'], s['subnet']['id'], p['port']['id'], expected_code=exc. HTTPBadRequest.code) def test_router_add_interface_cidr_overlapped_with_gateway(self): with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s1, self.subnet( cidr='10.0.0.0/16') as s2: self._set_net_external(s2['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s2['subnet']['network_id']) res = self._router_interface_action('add', r['router']['id'], s1['subnet']['id'], None) self.assertIn('port_id', res) def test_router_add_interface_by_port_cidr_overlapped_with_gateway(self): with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s1, self.subnet( cidr='10.0.0.0/16') as s2: with self.port(subnet=s1) as p: self._set_net_external(s2['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s2['subnet']['network_id']) res = self._router_interface_action('add', r['router']['id'], None, p['port']['id']) self.assertIn('port_id', res) def test_router_add_gateway_dup_subnet1_returns_400(self): with self.router() as r: with self.subnet() as s: self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None) self._set_net_external(s['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s['subnet']['network_id'], expected_code=exc.HTTPBadRequest.code) def test_router_add_gateway_dup_subnet2_returns_400(self): with self.router() as r: with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s['subnet']['network_id']) self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None, expected_code=exc. HTTPBadRequest.code) def test_router_add_gateway_multiple_subnets_ipv6(self): """Ensure external gateway set doesn't add excess IPs on router gw Setting the gateway of a router to an external network with more than one IPv4 and one IPv6 subnet should only add an address from the first IPv4 subnet, an address from the first IPv6-stateful subnet, and an address from each IPv6-stateless (SLAAC and DHCPv6-stateless) subnet """ with self.router() as r, self.network() as n: with self.subnet( cidr='10.0.0.0/24', network=n) as s1, ( self.subnet( cidr='10.0.1.0/24', network=n)) as s2, ( self.subnet( cidr='2001:db8::/64', network=n, ip_version=6, ipv6_ra_mode=lib_constants.IPV6_SLAAC, ipv6_address_mode=lib_constants.IPV6_SLAAC)) as s3, ( self.subnet( cidr='2001:db8:1::/64', network=n, ip_version=6, ipv6_ra_mode=lib_constants.DHCPV6_STATEFUL, ipv6_address_mode=lib_constants.DHCPV6_STATEFUL)) as s4, ( self.subnet( cidr='2001:db8:2::/64', network=n, ip_version=6, ipv6_ra_mode=lib_constants.DHCPV6_STATELESS, ipv6_address_mode=lib_constants.DHCPV6_STATELESS)) as s5: self._set_net_external(n['network']['id']) self._add_external_gateway_to_router( r['router']['id'], n['network']['id']) res = self._show('routers', r['router']['id']) fips = (res['router']['external_gateway_info'] ['external_fixed_ips']) fip_subnet_ids = {fip['subnet_id'] for fip in fips} # one of s1 or s2 should be in the list. if s1['subnet']['id'] in fip_subnet_ids: self.assertEqual({s1['subnet']['id'], s3['subnet']['id'], s4['subnet']['id'], s5['subnet']['id']}, fip_subnet_ids) else: self.assertEqual({s2['subnet']['id'], s3['subnet']['id'], s4['subnet']['id'], s5['subnet']['id']}, fip_subnet_ids) self._remove_external_gateway_from_router( r['router']['id'], n['network']['id']) def test_router_add_and_remove_gateway(self): with self.router() as r: with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s['subnet']['network_id']) body = self._show('routers', r['router']['id']) net_id = body['router']['external_gateway_info']['network_id'] self.assertEqual(net_id, s['subnet']['network_id']) self._remove_external_gateway_from_router( r['router']['id'], s['subnet']['network_id']) body = self._show('routers', r['router']['id']) gw_info = body['router']['external_gateway_info'] self.assertIsNone(gw_info) def test_router_add_and_remove_gateway_tenant_ctx(self): with self.router(tenant_id='noadmin', set_context=True) as r: with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) ctx = context.Context('', 'noadmin') self._add_external_gateway_to_router( r['router']['id'], s['subnet']['network_id'], neutron_context=ctx) body = self._show('routers', r['router']['id']) net_id = body['router']['external_gateway_info']['network_id'] self.assertEqual(net_id, s['subnet']['network_id']) self._remove_external_gateway_from_router( r['router']['id'], s['subnet']['network_id']) body = self._show('routers', r['router']['id']) gw_info = body['router']['external_gateway_info'] self.assertIsNone(gw_info) def test_create_router_port_with_device_id_of_other_tenants_router(self): with self.router() as admin_router: with self.network(tenant_id='tenant_a', set_context=True) as n: with self.subnet(network=n): for device_owner in lib_constants.ROUTER_INTERFACE_OWNERS: self._create_port( self.fmt, n['network']['id'], tenant_id='tenant_a', device_id=admin_router['router']['id'], device_owner=device_owner, set_context=True, expected_res_status=exc.HTTPConflict.code) def test_create_non_router_port_device_id_of_other_tenants_router_update( self): # This tests that HTTPConflict is raised if we create a non-router # port that matches the device_id of another tenants router and then # we change the device_owner to be network:router_interface. with self.router() as admin_router: with self.network(tenant_id='tenant_a', set_context=True) as n: with self.subnet(network=n): for device_owner in lib_constants.ROUTER_INTERFACE_OWNERS: port_res = self._create_port( self.fmt, n['network']['id'], tenant_id='tenant_a', device_id=admin_router['router']['id'], set_context=True) port = self.deserialize(self.fmt, port_res) neutron_context = context.Context('', 'tenant_a') data = {'port': {'device_owner': device_owner}} self._update('ports', port['port']['id'], data, neutron_context=neutron_context, expected_code=exc.HTTPConflict.code) def test_update_port_device_id_to_different_tenants_router(self): with self.router() as admin_router: with self.router(tenant_id='tenant_a', set_context=True) as tenant_router: with self.network(tenant_id='tenant_a', set_context=True) as n: with self.subnet(network=n) as s: port = self._router_interface_action( 'add', tenant_router['router']['id'], s['subnet']['id'], None, tenant_id='tenant_a') neutron_context = context.Context('', 'tenant_a') data = {'port': {'device_id': admin_router['router']['id']}} self._update('ports', port['port_id'], data, neutron_context=neutron_context, expected_code=exc.HTTPConflict.code) def test_router_add_gateway_invalid_network_returns_400(self): with self.router() as r: self._add_external_gateway_to_router( r['router']['id'], "foobar", expected_code=exc.HTTPBadRequest.code) def test_router_add_gateway_non_existent_network_returns_404(self): with self.router() as r: self._add_external_gateway_to_router( r['router']['id'], _uuid(), expected_code=exc.HTTPNotFound.code) def test_router_add_gateway_net_not_external_returns_400(self): with self.router() as r: with self.subnet() as s: # intentionally do not set net as external self._add_external_gateway_to_router( r['router']['id'], s['subnet']['network_id'], expected_code=exc.HTTPBadRequest.code) def test_router_add_gateway_no_subnet(self): with self.router() as r: with self.network() as n: self._set_net_external(n['network']['id']) self._add_external_gateway_to_router( r['router']['id'], n['network']['id']) body = self._show('routers', r['router']['id']) net_id = body['router']['external_gateway_info']['network_id'] self.assertEqual(net_id, n['network']['id']) self._remove_external_gateway_from_router( r['router']['id'], n['network']['id']) body = self._show('routers', r['router']['id']) gw_info = body['router']['external_gateway_info'] self.assertIsNone(gw_info) def test_router_add_gateway_no_subnet_forbidden(self): with self.router() as r: with self.network() as n: self._set_net_external(n['network']['id']) with mock.patch.object(registry, 'notify') as notify: errors = [ exceptions.NotificationError( 'foo_callback_id', n_exc.InvalidInput(error_message='forbidden')), ] notify.side_effect = exceptions.CallbackFailure( errors=errors) self._add_external_gateway_to_router( r['router']['id'], n['network']['id'], expected_code=exc.HTTPBadRequest.code) notify.assert_called_once_with( resources.ROUTER_GATEWAY, events.BEFORE_CREATE, mock.ANY, context=mock.ANY, router_id=r['router']['id'], network_id=n['network']['id'], subnets=[]) def test_router_remove_interface_inuse_returns_409(self): with self.router() as r: with self.subnet() as s: self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None) self._delete('routers', r['router']['id'], expected_code=exc.HTTPConflict.code) def test_router_remove_interface_callback_failure_returns_409(self): with self.router() as r,\ self.subnet() as s,\ mock.patch.object(registry, 'notify') as notify: errors = [ exceptions.NotificationError( 'foo_callback_id', n_exc.InUse()), ] self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None) # we fail the first time, but not the second, when # the clean-up takes place notify.side_effect = [ exceptions.CallbackFailure(errors=errors), None ] self._router_interface_action( 'remove', r['router']['id'], s['subnet']['id'], None, exc.HTTPConflict.code) def test_router_clear_gateway_callback_failure_returns_409(self): with self.router() as r,\ self.subnet() as s,\ mock.patch.object(registry, 'notify') as notify: errors = [ exceptions.NotificationError( 'foo_callback_id', n_exc.InUse()), ] self._set_net_external(s['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s['subnet']['network_id']) notify.side_effect = exceptions.CallbackFailure(errors=errors) self._remove_external_gateway_from_router( r['router']['id'], s['subnet']['network_id'], external_gw_info={}, expected_code=exc.HTTPConflict.code) def test_router_remove_interface_wrong_subnet_returns_400(self): with self.router() as r: with self.subnet() as s: with self.port() as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) self._router_interface_action('remove', r['router']['id'], s['subnet']['id'], p['port']['id'], exc.HTTPBadRequest.code) def test_router_remove_interface_nothing_returns_400(self): with self.router() as r: with self.subnet() as s: with self.port(subnet=s) as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) self._router_interface_action('remove', r['router']['id'], None, None, exc.HTTPBadRequest.code) #remove properly to clean-up self._router_interface_action('remove', r['router']['id'], None, p['port']['id']) def test_router_remove_interface_returns_200(self): with self.router() as r: with self.port() as p: body = self._router_interface_action('add', r['router']['id'], None, p['port']['id']) self._router_interface_action('remove', r['router']['id'], None, p['port']['id'], expected_body=body) def test_router_remove_interface_with_both_ids_returns_200(self): with self.router() as r: with self.subnet() as s: with self.port(subnet=s) as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) self._router_interface_action('remove', r['router']['id'], s['subnet']['id'], p['port']['id']) def test_router_remove_interface_wrong_port_returns_404(self): with self.router() as r: with self.subnet(): with self.port() as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) # create another port for testing failure case res = self._create_port(self.fmt, p['port']['network_id']) p2 = self.deserialize(self.fmt, res) self._router_interface_action('remove', r['router']['id'], None, p2['port']['id'], exc.HTTPNotFound.code) def test_router_remove_ipv6_subnet_from_interface(self): """Delete a subnet from a router interface Verify that deleting a subnet with router-interface-delete removes that subnet when there are multiple subnets on the interface and removes the interface when it is the last subnet on the interface. """ with self.router() as r, self.network() as n: with (self.subnet(network=n, cidr='fd00::1/64', ip_version=6) ) as s1, self.subnet(network=n, cidr='fd01::1/64', ip_version=6) as s2: body = self._router_interface_action('add', r['router']['id'], s1['subnet']['id'], None) self._router_interface_action('add', r['router']['id'], s2['subnet']['id'], None) port = self._show('ports', body['port_id']) self.assertEqual(2, len(port['port']['fixed_ips'])) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) port = self._show('ports', body['port_id']) self.assertEqual(1, len(port['port']['fixed_ips'])) self._router_interface_action('remove', r['router']['id'], s2['subnet']['id'], None) exp_code = exc.HTTPNotFound.code port = self._show('ports', body['port_id'], expected_code=exp_code) def test_router_delete(self): with self.router() as router: router_id = router['router']['id'] req = self.new_show_request('router', router_id) res = req.get_response(self._api_for_resource('router')) self.assertEqual(404, res.status_int) def test_router_delete_with_port_existed_returns_409(self): with self.subnet() as subnet: res = self._create_router(self.fmt, _uuid()) router = self.deserialize(self.fmt, res) self._router_interface_action('add', router['router']['id'], subnet['subnet']['id'], None) self._delete('routers', router['router']['id'], exc.HTTPConflict.code) def test_router_delete_with_floatingip_existed_returns_409(self): with self.port() as p: private_sub = {'subnet': {'id': p['port']['fixed_ips'][0]['subnet_id']}} with self.subnet(cidr='12.0.0.0/24') as public_sub: self._set_net_external(public_sub['subnet']['network_id']) res = self._create_router(self.fmt, _uuid()) r = self.deserialize(self.fmt, res) self._add_external_gateway_to_router( r['router']['id'], public_sub['subnet']['network_id']) self._router_interface_action('add', r['router']['id'], private_sub['subnet']['id'], None) res = self._create_floatingip( self.fmt, public_sub['subnet']['network_id'], port_id=p['port']['id']) self.assertEqual(exc.HTTPCreated.code, res.status_int) self._delete('routers', r['router']['id'], expected_code=exc.HTTPConflict.code) def test_router_show(self): name = 'router1' tenant_id = _uuid() expected_value = [('name', name), ('tenant_id', tenant_id), ('admin_state_up', True), ('status', 'ACTIVE'), ('external_gateway_info', None)] with self.router(name='router1', admin_state_up=True, tenant_id=tenant_id) as router: res = self._show('routers', router['router']['id']) for k, v in expected_value: self.assertEqual(res['router'][k], v) def test_network_update_external_failure(self): with self.router() as r: with self.subnet() as s1: self._set_net_external(s1['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s1['subnet']['network_id']) self._update('networks', s1['subnet']['network_id'], {'network': {extnet_apidef.EXTERNAL: False}}, expected_code=exc.HTTPConflict.code) def test_network_update_external(self): with self.router() as r: with self.network('test_net') as testnet: self._set_net_external(testnet['network']['id']) with self.subnet() as s1: self._set_net_external(s1['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s1['subnet']['network_id']) self._update('networks', testnet['network']['id'], {'network': {extnet_apidef.EXTERNAL: False}}) def test_floatingip_crd_ops(self): with self.floatingip_with_assoc() as fip: self._validate_floating_ip(fip) # post-delete, check that it is really gone body = self._list('floatingips') self.assertEqual(0, len(body['floatingips'])) self._show('floatingips', fip['floatingip']['id'], expected_code=exc.HTTPNotFound.code) def _test_floatingip_with_assoc_fails(self, plugin_method): with self.subnet(cidr='200.0.0.0/24') as public_sub: self._set_net_external(public_sub['subnet']['network_id']) with self.port() as private_port: with self.router() as r: sid = private_port['port']['fixed_ips'][0]['subnet_id'] private_sub = {'subnet': {'id': sid}} self._add_external_gateway_to_router( r['router']['id'], public_sub['subnet']['network_id']) self._router_interface_action('add', r['router']['id'], private_sub['subnet']['id'], None) with mock.patch(plugin_method) as pl: pl.side_effect = n_exc.BadRequest( resource='floatingip', msg='fake_error') res = self._create_floatingip( self.fmt, public_sub['subnet']['network_id'], port_id=private_port['port']['id']) self.assertEqual(400, res.status_int) for p in self._list('ports')['ports']: if (p['device_owner'] == lib_constants.DEVICE_OWNER_FLOATINGIP): self.fail('garbage port is not deleted') def test_floatingip_with_assoc_fails(self): self._test_floatingip_with_assoc_fails( 'neutron.db.l3_db.L3_NAT_dbonly_mixin._check_and_get_fip_assoc') def test_create_floatingip_with_assoc( self, expected_status=lib_constants.FLOATINGIP_STATUS_ACTIVE): with self.floatingip_with_assoc() as fip: body = self._show('floatingips', fip['floatingip']['id']) self.assertEqual(body['floatingip']['id'], fip['floatingip']['id']) self.assertEqual(body['floatingip']['port_id'], fip['floatingip']['port_id']) self.assertEqual(expected_status, body['floatingip']['status']) self.assertIsNotNone(body['floatingip']['fixed_ip_address']) self.assertIsNotNone(body['floatingip']['router_id']) def test_create_floatingip_non_admin_context_agent_notification(self): plugin = directory.get_plugin(plugin_constants.L3) if not hasattr(plugin, 'l3_rpc_notifier'): self.skipTest("Plugin does not support l3_rpc_notifier") with self.subnet(cidr='11.0.0.0/24') as public_sub,\ self.port() as private_port,\ self.router() as r: self._set_net_external(public_sub['subnet']['network_id']) subnet_id = private_port['port']['fixed_ips'][0]['subnet_id'] private_sub = {'subnet': {'id': subnet_id}} self._add_external_gateway_to_router( r['router']['id'], public_sub['subnet']['network_id']) self._router_interface_action( 'add', r['router']['id'], private_sub['subnet']['id'], None) with mock.patch.object(plugin.l3_rpc_notifier, 'routers_updated') as agent_notification: self._make_floatingip( self.fmt, public_sub['subnet']['network_id'], port_id=private_port['port']['id'], set_context=False) self.assertTrue(agent_notification.called) def test_floating_port_status_not_applicable(self): with self.floatingip_with_assoc(): port_body = self._list('ports', query_params='device_owner=network:floatingip')['ports'][0] self.assertEqual(lib_constants.PORT_STATUS_NOTAPPLICABLE, port_body['status']) def test_floatingip_update( self, expected_status=lib_constants.FLOATINGIP_STATUS_ACTIVE): with self.port() as p: private_sub = {'subnet': {'id': p['port']['fixed_ips'][0]['subnet_id']}} with self.floatingip_no_assoc(private_sub) as fip: body = self._show('floatingips', fip['floatingip']['id']) self.assertIsNone(body['floatingip']['port_id']) self.assertIsNone(body['floatingip']['fixed_ip_address']) self.assertEqual(expected_status, body['floatingip']['status']) port_id = p['port']['id'] ip_address = p['port']['fixed_ips'][0]['ip_address'] body = self._update('floatingips', fip['floatingip']['id'], {'floatingip': {'port_id': port_id}}) self.assertEqual(port_id, body['floatingip']['port_id']) self.assertEqual(ip_address, body['floatingip']['fixed_ip_address']) def test_floatingip_update_subnet_gateway_disabled( self, expected_status=lib_constants.FLOATINGIP_STATUS_ACTIVE): """Attach a floating IP to an instance Verify that the floating IP can be associated to a port whose subnet's gateway ip is not connected to the external router, but the router has an ip in that subnet. """ with self.subnet(cidr='30.0.0.0/24', gateway_ip=None) as private_sub: with self.port(private_sub) as p: subnet_id = p['port']['fixed_ips'][0]['subnet_id'] private_sub = {'subnet': {'id': subnet_id}} port_id = p['port']['id'] with self.router() as r: self._router_interface_action('add', r['router']['id'], None, port_id) with self.subnet(cidr='12.0.0.0/24') as public_sub: self._set_net_external(public_sub['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], public_sub['subnet']['network_id']) fip = self._make_floatingip(self.fmt, public_sub['subnet']['network_id']) body = self._show('floatingips', fip['floatingip']['id']) self.assertEqual(expected_status, body['floatingip']['status']) body = self._update('floatingips', fip['floatingip']['id'], {'floatingip': {'port_id': port_id}}) self.assertEqual(port_id, body['floatingip']['port_id']) self.assertEqual(p['port']['fixed_ips'][0]['ip_address'], body['floatingip']['fixed_ip_address']) self.assertEqual(r['router']['id'], body['floatingip']['router_id']) def test_floatingip_create_different_fixed_ip_same_port(self): '''This tests that it is possible to delete a port that has multiple floating ip addresses associated with it (each floating address associated with a unique fixed address). ''' with self.router() as r: with self.subnet(cidr='11.0.0.0/24') as public_sub: self._set_net_external(public_sub['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], public_sub['subnet']['network_id']) with self.subnet() as private_sub: ip_range = list(netaddr.IPNetwork( private_sub['subnet']['cidr'])) fixed_ips = [{'ip_address': str(ip_range[-3])}, {'ip_address': str(ip_range[-2])}] self._router_interface_action( 'add', r['router']['id'], private_sub['subnet']['id'], None) with self.port(subnet=private_sub, fixed_ips=fixed_ips) as p: fip1 = self._make_floatingip( self.fmt, public_sub['subnet']['network_id'], p['port']['id'], fixed_ip=str(ip_range[-2])) fip2 = self._make_floatingip( self.fmt, public_sub['subnet']['network_id'], p['port']['id'], fixed_ip=str(ip_range[-3])) # Test that floating ips are assigned successfully. body = self._show('floatingips', fip1['floatingip']['id']) self.assertEqual( body['floatingip']['port_id'], fip1['floatingip']['port_id']) body = self._show('floatingips', fip2['floatingip']['id']) self.assertEqual( body['floatingip']['port_id'], fip2['floatingip']['port_id']) self._delete('ports', p['port']['id']) # Test that port has been successfully deleted. body = self._show('ports', p['port']['id'], expected_code=exc.HTTPNotFound.code) def test_floatingip_update_different_fixed_ip_same_port(self): with self.subnet() as s: ip_range = list(netaddr.IPNetwork(s['subnet']['cidr'])) fixed_ips = [{'ip_address': str(ip_range[-3])}, {'ip_address': str(ip_range[-2])}] with self.port(subnet=s, fixed_ips=fixed_ips) as p: with self.floatingip_with_assoc( port_id=p['port']['id'], fixed_ip=str(ip_range[-3])) as fip: body = self._show('floatingips', fip['floatingip']['id']) self.assertEqual(fip['floatingip']['id'], body['floatingip']['id']) self.assertEqual(fip['floatingip']['port_id'], body['floatingip']['port_id']) self.assertEqual(str(ip_range[-3]), body['floatingip']['fixed_ip_address']) self.assertIsNotNone(body['floatingip']['router_id']) body_2 = self._update( 'floatingips', fip['floatingip']['id'], {'floatingip': {'port_id': p['port']['id'], 'fixed_ip_address': str(ip_range[-2])} }) self.assertEqual(fip['floatingip']['port_id'], body_2['floatingip']['port_id']) self.assertEqual(str(ip_range[-2]), body_2['floatingip']['fixed_ip_address']) def test_floatingip_update_invalid_fixed_ip(self): with self.subnet() as s: with self.port(subnet=s) as p: with self.floatingip_with_assoc( port_id=p['port']['id']) as fip: self._update( 'floatingips', fip['floatingip']['id'], {'floatingip': {'port_id': p['port']['id'], 'fixed_ip_address': '2001:db8::a'}}, expected_code=exc.HTTPBadRequest.code) def test_floatingip_update_to_same_port_id_twice( self, expected_status=lib_constants.FLOATINGIP_STATUS_ACTIVE): with self.port() as p: private_sub = {'subnet': {'id': p['port']['fixed_ips'][0]['subnet_id']}} with self.floatingip_no_assoc(private_sub) as fip: body = self._show('floatingips', fip['floatingip']['id']) self.assertIsNone(body['floatingip']['port_id']) self.assertIsNone(body['floatingip']['fixed_ip_address']) self.assertEqual(expected_status, body['floatingip']['status']) port_id = p['port']['id'] ip_address = p['port']['fixed_ips'][0]['ip_address'] # 1. Update floating IP with port_id (associate) body = self._update('floatingips', fip['floatingip']['id'], {'floatingip': {'port_id': port_id}}) self.assertEqual(port_id, body['floatingip']['port_id']) self.assertEqual(ip_address, body['floatingip']['fixed_ip_address']) # 2. Update floating IP with same port again body = self._update('floatingips', fip['floatingip']['id'], {'floatingip': {'port_id': port_id}}) # No errors, and nothing changed self.assertEqual(port_id, body['floatingip']['port_id']) self.assertEqual(ip_address, body['floatingip']['fixed_ip_address']) def test_floatingip_update_same_fixed_ip_same_port(self): with self.subnet() as private_sub: ip_range = list(netaddr.IPNetwork(private_sub['subnet']['cidr'])) fixed_ip = [{'ip_address': str(ip_range[-3])}] with self.port(subnet=private_sub, fixed_ips=fixed_ip) as p: with self.router() as r: with self.subnet(cidr='11.0.0.0/24') as public_sub: self._set_net_external( public_sub['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], public_sub['subnet']['network_id']) self._router_interface_action( 'add', r['router']['id'], private_sub['subnet']['id'], None) fip1 = self._make_floatingip( self.fmt, public_sub['subnet']['network_id']) fip2 = self._make_floatingip( self.fmt, public_sub['subnet']['network_id']) # 1. Update floating IP 1 with port_id and fixed_ip body_1 = self._update( 'floatingips', fip1['floatingip']['id'], {'floatingip': {'port_id': p['port']['id'], 'fixed_ip_address': str(ip_range[-3])} }) self.assertEqual(str(ip_range[-3]), body_1['floatingip']['fixed_ip_address']) self.assertEqual(p['port']['id'], body_1['floatingip']['port_id']) # 2. Update floating IP 2 with port_id and fixed_ip # mock out the sequential check plugin = 'neutron.db.l3_db.L3_NAT_dbonly_mixin' check_get = mock.patch( plugin + '._check_and_get_fip_assoc', fip=fip2, floating_db=mock.ANY, return_value=(p['port']['id'], str(ip_range[-3]), r['router']['id'])) check_and_get = check_get.start() # do regular _check_and_get_fip_assoc() after skip check_and_get.side_effect = check_get.stop() self._update( 'floatingips', fip2['floatingip']['id'], {'floatingip': {'port_id': p['port']['id'], 'fixed_ip_address': str(ip_range[-3]) }}, exc.HTTPConflict.code) body = self._show('floatingips', fip2['floatingip']['id']) self.assertIsNone( body['floatingip']['fixed_ip_address']) self.assertIsNone( body['floatingip']['port_id']) def test_create_multiple_floatingips_same_fixed_ip_same_port(self): '''This tests that if multiple API requests arrive to create floating IPs on same external network to same port with one fixed ip, the latter API requests would be blocked at database side. ''' with self.router() as r: with self.subnet(cidr='11.0.0.0/24') as public_sub: self._set_net_external(public_sub['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], public_sub['subnet']['network_id']) with self.subnet() as private_sub: ip_range = list(netaddr.IPNetwork( private_sub['subnet']['cidr'])) fixed_ips = [{'ip_address': str(ip_range[-3])}, {'ip_address': str(ip_range[-2])}] self._router_interface_action( 'add', r['router']['id'], private_sub['subnet']['id'], None) with self.port(subnet=private_sub, fixed_ips=fixed_ips) as p: # 1. Create floating IP 1 fip1 = self._make_floatingip( self.fmt, public_sub['subnet']['network_id'], p['port']['id'], fixed_ip=str(ip_range[-3])) # 2. Create floating IP 2 # mock out the sequential check plugin = 'neutron.db.l3_db.L3_NAT_dbonly_mixin' check_get = mock.patch( plugin + '._check_and_get_fip_assoc', fip=mock.ANY, floating_db=mock.ANY, return_value=(p['port']['id'], str(ip_range[-3]), r['router']['id'])) check_and_get = check_get.start() # do regular _check_and_get_fip_assoc() after skip check_and_get.side_effect = check_get.stop() self._make_floatingip( self.fmt, public_sub['subnet']['network_id'], p['port']['id'], fixed_ip=str(ip_range[-3]), http_status=exc.HTTPConflict.code) # Test that floating IP 1 is successfully created body = self._show('floatingips', fip1['floatingip']['id']) self.assertEqual( body['floatingip']['port_id'], fip1['floatingip']['port_id']) self._delete('ports', p['port']['id']) # Test that port has been successfully deleted. body = self._show('ports', p['port']['id'], expected_code=exc.HTTPNotFound.code) def test_first_floatingip_associate_notification(self): with self.port() as p: private_sub = {'subnet': {'id': p['port']['fixed_ips'][0]['subnet_id']}} with self.floatingip_no_assoc(private_sub) as fip: port_id = p['port']['id'] ip_address = p['port']['fixed_ips'][0]['ip_address'] with mock.patch.object(registry, 'notify') as notify: body = self._update('floatingips', fip['floatingip']['id'], {'floatingip': {'port_id': port_id}}) fip_addr = fip['floatingip']['floating_ip_address'] fip_network_id = fip['floatingip']['floating_network_id'] fip_id = fip['floatingip']['id'] router_id = body['floatingip']['router_id'] body = self._show('routers', router_id) notify.assert_any_call(resources.FLOATING_IP, events.AFTER_UPDATE, mock.ANY, context=mock.ANY, fixed_ip_address=ip_address, fixed_port_id=port_id, floating_ip_address=fip_addr, floating_network_id=fip_network_id, last_known_router_id=None, floating_ip_id=fip_id, router_id=router_id) def test_floatingip_disassociate_notification(self): with self.port() as p: private_sub = {'subnet': {'id': p['port']['fixed_ips'][0]['subnet_id']}} with self.floatingip_no_assoc(private_sub) as fip: port_id = p['port']['id'] body = self._update('floatingips', fip['floatingip']['id'], {'floatingip': {'port_id': port_id}}) with mock.patch.object(registry, 'notify') as notify: fip_addr = fip['floatingip']['floating_ip_address'] fip_network_id = fip['floatingip']['floating_network_id'] fip_id = fip['floatingip']['id'] router_id = body['floatingip']['router_id'] self._update('floatingips', fip['floatingip']['id'], {'floatingip': {'port_id': None}}) notify.assert_any_call(resources.FLOATING_IP, events.AFTER_UPDATE, mock.ANY, context=mock.ANY, fixed_ip_address=None, fixed_port_id=None, floating_ip_address=fip_addr, floating_network_id=fip_network_id, last_known_router_id=router_id, floating_ip_id=fip_id, router_id=None) def test_floatingip_association_on_unowned_router(self): # create a router owned by one tenant and associate the FIP with a # different tenant, assert that the FIP association succeeds with self.subnet(cidr='11.0.0.0/24') as public_sub: self._set_net_external(public_sub['subnet']['network_id']) with self.port() as private_port: with self.router(tenant_id='router-owner', set_context=True) as r: sid = private_port['port']['fixed_ips'][0]['subnet_id'] private_sub = {'subnet': {'id': sid}} self._add_external_gateway_to_router( r['router']['id'], public_sub['subnet']['network_id']) self._router_interface_action( 'add', r['router']['id'], private_sub['subnet']['id'], None) self._make_floatingip(self.fmt, public_sub['subnet']['network_id'], port_id=private_port['port']['id'], fixed_ip=None, set_context=True) def test_floatingip_update_different_router(self): # Create subnet with different CIDRs to account for plugins which # do not support overlapping IPs with self.subnet(cidr='10.0.0.0/24') as s1,\ self.subnet(cidr='10.0.1.0/24') as s2: with self.port(subnet=s1) as p1, self.port(subnet=s2) as p2: private_sub1 = {'subnet': {'id': p1['port']['fixed_ips'][0]['subnet_id']}} private_sub2 = {'subnet': {'id': p2['port']['fixed_ips'][0]['subnet_id']}} with self.subnet(cidr='12.0.0.0/24') as public_sub: with self.floatingip_no_assoc_with_public_sub( private_sub1, public_sub=public_sub) as (fip1, r1),\ self.floatingip_no_assoc_with_public_sub( private_sub2, public_sub=public_sub) as (fip2, r2): def assert_no_assoc(fip): body = self._show('floatingips', fip['floatingip']['id']) self.assertIsNone(body['floatingip']['port_id']) self.assertIsNone( body['floatingip']['fixed_ip_address']) assert_no_assoc(fip1) assert_no_assoc(fip2) def associate_and_assert(fip, port): port_id = port['port']['id'] ip_address = (port['port']['fixed_ips'] [0]['ip_address']) body = self._update( 'floatingips', fip['floatingip']['id'], {'floatingip': {'port_id': port_id}}) self.assertEqual(port_id, body['floatingip']['port_id']) self.assertEqual( ip_address, body['floatingip']['fixed_ip_address']) return body['floatingip']['router_id'] fip1_r1_res = associate_and_assert(fip1, p1) self.assertEqual(fip1_r1_res, r1['router']['id']) # The following operation will associate the floating # ip to a different router fip1_r2_res = associate_and_assert(fip1, p2) self.assertEqual(fip1_r2_res, r2['router']['id']) fip2_r1_res = associate_and_assert(fip2, p1) self.assertEqual(fip2_r1_res, r1['router']['id']) # disassociate fip1 self._update( 'floatingips', fip1['floatingip']['id'], {'floatingip': {'port_id': None}}) fip2_r2_res = associate_and_assert(fip2, p2) self.assertEqual(fip2_r2_res, r2['router']['id']) def test_floatingip_update_different_port_owner_as_admin(self): with self.subnet() as private_sub: with self.floatingip_no_assoc(private_sub) as fip: with self.port(subnet=private_sub, tenant_id='other') as p: body = self._update('floatingips', fip['floatingip']['id'], {'floatingip': {'port_id': p['port']['id']}}) self.assertEqual(p['port']['id'], body['floatingip']['port_id']) def test_floatingip_port_delete(self): with self.subnet() as private_sub: with self.floatingip_no_assoc(private_sub) as fip: with self.port(subnet=private_sub) as p: body = self._update('floatingips', fip['floatingip']['id'], {'floatingip': {'port_id': p['port']['id']}}) # note: once this port goes out of scope, the port will be # deleted, which is what we want to test. We want to confirm # that the fields are set back to None self._delete('ports', p['port']['id']) body = self._show('floatingips', fip['floatingip']['id']) self.assertEqual(body['floatingip']['id'], fip['floatingip']['id']) self.assertIsNone(body['floatingip']['port_id']) self.assertIsNone(body['floatingip']['fixed_ip_address']) self.assertIsNone(body['floatingip']['router_id']) def test_two_fips_one_port_invalid_return_409(self): with self.floatingip_with_assoc() as fip1: res = self._create_floatingip( self.fmt, fip1['floatingip']['floating_network_id'], fip1['floatingip']['port_id']) self.assertEqual(exc.HTTPConflict.code, res.status_int) def test_floating_ip_direct_port_delete_returns_409(self): found = False with self.floatingip_with_assoc(): for p in self._list('ports')['ports']: if p['device_owner'] == lib_constants.DEVICE_OWNER_FLOATINGIP: self._delete('ports', p['id'], expected_code=exc.HTTPConflict.code) found = True self.assertTrue(found) def _test_floatingip_with_invalid_create_port(self, plugin_class): with self.port() as p: private_sub = {'subnet': {'id': p['port']['fixed_ips'][0]['subnet_id']}} with self.subnet(cidr='12.0.0.0/24') as public_sub: self._set_net_external(public_sub['subnet']['network_id']) res = self._create_router(self.fmt, _uuid()) r = self.deserialize(self.fmt, res) self._add_external_gateway_to_router( r['router']['id'], public_sub['subnet']['network_id']) self._router_interface_action( 'add', r['router']['id'], private_sub['subnet']['id'], None) with mock.patch(plugin_class + '.create_port') as createport: createport.return_value = {'fixed_ips': [], 'id': '44'} res = self._create_floatingip( self.fmt, public_sub['subnet']['network_id'], port_id=p['port']['id']) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_floatingip_with_invalid_create_port(self): self._test_floatingip_with_invalid_create_port( 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2') def test_create_floatingip_with_subnet_id_non_admin(self): with self.subnet() as public_sub: self._set_net_external(public_sub['subnet']['network_id']) with self.router(): res = self._create_floatingip( self.fmt, public_sub['subnet']['network_id'], subnet_id=public_sub['subnet']['id'], set_context=True) self.assertEqual(exc.HTTPCreated.code, res.status_int) def test_create_floatingip_with_subnet_id_and_fip_address(self): with self.network() as ext_net: self._set_net_external(ext_net['network']['id']) with self.subnet(ext_net, cidr='10.10.10.0/24') as ext_subnet: with self.router(): res = self._create_floatingip( self.fmt, ext_net['network']['id'], subnet_id=ext_subnet['subnet']['id'], floating_ip='10.10.10.100') fip = self.deserialize(self.fmt, res) self.assertEqual(exc.HTTPCreated.code, res.status_int) self.assertEqual('10.10.10.100', fip['floatingip']['floating_ip_address']) def test_create_floatingip_with_subnet_and_invalid_fip_address(self): with self.network() as ext_net: self._set_net_external(ext_net['network']['id']) with self.subnet(ext_net, cidr='10.10.10.0/24') as ext_subnet: with self.router(): res = self._create_floatingip( self.fmt, ext_net['network']['id'], subnet_id=ext_subnet['subnet']['id'], floating_ip='20.20.20.200') data = self.deserialize(self.fmt, res) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) msg = str(n_exc.InvalidIpForSubnet(ip_address='20.20.20.200')) self.assertEqual('InvalidIpForSubnet', data['NeutronError']['type']) self.assertEqual(msg, data['NeutronError']['message']) def test_create_floatingip_with_multisubnet_id(self): with self.network() as network: self._set_net_external(network['network']['id']) with self.subnet(network, cidr='10.0.12.0/24') as subnet1: with self.subnet(network, cidr='10.0.13.0/24') as subnet2: with self.router(): res = self._create_floatingip( self.fmt, subnet1['subnet']['network_id'], subnet_id=subnet1['subnet']['id']) fip1 = self.deserialize(self.fmt, res) res = self._create_floatingip( self.fmt, subnet1['subnet']['network_id'], subnet_id=subnet2['subnet']['id']) fip2 = self.deserialize(self.fmt, res) self.assertTrue( fip1['floatingip']['floating_ip_address'].startswith('10.0.12')) self.assertTrue( fip2['floatingip']['floating_ip_address'].startswith('10.0.13')) def test_create_floatingip_with_wrong_subnet_id(self): with self.network() as network1: self._set_net_external(network1['network']['id']) with self.subnet(network1, cidr='10.0.12.0/24') as subnet1: with self.network() as network2: self._set_net_external(network2['network']['id']) with self.subnet(network2, cidr='10.0.13.0/24') as subnet2: with self.router(): res = self._create_floatingip( self.fmt, subnet1['subnet']['network_id'], subnet_id=subnet2['subnet']['id']) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_create_floatingip_no_ext_gateway_return_404(self): with self.subnet() as public_sub: self._set_net_external(public_sub['subnet']['network_id']) with self.port() as private_port: with self.router(): res = self._create_floatingip( self.fmt, public_sub['subnet']['network_id'], port_id=private_port['port']['id']) # this should be some kind of error self.assertEqual(exc.HTTPNotFound.code, res.status_int) def test_create_floating_non_ext_network_returns_400(self): with self.subnet() as public_sub: # normally we would set the network of public_sub to be # external, but the point of this test is to handle when # that is not the case with self.router(): res = self._create_floatingip( self.fmt, public_sub['subnet']['network_id']) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_create_floatingip_no_public_subnet_returns_400(self): with self.network() as public_network: with self.port() as private_port: with self.router() as r: sid = private_port['port']['fixed_ips'][0]['subnet_id'] private_sub = {'subnet': {'id': sid}} self._router_interface_action('add', r['router']['id'], private_sub['subnet']['id'], None) res = self._create_floatingip( self.fmt, public_network['network']['id'], port_id=private_port['port']['id']) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_create_floatingip_invalid_floating_network_id_returns_400(self): # API-level test - no need to create all objects for l3 plugin res = self._create_floatingip(self.fmt, 'iamnotanuuid', uuidutils.generate_uuid(), '192.168.0.1') self.assertEqual(400, res.status_int) def test_create_floatingip_invalid_floating_port_id_returns_400(self): # API-level test - no need to create all objects for l3 plugin res = self._create_floatingip(self.fmt, uuidutils.generate_uuid(), 'iamnotanuuid', '192.168.0.1') self.assertEqual(400, res.status_int) def test_create_floatingip_invalid_fixed_ip_address_returns_400(self): # API-level test - no need to create all objects for l3 plugin res = self._create_floatingip(self.fmt, uuidutils.generate_uuid(), uuidutils.generate_uuid(), 'iamnotnanip') self.assertEqual(400, res.status_int) def test_create_floatingip_invalid_fixed_ipv6_address_returns_400(self): # API-level test - no need to create all objects for l3 plugin res = self._create_floatingip(self.fmt, uuidutils.generate_uuid(), uuidutils.generate_uuid(), '2001:db8::a') self.assertEqual(400, res.status_int) def test_floatingip_list_with_sort(self): with self.subnet(cidr="10.0.0.0/24") as s1,\ self.subnet(cidr="11.0.0.0/24") as s2,\ self.subnet(cidr="12.0.0.0/24") as s3: network_id1 = s1['subnet']['network_id'] network_id2 = s2['subnet']['network_id'] network_id3 = s3['subnet']['network_id'] self._set_net_external(network_id1) self._set_net_external(network_id2) self._set_net_external(network_id3) fp1 = self._make_floatingip(self.fmt, network_id1) fp2 = self._make_floatingip(self.fmt, network_id2) fp3 = self._make_floatingip(self.fmt, network_id3) self._test_list_with_sort('floatingip', (fp3, fp2, fp1), [('floating_ip_address', 'desc')]) def test_floatingip_list_with_port_id(self): with self.floatingip_with_assoc() as fip: port_id = fip['floatingip']['port_id'] res = self._list('floatingips', query_params="port_id=%s" % port_id) self.assertEqual(1, len(res['floatingips'])) res = self._list('floatingips', query_params="port_id=aaa") self.assertEqual(0, len(res['floatingips'])) def test_floatingip_list_with_pagination(self): with self.subnet(cidr="10.0.0.0/24") as s1,\ self.subnet(cidr="11.0.0.0/24") as s2,\ self.subnet(cidr="12.0.0.0/24") as s3: network_id1 = s1['subnet']['network_id'] network_id2 = s2['subnet']['network_id'] network_id3 = s3['subnet']['network_id'] self._set_net_external(network_id1) self._set_net_external(network_id2) self._set_net_external(network_id3) fp1 = self._make_floatingip(self.fmt, network_id1) fp2 = self._make_floatingip(self.fmt, network_id2) fp3 = self._make_floatingip(self.fmt, network_id3) self._test_list_with_pagination( 'floatingip', (fp1, fp2, fp3), ('floating_ip_address', 'asc'), 2, 2) def test_floatingip_list_with_pagination_reverse(self): with self.subnet(cidr="10.0.0.0/24") as s1,\ self.subnet(cidr="11.0.0.0/24") as s2,\ self.subnet(cidr="12.0.0.0/24") as s3: network_id1 = s1['subnet']['network_id'] network_id2 = s2['subnet']['network_id'] network_id3 = s3['subnet']['network_id'] self._set_net_external(network_id1) self._set_net_external(network_id2) self._set_net_external(network_id3) fp1 = self._make_floatingip(self.fmt, network_id1) fp2 = self._make_floatingip(self.fmt, network_id2) fp3 = self._make_floatingip(self.fmt, network_id3) self._test_list_with_pagination_reverse( 'floatingip', (fp1, fp2, fp3), ('floating_ip_address', 'asc'), 2, 2) def test_floatingip_multi_external_one_internal(self): with self.subnet(cidr="10.0.0.0/24") as exs1,\ self.subnet(cidr="11.0.0.0/24") as exs2,\ self.subnet(cidr="12.0.0.0/24") as ins1: network_ex_id1 = exs1['subnet']['network_id'] network_ex_id2 = exs2['subnet']['network_id'] self._set_net_external(network_ex_id1) self._set_net_external(network_ex_id2) r2i_fixed_ips = [{'ip_address': '12.0.0.2'}] with self.router() as r1,\ self.router() as r2,\ self.port(subnet=ins1, fixed_ips=r2i_fixed_ips) as r2i_port: self._add_external_gateway_to_router( r1['router']['id'], network_ex_id1) self._router_interface_action('add', r1['router']['id'], ins1['subnet']['id'], None) self._add_external_gateway_to_router( r2['router']['id'], network_ex_id2) self._router_interface_action('add', r2['router']['id'], None, r2i_port['port']['id']) with self.port(subnet=ins1, fixed_ips=[{'ip_address': '12.0.0.3'}] ) as private_port: fp1 = self._make_floatingip(self.fmt, network_ex_id1, private_port['port']['id']) fp2 = self._make_floatingip(self.fmt, network_ex_id2, private_port['port']['id']) self.assertEqual(fp1['floatingip']['router_id'], r1['router']['id']) self.assertEqual(fp2['floatingip']['router_id'], r2['router']['id']) def test_floatingip_same_external_and_internal(self): # Select router with subnet's gateway_ip for floatingip when # routers connected to same subnet and external network. with self.subnet(cidr="10.0.0.0/24") as exs,\ self.subnet(cidr="12.0.0.0/24", gateway_ip="12.0.0.50") as ins: network_ex_id = exs['subnet']['network_id'] self._set_net_external(network_ex_id) r2i_fixed_ips = [{'ip_address': '12.0.0.2'}] with self.router() as r1,\ self.router() as r2,\ self.port(subnet=ins, fixed_ips=r2i_fixed_ips) as r2i_port: self._add_external_gateway_to_router( r1['router']['id'], network_ex_id) self._router_interface_action('add', r2['router']['id'], None, r2i_port['port']['id']) self._router_interface_action('add', r1['router']['id'], ins['subnet']['id'], None) self._add_external_gateway_to_router( r2['router']['id'], network_ex_id) with self.port(subnet=ins, fixed_ips=[{'ip_address': '12.0.0.8'}] ) as private_port: fp = self._make_floatingip(self.fmt, network_ex_id, private_port['port']['id']) self.assertEqual(r1['router']['id'], fp['floatingip']['router_id']) def _test_floatingip_via_router_interface(self, http_status): # NOTE(yamamoto): "exs" subnet is just to provide a gateway port # for the router. Otherwise the test would fail earlier without # reaching the code we want to test. (bug 1556884) with self.subnet(cidr="10.0.0.0/24") as exs, \ self.subnet(cidr="10.0.1.0/24") as ins1, \ self.subnet(cidr="10.0.2.0/24") as ins2: network_ex_id = exs['subnet']['network_id'] self._set_net_external(network_ex_id) network_in2_id = ins2['subnet']['network_id'] self._set_net_external(network_in2_id) with self.router() as r1, self.port(subnet=ins1) as private_port: self._add_external_gateway_to_router(r1['router']['id'], network_ex_id) self._router_interface_action('add', r1['router']['id'], ins1['subnet']['id'], None) self._router_interface_action('add', r1['router']['id'], ins2['subnet']['id'], None) self._make_floatingip(self.fmt, network_id=network_in2_id, port_id=private_port['port']['id'], http_status=http_status) def _get_router_for_floatingip_without_device_owner_check( self, context, internal_port, internal_subnet, external_network_id): gw_port = orm.aliased(models_v2.Port, name="gw_port") routerport_qry = context.session.query( l3_models.RouterPort.router_id, models_v2.IPAllocation.ip_address ).join( models_v2.Port, models_v2.IPAllocation ).filter( models_v2.Port.network_id == internal_port['network_id'], l3_models.RouterPort.port_type.in_( lib_constants.ROUTER_INTERFACE_OWNERS ), models_v2.IPAllocation.subnet_id == internal_subnet['id'] ).join( gw_port, gw_port.device_id == l3_models.RouterPort.router_id ).filter( gw_port.network_id == external_network_id, ).distinct() first_router_id = None for router_id, interface_ip in routerport_qry: if interface_ip == internal_subnet['gateway_ip']: return router_id if not first_router_id: first_router_id = router_id if first_router_id: return first_router_id raise l3_exc.ExternalGatewayForFloatingIPNotFound( subnet_id=internal_subnet['id'], external_network_id=external_network_id, port_id=internal_port['id']) def test_floatingip_via_router_interface_returns_404(self): self._test_floatingip_via_router_interface(exc.HTTPNotFound.code) def test_floatingip_via_router_interface_returns_201(self): # Override get_router_for_floatingip, as # networking-midonet's L3 service plugin would do. plugin = directory.get_plugin(plugin_constants.L3) with mock.patch.object(plugin, "get_router_for_floatingip", self._get_router_for_floatingip_without_device_owner_check): self._test_floatingip_via_router_interface(exc.HTTPCreated.code) def test_floatingip_delete_router_intf_with_subnet_id_returns_409(self): found = False with self.floatingip_with_assoc(): for p in self._list('ports')['ports']: if p['device_owner'] == lib_constants.DEVICE_OWNER_ROUTER_INTF: subnet_id = p['fixed_ips'][0]['subnet_id'] router_id = p['device_id'] self._router_interface_action( 'remove', router_id, subnet_id, None, expected_code=exc.HTTPConflict.code) found = True break self.assertTrue(found) def test_floatingip_delete_router_intf_with_port_id_returns_409(self): found = False with self.floatingip_with_assoc(): for p in self._list('ports')['ports']: if p['device_owner'] == lib_constants.DEVICE_OWNER_ROUTER_INTF: router_id = p['device_id'] self._router_interface_action( 'remove', router_id, None, p['id'], expected_code=exc.HTTPConflict.code) found = True break self.assertTrue(found) def _test_router_delete_subnet_inuse_returns_409(self, router, subnet): r, s = router, subnet self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None) # subnet cannot be deleted as it's attached to a router self._delete('subnets', s['subnet']['id'], expected_code=exc.HTTPConflict.code) def _ipv6_subnet(self, mode): return self.subnet(cidr='fd00::1/64', gateway_ip='fd00::1', ip_version=6, ipv6_ra_mode=mode, ipv6_address_mode=mode) def test_router_delete_subnet_inuse_returns_409(self): with self.router() as r: with self.subnet() as s: self._test_router_delete_subnet_inuse_returns_409(r, s) def test_router_delete_ipv6_slaac_subnet_inuse_returns_409(self): with self.router() as r: with self._ipv6_subnet(lib_constants.IPV6_SLAAC) as s: self._test_router_delete_subnet_inuse_returns_409(r, s) def test_router_delete_dhcpv6_stateless_subnet_inuse_returns_409(self): with self.router() as r: with self._ipv6_subnet(lib_constants.DHCPV6_STATELESS) as s: self._test_router_delete_subnet_inuse_returns_409(r, s) def test_delete_ext_net_with_disassociated_floating_ips(self): with self.network() as net: net_id = net['network']['id'] self._set_net_external(net_id) with self.subnet(network=net): self._make_floatingip(self.fmt, net_id) def test_create_floatingip_with_specific_ip(self): with self.subnet(cidr='10.0.0.0/24') as s: network_id = s['subnet']['network_id'] self._set_net_external(network_id) fp = self._make_floatingip(self.fmt, network_id, floating_ip='10.0.0.10') self.assertEqual('10.0.0.10', fp['floatingip']['floating_ip_address']) def test_create_floatingip_with_specific_ip_out_of_allocation(self): with self.subnet(cidr='10.0.0.0/24', allocation_pools=[ {'start': '10.0.0.10', 'end': '10.0.0.20'}] ) as s: network_id = s['subnet']['network_id'] self._set_net_external(network_id) fp = self._make_floatingip(self.fmt, network_id, floating_ip='10.0.0.30') self.assertEqual('10.0.0.30', fp['floatingip']['floating_ip_address']) def test_create_floatingip_with_specific_ip_non_admin(self): ctx = context.Context('user_id', 'tenant_id') with self.subnet(cidr='10.0.0.0/24') as s: network_id = s['subnet']['network_id'] self._set_net_external(network_id) self._make_floatingip(self.fmt, network_id, set_context=ctx, floating_ip='10.0.0.10', http_status=exc.HTTPForbidden.code) def test_create_floatingip_with_specific_ip_out_of_subnet(self): with self.subnet(cidr='10.0.0.0/24') as s: network_id = s['subnet']['network_id'] self._set_net_external(network_id) self._make_floatingip(self.fmt, network_id, floating_ip='10.0.1.10', http_status=exc.HTTPBadRequest.code) def test_create_floatingip_with_duplicated_specific_ip(self): with self.subnet(cidr='10.0.0.0/24') as s: network_id = s['subnet']['network_id'] self._set_net_external(network_id) self._make_floatingip(self.fmt, network_id, floating_ip='10.0.0.10') self._make_floatingip(self.fmt, network_id, floating_ip='10.0.0.10', http_status=exc.HTTPConflict.code) def test_create_floatingips_native_quotas(self): quota = 1 cfg.CONF.set_override('quota_floatingip', quota, group='QUOTAS') with self.subnet() as public_sub: self._set_net_external(public_sub['subnet']['network_id']) res = self._create_floatingip( self.fmt, public_sub['subnet']['network_id'], subnet_id=public_sub['subnet']['id']) self.assertEqual(exc.HTTPCreated.code, res.status_int) res = self._create_floatingip( self.fmt, public_sub['subnet']['network_id'], subnet_id=public_sub['subnet']['id']) self.assertEqual(exc.HTTPConflict.code, res.status_int) def test_router_specify_id_backend(self): plugin = directory.get_plugin(plugin_constants.L3) router_req = {'router': {'id': _uuid(), 'name': 'router', 'tenant_id': 'foo', 'admin_state_up': True}} result = plugin.create_router(context.Context('', 'foo'), router_req) self.assertEqual(router_req['router']['id'], result['id']) def test_create_floatingip_ipv6_only_network_returns_400(self): with self.subnet(cidr="2001:db8::/48", ip_version=6) as public_sub: self._set_net_external(public_sub['subnet']['network_id']) res = self._create_floatingip( self.fmt, public_sub['subnet']['network_id']) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_create_floatingip_ipv6_and_ipv4_network_creates_ipv4(self): with self.network() as n,\ self.subnet(cidr="2001:db8::/48", ip_version=6, network=n),\ self.subnet(cidr="192.168.1.0/24", ip_version=4, network=n): self._set_net_external(n['network']['id']) fip = self._make_floatingip(self.fmt, n['network']['id']) fip_set = netaddr.IPSet(netaddr.IPNetwork("192.168.1.0/24")) fip_ip = fip['floatingip']['floating_ip_address'] self.assertIn(netaddr.IPAddress(fip_ip), fip_set) def test_create_floatingip_with_assoc_to_ipv6_subnet(self): with self.subnet() as public_sub: self._set_net_external(public_sub['subnet']['network_id']) with self.subnet(cidr="2001:db8::/48", ip_version=6) as private_sub: with self.port(subnet=private_sub) as private_port: res = self._create_floatingip( self.fmt, public_sub['subnet']['network_id'], port_id=private_port['port']['id']) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_create_floatingip_with_assoc_to_ipv4_and_ipv6_port(self): with self.network() as n,\ self.subnet(cidr='10.0.0.0/24', network=n) as s4,\ self.subnet(cidr='2001:db8::/64', ip_version=6, network=n),\ self.port(subnet=s4) as p: self.assertEqual(2, len(p['port']['fixed_ips'])) ipv4_address = next(i['ip_address'] for i in p['port']['fixed_ips'] if netaddr.IPAddress(i['ip_address']).version == 4) with self.floatingip_with_assoc(port_id=p['port']['id']) as fip: self.assertEqual(fip['floatingip']['fixed_ip_address'], ipv4_address) floating_ip = netaddr.IPAddress( fip['floatingip']['floating_ip_address']) self.assertEqual(4, floating_ip.version) def test_create_router_gateway_fails_nested(self): # Force _update_router_gw_info failure plugin = directory.get_plugin(plugin_constants.L3) if not isinstance(plugin, l3_db.L3_NAT_dbonly_mixin): self.skipTest("Plugin is not L3_NAT_dbonly_mixin") ctx = context.Context('', 'foo') data = {'router': { 'name': 'router1', 'admin_state_up': True, 'external_gateway_info': {'network_id': 'some_uuid'}, 'tenant_id': 'some_tenant'}} def mock_fail__update_router_gw_info(ctx, router_id, info, router=None): # Fail with breaking transaction with ctx.session.begin(subtransactions=True): raise n_exc.NeutronException mock.patch.object(plugin, '_update_router_gw_info', side_effect=mock_fail__update_router_gw_info).start() def create_router_with_transaction(ctx, data): # Emulates what many plugins do with ctx.session.begin(subtransactions=True): plugin.create_router(ctx, data) # Verify router doesn't persist on failure self.assertRaises(n_exc.NeutronException, create_router_with_transaction, ctx, data) routers = plugin.get_routers(ctx) self.assertEqual(0, len(routers)) def test_create_router_gateway_fails_nested_delete_router_failed(self): # Force _update_router_gw_info failure plugin = directory.get_plugin(plugin_constants.L3) if not isinstance(plugin, l3_db.L3_NAT_dbonly_mixin): self.skipTest("Plugin is not L3_NAT_dbonly_mixin") ctx = context.Context('', 'foo') data = {'router': { 'name': 'router1', 'admin_state_up': True, 'external_gateway_info': {'network_id': 'some_uuid'}, 'tenant_id': 'some_tenant'}} def mock_fail__update_router_gw_info(ctx, router_id, info, router=None): # Fail with breaking transaction with ctx.session.begin(subtransactions=True): raise n_exc.NeutronException def mock_fail_delete_router(ctx, router_id): with ctx.session.begin(subtransactions=True): raise Exception() mock.patch.object(plugin, '_update_router_gw_info', side_effect=mock_fail__update_router_gw_info).start() mock.patch.object(plugin, 'delete_router', mock_fail_delete_router).start() def create_router_with_transaction(ctx, data): # Emulates what many plugins do with ctx.session.begin(subtransactions=True): plugin.create_router(ctx, data) # Verify router doesn't persist on failure self.assertRaises(n_exc.NeutronException, create_router_with_transaction, ctx, data) routers = plugin.get_routers(ctx) self.assertEqual(0, len(routers)) def test_router_add_interface_by_port_fails_nested(self): # Force _validate_router_port_info failure plugin = directory.get_plugin(plugin_constants.L3) if not isinstance(plugin, l3_db.L3_NAT_dbonly_mixin): self.skipTest("Plugin is not L3_NAT_dbonly_mixin") orig_update_port = self.plugin.update_port def mock_fail__validate_router_port_info(ctx, router, port_id): # Fail with raising BadRequest exception msg = "Failure mocking..." raise n_exc.BadRequest(resource='router', msg=msg) def mock_update_port_with_transaction(ctx, id, port): # Update port within a sub-transaction with ctx.session.begin(subtransactions=True): orig_update_port(ctx, id, port) def add_router_interface_with_transaction(ctx, router_id, interface_info): # Call add_router_interface() within a sub-transaction with ctx.session.begin(): plugin.add_router_interface(ctx, router_id, interface_info) tenant_id = _uuid() ctx = context.Context('', tenant_id) with self.network(tenant_id=tenant_id) as network, ( self.router(name='router1', admin_state_up=True, tenant_id=tenant_id)) as router: with self.subnet(network=network, cidr='10.0.0.0/24', tenant_id=tenant_id) as subnet: fixed_ips = [{'subnet_id': subnet['subnet']['id']}] with self.port(subnet=subnet, fixed_ips=fixed_ips, tenant_id=tenant_id) as port: mock.patch.object( self.plugin, 'update_port', side_effect=( mock_update_port_with_transaction)).start() mock.patch.object( plugin, '_validate_router_port_info', side_effect=( mock_fail__validate_router_port_info)).start() self.assertRaises(n_exc.BadRequest, add_router_interface_with_transaction, ctx, router['router']['id'], {'port_id': port['port']['id']}) # fetch port and confirm device_id and device_owner body = self._show('ports', port['port']['id']) self.assertEqual('', body['port']['device_owner']) self.assertEqual('', body['port']['device_id']) def test_update_subnet_gateway_for_external_net(self): """Test to make sure notification to routers occurs when the gateway ip address of a subnet of the external network is changed. """ plugin = directory.get_plugin(plugin_constants.L3) if not hasattr(plugin, 'l3_rpc_notifier'): self.skipTest("Plugin does not support l3_rpc_notifier") # make sure the callback is registered. registry.subscribe( l3_db.L3RpcNotifierMixin._notify_subnet_gateway_ip_update, resources.SUBNET, events.AFTER_UPDATE) with mock.patch.object(plugin.l3_rpc_notifier, 'routers_updated') as chk_method: with self.network() as network: allocation_pools = [{'start': '120.0.0.3', 'end': '120.0.0.254'}] with self.subnet(network=network, gateway_ip='120.0.0.1', allocation_pools=allocation_pools, cidr='120.0.0.0/24') as subnet: kwargs = { 'device_owner': lib_constants.DEVICE_OWNER_ROUTER_GW, 'device_id': 'fake_device'} with self.port(subnet=subnet, **kwargs): data = {'subnet': {'gateway_ip': '120.0.0.2'}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['subnet']['gateway_ip'], res['subnet']['gateway_ip']) chk_method.assert_called_with(mock.ANY, ['fake_device'], None) def test__notify_subnetpool_address_scope_update(self): plugin = directory.get_plugin(plugin_constants.L3) tenant_id = _uuid() with mock.patch.object( plugin, 'notify_routers_updated') as chk_method, \ self.subnetpool(prefixes=['10.0.0.0/24'], admin=True, name='sp', tenant_id=tenant_id) as subnetpool, \ self.router(tenant_id=tenant_id) as router, \ self.network(tenant_id=tenant_id) as network: subnetpool_id = subnetpool['subnetpool']['id'] data = {'subnet': { 'network_id': network['network']['id'], 'subnetpool_id': subnetpool_id, 'prefixlen': 24, 'ip_version': 4, 'tenant_id': tenant_id}} req = self.new_create_request('subnets', data) subnet = self.deserialize(self.fmt, req.get_response(self.api)) admin_ctx = context.get_admin_context() plugin.add_router_interface( admin_ctx, router['router']['id'], {'subnet_id': subnet['subnet']['id']}) l3_db.L3RpcNotifierMixin._notify_subnetpool_address_scope_update( mock.ANY, mock.ANY, mock.ANY, context=admin_ctx, subnetpool_id=subnetpool_id) chk_method.assert_called_with(admin_ctx, [router['router']['id']]) def test_janitor_clears_orphaned_floatingip_port(self): plugin = directory.get_plugin(plugin_constants.L3) with self.network() as n: # floating IP ports are initially created with a device ID of # PENDING and are updated after the floating IP is actually # created. port_res = self._create_port( self.fmt, n['network']['id'], tenant_id=n['network']['tenant_id'], device_id='PENDING', device_owner=lib_constants.DEVICE_OWNER_FLOATINGIP) port = self.deserialize(self.fmt, port_res) plugin._clean_garbage() # first call should just have marked it as a candidate so port # should still exist port = self._show('ports', port['port']['id']) self.assertEqual('PENDING', port['port']['device_id']) # second call will delete the port since it has no associated # floating IP plugin._clean_garbage() self._show('ports', port['port']['id'], expected_code=exc.HTTPNotFound.code) def test_janitor_updates_port_device_id(self): # if a server dies after the floating IP is created but before it # updates the floating IP port device ID, the janitor will be # responsible for updating the device ID to the correct value. plugin = directory.get_plugin(plugin_constants.L3) with self.floatingip_with_assoc() as fip: fip_port = self._list('ports', query_params='device_owner=network:floatingip')['ports'][0] # simulate a failed update by just setting the device_id of # the fip port back to PENDING data = {'port': {'device_id': 'PENDING'}} self._update('ports', fip_port['id'], data) plugin._clean_garbage() # first call just marks as candidate, so it shouldn't be changed port = self._show('ports', fip_port['id']) self.assertEqual('PENDING', port['port']['device_id']) # second call updates device ID to fip plugin._clean_garbage() # first call just marks as candidate, so it shouldn't be changed port = self._show('ports', fip_port['id']) self.assertEqual(fip['floatingip']['id'], port['port']['device_id']) def test_janitor_doesnt_delete_if_fixed_in_interim(self): # here we ensure that the janitor doesn't delete the port on the second # call if the conditions have been fixed plugin = directory.get_plugin(plugin_constants.L3) with self.network() as n: port_res = self._create_port( self.fmt, n['network']['id'], tenant_id=n['network']['tenant_id'], device_id='PENDING', device_owner=lib_constants.DEVICE_OWNER_FLOATINGIP) port = self.deserialize(self.fmt, port_res) plugin._clean_garbage() # first call should just have marked it as a candidate so port # should still exist port = self._show('ports', port['port']['id']) self.assertEqual('PENDING', port['port']['device_id']) data = {'port': {'device_id': 'something_else'}} self._update('ports', port['port']['id'], data) # now that the device ID has changed, the janitor shouldn't delete plugin._clean_garbage() self._show('ports', port['port']['id']) def test_router_delete_callback(self): def prevent_router_deletion(*args, **kwargs): # unsubscribe now that we have invoked the callback registry.unsubscribe(prevent_router_deletion, resources.ROUTER, events.BEFORE_DELETE) raise exc.HTTPForbidden registry.subscribe(prevent_router_deletion, resources.ROUTER, events.BEFORE_DELETE) with self.subnet(): res = self._create_router(self.fmt, _uuid()) router = self.deserialize(self.fmt, res) self._delete('routers', router['router']['id'], exc.HTTPForbidden.code) def test_associate_to_dhcp_port_fails(self): with self.subnet(cidr="10.0.0.0/24", ip_version=4) as sub: with self.port(subnet=sub, device_owner=lib_constants.DEVICE_OWNER_DHCP) as p: res = self._create_floatingip( self.fmt, sub['subnet']['network_id'], port_id=p['port']['id']) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) class L3AgentDbTestCaseBase(L3NatTestCaseMixin): """Unit tests for methods called by the L3 agent.""" def test_l3_agent_routers_query_interfaces(self): with self.router() as r: with self.port() as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) routers = self.plugin.get_sync_data( context.get_admin_context(), None) self.assertEqual(1, len(routers)) interfaces = routers[0][lib_constants.INTERFACE_KEY] self.assertEqual(1, len(interfaces)) subnets = interfaces[0]['subnets'] self.assertEqual(1, len(subnets)) subnet_id = subnets[0]['id'] wanted_subnetid = p['port']['fixed_ips'][0]['subnet_id'] self.assertEqual(wanted_subnetid, subnet_id) def test_l3_agent_sync_interfaces(self): """Test L3 interfaces query return valid result""" with self.router() as router1, self.router() as router2: with self.port() as port1, self.port() as port2: self._router_interface_action('add', router1['router']['id'], None, port1['port']['id']) self._router_interface_action('add', router2['router']['id'], None, port2['port']['id']) admin_ctx = context.get_admin_context() router1_id = router1['router']['id'] router2_id = router2['router']['id'] # Verify if router1 pass in, return only interface from router1 ifaces = self.plugin._get_sync_interfaces(admin_ctx, [router1_id]) self.assertEqual(1, len(ifaces)) self.assertEqual(router1_id, ifaces[0]['device_id']) # Verify if router1 and router2 pass in, return both interfaces ifaces = self.plugin._get_sync_interfaces(admin_ctx, [router1_id, router2_id]) self.assertEqual(2, len(ifaces)) device_list = [i['device_id'] for i in ifaces] self.assertIn(router1_id, device_list) self.assertIn(router2_id, device_list) #Verify if no router pass in, return empty list ifaces = self.plugin._get_sync_interfaces(admin_ctx, None) self.assertEqual(0, len(ifaces)) def test_l3_agent_routers_query_ignore_interfaces_with_moreThanOneIp(self): with self.router() as r: with self.subnet(cidr='9.0.1.0/24') as subnet: with self.port(subnet=subnet, fixed_ips=[{'ip_address': '9.0.1.3'}]) as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) port = {'port': {'fixed_ips': [{'ip_address': '9.0.1.4', 'subnet_id': subnet['subnet']['id']}, {'ip_address': '9.0.1.5', 'subnet_id': subnet['subnet']['id']}]}} ctx = context.get_admin_context() self.core_plugin.update_port(ctx, p['port']['id'], port) routers = self.plugin.get_sync_data(ctx, None) self.assertEqual(1, len(routers)) interfaces = routers[0].get(lib_constants.INTERFACE_KEY, []) self.assertEqual(1, len(interfaces)) def test_l3_agent_routers_query_gateway(self): with self.router() as r: with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s['subnet']['network_id']) routers = self.plugin.get_sync_data( context.get_admin_context(), [r['router']['id']]) self.assertEqual(1, len(routers)) gw_port = routers[0]['gw_port'] subnets = gw_port.get('subnets') self.assertEqual(1, len(subnets)) self.assertEqual(s['subnet']['id'], subnets[0]['id']) self._remove_external_gateway_from_router( r['router']['id'], s['subnet']['network_id']) def test_l3_agent_routers_query_floatingips(self): with self.floatingip_with_assoc() as fip: routers = self.plugin.get_sync_data( context.get_admin_context(), [fip['floatingip']['router_id']]) self.assertEqual(1, len(routers)) floatingips = routers[0][lib_constants.FLOATINGIP_KEY] self.assertEqual(1, len(floatingips)) self.assertEqual(floatingips[0]['id'], fip['floatingip']['id']) self.assertEqual(floatingips[0]['port_id'], fip['floatingip']['port_id']) self.assertIsNotNone(floatingips[0]['fixed_ip_address']) self.assertIsNotNone(floatingips[0]['router_id']) def _test_notify_op_agent(self, target_func, *args): l3_rpc_agent_api_str = ( 'neutron.api.rpc.agentnotifiers.l3_rpc_agent_api.L3AgentNotifyAPI') with mock.patch(l3_rpc_agent_api_str): plugin = directory.get_plugin(plugin_constants.L3) notifyApi = plugin.l3_rpc_notifier kargs = [item for item in args] kargs.append(notifyApi) target_func(*kargs) def _test_router_gateway_op_agent(self, notifyApi): with self.router() as r: with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s['subnet']['network_id']) self._remove_external_gateway_from_router( r['router']['id'], s['subnet']['network_id']) self.assertEqual( 2, notifyApi.routers_updated.call_count) def test_router_gateway_op_agent(self): self._test_notify_op_agent(self._test_router_gateway_op_agent) def _test_interfaces_op_agent(self, r, notifyApi): with self.port() as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) # clean-up self._router_interface_action('remove', r['router']['id'], None, p['port']['id']) self.assertEqual(2, notifyApi.routers_updated.call_count) def test_interfaces_op_agent(self): with self.router() as r: self._test_notify_op_agent( self._test_interfaces_op_agent, r) def _test_floatingips_op_agent(self, notifyApi): with self.floatingip_with_assoc(): pass # add gateway, add interface, associate, deletion of floatingip self.assertEqual(4, notifyApi.routers_updated.call_count) def test_floatingips_op_agent(self): self._test_notify_op_agent(self._test_floatingips_op_agent) def test_floatingips_create_precommit_event(self): fake_method = mock.Mock() try: registry.subscribe(fake_method, resources.FLOATING_IP, events.PRECOMMIT_CREATE) with self.floatingip_with_assoc() as f: fake_method.assert_called_once_with( resources.FLOATING_IP, events.PRECOMMIT_CREATE, mock.ANY, context=mock.ANY, floatingip=mock.ANY, floatingip_id=f['floatingip']['id'], floatingip_db=mock.ANY) finally: registry.unsubscribe(fake_method, resources.FLOATING_IP, events.PRECOMMIT_CREATE) def test_router_create_precommit_event(self): nset = lambda *a, **k: setattr(k['router_db'], 'name', 'hello') registry.subscribe(nset, resources.ROUTER, events.PRECOMMIT_CREATE) with self.router() as r: self.assertEqual('hello', r['router']['name']) def test_router_create_event_exception_preserved(self): # this exception should be propagated out of the callback and # converted into its API equivalent of 404 e404 = mock.Mock(side_effect=l3_exc.RouterNotFound(router_id='1')) registry.subscribe(e404, resources.ROUTER, events.PRECOMMIT_CREATE) res = self._create_router(self.fmt, 'tenid') self.assertEqual(exc.HTTPNotFound.code, res.status_int) # make sure nothing committed body = self._list('routers') self.assertFalse(body['routers']) def test_router_update_precommit_event(self): def _nset(r, v, s, payload=None): setattr(payload.desired_state, 'name', payload.states[0]['name'] + '_ha!') registry.subscribe(_nset, resources.ROUTER, events.PRECOMMIT_UPDATE) with self.router(name='original') as r: update = self._update('routers', r['router']['id'], {'router': {'name': 'hi'}}) # our rude callback should have changed the name to the original # plus some extra self.assertEqual('original_ha!', update['router']['name']) def test_router_update_event_exception_preserved(self): # this exception should be propagated out of the callback and # converted into its API equivalent of 404 e404 = mock.Mock(side_effect=l3_exc.RouterNotFound(router_id='1')) registry.subscribe(e404, resources.ROUTER, events.PRECOMMIT_UPDATE) with self.router(name='a') as r: self._update('routers', r['router']['id'], {'router': {'name': 'hi'}}, expected_code=exc.HTTPNotFound.code) # ensure it stopped the commit new = self._show('routers', r['router']['id']) self.assertEqual('a', new['router']['name']) def test_router_delete_precommit_event(self): deleted = [] auditor = lambda *a, **k: deleted.append(k['router_id']) registry.subscribe(auditor, resources.ROUTER, events.PRECOMMIT_DELETE) with self.router() as r: self._delete('routers', r['router']['id']) self.assertEqual([r['router']['id']], deleted) def test_router_delete_event_exception_preserved(self): # this exception should be propagated out of the callback and # converted into its API equivalent of 409 e409 = mock.Mock(side_effect=l3_exc.RouterInUse(router_id='1')) registry.subscribe(e409, resources.ROUTER, events.PRECOMMIT_DELETE) with self.router() as r: self._delete('routers', r['router']['id'], expected_code=exc.HTTPConflict.code) # ensure it stopped the commit self.assertTrue(self._show('routers', r['router']['id'])) class L3BaseForIntTests(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): mock_rescheduling = True def setUp(self, plugin=None, ext_mgr=None, service_plugins=None): if not plugin: plugin = 'neutron.tests.unit.extensions.test_l3.TestL3NatIntPlugin' # for these tests we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) ext_mgr = ext_mgr or L3TestExtensionManager() if self.mock_rescheduling: mock.patch('%s._check_router_needs_rescheduling' % plugin, new=lambda *a: False).start() super(L3BaseForIntTests, self).setUp(plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) self.setup_notification_driver() class L3BaseForSepTests(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def setUp(self, plugin=None, ext_mgr=None): # the plugin without L3 support if not plugin: plugin = 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin' # the L3 service plugin l3_plugin = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatServicePlugin') service_plugins = {'l3_plugin_name': l3_plugin} # for these tests we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) if not ext_mgr: ext_mgr = L3TestExtensionManager() super(L3BaseForSepTests, self).setUp(plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) self.setup_notification_driver() class L3NatDBIntAgentSchedulingTestCase(L3BaseForIntTests, L3NatTestCaseMixin, test_agent. AgentDBTestMixIn): """Unit tests for core plugin with L3 routing and scheduling integrated.""" def setUp(self, plugin='neutron.tests.unit.extensions.test_l3.' 'TestL3NatIntAgentSchedulingPlugin', ext_mgr=None, service_plugins=None): self.mock_rescheduling = False super(L3NatDBIntAgentSchedulingTestCase, self).setUp( plugin, ext_mgr, service_plugins) self.adminContext = context.get_admin_context() def _assert_router_on_agent(self, router_id, agent_host): plugin = directory.get_plugin(plugin_constants.L3) agents = plugin.list_l3_agents_hosting_router( self.adminContext, router_id)['agents'] self.assertEqual(1, len(agents)) self.assertEqual(agents[0]['host'], agent_host) def test_update_gateway_agent_exists_supporting_network(self): with self.router() as r, self.subnet() as s1, self.subnet() as s2: self._set_net_external(s1['subnet']['network_id']) l3_rpc_cb = l3_rpc.L3RpcCallback() helpers.register_l3_agent( host='host1', ext_net_id=s1['subnet']['network_id']) helpers.register_l3_agent( host='host2', internal_only=False, ext_net_id=s2['subnet']['network_id']) l3_rpc_cb.get_router_ids(self.adminContext, host='host1') self._assert_router_on_agent(r['router']['id'], 'host1') self._add_external_gateway_to_router( r['router']['id'], s1['subnet']['network_id']) self._assert_router_on_agent(r['router']['id'], 'host1') self._set_net_external(s2['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s2['subnet']['network_id']) self._assert_router_on_agent(r['router']['id'], 'host2') def test_update_gateway_agent_exists_supporting_multiple_network(self): with self.router() as r, self.subnet() as s1, self.subnet() as s2: self._set_net_external(s1['subnet']['network_id']) l3_rpc_cb = l3_rpc.L3RpcCallback() helpers.register_l3_agent( host='host1', ext_net_id=s1['subnet']['network_id']) helpers.register_l3_agent( host='host2', internal_only=False, ext_net_id='', ext_bridge='') l3_rpc_cb.get_router_ids(self.adminContext, host='host1') self._assert_router_on_agent(r['router']['id'], 'host1') self._add_external_gateway_to_router( r['router']['id'], s1['subnet']['network_id']) self._assert_router_on_agent(r['router']['id'], 'host1') self._set_net_external(s2['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s2['subnet']['network_id']) self._assert_router_on_agent(r['router']['id'], 'host2') def test_router_update_gateway_scheduling_not_supported(self): plugin = directory.get_plugin(plugin_constants.L3) mock.patch.object(plugin, 'router_supports_scheduling', return_value=False).start() with self.router() as r: with self.subnet() as s1: with self.subnet() as s2: self._set_net_external(s1['subnet']['network_id']) self._set_net_external(s2['subnet']['network_id']) # this should pass even though there are multiple # external networks since no scheduling decision needs # to be made self._add_external_gateway_to_router( r['router']['id'], s1['subnet']['network_id']) def test_router_update_gateway_no_eligible_l3_agent(self): with self.router() as r: with self.subnet() as s1: with self.subnet() as s2: self._set_net_external(s1['subnet']['network_id']) self._set_net_external(s2['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s1['subnet']['network_id'], expected_code=exc.HTTPBadRequest.code) class L3RpcCallbackTestCase(base.BaseTestCase): def setUp(self): super(L3RpcCallbackTestCase, self).setUp() self.mock_plugin = mock.patch.object( l3_rpc.L3RpcCallback, 'plugin', new_callable=mock.PropertyMock).start() self.mock_l3plugin = mock.patch.object( l3_rpc.L3RpcCallback, 'l3plugin', new_callable=mock.PropertyMock).start() self.l3_rpc_cb = l3_rpc.L3RpcCallback() def test__ensure_host_set_on_port_host_id_none(self): port = {'id': 'id', portbindings.HOST_ID: 'somehost'} self.l3_rpc_cb._ensure_host_set_on_port(None, None, port) self.assertFalse(self.l3_rpc_cb.plugin.update_port.called) def test__ensure_host_set_on_port_bad_bindings(self): for b in (portbindings.VIF_TYPE_BINDING_FAILED, portbindings.VIF_TYPE_UNBOUND): port = {'id': 'id', portbindings.HOST_ID: 'somehost', portbindings.VIF_TYPE: b} self.l3_rpc_cb._ensure_host_set_on_port(None, 'somehost', port) self.assertTrue(self.l3_rpc_cb.plugin.update_port.called) def test__ensure_host_set_on_port_update_on_concurrent_delete(self): port_id = 'foo_port_id' port = { 'id': port_id, 'device_owner': DEVICE_OWNER_COMPUTE, portbindings.HOST_ID: '', portbindings.VIF_TYPE: portbindings.VIF_TYPE_BINDING_FAILED } router_id = 'foo_router_id' self.l3_rpc_cb.plugin.update_port.side_effect = n_exc.PortNotFound( port_id=port_id) with mock.patch.object(l3_rpc.LOG, 'debug') as mock_log: self.l3_rpc_cb._ensure_host_set_on_port( mock.ANY, mock.ANY, port, router_id) self.l3_rpc_cb.plugin.update_port.assert_called_once_with( mock.ANY, port_id, {'port': {portbindings.HOST_ID: mock.ANY}}) self.assertTrue(mock_log.call_count) expected_message = ('Port foo_port_id not found while updating ' 'agent binding for router foo_router_id.') actual_message = mock_log.call_args[0][0] % mock_log.call_args[0][1] self.assertEqual(expected_message, actual_message) def test__ensure_host_set_on_ports_dvr_ha_router_with_gatway(self): context = mock.Mock() host = "fake_host" router_id = 'foo_router_id' router = {"id": router_id, "gw_port_host": host, "gw_port": {"id": "foo_port_id"}, "distributed": True, "ha": True} mock__ensure = mock.Mock() self.l3_rpc_cb._ensure_host_set_on_port = mock__ensure self.l3_rpc_cb._ensure_host_set_on_ports(context, host, [router]) mock__ensure.assert_called_once_with( context, host, router["gw_port"], router_id, ha_router_port=True) class L3AgentDbIntTestCase(L3BaseForIntTests, L3AgentDbTestCaseBase): """Unit tests for methods called by the L3 agent for the case where core plugin implements L3 routing. """ def setUp(self): super(L3AgentDbIntTestCase, self).setUp() self.core_plugin = TestL3NatIntPlugin() self.plugin = self.core_plugin class L3AgentDbSepTestCase(L3BaseForSepTests, L3AgentDbTestCaseBase): """Unit tests for methods called by the L3 agent for the case where separate service plugin implements L3 routing. """ def setUp(self): super(L3AgentDbSepTestCase, self).setUp() self.core_plugin = TestNoL3NatPlugin() self.plugin = TestL3NatServicePlugin() class TestL3DbOperationBounds(test_db_base_plugin_v2.DbOperationBoundMixin, L3NatTestCaseMixin, ml2_base.ML2TestFramework): def setUp(self): super(TestL3DbOperationBounds, self).setUp() ext_mgr = L3TestExtensionManager() self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) self.kwargs = self.get_api_kwargs() def test_router_list_queries_constant(self): with self.subnet(**self.kwargs) as s: self._set_net_external(s['subnet']['network_id']) def router_maker(): ext_info = {'network_id': s['subnet']['network_id']} res = self._create_router( self.fmt, arg_list=('external_gateway_info',), external_gateway_info=ext_info, **self.kwargs) return self.deserialize(self.fmt, res) self._assert_object_list_queries_constant(router_maker, 'routers') class TestL3DbOperationBoundsTenant(TestL3DbOperationBounds): admin = False class L3NatDBTestCaseMixin(object): """L3_NAT_dbonly_mixin specific test cases.""" def setUp(self): super(L3NatDBTestCaseMixin, self).setUp() plugin = directory.get_plugin(plugin_constants.L3) if not isinstance(plugin, l3_db.L3_NAT_dbonly_mixin): self.skipTest("Plugin is not L3_NAT_dbonly_mixin") def test_create_router_gateway_fails(self): """Force _update_router_gw_info failure and see the exception is propagated. """ plugin = directory.get_plugin(plugin_constants.L3) ctx = context.Context('', 'foo') class MyException(Exception): pass mock.patch.object(plugin, '_update_router_gw_info', side_effect=MyException).start() with self.network() as n: data = {'router': { 'name': 'router1', 'admin_state_up': True, 'tenant_id': ctx.tenant_id, 'external_gateway_info': {'network_id': n['network']['id']}}} self.assertRaises(MyException, plugin.create_router, ctx, data) # Verify router doesn't persist on failure routers = plugin.get_routers(ctx) self.assertEqual(0, len(routers)) class L3NatDBIntTestCase(L3BaseForIntTests, L3NatTestCaseBase, L3NatDBTestCaseMixin): """Unit tests for core plugin with L3 routing integrated.""" pass class L3NatDBSepTestCase(L3BaseForSepTests, L3NatTestCaseBase, L3NatDBTestCaseMixin): """Unit tests for a separate L3 routing service plugin.""" def test_port_deletion_prevention_handles_missing_port(self): pl = directory.get_plugin(plugin_constants.L3) self.assertIsNone( pl.prevent_l3_port_deletion(context.get_admin_context(), 'fakeid') ) class L3TestExtensionManagerWithDNS(L3TestExtensionManager): def get_resources(self): return l3.L3.get_resources() class L3NatDBFloatingIpTestCaseWithDNS(L3BaseForSepTests, L3NatTestCaseMixin): """Unit tests for floating ip with external DNS integration""" fmt = 'json' DNS_NAME = 'test' DNS_DOMAIN = 'test-domain.org.' PUBLIC_CIDR = '11.0.0.0/24' PRIVATE_CIDR = '10.0.0.0/24' mock_client = mock.MagicMock() mock_admin_client = mock.MagicMock() MOCK_PATH = ('neutron.services.externaldns.drivers.' 'designate.driver.get_clients') mock_config = {'return_value': (mock_client, mock_admin_client)} _extension_drivers = ['dns'] def setUp(self): ext_mgr = L3TestExtensionManagerWithDNS() plugin = 'neutron.plugins.ml2.plugin.Ml2Plugin' cfg.CONF.set_override('extension_drivers', self._extension_drivers, group='ml2') super(L3NatDBFloatingIpTestCaseWithDNS, self).setUp(plugin=plugin, ext_mgr=ext_mgr) cfg.CONF.set_override('external_dns_driver', 'designate') self.mock_client.reset_mock() self.mock_admin_client.reset_mock() def _create_network(self, fmt, name, admin_state_up, arg_list=None, set_context=False, tenant_id=None, **kwargs): new_arg_list = ('dns_domain',) if arg_list is not None: new_arg_list = arg_list + new_arg_list return super(L3NatDBFloatingIpTestCaseWithDNS, self)._create_network(fmt, name, admin_state_up, arg_list=new_arg_list, set_context=set_context, tenant_id=tenant_id, **kwargs) def _create_port(self, fmt, name, admin_state_up, arg_list=None, set_context=False, tenant_id=None, **kwargs): new_arg_list = ('dns_name',) if arg_list is not None: new_arg_list = arg_list + new_arg_list return super(L3NatDBFloatingIpTestCaseWithDNS, self)._create_port(fmt, name, admin_state_up, arg_list=new_arg_list, set_context=set_context, tenant_id=tenant_id, **kwargs) def _create_net_sub_port(self, dns_domain='', dns_name=''): with self.network(dns_domain=dns_domain) as n: with self.subnet(cidr=self.PRIVATE_CIDR, network=n) as private_sub: with self.port(private_sub, dns_name=dns_name) as p: return n, private_sub, p @contextlib.contextmanager def _create_floatingip_with_dns(self, net_dns_domain='', port_dns_name='', flip_dns_domain='', flip_dns_name='', assoc_port=False, private_sub=None): if private_sub is None: n, private_sub, p = self._create_net_sub_port( dns_domain=net_dns_domain, dns_name=port_dns_name) data = {'fmt': self.fmt} data['dns_domain'] = flip_dns_domain data['dns_name'] = flip_dns_name # Set ourselves up to call the right function with # the right arguments for the with block if assoc_port: data['tenant_id'] = n['network']['tenant_id'] data['port_id'] = p['port']['id'] create_floatingip = self.floatingip_with_assoc else: data['private_sub'] = private_sub create_floatingip = self.floatingip_no_assoc with create_floatingip(**data) as flip: yield flip['floatingip'] @contextlib.contextmanager def _create_floatingip_with_dns_on_update(self, net_dns_domain='', port_dns_name='', flip_dns_domain='', flip_dns_name=''): n, private_sub, p = self._create_net_sub_port( dns_domain=net_dns_domain, dns_name=port_dns_name) with self._create_floatingip_with_dns(flip_dns_domain=flip_dns_domain, flip_dns_name=flip_dns_name, private_sub=private_sub) as flip: flip_id = flip['id'] data = {'floatingip': {'port_id': p['port']['id']}} req = self.new_update_request('floatingips', data, flip_id) res = req.get_response(self._api_for_resource('floatingip')) self.assertEqual(200, res.status_code) floatingip = self.deserialize(self.fmt, res)['floatingip'] self.assertEqual(p['port']['id'], floatingip['port_id']) yield flip def _get_in_addr_zone_name(self, in_addr_name): units = self._get_bytes_or_nybles_to_skip(in_addr_name) return '.'.join(in_addr_name.split('.')[int(units):]) def _get_bytes_or_nybles_to_skip(self, in_addr_name): if 'in-addr.arpa' in in_addr_name: return (( 32 - cfg.CONF.designate.ipv4_ptr_zone_prefix_size) / 8) return (128 - cfg.CONF.designate.ipv6_ptr_zone_prefix_size) / 4 def _get_in_addr(self, record): in_addr_name = netaddr.IPAddress(record).reverse_dns in_addr_zone_name = self._get_in_addr_zone_name(in_addr_name) return in_addr_name, in_addr_zone_name def _assert_recordset_created(self, floating_ip_address): # The recordsets.create function should be called with: # dns_domain, dns_name, 'A', ip_address ('A' for IPv4, 'AAAA' for IPv6) self.mock_client.recordsets.create.assert_called_with(self.DNS_DOMAIN, self.DNS_NAME, 'A', [floating_ip_address]) in_addr_name, in_addr_zone_name = self._get_in_addr( floating_ip_address) self.mock_admin_client.recordsets.create.assert_called_with( in_addr_zone_name, in_addr_name, 'PTR', ['%s.%s' % (self.DNS_NAME, self.DNS_DOMAIN)]) @mock.patch(MOCK_PATH, **mock_config) def test_floatingip_create(self, mock_args): with self._create_floatingip_with_dns(): pass self.mock_client.recordsets.create.assert_not_called() self.mock_admin_client.recordsets.create.assert_not_called() @mock.patch(MOCK_PATH, **mock_config) def test_floatingip_create_with_flip_dns(self, mock_args): with self._create_floatingip_with_dns(flip_dns_domain=self.DNS_DOMAIN, flip_dns_name=self.DNS_NAME) as flip: floatingip = flip self._assert_recordset_created(floatingip['floating_ip_address']) self.assertEqual(self.DNS_DOMAIN, floatingip['dns_domain']) self.assertEqual(self.DNS_NAME, floatingip['dns_name']) @mock.patch(MOCK_PATH, **mock_config) def test_floatingip_create_with_net_port_dns(self, mock_args): cfg.CONF.set_override('dns_domain', self.DNS_DOMAIN) with self._create_floatingip_with_dns(net_dns_domain=self.DNS_DOMAIN, port_dns_name=self.DNS_NAME, assoc_port=True) as flip: floatingip = flip self._assert_recordset_created(floatingip['floating_ip_address']) @mock.patch(MOCK_PATH, **mock_config) def test_floatingip_create_with_flip_and_net_port_dns(self, mock_args): # If both network+port and the floating ip have dns domain and # dns name, floating ip's information should take priority cfg.CONF.set_override('dns_domain', self.DNS_DOMAIN) with self._create_floatingip_with_dns(net_dns_domain='junkdomain.org.', port_dns_name='junk', flip_dns_domain=self.DNS_DOMAIN, flip_dns_name=self.DNS_NAME, assoc_port=True) as flip: floatingip = flip # External DNS service should have been called with floating ip's # dns information, not the network+port's dns information self._assert_recordset_created(floatingip['floating_ip_address']) self.assertEqual(self.DNS_DOMAIN, floatingip['dns_domain']) self.assertEqual(self.DNS_NAME, floatingip['dns_name']) @mock.patch(MOCK_PATH, **mock_config) def test_floatingip_associate_port(self, mock_args): with self._create_floatingip_with_dns_on_update(): pass self.mock_client.recordsets.create.assert_not_called() self.mock_admin_client.recordsets.create.assert_not_called() @mock.patch(MOCK_PATH, **mock_config) def test_floatingip_associate_port_with_flip_dns(self, mock_args): with self._create_floatingip_with_dns_on_update( flip_dns_domain=self.DNS_DOMAIN, flip_dns_name=self.DNS_NAME) as flip: floatingip = flip self._assert_recordset_created(floatingip['floating_ip_address']) self.assertEqual(self.DNS_DOMAIN, floatingip['dns_domain']) self.assertEqual(self.DNS_NAME, floatingip['dns_name']) @mock.patch(MOCK_PATH, **mock_config) def test_floatingip_associate_port_with_net_port_dns(self, mock_args): cfg.CONF.set_override('dns_domain', self.DNS_DOMAIN) with self._create_floatingip_with_dns_on_update( net_dns_domain=self.DNS_DOMAIN, port_dns_name=self.DNS_NAME) as flip: floatingip = flip self._assert_recordset_created(floatingip['floating_ip_address']) @mock.patch(MOCK_PATH, **mock_config) def test_floatingip_associate_port_with_flip_and_net_port_dns(self, mock_args): # If both network+port and the floating ip have dns domain and # dns name, floating ip's information should take priority cfg.CONF.set_override('dns_domain', self.DNS_DOMAIN) with self._create_floatingip_with_dns_on_update( net_dns_domain='junkdomain.org.', port_dns_name='junk', flip_dns_domain=self.DNS_DOMAIN, flip_dns_name=self.DNS_NAME) as flip: floatingip = flip self._assert_recordset_created(floatingip['floating_ip_address']) self.assertEqual(self.DNS_DOMAIN, floatingip['dns_domain']) self.assertEqual(self.DNS_NAME, floatingip['dns_name']) @mock.patch(MOCK_PATH, **mock_config) def test_floatingip_disassociate_port(self, mock_args): cfg.CONF.set_override('dns_domain', self.DNS_DOMAIN) with self._create_floatingip_with_dns(net_dns_domain=self.DNS_DOMAIN, port_dns_name=self.DNS_NAME, assoc_port=True) as flip: fake_recordset = {'id': '', 'records': [flip['floating_ip_address']]} # This method is called during recordset deletion, which # will fail unless the list function call returns something like # this fake value self.mock_client.recordsets.list.return_value = ([fake_recordset]) # Port gets disassociated if port_id is not in the request body data = {'floatingip': {}} req = self.new_update_request('floatingips', data, flip['id']) res = req.get_response(self._api_for_resource('floatingip')) floatingip = self.deserialize(self.fmt, res)['floatingip'] flip_port_id = floatingip['port_id'] self.assertEqual(200, res.status_code) self.assertIsNone(flip_port_id) in_addr_name, in_addr_zone_name = self._get_in_addr( floatingip['floating_ip_address']) self.mock_client.recordsets.delete.assert_called_with( self.DNS_DOMAIN, '') self.mock_admin_client.recordsets.delete.assert_called_with( in_addr_zone_name, in_addr_name) @mock.patch(MOCK_PATH, **mock_config) def test_floatingip_delete(self, mock_args): cfg.CONF.set_override('dns_domain', self.DNS_DOMAIN) with self._create_floatingip_with_dns(flip_dns_domain=self.DNS_DOMAIN, flip_dns_name=self.DNS_NAME) as flip: floatingip = flip # This method is called during recordset deletion, which will # fail unless the list function call returns something like # this fake value fake_recordset = {'id': '', 'records': [floatingip['floating_ip_address']]} self.mock_client.recordsets.list.return_value = [fake_recordset] in_addr_name, in_addr_zone_name = self._get_in_addr( floatingip['floating_ip_address']) self.mock_client.recordsets.delete.assert_called_with( self.DNS_DOMAIN, '') self.mock_admin_client.recordsets.delete.assert_called_with( in_addr_zone_name, in_addr_name) @mock.patch(MOCK_PATH, **mock_config) def test_floatingip_no_PTR_record(self, mock_args): cfg.CONF.set_override('dns_domain', self.DNS_DOMAIN) # Disabling this option should stop the admin client from creating # PTR records. So set this option and make sure the admin client # wasn't called to create any records cfg.CONF.set_override('allow_reverse_dns_lookup', False, group='designate') with self._create_floatingip_with_dns(flip_dns_domain=self.DNS_DOMAIN, flip_dns_name=self.DNS_NAME) as flip: floatingip = flip self.mock_client.recordsets.create.assert_called_with(self.DNS_DOMAIN, self.DNS_NAME, 'A', [floatingip['floating_ip_address']]) self.mock_admin_client.recordsets.create.assert_not_called() self.assertEqual(self.DNS_DOMAIN, floatingip['dns_domain']) self.assertEqual(self.DNS_NAME, floatingip['dns_name']) neutron-12.1.1/neutron/tests/unit/extensions/test_portsecurity.py0000664000175000017500000004675013553660047025534 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import copy from neutron_lib.api.definitions import port_security as psec from neutron_lib.api import validators from neutron_lib import context from neutron_lib.exceptions import port_security as psec_exc from neutron_lib.plugins import directory from webob import exc from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.db import db_base_plugin_v2 from neutron.db import portsecurity_db from neutron.db import securitygroups_db from neutron.extensions import securitygroup as ext_sg from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit.extensions import test_securitygroup DB_PLUGIN_KLASS = ('neutron.tests.unit.extensions.test_portsecurity.' 'PortSecurityTestPlugin') class PortSecurityTestCase( test_securitygroup.SecurityGroupsTestCase, test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def setUp(self, plugin=None): self._backup = copy.deepcopy(ext_sg.RESOURCE_ATTRIBUTE_MAP) self.addCleanup(self._restore) ext_mgr = ( test_securitygroup.SecurityGroupTestExtensionManager()) super(PortSecurityTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr) # Check if a plugin supports security groups plugin_obj = directory.get_plugin() self._skip_security_group = ('security-group' not in plugin_obj.supported_extension_aliases) def _restore(self): ext_sg.RESOURCE_ATTRIBUTE_MAP = self._backup class PortSecurityTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, securitygroups_db.SecurityGroupDbMixin, portsecurity_db.PortSecurityDbMixin): """Test plugin that implements necessary calls on create/delete port for associating ports with security groups and port security. """ supported_extension_aliases = ["security-group", "port-security"] def create_network(self, context, network): tenant_id = network['network'].get('tenant_id') self._ensure_default_security_group(context, tenant_id) with db_api.context_manager.writer.using(context): neutron_db = super(PortSecurityTestPlugin, self).create_network( context, network) neutron_db.update(network['network']) self._process_network_port_security_create( context, network['network'], neutron_db) return neutron_db def update_network(self, context, id, network): with db_api.context_manager.writer.using(context): neutron_db = super(PortSecurityTestPlugin, self).update_network( context, id, network) if psec.PORTSECURITY in network['network']: self._process_network_port_security_update( context, network['network'], neutron_db) return neutron_db def get_network(self, context, id, fields=None): with db_api.context_manager.reader.using(context): net = super(PortSecurityTestPlugin, self).get_network( context, id) return db_utils.resource_fields(net, fields) def create_port(self, context, port): p = port['port'] p[ext_sg.SECURITYGROUPS] = self._get_security_groups_on_port( context, port) neutron_db = super(PortSecurityTestPlugin, self).create_port( context, port) p.update(neutron_db) (port_security, has_ip) = self._determine_port_security_and_has_ip( context, p) p[psec.PORTSECURITY] = port_security self._process_port_port_security_create(context, p, neutron_db) if (validators.is_attr_set(p.get(ext_sg.SECURITYGROUPS)) and not (port_security and has_ip)): raise psec_exc.PortSecurityAndIPRequiredForSecurityGroups() # Port requires ip and port_security enabled for security group if has_ip and port_security: self._ensure_default_security_group_on_port(context, port) if (p.get(ext_sg.SECURITYGROUPS) and p[psec.PORTSECURITY]): self._process_port_create_security_group( context, p, p[ext_sg.SECURITYGROUPS]) return port['port'] def update_port(self, context, id, port): delete_security_groups = self._check_update_deletes_security_groups( port) has_security_groups = self._check_update_has_security_groups(port) with db_api.context_manager.writer.using(context): ret_port = super(PortSecurityTestPlugin, self).update_port( context, id, port) # copy values over - but not fixed_ips port['port'].pop('fixed_ips', None) ret_port.update(port['port']) # populate port_security setting if psec.PORTSECURITY not in ret_port: ret_port[psec.PORTSECURITY] = self._get_port_security_binding( context, id) has_ip = self._ip_on_port(ret_port) # checks if security groups were updated adding/modifying # security groups, port security is set and port has ip if (has_security_groups and (not ret_port[psec.PORTSECURITY] or not has_ip)): raise psec_exc.PortSecurityAndIPRequiredForSecurityGroups() # Port security/IP was updated off. Need to check that no security # groups are on port. if ret_port[psec.PORTSECURITY] is not True or not has_ip: if has_security_groups: raise psec_exc.PortSecurityAndIPRequiredForSecurityGroups() # get security groups on port filters = {'port_id': [id]} security_groups = (super(PortSecurityTestPlugin, self). _get_port_security_group_bindings( context, filters)) if security_groups and not delete_security_groups: raise psec_exc.PortSecurityPortHasSecurityGroup() if (delete_security_groups or has_security_groups): # delete the port binding and read it with the new rules. self._delete_port_security_group_bindings(context, id) sgids = self._get_security_groups_on_port(context, port) # process port create sec groups needs port id port['id'] = id self._process_port_create_security_group(context, ret_port, sgids) if psec.PORTSECURITY in port['port']: self._process_port_port_security_update( context, port['port'], ret_port) return ret_port class PortSecurityDBTestCase(PortSecurityTestCase): def setUp(self, plugin=None, service_plugins=None): plugin = plugin or DB_PLUGIN_KLASS super(PortSecurityDBTestCase, self).setUp(plugin) class TestPortSecurity(PortSecurityDBTestCase): def test_create_network_with_portsecurity_mac(self): res = self._create_network('json', 'net1', True) net = self.deserialize('json', res) self.assertTrue(net['network'][psec.PORTSECURITY]) def test_create_network_with_portsecurity_false(self): res = self._create_network('json', 'net1', True, arg_list=('port_security_enabled',), port_security_enabled=False) net = self.deserialize('json', res) self.assertFalse(net['network'][psec.PORTSECURITY]) def test_updating_network_port_security(self): res = self._create_network('json', 'net1', True, port_security_enabled='True') net = self.deserialize('json', res) self.assertTrue(net['network'][psec.PORTSECURITY]) update_net = {'network': {psec.PORTSECURITY: False}} req = self.new_update_request('networks', update_net, net['network']['id']) net = self.deserialize('json', req.get_response(self.api)) self.assertFalse(net['network'][psec.PORTSECURITY]) req = self.new_show_request('networks', net['network']['id']) net = self.deserialize('json', req.get_response(self.api)) self.assertFalse(net['network'][psec.PORTSECURITY]) def test_create_port_default_true(self): with self.network() as net: res = self._create_port('json', net['network']['id']) port = self.deserialize('json', res) self.assertTrue(port['port'][psec.PORTSECURITY]) self._delete('ports', port['port']['id']) def test_create_port_passing_true(self): res = self._create_network('json', 'net1', True, arg_list=('port_security_enabled',), port_security_enabled=True) net = self.deserialize('json', res) res = self._create_port('json', net['network']['id']) port = self.deserialize('json', res) self.assertTrue(port['port'][psec.PORTSECURITY]) self._delete('ports', port['port']['id']) def test_create_port_on_port_security_false_network(self): res = self._create_network('json', 'net1', True, arg_list=('port_security_enabled',), port_security_enabled=False) net = self.deserialize('json', res) res = self._create_port('json', net['network']['id']) port = self.deserialize('json', res) self.assertFalse(port['port'][psec.PORTSECURITY]) self._delete('ports', port['port']['id']) def test_create_port_security_overrides_network_value(self): res = self._create_network('json', 'net1', True, arg_list=('port_security_enabled',), port_security_enabled=False) net = self.deserialize('json', res) res = self._create_port('json', net['network']['id'], arg_list=('port_security_enabled',), port_security_enabled=True) port = self.deserialize('json', res) self.assertTrue(port['port'][psec.PORTSECURITY]) self._delete('ports', port['port']['id']) def test_create_port_fails_with_secgroup_and_port_security_false(self): if self._skip_security_group: self.skipTest("Plugin does not support security groups") with self.network() as net: with self.subnet(network=net): security_group = self.deserialize( 'json', self._create_security_group(self.fmt, 'asdf', 'asdf')) security_group_id = security_group['security_group']['id'] res = self._create_port('json', net['network']['id'], arg_list=('security_groups', 'port_security_enabled'), security_groups=[security_group_id], port_security_enabled=False) self.assertEqual(400, res.status_int) def test_create_port_with_default_security_group(self): if self._skip_security_group: self.skipTest("Plugin does not support security groups") with self.network() as net: with self.subnet(network=net): res = self._create_port('json', net['network']['id']) port = self.deserialize('json', res) self.assertTrue(port['port'][psec.PORTSECURITY]) self.assertEqual(1, len(port['port'][ext_sg.SECURITYGROUPS])) self._delete('ports', port['port']['id']) def test_create_port_with_security_group_and_net_sec_false(self): # This tests that port_security_enabled is true when creating # a port on a network that is marked as port_security_enabled=False # that has a subnet and security_groups are passed it. if self._skip_security_group: self.skipTest("Plugin does not support security groups") res = self._create_network('json', 'net1', True, arg_list=('port_security_enabled',), port_security_enabled=False) net = self.deserialize('json', res) self._create_subnet('json', net['network']['id'], '10.0.0.0/24') security_group = self.deserialize( 'json', self._create_security_group(self.fmt, 'asdf', 'asdf')) security_group_id = security_group['security_group']['id'] res = self._create_port('json', net['network']['id'], arg_list=('security_groups', 'port_security_enabled'), port_security_enabled=True, security_groups=[security_group_id]) port = self.deserialize('json', res) self.assertTrue(port['port'][psec.PORTSECURITY]) self.assertEqual(port['port']['security_groups'], [security_group_id]) self._delete('ports', port['port']['id']) def test_create_port_without_security_group_and_net_sec_false(self): res = self._create_network('json', 'net1', True, arg_list=('port_security_enabled',), port_security_enabled=False) net = self.deserialize('json', res) self._create_subnet('json', net['network']['id'], '10.0.0.0/24') res = self._create_port('json', net['network']['id']) port = self.deserialize('json', res) self.assertFalse(port['port'][psec.PORTSECURITY]) self._delete('ports', port['port']['id']) def test_update_port_security_off_with_security_group(self): if self._skip_security_group: self.skipTest("Plugin does not support security groups") with self.network() as net: with self.subnet(network=net): res = self._create_port('json', net['network']['id']) port = self.deserialize('json', res) self.assertTrue(port['port'][psec.PORTSECURITY]) update_port = {'port': {psec.PORTSECURITY: False}} req = self.new_update_request('ports', update_port, port['port']['id']) res = req.get_response(self.api) self.assertEqual(409, res.status_int) # remove security group on port update_port = {'port': {ext_sg.SECURITYGROUPS: None}} req = self.new_update_request('ports', update_port, port['port']['id']) self.deserialize('json', req.get_response(self.api)) self._delete('ports', port['port']['id']) def test_update_port_remove_port_security_security_group(self): if self._skip_security_group: self.skipTest("Plugin does not support security groups") with self.network() as net: with self.subnet(network=net): res = self._create_port('json', net['network']['id'], arg_list=('port_security_enabled',), port_security_enabled=True) port = self.deserialize('json', res) self.assertTrue(port['port'][psec.PORTSECURITY]) # remove security group on port update_port = {'port': {ext_sg.SECURITYGROUPS: None, psec.PORTSECURITY: False}} req = self.new_update_request('ports', update_port, port['port']['id']) port = self.deserialize('json', req.get_response(self.api)) self.assertFalse(port['port'][psec.PORTSECURITY]) self.assertEqual(0, len(port['port'][ext_sg.SECURITYGROUPS])) self._delete('ports', port['port']['id']) def test_update_port_remove_port_security_security_group_read(self): if self._skip_security_group: self.skipTest("Plugin does not support security groups") with self.network() as net: with self.subnet(network=net): res = self._create_port('json', net['network']['id'], arg_list=('port_security_enabled',), port_security_enabled=True) port = self.deserialize('json', res) self.assertTrue(port['port'][psec.PORTSECURITY]) # remove security group on port update_port = {'port': {ext_sg.SECURITYGROUPS: None, psec.PORTSECURITY: False}} req = self.new_update_request('ports', update_port, port['port']['id']) self.deserialize('json', req.get_response(self.api)) sg_id = port['port'][ext_sg.SECURITYGROUPS] update_port = {'port': {ext_sg.SECURITYGROUPS: [sg_id[0]], psec.PORTSECURITY: True}} req = self.new_update_request('ports', update_port, port['port']['id']) port = self.deserialize('json', req.get_response(self.api)) self.assertTrue(port['port'][psec.PORTSECURITY]) self.assertEqual(1, len(port['port'][ext_sg.SECURITYGROUPS])) self._delete('ports', port['port']['id']) def test_create_port_security_off_shared_network(self): with self.network(shared=True) as net: with self.subnet(network=net): res = self._create_port('json', net['network']['id'], arg_list=('port_security_enabled',), port_security_enabled=False, tenant_id='not_network_owner', set_context=True) self.deserialize('json', res) self.assertEqual(403, res.status_int) def test_update_port_security_off_shared_network(self): with self.network(shared=True) as net: with self.subnet(network=net): res = self._create_port('json', net['network']['id'], tenant_id='not_network_owner', set_context=True) port = self.deserialize('json', res) # remove security group on port update_port = {'port': {ext_sg.SECURITYGROUPS: None, psec.PORTSECURITY: False}} req = self.new_update_request('ports', update_port, port['port']['id']) req.environ['neutron.context'] = context.Context( '', 'not_network_owner') res = req.get_response(self.api) self.assertEqual(exc.HTTPForbidden.code, res.status_int) neutron-12.1.1/neutron/tests/unit/extensions/test_vlantransparent.py0000664000175000017500000001027713553660047026175 0ustar zuulzuul00000000000000# Copyright (c) 2015 Cisco Systems Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from webob import exc as web_exc from neutron.db import db_base_plugin_v2 from neutron.db import vlantransparent_db as vlt_db from neutron.extensions import vlantransparent as vlt from neutron import quota from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit import testlib_api class VlanTransparentExtensionManager(object): def get_resources(self): return [] def get_actions(self): return [] def get_request_extensions(self): return [] def get_extended_resources(self, version): return vlt.Vlantransparent().get_extended_resources(version) class VlanTransparentExtensionTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, vlt_db.Vlantransparent_db_mixin): """Test plugin to mixin the VLAN transparent extensions.""" supported_extension_aliases = ["vlan-transparent"] def create_network(self, context, network): with context.session.begin(subtransactions=True): new_net = super(VlanTransparentExtensionTestPlugin, self).create_network(context, network) # Update the vlan_transparent in the database n = network['network'] vlan_transparent = vlt.get_vlan_transparent(n) network = self._get_network(context, new_net['id']) n['vlan_transparent'] = vlan_transparent network.update(n) return new_net class VlanTransparentExtensionTestCase(test_db_base_plugin_v2.TestNetworksV2): fmt = 'json' def setUp(self): plugin = ('neutron.tests.unit.extensions.test_vlantransparent.' 'VlanTransparentExtensionTestPlugin') # Update the plugin and extensions path ext_mgr = VlanTransparentExtensionManager() super(VlanTransparentExtensionTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr) quota.QUOTAS._driver = None cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', group='QUOTAS') def test_network_create_with_vlan_transparent_attr(self): vlantrans = {'vlan_transparent': True} with self.network(name='net1', **vlantrans) as net: req = self.new_show_request('networks', net['network']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(net['network']['name'], res['network']['name']) self.assertTrue(res['network'][vlt.VLANTRANSPARENT]) def test_network_create_with_bad_vlan_transparent_attr(self): vlantrans = {'vlan_transparent': "abc"} with testlib_api.ExpectedException( web_exc.HTTPClientError) as ctx_manager: with self.network(name='net1', **vlantrans): pass self.assertEqual(web_exc.HTTPClientError.code, ctx_manager.exception.code) def test_network_update_with_vlan_transparent_exception(self): with self.network(name='net1') as net: self._update('networks', net['network']['id'], {'network': {vlt.VLANTRANSPARENT: False}}, web_exc.HTTPBadRequest.code) req = self.new_show_request('networks', net['network']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(net['network']['name'], res['network']['name']) self.assertFalse(res['network'][vlt.VLANTRANSPARENT]) neutron-12.1.1/neutron/tests/unit/extensions/extendedattribute.py0000664000175000017500000000312613553660046025432 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import extensions EXTENDED_ATTRIBUTE = 'extended_attribute' EXTENDED_ATTRIBUTES_2_0 = { 'ext_test_resources': { EXTENDED_ATTRIBUTE: {'allow_post': True, 'allow_put': False, 'validate': {'type:uuid_or_none': None}, 'default': None, 'is_visible': True}, } } class Extendedattribute(extensions.ExtensionDescriptor): """Extension class supporting extended attribute for router.""" @classmethod def get_name(cls): return "Extended Extension Attributes" @classmethod def get_alias(cls): return "extended-ext-attr" @classmethod def get_description(cls): return "Provides extended_attr attribute to router" @classmethod def get_updated(cls): return "2013-02-05T00:00:00-00:00" def get_extended_resources(self, version): if version == "2.0": return EXTENDED_ATTRIBUTES_2_0 else: return {} neutron-12.1.1/neutron/tests/unit/extensions/extensionattribute.py0000664000175000017500000000625213553660046025651 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.api import extensions as api_extensions from neutron_lib.plugins import directory from neutron.api import extensions from neutron.api.v2 import base from neutron.quota import resource_registry # Attribute Map RESOURCE_ATTRIBUTE_MAP = { 'ext_test_resources': { 'id': {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True}, 'name': {'allow_post': True, 'allow_put': True, 'validate': {'type:string': None}, 'is_visible': True, 'default': ''}, 'tenant_id': {'allow_post': True, 'allow_put': False, 'required_by_policy': True, 'validate': {'type:string': None}, 'is_visible': True}, } } class Extensionattribute(api_extensions.ExtensionDescriptor): @classmethod def get_name(cls): return "Extension Test Resource" @classmethod def get_alias(cls): return "ext-obj-test" @classmethod def get_description(cls): return "Extension Test Resource" @classmethod def get_updated(cls): return "2013-02-05T10:00:00-00:00" def update_attributes_map(self, attributes): super(Extensionattribute, self).update_attributes_map( attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP) @classmethod def get_resources(cls): """Returns Ext Resources.""" exts = [] plugin = directory.get_plugin() resource_name = 'ext_test_resource' collection_name = resource_name + "s" params = RESOURCE_ATTRIBUTE_MAP.get(collection_name, dict()) resource_registry.register_resource_by_name(resource_name) controller = base.create_resource(collection_name, resource_name, plugin, params, member_actions={}) ex = extensions.ResourceExtension(collection_name, controller, member_actions={}) exts.append(ex) return exts def get_extended_resources(self, version): if version == "2.0": return RESOURCE_ATTRIBUTE_MAP else: return {} class ExtensionObjectTestPluginBase(object): @abc.abstractmethod def create_ext_test_resource(self, context, router): pass @abc.abstractmethod def get_ext_test_resource(self, context, id, fields=None): pass neutron-12.1.1/neutron/tests/unit/extensions/test_extraroute.py0000664000175000017500000006210013553660047025145 0ustar zuulzuul00000000000000# Copyright 2013, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import extraroute as xroute_apidef from neutron_lib import constants from neutron_lib import context from neutron_lib.utils import helpers from oslo_config import cfg from oslo_utils import uuidutils from webob import exc from neutron.db import extraroute_db from neutron.extensions import l3 from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit.extensions import test_l3 _uuid = uuidutils.generate_uuid _get_path = test_base._get_path class ExtraRouteTestExtensionManager(object): def get_resources(self): return l3.L3.get_resources() def get_actions(self): return [] def get_request_extensions(self): return [] # This plugin class is for tests with plugin that integrates L3. class TestExtraRouteIntPlugin(test_l3.TestL3NatIntPlugin, extraroute_db.ExtraRoute_db_mixin): supported_extension_aliases = ["external-net", "router", xroute_apidef.ALIAS] # A fake l3 service plugin class with extra route capability for # plugins that delegate away L3 routing functionality class TestExtraRouteL3NatServicePlugin(test_l3.TestL3NatServicePlugin, extraroute_db.ExtraRoute_db_mixin): supported_extension_aliases = ["router", xroute_apidef.ALIAS] class ExtraRouteDBTestCaseBase(object): def _routes_update_prepare( self, router_id, subnet_id, port_id, routes, skip_add=False, tenant_id=None): if not skip_add: self._router_interface_action( 'add', router_id, subnet_id, port_id, tenant_id=None) ctxt = context.Context('', tenant_id) if tenant_id else None self._update('routers', router_id, {'router': {'routes': routes}}, neutron_context=ctxt) return self._show('routers', router_id) def _routes_update_cleanup(self, port_id, subnet_id, router_id, routes): self._update('routers', router_id, {'router': {'routes': routes}}) self._router_interface_action('remove', router_id, subnet_id, port_id) def test_route_update_with_one_route(self): routes = [{'destination': '135.207.0.0/16', 'nexthop': '10.0.1.3'}] with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: fixed_ip_data = [{'ip_address': '10.0.1.2'}] with self.port(subnet=s, fixed_ips=fixed_ip_data) as p: body = self._routes_update_prepare(r['router']['id'], None, p['port']['id'], routes) self.assertEqual(routes, body['router']['routes']) self._routes_update_cleanup(p['port']['id'], None, r['router']['id'], []) def test_route_update_with_external_route(self): my_tenant = 'tenant1' with self.subnet(cidr='10.0.1.0/24', tenant_id='notme') as ext_subnet,\ self.port(subnet=ext_subnet) as nexthop_port: nexthop_ip = nexthop_port['port']['fixed_ips'][0]['ip_address'] routes = [{'destination': '135.207.0.0/16', 'nexthop': nexthop_ip}] self._set_net_external(ext_subnet['subnet']['network_id']) ext_info = {'network_id': ext_subnet['subnet']['network_id']} with self.router( external_gateway_info=ext_info, tenant_id=my_tenant) as r: body = self._routes_update_prepare( r['router']['id'], None, None, routes, skip_add=True, tenant_id=my_tenant) self.assertEqual(routes, body['router']['routes']) def test_route_update_with_route_via_another_tenant_subnet(self): my_tenant = 'tenant1' with self.subnet(cidr='10.0.1.0/24', tenant_id='notme') as subnet,\ self.port(subnet=subnet) as nexthop_port: nexthop_ip = nexthop_port['port']['fixed_ips'][0]['ip_address'] routes = [{'destination': '135.207.0.0/16', 'nexthop': nexthop_ip}] with self.router(tenant_id=my_tenant) as r: body = self._routes_update_prepare( r['router']['id'], subnet['subnet']['id'], None, routes, tenant_id=my_tenant) self.assertEqual(routes, body['router']['routes']) def test_route_clear_routes_with_None(self): routes = [{'destination': '135.207.0.0/16', 'nexthop': '10.0.1.3'}, {'destination': '12.0.0.0/8', 'nexthop': '10.0.1.4'}, {'destination': '141.212.0.0/16', 'nexthop': '10.0.1.5'}] with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: fixed_ip_data = [{'ip_address': '10.0.1.2'}] with self.port(subnet=s, fixed_ips=fixed_ip_data) as p: self._routes_update_prepare(r['router']['id'], None, p['port']['id'], routes) body = self._update('routers', r['router']['id'], {'router': {'routes': None}}) self.assertEqual([], body['router']['routes']) self._routes_update_cleanup(p['port']['id'], None, r['router']['id'], []) def test_router_interface_in_use_by_route(self): routes = [{'destination': '135.207.0.0/16', 'nexthop': '10.0.1.3'}] with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: fixed_ip_data = [{'ip_address': '10.0.1.2'}] with self.port(subnet=s, fixed_ips=fixed_ip_data) as p: body = self._routes_update_prepare(r['router']['id'], None, p['port']['id'], routes) self.assertEqual(routes, body['router']['routes']) self._router_interface_action( 'remove', r['router']['id'], None, p['port']['id'], expected_code=exc.HTTPConflict.code) self._routes_update_cleanup(p['port']['id'], None, r['router']['id'], []) def test_route_update_with_multi_routes(self): routes = [{'destination': '135.207.0.0/16', 'nexthop': '10.0.1.3'}, {'destination': '12.0.0.0/8', 'nexthop': '10.0.1.4'}, {'destination': '141.212.0.0/16', 'nexthop': '10.0.1.5'}] with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: fixed_ip_data = [{'ip_address': '10.0.1.2'}] with self.port(subnet=s, fixed_ips=fixed_ip_data) as p: body = self._routes_update_prepare(r['router']['id'], None, p['port']['id'], routes) self.assertEqual( sorted(body['router']['routes'], key=helpers.safe_sort_key), sorted(routes, key=helpers.safe_sort_key)) self._routes_update_cleanup(p['port']['id'], None, r['router']['id'], []) def test_routes_update_for_multiple_routers(self): with self.router() as r1,\ self.router() as r2,\ self.subnet(cidr='10.0.0.0/24') as s: with self.port(subnet=s) as p1,\ self.port(subnet=s) as p2: p1_ip = p1['port']['fixed_ips'][0]['ip_address'] p2_ip = p2['port']['fixed_ips'][0]['ip_address'] routes1 = [{'destination': '135.207.0.0/16', 'nexthop': p2_ip}] routes2 = [{'destination': '12.0.0.0/8', 'nexthop': p1_ip}] body = self._routes_update_prepare(r1['router']['id'], None, p1['port']['id'], routes1) self.assertEqual(routes1, body['router']['routes']) body = self._routes_update_prepare(r2['router']['id'], None, p2['port']['id'], routes2) self.assertEqual(routes2, body['router']['routes']) self._routes_update_cleanup(p1['port']['id'], None, r1['router']['id'], []) self._routes_update_cleanup(p2['port']['id'], None, r2['router']['id'], []) def test_router_update_delete_routes(self): routes_orig = [{'destination': '135.207.0.0/16', 'nexthop': '10.0.1.3'}, {'destination': '12.0.0.0/8', 'nexthop': '10.0.1.4'}, {'destination': '141.212.0.0/16', 'nexthop': '10.0.1.5'}] routes_left = [{'destination': '135.207.0.0/16', 'nexthop': '10.0.1.3'}, {'destination': '141.212.0.0/16', 'nexthop': '10.0.1.5'}] with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: fixed_ip_data = [{'ip_address': '10.0.1.2'}] with self.port(subnet=s, fixed_ips=fixed_ip_data) as p: body = self._routes_update_prepare(r['router']['id'], None, p['port']['id'], routes_orig) self.assertEqual( sorted(body['router']['routes'], key=helpers.safe_sort_key), sorted(routes_orig, key=helpers.safe_sort_key)) body = self._routes_update_prepare(r['router']['id'], None, p['port']['id'], routes_left, skip_add=True) self.assertEqual( sorted(body['router']['routes'], key=helpers.safe_sort_key), sorted(routes_left, key=helpers.safe_sort_key)) self._routes_update_cleanup(p['port']['id'], None, r['router']['id'], []) def _test_malformed_route(self, routes): with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: with self.port(subnet=s) as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) self._update('routers', r['router']['id'], {'router': {'routes': routes}}, expected_code=exc.HTTPBadRequest.code) # clean-up self._router_interface_action('remove', r['router']['id'], None, p['port']['id']) def test_no_destination_route(self): self._test_malformed_route([{'nexthop': '10.0.1.6'}]) def test_no_nexthop_route(self): self._test_malformed_route({'destination': '135.207.0.0/16'}) def test_none_destination(self): self._test_malformed_route([{'destination': None, 'nexthop': '10.0.1.3'}]) def test_none_nexthop(self): self._test_malformed_route([{'destination': '135.207.0.0/16', 'nexthop': None}]) def test_nexthop_is_port_ip(self): with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: with self.port(subnet=s) as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) port_ip = p['port']['fixed_ips'][0]['ip_address'] routes = [{'destination': '135.207.0.0/16', 'nexthop': port_ip}] self._update('routers', r['router']['id'], {'router': {'routes': routes}}, expected_code=exc.HTTPBadRequest.code) # clean-up self._router_interface_action('remove', r['router']['id'], None, p['port']['id']) def test_router_update_with_too_many_routes(self): with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: with self.port(subnet=s) as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) routes = [{'destination': '135.207.0.0/16', 'nexthop': '10.0.1.3'}, {'destination': '12.0.0.0/8', 'nexthop': '10.0.1.4'}, {'destination': '141.212.0.0/16', 'nexthop': '10.0.1.5'}, {'destination': '192.168.0.0/16', 'nexthop': '10.0.1.6'}] self._update('routers', r['router']['id'], {'router': {'routes': routes}}, expected_code=exc.HTTPBadRequest.code) # clean-up self._router_interface_action('remove', r['router']['id'], None, p['port']['id']) def test_router_update_with_dup_address(self): with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: with self.port(subnet=s) as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) routes = [{'destination': '135.207.0.0/16', 'nexthop': '10.0.1.3'}, {'destination': '135.207.0.0/16', 'nexthop': '10.0.1.3'}] self._update('routers', r['router']['id'], {'router': {'routes': routes}}, expected_code=exc.HTTPBadRequest.code) # clean-up self._router_interface_action('remove', r['router']['id'], None, p['port']['id']) def test_router_update_with_invalid_ip_address(self): with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: with self.port(subnet=s) as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) routes = [{'destination': '512.207.0.0/16', 'nexthop': '10.0.1.3'}] self._update('routers', r['router']['id'], {'router': {'routes': routes}}, expected_code=exc.HTTPBadRequest.code) routes = [{'destination': '127.207.0.0/48', 'nexthop': '10.0.1.3'}] self._update('routers', r['router']['id'], {'router': {'routes': routes}}, expected_code=exc.HTTPBadRequest.code) routes = [{'destination': 'invalid_ip_address', 'nexthop': '10.0.1.3'}] self._update('routers', r['router']['id'], {'router': {'routes': routes}}, expected_code=exc.HTTPBadRequest.code) # clean-up self._router_interface_action('remove', r['router']['id'], None, p['port']['id']) def test_router_update_with_invalid_nexthop_ip(self): with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: with self.port(subnet=s) as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) routes = [{'destination': '127.207.0.0/16', 'nexthop': ' 300.10.10.4'}] self._update('routers', r['router']['id'], {'router': {'routes': routes}}, expected_code=exc.HTTPBadRequest.code) # clean-up self._router_interface_action('remove', r['router']['id'], None, p['port']['id']) def test_router_update_with_nexthop_is_outside_port_subnet(self): with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: with self.port(subnet=s) as p: self._router_interface_action('add', r['router']['id'], None, p['port']['id']) routes = [{'destination': '127.207.0.0/16', 'nexthop': ' 20.10.10.4'}] self._update('routers', r['router']['id'], {'router': {'routes': routes}}, expected_code=exc.HTTPBadRequest.code) # clean-up self._router_interface_action('remove', r['router']['id'], None, p['port']['id']) def test_router_update_on_external_port(self): with self.router() as r: with self.subnet(cidr='10.0.1.0/24') as s: self._set_net_external(s['subnet']['network_id']) self._add_external_gateway_to_router( r['router']['id'], s['subnet']['network_id']) body = self._show('routers', r['router']['id']) net_id = body['router']['external_gateway_info']['network_id'] self.assertEqual(net_id, s['subnet']['network_id']) port_res = self._list_ports( 'json', 200, s['subnet']['network_id'], tenant_id=r['router']['tenant_id'], device_owner=constants.DEVICE_OWNER_ROUTER_GW) port_list = self.deserialize('json', port_res) self.assertEqual(1, len(port_list['ports'])) with self.port(subnet=s) as p: next_hop = p['port']['fixed_ips'][0]['ip_address'] routes = [{'destination': '135.207.0.0/16', 'nexthop': next_hop}] body = self._update('routers', r['router']['id'], {'router': {'routes': routes}}) body = self._show('routers', r['router']['id']) self.assertEqual(routes, body['router']['routes']) self._remove_external_gateway_from_router( r['router']['id'], s['subnet']['network_id']) body = self._show('routers', r['router']['id']) gw_info = body['router']['external_gateway_info'] self.assertIsNone(gw_info) def test_router_list_with_sort(self): with self.router(name='router1') as router1,\ self.router(name='router2') as router2,\ self.router(name='router3') as router3: self._test_list_with_sort('router', (router3, router2, router1), [('name', 'desc')]) def test_router_list_with_pagination(self): with self.router(name='router1') as router1,\ self.router(name='router2') as router2,\ self.router(name='router3') as router3: self._test_list_with_pagination('router', (router1, router2, router3), ('name', 'asc'), 2, 2) def test_router_list_with_pagination_reverse(self): with self.router(name='router1') as router1,\ self.router(name='router2') as router2,\ self.router(name='router3') as router3: self._test_list_with_pagination_reverse('router', (router1, router2, router3), ('name', 'asc'), 2, 2) class ExtraRouteDBIntTestCase(test_l3.L3NatDBIntTestCase, ExtraRouteDBTestCaseBase): def setUp(self, plugin=None, ext_mgr=None): if not plugin: plugin = ('neutron.tests.unit.extensions.test_extraroute.' 'TestExtraRouteIntPlugin') # for these tests we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) cfg.CONF.set_default('max_routes', 3) ext_mgr = ExtraRouteTestExtensionManager() super(test_l3.L3BaseForIntTests, self).setUp(plugin=plugin, ext_mgr=ext_mgr) self.setup_notification_driver() class ExtraRouteDBSepTestCase(test_l3.L3NatDBSepTestCase, ExtraRouteDBTestCaseBase): def setUp(self): # the plugin without L3 support plugin = 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin' # the L3 service plugin l3_plugin = ('neutron.tests.unit.extensions.test_extraroute.' 'TestExtraRouteL3NatServicePlugin') service_plugins = {'l3_plugin_name': l3_plugin} # for these tests we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) cfg.CONF.set_default('max_routes', 3) ext_mgr = ExtraRouteTestExtensionManager() super(test_l3.L3BaseForSepTests, self).setUp( plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) self.setup_notification_driver() neutron-12.1.1/neutron/tests/unit/extensions/__init__.py0000664000175000017500000000000013553660046023431 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/extensions/test_flavors.py0000664000175000017500000007544713553660047024441 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import copy import fixtures import mock from neutron_lib import context from neutron_lib.db import constants as db_const from neutron_lib.exceptions import flavors as flav_exc from neutron_lib.plugins import constants from oslo_config import cfg from oslo_utils import uuidutils from webob import exc from neutron.db import api as dbapi from neutron.db.models import l3 as l3_models from neutron.db import servicetype_db from neutron.extensions import flavors from neutron.objects import flavor as flavor_obj from neutron.services.flavors import flavors_plugin from neutron.services import provider_configuration as provconf from neutron.tests import base from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit import dummy_plugin from neutron.tests.unit.extensions import base as extension _uuid = uuidutils.generate_uuid _get_path = test_base._get_path _driver = ('neutron.tests.unit.extensions.test_flavors.' 'DummyServiceDriver') _provider = dummy_plugin.RESOURCE_NAME _long_name = 'x' * (db_const.NAME_FIELD_SIZE + 1) _long_description = 'x' * (db_const.LONG_DESCRIPTION_FIELD_SIZE + 1) class FlavorExtensionTestCase(extension.ExtensionTestCase): def setUp(self): super(FlavorExtensionTestCase, self).setUp() self._setUpExtension( 'neutron.services.flavors.flavors_plugin.FlavorsPlugin', constants.FLAVORS, {}, flavors.Flavors, '', supported_extension_aliases='flavors') def test_create_flavor(self): tenant_id = uuidutils.generate_uuid() # Use service_type FLAVORS since plugin must be loaded to validate data = {'flavor': {'name': 'GOLD', 'service_type': constants.FLAVORS, 'description': 'the best flavor', 'tenant_id': tenant_id, 'project_id': tenant_id, 'enabled': True}} expected = copy.deepcopy(data) expected['flavor']['service_profiles'] = [] instance = self.plugin.return_value instance.create_flavor.return_value = expected['flavor'] res = self.api.post(_get_path('flavors', fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt) instance.create_flavor.assert_called_with(mock.ANY, flavor=expected) res = self.deserialize(res) self.assertIn('flavor', res) self.assertEqual(expected, res) def test_create_flavor_invalid_service_type(self): tenant_id = uuidutils.generate_uuid() data = {'flavor': {'name': 'GOLD', 'service_type': 'BROKEN', 'description': 'the best flavor', 'tenant_id': tenant_id, 'enabled': True}} self.api.post(_get_path('flavors', fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) def test_create_flavor_too_long_name(self): tenant_id = uuidutils.generate_uuid() data = {'flavor': {'name': _long_name, 'service_type': constants.FLAVORS, 'description': 'the best flavor', 'tenant_id': tenant_id, 'enabled': True}} self.api.post(_get_path('flavors', fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) def test_create_flavor_too_long_description(self): tenant_id = uuidutils.generate_uuid() data = {'flavor': {'name': _long_name, 'service_type': constants.FLAVORS, 'description': _long_description, 'tenant_id': tenant_id, 'enabled': True}} self.api.post(_get_path('flavors', fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) def test_create_flavor_invalid_enabled(self): tenant_id = uuidutils.generate_uuid() data = {'flavor': {'name': _long_name, 'service_type': constants.FLAVORS, 'description': 'the best flavor', 'tenant_id': tenant_id, 'enabled': 'BROKEN'}} self.api.post(_get_path('flavors', fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) def test_update_flavor(self): flavor_id = 'fake_id' data = {'flavor': {'name': 'GOLD', 'description': 'the best flavor', 'enabled': True}} expected = copy.copy(data) expected['flavor']['service_profiles'] = [] instance = self.plugin.return_value instance.update_flavor.return_value = expected['flavor'] res = self.api.put(_get_path('flavors', id=flavor_id, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt) instance.update_flavor.assert_called_with(mock.ANY, flavor_id, flavor=expected) res = self.deserialize(res) self.assertIn('flavor', res) self.assertEqual(expected, res) def test_update_flavor_too_long_name(self): flavor_id = 'fake_id' data = {'flavor': {'name': _long_name, 'description': 'the best flavor', 'enabled': True}} self.api.put(_get_path('flavors', id=flavor_id, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) def test_update_flavor_too_long_description(self): flavor_id = 'fake_id' data = {'flavor': {'name': 'GOLD', 'description': _long_description, 'enabled': True}} self.api.put(_get_path('flavors', id=flavor_id, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) def test_update_flavor_invalid_enabled(self): flavor_id = 'fake_id' data = {'flavor': {'name': 'GOLD', 'description': _long_description, 'enabled': 'BROKEN'}} self.api.put(_get_path('flavors', id=flavor_id, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) def test_delete_flavor(self): flavor_id = 'fake_id' instance = self.plugin.return_value self.api.delete(_get_path('flavors', id=flavor_id, fmt=self.fmt), content_type='application/%s' % self.fmt) instance.delete_flavor.assert_called_with(mock.ANY, flavor_id) def test_show_flavor(self): flavor_id = 'fake_id' expected = {'flavor': {'id': flavor_id, 'name': 'GOLD', 'description': 'the best flavor', 'enabled': True, 'service_profiles': ['profile-1']}} instance = self.plugin.return_value instance.get_flavor.return_value = expected['flavor'] res = self.api.get(_get_path('flavors', id=flavor_id, fmt=self.fmt)) instance.get_flavor.assert_called_with(mock.ANY, flavor_id, fields=mock.ANY) res = self.deserialize(res) self.assertEqual(expected, res) def test_get_flavors(self): data = {'flavors': [{'id': 'id1', 'name': 'GOLD', 'description': 'the best flavor', 'enabled': True, 'service_profiles': ['profile-1']}, {'id': 'id2', 'name': 'GOLD', 'description': 'the best flavor', 'enabled': True, 'service_profiles': ['profile-2', 'profile-1']}]} instance = self.plugin.return_value instance.get_flavors.return_value = data['flavors'] res = self.api.get(_get_path('flavors', fmt=self.fmt)) instance.get_flavors.assert_called_with(mock.ANY, fields=mock.ANY, filters=mock.ANY) res = self.deserialize(res) self.assertEqual(data, res) def test_create_service_profile(self): tenant_id = uuidutils.generate_uuid() expected = {'service_profile': {'description': 'the best sp', 'driver': '', 'tenant_id': tenant_id, 'project_id': tenant_id, 'enabled': True, 'metainfo': '{"data": "value"}'}} instance = self.plugin.return_value instance.create_service_profile.return_value = ( expected['service_profile']) res = self.api.post(_get_path('service_profiles', fmt=self.fmt), self.serialize(expected), content_type='application/%s' % self.fmt) instance.create_service_profile.assert_called_with( mock.ANY, service_profile=expected) res = self.deserialize(res) self.assertIn('service_profile', res) self.assertEqual(expected, res) def test_create_service_profile_too_long_description(self): tenant_id = uuidutils.generate_uuid() expected = {'service_profile': {'description': _long_description, 'driver': '', 'tenant_id': tenant_id, 'enabled': True, 'metainfo': '{"data": "value"}'}} self.api.post(_get_path('service_profiles', fmt=self.fmt), self.serialize(expected), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) def test_create_service_profile_too_long_driver(self): tenant_id = uuidutils.generate_uuid() expected = {'service_profile': {'description': 'the best sp', 'driver': _long_description, 'tenant_id': tenant_id, 'enabled': True, 'metainfo': '{"data": "value"}'}} self.api.post(_get_path('service_profiles', fmt=self.fmt), self.serialize(expected), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) def test_create_service_profile_invalid_enabled(self): tenant_id = uuidutils.generate_uuid() expected = {'service_profile': {'description': 'the best sp', 'driver': '', 'tenant_id': tenant_id, 'enabled': 'BROKEN', 'metainfo': '{"data": "value"}'}} self.api.post(_get_path('service_profiles', fmt=self.fmt), self.serialize(expected), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) def test_update_service_profile(self): sp_id = "fake_id" expected = {'service_profile': {'description': 'the best sp', 'enabled': False, 'metainfo': '{"data1": "value3"}'}} instance = self.plugin.return_value instance.update_service_profile.return_value = ( expected['service_profile']) res = self.api.put(_get_path('service_profiles', id=sp_id, fmt=self.fmt), self.serialize(expected), content_type='application/%s' % self.fmt) instance.update_service_profile.assert_called_with( mock.ANY, sp_id, service_profile=expected) res = self.deserialize(res) self.assertIn('service_profile', res) self.assertEqual(expected, res) def test_update_service_profile_too_long_description(self): sp_id = "fake_id" expected = {'service_profile': {'description': 'the best sp', 'enabled': 'BROKEN', 'metainfo': '{"data1": "value3"}'}} self.api.put(_get_path('service_profiles', id=sp_id, fmt=self.fmt), self.serialize(expected), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) def test_update_service_profile_invalid_enabled(self): sp_id = "fake_id" expected = {'service_profile': {'description': 'the best sp', 'enabled': 'BROKEN', 'metainfo': '{"data1": "value3"}'}} self.api.put(_get_path('service_profiles', id=sp_id, fmt=self.fmt), self.serialize(expected), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) def test_delete_service_profile(self): sp_id = 'fake_id' instance = self.plugin.return_value self.api.delete(_get_path('service_profiles', id=sp_id, fmt=self.fmt), content_type='application/%s' % self.fmt) instance.delete_service_profile.assert_called_with(mock.ANY, sp_id) def test_show_service_profile(self): sp_id = 'fake_id' expected = {'service_profile': {'id': 'id1', 'driver': _driver, 'description': 'desc', 'metainfo': '{}', 'enabled': True}} instance = self.plugin.return_value instance.get_service_profile.return_value = ( expected['service_profile']) res = self.api.get(_get_path('service_profiles', id=sp_id, fmt=self.fmt)) instance.get_service_profile.assert_called_with(mock.ANY, sp_id, fields=mock.ANY) res = self.deserialize(res) self.assertEqual(expected, res) def test_get_service_profiles(self): expected = {'service_profiles': [{'id': 'id1', 'driver': _driver, 'description': 'desc', 'metainfo': '{}', 'enabled': True}, {'id': 'id2', 'driver': _driver, 'description': 'desc', 'metainfo': '{}', 'enabled': True}]} instance = self.plugin.return_value instance.get_service_profiles.return_value = ( expected['service_profiles']) res = self.api.get(_get_path('service_profiles', fmt=self.fmt)) instance.get_service_profiles.assert_called_with(mock.ANY, fields=mock.ANY, filters=mock.ANY) res = self.deserialize(res) self.assertEqual(expected, res) def test_associate_service_profile_with_flavor(self): tenant_id = uuidutils.generate_uuid() expected = {'service_profile': {'id': _uuid(), 'tenant_id': tenant_id, 'project_id': tenant_id}} instance = self.plugin.return_value instance.create_flavor_service_profile.return_value = ( expected['service_profile']) res = self.api.post('/flavors/fl_id/service_profiles', self.serialize(expected), content_type='application/%s' % self.fmt) instance.create_flavor_service_profile.assert_called_with( mock.ANY, service_profile=expected, flavor_id='fl_id') res = self.deserialize(res) self.assertEqual(expected, res) def test_disassociate_service_profile_with_flavor(self): instance = self.plugin.return_value instance.delete_flavor_service_profile.return_value = None self.api.delete('/flavors/fl_id/service_profiles/%s' % 'fake_spid', content_type='application/%s' % self.fmt) instance.delete_flavor_service_profile.assert_called_with( mock.ANY, 'fake_spid', flavor_id='fl_id') def test_update_association_error(self): """Confirm that update is not permitted with user error.""" new_id = uuidutils.generate_uuid() data = {'service_profile': {'id': new_id}} self.api.put('/flavors/fl_id/service_profiles/%s' % 'fake_spid', self.serialize(data), content_type='application/%s' % self.fmt, status=exc.HTTPBadRequest.code) class DummyServicePlugin(object): def driver_loaded(self, driver, service_profile): pass @classmethod def get_plugin_type(cls): return dummy_plugin.DUMMY_SERVICE_TYPE def get_plugin_description(self): return "Dummy service plugin, aware of flavors" class DummyServiceDriver(object): @staticmethod def get_service_type(): return dummy_plugin.DUMMY_SERVICE_TYPE def __init__(self, plugin): pass class FlavorPluginTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase, base.PluginFixture): def setUp(self): super(FlavorPluginTestCase, self).setUp() self.config_parse() cfg.CONF.set_override( 'service_plugins', ['neutron.tests.unit.extensions.test_flavors.DummyServicePlugin']) self.useFixture( fixtures.MonkeyPatch('neutron.manager.NeutronManager._instance')) self.plugin = flavors_plugin.FlavorsPlugin() self.ctx = context.get_admin_context() providers = [DummyServiceDriver.get_service_type() + ":" + _provider + ":" + _driver] self.service_manager = servicetype_db.ServiceTypeManager.get_instance() self.service_providers = mock.patch.object( provconf.NeutronModule, 'service_providers').start() self.service_providers.return_value = providers for provider in providers: self.service_manager.add_provider_configuration( provider.split(':')[0], provconf.ProviderConfiguration()) dbapi.context_manager.writer.get_engine() def _create_flavor(self, description=None): flavor = {'flavor': {'name': 'GOLD', 'service_type': dummy_plugin.DUMMY_SERVICE_TYPE, 'description': description or 'the best flavor', 'enabled': True}} return self.plugin.create_flavor(self.ctx, flavor), flavor def test_create_flavor(self): self._create_flavor() res = flavor_obj.Flavor.get_objects(self.ctx) self.assertEqual(1, len(res)) self.assertEqual('GOLD', res[0]['name']) self.assertEqual( dummy_plugin.DUMMY_SERVICE_TYPE, res[0]['service_type']) def test_update_flavor(self): fl, flavor = self._create_flavor() flavor = {'flavor': {'name': 'Silver', 'enabled': False}} self.plugin.update_flavor(self.ctx, fl['id'], flavor) res = flavor_obj.Flavor.get_object(self.ctx, id=fl['id']) self.assertEqual('Silver', res['name']) self.assertFalse(res['enabled']) def test_delete_flavor(self): fl, _ = self._create_flavor() self.plugin.delete_flavor(self.ctx, fl['id']) self.assertFalse(flavor_obj.Flavor.objects_exist(self.ctx)) def test_show_flavor(self): fl, _ = self._create_flavor() show_fl = self.plugin.get_flavor(self.ctx, fl['id']) self.assertEqual(fl, show_fl) def test_get_flavors(self): fl, flavor = self._create_flavor() flavor['flavor']['name'] = 'SILVER' self.plugin.create_flavor(self.ctx, flavor) show_fl = self.plugin.get_flavors(self.ctx) self.assertEqual(2, len(show_fl)) def _create_service_profile(self, description=None): data = {'service_profile': {'description': description or 'the best sp', 'driver': _driver, 'enabled': True, 'metainfo': '{"data": "value"}'}} sp = self.plugin.create_service_profile(self.ctx, data) return sp, data def test_create_service_profile(self): sp, data = self._create_service_profile() res = flavor_obj.ServiceProfile.get_object(self.ctx, id=sp['id']) self.assertIsNotNone(res) self.assertEqual(data['service_profile']['driver'], res.driver) self.assertEqual(data['service_profile']['metainfo'], res.metainfo) def test_create_service_profile_empty_driver(self): data = {'service_profile': {'description': 'the best sp', 'driver': '', 'enabled': True, 'metainfo': '{"data": "value"}'}} sp = self.plugin.create_service_profile(self.ctx, data) res = flavor_obj.ServiceProfile.get_object(self.ctx, id=sp['id']) self.assertIsNotNone(res) self.assertEqual(data['service_profile']['driver'], res.driver) self.assertEqual(data['service_profile']['metainfo'], res.metainfo) def test_create_service_profile_invalid_driver(self): data = {'service_profile': {'description': 'the best sp', 'driver': "Broken", 'enabled': True, 'metainfo': '{"data": "value"}'}} self.assertRaises(flav_exc.ServiceProfileDriverNotFound, self.plugin.create_service_profile, self.ctx, data) def test_create_service_profile_invalid_empty(self): data = {'service_profile': {'description': '', 'driver': '', 'enabled': True, 'metainfo': ''}} self.assertRaises(flav_exc.ServiceProfileEmpty, self.plugin.create_service_profile, self.ctx, data) def test_update_service_profile(self): sp, data = self._create_service_profile() data['service_profile']['metainfo'] = '{"data": "value1"}' sp = self.plugin.update_service_profile(self.ctx, sp['id'], data) res = flavor_obj.ServiceProfile.get_object(self.ctx, id=sp['id']) self.assertEqual(data['service_profile']['metainfo'], res['metainfo']) def test_delete_service_profile(self): sp, data = self._create_service_profile() self.plugin.delete_service_profile(self.ctx, sp['id']) res = flavor_obj.ServiceProfile.get_objects(self.ctx) self.assertFalse(res) def test_show_service_profile(self): sp, data = self._create_service_profile() sp_show = self.plugin.get_service_profile(self.ctx, sp['id']) self.assertEqual(sp, sp_show) def test_get_service_profiles(self): self._create_service_profile() self._create_service_profile(description='another sp') self.assertEqual(2, len(self.plugin.get_service_profiles(self.ctx))) def test_associate_service_profile_with_flavor(self): sp, data = self._create_service_profile() fl, data = self._create_flavor() self.plugin.create_flavor_service_profile( self.ctx, {'service_profile': {'id': sp['id']}}, fl['id']) binding = flavor_obj.FlavorServiceProfileBinding.get_objects( self.ctx)[0] self.assertEqual(fl['id'], binding['flavor_id']) self.assertEqual(sp['id'], binding['service_profile_id']) res = self.plugin.get_flavor(self.ctx, fl['id']) self.assertEqual(1, len(res['service_profiles'])) self.assertEqual(sp['id'], res['service_profiles'][0]) res = self.plugin.get_service_profile(self.ctx, sp['id']) self.assertEqual(1, len(res['flavors'])) self.assertEqual(fl['id'], res['flavors'][0]) def test_autodelete_flavor_associations(self): sp, data = self._create_service_profile() fl, data = self._create_flavor() self.plugin.create_flavor_service_profile( self.ctx, {'service_profile': {'id': sp['id']}}, fl['id']) self.plugin.delete_flavor(self.ctx, fl['id']) self.assertFalse( flavor_obj.FlavorServiceProfileBinding.objects_exist(self.ctx)) def test_associate_service_profile_with_flavor_exists(self): sp, data = self._create_service_profile() fl, data = self._create_flavor() self.plugin.create_flavor_service_profile( self.ctx, {'service_profile': {'id': sp['id']}}, fl['id']) self.assertRaises(flav_exc.FlavorServiceProfileBindingExists, self.plugin.create_flavor_service_profile, self.ctx, {'service_profile': {'id': sp['id']}}, fl['id']) def test_disassociate_service_profile_with_flavor(self): sp, data = self._create_service_profile() fl, data = self._create_flavor() self.plugin.create_flavor_service_profile( self.ctx, {'service_profile': {'id': sp['id']}}, fl['id']) self.plugin.delete_flavor_service_profile( self.ctx, sp['id'], fl['id']) self.assertFalse( flavor_obj.FlavorServiceProfileBinding.objects_exist(self.ctx)) self.assertRaises( flav_exc.FlavorServiceProfileBindingNotFound, self.plugin.delete_flavor_service_profile, self.ctx, sp['id'], fl['id']) def test_delete_service_profile_in_use(self): sp, data = self._create_service_profile() fl, data = self._create_flavor() self.plugin.create_flavor_service_profile( self.ctx, {'service_profile': {'id': sp['id']}}, fl['id']) self.assertRaises( flav_exc.ServiceProfileInUse, self.plugin.delete_service_profile, self.ctx, sp['id']) def test_delete_flavor_in_use(self): # make use of router since it has a flavor id fl, data = self._create_flavor() with self.ctx.session.begin(): self.ctx.session.add(l3_models.Router(flavor_id=fl['id'])) self.assertRaises( flav_exc.FlavorInUse, self.plugin.delete_flavor, self.ctx, fl['id']) def test_get_flavor_next_provider_no_binding(self): fl, data = self._create_flavor() self.assertRaises( flav_exc.FlavorServiceProfileBindingNotFound, self.plugin.get_flavor_next_provider, self.ctx, fl['id']) def test_get_flavor_next_provider_disabled(self): data = {'service_profile': {'description': 'the best sp', 'driver': _driver, 'enabled': False, 'metainfo': '{"data": "value"}'}} sp = self.plugin.create_service_profile(self.ctx, data) fl, data = self._create_flavor() self.plugin.create_flavor_service_profile( self.ctx, {'service_profile': {'id': sp['id']}}, fl['id']) self.assertRaises( flav_exc.ServiceProfileDisabled, self.plugin.get_flavor_next_provider, self.ctx, fl['id']) def test_get_flavor_next_provider_no_driver(self): data = {'service_profile': {'description': 'the best sp', 'driver': '', 'enabled': True, 'metainfo': '{"data": "value"}'}} sp = self.plugin.create_service_profile(self.ctx, data) fl, data = self._create_flavor() self.plugin.create_flavor_service_profile( self.ctx, {'service_profile': {'id': sp['id']}}, fl['id']) self.assertRaises( flav_exc.ServiceProfileDriverNotFound, self.plugin.get_flavor_next_provider, self.ctx, fl['id']) def test_get_flavor_next_provider(self): sp, data = self._create_service_profile() fl, data = self._create_flavor() self.plugin.create_flavor_service_profile( self.ctx, {'service_profile': {'id': sp['id']}}, fl['id']) providers = self.plugin.get_flavor_next_provider( self.ctx, fl['id']) self.assertEqual(_provider, providers[0].get('provider', None)) neutron-12.1.1/neutron/tests/unit/extensions/test_quotasv2_detail.py0000664000175000017500000001532313553660046026055 0ustar zuulzuul00000000000000# Copyright 2017 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from neutron_lib import fixture from oslo_config import cfg import webtest from neutron.api import extensions from neutron.api.v2 import router from neutron.common import config from neutron.conf import quota as qconf from neutron import quota from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit import testlib_api DEFAULT_QUOTAS_ACTION = 'details' TARGET_PLUGIN = 'neutron.plugins.ml2.plugin.Ml2Plugin' _get_path = test_base._get_path class DetailQuotaExtensionTestCase(testlib_api.WebTestCase): def setUp(self): super(DetailQuotaExtensionTestCase, self).setUp() # Ensure existing ExtensionManager is not used extensions.PluginAwareExtensionManager._instance = None self.useFixture(fixture.APIDefinitionFixture()) # Create the default configurations self.config_parse() # Update the plugin and extensions path self.setup_coreplugin('ml2') quota.QUOTAS = quota.QuotaEngine() self._plugin_patcher = mock.patch(TARGET_PLUGIN, autospec=True) self.plugin = self._plugin_patcher.start() self.plugin.return_value.supported_extension_aliases = \ ['quotas', 'quota_details'] # QUOTAS will register the items in conf when starting ext_mgr = extensions.PluginAwareExtensionManager.get_instance() app = config.load_paste_app('extensions_test_app') ext_middleware = extensions.ExtensionMiddleware(app, ext_mgr=ext_mgr) self.api = webtest.TestApp(ext_middleware) # Initialize the router for the core API in order to ensure core quota # resources are registered router.APIRouter() class DetailQuotaExtensionDbTestCase(DetailQuotaExtensionTestCase): fmt = 'json' def test_show_detail_quotas(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id)} res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt, endpoint=DEFAULT_QUOTAS_ACTION), extra_environ=env) self.assertEqual(200, res.status_int) quota = self.deserialize(res) self.assertEqual(0, quota['quota']['network']['reserved']) self.assertEqual(0, quota['quota']['subnet']['reserved']) self.assertEqual(0, quota['quota']['port']['reserved']) self.assertEqual(0, quota['quota']['network']['used']) self.assertEqual(0, quota['quota']['subnet']['used']) self.assertEqual(0, quota['quota']['port']['used']) self.assertEqual(qconf.DEFAULT_QUOTA_NETWORK, quota['quota']['network']['limit']) self.assertEqual(qconf.DEFAULT_QUOTA_SUBNET, quota['quota']['subnet']['limit']) self.assertEqual(qconf.DEFAULT_QUOTA_PORT, quota['quota']['port']['limit']) def test_detail_quotas_negative_limit_value(self): cfg.CONF.set_override( 'quota_port', -666, group='QUOTAS') cfg.CONF.set_override( 'quota_network', -10, group='QUOTAS') cfg.CONF.set_override( 'quota_subnet', -50, group='QUOTAS') tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=True)} res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt, endpoint=DEFAULT_QUOTAS_ACTION), extra_environ=env) self.assertEqual(200, res.status_int) quota = self.deserialize(res) self.assertEqual(0, quota['quota']['network']['reserved']) self.assertEqual(0, quota['quota']['subnet']['reserved']) self.assertEqual(0, quota['quota']['port']['reserved']) self.assertEqual(0, quota['quota']['network']['used']) self.assertEqual(0, quota['quota']['subnet']['used']) self.assertEqual(0, quota['quota']['port']['used']) self.assertEqual(qconf.DEFAULT_QUOTA, quota['quota']['network']['limit']) self.assertEqual(qconf.DEFAULT_QUOTA, quota['quota']['subnet']['limit']) self.assertEqual(qconf.DEFAULT_QUOTA, quota['quota']['port']['limit']) def test_show_detail_quotas_with_admin(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id + '2', is_admin=True)} res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt, endpoint=DEFAULT_QUOTAS_ACTION), extra_environ=env) self.assertEqual(200, res.status_int) quota = self.deserialize(res) self.assertEqual(0, quota['quota']['network']['reserved']) self.assertEqual(0, quota['quota']['subnet']['reserved']) self.assertEqual(0, quota['quota']['port']['reserved']) self.assertEqual(0, quota['quota']['network']['used']) self.assertEqual(0, quota['quota']['subnet']['used']) self.assertEqual(0, quota['quota']['port']['used']) self.assertEqual(qconf.DEFAULT_QUOTA_NETWORK, quota['quota']['network']['limit']) self.assertEqual(qconf.DEFAULT_QUOTA_SUBNET, quota['quota']['subnet']['limit']) self.assertEqual(qconf.DEFAULT_QUOTA_PORT, quota['quota']['port']['limit']) def test_detail_quotas_without_admin_forbidden_returns_403(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id + '2', is_admin=False)} res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt, endpoint=DEFAULT_QUOTAS_ACTION), extra_environ=env, expect_errors=True) self.assertEqual(403, res.status_int) neutron-12.1.1/neutron/tests/unit/extensions/test_network_ip_availability.py0000664000175000017500000005101413553660047027660 0ustar zuulzuul00000000000000# Copyright 2016 GoDaddy. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib import constants import neutron.api.extensions as api_ext import neutron.common.config as config import neutron.extensions import neutron.services.network_ip_availability.plugin as plugin_module import neutron.tests.unit.db.test_db_base_plugin_v2 as test_db_base_plugin_v2 API_RESOURCE = 'network-ip-availabilities' IP_AVAIL_KEY = 'network_ip_availability' IP_AVAILS_KEY = 'network_ip_availabilities' EXTENSIONS_PATH = ':'.join(neutron.extensions.__path__) PLUGIN_NAME = '%s.%s' % (plugin_module.NetworkIPAvailabilityPlugin.__module__, plugin_module.NetworkIPAvailabilityPlugin.__name__) class TestNetworkIPAvailabilityAPI( test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def setUp(self): svc_plugins = {'plugin_name': PLUGIN_NAME} super(TestNetworkIPAvailabilityAPI, self).setUp( service_plugins=svc_plugins) self.plugin = plugin_module.NetworkIPAvailabilityPlugin() ext_mgr = api_ext.PluginAwareExtensionManager( EXTENSIONS_PATH, {"network-ip-availability": self.plugin} ) app = config.load_paste_app('extensions_test_app') self.ext_api = api_ext.ExtensionMiddleware(app, ext_mgr=ext_mgr) def _validate_availability(self, network, availability, expected_used_ips, expected_total_ips=253): self.assertEqual(network['name'], availability['network_name']) self.assertEqual(network['id'], availability['network_id']) self.assertEqual(expected_used_ips, availability['used_ips']) self.assertEqual(expected_total_ips, availability['total_ips']) def _validate_from_availabilities(self, availabilities, wrapped_network, expected_used_ips, expected_total_ips=253): network = wrapped_network['network'] availability = self._find_availability(availabilities, network['id']) self.assertIsNotNone(availability) self._validate_availability(network, availability, expected_used_ips=expected_used_ips, expected_total_ips=expected_total_ips) def test_usages_query_list_with_fields_total_ips(self): with self.network() as net: with self.subnet(network=net): # list by query fields: total_ips params = 'fields=total_ips' request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAILS_KEY, response) self.assertEqual(1, len(response[IP_AVAILS_KEY])) availability = response[IP_AVAILS_KEY][0] self.assertIn('total_ips', availability) self.assertEqual(253, availability['total_ips']) self.assertNotIn('network_id', availability) def test_usages_query_show_with_fields_total_ips(self): with self.network() as net: with self.subnet(network=net): network = net['network'] # Show by query fields: total_ips params = ['total_ips'] request = self.new_show_request(API_RESOURCE, network['id'], fields=params) response = self.deserialize( self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAIL_KEY, response) availability = response[IP_AVAIL_KEY] self.assertIn('total_ips', availability) self.assertEqual(253, availability['total_ips']) self.assertNotIn('network_id', availability) @staticmethod def _find_availability(availabilities, net_id): for ip_availability in availabilities: if net_id == ip_availability['network_id']: return ip_availability def test_basic(self): with self.network() as net: with self.subnet(network=net): network = net['network'] # Get ALL request = self.new_list_request(API_RESOURCE, self.fmt) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAILS_KEY, response) self.assertEqual(1, len(response[IP_AVAILS_KEY])) self._validate_from_availabilities(response[IP_AVAILS_KEY], net, 0) # Get single via id request = self.new_show_request(API_RESOURCE, network['id']) response = self.deserialize( self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAIL_KEY, response) usage = response[IP_AVAIL_KEY] self._validate_availability(network, usage, 0) def test_usages_multi_nets_subnets(self): with self.network(name='net1') as n1,\ self.network(name='net2') as n2,\ self.network(name='net3') as n3: # n1 should have 2 subnets, n2 should have none, n3 has 1 with self.subnet(network=n1) as subnet1_1, \ self.subnet(cidr='40.0.0.0/24', network=n3) as subnet3_1: # Consume 3 ports n1, none n2, 2 ports on n3 with self.port(subnet=subnet1_1),\ self.port(subnet=subnet1_1),\ self.port(subnet=subnet1_1),\ self.port(subnet=subnet3_1),\ self.port(subnet=subnet3_1): # Test get ALL request = self.new_list_request(API_RESOURCE) response = self.deserialize( self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAILS_KEY, response) self.assertEqual(3, len(response[IP_AVAILS_KEY])) data = response[IP_AVAILS_KEY] self._validate_from_availabilities(data, n1, 3, 253) self._validate_from_availabilities(data, n2, 0, 0) self._validate_from_availabilities(data, n3, 2, 253) # Test get single via network id network = n1['network'] request = self.new_show_request(API_RESOURCE, network['id']) response = self.deserialize( self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAIL_KEY, response) self._validate_availability(network, response[IP_AVAIL_KEY], 3, 253) def test_usages_multi_nets_subnets_sums(self): with self.network(name='net1') as n1: # n1 has 2 subnets with self.subnet(network=n1) as subnet1_1, \ self.subnet(cidr='40.0.0.0/24', network=n1) as subnet1_2: # Consume 3 ports n1: 1 on subnet 1 and 2 on subnet 2 with self.port(subnet=subnet1_1),\ self.port(subnet=subnet1_2),\ self.port(subnet=subnet1_2): # Get ALL request = self.new_list_request(API_RESOURCE) response = self.deserialize( self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAILS_KEY, response) self.assertEqual(1, len(response[IP_AVAILS_KEY])) self._validate_from_availabilities(response[IP_AVAILS_KEY], n1, 3, 506) # Get single via network id network = n1['network'] request = self.new_show_request(API_RESOURCE, network['id']) response = self.deserialize( self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAIL_KEY, response) self._validate_availability(network, response[IP_AVAIL_KEY], 3, 506) def test_usages_port_consumed_v4(self): with self.network() as net: with self.subnet(network=net) as subnet: request = self.new_list_request(API_RESOURCE) # Consume 2 ports with self.port(subnet=subnet), self.port(subnet=subnet): response = self.deserialize(self.fmt, request.get_response( self.ext_api)) self._validate_from_availabilities(response[IP_AVAILS_KEY], net, 2) def test_usages_query_ip_version_v4(self): with self.network() as net: with self.subnet(network=net): # Get IPv4 params = 'ip_version=4' request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAILS_KEY, response) self.assertEqual(1, len(response[IP_AVAILS_KEY])) self._validate_from_availabilities(response[IP_AVAILS_KEY], net, 0) # Get IPv6 should return empty array params = 'ip_version=6' request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertEqual(0, len(response[IP_AVAILS_KEY])) def test_usages_query_ip_version_v6(self): with self.network() as net: with self.subnet( network=net, cidr='2607:f0d0:1002:51::/64', ip_version=6, ipv6_address_mode=constants.DHCPV6_STATELESS): # Get IPv6 params = 'ip_version=6' request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertEqual(1, len(response[IP_AVAILS_KEY])) self._validate_from_availabilities( response[IP_AVAILS_KEY], net, 0, 18446744073709551614) # Get IPv4 should return empty array params = 'ip_version=4' request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertEqual(0, len(response[IP_AVAILS_KEY])) def test_usages_ports_consumed_v6(self): with self.network() as net: with self.subnet( network=net, cidr='2607:f0d0:1002:51::/64', ip_version=6, ipv6_address_mode=constants.DHCPV6_STATELESS) as subnet: request = self.new_list_request(API_RESOURCE) # Consume 3 ports with self.port(subnet=subnet),\ self.port(subnet=subnet), \ self.port(subnet=subnet): response = self.deserialize( self.fmt, request.get_response(self.ext_api)) self._validate_from_availabilities(response[IP_AVAILS_KEY], net, 3, 18446744073709551614) def test_usages_query_network_id(self): with self.network() as net: with self.subnet(network=net): network = net['network'] test_id = network['id'] # Get by query param: network_id params = 'network_id=%s' % test_id request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAILS_KEY, response) self.assertEqual(1, len(response[IP_AVAILS_KEY])) self._validate_from_availabilities(response[IP_AVAILS_KEY], net, 0) # Get by NON-matching query param: network_id params = 'network_id=clearlywontmatch' request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertEqual(0, len(response[IP_AVAILS_KEY])) def test_usages_query_network_name(self): test_name = 'net_name_1' with self.network(name=test_name) as net: with self.subnet(network=net): # Get by query param: network_name params = 'network_name=%s' % test_name request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAILS_KEY, response) self.assertEqual(1, len(response[IP_AVAILS_KEY])) self._validate_from_availabilities(response[IP_AVAILS_KEY], net, 0) # Get by NON-matching query param: network_name params = 'network_name=clearly-wont-match' request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertEqual(0, len(response[IP_AVAILS_KEY])) def test_usages_query_tenant_id(self): test_tenant_id = 'a-unique-test-id' with self.network(tenant_id=test_tenant_id) as net: with self.subnet(network=net): # Get by query param: tenant_id params = 'tenant_id=%s' % test_tenant_id request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAILS_KEY, response) self.assertEqual(1, len(response[IP_AVAILS_KEY])) self._validate_from_availabilities(response[IP_AVAILS_KEY], net, 0) for net_avail in response[IP_AVAILS_KEY]: self.assertEqual(test_tenant_id, net_avail['tenant_id']) # Get by NON-matching query param: tenant_id params = 'tenant_id=clearly-wont-match' request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertEqual(0, len(response[IP_AVAILS_KEY])) def test_usages_query_project_id(self): test_project_id = 'a-unique-project-id' with self.network(tenant_id=test_project_id) as net: with self.subnet(network=net): # Get by query param: project_id params = 'project_id=%s' % test_project_id request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertIn(IP_AVAILS_KEY, response) self.assertEqual(1, len(response[IP_AVAILS_KEY])) self._validate_from_availabilities(response[IP_AVAILS_KEY], net, 0) for net_avail in response[IP_AVAILS_KEY]: self.assertEqual(test_project_id, net_avail['project_id']) # Get by NON-matching query param: project_id params = 'project_id=clearly-wont-match' request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize(self.fmt, request.get_response(self.ext_api)) self.assertEqual(0, len(response[IP_AVAILS_KEY])) def test_usages_multi_net_multi_subnet_46(self): # Setup mixed v4/v6 networks with IPs consumed on each with self.network(name='net-v6-1') as net_v6_1, \ self.network(name='net-v6-2') as net_v6_2, \ self.network(name='net-v4-1') as net_v4_1, \ self.network(name='net-v4-2') as net_v4_2: with self.subnet(network=net_v6_1, cidr='2607:f0d0:1002:51::/64', ip_version=6) as s61, \ self.subnet(network=net_v6_2, cidr='2607:f0d0:1003:52::/64', ip_version=6) as s62, \ self.subnet(network=net_v4_1, cidr='10.0.0.0/24') as s41, \ self.subnet(network=net_v4_2, cidr='10.0.1.0/24') as s42: with self.port(subnet=s61),\ self.port(subnet=s62), self.port(subnet=s62), \ self.port(subnet=s41), \ self.port(subnet=s42), self.port(subnet=s42): # Verify consumption across all request = self.new_list_request(API_RESOURCE) response = self.deserialize( self.fmt, request.get_response(self.ext_api)) avails_list = response[IP_AVAILS_KEY] self._validate_from_availabilities( avails_list, net_v6_1, 1, 18446744073709551614) self._validate_from_availabilities( avails_list, net_v6_2, 2, 18446744073709551614) self._validate_from_availabilities( avails_list, net_v4_1, 1, 253) self._validate_from_availabilities( avails_list, net_v4_2, 2, 253) # Query by IP versions. Ensure subnet versions match for ip_ver in [4, 6]: params = 'ip_version=%i' % ip_ver request = self.new_list_request(API_RESOURCE, params=params) response = self.deserialize( self.fmt, request.get_response(self.ext_api)) for net_avail in response[IP_AVAILS_KEY]: for sub in net_avail['subnet_ip_availability']: self.assertEqual(ip_ver, sub['ip_version']) # Verify consumption querying 2 network ids (IN clause) request = self.new_list_request( API_RESOURCE, params='network_id=%s&network_id=%s' % (net_v4_2['network']['id'], net_v6_2['network']['id'])) response = self.deserialize( self.fmt, request.get_response(self.ext_api)) avails_list = response[IP_AVAILS_KEY] self._validate_from_availabilities( avails_list, net_v6_2, 2, 18446744073709551614) self._validate_from_availabilities( avails_list, net_v4_2, 2, 253) neutron-12.1.1/neutron/tests/unit/extensions/test_tag.py0000664000175000017500000003734313553660047023531 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import context from oslo_utils import uuidutils import testscenarios from neutron.api import extensions from neutron.api.v2 import attributes from neutron.common import config import neutron.extensions from neutron.objects.qos import policy from neutron.objects import trunk from neutron.services.tag import tag_plugin from neutron.tests import fake_notifier from neutron.tests.unit.extensions import test_l3 from neutron.tests.unit.extensions import test_securitygroup DB_PLUGIN_KLASS = 'neutron.tests.unit.extensions.test_tag.TestTagPlugin' load_tests = testscenarios.load_tests_apply_scenarios extensions_path = ':'.join(neutron.extensions.__path__) class TestTagPlugin(test_securitygroup.SecurityGroupTestPlugin, test_l3.TestL3NatBasePlugin): __native_pagination_support = True __native_sorting_support = True supported_extension_aliases = ["external-net", "security-group"] class TestTagApiBase(test_securitygroup.SecurityGroupsTestCase, test_l3.L3NatTestCaseMixin): scenarios = [ ('Network Tag Test', dict(collection='networks', member='network')), ('Subnet Tag Test', dict(collection='subnets', member='subnet')), ('Port Tag Test', dict(collection='ports', member='port')), ('Subnetpool Tag Test', dict(collection='subnetpools', member='subnetpool')), ('Router Tag Test', dict(collection='routers', member='router')), ('Floatingip Tag Test', dict(collection='floatingips', member='floatingip')), ('Securitygroup Tag Test', dict(collection='security-groups', member='security_group')), ('QoS Policy Tag Test', dict(collection='policies', member='policy')), ('Trunk Tag Test', dict(collection='trunks', member='trunk')), ] def setUp(self): service_plugins = { 'TAG': "neutron.services.tag.tag_plugin.TagPlugin", 'router': "neutron.tests.unit.extensions.test_l3.TestL3NatServicePlugin"} super(TestTagApiBase, self).setUp(plugin=DB_PLUGIN_KLASS, service_plugins=service_plugins) plugin = tag_plugin.TagPlugin() l3_plugin = test_l3.TestL3NatServicePlugin() sec_plugin = test_securitygroup.SecurityGroupTestPlugin() ext_mgr = extensions.PluginAwareExtensionManager( extensions_path, {'router': l3_plugin, 'TAG': plugin, 'sec': sec_plugin} ) ext_mgr.extend_resources("2.0", attributes.RESOURCE_ATTRIBUTE_MAP) app = config.load_paste_app('extensions_test_app') self.ext_api = extensions.ExtensionMiddleware(app, ext_mgr=ext_mgr) def _is_object(self): return self.collection in ['policies', 'trunks'] def _prepare_make_resource(self): if self.collection == "floatingips": net = self._make_network(self.fmt, 'net1', True) self._set_net_external(net['network']['id']) self._make_subnet(self.fmt, net, '10.0.0.1', '10.0.0.0/24') info = {'network_id': net['network']['id']} self._make_router(self.fmt, None, external_gateway_info=info) self.net = net['network'] def _make_object(self): ctxt = context.get_admin_context() if self.collection == "policies": self.obj = policy.QosPolicy(context=ctxt, id=uuidutils.generate_uuid(), project_id='tenant', name='pol1', rules=[]) elif self.collection == "trunks": net = self._make_network(self.fmt, 'net1', True) port = self._make_port(self.fmt, net['network']['id']) self.obj = trunk.Trunk(context=ctxt, id=uuidutils.generate_uuid(), project_id='tenant', name='', port_id=port['port']['id']) self.obj.create() return self.obj.id def _make_resource(self): if self._is_object(): return self._make_object() if self.collection == "networks": res = self._make_network(self.fmt, 'net1', True) elif self.collection == "subnets": net = self._make_network(self.fmt, 'net1', True) res = self._make_subnet(self.fmt, net, '10.0.0.1', '10.0.0.0/24') elif self.collection == "ports": net = self._make_network(self.fmt, 'net1', True) res = self._make_port(self.fmt, net['network']['id']) elif self.collection == "subnetpools": res = self._make_subnetpool(self.fmt, ['10.0.0.0/8'], name='my pool', tenant_id="tenant") elif self.collection == "routers": res = self._make_router(self.fmt, None) elif self.collection == "floatingips": res = self._make_floatingip(self.fmt, self.net['id']) elif self.collection == "security-groups": res = self._make_security_group(self.fmt, 'sec1', '') return res[self.member]['id'] def _get_object_tags(self): ctxt = context.get_admin_context() res = self.obj.get_object(ctxt, id=self.resource_id) return res.to_dict()['tags'] def _get_resource_tags(self): if self._is_object(): return self._get_object_tags() res = self._show(self.collection, self.resource_id) return res[self.member]['tags'] def _put_tag(self, tag): req = self._req('PUT', self.collection, id=self.resource_id, subresource='tags', sub_id=tag) return req.get_response(self.ext_api) def _put_tags(self, tags=None, body=None): if tags: body = {'tags': tags} elif body: body = body else: body = {} req = self._req('PUT', self.collection, data=body, id=self.resource_id, subresource='tags') return req.get_response(self.ext_api) def _get_tag(self, tag): req = self._req('GET', self.collection, id=self.resource_id, subresource='tags', sub_id=tag) return req.get_response(self.ext_api) def _delete_tag(self, tag): req = self._req('DELETE', self.collection, id=self.resource_id, subresource='tags', sub_id=tag) return req.get_response(self.ext_api) def _delete_tags(self): req = self._req('DELETE', self.collection, id=self.resource_id, subresource='tags') return req.get_response(self.ext_api) def _assertEqualTags(self, expected, actual): self.assertEqual(set(expected), set(actual)) def _get_tags_filter_objects(self, tags, tags_any, not_tags, not_tags_any): filters = {} if tags: filters['tags'] = tags if tags_any: filters['tags-any'] = tags_any if not_tags: filters['not-tags'] = not_tags if not_tags_any: filters['not-tags-any'] = not_tags_any if self.collection == "policies": obj_class = policy.QosPolicy elif self.collection == "trunks": obj_class = trunk.Trunk ctxt = context.get_admin_context() res = obj_class.get_objects(ctxt, **filters) return [n.id for n in res] def _make_query_string(self, tags, tags_any, not_tags, not_tags_any): filter_strings = [] if tags: filter_strings.append("tags=" + ','.join(tags)) if tags_any: filter_strings.append("tags-any=" + ','.join(tags_any)) if not_tags: filter_strings.append("not-tags=" + ','.join(not_tags)) if not_tags_any: filter_strings.append("not-tags-any=" + ','.join(not_tags_any)) return '&'.join(filter_strings) def _get_tags_filter_resources(self, tags=None, tags_any=None, not_tags=None, not_tags_any=None): if self._is_object(): return self._get_tags_filter_objects(tags, tags_any, not_tags, not_tags_any) params = self._make_query_string(tags, tags_any, not_tags, not_tags_any) res = self._list(self.collection, query_params=params) return [n['id'] for n in res[self.collection.replace('-', '_')]] def _test_notification_report(self, expect_notify): notify = set(n['event_type'] for n in fake_notifier.NOTIFICATIONS) duplicated_notify = expect_notify & notify self.assertEqual(expect_notify, duplicated_notify) fake_notifier.reset() class TestResourceTagApi(TestTagApiBase): def setUp(self): super(TestResourceTagApi, self).setUp() self._prepare_make_resource() self.resource_id = self._make_resource() def test_put_tag(self): expect_notify = set(['tag.create.start', 'tag.create.end']) res = self._put_tag('red') self.assertEqual(201, res.status_int) tags = self._get_resource_tags() self._assertEqualTags(['red'], tags) self._test_notification_report(expect_notify) res = self._put_tag('blue') self.assertEqual(201, res.status_int) tags = self._get_resource_tags() self._assertEqualTags(['red', 'blue'], tags) self._test_notification_report(expect_notify) def test_put_tag_exists(self): res = self._put_tag('blue') self.assertEqual(201, res.status_int) res = self._put_tag('blue') self.assertEqual(201, res.status_int) def test_put_tags(self): expect_notify = set(['tag.update.start', 'tag.update.end']) res = self._put_tags(['red', 'green']) self.assertEqual(200, res.status_int) tags = self._get_resource_tags() self._assertEqualTags(['red', 'green'], tags) self._test_notification_report(expect_notify) def test_put_invalid_tags(self): res = self._put_tags() self.assertEqual(400, res.status_int) res = self._put_tags(body=7) self.assertEqual(400, res.status_int) res = self._put_tags(body={'invalid': True}) self.assertEqual(400, res.status_int) def test_put_tags_replace(self): res = self._put_tags(['red', 'green']) self.assertEqual(200, res.status_int) tags = self._get_resource_tags() self._assertEqualTags(['red', 'green'], tags) res = self._put_tags(['blue', 'red']) self.assertEqual(200, res.status_int) tags = self._get_resource_tags() self._assertEqualTags(['blue', 'red'], tags) def test_get_tag(self): res = self._put_tag('red') self.assertEqual(201, res.status_int) res = self._get_tag('red') self.assertEqual(204, res.status_int) def test_get_tag_notfound(self): res = self._put_tag('red') self.assertEqual(201, res.status_int) res = self._get_tag('green') self.assertEqual(404, res.status_int) def test_delete_tag(self): expect_notify = set(['tag.delete.start', 'tag.delete.end']) res = self._put_tags(['red', 'green']) self.assertEqual(200, res.status_int) res = self._delete_tag('red') self.assertEqual(204, res.status_int) tags = self._get_resource_tags() self._assertEqualTags(['green'], tags) self._test_notification_report(expect_notify) def test_delete_tag_notfound(self): res = self._put_tags(['red', 'green']) self.assertEqual(200, res.status_int) res = self._delete_tag('blue') self.assertEqual(404, res.status_int) def test_delete_tags(self): expect_notify = set(['tag.delete_all.start', 'tag.delete_all.end']) res = self._put_tags(['red', 'green']) self.assertEqual(200, res.status_int) res = self._delete_tags() self.assertEqual(204, res.status_int) tags = self._get_resource_tags() self._assertEqualTags([], tags) self._test_notification_report(expect_notify) class TestResourceTagFilter(TestTagApiBase): def setUp(self): super(TestResourceTagFilter, self).setUp() self._prepare_resource_tags() def _make_tags(self, resource_id, tags): body = {'tags': tags} req = self._req('PUT', self.collection, data=body, id=resource_id, subresource='tags') return req.get_response(self.ext_api) def _prepare_resource_tags(self): self._prepare_make_resource() self.res1 = self._make_resource() self.res2 = self._make_resource() self.res3 = self._make_resource() self.res4 = self._make_resource() self.res5 = self._make_resource() self.res_ids = [self.res1, self.res2, self.res3, self.res4, self.res5] self._make_tags(self.res1, ['red']) self._make_tags(self.res2, ['red', 'blue']) self._make_tags(self.res3, ['red', 'blue', 'green']) self._make_tags(self.res4, ['green']) # res5: no tags def _assertEqualResources(self, expected, resources): actual = [n for n in resources if n in self.res_ids] self.assertEqual(set(expected), set(actual)) def test_filter_tags_single(self): resources = self._get_tags_filter_resources(tags=['red']) self._assertEqualResources([self.res1, self.res2, self.res3], resources) def test_filter_tags_multi(self): resources = self._get_tags_filter_resources(tags=['red', 'blue']) self._assertEqualResources([self.res2, self.res3], resources) def test_filter_tags_any_single(self): resources = self._get_tags_filter_resources(tags_any=['blue']) self._assertEqualResources([self.res2, self.res3], resources) def test_filter_tags_any_multi(self): resources = self._get_tags_filter_resources(tags_any=['red', 'blue']) self._assertEqualResources([self.res1, self.res2, self.res3], resources) def test_filter_not_tags_single(self): resources = self._get_tags_filter_resources(not_tags=['red']) self._assertEqualResources([self.res4, self.res5], resources) def test_filter_not_tags_multi(self): resources = self._get_tags_filter_resources(not_tags=['red', 'blue']) self._assertEqualResources([self.res1, self.res4, self.res5], resources) def test_filter_not_tags_any_single(self): resources = self._get_tags_filter_resources(not_tags_any=['blue']) self._assertEqualResources([self.res1, self.res4, self.res5], resources) def test_filter_not_tags_any_multi(self): resources = self._get_tags_filter_resources(not_tags_any=['red', 'blue']) self._assertEqualResources([self.res4, self.res5], resources) neutron-12.1.1/neutron/tests/unit/extensions/test_external_net.py0000664000175000017500000002117213553660046025436 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import external_net as extnet_apidef from neutron_lib import constants from neutron_lib import context from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_utils import uuidutils import testtools from webob import exc from neutron.db import external_net_db from neutron.db import models_v2 from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit.db import test_db_base_plugin_v2 _uuid = uuidutils.generate_uuid _get_path = test_base._get_path class ExtNetTestExtensionManager(object): def get_resources(self): return [] def get_actions(self): return [] def get_request_extensions(self): return [] class ExtNetDBTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def _create_network(self, fmt, name, admin_state_up, **kwargs): """Override the routine for allowing the router:external attribute.""" # attributes containing a colon should be passed with # a double underscore new_args = dict(zip(map(lambda x: x.replace('__', ':'), kwargs), kwargs.values())) arg_list = new_args.pop('arg_list', ()) + (extnet_apidef.EXTERNAL,) return super(ExtNetDBTestCase, self)._create_network( fmt, name, admin_state_up, arg_list=arg_list, **new_args) def setUp(self): plugin = 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin' ext_mgr = ExtNetTestExtensionManager() super(ExtNetDBTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr) def _set_net_external(self, net_id): self._update('networks', net_id, {'network': {extnet_apidef.EXTERNAL: True}}) def test_list_nets_external(self): with self.network() as n1: self._set_net_external(n1['network']['id']) with self.network(): body = self._list('networks') self.assertEqual(2, len(body['networks'])) body = self._list('networks', query_params="%s=True" % extnet_apidef.EXTERNAL) self.assertEqual(1, len(body['networks'])) body = self._list('networks', query_params="%s=False" % extnet_apidef.EXTERNAL) self.assertEqual(1, len(body['networks'])) def test_list_nets_external_pagination(self): if self._skip_native_pagination: self.skipTest("Skip test for not implemented pagination feature") with self.network(name='net1') as n1, self.network(name='net3') as n3: self._set_net_external(n1['network']['id']) self._set_net_external(n3['network']['id']) with self.network(name='net2') as n2: self._test_list_with_pagination( 'network', (n1, n3), ('name', 'asc'), 1, 3, query_params='router:external=True') self._test_list_with_pagination( 'network', (n2, ), ('name', 'asc'), 1, 2, query_params='router:external=False') def test_get_network_succeeds_without_filter(self): plugin = directory.get_plugin() ctx = context.Context(None, None, is_admin=True) result = plugin.get_networks(ctx, filters=None) self.assertEqual([], result) def test_update_network_set_external_non_admin_fails(self): # Assert that a non-admin user cannot update the # router:external attribute with self.network(tenant_id='noadmin') as network: data = {'network': {'router:external': True}} req = self.new_update_request('networks', data, network['network']['id']) req.environ['neutron.context'] = context.Context('', 'noadmin') res = req.get_response(self.api) self.assertEqual(exc.HTTPForbidden.code, res.status_int) def test_update_network_external_net_with_ports_set_not_shared(self): with self.network(router__external=True, shared=True) as ext_net,\ self.subnet(network=ext_net) as ext_subnet, \ self.port(subnet=ext_subnet, tenant_id='', device_owner=constants.DEVICE_OWNER_ROUTER_SNAT): data = {'network': {'shared': False}} req = self.new_update_request('networks', data, ext_net['network']['id']) res = req.get_response(self.api) self.assertEqual(exc.HTTPOk.code, res.status_int) ctx = context.Context(None, None, is_admin=True) plugin = directory.get_plugin() result = plugin.get_networks(ctx) self.assertFalse(result[0]['shared']) def test_network_filter_hook_admin_context(self): ctx = context.Context(None, None, is_admin=True) model = models_v2.Network conditions = external_net_db._network_filter_hook(ctx, model, []) self.assertEqual([], conditions) def test_network_filter_hook_nonadmin_context(self): ctx = context.Context('edinson', 'cavani') model = models_v2.Network txt = ("networkrbacs.action = :action_1 AND " "networkrbacs.target_tenant = :target_tenant_1 OR " "networkrbacs.target_tenant = :target_tenant_2") conditions = external_net_db._network_filter_hook(ctx, model, []) self.assertEqual(conditions.__str__(), txt) # Try to concatenate conditions txt2 = (txt.replace('tenant_1', 'tenant_3'). replace('tenant_2', 'tenant_4'). replace('action_1', 'action_2')) conditions = external_net_db._network_filter_hook(ctx, model, conditions) self.assertEqual(conditions.__str__(), "%s OR %s" % (txt, txt2)) def test_create_port_external_network_non_admin_fails(self): with self.network(router__external=True) as ext_net: with self.subnet(network=ext_net) as ext_subnet: with testtools.ExpectedException( exc.HTTPClientError) as ctx_manager: with self.port(subnet=ext_subnet, set_context='True', tenant_id='noadmin'): pass self.assertEqual(403, ctx_manager.exception.code) def test_create_port_external_network_admin_succeeds(self): with self.network(router__external=True) as ext_net: with self.subnet(network=ext_net) as ext_subnet: with self.port(subnet=ext_subnet) as port: self.assertEqual(port['port']['network_id'], ext_net['network']['id']) def test_create_external_network_non_admin_fails(self): with testtools.ExpectedException(exc.HTTPClientError) as ctx_manager: with self.network(router__external=True, set_context='True', tenant_id='noadmin'): pass self.assertEqual(403, ctx_manager.exception.code) def test_create_external_network_admin_succeeds(self): with self.network(router__external=True) as ext_net: self.assertTrue(ext_net['network'][extnet_apidef.EXTERNAL]) def test_delete_network_check_disassociated_floatingips(self): l3_mock = mock.Mock() directory.add_plugin(plugin_constants.L3, l3_mock) with self.network() as net: req = self.new_delete_request('networks', net['network']['id']) res = req.get_response(self.api) self.assertEqual(exc.HTTPNoContent.code, res.status_int) (l3_mock.delete_disassociated_floatingips .assert_called_once_with(mock.ANY, net['network']['id'])) neutron-12.1.1/neutron/tests/unit/extensions/test_data_plane_status.py0000664000175000017500000001332213553660047026440 0ustar zuulzuul00000000000000# Copyright (c) 2017 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from webob import exc as web_exc from neutron_lib.api.definitions import data_plane_status as dps_lib from neutron_lib.api.definitions import port as port_def from neutron_lib import constants from neutron.db import _resource_extend as resource_extend from neutron.db import data_plane_status_db as dps_db from neutron.db import db_base_plugin_v2 from neutron.extensions import data_plane_status as dps_ext from neutron.tests import fake_notifier from neutron.tests.unit.db import test_db_base_plugin_v2 class DataPlaneStatusTestExtensionManager(object): def get_resources(self): return [] def get_actions(self): return [] def get_request_extensions(self): return [] def get_extended_resources(self, version): return dps_ext.Data_plane_status.get_extended_resources(version) @resource_extend.has_resource_extenders class DataPlaneStatusExtensionTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, dps_db.DataPlaneStatusMixin): supported_extension_aliases = ["data-plane-status"] @staticmethod @resource_extend.extends([port_def.COLLECTION_NAME]) def _extend_port_data_plane_status(port_res, port_db): return dps_db.DataPlaneStatusMixin._extend_port_data_plane_status( port_res, port_db) def update_port(self, context, id, port): with context.session.begin(subtransactions=True): ret_port = super(DataPlaneStatusExtensionTestPlugin, self).update_port(context, id, port) if dps_lib.DATA_PLANE_STATUS in port['port']: self._process_update_port_data_plane_status(context, port['port'], ret_port) return ret_port class DataPlaneStatusExtensionTestCase( test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def setUp(self): plugin = ('neutron.tests.unit.extensions.test_data_plane_status.' 'DataPlaneStatusExtensionTestPlugin') ext_mgr = DataPlaneStatusTestExtensionManager() super(DataPlaneStatusExtensionTestCase, self).setUp( plugin=plugin, ext_mgr=ext_mgr) def test_update_port_data_plane_status(self): with self.port() as port: data = {'port': {'data_plane_status': constants.ACTIVE}} req = self.new_update_request(port_def.COLLECTION_NAME, data, port['port']['id']) res = req.get_response(self.api) p = self.deserialize(self.fmt, res)['port'] self.assertEqual(200, res.status_code) self.assertEqual(p[dps_lib.DATA_PLANE_STATUS], constants.ACTIVE) def test_port_create_data_plane_status_default_none(self): with self.port(name='port1') as port: req = self.new_show_request( port_def.COLLECTION_NAME, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertIsNone(res['port'][dps_lib.DATA_PLANE_STATUS]) def test_port_create_invalid_attr_data_plane_status(self): kwargs = {dps_lib.DATA_PLANE_STATUS: constants.ACTIVE} with self.network() as network: with self.subnet(network=network): res = self._create_port(self.fmt, network['network']['id'], arg_list=(dps_lib.DATA_PLANE_STATUS,), **kwargs) self.assertEqual(400, res.status_code) def test_port_update_preserves_data_plane_status(self): with self.port(name='port1') as port: res = self._update(port_def.COLLECTION_NAME, port['port']['id'], {'port': {dps_lib.DATA_PLANE_STATUS: constants.ACTIVE}}) res = self._update(port_def.COLLECTION_NAME, port['port']['id'], {'port': {'name': 'port2'}}) self.assertEqual(res['port']['name'], 'port2') self.assertEqual(res['port'][dps_lib.DATA_PLANE_STATUS], constants.ACTIVE) def test_port_update_with_invalid_data_plane_status(self): with self.port(name='port1') as port: self._update(port_def.COLLECTION_NAME, port['port']['id'], {'port': {dps_lib.DATA_PLANE_STATUS: "abc"}}, web_exc.HTTPBadRequest.code) def test_port_update_event_on_data_plane_status(self): expect_notify = set(['port.update.start', 'port.update.end']) with self.port(name='port1') as port: self._update(port_def.COLLECTION_NAME, port['port']['id'], {'port': {dps_lib.DATA_PLANE_STATUS: constants.ACTIVE}}) notify = set(n['event_type'] for n in fake_notifier.NOTIFICATIONS) duplicated_notify = expect_notify & notify self.assertEqual(expect_notify, duplicated_notify) fake_notifier.reset() neutron-12.1.1/neutron/tests/unit/extensions/test_timestamp.py0000664000175000017500000002753013553660047024756 0ustar zuulzuul00000000000000# Copyright 2015 HuaWei Technologies. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import mock from neutron_lib import context from neutron_lib.plugins import directory from oslo_utils import timeutils from oslo_utils import uuidutils import six from neutron.db import db_base_plugin_v2 from neutron.extensions import timestamp from neutron import manager from neutron.objects import network as net_obj from neutron.objects import tag as tag_obj from neutron.tests.unit.db import test_db_base_plugin_v2 class TimeStampExtensionManager(object): def get_resources(self): return [] def get_actions(self): return [] def get_request_extensions(self): return [] def get_extended_resources(self, version): return timestamp.Timestamp().get_extended_resources(version) class TimeStampTestPlugin(db_base_plugin_v2.NeutronDbPluginV2): """Just for test with TimeStampPlugin""" class TimeStampChangedsinceTestCase(test_db_base_plugin_v2. NeutronDbPluginV2TestCase): plugin = ('neutron.tests.unit.extensions.test_timestamp.' + 'TimeStampTestPlugin') def setUp(self): ext_mgr = TimeStampExtensionManager() super(TimeStampChangedsinceTestCase, self).setUp(plugin=self.plugin, ext_mgr=ext_mgr) self.addCleanup(manager.NeutronManager.clear_instance) def setup_coreplugin(self, core_plugin=None, load_plugins=True): super(TimeStampChangedsinceTestCase, self).setup_coreplugin( self.plugin, load_plugins=False) self.patched_default_svc_plugins.return_value = ['timestamp'] manager.init() def _get_resp_with_changed_since(self, resource_type, changed_since): query_params = 'changed_since=%s' % changed_since req = self.new_list_request('%ss' % resource_type, self.fmt, query_params) resources = self.deserialize(self.fmt, req.get_response(self.api)) return resources def _return_by_timedelay(self, resource, timedelay): resource_type = six.next(six.iterkeys(resource)) time_create = timeutils.parse_isotime( resource[resource_type]['updated_at']) time_before = datetime.timedelta(seconds=timedelay) addedtime_string = (datetime.datetime. strftime(time_create + time_before, '%Y-%m-%dT%H:%M:%S')) + 'Z' return self._get_resp_with_changed_since(resource_type, addedtime_string) def _update_test_resource_by_name(self, resource): resource_type = six.next(six.iterkeys(resource)) name = resource[resource_type]['name'] data = {resource_type: {'name': '%s_new' % name}} req = self.new_update_request('%ss' % resource_type, data, resource[resource_type]['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) return res def _set_timestamp_by_show(self, resource, type): req = self.new_show_request('%ss' % type, resource[type]['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) resource[type]['created_at'] = res[type]['created_at'] resource[type]['updated_at'] = res[type]['updated_at'] def _list_resources_with_changed_since(self, resource): # assert list results contain the net info when # changed_since equal with the net updated time. resource_type = six.next(six.iterkeys(resource)) if resource_type in ['network', 'port']: self._set_timestamp_by_show(resource, resource_type) resources = self._get_resp_with_changed_since(resource_type, resource[resource_type][ 'updated_at']) self.assertEqual(resource[resource_type]['id'], resources[resource_type + 's'][0]['id']) # assert list results contain the net info when changed_since # is earlier than the net updated time. resources = self._return_by_timedelay(resource, -1) self.assertEqual(resource[resource_type]['id'], resources[resource_type + 's'][0]['id']) # assert list results is Null when changed_since # is later with the net updated time. resources = self._return_by_timedelay(resource, 1) self.assertEqual([], resources[resource_type + 's']) def _test_list_mutiple_resources_with_changed_since(self, first, second): resource_type = six.next(six.iterkeys(first)) if resource_type in ['network', 'port']: self._set_timestamp_by_show(first, resource_type) self._set_timestamp_by_show(second, resource_type) # update names of second new_second = self._update_test_resource_by_name(second) # now the queue of order by # updated_at is first < new_second # test changed_since < first's updated_at resources = self._return_by_timedelay(first, -1) for resource in [first[resource_type]['id'], new_second[resource_type]['id']]: self.assertIn(resource, [n['id'] for n in resources[resource_type + 's']]) # test changed_since = first's updated_at resources = self._return_by_timedelay(first, 0) for resource in [first[resource_type]['id'], new_second[resource_type]['id']]: self.assertIn(resource, [n['id'] for n in resources[resource_type + 's']]) # test first < changed_since < second resources = self._return_by_timedelay(new_second, -1) self.assertIn(new_second[resource_type]['id'], [n['id'] for n in resources[resource_type + 's']]) # test first < changed_since = second resources = self._return_by_timedelay(new_second, 0) self.assertIn(new_second[resource_type]['id'], [n['id'] for n in resources[resource_type + 's']]) #test first < second < changed_since resources = self._return_by_timedelay(new_second, 3) self.assertEqual({resource_type + 's': []}, resources) def test_list_networks_with_changed_since(self): with self.network('net1') as net: self._list_resources_with_changed_since(net) def test_list_subnets_with_changed_since(self): with self.network('net2') as net: with self.subnet(network=net) as subnet: self._list_resources_with_changed_since(subnet) def test_list_ports_with_changed_since(self): with self.network('net3') as net: with self.subnet(network=net) as subnet: with self.port(subnet=subnet) as port: self._list_resources_with_changed_since(port) def test_list_subnetpools_with_changed_since(self): prefixes = ['3.3.3.3/24', '4.4.4.4/24'] with self.subnetpool(prefixes, tenant_id=self._tenant_id, name='sp_test02') as subnetpool: self._list_resources_with_changed_since(subnetpool) def test_list_mutiple_networks_with_changed_since(self): with self.network('net1') as net1, self.network('net2') as net2: self._test_list_mutiple_resources_with_changed_since(net1, net2) def test_list_mutiple_subnets_with_changed_since(self): with self.network('net1') as net1, self.network('net2') as net2: with self.subnet(network=net1) as subnet1, self.subnet( network=net2) as subnet2: self._test_list_mutiple_resources_with_changed_since(subnet1, subnet2) def test_list_mutiple_subnetpools_with_changed_since(self): prefixes1 = ['3.3.3.3/24', '4.4.4.4/24'] prefixes2 = ['5.5.5.5/24', '6.6.6.6/24'] with self.subnetpool(prefixes1, tenant_id=self._tenant_id, name='sp01') as sp1: with self.subnetpool(prefixes2, tenant_id=self._tenant_id, name='sp02') as sp2: self._test_list_mutiple_resources_with_changed_since(sp1, sp2) def test_list_mutiple_ports_with_changed_since(self): with self.network('net') as net: with self.subnet(network=net) as subnet: with self.port(subnet=subnet) as p1, self.port( subnet=subnet) as p2: self._test_list_mutiple_resources_with_changed_since(p1, p2) def test_list_resources_with_invalid_changed_since(self): # check when input --changed-since with no arg, then filters # stored as 'True'. And also check other invalid inputs changed_sinces = ['123', 'True', 'AAAA-BB-CCTDD-EE-FFZ', '9a9b-11-00T99-1a-r3Z', '0000-00-00T00-00-00Z'] for resource in ['network', 'subnet', 'port', 'subnetpool']: for changed_since in changed_sinces: req = self.new_list_request('%ss' % resource, self.fmt, 'changed_since=%s' % changed_since) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(list(res.values())[0]['type'], 'InvalidInput') def test_timestamp_fields_ignored_in_update(self): ctx = context.get_admin_context() with self.port() as port: plugin = directory.get_plugin() port = plugin.get_port(ctx, port['port']['id']) port['name'] = 'updated' port['created_at'] = '2011-04-06T14:34:23' port['updated_at'] = '2012-04-06T15:34:23' updated = plugin.update_port(ctx, port['id'], {'port': port}) self.assertEqual('updated', updated['name']) self.assertNotEqual(port['updated_at'], updated['updated_at']) self.assertNotEqual(port['created_at'], updated['created_at']) class TimeStampDBMixinTestCase(TimeStampChangedsinceTestCase): """Test timestamp_db.TimeStamp_db_mixin()""" def _save_network(self, network_id): ctx = context.get_admin_context() obj = net_obj.Network(ctx, id=network_id) obj.create() return obj.standard_attr_id # Use tag as non StandardAttribute object def _save_tag(self, tags, standard_attr_id): ctx = context.get_admin_context() for tag in tags: tag_obj.Tag(ctx, standard_attr_id=standard_attr_id, tag=tag).create() def test_update_timpestamp(self): network_id = uuidutils.generate_uuid() tags = ["red", "blue"] with mock.patch('oslo_utils.timeutils.utcnow') as timenow: timenow.return_value = datetime.datetime(2016, 3, 11, 0, 0) # Test to update StandardAttribute object standard_attr_id = self._save_network(network_id) self.assertEqual(1, timenow.call_count) # Test not to update non StandardAttribute object self._save_tag(tags, standard_attr_id) self.assertEqual(1, timenow.call_count) neutron-12.1.1/neutron/tests/unit/extensions/test_segment.py0000664000175000017500000033140313553660047024412 0ustar zuulzuul00000000000000# Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from keystoneauth1 import exceptions as ks_exc import mock import netaddr from neutron_lib.api.definitions import ip_allocation as ipalloc_apidef from neutron_lib.api.definitions import l2_adjacency as l2adj_apidef from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import exceptions from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from novaclient import exceptions as nova_exc from oslo_config import cfg from oslo_utils import uuidutils import webob.exc from neutron.common import exceptions as neutron_exc from neutron.conf.plugins.ml2.drivers import driver_type from neutron.db import agents_db from neutron.db import agentschedulers_db from neutron.db import db_base_plugin_v2 from neutron.db import portbindings_db from neutron.db import segments_db from neutron.extensions import segment as ext_segment from neutron.objects import network from neutron.services.segments import db from neutron.services.segments import exceptions as segment_exc from neutron.services.segments import placement_client from neutron.services.segments import plugin as seg_plugin from neutron.tests import base from neutron.tests.common import helpers from neutron.tests.unit.db import test_db_base_plugin_v2 SERVICE_PLUGIN_KLASS = 'neutron.services.segments.plugin.Plugin' TEST_PLUGIN_KLASS = ( 'neutron.tests.unit.extensions.test_segment.SegmentTestPlugin') DHCP_HOSTA = 'dhcp-host-a' DHCP_HOSTB = 'dhcp-host-b' HTTP_NOT_FOUND = 404 class SegmentTestExtensionManager(object): def get_resources(self): return ext_segment.Segment.get_resources() def get_actions(self): return [] def get_request_extensions(self): return [] class SegmentTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def setUp(self, plugin=None): # Remove MissingAuthPlugin exception from logs self.patch_notifier = mock.patch( 'neutron.notifiers.batch_notifier.BatchNotifier._notify') self.patch_notifier.start() if not plugin: plugin = TEST_PLUGIN_KLASS service_plugins = {'segments_plugin_name': SERVICE_PLUGIN_KLASS} cfg.CONF.set_override('service_plugins', [SERVICE_PLUGIN_KLASS]) ext_mgr = SegmentTestExtensionManager() super(SegmentTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) def _create_segment(self, fmt, expected_res_status=None, **kwargs): segment = {'segment': {}} for k, v in kwargs.items(): segment['segment'][k] = None if v is None else str(v) segment_req = self.new_create_request( 'segments', segment, fmt) segment_res = segment_req.get_response(self.ext_api) if expected_res_status: self.assertEqual(segment_res.status_int, expected_res_status) return segment_res def _make_segment(self, fmt, **kwargs): res = self._create_segment(fmt, **kwargs) if res.status_int >= webob.exc.HTTPClientError.code: res.charset = 'utf8' raise webob.exc.HTTPClientError( code=res.status_int, explanation=str(res)) return self.deserialize(fmt, res) def segment(self, **kwargs): kwargs.setdefault('network_type', 'net_type') return self._make_segment( self.fmt, tenant_id=self._tenant_id, **kwargs) def _test_create_segment(self, expected=None, **kwargs): keys = kwargs.copy() segment = self.segment(**keys) self._validate_resource(segment, keys, 'segment') if expected: self._compare_resource(segment, expected, 'segment') return segment class SegmentTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, portbindings_db.PortBindingMixin, db.SegmentDbMixin): __native_pagination_support = True __native_sorting_support = True supported_extension_aliases = ["segment", "binding", "ip_allocation"] def get_plugin_description(self): return "Network Segments" @classmethod def get_plugin_type(cls): return "segments" def create_port(self, context, port): port_dict = super(SegmentTestPlugin, self).create_port(context, port) self._process_portbindings_create_and_update( context, port['port'], port_dict) return port_dict def update_port(self, context, id, port): port_dict = super(SegmentTestPlugin, self).update_port( context, id, port) self._process_portbindings_create_and_update( context, port['port'], port_dict) return port_dict class TestSegmentNameDescription(SegmentTestCase): def setUp(self): super(TestSegmentNameDescription, self).setUp() with self.network() as network: self.network = network['network'] def _test_create_segment(self, expected=None, **kwargs): for d in (kwargs, expected): if d is None: continue d.setdefault('network_id', self.network['id']) d.setdefault('name', None) d.setdefault('description', None) d.setdefault('physical_network', 'phys_net') d.setdefault('network_type', 'net_type') d.setdefault('segmentation_id', 200) return super(TestSegmentNameDescription, self)._test_create_segment( expected, **kwargs) def test_create_segment_no_name_description(self): self._test_create_segment(expected={}) def test_create_segment_with_name(self): expected_segment = {'name': 'segment_name'} self._test_create_segment(name='segment_name', expected=expected_segment) def test_create_segment_with_description(self): expected_segment = {'description': 'A segment'} self._test_create_segment(description='A segment', expected=expected_segment) def test_update_segment_set_name(self): segment = self._test_create_segment() result = self._update('segments', segment['segment']['id'], {'segment': {'name': 'Segment name'}}, expected_code=webob.exc.HTTPOk.code) self.assertEqual('Segment name', result['segment']['name']) def test_update_segment_set_description(self): segment = self._test_create_segment() result = self._update('segments', segment['segment']['id'], {'segment': {'description': 'Segment desc'}}, expected_code=webob.exc.HTTPOk.code) self.assertEqual('Segment desc', result['segment']['description']) def test_update_segment_set_name_to_none(self): segment = self._test_create_segment( description='A segment', name='segment') result = self._update('segments', segment['segment']['id'], {'segment': {'name': None}}, expected_code=webob.exc.HTTPOk.code) self.assertIsNone(result['segment']['name']) def test_update_segment_set_description_to_none(self): segment = self._test_create_segment( description='A segment', name='segment') result = self._update('segments', segment['segment']['id'], {'segment': {'description': None}}, expected_code=webob.exc.HTTPOk.code) self.assertIsNone(result['segment']['description']) class TestSegment(SegmentTestCase): def test_create_segment(self): with self.network() as network: network = network['network'] expected_segment = {'network_id': network['id'], 'physical_network': 'phys_net', 'network_type': 'net_type', 'segmentation_id': 200} self._test_create_segment(network_id=network['id'], physical_network='phys_net', segmentation_id=200, expected=expected_segment) def test_create_segment_non_existent_network(self): exc = self.assertRaises(webob.exc.HTTPClientError, self._test_create_segment, network_id=uuidutils.generate_uuid(), physical_network='phys_net', segmentation_id=200) self.assertEqual(HTTP_NOT_FOUND, exc.code) self.assertIn('NetworkNotFound', exc.explanation) def test_create_segment_no_phys_net(self): with self.network() as network: network = network['network'] expected_segment = {'network_id': network['id'], 'physical_network': None, 'network_type': 'net_type', 'segmentation_id': 200} self._test_create_segment(network_id=network['id'], segmentation_id=200, expected=expected_segment) def test_create_segment_no_segmentation_id(self): def _mock_reserve_segmentation_id(rtype, event, trigger, context, segment): if not segment.get('segmentation_id'): segment['segmentation_id'] = 200 with self.network() as network: network = network['network'] registry.subscribe(_mock_reserve_segmentation_id, resources.SEGMENT, events.PRECOMMIT_CREATE) expected_segment = {'network_id': network['id'], 'physical_network': 'phys_net', 'network_type': 'net_type', 'segmentation_id': 200} self._test_create_segment(network_id=network['id'], physical_network='phys_net', expected=expected_segment) def test_create_segment_with_exception_in_core_plugin(self): cxt = context.get_admin_context() with self.network() as network: network = network['network'] with mock.patch.object(registry, 'notify') as notify: notify.side_effect = exceptions.CallbackFailure(errors=Exception) self.assertRaises(webob.exc.HTTPClientError, self.segment, network_id=network['id'], segmentation_id=200) network_segments = segments_db.get_network_segments(cxt, network['id']) self.assertEqual([], network_segments) def test_create_segments_in_certain_order(self): cxt = context.get_admin_context() with self.network() as network: network = network['network'] segment1 = self.segment( network_id=network['id'], segmentation_id=200) segment2 = self.segment( network_id=network['id'], segmentation_id=201) segment3 = self.segment( network_id=network['id'], segmentation_id=202) network_segments = segments_db.get_network_segments(cxt, network['id']) self.assertEqual(segment1['segment']['id'], network_segments[0]['id']) self.assertEqual(segment2['segment']['id'], network_segments[1]['id']) self.assertEqual(segment3['segment']['id'], network_segments[2]['id']) def test_delete_segment(self): with self.network() as network: network = network['network'] self.segment(network_id=network['id'], segmentation_id=200) segment = self.segment(network_id=network['id'], segmentation_id=201) self._delete('segments', segment['segment']['id']) self._show('segments', segment['segment']['id'], expected_code=webob.exc.HTTPNotFound.code) def test_delete_segment_failed_with_subnet_associated(self): with self.network() as network: net = network['network'] segment = self._test_create_segment(network_id=net['id'], segmentation_id=200) segment_id = segment['segment']['id'] with self.subnet(network=network, segment_id=segment_id): self._delete('segments', segment_id, expected_code=webob.exc.HTTPConflict.code) exist_segment = self._show('segments', segment_id) self.assertEqual(segment_id, exist_segment['segment']['id']) def test_get_segment(self): with self.network() as network: network = network['network'] segment = self._test_create_segment(network_id=network['id'], physical_network='phys_net', segmentation_id=200) req = self.new_show_request('segments', segment['segment']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(segment['segment']['id'], res['segment']['id']) def test_list_segments(self): with self.network() as network: network = network['network'] self._test_create_segment(network_id=network['id'], physical_network='phys_net1', segmentation_id=200) self._test_create_segment(network_id=network['id'], physical_network='phys_net2', segmentation_id=201) res = self._list('segments') self.assertEqual(2, len(res['segments'])) def test_update_segments(self): with self.network() as network: net = network['network'] segment = self._test_create_segment(network_id=net['id'], segmentation_id=200) segment['segment']['segmentation_id'] = '201' self._update('segments', segment['segment']['id'], segment, expected_code=webob.exc.HTTPClientError.code) def test_segment_notification_on_delete_network(self): with mock.patch.object(db, '_delete_segments_for_network') as dsn: db.subscribe() with self.network() as network: network = network['network'] self._delete('networks', network['id']) dsn.assert_called_with(resources.NETWORK, events.PRECOMMIT_DELETE, mock.ANY, context=mock.ANY, network_id=mock.ANY) class TestSegmentML2(SegmentTestCase): def setUp(self): super(TestSegmentML2, self).setUp(plugin='ml2') def test_segment_notification_on_create_network(self): with mock.patch.object(registry, 'notify') as notify: with self.network(): pass notify.assert_any_call(resources.SEGMENT, events.PRECOMMIT_CREATE, context=mock.ANY, segment=mock.ANY, trigger=mock.ANY) class TestSegmentSubnetAssociation(SegmentTestCase): def test_basic_association(self): with self.network() as network: net = network['network'] segment = self._test_create_segment(network_id=net['id'], segmentation_id=200) segment_id = segment['segment']['id'] with self.subnet(network=network, segment_id=segment_id) as subnet: subnet = subnet['subnet'] request = self.new_show_request('subnets', subnet['id']) response = request.get_response(self.api) res = self.deserialize(self.fmt, response) self.assertEqual(segment_id, res['subnet']['segment_id']) def test_association_network_mismatch(self): with self.network() as network1: with self.network() as network2: net = network1['network'] segment = self._test_create_segment(network_id=net['id'], segmentation_id=200) res = self._create_subnet(self.fmt, net_id=network2['network']['id'], tenant_id=network2['network']['tenant_id'], gateway_ip=constants.ATTR_NOT_SPECIFIED, cidr='10.0.0.0/24', segment_id=segment['segment']['id']) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_association_segment_not_found(self): with self.network() as network: net = network['network'] segment_id = uuidutils.generate_uuid() res = self._create_subnet(self.fmt, net_id=net['id'], tenant_id=net['tenant_id'], gateway_ip=constants.ATTR_NOT_SPECIFIED, cidr='10.0.0.0/24', segment_id=segment_id) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_only_some_subnets_associated_not_allowed(self): with self.network() as network: with self.subnet(network=network): net = network['network'] segment = self._test_create_segment(network_id=net['id'], segmentation_id=200) res = self._create_subnet(self.fmt, net_id=net['id'], tenant_id=net['tenant_id'], gateway_ip=constants.ATTR_NOT_SPECIFIED, cidr='10.0.1.0/24', segment_id=segment['segment']['id']) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_association_to_dynamic_segment_not_allowed(self): cxt = context.get_admin_context() with self.network() as network: net = network['network'] # Can't create a dynamic segment through the API segment = {segments_db.NETWORK_TYPE: 'phys_net', segments_db.PHYSICAL_NETWORK: 'net_type', segments_db.SEGMENTATION_ID: 200} segments_db.add_network_segment(cxt, network_id=net['id'], segment=segment, is_dynamic=True) res = self._create_subnet(self.fmt, net_id=net['id'], tenant_id=net['tenant_id'], gateway_ip=constants.ATTR_NOT_SPECIFIED, cidr='10.0.0.0/24', segment_id=segment['id']) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) class HostSegmentMappingTestCase(SegmentTestCase): _mechanism_drivers = ['logger'] def setUp(self, plugin=None): cfg.CONF.set_override('mechanism_drivers', self._mechanism_drivers, group='ml2') # NOTE(dasm): ml2_type_vlan requires to be registered before used. # This piece was refactored and removed from .config, so it causes # a problem, when tests are executed with pdb. # There is no problem when tests are running without debugger. driver_type.register_ml2_drivers_vlan_opts() cfg.CONF.set_override('network_vlan_ranges', ['phys_net1', 'phys_net2'], group='ml2_type_vlan') if not plugin: plugin = 'ml2' super(HostSegmentMappingTestCase, self).setUp(plugin=plugin) db.subscribe() def _get_segments_for_host(self, host): ctx = context.get_admin_context() segment_host_mapping = network.SegmentHostMapping.get_objects( ctx, host=host) return {seg_host['segment_id']: seg_host for seg_host in segment_host_mapping} def _register_agent(self, host, mappings=None, plugin=None, start_flag=True): helpers.register_ovs_agent(host=host, bridge_mappings=mappings, plugin=self.plugin, start_flag=start_flag) def _test_one_segment_one_host(self, host): physical_network = 'phys_net1' with self.network() as network: network = network['network'] segment = self._test_create_segment( network_id=network['id'], physical_network=physical_network, segmentation_id=200, network_type=constants.TYPE_VLAN)['segment'] self._register_agent(host, mappings={physical_network: 'br-eth-1'}, plugin=self.plugin) segments_host_db = self._get_segments_for_host(host) self.assertEqual(1, len(segments_host_db)) self.assertEqual(segment['id'], segments_host_db[segment['id']]['segment_id']) self.assertEqual(host, segments_host_db[segment['id']]['host']) return segment class TestMl2HostSegmentMappingNoAgent(HostSegmentMappingTestCase): def setUp(self, plugin=None): if not plugin: plugin = TEST_PLUGIN_KLASS super(TestMl2HostSegmentMappingNoAgent, self).setUp(plugin=plugin) def test_update_segment_host_mapping(self): ctx = context.get_admin_context() host = 'host1' physnets = ['phys_net1'] with self.network() as network: network = network['network'] segment = self._test_create_segment( network_id=network['id'], physical_network='phys_net1', segmentation_id=200, network_type=constants.TYPE_VLAN)['segment'] self._test_create_segment( network_id=network['id'], physical_network='phys_net2', segmentation_id=201, network_type=constants.TYPE_VLAN)['segment'] segments = db.get_segments_with_phys_nets(ctx, physnets) segment_ids = {segment['id'] for segment in segments} db.update_segment_host_mapping(ctx, host, segment_ids) segments_host_db = self._get_segments_for_host(host) self.assertEqual(1, len(segments_host_db)) self.assertEqual(segment['id'], segments_host_db[segment['id']]['segment_id']) self.assertEqual(host, segments_host_db[segment['id']]['host']) def test_map_segment_to_hosts(self): ctx = context.get_admin_context() hosts = {'host1', 'host2', 'host3'} with self.network() as network: network = network['network'] segment = self._test_create_segment( network_id=network['id'], physical_network='phys_net1', segmentation_id=200, network_type=constants.TYPE_VLAN)['segment'] db.map_segment_to_hosts(ctx, segment['id'], hosts) updated_segment = self.plugin.get_segment(ctx, segment['id']) self.assertEqual(hosts, set(updated_segment['hosts'])) def test_get_all_hosts_mapped_with_segments(self): ctx = context.get_admin_context() hosts = set() with self.network() as network: network_id = network['network']['id'] for i in range(1, 3): host = "host%s" % i segment = self._test_create_segment( network_id=network_id, physical_network='phys_net%s' % i, segmentation_id=200 + i, network_type=constants.TYPE_VLAN) db.update_segment_host_mapping( ctx, host, {segment['segment']['id']}) hosts.add(host) # Now they are 2 hosts with segment being mapped. actual_hosts = db.get_hosts_mapped_with_segments(ctx) self.assertEqual(hosts, actual_hosts) class TestMl2HostSegmentMappingOVS(HostSegmentMappingTestCase): _mechanism_drivers = ['openvswitch', 'logger'] mock_path = 'neutron.services.segments.db.update_segment_host_mapping' def test_new_agent(self): host = 'host1' self._test_one_segment_one_host(host) def test_updated_agent_changed_physical_networks(self): host = 'host1' physical_networks = ['phys_net1', 'phys_net2'] networks = [] segments = [] for i in range(len(physical_networks)): with self.network() as network: networks.append(network['network']) segments.append(self._test_create_segment( network_id=networks[i]['id'], physical_network=physical_networks[i], segmentation_id=200, network_type=constants.TYPE_VLAN)['segment']) self._register_agent(host, mappings={physical_networks[0]: 'br-eth-1', physical_networks[1]: 'br-eth-2'}, plugin=self.plugin) segments_host_db = self._get_segments_for_host(host) self.assertEqual(len(physical_networks), len(segments_host_db)) for segment in segments: self.assertEqual(segment['id'], segments_host_db[segment['id']]['segment_id']) self.assertEqual(host, segments_host_db[segment['id']]['host']) self._register_agent(host, mappings={physical_networks[0]: 'br-eth-1'}, plugin=self.plugin) segments_host_db = self._get_segments_for_host(host) self.assertEqual(1, len(segments_host_db)) self.assertEqual(segments[0]['id'], segments_host_db[segments[0]['id']]['segment_id']) self.assertEqual(host, segments_host_db[segments[0]['id']]['host']) def test_same_segment_two_hosts(self): host1 = 'host1' host2 = 'host2' physical_network = 'phys_net1' segment = self._test_one_segment_one_host(host1) self._register_agent(host2, mappings={physical_network: 'br-eth-1'}, plugin=self.plugin) segments_host_db = self._get_segments_for_host(host2) self.assertEqual(1, len(segments_host_db)) self.assertEqual(segment['id'], segments_host_db[segment['id']]['segment_id']) self.assertEqual(host2, segments_host_db[segment['id']]['host']) def test_update_agent_only_change_agent_host_mapping(self): host1 = 'host1' host2 = 'host2' physical_network = 'phys_net1' with self.network() as network: network = network['network'] segment1 = self._test_create_segment( network_id=network['id'], physical_network=physical_network, segmentation_id=200, network_type=constants.TYPE_VLAN)['segment'] self._register_agent(host1, mappings={physical_network: 'br-eth-1'}, plugin=self.plugin) self._register_agent(host2, mappings={physical_network: 'br-eth-1'}, plugin=self.plugin) # Update agent at host2 should only change mapping with host2. other_phys_net = 'phys_net2' segment2 = self._test_create_segment( network_id=network['id'], physical_network=other_phys_net, segmentation_id=201, network_type=constants.TYPE_VLAN)['segment'] self._register_agent(host2, mappings={other_phys_net: 'br-eth-2'}, plugin=self.plugin) # We should have segment1 map to host1 and segment2 map to host2 now segments_host_db1 = self._get_segments_for_host(host1) self.assertEqual(1, len(segments_host_db1)) self.assertEqual(segment1['id'], segments_host_db1[segment1['id']]['segment_id']) self.assertEqual(host1, segments_host_db1[segment1['id']]['host']) segments_host_db2 = self._get_segments_for_host(host2) self.assertEqual(1, len(segments_host_db2)) self.assertEqual(segment2['id'], segments_host_db2[segment2['id']]['segment_id']) self.assertEqual(host2, segments_host_db2[segment2['id']]['host']) def test_new_segment_after_host_reg(self): host1 = 'host1' physical_network = 'phys_net1' segment = self._test_one_segment_one_host(host1) with self.network() as network: network = network['network'] segment2 = self._test_create_segment( network_id=network['id'], physical_network=physical_network, segmentation_id=201, network_type=constants.TYPE_VLAN)['segment'] segments_host_db = self._get_segments_for_host(host1) self.assertEqual(set((segment['id'], segment2['id'])), set(segments_host_db)) def test_segment_deletion_removes_host_mapping(self): host = 'host1' segment = self._test_one_segment_one_host(host) self._delete('segments', segment['id']) segments_host_db = self._get_segments_for_host(host) self.assertFalse(segments_host_db) @mock.patch(mock_path) def test_agent_with_no_mappings(self, mock): host = 'host1' physical_network = 'phys_net1' with self.network() as network: network = network['network'] self._test_create_segment( network_id=network['id'], physical_network=physical_network, segmentation_id=200, network_type=constants.TYPE_VLAN) self._register_agent(host, plugin=self.plugin) segments_host_db = self._get_segments_for_host(host) self.assertFalse(segments_host_db) self.assertFalse(mock.mock_calls) class TestMl2HostSegmentMappingLinuxBridge(TestMl2HostSegmentMappingOVS): _mechanism_drivers = ['linuxbridge', 'logger'] def _register_agent(self, host, mappings=None, plugin=None): helpers.register_linuxbridge_agent(host=host, bridge_mappings=mappings, plugin=self.plugin) class TestMl2HostSegmentMappingMacvtap(TestMl2HostSegmentMappingOVS): _mechanism_drivers = ['macvtap', 'logger'] def _register_agent(self, host, mappings=None, plugin=None): helpers.register_macvtap_agent(host=host, interface_mappings=mappings, plugin=self.plugin) class TestMl2HostSegmentMappingSriovNicSwitch(TestMl2HostSegmentMappingOVS): _mechanism_drivers = ['sriovnicswitch', 'logger'] def _register_agent(self, host, mappings=None, plugin=None): helpers.register_sriovnicswitch_agent(host=host, device_mappings=mappings, plugin=self.plugin) class NoSupportHostSegmentMappingPlugin(db_base_plugin_v2.NeutronDbPluginV2, db.SegmentDbMixin, agents_db.AgentDbMixin): __native_pagination_support = True __native_sorting_support = True supported_extension_aliases = [] class TestHostSegmentMappingNoSupportFromPlugin(HostSegmentMappingTestCase): mock_path = 'neutron.services.segments.db.update_segment_host_mapping' def setUp(self): plugin = ('neutron.tests.unit.extensions.test_segment.' 'NoSupportHostSegmentMappingPlugin') super(TestHostSegmentMappingNoSupportFromPlugin, self).setUp( plugin=plugin) @mock.patch(mock_path) def test_host_segments_not_updated(self, mock): host = 'host1' physical_network = 'phys_net1' with self.network() as network: network = network['network'] self._test_create_segment(network_id=network['id'], physical_network=physical_network, segmentation_id=200, network_type=constants.TYPE_VLAN) self._register_agent(host, mappings={physical_network: 'br-eth-1'}, plugin=self.plugin) segments_host_db = self._get_segments_for_host(host) self.assertFalse(segments_host_db) self.assertFalse(mock.mock_calls) class TestMl2HostSegmentMappingAgentServerSynch(HostSegmentMappingTestCase): _mechanism_drivers = ['openvswitch', 'logger'] mock_path = 'neutron.services.segments.db.update_segment_host_mapping' @mock.patch(mock_path) def test_starting_server_processes_agents(self, mock_function): host = 'agent_updating_starting_server' physical_network = 'phys_net1' self._register_agent(host, mappings={physical_network: 'br-eth-1'}, plugin=self.plugin, start_flag=False) self.assertIn(host, db.reported_hosts) self.assertEqual(1, mock_function.call_count) expected_call = mock.call(mock.ANY, host, set()) mock_function.assert_has_calls([expected_call]) @mock.patch(mock_path) def test_starting_agent_is_processed(self, mock_function): host = 'starting_agent' physical_network = 'phys_net1' self._register_agent(host, mappings={physical_network: 'br-eth-1'}, plugin=self.plugin, start_flag=False) self.assertIn(host, db.reported_hosts) self._register_agent(host, mappings={physical_network: 'br-eth-1'}, plugin=self.plugin, start_flag=True) self.assertIn(host, db.reported_hosts) self.assertEqual(2, mock_function.call_count) expected_call = mock.call(mock.ANY, host, set()) mock_function.assert_has_calls([expected_call, expected_call]) @mock.patch(mock_path) def test_no_starting_agent_is_not_processed(self, mock_function): host = 'agent_with_no_start_update' physical_network = 'phys_net1' self._register_agent(host, mappings={physical_network: 'br-eth-1'}, plugin=self.plugin, start_flag=False) self.assertIn(host, db.reported_hosts) mock_function.reset_mock() self._register_agent(host, mappings={physical_network: 'br-eth-1'}, plugin=self.plugin, start_flag=False) self.assertIn(host, db.reported_hosts) mock_function.assert_not_called() class SegmentAwareIpamTestCase(SegmentTestCase): def _setup_host_mappings(self, mappings=()): ctx = context.get_admin_context() for segment_id, host in mappings: network.SegmentHostMapping( ctx, segment_id=segment_id, host=host).create() def _create_test_segment_with_subnet(self, network=None, cidr='2001:db8:0:0::/64', physnet='physnet'): """Creates one network with one segment and one subnet""" network, segment = self._create_test_network_and_segment(network, physnet) subnet = self._create_test_subnet_with_segment(network, segment, cidr) return network, segment, subnet def _create_test_network_and_segment(self, network=None, physnet='physnet'): if not network: with self.network() as network: pass segment = self._test_create_segment( network_id=network['network']['id'], physical_network=physnet, network_type=constants.TYPE_VLAN) return network, segment def _create_test_subnet_with_segment(self, network, segment, cidr='2001:db8:0:0::/64', allocation_pools=None): ip_version = netaddr.IPNetwork(cidr).version if cidr else None with self.subnet(network=network, segment_id=segment['segment']['id'], ip_version=ip_version, cidr=cidr, allocation_pools=allocation_pools) as subnet: self._validate_l2_adjacency(network['network']['id'], is_adjacent=False) return subnet def _validate_l2_adjacency(self, network_id, is_adjacent): request = self.new_show_request('networks', network_id) response = self.deserialize(self.fmt, request.get_response(self.api)) self.assertEqual(is_adjacent, response['network'][l2adj_apidef.L2_ADJACENCY]) class TestSegmentAwareIpam(SegmentAwareIpamTestCase): def _create_test_segments_with_subnets(self, num): """Creates one network with num segments and num subnets""" with self.network() as network: segments, subnets = [], [] for i in range(num): cidr = '2001:db8:0:%s::/64' % i physnet = 'physnet%s' % i _net, segment, subnet = self._create_test_segment_with_subnet( network=network, cidr=cidr, physnet=physnet) segments.append(segment) subnets.append(subnet) return network, segments, subnets def test_port_create_with_segment_subnets(self): """No binding information is provided, defer IP allocation""" network, segment, subnet = self._create_test_segment_with_subnet() response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id']) res = self.deserialize(self.fmt, response) # Don't allocate IPs in this case because we didn't give binding info self.assertEqual(0, len(res['port']['fixed_ips'])) def test_port_create_fixed_ips_with_segment_subnets_no_binding_info(self): """Fixed IP provided and no binding info, do not defer IP allocation""" network, segment, subnet = self._create_test_segment_with_subnet() response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], fixed_ips=[ {'subnet_id': subnet['subnet']['id']} ]) res = self.deserialize(self.fmt, response) # We gave fixed_ips, allocate IPs in this case despite no binding info self._validate_immediate_ip_allocation(res['port']['id']) def _assert_one_ip_in_subnet(self, response, cidr): res = self.deserialize(self.fmt, response) self.assertEqual(1, len(res['port']['fixed_ips'])) ip = res['port']['fixed_ips'][0]['ip_address'] ip_net = netaddr.IPNetwork(cidr) self.assertIn(ip, ip_net) def test_port_create_with_binding_information(self): """Binding information is provided, subnets are on segments""" network, segments, subnets = self._create_test_segments_with_subnets(3) # Map the host to the middle segment (by mocking host/segment mapping) self._setup_host_mappings([ (segments[1]['segment']['id'], 'fakehost'), (segments[1]['segment']['id'], 'otherhost'), (segments[0]['segment']['id'], 'thirdhost')]) response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) res = self.deserialize(self.fmt, response) self._validate_immediate_ip_allocation(res['port']['id']) # Since host mapped to middle segment, IP must come from middle subnet self._assert_one_ip_in_subnet(response, subnets[1]['subnet']['cidr']) def test_port_create_with_binding_and_no_subnets(self): """Binding information is provided, no subnets.""" with self.network() as network: segment = self._test_create_segment( network_id=network['network']['id'], physical_network='physnet', network_type=constants.TYPE_VLAN) # Map the host to the segment self._setup_host_mappings([(segment['segment']['id'], 'fakehost')]) response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) res = self.deserialize(self.fmt, response) # No subnets, so no allocation. But, it shouldn't be an error. self.assertEqual(0, len(res['port']['fixed_ips'])) def test_port_create_with_binding_information_fallback(self): """Binding information is provided, subnets not on segments""" with self.network() as network: with self.subnet(network=network, ip_version=6, cidr='2001:db8:0:0::/64') as subnet: segment = self._test_create_segment( network_id=network['network']['id'], physical_network='physnet', network_type=constants.TYPE_VLAN) self._validate_l2_adjacency(network['network']['id'], is_adjacent=True) # Map the host to the segment self._setup_host_mappings([(segment['segment']['id'], 'fakehost')]) response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) res = self.deserialize(self.fmt, response) self._validate_immediate_ip_allocation(res['port']['id']) # Since the subnet is not on a segment, fall back to it self._assert_one_ip_in_subnet(response, subnet['subnet']['cidr']) def test_port_create_on_unconnected_host(self): """Binding information provided, host not connected to any segment""" network, segment, _subnet = self._create_test_segment_with_subnet() response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) res = self.deserialize(self.fmt, response) self.assertEqual(webob.exc.HTTPConflict.code, response.status_int) self.assertEqual(segment_exc.HostNotConnectedToAnySegment.__name__, res['NeutronError']['type']) # Ensure that mapping the segment to other hosts doesn't trip it up self._setup_host_mappings([(segment['segment']['id'], 'otherhost')]) response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) res = self.deserialize(self.fmt, response) self.assertEqual(webob.exc.HTTPConflict.code, response.status_int) self.assertEqual(segment_exc.HostNotConnectedToAnySegment.__name__, res['NeutronError']['type']) def test_port_create_on_multiconnected_host(self): """Binding information provided, host connected to multiple segments""" network, segments, subnets = self._create_test_segments_with_subnets(2) # This host is bound to multiple hosts self._setup_host_mappings([(segments[0]['segment']['id'], 'fakehost'), (segments[1]['segment']['id'], 'fakehost')]) response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) res = self.deserialize(self.fmt, response) self.assertEqual(webob.exc.HTTPConflict.code, response.status_int) self.assertEqual(segment_exc.HostConnectedToMultipleSegments.__name__, res['NeutronError']['type']) def test_port_update_with_fixed_ips_ok_if_no_binding_host(self): """No binding host information is provided, subnets on segments""" with self.network() as network: segment = self._test_create_segment( network_id=network['network']['id'], physical_network='physnet', network_type=constants.TYPE_VLAN) # Create a port with no IP address (since there is no subnet) port = self._create_deferred_ip_port(network) # Create the subnet and try to update the port to get an IP with self.subnet(network=network, segment_id=segment['segment']['id']) as subnet: # Try requesting an IP (but the only subnet is on a segment) data = {'port': { 'fixed_ips': [{'subnet_id': subnet['subnet']['id']}]}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) # The IP is allocated since there is no binding host info any # subnet can be used for allocation. self.assertEqual(webob.exc.HTTPOk.code, response.status_int) def test_port_update_with_fixed_ips_fail_if_host_not_on_segment(self): """Binding information is provided, subnets on segments. Update to subnet on different segment fails. """ network, segments, subnets = self._create_test_segments_with_subnets(2) # Setup host mappings self._setup_host_mappings([(segments[0]['segment']['id'], 'fakehost')]) # Create a port and validate immediate ip allocation res = self._create_port_and_show(network, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) self._validate_immediate_ip_allocation(res['port']['id']) # Try requesting an new IP, but the subnet does not match host segment port_id = res['port']['id'] data = {'port': { 'fixed_ips': [{'subnet_id': subnets[1]['subnet']['id']}]}} port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) # Port update fails. self.assertEqual(webob.exc.HTTPBadRequest.code, response.status_int) def _create_port_and_show(self, network, **kwargs): response = self._create_port( self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], **kwargs) port = self.deserialize(self.fmt, response) request = self.new_show_request('ports', port['port']['id']) return self.deserialize(self.fmt, request.get_response(self.api)) def test_port_create_with_no_fixed_ips_no_ipam_on_routed_network(self): """Ports requesting no fixed_ips not deferred, even on routed net""" with self.network() as network: segment = self._test_create_segment( network_id=network['network']['id'], physical_network='physnet', network_type=constants.TYPE_VLAN) with self.subnet(network=network, segment_id=segment['segment']['id']): pass # Create an unbound port requesting no IP addresses response = self._create_port_and_show(network, fixed_ips=[]) self.assertEqual([], response['port']['fixed_ips']) self.assertEqual(ipalloc_apidef.IP_ALLOCATION_NONE, response['port'][ipalloc_apidef.IP_ALLOCATION]) def test_port_create_with_no_fixed_ips_no_ipam(self): """Ports without addresses on non-routed networks are not deferred""" with self.network() as network: with self.subnet(network=network): pass # Create an unbound port requesting no IP addresses response = self._create_port_and_show(network, fixed_ips=[]) self.assertEqual([], response['port']['fixed_ips']) self.assertEqual(ipalloc_apidef.IP_ALLOCATION_NONE, response['port'][ipalloc_apidef.IP_ALLOCATION]) def test_port_without_ip_not_deferred(self): """Ports without addresses on non-routed networks are not deferred""" with self.network() as network: pass # Create a bound port with no IP address (since there is no subnet) response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) port = self.deserialize(self.fmt, response) request = self.new_show_request('ports', port['port']['id']) response = self.deserialize(self.fmt, request.get_response(self.api)) self.assertEqual([], response['port']['fixed_ips']) self.assertEqual(ipalloc_apidef.IP_ALLOCATION_IMMEDIATE, response['port'][ipalloc_apidef.IP_ALLOCATION]) def test_port_without_ip_not_deferred_no_binding(self): """Ports without addresses on non-routed networks are not deferred""" with self.network() as network: pass # Create a unbound port with no IP address (since there is no subnet) response = self._create_port_and_show(network) self.assertEqual([], response['port']['fixed_ips']) self.assertEqual(ipalloc_apidef.IP_ALLOCATION_IMMEDIATE, response['port'][ipalloc_apidef.IP_ALLOCATION]) def test_port_update_is_host_aware(self): """Binding information is provided, subnets on segments""" with self.network() as network: segment = self._test_create_segment( network_id=network['network']['id'], physical_network='physnet', network_type=constants.TYPE_VLAN) # Map the host to the segment self._setup_host_mappings([(segment['segment']['id'], 'fakehost')]) # Create a bound port with no IP address (since there is no subnet) response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) port = self.deserialize(self.fmt, response) # Create the subnet and try to update the port to get an IP with self.subnet(network=network, segment_id=segment['segment']['id']) as subnet: self._validate_l2_adjacency(network['network']['id'], is_adjacent=False) # Try requesting an IP (but the only subnet is on a segment) data = {'port': { 'fixed_ips': [{'subnet_id': subnet['subnet']['id']}]}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) # Since port is bound and there is a mapping to segment, it succeeds. self.assertEqual(webob.exc.HTTPOk.code, response.status_int) self._assert_one_ip_in_subnet(response, subnet['subnet']['cidr']) def _validate_deferred_ip_allocation(self, port_id): request = self.new_show_request('ports', port_id) response = self.deserialize(self.fmt, request.get_response(self.api)) self.assertEqual(ipalloc_apidef.IP_ALLOCATION_DEFERRED, response['port'][ipalloc_apidef.IP_ALLOCATION]) ips = response['port']['fixed_ips'] self.assertEqual(0, len(ips)) def _validate_immediate_ip_allocation(self, port_id): request = self.new_show_request('ports', port_id) response = self.deserialize(self.fmt, request.get_response(self.api)) self.assertEqual(ipalloc_apidef.IP_ALLOCATION_IMMEDIATE, response['port'][ipalloc_apidef.IP_ALLOCATION]) ips = response['port']['fixed_ips'] self.assertNotEqual(0, len(ips)) def _create_deferred_ip_port(self, network): response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id']) port = self.deserialize(self.fmt, response) ips = port['port']['fixed_ips'] self.assertEqual(0, len(ips)) return port def test_port_update_deferred_allocation(self): """Binding information is provided on update, subnets on segments""" network, segment, subnet = self._create_test_segment_with_subnet() # Map the host to the segment self._setup_host_mappings([(segment['segment']['id'], 'fakehost')]) port = self._create_deferred_ip_port(network) self._validate_deferred_ip_allocation(port['port']['id']) # Try requesting an IP (but the only subnet is on a segment) data = {'port': {portbindings.HOST_ID: 'fakehost'}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) # Port update succeeds and allocates a new IP address. self.assertEqual(webob.exc.HTTPOk.code, response.status_int) self._assert_one_ip_in_subnet(response, subnet['subnet']['cidr']) def test_port_update_deferred_allocation_no_segments(self): """Binding information is provided, subnet created after port""" with self.network() as network: pass port = self._create_deferred_ip_port(network) # Create the subnet and try to update the port to get an IP with self.subnet(network=network): data = {'port': {portbindings.HOST_ID: 'fakehost'}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, response.status_int) res = self.deserialize(self.fmt, response) self.assertEqual(0, len(res['port']['fixed_ips'])) def test_port_update_deferred_allocation_no_ipam(self): """Binding information is provided on update. Don't allocate.""" with self.network() as network: with self.subnet(network=network): pass response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], fixed_ips=[]) port = self.deserialize(self.fmt, response) ips = port['port']['fixed_ips'] self.assertEqual(0, len(ips)) # Create the subnet and try to update the port to get an IP data = {'port': {portbindings.HOST_ID: 'fakehost'}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, response.status_int) res = self.deserialize(self.fmt, response) self.assertEqual(0, len(res['port']['fixed_ips'])) def test_port_update_deferred_allocation_no_segments_manual_alloc(self): """Binding information is provided, subnet created after port""" with self.network() as network: pass port = self._create_deferred_ip_port(network) # Create the subnet and try to update the port to get an IP with self.subnet(network=network) as subnet: data = {'port': { portbindings.HOST_ID: 'fakehost', 'fixed_ips': [{'subnet_id': subnet['subnet']['id']}]}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, response.status_int) self._assert_one_ip_in_subnet(response, subnet['subnet']['cidr']) # Do a show to be sure that only one IP is recorded port_req = self.new_show_request('ports', port_id) response = port_req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, response.status_int) self._assert_one_ip_in_subnet(response, subnet['subnet']['cidr']) def test_port_update_deferred_allocation_no_segments_empty_alloc(self): """Binding information is provided, subnet created after port""" with self.network() as network: pass port = self._create_deferred_ip_port(network) # Create the subnet and update the port but specify no IPs with self.subnet(network=network): data = {'port': { portbindings.HOST_ID: 'fakehost', 'fixed_ips': []}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, response.status_int) res = self.deserialize(self.fmt, response) # Since I specifically requested no IP addresses, I shouldn't get one. self.assertEqual(0, len(res['port']['fixed_ips'])) def test_port_update_deferred_allocation_no_host_mapping(self): """Binding information is provided on update, subnets on segments""" network, segment, subnet = self._create_test_segment_with_subnet() port = self._create_deferred_ip_port(network) self._validate_deferred_ip_allocation(port['port']['id']) # Try requesting an IP (but the only subnet is on a segment) data = {'port': {portbindings.HOST_ID: 'fakehost'}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) res = self.deserialize(self.fmt, response) # Gets conflict because it can't map the host to a segment self.assertEqual(webob.exc.HTTPConflict.code, response.status_int) self.assertEqual(segment_exc.HostNotConnectedToAnySegment.__name__, res['NeutronError']['type']) def test_port_update_deferred_allocation_multiple_host_mapping(self): """Binding information is provided on update, subnets on segments""" network, segments, _s = self._create_test_segments_with_subnets(2) port = self._create_deferred_ip_port(network) self._validate_deferred_ip_allocation(port['port']['id']) # This host is bound to multiple segments self._setup_host_mappings([(segments[0]['segment']['id'], 'fakehost'), (segments[1]['segment']['id'], 'fakehost')]) # Try requesting an IP (but the only subnet is on a segment) data = {'port': {portbindings.HOST_ID: 'fakehost'}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) res = self.deserialize(self.fmt, response) # Gets conflict because it can't map the host to a segment self.assertEqual(webob.exc.HTTPConflict.code, response.status_int) self.assertEqual(segment_exc.HostConnectedToMultipleSegments.__name__, res['NeutronError']['type']) def test_port_update_allocate_no_segments(self): """Binding information is provided, subnet created after port""" with self.network() as network: pass # Create a bound port with no IP address (since there is not subnet) port = self._create_deferred_ip_port(network) # Create the subnet and try to update the port to get an IP with self.subnet(network=network) as subnet: # Try requesting an IP (but the only subnet is on a segment) data = {'port': { 'fixed_ips': [{'subnet_id': subnet['subnet']['id']}]}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) # Since port is bound and there is a mapping to segment, it succeeds. self.assertEqual(webob.exc.HTTPOk.code, response.status_int) self._assert_one_ip_in_subnet(response, subnet['subnet']['cidr']) def test_port_update_deferred_allocation_no_ips(self): """Binding information is provided on update, subnets on segments""" network, segments, subnets = self._create_test_segments_with_subnets(2) self._setup_host_mappings([(segments[0]['segment']['id'], 'fakehost2'), (segments[1]['segment']['id'], 'fakehost')]) port = self._create_deferred_ip_port(network) # Update the subnet on the second segment to be out of IPs subnet_data = {'subnet': {'allocation_pools': []}} subnet_req = self.new_update_request('subnets', subnet_data, subnets[1]['subnet']['id']) subnet_response = subnet_req.get_response(self.api) res = self.deserialize(self.fmt, subnet_response) # Try requesting an IP (but the subnet ran out of ips) data = {'port': {portbindings.HOST_ID: 'fakehost'}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) res = self.deserialize(self.fmt, response) # Since port is bound and there is a mapping to segment, it succeeds. self.assertEqual(webob.exc.HTTPConflict.code, response.status_int) self.assertEqual(n_exc.IpAddressGenerationFailure.__name__, res['NeutronError']['type']) def test_port_update_fails_if_host_on_wrong_segment(self): """Update a port with existing IPs to a host where they don't work""" network, segments, subnets = self._create_test_segments_with_subnets(2) self._setup_host_mappings([(segments[0]['segment']['id'], 'fakehost2'), (segments[1]['segment']['id'], 'fakehost')]) # Create a bound port with an IP address response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) self._assert_one_ip_in_subnet(response, subnets[1]['subnet']['cidr']) port = self.deserialize(self.fmt, response) # Now, try to update binding to a host on the other segment data = {'port': {portbindings.HOST_ID: 'fakehost2'}} port_req = self.new_update_request('ports', data, port['port']['id']) response = port_req.get_response(self.api) # It fails since the IP address isn't compatible with the new segment self.assertEqual(webob.exc.HTTPConflict.code, response.status_int) def test_port_update_fails_if_host_on_good_segment(self): """Update a port with existing IPs to a host where they don't work""" network, segments, subnets = self._create_test_segments_with_subnets(2) self._setup_host_mappings([(segments[0]['segment']['id'], 'fakehost2'), (segments[1]['segment']['id'], 'fakehost1'), (segments[1]['segment']['id'], 'fakehost')]) # Create a bound port with an IP address response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) self._assert_one_ip_in_subnet(response, subnets[1]['subnet']['cidr']) port = self.deserialize(self.fmt, response) # Now, try to update binding to another host in same segment data = {'port': {portbindings.HOST_ID: 'fakehost1'}} port_req = self.new_update_request('ports', data, port['port']['id']) response = port_req.get_response(self.api) # Since the new host is in the same segment, it succeeds. self.assertEqual(webob.exc.HTTPOk.code, response.status_int) def test_port_update_deferred_allocation_binding_info_and_new_mac(self): """Binding information and new mac address is provided on update""" network, segment, subnet = self._create_test_segment_with_subnet() # Map the host to the segment self._setup_host_mappings([(segment['segment']['id'], 'fakehost')]) port = self._create_deferred_ip_port(network) self._validate_deferred_ip_allocation(port['port']['id']) # Try requesting an IP (but the only subnet is on a segment) data = {'port': {portbindings.HOST_ID: 'fakehost', 'mac_address': '00:00:00:00:00:01'}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) # Port update succeeds and allocates a new IP address. self.assertEqual(webob.exc.HTTPOk.code, response.status_int) self._assert_one_ip_in_subnet(response, subnet['subnet']['cidr']) class TestSegmentAwareIpamML2(TestSegmentAwareIpam): VLAN_MIN = 200 VLAN_MAX = 209 def setUp(self): # NOTE(mlavalle): ml2_type_vlan requires to be registered before used. # This piece was refactored and removed from .config, so it causes # a problem, when tests are executed with pdb. # There is no problem when tests are running without debugger. driver_type.register_ml2_drivers_vlan_opts() cfg.CONF.set_override( 'network_vlan_ranges', ['physnet:%s:%s' % (self.VLAN_MIN, self.VLAN_MAX), 'physnet0:%s:%s' % (self.VLAN_MIN, self.VLAN_MAX), 'physnet1:%s:%s' % (self.VLAN_MIN, self.VLAN_MAX), 'physnet2:%s:%s' % (self.VLAN_MIN, self.VLAN_MAX)], group='ml2_type_vlan') super(TestSegmentAwareIpamML2, self).setUp(plugin='ml2') def test_segmentation_id_stored_in_db(self): network, segment, subnet = self._create_test_segment_with_subnet() self.assertTrue(self.VLAN_MIN <= segment['segment']['segmentation_id'] <= self.VLAN_MAX) retrieved_segment = self._show('segments', segment['segment']['id']) self.assertEqual(segment['segment']['segmentation_id'], retrieved_segment['segment']['segmentation_id']) class TestNovaSegmentNotifier(SegmentAwareIpamTestCase): _mechanism_drivers = ['openvswitch', 'logger'] def setUp(self): cfg.CONF.set_override('mechanism_drivers', self._mechanism_drivers, group='ml2') cfg.CONF.set_override('network_vlan_ranges', ['physnet:200:209', 'physnet0:200:209', 'physnet1:200:209', 'physnet2:200:209'], group='ml2_type_vlan') super(TestNovaSegmentNotifier, self).setUp(plugin='ml2') # Need notifier here self.patch_notifier.stop() self._mock_keystone_auth() self.segments_plugin = directory.get_plugin(ext_segment.SEGMENTS) nova_updater = self.segments_plugin.nova_updater nova_updater.p_client = mock.MagicMock() self.mock_p_client = nova_updater.p_client nova_updater.n_client = mock.MagicMock() self.mock_n_client = nova_updater.n_client self.batch_notifier = nova_updater.batch_notifier self.batch_notifier._waiting_to_send = True def _mock_keystone_auth(self): # Use to remove MissingAuthPlugin exception when notifier is needed self.mock_load_auth_p = mock.patch( 'keystoneauth1.loading.load_auth_from_conf_options') self.mock_load_auth = self.mock_load_auth_p.start() self.mock_request_p = mock.patch( 'keystoneauth1.session.Session.request') self.mock_request = self.mock_request_p.start() def _calculate_inventory_total_and_reserved(self, subnet): total = 0 reserved = 0 allocation_pools = subnet.get('allocation_pools') or [] for pool in allocation_pools: total += int(netaddr.IPAddress(pool['end']) - netaddr.IPAddress(pool['start'])) + 1 if total: if subnet['gateway_ip']: total += 1 reserved += 1 if subnet['enable_dhcp']: reserved += 1 return total, reserved def _assert_inventory_creation(self, segment_id, aggregate, subnet): self.batch_notifier._notify() self.mock_p_client.get_inventory.assert_called_with( segment_id, seg_plugin.IPV4_RESOURCE_CLASS) self.mock_p_client.update_inventory.assert_not_called() name = seg_plugin.SEGMENT_NAME_STUB % segment_id resource_provider = {'name': name, 'uuid': segment_id} self.mock_p_client.create_resource_provider.assert_called_with( resource_provider) self.mock_n_client.aggregates.create.assert_called_with(name, None) self.mock_p_client.associate_aggregates.assert_called_with( segment_id, [aggregate.uuid]) self.mock_n_client.aggregates.add_host.assert_called_with(aggregate.id, 'fakehost') total, reserved = self._calculate_inventory_total_and_reserved( subnet['subnet']) inventory, _ = self._get_inventory(total, reserved) self.mock_p_client.create_inventory.assert_called_with( segment_id, inventory) self.assertEqual( inventory['total'], self.mock_p_client.create_inventory.call_args[0][1]['total']) self.assertEqual( inventory['reserved'], self.mock_p_client.create_inventory.call_args[0][1]['reserved']) self.mock_p_client.reset_mock() self.mock_p_client.get_inventory.side_effect = None self.mock_n_client.reset_mock() def _test_first_subnet_association_with_segment(self, cidr='10.0.0.0/24', allocation_pools=None): network, segment = self._create_test_network_and_segment() segment_id = segment['segment']['id'] self._setup_host_mappings([(segment_id, 'fakehost')]) self.mock_p_client.get_inventory.side_effect = ( neutron_exc.PlacementResourceProviderNotFound( resource_provider=segment_id, resource_class=seg_plugin.IPV4_RESOURCE_CLASS)) aggregate = mock.MagicMock() aggregate.uuid = uuidutils.generate_uuid() aggregate.id = 1 self.mock_n_client.aggregates.create.return_value = aggregate subnet = self._create_test_subnet_with_segment( network, segment, cidr=cidr, allocation_pools=allocation_pools) self._assert_inventory_creation(segment_id, aggregate, subnet) return network, segment, subnet def test_first_subnet_association_with_segment(self): self._test_first_subnet_association_with_segment() def _assert_inventory_update(self, segment_id, inventory, subnet=None, original_subnet=None): self.batch_notifier._notify() self.mock_p_client.get_inventory.assert_called_with( segment_id, seg_plugin.IPV4_RESOURCE_CLASS) original_total = original_reserved = total = reserved = 0 if original_subnet: original_total, original_reserved = ( self._calculate_inventory_total_and_reserved(original_subnet)) if subnet: total, reserved = self._calculate_inventory_total_and_reserved( subnet) inventory['total'] += total - original_total inventory['reserved'] += reserved - original_reserved self.mock_p_client.update_inventory.assert_called_with(segment_id, inventory, seg_plugin.IPV4_RESOURCE_CLASS) self.assertEqual( inventory['total'], self.mock_p_client.update_inventory.call_args[0][1]['total']) self.assertEqual( inventory['reserved'], self.mock_p_client.update_inventory.call_args[0][1]['reserved']) self.mock_p_client.reset_mock() self.mock_n_client.reset_mock() def _get_inventory(self, total, reserved): inventory = {'total': total, 'reserved': reserved, 'min_unit': 1, 'max_unit': 1, 'step_size': 1, 'allocation_ratio': 1.0, 'resource_class': seg_plugin.IPV4_RESOURCE_CLASS} return inventory, copy.deepcopy(inventory) def _test_second_subnet_association_with_segment(self): network, segment, first_subnet = ( self._test_first_subnet_association_with_segment()) segment_id = segment['segment']['id'] # Associate an IPv6 subnet with the segment self._create_test_subnet_with_segment(network, segment) first_total, first_reserved = ( self._calculate_inventory_total_and_reserved( first_subnet['subnet'])) inventory, original_inventory = self._get_inventory(first_total, first_reserved) self.mock_p_client.get_inventory.return_value = inventory second_subnet = self._create_test_subnet_with_segment( network, segment, cidr='10.0.1.0/24') self._assert_inventory_update(segment_id, original_inventory, subnet=second_subnet['subnet']) return segment_id, first_subnet, second_subnet def test_second_subnet_association_with_segment(self): self._test_second_subnet_association_with_segment() def test_delete_last_ipv4_subnet(self): network, segment, subnet = ( self._test_first_subnet_association_with_segment()) # Associate an IPv6 subnet with the segment self._create_test_subnet_with_segment(network, segment) segment_id = segment['segment']['id'] aggregate = mock.MagicMock() aggregate.uuid = uuidutils.generate_uuid() aggregate.id = 1 aggregate.hosts = ['fakehost1'] self.mock_p_client.list_aggregates.return_value = { 'aggregates': [aggregate.uuid]} self.mock_n_client.aggregates.list.return_value = [aggregate] self.mock_n_client.aggregates.get_details.return_value = aggregate self._delete('subnets', subnet['subnet']['id']) self.batch_notifier._notify() self._assert_inventory_delete(segment_id, aggregate) def _assert_inventory_delete(self, segment_id, aggregate): self.mock_p_client.list_aggregates.assert_called_with(segment_id) self.assertEqual(1, self.mock_n_client.aggregates.list.call_count) self.mock_n_client.aggregates.get_details.assert_called_with( aggregate.id) calls = [mock.call(aggregate.id, host) for host in aggregate.hosts] self.mock_n_client.aggregates.remove_host.assert_has_calls(calls) self.mock_n_client.aggregates.delete.assert_called_with(aggregate.id) self.mock_p_client.delete_resource_provider.assert_called_with( segment_id) self.mock_p_client.reset_mock() self.mock_n_client.reset_mock() def test_delete_ipv4_subnet(self): segment_id, first_subnet, second_subnet = ( self._test_second_subnet_association_with_segment()) first_total, first_reserved = ( self._calculate_inventory_total_and_reserved( first_subnet['subnet'])) second_total, second_reserved = ( self._calculate_inventory_total_and_reserved( second_subnet['subnet'])) inventory, original_inventory = self._get_inventory( first_total + second_total, first_reserved + second_reserved) self.mock_p_client.get_inventory.return_value = inventory self._delete('subnets', first_subnet['subnet']['id']) self._assert_inventory_update(segment_id, original_inventory, original_subnet=first_subnet['subnet']) def _test_update_ipv4_subnet_allocation_pools(self, allocation_pools, new_allocation_pools): network, segment, original_subnet = ( self._test_first_subnet_association_with_segment( cidr='10.0.0.0/24', allocation_pools=allocation_pools)) segment_id = segment['segment']['id'] self.mock_p_client.reset_mock() self.mock_n_client.reset_mock() total, reserved = self._calculate_inventory_total_and_reserved( original_subnet['subnet']) inventory, original_inventory = self._get_inventory(total, reserved) self.mock_p_client.get_inventory.return_value = inventory subnet_data = {'subnet': {'allocation_pools': new_allocation_pools}} subnet_req = self.new_update_request('subnets', subnet_data, original_subnet['subnet']['id']) subnet = self.deserialize(self.fmt, subnet_req.get_response(self.api)) self._assert_inventory_update( segment_id, original_inventory, subnet=subnet['subnet'], original_subnet=original_subnet['subnet']) def test_update_ipv4_subnet_expand_allocation_pool(self): self._test_update_ipv4_subnet_allocation_pools( [{'start': '10.0.0.2', 'end': '10.0.0.100'}], [{'start': '10.0.0.2', 'end': '10.0.0.254'}]) def test_update_ipv4_subnet_add_allocation_pool(self): self._test_update_ipv4_subnet_allocation_pools( [{'start': '10.0.0.2', 'end': '10.0.0.100'}], [{'start': '10.0.0.2', 'end': '10.0.0.100'}, {'start': '10.0.0.200', 'end': '10.0.0.254'}]) def test_update_ipv4_subnet_contract_allocation_pool(self): self._test_update_ipv4_subnet_allocation_pools( [{'start': '10.0.0.2', 'end': '10.0.0.254'}], [{'start': '10.0.0.2', 'end': '10.0.0.100'}]) def test_update_ipv4_subnet_remove_allocation_pool(self): self._test_update_ipv4_subnet_allocation_pools( [{'start': '10.0.0.2', 'end': '10.0.0.100'}, {'start': '10.0.0.200', 'end': '10.0.0.254'}], [{'start': '10.0.0.2', 'end': '10.0.0.100'}]) def _test_update_ipv4_subnet_delete_allocation_pools(self): segment_id, first_subnet, second_subnet = ( self._test_second_subnet_association_with_segment()) first_total, first_reserved = ( self._calculate_inventory_total_and_reserved( first_subnet['subnet'])) second_total, second_reserved = ( self._calculate_inventory_total_and_reserved( second_subnet['subnet'])) inventory, original_inventory = self._get_inventory( first_total + second_total, first_reserved + second_reserved) self.mock_p_client.get_inventory.return_value = inventory subnet_data = {'subnet': {'allocation_pools': []}} subnet_req = self.new_update_request('subnets', subnet_data, first_subnet['subnet']['id']) subnet_req.get_response(self.api) self._assert_inventory_update(segment_id, original_inventory, original_subnet=first_subnet['subnet']) return segment_id, second_subnet def test_update_ipv4_subnet_delete_allocation_pools(self): self._test_update_ipv4_subnet_delete_allocation_pools() def test_update_ipv4_subnet_delete_restore_last_allocation_pool(self): segment_id, subnet = ( self._test_update_ipv4_subnet_delete_allocation_pools()) self.mock_p_client.reset_mock() self.mock_n_client.reset_mock() allocation_pools = subnet['subnet']['allocation_pools'] aggregate = mock.MagicMock() aggregate.uuid = uuidutils.generate_uuid() aggregate.id = 1 aggregate.hosts = ['fakehost1'] self.mock_p_client.list_aggregates.return_value = { 'aggregates': [aggregate.uuid]} self.mock_n_client.aggregates.list.return_value = [aggregate] self.mock_n_client.aggregates.get_details.return_value = aggregate subnet_data = {'subnet': {'allocation_pools': []}} self._update('subnets', subnet['subnet']['id'], subnet_data) self.batch_notifier._notify() self._assert_inventory_delete(segment_id, aggregate) self.mock_p_client.get_inventory.side_effect = ( neutron_exc.PlacementResourceProviderNotFound( resource_provider=segment_id, resource_class=seg_plugin.IPV4_RESOURCE_CLASS)) aggregate.hosts = [] self.mock_n_client.aggregates.create.return_value = aggregate subnet_data = {'subnet': {'allocation_pools': allocation_pools}} subnet = self._update('subnets', subnet['subnet']['id'], subnet_data) self._assert_inventory_creation(segment_id, aggregate, subnet) def test_add_host_to_segment_aggregate(self): db.subscribe() network, segment, first_subnet = ( self._test_first_subnet_association_with_segment()) segment_id = segment['segment']['id'] aggregate = mock.MagicMock() aggregate.uuid = uuidutils.generate_uuid() aggregate.id = 1 aggregate.hosts = ['fakehost1'] self.mock_p_client.list_aggregates.return_value = { 'aggregates': [aggregate.uuid]} self.mock_n_client.aggregates.list.return_value = [aggregate] host = 'otherfakehost' helpers.register_ovs_agent(host=host, bridge_mappings={'physnet': 'br-eth-1'}, plugin=self.plugin, start_flag=True) self.batch_notifier._notify() self.mock_p_client.list_aggregates.assert_called_with(segment_id) self.assertEqual(1, self.mock_n_client.aggregates.list.call_count) self.mock_n_client.aggregates.add_host.assert_called_with(aggregate.id, host) def test_add_host_to_non_existent_segment_aggregate(self): self._mock_keystone_auth() db.subscribe() network, segment, first_subnet = ( self._test_first_subnet_association_with_segment()) with mock.patch.object(seg_plugin.LOG, 'info') as log: segment_id = segment['segment']['id'] aggregate = mock.MagicMock() aggregate.uuid = uuidutils.generate_uuid() aggregate.id = 1 aggregate.hosts = ['fakehost1'] self.mock_p_client.list_aggregates.side_effect = ( neutron_exc.PlacementAggregateNotFound( resource_provider=segment_id)) self.mock_n_client.aggregates.list.return_value = [aggregate] host = 'otherfakehost' helpers.register_ovs_agent(host=host, bridge_mappings={'physnet': 'br-eth-1'}, plugin=self.plugin, start_flag=True) self.batch_notifier._notify() self.mock_p_client.list_aggregates.assert_called_with(segment_id) self.assertTrue(log.called) self.mock_n_client.aggregates.add_host.assert_not_called() def test_add_host_segment_aggregate_conflict(self): db.subscribe() network, segment, first_subnet = ( self._test_first_subnet_association_with_segment()) with mock.patch.object(seg_plugin.LOG, 'info') as log: segment_id = segment['segment']['id'] aggregate = mock.MagicMock() aggregate.uuid = uuidutils.generate_uuid() aggregate.id = 1 aggregate.hosts = ['fakehost1'] self.mock_p_client.list_aggregates.return_value = { 'aggregates': [aggregate.uuid]} self.mock_n_client.aggregates.add_host.side_effect = ( nova_exc.Conflict(nova_exc.Conflict.http_status)) self.mock_n_client.aggregates.list.return_value = [aggregate] host = 'otherfakehost' helpers.register_ovs_agent(host=host, bridge_mappings={'physnet': 'br-eth-1'}, plugin=self.plugin, start_flag=True) self.batch_notifier._notify() self.mock_p_client.list_aggregates.assert_called_with(segment_id) self.mock_n_client.aggregates.add_host.assert_called_with( aggregate.id, host) self.assertTrue(log.called) def _assert_inventory_update_port(self, segment_id, inventory, num_fixed_ips): inventory['reserved'] += num_fixed_ips self.mock_p_client.get_inventory.assert_called_with( segment_id, seg_plugin.IPV4_RESOURCE_CLASS) self.mock_p_client.update_inventory.assert_called_with(segment_id, inventory, seg_plugin.IPV4_RESOURCE_CLASS) self.assertEqual( inventory['total'], self.mock_p_client.update_inventory.call_args[0][1]['total']) self.assertEqual( inventory['reserved'], self.mock_p_client.update_inventory.call_args[0][1]['reserved']) self.mock_p_client.reset_mock() self.mock_n_client.reset_mock() def _create_test_port(self, network_id, tenant_id, subnet, **kwargs): port = self._make_port(self.fmt, network_id, tenant_id=tenant_id, arg_list=(portbindings.HOST_ID,), **kwargs) self.batch_notifier._notify() return port def _test_create_port(self, **kwargs): network, segment, subnet = ( self._test_first_subnet_association_with_segment()) total, reserved = self._calculate_inventory_total_and_reserved( subnet['subnet']) inventory, original_inventory = self._get_inventory(total, reserved) self.mock_p_client.get_inventory.return_value = inventory port = self._create_test_port(network['network']['id'], network['network']['tenant_id'], subnet, **kwargs) return segment['segment']['id'], original_inventory, port def test_create_bound_port(self): kwargs = {portbindings.HOST_ID: 'fakehost'} segment_id, original_inventory, _ = self._test_create_port(**kwargs) self._assert_inventory_update_port(segment_id, original_inventory, 1) def test_create_bound_port_compute_owned(self): kwargs = {portbindings.HOST_ID: 'fakehost', 'device_owner': constants.DEVICE_OWNER_COMPUTE_PREFIX} self._test_create_port(**kwargs) self.mock_p_client.get_inventory.assert_not_called() self.mock_p_client.update_inventory.assert_not_called() def test_create_bound_port_dhcp_owned(self): kwargs = {portbindings.HOST_ID: 'fakehost', 'device_owner': constants.DEVICE_OWNER_DHCP} self._test_create_port(**kwargs) self.mock_p_client.get_inventory.assert_not_called() self.mock_p_client.update_inventory.assert_not_called() def test_create_unbound_port(self): self._test_create_port() self.mock_p_client.get_inventory.assert_not_called() self.mock_p_client.update_inventory.assert_not_called() def test_delete_bound_port(self): kwargs = {portbindings.HOST_ID: 'fakehost'} segment_id, before_create_inventory, port = self._test_create_port( **kwargs) self.mock_p_client.reset_mock() inventory, original_inventory = self._get_inventory( before_create_inventory['total'], before_create_inventory['reserved'] + 1) self.mock_p_client.get_inventory.return_value = inventory self._delete('ports', port['port']['id']) self.batch_notifier._notify() self._assert_inventory_update_port(segment_id, original_inventory, -1) def _create_port_for_update_test(self, num_fixed_ips=1, dhcp_owned=False, compute_owned=False): segment_id, first_subnet, second_subnet = ( self._test_second_subnet_association_with_segment()) first_total, first_reserved = ( self._calculate_inventory_total_and_reserved( first_subnet['subnet'])) second_total, second_reserved = ( self._calculate_inventory_total_and_reserved( second_subnet['subnet'])) inventory, original_inventory = self._get_inventory( first_total + second_total, first_reserved + second_reserved) self.mock_p_client.get_inventory.return_value = inventory kwargs = {portbindings.HOST_ID: 'fakehost', 'fixed_ips': [{'subnet_id': first_subnet['subnet']['id']}]} created_fixed_ips = num_fixed_ips if num_fixed_ips > 1: kwargs['fixed_ips'].append( {'subnet_id': second_subnet['subnet']['id']}) if dhcp_owned: kwargs['device_owner'] = constants.DEVICE_OWNER_DHCP if compute_owned: kwargs['device_owner'] = constants.DEVICE_OWNER_COMPUTE_PREFIX port = self._create_test_port(first_subnet['subnet']['network_id'], first_subnet['subnet']['tenant_id'], first_subnet, **kwargs) if dhcp_owned or compute_owned: self.mock_p_client.get_inventory.assert_not_called() self.mock_p_client.update_inventory.assert_not_called() else: self._assert_inventory_update_port(segment_id, original_inventory, created_fixed_ips) return first_subnet, second_subnet, port def _port_update(self, first_subnet, second_subnet, fixed_ips_subnets, port, reserved_increment_before=1, reserved_increment_after=1, dhcp_owned=False, compute_owned=False): first_total, first_reserved = ( self._calculate_inventory_total_and_reserved( first_subnet['subnet'])) second_total, second_reserved = ( self._calculate_inventory_total_and_reserved( second_subnet['subnet'])) inventory, original_inventory = self._get_inventory( first_total + second_total, first_reserved + second_reserved + reserved_increment_before) self.mock_p_client.get_inventory.return_value = inventory port_data = {'port': {'device_owner': ''}} if fixed_ips_subnets: port_data['port']['fixed_ips'] = [] for subnet in fixed_ips_subnets: port_data['port']['fixed_ips'].append( {'subnet_id': subnet['subnet']['id']}) if dhcp_owned: port_data['port']['device_owner'] = constants.DEVICE_OWNER_DHCP if compute_owned: port_data['port']['device_owner'] = ( constants.DEVICE_OWNER_COMPUTE_PREFIX) self._update('ports', port['port']['id'], port_data) self.batch_notifier._notify() self._assert_inventory_update_port( first_subnet['subnet']['segment_id'], original_inventory, reserved_increment_after) def test_update_port_add_fixed_ip(self): first_subnet, second_subnet, port = self._create_port_for_update_test() self._port_update(first_subnet, second_subnet, [first_subnet, second_subnet], port) def test_update_port_remove_fixed_ip(self): first_subnet, second_subnet, port = self._create_port_for_update_test( num_fixed_ips=2) self._port_update(first_subnet, second_subnet, [first_subnet], port, reserved_increment_before=2, reserved_increment_after=-1) def test_update_port_change_to_dhcp_owned(self): first_subnet, second_subnet, port = self._create_port_for_update_test() self._port_update(first_subnet, second_subnet, [], port, reserved_increment_after=-1, dhcp_owned=True) def test_update_port_change_to_no_dhcp_owned(self): first_subnet, second_subnet, port = self._create_port_for_update_test( dhcp_owned=True) self._port_update(first_subnet, second_subnet, [], port, reserved_increment_before=0, reserved_increment_after=1) def test_update_port_change_to_compute_owned(self): first_subnet, second_subnet, port = self._create_port_for_update_test() self._port_update(first_subnet, second_subnet, [], port, reserved_increment_after=-1, compute_owned=True) def test_update_port_change_to_no_compute_owned(self): first_subnet, second_subnet, port = self._create_port_for_update_test( compute_owned=True) self._port_update(first_subnet, second_subnet, [], port, reserved_increment_before=0, reserved_increment_after=1) def test_placement_api_inventory_update_conflict(self): with mock.patch.object(seg_plugin.LOG, 'debug') as log_debug: with mock.patch.object(seg_plugin.LOG, 'error') as log_error: event = seg_plugin.Event(mock.ANY, mock.ANY, total=1, reserved=0) inventory, original_inventory = self._get_inventory(100, 2) self.mock_p_client.get_inventory.return_value = inventory self.mock_p_client.update_inventory.side_effect = ( neutron_exc.PlacementInventoryUpdateConflict( resource_provider=mock.ANY, resource_class=seg_plugin.IPV4_RESOURCE_CLASS)) self.segments_plugin.nova_updater._update_nova_inventory(event) self.assertEqual(seg_plugin.MAX_INVENTORY_UPDATE_RETRIES, self.mock_p_client.get_inventory.call_count) self.assertEqual( seg_plugin.MAX_INVENTORY_UPDATE_RETRIES, self.mock_p_client.update_inventory.call_count) self.assertEqual( seg_plugin.MAX_INVENTORY_UPDATE_RETRIES, log_debug.call_count) self.assertTrue(log_error.called) def test_placement_api_not_available(self): with mock.patch.object(seg_plugin.LOG, 'debug') as log: event = seg_plugin.Event( self.segments_plugin.nova_updater._update_nova_inventory, mock.ANY, total=1, reserved=0) self.mock_p_client.get_inventory.side_effect = ( neutron_exc.PlacementEndpointNotFound()) self.segments_plugin.nova_updater._send_notifications([event]) self.assertTrue(log.called) class TestDhcpAgentSegmentScheduling(HostSegmentMappingTestCase): _mechanism_drivers = ['openvswitch', 'logger'] mock_path = 'neutron.services.segments.db.update_segment_host_mapping' block_dhcp_notifier = False def setUp(self): super(TestDhcpAgentSegmentScheduling, self).setUp() self.dhcp_agent_db = agentschedulers_db.DhcpAgentSchedulerDbMixin() self.ctx = context.get_admin_context() def _test_create_network_and_segment(self, phys_net): with self.network() as net: network = net['network'] segment = self._test_create_segment(network_id=network['id'], physical_network=phys_net, segmentation_id=200, network_type='vlan') dhcp_agents = self.dhcp_agent_db.get_dhcp_agents_hosting_networks( self.ctx, [network['id']]) self.assertEqual(0, len(dhcp_agents)) return network, segment['segment'] def _test_create_subnet(self, network, segment, cidr=None, enable_dhcp=True): cidr = cidr or '10.0.0.0/24' ip_version = 4 with self.subnet(network={'network': network}, segment_id=segment['id'], ip_version=ip_version, cidr=cidr, enable_dhcp=enable_dhcp) as subnet: pass return subnet['subnet'] def _register_dhcp_agents(self, hosts=None): hosts = hosts or [DHCP_HOSTA, DHCP_HOSTB] for host in hosts: helpers.register_dhcp_agent(host) def test_network_scheduling_on_segment_creation(self): self._register_dhcp_agents() self._test_create_network_and_segment('phys_net1') def test_segment_scheduling_no_host_mapping(self): self._register_dhcp_agents() network, segment = self._test_create_network_and_segment('phys_net1') self._test_create_subnet(network, segment) dhcp_agents = self.dhcp_agent_db.get_dhcp_agents_hosting_networks( self.ctx, [network['id']]) self.assertEqual(0, len(dhcp_agents)) def test_segment_scheduling_with_host_mapping(self): phys_net1 = 'phys_net1' self._register_dhcp_agents() network, segment = self._test_create_network_and_segment(phys_net1) self._register_agent(DHCP_HOSTA, mappings={phys_net1: 'br-eth-1'}, plugin=self.plugin) self._test_create_subnet(network, segment) dhcp_agents = self.dhcp_agent_db.get_dhcp_agents_hosting_networks( self.ctx, [network['id']]) self.assertEqual(1, len(dhcp_agents)) self.assertEqual(DHCP_HOSTA, dhcp_agents[0]['host']) def test_segment_scheduling_with_multiple_host_mappings(self): phys_net1 = 'phys_net1' phys_net2 = 'phys_net2' self._register_dhcp_agents([DHCP_HOSTA, DHCP_HOSTB, 'MEHA', 'MEHB']) network, segment1 = self._test_create_network_and_segment(phys_net1) segment2 = self._test_create_segment(network_id=network['id'], physical_network=phys_net2, segmentation_id=200, network_type='vlan')['segment'] self._register_agent(DHCP_HOSTA, mappings={phys_net1: 'br-eth-1'}, plugin=self.plugin) self._register_agent(DHCP_HOSTB, mappings={phys_net2: 'br-eth-1'}, plugin=self.plugin) self._test_create_subnet(network, segment1) self._test_create_subnet(network, segment2, cidr='11.0.0.0/24') dhcp_agents = self.dhcp_agent_db.get_dhcp_agents_hosting_networks( self.ctx, [network['id']]) self.assertEqual(2, len(dhcp_agents)) agent_hosts = [agent['host'] for agent in dhcp_agents] self.assertIn(DHCP_HOSTA, agent_hosts) self.assertIn(DHCP_HOSTB, agent_hosts) class PlacementAPIClientTestCase(base.DietTestCase): """Test the Placement API client.""" def setUp(self): super(PlacementAPIClientTestCase, self).setUp() self.mock_load_auth_p = mock.patch( 'keystoneauth1.loading.load_auth_from_conf_options') self.mock_load_auth = self.mock_load_auth_p.start() self.mock_request_p = mock.patch( 'keystoneauth1.session.Session.request') self.mock_request = self.mock_request_p.start() self.client = placement_client.PlacementAPIClient() @mock.patch('keystoneauth1.session.Session') @mock.patch('keystoneauth1.loading.load_auth_from_conf_options') def test_constructor(self, load_auth_mock, ks_sess_mock): placement_client.PlacementAPIClient() load_auth_mock.assert_called_once_with(cfg.CONF, 'placement') ks_sess_mock.assert_called_once_with(auth=load_auth_mock.return_value, cert=None, timeout=None, verify=True) def test_create_resource_provider(self): expected_payload = 'fake_resource_provider' self.client.create_resource_provider(expected_payload) expected_url = '/resource_providers' self.mock_request.assert_called_once_with( expected_url, 'POST', endpoint_filter={'region_name': mock.ANY, 'interface': 'public', 'service_type': 'placement'}, json=expected_payload) def test_delete_resource_provider(self): rp_uuid = uuidutils.generate_uuid() self.client.delete_resource_provider(rp_uuid) expected_url = '/resource_providers/%s' % rp_uuid self.mock_request.assert_called_once_with( expected_url, 'DELETE', endpoint_filter={'region_name': mock.ANY, 'interface': 'public', 'service_type': 'placement'}) def test_create_inventory(self): expected_payload = 'fake_inventory' rp_uuid = uuidutils.generate_uuid() self.client.create_inventory(rp_uuid, expected_payload) expected_url = '/resource_providers/%s/inventories' % rp_uuid self.mock_request.assert_called_once_with( expected_url, 'POST', endpoint_filter={'region_name': mock.ANY, 'interface': 'public', 'service_type': 'placement'}, json=expected_payload) def test_get_inventory(self): rp_uuid = uuidutils.generate_uuid() resource_class = 'fake_resource_class' self.client.get_inventory(rp_uuid, resource_class) expected_url = '/resource_providers/%s/inventories/%s' % ( rp_uuid, resource_class) self.mock_request.assert_called_once_with( expected_url, 'GET', endpoint_filter={'region_name': mock.ANY, 'interface': 'public', 'service_type': 'placement'}) def _test_get_inventory_not_found(self, details, expected_exception): rp_uuid = uuidutils.generate_uuid() resource_class = 'fake_resource_class' self.mock_request.side_effect = ks_exc.NotFound(details=details) self.assertRaises(expected_exception, self.client.get_inventory, rp_uuid, resource_class) def test_get_inventory_not_found_no_resource_provider(self): self._test_get_inventory_not_found( "No resource provider with uuid", neutron_exc.PlacementResourceProviderNotFound) def test_get_inventory_not_found_no_inventory(self): self._test_get_inventory_not_found( "No inventory of class", neutron_exc.PlacementInventoryNotFound) def test_get_inventory_not_found_unknown_cause(self): self._test_get_inventory_not_found("Unknown cause", ks_exc.NotFound) def test_update_inventory(self): expected_payload = 'fake_inventory' rp_uuid = uuidutils.generate_uuid() resource_class = 'fake_resource_class' self.client.update_inventory(rp_uuid, expected_payload, resource_class) expected_url = '/resource_providers/%s/inventories/%s' % ( rp_uuid, resource_class) self.mock_request.assert_called_once_with( expected_url, 'PUT', endpoint_filter={'region_name': mock.ANY, 'interface': 'public', 'service_type': 'placement'}, json=expected_payload) def test_update_inventory_conflict(self): rp_uuid = uuidutils.generate_uuid() expected_payload = 'fake_inventory' resource_class = 'fake_resource_class' self.mock_request.side_effect = ks_exc.Conflict self.assertRaises(neutron_exc.PlacementInventoryUpdateConflict, self.client.update_inventory, rp_uuid, expected_payload, resource_class) def test_associate_aggregates(self): expected_payload = 'fake_aggregates' rp_uuid = uuidutils.generate_uuid() self.client.associate_aggregates(rp_uuid, expected_payload) expected_url = '/resource_providers/%s/aggregates' % rp_uuid self.mock_request.assert_called_once_with( expected_url, 'PUT', endpoint_filter={'region_name': mock.ANY, 'interface': 'public', 'service_type': 'placement'}, json=expected_payload, headers={'openstack-api-version': 'placement 1.1'}) def test_list_aggregates(self): rp_uuid = uuidutils.generate_uuid() self.client.list_aggregates(rp_uuid) expected_url = '/resource_providers/%s/aggregates' % rp_uuid self.mock_request.assert_called_once_with( expected_url, 'GET', endpoint_filter={'region_name': mock.ANY, 'interface': 'public', 'service_type': 'placement'}, headers={'openstack-api-version': 'placement 1.1'}) def test_list_aggregates_not_found(self): rp_uuid = uuidutils.generate_uuid() self.mock_request.side_effect = ks_exc.NotFound self.assertRaises(neutron_exc.PlacementAggregateNotFound, self.client.list_aggregates, rp_uuid) def test_placement_api_not_found(self): rp_uuid = uuidutils.generate_uuid() self.mock_request.side_effect = ks_exc.EndpointNotFound self.assertRaises(neutron_exc.PlacementEndpointNotFound, self.client.list_aggregates, rp_uuid) class TestSegmentHostMappingNoStore( test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def setUp(self): driver_type.register_ml2_drivers_vlan_opts() cfg.CONF.set_override('network_vlan_ranges', ['phys_net1'], group='ml2_type_vlan') cfg.CONF.set_override('service_plugins', []) super(TestSegmentHostMappingNoStore, self).setUp( plugin='neutron.plugins.ml2.plugin.Ml2Plugin') # set to None for simulating server start db._USER_CONFIGURED_SEGMENT_PLUGIN = None db.subscribe() @mock.patch('neutron.services.segments.db.update_segment_host_mapping') @mock.patch('neutron.services.segments.db.map_segment_to_hosts') def test_no_segmenthostmapping_when_disable_segment( self, mock_map_segment_to_hosts, mock_update_segment_mapping): with self.network( arg_list=('provider:network_type', 'provider:physical_network', 'provider:segmentation_id'), **{'provider:network_type': 'vlan', 'provider:physical_network': 'phys_net1', 'provider:segmentation_id': '400'}) as network: network['network'] mock_map_segment_to_hosts.assert_not_called() host1 = 'test_host' physical_network = 'phys_net1' helpers.register_ovs_agent( host=host1, bridge_mappings={physical_network: 'br-eth-1'}, plugin=self.plugin) mock_update_segment_mapping.assert_not_called() neutron-12.1.1/neutron/tests/unit/extensions/test_securitygroup.py0000664000175000017500000027304313553660047025701 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import contextlib import copy import mock from neutron_lib.api import validators from neutron_lib import constants as const from neutron_lib import context from neutron_lib.db import constants as db_const from neutron_lib.plugins import directory from oslo_config import cfg import oslo_db.exception as exc import testtools import webob.exc from neutron.common import exceptions as n_exc from neutron.db import api as db_api from neutron.db import db_base_plugin_v2 from neutron.db import securitygroups_db from neutron.extensions import securitygroup as ext_sg from neutron.extensions import standardattrdescription from neutron.tests import base from neutron.tests.unit.db import test_db_base_plugin_v2 DB_PLUGIN_KLASS = ('neutron.tests.unit.extensions.test_securitygroup.' 'SecurityGroupTestPlugin') LONG_NAME_OK = 'x' * (db_const.NAME_FIELD_SIZE) LONG_NAME_NG = 'x' * (db_const.NAME_FIELD_SIZE + 1) class SecurityGroupTestExtensionManager(object): def get_resources(self): # The description of security_group_rules will be added by extending # standardattrdescription. But as API router will not be initialized # in test code, manually add it. ext_res = (standardattrdescription.Standardattrdescription(). get_extended_resources("2.0")) if ext_sg.SECURITYGROUPRULES in ext_res: existing_sg_rule_attr_map = ( ext_sg.RESOURCE_ATTRIBUTE_MAP[ext_sg.SECURITYGROUPRULES]) sg_rule_attr_desc = ext_res[ext_sg.SECURITYGROUPRULES] existing_sg_rule_attr_map.update(sg_rule_attr_desc) return ext_sg.Securitygroup.get_resources() def get_actions(self): return [] def get_request_extensions(self): return [] class SecurityGroupsTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def _build_security_group(self, name, description, **kwargs): data = { 'security_group': { 'name': name, 'tenant_id': kwargs.get( 'tenant_id', test_db_base_plugin_v2.TEST_TENANT_ID), 'description': description}} return data def _create_security_group_response(self, fmt, data, **kwargs): security_group_req = self.new_create_request('security-groups', data, fmt) if (kwargs.get('set_context') and 'tenant_id' in kwargs): # create a specific auth context for this request security_group_req.environ['neutron.context'] = ( context.Context('', kwargs['tenant_id'])) return security_group_req.get_response(self.ext_api) def _create_security_group(self, fmt, name, description, **kwargs): data = self._build_security_group(name, description, **kwargs) return self._create_security_group_response(fmt, data, **kwargs) def _build_security_group_rule( self, security_group_id, direction, proto, port_range_min=None, port_range_max=None, remote_ip_prefix=None, remote_group_id=None, tenant_id=test_db_base_plugin_v2.TEST_TENANT_ID, ethertype=const.IPv4): data = {'security_group_rule': {'security_group_id': security_group_id, 'direction': direction, 'protocol': proto, 'ethertype': ethertype, 'tenant_id': tenant_id}} if port_range_min: data['security_group_rule']['port_range_min'] = port_range_min if port_range_max: data['security_group_rule']['port_range_max'] = port_range_max if remote_ip_prefix: data['security_group_rule']['remote_ip_prefix'] = remote_ip_prefix if remote_group_id: data['security_group_rule']['remote_group_id'] = remote_group_id return data def _create_security_group_rule(self, fmt, rules, **kwargs): security_group_rule_req = self.new_create_request( 'security-group-rules', rules, fmt) if (kwargs.get('set_context') and 'tenant_id' in kwargs): # create a specific auth context for this request security_group_rule_req.environ['neutron.context'] = ( context.Context('', kwargs['tenant_id'])) elif kwargs.get('admin_context'): security_group_rule_req.environ['neutron.context'] = ( context.Context(user_id='admin', tenant_id='admin-tenant', is_admin=True)) return security_group_rule_req.get_response(self.ext_api) def _make_security_group(self, fmt, name, description, **kwargs): res = self._create_security_group(fmt, name, description, **kwargs) if res.status_int >= webob.exc.HTTPBadRequest.code: raise webob.exc.HTTPClientError(code=res.status_int) return self.deserialize(fmt, res) def _make_security_group_rule(self, fmt, rules, **kwargs): res = self._create_security_group_rule(self.fmt, rules) if res.status_int >= webob.exc.HTTPBadRequest.code: raise webob.exc.HTTPClientError(code=res.status_int) return self.deserialize(fmt, res) @contextlib.contextmanager def security_group(self, name='webservers', description='webservers', fmt=None): if not fmt: fmt = self.fmt security_group = self._make_security_group(fmt, name, description) yield security_group @contextlib.contextmanager def security_group_rule(self, security_group_id='4cd70774-cc67-4a87-9b39-7' 'd1db38eb087', direction='ingress', protocol=const.PROTO_NAME_TCP, port_range_min='22', port_range_max='22', remote_ip_prefix=None, remote_group_id=None, fmt=None, ethertype=const.IPv4): if not fmt: fmt = self.fmt rule = self._build_security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_ip_prefix, remote_group_id, ethertype=ethertype) security_group_rule = self._make_security_group_rule(self.fmt, rule) yield security_group_rule def _delete_default_security_group_egress_rules(self, security_group_id): """Deletes default egress rules given a security group ID.""" res = self._list( 'security-group-rules', query_params='security_group_id=%s' % security_group_id) for r in res['security_group_rules']: if (r['direction'] == 'egress' and not r['port_range_max'] and not r['port_range_min'] and not r['protocol'] and not r['remote_ip_prefix']): self._delete('security-group-rules', r['id']) def _assert_sg_rule_has_kvs(self, security_group_rule, expected_kvs): """Asserts that the sg rule has expected key/value pairs passed in as expected_kvs dictionary """ for k, v in expected_kvs.items(): self.assertEqual(security_group_rule[k], v) class SecurityGroupTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, securitygroups_db.SecurityGroupDbMixin): """Test plugin that implements necessary calls on create/delete port for associating ports with security groups. """ __native_pagination_support = True __native_sorting_support = True supported_extension_aliases = ["security-group"] def create_port(self, context, port): tenant_id = port['port']['tenant_id'] default_sg = self._ensure_default_security_group(context, tenant_id) if not validators.is_attr_set(port['port'].get(ext_sg.SECURITYGROUPS)): port['port'][ext_sg.SECURITYGROUPS] = [default_sg] with db_api.context_manager.writer.using(context): sgids = self._get_security_groups_on_port(context, port) port = super(SecurityGroupTestPlugin, self).create_port(context, port) self._process_port_create_security_group(context, port, sgids) return port def update_port(self, context, id, port): with db_api.context_manager.writer.using(context): if ext_sg.SECURITYGROUPS in port['port']: port['port'][ext_sg.SECURITYGROUPS] = ( self._get_security_groups_on_port(context, port)) # delete the port binding and read it with the new rules self._delete_port_security_group_bindings(context, id) port['port']['id'] = id self._process_port_create_security_group( context, port['port'], port['port'].get(ext_sg.SECURITYGROUPS)) port = super(SecurityGroupTestPlugin, self).update_port( context, id, port) return port def create_network(self, context, network): self._ensure_default_security_group(context, network['network']['tenant_id']) return super(SecurityGroupTestPlugin, self).create_network(context, network) def get_ports(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): sorts = sorts or [] neutron_lports = super(SecurityGroupTestPlugin, self).get_ports( context, filters, sorts=sorts, limit=limit, marker=marker, page_reverse=page_reverse) return neutron_lports class SecurityGroupDBTestCase(SecurityGroupsTestCase): def setUp(self, plugin=None, ext_mgr=None): self._backup = copy.deepcopy(ext_sg.RESOURCE_ATTRIBUTE_MAP) self.addCleanup(self._restore) plugin = plugin or DB_PLUGIN_KLASS ext_mgr = ext_mgr or SecurityGroupTestExtensionManager() super(SecurityGroupDBTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr) def _restore(self): ext_sg.RESOURCE_ATTRIBUTE_MAP = self._backup class TestSecurityGroups(SecurityGroupDBTestCase): def test_create_security_group(self): name = 'webservers' description = 'my webservers' keys = [('name', name,), ('description', description)] with self.security_group(name, description) as security_group: for k, v, in keys: self.assertEqual(security_group['security_group'][k], v) # Verify that default egress rules have been created sg_rules = security_group['security_group']['security_group_rules'] self.assertEqual(2, len(sg_rules)) v4_rules = [r for r in sg_rules if r['ethertype'] == const.IPv4] self.assertEqual(1, len(v4_rules)) v4_rule = v4_rules[0] expected = {'direction': 'egress', 'ethertype': const.IPv4, 'remote_group_id': None, 'remote_ip_prefix': None, 'protocol': None, 'port_range_max': None, 'port_range_min': None} self._assert_sg_rule_has_kvs(v4_rule, expected) v6_rules = [r for r in sg_rules if r['ethertype'] == const.IPv6] self.assertEqual(1, len(v6_rules)) v6_rule = v6_rules[0] expected = {'direction': 'egress', 'ethertype': const.IPv6, 'remote_group_id': None, 'remote_ip_prefix': None, 'protocol': None, 'port_range_max': None, 'port_range_min': None} self._assert_sg_rule_has_kvs(v6_rule, expected) def test_create_security_group_bulk(self): rule1 = self._build_security_group("sg_1", "sec_grp_1") rule2 = self._build_security_group("sg_2", "sec_grp_2") rules = {'security_groups': [rule1['security_group'], rule2['security_group']]} res = self._create_security_group_response(self.fmt, rules) ret = self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) self.assertEqual(2, len(ret['security_groups'])) def test_skip_duplicate_default_sg_error(self): num_called = [0] original_func = self.plugin.create_security_group def side_effect(context, security_group, default_sg): # can't always raise, or create_security_group will hang self.assertTrue(default_sg) self.assertLess(num_called[0], 2) num_called[0] += 1 ret = original_func(context, security_group, default_sg) if num_called[0] == 1: return ret # make another call to cause an exception. # NOTE(yamamoto): raising the exception by ourselves # doesn't update the session state appropriately. self.assertRaises(exc.DBDuplicateEntry, original_func, context, security_group, default_sg) with mock.patch.object(SecurityGroupTestPlugin, 'create_security_group', side_effect=side_effect): self.plugin.create_network( context.get_admin_context(), {'network': {'name': 'foo', 'admin_state_up': True, 'shared': False, 'tenant_id': 'bar'}}) def test_update_security_group(self): with self.security_group() as sg: data = {'security_group': {'name': 'new_name', 'description': 'new_desc'}} req = self.new_update_request('security-groups', data, sg['security_group']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(data['security_group']['name'], res['security_group']['name']) self.assertEqual(data['security_group']['description'], res['security_group']['description']) def test_update_security_group_name_to_default_fail(self): with self.security_group() as sg: data = {'security_group': {'name': 'default', 'description': 'new_desc'}} req = self.new_update_request('security-groups', data, sg['security_group']['id']) req.environ['neutron.context'] = context.Context('', 'somebody') res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_default_security_group_name_fail(self): with self.network(): res = self.new_list_request('security-groups') sg = self.deserialize(self.fmt, res.get_response(self.ext_api)) data = {'security_group': {'name': 'new_name', 'description': 'new_desc'}} req = self.new_update_request('security-groups', data, sg['security_groups'][0]['id']) req.environ['neutron.context'] = context.Context('', 'somebody') res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_update_default_security_group_with_description(self): with self.network(): res = self.new_list_request('security-groups') sg = self.deserialize(self.fmt, res.get_response(self.ext_api)) data = {'security_group': {'description': 'new_desc'}} req = self.new_update_request('security-groups', data, sg['security_groups'][0]['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(data['security_group']['description'], res['security_group']['description']) def test_update_security_group_with_max_name_length(self): with self.security_group() as sg: data = {'security_group': {'name': LONG_NAME_OK, 'description': 'new_desc'}} req = self.new_update_request('security-groups', data, sg['security_group']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(data['security_group']['name'], res['security_group']['name']) self.assertEqual(data['security_group']['description'], res['security_group']['description']) def test_update_security_group_with_too_long_name(self): with self.security_group() as sg: data = {'security_group': {'name': LONG_NAME_NG, 'description': 'new_desc'}} req = self.new_update_request('security-groups', data, sg['security_group']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_update_security_group_with_boolean_type_name(self): with self.security_group() as sg: data = {'security_group': {'name': True, 'description': 'new_desc'}} req = self.new_update_request('security-groups', data, sg['security_group']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_check_default_security_group_description(self): with self.network(): res = self.new_list_request('security-groups') sg = self.deserialize(self.fmt, res.get_response(self.ext_api)) self.assertEqual('Default security group', sg['security_groups'][0]['description']) def test_default_security_group(self): with self.network(): res = self.new_list_request('security-groups') groups = self.deserialize(self.fmt, res.get_response(self.ext_api)) self.assertEqual(1, len(groups['security_groups'])) def test_create_default_security_group_fail(self): name = 'default' description = 'my webservers' res = self._create_security_group(self.fmt, name, description) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_default_security_group_check_case_insensitive(self): name = 'DEFAULT' description = 'my webservers' res = self._create_security_group(self.fmt, name, description) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_security_group_with_max_name_length(self): description = 'my webservers' res = self._create_security_group(self.fmt, LONG_NAME_OK, description) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) def test_create_security_group_with_too_long_name(self): description = 'my webservers' res = self._create_security_group(self.fmt, LONG_NAME_NG, description) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_with_boolean_type_name(self): description = 'my webservers' res = self._create_security_group(self.fmt, True, description) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_list_security_groups(self): with self.security_group(name='sg1', description='sg') as v1,\ self.security_group(name='sg2', description='sg') as v2,\ self.security_group(name='sg3', description='sg') as v3: security_groups = (v1, v2, v3) self._test_list_resources('security-group', security_groups, query_params='description=sg') def test_list_security_groups_with_sort(self): with self.security_group(name='sg1', description='sg') as sg1,\ self.security_group(name='sg2', description='sg') as sg2,\ self.security_group(name='sg3', description='sg') as sg3: self._test_list_with_sort('security-group', (sg3, sg2, sg1), [('name', 'desc')], query_params='description=sg') def test_list_security_groups_with_pagination(self): with self.security_group(name='sg1', description='sg') as sg1,\ self.security_group(name='sg2', description='sg') as sg2,\ self.security_group(name='sg3', description='sg') as sg3: self._test_list_with_pagination('security-group', (sg1, sg2, sg3), ('name', 'asc'), 2, 2, query_params='description=sg') def test_list_security_groups_with_pagination_reverse(self): with self.security_group(name='sg1', description='sg') as sg1,\ self.security_group(name='sg2', description='sg') as sg2,\ self.security_group(name='sg3', description='sg') as sg3: self._test_list_with_pagination_reverse( 'security-group', (sg1, sg2, sg3), ('name', 'asc'), 2, 2, query_params='description=sg') def test_create_security_group_rule_ethertype_invalid_as_number(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] ethertype = 2 rule = self._build_security_group_rule( security_group_id, 'ingress', const.PROTO_NAME_TCP, '22', '22', None, None, ethertype=ethertype) res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_ethertype_invalid_for_protocol(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] rule = self._build_security_group_rule( security_group_id, 'ingress', const.PROTO_NAME_IPV6_ICMP) res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_invalid_ip_prefix(self): name = 'webservers' description = 'my webservers' for bad_prefix in ['bad_ip', 256, "2001:db8:a::123/129", '172.30./24']: with self.security_group(name, description) as sg: sg_id = sg['security_group']['id'] remote_ip_prefix = bad_prefix rule = self._build_security_group_rule( sg_id, 'ingress', const.PROTO_NAME_TCP, '22', '22', remote_ip_prefix) res = self._create_security_group_rule(self.fmt, rule) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_invalid_ethertype_for_prefix(self): name = 'webservers' description = 'my webservers' test_addr = {'192.168.1.1/24': 'IPv6', '2001:db8:1234::/48': 'IPv4', '192.168.2.1/24': 'BadEthertype'} for remote_ip_prefix, ethertype in test_addr.items(): with self.security_group(name, description) as sg: sg_id = sg['security_group']['id'] rule = self._build_security_group_rule( sg_id, 'ingress', const.PROTO_NAME_TCP, '22', '22', remote_ip_prefix, None, ethertype=ethertype) res = self._create_security_group_rule(self.fmt, rule) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_with_unmasked_prefix(self): name = 'webservers' description = 'my webservers' addr = {'10.1.2.3': {'mask': '32', 'ethertype': 'IPv4'}, 'fe80::2677:3ff:fe7d:4c': {'mask': '128', 'ethertype': 'IPv6'}} for ip in addr: with self.security_group(name, description) as sg: sg_id = sg['security_group']['id'] ethertype = addr[ip]['ethertype'] remote_ip_prefix = ip rule = self._build_security_group_rule( sg_id, 'ingress', const.PROTO_NAME_TCP, '22', '22', remote_ip_prefix, None, ethertype=ethertype) res = self._create_security_group_rule(self.fmt, rule) self.assertEqual(res.status_int, 201) res_sg = self.deserialize(self.fmt, res) prefix = res_sg['security_group_rule']['remote_ip_prefix'] self.assertEqual(prefix, '%s/%s' % (ip, addr[ip]['mask'])) def test_create_security_group_rule_tcp_protocol_as_number(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] protocol = const.PROTO_NUM_TCP # TCP rule = self._build_security_group_rule( security_group_id, 'ingress', protocol, '22', '22') res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) def test_create_security_group_rule_protocol_as_number(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] protocol = 2 rule = self._build_security_group_rule( security_group_id, 'ingress', protocol) res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) def test_create_security_group_rule_protocol_as_number_with_port(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] protocol = 111 rule = self._build_security_group_rule( security_group_id, 'ingress', protocol, '70') res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) def test_create_security_group_rule_protocol_as_number_range(self): # This is a SG rule with a port range, but treated as a single # port since min/max are the same. name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] protocol = 111 rule = self._build_security_group_rule( security_group_id, 'ingress', protocol, '70', '70') res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) def test_create_security_group_rule_protocol_as_number_range_bad(self): # Only certain protocols support a SG rule with a port range name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] protocol = 111 rule = self._build_security_group_rule( security_group_id, 'ingress', protocol, '70', '71') res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_case_insensitive(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] direction = "ingress" remote_ip_prefix = "10.0.0.0/24" protocol = 'TCP' port_range_min = 22 port_range_max = 22 ethertype = 'ipV4' with self.security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_ip_prefix, ethertype=ethertype) as rule: # the lower case value will be return self.assertEqual(rule['security_group_rule']['protocol'], protocol.lower()) self.assertEqual(rule['security_group_rule']['ethertype'], const.IPv4) def test_get_security_group(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: remote_group_id = sg['security_group']['id'] res = self.new_show_request('security-groups', remote_group_id) security_group_id = sg['security_group']['id'] direction = "ingress" remote_ip_prefix = "10.0.0.0/24" protocol = const.PROTO_NAME_TCP port_range_min = 22 port_range_max = 22 keys = [('remote_ip_prefix', remote_ip_prefix), ('security_group_id', security_group_id), ('direction', direction), ('protocol', protocol), ('port_range_min', port_range_min), ('port_range_max', port_range_max)] with self.security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_ip_prefix): group = self.deserialize( self.fmt, res.get_response(self.ext_api)) sg_rule = group['security_group']['security_group_rules'] self.assertEqual(group['security_group']['id'], remote_group_id) self.assertEqual(3, len(sg_rule)) sg_rule = [r for r in sg_rule if r['direction'] == 'ingress'] for k, v, in keys: self.assertEqual(sg_rule[0][k], v) def test_get_security_group_empty_rules(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: remote_group_id = sg['security_group']['id'] self._delete_default_security_group_egress_rules( remote_group_id) res = self.new_show_request('security-groups', remote_group_id) group = self.deserialize( self.fmt, res.get_response(self.ext_api)) sg_rule = group['security_group']['security_group_rules'] self.assertEqual(group['security_group']['id'], remote_group_id) self.assertEqual(0, len(sg_rule)) def test_get_security_group_empty_rules_id_only(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: remote_group_id = sg['security_group']['id'] self._delete_default_security_group_egress_rules( remote_group_id) res = self.new_show_request('security-groups', remote_group_id, fields=['id']) group = self.deserialize( self.fmt, res.get_response(self.ext_api)) secgroup = group['security_group'] self.assertFalse('security_group_rules' in secgroup) self.assertEqual(group['security_group']['id'], remote_group_id) # This test case checks that admins from a different tenant can add rules # as themselves. This is an odd behavior, with some weird GET semantics, # but this test is checking that we don't break that old behavior, at least # until we make a conscious choice to do so. def test_create_security_group_rules_admin_tenant(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: # Add a couple normal rules rule = self._build_security_group_rule( sg['security_group']['id'], "ingress", const.PROTO_NAME_TCP, port_range_min=22, port_range_max=22, remote_ip_prefix="10.0.0.0/24", ethertype=const.IPv4) self._make_security_group_rule(self.fmt, rule) rule = self._build_security_group_rule( sg['security_group']['id'], "ingress", const.PROTO_NAME_TCP, port_range_min=22, port_range_max=22, remote_ip_prefix="10.0.1.0/24", ethertype=const.IPv4) self._make_security_group_rule(self.fmt, rule) # Let's add a rule as admin, with a different tenant_id. The # results of this call are arguably a bug, but it is past behavior. rule = self._build_security_group_rule( sg['security_group']['id'], "ingress", const.PROTO_NAME_TCP, port_range_min=22, port_range_max=22, remote_ip_prefix="10.0.2.0/24", ethertype=const.IPv4, tenant_id='admin-tenant') self._make_security_group_rule(self.fmt, rule, admin_context=True) # Now, let's make sure all the rules are there, with their odd # tenant_id behavior. res = self.new_list_request('security-groups') sgs = self.deserialize(self.fmt, res.get_response(self.ext_api)) for sg in sgs['security_groups']: if sg['name'] == "webservers": rules = sg['security_group_rules'] self.assertEqual(len(rules), 5) self.assertNotEqual(rules[3]['tenant_id'], 'admin-tenant') self.assertEqual(rules[4]['tenant_id'], 'admin-tenant') def test_get_security_group_on_port_from_wrong_tenant(self): plugin = directory.get_plugin() if not hasattr(plugin, '_get_security_groups_on_port'): self.skipTest("plugin doesn't use the mixin with this method") neutron_context = context.get_admin_context() res = self._create_security_group(self.fmt, 'webservers', 'webservers', tenant_id='bad_tenant') sg1 = self.deserialize(self.fmt, res) with testtools.ExpectedException(ext_sg.SecurityGroupNotFound): plugin._get_security_groups_on_port( neutron_context, {'port': {'security_groups': [sg1['security_group']['id']], 'tenant_id': 'tenant'}} ) def test_delete_security_group(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: remote_group_id = sg['security_group']['id'] self._delete('security-groups', remote_group_id, webob.exc.HTTPNoContent.code) def test_delete_default_security_group_admin(self): with self.network(): res = self.new_list_request('security-groups') sg = self.deserialize(self.fmt, res.get_response(self.ext_api)) self._delete('security-groups', sg['security_groups'][0]['id'], webob.exc.HTTPNoContent.code) def test_delete_default_security_group_nonadmin(self): with self.network(): res = self.new_list_request('security-groups') sg = self.deserialize(self.fmt, res.get_response(self.ext_api)) neutron_context = context.Context( '', test_db_base_plugin_v2.TEST_TENANT_ID) self._delete('security-groups', sg['security_groups'][0]['id'], webob.exc.HTTPConflict.code, neutron_context=neutron_context) def test_security_group_list_creates_default_security_group(self): neutron_context = context.Context( '', test_db_base_plugin_v2.TEST_TENANT_ID) sg = self._list('security-groups', neutron_context=neutron_context).get('security_groups') self.assertEqual(1, len(sg)) def test_security_group_port_create_creates_default_security_group(self): res = self._create_network(self.fmt, 'net1', True, tenant_id='not_admin', set_context=True) net1 = self.deserialize(self.fmt, res) res = self._create_port(self.fmt, net1['network']['id'], tenant_id='not_admin', set_context=True) sg = self._list('security-groups').get('security_groups') self.assertEqual(1, len(sg)) def test_default_security_group_rules(self): with self.network(): res = self.new_list_request('security-groups') groups = self.deserialize(self.fmt, res.get_response(self.ext_api)) self.assertEqual(len(groups['security_groups']), 1) security_group_id = groups['security_groups'][0]['id'] res = self.new_list_request('security-group-rules') rules = self.deserialize(self.fmt, res.get_response(self.ext_api)) self.assertEqual(len(rules['security_group_rules']), 4) # Verify default rule for v4 egress sg_rules = rules['security_group_rules'] rules = [ r for r in sg_rules if r['direction'] == 'egress' and r['ethertype'] == const.IPv4 ] self.assertEqual(1, len(rules)) v4_egress = rules[0] expected = {'direction': 'egress', 'ethertype': const.IPv4, 'remote_group_id': None, 'remote_ip_prefix': None, 'protocol': None, 'port_range_max': None, 'port_range_min': None} self._assert_sg_rule_has_kvs(v4_egress, expected) # Verify default rule for v6 egress rules = [ r for r in sg_rules if r['direction'] == 'egress' and r['ethertype'] == const.IPv6 ] self.assertEqual(1, len(rules)) v6_egress = rules[0] expected = {'direction': 'egress', 'ethertype': const.IPv6, 'remote_group_id': None, 'remote_ip_prefix': None, 'protocol': None, 'port_range_max': None, 'port_range_min': None} self._assert_sg_rule_has_kvs(v6_egress, expected) # Verify default rule for v4 ingress rules = [ r for r in sg_rules if r['direction'] == 'ingress' and r['ethertype'] == const.IPv4 ] self.assertEqual(1, len(rules)) v4_ingress = rules[0] expected = {'direction': 'ingress', 'ethertype': const.IPv4, 'remote_group_id': security_group_id, 'remote_ip_prefix': None, 'protocol': None, 'port_range_max': None, 'port_range_min': None} self._assert_sg_rule_has_kvs(v4_ingress, expected) # Verify default rule for v6 ingress rules = [ r for r in sg_rules if r['direction'] == 'ingress' and r['ethertype'] == const.IPv6 ] self.assertEqual(1, len(rules)) v6_ingress = rules[0] expected = {'direction': 'ingress', 'ethertype': const.IPv6, 'remote_group_id': security_group_id, 'remote_ip_prefix': None, 'protocol': None, 'port_range_max': None, 'port_range_min': None} self._assert_sg_rule_has_kvs(v6_ingress, expected) def test_create_security_group_rule_remote_ip_prefix(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] direction = "ingress" remote_ip_prefix = "10.0.0.0/24" protocol = const.PROTO_NAME_TCP port_range_min = 22 port_range_max = 22 keys = [('remote_ip_prefix', remote_ip_prefix), ('security_group_id', security_group_id), ('direction', direction), ('protocol', protocol), ('port_range_min', port_range_min), ('port_range_max', port_range_max)] with self.security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_ip_prefix) as rule: for k, v, in keys: self.assertEqual(rule['security_group_rule'][k], v) def test_create_security_group_rule_group_id(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: with self.security_group(name, description) as sg2: security_group_id = sg['security_group']['id'] direction = "ingress" remote_group_id = sg2['security_group']['id'] protocol = const.PROTO_NAME_TCP port_range_min = 22 port_range_max = 22 keys = [('remote_group_id', remote_group_id), ('security_group_id', security_group_id), ('direction', direction), ('protocol', protocol), ('port_range_min', port_range_min), ('port_range_max', port_range_max)] with self.security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_group_id=remote_group_id ) as rule: for k, v, in keys: self.assertEqual(rule['security_group_rule'][k], v) def test_create_security_group_rule_icmp_with_type_and_code(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] direction = "ingress" remote_ip_prefix = "10.0.0.0/24" protocol = const.PROTO_NAME_ICMP # port_range_min (ICMP type) is greater than port_range_max # (ICMP code) in order to confirm min <= max port check is # not called for ICMP. port_range_min = 8 port_range_max = 5 keys = [('remote_ip_prefix', remote_ip_prefix), ('security_group_id', security_group_id), ('direction', direction), ('protocol', protocol), ('port_range_min', port_range_min), ('port_range_max', port_range_max)] with self.security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_ip_prefix) as rule: for k, v, in keys: self.assertEqual(rule['security_group_rule'][k], v) def test_create_security_group_rule_icmp_with_type_only(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] direction = "ingress" remote_ip_prefix = "10.0.0.0/24" protocol = const.PROTO_NAME_ICMP # ICMP type port_range_min = 8 # ICMP code port_range_max = None keys = [('remote_ip_prefix', remote_ip_prefix), ('security_group_id', security_group_id), ('direction', direction), ('protocol', protocol), ('port_range_min', port_range_min), ('port_range_max', port_range_max)] with self.security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_ip_prefix) as rule: for k, v, in keys: self.assertEqual(rule['security_group_rule'][k], v) def test_create_security_group_rule_icmpv6_with_type_only(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] direction = "ingress" ethertype = const.IPv6 remote_ip_prefix = "2001::f401:56ff:fefe:d3dc/128" protocol = const.PROTO_NAME_IPV6_ICMP # ICMPV6 type port_range_min = const.ICMPV6_TYPE_RA # ICMPV6 code port_range_max = None keys = [('remote_ip_prefix', remote_ip_prefix), ('security_group_id', security_group_id), ('direction', direction), ('ethertype', ethertype), ('protocol', protocol), ('port_range_min', port_range_min), ('port_range_max', port_range_max)] with self.security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_ip_prefix, None, None, ethertype) as rule: for k, v, in keys: self.assertEqual(rule['security_group_rule'][k], v) def test_create_security_group_rule_icmpv6_legacy_protocol_name(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] direction = "ingress" ethertype = const.IPv6 remote_ip_prefix = "2001::f401:56ff:fefe:d3dc/128" protocol = const.PROTO_NAME_IPV6_ICMP_LEGACY keys = [('remote_ip_prefix', remote_ip_prefix), ('security_group_id', security_group_id), ('direction', direction), ('ethertype', ethertype), ('protocol', protocol)] with self.security_group_rule(security_group_id, direction, protocol, None, None, remote_ip_prefix, None, None, ethertype) as rule: for k, v, in keys: self.assertEqual(rule['security_group_rule'][k], v) def test_create_security_group_source_group_ip_and_ip_prefix(self): security_group_id = "4cd70774-cc67-4a87-9b39-7d1db38eb087" direction = "ingress" remote_ip_prefix = "10.0.0.0/24" protocol = const.PROTO_NAME_TCP port_range_min = 22 port_range_max = 22 remote_group_id = "9cd70774-cc67-4a87-9b39-7d1db38eb087" rule = self._build_security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_ip_prefix, remote_group_id) res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_bad_security_group_id(self): security_group_id = "4cd70774-cc67-4a87-9b39-7d1db38eb087" direction = "ingress" remote_ip_prefix = "10.0.0.0/24" protocol = const.PROTO_NAME_TCP port_range_min = 22 port_range_max = 22 rule = self._build_security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_ip_prefix) res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_create_security_group_rule_bad_tenant(self): with self.security_group() as sg: rule = {'security_group_rule': {'security_group_id': sg['security_group']['id'], 'direction': 'ingress', 'protocol': const.PROTO_NAME_TCP, 'port_range_min': '22', 'port_range_max': '22', 'tenant_id': "bad_tenant"}} res = self._create_security_group_rule(self.fmt, rule, tenant_id='bad_tenant', set_context=True) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_create_security_group_rule_bad_tenant_remote_group_id(self): with self.security_group() as sg: res = self._create_security_group(self.fmt, 'webservers', 'webservers', tenant_id='bad_tenant') sg2 = self.deserialize(self.fmt, res) rule = {'security_group_rule': {'security_group_id': sg2['security_group']['id'], 'direction': 'ingress', 'protocol': const.PROTO_NAME_TCP, 'port_range_min': '22', 'port_range_max': '22', 'tenant_id': 'bad_tenant', 'remote_group_id': sg['security_group']['id']}} res = self._create_security_group_rule(self.fmt, rule, tenant_id='bad_tenant', set_context=True) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_create_security_group_rule_bad_tenant_security_group_rule(self): with self.security_group() as sg: res = self._create_security_group(self.fmt, 'webservers', 'webservers', tenant_id='bad_tenant') self.deserialize(self.fmt, res) rule = {'security_group_rule': {'security_group_id': sg['security_group']['id'], 'direction': 'ingress', 'protocol': const.PROTO_NAME_TCP, 'port_range_min': '22', 'port_range_max': '22', 'tenant_id': 'bad_tenant'}} res = self._create_security_group_rule(self.fmt, rule, tenant_id='bad_tenant', set_context=True) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_create_security_group_rule_bad_remote_group_id(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] remote_group_id = "4cd70774-cc67-4a87-9b39-7d1db38eb087" direction = "ingress" protocol = const.PROTO_NAME_TCP port_range_min = 22 port_range_max = 22 rule = self._build_security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_group_id=remote_group_id) res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_create_security_group_rule_duplicate_rules(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id) as sgr: rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '22', '22') res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) self.assertIn(sgr['security_group_rule']['id'], res.json['NeutronError']['message']) def test_create_security_group_rule_duplicate_rules_diff_desc(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id) as sgr: rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '22', '22') rule['security_group_rule']['description'] = "description" res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) self.assertIn(sgr['security_group_rule']['id'], res.json['NeutronError']['message']) def test_create_security_group_rule_duplicate_rules_proto_name_num(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id): rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '22', '22') self._create_security_group_rule(self.fmt, rule) rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NUM_TCP, '22', '22') res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_security_group_rule_duplicate_rules_proto_num_name(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id): rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NUM_UDP, '50', '100') self._create_security_group_rule(self.fmt, rule) rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_UDP, '50', '100') res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_security_group_rule_min_port_greater_max(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id): for protocol in [const.PROTO_NAME_TCP, const.PROTO_NAME_UDP, const.PROTO_NUM_TCP, const.PROTO_NUM_UDP]: rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', protocol, '50', '22') res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_ports_but_no_protocol(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id): rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', None, '22', '22') res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_port_range_min_only(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id): rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '22', None) res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_port_range_max_only(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id): rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, None, '22') res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_icmp_type_too_big(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id): rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_ICMP, '256', None) res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_icmp_code_too_big(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id): rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_ICMP, '8', '256') res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_icmp_with_code_only(self): name = 'webservers' description = 'my webservers' with self.security_group(name, description) as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id): for code in ['2', '0']: rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_ICMP, None, code) res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_list_ports_security_group(self): with self.network() as n: with self.subnet(n): self._create_port(self.fmt, n['network']['id']) req = self.new_list_request('ports') res = req.get_response(self.api) ports = self.deserialize(self.fmt, res) port = ports['ports'][0] self.assertEqual(len(port[ext_sg.SECURITYGROUPS]), 1) self._delete('ports', port['id']) def test_list_security_group_rules(self): with self.security_group(name='sg') as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id, direction='egress', port_range_min=22, port_range_max=22) as sgr1,\ self.security_group_rule(security_group_id, direction='egress', port_range_min=23, port_range_max=23) as sgr2,\ self.security_group_rule(security_group_id, direction='egress', port_range_min=24, port_range_max=24) as sgr3: # Delete default rules as they would fail the following # assertion at the end. self._delete_default_security_group_egress_rules( security_group_id) q = 'direction=egress&security_group_id=' + security_group_id self._test_list_resources('security-group-rule', [sgr1, sgr2, sgr3], query_params=q) def test_list_security_group_rules_with_sort(self): with self.security_group(name='sg') as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id, direction='egress', port_range_min=22, port_range_max=22) as sgr1,\ self.security_group_rule(security_group_id, direction='egress', port_range_min=23, port_range_max=23) as sgr2,\ self.security_group_rule(security_group_id, direction='egress', port_range_min=24, port_range_max=24) as sgr3: # Delete default rules as they would fail the following # assertion at the end. self._delete_default_security_group_egress_rules( security_group_id) q = 'direction=egress&security_group_id=' + security_group_id self._test_list_with_sort('security-group-rule', (sgr3, sgr2, sgr1), [('port_range_max', 'desc')], query_params=q) def test_list_security_group_rules_with_pagination(self): with self.security_group(name='sg') as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id, direction='egress', port_range_min=22, port_range_max=22) as sgr1,\ self.security_group_rule(security_group_id, direction='egress', port_range_min=23, port_range_max=23) as sgr2,\ self.security_group_rule(security_group_id, direction='egress', port_range_min=24, port_range_max=24) as sgr3: # Delete default rules as they would fail the following # assertion at the end. self._delete_default_security_group_egress_rules( security_group_id) q = 'direction=egress&security_group_id=' + security_group_id self._test_list_with_pagination( 'security-group-rule', (sgr3, sgr2, sgr1), ('port_range_max', 'desc'), 2, 2, query_params=q) def test_list_security_group_rules_with_pagination_reverse(self): with self.security_group(name='sg') as sg: security_group_id = sg['security_group']['id'] with self.security_group_rule(security_group_id, direction='egress', port_range_min=22, port_range_max=22) as sgr1,\ self.security_group_rule(security_group_id, direction='egress', port_range_min=23, port_range_max=23) as sgr2,\ self.security_group_rule(security_group_id, direction='egress', port_range_min=24, port_range_max=24) as sgr3: self._test_list_with_pagination_reverse( 'security-group-rule', (sgr3, sgr2, sgr1), ('port_range_max', 'desc'), 2, 2, query_params='direction=egress') def test_create_port_with_multiple_security_groups(self): with self.network() as n: with self.subnet(n): with self.security_group() as sg1: with self.security_group() as sg2: res = self._create_port( self.fmt, n['network']['id'], security_groups=[sg1['security_group']['id'], sg2['security_group']['id']]) port = self.deserialize(self.fmt, res) self.assertEqual(2, len( port['port'][ext_sg.SECURITYGROUPS])) self._delete('ports', port['port']['id']) def test_create_port_with_no_security_groups(self): with self.network() as n: with self.subnet(n): res = self._create_port(self.fmt, n['network']['id'], security_groups=[]) port = self.deserialize(self.fmt, res) self.assertEqual([], port['port'][ext_sg.SECURITYGROUPS]) def test_update_port_with_security_group(self): with self.network() as n: with self.subnet(n): with self.security_group() as sg: res = self._create_port(self.fmt, n['network']['id']) port = self.deserialize(self.fmt, res) data = {'port': {'fixed_ips': port['port']['fixed_ips'], 'name': port['port']['name'], ext_sg.SECURITYGROUPS: [sg['security_group']['id']]}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(res['port'][ext_sg.SECURITYGROUPS][0], sg['security_group']['id']) # Test update port without security group data = {'port': {'fixed_ips': port['port']['fixed_ips'], 'name': port['port']['name']}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(res['port'][ext_sg.SECURITYGROUPS][0], sg['security_group']['id']) self._delete('ports', port['port']['id']) def test_update_port_with_multiple_security_groups(self): with self.network() as n: with self.subnet(n) as s: with self.port(s) as port: with self.security_group() as sg1: with self.security_group() as sg2: data = {'port': {ext_sg.SECURITYGROUPS: [sg1['security_group']['id'], sg2['security_group']['id']]}} req = self.new_update_request( 'ports', data, port['port']['id']) port = self.deserialize( self.fmt, req.get_response(self.api)) self.assertEqual( 2, len(port['port'][ext_sg.SECURITYGROUPS])) def test_update_port_remove_security_group_empty_list(self): with self.network() as n: with self.subnet(n): with self.security_group() as sg: res = self._create_port(self.fmt, n['network']['id'], security_groups=( [sg['security_group']['id']])) port = self.deserialize(self.fmt, res) data = {'port': {'fixed_ips': port['port']['fixed_ips'], 'name': port['port']['name'], 'security_groups': []}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual([], res['port'].get(ext_sg.SECURITYGROUPS)) self._delete('ports', port['port']['id']) def test_update_port_remove_security_group_none(self): with self.network() as n: with self.subnet(n): with self.security_group() as sg: res = self._create_port(self.fmt, n['network']['id'], security_groups=( [sg['security_group']['id']])) port = self.deserialize(self.fmt, res) data = {'port': {'fixed_ips': port['port']['fixed_ips'], 'name': port['port']['name'], 'security_groups': None}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual([], res['port'].get(ext_sg.SECURITYGROUPS)) self._delete('ports', port['port']['id']) def test_update_port_with_invalid_type_in_security_groups_param(self): with self.network() as n: with self.subnet(n): with self.security_group() as sg: res = self._create_port(self.fmt, n['network']['id'], security_groups=( [sg['security_group']['id']])) port = self.deserialize(self.fmt, res) data = {'port': {'fixed_ips': port['port']['fixed_ips'], 'name': port['port']['name'], 'security_groups': True}} req = self.new_update_request('ports', data, port['port']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_port_with_bad_security_group(self): with self.network() as n: with self.subnet(n): res = self._create_port(self.fmt, n['network']['id'], security_groups=['bad_id']) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_port_with_invalid_type_in_security_groups_param(self): with self.network() as n: with self.subnet(n): res = self._create_port(self.fmt, n['network']['id'], security_groups=True) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_delete_security_group_port_in_use(self): with self.network() as n: with self.subnet(n): with self.security_group() as sg: res = self._create_port(self.fmt, n['network']['id'], security_groups=( [sg['security_group']['id']])) port = self.deserialize(self.fmt, res) self.assertEqual(port['port'][ext_sg.SECURITYGROUPS][0], sg['security_group']['id']) # try to delete security group that's in use res = self._delete('security-groups', sg['security_group']['id'], webob.exc.HTTPConflict.code) # delete the blocking port self._delete('ports', port['port']['id']) def test_create_security_group_rule_bulk_native(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk " "security_group_rule create") with self.security_group() as sg: rule1 = self._build_security_group_rule(sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '22', '22', '10.0.0.1/24') rule2 = self._build_security_group_rule(sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '23', '23', '10.0.0.1/24') rules = {'security_group_rules': [rule1['security_group_rule'], rule2['security_group_rule']]} res = self._create_security_group_rule(self.fmt, rules) ret = self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) self.assertEqual(2, len(ret['security_group_rules'])) def test_create_security_group_rule_bulk_emulated(self): real_has_attr = hasattr #ensures the API choose the emulation code path def fakehasattr(item, attr): if attr.endswith('__native_bulk_support'): return False return real_has_attr(item, attr) with mock.patch('six.moves.builtins.hasattr', new=fakehasattr): with self.security_group() as sg: rule1 = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '22', '22', '10.0.0.1/24') rule2 = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '23', '23', '10.0.0.1/24') rules = {'security_group_rules': [rule1['security_group_rule'], rule2['security_group_rule']] } res = self._create_security_group_rule(self.fmt, rules) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) def test_create_security_group_rule_allow_all_ipv4(self): with self.security_group() as sg: rule = {'security_group_id': sg['security_group']['id'], 'direction': 'ingress', 'ethertype': const.IPv4, 'tenant_id': test_db_base_plugin_v2.TEST_TENANT_ID} res = self._create_security_group_rule( self.fmt, {'security_group_rule': rule}) rule = self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) def test_create_security_group_rule_allow_all_ipv4_v6_bulk(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk " "security_group_rule create") with self.security_group() as sg: rule_v4 = {'security_group_id': sg['security_group']['id'], 'direction': 'ingress', 'ethertype': const.IPv4, 'tenant_id': test_db_base_plugin_v2.TEST_TENANT_ID} rule_v6 = {'security_group_id': sg['security_group']['id'], 'direction': 'ingress', 'ethertype': const.IPv6, 'tenant_id': test_db_base_plugin_v2.TEST_TENANT_ID} rules = {'security_group_rules': [rule_v4, rule_v6]} res = self._create_security_group_rule(self.fmt, rules) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) def test_create_security_group_rule_duplicate_rule_in_post(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk " "security_group_rule create") with self.security_group() as sg: rule = self._build_security_group_rule(sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '22', '22', '10.0.0.1/24') rules = {'security_group_rules': [rule['security_group_rule'], rule['security_group_rule']]} res = self._create_security_group_rule(self.fmt, rules) rule = self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_security_group_rule_duplicate_rule_in_post_emulated(self): real_has_attr = hasattr #ensures the API choose the emulation code path def fakehasattr(item, attr): if attr.endswith('__native_bulk_support'): return False return real_has_attr(item, attr) with mock.patch('six.moves.builtins.hasattr', new=fakehasattr): with self.security_group() as sg: rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '22', '22', '10.0.0.1/24') rules = {'security_group_rules': [rule['security_group_rule'], rule['security_group_rule']]} res = self._create_security_group_rule(self.fmt, rules) rule = self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_security_group_rule_duplicate_rule_db(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk " "security_group_rule create") with self.security_group() as sg: rule = self._build_security_group_rule(sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '22', '22', '10.0.0.1/24') rules = {'security_group_rules': [rule]} self._create_security_group_rule(self.fmt, rules) res = self._create_security_group_rule(self.fmt, rules) rule = self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_security_group_rule_duplicate_rule_db_emulated(self): real_has_attr = hasattr #ensures the API choose the emulation code path def fakehasattr(item, attr): if attr.endswith('__native_bulk_support'): return False return real_has_attr(item, attr) with mock.patch('six.moves.builtins.hasattr', new=fakehasattr): with self.security_group() as sg: rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '22', '22', '10.0.0.1/24') rules = {'security_group_rules': [rule]} self._create_security_group_rule(self.fmt, rules) res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_security_groups_native_quotas(self): quota = 1 cfg.CONF.set_override('quota_security_group', quota, group='QUOTAS') name = 'quota_test' description = 'quota_test' res = self._create_security_group(self.fmt, name, description) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) res = self._create_security_group(self.fmt, name, description) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_security_group_rules_native_quotas(self): name = 'quota_test' description = 'quota_test' with self.security_group(name, description) as sg: # avoid the number of default security group rules sgr = self._list('security-group-rules').get( 'security_group_rules') quota = len(sgr) + 1 cfg.CONF.set_override( 'quota_security_group_rule', quota, group='QUOTAS') security_group_id = sg['security_group']['id'] rule = self._build_security_group_rule( security_group_id, 'ingress', const.PROTO_NAME_TCP, '22', '22') res = self._create_security_group_rule(self.fmt, rule) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) rule = self._build_security_group_rule( security_group_id, 'egress', const.PROTO_NAME_TCP, '22', '22') res = self._create_security_group_rule(self.fmt, rule) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_security_group_rule_different_security_group_ids(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk " "security_group_rule create") with self.security_group() as sg1: with self.security_group() as sg2: rule1 = self._build_security_group_rule( sg1['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '22', '22', '10.0.0.1/24') rule2 = self._build_security_group_rule( sg2['security_group']['id'], 'ingress', const.PROTO_NAME_TCP, '23', '23', '10.0.0.1/24') rules = {'security_group_rules': [rule1['security_group_rule'], rule2['security_group_rule']] } res = self._create_security_group_rule(self.fmt, rules) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_with_invalid_ethertype(self): security_group_id = "4cd70774-cc67-4a87-9b39-7d1db38eb087" direction = "ingress" remote_ip_prefix = "10.0.0.0/24" protocol = const.PROTO_NAME_TCP port_range_min = 22 port_range_max = 22 remote_group_id = "9cd70774-cc67-4a87-9b39-7d1db38eb087" rule = self._build_security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_ip_prefix, remote_group_id, ethertype='IPv5') res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_with_invalid_protocol(self): security_group_id = "4cd70774-cc67-4a87-9b39-7d1db38eb087" direction = "ingress" remote_ip_prefix = "10.0.0.0/24" protocol = 'tcp/ip' port_range_min = 22 port_range_max = 22 remote_group_id = "9cd70774-cc67-4a87-9b39-7d1db38eb087" rule = self._build_security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_ip_prefix, remote_group_id) res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_with_invalid_tcp_or_udp_protocol(self): security_group_id = "4cd70774-cc67-4a87-9b39-7d1db38eb087" direction = "ingress" remote_ip_prefix = "10.0.0.0/24" protocol = 'tcp' port_range_min = 0 port_range_max = 80 remote_group_id = "9cd70774-cc67-4a87-9b39-7d1db38eb087" rule = self._build_security_group_rule(security_group_id, direction, protocol, port_range_min, port_range_max, remote_ip_prefix, remote_group_id) res = self._create_security_group_rule(self.fmt, rule) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_port_with_non_uuid(self): with self.network() as n: with self.subnet(n): res = self._create_port(self.fmt, n['network']['id'], security_groups=['not_valid']) self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_security_group_rule_with_specific_id(self): neutron_context = context.Context( '', test_db_base_plugin_v2.TEST_TENANT_ID) specified_id = "4cd70774-cc67-4a87-9b39-7d1db38eb087" with self.security_group() as sg: rule = self._build_security_group_rule( sg['security_group']['id'], 'ingress', const.PROTO_NUM_TCP) rule['security_group_rule'].update({'id': specified_id, 'port_range_min': None, 'port_range_max': None, 'remote_ip_prefix': None, 'remote_group_id': None}) result = self.plugin.create_security_group_rule( neutron_context, rule) self.assertEqual(specified_id, result['id']) class TestConvertIPPrefixToCIDR(base.BaseTestCase): def test_convert_bad_ip_prefix_to_cidr(self): for val in ['bad_ip', 256, "2001:db8:a::123/129"]: self.assertRaises(n_exc.InvalidCIDR, ext_sg.convert_ip_prefix_to_cidr, val) self.assertIsNone(ext_sg.convert_ip_prefix_to_cidr(None)) def test_convert_ip_prefix_no_netmask_to_cidr(self): addr = {'10.1.2.3': '32', 'fe80::2677:3ff:fe7d:4c': '128'} for k, v in addr.items(): self.assertEqual(ext_sg.convert_ip_prefix_to_cidr(k), '%s/%s' % (k, v)) def test_convert_ip_prefix_with_netmask_to_cidr(self): addresses = ['10.1.0.0/16', '10.1.2.3/32', '2001:db8:1234::/48'] for addr in addresses: self.assertEqual(addr, ext_sg.convert_ip_prefix_to_cidr(addr)) class TestConvertProtocol(base.BaseTestCase): def test_convert_numeric_protocol(self): self.assertIsInstance(ext_sg.convert_protocol('2'), str) def test_convert_bad_protocol(self): for val in ['bad', '256', '-1']: self.assertRaises(ext_sg.SecurityGroupRuleInvalidProtocol, ext_sg.convert_protocol, val) def test_convert_numeric_protocol_to_string(self): self.assertIsInstance(ext_sg.convert_protocol(2), str) class TestConvertEtherType(base.BaseTestCase): def test_convert_unsupported_ethertype(self): for val in ['ip', 'ip4', 'ip6', '']: self.assertRaises(ext_sg.SecurityGroupRuleInvalidEtherType, ext_sg.convert_ethertype_to_case_insensitive, val) neutron-12.1.1/neutron/tests/unit/extensions/test_subnet_service_types.py0000664000175000017500000003651213553660047027217 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import webob.exc from neutron_lib.api.definitions import portbindings from neutron.db import db_base_plugin_v2 from neutron.db import subnet_service_type_db_models from neutron.extensions import subnet_service_types from neutron.tests.unit.db import test_db_base_plugin_v2 class SubnetServiceTypesExtensionManager(object): def get_resources(self): return [] def get_actions(self): return [] def get_request_extensions(self): return [] def get_extended_resources(self, version): extension = subnet_service_types.Subnet_service_types() return extension.get_extended_resources(version) class SubnetServiceTypesExtensionTestPlugin( db_base_plugin_v2.NeutronDbPluginV2, subnet_service_type_db_models.SubnetServiceTypeMixin): """Test plugin to mixin the subnet service_types extension. """ supported_extension_aliases = ["subnet-service-types", "binding"] class SubnetServiceTypesExtensionTestCase( test_db_base_plugin_v2.NeutronDbPluginV2TestCase): """Test API extension subnet_service_types attributes. """ CIDRS = ['10.0.0.0/8', '20.0.0.0/8', '30.0.0.0/8'] IP_VERSION = 4 def setUp(self): plugin = ('neutron.tests.unit.extensions.test_subnet_service_types.' + 'SubnetServiceTypesExtensionTestPlugin') ext_mgr = SubnetServiceTypesExtensionManager() super(SubnetServiceTypesExtensionTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr) def _create_service_subnet(self, service_types=None, cidr=None, network=None, enable_dhcp=False): if not network: with self.network() as network: pass network = network['network'] if not cidr: cidr = self.CIDRS[0] args = {'net_id': network['id'], 'tenant_id': network['tenant_id'], 'cidr': cidr, 'ip_version': self.IP_VERSION, 'enable_dhcp': enable_dhcp} if service_types: args['service_types'] = service_types return self._create_subnet(self.fmt, **args) def _test_create_subnet(self, service_types, expect_fail=False): res = self._create_service_subnet(service_types) if expect_fail: self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) else: subnet = self.deserialize('json', res) subnet = subnet['subnet'] self.assertEqual(len(service_types), len(subnet['service_types'])) for service in service_types: self.assertIn(service, subnet['service_types']) def test_create_subnet_blank_type(self): self._test_create_subnet([]) def test_create_subnet_bar_type(self): self._test_create_subnet(['network:bar']) def test_create_subnet_foo_type(self): self._test_create_subnet(['compute:foo']) def test_create_subnet_bar_and_foo_type(self): self._test_create_subnet(['network:bar', 'compute:foo']) def test_create_subnet_invalid_type(self): self._test_create_subnet(['foo'], expect_fail=True) self._test_create_subnet([1], expect_fail=True) def test_create_subnet_no_type(self): res = self._create_service_subnet() subnet = self.deserialize('json', res) subnet = subnet['subnet'] self.assertFalse(subnet['service_types']) def _test_update_subnet(self, subnet, service_types, fail_code=None): data = {'subnet': {'service_types': service_types}} req = self.new_update_request('subnets', data, subnet['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) if fail_code is not None: self.assertEqual(fail_code, res['NeutronError']['type']) else: subnet = res['subnet'] self.assertEqual(len(service_types), len(subnet['service_types'])) for service in service_types: self.assertIn(service, subnet['service_types']) def test_update_subnet_zero_to_one(self): service_types = ['network:foo'] # Create a subnet with no service type res = self._create_service_subnet() subnet = self.deserialize('json', res)['subnet'] # Update it with a single service type self._test_update_subnet(subnet, service_types) def test_update_subnet_one_to_two(self): service_types = ['network:foo'] # Create a subnet with one service type res = self._create_service_subnet(service_types) subnet = self.deserialize('json', res)['subnet'] # Update it with two service types service_types.append('compute:bar') self._test_update_subnet(subnet, service_types) def test_update_subnet_two_to_one(self): service_types = ['network:foo', 'compute:bar'] # Create a subnet with two service types res = self._create_service_subnet(service_types) subnet = self.deserialize('json', res)['subnet'] # Update it with one service type service_types = ['network:foo'] self._test_update_subnet(subnet, service_types) def test_update_subnet_one_to_zero(self): service_types = ['network:foo'] # Create a subnet with one service type res = self._create_service_subnet(service_types) subnet = self.deserialize('json', res)['subnet'] # Update it with zero service types service_types = [] self._test_update_subnet(subnet, service_types) def test_update_subnet_invalid_type(self): # Create a subnet with no service type res = self._create_service_subnet() subnet = self.deserialize('json', res)['subnet'] # Update it with invalid service type(s) self._test_update_subnet(subnet, ['foo'], fail_code='InvalidSubnetServiceType') self._test_update_subnet(subnet, [2], fail_code='InvalidInputSubnetServiceType') def _assert_port_res(self, port, service_type, subnet, fallback, error='IpAddressGenerationFailureNoMatchingSubnet'): res = self.deserialize('json', port) if fallback: port = res['port'] self.assertEqual(1, len(port['fixed_ips'])) self.assertEqual(service_type, port['device_owner']) self.assertEqual(subnet['id'], port['fixed_ips'][0]['subnet_id']) else: self.assertEqual(error, res['NeutronError']['type']) def test_create_port_with_matching_service_type(self): with self.network() as network: pass matching_type = 'network:foo' non_matching_type = 'network:bar' # Create a subnet with no service types self._create_service_subnet(network=network) # Create a subnet with a non-matching service type self._create_service_subnet([non_matching_type], cidr=self.CIDRS[2], network=network) # Create a subnet with a service type to match the port device owner res = self._create_service_subnet([matching_type], cidr=self.CIDRS[1], network=network) service_subnet = self.deserialize('json', res)['subnet'] # Create a port with device owner matching the correct service subnet network = network['network'] port = self._create_port(self.fmt, net_id=network['id'], tenant_id=network['tenant_id'], device_owner=matching_type) self._assert_port_res(port, matching_type, service_subnet, True) def test_create_port_without_matching_service_type(self, fallback=True): with self.network() as network: pass subnet = '' matching_type = 'compute:foo' non_matching_type = 'network:foo' if fallback: # Create a subnet with no service types res = self._create_service_subnet(network=network) subnet = self.deserialize('json', res)['subnet'] # Create a subnet with a non-matching service type self._create_service_subnet([non_matching_type], cidr=self.CIDRS[1], network=network) # Create a port with device owner not matching the service subnet network = network['network'] port = self._create_port(self.fmt, net_id=network['id'], tenant_id=network['tenant_id'], device_owner=matching_type) self._assert_port_res(port, matching_type, subnet, fallback) def test_create_port_without_matching_service_type_no_fallback(self): self.test_create_port_without_matching_service_type(fallback=False) def test_create_port_no_device_owner(self, fallback=True): with self.network() as network: pass subnet = '' service_type = 'compute:foo' if fallback: # Create a subnet with no service types res = self._create_service_subnet(network=network) subnet = self.deserialize('json', res)['subnet'] # Create a subnet with a service_type self._create_service_subnet([service_type], cidr=self.CIDRS[1], network=network) # Create a port without a device owner network = network['network'] port = self._create_port(self.fmt, net_id=network['id'], tenant_id=network['tenant_id']) self._assert_port_res(port, '', subnet, fallback) def test_create_port_no_device_owner_no_fallback(self): self.test_create_port_no_device_owner(fallback=False) def test_create_port_exhausted_subnet(self, fallback=True): with self.network() as network: pass subnet = '' service_type = 'compute:foo' if fallback: # Create a subnet with no service types res = self._create_service_subnet(network=network) subnet = self.deserialize('json', res)['subnet'] # Create a subnet with a service_type res = self._create_service_subnet([service_type], cidr=self.CIDRS[1], network=network) service_subnet = self.deserialize('json', res)['subnet'] # Update the service subnet with empty allocation pools data = {'subnet': {'allocation_pools': []}} req = self.new_update_request('subnets', data, service_subnet['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) # Create a port with a matching device owner network = network['network'] port = self._create_port(self.fmt, net_id=network['id'], tenant_id=network['tenant_id'], device_owner=service_type) self._assert_port_res(port, service_type, subnet, fallback, error='IpAddressGenerationFailure') def test_create_port_exhausted_subnet_no_fallback(self): self.test_create_port_exhausted_subnet(fallback=False) def test_create_dhcp_port_compute_subnet(self, enable_dhcp=True): with self.network() as network: pass res = self._create_service_subnet(['compute:nova'], network=network, enable_dhcp=enable_dhcp) subnet = self.deserialize('json', res)['subnet'] network = network['network'] port = self._create_port(self.fmt, net_id=network['id'], tenant_id=network['tenant_id'], fixed_ips=[{'subnet_id': subnet['id']}], device_owner='network:dhcp') self._assert_port_res(port, 'network:dhcp', subnet, enable_dhcp) def test_create_dhcp_port_compute_subnet_no_dhcp(self): self.test_create_dhcp_port_compute_subnet(enable_dhcp=False) def test_update_port_fixed_ips(self): with self.network() as network: pass service_type = 'compute:foo' # Create a subnet with a service_type res = self._create_service_subnet([service_type], cidr=self.CIDRS[1], network=network) service_subnet = self.deserialize('json', res)['subnet'] # Create a port with a matching device owner network = network['network'] port = self._create_port(self.fmt, net_id=network['id'], tenant_id=network['tenant_id'], device_owner=service_type) port = self.deserialize('json', port)['port'] # Update the port's fixed_ips. It's ok to reuse the same IP it already # has. ip_address = port['fixed_ips'][0]['ip_address'] data = {'port': {'fixed_ips': [{'subnet_id': service_subnet['id'], 'ip_address': ip_address}]}} # self._update will fail with a MismatchError if the update cannot be # applied port = self._update('ports', port['id'], data) def test_update_port_host_binding(self): with self.network() as network: pass service_type = 'compute:foo' # Create a subnet with a service_type self._create_service_subnet([service_type], cidr=self.CIDRS[1], network=network) # Create a port with a matching device owner network = network['network'] port = self._create_port(self.fmt, net_id=network['id'], tenant_id=network['tenant_id'], device_owner=service_type, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) port = self.deserialize('json', port)['port'] # Update the port's host binding. data = {'port': {portbindings.HOST_ID: 'fakehost2'}} # self._update will fail with a MismatchError if the update cannot be # applied port = self._update('ports', port['id'], data) class SubnetServiceTypesExtensionTestCasev6( SubnetServiceTypesExtensionTestCase): CIDRS = ['2001:db8:2::/64', '2001:db8:3::/64', '2001:db8:4::/64'] IP_VERSION = 6 neutron-12.1.1/neutron/tests/unit/extensions/test_quotasv2.py0000664000175000017500000005666513553660047024552 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys import mock from neutron_lib import context from neutron_lib.db import constants from neutron_lib import fixture from oslo_config import cfg import testtools from webob import exc import webtest from neutron.api import extensions from neutron.api.v2 import router from neutron.common import config from neutron.common import exceptions from neutron.conf import quota as qconf from neutron.db.quota import driver from neutron import quota from neutron.quota import resource_registry from neutron.tests import base from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit import testlib_api DEFAULT_QUOTAS_ACTION = 'default' TARGET_PLUGIN = 'neutron.plugins.ml2.plugin.Ml2Plugin' _get_path = test_base._get_path class QuotaExtensionTestCase(testlib_api.WebTestCase): def setUp(self): super(QuotaExtensionTestCase, self).setUp() # Ensure existing ExtensionManager is not used extensions.PluginAwareExtensionManager._instance = None self.useFixture(fixture.APIDefinitionFixture()) # Create the default configurations self.config_parse() # Update the plugin and extensions path self.setup_coreplugin('ml2') quota.QUOTAS = quota.QuotaEngine() self._plugin_patcher = mock.patch(TARGET_PLUGIN, autospec=True) self.plugin = self._plugin_patcher.start() self.plugin.return_value.supported_extension_aliases = ['quotas'] # QUOTAS will register the items in conf when starting # extra1 here is added later, so have to do it manually resource_registry.register_resource_by_name('extra1') ext_mgr = extensions.PluginAwareExtensionManager.get_instance() app = config.load_paste_app('extensions_test_app') ext_middleware = extensions.ExtensionMiddleware(app, ext_mgr=ext_mgr) self.api = webtest.TestApp(ext_middleware) # Initialize the router for the core API in order to ensure core quota # resources are registered router.APIRouter() def _test_quota_default_values(self, expected_values): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id)} res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt), extra_environ=env) quota = self.deserialize(res) for resource, expected_value in expected_values.items(): self.assertEqual(expected_value, quota['quota'][resource]) class QuotaExtensionDbTestCase(QuotaExtensionTestCase): fmt = 'json' def setUp(self): cfg.CONF.set_override( 'quota_driver', 'neutron.db.quota.driver.DbQuotaDriver', group='QUOTAS') super(QuotaExtensionDbTestCase, self).setUp() def test_quotas_loaded_right(self): res = self.api.get(_get_path('quotas', fmt=self.fmt)) quota = self.deserialize(res) self.assertEqual([], quota['quotas']) self.assertEqual(200, res.status_int) def test_quotas_default_values(self): self._test_quota_default_values( {'network': qconf.DEFAULT_QUOTA_NETWORK, 'subnet': qconf.DEFAULT_QUOTA_SUBNET, 'port': qconf.DEFAULT_QUOTA_PORT, 'extra1': qconf.DEFAULT_QUOTA}) def test_quotas_negative_default_value(self): cfg.CONF.set_override( 'quota_port', -666, group='QUOTAS') cfg.CONF.set_override( 'quota_network', -10, group='QUOTAS') cfg.CONF.set_override( 'quota_subnet', -50, group='QUOTAS') self._test_quota_default_values( {'network': qconf.DEFAULT_QUOTA, 'subnet': qconf.DEFAULT_QUOTA, 'port': qconf.DEFAULT_QUOTA, 'extra1': qconf.DEFAULT_QUOTA}) def test_show_default_quotas_with_admin(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id + '2', is_admin=True)} res = self.api.get(_get_path('quotas', id=tenant_id, action=DEFAULT_QUOTAS_ACTION, fmt=self.fmt), extra_environ=env) self.assertEqual(200, res.status_int) quota = self.deserialize(res) self.assertEqual( qconf.DEFAULT_QUOTA_NETWORK, quota['quota']['network']) self.assertEqual( qconf.DEFAULT_QUOTA_SUBNET, quota['quota']['subnet']) self.assertEqual( qconf.DEFAULT_QUOTA_PORT, quota['quota']['port']) def test_show_default_quotas_with_owner_tenant(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=False)} res = self.api.get(_get_path('quotas', id=tenant_id, action=DEFAULT_QUOTAS_ACTION, fmt=self.fmt), extra_environ=env) self.assertEqual(200, res.status_int) quota = self.deserialize(res) self.assertEqual( qconf.DEFAULT_QUOTA_NETWORK, quota['quota']['network']) self.assertEqual( qconf.DEFAULT_QUOTA_SUBNET, quota['quota']['subnet']) self.assertEqual( qconf.DEFAULT_QUOTA_PORT, quota['quota']['port']) def test_show_default_quotas_without_admin_forbidden_returns_403(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id + '2', is_admin=False)} res = self.api.get(_get_path('quotas', id=tenant_id, action=DEFAULT_QUOTAS_ACTION, fmt=self.fmt), extra_environ=env, expect_errors=True) self.assertEqual(403, res.status_int) def test_show_quotas_with_admin(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id + '2', is_admin=True)} res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt), extra_environ=env) self.assertEqual(200, res.status_int) quota = self.deserialize(res) self.assertEqual( qconf.DEFAULT_QUOTA_NETWORK, quota['quota']['network']) self.assertEqual( qconf.DEFAULT_QUOTA_SUBNET, quota['quota']['subnet']) self.assertEqual( qconf.DEFAULT_QUOTA_PORT, quota['quota']['port']) def test_show_quotas_without_admin_forbidden_returns_403(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id + '2', is_admin=False)} res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt), extra_environ=env, expect_errors=True) self.assertEqual(403, res.status_int) def test_show_quotas_with_owner_tenant(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=False)} res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt), extra_environ=env) self.assertEqual(200, res.status_int) quota = self.deserialize(res) self.assertEqual( qconf.DEFAULT_QUOTA_NETWORK, quota['quota']['network']) self.assertEqual( qconf.DEFAULT_QUOTA_SUBNET, quota['quota']['subnet']) self.assertEqual( qconf.DEFAULT_QUOTA_PORT, quota['quota']['port']) def test_list_quotas_with_admin(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=True)} res = self.api.get(_get_path('quotas', fmt=self.fmt), extra_environ=env) self.assertEqual(200, res.status_int) quota = self.deserialize(res) self.assertEqual([], quota['quotas']) def test_list_quotas_without_admin_forbidden_returns_403(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=False)} res = self.api.get(_get_path('quotas', fmt=self.fmt), extra_environ=env, expect_errors=True) self.assertEqual(403, res.status_int) def test_update_quotas_without_admin_forbidden_returns_403(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=False)} quotas = {'quota': {'network': 100}} res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), extra_environ=env, expect_errors=True) self.assertEqual(403, res.status_int) def test_update_quotas_with_non_integer_returns_400(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=True)} quotas = {'quota': {'network': 'abc'}} res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), extra_environ=env, expect_errors=True) self.assertEqual(400, res.status_int) def test_update_quotas_with_negative_integer_returns_400(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=True)} quotas = {'quota': {'network': -2}} res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), extra_environ=env, expect_errors=True) self.assertEqual(400, res.status_int) def test_update_quotas_with_out_of_range_integer_returns_400(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=True)} quotas = {'quota': {'network': constants.DB_INTEGER_MAX_VALUE + 1}} res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), extra_environ=env, expect_errors=True) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_update_quotas_to_unlimited(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=True)} quotas = {'quota': {'network': -1}} res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), extra_environ=env, expect_errors=False) self.assertEqual(200, res.status_int) def test_update_quotas_exceeding_current_limit(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=True)} quotas = {'quota': {'network': 120}} res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), extra_environ=env, expect_errors=False) self.assertEqual(200, res.status_int) def test_update_quotas_with_non_support_resource_returns_400(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=True)} quotas = {'quota': {'abc': 100}} res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), extra_environ=env, expect_errors=True) self.assertEqual(400, res.status_int) def test_update_quotas_with_admin(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id + '2', is_admin=True)} quotas = {'quota': {'network': 100}} res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), extra_environ=env) self.assertEqual(200, res.status_int) env2 = {'neutron.context': context.Context('', tenant_id)} res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt), extra_environ=env2) quota = self.deserialize(res) self.assertEqual(100, quota['quota']['network']) self.assertEqual(qconf.DEFAULT_QUOTA_SUBNET, quota['quota']['subnet']) self.assertEqual(qconf.DEFAULT_QUOTA_PORT, quota['quota']['port']) def test_update_attributes(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id + '2', is_admin=True)} quotas = {'quota': {'extra1': 100}} res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), extra_environ=env) self.assertEqual(200, res.status_int) env2 = {'neutron.context': context.Context('', tenant_id)} res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt), extra_environ=env2) quota = self.deserialize(res) self.assertEqual(100, quota['quota']['extra1']) def test_delete_quotas_with_admin(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id + '2', is_admin=True)} # Create a quota to ensure we have something to delete quotas = {'quota': {'network': 100}} self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), extra_environ=env) res = self.api.delete(_get_path('quotas', id=tenant_id, fmt=self.fmt), extra_environ=env) self.assertEqual(204, res.status_int) def test_delete_quotas_without_admin_forbidden_returns_403(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=False)} res = self.api.delete(_get_path('quotas', id=tenant_id, fmt=self.fmt), extra_environ=env, expect_errors=True) self.assertEqual(403, res.status_int) def test_delete_quota_with_unknown_tenant_returns_404(self): tenant_id = 'idnotexist' env = {'neutron.context': context.Context('', tenant_id + '2', is_admin=True)} res = self.api.delete(_get_path('quotas', id=tenant_id, fmt=self.fmt), extra_environ=env, expect_errors=True) self.assertEqual(exc.HTTPNotFound.code, res.status_int) def test_quotas_loaded_bad_returns_404(self): try: res = self.api.get(_get_path('quotas'), expect_errors=True) self.assertEqual(404, res.status_int) except Exception: pass def test_quotas_limit_check(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=True)} quotas = {'quota': {'network': 5}} res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), extra_environ=env) self.assertEqual(200, res.status_int) quota.QUOTAS.limit_check(context.Context('', tenant_id), tenant_id, network=4) def test_quotas_limit_check_with_invalid_quota_value(self): tenant_id = 'tenant_id1' with testtools.ExpectedException(exceptions.InvalidQuotaValue): quota.QUOTAS.limit_check(context.Context('', tenant_id), tenant_id, network=-2) def test_quotas_limit_check_with_not_registered_resource_fails(self): tenant_id = 'tenant_id1' self.assertRaises(exceptions.QuotaResourceUnknown, quota.QUOTAS.limit_check, context.get_admin_context(), tenant_id, foobar=1) def test_quotas_get_tenant_from_request_context(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=True)} res = self.api.get(_get_path('quotas/tenant', fmt=self.fmt), extra_environ=env) self.assertEqual(200, res.status_int) quota = self.deserialize(res) self.assertEqual(quota['tenant']['tenant_id'], tenant_id) def test_quotas_get_tenant_from_empty_request_context_returns_400(self): env = {'neutron.context': context.Context('', '', is_admin=True)} res = self.api.get(_get_path('quotas/tenant', fmt=self.fmt), extra_environ=env, expect_errors=True) self.assertEqual(400, res.status_int) def test_make_reservation_resource_unknown_raises(self): tenant_id = 'tenant_id1' self.assertRaises(exceptions.QuotaResourceUnknown, quota.QUOTAS.make_reservation, context.get_admin_context(), tenant_id, {'foobar': 1}, plugin=None) def test_make_reservation_negative_delta_raises(self): tenant_id = 'tenant_id1' self.assertRaises(exceptions.InvalidQuotaValue, quota.QUOTAS.make_reservation, context.get_admin_context(), tenant_id, {'network': -1}, plugin=None) class QuotaExtensionCfgTestCase(QuotaExtensionTestCase): fmt = 'json' def setUp(self): cfg.CONF.set_override( 'quota_driver', 'neutron.quota.ConfDriver', group='QUOTAS') super(QuotaExtensionCfgTestCase, self).setUp() def test_quotas_default_values(self): self._test_quota_default_values( {'network': qconf.DEFAULT_QUOTA_NETWORK, 'subnet': qconf.DEFAULT_QUOTA_SUBNET, 'port': qconf.DEFAULT_QUOTA_PORT, 'extra1': qconf.DEFAULT_QUOTA}) def test_quotas_negative_default_value(self): cfg.CONF.set_override( 'quota_port', -666, group='QUOTAS') self._test_quota_default_values( {'network': qconf.DEFAULT_QUOTA_NETWORK, 'subnet': qconf.DEFAULT_QUOTA_SUBNET, 'port': qconf.DEFAULT_QUOTA, 'extra1': qconf.DEFAULT_QUOTA}) def test_show_quotas_with_admin(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id + '2', is_admin=True)} res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt), extra_environ=env) self.assertEqual(200, res.status_int) def test_show_quotas_without_admin_forbidden(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id + '2', is_admin=False)} res = self.api.get(_get_path('quotas', id=tenant_id, fmt=self.fmt), extra_environ=env, expect_errors=True) self.assertEqual(403, res.status_int) def test_update_quotas_forbidden(self): tenant_id = 'tenant_id1' quotas = {'quota': {'network': 100}} res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), expect_errors=True) self.assertEqual(403, res.status_int) def test_delete_quotas_forbidden(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id, is_admin=False)} res = self.api.delete(_get_path('quotas', id=tenant_id, fmt=self.fmt), extra_environ=env, expect_errors=True) self.assertEqual(403, res.status_int) class TestDbQuotaDriver(base.BaseTestCase): """Test for neutron.db.quota.driver.DbQuotaDriver.""" def test_get_tenant_quotas_arg(self): """Call neutron.db.quota.driver.DbQuotaDriver._get_quotas.""" quota_driver = driver.DbQuotaDriver() ctx = context.Context('', 'bar') foo_quotas = {'network': 5} default_quotas = {'network': 10} target_tenant = 'foo' with mock.patch.object(driver.DbQuotaDriver, 'get_tenant_quotas', return_value=foo_quotas) as get_tenant_quotas: quotas = quota_driver._get_quotas(ctx, target_tenant, default_quotas) self.assertEqual(quotas, foo_quotas) get_tenant_quotas.assert_called_once_with(ctx, default_quotas, target_tenant) class TestQuotaDriverLoad(base.BaseTestCase): def setUp(self): super(TestQuotaDriverLoad, self).setUp() # Make sure QuotaEngine is reinitialized in each test. quota.QUOTAS._driver = None def _test_quota_driver(self, cfg_driver, loaded_driver, with_quota_db_module=True): cfg.CONF.set_override('quota_driver', cfg_driver, group='QUOTAS') with mock.patch.dict(sys.modules, {}): if (not with_quota_db_module and 'neutron.db.quota.driver' in sys.modules): del sys.modules['neutron.db.quota.driver'] driver = quota.QUOTAS.get_driver() self.assertEqual(loaded_driver, driver.__class__.__name__) def test_quota_db_driver_with_quotas_table(self): self._test_quota_driver('neutron.db.quota.driver.DbQuotaDriver', 'DbQuotaDriver', True) def test_quota_db_driver_fallback_conf_driver(self): self._test_quota_driver('neutron.db.quota.driver.DbQuotaDriver', 'ConfDriver', False) def test_quota_conf_driver(self): self._test_quota_driver('neutron.quota.ConfDriver', 'ConfDriver', True) neutron-12.1.1/neutron/tests/unit/privileged/0000775000175000017500000000000013553660157021270 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/privileged/agent/0000775000175000017500000000000013553660157022366 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/privileged/agent/linux/0000775000175000017500000000000013553660157023525 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/privileged/agent/linux/__init__.py0000664000175000017500000000000013553660046025621 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/privileged/agent/linux/test_netlink_lib.py0000664000175000017500000004011113553660047027423 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants import testtools from neutron.common import exceptions from neutron.privileged.agent.linux import netlink_constants as nl_constants from neutron.privileged.agent.linux import netlink_lib as nl_lib from neutron.tests import base FAKE_ICMP_ENTRY = {'ipversion': 4, 'protocol': 'icmp', 'type': '8', 'code': '0', 'id': 1234, 'src': '1.1.1.1', 'dst': '2.2.2.2', 'zone': 1} FAKE_TCP_ENTRY = {'ipversion': 4, 'protocol': 'tcp', 'sport': 1, 'dport': 2, 'src': '1.1.1.1', 'dst': '2.2.2.2', 'zone': 1} FAKE_UDP_ENTRY = {'ipversion': 4, 'protocol': 'udp', 'sport': 1, 'dport': 2, 'src': '1.1.1.1', 'dst': '2.2.2.2', 'zone': 1} class NetlinkLibTestCase(base.BaseTestCase): def setUp(self): super(NetlinkLibTestCase, self).setUp() nl_lib.nfct = mock.Mock() nl_lib.libc = mock.Mock() def test_open_new_conntrack_handler_failed(self): nl_lib.nfct.nfct_open.return_value = None with testtools.ExpectedException(exceptions.CTZoneExhaustedError): with nl_lib.ConntrackManager(): nl_lib.nfct.nfct_open.assert_called_once_with() nl_lib.nfct.nfct_close.assert_not_called() def test_open_new_conntrack_handler_pass(self): with nl_lib.ConntrackManager(): nl_lib.nfct.nfct_open.assert_called_once_with( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK) nl_lib.nfct.nfct_close.assert_called_once_with(nl_lib.nfct.nfct_open( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK)) def test_conntrack_list_entries(self): with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once_with( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK) conntrack.list_entries() nl_lib.nfct.nfct_callback_register.assert_has_calls( [mock.call(nl_lib.nfct.nfct_open(), nl_constants.NFCT_T_ALL, mock.ANY, None)]) nl_lib.nfct.nfct_query.assert_called_once_with( nl_lib.nfct.nfct_open( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK), nl_constants.NFCT_Q_DUMP, mock.ANY) nl_lib.nfct.nfct_close.assert_called_once_with(nl_lib.nfct.nfct_open( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK)) def test_conntrack_new_failed(self): nl_lib.nfct.nfct_new.return_value = None with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once_with( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK) conntrack.delete_entries([FAKE_ICMP_ENTRY]) nl_lib.nfct.nfct_new.assert_called_once_with() nl_lib.nfct.nfct_destroy.assert_called_once_with(None) nl_lib.nfct.nfct_close.assert_called_once_with(nl_lib.nfct.nfct_open( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK)) def test_conntrack_delete_icmp_entry(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once_with( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK) conntrack.delete_entries([FAKE_ICMP_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['icmp']), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_CODE, int(FAKE_ICMP_ENTRY['code'])), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_TYPE, int(FAKE_ICMP_ENTRY['type'])) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_ICMP_ID, nl_lib.libc.htons(FAKE_ICMP_ENTRY['id'])), mock.call(conntrack_filter, nl_constants.ATTR_ZONE, int(FAKE_ICMP_ENTRY['zone'])) ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, str(conntrack._convert_text_to_binary( FAKE_ICMP_ENTRY['src'], 4)) ), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, str(conntrack._convert_text_to_binary( FAKE_ICMP_ENTRY['dst'], 4)) ), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once_with(conntrack_filter) nl_lib.nfct.nfct_close.assert_called_once_with(nl_lib.nfct.nfct_open( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK)) def test_conntrack_delete_udp_entry(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once_with( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK) conntrack.delete_entries([FAKE_UDP_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['udp']) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_PORT_SRC, nl_lib.libc.htons(FAKE_UDP_ENTRY['sport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_DST, nl_lib.libc.htons(FAKE_UDP_ENTRY['dport'])), mock.call(conntrack_filter, nl_constants.ATTR_ZONE, int(FAKE_ICMP_ENTRY['zone'])) ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, str(conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['src'], 4)) ), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, str(conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['dst'], 4)) ), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once_with(conntrack_filter) nl_lib.nfct.nfct_close.assert_called_once_with(nl_lib.nfct.nfct_open( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK)) def test_conntrack_delete_tcp_entry(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once_with( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK) conntrack.delete_entries([FAKE_TCP_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['tcp']) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_PORT_SRC, nl_lib.libc.htons(FAKE_TCP_ENTRY['sport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_DST, nl_lib.libc.htons(FAKE_TCP_ENTRY['dport'])), mock.call(conntrack_filter, nl_constants.ATTR_ZONE, int(FAKE_ICMP_ENTRY['zone'])) ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, str(conntrack._convert_text_to_binary( FAKE_TCP_ENTRY['src'], 4)) ), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, str(conntrack._convert_text_to_binary( FAKE_TCP_ENTRY['dst'], 4)) ), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once_with(conntrack_filter) nl_lib.nfct.nfct_close.assert_called_once_with(nl_lib.nfct.nfct_open( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK)) def test_conntrack_delete_entries(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once_with( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK) conntrack.delete_entries([FAKE_ICMP_ENTRY, FAKE_TCP_ENTRY, FAKE_UDP_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['tcp']), mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['udp']), mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['icmp']), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_CODE, int(FAKE_ICMP_ENTRY['code'])), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_TYPE, int(FAKE_ICMP_ENTRY['type'])) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_PORT_SRC, nl_lib.libc.htons(FAKE_TCP_ENTRY['sport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_DST, nl_lib.libc.htons(FAKE_TCP_ENTRY['dport'])), mock.call(conntrack_filter, nl_constants.ATTR_ZONE, int(FAKE_TCP_ENTRY['zone'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_SRC, nl_lib.libc.htons(FAKE_UDP_ENTRY['sport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_DST, nl_lib.libc.htons(FAKE_UDP_ENTRY['dport'])), mock.call(conntrack_filter, nl_constants.ATTR_ZONE, int(FAKE_UDP_ENTRY['zone'])), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_ID, nl_lib.libc.htons(FAKE_ICMP_ENTRY['id'])), mock.call(conntrack_filter, nl_constants.ATTR_ZONE, int(FAKE_ICMP_ENTRY['zone'])) ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, str(conntrack._convert_text_to_binary( FAKE_TCP_ENTRY['src'], 4)) ), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, str(conntrack._convert_text_to_binary( FAKE_TCP_ENTRY['dst'], 4))), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, str(conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['src'], 4)) ), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, str(conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['dst'], 4)) ), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, str(conntrack._convert_text_to_binary( FAKE_ICMP_ENTRY['src'], 4)) ), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, str(conntrack._convert_text_to_binary( FAKE_ICMP_ENTRY['dst'], 4)) ), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once_with(conntrack_filter) nl_lib.nfct.nfct_close.assert_called_once_with(nl_lib.nfct.nfct_open( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK)) neutron-12.1.1/neutron/tests/unit/privileged/agent/__init__.py0000664000175000017500000000000013553660046024462 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/privileged/__init__.py0000664000175000017500000000000013553660046023364 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/notifiers/0000775000175000017500000000000013553660157021140 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/notifiers/__init__.py0000664000175000017500000000000013553660046023234 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/notifiers/test_nova.py0000664000175000017500000004146213553660047023521 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as n_const from neutron_lib import context as n_ctx from neutron_lib import exceptions as n_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from novaclient import api_versions from novaclient import exceptions as nova_exceptions from oslo_config import cfg from oslo_utils import uuidutils from sqlalchemy.orm import attributes as sql_attr from neutron.notifiers import nova from neutron.objects import ports as port_obj from neutron.tests import base DEVICE_OWNER_COMPUTE = n_const.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' DEVICE_OWNER_BAREMETAL = n_const.DEVICE_OWNER_BAREMETAL_PREFIX + 'fake' class TestNovaNotify(base.BaseTestCase): def setUp(self, plugin=None): super(TestNovaNotify, self).setUp() self.ctx = n_ctx.get_admin_context() self.port_uuid = uuidutils.generate_uuid() class FakePlugin(object): def get_port(self, context, port_id): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' return {'device_id': device_id, 'device_owner': DEVICE_OWNER_COMPUTE, 'id': port_id} self.nova_notifier = nova.Notifier() directory.add_plugin(plugin_constants.CORE, FakePlugin()) def test_notify_port_status_all_values(self): states = [n_const.PORT_STATUS_ACTIVE, n_const.PORT_STATUS_DOWN, n_const.PORT_STATUS_ERROR, n_const.PORT_STATUS_BUILD, sql_attr.NO_VALUE] device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' # test all combinations for previous_port_status in states: for current_port_status in states: port = port_obj.Port(self.ctx, id=self.port_uuid, device_id=device_id, device_owner=DEVICE_OWNER_COMPUTE, status=current_port_status) self._record_port_status_changed_helper(current_port_status, previous_port_status, port) def test_port_without_uuid_device_id_no_notify(self): port = port_obj.Port(self.ctx, id=self.port_uuid, device_id='compute_probe:', device_owner=DEVICE_OWNER_COMPUTE, status=n_const.PORT_STATUS_ACTIVE) self._record_port_status_changed_helper(n_const.PORT_STATUS_ACTIVE, sql_attr.NO_VALUE, port) def test_port_without_device_owner_no_notify(self): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' port = port_obj.Port(self.ctx, id=self.port_uuid, device_id=device_id, device_owner="", status=n_const.PORT_STATUS_ACTIVE) self._record_port_status_changed_helper(n_const.PORT_STATUS_ACTIVE, sql_attr.NO_VALUE, port) def test_port_without_device_id_no_notify(self): port = port_obj.Port(self.ctx, id=self.port_uuid, device_id="", device_owner=n_const.DEVICE_OWNER_DHCP, status=n_const.PORT_STATUS_ACTIVE) self._record_port_status_changed_helper(n_const.PORT_STATUS_ACTIVE, sql_attr.NO_VALUE, port) def test_non_compute_instances_no_notify(self): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' port = port_obj.Port(self.ctx, id=self.port_uuid, device_id=device_id, device_owner=n_const.DEVICE_OWNER_DHCP, status=n_const.PORT_STATUS_ACTIVE) self._record_port_status_changed_helper(n_const.PORT_STATUS_ACTIVE, sql_attr.NO_VALUE, port) def _record_port_status_changed_helper(self, current_port_status, previous_port_status, port): if not (port.device_id and port.id and port.device_owner and port.device_owner.startswith( n_const.DEVICE_OWNER_COMPUTE_PREFIX) and uuidutils.is_uuid_like(port.device_id)): return if (previous_port_status == n_const.PORT_STATUS_ACTIVE and current_port_status == n_const.PORT_STATUS_DOWN): event_name = nova.VIF_UNPLUGGED elif (previous_port_status in [sql_attr.NO_VALUE, n_const.PORT_STATUS_DOWN, n_const.PORT_STATUS_BUILD] and current_port_status in [n_const.PORT_STATUS_ACTIVE, n_const.PORT_STATUS_ERROR]): event_name = nova.VIF_PLUGGED else: return status = nova.NEUTRON_NOVA_EVENT_STATUS_MAP.get(current_port_status) self.nova_notifier.record_port_status_changed(port, current_port_status, previous_port_status, None) event = {'server_uuid': port.device_id, 'status': status, 'name': event_name, 'tag': self.port_uuid} self.assertEqual(event, port._notify_event) def test_update_fixed_ip_changed(self): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' returned_obj = {'port': {'device_owner': DEVICE_OWNER_COMPUTE, 'id': u'bee50827-bcee-4cc8-91c1-a27b0ce54222', 'device_id': device_id}} expected_event = {'server_uuid': device_id, 'name': 'network-changed', 'tag': returned_obj['port']['id']} event = self.nova_notifier.create_port_changed_event('update_port', {}, returned_obj) self.assertEqual(event, expected_event) def test_create_floatingip_notify(self): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' returned_obj = {'floatingip': {'port_id': u'bee50827-bcee-4cc8-91c1-a27b0ce54222'}} expected_event = {'server_uuid': device_id, 'name': 'network-changed', 'tag': returned_obj['floatingip']['port_id']} event = self.nova_notifier.create_port_changed_event( 'create_floatingip', {}, returned_obj) self.assertEqual(event, expected_event) def test_create_floatingip_no_port_id_no_notify(self): returned_obj = {'floatingip': {'port_id': None}} event = self.nova_notifier.create_port_changed_event( 'create_floatingip', {}, returned_obj) self.assertFalse(event, None) def test_delete_floatingip_notify(self): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' returned_obj = {'floatingip': {'port_id': u'bee50827-bcee-4cc8-91c1-a27b0ce54222'}} expected_event = {'server_uuid': device_id, 'name': 'network-changed', 'tag': returned_obj['floatingip']['port_id']} event = self.nova_notifier.create_port_changed_event( 'delete_floatingip', {}, returned_obj) self.assertEqual(expected_event, event) def test_delete_floatingip_deleted_port_no_notify(self): port_id = 'bee50827-bcee-4cc8-91c1-a27b0ce54222' with mock.patch.object( directory.get_plugin(), 'get_port', side_effect=n_exc.PortNotFound(port_id=port_id)): returned_obj = {'floatingip': {'port_id': port_id}} event = self.nova_notifier.create_port_changed_event( 'delete_floatingip', {}, returned_obj) self.assertIsNone(event) def test_delete_floatingip_no_port_id_no_notify(self): returned_obj = {'floatingip': {'port_id': None}} event = self.nova_notifier.create_port_changed_event( 'delete_floatingip', {}, returned_obj) self.assertIsNone(event) def test_associate_floatingip_notify(self): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' returned_obj = {'floatingip': {'port_id': u'5a39def4-3d3f-473d-9ff4-8e90064b9cc1'}} original_obj = {'port_id': None} expected_event = {'server_uuid': device_id, 'name': 'network-changed', 'tag': returned_obj['floatingip']['port_id']} event = self.nova_notifier.create_port_changed_event( 'update_floatingip', original_obj, returned_obj) self.assertEqual(expected_event, event) def test_disassociate_floatingip_notify(self): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' returned_obj = {'floatingip': {'port_id': None}} original_obj = {'port_id': '5a39def4-3d3f-473d-9ff4-8e90064b9cc1'} expected_event = {'server_uuid': device_id, 'name': 'network-changed', 'tag': original_obj['port_id']} event = self.nova_notifier.create_port_changed_event( 'update_floatingip', original_obj, returned_obj) self.assertEqual(expected_event, event) def test_no_notification_notify_nova_on_port_data_changes_false(self): cfg.CONF.set_override('notify_nova_on_port_data_changes', False) with mock.patch.object(self.nova_notifier, 'send_events') as send_events: self.nova_notifier.send_network_change('update_floatingip', {}, {}) self.assertFalse(send_events.called, False) def test_nova_send_events_returns_bad_list(self): with mock.patch.object( self.nova_notifier.nclient.server_external_events, 'create') as nclient_create: nclient_create.return_value = 'i am a string!' self.nova_notifier.send_events([]) def test_nova_send_event_rasies_404(self): with mock.patch.object( self.nova_notifier.nclient.server_external_events, 'create') as nclient_create: nclient_create.side_effect = nova_exceptions.NotFound self.nova_notifier.send_events([]) def test_nova_send_events_raises(self): with mock.patch.object( self.nova_notifier.nclient.server_external_events, 'create') as nclient_create: nclient_create.side_effect = Exception self.nova_notifier.send_events([]) def test_nova_send_events_returns_non_200(self): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' with mock.patch.object( self.nova_notifier.nclient.server_external_events, 'create') as nclient_create: nclient_create.return_value = [{'code': 404, 'name': 'network-changed', 'server_uuid': device_id}] self.nova_notifier.send_events( [{'name': 'network-changed', 'server_uuid': device_id}]) def test_nova_send_events_return_200(self): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' with mock.patch.object( self.nova_notifier.nclient.server_external_events, 'create') as nclient_create: nclient_create.return_value = [{'code': 200, 'name': 'network-changed', 'server_uuid': device_id}] self.nova_notifier.send_events( [{'name': 'network-changed', 'server_uuid': device_id}]) def test_nova_send_events_multiple(self): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' with mock.patch.object( self.nova_notifier.nclient.server_external_events, 'create') as nclient_create: nclient_create.return_value = [{'code': 200, 'name': 'network-changed', 'server_uuid': device_id}, {'code': 200, 'name': 'network-changed', 'server_uuid': device_id}] self.nova_notifier.send_events([ {'name': 'network-changed', 'server_uuid': device_id}, {'name': 'network-changed', 'server_uuid': device_id}]) def test_reassociate_floatingip_without_disassociate_event(self): returned_obj = {'floatingip': {'port_id': 'f5348a16-609a-4971-b0f0-4b8def5235fb'}} original_obj = {'port_id': '5a39def4-3d3f-473d-9ff4-8e90064b9cc1'} self.nova_notifier._waiting_to_send = True self.nova_notifier.send_network_change( 'update_floatingip', original_obj, returned_obj) self.assertEqual( 2, len(self.nova_notifier.batch_notifier.pending_events)) returned_obj_non = {'floatingip': {'port_id': None}} event_dis = self.nova_notifier.create_port_changed_event( 'update_floatingip', original_obj, returned_obj_non) event_assoc = self.nova_notifier.create_port_changed_event( 'update_floatingip', original_obj, returned_obj) self.assertEqual( self.nova_notifier.batch_notifier.pending_events[0], event_dis) self.assertEqual( self.nova_notifier.batch_notifier.pending_events[1], event_assoc) def test_delete_port_notify(self): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' port_id = 'bee50827-bcee-4cc8-91c1-a27b0ce54222' returned_obj = {'port': {'device_owner': DEVICE_OWNER_COMPUTE, 'id': port_id, 'device_id': device_id}} expected_event = {'server_uuid': device_id, 'name': nova.VIF_DELETED, 'tag': port_id} event = self.nova_notifier.create_port_changed_event('delete_port', {}, returned_obj) self.assertEqual(expected_event, event) @mock.patch('novaclient.client.Client') def test_endpoint_types(self, mock_client): nova.Notifier() mock_client.assert_called_once_with( api_versions.APIVersion(nova.NOVA_API_VERSION), session=mock.ANY, region_name=cfg.CONF.nova.region_name, endpoint_type='public', extensions=mock.ANY) mock_client.reset_mock() cfg.CONF.set_override('endpoint_type', 'internal', 'nova') nova.Notifier() mock_client.assert_called_once_with( api_versions.APIVersion(nova.NOVA_API_VERSION), session=mock.ANY, region_name=cfg.CONF.nova.region_name, endpoint_type='internal', extensions=mock.ANY) def test_notify_port_active_direct(self): device_id = '32102d7b-1cf4-404d-b50a-97aae1f55f87' port_id = 'bee50827-bcee-4cc8-91c1-a27b0ce54222' port = port_obj.Port(self.ctx, id=port_id, device_id=device_id, device_owner=DEVICE_OWNER_COMPUTE) expected_event = {'server_uuid': device_id, 'name': nova.VIF_PLUGGED, 'status': 'completed', 'tag': port_id} self.nova_notifier.notify_port_active_direct(port) self.assertEqual( 1, len(self.nova_notifier.batch_notifier.pending_events)) self.assertEqual(expected_event, self.nova_notifier.batch_notifier.pending_events[0]) neutron-12.1.1/neutron/tests/unit/notifiers/test_batch_notifier.py0000664000175000017500000000471213553660047025533 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import eventlet import mock from neutron.notifiers import batch_notifier from neutron.tests import base class TestBatchNotifier(base.BaseTestCase): def setUp(self): super(TestBatchNotifier, self).setUp() self.notifier = batch_notifier.BatchNotifier(0.1, lambda x: x) self.spawn_n_p = mock.patch('eventlet.spawn_n') self.spawn_n = self.spawn_n_p.start() def test_queue_event_no_event(self): self.notifier.queue_event(None) self.assertEqual(0, len(self.notifier.pending_events)) self.assertEqual(0, self.spawn_n.call_count) def test_queue_event_first_event(self): self.notifier.queue_event(mock.Mock()) self.assertEqual(1, len(self.notifier.pending_events)) self.assertEqual(1, self.spawn_n.call_count) def test_queue_event_multiple_events(self): self.spawn_n_p.stop() c_mock = mock.patch.object(self.notifier, 'callback').start() events = 6 for i in range(0, events): self.notifier.queue_event(mock.Mock()) eventlet.sleep(0) # yield to let coro execute while self.notifier.pending_events: # wait for coroutines to finish eventlet.sleep(0.1) self.assertEqual(2, c_mock.call_count) self.assertEqual(6, sum(len(c[0][0]) for c in c_mock.call_args_list)) self.assertEqual(0, len(self.notifier.pending_events)) def test_queue_event_call_send_events(self): with mock.patch.object(self.notifier, 'callback') as send_events: self.spawn_n.side_effect = lambda func: func() self.notifier.queue_event(mock.Mock()) while self.notifier.pending_events: # wait for coroutines to finish eventlet.sleep(0.1) self.assertTrue(send_events.called) neutron-12.1.1/neutron/tests/unit/cmd/0000775000175000017500000000000013553660156017700 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/cmd/test_ovs_cleanup.py0000664000175000017500000000444613553660047023636 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools import mock from oslo_utils import uuidutils from neutron.agent.common import ovs_lib from neutron.agent.linux import ip_lib from neutron.cmd import ovs_cleanup as util from neutron.tests import base class TestOVSCleanup(base.BaseTestCase): def test_collect_neutron_ports(self): port1 = ovs_lib.VifPort('tap1234', 1, uuidutils.generate_uuid(), '11:22:33:44:55:66', 'br') port2 = ovs_lib.VifPort('tap5678', 2, uuidutils.generate_uuid(), '77:88:99:aa:bb:cc', 'br') port3 = ovs_lib.VifPort('tap90ab', 3, uuidutils.generate_uuid(), '99:00:aa:bb:cc:dd', 'br') ports = [[port1, port2], [port3]] portnames = [p.port_name for p in itertools.chain(*ports)] with mock.patch('neutron.agent.common.ovs_lib.OVSBridge') as ovs: ovs.return_value.get_vif_ports.side_effect = ports bridges = ['br-int', 'br-ex'] ret = util.collect_neutron_ports(bridges) self.assertEqual(ret, portnames) @mock.patch.object(ip_lib, 'IPDevice') def test_delete_neutron_ports(self, mock_ip): ports = ['tap1234', 'tap5678', 'tap09ab'] port_found = [True, False, True] mock_ip.return_value.exists.side_effect = port_found util.delete_neutron_ports(ports) mock_ip.assert_has_calls( [mock.call('tap1234'), mock.call().exists(), mock.call().link.delete(), mock.call('tap5678'), mock.call().exists(), mock.call('tap09ab'), mock.call().exists(), mock.call().link.delete()]) neutron-12.1.1/neutron/tests/unit/cmd/test_sanity_check.py0000664000175000017500000000155013553660047023755 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.cmd import sanity_check from neutron.tests import base class TestSanityCheck(base.BaseTestCase): def test_setup_conf(self): # verify that configuration can be successfully imported sanity_check.setup_conf() neutron-12.1.1/neutron/tests/unit/cmd/__init__.py0000664000175000017500000000000013553660046021775 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/cmd/test_netns_cleanup.py0000664000175000017500000004250313553660047024152 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import signal import mock import testtools from neutron.cmd import netns_cleanup as util from neutron.tests import base NETSTAT_NETNS_OUTPUT = (""" Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State\ PID/Program name tcp 0 0 0.0.0.0:9697 0.0.0.0:* LISTEN\ 1347/python raw 0 0 0.0.0.0:112 0.0.0.0:* 7\ 1279/keepalived raw 0 0 0.0.0.0:112 0.0.0.0:* 7\ 1279/keepalived raw6 0 0 :::58 :::* 7\ 1349/radvd Active UNIX domain sockets (only servers) Proto RefCnt Flags Type State I-Node PID/Program name\ Path unix 2 [ ACC ] STREAM LISTENING 82039530 1353/python\ /tmp/rootwrap-VKSm8a/rootwrap.sock """) NETSTAT_NO_NAMESPACE = (""" Cannot open network namespace "qrouter-e6f206b2-4e8d-4597-a7e1-c3a20337e9c6":\ No such file or directory """) NETSTAT_NO_LISTEN_PROCS = (""" Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State\ PID/Program name Active UNIX domain sockets (only servers) Proto RefCnt Flags Type State I-Node PID/Program name\ Path """) class TestNetnsCleanup(base.BaseTestCase): def setUp(self): super(TestNetnsCleanup, self).setUp() conn_patcher = mock.patch( 'neutron.agent.ovsdb.impl_idl._connection') conn_patcher.start() self.addCleanup(conn_patcher.stop) def test_kill_dhcp(self, dhcp_active=True): conf = mock.Mock() conf.dhcp_driver = 'driver' method_to_patch = 'oslo_utils.importutils.import_object' with mock.patch(method_to_patch) as import_object: driver = mock.Mock() driver.active = dhcp_active import_object.return_value = driver util.kill_dhcp(conf, 'ns') expected_params = {'conf': conf, 'network': mock.ANY, 'process_monitor': mock.ANY, 'plugin': mock.ANY} import_object.assert_called_once_with('driver', **expected_params) if dhcp_active: driver.assert_has_calls([mock.call.disable()]) else: self.assertFalse(driver.called) def test_kill_dhcp_no_active(self): self.test_kill_dhcp(False) def test_eligible_for_deletion_ns_not_uuid(self): conf = mock.Mock() conf.agent_type = None ns = 'not_a_uuid' self.assertFalse(util.eligible_for_deletion(conf, ns)) def _test_eligible_for_deletion_helper(self, prefix, force, is_empty, expected): ns = prefix + '6e322ac7-ab50-4f53-9cdc-d1d3c1164b6d' conf = mock.Mock() conf.agent_type = None with mock.patch('neutron.agent.linux.ip_lib.IPWrapper') as ip_wrap: ip_wrap.return_value.namespace_is_empty.return_value = is_empty self.assertEqual(expected, util.eligible_for_deletion(conf, ns, force)) expected_calls = [mock.call(namespace=ns)] if not force: expected_calls.append(mock.call().namespace_is_empty()) ip_wrap.assert_has_calls(expected_calls) def test_eligible_for_deletion_empty(self): self._test_eligible_for_deletion_helper('qrouter-', False, True, True) def test_eligible_for_deletion_not_empty(self): self._test_eligible_for_deletion_helper('qdhcp-', False, False, False) def test_eligible_for_deletion_not_empty_forced(self): self._test_eligible_for_deletion_helper('qdhcp-', True, False, True) def test_eligible_for_deletion_fip_namespace(self): self._test_eligible_for_deletion_helper('fip-', False, True, True) def test_eligible_for_deletion_lbaas_namespace(self): self._test_eligible_for_deletion_helper('qlbaas-', False, True, True) def test_eligible_for_deletion_snat_namespace(self): self._test_eligible_for_deletion_helper('snat-', False, True, True) def test_eligible_for_deletion_filtered_by_agent_type(self): ns_dhcp = 'qdhcp-' + '6e322ac7-ab50-4f53-9cdc-d1d3c1164b6d' ns_l3 = 'qrouter-' + '6e322ac7-ab50-4f53-9cdc-d1d3c1164b6d' conf = mock.Mock() conf.agent_type = 'dhcp' with mock.patch('neutron.agent.linux.ip_lib.IPWrapper') as ip_wrap: ip_wrap.return_value.namespace_is_empty.return_value = True self.assertTrue(util.eligible_for_deletion(conf, ns_dhcp, False)) self.assertFalse(util.eligible_for_deletion(conf, ns_l3, False)) expected_calls = [mock.call(namespace=ns_dhcp), mock.call().namespace_is_empty()] ip_wrap.assert_has_calls(expected_calls) def test_unplug_device_regular_device(self): conf = mock.Mock() device = mock.Mock() util.unplug_device(conf, device) device.assert_has_calls([mock.call.link.delete()]) def test_unplug_device_ovs_port(self): conf = mock.Mock() conf.ovs_integration_bridge = 'br-int' device = mock.Mock() device.name = 'tap1' device.link.delete.side_effect = RuntimeError with mock.patch( 'neutron.agent.common.ovs_lib.OVSBridge') as ovs_br_cls: br_patch = mock.patch( 'neutron.agent.common.ovs_lib.BaseOVS.get_bridge_for_iface') with br_patch as mock_get_bridge_for_iface: mock_get_bridge_for_iface.return_value = 'br-int' ovs_bridge = mock.Mock() ovs_br_cls.return_value = ovs_bridge util.unplug_device(conf, device) mock_get_bridge_for_iface.assert_called_once_with('tap1') ovs_br_cls.assert_called_once_with('br-int') ovs_bridge.assert_has_calls( [mock.call.delete_port(device.name)]) def test_unplug_device_cannot_determine_bridge_port(self): conf = mock.Mock() conf.ovs_integration_bridge = 'br-int' device = mock.Mock() device.name = 'tap1' device.link.delete.side_effect = RuntimeError with mock.patch( 'neutron.agent.common.ovs_lib.OVSBridge') as ovs_br_cls: br_patch = mock.patch( 'neutron.agent.common.ovs_lib.BaseOVS.get_bridge_for_iface') with br_patch as mock_get_bridge_for_iface: with mock.patch.object(util.LOG, 'debug') as debug: mock_get_bridge_for_iface.return_value = None ovs_bridge = mock.Mock() ovs_br_cls.return_value = ovs_bridge util.unplug_device(conf, device) mock_get_bridge_for_iface.assert_called_once_with('tap1') self.assertEqual([], ovs_br_cls.mock_calls) self.assertTrue(debug.called) def _test_find_listen_pids_namespace_helper(self, expected, netstat_output=None): with mock.patch('neutron.agent.linux.ip_lib.IPWrapper') as ip_wrap: ip_wrap.return_value.netns.execute.return_value = netstat_output observed = util.find_listen_pids_namespace(mock.ANY) self.assertEqual(expected, observed) def test_find_listen_pids_namespace_correct_output(self): expected = set(['1347', '1279', '1349', '1353']) self._test_find_listen_pids_namespace_helper(expected, NETSTAT_NETNS_OUTPUT) def test_find_listen_pids_namespace_no_procs(self): expected = set() self._test_find_listen_pids_namespace_helper(expected, NETSTAT_NO_LISTEN_PROCS) def test_find_listen_pids_namespace_no_namespace(self): expected = set() self._test_find_listen_pids_namespace_helper(expected, NETSTAT_NO_NAMESPACE) def _test__kill_listen_processes_helper(self, pids, parents, children, kills_expected, force): def _get_element(dct, x): return dct.get(x, []) def _find_childs(x, recursive): return _get_element(children, x) def _find_parent(x): return _get_element(parents, x) utils_mock = dict( find_fork_top_parent=mock.DEFAULT, find_child_pids=mock.DEFAULT, get_cmdline_from_pid=mock.DEFAULT, kill_process=mock.DEFAULT) self.log_mock = mock.patch.object(util, 'LOG').start() with mock.patch.multiple('neutron.agent.linux.utils', **utils_mock)\ as mocks: mocks['find_fork_top_parent'].side_effect = _find_parent mocks['find_child_pids'].side_effect = _find_childs with mock.patch.object(util, 'find_listen_pids_namespace', return_value=pids): calls = [] for pid, sig in kills_expected: calls.append(mock.call(pid, sig, run_as_root=True)) util._kill_listen_processes(mock.ANY, force=force) mocks['kill_process'].assert_has_calls(calls, any_order=True) def test__kill_listen_processes_only_parents_force_false(self): pids = ['4', '5', '6'] parents = {'4': '1', '5': '5', '6': '2'} children = {} kills_expected = [('1', signal.SIGTERM), ('5', signal.SIGTERM), ('2', signal.SIGTERM)] self._test__kill_listen_processes_helper(pids, parents, children, kills_expected, False) def test__kill_listen_processes_parents_and_childs(self): pids = ['4', '5', '6'] parents = {'4': '1', '5': '2', '6': '3'} children = {'1': ['4'], '2': ['5'], '3': ['6', '8', '7']} kills_expected = [(str(x), signal.SIGKILL) for x in range(1, 9)] self._test__kill_listen_processes_helper(pids, parents, children, kills_expected, True) def test_kill_listen_processes(self): with mock.patch.object(util, '_kill_listen_processes', return_value=1) as mock_kill_listen: with mock.patch.object(util, 'wait_until_no_listen_pids_namespace', side_effect=[util.PidsInNamespaceException, None]): namespace = mock.ANY util.kill_listen_processes(namespace) mock_kill_listen.assert_has_calls( [mock.call(namespace, force=False), mock.call(namespace, force=True)]) def test_kill_listen_processes_still_procs(self): with mock.patch.object(util, '_kill_listen_processes', return_value=1): with mock.patch.object(util, 'wait_until_no_listen_pids_namespace', side_effect=util.PidsInNamespaceException): namespace = mock.ANY with testtools.ExpectedException( util.PidsInNamespaceException): util.kill_listen_processes(namespace) def test_kill_listen_processes_no_procs(self): with mock.patch.object(util, '_kill_listen_processes', return_value=0) as mock_kill_listen: with mock.patch.object(util, 'wait_until_no_listen_pids_namespace')\ as wait_until_mock: namespace = mock.ANY util.kill_listen_processes(namespace) mock_kill_listen.assert_called_once_with(namespace, force=False) self.assertFalse(wait_until_mock.called) def _test_destroy_namespace_helper(self, force, num_devices): ns = 'qrouter-6e322ac7-ab50-4f53-9cdc-d1d3c1164b6d' conf = mock.Mock() lo_device = mock.Mock() lo_device.name = 'lo' devices = [lo_device] while num_devices: dev = mock.Mock() dev.name = 'tap%d' % num_devices devices.append(dev) num_devices -= 1 with mock.patch('neutron.agent.linux.ip_lib.IPWrapper') as ip_wrap: ip_wrap.return_value.get_devices.return_value = devices ip_wrap.return_value.netns.exists.return_value = True with mock.patch.object(util, 'kill_listen_processes'): with mock.patch.object(util, 'unplug_device') as unplug: with mock.patch.object(util, 'kill_dhcp') as kill_dhcp: util.destroy_namespace(conf, ns, force) expected = [mock.call(namespace=ns)] if force: expected.extend([ mock.call().netns.exists(ns), mock.call().get_devices()]) self.assertTrue(kill_dhcp.called) unplug.assert_has_calls( [mock.call(conf, d) for d in devices[1:]]) expected.append( mock.call().garbage_collect_namespace()) ip_wrap.assert_has_calls(expected) def test_destroy_namespace_empty(self): self._test_destroy_namespace_helper(False, 0) def test_destroy_namespace_not_empty(self): self._test_destroy_namespace_helper(False, 1) def test_destroy_namespace_not_empty_forced(self): self._test_destroy_namespace_helper(True, 2) def test_destroy_namespace_exception(self): ns = 'qrouter-6e322ac7-ab50-4f53-9cdc-d1d3c1164b6d' conf = mock.Mock() with mock.patch('neutron.agent.linux.ip_lib.IPWrapper') as ip_wrap: ip_wrap.side_effect = Exception() util.destroy_namespace(conf, ns) def test_main(self): namespaces = ['ns1', 'ns2'] with mock.patch('neutron.agent.linux.ip_lib.' 'list_network_namespaces') as listnetns: listnetns.return_value = namespaces with mock.patch('time.sleep') as time_sleep: conf = mock.Mock() conf.force = False methods_to_mock = dict( eligible_for_deletion=mock.DEFAULT, destroy_namespace=mock.DEFAULT, setup_conf=mock.DEFAULT) with mock.patch.multiple(util, **methods_to_mock) as mocks: mocks['eligible_for_deletion'].return_value = True mocks['setup_conf'].return_value = conf with mock.patch('neutron.common.config.setup_logging'): util.main() mocks['eligible_for_deletion'].assert_has_calls( [mock.call(conf, 'ns1', False), mock.call(conf, 'ns2', False)]) mocks['destroy_namespace'].assert_has_calls( [mock.call(conf, 'ns1', False), mock.call(conf, 'ns2', False)]) self.assertEqual(1, listnetns.call_count) time_sleep.assert_called_once_with(2) def test_main_no_candidates(self): namespaces = ['ns1', 'ns2'] with mock.patch('neutron.agent.linux.ip_lib.' 'list_network_namespaces') as listnetns: listnetns.return_value = namespaces with mock.patch('time.sleep') as time_sleep: conf = mock.Mock() conf.force = False methods_to_mock = dict( eligible_for_deletion=mock.DEFAULT, destroy_namespace=mock.DEFAULT, setup_conf=mock.DEFAULT) with mock.patch.multiple(util, **methods_to_mock) as mocks: mocks['eligible_for_deletion'].return_value = False mocks['setup_conf'].return_value = conf with mock.patch('neutron.common.config.setup_logging'): util.main() self.assertEqual(1, listnetns.call_count) mocks['eligible_for_deletion'].assert_has_calls( [mock.call(conf, 'ns1', False), mock.call(conf, 'ns2', False)]) self.assertFalse(mocks['destroy_namespace'].called) self.assertFalse(time_sleep.called) neutron-12.1.1/neutron/tests/unit/conf/0000775000175000017500000000000013553660156020062 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/conf/agent/0000775000175000017500000000000013553660157021161 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/conf/agent/test_common.py0000664000175000017500000000306113553660046024057 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.conf.agent import common as config from neutron.tests import base def test_setup_conf(): conf = config.setup_conf() assert conf.state_path.endswith('/var/lib/neutron') class TestRootHelper(base.BaseTestCase): def test_agent_root_helper(self): conf = config.setup_conf() config.register_root_helper(conf) conf.set_override('root_helper', 'my_root_helper', 'AGENT') self.assertEqual(config.get_root_helper(conf), 'my_root_helper') def test_root_default(self): conf = config.setup_conf() config.register_root_helper(conf) self.assertEqual(config.get_root_helper(conf), 'sudo') def test_agent_root_helper_daemon(self): conf = config.setup_conf() config.register_root_helper(conf) rhd = 'my_root_helper_daemon' conf.set_override('root_helper_daemon', rhd, 'AGENT') self.assertEqual(rhd, conf.AGENT.root_helper_daemon) neutron-12.1.1/neutron/tests/unit/conf/agent/__init__.py0000664000175000017500000000000013553660046023255 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/hacking/0000775000175000017500000000000013553660157020542 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/hacking/test_checks.py0000664000175000017500000003041413553660047023413 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import re from flake8 import engine from hacking.tests import test_doctest as hacking_doctest import pep8 import pkg_resources import testscenarios import testtools from testtools import content from testtools import matchers from neutron.hacking import checks from neutron.tests import base CREATE_DUMMY_MATCH_OBJECT = re.compile('a') class HackingTestCase(base.BaseTestCase): def assertLinePasses(self, func, line): with testtools.ExpectedException(StopIteration): next(func(line)) def assertLineFails(self, func, line): self.assertIsInstance(next(func(line)), tuple) def test_assert_called_once_with(self): fail_code1 = """ mock = Mock() mock.method(1, 2, 3, test='wow') mock.method.assert_called_once() """ fail_code2 = """ mock = Mock() mock.method(1, 2, 3, test='wow') mock.method.assertCalledOnceWith() """ fail_code3 = """ mock = Mock() mock.method(1, 2, 3, test='wow') mock.method.called_once_with() """ fail_code4 = """ mock = Mock() mock.method(1, 2, 3, test='wow') mock.method.assert_has_called() """ pass_code = """ mock = Mock() mock.method(1, 2, 3, test='wow') mock.method.assert_called_once_with() """ pass_code2 = """ mock = Mock() mock.method(1, 2, 3, test='wow') mock.method.assert_has_calls() """ self.assertEqual( 1, len(list(checks.check_assert_called_once_with(fail_code1, "neutron/tests/test_assert.py")))) self.assertEqual( 1, len(list(checks.check_assert_called_once_with(fail_code2, "neutron/tests/test_assert.py")))) self.assertEqual( 1, len(list(checks.check_assert_called_once_with(fail_code3, "neutron/tests/test_assert.py")))) self.assertEqual( 0, len(list(checks.check_assert_called_once_with(pass_code, "neutron/tests/test_assert.py")))) self.assertEqual( 1, len(list(checks.check_assert_called_once_with(fail_code4, "neutron/tests/test_assert.py")))) self.assertEqual( 0, len(list(checks.check_assert_called_once_with(pass_code2, "neutron/tests/test_assert.py")))) def test_asserttruefalse(self): true_fail_code1 = """ test_bool = True self.assertEqual(True, test_bool) """ true_fail_code2 = """ test_bool = True self.assertEqual(test_bool, True) """ true_pass_code = """ test_bool = True self.assertTrue(test_bool) """ false_fail_code1 = """ test_bool = False self.assertEqual(False, test_bool) """ false_fail_code2 = """ test_bool = False self.assertEqual(test_bool, False) """ false_pass_code = """ test_bool = False self.assertFalse(test_bool) """ self.assertEqual( 1, len(list( checks.check_asserttruefalse(true_fail_code1, "neutron/tests/test_assert.py")))) self.assertEqual( 1, len(list( checks.check_asserttruefalse(true_fail_code2, "neutron/tests/test_assert.py")))) self.assertEqual( 0, len(list( checks.check_asserttruefalse(true_pass_code, "neutron/tests/test_assert.py")))) self.assertEqual( 1, len(list( checks.check_asserttruefalse(false_fail_code1, "neutron/tests/test_assert.py")))) self.assertEqual( 1, len(list( checks.check_asserttruefalse(false_fail_code2, "neutron/tests/test_assert.py")))) self.assertFalse( list( checks.check_asserttruefalse(false_pass_code, "neutron/tests/test_assert.py"))) def test_assertempty(self): fail_code = """ test_empty = %s self.assertEqual(test_empty, %s) """ pass_code1 = """ test_empty = %s self.assertEqual(%s, test_empty) """ pass_code2 = """ self.assertEqual(123, foo(abc, %s)) """ empty_cases = ['{}', '[]', '""', "''", '()', 'set()'] for ec in empty_cases: self.assertEqual( 1, len(list(checks.check_assertempty(fail_code % (ec, ec), "neutron/tests/test_assert.py")))) self.assertEqual( 0, len(list(checks.check_asserttruefalse(pass_code1 % (ec, ec), "neutron/tests/test_assert.py")))) self.assertEqual( 0, len(list(checks.check_asserttruefalse(pass_code2 % ec, "neutron/tests/test_assert.py")))) def test_assertisinstance(self): fail_code = """ self.assertTrue(isinstance(observed, ANY_TYPE)) """ pass_code1 = """ self.assertEqual(ANY_TYPE, type(observed)) """ pass_code2 = """ self.assertIsInstance(observed, ANY_TYPE) """ self.assertEqual( 1, len(list(checks.check_assertisinstance(fail_code, "neutron/tests/test_assert.py")))) self.assertEqual( 0, len(list(checks.check_assertisinstance(pass_code1, "neutron/tests/test_assert.py")))) self.assertEqual( 0, len(list(checks.check_assertisinstance(pass_code2, "neutron/tests/test_assert.py")))) def test_assertequal_for_httpcode(self): fail_code = """ self.assertEqual(res.status_int, webob.exc.HTTPNoContent.code) """ pass_code = """ self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) """ self.assertEqual( 1, len(list(checks.check_assertequal_for_httpcode(fail_code, "neutron/tests/test_assert.py")))) self.assertEqual( 0, len(list(checks.check_assertequal_for_httpcode(pass_code, "neutron/tests/test_assert.py")))) def test_unittest_imports(self): f = checks.check_unittest_imports self.assertLinePasses(f, 'from unittest2') self.assertLinePasses(f, 'import unittest2') self.assertLinePasses(f, 'from unitest2 import case') self.assertLinePasses(f, 'unittest2.TestSuite') self.assertLineFails(f, 'from unittest import case') self.assertLineFails(f, 'from unittest.TestSuite') self.assertLineFails(f, 'import unittest') def test_check_no_imports_from_tests(self): fail_codes = ('from neutron import tests', 'from neutron.tests import base', 'import neutron.tests.base') for fail_code in fail_codes: self.assertEqual( 1, len(list( checks.check_no_imports_from_tests( fail_code, "neutron/common/rpc.py", None)))) self.assertEqual( 0, len(list( checks.check_no_imports_from_tests( fail_code, "neutron/tests/test_fake.py", None)))) def test_check_python3_filter(self): f = checks.check_python3_no_filter self.assertLineFails(f, "filter(lambda obj: test(obj), data)") self.assertLinePasses(f, "[obj for obj in data if test(obj)]") self.assertLinePasses(f, "filter(function, range(0,10))") self.assertLinePasses(f, "lambda x, y: x+y") # The following is borrowed from hacking/tests/test_doctest.py. # Tests defined in docstring is easier to understand # in some cases, for example, hacking rules which take tokens as argument. # TODO(amotoki): Migrate existing unit tests above to docstring tests. # NOTE(amotoki): Is it better to enhance HackingDocTestCase in hacking repo to # pass filename to pep8.Checker so that we can reuse it in this test. # I am not sure whether unit test class is public. SELFTEST_REGEX = re.compile(r'\b(Okay|N\d{3})(\((\S+)\))?:\s(.*)') # Each scenario is (name, dict(filename=..., lines=.., options=..., code=...)) file_cases = [] class HackingDocTestCase(hacking_doctest.HackingTestCase): scenarios = file_cases def test_pep8(self): # NOTE(jecarey): Add tests marked as off_by_default to enable testing turn_on = set(['H106']) if self.options.select: turn_on.update(self.options.select) self.options.select = tuple(turn_on) self.options.ignore = ('N530',) report = pep8.BaseReport(self.options) checker = pep8.Checker(filename=self.filename, lines=self.lines, options=self.options, report=report) checker.check_all() self.addDetail('doctest', content.text_content(self.raw)) if self.code == 'Okay': self.assertThat( len(report.counters), matchers.Not(matchers.GreaterThan( len(self.options.benchmark_keys))), "incorrectly found %s" % ', '.join( [key for key in report.counters if key not in self.options.benchmark_keys])) else: self.addDetail('reason', content.text_content("Failed to trigger rule %s" % self.code)) self.assertIn(self.code, report.counters) def _get_lines(check): for line in check.__doc__.splitlines(): line = line.lstrip() match = SELFTEST_REGEX.match(line) if match is None: continue yield (line, match.groups()) def load_tests(loader, tests, pattern): default_checks = [e.name for e in pkg_resources.iter_entry_points('flake8.extension')] flake8_style = engine.get_style_guide( parse_argv=False, # We are testing neutron-specific hacking rules, so there is no need # to run the checks registered by hacking or other flake8 extensions. ignore=default_checks) options = flake8_style.options for name, check in checks.__dict__.items(): if not hasattr(check, 'name'): continue if check.name != checks.__name__: continue if not check.__doc__: continue for (lineno, (raw, line)) in enumerate(_get_lines(check)): code, __, filename, source = line lines = [part.replace(r'\t', '\t') + '\n' for part in source.split(r'\n')] file_cases.append(("%s-line-%s" % (name, lineno), dict(lines=lines, raw=raw, options=options, code=code, filename=filename))) return testscenarios.load_tests_apply_scenarios(loader, tests, pattern) neutron-12.1.1/neutron/tests/unit/hacking/__init__.py0000664000175000017500000000000013553660046022636 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/debug/0000775000175000017500000000000013553660157020224 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/debug/__init__.py0000664000175000017500000000000013553660046022320 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/debug/test_commands.py0000664000175000017500000003750413553660047023445 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import socket import mock from neutron_lib.api.definitions import portbindings from oslo_config import cfg from neutron.agent.linux import interface from neutron.common import config as common_config from neutron.conf.agent import common as config from neutron.debug import commands from neutron.debug import debug_agent from neutron.tests import base class MyApp(object): def __init__(self, _stdout): self.stdout = _stdout class TestDebugCommands(base.BaseTestCase): def setUp(self): super(TestDebugCommands, self).setUp() config.register_interface_opts() cfg.CONF.register_opts(config.EXT_NET_BRIDGE_OPTS) common_config.init([]) config.register_interface_driver_opts_helper(cfg.CONF) device_exists_p = mock.patch( 'neutron.agent.linux.ip_lib.device_exists', return_value=False) device_exists_p.start() namespace_e_p = mock.patch( 'neutron.agent.linux.ip_lib.network_namespace_exists') namespace_e_p.start() namespace_d_p = mock.patch( 'neutron.agent.linux.ip_lib.delete_network_namespace') namespace_d_p.start() ensure_namespace_p = mock.patch( 'neutron.agent.linux.ip_lib.IPWrapper.ensure_namespace') ensure_namespace_p.start() dvr_cls_p = mock.patch('neutron.agent.linux.interface.NullDriver') driver_cls = dvr_cls_p.start() mock_driver = mock.MagicMock() mock_driver.DEV_NAME_LEN = ( interface.LinuxInterfaceDriver.DEV_NAME_LEN) mock_driver.get_device_name.return_value = 'tap12345678-12' driver_cls.return_value = mock_driver self.driver = mock_driver client_cls_p = mock.patch('neutronclient.v2_0.client.Client') client_cls = client_cls_p.start() client_inst = mock.Mock() client_cls.return_value = client_inst fake_network = {'network': {'id': 'fake_net', 'tenant_id': 'fake_tenant', 'subnets': ['fake_subnet']}} fake_port = {'port': {'id': 'fake_port', 'device_owner': 'fake_device', 'mac_address': 'aa:bb:cc:dd:ee:ffa', 'network_id': 'fake_net', 'fixed_ips': [{'subnet_id': 'fake_subnet', 'ip_address': '10.0.0.3'}] }} fake_ports = {'ports': [fake_port['port']]} self.fake_ports = fake_ports allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.254'}] fake_subnet_v4 = {'subnet': {'name': 'fake_subnet_v4', 'id': 'fake_subnet', 'network_id': 'fake_net', 'gateway_ip': '10.0.0.1', 'dns_nameservers': ['10.0.0.2'], 'host_routes': [], 'cidr': '10.0.0.0/24', 'allocation_pools': allocation_pools, 'enable_dhcp': True, 'ip_version': 4}} client_inst.list_ports.return_value = fake_ports client_inst.create_port.return_value = fake_port client_inst.show_port.return_value = fake_port client_inst.show_network.return_value = fake_network client_inst.show_subnet.return_value = fake_subnet_v4 self.client = client_inst mock_std = mock.Mock() self.app = MyApp(mock_std) self.app.debug_agent = debug_agent.NeutronDebugAgent(cfg.CONF, client_inst, mock_driver) def _test_create_probe(self, device_owner): cmd = commands.CreateProbe(self.app, None) cmd_parser = cmd.get_parser('create_probe') if device_owner == debug_agent.DEVICE_OWNER_COMPUTE_PROBE: args = ['fake_net', '--device-owner', 'compute'] else: args = ['fake_net'] parsed_args = cmd_parser.parse_args(args) cmd.run(parsed_args) fake_port = {'port': {'device_owner': device_owner, 'admin_state_up': True, 'network_id': 'fake_net', 'tenant_id': 'fake_tenant', portbindings.HOST_ID: cfg.CONF.host, 'fixed_ips': [{'subnet_id': 'fake_subnet'}], 'device_id': socket.gethostname()}} namespace = 'qprobe-fake_port' self.client.assert_has_calls([mock.call.show_network('fake_net'), mock.call.show_subnet('fake_subnet'), mock.call.create_port(fake_port), mock.call.show_subnet('fake_subnet')]) self.driver.assert_has_calls([mock.call.get_device_name(mock.ANY), mock.call.plug('fake_net', 'fake_port', 'tap12345678-12', 'aa:bb:cc:dd:ee:ffa', bridge=None, namespace=namespace), mock.call.init_l3('tap12345678-12', ['10.0.0.3/24'], namespace=namespace )]) def test_create_network_probe(self): self._test_create_probe(debug_agent.DEVICE_OWNER_NETWORK_PROBE) def test_create_nova_probe(self): self._test_create_probe(debug_agent.DEVICE_OWNER_COMPUTE_PROBE) def _test_create_probe_external(self, device_owner): fake_network = {'network': {'id': 'fake_net', 'tenant_id': 'fake_tenant', 'router:external': True, 'subnets': ['fake_subnet']}} self.client.show_network.return_value = fake_network cmd = commands.CreateProbe(self.app, None) cmd_parser = cmd.get_parser('create_probe') if device_owner == debug_agent.DEVICE_OWNER_COMPUTE_PROBE: args = ['fake_net', '--device-owner', 'compute'] else: args = ['fake_net'] parsed_args = cmd_parser.parse_args(args) cmd.run(parsed_args) fake_port = {'port': {'device_owner': device_owner, 'admin_state_up': True, 'network_id': 'fake_net', 'tenant_id': 'fake_tenant', portbindings.HOST_ID: cfg.CONF.host, 'fixed_ips': [{'subnet_id': 'fake_subnet'}], 'device_id': socket.gethostname()}} namespace = 'qprobe-fake_port' self.client.assert_has_calls([mock.call.show_network('fake_net'), mock.call.show_subnet('fake_subnet'), mock.call.create_port(fake_port), mock.call.show_subnet('fake_subnet')]) self.driver.assert_has_calls([mock.call.get_device_name(mock.ANY), mock.call.plug('fake_net', 'fake_port', 'tap12345678-12', 'aa:bb:cc:dd:ee:ffa', bridge='', namespace=namespace), mock.call.init_l3('tap12345678-12', ['10.0.0.3/24'], namespace=namespace )]) def test_create_network_probe_external(self): self._test_create_probe_external( debug_agent.DEVICE_OWNER_NETWORK_PROBE) def test_create_nova_probe_external(self): self._test_create_probe_external( debug_agent.DEVICE_OWNER_COMPUTE_PROBE) def test_delete_probe(self): cmd = commands.DeleteProbe(self.app, None) cmd_parser = cmd.get_parser('delete_probe') args = ['fake_port'] parsed_args = cmd_parser.parse_args(args) cmd.run(parsed_args) namespace = 'qprobe-fake_port' self.client.assert_has_calls([mock.call.show_port('fake_port'), mock.call.show_network('fake_net'), mock.call.show_subnet('fake_subnet'), mock.call.delete_port('fake_port')]) self.driver.assert_has_calls([mock.call.get_device_name(mock.ANY), mock.call.unplug('tap12345678-12', namespace=namespace, bridge=None)]) def test_delete_probe_external(self): fake_network = {'network': {'id': 'fake_net', 'tenant_id': 'fake_tenant', 'router:external': True, 'subnets': ['fake_subnet']}} self.client.show_network.return_value = fake_network cmd = commands.DeleteProbe(self.app, None) cmd_parser = cmd.get_parser('delete_probe') args = ['fake_port'] parsed_args = cmd_parser.parse_args(args) cmd.run(parsed_args) namespace = 'qprobe-fake_port' self.client.assert_has_calls([mock.call.show_port('fake_port'), mock.call.show_network('fake_net'), mock.call.show_subnet('fake_subnet'), mock.call.delete_port('fake_port')]) self.driver.assert_has_calls([mock.call.get_device_name(mock.ANY), mock.call.unplug('tap12345678-12', namespace=namespace, bridge='')]) def test_list_probe(self): cmd = commands.ListProbe(self.app, None) cmd_parser = cmd.get_parser('list_probe') args = [] parsed_args = cmd_parser.parse_args(args) cmd.run(parsed_args) self.client.assert_has_calls( [mock.call.list_ports( device_owner=[debug_agent.DEVICE_OWNER_NETWORK_PROBE, debug_agent.DEVICE_OWNER_COMPUTE_PROBE])]) def test_exec_command(self): cmd = commands.ExecProbe(self.app, None) cmd_parser = cmd.get_parser('exec_command') args = ['fake_port', 'fake_command'] parsed_args = cmd_parser.parse_args(args) with mock.patch('neutron.agent.linux.ip_lib.IpNetnsCommand') as ns: cmd.run(parsed_args) ns.assert_has_calls([mock.call.execute(mock.ANY)]) self.client.assert_has_calls([mock.call.show_port('fake_port')]) def test_clear_probe(self): cmd = commands.ClearProbe(self.app, None) cmd_parser = cmd.get_parser('clear_probe') args = [] parsed_args = cmd_parser.parse_args(args) cmd.run(parsed_args) namespace = 'qprobe-fake_port' self.client.assert_has_calls( [mock.call.list_ports( device_id=socket.gethostname(), device_owner=[debug_agent.DEVICE_OWNER_NETWORK_PROBE, debug_agent.DEVICE_OWNER_COMPUTE_PROBE]), mock.call.show_port('fake_port'), mock.call.show_network('fake_net'), mock.call.show_subnet('fake_subnet'), mock.call.delete_port('fake_port')]) self.driver.assert_has_calls([mock.call.get_device_name(mock.ANY), mock.call.unplug('tap12345678-12', namespace=namespace, bridge=None)]) def test_ping_all_with_ensure_port(self): fake_ports = self.fake_ports def fake_port_list(network_id=None, device_owner=None, device_id=None): if network_id: # In order to test ensure_port, return [] return {'ports': []} return fake_ports self.client.list_ports.side_effect = fake_port_list cmd = commands.PingAll(self.app, None) cmd_parser = cmd.get_parser('ping_all') args = [] parsed_args = cmd_parser.parse_args(args) namespace = 'qprobe-fake_port' with mock.patch('neutron.agent.linux.ip_lib.IpNetnsCommand') as ns: cmd.run(parsed_args) ns.assert_has_calls([mock.call.execute(mock.ANY)]) fake_port = {'port': {'device_owner': debug_agent.DEVICE_OWNER_NETWORK_PROBE, 'admin_state_up': True, 'network_id': 'fake_net', 'tenant_id': 'fake_tenant', portbindings.HOST_ID: cfg.CONF.host, 'fixed_ips': [{'subnet_id': 'fake_subnet'}], 'device_id': socket.gethostname()}} expected = [mock.call.show_network('fake_net'), mock.call.show_subnet('fake_subnet'), mock.call.create_port(fake_port), mock.call.show_subnet('fake_subnet')] self.client.assert_has_calls(expected) self.driver.assert_has_calls([mock.call.init_l3('tap12345678-12', ['10.0.0.3/24'], namespace=namespace )]) def test_ping_all(self): cmd = commands.PingAll(self.app, None) cmd_parser = cmd.get_parser('ping_all') args = [] parsed_args = cmd_parser.parse_args(args) with mock.patch('neutron.agent.linux.ip_lib.IpNetnsCommand') as ns: cmd.run(parsed_args) ns.assert_has_calls([mock.call.execute(mock.ANY)]) expected = [mock.call.list_ports(), mock.call.list_ports( network_id='fake_net', device_owner=debug_agent.DEVICE_OWNER_NETWORK_PROBE, device_id=socket.gethostname()), mock.call.show_subnet('fake_subnet'), mock.call.show_port('fake_port')] self.client.assert_has_calls(expected) def test_ping_all_v6(self): fake_subnet_v6 = {'subnet': {'name': 'fake_v6', 'ip_version': 6}} self.client.show_subnet.return_value = fake_subnet_v6 cmd = commands.PingAll(self.app, None) cmd_parser = cmd.get_parser('ping_all') args = [] parsed_args = cmd_parser.parse_args(args) with mock.patch('neutron.agent.linux.ip_lib.IpNetnsCommand') as ns: cmd.run(parsed_args) ns.assert_has_calls([mock.call.execute(mock.ANY)]) self.client.assert_has_calls([mock.call.list_ports()]) neutron-12.1.1/neutron/tests/unit/test_worker.py0000664000175000017500000000275313553660046022064 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron.common import utils from neutron.tests import base from neutron import worker as neutron_worker class PeriodicWorkerTestCase(base.BaseTestCase): def test_periodic_worker_lifecycle(self): check_function = mock.Mock() worker = neutron_worker.PeriodicWorker( check_function, interval=1, initial_delay=1) self.addCleanup(worker.stop) worker.wait() self.assertFalse(check_function.called) worker.start() utils.wait_until_true( lambda: check_function.called, timeout=5, exception=RuntimeError("check_function not called")) worker.stop() check_function.reset_mock() worker.wait() self.assertFalse(check_function.called) worker.reset() utils.wait_until_true( lambda: check_function.called, timeout=5, exception=RuntimeError("check_function not called")) neutron-12.1.1/neutron/tests/unit/scheduler/0000775000175000017500000000000013553660157021114 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py0000664000175000017500000026474713553660047026301 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import contextlib import datetime import mock from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib import context as n_context from neutron_lib.exceptions import l3 as l3_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import importutils from oslo_utils import timeutils from oslo_utils import uuidutils from sqlalchemy import orm import testscenarios import testtools from neutron.db import db_base_plugin_v2 as db_v2 from neutron.db import l3_db from neutron.db import l3_dvr_ha_scheduler_db from neutron.db import l3_dvrscheduler_db from neutron.db import l3_hamode_db from neutron.db import l3_hascheduler_db from neutron.extensions import l3agentscheduler as l3agent from neutron import manager from neutron.objects import agent as agent_obj from neutron.objects import l3_hamode from neutron.objects import l3agent as rb_obj from neutron.scheduler import l3_agent_scheduler from neutron.tests import base from neutron.tests.common import helpers from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit.extensions import test_l3 from neutron.tests.unit import testlib_api # the below code is required for the following reason # (as documented in testscenarios) """Multiply tests depending on their 'scenarios' attribute. This can be assigned to 'load_tests' in any test module to make this automatically work across tests in the module. """ load_tests = testscenarios.load_tests_apply_scenarios HOST_DVR = 'my_l3_host_dvr' HOST_DVR_SNAT = 'my_l3_host_dvr_snat' DEVICE_OWNER_COMPUTE = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' DEVICE_OWNER_COMPUTE_NOVA = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'nova' class FakeL3Scheduler(l3_agent_scheduler.L3Scheduler): def schedule(self): pass def _choose_router_agent(self): pass def _choose_router_agents_for_ha(self): pass class FakePortDB(object): def __init__(self, port_list): self._port_list = port_list def _get_query_answer(self, port_list, filters): answers = [] for port in port_list: matched = True for key, search_values in filters.items(): port_value = port.get(key, None) if not port_value: matched = False break if isinstance(port_value, list): sub_answers = self._get_query_answer(port_value, search_values) matched = len(sub_answers) > 0 else: matched = port_value in search_values if not matched: break if matched: answers.append(port) return answers def get_port(self, context, port_id): for port in self._port_list: if port['id'] == port_id: if port['tenant_id'] == context.tenant_id or context.is_admin: return port break return None def get_ports(self, context, filters=None): query_filters = dict() if filters: query_filters.update(filters) if not context.is_admin: query_filters['tenant_id'] = [context.tenant_id] result = self._get_query_answer(self._port_list, query_filters) return result class L3SchedulerBaseTestCase(base.BaseTestCase): def setUp(self): super(L3SchedulerBaseTestCase, self).setUp() self.scheduler = FakeL3Scheduler() self.plugin = mock.Mock() def _test__get_routers_can_schedule(self, routers, agent, target_routers): self.plugin.get_l3_agent_candidates.return_value = agent result = self.scheduler._get_routers_can_schedule( self.plugin, mock.ANY, routers, mock.ANY) self.assertEqual(target_routers, result) def test__get_routers_can_schedule_with_compat_agent(self): routers = [{'id': 'foo_router'}] self._test__get_routers_can_schedule(routers, mock.ANY, routers) def test__get_routers_can_schedule_with_no_compat_agent(self): routers = [{'id': 'foo_router'}] self._test__get_routers_can_schedule(routers, None, []) def test__bind_routers_centralized(self): routers = [{'id': 'foo_router'}] agent = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid()) with mock.patch.object(self.scheduler, 'bind_router') as mock_bind: self.scheduler._bind_routers(mock.ANY, mock.ANY, routers, agent) mock_bind.assert_called_once_with(mock.ANY, mock.ANY, 'foo_router', agent.id) def _test__bind_routers_ha(self, has_binding): routers = [{'id': 'foo_router', 'ha': True, 'tenant_id': '42'}] agent = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid()) with mock.patch.object(self.scheduler, '_router_has_binding', return_value=has_binding) as mock_has_binding,\ mock.patch.object(self.scheduler, 'create_ha_port_and_bind') as mock_bind: self.scheduler._bind_routers(mock.ANY, mock.ANY, routers, agent) mock_has_binding.assert_called_once_with(mock.ANY, 'foo_router', agent.id) self.assertEqual(not has_binding, mock_bind.called) def test__bind_routers_ha_has_binding(self): self._test__bind_routers_ha(has_binding=True) def test__bind_routers_ha_no_binding(self): self._test__bind_routers_ha(has_binding=False) def test__get_candidates_iterable_on_early_returns(self): plugin = mock.MagicMock() # non-distributed router already hosted plugin.get_l3_agents_hosting_routers.return_value = [{'id': 'a1'}] router = {'distributed': False, 'id': 'falafel'} iter(self.scheduler._get_candidates(plugin, mock.MagicMock(), router)) # distributed router but no agents router['distributed'] = True plugin.get_l3_agents.return_value = [] iter(self.scheduler._get_candidates(plugin, mock.MagicMock(), router)) self.assertFalse(plugin.get_l3_agent_candidates.called) def test__get_candidates_skips_get_l3_agent_candidates_if_dvr_scheduled( self): plugin = mock.MagicMock() # distributed router already hosted plugin.get_l3_agents_hosting_routers.return_value = [{'id': 'a1'}] router = {'distributed': True, 'id': uuidutils.generate_uuid()} plugin.get_l3_agents.return_value = ['a1'] self.scheduler._get_candidates(plugin, mock.MagicMock(), router) self.assertFalse(plugin.get_l3_agent_candidates.called) class L3SchedulerBaseMixin(object): def _register_l3_agents(self, plugin=None): self.agent1 = helpers.register_l3_agent( 'host_1', constants.L3_AGENT_MODE_LEGACY) self.agent_id1 = self.agent1.id self.agent2 = helpers.register_l3_agent( 'host_2', constants.L3_AGENT_MODE_LEGACY) self.agent_id2 = self.agent2.id def _register_l3_dvr_agents(self): self.l3_dvr_agent = helpers.register_l3_agent( HOST_DVR, constants.L3_AGENT_MODE_DVR) self.l3_dvr_agent_id = self.l3_dvr_agent.id self.l3_dvr_snat_agent = helpers.register_l3_agent( HOST_DVR_SNAT, constants.L3_AGENT_MODE_DVR_SNAT) self.l3_dvr_snat_id = self.l3_dvr_snat_agent.id def _set_l3_agent_admin_state(self, context, agent_id, state=True): update = {'agent': {'admin_state_up': state}} self.plugin.update_agent(context, agent_id, update) def _set_l3_agent_dead(self, agent_id): update = { 'agent': { 'heartbeat_timestamp': timeutils.utcnow() - datetime.timedelta(hours=1)}} self.plugin.update_agent(self.adminContext, agent_id, update) @contextlib.contextmanager def router_with_ext_gw(self, name='router1', admin_state_up=True, fmt=None, tenant_id=uuidutils.generate_uuid(), external_gateway_info=None, subnet=None, set_context=False, **kwargs): router = self._make_router(fmt or self.fmt, tenant_id, name, admin_state_up, external_gateway_info, set_context, **kwargs) self._add_external_gateway_to_router( router['router']['id'], subnet['subnet']['network_id']) yield router self._remove_external_gateway_from_router( router['router']['id'], subnet['subnet']['network_id']) self._delete('routers', router['router']['id']) class L3SchedulerTestBaseMixin(object): def _test_add_router_to_l3_agent(self, distributed=False, already_scheduled=False, external_gw=None): agent_id = self.agent_id1 agent = self.agent1 if distributed: self._register_l3_dvr_agents() agent_id = self.l3_dvr_snat_id agent = self.l3_dvr_snat_agent router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r1') router['router']['distributed'] = distributed router['router']['external_gateway_info'] = external_gw if already_scheduled: self._test_schedule_bind_router(agent, router) with mock.patch.object(self.plugin, "validate_agent_router_combination"),\ mock.patch.object(self.plugin, "create_router_to_agent_binding") as auto_s,\ mock.patch('neutron.db.l3_db.L3_NAT_db_mixin.get_router', return_value=router['router']): self.plugin.add_router_to_l3_agent(self.adminContext, agent_id, router['router']['id']) self.assertNotEqual(already_scheduled, auto_s.called) def test__unbind_router_removes_binding(self): agent_id = self.agent_id1 agent = self.agent1 router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r1') self._test_schedule_bind_router(agent, router) self.plugin._unbind_router(self.adminContext, router['router']['id'], agent_id) bindings = rb_obj.RouterL3AgentBinding.get_l3_agents_by_router_ids( self.adminContext, [router['router']['id']]) self.assertEqual(0, len(bindings)) def _create_router_for_l3_agent_dvr_test(self, distributed=False, external_gw=None): router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r1') router['router']['distributed'] = distributed router['router']['external_gateway_info'] = external_gw return router def _prepare_l3_agent_dvr_move_exceptions(self, distributed=False, external_gw=None, agent_id=None, expected_exception=None): router = self._create_router_for_l3_agent_dvr_test( distributed=distributed, external_gw=external_gw) with mock.patch.object(self.plugin, "create_router_to_agent_binding"),\ mock.patch('neutron.db.l3_db.L3_NAT_db_mixin.get_router', return_value=router['router']): self.assertRaises(expected_exception, self.plugin.add_router_to_l3_agent, self.adminContext, agent_id, router['router']['id']) def test__schedule_router_skips_unschedulable_routers(self): mock.patch.object(self.plugin, 'router_supports_scheduling', return_value=False).start() scheduler = l3_agent_scheduler.ChanceScheduler() self.assertIsNone(scheduler._schedule_router(self.plugin, self.adminContext, 'router_id')) def test_add_router_to_l3_agent_mismatch_error_dvr_to_legacy(self): self._register_l3_agents() self._prepare_l3_agent_dvr_move_exceptions( distributed=True, agent_id=self.agent_id1, expected_exception=l3agent.RouterL3AgentMismatch) def test_add_router_to_l3_agent_mismatch_error_legacy_to_dvr(self): self._register_l3_dvr_agents() self._prepare_l3_agent_dvr_move_exceptions( agent_id=self.l3_dvr_agent_id, expected_exception=l3agent.DVRL3CannotAssignToDvrAgent) def test_add_router_to_l3_agent_mismatch_error_dvr_to_dvr(self): self._register_l3_dvr_agents() self._prepare_l3_agent_dvr_move_exceptions( distributed=True, agent_id=self.l3_dvr_agent_id, expected_exception=l3agent.DVRL3CannotAssignToDvrAgent) def test_add_router_to_l3_agent_dvr_to_snat(self): external_gw_info = { "network_id": uuidutils.generate_uuid(), "enable_snat": True } self._register_l3_dvr_agents() agent_id = self.l3_dvr_snat_id router = self._create_router_for_l3_agent_dvr_test( distributed=True, external_gw=external_gw_info) with mock.patch.object(self.plugin, "validate_agent_router_combination"),\ mock.patch.object( self.plugin, "create_router_to_agent_binding") as rtr_agent_binding,\ mock.patch('neutron.db.l3_db.L3_NAT_db_mixin.get_router', return_value=router['router']): self.plugin.add_router_to_l3_agent(self.adminContext, agent_id, router['router']['id']) rtr_agent_binding.assert_called_once_with( self.adminContext, mock.ANY, router['router']) def test_add_router_to_l3_agent(self): self._test_add_router_to_l3_agent() def test_add_distributed_router_to_l3_agent(self): external_gw_info = { "network_id": uuidutils.generate_uuid(), "enable_snat": True } self._test_add_router_to_l3_agent(distributed=True, external_gw=external_gw_info) def test_add_router_to_l3_agent_already_scheduled(self): self._test_add_router_to_l3_agent(already_scheduled=True) def test_add_distributed_router_to_l3_agent_already_scheduled(self): external_gw_info = { "network_id": uuidutils.generate_uuid(), "enable_snat": True } self._test_add_router_to_l3_agent(distributed=True, already_scheduled=True, external_gw=external_gw_info) def test_remove_router_from_l3_agent_in_dvr_mode(self): self._register_l3_dvr_agents() self.assertRaises(l3agent.DVRL3CannotRemoveFromDvrAgent, self.plugin.remove_router_from_l3_agent, self.adminContext, self.l3_dvr_agent_id, mock.ANY) def test_remove_router_from_l3_agent_in_dvr_snat_mode(self): self._register_l3_dvr_agents() router = self._create_router_for_l3_agent_dvr_test( distributed=True) agent_id = self.l3_dvr_snat_id l3_notifier = mock.Mock() self.plugin.agent_notifiers = {constants.AGENT_TYPE_L3: l3_notifier} self.plugin.remove_router_from_l3_agent(self.adminContext, agent_id, router['router']['id']) l3_notifier.router_removed_from_agent.assert_called_once_with( self.adminContext, router['router']['id'], self.l3_dvr_snat_agent.host) def _prepare_schedule_dvr_tests(self): scheduler = l3_agent_scheduler.ChanceScheduler() agent = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid()) agent.admin_state_up = True agent.heartbeat_timestamp = timeutils.utcnow() plugin = mock.Mock() plugin.get_l3_agents_hosting_routers.return_value = [] plugin.get_l3_agents.return_value = [agent] plugin.get_l3_agent_candidates.return_value = [agent] return scheduler, agent, plugin def test_schedule_dvr_router_without_snatbinding_and_no_gw(self): scheduler, agent, plugin = self._prepare_schedule_dvr_tests() sync_router = { 'id': 'foo_router_id', 'distributed': True } plugin.get_router.return_value = sync_router with mock.patch.object(scheduler, 'bind_router'),\ mock.patch.object(plugin, 'get_snat_bindings', return_value=False): scheduler._schedule_router( plugin, self.adminContext, 'foo_router_id', None) expected_calls = [ mock.call.get_router(mock.ANY, 'foo_router_id'), mock.call.get_l3_agents_hosting_routers( mock.ANY, ['foo_router_id'], admin_state_up=True), mock.call.get_l3_agents(mock.ANY, active=True), mock.call.get_l3_agent_candidates(mock.ANY, sync_router, [agent]), ] plugin.assert_has_calls(expected_calls) def test_schedule_router_distributed(self): scheduler, agent, plugin = self._prepare_schedule_dvr_tests() sync_router = { 'id': 'foo_router_id', 'distributed': True, 'external_gateway_info': { 'network_id': uuidutils.generate_uuid(), 'enable_snat': True } } plugin.get_router.return_value = sync_router with mock.patch.object(scheduler, 'bind_router'): scheduler._schedule_router( plugin, self.adminContext, 'foo_router_id', None) expected_calls = [ mock.call.get_router(mock.ANY, 'foo_router_id'), mock.call.get_l3_agents_hosting_routers( mock.ANY, ['foo_router_id'], admin_state_up=True), mock.call.get_l3_agents(mock.ANY, active=True), mock.call.get_l3_agent_candidates(mock.ANY, sync_router, [agent]), ] plugin.assert_has_calls(expected_calls) def _test_schedule_bind_router(self, agent, router): ctx = self.adminContext scheduler = l3_agent_scheduler.ChanceScheduler() rid = router['router']['id'] scheduler.bind_router(self.plugin, ctx, rid, agent.id) results = rb_obj.RouterL3AgentBinding.get_objects(ctx, router_id=rid) self.assertGreater(len(results), 0) self.assertIn(agent.id, [bind.l3_agent_id for bind in results]) def test_bind_new_router(self): router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r1') with mock.patch.object(l3_agent_scheduler.LOG, 'debug') as flog: self._test_schedule_bind_router(self.agent1, router) self.assertEqual(1, flog.call_count) args, kwargs = flog.call_args self.assertIn('is scheduled', args[0]) def test_bind_absent_router(self): scheduler = l3_agent_scheduler.ChanceScheduler() # checking that bind_router() is not throwing # when supplied with router_id of non-existing router scheduler.bind_router(self.plugin, self.adminContext, uuidutils.generate_uuid(), self.agent_id1) def test_bind_existing_router(self): router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r2') self._test_schedule_bind_router(self.agent1, router) with mock.patch.object(l3_agent_scheduler.LOG, 'debug') as flog: self._test_schedule_bind_router(self.agent1, router) self.assertEqual(1, flog.call_count) args, kwargs = flog.call_args self.assertIn('has already been scheduled', args[0]) def _check_get_l3_agent_candidates( self, router, agent_list, exp_host, count=1): candidates = self.plugin.get_l3_agent_candidates(self.adminContext, router, agent_list) self.assertEqual(count, len(candidates)) if count: self.assertEqual(exp_host, candidates[0]['host']) def test_get_l3_agent_candidates_legacy(self): self._register_l3_dvr_agents() router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r2') router['external_gateway_info'] = None router['id'] = uuidutils.generate_uuid() agent_list = [self.agent1, self.l3_dvr_agent] # test legacy agent_mode case: only legacy agent should be candidate router['distributed'] = False exp_host = 'host_1' self._check_get_l3_agent_candidates(router, agent_list, exp_host) def test_get_l3_agent_candidates_dvr(self): self._register_l3_dvr_agents() router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r2') router['external_gateway_info'] = None router['id'] = uuidutils.generate_uuid() agent_list = [self.agent1, self.l3_dvr_agent] # test dvr agent_mode case no candidates router['distributed'] = True self.get_subnet_ids_on_router = mock.Mock() self._check_dvr_serviceable_ports_on_host = mock.Mock( return_value=True) self._check_get_l3_agent_candidates(router, agent_list, None, count=0) def test_get_l3_agent_candidates_dvr_no_vms(self): self._register_l3_dvr_agents() router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r2') router['external_gateway_info'] = None router['id'] = uuidutils.generate_uuid() agent_list = [self.agent1, self.l3_dvr_agent] router['distributed'] = True # Test no VMs present case self.get_subnet_ids_on_router = mock.Mock() self._check_dvr_serviceable_ports_on_host = mock.Mock( return_value=False) self._check_get_l3_agent_candidates( router, agent_list, HOST_DVR, count=0) def test_get_l3_agent_candidates_dvr_snat(self): self._register_l3_dvr_agents() router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r2') router['external_gateway_info'] = None router['id'] = uuidutils.generate_uuid() router['distributed'] = True agent_list = [self.l3_dvr_snat_agent] self.get_subnet_ids_on_router = mock.Mock() self._check_dvr_serviceable_ports_on_host = mock.Mock( return_value=True) self._check_get_l3_agent_candidates(router, agent_list, HOST_DVR_SNAT) def test_get_l3_agent_candidates_dvr_snat_no_vms(self): self._register_l3_dvr_agents() router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r2') router['external_gateway_info'] = None router['id'] = uuidutils.generate_uuid() router['distributed'] = True agent_list = [self.l3_dvr_snat_agent] self._check_dvr_serviceable_ports_on_host = mock.Mock( return_value=False) # Test no VMs present case self.get_subnet_ids_on_router = mock.Mock() self._check_dvr_serviceable_ports_on_host.return_value = False self._check_get_l3_agent_candidates( router, agent_list, HOST_DVR_SNAT, count=1) def test_get_l3_agent_candidates_dvr_ha_snat_no_vms(self): self._register_l3_dvr_agents() router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r2') router['external_gateway_info'] = None router['id'] = uuidutils.generate_uuid() router['distributed'] = True router['ha'] = True agent_list = [self.l3_dvr_snat_agent] self.check_ports_exist_on_l3agent = mock.Mock(return_value=False) # Test no VMs present case self.check_ports_exist_on_l3agent.return_value = False self.get_subnet_ids_on_router = mock.Mock(return_value=set()) self._check_get_l3_agent_candidates( router, agent_list, HOST_DVR_SNAT, count=1) def test_get_l3_agent_candidates_centralized(self): self._register_l3_dvr_agents() router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r2') router['external_gateway_info'] = None router['id'] = uuidutils.generate_uuid() # check centralized test case router['distributed'] = False agent_list = [self.l3_dvr_snat_agent] self._check_get_l3_agent_candidates(router, agent_list, HOST_DVR_SNAT) def test_get_l3_agents_hosting_routers(self): agent = helpers.register_l3_agent('host_6') router = self._make_router(self.fmt, tenant_id=uuidutils.generate_uuid(), name='r1') ctx = self.adminContext router_id = router['router']['id'] self.plugin.router_scheduler.bind_router(self.plugin, ctx, router_id, agent.id) agents = self.plugin.get_l3_agents_hosting_routers(ctx, [router_id]) self.assertEqual([agent.id], [agt.id for agt in agents]) agents = self.plugin.get_l3_agents_hosting_routers(ctx, [router_id], admin_state_up=True) self.assertEqual([agent.id], [agt.id for agt in agents]) self._set_l3_agent_admin_state(ctx, agent.id, False) agents = self.plugin.get_l3_agents_hosting_routers(ctx, [router_id]) self.assertEqual([agent.id], [agt.id for agt in agents]) agents = self.plugin.get_l3_agents_hosting_routers(ctx, [router_id], admin_state_up=True) self.assertEqual([], agents) class L3SchedulerTestCaseMixin(test_l3.L3NatTestCaseMixin, L3SchedulerBaseMixin, L3SchedulerTestBaseMixin): def setUp(self): self.mock_rescheduling = False ext_mgr = test_l3.L3TestExtensionManager() plugin_str = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatIntAgentSchedulingPlugin') super(L3SchedulerTestCaseMixin, self).setUp(plugin=plugin_str, ext_mgr=ext_mgr) self.adminContext = n_context.get_admin_context() self.plugin = directory.get_plugin() self.plugin.router_scheduler = importutils.import_object( 'neutron.scheduler.l3_agent_scheduler.ChanceScheduler' ) self._register_l3_agents() class L3AgentChanceSchedulerTestCase(L3SchedulerTestCaseMixin, test_db_base_plugin_v2. NeutronDbPluginV2TestCase): def setUp(self): super(L3AgentChanceSchedulerTestCase, self).setUp() # Removes MissingAuthPlugin exception from logs self.patch_notifier = mock.patch( 'neutron.notifiers.batch_notifier.BatchNotifier._notify') self.patch_notifier.start() def test_random_scheduling(self): random_patch = mock.patch('random.choice') random_mock = random_patch.start() def side_effect(seq): return seq[0] random_mock.side_effect = side_effect with self.subnet() as subnet: self._set_net_external(subnet['subnet']['network_id']) with self.router_with_ext_gw(name='r1', subnet=subnet) as r1: agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['router']['id']], admin_state_up=True) self.assertEqual(1, len(agents)) self.assertEqual(1, random_mock.call_count) with self.router_with_ext_gw(name='r2', subnet=subnet) as r2: agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r2['router']['id']], admin_state_up=True) self.assertEqual(len(agents), 1) self.assertEqual(2, random_mock.call_count) random_patch.stop() def test_scheduler_auto_schedule_when_agent_added(self): self._set_l3_agent_admin_state(self.adminContext, self.agent_id1, False) self._set_l3_agent_admin_state(self.adminContext, self.agent_id2, False) with self.subnet() as subnet: self._set_net_external(subnet['subnet']['network_id']) with self.router_with_ext_gw(name='r1', subnet=subnet) as r1: agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['router']['id']], admin_state_up=True) self.assertEqual(0, len(agents)) self._set_l3_agent_admin_state(self.adminContext, self.agent_id1, True) self.plugin.auto_schedule_routers(self.adminContext, 'host_1') agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['router']['id']], admin_state_up=True) self.assertEqual('host_1', agents[0]['host']) class L3AgentLeastRoutersSchedulerTestCase(L3SchedulerTestCaseMixin, test_db_base_plugin_v2. NeutronDbPluginV2TestCase): def setUp(self): super(L3AgentLeastRoutersSchedulerTestCase, self).setUp() self.plugin.router_scheduler = importutils.import_object( 'neutron.scheduler.l3_agent_scheduler.LeastRoutersScheduler' ) def test_scheduler(self): # disable one agent to force the scheduling to the only one. self._set_l3_agent_admin_state(self.adminContext, self.agent_id2, False) with self.subnet() as subnet: self._set_net_external(subnet['subnet']['network_id']) with self.router_with_ext_gw(name='r1', subnet=subnet) as r1: agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['router']['id']], admin_state_up=True) self.assertEqual(1, len(agents)) agent_id1 = agents[0]['id'] with self.router_with_ext_gw(name='r2', subnet=subnet) as r2: agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r2['router']['id']], admin_state_up=True) self.assertEqual(1, len(agents)) agent_id2 = agents[0]['id'] self.assertEqual(agent_id1, agent_id2) # re-enable the second agent to see whether the next router # spawned will be on this one. self._set_l3_agent_admin_state(self.adminContext, self.agent_id2, True) with self.router_with_ext_gw(name='r3', subnet=subnet) as r3: agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r3['router']['id']], admin_state_up=True) self.assertEqual(1, len(agents)) agent_id3 = agents[0]['id'] self.assertNotEqual(agent_id1, agent_id3) class L3DvrScheduler(l3_db.L3_NAT_db_mixin, l3_dvrscheduler_db.L3_DVRsch_db_mixin): pass class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, test_db_base_plugin_v2. NeutronDbPluginV2TestCase): l3_plugin = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatAgentSchedulingServicePlugin') def setUp(self): if self.l3_plugin: service_plugins = { 'l3_plugin_name': self.l3_plugin, 'flavors_plugin_name': 'neutron.services.flavors.' 'flavors_plugin.FlavorsPlugin' } else: service_plugins = None super(L3DvrSchedulerTestCase, self).setUp('ml2', service_plugins=service_plugins) self.setup_coreplugin('ml2') self.adminContext = n_context.get_admin_context() self.dut = L3DvrScheduler() self.l3plugin = directory.get_plugin(plugin_constants.L3) def test__notify_l3_agent_update_port_with_allowed_address_pairs_revert( self): port_id = uuidutils.generate_uuid() kwargs = { 'context': self.adminContext, 'port': { 'id': port_id, 'admin_state_up': False, portbindings.HOST_ID: 'vm-host', 'device_id': 'vm-id', 'allowed_address_pairs': [ {'ip_address': '10.1.0.201', 'mac_address': 'aa:bb:cc:dd:ee:ff'}], 'device_owner': DEVICE_OWNER_COMPUTE, }, 'original_port': { 'id': port_id, 'admin_state_up': True, portbindings.HOST_ID: 'vm-host', 'device_id': 'vm-id', 'allowed_address_pairs': [ {'ip_address': '10.1.0.201', 'mac_address': 'aa:bb:cc:dd:ee:ff'}], 'device_owner': DEVICE_OWNER_COMPUTE, }, } port = kwargs.get('original_port') l3plugin = mock.Mock() directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) l3plugin._get_allowed_address_pair_fixed_ips.return_value = ( ['10.1.0.21']) self.assertFalse( l3plugin.update_arp_entry_for_dvr_service_port.called) l3plugin.delete_arp_entry_for_dvr_service_port.\ assert_called_once_with( self.adminContext, port, fixed_ips_to_delete=mock.ANY) def test__notify_l3_agent_update_port_with_allowed_address_pairs(self): port_id = uuidutils.generate_uuid() kwargs = { 'context': self.adminContext, 'port': { 'id': port_id, portbindings.HOST_ID: 'vm-host', 'allowed_address_pairs': [ {'ip_address': '10.1.0.201', 'mac_address': 'aa:bb:cc:dd:ee:ff'}], 'device_id': 'vm-id', 'device_owner': DEVICE_OWNER_COMPUTE, 'admin_state_up': True, }, 'original_port': { 'id': port_id, portbindings.HOST_ID: 'vm-host', 'device_id': 'vm-id', 'device_owner': DEVICE_OWNER_COMPUTE, 'admin_state_up': True, }, } l3plugin = mock.Mock() directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) self.assertTrue( l3plugin.update_arp_entry_for_dvr_service_port.called) def test__notify_l3_agent_when_unbound_port_migrates_to_bound_host(self): port_id = 'fake-port' kwargs = { 'context': self.adminContext, 'original_port': { 'id': port_id, portbindings.HOST_ID: '', 'device_owner': '', 'admin_state_up': True, }, 'port': { 'id': port_id, portbindings.HOST_ID: 'vm-host', 'device_owner': DEVICE_OWNER_COMPUTE, 'mac_address': '02:04:05:17:18:19' }, } port = kwargs.get('port') plugin = directory.get_plugin() l3plugin = mock.Mock() l3plugin.supported_extension_aliases = [ 'router', constants.L3_AGENT_SCHEDULER_EXT_ALIAS, constants.L3_DISTRIBUTED_EXT_ALIAS ] directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', plugin, **kwargs) l3plugin.dvr_handle_new_service_port.assert_called_once_with( self.adminContext, port, unbound_migrate=True) def test__notify_l3_agent_update_port_no_removing_routers(self): port_id = 'fake-port' kwargs = { 'context': self.adminContext, 'port': None, 'original_port': { 'id': port_id, portbindings.HOST_ID: 'vm-host', 'device_id': 'vm-id', 'device_owner': DEVICE_OWNER_COMPUTE, 'mac_address': '02:04:05:17:18:19' }, 'mac_address_updated': True } plugin = directory.get_plugin() l3plugin = mock.Mock() l3plugin.supported_extension_aliases = [ 'router', constants.L3_AGENT_SCHEDULER_EXT_ALIAS, constants.L3_DISTRIBUTED_EXT_ALIAS ] directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', plugin, **kwargs) self.assertFalse( l3plugin.update_arp_entry_for_dvr_service_port.called) self.assertFalse( l3plugin.dvr_handle_new_service_port.called) self.assertFalse(l3plugin.remove_router_from_l3_agent.called) self.assertFalse(l3plugin.get_dvr_routers_to_remove.called) def test__notify_l3_agent_new_port_action(self): kwargs = { 'context': self.adminContext, 'original_port': None, 'port': { 'device_owner': DEVICE_OWNER_COMPUTE, }, } l3plugin = mock.Mock() directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_new_port( 'port', 'after_create', mock.ANY, **kwargs) l3plugin.update_arp_entry_for_dvr_service_port.\ assert_called_once_with( self.adminContext, kwargs.get('port')) l3plugin.dvr_handle_new_service_port.assert_called_once_with( self.adminContext, kwargs.get('port')) def test__notify_l3_agent_new_port_no_action(self): kwargs = { 'context': self.adminContext, 'original_port': None, 'port': { 'device_owner': constants.DEVICE_OWNER_NETWORK_PREFIX + 'None', } } l3plugin = mock.Mock() directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_new_port( 'port', 'after_create', mock.ANY, **kwargs) self.assertFalse( l3plugin.update_arp_entry_for_dvr_service_port.called) self.assertFalse( l3plugin.dvr_handle_new_service_port.called) def test__notify_l3_agent_update_port_with_migration_port_profile(self): kwargs = { 'context': self.adminContext, 'original_port': { portbindings.HOST_ID: 'vm-host', 'device_owner': DEVICE_OWNER_COMPUTE, }, 'port': { portbindings.HOST_ID: 'vm-host', 'device_owner': DEVICE_OWNER_COMPUTE, portbindings.PROFILE: {'migrating_to': 'vm-host2'}, }, } l3plugin = mock.Mock() directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) l3plugin.dvr_handle_new_service_port.assert_called_once_with( self.adminContext, kwargs.get('port'), dest_host='vm-host2') l3plugin.update_arp_entry_for_dvr_service_port.\ assert_called_once_with( self.adminContext, kwargs.get('port')) def test__notify_l3_agent_update_port_no_action(self): kwargs = { 'context': self.adminContext, 'original_port': { portbindings.HOST_ID: 'vm-host', 'device_owner': DEVICE_OWNER_COMPUTE, }, 'port': { portbindings.HOST_ID: 'vm-host', 'device_owner': DEVICE_OWNER_COMPUTE, }, } l3plugin = mock.Mock() directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) self.assertFalse( l3plugin.update_arp_entry_for_dvr_service_port.called) self.assertFalse( l3plugin.dvr_handle_new_service_port.called) self.assertFalse(l3plugin.remove_router_from_l3_agent.called) self.assertFalse(l3plugin.get_dvr_routers_to_remove.called) def test__notify_l3_agent_update_port_with_mac_address_update(self): kwargs = { 'context': self.adminContext, 'original_port': { portbindings.HOST_ID: 'vm-host', 'mac_address': '02:04:05:17:18:19' }, 'port': { portbindings.HOST_ID: 'vm-host', 'mac_address': '02:04:05:17:18:29' }, 'mac_address_updated': True } l3plugin = mock.Mock() directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) l3plugin.update_arp_entry_for_dvr_service_port.\ assert_called_once_with( self.adminContext, kwargs.get('port')) self.assertFalse(l3plugin.dvr_handle_new_service_port.called) def test__notify_l3_agent_port_binding_change(self): self._test__notify_l3_agent_port_binding_change() def test__notify_l3_agent_port_binding_change_removed_routers(self): router_to_remove = [{'agent_id': 'foo_agent', 'router_id': 'foo_id', 'host': 'vm-host1'}] self._test__notify_l3_agent_port_binding_change(router_to_remove) def test__notify_l3_agent_port_binding_change_removed_routers_fip(self): fip = {'router_id': 'router_id'} router_to_remove = [{'agent_id': 'foo_agent', 'router_id': 'foo_id', 'host': 'vm-host1'}] self._test__notify_l3_agent_port_binding_change(router_to_remove, fip) def test__notify_l3_agent_port_binding_change_with_fip(self): fip = {'router_id': 'router_id'} self._test__notify_l3_agent_port_binding_change(None, fip) def _test__notify_l3_agent_port_binding_change(self, routers_to_remove=None, fip=None): source_host = 'vm-host1' kwargs = { 'context': self.adminContext, 'original_port': { 'id': uuidutils.generate_uuid(), portbindings.HOST_ID: source_host, 'device_owner': DEVICE_OWNER_COMPUTE, }, 'port': { portbindings.HOST_ID: 'vm-host2', 'device_owner': DEVICE_OWNER_COMPUTE, }, } l3plugin = mock.Mock() directory.add_plugin(plugin_constants.L3, l3plugin) with mock.patch.object(l3plugin, 'get_dvr_routers_to_remove', return_value=routers_to_remove),\ mock.patch.object(l3plugin, '_get_floatingips_by_port_id', return_value=[fip] if fip else []): l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) if routers_to_remove: (l3plugin.l3_rpc_notifier.router_removed_from_agent. assert_called_once_with(mock.ANY, 'foo_id', source_host)) self.assertEqual( 1, l3plugin.delete_arp_entry_for_dvr_service_port.call_count) if fip and not routers_to_remove: (l3plugin.l3_rpc_notifier.routers_updated_on_host. assert_called_once_with(mock.ANY, ['router_id'], source_host)) self.assertEqual( 1, l3plugin.update_arp_entry_for_dvr_service_port.call_count) l3plugin.dvr_handle_new_service_port.assert_called_once_with( self.adminContext, kwargs.get('port'), dest_host=None) def test__notify_l3_agent_update_port_removing_routers(self): port_id = 'fake-port' source_host = 'vm-host' kwargs = { 'context': self.adminContext, 'port': { 'id': port_id, portbindings.HOST_ID: None, 'device_id': '', 'device_owner': '' }, 'mac_address_updated': False, 'original_port': { 'id': port_id, portbindings.HOST_ID: source_host, 'device_id': 'vm-id', 'device_owner': DEVICE_OWNER_COMPUTE } } plugin = directory.get_plugin() l3plugin = mock.Mock() l3plugin.supported_extension_aliases = [ 'router', constants.L3_AGENT_SCHEDULER_EXT_ALIAS, constants.L3_DISTRIBUTED_EXT_ALIAS ] directory.add_plugin(plugin_constants.L3, l3plugin) with mock.patch.object(l3plugin, 'get_dvr_routers_to_remove', return_value=[{'agent_id': 'foo_agent', 'router_id': 'foo_id', 'host': source_host}]),\ mock.patch.object(l3plugin, '_get_floatingips_by_port_id', return_value=[]): l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', plugin, **kwargs) self.assertEqual( 1, l3plugin.delete_arp_entry_for_dvr_service_port.call_count) l3plugin.delete_arp_entry_for_dvr_service_port.\ assert_called_once_with( self.adminContext, mock.ANY) self.assertFalse( l3plugin.dvr_handle_new_service_port.called) (l3plugin.l3_rpc_notifier.router_removed_from_agent. assert_called_once_with(mock.ANY, 'foo_id', source_host)) def test__notify_port_delete(self): plugin = directory.get_plugin() l3plugin = mock.Mock() l3plugin.supported_extension_aliases = [ 'router', constants.L3_AGENT_SCHEDULER_EXT_ALIAS, constants.L3_DISTRIBUTED_EXT_ALIAS ] directory.add_plugin(plugin_constants.L3, l3plugin) port = { 'id': uuidutils.generate_uuid(), 'device_id': 'abcd', 'device_owner': DEVICE_OWNER_COMPUTE_NOVA, portbindings.HOST_ID: 'host1', } kwargs = { 'context': self.adminContext, 'port': port, 'removed_routers': [ {'agent_id': 'foo_agent', 'router_id': 'foo_id'}, ], } removed_routers = [{'agent_id': 'foo_agent', 'router_id': 'foo_id', 'host': 'foo_host'}] l3plugin.get_dvr_routers_to_remove.return_value = removed_routers l3_dvrscheduler_db._notify_port_delete( 'port', 'after_delete', plugin, **kwargs) l3plugin.delete_arp_entry_for_dvr_service_port.\ assert_called_once_with( self.adminContext, mock.ANY) (l3plugin.l3_rpc_notifier.router_removed_from_agent. assert_called_once_with(mock.ANY, 'foo_id', 'foo_host')) def test_dvr_handle_new_service_port(self): port = { 'id': 'port1', 'device_id': 'abcd', 'device_owner': DEVICE_OWNER_COMPUTE_NOVA, portbindings.HOST_ID: 'host1', 'fixed_ips': [ { 'subnet_id': '80947d4a-fbc8-484b-9f92-623a6bfcf3e0', 'ip_address': '10.10.10.3' } ] } dvr_ports = [ { 'id': 'dvr_port1', 'device_id': 'r1', 'device_owner': constants.DEVICE_OWNER_DVR_INTERFACE, 'fixed_ips': [ { 'subnet_id': '80947d4a-fbc8-484b-9f92-623a6bfcf3e0', 'ip_address': '10.10.10.1' } ] }, { 'id': 'dvr_port2', 'device_id': 'r2', 'device_owner': constants.DEVICE_OWNER_DVR_INTERFACE, 'fixed_ips': [ { 'subnet_id': '80947d4a-fbc8-484b-9f92-623a6bfcf3e0', 'ip_address': '10.10.10.123' } ] } ] agent_on_host = {'id': 'agent1'} with mock.patch( 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' '.get_ports', return_value=dvr_ports),\ mock.patch('neutron.api.rpc.agentnotifiers.l3_rpc_agent_api' '.L3AgentNotifyAPI'),\ mock.patch.object( self.dut, 'get_l3_agents', return_value=[agent_on_host]) as get_l3_agents: self.dut.dvr_handle_new_service_port( self.adminContext, port) get_l3_agents.assert_called_once_with( self.adminContext, filters={'host': [port[portbindings.HOST_ID]]}) self.dut.l3_rpc_notifier.routers_updated_on_host.\ assert_called_once_with(self.adminContext, {'r1', 'r2'}, 'host1') self.assertFalse(self.dut.l3_rpc_notifier.routers_updated.called) def test_get_dvr_routers_by_subnet_ids(self): subnet_id = '80947d4a-fbc8-484b-9f92-623a6bfcf3e0' dvr_port = { 'id': 'dvr_port1', 'device_id': 'r1', 'device_owner': constants.DEVICE_OWNER_DVR_INTERFACE, 'fixed_ips': [ { 'subnet_id': subnet_id, 'ip_address': '10.10.10.1' } ] } r1 = { 'id': 'r1', 'distributed': True, } with mock.patch( 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' '.get_port', return_value=dvr_port),\ mock.patch('neutron.db.db_base_plugin_v2.NeutronDbPluginV2' '.get_ports', return_value=[dvr_port]): router_id = self.dut.get_dvr_routers_by_subnet_ids( self.adminContext, [subnet_id]) self.assertEqual(r1['id'], router_id.pop()) def test_get_subnet_ids_on_router(self): dvr_port = { 'id': 'dvr_port1', 'device_id': 'r1', 'device_owner': constants.DEVICE_OWNER_DVR_INTERFACE, 'fixed_ips': [ { 'subnet_id': '80947d4a-fbc8-484b-9f92-623a6bfcf3e0', 'ip_address': '10.10.10.1' } ] } r1 = { 'id': 'r1', 'distributed': True, } with mock.patch( 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' '.get_ports', return_value=[dvr_port]): sub_ids = self.dut.get_subnet_ids_on_router(self.adminContext, r1['id']) self.assertEqual(sub_ids.pop(), dvr_port.get('fixed_ips').pop(0).get('subnet_id')) def test_get_subnet_ids_on_router_no_subnet(self): dvr_port = { 'id': 'dvr_port1', 'device_id': 'r1', 'device_owner': constants.DEVICE_OWNER_DVR_INTERFACE, 'fixed_ips': [] } r1 = { 'id': 'r1', 'distributed': True, } with mock.patch.object(db_v2.NeutronDbPluginV2, 'get_ports', return_value=[dvr_port]): sub_ids = self.dut.get_subnet_ids_on_router(self.adminContext, r1['id']) self.assertEqual(0, len(sub_ids)) def test__check_dvr_serviceable_ports_on_host(self): # HOST_DVR = 'my_l3_host_dvr' # HOST_DVR_SNAT = 'my_l3_host_dvr_snat' # HOST_DVR is a sub-string of HOST_DVR_SNAT self._register_l3_dvr_agents() host_args = {'admin_state_up': True, portbindings.PROFILE: {'migrating to': HOST_DVR_SNAT}} with self.network() as network: with self.subnet(network=network) as subnet: subnet_ids = [] subnet_ids.append(subnet['subnet']['id']) with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=('admin_state_up', portbindings.PROFILE,), **host_args): # Check DVR serviceable ports on HOST_DVR_SNAT. # Should find existence since it is an exact match to the # target host name of the port binding profile. result0 = self.l3plugin. \ _check_dvr_serviceable_ports_on_host(self.adminContext, self.l3_dvr_snat_agent['host'], subnet_ids) # Check DVR serviceable ports on HOST_DVR. # Should not find existence since the sub-string won't get # matched with the target host. result1 = self.l3plugin. \ _check_dvr_serviceable_ports_on_host(self.adminContext, self.l3_dvr_agent['host'], subnet_ids) self.assertTrue(result0) self.assertFalse(result1) def _prepare_schedule_snat_tests(self): agent = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid()) agent.admin_state_up = True agent.heartbeat_timestamp = timeutils.utcnow() router = { 'id': 'foo_router_id', 'distributed': True, 'external_gateway_info': { 'network_id': uuidutils.generate_uuid(), 'enable_snat': True } } return agent, router class L3HAPlugin(db_v2.NeutronDbPluginV2, l3_hamode_db.L3_HA_NAT_db_mixin, l3_hascheduler_db.L3_HA_scheduler_db_mixin): supported_extension_aliases = ["l3-ha", "router_availability_zone"] @classmethod def get_plugin_type(cls): return plugin_constants.L3 def get_plugin_description(self): return "L3 Routing Service Plugin for testing" class L3HATestCaseMixin(testlib_api.SqlTestCase, L3SchedulerBaseMixin): def setUp(self): super(L3HATestCaseMixin, self).setUp() self.adminContext = n_context.get_admin_context() mock.patch('neutron.common.rpc.get_client').start() self.setup_coreplugin('ml2', load_plugins=False) cfg.CONF.set_override('service_plugins', ['neutron.tests.unit.scheduler.' 'test_l3_agent_scheduler.L3HAPlugin']) cfg.CONF.set_override('max_l3_agents_per_router', 0) manager.init() self.plugin = directory.get_plugin(plugin_constants.L3) self.plugin.router_scheduler = importutils.import_object( 'neutron.scheduler.l3_agent_scheduler.ChanceScheduler' ) self._register_l3_agents() @staticmethod def get_router_l3_agent_binding(context, router_id, l3_agent_id=None, binding_index=None): args = {'router_id': router_id} if l3_agent_id: args['l3_agent_id'] = l3_agent_id if binding_index: args['binding_index'] = binding_index return rb_obj.RouterL3AgentBinding.get_objects(context, **args) def _create_ha_router(self, ha=True, tenant_id='tenant1', az_hints=None): self.adminContext.tenant_id = tenant_id router = {'name': 'router1', 'admin_state_up': True, 'tenant_id': tenant_id} if ha is not None: router['ha'] = ha if az_hints is None: az_hints = [] router['availability_zone_hints'] = az_hints return self.plugin.create_router(self.adminContext, {'router': router}) def test_create_ha_port_and_bind_catch_integrity_error(self): router = self._create_ha_router(tenant_id='foo_tenant') self.plugin.schedule_router(self.adminContext, router['id']) agent = {'id': 'foo_agent'} orig_fn = orm.Session.add def db_ref_err_for_add_haportbinding(s, instance): if instance.__class__.__name__ == 'L3HARouterAgentPortBinding': instance.router_id = 'nonexistent_router' return orig_fn(s, instance) with mock.patch.object(self.plugin.router_scheduler, 'bind_router'): with mock.patch.object( orm.Session, 'add', side_effect=db_ref_err_for_add_haportbinding, autospec=True): self.plugin.router_scheduler.create_ha_port_and_bind( self.plugin, self.adminContext, router['id'], router['tenant_id'], agent) def test_create_ha_port_and_bind_wont_create_redundant_ports(self): # When migrating from HA to DVR+HA router, create_ha_port_and_bind # should create only one network:router_ha_interface port on a router # when binding to same agent. So we need only one agent for testing # (preferably with dvr_snat mode). agent_obj.Agent.update_objects( self.adminContext, {'admin_state_up': False}) l3_dvr_snat_agent = helpers.register_l3_agent( 'fake_l3_host_dvr_snat', constants.L3_AGENT_MODE_DVR_SNAT) router = self._create_ha_router(tenant_id='foo_tenant') self.plugin.schedule_router(self.adminContext, router['id']) router['admin_state_up'] = False updated_router1 = self.plugin.update_router( self.adminContext, router['id'], {'router': router}) updated_router1['distributed'] = True self.plugin.update_router( self.adminContext, router['id'], {'router': updated_router1}) self.plugin.router_scheduler.create_ha_port_and_bind( self.plugin, self.adminContext, router['id'], router['tenant_id'], l3_dvr_snat_agent) filters = {'device_owner': ['network:router_ha_interface'], 'device_id': [router['id']]} self.core_plugin = directory.get_plugin() ports = self.core_plugin.get_ports( self.adminContext, filters=filters) self.assertEqual(1, len(ports)) def test_create_ha_port_and_bind_catch_router_not_found(self): router = self._create_ha_router(tenant_id='foo_tenant') self.plugin.schedule_router(self.adminContext, router['id']) agent = {'id': 'foo_agent'} with mock.patch.object(self.plugin.router_scheduler, 'bind_router'): with mock.patch.object( self.plugin, 'add_ha_port', side_effect=l3_exc.RouterNotFound( router_id='foo_router')),\ mock.patch.object( self.plugin, 'safe_delete_ha_network') as sd_ha_net: self.plugin.router_scheduler.create_ha_port_and_bind( self.plugin, self.adminContext, router['id'], router['tenant_id'], agent) self.assertTrue(sd_ha_net.called) def test_create_ha_port_and_bind_bind_router_returns_None(self): router = self._create_ha_router(tenant_id='foo_tenant') agent = {'id': 'foo_agent'} with mock.patch.object(self.plugin.router_scheduler, 'bind_router', return_value=None): with mock.patch.object(self.plugin, 'add_ha_port') as add_ha_port: self.plugin.router_scheduler.create_ha_port_and_bind( self.plugin, self.adminContext, router['id'], router['tenant_id'], agent) self.assertFalse(add_ha_port.called) class VacantBindingIndexTestCase(L3HATestCaseMixin): """Test various scenarios for get_vacant_binding_index(). binding_index The binding_index we want to delete/unschedule. is_manual_scheduling Whether or not this is a scheduling requested by the user (`neutron l3-agent-router-add`) or by some worker (scheduler or RPC from agent). If this is a manual scheduling we should always comply. """ binding_scenarios = [ ('Delete first binding_index', dict(binding_index=1)), ('Delete middle binding_index', dict(binding_index=2)), ('Delete last binding_index', dict(binding_index=3)), ('Do not remove any bindings', dict(binding_index=None)), ] manual_scheduling_scenarios = [ ('with manual scheduling', dict(is_manual_scheduling=True)), ('without manual scheduling', dict(is_manual_scheduling=False)), ] scenarios = testscenarios.multiply_scenarios( binding_scenarios, manual_scheduling_scenarios) def test_get_vacant_binding_index(self): helpers.register_l3_agent('host_3') cfg.CONF.set_override('max_l3_agents_per_router', 3) router = self._create_ha_router() if self.binding_index: bindings = self.get_router_l3_agent_binding( self.adminContext, router['id'], binding_index=self.binding_index) self.assertEqual(1, len(bindings)) bindings[0].delete() vacant_binding_index = self.plugin.get_vacant_binding_index( self.adminContext, router['id'], self.is_manual_scheduling) if self.binding_index: self.assertEqual(self.binding_index, vacant_binding_index) else: if self.is_manual_scheduling: # If this is a manual scheduling, the user requested the # binding so we should always provide a new one. self.assertEqual(cfg.CONF.max_l3_agents_per_router + 1, vacant_binding_index) else: # Else, we already have 3 so -1 is the 'error' value. self.assertEqual(-1, vacant_binding_index) class L3_HA_scheduler_db_mixinTestCase(L3HATestCaseMixin): def _register_l3_agents(self, plugin=None): super(L3_HA_scheduler_db_mixinTestCase, self)._register_l3_agents(plugin=plugin) self.agent3 = helpers.register_l3_agent(host='host_3') self.agent_id3 = self.agent3.id self.agent4 = helpers.register_l3_agent(host='host_4') self.agent_id4 = self.agent4.id def test_get_routers_l3_agents_count(self): router1 = self._create_ha_router() cfg.CONF.set_override('max_l3_agents_per_router', 2) router2 = self._create_ha_router() router3 = self._create_ha_router(ha=False) result = self.plugin.get_routers_l3_agents_count(self.adminContext) self.assertEqual(3, len(result)) check_result = [(router['id'], agents) for router, agents in result] self.assertIn((router1['id'], 4), check_result) self.assertIn((router2['id'], 2), check_result) self.assertIn((router3['id'], 0), check_result) def test_get_ordered_l3_agents_by_num_routers(self): # Mock scheduling so that the test can control it explicitly mock.patch.object(l3_hamode_db.L3_HA_NAT_db_mixin, '_notify_router_updated').start() with mock.patch.object(self.plugin, 'schedule_router'): router1 = self._create_ha_router() router2 = self._create_ha_router() router3 = self._create_ha_router(ha=False) router4 = self._create_ha_router(ha=False) # Agent 1 will host 0 routers, agent 2 will host 1, agent 3 will # host 2, and agent 4 will host 3. self.plugin.schedule_router(self.adminContext, router1['id'], candidates=[self.agent2, self.agent4]) self.plugin.schedule_router(self.adminContext, router2['id'], candidates=[self.agent3, self.agent4]) self.plugin.schedule_router(self.adminContext, router3['id'], candidates=[self.agent3]) self.plugin.schedule_router(self.adminContext, router4['id'], candidates=[self.agent4]) agent_ids = [self.agent_id1, self.agent_id2, self.agent_id3, self.agent_id4] result = self.plugin.get_l3_agents_ordered_by_num_routers( self.adminContext, agent_ids) self.assertEqual(agent_ids, [record['id'] for record in result]) class L3AgentSchedulerDbMixinTestCase(L3HATestCaseMixin): def _setup_ha_router(self): router = self._create_ha_router() agents = self._get_agents_scheduled_for_router(router) return router, agents def test_reschedule_ha_routers_from_down_agents(self): agents = self._setup_ha_router()[1] self.assertEqual(2, len(agents)) self._set_l3_agent_dead(self.agent_id1) with mock.patch.object(self.plugin, 'reschedule_router') as reschedule: self.plugin.reschedule_routers_from_down_agents() self.assertFalse(reschedule.called) def test_list_l3_agents_hosting_ha_router(self): router = self._create_ha_router() agents = self.plugin.list_l3_agents_hosting_router( self.adminContext, router['id'])['agents'] for agent in agents: self.assertEqual('standby', agent['ha_state']) self.plugin.update_routers_states( self.adminContext, {router['id']: 'active'}, self.agent1.host) agents = self.plugin.list_l3_agents_hosting_router( self.adminContext, router['id'])['agents'] for agent in agents: expected_state = ('active' if agent['host'] == self.agent1.host else 'standby') self.assertEqual(expected_state, agent['ha_state']) def test_list_l3_agents_hosting_legacy_router(self): router = self._create_ha_router(ha=False) self.plugin.schedule_router(self.adminContext, router['id']) agent = self.plugin.list_l3_agents_hosting_router( self.adminContext, router['id'])['agents'][0] self.assertIsNone(agent['ha_state']) def test_get_agents_dict_for_router_unscheduled_returns_empty_list(self): self.assertEqual({'agents': []}, self.plugin._get_agents_dict_for_router([])) def test_router_doesnt_support_scheduling(self): with mock.patch.object(self.plugin, 'router_supports_scheduling', return_value=False): agent = helpers.register_l3_agent(host='myhost_3') with testtools.ExpectedException( l3agent.RouterDoesntSupportScheduling): self.plugin.add_router_to_l3_agent( self.adminContext, agent.id, 'router_id') def test_manual_add_ha_router_to_agent(self): cfg.CONF.set_override('max_l3_agents_per_router', 2) router, agents = self._setup_ha_router() self.assertEqual(2, len(agents)) agent = helpers.register_l3_agent(host='myhost_3') # We allow to exceed max l3 agents per router via manual scheduling self.plugin.add_router_to_l3_agent( self.adminContext, agent.id, router['id']) agents = self._get_agents_scheduled_for_router(router) self.assertIn(agent.id, [_agent.id for _agent in agents]) self.assertEqual(3, len(agents)) def test_manual_remove_ha_router_from_agent(self): router, agents = self._setup_ha_router() self.assertEqual(2, len(agents)) agent = agents.pop() # Remove router from agent and make sure it is removed self.plugin.remove_router_from_l3_agent( self.adminContext, agent.id, router['id']) agents = self._get_agents_scheduled_for_router(router) self.assertEqual(1, len(agents)) self.assertNotIn(agent.id, [_agent.id for _agent in agents]) def test_manual_remove_ha_router_from_all_agents(self): router, agents = self._setup_ha_router() self.assertEqual(2, len(agents)) agent = agents.pop() self.plugin.remove_router_from_l3_agent( self.adminContext, agent.id, router['id']) agent = agents.pop() self.plugin.remove_router_from_l3_agent( self.adminContext, agent.id, router['id']) agents = self._get_agents_scheduled_for_router(router) self.assertEqual(0, len(agents)) def _get_agents_scheduled_for_router(self, router): return self.plugin.get_l3_agents_hosting_routers( self.adminContext, [router['id']], admin_state_up=True) def test_delete_ha_interfaces_from_agent(self): router, agents = self._setup_ha_router() agent = agents.pop() self.plugin.remove_router_from_l3_agent( self.adminContext, agent.id, router['id']) objs = l3_hamode.L3HARouterAgentPortBinding.get_objects( self.adminContext, router_id=router['id']) results = [binding.l3_agent_id for binding in objs] self.assertNotIn(agent.id, results) def test_add_ha_interface_to_l3_agent(self): agent = self.plugin.get_agents_db(self.adminContext)[0] router = self._create_ha_router() self.plugin.add_router_to_l3_agent(self.adminContext, agent.id, router['id']) # Verify agent has HA interface ha_ports = self.plugin.get_ha_router_port_bindings(self.adminContext, [router['id']]) self.assertIn(agent.id, [ha_port.l3_agent_id for ha_port in ha_ports]) def test_schedule_routers_unique_binding_indices(self): cfg.CONF.set_override('max_l3_agents_per_router', 2) router = self._create_ha_router() bindings = self.get_router_l3_agent_binding(self.adminContext, router['id']) binding_indices = [binding.binding_index for binding in bindings] self.assertEqual(list(range(1, cfg.CONF.max_l3_agents_per_router + 1)), binding_indices) def test_bind_router_twice_for_non_ha(self): router = self._create_ha_router(ha=False) self.plugin.router_scheduler.bind_router(self.plugin, self.adminContext, router['id'], self.agent_id1) self.plugin.router_scheduler.bind_router(self.plugin, self.adminContext, router['id'], self.agent_id2) # Make sure the second bind_router call didn't schedule the router to # more agents than allowed. agents = self.plugin.get_l3_agents_hosting_routers(self.adminContext, [router['id']]) self.assertEqual(1, len(agents)) # Moreover, make sure that the agent that did get bound, only got bound # once. bindings = self.get_router_l3_agent_binding( self.adminContext, router['id'], l3_agent_id=agents[0]['id']) self.assertEqual(1, len(bindings)) class L3HAChanceSchedulerTestCase(L3HATestCaseMixin): def test_scheduler_with_ha_enabled(self): router = self._create_ha_router() agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [router['id']], admin_state_up=True) self.assertEqual(2, len(agents)) for agent in agents: sync_data = self.plugin.get_ha_sync_data_for_host( self.adminContext, router_ids=[router['id']], host=agent.host, agent=agent) self.assertEqual(1, len(sync_data)) interface = sync_data[0][constants.HA_INTERFACE_KEY] self.assertIsNotNone(interface) def test_auto_schedule(self): # Mock scheduling so that the test can control it explicitly mock.patch.object(l3_hamode_db.L3_HA_NAT_db_mixin, '_notify_router_updated').start() router = self._create_ha_router() self.plugin.auto_schedule_routers(self.adminContext, self.agent1.host) self.plugin.auto_schedule_routers(self.adminContext, self.agent2.host) agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [router['id']]) self.assertEqual(2, len(agents)) def test_auto_schedule_specific_router_when_agent_added(self): self._auto_schedule_when_agent_added(True) def test_auto_schedule_all_routers_when_agent_added(self): self._auto_schedule_when_agent_added(False) def test_auto_schedule_ha_router_when_incompatible_agent_exist(self): handle_internal_only_routers_agent = helpers.register_l3_agent( 'host_3', constants.L3_AGENT_MODE_LEGACY, internal_only=False) router = self._create_ha_router() self.plugin.auto_schedule_routers( self.adminContext, handle_internal_only_routers_agent.host) agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [router['id']], admin_state_up=True) agent_ids = [agent['id'] for agent in agents] self.assertEqual(2, len(agents)) self.assertNotIn(handle_internal_only_routers_agent.id, agent_ids) def test_auto_schedule_ha_router_when_dvr_agent_exist(self): dvr_agent = helpers.register_l3_agent( HOST_DVR, constants.L3_AGENT_MODE_DVR) router = self._create_ha_router() self.plugin.auto_schedule_routers(self.adminContext, dvr_agent.host) agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [router['id']], admin_state_up=True) agent_ids = [agent['id'] for agent in agents] self.assertEqual(2, len(agents)) self.assertNotIn(dvr_agent.id, agent_ids) def _auto_schedule_when_agent_added(self, specific_router): router = self._create_ha_router() agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [router['id']], admin_state_up=True) self.assertEqual(2, len(agents)) agent_ids = [agent['id'] for agent in agents] self.assertIn(self.agent_id1, agent_ids) self.assertIn(self.agent_id2, agent_ids) agent = helpers.register_l3_agent(host='host_3') self.agent_id3 = agent.id self.plugin.auto_schedule_routers(self.adminContext, 'host_3') agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [router['id']], admin_state_up=True) self.assertEqual(3, len(agents)) # Simulate agent restart to make sure we don't try to re-bind self.plugin.auto_schedule_routers(self.adminContext, 'host_3') class L3HALeastRoutersSchedulerTestCase(L3HATestCaseMixin): def _register_l3_agents(self, plugin=None): super(L3HALeastRoutersSchedulerTestCase, self)._register_l3_agents(plugin=plugin) agent = helpers.register_l3_agent(host='host_3') self.agent_id3 = agent.id agent = helpers.register_l3_agent(host='host_4') self.agent_id4 = agent.id def setUp(self): super(L3HALeastRoutersSchedulerTestCase, self).setUp() self.plugin.router_scheduler = importutils.import_object( 'neutron.scheduler.l3_agent_scheduler.LeastRoutersScheduler' ) def test_scheduler(self): cfg.CONF.set_override('max_l3_agents_per_router', 2) # disable the third agent to be sure that the router will # be scheduled of the two firsts self._set_l3_agent_admin_state(self.adminContext, self.agent_id3, False) self._set_l3_agent_admin_state(self.adminContext, self.agent_id4, False) r1 = self._create_ha_router() agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['id']], admin_state_up=True) self.assertEqual(2, len(agents)) agent_ids = [agent['id'] for agent in agents] self.assertIn(self.agent_id1, agent_ids) self.assertIn(self.agent_id2, agent_ids) self._set_l3_agent_admin_state(self.adminContext, self.agent_id3, True) self._set_l3_agent_admin_state(self.adminContext, self.agent_id4, True) r2 = self._create_ha_router() agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r2['id']], admin_state_up=True) self.assertEqual(2, len(agents)) agent_ids = [agent['id'] for agent in agents] self.assertIn(self.agent_id3, agent_ids) self.assertIn(self.agent_id4, agent_ids) class TestGetL3AgentsWithFilter(testlib_api.SqlTestCase, L3SchedulerBaseMixin): """Test cases to test get_l3_agents. 6 l3 agents are registered in the order - legacy, dvr_snat, dvr, dvr_no_external, fake_mode and legacy """ scenarios = [ ('no filter', dict(agent_modes=[], host=['host_1'], expected_agent_modes=['legacy', 'dvr_snat', 'dvr', 'dvr_no_external', 'fake_mode', 'legacy'], expected_host=['host_1'])), ('legacy', dict(agent_modes=['legacy'], host=['host_1'], expected_agent_modes=['legacy', 'legacy'], expected_host=['host_1'])), ('dvr_snat', dict(agent_modes=['dvr_snat'], host=['host_2'], expected_agent_modes=['dvr_snat'], expected_host=['host_2'])), ('dvr', dict(agent_modes=['dvr'], host=['host_3'], expected_agent_modes=['dvr'], expected_host=['host_3'])), ('dvr_no_external', dict(agent_modes=['dvr_no_external'], host=['host_4'], expected_agent_modes=['dvr_no_external'], expected_host=['host_4'])), ('dvr_snat and dvr', dict(agent_modes=['dvr_snat', 'dvr'], host=['host_5'], expected_agent_modes=['dvr_snat', 'dvr'], expected_host=['host_5'])), ('dvr_snat and dvr_no_external', dict(agent_modes=['dvr_snat', 'dvr_no_external'], host=['host_5'], expected_agent_modes=['dvr_snat', 'dvr_no_external'], expected_host=['host_5'])), ('dvr_snat, dvr and dvr_no_external', dict(agent_modes=['dvr_snat', 'dvr', 'dvr_no_external'], host=['host_6'], expected_agent_modes=['dvr_snat', 'dvr', 'dvr_no_external'], expected_host=['host_6'])), ('invalid', dict(agent_modes=['invalid'], host=['host_invalid'], expected_agent_modes=[], expected_host=[])), ] def setUp(self): super(TestGetL3AgentsWithFilter, self).setUp() self.plugin = L3HAPlugin() self.setup_coreplugin('ml2') self.adminContext = n_context.get_admin_context() hosts = ['host_1', 'host_2', 'host_3', 'host_4', 'host_5', 'host_6'] agent_modes = ['legacy', 'dvr_snat', 'dvr', 'dvr_no_external', 'fake_mode', 'legacy'] for host, agent_mode in zip(hosts, agent_modes): helpers.register_l3_agent(host, agent_mode) class TestGetL3AgentsWithAgentModeFilter(TestGetL3AgentsWithFilter): """Test cases to test get_l3_agents 'agent_mode'. This class tests the L3AgentSchedulerDbMixin.get_l3_agents() for the 'agent_mode' filter with various values. """ def _get_agent_mode(self, agent): agent_conf = self.plugin.get_configuration_dict(agent) return agent_conf.get('agent_mode', 'None') def test_get_l3_agents(self): l3_agents = self.plugin.get_l3_agents( self.adminContext, filters={'agent_modes': self.agent_modes}) self.assertEqual(len(self.expected_agent_modes), len(l3_agents)) returned_agent_modes = [self._get_agent_mode(agent) for agent in l3_agents] self.assertItemsEqual(self.expected_agent_modes, returned_agent_modes) class TestGetL3AgentsWithHostFilter(TestGetL3AgentsWithFilter): """Test cases to test get_l3_agents 'hosts'. This class tests the L3AgentSchedulerDbMixin.get_l3_agents() for the 'host' filter with various values. """ def _get_host(self, agent): return agent.get('host', 'None') def test_get_l3_agents(self): l3_agents = self.plugin.get_l3_agents( self.adminContext, filters={'host': self.host}) self.assertEqual(len(self.expected_host), len(l3_agents)) returned_host = [self._get_host(agent) for agent in l3_agents] self.assertEqual(self.expected_host, returned_host) class L3AgentAZLeastRoutersSchedulerTestCase(L3HATestCaseMixin): def setUp(self): super(L3AgentAZLeastRoutersSchedulerTestCase, self).setUp() self.plugin.router_scheduler = importutils.import_object( 'neutron.scheduler.l3_agent_scheduler.AZLeastRoutersScheduler') # Mock scheduling so that the test can control it explicitly mock.patch.object(l3_hamode_db.L3_HA_NAT_db_mixin, '_notify_router_updated').start() # Removes MissingAuthPlugin exception from logs self.patch_notifier = mock.patch( 'neutron.notifiers.batch_notifier.BatchNotifier._notify') self.patch_notifier.start() def _register_l3_agents(self): self.agent1 = helpers.register_l3_agent(host='az1-host1', az='az1') self.agent2 = helpers.register_l3_agent(host='az1-host2', az='az1') self.agent3 = helpers.register_l3_agent(host='az2-host1', az='az2') self.agent4 = helpers.register_l3_agent(host='az2-host2', az='az2') self.agent5 = helpers.register_l3_agent(host='az3-host1', az='az3') self.agent6 = helpers.register_l3_agent(host='az3-host2', az='az3') def test_az_scheduler_auto_schedule(self): r1 = self._create_ha_router(ha=False, az_hints=['az1']) self.plugin.auto_schedule_routers(self.adminContext, 'az1-host2') agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['id']]) self.assertEqual(1, len(agents)) self.assertEqual('az1-host2', agents[0]['host']) def test_az_scheduler_auto_schedule_no_match(self): r1 = self._create_ha_router(ha=False, az_hints=['az1']) self.plugin.auto_schedule_routers(self.adminContext, 'az2-host1') agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['id']]) self.assertEqual(0, len(agents)) def test_az_scheduler_default_az(self): cfg.CONF.set_override('default_availability_zones', ['az2']) r1 = self._create_ha_router(ha=False) r2 = self._create_ha_router(ha=False) r3 = self._create_ha_router(ha=False) self.plugin.schedule_router(self.adminContext, r1['id']) self.plugin.schedule_router(self.adminContext, r2['id']) self.plugin.schedule_router(self.adminContext, r3['id']) agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['id'], r2['id'], r3['id']]) self.assertEqual(3, len(agents)) expected_hosts = set(['az2-host1', 'az2-host2']) hosts = set([a['host'] for a in agents]) self.assertEqual(expected_hosts, hosts) def test_az_scheduler_az_hints(self): r1 = self._create_ha_router(ha=False, az_hints=['az3']) r2 = self._create_ha_router(ha=False, az_hints=['az3']) r3 = self._create_ha_router(ha=False, az_hints=['az3']) self.plugin.schedule_router(self.adminContext, r1['id']) self.plugin.schedule_router(self.adminContext, r2['id']) self.plugin.schedule_router(self.adminContext, r3['id']) agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['id'], r2['id'], r3['id']]) self.assertEqual(3, len(agents)) expected_hosts = set(['az3-host1', 'az3-host2']) hosts = set([a['host'] for a in agents]) self.assertEqual(expected_hosts, hosts) def test_az_scheduler_least_routers(self): r1 = self._create_ha_router(ha=False, az_hints=['az1']) r2 = self._create_ha_router(ha=False, az_hints=['az1']) r3 = self._create_ha_router(ha=False, az_hints=['az1']) r4 = self._create_ha_router(ha=False, az_hints=['az1']) self.plugin.schedule_router(self.adminContext, r1['id']) self.plugin.schedule_router(self.adminContext, r2['id']) self.plugin.schedule_router(self.adminContext, r3['id']) self.plugin.schedule_router(self.adminContext, r4['id']) agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['id'], r2['id'], r3['id'], r4['id']]) host_num = collections.defaultdict(int) for agent in agents: host_num[agent['host']] += 1 self.assertEqual(2, host_num['az1-host1']) self.assertEqual(2, host_num['az1-host2']) def test_az_scheduler_ha_az_hints(self): cfg.CONF.set_override('max_l3_agents_per_router', 2) r1 = self._create_ha_router(az_hints=['az1', 'az3']) self.plugin.schedule_router(self.adminContext, r1['id']) agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['id']]) self.assertEqual(2, len(agents)) expected_azs = set(['az1', 'az3']) azs = set([a['availability_zone'] for a in agents]) self.assertEqual(expected_azs, azs) def test_az_scheduler_ha_auto_schedule(self): cfg.CONF.set_override('max_l3_agents_per_router', 3) self._set_l3_agent_admin_state(self.adminContext, self.agent2['id'], state=False) self._set_l3_agent_admin_state(self.adminContext, self.agent6['id'], state=False) r1 = self._create_ha_router(az_hints=['az1', 'az3']) self.plugin.schedule_router(self.adminContext, r1['id']) agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['id']]) self.assertEqual(2, len(agents)) hosts = set([a['host'] for a in agents]) self.assertEqual(set(['az1-host1', 'az3-host1']), hosts) self._set_l3_agent_admin_state(self.adminContext, self.agent6['id'], state=True) self.plugin.auto_schedule_routers(self.adminContext, 'az3-host2') agents = self.plugin.get_l3_agents_hosting_routers( self.adminContext, [r1['id']]) self.assertEqual(3, len(agents)) expected_hosts = set(['az1-host1', 'az3-host1', 'az3-host2']) hosts = set([a['host'] for a in agents]) self.assertEqual(expected_hosts, hosts) def test__get_routers_can_schedule_with_no_target_routers(self): result = self.plugin.router_scheduler._get_routers_can_schedule( self.plugin, mock.ANY, [], mock.ANY) self.assertEqual([], result) class L3DVRHAPlugin(db_v2.NeutronDbPluginV2, l3_hamode_db.L3_HA_NAT_db_mixin, l3_dvr_ha_scheduler_db.L3_DVR_HA_scheduler_db_mixin): pass class L3DVRHATestCaseMixin(testlib_api.SqlTestCase, L3SchedulerBaseMixin): def setUp(self): super(L3DVRHATestCaseMixin, self).setUp() self.adminContext = n_context.get_admin_context() self.plugin = L3DVRHAPlugin() neutron-12.1.1/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py0000664000175000017500000012313013553660047026655 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import datetime from operator import attrgetter import random import mock from neutron_lib import constants from neutron_lib import context from oslo_config import cfg from oslo_utils import importutils from oslo_utils import uuidutils import testscenarios from neutron.db import agentschedulers_db as sched_db from neutron.db import common_db_mixin from neutron.extensions import dhcpagentscheduler from neutron.objects import agent from neutron.objects import network as network_obj from neutron.scheduler import dhcp_agent_scheduler from neutron.services.segments import db as segments_service_db from neutron.tests.common import helpers from neutron.tests.unit.plugins.ml2 import test_plugin from neutron.tests.unit import testlib_api # Required to generate tests from scenarios. Not compatible with nose. load_tests = testscenarios.load_tests_apply_scenarios HOST_C = 'host-c' HOST_D = 'host-d' class TestDhcpSchedulerBaseTestCase(testlib_api.SqlTestCase): CORE_PLUGIN = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' def setUp(self): super(TestDhcpSchedulerBaseTestCase, self).setUp() self.setup_coreplugin(self.CORE_PLUGIN) self.ctx = context.get_admin_context() self.network = {'id': uuidutils.generate_uuid()} self.network_id = self.network['id'] self._save_networks([self.network_id]) def _create_and_set_agents_down(self, hosts, down_agent_count=0, admin_state_up=True, az=helpers.DEFAULT_AZ): agents = [] for i, host in enumerate(hosts): is_alive = i >= down_agent_count agents.append(helpers.register_dhcp_agent( host, admin_state_up=admin_state_up, alive=is_alive, az=az)) return agents def _save_networks(self, networks): for network_id in networks: network_obj.Network(self.ctx, id=network_id).create() def _test_schedule_bind_network(self, agents, network_id): scheduler = dhcp_agent_scheduler.ChanceScheduler() scheduler.resource_filter.bind(self.ctx, agents, network_id) binding_objs = network_obj.NetworkDhcpAgentBinding.get_objects( self.ctx, network_id=network_id) self.assertEqual(len(agents), len(binding_objs)) for result in binding_objs: self.assertEqual(network_id, result.network_id) class TestDhcpScheduler(TestDhcpSchedulerBaseTestCase): def test_schedule_bind_network_single_agent(self): agents = self._create_and_set_agents_down(['host-a']) self._test_schedule_bind_network(agents, self.network_id) def test_schedule_bind_network_multi_agents(self): agents = self._create_and_set_agents_down(['host-a', 'host-b']) self._test_schedule_bind_network(agents, self.network_id) def test_schedule_bind_network_multi_agent_fail_one(self): agents = self._create_and_set_agents_down(['host-a']) self._test_schedule_bind_network(agents, self.network_id) with mock.patch.object(dhcp_agent_scheduler.LOG, 'info') as fake_log: self._test_schedule_bind_network(agents, self.network_id) self.assertEqual(1, fake_log.call_count) def _test_get_agents_and_scheduler_for_dead_agent(self): agents = self._create_and_set_agents_down(['dead_host', 'alive_host'], 1) dead_agent = [agents[0]] alive_agent = [agents[1]] self._test_schedule_bind_network(dead_agent, self.network_id) scheduler = dhcp_agent_scheduler.ChanceScheduler() return dead_agent, alive_agent, scheduler def _test_reschedule_vs_network_on_dead_agent(self, active_hosts_only): dead_agent, alive_agent, scheduler = ( self._test_get_agents_and_scheduler_for_dead_agent()) network = {'id': self.network_id} plugin = mock.Mock() plugin.get_subnets.return_value = [{"network_id": self.network_id, "enable_dhcp": True}] plugin.get_agent_objects.return_value = dead_agent + alive_agent plugin.filter_hosts_with_network_access.side_effect = ( lambda context, network_id, hosts: hosts) if active_hosts_only: plugin.get_dhcp_agents_hosting_networks.return_value = [] self.assertTrue( scheduler.schedule( plugin, self.ctx, network)) else: plugin.get_dhcp_agents_hosting_networks.return_value = dead_agent self.assertFalse( scheduler.schedule( plugin, self.ctx, network)) def test_network_rescheduled_when_db_returns_active_hosts(self): self._test_reschedule_vs_network_on_dead_agent(True) def test_network_not_rescheduled_when_db_returns_all_hosts(self): self._test_reschedule_vs_network_on_dead_agent(False) def _get_agent_binding_from_db(self, agent): return network_obj.NetworkDhcpAgentBinding.get_objects( self.ctx, dhcp_agent_id=agent[0].id) def _test_auto_reschedule_vs_network_on_dead_agent(self, active_hosts_only): dead_agent, alive_agent, scheduler = ( self._test_get_agents_and_scheduler_for_dead_agent()) plugin = mock.Mock() plugin.get_subnets.return_value = [{"network_id": self.network_id, "enable_dhcp": True, "segment_id": None}] plugin.get_network.return_value = self.network if active_hosts_only: plugin.get_dhcp_agents_hosting_networks.return_value = [] else: plugin.get_dhcp_agents_hosting_networks.return_value = dead_agent network_assigned_to_dead_agent = ( self._get_agent_binding_from_db(dead_agent)) self.assertEqual(1, len(network_assigned_to_dead_agent)) self.assertTrue( scheduler.auto_schedule_networks( plugin, self.ctx, "alive_host")) network_assigned_to_dead_agent = ( self._get_agent_binding_from_db(dead_agent)) network_assigned_to_alive_agent = ( self._get_agent_binding_from_db(alive_agent)) self.assertEqual(1, len(network_assigned_to_dead_agent)) if active_hosts_only: self.assertEqual(1, len(network_assigned_to_alive_agent)) else: self.assertEqual(0, len(network_assigned_to_alive_agent)) def test_network_auto_rescheduled_when_db_returns_active_hosts(self): self._test_auto_reschedule_vs_network_on_dead_agent(True) def test_network_not_auto_rescheduled_when_db_returns_all_hosts(self): self._test_auto_reschedule_vs_network_on_dead_agent(False) class TestAutoScheduleNetworks(TestDhcpSchedulerBaseTestCase): """Unit test scenarios for ChanceScheduler.auto_schedule_networks. network_present Network is present or not enable_dhcp Dhcp is enabled or disabled in the subnet of the network scheduled_already Network is already scheduled to the agent or not agent_down Dhcp agent is down or alive valid_host If true, then an valid host is passed to schedule the network, else an invalid host is passed. az_hints 'availability_zone_hints' of the network. note that default 'availability_zone' of an agent is 'nova'. """ scenarios = [ ('Network present', dict(network_present=True, enable_dhcp=True, scheduled_already=False, agent_down=False, valid_host=True, az_hints=[])), ('No network', dict(network_present=False, enable_dhcp=False, scheduled_already=False, agent_down=False, valid_host=True, az_hints=[])), ('Network already scheduled', dict(network_present=True, enable_dhcp=True, scheduled_already=True, agent_down=False, valid_host=True, az_hints=[])), ('Agent down', dict(network_present=True, enable_dhcp=True, scheduled_already=False, agent_down=False, valid_host=True, az_hints=[])), ('dhcp disabled', dict(network_present=True, enable_dhcp=False, scheduled_already=False, agent_down=False, valid_host=False, az_hints=[])), ('Invalid host', dict(network_present=True, enable_dhcp=True, scheduled_already=False, agent_down=False, valid_host=False, az_hints=[])), ('Match AZ', dict(network_present=True, enable_dhcp=True, scheduled_already=False, agent_down=False, valid_host=True, az_hints=['nova'])), ('Not match AZ', dict(network_present=True, enable_dhcp=True, scheduled_already=False, agent_down=False, valid_host=True, az_hints=['not-match'])), ] def test_auto_schedule_network(self): plugin = mock.MagicMock() plugin.get_subnets.return_value = ( [{"network_id": self.network_id, "enable_dhcp": self.enable_dhcp, "segment_id": None}] if self.network_present else []) plugin.get_network.return_value = {'availability_zone_hints': self.az_hints} scheduler = dhcp_agent_scheduler.ChanceScheduler() if self.network_present: down_agent_count = 1 if self.agent_down else 0 agents = self._create_and_set_agents_down( ['host-a'], down_agent_count=down_agent_count) if self.scheduled_already: self._test_schedule_bind_network(agents, self.network_id) expected_result = (self.network_present and self.enable_dhcp) expected_hosted_agents = (1 if expected_result and self.valid_host else 0) if (self.az_hints and agents[0]['availability_zone'] not in self.az_hints): expected_hosted_agents = 0 host = "host-a" if self.valid_host else "host-b" observed_ret_value = scheduler.auto_schedule_networks( plugin, self.ctx, host) self.assertEqual(expected_result, observed_ret_value) count_hosted_agents = network_obj.NetworkDhcpAgentBinding.count( self.ctx) self.assertEqual(expected_hosted_agents, count_hosted_agents) class TestAutoScheduleSegments(test_plugin.Ml2PluginV2TestCase, TestDhcpSchedulerBaseTestCase): """Unit test scenarios for ChanceScheduler""" CORE_PLUGIN = 'neutron.plugins.ml2.plugin.Ml2Plugin' def setUp(self): super(TestAutoScheduleSegments, self).setUp() self.plugin = self.driver self.segments_plugin = importutils.import_object( 'neutron.services.segments.plugin.Plugin') self.ctx = context.get_admin_context() # Remove MissingAuthPlugin exception from logs mock.patch( 'neutron.notifiers.batch_notifier.BatchNotifier._notify').start() def _create_network(self): net = self.plugin.create_network( self.ctx, {'network': {'name': 'name', 'tenant_id': 'tenant_one', 'admin_state_up': True, 'shared': True}}) return net['id'] def _create_segment(self, network_id): seg = self.segments_plugin.create_segment( self.ctx, {'segment': {'network_id': network_id, 'name': None, 'description': None, 'physical_network': 'physnet1', 'network_type': 'vlan', 'segmentation_id': constants.ATTR_NOT_SPECIFIED}}) return seg['id'] def _create_subnet(self, segment_id, network_id, cidr='192.168.10.0/24'): subnet = self.plugin.create_subnet( self.ctx, {'subnet': {'name': 'name', 'ip_version': 4, 'network_id': network_id, 'cidr': cidr, 'gateway_ip': constants.ATTR_NOT_SPECIFIED, 'allocation_pools': constants.ATTR_NOT_SPECIFIED, 'dns_nameservers': constants.ATTR_NOT_SPECIFIED, 'host_routes': constants.ATTR_NOT_SPECIFIED, 'tenant_id': 'tenant_one', 'enable_dhcp': True, 'segment_id': segment_id}}) return subnet['id'] def test_auto_schedule_one_network_one_segment_one_subnet(self): net_id = self._create_network() seg_id = self._create_segment(net_id) self._create_subnet(seg_id, net_id) helpers.register_dhcp_agent(HOST_C) segments_service_db.update_segment_host_mapping( self.ctx, HOST_C, {seg_id}) scheduler = dhcp_agent_scheduler.ChanceScheduler() observed_return_val = scheduler.auto_schedule_networks( self.plugin, self.ctx, HOST_C) self.assertTrue(observed_return_val) agent1 = self.plugin.get_dhcp_agents_hosting_networks( self.ctx, [net_id]) self.assertEqual(1, len(agent1)) self.assertEqual('host-c', agent1[0]['host']) def test_auto_schedule_one_network_one_segment_two_subnet(self): net_id = self._create_network() seg_id = self._create_segment(net_id) self._create_subnet(seg_id, net_id) self._create_subnet(seg_id, net_id, '192.168.11.0/24') helpers.register_dhcp_agent(HOST_C) segments_service_db.update_segment_host_mapping( self.ctx, HOST_C, {seg_id}) scheduler = dhcp_agent_scheduler.ChanceScheduler() observed_return_val = scheduler.auto_schedule_networks( self.plugin, self.ctx, HOST_C) self.assertTrue(observed_return_val) agent1 = self.plugin.get_dhcp_agents_hosting_networks( self.ctx, [net_id]) self.assertEqual(1, len(agent1)) self.assertEqual('host-c', agent1[0]['host']) def test_auto_schedule_one_network_two_segments_with_one_subnet_each(self): net_id = self._create_network() seg1_id = self._create_segment(net_id) self._create_subnet(seg1_id, net_id) helpers.register_dhcp_agent(HOST_D) segments_service_db.update_segment_host_mapping( self.ctx, HOST_D, {seg1_id}) scheduler = dhcp_agent_scheduler.ChanceScheduler() observed_val_first_segment = scheduler.auto_schedule_networks( self.plugin, self.ctx, HOST_D) self.assertTrue(observed_val_first_segment) agents = self.plugin.get_dhcp_agents_hosting_networks( self.ctx, [net_id]) self.assertEqual(1, len(agents)) seg2_id = self._create_segment(net_id) self._create_subnet(seg2_id, net_id, '192.168.11.0/24') helpers.register_dhcp_agent(HOST_C) segments_service_db.update_segment_host_mapping( self.ctx, HOST_C, {seg2_id}) observed_val_second_segment = scheduler.auto_schedule_networks( self.plugin, self.ctx, HOST_C) self.assertTrue(observed_val_second_segment) agents = self.plugin.get_dhcp_agents_hosting_networks( self.ctx, [net_id]) self.assertEqual(2, len(agents)) class TestNetworksFailover(TestDhcpSchedulerBaseTestCase, sched_db.DhcpAgentSchedulerDbMixin, common_db_mixin.CommonDbMixin): def test_reschedule_network_from_down_agent(self): net_id = uuidutils.generate_uuid() agents = self._create_and_set_agents_down(['host-a', 'host-b'], 1) self._test_schedule_bind_network([agents[0]], self.network_id) self._save_networks([net_id]) self._test_schedule_bind_network([agents[1]], net_id) with mock.patch.object(self, 'remove_network_from_dhcp_agent') as rn,\ mock.patch.object(self, 'schedule_network', return_value=[agents[1]]) as sch,\ mock.patch.object(self, 'get_network', create=True, return_value={'id': self.network_id}): notifier = mock.MagicMock() self.agent_notifiers[constants.AGENT_TYPE_DHCP] = notifier self.remove_networks_from_down_agents() rn.assert_called_with(mock.ANY, agents[0].id, self.network_id, notify=False) sch.assert_called_with(mock.ANY, {'id': self.network_id}) notifier.network_added_to_agent.assert_called_with( mock.ANY, self.network_id, agents[1].host) def _test_failed_rescheduling(self, rn_side_effect=None): agents = self._create_and_set_agents_down(['host-a', 'host-b'], 1) self._test_schedule_bind_network([agents[0]], self.network_id) with mock.patch.object(self, 'remove_network_from_dhcp_agent', side_effect=rn_side_effect) as rn,\ mock.patch.object(self, 'schedule_network', return_value=None) as sch,\ mock.patch.object(self, 'get_network', create=True, return_value={'id': self.network_id}): notifier = mock.MagicMock() self.agent_notifiers[constants.AGENT_TYPE_DHCP] = notifier self.remove_networks_from_down_agents() rn.assert_called_with(mock.ANY, agents[0].id, self.network_id, notify=False) sch.assert_called_with(mock.ANY, {'id': self.network_id}) self.assertFalse(notifier.network_added_to_agent.called) def test_reschedule_network_from_down_agent_failed(self): self._test_failed_rescheduling() def test_reschedule_network_from_down_agent_concurrent_removal(self): self._test_failed_rescheduling( rn_side_effect=dhcpagentscheduler.NetworkNotHostedByDhcpAgent( network_id='foo', agent_id='bar')) def _create_test_networks(self, num_net=0): networks = [network_obj.Network( self.ctx, id=uuidutils.generate_uuid(), name='network-%s' % (i)) for i in range(num_net)] for net in networks: net.create() return [net.id for net in networks] def _create_dhcp_agents(self): timestamp = datetime.datetime.now() dhcp_agent_ids = [uuidutils.generate_uuid() for x in range(2)] dhcp_agent_1 = agent.Agent(self.ctx, id=dhcp_agent_ids[0], agent_type='DHCP Agent', topic='fake_topic', host='fake_host', binary='fake_binary', created_at=timestamp, started_at=timestamp, heartbeat_timestamp=timestamp, configurations={}, load=0) dhcp_agent_1.create() dhcp_agent_2 = agent.Agent(self.ctx, id=dhcp_agent_ids[1], agent_type='DHCP Agent', topic='fake_topic', host='fake_host_1', binary='fake_binary', created_at=timestamp, started_at=timestamp, heartbeat_timestamp=timestamp, configurations={}, load=0) dhcp_agent_2.create() return [dhcp_agent_1.id, dhcp_agent_2.id] def test_filter_bindings(self): self.ctx = context.get_admin_context() dhcp_agt_ids = self._create_dhcp_agents() network_ids = sorted(self._create_test_networks(num_net=4)) ndab_obj1 = network_obj.NetworkDhcpAgentBinding(self.ctx, network_id=network_ids[0], dhcp_agent_id=dhcp_agt_ids[0]) ndab_obj1.create() ndab_obj2 = network_obj.NetworkDhcpAgentBinding(self.ctx, network_id=network_ids[1], dhcp_agent_id=dhcp_agt_ids[0]) ndab_obj2.create() ndab_obj3 = network_obj.NetworkDhcpAgentBinding(self.ctx, network_id=network_ids[2], dhcp_agent_id=dhcp_agt_ids[1]) ndab_obj3.create() ndab_obj4 = network_obj.NetworkDhcpAgentBinding(self.ctx, network_id=network_ids[3], dhcp_agent_id=dhcp_agt_ids[1]) ndab_obj4.create() bindings_objs = sorted(network_obj.NetworkDhcpAgentBinding.get_objects( self.ctx), key=attrgetter('network_id')) with mock.patch.object(self, 'agent_starting_up', side_effect=[True, False]): res = [b for b in self._filter_bindings(None, bindings_objs)] # once per each agent id1 and id2 self.assertEqual(2, len(res)) res_ids = [b.network_id for b in res] self.assertIn(network_ids[2], res_ids) self.assertIn(network_ids[3], res_ids) def test_reschedule_network_from_down_agent_failed_on_unexpected(self): agents = self._create_and_set_agents_down(['host-a'], 1) self._test_schedule_bind_network([agents[0]], self.network_id) with mock.patch.object( self, '_filter_bindings', side_effect=Exception()): # just make sure that no exception is raised self.remove_networks_from_down_agents() def test_reschedule_network_catches_exceptions_on_fetching_bindings(self): with mock.patch('neutron_lib.context.get_admin_context') as get_ctx: mock_ctx = mock.Mock() get_ctx.return_value = mock_ctx mock_ctx.session.query.side_effect = Exception() # just make sure that no exception is raised self.remove_networks_from_down_agents() def test_reschedule_doesnt_occur_if_no_agents(self): agents = self._create_and_set_agents_down(['host-a', 'host-b'], 2) self._test_schedule_bind_network([agents[0]], self.network_id) with mock.patch.object( self, 'remove_network_from_dhcp_agent') as rn: self.remove_networks_from_down_agents() self.assertFalse(rn.called) class DHCPAgentWeightSchedulerTestCase(test_plugin.Ml2PluginV2TestCase): """Unit test scenarios for WeightScheduler.schedule.""" def setUp(self): super(DHCPAgentWeightSchedulerTestCase, self).setUp() weight_scheduler = ( 'neutron.scheduler.dhcp_agent_scheduler.WeightScheduler') cfg.CONF.set_override('network_scheduler_driver', weight_scheduler) self.plugin = self.driver mock.patch.object( self.plugin, 'filter_hosts_with_network_access', side_effect=lambda context, network_id, hosts: hosts).start() self.plugin.network_scheduler = importutils.import_object( weight_scheduler) cfg.CONF.set_override("dhcp_load_type", "networks") self.segments_plugin = importutils.import_object( 'neutron.services.segments.plugin.Plugin') self.ctx = context.get_admin_context() def _create_network(self): net = self.plugin.create_network( self.ctx, {'network': {'name': 'name', 'tenant_id': 'tenant_one', 'admin_state_up': True, 'shared': True}}) return net['id'] def _create_segment(self, network_id): seg = self.segments_plugin.create_segment( self.ctx, {'segment': {'network_id': network_id, 'name': None, 'description': None, 'physical_network': 'physnet1', 'network_type': 'vlan', 'segmentation_id': constants.ATTR_NOT_SPECIFIED}}) return seg['id'] def test_scheduler_one_agents_per_network(self): net_id = self._create_network() helpers.register_dhcp_agent(HOST_C) self.plugin.network_scheduler.schedule(self.plugin, self.ctx, {'id': net_id}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, [net_id]) self.assertEqual(1, len(agents)) def test_scheduler_two_agents_per_network(self): cfg.CONF.set_override('dhcp_agents_per_network', 2) net_id = self._create_network() helpers.register_dhcp_agent(HOST_C) helpers.register_dhcp_agent(HOST_D) self.plugin.network_scheduler.schedule(self.plugin, self.ctx, {'id': net_id}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, [net_id]) self.assertEqual(2, len(agents)) def test_scheduler_no_active_agents(self): net_id = self._create_network() self.plugin.network_scheduler.schedule(self.plugin, self.ctx, {'id': net_id}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, [net_id]) self.assertEqual(0, len(agents)) def test_scheduler_equal_distribution(self): net_id_1 = self._create_network() net_id_2 = self._create_network() net_id_3 = self._create_network() helpers.register_dhcp_agent(HOST_C) helpers.register_dhcp_agent(HOST_D, networks=1) self.plugin.network_scheduler.schedule( self.plugin, context.get_admin_context(), {'id': net_id_1}) helpers.register_dhcp_agent(HOST_D, networks=2) self.plugin.network_scheduler.schedule( self.plugin, context.get_admin_context(), {'id': net_id_2}) helpers.register_dhcp_agent(HOST_C, networks=4) self.plugin.network_scheduler.schedule( self.plugin, context.get_admin_context(), {'id': net_id_3}) agent1 = self.plugin.get_dhcp_agents_hosting_networks( self.ctx, [net_id_1]) agent2 = self.plugin.get_dhcp_agents_hosting_networks( self.ctx, [net_id_2]) agent3 = self.plugin.get_dhcp_agents_hosting_networks( self.ctx, [net_id_3]) self.assertEqual('host-c', agent1[0]['host']) self.assertEqual('host-c', agent2[0]['host']) self.assertEqual('host-d', agent3[0]['host']) def _get_network_with_candidate_hosts(self, net_id, seg_id): # expire the session so that the segment is fully reloaded on fetch, # including its new host mapping self.ctx.session.expire_all() net = self.plugin.get_network(self.ctx, net_id) seg = self.segments_plugin.get_segment(self.ctx, seg_id) net['candidate_hosts'] = seg['hosts'] return net def test_schedule_segment_one_hostable_agent(self): net_id = self._create_network() seg_id = self._create_segment(net_id) helpers.register_dhcp_agent(HOST_C) helpers.register_dhcp_agent(HOST_D) segments_service_db.update_segment_host_mapping( self.ctx, HOST_C, {seg_id}) net = self._get_network_with_candidate_hosts(net_id, seg_id) agents = self.plugin.network_scheduler.schedule( self.plugin, self.ctx, net) self.assertEqual(1, len(agents)) self.assertEqual(HOST_C, agents[0].host) def test_schedule_segment_many_hostable_agents(self): net_id = self._create_network() seg_id = self._create_segment(net_id) helpers.register_dhcp_agent(HOST_C) helpers.register_dhcp_agent(HOST_D) segments_service_db.update_segment_host_mapping( self.ctx, HOST_C, {seg_id}) segments_service_db.update_segment_host_mapping( self.ctx, HOST_D, {seg_id}) net = self._get_network_with_candidate_hosts(net_id, seg_id) agents = self.plugin.network_scheduler.schedule( self.plugin, self.ctx, net) self.assertEqual(1, len(agents)) self.assertIn(agents[0].host, [HOST_C, HOST_D]) def test_schedule_segment_no_host_mapping(self): net_id = self._create_network() seg_id = self._create_segment(net_id) helpers.register_dhcp_agent(HOST_C) helpers.register_dhcp_agent(HOST_D) net = self.plugin.get_network(self.ctx, net_id) seg = self.segments_plugin.get_segment(self.ctx, seg_id) net['candidate_hosts'] = seg['hosts'] agents = self.plugin.network_scheduler.schedule( self.plugin, self.ctx, net) self.assertEqual(0, len(agents)) def test_schedule_segment_two_agents_per_segment(self): cfg.CONF.set_override('dhcp_agents_per_network', 2) net_id = self._create_network() seg_id = self._create_segment(net_id) helpers.register_dhcp_agent(HOST_C) helpers.register_dhcp_agent(HOST_D) segments_service_db.update_segment_host_mapping( self.ctx, HOST_C, {seg_id}) segments_service_db.update_segment_host_mapping( self.ctx, HOST_D, {seg_id}) net = self._get_network_with_candidate_hosts(net_id, seg_id) agents = self.plugin.network_scheduler.schedule( self.plugin, self.ctx, net) self.assertEqual(2, len(agents)) self.assertIn(agents[0].host, [HOST_C, HOST_D]) self.assertIn(agents[1].host, [HOST_C, HOST_D]) def test_schedule_segment_two_agents_per_segment_one_hostable_agent(self): cfg.CONF.set_override('dhcp_agents_per_network', 2) net_id = self._create_network() seg_id = self._create_segment(net_id) helpers.register_dhcp_agent(HOST_C) helpers.register_dhcp_agent(HOST_D) segments_service_db.update_segment_host_mapping( self.ctx, HOST_C, {seg_id}) net = self._get_network_with_candidate_hosts(net_id, seg_id) agents = self.plugin.network_scheduler.schedule( self.plugin, self.ctx, net) self.assertEqual(1, len(agents)) self.assertEqual(HOST_C, agents[0].host) class TestDhcpSchedulerFilter(TestDhcpSchedulerBaseTestCase, sched_db.DhcpAgentSchedulerDbMixin): def _test_get_dhcp_agents_hosting_networks(self, expected, **kwargs): agents = self._create_and_set_agents_down(['host-a', 'host-b'], 1) agents += self._create_and_set_agents_down(['host-c', 'host-d'], 1, admin_state_up=False) networks = kwargs.pop('networks', [self.network_id]) for network in networks: self._test_schedule_bind_network(agents, network) agents = self.get_dhcp_agents_hosting_networks(self.ctx, networks, **kwargs) host_ids = set(a['host'] for a in agents) self.assertEqual(expected, host_ids) def test_get_dhcp_agents_hosting_networks_default(self): self._test_get_dhcp_agents_hosting_networks({'host-a', 'host-b', 'host-c', 'host-d'}) def test_get_dhcp_agents_hosting_networks_active(self): self._test_get_dhcp_agents_hosting_networks({'host-b', 'host-d'}, active=True) def test_get_dhcp_agents_hosting_networks_admin_up(self): self._test_get_dhcp_agents_hosting_networks({'host-a', 'host-b'}, admin_state_up=True) def test_get_dhcp_agents_hosting_networks_active_admin_up(self): self._test_get_dhcp_agents_hosting_networks({'host-b'}, active=True, admin_state_up=True) def test_get_dhcp_agents_hosting_networks_admin_down(self): self._test_get_dhcp_agents_hosting_networks({'host-c', 'host-d'}, admin_state_up=False) def test_get_dhcp_agents_hosting_networks_active_admin_down(self): self._test_get_dhcp_agents_hosting_networks({'host-d'}, active=True, admin_state_up=False) def test_get_dhcp_agents_hosting_many_networks(self): net_id = uuidutils.generate_uuid() self._save_networks([net_id]) networks = [net_id, self.network_id] self._test_get_dhcp_agents_hosting_networks({'host-a', 'host-b', 'host-c', 'host-d'}, networks=networks) def test_get_dhcp_agents_host_network_filter_by_hosts(self): self._test_get_dhcp_agents_hosting_networks({'host-a'}, hosts=['host-a']) class DHCPAgentAZAwareWeightSchedulerTestCase(TestDhcpSchedulerBaseTestCase): def setUp(self): super(DHCPAgentAZAwareWeightSchedulerTestCase, self).setUp() self.setup_coreplugin('ml2') cfg.CONF.set_override("network_scheduler_driver", 'neutron.scheduler.dhcp_agent_scheduler.AZAwareWeightScheduler') self.plugin = importutils.import_object('neutron.plugins.ml2.plugin.' 'Ml2Plugin') mock.patch.object( self.plugin, 'filter_hosts_with_network_access', side_effect=lambda context, network_id, hosts: hosts).start() cfg.CONF.set_override('dhcp_agents_per_network', 1) cfg.CONF.set_override("dhcp_load_type", "networks") def test_az_scheduler_one_az_hints(self): net_id = uuidutils.generate_uuid() self._save_networks([net_id]) helpers.register_dhcp_agent('az1-host1', networks=1, az='az1') helpers.register_dhcp_agent('az1-host2', networks=2, az='az1') helpers.register_dhcp_agent('az2-host1', networks=3, az='az2') helpers.register_dhcp_agent('az2-host2', networks=4, az='az2') self.plugin.network_scheduler.schedule(self.plugin, self.ctx, {'id': net_id, 'availability_zone_hints': ['az2']}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, [net_id]) self.assertEqual(1, len(agents)) self.assertEqual('az2-host1', agents[0]['host']) def test_az_scheduler_default_az_hints(self): net_id = uuidutils.generate_uuid() cfg.CONF.set_override('default_availability_zones', ['az1']) self._save_networks([net_id]) helpers.register_dhcp_agent('az1-host1', networks=1, az='az1') helpers.register_dhcp_agent('az1-host2', networks=2, az='az1') helpers.register_dhcp_agent('az2-host1', networks=3, az='az2') helpers.register_dhcp_agent('az2-host2', networks=4, az='az2') self.plugin.network_scheduler.schedule(self.plugin, self.ctx, {'id': net_id, 'availability_zone_hints': []}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, [net_id]) self.assertEqual(1, len(agents)) self.assertEqual('az1-host1', agents[0]['host']) def test_az_scheduler_two_az_hints(self): net_id = uuidutils.generate_uuid() cfg.CONF.set_override('dhcp_agents_per_network', 2) self._save_networks([net_id]) helpers.register_dhcp_agent('az1-host1', networks=1, az='az1') helpers.register_dhcp_agent('az1-host2', networks=2, az='az1') helpers.register_dhcp_agent('az2-host1', networks=3, az='az2') helpers.register_dhcp_agent('az2-host2', networks=4, az='az2') helpers.register_dhcp_agent('az3-host1', networks=5, az='az3') helpers.register_dhcp_agent('az3-host2', networks=6, az='az3') self.plugin.network_scheduler.schedule(self.plugin, self.ctx, {'id': net_id, 'availability_zone_hints': ['az1', 'az3']}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, [net_id]) self.assertEqual(2, len(agents)) expected_hosts = set(['az1-host1', 'az3-host1']) hosts = set([a['host'] for a in agents]) self.assertEqual(expected_hosts, hosts) def test_az_scheduler_two_az_hints_one_available_az(self): net_id = uuidutils.generate_uuid() cfg.CONF.set_override('dhcp_agents_per_network', 2) self._save_networks([net_id]) helpers.register_dhcp_agent('az1-host1', networks=1, az='az1') helpers.register_dhcp_agent('az1-host2', networks=2, az='az1') helpers.register_dhcp_agent('az2-host1', networks=3, alive=False, az='az2') helpers.register_dhcp_agent('az2-host2', networks=4, admin_state_up=False, az='az2') self.plugin.network_scheduler.schedule(self.plugin, self.ctx, {'id': net_id, 'availability_zone_hints': ['az1', 'az2']}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, [net_id]) self.assertEqual(2, len(agents)) expected_hosts = set(['az1-host1', 'az1-host2']) hosts = set([a['host'] for a in agents]) self.assertEqual(expected_hosts, hosts) def _test_az_scheduler_no_az_hints(self, multiple_agent=False): net_id = uuidutils.generate_uuid() num_agent = 2 if multiple_agent else 1 cfg.CONF.set_override('dhcp_agents_per_network', num_agent) self._save_networks([net_id]) helpers.register_dhcp_agent('az1-host1', networks=2, az='az1') helpers.register_dhcp_agent('az1-host2', networks=3, az='az1') helpers.register_dhcp_agent('az2-host1', networks=2, az='az2') helpers.register_dhcp_agent('az2-host2', networks=1, az='az2') self.plugin.network_scheduler.schedule(self.plugin, self.ctx, {'id': net_id, 'availability_zone_hints': []}) agents = self.plugin.get_dhcp_agents_hosting_networks(self.ctx, [net_id]) self.assertEqual(num_agent, len(agents)) if multiple_agent: expected_hosts = set(['az1-host1', 'az2-host2']) else: expected_hosts = set(['az2-host2']) hosts = {a['host'] for a in agents} self.assertEqual(expected_hosts, hosts) def test_az_scheduler_no_az_hints_multiple_agent(self): self._test_az_scheduler_no_az_hints(multiple_agent=True) def test_az_scheduler_no_az_hints_one_agent(self): self._test_az_scheduler_no_az_hints() def test_az_scheduler_select_az_with_least_weight(self): self._save_networks([uuidutils.generate_uuid()]) dhcp_agents = [] # Register 6 dhcp agents in 3 AZs, every AZ will have 2 agents. dhcp_agents.append( helpers.register_dhcp_agent('az1-host1', networks=6, az='az1')) dhcp_agents.append( helpers.register_dhcp_agent('az1-host2', networks=5, az='az1')) dhcp_agents.append( helpers.register_dhcp_agent('az2-host1', networks=4, az='az2')) dhcp_agents.append( helpers.register_dhcp_agent('az2-host2', networks=3, az='az2')) dhcp_agents.append( helpers.register_dhcp_agent('az3-host1', networks=2, az='az3')) dhcp_agents.append( helpers.register_dhcp_agent('az3-host2', networks=1, az='az3')) # Try multiple times to verify that the select of AZ scheduler will # output stably. for i in range(3): # Shuffle the agents random.shuffle(dhcp_agents) # Select agents with empty resource_hosted_agents. This means each # AZ will have same amount of agents scheduled (0 in this case) agents_select = self.plugin.network_scheduler.select( self.plugin, self.ctx, dhcp_agents, [], 2) self.assertEqual(2, len(agents_select)) # The agent and az with least weight should always be selected # first self.assertEqual('az3-host2', agents_select[0]['host']) self.assertEqual('az3', agents_select[0]['availability_zone']) # The second selected agent should be the agent with least weight, # which is also not in the same az as the first selected agent. self.assertEqual('az2-host2', agents_select[1]['host']) self.assertEqual('az2', agents_select[1]['availability_zone']) neutron-12.1.1/neutron/tests/unit/scheduler/__init__.py0000664000175000017500000000000013553660046023210 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/__init__.py0000664000175000017500000000130713553660046021245 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.common import eventlet_utils eventlet_utils.monkey_patch() neutron-12.1.1/neutron/tests/unit/api/0000775000175000017500000000000013553660156017706 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/0000775000175000017500000000000013553660156020472 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/callbacks/0000775000175000017500000000000013553660156022411 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/callbacks/producer/0000775000175000017500000000000013553660156024234 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/callbacks/producer/__init__.py0000664000175000017500000000000013553660046026331 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/callbacks/producer/test_registry.py0000664000175000017500000000536013553660046027517 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.api.rpc.callbacks import exceptions from neutron.api.rpc.callbacks.producer import registry from neutron.api.rpc.callbacks import resources from neutron.objects.qos import policy from neutron.tests.unit.services.qos import base class ProducerRegistryTestCase(base.BaseQosTestCase): def test_pull_returns_callback_result(self): policy_obj = policy.QosPolicy(context=None) def _fake_policy_cb(*args, **kwargs): return policy_obj registry.provide(_fake_policy_cb, resources.QOS_POLICY) self.assertEqual( policy_obj, registry.pull(resources.QOS_POLICY, 'fake_id')) def test_pull_does_not_raise_on_none(self): def _none_cb(*args, **kwargs): pass registry.provide(_none_cb, resources.QOS_POLICY) obj = registry.pull(resources.QOS_POLICY, 'fake_id') self.assertIsNone(obj) def test_pull_raises_on_wrong_object_type(self): def _wrong_type_cb(*args, **kwargs): return object() registry.provide(_wrong_type_cb, resources.QOS_POLICY) self.assertRaises( exceptions.CallbackWrongResourceType, registry.pull, resources.QOS_POLICY, 'fake_id') def test_pull_raises_on_callback_not_found(self): self.assertRaises( exceptions.CallbackNotFound, registry.pull, resources.QOS_POLICY, 'fake_id') def test__get_manager_is_singleton(self): self.assertIs(registry._get_manager(), registry._get_manager()) def test_unprovide(self): def _fake_policy_cb(*args, **kwargs): pass registry.provide(_fake_policy_cb, resources.QOS_POLICY) registry.unprovide(_fake_policy_cb, resources.QOS_POLICY) self.assertRaises( exceptions.CallbackNotFound, registry.pull, resources.QOS_POLICY, 'fake_id') def test_clear_unprovides_all_producers(self): def _fake_policy_cb(*args, **kwargs): pass registry.provide(_fake_policy_cb, resources.QOS_POLICY) registry.clear() self.assertRaises( exceptions.CallbackNotFound, registry.pull, resources.QOS_POLICY, 'fake_id') neutron-12.1.1/neutron/tests/unit/api/rpc/callbacks/consumer/0000775000175000017500000000000013553660156024244 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/callbacks/consumer/__init__.py0000664000175000017500000000000013553660046026341 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/callbacks/consumer/test_registry.py0000664000175000017500000000510213553660047027522 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.api.rpc.callbacks.consumer import registry from neutron.tests import base class ConsumerRegistryTestCase(base.BaseTestCase): def test__get_manager_is_singleton(self): self.assertIs(registry._get_manager(), registry._get_manager()) @mock.patch.object(registry, '_get_manager') def test_subscribe(self, manager_mock): callback = lambda: None registry.subscribe(callback, 'TYPE') manager_mock().register.assert_called_with(callback, 'TYPE') @mock.patch.object(registry, '_get_manager') def test_register(self, manager_mock): callback = lambda: None registry.register(callback, 'TYPE') manager_mock().register.assert_called_with(callback, 'TYPE') @mock.patch.object(registry, '_get_manager') def test_unsubscribe(self, manager_mock): callback = lambda: None registry.unsubscribe(callback, 'TYPE') manager_mock().unregister.assert_called_with(callback, 'TYPE') @mock.patch.object(registry, '_get_manager') def test_clear(self, manager_mock): registry.clear() manager_mock().clear.assert_called_with() @mock.patch.object(registry, '_get_manager') def test_push(self, manager_mock): resource_type_ = object() resource_ = object() event_type_ = object() context = mock.Mock() callback1 = mock.Mock() callback2 = mock.Mock() legacy_callback = mock.Mock() registry.register(callback1, 'x') registry.register(callback2, 'x') registry.subscribe(legacy_callback, 'x') callbacks = {callback1, callback2, legacy_callback} manager_mock().get_callbacks.return_value = callbacks registry.push(context, resource_type_, [resource_], event_type_) for callback in (callback1, callback2): callback.assert_called_with(context, resource_type_, [resource_], event_type_) legacy_callback.assert_called_with([resource_], event_type_) neutron-12.1.1/neutron/tests/unit/api/rpc/callbacks/test_resources.py0000664000175000017500000000514713553660046026041 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_versionedobjects import base as obj_base from neutron.api.rpc.callbacks import resources from neutron.objects.qos import policy from neutron.tests import base class GetResourceTypeTestCase(base.BaseTestCase): def test_get_resource_type_none(self): self.assertIsNone(resources.get_resource_type(None)) def test_get_resource_type_wrong_type(self): self.assertIsNone(resources.get_resource_type(object())) def test_get_resource_type(self): # we could use any other registered NeutronObject type here self.assertEqual(policy.QosPolicy.obj_name(), resources.get_resource_type(policy.QosPolicy())) class IsValidResourceTypeTestCase(base.BaseTestCase): def test_known_type(self): # it could be any other NeutronObject, assuming it's known to RPC # callbacks self.assertTrue(resources.is_valid_resource_type( policy.QosPolicy.obj_name())) def test_unknown_type(self): self.assertFalse( resources.is_valid_resource_type('unknown-resource-type')) class GetResourceClsTestCase(base.BaseTestCase): def test_known_type(self): # it could be any other NeutronObject, assuming it's known to RPC # callbacks self.assertEqual(policy.QosPolicy, resources.get_resource_cls(resources.QOS_POLICY)) def test_unknown_type(self): self.assertIsNone(resources.get_resource_cls('unknown-resource-type')) class RegisterResourceClass(base.BaseTestCase): def test_register_resource_class(self): class DummyOVO(obj_base.VersionedObject): pass self.assertFalse( resources.is_valid_resource_type('DummyOVO')) resources.register_resource_class(DummyOVO) self.assertTrue( resources.is_valid_resource_type('DummyOVO')) def test_register_bogus_resource_class(self): class DummyOVO(object): pass self.assertRaises(ValueError, resources.register_resource_class, DummyOVO) neutron-12.1.1/neutron/tests/unit/api/rpc/callbacks/test_resource_manager.py0000664000175000017500000001266413553660047027353 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import exceptions from neutron.api.rpc.callbacks import exceptions as rpc_exc from neutron.api.rpc.callbacks import resource_manager from neutron.tests.unit.services.qos import base IS_VALID_RESOURCE_TYPE = ( 'neutron.api.rpc.callbacks.resources.is_valid_resource_type') class ResourceCallbacksManagerTestCaseMixin(object): def test_register_fails_on_invalid_type(self): self.assertRaises( exceptions.Invalid, self.mgr.register, lambda: None, 'TYPE') @mock.patch(IS_VALID_RESOURCE_TYPE, return_value=True) def test_clear_unregisters_all_callbacks(self, *mocks): self.mgr.register(lambda: None, 'TYPE1') self.mgr.register(lambda: None, 'TYPE2') self.mgr.clear() self.assertEqual([], self.mgr.get_subscribed_types()) def test_unregister_fails_on_invalid_type(self): self.assertRaises( exceptions.Invalid, self.mgr.unregister, lambda: None, 'TYPE') @mock.patch(IS_VALID_RESOURCE_TYPE, return_value=True) def test_unregister_fails_on_unregistered_callback(self, *mocks): self.assertRaises( rpc_exc.CallbackNotFound, self.mgr.unregister, lambda: None, 'TYPE') @mock.patch(IS_VALID_RESOURCE_TYPE, return_value=True) def test_unregister_unregisters_callback(self, *mocks): callback = lambda: None self.mgr.register(callback, 'TYPE') self.mgr.unregister(callback, 'TYPE') self.assertEqual([], self.mgr.get_subscribed_types()) @mock.patch(IS_VALID_RESOURCE_TYPE, return_value=True) def test___init___does_not_reset_callbacks(self, *mocks): callback = lambda: None self.mgr.register(callback, 'TYPE') resource_manager.ProducerResourceCallbacksManager() self.assertEqual(['TYPE'], self.mgr.get_subscribed_types()) class ProducerResourceCallbacksManagerTestCase( base.BaseQosTestCase, ResourceCallbacksManagerTestCaseMixin): def setUp(self): super(ProducerResourceCallbacksManagerTestCase, self).setUp() self.mgr = self.prod_mgr @mock.patch(IS_VALID_RESOURCE_TYPE, return_value=True) def test_register_registers_callback(self, *mocks): callback = lambda: None self.mgr.register(callback, 'TYPE') self.assertEqual(callback, self.mgr.get_callback('TYPE')) @mock.patch(IS_VALID_RESOURCE_TYPE, return_value=True) def test_register_fails_on_multiple_calls(self, *mocks): self.mgr.register(lambda: None, 'TYPE') self.assertRaises( rpc_exc.CallbacksMaxLimitReached, self.mgr.register, lambda: None, 'TYPE') def test_get_callback_fails_on_invalid_type(self): self.assertRaises( exceptions.Invalid, self.mgr.get_callback, 'TYPE') @mock.patch(IS_VALID_RESOURCE_TYPE, return_value=True) def test_get_callback_fails_on_unregistered_callback( self, *mocks): self.assertRaises( rpc_exc.CallbackNotFound, self.mgr.get_callback, 'TYPE') @mock.patch(IS_VALID_RESOURCE_TYPE, return_value=True) def test_get_callback_returns_proper_callback(self, *mocks): callback1 = lambda: None callback2 = lambda: None self.mgr.register(callback1, 'TYPE1') self.mgr.register(callback2, 'TYPE2') self.assertEqual(callback1, self.mgr.get_callback('TYPE1')) self.assertEqual(callback2, self.mgr.get_callback('TYPE2')) class ConsumerResourceCallbacksManagerTestCase( base.BaseQosTestCase, ResourceCallbacksManagerTestCaseMixin): def setUp(self): super(ConsumerResourceCallbacksManagerTestCase, self).setUp() self.mgr = self.cons_mgr @mock.patch(IS_VALID_RESOURCE_TYPE, return_value=True) def test_register_registers_callback(self, *mocks): callback = lambda: None self.mgr.register(callback, 'TYPE') self.assertEqual({callback}, self.mgr.get_callbacks('TYPE')) @mock.patch(IS_VALID_RESOURCE_TYPE, return_value=True) def test_register_succeeds_on_multiple_calls(self, *mocks): callback1 = lambda: None callback2 = lambda: None self.mgr.register(callback1, 'TYPE') self.mgr.register(callback2, 'TYPE') @mock.patch(IS_VALID_RESOURCE_TYPE, return_value=True) def test_get_callbacks_fails_on_unregistered_callback( self, *mocks): self.assertRaises( rpc_exc.CallbackNotFound, self.mgr.get_callbacks, 'TYPE') @mock.patch(IS_VALID_RESOURCE_TYPE, return_value=True) def test_get_callbacks_returns_proper_callbacks(self, *mocks): callback1 = lambda: None callback2 = lambda: None self.mgr.register(callback1, 'TYPE1') self.mgr.register(callback2, 'TYPE2') self.assertEqual(set([callback1]), self.mgr.get_callbacks('TYPE1')) self.assertEqual(set([callback2]), self.mgr.get_callbacks('TYPE2')) neutron-12.1.1/neutron/tests/unit/api/rpc/callbacks/__init__.py0000664000175000017500000000000013553660046024506 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/callbacks/test_version_manager.py0000664000175000017500000001472513553660046027210 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.api.rpc.callbacks import exceptions from neutron.api.rpc.callbacks import resources from neutron.api.rpc.callbacks import version_manager from neutron.db import agents_db from neutron.tests import base TEST_RESOURCE_TYPE = 'TestResourceType' TEST_VERSION_A = '1.11' TEST_VERSION_B = '1.12' TEST_RESOURCE_TYPE_2 = 'AnotherResource' AGENT_HOST_1 = 'host-1' AGENT_HOST_2 = 'host-2' AGENT_TYPE_1 = 'dhcp-agent' AGENT_TYPE_2 = 'openvswitch-agent' CONSUMER_1 = version_manager.AgentConsumer(AGENT_TYPE_1, AGENT_HOST_1) CONSUMER_2 = version_manager.AgentConsumer(AGENT_TYPE_2, AGENT_HOST_2) class ResourceConsumerTrackerTest(base.BaseTestCase): def test_consumer_set_versions(self): cv = version_manager.ResourceConsumerTracker() cv.set_versions(CONSUMER_1, {TEST_RESOURCE_TYPE: TEST_VERSION_A}) self.assertIn(TEST_VERSION_A, cv.get_resource_versions(TEST_RESOURCE_TYPE)) def test_consumer_updates_version(self): cv = version_manager.ResourceConsumerTracker() for version in [TEST_VERSION_A, TEST_VERSION_B]: cv.set_versions(CONSUMER_1, {TEST_RESOURCE_TYPE: version}) self.assertEqual(set([TEST_VERSION_B]), cv.get_resource_versions(TEST_RESOURCE_TYPE)) def test_multiple_consumer_version_update(self): cv = version_manager.ResourceConsumerTracker() cv.set_versions(CONSUMER_1, {TEST_RESOURCE_TYPE: TEST_VERSION_A}) cv.set_versions(CONSUMER_2, {TEST_RESOURCE_TYPE: TEST_VERSION_A}) cv.set_versions(CONSUMER_1, {TEST_RESOURCE_TYPE: TEST_VERSION_B}) self.assertEqual(set([TEST_VERSION_A, TEST_VERSION_B]), cv.get_resource_versions(TEST_RESOURCE_TYPE)) def test_consumer_downgrades_removing_resource(self): cv = version_manager.ResourceConsumerTracker() cv.set_versions(CONSUMER_1, {TEST_RESOURCE_TYPE: TEST_VERSION_B, TEST_RESOURCE_TYPE_2: TEST_VERSION_A}) cv.set_versions(CONSUMER_1, {TEST_RESOURCE_TYPE: TEST_VERSION_A}) self.assertEqual(set(), cv.get_resource_versions(TEST_RESOURCE_TYPE_2)) self.assertEqual(set([TEST_VERSION_A]), cv.get_resource_versions(TEST_RESOURCE_TYPE)) def test_consumer_downgrades_stops_reporting(self): cv = version_manager.ResourceConsumerTracker() cv.set_versions(CONSUMER_1, {TEST_RESOURCE_TYPE: TEST_VERSION_B, TEST_RESOURCE_TYPE_2: TEST_VERSION_A}) cv.set_versions(CONSUMER_1, {}) for resource_type in [TEST_RESOURCE_TYPE, TEST_RESOURCE_TYPE_2]: self.assertEqual(set(), cv.get_resource_versions(resource_type)) def test_different_adds_triggers_recalculation(self): cv = version_manager.ResourceConsumerTracker() for version in [TEST_VERSION_A, TEST_VERSION_B]: cv.set_versions(CONSUMER_1, {TEST_RESOURCE_TYPE: version}) self.assertTrue(cv._needs_recalculation) cv._recalculate_versions = mock.Mock() cv.get_resource_versions(TEST_RESOURCE_TYPE) cv._recalculate_versions.assert_called_once_with() class CachedResourceConsumerTrackerTest(base.BaseTestCase): def setUp(self): super(CachedResourceConsumerTrackerTest, self).setUp() self.refreshed = False class _FakePlugin(agents_db.AgentDbMixin): @staticmethod def get_agents_resource_versions(tracker): self.refreshed = True tracker.set_versions(CONSUMER_1, {TEST_RESOURCE_TYPE: TEST_VERSION_A}) self.get_plugin = mock.patch('neutron_lib.plugins.directory' '.get_plugin').start() self.get_plugin.return_value = _FakePlugin() def test_plugin_does_not_implement_agentsdb_exception(self): self.get_plugin.return_value = object() cached_tracker = version_manager.CachedResourceConsumerTracker() self.assertRaises(exceptions.NoAgentDbMixinImplemented, cached_tracker.get_resource_versions, resources.QOS_POLICY) def test_consumer_versions_callback(self): cached_tracker = version_manager.CachedResourceConsumerTracker() self.assertIn(TEST_VERSION_A, cached_tracker.get_resource_versions( TEST_RESOURCE_TYPE)) def test_update_versions(self): cached_tracker = version_manager.CachedResourceConsumerTracker() initial_versions = cached_tracker.get_resource_versions( TEST_RESOURCE_TYPE) initial_versions_2 = cached_tracker.get_resource_versions( TEST_RESOURCE_TYPE_2) cached_tracker.update_versions( CONSUMER_1, {TEST_RESOURCE_TYPE: TEST_VERSION_B, TEST_RESOURCE_TYPE_2: TEST_VERSION_A}) final_versions = cached_tracker.get_resource_versions( TEST_RESOURCE_TYPE) final_versions_2 = cached_tracker.get_resource_versions( TEST_RESOURCE_TYPE_2) self.assertNotEqual(initial_versions, final_versions) self.assertNotEqual(initial_versions_2, final_versions_2) def test_versions_ttl(self): cached_tracker = version_manager.CachedResourceConsumerTracker() with mock.patch('time.time') as time_patch: time_patch.return_value = 1 cached_tracker.get_resource_versions(TEST_RESOURCE_TYPE) self.assertTrue(self.refreshed) self.refreshed = False time_patch.return_value = 2 cached_tracker.get_resource_versions(TEST_RESOURCE_TYPE) self.assertFalse(self.refreshed) time_patch.return_value = 2 + version_manager.VERSIONS_TTL cached_tracker.get_resource_versions(TEST_RESOURCE_TYPE) self.assertTrue(self.refreshed) neutron-12.1.1/neutron/tests/unit/api/rpc/__init__.py0000664000175000017500000000000013553660046022567 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/agentnotifiers/0000775000175000017500000000000013553660156023513 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/agentnotifiers/test_l3_rpc_agent_api.py0000664000175000017500000000333413553660047030317 0ustar zuulzuul00000000000000# Copyright (c) 2016 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api from neutron.tests import base class TestL3AgentNotifyAPI(base.BaseTestCase): def setUp(self): super(TestL3AgentNotifyAPI, self).setUp() self.rpc_client_mock = mock.patch( 'neutron.common.rpc.get_client').start().return_value self.l3_notifier = l3_rpc_agent_api.L3AgentNotifyAPI() def _test_arp_update(self, method): arp_table = {'ip_address': '1.1.1.1', 'mac_address': '22:f1:6c:9c:79:4a', 'subnet_id': 'subnet_id'} router_id = 'router_id' getattr(self.l3_notifier, method)(mock.Mock(), router_id, arp_table) self.rpc_client_mock.prepare.assert_called_once_with( fanout=True, version='1.2') cctxt = self.rpc_client_mock.prepare.return_value cctxt.cast.assert_called_once_with( mock.ANY, method, payload={'router_id': router_id, 'arp_table': arp_table}) def test_add_arp_entry(self): self._test_arp_update('add_arp_entry') def test_del_arp_entry(self): self._test_arp_update('del_arp_entry') neutron-12.1.1/neutron/tests/unit/api/rpc/agentnotifiers/test_dhcp_rpc_agent_api.py0000664000175000017500000003443013553660047030720 0ustar zuulzuul00000000000000# Copyright (c) 2013 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import copy import datetime import mock from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib.plugins import directory from oslo_utils import timeutils from oslo_utils import uuidutils from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api from neutron.common import utils from neutron.db.agentschedulers_db import cfg from neutron.objects import agent as agent_obj from neutron.tests import base class TestDhcpAgentNotifyAPI(base.BaseTestCase): def setUp(self): super(TestDhcpAgentNotifyAPI, self).setUp() self.notifier = ( dhcp_rpc_agent_api.DhcpAgentNotifyAPI(plugin=mock.Mock())) mock_util_p = mock.patch.object(utils, 'is_extension_supported') mock_log_p = mock.patch.object(dhcp_rpc_agent_api, 'LOG') mock_fanout_p = mock.patch.object(self.notifier, '_fanout_message') mock_cast_p = mock.patch.object(self.notifier, '_cast_message') self.mock_util = mock_util_p.start() self.mock_log = mock_log_p.start() self.mock_fanout = mock_fanout_p.start() self.mock_cast = mock_cast_p.start() def _test__schedule_network(self, network, new_agents=None, existing_agents=None, expected_casts=0, expected_warnings=0): self.notifier.plugin.schedule_network.return_value = new_agents agents = self.notifier._schedule_network( mock.ANY, network, existing_agents) if new_agents is None: new_agents = [] self.assertEqual(new_agents + existing_agents, agents) self.assertEqual(expected_casts, self.mock_cast.call_count) self.assertEqual(expected_warnings, self.mock_log.warning.call_count) def test__schedule_network(self): agent = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid(), host='host') agent.admin_state_up = True agent.heartbeat_timestamp = timeutils.utcnow() network = {'id': 'foo_net_id'} self._test__schedule_network(network, new_agents=[agent], existing_agents=[], expected_casts=1, expected_warnings=0) def test__schedule_network_no_existing_agents(self): agent = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid()) agent.admin_state_up = True agent.heartbeat_timestamp = timeutils.utcnow() network = {'id': 'foo_net_id'} self._test__schedule_network(network, new_agents=None, existing_agents=[agent], expected_casts=0, expected_warnings=0) def test__schedule_network_no_new_agents(self): network = {'id': 'foo_net_id'} self._test__schedule_network(network, new_agents=None, existing_agents=[], expected_casts=0, expected_warnings=1) def _test__get_enabled_agents(self, network, agents=None, port_count=0, expected_warnings=0, expected_errors=0): self.notifier.plugin.get_ports_count.return_value = port_count enabled_agents = self.notifier._get_enabled_agents( mock.ANY, network, agents, mock.ANY, mock.ANY) if not cfg.CONF.enable_services_on_agents_with_admin_state_down: agents = [x for x in agents if x.admin_state_up] self.assertEqual(agents, enabled_agents) self.assertEqual(expected_warnings, self.mock_log.warning.call_count) self.assertEqual(expected_errors, self.mock_log.error.call_count) def test__get_enabled_agents(self): agent1 = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid()) agent1.admin_state_up = True agent1.heartbeat_timestamp = timeutils.utcnow() agent2 = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid()) agent2.admin_state_up = False agent2.heartbeat_timestamp = timeutils.utcnow() network = {'id': 'foo_network_id'} self._test__get_enabled_agents(network, agents=[agent1]) def test__get_enabled_agents_with_inactive_ones(self): agent1 = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid()) agent1.admin_state_up = True agent1.heartbeat_timestamp = timeutils.utcnow() agent2 = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid()) agent2.admin_state_up = True # This is effectively an inactive agent agent2.heartbeat_timestamp = datetime.datetime(2000, 1, 1, 0, 0) network = {'id': 'foo_network_id'} self._test__get_enabled_agents(network, agents=[agent1, agent2], expected_warnings=1, expected_errors=0) def test__get_enabled_agents_with_notification_required(self): network = {'id': 'foo_network_id', 'subnets': ['foo_subnet_id']} agent = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid()) agent.admin_state_up = False agent.heartbeat_timestamp = timeutils.utcnow() self._test__get_enabled_agents(network, [agent], port_count=20, expected_warnings=0, expected_errors=1) def test__get_enabled_agents_with_admin_state_down(self): cfg.CONF.set_override( 'enable_services_on_agents_with_admin_state_down', True) agent1 = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid()) agent1.admin_state_up = True agent1.heartbeat_timestamp = timeutils.utcnow() agent2 = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid()) agent2.admin_state_up = False agent2.heartbeat_timestamp = timeutils.utcnow() network = {'id': 'foo_network_id'} self._test__get_enabled_agents(network, agents=[agent1, agent2]) def test__notify_agents_allocate_priority(self): mock_context = mock.MagicMock() mock_context.is_admin = True methods = ['network_create_end', 'network_update_end', 'network_delete_end', 'subnet_create_end', 'subnet_update_end', 'subnet_delete_end', 'port_create_end', 'port_update_end', 'port_delete_end'] with mock.patch.object(self.notifier, '_schedule_network') as f: with mock.patch.object(self.notifier, '_get_enabled_agents') as g: for method in methods: f.return_value = [mock.MagicMock()] g.return_value = [mock.MagicMock()] payload = {} if method.startswith('port'): payload['port'] = \ {'device_id': constants.DEVICE_ID_RESERVED_DHCP_PORT} expected_payload = copy.deepcopy(payload) expected_payload['priority'] = \ dhcp_rpc_agent_api.METHOD_PRIORITY_MAP.get(method) self.notifier._notify_agents(mock_context, method, payload, 'fake_network_id') if method == 'network_delete_end': self.mock_fanout.assert_called_with(mock.ANY, method, expected_payload) elif method != 'network_create_end': if method == 'port_create_end': expected_payload['priority'] = \ dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH self.mock_cast.assert_called_with(mock.ANY, method, expected_payload, mock.ANY, mock.ANY) def test__notify_agents_fanout_required(self): self.notifier._notify_agents(mock.ANY, 'network_delete_end', {}, 'foo_network_id') self.assertEqual(1, self.mock_fanout.call_count) def _test__notify_agents_with_function( self, function, expected_scheduling=0, expected_casts=0): with mock.patch.object(self.notifier, '_schedule_network') as f: with mock.patch.object(self.notifier, '_get_enabled_agents') as g: agent = agent_obj.Agent(mock.ANY, id=uuidutils.generate_uuid(), host='host', topic='topic') agent.admin_state_up = True agent.heartbeat_timestamp = timeutils.utcnow() g.return_value = [agent] function() self.assertEqual(expected_scheduling, f.call_count) self.assertEqual(expected_casts, self.mock_cast.call_count) def _test__notify_agents(self, method, expected_scheduling=0, expected_casts=0, payload=None): payload = payload or {'port': {}} self._test__notify_agents_with_function( lambda: self.notifier._notify_agents( mock.Mock(), method, payload, 'foo_network_id'), expected_scheduling, expected_casts) def test__notify_agents_cast_required_with_scheduling(self): self._test__notify_agents('port_create_end', expected_scheduling=1, expected_casts=1) def test__notify_agents_cast_required_wo_scheduling_on_port_update(self): self._test__notify_agents('port_update_end', expected_scheduling=0, expected_casts=1) def test__notify_agents_cast_required_with_scheduling_subnet_create(self): self._test__notify_agents('subnet_create_end', expected_scheduling=1, expected_casts=1, payload={'subnet': {}}) def test__notify_agents_cast_required_with_scheduling_segment(self): network_id = 'foo_network_id' segment_id = 'foo_segment_id' subnet = {'subnet': {'segment_id': segment_id}} segment = {'id': segment_id, 'network_id': network_id, 'hosts': ['host-a']} self.notifier.plugin.get_network.return_value = {'id': network_id} segment_sp = mock.Mock() segment_sp.get_segment.return_value = segment directory.add_plugin('segments', segment_sp) self._test__notify_agents('subnet_create_end', expected_scheduling=1, expected_casts=1, payload=subnet) get_agents = self.notifier.plugin.get_dhcp_agents_hosting_networks get_agents.assert_called_once_with( mock.ANY, [network_id], hosts=segment['hosts']) def test__notify_agents_no_action(self): self._test__notify_agents('network_create_end', expected_scheduling=0, expected_casts=0) def test__notify_agents_with_router_interface_add(self): self._test__notify_agents_with_function( lambda: self.notifier._after_router_interface_created( mock.ANY, mock.ANY, mock.ANY, context=mock.Mock(), port={'id': 'foo_port_id', 'network_id': 'foo_network_id'}), expected_scheduling=1, expected_casts=1) def test__notify_agents_with_router_interface_delete(self): self._test__notify_agents_with_function( lambda: self.notifier._after_router_interface_deleted( mock.ANY, mock.ANY, mock.ANY, context=mock.Mock(), port={'id': 'foo_port_id', 'network_id': 'foo_network_id'}), expected_scheduling=0, expected_casts=1) def test__fanout_message(self): self.notifier._fanout_message(mock.ANY, mock.ANY, mock.ANY) self.assertEqual(1, self.mock_fanout.call_count) def test__cast_message(self): self.notifier._cast_message(mock.ANY, mock.ANY, mock.ANY) self.assertEqual(1, self.mock_cast.call_count) def test__native_notification_unsubscribes(self): self.assertFalse(self.notifier._unsubscribed_resources) for res in (resources.PORT, resources.NETWORK, resources.SUBNET): self.notifier._unsubscribed_resources = [] kwargs = {res: {}} registry.notify(res, events.AFTER_CREATE, self, context=mock.Mock(), **kwargs) # don't unsubscribe until all three types are observed self.assertEqual([], self.notifier._unsubscribed_resources) registry.notify(res, events.AFTER_UPDATE, self, context=mock.Mock(), **kwargs) self.assertEqual([], self.notifier._unsubscribed_resources) registry.notify(res, events.AFTER_DELETE, self, context=mock.Mock(), **kwargs) self.assertEqual([res], self.notifier._unsubscribed_resources) # after first time, no further unsubscribing should happen registry.notify(res, events.AFTER_CREATE, self, context=mock.Mock(), **kwargs) self.assertEqual([res], self.notifier._unsubscribed_resources) def test__only_status_changed(self): p1 = {'id': 1, 'status': 'DOWN', 'updated_at': '10:00:00', 'revision_number': 1} p2 = dict(p1) p2['status'] = 'ACTIVE' p2['revision_number'] = 2 p2['updated_at'] = '10:00:01' self.assertTrue(self.notifier._only_status_changed(p1, p2)) p2['name'] = 'test' self.assertFalse(self.notifier._only_status_changed(p1, p2)) p1['name'] = 'test' self.assertTrue(self.notifier._only_status_changed(p1, p2)) p1['name'] = 'test1' self.assertFalse(self.notifier._only_status_changed(p1, p2)) neutron-12.1.1/neutron/tests/unit/api/rpc/agentnotifiers/__init__.py0000664000175000017500000000000013553660046025610 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/handlers/0000775000175000017500000000000013553660156022272 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/handlers/test_dvr_rpc.py0000664000175000017500000000407713553660046025350 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron.api.rpc.handlers import dvr_rpc from neutron.tests import base class DVRServerRpcApiTestCase(base.BaseTestCase): def setUp(self): self.client_p = mock.patch.object(dvr_rpc.n_rpc, "get_client") self.client = self.client_p.start() self.rpc = dvr_rpc.DVRServerRpcApi('fake_topic') self.mock_cctxt = self.rpc.client.prepare.return_value self.ctxt = mock.ANY super(DVRServerRpcApiTestCase, self).setUp() def test_get_dvr_mac_address_by_host(self): self.rpc.get_dvr_mac_address_by_host(self.ctxt, 'foo_host') self.mock_cctxt.call.assert_called_with( self.ctxt, 'get_dvr_mac_address_by_host', host='foo_host') def test_get_dvr_mac_address_list(self): self.rpc.get_dvr_mac_address_list(self.ctxt) self.mock_cctxt.call.assert_called_with( self.ctxt, 'get_dvr_mac_address_list') def test_get_ports_on_host_by_subnet(self): self.rpc.get_ports_on_host_by_subnet( self.ctxt, 'foo_host', 'foo_subnet') self.mock_cctxt.call.assert_called_with( self.ctxt, 'get_ports_on_host_by_subnet', host='foo_host', subnet='foo_subnet') def test_get_subnet_for_dvr(self): self.rpc.get_subnet_for_dvr( self.ctxt, 'foo_subnet', fixed_ips='foo_fixed_ips') self.mock_cctxt.call.assert_called_with( self.ctxt, 'get_subnet_for_dvr', subnet='foo_subnet', fixed_ips='foo_fixed_ips') neutron-12.1.1/neutron/tests/unit/api/rpc/handlers/test_dhcp_rpc.py0000664000175000017500000004262213553660047025472 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import exceptions as n_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_db import exception as db_exc from oslo_messaging.rpc import dispatcher as rpc_dispatcher from neutron.api.rpc.handlers import dhcp_rpc from neutron.common import utils from neutron.db import provisioning_blocks from neutron.tests import base class TestDhcpRpcCallback(base.BaseTestCase): def setUp(self): super(TestDhcpRpcCallback, self).setUp() self.plugin = mock.MagicMock() directory.add_plugin(plugin_constants.CORE, self.plugin) self.callbacks = dhcp_rpc.DhcpRpcCallback() self.log_p = mock.patch('neutron.api.rpc.handlers.dhcp_rpc.LOG') self.log = self.log_p.start() set_dirty_p = mock.patch('neutron.quota.resource_registry.' 'set_resources_dirty') self.mock_set_dirty = set_dirty_p.start() self.utils_p = mock.patch('neutron.plugins.common.utils.create_port') self.utils = self.utils_p.start() self.agent_hosting_network_p = mock.patch.object(self.callbacks, '_is_dhcp_agent_hosting_network') self.mock_agent_hosting_network = self.agent_hosting_network_p.start() self.mock_agent_hosting_network.return_value = True self.segment_plugin = mock.MagicMock() directory.add_plugin('segments', self.segment_plugin) def test_group_by_network_id(self): port1 = {'network_id': 'a'} port2 = {'network_id': 'b'} port3 = {'network_id': 'a'} grouped_ports = self.callbacks._group_by_network_id( [port1, port2, port3]) expected = {'a': [port1, port3], 'b': [port2]} self.assertEqual(expected, grouped_ports) def test_get_active_networks_info(self): plugin_retval = [{'id': 'a'}, {'id': 'b'}] self.plugin.get_networks.return_value = plugin_retval port = {'network_id': 'a'} subnet = {'network_id': 'b', 'id': 'c'} self.plugin.get_ports.return_value = [port] self.plugin.get_subnets.return_value = [subnet] networks = self.callbacks.get_active_networks_info(mock.Mock(), host='host') expected = [{'id': 'a', 'non_local_subnets': [], 'subnets': [], 'ports': [port]}, {'id': 'b', 'non_local_subnets': [], 'subnets': [subnet], 'ports': []}] self.assertEqual(expected, networks) def test_get_active_networks_info_with_routed_networks(self): plugin_retval = [{'id': 'a'}, {'id': 'b'}] port = {'network_id': 'a'} subnets = [{'network_id': 'b', 'id': 'c', 'segment_id': '1'}, {'network_id': 'a', 'id': 'e'}, {'network_id': 'b', 'id': 'd', 'segment_id': '3'}] self.plugin.get_ports.return_value = [port] self.plugin.get_networks.return_value = plugin_retval hostseg_retval = ['1', '2'] self.segment_plugin.get_segments_by_hosts.return_value = hostseg_retval self.plugin.get_subnets.return_value = subnets networks = self.callbacks.get_active_networks_info(mock.Mock(), host='host') expected = [{'id': 'a', 'non_local_subnets': [], 'subnets': [subnets[1]], 'ports': [port]}, {'id': 'b', 'non_local_subnets': [subnets[2]], 'subnets': [subnets[0]], 'ports': []}] self.assertEqual(expected, networks) def _test_get_active_networks_info_enable_dhcp_filter(self, enable_dhcp_filter): plugin_retval = [{'id': 'a'}, {'id': 'b'}] self.plugin.get_networks.return_value = plugin_retval self.callbacks.get_active_networks_info(mock.Mock(), host='host', enable_dhcp_filter=enable_dhcp_filter) filters = {'network_id': ['a', 'b']} if enable_dhcp_filter: filters['enable_dhcp'] = [True] self.plugin.get_subnets.assert_called_once_with(mock.ANY, filters=filters) def test_get_active_networks_info_enable_dhcp_filter_false(self): self._test_get_active_networks_info_enable_dhcp_filter(False) def test_get_active_networks_info_enable_dhcp_filter_true(self): self._test_get_active_networks_info_enable_dhcp_filter(True) def _test__port_action_with_failures(self, exc=None, action=None): port = { 'network_id': 'foo_network_id', 'device_owner': constants.DEVICE_OWNER_DHCP, 'fixed_ips': [{'subnet_id': 'foo_subnet_id'}] } self.plugin.create_port.side_effect = exc self.utils.side_effect = exc self.assertIsNone(self.callbacks._port_action(self.plugin, mock.Mock(), {'port': port}, action)) def _test__port_action_good_action(self, action, port, expected_call): self.callbacks._port_action(self.plugin, mock.Mock(), port, action) if action == 'create_port': self.utils.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY) else: self.plugin.assert_has_calls([expected_call]) def test_port_action_create_port(self): self._test__port_action_good_action( 'create_port', mock.Mock(), mock.call.create_port(mock.ANY, mock.ANY)) def test_port_action_update_port(self): fake_port = {'id': 'foo_port_id', 'port': mock.Mock()} self._test__port_action_good_action( 'update_port', fake_port, mock.call.update_port(mock.ANY, 'foo_port_id', mock.ANY)) def test__port_action_bad_action(self): self.assertRaises( n_exc.Invalid, self._test__port_action_with_failures, exc=None, action='foo_action') def test_create_port_catch_network_not_found(self): self._test__port_action_with_failures( exc=n_exc.NetworkNotFound(net_id='foo_network_id'), action='create_port') def test_create_port_catch_subnet_not_found(self): self._test__port_action_with_failures( exc=n_exc.SubnetNotFound(subnet_id='foo_subnet_id'), action='create_port') def test_create_port_catch_db_reference_error(self): self._test__port_action_with_failures( exc=db_exc.DBReferenceError('a', 'b', 'c', 'd'), action='create_port') def test_create_port_catch_ip_generation_failure_reraise(self): self.assertRaises( n_exc.IpAddressGenerationFailure, self._test__port_action_with_failures, exc=n_exc.IpAddressGenerationFailure(net_id='foo_network_id'), action='create_port') def test_create_port_catch_and_handle_ip_generation_failure(self): self.plugin.get_subnet.side_effect = ( n_exc.SubnetNotFound(subnet_id='foo_subnet_id')) self._test__port_action_with_failures( exc=n_exc.IpAddressGenerationFailure(net_id='foo_network_id'), action='create_port') self._test__port_action_with_failures( exc=n_exc.InvalidInput(error_message='sorry'), action='create_port') def test_update_port_missing_port_on_get(self): self.plugin.get_port.side_effect = n_exc.PortNotFound(port_id='66') self.assertIsNone(self.callbacks.update_dhcp_port( context='ctx', host='host', port_id='66', port={'port': {'network_id': 'a'}})) def test_update_port_missing_port_on_update(self): self.plugin.get_port.return_value = { 'device_id': constants.DEVICE_ID_RESERVED_DHCP_PORT} self.plugin.update_port.side_effect = n_exc.PortNotFound(port_id='66') self.assertIsNone(self.callbacks.update_dhcp_port( context='ctx', host='host', port_id='66', port={'port': {'network_id': 'a'}})) def test_get_network_info_return_none_on_not_found(self): self.plugin.get_network.side_effect = n_exc.NetworkNotFound(net_id='a') retval = self.callbacks.get_network_info(mock.Mock(), network_id='a') self.assertIsNone(retval) def _test_get_network_info(self, segmented_network=False, routed_network=False): network_retval = dict(id='a') if not routed_network: subnet_retval = [dict(id='a'), dict(id='c'), dict(id='b')] else: subnet_retval = [dict(id='c', segment_id='1'), dict(id='b', segment_id='2'), dict(id='a', segment_id='1')] port_retval = mock.Mock() self.plugin.get_network.return_value = network_retval self.plugin.get_subnets.return_value = subnet_retval self.plugin.get_ports.return_value = port_retval if segmented_network: self.segment_plugin.get_segments.return_value = [dict(id='1'), dict(id='2')] self.segment_plugin.get_segments_by_hosts.return_value = ['1'] retval = self.callbacks.get_network_info(mock.Mock(), network_id='a') self.assertEqual(retval, network_retval) sorted_nonlocal_subnet_retval = [] if not routed_network: sorted_subnet_retval = [dict(id='a'), dict(id='b'), dict(id='c')] else: sorted_subnet_retval = [dict(id='a', segment_id='1'), dict(id='c', segment_id='1')] sorted_nonlocal_subnet_retval = [dict(id='b', segment_id='2')] self.assertEqual(retval['subnets'], sorted_subnet_retval) self.assertEqual(retval['non_local_subnets'], sorted_nonlocal_subnet_retval) self.assertEqual(retval['ports'], port_retval) def test_get_network_info(self): self._test_get_network_info() def test_get_network_info_with_routed_network(self): self._test_get_network_info(segmented_network=True, routed_network=True) def test_get_network_info_with_segmented_network_but_not_routed(self): self._test_get_network_info(segmented_network=True) def test_get_network_info_with_non_segmented_network(self): self._test_get_network_info() def test_update_dhcp_port_verify_port_action_port_dict(self): port = {'port': {'network_id': 'foo_network_id', 'device_owner': constants.DEVICE_OWNER_DHCP, 'fixed_ips': [{'subnet_id': 'foo_subnet_id'}]} } expected_port = {'port': {'network_id': 'foo_network_id', 'device_owner': constants.DEVICE_OWNER_DHCP, portbindings.HOST_ID: 'foo_host', 'fixed_ips': [{'subnet_id': 'foo_subnet_id'}] }, 'id': 'foo_port_id' } def _fake_port_action(plugin, context, port, action): self.assertEqual(expected_port, port) self.plugin.get_port.return_value = { 'device_id': constants.DEVICE_ID_RESERVED_DHCP_PORT} self.callbacks._port_action = _fake_port_action self.callbacks.update_dhcp_port(mock.Mock(), host='foo_host', port_id='foo_port_id', port=port) def test_update_reserved_dhcp_port(self): port = {'port': {'network_id': 'foo_network_id', 'device_owner': constants.DEVICE_OWNER_DHCP, 'fixed_ips': [{'subnet_id': 'foo_subnet_id'}]} } expected_port = {'port': {'network_id': 'foo_network_id', 'device_owner': constants.DEVICE_OWNER_DHCP, portbindings.HOST_ID: 'foo_host', 'fixed_ips': [{'subnet_id': 'foo_subnet_id'}] }, 'id': 'foo_port_id' } def _fake_port_action(plugin, context, port, action): self.assertEqual(expected_port, port) self.plugin.get_port.return_value = { 'device_id': utils.get_dhcp_agent_device_id('foo_network_id', 'foo_host')} self.callbacks._port_action = _fake_port_action self.callbacks.update_dhcp_port( mock.Mock(), host='foo_host', port_id='foo_port_id', port=port) self.plugin.get_port.return_value = { 'device_id': 'other_id'} res = self.callbacks.update_dhcp_port(mock.Mock(), host='foo_host', port_id='foo_port_id', port=port) self.assertIsNone(res) def test_update_dhcp_port(self): port = {'port': {'network_id': 'foo_network_id', 'device_owner': constants.DEVICE_OWNER_DHCP, 'fixed_ips': [{'subnet_id': 'foo_subnet_id'}]} } expected_port = {'port': {'network_id': 'foo_network_id', 'device_owner': constants.DEVICE_OWNER_DHCP, portbindings.HOST_ID: 'foo_host', 'fixed_ips': [{'subnet_id': 'foo_subnet_id'}] }, 'id': 'foo_port_id' } self.plugin.get_port.return_value = { 'device_id': constants.DEVICE_ID_RESERVED_DHCP_PORT} self.callbacks.update_dhcp_port(mock.Mock(), host='foo_host', port_id='foo_port_id', port=port) self.plugin.assert_has_calls([ mock.call.update_port(mock.ANY, 'foo_port_id', expected_port)]) def test_update_dhcp_port_with_agent_not_hosting_network(self): port = {'port': {'network_id': 'foo_network_id', 'device_owner': constants.DEVICE_OWNER_DHCP, 'fixed_ips': [{'subnet_id': 'foo_subnet_id'}]} } self.plugin.get_port.return_value = { 'device_id': constants.DEVICE_ID_RESERVED_DHCP_PORT} self.mock_agent_hosting_network.return_value = False self.assertRaises(rpc_dispatcher.ExpectedException, self.callbacks.update_dhcp_port, mock.Mock(), host='foo_host', port_id='foo_port_id', port=port) def test__is_dhcp_agent_hosting_network(self): self.agent_hosting_network_p.stop() agent = mock.Mock() with mock.patch.object(self.plugin, 'get_dhcp_agents_hosting_networks', return_value=[agent]): ret = self.callbacks._is_dhcp_agent_hosting_network(self.plugin, mock.Mock(), host='foo_host', network_id='foo_network_id') self.assertTrue(ret) def test__is_dhcp_agent_hosting_network_false(self): self.agent_hosting_network_p.stop() with mock.patch.object(self.plugin, 'get_dhcp_agents_hosting_networks', return_value=[]): ret = self.callbacks._is_dhcp_agent_hosting_network(self.plugin, mock.Mock(), host='foo_host', network_id='foo_network_id') self.assertFalse(ret) def test_release_dhcp_port(self): port_retval = dict(id='port_id', fixed_ips=[dict(subnet_id='a')]) self.plugin.get_ports.return_value = [port_retval] self.callbacks.release_dhcp_port(mock.ANY, network_id='netid', device_id='devid') self.plugin.assert_has_calls([ mock.call.delete_ports_by_device_id(mock.ANY, 'devid', 'netid')]) def test_dhcp_ready_on_ports(self): context = mock.Mock() port_ids = range(10) with mock.patch.object(provisioning_blocks, 'provisioning_complete') as pc: self.callbacks.dhcp_ready_on_ports(context, port_ids) calls = [mock.call(context, port_id, resources.PORT, provisioning_blocks.DHCP_ENTITY) for port_id in port_ids] pc.assert_has_calls(calls) neutron-12.1.1/neutron/tests/unit/api/rpc/handlers/test_l3_rpc.py0000664000175000017500000000554513553660047025075 0ustar zuulzuul00000000000000# Copyright (c) 2015 Cisco Systems # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import netaddr from neutron_lib import constants from neutron_lib import context from neutron_lib.plugins import directory from oslo_config import cfg from neutron.api.rpc.handlers import l3_rpc from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit import testlib_api class TestL3RpcCallback(testlib_api.SqlTestCase): def setUp(self): super(TestL3RpcCallback, self).setUp() self.setup_coreplugin(test_db_base_plugin_v2.DB_PLUGIN_KLASS) self.plugin = directory.get_plugin() self.ctx = context.get_admin_context() cfg.CONF.set_override('ipv6_pd_enabled', True) self.callbacks = l3_rpc.L3RpcCallback() self.network = self._prepare_network() def _prepare_network(self): network = {'network': {'name': 'abc', 'shared': False, 'tenant_id': 'tenant_id', 'admin_state_up': True}} return self.plugin.create_network(self.ctx, network) def _prepare_ipv6_pd_subnet(self): subnet = {'subnet': {'network_id': self.network['id'], 'tenant_id': 'tenant_id', 'cidr': None, 'ip_version': 6, 'use_default_subnetpool': True, 'name': 'ipv6_pd', 'enable_dhcp': True, 'host_routes': None, 'dns_nameservers': None, 'allocation_pools': None, 'ipv6_ra_mode': constants.IPV6_SLAAC, 'ipv6_address_mode': constants.IPV6_SLAAC}} return self.plugin.create_subnet(self.ctx, subnet) def test_process_prefix_update(self): subnet = self._prepare_ipv6_pd_subnet() data = {subnet['id']: netaddr.IPNetwork('2001:db8::/64')} allocation_pools = [{'start': '2001:db8::2', 'end': '2001:db8::ffff:ffff:ffff:ffff'}] res = self.callbacks.process_prefix_update(self.ctx, subnets=data) updated_subnet = res[0] self.assertEqual(str(data[subnet['id']]), updated_subnet['cidr']) self.assertEqual(updated_subnet['allocation_pools'], allocation_pools) neutron-12.1.1/neutron/tests/unit/api/rpc/handlers/__init__.py0000664000175000017500000000000013553660046024367 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/rpc/handlers/test_resources_rpc.py0000664000175000017500000003063513553660047026567 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mellanox Technologies, Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib import context from oslo_utils import uuidutils from oslo_versionedobjects import fields as obj_fields import testtools from neutron.api.rpc.callbacks import resources from neutron.api.rpc.callbacks import version_manager from neutron.api.rpc.handlers import resources_rpc from neutron.common import topics from neutron.objects import base as objects_base from neutron.objects import common_types from neutron.tests import base from neutron.tests.unit.objects import test_base as objects_test_base TEST_EVENT = 'test_event' TEST_VERSION = '1.0' def _create_test_dict(uuid=None): return {'id': uuid or uuidutils.generate_uuid(), 'field': 'foo'} def _create_test_resource(context=None, resource_cls=None): resource_cls = resource_cls or FakeResource resource_dict = _create_test_dict() resource = resource_cls(context, **resource_dict) resource.obj_reset_changes() return resource class BaseFakeResource(objects_base.NeutronObject): @classmethod def get_objects(cls, context, **kwargs): return list() class FakeResource(BaseFakeResource): VERSION = TEST_VERSION fields = { 'id': common_types.UUIDField(), 'field': obj_fields.StringField() } class FakeResource2(BaseFakeResource): VERSION = TEST_VERSION fields = { 'id': common_types.UUIDField(), 'field': obj_fields.StringField() } class ResourcesRpcBaseTestCase(base.BaseTestCase): def setUp(self): super(ResourcesRpcBaseTestCase, self).setUp() self.obj_registry = self.useFixture( objects_test_base.NeutronObjectRegistryFixture()) self.context = context.get_admin_context() mock.patch.object(resources_rpc.resources, 'is_valid_resource_type').start() mock.patch.object(resources_rpc.resources, 'get_resource_cls', side_effect=self._get_resource_cls).start() self.resource_objs = [_create_test_resource(self.context) for _ in range(2)] self.resource_objs2 = [_create_test_resource(self.context, FakeResource2) for _ in range(2)] @staticmethod def _get_resource_cls(resource_type): return {FakeResource.obj_name(): FakeResource, FakeResource2.obj_name(): FakeResource2}.get(resource_type) class _ValidateResourceTypeTestCase(base.BaseTestCase): def setUp(self): super(_ValidateResourceTypeTestCase, self).setUp() self.is_valid_mock = mock.patch.object( resources_rpc.resources, 'is_valid_resource_type').start() def test_valid_type(self): self.is_valid_mock.return_value = True resources_rpc._validate_resource_type('foo') def test_invalid_type(self): self.is_valid_mock.return_value = False with testtools.ExpectedException( resources_rpc.InvalidResourceTypeClass): resources_rpc._validate_resource_type('foo') class _ResourceTypeVersionedTopicTestCase(base.BaseTestCase): @mock.patch.object(resources_rpc, '_validate_resource_type') def test_resource_type_versioned_topic(self, validate_mock): obj_name = FakeResource.obj_name() expected = topics.RESOURCE_TOPIC_PATTERN % { 'resource_type': 'FakeResource', 'version': '1.0'} with mock.patch.object(resources_rpc.resources, 'get_resource_cls', return_value=FakeResource): observed = resources_rpc.resource_type_versioned_topic(obj_name) self.assertEqual(expected, observed) class ResourcesPullRpcApiTestCase(ResourcesRpcBaseTestCase): def setUp(self): super(ResourcesPullRpcApiTestCase, self).setUp() self.rpc = resources_rpc.ResourcesPullRpcApi() mock.patch.object(self.rpc, 'client').start() self.cctxt_mock = self.rpc.client.prepare.return_value def test_is_singleton(self): self.assertIs(self.rpc, resources_rpc.ResourcesPullRpcApi()) def test_pull(self): self.obj_registry.register(FakeResource) expected_obj = _create_test_resource(self.context) resource_id = expected_obj.id self.cctxt_mock.call.return_value = expected_obj.obj_to_primitive() result = self.rpc.pull( self.context, FakeResource.obj_name(), resource_id) self.cctxt_mock.call.assert_called_once_with( self.context, 'pull', resource_type='FakeResource', version=TEST_VERSION, resource_id=resource_id) self.assertEqual(expected_obj, result) def test_bulk_pull(self): self.obj_registry.register(FakeResource) expected_objs = [_create_test_resource(self.context), _create_test_resource(self.context)] self.cctxt_mock.call.return_value = [ e.obj_to_primitive() for e in expected_objs] filter_kwargs = {'a': 'b', 'c': 'd'} result = self.rpc.bulk_pull( self.context, FakeResource.obj_name(), filter_kwargs=filter_kwargs) self.cctxt_mock.call.assert_called_once_with( self.context, 'bulk_pull', resource_type='FakeResource', version=TEST_VERSION, filter_kwargs=filter_kwargs) self.assertEqual(expected_objs, result) def test_pull_resource_not_found(self): resource_dict = _create_test_dict() resource_id = resource_dict['id'] self.cctxt_mock.call.return_value = None with testtools.ExpectedException(resources_rpc.ResourceNotFound): self.rpc.pull(self.context, FakeResource.obj_name(), resource_id) class ResourcesPushToServerRpcCallbackTestCase(ResourcesRpcBaseTestCase): def test_report_versions(self): callbacks = resources_rpc.ResourcesPushToServerRpcCallback() with mock.patch('neutron.api.rpc.callbacks.version_manager' '.update_versions') as update_versions: version_map = {'A': '1.0'} callbacks.report_agent_resource_versions(context=mock.ANY, agent_type='DHCP Agent', agent_host='fake-host', version_map=version_map) update_versions.assert_called_once_with(mock.ANY, version_map) class ResourcesPullRpcCallbackTestCase(ResourcesRpcBaseTestCase): def setUp(self): super(ResourcesPullRpcCallbackTestCase, self).setUp() self.obj_registry.register(FakeResource) self.callbacks = resources_rpc.ResourcesPullRpcCallback() self.resource_obj = _create_test_resource(self.context) def test_pull(self): resource_dict = _create_test_dict(uuid=self.resource_obj.id) with mock.patch.object( resources_rpc.prod_registry, 'pull', return_value=self.resource_obj) as registry_mock: primitive = self.callbacks.pull( self.context, resource_type=FakeResource.obj_name(), version=TEST_VERSION, resource_id=self.resource_obj.id) registry_mock.assert_called_once_with( 'FakeResource', self.resource_obj.id, context=self.context) self.assertEqual(resource_dict, primitive['versioned_object.data']) self.assertEqual(self.resource_obj.obj_to_primitive(), primitive) def test_bulk_pull(self): r1 = self.resource_obj r2 = _create_test_resource(self.context) @classmethod def get_objs(*args, **kwargs): if 'id' not in kwargs: return [r1, r2] return [r for r in [r1, r2] if r.id == kwargs['id']] # the bulk interface currently retrieves directly from the registry with mock.patch.object(FakeResource, 'get_objects', new=get_objs): objs = self.callbacks.bulk_pull( self.context, resource_type=FakeResource.obj_name(), version=TEST_VERSION) self.assertItemsEqual([r1.obj_to_primitive(), r2.obj_to_primitive()], objs) objs = self.callbacks.bulk_pull( self.context, resource_type=FakeResource.obj_name(), version=TEST_VERSION, filter_kwargs={'id': r1.id}) self.assertEqual([r1.obj_to_primitive()], objs) @mock.patch.object(FakeResource, 'obj_to_primitive') def test_pull_backports_to_older_version(self, to_prim_mock): with mock.patch.object(resources_rpc.prod_registry, 'pull', return_value=self.resource_obj): self.callbacks.pull( self.context, resource_type=FakeResource.obj_name(), version='0.9', # less than initial version 1.0 resource_id=self.resource_obj.id) to_prim_mock.assert_called_with(target_version='0.9') class ResourcesPushRpcApiTestCase(ResourcesRpcBaseTestCase): """Tests the neutron server side of the RPC interface.""" def setUp(self): super(ResourcesPushRpcApiTestCase, self).setUp() mock.patch.object(resources_rpc.n_rpc, 'get_client').start() self.rpc = resources_rpc.ResourcesPushRpcApi() self.cctxt_mock = self.rpc.client.prepare.return_value mock.patch.object(version_manager, 'get_resource_versions', return_value=set([TEST_VERSION])).start() def test__prepare_object_fanout_context(self): expected_topic = topics.RESOURCE_TOPIC_PATTERN % { 'resource_type': resources.get_resource_type( self.resource_objs[0]), 'version': TEST_VERSION} observed = self.rpc._prepare_object_fanout_context( self.resource_objs[0], self.resource_objs[0].VERSION, '1.0') self.rpc.client.prepare.assert_called_once_with( fanout=True, topic=expected_topic, version='1.0') self.assertEqual(self.cctxt_mock, observed) def test_push_single_type(self): self.rpc.push( self.context, self.resource_objs, TEST_EVENT) self.cctxt_mock.cast.assert_called_once_with( self.context, 'push', resource_list=[resource.obj_to_primitive() for resource in self.resource_objs], event_type=TEST_EVENT) def test_push_mixed(self): self.rpc.push( self.context, self.resource_objs + self.resource_objs2, event_type=TEST_EVENT) self.cctxt_mock.cast.assert_any_call( self.context, 'push', resource_list=[resource.obj_to_primitive() for resource in self.resource_objs], event_type=TEST_EVENT) self.cctxt_mock.cast.assert_any_call( self.context, 'push', resource_list=[resource.obj_to_primitive() for resource in self.resource_objs2], event_type=TEST_EVENT) class ResourcesPushRpcCallbackTestCase(ResourcesRpcBaseTestCase): """Tests the agent-side of the RPC interface.""" def setUp(self): super(ResourcesPushRpcCallbackTestCase, self).setUp() self.callbacks = resources_rpc.ResourcesPushRpcCallback() @mock.patch.object(resources_rpc.cons_registry, 'push') def test_push(self, reg_push_mock): self.obj_registry.register(FakeResource) self.callbacks.push(self.context, resource_list=[resource.obj_to_primitive() for resource in self.resource_objs], event_type=TEST_EVENT) reg_push_mock.assert_called_once_with(self.context, self.resource_objs[0].obj_name(), self.resource_objs, TEST_EVENT) neutron-12.1.1/neutron/tests/unit/api/rpc/handlers/test_securitygroups_rpc.py0000664000175000017500000001562113553660047027662 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from oslo_utils import uuidutils from neutron.agent import resource_cache from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import securitygroups_rpc from neutron import objects from neutron.objects.port.extensions import port_security as psec from neutron.objects import ports from neutron.objects import securitygroup from neutron.tests import base class SecurityGroupServerRpcApiTestCase(base.BaseTestCase): def test_security_group_rules_for_devices(self): rpcapi = securitygroups_rpc.SecurityGroupServerRpcApi('fake_topic') with mock.patch.object(rpcapi.client, 'call') as rpc_mock,\ mock.patch.object(rpcapi.client, 'prepare') as prepare_mock: prepare_mock.return_value = rpcapi.client rpcapi.security_group_rules_for_devices('context', ['fake_device']) rpc_mock.assert_called_once_with( 'context', 'security_group_rules_for_devices', devices=['fake_device']) class SGAgentRpcCallBackMixinTestCase(base.BaseTestCase): def setUp(self): super(SGAgentRpcCallBackMixinTestCase, self).setUp() self.rpc = securitygroups_rpc.SecurityGroupAgentRpcCallbackMixin() self.rpc.sg_agent = mock.Mock() def test_security_groups_rule_updated(self): self.rpc.security_groups_rule_updated(None, security_groups=['fake_sgid']) self.rpc.sg_agent.assert_has_calls( [mock.call.security_groups_rule_updated(['fake_sgid'])]) def test_security_groups_member_updated(self): self.rpc.security_groups_member_updated(None, security_groups=['fake_sgid']) self.rpc.sg_agent.assert_has_calls( [mock.call.security_groups_member_updated(['fake_sgid'])]) class SecurityGroupServerAPIShimTestCase(base.BaseTestCase): def setUp(self): super(SecurityGroupServerAPIShimTestCase, self).setUp() objects.register_objects() resource_types = [resources.PORT, resources.SECURITYGROUP, resources.SECURITYGROUPRULE] self.rcache = resource_cache.RemoteResourceCache(resource_types) # prevent any server lookup attempts mock.patch.object(self.rcache, '_flood_cache_for_query').start() self.shim = securitygroups_rpc.SecurityGroupServerAPIShim(self.rcache) self.sg_agent = mock.Mock() self.shim.register_legacy_sg_notification_callbacks(self.sg_agent) self.ctx = context.get_admin_context() def _make_port_ovo(self, ip, **kwargs): attrs = {'id': uuidutils.generate_uuid(), 'network_id': uuidutils.generate_uuid(), 'security_group_ids': set(), 'device_owner': 'compute:None', 'allowed_address_pairs': []} attrs['fixed_ips'] = [ports.IPAllocation( port_id=attrs['id'], subnet_id=uuidutils.generate_uuid(), network_id=attrs['network_id'], ip_address=ip)] attrs.update(**kwargs) p = ports.Port(self.ctx, **attrs) self.rcache.record_resource_update(self.ctx, 'Port', p) return p def _make_security_group_ovo(self, **kwargs): attrs = {'id': uuidutils.generate_uuid(), 'revision_number': 1} sg_rule = securitygroup.SecurityGroupRule( id=uuidutils.generate_uuid(), security_group_id=attrs['id'], direction='ingress', ethertype='IPv4', protocol='tcp', port_range_min=400, remote_group_id=attrs['id'], revision_number=1, ) attrs['rules'] = [sg_rule] attrs.update(**kwargs) sg = securitygroup.SecurityGroup(self.ctx, **attrs) self.rcache.record_resource_update(self.ctx, 'SecurityGroup', sg) return sg def test_sg_parent_ops_affect_rules(self): s1 = self._make_security_group_ovo() filters = {'security_group_id': (s1.id, )} self.assertEqual( s1.rules, self.rcache.get_resources('SecurityGroupRule', filters)) self.sg_agent.security_groups_rule_updated.assert_called_once_with( [s1.id]) self.sg_agent.security_groups_rule_updated.reset_mock() self.rcache.record_resource_delete(self.ctx, 'SecurityGroup', s1.id) self.assertEqual( [], self.rcache.get_resources('SecurityGroupRule', filters)) self.sg_agent.security_groups_rule_updated.assert_called_once_with( [s1.id]) def test_security_group_info_for_devices(self): s1 = self._make_security_group_ovo() p1 = self._make_port_ovo(ip='1.1.1.1', security_group_ids={s1.id}) p2 = self._make_port_ovo( ip='2.2.2.2', security_group_ids={s1.id}, security=psec.PortSecurity(port_security_enabled=False)) p3 = self._make_port_ovo(ip='3.3.3.3', security_group_ids={s1.id}, device_owner='network:dhcp') ids = [p1.id, p2.id, p3.id] info = self.shim.security_group_info_for_devices(self.ctx, ids) self.assertIn('1.1.1.1', info['sg_member_ips'][s1.id]['IPv4']) self.assertIn('2.2.2.2', info['sg_member_ips'][s1.id]['IPv4']) self.assertIn('3.3.3.3', info['sg_member_ips'][s1.id]['IPv4']) self.assertIn(p1.id, info['devices'].keys()) self.assertIn(p2.id, info['devices'].keys()) # P3 is a trusted port so it doesn't have rules self.assertNotIn(p3.id, info['devices'].keys()) self.assertEqual([s1.id], list(info['security_groups'].keys())) self.assertTrue(info['devices'][p1.id]['port_security_enabled']) self.assertFalse(info['devices'][p2.id]['port_security_enabled']) def test_sg_member_update_events(self): s1 = self._make_security_group_ovo() p1 = self._make_port_ovo(ip='1.1.1.1', security_group_ids={s1.id}) self._make_port_ovo(ip='2.2.2.2', security_group_ids={s1.id}) self.sg_agent.security_groups_member_updated.assert_called_with( {s1.id}) self.sg_agent.security_groups_member_updated.reset_mock() self.rcache.record_resource_delete(self.ctx, 'Port', p1.id) self.sg_agent.security_groups_member_updated.assert_called_with( {s1.id}) neutron-12.1.1/neutron/tests/unit/api/test_versions.py0000664000175000017500000000175413553660047023175 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.api import versions from neutron.tests import base @mock.patch('neutron.api.versions.Versions.__init__', return_value=None) @mock.patch('neutron.pecan_wsgi.app.versions_factory') class TestVersions(base.BaseTestCase): def test_pecan_factory(self, pecan_mock, legacy_mock): versions.Versions.factory({}) pecan_mock.assert_called_once_with({}) legacy_mock.assert_not_called() neutron-12.1.1/neutron/tests/unit/api/__init__.py0000664000175000017500000000000013553660046022003 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/v2/0000775000175000017500000000000013553660156020235 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/v2/test_base.py0000664000175000017500000020300413553660047022556 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import mock from neutron_lib.api import converters from neutron_lib.callbacks import registry from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions as n_exc from neutron_lib import fixture from neutron_lib.plugins import directory from oslo_config import cfg from oslo_db import exception as db_exc from oslo_policy import policy as oslo_policy from oslo_utils import uuidutils import six import six.moves.urllib.parse as urlparse import webob from webob import exc import webtest from neutron.api import api_common from neutron.api import extensions from neutron.api.v2 import attributes from neutron.api.v2 import base as v2_base from neutron.api.v2 import router from neutron import policy from neutron import quota from neutron.quota import resource_registry from neutron.tests import base from neutron.tests import fake_notifier from neutron.tests.unit import dummy_plugin from neutron.tests.unit import testlib_api EXTDIR = os.path.join(base.ROOTDIR, 'unit/extensions') _uuid = uuidutils.generate_uuid def _get_path(resource, id=None, action=None, fmt=None, endpoint=None): path = '/%s' % resource if id is not None: path = path + '/%s' % id if action is not None: path = path + '/%s' % action if endpoint is not None: path = path + '/%s' % endpoint if fmt is not None: path = path + '.%s' % fmt return path class APIv2TestBase(base.BaseTestCase): def setUp(self): super(APIv2TestBase, self).setUp() plugin = 'neutron.neutron_plugin_base_v2.NeutronPluginBaseV2' # Ensure existing ExtensionManager is not used extensions.PluginAwareExtensionManager._instance = None # Create the default configurations self.config_parse() # Update the plugin self.setup_coreplugin(plugin, load_plugins=False) self._plugin_patcher = mock.patch(plugin, autospec=True) self.plugin = self._plugin_patcher.start() instance = self.plugin.return_value instance._NeutronPluginBaseV2__native_pagination_support = True instance._NeutronPluginBaseV2__native_sorting_support = True api = router.APIRouter() self.api = webtest.TestApp(api) quota.QUOTAS._driver = None cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', group='QUOTAS') # APIRouter initialization resets policy module, re-initializing it policy.init() class _ArgMatcher(object): """An adapter to assist mock assertions, used to custom compare.""" def __init__(self, cmp, obj): self.cmp = cmp self.obj = obj def __eq__(self, other): return self.cmp(self.obj, other) def _list_cmp(l1, l2): return set(l1) == set(l2) class APIv2TestCase(APIv2TestBase): @staticmethod def _get_policy_attrs(attr_info): policy_attrs = {name for (name, info) in attr_info.items() if info.get('required_by_policy')} if 'tenant_id' in policy_attrs: policy_attrs.add('project_id') return sorted(policy_attrs) def _do_field_list(self, resource, base_fields): attr_info = attributes.RESOURCE_ATTRIBUTE_MAP[resource] policy_attrs = self._get_policy_attrs(attr_info) for name, info in attr_info.items(): if info.get('primary_key'): policy_attrs.append(name) fields = base_fields fields.extend(policy_attrs) return fields def _get_collection_kwargs(self, skipargs=None, **kwargs): skipargs = skipargs or [] args_list = ['filters', 'fields', 'sorts', 'limit', 'marker', 'page_reverse'] args_dict = dict( (arg, mock.ANY) for arg in set(args_list) - set(skipargs)) args_dict.update(kwargs) return args_dict def test_fields(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'fields': 'foo'}) fields = self._do_field_list('networks', ['foo']) kwargs = self._get_collection_kwargs(fields=fields) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_fields_multiple(self): instance = self.plugin.return_value instance.get_networks.return_value = [] fields = self._do_field_list('networks', ['bar', 'foo']) self.api.get(_get_path('networks'), {'fields': ['foo', 'bar']}) kwargs = self._get_collection_kwargs(fields=fields) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_fields_multiple_with_empty(self): instance = self.plugin.return_value instance.get_networks.return_value = [] fields = self._do_field_list('networks', ['foo']) self.api.get(_get_path('networks'), {'fields': ['foo', '']}) kwargs = self._get_collection_kwargs(fields=fields) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_fields_empty(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'fields': ''}) kwargs = self._get_collection_kwargs(fields=[]) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_fields_multiple_empty(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'fields': ['', '']}) kwargs = self._get_collection_kwargs(fields=[]) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_filters(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'name': 'bar'}) filters = {'name': ['bar']} kwargs = self._get_collection_kwargs(filters=filters) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_filters_empty(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'name': ''}) filters = {} kwargs = self._get_collection_kwargs(filters=filters) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_filters_multiple_empty(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'name': ['', '']}) filters = {} kwargs = self._get_collection_kwargs(filters=filters) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_filters_multiple_with_empty(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'name': ['bar', '']}) filters = {'name': ['bar']} kwargs = self._get_collection_kwargs(filters=filters) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_filters_multiple_values(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'name': ['bar', 'bar2']}) filters = {'name': ['bar', 'bar2']} kwargs = self._get_collection_kwargs(filters=filters) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_filters_multiple(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'name': 'bar', 'tenant_id': 'bar2'}) filters = {'name': ['bar'], 'tenant_id': ['bar2']} kwargs = self._get_collection_kwargs(filters=filters) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_filters_with_fields(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'name': 'bar', 'fields': 'foo'}) filters = {'name': ['bar']} fields = self._do_field_list('networks', ['foo']) kwargs = self._get_collection_kwargs(filters=filters, fields=fields) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_filters_with_convert_to(self): instance = self.plugin.return_value instance.get_ports.return_value = [] self.api.get(_get_path('ports'), {'admin_state_up': 'true'}) filters = {'admin_state_up': [True]} kwargs = self._get_collection_kwargs(filters=filters) instance.get_ports.assert_called_once_with(mock.ANY, **kwargs) def test_filters_with_convert_list_to(self): instance = self.plugin.return_value instance.get_ports.return_value = [] self.api.get(_get_path('ports'), {'fixed_ips': ['ip_address=foo', 'subnet_id=bar']}) filters = {'fixed_ips': {'ip_address': ['foo'], 'subnet_id': ['bar']}} kwargs = self._get_collection_kwargs(filters=filters) instance.get_ports.assert_called_once_with(mock.ANY, **kwargs) def test_limit(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'limit': '10'}) kwargs = self._get_collection_kwargs(limit=10) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_limit_with_great_than_max_limit(self): cfg.CONF.set_default('pagination_max_limit', '1000') instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'limit': '1001'}) kwargs = self._get_collection_kwargs(limit=1000) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_limit_with_zero(self): cfg.CONF.set_default('pagination_max_limit', '1000') instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'limit': '0'}) kwargs = self._get_collection_kwargs(limit=1000) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_limit_with_unspecific(self): cfg.CONF.set_default('pagination_max_limit', '1000') instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks')) kwargs = self._get_collection_kwargs(limit=1000) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_limit_with_negative_value(self): cfg.CONF.set_default('pagination_max_limit', '1000') instance = self.plugin.return_value instance.get_networks.return_value = [] res = self.api.get(_get_path('networks'), {'limit': -1}, expect_errors=True) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_limit_with_non_integer(self): instance = self.plugin.return_value instance.get_networks.return_value = [] res = self.api.get(_get_path('networks'), {'limit': 'abc'}, expect_errors=True) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) self.assertIn('abc', res) def test_limit_with_infinite_pagination_max_limit(self): instance = self.plugin.return_value instance.get_networks.return_value = [] cfg.CONF.set_override('pagination_max_limit', 'Infinite') self.api.get(_get_path('networks')) kwargs = self._get_collection_kwargs(limit=None) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_limit_with_negative_pagination_max_limit(self): instance = self.plugin.return_value instance.get_networks.return_value = [] cfg.CONF.set_default('pagination_max_limit', '-1') self.api.get(_get_path('networks')) kwargs = self._get_collection_kwargs(limit=None) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_limit_with_non_integer_pagination_max_limit(self): instance = self.plugin.return_value instance.get_networks.return_value = [] cfg.CONF.set_default('pagination_max_limit', 'abc') self.api.get(_get_path('networks')) kwargs = self._get_collection_kwargs(limit=None) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_marker(self): cfg.CONF.set_override('pagination_max_limit', '1000') instance = self.plugin.return_value instance.get_networks.return_value = [] marker = _uuid() self.api.get(_get_path('networks'), {'marker': marker}) kwargs = self._get_collection_kwargs(limit=1000, marker=marker) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_page_reverse(self): calls = [] instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'page_reverse': 'True'}) kwargs = self._get_collection_kwargs(page_reverse=True) calls.append(mock.call.get_networks(mock.ANY, **kwargs)) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) instance.get_networks.reset_mock() self.api.get(_get_path('networks'), {'page_reverse': 'False'}) kwargs = self._get_collection_kwargs(page_reverse=False) calls.append(mock.call.get_networks(mock.ANY, **kwargs)) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_page_reverse_with_non_bool(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'page_reverse': 'abc'}) kwargs = self._get_collection_kwargs(page_reverse=False) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_page_reverse_with_unspecific(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks')) kwargs = self._get_collection_kwargs(page_reverse=False) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_sort(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'sort_key': ['name', 'admin_state_up'], 'sort_dir': ['desc', 'asc']}) kwargs = self._get_collection_kwargs(sorts=[('name', False), ('admin_state_up', True), ('id', True)]) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_sort_with_primary_key(self): instance = self.plugin.return_value instance.get_networks.return_value = [] self.api.get(_get_path('networks'), {'sort_key': ['name', 'admin_state_up', 'id'], 'sort_dir': ['desc', 'asc', 'desc']}) kwargs = self._get_collection_kwargs(sorts=[('name', False), ('admin_state_up', True), ('id', False)]) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_sort_without_direction(self): instance = self.plugin.return_value instance.get_networks.return_value = [] res = self.api.get(_get_path('networks'), {'sort_key': ['name']}, expect_errors=True) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_sort_with_invalid_attribute(self): instance = self.plugin.return_value instance.get_networks.return_value = [] res = self.api.get(_get_path('networks'), {'sort_key': 'abc', 'sort_dir': 'asc'}, expect_errors=True) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_sort_with_invalid_dirs(self): instance = self.plugin.return_value instance.get_networks.return_value = [] res = self.api.get(_get_path('networks'), {'sort_key': 'name', 'sort_dir': 'abc'}, expect_errors=True) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_emulated_sort(self): instance = self.plugin.return_value instance._NeutronPluginBaseV2__native_pagination_support = False instance._NeutronPluginBaseV2__native_sorting_support = False instance.get_networks.return_value = [] api = webtest.TestApp(router.APIRouter()) api.get(_get_path('networks'), {'sort_key': ['name', 'status'], 'sort_dir': ['desc', 'asc']}) kwargs = self._get_collection_kwargs( skipargs=['sorts', 'limit', 'marker', 'page_reverse']) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_emulated_sort_without_sort_field(self): instance = self.plugin.return_value instance._NeutronPluginBaseV2__native_pagination_support = False instance._NeutronPluginBaseV2__native_sorting_support = False instance.get_networks.return_value = [] api = webtest.TestApp(router.APIRouter()) api.get(_get_path('networks'), {'sort_key': ['name', 'status'], 'sort_dir': ['desc', 'asc'], 'fields': ['subnets']}) kwargs = self._get_collection_kwargs( skipargs=['sorts', 'limit', 'marker', 'page_reverse'], fields=_ArgMatcher(_list_cmp, ['name', 'status', 'id', 'subnets', 'shared', 'project_id', 'tenant_id'])) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_emulated_pagination(self): instance = self.plugin.return_value instance._NeutronPluginBaseV2__native_pagination_support = False instance.get_networks.return_value = [] api = webtest.TestApp(router.APIRouter()) api.get(_get_path('networks'), {'limit': 10, 'marker': 'foo', 'page_reverse': False}) kwargs = self._get_collection_kwargs(skipargs=['limit', 'marker', 'page_reverse']) instance.get_networks.assert_called_once_with(mock.ANY, **kwargs) def test_native_pagination_without_native_sorting(self): instance = self.plugin.return_value instance._NeutronPluginBaseV2__native_sorting_support = False self.assertRaises(n_exc.Invalid, router.APIRouter) # Note: since all resources use the same controller and validation # logic, we actually get really good coverage from testing just networks. class JSONV2TestCase(APIv2TestBase, testlib_api.WebTestCase): def _test_list(self, req_tenant_id, real_tenant_id): env = {} if req_tenant_id: env = {'neutron.context': context.Context('', req_tenant_id)} input_dict = {'id': uuidutils.generate_uuid(), 'name': 'net1', 'admin_state_up': True, 'status': "ACTIVE", 'tenant_id': real_tenant_id, 'shared': False, 'subnets': []} return_value = [input_dict] instance = self.plugin.return_value instance.get_networks.return_value = return_value res = self.api.get(_get_path('networks', fmt=self.fmt), extra_environ=env) res = self.deserialize(res) self.assertIn('networks', res) if not req_tenant_id or req_tenant_id == real_tenant_id: # expect full list returned self.assertEqual(1, len(res['networks'])) output_dict = res['networks'][0] input_dict['shared'] = False self.assertEqual(len(input_dict), len(output_dict)) for k, v in input_dict.items(): self.assertEqual(v, output_dict[k]) else: # expect no results self.assertEqual(0, len(res['networks'])) def test_list_noauth(self): self._test_list(None, _uuid()) def test_list_keystone(self): tenant_id = _uuid() self._test_list(tenant_id, tenant_id) def test_list_keystone_bad(self): tenant_id = _uuid() self._test_list(tenant_id + "bad", tenant_id) def test_list_pagination(self): id1 = str(_uuid()) id2 = str(_uuid()) input_dict1 = {'id': id1, 'name': 'net1', 'admin_state_up': True, 'status': "ACTIVE", 'tenant_id': '', 'shared': False, 'subnets': []} input_dict2 = {'id': id2, 'name': 'net2', 'admin_state_up': True, 'status': "ACTIVE", 'tenant_id': '', 'shared': False, 'subnets': []} return_value = [input_dict1, input_dict2] instance = self.plugin.return_value instance.get_networks.return_value = return_value params = {'limit': ['2'], 'marker': [str(_uuid())], 'sort_key': ['name'], 'sort_dir': ['asc']} res = self.api.get(_get_path('networks'), params=params).json self.assertEqual(2, len(res['networks'])) self.assertEqual(sorted([id1, id2]), sorted([res['networks'][0]['id'], res['networks'][1]['id']])) self.assertIn('networks_links', res) next_links = [] previous_links = [] for r in res['networks_links']: if r['rel'] == 'next': next_links.append(r) if r['rel'] == 'previous': previous_links.append(r) self.assertEqual(1, len(next_links)) self.assertEqual(1, len(previous_links)) url = urlparse.urlparse(next_links[0]['href']) self.assertEqual(url.path, _get_path('networks')) params['marker'] = [id2] self.assertEqual(params, urlparse.parse_qs(url.query)) url = urlparse.urlparse(previous_links[0]['href']) self.assertEqual(url.path, _get_path('networks')) params['marker'] = [id1] params['page_reverse'] = ['True'] self.assertEqual(params, urlparse.parse_qs(url.query)) def test_list_pagination_with_last_page(self): id = str(_uuid()) input_dict = {'id': id, 'name': 'net1', 'admin_state_up': True, 'status': "ACTIVE", 'tenant_id': '', 'shared': False, 'subnets': []} return_value = [input_dict] instance = self.plugin.return_value instance.get_networks.return_value = return_value params = {'limit': ['2'], 'marker': str(_uuid())} res = self.api.get(_get_path('networks'), params=params).json self.assertEqual(1, len(res['networks'])) self.assertEqual(id, res['networks'][0]['id']) self.assertIn('networks_links', res) previous_links = [] for r in res['networks_links']: self.assertNotEqual(r['rel'], 'next') if r['rel'] == 'previous': previous_links.append(r) self.assertEqual(1, len(previous_links)) url = urlparse.urlparse(previous_links[0]['href']) self.assertEqual(url.path, _get_path('networks')) expect_params = params.copy() expect_params['marker'] = [id] expect_params['page_reverse'] = ['True'] self.assertEqual(expect_params, urlparse.parse_qs(url.query)) def test_list_pagination_with_empty_page(self): return_value = [] instance = self.plugin.return_value instance.get_networks.return_value = return_value params = {'limit': ['2'], 'marker': str(_uuid())} res = self.api.get(_get_path('networks'), params=params).json self.assertEqual([], res['networks']) previous_links = [] if 'networks_links' in res: for r in res['networks_links']: self.assertNotEqual(r['rel'], 'next') if r['rel'] == 'previous': previous_links.append(r) self.assertEqual(1, len(previous_links)) url = urlparse.urlparse(previous_links[0]['href']) self.assertEqual(url.path, _get_path('networks')) expect_params = params.copy() del expect_params['marker'] expect_params['page_reverse'] = ['True'] self.assertEqual(expect_params, urlparse.parse_qs(url.query)) def test_list_pagination_reverse_with_last_page(self): id = str(_uuid()) input_dict = {'id': id, 'name': 'net1', 'admin_state_up': True, 'status': "ACTIVE", 'tenant_id': '', 'shared': False, 'subnets': []} return_value = [input_dict] instance = self.plugin.return_value instance.get_networks.return_value = return_value params = {'limit': ['2'], 'marker': [str(_uuid())], 'page_reverse': ['True']} res = self.api.get(_get_path('networks'), params=params).json self.assertEqual(len(res['networks']), 1) self.assertEqual(id, res['networks'][0]['id']) self.assertIn('networks_links', res) next_links = [] for r in res['networks_links']: self.assertNotEqual(r['rel'], 'previous') if r['rel'] == 'next': next_links.append(r) self.assertEqual(1, len(next_links)) url = urlparse.urlparse(next_links[0]['href']) self.assertEqual(url.path, _get_path('networks')) expected_params = params.copy() del expected_params['page_reverse'] expected_params['marker'] = [id] self.assertEqual(expected_params, urlparse.parse_qs(url.query)) def test_list_pagination_reverse_with_empty_page(self): return_value = [] instance = self.plugin.return_value instance.get_networks.return_value = return_value params = {'limit': ['2'], 'marker': [str(_uuid())], 'page_reverse': ['True']} res = self.api.get(_get_path('networks'), params=params).json self.assertEqual([], res['networks']) next_links = [] if 'networks_links' in res: for r in res['networks_links']: self.assertNotEqual(r['rel'], 'previous') if r['rel'] == 'next': next_links.append(r) self.assertEqual(1, len(next_links)) url = urlparse.urlparse(next_links[0]['href']) self.assertEqual(url.path, _get_path('networks')) expect_params = params.copy() del expect_params['marker'] del expect_params['page_reverse'] self.assertEqual(expect_params, urlparse.parse_qs(url.query)) def test_create(self): net_id = _uuid() data = {'network': {'name': 'net1', 'admin_state_up': True, 'tenant_id': _uuid()}} return_value = {'subnets': [], 'status': "ACTIVE", 'id': net_id} return_value.update(data['network'].copy()) instance = self.plugin.return_value instance.create_network.return_value = return_value instance.get_networks_count.return_value = 0 res = self.api.post(_get_path('networks', fmt=self.fmt), self.serialize(data), content_type='application/' + self.fmt) self.assertEqual(exc.HTTPCreated.code, res.status_int) res = self.deserialize(res) self.assertIn('network', res) net = res['network'] self.assertEqual(net_id, net['id']) self.assertEqual("ACTIVE", net['status']) def test_create_use_defaults(self): net_id = _uuid() tenant_id = _uuid() initial_input = {'network': {'name': 'net1', 'tenant_id': tenant_id, 'project_id': tenant_id}} full_input = {'network': {'admin_state_up': True, 'shared': False}} full_input['network'].update(initial_input['network']) return_value = {'id': net_id, 'status': "ACTIVE"} return_value.update(full_input['network']) instance = self.plugin.return_value instance.create_network.return_value = return_value instance.get_networks_count.return_value = 0 res = self.api.post(_get_path('networks', fmt=self.fmt), self.serialize(initial_input), content_type='application/' + self.fmt) instance.create_network.assert_called_with(mock.ANY, network=full_input) self.assertEqual(exc.HTTPCreated.code, res.status_int) res = self.deserialize(res) self.assertIn('network', res) net = res['network'] self.assertEqual(net_id, net['id']) self.assertTrue(net['admin_state_up']) self.assertEqual("ACTIVE", net['status']) def test_create_no_keystone_env(self): data = {'name': 'net1'} self._test_create_failure_bad_request('networks', data) def test_create_with_keystone_env(self): tenant_id = _uuid() net_id = _uuid() env = {'neutron.context': context.Context('', tenant_id)} # tenant_id should be fetched from env initial_input = {'network': {'name': 'net1'}} full_input = {'network': {'admin_state_up': True, 'shared': False, 'tenant_id': tenant_id, 'project_id': tenant_id}} full_input['network'].update(initial_input['network']) return_value = {'id': net_id, 'status': "ACTIVE"} return_value.update(full_input['network']) instance = self.plugin.return_value instance.create_network.return_value = return_value instance.get_networks_count.return_value = 0 res = self.api.post(_get_path('networks', fmt=self.fmt), self.serialize(initial_input), content_type='application/' + self.fmt, extra_environ=env) instance.create_network.assert_called_with(mock.ANY, network=full_input) self.assertEqual(exc.HTTPCreated.code, res.status_int) def test_create_bad_keystone_tenant(self): tenant_id = _uuid() data = {'network': {'name': 'net1', 'tenant_id': tenant_id}} env = {'neutron.context': context.Context('', tenant_id + "bad")} self._test_create_failure_bad_request('networks', data, extra_environ=env) def test_create_no_body(self): data = {'whoa': None} self._test_create_failure_bad_request('networks', data) def test_create_body_string_not_json(self): data = 'a string' self._test_create_failure_bad_request('networks', data) def test_create_body_boolean_not_json(self): data = True self._test_create_failure_bad_request('networks', data) def test_create_no_resource(self): data = {} self._test_create_failure_bad_request('networks', data) def test_create_missing_attr(self): data = {'port': {'what': 'who', 'tenant_id': _uuid()}} self._test_create_failure_bad_request('ports', data) def test_create_readonly_attr(self): data = {'network': {'name': 'net1', 'tenant_id': _uuid(), 'status': "ACTIVE"}} self._test_create_failure_bad_request('networks', data) def test_create_with_too_long_name(self): data = {'network': {'name': "12345678" * 32, 'admin_state_up': True, 'tenant_id': _uuid()}} res = self.api.post(_get_path('networks', fmt=self.fmt), self.serialize(data), content_type='application/' + self.fmt, expect_errors=True) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_create_bulk(self): data = {'networks': [{'name': 'net1', 'admin_state_up': True, 'tenant_id': _uuid()}, {'name': 'net2', 'admin_state_up': True, 'tenant_id': _uuid()}]} def side_effect(context, network): net = network.copy() net['network'].update({'subnets': []}) return net['network'] instance = self.plugin.return_value instance.create_network.side_effect = side_effect instance.get_networks_count.return_value = 0 res = self.api.post(_get_path('networks', fmt=self.fmt), self.serialize(data), content_type='application/' + self.fmt) self.assertEqual(exc.HTTPCreated.code, res.status_int) def _test_create_failure_bad_request(self, resource, data, **kwargs): res = self.api.post(_get_path(resource, fmt=self.fmt), self.serialize(data), content_type='application/' + self.fmt, expect_errors=True, **kwargs) self.assertEqual(exc.HTTPBadRequest.code, res.status_int) def test_create_bulk_networks_none(self): self._test_create_failure_bad_request('networks', {'networks': None}) def test_create_bulk_networks_empty_list(self): self._test_create_failure_bad_request('networks', {'networks': []}) def test_create_bulk_missing_attr(self): data = {'ports': [{'what': 'who', 'tenant_id': _uuid()}]} self._test_create_failure_bad_request('ports', data) def test_create_bulk_partial_body(self): data = {'ports': [{'device_id': 'device_1', 'tenant_id': _uuid()}, {'tenant_id': _uuid()}]} self._test_create_failure_bad_request('ports', data) def test_create_attr_not_specified(self): net_id = _uuid() tenant_id = _uuid() device_id = _uuid() initial_input = {'port': {'name': '', 'network_id': net_id, 'tenant_id': tenant_id, 'project_id': tenant_id, 'device_id': device_id, 'admin_state_up': True}} full_input = {'port': {'admin_state_up': True, 'mac_address': constants.ATTR_NOT_SPECIFIED, 'fixed_ips': constants.ATTR_NOT_SPECIFIED, 'device_owner': ''}} full_input['port'].update(initial_input['port']) return_value = {'id': _uuid(), 'status': 'ACTIVE', 'admin_state_up': True, 'mac_address': 'ca:fe:de:ad:be:ef', 'device_id': device_id, 'device_owner': ''} return_value.update(initial_input['port']) instance = self.plugin.return_value instance.get_network.return_value = { 'tenant_id': six.text_type(tenant_id) } instance.get_ports_count.return_value = 1 instance.create_port.return_value = return_value res = self.api.post(_get_path('ports', fmt=self.fmt), self.serialize(initial_input), content_type='application/' + self.fmt) instance.create_port.assert_called_with(mock.ANY, port=full_input) self.assertEqual(exc.HTTPCreated.code, res.status_int) res = self.deserialize(res) self.assertIn('port', res) port = res['port'] self.assertEqual(net_id, port['network_id']) self.assertEqual('ca:fe:de:ad:be:ef', port['mac_address']) def test_create_return_extra_attr(self): net_id = _uuid() data = {'network': {'name': 'net1', 'admin_state_up': True, 'tenant_id': _uuid()}} return_value = {'subnets': [], 'status': "ACTIVE", 'id': net_id, 'v2attrs:something': "123"} return_value.update(data['network'].copy()) instance = self.plugin.return_value instance.create_network.return_value = return_value instance.get_networks_count.return_value = 0 res = self.api.post(_get_path('networks', fmt=self.fmt), self.serialize(data), content_type='application/' + self.fmt) self.assertEqual(exc.HTTPCreated.code, res.status_int) res = self.deserialize(res) self.assertIn('network', res) net = res['network'] self.assertEqual(net_id, net['id']) self.assertEqual("ACTIVE", net['status']) self.assertNotIn('v2attrs:something', net) def test_fields(self): return_value = {'name': 'net1', 'admin_state_up': True, 'subnets': []} instance = self.plugin.return_value instance.get_network.return_value = return_value self.api.get(_get_path('networks', id=uuidutils.generate_uuid(), fmt=self.fmt)) def _test_delete(self, req_tenant_id, real_tenant_id, expected_code, expect_errors=False): env = {} if req_tenant_id: env = {'neutron.context': context.Context('', req_tenant_id)} instance = self.plugin.return_value instance.get_network.return_value = {'tenant_id': real_tenant_id, 'shared': False} instance.delete_network.return_value = None res = self.api.delete(_get_path('networks', id=uuidutils.generate_uuid(), fmt=self.fmt), extra_environ=env, expect_errors=expect_errors) self.assertEqual(expected_code, res.status_int) def test_delete_noauth(self): self._test_delete(None, _uuid(), exc.HTTPNoContent.code) def test_delete_keystone(self): tenant_id = _uuid() self._test_delete(tenant_id, tenant_id, exc.HTTPNoContent.code) def test_delete_keystone_bad_tenant(self): tenant_id = _uuid() self._test_delete(tenant_id + "bad", tenant_id, exc.HTTPNotFound.code, expect_errors=True) def _test_get(self, req_tenant_id, real_tenant_id, expected_code, expect_errors=False): env = {} shared = False if req_tenant_id: env = {'neutron.context': context.Context('', req_tenant_id)} if req_tenant_id.endswith('another'): shared = True env['neutron.context'].roles = ['tenant_admin'] data = {'tenant_id': real_tenant_id, 'shared': shared} instance = self.plugin.return_value instance.get_network.return_value = data res = self.api.get(_get_path('networks', id=uuidutils.generate_uuid(), fmt=self.fmt), extra_environ=env, expect_errors=expect_errors) self.assertEqual(expected_code, res.status_int) return res def test_get_noauth(self): self._test_get(None, _uuid(), 200) def test_get_keystone(self): tenant_id = _uuid() self._test_get(tenant_id, tenant_id, 200) def test_get_keystone_bad_tenant(self): tenant_id = _uuid() self._test_get(tenant_id + "bad", tenant_id, exc.HTTPNotFound.code, expect_errors=True) def test_get_keystone_shared_network(self): tenant_id = _uuid() self._test_get(tenant_id + "another", tenant_id, 200) def test_get_keystone_strip_admin_only_attribute(self): tenant_id = _uuid() # Inject rule in policy engine rules = oslo_policy.Rules.from_dict( {'get_network:name': "rule:admin_only"}) policy.set_rules(rules, overwrite=False) res = self._test_get(tenant_id, tenant_id, 200) res = self.deserialize(res) self.assertNotIn('name', res['network']) def _test_update(self, req_tenant_id, real_tenant_id, expected_code, expect_errors=False): env = {} if req_tenant_id: env = {'neutron.context': context.Context('', req_tenant_id)} # leave out 'name' field intentionally data = {'network': {'admin_state_up': True}} return_value = {'subnets': []} return_value.update(data['network'].copy()) instance = self.plugin.return_value instance.get_network.return_value = {'tenant_id': real_tenant_id, 'shared': False} instance.update_network.return_value = return_value res = self.api.put(_get_path('networks', id=uuidutils.generate_uuid(), fmt=self.fmt), self.serialize(data), extra_environ=env, expect_errors=expect_errors) # Ensure id attribute is included in fields returned by GET call # in update procedure. self.assertEqual(1, instance.get_network.call_count) self.assertIn('id', instance.get_network.call_args[1]['fields']) self.assertEqual(res.status_int, expected_code) def test_update_noauth(self): self._test_update(None, _uuid(), 200) def test_update_keystone(self): tenant_id = _uuid() self._test_update(tenant_id, tenant_id, 200) def test_update_keystone_bad_tenant(self): tenant_id = _uuid() self._test_update(tenant_id + "bad", tenant_id, exc.HTTPNotFound.code, expect_errors=True) def test_update_keystone_no_tenant(self): tenant_id = _uuid() self._test_update(tenant_id, None, exc.HTTPNotFound.code, expect_errors=True) def test_update_readonly_field(self): data = {'network': {'status': "NANANA"}} res = self.api.put(_get_path('networks', id=_uuid()), self.serialize(data), content_type='application/' + self.fmt, expect_errors=True) self.assertEqual(400, res.status_int) def test_invalid_attribute_field(self): data = {'network': {'invalid_key1': "foo1", 'invalid_key2': "foo2"}} res = self.api.put(_get_path('networks', id=_uuid()), self.serialize(data), content_type='application/' + self.fmt, expect_errors=True) self.assertEqual(400, res.status_int) def test_retry_on_index(self): instance = self.plugin.return_value instance.get_networks.side_effect = [db_exc.RetryRequest(None), []] api = webtest.TestApp(router.APIRouter()) api.get(_get_path('networks', fmt=self.fmt)) self.assertTrue(instance.get_networks.called) def test_retry_on_show(self): instance = self.plugin.return_value instance.get_network.side_effect = [db_exc.RetryRequest(None), {}] api = webtest.TestApp(router.APIRouter()) api.get(_get_path('networks', _uuid(), fmt=self.fmt)) self.assertTrue(instance.get_network.called) class SubresourceTest(base.BaseTestCase): def setUp(self): super(SubresourceTest, self).setUp() raise self.skipException('this class will be deleted') plugin = 'neutron.tests.unit.api.v2.test_base.TestSubresourcePlugin' extensions.PluginAwareExtensionManager._instance = None self.useFixture(fixture.APIDefinitionFixture()) self.config_parse() self.setup_coreplugin(plugin, load_plugins=False) self._plugin_patcher = mock.patch(plugin, autospec=True) self.plugin = self._plugin_patcher.start() api = router.APIRouter() SUB_RESOURCES = {} RESOURCE_ATTRIBUTE_MAP = {} SUB_RESOURCES[dummy_plugin.RESOURCE_NAME] = { 'collection_name': 'dummies', 'parent': {'collection_name': 'networks', 'member_name': 'network'} } RESOURCE_ATTRIBUTE_MAP['dummies'] = { 'foo': {'allow_post': True, 'allow_put': True, 'validate': {'type:string': None}, 'default': '', 'is_visible': True}, 'tenant_id': {'allow_post': True, 'allow_put': False, 'validate': {'type:string': None}, 'required_by_policy': True, 'is_visible': True} } collection_name = SUB_RESOURCES[ dummy_plugin.RESOURCE_NAME].get('collection_name') resource_name = dummy_plugin.RESOURCE_NAME parent = SUB_RESOURCES[dummy_plugin.RESOURCE_NAME].get('parent') params = RESOURCE_ATTRIBUTE_MAP['dummies'] member_actions = {'mactions': 'GET'} _plugin = directory.get_plugin() controller = v2_base.create_resource(collection_name, resource_name, _plugin, params, member_actions=member_actions, parent=parent, allow_bulk=True, allow_pagination=True, allow_sorting=True) path_prefix = "/%s/{%s_id}/%s" % (parent['collection_name'], parent['member_name'], collection_name) mapper_kwargs = dict(controller=controller, path_prefix=path_prefix) api.map.collection(collection_name, resource_name, **mapper_kwargs) api.map.resource(collection_name, collection_name, controller=controller, parent_resource=parent, member=member_actions) self.api = webtest.TestApp(api) def test_index_sub_resource(self): instance = self.plugin.return_value self.api.get('/networks/id1/dummies') instance.get_network_dummies.assert_called_once_with(mock.ANY, filters=mock.ANY, fields=mock.ANY, network_id='id1') def test_show_sub_resource(self): instance = self.plugin.return_value dummy_id = _uuid() self.api.get('/networks/id1' + _get_path('dummies', id=dummy_id)) instance.get_network_dummy.assert_called_once_with(mock.ANY, dummy_id, network_id='id1', fields=mock.ANY) def test_create_sub_resource(self): instance = self.plugin.return_value tenant_id = _uuid() body = { dummy_plugin.RESOURCE_NAME: { 'foo': 'bar', 'tenant_id': tenant_id, 'project_id': tenant_id } } self.api.post_json('/networks/id1/dummies', body) instance.create_network_dummy.assert_called_once_with(mock.ANY, network_id='id1', dummy=body) def test_update_sub_resource(self): instance = self.plugin.return_value dummy_id = _uuid() body = {dummy_plugin.RESOURCE_NAME: {'foo': 'bar'}} self.api.put_json('/networks/id1' + _get_path('dummies', id=dummy_id), body) instance.update_network_dummy.assert_called_once_with(mock.ANY, dummy_id, network_id='id1', dummy=body) def test_update_subresource_to_none(self): instance = self.plugin.return_value dummy_id = _uuid() body = {dummy_plugin.RESOURCE_NAME: {}} self.api.put_json('/networks/id1' + _get_path('dummies', id=dummy_id), body) instance.update_network_dummy.assert_called_once_with(mock.ANY, dummy_id, network_id='id1', dummy=body) def test_delete_sub_resource(self): instance = self.plugin.return_value dummy_id = _uuid() self.api.delete('/networks/id1' + _get_path('dummies', id=dummy_id)) instance.delete_network_dummy.assert_called_once_with(mock.ANY, dummy_id, network_id='id1') def test_sub_resource_member_actions(self): instance = self.plugin.return_value dummy_id = _uuid() self.api.get('/networks/id1' + _get_path('dummies', id=dummy_id, action='mactions')) instance.mactions.assert_called_once_with(mock.ANY, dummy_id, network_id='id1') # Note: since all resources use the same controller and validation # logic, we actually get really good coverage from testing just networks. class V2Views(base.BaseTestCase): def _view(self, keys, collection, resource): data = dict((key, 'value') for key in keys) data['fake'] = 'value' attr_info = attributes.RESOURCE_ATTRIBUTE_MAP[collection] controller = v2_base.Controller(None, collection, resource, attr_info) res = controller._view(context.get_admin_context(), data) self.assertNotIn('fake', res) for key in keys: self.assertIn(key, res) def test_network(self): keys = ('id', 'name', 'subnets', 'admin_state_up', 'status', 'tenant_id') self._view(keys, 'networks', 'network') def test_port(self): keys = ('id', 'network_id', 'mac_address', 'fixed_ips', 'device_id', 'admin_state_up', 'tenant_id', 'status') self._view(keys, 'ports', 'port') def test_subnet(self): keys = ('id', 'network_id', 'tenant_id', 'gateway_ip', 'ip_version', 'cidr', 'enable_dhcp') self._view(keys, 'subnets', 'subnet') class NotificationTest(APIv2TestBase): def setUp(self): super(NotificationTest, self).setUp() fake_notifier.reset() def _resource_op_notifier(self, opname, resource, expected_errors=False): initial_input = {resource: {'name': 'myname'}} instance = self.plugin.return_value instance.get_networks.return_value = initial_input instance.get_networks_count.return_value = 0 expected_code = exc.HTTPCreated.code if opname == 'create': initial_input[resource]['tenant_id'] = _uuid() res = self.api.post_json( _get_path('networks'), initial_input, expect_errors=expected_errors) if opname == 'update': res = self.api.put_json( _get_path('networks', id=_uuid()), initial_input, expect_errors=expected_errors) expected_code = exc.HTTPOk.code if opname == 'delete': initial_input[resource]['tenant_id'] = _uuid() res = self.api.delete( _get_path('networks', id=_uuid()), expect_errors=expected_errors) expected_code = exc.HTTPNoContent.code expected_events = ('.'.join([resource, opname, "start"]), '.'.join([resource, opname, "end"])) self.assertEqual(len(expected_events), len(fake_notifier.NOTIFICATIONS)) for msg, event in zip(fake_notifier.NOTIFICATIONS, expected_events): self.assertEqual('INFO', msg['priority']) self.assertEqual(event, msg['event_type']) if opname == 'delete' and event == 'network.delete.end': self.assertIn('payload', msg) resource = msg['payload'] self.assertIn('network_id', resource) self.assertIn('network', resource) self.assertEqual(expected_code, res.status_int) def test_network_create_notifer(self): self._resource_op_notifier('create', 'network') def test_network_delete_notifer(self): self._resource_op_notifier('delete', 'network') def test_network_update_notifer(self): self._resource_op_notifier('update', 'network') class RegistryNotificationTest(APIv2TestBase): def setUp(self): # This test does not have database support so tracking cannot be used cfg.CONF.set_override('track_quota_usage', False, group='QUOTAS') super(RegistryNotificationTest, self).setUp() def _test_registry_notify(self, opname, resource, initial_input=None): instance = self.plugin.return_value instance.get_networks.return_value = initial_input instance.get_networks_count.return_value = 0 expected_code = exc.HTTPCreated.code with mock.patch.object(registry, 'publish') as notify: if opname == 'create': res = self.api.post_json( _get_path('networks'), initial_input) if opname == 'update': res = self.api.put_json( _get_path('networks', id=_uuid()), initial_input) expected_code = exc.HTTPOk.code if opname == 'delete': res = self.api.delete(_get_path('networks', id=_uuid())) expected_code = exc.HTTPNoContent.code self.assertTrue(notify.called) self.assertEqual(expected_code, res.status_int) def test_network_create_registry_notify(self): input = {'network': {'name': 'net', 'tenant_id': _uuid()}} self._test_registry_notify('create', 'network', input) def test_network_delete_registry_notify(self): self._test_registry_notify('delete', 'network') def test_network_update_registry_notify(self): input = {'network': {'name': 'net'}} self._test_registry_notify('update', 'network', input) def test_networks_create_bulk_registry_notify(self): input = {'networks': [{'name': 'net1', 'tenant_id': _uuid()}, {'name': 'net2', 'tenant_id': _uuid()}]} self._test_registry_notify('create', 'network', input) class QuotaTest(APIv2TestBase): def setUp(self): # This test does not have database support so tracking cannot be used cfg.CONF.set_override('track_quota_usage', False, group='QUOTAS') super(QuotaTest, self).setUp() # Use mock to let the API use a different QuotaEngine instance for # unit test in this class. This will ensure resource are registered # again and instantiated with neutron.quota.resource.CountableResource replacement_registry = resource_registry.ResourceRegistry() registry_patcher = mock.patch('neutron.quota.resource_registry.' 'ResourceRegistry.get_instance') mock_registry = registry_patcher.start().return_value mock_registry.get_resource = replacement_registry.get_resource mock_registry.resources = replacement_registry.resources # Register a resource replacement_registry.register_resource_by_name('network') def test_create_network_quota(self): cfg.CONF.set_override('quota_network', 1, group='QUOTAS') initial_input = {'network': {'name': 'net1', 'tenant_id': _uuid()}} full_input = {'network': {'admin_state_up': True, 'subnets': []}} full_input['network'].update(initial_input['network']) instance = self.plugin.return_value instance.get_networks_count.return_value = 1 res = self.api.post_json( _get_path('networks'), initial_input, expect_errors=True) instance.get_networks_count.assert_called_with(mock.ANY, filters=mock.ANY) self.assertIn("Quota exceeded for resources", res.json['NeutronError']['message']) def test_create_network_quota_no_counts(self): cfg.CONF.set_override('quota_network', 1, group='QUOTAS') initial_input = {'network': {'name': 'net1', 'tenant_id': _uuid()}} full_input = {'network': {'admin_state_up': True, 'subnets': []}} full_input['network'].update(initial_input['network']) instance = self.plugin.return_value instance.get_networks_count.side_effect = ( NotImplementedError()) instance.get_networks.return_value = ["foo"] res = self.api.post_json( _get_path('networks'), initial_input, expect_errors=True) instance.get_networks_count.assert_called_with(mock.ANY, filters=mock.ANY) self.assertIn("Quota exceeded for resources", res.json['NeutronError']['message']) def test_create_network_quota_without_limit(self): cfg.CONF.set_override('quota_network', -1, group='QUOTAS') initial_input = {'network': {'name': 'net1', 'tenant_id': _uuid()}} instance = self.plugin.return_value instance.get_networks_count.return_value = 3 res = self.api.post_json( _get_path('networks'), initial_input) self.assertEqual(exc.HTTPCreated.code, res.status_int) class ExtensionTestCase(base.BaseTestCase): def setUp(self): # This test does not have database support so tracking cannot be used cfg.CONF.set_override('track_quota_usage', False, group='QUOTAS') super(ExtensionTestCase, self).setUp() plugin = 'neutron.neutron_plugin_base_v2.NeutronPluginBaseV2' # Ensure existing ExtensionManager is not used extensions.PluginAwareExtensionManager._instance = None self.useFixture(fixture.APIDefinitionFixture()) # Create the default configurations self.config_parse() # Update the plugin and extensions path self.setup_coreplugin(plugin, load_plugins=False) cfg.CONF.set_override('api_extensions_path', EXTDIR) self._plugin_patcher = mock.patch(plugin, autospec=True) self.plugin = self._plugin_patcher.start() # Instantiate mock plugin and enable the V2attributes extension self.plugin.return_value.supported_extension_aliases = ["v2attrs"] api = router.APIRouter() self.api = webtest.TestApp(api) quota.QUOTAS._driver = None cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', group='QUOTAS') def test_extended_create(self): net_id = _uuid() tenant_id = _uuid() initial_input = {'network': {'name': 'net1', 'tenant_id': tenant_id, 'project_id': tenant_id, 'v2attrs:something_else': "abc"}} data = {'network': {'admin_state_up': True, 'shared': False}} data['network'].update(initial_input['network']) return_value = {'subnets': [], 'status': "ACTIVE", 'id': net_id, 'v2attrs:something': "123"} return_value.update(data['network'].copy()) instance = self.plugin.return_value instance.create_network.return_value = return_value instance.get_networks_count.return_value = 0 res = self.api.post_json(_get_path('networks'), initial_input) instance.create_network.assert_called_with(mock.ANY, network=data) self.assertEqual(exc.HTTPCreated.code, res.status_int) self.assertIn('network', res.json) net = res.json['network'] self.assertEqual(net_id, net['id']) self.assertEqual("ACTIVE", net['status']) self.assertEqual("123", net['v2attrs:something']) self.assertNotIn('v2attrs:something_else', net) class TestSubresourcePlugin(object): def get_network_dummies(self, context, network_id, filters=None, fields=None): return [] def get_network_dummy(self, context, id, network_id, fields=None): return {} def create_network_dummy(self, context, network_id, dummy): return {} def update_network_dummy(self, context, id, network_id, dummy): return {} def delete_network_dummy(self, context, id, network_id): return def mactions(self, context, id, network_id): return class ListArgsTestCase(base.BaseTestCase): def test_list_args(self): path = '/?fields=4&foo=3&fields=2&bar=1' request = webob.Request.blank(path) expect_val = ['2', '4'] actual_val = api_common.list_args(request, 'fields') self.assertEqual(expect_val, sorted(actual_val)) def test_list_args_with_empty(self): path = '/?foo=4&bar=3&baz=2&qux=1' request = webob.Request.blank(path) self.assertEqual([], api_common.list_args(request, 'fields')) class FiltersTestCase(base.BaseTestCase): def test_all_skip_args(self): path = '/?fields=4&fields=3&fields=2&fields=1' request = webob.Request.blank(path) self.assertEqual({}, api_common.get_filters(request, None, ["fields"])) def test_blank_values(self): path = '/?foo=&bar=&baz=&qux=' request = webob.Request.blank(path) self.assertEqual({}, api_common.get_filters(request, {})) def test_no_attr_info(self): path = '/?foo=4&bar=3&baz=2&qux=1' request = webob.Request.blank(path) expect_val = {'foo': ['4'], 'bar': ['3'], 'baz': ['2'], 'qux': ['1']} actual_val = api_common.get_filters(request, {}) self.assertEqual(expect_val, actual_val) def test_attr_info_without_conversion(self): path = '/?foo=4&bar=3&baz=2&qux=1' request = webob.Request.blank(path) attr_info = {'foo': {'key': 'val'}} expect_val = {'foo': ['4'], 'bar': ['3'], 'baz': ['2'], 'qux': ['1']} actual_val = api_common.get_filters(request, attr_info) self.assertEqual(expect_val, actual_val) def test_attr_info_with_convert_list_to(self): path = '/?foo=key=4&bar=3&foo=key=2&qux=1' request = webob.Request.blank(path) attr_info = { 'foo': { 'convert_list_to': converters.convert_kvp_list_to_dict, } } expect_val = {'foo': {'key': ['2', '4']}, 'bar': ['3'], 'qux': ['1']} actual_val = api_common.get_filters(request, attr_info) self.assertOrderedEqual(expect_val, actual_val) def test_attr_info_with_convert_to(self): path = '/?foo=4&bar=3&baz=2&qux=1' request = webob.Request.blank(path) attr_info = {'foo': {'convert_to': converters.convert_to_int}} expect_val = {'foo': [4], 'bar': ['3'], 'baz': ['2'], 'qux': ['1']} actual_val = api_common.get_filters(request, attr_info) self.assertEqual(expect_val, actual_val) def test_attr_info_with_base_db_attributes(self): path = '/?__contains__=1&__class__=2' request = webob.Request.blank(path) self.assertEqual({}, api_common.get_filters(request, {})) class CreateResourceTestCase(base.BaseTestCase): def test_resource_creation(self): resource = v2_base.create_resource('fakes', 'fake', None, {}) self.assertIsInstance(resource, webob.dec.wsgify) neutron-12.1.1/neutron/tests/unit/api/v2/test_attributes.py0000664000175000017500000000232513553660047024035 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.api.v2 import attributes from neutron.tests import base class TestHelpers(base.DietTestCase): def _verify_port_attributes(self, attrs): for test_attribute in ('id', 'name', 'mac_address', 'network_id', 'tenant_id', 'fixed_ips', 'status'): self.assertIn(test_attribute, attrs) def test_get_collection_info(self): attrs = attributes.get_collection_info('ports') self._verify_port_attributes(attrs) def test_get_collection_info_missing(self): self.assertFalse(attributes.get_collection_info('meh')) neutron-12.1.1/neutron/tests/unit/api/v2/test_resource.py0000664000175000017500000004025713553660046023503 0ustar zuulzuul00000000000000# Copyright (c) 2012 Intel Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from neutron_lib import exceptions as n_exc import oslo_i18n from webob import exc import webtest from neutron._i18n import _ from neutron.api.v2 import resource as wsgi_resource from neutron.common import utils from neutron.tests import base from neutron import wsgi class RequestTestCase(base.BaseTestCase): def setUp(self): super(RequestTestCase, self).setUp() self.req = wsgi_resource.Request({'foo': 'bar'}) def test_content_type_missing(self): request = wsgi.Request.blank('/tests/123', method='POST') request.body = b"" self.assertIsNone(request.get_content_type()) def test_content_type_with_charset(self): request = wsgi.Request.blank('/tests/123') request.headers["Content-Type"] = "application/json; charset=UTF-8" result = request.get_content_type() self.assertEqual("application/json", result) def test_content_type_with_partial_matched_string(self): request = wsgi.Request.blank('/tests/123') request.headers["Content-Type"] = "application/j" result = request.best_match_content_type() self.assertEqual("application/json", result) def test_content_type_from_accept(self): content_type = 'application/json' request = wsgi.Request.blank('/tests/123') request.headers["Accept"] = content_type result = request.best_match_content_type() self.assertEqual(content_type, result) def test_content_type_from_accept_best(self): request = wsgi.Request.blank('/tests/123') request.headers["Accept"] = "application/json" result = request.best_match_content_type() self.assertEqual("application/json", result) request = wsgi.Request.blank('/tests/123') request.headers["Accept"] = ("application/json; q=0.3, " "application/xml; q=0.9") result = request.best_match_content_type() self.assertEqual("application/json", result) def test_content_type_from_query_extension(self): request = wsgi.Request.blank('/tests/123.json') result = request.best_match_content_type() self.assertEqual("application/json", result) request = wsgi.Request.blank('/tests/123.invalid') result = request.best_match_content_type() self.assertEqual("application/json", result) def test_content_type_accept_and_query_extension(self): request = wsgi.Request.blank('/tests/123.json') request.headers["Accept"] = "application/xml" result = request.best_match_content_type() self.assertEqual("application/json", result) def test_content_type_accept_default(self): request = wsgi.Request.blank('/tests/123.unsupported') request.headers["Accept"] = "application/unsupported1" result = request.best_match_content_type() self.assertEqual("application/json", result) def test_context_with_neutron_context(self): ctxt = context.Context('fake_user', 'fake_tenant') self.req.environ['neutron.context'] = ctxt self.assertEqual(self.req.context, ctxt) def test_context_without_neutron_context(self): self.assertTrue(self.req.context.is_admin) def test_request_context_elevated(self): user_context = context.Context( 'fake_user', 'fake_project', is_admin=False) self.assertFalse(user_context.is_admin) admin_context = user_context.elevated() self.assertFalse(user_context.is_admin) self.assertTrue(admin_context.is_admin) self.assertNotIn('admin', user_context.roles) self.assertIn('admin', admin_context.roles) def test_best_match_language(self): # Test that we are actually invoking language negotiation by webop request = wsgi.Request.blank('/') oslo_i18n.get_available_languages = mock.MagicMock() oslo_i18n.get_available_languages.return_value = ['known-language', 'es', 'zh'] request.headers['Accept-Language'] = 'known-language' language = request.best_match_language() self.assertEqual('known-language', language) # If the Accept-Leader is an unknown language, missing or empty, # the best match locale should be None request.headers['Accept-Language'] = 'unknown-language' language = request.best_match_language() self.assertIsNone(language) request.headers['Accept-Language'] = '' language = request.best_match_language() self.assertIsNone(language) request.headers.pop('Accept-Language') language = request.best_match_language() self.assertIsNone(language) class ResourceTestCase(base.BaseTestCase): @staticmethod def _get_deserializer(): return wsgi.JSONDeserializer() def test_unmapped_neutron_error_with_json(self): msg = u'\u7f51\u7edc' class TestException(n_exc.NeutronException): message = msg expected_res = {'body': { 'NeutronError': { 'type': 'TestException', 'message': msg, 'detail': ''}}} controller = mock.MagicMock() controller.test.side_effect = TestException() resource = webtest.TestApp(wsgi_resource.Resource(controller)) environ = {'wsgiorg.routing_args': (None, {'action': 'test', 'format': 'json'})} res = resource.get('', extra_environ=environ, expect_errors=True) self.assertEqual(exc.HTTPInternalServerError.code, res.status_int) self.assertEqual(expected_res, wsgi.JSONDeserializer().deserialize(res.body)) @mock.patch('oslo_i18n.translate') def test_unmapped_neutron_error_localized(self, mock_translation): msg_translation = 'Translated error' mock_translation.return_value = msg_translation msg = _('Unmapped error') class TestException(n_exc.NeutronException): message = msg controller = mock.MagicMock() controller.test.side_effect = TestException() resource = webtest.TestApp(wsgi_resource.Resource(controller)) environ = {'wsgiorg.routing_args': (None, {'action': 'test', 'format': 'json'})} res = resource.get('', extra_environ=environ, expect_errors=True) self.assertEqual(exc.HTTPInternalServerError.code, res.status_int) self.assertIn(msg_translation, str(wsgi.JSONDeserializer().deserialize(res.body))) def test_mapped_neutron_error_with_json(self): msg = u'\u7f51\u7edc' class TestException(n_exc.NeutronException): message = msg expected_res = {'body': { 'NeutronError': { 'type': 'TestException', 'message': msg, 'detail': ''}}} controller = mock.MagicMock() controller.test.side_effect = TestException() faults = {TestException: exc.HTTPGatewayTimeout} resource = webtest.TestApp(wsgi_resource.Resource(controller, faults=faults)) environ = {'wsgiorg.routing_args': (None, {'action': 'test', 'format': 'json'})} res = resource.get('', extra_environ=environ, expect_errors=True) self.assertEqual(exc.HTTPGatewayTimeout.code, res.status_int) self.assertEqual(expected_res, wsgi.JSONDeserializer().deserialize(res.body)) @mock.patch('oslo_i18n.translate') def test_mapped_neutron_error_localized(self, mock_translation): msg_translation = 'Translated error' mock_translation.return_value = msg_translation msg = _('Unmapped error') class TestException(n_exc.NeutronException): message = msg controller = mock.MagicMock() controller.test.side_effect = TestException() faults = {TestException: exc.HTTPGatewayTimeout} resource = webtest.TestApp(wsgi_resource.Resource(controller, faults=faults)) environ = {'wsgiorg.routing_args': (None, {'action': 'test', 'format': 'json'})} res = resource.get('', extra_environ=environ, expect_errors=True) self.assertEqual(exc.HTTPGatewayTimeout.code, res.status_int) self.assertIn(msg_translation, str(wsgi.JSONDeserializer().deserialize(res.body))) @staticmethod def _make_request_with_side_effect(side_effect): controller = mock.MagicMock() controller.test.side_effect = side_effect resource = webtest.TestApp(wsgi_resource.Resource(controller)) routing_args = {'action': 'test'} environ = {'wsgiorg.routing_args': (None, routing_args)} res = resource.get('', extra_environ=environ, expect_errors=True) return res def test_http_error(self): res = self._make_request_with_side_effect(exc.HTTPGatewayTimeout()) # verify that the exception structure is the one expected # by the python-neutronclient self.assertEqual(exc.HTTPGatewayTimeout().explanation, res.json['NeutronError']['message']) self.assertEqual('HTTPGatewayTimeout', res.json['NeutronError']['type']) self.assertEqual('', res.json['NeutronError']['detail']) self.assertEqual(exc.HTTPGatewayTimeout.code, res.status_int) def test_unhandled_error(self): expected_res = {'body': {'NeutronError': {'detail': '', 'message': _( 'Request Failed: internal server ' 'error while processing your request.'), 'type': 'HTTPInternalServerError'}}} res = self._make_request_with_side_effect(side_effect=Exception()) self.assertEqual(exc.HTTPInternalServerError.code, res.status_int) self.assertEqual(expected_res, self._get_deserializer().deserialize(res.body)) def test_not_implemented_error(self): expected_res = {'body': {'NeutronError': {'detail': '', 'message': _( 'The server has either erred or is ' 'incapable of performing the requested ' 'operation.'), 'type': 'HTTPNotImplemented'}}} res = self._make_request_with_side_effect(exc.HTTPNotImplemented()) self.assertEqual(exc.HTTPNotImplemented.code, res.status_int) self.assertEqual(expected_res, self._get_deserializer().deserialize(res.body)) def test_status_200(self): controller = mock.MagicMock() controller.test = lambda request: {'foo': 'bar'} resource = webtest.TestApp(wsgi_resource.Resource(controller)) environ = {'wsgiorg.routing_args': (None, {'action': 'test'})} res = resource.get('', extra_environ=environ) self.assertEqual(200, res.status_int) def _test_unhandled_error_logs_details(self, e, expected_details): with mock.patch.object(wsgi_resource.LOG, 'exception') as log: self._make_request_with_side_effect(side_effect=e) log.assert_called_with( mock.ANY, {'action': mock.ANY, 'details': expected_details}) def test_unhandled_error_logs_attached_details(self): e = Exception() utils.attach_exc_details(e, 'attached_details') self._test_unhandled_error_logs_details(e, 'attached_details') def test_unhandled_error_logs_no_attached_details(self): e = Exception() self._test_unhandled_error_logs_details(e, 'No details.') def test_status_204(self): controller = mock.MagicMock() controller.test = lambda request: {'foo': 'bar'} resource = webtest.TestApp(wsgi_resource.Resource(controller)) environ = {'wsgiorg.routing_args': (None, {'action': 'delete'})} res = resource.delete('', extra_environ=environ) self.assertEqual(204, res.status_int) def test_action_status(self): controller = mock.MagicMock() controller.test = lambda request: {'foo': 'bar'} action_status = {'test_200': 200, 'test_201': 201, 'test_204': 204} resource = webtest.TestApp( wsgi_resource.Resource(controller, action_status=action_status)) for action in action_status: environ = {'wsgiorg.routing_args': (None, {'action': action})} res = resource.get('', extra_environ=environ) self.assertEqual(action_status[action], res.status_int) def _test_error_log_level(self, expected_webob_exc, expect_log_info=False, use_fault_map=True, exc_raised=None): if not exc_raised: class TestException(n_exc.NeutronException): message = 'Test Exception' exc_raised = TestException controller = mock.MagicMock() controller.test.side_effect = exc_raised() faults = {exc_raised: expected_webob_exc} if use_fault_map else {} resource = webtest.TestApp(wsgi_resource.Resource(controller, faults)) environ = {'wsgiorg.routing_args': (None, {'action': 'test'})} with mock.patch.object(wsgi_resource, 'LOG') as log: res = resource.get('', extra_environ=environ, expect_errors=True) self.assertEqual(expected_webob_exc.code, res.status_int) self.assertEqual(expect_log_info, log.info.called) self.assertNotEqual(expect_log_info, log.exception.called) def test_4xx_error_logged_info_level(self): self._test_error_log_level(exc.HTTPNotFound, expect_log_info=True) def test_non_4xx_error_logged_exception_level(self): self._test_error_log_level(exc.HTTPServiceUnavailable, expect_log_info=False) def test_unmapped_error_logged_exception_level(self): self._test_error_log_level(exc.HTTPInternalServerError, expect_log_info=False, use_fault_map=False) def test_webob_4xx_logged_info_level(self): self._test_error_log_level(exc.HTTPNotFound, use_fault_map=False, expect_log_info=True, exc_raised=exc.HTTPNotFound) def test_webob_5xx_logged_info_level(self): self._test_error_log_level(exc.HTTPServiceUnavailable, use_fault_map=False, expect_log_info=False, exc_raised=exc.HTTPServiceUnavailable) def test_no_route_args(self): controller = mock.MagicMock() resource = webtest.TestApp(wsgi_resource.Resource(controller)) environ = {} res = resource.get('', extra_environ=environ, expect_errors=True) self.assertEqual(exc.HTTPInternalServerError.code, res.status_int) def test_post_with_body(self): controller = mock.MagicMock() controller.test = lambda request, body: {'foo': 'bar'} resource = webtest.TestApp(wsgi_resource.Resource(controller)) environ = {'wsgiorg.routing_args': (None, {'action': 'test'})} res = resource.post('', params='{"key": "val"}', extra_environ=environ) self.assertEqual(200, res.status_int) neutron-12.1.1/neutron/tests/unit/api/v2/test_router.py0000664000175000017500000000174613553660046023174 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.api.v2 import router from neutron.tests import base @mock.patch('neutron.api.v2.router.APIRouter.__init__', return_value=None) @mock.patch('neutron.pecan_wsgi.app.v2_factory') class TestRouter(base.BaseTestCase): def test_pecan_factory(self, pecan_mock, legacy_mock): router.APIRouter.factory({}) pecan_mock.assert_called_once_with({}) legacy_mock.assert_not_called() neutron-12.1.1/neutron/tests/unit/api/v2/__init__.py0000664000175000017500000000000013553660046022332 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/api/test_api_common.py0000664000175000017500000000243713553660047023445 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron.api import api_common from neutron.tests import base class PrepareUrlTestCase(base.BaseTestCase): def test_no_configured_prefix(self): self.assertFalse(cfg.CONF.network_link_prefix) requrl = 'http://neutron.example/sub/ports.json?test=1' # should be unchanged self.assertEqual(requrl, api_common.prepare_url(requrl)) def test_configured_prefix(self): cfg.CONF.set_override('network_link_prefix', 'http://quantum.example') requrl = 'http://neutron.example/sub/ports.json?test=1' expected = 'http://quantum.example/sub/ports.json?test=1' self.assertEqual(expected, api_common.prepare_url(requrl)) neutron-12.1.1/neutron/tests/unit/api/test_extensions.py0000664000175000017500000012764613553660047023535 0ustar zuulzuul00000000000000# Copyright (c) 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import fixtures import mock from neutron_lib.plugins import constants as lib_const from neutron_lib.plugins import directory from neutron_lib.services import base as service_base from oslo_config import cfg from oslo_log import log as logging from oslo_serialization import jsonutils from oslo_service import wsgi as base_wsgi import routes import testtools import webob import webob.exc as webexc import webtest import neutron from neutron.api import extensions from neutron.common import config from neutron.common import exceptions from neutron.plugins.common import constants from neutron import quota from neutron.tests import base from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit import dummy_plugin from neutron.tests.unit import extension_stubs as ext_stubs import neutron.tests.unit.extensions from neutron.tests.unit.extensions import extendedattribute as extattr from neutron.tests.unit import testlib_api from neutron import wsgi LOG = logging.getLogger(__name__) _uuid = test_base._uuid _get_path = test_base._get_path extensions_path = ':'.join(neutron.tests.unit.extensions.__path__) class CustomExtensionCheckMapMemento(fixtures.Fixture): """Create a copy of the custom extension support check map so it can be restored during test cleanup. """ def _setUp(self): self._map_contents_backup = copy.deepcopy( extensions.EXTENSION_SUPPORTED_CHECK_MAP ) self._plugin_agnostic_extensions_backup = set( extensions._PLUGIN_AGNOSTIC_EXTENSIONS ) self.addCleanup(self._restore) def _restore(self): extensions.EXTENSION_SUPPORTED_CHECK_MAP = self._map_contents_backup extensions._PLUGIN_AGNOSTIC_EXTENSIONS = ( self._plugin_agnostic_extensions_backup ) class ExtensionsTestApp(base_wsgi.Router): def __init__(self, options=None): options = options or {} mapper = routes.Mapper() controller = ext_stubs.StubBaseAppController() mapper.resource("dummy_resource", "/dummy_resources", controller=controller) super(ExtensionsTestApp, self).__init__(mapper) class FakePluginWithExtension(service_base.ServicePluginBase): """A fake plugin used only for extension testing in this file.""" supported_extension_aliases = ["FOXNSOX"] def method_to_support_foxnsox_extension(self, context): self._log("method_to_support_foxnsox_extension", context) def get_plugin_type(self): pass def get_plugin_description(self): pass class ExtensionPathTest(base.BaseTestCase): def setUp(self): self.base_path = extensions.get_extensions_path() super(ExtensionPathTest, self).setUp() def test_get_extensions_path_with_plugins(self): cfg.CONF.set_override('api_extensions_path', 'neutron/tests/unit/extensions') path = extensions.get_extensions_path( {lib_const.CORE: FakePluginWithExtension()}) self.assertEqual(path, '%s:neutron/tests/unit/extensions' % self.base_path) def test_get_extensions_path_no_extensions(self): # Reset to default value, as it's overridden by base class cfg.CONF.set_override('api_extensions_path', '') path = extensions.get_extensions_path() self.assertEqual(path, self.base_path) def test_get_extensions_path_single_extension(self): cfg.CONF.set_override('api_extensions_path', 'path1') path = extensions.get_extensions_path() self.assertEqual(path, '%s:path1' % self.base_path) def test_get_extensions_path_multiple_extensions(self): cfg.CONF.set_override('api_extensions_path', 'path1:path2') path = extensions.get_extensions_path() self.assertEqual(path, '%s:path1:path2' % self.base_path) def test_get_extensions_path_duplicate_extensions(self): cfg.CONF.set_override('api_extensions_path', 'path1:path1') path = extensions.get_extensions_path() self.assertEqual(path, '%s:path1' % self.base_path) class ResourceExtensionTest(base.BaseTestCase): class ResourceExtensionController(wsgi.Controller): def index(self, request): return "resource index" def show(self, request, id): return {'data': {'id': id}} def notimplemented_function(self, request, id): return webob.exc.HTTPNotImplemented() def custom_member_action(self, request, id): return {'member_action': 'value'} def custom_collection_method(self, request, **kwargs): return {'collection': 'value'} def custom_collection_action(self, request, **kwargs): return {'collection': 'value'} class DummySvcPlugin(wsgi.Controller): @classmethod def get_plugin_type(cls): return dummy_plugin.DUMMY_SERVICE_TYPE def index(self, request, **kwargs): return "resource index" def custom_member_action(self, request, **kwargs): return {'member_action': 'value'} def collection_action(self, request, **kwargs): return {'collection': 'value'} def show(self, request, id): return {'data': {'id': id}} def test_exceptions_notimplemented(self): controller = self.ResourceExtensionController() member = {'notimplemented_function': "GET"} res_ext = extensions.ResourceExtension('tweedles', controller, member_actions=member) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) # Ideally we would check for a 501 code here but webtest doesn't take # anything that is below 200 or above 400 so we can't actually check # it. It throws webtest.AppError instead. try: test_app.get("/tweedles/some_id/notimplemented_function") # Shouldn't be reached self.fail() except webtest.AppError as e: self.assertIn('501', str(e)) def test_resource_can_be_added_as_extension(self): res_ext = extensions.ResourceExtension( 'tweedles', self.ResourceExtensionController()) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) index_response = test_app.get("/tweedles") self.assertEqual(200, index_response.status_int) self.assertEqual(b"resource index", index_response.body) show_response = test_app.get("/tweedles/25266") self.assertEqual({'data': {'id': "25266"}}, show_response.json) def test_resource_gets_prefix_of_plugin(self): class DummySvcPlugin(wsgi.Controller): def index(self, request): return "" @classmethod def get_plugin_type(cls): return dummy_plugin.DUMMY_SERVICE_TYPE res_ext = extensions.ResourceExtension( 'tweedles', DummySvcPlugin(), path_prefix="/dummy_svc") test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) index_response = test_app.get("/dummy_svc/tweedles") self.assertEqual(200, index_response.status_int) def test_resource_extension_with_custom_member_action(self): controller = self.ResourceExtensionController() member = {'custom_member_action': "GET"} res_ext = extensions.ResourceExtension('tweedles', controller, member_actions=member) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.get("/tweedles/some_id/custom_member_action") self.assertEqual(200, response.status_int) self.assertEqual(jsonutils.loads(response.body)['member_action'], "value") def test_resource_ext_with_custom_member_action_gets_plugin_prefix(self): controller = self.DummySvcPlugin() member = {'custom_member_action': "GET"} collections = {'collection_action': "GET"} res_ext = extensions.ResourceExtension('tweedles', controller, path_prefix="/dummy_svc", member_actions=member, collection_actions=collections) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.get("/dummy_svc/tweedles/1/custom_member_action") self.assertEqual(200, response.status_int) self.assertEqual(jsonutils.loads(response.body)['member_action'], "value") response = test_app.get("/dummy_svc/tweedles/collection_action") self.assertEqual(200, response.status_int) self.assertEqual(jsonutils.loads(response.body)['collection'], "value") def test_plugin_prefix_with_parent_resource(self): controller = self.DummySvcPlugin() parent = dict(member_name="tenant", collection_name="tenants") member = {'custom_member_action': "GET"} collections = {'collection_action': "GET"} res_ext = extensions.ResourceExtension('tweedles', controller, parent, path_prefix="/dummy_svc", member_actions=member, collection_actions=collections) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) index_response = test_app.get("/dummy_svc/tenants/1/tweedles") self.assertEqual(200, index_response.status_int) response = test_app.get("/dummy_svc/tenants/1/" "tweedles/1/custom_member_action") self.assertEqual(200, response.status_int) self.assertEqual(jsonutils.loads(response.body)['member_action'], "value") response = test_app.get("/dummy_svc/tenants/2/" "tweedles/collection_action") self.assertEqual(200, response.status_int) self.assertEqual(jsonutils.loads(response.body)['collection'], "value") def test_resource_extension_for_get_custom_collection_action(self): controller = self.ResourceExtensionController() collections = {'custom_collection_action': "GET"} res_ext = extensions.ResourceExtension('tweedles', controller, collection_actions=collections) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.get("/tweedles/custom_collection_action") self.assertEqual(200, response.status_int) LOG.debug(jsonutils.loads(response.body)) self.assertEqual(jsonutils.loads(response.body)['collection'], "value") def test_resource_extension_for_put_custom_collection_action(self): controller = self.ResourceExtensionController() collections = {'custom_collection_action': "PUT"} res_ext = extensions.ResourceExtension('tweedles', controller, collection_actions=collections) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.put("/tweedles/custom_collection_action") self.assertEqual(200, response.status_int) self.assertEqual(jsonutils.loads(response.body)['collection'], 'value') def test_resource_extension_for_post_custom_collection_action(self): controller = self.ResourceExtensionController() collections = {'custom_collection_action': "POST"} res_ext = extensions.ResourceExtension('tweedles', controller, collection_actions=collections) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.post("/tweedles/custom_collection_action") self.assertEqual(200, response.status_int) self.assertEqual(jsonutils.loads(response.body)['collection'], 'value') def test_resource_extension_for_delete_custom_collection_action(self): controller = self.ResourceExtensionController() collections = {'custom_collection_action': "DELETE"} res_ext = extensions.ResourceExtension('tweedles', controller, collection_actions=collections) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.delete("/tweedles/custom_collection_action") self.assertEqual(200, response.status_int) self.assertEqual(jsonutils.loads(response.body)['collection'], 'value') def test_resource_ext_for_formatted_req_on_custom_collection_action(self): controller = self.ResourceExtensionController() collections = {'custom_collection_action': "GET"} res_ext = extensions.ResourceExtension('tweedles', controller, collection_actions=collections) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.get("/tweedles/custom_collection_action.json") self.assertEqual(200, response.status_int) self.assertEqual(jsonutils.loads(response.body)['collection'], "value") def test_resource_ext_for_nested_resource_custom_collection_action(self): controller = self.ResourceExtensionController() collections = {'custom_collection_action': "GET"} parent = dict(collection_name='beetles', member_name='beetle') res_ext = extensions.ResourceExtension('tweedles', controller, collection_actions=collections, parent=parent) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.get("/beetles/beetle_id" "/tweedles/custom_collection_action") self.assertEqual(200, response.status_int) self.assertEqual(jsonutils.loads(response.body)['collection'], "value") def test_resource_extension_for_get_custom_collection_method(self): controller = self.ResourceExtensionController() collections = {'custom_collection_method': "GET"} res_ext = extensions.ResourceExtension('tweedles', controller, collection_methods=collections) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.get("/tweedles") self.assertEqual(200, response.status_int) self.assertEqual("value", jsonutils.loads(response.body)['collection']) def test_resource_extension_for_put_custom_collection_method(self): controller = self.ResourceExtensionController() collections = {'custom_collection_method': "PUT"} res_ext = extensions.ResourceExtension('tweedles', controller, collection_methods=collections) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.put("/tweedles") self.assertEqual(200, response.status_int) self.assertEqual('value', jsonutils.loads(response.body)['collection']) def test_resource_extension_for_post_custom_collection_method(self): controller = self.ResourceExtensionController() collections = {'custom_collection_method': "POST"} res_ext = extensions.ResourceExtension('tweedles', controller, collection_methods=collections) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.post("/tweedles") self.assertEqual(200, response.status_int) self.assertEqual('value', jsonutils.loads(response.body)['collection']) def test_resource_extension_for_delete_custom_collection_method(self): controller = self.ResourceExtensionController() collections = {'custom_collection_method': "DELETE"} res_ext = extensions.ResourceExtension('tweedles', controller, collection_methods=collections) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.delete("/tweedles") self.assertEqual(200, response.status_int) self.assertEqual('value', jsonutils.loads(response.body)['collection']) def test_resource_ext_for_formatted_req_on_custom_collection_method(self): controller = self.ResourceExtensionController() collections = {'custom_collection_method': "GET"} res_ext = extensions.ResourceExtension('tweedles', controller, collection_methods=collections) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.get("/tweedles.json") self.assertEqual(200, response.status_int) self.assertEqual("value", jsonutils.loads(response.body)['collection']) def test_resource_ext_for_nested_resource_custom_collection_method(self): controller = self.ResourceExtensionController() collections = {'custom_collection_method': "GET"} parent = {'collection_name': 'beetles', 'member_name': 'beetle'} res_ext = extensions.ResourceExtension('tweedles', controller, collection_methods=collections, parent=parent) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.get("/beetles/beetle_id/tweedles") self.assertEqual(200, response.status_int) self.assertEqual("value", jsonutils.loads(response.body)['collection']) def test_resource_extension_with_custom_member_action_and_attr_map(self): controller = self.ResourceExtensionController() member = {'custom_member_action': "GET"} params = { 'tweedles': { 'id': {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True}, 'name': {'allow_post': True, 'allow_put': True, 'validate': {'type:string': None}, 'default': '', 'is_visible': True}, } } res_ext = extensions.ResourceExtension('tweedles', controller, member_actions=member, attr_map=params) test_app = _setup_extensions_test_app(SimpleExtensionManager(res_ext)) response = test_app.get("/tweedles/some_id/custom_member_action") self.assertEqual(200, response.status_int) self.assertEqual(jsonutils.loads(response.body)['member_action'], "value") def test_returns_404_for_non_existent_extension(self): test_app = _setup_extensions_test_app(SimpleExtensionManager(None)) response = test_app.get("/non_extistant_extension", status='*') self.assertEqual(404, response.status_int) class ActionExtensionTest(base.BaseTestCase): def setUp(self): super(ActionExtensionTest, self).setUp() self.extension_app = _setup_extensions_test_app() def test_extended_action_for_adding_extra_data(self): action_name = 'FOXNSOX:add_tweedle' action_params = dict(name='Beetle') req_body = jsonutils.dumps({action_name: action_params}) response = self.extension_app.post('/dummy_resources/1/action', req_body, content_type='application/json') self.assertEqual(b"Tweedle Beetle Added.", response.body) def test_extended_action_for_deleting_extra_data(self): action_name = 'FOXNSOX:delete_tweedle' action_params = dict(name='Bailey') req_body = jsonutils.dumps({action_name: action_params}) response = self.extension_app.post("/dummy_resources/1/action", req_body, content_type='application/json') self.assertEqual(b"Tweedle Bailey Deleted.", response.body) def test_returns_404_for_non_existent_action(self): non_existent_action = 'blah_action' action_params = dict(name="test") req_body = jsonutils.dumps({non_existent_action: action_params}) response = self.extension_app.post("/dummy_resources/1/action", req_body, content_type='application/json', status='*') self.assertEqual(404, response.status_int) def test_returns_404_for_non_existent_resource(self): action_name = 'add_tweedle' action_params = dict(name='Beetle') req_body = jsonutils.dumps({action_name: action_params}) response = self.extension_app.post("/asdf/1/action", req_body, content_type='application/json', status='*') self.assertEqual(404, response.status_int) class RequestExtensionTest(base.BaseTestCase): def test_headers_can_be_extended(self): def extend_headers(req, res): assert req.headers['X-NEW-REQUEST-HEADER'] == "sox" res.headers['X-NEW-RESPONSE-HEADER'] = "response_header_data" return res app = self._setup_app_with_request_handler(extend_headers, 'GET') response = app.get("/dummy_resources/1", headers={'X-NEW-REQUEST-HEADER': "sox"}) self.assertEqual(response.headers['X-NEW-RESPONSE-HEADER'], "response_header_data") def test_extend_get_resource_response(self): def extend_response_data(req, res): data = jsonutils.loads(res.body) data['FOXNSOX:extended_key'] = req.GET.get('extended_key') res.body = jsonutils.dump_as_bytes(data) return res app = self._setup_app_with_request_handler(extend_response_data, 'GET') response = app.get("/dummy_resources/1?extended_key=extended_data") self.assertEqual(200, response.status_int) response_data = jsonutils.loads(response.body) self.assertEqual('extended_data', response_data['FOXNSOX:extended_key']) self.assertEqual('knox', response_data['fort']) def test_get_resources(self): app = _setup_extensions_test_app() response = app.get("/dummy_resources/1?chewing=newblue") response_data = jsonutils.loads(response.body) self.assertEqual('newblue', response_data['FOXNSOX:googoose']) self.assertEqual("Pig Bands!", response_data['FOXNSOX:big_bands']) def test_edit_previously_uneditable_field(self): def _update_handler(req, res): data = jsonutils.loads(res.body) data['uneditable'] = req.params['uneditable'] res.body = jsonutils.dump_as_bytes(data) return res base_app = webtest.TestApp(setup_base_app(self)) response = base_app.put("/dummy_resources/1", {'uneditable': "new_value"}) self.assertEqual(response.json['uneditable'], "original_value") ext_app = self._setup_app_with_request_handler(_update_handler, 'PUT') ext_response = ext_app.put("/dummy_resources/1", {'uneditable': "new_value"}) self.assertEqual(ext_response.json['uneditable'], "new_value") def _setup_app_with_request_handler(self, handler, verb): req_ext = extensions.RequestExtension(verb, '/dummy_resources/:(id)', handler) manager = SimpleExtensionManager(None, None, req_ext) return _setup_extensions_test_app(manager) class ExtensionManagerTest(base.BaseTestCase): def test_optional_extensions_no_error(self): ext_mgr = extensions.ExtensionManager('') attr_map = {} ext_mgr.add_extension(ext_stubs.StubExtension('foo_alias', optional=['cats'])) ext_mgr.extend_resources("2.0", attr_map) self.assertIn('foo_alias', ext_mgr.extensions) def test_missing_required_extensions_raise_error(self): ext_mgr = extensions.ExtensionManager('') attr_map = {} ext_mgr.add_extension(ext_stubs.StubExtensionWithReqs('foo_alias')) self.assertRaises(exceptions.ExtensionsNotFound, ext_mgr.extend_resources, "2.0", attr_map) def test_missing_required_extensions_gracefully_error(self): ext_mgr = extensions.ExtensionManager('') attr_map = {} default_ext = list(constants.DEFAULT_SERVICE_PLUGINS.values())[0] ext_mgr.add_extension(ext_stubs.StubExtensionWithReqs(default_ext)) ext_mgr.extend_resources("2.0", attr_map) # none of the default extensions should be loaded as their # requirements are not satisfied, and yet we do not fail. self.assertFalse(ext_mgr.extensions) def test__check_faulty_extensions_raise_not_default_ext(self): ext_mgr = extensions.ExtensionManager('') with testtools.ExpectedException(exceptions.ExtensionsNotFound): ext_mgr._check_faulty_extensions(set(['foo'])) def test_invalid_extensions_are_not_registered(self): class InvalidExtension(object): """Invalid extension. This Extension doesn't implement extension methods : get_name, get_description and get_updated """ def get_alias(self): return "invalid_extension" ext_mgr = extensions.ExtensionManager('') ext_mgr.add_extension(InvalidExtension()) ext_mgr.add_extension(ext_stubs.StubExtension("valid_extension")) self.assertIn('valid_extension', ext_mgr.extensions) self.assertNotIn('invalid_extension', ext_mgr.extensions) def test_assignment_of_attr_map(self): """Unit test for bug 1443342 In this bug, an extension that extended multiple resources with the same dict would cause future extensions to inadvertently modify the resources of all of the resources since they were referencing the same dictionary. """ class MultiResourceExtension(ext_stubs.StubExtension): """Generated Extended Resources. This extension's extended resource will assign to more than one resource. """ def get_extended_resources(self, version): EXTENDED_TIMESTAMP = { 'created_at': {'allow_post': False, 'allow_put': False, 'is_visible': True}} EXTENDED_RESOURCES = ["ext1", "ext2"] attrs = {} for resources in EXTENDED_RESOURCES: attrs[resources] = EXTENDED_TIMESTAMP return attrs class AttrExtension(ext_stubs.StubExtension): def get_extended_resources(self, version): attrs = { self.alias: { '%s-attr' % self.alias: {'allow_post': False, 'allow_put': False, 'is_visible': True}}} return attrs ext_mgr = extensions.ExtensionManager('') attr_map = {} ext_mgr.add_extension(MultiResourceExtension('timestamp')) ext_mgr.extend_resources("2.0", attr_map) ext_mgr.add_extension(AttrExtension("ext1")) ext_mgr.add_extension(AttrExtension("ext2")) ext_mgr.extend_resources("2.0", attr_map) self.assertIn('created_at', attr_map['ext2']) self.assertIn('created_at', attr_map['ext1']) # now we need to make sure the attrextensions didn't leak across self.assertNotIn('ext1-attr', attr_map['ext2']) self.assertNotIn('ext2-attr', attr_map['ext1']) def test_extension_extends_sub_resource(self): """Unit test for bug 1722842 Check that an extension can extend a sub-resource """ RESOURCE = "test_resource" SUB_RESOURCE_NAME = "test_sub_resource" INITIAL_PARAM = "dummy_param1" ADDITIONAL_PARAM = "dummy_param2" SUB_RESOURCE = { 'parent': {'member_name': RESOURCE}, 'parameters': { INITIAL_PARAM: {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True} } } class BaseExtension(ext_stubs.StubExtension): def get_extended_resources(self, version): return { SUB_RESOURCE_NAME: SUB_RESOURCE } class ExtensionExtendingASubresource(ext_stubs.StubExtension): def get_extended_resources(self, version): return { SUB_RESOURCE_NAME: { 'parameters': { ADDITIONAL_PARAM: {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True} } } } def get_required_extensions(self): return ['base_extension'] ext_mgr = extensions.ExtensionManager('') attr_map = {} ext_mgr.add_extension(BaseExtension('base_extension')) ext_mgr.add_extension( ExtensionExtendingASubresource()) ext_mgr.extend_resources("2.0", attr_map) # check that the parent descriptor is untouched self.assertEqual(SUB_RESOURCE['parent'], attr_map[SUB_RESOURCE_NAME]['parent']) # check that the initial attribute is still here self.assertIn(INITIAL_PARAM, attr_map[SUB_RESOURCE_NAME]['parameters']) # check that the new attribute is here as well self.assertIn(ADDITIONAL_PARAM, attr_map[SUB_RESOURCE_NAME]['parameters']) class PluginAwareExtensionManagerTest(base.BaseTestCase): def test_unsupported_extensions_are_not_loaded(self): stub_plugin = ext_stubs.StubPlugin(supported_extensions=["e1", "e3"]) plugin_info = {lib_const.CORE: stub_plugin} with mock.patch("neutron.api.extensions.PluginAwareExtensionManager." "check_if_plugin_extensions_loaded"): ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info) ext_mgr.add_extension(ext_stubs.StubExtension("e1")) ext_mgr.add_extension(ext_stubs.StubExtension("e2")) ext_mgr.add_extension(ext_stubs.StubExtension("e3")) self.assertIn("e1", ext_mgr.extensions) self.assertNotIn("e2", ext_mgr.extensions) self.assertIn("e3", ext_mgr.extensions) def test_extensions_are_not_loaded_for_plugins_unaware_of_extensions(self): class ExtensionUnawarePlugin(object): """This plugin does not implement supports_extension method. Extensions will not be loaded when this plugin is used. """ pass plugin_info = {lib_const.CORE: ExtensionUnawarePlugin()} ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info) ext_mgr.add_extension(ext_stubs.StubExtension("e1")) self.assertNotIn("e1", ext_mgr.extensions) def test_extensions_not_loaded_for_plugin_without_expected_interface(self): class PluginWithoutExpectedIface(object): """Does not implement get_foo method as expected by extension.""" supported_extension_aliases = ["supported_extension"] plugin_info = {lib_const.CORE: PluginWithoutExpectedIface()} with mock.patch("neutron.api.extensions.PluginAwareExtensionManager." "check_if_plugin_extensions_loaded"): ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info) ext_mgr.add_extension(ext_stubs.ExtensionExpectingPluginInterface( "supported_extension")) self.assertNotIn("e1", ext_mgr.extensions) def test_extensions_are_loaded_for_plugin_with_expected_interface(self): class PluginWithExpectedInterface(service_base.ServicePluginBase): """Implements get_foo method as expected by extension.""" supported_extension_aliases = ["supported_extension"] def get_foo(self, bar=None): pass def get_plugin_type(self): pass def get_plugin_description(self): pass plugin_info = {lib_const.CORE: PluginWithExpectedInterface()} with mock.patch("neutron.api.extensions.PluginAwareExtensionManager." "check_if_plugin_extensions_loaded"): ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info) ext_mgr.add_extension(ext_stubs.ExtensionExpectingPluginInterface( "supported_extension")) self.assertIn("supported_extension", ext_mgr.extensions) def test_extensions_expecting_neutron_plugin_interface_are_loaded(self): class ExtensionForQuamtumPluginInterface(ext_stubs.StubExtension): """This Extension does not implement get_plugin_interface method. This will work with any plugin implementing NeutronPluginBase """ pass stub_plugin = ext_stubs.StubPlugin(supported_extensions=["e1"]) plugin_info = {lib_const.CORE: stub_plugin} with mock.patch("neutron.api.extensions.PluginAwareExtensionManager." "check_if_plugin_extensions_loaded"): ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info) ext_mgr.add_extension(ExtensionForQuamtumPluginInterface("e1")) self.assertIn("e1", ext_mgr.extensions) def test_extensions_without_need_for__plugin_interface_are_loaded(self): class ExtensionWithNoNeedForPluginInterface(ext_stubs.StubExtension): """This Extension does not need any plugin interface. This will work with any plugin implementing NeutronPluginBase """ def get_plugin_interface(self): return None stub_plugin = ext_stubs.StubPlugin(supported_extensions=["e1"]) plugin_info = {lib_const.CORE: stub_plugin} with mock.patch("neutron.api.extensions.PluginAwareExtensionManager." "check_if_plugin_extensions_loaded"): ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info) ext_mgr.add_extension(ExtensionWithNoNeedForPluginInterface("e1")) self.assertIn("e1", ext_mgr.extensions) def test_extension_loaded_for_non_core_plugin(self): class NonCorePluginExtenstion(ext_stubs.StubExtension): def get_plugin_interface(self): return None stub_plugin = ext_stubs.StubPlugin(supported_extensions=["e1"]) plugin_info = {dummy_plugin.DUMMY_SERVICE_TYPE: stub_plugin} with mock.patch("neutron.api.extensions.PluginAwareExtensionManager." "check_if_plugin_extensions_loaded"): ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info) ext_mgr.add_extension(NonCorePluginExtenstion("e1")) self.assertIn("e1", ext_mgr.extensions) def test_unloaded_supported_extensions_raises_exception(self): stub_plugin = ext_stubs.StubPlugin( supported_extensions=["unloaded_extension"]) plugin_info = {lib_const.CORE: stub_plugin} self.assertRaises(exceptions.ExtensionsNotFound, extensions.PluginAwareExtensionManager, '', plugin_info) def test_custom_supported_implementation(self): self.useFixture(CustomExtensionCheckMapMemento()) class FakePlugin(object): pass class FakeExtension(ext_stubs.StubExtension): extensions.register_custom_supported_check( 'stub_extension', lambda: True, plugin_agnostic=True ) ext = FakeExtension() plugin_info = {lib_const.CORE: FakePlugin()} ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info) ext_mgr.add_extension(ext) self.assertIn("stub_extension", ext_mgr.extensions) extensions.register_custom_supported_check( 'stub_extension', lambda: False, plugin_agnostic=True ) ext_mgr = extensions.PluginAwareExtensionManager('', plugin_info) ext_mgr.add_extension(ext) self.assertNotIn("stub_extension", ext_mgr.extensions) def test_custom_supported_implementation_plugin_specific(self): self.useFixture(CustomExtensionCheckMapMemento()) class FakePlugin(object): pass class FakeExtension(ext_stubs.StubExtension): extensions.register_custom_supported_check( 'stub_plugin_extension', lambda: True, plugin_agnostic=False ) plugin_info = {lib_const.CORE: FakePlugin()} self.assertRaises( exceptions.ExtensionsNotFound, extensions.PluginAwareExtensionManager, '', plugin_info) class ExtensionControllerTest(testlib_api.WebTestCase): def setUp(self): super(ExtensionControllerTest, self).setUp() self.test_app = _setup_extensions_test_app() def test_index_gets_all_registerd_extensions(self): response = self.test_app.get("/extensions." + self.fmt) res_body = self.deserialize(response) foxnsox = res_body["extensions"][0] self.assertEqual(foxnsox["alias"], "FOXNSOX") def test_extension_can_be_accessed_by_alias(self): response = self.test_app.get("/extensions/FOXNSOX." + self.fmt) foxnsox_extension = self.deserialize(response) foxnsox_extension = foxnsox_extension['extension'] self.assertEqual(foxnsox_extension["alias"], "FOXNSOX") def test_show_returns_not_found_for_non_existent_extension(self): response = self.test_app.get("/extensions/non_existent" + self.fmt, status="*") self.assertEqual(response.status_int, 404) def app_factory(global_conf, **local_conf): conf = global_conf.copy() conf.update(local_conf) return ExtensionsTestApp(conf) def setup_base_app(test): base.BaseTestCase.config_parse() app = config.load_paste_app('extensions_test_app') return app def setup_extensions_middleware(extension_manager=None): extension_manager = (extension_manager or extensions.PluginAwareExtensionManager( extensions_path, {lib_const.CORE: FakePluginWithExtension()})) base.BaseTestCase.config_parse() app = config.load_paste_app('extensions_test_app') return extensions.ExtensionMiddleware(app, ext_mgr=extension_manager) def _setup_extensions_test_app(extension_manager=None): return webtest.TestApp(setup_extensions_middleware(extension_manager)) class SimpleExtensionManager(object): def __init__(self, resource_ext=None, action_ext=None, request_ext=None): self.resource_ext = resource_ext self.action_ext = action_ext self.request_ext = request_ext def get_resources(self): resource_exts = [] if self.resource_ext: resource_exts.append(self.resource_ext) return resource_exts def get_actions(self): action_exts = [] if self.action_ext: action_exts.append(self.action_ext) return action_exts def get_request_extensions(self): request_extensions = [] if self.request_ext: request_extensions.append(self.request_ext) return request_extensions class ExtensionExtendedAttributeTestPlugin(object): supported_extension_aliases = [ 'ext-obj-test', "extended-ext-attr" ] def __init__(self, configfile=None): super(ExtensionExtendedAttributeTestPlugin, self) self.objs = [] self.objh = {} def create_ext_test_resource(self, context, ext_test_resource): obj = ext_test_resource['ext_test_resource'] id = _uuid() obj['id'] = id self.objs.append(obj) self.objh.update({id: obj}) return obj def get_ext_test_resources(self, context, filters=None, fields=None): return self.objs def get_ext_test_resource(self, context, id, fields=None): return self.objh[id] class ExtensionExtendedAttributeTestCase(base.BaseTestCase): def setUp(self): super(ExtensionExtendedAttributeTestCase, self).setUp() plugin = ( "neutron.tests.unit.api.test_extensions." "ExtensionExtendedAttributeTestPlugin" ) # point config file to: neutron/tests/etc/neutron.conf self.config_parse() self.setup_coreplugin(plugin) ext_mgr = extensions.PluginAwareExtensionManager( extensions_path, {lib_const.CORE: ExtensionExtendedAttributeTestPlugin()} ) ext_mgr.extend_resources("2.0", {}) extensions.PluginAwareExtensionManager._instance = ext_mgr app = config.load_paste_app('extensions_test_app') self._api = extensions.ExtensionMiddleware(app, ext_mgr=ext_mgr) self._tenant_id = "8c70909f-b081-452d-872b-df48e6c355d1" self.agentscheduler_dbMinxin = directory.get_plugin() quota.QUOTAS._driver = None cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', group='QUOTAS') def _do_request(self, method, path, data=None, params=None, action=None): content_type = 'application/json' body = None if data is not None: # empty dict is valid body = wsgi.Serializer().serialize(data, content_type) req = testlib_api.create_request( path, body, content_type, method, query_string=params) res = req.get_response(self._api) if res.status_code >= 400: raise webexc.HTTPClientError(detail=res.body, code=res.status_code) if res.status_code != webexc.HTTPNoContent.code: return res.json def _ext_test_resource_create(self, attr=None): data = { "ext_test_resource": { "tenant_id": self._tenant_id, "name": "test", extattr.EXTENDED_ATTRIBUTE: attr } } res = self._do_request('POST', _get_path('ext_test_resources'), data) return res['ext_test_resource'] def test_ext_test_resource_create(self): ext_test_resource = self._ext_test_resource_create() attr = _uuid() ext_test_resource = self._ext_test_resource_create(attr) self.assertEqual(ext_test_resource[extattr.EXTENDED_ATTRIBUTE], attr) def test_ext_test_resource_get(self): attr = _uuid() obj = self._ext_test_resource_create(attr) obj_id = obj['id'] res = self._do_request('GET', _get_path( 'ext_test_resources/{0}'.format(obj_id))) obj2 = res['ext_test_resource'] self.assertEqual(obj2[extattr.EXTENDED_ATTRIBUTE], attr) neutron-12.1.1/neutron/tests/unit/core_extensions/0000775000175000017500000000000013553660157022345 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/core_extensions/__init__.py0000664000175000017500000000000013553660046024441 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/core_extensions/test_qos.py0000664000175000017500000004127013553660047024562 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from neutron_lib.plugins import constants as plugin_constants from neutron_lib.services.qos import constants as qos_consts from oslo_utils import uuidutils from neutron.common import exceptions as n_exc from neutron.core_extensions import base as base_core from neutron.core_extensions import qos as qos_core from neutron.objects.qos import policy from neutron.tests import base def _get_test_dbdata(qos_policy_id): return {'id': None, 'qos_policy_binding': {'policy_id': qos_policy_id, 'network_id': 'fake_net_id'}} class QosCoreResourceExtensionTestCase(base.BaseTestCase): def setUp(self): super(QosCoreResourceExtensionTestCase, self).setUp() self.core_extension = qos_core.QosCoreResourceExtension() policy_p = mock.patch('neutron.objects.qos.policy.QosPolicy') self.policy_m = policy_p.start() self.context = context.get_admin_context() self.non_admin_context = context.Context('user_id', 'tenant_id') def test_process_fields_no_qos_policy_id(self): self.core_extension.process_fields( self.context, base_core.PORT, mock.ANY, {}, None) self.assertFalse(self.policy_m.called) def _mock_plugin_loaded(self, plugin_loaded): plugins = {} if plugin_loaded: plugins[plugin_constants.QOS] = None return mock.patch('neutron_lib.plugins.directory.get_plugins', return_value=plugins) def test_process_fields_no_qos_plugin_loaded(self): with self._mock_plugin_loaded(False): self.core_extension.process_fields( self.context, base_core.PORT, mock.ANY, {qos_consts.QOS_POLICY_ID: None}, None) self.assertFalse(self.policy_m.called) def test_process_fields_port_new_policy(self): with self._mock_plugin_loaded(True): qos_policy_id = mock.Mock() actual_port = {'id': mock.Mock(), qos_consts.QOS_POLICY_ID: qos_policy_id} qos_policy = mock.MagicMock() self.policy_m.get_object = mock.Mock(return_value=qos_policy) self.core_extension.process_fields( self.context, base_core.PORT, base_core.EVENT_UPDATE, {qos_consts.QOS_POLICY_ID: qos_policy_id}, actual_port) qos_policy.attach_port.assert_called_once_with(actual_port['id']) def test_process_fields_port_updated_policy(self): with self._mock_plugin_loaded(True): qos_policy1_id = mock.Mock() qos_policy2_id = mock.Mock() port_id = mock.Mock() actual_port = {'id': port_id, qos_consts.QOS_POLICY_ID: qos_policy1_id} old_qos_policy = mock.MagicMock() self.policy_m.get_port_policy = mock.Mock( return_value=old_qos_policy) new_qos_policy = mock.MagicMock() self.policy_m.get_object = mock.Mock(return_value=new_qos_policy) self.core_extension.process_fields( self.context, base_core.PORT, base_core.EVENT_UPDATE, {qos_consts.QOS_POLICY_ID: qos_policy2_id}, actual_port) old_qos_policy.detach_port.assert_called_once_with(port_id) new_qos_policy.attach_port.assert_called_once_with(port_id) self.assertEqual(qos_policy2_id, actual_port['qos_policy_id']) def test_process_resource_port_updated_no_policy(self): with self._mock_plugin_loaded(True): port_id = mock.Mock() qos_policy_id = mock.Mock() actual_port = {'id': port_id, qos_consts.QOS_POLICY_ID: qos_policy_id} old_qos_policy = mock.MagicMock() self.policy_m.get_port_policy = mock.Mock( return_value=old_qos_policy) new_qos_policy = mock.MagicMock() self.policy_m.get_object = mock.Mock(return_value=new_qos_policy) self.core_extension.process_fields( self.context, base_core.PORT, base_core.EVENT_UPDATE, {qos_consts.QOS_POLICY_ID: None}, actual_port) old_qos_policy.detach_port.assert_called_once_with(port_id) self.assertIsNone(actual_port['qos_policy_id']) def _process_port_updated_policy(self, context, shared, policy_tenant_id): with self._mock_plugin_loaded(True): port_id = mock.sentinel.port_id qos_policy_id = mock.sentinel.policy_id actual_port = {'id': port_id, qos_consts.QOS_POLICY_ID: qos_policy_id} old_qos_policy = mock.MagicMock() old_qos_policy.shared = shared old_qos_policy.tenant_id = policy_tenant_id self.policy_m.get_port_policy = mock.Mock( return_value=old_qos_policy) self.core_extension.process_fields( context, base_core.PORT, base_core.EVENT_UPDATE, {qos_consts.QOS_POLICY_ID: None}, actual_port) old_qos_policy.detach_port.assert_called_once_with(port_id) def test_process_resource_port_updated_remove_own_policy(self): self._process_port_updated_policy( context=self.non_admin_context, shared=False, policy_tenant_id=self.non_admin_context.tenant_id) def test_process_resource_port_updated_admin_remove_provided_policy(self): self._process_port_updated_policy( context=self.context, shared=False, policy_tenant_id=self.non_admin_context.tenant_id) def test_process_resource_port_updated_remove_shared_policy(self): self._process_port_updated_policy( context=self.non_admin_context, shared=True, policy_tenant_id=self.context.tenant_id) def test_process_resource_port_updated_remove_provided_policy(self): self.policy_m.is_accessible.return_value = False self.assertRaises(n_exc.PolicyRemoveAuthorizationError, self._process_port_updated_policy, context=self.non_admin_context, shared=False, policy_tenant_id=self.context.tenant_id) def test_process_resource_update_network_updated_no_policy(self): with self._mock_plugin_loaded(True): network_id = mock.Mock() qos_policy_id = mock.Mock() actual_network = {'id': network_id, qos_consts.QOS_POLICY_ID: qos_policy_id} old_qos_policy = mock.MagicMock() self.policy_m.get_network_policy = mock.Mock( return_value=old_qos_policy) new_qos_policy = mock.MagicMock() self.policy_m.get_object = mock.Mock(return_value=new_qos_policy) self.core_extension.process_fields( self.context, base_core.NETWORK, base_core.EVENT_UPDATE, {qos_consts.QOS_POLICY_ID: None}, actual_network) old_qos_policy.detach_network.assert_called_once_with(network_id) self.assertIsNone(actual_network['qos_policy_id']) def test_process_fields_update_network_new_policy(self): with self._mock_plugin_loaded(True): qos_policy_id = mock.Mock() actual_network = {'id': mock.Mock(), qos_consts.QOS_POLICY_ID: qos_policy_id} qos_policy = mock.MagicMock() self.policy_m.get_object = mock.Mock(return_value=qos_policy) self.core_extension.process_fields( self.context, base_core.NETWORK, base_core.EVENT_UPDATE, {qos_consts.QOS_POLICY_ID: qos_policy_id}, actual_network) qos_policy.attach_network.assert_called_once_with( actual_network['id']) def test_process_fields_update_network_updated_policy(self): with self._mock_plugin_loaded(True): qos_policy_id = mock.Mock() network_id = mock.Mock() actual_network = {'id': network_id, qos_consts.QOS_POLICY_ID: qos_policy_id} old_qos_policy = mock.MagicMock() self.policy_m.get_network_policy = mock.Mock( return_value=old_qos_policy) new_qos_policy = mock.MagicMock() self.policy_m.get_object = mock.Mock(return_value=new_qos_policy) self.core_extension.process_fields( self.context, base_core.NETWORK, base_core.EVENT_UPDATE, {qos_consts.QOS_POLICY_ID: qos_policy_id}, actual_network) old_qos_policy.detach_network.assert_called_once_with(network_id) new_qos_policy.attach_network.assert_called_once_with(network_id) def _process_network_updated_policy(self, context, shared, policy_tenant_id): with self._mock_plugin_loaded(True): qos_policy_id = mock.sentinel.policy_id network_id = mock.sentinel.net_id actual_network = {'id': network_id, qos_consts.QOS_POLICY_ID: qos_policy_id} old_qos_policy = mock.MagicMock() old_qos_policy.shared = shared old_qos_policy.tenant_id = policy_tenant_id self.policy_m.get_network_policy.return_value = old_qos_policy self.core_extension.process_fields( context, base_core.NETWORK, base_core.EVENT_UPDATE, {qos_consts.QOS_POLICY_ID: None}, actual_network) old_qos_policy.detach_network.assert_called_once_with(network_id) def test_process_fields_update_network_updated_remove_shared_policy(self): self._process_network_updated_policy( context=self.non_admin_context, shared=True, policy_tenant_id=self.context.tenant_id) def test_process_fields_network_updated_remove_own_policy(self): self._process_network_updated_policy( context=self.non_admin_context, shared=True, policy_tenant_id=self.non_admin_context.tenant_id) def test_process_fields_update_network_admin_remove_provided_policy(self): self._process_network_updated_policy( context=self.context, shared=True, policy_tenant_id=self.non_admin_context.tenant_id) def test_process_fields_update_network_remove_provided_policy(self): self.policy_m.is_accessible.return_value = False self.assertRaises(n_exc.PolicyRemoveAuthorizationError, self._process_network_updated_policy, context=self.non_admin_context, shared=False, policy_tenant_id=self.context.tenant_id) def test_process_fields_create_network(self): with self._mock_plugin_loaded(True): qos_policy_id = mock.Mock() network_id = mock.Mock() actual_network = {'id': network_id, qos_consts.QOS_POLICY_ID: qos_policy_id} self.policy_m.get_network_policy = mock.Mock( return_value=qos_policy_id) qos_policy = mock.MagicMock() self.policy_m.get_object = mock.Mock(return_value=qos_policy) self.core_extension.process_fields( self.context, base_core.NETWORK, base_core.EVENT_CREATE, actual_network, actual_network) qos_policy.attach_network.assert_called_once_with(network_id) def test_process_fields_create_network_no_policy(self): with self._mock_plugin_loaded(True): project_id = mock.Mock() network_id = mock.Mock() actual_network = {'project_id': project_id, 'id': network_id, qos_consts.QOS_POLICY_ID: None} qos_policy_id = mock.Mock() qos_policy = mock.MagicMock() with mock.patch.object(policy.QosPolicyDefault, "get_object", return_value=qos_policy_id) as mock_get_default_policy_id: self.policy_m.get_object = mock.Mock(return_value=qos_policy) self.core_extension.process_fields( self.context, base_core.NETWORK, base_core.EVENT_CREATE, actual_network, actual_network) qos_policy.attach_network.assert_called_once_with(network_id) mock_get_default_policy_id.assert_called_once_with( self.context, project_id=project_id) def test_process_fields_create_network_no_default_policy(self): with self._mock_plugin_loaded(True): project_id = mock.Mock() network_id = mock.Mock() actual_network = {'project_id': project_id, 'id': network_id, qos_consts.QOS_POLICY_ID: None} qos_policy = mock.MagicMock() with mock.patch.object(policy.QosPolicyDefault, "get_object", return_value=None) as mock_get_default_policy_id: self.policy_m.get_object = mock.Mock(return_value=qos_policy) self.core_extension.process_fields( self.context, base_core.NETWORK, base_core.EVENT_CREATE, actual_network, actual_network) qos_policy.attach_network.assert_not_called() mock_get_default_policy_id.assert_called_once_with( self.context, project_id=project_id) def test_extract_fields_plugin_not_loaded(self): with self._mock_plugin_loaded(False): fields = self.core_extension.extract_fields(None, None) self.assertEqual({}, fields) def _test_extract_fields_for_port(self, qos_policy_id): with self._mock_plugin_loaded(True): fields = self.core_extension.extract_fields( base_core.PORT, _get_test_dbdata(qos_policy_id)) self.assertEqual({qos_consts.QOS_POLICY_ID: qos_policy_id}, fields) def test_extract_fields_no_port_policy(self): self._test_extract_fields_for_port(None) def test_extract_fields_port_policy_exists(self): qos_policy_id = mock.Mock() self._test_extract_fields_for_port(qos_policy_id) def _test_extract_fields_for_network(self, qos_policy_id): with self._mock_plugin_loaded(True): fields = self.core_extension.extract_fields( base_core.NETWORK, _get_test_dbdata(qos_policy_id)) self.assertEqual({qos_consts.QOS_POLICY_ID: qos_policy_id}, fields) def test_extract_fields_no_network_policy(self): self._test_extract_fields_for_network(None) def test_extract_fields_network_policy_exists(self): qos_policy_id = mock.Mock() qos_policy = mock.Mock() qos_policy.id = qos_policy_id self._test_extract_fields_for_network(qos_policy_id) def test__create_network_policy(self): default_policy_id = uuidutils.generate_uuid() network_policy_id = uuidutils.generate_uuid() policy_mock = mock.MagicMock(qos_policy_id=default_policy_id) network_changes = mock.Mock() network = {'id': 'dummy_id', 'project_id': 'dummy_project', qos_consts.QOS_POLICY_ID: None} with mock.patch.object(policy.QosPolicyDefault, 'get_object', return_value=policy_mock),\ mock.patch.object(policy.QosPolicy, 'get_object'): # Creating network with policy id network_changes.get.return_value = network_policy_id self.core_extension._create_network_policy( self.context, network, network_changes) self.assertEqual(network_policy_id, network[qos_consts.QOS_POLICY_ID]) # Creating network without policy id network_changes.get.return_value = None self.core_extension._create_network_policy( self.context, network, network_changes) self.assertEqual(default_policy_id, network[qos_consts.QOS_POLICY_ID]) neutron-12.1.1/neutron/tests/unit/tests/0000775000175000017500000000000013553660157020300 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/example/0000775000175000017500000000000013553660157021733 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/example/dir/0000775000175000017500000000000013553660157022511 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/example/dir/subdir/0000775000175000017500000000000013553660157024001 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/example/dir/subdir/__init__.py0000664000175000017500000000000013553660046026075 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/example/dir/subdir/example_module.py0000664000175000017500000000000013553660046027336 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/example/dir/__init__.py0000664000175000017500000000000013553660046024605 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/example/dir/example_module.py0000664000175000017500000000000013553660046026046 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/example/__init__.py0000664000175000017500000000000013553660046024027 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/example/README0000664000175000017500000000014013553660046022603 0ustar zuulzuul00000000000000This directory is used by: neutron.tests.unit.tests.test_tools.ImportModulesRecursivelyTestCase neutron-12.1.1/neutron/tests/unit/tests/test_base.py0000664000175000017500000000575213553660046022631 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Tests to test the test framework""" import sys import eventlet.timeout import unittest2 from neutron.tests import base class BrokenExceptionHandlerTestCase(base.DietTestCase): # Embedded to hide from the regular test discovery class MyTestCase(base.DietTestCase): def setUp(self): super(BrokenExceptionHandlerTestCase.MyTestCase, self).setUp() self.addOnException(self._diag_collect) def _diag_collect(self, exc_info): raise ValueError('whoopsie daisy') def runTest(self): raise IndexError("Thou shalt not pass by reference") def test_broken_exception_handler(self): result = self.MyTestCase().run() # ensure both exceptions are logged self.assertIn('Thou shalt', result.errors[0][1]) self.assertIn('whoopsie', result.errors[0][1]) self.assertFalse(result.wasSuccessful()) class SystemExitTestCase(base.DietTestCase): # Embedded to hide from the regular test discovery class MyTestCase(base.DietTestCase): def __init__(self, exitcode): super(SystemExitTestCase.MyTestCase, self).__init__() self.exitcode = exitcode def runTest(self): if self.exitcode is not None: sys.exit(self.exitcode) def test_no_sysexit(self): result = self.MyTestCase(exitcode=None).run() self.assertTrue(result.wasSuccessful()) def test_sysexit(self): expectedFails = [self.MyTestCase(exitcode) for exitcode in (0, 1)] suite = unittest2.TestSuite(tests=expectedFails) result = self.defaultTestResult() try: suite.run(result) except SystemExit: self.fail('SystemExit escaped!') self.assertEqual([], result.errors) self.assertItemsEqual(set(id(t) for t in expectedFails), set(id(t) for (t, traceback) in result.failures)) class CatchTimeoutTestCase(base.DietTestCase): # Embedded to hide from the regular test discovery class MyTestCase(base.DietTestCase): def test_case(self): raise eventlet.Timeout() def runTest(self): return self.test_case() def test_catch_timeout(self): try: result = self.MyTestCase().run() self.assertFalse(result.wasSuccessful()) except eventlet.Timeout: self.fail('Timeout escaped!') neutron-12.1.1/neutron/tests/unit/tests/__init__.py0000664000175000017500000000000013553660046022374 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/functional/0000775000175000017500000000000013553660157022442 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/functional/test_base.py0000664000175000017500000000430513553660046024764 0ustar zuulzuul00000000000000# Copyright 2019 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg from neutron.tests import base from neutron.tests.functional import base as functional_base NEW_CONFIG_GROUP = cfg.OptGroup('testgroup', title='Test wrapping cfg register') SOME_OPTIONS = [ cfg.StrOpt('str_opt', default='default_value'), cfg.StrOpt('int_opt', default=1), cfg.BoolOpt('bool_opt', default=True) ] def register_some_options(cfg=cfg.CONF): cfg.register_opts(SOME_OPTIONS, 'testgroup') class ConfigDecoratorTestCase(base.BaseTestCase): def setUp(self): super(ConfigDecoratorTestCase, self).setUp() cfg.CONF.register_group(NEW_CONFIG_GROUP) def test_no_config_decorator(self): register_some_options() self.assertEqual('default_value', cfg.CONF.testgroup.str_opt) self.assertEqual('1', cfg.CONF.testgroup.int_opt) self.assertTrue(cfg.CONF.testgroup.bool_opt) def test_override_variables(self): opts = [('str_opt', 'another_value', 'testgroup'), ('int_opt', 123, 'testgroup'), ('bool_opt', False, 'testgroup')] cfg_decorator = functional_base.config_decorator(register_some_options, opts) mock.patch('neutron.tests.unit.tests.functional.test_base.' 'register_some_options', new=cfg_decorator).start() register_some_options() self.assertEqual('another_value', cfg.CONF.testgroup.str_opt) self.assertEqual('123', cfg.CONF.testgroup.int_opt) self.assertFalse(cfg.CONF.testgroup.bool_opt) neutron-12.1.1/neutron/tests/unit/tests/functional/__init__.py0000664000175000017500000000000013553660046024536 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/common/0000775000175000017500000000000013553660157021570 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/common/__init__.py0000664000175000017500000000000013553660046023664 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/tests/common/test_net_helpers.py0000664000175000017500000000661213553660047025514 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as n_const from neutron.tests import base from neutron.tests.common import net_helpers ss_output = """ State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 10 127.0.0.1:6640 *:* LISTEN 0 128 *:46675 *:* LISTEN 0 128 *:22 *:* LISTEN 0 128 *:5432 *:* LISTEN 0 128 *:3260 *:* LISTEN 0 50 *:3306 *:* ESTAB 0 36 10.0.0.202:22 10.0.0.44:45258 ESTAB 0 0 127.0.0.1:32965 127.0.0.1:4369 ESTAB 0 0 10.0.0.202:22 10.0.0.44:36104 LISTEN 0 128 :::80 :::* LISTEN 0 128 :::4369 :::* LISTEN 0 128 :::22 :::* LISTEN 0 128 :::5432 :::* LISTEN 0 128 :::3260 :::* LISTEN 0 128 :::5672 :::* ESTAB 0 0 ::ffff:127.0.0.1:4369 ::ffff:127.0.0.1:32965 """ ss_output_template = """ LISTEN 0 10 127.0.0.1:%d *:* """ class PortAllocationTestCase(base.DietTestCase): def test__get_source_ports_from_ss_output(self): result = net_helpers._get_source_ports_from_ss_output(ss_output) expected = {6640, 46675, 5432, 3260, 3306, 22, 32965, 4369, 5672, 80} self.assertEqual(expected, result) def test_get_free_namespace_port(self): ss_output2 = ss_output for p in range(1024, 32767): ss_output2 += ss_output_template % p with mock.patch('neutron.agent.linux.ip_lib.IPWrapper') \ as ipwrapper, \ mock.patch('neutron.agent.linux.utils.execute') as ex: m = mock.MagicMock() m.netns.execute.return_value = ss_output2 ipwrapper.return_value = m local_port_range_start = 32768 ex.return_value = "%s\t61000" % local_port_range_start result = net_helpers.get_free_namespace_port( n_const.PROTO_NAME_TCP) self.assertEqual((local_port_range_start - 1), result) def test_get_unused_port(self): with mock.patch('neutron.agent.linux.utils.execute') as ex: ex.return_value = "2048\t61000" result = net_helpers.get_unused_port(set(range(1025, 2048))) self.assertEqual(1024, result) neutron-12.1.1/neutron/tests/unit/tests/test_post_mortem_debug.py0000664000175000017500000000764513553660046025440 0ustar zuulzuul00000000000000# Copyright 2013 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys import mock from six import moves from neutron.tests import base from neutron.tests import post_mortem_debug class TestTesttoolsExceptionHandler(base.BaseTestCase): def test_exception_handler(self): try: self.fail() except Exception: exc_info = sys.exc_info() with mock.patch('traceback.print_exception') as mock_print_exception: with mock.patch('pdb.post_mortem') as mock_post_mortem: with mock.patch.object(post_mortem_debug, 'get_ignored_traceback', return_value=mock.Mock()): post_mortem_debug.get_exception_handler('pdb')(exc_info) # traceback will become post_mortem_debug.FilteredTraceback filtered_exc_info = (exc_info[0], exc_info[1], mock.ANY) mock_print_exception.assert_called_once_with(*filtered_exc_info) mock_post_mortem.assert_called_once_with(mock.ANY) def test__get_debugger(self): def import_mock(name, *args): mod_mock = mock.Mock() mod_mock.__name__ = name mod_mock.post_mortem = mock.Mock() return mod_mock with mock.patch('six.moves.builtins.__import__', side_effect=import_mock): pdb_debugger = post_mortem_debug._get_debugger('pdb') pudb_debugger = post_mortem_debug._get_debugger('pudb') self.assertEqual('pdb', pdb_debugger.__name__) self.assertEqual('pudb', pudb_debugger.__name__) class TestFilteredTraceback(base.BaseTestCase): def test_filter_traceback(self): tb1 = mock.Mock() tb2 = mock.Mock() tb1.tb_next = tb2 tb2.tb_next = None ftb1 = post_mortem_debug.FilteredTraceback(tb1, tb2) for attr in ['lasti', 'lineno', 'frame']: attr_name = 'tb_%s' % attr self.assertEqual(getattr(tb1, attr_name, None), getattr(ftb1, attr_name, None)) self.assertIsNone(ftb1.tb_next) class TestGetIgnoredTraceback(base.BaseTestCase): def _test_get_ignored_traceback(self, ignored_bit_array, expected): root_tb = mock.Mock() tb = root_tb tracebacks = [tb] for x in moves.range(len(ignored_bit_array) - 1): tb.tb_next = mock.Mock() tb = tb.tb_next tracebacks.append(tb) tb.tb_next = None tb = root_tb for ignored in ignored_bit_array: if ignored: tb.tb_frame.f_globals = ['__unittest'] else: tb.tb_frame.f_globals = [] tb = tb.tb_next actual = post_mortem_debug.get_ignored_traceback(root_tb) if expected is not None: expected = tracebacks[expected] self.assertEqual(expected, actual) def test_no_ignored_tracebacks(self): self._test_get_ignored_traceback([0, 0, 0], None) def test_single_member_trailing_chain(self): self._test_get_ignored_traceback([0, 0, 1], 2) def test_two_member_trailing_chain(self): self._test_get_ignored_traceback([0, 1, 1], 1) def test_first_traceback_ignored(self): self._test_get_ignored_traceback([1, 0, 0], None) def test_middle_traceback_ignored(self): self._test_get_ignored_traceback([0, 1, 0], None) neutron-12.1.1/neutron/tests/unit/db/0000775000175000017500000000000013553660157017523 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/db/test_l3_db.py0000664000175000017500000003605513553660047022126 0ustar zuulzuul00000000000000# Copyright 2015 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock import netaddr from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as n_const from neutron_lib import context from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import l3 as l3_exc from neutron_lib.plugins import directory from oslo_utils import uuidutils import testtools from neutron.db import l3_db from neutron.db.models import l3 as l3_models from neutron.objects import router as l3_obj from neutron.tests import base class TestL3_NAT_dbonly_mixin(base.BaseTestCase): def setUp(self): super(TestL3_NAT_dbonly_mixin, self).setUp() self.db = l3_db.L3_NAT_dbonly_mixin() def test__each_port_having_fixed_ips_none(self): """Be sure the method returns an empty list when None is passed""" filtered = l3_db.L3_NAT_dbonly_mixin._each_port_having_fixed_ips(None) self.assertEqual([], list(filtered)) def test__new__passes_args(self): class T(l3_db.L3_NAT_db_mixin): def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs t = T(1, 2, a=3) self.assertEqual((1, 2), t.args) self.assertEqual({'a': 3}, t.kwargs) def test__each_port_having_fixed_ips(self): """Basic test that ports without fixed ips are filtered out""" ports = [{'id': 'a', 'fixed_ips': [mock.sentinel.fixedip]}, {'id': 'b'}] filtered = l3_db.L3_NAT_dbonly_mixin._each_port_having_fixed_ips(ports) ids = [p['id'] for p in filtered] self.assertEqual(['a'], ids) def test__get_subnets_by_network_no_query(self): """Basic test that no query is performed if no Ports are passed""" context = mock.Mock() with mock.patch.object(directory, 'get_plugin') as get_p: self.db._get_subnets_by_network_list(context, []) self.assertFalse(context.session.query.called) self.assertFalse(get_p.called) def test__get_subnets_by_network(self): """Basic test that the right query is called""" context = mock.MagicMock() query = context.session.query().outerjoin().filter() query.__iter__.return_value = [(mock.sentinel.subnet_db, mock.sentinel.address_scope_id)] with mock.patch.object(directory, 'get_plugin') as get_p: get_p()._make_subnet_dict.return_value = { 'network_id': mock.sentinel.network_id} subnets = self.db._get_subnets_by_network_list( context, [mock.sentinel.network_id]) self.assertEqual({ mock.sentinel.network_id: [{ 'address_scope_id': mock.sentinel.address_scope_id, 'network_id': mock.sentinel.network_id}]}, subnets) def test__get_mtus_by_network_list(self): """Basic test that the query get_networks is correctly""" network = {'id': mock.sentinel.network_id, 'name': mock.sentinel.name, 'mtu': mock.sentinel.mtu} with mock.patch.object(directory, 'get_plugin') as get_p: get_p().get_networks.return_value = [network] result = self.db._get_mtus_by_network_list( mock.sentinel.context, [mock.sentinel.network_id]) get_p().get_networks.assert_called_once_with( mock.sentinel.context, filters={'id': [mock.sentinel.network_id]}, fields=['id', 'mtu']) self.assertEqual({mock.sentinel.network_id: mock.sentinel.mtu}, result) def test__populate_ports_for_subnets_none(self): """Basic test that the method runs correctly with no ports""" ports = [] with mock.patch.object(directory, 'get_plugin') as get_p: get_p().get_networks.return_value = [] self.db._populate_mtu_and_subnets_for_ports(mock.sentinel.context, ports) self.assertEqual([], ports) @mock.patch.object(l3_db.L3_NAT_dbonly_mixin, '_get_subnets_by_network_list') def test__populate_ports_for_subnets(self, get_subnets_by_network): cidr = "2001:db8::/64" subnet = {'id': mock.sentinel.subnet_id, 'cidr': cidr, 'gateway_ip': mock.sentinel.gateway_ip, 'dns_nameservers': mock.sentinel.dns_nameservers, 'ipv6_ra_mode': mock.sentinel.ipv6_ra_mode, 'subnetpool_id': mock.sentinel.subnetpool_id, 'address_scope_id': mock.sentinel.address_scope_id} get_subnets_by_network.return_value = {'net_id': [subnet]} ports = [{'network_id': 'net_id', 'id': 'port_id', 'fixed_ips': [{'subnet_id': mock.sentinel.subnet_id}]}] with mock.patch.object(directory, 'get_plugin') as get_p: get_p().get_networks.return_value = [{'id': 'net_id', 'mtu': 1446}] self.db._populate_mtu_and_subnets_for_ports(mock.sentinel.context, ports) keys = ('id', 'cidr', 'gateway_ip', 'ipv6_ra_mode', 'subnetpool_id', 'dns_nameservers') address_scopes = {4: None, 6: mock.sentinel.address_scope_id} self.assertEqual([{'extra_subnets': [], 'fixed_ips': [{'subnet_id': mock.sentinel.subnet_id, 'prefixlen': 64}], 'id': 'port_id', 'mtu': 1446, 'network_id': 'net_id', 'subnets': [{k: subnet[k] for k in keys}], 'address_scopes': address_scopes}], ports) def test__get_sync_floating_ips_no_query(self): """Basic test that no query is performed if no router ids are passed""" db = l3_db.L3_NAT_dbonly_mixin() context = mock.Mock() db._get_sync_floating_ips(context, []) self.assertFalse(context.session.query.called) @mock.patch.object(l3_db.L3_NAT_dbonly_mixin, '_make_floatingip_dict') def test__make_floatingip_dict_with_scope(self, make_fip_dict): db = l3_db.L3_NAT_dbonly_mixin() make_fip_dict.return_value = {'id': mock.sentinel.fip_ip} result = db._make_floatingip_dict_with_scope( mock.sentinel.floating_ip_db, mock.sentinel.address_scope_id) self.assertEqual({ 'fixed_ip_address_scope': mock.sentinel.address_scope_id, 'id': mock.sentinel.fip_ip}, result) def test__unique_floatingip_iterator(self): context = mock.MagicMock() query = mock.MagicMock() query.order_by().__iter__.return_value = [ ({'id': 'id1'}, 'scope1'), ({'id': 'id1'}, 'scope1'), ({'id': 'id2'}, 'scope2'), ({'id': 'id2'}, 'scope2'), ({'id': 'id2'}, 'scope2'), ({'id': 'id3'}, 'scope3')] query.reset_mock() with mock.patch.object( l3_obj.FloatingIP, '_load_object', side_effect=({'id': 'id1'}, {'id': 'id2'}, {'id': 'id3'})): result = list( l3_obj.FloatingIP._unique_floatingip_iterator(context, query)) query.order_by.assert_called_once_with(l3_models.FloatingIP.id) self.assertEqual([({'id': 'id1'}, 'scope1'), ({'id': 'id2'}, 'scope2'), ({'id': 'id3'}, 'scope3')], result) @mock.patch.object(directory, 'get_plugin') def test_prevent_l3_port_deletion_port_not_found(self, gp): # port not found doesn't prevent gp.return_value.get_port.side_effect = n_exc.PortNotFound(port_id='1') self.db.prevent_l3_port_deletion(None, None) @mock.patch.object(directory, 'get_plugin') def test_prevent_l3_port_device_owner_not_router(self, gp): # ignores other device owners gp.return_value.get_port.return_value = {'device_owner': 'cat'} self.db.prevent_l3_port_deletion(None, None) @mock.patch.object(directory, 'get_plugin') def test_prevent_l3_port_no_fixed_ips(self, gp): # without fixed IPs is allowed gp.return_value.get_port.return_value = { 'device_owner': n_const.DEVICE_OWNER_ROUTER_INTF, 'fixed_ips': [], 'id': 'f' } self.db.prevent_l3_port_deletion(None, None) @mock.patch.object(directory, 'get_plugin') def test_prevent_l3_port_no_router(self, gp): # without router is allowed gp.return_value.get_port.return_value = { 'device_owner': n_const.DEVICE_OWNER_ROUTER_INTF, 'device_id': '44', 'id': 'f', 'fixed_ips': [{'ip_address': '1.1.1.1', 'subnet_id': '4'}]} self.db.get_router = mock.Mock() self.db.get_router.side_effect = l3_exc.RouterNotFound(router_id='44') self.db.prevent_l3_port_deletion(mock.Mock(), None) @mock.patch.object(directory, 'get_plugin') def test_prevent_l3_port_existing_router(self, gp): gp.return_value.get_port.return_value = { 'device_owner': n_const.DEVICE_OWNER_ROUTER_INTF, 'device_id': 'some_router', 'id': 'f', 'fixed_ips': [{'ip_address': '1.1.1.1', 'subnet_id': '4'}]} self.db.get_router = mock.Mock() with testtools.ExpectedException(n_exc.ServicePortInUse): self.db.prevent_l3_port_deletion(mock.Mock(), None) @mock.patch.object(directory, 'get_plugin') def test_prevent_l3_port_existing_floating_ip(self, gp): ctx = context.get_admin_context() gp.return_value.get_port.return_value = { 'device_owner': n_const.DEVICE_OWNER_FLOATINGIP, 'device_id': 'some_flip', 'id': 'f', 'fixed_ips': [{'ip_address': '1.1.1.1', 'subnet_id': '4'}]} with mock.patch.object(l3_obj.FloatingIP, 'objects_exist', return_value=mock.Mock()),\ testtools.ExpectedException(n_exc.ServicePortInUse): self.db.prevent_l3_port_deletion(ctx, None) @mock.patch.object(directory, 'get_plugin') def test_subscribe_address_scope_of_subnetpool(self, gp): l3_db.L3RpcNotifierMixin() registry.notify(resources.SUBNETPOOL_ADDRESS_SCOPE, events.AFTER_UPDATE, mock.ANY, context=mock.MagicMock(), subnetpool_id='fake_id') self.assertTrue(gp.return_value.notify_routers_updated.called) def test__check_and_get_fip_assoc_with_extra_association_no_change(self): fip = {'extra_key': 'value'} context = mock.MagicMock() floatingip_obj = l3_obj.FloatingIP( context, id=uuidutils.generate_uuid(), floating_network_id=uuidutils.generate_uuid(), floating_ip_address=netaddr.IPAddress('8.8.8.8'), fixed_port_id=uuidutils.generate_uuid(), floating_port_id=uuidutils.generate_uuid()) with mock.patch.object( l3_db.L3_NAT_dbonly_mixin, '_get_assoc_data', return_value=('1', '2', '3')) as mock_get_assoc_data: self.db._check_and_get_fip_assoc(context, fip, floatingip_obj) context.session.query.assert_not_called() mock_get_assoc_data.assert_called_once_with( mock.ANY, fip, floatingip_obj) def test__notify_attaching_interface(self): with mock.patch.object(l3_db.registry, 'notify') as mock_notify: context = mock.MagicMock() router_id = 'router_id' net_id = 'net_id' router_db = mock.Mock() router_db.id = router_id port = {'network_id': net_id} intf = {} self.db._notify_attaching_interface(context, router_db, port, intf) kwargs = {'context': context, 'router_id': router_id, 'network_id': net_id, 'interface_info': intf, 'router_db': router_db, 'port': port} mock_notify.assert_called_once_with( resources.ROUTER_INTERFACE, events.BEFORE_CREATE, self.db, **kwargs) class L3_NAT_db_mixin(base.BaseTestCase): def setUp(self): super(L3_NAT_db_mixin, self).setUp() self.db = l3_db.L3_NAT_db_mixin() def _test_create_router(self, external_gateway_info=None): router_db = l3_models.Router(id='123') router_dict = {'id': '123', 'tenant_id': '456', 'external_gateway_info': external_gateway_info} # Need to use a copy here as the create_router method pops the gateway # information router_input = {'router': router_dict.copy()} with mock.patch.object(l3_db.L3_NAT_dbonly_mixin, '_create_router_db', return_value=router_db) as crd,\ mock.patch.object(l3_db.L3_NAT_dbonly_mixin, '_make_router_dict', return_value=router_dict),\ mock.patch.object(l3_db.L3_NAT_dbonly_mixin, '_update_router_gw_info') as urgi,\ mock.patch.object(l3_db.L3_NAT_dbonly_mixin, '_get_router', return_value=router_db),\ mock.patch.object(l3_db.L3_NAT_db_mixin, 'notify_router_updated')\ as nru: self.db.create_router(mock.Mock(), router_input) self.assertTrue(crd.called) if external_gateway_info: self.assertTrue(urgi.called) self.assertTrue(nru.called) else: self.assertFalse(urgi.called) self.assertFalse(nru.called) def test_create_router_no_gateway(self): self._test_create_router() def test_create_router_gateway(self): ext_gateway_info = {'network_id': 'net-id', 'enable_snat': True, 'external_fixed_ips': [ {'subnet_id': 'subnet-id', 'ip_address': 'ip'}]} self._test_create_router(ext_gateway_info) def test_add_router_interface_no_interface_info(self): router_db = l3_models.Router(id='123') with mock.patch.object(l3_db.L3_NAT_dbonly_mixin, '_get_router', return_value=router_db): self.assertRaises( n_exc.BadRequest, self.db.add_router_interface, mock.Mock(), router_db.id) neutron-12.1.1/neutron/tests/unit/db/test_segments_db.py0000664000175000017500000000176313553660046023432 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.db import segments_db from neutron.tests import base class TestSegmentsDb(base.BaseTestCase): def test_get_networks_segments_with_empty_networks(self): context = mock.MagicMock() net_segs = segments_db.get_networks_segments(context, []) self.assertFalse(context.session.query.called) self.assertEqual({}, net_segs) neutron-12.1.1/neutron/tests/unit/db/test_securitygroups_db.py0000664000175000017500000005540013553660047024712 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import copy import mock from neutron_lib.callbacks import events from neutron_lib.callbacks import exceptions from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context import sqlalchemy import testtools from neutron.db import common_db_mixin from neutron.db import securitygroups_db from neutron.extensions import securitygroup from neutron.services.revisions import revision_plugin from neutron.tests.unit import testlib_api FAKE_SECGROUP = { 'security_group': { "tenant_id": 'fake', 'description': 'fake', 'name': 'fake' } } FAKE_SECGROUP_RULE = { 'security_group_rule': { "tenant_id": 'fake', 'description': 'fake', 'name': 'fake', 'port_range_min': '21', 'protocol': 'tcp', 'port_range_max': '23', 'remote_ip_prefix': '10.0.0.1', 'ethertype': 'IPv4', 'remote_group_id': None, 'security_group_id': 'None', 'direction': 'ingress' } } DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' def fake_callback(resource, event, *args, **kwargs): raise KeyError('bar') class SecurityGroupDbMixinImpl(securitygroups_db.SecurityGroupDbMixin, common_db_mixin.CommonDbMixin): pass class SecurityGroupDbMixinTestCase(testlib_api.SqlTestCase): def setUp(self): super(SecurityGroupDbMixinTestCase, self).setUp() self.setup_coreplugin(core_plugin=DB_PLUGIN_KLASS) self.ctx = context.get_admin_context() self.mixin = SecurityGroupDbMixinImpl() def test_create_security_group_conflict(self): with mock.patch.object(registry, "notify") as mock_notify: mock_notify.side_effect = exceptions.CallbackFailure(Exception()) secgroup = {'security_group': mock.ANY} with testtools.ExpectedException( securitygroup.SecurityGroupConflict): self.mixin.create_security_group(self.ctx, secgroup) def test_delete_security_group_in_use(self): with mock.patch.object(self.mixin, '_get_port_security_group_bindings'),\ mock.patch.object(self.mixin, '_get_security_group'),\ mock.patch.object(registry, "notify") as mock_notify: mock_notify.side_effect = exceptions.CallbackFailure(Exception()) with testtools.ExpectedException( securitygroup.SecurityGroupInUse): self.mixin.delete_security_group(self.ctx, mock.ANY) def test_update_security_group_conflict(self): with mock.patch.object(registry, "notify") as mock_notify: mock_notify.side_effect = exceptions.CallbackFailure(Exception()) secgroup = {'security_group': mock.ANY} with testtools.ExpectedException( securitygroup.SecurityGroupConflict): self.mixin.update_security_group(self.ctx, 'foo_id', secgroup) def test_create_security_group_rule_conflict(self): with mock.patch.object(self.mixin, '_validate_security_group_rule'),\ mock.patch.object(self.mixin, '_check_for_duplicate_rules'),\ mock.patch.object(registry, "notify") as mock_notify: mock_notify.side_effect = exceptions.CallbackFailure(Exception()) with testtools.ExpectedException( securitygroup.SecurityGroupConflict): self.mixin.create_security_group_rule( self.ctx, mock.MagicMock()) def test__check_for_duplicate_rules_does_not_drop_protocol(self): with mock.patch.object(self.mixin, 'get_security_group', return_value=None): context = mock.Mock() rule_dict = { 'security_group_rule': {'protocol': None, 'tenant_id': 'fake', 'security_group_id': 'fake', 'direction': 'fake'} } self.mixin._check_for_duplicate_rules(context, 'fake', [rule_dict]) self.assertIn('protocol', rule_dict['security_group_rule']) def test__check_for_duplicate_rules_ignores_rule_id(self): rules = [{'security_group_rule': {'protocol': 'tcp', 'id': 'fake1'}}, {'security_group_rule': {'protocol': 'tcp', 'id': 'fake2'}}] # NOTE(arosen): the name of this exception is a little misleading # in this case as this test, tests that the id fields are dropped # while being compared. This is in the case if a plugin specifies # the rule ids themselves. with mock.patch.object(self.mixin, 'get_security_group', return_value=None): self.assertRaises(securitygroup.DuplicateSecurityGroupRuleInPost, self.mixin._check_for_duplicate_rules, context, 'fake', rules) def test_check_for_duplicate_diff_rules_remote_ip_prefix_ipv4(self): fake_secgroup = copy.deepcopy(FAKE_SECGROUP) fake_secgroup['security_group_rules'] = \ [{'id': 'fake', 'tenant_id': 'fake', 'ethertype': 'IPv4', 'direction': 'ingress', 'security_group_id': 'fake', 'remote_ip_prefix': None}] with mock.patch.object(self.mixin, 'get_security_group', return_value=fake_secgroup): context = mock.Mock() rule_dict = { 'security_group_rule': {'id': 'fake2', 'tenant_id': 'fake', 'security_group_id': 'fake', 'ethertype': 'IPv4', 'direction': 'ingress', 'remote_ip_prefix': '0.0.0.0/0'} } self.assertRaises(securitygroup.SecurityGroupRuleExists, self.mixin._check_for_duplicate_rules, context, 'fake', [rule_dict]) def test_check_for_duplicate_diff_rules_remote_ip_prefix_ipv6(self): fake_secgroup = copy.deepcopy(FAKE_SECGROUP) fake_secgroup['security_group_rules'] = \ [{'id': 'fake', 'tenant_id': 'fake', 'ethertype': 'IPv6', 'direction': 'ingress', 'security_group_id': 'fake', 'remote_ip_prefix': None}] with mock.patch.object(self.mixin, 'get_security_group', return_value=fake_secgroup): context = mock.Mock() rule_dict = { 'security_group_rule': {'id': 'fake2', 'tenant_id': 'fake', 'security_group_id': 'fake', 'ethertype': 'IPv6', 'direction': 'ingress', 'remote_ip_prefix': '::/0'} } self.assertRaises(securitygroup.SecurityGroupRuleExists, self.mixin._check_for_duplicate_rules, context, 'fake', [rule_dict]) def test_delete_security_group_rule_in_use(self): with mock.patch.object(registry, "notify") as mock_notify: mock_notify.side_effect = exceptions.CallbackFailure(Exception()) with testtools.ExpectedException( securitygroup.SecurityGroupRuleInUse): self.mixin.delete_security_group_rule(self.ctx, mock.ANY) def test_delete_security_group_rule_raise_error_on_not_found(self): with testtools.ExpectedException( securitygroup.SecurityGroupRuleNotFound): self.mixin.delete_security_group_rule(self.ctx, 'foo_rule') def test_validate_ethertype_and_protocol(self): fake_ipv4_rules = [{'protocol': constants.PROTO_NAME_IPV6_ICMP, 'ethertype': constants.IPv4}, {'protocol': constants.PROTO_NAME_IPV6_ICMP_LEGACY, 'ethertype': constants.IPv4}, {'protocol': constants.PROTO_NAME_IPV6_ENCAP, 'ethertype': constants.IPv4}, {'protocol': constants.PROTO_NAME_IPV6_ROUTE, 'ethertype': constants.IPv4}, {'protocol': constants.PROTO_NAME_IPV6_FRAG, 'ethertype': constants.IPv4}, {'protocol': constants.PROTO_NAME_IPV6_NONXT, 'ethertype': constants.IPv4}, {'protocol': constants.PROTO_NAME_IPV6_OPTS, 'ethertype': constants.IPv4}, {'protocol': str(constants.PROTO_NUM_IPV6_ICMP), 'ethertype': constants.IPv4}, {'protocol': str(constants.PROTO_NUM_IPV6_ENCAP), 'ethertype': constants.IPv4}, {'protocol': str(constants.PROTO_NUM_IPV6_ROUTE), 'ethertype': constants.IPv4}, {'protocol': str(constants.PROTO_NUM_IPV6_FRAG), 'ethertype': constants.IPv4}, {'protocol': str(constants.PROTO_NUM_IPV6_NONXT), 'ethertype': constants.IPv4}, {'protocol': str(constants.PROTO_NUM_IPV6_OPTS), 'ethertype': constants.IPv4}] # test wrong protocols for rule in fake_ipv4_rules: with testtools.ExpectedException( securitygroup.SecurityGroupEthertypeConflictWithProtocol): self.mixin._validate_ethertype_and_protocol(rule) def test_security_group_precommit_create_event_fail(self): registry.subscribe(fake_callback, resources.SECURITY_GROUP, events.PRECOMMIT_CREATE) with mock.patch.object(sqlalchemy.orm.session.SessionTransaction, 'rollback') as mock_rollback: self.assertRaises(securitygroup.SecurityGroupConflict, self.mixin.create_security_group, self.ctx, FAKE_SECGROUP) self.assertTrue(mock_rollback.called) def test_security_group_precommit_update_event_fail(self): registry.subscribe(fake_callback, resources.SECURITY_GROUP, events.PRECOMMIT_UPDATE) sg_dict = self.mixin.create_security_group(self.ctx, FAKE_SECGROUP) with mock.patch.object(sqlalchemy.orm.session.SessionTransaction, 'rollback') as mock_rollback: self.assertRaises(securitygroup.SecurityGroupConflict, self.mixin.update_security_group, self.ctx, sg_dict['id'], FAKE_SECGROUP) self.assertTrue(mock_rollback.called) def test_security_group_precommit_delete_event_fail(self): registry.subscribe(fake_callback, resources.SECURITY_GROUP, events.PRECOMMIT_DELETE) sg_dict = self.mixin.create_security_group(self.ctx, FAKE_SECGROUP) with mock.patch.object(sqlalchemy.orm.session.SessionTransaction, 'rollback') as mock_rollback: self.assertRaises(securitygroup.SecurityGroupInUse, self.mixin.delete_security_group, self.ctx, sg_dict['id']) self.assertTrue(mock_rollback.called) def _test_security_group_precommit_create_event(self, with_revisions=False): DEFAULT_SECGROUP = { 'tenant_id': FAKE_SECGROUP['security_group']['tenant_id'], 'name': 'default', 'description': 'Default security group', } DEFAULT_SECGROUP_DICT = { 'id': mock.ANY, 'tenant_id': FAKE_SECGROUP['security_group']['tenant_id'], 'project_id': FAKE_SECGROUP['security_group']['tenant_id'], 'name': 'default', 'description': 'Default security group', 'security_group_rules': [ # Four rules for egress/ingress and ipv4/ipv6 mock.ANY, mock.ANY, mock.ANY, mock.ANY, ], } if with_revisions: DEFAULT_SECGROUP_DICT.update({ 'revision_number': mock.ANY, }) with mock.patch.object(registry, "notify") as mock_notify: sg_dict = self.mixin.create_security_group(self.ctx, FAKE_SECGROUP) mock_notify.assert_has_calls([ mock.call('security_group', 'before_create', mock.ANY, context=mock.ANY, is_default=False, security_group=FAKE_SECGROUP['security_group']), mock.call('security_group', 'before_create', mock.ANY, context=mock.ANY, is_default=True, security_group=DEFAULT_SECGROUP), mock.call('security_group', 'precommit_create', mock.ANY, context=mock.ANY, is_default=True, security_group=DEFAULT_SECGROUP_DICT), mock.call('security_group', 'after_create', mock.ANY, context=mock.ANY, is_default=True, security_group=DEFAULT_SECGROUP_DICT), mock.call('security_group', 'precommit_create', mock.ANY, context=mock.ANY, is_default=False, security_group=sg_dict), mock.call('security_group', 'after_create', mock.ANY, context=mock.ANY, is_default=False, security_group=sg_dict)]) # Ensure that the result of create is same as get. # Especially we want to check the revision number here. sg_dict_got = self.mixin.get_security_group( self.ctx, sg_dict['id']) self.assertEqual(sg_dict, sg_dict_got) def test_security_group_precommit_create_event_with_revisions(self): revision = revision_plugin.RevisionPlugin() self._test_security_group_precommit_create_event(True) del revision # appease pep8 def test_security_group_precommit_create_event(self): self._test_security_group_precommit_create_event() def test_security_group_precommit_update_event(self): original_sg_dict = self.mixin.create_security_group(self.ctx, FAKE_SECGROUP) sg_id = original_sg_dict['id'] with mock.patch.object(registry, "publish") as mock_notify: fake_secgroup = copy.deepcopy(FAKE_SECGROUP) fake_secgroup['security_group']['name'] = 'updated_fake' sg_dict = self.mixin.update_security_group( self.ctx, sg_id, fake_secgroup) mock_notify.assert_has_calls( [mock.call('security_group', 'precommit_update', mock.ANY, payload=mock.ANY)]) payload = mock_notify.call_args[1]['payload'] self.assertEqual(original_sg_dict, payload.states[0]) self.assertEqual(sg_id, payload.resource_id) self.assertEqual(sg_dict, payload.desired_state) def test_security_group_precommit_and_after_delete_event(self): sg_dict = self.mixin.create_security_group(self.ctx, FAKE_SECGROUP) with mock.patch.object(registry, "notify") as mock_notify: self.mixin.delete_security_group(self.ctx, sg_dict['id']) sg_dict['security_group_rules'] = mock.ANY mock_notify.assert_has_calls( [mock.call('security_group', 'precommit_delete', mock.ANY, context=mock.ANY, security_group=sg_dict, security_group_id=sg_dict['id'], security_group_rule_ids=[mock.ANY, mock.ANY]), mock.call('security_group', 'after_delete', mock.ANY, context=mock.ANY, security_group_id=sg_dict['id'], security_group_rule_ids=[mock.ANY, mock.ANY])]) def test_security_group_rule_precommit_create_event_fail(self): registry.subscribe(fake_callback, resources.SECURITY_GROUP_RULE, events.PRECOMMIT_CREATE) sg_dict = self.mixin.create_security_group(self.ctx, FAKE_SECGROUP) fake_rule = FAKE_SECGROUP_RULE fake_rule['security_group_rule']['security_group_id'] = sg_dict['id'] with mock.patch.object(sqlalchemy.orm.session.SessionTransaction, 'rollback') as mock_rollback,\ mock.patch.object(self.mixin, '_get_security_group'): self.assertRaises(securitygroup.SecurityGroupConflict, self.mixin.create_security_group_rule, self.ctx, fake_rule) self.assertTrue(mock_rollback.called) def test_security_group_rule_precommit_delete_event_fail(self): registry.subscribe(fake_callback, resources.SECURITY_GROUP_RULE, events.PRECOMMIT_DELETE) sg_dict = self.mixin.create_security_group(self.ctx, FAKE_SECGROUP) fake_rule = FAKE_SECGROUP_RULE fake_rule['security_group_rule']['security_group_id'] = sg_dict['id'] with mock.patch.object(sqlalchemy.orm.session.SessionTransaction, 'rollback') as mock_rollback,\ mock.patch.object(self.mixin, '_get_security_group'): sg_rule_dict = self.mixin.create_security_group_rule(self.ctx, fake_rule) self.assertRaises(securitygroup.SecurityGroupRuleInUse, self.mixin.delete_security_group_rule, self.ctx, sg_rule_dict['id']) self.assertTrue(mock_rollback.called) def test_security_group_rule_precommit_create_event(self): sg_dict = self.mixin.create_security_group(self.ctx, FAKE_SECGROUP) fake_rule = FAKE_SECGROUP_RULE fake_rule['security_group_rule']['security_group_id'] = sg_dict['id'] with mock.patch.object(registry, "notify") as mock_notify, \ mock.patch.object(self.mixin, '_get_security_group'): mock_notify.assert_has_calls([mock.call('security_group_rule', 'precommit_create', mock.ANY, context=mock.ANY, security_group_rule=self.mixin.create_security_group_rule( self.ctx, fake_rule))]) def test_sg_rule_before_precommit_and_after_delete_event(self): sg_dict = self.mixin.create_security_group(self.ctx, FAKE_SECGROUP) fake_rule = FAKE_SECGROUP_RULE fake_rule['security_group_rule']['security_group_id'] = sg_dict['id'] with mock.patch.object(registry, "notify") as mock_notify, \ mock.patch.object(self.mixin, '_get_security_group'): sg_rule_dict = self.mixin.create_security_group_rule(self.ctx, fake_rule) self.mixin.delete_security_group_rule(self.ctx, sg_rule_dict['id']) mock_notify.assert_has_calls([mock.call('security_group_rule', 'before_delete', mock.ANY, context=mock.ANY, security_group_rule_id=sg_rule_dict['id'])]) mock_notify.assert_has_calls([mock.call('security_group_rule', 'precommit_delete', mock.ANY, context=mock.ANY, security_group_id=sg_dict['id'], security_group_rule_id=sg_rule_dict['id'])]) mock_notify.assert_has_calls([mock.call('security_group_rule', 'after_delete', mock.ANY, context=mock.ANY, security_group_rule_id=sg_rule_dict['id'], security_group_id=sg_dict['id'])]) def test_get_ip_proto_name_and_num(self): protocols = [constants.PROTO_NAME_UDP, str(constants.PROTO_NUM_TCP), 'blah', '111'] protocol_names_nums = ( [[constants.PROTO_NAME_UDP, str(constants.PROTO_NUM_UDP)], [constants.PROTO_NAME_TCP, str(constants.PROTO_NUM_TCP)], ['blah', 'blah'], ['111', '111']]) for i, protocol in enumerate(protocols): self.assertEqual(protocol_names_nums[i], self.mixin._get_ip_proto_name_and_num(protocol)) def test__validate_port_range_for_icmp_exception(self): states = [(1, 256, securitygroup.SecurityGroupInvalidIcmpValue), (None, 6, securitygroup.SecurityGroupMissingIcmpType), (300, 1, securitygroup.SecurityGroupInvalidIcmpValue)] for protocol in (constants.PROTO_NAME_ICMP, constants.PROTO_NAME_IPV6_ICMP, constants.PROTO_NAME_IPV6_ICMP_LEGACY): for pmin, pmax, exception in states: self.assertRaises(exception, self.mixin._validate_port_range, {'port_range_min': pmin, 'port_range_max': pmax, 'protocol': protocol}) def test__validate_port_range_exception(self): self.assertRaises(securitygroup.SecurityGroupInvalidPortValue, self.mixin._validate_port_range, {'port_range_min': 0, 'port_range_max': None, 'protocol': constants.PROTO_NAME_TCP}) self.assertRaises(securitygroup.SecurityGroupInvalidPortRange, self.mixin._validate_port_range, {'port_range_min': 1, 'port_range_max': None, 'protocol': constants.PROTO_NAME_SCTP}) self.assertRaises(securitygroup.SecurityGroupInvalidPortRange, self.mixin._validate_port_range, {'port_range_min': 1000, 'port_range_max': 1, 'protocol': constants.PROTO_NAME_UDPLITE}) self.assertRaises( securitygroup.SecurityGroupInvalidProtocolForPortRange, self.mixin._validate_port_range, {'port_range_min': 100, 'port_range_max': 200, 'protocol': '111'}) neutron-12.1.1/neutron/tests/unit/db/test_api.py0000664000175000017500000001663513553660047021716 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib import exceptions from oslo_db import exception as db_exc import osprofiler import sqlalchemy from sqlalchemy.orm import exc import testtools from neutron.db import api as db_api from neutron.tests import base class TestExceptionToRetryContextManager(base.BaseTestCase): def test_translates_single_exception(self): with testtools.ExpectedException(db_exc.RetryRequest): with db_api.exc_to_retry(ValueError): raise ValueError() def test_translates_multiple_exception_types(self): with testtools.ExpectedException(db_exc.RetryRequest): with db_api.exc_to_retry((ValueError, TypeError)): raise TypeError() def test_translates_DBerror_inner_exception(self): with testtools.ExpectedException(db_exc.RetryRequest): with db_api.exc_to_retry(ValueError): raise db_exc.DBError(ValueError()) def test_passes_other_exceptions(self): with testtools.ExpectedException(ValueError): with db_api.exc_to_retry(TypeError): raise ValueError() def test_inner_exception_preserved_in_retryrequest(self): try: exc = ValueError('test') with db_api.exc_to_retry(ValueError): raise exc except db_exc.RetryRequest as e: self.assertEqual(exc, e.inner_exc) def test_retries_on_multi_exception_containing_target(self): with testtools.ExpectedException(db_exc.RetryRequest): with db_api.exc_to_retry(ValueError): e = exceptions.MultipleExceptions([ValueError(), TypeError()]) raise e class TestDeadLockDecorator(base.BaseTestCase): @db_api.retry_db_errors def _decorated_function(self, fail_count, exc_to_raise): self.fail_count = getattr(self, 'fail_count', fail_count + 1) - 1 if self.fail_count: raise exc_to_raise def test_regular_exception_excluded(self): with testtools.ExpectedException(ValueError): self._decorated_function(1, ValueError) def test_staledata_error_caught(self): e = exc.StaleDataError() self.assertIsNone(self._decorated_function(1, e)) def test_dbconnection_error_caught(self): e = db_exc.DBConnectionError() self.assertIsNone(self._decorated_function(1, e)) def test_multi_exception_contains_retry(self): e = exceptions.MultipleExceptions( [ValueError(), db_exc.RetryRequest(TypeError())]) self.assertIsNone(self._decorated_function(1, e)) def test_multi_exception_contains_deadlock(self): e = exceptions.MultipleExceptions([ValueError(), db_exc.DBDeadlock()]) self.assertIsNone(self._decorated_function(1, e)) def test_multi_nested_exception_contains_deadlock(self): i = exceptions.MultipleExceptions([ValueError(), db_exc.DBDeadlock()]) e = exceptions.MultipleExceptions([ValueError(), i]) self.assertIsNone(self._decorated_function(1, e)) def test_multi_exception_raised_on_exceed(self): # limit retries so this doesn't take 40 seconds mock.patch.object(db_api._retry_db_errors, 'max_retries', new=2).start() e = exceptions.MultipleExceptions([ValueError(), db_exc.DBDeadlock()]) with testtools.ExpectedException(exceptions.MultipleExceptions): self._decorated_function(db_api.MAX_RETRIES + 1, e) def test_mysql_savepoint_error(self): e = db_exc.DBError("(pymysql.err.InternalError) (1305, u'SAVEPOINT " "sa_savepoint_1 does not exist')") self.assertIsNone(self._decorated_function(1, e)) @db_api.retry_if_session_inactive('alt_context') def _alt_context_function(self, alt_context, *args, **kwargs): return self._decorated_function(*args, **kwargs) @db_api.retry_if_session_inactive() def _context_function(self, context, list_arg, dict_arg, fail_count, exc_to_raise): list_arg.append(1) dict_arg[max(dict_arg.keys()) + 1] = True self.fail_count = getattr(self, 'fail_count', fail_count + 1) - 1 if self.fail_count: raise exc_to_raise return list_arg, dict_arg def test_stacked_retries_dont_explode_retry_count(self): context = mock.Mock() context.session.is_active = False e = db_exc.DBConnectionError() mock.patch('time.sleep').start() with testtools.ExpectedException(db_exc.DBConnectionError): # after 10 failures, the inner retry should give up and # the exception should be tagged to prevent the outer retry self._alt_context_function(context, 11, e) def test_retry_if_session_inactive_args_not_mutated_after_retries(self): context = mock.Mock() context.session.is_active = False list_arg = [1, 2, 3, 4] dict_arg = {1: 'a', 2: 'b'} l, d = self._context_function(context, list_arg, dict_arg, 5, db_exc.DBDeadlock()) # even though we had 5 failures the list and dict should only # be mutated once self.assertEqual(5, len(l)) self.assertEqual(3, len(d)) def test_retry_if_session_inactive_kwargs_not_mutated_after_retries(self): context = mock.Mock() context.session.is_active = False list_arg = [1, 2, 3, 4] dict_arg = {1: 'a', 2: 'b'} l, d = self._context_function(context, list_arg=list_arg, dict_arg=dict_arg, fail_count=5, exc_to_raise=db_exc.DBDeadlock()) # even though we had 5 failures the list and dict should only # be mutated once self.assertEqual(5, len(l)) self.assertEqual(3, len(d)) def test_retry_if_session_inactive_no_retry_in_active_session(self): context = mock.Mock() context.session.is_active = True with testtools.ExpectedException(db_exc.DBDeadlock): # retry decorator should have no effect in an active session self._context_function(context, [], {1: 2}, fail_count=1, exc_to_raise=db_exc.DBDeadlock()) class TestCommonDBfunctions(base.BaseTestCase): def test_set_hook(self): with mock.patch.object(osprofiler.opts, 'is_trace_enabled', return_value=True),\ mock.patch.object(osprofiler.opts, 'is_db_trace_enabled', return_value=True): with mock.patch.object(osprofiler.sqlalchemy, 'add_tracing') as add_tracing: engine_mock = mock.Mock() db_api.set_hook(engine_mock) add_tracing.assert_called_with(sqlalchemy, engine_mock, 'neutron.db') neutron-12.1.1/neutron/tests/unit/db/test_sqlalchemytypes.py0000664000175000017500000002246713553660047024374 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import netaddr from neutron_lib import context from neutron_lib.utils import net from oslo_db import exception from oslo_db.sqlalchemy import enginefacade from oslo_db.sqlalchemy import test_fixtures from oslo_utils import timeutils from oslo_utils import uuidutils from oslotest import base as test_base import six import sqlalchemy as sa from neutron.db import sqlalchemytypes from neutron.tests import tools @six.add_metaclass(abc.ABCMeta) class SqlAlchemyTypesBaseTestCase(test_fixtures.OpportunisticDBTestMixin, test_base.BaseTestCase): def setUp(self): super(SqlAlchemyTypesBaseTestCase, self).setUp() self.engine = enginefacade.writer.get_engine() meta = sa.MetaData(bind=self.engine) self.test_table = self._get_test_table(meta) self.test_table.create() self.addCleanup(meta.drop_all) self.ctxt = context.get_admin_context() @abc.abstractmethod def _get_test_table(self, meta): """Returns a new sa.Table() object for this test case.""" def _add_row(self, **kargs): self.engine.execute(self.test_table.insert().values(**kargs)) def _get_all(self): rows_select = self.test_table.select() return self.engine.execute(rows_select).fetchall() def _update_row(self, **kargs): self.engine.execute(self.test_table.update().values(**kargs)) def _delete_rows(self): self.engine.execute(self.test_table.delete()) def _validate_crud(self, data_field_name, expected=None): objs = self._get_all() self.assertEqual(len(expected) if expected else 0, len(objs)) if expected: for obj in objs: name = obj['id'] self.assertEqual(expected[name], obj[data_field_name]) class IPAddressTestCase(SqlAlchemyTypesBaseTestCase): def _get_test_table(self, meta): return sa.Table( 'fakeipaddressmodels', meta, sa.Column('id', sa.String(36), primary_key=True, nullable=False), sa.Column('ip', sqlalchemytypes.IPAddress)) def _validate_ip_address(self, data_field_name, expected=None): objs = self._get_all() self.assertEqual(len(expected) if expected else 0, len(objs)) if expected: for obj in objs: name = obj['id'] self.assertEqual(expected[name], obj[data_field_name]) def _test_crud(self, ip_addresses): ip = netaddr.IPAddress(ip_addresses[0]) self._add_row(id='fake_id', ip=ip) self._validate_ip_address(data_field_name='ip', expected={'fake_id': ip}) ip2 = netaddr.IPAddress(ip_addresses[1]) self._update_row(ip=ip2) self._validate_ip_address(data_field_name='ip', expected={'fake_id': ip2}) self._delete_rows() self._validate_ip_address(data_field_name='ip', expected=None) def test_crud(self): ip_addresses = ["10.0.0.1", "10.0.0.2"] self._test_crud(ip_addresses) ip_addresses = [ "2210::ffff:ffff:ffff:ffff", "2120::ffff:ffff:ffff:ffff" ] self._test_crud(ip_addresses) def test_wrong_type(self): self.assertRaises(exception.DBError, self._add_row, id='fake_id', ip="") self.assertRaises(exception.DBError, self._add_row, id='fake_id', ip="10.0.0.5") def _test_multiple_create(self, entries): reference = {} for entry in entries: ip = netaddr.IPAddress(entry['ip']) name = entry['name'] self._add_row(id=name, ip=ip) reference[name] = ip self._validate_ip_address(data_field_name='ip', expected=reference) self._delete_rows() self._validate_ip_address(data_field_name='ip', expected=None) def test_multiple_create(self): ip_addresses = [ {'name': 'fake_id1', 'ip': "10.0.0.5"}, {'name': 'fake_id2', 'ip': "10.0.0.1"}, {'name': 'fake_id3', 'ip': "2210::ffff:ffff:ffff:ffff"}, {'name': 'fake_id4', 'ip': "2120::ffff:ffff:ffff:ffff"} ] self._test_multiple_create(ip_addresses) class CIDRTestCase(SqlAlchemyTypesBaseTestCase): def _get_test_table(self, meta): return sa.Table( 'fakecidrmodels', meta, sa.Column('id', sa.String(36), primary_key=True, nullable=False), sa.Column('cidr', sqlalchemytypes.CIDR) ) def _get_one(self, value): row_select = self.test_table.select().\ where(self.test_table.c.cidr == value) return self.engine.execute(row_select).first() def _update_row(self, key, cidr): self.engine.execute( self.test_table.update().values(cidr=cidr). where(self.test_table.c.cidr == key)) def test_crud(self): cidrs = ["10.0.0.0/24", "10.123.250.9/32", "2001:db8::/42", "fe80::21e:67ff:fed0:56f0/64"] for cidr_str in cidrs: cidr = netaddr.IPNetwork(cidr_str) self._add_row(id=uuidutils.generate_uuid(), cidr=cidr) obj = self._get_one(cidr) self.assertEqual(cidr, obj['cidr']) random_cidr = netaddr.IPNetwork(tools.get_random_cidr()) self._update_row(cidr, random_cidr) obj = self._get_one(random_cidr) self.assertEqual(random_cidr, obj['cidr']) objs = self._get_all() self.assertEqual(len(cidrs), len(objs)) self._delete_rows() objs = self._get_all() self.assertEqual(0, len(objs)) def test_wrong_cidr(self): wrong_cidrs = ["10.500.5.0/24", "10.0.0.1/40", "10.0.0.10.0/24", "cidr", "", '2001:db8:5000::/64', '2001:db8::/130'] for cidr in wrong_cidrs: self.assertRaises(exception.DBError, self._add_row, id=uuidutils.generate_uuid(), cidr=cidr) class MACAddressTestCase(SqlAlchemyTypesBaseTestCase): def _get_test_table(self, meta): return sa.Table( 'fakemacaddressmodels', meta, sa.Column('id', sa.String(36), primary_key=True, nullable=False), sa.Column('mac', sqlalchemytypes.MACAddress) ) def _get_one(self, value): row_select = self.test_table.select().\ where(self.test_table.c.mac == value) return self.engine.execute(row_select).first() def _get_all(self): rows_select = self.test_table.select() return self.engine.execute(rows_select).fetchall() def _update_row(self, key, mac): self.engine.execute( self.test_table.update().values(mac=mac). where(self.test_table.c.mac == key)) def _delete_row(self): self.engine.execute( self.test_table.delete()) def test_crud(self): mac_addresses = ['FA:16:3E:00:00:01', 'FA:16:3E:00:00:02'] for mac in mac_addresses: mac = netaddr.EUI(mac) self._add_row(id=uuidutils.generate_uuid(), mac=mac) obj = self._get_one(mac) self.assertEqual(mac, obj['mac']) random_mac = netaddr.EUI(net.get_random_mac( ['fe', '16', '3e', '00', '00', '00'])) self._update_row(mac, random_mac) obj = self._get_one(random_mac) self.assertEqual(random_mac, obj['mac']) objs = self._get_all() self.assertEqual(len(mac_addresses), len(objs)) self._delete_rows() objs = self._get_all() self.assertEqual(0, len(objs)) def test_wrong_mac(self): wrong_macs = ["fake", "", -1, "FK:16:3E:00:00:02", "FA:16:3E:00:00:020"] for mac in wrong_macs: self.assertRaises(exception.DBError, self._add_row, id=uuidutils.generate_uuid(), mac=mac) class TruncatedDateTimeTestCase(SqlAlchemyTypesBaseTestCase): def _get_test_table(self, meta): return sa.Table( 'timetable', meta, sa.Column('id', sa.String(36), primary_key=True, nullable=False), sa.Column('thetime', sqlalchemytypes.TruncatedDateTime) ) def test_microseconds_truncated(self): tstamp = timeutils.utcnow() tstamp_low = tstamp.replace(microsecond=111111) tstamp_high = tstamp.replace(microsecond=999999) self._add_row(id=1, thetime=tstamp_low) self._add_row(id=2, thetime=tstamp_high) rows = self._get_all() self.assertEqual(2, len(rows)) self.assertEqual(rows[0].thetime, rows[1].thetime) neutron-12.1.1/neutron/tests/unit/db/test_agentschedulers_db.py0000664000175000017500000022373113553660047024767 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import datetime import mock from neutron_lib import constants from neutron_lib import context from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_db import exception as db_exc import oslo_messaging from oslo_utils import uuidutils from webob import exc from neutron.api import extensions from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api from neutron.api.rpc.handlers import dhcp_rpc from neutron.api.rpc.handlers import l3_rpc from neutron.db import agents_db from neutron.db import agentschedulers_db from neutron.db.models import agent as agent_model from neutron.extensions import dhcpagentscheduler from neutron.extensions import l3agentscheduler from neutron.objects import agent as ag_obj from neutron.objects import l3agent as rb_obj from neutron.tests.common import helpers from neutron.tests import fake_notifier from neutron.tests.unit.api import test_extensions from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin from neutron.tests.unit.extensions import test_agent from neutron.tests.unit.extensions import test_l3 from neutron.tests.unit import testlib_api from neutron import wsgi L3_HOSTA = 'hosta' DHCP_HOSTA = 'hosta' L3_HOSTB = 'hostb' DHCP_HOSTC = 'hostc' DHCP_HOSTD = 'hostd' DEVICE_OWNER_COMPUTE = ''.join([constants.DEVICE_OWNER_COMPUTE_PREFIX, 'test:', DHCP_HOSTA]) class AgentSchedulerTestMixIn(object): block_dhcp_notifier = False def _request_list(self, path, admin_context=True, expected_code=exc.HTTPOk.code): req = self._path_req(path, admin_context=admin_context) res = req.get_response(self.ext_api) self.assertEqual(expected_code, res.status_int) return self.deserialize(self.fmt, res) def _path_req(self, path, method='GET', data=None, query_string=None, admin_context=True): content_type = 'application/%s' % self.fmt body = None if data is not None: # empty dict is valid body = wsgi.Serializer().serialize(data, content_type) if admin_context: return testlib_api.create_request( path, body, content_type, method, query_string=query_string) else: return testlib_api.create_request( path, body, content_type, method, query_string=query_string, context=context.Context('', 'tenant_id')) def _path_create_request(self, path, data, admin_context=True): return self._path_req(path, method='POST', data=data, admin_context=admin_context) def _path_show_request(self, path, admin_context=True): return self._path_req(path, admin_context=admin_context) def _path_delete_request(self, path, admin_context=True): return self._path_req(path, method='DELETE', admin_context=admin_context) def _path_update_request(self, path, data, admin_context=True): return self._path_req(path, method='PUT', data=data, admin_context=admin_context) def _list_routers_hosted_by_l3_agent(self, agent_id, expected_code=exc.HTTPOk.code, admin_context=True): path = "/agents/%s/%s.%s" % (agent_id, l3agentscheduler.L3_ROUTERS, self.fmt) return self._request_list(path, expected_code=expected_code, admin_context=admin_context) def _list_networks_hosted_by_dhcp_agent(self, agent_id, expected_code=exc.HTTPOk.code, admin_context=True): path = "/agents/%s/%s.%s" % (agent_id, dhcpagentscheduler.DHCP_NETS, self.fmt) return self._request_list(path, expected_code=expected_code, admin_context=admin_context) def _list_l3_agents_hosting_router(self, router_id, expected_code=exc.HTTPOk.code, admin_context=True): path = "/routers/%s/%s.%s" % (router_id, l3agentscheduler.L3_AGENTS, self.fmt) return self._request_list(path, expected_code=expected_code, admin_context=admin_context) def _list_dhcp_agents_hosting_network(self, network_id, expected_code=exc.HTTPOk.code, admin_context=True): path = "/networks/%s/%s.%s" % (network_id, dhcpagentscheduler.DHCP_AGENTS, self.fmt) return self._request_list(path, expected_code=expected_code, admin_context=admin_context) def _add_router_to_l3_agent(self, id, router_id, expected_code=exc.HTTPCreated.code, admin_context=True): path = "/agents/%s/%s.%s" % (id, l3agentscheduler.L3_ROUTERS, self.fmt) req = self._path_create_request(path, {'router_id': router_id}, admin_context=admin_context) res = req.get_response(self.ext_api) self.assertEqual(expected_code, res.status_int) def _add_network_to_dhcp_agent(self, id, network_id, expected_code=exc.HTTPCreated.code, admin_context=True): path = "/agents/%s/%s.%s" % (id, dhcpagentscheduler.DHCP_NETS, self.fmt) req = self._path_create_request(path, {'network_id': network_id}, admin_context=admin_context) res = req.get_response(self.ext_api) self.assertEqual(expected_code, res.status_int) def _remove_network_from_dhcp_agent(self, id, network_id, expected_code=exc.HTTPNoContent.code, admin_context=True): path = "/agents/%s/%s/%s.%s" % (id, dhcpagentscheduler.DHCP_NETS, network_id, self.fmt) req = self._path_delete_request(path, admin_context=admin_context) res = req.get_response(self.ext_api) self.assertEqual(expected_code, res.status_int) def _remove_router_from_l3_agent(self, id, router_id, expected_code=exc.HTTPNoContent.code, admin_context=True): path = "/agents/%s/%s/%s.%s" % (id, l3agentscheduler.L3_ROUTERS, router_id, self.fmt) req = self._path_delete_request(path, admin_context=admin_context) res = req.get_response(self.ext_api) self.assertEqual(expected_code, res.status_int) def _assert_notify(self, notifications, expected_event_type): event_types = [event['event_type'] for event in notifications] self.assertIn(expected_event_type, event_types) def test_agent_registration_bad_timestamp(self): callback = agents_db.AgentExtRpcCallback() delta_time = datetime.datetime.now() - datetime.timedelta(days=1) str_time = delta_time.strftime('%Y-%m-%dT%H:%M:%S.%f') callback.report_state( self.adminContext, agent_state={ 'agent_state': helpers._get_dhcp_agent_dict(DHCP_HOSTA)}, time=str_time) def test_agent_registration_invalid_timestamp_allowed(self): callback = agents_db.AgentExtRpcCallback() utc_time = datetime.datetime.utcnow() delta_time = utc_time - datetime.timedelta(seconds=10) str_time = delta_time.strftime('%Y-%m-%dT%H:%M:%S.%f') callback.report_state( self.adminContext, agent_state={ 'agent_state': helpers._get_dhcp_agent_dict(DHCP_HOSTA)}, time=str_time) def _disable_agent(self, agent_id, admin_state_up=False): new_agent = {} new_agent['agent'] = {} new_agent['agent']['admin_state_up'] = admin_state_up self._update('agents', agent_id, new_agent) def _get_agent_id(self, agent_type, host): agents = self._list_agents() for agent_data in agents['agents']: if (agent_data['agent_type'] == agent_type and agent_data['host'] == host): return agent_data['id'] class OvsAgentSchedulerTestCaseBase(test_l3.L3NatTestCaseMixin, test_agent.AgentDBTestMixIn, AgentSchedulerTestMixIn, test_plugin.NeutronDbPluginV2TestCase): fmt = 'json' l3_plugin = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatAgentSchedulingServicePlugin') def setUp(self): if self.l3_plugin: service_plugins = { 'l3_plugin_name': self.l3_plugin, 'flavors_plugin_name': 'neutron.services.flavors.' 'flavors_plugin.FlavorsPlugin' } else: service_plugins = None # NOTE(ivasilevskaya) mocking this way allows some control over mocked # client like further method mocking with asserting calls self.client_mock = mock.MagicMock(name="mocked client") mock.patch('neutron.common.rpc.get_client' ).start().return_value = self.client_mock super(OvsAgentSchedulerTestCaseBase, self).setUp( 'ml2', service_plugins=service_plugins) mock.patch.object( self.plugin, 'filter_hosts_with_network_access', side_effect=lambda context, network_id, hosts: hosts).start() ext_mgr = extensions.PluginAwareExtensionManager.get_instance() self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) self.adminContext = context.get_admin_context() self.l3plugin = directory.get_plugin(plugin_constants.L3) self.l3_notify_p = mock.patch( 'neutron.extensions.l3agentscheduler.notify') self.patched_l3_notify = self.l3_notify_p.start() self.l3_periodic_p = mock.patch('neutron.db.l3_agentschedulers_db.' 'L3AgentSchedulerDbMixin.' 'add_periodic_l3_agent_status_check') self.patched_l3_periodic = self.l3_periodic_p.start() self.dhcp_notify_p = mock.patch( 'neutron.extensions.dhcpagentscheduler.notify') self.patched_dhcp_notify = self.dhcp_notify_p.start() class OvsAgentSchedulerTestCase(OvsAgentSchedulerTestCaseBase): def test_report_states(self): self._register_agent_states() agents = self._list_agents() self.assertEqual(4, len(agents['agents'])) def test_list_router_ids_on_host_no_l3_agent(self): l3_rpc_cb = l3_rpc.L3RpcCallback() self.assertEqual( [], l3_rpc_cb.get_router_ids(self.adminContext, host="fake host")) def test_network_scheduling_on_network_creation(self): self._register_agent_states() with self.network() as net: dhcp_agents = self._list_dhcp_agents_hosting_network( net['network']['id']) self.assertEqual(0, len(dhcp_agents['agents'])) def test_network_auto_schedule_with_disabled(self): cfg.CONF.set_override('allow_overlapping_ips', True) with self.subnet(), self.subnet(): dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback() self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTA) hostc_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTC) self._disable_agent(hosta_id) dhcp_rpc_cb.get_active_networks_info( self.adminContext, host=DHCP_HOSTA) # second agent will host all the networks since first is disabled. dhcp_rpc_cb.get_active_networks_info( self.adminContext, host=DHCP_HOSTC) networks = self._list_networks_hosted_by_dhcp_agent(hostc_id) num_hostc_nets = len(networks['networks']) networks = self._list_networks_hosted_by_dhcp_agent(hosta_id) num_hosta_nets = len(networks['networks']) self.assertEqual(0, num_hosta_nets) self.assertEqual(2, num_hostc_nets) def test_network_auto_schedule_with_no_dhcp(self): cfg.CONF.set_override('allow_overlapping_ips', True) with self.subnet(enable_dhcp=False), self.subnet(enable_dhcp=False): dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback() self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTA) hostc_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTC) self._disable_agent(hosta_id) dhcp_rpc_cb.get_active_networks_info( self.adminContext, host=DHCP_HOSTA) dhcp_rpc_cb.get_active_networks_info( self.adminContext, host=DHCP_HOSTC) networks = self._list_networks_hosted_by_dhcp_agent(hostc_id) num_hostc_nets = len(networks['networks']) networks = self._list_networks_hosted_by_dhcp_agent(hosta_id) num_hosta_nets = len(networks['networks']) self.assertEqual(0, num_hosta_nets) self.assertEqual(0, num_hostc_nets) def test_network_auto_schedule_with_multiple_agents(self): cfg.CONF.set_override('dhcp_agents_per_network', 2) cfg.CONF.set_override('allow_overlapping_ips', True) with self.subnet(), self.subnet(): dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback() self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTA) hostc_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTC) dhcp_rpc_cb.get_active_networks_info( self.adminContext, host=DHCP_HOSTA) dhcp_rpc_cb.get_active_networks_info( self.adminContext, host=DHCP_HOSTC) networks = self._list_networks_hosted_by_dhcp_agent(hostc_id) num_hostc_nets = len(networks['networks']) networks = self._list_networks_hosted_by_dhcp_agent(hosta_id) num_hosta_nets = len(networks['networks']) self.assertEqual(2, num_hosta_nets) self.assertEqual(2, num_hostc_nets) def test_network_auto_schedule_restart_dhcp_agent(self): cfg.CONF.set_override('dhcp_agents_per_network', 2) with self.subnet() as sub1: dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback() self._register_agent_states() dhcp_rpc_cb.get_active_networks_info( self.adminContext, host=DHCP_HOSTA) dhcp_rpc_cb.get_active_networks_info( self.adminContext, host=DHCP_HOSTA) dhcp_agents = self._list_dhcp_agents_hosting_network( sub1['subnet']['network_id']) self.assertEqual(1, len(dhcp_agents['agents'])) def test_network_auto_schedule_with_hosted(self): # one agent hosts all the networks, other hosts none cfg.CONF.set_override('allow_overlapping_ips', True) with self.subnet() as sub1, self.subnet(): dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback() self._register_agent_states() dhcp_rpc_cb.get_active_networks_info( self.adminContext, host=DHCP_HOSTA) # second agent will not host the network since first has got it. dhcp_rpc_cb.get_active_networks_info( self.adminContext, host=DHCP_HOSTC) dhcp_agents = self._list_dhcp_agents_hosting_network( sub1['subnet']['network_id']) hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTA) hostc_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTC) hosta_nets = self._list_networks_hosted_by_dhcp_agent(hosta_id) num_hosta_nets = len(hosta_nets['networks']) hostc_nets = self._list_networks_hosted_by_dhcp_agent(hostc_id) num_hostc_nets = len(hostc_nets['networks']) self.assertEqual(2, num_hosta_nets) self.assertEqual(0, num_hostc_nets) self.assertEqual(1, len(dhcp_agents['agents'])) self.assertEqual(DHCP_HOSTA, dhcp_agents['agents'][0]['host']) def test_network_auto_schedule_with_hosted_2(self): # one agent hosts one network dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback() cfg.CONF.set_override('allow_overlapping_ips', True) with self.subnet() as sub1: helpers.register_dhcp_agent(DHCP_HOSTA) dhcp_rpc_cb.get_active_networks_info( self.adminContext, host=DHCP_HOSTA) hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTA) self._disable_agent(hosta_id, admin_state_up=False) with self.subnet() as sub2: helpers.register_dhcp_agent(DHCP_HOSTC) dhcp_rpc_cb.get_active_networks_info(self.adminContext, host=DHCP_HOSTC) dhcp_agents_1 = self._list_dhcp_agents_hosting_network( sub1['subnet']['network_id']) dhcp_agents_2 = self._list_dhcp_agents_hosting_network( sub2['subnet']['network_id']) hosta_nets = self._list_networks_hosted_by_dhcp_agent(hosta_id) num_hosta_nets = len(hosta_nets['networks']) hostc_id = self._get_agent_id( constants.AGENT_TYPE_DHCP, DHCP_HOSTC) hostc_nets = self._list_networks_hosted_by_dhcp_agent(hostc_id) num_hostc_nets = len(hostc_nets['networks']) self.assertEqual(1, num_hosta_nets) self.assertEqual(1, num_hostc_nets) self.assertEqual(1, len(dhcp_agents_1['agents'])) self.assertEqual(1, len(dhcp_agents_2['agents'])) self.assertEqual(DHCP_HOSTA, dhcp_agents_1['agents'][0]['host']) self.assertEqual(DHCP_HOSTC, dhcp_agents_2['agents'][0]['host']) def test_network_scheduling_on_port_creation(self): with self.subnet() as subnet: dhcp_agents = self._list_dhcp_agents_hosting_network( subnet['subnet']['network_id']) result0 = len(dhcp_agents['agents']) self._register_agent_states() with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE) as port: dhcp_agents = self._list_dhcp_agents_hosting_network( port['port']['network_id']) result1 = len(dhcp_agents['agents']) self.assertEqual(0, result0) self.assertEqual(1, result1) def test_network_ha_scheduling_on_port_creation(self): cfg.CONF.set_override('dhcp_agents_per_network', 2) with self.subnet() as subnet: dhcp_agents = self._list_dhcp_agents_hosting_network( subnet['subnet']['network_id']) result0 = len(dhcp_agents['agents']) self._register_agent_states() with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE) as port: dhcp_agents = self._list_dhcp_agents_hosting_network( port['port']['network_id']) result1 = len(dhcp_agents['agents']) self.assertEqual(0, result0) self.assertEqual(2, result1) def test_network_ha_scheduling_on_port_creation_with_new_agent(self): cfg.CONF.set_override('dhcp_agents_per_network', 3) with self.subnet() as subnet: dhcp_agents = self._list_dhcp_agents_hosting_network( subnet['subnet']['network_id']) result0 = len(dhcp_agents['agents']) self._register_agent_states() with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE) as port: dhcp_agents = self._list_dhcp_agents_hosting_network( port['port']['network_id']) result1 = len(dhcp_agents['agents']) helpers.register_dhcp_agent('host1') with self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE) as port: dhcp_agents = self._list_dhcp_agents_hosting_network( port['port']['network_id']) result2 = len(dhcp_agents['agents']) self.assertEqual(0, result0) self.assertEqual(2, result1) self.assertEqual(3, result2) def test_network_scheduler_with_disabled_agent(self): helpers.register_dhcp_agent(DHCP_HOSTA) with self.port() as port1: dhcp_agents = self._list_dhcp_agents_hosting_network( port1['port']['network_id']) self._delete('ports', port1['port']['id']) self._delete('networks', port1['port']['network_id']) self.assertEqual(1, len(dhcp_agents['agents'])) agents = self._list_agents() self._disable_agent(agents['agents'][0]['id']) with self.port() as port2: dhcp_agents = self._list_dhcp_agents_hosting_network( port2['port']['network_id']) self._delete('ports', port2['port']['id']) self.assertEqual(0, len(dhcp_agents['agents'])) def test_is_eligible_agent(self): agent_startup = ('neutron.db.agentschedulers_db.' 'DhcpAgentSchedulerDbMixin.agent_starting_up') is_eligible_agent = ('neutron.db.agentschedulers_db.' 'AgentSchedulerDbMixin.is_eligible_agent') dhcp_mixin = agentschedulers_db.DhcpAgentSchedulerDbMixin() with mock.patch(agent_startup) as startup,\ mock.patch(is_eligible_agent) as elig: tests = [(True, True), (True, False), (False, True), (False, False)] for rv1, rv2 in tests: startup.return_value = rv1 elig.return_value = rv2 self.assertEqual(rv1 or rv2, dhcp_mixin.is_eligible_agent(None, None, None)) def test_network_scheduler_with_down_agent(self): helpers.register_dhcp_agent(DHCP_HOSTA) eligible_agent_str = ('neutron.db.agentschedulers_db.' 'DhcpAgentSchedulerDbMixin.is_eligible_agent') with mock.patch(eligible_agent_str) as eligible_agent: eligible_agent.return_value = True with self.port() as port: dhcp_agents = self._list_dhcp_agents_hosting_network( port['port']['network_id']) self._delete('ports', port['port']['id']) self._delete('networks', port['port']['network_id']) self.assertEqual(1, len(dhcp_agents['agents'])) with mock.patch(eligible_agent_str) as eligible_agent: eligible_agent.return_value = False with self.port() as port: dhcp_agents = self._list_dhcp_agents_hosting_network( port['port']['network_id']) self._delete('ports', port['port']['id']) self.assertEqual(0, len(dhcp_agents['agents'])) def test_network_scheduler_with_hosted_network(self): plugin = directory.get_plugin() helpers.register_dhcp_agent(DHCP_HOSTA) with self.port() as port1: dhcp_agents = self._list_dhcp_agents_hosting_network( port1['port']['network_id']) self.assertEqual(1, len(dhcp_agents['agents'])) with mock.patch.object(plugin, 'get_dhcp_agents_hosting_networks', autospec=True) as mock_hosting_agents: mock_hosting_agents.return_value = plugin.get_agent_objects( self.adminContext) with self.network('test') as net1: pass with self.subnet(network=net1, cidr='10.0.1.0/24') as subnet1: pass with self.port(subnet=subnet1) as port2: pass dhcp_agents = self._list_dhcp_agents_hosting_network( port2['port']['network_id']) self.assertEqual(0, len(dhcp_agents['agents'])) def test_network_policy(self): with self.network() as net1: self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTA) self._list_networks_hosted_by_dhcp_agent( hosta_id, expected_code=exc.HTTPForbidden.code, admin_context=False) self._add_network_to_dhcp_agent( hosta_id, net1['network']['id'], expected_code=exc.HTTPForbidden.code, admin_context=False) self._add_network_to_dhcp_agent(hosta_id, net1['network']['id']) self._remove_network_from_dhcp_agent( hosta_id, net1['network']['id'], expected_code=exc.HTTPForbidden.code, admin_context=False) self._list_dhcp_agents_hosting_network( net1['network']['id'], expected_code=exc.HTTPForbidden.code, admin_context=False) def _test_network_add_to_dhcp_agent(self, admin_state_up=True): with self.network() as net1: self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTA) if not admin_state_up: self._set_agent_admin_state_up(DHCP_HOSTA, False) num_before_add = len( self._list_networks_hosted_by_dhcp_agent( hosta_id)['networks']) self._add_network_to_dhcp_agent(hosta_id, net1['network']['id']) num_after_add = len( self._list_networks_hosted_by_dhcp_agent( hosta_id)['networks']) self.assertEqual(0, num_before_add) self.assertEqual(1, num_after_add) def test_network_add_to_dhcp_agent(self): self._test_network_add_to_dhcp_agent() def test_network_add_to_dhcp_agent_with_admin_state_down(self): cfg.CONF.set_override( 'enable_services_on_agents_with_admin_state_down', True) self._test_network_add_to_dhcp_agent(admin_state_up=False) def test_network_remove_from_dhcp_agent(self): agent = helpers.register_dhcp_agent(DHCP_HOSTA) hosta_id = agent.id with self.port() as port1: num_before_remove = len( self._list_networks_hosted_by_dhcp_agent( hosta_id)['networks']) self._remove_network_from_dhcp_agent(hosta_id, port1['port']['network_id']) num_after_remove = len( self._list_networks_hosted_by_dhcp_agent( hosta_id)['networks']) self.assertEqual(1, num_before_remove) self.assertEqual(0, num_after_remove) def test_list_active_networks_on_not_registered_yet_dhcp_agent(self): plugin = directory.get_plugin() nets = plugin.list_active_networks_on_active_dhcp_agent( self.adminContext, host=DHCP_HOSTA) self.assertEqual([], nets) def test_reserved_port_after_network_remove_from_dhcp_agent(self): helpers.register_dhcp_agent(DHCP_HOSTA) hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTA) with self.port(device_owner=constants.DEVICE_OWNER_DHCP, host=DHCP_HOSTA) as port1: self._remove_network_from_dhcp_agent(hosta_id, port1['port']['network_id']) port_res = self._list_ports( 'json', 200, network_id=port1['port']['network_id']) port_list = self.deserialize('json', port_res) self.assertEqual(port_list['ports'][0]['device_id'], constants.DEVICE_ID_RESERVED_DHCP_PORT) def _test_get_active_networks_from_admin_state_down_agent(self, keep_services): if keep_services: cfg.CONF.set_override( 'enable_services_on_agents_with_admin_state_down', True) helpers.register_dhcp_agent(DHCP_HOSTA) dhcp_rpc_cb = dhcp_rpc.DhcpRpcCallback() with self.port(): nets = dhcp_rpc_cb.get_active_networks_info(self.adminContext, host=DHCP_HOSTA) self.assertEqual(1, len(nets)) self._set_agent_admin_state_up(DHCP_HOSTA, False) nets = dhcp_rpc_cb.get_active_networks_info(self.adminContext, host=DHCP_HOSTA) if keep_services: self.assertEqual(1, len(nets)) else: self.assertEqual(0, len(nets)) def test_dhcp_agent_keep_services_off(self): self._test_get_active_networks_from_admin_state_down_agent(False) def test_dhcp_agent_keep_services_on(self): self._test_get_active_networks_from_admin_state_down_agent(True) def _take_down_agent_and_run_reschedule(self, host): # take down the agent on host A and ensure B is alive self.adminContext.session.begin(subtransactions=True) query = self.adminContext.session.query(agent_model.Agent) agt = query.filter_by(host=host).first() agt.heartbeat_timestamp = ( agt.heartbeat_timestamp - datetime.timedelta(hours=1)) self.adminContext.session.commit() plugin = directory.get_plugin(plugin_constants.L3) plugin.reschedule_routers_from_down_agents() def _set_agent_admin_state_up(self, host, state): self.adminContext.session.begin(subtransactions=True) query = self.adminContext.session.query(agent_model.Agent) agt_db = query.filter_by(host=host).first() agt_db.admin_state_up = state self.adminContext.session.commit() def test_router_rescheduler_catches_rpc_db_and_reschedule_exceptions(self): with self.router(): l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() # schedule the router to host A l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) plugin = directory.get_plugin(plugin_constants.L3) mock.patch.object( plugin, 'reschedule_router', side_effect=[ db_exc.DBError(), oslo_messaging.RemoteError(), l3agentscheduler.RouterReschedulingFailed(router_id='f', agent_id='f'), ValueError('this raises'), Exception() ]).start() self._take_down_agent_and_run_reschedule(L3_HOSTA) # DBError self._take_down_agent_and_run_reschedule(L3_HOSTA) # RemoteError self._take_down_agent_and_run_reschedule(L3_HOSTA) # schedule err self._take_down_agent_and_run_reschedule(L3_HOSTA) # Value error self._take_down_agent_and_run_reschedule(L3_HOSTA) # Exception def test_router_rescheduler_catches_exceptions_on_fetching_bindings(self): with mock.patch('neutron_lib.context.get_admin_context') as get_ctx: mock_ctx = mock.Mock() get_ctx.return_value = mock_ctx mock_ctx.session.query.side_effect = db_exc.DBError() plugin = directory.get_plugin(plugin_constants.L3) # check that no exception is raised plugin.reschedule_routers_from_down_agents() def test_router_rescheduler_iterates_after_reschedule_failure(self): plugin = directory.get_plugin(plugin_constants.L3) l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() with self.router() as r1, self.router() as r2: # schedule the routers to host A l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) rs_mock = mock.patch.object( plugin, 'reschedule_router', side_effect=l3agentscheduler.RouterReschedulingFailed( router_id='f', agent_id='f'), ).start() self._take_down_agent_and_run_reschedule(L3_HOSTA) # make sure both had a reschedule attempt even though first failed rs_mock.assert_has_calls([mock.call(mock.ANY, r1['router']['id']), mock.call(mock.ANY, r2['router']['id'])], any_order=True) def test_router_is_not_rescheduled_from_alive_agent(self): with self.router(): l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() # schedule the router to host A l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) with mock.patch('neutron.db.l3_agentschedulers_db.' 'L3AgentSchedulerDbMixin.reschedule_router') as rr: # take down some unrelated agent and run reschedule check self._take_down_agent_and_run_reschedule(DHCP_HOSTC) self.assertFalse(rr.called) def test_router_is_not_rescheduled_if_agent_is_back_online(self): plugin = directory.get_plugin(plugin_constants.L3) l3_rpc_cb = l3_rpc.L3RpcCallback() agent = helpers.register_l3_agent(host=L3_HOSTA) with self.router(),\ self.router(),\ mock.patch.object(plugin, 'reschedule_router') as rs_mock,\ mock.patch.object(plugin, '_get_agent') as get_agent_mock: # schedule the routers to the agent l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) self._take_down_agent_and_run_reschedule(L3_HOSTA) # since _get_agent is mocked it will return Mock object and # agent.is_active will return true, so no rescheduling will be done self.assertFalse(rs_mock.called) # should be called only once as for second router alive agent id # will be in cache get_agent_mock.assert_called_once_with(mock.ANY, agent['id']) def test_router_reschedule_from_dead_agent(self): with self.router(): l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() # schedule the router to host A ret_a = l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) self._take_down_agent_and_run_reschedule(L3_HOSTA) # B should now pick up the router ret_b = l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTB) self.assertEqual(ret_b, ret_a) def test_router_no_reschedule_from_dead_admin_down_agent(self): with self.router() as r: l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() # schedule the router to host A l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) self._set_agent_admin_state_up(L3_HOSTA, False) self._take_down_agent_and_run_reschedule(L3_HOSTA) # A should still have it even though it was inactive due to the # admin_state being down bindings = rb_obj.RouterL3AgentBinding.get_objects( self.adminContext, router_id=r['router']['id']) binding = bindings.pop() if bindings else None l3_agent = ag_obj.Agent.get_objects( self.adminContext, id=binding.l3_agent_id) self.assertEqual(l3_agent[0].host, L3_HOSTA) # B should not pick up the router ret_b = l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTB) self.assertFalse(ret_b) def test_router_reschedule_succeeded_after_failed_notification(self): l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() with self.router() as router: # schedule the router to host A l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) ctxt_mock = mock.MagicMock() call_mock = mock.MagicMock( side_effect=[oslo_messaging.MessagingTimeout, None]) ctxt_mock.call = call_mock self.client_mock.prepare = mock.MagicMock(return_value=ctxt_mock) self._take_down_agent_and_run_reschedule(L3_HOSTA) self.assertEqual(2, call_mock.call_count) # make sure router was rescheduled even when first attempt # failed to notify l3 agent l3_agents = self._list_l3_agents_hosting_router( router['router']['id'])['agents'] self.assertEqual(1, len(l3_agents)) self.assertEqual(L3_HOSTB, l3_agents[0]['host']) def test_router_reschedule_failed_notification_all_attempts(self): l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() with self.router() as router: # schedule the router to host A l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) # mock client.prepare and context.call ctxt_mock = mock.MagicMock() call_mock = mock.MagicMock( side_effect=oslo_messaging.MessagingTimeout) ctxt_mock.call = call_mock self.client_mock.prepare = mock.MagicMock(return_value=ctxt_mock) # perform operations self._take_down_agent_and_run_reschedule(L3_HOSTA) self.assertEqual( l3_rpc_agent_api.AGENT_NOTIFY_MAX_ATTEMPTS, call_mock.call_count) l3_agents = self._list_l3_agents_hosting_router( router['router']['id'])['agents'] self.assertEqual(0, len(l3_agents)) def test_router_auto_schedule_with_invalid_router(self): with self.router() as router: l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() self._delete('routers', router['router']['id']) # deleted router ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA, router_ids=[router['router']['id']]) self.assertFalse(ret_a) # non-existent router ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA, router_ids=[uuidutils.generate_uuid()]) self.assertFalse(ret_a) def test_router_auto_schedule_with_hosted(self): with self.router() as router: l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() ret_a = l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) ret_b = l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTB) l3_agents = self._list_l3_agents_hosting_router( router['router']['id']) self.assertEqual(1, len(ret_a)) self.assertIn(router['router']['id'], ret_a) self.assertFalse(len(ret_b)) self.assertEqual(1, len(l3_agents['agents'])) self.assertEqual(L3_HOSTA, l3_agents['agents'][0]['host']) def test_router_auto_schedule_restart_l3_agent(self): with self.router(): l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) def test_router_auto_schedule_with_hosted_2(self): # one agent hosts one router l3_rpc_cb = l3_rpc.L3RpcCallback() with self.router() as router1: hosta_id = helpers.register_l3_agent(host=L3_HOSTA).id l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) self._disable_agent(hosta_id, admin_state_up=False) with self.router() as router2: hostb_id = helpers.register_l3_agent(host=L3_HOSTB).id l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTB) l3_agents_1 = self._list_l3_agents_hosting_router( router1['router']['id']) l3_agents_2 = self._list_l3_agents_hosting_router( router2['router']['id']) hosta_routers = self._list_routers_hosted_by_l3_agent(hosta_id) num_hosta_routers = len(hosta_routers['routers']) hostb_routers = self._list_routers_hosted_by_l3_agent(hostb_id) num_hostb_routers = len(hostb_routers['routers']) self.assertEqual(1, num_hosta_routers) self.assertEqual(1, num_hostb_routers) self.assertEqual(1, len(l3_agents_1['agents'])) self.assertEqual(1, len(l3_agents_2['agents'])) self.assertEqual(L3_HOSTA, l3_agents_1['agents'][0]['host']) self.assertEqual(L3_HOSTB, l3_agents_2['agents'][0]['host']) def test_router_auto_schedule_with_disabled(self): with self.router(), self.router(): l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTA) hostb_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTB) self._disable_agent(hosta_id) # first agent will not host router since it is disabled l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) # second agent will host all the routers since first is disabled. l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTB) hostb_routers = self._list_routers_hosted_by_l3_agent(hostb_id) num_hostb_routers = len(hostb_routers['routers']) hosta_routers = self._list_routers_hosted_by_l3_agent(hosta_id) num_hosta_routers = len(hosta_routers['routers']) self.assertEqual(2, num_hostb_routers) self.assertEqual(0, num_hosta_routers) def test_rpc_sync_routers(self): l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() # No routers ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA) self.assertEqual(0, len(ret_a)) with self.router() as v1, self.router() as v2, self.router() as v3: routers = (v1, v2, v3) router_ids = [r['router']['id'] for r in routers] # auto schedule routers first l3_rpc_cb.get_router_ids(self.adminContext, host=L3_HOSTA) # Get all routers ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA) self.assertEqual(3, len(ret_a)) self.assertEqual(set(router_ids), set([r['id'] for r in ret_a])) # Get all routers (router_ids=None) ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA, router_ids=None) self.assertEqual(3, len(ret_a)) self.assertEqual(set(router_ids), set([r['id'] for r in ret_a])) # Get router2 only ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA, router_ids=[router_ids[1]]) self.assertEqual(1, len(ret_a)) self.assertIn(router_ids[1], [r['id'] for r in ret_a]) # Get router1 and router3 ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA, router_ids=[router_ids[0], router_ids[2]]) self.assertEqual(2, len(ret_a)) self.assertIn(router_ids[0], [r['id'] for r in ret_a]) self.assertIn(router_ids[2], [r['id'] for r in ret_a]) def test_sync_router(self): l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTA) with self.router() as r1: ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA, router_ids=[r1['router']['id']]) # Not return router to agent if the router is not bound to it. self.assertEqual([], ret_a) host_routers = self._list_routers_hosted_by_l3_agent(hosta_id) # No router will be auto scheduled. self.assertEqual(0, len(host_routers['routers'])) def test_sync_dvr_router(self): l3_rpc_cb = l3_rpc.L3RpcCallback() dvr_agents = self._register_dvr_agents() with self.router() as r1, \ mock.patch.object(self.l3plugin, 'get_subnet_ids_on_router', return_value=['fake_subnet_id']), \ mock.patch.object(self.l3plugin, '_check_dvr_serviceable_ports_on_host', return_value=True): for l3_agent in dvr_agents: host = l3_agent['host'] ret_a = l3_rpc_cb.sync_routers(self.adminContext, host=host, router_ids=[r1['router']['id']]) router_ids = [r['id'] for r in ret_a] # Return router to agent if there is dvr service port in agent. self.assertIn(r1['router']['id'], router_ids) host_routers = self._list_routers_hosted_by_l3_agent( l3_agent['id']) # No router will be auto scheduled. self.assertEqual(0, len(host_routers['routers'])) def test_router_without_l3_agents(self): with self.subnet() as s: self._set_net_external(s['subnet']['network_id']) data = {'router': {'tenant_id': uuidutils.generate_uuid()}} data['router']['name'] = 'router1' data['router']['external_gateway_info'] = { 'network_id': s['subnet']['network_id']} router_req = self.new_create_request('routers', data, self.fmt) res = router_req.get_response(self.ext_api) router = self.deserialize(self.fmt, res) l3agents = ( self.l3plugin.get_l3_agents_hosting_routers( self.adminContext, [router['router']['id']])) self._delete('routers', router['router']['id']) self.assertEqual(0, len(l3agents)) def test_dvr_router_scheduling_to_only_dvr_snat_agent(self): self._register_dvr_agents() with self.subnet() as s: net_id = s['subnet']['network_id'] self._set_net_external(net_id) router = {'name': 'router1', 'external_gateway_info': {'network_id': net_id}, 'tenant_id': 'tenant_id', 'admin_state_up': True, 'distributed': True} r = self.l3plugin.create_router(self.adminContext, {'router': router}) with mock.patch.object( self.l3plugin, '_check_dvr_serviceable_ports_on_host') as ports_exist: # emulating dvr serviceable ports exist on compute node ports_exist.return_value = True self.l3plugin.schedule_router( self.adminContext, r['id']) l3agents = self._list_l3_agents_hosting_router(r['id']) self.assertEqual(1, len(l3agents['agents'])) agent = l3agents['agents'][0] self.assertEqual('dvr_snat', agent['configurations']['agent_mode']) def test_dvr_router_csnat_rescheduling(self): helpers.register_l3_agent( host=L3_HOSTA, agent_mode=constants.L3_AGENT_MODE_DVR_SNAT) helpers.register_l3_agent( host=L3_HOSTB, agent_mode=constants.L3_AGENT_MODE_DVR_SNAT) with self.subnet() as s: net_id = s['subnet']['network_id'] self._set_net_external(net_id) router = {'name': 'router1', 'external_gateway_info': {'network_id': net_id}, 'tenant_id': 'tenant_id', 'admin_state_up': True, 'distributed': True} r = self.l3plugin.create_router(self.adminContext, {'router': router}) self.l3plugin.schedule_router( self.adminContext, r['id']) l3agents = self._list_l3_agents_hosting_router(r['id']) self.assertEqual(1, len(l3agents['agents'])) agent_host = l3agents['agents'][0]['host'] self._take_down_agent_and_run_reschedule(agent_host) l3agents = self._list_l3_agents_hosting_router(r['id']) self.assertEqual(1, len(l3agents['agents'])) new_agent_host = l3agents['agents'][0]['host'] self.assertNotEqual(agent_host, new_agent_host) def test_dvr_router_manual_rescheduling(self): helpers.register_l3_agent( host=L3_HOSTA, agent_mode=constants.L3_AGENT_MODE_DVR_SNAT) helpers.register_l3_agent( host=L3_HOSTB, agent_mode=constants.L3_AGENT_MODE_DVR_SNAT) with self.subnet() as s: net_id = s['subnet']['network_id'] self._set_net_external(net_id) router = {'name': 'router1', 'external_gateway_info': {'network_id': net_id}, 'tenant_id': 'tenant_id', 'admin_state_up': True, 'distributed': True} r = self.l3plugin.create_router(self.adminContext, {'router': router}) self.l3plugin.schedule_router( self.adminContext, r['id']) l3agents = self.l3plugin.list_l3_agents_hosting_router( self.adminContext, r['id']) self.assertEqual(1, len(l3agents['agents'])) agent = l3agents['agents'][0] # NOTE: Removing the router from the l3_agent will # remove all the namespace since there is no other # serviceable ports in the node that requires it. self.l3plugin.remove_router_from_l3_agent( self.adminContext, agent['id'], r['id']) l3agents = self.l3plugin.list_l3_agents_hosting_router( self.adminContext, r['id']) self.assertEqual(0, len(l3agents['agents'])) self.l3plugin.add_router_to_l3_agent( self.adminContext, agent['id'], r['id']) l3agents = self.l3plugin.list_l3_agents_hosting_router( self.adminContext, r['id']) self.assertEqual(1, len(l3agents['agents'])) new_agent = l3agents['agents'][0] self.assertEqual(agent['id'], new_agent['id']) def test_router_sync_data(self): with self.subnet() as s1,\ self.subnet(cidr='10.0.2.0/24') as s2,\ self.subnet(cidr='10.0.3.0/24') as s3: self._register_agent_states() self._set_net_external(s1['subnet']['network_id']) data = {'router': {'tenant_id': uuidutils.generate_uuid()}} data['router']['name'] = 'router1' data['router']['external_gateway_info'] = { 'network_id': s1['subnet']['network_id']} router_req = self.new_create_request('routers', data, self.fmt) res = router_req.get_response(self.ext_api) router = self.deserialize(self.fmt, res) self._router_interface_action('add', router['router']['id'], s2['subnet']['id'], None) self._router_interface_action('add', router['router']['id'], s3['subnet']['id'], None) l3agents = self._list_l3_agents_hosting_router( router['router']['id']) self.assertEqual(1, len(l3agents['agents'])) agents = self._list_agents() another_l3_agent_id = None another_l3_agent_host = None default = l3agents['agents'][0]['id'] for com in agents['agents']: if (com['id'] != default and com['agent_type'] == constants.AGENT_TYPE_L3): another_l3_agent_id = com['id'] another_l3_agent_host = com['host'] break self.assertIsNotNone(another_l3_agent_id) self._add_router_to_l3_agent(another_l3_agent_id, router['router']['id'], expected_code=exc.HTTPConflict.code) self._remove_router_from_l3_agent(default, router['router']['id']) self._add_router_to_l3_agent(another_l3_agent_id, router['router']['id']) l3agents = self._list_l3_agents_hosting_router( router['router']['id']) self.assertEqual(another_l3_agent_host, l3agents['agents'][0]['host']) self._remove_router_from_l3_agent(another_l3_agent_id, router['router']['id']) self._router_interface_action('remove', router['router']['id'], s2['subnet']['id'], None) l3agents = self._list_l3_agents_hosting_router( router['router']['id']) self.assertEqual(1, len(l3agents['agents'])) self._router_interface_action('remove', router['router']['id'], s3['subnet']['id'], None) self._delete('routers', router['router']['id']) def _test_router_add_to_l3_agent(self, admin_state_up=True): with self.router() as router1: self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTA) if not admin_state_up: self._set_agent_admin_state_up(L3_HOSTA, False) num_before_add = len( self._list_routers_hosted_by_l3_agent( hosta_id)['routers']) self._add_router_to_l3_agent(hosta_id, router1['router']['id']) hostb_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTB) self._add_router_to_l3_agent(hostb_id, router1['router']['id'], expected_code=exc.HTTPConflict.code) num_after_add = len( self._list_routers_hosted_by_l3_agent( hosta_id)['routers']) self.assertEqual(0, num_before_add) self.assertEqual(1, num_after_add) def test_router_add_to_l3_agent(self): self._test_router_add_to_l3_agent() def test_router_add_to_l3_agent_with_admin_state_down(self): cfg.CONF.set_override( 'enable_services_on_agents_with_admin_state_down', True) self._test_router_add_to_l3_agent(admin_state_up=False) def test_router_add_to_l3_agent_two_times(self): with self.router() as router1: self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTA) self._add_router_to_l3_agent(hosta_id, router1['router']['id']) # scheduling twice on the same agent is fine self._add_router_to_l3_agent(hosta_id, router1['router']['id']) def test_router_add_to_two_l3_agents(self): with self.router() as router1: self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTA) hostb_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTB) self._add_router_to_l3_agent(hosta_id, router1['router']['id']) self._add_router_to_l3_agent(hostb_id, router1['router']['id'], expected_code=exc.HTTPConflict.code) def test_router_policy(self): with self.router() as router1: self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTA) self._list_routers_hosted_by_l3_agent( hosta_id, expected_code=exc.HTTPForbidden.code, admin_context=False) self._add_router_to_l3_agent( hosta_id, router1['router']['id'], expected_code=exc.HTTPForbidden.code, admin_context=False) self._add_router_to_l3_agent( hosta_id, router1['router']['id']) self._remove_router_from_l3_agent( hosta_id, router1['router']['id'], expected_code=exc.HTTPForbidden.code, admin_context=False) self._list_l3_agents_hosting_router( router1['router']['id'], expected_code=exc.HTTPForbidden.code, admin_context=False) def _test_sync_routers_from_admin_state_down_agent(self, keep_services): if keep_services: cfg.CONF.set_override( 'enable_services_on_agents_with_admin_state_down', True) l3_rpc_cb = l3_rpc.L3RpcCallback() self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTA) with self.router() as router: self._add_router_to_l3_agent(hosta_id, router['router']['id']) routers = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA) self.assertEqual(1, len(routers)) self._set_agent_admin_state_up(L3_HOSTA, False) routers = l3_rpc_cb.sync_routers(self.adminContext, host=L3_HOSTA) if keep_services: self.assertEqual(1, len(routers)) else: self.assertEqual(0, len(routers)) def test_l3_agent_keep_services_off(self): self._test_sync_routers_from_admin_state_down_agent(False) def test_l3_agent_keep_services_on(self): self._test_sync_routers_from_admin_state_down_agent(True) def test_list_routers_hosted_by_l3_agent_with_invalid_agent(self): invalid_agentid = 'non_existing_agent' self._list_routers_hosted_by_l3_agent(invalid_agentid, exc.HTTPNotFound.code) def test_list_networks_hosted_by_dhcp_agent_with_invalid_agent(self): invalid_agentid = 'non_existing_agent' self._list_networks_hosted_by_dhcp_agent(invalid_agentid, exc.HTTPNotFound.code) class OvsDhcpAgentNotifierTestCase(test_agent.AgentDBTestMixIn, AgentSchedulerTestMixIn, test_plugin.NeutronDbPluginV2TestCase): def setUp(self): super(OvsDhcpAgentNotifierTestCase, self).setUp('ml2') mock.patch.object( self.plugin, 'filter_hosts_with_network_access', side_effect=lambda context, network_id, hosts: hosts).start() plugin = directory.get_plugin() self.dhcp_notifier = plugin.agent_notifiers[constants.AGENT_TYPE_DHCP] self.dhcp_notifier_cast = mock.patch( 'neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.' 'DhcpAgentNotifyAPI._cast_message').start() ext_mgr = extensions.PluginAwareExtensionManager.get_instance() self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) self.adminContext = context.get_admin_context() fake_notifier.reset() def test_network_add_to_dhcp_agent_notification(self): with self.network() as net1: network_id = net1['network']['id'] self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTA) self._add_network_to_dhcp_agent(hosta_id, network_id) self.dhcp_notifier_cast.assert_called_with( mock.ANY, 'network_create_end', {'network': {'id': network_id}, 'priority': dhcp_rpc_agent_api.PRIORITY_NETWORK_CREATE}, DHCP_HOSTA) notifications = fake_notifier.NOTIFICATIONS expected_event_type = 'dhcp_agent.network.add' self._assert_notify(notifications, expected_event_type) def test_network_remove_from_dhcp_agent_notification(self): with self.network() as net1: network_id = net1['network']['id'] self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTA) self._add_network_to_dhcp_agent(hosta_id, network_id) self._remove_network_from_dhcp_agent(hosta_id, network_id) self.dhcp_notifier_cast.assert_called_with( mock.ANY, 'network_delete_end', {'network_id': network_id, 'priority': dhcp_rpc_agent_api.PRIORITY_NETWORK_DELETE}, DHCP_HOSTA) notifications = fake_notifier.NOTIFICATIONS expected_event_type = 'dhcp_agent.network.remove' self._assert_notify(notifications, expected_event_type) def test_agent_updated_dhcp_agent_notification(self): self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP, DHCP_HOSTA) self._disable_agent(hosta_id, admin_state_up=False) self.dhcp_notifier_cast.assert_called_with( mock.ANY, 'agent_updated', {'admin_state_up': False}, DHCP_HOSTA) def _api_network_port_create( self, hosts, gateway=constants.ATTR_NOT_SPECIFIED, owner=None): for host in hosts: helpers.register_dhcp_agent(host) with self.network() as net1: with self.subnet(network=net1, gateway_ip=gateway) as subnet1: if owner: with self.port(subnet=subnet1, device_owner=owner) as port: return [net1, subnet1, port] else: with self.port(subnet=subnet1) as port: return [net1, subnet1, port] def _network_port_create(self, *args, **kwargs): net, sub, port = self._api_network_port_create(*args, **kwargs) dhcp_notifier = self.plugin.agent_notifiers[constants.AGENT_TYPE_DHCP] if (not hasattr(dhcp_notifier, 'uses_native_notifications') or not all(dhcp_notifier.uses_native_notifications[r]['create'] for r in ('port', 'subnet', 'network'))): return net, sub, port # since plugin has native dhcp notifications, the payloads will be the # same as the getter outputs ctx = context.get_admin_context() net['network'] = self.plugin.get_network(ctx, net['network']['id']) sub['subnet'] = self.plugin.get_subnet(ctx, sub['subnet']['id']) sub['priority'] = dhcp_rpc_agent_api.PRIORITY_SUBNET_UPDATE port['port'] = self.plugin.get_port(ctx, port['port']['id']) return net, sub, port def _notification_mocks(self, hosts, net, subnet, port, port_priority): host_calls = {} for host in hosts: expected_calls = [ mock.call( mock.ANY, 'network_create_end', {'priority': dhcp_rpc_agent_api.PRIORITY_NETWORK_CREATE, 'network': {'id': net['network']['id']}}, host), mock.call( mock.ANY, 'subnet_create_end', subnet, host, 'dhcp_agent'), mock.call( mock.ANY, 'port_create_end', {'port': port['port'], 'priority': port_priority}, host, 'dhcp_agent')] host_calls[host] = expected_calls return host_calls def test_network_port_create_notification(self): hosts = [DHCP_HOSTA] net, subnet, port = self._network_port_create(hosts) expected_calls = self._notification_mocks( hosts, net, subnet, port, dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH) self.assertEqual( expected_calls[DHCP_HOSTA], self.dhcp_notifier_cast.call_args_list) def test_network_ha_port_create_notification(self): cfg.CONF.set_override('dhcp_agents_per_network', 3) hosts = [DHCP_HOSTA, DHCP_HOSTC, DHCP_HOSTD] net, subnet, port = self._network_port_create(hosts) for host_call in self.dhcp_notifier_cast.call_args_list: if ("'priority': " + str( dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH) in str(host_call)): if DHCP_HOSTA in str(host_call): expected_high_calls = self._notification_mocks( [DHCP_HOSTA], net, subnet, port, dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH) high_host = DHCP_HOSTA hosts.pop(0) elif DHCP_HOSTC in str(host_call): expected_high_calls = self._notification_mocks( [DHCP_HOSTC], net, subnet, port, dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH) high_host = DHCP_HOSTC hosts.pop(1) elif DHCP_HOSTD in str(host_call): expected_high_calls = self._notification_mocks( [DHCP_HOSTD], net, subnet, port, dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH) high_host = DHCP_HOSTD hosts.pop(2) expected_low_calls = self._notification_mocks( hosts, net, subnet, port, dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_LOW) for expected in expected_high_calls[high_host]: self.assertIn(expected, self.dhcp_notifier_cast.call_args_list) for host, low_expecteds in expected_low_calls.items(): for expected in low_expecteds: self.assertIn(expected, self.dhcp_notifier_cast.call_args_list) def _is_schedule_network_called(self, device_id): dhcp_notifier_schedule = mock.patch( 'neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.' 'DhcpAgentNotifyAPI._schedule_network').start() plugin = directory.get_plugin() with self.subnet() as subnet,\ self.port(subnet=subnet, device_id=device_id),\ mock.patch.object(plugin, 'get_dhcp_agents_hosting_networks', return_value=[]): return dhcp_notifier_schedule.call_count > 1 def test_reserved_dhcp_port_creation(self): device_id = constants.DEVICE_ID_RESERVED_DHCP_PORT self.assertFalse(self._is_schedule_network_called(device_id)) def test_unreserved_dhcp_port_creation(self): device_id = 'not_reserved' self.assertTrue(self._is_schedule_network_called(device_id)) class OvsL3AgentNotifierTestCase(test_l3.L3NatTestCaseMixin, test_agent.AgentDBTestMixIn, AgentSchedulerTestMixIn, test_plugin.NeutronDbPluginV2TestCase): l3_plugin = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatAgentSchedulingServicePlugin') def setUp(self): self.dhcp_notifier_cls_p = mock.patch( 'neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.' 'DhcpAgentNotifyAPI') self.dhcp_notifier = mock.Mock(name='dhcp_notifier') self.dhcp_notifier_cls = self.dhcp_notifier_cls_p.start() self.dhcp_notifier_cls.return_value = self.dhcp_notifier if self.l3_plugin: service_plugins = { 'l3_plugin_name': self.l3_plugin, 'flavors_plugin_name': 'neutron.services.flavors.' 'flavors_plugin.FlavorsPlugin' } else: service_plugins = None super(OvsL3AgentNotifierTestCase, self).setUp( 'ml2', service_plugins=service_plugins) ext_mgr = extensions.PluginAwareExtensionManager.get_instance() self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) self.adminContext = context.get_admin_context() fake_notifier.reset() def test_router_add_to_l3_agent_notification(self): l3_plugin = directory.get_plugin(plugin_constants.L3) l3_notifier = l3_plugin.agent_notifiers[constants.AGENT_TYPE_L3] with mock.patch.object( l3_notifier.client, 'prepare', return_value=l3_notifier.client) as mock_prepare,\ mock.patch.object(l3_notifier.client, 'call') as mock_call,\ self.router() as router1: self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTA) self._add_router_to_l3_agent(hosta_id, router1['router']['id']) routers = [router1['router']['id']] mock_prepare.assert_called_with(server='hosta') mock_call.assert_called_with( mock.ANY, 'router_added_to_agent', payload=routers) notifications = fake_notifier.NOTIFICATIONS expected_event_type = 'l3_agent.router.add' self._assert_notify(notifications, expected_event_type) def test_router_remove_from_l3_agent_notification(self): l3_plugin = directory.get_plugin(plugin_constants.L3) l3_notifier = l3_plugin.agent_notifiers[constants.AGENT_TYPE_L3] with mock.patch.object( l3_notifier.client, 'prepare', return_value=l3_notifier.client) as mock_prepare,\ mock.patch.object(l3_notifier.client, 'cast') as mock_cast,\ mock.patch.object(l3_notifier.client, 'call'),\ self.router() as router1: self._register_agent_states() hosta_id = self._get_agent_id(constants.AGENT_TYPE_L3, L3_HOSTA) self._add_router_to_l3_agent(hosta_id, router1['router']['id']) self._remove_router_from_l3_agent(hosta_id, router1['router']['id']) mock_prepare.assert_called_with(server='hosta') mock_cast.assert_called_with( mock.ANY, 'router_removed_from_agent', payload={'router_id': router1['router']['id']}) notifications = fake_notifier.NOTIFICATIONS expected_event_type = 'l3_agent.router.remove' self._assert_notify(notifications, expected_event_type) def test_agent_updated_l3_agent_notification(self): l3_plugin = directory.get_plugin(plugin_constants.L3) l3_notifier = l3_plugin.agent_notifiers[constants.AGENT_TYPE_L3] with mock.patch.object( l3_notifier.client, 'prepare', return_value=l3_notifier.client) as mock_prepare,\ mock.patch.object(l3_notifier.client, 'cast') as mock_cast: agent_id = helpers.register_l3_agent(L3_HOSTA).id self._disable_agent(agent_id, admin_state_up=False) mock_prepare.assert_called_with(server=L3_HOSTA) mock_cast.assert_called_with( mock.ANY, 'agent_updated', payload={'admin_state_up': False}) neutron-12.1.1/neutron/tests/unit/db/test_allowedaddresspairs_db.py0000664000175000017500000004262513553660047025644 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef from neutron_lib.api.definitions import port_security as psec from neutron_lib.api import validators from neutron_lib.plugins import directory from oslo_config import cfg from webob import exc as web_exc from neutron.db import allowedaddresspairs_db as addr_pair_db from neutron.db import db_base_plugin_v2 from neutron.db import portsecurity_db from neutron.extensions import securitygroup as secgroup from neutron.tests.unit.db import test_db_base_plugin_v2 DB_PLUGIN_KLASS = ('neutron.tests.unit.db.test_allowedaddresspairs_db.' 'AllowedAddressPairTestPlugin') class AllowedAddressPairTestCase( test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def setUp(self, plugin=None, ext_mgr=None): super(AllowedAddressPairTestCase, self).setUp(plugin) # Check if a plugin supports security groups plugin_obj = directory.get_plugin() self._skip_port_security = ('port-security' not in plugin_obj.supported_extension_aliases) class AllowedAddressPairTestPlugin(portsecurity_db.PortSecurityDbMixin, db_base_plugin_v2.NeutronDbPluginV2, addr_pair_db.AllowedAddressPairsMixin): """Test plugin that implements necessary calls on create/delete port for associating ports with port security and allowed address pairs. """ supported_extension_aliases = ["allowed-address-pairs"] def create_port(self, context, port): p = port['port'] with context.session.begin(subtransactions=True): neutron_db = super(AllowedAddressPairTestPlugin, self).create_port( context, port) p.update(neutron_db) if validators.is_attr_set(p.get(addr_apidef.ADDRESS_PAIRS)): self._process_create_allowed_address_pairs( context, p, p[addr_apidef.ADDRESS_PAIRS]) else: p[addr_apidef.ADDRESS_PAIRS] = None return port['port'] def update_port(self, context, id, port): delete_addr_pairs = self._check_update_deletes_allowed_address_pairs( port) has_addr_pairs = self._check_update_has_allowed_address_pairs(port) with context.session.begin(subtransactions=True): ret_port = super(AllowedAddressPairTestPlugin, self).update_port( context, id, port) # copy values over - but not fixed_ips port['port'].pop('fixed_ips', None) ret_port.update(port['port']) if (delete_addr_pairs or has_addr_pairs): # delete address pairs and readd them self._delete_allowed_address_pairs(context, id) self._process_create_allowed_address_pairs( context, ret_port, ret_port[addr_apidef.ADDRESS_PAIRS]) return ret_port class AllowedAddressPairDBTestCase(AllowedAddressPairTestCase): def setUp(self, plugin=None, ext_mgr=None): plugin = plugin or DB_PLUGIN_KLASS super(AllowedAddressPairDBTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr) class TestAllowedAddressPairs(AllowedAddressPairDBTestCase): def test_create_port_allowed_address_pairs_bad_format(self): with self.network() as net: bad_values = [False, True, 1.1, 1, ['ip_address'], ['mac_address']] for value in bad_values: self._create_port( self.fmt, net['network']['id'], expected_res_status=web_exc.HTTPBadRequest.code, arg_list=(addr_apidef.ADDRESS_PAIRS,), allowed_address_pairs=value) def test_create_port_allowed_address_pairs(self): with self.network() as net: address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'}] res = self._create_port(self.fmt, net['network']['id'], arg_list=(addr_apidef.ADDRESS_PAIRS,), allowed_address_pairs=address_pairs) port = self.deserialize(self.fmt, res) self.assertEqual(port['port'][addr_apidef.ADDRESS_PAIRS], address_pairs) self._delete('ports', port['port']['id']) def test_create_port_security_true_allowed_address_pairs(self): if self._skip_port_security: self.skipTest("Plugin does not implement port-security extension") with self.network() as net: address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'}] res = self._create_port(self.fmt, net['network']['id'], arg_list=('port_security_enabled', addr_apidef.ADDRESS_PAIRS,), port_security_enabled=True, allowed_address_pairs=address_pairs) port = self.deserialize(self.fmt, res) self.assertTrue(port['port'][psec.PORTSECURITY]) self.assertEqual(port['port'][addr_apidef.ADDRESS_PAIRS], address_pairs) self._delete('ports', port['port']['id']) def test_create_port_security_false_allowed_address_pairs(self): if self._skip_port_security: self.skipTest("Plugin does not implement port-security extension") with self.network() as net: address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'}] res = self._create_port(self.fmt, net['network']['id'], arg_list=('port_security_enabled', addr_apidef.ADDRESS_PAIRS,), port_security_enabled=False, allowed_address_pairs=address_pairs) self.deserialize(self.fmt, res) self.assertEqual(409, res.status_int) address_pairs = [] res = self._create_port(self.fmt, net['network']['id'], arg_list=('port_security_enabled', addr_apidef.ADDRESS_PAIRS,), port_security_enabled=False, allowed_address_pairs=address_pairs) port = self.deserialize(self.fmt, res) self.assertFalse(port['port'][psec.PORTSECURITY]) self.assertEqual(port['port'][addr_apidef.ADDRESS_PAIRS], address_pairs) self._delete('ports', port['port']['id']) def test_create_port_bad_mac(self): address_pairs = [{'mac_address': 'invalid_mac', 'ip_address': '10.0.0.1'}] self._create_port_with_address_pairs(address_pairs, 400) def test_create_port_bad_ip(self): address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1222'}] self._create_port_with_address_pairs(address_pairs, 400) def test_create_missing_mac_field(self): address_pairs = [{'ip_address': '10.0.0.1'}] self._create_port_with_address_pairs(address_pairs, 201) def test_create_missing_ip_field(self): address_pairs = [{'mac_address': '00:00:00:00:00:01'}] self._create_port_with_address_pairs(address_pairs, 400) def test_create_duplicate_mac_ip(self): address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'}, {'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'}] self._create_port_with_address_pairs(address_pairs, 400) def test_more_than_max_allowed_address_pair(self): cfg.CONF.set_default('max_allowed_address_pair', 3) address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'}, {'mac_address': '00:00:00:00:00:02', 'ip_address': '10.0.0.2'}, {'mac_address': '00:00:00:00:00:03', 'ip_address': '10.0.0.3'}, {'mac_address': '00:00:00:00:00:04', 'ip_address': '10.0.0.4'}] self._create_port_with_address_pairs(address_pairs, 400) def test_equal_to_max_allowed_address_pair(self): cfg.CONF.set_default('max_allowed_address_pair', 3) address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'}, {'mac_address': '00:00:00:00:00:02', 'ip_address': '10.0.0.2'}, {'mac_address': '00:00:00:00:00:03', 'ip_address': '10.0.0.3'}] self._create_port_with_address_pairs(address_pairs, 201) def test_create_overlap_with_fixed_ip(self): address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.2'}] with self.network() as network: with self.subnet(network=network, cidr='10.0.0.0/24') as subnet: fixed_ips = [{'subnet_id': subnet['subnet']['id'], 'ip_address': '10.0.0.2'}] res = self._create_port(self.fmt, network['network']['id'], arg_list=(addr_apidef.ADDRESS_PAIRS, 'fixed_ips'), allowed_address_pairs=address_pairs, fixed_ips=fixed_ips) self.assertEqual(res.status_int, 201) port = self.deserialize(self.fmt, res) self._delete('ports', port['port']['id']) def test_create_port_extra_args(self): address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1', 'icbb': 'agreed'}] self._create_port_with_address_pairs(address_pairs, 400) def test_create_port_with_unexpected_address_pairs_format(self): address_pairs = {'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'} self._create_port_with_address_pairs(address_pairs, 400) def _create_port_with_address_pairs(self, address_pairs, ret_code): with self.network() as net: res = self._create_port(self.fmt, net['network']['id'], arg_list=(addr_apidef.ADDRESS_PAIRS,), allowed_address_pairs=address_pairs) port = self.deserialize(self.fmt, res) self.assertEqual(res.status_int, ret_code) if ret_code == 201: self._delete('ports', port['port']['id']) def test_update_port_allowed_address_pairs_bad_format(self): with self.network() as net: res = self._create_port(self.fmt, net['network']['id']) port = self.deserialize(self.fmt, res) bad_values = [False, True, 1.1, 1, ['ip_address'], ['mac_address']] for value in bad_values: update_port = {'port': {addr_apidef.ADDRESS_PAIRS: value}} req = self.new_update_request('ports', update_port, port['port']['id']) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_update_add_address_pairs(self): with self.network() as net: res = self._create_port(self.fmt, net['network']['id']) port = self.deserialize(self.fmt, res) address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'}] update_port = {'port': {addr_apidef.ADDRESS_PAIRS: address_pairs}} req = self.new_update_request('ports', update_port, port['port']['id']) port = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(port['port'][addr_apidef.ADDRESS_PAIRS], address_pairs) self._delete('ports', port['port']['id']) def test_update_add_address_pairs_with_unexpected_format(self): with self.network() as net: res = self._create_port(self.fmt, net['network']['id']) port = self.deserialize(self.fmt, res) address_pairs = {'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'} update_port = {'port': {addr_apidef.ADDRESS_PAIRS: address_pairs}} req = self.new_update_request('ports', update_port, port['port']['id']) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_create_address_gets_port_mac(self): with self.network() as net: address_pairs = [{'ip_address': '23.23.23.23'}] res = self._create_port(self.fmt, net['network']['id'], arg_list=('port_security_enabled', addr_apidef.ADDRESS_PAIRS,), allowed_address_pairs=address_pairs) port = self.deserialize(self.fmt, res)['port'] port_addr_mac = port[addr_apidef.ADDRESS_PAIRS][0]['mac_address'] self.assertEqual(port_addr_mac, port['mac_address']) self._delete('ports', port['id']) def test_update_port_security_off_address_pairs(self): if self._skip_port_security: self.skipTest("Plugin does not implement port-security extension") with self.network() as net: with self.subnet(network=net) as subnet: address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'}] # The port should not have any security-groups associated to it with self.port(subnet=subnet, arg_list=(psec.PORTSECURITY, addr_apidef.ADDRESS_PAIRS, secgroup.SECURITYGROUPS), port_security_enabled=True, allowed_address_pairs=address_pairs, security_groups=[]) as port: update_port = {'port': {psec.PORTSECURITY: False}} req = self.new_update_request('ports', update_port, port['port']['id']) res = req.get_response(self.api) self.assertEqual(409, res.status_int) def test_update_with_none_and_own_mac_for_duplicate_ip(self): with self.network() as net: res = self._create_port(self.fmt, net['network']['id']) port = self.deserialize(self.fmt, res) mac_address = port['port']['mac_address'] address_pairs = [{'ip_address': '10.0.0.1'}, {'mac_address': mac_address, 'ip_address': '10.0.0.1'}] update_port = {'port': {addr_apidef.ADDRESS_PAIRS: address_pairs}} req = self.new_update_request('ports', update_port, port['port']['id']) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_create_port_remove_allowed_address_pairs_with_list(self): self._test_create_port_remove_allowed_address_pairs([]) def test_create_port_remove_allowed_address_pairs_with_none(self): self._test_create_port_remove_allowed_address_pairs(None) def _test_create_port_remove_allowed_address_pairs(self, update_value): with self.network() as net: address_pairs = [{'mac_address': '00:00:00:00:00:01', 'ip_address': '10.0.0.1'}] res = self._create_port(self.fmt, net['network']['id'], arg_list=(addr_apidef.ADDRESS_PAIRS,), allowed_address_pairs=address_pairs) port = self.deserialize(self.fmt, res) update_port = {'port': {addr_apidef.ADDRESS_PAIRS: update_value}} req = self.new_update_request('ports', update_port, port['port']['id']) port = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual([], port['port'][addr_apidef.ADDRESS_PAIRS]) self._delete('ports', port['port']['id']) neutron-12.1.1/neutron/tests/unit/db/test_rbac_db_mixin.py0000664000175000017500000002553513553660047023724 0ustar zuulzuul00000000000000# Copyright (c) 2016 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import events from neutron_lib import constants from neutron_lib import context from neutron.db.db_base_plugin_v2 import NeutronDbPluginV2 as db_plugin_v2 from neutron.extensions import rbac as ext_rbac from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin class NetworkRbacTestcase(test_plugin.NeutronDbPluginV2TestCase): def setUp(self): self.context = context.get_admin_context() super(NetworkRbacTestcase, self).setUp(plugin='ml2') def _make_networkrbac(self, network, target, action='access_as_shared'): policy = {'rbac_policy': {'tenant_id': network['network']['tenant_id'], 'object_id': network['network']['id'], 'object_type': 'network', 'action': action, 'target_tenant': target}} return policy def _setup_networkrbac_and_port(self, network, target_tenant): policy = self._make_networkrbac(network, target_tenant) netrbac = self.plugin.create_rbac_policy(self.context, policy) test_port = {'port': {'name': 'test-port', 'network_id': network['network']['id'], 'mac_address': constants.ATTR_NOT_SPECIFIED, 'fixed_ips': constants.ATTR_NOT_SPECIFIED, 'admin_state_up': True, 'device_id': 'device_id', 'device_owner': 'device_owner', 'tenant_id': target_tenant}} port = self.plugin.create_port(self.context, test_port) return netrbac, port def _assert_external_net_state(self, net_id, is_external): req = self.new_show_request('networks', net_id) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(is_external, res['network']['router:external']) def test_create_network_rbac_external(self): with self.network() as ext_net: net_id = ext_net['network']['id'] self._assert_external_net_state(net_id, is_external=False) policy = self._make_networkrbac(ext_net, '*', 'access_as_external') self.plugin.create_rbac_policy(self.context, policy) self._assert_external_net_state(net_id, is_external=True) def test_update_network_rbac_external_valid(self): orig_target = 'test-tenant-2' new_target = 'test-tenant-3' with self.network() as ext_net: policy = self._make_networkrbac(ext_net, orig_target, 'access_as_external') netrbac = self.plugin.create_rbac_policy(self.context, policy) update_policy = {'rbac_policy': {'target_tenant': new_target}} netrbac2 = self.plugin.update_rbac_policy(self.context, netrbac['id'], update_policy) policy['rbac_policy']['target_tenant'] = new_target for k, v in policy['rbac_policy'].items(): self.assertEqual(netrbac2[k], v) def test_delete_network_rbac_external(self): with self.network() as ext_net: net_id = ext_net['network']['id'] self._assert_external_net_state(net_id, is_external=False) policy = self._make_networkrbac(ext_net, '*', 'access_as_external') net_rbac = self.plugin.create_rbac_policy(self.context, policy) self._assert_external_net_state(net_id, is_external=True) self.plugin.delete_rbac_policy(self.context, net_rbac['id']) self._assert_external_net_state(net_id, is_external=False) def test_delete_network_rbac_external_with_multi_rbac_policy(self): with self.network() as ext_net: net_id = ext_net['network']['id'] self._assert_external_net_state(net_id, is_external=False) policy1 = self._make_networkrbac(ext_net, 'test-tenant-1', 'access_as_external') net_rbac1 = self.plugin.create_rbac_policy(self.context, policy1) self._assert_external_net_state(net_id, is_external=True) policy2 = self._make_networkrbac(ext_net, 'test-tenant-2', 'access_as_external') self.plugin.create_rbac_policy(self.context, policy2) self._assert_external_net_state(net_id, is_external=True) self.plugin.delete_rbac_policy(self.context, net_rbac1['id']) self._assert_external_net_state(net_id, is_external=True) def test_delete_external_network_shared_rbac(self): with self.network() as ext_net: net_id = ext_net['network']['id'] self.plugin.update_network( self.context, net_id, {'network': {'router:external': True}}) self._assert_external_net_state(net_id, is_external=True) policy = self._make_networkrbac(ext_net, 'test-tenant-2') net_rbac = self.plugin.create_rbac_policy(self.context, policy) self.plugin.delete_rbac_policy(self.context, net_rbac['id']) # Make sure that external attribute not changed. self._assert_external_net_state(net_id, is_external=True) def test_update_networkrbac_valid(self): orig_target = 'test-tenant-2' new_target = 'test-tenant-3' with self.network() as net: policy = self._make_networkrbac(net, orig_target) netrbac = self.plugin.create_rbac_policy(self.context, policy) update_policy = {'rbac_policy': {'target_tenant': new_target}} netrbac2 = self.plugin.update_rbac_policy(self.context, netrbac['id'], update_policy) policy['rbac_policy']['target_tenant'] = new_target for k, v in policy['rbac_policy'].items(): self.assertEqual(netrbac2[k], v) def test_delete_networkrbac_in_use_fail(self): with self.network() as net: netrbac, _ = self._setup_networkrbac_and_port( network=net, target_tenant='test-tenant-2') self.assertRaises(ext_rbac.RbacPolicyInUse, self.plugin.delete_rbac_policy, self.context, netrbac['id']) def test_port_presence_prevents_network_rbac_policy_deletion(self): with self.network() as net: netrbac, port = self._setup_networkrbac_and_port( network=net, target_tenant='alice') self.assertRaises(ext_rbac.RbacPolicyInUse, self.plugin.delete_rbac_policy, self.context, netrbac['id']) # a wildcard policy should allow the specific policy to be deleted # since it allows the remaining port wild_policy = self._make_networkrbac(net, '*') wild_policy = self.plugin.create_rbac_policy(self.context, wild_policy) self.plugin.delete_rbac_policy(self.context, netrbac['id']) # now that wildcard is the only remaining, it should be subjected # to to the same restriction self.assertRaises(ext_rbac.RbacPolicyInUse, self.plugin.delete_rbac_policy, self.context, wild_policy['id']) # similarly, we can't update the policy to a different tenant update_policy = {'rbac_policy': {'target_tenant': 'bob'}} self.assertRaises(ext_rbac.RbacPolicyInUse, self.plugin.update_rbac_policy, self.context, wild_policy['id'], update_policy) # after port anchor is gone, update and delete should pass self.plugin.delete_port(self.context, port['id']) self.plugin.update_rbac_policy( self.context, wild_policy['id'], update_policy) self.plugin.delete_rbac_policy(self.context, wild_policy['id']) # check that policy is indeed gone self.assertRaises(ext_rbac.RbacPolicyNotFound, self.plugin.get_rbac_policy, self.context, wild_policy['id']) def test_delete_networkrbac_self_share(self): net_id = 'my-network' net_owner = 'my-tenant-id' net = {'network': {'id': net_id, 'tenant_id': net_owner}} policy = self._make_networkrbac(net, net_owner)['rbac_policy'] kwargs = {} with mock.patch.object(db_plugin_v2, '_get_network') as get_net,\ mock.patch.object(db_plugin_v2, 'ensure_no_tenant_ports_on_network') as ensure: get_net.return_value = net['network'] self.plugin.validate_network_rbac_policy_change( None, events.BEFORE_DELETE, None, self.context, 'network', policy, **kwargs) self.assertEqual(0, ensure.call_count) def test_update_self_share_networkrbac(self): net_id = 'my-network' net_owner = 'my-tenant-id' net = {'network': {'id': net_id, 'tenant_id': net_owner}} policy = self._make_networkrbac(net, net_owner)['rbac_policy'] kwargs = {'policy_update': {'target_tenant': 'new-target-tenant'}} with mock.patch.object(db_plugin_v2, '_get_network') as get_net,\ mock.patch.object(db_plugin_v2, 'ensure_no_tenant_ports_on_network') as ensure: get_net.return_value = net['network'] self.plugin.validate_network_rbac_policy_change( None, events.BEFORE_UPDATE, None, self.context, 'network', policy, **kwargs) self.assertEqual(0, ensure.call_count) neutron-12.1.1/neutron/tests/unit/db/test_ipam_backend_mixin.py0000664000175000017500000003661113553660047024742 0ustar zuulzuul00000000000000# Copyright (c) 2015 Infoblox Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import netaddr from neutron_lib.api.definitions import portbindings from neutron_lib import constants from oslo_utils import uuidutils import webob.exc from neutron.db import db_base_plugin_v2 from neutron.db import ipam_backend_mixin from neutron.db import portbindings_db from neutron.objects import subnet as subnet_obj from neutron.tests import base from neutron.tests.unit.db import test_db_base_plugin_v2 class TestIpamBackendMixin(base.BaseTestCase): def setUp(self): super(TestIpamBackendMixin, self).setUp() self.mixin = ipam_backend_mixin.IpamBackendMixin() self.ctx = mock.Mock() self.default_new_ips = (('id-1', '192.168.1.1'), ('id-2', '192.168.1.2')) self.default_original_ips = (('id-1', '192.168.1.1'), ('id-5', '172.20.16.5')) self.owner_non_router = constants.DEVICE_OWNER_DHCP self.owner_router = constants.DEVICE_OWNER_ROUTER_INTF def _prepare_ips(self, ips): results = [] for ip in ips: ip_dict = {'ip_address': ip[1], 'subnet_id': ip[0]} if len(ip) > 2: ip_dict['delete_subnet'] = ip[2] results.append(ip_dict) return results def _mock_slaac_subnet_on(self): slaac_subnet_obj = subnet_obj.Subnet( self.ctx, ipv6_address_mode=constants.IPV6_SLAAC, ipv6_ra_mode=constants.IPV6_SLAAC) self.mixin._get_subnet_object = mock.Mock( return_value=slaac_subnet_obj) def _mock_slaac_subnet_off(self): non_slaac_subnet_obj = subnet_obj.Subnet( self.ctx, ipv6_address_mode=None, ipv6_ra_mode=None) self.mixin._get_subnet_object = mock.Mock( return_value=non_slaac_subnet_obj) def _mock_slaac_for_subnet_ids(self, subnet_ids): """Mock incoming subnets as autoaddressed.""" def _get_subnet_object(context, subnet_id): if subnet_id in subnet_ids: return subnet_obj.Subnet( self.ctx, ipv6_address_mode=constants.IPV6_SLAAC, ipv6_ra_mode=constants.IPV6_SLAAC) else: return subnet_obj.Subnet( self.ctx, ipv6_address_mode=None, ipv6_ra_mode=None) self.mixin._get_subnet_object = mock.Mock( side_effect=_get_subnet_object) def _test_get_changed_ips_for_port(self, expected, original_ips, new_ips, owner): change = self.mixin._get_changed_ips_for_port(self.ctx, original_ips, new_ips, owner) self.assertItemsEqual(expected.add, change.add) self.assertItemsEqual(expected.original, change.original) self.assertItemsEqual(expected.remove, change.remove) def test__get_changed_ips_for_port(self): new_ips = self._prepare_ips(self.default_new_ips) original_ips = self._prepare_ips(self.default_original_ips) expected_change = self.mixin.Changes(add=[new_ips[1]], original=[original_ips[0]], remove=[original_ips[1]]) self._test_get_changed_ips_for_port(expected_change, original_ips, new_ips, self.owner_router) def test__get_changed_ips_for_port_autoaddress(self): new_ips = self._prepare_ips(self.default_new_ips) original = (('id-1', '192.168.1.1'), ('id-5', '2000:1234:5678::12FF:FE34:5678')) original_ips = self._prepare_ips(original) self._mock_slaac_subnet_on() expected_change = self.mixin.Changes(add=[new_ips[1]], original=original_ips, remove=[]) self._test_get_changed_ips_for_port(expected_change, original_ips, new_ips, self.owner_non_router) def test__get_changed_ips_for_port_remove_autoaddress(self): new = (('id-5', '2000:1234:5678::12FF:FE34:5678', True), ('id-1', '192.168.1.1')) new_ips = self._prepare_ips(new) reference_ips = [ip for ip in new_ips if ip['subnet_id'] == 'id-1'] original = (('id-5', '2000:1234:5678::12FF:FE34:5678'),) original_ips = self._prepare_ips(original) # mock ipv6 subnet as auto addressed and leave ipv4 as regular self._mock_slaac_for_subnet_ids([new[0][0]]) # Autoaddressed ip allocation has to be removed # if it has 'delete_subnet' flag set to True expected_change = self.mixin.Changes(add=reference_ips, original=[], remove=original_ips) self._test_get_changed_ips_for_port(expected_change, original_ips, new_ips, self.owner_non_router) def test__get_changed_ips_for_port_autoaddress_ipv6_pd_enabled(self): owner_not_router = constants.DEVICE_OWNER_DHCP new_ips = self._prepare_ips(self.default_new_ips) original = (('id-1', '192.168.1.1'), ('id-5', '2000:1234:5678::12FF:FE34:5678')) original_ips = self._prepare_ips(original) # mock to test auto address part pd_subnet_obj = subnet_obj.Subnet( self.ctx, id=uuidutils.generate_uuid(), subnetpool_id=constants.IPV6_PD_POOL_ID, ipv6_address_mode=constants.IPV6_SLAAC, ipv6_ra_mode=constants.IPV6_SLAAC) self.mixin._get_subnet_object = mock.Mock(return_value=pd_subnet_obj) # make a copy of original_ips # since it is changed by _get_changed_ips_for_port expected_change = self.mixin.Changes(add=[new_ips[1]], original=[original_ips[0]], remove=[original_ips[1]]) self._test_get_changed_ips_for_port(expected_change, original_ips, new_ips, owner_not_router) def _test_get_changed_ips_for_port_no_ip_address(self): # IP address should be added if only subnet_id is provided, # independently from auto_address status for subnet new_ips = [{'subnet_id': 'id-3'}] original_ips = [] expected_change = self.mixin.Changes(add=[new_ips[0]], original=[], remove=[]) self._test_get_changed_ips_for_port(expected_change, original_ips, new_ips, self.owner_non_router) def test__get_changed_ips_for_port_no_ip_address_no_slaac(self): self._mock_slaac_subnet_off() self._test_get_changed_ips_for_port_no_ip_address() def test__get_changed_ips_for_port_no_ip_address_slaac(self): self._mock_slaac_subnet_on() self._test_get_changed_ips_for_port_no_ip_address() def test__get_changed_ips_for_port_subnet_id_no_ip(self): # If a subnet is specified without an IP address only allocate a new # address if one doesn't exist self._mock_slaac_subnet_off() new_ips = [{'subnet_id': 'id-3'}] original_ips = [{'subnet_id': 'id-3', 'ip_address': '4.3.2.1'}] expected_change = self.mixin.Changes( add=[], original=[{'subnet_id': 'id-3', 'ip_address': '4.3.2.1'}], remove=[]) self._test_get_changed_ips_for_port(expected_change, original_ips, new_ips, self.owner_non_router) def test__get_changed_ips_for_port_multiple_ips_one_subnet_add_third(self): # If a subnet is specified without an IP address only allocate a new # address if one doesn't exist self._mock_slaac_subnet_off() new_ips = [{'subnet_id': 'id-3', 'ip_address': '4.3.2.1'}, {'subnet_id': 'id-3'}, {'subnet_id': 'id-3', 'ip_address': '4.3.2.10'}] original_ips = [{'subnet_id': 'id-3', 'ip_address': '4.3.2.1'}, {'subnet_id': 'id-3', 'ip_address': '4.3.2.10'}] expected_change = self.mixin.Changes( add=[{'subnet_id': 'id-3'}], original=[{'subnet_id': 'id-3', 'ip_address': '4.3.2.1'}, {'subnet_id': 'id-3', 'ip_address': '4.3.2.10'}], remove=[]) self._test_get_changed_ips_for_port(expected_change, original_ips, new_ips, self.owner_non_router) def test__get_changed_ips_for_port_multiple_ips_one_subnet_noip(self): # If a subnet is specified without an IP address only allocate a new # address if one doesn't exist self._mock_slaac_subnet_off() new_ips = [{'subnet_id': 'id-3'}, {'subnet_id': 'id-3'}] original_ips = [{'subnet_id': 'id-3', 'ip_address': '4.3.2.1'}, {'subnet_id': 'id-3', 'ip_address': '4.3.2.10'}] expected_change = self.mixin.Changes( add=[], original=[{'subnet_id': 'id-3', 'ip_address': '4.3.2.1'}, {'subnet_id': 'id-3', 'ip_address': '4.3.2.10'}], remove=[]) self._test_get_changed_ips_for_port(expected_change, original_ips, new_ips, self.owner_non_router) def test__get_changed_ips_for_port_subnet_id_no_ip_ipv6(self): # If a subnet is specified without an IP address only allocate a new # address if one doesn't exist self._mock_slaac_subnet_off() new_ips = [{'subnet_id': 'id-3'}] original_ips = [{'subnet_id': 'id-3', 'ip_address': '2001:db8::8'}] expected_change = self.mixin.Changes( add=[], original=[{'subnet_id': 'id-3', 'ip_address': '2001:db8::8'}], remove=[]) self._test_get_changed_ips_for_port(expected_change, original_ips, new_ips, self.owner_non_router) def test__get_changed_ips_for_port_subnet_id_no_ip_eui64(self): # If a subnet is specified without an IP address allocate a new address # if the address is eui-64. This supports changing prefix when prefix # delegation is in use. self._mock_slaac_subnet_off() new_ips = [{'subnet_id': 'id-3'}] original_ips = [{'subnet_id': 'id-3', 'ip_address': '2001::eeb1:d7ff:fe2c:9c5f'}] expected_change = self.mixin.Changes( add=[{'subnet_id': 'id-3'}], original=[], remove=[{'subnet_id': 'id-3', 'ip_address': '2001::eeb1:d7ff:fe2c:9c5f'}]) self._test_get_changed_ips_for_port(expected_change, original_ips, new_ips, self.owner_non_router) def test__is_ip_required_by_subnet_for_router_port(self): # Owner -> router: # _get_subnet_object should not be called, # expected True self._mock_slaac_subnet_off() result = self.mixin._is_ip_required_by_subnet(self.ctx, 'id', self.owner_router) self.assertTrue(result) self.assertFalse(self.mixin._get_subnet_object.called) def test__is_ip_required_by_subnet_for_non_router_port(self): # Owner -> not router: # _get_subnet_object should be called, # expected True, because subnet is not slaac self._mock_slaac_subnet_off() result = self.mixin._is_ip_required_by_subnet(self.ctx, 'id', self.owner_non_router) self.assertTrue(result) self.assertTrue(self.mixin._get_subnet_object.called) def test__is_ip_required_by_subnet_for_non_router_port_and_slaac(self): # Owner -> not router: # _get_subnet_object should be called, # expected False, because subnet is slaac self._mock_slaac_subnet_on() result = self.mixin._is_ip_required_by_subnet(self.ctx, 'id', self.owner_non_router) self.assertFalse(result) self.assertTrue(self.mixin._get_subnet_object.called) class TestPlugin(db_base_plugin_v2.NeutronDbPluginV2, portbindings_db.PortBindingMixin): __native_pagination_support = True __native_sorting_support = True supported_extension_aliases = ["binding"] def get_plugin_description(self): return "Test Plugin" @classmethod def get_plugin_type(cls): return "test_plugin" def create_port(self, context, port): port_dict = super(TestPlugin, self).create_port(context, port) self._process_portbindings_create_and_update( context, port['port'], port_dict) return port_dict class TestPortUpdateIpam(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def setUp(self, plugin=None): if not plugin: plugin = 'neutron.tests.unit.db.test_ipam_backend_mixin.TestPlugin' super(TestPortUpdateIpam, self).setUp(plugin=plugin) def test_port_update_allocate_from_net_subnet(self): """Tests that a port can get address by updating fixed_ips""" with self.network() as network: pass # Create a bound port with no IP address (since there is not subnet) response = self._create_port(self.fmt, net_id=network['network']['id'], tenant_id=network['network']['tenant_id'], arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) port = self.deserialize(self.fmt, response) # Create the subnet and try to update the port to get an IP with self.subnet(network=network) as subnet: data = {'port': { 'fixed_ips': [{'subnet_id': subnet['subnet']['id']}]}} port_id = port['port']['id'] port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) res = self.deserialize(self.fmt, response) self.assertEqual(webob.exc.HTTPOk.code, response.status_int) self.assertEqual(1, len(res['port']['fixed_ips'])) ip = res['port']['fixed_ips'][0]['ip_address'] ip_net = netaddr.IPNetwork(subnet['subnet']['cidr']) self.assertIn(ip, ip_net) class TestPortUpdateIpamML2(TestPortUpdateIpam): def setUp(self): super(TestPortUpdateIpamML2, self).setUp(plugin='ml2') neutron-12.1.1/neutron/tests/unit/db/quota/0000775000175000017500000000000013553660157020654 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/db/quota/test_api.py0000664000175000017500000003626413553660046023046 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import mock from neutron_lib import context from neutron_lib.plugins import constants as const from neutron_lib.plugins import directory from oslo_config import cfg from neutron.db.quota import api as quota_api from neutron.tests.unit.db.quota import test_driver from neutron.tests.unit import testlib_api DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' class TestQuotaDbApi(testlib_api.SqlTestCaseLight): def _set_context(self): self.tenant_id = 'Higuain' self.context = context.Context('Gonzalo', self.tenant_id, is_admin=False, is_advsvc=False) def _create_reservation(self, resource_deltas, tenant_id=None, expiration=None): tenant_id = tenant_id or self.tenant_id return quota_api.create_reservation( self.context, tenant_id, resource_deltas, expiration) def _create_quota_usage(self, resource, used, tenant_id=None): tenant_id = tenant_id or self.tenant_id return quota_api.set_quota_usage(context.get_admin_context(), resource, tenant_id, in_use=used) def _verify_quota_usage(self, usage_info, expected_resource=None, expected_used=None, expected_dirty=None): self.assertEqual(self.tenant_id, usage_info.tenant_id) if expected_resource: self.assertEqual(expected_resource, usage_info.resource) if expected_dirty is not None: self.assertEqual(expected_dirty, usage_info.dirty) if expected_used is not None: self.assertEqual(expected_used, usage_info.used) def setUp(self): super(TestQuotaDbApi, self).setUp() self._set_context() self.plugin = test_driver.FakePlugin() directory.add_plugin(const.CORE, self.plugin) cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) def test_create_quota_usage(self): usage_info = self._create_quota_usage('goals', 26) self._verify_quota_usage(usage_info, expected_resource='goals', expected_used=26) def test_update_quota_usage(self): self._create_quota_usage('goals', 26) # Higuain scores a double usage_info_1 = quota_api.set_quota_usage( self.context, 'goals', self.tenant_id, in_use=28) self._verify_quota_usage(usage_info_1, expected_used=28) usage_info_2 = quota_api.set_quota_usage( self.context, 'goals', self.tenant_id, in_use=24) self._verify_quota_usage(usage_info_2, expected_used=24) def test_update_quota_usage_with_deltas(self): self._create_quota_usage('goals', 26) # Higuain scores a double usage_info_1 = quota_api.set_quota_usage( self.context, 'goals', self.tenant_id, in_use=2, delta=True) self._verify_quota_usage(usage_info_1, expected_used=28) def test_set_quota_usage_dirty(self): self._create_quota_usage('goals', 26) # Higuain needs a shower after the match self.assertEqual(1, quota_api.set_quota_usage_dirty( self.context, 'goals', self.tenant_id)) usage_info = quota_api.get_quota_usage_by_resource_and_tenant( self.context, 'goals', self.tenant_id) self._verify_quota_usage(usage_info, expected_dirty=True) # Higuain is clean now self.assertEqual(1, quota_api.set_quota_usage_dirty( self.context, 'goals', self.tenant_id, dirty=False)) usage_info = quota_api.get_quota_usage_by_resource_and_tenant( self.context, 'goals', self.tenant_id) self._verify_quota_usage(usage_info, expected_dirty=False) def test_set_dirty_non_existing_quota_usage(self): self.assertEqual(0, quota_api.set_quota_usage_dirty( self.context, 'meh', self.tenant_id)) def test_set_resources_quota_usage_dirty(self): self._create_quota_usage('goals', 26) self._create_quota_usage('assists', 11) self._create_quota_usage('bookings', 3) self.assertEqual(2, quota_api.set_resources_quota_usage_dirty( self.context, ['goals', 'bookings'], self.tenant_id)) usage_info_goals = quota_api.get_quota_usage_by_resource_and_tenant( self.context, 'goals', self.tenant_id) usage_info_assists = quota_api.get_quota_usage_by_resource_and_tenant( self.context, 'assists', self.tenant_id) usage_info_bookings = quota_api.get_quota_usage_by_resource_and_tenant( self.context, 'bookings', self.tenant_id) self._verify_quota_usage(usage_info_goals, expected_dirty=True) self._verify_quota_usage(usage_info_assists, expected_dirty=False) self._verify_quota_usage(usage_info_bookings, expected_dirty=True) def test_set_resources_quota_usage_dirty_with_empty_list(self): self._create_quota_usage('goals', 26) self._create_quota_usage('assists', 11) self._create_quota_usage('bookings', 3) # Expect all the resources for the tenant to be set dirty self.assertEqual(3, quota_api.set_resources_quota_usage_dirty( self.context, [], self.tenant_id)) usage_info_goals = quota_api.get_quota_usage_by_resource_and_tenant( self.context, 'goals', self.tenant_id) usage_info_assists = quota_api.get_quota_usage_by_resource_and_tenant( self.context, 'assists', self.tenant_id) usage_info_bookings = quota_api.get_quota_usage_by_resource_and_tenant( self.context, 'bookings', self.tenant_id) self._verify_quota_usage(usage_info_goals, expected_dirty=True) self._verify_quota_usage(usage_info_assists, expected_dirty=True) self._verify_quota_usage(usage_info_bookings, expected_dirty=True) # Higuain is clean now self.assertEqual(1, quota_api.set_quota_usage_dirty( self.context, 'goals', self.tenant_id, dirty=False)) usage_info = quota_api.get_quota_usage_by_resource_and_tenant( self.context, 'goals', self.tenant_id) self._verify_quota_usage(usage_info, expected_dirty=False) def _test_set_all_quota_usage_dirty(self, expected): self._create_quota_usage('goals', 26) self._create_quota_usage('goals', 12, tenant_id='Callejon') self.assertEqual(expected, quota_api.set_all_quota_usage_dirty( self.context, 'goals')) def test_set_all_quota_usage_dirty(self): # All goal scorers need a shower after the match, but since this is not # admin context we can clean only one self._test_set_all_quota_usage_dirty(expected=1) def test_get_quota_usage_by_tenant(self): self._create_quota_usage('goals', 26) self._create_quota_usage('assists', 11) # Create a resource for a different tenant self._create_quota_usage('mehs', 99, tenant_id='buffon') usage_infos = quota_api.get_quota_usage_by_tenant_id( self.context, self.tenant_id) self.assertEqual(2, len(usage_infos)) resources = [info.resource for info in usage_infos] self.assertIn('goals', resources) self.assertIn('assists', resources) def test_get_quota_usage_by_resource(self): self._create_quota_usage('goals', 26) self._create_quota_usage('assists', 11) self._create_quota_usage('goals', 12, tenant_id='Callejon') usage_infos = quota_api.get_quota_usage_by_resource( self.context, 'goals') # Only 1 result expected in tenant context self.assertEqual(1, len(usage_infos)) self._verify_quota_usage(usage_infos[0], expected_resource='goals', expected_used=26) def test_get_quota_usage_by_tenant_and_resource(self): self._create_quota_usage('goals', 26) usage_info = quota_api.get_quota_usage_by_resource_and_tenant( self.context, 'goals', self.tenant_id) self._verify_quota_usage(usage_info, expected_resource='goals', expected_used=26) def test_get_non_existing_quota_usage_returns_none(self): self.assertIsNone(quota_api.get_quota_usage_by_resource_and_tenant( self.context, 'goals', self.tenant_id)) def _verify_reserved_resources(self, expected, actual): for (resource, delta) in actual.items(): self.assertIn(resource, expected) self.assertEqual(delta, expected[resource]) del expected[resource] self.assertFalse(expected) def test_create_reservation(self): resources = {'goals': 2, 'assists': 1} resv = self._create_reservation(resources) self.assertEqual(self.tenant_id, resv.tenant_id) self._verify_reserved_resources(resources, resv.deltas) def test_create_reservation_with_expiration(self): resources = {'goals': 2, 'assists': 1} exp_date = datetime.datetime(2016, 3, 31, 14, 30) resv = self._create_reservation(resources, expiration=exp_date) self.assertEqual(self.tenant_id, resv.tenant_id) self.assertEqual(exp_date, resv.expiration) self._verify_reserved_resources(resources, resv.deltas) def test_remove_non_existent_reservation(self): self.assertIsNone(quota_api.remove_reservation(self.context, 'meh')) def _get_reservations_for_resource_helper(self): # create three reservation, 1 expired resources_1 = {'goals': 2, 'assists': 1} resources_2 = {'goals': 3, 'bookings': 1} resources_3 = {'bookings': 2, 'assists': 2} exp_date_1 = datetime.datetime(2016, 3, 31, 14, 30) exp_date_2 = datetime.datetime(2015, 3, 31, 14, 30) self._create_reservation(resources_1, expiration=exp_date_1) self._create_reservation(resources_2, expiration=exp_date_1) self._create_reservation(resources_3, expiration=exp_date_2) def test_get_reservations_for_resources(self): with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow: self._get_reservations_for_resource_helper() mock_utcnow.return_value = datetime.datetime( 2015, 5, 20, 0, 0) deltas = quota_api.get_reservations_for_resources( self.context, self.tenant_id, ['goals', 'assists', 'bookings']) self.assertIn('goals', deltas) self.assertEqual(5, deltas['goals']) self.assertIn('assists', deltas) self.assertEqual(1, deltas['assists']) self.assertIn('bookings', deltas) self.assertEqual(1, deltas['bookings']) self.assertEqual(3, len(deltas)) def test_get_expired_reservations_for_resources(self): with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow: mock_utcnow.return_value = datetime.datetime( 2015, 5, 20, 0, 0) self._get_reservations_for_resource_helper() deltas = quota_api.get_reservations_for_resources( self.context, self.tenant_id, ['goals', 'assists', 'bookings'], expired=True) self.assertIn('assists', deltas) self.assertEqual(2, deltas['assists']) self.assertIn('bookings', deltas) self.assertEqual(2, deltas['bookings']) self.assertEqual(2, len(deltas)) def test_get_reservation_for_resources_with_empty_list(self): self.assertIsNone(quota_api.get_reservations_for_resources( self.context, self.tenant_id, [])) def test_remove_expired_reservations(self): with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow: mock_utcnow.return_value = datetime.datetime( 2015, 5, 20, 0, 0) resources = {'goals': 2, 'assists': 1} exp_date_1 = datetime.datetime(2016, 3, 31, 14, 30) resv_1 = self._create_reservation(resources, expiration=exp_date_1) exp_date_2 = datetime.datetime(2015, 3, 31, 14, 30) resv_2 = self._create_reservation(resources, expiration=exp_date_2) self.assertEqual(1, quota_api.remove_expired_reservations( self.context, self.tenant_id)) self.assertIsNone(quota_api.get_reservation( self.context, resv_2.reservation_id)) self.assertIsNotNone(quota_api.get_reservation( self.context, resv_1.reservation_id)) def test_remove_expired_reservations_no_tenant(self): with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow: mock_utcnow.return_value = datetime.datetime( 2015, 5, 20, 0, 0) resources = {'goals': 2, 'assists': 1} exp_date_1 = datetime.datetime(2014, 3, 31, 14, 30) resv_1 = self._create_reservation(resources, expiration=exp_date_1) exp_date_2 = datetime.datetime(2015, 3, 31, 14, 30) resv_2 = self._create_reservation(resources, expiration=exp_date_2, tenant_id='Callejon') self.assertEqual(2, quota_api.remove_expired_reservations( context.get_admin_context())) self.assertIsNone(quota_api.get_reservation( self.context, resv_2.reservation_id)) self.assertIsNone(quota_api.get_reservation( self.context, resv_1.reservation_id)) class TestQuotaDbApiAdminContext(TestQuotaDbApi): def _set_context(self): self.tenant_id = 'Higuain' self.context = context.Context('Gonzalo', self.tenant_id, is_admin=True, is_advsvc=True) def test_get_quota_usage_by_resource(self): self._create_quota_usage('goals', 26) self._create_quota_usage('assists', 11) self._create_quota_usage('goals', 12, tenant_id='Callejon') usage_infos = quota_api.get_quota_usage_by_resource( self.context, 'goals') # 2 results expected in admin context self.assertEqual(2, len(usage_infos)) for usage_info in usage_infos: self.assertEqual('goals', usage_info.resource) def test_set_all_quota_usage_dirty(self): # All goal scorers need a shower after the match, and with admin # context we should be able to clean all of them self._test_set_all_quota_usage_dirty(expected=2) neutron-12.1.1/neutron/tests/unit/db/quota/test_driver.py0000664000175000017500000003134713553660047023566 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib import context from neutron_lib import exceptions as lib_exc from neutron.common import exceptions from neutron.db import db_base_plugin_v2 as base_plugin from neutron.db.quota import api as quota_api from neutron.db.quota import driver from neutron.objects import quota as quota_obj from neutron.quota import resource from neutron.tests import base from neutron.tests.unit import quota as test_quota from neutron.tests.unit import testlib_api DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' def _count_resource(context, resource, tenant_id): """A fake counting function to determine current used counts""" if resource[-1] == 's': resource = resource[:-1] result = quota_obj.QuotaUsage.get_object_dirty_protected( context, resource=resource) return 0 if not result else result.in_use class FakePlugin(base_plugin.NeutronDbPluginV2, driver.DbQuotaDriver): """A fake plugin class containing all DB methods.""" class TestResource(object): """Describe a test resource for quota checking.""" def __init__(self, name, default, fake_count=0): self.name = name self.quota = default self.fake_count = fake_count @property def default(self): return self.quota def count(self, *args, **kwargs): return self.fake_count class TestTrackedResource(resource.TrackedResource): """Describes a test tracked resource for detailed quota checking""" def __init__(self, name, model_class, flag=None, plural_name=None): super(TestTrackedResource, self).__init__( name, model_class, flag=flag, plural_name=None) @property def default(self): return self.flag class TestCountableResource(resource.CountableResource): """Describes a test countable resource for detailed quota checking""" def __init__(self, name, count, flag=-1, plural_name=None): super(TestCountableResource, self).__init__( name, count, flag=flag, plural_name=None) @property def default(self): return self.flag PROJECT = 'prj_test' RESOURCE = 'res_test' ALT_RESOURCE = 'res_test_meh' class TestDbQuotaDriver(testlib_api.SqlTestCase, base.BaseTestCase): def setUp(self): super(TestDbQuotaDriver, self).setUp() self.plugin = FakePlugin() self.context = context.get_admin_context() self.setup_coreplugin(core_plugin=DB_PLUGIN_KLASS) def test_create_quota_limit(self): defaults = {RESOURCE: TestResource(RESOURCE, 4)} self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) quotas = self.plugin.get_tenant_quotas(self.context, defaults, PROJECT) self.assertEqual(2, quotas[RESOURCE]) def test_update_quota_limit(self): defaults = {RESOURCE: TestResource(RESOURCE, 4)} self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 3) quotas = self.plugin.get_tenant_quotas(self.context, defaults, PROJECT) self.assertEqual(3, quotas[RESOURCE]) def test_delete_tenant_quota_restores_default_limit(self): defaults = {RESOURCE: TestResource(RESOURCE, 4)} self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) self.plugin.delete_tenant_quota(self.context, PROJECT) quotas = self.plugin.get_tenant_quotas(self.context, defaults, PROJECT) self.assertEqual(4, quotas[RESOURCE]) def test_get_default_quotas(self): defaults = {RESOURCE: TestResource(RESOURCE, 4)} user_ctx = context.Context(user_id=PROJECT, tenant_id=PROJECT) self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) quotas = self.plugin.get_default_quotas(user_ctx, defaults, PROJECT) self.assertEqual(4, quotas[RESOURCE]) def test_get_tenant_quotas(self): user_ctx = context.Context(user_id=PROJECT, tenant_id=PROJECT) self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) quotas = self.plugin.get_tenant_quotas(user_ctx, {}, PROJECT) self.assertEqual(2, quotas[RESOURCE]) def test_get_tenant_quotas_different_tenant(self): user_ctx = context.Context(user_id=PROJECT, tenant_id='another_project') self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) # It is appropriate to use assertFalse here as the expected return # value is an empty dict (the defaults passed in the statement below # after the request context) self.assertFalse(self.plugin.get_tenant_quotas(user_ctx, {}, PROJECT)) def test_get_all_quotas(self): project_1 = 'prj_test_1' project_2 = 'prj_test_2' resource_1 = 'res_test_1' resource_2 = 'res_test_2' resources = {resource_1: TestResource(resource_1, 3), resource_2: TestResource(resource_2, 5)} self.plugin.update_quota_limit(self.context, project_1, resource_1, 7) self.plugin.update_quota_limit(self.context, project_2, resource_2, 9) quotas = self.plugin.get_all_quotas(self.context, resources) # Expect two tenants' quotas self.assertEqual(2, len(quotas)) # But not quotas for the same tenant twice self.assertNotEqual(quotas[0]['tenant_id'], quotas[1]['tenant_id']) # Check the expected limits. The quotas can be in any order. for quota in quotas: project = quota['tenant_id'] self.assertIn(project, (project_1, project_2)) if project == project_1: expected_limit_r1 = 7 expected_limit_r2 = 5 if project == project_2: expected_limit_r1 = 3 expected_limit_r2 = 9 self.assertEqual(expected_limit_r1, quota[resource_1]) self.assertEqual(expected_limit_r2, quota[resource_2]) def test_limit_check(self): resources = {RESOURCE: TestResource(RESOURCE, 2)} values = {RESOURCE: 1} self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) self.plugin.limit_check(self.context, PROJECT, resources, values) def test_limit_check_over_quota(self): resources = {RESOURCE: TestResource(RESOURCE, 2)} values = {RESOURCE: 3} self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) self.assertRaises(lib_exc.OverQuota, self.plugin.limit_check, context.get_admin_context(), PROJECT, resources, values) def test_limit_check_equals_to_quota(self): resources = {RESOURCE: TestResource(RESOURCE, 2)} values = {RESOURCE: 2} self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) self.plugin.limit_check(self.context, PROJECT, resources, values) def test_limit_check_value_lower_than_zero(self): resources = {RESOURCE: TestResource(RESOURCE, 2)} values = {RESOURCE: -1} self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) self.assertRaises(exceptions.InvalidQuotaValue, self.plugin.limit_check, context.get_admin_context(), PROJECT, resources, values) def _test_make_reservation_success(self, quota_driver, resource_name, deltas): resources = {resource_name: TestResource(resource_name, 2)} self.plugin.update_quota_limit(self.context, PROJECT, resource_name, 2) reservation = quota_driver.make_reservation( self.context, self.context.tenant_id, resources, deltas, self.plugin) self.assertIn(resource_name, reservation.deltas) self.assertEqual(deltas[resource_name], reservation.deltas[resource_name]) self.assertEqual(self.context.tenant_id, reservation.tenant_id) def test_make_reservation_single_resource(self): quota_driver = driver.DbQuotaDriver() self._test_make_reservation_success( quota_driver, RESOURCE, {RESOURCE: 1}) def test_make_reservation_fill_quota(self): quota_driver = driver.DbQuotaDriver() self._test_make_reservation_success( quota_driver, RESOURCE, {RESOURCE: 2}) def test_make_reservation_multiple_resources(self): quota_driver = driver.DbQuotaDriver() resources = {RESOURCE: TestResource(RESOURCE, 2), ALT_RESOURCE: TestResource(ALT_RESOURCE, 2)} deltas = {RESOURCE: 1, ALT_RESOURCE: 2} self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) self.plugin.update_quota_limit(self.context, PROJECT, ALT_RESOURCE, 2) reservation = quota_driver.make_reservation( self.context, self.context.tenant_id, resources, deltas, self.plugin) self.assertIn(RESOURCE, reservation.deltas) self.assertIn(ALT_RESOURCE, reservation.deltas) self.assertEqual(1, reservation.deltas[RESOURCE]) self.assertEqual(2, reservation.deltas[ALT_RESOURCE]) self.assertEqual(self.context.tenant_id, reservation.tenant_id) def test_make_reservation_over_quota_fails(self): quota_driver = driver.DbQuotaDriver() resources = {RESOURCE: TestResource(RESOURCE, 2, fake_count=2)} deltas = {RESOURCE: 1} self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) self.assertRaises(lib_exc.OverQuota, quota_driver.make_reservation, self.context, self.context.tenant_id, resources, deltas, self.plugin) def test_get_detailed_tenant_quotas_resource(self): res = {RESOURCE: TestTrackedResource(RESOURCE, test_quota.MehModel)} self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 6) quota_driver = driver.DbQuotaDriver() quota_driver.make_reservation(self.context, PROJECT, res, {RESOURCE: 1}, self.plugin) quota_api.set_quota_usage(self.context, RESOURCE, PROJECT, 2) detailed_quota = self.plugin.get_detailed_tenant_quotas(self.context, res, PROJECT) self.assertEqual(6, detailed_quota[RESOURCE]['limit']) self.assertEqual(2, detailed_quota[RESOURCE]['used']) self.assertEqual(1, detailed_quota[RESOURCE]['reserved']) def test_get_detailed_tenant_quotas_multiple_resource(self): project_1 = 'prj_test_1' resource_1 = 'res_test_1' resource_2 = 'res_test_2' resources = {resource_1: TestTrackedResource(resource_1, test_quota.MehModel), resource_2: TestCountableResource(resource_2, _count_resource)} self.plugin.update_quota_limit(self.context, project_1, resource_1, 6) self.plugin.update_quota_limit(self.context, project_1, resource_2, 9) quota_driver = driver.DbQuotaDriver() quota_driver.make_reservation(self.context, project_1, resources, {resource_1: 1, resource_2: 7}, self.plugin) quota_api.set_quota_usage(self.context, resource_1, project_1, 2) quota_api.set_quota_usage(self.context, resource_2, project_1, 3) detailed_quota = self.plugin.get_detailed_tenant_quotas(self.context, resources, project_1) self.assertEqual(6, detailed_quota[resource_1]['limit']) self.assertEqual(1, detailed_quota[resource_1]['reserved']) self.assertEqual(2, detailed_quota[resource_1]['used']) self.assertEqual(9, detailed_quota[resource_2]['limit']) self.assertEqual(7, detailed_quota[resource_2]['reserved']) self.assertEqual(3, detailed_quota[resource_2]['used']) neutron-12.1.1/neutron/tests/unit/db/quota/__init__.py0000664000175000017500000000000013553660046022750 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/db/test_db_base_plugin_common.py0000664000175000017500000000617013553660046025442 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.db import db_base_plugin_common from neutron.tests import base class DummyObject(object): def __init__(self, **kwargs): self.kwargs = kwargs def to_dict(self): return self.kwargs class ConvertToDictTestCase(base.BaseTestCase): @db_base_plugin_common.convert_result_to_dict def method_dict(self, fields=None): return DummyObject(one=1, two=2, three=3) @db_base_plugin_common.convert_result_to_dict def method_list(self): return [DummyObject(one=1, two=2, three=3)] * 3 def test_simple_object(self): expected = {'one': 1, 'two': 2, 'three': 3} observed = self.method_dict() self.assertEqual(expected, observed) def test_list_of_objects(self): expected = [{'one': 1, 'two': 2, 'three': 3}] * 3 observed = self.method_list() self.assertEqual(expected, observed) class FilterFieldsTestCase(base.BaseTestCase): @db_base_plugin_common.filter_fields def method_dict(self, fields=None): return {'one': 1, 'two': 2, 'three': 3} @db_base_plugin_common.filter_fields def method_list(self, fields=None): return [self.method_dict() for _ in range(3)] @db_base_plugin_common.filter_fields def method_multiple_arguments(self, not_used, fields=None, also_not_used=None): return {'one': 1, 'two': 2, 'three': 3} def test_no_fields(self): expected = {'one': 1, 'two': 2, 'three': 3} observed = self.method_dict() self.assertEqual(expected, observed) def test_dict(self): expected = {'two': 2} observed = self.method_dict(['two']) self.assertEqual(expected, observed) def test_list(self): expected = [{'two': 2}, {'two': 2}, {'two': 2}] observed = self.method_list(['two']) self.assertEqual(expected, observed) def test_multiple_arguments_positional(self): expected = {'two': 2} observed = self.method_multiple_arguments(list(), ['two']) self.assertEqual(expected, observed) def test_multiple_arguments_positional_and_keywords(self): expected = {'two': 2} observed = self.method_multiple_arguments(fields=['two'], not_used=None) self.assertEqual(expected, observed) def test_multiple_arguments_keyword(self): expected = {'two': 2} observed = self.method_multiple_arguments(list(), fields=['two']) self.assertEqual(expected, observed) neutron-12.1.1/neutron/tests/unit/db/test_ipam_pluggable_backend.py0000664000175000017500000012343513553660047025561 0ustar zuulzuul00000000000000# Copyright (c) 2015 Infoblox Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import mock import netaddr from neutron_lib import constants from neutron_lib import context as ncontext from neutron_lib import exceptions as n_exc from oslo_config import cfg from oslo_db import exception as db_exc from oslo_utils import netutils from oslo_utils import uuidutils import webob.exc from neutron.db import ipam_backend_mixin from neutron.db import ipam_pluggable_backend from neutron.db import models_v2 from neutron.ipam import requests as ipam_req from neutron.objects import ports as port_obj from neutron.objects import subnet as obj_subnet from neutron.tests.unit.db import test_db_base_plugin_v2 as test_db_base class UseIpamMixin(object): def setUp(self): cfg.CONF.set_override("ipam_driver", 'internal') super(UseIpamMixin, self).setUp() class TestIpamHTTPResponse(UseIpamMixin, test_db_base.TestV2HTTPResponse): pass class TestIpamPorts(UseIpamMixin, test_db_base.TestPortsV2): pass class TestIpamNetworks(UseIpamMixin, test_db_base.TestNetworksV2): pass class TestIpamSubnets(UseIpamMixin, test_db_base.TestSubnetsV2): pass class TestIpamSubnetPool(UseIpamMixin, test_db_base.TestSubnetPoolsV2): pass class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase): def setUp(self): cfg.CONF.set_override("ipam_driver", 'internal') super(TestDbBasePluginIpam, self).setUp() self.tenant_id = uuidutils.generate_uuid() self.subnet_id = uuidutils.generate_uuid() self.admin_context = ncontext.get_admin_context() def _prepare_mocks(self, address_factory=None, subnet_factory=None): if address_factory is None: address_factory = ipam_req.AddressRequestFactory if subnet_factory is None: subnet_factory = ipam_req.SubnetRequestFactory mocks = { 'driver': mock.Mock(), 'subnet': mock.Mock(), 'subnets': mock.Mock(), 'port': { 'device_owner': constants.DEVICE_OWNER_COMPUTE_PREFIX + 'None' }, 'subnet_request': ipam_req.SpecificSubnetRequest( self.tenant_id, self.subnet_id, '10.0.0.0/24', '10.0.0.1', [netaddr.IPRange('10.0.0.2', '10.0.0.254')]), } mocks['driver'].get_subnet.return_value = mocks['subnet'] mocks['driver'].allocate_subnet.return_value = mocks['subnet'] mocks['driver'].get_allocator.return_value = mocks['subnets'] mocks['subnets'].allocate.return_value = ( '127.0.0.1', uuidutils.generate_uuid()) mocks['driver'].get_subnet_request_factory.return_value = ( subnet_factory) mocks['driver'].get_address_request_factory.return_value = ( address_factory) mocks['subnet'].get_details.return_value = mocks['subnet_request'] return mocks def _prepare_ipam(self): mocks = self._prepare_mocks() mocks['ipam'] = ipam_pluggable_backend.IpamPluggableBackend() return mocks def _prepare_mocks_with_pool_mock(self, pool_mock, address_factory=None, subnet_factory=None): mocks = self._prepare_mocks(address_factory=address_factory, subnet_factory=subnet_factory) pool_mock.get_instance.return_value = mocks['driver'] return mocks def _get_allocate_mock(self, subnet_id, auto_ip='10.0.0.2', fail_ip='127.0.0.1', exception=n_exc.InvalidInput( error_message='SomeError')): def allocate_mock(request): if isinstance(request, ipam_req.SpecificAddressRequest): if request.address == netaddr.IPAddress(fail_ip): raise exception else: return str(request.address), subnet_id else: return auto_ip, subnet_id return allocate_mock def _get_deallocate_mock(self, fail_ip='127.0.0.1', exception=n_exc.InvalidInput( error_message='SomeError')): def deallocate_mock(ip): if str(ip) == fail_ip: raise exception return deallocate_mock def _validate_allocate_calls(self, expected_calls, mocks): self.assertTrue(mocks['subnets'].allocate.called) actual_calls = mocks['subnets'].allocate.call_args_list self.assertEqual(len(expected_calls), len(actual_calls)) i = 0 for call in expected_calls: if call['ip_address']: self.assertIsInstance(actual_calls[i][0][0], ipam_req.SpecificAddressRequest) self.assertEqual(netaddr.IPAddress(call['ip_address']), actual_calls[i][0][0].address) else: self.assertIsInstance(actual_calls[i][0][0], ipam_req.AnyAddressRequest) i += 1 def _convert_to_ips(self, data): ips = [{'ip_address': ip, 'subnet_id': data[ip][1], 'subnet_cidr': data[ip][0]} for ip in data] return sorted(ips, key=lambda t: t['subnet_cidr']) def _gen_subnet_id(self): return uuidutils.generate_uuid() def test_deallocate_single_ip(self): mocks = self._prepare_ipam() ip = '192.168.12.45' data = {ip: ['192.168.12.0/24', self._gen_subnet_id()]} ips = self._convert_to_ips(data) mocks['ipam']._ipam_deallocate_ips(mock.ANY, mocks['driver'], mock.ANY, ips) mocks['driver'].get_subnet.assert_called_once_with(data[ip][1]) mocks['subnet'].deallocate.assert_called_once_with(ip) def test_deallocate_multiple_ips(self): mocks = self._prepare_ipam() data = {'192.168.43.15': ['192.168.43.0/24', self._gen_subnet_id()], '172.23.158.84': ['172.23.128.0/17', self._gen_subnet_id()], '8.8.8.8': ['8.0.0.0/8', self._gen_subnet_id()]} ips = self._convert_to_ips(data) mocks['ipam']._ipam_deallocate_ips(mock.ANY, mocks['driver'], mock.ANY, ips) get_calls = [mock.call(data[ip][1]) for ip in data] mocks['driver'].get_subnet.assert_has_calls(get_calls, any_order=True) ip_calls = [mock.call(ip) for ip in data] mocks['subnet'].deallocate.assert_has_calls(ip_calls, any_order=True) def _single_ip_allocate_helper(self, mocks, ip, network, subnet): ips = [{'subnet_cidr': network, 'subnet_id': subnet}] if ip: ips[0]['ip_address'] = ip allocated_ips = mocks['ipam']._ipam_allocate_ips( mock.ANY, mocks['driver'], mocks['port'], ips) mocks['driver'].get_allocator.assert_called_once_with([subnet]) self.assertTrue(mocks['subnets'].allocate.called) request = mocks['subnets'].allocate.call_args[0][0] return {'ips': allocated_ips, 'request': request} def test_allocate_single_fixed_ip(self): mocks = self._prepare_ipam() ip = '192.168.15.123' subnet_id = self._gen_subnet_id() mocks['subnets'].allocate.return_value = ip, subnet_id results = self._single_ip_allocate_helper(mocks, ip, '192.168.15.0/24', subnet_id) self.assertIsInstance(results['request'], ipam_req.SpecificAddressRequest) self.assertEqual(netaddr.IPAddress(ip), results['request'].address) self.assertEqual(ip, results['ips'][0]['ip_address'], 'Should allocate the same ip as passed') def test_allocate_single_any_ip(self): mocks = self._prepare_ipam() network = '192.168.15.0/24' ip = '192.168.15.83' subnet_id = self._gen_subnet_id() mocks['subnets'].allocate.return_value = ip, subnet_id results = self._single_ip_allocate_helper(mocks, '', network, subnet_id) self.assertIsInstance(results['request'], ipam_req.AnyAddressRequest) self.assertEqual(ip, results['ips'][0]['ip_address']) def test_allocate_eui64_ip(self): mocks = self._prepare_ipam() ip = {'subnet_id': self._gen_subnet_id(), 'subnet_cidr': '2001:470:abcd::/64', 'mac': '6c:62:6d:de:cf:49', 'eui64_address': True} eui64_ip = netutils.get_ipv6_addr_by_EUI64(ip['subnet_cidr'], ip['mac']) mocks['ipam']._ipam_allocate_ips(mock.ANY, mocks['driver'], mock.ANY, [ip]) request = mocks['subnets'].allocate.call_args[0][0] self.assertIsInstance(request, ipam_req.AutomaticAddressRequest) self.assertEqual(eui64_ip, request.address) def test_allocate_multiple_ips(self): mocks = self._prepare_ipam() subnet_id = self._gen_subnet_id() data = {'': ['172.23.128.0/17', subnet_id], '192.168.43.15': ['192.168.43.0/24', self._gen_subnet_id()], '8.8.8.8': ['8.0.0.0/8', self._gen_subnet_id()]} ips = self._convert_to_ips(data) mocks['subnets'].allocate.side_effect = self._get_allocate_mock( subnet_id, auto_ip='172.23.128.94') mocks['ipam']._ipam_allocate_ips( mock.ANY, mocks['driver'], mocks['port'], ips) get_calls = [mock.call([data[ip][1]]) for ip in data] mocks['driver'].get_allocator.assert_has_calls( get_calls, any_order=True) self._validate_allocate_calls(ips, mocks) def _test_allocate_multiple_ips_with_exception(self, exc_on_deallocate=False): mocks = self._prepare_ipam() fail_ip = '192.168.43.15' auto_ip = '172.23.128.94' subnet_id = self._gen_subnet_id() data = {'': ['172.23.128.0/17', subnet_id], fail_ip: ['192.168.43.0/24', self._gen_subnet_id()], '8.8.8.8': ['8.0.0.0/8', self._gen_subnet_id()]} ips = self._convert_to_ips(data) mocks['subnets'].allocate.side_effect = self._get_allocate_mock( subnet_id, auto_ip=auto_ip, fail_ip=fail_ip, exception=db_exc.DBDeadlock()) # Exception should be raised on attempt to allocate second ip. # Revert action should be performed for the already allocated ips, # In this test case only one ip should be deallocated # and original error should be reraised self.assertRaises(db_exc.DBDeadlock, mocks['ipam']._ipam_allocate_ips, mock.ANY, mocks['driver'], mocks['port'], ips) # get_subnet should be called only for the first two networks get_calls = [mock.call([data[ip][1]]) for ip in ['', fail_ip]] mocks['driver'].get_allocator.assert_has_calls( get_calls, any_order=True) # Allocate should be called for the first two ips only self._validate_allocate_calls(ips[:-1], mocks) # Deallocate should be called for the first ip only mocks['subnet'].deallocate.assert_called_once_with(auto_ip) def test_allocate_multiple_ips_with_exception(self): self._test_allocate_multiple_ips_with_exception() def test_allocate_multiple_ips_with_exception_on_rollback(self): # Validate that original exception is not replaced with one raised on # rollback (during deallocate) self._test_allocate_multiple_ips_with_exception(exc_on_deallocate=True) def test_deallocate_multiple_ips_with_exception(self): mocks = self._prepare_ipam() fail_ip = '192.168.43.15' data = {fail_ip: ['192.168.43.0/24', self._gen_subnet_id()], '0.10.8.8': ['0.10.0.0/8', self._gen_subnet_id()]} ips = self._convert_to_ips(data) mocks['subnet'].deallocate.side_effect = self._get_deallocate_mock( fail_ip=fail_ip, exception=db_exc.DBDeadlock()) mocks['subnet'].allocate.side_effect = ValueError('Some-error') # Validate that exception from deallocate (DBDeadlock) is not replaced # by exception from allocate (ValueError) in rollback block, # so original exception is not changed self.assertRaises(db_exc.DBDeadlock, mocks['ipam']._ipam_deallocate_ips, mock.ANY, mocks['driver'], mock.ANY, ips) mocks['subnets'].allocate.assert_called_once_with(mock.ANY) def test_test_fixed_ips_for_port_pd_gateway(self): context = mock.Mock() pluggable_backend = ipam_pluggable_backend.IpamPluggableBackend() with self.subnet(cidr=constants.PROVISIONAL_IPV6_PD_PREFIX, ip_version=6) as subnet: subnet = subnet['subnet'] fixed_ips = [{'subnet_id': subnet['id'], 'ip_address': '::1'}] filtered_ips = (pluggable_backend. _test_fixed_ips_for_port(context, subnet['network_id'], fixed_ips, constants.DEVICE_OWNER_ROUTER_INTF, [subnet])) # Assert that ports created on prefix delegation subnets # will be returned without an ip address. This prevents router # interfaces being given the ::1 gateway address. self.assertEqual(1, len(filtered_ips)) self.assertEqual(subnet['id'], filtered_ips[0]['subnet_id']) self.assertNotIn('ip_address', filtered_ips[0]) @mock.patch('neutron.ipam.driver.Pool') def test_create_subnet_over_ipam(self, pool_mock): mocks = self._prepare_mocks_with_pool_mock(pool_mock) cidr = '192.168.0.0/24' allocation_pools = [{'start': '192.168.0.2', 'end': '192.168.0.254'}] with self.subnet(allocation_pools=allocation_pools, cidr=cidr): pool_mock.get_instance.assert_called_once_with(None, mock.ANY) self.assertTrue(mocks['driver'].allocate_subnet.called) request = mocks['driver'].allocate_subnet.call_args[0][0] self.assertIsInstance(request, ipam_req.SpecificSubnetRequest) self.assertEqual(netaddr.IPNetwork(cidr), request.subnet_cidr) @mock.patch('neutron.ipam.driver.Pool') def test_create_ipv6_pd_subnet_over_ipam(self, pool_mock): mocks = self._prepare_mocks_with_pool_mock(pool_mock) cfg.CONF.set_override('ipv6_pd_enabled', True) cidr = constants.PROVISIONAL_IPV6_PD_PREFIX allocation_pools = [netaddr.IPRange('::2', '::ffff:ffff:ffff:ffff')] with self.subnet(cidr=None, ip_version=6, subnetpool_id=constants.IPV6_PD_POOL_ID, ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC): self.assertEqual(2, pool_mock.get_instance.call_count) self.assertTrue(mocks['driver'].allocate_subnet.called) request = mocks['driver'].allocate_subnet.call_args[0][0] self.assertIsInstance(request, ipam_req.SpecificSubnetRequest) self.assertEqual(netaddr.IPNetwork(cidr), request.subnet_cidr) self.assertEqual(allocation_pools, request.allocation_pools) @mock.patch('neutron.ipam.driver.Pool') def test_create_subnet_over_ipam_with_rollback(self, pool_mock): mocks = self._prepare_mocks_with_pool_mock(pool_mock) mocks['driver'].allocate_subnet.side_effect = ValueError cidr = '10.0.2.0/24' with self.network() as network: self._create_subnet(self.fmt, network['network']['id'], cidr, expected_res_status=500) pool_mock.get_instance.assert_called_once_with(None, mock.ANY) self.assertTrue(mocks['driver'].allocate_subnet.called) request = mocks['driver'].allocate_subnet.call_args[0][0] self.assertIsInstance(request, ipam_req.SpecificSubnetRequest) self.assertEqual(netaddr.IPNetwork(cidr), request.subnet_cidr) # Verify no subnet was created for network req = self.new_show_request('networks', network['network']['id']) res = req.get_response(self.api) net = self.deserialize(self.fmt, res) self.assertEqual(0, len(net['network']['subnets'])) def _test_rollback_on_subnet_creation(self, pool_mock, driver_mocks): cidr = '10.0.2.0/24' with mock.patch.object( ipam_backend_mixin.IpamBackendMixin, '_save_subnet', side_effect=ValueError), self.network() as network: self._create_subnet(self.fmt, network['network']['id'], cidr, expected_res_status=500) pool_mock.get_instance.assert_any_call(None, mock.ANY) self.assertEqual(2, pool_mock.get_instance.call_count) self.assertTrue(driver_mocks['driver'].allocate_subnet.called) request = driver_mocks['driver'].allocate_subnet.call_args[0][0] self.assertIsInstance(request, ipam_req.SpecificSubnetRequest) self.assertEqual(netaddr.IPNetwork(cidr), request.subnet_cidr) # Verify remove ipam subnet was called driver_mocks['driver'].remove_subnet.assert_called_once_with( self.subnet_id) @mock.patch('neutron.ipam.driver.Pool') def test_ipam_subnet_deallocated_if_create_fails(self, pool_mock): driver_mocks = self._prepare_mocks_with_pool_mock(pool_mock) self._test_rollback_on_subnet_creation(pool_mock, driver_mocks) @mock.patch('neutron.ipam.driver.Pool') def test_ipam_subnet_create_and_rollback_fails(self, pool_mock): driver_mocks = self._prepare_mocks_with_pool_mock(pool_mock) # remove_subnet is called on rollback stage and n_exc.NotFound # typically produces 404 error. Validate that exception from # rollback stage is silenced and main exception (ValueError in this # case) is reraised. So resulting http status should be 500. driver_mocks['driver'].remove_subnet.side_effect = n_exc.NotFound self._test_rollback_on_subnet_creation(pool_mock, driver_mocks) @mock.patch('neutron.ipam.driver.Pool') def test_update_subnet_over_ipam(self, pool_mock): mocks = self._prepare_mocks_with_pool_mock(pool_mock) cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.254'}] with self.subnet(allocation_pools=allocation_pools, cidr=cidr) as subnet: data = {'subnet': {'allocation_pools': [ {'start': '10.0.0.10', 'end': '10.0.0.20'}, {'start': '10.0.0.30', 'end': '10.0.0.40'}]}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(200, res.status_code) pool_mock.get_instance.assert_any_call(None, mock.ANY) self.assertEqual(2, pool_mock.get_instance.call_count) self.assertTrue(mocks['driver'].update_subnet.called) request = mocks['driver'].update_subnet.call_args[0][0] self.assertIsInstance(request, ipam_req.SpecificSubnetRequest) self.assertEqual(netaddr.IPNetwork(cidr), request.subnet_cidr) ip_ranges = [netaddr.IPRange(p['start'], p['end']) for p in data['subnet']['allocation_pools']] self.assertEqual(ip_ranges, request.allocation_pools) @mock.patch('neutron.ipam.driver.Pool') def test_delete_subnet_over_ipam(self, pool_mock): mocks = self._prepare_mocks_with_pool_mock(pool_mock) gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) subnet = self._make_subnet(self.fmt, network, gateway_ip, cidr, ip_version=4) req = self.new_delete_request('subnets', subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) pool_mock.get_instance.assert_any_call(None, mock.ANY) self.assertEqual(2, pool_mock.get_instance.call_count) mocks['driver'].remove_subnet.assert_called_once_with( subnet['subnet']['id']) @mock.patch('neutron.ipam.driver.Pool') def test_delete_subnet_over_ipam_with_rollback(self, pool_mock): mocks = self._prepare_mocks_with_pool_mock(pool_mock) mocks['driver'].remove_subnet.side_effect = ValueError gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) subnet = self._make_subnet(self.fmt, network, gateway_ip, cidr, ip_version=4) req = self.new_delete_request('subnets', subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPServerError.code, res.status_int) pool_mock.get_instance.assert_any_call(None, mock.ANY) self.assertEqual(2, pool_mock.get_instance.call_count) mocks['driver'].remove_subnet.assert_called_once_with( subnet['subnet']['id']) # Verify subnet was recreated after failed ipam call subnet_req = self.new_show_request('subnets', subnet['subnet']['id']) raw_res = subnet_req.get_response(self.api) sub_res = self.deserialize(self.fmt, raw_res) self.assertIn(sub_res['subnet']['cidr'], cidr) self.assertIn(sub_res['subnet']['gateway_ip'], gateway_ip) @mock.patch('neutron.ipam.driver.Pool') def test_create_port_ipam(self, pool_mock): mocks = self._prepare_mocks_with_pool_mock(pool_mock) auto_ip = '10.0.0.2' expected_calls = [{'ip_address': ''}] with self.subnet() as subnet: mocks['subnets'].allocate.side_effect = self._get_allocate_mock( subnet['subnet']['id'], auto_ip=auto_ip) with self.port(subnet=subnet) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(ips[0]['ip_address'], auto_ip) self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) self._validate_allocate_calls(expected_calls, mocks) @mock.patch('neutron.ipam.driver.Pool') def test_create_port_ipam_with_rollback(self, pool_mock): mocks = self._prepare_mocks_with_pool_mock(pool_mock) mocks['subnet'].allocate.side_effect = ValueError with self.network() as network: with self.subnet(network=network): net_id = network['network']['id'] data = { 'port': {'network_id': net_id, 'tenant_id': network['network']['tenant_id']}} port_req = self.new_create_request('ports', data) res = port_req.get_response(self.api) self.assertEqual(webob.exc.HTTPServerError.code, res.status_int) # verify no port left after failure req = self.new_list_request('ports', self.fmt, "network_id=%s" % net_id) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(0, len(res['ports'])) @mock.patch('neutron.ipam.driver.Pool') def test_update_port_ipam(self, pool_mock): mocks = self._prepare_mocks_with_pool_mock(pool_mock) auto_ip = '10.0.0.2' new_ip = '10.0.0.15' expected_calls = [{'ip_address': ip} for ip in ['', new_ip]] with self.subnet() as subnet: mocks['subnets'].allocate.side_effect = self._get_allocate_mock( subnet['subnet']['id'], auto_ip=auto_ip) with self.port(subnet=subnet) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(auto_ip, ips[0]['ip_address']) # Update port with another new ip data = {"port": {"fixed_ips": [{ 'subnet_id': subnet['subnet']['id'], 'ip_address': new_ip}]}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) ips = res['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(new_ip, ips[0]['ip_address']) # Allocate should be called for the first two networks self._validate_allocate_calls(expected_calls, mocks) # Deallocate should be called for the first ip only mocks['subnet'].deallocate.assert_called_once_with(auto_ip) @mock.patch('neutron.ipam.driver.Pool') def test_delete_port_ipam(self, pool_mock): mocks = self._prepare_mocks_with_pool_mock(pool_mock) auto_ip = '10.0.0.2' with self.subnet() as subnet: mocks['subnets'].allocate.side_effect = self._get_allocate_mock( subnet['subnet']['id'], auto_ip=auto_ip) with self.port(subnet=subnet) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(auto_ip, ips[0]['ip_address']) req = self.new_delete_request('ports', port['port']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) mocks['subnet'].deallocate.assert_called_once_with(auto_ip) def test_recreate_port_ipam(self): with self.subnet() as subnet: subnet_cidr = subnet['subnet']['cidr'] with self.port(subnet=subnet) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) orig_ip = ips[0]['ip_address'] self.assertIn(netaddr.IPAddress(ips[0]['ip_address']), netaddr.IPSet(netaddr.IPNetwork(subnet_cidr))) req = self.new_delete_request('ports', port['port']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) with self.port(subnet=subnet, fixed_ips=ips) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(orig_ip, ips[0]['ip_address']) def test_recreate_port_ipam_specific_ip(self): with self.subnet() as subnet: ip = '10.0.0.2' fixed_ip_data = [{'subnet_id': subnet['subnet']['id'], 'ip_address': ip}] with self.port(subnet=subnet, fixed_ips=fixed_ip_data) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(ip, ips[0]['ip_address']) req = self.new_delete_request('ports', port['port']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) with self.port(subnet=subnet, fixed_ips=ips) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(ip, ips[0]['ip_address']) @mock.patch('neutron.ipam.driver.Pool') def test_update_ips_for_port_passes_port_dict_to_factory(self, pool_mock): address_factory = mock.Mock() mocks = self._prepare_mocks_with_pool_mock( pool_mock, address_factory=address_factory) context = mock.Mock() new_ips = mock.Mock() original_ips = mock.Mock() mac = mock.Mock() ip_dict = {'ip_address': '192.1.1.10', 'subnet_id': uuidutils.generate_uuid()} changes = ipam_pluggable_backend.IpamPluggableBackend.Changes( add=[ip_dict], original=[], remove=[]) changes_mock = mock.Mock(return_value=changes) fixed_ips_mock = mock.Mock(return_value=changes.add) mocks['ipam'] = ipam_pluggable_backend.IpamPluggableBackend() mocks['ipam']._get_changed_ips_for_port = changes_mock mocks['ipam']._ipam_get_subnets = mock.Mock(return_value=[]) mocks['ipam']._test_fixed_ips_for_port = fixed_ips_mock mocks['ipam']._update_ips_for_pd_subnet = mock.Mock(return_value=[]) port_dict = {'device_owner': uuidutils.generate_uuid(), 'network_id': uuidutils.generate_uuid()} mocks['ipam']._update_ips_for_port(context, port_dict, None, original_ips, new_ips, mac) mocks['driver'].get_address_request_factory.assert_called_once_with() mocks['ipam']._ipam_get_subnets.assert_called_once_with( context, network_id=port_dict['network_id'], fixed_configured=True, host=None, service_type=port_dict['device_owner']) # Validate port_dict is passed into address_factory address_factory.get_request.assert_called_once_with(context, port_dict, ip_dict) @mock.patch('neutron.ipam.driver.Pool') def test_update_ips_for_port_passes_port_id_to_factory(self, pool_mock): port_id = uuidutils.generate_uuid() network_id = uuidutils.generate_uuid() address_factory = mock.Mock() mocks = self._prepare_mocks_with_pool_mock( pool_mock, address_factory=address_factory) context = mock.Mock() ip_dict = {'ip_address': '192.1.1.10', 'subnet_id': uuidutils.generate_uuid()} port_dict = {'port': {'device_owner': uuidutils.generate_uuid(), 'network_id': network_id, 'fixed_ips': [ip_dict]}} subnets = [{'id': ip_dict['subnet_id'], 'network_id': network_id, 'cidr': '192.1.1.0/24', 'ip_version': 4, 'ipv6_address_mode': None, 'ipv6_ra_mode': None}] get_subnets_mock = mock.Mock(return_value=subnets) get_subnet_mock = mock.Mock(return_value=subnets[0]) mocks['ipam'] = ipam_pluggable_backend.IpamPluggableBackend() mocks['ipam']._ipam_get_subnets = get_subnets_mock mocks['ipam']._get_subnet = get_subnet_mock with mock.patch.object(port_obj.IPAllocation, 'create'): mocks['ipam'].allocate_ips_for_port_and_store(context, port_dict, port_id) mocks['driver'].get_address_request_factory.assert_called_once_with() port_dict_with_id = port_dict['port'].copy() port_dict_with_id['id'] = port_id # Validate port id is added to port dict before address_factory call ip_dict.pop('device_owner') address_factory.get_request.assert_called_once_with(context, port_dict_with_id, ip_dict) # Verify incoming port dict is not changed ('id' is not added to it) self.assertIsNone(port_dict['port'].get('id')) def _test_update_db_subnet(self, pool_mock, subnet, expected_subnet, old_pools): subnet_factory = mock.Mock() context = self.admin_context if 'cidr' in subnet: subnet['cidr'] = netaddr.IPNetwork(subnet['cidr']) if 'cidr' in expected_subnet: expected_subnet['cidr'] = netaddr.IPNetwork( expected_subnet['cidr']) mocks = self._prepare_mocks_with_pool_mock( pool_mock, subnet_factory=subnet_factory) mocks['ipam'] = ipam_pluggable_backend.IpamPluggableBackend() mocks['ipam'].update_db_subnet( context, subnet['id'], subnet, old_pools) mocks['driver'].get_subnet_request_factory.assert_called_once_with() subnet_factory.get_request.assert_called_once_with(context, expected_subnet, None) @mock.patch('neutron.ipam.driver.Pool') def test_update_db_subnet_unchanged_pools(self, pool_mock): old_pools = [{'start': '192.1.1.2', 'end': '192.1.1.254'}] context = self.admin_context subnet = {'id': uuidutils.generate_uuid(), 'ip_version': constants.IP_VERSION_4, 'cidr': '192.1.1.0/24', 'ipv6_address_mode': None, 'ipv6_ra_mode': None} subnet_with_pools = subnet.copy() subnet_db = models_v2.Subnet(**subnet_with_pools) context.session.add(subnet_db) context.session.flush() subnet_with_pools['allocation_pools'] = old_pools # if subnet has no allocation pools set, then old pools has to # be added to subnet dict passed to request factory self._test_update_db_subnet(pool_mock, subnet, subnet_with_pools, old_pools) @mock.patch('neutron.ipam.driver.Pool') def test_update_db_subnet_new_pools(self, pool_mock): old_pools = [{'start': '192.1.1.2', 'end': '192.1.1.254'}] context = self.admin_context subnet = {'id': uuidutils.generate_uuid(), 'ip_version': constants.IP_VERSION_4, 'cidr': '192.1.1.0/24', 'ipv6_address_mode': None, 'ipv6_ra_mode': None} # make a copy of subnet for validation, since update_subnet changes # incoming subnet dict expected_subnet = subnet.copy() subnet_db = models_v2.Subnet(**subnet) context.session.add(subnet_db) context.session.flush() subnet['allocation_pools'] = [ netaddr.IPRange('192.1.1.10', '192.1.1.254')] expected_subnet = subnet.copy() obj_subnet.IPAllocationPool(context, subnet_id=subnet['id'], start='192.1.1.10', end='192.1.1.254').create() context.session.refresh(subnet_db) # validate that subnet passed to request factory is the same as # incoming one, i.e. new pools in it are not overwritten by old pools self._test_update_db_subnet(pool_mock, subnet, expected_subnet, old_pools) @mock.patch('neutron.ipam.driver.Pool') def test_update_db_subnet_new_pools_exception(self, pool_mock): context = mock.Mock() mocks = self._prepare_mocks_with_pool_mock(pool_mock) mocks['ipam'] = ipam_pluggable_backend.IpamPluggableBackend() new_port = {'fixed_ips': [{'ip_address': '192.168.1.20', 'subnet_id': uuidutils.generate_uuid()}, {'ip_address': '192.168.1.50', 'subnet_id': uuidutils.generate_uuid()}]} db_port = models_v2.Port(id=uuidutils.generate_uuid(), network_id=uuidutils.generate_uuid()) old_port = {'fixed_ips': [{'ip_address': '192.168.1.10', 'subnet_id': uuidutils.generate_uuid()}, {'ip_address': '192.168.1.50', 'subnet_id': uuidutils.generate_uuid()}]} changes = mocks['ipam'].Changes( add=[{'ip_address': '192.168.1.20', 'subnet_id': uuidutils.generate_uuid()}], original=[{'ip_address': '192.168.1.50', 'subnet_id': uuidutils.generate_uuid()}], remove=[{'ip_address': '192.168.1.10', 'subnet_id': uuidutils.generate_uuid()}]) mocks['ipam']._delete_ip_allocation = mock.Mock() mocks['ipam']._make_port_dict = mock.Mock(return_value=old_port) mocks['ipam']._update_ips_for_port = mock.Mock(return_value=changes) mocks['ipam']._update_db_port = mock.Mock( side_effect=db_exc.DBDeadlock) # emulate raising exception on rollback actions mocks['ipam']._ipam_deallocate_ips = mock.Mock(side_effect=ValueError) mocks['ipam']._ipam_allocate_ips = mock.Mock(side_effect=ValueError) # Validate original exception (DBDeadlock) is not overridden by # exception raised on rollback (ValueError) with mock.patch.object(port_obj.IPAllocation, 'create'): self.assertRaises(db_exc.DBDeadlock, mocks['ipam'].update_port_with_ips, context, None, db_port, new_port, mock.Mock()) mocks['ipam']._ipam_deallocate_ips.assert_called_once_with( context, mocks['driver'], db_port, changes.add, revert_on_fail=False) mocks['ipam']._ipam_allocate_ips.assert_called_once_with( context, mocks['driver'], db_port, changes.remove, revert_on_fail=False) class TestRollback(test_db_base.NeutronDbPluginV2TestCase): def setUp(self): cfg.CONF.set_override('ipam_driver', 'internal') super(TestRollback, self).setUp() def test_ipam_rollback_not_broken_on_session_rollback(self): """Triggers an error that calls rollback on session.""" with self.network() as net: with self.subnet(network=net, cidr='10.0.1.0/24') as subnet1: with self.subnet(network=net, cidr='10.0.2.0/24') as subnet2: pass # If this test fails and this method appears in the server side stack # trace then IPAM rollback was likely tried using a session which had # already been rolled back by the DB exception. def rollback(func, *args, **kwargs): func(*args, **kwargs) # Ensure DBDuplicate exception is raised in the context where IPAM # rollback is triggered. It "breaks" the session because it triggers DB # rollback. Inserting a flush in _store_ip_allocation does this. orig = ipam_pluggable_backend.IpamPluggableBackend._store_ip_allocation def store(context, ip_address, *args, **kwargs): try: return orig(context, ip_address, *args, **kwargs) finally: context.session.flush() # Create a port to conflict with later. Simulates a race for addresses. result = self._create_port( self.fmt, net_id=net['network']['id'], fixed_ips=[{'subnet_id': subnet1['subnet']['id']}, {'subnet_id': subnet2['subnet']['id']}]) port = self.deserialize(self.fmt, result) fixed_ips = port['port']['fixed_ips'] # Hands out the same 2nd IP to create conflict and trigger rollback ips = [{'subnet_id': fixed_ips[0]['subnet_id'], 'ip_address': fixed_ips[0]['ip_address']}, {'subnet_id': fixed_ips[1]['subnet_id'], 'ip_address': fixed_ips[1]['ip_address']}] def alloc(*args, **kwargs): def increment_address(a): a['ip_address'] = str(netaddr.IPAddress(a['ip_address']) + 1) # Increment 1st address to return a free address on the first call increment_address(ips[0]) try: return copy.deepcopy(ips) finally: # Increment 2nd address to return free address on the 2nd call increment_address(ips[1]) Backend = ipam_pluggable_backend.IpamPluggableBackend with mock.patch.object(Backend, '_store_ip_allocation', wraps=store),\ mock.patch.object(Backend, '_safe_rollback', wraps=rollback),\ mock.patch.object(Backend, '_allocate_ips_for_port', wraps=alloc): # Create port with two addresses. The wrapper lets one succeed # then simulates race for the second to trigger IPAM rollback. response = self._create_port( self.fmt, net_id=net['network']['id'], fixed_ips=[{'subnet_id': subnet1['subnet']['id']}, {'subnet_id': subnet2['subnet']['id']}]) # When all goes well, retry kicks in and the operation is successful. self.assertEqual(webob.exc.HTTPCreated.code, response.status_int) neutron-12.1.1/neutron/tests/unit/db/test_extraroute_db.py0000664000175000017500000000531313553660047024003 0ustar zuulzuul00000000000000# Copyright (c) 2016 Midokura SARL # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from neutron_lib.plugins import constants from neutron_lib.plugins import directory from neutron.db import common_db_mixin from neutron.db import extraroute_db from neutron.tests.unit import testlib_api class _Plugin(common_db_mixin.CommonDbMixin, extraroute_db.ExtraRoute_dbonly_mixin): pass class TestExtraRouteDb(testlib_api.SqlTestCase): def setUp(self): super(TestExtraRouteDb, self).setUp() self._plugin = _Plugin() directory.add_plugin(constants.CORE, self._plugin) def test_update(self): ctx = context.get_admin_context() create_request = { 'router': { 'name': 'my router', 'tenant_id': 'my tenant', 'admin_state_up': True, } } router = self._plugin.create_router(ctx, create_request) self.assertItemsEqual(router['routes'], []) router_id = router['id'] routes = [ {'destination': '10.0.0.0/24', 'nexthop': '1.1.1.4'}, {'destination': '10.1.0.0/24', 'nexthop': '1.1.1.3'}, {'destination': '10.2.0.0/24', 'nexthop': '1.1.1.2'}, ] self._test_update_routes(ctx, router_id, router, routes) routes = [ {'destination': '10.0.0.0/24', 'nexthop': '1.1.1.4'}, {'destination': '10.2.0.0/24', 'nexthop': '1.1.1.2'}, {'destination': '10.3.0.0/24', 'nexthop': '1.1.1.1'}, ] self._test_update_routes(ctx, router_id, router, routes) def _test_update_routes(self, ctx, router_id, router, routes): router['routes'] = routes update_request = { 'router': router, } with mock.patch.object(self._plugin, '_validate_routes'): updated_router = self._plugin.update_router(ctx, router_id, update_request) self.assertItemsEqual(updated_router['routes'], routes) got_router = self._plugin.get_router(ctx, router_id) self.assertItemsEqual(got_router['routes'], routes) neutron-12.1.1/neutron/tests/unit/db/test_l3_hamode_db.py0000664000175000017500000017331613553660047023445 0ustar zuulzuul00000000000000# Copyright (C) 2014 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import external_net as extnet_apidef from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import provider_net as providernet from neutron_lib.callbacks import events from neutron_lib.callbacks import exceptions as c_exc from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import l3 as l3_exc from neutron_lib.exceptions import l3_ext_ha_mode as l3ha_exc from neutron_lib.objects import exceptions from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_db import exception as db_exc from oslo_utils import uuidutils import sqlalchemy as sa from sqlalchemy import orm import testtools from neutron.agent.common import utils as agent_utils from neutron.api.rpc.handlers import l3_rpc from neutron.common import constants as n_const from neutron.db import agents_db from neutron.db import common_db_mixin from neutron.db import l3_agentschedulers_db from neutron.db import l3_hamode_db from neutron.db.models import l3ha as l3ha_model from neutron.objects import l3_hamode from neutron.scheduler import l3_agent_scheduler from neutron.services.revisions import revision_plugin from neutron.tests.common import helpers from neutron.tests.unit import testlib_api _uuid = uuidutils.generate_uuid class FakeL3PluginWithAgents(common_db_mixin.CommonDbMixin, l3_hamode_db.L3_HA_NAT_db_mixin, l3_agentschedulers_db.L3AgentSchedulerDbMixin, agents_db.AgentDbMixin): pass class L3HATestFramework(testlib_api.SqlTestCase): def setUp(self): super(L3HATestFramework, self).setUp() self.setup_coreplugin('ml2') self.core_plugin = directory.get_plugin() notif_p = mock.patch.object(l3_hamode_db.L3_HA_NAT_db_mixin, '_notify_router_updated') self.notif_m = notif_p.start() cfg.CONF.set_override('allow_overlapping_ips', True) self.plugin = FakeL3PluginWithAgents() directory.add_plugin(plugin_constants.L3, self.plugin) self.plugin.router_scheduler = l3_agent_scheduler.ChanceScheduler() self.agent1 = helpers.register_l3_agent() self.agent2 = helpers.register_l3_agent( 'host_2', constants.L3_AGENT_MODE_DVR_SNAT) @property def admin_ctx(self): # Property generates a new session on each reference so different # API calls don't share a session with possible stale objects return context.get_admin_context() def _create_router(self, ha=True, tenant_id='tenant1', distributed=None, ctx=None, admin_state_up=True): if ctx is None: ctx = self.admin_ctx ctx.tenant_id = tenant_id router = {'name': 'router1', 'admin_state_up': admin_state_up, 'tenant_id': tenant_id} if ha is not None: router['ha'] = ha if distributed is not None: router['distributed'] = distributed return self.plugin.create_router(ctx, {'router': router}) def _migrate_router(self, router_id, ha): self._update_router(router_id, admin_state=False) self._update_router(router_id, ha=ha) return self._update_router(router_id, admin_state=True) def _update_router(self, router_id, ha=None, distributed=None, ctx=None, admin_state=None): if ctx is None: ctx = self.admin_ctx data = {'ha': ha} if ha is not None else {} if distributed is not None: data['distributed'] = distributed if admin_state is not None: data['admin_state_up'] = admin_state self.plugin.update_router(ctx, router_id, {'router': data}) return self.plugin._get_router(ctx, router_id) class L3HATestCase(L3HATestFramework): def test_verify_configuration_succeed(self): # Default configuration should pass self.plugin._verify_configuration() def test_verify_configuration_l3_ha_net_cidr_is_not_a_cidr(self): cfg.CONF.set_override('l3_ha_net_cidr', 'not a cidr') self.assertRaises( l3ha_exc.HANetworkCIDRNotValid, self.plugin._verify_configuration) def test_verify_configuration_l3_ha_net_cidr_is_not_a_subnet(self): cfg.CONF.set_override('l3_ha_net_cidr', '10.0.0.1/8') self.assertRaises( l3ha_exc.HANetworkCIDRNotValid, self.plugin._verify_configuration) def test_verify_configuration_max_l3_agents_below_0(self): cfg.CONF.set_override('max_l3_agents_per_router', -5) self.assertRaises( l3ha_exc.HAMaximumAgentsNumberNotValid, self.plugin._check_num_agents_per_router) def test_verify_configuration_max_l3_agents_unlimited(self): cfg.CONF.set_override('max_l3_agents_per_router', l3_hamode_db.UNLIMITED_AGENTS_PER_ROUTER) self.plugin._check_num_agents_per_router() def test_get_ha_router_port_bindings(self): router = self._create_router() bindings = self.plugin.get_ha_router_port_bindings( self.admin_ctx, [router['id']]) binding_dicts = [{'router_id': binding['router_id'], 'l3_agent_id': binding['l3_agent_id']} for binding in bindings] self.assertIn({'router_id': router['id'], 'l3_agent_id': self.agent1['id']}, binding_dicts) self.assertIn({'router_id': router['id'], 'l3_agent_id': self.agent2['id']}, binding_dicts) def test_get_l3_bindings_hosting_router_with_ha_states_ha_router(self): router = self._create_router() self.plugin.update_routers_states( self.admin_ctx, {router['id']: 'active'}, self.agent1['host']) bindings = self.plugin.get_l3_bindings_hosting_router_with_ha_states( self.admin_ctx, router['id']) agent_ids = [(agent[0]['id'], agent[1]) for agent in bindings] self.assertIn((self.agent1['id'], 'active'), agent_ids) self.assertIn((self.agent2['id'], 'standby'), agent_ids) def test_get_l3_bindings_hosting_router_with_ha_states_not_scheduled(self): router = self._create_router(ha=False) # Check that there no L3 agents scheduled for this router res = l3_hamode.L3HARouterAgentPortBinding.get_objects( self.admin_ctx, router_id=router['id']) self.assertEqual([], [r.agent for r in res]) bindings = self.plugin.get_l3_bindings_hosting_router_with_ha_states( self.admin_ctx, router['id']) self.assertEqual([], bindings) def _assert_ha_state_for_agent(self, router, agent, state=n_const.HA_ROUTER_STATE_STANDBY): bindings = ( self.plugin.get_l3_bindings_hosting_router_with_ha_states( self.admin_ctx, router['id'])) agent_ids = [(a[0]['id'], a[1]) for a in bindings] self.assertIn((agent['id'], state), agent_ids) def test_get_l3_bindings_hosting_router_with_ha_states_active_and_dead( self): router = self._create_router() self.plugin.update_routers_states( self.admin_ctx, {router['id']: n_const.HA_ROUTER_STATE_ACTIVE}, self.agent1['host']) self.plugin.update_routers_states( self.admin_ctx, {router['id']: n_const.HA_ROUTER_STATE_ACTIVE}, self.agent2['host']) with mock.patch.object(agent_utils, 'is_agent_down', return_value=True): self._assert_ha_state_for_agent(router, self.agent1) def test_get_l3_bindings_hosting_router_agents_admin_state_up_is_false( self): router = self._create_router() self.plugin.update_routers_states( self.admin_ctx, {router['id']: n_const.HA_ROUTER_STATE_ACTIVE}, self.agent1['host']) self.plugin.update_routers_states( self.admin_ctx, {router['id']: n_const.HA_ROUTER_STATE_ACTIVE}, self.agent2['host']) helpers.set_agent_admin_state(self.agent1['id']) self._assert_ha_state_for_agent(router, self.agent1) def test_get_l3_bindings_hosting_router_with_ha_states_one_dead(self): router = self._create_router() self.plugin.update_routers_states( self.admin_ctx, {router['id']: n_const.HA_ROUTER_STATE_ACTIVE}, self.agent1['host']) self.plugin.update_routers_states( self.admin_ctx, {router['id']: n_const.HA_ROUTER_STATE_STANDBY}, self.agent2['host']) with mock.patch.object(agent_utils, 'is_agent_down', return_value=True): self._assert_ha_state_for_agent( router, self.agent1, state=n_const.HA_ROUTER_STATE_ACTIVE) def test_ha_router_create(self): router = self._create_router() self.assertTrue(router['ha']) def test_ha_router_create_with_distributed(self): helpers.register_l3_agent( 'host_3', constants.L3_AGENT_MODE_DVR_SNAT) router = self._create_router(ha=True, distributed=True) self.assertTrue(router['ha']) self.assertTrue(router['distributed']) ha_network = self.plugin.get_ha_network(self.admin_ctx, router['tenant_id']) self.assertIsNotNone(ha_network) def test_no_ha_router_create(self): router = self._create_router(ha=False) self.assertFalse(router['ha']) def test_add_ha_network_settings(self): cfg.CONF.set_override('l3_ha_network_type', 'abc') cfg.CONF.set_override('l3_ha_network_physical_name', 'def') network = {} self.plugin._add_ha_network_settings(network) self.assertEqual('abc', network[providernet.NETWORK_TYPE]) self.assertEqual('def', network[providernet.PHYSICAL_NETWORK]) def test_router_create_with_ha_conf_enabled(self): cfg.CONF.set_override('l3_ha', True) router = self._create_router(ha=None) self.assertTrue(router['ha']) def test_ha_interface_concurrent_create_on_delete(self): # this test depends on protection from the revision plugin so # we have to initialize it revision_plugin.RevisionPlugin() router = self._create_router(ha=True) def jam_in_interface(*args, **kwargs): ctx = context.get_admin_context() net = self.plugin._ensure_vr_id_and_network( ctx, self.plugin._get_router(ctx, router['id'])) self.plugin.add_ha_port( ctx, router['id'], net.network_id, router['tenant_id']) registry.unsubscribe(jam_in_interface, resources.ROUTER, events.PRECOMMIT_DELETE) registry.subscribe(jam_in_interface, resources.ROUTER, events.PRECOMMIT_DELETE) self.plugin.delete_router(self.admin_ctx, router['id']) def test_ha_router_delete_with_distributed(self): router = self._create_router(ha=True, distributed=True) self.plugin.delete_router(self.admin_ctx, router['id']) self.assertRaises(l3_exc.RouterNotFound, self.plugin._get_router, self.admin_ctx, router['id']) def test_migration_from_ha(self): router = self._create_router() self.assertTrue(router['ha']) router = self._migrate_router(router['id'], False) self.assertFalse(router.extra_attributes['ha']) self.assertIsNone(router.extra_attributes['ha_vr_id']) def test_migration_to_ha(self): router = self._create_router(ha=False) self.assertFalse(router['ha']) router = self._migrate_router(router['id'], True) self.assertTrue(router.extra_attributes['ha']) self.assertIsNotNone(router.extra_attributes['ha_vr_id']) def test_migration_requires_admin_state_down(self): router = self._create_router(ha=False) e = self.assertRaises(c_exc.CallbackFailure, self._update_router, router['id'], ha=True) self.assertIsInstance(e.inner_exceptions[0], n_exc.BadRequest) def test_migrate_ha_router_to_distributed_and_ha(self): router = self._create_router(ha=True, admin_state_up=False, distributed=False) self.assertTrue(router['ha']) after_update = self._update_router(router['id'], ha=True, distributed=True) self.assertTrue(after_update.extra_attributes.ha) self.assertTrue(after_update.extra_attributes.distributed) def test_migrate_ha_router_to_distributed_and_not_ha(self): router = self._create_router(ha=True, admin_state_up=False, distributed=False) self.assertTrue(router['ha']) after_update = self._update_router(router['id'], ha=False, distributed=True) self.assertFalse(after_update.extra_attributes.ha) self.assertTrue(after_update.extra_attributes.distributed) def test_migrate_dvr_router_to_ha_and_not_dvr(self): router = self._create_router(ha=False, admin_state_up=False, distributed=True) self.assertTrue(router['distributed']) after_update = self._update_router(router['id'], ha=True, distributed=False) self.assertTrue(after_update.extra_attributes.ha) self.assertFalse(after_update.extra_attributes.distributed) def test_migrate_dvr_router_to_ha_and_dvr(self): router = self._create_router(ha=False, admin_state_up=False, distributed=True) self.assertTrue(router['distributed']) after_update = self._update_router(router['id'], ha=True, distributed=True) self.assertTrue(after_update.extra_attributes.ha) self.assertTrue(after_update.extra_attributes.distributed) def test_migrate_distributed_router_to_ha(self): router = self._create_router(ha=False, admin_state_up=False, distributed=True) self.assertFalse(router['ha']) self.assertTrue(router['distributed']) after_update = self._update_router(router['id'], ha=True, distributed=False) self.assertTrue(after_update.extra_attributes.ha) self.assertFalse(after_update.extra_attributes.distributed) def test_migrate_legacy_router_to_distributed_and_ha(self): router = self._create_router(ha=False, admin_state_up=False, distributed=False) self.assertFalse(router['ha']) self.assertFalse(router['distributed']) after_update = self._update_router(router['id'], ha=True, distributed=True) self.assertTrue(after_update.extra_attributes.ha) self.assertTrue(after_update.extra_attributes.distributed) def test_unbind_ha_router(self): router = self._create_router() bound_agents = self.plugin.get_l3_agents_hosting_routers( self.admin_ctx, [router['id']]) self.assertEqual(2, len(bound_agents)) self.plugin._unbind_ha_router(self.admin_ctx, router['id']) bound_agents = self.plugin.get_l3_agents_hosting_routers( self.admin_ctx, [router['id']]) self.assertEqual(0, len(bound_agents)) def test_get_ha_sync_data_for_host_with_non_dvr_agent(self): with mock.patch.object(self.plugin, '_get_dvr_sync_data') as mock_get_sync: self.plugin.supported_extension_aliases = ['dvr', 'l3-ha'] self.plugin.get_ha_sync_data_for_host(self.admin_ctx, self.agent1['host'], self.agent1) self.assertFalse(mock_get_sync.called) def test_get_ha_sync_data_for_host_with_dvr_agent(self): with mock.patch.object(self.plugin, '_get_dvr_sync_data') as mock_get_sync: self.plugin.supported_extension_aliases = ['dvr', 'l3-ha'] self.plugin.get_ha_sync_data_for_host(self.admin_ctx, self.agent2['host'], self.agent2) self.assertTrue(mock_get_sync.called) def test_l3_agent_routers_query_interface(self): router = self._create_router() routers = self.plugin.get_ha_sync_data_for_host(self.admin_ctx, self.agent1['host'], self.agent1) self.assertEqual(1, len(routers)) router = routers[0] self.assertIsNotNone(router.get('ha')) interface = router.get(constants.HA_INTERFACE_KEY) self.assertIsNotNone(interface) self.assertEqual(constants.DEVICE_OWNER_ROUTER_HA_INTF, interface['device_owner']) subnets = interface['subnets'] self.assertEqual(1, len(subnets)) self.assertEqual(cfg.CONF.l3_ha_net_cidr, subnets[0]['cidr']) def test_l3_agent_routers_query_interface_includes_dvrsnat(self): router = self._create_router(distributed=True) routers = self.plugin.get_ha_sync_data_for_host(self.admin_ctx, 'a-dvr_snat-host', self.agent2) self.assertEqual(1, len(routers)) router = routers[0] self.assertTrue(router.get('ha')) interface = router.get(constants.HA_INTERFACE_KEY) self.assertIsNone(interface) def test_unique_ha_network_per_tenant(self): tenant1 = _uuid() tenant2 = _uuid() self._create_router(tenant_id=tenant1) self._create_router(tenant_id=tenant2) ha_network1 = self.plugin.get_ha_network(self.admin_ctx, tenant1) ha_network2 = self.plugin.get_ha_network(self.admin_ctx, tenant2) self.assertNotEqual( ha_network1['network_id'], ha_network2['network_id']) def _deployed_router_change_ha_flag(self, to_ha): router1 = self._create_router(ha=not to_ha) routers = self.plugin.get_ha_sync_data_for_host( self.admin_ctx, self.agent1['host'], self.agent1) router = routers[0] interface = router.get(constants.HA_INTERFACE_KEY) if to_ha: self.assertIsNone(interface) else: self.assertIsNotNone(interface) self._migrate_router(router['id'], to_ha) self.plugin.schedule_router(self.admin_ctx, router1['id']) routers = self.plugin.get_ha_sync_data_for_host( self.admin_ctx, self.agent1['host'], self.agent1) router = routers[0] interface = router.get(constants.HA_INTERFACE_KEY) if to_ha: self.assertIsNotNone(interface) else: self.assertIsNone(interface) def test_deployed_router_can_have_ha_enabled(self): self._deployed_router_change_ha_flag(to_ha=True) def test_deployed_router_can_have_ha_disabled(self): self._deployed_router_change_ha_flag(to_ha=False) def test_create_ha_router_notifies_agent(self): self._create_router() self.assertTrue(self.notif_m.called) def test_update_router_to_ha_notifies_agent(self): router = self._create_router(ha=False) self.notif_m.reset_mock() self._migrate_router(router['id'], True) self.assertTrue(self.notif_m.called) def test_unique_vr_id_between_routers(self): self._create_router() self._create_router() routers = self.plugin.get_ha_sync_data_for_host( self.admin_ctx, self.agent1['host'], self.agent1) self.assertEqual(2, len(routers)) self.assertNotEqual(routers[0]['ha_vr_id'], routers[1]['ha_vr_id']) @mock.patch('neutron.db.l3_hamode_db.VR_ID_RANGE', new=set(range(1, 1))) def test_vr_id_depleted(self): self.assertEqual(constants.ERROR, self._create_router()['status']) @mock.patch('neutron.db.l3_hamode_db.VR_ID_RANGE', new=set(range(1, 2))) def test_vr_id_unique_range_per_tenant(self): self._create_router() self._create_router(tenant_id=_uuid()) routers = self.plugin.get_ha_sync_data_for_host( self.admin_ctx, self.agent1['host'], self.agent1) self.assertEqual(2, len(routers)) self.assertEqual(routers[0]['ha_vr_id'], routers[1]['ha_vr_id']) @mock.patch('neutron.db.l3_hamode_db.MAX_ALLOCATION_TRIES', new=2) def test_vr_id_allocation_contraint_conflict(self): router = self._create_router() network = self.plugin.get_ha_network(self.admin_ctx, router['tenant_id']) router_db = self.plugin._get_router(self.admin_ctx, router['id']) self.assertIsNone(self.plugin._ensure_vr_id(self.admin_ctx, router_db, network)) def test_vr_id_allocation_delete_router(self): router = self._create_router() network = self.plugin.get_ha_network(self.admin_ctx, router['tenant_id']) allocs_before = self.plugin._get_allocated_vr_id(self.admin_ctx, network.network_id) router = self._create_router() allocs_current = self.plugin._get_allocated_vr_id(self.admin_ctx, network.network_id) self.assertNotEqual(allocs_before, allocs_current) self.plugin.delete_router(self.admin_ctx, router['id']) allocs_after = self.plugin._get_allocated_vr_id(self.admin_ctx, network.network_id) self.assertEqual(allocs_before, allocs_after) def test_vr_id_allocation_router_migration(self): router = self._create_router() network = self.plugin.get_ha_network(self.admin_ctx, router['tenant_id']) allocs_before = self.plugin._get_allocated_vr_id(self.admin_ctx, network.network_id) router = self._create_router() self._migrate_router(router['id'], False) allocs_after = self.plugin._get_allocated_vr_id(self.admin_ctx, network.network_id) self.assertEqual(allocs_before, allocs_after) def test_migration_delete_ha_network_if_last_router(self): router = self._create_router() self._migrate_router(router['id'], False) self.assertIsNone( self.plugin.get_ha_network(self.admin_ctx, router['tenant_id'])) def test_migration_no_delete_ha_network_if_not_last_router(self): router = self._create_router() router2 = self._create_router() network = self.plugin.get_ha_network(self.admin_ctx, router['tenant_id']) network2 = self.plugin.get_ha_network(self.admin_ctx, router2['tenant_id']) self.assertEqual(network.network_id, network2.network_id) self._migrate_router(router['id'], False) self.assertIsNotNone( self.plugin.get_ha_network(self.admin_ctx, router2['tenant_id'])) def test_one_ha_router_one_not(self): self._create_router(ha=False) self._create_router() routers = self.plugin.get_ha_sync_data_for_host( self.admin_ctx, self.agent1['host'], self.agent1) ha0 = routers[0]['ha'] ha1 = routers[1]['ha'] self.assertNotEqual(ha0, ha1) def test_add_ha_port_subtransactions_blocked(self): ctx = self.admin_ctx with ctx.session.begin(): self.assertRaises(RuntimeError, self.plugin.add_ha_port, ctx, 'id', 'id', 'id') def test_add_ha_port_binding_failure_rolls_back_port(self): router = self._create_router() device_filter = {'device_id': [router['id']]} ports_before = self.core_plugin.get_ports( self.admin_ctx, filters=device_filter) network = self.plugin.get_ha_network(self.admin_ctx, router['tenant_id']) with mock.patch.object(l3ha_model, 'L3HARouterAgentPortBinding', side_effect=ValueError): self.assertRaises(ValueError, self.plugin.add_ha_port, self.admin_ctx, router['id'], network.network_id, router['tenant_id']) ports_after = self.core_plugin.get_ports( self.admin_ctx, filters=device_filter) self.assertEqual(ports_before, ports_after) def test_create_ha_network_binding_failure_rolls_back_network(self): networks_before = self.core_plugin.get_networks(self.admin_ctx) with mock.patch.object(l3_hamode, 'L3HARouterNetwork', side_effect=ValueError): self.assertRaises(ValueError, self.plugin._create_ha_network, self.admin_ctx, _uuid()) networks_after = self.core_plugin.get_networks(self.admin_ctx) self.assertEqual(networks_before, networks_after) def test_create_ha_network_subnet_failure_rolls_back_network(self): networks_before = self.core_plugin.get_networks(self.admin_ctx) with mock.patch.object(self.plugin, '_create_ha_subnet', side_effect=ValueError): self.assertRaises(ValueError, self.plugin._create_ha_network, self.admin_ctx, _uuid()) networks_after = self.core_plugin.get_networks(self.admin_ctx) self.assertEqual(networks_before, networks_after) def test_ensure_vr_id_and_network_net_exists(self): router = self._create_router() router_db = self.plugin._get_router(self.admin_ctx, router['id']) with mock.patch.object(self.plugin, '_create_ha_network') as create: self.plugin._ensure_vr_id_and_network( self.admin_ctx, router_db) self.assertFalse(create.called) def test_ensure_vr_id_and_network_concurrent_create(self): # create a non-ha router so we can manually invoke the create ha # interfaces call down below router = self._create_router(ha=False) router_db = self.plugin._get_router(self.admin_ctx, router['id']) orig_create = self.plugin._create_ha_network created_nets = [] def _create_ha_network(*args, **kwargs): # create the network and then raise the error to simulate another # worker creating the network before us. created_nets.append(orig_create(*args, **kwargs)) raise db_exc.DBDuplicateEntry(columns=['tenant_id']) with mock.patch.object(self.plugin, '_create_ha_network', new=_create_ha_network): net = self.plugin._ensure_vr_id_and_network( self.admin_ctx, router_db) # ensure that it used the concurrently created network self.assertEqual([net], created_nets) def _test_ensure_with_patched_ensure_vr_id(self, _ensure_vr_id): # create a non-ha router so we can manually invoke the create ha # interfaces call down below router = self._create_router(ha=False) router_db = self.plugin._get_router(self.admin_ctx, router['id']) with mock.patch.object(self.plugin, '_ensure_vr_id', new=_ensure_vr_id): self.plugin._ensure_vr_id_and_network( self.admin_ctx, router_db) self.assertTrue(_ensure_vr_id.called) def test_ensure_vr_id_and_network_interface_failure(self): def _ensure_vr_id(ctx, rdb, ha_net): raise ValueError('broken') with testtools.ExpectedException(ValueError): self._test_ensure_with_patched_ensure_vr_id(_ensure_vr_id) self.assertEqual([], self.core_plugin.get_networks(self.admin_ctx)) def test_ensure_vr_id_and_network_concurrent_delete(self): orig_create = self.plugin._ensure_vr_id def _ensure_vr_id(ctx, rdb, ha_net): # concurrent delete on the first attempt if not getattr(_ensure_vr_id, 'called', False): setattr(_ensure_vr_id, 'called', True) self.core_plugin.delete_network(self.admin_ctx, ha_net['network_id']) return orig_create(ctx, rdb, ha_net) self._test_ensure_with_patched_ensure_vr_id(_ensure_vr_id) def test_ensure_vr_id_and_network_concurrent_swap(self): orig_create = self.plugin._ensure_vr_id def _ensure_vr_id(ctx, rdb, ha_net): # concurrent delete on the first attempt if not getattr(_ensure_vr_id, 'called', False): setattr(_ensure_vr_id, 'called', True) self.core_plugin.delete_network(self.admin_ctx, ha_net['network_id']) self.plugin._create_ha_network(self.admin_ctx, rdb.tenant_id) return orig_create(ctx, rdb, ha_net) self._test_ensure_with_patched_ensure_vr_id(_ensure_vr_id) def test_create_ha_network_tenant_binding_raises_duplicate(self): router = self._create_router() network = self.plugin.get_ha_network(self.admin_ctx, router['tenant_id']) self.plugin._create_ha_network_tenant_binding( self.admin_ctx, 't1', network['network_id']) with testtools.ExpectedException( exceptions.NeutronDbObjectDuplicateEntry): self.plugin._create_ha_network_tenant_binding( self.admin_ctx, 't1', network['network_id']) def test_create_router_db_vr_id_allocation_goes_to_error(self): for method in ('_ensure_vr_id', '_notify_router_updated'): with mock.patch.object(self.plugin, method, side_effect=ValueError): self.assertEqual(constants.ERROR, self._create_router()['status']) def test_get_active_host_for_ha_router(self): router = self._create_router() self.assertIsNone( self.plugin.get_active_host_for_ha_router( self.admin_ctx, router['id'])) self.plugin.update_routers_states( self.admin_ctx, {router['id']: 'active'}, self.agent2['host']) self.assertEqual( self.agent2['host'], self.plugin.get_active_host_for_ha_router( self.admin_ctx, router['id'])) def test_update_routers_states(self): router1 = self._create_router() router2 = self._create_router() routers = self.plugin.get_ha_sync_data_for_host( self.admin_ctx, self.agent1['host'], self.agent1) for router in routers: self.assertEqual('standby', router[n_const.HA_ROUTER_STATE_KEY]) states = {router1['id']: 'active', router2['id']: 'standby'} self.plugin.update_routers_states( self.admin_ctx, states, self.agent1['host']) routers = self.plugin.get_ha_sync_data_for_host( self.admin_ctx, self.agent1['host'], self.agent1) for router in routers: self.assertEqual(states[router['id']], router[n_const.HA_ROUTER_STATE_KEY]) def test_sync_ha_router_info_ha_interface_port_concurrently_deleted(self): ctx = self.admin_ctx router1 = self._create_router() router2 = self._create_router() # retrieve all router ha port bindings bindings = self.plugin.get_ha_router_port_bindings( ctx, [router1['id'], router2['id']]) self.assertEqual(4, len(bindings)) routers = self.plugin.get_ha_sync_data_for_host( self.admin_ctx, self.agent1['host'], self.agent1) self.assertEqual(2, len(routers)) bindings = self.plugin.get_ha_router_port_bindings( ctx, [router1['id'], router2['id']], self.agent1['host']) self.assertEqual(2, len(bindings)) fake_binding = mock.Mock() fake_binding.router_id = bindings[1].router_id fake_binding.port = None with mock.patch.object( self.plugin, "get_ha_router_port_bindings", return_value=[bindings[0], fake_binding]): routers = self.plugin.get_ha_sync_data_for_host( ctx, self.agent1['host'], self.agent1) self.assertEqual(1, len(routers)) self.assertIsNotNone(routers[0].get(constants.HA_INTERFACE_KEY)) def test_sync_ha_router_info_router_concurrently_deleted(self): self._create_router() with mock.patch.object( self.plugin, "get_ha_router_port_bindings", return_value=[]): routers = self.plugin.get_ha_sync_data_for_host( self.admin_ctx, self.agent1['host'], self.agent1) self.assertEqual(0, len(routers)) def test_sync_ha_router_info_router_concurrently_deleted_agent_dvr(self): self._create_router() orig_func = self.plugin._process_sync_ha_data def process_sync_ha_data(context, routers, host, agent_mode): return orig_func(context, routers, host, is_any_dvr_agent=True) with mock.patch.object(self.plugin, '_process_sync_ha_data', side_effect=process_sync_ha_data): routers = self.plugin.get_ha_sync_data_for_host( self.admin_ctx, self.agent1['host'], self.agent1) self.assertEqual(1, len(routers)) def test_set_router_states_handles_concurrently_deleted_router(self): router1 = self._create_router() router2 = self._create_router() ctx = self.admin_ctx bindings = self.plugin.get_ha_router_port_bindings( ctx, [router1['id'], router2['id']]) self.plugin.delete_router(self.admin_ctx, router1['id']) self.plugin._set_router_states( ctx, bindings, {router1['id']: 'active', router2['id']: 'active'}) routers = self.plugin.get_ha_sync_data_for_host( self.admin_ctx, self.agent1['host'], self.agent1) self.assertEqual('active', routers[0][n_const.HA_ROUTER_STATE_KEY]) def test_update_routers_states_port_not_found(self): router1 = self._create_router() port = {'id': 'foo', 'device_id': router1['id']} with mock.patch.object(self.core_plugin, 'get_ports', return_value=[port]): with mock.patch.object( self.core_plugin, 'update_port', side_effect=n_exc.PortNotFound(port_id='foo')): states = {router1['id']: 'active'} self.plugin.update_routers_states( self.admin_ctx, states, self.agent1['host']) def test_exclude_dvr_agents_for_ha_candidates(self): """Test dvr agents configured with "dvr" only, as opposed to "dvr_snat", are excluded. This test case tests that when get_number_of_agents_for_scheduling is called, it does not count dvr only agents. """ # Test setup registers two l3 agents. # Register another l3 agent with dvr mode and assert that # get_number_of_ha_agent_candidates return 2. helpers.register_l3_agent('host_3', constants.L3_AGENT_MODE_DVR) num_ha_candidates = self.plugin.get_number_of_agents_for_scheduling( self.admin_ctx) self.assertEqual(2, num_ha_candidates) def test_include_dvr_snat_agents_for_ha_candidates(self): """Test dvr agents configured with "dvr_snat" are excluded. This test case tests that when get_number_of_agents_for_scheduling is called, it ounts dvr_snat agents. """ # Test setup registers two l3 agents. # Register another l3 agent with dvr mode and assert that # get_number_of_ha_agent_candidates return 2. helpers.register_l3_agent('host_3', constants.L3_AGENT_MODE_DVR_SNAT) num_ha_candidates = self.plugin.get_number_of_agents_for_scheduling( self.admin_ctx) self.assertEqual(3, num_ha_candidates) def test_ha_network_deleted_if_no_ha_router_present_two_tenants(self): # Create two routers in different tenants. router1 = self._create_router() router2 = self._create_router(tenant_id='tenant2') nets_before = [net['name'] for net in self.core_plugin.get_networks(self.admin_ctx)] # Check that HA networks created for each tenant self.assertIn('HA network tenant %s' % router1['tenant_id'], nets_before) self.assertIn('HA network tenant %s' % router2['tenant_id'], nets_before) # Delete router1 self.plugin.delete_router(self.admin_ctx, router1['id']) nets_after = [net['name'] for net in self.core_plugin.get_networks(self.admin_ctx)] # Check that HA network for tenant1 is deleted and for tenant2 is not. self.assertNotIn('HA network tenant %s' % router1['tenant_id'], nets_after) self.assertIn('HA network tenant %s' % router2['tenant_id'], nets_after) def test_ha_network_is_not_delete_if_ha_router_is_present(self): # Create 2 routers in one tenant and check if one is deleted, HA # network still exists. router1 = self._create_router() router2 = self._create_router() nets_before = [net['name'] for net in self.core_plugin.get_networks(self.admin_ctx)] self.assertIn('HA network tenant %s' % router1['tenant_id'], nets_before) self.plugin.delete_router(self.admin_ctx, router2['id']) nets_after = [net['name'] for net in self.core_plugin.get_networks(self.admin_ctx)] self.assertIn('HA network tenant %s' % router1['tenant_id'], nets_after) def test_ha_network_delete_ha_and_non_ha_router(self): # Create HA and non-HA router. Check after deletion HA router HA # network is deleted. router1 = self._create_router(ha=False) router2 = self._create_router() nets_before = [net['name'] for net in self.core_plugin.get_networks(self.admin_ctx)] self.assertIn('HA network tenant %s' % router1['tenant_id'], nets_before) self.plugin.delete_router(self.admin_ctx, router2['id']) nets_after = [net['name'] for net in self.core_plugin.get_networks(self.admin_ctx)] self.assertNotIn('HA network tenant %s' % router1['tenant_id'], nets_after) def _test_ha_network_is_not_deleted_raise_exception(self, exception): router1 = self._create_router() nets_before = [net['name'] for net in self.core_plugin.get_networks(self.admin_ctx)] self.assertIn('HA network tenant %s' % router1['tenant_id'], nets_before) ha_network = self.plugin.get_ha_network(self.admin_ctx, router1['tenant_id']) with mock.patch.object(self.plugin, '_delete_ha_network', side_effect=exception): self.plugin.safe_delete_ha_network(self.admin_ctx, ha_network, router1['tenant_id']) nets_after = [net['name'] for net in self.core_plugin.get_networks(self.admin_ctx)] self.assertIn('HA network tenant %s' % router1['tenant_id'], nets_after) def test_ha_network_is_not_deleted_if_another_ha_router_is_created(self): # If another router was created during deletion of current router, # _delete_ha_network will fail with InvalidRequestError. Check that HA # network won't be deleted. self._test_ha_network_is_not_deleted_raise_exception( sa.exc.InvalidRequestError) def test_ha_network_is_not_deleted_if_network_in_use(self): self._test_ha_network_is_not_deleted_raise_exception( n_exc.NetworkInUse(net_id="foo_net_id")) def test_ha_network_is_not_deleted_if_db_deleted_error(self): self._test_ha_network_is_not_deleted_raise_exception( orm.exc.ObjectDeletedError(None)) def test_ha_router_create_failed_no_ha_network_delete(self): tenant_id = "foo_tenant_id" nets_before = self.core_plugin.get_networks(self.admin_ctx) self.assertNotIn('HA network tenant %s' % tenant_id, nets_before) # Unable to create HA network with mock.patch.object(self.core_plugin, 'create_network', side_effect=n_exc.NoNetworkAvailable): e = self.assertRaises(c_exc.CallbackFailure, self._create_router, True, tenant_id) self.assertIsInstance(e.inner_exceptions[0], n_exc.NoNetworkAvailable) nets_after = self.core_plugin.get_networks(self.admin_ctx) self.assertEqual(nets_before, nets_after) self.assertNotIn('HA network tenant %s' % tenant_id, nets_after) def test_update_port_status_port_bingding_deleted_concurrently(self): router1 = self._create_router() states = {router1['id']: 'active'} with mock.patch.object(self.plugin, 'get_ha_router_port_bindings'): (l3_hamode.L3HARouterAgentPortBinding.delete_objects( self.admin_ctx, router_id=router1['id'])) self.plugin.update_routers_states( self.admin_ctx, states, self.agent1['host']) class L3HAModeDbTestCase(L3HATestFramework): def _create_network(self, plugin, ctx, name='net', tenant_id='tenant1', external=False): network = {'network': {'name': name, 'shared': False, 'admin_state_up': True, 'tenant_id': tenant_id, extnet_apidef.EXTERNAL: external}} return plugin.create_network(ctx, network)['id'] def _create_subnet(self, plugin, ctx, network_id, cidr='10.0.0.0/8', name='subnet', tenant_id='tenant1'): subnet = {'subnet': {'name': name, 'ip_version': 4, 'network_id': network_id, 'cidr': cidr, 'gateway_ip': constants.ATTR_NOT_SPECIFIED, 'allocation_pools': constants.ATTR_NOT_SPECIFIED, 'dns_nameservers': constants.ATTR_NOT_SPECIFIED, 'host_routes': constants.ATTR_NOT_SPECIFIED, 'tenant_id': tenant_id, 'enable_dhcp': True, 'ipv6_ra_mode': constants.ATTR_NOT_SPECIFIED}} created_subnet = plugin.create_subnet(ctx, subnet) return created_subnet def _test_device_owner(self, router_id, dvr, ha): if dvr: device_owner = constants.DEVICE_OWNER_DVR_INTERFACE elif ha: device_owner = constants.DEVICE_OWNER_HA_REPLICATED_INT else: device_owner = constants.DEVICE_OWNER_ROUTER_INTF filters = {'device_id': [router_id], 'device_owner': [device_owner]} ports = self.core_plugin.get_ports(self.admin_ctx, filters=filters) self.assertEqual(1, len(ports)) def _test_device_owner_during_router_migration( self, before_ha=False, before_dvr=False, after_ha=False, after_dvr=False): # As HA router is supported only in this test file, # we test all migrations here router = self._create_router( ctx=self.admin_ctx, ha=before_ha, distributed=before_dvr) network_id = self._create_network(self.core_plugin, self.admin_ctx) subnet = self._create_subnet( self.core_plugin, self.admin_ctx, network_id) interface_info = {'subnet_id': subnet['id']} self.plugin.add_router_interface( self.admin_ctx, router['id'], interface_info) self._test_device_owner(router['id'], before_dvr, before_ha) self.plugin.update_router( self.admin_ctx, router['id'], {'router': {'admin_state_up': False}}) self.plugin.update_router( self.admin_ctx, router['id'], {'router': {'distributed': after_dvr, 'ha': after_ha}}) self._test_device_owner(router['id'], after_dvr, after_ha) def test_device_owner_during_router_migration_from_dvr_to_ha(self): self._test_device_owner_during_router_migration( before_dvr=True, after_ha=True) def test_device_owner_during_router_migration_from_dvr_to_dvrha(self): self._test_device_owner_during_router_migration( before_dvr=True, after_ha=True, after_dvr=True) def test_device_owner_during_router_migration_from_dvr_to_legacy(self): self._test_device_owner_during_router_migration(before_dvr=True) def test_device_owner_during_router_migration_from_ha_to_legacy(self): self._test_device_owner_during_router_migration(before_ha=True) def test_device_owner_during_router_migration_from_ha_to_dvr(self): self._test_device_owner_during_router_migration( before_ha=True, after_dvr=True) def test_device_owner_during_router_migration_from_ha_to_dvrha(self): self._test_device_owner_during_router_migration( before_ha=True, after_ha=True, after_dvr=True) def test_device_owner_during_router_migration_from_legacy_to_dvr(self): self._test_device_owner_during_router_migration(after_dvr=True) def test_device_owner_during_router_migration_from_legacy_to_ha(self): self._test_device_owner_during_router_migration(after_ha=True) def test_remove_ha_in_use(self): router = self._create_router(ctx=self.admin_ctx) network_id = self._create_network(self.core_plugin, self.admin_ctx) subnet = self._create_subnet(self.core_plugin, self.admin_ctx, network_id) interface_info = {'subnet_id': subnet['id']} self.plugin.add_router_interface(self.admin_ctx, router['id'], interface_info) self.assertRaises(l3_exc.RouterInUse, self.plugin.delete_router, self.admin_ctx, router['id']) bindings = self.plugin.get_ha_router_port_bindings( self.admin_ctx, [router['id']]) self.assertEqual(2, len(bindings)) def test_update_router_port_bindings_no_ports(self): self.plugin._update_router_port_bindings( self.admin_ctx, {}, self.agent1['host']) def _get_first_interface(self, router_id): device_filter = {'device_id': [router_id], 'device_owner': [constants.DEVICE_OWNER_HA_REPLICATED_INT]} return self.core_plugin.get_ports( self.admin_ctx, filters=device_filter)[0] def _get_router_port_bindings(self, router_id): device_filter = {'device_id': [router_id], 'device_owner': [constants.DEVICE_OWNER_HA_REPLICATED_INT, constants.DEVICE_OWNER_ROUTER_SNAT, constants.DEVICE_OWNER_ROUTER_GW]} return self.core_plugin.get_ports( self.admin_ctx, filters=device_filter) def test_update_router_port_bindings_updates_host(self): ext_net = self._create_network(self.core_plugin, self.admin_ctx, external=True) network_id = self._create_network(self.core_plugin, self.admin_ctx) subnet = self._create_subnet(self.core_plugin, self.admin_ctx, network_id) interface_info = {'subnet_id': subnet['id']} router = self._create_router() self.plugin._update_router_gw_info(self.admin_ctx, router['id'], {'network_id': ext_net}) self.plugin.add_router_interface(self.admin_ctx, router['id'], interface_info) self.plugin._update_router_port_bindings( self.admin_ctx, {router['id']: 'active'}, self.agent1['host']) for port in self._get_router_port_bindings(router['id']): self.assertEqual(self.agent1['host'], port[portbindings.HOST_ID]) self.plugin._update_router_port_bindings( self.admin_ctx, {router['id']: 'active'}, self.agent2['host']) for port in self._get_router_port_bindings(router['id']): self.assertEqual(self.agent2['host'], port[portbindings.HOST_ID]) def test_update_router_port_bindings_updates_host_only(self): ext_net = self._create_network(self.core_plugin, self.admin_ctx, external=True) network_id = self._create_network(self.core_plugin, self.admin_ctx) subnet = self._create_subnet(self.core_plugin, self.admin_ctx, network_id) interface_info = {'subnet_id': subnet['id']} router = self._create_router() self.plugin._update_router_gw_info(self.admin_ctx, router['id'], {'network_id': ext_net}) iface = self.plugin.add_router_interface(self.admin_ctx, router['id'], interface_info) with mock.patch.object( self.plugin._core_plugin, 'update_port') as update_port_mock: self.plugin._update_router_port_bindings( self.admin_ctx, {router['id']: 'active'}, self.agent1['host']) port_payload = { port_def.RESOURCE_NAME: { portbindings.HOST_ID: self.agent1['host'] } } update_port_mock.assert_called_with( mock.ANY, iface['port_id'], port_payload) def test_update_all_ha_network_port_statuses(self): router = self._create_router(ha=True) callback = l3_rpc.L3RpcCallback() callback._l3plugin = self.plugin host = self.agent1['host'] ctx = self.admin_ctx bindings = self.plugin.get_ha_router_port_bindings( ctx, [router['id']]) binding = [binding for binding in bindings if binding.l3_agent_id == self.agent1['id']][0] port = self.core_plugin.get_port(ctx, binding.port_id) # As network segments are not available, mock bind_port # to avoid binding failures def bind_port(context): binding = context._binding binding.vif_type = portbindings.VIF_TYPE_OVS with mock.patch.object(self.core_plugin.mechanism_manager, 'bind_port', side_effect=bind_port): callback._ensure_host_set_on_port( ctx, host, port, router_id=router['id']) # Port status will be DOWN by default as we are not having # l2 agent in test, so update it to ACTIVE. self.core_plugin.update_port_status( ctx, port['id'], constants.PORT_STATUS_ACTIVE, host=host) port = self.core_plugin.get_port(ctx, port['id']) self.assertEqual(constants.PORT_STATUS_ACTIVE, port['status']) callback.update_all_ha_network_port_statuses(ctx, host) port = self.core_plugin.get_port(ctx, port['id']) self.assertEqual(constants.PORT_STATUS_DOWN, port['status']) def test_ensure_host_set_on_ports_dvr_ha_binds_to_active(self): agent3 = helpers.register_l3_agent('host_3', constants.L3_AGENT_MODE_DVR_SNAT) ext_net = self._create_network(self.core_plugin, self.admin_ctx, external=True) int_net = self._create_network(self.core_plugin, self.admin_ctx) subnet = self._create_subnet(self.core_plugin, self.admin_ctx, int_net) interface_info = {'subnet_id': subnet['id']} router = self._create_router(ha=True, distributed=True) self.plugin._update_router_gw_info(self.admin_ctx, router['id'], {'network_id': ext_net}) self.plugin.add_router_interface(self.admin_ctx, router['id'], interface_info) ctx = self.admin_ctx bindings = self.plugin.get_ha_router_port_bindings( ctx, router_ids=[router['id']], host=self.agent2['host']) self.plugin._set_router_states(ctx, bindings, {router['id']: 'active'}) callback = l3_rpc.L3RpcCallback() callback._l3plugin = self.plugin # Get router with interfaces router = self.plugin._get_dvr_sync_data(self.admin_ctx, self.agent2['host'], self.agent2, [router['id']])[0] callback._ensure_host_set_on_ports(self.admin_ctx, agent3['host'], [router]) device_filter = {'device_id': [router['id']], 'device_owner': [constants.DEVICE_OWNER_ROUTER_SNAT] } port = self.core_plugin.get_ports(self.admin_ctx, filters=device_filter)[0] self.assertNotEqual(agent3['host'], port[portbindings.HOST_ID]) callback._ensure_host_set_on_ports(self.admin_ctx, self.agent2['host'], [router]) port = self.core_plugin.get_ports(self.admin_ctx, filters=device_filter)[0] self.assertEqual(self.agent2['host'], port[portbindings.HOST_ID]) def test_ensure_host_set_on_ports_binds_correctly(self): network_id = self._create_network(self.core_plugin, self.admin_ctx) subnet = self._create_subnet(self.core_plugin, self.admin_ctx, network_id) interface_info = {'subnet_id': subnet['id']} router = self._create_router() self.plugin.add_router_interface(self.admin_ctx, router['id'], interface_info) port = self._get_first_interface(router['id']) self.assertEqual('', port[portbindings.HOST_ID]) # Update the router object to include the first interface router = ( self.plugin.list_active_sync_routers_on_active_l3_agent( self.admin_ctx, self.agent1['host'], [router['id']]))[0] # ensure_host_set_on_ports binds an unbound port callback = l3_rpc.L3RpcCallback() callback._l3plugin = self.plugin callback._ensure_host_set_on_ports( self.admin_ctx, self.agent1['host'], [router]) port = self._get_first_interface(router['id']) self.assertEqual(self.agent1['host'], port[portbindings.HOST_ID]) # ensure_host_set_on_ports does not rebind a bound port router = ( self.plugin.list_active_sync_routers_on_active_l3_agent( self.admin_ctx, self.agent1['host'], [router['id']]))[0] callback._ensure_host_set_on_ports( self.admin_ctx, self.agent2['host'], [router]) port = self._get_first_interface(router['id']) self.assertEqual(self.agent1['host'], port[portbindings.HOST_ID]) def test_is_ha_router_port(self): network_id = self._create_network(self.core_plugin, self.admin_ctx) subnet = self._create_subnet(self.core_plugin, self.admin_ctx, network_id) interface_info = {'subnet_id': subnet['id']} router = self._create_router() self.plugin.add_router_interface(self.admin_ctx, router['id'], interface_info) port = self._get_first_interface(router['id']) self.assertTrue(l3_hamode_db.is_ha_router_port( self.admin_ctx, port['device_owner'], port['device_id'])) def test_is_ha_router_port_for_normal_port(self): network_id = self._create_network(self.core_plugin, self.admin_ctx) subnet = self._create_subnet(self.core_plugin, self.admin_ctx, network_id) interface_info = {'subnet_id': subnet['id']} router = self._create_router(ha=False) self.plugin.add_router_interface(self.admin_ctx, router['id'], interface_info) device_filter = {'device_id': [router['id']], 'device_owner': [constants.DEVICE_OWNER_ROUTER_INTF]} port = self.core_plugin.get_ports( self.admin_ctx, filters=device_filter)[0] self.assertFalse(l3_hamode_db.is_ha_router_port( self.admin_ctx, port['device_owner'], port['device_id'])) def test_migration_from_ha(self): router = self._create_router() self.assertTrue(router['ha']) network_id = self._create_network(self.core_plugin, self.admin_ctx) subnet = self._create_subnet(self.core_plugin, self.admin_ctx, network_id) interface_info = {'subnet_id': subnet['id']} self.plugin.add_router_interface(self.admin_ctx, router['id'], interface_info) router = self._migrate_router(router['id'], False) self.assertFalse(router.extra_attributes['ha']) for routerport in router.attached_ports: self.assertEqual(constants.DEVICE_OWNER_ROUTER_INTF, routerport.port_type) self.assertEqual(constants.DEVICE_OWNER_ROUTER_INTF, routerport.port.device_owner) def test__get_sync_routers_with_state_change_and_check_gw_port_host(self): ext_net = self._create_network(self.core_plugin, self.admin_ctx, external=True) network_id = self._create_network(self.core_plugin, self.admin_ctx) subnet = self._create_subnet(self.core_plugin, self.admin_ctx, network_id) interface_info = {'subnet_id': subnet['id']} router = self._create_router() self.plugin._update_router_gw_info(self.admin_ctx, router['id'], {'network_id': ext_net}) self.plugin.add_router_interface(self.admin_ctx, router['id'], interface_info) self.plugin.update_routers_states( self.admin_ctx, {router['id']: n_const.HA_ROUTER_STATE_ACTIVE}, self.agent1['host']) self.plugin.update_routers_states( self.admin_ctx, {router['id']: n_const.HA_ROUTER_STATE_STANDBY}, self.agent2['host']) routers = self.plugin._get_sync_routers(self.admin_ctx, router_ids=[router['id']]) self.assertEqual(self.agent1['host'], routers[0]['gw_port_host']) self.plugin.update_routers_states( self.admin_ctx, {router['id']: n_const.HA_ROUTER_STATE_STANDBY}, self.agent1['host']) self.plugin.update_routers_states( self.admin_ctx, {router['id']: n_const.HA_ROUTER_STATE_ACTIVE}, self.agent2['host']) routers = self.plugin._get_sync_routers(self.admin_ctx, router_ids=[router['id']]) self.assertEqual(self.agent2['host'], routers[0]['gw_port_host']) class L3HAUserTestCase(L3HATestFramework): def setUp(self): super(L3HAUserTestCase, self).setUp() self.user_ctx = context.Context('', _uuid()) def test_create_ha_router(self): self._create_router(ctx=self.user_ctx) def test_update_router(self): router = self._create_router(ctx=self.user_ctx) self._update_router(router['id'], ctx=self.user_ctx) def test_delete_router(self): router = self._create_router(ctx=self.user_ctx) self.plugin.delete_router(self.user_ctx, router['id']) neutron-12.1.1/neutron/tests/unit/db/__init__.py0000664000175000017500000000000013553660046021617 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/db/test_agents_db.py0000664000175000017500000003676113553660047023075 0ustar zuulzuul00000000000000# pylint: disable=pointless-string-statement # Copyright (c) 2013 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import copy import datetime import mock from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions as n_exc from oslo_config import cfg from oslo_db import exception as exc from oslo_utils import timeutils import testscenarios from neutron.db import agents_db from neutron.db import db_base_plugin_v2 as base_plugin from neutron.objects import agent as agent_obj from neutron.objects import base from neutron.tests.unit import testlib_api # the below code is required for the following reason # (as documented in testscenarios) """Multiply tests depending on their 'scenarios' attribute. This can be assigned to 'load_tests' in any test module to make this automatically work across tests in the module. """ load_tests = testscenarios.load_tests_apply_scenarios TEST_RESOURCE_VERSIONS = {"A": "1.0"} AGENT_STATUS = {'agent_type': 'Open vSwitch agent', 'binary': 'neutron-openvswitch-agent', 'host': 'overcloud-notcompute', 'topic': 'N/A', 'resource_versions': TEST_RESOURCE_VERSIONS} TEST_TIME = '2016-02-26T17:08:06.116' class FakePlugin(base_plugin.NeutronDbPluginV2, agents_db.AgentDbMixin): """A fake plugin class containing all DB methods.""" class TestAgentsDbBase(testlib_api.SqlTestCase): def setUp(self): super(TestAgentsDbBase, self).setUp() self.context = context.get_admin_context() self.plugin = FakePlugin() def _get_agents(self, hosts, agent_type): return [ agent_obj.Agent( context=self.context, binary='foo-agent', host=host, agent_type=agent_type, topic='foo_topic', configurations="{}", created_at=timeutils.utcnow(), started_at=timeutils.utcnow(), heartbeat_timestamp=timeutils.utcnow()) for host in hosts ] def _create_and_save_agents(self, hosts, agent_type, down_agents_count=0, down_but_version_considered=0): agents = self._get_agents(hosts, agent_type) # bring down the specified agents for agent in agents[:down_agents_count]: agent['heartbeat_timestamp'] -= datetime.timedelta(minutes=60) # bring down just enough so their version is still considered for agent in agents[down_agents_count:( down_but_version_considered + down_agents_count)]: agent['heartbeat_timestamp'] -= datetime.timedelta( seconds=(cfg.CONF.agent_down_time + 1)) for agent in agents: agent.create() return agents class TestAgentsDbMixin(TestAgentsDbBase): def setUp(self): super(TestAgentsDbMixin, self).setUp() self.agent_status = dict(AGENT_STATUS) def test_get_enabled_agent_on_host_found(self): agents = self._create_and_save_agents(['foo_host'], constants.AGENT_TYPE_L3) expected = self.plugin.get_enabled_agent_on_host( self.context, constants.AGENT_TYPE_L3, 'foo_host') self.assertEqual(expected, agents[0]) def test_get_enabled_agent_on_host_not_found(self): with mock.patch.object(agents_db.LOG, 'debug') as mock_log: agent = self.plugin.get_enabled_agent_on_host( self.context, constants.AGENT_TYPE_L3, 'foo_agent') self.assertIsNone(agent) self.assertTrue(mock_log.called) def _assert_ref_fields_are_equal(self, reference, result): """Compare (key, value) pairs of a reference dict with the result Note: the result MAY have additional keys """ for field, value in reference.items(): self.assertEqual(value, result[field], field) def test_create_or_update_agent_new_entry(self): self.plugin.create_or_update_agent(self.context, self.agent_status) agent = self.plugin.get_agents(self.context)[0] self._assert_ref_fields_are_equal(self.agent_status, agent) def test_create_or_update_agent_existing_entry(self): self.plugin.create_or_update_agent(self.context, self.agent_status) self.plugin.create_or_update_agent(self.context, self.agent_status) self.plugin.create_or_update_agent(self.context, self.agent_status) agents = self.plugin.get_agents(self.context) self.assertEqual(len(agents), 1) agent = agents[0] self._assert_ref_fields_are_equal(self.agent_status, agent) def test_create_or_update_agent_logs_heartbeat(self): status = self.agent_status.copy() status['configurations'] = {'log_agent_heartbeats': True} with mock.patch.object(agents_db.LOG, 'info') as info: self.plugin.create_or_update_agent(self.context, status) self.assertTrue(info.called) status['configurations'] = {'log_agent_heartbeats': False} info.reset_mock() self.plugin.create_or_update_agent(self.context, status) self.assertFalse(info.called) def test_create_or_update_agent_concurrent_insert(self): # NOTE(rpodolyaka): emulate violation of the unique constraint caused # by a concurrent insert. Ensure we make another # attempt on fail mock.patch( 'neutron.objects.base.NeutronDbObject.modify_fields_from_db' ).start() mock.patch.object(self.context.session, 'expunge').start() with mock.patch('neutron.objects.db.api.create_object') as add_mock: add_mock.side_effect = [ exc.DBDuplicateEntry(), mock.Mock() ] self.plugin.create_or_update_agent(self.context, self.agent_status) self.assertEqual(add_mock.call_count, 2, "Agent entry creation hasn't been retried") def test_create_or_update_agent_disable_new_agents(self): cfg.CONF.set_override('enable_new_agents', False) self.plugin.create_or_update_agent(self.context, self.agent_status) agent = self.plugin.get_agents(self.context)[0] self.assertFalse(agent['admin_state_up']) def test_agent_health_check(self): agents = [{'agent_type': "DHCP Agent", 'heartbeat_timestamp': '2015-05-06 22:40:40.432295', 'host': 'some.node', 'alive': True}] with mock.patch.object(self.plugin, 'get_agents', return_value=agents),\ mock.patch.object(agents_db.LOG, 'warning') as warn,\ mock.patch.object(agents_db.LOG, 'debug') as debug: self.plugin.agent_health_check() self.assertTrue(debug.called) self.assertFalse(warn.called) agents[0]['alive'] = False self.plugin.agent_health_check() warn.assert_called_once_with( mock.ANY, {'count': 1, 'total': 1, 'data': " Type Last heartbeat host\n" " DHCP Agent 2015-05-06 22:40:40.432295 some.node"} ) def test__get_dict(self): db_obj = mock.Mock(conf1='{"test": "1234"}') conf1 = self.plugin._get_dict(db_obj, 'conf1') self.assertIn('test', conf1) self.assertEqual("1234", conf1['test']) def test__get_dict_missing(self): with mock.patch.object(agents_db.LOG, 'warning') as warn: db_obj = mock.Mock(spec=['agent_type', 'host']) self.plugin._get_dict(db_obj, 'missing_conf') self.assertTrue(warn.called) def test__get_dict_ignore_missing(self): with mock.patch.object(agents_db.LOG, 'warning') as warn: db_obj = mock.Mock(spec=['agent_type', 'host']) missing_conf = self.plugin._get_dict(db_obj, 'missing_conf', ignore_missing=True) self.assertEqual({}, missing_conf) warn.assert_not_called() def test__get_dict_broken(self): with mock.patch.object(agents_db.LOG, 'warning') as warn: db_obj = mock.Mock(conf1='{"test": BROKEN') conf1 = self.plugin._get_dict(db_obj, 'conf1', ignore_missing=True) self.assertEqual({}, conf1) self.assertTrue(warn.called) def get_configurations_dict(self): db_obj = mock.Mock(configurations='{"cfg1": "val1"}') cfg = self.plugin.get_configuration_dict(db_obj) self.assertIn('cfg', cfg) def test_get_agents_resource_versions(self): tracker = mock.Mock() self._create_and_save_agents( ['host-%d' % i for i in range(5)], constants.AGENT_TYPE_L3, down_agents_count=3, down_but_version_considered=2) self.plugin.get_agents_resource_versions(tracker) self.assertEqual(tracker.set_versions.call_count, 2) class TestAgentsDbGetAgents(TestAgentsDbBase): scenarios = [ ('Get all agents', dict(agents=5, down_agents=2, agents_alive=None, expected_agents=5)), ('Get alive agents (True)', dict(agents=5, down_agents=2, agents_alive='True', expected_agents=3)), ('Get down agents (False)', dict(agents=5, down_agents=2, agents_alive='False', expected_agents=2)), ('Get alive agents (true)', dict(agents=5, down_agents=2, agents_alive='true', expected_agents=3)), ('Get down agents (false)', dict(agents=5, down_agents=2, agents_alive='false', expected_agents=2)), ('Get agents invalid alive filter', dict(agents=5, down_agents=2, agents_alive='invalid', expected_agents=None)), ] def setUp(self): # ensure that the first scenario will execute with nosetests if not hasattr(self, 'agents'): self.__dict__.update(self.scenarios[0][1]) super(TestAgentsDbGetAgents, self).setUp() def test_get_agents(self): hosts = ['host-%s' % i for i in range(self.agents)] self._create_and_save_agents(hosts, constants.AGENT_TYPE_L3, down_agents_count=self.down_agents) if self.agents_alive == 'invalid': self.assertRaises(n_exc.InvalidInput, self.plugin.get_agents, self.context, filters={'alive': [self.agents_alive]}) else: returned_agents = self.plugin.get_agents( self.context, filters={'alive': [self.agents_alive]} if self.agents_alive else None) self.assertEqual(self.expected_agents, len(returned_agents)) if self.agents_alive: alive = (self.agents_alive == 'True' or self.agents_alive == 'true') for agent in returned_agents: self.assertEqual(alive, agent['alive']) class TestAgentExtRpcCallback(TestAgentsDbBase): def setUp(self): super(TestAgentExtRpcCallback, self).setUp() self.callback = agents_db.AgentExtRpcCallback(self.plugin) self.callback.server_versions_rpc = mock.Mock() self.versions_rpc = self.callback.server_versions_rpc self.callback.START_TIME = datetime.datetime(datetime.MINYEAR, 1, 1) self.update_versions = mock.patch( 'neutron.api.rpc.callbacks.version_manager.' 'update_versions').start() self.agent_state = {'agent_state': dict(AGENT_STATUS)} def test_create_or_update_agent_updates_version_manager(self): self.callback.report_state(self.context, agent_state=self.agent_state, time=TEST_TIME) self.update_versions.assert_called_once_with( mock.ANY, TEST_RESOURCE_VERSIONS) def test_create_or_update_agent_updates_other_servers(self): callback = self.callback callback.report_state(self.context, agent_state=self.agent_state, time=TEST_TIME) report_agent_resource_versions = ( self.versions_rpc.report_agent_resource_versions) report_agent_resource_versions.assert_called_once_with( mock.ANY, mock.ANY, mock.ANY, TEST_RESOURCE_VERSIONS) def test_no_version_updates_on_further_state_reports(self): self.test_create_or_update_agent_updates_version_manager() # agents include resource_versions only in the first report after # start so versions should not be updated on the second report second_agent_state = copy.deepcopy(self.agent_state) second_agent_state['agent_state'].pop('resource_versions') self.update_versions.reset_mock() report_agent_resource_versions = ( self.versions_rpc.report_agent_resource_versions) report_agent_resource_versions.reset_mock() self.callback.report_state(self.context, agent_state=second_agent_state, time=TEST_TIME) self.assertFalse(self.update_versions.called) self.assertFalse(report_agent_resource_versions.called) def test_version_updates_on_agent_revival(self): self.test_create_or_update_agent_updates_version_manager() second_agent_state = copy.deepcopy(self.agent_state) second_agent_state['agent_state'].pop('resource_versions') self._take_down_agent() self.update_versions.reset_mock() report_agent_resource_versions = ( self.versions_rpc.report_agent_resource_versions) report_agent_resource_versions.reset_mock() # agent didn't include resource_versions in report but server will # take them from db for the revived agent self.callback.report_state(self.context, agent_state=second_agent_state, time=TEST_TIME) self.update_versions.assert_called_once_with( mock.ANY, TEST_RESOURCE_VERSIONS) report_agent_resource_versions.assert_called_once_with( mock.ANY, mock.ANY, mock.ANY, TEST_RESOURCE_VERSIONS) def _take_down_agent(self): with self.context.session.begin(subtransactions=True): pager = base.Pager(limit=1) agent_objs = agent_obj.Agent.get_objects(self.context, _pager=pager) agent_objs[0].heartbeat_timestamp = ( agent_objs[0].heartbeat_timestamp - datetime.timedelta( hours=1)) agent_objs[0].update() neutron-12.1.1/neutron/tests/unit/db/test_dvr_mac_db.py0000664000175000017500000002302113553660047023210 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation, all rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions as lib_exc from neutron_lib.plugins import directory from neutron_lib.utils import net from neutron.db import dvr_mac_db from neutron.extensions import dvr from neutron.objects import router from neutron.tests import tools from neutron.tests.unit.plugins.ml2 import test_plugin class DVRDbMixinImpl(dvr_mac_db.DVRDbMixin): def __init__(self, notifier): self.notifier = notifier class DvrDbMixinTestCase(test_plugin.Ml2PluginV2TestCase): def setUp(self): super(DvrDbMixinTestCase, self).setUp() self.ctx = context.get_admin_context() self.mixin = DVRDbMixinImpl(mock.Mock()) def _create_dvr_mac_entry(self, host, mac_address): router.DVRMacAddress( self.ctx, host=host, mac_address=mac_address).create() def test__get_dvr_mac_address_by_host(self): entry = router.DVRMacAddress( self.ctx, host='foo_host', mac_address=tools.get_random_EUI()) entry.create() result = self.mixin._get_dvr_mac_address_by_host(self.ctx, 'foo_host') self.assertEqual(entry.to_dict(), result) def test__get_dvr_mac_address_by_host_not_found(self): self.assertRaises(dvr.DVRMacAddressNotFound, self.mixin._get_dvr_mac_address_by_host, self.ctx, 'foo_host') def test__create_dvr_mac_address_success(self): entry = {'host': 'foo_host', 'mac_address': tools.get_random_EUI()} with mock.patch.object(net, 'get_random_mac') as f: f.return_value = entry['mac_address'] expected = self.mixin._create_dvr_mac_address( self.ctx, entry['host']) self.assertEqual(expected, entry) def test__create_dvr_mac_address_retries_exceeded_retry_logic(self): # limit retries so test doesn't take 40 seconds mock.patch('neutron.db.api._retry_db_errors.max_retries', new=2).start() non_unique_mac = tools.get_random_EUI() self._create_dvr_mac_entry('foo_host_1', non_unique_mac) with mock.patch.object(net, 'get_random_mac') as f: f.return_value = non_unique_mac self.assertRaises(lib_exc.HostMacAddressGenerationFailure, self.mixin._create_dvr_mac_address, self.ctx, "foo_host_2") def test_mac_not_cleared_on_agent_delete_event_with_remaining_agents(self): plugin = directory.get_plugin() mac_1 = tools.get_random_EUI() mac_2 = tools.get_random_EUI() self._create_dvr_mac_entry('host_1', mac_1) self._create_dvr_mac_entry('host_2', mac_2) agent1 = {'host': 'host_1', 'id': 'a1'} agent2 = {'host': 'host_1', 'id': 'a2'} with mock.patch.object(plugin, 'get_agents', return_value=[agent2]): with mock.patch.object(plugin, 'notifier') as notifier: registry.notify(resources.AGENT, events.BEFORE_DELETE, self, context=self.ctx, agent=agent1) mac_list = self.mixin.get_dvr_mac_address_list(self.ctx) for mac in mac_list: self.assertIsInstance(mac, dict) self.assertEqual(2, len(mac_list)) self.assertFalse(notifier.dvr_mac_address_update.called) def test_mac_cleared_on_agent_delete_event(self): plugin = directory.get_plugin() mac_1 = tools.get_random_EUI() mac_2 = tools.get_random_EUI() self._create_dvr_mac_entry('host_1', mac_1) self._create_dvr_mac_entry('host_2', mac_2) agent = {'host': 'host_1', 'id': 'a1'} with mock.patch.object(plugin, 'notifier') as notifier: registry.notify(resources.AGENT, events.BEFORE_DELETE, self, context=self.ctx, agent=agent) mac_list = self.mixin.get_dvr_mac_address_list(self.ctx) self.assertEqual(1, len(mac_list)) for mac in mac_list: self.assertIsInstance(mac, dict) self.assertEqual('host_2', mac_list[0]['host']) notifier.dvr_mac_address_update.assert_called_once_with( self.ctx, mac_list) def test_get_dvr_mac_address_list(self): mac_1 = tools.get_random_EUI() mac_2 = tools.get_random_EUI() self._create_dvr_mac_entry('host_1', mac_1) self._create_dvr_mac_entry('host_2', mac_2) mac_list = self.mixin.get_dvr_mac_address_list(self.ctx) self.assertEqual(2, len(mac_list)) for mac in mac_list: self.assertIsInstance(mac, dict) def test_get_dvr_mac_address_by_host_existing_host(self): self._create_dvr_mac_entry('foo_host', tools.get_random_EUI()) with mock.patch.object(self.mixin, '_get_dvr_mac_address_by_host') as f: self.mixin.get_dvr_mac_address_by_host(self.ctx, 'foo_host') self.assertEqual(1, f.call_count) def test_get_dvr_mac_address_by_host_missing_host(self): with mock.patch.object(self.mixin, '_create_dvr_mac_address') as f: self.mixin.get_dvr_mac_address_by_host(self.ctx, 'foo_host') self.assertEqual(1, f.call_count) def test_get_subnet_for_dvr_returns_correct_mac(self): with self.subnet() as subnet,\ self.port(subnet=subnet),\ self.port(subnet=subnet): dvr_subnet = self.mixin.get_subnet_for_dvr(self.ctx, subnet['subnet']['id']) # no gateway port should be found so no info should be returned self.assertEqual({}, dvr_subnet) with self.port( subnet=subnet, fixed_ips=[{'ip_address': subnet['subnet'][ 'gateway_ip']}]) as gw_port: dvr_subnet = self.mixin.get_subnet_for_dvr( self.ctx, subnet['subnet']['id']) self.assertEqual(gw_port['port']['mac_address'], dvr_subnet['gateway_mac']) def test_get_subnet_for_dvr_returns_correct_mac_fixed_ips_passed(self): with self.subnet() as subnet,\ self.port(subnet=subnet, fixed_ips=[{'ip_address': '10.0.0.2'}]),\ self.port(subnet=subnet, fixed_ips=[{'ip_address': '10.0.0.3'}]): fixed_ips = [{'subnet_id': subnet['subnet']['id'], 'ip_address': '10.0.0.4'}] dvr_subnet = self.mixin.get_subnet_for_dvr( self.ctx, subnet['subnet']['id'], fixed_ips) # no gateway port should be found so no info should be returned self.assertEqual({}, dvr_subnet) with self.port( subnet=subnet, fixed_ips=[{'ip_address': '10.0.0.4'}]) as gw_port: dvr_subnet = self.mixin.get_subnet_for_dvr( self.ctx, subnet['subnet']['id'], fixed_ips) self.assertEqual(gw_port['port']['mac_address'], dvr_subnet['gateway_mac']) def test_get_ports_on_host_by_subnet(self): HOST = 'host1' host_arg = {portbindings.HOST_ID: HOST} arg_list = (portbindings.HOST_ID,) with self.subnet() as subnet,\ self.port(subnet=subnet, device_owner=constants.DEVICE_OWNER_COMPUTE_PREFIX, arg_list=arg_list, **host_arg) as compute_port,\ self.port(subnet=subnet, device_owner=constants.DEVICE_OWNER_DHCP, arg_list=arg_list, **host_arg) as dhcp_port,\ self.port(subnet=subnet, device_owner=constants.DEVICE_OWNER_LOADBALANCER, arg_list=arg_list, **host_arg) as lb_port,\ self.port(device_owner=constants.DEVICE_OWNER_COMPUTE_PREFIX, arg_list=arg_list, **host_arg),\ self.port(subnet=subnet, device_owner=constants.DEVICE_OWNER_COMPUTE_PREFIX, arg_list=arg_list, **{portbindings.HOST_ID: 'other'}),\ self.port(subnet=subnet, device_owner=constants.DEVICE_OWNER_NETWORK_PREFIX, arg_list=arg_list, **host_arg): expected_ids = [port['port']['id'] for port in [compute_port, dhcp_port, lb_port]] dvr_ports = self.mixin.get_ports_on_host_by_subnet( self.ctx, HOST, subnet['subnet']['id']) self.assertEqual(len(expected_ids), len(dvr_ports)) self.assertItemsEqual(expected_ids, [port['id'] for port in dvr_ports]) neutron-12.1.1/neutron/tests/unit/db/metering/0000775000175000017500000000000013553660157021335 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/db/metering/__init__.py0000664000175000017500000000000013553660046023431 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/db/metering/test_metering_db.py0000664000175000017500000003647713553660046025243 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib from neutron_lib.api.definitions import metering as metering_apidef from neutron_lib import constants as n_consts from neutron_lib import context from neutron_lib.db import constants as db_const from neutron_lib.plugins import constants from oslo_utils import uuidutils import webob.exc from neutron.api import extensions from neutron.common import config import neutron.extensions from neutron.services.metering import metering_plugin from neutron.tests.unit.db import test_db_base_plugin_v2 DB_METERING_PLUGIN_KLASS = ( "neutron.services.metering." "metering_plugin.MeteringPlugin" ) extensions_path = ':'.join(neutron.extensions.__path__) _long_description_ok = 'x' * (db_const.LONG_DESCRIPTION_FIELD_SIZE) _long_description_ng = 'x' * (db_const.LONG_DESCRIPTION_FIELD_SIZE + 1) _fake_uuid = uuidutils.generate_uuid class MeteringPluginDbTestCaseMixin(object): def _create_metering_label(self, fmt, name, description, **kwargs): data = {'metering_label': {'name': name, 'tenant_id': kwargs.get('tenant_id', 'test-tenant'), 'shared': kwargs.get('shared', False), 'description': description}} req = self.new_create_request('metering-labels', data, fmt) if kwargs.get('set_context') and 'tenant_id' in kwargs: # create a specific auth context for this request req.environ['neutron.context'] = ( context.Context('', kwargs['tenant_id'], is_admin=kwargs.get('is_admin', True))) return req.get_response(self.ext_api) def _make_metering_label(self, fmt, name, description, **kwargs): res = self._create_metering_label(fmt, name, description, **kwargs) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) return self.deserialize(fmt, res) def _create_metering_label_rule(self, fmt, metering_label_id, direction, remote_ip_prefix, excluded, **kwargs): data = {'metering_label_rule': {'metering_label_id': metering_label_id, 'tenant_id': kwargs.get('tenant_id', 'test-tenant'), 'direction': direction, 'excluded': excluded, 'remote_ip_prefix': remote_ip_prefix}} req = self.new_create_request('metering-label-rules', data, fmt) if kwargs.get('set_context') and 'tenant_id' in kwargs: # create a specific auth context for this request req.environ['neutron.context'] = ( context.Context('', kwargs['tenant_id'])) return req.get_response(self.ext_api) def _make_metering_label_rule(self, fmt, metering_label_id, direction, remote_ip_prefix, excluded, **kwargs): res = self._create_metering_label_rule(fmt, metering_label_id, direction, remote_ip_prefix, excluded, **kwargs) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) return self.deserialize(fmt, res) @contextlib.contextmanager def metering_label(self, name='label', description='desc', fmt=None, **kwargs): if not fmt: fmt = self.fmt metering_label = self._make_metering_label(fmt, name, description, **kwargs) yield metering_label @contextlib.contextmanager def metering_label_rule(self, metering_label_id=None, direction='ingress', remote_ip_prefix='10.0.0.0/24', excluded='false', fmt=None): if not fmt: fmt = self.fmt metering_label_rule = self._make_metering_label_rule(fmt, metering_label_id, direction, remote_ip_prefix, excluded) yield metering_label_rule class MeteringPluginDbTestCase( test_db_base_plugin_v2.NeutronDbPluginV2TestCase, MeteringPluginDbTestCaseMixin): fmt = 'json' resource_prefix_map = dict( (k.replace('_', '-'), "/metering") for k in metering_apidef.RESOURCE_ATTRIBUTE_MAP.keys() ) def setUp(self, plugin=None): service_plugins = {'metering_plugin_name': DB_METERING_PLUGIN_KLASS} super(MeteringPluginDbTestCase, self).setUp( plugin=plugin, service_plugins=service_plugins ) self.plugin = metering_plugin.MeteringPlugin() ext_mgr = extensions.PluginAwareExtensionManager( extensions_path, {constants.METERING: self.plugin} ) app = config.load_paste_app('extensions_test_app') self.ext_api = extensions.ExtensionMiddleware(app, ext_mgr=ext_mgr) class TestMetering(MeteringPluginDbTestCase): def test_create_metering_label(self): name = 'my label' description = 'my metering label' keys = [('name', name,), ('description', description)] with self.metering_label(name, description) as metering_label: for k, v, in keys: self.assertEqual(metering_label['metering_label'][k], v) def test_create_metering_label_shared(self): name = 'my label' description = 'my metering label' shared = True keys = [('name', name,), ('description', description), ('shared', shared)] with self.metering_label(name, description, shared=shared) as metering_label: for k, v, in keys: self.assertEqual(metering_label['metering_label'][k], v) def test_create_metering_label_with_max_description_length(self): res = self._create_metering_label(self.fmt, 'my label', _long_description_ok) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) def test_create_metering_label_with_too_long_description(self): res = self._create_metering_label(self.fmt, 'my label', _long_description_ng) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_update_metering_label(self): name = 'my label' description = 'my metering label' data = {'metering_label': {}} with self.metering_label(name, description) as metering_label: metering_label_id = metering_label['metering_label']['id'] self._update('metering-labels', metering_label_id, data, webob.exc.HTTPNotImplemented.code) def test_delete_metering_label(self): name = 'my label' description = 'my metering label' with self.metering_label(name, description) as metering_label: metering_label_id = metering_label['metering_label']['id'] self._delete('metering-labels', metering_label_id, 204) def test_list_metering_label(self): name = 'my label' description = 'my metering label' with self.metering_label(name, description) as v1,\ self.metering_label(name, description) as v2: metering_label = (v1, v2) self._test_list_resources('metering-label', metering_label) def test_create_metering_label_rule(self): name = 'my label' description = 'my metering label' with self.metering_label(name, description) as metering_label: metering_label_id = metering_label['metering_label']['id'] direction = 'egress' remote_ip_prefix = '192.168.0.0/24' excluded = True keys = [('metering_label_id', metering_label_id), ('direction', direction), ('excluded', excluded), ('remote_ip_prefix', remote_ip_prefix)] with self.metering_label_rule(metering_label_id, direction, remote_ip_prefix, excluded) as label_rule: for k, v, in keys: self.assertEqual(label_rule['metering_label_rule'][k], v) def test_create_metering_label_rule_with_non_existent_label(self): direction = 'egress' remote_ip_prefix = '192.168.0.0/24' excluded = True res = self._create_metering_label_rule(self.fmt, _fake_uuid(), direction, remote_ip_prefix, excluded) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_update_metering_label_rule(self): name = 'my label' description = 'my metering label' direction = 'egress' remote_ip_prefix = '192.168.0.0/24' data = {'metering_label_rule': {}} with self.metering_label(name, description) as metering_label, \ self.metering_label_rule( metering_label['metering_label']['id'], direction, remote_ip_prefix) as label_rule: rule_id = label_rule['metering_label_rule']['id'] self._update('metering-label-rules', rule_id, data, webob.exc.HTTPNotImplemented.code) def test_delete_metering_label_rule(self): name = 'my label' description = 'my metering label' with self.metering_label(name, description) as metering_label: metering_label_id = metering_label['metering_label']['id'] direction = 'egress' remote_ip_prefix = '192.168.0.0/24' excluded = True with self.metering_label_rule(metering_label_id, direction, remote_ip_prefix, excluded) as label_rule: rule_id = label_rule['metering_label_rule']['id'] self._delete('metering-label-rules', rule_id, 204) def test_list_metering_label_rule(self): name = 'my label' description = 'my metering label' with self.metering_label(name, description) as metering_label: metering_label_id = metering_label['metering_label']['id'] direction = 'egress' remote_ip_prefix = '192.168.0.0/24' excluded = True with self.metering_label_rule(metering_label_id, direction, remote_ip_prefix, excluded) as v1,\ self.metering_label_rule(metering_label_id, 'ingress', remote_ip_prefix, excluded) as v2: metering_label_rule = (v1, v2) self._test_list_resources('metering-label-rule', metering_label_rule) def test_create_metering_label_rules(self): name = 'my label' description = 'my metering label' with self.metering_label(name, description) as metering_label: metering_label_id = metering_label['metering_label']['id'] direction = 'egress' remote_ip_prefix = '192.168.0.0/24' excluded = True with self.metering_label_rule(metering_label_id, direction, remote_ip_prefix, excluded) as v1,\ self.metering_label_rule(metering_label_id, direction, n_consts.IPv4_ANY, False) as v2: metering_label_rule = (v1, v2) self._test_list_resources('metering-label-rule', metering_label_rule) def test_create_overlap_metering_label_rules(self): name = 'my label' description = 'my metering label' with self.metering_label(name, description) as metering_label: metering_label_id = metering_label['metering_label']['id'] direction = 'egress' remote_ip_prefix1 = '192.168.0.0/24' remote_ip_prefix2 = '192.168.0.0/16' excluded = True with self.metering_label_rule(metering_label_id, direction, remote_ip_prefix1, excluded): res = self._create_metering_label_rule(self.fmt, metering_label_id, direction, remote_ip_prefix2, excluded) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_metering_label_rule_two_labels(self): name1 = 'my label 1' name2 = 'my label 2' description = 'my metering label' with self.metering_label(name1, description) as metering_label1: metering_label_id1 = metering_label1['metering_label']['id'] with self.metering_label(name2, description) as metering_label2: metering_label_id2 = metering_label2['metering_label']['id'] direction = 'egress' remote_ip_prefix = '192.168.0.0/24' excluded = True with self.metering_label_rule(metering_label_id1, direction, remote_ip_prefix, excluded) as v1,\ self.metering_label_rule(metering_label_id2, direction, remote_ip_prefix, excluded) as v2: metering_label_rule = (v1, v2) self._test_list_resources('metering-label-rule', metering_label_rule) neutron-12.1.1/neutron/tests/unit/db/test_provisioning_blocks.py0000664000175000017500000001534013553660047025220 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import context as n_ctx import testtools from neutron.db import api as db_api from neutron.db import models_v2 from neutron.db import provisioning_blocks as pb from neutron.objects import network as net_obj from neutron.tests.unit import testlib_api CORE_PLUGIN = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' class TestStatusBarriers(testlib_api.SqlTestCase): def setUp(self): super(TestStatusBarriers, self).setUp() self.setup_coreplugin(CORE_PLUGIN) self.ctx = n_ctx.get_admin_context() self.provisioned = mock.Mock() self.port = self._make_port() registry.subscribe(self.provisioned, resources.PORT, pb.PROVISIONING_COMPLETE) def _make_net(self): network_obj = net_obj.Network(self.ctx, name='net_net', status='ACTIVE', project_id='1', admin_state_up=True) network_obj.create() return network_obj def _make_port(self): net = self._make_net() with db_api.context_manager.writer.using(self.ctx): port = models_v2.Port(network_id=net.id, mac_address='1', tenant_id='1', admin_state_up=True, status='DOWN', device_id='2', device_owner='3') self.ctx.session.add(port) return port def test_no_callback_on_missing_object(self): pb.provisioning_complete(self.ctx, 'someid', resources.PORT, 'entity') self.assertFalse(self.provisioned.called) def test_provisioned_with_no_components(self): pb.provisioning_complete(self.ctx, self.port.id, resources.PORT, 'entity') self.assertTrue(self.provisioned.called) def test_provisioned_after_component_finishes(self): pb.add_provisioning_component(self.ctx, self.port.id, resources.PORT, 'entity') pb.provisioning_complete(self.ctx, self.port.id, resources.PORT, 'entity') self.assertTrue(self.provisioned.called) def test_not_provisioned_until_final_component_complete(self): pb.add_provisioning_component(self.ctx, self.port.id, resources.PORT, 'entity1') pb.add_provisioning_component(self.ctx, self.port.id, resources.PORT, 'entity2') pb.provisioning_complete(self.ctx, self.port.id, resources.PORT, 'entity1') self.assertFalse(self.provisioned.called) pb.provisioning_complete(self.ctx, self.port.id, resources.PORT, 'entity2') self.assertTrue(self.provisioned.called) def test_provisioning_of_correct_item(self): port2 = self._make_port() pb.add_provisioning_component(self.ctx, self.port.id, resources.PORT, 'entity1') pb.provisioning_complete(self.ctx, port2.id, resources.PORT, 'entity1') self.provisioned.assert_called_once_with( resources.PORT, pb.PROVISIONING_COMPLETE, mock.ANY, context=self.ctx, object_id=port2.id) def test_not_provisioned_when_wrong_component_reports(self): pb.add_provisioning_component(self.ctx, self.port.id, resources.PORT, 'entity1') pb.provisioning_complete(self.ctx, self.port.id, resources.PORT, 'entity2') self.assertFalse(self.provisioned.called) def test_is_object_blocked(self): pb.add_provisioning_component(self.ctx, self.port.id, resources.PORT, 'e1') self.assertTrue(pb.is_object_blocked(self.ctx, self.port.id, resources.PORT)) self.assertFalse(pb.is_object_blocked(self.ctx, 'xyz', resources.PORT)) pb.provisioning_complete(self.ctx, self.port.id, resources.PORT, 'e1') self.assertFalse(pb.is_object_blocked(self.ctx, self.port.id, resources.PORT)) def test_remove_provisioning_component(self): pb.add_provisioning_component(self.ctx, self.port.id, resources.PORT, 'e1') pb.add_provisioning_component(self.ctx, self.port.id, resources.PORT, 'e2') self.assertTrue(pb.remove_provisioning_component( self.ctx, self.port.id, resources.PORT, 'e1')) self.assertFalse(self.provisioned.called) pb.provisioning_complete(self.ctx, self.port.id, resources.PORT, 'other') self.assertFalse(self.provisioned.called) pb.provisioning_complete(self.ctx, self.port.id, resources.PORT, 'e2') self.assertTrue(self.provisioned.called) def test_adding_component_idempotent(self): for i in range(5): pb.add_provisioning_component(self.ctx, self.port.id, resources.PORT, 'entity1') pb.provisioning_complete(self.ctx, self.port.id, resources.PORT, 'entity1') self.assertTrue(self.provisioned.called) def test_adding_component_for_new_resource_type(self): provisioned = mock.Mock() registry.subscribe(provisioned, 'NETWORK', pb.PROVISIONING_COMPLETE) net = self._make_net() # expect failed because the model was not registered for the type with testtools.ExpectedException(RuntimeError): pb.add_provisioning_component(self.ctx, net.id, 'NETWORK', 'ent') pb.add_model_for_resource('NETWORK', models_v2.Network) pb.add_provisioning_component(self.ctx, net.id, 'NETWORK', 'ent') pb.provisioning_complete(self.ctx, net.id, 'NETWORK', 'ent') self.assertTrue(provisioned.called) neutron-12.1.1/neutron/tests/unit/db/test_portsecurity_db.py0000664000175000017500000000356613553660047024365 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.plugins import constants from neutron_lib.plugins import directory from neutron.db import portsecurity_db as pd from neutron.db import portsecurity_db_common as pdc from neutron.tests import base common = pdc.PortSecurityDbCommon class FakePlugin(pd.PortSecurityDbMixin): supported_extension_aliases = ['port-security'] class PortSecurityDbMixinTestCase(base.BaseTestCase): def setUp(self): super(PortSecurityDbMixinTestCase, self).setUp() self.plugin = FakePlugin() directory.add_plugin(constants.CORE, self.plugin) @mock.patch.object(common, '_extend_port_security_dict') def test__extend_port_security_dict_relies_on_common(self, extend): response = mock.Mock() dbdata = mock.Mock() self.plugin._extend_port_security_dict(response, dbdata) extend.assert_called_once_with(response, dbdata) @mock.patch.object(common, '_extend_port_security_dict') def test__extend_port_security_dict_ignored_if_extension_disabled(self, extend): response = mock.Mock() dbdata = mock.Mock() self.plugin.supported_extension_aliases = [] self.plugin._extend_port_security_dict(response, dbdata) self.assertFalse(extend.called) neutron-12.1.1/neutron/tests/unit/db/test_db_base_plugin_v2.py0000664000175000017500000123561713553660047024515 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib import copy import functools import itertools import eventlet import mock import netaddr from neutron_lib.callbacks import exceptions from neutron_lib.callbacks import registry from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions as lib_exc from neutron_lib import fixture from neutron_lib.plugins import directory from neutron_lib.utils import helpers from neutron_lib.utils import net from oslo_concurrency import lockutils from oslo_config import cfg from oslo_utils import importutils from oslo_utils import netutils from oslo_utils import uuidutils from sqlalchemy import orm import testtools from testtools import matchers import webob.exc import neutron from neutron.api import api_common from neutron.api import extensions from neutron.api.v2 import router from neutron.common import exceptions as n_exc from neutron.common import ipv6_utils from neutron.common import test_lib from neutron.common import utils from neutron.db import api as db_api from neutron.db import db_base_plugin_common from neutron.db import ipam_backend_mixin from neutron.db.models import l3 as l3_models from neutron.db.models import securitygroup as sg_models from neutron.db import models_v2 from neutron.db import rbac_db_models from neutron.db import standard_attr from neutron.ipam.drivers.neutrondb_ipam import driver as ipam_driver from neutron.ipam import exceptions as ipam_exc from neutron.objects import router as l3_obj from neutron import policy from neutron.tests import base from neutron.tests import tools from neutron.tests.unit.api import test_extensions from neutron.tests.unit import testlib_api DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' DEVICE_OWNER_COMPUTE = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' DEVICE_OWNER_NOT_COMPUTE = constants.DEVICE_OWNER_DHCP TEST_TENANT_ID = '46f70361-ba71-4bd0-9769-3573fd227c4b' def optional_ctx(obj, fallback, **kwargs): if not obj: return fallback(**kwargs) @contextlib.contextmanager def context_wrapper(): yield obj return context_wrapper() def _fake_get_pagination_helper(self, request): return api_common.PaginationEmulatedHelper(request, self._primary_key) def _fake_get_sorting_helper(self, request): return api_common.SortingEmulatedHelper(request, self._attr_info) # TODO(banix): Move the following method to ML2 db test module when ML2 # mechanism driver unit tests are corrected to use Ml2PluginV2TestCase # instead of directly using NeutronDbPluginV2TestCase def _get_create_db_method(resource): ml2_method = '_create_%s_db' % resource if hasattr(directory.get_plugin(), ml2_method): return ml2_method else: return 'create_%s' % resource class NeutronDbPluginV2TestCase(testlib_api.WebTestCase): fmt = 'json' resource_prefix_map = {} block_dhcp_notifier = True def setUp(self, plugin=None, service_plugins=None, ext_mgr=None): super(NeutronDbPluginV2TestCase, self).setUp() cfg.CONF.set_override('notify_nova_on_port_status_changes', False) cfg.CONF.set_override('allow_overlapping_ips', True) # Make sure at each test according extensions for the plugin is loaded extensions.PluginAwareExtensionManager._instance = None # Save the attributes map in case the plugin will alter it # loading extensions self.useFixture(fixture.APIDefinitionFixture()) self._tenant_id = TEST_TENANT_ID if not plugin: plugin = DB_PLUGIN_KLASS if self.block_dhcp_notifier: mock.patch('neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.' 'DhcpAgentNotifyAPI').start() # Update the plugin self.setup_coreplugin(plugin, load_plugins=False) cfg.CONF.set_override( 'service_plugins', [test_lib.test_config.get(key, default) for key, default in (service_plugins or {}).items()] ) cfg.CONF.set_override('base_mac', "12:34:56:78:00:00") cfg.CONF.set_override('max_dns_nameservers', 2) cfg.CONF.set_override('max_subnet_host_routes', 2) self.api = router.APIRouter() # Set the default status self.net_create_status = 'ACTIVE' self.port_create_status = 'ACTIVE' def _is_native_bulk_supported(): plugin_obj = directory.get_plugin() native_bulk_attr_name = ("_%s__native_bulk_support" % plugin_obj.__class__.__name__) return getattr(plugin_obj, native_bulk_attr_name, False) self._skip_native_bulk = not _is_native_bulk_supported() def _is_native_pagination_support(): native_pagination_attr_name = ( "_%s__native_pagination_support" % directory.get_plugin().__class__.__name__) return getattr(directory.get_plugin(), native_pagination_attr_name, False) self._skip_native_pagination = not _is_native_pagination_support() def _is_native_sorting_support(): native_sorting_attr_name = ( "_%s__native_sorting_support" % directory.get_plugin().__class__.__name__) return getattr(directory.get_plugin(), native_sorting_attr_name, False) self.plugin = directory.get_plugin() self._skip_native_sorting = not _is_native_sorting_support() if ext_mgr: self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) # NOTE(amotoki): policy._ENFORCER is initialized in # neutron.tests.base.BaseTestCase.setUp() but this is too early # and neutron.policy.FieldCheck conv_func does not work # because extended resources are not populated to # attributes.RESOURCES yet. # Thus we need to refresh the default policy rules after loading # extensions. Especially it is important to re-instantiate # DefaultRule() under neutron.conf.policies. To do this, # we need to reload the default policy modules. policy.reset() policy.init() def setup_config(self): # Create the default configurations args = ['--config-file', base.etcdir('neutron.conf')] # If test_config specifies some config-file, use it, as well for config_file in test_lib.test_config.get('config_files', []): args.extend(['--config-file', config_file]) super(NeutronDbPluginV2TestCase, self).setup_config(args=args) def _req(self, method, resource, data=None, fmt=None, id=None, params=None, action=None, subresource=None, sub_id=None, context=None, headers=None): fmt = fmt or self.fmt path = '/%s.%s' % ( '/'.join(p for p in (resource, id, subresource, sub_id, action) if p), fmt ) prefix = self.resource_prefix_map.get(resource) if prefix: path = prefix + path content_type = 'application/%s' % fmt body = None if data is not None: # empty dict is valid body = self.serialize(data) return testlib_api.create_request(path, body, content_type, method, query_string=params, context=context, headers=headers) def new_create_request(self, resource, data, fmt=None, id=None, subresource=None, context=None): return self._req('POST', resource, data, fmt, id=id, subresource=subresource, context=context) def new_list_request(self, resource, fmt=None, params=None, subresource=None): return self._req( 'GET', resource, None, fmt, params=params, subresource=subresource ) def new_show_request(self, resource, id, fmt=None, subresource=None, fields=None, sub_id=None): if fields: params = "&".join(["fields=%s" % x for x in fields]) else: params = None return self._req('GET', resource, None, fmt, id=id, params=params, subresource=subresource, sub_id=sub_id) def new_delete_request(self, resource, id, fmt=None, subresource=None, sub_id=None, data=None, headers=None): return self._req( 'DELETE', resource, data, fmt, id=id, subresource=subresource, sub_id=sub_id, headers=headers ) def new_update_request(self, resource, data, id, fmt=None, subresource=None, context=None, sub_id=None, headers=None): return self._req( 'PUT', resource, data, fmt, id=id, subresource=subresource, sub_id=sub_id, context=context, headers=headers ) def new_action_request(self, resource, data, id, action, fmt=None, subresource=None, sub_id=None): return self._req( 'PUT', resource, data, fmt, id=id, action=action, subresource=subresource, sub_id=sub_id ) def deserialize(self, content_type, response): ctype = 'application/%s' % content_type data = self._deserializers[ctype].deserialize(response.body)['body'] return data def _create_bulk_from_list(self, fmt, resource, objects, **kwargs): """Creates a bulk request from a list of objects.""" collection = "%ss" % resource req_data = {collection: objects} req = self.new_create_request(collection, req_data, fmt) if ('set_context' in kwargs and kwargs['set_context'] is True and 'tenant_id' in kwargs): # create a specific auth context for this request req.environ['neutron.context'] = context.Context( '', kwargs['tenant_id']) elif 'context' in kwargs: req.environ['neutron.context'] = kwargs['context'] return req.get_response(self.api) def _create_bulk(self, fmt, number, resource, data, name='test', **kwargs): """Creates a bulk request for any kind of resource.""" objects = [] collection = "%ss" % resource for i in range(number): obj = copy.deepcopy(data) obj[resource]['name'] = "%s_%s" % (name, i) if 'override' in kwargs and i in kwargs['override']: obj[resource].update(kwargs['override'][i]) objects.append(obj) req_data = {collection: objects} req = self.new_create_request(collection, req_data, fmt) if ('set_context' in kwargs and kwargs['set_context'] is True and 'tenant_id' in kwargs): # create a specific auth context for this request req.environ['neutron.context'] = context.Context( '', kwargs['tenant_id']) elif 'context' in kwargs: req.environ['neutron.context'] = kwargs['context'] return req.get_response(self.api) def _create_network(self, fmt, name, admin_state_up, arg_list=None, set_context=False, tenant_id=None, **kwargs): tenant_id = tenant_id or self._tenant_id data = {'network': {'name': name, 'admin_state_up': admin_state_up, 'tenant_id': tenant_id}} for arg in (('admin_state_up', 'tenant_id', 'shared', 'vlan_transparent', 'availability_zone_hints') + (arg_list or ())): # Arg must be present if arg in kwargs: data['network'][arg] = kwargs[arg] network_req = self.new_create_request('networks', data, fmt) if set_context and tenant_id: # create a specific auth context for this request network_req.environ['neutron.context'] = context.Context( '', tenant_id) return network_req.get_response(self.api) def _create_network_bulk(self, fmt, number, name, admin_state_up, **kwargs): base_data = {'network': {'admin_state_up': admin_state_up, 'tenant_id': self._tenant_id}} return self._create_bulk(fmt, number, 'network', base_data, **kwargs) def _create_subnet(self, fmt, net_id, cidr, expected_res_status=None, **kwargs): data = {'subnet': {'network_id': net_id, 'ip_version': 4, 'tenant_id': self._tenant_id}} if cidr: data['subnet']['cidr'] = cidr for arg in ('ip_version', 'tenant_id', 'subnetpool_id', 'prefixlen', 'enable_dhcp', 'allocation_pools', 'segment_id', 'dns_nameservers', 'host_routes', 'shared', 'ipv6_ra_mode', 'ipv6_address_mode', 'service_types'): # Arg must be present and not null (but can be false) if kwargs.get(arg) is not None: data['subnet'][arg] = kwargs[arg] if ('gateway_ip' in kwargs and kwargs['gateway_ip'] is not constants.ATTR_NOT_SPECIFIED): data['subnet']['gateway_ip'] = kwargs['gateway_ip'] subnet_req = self.new_create_request('subnets', data, fmt) if (kwargs.get('set_context') and 'tenant_id' in kwargs): # create a specific auth context for this request subnet_req.environ['neutron.context'] = context.Context( '', kwargs['tenant_id']) subnet_res = subnet_req.get_response(self.api) if expected_res_status: self.assertEqual(expected_res_status, subnet_res.status_int) return subnet_res def _create_subnet_bulk(self, fmt, number, net_id, name, ip_version=4, **kwargs): base_data = {'subnet': {'network_id': net_id, 'ip_version': ip_version, 'tenant_id': self._tenant_id}} if 'ipv6_mode' in kwargs: base_data['subnet']['ipv6_ra_mode'] = kwargs['ipv6_mode'] base_data['subnet']['ipv6_address_mode'] = kwargs['ipv6_mode'] # auto-generate cidrs as they should not overlap base_cidr = "10.0.%s.0/24" if ip_version == constants.IP_VERSION_6: base_cidr = "fd%s::/64" overrides = dict((k, v) for (k, v) in zip(range(number), [{'cidr': base_cidr % num} for num in range(number)])) kwargs.update({'override': overrides}) return self._create_bulk(fmt, number, 'subnet', base_data, **kwargs) def _create_subnetpool(self, fmt, prefixes, expected_res_status=None, admin=False, **kwargs): subnetpool = {'subnetpool': {'prefixes': prefixes}} for k, v in kwargs.items(): subnetpool['subnetpool'][k] = str(v) api = self._api_for_resource('subnetpools') subnetpools_req = self.new_create_request('subnetpools', subnetpool, fmt) if not admin: neutron_context = context.Context('', kwargs['tenant_id']) subnetpools_req.environ['neutron.context'] = neutron_context subnetpool_res = subnetpools_req.get_response(api) if expected_res_status: self.assertEqual(expected_res_status, subnetpool_res.status_int) return subnetpool_res def _create_port(self, fmt, net_id, expected_res_status=None, arg_list=None, set_context=False, tenant_id=None, **kwargs): tenant_id = tenant_id or self._tenant_id data = {'port': {'network_id': net_id, 'tenant_id': tenant_id}} for arg in (('admin_state_up', 'device_id', 'mac_address', 'name', 'fixed_ips', 'tenant_id', 'device_owner', 'security_groups') + (arg_list or ())): # Arg must be present if arg in kwargs: data['port'][arg] = kwargs[arg] # create a dhcp port device id if one hasn't been supplied if ('device_owner' in kwargs and kwargs['device_owner'] == constants.DEVICE_OWNER_DHCP and 'host' in kwargs and 'device_id' not in kwargs): device_id = utils.get_dhcp_agent_device_id(net_id, kwargs['host']) data['port']['device_id'] = device_id port_req = self.new_create_request('ports', data, fmt) if set_context and tenant_id: # create a specific auth context for this request port_req.environ['neutron.context'] = context.Context( '', tenant_id) port_res = port_req.get_response(self.api) if expected_res_status: self.assertEqual(expected_res_status, port_res.status_int) return port_res def _list_ports(self, fmt, expected_res_status=None, net_id=None, **kwargs): query_params = [] if net_id: query_params.append("network_id=%s" % net_id) if kwargs.get('device_owner'): query_params.append("device_owner=%s" % kwargs.get('device_owner')) port_req = self.new_list_request('ports', fmt, '&'.join(query_params)) if ('set_context' in kwargs and kwargs['set_context'] is True and 'tenant_id' in kwargs): # create a specific auth context for this request port_req.environ['neutron.context'] = context.Context( '', kwargs['tenant_id']) port_res = port_req.get_response(self.api) if expected_res_status: self.assertEqual(expected_res_status, port_res.status_int) return port_res def _create_port_bulk(self, fmt, number, net_id, name, admin_state_up, **kwargs): base_data = {'port': {'network_id': net_id, 'admin_state_up': admin_state_up, 'tenant_id': self._tenant_id}} return self._create_bulk(fmt, number, 'port', base_data, **kwargs) def _make_network(self, fmt, name, admin_state_up, **kwargs): res = self._create_network(fmt, name, admin_state_up, **kwargs) # TODO(salvatore-orlando): do exception handling in this test module # in a uniform way (we do it differently for ports, subnets, and nets # Things can go wrong - raise HTTP exc with res code only # so it can be caught by unit tests if res.status_int >= webob.exc.HTTPClientError.code: raise webob.exc.HTTPClientError(code=res.status_int) return self.deserialize(fmt, res) def _make_subnet(self, fmt, network, gateway, cidr, subnetpool_id=None, allocation_pools=None, ip_version=4, enable_dhcp=True, dns_nameservers=None, host_routes=None, shared=None, ipv6_ra_mode=None, ipv6_address_mode=None, tenant_id=None, set_context=False, segment_id=None): res = self._create_subnet(fmt, net_id=network['network']['id'], cidr=cidr, subnetpool_id=subnetpool_id, segment_id=segment_id, gateway_ip=gateway, tenant_id=(tenant_id or network['network']['tenant_id']), allocation_pools=allocation_pools, ip_version=ip_version, enable_dhcp=enable_dhcp, dns_nameservers=dns_nameservers, host_routes=host_routes, shared=shared, ipv6_ra_mode=ipv6_ra_mode, ipv6_address_mode=ipv6_address_mode, set_context=set_context) # Things can go wrong - raise HTTP exc with res code only # so it can be caught by unit tests if res.status_int >= webob.exc.HTTPClientError.code: raise webob.exc.HTTPClientError(code=res.status_int) return self.deserialize(fmt, res) def _make_v6_subnet(self, network, ra_addr_mode, ipv6_pd=False): cidr = 'fe80::/64' gateway = 'fe80::1' subnetpool_id = None if ipv6_pd: cidr = None gateway = None subnetpool_id = constants.IPV6_PD_POOL_ID cfg.CONF.set_override('ipv6_pd_enabled', True) return (self._make_subnet(self.fmt, network, gateway=gateway, subnetpool_id=subnetpool_id, cidr=cidr, ip_version=6, ipv6_ra_mode=ra_addr_mode, ipv6_address_mode=ra_addr_mode)) def _make_subnetpool(self, fmt, prefixes, admin=False, **kwargs): res = self._create_subnetpool(fmt, prefixes, None, admin, **kwargs) # Things can go wrong - raise HTTP exc with res code only # so it can be caught by unit tests if res.status_int >= webob.exc.HTTPClientError.code: raise webob.exc.HTTPClientError(code=res.status_int) return self.deserialize(fmt, res) def _make_port(self, fmt, net_id, expected_res_status=None, **kwargs): res = self._create_port(fmt, net_id, expected_res_status, **kwargs) # Things can go wrong - raise HTTP exc with res code only # so it can be caught by unit tests if res.status_int >= webob.exc.HTTPClientError.code: raise webob.exc.HTTPClientError(code=res.status_int) return self.deserialize(fmt, res) def _api_for_resource(self, resource): if resource in ['networks', 'subnets', 'ports', 'subnetpools']: return self.api else: return self.ext_api def _delete(self, collection, id, expected_code=webob.exc.HTTPNoContent.code, neutron_context=None, headers=None): req = self.new_delete_request(collection, id, headers=headers) if neutron_context: # create a specific auth context for this request req.environ['neutron.context'] = neutron_context res = req.get_response(self._api_for_resource(collection)) self.assertEqual(expected_code, res.status_int) def _show_response(self, resource, id, neutron_context=None): req = self.new_show_request(resource, id) if neutron_context: # create a specific auth context for this request req.environ['neutron.context'] = neutron_context return req.get_response(self._api_for_resource(resource)) def _show(self, resource, id, expected_code=webob.exc.HTTPOk.code, neutron_context=None): res = self._show_response(resource, id, neutron_context=neutron_context) self.assertEqual(expected_code, res.status_int) return self.deserialize(self.fmt, res) def _update(self, resource, id, new_data, expected_code=webob.exc.HTTPOk.code, neutron_context=None, headers=None): req = self.new_update_request(resource, new_data, id, headers=headers) if neutron_context: # create a specific auth context for this request req.environ['neutron.context'] = neutron_context res = req.get_response(self._api_for_resource(resource)) self.assertEqual(expected_code, res.status_int) return self.deserialize(self.fmt, res) def _list(self, resource, fmt=None, neutron_context=None, query_params=None): fmt = fmt or self.fmt req = self.new_list_request(resource, fmt, query_params) if neutron_context: req.environ['neutron.context'] = neutron_context res = req.get_response(self._api_for_resource(resource)) self.assertEqual(webob.exc.HTTPOk.code, res.status_int) return self.deserialize(fmt, res) def _fail_second_call(self, patched_plugin, orig, *args, **kwargs): """Invoked by test cases for injecting failures in plugin.""" def second_call(*args, **kwargs): raise lib_exc.NeutronException() patched_plugin.side_effect = second_call return orig(*args, **kwargs) def _validate_behavior_on_bulk_failure( self, res, collection, errcode=webob.exc.HTTPClientError.code): self.assertEqual(errcode, res.status_int) req = self.new_list_request(collection) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, res.status_int) items = self.deserialize(self.fmt, res) self.assertEqual(0, len(items[collection])) def _validate_behavior_on_bulk_success(self, res, collection, names=['test_0', 'test_1']): self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) items = self.deserialize(self.fmt, res)[collection] self.assertEqual(len(items), 2) self.assertEqual(items[0]['name'], 'test_0') self.assertEqual(items[1]['name'], 'test_1') def _test_list_resources(self, resource, items, neutron_context=None, query_params=None): res = self._list('%ss' % resource, neutron_context=neutron_context, query_params=query_params) resource = resource.replace('-', '_') self.assertItemsEqual([i['id'] for i in res['%ss' % resource]], [i[resource]['id'] for i in items]) @contextlib.contextmanager def network(self, name='net1', admin_state_up=True, fmt=None, **kwargs): network = self._make_network(fmt or self.fmt, name, admin_state_up, **kwargs) yield network @contextlib.contextmanager def subnet(self, network=None, gateway_ip=constants.ATTR_NOT_SPECIFIED, cidr='10.0.0.0/24', subnetpool_id=None, segment_id=None, fmt=None, ip_version=4, allocation_pools=None, enable_dhcp=True, dns_nameservers=None, host_routes=None, shared=None, ipv6_ra_mode=None, ipv6_address_mode=None, tenant_id=None, service_types=None, set_context=False): cidr = netaddr.IPNetwork(cidr) if cidr else None if (gateway_ip is not None and gateway_ip != constants.ATTR_NOT_SPECIFIED): gateway_ip = netaddr.IPAddress(gateway_ip) with optional_ctx(network, self.network, set_context=set_context, tenant_id=tenant_id) as network_to_use: subnet = self._make_subnet(fmt or self.fmt, network_to_use, gateway_ip, cidr, subnetpool_id, allocation_pools, ip_version, enable_dhcp, dns_nameservers, host_routes, segment_id=segment_id, shared=shared, ipv6_ra_mode=ipv6_ra_mode, ipv6_address_mode=ipv6_address_mode, tenant_id=tenant_id, set_context=set_context) yield subnet @contextlib.contextmanager def subnetpool(self, prefixes, admin=False, **kwargs): subnetpool = self._make_subnetpool(self.fmt, prefixes, admin, **kwargs) yield subnetpool @contextlib.contextmanager def port(self, subnet=None, fmt=None, set_context=False, tenant_id=None, **kwargs): with optional_ctx( subnet, self.subnet, set_context=set_context, tenant_id=tenant_id) as subnet_to_use: net_id = subnet_to_use['subnet']['network_id'] port = self._make_port( fmt or self.fmt, net_id, set_context=set_context, tenant_id=tenant_id, **kwargs) yield port def _test_list_with_sort(self, resource, items, sorts, resources=None, query_params=''): query_str = query_params for key, direction in sorts: query_str = query_str + "&sort_key=%s&sort_dir=%s" % (key, direction) if not resources: resources = '%ss' % resource req = self.new_list_request(resources, params=query_str) api = self._api_for_resource(resources) res = self.deserialize(self.fmt, req.get_response(api)) resource = resource.replace('-', '_') resources = resources.replace('-', '_') expected_res = [item[resource]['id'] for item in items] self.assertEqual(expected_res, [n['id'] for n in res[resources]]) def _test_list_with_pagination(self, resource, items, sort, limit, expected_page_num, resources=None, query_params='', verify_key='id'): if not resources: resources = '%ss' % resource query_str = query_params + '&' if query_params else '' query_str = query_str + ("limit=%s&sort_key=%s&" "sort_dir=%s") % (limit, sort[0], sort[1]) req = self.new_list_request(resources, params=query_str) items_res = [] page_num = 0 api = self._api_for_resource(resources) resource = resource.replace('-', '_') resources = resources.replace('-', '_') while req: page_num = page_num + 1 res = self.deserialize(self.fmt, req.get_response(api)) self.assertThat(len(res[resources]), matchers.LessThan(limit + 1)) items_res = items_res + res[resources] req = None if '%s_links' % resources in res: for link in res['%s_links' % resources]: if link['rel'] == 'next': content_type = 'application/%s' % self.fmt req = testlib_api.create_request(link['href'], '', content_type) self.assertEqual(len(res[resources]), limit) self.assertEqual(expected_page_num, page_num) self.assertEqual([item[resource][verify_key] for item in items], [n[verify_key] for n in items_res]) def _test_list_with_pagination_reverse(self, resource, items, sort, limit, expected_page_num, resources=None, query_params=''): if not resources: resources = '%ss' % resource resource = resource.replace('-', '_') api = self._api_for_resource(resources) marker = items[-1][resource]['id'] query_str = query_params + '&' if query_params else '' query_str = query_str + ("limit=%s&page_reverse=True&" "sort_key=%s&sort_dir=%s&" "marker=%s") % (limit, sort[0], sort[1], marker) req = self.new_list_request(resources, params=query_str) item_res = [items[-1][resource]] page_num = 0 resources = resources.replace('-', '_') while req: page_num = page_num + 1 res = self.deserialize(self.fmt, req.get_response(api)) self.assertThat(len(res[resources]), matchers.LessThan(limit + 1)) res[resources].reverse() item_res = item_res + res[resources] req = None if '%s_links' % resources in res: for link in res['%s_links' % resources]: if link['rel'] == 'previous': content_type = 'application/%s' % self.fmt req = testlib_api.create_request(link['href'], '', content_type) self.assertEqual(len(res[resources]), limit) self.assertEqual(expected_page_num, page_num) expected_res = [item[resource]['id'] for item in items] expected_res.reverse() self.assertEqual(expected_res, [n['id'] for n in item_res]) def _compare_resource(self, observed_res, expected_res, res_name): ''' Compare the observed and expected resources (ie compare subnets) ''' for k in expected_res: self.assertIn(k, observed_res[res_name]) if isinstance(expected_res[k], list): self.assertEqual(sorted(expected_res[k]), sorted(observed_res[res_name][k])) else: self.assertEqual(expected_res[k], observed_res[res_name][k]) def _validate_resource(self, resource, keys, res_name): ipv6_zero_gateway = False ipv6_null_gateway = False if res_name == 'subnet': attrs = resource[res_name] if not attrs['gateway_ip']: ipv6_null_gateway = True elif (attrs['ip_version'] is constants.IP_VERSION_6 and attrs['gateway_ip'][-2:] == "::"): ipv6_zero_gateway = True for k in keys: self.assertIn(k, resource[res_name]) if isinstance(keys[k], list): self.assertEqual( sorted(keys[k], key=helpers.safe_sort_key), sorted(resource[res_name][k], key=helpers.safe_sort_key)) else: if not ipv6_null_gateway: if (k == 'gateway_ip' and ipv6_zero_gateway and keys[k][-3:] == "::0"): self.assertEqual(keys[k][:-1], resource[res_name][k]) else: self.assertEqual(keys[k], resource[res_name][k]) class TestBasicGet(NeutronDbPluginV2TestCase): def test_single_get_admin(self): plugin = neutron.db.db_base_plugin_v2.NeutronDbPluginV2() with self.network() as network: net_id = network['network']['id'] ctx = context.get_admin_context() n = plugin._get_network(ctx, net_id) self.assertEqual(net_id, n.id) def test_single_get_tenant(self): plugin = neutron.db.db_base_plugin_v2.NeutronDbPluginV2() with self.network() as network: net_id = network['network']['id'] ctx = context.get_admin_context() n = plugin._get_network(ctx, net_id) self.assertEqual(net_id, n.id) class TestV2HTTPResponse(NeutronDbPluginV2TestCase): def test_create_returns_201(self): res = self._create_network(self.fmt, 'net2', True) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) def test_list_returns_200(self): req = self.new_list_request('networks') res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, res.status_int) def _check_list_with_fields(self, res, field_name): self.assertEqual(webob.exc.HTTPOk.code, res.status_int) body = self.deserialize(self.fmt, res) # further checks: 1 networks self.assertEqual(1, len(body['networks'])) # 1 field in the network record self.assertEqual(1, len(body['networks'][0])) # field is 'name' self.assertIn(field_name, body['networks'][0]) def test_list_with_fields(self): self._create_network(self.fmt, 'some_net', True) req = self.new_list_request('networks', params="fields=name") res = req.get_response(self.api) self._check_list_with_fields(res, 'name') def test_list_with_fields_noadmin(self): tenant_id = 'some_tenant' self._create_network(self.fmt, 'some_net', True, tenant_id=tenant_id, set_context=True) req = self.new_list_request('networks', params="fields=name") req.environ['neutron.context'] = context.Context('', tenant_id) res = req.get_response(self.api) self._check_list_with_fields(res, 'name') def test_list_with_fields_noadmin_and_policy_field(self): """If a field used by policy is selected, do not duplicate it. Verifies that if the field parameter explicitly specifies a field which is used by the policy engine, then it is not duplicated in the response. """ tenant_id = 'some_tenant' self._create_network(self.fmt, 'some_net', True, tenant_id=tenant_id, set_context=True) req = self.new_list_request('networks', params="fields=tenant_id") req.environ['neutron.context'] = context.Context('', tenant_id) res = req.get_response(self.api) self._check_list_with_fields(res, 'tenant_id') def test_show_returns_200(self): with self.network() as net: req = self.new_show_request('networks', net['network']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, res.status_int) def test_delete_returns_204(self): res = self._create_network(self.fmt, 'net1', True) net = self.deserialize(self.fmt, res) req = self.new_delete_request('networks', net['network']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) def test_delete_with_req_body_returns_400(self): res = self._create_network(self.fmt, 'net1', True) net = self.deserialize(self.fmt, res) data = {"network": {"id": net['network']['id']}} req = self.new_delete_request('networks', net['network']['id'], data=data) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_update_returns_200(self): with self.network() as net: req = self.new_update_request('networks', {'network': {'name': 'steve'}}, net['network']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, res.status_int) def test_update_invalid_json_400(self): with self.network() as net: req = self.new_update_request('networks', '{{"name": "aaa"}}', net['network']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_bad_route_404(self): req = self.new_list_request('doohickeys') res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) class TestPortsV2(NeutronDbPluginV2TestCase): def test_create_port_json(self): keys = [('admin_state_up', True), ('status', self.port_create_status)] with self.network(shared=True) as network: with self.subnet(network=network) as subnet: with self.port(name='myname') as port: for k, v in keys: self.assertEqual(port['port'][k], v) self.assertIn('mac_address', port['port']) ips = port['port']['fixed_ips'] subnet_ip_net = netaddr.IPNetwork(subnet['subnet']['cidr']) self.assertEqual(1, len(ips)) self.assertIn(netaddr.IPAddress(ips[0]['ip_address']), subnet_ip_net) self.assertEqual('myname', port['port']['name']) def test_create_port_as_admin(self): with self.network() as network: self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPCreated.code, tenant_id='bad_tenant_id', device_id='fake_device', device_owner='fake_owner', fixed_ips=[], set_context=False) def test_create_port_bad_tenant(self): with self.network() as network: self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPNotFound.code, tenant_id='bad_tenant_id', device_id='fake_device', device_owner='fake_owner', fixed_ips=[], set_context=True) def test_create_port_public_network(self): keys = [('admin_state_up', True), ('status', self.port_create_status)] with self.network(shared=True) as network: port_res = self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPCreated.code, tenant_id='another_tenant', set_context=True) port = self.deserialize(self.fmt, port_res) for k, v in keys: self.assertEqual(port['port'][k], v) self.assertIn('mac_address', port['port']) self._delete('ports', port['port']['id']) def test_create_port_None_values(self): with self.network() as network: keys = ['device_owner', 'name', 'device_id'] for key in keys: # test with each as None and rest as '' kwargs = {k: '' for k in keys} kwargs[key] = None self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPClientError.code, tenant_id='tenant_id', fixed_ips=[], set_context=False, **kwargs) def test_create_port_public_network_with_ip(self): with self.network(shared=True) as network: ip_net = netaddr.IPNetwork('10.0.0.0/24') with self.subnet(network=network, cidr=str(ip_net)): keys = [('admin_state_up', True), ('status', self.port_create_status)] port_res = self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPCreated.code, tenant_id='another_tenant', set_context=True) port = self.deserialize(self.fmt, port_res) for k, v in keys: self.assertEqual(port['port'][k], v) port_ip = port['port']['fixed_ips'][0]['ip_address'] self.assertIn(port_ip, ip_net) self.assertIn('mac_address', port['port']) self._delete('ports', port['port']['id']) def test_create_port_anticipating_allocation(self): with self.network(shared=True) as network: with self.subnet(network=network, cidr='10.0.0.0/24') as subnet: fixed_ips = [{'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet['subnet']['id'], 'ip_address': '10.0.0.2'}] self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPCreated.code, fixed_ips=fixed_ips) def test_create_port_public_network_with_invalid_ip_no_subnet_id(self, expected_error='InvalidIpForNetwork'): with self.network(shared=True) as network: with self.subnet(network=network, cidr='10.0.0.0/24'): ips = [{'ip_address': '1.1.1.1'}] res = self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPBadRequest.code, fixed_ips=ips, set_context=True) data = self.deserialize(self.fmt, res) msg = str(lib_exc.InvalidIpForNetwork(ip_address='1.1.1.1')) self.assertEqual(expected_error, data['NeutronError']['type']) self.assertEqual(msg, data['NeutronError']['message']) def test_create_port_public_network_with_invalid_ip_and_subnet_id(self, expected_error='InvalidIpForSubnet'): with self.network(shared=True) as network: with self.subnet(network=network, cidr='10.0.0.0/24') as subnet: ips = [{'subnet_id': subnet['subnet']['id'], 'ip_address': '1.1.1.1'}] res = self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPBadRequest.code, fixed_ips=ips, set_context=True) data = self.deserialize(self.fmt, res) msg = str(lib_exc.InvalidIpForSubnet(ip_address='1.1.1.1')) self.assertEqual(expected_error, data['NeutronError']['type']) self.assertEqual(msg, data['NeutronError']['message']) def test_create_ports_bulk_native(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk port create") with self.network() as net: res = self._create_port_bulk(self.fmt, 2, net['network']['id'], 'test', True) self._validate_behavior_on_bulk_success(res, 'ports') for p in self.deserialize(self.fmt, res)['ports']: self._delete('ports', p['id']) def test_create_ports_bulk_emulated(self): real_has_attr = hasattr #ensures the API choose the emulation code path def fakehasattr(item, attr): if attr.endswith('__native_bulk_support'): return False return real_has_attr(item, attr) with mock.patch('six.moves.builtins.hasattr', new=fakehasattr): with self.network() as net: res = self._create_port_bulk(self.fmt, 2, net['network']['id'], 'test', True) self._validate_behavior_on_bulk_success(res, 'ports') for p in self.deserialize(self.fmt, res)['ports']: self._delete('ports', p['id']) def test_create_ports_bulk_wrong_input(self): with self.network() as net: overrides = {1: {'admin_state_up': 'doh'}} res = self._create_port_bulk(self.fmt, 2, net['network']['id'], 'test', True, override=overrides) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) req = self.new_list_request('ports') res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, res.status_int) ports = self.deserialize(self.fmt, res) self.assertEqual(0, len(ports['ports'])) def test_get_ports_count(self): with self.port(), self.port(), self.port(), self.port() as p: tenid = p['port']['tenant_id'] ctx = context.Context(user_id=None, tenant_id=tenid, is_admin=False) pl = directory.get_plugin() count = pl.get_ports_count(ctx, filters={'tenant_id': [tenid]}) self.assertEqual(4, count) def test_create_ports_bulk_emulated_plugin_failure(self): real_has_attr = hasattr #ensures the API choose the emulation code path def fakehasattr(item, attr): if attr.endswith('__native_bulk_support'): return False return real_has_attr(item, attr) with mock.patch('six.moves.builtins.hasattr', new=fakehasattr): orig = directory.get_plugin().create_port method_to_patch = _get_create_db_method('port') with mock.patch.object(directory.get_plugin(), method_to_patch) as patched_plugin: def side_effect(*args, **kwargs): return self._fail_second_call(patched_plugin, orig, *args, **kwargs) patched_plugin.side_effect = side_effect with self.network() as net: res = self._create_port_bulk(self.fmt, 2, net['network']['id'], 'test', True) # We expect a 500 as we injected a fault in the plugin self._validate_behavior_on_bulk_failure( res, 'ports', webob.exc.HTTPServerError.code ) def test_create_ports_bulk_native_plugin_failure(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk port create") ctx = context.get_admin_context() with self.network() as net: plugin = directory.get_plugin() orig = plugin.create_port method_to_patch = _get_create_db_method('port') with mock.patch.object(plugin, method_to_patch) as patched_plugin: def side_effect(*args, **kwargs): return self._fail_second_call(patched_plugin, orig, *args, **kwargs) patched_plugin.side_effect = side_effect res = self._create_port_bulk(self.fmt, 2, net['network']['id'], 'test', True, context=ctx) # We expect a 500 as we injected a fault in the plugin self._validate_behavior_on_bulk_failure( res, 'ports', webob.exc.HTTPServerError.code) def test_list_ports(self): # for this test we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) with self.port() as v1, self.port() as v2, self.port() as v3: ports = (v1, v2, v3) self._test_list_resources('port', ports) def _test_list_ports_filtered_by_fixed_ip(self, **kwargs): # for this test we need to enable overlapping ips cfg.CONF.set_default('allow_overlapping_ips', True) with self.port() as port1, self.port(): fixed_ips = port1['port']['fixed_ips'][0] query_params = """ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s """.strip() % (fixed_ips['ip_address'], '192.168.126.5', fixed_ips['subnet_id']) extra_params = "&".join(["{}={}".format(k, v) for k, v in kwargs.items()]) if extra_params: query_params = "{}&{}".format(query_params, extra_params) self._test_list_resources('port', [port1], query_params=query_params) def test_list_ports_filtered_by_fixed_ip(self): self._test_list_ports_filtered_by_fixed_ip() def test_list_ports_filtered_by_fixed_ip_with_limit(self): self._test_list_ports_filtered_by_fixed_ip(limit=500) def test_list_ports_public_network(self): with self.network(shared=True) as network: with self.subnet(network) as subnet: with self.port(subnet, tenant_id='tenant_1') as port1,\ self.port(subnet, tenant_id='tenant_2') as port2: # Admin request - must return both ports self._test_list_resources('port', [port1, port2]) # Tenant_1 request - must return single port n_context = context.Context('', 'tenant_1') self._test_list_resources('port', [port1], neutron_context=n_context) # Tenant_2 request - must return single port n_context = context.Context('', 'tenant_2') self._test_list_resources('port', [port2], neutron_context=n_context) def test_list_ports_for_network_owner(self): with self.network(tenant_id='tenant_1') as network: with self.subnet(network) as subnet: with self.port(subnet, tenant_id='tenant_1') as port1,\ self.port(subnet, tenant_id='tenant_2') as port2: # network owner request, should return all ports port_res = self._list_ports( 'json', set_context=True, tenant_id='tenant_1') port_list = self.deserialize('json', port_res)['ports'] port_ids = [p['id'] for p in port_list] self.assertEqual(2, len(port_list)) self.assertIn(port1['port']['id'], port_ids) self.assertIn(port2['port']['id'], port_ids) # another tenant request, only return ports belong to it port_res = self._list_ports( 'json', set_context=True, tenant_id='tenant_2') port_list = self.deserialize('json', port_res)['ports'] port_ids = [p['id'] for p in port_list] self.assertEqual(1, len(port_list)) self.assertNotIn(port1['port']['id'], port_ids) self.assertIn(port2['port']['id'], port_ids) def test_list_ports_with_sort_native(self): if self._skip_native_sorting: self.skipTest("Skip test for not implemented sorting feature") cfg.CONF.set_default('allow_overlapping_ips', True) with self.port(admin_state_up='True', mac_address='00:00:00:00:00:01') as port1,\ self.port(admin_state_up='False', mac_address='00:00:00:00:00:02') as port2,\ self.port(admin_state_up='False', mac_address='00:00:00:00:00:03') as port3: self._test_list_with_sort('port', (port3, port2, port1), [('admin_state_up', 'asc'), ('mac_address', 'desc')]) def test_list_ports_with_sort_emulated(self): helper_patcher = mock.patch( 'neutron.api.v2.base.Controller._get_sorting_helper', new=_fake_get_sorting_helper) helper_patcher.start() cfg.CONF.set_default('allow_overlapping_ips', True) with self.port(admin_state_up='True', mac_address='00:00:00:00:00:01') as port1,\ self.port(admin_state_up='False', mac_address='00:00:00:00:00:02') as port2,\ self.port(admin_state_up='False', mac_address='00:00:00:00:00:03') as port3: self._test_list_with_sort('port', (port3, port2, port1), [('admin_state_up', 'asc'), ('mac_address', 'desc')]) def test_list_ports_with_pagination_native(self): if self._skip_native_pagination: self.skipTest("Skip test for not implemented pagination feature") cfg.CONF.set_default('allow_overlapping_ips', True) with self.port(mac_address='00:00:00:00:00:01') as port1,\ self.port(mac_address='00:00:00:00:00:02') as port2,\ self.port(mac_address='00:00:00:00:00:03') as port3: self._test_list_with_pagination('port', (port1, port2, port3), ('mac_address', 'asc'), 2, 2) def test_list_ports_with_pagination_emulated(self): helper_patcher = mock.patch( 'neutron.api.v2.base.Controller._get_pagination_helper', new=_fake_get_pagination_helper) helper_patcher.start() cfg.CONF.set_default('allow_overlapping_ips', True) with self.port(mac_address='00:00:00:00:00:01') as port1,\ self.port(mac_address='00:00:00:00:00:02') as port2,\ self.port(mac_address='00:00:00:00:00:03') as port3: self._test_list_with_pagination('port', (port1, port2, port3), ('mac_address', 'asc'), 2, 2) def test_list_ports_with_pagination_reverse_native(self): if self._skip_native_pagination: self.skipTest("Skip test for not implemented pagination feature") cfg.CONF.set_default('allow_overlapping_ips', True) with self.port(mac_address='00:00:00:00:00:01') as port1,\ self.port(mac_address='00:00:00:00:00:02') as port2,\ self.port(mac_address='00:00:00:00:00:03') as port3: self._test_list_with_pagination_reverse('port', (port1, port2, port3), ('mac_address', 'asc'), 2, 2) def test_list_ports_with_pagination_reverse_emulated(self): helper_patcher = mock.patch( 'neutron.api.v2.base.Controller._get_pagination_helper', new=_fake_get_pagination_helper) helper_patcher.start() cfg.CONF.set_default('allow_overlapping_ips', True) with self.port(mac_address='00:00:00:00:00:01') as port1,\ self.port(mac_address='00:00:00:00:00:02') as port2,\ self.port(mac_address='00:00:00:00:00:03') as port3: self._test_list_with_pagination_reverse('port', (port1, port2, port3), ('mac_address', 'asc'), 2, 2) def test_show_port(self): with self.port() as port: req = self.new_show_request('ports', port['port']['id'], self.fmt) sport = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(port['port']['id'], sport['port']['id']) def test_delete_port(self): with self.port() as port: self._delete('ports', port['port']['id']) self._show('ports', port['port']['id'], expected_code=webob.exc.HTTPNotFound.code) def test_delete_port_public_network(self): with self.network(shared=True) as network: port_res = self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPCreated.code, tenant_id='another_tenant', set_context=True) port = self.deserialize(self.fmt, port_res) self._delete('ports', port['port']['id']) self._show('ports', port['port']['id'], expected_code=webob.exc.HTTPNotFound.code) def test_delete_port_by_network_owner(self): with self.network(tenant_id='tenant_1') as network: with self.subnet(network) as subnet: with self.port(subnet, tenant_id='tenant_2') as port: self._delete( 'ports', port['port']['id'], neutron_context=context.Context('', 'tenant_1')) self._show('ports', port['port']['id'], expected_code=webob.exc.HTTPNotFound.code) def test_update_port_with_stale_subnet(self): with self.network(shared=True) as network: port = self._make_port(self.fmt, network['network']['id']) subnet = self._make_subnet(self.fmt, network, '10.0.0.1', '10.0.0.0/24') data = {'port': {'fixed_ips': [{'subnet_id': subnet['subnet']['id']}]}} # mock _get_subnets, to return this subnet mock.patch.object(ipam_backend_mixin.IpamBackendMixin, '_ipam_get_subnets', return_value=[subnet['subnet']]).start() # Delete subnet, to mock the subnet as stale. self._delete('subnets', subnet['subnet']['id']) self._show('subnets', subnet['subnet']['id'], expected_code=webob.exc.HTTPNotFound.code) # Though _get_subnets returns the subnet, subnet was deleted later # while ipam is updating the port. So port update should fail. req = self.new_update_request('ports', data, port['port']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_port_update_with_ipam_error(self): with self.network() as network,\ self.subnet(), self.subnet(),\ self.port(network=network) as port,\ mock.patch('neutron.ipam.drivers.neutrondb_ipam.' 'driver.NeutronDbSubnet.deallocate') as f: f.side_effect = [ ipam_exc.IpAddressAllocationNotFound( ip_address='foo_i', subnet_id='foo_s'), None, ] data = {'port': {'name': 'fool-me'}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual('fool-me', res['port']['name']) def test_update_port(self): with self.port() as port: data = {'port': {'admin_state_up': False}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(res['port']['admin_state_up'], data['port']['admin_state_up']) def update_port_mac(self, port, updated_fixed_ips=None): orig_mac = port['mac_address'] mac = orig_mac.split(':') mac[5] = '01' if mac[5] != '01' else '00' new_mac = ':'.join(mac) data = {'port': {'mac_address': new_mac}} if updated_fixed_ips: data['port']['fixed_ips'] = updated_fixed_ips req = self.new_update_request('ports', data, port['id']) return req.get_response(self.api), new_mac def _verify_ips_after_mac_change(self, orig_port, new_port): for fip in orig_port['port']['fixed_ips']: subnet = self._show('subnets', fip['subnet_id']) if ipv6_utils.is_auto_address_subnet(subnet['subnet']): port_mac = new_port['port']['mac_address'] subnet_cidr = subnet['subnet']['cidr'] eui_addr = str(netutils.get_ipv6_addr_by_EUI64(subnet_cidr, port_mac)) fip = {'ip_address': eui_addr, 'subnet_id': subnet['subnet']['id']} self.assertIn(fip, new_port['port']['fixed_ips']) self.assertEqual(len(orig_port['port']['fixed_ips']), len(new_port['port']['fixed_ips'])) def check_update_port_mac( self, expected_status=webob.exc.HTTPOk.code, expected_error='StateInvalid', subnet=None, device_owner=DEVICE_OWNER_COMPUTE, updated_fixed_ips=None, host_arg=None, arg_list=None): host_arg = host_arg or {} arg_list = arg_list or [] with self.port(device_owner=device_owner, subnet=subnet, arg_list=arg_list, **host_arg) as port: self.assertIn('mac_address', port['port']) res, new_mac = self.update_port_mac( port['port'], updated_fixed_ips=updated_fixed_ips) self.assertEqual(expected_status, res.status_int) if expected_status == webob.exc.HTTPOk.code: result = self.deserialize(self.fmt, res) self.assertIn('port', result) self.assertEqual(new_mac, result['port']['mac_address']) if updated_fixed_ips is None: self._verify_ips_after_mac_change(port, result) else: self.assertEqual(len(updated_fixed_ips), len(result['port']['fixed_ips'])) else: error = self.deserialize(self.fmt, res) self.assertEqual(expected_error, error['NeutronError']['type']) def test_update_port_mac(self): self.check_update_port_mac() # sub-classes for plugins/drivers that support mac address update # override this method def test_dhcp_port_ips_prefer_next_available_ip(self): # test to check that DHCP ports get the first available IP in the # allocation range with self.subnet() as subnet: port_ips = [] for _ in range(10): with self.port(device_owner=constants.DEVICE_OWNER_DHCP, subnet=subnet) as port: port_ips.append(port['port']['fixed_ips'][0]['ip_address']) first_ip = netaddr.IPAddress(port_ips[0]) expected = [str(first_ip + i) for i in range(10)] self.assertEqual(expected, port_ips) def test_update_port_mac_ip(self): with self.subnet() as subnet: updated_fixed_ips = [{'subnet_id': subnet['subnet']['id'], 'ip_address': '10.0.0.3'}] self.check_update_port_mac(subnet=subnet, updated_fixed_ips=updated_fixed_ips) def test_update_port_mac_v6_slaac(self): with self.network() as n: pass # add a couple of v4 networks to ensure they aren't interferred with with self.subnet(network=n) as v4_1, \ self.subnet(network=n, cidr='7.0.0.0/24') as v4_2: pass with self.subnet(network=n, gateway_ip='fe80::1', cidr='2607:f0d0:1002:51::/64', ip_version=6, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: self.assertTrue( ipv6_utils.is_auto_address_subnet(subnet['subnet'])) fixed_ips_req = { 'fixed_ips': [{'subnet_id': subnet['subnet']['id']}, {'subnet_id': v4_1['subnet']['id']}, {'subnet_id': v4_1['subnet']['id']}, {'subnet_id': v4_2['subnet']['id']}, {'subnet_id': v4_2['subnet']['id']}] } self.check_update_port_mac(subnet=subnet, host_arg=fixed_ips_req) def test_update_port_mac_bad_owner(self): self.check_update_port_mac( device_owner=DEVICE_OWNER_NOT_COMPUTE, expected_status=webob.exc.HTTPConflict.code, expected_error='UnsupportedPortDeviceOwner') def check_update_port_mac_used(self, expected_error='MacAddressInUse'): with self.subnet() as subnet: with self.port(subnet=subnet) as port: with self.port(subnet=subnet) as port2: self.assertIn('mac_address', port['port']) new_mac = port2['port']['mac_address'] data = {'port': {'mac_address': new_mac}} req = self.new_update_request('ports', data, port['port']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) error = self.deserialize(self.fmt, res) self.assertEqual(expected_error, error['NeutronError']['type']) def test_update_port_mac_used(self): self.check_update_port_mac_used() def test_update_port_not_admin(self): res = self._create_network(self.fmt, 'net1', True, tenant_id='not_admin', set_context=True) net1 = self.deserialize(self.fmt, res) res = self._create_port(self.fmt, net1['network']['id'], tenant_id='not_admin', set_context=True) port = self.deserialize(self.fmt, res) data = {'port': {'admin_state_up': False}} neutron_context = context.Context('', 'not_admin') port = self._update('ports', port['port']['id'], data, neutron_context=neutron_context) self.assertFalse(port['port']['admin_state_up']) def test_update_device_id_unchanged(self): with self.port() as port: data = {'port': {'admin_state_up': True, 'device_id': port['port']['device_id']}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertTrue(res['port']['admin_state_up']) def test_update_device_id_null(self): with self.port() as port: data = {'port': {'device_id': None}} req = self.new_update_request('ports', data, port['port']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_delete_network_if_port_exists(self): with self.port() as port: req = self.new_delete_request('networks', port['port']['network_id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_delete_network_port_exists_owned_by_network(self): res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) network_id = network['network']['id'] self._create_port(self.fmt, network_id, device_owner=constants.DEVICE_OWNER_DHCP) req = self.new_delete_request('networks', network_id) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) def test_delete_network_port_exists_owned_by_network_race(self): res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) network_id = network['network']['id'] self._create_port(self.fmt, network_id, device_owner=constants.DEVICE_OWNER_DHCP) # skip first port delete to simulate create after auto clean plugin = directory.get_plugin() p = mock.patch.object(plugin, 'delete_port') mock_del_port = p.start() mock_del_port.side_effect = lambda *a, **k: p.stop() req = self.new_delete_request('networks', network_id) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) def test_delete_network_port_exists_owned_by_network_port_not_found(self): """Tests that we continue to gracefully delete the network even if a neutron:dhcp-owned port was deleted concurrently. """ res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) network_id = network['network']['id'] self._create_port(self.fmt, network_id, device_owner=constants.DEVICE_OWNER_DHCP) # Raise PortNotFound when trying to delete the port to simulate a # concurrent delete race; note that we actually have to delete the port # "out of band" otherwise deleting the network will fail because of # constraints in the data model. plugin = directory.get_plugin() orig_delete = plugin.delete_port def fake_delete_port(context, id): # Delete the port for real from the database and then raise # PortNotFound to simulate the race. self.assertIsNone(orig_delete(context, id)) raise lib_exc.PortNotFound(port_id=id) p = mock.patch.object(plugin, 'delete_port') mock_del_port = p.start() mock_del_port.side_effect = fake_delete_port req = self.new_delete_request('networks', network_id) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) def test_update_port_delete_ip(self): with self.subnet() as subnet: with self.port(subnet=subnet) as port: data = {'port': {'admin_state_up': False, 'fixed_ips': []}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['port']['admin_state_up'], res['port']['admin_state_up']) self.assertEqual(data['port']['fixed_ips'], res['port']['fixed_ips']) def test_no_more_port_exception(self): with self.subnet(cidr='10.0.0.0/31', enable_dhcp=False) as subnet: id = subnet['subnet']['network_id'] res = self._create_port(self.fmt, id) data = self.deserialize(self.fmt, res) msg = str(lib_exc.IpAddressGenerationFailure(net_id=id)) self.assertEqual(data['NeutronError']['message'], msg) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_ports_native_quotas(self): quota = 1 cfg.CONF.set_override('quota_port', quota, group='QUOTAS') with self.network() as network: res = self._create_port(self.fmt, network['network']['id']) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) res = self._create_port(self.fmt, network['network']['id']) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_ports_bulk_native_quotas(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk port create") quota = 4 cfg.CONF.set_override('quota_port', quota, group='QUOTAS') with self.network() as network: res = self._create_port_bulk(self.fmt, quota + 1, network['network']['id'], 'test', True) self._validate_behavior_on_bulk_failure( res, 'ports', errcode=webob.exc.HTTPConflict.code) def test_update_port_update_ip(self): """Test update of port IP. Check that a configured IP 10.0.0.2 is replaced by 10.0.0.10. """ with self.subnet() as subnet: fixed_ip_data = [{'ip_address': '10.0.0.2', 'subnet_id': subnet['subnet']['id']}] with self.port(subnet=subnet, fixed_ips=fixed_ip_data) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual('10.0.0.2', ips[0]['ip_address']) self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) data = {'port': {'fixed_ips': [{'subnet_id': subnet['subnet']['id'], 'ip_address': "10.0.0.10"}]}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) ips = res['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual('10.0.0.10', ips[0]['ip_address']) self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) def test_update_port_update_ip_address_only(self): with self.subnet() as subnet: ip_address = '10.0.0.2' fixed_ip_data = [{'ip_address': ip_address, 'subnet_id': subnet['subnet']['id']}] with self.port(subnet=subnet, fixed_ips=fixed_ip_data) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(ip_address, ips[0]['ip_address']) self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) data = {'port': {'fixed_ips': [{'subnet_id': subnet['subnet']['id'], 'ip_address': "10.0.0.10"}, {'ip_address': ip_address}]}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) ips = res['port']['fixed_ips'] self.assertEqual(2, len(ips)) self.assertIn({'ip_address': ip_address, 'subnet_id': subnet['subnet']['id']}, ips) self.assertIn({'ip_address': '10.0.0.10', 'subnet_id': subnet['subnet']['id']}, ips) def test_update_port_update_ips(self): """Update IP and associate new IP on port. Check a port update with the specified subnet_id's. A IP address will be allocated for each subnet_id. """ with self.subnet() as subnet: with self.port(subnet=subnet) as port: data = {'port': {'admin_state_up': False, 'fixed_ips': [{'subnet_id': subnet['subnet']['id'], 'ip_address': '10.0.0.3'}]}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['port']['admin_state_up'], res['port']['admin_state_up']) ips = res['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual('10.0.0.3', ips[0]['ip_address'], '10.0.0.3') self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) def test_update_port_add_additional_ip(self): """Test update of port with additional IP.""" with self.subnet() as subnet: with self.port(subnet=subnet) as port: data = {'port': {'admin_state_up': False, 'fixed_ips': [{'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet['subnet']['id']}]}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['port']['admin_state_up'], res['port']['admin_state_up']) ips = res['port']['fixed_ips'] self.assertEqual(2, len(ips)) self.assertNotEqual(ips[0]['ip_address'], ips[1]['ip_address']) network_ip_net = netaddr.IPNetwork(subnet['subnet']['cidr']) self.assertIn(ips[0]['ip_address'], network_ip_net) self.assertIn(ips[1]['ip_address'], network_ip_net) def test_update_port_invalid_fixed_ip_address_v6_slaac(self): with self.subnet( cidr='2607:f0d0:1002:51::/64', ip_version=6, ipv6_address_mode=constants.IPV6_SLAAC, gateway_ip=constants.ATTR_NOT_SPECIFIED) as subnet: with self.port(subnet=subnet) as port: ips = port['port']['fixed_ips'] ip_address = '2607:f0d0:1002:51::5' self.assertEqual(1, len(ips)) port_mac = port['port']['mac_address'] subnet_id = subnet['subnet']['id'] subnet_cidr = subnet['subnet']['cidr'] eui_addr = str(netutils.get_ipv6_addr_by_EUI64(subnet_cidr, port_mac)) self.assertEqual(ips[0]['ip_address'], eui_addr) self.assertEqual(ips[0]['subnet_id'], subnet_id) data = {'port': {'fixed_ips': [{'subnet_id': subnet_id, 'ip_address': ip_address}]}} req = self.new_update_request('ports', data, port['port']['id']) res = req.get_response(self.api) err = self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) self.assertEqual('AllocationOnAutoAddressSubnet', err['NeutronError']['type']) msg = str(ipam_exc.AllocationOnAutoAddressSubnet( ip=ip_address, subnet_id=subnet_id)) self.assertEqual(err['NeutronError']['message'], msg) def test_requested_duplicate_mac(self): with self.port() as port: mac = port['port']['mac_address'] # check that MAC address matches base MAC base_mac = cfg.CONF.base_mac[0:2] self.assertTrue(mac.startswith(base_mac)) kwargs = {"mac_address": mac} net_id = port['port']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_mac_generation(self): cfg.CONF.set_override('base_mac', "12:34:56:00:00:00") with self.port() as port: mac = port['port']['mac_address'] self.assertTrue(mac.startswith("12:34:56")) def test_mac_generation_4octet(self): cfg.CONF.set_override('base_mac', "12:34:56:78:00:00") with self.port() as port: mac = port['port']['mac_address'] self.assertTrue(mac.startswith("12:34:56:78")) def test_duplicate_mac_generation(self): # simulate duplicate mac generation to make sure DBDuplicate is retried responses = ['12:34:56:78:00:00', '12:34:56:78:00:00', '12:34:56:78:00:01'] with mock.patch.object(net, 'get_random_mac', side_effect=responses) as grand_mac: with self.subnet() as s: with self.port(subnet=s) as p1, self.port(subnet=s) as p2: self.assertEqual('12:34:56:78:00:00', p1['port']['mac_address']) self.assertEqual('12:34:56:78:00:01', p2['port']['mac_address']) self.assertEqual(3, grand_mac.call_count) def test_bad_mac_format(self): cfg.CONF.set_override('base_mac', "bad_mac") try: self.plugin._check_base_mac_format() except Exception: return self.fail("No exception for illegal base_mac format") def test_is_mac_in_use(self): ctx = context.get_admin_context() with self.port() as port: net_id = port['port']['network_id'] mac = port['port']['mac_address'] self.assertTrue(self.plugin._is_mac_in_use(ctx, net_id, mac)) mac2 = '00:22:00:44:00:66' # other mac, same network self.assertFalse(self.plugin._is_mac_in_use(ctx, net_id, mac2)) net_id2 = port['port']['id'] # other net uuid, same mac self.assertFalse(self.plugin._is_mac_in_use(ctx, net_id2, mac)) def test_requested_duplicate_ip(self): with self.subnet() as subnet: subnet_ip_net = netaddr.IPNetwork(subnet['subnet']['cidr']) with self.port(subnet=subnet) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertIn(ips[0]['ip_address'], subnet_ip_net) self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) # Check configuring of duplicate IP kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id'], 'ip_address': ips[0]['ip_address']}]} net_id = port['port']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_requested_subnet_id(self): with self.subnet() as subnet: subnet_ip_net = netaddr.IPNetwork(subnet['subnet']['cidr']) with self.port(subnet=subnet) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertIn(netaddr.IPAddress(ips[0]['ip_address']), netaddr.IPSet(subnet_ip_net)) self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) # Request a IP from specific subnet kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id']}]} net_id = port['port']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) port2 = self.deserialize(self.fmt, res) ips = port2['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertIn(ips[0]['ip_address'], subnet_ip_net) self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) self._delete('ports', port2['port']['id']) def test_requested_subnet_id_not_on_network(self): with self.subnet() as subnet: with self.port(subnet=subnet) as port: # Create new network res = self._create_network(fmt=self.fmt, name='net2', admin_state_up=True) network2 = self.deserialize(self.fmt, res) subnet2 = self._make_subnet(self.fmt, network2, "1.1.1.1", "1.1.1.0/24", ip_version=4) net_id = port['port']['network_id'] # Request a IP from specific subnet kwargs = {"fixed_ips": [{'subnet_id': subnet2['subnet']['id']}]} net_id = port['port']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_overlapping_subnets(self): with self.subnet() as subnet: tenant_id = subnet['subnet']['tenant_id'] net_id = subnet['subnet']['network_id'] res = self._create_subnet(self.fmt, tenant_id=tenant_id, net_id=net_id, cidr='10.0.0.225/28', ip_version=4, gateway_ip=constants.ATTR_NOT_SPECIFIED) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_requested_subnet_id_v4_and_v6(self): with self.subnet() as subnet: # Get a IPv4 and IPv6 address tenant_id = subnet['subnet']['tenant_id'] net_id = subnet['subnet']['network_id'] res = self._create_subnet( self.fmt, tenant_id=tenant_id, net_id=net_id, cidr='2607:f0d0:1002:51::/124', ip_version=6, gateway_ip=constants.ATTR_NOT_SPECIFIED) subnet2 = self.deserialize(self.fmt, res) kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet2['subnet']['id']}]} res = self._create_port(self.fmt, net_id=net_id, **kwargs) port3 = self.deserialize(self.fmt, res) ips = port3['port']['fixed_ips'] cidr_v4 = subnet['subnet']['cidr'] cidr_v6 = subnet2['subnet']['cidr'] self.assertEqual(2, len(ips)) self._test_requested_port_subnet_ids(ips, [subnet['subnet']['id'], subnet2['subnet']['id']]) self._test_dual_stack_port_ip_addresses_in_subnets(ips, cidr_v4, cidr_v6) res = self._create_port(self.fmt, net_id=net_id) port4 = self.deserialize(self.fmt, res) # Check that a v4 and a v6 address are allocated ips = port4['port']['fixed_ips'] self.assertEqual(2, len(ips)) self._test_requested_port_subnet_ids(ips, [subnet['subnet']['id'], subnet2['subnet']['id']]) self._test_dual_stack_port_ip_addresses_in_subnets(ips, cidr_v4, cidr_v6) self._delete('ports', port3['port']['id']) self._delete('ports', port4['port']['id']) def _test_requested_port_subnet_ids(self, ips, expected_subnet_ids): self.assertEqual(set(x['subnet_id'] for x in ips), set(expected_subnet_ids)) def _test_dual_stack_port_ip_addresses_in_subnets(self, ips, cidr_v4, cidr_v6): ip_net_v4 = netaddr.IPNetwork(cidr_v4) ip_net_v6 = netaddr.IPNetwork(cidr_v6) for address in ips: ip_addr = netaddr.IPAddress(address['ip_address']) expected_ip_net = ip_net_v4 if ip_addr.version == 4 else ip_net_v6 self.assertIn(ip_addr, expected_ip_net) def test_create_port_invalid_fixed_ip_address_v6_pd_slaac(self): with self.network(name='net') as network: subnet = self._make_v6_subnet( network, constants.IPV6_SLAAC, ipv6_pd=True) net_id = subnet['subnet']['network_id'] subnet_id = subnet['subnet']['id'] # update subnet with new prefix prefix = '2001::/64' data = {'subnet': {'cidr': prefix}} self.plugin.update_subnet(context.get_admin_context(), subnet_id, data) kwargs = {"fixed_ips": [{'subnet_id': subnet_id, 'ip_address': '2001::2'}]} # pd is a auto address subnet, so can't have 2001::2 res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_port_invalid_fixed_ip_address_v6_pd_slaac(self): with self.network(name='net') as network: subnet = self._make_v6_subnet( network, constants.IPV6_SLAAC, ipv6_pd=True) net_id = subnet['subnet']['network_id'] subnet_id = subnet['subnet']['id'] # update subnet with new prefix prefix = '2001::/64' data = {'subnet': {'cidr': prefix}} self.plugin.update_subnet(context.get_admin_context(), subnet_id, data) # create port and check for eui addr with 2001::/64 prefix. res = self._create_port(self.fmt, net_id=net_id) port = self.deserialize(self.fmt, res) port_mac = port['port']['mac_address'] eui_addr = str(netutils.get_ipv6_addr_by_EUI64( prefix, port_mac)) fixedips = [{'subnet_id': subnet_id, 'ip_address': eui_addr}] self.assertEqual(fixedips, port['port']['fixed_ips']) # try update port with 2001::2. update should fail as # pd is a auto address subnet, so can't have 2001::2 data = {'port': {"fixed_ips": [{'subnet_id': subnet_id, 'ip_address': '2001::2'}]}} req = self.new_update_request('ports', data, port['port']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_port_invalid_subnet_v6_pd_slaac(self): with self.network(name='net') as network: subnet = self._make_v6_subnet( network, constants.IPV6_SLAAC, ipv6_pd=True) subnet_id = subnet['subnet']['id'] # update subnet with new prefix prefix = '2001::/64' data = {'subnet': {'cidr': prefix}} self.plugin.update_subnet(context.get_admin_context(), subnet_id, data) # Create port on network2 res = self._create_network(fmt=self.fmt, name='net2', admin_state_up=True) network2 = self.deserialize(self.fmt, res) self._make_subnet(self.fmt, network2, "1.1.1.1", "1.1.1.0/24", ip_version=4) res = self._create_port(self.fmt, net_id=network2['network']['id']) port = self.deserialize(self.fmt, res) # try update port with 1st network's PD subnet data = {'port': {"fixed_ips": [{'subnet_id': subnet_id}]}} req = self.new_update_request('ports', data, port['port']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_requested_invalid_fixed_ip_address_v6_slaac(self): with self.subnet(gateway_ip='fe80::1', cidr='2607:f0d0:1002:51::/64', ip_version=6, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id'], 'ip_address': '2607:f0d0:1002:51::5'}]} net_id = subnet['subnet']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_requested_fixed_ip_address_v6_slaac_router_iface(self): with self.subnet(gateway_ip='fe80::1', cidr='fe80::/64', ip_version=6, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id'], 'ip_address': 'fe80::1'}]} net_id = subnet['subnet']['network_id'] device_owner = constants.DEVICE_OWNER_ROUTER_INTF res = self._create_port(self.fmt, net_id=net_id, device_owner=device_owner, **kwargs) port = self.deserialize(self.fmt, res) self.assertEqual(len(port['port']['fixed_ips']), 1) self.assertEqual(port['port']['fixed_ips'][0]['ip_address'], 'fe80::1') def test_requested_subnet_id_v6_slaac(self): with self.subnet(gateway_ip='fe80::1', cidr='2607:f0d0:1002:51::/64', ip_version=6, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: with self.port(subnet, fixed_ips=[{'subnet_id': subnet['subnet']['id']}]) as port: port_mac = port['port']['mac_address'] subnet_cidr = subnet['subnet']['cidr'] eui_addr = str(netutils.get_ipv6_addr_by_EUI64(subnet_cidr, port_mac)) self.assertEqual(port['port']['fixed_ips'][0]['ip_address'], eui_addr) def test_requested_subnet_id_v4_and_v6_slaac(self): with self.network() as network: with self.subnet(network) as subnet,\ self.subnet( network, cidr='2607:f0d0:1002:51::/64', ip_version=6, gateway_ip='fe80::1', ipv6_address_mode=constants.IPV6_SLAAC) as subnet2: with self.port( subnet, fixed_ips=[{'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet2['subnet']['id']}] ) as port: ips = port['port']['fixed_ips'] subnet1_net = netaddr.IPNetwork(subnet['subnet']['cidr']) subnet2_net = netaddr.IPNetwork(subnet2['subnet']['cidr']) network_ip_set = netaddr.IPSet(subnet1_net) network_ip_set.add(subnet2_net) self.assertEqual(2, len(ips)) port_mac = port['port']['mac_address'] subnet_cidr = subnet2['subnet']['cidr'] eui_addr = str(netutils.get_ipv6_addr_by_EUI64( subnet_cidr, port_mac)) self.assertIn(ips[0]['ip_address'], network_ip_set) self.assertIn(ips[1]['ip_address'], network_ip_set) self.assertIn({'ip_address': eui_addr, 'subnet_id': subnet2['subnet']['id']}, ips) def test_create_router_port_ipv4_and_ipv6_slaac_no_fixed_ips(self): with self.network() as network: # Create an IPv4 and an IPv6 SLAAC subnet on the network with self.subnet(network) as subnet_v4,\ self.subnet(network, cidr='2607:f0d0:1002:51::/64', ip_version=6, gateway_ip='fe80::1', ipv6_address_mode=constants.IPV6_SLAAC): subnet_ip_net = netaddr.IPNetwork(subnet_v4['subnet']['cidr']) # Create a router port without specifying fixed_ips port = self._make_port( self.fmt, network['network']['id'], device_owner=constants.DEVICE_OWNER_ROUTER_INTF) # Router port should only have an IPv4 address fixed_ips = port['port']['fixed_ips'] self.assertEqual(1, len(fixed_ips)) self.assertIn(fixed_ips[0]['ip_address'], subnet_ip_net) @staticmethod def _calc_ipv6_addr_by_EUI64(port, subnet): port_mac = port['port']['mac_address'] subnet_cidr = subnet['subnet']['cidr'] return str(netutils.get_ipv6_addr_by_EUI64(subnet_cidr, port_mac)) def test_ip_allocation_for_ipv6_subnet_slaac_address_mode(self): res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) subnet = self._make_v6_subnet(network, constants.IPV6_SLAAC) port = self._make_port(self.fmt, network['network']['id']) self.assertEqual(1, len(port['port']['fixed_ips'])) self.assertEqual(self._calc_ipv6_addr_by_EUI64(port, subnet), port['port']['fixed_ips'][0]['ip_address']) def _test_create_port_with_ipv6_subnet_in_fixed_ips(self, addr_mode, ipv6_pd=False): """Test port create with an IPv6 subnet incl in fixed IPs.""" with self.network(name='net') as network: subnet = self._make_v6_subnet(network, addr_mode, ipv6_pd) subnet_id = subnet['subnet']['id'] fixed_ips = [{'subnet_id': subnet_id}] with self.port(subnet=subnet, fixed_ips=fixed_ips) as port: port_fixed_ips = port['port']['fixed_ips'] self.assertEqual(1, len(port_fixed_ips)) if addr_mode == constants.IPV6_SLAAC: exp_ip_addr = self._calc_ipv6_addr_by_EUI64(port, subnet) self.assertEqual(exp_ip_addr, port_fixed_ips[0]['ip_address']) self.assertIn(port_fixed_ips[0]['ip_address'], netaddr.IPNetwork(subnet['subnet']['cidr'])) def test_create_port_with_ipv6_slaac_subnet_in_fixed_ips(self): self._test_create_port_with_ipv6_subnet_in_fixed_ips( addr_mode=constants.IPV6_SLAAC) def test_create_port_with_ipv6_pd_subnet_in_fixed_ips(self): self._test_create_port_with_ipv6_subnet_in_fixed_ips( addr_mode=constants.IPV6_SLAAC, ipv6_pd=True) def test_create_port_with_ipv6_dhcp_stateful_subnet_in_fixed_ips(self): self._test_create_port_with_ipv6_subnet_in_fixed_ips( addr_mode=constants.DHCPV6_STATEFUL) def test_create_port_with_multiple_ipv4_and_ipv6_subnets(self): """Test port create with multiple IPv4, IPv6 DHCP/SLAAC subnets.""" res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) sub_dicts = [ {'gateway': '10.0.0.1', 'cidr': '10.0.0.0/24', 'ip_version': 4, 'ra_addr_mode': None}, {'gateway': '10.0.1.1', 'cidr': '10.0.1.0/24', 'ip_version': 4, 'ra_addr_mode': None}, {'gateway': 'fe80::1', 'cidr': 'fe80::/64', 'ip_version': 6, 'ra_addr_mode': constants.IPV6_SLAAC}, {'gateway': 'fe81::1', 'cidr': 'fe81::/64', 'ip_version': 6, 'ra_addr_mode': constants.IPV6_SLAAC}, {'gateway': 'fe82::1', 'cidr': 'fe82::/64', 'ip_version': 6, 'ra_addr_mode': constants.DHCPV6_STATEFUL}, {'gateway': 'fe83::1', 'cidr': 'fe83::/64', 'ip_version': 6, 'ra_addr_mode': constants.DHCPV6_STATEFUL}] subnets = {} for sub_dict in sub_dicts: subnet = self._make_subnet( self.fmt, network, gateway=sub_dict['gateway'], cidr=sub_dict['cidr'], ip_version=sub_dict['ip_version'], ipv6_ra_mode=sub_dict['ra_addr_mode'], ipv6_address_mode=sub_dict['ra_addr_mode']) subnets[subnet['subnet']['id']] = sub_dict res = self._create_port(self.fmt, net_id=network['network']['id']) port = self.deserialize(self.fmt, res) # Since the create port request was made without a list of fixed IPs, # the port should be associated with addresses for one of the # IPv4 subnets, one of the DHCPv6 subnets, and both of the IPv6 # SLAAC subnets. self.assertEqual(4, len(port['port']['fixed_ips'])) addr_mode_count = {None: 0, constants.DHCPV6_STATEFUL: 0, constants.IPV6_SLAAC: 0} for fixed_ip in port['port']['fixed_ips']: subnet_id = fixed_ip['subnet_id'] if subnet_id in subnets: addr_mode_count[subnets[subnet_id]['ra_addr_mode']] += 1 self.assertEqual(1, addr_mode_count[None]) self.assertEqual(1, addr_mode_count[constants.DHCPV6_STATEFUL]) self.assertEqual(2, addr_mode_count[constants.IPV6_SLAAC]) def test_delete_port_with_ipv6_slaac_address(self): """Test that a port with an IPv6 SLAAC address can be deleted.""" res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) # Create a port that has an associated IPv6 SLAAC address self._make_v6_subnet(network, constants.IPV6_SLAAC) res = self._create_port(self.fmt, net_id=network['network']['id']) port = self.deserialize(self.fmt, res) self.assertEqual(1, len(port['port']['fixed_ips'])) # Confirm that the port can be deleted self._delete('ports', port['port']['id']) self._show('ports', port['port']['id'], expected_code=webob.exc.HTTPNotFound.code) def test_update_port_with_ipv6_slaac_subnet_in_fixed_ips(self): """Test port update with an IPv6 SLAAC subnet in fixed IPs.""" res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) # Create a port using an IPv4 subnet and an IPv6 SLAAC subnet self._make_subnet(self.fmt, network, gateway='10.0.0.1', cidr='10.0.0.0/24', ip_version=4) subnet_v6 = self._make_v6_subnet(network, constants.IPV6_SLAAC) res = self._create_port(self.fmt, net_id=network['network']['id']) port = self.deserialize(self.fmt, res) self.assertEqual(2, len(port['port']['fixed_ips'])) # Update port including only the IPv6 SLAAC subnet data = {'port': {'fixed_ips': [{'subnet_id': subnet_v6['subnet']['id']}]}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) # Port should only have an address corresponding to IPv6 SLAAC subnet ips = res['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(self._calc_ipv6_addr_by_EUI64(port, subnet_v6), ips[0]['ip_address']) def test_update_port_with_new_ipv6_slaac_subnet_in_fixed_ips(self): """Test port update with a new IPv6 SLAAC subnet in fixed IPs.""" res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) # Create a port using an IPv4 subnet and an IPv6 SLAAC subnet subnet_v4 = self._make_subnet(self.fmt, network, gateway='10.0.0.1', cidr='10.0.0.0/24', ip_version=4) subnet_v6 = self._make_v6_subnet(network, constants.IPV6_SLAAC) res = self._create_port(self.fmt, net_id=network['network']['id']) port = self.deserialize(self.fmt, res) self.assertEqual(2, len(port['port']['fixed_ips'])) # Update port to have only IPv4 address ips = [{'subnet_id': subnet_v4['subnet']['id']}, {'subnet_id': subnet_v6['subnet']['id'], 'delete_subnet': True}] data = {'port': {'fixed_ips': ips}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) # Port should only have an address corresponding to IPv4 subnet ips = res['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(subnet_v4['subnet']['id'], ips[0]['subnet_id']) # Now update port and request an additional address on the IPv6 SLAAC # subnet. ips.append({'subnet_id': subnet_v6['subnet']['id']}) data = {'port': {'fixed_ips': ips}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) ips = res['port']['fixed_ips'] # Port should have IPs on both IPv4 and IPv6 subnets self.assertEqual(2, len(ips)) self.assertEqual(set([subnet_v4['subnet']['id'], subnet_v6['subnet']['id']]), set([ip['subnet_id'] for ip in ips])) def test_update_port_excluding_ipv6_slaac_subnet_from_fixed_ips(self): """Test port update excluding IPv6 SLAAC subnet from fixed ips.""" res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) # Create a port using an IPv4 subnet and an IPv6 SLAAC subnet subnet_v4 = self._make_subnet(self.fmt, network, gateway='10.0.0.1', cidr='10.0.0.0/24', ip_version=4) subnet_v6 = self._make_v6_subnet(network, constants.IPV6_SLAAC) res = self._create_port(self.fmt, net_id=network['network']['id']) port = self.deserialize(self.fmt, res) self.assertEqual(2, len(port['port']['fixed_ips'])) # Update port including only the IPv4 subnet data = {'port': {'fixed_ips': [{'subnet_id': subnet_v4['subnet']['id'], 'ip_address': "10.0.0.10"}]}} req = self.new_update_request('ports', data, port['port']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) # Port should still have an addr corresponding to IPv6 SLAAC subnet ips = res['port']['fixed_ips'] self.assertEqual(2, len(ips)) eui_addr = self._calc_ipv6_addr_by_EUI64(port, subnet_v6) expected_v6_ip = {'subnet_id': subnet_v6['subnet']['id'], 'ip_address': eui_addr} self.assertIn(expected_v6_ip, ips) def test_ip_allocation_for_ipv6_2_subnet_slaac_mode(self): res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) v6_subnet_1 = self._make_subnet(self.fmt, network, gateway='2001:100::1', cidr='2001:100::0/64', ip_version=6, ipv6_ra_mode=constants.IPV6_SLAAC) v6_subnet_2 = self._make_subnet(self.fmt, network, gateway='2001:200::1', cidr='2001:200::0/64', ip_version=6, ipv6_ra_mode=constants.IPV6_SLAAC) port = self._make_port(self.fmt, network['network']['id']) port_mac = port['port']['mac_address'] cidr_1 = v6_subnet_1['subnet']['cidr'] cidr_2 = v6_subnet_2['subnet']['cidr'] eui_addr_1 = str(netutils.get_ipv6_addr_by_EUI64(cidr_1, port_mac)) eui_addr_2 = str(netutils.get_ipv6_addr_by_EUI64(cidr_2, port_mac)) self.assertEqual({eui_addr_1, eui_addr_2}, {fixed_ip['ip_address'] for fixed_ip in port['port']['fixed_ips']}) def test_range_allocation(self): with self.subnet(gateway_ip='10.0.0.3', cidr='10.0.0.0/29') as subnet: kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet['subnet']['id']}]} net_id = subnet['subnet']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) port = self.deserialize(self.fmt, res) ips = port['port']['fixed_ips'] self.assertEqual(5, len(ips)) alloc = ['10.0.0.1', '10.0.0.2', '10.0.0.4', '10.0.0.5', '10.0.0.6'] for ip in ips: self.assertIn(ip['ip_address'], alloc) self.assertEqual(ip['subnet_id'], subnet['subnet']['id']) alloc.remove(ip['ip_address']) self.assertEqual(0, len(alloc)) self._delete('ports', port['port']['id']) with self.subnet(gateway_ip='11.0.0.6', cidr='11.0.0.0/29') as subnet: kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet['subnet']['id']}, {'subnet_id': subnet['subnet']['id']}]} net_id = subnet['subnet']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) port = self.deserialize(self.fmt, res) ips = port['port']['fixed_ips'] self.assertEqual(5, len(ips)) alloc = ['11.0.0.1', '11.0.0.2', '11.0.0.3', '11.0.0.4', '11.0.0.5'] for ip in ips: self.assertIn(ip['ip_address'], alloc) self.assertEqual(ip['subnet_id'], subnet['subnet']['id']) alloc.remove(ip['ip_address']) self.assertEqual(0, len(alloc)) self._delete('ports', port['port']['id']) def test_requested_invalid_fixed_ips(self): with self.subnet() as subnet: subnet_ip_net = netaddr.IPNetwork(subnet['subnet']['cidr']) with self.port(subnet=subnet) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertIn(ips[0]['ip_address'], subnet_ip_net) self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) # Test invalid subnet_id kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id']}, {'subnet_id': '00000000-ffff-ffff-ffff-000000000000'}]} net_id = port['port']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) port2 = self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) # Test invalid IP address on specified subnet_id kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id'], 'ip_address': '1.1.1.1'}]} net_id = port['port']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) port2 = self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) # Test invalid addresses - IP's not on subnet or network # address or broadcast address bad_ips = ['1.1.1.1', '10.0.0.0', '10.0.0.255'] net_id = port['port']['network_id'] for ip in bad_ips: kwargs = {"fixed_ips": [{'ip_address': ip}]} res = self._create_port(self.fmt, net_id=net_id, **kwargs) port2 = self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) # Enable allocation of gateway address kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id'], 'ip_address': '10.0.0.1'}]} net_id = port['port']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) port2 = self.deserialize(self.fmt, res) ips = port2['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual('10.0.0.1', ips[0]['ip_address']) self.assertEqual(subnet['subnet']['id'], ips[0]['subnet_id']) self._delete('ports', port2['port']['id']) def test_invalid_ip(self): with self.subnet() as subnet: # Allocate specific IP kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id'], 'ip_address': '1011.0.0.5'}]} net_id = subnet['subnet']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_duplicate_ips(self): with self.subnet() as subnet: # Allocate specific IP kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id'], 'ip_address': '10.0.0.5'}, {'subnet_id': subnet['subnet']['id'], 'ip_address': '10.0.0.5'}]} net_id = subnet['subnet']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_fixed_ip_invalid_subnet_id(self): with self.subnet() as subnet: # Allocate specific IP kwargs = {"fixed_ips": [{'subnet_id': 'i am invalid', 'ip_address': '10.0.0.5'}]} net_id = subnet['subnet']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_fixed_ip_invalid_ip(self): with self.subnet() as subnet: # Allocate specific IP kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id'], 'ip_address': '10.0.0.55555'}]} net_id = subnet['subnet']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_requested_ips_only(self): with self.subnet() as subnet: fixed_ip_data = [{'ip_address': '10.0.0.2', 'subnet_id': subnet['subnet']['id']}] with self.port(subnet=subnet, fixed_ips=fixed_ip_data) as port: ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual('10.0.0.2', ips[0]['ip_address']) self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) ips_only = ['10.0.0.18', '10.0.0.20', '10.0.0.22', '10.0.0.21', '10.0.0.3', '10.0.0.17', '10.0.0.19'] ports_to_delete = [] for i in ips_only: kwargs = {"fixed_ips": [{'ip_address': i}]} net_id = port['port']['network_id'] res = self._create_port(self.fmt, net_id=net_id, **kwargs) port = self.deserialize(self.fmt, res) ports_to_delete.append(port) ips = port['port']['fixed_ips'] self.assertEqual(1, len(ips)) self.assertEqual(i, ips[0]['ip_address']) self.assertEqual(subnet['subnet']['id'], ips[0]['subnet_id']) for p in ports_to_delete: self._delete('ports', p['port']['id']) def test_invalid_admin_state(self): with self.network() as network: data = {'port': {'network_id': network['network']['id'], 'tenant_id': network['network']['tenant_id'], 'admin_state_up': 7, 'fixed_ips': []}} port_req = self.new_create_request('ports', data) res = port_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_invalid_mac_address(self): with self.network() as network: data = {'port': {'network_id': network['network']['id'], 'tenant_id': network['network']['tenant_id'], 'admin_state_up': 1, 'mac_address': 'mac', 'fixed_ips': []}} port_req = self.new_create_request('ports', data) res = port_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_delete_ports_by_device_id(self): plugin = directory.get_plugin() ctx = context.get_admin_context() with self.subnet() as subnet: with self.port(subnet=subnet, device_id='owner1') as p1,\ self.port(subnet=subnet, device_id='owner1') as p2,\ self.port(subnet=subnet, device_id='owner2') as p3: network_id = subnet['subnet']['network_id'] plugin.delete_ports_by_device_id(ctx, 'owner1', network_id) self._show('ports', p1['port']['id'], expected_code=webob.exc.HTTPNotFound.code) self._show('ports', p2['port']['id'], expected_code=webob.exc.HTTPNotFound.code) self._show('ports', p3['port']['id'], expected_code=webob.exc.HTTPOk.code) def _test_delete_ports_by_device_id_second_call_failure(self, plugin): ctx = context.get_admin_context() with self.subnet() as subnet: with self.port(subnet=subnet, device_id='owner1') as p1,\ self.port(subnet=subnet, device_id='owner1') as p2,\ self.port(subnet=subnet, device_id='owner2') as p3: orig = plugin.delete_port with mock.patch.object(plugin, 'delete_port') as del_port: def side_effect(*args, **kwargs): return self._fail_second_call(del_port, orig, *args, **kwargs) del_port.side_effect = side_effect network_id = subnet['subnet']['network_id'] self.assertRaises(lib_exc.NeutronException, plugin.delete_ports_by_device_id, ctx, 'owner1', network_id) statuses = { self._show_response('ports', p['port']['id']).status_int for p in [p1, p2]} expected = {webob.exc.HTTPNotFound.code, webob.exc.HTTPOk.code} self.assertEqual(expected, statuses) self._show('ports', p3['port']['id'], expected_code=webob.exc.HTTPOk.code) def test_delete_ports_by_device_id_second_call_failure(self): plugin = directory.get_plugin() self._test_delete_ports_by_device_id_second_call_failure(plugin) def _test_delete_ports_ignores_port_not_found(self, plugin): ctx = context.get_admin_context() with self.subnet() as subnet: with self.port(subnet=subnet, device_id='owner1') as p,\ mock.patch.object(plugin, 'delete_port') as del_port: del_port.side_effect = lib_exc.PortNotFound( port_id=p['port']['id'] ) network_id = subnet['subnet']['network_id'] try: plugin.delete_ports_by_device_id(ctx, 'owner1', network_id) except lib_exc.PortNotFound: self.fail("delete_ports_by_device_id unexpectedly raised " "a PortNotFound exception. It should ignore " "this exception because it is often called at " "the same time other concurrent operations are " "deleting some of the same ports.") def test_delete_ports_ignores_port_not_found(self): plugin = directory.get_plugin() self._test_delete_ports_ignores_port_not_found(plugin) class TestNetworksV2(NeutronDbPluginV2TestCase): # NOTE(cerberus): successful network update and delete are # effectively tested above def test_create_network(self): name = 'net1' keys = [('subnets', []), ('name', name), ('admin_state_up', True), ('status', self.net_create_status), ('shared', False)] with self.network(name=name) as net: for k, v in keys: self.assertEqual(net['network'][k], v) def test_create_public_network(self): name = 'public_net' keys = [('subnets', []), ('name', name), ('admin_state_up', True), ('status', self.net_create_status), ('shared', True)] with self.network(name=name, shared=True) as net: for k, v in keys: self.assertEqual(net['network'][k], v) def test_create_public_network_no_admin_tenant(self): name = 'public_net' with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: with self.network(name=name, shared=True, tenant_id="another_tenant", set_context=True): pass self.assertEqual(webob.exc.HTTPForbidden.code, ctx_manager.exception.code) def test_update_network(self): with self.network() as network: data = {'network': {'name': 'a_brand_new_name'}} req = self.new_update_request('networks', data, network['network']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['network']['name'], res['network']['name']) def test_update_shared_network_noadmin_returns_403(self): with self.network(shared=True) as network: data = {'network': {'name': 'a_brand_new_name'}} req = self.new_update_request('networks', data, network['network']['id']) req.environ['neutron.context'] = context.Context('', 'somebody') res = req.get_response(self.api) self.assertEqual(403, res.status_int) def test_update_network_set_shared(self): with self.network(shared=False) as network: data = {'network': {'shared': True}} req = self.new_update_request('networks', data, network['network']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertTrue(res['network']['shared']) def test_update_network_set_shared_owner_returns_403(self): with self.network(shared=False) as network: net_owner = network['network']['tenant_id'] data = {'network': {'shared': True}} req = self.new_update_request('networks', data, network['network']['id']) req.environ['neutron.context'] = context.Context('u', net_owner) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPForbidden.code, res.status_int) def test_update_network_with_subnet_set_shared(self): with self.network(shared=False) as network: with self.subnet(network=network) as subnet: data = {'network': {'shared': True}} req = self.new_update_request('networks', data, network['network']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertTrue(res['network']['shared']) # must query db to see whether subnet's shared attribute # has been updated or not ctx = context.Context('', '', is_admin=True) subnet_db = directory.get_plugin().get_subnet( ctx, subnet['subnet']['id']) self.assertTrue(subnet_db['shared']) def test_update_network_set_not_shared_single_tenant(self): with self.network(shared=True) as network: res1 = self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPCreated.code, tenant_id=network['network']['tenant_id'], set_context=True) data = {'network': {'shared': False}} req = self.new_update_request('networks', data, network['network']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertFalse(res['network']['shared']) port1 = self.deserialize(self.fmt, res1) self._delete('ports', port1['port']['id']) def test_update_network_set_not_shared_other_tenant_returns_409(self): with self.network(shared=True) as network: res1 = self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPCreated.code, tenant_id='somebody_else', set_context=True) data = {'network': {'shared': False}} req = self.new_update_request('networks', data, network['network']['id']) self.assertEqual(webob.exc.HTTPConflict.code, req.get_response(self.api).status_int) port1 = self.deserialize(self.fmt, res1) self._delete('ports', port1['port']['id']) def test_update_network_set_not_shared_other_tenant_access_via_rbac(self): with self.network(shared=True) as network: ctx = context.get_admin_context() with db_api.context_manager.writer.using(ctx): ctx.session.add( rbac_db_models.NetworkRBAC( object_id=network['network']['id'], action='access_as_shared', tenant_id=network['network']['tenant_id'], target_tenant='somebody_else') ) ctx.session.add( rbac_db_models.NetworkRBAC( object_id=network['network']['id'], action='access_as_shared', tenant_id=network['network']['tenant_id'], target_tenant='one_more_somebody_else') ) res1 = self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPCreated.code, tenant_id='somebody_else', set_context=True) data = {'network': {'shared': False}} req = self.new_update_request('networks', data, network['network']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertFalse(res['network']['shared']) port1 = self.deserialize(self.fmt, res1) self._delete('ports', port1['port']['id']) def test_update_network_set_not_shared_multi_tenants_returns_409(self): with self.network(shared=True) as network: res1 = self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPCreated.code, tenant_id='somebody_else', set_context=True) res2 = self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPCreated.code, tenant_id=network['network']['tenant_id'], set_context=True) data = {'network': {'shared': False}} req = self.new_update_request('networks', data, network['network']['id']) self.assertEqual(webob.exc.HTTPConflict.code, req.get_response(self.api).status_int) port1 = self.deserialize(self.fmt, res1) port2 = self.deserialize(self.fmt, res2) self._delete('ports', port1['port']['id']) self._delete('ports', port2['port']['id']) def test_update_network_set_not_shared_multi_tenants2_returns_409(self): with self.network(shared=True) as network: res1 = self._create_port(self.fmt, network['network']['id'], webob.exc.HTTPCreated.code, tenant_id='somebody_else', set_context=True) self._create_subnet(self.fmt, network['network']['id'], '10.0.0.0/24', webob.exc.HTTPCreated.code, tenant_id=network['network']['tenant_id'], set_context=True) data = {'network': {'shared': False}} req = self.new_update_request('networks', data, network['network']['id']) self.assertEqual(webob.exc.HTTPConflict.code, req.get_response(self.api).status_int) port1 = self.deserialize(self.fmt, res1) self._delete('ports', port1['port']['id']) def test_create_networks_bulk_native(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk network create") res = self._create_network_bulk(self.fmt, 2, 'test', True) self._validate_behavior_on_bulk_success(res, 'networks') def test_create_networks_native_quotas(self): quota = 1 cfg.CONF.set_override('quota_network', quota, group='QUOTAS') res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_networks_bulk_native_quotas(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk network create") quota = 4 cfg.CONF.set_override('quota_network', quota, group='QUOTAS') res = self._create_network_bulk(self.fmt, quota + 1, 'test', True) self._validate_behavior_on_bulk_failure( res, 'networks', errcode=webob.exc.HTTPConflict.code) def test_create_networks_bulk_tenants_and_quotas(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk network create") quota = 2 cfg.CONF.set_override('quota_network', quota, group='QUOTAS') networks = [{'network': {'name': 'n1', 'tenant_id': self._tenant_id}}, {'network': {'name': 'n2', 'tenant_id': self._tenant_id}}, {'network': {'name': 'n1', 'tenant_id': 't1'}}, {'network': {'name': 'n2', 'tenant_id': 't1'}}] res = self._create_bulk_from_list(self.fmt, 'network', networks) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) def test_create_networks_bulk_tenants_and_quotas_fail(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk network create") quota = 2 cfg.CONF.set_override('quota_network', quota, group='QUOTAS') networks = [{'network': {'name': 'n1', 'tenant_id': self._tenant_id}}, {'network': {'name': 'n2', 'tenant_id': self._tenant_id}}, {'network': {'name': 'n1', 'tenant_id': 't1'}}, {'network': {'name': 'n3', 'tenant_id': self._tenant_id}}, {'network': {'name': 'n2', 'tenant_id': 't1'}}] res = self._create_bulk_from_list(self.fmt, 'network', networks) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_networks_bulk_emulated(self): real_has_attr = hasattr #ensures the API choose the emulation code path def fakehasattr(item, attr): if attr.endswith('__native_bulk_support'): return False return real_has_attr(item, attr) with mock.patch('six.moves.builtins.hasattr', new=fakehasattr): res = self._create_network_bulk(self.fmt, 2, 'test', True) self._validate_behavior_on_bulk_success(res, 'networks') def test_create_networks_bulk_wrong_input(self): res = self._create_network_bulk(self.fmt, 2, 'test', True, override={1: {'admin_state_up': 'doh'}}) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) req = self.new_list_request('networks') res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, res.status_int) nets = self.deserialize(self.fmt, res) self.assertEqual(0, len(nets['networks'])) def test_create_networks_bulk_emulated_plugin_failure(self): real_has_attr = hasattr def fakehasattr(item, attr): if attr.endswith('__native_bulk_support'): return False return real_has_attr(item, attr) orig = directory.get_plugin().create_network #ensures the API choose the emulation code path with mock.patch('six.moves.builtins.hasattr', new=fakehasattr): method_to_patch = _get_create_db_method('network') with mock.patch.object(directory.get_plugin(), method_to_patch) as patched_plugin: def side_effect(*args, **kwargs): return self._fail_second_call(patched_plugin, orig, *args, **kwargs) patched_plugin.side_effect = side_effect res = self._create_network_bulk(self.fmt, 2, 'test', True) # We expect a 500 as we injected a fault in the plugin self._validate_behavior_on_bulk_failure( res, 'networks', webob.exc.HTTPServerError.code ) def test_create_networks_bulk_native_plugin_failure(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk network create") orig = directory.get_plugin().create_network method_to_patch = _get_create_db_method('network') with mock.patch.object(directory.get_plugin(), method_to_patch) as patched_plugin: def side_effect(*args, **kwargs): return self._fail_second_call(patched_plugin, orig, *args, **kwargs) patched_plugin.side_effect = side_effect res = self._create_network_bulk(self.fmt, 2, 'test', True) # We expect a 500 as we injected a fault in the plugin self._validate_behavior_on_bulk_failure( res, 'networks', webob.exc.HTTPServerError.code ) def test_list_networks(self): with self.network() as v1, self.network() as v2, self.network() as v3: networks = (v1, v2, v3) self._test_list_resources('network', networks) def test_list_networks_with_sort_native(self): if self._skip_native_sorting: self.skipTest("Skip test for not implemented sorting feature") with self.network(admin_state_up=True, name='net1') as net1,\ self.network(admin_state_up=False, name='net2') as net2,\ self.network(admin_state_up=False, name='net3') as net3: self._test_list_with_sort('network', (net3, net2, net1), [('admin_state_up', 'asc'), ('name', 'desc')]) def test_list_networks_with_sort_extended_attr_native_returns_400(self): if self._skip_native_sorting: self.skipTest("Skip test for not implemented sorting feature") with self.network(admin_state_up=True, name='net1'),\ self.network(admin_state_up=False, name='net2'),\ self.network(admin_state_up=False, name='net3'): req = self.new_list_request( 'networks', params='sort_key=provider:segmentation_id&sort_dir=asc') res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_list_networks_with_sort_remote_key_native_returns_400(self): if self._skip_native_sorting: self.skipTest("Skip test for not implemented sorting feature") with self.network(admin_state_up=True, name='net1'),\ self.network(admin_state_up=False, name='net2'),\ self.network(admin_state_up=False, name='net3'): req = self.new_list_request( 'networks', params='sort_key=subnets&sort_dir=asc') res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_list_networks_with_sort_emulated(self): helper_patcher = mock.patch( 'neutron.api.v2.base.Controller._get_sorting_helper', new=_fake_get_sorting_helper) helper_patcher.start() with self.network(admin_state_up=True, name='net1') as net1,\ self.network(admin_state_up=False, name='net2') as net2,\ self.network(admin_state_up=False, name='net3') as net3: self._test_list_with_sort('network', (net3, net2, net1), [('admin_state_up', 'asc'), ('name', 'desc')]) def test_list_networks_with_pagination_native(self): if self._skip_native_pagination: self.skipTest("Skip test for not implemented pagination feature") with self.network(name='net1') as net1,\ self.network(name='net2') as net2,\ self.network(name='net3') as net3: self._test_list_with_pagination('network', (net1, net2, net3), ('name', 'asc'), 2, 2) def test_list_networks_with_pagination_emulated(self): helper_patcher = mock.patch( 'neutron.api.v2.base.Controller._get_pagination_helper', new=_fake_get_pagination_helper) helper_patcher.start() with self.network(name='net1') as net1,\ self.network(name='net2') as net2,\ self.network(name='net3') as net3: self._test_list_with_pagination('network', (net1, net2, net3), ('name', 'asc'), 2, 2) def test_list_networks_without_pk_in_fields_pagination_emulated(self): helper_patcher = mock.patch( 'neutron.api.v2.base.Controller._get_pagination_helper', new=_fake_get_pagination_helper) helper_patcher.start() with self.network(name='net1', shared=True) as net1,\ self.network(name='net2', shared=False) as net2,\ self.network(name='net3', shared=True) as net3: self._test_list_with_pagination('network', (net1, net2, net3), ('name', 'asc'), 2, 2, query_params="fields=name", verify_key='name') def test_list_networks_without_pk_in_fields_pagination_native(self): if self._skip_native_pagination: self.skipTest("Skip test for not implemented pagination feature") with self.network(name='net1') as net1,\ self.network(name='net2') as net2,\ self.network(name='net3') as net3: self._test_list_with_pagination('network', (net1, net2, net3), ('name', 'asc'), 2, 2, query_params="fields=shared", verify_key='shared') def test_list_networks_with_pagination_reverse_native(self): if self._skip_native_pagination: self.skipTest("Skip test for not implemented pagination feature") with self.network(name='net1') as net1,\ self.network(name='net2') as net2,\ self.network(name='net3') as net3: self._test_list_with_pagination_reverse('network', (net1, net2, net3), ('name', 'asc'), 2, 2) def test_list_networks_with_pagination_reverse_emulated(self): helper_patcher = mock.patch( 'neutron.api.v2.base.Controller._get_pagination_helper', new=_fake_get_pagination_helper) helper_patcher.start() with self.network(name='net1') as net1,\ self.network(name='net2') as net2,\ self.network(name='net3') as net3: self._test_list_with_pagination_reverse('network', (net1, net2, net3), ('name', 'asc'), 2, 2) def test_list_networks_with_parameters(self): with self.network(name='net1', admin_state_up=False) as net1,\ self.network(name='net2') as net2: query_params = 'admin_state_up=False' self._test_list_resources('network', [net1], query_params=query_params) query_params = 'admin_state_up=True' self._test_list_resources('network', [net2], query_params=query_params) def test_list_networks_with_fields(self): with self.network(name='net1'): req = self.new_list_request('networks', params='fields=name') res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(1, len(res['networks'])) net = res['networks'][0] self.assertEqual('net1', net['name']) self.assertNotIn('id', net) self.assertNotIn('tenant_id', net) self.assertNotIn('project_id', net) def test_list_networks_with_parameters_invalid_values(self): with self.network(name='net1', admin_state_up=False),\ self.network(name='net2'): req = self.new_list_request('networks', params='admin_state_up=fake') res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_list_shared_networks_with_non_admin_user(self): with self.network(shared=False, name='net1', tenant_id='tenant1') as net1,\ self.network(shared=True, name='net2', tenant_id='another_tenant') as net2,\ self.network(shared=False, name='net3', tenant_id='another_tenant'): ctx = context.Context(user_id='non_admin', tenant_id='tenant1', is_admin=False) self._test_list_resources('network', (net1, net2), ctx) def test_show_network(self): with self.network(name='net1') as net: req = self.new_show_request('networks', net['network']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(res['network']['name'], net['network']['name']) def test_show_network_with_subnet(self): with self.network(name='net1') as net: with self.subnet(net) as subnet: req = self.new_show_request('networks', net['network']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(res['network']['subnets'][0], subnet['subnet']['id']) def test_invalid_admin_status(self): value = [[7, False, webob.exc.HTTPClientError.code], [True, True, webob.exc.HTTPCreated.code], ["True", True, webob.exc.HTTPCreated.code], ["true", True, webob.exc.HTTPCreated.code], [1, True, webob.exc.HTTPCreated.code], ["False", False, webob.exc.HTTPCreated.code], [False, False, webob.exc.HTTPCreated.code], ["false", False, webob.exc.HTTPCreated.code], ["7", False, webob.exc.HTTPClientError.code]] for v in value: data = {'network': {'name': 'net', 'admin_state_up': v[0], 'tenant_id': self._tenant_id}} network_req = self.new_create_request('networks', data) req = network_req.get_response(self.api) self.assertEqual(req.status_int, v[2]) if v[2] == webob.exc.HTTPCreated.code: res = self.deserialize(self.fmt, req) self.assertEqual(res['network']['admin_state_up'], v[1]) class TestSubnetsV2(NeutronDbPluginV2TestCase): def _test_create_subnet(self, network=None, expected=None, **kwargs): keys = kwargs.copy() keys.setdefault('cidr', '10.0.0.0/24') keys.setdefault('ip_version', 4) keys.setdefault('enable_dhcp', True) with self.subnet(network=network, **keys) as subnet: # verify the response has each key with the correct value self._validate_resource(subnet, keys, 'subnet') # verify the configured validations are correct if expected: self._compare_resource(subnet, expected, 'subnet') self._delete('subnets', subnet['subnet']['id']) return subnet def test_create_subnet(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' subnet = self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr) self.assertEqual(4, subnet['subnet']['ip_version']) self.assertIn('name', subnet['subnet']) def test_create_subnet_with_network_different_tenant(self): with self.network(shared=False, tenant_id='tenant1') as network: ctx = context.Context(user_id='non_admin', tenant_id='tenant2', is_admin=False) data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': '4', 'gateway_ip': '10.0.2.1'}} req = self.new_create_request('subnets', data, self.fmt, context=ctx) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_create_two_subnets(self): gateway_ips = ['10.0.0.1', '10.0.1.1'] cidrs = ['10.0.0.0/24', '10.0.1.0/24'] with self.network() as network: with self.subnet(network=network, gateway_ip=gateway_ips[0], cidr=cidrs[0]): with self.subnet(network=network, gateway_ip=gateway_ips[1], cidr=cidrs[1]): net_req = self.new_show_request('networks', network['network']['id']) raw_res = net_req.get_response(self.api) net_res = self.deserialize(self.fmt, raw_res) for subnet_id in net_res['network']['subnets']: sub_req = self.new_show_request('subnets', subnet_id) raw_res = sub_req.get_response(self.api) sub_res = self.deserialize(self.fmt, raw_res) self.assertIn(sub_res['subnet']['cidr'], cidrs) self.assertIn(sub_res['subnet']['gateway_ip'], gateway_ips) def test_create_two_subnets_same_cidr_returns_400(self): gateway_ip_1 = '10.0.0.1' cidr_1 = '10.0.0.0/24' gateway_ip_2 = '10.0.0.10' cidr_2 = '10.0.0.0/24' with self.network() as network: with self.subnet(network=network, gateway_ip=gateway_ip_1, cidr=cidr_1): with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: with self.subnet(network=network, gateway_ip=gateway_ip_2, cidr=cidr_2): pass self.assertEqual(webob.exc.HTTPClientError.code, ctx_manager.exception.code) def test_create_subnet_bad_V4_cidr(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0', 'ip_version': '4', 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.2.1'}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_with_cidr_and_default_subnetpool(self): """Expect subnet-create to keep semantic with default pools.""" with self.network() as network: tenant_id = network['network']['tenant_id'] subnetpool_prefix = '10.0.0.0/8' with self.subnetpool(prefixes=[subnetpool_prefix], admin=True, name="My subnet pool", tenant_id=tenant_id, min_prefixlen='25', is_default=True): data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.0.0/24', 'ip_version': '4', 'tenant_id': tenant_id}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) subnet = self.deserialize(self.fmt, res)['subnet'] self.assertIsNone(subnet['subnetpool_id']) def test_create_subnet_no_cidr_and_default_subnetpool(self): """Expect subnet-create to keep semantic with default pools.""" with self.network() as network: tenant_id = network['network']['tenant_id'] subnetpool_prefix = '10.0.0.0/8' with self.subnetpool(prefixes=[subnetpool_prefix], admin=True, name="My subnet pool", tenant_id=tenant_id, min_prefixlen='25', is_default=True): data = {'subnet': {'network_id': network['network']['id'], 'ip_version': '4', 'tenant_id': tenant_id}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual( webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_no_ip_version(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'tenant_id': network['network']['tenant_id']}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_only_ip_version_v6_no_pool(self): with self.network() as network: tenant_id = network['network']['tenant_id'] cfg.CONF.set_override('ipv6_pd_enabled', False) data = {'subnet': {'network_id': network['network']['id'], 'ip_version': '6', 'tenant_id': tenant_id}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_bad_V4_cidr_prefix_len(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': constants.IPv4_ANY, 'ip_version': '4', 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '0.0.0.1'}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_bad_V6_cidr(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': 'fe80::', 'ip_version': '6', 'tenant_id': network['network']['tenant_id'], 'gateway_ip': 'fe80::1'}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_V6_slaac_big_prefix(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '2014::/65', 'ip_version': '6', 'tenant_id': network['network']['tenant_id'], 'gateway_ip': 'fe80::1', 'ipv6_address_mode': 'slaac'}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_2_subnets_overlapping_cidr_allowed_returns_200(self): cidr_1 = '10.0.0.0/23' cidr_2 = '10.0.0.0/24' cfg.CONF.set_override('allow_overlapping_ips', True) with self.subnet(cidr=cidr_1), self.subnet(cidr=cidr_2): pass def test_create_2_subnets_overlapping_cidr_not_allowed_returns_400(self): cidr_1 = '10.0.0.0/23' cidr_2 = '10.0.0.0/24' cfg.CONF.set_override('allow_overlapping_ips', False) with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: with self.subnet(cidr=cidr_1), self.subnet(cidr=cidr_2): pass self.assertEqual(webob.exc.HTTPClientError.code, ctx_manager.exception.code) def test_create_subnets_bulk_native(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk subnet create") with self.network() as net: res = self._create_subnet_bulk(self.fmt, 2, net['network']['id'], 'test') self._validate_behavior_on_bulk_success(res, 'subnets') def test_create_subnets_bulk_native_ipv6(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk subnet create") with self.network() as net: res = self._create_subnet_bulk(self.fmt, 2, net['network']['id'], 'test', ip_version=constants.IP_VERSION_6, ipv6_mode=constants.IPV6_SLAAC) self._validate_behavior_on_bulk_success(res, 'subnets') def test_create_subnets_bulk_emulated(self): real_has_attr = hasattr #ensures the API choose the emulation code path def fakehasattr(item, attr): if attr.endswith('__native_bulk_support'): return False return real_has_attr(item, attr) with mock.patch('six.moves.builtins.hasattr', new=fakehasattr): with self.network() as net: res = self._create_subnet_bulk(self.fmt, 2, net['network']['id'], 'test') self._validate_behavior_on_bulk_success(res, 'subnets') def test_create_subnets_bulk_emulated_plugin_failure(self): real_has_attr = hasattr #ensures the API choose the emulation code path def fakehasattr(item, attr): if attr.endswith('__native_bulk_support'): return False return real_has_attr(item, attr) with mock.patch('six.moves.builtins.hasattr', new=fakehasattr): orig = directory.get_plugin().create_subnet method_to_patch = _get_create_db_method('subnet') with mock.patch.object(directory.get_plugin(), method_to_patch) as patched_plugin: def side_effect(*args, **kwargs): self._fail_second_call(patched_plugin, orig, *args, **kwargs) patched_plugin.side_effect = side_effect with self.network() as net: res = self._create_subnet_bulk(self.fmt, 2, net['network']['id'], 'test') self._delete('networks', net['network']['id']) # We expect a 500 as we injected a fault in the plugin self._validate_behavior_on_bulk_failure( res, 'subnets', webob.exc.HTTPServerError.code ) def test_create_subnets_bulk_native_plugin_failure(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk subnet create") plugin = directory.get_plugin() orig = plugin.create_subnet method_to_patch = _get_create_db_method('subnet') with mock.patch.object(plugin, method_to_patch) as patched_plugin: def side_effect(*args, **kwargs): return self._fail_second_call(patched_plugin, orig, *args, **kwargs) patched_plugin.side_effect = side_effect with self.network() as net: res = self._create_subnet_bulk(self.fmt, 2, net['network']['id'], 'test') # We expect a 500 as we injected a fault in the plugin self._validate_behavior_on_bulk_failure( res, 'subnets', webob.exc.HTTPServerError.code ) def test_delete_subnet(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' # Create new network res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) subnet = self._make_subnet(self.fmt, network, gateway_ip, cidr, ip_version=4) req = self.new_delete_request('subnets', subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) def test_delete_subnet_port_exists_owned_by_network(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' # Create new network res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) subnet = self._make_subnet(self.fmt, network, gateway_ip, cidr, ip_version=4) self._create_port(self.fmt, network['network']['id'], device_owner=constants.DEVICE_OWNER_DHCP) req = self.new_delete_request('subnets', subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) def test_delete_subnet_dhcp_port_associated_with_other_subnets(self): res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) subnet1 = self._make_subnet(self.fmt, network, '10.0.0.1', '10.0.0.0/24', ip_version=4) subnet2 = self._make_subnet(self.fmt, network, '10.0.1.1', '10.0.1.0/24', ip_version=4) res = self._create_port(self.fmt, network['network']['id'], device_owner=constants.DEVICE_OWNER_DHCP, fixed_ips=[ {'subnet_id': subnet1['subnet']['id']}, {'subnet_id': subnet2['subnet']['id']} ]) port = self.deserialize(self.fmt, res) expected_subnets = [subnet1['subnet']['id'], subnet2['subnet']['id']] self.assertEqual(expected_subnets, [s['subnet_id'] for s in port['port']['fixed_ips']]) req = self.new_delete_request('subnets', subnet1['subnet']['id']) res = req.get_response(self.api) self.assertEqual(204, res.status_int) port = self._show('ports', port['port']['id']) expected_subnets = [subnet2['subnet']['id']] self.assertEqual(expected_subnets, [s['subnet_id'] for s in port['port']['fixed_ips']]) req = self.new_delete_request('subnets', subnet2['subnet']['id']) res = req.get_response(self.api) self.assertEqual(204, res.status_int) port = self._show('ports', port['port']['id']) self.assertFalse(port['port']['fixed_ips']) def test_delete_subnet_port_exists_owned_by_other(self): with self.subnet() as subnet: with self.port(subnet=subnet): id = subnet['subnet']['id'] req = self.new_delete_request('subnets', id) res = req.get_response(self.api) data = self.deserialize(self.fmt, res) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) msg = str(lib_exc.SubnetInUse(subnet_id=id)) self.assertEqual(data['NeutronError']['message'], msg) def test_delete_subnet_with_other_subnet_on_network_still_in_use(self): with self.network() as network: with self.subnet(network=network) as subnet1,\ self.subnet(network=network, cidr='10.0.1.0/24') as subnet2: subnet1_id = subnet1['subnet']['id'] subnet2_id = subnet2['subnet']['id'] with self.port( subnet=subnet1, fixed_ips=[{'subnet_id': subnet1_id}]): req = self.new_delete_request('subnets', subnet2_id) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) def _create_slaac_subnet_and_port(self, port_owner=None): # Create an IPv6 SLAAC subnet and a port using that subnet res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) subnet = self._make_subnet(self.fmt, network, gateway='fe80::1', cidr='fe80::/64', ip_version=6, ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) kwargs = {} if port_owner: kwargs['device_owner'] = port_owner if port_owner in constants.ROUTER_INTERFACE_OWNERS: kwargs['fixed_ips'] = [{'ip_address': 'fe80::1'}] res = self._create_port(self.fmt, net_id=network['network']['id'], **kwargs) port = self.deserialize(self.fmt, res) self.assertEqual(1, len(port['port']['fixed_ips'])) # The port should have an address from the subnet req = self.new_show_request('ports', port['port']['id'], self.fmt) res = req.get_response(self.api) sport = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(1, len(sport['port']['fixed_ips'])) return subnet, port def test_delete_subnet_ipv6_slaac_port_exists(self): """Test IPv6 SLAAC subnet delete when a port is still using subnet.""" subnet, port = self._create_slaac_subnet_and_port() # Delete the subnet req = self.new_delete_request('subnets', subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) # The port should no longer have an address from the deleted subnet req = self.new_show_request('ports', port['port']['id'], self.fmt) res = req.get_response(self.api) sport = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(0, len(sport['port']['fixed_ips'])) def test_delete_subnet_ipv6_slaac_router_port_exists(self): """Test IPv6 SLAAC subnet delete with a router port using the subnet""" subnet, port = self._create_slaac_subnet_and_port( constants.DEVICE_OWNER_ROUTER_INTF) # Delete the subnet and assert that we get a HTTP 409 error req = self.new_delete_request('subnets', subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) # The subnet should still exist and the port should still have an # address from the subnet req = self.new_show_request('subnets', subnet['subnet']['id'], self.fmt) res = req.get_response(self.api) ssubnet = self.deserialize(self.fmt, req.get_response(self.api)) self.assertIsNotNone(ssubnet) req = self.new_show_request('ports', port['port']['id'], self.fmt) res = req.get_response(self.api) sport = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(1, len(sport['port']['fixed_ips'])) port_subnet_ids = [fip['subnet_id'] for fip in sport['port']['fixed_ips']] self.assertIn(subnet['subnet']['id'], port_subnet_ids) def test_delete_network(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' # Create new network res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) subnet = self._make_subnet(self.fmt, network, gateway_ip, cidr, ip_version=4) req = self.new_delete_request('networks', network['network']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) req = self.new_show_request('subnets', subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_create_subnet_bad_tenant(self): with self.network() as network: self._create_subnet(self.fmt, network['network']['id'], '10.0.2.0/24', webob.exc.HTTPNotFound.code, ip_version=4, tenant_id='bad_tenant_id', gateway_ip='10.0.2.1', device_owner='fake_owner', set_context=True) def test_create_subnet_as_admin(self): with self.network() as network: self._create_subnet(self.fmt, network['network']['id'], '10.0.2.0/24', webob.exc.HTTPCreated.code, ip_version=4, tenant_id='bad_tenant_id', gateway_ip='10.0.2.1', device_owner='fake_owner', set_context=False) def test_create_subnet_nonzero_cidr(self): # Pass None as gateway_ip to prevent ip auto allocation for gw # Previously gateway ip was allocated after validations, # so no errors were raised if gw ip was out of range. with self.subnet(cidr='10.129.122.5/8') as v1,\ self.subnet(cidr='11.129.122.5/15') as v2,\ self.subnet(cidr='12.129.122.5/16') as v3,\ self.subnet(cidr='13.129.122.5/18') as v4,\ self.subnet(cidr='14.129.122.5/22') as v5,\ self.subnet(cidr='15.129.122.5/24') as v6,\ self.subnet(cidr='16.129.122.5/28') as v7,\ self.subnet(cidr='17.129.122.5/32', gateway_ip=None, enable_dhcp=False) as v8: subs = (v1, v2, v3, v4, v5, v6, v7, v8) # the API should accept and correct these for users self.assertEqual('10.0.0.0/8', subs[0]['subnet']['cidr']) self.assertEqual('11.128.0.0/15', subs[1]['subnet']['cidr']) self.assertEqual('12.129.0.0/16', subs[2]['subnet']['cidr']) self.assertEqual('13.129.64.0/18', subs[3]['subnet']['cidr']) self.assertEqual('14.129.120.0/22', subs[4]['subnet']['cidr']) self.assertEqual('15.129.122.0/24', subs[5]['subnet']['cidr']) self.assertEqual('16.129.122.0/28', subs[6]['subnet']['cidr']) self.assertEqual('17.129.122.5/32', subs[7]['subnet']['cidr']) def _test_create_subnet_with_invalid_netmask_returns_400(self, *args): with self.network() as network: for cidr in args: ip_version = netaddr.IPNetwork(cidr).version self._create_subnet(self.fmt, network['network']['id'], cidr, webob.exc.HTTPClientError.code, ip_version=ip_version) def test_create_subnet_with_invalid_netmask_returns_400_ipv4(self): self._test_create_subnet_with_invalid_netmask_returns_400( '10.0.0.0/31', '10.0.0.0/32') def test_create_subnet_with_invalid_netmask_returns_400_ipv6(self): self._test_create_subnet_with_invalid_netmask_returns_400( 'cafe:cafe::/127', 'cafe:cafe::/128') def test_create_subnet_bad_ip_version(self): with self.network() as network: # Check bad IP version data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': 'abc', 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.2.1'}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_bad_ip_version_null(self): with self.network() as network: # Check bad IP version data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': None, 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.2.1'}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_bad_uuid(self): with self.network() as network: # Check invalid UUID data = {'subnet': {'network_id': None, 'cidr': '10.0.2.0/24', 'ip_version': 4, 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.2.1'}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_bad_boolean(self): with self.network() as network: # Check invalid boolean data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': '4', 'enable_dhcp': None, 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.2.1'}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_bad_pools(self): with self.network() as network: # Check allocation pools allocation_pools = [[{'end': '10.0.0.254'}], [{'start': '10.0.0.254'}], [{'start': '1000.0.0.254'}], [{'start': '10.0.0.2', 'end': '10.0.0.254'}, {'end': '10.0.0.254'}], None, [{'start': '10.0.0.200', 'end': '10.0.3.20'}], [{'start': '10.0.2.250', 'end': '10.0.3.5'}], [{'start': '10.0.2.10', 'end': '10.0.2.5'}], [{'start': '10.0.0.2', 'end': '10.0.0.3'}, {'start': '10.0.0.2', 'end': '10.0.0.3'}]] tenant_id = network['network']['tenant_id'] for pool in allocation_pools: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': '4', 'tenant_id': tenant_id, 'gateway_ip': '10.0.2.1', 'allocation_pools': pool}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_bad_nameserver(self): with self.network() as network: # Check nameservers nameserver_pools = [['1100.0.0.2'], ['1.1.1.2', '1.1000.1.3'], ['1.1.1.2', '1.1.1.2']] tenant_id = network['network']['tenant_id'] for nameservers in nameserver_pools: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': '4', 'tenant_id': tenant_id, 'gateway_ip': '10.0.2.1', 'dns_nameservers': nameservers}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_bad_hostroutes(self): with self.network() as network: # Check hostroutes hostroute_pools = [[{'destination': '100.0.0.0/24'}], [{'nexthop': '10.0.2.20'}], [{'nexthop': '10.0.2.20', 'destination': '100.0.0.0/8'}, {'nexthop': '10.0.2.20', 'destination': '100.0.0.0/8'}]] tenant_id = network['network']['tenant_id'] for hostroutes in hostroute_pools: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': '4', 'tenant_id': tenant_id, 'gateway_ip': '10.0.2.1', 'host_routes': hostroutes}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_defaults(self): gateway = '10.0.0.1' cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.254'}] enable_dhcp = True subnet = self._test_create_subnet() # verify cidr & gw have been correctly generated self.assertEqual(cidr, subnet['subnet']['cidr']) self.assertEqual(gateway, subnet['subnet']['gateway_ip']) self.assertEqual(enable_dhcp, subnet['subnet']['enable_dhcp']) self.assertEqual(allocation_pools, subnet['subnet']['allocation_pools']) def test_create_subnet_gw_values(self): cidr = '10.0.0.0/24' # Gateway is last IP in range gateway = '10.0.0.254' allocation_pools = [{'start': '10.0.0.1', 'end': '10.0.0.253'}] expected = {'gateway_ip': gateway, 'cidr': cidr, 'allocation_pools': allocation_pools} self._test_create_subnet(expected=expected, gateway_ip=gateway) # Gateway is first in subnet gateway = '10.0.0.1' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.254'}] expected = {'gateway_ip': gateway, 'cidr': cidr, 'allocation_pools': allocation_pools} self._test_create_subnet(expected=expected, gateway_ip=gateway) def test_create_subnet_ipv6_gw_values(self): cidr = '2001::/64' # Gateway is last IP in IPv6 DHCPv6 stateful subnet gateway = '2001::ffff:ffff:ffff:ffff' allocation_pools = [{'start': '2001::1', 'end': '2001::ffff:ffff:ffff:fffe'}] expected = {'gateway_ip': gateway, 'cidr': cidr, 'allocation_pools': allocation_pools} self._test_create_subnet(expected=expected, gateway_ip=gateway, cidr=cidr, ip_version=6, ipv6_ra_mode=constants.DHCPV6_STATEFUL, ipv6_address_mode=constants.DHCPV6_STATEFUL) # Gateway is first IP in IPv6 DHCPv6 stateful subnet gateway = '2001::1' allocation_pools = [{'start': '2001::2', 'end': '2001::ffff:ffff:ffff:ffff'}] expected = {'gateway_ip': gateway, 'cidr': cidr, 'allocation_pools': allocation_pools} self._test_create_subnet(expected=expected, gateway_ip=gateway, cidr=cidr, ip_version=6, ipv6_ra_mode=constants.DHCPV6_STATEFUL, ipv6_address_mode=constants.DHCPV6_STATEFUL) # If gateway_ip is not specified, allocate first IP from the subnet expected = {'gateway_ip': gateway, 'cidr': cidr} self._test_create_subnet(expected=expected, cidr=cidr, ip_version=6, ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) @testtools.skipIf(tools.is_bsd(), 'bug/1484837') def test_create_subnet_ipv6_pd_gw_values(self): cidr = constants.PROVISIONAL_IPV6_PD_PREFIX # Gateway is last IP in IPv6 DHCPv6 Stateless subnet gateway = '::ffff:ffff:ffff:ffff' allocation_pools = [{'start': '::1', 'end': '::ffff:ffff:ffff:fffe'}] expected = {'gateway_ip': gateway, 'cidr': cidr, 'allocation_pools': allocation_pools} self._test_create_subnet(expected=expected, gateway_ip=gateway, cidr=cidr, ip_version=6, ipv6_ra_mode=constants.DHCPV6_STATELESS, ipv6_address_mode=constants.DHCPV6_STATELESS) # Gateway is first IP in IPv6 DHCPv6 Stateless subnet gateway = '::1' allocation_pools = [{'start': '::2', 'end': '::ffff:ffff:ffff:ffff'}] expected = {'gateway_ip': gateway, 'cidr': cidr, 'allocation_pools': allocation_pools} self._test_create_subnet(expected=expected, gateway_ip=gateway, cidr=cidr, ip_version=6, ipv6_ra_mode=constants.DHCPV6_STATELESS, ipv6_address_mode=constants.DHCPV6_STATELESS) # If gateway_ip is not specified, allocate first IP from the subnet expected = {'gateway_ip': gateway, 'cidr': cidr} self._test_create_subnet(expected=expected, cidr=cidr, ip_version=6, ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) def test_create_subnet_gw_outside_cidr_returns_201(self): with self.network() as network: self._create_subnet(self.fmt, network['network']['id'], '10.0.0.0/24', webob.exc.HTTPCreated.code, gateway_ip='100.0.0.1') def test_create_subnet_gw_is_nw_addr_returns_400(self): with self.network() as network: self._create_subnet(self.fmt, network['network']['id'], '10.0.0.0/24', webob.exc.HTTPClientError.code, gateway_ip='10.0.0.0') def test_create_subnet_gw_is_broadcast_addr_returns_400(self): with self.network() as network: self._create_subnet(self.fmt, network['network']['id'], '10.0.0.0/24', webob.exc.HTTPClientError.code, gateway_ip='10.0.0.255') def test_create_subnet_gw_of_network_returns_400(self): with self.network() as network: self._create_subnet(self.fmt, network['network']['id'], '10.0.0.0/24', webob.exc.HTTPClientError.code, gateway_ip='10.0.0.0') def test_create_subnet_gw_bcast_returns_400(self): with self.network() as network: self._create_subnet(self.fmt, network['network']['id'], '10.0.0.0/24', webob.exc.HTTPClientError.code, gateway_ip='10.0.0.255') def test_create_subnet_with_allocation_pool(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.100'}] self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, allocation_pools=allocation_pools) def test_create_subnet_with_none_gateway(self): cidr = '10.0.0.0/24' self._test_create_subnet(gateway_ip=None, cidr=cidr) def test_create_subnet_with_none_gateway_fully_allocated(self): cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.1', 'end': '10.0.0.254'}] self._test_create_subnet(gateway_ip=None, cidr=cidr, allocation_pools=allocation_pools) def test_subnet_with_allocation_range(self): with self.network() as network: net_id = network['network']['id'] data = {'subnet': {'network_id': net_id, 'cidr': '10.0.0.0/24', 'ip_version': 4, 'gateway_ip': '10.0.0.1', 'tenant_id': network['network']['tenant_id'], 'allocation_pools': [{'start': '10.0.0.100', 'end': '10.0.0.120'}]}} subnet_req = self.new_create_request('subnets', data) subnet = self.deserialize(self.fmt, subnet_req.get_response(self.api)) # Check fixed IP not in allocation range kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id'], 'ip_address': '10.0.0.10'}]} res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) port = self.deserialize(self.fmt, res) # delete the port self._delete('ports', port['port']['id']) # Check when fixed IP is gateway kwargs = {"fixed_ips": [{'subnet_id': subnet['subnet']['id'], 'ip_address': '10.0.0.1'}]} res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) port = self.deserialize(self.fmt, res) # delete the port self._delete('ports', port['port']['id']) def test_create_subnet_with_none_gateway_allocation_pool(self): cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.100'}] self._test_create_subnet(gateway_ip=None, cidr=cidr, allocation_pools=allocation_pools) def test_create_subnet_with_v6_allocation_pool(self): gateway_ip = 'fe80::1' cidr = 'fe80::/80' allocation_pools = [{'start': 'fe80::2', 'end': 'fe80::ffff:fffa:ffff'}] self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, ip_version=6, allocation_pools=allocation_pools) @testtools.skipIf(tools.is_bsd(), 'bug/1484837') def test_create_subnet_with_v6_pd_allocation_pool(self): gateway_ip = '::1' cidr = constants.PROVISIONAL_IPV6_PD_PREFIX allocation_pools = [{'start': '::2', 'end': '::ffff:ffff:ffff:fffe'}] self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, ip_version=6, allocation_pools=allocation_pools) def test_create_subnet_with_large_allocation_pool(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/8' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.100'}, {'start': '10.1.0.0', 'end': '10.200.0.100'}] self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, allocation_pools=allocation_pools) def test_create_subnet_multiple_allocation_pools(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.100'}, {'start': '10.0.0.110', 'end': '10.0.0.150'}] self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, allocation_pools=allocation_pools) def test_create_subnet_with_dhcp_disabled(self): enable_dhcp = False self._test_create_subnet(enable_dhcp=enable_dhcp) def test_create_subnet_default_gw_conflict_allocation_pool_returns_409( self): cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.1', 'end': '10.0.0.5'}] with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: self._test_create_subnet(cidr=cidr, allocation_pools=allocation_pools) self.assertEqual(webob.exc.HTTPConflict.code, ctx_manager.exception.code) def test_create_subnet_gateway_in_allocation_pool_returns_409(self): gateway_ip = '10.0.0.50' cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.1', 'end': '10.0.0.100'}] with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, allocation_pools=allocation_pools) self.assertEqual(webob.exc.HTTPConflict.code, ctx_manager.exception.code) def test_create_subnet_overlapping_allocation_pools_returns_409(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.150'}, {'start': '10.0.0.140', 'end': '10.0.0.180'}] with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, allocation_pools=allocation_pools) self.assertEqual(webob.exc.HTTPConflict.code, ctx_manager.exception.code) def test_create_subnet_invalid_allocation_pool_returns_400(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.256'}] with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, allocation_pools=allocation_pools) self.assertEqual(webob.exc.HTTPClientError.code, ctx_manager.exception.code) def test_create_subnet_out_of_range_allocation_pool_returns_400(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.1.6'}] with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, allocation_pools=allocation_pools) self.assertEqual(webob.exc.HTTPClientError.code, ctx_manager.exception.code) def test_create_subnet_shared_returns_400(self): cidr = '10.0.0.0/24' with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: self._test_create_subnet(cidr=cidr, shared=True) self.assertEqual(webob.exc.HTTPClientError.code, ctx_manager.exception.code) def test_create_subnet_inconsistent_ipv6_cidrv4(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': 6, 'tenant_id': network['network']['tenant_id']}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_inconsistent_ipv4_cidrv6(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': 'fe80::0/80', 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_inconsistent_ipv4_gatewayv6(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': 4, 'gateway_ip': 'fe80::1', 'tenant_id': network['network']['tenant_id']}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_inconsistent_ipv6_gatewayv4(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': 'fe80::0/80', 'ip_version': 6, 'gateway_ip': '192.168.0.1', 'tenant_id': network['network']['tenant_id']}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_inconsistent_ipv6_dns_v4(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': 'fe80::0/80', 'ip_version': 6, 'dns_nameservers': ['192.168.0.1'], 'tenant_id': network['network']['tenant_id']}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_inconsistent_ipv4_hostroute_dst_v6(self): host_routes = [{'destination': 'fe80::0/48', 'nexthop': '10.0.2.20'}] with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': 4, 'host_routes': host_routes, 'tenant_id': network['network']['tenant_id']}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_inconsistent_ipv4_hostroute_np_v6(self): host_routes = [{'destination': '172.16.0.0/24', 'nexthop': 'fe80::1'}] with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': 4, 'host_routes': host_routes, 'tenant_id': network['network']['tenant_id']}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def _test_validate_subnet_ipv6_modes(self, cur_subnet=None, expect_success=True, **modes): plugin = directory.get_plugin() ctx = context.get_admin_context() new_subnet = {'ip_version': 6, 'cidr': 'fe80::/64', 'enable_dhcp': True, 'ipv6_address_mode': None, 'ipv6_ra_mode': None} for mode, value in modes.items(): new_subnet[mode] = value if expect_success: plugin._validate_subnet(ctx, new_subnet, cur_subnet) else: self.assertRaises(lib_exc.InvalidInput, plugin._validate_subnet, ctx, new_subnet, cur_subnet) def _test_validate_subnet_ipv6_pd_modes(self, cur_subnet=None, expect_success=True, **modes): plugin = directory.get_plugin() ctx = context.get_admin_context() new_subnet = {'ip_version': 6, 'cidr': constants.PROVISIONAL_IPV6_PD_PREFIX, 'enable_dhcp': True, 'ipv6_address_mode': None, 'ipv6_ra_mode': None} for mode, value in modes.items(): new_subnet[mode] = value if expect_success: plugin._validate_subnet(ctx, new_subnet, cur_subnet) else: self.assertRaises(lib_exc.InvalidInput, plugin._validate_subnet, ctx, new_subnet, cur_subnet) def test_create_subnet_ipv6_ra_modes(self): # Test all RA modes with no address mode specified for ra_mode in constants.IPV6_MODES: self._test_validate_subnet_ipv6_modes( ipv6_ra_mode=ra_mode) self._test_validate_subnet_ipv6_pd_modes( ipv6_ra_mode=ra_mode) def test_create_subnet_ipv6_addr_modes(self): # Test all address modes with no RA mode specified for addr_mode in constants.IPV6_MODES: self._test_validate_subnet_ipv6_modes( ipv6_address_mode=addr_mode) self._test_validate_subnet_ipv6_pd_modes( ipv6_address_mode=addr_mode) def test_create_subnet_ipv6_same_ra_and_addr_modes(self): # Test all ipv6 modes with ra_mode==addr_mode for ipv6_mode in constants.IPV6_MODES: self._test_validate_subnet_ipv6_modes( ipv6_ra_mode=ipv6_mode, ipv6_address_mode=ipv6_mode) self._test_validate_subnet_ipv6_pd_modes( ipv6_ra_mode=ipv6_mode, ipv6_address_mode=ipv6_mode) def test_create_subnet_ipv6_different_ra_and_addr_modes(self): # Test all ipv6 modes with ra_mode!=addr_mode for ra_mode, addr_mode in itertools.permutations( constants.IPV6_MODES, 2): self._test_validate_subnet_ipv6_modes( expect_success=not (ra_mode and addr_mode), ipv6_ra_mode=ra_mode, ipv6_address_mode=addr_mode) self._test_validate_subnet_ipv6_pd_modes( expect_success=not (ra_mode and addr_mode), ipv6_ra_mode=ra_mode, ipv6_address_mode=addr_mode) def test_create_subnet_ipv6_out_of_cidr_global(self): gateway_ip = '2000::1' cidr = '2001::/64' subnet = self._test_create_subnet( gateway_ip=gateway_ip, cidr=cidr, ip_version=constants.IP_VERSION_6, ipv6_ra_mode=constants.DHCPV6_STATEFUL, ipv6_address_mode=constants.DHCPV6_STATEFUL) self.assertEqual(constants.IP_VERSION_6, subnet['subnet']['ip_version']) self.assertEqual(gateway_ip, subnet['subnet']['gateway_ip']) self.assertEqual(cidr, subnet['subnet']['cidr']) def _create_subnet_ipv6_gw(self, gateway_ip, cidr): subnet = self._test_create_subnet( gateway_ip=gateway_ip, cidr=cidr, ip_version=constants.IP_VERSION_6, ipv6_ra_mode=constants.DHCPV6_STATEFUL, ipv6_address_mode=constants.DHCPV6_STATEFUL) self.assertEqual(constants.IP_VERSION_6, subnet['subnet']['ip_version']) if gateway_ip and gateway_ip[-3:] == '::0': self.assertEqual(gateway_ip[:-1], subnet['subnet']['gateway_ip']) else: self.assertEqual(gateway_ip, subnet['subnet']['gateway_ip']) self.assertEqual(cidr, subnet['subnet']['cidr']) def test_create_subnet_ipv6_gw_is_nw_start_addr(self): gateway_ip = '2001::0' cidr = '2001::/64' self._create_subnet_ipv6_gw(gateway_ip, cidr) def test_create_subnet_ipv6_gw_is_nw_start_addr_canonicalize(self): gateway_ip = '2001::' cidr = '2001::/64' self._create_subnet_ipv6_gw(gateway_ip, cidr) def test_create_subnet_ipv6_gw_is_nw_end_addr(self): gateway_ip = '2001::ffff' cidr = '2001::/112' self._create_subnet_ipv6_gw(gateway_ip, cidr) def test_create_subnet_ipv6_out_of_cidr_lla(self): gateway_ip = 'fe80::1' cidr = '2001::/64' self._test_create_subnet( gateway_ip=gateway_ip, cidr=cidr, ip_version=6, ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) def test_create_subnet_ipv6_first_ip_owned_by_router(self): cidr = '2001::/64' with self.network() as network: net_id = network['network']['id'] with self.subnet(network=network, ip_version=constants.IP_VERSION_6, cidr=cidr) as subnet: fixed_ip = [{'subnet_id': subnet['subnet']['id'], 'ip_address': '2001::'}] kwargs = {'fixed_ips': fixed_ip, 'tenant_id': 'tenant_id', 'device_id': 'fake_device', 'device_owner': constants.DEVICE_OWNER_ROUTER_GW} res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) def test_create_subnet_ipv6_first_ip_owned_by_non_router(self): cidr = '2001::/64' with self.network() as network: net_id = network['network']['id'] with self.subnet(network=network, ip_version=constants.IP_VERSION_6, cidr=cidr) as subnet: fixed_ip = [{'subnet_id': subnet['subnet']['id'], 'ip_address': '2001::'}] kwargs = {'fixed_ips': fixed_ip, 'tenant_id': 'tenant_id', 'device_id': 'fake_device', 'device_owner': 'fake_owner'} res = self._create_port(self.fmt, net_id=net_id, **kwargs) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_ipv6_attributes_no_dhcp_enabled(self): gateway_ip = 'fe80::1' cidr = 'fe80::/64' with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: for mode in constants.IPV6_MODES: self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, ip_version=6, enable_dhcp=False, ipv6_ra_mode=mode, ipv6_address_mode=mode) self.assertEqual(webob.exc.HTTPClientError.code, ctx_manager.exception.code) def test_create_subnet_invalid_ipv6_ra_mode(self): gateway_ip = 'fe80::1' cidr = 'fe80::/80' with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, ip_version=6, ipv6_ra_mode='foo', ipv6_address_mode='slaac') self.assertEqual(webob.exc.HTTPClientError.code, ctx_manager.exception.code) def test_create_subnet_invalid_ipv6_address_mode(self): gateway_ip = 'fe80::1' cidr = 'fe80::/80' with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, ip_version=6, ipv6_ra_mode='slaac', ipv6_address_mode='baz') self.assertEqual(webob.exc.HTTPClientError.code, ctx_manager.exception.code) def test_create_subnet_ipv6_ra_mode_ip_version_4(self): cidr = '10.0.2.0/24' with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: self._test_create_subnet(cidr=cidr, ip_version=4, ipv6_ra_mode=constants.DHCPV6_STATEFUL) self.assertEqual(webob.exc.HTTPClientError.code, ctx_manager.exception.code) def test_create_subnet_ipv6_address_mode_ip_version_4(self): cidr = '10.0.2.0/24' with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: self._test_create_subnet( cidr=cidr, ip_version=4, ipv6_address_mode=constants.DHCPV6_STATEFUL) self.assertEqual(webob.exc.HTTPClientError.code, ctx_manager.exception.code) def _test_create_subnet_ipv6_auto_addr_with_port_on_network( self, addr_mode, device_owner=DEVICE_OWNER_COMPUTE, insert_db_reference_error=False, insert_port_not_found=False, insert_address_allocated=False): # Create a network with one IPv4 subnet and one port with self.network() as network,\ self.subnet(network=network) as v4_subnet,\ self.port(subnet=v4_subnet, device_owner=device_owner) as port: if insert_db_reference_error: orig_fn = orm.Session.add def db_ref_err_for_ipalloc(s, instance): if instance.__class__.__name__ == 'IPAllocation': # tweak port_id to cause a FK violation, # thus DBReferenceError instance.port_id = 'nonexistent' return orig_fn(s, instance) mock.patch.object(orm.Session, 'add', side_effect=db_ref_err_for_ipalloc, autospec=True).start() v6_subnet = {'ip_version': 6, 'cidr': 'fe80::/64', 'gateway_ip': 'fe80::1', 'tenant_id': v4_subnet['subnet']['tenant_id']} mock.patch.object(db_base_plugin_common.DbBasePluginCommon, '_get_subnet', return_value=v6_subnet).start() # Add an IPv6 auto-address subnet to the network with mock.patch.object(directory.get_plugin(), 'update_port') as mock_updated_port: if insert_port_not_found: mock_updated_port.side_effect = lib_exc.PortNotFound( port_id=port['port']['id']) if insert_address_allocated: mock.patch.object( ipam_driver.NeutronDbSubnet, '_verify_ip', side_effect=ipam_exc.IpAddressAlreadyAllocated( subnet_id=mock.ANY, ip=mock.ANY)).start() v6_subnet = self._make_subnet(self.fmt, network, 'fe80::1', 'fe80::/64', ip_version=6, ipv6_ra_mode=addr_mode, ipv6_address_mode=addr_mode) if (insert_db_reference_error or insert_address_allocated or device_owner == constants.DEVICE_OWNER_ROUTER_SNAT or device_owner in constants.ROUTER_INTERFACE_OWNERS): # DVR SNAT, router interfaces and DHCP ports should not have # been updated with addresses from the new auto-address subnet self.assertEqual(1, len(port['port']['fixed_ips'])) else: # Confirm that the port has been updated with an address # from the new auto-address subnet mock_updated_port.assert_called_with(mock.ANY, port['port']['id'], mock.ANY) req = self.new_show_request('ports', port['port']['id'], self.fmt) sport = self.deserialize(self.fmt, req.get_response(self.api)) fixed_ips = sport['port']['fixed_ips'] self.assertEqual(2, len(fixed_ips)) self.assertIn(v6_subnet['subnet']['id'], [fixed_ip['subnet_id'] for fixed_ip in fixed_ips]) def test_create_subnet_ipv6_slaac_with_port_on_network(self): self._test_create_subnet_ipv6_auto_addr_with_port_on_network( constants.IPV6_SLAAC) def test_create_subnet_dhcpv6_stateless_with_port_on_network(self): self._test_create_subnet_ipv6_auto_addr_with_port_on_network( constants.DHCPV6_STATELESS) def test_create_subnet_ipv6_slaac_with_dhcp_port_on_network(self): self._test_create_subnet_ipv6_auto_addr_with_port_on_network( constants.IPV6_SLAAC, device_owner=constants.DEVICE_OWNER_DHCP) def test_create_subnet_dhcpv6_stateless_with_ip_already_allocated(self): self._test_create_subnet_ipv6_auto_addr_with_port_on_network( constants.DHCPV6_STATELESS, insert_address_allocated=True) def test_create_subnet_ipv6_slaac_with_ip_already_allocated(self): self._test_create_subnet_ipv6_auto_addr_with_port_on_network( constants.IPV6_SLAAC, insert_address_allocated=True) def test_create_subnet_ipv6_slaac_with_router_intf_on_network(self): self._test_create_subnet_ipv6_auto_addr_with_port_on_network( constants.IPV6_SLAAC, device_owner=constants.DEVICE_OWNER_ROUTER_INTF) def test_create_subnet_ipv6_slaac_with_snat_intf_on_network(self): self._test_create_subnet_ipv6_auto_addr_with_port_on_network( constants.IPV6_SLAAC, device_owner=constants.DEVICE_OWNER_ROUTER_SNAT) def test_create_subnet_ipv6_slaac_with_db_reference_error(self): self._test_create_subnet_ipv6_auto_addr_with_port_on_network( constants.IPV6_SLAAC, insert_db_reference_error=True) def test_create_subnet_ipv6_slaac_with_port_not_found(self): self._test_create_subnet_ipv6_auto_addr_with_port_on_network( constants.IPV6_SLAAC, insert_port_not_found=True) def test_bulk_create_subnet_ipv6_auto_addr_with_port_on_network(self): # Create a network with one IPv4 subnet and one port with self.network() as network,\ self.subnet(network=network) as v4_subnet,\ self.port(subnet=v4_subnet, device_owner=constants.DEVICE_OWNER_DHCP) as port: # Add 2 IPv6 auto-address subnets in a bulk request self._create_subnet_bulk( self.fmt, 2, network['network']['id'], 'test', ip_version=constants.IP_VERSION_6, ipv6_mode=constants.IPV6_SLAAC) # Confirm that the port has been updated with addresses # from the new auto-address subnets req = self.new_show_request('ports', port['port']['id'], self.fmt) sport = self.deserialize(self.fmt, req.get_response(self.api)) fixed_ips = sport['port']['fixed_ips'] self.assertEqual(3, len(fixed_ips)) def test_update_subnet_no_gateway(self): with self.subnet() as subnet: data = {'subnet': {'gateway_ip': '10.0.0.1'}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['subnet']['gateway_ip'], res['subnet']['gateway_ip']) data = {'subnet': {'gateway_ip': None}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertIsNone(data['subnet']['gateway_ip']) def test_subnet_usable_after_update(self): with self.subnet() as subnet: data = {'subnet': {'name': 'newname'}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['subnet']['name'], res['subnet']['name']) with self.port(subnet=subnet): pass def test_update_subnet(self): with self.subnet() as subnet: data = {'subnet': {'gateway_ip': '10.0.0.1'}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['subnet']['gateway_ip'], res['subnet']['gateway_ip']) def test_update_subnet_adding_additional_host_routes_and_dns(self): host_routes = [{'destination': '172.16.0.0/24', 'nexthop': '10.0.2.2'}] with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': 4, 'dns_nameservers': ['192.168.0.1'], 'host_routes': host_routes, 'tenant_id': network['network']['tenant_id']}} subnet_req = self.new_create_request('subnets', data) res = self.deserialize(self.fmt, subnet_req.get_response(self.api)) host_routes = [{'destination': '172.16.0.0/24', 'nexthop': '10.0.2.2'}, {'destination': '192.168.0.0/24', 'nexthop': '10.0.2.3'}] dns_nameservers = ['192.168.0.1', '192.168.0.2'] data = {'subnet': {'host_routes': host_routes, 'dns_nameservers': dns_nameservers}} req = self.new_update_request('subnets', data, res['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual( sorted(res['subnet']['host_routes'], key=helpers.safe_sort_key), sorted(host_routes, key=helpers.safe_sort_key)) self.assertEqual(dns_nameservers, res['subnet']['dns_nameservers']) def _test_subnet_update_ipv4_and_ipv6_pd_subnets(self, ra_addr_mode): # Test prefix update for IPv6 PD subnet # when the network has both IPv4 and IPv6 PD subnets. # Created two networks. First network has IPv4 and IPv6 PD subnets. # Second network has IPv4 subnet. A port is created on each network. # When update_subnet called for PD subnet with new prefix, port on # network with PD subnet should get new IPV6 address, but IPv4 # addresses should remain same. # And update_port should be called only for this port and with # v4 subnet along with v4 address and v6 pd subnet as fixed_ips. orig_update_port = self.plugin.update_port with self.network() as network: with self.subnet(network=network), ( mock.patch.object(self.plugin, 'update_port')) as update_port: # Create port on second network network2 = self._make_network(self.fmt, 'net2', True) self._make_subnet(self.fmt, network2, "1.1.1.1", "1.1.1.0/24", ip_version=4) self._make_port(self.fmt, net_id=network2['network']['id']) subnet = self._make_v6_subnet( network, ra_addr_mode, ipv6_pd=True) port = self._make_port(self.fmt, subnet['subnet']['network_id']) port_dict = port['port'] # When update_port called, port should have below fips fips = port_dict['fixed_ips'] for fip in fips: if fip['subnet_id'] == subnet['subnet']['id']: fip.pop('ip_address') def mock_update_port(context, id, port): self.assertEqual(port_dict['id'], id) self.assertEqual(fips, port['port']['fixed_ips']) orig_update_port(context, id, port) update_port.side_effect = mock_update_port # update subnet with new prefix prefix = '2001::/64' data = {'subnet': {'cidr': prefix}} self.plugin.update_subnet(context.get_admin_context(), subnet['subnet']['id'], data) # create expected fixed_ips port_mac = port_dict['mac_address'] eui_addr = str(netutils.get_ipv6_addr_by_EUI64(prefix, port_mac)) ips = port_dict['fixed_ips'] for fip in ips: if fip['subnet_id'] == subnet['subnet']['id']: fip['ip_address'] = eui_addr # check if port got IPv6 address with new prefix req = self.new_show_request('ports', port['port']['id'], self.fmt) updated_port = self.deserialize(self.fmt, req.get_response(self.api)) new_ips = updated_port['port']['fixed_ips'] self.assertEqual(2, len(ips)) self.assertEqual(ips, new_ips) def test_subnet_update_ipv4_and_ipv6_pd_slaac_subnets(self): self._test_subnet_update_ipv4_and_ipv6_pd_subnets( ra_addr_mode=constants.IPV6_SLAAC) def test_subnet_update_ipv4_and_ipv6_pd_v6stateless_subnets(self): self._test_subnet_update_ipv4_and_ipv6_pd_subnets( ra_addr_mode=constants.DHCPV6_STATELESS) def test_update_subnet_shared_returns_400(self): with self.network(shared=True) as network: with self.subnet(network=network) as subnet: data = {'subnet': {'shared': True}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_gw_outside_cidr_returns_200(self): with self.network() as network: with self.subnet(network=network) as subnet: data = {'subnet': {'gateway_ip': '100.0.0.1'}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPOk.code, res.status_int) def test_update_subnet_gw_ip_in_use_by_router_returns_409(self): with self.network() as network: with self.subnet(network=network, allocation_pools=[{'start': '10.0.0.2', 'end': '10.0.0.8'}]) as subnet: s = subnet['subnet'] with self.port( subnet=subnet, fixed_ips=[{'subnet_id': s['id'], 'ip_address': s['gateway_ip']}] ) as port: # this protection only applies to router ports so we need # to make this port belong to a router ctx = context.get_admin_context() with db_api.context_manager.writer.using(ctx): router = l3_models.Router() ctx.session.add(router) rp = l3_obj.RouterPort(ctx, router_id=router.id, port_id=port['port']['id']) rp.create() data = {'subnet': {'gateway_ip': '10.0.0.99'}} req = self.new_update_request('subnets', data, s['id']) res = req.get_response(self.api) self.assertEqual(409, res.status_int) # should work fine if it's not a router port rp.delete() with db_api.context_manager.writer.using(ctx): ctx.session.delete(router) res = req.get_response(self.api) self.assertEqual(res.status_int, 200) def test_update_subnet_inconsistent_ipv4_gatewayv6(self): with self.network() as network: with self.subnet(network=network) as subnet: data = {'subnet': {'gateway_ip': 'fe80::1'}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_inconsistent_ipv6_gatewayv4(self): with self.network() as network: with self.subnet(network=network, ip_version=6, cidr='fe80::/48') as subnet: data = {'subnet': {'gateway_ip': '10.1.1.1'}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_inconsistent_ipv4_dns_v6(self): dns_nameservers = ['fe80::1'] with self.network() as network: with self.subnet(network=network) as subnet: data = {'subnet': {'dns_nameservers': dns_nameservers}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_inconsistent_ipv6_hostroute_dst_v4(self): host_routes = [{'destination': 'fe80::0/48', 'nexthop': '10.0.2.20'}] with self.network() as network: with self.subnet(network=network, ip_version=6, cidr='fe80::/48') as subnet: data = {'subnet': {'host_routes': host_routes}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_inconsistent_ipv6_hostroute_np_v4(self): host_routes = [{'destination': '172.16.0.0/24', 'nexthop': 'fe80::1'}] with self.network() as network: with self.subnet(network=network, ip_version=6, cidr='fe80::/48') as subnet: data = {'subnet': {'host_routes': host_routes}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_gateway_in_allocation_pool_returns_409(self): allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.254'}] with self.network() as network: with self.subnet(network=network, allocation_pools=allocation_pools, cidr='10.0.0.0/24') as subnet: data = {'subnet': {'gateway_ip': '10.0.0.50'}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_subnet_ipv6_attributes_fails(self): with self.subnet(ip_version=6, cidr='fe80::/64', ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: data = {'subnet': {'ipv6_ra_mode': constants.DHCPV6_STATEFUL, 'ipv6_address_mode': constants.DHCPV6_STATEFUL}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_ipv6_ra_mode_fails(self): with self.subnet(ip_version=6, cidr='fe80::/64', ipv6_ra_mode=constants.IPV6_SLAAC) as subnet: data = {'subnet': {'ipv6_ra_mode': constants.DHCPV6_STATEFUL}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_ipv6_address_mode_fails(self): with self.subnet(ip_version=6, cidr='fe80::/64', ipv6_address_mode=constants.IPV6_SLAAC) as subnet: data = {'subnet': {'ipv6_address_mode': constants.DHCPV6_STATEFUL}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_ipv6_cannot_disable_dhcp(self): with self.subnet(ip_version=6, cidr='fe80::/64', ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: data = {'subnet': {'enable_dhcp': False}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_ipv6_ra_mode_ip_version_4(self): with self.network() as network: with self.subnet(network=network) as subnet: data = {'subnet': {'ipv6_ra_mode': constants.DHCPV6_STATEFUL}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_ipv6_address_mode_ip_version_4(self): with self.network() as network: with self.subnet(network=network) as subnet: data = {'subnet': {'ipv6_address_mode': constants.DHCPV6_STATEFUL}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def _verify_updated_subnet_allocation_pools(self, res, with_gateway_ip): res = self.deserialize(self.fmt, res) self.assertEqual(2, len(res['subnet']['allocation_pools'])) res_vals = ( list(res['subnet']['allocation_pools'][0].values()) + list(res['subnet']['allocation_pools'][1].values()) ) for pool_val in ['10', '20', '30', '40']: self.assertIn('192.168.0.%s' % (pool_val), res_vals) if with_gateway_ip: self.assertEqual('192.168.0.9', (res['subnet']['gateway_ip'])) def _test_update_subnet_allocation_pools(self, with_gateway_ip=False): """Test that we can successfully update with sane params. This will create a subnet with specified allocation_pools Then issue an update (PUT) to update these using correct (i.e. non erroneous) params. Finally retrieve the updated subnet and verify. """ allocation_pools = [{'start': '192.168.0.2', 'end': '192.168.0.254'}] with self.network() as network: with self.subnet(network=network, allocation_pools=allocation_pools, cidr='192.168.0.0/24') as subnet: data = {'subnet': {'allocation_pools': [ {'start': '192.168.0.10', 'end': '192.168.0.20'}, {'start': '192.168.0.30', 'end': '192.168.0.40'}]}} if with_gateway_ip: data['subnet']['gateway_ip'] = '192.168.0.9' req = self.new_update_request('subnets', data, subnet['subnet']['id']) #check res code and contents res = req.get_response(self.api) self.assertEqual(200, res.status_code) self._verify_updated_subnet_allocation_pools(res, with_gateway_ip) #GET subnet to verify DB updated correctly req = self.new_show_request('subnets', subnet['subnet']['id'], self.fmt) res = req.get_response(self.api) self._verify_updated_subnet_allocation_pools(res, with_gateway_ip) def test_update_subnet_allocation_pools(self): self._test_update_subnet_allocation_pools() def test_update_subnet_allocation_pools_and_gateway_ip(self): self._test_update_subnet_allocation_pools(with_gateway_ip=True) #updating alloc pool to something outside subnet.cidr def test_update_subnet_allocation_pools_invalid_pool_for_cidr(self): """Test update alloc pool to something outside subnet.cidr. This makes sure that an erroneous allocation_pool specified in a subnet update (outside subnet cidr) will result in an error. """ allocation_pools = [{'start': '192.168.0.2', 'end': '192.168.0.254'}] with self.network() as network: with self.subnet(network=network, allocation_pools=allocation_pools, cidr='192.168.0.0/24') as subnet: data = {'subnet': {'allocation_pools': [ {'start': '10.0.0.10', 'end': '10.0.0.20'}]}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) #updating alloc pool on top of existing subnet.gateway_ip def test_update_subnet_allocation_pools_over_gateway_ip_returns_409(self): allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.254'}] with self.network() as network: with self.subnet(network=network, allocation_pools=allocation_pools, cidr='10.0.0.0/24') as subnet: data = {'subnet': {'allocation_pools': [ {'start': '10.0.0.1', 'end': '10.0.0.254'}]}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_subnet_allocation_pools_invalid_returns_400(self): allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.254'}] with self.network() as network: with self.subnet(network=network, allocation_pools=allocation_pools, cidr='10.0.0.0/24') as subnet: # Check allocation pools invalid_pools = [[{'end': '10.0.0.254'}], [{'start': '10.0.0.254'}], [{'start': '1000.0.0.254'}], [{'start': '10.0.0.2', 'end': '10.0.0.254'}, {'end': '10.0.0.254'}], None, [{'start': '10.0.0.200', 'end': '10.0.3.20'}], [{'start': '10.0.2.250', 'end': '10.0.3.5'}], [{'start': '10.0.0.0', 'end': '10.0.0.50'}], [{'start': '10.0.2.10', 'end': '10.0.2.5'}], [{'start': 'fe80::2', 'end': 'fe80::ffff'}]] for pool in invalid_pools: data = {'subnet': {'allocation_pools': pool}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_allocation_pools_overlapping_returns_409(self): allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.254'}] with self.network() as network: with self.subnet(network=network, allocation_pools=allocation_pools, cidr='10.0.0.0/24') as subnet: data = {'subnet': {'allocation_pools': [ {'start': '10.0.0.20', 'end': '10.0.0.40'}, {'start': '10.0.0.30', 'end': '10.0.0.50'}]}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_subnets_native_quotas(self): quota = 1 cfg.CONF.set_override('quota_subnet', quota, group='QUOTAS') with self.network() as network: res = self._create_subnet( self.fmt, network['network']['id'], '10.0.0.0/24', tenant_id=network['network']['tenant_id']) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) res = self._create_subnet( self.fmt, network['network']['id'], '10.1.0.0/24', tenant_id=network['network']['tenant_id']) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_subnets_bulk_native_quotas(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk subnet create") quota = 4 cfg.CONF.set_override('quota_subnet', quota, group='QUOTAS') with self.network() as network: res = self._create_subnet_bulk(self.fmt, quota + 1, network['network']['id'], 'test') self._validate_behavior_on_bulk_failure( res, 'subnets', errcode=webob.exc.HTTPConflict.code) def test_show_subnet(self): with self.network() as network: with self.subnet(network=network) as subnet: req = self.new_show_request('subnets', subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(res['subnet']['id'], subnet['subnet']['id']) self.assertEqual(res['subnet']['network_id'], network['network']['id']) def test_get_subnets_count(self): with self.network() as network: with self.subnet(network=network, gateway_ip='10.0.0.1', cidr='10.0.0.0/24'),\ self.subnet(network=network, gateway_ip='10.0.1.1', cidr='10.0.1.0/24'),\ self.subnet(network=network, gateway_ip='10.0.2.1', cidr='10.0.2.0/24'): project_id = network['network']['project_id'] ctx = context.Context(user_id=None, tenant_id=project_id, is_admin=False) pl = directory.get_plugin() count = pl.get_subnets_count( ctx, filters={'project_id': [project_id]}) self.assertEqual(3, count) def test_get_subnets_count_filter_by_project_id(self): project_id = uuidutils.generate_uuid() with self.network() as network: with self.subnet(network=network, gateway_ip='10.0.0.1', cidr='10.0.0.0/24', tenant_id=project_id),\ self.subnet(network=network, gateway_ip='10.0.1.1', cidr='10.0.1.0/24'),\ self.subnet(network=network, gateway_ip='10.0.2.1', cidr='10.0.2.0/24'): ctx = context.Context(user_id=None, tenant_id=project_id, is_admin=True) pl = directory.get_plugin() count = pl.get_subnets_count(ctx, filters={'project_id': [project_id]}) self.assertEqual(1, count) net_project_id = network['network']['project_id'] count = pl.get_subnets_count( ctx, filters={'project_id': [net_project_id]}) self.assertEqual(2, count) def test_get_subnets_count_filter_by_unknown_filter(self): with self.network() as network: with self.subnet(network=network, gateway_ip='10.0.0.1', cidr='10.0.0.0/24'),\ self.subnet(network=network, gateway_ip='10.0.1.1', cidr='10.0.1.0/24'),\ self.subnet(network=network, gateway_ip='10.0.2.1', cidr='10.0.2.0/24'): project_id = network['network']['project_id'] ctx = context.Context(user_id=None, tenant_id=project_id, is_admin=False) pl = directory.get_plugin() count = pl.get_subnets_count(ctx, filters={'fake_filter': [True]}) self.assertEqual(3, count) # change the filter value and get the same result count = pl.get_subnets_count(ctx, filters={'fake_filter': [False]}) self.assertEqual(3, count) def test_list_subnets(self): with self.network() as network: with self.subnet(network=network, gateway_ip='10.0.0.1', cidr='10.0.0.0/24') as v1,\ self.subnet(network=network, gateway_ip='10.0.1.1', cidr='10.0.1.0/24') as v2,\ self.subnet(network=network, gateway_ip='10.0.2.1', cidr='10.0.2.0/24') as v3: subnets = (v1, v2, v3) self._test_list_resources('subnet', subnets) def test_list_subnets_shared(self): with self.network(shared=True) as network: with self.subnet(network=network, cidr='10.0.0.0/24') as subnet: with self.subnet(cidr='10.0.1.0/24') as priv_subnet: # normal user should see only 1 subnet req = self.new_list_request('subnets') req.environ['neutron.context'] = context.Context( '', 'some_tenant') res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(1, len(res['subnets'])) self.assertEqual(res['subnets'][0]['cidr'], subnet['subnet']['cidr']) # admin will see both subnets admin_req = self.new_list_request('subnets') admin_res = self.deserialize( self.fmt, admin_req.get_response(self.api)) self.assertEqual(2, len(admin_res['subnets'])) cidrs = [sub['cidr'] for sub in admin_res['subnets']] self.assertIn(subnet['subnet']['cidr'], cidrs) self.assertIn(priv_subnet['subnet']['cidr'], cidrs) def test_list_subnets_filtering_by_project_id(self): with self.network() as network: with self.subnet(network=network, gateway_ip='10.0.0.1', cidr='10.0.0.0/24') as v1,\ self.subnet(network=network, gateway_ip='10.0.1.1', cidr='10.0.1.0/24') as v2: subnets = (v1, v2) query_params = ('project_id={0}'. format(network['network']['project_id'])) self._test_list_resources('subnet', subnets, query_params=query_params) query_params = ('project_id={0}'. format(uuidutils.generate_uuid())) self._test_list_resources('subnet', [], query_params=query_params) def test_list_subnets_filtering_by_cidr_used_on_create(self): with self.network() as network: with self.subnet(network=network, gateway_ip='10.0.0.1', cidr='10.0.0.11/24') as v1,\ self.subnet(network=network, gateway_ip='10.0.1.1', cidr='10.0.1.11/24') as v2: subnets = (v1, v2) query_params = ('cidr=10.0.0.11/24&cidr=10.0.1.11/24') self._test_list_resources('subnet', subnets, query_params=query_params) def test_list_subnets_filtering_by_unknown_filter(self): with self.network() as network: with self.subnet(network=network, gateway_ip='10.0.0.1', cidr='10.0.0.0/24') as v1,\ self.subnet(network=network, gateway_ip='10.0.1.1', cidr='10.0.1.0/24') as v2: subnets = (v1, v2) query_params = 'admin_state_up=True' self._test_list_resources('subnet', subnets, query_params=query_params) # test with other value to check if we have the same results query_params = 'admin_state_up=False' self._test_list_resources('subnet', subnets, query_params=query_params) def test_list_subnets_with_parameter(self): with self.network() as network: with self.subnet(network=network, gateway_ip='10.0.0.1', cidr='10.0.0.0/24') as v1,\ self.subnet(network=network, gateway_ip='10.0.1.1', cidr='10.0.1.0/24') as v2: subnets = (v1, v2) query_params = 'ip_version=4&ip_version=6' self._test_list_resources('subnet', subnets, query_params=query_params) query_params = 'ip_version=6' self._test_list_resources('subnet', [], query_params=query_params) def test_list_subnets_with_sort_native(self): if self._skip_native_sorting: self.skipTest("Skip test for not implemented sorting feature") with self.subnet(enable_dhcp=True, cidr='10.0.0.0/24') as subnet1,\ self.subnet(enable_dhcp=False, cidr='11.0.0.0/24') as subnet2,\ self.subnet(enable_dhcp=False, cidr='12.0.0.0/24') as subnet3: self._test_list_with_sort('subnet', (subnet3, subnet2, subnet1), [('enable_dhcp', 'asc'), ('cidr', 'desc')]) def test_list_subnets_with_sort_emulated(self): helper_patcher = mock.patch( 'neutron.api.v2.base.Controller._get_sorting_helper', new=_fake_get_sorting_helper) helper_patcher.start() with self.subnet(enable_dhcp=True, cidr='10.0.0.0/24') as subnet1,\ self.subnet(enable_dhcp=False, cidr='11.0.0.0/24') as subnet2,\ self.subnet(enable_dhcp=False, cidr='12.0.0.0/24') as subnet3: self._test_list_with_sort('subnet', (subnet3, subnet2, subnet1), [('enable_dhcp', 'asc'), ('cidr', 'desc')]) def test_list_subnets_with_pagination_native(self): if self._skip_native_pagination: self.skipTest("Skip test for not implemented sorting feature") with self.subnet(cidr='10.0.0.0/24') as subnet1,\ self.subnet(cidr='11.0.0.0/24') as subnet2,\ self.subnet(cidr='12.0.0.0/24') as subnet3: self._test_list_with_pagination('subnet', (subnet1, subnet2, subnet3), ('cidr', 'asc'), 2, 2) def test_list_subnets_with_pagination_emulated(self): helper_patcher = mock.patch( 'neutron.api.v2.base.Controller._get_pagination_helper', new=_fake_get_pagination_helper) helper_patcher.start() with self.subnet(cidr='10.0.0.0/24') as subnet1,\ self.subnet(cidr='11.0.0.0/24') as subnet2,\ self.subnet(cidr='12.0.0.0/24') as subnet3: self._test_list_with_pagination('subnet', (subnet1, subnet2, subnet3), ('cidr', 'asc'), 2, 2) def test_list_subnets_with_pagination_reverse_native(self): if self._skip_native_sorting: self.skipTest("Skip test for not implemented sorting feature") with self.subnet(cidr='10.0.0.0/24') as subnet1,\ self.subnet(cidr='11.0.0.0/24') as subnet2,\ self.subnet(cidr='12.0.0.0/24') as subnet3: self._test_list_with_pagination_reverse('subnet', (subnet1, subnet2, subnet3), ('cidr', 'asc'), 2, 2) def test_list_subnets_with_pagination_reverse_emulated(self): helper_patcher = mock.patch( 'neutron.api.v2.base.Controller._get_pagination_helper', new=_fake_get_pagination_helper) helper_patcher.start() with self.subnet(cidr='10.0.0.0/24') as subnet1,\ self.subnet(cidr='11.0.0.0/24') as subnet2,\ self.subnet(cidr='12.0.0.0/24') as subnet3: self._test_list_with_pagination_reverse('subnet', (subnet1, subnet2, subnet3), ('cidr', 'asc'), 2, 2) def test_invalid_ip_version(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': 7, 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.2.1'}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_invalid_subnet(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': 'invalid', 'ip_version': 4, 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.2.1'}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def _test_unsupported_subnet_cidr(self, subnet_cidr): with self.network() as network: subnet = {'network_id': network['network']['id'], 'cidr': subnet_cidr, 'ip_version': 4, 'enable_dhcp': True, 'tenant_id': network['network']['tenant_id']} plugin = directory.get_plugin() if hasattr(plugin, '_validate_subnet'): self.assertRaises(lib_exc.InvalidInput, plugin._validate_subnet, context.get_admin_context(), subnet) def test_unsupported_subnet_cidr_multicast(self): self._test_unsupported_subnet_cidr("224.0.0.1/16") def test_unsupported_subnet_cidr_loopback(self): self._test_unsupported_subnet_cidr("127.0.0.1/8") def test_invalid_ip_address(self): with self.network() as network: data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': 4, 'tenant_id': network['network']['tenant_id'], 'gateway_ip': 'ipaddress'}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_invalid_uuid(self): with self.network() as network: data = {'subnet': {'network_id': 'invalid-uuid', 'cidr': '10.0.2.0/24', 'ip_version': 4, 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.0.1'}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_with_one_dns(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.100'}] dns_nameservers = ['1.2.3.4'] self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, allocation_pools=allocation_pools, dns_nameservers=dns_nameservers) def test_create_subnet_with_two_dns(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.100'}] dns_nameservers = ['1.2.3.4', '4.3.2.1'] self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, allocation_pools=allocation_pools, dns_nameservers=dns_nameservers) def test_create_subnet_with_too_many_dns(self): with self.network() as network: dns_list = ['1.1.1.1', '2.2.2.2', '3.3.3.3'] data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': 4, 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.0.1', 'dns_nameservers': dns_list}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_create_subnet_with_one_host_route(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.100'}] host_routes = [{'destination': '135.207.0.0/16', 'nexthop': '1.2.3.4'}] self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, allocation_pools=allocation_pools, host_routes=host_routes) def test_create_subnet_with_two_host_routes(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' allocation_pools = [{'start': '10.0.0.2', 'end': '10.0.0.100'}] host_routes = [{'destination': '135.207.0.0/16', 'nexthop': '1.2.3.4'}, {'destination': '12.0.0.0/8', 'nexthop': '4.3.2.1'}] self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, allocation_pools=allocation_pools, host_routes=host_routes) def test_create_subnet_with_too_many_routes(self): with self.network() as network: host_routes = [{'destination': '135.207.0.0/16', 'nexthop': '1.2.3.4'}, {'destination': '12.0.0.0/8', 'nexthop': '4.3.2.1'}, {'destination': '141.212.0.0/16', 'nexthop': '2.2.2.2'}] data = {'subnet': {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': 4, 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.0.1', 'host_routes': host_routes}} subnet_req = self.new_create_request('subnets', data) res = subnet_req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_dns(self): with self.subnet() as subnet: data = {'subnet': {'dns_nameservers': ['11.0.0.1']}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['subnet']['dns_nameservers'], res['subnet']['dns_nameservers']) def test_subnet_lifecycle_dns_retains_order(self): cfg.CONF.set_override('max_dns_nameservers', 3) with self.subnet(dns_nameservers=['1.1.1.1', '2.2.2.2', '3.3.3.3']) as subnet: subnets = self._show('subnets', subnet['subnet']['id'], expected_code=webob.exc.HTTPOk.code) self.assertEqual(['1.1.1.1', '2.2.2.2', '3.3.3.3'], subnets['subnet']['dns_nameservers']) data = {'subnet': {'dns_nameservers': ['2.2.2.2', '3.3.3.3', '1.1.1.1']}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['subnet']['dns_nameservers'], res['subnet']['dns_nameservers']) subnets = self._show('subnets', subnet['subnet']['id'], expected_code=webob.exc.HTTPOk.code) self.assertEqual(data['subnet']['dns_nameservers'], subnets['subnet']['dns_nameservers']) def test_update_subnet_dns_to_None(self): with self.subnet(dns_nameservers=['11.0.0.1']) as subnet: data = {'subnet': {'dns_nameservers': None}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual([], res['subnet']['dns_nameservers']) data = {'subnet': {'dns_nameservers': ['11.0.0.3']}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['subnet']['dns_nameservers'], res['subnet']['dns_nameservers']) def test_update_subnet_dns_with_too_many_entries(self): with self.subnet() as subnet: dns_list = ['1.1.1.1', '2.2.2.2', '3.3.3.3'] data = {'subnet': {'dns_nameservers': dns_list}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_update_subnet_route(self): with self.subnet() as subnet: data = {'subnet': {'host_routes': [{'destination': '12.0.0.0/8', 'nexthop': '1.2.3.4'}]}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['subnet']['host_routes'], res['subnet']['host_routes']) def test_update_subnet_route_to_None(self): with self.subnet(host_routes=[{'destination': '12.0.0.0/8', 'nexthop': '1.2.3.4'}]) as subnet: data = {'subnet': {'host_routes': None}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual([], res['subnet']['host_routes']) data = {'subnet': {'host_routes': [{'destination': '12.0.0.0/8', 'nexthop': '1.2.3.4'}]}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(data['subnet']['host_routes'], res['subnet']['host_routes']) def _test_update_subnet(self, old_gw=None, new_gw=None, check_gateway=False): allocation_pools = [{'start': '192.168.0.16', 'end': '192.168.0.254'}] with self.network() as network: with self.subnet(network=network, gateway_ip=old_gw, allocation_pools=allocation_pools, cidr='192.168.0.0/24') as subnet: data = { 'subnet': { 'allocation_pools': [ {'start': '192.168.0.10', 'end': '192.168.0.20'}, {'start': '192.168.0.30', 'end': '192.168.0.40'}], 'gateway_ip': new_gw}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(200, res.status_code) self._verify_updated_subnet_allocation_pools( res, with_gateway_ip=check_gateway) def test_update_subnet_from_no_gw_to_no_gw(self): self._test_update_subnet() def test_update_subnet_from_gw_to_no_gw(self): self._test_update_subnet(old_gw='192.168.0.15') def test_update_subnet_from_gw_to_new_gw(self): self._test_update_subnet(old_gw='192.168.0.15', new_gw='192.168.0.9', check_gateway=True) def test_update_subnet_route_with_too_many_entries(self): with self.subnet() as subnet: data = {'subnet': {'host_routes': [ {'destination': '12.0.0.0/8', 'nexthop': '1.2.3.4'}, {'destination': '13.0.0.0/8', 'nexthop': '1.2.3.5'}, {'destination': '14.0.0.0/8', 'nexthop': '1.2.3.6'}]}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) def test_delete_subnet_with_dns(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' dns_nameservers = ['1.2.3.4'] # Create new network res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) subnet = self._make_subnet(self.fmt, network, gateway_ip, cidr, ip_version=4, dns_nameservers=dns_nameservers) req = self.new_delete_request('subnets', subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) def test_delete_subnet_with_route(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' host_routes = [{'destination': '135.207.0.0/16', 'nexthop': '1.2.3.4'}] # Create new network res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) subnet = self._make_subnet(self.fmt, network, gateway_ip, cidr, ip_version=4, host_routes=host_routes) req = self.new_delete_request('subnets', subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) def test_delete_subnet_with_dns_and_route(self): gateway_ip = '10.0.0.1' cidr = '10.0.0.0/24' dns_nameservers = ['1.2.3.4'] host_routes = [{'destination': '135.207.0.0/16', 'nexthop': '1.2.3.4'}] # Create new network res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) subnet = self._make_subnet(self.fmt, network, gateway_ip, cidr, ip_version=4, dns_nameservers=dns_nameservers, host_routes=host_routes) req = self.new_delete_request('subnets', subnet['subnet']['id']) res = req.get_response(self.api) self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) def test_delete_subnet_with_callback(self): with self.subnet() as subnet,\ mock.patch.object(registry, 'notify') as notify: errors = [ exceptions.NotificationError( 'fake_id', lib_exc.NeutronException()), ] notify.side_effect = [ exceptions.CallbackFailure(errors=errors), None ] # Make sure the delete request fails delete_request = self.new_delete_request('subnets', subnet['subnet']['id']) delete_response = delete_request.get_response(self.api) self.assertIn('NeutronError', delete_response.json) self.assertEqual('SubnetInUse', delete_response.json['NeutronError']['type']) # Make sure the subnet wasn't deleted list_request = self.new_list_request( 'subnets', params="id=%s" % subnet['subnet']['id']) list_response = list_request.get_response(self.api) self.assertEqual(subnet['subnet']['id'], list_response.json['subnets'][0]['id']) def _helper_test_validate_subnet(self, option, exception): cfg.CONF.set_override(option, 0) with self.network() as network: subnet = {'network_id': network['network']['id'], 'cidr': '10.0.2.0/24', 'ip_version': 4, 'tenant_id': network['network']['tenant_id'], 'gateway_ip': '10.0.2.1', 'dns_nameservers': ['8.8.8.8'], 'host_routes': [{'destination': '135.207.0.0/16', 'nexthop': '1.2.3.4'}]} plugin = directory.get_plugin() e = self.assertRaises(exception, plugin._validate_subnet, context.get_admin_context(), subnet) self.assertThat( str(e), matchers.Not(matchers.Contains('built-in function id'))) def test_validate_subnet_dns_nameservers_exhausted(self): self._helper_test_validate_subnet( 'max_dns_nameservers', n_exc.DNSNameServersExhausted) def test_validate_subnet_host_routes_exhausted(self): self._helper_test_validate_subnet( 'max_subnet_host_routes', n_exc.HostRoutesExhausted) def test_port_prevents_network_deletion(self): with self.port() as p: self._delete('networks', p['port']['network_id'], expected_code=webob.exc.HTTPConflict.code) def test_port_prevents_subnet_deletion(self): with self.port() as p: self._delete('subnets', p['port']['fixed_ips'][0]['subnet_id'], expected_code=webob.exc.HTTPConflict.code) class TestSubnetPoolsV2(NeutronDbPluginV2TestCase): _POOL_NAME = 'test-pool' def _test_create_subnetpool(self, prefixes, expected=None, admin=False, **kwargs): keys = kwargs.copy() keys.setdefault('tenant_id', self._tenant_id) with self.subnetpool(prefixes, admin, **keys) as subnetpool: self._validate_resource(subnetpool, keys, 'subnetpool') if expected: self._compare_resource(subnetpool, expected, 'subnetpool') return subnetpool def _validate_default_prefix(self, prefix, subnetpool): self.assertEqual(subnetpool['subnetpool']['default_prefixlen'], prefix) def _validate_min_prefix(self, prefix, subnetpool): self.assertEqual(subnetpool['subnetpool']['min_prefixlen'], prefix) def _validate_max_prefix(self, prefix, subnetpool): self.assertEqual(subnetpool['subnetpool']['max_prefixlen'], prefix) def _validate_is_default(self, subnetpool): self.assertTrue(subnetpool['subnetpool']['is_default']) def test_create_subnetpool_empty_prefix_list(self): self.assertRaises(webob.exc.HTTPClientError, self._test_create_subnetpool, [], name=self._POOL_NAME, tenant_id=self._tenant_id, min_prefixlen='21') def test_create_default_subnetpools(self): for cidr, min_prefixlen in (['fe80::/48', '64'], ['10.10.10.0/24', '24']): pool = self._test_create_subnetpool([cidr], admin=True, tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen=min_prefixlen, is_default=True) self._validate_is_default(pool) def test_cannot_create_multiple_default_subnetpools(self): for cidr1, cidr2, min_prefixlen in (['fe80::/48', '2001::/48', '64'], ['10.10.10.0/24', '10.10.20.0/24', '24']): pool = self._test_create_subnetpool([cidr1], admin=True, tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen=min_prefixlen, is_default=True) self._validate_is_default(pool) self.assertRaises(webob.exc.HTTPClientError, self._test_create_subnetpool, [cidr2], admin=True, tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen=min_prefixlen, is_default=True) def test_create_subnetpool_ipv4_24_with_defaults(self): subnet = netaddr.IPNetwork('10.10.10.0/24') subnetpool = self._test_create_subnetpool([subnet.cidr], name=self._POOL_NAME, tenant_id=self._tenant_id, min_prefixlen='21') self._validate_default_prefix('21', subnetpool) self._validate_min_prefix('21', subnetpool) def test_create_subnetpool_ipv4_21_with_defaults(self): subnet = netaddr.IPNetwork('10.10.10.0/21') subnetpool = self._test_create_subnetpool([subnet.cidr], name=self._POOL_NAME, tenant_id=self._tenant_id, min_prefixlen='21') self._validate_default_prefix('21', subnetpool) self._validate_min_prefix('21', subnetpool) def test_create_subnetpool_ipv4_default_prefix_too_small(self): subnet = netaddr.IPNetwork('10.10.10.0/21') self.assertRaises(webob.exc.HTTPClientError, self._test_create_subnetpool, [subnet.cidr], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21', default_prefixlen='20') def test_create_subnetpool_ipv4_default_prefix_too_large(self): subnet = netaddr.IPNetwork('10.10.10.0/21') self.assertRaises(webob.exc.HTTPClientError, self._test_create_subnetpool, [subnet.cidr], tenant_id=self._tenant_id, name=self._POOL_NAME, max_prefixlen=24, default_prefixlen='32') def test_create_subnetpool_ipv4_default_prefix_bounds(self): subnet = netaddr.IPNetwork('10.10.10.0/21') subnetpool = self._test_create_subnetpool([subnet.cidr], tenant_id=self._tenant_id, name=self._POOL_NAME) self._validate_min_prefix('8', subnetpool) self._validate_default_prefix('8', subnetpool) self._validate_max_prefix('32', subnetpool) def test_create_subnetpool_ipv6_default_prefix_bounds(self): subnet = netaddr.IPNetwork('fe80::/48') subnetpool = self._test_create_subnetpool([subnet.cidr], tenant_id=self._tenant_id, name=self._POOL_NAME) self._validate_min_prefix('64', subnetpool) self._validate_default_prefix('64', subnetpool) self._validate_max_prefix('128', subnetpool) def test_create_subnetpool_ipv4_supported_default_prefix(self): subnet = netaddr.IPNetwork('10.10.10.0/21') subnetpool = self._test_create_subnetpool([subnet.cidr], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21', default_prefixlen='26') self._validate_default_prefix('26', subnetpool) def test_create_subnetpool_ipv4_supported_min_prefix(self): subnet = netaddr.IPNetwork('10.10.10.0/24') subnetpool = self._test_create_subnetpool([subnet.cidr], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='26') self._validate_min_prefix('26', subnetpool) self._validate_default_prefix('26', subnetpool) def test_create_subnetpool_ipv4_default_prefix_smaller_than_min(self): subnet = netaddr.IPNetwork('10.10.10.0/21') self.assertRaises(webob.exc.HTTPClientError, self._test_create_subnetpool, [subnet.cidr], tenant_id=self._tenant_id, name=self._POOL_NAME, default_prefixlen='22', min_prefixlen='23') def test_create_subnetpool_mixed_ip_version(self): subnet_v4 = netaddr.IPNetwork('10.10.10.0/21') subnet_v6 = netaddr.IPNetwork('fe80::/48') self.assertRaises(webob.exc.HTTPClientError, self._test_create_subnetpool, [subnet_v4.cidr, subnet_v6.cidr], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') def test_create_subnetpool_ipv6_with_defaults(self): subnet = netaddr.IPNetwork('fe80::/48') subnetpool = self._test_create_subnetpool([subnet.cidr], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='48') self._validate_default_prefix('48', subnetpool) self._validate_min_prefix('48', subnetpool) def test_get_subnetpool(self): subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='24') req = self.new_show_request('subnetpools', subnetpool['subnetpool']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(subnetpool['subnetpool']['id'], res['subnetpool']['id']) def test_get_subnetpool_different_tenants_not_shared(self): subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], shared=False, tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='24') req = self.new_show_request('subnetpools', subnetpool['subnetpool']['id']) neutron_context = context.Context('', 'not-the-owner') req.environ['neutron.context'] = neutron_context res = req.get_response(self.api) self.assertEqual(404, res.status_int) def test_get_subnetpool_different_tenants_shared(self): subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], None, True, name=self._POOL_NAME, min_prefixlen='24', shared=True) req = self.new_show_request('subnetpools', subnetpool['subnetpool']['id']) neutron_context = context.Context('', self._tenant_id) req.environ['neutron.context'] = neutron_context res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(subnetpool['subnetpool']['id'], res['subnetpool']['id']) def test_list_subnetpools_different_tenants_shared(self): self._test_create_subnetpool(['10.10.10.0/24'], None, True, name=self._POOL_NAME, min_prefixlen='24', shared=True) admin_res = self._list('subnetpools') mortal_res = self._list('subnetpools', neutron_context=context.Context('', 'not-the-owner')) self.assertEqual(1, len(admin_res['subnetpools'])) self.assertEqual(1, len(mortal_res['subnetpools'])) def test_list_subnetpools_different_tenants_not_shared(self): self._test_create_subnetpool(['10.10.10.0/24'], None, True, name=self._POOL_NAME, min_prefixlen='24', shared=False) admin_res = self._list('subnetpools') mortal_res = self._list('subnetpools', neutron_context=context.Context('', 'not-the-owner')) self.assertEqual(1, len(admin_res['subnetpools'])) self.assertEqual(0, len(mortal_res['subnetpools'])) def test_list_subnetpools_filters_none(self): subnet_pool = self._test_create_subnetpool(['10.10.10.0/24'], None, True, name=self._POOL_NAME, min_prefixlen='24', shared=True) sp_list = self.plugin.get_subnetpools( context.Context('', 'not-the-owner')) self.assertEqual(1, len(sp_list)) self.assertEqual(subnet_pool['subnetpool']['id'], sp_list[0]['id']) def test_delete_subnetpool(self): subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='24') req = self.new_delete_request('subnetpools', subnetpool['subnetpool']['id']) res = req.get_response(self.api) self.assertEqual(204, res.status_int) def test_delete_nonexistent_subnetpool(self): req = self.new_delete_request('subnetpools', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa') res = req.get_response(self._api_for_resource('subnetpools')) self.assertEqual(404, res.status_int) def test_update_subnetpool_prefix_list_append(self): initial_subnetpool = self._test_create_subnetpool(['10.10.8.0/21'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='24') data = {'subnetpool': {'prefixes': ['10.10.8.0/21', '3.3.3.0/24', '2.2.2.0/24']}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) api = self._api_for_resource('subnetpools') res = self.deserialize(self.fmt, req.get_response(api)) self.assertItemsEqual(res['subnetpool']['prefixes'], ['10.10.8.0/21', '3.3.3.0/24', '2.2.2.0/24']) def test_update_subnetpool_prefix_list_compaction(self): initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='24') data = {'subnetpool': {'prefixes': ['10.10.10.0/24', '10.10.11.0/24']}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) api = self._api_for_resource('subnetpools') res = self.deserialize(self.fmt, req.get_response(api)) self.assertItemsEqual(res['subnetpool']['prefixes'], ['10.10.10.0/23']) def test_illegal_subnetpool_prefix_list_update(self): initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='24') data = {'subnetpool': {'prefixes': ['10.10.11.0/24']}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) api = self._api_for_resource('subnetpools') res = req.get_response(api) self.assertEqual(400, res.status_int) def test_update_subnetpool_default_prefix(self): initial_subnetpool = self._test_create_subnetpool(['10.10.8.0/21'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='24') data = {'subnetpool': {'default_prefixlen': '26'}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) api = self._api_for_resource('subnetpools') res = self.deserialize(self.fmt, req.get_response(api)) self.assertEqual(26, res['subnetpool']['default_prefixlen']) def test_update_subnetpool_min_prefix(self): initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='24') data = {'subnetpool': {'min_prefixlen': '21'}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(21, res['subnetpool']['min_prefixlen']) def test_update_subnetpool_min_prefix_larger_than_max(self): initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21', max_prefixlen='24') data = {'subnetpool': {'min_prefixlen': '28'}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_update_subnetpool_max_prefix(self): initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21', max_prefixlen='24') data = {'subnetpool': {'max_prefixlen': '26'}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(26, res['subnetpool']['max_prefixlen']) def test_update_subnetpool_max_prefix_less_than_min(self): initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='24') data = {'subnetpool': {'max_prefixlen': '21'}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_update_subnetpool_max_prefix_less_than_default(self): initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21', default_prefixlen='24') data = {'subnetpool': {'max_prefixlen': '22'}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_update_subnetpool_default_prefix_less_than_min(self): initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') data = {'subnetpool': {'default_prefixlen': '20'}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_update_subnetpool_default_prefix_larger_than_max(self): initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21', max_prefixlen='24') data = {'subnetpool': {'default_prefixlen': '28'}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_update_subnetpool_prefix_list_mixed_ip_version(self): initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='24') data = {'subnetpool': {'prefixes': ['fe80::/48']}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_update_subnetpool_default_quota(self): initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='24', default_quota=10) self.assertEqual(10, initial_subnetpool['subnetpool']['default_quota']) data = {'subnetpool': {'default_quota': '1'}} req = self.new_update_request('subnetpools', data, initial_subnetpool['subnetpool']['id']) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(1, res['subnetpool']['default_quota']) def test_allocate_subnet_bad_gateway(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/8'], tenant_id=self._tenant_id, name=self._POOL_NAME, default_prefixlen='24') # Request a subnet allocation (no CIDR) data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'prefixlen': 32, 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) result = req.get_response(self.api) self.assertEqual(201, result.status_int) def test_allocate_any_subnet_with_prefixlen(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') # Request a subnet allocation (no CIDR) data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'prefixlen': 24, 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) res = self.deserialize(self.fmt, req.get_response(self.api)) subnet = netaddr.IPNetwork(res['subnet']['cidr']) self.assertEqual(24, subnet.prefixlen) # Assert the allocated subnet CIDR is a subnet of our pool prefix supernet = netaddr.smallest_matching_cidr( subnet, sp['subnetpool']['prefixes']) self.assertEqual(supernet, netaddr.IPNetwork('10.10.0.0/16')) def test_allocate_any_subnet_with_default_prefixlen(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') # Request any subnet allocation using default prefix data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) res = self.deserialize(self.fmt, req.get_response(self.api)) subnet = netaddr.IPNetwork(res['subnet']['cidr']) self.assertEqual(subnet.prefixlen, int(sp['subnetpool']['default_prefixlen'])) def test_allocate_specific_subnet_with_mismatch_prefixlen(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'cidr': '10.10.1.0/24', 'prefixlen': 26, 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_allocate_specific_subnet_with_matching_prefixlen(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'cidr': '10.10.1.0/24', 'prefixlen': 24, 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_allocate_specific_subnet(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') # Request a specific subnet allocation data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'cidr': '10.10.1.0/24', 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) res = self.deserialize(self.fmt, req.get_response(self.api)) # Assert the allocated subnet CIDR is what we expect subnet = netaddr.IPNetwork(res['subnet']['cidr']) self.assertEqual(netaddr.IPNetwork('10.10.1.0/24'), subnet) def test_allocate_specific_subnet_non_existent_prefix(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') # Request a specific subnet allocation data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'cidr': '192.168.1.0/24', 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) res = req.get_response(self.api) self.assertEqual(500, res.status_int) def test_allocate_specific_subnet_already_allocated(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') # Request a specific subnet allocation data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'cidr': '10.10.10.0/24', 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) # Allocate the subnet res = req.get_response(self.api) self.assertEqual(201, res.status_int) # Attempt to allocate it again res = req.get_response(self.api) # Assert error self.assertEqual(500, res.status_int) def test_allocate_specific_subnet_prefix_too_small(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') # Request a specific subnet allocation data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'cidr': '10.10.0.0/20', 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_allocate_specific_subnet_prefix_specific_gw(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') # Request a specific subnet allocation data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'cidr': '10.10.1.0/24', 'gateway_ip': '10.10.1.254', 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual('10.10.1.254', res['subnet']['gateway_ip']) def test_allocate_specific_subnet_prefix_allocation_pools(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') # Request a specific subnet allocation pools = [{'start': '10.10.1.2', 'end': '10.10.1.253'}] data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'cidr': '10.10.1.0/24', 'gateway_ip': '10.10.1.1', 'ip_version': 4, 'allocation_pools': pools, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) res = self.deserialize(self.fmt, req.get_response(self.api)) self.assertEqual(pools[0]['start'], res['subnet']['allocation_pools'][0]['start']) self.assertEqual(pools[0]['end'], res['subnet']['allocation_pools'][0]['end']) def test_allocate_any_subnet_prefix_allocation_pools(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.10.0/24'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') # Request an any subnet allocation pools = [{'start': '10.10.10.1', 'end': '10.10.10.254'}] data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'prefixlen': '24', 'ip_version': 4, 'allocation_pools': pools, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_allocate_specific_subnet_prefix_too_large(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21', max_prefixlen='21') # Request a specific subnet allocation data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'cidr': '10.10.0.0/24', 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_delete_subnetpool_existing_allocations(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21') data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'cidr': '10.10.0.0/24', 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) req.get_response(self.api) req = self.new_delete_request('subnetpools', sp['subnetpool']['id']) res = req.get_response(self.api) self.assertEqual(400, res.status_int) def test_allocate_subnet_over_quota(self): with self.network() as network: sp = self._test_create_subnetpool(['10.10.0.0/16'], tenant_id=self._tenant_id, name=self._POOL_NAME, min_prefixlen='21', default_quota=2048) # Request a specific subnet allocation data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'ip_version': 4, 'prefixlen': 21, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) # Allocate a subnet to fill the quota res = req.get_response(self.api) self.assertEqual(201, res.status_int) # Attempt to allocate a /21 again res = req.get_response(self.api) # Assert error self.assertEqual(409, res.status_int) def test_allocate_any_ipv4_subnet_ipv6_pool(self): with self.network() as network: sp = self._test_create_subnetpool(['2001:db8:1:2::/63'], tenant_id=self._tenant_id, name=self._POOL_NAME) # Request a specific subnet allocation data = {'subnet': {'network_id': network['network']['id'], 'subnetpool_id': sp['subnetpool']['id'], 'ip_version': 4, 'tenant_id': network['network']['tenant_id']}} req = self.new_create_request('subnets', data) res = req.get_response(self.api) self.assertEqual(400, res.status_int) class DbModelMixin(object): """DB model tests.""" def test_make_network_dict_outside_engine_facade_manager(self): mock.patch.object(directory, 'get_plugin').start() ctx = context.get_admin_context() with db_api.context_manager.writer.using(ctx): network = models_v2.Network(name="net_net", status="OK", admin_state_up=True) ctx.session.add(network) with db_api.autonested_transaction(ctx.session): sg = sg_models.SecurityGroup(name='sg', description='sg') ctx.session.add(sg) # ensure db rels aren't loaded until commit for network object # by sharing after a nested transaction ctx.session.add( rbac_db_models.NetworkRBAC(object_id=network.id, action='access_as_shared', tenant_id=network.tenant_id, target_tenant='*') ) net2 = models_v2.Network(name="net_net2", status="OK", admin_state_up=True) ctx.session.add(net2) pl = db_base_plugin_common.DbBasePluginCommon() self.assertTrue(pl._make_network_dict(network, context=ctx)['shared']) self.assertFalse(pl._make_network_dict(net2, context=ctx)['shared']) def test_repr(self): """testing the string representation of 'model' classes.""" network = models_v2.Network(name="net_net", status="OK", admin_state_up=True) actual_repr_output = repr(network) exp_start_with = "") final_exp = exp_start_with + exp_middle + exp_end_with self.assertEqual(final_exp, actual_repr_output) def _make_security_group_and_rule(self, ctx): with db_api.context_manager.writer.using(ctx): sg = sg_models.SecurityGroup(name='sg', description='sg') rule = sg_models.SecurityGroupRule( security_group=sg, port_range_min=1, port_range_max=2, protocol='TCP', ethertype='v4', direction='ingress', remote_ip_prefix='0.0.0.0/0') ctx.session.add(sg) ctx.session.add(rule) return sg, rule def _make_floating_ip(self, ctx, port_id): flip = l3_obj.FloatingIP( ctx, floating_ip_address=netaddr.IPAddress('1.2.3.4'), floating_network_id=uuidutils.generate_uuid(), floating_port_id=port_id) flip.create() return flip def _make_router(self, ctx): with db_api.context_manager.writer.using(ctx): router = l3_models.Router() ctx.session.add(router) return router def _get_neutron_attr(self, ctx, attr_id): return ctx.session.query( standard_attr.StandardAttribute).filter( standard_attr.StandardAttribute.id == attr_id).one() def _test_standardattr_removed_on_obj_delete(self, ctx, obj): attr_id = obj.standard_attr_id self.assertEqual( obj.__table__.name, self._get_neutron_attr(ctx, attr_id).resource_type) with ctx.session.begin(): ctx.session.delete(obj) with testtools.ExpectedException(orm.exc.NoResultFound): # we want to make sure that the attr resource was removed self._get_neutron_attr(ctx, attr_id) def test_staledata_error_on_concurrent_object_update_network(self): ctx = context.get_admin_context() network = self._make_network(ctx) self._test_staledata_error_on_concurrent_object_update( models_v2.Network, network['id']) def test_staledata_error_on_concurrent_object_update_port(self): ctx = context.get_admin_context() network = self._make_network(ctx) port = self._make_port(ctx, network.id) self._test_staledata_error_on_concurrent_object_update( models_v2.Port, port['id']) def test_staledata_error_on_concurrent_object_update_subnet(self): ctx = context.get_admin_context() network = self._make_network(ctx) subnet = self._make_subnet(ctx, network.id) self._test_staledata_error_on_concurrent_object_update( models_v2.Subnet, subnet['id']) def test_staledata_error_on_concurrent_object_update_subnetpool(self): ctx = context.get_admin_context() subnetpool = self._make_subnetpool(ctx) self._test_staledata_error_on_concurrent_object_update( models_v2.SubnetPool, subnetpool['id']) def test_staledata_error_on_concurrent_object_update_router(self): ctx = context.get_admin_context() router = self._make_router(ctx) self._test_staledata_error_on_concurrent_object_update( l3_models.Router, router['id']) def test_staledata_error_on_concurrent_object_update_floatingip(self): ctx = context.get_admin_context() network = self._make_network(ctx) port = self._make_port(ctx, network.id) flip = self._make_floating_ip(ctx, port.id) self._test_staledata_error_on_concurrent_object_update( flip.db_model, flip.id) def test_staledata_error_on_concurrent_object_update_sg(self): ctx = context.get_admin_context() sg, rule = self._make_security_group_and_rule(ctx) self._test_staledata_error_on_concurrent_object_update( sg_models.SecurityGroup, sg['id']) self._test_staledata_error_on_concurrent_object_update( sg_models.SecurityGroupRule, rule['id']) def _test_staledata_error_on_concurrent_object_update(self, model, dbid): """Test revision compare and swap update breaking on concurrent update. In this test we start an update of the name on a model in an eventlet coroutine where it will be blocked before it can commit the results. Then while it is blocked, we will update the description of the model in the foregound and ensure that this results in the coroutine receiving a StaleDataError as expected. """ lock = functools.partial(lockutils.lock, uuidutils.generate_uuid()) self._blocked_on_lock = False def _lock_blocked_name_update(): ctx = context.get_admin_context() with db_api.context_manager.writer.using(ctx): thing = ctx.session.query(model).filter_by(id=dbid).one() thing.bump_revision() thing.name = 'newname' self._blocked_on_lock = True with lock(): return thing with lock(): coro = eventlet.spawn(_lock_blocked_name_update) # wait for the coroutine to get blocked on the lock before # we proceed to update the record underneath it while not self._blocked_on_lock: eventlet.sleep(0) ctx = context.get_admin_context() with db_api.context_manager.writer.using(ctx): thing = ctx.session.query(model).filter_by(id=dbid).one() thing.bump_revision() thing.description = 'a description' revision_after_build = thing.revision_number with testtools.ExpectedException(orm.exc.StaleDataError): # the coroutine should have encountered a stale data error because # the status update thread would have bumped the revision number # while it was waiting to commit coro.wait() # another attempt should work fine thing = _lock_blocked_name_update() self.assertEqual('a description', thing.description) self.assertEqual('newname', thing.name) self.assertGreater(thing.revision_number, revision_after_build) def test_standardattr_removed_on_subnet_delete(self): ctx = context.get_admin_context() network = self._make_network(ctx) subnet = self._make_subnet(ctx, network.id) self._test_standardattr_removed_on_obj_delete(ctx, subnet) def test_standardattr_removed_on_network_delete(self): ctx = context.get_admin_context() network = self._make_network(ctx) self._test_standardattr_removed_on_obj_delete(ctx, network) def test_standardattr_removed_on_subnetpool_delete(self): ctx = context.get_admin_context() spool = self._make_subnetpool(ctx) self._test_standardattr_removed_on_obj_delete(ctx, spool) def test_standardattr_removed_on_port_delete(self): ctx = context.get_admin_context() network = self._make_network(ctx) port = self._make_port(ctx, network.id) self._test_standardattr_removed_on_obj_delete(ctx, port) def test_standardattr_removed_on_sg_delete(self): ctx = context.get_admin_context() sg, rule = self._make_security_group_and_rule(ctx) self._test_standardattr_removed_on_obj_delete(ctx, sg) # make sure the attr entry was wiped out for the rule as well with testtools.ExpectedException(orm.exc.NoResultFound): self._get_neutron_attr(ctx, rule.standard_attr_id) def test_standardattr_removed_on_floating_ip_delete(self): ctx = context.get_admin_context() network = self._make_network(ctx) port = self._make_port(ctx, network.id) flip = self._make_floating_ip(ctx, port.id) # TODO(lujinluo): Change flip.db_obj to flip once all # codes are migrated to use Floating IP OVO object. self._test_standardattr_removed_on_obj_delete(ctx, flip.db_obj) def test_standardattr_removed_on_router_delete(self): ctx = context.get_admin_context() router = self._make_router(ctx) self._test_standardattr_removed_on_obj_delete(ctx, router) def test_resource_type_fields(self): ctx = context.get_admin_context() network = self._make_network(ctx) port = self._make_port(ctx, network.id) subnet = self._make_subnet(ctx, network.id) spool = self._make_subnetpool(ctx) for disc, obj in (('ports', port), ('networks', network), ('subnets', subnet), ('subnetpools', spool)): self.assertEqual( disc, obj.standard_attr.resource_type) class DbModelTenantTestCase(DbModelMixin, testlib_api.SqlTestCase): def _make_network(self, ctx): with db_api.context_manager.writer.using(ctx): network = models_v2.Network(name="net_net", status="OK", tenant_id='dbcheck', admin_state_up=True) ctx.session.add(network) return network def _make_subnet(self, ctx, network_id): with db_api.context_manager.writer.using(ctx): subnet = models_v2.Subnet(name="subsub", ip_version=4, tenant_id='dbcheck', cidr='turn_down_for_what', network_id=network_id) ctx.session.add(subnet) return subnet def _make_port(self, ctx, network_id): with ctx.session.begin(): port = models_v2.Port(network_id=network_id, mac_address='1', tenant_id='dbcheck', admin_state_up=True, status="COOL", device_id="devid", device_owner="me") ctx.session.add(port) return port def _make_subnetpool(self, ctx): with db_api.context_manager.writer.using(ctx): subnetpool = models_v2.SubnetPool( ip_version=4, default_prefixlen=4, min_prefixlen=4, max_prefixlen=4, shared=False, default_quota=4, address_scope_id='f', tenant_id='dbcheck', is_default=False ) ctx.session.add(subnetpool) return subnetpool class DbModelProjectTestCase(DbModelMixin, testlib_api.SqlTestCase): def _make_network(self, ctx): with db_api.context_manager.writer.using(ctx): network = models_v2.Network(name="net_net", status="OK", project_id='dbcheck', admin_state_up=True) ctx.session.add(network) return network def _make_subnet(self, ctx, network_id): with db_api.context_manager.writer.using(ctx): subnet = models_v2.Subnet(name="subsub", ip_version=4, project_id='dbcheck', cidr='turn_down_for_what', network_id=network_id) ctx.session.add(subnet) return subnet def _make_port(self, ctx, network_id): with ctx.session.begin(): port = models_v2.Port(network_id=network_id, mac_address='1', project_id='dbcheck', admin_state_up=True, status="COOL", device_id="devid", device_owner="me") ctx.session.add(port) return port def _make_subnetpool(self, ctx): with db_api.context_manager.writer.using(ctx): subnetpool = models_v2.SubnetPool( ip_version=4, default_prefixlen=4, min_prefixlen=4, max_prefixlen=4, shared=False, default_quota=4, address_scope_id='f', project_id='dbcheck', is_default=False ) ctx.session.add(subnetpool) return subnetpool class NeutronDbPluginV2AsMixinTestCase(NeutronDbPluginV2TestCase, testlib_api.SqlTestCase): """Tests for NeutronDbPluginV2 as Mixin. While NeutronDbPluginV2TestCase checks NeutronDbPlugin and all plugins as a complete plugin, this test case verifies abilities of NeutronDbPlugin which are provided to other plugins (e.g. DB operations). This test case may include tests only for NeutronDbPlugin, so this should not be used in unit tests for other plugins. """ def setUp(self): super(NeutronDbPluginV2AsMixinTestCase, self).setUp() self.plugin = importutils.import_object(DB_PLUGIN_KLASS) self.context = context.get_admin_context() self.net_data = {'network': {'id': 'fake-id', 'name': 'net1', 'admin_state_up': True, 'tenant_id': TEST_TENANT_ID, 'shared': False}} def test_create_network_with_default_status(self): net = self.plugin.create_network(self.context, self.net_data) default_net_create_status = 'ACTIVE' expected = [('id', 'fake-id'), ('name', 'net1'), ('admin_state_up', True), ('tenant_id', TEST_TENANT_ID), ('shared', False), ('status', default_net_create_status)] for k, v in expected: self.assertEqual(net[k], v) def test_create_network_with_status_BUILD(self): self.net_data['network']['status'] = 'BUILD' net = self.plugin.create_network(self.context, self.net_data) self.assertEqual(net['status'], 'BUILD') def test_get_user_allocation_for_dhcp_port_returns_none(self): plugin = directory.get_plugin() with self.network() as net, self.network() as net1: with self.subnet(network=net, cidr='10.0.0.0/24') as subnet,\ self.subnet(network=net1, cidr='10.0.1.0/24') as subnet1: with self.port(subnet=subnet, device_owner=constants.DEVICE_OWNER_DHCP),\ self.port(subnet=subnet1): # check that user allocations on another network don't # affect _subnet_get_user_allocation method res = plugin._subnet_get_user_allocation( context.get_admin_context(), subnet['subnet']['id']) self.assertIsNone(res) def test__validate_network_subnetpools(self): network = models_v2.Network() network.subnets = [models_v2.Subnet(subnetpool_id='test_id', ip_version=4)] new_subnetpool_id = None self.assertRaises(n_exc.NetworkSubnetPoolAffinityError, self.plugin.ipam._validate_network_subnetpools, network, new_subnetpool_id, 4) class TestNetworks(testlib_api.SqlTestCase): def setUp(self): super(TestNetworks, self).setUp() self._tenant_id = TEST_TENANT_ID # Update the plugin self.setup_coreplugin(DB_PLUGIN_KLASS) def _create_network(self, plugin, ctx, shared=True): network = {'network': {'name': 'net', 'shared': shared, 'admin_state_up': True, 'tenant_id': self._tenant_id}} created_network = plugin.create_network(ctx, network) return (network, created_network['id']) def _create_port(self, plugin, ctx, net_id, device_owner, tenant_id): port = {'port': {'name': 'port', 'network_id': net_id, 'mac_address': constants.ATTR_NOT_SPECIFIED, 'fixed_ips': constants.ATTR_NOT_SPECIFIED, 'admin_state_up': True, 'device_id': 'device_id', 'device_owner': device_owner, 'tenant_id': tenant_id}} plugin.create_port(ctx, port) def _test_update_shared_net_used(self, device_owner, expected_exception=None): plugin = directory.get_plugin() ctx = context.get_admin_context() network, net_id = self._create_network(plugin, ctx) self._create_port(plugin, ctx, net_id, device_owner, self._tenant_id + '1') network['network']['shared'] = False if (expected_exception): with testlib_api.ExpectedException(expected_exception): plugin.update_network(ctx, net_id, network) else: plugin.update_network(ctx, net_id, network) def test_update_shared_net_used_fails(self): self._test_update_shared_net_used('', n_exc.InvalidSharedSetting) def test_update_shared_net_used_as_router_gateway(self): self._test_update_shared_net_used( constants.DEVICE_OWNER_ROUTER_GW) def test_update_shared_net_used_by_floating_ip(self): self._test_update_shared_net_used( constants.DEVICE_OWNER_FLOATINGIP) class DbOperationBoundMixin(object): """Mixin to support tests that assert constraints on DB operations.""" admin = True def setUp(self, *args, **kwargs): super(DbOperationBoundMixin, self).setUp(*args, **kwargs) self.useFixture(fixture.APIDefinitionFixture()) self._recorded_statements = [] def _event_incrementer(conn, clauseelement, *args, **kwargs): self._recorded_statements.append(str(clauseelement)) engine = db_api.context_manager.writer.get_engine() db_api.sqla_listen(engine, 'after_execute', _event_incrementer) def _get_context(self): if self.admin: return context.get_admin_context() return context.Context('', 'fake') def get_api_kwargs(self): context_ = self._get_context() return {'set_context': True, 'tenant_id': context_.tenant} def _list_and_record_queries(self, resource, query_params=None): kwargs = {'neutron_context': self._get_context()} if query_params: kwargs['query_params'] = query_params # list once before tracking to flush out any quota recalculations. # otherwise the first list after a create will be different than # a subsequent list with no create. self._list(resource, **kwargs) self._recorded_statements = [] self.assertNotEqual([], self._list(resource, **kwargs)) # sanity check to make sure queries are being observed self.assertNotEqual(0, len(self._recorded_statements)) return list(self._recorded_statements) def _assert_object_list_queries_constant(self, obj_creator, plural, filters=None): obj_creator() before_queries = self._list_and_record_queries(plural) # one more thing shouldn't change the db query count obj = list(obj_creator().values())[0] after_queries = self._list_and_record_queries(plural) self.assertEqual(len(before_queries), len(after_queries), self._qry_fail_msg(before_queries, after_queries)) # using filters shouldn't change the count either if filters: query_params = "&".join(["%s=%s" % (f, obj[f]) for f in filters]) after_queries = self._list_and_record_queries(plural, query_params) self.assertEqual(len(before_queries), len(after_queries), self._qry_fail_msg(before_queries, after_queries)) def _qry_fail_msg(self, before, after): return "\n".join(["queries before:"] + before + ["queries after:"] + after) neutron-12.1.1/neutron/tests/unit/db/test_portsecurity_db_common.py0000664000175000017500000000627513553660047025735 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import port_security as psec from neutron.db import common_db_mixin from neutron.db import portsecurity_db_common as pdc from neutron.objects import base as objects_base from neutron.objects import network from neutron.objects.port.extensions import port_security as p_ps from neutron.tests import base class FakePlugin(pdc.PortSecurityDbCommon, common_db_mixin.CommonDbMixin): pass class PortSecurityDbCommonTestCase(base.BaseTestCase): def setUp(self): super(PortSecurityDbCommonTestCase, self).setUp() self.plugin = FakePlugin() def _test__get_security_binding_no_binding(self, getter): port_sec_enabled = True req = {psec.PORTSECURITY: port_sec_enabled} res = {} with mock.patch.object( objects_base.NeutronDbObject, 'get_object', return_value=None): val = getter(req, res) self.assertEqual(port_sec_enabled, val) def test__get_port_security_binding_no_binding(self): self._test__get_security_binding_no_binding( self.plugin._get_port_security_binding) def test__get_network_security_binding_no_binding(self): self._test__get_security_binding_no_binding( self.plugin._get_network_security_binding) def _test__process_security_update_no_binding(self, res_name, obj_cls, updater): req = {psec.PORTSECURITY: False} res = {'id': 'fake-id'} context = mock.MagicMock() with mock.patch.object( self.plugin, '_process_port_security_create') as creator: with mock.patch.object( objects_base.NeutronDbObject, 'get_object', return_value=None): updater(context, req, res) creator.assert_called_with(context, obj_cls, res_name, req, res) def test__process_port_port_security_update_no_binding(self): self._test__process_security_update_no_binding( 'port', p_ps.PortSecurity, self.plugin._process_port_port_security_update) def test__process_network_port_security_update_no_binding(self): self._test__process_security_update_no_binding( 'network', network.NetworkPortSecurity, self.plugin._process_network_port_security_update) def test__extend_port_security_dict_no_port_security(self): for db_data in ({'port_security': None, 'name': 'net1'}, {}): response_data = {} self.plugin._extend_port_security_dict(response_data, db_data) self.assertTrue(response_data[psec.PORTSECURITY]) neutron-12.1.1/neutron/tests/unit/db/test_standard_attr.py0000664000175000017500000001316213553660047023767 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import gc from neutron_lib import context from sqlalchemy.ext import declarative import testtools from neutron.db import standard_attr from neutron.tests import base from neutron.tests.unit import testlib_api class StandardAttrTestCase(base.BaseTestCase): def setUp(self): super(StandardAttrTestCase, self).setUp() self.addCleanup(gc.collect) def _make_decl_base(self): # construct a new base so we don't interfere with the main # base used in the sql test fixtures return declarative.declarative_base( cls=standard_attr.model_base.NeutronBaseV2) def test_standard_attr_resource_model_map(self): rs_map = standard_attr.get_standard_attr_resource_model_map() base = self._make_decl_base() class MyModel(standard_attr.HasStandardAttributes, standard_attr.model_base.HasId, base): api_collections = ['my_resource', 'my_resource2'] rs_map = standard_attr.get_standard_attr_resource_model_map() self.assertEqual(MyModel, rs_map['my_resource']) self.assertEqual(MyModel, rs_map['my_resource2']) class Dup(standard_attr.HasStandardAttributes, standard_attr.model_base.HasId, base): api_collections = ['my_resource'] with testtools.ExpectedException(RuntimeError): standard_attr.get_standard_attr_resource_model_map() def test_standard_attr_resource_parent_map(self): base = self._make_decl_base() class TagSupportModel(standard_attr.HasStandardAttributes, standard_attr.model_base.HasId, base): collection_resource_map = {'collection_name': 'member_name'} tag_support = True class TagUnsupportModel(standard_attr.HasStandardAttributes, standard_attr.model_base.HasId, base): collection_resource_map = {'collection_name2': 'member_name2'} tag_support = False class TagUnsupportModel2(standard_attr.HasStandardAttributes, standard_attr.model_base.HasId, base): collection_resource_map = {'collection_name3': 'member_name3'} parent_map = standard_attr.get_tag_resource_parent_map() self.assertEqual('member_name', parent_map['collection_name']) self.assertNotIn('collection_name2', parent_map) self.assertNotIn('collection_name3', parent_map) class DupTagSupportModel(standard_attr.HasStandardAttributes, standard_attr.model_base.HasId, base): collection_resource_map = {'collection_name': 'member_name'} tag_support = True with testtools.ExpectedException(RuntimeError): standard_attr.get_tag_resource_parent_map() class StandardAttrAPIImapctTestCase(testlib_api.SqlTestCase): """Test case to determine if a resource has had new fields exposed.""" def test_api_collections_are_expected(self): # NOTE to reviewers. If this test is being modified, it means the # resources being extended by standard attr extensions have changed. # Ensure that the patch has made this discoverable to API users. # This means a new extension for a new resource or a new extension # indicating that an existing resource now has standard attributes. # Ensure devref list of resources is updated at # doc/source/devref/api_extensions.rst expected = ['subnets', 'trunks', 'routers', 'segments', 'security_group_rules', 'networks', 'policies', 'subnetpools', 'ports', 'security_groups', 'floatingips', 'logs'] self.assertEqual( set(expected), set(standard_attr.get_standard_attr_resource_model_map().keys()) ) def test_api_tag_support_is_expected(self): # NOTE: If this test is being modified, it means the resources for tag # support are extended. It changes tag support API. The API change # should be exposed in release note for API users. And also it should # be list as other tag support resources in doc/source/devref/tag.rst expected = ['subnets', 'trunks', 'routers', 'networks', 'policies', 'subnetpools', 'ports', 'security_groups', 'floatingips'] self.assertEqual( set(expected), set(standard_attr.get_tag_resource_parent_map().keys()) ) class StandardAttrRevisesBulkDeleteTestCase(testlib_api.SqlTestCase): def test_bulk_delete_protection(self): # security group rules increment security groups so they must not be # allowed to be deleted in bulk mm = standard_attr.get_standard_attr_resource_model_map() sg_rule_model = mm['security_group_rules'] with testtools.ExpectedException(RuntimeError): ctx = context.get_admin_context() ctx.session.query(sg_rule_model).delete() neutron-12.1.1/neutron/tests/unit/db/test_common_db_mixin.py0000664000175000017500000000367713553660047024310 0ustar zuulzuul00000000000000# Copyright 2016 # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import context from neutron.db import _utils as db_utils from neutron.tests.unit import testlib_api class TestCommonHelpFunctions(testlib_api.SqlTestCase): def setUp(self): super(TestCommonHelpFunctions, self).setUp() self.admin_ctx = context.get_admin_context() def test__safe_creation_create_bindings_fails(self): create_fn = mock.Mock(return_value={'id': 1234}) create_bindings = mock.Mock(side_effect=ValueError) tx_check = lambda i: setattr(self, '_active', self.admin_ctx.session.is_active) delete_fn = mock.Mock(side_effect=tx_check) self.assertRaises(ValueError, db_utils.safe_creation, self.admin_ctx, create_fn, delete_fn, create_bindings) delete_fn.assert_called_once_with(1234) self.assertTrue(self._active) def test__safe_creation_deletion_fails(self): create_fn = mock.Mock(return_value={'id': 1234}) create_bindings = mock.Mock(side_effect=ValueError) delete_fn = mock.Mock(side_effect=EnvironmentError) self.assertRaises(ValueError, db_utils.safe_creation, self.admin_ctx, create_fn, delete_fn, create_bindings) delete_fn.assert_called_once_with(1234) neutron-12.1.1/neutron/tests/unit/db/test_migration.py0000664000175000017500000007360313553660047023134 0ustar zuulzuul00000000000000# Copyright 2012 New Dream Network, LLC (DreamHost) # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import os import re import sys import textwrap from alembic.autogenerate import api as alembic_ag_api from alembic import config as alembic_config from alembic.operations import ops as alembic_ops from alembic import script as alembic_script import fixtures import mock from neutron_lib.utils import helpers from oslo_utils import fileutils import pkg_resources import sqlalchemy as sa from testtools import matchers from neutron.conf.db import migration_cli from neutron.db import migration from neutron.db.migration import autogen from neutron.db.migration import cli from neutron.tests import base from neutron.tests import tools from neutron.tests.unit import testlib_api class FakeConfig(object): service = '' class FakeRevision(object): path = 'fakepath' def __init__(self, labels=None, down_revision=None, is_branch_point=False): if not labels: labels = set() self.branch_labels = labels self.down_revision = down_revision self.is_branch_point = is_branch_point self.revision = helpers.get_random_string(10) self.module = mock.MagicMock() class MigrationEntrypointsMemento(fixtures.Fixture): '''Create a copy of the migration entrypoints map so it can be restored during test cleanup. ''' def _setUp(self): self.ep_backup = {} for proj, ep in migration_cli.migration_entrypoints.items(): self.ep_backup[proj] = copy.copy(ep) self.addCleanup(self.restore) def restore(self): migration_cli.migration_entrypoints = self.ep_backup class TestDbMigration(base.BaseTestCase): def setUp(self): super(TestDbMigration, self).setUp() mock.patch('alembic.op.get_bind').start() self.mock_alembic_is_offline = mock.patch( 'alembic.context.is_offline_mode', return_value=False).start() self.mock_alembic_is_offline.return_value = False self.mock_sa_inspector = mock.patch( 'sqlalchemy.engine.reflection.Inspector').start() def _prepare_mocked_sqlalchemy_inspector(self): mock_inspector = mock.MagicMock() mock_inspector.get_table_names.return_value = ['foo', 'bar'] mock_inspector.get_columns.return_value = [{'name': 'foo_column'}, {'name': 'bar_column'}] self.mock_sa_inspector.from_engine.return_value = mock_inspector def test_schema_has_table(self): self._prepare_mocked_sqlalchemy_inspector() self.assertTrue(migration.schema_has_table('foo')) def test_schema_has_table_raises_if_offline(self): self.mock_alembic_is_offline.return_value = True self.assertRaises(RuntimeError, migration.schema_has_table, 'foo') def test_schema_has_column_missing_table(self): self._prepare_mocked_sqlalchemy_inspector() self.assertFalse(migration.schema_has_column('meh', 'meh')) def test_schema_has_column(self): self._prepare_mocked_sqlalchemy_inspector() self.assertTrue(migration.schema_has_column('foo', 'foo_column')) def test_schema_has_column_raises_if_offline(self): self.mock_alembic_is_offline.return_value = True self.assertRaises(RuntimeError, migration.schema_has_column, 'foo', 'foo_col') def test_schema_has_column_missing_column(self): self._prepare_mocked_sqlalchemy_inspector() self.assertFalse(migration.schema_has_column( 'foo', column_name='meh')) class TestCli(base.BaseTestCase): def setUp(self): super(TestCli, self).setUp() self.do_alembic_cmd_p = mock.patch.object(cli, 'do_alembic_command') self.do_alembic_cmd = self.do_alembic_cmd_p.start() self.mock_alembic_err = mock.patch('alembic.util.err').start() self.mock_alembic_warn = mock.patch('alembic.util.warn').start() self.mock_alembic_err.side_effect = SystemExit def mocked_root_dir(cfg): return os.path.join('/fake/dir', cli._get_project_base(cfg)) mock_root = mock.patch.object(cli, '_get_package_root_dir').start() mock_root.side_effect = mocked_root_dir # Avoid creating fake directories mock.patch('oslo_utils.fileutils.ensure_tree').start() # Set up some configs and entrypoints for tests to chew on self.configs = [] self.projects = ('neutron', 'networking-foo', 'neutron-fwaas') ini = os.path.join(os.path.dirname(cli.__file__), 'alembic.ini') self.useFixture(MigrationEntrypointsMemento()) migration_cli.migration_entrypoints = {} for project in self.projects: config = alembic_config.Config(ini) config.set_main_option('neutron_project', project) module_name = project.replace('-', '_') + '.db.migration' attrs = ('alembic_migrations',) script_location = ':'.join([module_name, attrs[0]]) config.set_main_option('script_location', script_location) self.configs.append(config) entrypoint = pkg_resources.EntryPoint(project, module_name, attrs=attrs) migration_cli.migration_entrypoints[project] = entrypoint def _main_test_helper(self, argv, func_name, exp_kwargs=[{}]): with mock.patch.object(sys, 'argv', argv),\ mock.patch.object(cli, 'run_sanity_checks'),\ mock.patch.object(cli, 'validate_revisions'): cli.main() def _append_version_path(args): args = copy.copy(args) if 'autogenerate' in args and not args['autogenerate']: args['version_path'] = mock.ANY return args self.do_alembic_cmd.assert_has_calls( [mock.call(mock.ANY, func_name, **_append_version_path(kwargs)) for kwargs in exp_kwargs] ) def test_stamp(self): self._main_test_helper( ['prog', 'stamp', 'foo'], 'stamp', [{'revision': 'foo', 'sql': False}] ) self._main_test_helper( ['prog', 'stamp', 'foo', '--sql'], 'stamp', [{'revision': 'foo', 'sql': True}] ) def _validate_cmd(self, cmd): self._main_test_helper( ['prog', cmd], cmd, [{'verbose': False}]) self._main_test_helper( ['prog', cmd, '--verbose'], cmd, [{'verbose': True}]) def test_branches(self): self._validate_cmd('branches') def test_current(self): self._validate_cmd('current') def test_history(self): self._validate_cmd('history') def test_heads(self): self._validate_cmd('heads') def test_check_migration(self): with mock.patch.object(cli, 'validate_head_files') as validate: self._main_test_helper(['prog', 'check_migration'], 'branches') self.assertEqual(len(self.projects), validate.call_count) def _test_database_sync_revision(self, separate_branches=True): with mock.patch.object(cli, 'update_head_files') as update: if separate_branches: mock.patch('os.path.exists').start() expected_kwargs = [{ 'message': 'message', 'sql': False, 'autogenerate': True, }] self._main_test_helper( ['prog', 'revision', '--autogenerate', '-m', 'message'], 'revision', expected_kwargs ) self.assertEqual(len(self.projects), update.call_count) update.reset_mock() expected_kwargs = [{ 'message': 'message', 'sql': True, 'autogenerate': False, 'head': cli._get_branch_head(branch) } for branch in cli.MIGRATION_BRANCHES] for kwarg in expected_kwargs: kwarg['autogenerate'] = False kwarg['sql'] = True self._main_test_helper( ['prog', 'revision', '--sql', '-m', 'message'], 'revision', expected_kwargs ) self.assertEqual(len(self.projects), update.call_count) update.reset_mock() expected_kwargs = [{ 'message': 'message', 'sql': False, 'autogenerate': False, 'head': 'expand@head' }] self._main_test_helper( ['prog', 'revision', '-m', 'message', '--expand'], 'revision', expected_kwargs ) self.assertEqual(len(self.projects), update.call_count) update.reset_mock() for kwarg in expected_kwargs: kwarg['head'] = 'contract@head' self._main_test_helper( ['prog', 'revision', '-m', 'message', '--contract'], 'revision', expected_kwargs ) self.assertEqual(len(self.projects), update.call_count) def test_database_sync_revision(self): self._test_database_sync_revision() def test_database_sync_revision_no_branches(self): # Test that old branchless approach is still supported self._test_database_sync_revision(separate_branches=False) def test_upgrade_revision(self): self._main_test_helper( ['prog', 'upgrade', '--sql', 'head'], 'upgrade', [{'desc': None, 'revision': 'heads', 'sql': True}] ) def test_upgrade_delta(self): self._main_test_helper( ['prog', 'upgrade', '--delta', '3'], 'upgrade', [{'desc': None, 'revision': '+3', 'sql': False}] ) def test_upgrade_revision_delta(self): self._main_test_helper( ['prog', 'upgrade', 'kilo', '--delta', '3'], 'upgrade', [{'desc': None, 'revision': 'kilo+3', 'sql': False}] ) def test_upgrade_expand(self): self._main_test_helper( ['prog', 'upgrade', '--expand'], 'upgrade', [{'desc': cli.EXPAND_BRANCH, 'revision': 'expand@head', 'sql': False}] ) def test_upgrade_expand_contract_are_mutually_exclusive(self): with testlib_api.ExpectedException(SystemExit): self._main_test_helper( ['prog', 'upgrade', '--expand --contract'], 'upgrade') def _test_upgrade_conflicts_with_revision(self, mode): with testlib_api.ExpectedException(SystemExit): self._main_test_helper( ['prog', 'upgrade', '--%s revision1' % mode], 'upgrade') def _test_upgrade_conflicts_with_delta(self, mode): with testlib_api.ExpectedException(SystemExit): self._main_test_helper( ['prog', 'upgrade', '--%s +3' % mode], 'upgrade') def _test_revision_autogenerate_conflicts_with_branch(self, branch): with testlib_api.ExpectedException(SystemExit): self._main_test_helper( ['prog', 'revision', '--autogenerate', '--%s' % branch], 'revision') def test_revision_autogenerate_conflicts_with_expand(self): self._test_revision_autogenerate_conflicts_with_branch( cli.EXPAND_BRANCH) def test_revision_autogenerate_conflicts_with_contract(self): self._test_revision_autogenerate_conflicts_with_branch( cli.CONTRACT_BRANCH) def test_upgrade_expand_conflicts_with_revision(self): self._test_upgrade_conflicts_with_revision('expand') def test_upgrade_contract_conflicts_with_revision(self): self._test_upgrade_conflicts_with_revision('contract') def test_upgrade_expand_conflicts_with_delta(self): self._test_upgrade_conflicts_with_delta('expand') def test_upgrade_contract_conflicts_with_delta(self): self._test_upgrade_conflicts_with_delta('contract') def test_upgrade_contract(self): self._main_test_helper( ['prog', 'upgrade', '--contract'], 'upgrade', [{'desc': cli.CONTRACT_BRANCH, 'revision': 'contract@head', 'sql': False}] ) @mock.patch('alembic.script.ScriptDirectory.walk_revisions') def test_upgrade_milestone_expand_before_contract(self, walk_mock): c_revs = [FakeRevision(labels={cli.CONTRACT_BRANCH}) for r in range(5)] c_revs[1].module.neutron_milestone = [migration.LIBERTY] e_revs = [FakeRevision(labels={cli.EXPAND_BRANCH}) for r in range(5)] e_revs[3].module.neutron_milestone = [migration.LIBERTY] walk_mock.return_value = c_revs + e_revs self._main_test_helper( ['prog', '--subproject', 'neutron', 'upgrade', 'liberty'], 'upgrade', [{'desc': cli.EXPAND_BRANCH, 'revision': e_revs[3].revision, 'sql': False}, {'desc': cli.CONTRACT_BRANCH, 'revision': c_revs[1].revision, 'sql': False}] ) def assert_command_fails(self, command): # Avoid cluttering stdout with argparse error messages mock.patch('argparse.ArgumentParser._print_message').start() with mock.patch.object(sys, 'argv', command), mock.patch.object( cli, 'run_sanity_checks'): self.assertRaises(SystemExit, cli.main) def test_downgrade_fails(self): self.assert_command_fails(['prog', 'downgrade', '--sql', 'juno']) def test_upgrade_negative_relative_revision_fails(self): self.assert_command_fails(['prog', 'upgrade', '-2']) def test_upgrade_negative_delta_fails(self): self.assert_command_fails(['prog', 'upgrade', '--delta', '-2']) def test_upgrade_rejects_delta_with_relative_revision(self): self.assert_command_fails(['prog', 'upgrade', '+2', '--delta', '3']) def _test_validate_head_files_helper(self, heads, contract_head='', expand_head=''): fake_config = self.configs[0] head_files_not_exist = (contract_head == expand_head == '') with mock.patch('alembic.script.ScriptDirectory.from_config') as fc,\ mock.patch('os.path.exists') as os_mock: if head_files_not_exist: os_mock.return_value = False else: os_mock.return_value = True fc.return_value.get_heads.return_value = heads revs = {heads[0]: FakeRevision(labels=cli.CONTRACT_BRANCH), heads[1]: FakeRevision(labels=cli.EXPAND_BRANCH)} fc.return_value.get_revision.side_effect = revs.__getitem__ mock_open_con = self.useFixture( tools.OpenFixture(cli._get_contract_head_file_path( fake_config), contract_head + '\n')).mock_open mock_open_ex = self.useFixture( tools.OpenFixture(cli._get_expand_head_file_path( fake_config), expand_head + '\n')).mock_open if contract_head in heads and expand_head in heads: cli.validate_head_files(fake_config) elif head_files_not_exist: cli.validate_head_files(fake_config) self.assertTrue(self.mock_alembic_warn.called) else: self.assertRaises( SystemExit, cli.validate_head_files, fake_config ) self.assertTrue(self.mock_alembic_err.called) if contract_head in heads and expand_head in heads: mock_open_ex.assert_called_with( cli._get_expand_head_file_path(fake_config)) mock_open_con.assert_called_with( cli._get_contract_head_file_path(fake_config)) if not head_files_not_exist: fc.assert_called_once_with(fake_config) def test_validate_head_files_success(self): self._test_validate_head_files_helper(['a', 'b'], contract_head='a', expand_head='b') def test_validate_head_files_missing_file(self): self._test_validate_head_files_helper(['a', 'b']) def test_validate_head_files_wrong_contents(self): self._test_validate_head_files_helper(['a', 'b'], contract_head='c', expand_head='d') @mock.patch.object(fileutils, 'delete_if_exists') def test_update_head_files_success(self, *mocks): heads = ['a', 'b'] mock_open_con = self.useFixture( tools.OpenFixture(cli._get_contract_head_file_path( self.configs[0]))).mock_open mock_open_ex = self.useFixture( tools.OpenFixture(cli._get_expand_head_file_path( self.configs[0]))).mock_open with mock.patch('alembic.script.ScriptDirectory.from_config') as fc: fc.return_value.get_heads.return_value = heads revs = {heads[0]: FakeRevision(labels=cli.CONTRACT_BRANCH), heads[1]: FakeRevision(labels=cli.EXPAND_BRANCH)} fc.return_value.get_revision.side_effect = revs.__getitem__ cli.update_head_files(self.configs[0]) mock_open_con.return_value.write.assert_called_with( heads[0] + '\n') mock_open_ex.return_value.write.assert_called_with(heads[1] + '\n') old_head_file = cli._get_head_file_path( self.configs[0]) old_heads_file = cli._get_heads_file_path( self.configs[0]) delete_if_exists = mocks[0] self.assertIn(mock.call(old_head_file), delete_if_exists.call_args_list) self.assertIn(mock.call(old_heads_file), delete_if_exists.call_args_list) def test_get_project_base(self): config = alembic_config.Config() config.set_main_option('script_location', 'a.b.c:d') proj_base = cli._get_project_base(config) self.assertEqual('a', proj_base) def test_get_root_versions_dir(self): config = alembic_config.Config() config.set_main_option('script_location', 'a.b.c:d') versions_dir = cli._get_root_versions_dir(config) self.assertEqual('/fake/dir/a/a/b/c/d/versions', versions_dir) def test_get_subproject_script_location(self): foo_ep = cli._get_subproject_script_location('networking-foo') expected = 'networking_foo.db.migration:alembic_migrations' self.assertEqual(expected, foo_ep) def test_get_subproject_script_location_not_installed(self): self.assertRaises( SystemExit, cli._get_subproject_script_location, 'not-installed') def test_get_subproject_base_not_installed(self): self.assertRaises( SystemExit, cli._get_subproject_base, 'not-installed') def test__compare_labels_ok(self): labels = {'label1', 'label2'} fake_revision = FakeRevision(labels) cli._compare_labels(fake_revision, {'label1', 'label2'}) def test__compare_labels_fail_unexpected_labels(self): labels = {'label1', 'label2', 'label3'} fake_revision = FakeRevision(labels) self.assertRaises( SystemExit, cli._compare_labels, fake_revision, {'label1', 'label2'}) @mock.patch.object(cli, '_compare_labels') def test__validate_single_revision_labels_branchless_fail_different_labels( self, compare_mock): fake_down_revision = FakeRevision() fake_revision = FakeRevision(down_revision=fake_down_revision) script_dir = mock.Mock() script_dir.get_revision.return_value = fake_down_revision cli._validate_single_revision_labels(script_dir, fake_revision, label=None) expected_labels = set() compare_mock.assert_has_calls( [mock.call(revision, expected_labels) for revision in (fake_revision, fake_down_revision)] ) @mock.patch.object(cli, '_compare_labels') def test__validate_single_revision_labels_branches_fail_different_labels( self, compare_mock): fake_down_revision = FakeRevision() fake_revision = FakeRevision(down_revision=fake_down_revision) script_dir = mock.Mock() script_dir.get_revision.return_value = fake_down_revision cli._validate_single_revision_labels( script_dir, fake_revision, label='fakebranch') expected_labels = {'fakebranch'} compare_mock.assert_has_calls( [mock.call(revision, expected_labels) for revision in (fake_revision, fake_down_revision)] ) @mock.patch.object(cli, '_validate_single_revision_labels') def test__validate_revision_validates_branches(self, validate_mock): script_dir = mock.Mock() fake_revision = FakeRevision() branch = cli.MIGRATION_BRANCHES[0] fake_revision.path = os.path.join('/fake/path', branch) cli._validate_revision(script_dir, fake_revision) validate_mock.assert_called_with( script_dir, fake_revision, label=branch) @mock.patch.object(cli, '_validate_single_revision_labels') def test__validate_revision_validates_branchless_migrations( self, validate_mock): script_dir = mock.Mock() fake_revision = FakeRevision() cli._validate_revision(script_dir, fake_revision) validate_mock.assert_called_with(script_dir, fake_revision) @mock.patch.object(cli, '_validate_revision') @mock.patch('alembic.script.ScriptDirectory.walk_revisions') def test_validate_revisions_walks_thru_all_revisions( self, walk_mock, validate_mock): revisions = [FakeRevision() for i in range(10)] walk_mock.return_value = revisions cli.validate_revisions(self.configs[0]) validate_mock.assert_has_calls( [mock.call(mock.ANY, revision) for revision in revisions] ) @mock.patch.object(cli, '_validate_revision') @mock.patch('alembic.script.ScriptDirectory.walk_revisions') def test_validate_revisions_fails_on_multiple_branch_points( self, walk_mock, validate_mock): revisions = [FakeRevision(is_branch_point=True) for i in range(2)] walk_mock.return_value = revisions self.assertRaises( SystemExit, cli.validate_revisions, self.configs[0]) @mock.patch('alembic.script.ScriptDirectory.walk_revisions') def test__get_branch_points(self, walk_mock): revisions = [FakeRevision(is_branch_point=tools.get_random_boolean) for i in range(50)] walk_mock.return_value = revisions script_dir = alembic_script.ScriptDirectory.from_config( self.configs[0]) self.assertEqual(set(rev for rev in revisions if rev.is_branch_point), set(cli._get_branch_points(script_dir))) @mock.patch.object(cli, '_get_version_branch_path') def test_autogen_process_directives(self, get_version_branch_path): get_version_branch_path.side_effect = lambda cfg, release, branch: ( "/foo/expand" if branch == 'expand' else "/foo/contract") migration_script = alembic_ops.MigrationScript( 'eced083f5df', # these directives will be split into separate # expand/contract scripts alembic_ops.UpgradeOps( ops=[ alembic_ops.CreateTableOp( 'organization', [ sa.Column('id', sa.Integer(), primary_key=True), sa.Column('name', sa.String(50), nullable=False) ] ), alembic_ops.ModifyTableOps( 'user', ops=[ alembic_ops.AddColumnOp( 'user', sa.Column('organization_id', sa.Integer()) ), alembic_ops.CreateForeignKeyOp( 'org_fk', 'user', 'organization', ['organization_id'], ['id'] ), alembic_ops.DropConstraintOp( 'user', 'uq_user_org' ), alembic_ops.DropColumnOp( 'user', 'organization_name' ) ] ) ] ), # these will be discarded alembic_ops.DowngradeOps( ops=[ alembic_ops.AddColumnOp( 'user', sa.Column( 'organization_name', sa.String(50), nullable=True) ), alembic_ops.CreateUniqueConstraintOp( 'uq_user_org', 'user', ['user_name', 'organization_name'] ), alembic_ops.ModifyTableOps( 'user', ops=[ alembic_ops.DropConstraintOp('org_fk', 'user'), alembic_ops.DropColumnOp('user', 'organization_id') ] ), alembic_ops.DropTableOp('organization') ] ), message='create the organization table and ' 'replace user.organization_name' ) directives = [migration_script] autogen.process_revision_directives( mock.Mock(), mock.Mock(), directives ) expand = directives[0] contract = directives[1] self.assertEqual("/foo/expand", expand.version_path) self.assertEqual("/foo/contract", contract.version_path) self.assertTrue(expand.downgrade_ops.is_empty()) self.assertTrue(contract.downgrade_ops.is_empty()) def _get_regex(s): s = textwrap.dedent(s) s = re.escape(s) # alembic 0.8.9 added additional leading '# ' before comments return s.replace('\\#\\#\\#\\ ', '(# )?### ') expected_regex = ("""\ ### commands auto generated by Alembic - please adjust! ### op.create_table('organization', sa.Column('id', sa.Integer(), nullable=False), sa.Column('name', sa.String(length=50), nullable=False), sa.PrimaryKeyConstraint('id') ) op.add_column('user', """ """sa.Column('organization_id', sa.Integer(), nullable=True)) op.create_foreign_key('org_fk', 'user', """ """'organization', ['organization_id'], ['id']) ### end Alembic commands ###""") self.assertThat( alembic_ag_api.render_python_code(expand.upgrade_ops), matchers.MatchesRegex(_get_regex(expected_regex))) expected_regex = ("""\ ### commands auto generated by Alembic - please adjust! ### op.drop_constraint('user', 'uq_user_org', type_=None) op.drop_column('user', 'organization_name') ### end Alembic commands ###""") self.assertThat( alembic_ag_api.render_python_code(contract.upgrade_ops), matchers.MatchesRegex(_get_regex(expected_regex))) @mock.patch('alembic.script.ScriptDirectory.walk_revisions') def test__find_milestone_revisions_one_branch(self, walk_mock): c_revs = [FakeRevision(labels={cli.CONTRACT_BRANCH}) for r in range(5)] c_revs[1].module.neutron_milestone = [migration.LIBERTY] walk_mock.return_value = c_revs m = cli._find_milestone_revisions(self.configs[0], 'liberty', cli.CONTRACT_BRANCH) self.assertEqual(1, len(m)) m = cli._find_milestone_revisions(self.configs[0], 'liberty', cli.EXPAND_BRANCH) self.assertEqual(0, len(m)) @mock.patch('alembic.script.ScriptDirectory.walk_revisions') def test__find_milestone_revisions_two_branches(self, walk_mock): c_revs = [FakeRevision(labels={cli.CONTRACT_BRANCH}) for r in range(5)] c_revs[1].module.neutron_milestone = [migration.LIBERTY] e_revs = [FakeRevision(labels={cli.EXPAND_BRANCH}) for r in range(5)] e_revs[3].module.neutron_milestone = [migration.LIBERTY] walk_mock.return_value = c_revs + e_revs m = cli._find_milestone_revisions(self.configs[0], 'liberty') self.assertEqual(2, len(m)) m = cli._find_milestone_revisions(self.configs[0], 'mitaka') self.assertEqual(0, len(m)) @mock.patch('alembic.script.ScriptDirectory.walk_revisions') def test__find_milestone_revisions_branchless(self, walk_mock): revisions = [FakeRevision() for r in range(5)] revisions[2].module.neutron_milestone = [migration.LIBERTY] walk_mock.return_value = revisions m = cli._find_milestone_revisions(self.configs[0], 'liberty') self.assertEqual(1, len(m)) m = cli._find_milestone_revisions(self.configs[0], 'mitaka') self.assertEqual(0, len(m)) class TestSafetyChecks(base.BaseTestCase): def test_validate_revisions(self, *mocks): cli.validate_revisions(cli.get_neutron_config()) neutron-12.1.1/neutron/tests/unit/db/test_l3_dvr_db.py0000664000175000017500000014015713553660047023000 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation, all rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as const from neutron_lib import context from neutron_lib import exceptions from neutron_lib.exceptions import l3 as l3_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_utils import uuidutils from neutron.db import agents_db from neutron.db import common_db_mixin from neutron.db import l3_db from neutron.db import l3_dvr_db from neutron.db import l3_dvrscheduler_db from neutron.db.models import l3 as l3_models from neutron.db import models_v2 from neutron.objects import agent as agent_obj from neutron.objects import l3agent as rb_obj from neutron.objects import router as router_obj from neutron.tests.unit.db import test_db_base_plugin_v2 _uuid = uuidutils.generate_uuid class FakeL3Plugin(common_db_mixin.CommonDbMixin, l3_dvr_db.L3_NAT_with_dvr_db_mixin, l3_dvrscheduler_db.L3_DVRsch_db_mixin, agents_db.AgentDbMixin): pass class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def setUp(self): super(L3DvrTestCase, self).setUp(plugin='ml2') self.core_plugin = directory.get_plugin() self.ctx = context.get_admin_context() self.mixin = FakeL3Plugin() directory.add_plugin(plugin_constants.L3, self.mixin) def _create_router(self, router): with self.ctx.session.begin(subtransactions=True): return self.mixin._create_router_db(self.ctx, router, 'foo_tenant') def create_port(self, net_id, port_info): with self.ctx.session.begin(subtransactions=True): return self._create_port(self.fmt, net_id, **port_info) def _test__create_router_db(self, expected=False, distributed=None): router = {'name': 'foo_router', 'admin_state_up': True} if distributed is not None: router['distributed'] = distributed result = self._create_router(router) self.assertEqual(expected, result.extra_attributes['distributed']) def test_create_router_db_default(self): self._test__create_router_db(expected=False) def test_create_router_db_centralized(self): self._test__create_router_db(expected=False, distributed=False) def test_create_router_db_distributed(self): self._test__create_router_db(expected=True, distributed=True) def test__validate_router_migration_on_router_update(self): router = { 'name': 'foo_router', 'admin_state_up': True, 'distributed': True } router_db = self._create_router(router) self.assertFalse(self.mixin._validate_router_migration( self.ctx, router_db, {'name': 'foo_router_2'})) def test__validate_router_migration_raise_error(self): router = { 'name': 'foo_router', 'admin_state_up': True, 'distributed': True } router_db = self._create_router(router) self.assertRaises(exceptions.BadRequest, self.mixin._validate_router_migration, self.ctx, router_db, {'distributed': False}) def test_upgrade_active_router_to_distributed_validation_failure(self): router = {'name': 'foo_router', 'admin_state_up': True} router_db = self._create_router(router) update = {'distributed': True} self.assertRaises(exceptions.BadRequest, self.mixin._validate_router_migration, self.ctx, router_db, update) def test_update_router_db_centralized_to_distributed(self): router = {'name': 'foo_router', 'admin_state_up': True} agent = {'id': _uuid()} distributed = {'distributed': True} router_db = self._create_router(router) router_id = router_db['id'] self.assertFalse(router_db.extra_attributes.distributed) self.mixin._get_router = mock.Mock(return_value=router_db) self.mixin._validate_router_migration = mock.Mock() self.mixin._migrate_router_ports = mock.Mock() self.mixin.list_l3_agents_hosting_router = mock.Mock( return_value={'agents': [agent]}) self.mixin._unbind_router = mock.Mock() router_db = self.mixin._update_router_db( self.ctx, router_id, distributed) # Assert that the DB value has changed self.assertTrue(router_db.extra_attributes.distributed) self.assertEqual(1, self.mixin._migrate_router_ports.call_count) def _test_get_device_owner(self, is_distributed=False, expected=const.DEVICE_OWNER_ROUTER_INTF, pass_router_id=True): router = { 'name': 'foo_router', 'admin_state_up': True, 'distributed': is_distributed } router_db = self._create_router(router) router_pass = router_db['id'] if pass_router_id else router_db with mock.patch.object(self.mixin, '_get_router') as f: f.return_value = router_db result = self.mixin._get_device_owner(self.ctx, router_pass) self.assertEqual(expected, result) def test_get_device_owner_by_router_id(self): self._test_get_device_owner() def test__get_device_owner_centralized(self): self._test_get_device_owner(pass_router_id=False) def test__get_device_owner_distributed(self): self._test_get_device_owner( is_distributed=True, expected=const.DEVICE_OWNER_DVR_INTERFACE, pass_router_id=False) def _test__is_distributed_router(self, router, expected): result = l3_dvr_db.is_distributed_router(router) self.assertEqual(expected, result) def test__is_distributed_router_by_db_object(self): router = {'name': 'foo_router', 'admin_state_up': True} router_db = self._create_router(router) self.mixin._get_device_owner(mock.ANY, router_db) def test__is_distributed_router_default(self): router = {'id': 'foo_router_id'} self._test__is_distributed_router(router, False) def test__is_distributed_router_centralized(self): router = {'id': 'foo_router_id', 'distributed': False} self._test__is_distributed_router(router, False) def test__is_distributed_router_distributed(self): router = {'id': 'foo_router_id', 'distributed': True} self._test__is_distributed_router(router, True) def test__get_agent_gw_ports_exist_for_network(self): plugin = mock.Mock() directory.add_plugin(plugin_constants.CORE, plugin) plugin.get_ports.return_value = [] self.mixin._get_agent_gw_ports_exist_for_network( self.ctx, 'network_id', 'host', 'agent_id') plugin.get_ports.assert_called_with(self.ctx, { 'network_id': ['network_id'], 'device_id': ['agent_id'], 'device_owner': [const.DEVICE_OWNER_AGENT_GW]}) def _test_prepare_direct_delete_dvr_internal_ports(self, port): plugin = mock.Mock() directory.add_plugin(plugin_constants.CORE, plugin) plugin.get_port.return_value = port self.mixin._router_exists = mock.Mock(return_value=True) self.assertRaises(exceptions.ServicePortInUse, self.mixin.prevent_l3_port_deletion, self.ctx, port['id']) def test_prevent_delete_floatingip_agent_gateway_port(self): port = { 'id': 'my_port_id', 'fixed_ips': mock.ANY, 'device_id': 'r_id', 'device_owner': const.DEVICE_OWNER_AGENT_GW } self._test_prepare_direct_delete_dvr_internal_ports(port) def test_prevent_delete_csnat_port(self): port = { 'id': 'my_port_id', 'fixed_ips': mock.ANY, 'device_id': 'r_id', 'device_owner': const.DEVICE_OWNER_ROUTER_SNAT } self._test_prepare_direct_delete_dvr_internal_ports(port) def test__create_gw_port_with_no_gateway(self): router = { 'name': 'foo_router', 'admin_state_up': True, 'distributed': True, } router_db = self._create_router(router) router_id = router_db['id'] self.assertTrue(router_db.extra_attributes.distributed) with mock.patch.object(l3_dvr_db.l3_db.L3_NAT_db_mixin, '_create_gw_port'),\ mock.patch.object( self.mixin, '_create_snat_intf_ports_if_not_exists') as cs: self.mixin._create_gw_port( self.ctx, router_id, router_db, mock.ANY, mock.ANY) self.assertFalse(cs.call_count) def test_build_routers_list_with_gw_port_mismatch(self): routers = [{'gw_port_id': 'foo_gw_port_id', 'id': 'foo_router_id'}] gw_ports = {} routers = self.mixin._build_routers_list(self.ctx, routers, gw_ports) self.assertIsNone(routers[0].get('gw_port')) def _helper_delete_floatingip_agent_gateway_port(self, port_host): ports = [{ 'id': 'my_port_id', portbindings.HOST_ID: 'foo_host', 'network_id': 'ext_network_id', 'device_owner': const.DEVICE_OWNER_ROUTER_GW }, { 'id': 'my_new_port_id', portbindings.HOST_ID: 'my_foo_host', 'network_id': 'ext_network_id', 'device_owner': const.DEVICE_OWNER_ROUTER_GW }] plugin = mock.Mock() directory.add_plugin(plugin_constants.CORE, plugin) plugin.get_ports.return_value = ports self.mixin.delete_floatingip_agent_gateway_port( self.ctx, port_host, 'ext_network_id') plugin.get_ports.assert_called_with(self.ctx, filters={ 'network_id': ['ext_network_id'], 'device_owner': [const.DEVICE_OWNER_AGENT_GW]}) if port_host: plugin.ipam.delete_port.assert_called_once_with( self.ctx, 'my_port_id') else: plugin.ipam.delete_port.assert_called_with( self.ctx, 'my_new_port_id') def test_delete_floatingip_agent_gateway_port_without_host_id(self): self._helper_delete_floatingip_agent_gateway_port(None) def test_delete_floatingip_agent_gateway_port_with_host_id(self): self._helper_delete_floatingip_agent_gateway_port( 'foo_host') def _setup_delete_current_gw_port_deletes_dvr_internal_ports( self, port=None, gw_port=True, new_network_id='ext_net_id_2'): router_db = { 'name': 'foo_router', 'admin_state_up': True, 'distributed': True } router = self._create_router(router_db) if gw_port: with self.subnet(cidr='10.10.10.0/24') as subnet: port_dict = { 'device_id': router.id, 'device_owner': const.DEVICE_OWNER_ROUTER_GW, 'admin_state_up': True, 'fixed_ips': [{'subnet_id': subnet['subnet']['id'], 'ip_address': '10.10.10.100'}] } net_id = subnet['subnet']['network_id'] port_res = self.create_port(net_id, port_dict) port_res_dict = self.deserialize(self.fmt, port_res) with self.ctx.session.begin(subtransactions=True): port_db = self.ctx.session.query(models_v2.Port).filter_by( id=port_res_dict['port']['id']).one() router.gw_port = port_db router_port = l3_models.RouterPort( router_id=router.id, port_id=port_db.id, port_type=const.DEVICE_OWNER_ROUTER_GW ) self.ctx.session.add(router) self.ctx.session.add(router_port) else: net_id = None plugin = mock.Mock() directory.add_plugin(plugin_constants.CORE, plugin) with mock.patch.object(l3_dvr_db.l3_db.L3_NAT_db_mixin, 'router_gw_port_has_floating_ips', return_value=False),\ mock.patch.object( self.mixin, '_get_router') as grtr,\ mock.patch.object( self.mixin, 'delete_csnat_router_interface_ports') as del_csnat_port,\ mock.patch.object( self.mixin, 'delete_floatingip_agent_gateway_port') as del_agent_gw_port,\ mock.patch.object( self.mixin.l3_rpc_notifier, 'delete_fipnamespace_for_ext_net') as del_fip: plugin.get_ports.return_value = port grtr.return_value = router self.mixin._delete_current_gw_port( self.ctx, router['id'], router, new_network_id) return router, plugin, net_id, del_csnat_port,\ del_agent_gw_port, del_fip def test_delete_current_gw_port_deletes_fip_agent_gw_port_and_fipnamespace( self): rtr, plugin, ext_net_id, d_csnat_port, d_agent_gw_port, del_fip = ( self._setup_delete_current_gw_port_deletes_dvr_internal_ports()) self.assertFalse(d_csnat_port.called) self.assertTrue(d_agent_gw_port.called) d_agent_gw_port.assert_called_once_with(mock.ANY, None, ext_net_id) del_fip.assert_called_once_with(self.ctx, ext_net_id) def test_delete_current_gw_port_never_calls_delete_fip_agent_gw_port(self): port = [{ 'id': 'my_port_id', 'network_id': 'ext_net_id', 'device_owner': const.DEVICE_OWNER_ROUTER_GW }, { 'id': 'my_new_port_id', 'network_id': 'ext_net_id', 'device_owner': const.DEVICE_OWNER_ROUTER_GW }] rtr, plugin, ext_net_id, d_csnat_port, d_agent_gw_port, del_fip = ( self._setup_delete_current_gw_port_deletes_dvr_internal_ports( port=port)) self.assertFalse(d_csnat_port.called) self.assertFalse(d_agent_gw_port.called) self.assertFalse(del_fip.called) self.assertIsNotNone(ext_net_id) def test_delete_current_gw_port_never_calls_delete_fipnamespace(self): rtr, plugin, ext_net_id, d_csnat_port, d_agent_gw_port, del_fip = ( self._setup_delete_current_gw_port_deletes_dvr_internal_ports( gw_port=False)) self.assertFalse(d_csnat_port.called) self.assertFalse(d_agent_gw_port.called) self.assertFalse(del_fip.called) self.assertIsNone(ext_net_id) def test_delete_current_gw_port_deletes_csnat_port(self): rtr, plugin, ext_net_id, d_csnat_port, d_agent_gw_port, del_fip = ( self._setup_delete_current_gw_port_deletes_dvr_internal_ports( new_network_id=None)) self.assertTrue(d_csnat_port.called) self.assertTrue(d_agent_gw_port.called) d_csnat_port.assert_called_once_with(mock.ANY, rtr) d_agent_gw_port.assert_called_once_with(mock.ANY, None, ext_net_id) del_fip.assert_called_once_with(mock.ANY, ext_net_id) def _floatingip_on_port_test_setup(self, hostid): router = {'id': 'foo_router_id', 'distributed': True} floatingip = { 'id': _uuid(), 'port_id': _uuid(), 'router_id': 'foo_router_id', } if hostid is not None: floatingip['host'] = hostid else: hostid = 'not_my_host_id' routers = { 'foo_router_id': router } fipagent = { 'id': _uuid() } # NOTE: mock.patch is not needed here since self.mixin is created fresh # for each test. It doesn't work with some methods since the mixin is # tested in isolation (e.g. _get_agent_by_type_and_host). self.mixin._get_dvr_service_port_hostid = mock.Mock( return_value=hostid) self.mixin._get_agent_by_type_and_host = mock.Mock( return_value=fipagent) self.mixin._get_fip_agent_gw_ports = mock.Mock( return_value='fip_interface') agent = mock.Mock() agent.id = fipagent['id'] self.mixin._process_floating_ips_dvr(self.ctx, routers, [floatingip], hostid, agent) return (router, floatingip) def test_floatingip_on_port_no_host_key(self): router, fip = self._floatingip_on_port_test_setup(None) self.assertNotIn(const.FLOATINGIP_KEY, router) def test_floatingip_on_port_with_host(self): router, fip = self._floatingip_on_port_test_setup(_uuid()) self.assertIn(const.FLOATINGIP_KEY, router) self.assertIn(fip, router[const.FLOATINGIP_KEY]) def _setup_test_create_floatingip( self, fip, floatingip_db, router_db): port = { 'id': '1234', portbindings.HOST_ID: 'myhost', 'network_id': 'external_net' } with mock.patch.object(self.mixin, 'get_router') as grtr,\ mock.patch.object(self.mixin, '_get_dvr_service_port_hostid') as vmp,\ mock.patch.object( self.mixin, '_get_dvr_migrating_service_port_hostid' ) as mvmp,\ mock.patch.object( self.mixin, 'create_fip_agent_gw_port_if_not_exists') as c_fip,\ mock.patch.object(l3_dvr_db.l3_db.L3_NAT_db_mixin, '_update_fip_assoc'): grtr.return_value = router_db vmp.return_value = 'my-host' mvmp.return_value = 'my-future-host' registry.notify(resources.FLOATING_IP, events.AFTER_UPDATE, self, context=mock.Mock(), router_id=router_db['id'], fixed_port_id=port['id'], floating_ip_id=fip['id'], floating_network_id=fip['floating_network_id'], fixed_ip_address='1.2.3.4') return c_fip def test_create_floatingip_agent_gw_port_with_dvr_router(self): floatingip = { 'id': _uuid(), 'router_id': 'foo_router_id' } router = {'id': 'foo_router_id', 'distributed': True} fip = { 'id': _uuid(), 'floating_network_id': _uuid(), 'port_id': _uuid() } create_fip = ( self._setup_test_create_floatingip( fip, floatingip, router)) self.assertTrue(create_fip.called) def test_create_fip_agent_gw_port_if_not_exists_with_l3_agent(self): fport_db = {'id': _uuid()} self.mixin._get_agent_gw_ports_exist_for_network = mock.Mock( return_value=fport_db) fipagent = agent_obj.Agent( self.ctx, id=_uuid(), binary='foo-agent', host='host', agent_type='L3 agent', topic='foo_topic', configurations={"agent_mode": "dvr_no_external"}) self.mixin._get_agent_by_type_and_host = mock.Mock( return_value=fipagent) fport = self.mixin.create_fip_agent_gw_port_if_not_exists( self.ctx, 'network_id', 'host') self.assertIsNone(fport) fipagent = agent_obj.Agent( self.ctx, id=_uuid(), binary='foo-agent', host='host', agent_type='L3 agent', topic='foo_topic', configurations={"agent_mode": "dvr"}) self.mixin._get_agent_by_type_and_host = mock.Mock( return_value=fipagent) fport = self.mixin.create_fip_agent_gw_port_if_not_exists( self.ctx, 'network_id', 'host') self.assertIsNotNone(fport) def test_create_floatingip_agent_gw_port_with_non_dvr_router(self): floatingip = { 'id': _uuid(), 'router_id': 'foo_router_id' } router = {'id': 'foo_router_id', 'distributed': False} fip = { 'id': _uuid(), 'floating_network_id': _uuid(), 'port_id': _uuid() } create_fip = ( self._setup_test_create_floatingip( fip, floatingip, router)) self.assertFalse(create_fip.called) def test_update_router_gw_info_external_network_change(self): router_dict = {'name': 'test_router', 'admin_state_up': True, 'distributed': True} router = self._create_router(router_dict) with self.network() as net_ext_1,\ self.network() as net_ext_2,\ self.subnet() as subnet: ext_net_1_id = net_ext_1['network']['id'] self.core_plugin.update_network( self.ctx, ext_net_1_id, {'network': {'router:external': True}}) self.mixin.update_router( self.ctx, router['id'], {'router': {'external_gateway_info': {'network_id': ext_net_1_id}}}) self.mixin.add_router_interface(self.ctx, router['id'], {'subnet_id': subnet['subnet']['id']}) ext_net_2_id = net_ext_2['network']['id'] self.core_plugin.update_network( self.ctx, ext_net_2_id, {'network': {'router:external': True}}) self.mixin.update_router( self.ctx, router['id'], {'router': {'external_gateway_info': {'network_id': ext_net_2_id}}}) csnat_filters = {'device_owner': [const.DEVICE_OWNER_ROUTER_SNAT]} csnat_ports = self.core_plugin.get_ports( self.ctx, filters=csnat_filters) self.assertEqual(1, len(csnat_ports)) def _test_csnat_ports_removal(self, ha=False): router_dict = {'name': 'test_router', 'admin_state_up': True, 'distributed': True} router = self._create_router(router_dict) with self.network() as net_ext,\ self.subnet() as subnet: ext_net_id = net_ext['network']['id'] self.core_plugin.update_network( self.ctx, ext_net_id, {'network': {'router:external': True}}) self.mixin.update_router( self.ctx, router['id'], {'router': {'external_gateway_info': {'network_id': ext_net_id}}}) self.mixin.add_router_interface(self.ctx, router['id'], {'subnet_id': subnet['subnet']['id']}) csnat_filters = {'device_owner': [const.DEVICE_OWNER_ROUTER_SNAT]} csnat_ports = self.core_plugin.get_ports( self.ctx, filters=csnat_filters) self.assertEqual(1, len(csnat_ports)) self.mixin.update_router( self.ctx, router['id'], {'router': {'admin_state_up': False}}) self.mixin.update_router( self.ctx, router['id'], {'router': {'distributed': False, 'ha': ha}}) csnat_ports = self.core_plugin.get_ports( self.ctx, filters=csnat_filters) self.assertEqual(0, len(csnat_ports)) def test_distributed_to_centralized_csnat_ports_removal(self): self._test_csnat_ports_removal() def test_distributed_to_ha_csnat_ports_removal(self): self._test_csnat_ports_removal(ha=True) def test_update_router_gw_info_csnat_ports_add(self): router_dict = {'name': 'test_router', 'admin_state_up': True, 'distributed': True} router = self._create_router(router_dict) with self.network() as net_ext,\ self.network() as net_int,\ self.subnet( network=net_int, cidr='2001:db8:1::/64', gateway_ip='2001:db8:1::1', ip_version=const.IP_VERSION_6) as v6_subnet1,\ self.subnet( network=net_int, cidr='2001:db8:2::/64', gateway_ip='2001:db8:2::1', ip_version=const.IP_VERSION_6) as v6_subnet2,\ self.subnet( network=net_int, cidr='10.10.10.0/24') as v4_subnet: self.core_plugin.update_network( self.ctx, net_ext['network']['id'], {'network': {'router:external': True}}) # Add router interface, then set router gateway self.mixin.add_router_interface(self.ctx, router['id'], {'subnet_id': v6_subnet1['subnet']['id']}) self.mixin.add_router_interface(self.ctx, router['id'], {'subnet_id': v6_subnet2['subnet']['id']}) self.mixin.add_router_interface(self.ctx, router['id'], {'subnet_id': v4_subnet['subnet']['id']}) dvr_filters = {'device_owner': [const.DEVICE_OWNER_DVR_INTERFACE]} dvr_ports = self.core_plugin.get_ports( self.ctx, filters=dvr_filters) # One for IPv4, one for two IPv6 subnets self.assertEqual(2, len(dvr_ports)) self.mixin.update_router( self.ctx, router['id'], {'router': {'external_gateway_info': {'network_id': net_ext['network']['id']}}}) csnat_filters = {'device_owner': [const.DEVICE_OWNER_ROUTER_SNAT]} csnat_ports = self.core_plugin.get_ports( self.ctx, filters=csnat_filters) # One for IPv4, one for two IPv6 subnets self.assertEqual(2, len(csnat_ports)) # Remove v4 subnet interface from router self.mixin.remove_router_interface( self.ctx, router['id'], {'subnet_id': v4_subnet['subnet']['id']}) dvr_ports = self.core_plugin.get_ports( self.ctx, filters=dvr_filters) self.assertEqual(1, len(dvr_ports)) csnat_ports = self.core_plugin.get_ports( self.ctx, filters=csnat_filters) self.assertEqual(1, len(csnat_ports)) self.assertEqual(2, len(csnat_ports[0]['fixed_ips'])) def test_remove_router_interface_csnat_ports_removal(self): router_dict = {'name': 'test_router', 'admin_state_up': True, 'distributed': True} router = self._create_router(router_dict) with self.network() as net_ext,\ self.subnet() as subnet1,\ self.subnet(cidr='20.0.0.0/24') as subnet2: ext_net_id = net_ext['network']['id'] self.core_plugin.update_network( self.ctx, ext_net_id, {'network': {'router:external': True}}) self.mixin.update_router( self.ctx, router['id'], {'router': {'external_gateway_info': {'network_id': ext_net_id}}}) self.mixin.add_router_interface(self.ctx, router['id'], {'subnet_id': subnet1['subnet']['id']}) self.mixin.add_router_interface(self.ctx, router['id'], {'subnet_id': subnet2['subnet']['id']}) csnat_filters = {'device_owner': [const.DEVICE_OWNER_ROUTER_SNAT]} csnat_ports = self.core_plugin.get_ports( self.ctx, filters=csnat_filters) self.assertEqual(2, len(csnat_ports)) dvr_filters = {'device_owner': [const.DEVICE_OWNER_DVR_INTERFACE]} dvr_ports = self.core_plugin.get_ports( self.ctx, filters=dvr_filters) self.assertEqual(2, len(dvr_ports)) self.mixin.remove_router_interface( self.ctx, router['id'], {'port_id': dvr_ports[0]['id']}) csnat_ports = self.core_plugin.get_ports( self.ctx, filters=csnat_filters) self.assertEqual(1, len(csnat_ports)) self.assertEqual(dvr_ports[1]['fixed_ips'][0]['subnet_id'], csnat_ports[0]['fixed_ips'][0]['subnet_id']) dvr_ports = self.core_plugin.get_ports( self.ctx, filters=dvr_filters) self.assertEqual(1, len(dvr_ports)) def _setup_router_with_v4_and_v6(self): router_dict = {'name': 'test_router', 'admin_state_up': True, 'distributed': True} router = self._create_router(router_dict) with self.network() as net_ext, self.network() as net_int: ext_net_id = net_ext['network']['id'] self.core_plugin.update_network( self.ctx, ext_net_id, {'network': {'router:external': True}}) self.mixin.update_router( self.ctx, router['id'], {'router': {'external_gateway_info': {'network_id': ext_net_id}}}) with self.subnet( network=net_int, cidr='20.0.0.0/24') as subnet_v4,\ self.subnet( network=net_int, cidr='fe80::/64', gateway_ip='fe80::1', ip_version=6) as subnet_v6: self.mixin.add_router_interface(self.ctx, router['id'], {'subnet_id': subnet_v4['subnet']['id']}) self.mixin.add_router_interface(self.ctx, router['id'], {'subnet_id': subnet_v6['subnet']['id']}) return router, subnet_v4, subnet_v6 def test_undo_router_interface_change_on_csnat_error(self): self._test_undo_router_interface_change_on_csnat_error(False) def test_undo_router_interface_change_on_csnat_error_revert_failure(self): self._test_undo_router_interface_change_on_csnat_error(True) def _test_undo_router_interface_change_on_csnat_error(self, fail_revert): router, subnet_v4, subnet_v6 = self._setup_router_with_v4_and_v6() net = {'network': {'id': subnet_v6['subnet']['network_id'], 'tenant_id': subnet_v6['subnet']['tenant_id']}} orig_update = self.mixin._core_plugin.update_port def update_port(*args, **kwargs): # 1st port update is the interface, 2nd is csnat, 3rd is revert # we want to simulate errors after the 1st update_port.calls += 1 if update_port.calls == 2: raise RuntimeError('csnat update failure') if update_port.calls == 3 and fail_revert: # this is to ensure that if the revert fails, the original # exception is raised (not this ValueError) raise ValueError('failure from revert') return orig_update(*args, **kwargs) update_port.calls = 0 self.mixin._core_plugin.update_port = update_port with self.subnet(network=net, cidr='fe81::/64', gateway_ip='fe81::1', ip_version=6) as subnet2_v6: self.mixin.add_router_interface(self.ctx, router['id'], {'subnet_id': subnet2_v6['subnet']['id']}) if fail_revert: # a revert failure will mean the interface is still added # so we can't re-add it return # starting over should work if first interface was cleaned up self.mixin.add_router_interface(self.ctx, router['id'], {'subnet_id': subnet2_v6['subnet']['id']}) def test_remove_router_interface_csnat_ports_removal_with_ipv6(self): router, subnet_v4, subnet_v6 = self._setup_router_with_v4_and_v6() csnat_filters = {'device_owner': [const.DEVICE_OWNER_ROUTER_SNAT]} csnat_ports = self.core_plugin.get_ports( self.ctx, filters=csnat_filters) self.assertEqual(2, len(csnat_ports)) dvr_filters = {'device_owner': [const.DEVICE_OWNER_DVR_INTERFACE]} dvr_ports = self.core_plugin.get_ports( self.ctx, filters=dvr_filters) self.assertEqual(2, len(dvr_ports)) self.mixin.remove_router_interface( self.ctx, router['id'], {'subnet_id': subnet_v4['subnet']['id']}) csnat_ports = self.core_plugin.get_ports( self.ctx, filters=csnat_filters) self.assertEqual(1, len(csnat_ports)) self.assertEqual( subnet_v6['subnet']['id'], csnat_ports[0]['fixed_ips'][0]['subnet_id']) dvr_ports = self.core_plugin.get_ports( self.ctx, filters=dvr_filters) self.assertEqual(1, len(dvr_ports)) def test_remove_router_interface_csnat_port_missing_ip(self): # NOTE(kevinbenton): this is a contrived scenario to reproduce # a condition observed in bug/1609540. Once we figure out why # these ports lose their IP we can remove this test. router, subnet_v4, subnet_v6 = self._setup_router_with_v4_and_v6() self.mixin.remove_router_interface( self.ctx, router['id'], {'subnet_id': subnet_v4['subnet']['id']}) csnat_filters = {'device_owner': [const.DEVICE_OWNER_ROUTER_SNAT]} csnat_ports = self.core_plugin.get_ports( self.ctx, filters=csnat_filters) self.core_plugin.update_port(self.ctx, csnat_ports[0]['id'], {'port': {'fixed_ips': []}}) self.mixin.remove_router_interface( self.ctx, router['id'], {'subnet_id': subnet_v6['subnet']['id']}) def test__validate_router_migration_notify_advanced_services(self): router = {'name': 'foo_router', 'admin_state_up': False} router_db = self._create_router(router) with mock.patch.object(l3_dvr_db.registry, 'notify') as mock_notify: self.mixin._validate_router_migration( self.ctx, router_db, {'distributed': True}) kwargs = {'context': self.ctx, 'router': router_db} mock_notify.assert_called_once_with( 'router', 'before_update', self.mixin, **kwargs) def test_validate_add_router_interface_by_subnet_notify_advanced_services( self): router = {'name': 'foo_router', 'admin_state_up': False} router_db = self._create_router(router) with self.network() as net, \ self.subnet(network={'network': net['network']}) as sub, \ mock.patch.object( self.mixin, '_notify_attaching_interface') as mock_notify: interface_info = {'subnet_id': sub['subnet']['id']} self.mixin.add_router_interface(self.ctx, router_db.id, interface_info) mock_notify.assert_called_once_with(self.ctx, router_db=router_db, port=mock.ANY, interface_info=interface_info) def test_validate_add_router_interface_by_port_notify_advanced_services( self): router = {'name': 'foo_router', 'admin_state_up': False} router_db = self._create_router(router) with self.network() as net, \ self.subnet(network={'network': net['network']}) as sub, \ self.port(subnet=sub) as port, \ mock.patch.object( self.mixin, '_notify_attaching_interface') as mock_notify: interface_info = {'port_id': port['port']['id']} self.mixin.add_router_interface(self.ctx, router_db.id, interface_info) mock_notify.assert_called_once_with(self.ctx, router_db=router_db, port=mock.ANY, interface_info=interface_info) def test__generate_arp_table_and_notify_agent(self): fixed_ip = { 'ip_address': '1.2.3.4', 'subnet_id': _uuid()} mac_address = "00:11:22:33:44:55" expected_arp_table = { 'ip_address': fixed_ip['ip_address'], 'subnet_id': fixed_ip['subnet_id'], 'mac_address': mac_address} notifier = mock.Mock() ports = [{'id': _uuid(), 'device_id': 'router_1'}, {'id': _uuid(), 'device_id': 'router_2'}] with mock.patch.object(self.core_plugin, "get_ports", return_value=ports): self.mixin._generate_arp_table_and_notify_agent( self.ctx, fixed_ip, mac_address, notifier) notifier.assert_has_calls([ mock.call(self.ctx, "router_1", expected_arp_table), mock.call(self.ctx, "router_2", expected_arp_table)]) def _test_update_arp_entry_for_dvr_service_port( self, device_owner, action): router_dict = {'name': 'test_router', 'admin_state_up': True, 'distributed': True} router = self._create_router(router_dict) plugin = mock.Mock() directory.add_plugin(plugin_constants.CORE, plugin) l3_notify = self.mixin.l3_rpc_notifier = mock.Mock() port = { 'id': 'my_port_id', 'fixed_ips': [ {'subnet_id': '51edc9e0-24f9-47f2-8e1e-2a41cb691323', 'ip_address': '10.0.0.11'}, {'subnet_id': '2b7c8a07-6f8e-4937-8701-f1d5da1a807c', 'ip_address': '10.0.0.21'}, {'subnet_id': '48534187-f077-4e81-93ff-81ec4cc0ad3b', 'ip_address': 'fd45:1515:7e0:0:f816:3eff:fe1a:1111'}], 'mac_address': 'my_mac', 'device_owner': device_owner } dvr_port = { 'id': 'dvr_port_id', 'fixed_ips': mock.ANY, 'device_owner': const.DEVICE_OWNER_DVR_INTERFACE, 'device_id': router['id'] } plugin.get_ports.return_value = [dvr_port] if action == 'add': self.mixin.update_arp_entry_for_dvr_service_port( self.ctx, port) self.assertEqual(3, l3_notify.add_arp_entry.call_count) elif action == 'del': self.mixin.delete_arp_entry_for_dvr_service_port( self.ctx, port) self.assertEqual(3, l3_notify.del_arp_entry.call_count) def test_update_arp_entry_for_dvr_service_port_added(self): action = 'add' device_owner = const.DEVICE_OWNER_LOADBALANCER self._test_update_arp_entry_for_dvr_service_port(device_owner, action) def test_update_arp_entry_for_dvr_service_port_deleted(self): action = 'del' device_owner = const.DEVICE_OWNER_LOADBALANCER self._test_update_arp_entry_for_dvr_service_port(device_owner, action) def test_add_router_interface_csnat_ports_failure(self): router_dict = {'name': 'test_router', 'admin_state_up': True, 'distributed': True} router = self._create_router(router_dict) with self.network() as net_ext,\ self.subnet() as subnet: ext_net_id = net_ext['network']['id'] self.core_plugin.update_network( self.ctx, ext_net_id, {'network': {'router:external': True}}) self.mixin.update_router( self.ctx, router['id'], {'router': {'external_gateway_info': {'network_id': ext_net_id}}}) with mock.patch.object( self.mixin, '_add_csnat_router_interface_port') as f: f.side_effect = RuntimeError() self.assertRaises( l3_exc.RouterInterfaceAttachmentConflict, self.mixin.add_router_interface, self.ctx, router['id'], {'subnet_id': subnet['subnet']['id']}) filters = { 'device_id': [router['id']], } router_ports = self.core_plugin.get_ports(self.ctx, filters) self.assertEqual(1, len(router_ports)) self.assertEqual(const.DEVICE_OWNER_ROUTER_GW, router_ports[0]['device_owner']) def test_csnat_port_not_created_on_RouterPort_update_exception(self): router_dict = {'name': 'test_router', 'admin_state_up': True, 'distributed': True} router = self._create_router(router_dict) with self.network() as net_ext,\ self.subnet() as subnet: ext_net_id = net_ext['network']['id'] self.core_plugin.update_network( self.ctx, ext_net_id, {'network': {'router:external': True}}) self.mixin.update_router( self.ctx, router['id'], {'router': {'external_gateway_info': {'network_id': ext_net_id}}}) net_id = subnet['subnet']['network_id'] with mock.patch.object( router_obj.RouterPort, 'create') as rtrport_update: rtrport_update.side_effect = Exception() self.assertRaises( l3_exc.RouterInterfaceAttachmentConflict, self.mixin.add_router_interface, self.ctx, router['id'], {'subnet_id': subnet['subnet']['id']}) filters = { 'network_id': [net_id], 'device_owner': [const.DEVICE_OWNER_ROUTER_SNAT] } router_ports = self.core_plugin.get_ports(self.ctx, filters) self.assertEqual(0, len(router_ports)) def test_add_router_interface_by_port_failure(self): router_dict = {'name': 'test_router', 'admin_state_up': True, 'distributed': True} router = self._create_router(router_dict) with self.subnet(cidr='10.10.10.0/24') as subnet: port_dict = { 'device_id': '', 'device_owner': '', 'admin_state_up': True, 'fixed_ips': [{'subnet_id': subnet['subnet']['id'], 'ip_address': '10.10.10.100'}] } net_id = subnet['subnet']['network_id'] port_res = self.create_port(net_id, port_dict) port = self.deserialize(self.fmt, port_res) self.assertIn('port', port, message='Create port failed.') orig_update_port = self.mixin._core_plugin.update_port call_info = {'count': 0} def _fake_update_port(*args, **kwargs): call_info['count'] += 1 if call_info['count'] == 2: raise RuntimeError() else: return orig_update_port(*args, **kwargs) # NOTE(trananhkma): expect that update_port() only raises an error # at the 2nd function call (Update owner after actual process # again in order). with mock.patch.object(self.mixin._core_plugin, 'update_port', side_effect=_fake_update_port): self.assertRaises( RuntimeError, self.mixin.add_router_interface, self.ctx, router['id'], {'port_id': port['port']['id']}) # expire since we are re-using the session which might have stale # ports in it self.ctx.session.expire_all() port_info = self.core_plugin.get_port(self.ctx, port['port']['id']) self.assertEqual(port_dict['device_id'], port_info['device_id']) self.assertEqual(port_dict['device_owner'], port_info['device_owner']) def test__get_sync_routers_check_gw_port_host(self): router_dict = {'name': 'test_router', 'admin_state_up': True, 'distributed': True} router = self._create_router(router_dict) with self.network() as public,\ self.subnet() as subnet: ext_net_1_id = public['network']['id'] self.core_plugin.update_network( self.ctx, ext_net_1_id, {'network': {'router:external': True}}) self.mixin.update_router( self.ctx, router['id'], {'router': {'external_gateway_info': {'network_id': ext_net_1_id}}}) self.mixin.add_router_interface(self.ctx, router['id'], {'subnet_id': subnet['subnet']['id']}) routers = self.mixin._get_sync_routers(self.ctx, router_ids=[router['id']]) self.assertIsNone(routers[0]['gw_port_host']) agent = mock.Mock() agent.host = "fake-host" bind = mock.Mock() bind.l3_agent_id = "fake-id" with mock.patch.object( rb_obj.RouterL3AgentBinding, 'get_objects', return_value=[bind]), mock.patch.object( agent_obj.Agent, 'get_object', return_value=agent): routers = self.mixin._get_sync_routers( self.ctx, router_ids=[router['id']]) self.assertEqual("fake-host", routers[0]['gw_port_host']) def test_is_router_distributed(self): router_id = 'router_id' with mock.patch.object(self.mixin, 'get_router') as \ mock_get_router: mock_get_router.return_value = {'distributed': True} self.assertTrue( self.mixin.is_router_distributed(self.ctx, router_id)) @mock.patch.object(l3_db, 'can_port_be_bound_to_virtual_bridge', return_value=True) def test__get_assoc_data_valid_vnic_type(self, *args): with mock.patch.object(self.mixin, '_internal_fip_assoc_data') as \ mock_fip_assoc_data, \ mock.patch.object(self.mixin, '_get_router_for_floatingip') \ as mock_router_fip, \ mock.patch.object(self.mixin, 'is_router_distributed', return_value=True): port = {portbindings.VNIC_TYPE: portbindings.VNIC_NORMAL} mock_fip_assoc_data.return_value = (port, 'subnet_id', 'ip_addr') mock_router_fip.return_value = 'router_id' fip = {'port_id': 'port_id'} self.assertEqual( ('port_id', 'ip_addr', 'router_id'), self.mixin._get_assoc_data(self.ctx, fip, mock.Mock())) @mock.patch.object(l3_db, 'can_port_be_bound_to_virtual_bridge', return_value=False) def test__get_assoc_data_invalid_vnic_type(self, *args): with mock.patch.object(self.mixin, '_internal_fip_assoc_data') as \ mock_fip_assoc_data, \ mock.patch.object(self.mixin, '_get_router_for_floatingip') \ as mock_router_fip, \ mock.patch.object(self.mixin, 'is_router_distributed', return_value=True): port = {portbindings.VNIC_TYPE: portbindings.VNIC_NORMAL} mock_fip_assoc_data.return_value = (port, 'subnet_id', 'ip_addr') mock_router_fip.return_value = 'router_id' self.assertRaises( exceptions.BadRequest, self.mixin._get_assoc_data, self.ctx, mock.ANY, mock.Mock()) neutron-12.1.1/neutron/tests/unit/common/0000775000175000017500000000000013553660157020426 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/common/test_cache_utils.py0000664000175000017500000001076413553660046024327 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg from oslo_config import fixture as config_fixture from neutron.common import cache_utils as cache from neutron.tests import base class CacheConfFixture(config_fixture.Config): def setUp(self): super(CacheConfFixture, self).setUp() cache.register_oslo_configs(self.conf) self.config(enabled=True, group='cache') class TestOsloCache(base.BaseTestCase): def setUp(self): super(TestOsloCache, self).setUp() self.memory_conf = cfg.ConfigOpts() memory_conf_fixture = CacheConfFixture(self.memory_conf) self.useFixture(memory_conf_fixture) self.dict_conf = cfg.ConfigOpts() dict_conf_fixture = CacheConfFixture(self.dict_conf) self.useFixture(dict_conf_fixture) dict_conf_fixture.config(expiration_time=60, backend='oslo_cache.dict', group='cache') self.null_cache_conf = cfg.ConfigOpts() null_conf_fixture = CacheConfFixture(self.null_cache_conf) self.useFixture(null_conf_fixture) null_conf_fixture.config(expiration_time=600, backend='dogpile.cache.null', group='cache') def _test_get_cache_region_helper(self, conf): region = cache._get_cache_region(conf) self.assertIsNotNone(region) def test_get_cache_region(self): self._test_get_cache_region_helper(self.dict_conf) self._test_get_cache_region_helper(self.null_cache_conf) @mock.patch('neutron.common.cache_utils._get_cache_region') def test_get_cache(self, mock_get_cache_region): self.assertIsNotNone(cache.get_cache(self.memory_conf)) self.assertIsNotNone(cache.get_cache(self.dict_conf)) self.assertIsNotNone(cache.get_cache(self.null_cache_conf)) mock_get_cache_region.assert_has_calls( [mock.call(self.dict_conf), mock.call(self.null_cache_conf)] ) class _CachingDecorator(object): def __init__(self): self.func_retval = 'bar' self._cache = mock.Mock() @cache.cache_method_results def func(self, *args, **kwargs): return self.func_retval class TestCachingDecorator(base.BaseTestCase): def setUp(self): super(TestCachingDecorator, self).setUp() self.decor = _CachingDecorator() self.func_name = '%(module)s._CachingDecorator.func' % { 'module': self.__module__ } self.not_cached = self.decor.func.func.__self__._not_cached def test_cache_miss(self): expected_key = (self.func_name, 1, 2, ('foo', 'bar')) args = (1, 2) kwargs = {'foo': 'bar'} self.decor._cache.get.return_value = self.not_cached retval = self.decor.func(*args, **kwargs) self.decor._cache.set.assert_called_once_with( str(expected_key), self.decor.func_retval) self.assertEqual(self.decor.func_retval, retval) def test_cache_hit(self): expected_key = (self.func_name, 1, 2, ('foo', 'bar')) args = (1, 2) kwargs = {'foo': 'bar'} retval = self.decor.func(*args, **kwargs) self.assertFalse(self.decor._cache.set.called) self.assertEqual(self.decor._cache.get.return_value, retval) self.decor._cache.get.assert_called_once_with(str(expected_key)) def test_get_unhashable(self): expected_key = (self.func_name, [1], 2) self.decor._cache.get.side_effect = TypeError retval = self.decor.func([1], 2) self.assertFalse(self.decor._cache.set.called) self.assertEqual(self.decor.func_retval, retval) self.decor._cache.get.assert_called_once_with(str(expected_key)) def test_missing_cache(self): delattr(self.decor, '_cache') self.assertRaises(NotImplementedError, self.decor.func, (1, 2)) def test_no_cache(self): self.decor._cache = False retval = self.decor.func((1, 2)) self.assertEqual(self.decor.func_retval, retval) neutron-12.1.1/neutron/tests/unit/common/test_rpc.py0000664000175000017500000004762613553660047022640 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import fixtures import mock from oslo_config import cfg import oslo_messaging as messaging from oslo_messaging import conffixture as messaging_conffixture from oslo_messaging.rpc import dispatcher import testtools from neutron.common import rpc from neutron.tests import base CONF = cfg.CONF CONF.import_opt('state_path', 'neutron.conf.common') class RPCFixture(fixtures.Fixture): def _setUp(self): self.trans = copy.copy(rpc.TRANSPORT) self.noti_trans = copy.copy(rpc.NOTIFICATION_TRANSPORT) self.noti = copy.copy(rpc.NOTIFIER) self.all_mods = copy.copy(rpc.ALLOWED_EXMODS) self.ext_mods = copy.copy(rpc.EXTRA_EXMODS) self.addCleanup(self._reset_everything) def _reset_everything(self): rpc.TRANSPORT = self.trans rpc.NOTIFICATION_TRANSPORT = self.noti_trans rpc.NOTIFIER = self.noti rpc.ALLOWED_EXMODS = self.all_mods rpc.EXTRA_EXMODS = self.ext_mods class TestRPC(base.DietTestCase): def setUp(self): super(TestRPC, self).setUp() self.useFixture(RPCFixture()) @mock.patch.object(rpc, 'get_allowed_exmods') @mock.patch.object(rpc, 'RequestContextSerializer') @mock.patch.object(messaging, 'get_rpc_transport') @mock.patch.object(messaging, 'get_notification_transport') @mock.patch.object(messaging, 'Notifier') def test_init(self, mock_not, mock_noti_trans, mock_trans, mock_ser, mock_exmods): notifier = mock.Mock() transport = mock.Mock() noti_transport = mock.Mock() serializer = mock.Mock() conf = mock.Mock() mock_exmods.return_value = ['foo'] mock_trans.return_value = transport mock_noti_trans.return_value = noti_transport mock_ser.return_value = serializer mock_not.return_value = notifier rpc.init(conf) mock_exmods.assert_called_once_with() mock_trans.assert_called_once_with(conf, allowed_remote_exmods=['foo']) mock_noti_trans.assert_called_once_with(conf, allowed_remote_exmods=['foo']) mock_not.assert_called_once_with(noti_transport, serializer=serializer) self.assertIsNotNone(rpc.TRANSPORT) self.assertIsNotNone(rpc.NOTIFICATION_TRANSPORT) self.assertIsNotNone(rpc.NOTIFIER) def test_cleanup_transport_null(self): rpc.NOTIFIER = mock.Mock() rpc.NOTIFICATION_TRANSPORT = mock.Mock() self.assertRaises(AssertionError, rpc.cleanup) def test_cleanup_notification_transport_null(self): rpc.TRANSPORT = mock.Mock() rpc.NOTIFIER = mock.Mock() self.assertRaises(AssertionError, rpc.cleanup) def test_cleanup_notifier_null(self): rpc.TRANSPORT = mock.Mock() rpc.NOTIFICATION_TRANSPORT = mock.Mock() self.assertRaises(AssertionError, rpc.cleanup) def test_cleanup(self): rpc.NOTIFIER = mock.Mock() rpc.NOTIFICATION_TRANSPORT = mock.Mock() rpc.TRANSPORT = mock.Mock() trans_cleanup = mock.Mock() not_trans_cleanup = mock.Mock() rpc.TRANSPORT.cleanup = trans_cleanup rpc.NOTIFICATION_TRANSPORT.cleanup = not_trans_cleanup rpc.cleanup() trans_cleanup.assert_called_once_with() not_trans_cleanup.assert_called_once_with() self.assertIsNone(rpc.TRANSPORT) self.assertIsNone(rpc.NOTIFICATION_TRANSPORT) self.assertIsNone(rpc.NOTIFIER) def test_add_extra_exmods(self): rpc.EXTRA_EXMODS = [] rpc.add_extra_exmods('foo', 'bar') self.assertEqual(['foo', 'bar'], rpc.EXTRA_EXMODS) def test_clear_extra_exmods(self): rpc.EXTRA_EXMODS = ['foo', 'bar'] rpc.clear_extra_exmods() self.assertEqual(0, len(rpc.EXTRA_EXMODS)) def test_get_allowed_exmods(self): rpc.ALLOWED_EXMODS = ['foo'] rpc.EXTRA_EXMODS = ['bar'] exmods = rpc.get_allowed_exmods() self.assertEqual(['foo', 'bar'], exmods) @mock.patch.object(rpc, 'RequestContextSerializer') @mock.patch.object(rpc, 'BackingOffClient') def test_get_client(self, mock_client, mock_ser): rpc.TRANSPORT = mock.Mock() tgt = mock.Mock() ser = mock.Mock() mock_client.return_value = 'client' mock_ser.return_value = ser client = rpc.get_client(tgt, version_cap='1.0', serializer='foo') mock_ser.assert_called_once_with('foo') mock_client.assert_called_once_with(rpc.TRANSPORT, tgt, version_cap='1.0', serializer=ser) self.assertEqual('client', client) @mock.patch.object(rpc, 'RequestContextSerializer') @mock.patch.object(messaging, 'get_rpc_server') def test_get_server(self, mock_get, mock_ser): rpc.TRANSPORT = mock.Mock() ser = mock.Mock() tgt = mock.Mock() ends = mock.Mock() mock_ser.return_value = ser mock_get.return_value = 'server' server = rpc.get_server(tgt, ends, serializer='foo') mock_ser.assert_called_once_with('foo') access_policy = dispatcher.DefaultRPCAccessPolicy mock_get.assert_called_once_with(rpc.TRANSPORT, tgt, ends, 'eventlet', ser, access_policy=access_policy) self.assertEqual('server', server) def test_get_notifier(self): rpc.NOTIFIER = mock.Mock() mock_prep = mock.Mock() mock_prep.return_value = 'notifier' rpc.NOTIFIER.prepare = mock_prep notifier = rpc.get_notifier('service', publisher_id='foo') mock_prep.assert_called_once_with(publisher_id='foo') self.assertEqual('notifier', notifier) def test_get_notifier_null_publisher(self): rpc.NOTIFIER = mock.Mock() mock_prep = mock.Mock() mock_prep.return_value = 'notifier' rpc.NOTIFIER.prepare = mock_prep notifier = rpc.get_notifier('service', host='bar') mock_prep.assert_called_once_with(publisher_id='service.bar') self.assertEqual('notifier', notifier) class TestRequestContextSerializer(base.DietTestCase): def setUp(self): super(TestRequestContextSerializer, self).setUp() self.mock_base = mock.Mock() self.ser = rpc.RequestContextSerializer(self.mock_base) self.ser_null = rpc.RequestContextSerializer(None) def test_serialize_entity(self): self.mock_base.serialize_entity.return_value = 'foo' ser_ent = self.ser.serialize_entity('context', 'entity') self.mock_base.serialize_entity.assert_called_once_with('context', 'entity') self.assertEqual('foo', ser_ent) def test_deserialize_entity(self): self.mock_base.deserialize_entity.return_value = 'foo' deser_ent = self.ser.deserialize_entity('context', 'entity') self.mock_base.deserialize_entity.assert_called_once_with('context', 'entity') self.assertEqual('foo', deser_ent) def test_deserialize_entity_null_base(self): deser_ent = self.ser_null.deserialize_entity('context', 'entity') self.assertEqual('entity', deser_ent) def test_serialize_context(self): context = mock.Mock() self.ser.serialize_context(context) context.to_dict.assert_called_once_with() def test_deserialize_context(self): context_dict = {'foo': 'bar', 'user_id': 1, 'tenant_id': 1, 'is_admin': True} c = self.ser.deserialize_context(context_dict) self.assertEqual(1, c.user_id) self.assertEqual(1, c.project_id) def test_deserialize_context_no_user_id(self): context_dict = {'foo': 'bar', 'user': 1, 'tenant_id': 1, 'is_admin': True} c = self.ser.deserialize_context(context_dict) self.assertEqual(1, c.user_id) self.assertEqual(1, c.project_id) def test_deserialize_context_no_tenant_id(self): context_dict = {'foo': 'bar', 'user_id': 1, 'project_id': 1, 'is_admin': True} c = self.ser.deserialize_context(context_dict) self.assertEqual(1, c.user_id) self.assertEqual(1, c.project_id) def test_deserialize_context_no_ids(self): context_dict = {'foo': 'bar', 'is_admin': True} c = self.ser.deserialize_context(context_dict) self.assertIsNone(c.user_id) self.assertIsNone(c.project_id) class ServiceTestCase(base.DietTestCase): # the class cannot be based on BaseTestCase since it mocks rpc.Connection def setUp(self): super(ServiceTestCase, self).setUp() self.host = 'foo' self.topic = 'neutron-agent' self.target_mock = mock.patch('oslo_messaging.Target') self.target_mock.start() self.messaging_conf = messaging_conffixture.ConfFixture(CONF) self.messaging_conf.transport_driver = 'fake' self.messaging_conf.response_timeout = 0 self.useFixture(self.messaging_conf) self.addCleanup(rpc.cleanup) rpc.init(CONF) def test_operations(self): with mock.patch('oslo_messaging.get_rpc_server') as get_rpc_server: rpc_server = get_rpc_server.return_value service = rpc.Service(self.host, self.topic) service.start() rpc_server.start.assert_called_once_with() service.stop() rpc_server.stop.assert_called_once_with() rpc_server.wait.assert_called_once_with() class TimeoutTestCase(base.DietTestCase): def setUp(self): super(TimeoutTestCase, self).setUp() self.messaging_conf = messaging_conffixture.ConfFixture(CONF) self.messaging_conf.transport_driver = 'fake' self.messaging_conf.response_timeout = 0 self.useFixture(self.messaging_conf) self.addCleanup(rpc.cleanup) rpc.init(CONF) rpc.TRANSPORT = mock.MagicMock() rpc.TRANSPORT._send.side_effect = messaging.MessagingTimeout target = messaging.Target(version='1.0', topic='testing') self.client = rpc.get_client(target) self.call_context = mock.Mock() self.sleep = mock.patch('time.sleep').start() rpc.TRANSPORT.conf.rpc_response_timeout = 10 def test_timeout_unaffected_when_explicitly_set(self): rpc.TRANSPORT.conf.rpc_response_timeout = 5 ctx = self.client.prepare(topic='sandwiches', timeout=77) with testtools.ExpectedException(messaging.MessagingTimeout): ctx.call(self.call_context, 'create_pb_and_j') # ensure that the timeout was not increased and the back-off sleep # wasn't called self.assertEqual( 5, rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['create_pb_and_j']) self.assertFalse(self.sleep.called) def test_timeout_store_defaults(self): # any method should default to the configured timeout self.assertEqual( rpc.TRANSPORT.conf.rpc_response_timeout, rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_1']) self.assertEqual( rpc.TRANSPORT.conf.rpc_response_timeout, rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_2']) # a change to an existing should not affect new or existing ones rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_2'] = 7000 self.assertEqual( rpc.TRANSPORT.conf.rpc_response_timeout, rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_1']) self.assertEqual( rpc.TRANSPORT.conf.rpc_response_timeout, rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_3']) def test_method_timeout_sleep(self): rpc.TRANSPORT.conf.rpc_response_timeout = 2 for i in range(100): with testtools.ExpectedException(messaging.MessagingTimeout): self.client.call(self.call_context, 'method_1') # sleep value should always be between 0 and configured timeout self.assertGreaterEqual(self.sleep.call_args_list[0][0][0], 0) self.assertLessEqual(self.sleep.call_args_list[0][0][0], 2) self.sleep.reset_mock() def test_method_timeout_increases_on_timeout_exception(self): rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_1'] = 1 for i in range(5): with testtools.ExpectedException(messaging.MessagingTimeout): self.client.call(self.call_context, 'method_1') # we only care to check the timeouts sent to the transport timeouts = [call[1]['timeout'] for call in rpc.TRANSPORT._send.call_args_list] self.assertEqual([1, 2, 4, 8, 16], timeouts) def test_method_timeout_10x_config_ceiling(self): rpc.TRANSPORT.conf.rpc_response_timeout = 10 # 5 doublings should max out at the 10xdefault ceiling for i in range(5): with testtools.ExpectedException(messaging.MessagingTimeout): self.client.call(self.call_context, 'method_1') self.assertEqual( 10 * rpc.TRANSPORT.conf.rpc_response_timeout, rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_1']) with testtools.ExpectedException(messaging.MessagingTimeout): self.client.call(self.call_context, 'method_1') self.assertEqual( 10 * rpc.TRANSPORT.conf.rpc_response_timeout, rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_1']) def test_timeout_unchanged_on_other_exception(self): rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_1'] = 1 rpc.TRANSPORT._send.side_effect = ValueError with testtools.ExpectedException(ValueError): self.client.call(self.call_context, 'method_1') rpc.TRANSPORT._send.side_effect = messaging.MessagingTimeout with testtools.ExpectedException(messaging.MessagingTimeout): self.client.call(self.call_context, 'method_1') timeouts = [call[1]['timeout'] for call in rpc.TRANSPORT._send.call_args_list] self.assertEqual([1, 1], timeouts) def test_timeouts_for_methods_tracked_independently(self): rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_1'] = 1 rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_2'] = 1 for method in ('method_1', 'method_1', 'method_2', 'method_1', 'method_2'): with testtools.ExpectedException(messaging.MessagingTimeout): self.client.call(self.call_context, method) timeouts = [call[1]['timeout'] for call in rpc.TRANSPORT._send.call_args_list] self.assertEqual([1, 2, 1, 4, 2], timeouts) def test_timeouts_for_namespaces_tracked_independently(self): rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['ns1.method'] = 1 rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['ns2.method'] = 1 for ns in ('ns1', 'ns2'): self.client.target.namespace = ns for i in range(4): with testtools.ExpectedException(messaging.MessagingTimeout): self.client.call(self.call_context, 'method') timeouts = [call[1]['timeout'] for call in rpc.TRANSPORT._send.call_args_list] self.assertEqual([1, 2, 4, 8, 1, 2, 4, 8], timeouts) def test_method_timeout_increases_with_prepare(self): rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_1'] = 1 ctx = self.client.prepare(version='1.4') with testtools.ExpectedException(messaging.MessagingTimeout): ctx.call(self.call_context, 'method_1') with testtools.ExpectedException(messaging.MessagingTimeout): ctx.call(self.call_context, 'method_1') # we only care to check the timeouts sent to the transport timeouts = [call[1]['timeout'] for call in rpc.TRANSPORT._send.call_args_list] self.assertEqual([1, 2], timeouts) def test_set_max_timeout_caps_all_methods(self): rpc.TRANSPORT.conf.rpc_response_timeout = 300 rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_1'] = 100 rpc.BackingOffClient.set_max_timeout(50) # both explicitly tracked self.assertEqual( 50, rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_1']) # as well as new methods self.assertEqual( 50, rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_2']) def test_set_max_timeout_retains_lower_timeouts(self): rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_1'] = 10 rpc.BackingOffClient.set_max_timeout(50) self.assertEqual( 10, rpc._BackingOffContextWrapper._METHOD_TIMEOUTS['method_1']) def test_set_max_timeout_overrides_default_timeout(self): rpc.TRANSPORT.conf.rpc_response_timeout = 10 self.assertEqual( 10 * 10, rpc._BackingOffContextWrapper.get_max_timeout()) rpc._BackingOffContextWrapper.set_max_timeout(10) self.assertEqual(10, rpc._BackingOffContextWrapper.get_max_timeout()) class CastExceptionTestCase(base.DietTestCase): def setUp(self): super(CastExceptionTestCase, self).setUp() self.messaging_conf = messaging_conffixture.ConfFixture(CONF) self.messaging_conf.transport_driver = 'fake' self.messaging_conf.response_timeout = 0 self.useFixture(self.messaging_conf) self.addCleanup(rpc.cleanup) rpc.init(CONF) rpc.TRANSPORT = mock.MagicMock() rpc.TRANSPORT._send.side_effect = Exception target = messaging.Target(version='1.0', topic='testing') self.client = rpc.get_client(target) self.cast_context = mock.Mock() def test_cast_catches_exception(self): self.client.cast(self.cast_context, 'method_1') class TestConnection(base.DietTestCase): def setUp(self): super(TestConnection, self).setUp() self.conn = rpc.Connection() @mock.patch.object(messaging, 'Target') @mock.patch.object(cfg, 'CONF') @mock.patch.object(rpc, 'get_server') def test_create_consumer(self, mock_get, mock_cfg, mock_tgt): mock_cfg.host = 'foo' server = mock.Mock() target = mock.Mock() mock_get.return_value = server mock_tgt.return_value = target self.conn.create_consumer('topic', 'endpoints', fanout=True) mock_tgt.assert_called_once_with(topic='topic', server='foo', fanout=True) mock_get.assert_called_once_with(target, 'endpoints') self.assertEqual([server], self.conn.servers) def test_consume_in_threads(self): self.conn.servers = [mock.Mock(), mock.Mock()] servs = self.conn.consume_in_threads() for serv in self.conn.servers: serv.start.assert_called_once_with() self.assertEqual(servs, self.conn.servers) def test_close(self): self.conn.servers = [mock.Mock(), mock.Mock()] self.conn.close() for serv in self.conn.servers: serv.stop.assert_called_once_with() serv.wait.assert_called_once_with() neutron-12.1.1/neutron/tests/unit/common/test__deprecate.py0000664000175000017500000000617113553660046024134 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from oslo_utils import importutils from neutron.tests import base from neutron.tests.unit.common import moved_globals_target as new_mod def module_path(code): return 'neutron.tests.unit.common.moved_globals_' + code def import_code(code): return importutils.import_module(module_path(code)) def expect_moved(code, name, new_name=None): old_path = '.'.join([module_path(code), name]) new_path = '.'.join([new_mod.__name__, new_name or name]) message = 'moved to ' + new_path return old_path, message def expect_renamed(code, old_name, new_name): old_path = '.'.join([module_path(code), old_name]) new_path = '.'.join([module_path(code), new_name]) message = 'renamed to ' + new_path return old_path, message class TestMovedGlobals(base.BaseTestCase): def test_moved_global(self): code = 'code1' old_mod = import_code(code) with mock.patch('debtcollector.deprecate') as dc: self.assertEqual(new_mod.a, old_mod.a) old_path, msg = expect_moved(code, 'a') dc.assert_called_once_with(old_path, message=msg, stacklevel=4) def test_moved_global_no_attr(self): mod = import_code('code1') self.assertRaises(AttributeError, lambda: mod.NO_SUCH_ATTRIBUTE) def test_renamed_global(self): code = 'code1' mod = import_code(code) with mock.patch('debtcollector.deprecate') as dc: self.assertEqual(mod.d, mod.c) old_path, msg = expect_renamed(code, 'c', 'd') dc.assert_called_once_with(old_path, message=msg, stacklevel=4) def test_moved_global_renamed(self): code = 'code1' old_mod = import_code(code) with mock.patch('debtcollector.deprecate') as dc: self.assertEqual(new_mod.f, old_mod.e) old_path, msg = expect_moved(code, 'e', new_name='f') dc.assert_called_once_with(old_path, message=msg, stacklevel=4) def test_set_unmoved_global(self): mod = import_code('code1') mod.d = 'dibatag' self.assertEqual('dibatag', mod.d) def test_set_new_global(self): mod = import_code('code1') mod.n = 'nyala' self.assertEqual('nyala', mod.n) def test_delete_unmoved_global(self): mod = import_code('code1') self.assertEqual('gelada', mod.g) def delete_g(): del mod.g delete_g() self.assertRaises(AttributeError, lambda: mod.g) self.failUnlessRaises(AttributeError, delete_g) def test_not_last_line(self): self.assertRaises(SystemExit, import_code, 'code2') neutron-12.1.1/neutron/tests/unit/common/test_utils.py0000664000175000017500000007256313553660047023212 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os.path import random import re import sys import ddt import eventlet import mock import netaddr from neutron_lib import constants from neutron_lib import exceptions as exc from oslo_log import log as logging import six import testscenarios import testtools from neutron.common import constants as common_constants from neutron.common import exceptions as n_exc from neutron.common import utils from neutron.plugins.common import utils as plugin_utils from neutron.tests import base from neutron.tests.unit import tests load_tests = testscenarios.load_tests_apply_scenarios class _PortRange(object): """A linked list of port ranges.""" def __init__(self, base, prev_ref=None): self.base = base self.mask = 0xffff self.prev_ref = prev_ref @property def possible_mask_base(self): return self.base & (self.mask << 1) @property def can_merge(self): return (self.prev_ref and self.possible_mask_base == self.prev_ref.possible_mask_base and self.mask == self.prev_ref.mask) def shake(self): """Try to merge ranges created earlier. If previous number in a list can be merged with current item under common mask, it's merged. Then it continues to do the same with the rest of the list. """ while self.can_merge: self.mask <<= 1 self.base = self.prev_ref.base if self.prev_ref: self.prev_ref = self.prev_ref.prev_ref def __str__(self): return _hex_format(self.base, self.mask) def get_list(self): if self.prev_ref: return self.prev_ref.get_list() + [str(self)] return [str(self)] _hex_str = lambda num: format(num, '#06x') def _hex_format(port, mask): if mask != 0xffff: return "%s/%s" % (_hex_str(port), _hex_str(0xffff & mask)) return _hex_str(port) def _port_rule_masking(port_min, port_max): current = None for num in range(port_min, port_max + 1): port_range = _PortRange(num, prev_ref=current) port_range.shake() current = port_range return current.get_list() class TestParseTunnelRangesMixin(object): TUN_MIN = None TUN_MAX = None TYPE = None _err_prefix = "Invalid network tunnel range: '%d:%d' - " _err_suffix = "%s is not a valid %s identifier." _err_range = "End of tunnel range is less than start of tunnel range." def _build_invalid_tunnel_range_msg(self, t_range_tuple, n): bad_id = t_range_tuple[n - 1] return (self._err_prefix % t_range_tuple) + (self._err_suffix % (bad_id, self.TYPE)) def _build_range_reversed_msg(self, t_range_tuple): return (self._err_prefix % t_range_tuple) + self._err_range def _verify_range(self, tunnel_range): return plugin_utils.verify_tunnel_range(tunnel_range, self.TYPE) def _check_range_valid_ranges(self, tunnel_range): self.assertIsNone(self._verify_range(tunnel_range)) def _check_range_invalid_ranges(self, bad_range, which): expected_msg = self._build_invalid_tunnel_range_msg(bad_range, which) err = self.assertRaises(exc.NetworkTunnelRangeError, self._verify_range, bad_range) self.assertEqual(expected_msg, str(err)) def _check_range_reversed(self, bad_range): err = self.assertRaises(exc.NetworkTunnelRangeError, self._verify_range, bad_range) expected_msg = self._build_range_reversed_msg(bad_range) self.assertEqual(expected_msg, str(err)) def test_range_tunnel_id_valid(self): self._check_range_valid_ranges((self.TUN_MIN, self.TUN_MAX)) def test_range_tunnel_id_invalid(self): self._check_range_invalid_ranges((-1, self.TUN_MAX), 1) self._check_range_invalid_ranges((self.TUN_MIN, self.TUN_MAX + 1), 2) self._check_range_invalid_ranges((self.TUN_MIN - 1, self.TUN_MAX + 1), 1) def test_range_tunnel_id_reversed(self): self._check_range_reversed((self.TUN_MAX, self.TUN_MIN)) class TestGreTunnelRangeVerifyValid(TestParseTunnelRangesMixin, base.BaseTestCase): TUN_MIN = constants.MIN_GRE_ID TUN_MAX = constants.MAX_GRE_ID TYPE = constants.TYPE_GRE class TestVxlanTunnelRangeVerifyValid(TestParseTunnelRangesMixin, base.BaseTestCase): TUN_MIN = constants.MIN_VXLAN_VNI TUN_MAX = constants.MAX_VXLAN_VNI TYPE = constants.TYPE_VXLAN class UtilTestParseVlanRanges(base.BaseTestCase): _err_prefix = "Invalid network VLAN range: '" _err_bad_count = "' - 'Need exactly two values for VLAN range'." _err_bad_vlan = "' - '%s is not a valid VLAN tag'." _err_range = "' - 'End of VLAN range is less than start of VLAN range'." def _range_err_bad_count(self, nv_range): return self._err_prefix + nv_range + self._err_bad_count def _range_invalid_vlan(self, nv_range, n): vlan = nv_range.split(':')[n] return self._err_prefix + nv_range + (self._err_bad_vlan % vlan) def _nrange_invalid_vlan(self, nv_range, n): vlan = nv_range.split(':')[n] v_range = ':'.join(nv_range.split(':')[1:]) return self._err_prefix + v_range + (self._err_bad_vlan % vlan) def _vrange_invalid_vlan(self, v_range_tuple, n): vlan = v_range_tuple[n - 1] v_range_str = '%d:%d' % v_range_tuple return self._err_prefix + v_range_str + (self._err_bad_vlan % vlan) def _vrange_invalid(self, v_range_tuple): v_range_str = '%d:%d' % v_range_tuple return self._err_prefix + v_range_str + self._err_range class TestVlanNetworkNameValid(base.BaseTestCase): def parse_vlan_ranges(self, vlan_range): return plugin_utils.parse_network_vlan_ranges(vlan_range) def test_validate_provider_phynet_name_mixed(self): self.assertRaises(n_exc.PhysicalNetworkNameError, self.parse_vlan_ranges, ['', ':23:30', 'physnet1', 'tenant_net:100:200']) def test_validate_provider_phynet_name_bad(self): self.assertRaises(n_exc.PhysicalNetworkNameError, self.parse_vlan_ranges, [':1:34']) class TestVlanRangeVerifyValid(UtilTestParseVlanRanges): def verify_range(self, vlan_range): return plugin_utils.verify_vlan_range(vlan_range) def test_range_valid_ranges(self): self.assertIsNone(self.verify_range((1, 2))) self.assertIsNone(self.verify_range((1, 1999))) self.assertIsNone(self.verify_range((100, 100))) self.assertIsNone(self.verify_range((100, 200))) self.assertIsNone(self.verify_range((4001, 4094))) self.assertIsNone(self.verify_range((1, 4094))) def check_one_vlan_invalid(self, bad_range, which): expected_msg = self._vrange_invalid_vlan(bad_range, which) err = self.assertRaises(n_exc.NetworkVlanRangeError, self.verify_range, bad_range) self.assertEqual(str(err), expected_msg) def test_range_first_vlan_invalid_negative(self): self.check_one_vlan_invalid((-1, 199), 1) def test_range_first_vlan_invalid_zero(self): self.check_one_vlan_invalid((0, 199), 1) def test_range_first_vlan_invalid_limit_plus_one(self): self.check_one_vlan_invalid((4095, 199), 1) def test_range_first_vlan_invalid_too_big(self): self.check_one_vlan_invalid((9999, 199), 1) def test_range_second_vlan_invalid_negative(self): self.check_one_vlan_invalid((299, -1), 2) def test_range_second_vlan_invalid_zero(self): self.check_one_vlan_invalid((299, 0), 2) def test_range_second_vlan_invalid_limit_plus_one(self): self.check_one_vlan_invalid((299, 4095), 2) def test_range_second_vlan_invalid_too_big(self): self.check_one_vlan_invalid((299, 9999), 2) def test_range_both_vlans_invalid_01(self): self.check_one_vlan_invalid((-1, 0), 1) def test_range_both_vlans_invalid_02(self): self.check_one_vlan_invalid((0, 4095), 1) def test_range_both_vlans_invalid_03(self): self.check_one_vlan_invalid((4095, 9999), 1) def test_range_both_vlans_invalid_04(self): self.check_one_vlan_invalid((9999, -1), 1) def test_range_reversed(self): bad_range = (95, 10) expected_msg = self._vrange_invalid(bad_range) err = self.assertRaises(n_exc.NetworkVlanRangeError, self.verify_range, bad_range) self.assertEqual(str(err), expected_msg) class TestParseOneVlanRange(UtilTestParseVlanRanges): def parse_one(self, cfg_entry): return plugin_utils.parse_network_vlan_range(cfg_entry) def test_parse_one_net_no_vlan_range(self): config_str = "net1" expected_networks = ("net1", None) self.assertEqual(expected_networks, self.parse_one(config_str)) def test_parse_one_net_and_vlan_range(self): config_str = "net1:100:199" expected_networks = ("net1", (100, 199)) self.assertEqual(expected_networks, self.parse_one(config_str)) def test_parse_one_net_incomplete_range(self): config_str = "net1:100" expected_msg = self._range_err_bad_count(config_str) err = self.assertRaises(n_exc.NetworkVlanRangeError, self.parse_one, config_str) self.assertEqual(expected_msg, str(err)) def test_parse_one_net_range_too_many(self): config_str = "net1:100:150:200" expected_msg = self._range_err_bad_count(config_str) err = self.assertRaises(n_exc.NetworkVlanRangeError, self.parse_one, config_str) self.assertEqual(expected_msg, str(err)) def test_parse_one_net_vlan1_not_int(self): config_str = "net1:foo:199" expected_msg = self._range_invalid_vlan(config_str, 1) err = self.assertRaises(n_exc.NetworkVlanRangeError, self.parse_one, config_str) self.assertEqual(expected_msg, str(err)) def test_parse_one_net_vlan2_not_int(self): config_str = "net1:100:bar" expected_msg = self._range_invalid_vlan(config_str, 2) err = self.assertRaises(n_exc.NetworkVlanRangeError, self.parse_one, config_str) self.assertEqual(expected_msg, str(err)) def test_parse_one_net_and_max_range(self): config_str = "net1:1:4094" expected_networks = ("net1", (1, 4094)) self.assertEqual(expected_networks, self.parse_one(config_str)) def test_parse_one_net_range_bad_vlan1(self): config_str = "net1:9000:150" expected_msg = self._nrange_invalid_vlan(config_str, 1) err = self.assertRaises(n_exc.NetworkVlanRangeError, self.parse_one, config_str) self.assertEqual(expected_msg, str(err)) def test_parse_one_net_range_bad_vlan2(self): config_str = "net1:4000:4999" expected_msg = self._nrange_invalid_vlan(config_str, 2) err = self.assertRaises(n_exc.NetworkVlanRangeError, self.parse_one, config_str) self.assertEqual(expected_msg, str(err)) class TestParseVlanRangeList(UtilTestParseVlanRanges): def parse_list(self, cfg_entries): return plugin_utils.parse_network_vlan_ranges(cfg_entries) def test_parse_list_one_net_no_vlan_range(self): config_list = ["net1"] expected_networks = {"net1": []} self.assertEqual(expected_networks, self.parse_list(config_list)) def test_parse_list_one_net_vlan_range(self): config_list = ["net1:100:199"] expected_networks = {"net1": [(100, 199)]} self.assertEqual(expected_networks, self.parse_list(config_list)) def test_parse_two_nets_no_vlan_range(self): config_list = ["net1", "net2"] expected_networks = {"net1": [], "net2": []} self.assertEqual(expected_networks, self.parse_list(config_list)) def test_parse_two_nets_range_and_no_range(self): config_list = ["net1:100:199", "net2"] expected_networks = {"net1": [(100, 199)], "net2": []} self.assertEqual(expected_networks, self.parse_list(config_list)) def test_parse_two_nets_no_range_and_range(self): config_list = ["net1", "net2:200:299"] expected_networks = {"net1": [], "net2": [(200, 299)]} self.assertEqual(expected_networks, self.parse_list(config_list)) def test_parse_two_nets_bad_vlan_range1(self): config_list = ["net1:100", "net2:200:299"] expected_msg = self._range_err_bad_count(config_list[0]) err = self.assertRaises(n_exc.NetworkVlanRangeError, self.parse_list, config_list) self.assertEqual(expected_msg, str(err)) def test_parse_two_nets_vlan_not_int2(self): config_list = ["net1:100:199", "net2:200:0x200"] expected_msg = self._range_invalid_vlan(config_list[1], 2) err = self.assertRaises(n_exc.NetworkVlanRangeError, self.parse_list, config_list) self.assertEqual(expected_msg, str(err)) def test_parse_two_nets_and_append_1_2(self): config_list = ["net1:100:199", "net1:1000:1099", "net2:200:299"] expected_networks = {"net1": [(100, 199), (1000, 1099)], "net2": [(200, 299)]} self.assertEqual(expected_networks, self.parse_list(config_list)) def test_parse_two_nets_and_append_1_3(self): config_list = ["net1:100:199", "net2:200:299", "net1:1000:1099"] expected_networks = {"net1": [(100, 199), (1000, 1099)], "net2": [(200, 299)]} self.assertEqual(expected_networks, self.parse_list(config_list)) class TestExceptionLogger(base.BaseTestCase): def test_normal_call(self): result = "Result" @utils.exception_logger() def func(): return result self.assertEqual(result, func()) def test_raise(self): result = "Result" @utils.exception_logger() def func(): raise RuntimeError(result) self.assertRaises(RuntimeError, func) def test_spawn_normal(self): result = "Result" logger = mock.Mock() @utils.exception_logger(logger=logger) def func(): return result gt = eventlet.spawn(func) self.assertEqual(result, gt.wait()) self.assertFalse(logger.called) def test_spawn_raise(self): result = "Result" logger = mock.Mock() @utils.exception_logger(logger=logger) def func(): raise RuntimeError(result) gt = eventlet.spawn(func) self.assertRaises(RuntimeError, gt.wait) self.assertTrue(logger.called) def test_pool_spawn_normal(self): logger = mock.Mock() calls = mock.Mock() @utils.exception_logger(logger=logger) def func(i): calls(i) pool = eventlet.GreenPool(4) for i in range(0, 4): pool.spawn(func, i) pool.waitall() calls.assert_has_calls([mock.call(0), mock.call(1), mock.call(2), mock.call(3)], any_order=True) self.assertFalse(logger.called) def test_pool_spawn_raise(self): logger = mock.Mock() calls = mock.Mock() @utils.exception_logger(logger=logger) def func(i): if i == 2: raise RuntimeError(2) else: calls(i) pool = eventlet.GreenPool(4) for i in range(0, 4): pool.spawn(func, i) pool.waitall() calls.assert_has_calls([mock.call(0), mock.call(1), mock.call(3)], any_order=True) self.assertTrue(logger.called) class TestDvrServices(base.BaseTestCase): def _test_is_dvr_serviced(self, device_owner, expected): self.assertEqual(expected, utils.is_dvr_serviced(device_owner)) def test_is_dvr_serviced_with_lb_port(self): self._test_is_dvr_serviced(constants.DEVICE_OWNER_LOADBALANCER, True) def test_is_dvr_serviced_with_lbv2_port(self): self._test_is_dvr_serviced(constants.DEVICE_OWNER_LOADBALANCERV2, True) def test_is_dvr_serviced_with_dhcp_port(self): self._test_is_dvr_serviced(constants.DEVICE_OWNER_DHCP, True) def test_is_dvr_serviced_with_vm_port(self): self._test_is_dvr_serviced(constants.DEVICE_OWNER_COMPUTE_PREFIX, True) class TestFipServices(base.BaseTestCase): def _test_is_fip_serviced(self, device_owner, expected): self.assertEqual(expected, utils.is_fip_serviced(device_owner)) def test_is_fip_serviced_with_lb_port(self): self._test_is_fip_serviced(constants.DEVICE_OWNER_LOADBALANCER, True) def test_is_fip_serviced_with_lbv2_port(self): self._test_is_fip_serviced(constants.DEVICE_OWNER_LOADBALANCERV2, True) def test_is_fip_serviced_with_dhcp_port(self): self._test_is_fip_serviced(constants.DEVICE_OWNER_DHCP, False) def test_is_fip_serviced_with_vm_port(self): self._test_is_fip_serviced(constants.DEVICE_OWNER_COMPUTE_PREFIX, True) class TestIpToCidr(base.BaseTestCase): def test_ip_to_cidr_ipv4_default(self): self.assertEqual('15.1.2.3/32', utils.ip_to_cidr('15.1.2.3')) def test_ip_to_cidr_ipv4_prefix(self): self.assertEqual('15.1.2.3/24', utils.ip_to_cidr('15.1.2.3', 24)) def test_ip_to_cidr_ipv4_netaddr(self): ip_address = netaddr.IPAddress('15.1.2.3') self.assertEqual('15.1.2.3/32', utils.ip_to_cidr(ip_address)) def test_ip_to_cidr_ipv4_bad_prefix(self): self.assertRaises(netaddr.core.AddrFormatError, utils.ip_to_cidr, '15.1.2.3', 33) def test_ip_to_cidr_ipv6_default(self): self.assertEqual('::1/128', utils.ip_to_cidr('::1')) def test_ip_to_cidr_ipv6_prefix(self): self.assertEqual('::1/64', utils.ip_to_cidr('::1', 64)) def test_ip_to_cidr_ipv6_bad_prefix(self): self.assertRaises(netaddr.core.AddrFormatError, utils.ip_to_cidr, '2000::1', 129) class TestCidrIsHost(base.BaseTestCase): def test_is_cidr_host_ipv4(self): self.assertTrue(utils.is_cidr_host('15.1.2.3/32')) def test_is_cidr_host_ipv4_not_cidr(self): self.assertRaises(ValueError, utils.is_cidr_host, '15.1.2.3') def test_is_cidr_host_ipv6(self): self.assertTrue(utils.is_cidr_host('2000::1/128')) def test_is_cidr_host_ipv6_netaddr(self): net = netaddr.IPNetwork("2000::1") self.assertTrue(utils.is_cidr_host(net)) def test_is_cidr_host_ipv6_32(self): self.assertFalse(utils.is_cidr_host('2000::1/32')) def test_is_cidr_host_ipv6_not_cidr(self): self.assertRaises(ValueError, utils.is_cidr_host, '2000::1') def test_is_cidr_host_ipv6_not_cidr_netaddr(self): ip_address = netaddr.IPAddress("2000::3") self.assertRaises(ValueError, utils.is_cidr_host, ip_address) class TestIpVersionFromInt(base.BaseTestCase): def test_ip_version_from_int_ipv4(self): self.assertEqual(constants.IPv4, utils.ip_version_from_int(4)) def test_ip_version_from_int_ipv6(self): self.assertEqual(constants.IPv6, utils.ip_version_from_int(6)) def test_ip_version_from_int_illegal_int(self): self.assertRaises(ValueError, utils.ip_version_from_int, 8) class TestIsVersionGreaterEqual(base.BaseTestCase): def test_is_version_greater_equal_greater(self): self.assertTrue(utils.is_version_greater_equal('1.6.2', '1.6.0')) def test_is_version_greater_equal_equal(self): self.assertTrue(utils.is_version_greater_equal('1.6.2', '1.6.2')) def test_is_version_greater_equal_less(self): self.assertFalse(utils.is_version_greater_equal('1.6.0', '1.6.2')) class TestDelayedStringRenderer(base.BaseTestCase): def test_call_deferred_until_str(self): my_func = mock.MagicMock(return_value='Brie cheese!') delayed = utils.DelayedStringRenderer(my_func, 1, 2, key_arg=44) self.assertFalse(my_func.called) string = "Type: %s" % delayed my_func.assert_called_once_with(1, 2, key_arg=44) self.assertEqual("Type: Brie cheese!", string) def test_not_called_with_low_log_level(self): LOG = logging.getLogger(__name__) # make sure we return logging to previous level current_log_level = LOG.logger.getEffectiveLevel() self.addCleanup(LOG.logger.setLevel, current_log_level) my_func = mock.MagicMock() delayed = utils.DelayedStringRenderer(my_func) # set to warning so we shouldn't be logging debug messages LOG.logger.setLevel(logging.logging.WARNING) LOG.debug("Hello %s", delayed) self.assertFalse(my_func.called) # but it should be called with the debug level LOG.logger.setLevel(logging.logging.DEBUG) LOG.debug("Hello %s", delayed) self.assertTrue(my_func.called) class TestPortRuleMasking(base.BaseTestCase): def test_port_rule_wrong_input(self): with testtools.ExpectedException(ValueError): utils.port_rule_masking(12, 5) def compare_port_ranges_results(self, port_min, port_max): observed = utils.port_rule_masking(port_min, port_max) expected = _port_rule_masking(port_min, port_max) self.assertItemsEqual(expected, observed) def test_port_rule_masking_random_ranges(self): # calling randint a bunch of times is really slow randports = sorted(random.sample(six.moves.range(1, 65536), 2000)) port_max = 0 for i in randports: port_min = port_max port_max = i self.compare_port_ranges_results(port_min, port_max) def test_port_rule_masking_edge_cases(self): # (port_min, port_max) tuples TESTING_DATA = [ (5, 12), (20, 130), (4501, 33057), (0, 65535), (22, 22), (5001, 5001), (0, 7), (8, 15), (1, 127), ] for port_min, port_max in TESTING_DATA: self.compare_port_ranges_results(port_min, port_max) class TestAuthenticEUI(base.BaseTestCase): def test_retains_original_format(self): for mac_str in ('FA-16-3E-73-A2-E9', 'fa:16:3e:73:a2:e9'): self.assertEqual(mac_str, str(utils.AuthenticEUI(mac_str))) def test_invalid_values(self): for mac in ('XXXX', 'ypp', 'g3:vvv'): with testtools.ExpectedException(netaddr.core.AddrFormatError): utils.AuthenticEUI(mac) class TestAuthenticIPNetwork(base.BaseTestCase): def test_retains_original_format(self): for addr_str in ('10.0.0.0/24', '10.0.0.10/32', '100.0.0.1'): self.assertEqual(addr_str, str(utils.AuthenticIPNetwork(addr_str))) def test_invalid_values(self): for addr in ('XXXX', 'ypp', 'g3:vvv'): with testtools.ExpectedException(netaddr.core.AddrFormatError): utils.AuthenticIPNetwork(addr) class TestExcDetails(base.BaseTestCase): def test_attach_exc_details(self): e = Exception() utils.attach_exc_details(e, 'details') self.assertEqual('details', utils.extract_exc_details(e)) def test_attach_exc_details_with_interpolation(self): e = Exception() utils.attach_exc_details(e, 'details: %s', 'foo') self.assertEqual('details: foo', utils.extract_exc_details(e)) def test_attach_exc_details_with_None_interpolation(self): e = Exception() utils.attach_exc_details(e, 'details: %s', None) self.assertEqual( 'details: %s' % str(None), utils.extract_exc_details(e)) def test_attach_exc_details_with_multiple_interpolation(self): e = Exception() utils.attach_exc_details( e, 'details: %s, %s', ('foo', 'bar')) self.assertEqual('details: foo, bar', utils.extract_exc_details(e)) def test_attach_exc_details_with_dict_interpolation(self): e = Exception() utils.attach_exc_details( e, 'details: %(foo)s, %(bar)s', {'foo': 'foo', 'bar': 'bar'}) self.assertEqual('details: foo, bar', utils.extract_exc_details(e)) def test_extract_exc_details_no_details_attached(self): self.assertIsInstance( utils.extract_exc_details(Exception()), six.text_type) @ddt.ddt class ImportModulesRecursivelyTestCase(base.BaseTestCase): @ddt.data('/', r'\\') def test_recursion(self, separator): expected_modules = ( 'neutron.tests.unit.tests.example.dir.example_module', 'neutron.tests.unit.tests.example.dir.subdir.example_module', ) for module in expected_modules: sys.modules.pop(module, None) topdir = re.sub(r'[/\\]+', separator, os.path.dirname(tests.__file__)) modules = utils.import_modules_recursively(topdir) for module in expected_modules: self.assertIn(module, modules) self.assertIn(module, sys.modules) class TestThrottler(base.BaseTestCase): def test_throttler(self): threshold = 1 orig_function = mock.Mock() # Add this magic name as it's required by functools orig_function.__name__ = 'mock_func' throttled_func = utils.throttler(threshold)(orig_function) throttled_func() sleep = utils.eventlet.sleep def sleep_mock(amount_to_sleep): sleep(amount_to_sleep) self.assertTrue(threshold > amount_to_sleep) with mock.patch.object(utils.eventlet, "sleep", side_effect=sleep_mock): throttled_func() self.assertEqual(2, orig_function.call_count) lock_with_timer = six.get_function_closure( throttled_func)[1].cell_contents timestamp = lock_with_timer.timestamp - threshold lock_with_timer.timestamp = timestamp throttled_func() self.assertEqual(3, orig_function.call_count) self.assertTrue(timestamp < lock_with_timer.timestamp) def test_method_docstring_is_preserved(self): class Klass(object): @utils.throttler() def method(self): """Docstring""" self.assertEqual("Docstring", Klass.method.__doc__) def test_method_still_callable(self): class Klass(object): @utils.throttler() def method(self): pass obj = Klass() obj.method() class BaseUnitConversionTest(object): def test_bytes_to_bits(self): test_values = [ (0, 0), # 0 bytes should be 0 bits (1, 8) # 1 byte should be 8 bits ] for input_bytes, expected_bits in test_values: self.assertEqual( expected_bits, utils.bytes_to_bits(input_bytes) ) class TestSIUnitConversions(BaseUnitConversionTest, base.BaseTestCase): base_unit = common_constants.SI_BASE def test_bits_to_kilobits(self): test_values = [ (0, 0), # 0 bites should be 0 kilobites (1, 1), # 1 bit should be 1 kilobit (999, 1), # 999 bits should be 1 kilobit (1000, 1), # 1000 bits should be 1 kilobit (1001, 2) # 1001 bits should be 2 kilobits ] for input_bits, expected_kilobits in test_values: self.assertEqual( expected_kilobits, utils.bits_to_kilobits(input_bits, self.base_unit) ) class TestIECUnitConversions(BaseUnitConversionTest, base.BaseTestCase): base_unit = common_constants.IEC_BASE def test_bits_to_kilobits(self): test_values = [ (0, 0), # 0 bites should be 0 kilobites (1, 1), # 1 bit should be 1 kilobit (1023, 1), # 1023 bits should be 1 kilobit (1024, 1), # 1024 bits should be 1 kilobit (1025, 2) # 1025 bits should be 2 kilobits ] for input_bits, expected_kilobits in test_values: self.assertEqual( expected_kilobits, utils.bits_to_kilobits(input_bits, self.base_unit) ) neutron-12.1.1/neutron/tests/unit/common/moved_globals_code1.py0000664000175000017500000000200213553660046024657 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. """ Used by test cases in test__deprecate.py """ from neutron.common import _deprecate from neutron.tests.unit.common import moved_globals_target # a has been moved to moved_globals_target.a b = 'barasingha' # c has been renamed to d d = 'capybara' # e has been moved to moved_globals_target.f g = 'gelada' _deprecate._moved_global('c', new_name='d') _deprecate._moved_global('e', new_name='f', new_module=moved_globals_target) _deprecate._MovedGlobals(moved_globals_target) neutron-12.1.1/neutron/tests/unit/common/__init__.py0000664000175000017500000000000013553660046022522 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/unit/common/test_ipv6_utils.py0000664000175000017500000001171213553660047024143 0ustar zuulzuul00000000000000# Copyright 2013 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import mock from neutron_lib import constants from neutron.common import ipv6_utils from neutron.tests import base from neutron.tests import tools class TestIsEnabledAndBindByDefault(base.BaseTestCase): def setUp(self): super(TestIsEnabledAndBindByDefault, self).setUp() def reset_detection_flag(): ipv6_utils._IS_IPV6_ENABLED = None reset_detection_flag() self.addCleanup(reset_detection_flag) self.mock_exists = mock.patch("os.path.exists", return_value=True).start() self.proc_path = '/proc/sys/net/ipv6/conf/default/disable_ipv6' def test_enabled(self): self.useFixture(tools.OpenFixture(self.proc_path, '0')) enabled = ipv6_utils.is_enabled_and_bind_by_default() self.assertTrue(enabled) def test_disabled(self): self.useFixture(tools.OpenFixture(self.proc_path, '1')) enabled = ipv6_utils.is_enabled_and_bind_by_default() self.assertFalse(enabled) def test_disabled_non_exists(self): mo = self.useFixture(tools.OpenFixture(self.proc_path, '1')).mock_open self.mock_exists.return_value = False enabled = ipv6_utils.is_enabled_and_bind_by_default() self.assertFalse(enabled) self.assertFalse(mo.called) def test_memoize(self): mo = self.useFixture(tools.OpenFixture(self.proc_path, '0')).mock_open ipv6_utils.is_enabled_and_bind_by_default() enabled = ipv6_utils.is_enabled_and_bind_by_default() self.assertTrue(enabled) mo.assert_called_once_with(self.proc_path, 'r') class TestIsAutoAddressSubnet(base.BaseTestCase): def setUp(self): self.subnet = { 'cidr': '2001:200::/64', 'gateway_ip': '2001:200::1', 'ip_version': 6, 'ipv6_address_mode': None, 'ipv6_ra_mode': None } super(TestIsAutoAddressSubnet, self).setUp() def test_combinations(self): Mode = collections.namedtuple('Mode', "addr_mode ra_mode " "is_auto_address") subnets = [ Mode(None, None, False), Mode(constants.DHCPV6_STATEFUL, None, False), Mode(constants.DHCPV6_STATELESS, None, True), Mode(constants.IPV6_SLAAC, None, True), Mode(None, constants.DHCPV6_STATEFUL, False), Mode(None, constants.DHCPV6_STATELESS, True), Mode(None, constants.IPV6_SLAAC, True), Mode(constants.DHCPV6_STATEFUL, constants.DHCPV6_STATEFUL, False), Mode(constants.DHCPV6_STATELESS, constants.DHCPV6_STATELESS, True), Mode(constants.IPV6_SLAAC, constants.IPV6_SLAAC, True), ] for subnet in subnets: self.subnet['ipv6_address_mode'] = subnet.addr_mode self.subnet['ipv6_ra_mode'] = subnet.ra_mode self.assertEqual(subnet.is_auto_address, ipv6_utils.is_auto_address_subnet(self.subnet)) class TestIsEui64Address(base.BaseTestCase): def _test_eui_64(self, ips, expected): for ip in ips: self.assertEqual(expected, ipv6_utils.is_eui64_address(ip), "Error on %s" % ip) def test_invalid_eui64_addresses(self): ips = ('192.168.1.1', '192.168.1.0', '255.255.255.255', '0.0.0.0', 'fffe::', 'ff80::1', 'fffe::0cad:12ff:ff44:5566', 'fffe::0cad:12fe:fe44:5566', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff') self._test_eui_64(ips, False) class TestValidIpv6URL(base.BaseTestCase): def test_valid_ipv6_url(self): host = "::1" port = 443 self.assertEqual("[::1]:443", ipv6_utils.valid_ipv6_url(host, port)) def test_invalid_ipv6_url(self): host = "::1" port = 443 self.assertNotEqual("::1:443", ipv6_utils.valid_ipv6_url(host, port)) def test_valid_ipv4_url(self): host = "192.168.1.2" port = 443 self.assertEqual("192.168.1.2:443", ipv6_utils.valid_ipv6_url(host, port)) def test_valid_hostname_url(self): host = "controller" port = 443 self.assertEqual("controller:443", ipv6_utils.valid_ipv6_url(host, port)) neutron-12.1.1/neutron/tests/unit/common/moved_globals_target.py0000664000175000017500000000116113553660046025157 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. """ Used by test cases in test__deprecate.py """ a = 'aardvark' f = 'echidna' neutron-12.1.1/neutron/tests/unit/common/moved_globals_code2.py0000664000175000017500000000140713553660046024670 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. """ Used by test cases in test__deprecate.py """ from neutron.common import _deprecate from neutron.tests.unit.common import moved_globals_target global1 = 'foo' _deprecate._MovedGlobals(moved_globals_target) global2 = 'bar' neutron-12.1.1/neutron/tests/unit/test_service.py0000664000175000017500000000511413553660047022206 0ustar zuulzuul00000000000000# Copyright 2015 Mirantis Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from oslo_config import cfg from neutron import service from neutron.tests import base from neutron.tests.unit import test_wsgi class TestRpcWorker(test_wsgi.TestServiceBase): def test_reset(self): _plugin = mock.Mock() rpc_worker = service.RpcWorker(_plugin) self._test_reset(rpc_worker) class TestRunWsgiApp(base.BaseTestCase): def setUp(self): super(TestRunWsgiApp, self).setUp() self.processor_count = mock.patch( 'oslo_concurrency.processutils.get_worker_count' ).start().return_value def _test_api_workers(self, config_value, expected_passed_value): if config_value is not None: cfg.CONF.set_override('api_workers', config_value) with mock.patch('neutron.wsgi.Server') as mock_server: service.run_wsgi_app(mock.sentinel.app) start_call = mock_server.return_value.start.call_args expected_call = mock.call( mock.ANY, mock.ANY, mock.ANY, workers=expected_passed_value) self.assertEqual(expected_call, start_call) def test_api_workers_zero(self): self._test_api_workers(0, 0) def test_api_workers_default(self): self._test_api_workers(None, self.processor_count) def test_api_workers_defined(self): self._test_api_workers(42, 42) def test_start_all_workers(self): cfg.CONF.set_override('api_workers', 0) mock.patch.object(service, '_get_rpc_workers').start() mock.patch.object(service, '_get_plugins_workers').start() mock.patch.object(service, '_start_workers').start() callback = mock.Mock() registry.subscribe(callback, resources.PROCESS, events.AFTER_SPAWN) service.start_all_workers() callback.assert_called_once_with( resources.PROCESS, events.AFTER_SPAWN, mock.ANY, payload=None) neutron-12.1.1/neutron/tests/unit/dummy_plugin.py0000664000175000017500000001322413553660047022221 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions from neutron_lib.plugins import directory from neutron_lib.services import base as service_base from oslo_utils import uuidutils from neutron.api import extensions from neutron.api.v2 import base from neutron.db import servicetype_db from neutron.extensions import servicetype from neutron import neutron_plugin_base_v2 RESOURCE_NAME = "dummy" COLLECTION_NAME = "%ss" % RESOURCE_NAME DUMMY_SERVICE_TYPE = "DUMMY" # Attribute Map for dummy resource RESOURCE_ATTRIBUTE_MAP = { COLLECTION_NAME: { 'id': {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True}, 'name': {'allow_post': True, 'allow_put': True, 'validate': {'type:string': None}, 'is_visible': True, 'default': ''}, 'tenant_id': {'allow_post': True, 'allow_put': False, 'required_by_policy': True, 'is_visible': True}, 'service_type': {'allow_post': True, 'allow_put': False, 'validate': {'type:servicetype_ref': None}, 'is_visible': True, 'default': None} } } class Dummy(object): @classmethod def get_name(cls): return RESOURCE_NAME @classmethod def get_alias(cls): return RESOURCE_NAME @classmethod def get_description(cls): return "Dummy stuff" @classmethod def get_updated(cls): return "2012-11-20T10:00:00-00:00" @classmethod def get_resources(cls): """Returns Extended Resource for dummy management.""" dummy_inst = directory.get_plugin(DUMMY_SERVICE_TYPE) controller = base.create_resource( COLLECTION_NAME, RESOURCE_NAME, dummy_inst, RESOURCE_ATTRIBUTE_MAP[COLLECTION_NAME]) return [extensions.ResourceExtension(COLLECTION_NAME, controller)] class DummyServicePlugin(service_base.ServicePluginBase): """This is a simple plugin for managing instances of a fictional 'dummy' service. This plugin is provided as a proof-of-concept of how advanced service might leverage the service type extension. Ideally, instances of real advanced services, such as load balancing or VPN will adopt a similar solution. """ supported_extension_aliases = [RESOURCE_NAME, servicetype.EXT_ALIAS] path_prefix = "/dummy_svc" agent_notifiers = {RESOURCE_NAME: 'dummy_agent_notifier'} def __init__(self): self.svctype_mgr = servicetype_db.ServiceTypeManager.get_instance() self.dummys = {} @classmethod def get_plugin_type(cls): return DUMMY_SERVICE_TYPE def get_plugin_description(self): return "Neutron Dummy Service Plugin" def get_dummys(self, context, filters, fields): return self.dummys.values() def get_dummy(self, context, id, fields): try: return self.dummys[id] except KeyError: raise exceptions.NotFound() def create_dummy(self, context, dummy): d = dummy[RESOURCE_NAME] d['id'] = uuidutils.generate_uuid() self.dummys[d['id']] = d self.svctype_mgr.increase_service_type_refcount(context, d['service_type']) return d def update_dummy(self, context, id, dummy): pass def delete_dummy(self, context, id): try: svc_type_id = self.dummys[id]['service_type'] del self.dummys[id] self.svctype_mgr.decrease_service_type_refcount(context, svc_type_id) except KeyError: raise exceptions.NotFound() class DummyCorePluginWithoutDatastore( neutron_plugin_base_v2.NeutronPluginBaseV2): def create_subnet(self, context, subnet): pass def update_subnet(self, context, id, subnet): pass def get_subnet(self, context, id, fields=None): pass def get_subnets(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass def delete_subnet(self, context, id): pass def create_network(self, context, network): pass def update_network(self, context, id, network): pass def get_network(self, context, id, fields=None): pass def get_networks(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass def delete_network(self, context, id): pass def create_port(self, context, port): pass def update_port(self, context, id, port): pass def get_port(self, context, id, fields=None): pass def get_ports(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pass def delete_port(self, context, id): pass neutron-12.1.1/neutron/tests/unit/extension_stubs.py0000664000175000017500000000443613553660046022750 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.api import extensions as api_extensions from neutron_lib.services import base from neutron import wsgi class StubExtension(api_extensions.ExtensionDescriptor): def __init__(self, alias="stub_extension", optional=None): self.alias = alias self.optional = optional or [] def get_name(self): return "Stub Extension" def get_alias(self): return self.alias def get_description(self): return "" def get_updated(self): return "" def get_optional_extensions(self): return self.optional class StubExtensionWithReqs(StubExtension): def get_required_extensions(self): return ["foo"] class StubPlugin(object): def __init__(self, supported_extensions=None): supported_extensions = supported_extensions or [] self.supported_extension_aliases = supported_extensions class ExtensionExpectingPluginInterface(StubExtension): """Expect plugin to implement all methods in StubPluginInterface. This extension expects plugin to implement all the methods defined in StubPluginInterface. """ def get_plugin_interface(self): return StubPluginInterface class StubPluginInterface(base.ServicePluginBase): @abc.abstractmethod def get_foo(self, bar=None): pass def get_plugin_type(self): pass def get_plugin_description(self): pass class StubBaseAppController(wsgi.Controller): def index(self, request): return "base app index" def show(self, request, id): return {'fort': 'knox'} def update(self, request, id): return {'uneditable': 'original_value'} neutron-12.1.1/neutron/tests/contrib/0000775000175000017500000000000013553660156017616 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/contrib/testing.filters0000664000175000017500000000464513553660046022674 0ustar zuulzuul00000000000000# neutron-rootwrap command filters to support functional testing. It # is NOT intended to be used outside of a test environment. # # This file should be owned by (and only-writeable by) the root user [Filters] # enable ping from namespace ping_filter: CommandFilter, ping, root ping6_filter: CommandFilter, ping6, root ping_kill: KillFilter, root, ping, -2 # enable curl from namespace curl_filter: RegExpFilter, /usr/bin/curl, root, curl, --max-time, \d+, -D-, http://[0-9a-z:./-]+ ncat_filter: CommandFilter, ncat, root ncat_kill: KillFilter, root, ncat, -9 ss_filter: CommandFilter, ss, root # enable neutron-linuxbridge-cleanup from namespace lb_cleanup_filter: RegExpFilter, neutron-linuxbridge-cleanup, root, neutron-linuxbridge-cleanup, --config-file, .* # enable dhclient from namespace dhclient_filter: CommandFilter, dhclient, root dhclient_kill: KillFilter, root, dhclient, -9 # Actually, dhclient is used for test dhcp-agent and runs # in dhcp-agent namespace. If in that namespace resolv.conf file not exist # dhclient will override system /etc/resolv.conf # Filters below are limit functions mkdir, rm and touch # only to create and delete file resolv.conf in the namespace mkdir_filter: RegExpFilter, /bin/mkdir, root, mkdir, -p, /etc/netns/qdhcp-[0-9a-z./-]+ rm_filter: RegExpFilter, /bin/rm, root, rm, -r, /etc/netns/qdhcp-[0-9a-z./-]+ touch_filter: RegExpFilter, /bin/touch, root, touch, /etc/netns/qdhcp-[0-9a-z./-]+/resolv.conf touch_filter2: RegExpFilter, /usr/bin/touch, root, touch, /etc/netns/qdhcp-[0-9a-z./-]+/resolv.conf # needed by test_ovs_flows which runs ovs-appctl ofproto/trace ovstrace_filter: RegExpFilter, ovs-appctl, root, ovs-appctl, ofproto/trace, .*, .* # needed for TestGetRootHelperChildPid bash_filter: RegExpFilter, /bin/bash, root, bash, -c, \(sleep 100\) sleep_kill: KillFilter, root, sleep, -9 #needed by test_netns_cleanup process_spawn: EnvFilter, env, root, PATH=, python ip_exec: IpNetnsExecFilter, ip, root ps: CommandFilter, ps, root pid_kill: RegExpFilter, kill, root, kill, -\d+, .* #needed to set up fullstack 'multinode' environment rabbitmqctl: CommandFilter, rabbitmqctl, root linuxbridge_agent: CommandFilter, neutron-linuxbridge-agent, root dhcp_agent: CommandFilter, dhcp_agent.py, root ovs_agent: CommandFilter, ovs_agent.py, root l3_agent: CommandFilter, l3_agent.py, root #needed to capture and analyze traffic in fullstack tests (f.e. in DSCP scenarios) tcpdump: CommandFilter, tcpdump, root neutron-12.1.1/neutron/tests/contrib/post_test_hook.sh0000664000175000017500000000411213553660047023213 0ustar zuulzuul00000000000000#!/usr/bin/env bash set -xe NEUTRON_DIR="$BASE/new/neutron" SCRIPTS_DIR="/usr/os-testr-env/bin/" venv=${1:-"dsvm-functional"} function generate_testr_results { # Give job user rights to access tox logs sudo -H -u $owner chmod o+rw . sudo -H -u $owner chmod o+rw -R .stestr if [ -f ".stestr/0" ] ; then .tox/$venv/bin/subunit-1to2 < .stestr/0 > ./stestr.subunit $SCRIPTS_DIR/subunit2html ./stestr.subunit testr_results.html gzip -9 ./stestr.subunit gzip -9 ./testr_results.html sudo mv ./*.gz /opt/stack/logs/ fi } function generate_log_index { local xtrace xtrace=$(set +o | grep xtrace) set +o xtrace # honor job flavors like -python35 case $venv in *"dsvm-fullstack"*) venv="dsvm-fullstack" ;; *"dsvm-functional"*) venv="dsvm-functional" ;; *) echo "Unrecognized environment $venv". exit 1 esac virtualenv /tmp/os-log-merger /tmp/os-log-merger/bin/pip install -U os-log-merger==1.1.0 files=$(find /opt/stack/logs/$venv-logs -name '*.txt' -o -name '*.log') # -a3 to truncate common path prefix # || true to avoid the whole run failure because of os-log-merger crashes and such # TODO(ihrachys) remove || true when we have more trust in os-log-merger contents=$(/tmp/os-log-merger/bin/os-log-merger -a3 $files || true) # don't store DEBUG level messages because they are not very useful, # and are not indexed by logstash anyway echo "$contents" | grep -v DEBUG | sudo tee /opt/stack/logs/$venv-index.txt > /dev/null $xtrace } if [[ "$venv" == dsvm-functional* ]] || [[ "$venv" == dsvm-fullstack* ]]; then owner=stack sudo_env= # Set owner permissions according to job's requirements. cd $NEUTRON_DIR sudo chown -R $owner:stack $NEUTRON_DIR # Run tests echo "Running neutron $venv test suite" set +e sudo -H -u $owner $sudo_env tox -e $venv testr_exit_code=$? set -e # Collect and parse results generate_testr_results generate_log_index exit $testr_exit_code fi neutron-12.1.1/neutron/tests/contrib/hooks/0000775000175000017500000000000013553660156020741 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/contrib/hooks/disable_dvr0000664000175000017500000000007313553660046023140 0ustar zuulzuul00000000000000[[post-config|/$NEUTRON_CONF]] [DEFAULT] enable_dvr=False neutron-12.1.1/neutron/tests/contrib/hooks/segments0000664000175000017500000000004013553660046022501 0ustar zuulzuul00000000000000enable_service neutron-segments neutron-12.1.1/neutron/tests/contrib/hooks/ovsfw0000664000175000017500000000013213553660047022023 0ustar zuulzuul00000000000000[[post-config|/$NEUTRON_CORE_PLUGIN_CONF]] [securitygroup] firewall_driver = openvswitch neutron-12.1.1/neutron/tests/contrib/hooks/linuxbridge_type_drivers0000664000175000017500000000034313553660047025776 0ustar zuulzuul00000000000000[[test-config|$TEMPEST_CONFIG]] [neutron_plugin_options] available_type_drivers=flat,vlan,local,vxlan [[post-config|/$NEUTRON_CORE_PLUGIN_CONF]] [ml2] type_drivers=flat,vlan,local,vxlan [ml2_type_vxlan] vni_ranges = 1:2000 neutron-12.1.1/neutron/tests/contrib/hooks/quotas0000664000175000017500000000026713553660046022203 0ustar zuulzuul00000000000000[[post-config|$NEUTRON_CONF]] [quotas] # x10 of default quotas (at the time of writing) quota_router=100 quota_floatingip=500 quota_security_group=100 quota_security_group_rule=1000 neutron-12.1.1/neutron/tests/contrib/hooks/osprofiler0000664000175000017500000000045413553660046023051 0ustar zuulzuul00000000000000[[post-config|/etc/neutron/api-paste.ini]] [composite:neutronapi_v2_0] use = call:neutron.auth:pipeline_factory noauth = cors request_id catch_errors osprofiler extensions neutronapiapp_v2_0 keystone = cors request_id catch_errors osprofiler authtoken keystonecontext extensions neutronapiapp_v2_0 neutron-12.1.1/neutron/tests/contrib/hooks/tunnel_types0000664000175000017500000000026413553660046023415 0ustar zuulzuul00000000000000# ideally we would configure it in openvswitch_agent.ini but devstack doesn't # load it for its l2 agent [[post-config|/$NEUTRON_CORE_PLUGIN_CONF]] [AGENT] tunnel_types=gre,vxlan neutron-12.1.1/neutron/tests/contrib/hooks/api_all_extensions0000664000175000017500000000405013553660047024542 0ustar zuulzuul00000000000000# Keep entries alphabetically # NOTE: The first entry should not use '+=' and a comma. NETWORK_API_EXTENSIONS="address-scope" NETWORK_API_EXTENSIONS+=",agent" NETWORK_API_EXTENSIONS+=",allowed-address-pairs" NETWORK_API_EXTENSIONS+=",auto-allocated-topology" NETWORK_API_EXTENSIONS+=",availability_zone" NETWORK_API_EXTENSIONS+=",binding" NETWORK_API_EXTENSIONS+=",default-subnetpools" NETWORK_API_EXTENSIONS+=",dhcp_agent_scheduler" NETWORK_API_EXTENSIONS+=",dns-integration" NETWORK_API_EXTENSIONS+=",dvr" NETWORK_API_EXTENSIONS+=",ext-gw-mode" NETWORK_API_EXTENSIONS+=",external-net" NETWORK_API_EXTENSIONS+=",extra_dhcp_opt" NETWORK_API_EXTENSIONS+=",extraroute" NETWORK_API_EXTENSIONS+=",flavors" NETWORK_API_EXTENSIONS+=",ip-substring-filtering" NETWORK_API_EXTENSIONS+=",l3-flavors" NETWORK_API_EXTENSIONS+=",l3-ha" NETWORK_API_EXTENSIONS+=",l3_agent_scheduler" NETWORK_API_EXTENSIONS+=",logging" NETWORK_API_EXTENSIONS+=",metering" NETWORK_API_EXTENSIONS+=",multi-provider" NETWORK_API_EXTENSIONS+=",net-mtu" NETWORK_API_EXTENSIONS+=",net-mtu-writable" NETWORK_API_EXTENSIONS+=",network-ip-availability" NETWORK_API_EXTENSIONS+=",network_availability_zone" NETWORK_API_EXTENSIONS+=",pagination" NETWORK_API_EXTENSIONS+=",port-security" NETWORK_API_EXTENSIONS+=",project-id" NETWORK_API_EXTENSIONS+=",provider" NETWORK_API_EXTENSIONS+=",qos" NETWORK_API_EXTENSIONS+=",quotas" NETWORK_API_EXTENSIONS+=",quota_details" NETWORK_API_EXTENSIONS+=",rbac-policies" NETWORK_API_EXTENSIONS+=",router" NETWORK_API_EXTENSIONS+=",router_availability_zone" NETWORK_API_EXTENSIONS+=",security-group" NETWORK_API_EXTENSIONS+=",segment" NETWORK_API_EXTENSIONS+=",service-type" NETWORK_API_EXTENSIONS+=",sorting" NETWORK_API_EXTENSIONS+=",standard-attr-description" NETWORK_API_EXTENSIONS+=",standard-attr-revisions" NETWORK_API_EXTENSIONS+=",standard-attr-timestamp" NETWORK_API_EXTENSIONS+=",standard-attr-tag" NETWORK_API_EXTENSIONS+=",subnet_allocation" NETWORK_API_EXTENSIONS+=",tag" NETWORK_API_EXTENSIONS+=",tag-ext" NETWORK_API_EXTENSIONS+=",trunk" NETWORK_API_EXTENSIONS+=",trunk-details" neutron-12.1.1/neutron/tests/contrib/hooks/dns0000664000175000017500000000003313553660046021442 0ustar zuulzuul00000000000000enable_service neutron-dns neutron-12.1.1/neutron/tests/contrib/hooks/iptables_verify0000664000175000017500000000011513553660047024047 0ustar zuulzuul00000000000000[[post-config|/etc/neutron/neutron.conf]] [AGENT] debug_iptables_rules=True neutron-12.1.1/neutron/tests/contrib/hooks/qos0000664000175000017500000000013513553660047021464 0ustar zuulzuul00000000000000enable_plugin neutron https://git.openstack.org/openstack/neutron enable_service neutron-qos neutron-12.1.1/neutron/tests/contrib/hooks/dstat0000664000175000017500000000002513553660047021777 0ustar zuulzuul00000000000000enable_service dstat neutron-12.1.1/neutron/tests/contrib/hooks/vlan_provider0000664000175000017500000000025213553660046023533 0ustar zuulzuul00000000000000[[test-config|$TEMPEST_CONFIG]] [neutron_plugin_options] provider_vlans=foo, [[post-config|/$NEUTRON_CORE_PLUGIN_CONF]] [ml2_type_vlan] network_vlan_ranges = foo:1:10 neutron-12.1.1/neutron/tests/contrib/hooks/ubuntu_image0000664000175000017500000000046613553660047023355 0ustar zuulzuul00000000000000DOWNLOAD_DEFAULT_IMAGES=False IMAGE_URLS="http://cloud-images.ubuntu.com/releases/16.04/release-20170113/ubuntu-16.04-server-cloudimg-amd64-disk1.img," DEFAULT_INSTANCE_TYPE=ds512M DEFAULT_INSTANCE_USER=ubuntu BUILD_TIMEOUT=784 [[test-config|$TEMPEST_CONFIG]] [neutron_plugin_options] image_is_advanced=True neutron-12.1.1/neutron/tests/contrib/hooks/log0000664000175000017500000000020013553660046021433 0ustar zuulzuul00000000000000enable_service neutron-log [[post-config|/$NEUTRON_CORE_PLUGIN_CONF]] [network_log] local_output_log_base = /tmp/test_log.log neutron-12.1.1/neutron/tests/contrib/hooks/trunk0000664000175000017500000000003513553660046022023 0ustar zuulzuul00000000000000enable_service neutron-trunk neutron-12.1.1/neutron/tests/contrib/hooks/disable_dvr_tests0000664000175000017500000000004513553660046024361 0ustar zuulzuul00000000000000DISABLE_NETWORK_API_EXTENSIONS="dvr" neutron-12.1.1/neutron/tests/contrib/hooks/availability_zone0000664000175000017500000000034513553660046024371 0ustar zuulzuul00000000000000[[test-config|$TEMPEST_CONFIG]] [neutron_plugin_options] agent_availability_zone = nova [[post-config|/$NEUTRON_L3_CONF]] [agent] availability_zone = nova [[post-config|/$NEUTRON_DHCP_CONF]] [agent] availability_zone = nova neutron-12.1.1/neutron/tests/contrib/hooks/openvswitch_type_drivers0000664000175000017500000000044313553660046026033 0ustar zuulzuul00000000000000[[test-config|$TEMPEST_CONFIG]] [neutron_plugin_options] available_type_drivers=flat,geneve,vlan,gre,local,vxlan [[post-config|/$NEUTRON_CORE_PLUGIN_CONF]] [ml2] type_drivers=flat,geneve,vlan,gre,local,vxlan [ml2_type_vxlan] vni_ranges = 1:2000 [ml2_type_gre] tunnel_id_ranges = 1:1000 neutron-12.1.1/neutron/tests/contrib/hooks/stack_base0000664000175000017500000000055013553660047022762 0ustar zuulzuul00000000000000# set password, otherwise devstack enters interactive mode and fails ADMIN_PASSWORD=secretadmin # don't use screen to start services (needed to disable colorization in # captured service logs) USE_SCREEN=False # start with an empty service list, otherwise devstack will configure several # 'default' services, including rabbitmq and mysql disable_all_services neutron-12.1.1/neutron/tests/contrib/gate_hook.sh0000664000175000017500000001122113553660047022106 0ustar zuulzuul00000000000000#!/usr/bin/env bash set -ex VENV=${1:-"dsvm-functional"} FLAVOR=${2:-"all"} GATE_DEST=$BASE/new NEUTRON_PATH=$GATE_DEST/neutron GATE_HOOKS=$NEUTRON_PATH/neutron/tests/contrib/hooks DEVSTACK_PATH=$GATE_DEST/devstack LOCAL_CONF=$DEVSTACK_PATH/late-local.conf RALLY_EXTRA_DIR=$NEUTRON_PATH/rally-jobs/extra DSCONF=/tmp/devstack-tools/bin/dsconf # Install devstack-tools used to produce local.conf; we can't rely on # test-requirements.txt because the gate hook is triggered before neutron is # installed sudo -H pip install virtualenv virtualenv /tmp/devstack-tools /tmp/devstack-tools/bin/pip install -U devstack-tools==0.4.0 # Inject config from hook into localrc function load_rc_hook { local hook="$1" local tmpfile local config tmpfile=$(tempfile) config=$(cat $GATE_HOOKS/$hook) echo "[[local|localrc]]" > $tmpfile $DSCONF setlc_raw $tmpfile "$config" $DSCONF merge_lc $LOCAL_CONF $tmpfile rm -f $tmpfile } # Inject config from hook into local.conf function load_conf_hook { local hook="$1" $DSCONF merge_lc $LOCAL_CONF $GATE_HOOKS/$hook } # Tweak gate configuration for our rally scenarios function load_rc_for_rally { for file in $(ls $RALLY_EXTRA_DIR/*.setup); do tmpfile=$(tempfile) config=$(cat $file) echo "[[local|localrc]]" > $tmpfile $DSCONF setlc_raw $tmpfile "$config" $DSCONF merge_lc $LOCAL_CONF $tmpfile rm -f $tmpfile done } case $VENV in "dsvm-functional"|"dsvm-fullstack"|"dsvm-functional-python35"|"dsvm-fullstack-python35") # The following need to be set before sourcing # configure_for_func_testing. GATE_STACK_USER=stack PROJECT_NAME=neutron IS_GATE=True LOCAL_CONF=$DEVSTACK_PATH/local.conf source $DEVSTACK_PATH/functions source $NEUTRON_PATH/devstack/lib/ovs source $NEUTRON_PATH/tools/configure_for_func_testing.sh configure_host_for_func_testing # Because of bug present in current Ubuntu Xenial kernel version # we need a fix for VXLAN local tunneling. if [[ "$VENV" =~ "dsvm-fullstack" ]]; then # The OVS_BRANCH variable is used by git checkout. In the case below, # we use openvswitch commit 175be4bf23a206b264719b5661707af186b31f32 # that contains a fix for usage of VXLAN tunnels on a single node # (commit 741f47cf35df2bfc7811b2cff75c9bb8d05fd26f) and is compatible # with kernel 4.4.0-145 # NOTE(slaweq): Replace with a release tag when one is available. # See commit 138df3e563de9da0e5a4155b3534a69621495742 (on the ovs repo). OVS_BRANCH="175be4bf23a206b264719b5661707af186b31f32" compile_ovs_kernel_module elif [[ "$VENV" =~ "dsvm-functional" ]]; then # NOTE(slaweq): there is some bug in keepalived # 1:1.2.24-1ubuntu0.16.04.1, and because of that we have to use older # version for tests as workaround. For details check # https://bugs.launchpad.net/neutron/+bug/1788185 # https://bugs.launchpad.net/ubuntu/+source/keepalived/+bug/1789045 sudo apt-get install -y --allow-downgrades keepalived=1:1.2.19-1 fi # prepare base environment for ./stack.sh load_rc_hook stack_base # enable monitoring load_rc_hook dstat ;; "api"|"api-pecan"|"full-ovsfw"|"full-pecan"|"dsvm-scenario-ovs"|"dsvm-scenario-linuxbridge") # TODO(ihrachys) consider feeding result of ext-list into tempest.conf load_rc_hook api_all_extensions if [ "${FLAVOR}" = "dvrskip" ]; then load_rc_hook disable_dvr_tests fi load_conf_hook quotas load_rc_hook dns load_rc_hook qos load_rc_hook segments load_rc_hook trunk load_conf_hook vlan_provider load_conf_hook osprofiler load_conf_hook availability_zone if [[ "$VENV" =~ "dsvm-scenario" ]]; then load_rc_hook ubuntu_image fi if [[ "$VENV" =~ "dsvm-scenario-linuxbridge" ]]; then load_conf_hook iptables_verify fi if [[ "$VENV" =~ "pecan" ]]; then load_conf_hook pecan fi if [[ "$VENV" =~ "ovs" ]]; then load_conf_hook ovsfw fi if [[ "$VENV" != "dsvm-scenario-linuxbridge" ]]; then load_conf_hook tunnel_types load_rc_hook log # bug 1743463 fi if [[ "$VENV" =~ "dsvm-scenario-linuxbridge" ]]; then # linuxbridge doesn't support gre load_conf_hook linuxbridge_type_drivers else load_conf_hook openvswitch_type_drivers fi if [[ "$FLAVOR" = "dvrskip" ]]; then load_conf_hook disable_dvr fi ;; "rally") load_rc_for_rally ;; *) echo "Unrecognized environment $VENV". exit 1 esac export DEVSTACK_LOCALCONF=$(cat $LOCAL_CONF) $BASE/new/devstack-gate/devstack-vm-gate.sh neutron-12.1.1/neutron/tests/contrib/README0000664000175000017500000000020413553660046020470 0ustar zuulzuul00000000000000The files in this directory are intended for use by the Neutron infra jobs that run the various functional test suites in the gate. neutron-12.1.1/neutron/tests/tempest/0000775000175000017500000000000013553660156017637 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/services/0000775000175000017500000000000013553660156021462 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/services/__init__.py0000664000175000017500000000000013553660047023560 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/services/network/0000775000175000017500000000000013553660156023153 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/services/network/json/0000775000175000017500000000000013553660156024124 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/services/network/json/network_client.py0000664000175000017500000012002613553660047027525 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import time from oslo_serialization import jsonutils from six.moves.urllib import parse as urlparse from tempest.lib.common import rest_client as service_client from tempest.lib import exceptions as lib_exc from neutron.tests.tempest import exceptions class NetworkClientJSON(service_client.RestClient): """ Tempest REST client for Neutron. Uses v2 of the Neutron API, since the V1 API has been removed from the code base. Implements create, delete, update, list and show for the basic Neutron abstractions (networks, sub-networks, routers, ports and floating IP): Implements add/remove interface to router using subnet ID / port ID It also implements list, show, update and reset for OpenStack Networking quotas """ version = '2.0' uri_prefix = "v2.0" def get_uri(self, plural_name): # get service prefix from resource name # The following list represents resource names that do not require # changing underscore to a hyphen hyphen_exceptions = ["service_profiles"] # the following map is used to construct proper URI # for the given neutron resource service_resource_prefix_map = { 'networks': '', 'subnets': '', 'subnetpools': '', 'ports': '', 'metering_labels': 'metering', 'metering_label_rules': 'metering', 'policies': 'qos', 'bandwidth_limit_rules': 'qos', 'minimum_bandwidth_rules': 'qos', 'rule_types': 'qos', 'rbac-policies': '', } service_prefix = service_resource_prefix_map.get( plural_name) if plural_name not in hyphen_exceptions: plural_name = plural_name.replace("_", "-") if service_prefix: uri = '%s/%s/%s' % (self.uri_prefix, service_prefix, plural_name) else: uri = '%s/%s' % (self.uri_prefix, plural_name) return uri def build_uri(self, plural_name, **kwargs): uri = self.get_uri(plural_name) if kwargs: uri += '?' + urlparse.urlencode(kwargs, doseq=1) return uri def pluralize(self, resource_name): # get plural from map or just add 's' # map from resource name to a plural name # needed only for those which can't be constructed as name + 's' resource_plural_map = { 'security_groups': 'security_groups', 'security_group_rules': 'security_group_rules', 'quotas': 'quotas', 'qos_policy': 'policies', 'rbac_policy': 'rbac_policies', 'network_ip_availability': 'network_ip_availabilities', } return resource_plural_map.get(resource_name, resource_name + 's') def get_uri_with_links(self, plural_name, uri): resp, body = self.get(uri) result = {plural_name: self.deserialize_list(body)} links = self.deserialize_links(body) self.expected_success(200, resp.status) return links, service_client.ResponseBody(resp, result) def _lister(self, plural_name): def _list(**filters): uri = self.build_uri(plural_name, **filters) resp, body = self.get(uri) result = {plural_name: self.deserialize_list(body)} self.expected_success(200, resp.status) return service_client.ResponseBody(resp, result) return _list def _deleter(self, resource_name): def _delete(resource_id): plural = self.pluralize(resource_name) uri = '%s/%s' % (self.get_uri(plural), resource_id) resp, body = self.delete(uri) self.expected_success(204, resp.status) return service_client.ResponseBody(resp, body) return _delete def _shower(self, resource_name): def _show(resource_id, **fields): # fields is a dict which key is 'fields' and value is a # list of field's name. An example: # {'fields': ['id', 'name']} plural = self.pluralize(resource_name) if 'details_quotas' in plural: details, plural = plural.split('_') uri = '%s/%s/%s' % (self.get_uri(plural), resource_id, details) else: uri = '%s/%s' % (self.get_uri(plural), resource_id) if fields: uri += '?' + urlparse.urlencode(fields, doseq=1) resp, body = self.get(uri) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) return _show def _creater(self, resource_name): def _create(**kwargs): plural = self.pluralize(resource_name) uri = self.get_uri(plural) post_data = self.serialize({resource_name: kwargs}) resp, body = self.post(uri, post_data) body = self.deserialize_single(body) self.expected_success(201, resp.status) return service_client.ResponseBody(resp, body) return _create def _updater(self, resource_name): def _update(res_id, **kwargs): headers = kwargs.pop('headers', {}) plural = self.pluralize(resource_name) uri = '%s/%s' % (self.get_uri(plural), res_id) post_data = self.serialize({resource_name: kwargs}) resp, body = self.put(uri, post_data, headers=headers) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) return _update def __getattr__(self, name): method_prefixes = ["list_", "delete_", "show_", "create_", "update_"] method_functors = [self._lister, self._deleter, self._shower, self._creater, self._updater] for index, prefix in enumerate(method_prefixes): prefix_len = len(prefix) if name[:prefix_len] == prefix: return method_functors[index](name[prefix_len:]) raise AttributeError(name) # Subnetpool methods def create_subnetpool(self, name, **kwargs): subnetpool_data = {'name': name} for arg in kwargs: subnetpool_data[arg] = kwargs[arg] post_data = {'subnetpool': subnetpool_data} body = self.serialize_list(post_data, "subnetpools", "subnetpool") uri = self.get_uri("subnetpools") resp, body = self.post(uri, body) body = {'subnetpool': self.deserialize_list(body)} self.expected_success(201, resp.status) return service_client.ResponseBody(resp, body) def get_subnetpool(self, id): uri = self.get_uri("subnetpools") subnetpool_uri = '%s/%s' % (uri, id) resp, body = self.get(subnetpool_uri) body = {'subnetpool': self.deserialize_list(body)} self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def delete_subnetpool(self, id): uri = self.get_uri("subnetpools") subnetpool_uri = '%s/%s' % (uri, id) resp, body = self.delete(subnetpool_uri) self.expected_success(204, resp.status) return service_client.ResponseBody(resp, body) def list_subnetpools(self, **filters): uri = self.get_uri("subnetpools") if filters: uri = '?'.join([uri, urlparse.urlencode(filters)]) resp, body = self.get(uri) body = {'subnetpools': self.deserialize_list(body)} self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def update_subnetpool(self, id, **kwargs): subnetpool_data = {} for arg in kwargs: subnetpool_data[arg] = kwargs[arg] post_data = {'subnetpool': subnetpool_data} body = self.serialize_list(post_data, "subnetpools", "subnetpool") uri = self.get_uri("subnetpools") subnetpool_uri = '%s/%s' % (uri, id) resp, body = self.put(subnetpool_uri, body) body = {'subnetpool': self.deserialize_list(body)} self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) # Common methods that are hard to automate def create_bulk_network(self, names, shared=False): network_list = [{'name': name, 'shared': shared} for name in names] post_data = {'networks': network_list} body = self.serialize_list(post_data, "networks", "network") uri = self.get_uri("networks") resp, body = self.post(uri, body) body = {'networks': self.deserialize_list(body)} self.expected_success(201, resp.status) return service_client.ResponseBody(resp, body) def create_bulk_subnet(self, subnet_list): post_data = {'subnets': subnet_list} body = self.serialize_list(post_data, 'subnets', 'subnet') uri = self.get_uri('subnets') resp, body = self.post(uri, body) body = {'subnets': self.deserialize_list(body)} self.expected_success(201, resp.status) return service_client.ResponseBody(resp, body) def create_bulk_port(self, port_list): post_data = {'ports': port_list} body = self.serialize_list(post_data, 'ports', 'port') uri = self.get_uri('ports') resp, body = self.post(uri, body) body = {'ports': self.deserialize_list(body)} self.expected_success(201, resp.status) return service_client.ResponseBody(resp, body) def create_bulk_security_groups(self, security_group_list): group_list = [{'security_group': {'name': name}} for name in security_group_list] post_data = {'security_groups': group_list} body = self.serialize_list(post_data, 'security_groups', 'security_group') uri = self.get_uri("security-groups") resp, body = self.post(uri, body) body = {'security_groups': self.deserialize_list(body)} self.expected_success(201, resp.status) return service_client.ResponseBody(resp, body) def wait_for_resource_deletion(self, resource_type, id): """Waits for a resource to be deleted.""" start_time = int(time.time()) while True: if self.is_resource_deleted(resource_type, id): return if int(time.time()) - start_time >= self.build_timeout: raise exceptions.TimeoutException time.sleep(self.build_interval) def is_resource_deleted(self, resource_type, id): method = 'show_' + resource_type try: getattr(self, method)(id) except AttributeError: raise Exception("Unknown resource type %s " % resource_type) except lib_exc.NotFound: return True return False def deserialize_single(self, body): return jsonutils.loads(body) def deserialize_list(self, body): res = jsonutils.loads(body) # expecting response in form # {'resources': [ res1, res2] } => when pagination disabled # {'resources': [..], 'resources_links': {}} => if pagination enabled for k in res.keys(): if k.endswith("_links"): continue return res[k] def deserialize_links(self, body): res = jsonutils.loads(body) # expecting response in form # {'resources': [ res1, res2] } => when pagination disabled # {'resources': [..], 'resources_links': {}} => if pagination enabled for k in res.keys(): if k.endswith("_links"): return { link['rel']: link['href'] for link in res[k] } return {} def serialize(self, data): return jsonutils.dumps(data) def serialize_list(self, data, root=None, item=None): return self.serialize(data) def update_quotas(self, tenant_id, **kwargs): put_body = {'quota': kwargs} body = jsonutils.dumps(put_body) uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id) resp, body = self.put(uri, body) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body['quota']) def reset_quotas(self, tenant_id): uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id) resp, body = self.delete(uri) self.expected_success(204, resp.status) return service_client.ResponseBody(resp, body) def create_router(self, name, admin_state_up=True, **kwargs): post_body = {'router': kwargs} post_body['router']['name'] = name post_body['router']['admin_state_up'] = admin_state_up body = jsonutils.dumps(post_body) uri = '%s/routers' % (self.uri_prefix) resp, body = self.post(uri, body) self.expected_success(201, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def _update_router(self, router_id, set_enable_snat, **kwargs): uri = '%s/routers/%s' % (self.uri_prefix, router_id) resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) update_body = {} update_body['name'] = kwargs.get('name', body['router']['name']) update_body['admin_state_up'] = kwargs.get( 'admin_state_up', body['router']['admin_state_up']) if 'description' in kwargs: update_body['description'] = kwargs['description'] cur_gw_info = body['router']['external_gateway_info'] if cur_gw_info: # TODO(kevinbenton): setting the external gateway info is not # allowed for a regular tenant. If the ability to update is also # merged, a test case for this will need to be added similar to # the SNAT case. cur_gw_info.pop('external_fixed_ips', None) if not set_enable_snat: cur_gw_info.pop('enable_snat', None) update_body['external_gateway_info'] = kwargs.get( 'external_gateway_info', body['router']['external_gateway_info']) if 'distributed' in kwargs: update_body['distributed'] = kwargs['distributed'] if 'ha' in kwargs: update_body['ha'] = kwargs['ha'] update_body = dict(router=update_body) update_body = jsonutils.dumps(update_body) resp, body = self.put(uri, update_body) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def update_router(self, router_id, **kwargs): """Update a router leaving enable_snat to its default value.""" # If external_gateway_info contains enable_snat the request will fail # with 404 unless executed with admin client, and therefore we instruct # _update_router to not set this attribute # NOTE(salv-orlando): The above applies as long as Neutron's default # policy is to restrict enable_snat usage to admins only. return self._update_router(router_id, set_enable_snat=False, **kwargs) def update_router_with_snat_gw_info(self, router_id, **kwargs): """Update a router passing also the enable_snat attribute. This method must be execute with admin credentials, otherwise the API call will return a 404 error. """ return self._update_router(router_id, set_enable_snat=True, **kwargs) def add_router_interface_with_subnet_id(self, router_id, subnet_id): uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix, router_id) update_body = {"subnet_id": subnet_id} update_body = jsonutils.dumps(update_body) resp, body = self.put(uri, update_body) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def add_router_interface_with_port_id(self, router_id, port_id): uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix, router_id) update_body = {"port_id": port_id} update_body = jsonutils.dumps(update_body) resp, body = self.put(uri, update_body) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def remove_router_interface_with_subnet_id(self, router_id, subnet_id): uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix, router_id) update_body = {"subnet_id": subnet_id} update_body = jsonutils.dumps(update_body) resp, body = self.put(uri, update_body) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def remove_router_interface_with_port_id(self, router_id, port_id): uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix, router_id) update_body = {"port_id": port_id} update_body = jsonutils.dumps(update_body) resp, body = self.put(uri, update_body) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def list_router_interfaces(self, uuid): uri = '%s/ports?device_id=%s' % (self.uri_prefix, uuid) resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def update_agent(self, agent_id, agent_info): """ :param agent_info: Agent update information. E.g {"admin_state_up": True} """ uri = '%s/agents/%s' % (self.uri_prefix, agent_id) agent = {"agent": agent_info} body = jsonutils.dumps(agent) resp, body = self.put(uri, body) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def list_routers_on_l3_agent(self, agent_id): uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id) resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def list_l3_agents_hosting_router(self, router_id): uri = '%s/routers/%s/l3-agents' % (self.uri_prefix, router_id) resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def add_router_to_l3_agent(self, agent_id, router_id): uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id) post_body = {"router_id": router_id} body = jsonutils.dumps(post_body) resp, body = self.post(uri, body) self.expected_success(201, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def remove_router_from_l3_agent(self, agent_id, router_id): uri = '%s/agents/%s/l3-routers/%s' % ( self.uri_prefix, agent_id, router_id) resp, body = self.delete(uri) self.expected_success(204, resp.status) return service_client.ResponseBody(resp, body) def list_dhcp_agent_hosting_network(self, network_id): uri = '%s/networks/%s/dhcp-agents' % (self.uri_prefix, network_id) resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def list_networks_hosted_by_one_dhcp_agent(self, agent_id): uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id) resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def remove_network_from_dhcp_agent(self, agent_id, network_id): uri = '%s/agents/%s/dhcp-networks/%s' % (self.uri_prefix, agent_id, network_id) resp, body = self.delete(uri) self.expected_success(204, resp.status) return service_client.ResponseBody(resp, body) def update_extra_routes(self, router_id, nexthop, destination): uri = '%s/routers/%s' % (self.uri_prefix, router_id) put_body = { 'router': { 'routes': [{'nexthop': nexthop, "destination": destination}] } } body = jsonutils.dumps(put_body) resp, body = self.put(uri, body) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def delete_extra_routes(self, router_id): uri = '%s/routers/%s' % (self.uri_prefix, router_id) null_routes = None put_body = { 'router': { 'routes': null_routes } } body = jsonutils.dumps(put_body) resp, body = self.put(uri, body) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def add_dhcp_agent_to_network(self, agent_id, network_id): post_body = {'network_id': network_id} body = jsonutils.dumps(post_body) uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id) resp, body = self.post(uri, body) self.expected_success(201, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def list_qos_policies(self, **filters): if filters: uri = '%s/qos/policies?%s' % (self.uri_prefix, urlparse.urlencode(filters)) else: uri = '%s/qos/policies' % self.uri_prefix resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def create_qos_policy(self, name, description=None, shared=False, tenant_id=None, is_default=False): uri = '%s/qos/policies' % self.uri_prefix post_data = { 'policy': { 'name': name, 'shared': shared, 'is_default': is_default } } if description is not None: post_data['policy']['description'] = description if tenant_id is not None: post_data['policy']['tenant_id'] = tenant_id resp, body = self.post(uri, self.serialize(post_data)) body = self.deserialize_single(body) self.expected_success(201, resp.status) return service_client.ResponseBody(resp, body) def update_qos_policy(self, policy_id, **kwargs): uri = '%s/qos/policies/%s' % (self.uri_prefix, policy_id) post_data = self.serialize({'policy': kwargs}) resp, body = self.put(uri, post_data) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def create_bandwidth_limit_rule(self, policy_id, max_kbps, max_burst_kbps, direction=None): uri = '%s/qos/policies/%s/bandwidth_limit_rules' % ( self.uri_prefix, policy_id) post_data = { 'bandwidth_limit_rule': { 'max_kbps': max_kbps, 'max_burst_kbps': max_burst_kbps } } if direction: post_data['bandwidth_limit_rule']['direction'] = direction resp, body = self.post(uri, self.serialize(post_data)) self.expected_success(201, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def list_bandwidth_limit_rules(self, policy_id): uri = '%s/qos/policies/%s/bandwidth_limit_rules' % ( self.uri_prefix, policy_id) resp, body = self.get(uri) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def show_bandwidth_limit_rule(self, policy_id, rule_id): uri = '%s/qos/policies/%s/bandwidth_limit_rules/%s' % ( self.uri_prefix, policy_id, rule_id) resp, body = self.get(uri) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def update_bandwidth_limit_rule(self, policy_id, rule_id, **kwargs): uri = '%s/qos/policies/%s/bandwidth_limit_rules/%s' % ( self.uri_prefix, policy_id, rule_id) if "direction" in kwargs and kwargs['direction'] is None: kwargs.pop('direction') post_data = {'bandwidth_limit_rule': kwargs} resp, body = self.put(uri, jsonutils.dumps(post_data)) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def delete_bandwidth_limit_rule(self, policy_id, rule_id): uri = '%s/qos/policies/%s/bandwidth_limit_rules/%s' % ( self.uri_prefix, policy_id, rule_id) resp, body = self.delete(uri) self.expected_success(204, resp.status) return service_client.ResponseBody(resp, body) def create_dscp_marking_rule(self, policy_id, dscp_mark): uri = '%s/qos/policies/%s/dscp_marking_rules' % ( self.uri_prefix, policy_id) post_data = self.serialize({ 'dscp_marking_rule': { 'dscp_mark': dscp_mark } }) resp, body = self.post(uri, post_data) self.expected_success(201, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def list_dscp_marking_rules(self, policy_id): uri = '%s/qos/policies/%s/dscp_marking_rules' % ( self.uri_prefix, policy_id) resp, body = self.get(uri) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def show_dscp_marking_rule(self, policy_id, rule_id): uri = '%s/qos/policies/%s/dscp_marking_rules/%s' % ( self.uri_prefix, policy_id, rule_id) resp, body = self.get(uri) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def update_dscp_marking_rule(self, policy_id, rule_id, **kwargs): uri = '%s/qos/policies/%s/dscp_marking_rules/%s' % ( self.uri_prefix, policy_id, rule_id) post_data = {'dscp_marking_rule': kwargs} resp, body = self.put(uri, jsonutils.dumps(post_data)) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def delete_dscp_marking_rule(self, policy_id, rule_id): uri = '%s/qos/policies/%s/dscp_marking_rules/%s' % ( self.uri_prefix, policy_id, rule_id) resp, body = self.delete(uri) self.expected_success(204, resp.status) return service_client.ResponseBody(resp, body) def create_minimum_bandwidth_rule(self, policy_id, direction, min_kbps=None): uri = '%s/qos/policies/%s/minimum_bandwidth_rules' % ( self.uri_prefix, policy_id) data = { 'direction': direction, } if min_kbps is not None: data['min_kbps'] = min_kbps post_data = self.serialize({'minimum_bandwidth_rule': data}) resp, body = self.post(uri, post_data) self.expected_success(201, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def list_minimum_bandwidth_rules(self, policy_id): uri = '%s/qos/policies/%s/minimum_bandwidth_rules' % ( self.uri_prefix, policy_id) resp, body = self.get(uri) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def show_minimum_bandwidth_rule(self, policy_id, rule_id): uri = '%s/qos/policies/%s/minimum_bandwidth_rules/%s' % ( self.uri_prefix, policy_id, rule_id) resp, body = self.get(uri) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def update_minimum_bandwidth_rule(self, policy_id, rule_id, **kwargs): uri = '%s/qos/policies/%s/minimum_bandwidth_rules/%s' % ( self.uri_prefix, policy_id, rule_id) post_data = {'minimum_bandwidth_rule': kwargs} resp, body = self.put(uri, jsonutils.dumps(post_data)) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def delete_minimum_bandwidth_rule(self, policy_id, rule_id): uri = '%s/qos/policies/%s/minimum_bandwidth_rules/%s' % ( self.uri_prefix, policy_id, rule_id) resp, body = self.delete(uri) self.expected_success(204, resp.status) return service_client.ResponseBody(resp, body) def list_qos_rule_types(self): uri = '%s/qos/rule-types' % self.uri_prefix resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def show_qos_rule_type(self, rule_type_name): uri = '%s/qos/rule-types/%s' % ( self.uri_prefix, rule_type_name) resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def create_trunk(self, parent_port_id, subports, tenant_id=None, name=None, admin_state_up=None, description=None): uri = '%s/trunks' % self.uri_prefix post_data = { 'trunk': { 'port_id': parent_port_id, } } if subports is not None: post_data['trunk']['sub_ports'] = subports if tenant_id is not None: post_data['trunk']['tenant_id'] = tenant_id if name is not None: post_data['trunk']['name'] = name if description is not None: post_data['trunk']['description'] = description if admin_state_up is not None: post_data['trunk']['admin_state_up'] = admin_state_up resp, body = self.post(uri, self.serialize(post_data)) body = self.deserialize_single(body) self.expected_success(201, resp.status) return service_client.ResponseBody(resp, body) def update_trunk(self, trunk_id, **kwargs): put_body = {'trunk': kwargs} body = jsonutils.dumps(put_body) uri = '%s/trunks/%s' % (self.uri_prefix, trunk_id) resp, body = self.put(uri, body) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def show_trunk(self, trunk_id): uri = '%s/trunks/%s' % (self.uri_prefix, trunk_id) resp, body = self.get(uri) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def list_trunks(self, **kwargs): uri = '%s/trunks' % self.uri_prefix if kwargs: uri += '?' + urlparse.urlencode(kwargs, doseq=1) resp, body = self.get(uri) self.expected_success(200, resp.status) body = self.deserialize_single(body) return service_client.ResponseBody(resp, body) def delete_trunk(self, trunk_id): uri = '%s/trunks/%s' % (self.uri_prefix, trunk_id) resp, body = self.delete(uri) self.expected_success(204, resp.status) return service_client.ResponseBody(resp, body) def _subports_action(self, action, trunk_id, subports): uri = '%s/trunks/%s/%s' % (self.uri_prefix, trunk_id, action) resp, body = self.put(uri, jsonutils.dumps({'sub_ports': subports})) body = self.deserialize_single(body) self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def add_subports(self, trunk_id, subports): return self._subports_action('add_subports', trunk_id, subports) def remove_subports(self, trunk_id, subports): return self._subports_action('remove_subports', trunk_id, subports) def get_subports(self, trunk_id): uri = '%s/trunks/%s/%s' % (self.uri_prefix, trunk_id, 'get_subports') resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def get_auto_allocated_topology(self, tenant_id=None): uri = '%s/auto-allocated-topology/%s' % (self.uri_prefix, tenant_id) resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def delete_auto_allocated_topology(self, tenant_id=None): uri = '%s/auto-allocated-topology/%s' % (self.uri_prefix, tenant_id) resp, body = self.delete(uri) self.expected_success(204, resp.status) return service_client.ResponseBody(resp, body) def create_flavor_service_profile(self, flavor_id, service_profile_id): body = jsonutils.dumps({'service_profile': {'id': service_profile_id}}) uri = '%s/flavors/%s/service_profiles' % (self.uri_prefix, flavor_id) resp, body = self.post(uri, body) self.expected_success(201, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def list_flavor_service_profiles(self, flavor_id): uri = '%s/flavors/%s/service_profiles' % (self.uri_prefix, flavor_id) resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def delete_flavor_service_profile(self, flavor_id, service_profile_id): uri = '%s/flavors/%s/service_profiles/%s' % (self.uri_prefix, flavor_id, service_profile_id) resp, body = self.delete(uri) self.expected_success(204, resp.status) return service_client.ResponseBody(resp, body) def create_security_group_rule(self, direction, security_group_id, **kwargs): post_body = {'security_group_rule': kwargs} post_body['security_group_rule']['direction'] = direction post_body['security_group_rule'][ 'security_group_id'] = security_group_id body = jsonutils.dumps(post_body) uri = '%s/security-group-rules' % self.uri_prefix resp, body = self.post(uri, body) self.expected_success(201, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def list_security_groups(self, **kwargs): post_body = {'security_groups': kwargs} body = jsonutils.dumps(post_body) uri = '%s/security-groups' % self.uri_prefix if kwargs: uri += '?' + urlparse.urlencode(kwargs, doseq=1) resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def delete_security_group(self, security_group_id): uri = '%s/security-groups/%s' % ( self.uri_prefix, security_group_id) resp, body = self.delete(uri) self.expected_success(204, resp.status) return service_client.ResponseBody(resp, body) def list_ports(self, **kwargs): post_body = {'ports': kwargs} body = jsonutils.dumps(post_body) uri = '%s/ports' % self.uri_prefix if kwargs: uri += '?' + urlparse.urlencode(kwargs, doseq=1) resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def create_floatingip(self, floating_network_id, **kwargs): post_body = {'floatingip': { 'floating_network_id': floating_network_id}} if kwargs: post_body['floatingip'].update(kwargs) body = jsonutils.dumps(post_body) uri = '%s/floatingips' % self.uri_prefix resp, body = self.post(uri, body) self.expected_success(201, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def create_network_keystone_v3(self, name, project_id, tenant_id=None): uri = '%s/networks' % self.uri_prefix post_data = { 'network': { 'name': name, 'project_id': project_id } } if tenant_id is not None: post_data['network']['tenant_id'] = tenant_id resp, body = self.post(uri, self.serialize(post_data)) body = self.deserialize_single(body) self.expected_success(201, resp.status) return service_client.ResponseBody(resp, body) def list_extensions(self, **filters): uri = self.get_uri("extensions") if filters: uri = '?'.join([uri, urlparse.urlencode(filters)]) resp, body = self.get(uri) body = {'extensions': self.deserialize_list(body)} self.expected_success(200, resp.status) return service_client.ResponseBody(resp, body) def get_tags(self, resource_type, resource_id): uri = '%s/%s/%s/tags' % ( self.uri_prefix, resource_type, resource_id) resp, body = self.get(uri) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def get_tag(self, resource_type, resource_id, tag): uri = '%s/%s/%s/tags/%s' % ( self.uri_prefix, resource_type, resource_id, tag) resp, body = self.get(uri) self.expected_success(204, resp.status) def update_tag(self, resource_type, resource_id, tag): uri = '%s/%s/%s/tags/%s' % ( self.uri_prefix, resource_type, resource_id, tag) resp, body = self.put(uri, None) self.expected_success(201, resp.status) def update_tags(self, resource_type, resource_id, tags): uri = '%s/%s/%s/tags' % ( self.uri_prefix, resource_type, resource_id) req_body = jsonutils.dumps({'tags': tags}) resp, body = self.put(uri, req_body) self.expected_success(200, resp.status) body = jsonutils.loads(body) return service_client.ResponseBody(resp, body) def delete_tags(self, resource_type, resource_id): uri = '%s/%s/%s/tags' % ( self.uri_prefix, resource_type, resource_id) resp, body = self.delete(uri) self.expected_success(204, resp.status) def delete_tag(self, resource_type, resource_id, tag): uri = '%s/%s/%s/tags/%s' % ( self.uri_prefix, resource_type, resource_id, tag) resp, body = self.delete(uri) self.expected_success(204, resp.status) neutron-12.1.1/neutron/tests/tempest/services/network/json/__init__.py0000664000175000017500000000000013553660047026222 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/services/network/__init__.py0000664000175000017500000000000013553660047025251 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/exceptions.py0000664000175000017500000000171013553660047022370 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest.lib import exceptions TempestException = exceptions.TempestException class InvalidConfiguration(TempestException): message = "Invalid Configuration" class InvalidCredentials(TempestException): message = "Invalid Credentials" class InvalidServiceTag(TempestException): message = "Invalid service tag" neutron-12.1.1/neutron/tests/tempest/scenario/0000775000175000017500000000000013553660156021442 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/scenario/exceptions.py0000664000175000017500000000223013553660047024171 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest.lib import exceptions TempestException = exceptions.TempestException class QoSLimitReached(TempestException): message = "Limit reached, limit = %(limit)d" class SocketConnectionRefused(TempestException): message = "Unable to connect to %(host)s port %(port)d:Connection Refused" class ConnectionTimeoutException(TempestException): message = "Timeout connecting to %(host)s port %(port)d" class FileCreationFailedException(TempestException): message = "File %(file)s has not been created or has the wrong size" neutron-12.1.1/neutron/tests/tempest/scenario/constants.py0000664000175000017500000000130113553660047024022 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. SERVER_STATUS_ACTIVE = 'ACTIVE' DEFAULT_SECURITY_GROUP = 'default' LIMIT_KILO_BITS_PER_SECOND = 1000 SOCKET_CONNECT_TIMEOUT = 60 neutron-12.1.1/neutron/tests/tempest/scenario/base.py0000664000175000017500000003211613553660047022730 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import subprocess import netaddr from oslo_log import log from tempest.common.utils import net_utils from tempest.common import waiters from tempest.lib.common.utils import data_utils from tempest.lib.common.utils import test_utils from tempest.lib import exceptions as lib_exc from neutron.tests.tempest.api import base as base_api from neutron.tests.tempest.common import ssh from neutron.tests.tempest import config from neutron.tests.tempest.scenario import constants CONF = config.CONF LOG = log.getLogger(__name__) class BaseTempestTestCase(base_api.BaseNetworkTest): @classmethod def resource_setup(cls): super(BaseTempestTestCase, cls).resource_setup() cls.keypairs = [] @classmethod def resource_cleanup(cls): for keypair in cls.keypairs: cls.os_primary.keypairs_client.delete_keypair( keypair_name=keypair['name']) super(BaseTempestTestCase, cls).resource_cleanup() def create_server(self, flavor_ref, image_ref, key_name, networks, name=None, security_groups=None): """Create a server using tempest lib All the parameters are the ones used in Compute API Args: flavor_ref(str): The flavor of the server to be provisioned. image_ref(str): The image of the server to be provisioned. key_name(str): SSH key to to be used to connect to the provisioned server. networks(list): List of dictionaries where each represent an interface to be attached to the server. For network it should be {'uuid': network_uuid} and for port it should be {'port': port_uuid} name(str): Name of the server to be provisioned. security_groups(list): List of dictionaries where the keys is 'name' and the value is the name of the security group. If it's not passed the default security group will be used. """ name = name or data_utils.rand_name('server-test') if not security_groups: security_groups = [{'name': 'default'}] server = self.os_primary.servers_client.create_server( name=name, flavorRef=flavor_ref, imageRef=image_ref, key_name=key_name, networks=networks, security_groups=security_groups) self.addCleanup(test_utils.call_and_ignore_notfound_exc, waiters.wait_for_server_termination, self.os_primary.servers_client, server['server']['id']) self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.os_primary.servers_client.delete_server, server['server']['id']) return server @classmethod def create_keypair(cls, client=None): client = client or cls.os_primary.keypairs_client name = data_utils.rand_name('keypair-test') body = client.create_keypair(name=name) cls.keypairs.append(body['keypair']) return body['keypair'] @classmethod def create_secgroup_rules(cls, rule_list, secgroup_id=None): client = cls.os_primary.network_client if not secgroup_id: sgs = client.list_security_groups()['security_groups'] for sg in sgs: if sg['name'] == constants.DEFAULT_SECURITY_GROUP: secgroup_id = sg['id'] break for rule in rule_list: direction = rule.pop('direction') client.create_security_group_rule( direction=direction, security_group_id=secgroup_id, **rule) @classmethod def create_loginable_secgroup_rule(cls, secgroup_id=None): """This rule is intended to permit inbound ssh Allowing ssh traffic traffic from all sources, so no group_id is provided. Setting a group_id would only permit traffic from ports belonging to the same security group. """ rule_list = [{'protocol': 'tcp', 'direction': 'ingress', 'port_range_min': 22, 'port_range_max': 22, 'remote_ip_prefix': '0.0.0.0/0'}] cls.create_secgroup_rules(rule_list, secgroup_id=secgroup_id) @classmethod def create_pingable_secgroup_rule(cls, secgroup_id=None): """This rule is intended to permit inbound ping """ rule_list = [{'protocol': 'icmp', 'direction': 'ingress', 'port_range_min': 8, # type 'port_range_max': 0, # code 'remote_ip_prefix': '0.0.0.0/0'}] cls.create_secgroup_rules(rule_list, secgroup_id=secgroup_id) @classmethod def create_router_by_client(cls, is_admin=False, **kwargs): kwargs.update({'router_name': data_utils.rand_name('router'), 'admin_state_up': True, 'external_network_id': CONF.network.public_network_id}) if not is_admin: router = cls.create_router(**kwargs) else: router = cls.create_admin_router(**kwargs) LOG.debug("Created router %s", router['name']) cls.routers.append(router) return router def create_and_associate_floatingip(self, port_id): fip = self.os_primary.network_client.create_floatingip( CONF.network.public_network_id, port_id=port_id)['floatingip'] self.floating_ips.append(fip) return fip def setup_network_and_server(self, router=None, **kwargs): """Create network resources and a server. Creating a network, subnet, router, keypair, security group and a server. """ self.network = self.create_network() LOG.debug("Created network %s", self.network['name']) self.subnet = self.create_subnet(self.network) LOG.debug("Created subnet %s", self.subnet['id']) secgroup = self.os_primary.network_client.create_security_group( name=data_utils.rand_name('secgroup')) LOG.debug("Created security group %s", secgroup['security_group']['name']) self.security_groups.append(secgroup['security_group']) if not router: router = self.create_router_by_client(**kwargs) self.create_router_interface(router['id'], self.subnet['id']) self.keypair = self.create_keypair() self.create_loginable_secgroup_rule( secgroup_id=secgroup['security_group']['id']) self.server = self.create_server( flavor_ref=CONF.compute.flavor_ref, image_ref=CONF.compute.image_ref, key_name=self.keypair['name'], networks=[{'uuid': self.network['id']}], security_groups=[{'name': secgroup['security_group']['name']}]) waiters.wait_for_server_status(self.os_primary.servers_client, self.server['server']['id'], constants.SERVER_STATUS_ACTIVE) self.port = self.client.list_ports(network_id=self.network['id'], device_id=self.server[ 'server']['id'])['ports'][0] self.fip = self.create_and_associate_floatingip(self.port['id']) def check_connectivity(self, host, ssh_user, ssh_key, servers=None): ssh_client = ssh.Client(host, ssh_user, pkey=ssh_key) try: ssh_client.test_connection_auth() except lib_exc.SSHTimeout as ssh_e: LOG.debug(ssh_e) self._log_console_output(servers) raise def _log_console_output(self, servers=None): if not CONF.compute_feature_enabled.console_output: LOG.debug('Console output not supported, cannot log') return if not servers: servers = self.os_primary.servers_client.list_servers() servers = servers['servers'] for server in servers: try: console_output = ( self.os_primary.servers_client.get_console_output( server['id'])['output']) LOG.debug('Console output for %s\nbody=\n%s', server['id'], console_output) except lib_exc.NotFound: LOG.debug("Server %s disappeared(deleted) while looking " "for the console log", server['id']) def _check_remote_connectivity(self, source, dest, should_succeed=True, nic=None, mtu=None, fragmentation=True): """check ping server via source ssh connection :param source: RemoteClient: an ssh connection from which to ping :param dest: and IP to ping against :param should_succeed: boolean should ping succeed or not :param nic: specific network interface to ping from :param mtu: mtu size for the packet to be sent :param fragmentation: Flag for packet fragmentation :returns: boolean -- should_succeed == ping :returns: ping is false if ping failed """ def ping_host(source, host, count=CONF.validation.ping_count, size=CONF.validation.ping_size, nic=None, mtu=None, fragmentation=True): addr = netaddr.IPAddress(host) cmd = 'ping6' if addr.version == 6 else 'ping' if nic: cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic) if mtu: if not fragmentation: cmd += ' -M do' size = str(net_utils.get_ping_payload_size( mtu=mtu, ip_version=addr.version)) cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host) return source.exec_command(cmd) def ping_remote(): try: result = ping_host(source, dest, nic=nic, mtu=mtu, fragmentation=fragmentation) except lib_exc.SSHExecCommandFailed: LOG.warning('Failed to ping IP: %s via a ssh connection ' 'from: %s.', dest, source.host) return not should_succeed LOG.debug('ping result: %s', result) # Assert that the return traffic was from the correct # source address. from_source = 'from %s' % dest self.assertIn(from_source, result) return should_succeed return test_utils.call_until_true(ping_remote, CONF.validation.ping_timeout, 1) def check_remote_connectivity(self, source, dest, should_succeed=True, nic=None, mtu=None, fragmentation=True): self.assertTrue(self._check_remote_connectivity( source, dest, should_succeed, nic, mtu, fragmentation)) def ping_ip_address(self, ip_address, should_succeed=True, ping_timeout=None, mtu=None): # the code is taken from tempest/scenario/manager.py in tempest git timeout = ping_timeout or CONF.validation.ping_timeout cmd = ['ping', '-c1', '-w1'] if mtu: cmd += [ # don't fragment '-M', 'do', # ping receives just the size of ICMP payload '-s', str(net_utils.get_ping_payload_size(mtu, 4)) ] cmd.append(ip_address) def ping(): proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc.communicate() return (proc.returncode == 0) == should_succeed caller = test_utils.find_test_caller() LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the' ' expected result is %(should_succeed)s', { 'caller': caller, 'ip': ip_address, 'timeout': timeout, 'should_succeed': 'reachable' if should_succeed else 'unreachable' }) result = test_utils.call_until_true(ping, timeout, 1) LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the ' 'ping result is %(result)s', { 'caller': caller, 'ip': ip_address, 'timeout': timeout, 'result': 'expected' if result else 'unexpected' }) return result neutron-12.1.1/neutron/tests/tempest/scenario/__init__.py0000664000175000017500000000000013553660047023540 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/config.py0000664000175000017500000000465013553660047021462 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from tempest import config CONF = config.CONF NeutronPluginOptions = [ cfg.ListOpt('provider_vlans', default=[], help='List of provider networks available in the deployment.'), cfg.BoolOpt('specify_floating_ip_address_available', default=True, help='Allow passing an IP Address of the floating ip when ' 'creating the floating ip'), cfg.ListOpt('available_type_drivers', default=[], help='List of network types available to neutron, ' 'e.g. vxlan,vlan,gre.'), cfg.BoolOpt('image_is_advanced', default=False, help='Image that supports features that cirros does not, like' ' Ubuntu or CentOS supporting advanced features'), cfg.StrOpt('agent_availability_zone', help='The availability zone for all agents in the deployment. ' 'Configure this only when the single value is used by ' 'all agents in the deployment.'), ] # TODO(amuller): Redo configuration options registration as part of the planned # transition to the Tempest plugin architecture for opt in NeutronPluginOptions: CONF.register_opt(opt, 'neutron_plugin_options') config_opts_translator = { 'project_network_cidr': 'tenant_network_cidr', 'project_network_v6_cidr': 'tenant_network_v6_cidr', 'project_network_mask_bits': 'tenant_network_mask_bits', 'project_network_v6_mask_bits': 'tenant_network_v6_mask_bits'} def safe_get_config_value(group, name): """Safely get Oslo config opts from Tempest, using old and new names.""" conf_group = getattr(CONF, group) try: return getattr(conf_group, name) except cfg.NoSuchOptError: return getattr(conf_group, config_opts_translator[name]) neutron-12.1.1/neutron/tests/tempest/__init__.py0000664000175000017500000000000013553660047021735 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/api/0000775000175000017500000000000013553660156020410 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/api/base.py0000664000175000017500000007624313553660047021707 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import math import netaddr from neutron_lib import constants as const from tempest.common import utils as tutils from tempest.lib.common.utils import data_utils from tempest.lib import exceptions as lib_exc from tempest import test from neutron.common import constants from neutron.common import utils from neutron.tests.tempest.api import clients from neutron.tests.tempest import config from neutron.tests.tempest import exceptions CONF = config.CONF class BaseNetworkTest(test.BaseTestCase): """ Base class for the Neutron tests that use the Tempest Neutron REST client Per the Neutron API Guide, API v1.x was removed from the source code tree (docs.openstack.org/api/openstack-network/2.0/content/Overview-d1e71.html) Therefore, v2.x of the Neutron API is assumed. It is also assumed that the following options are defined in the [network] section of etc/tempest.conf: project_network_cidr with a block of cidr's from which smaller blocks can be allocated for tenant networks project_network_mask_bits with the mask bits to be used to partition the block defined by tenant-network_cidr Finally, it is assumed that the following option is defined in the [service_available] section of etc/tempest.conf neutron as True """ force_tenant_isolation = False credentials = ['primary'] # Default to ipv4. _ip_version = 4 @classmethod def get_client_manager(cls, credential_type=None, roles=None, force_new=None): manager = super(BaseNetworkTest, cls).get_client_manager( credential_type=credential_type, roles=roles, force_new=force_new ) # Neutron uses a different clients manager than the one in the Tempest return clients.Manager(manager.credentials) @classmethod def skip_checks(cls): super(BaseNetworkTest, cls).skip_checks() if not CONF.service_available.neutron: raise cls.skipException("Neutron support is required") if cls._ip_version == 6 and not CONF.network_feature_enabled.ipv6: raise cls.skipException("IPv6 Tests are disabled.") for req_ext in getattr(cls, 'required_extensions', []): if not tutils.is_extension_enabled(req_ext, 'network'): msg = "%s extension not enabled." % req_ext raise cls.skipException(msg) @classmethod def setup_credentials(cls): # Create no network resources for these test. cls.set_network_resources() super(BaseNetworkTest, cls).setup_credentials() @classmethod def setup_clients(cls): super(BaseNetworkTest, cls).setup_clients() cls.client = cls.os_primary.network_client @classmethod def resource_setup(cls): super(BaseNetworkTest, cls).resource_setup() cls.networks = [] cls.admin_networks = [] cls.subnets = [] cls.admin_subnets = [] cls.ports = [] cls.routers = [] cls.floating_ips = [] cls.metering_labels = [] cls.service_profiles = [] cls.flavors = [] cls.metering_label_rules = [] cls.qos_rules = [] cls.qos_policies = [] cls.ethertype = "IPv" + str(cls._ip_version) cls.address_scopes = [] cls.admin_address_scopes = [] cls.subnetpools = [] cls.admin_subnetpools = [] cls.security_groups = [] cls.projects = [] @classmethod def resource_cleanup(cls): if CONF.service_available.neutron: # Clean up floating IPs for floating_ip in cls.floating_ips: cls._try_delete_resource(cls.client.delete_floatingip, floating_ip['id']) # Clean up routers for router in cls.routers: cls._try_delete_resource(cls.delete_router, router) # Clean up metering label rules for metering_label_rule in cls.metering_label_rules: cls._try_delete_resource( cls.admin_client.delete_metering_label_rule, metering_label_rule['id']) # Clean up metering labels for metering_label in cls.metering_labels: cls._try_delete_resource( cls.admin_client.delete_metering_label, metering_label['id']) # Clean up flavors for flavor in cls.flavors: cls._try_delete_resource( cls.admin_client.delete_flavor, flavor['id']) # Clean up service profiles for service_profile in cls.service_profiles: cls._try_delete_resource( cls.admin_client.delete_service_profile, service_profile['id']) # Clean up ports for port in cls.ports: cls._try_delete_resource(cls.client.delete_port, port['id']) # Clean up subnets for subnet in cls.subnets: cls._try_delete_resource(cls.client.delete_subnet, subnet['id']) # Clean up admin subnets for subnet in cls.admin_subnets: cls._try_delete_resource(cls.admin_client.delete_subnet, subnet['id']) # Clean up networks for network in cls.networks: cls._try_delete_resource(cls.client.delete_network, network['id']) # Clean up admin networks for network in cls.admin_networks: cls._try_delete_resource(cls.admin_client.delete_network, network['id']) # Clean up security groups for secgroup in cls.security_groups: cls._try_delete_resource(cls.client.delete_security_group, secgroup['id']) for subnetpool in cls.subnetpools: cls._try_delete_resource(cls.client.delete_subnetpool, subnetpool['id']) for subnetpool in cls.admin_subnetpools: cls._try_delete_resource(cls.admin_client.delete_subnetpool, subnetpool['id']) for address_scope in cls.address_scopes: cls._try_delete_resource(cls.client.delete_address_scope, address_scope['id']) for address_scope in cls.admin_address_scopes: cls._try_delete_resource( cls.admin_client.delete_address_scope, address_scope['id']) for project in cls.projects: cls._try_delete_resource( cls.identity_admin_client.delete_project, project['id']) # Clean up QoS rules for qos_rule in cls.qos_rules: cls._try_delete_resource(cls.admin_client.delete_qos_rule, qos_rule['id']) # Clean up QoS policies # as all networks and ports are already removed, QoS policies # shouldn't be "in use" for qos_policy in cls.qos_policies: cls._try_delete_resource(cls.admin_client.delete_qos_policy, qos_policy['id']) super(BaseNetworkTest, cls).resource_cleanup() @classmethod def _try_delete_resource(cls, delete_callable, *args, **kwargs): """Cleanup resources in case of test-failure Some resources are explicitly deleted by the test. If the test failed to delete a resource, this method will execute the appropriate delete methods. Otherwise, the method ignores NotFound exceptions thrown for resources that were correctly deleted by the test. :param delete_callable: delete method :param args: arguments for delete method :param kwargs: keyword arguments for delete method """ try: delete_callable(*args, **kwargs) # if resource is not found, this means it was deleted in the test except lib_exc.NotFound: pass @classmethod def create_network(cls, network_name=None, client=None, **kwargs): """Wrapper utility that returns a test network.""" network_name = network_name or data_utils.rand_name('test-network-') client = client or cls.client body = client.create_network(name=network_name, **kwargs) network = body['network'] if client is cls.client: cls.networks.append(network) else: cls.admin_networks.append(network) return network @classmethod def create_shared_network(cls, network_name=None, **post_body): network_name = network_name or data_utils.rand_name('sharednetwork-') post_body.update({'name': network_name, 'shared': True}) body = cls.admin_client.create_network(**post_body) network = body['network'] cls.admin_networks.append(network) return network @classmethod def create_network_keystone_v3(cls, network_name=None, project_id=None, tenant_id=None, client=None): """Wrapper utility that creates a test network with project_id.""" client = client or cls.client network_name = network_name or data_utils.rand_name( 'test-network-with-project_id') project_id = cls.client.tenant_id body = client.create_network_keystone_v3(network_name, project_id, tenant_id) network = body['network'] if client is cls.client: cls.networks.append(network) else: cls.admin_networks.append(network) return network @classmethod def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None, ip_version=None, client=None, **kwargs): """Wrapper utility that returns a test subnet.""" # allow tests to use admin client if not client: client = cls.client # The cidr and mask_bits depend on the ip version. ip_version = ip_version if ip_version is not None else cls._ip_version gateway_not_set = gateway == '' if ip_version == 4: cidr = cidr or netaddr.IPNetwork( config.safe_get_config_value( 'network', 'project_network_cidr')) mask_bits = ( mask_bits or config.safe_get_config_value( 'network', 'project_network_mask_bits')) elif ip_version == 6: cidr = ( cidr or netaddr.IPNetwork( config.safe_get_config_value( 'network', 'project_network_v6_cidr'))) mask_bits = ( mask_bits or config.safe_get_config_value( 'network', 'project_network_v6_mask_bits')) # Find a cidr that is not in use yet and create a subnet with it for subnet_cidr in cidr.subnet(mask_bits): if gateway_not_set: gateway_ip = str(netaddr.IPAddress(subnet_cidr) + 1) else: gateway_ip = gateway try: body = client.create_subnet( network_id=network['id'], cidr=str(subnet_cidr), ip_version=ip_version, gateway_ip=gateway_ip, **kwargs) break except lib_exc.BadRequest as e: is_overlapping_cidr = 'overlaps with another subnet' in str(e) if not is_overlapping_cidr: raise else: message = 'Available CIDR for subnet creation could not be found' raise ValueError(message) subnet = body['subnet'] if client is cls.client: cls.subnets.append(subnet) else: cls.admin_subnets.append(subnet) return subnet @classmethod def create_port(cls, network, **kwargs): """Wrapper utility that returns a test port.""" body = cls.client.create_port(network_id=network['id'], **kwargs) port = body['port'] cls.ports.append(port) return port @classmethod def update_port(cls, port, **kwargs): """Wrapper utility that updates a test port.""" body = cls.client.update_port(port['id'], **kwargs) return body['port'] @classmethod def _create_router_with_client( cls, client, router_name=None, admin_state_up=False, external_network_id=None, enable_snat=None, **kwargs ): ext_gw_info = {} if external_network_id: ext_gw_info['network_id'] = external_network_id if enable_snat is not None: ext_gw_info['enable_snat'] = enable_snat body = client.create_router( router_name, external_gateway_info=ext_gw_info, admin_state_up=admin_state_up, **kwargs) router = body['router'] cls.routers.append(router) return router @classmethod def create_router(cls, *args, **kwargs): return cls._create_router_with_client(cls.client, *args, **kwargs) @classmethod def create_admin_router(cls, *args, **kwargs): return cls._create_router_with_client(cls.os_admin.network_client, *args, **kwargs) @classmethod def create_floatingip(cls, external_network_id): """Wrapper utility that returns a test floating IP.""" body = cls.client.create_floatingip( floating_network_id=external_network_id) fip = body['floatingip'] cls.floating_ips.append(fip) return fip @classmethod def create_router_interface(cls, router_id, subnet_id): """Wrapper utility that returns a router interface.""" interface = cls.client.add_router_interface_with_subnet_id( router_id, subnet_id) return interface @classmethod def get_supported_qos_rule_types(cls): body = cls.client.list_qos_rule_types() return [rule_type['type'] for rule_type in body['rule_types']] @classmethod def create_qos_policy(cls, name, description=None, shared=False, tenant_id=None, is_default=False): """Wrapper utility that returns a test QoS policy.""" body = cls.admin_client.create_qos_policy( name, description, shared, tenant_id, is_default) qos_policy = body['policy'] cls.qos_policies.append(qos_policy) return qos_policy @classmethod def create_qos_bandwidth_limit_rule(cls, policy_id, max_kbps, max_burst_kbps, direction=const.EGRESS_DIRECTION): """Wrapper utility that returns a test QoS bandwidth limit rule.""" body = cls.admin_client.create_bandwidth_limit_rule( policy_id, max_kbps, max_burst_kbps, direction) qos_rule = body['bandwidth_limit_rule'] cls.qos_rules.append(qos_rule) return qos_rule @classmethod def delete_router(cls, router): body = cls.client.list_router_interfaces(router['id']) interfaces = [port for port in body['ports'] if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS] for i in interfaces: try: cls.client.remove_router_interface_with_subnet_id( router['id'], i['fixed_ips'][0]['subnet_id']) except lib_exc.NotFound: pass cls.client.delete_router(router['id']) @classmethod def create_address_scope(cls, name, is_admin=False, **kwargs): if is_admin: body = cls.admin_client.create_address_scope(name=name, **kwargs) cls.admin_address_scopes.append(body['address_scope']) else: body = cls.client.create_address_scope(name=name, **kwargs) cls.address_scopes.append(body['address_scope']) return body['address_scope'] @classmethod def create_subnetpool(cls, name, is_admin=False, **kwargs): if is_admin: body = cls.admin_client.create_subnetpool(name, **kwargs) cls.admin_subnetpools.append(body['subnetpool']) else: body = cls.client.create_subnetpool(name, **kwargs) cls.subnetpools.append(body['subnetpool']) return body['subnetpool'] @classmethod def create_project(cls, name=None, description=None): test_project = name or data_utils.rand_name('test_project_') test_description = description or data_utils.rand_name('desc_') project = cls.identity_admin_client.create_project( name=test_project, description=test_description)['project'] cls.projects.append(project) return project @classmethod def create_security_group(cls, name, **kwargs): body = cls.client.create_security_group(name=name, **kwargs) cls.security_groups.append(body['security_group']) return body['security_group'] class BaseAdminNetworkTest(BaseNetworkTest): credentials = ['primary', 'admin'] @classmethod def setup_clients(cls): super(BaseAdminNetworkTest, cls).setup_clients() cls.admin_client = cls.os_admin.network_client cls.identity_admin_client = cls.os_admin.projects_client @classmethod def create_metering_label(cls, name, description): """Wrapper utility that returns a test metering label.""" body = cls.admin_client.create_metering_label( description=description, name=data_utils.rand_name("metering-label")) metering_label = body['metering_label'] cls.metering_labels.append(metering_label) return metering_label @classmethod def create_metering_label_rule(cls, remote_ip_prefix, direction, metering_label_id): """Wrapper utility that returns a test metering label rule.""" body = cls.admin_client.create_metering_label_rule( remote_ip_prefix=remote_ip_prefix, direction=direction, metering_label_id=metering_label_id) metering_label_rule = body['metering_label_rule'] cls.metering_label_rules.append(metering_label_rule) return metering_label_rule @classmethod def create_flavor(cls, name, description, service_type): """Wrapper utility that returns a test flavor.""" body = cls.admin_client.create_flavor( description=description, service_type=service_type, name=name) flavor = body['flavor'] cls.flavors.append(flavor) return flavor @classmethod def create_service_profile(cls, description, metainfo, driver): """Wrapper utility that returns a test service profile.""" body = cls.admin_client.create_service_profile( driver=driver, metainfo=metainfo, description=description) service_profile = body['service_profile'] cls.service_profiles.append(service_profile) return service_profile @classmethod def get_unused_ip(cls, net_id, ip_version=None): """Get an unused ip address in a allocation pool of net""" body = cls.admin_client.list_ports(network_id=net_id) ports = body['ports'] used_ips = [] for port in ports: used_ips.extend( [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']]) body = cls.admin_client.list_subnets(network_id=net_id) subnets = body['subnets'] for subnet in subnets: if ip_version and subnet['ip_version'] != ip_version: continue cidr = subnet['cidr'] allocation_pools = subnet['allocation_pools'] iterators = [] if allocation_pools: for allocation_pool in allocation_pools: iterators.append(netaddr.iter_iprange( allocation_pool['start'], allocation_pool['end'])) else: net = netaddr.IPNetwork(cidr) def _iterip(): for ip in net: if ip not in (net.network, net.broadcast): yield ip iterators.append(iter(_iterip())) for iterator in iterators: for ip in iterator: if str(ip) not in used_ips: return str(ip) message = ( "net(%s) has no usable IP address in allocation pools" % net_id) raise exceptions.InvalidConfiguration(message) def require_qos_rule_type(rule_type): def decorator(f): @functools.wraps(f) def wrapper(self, *func_args, **func_kwargs): if rule_type not in self.get_supported_qos_rule_types(): raise self.skipException( "%s rule type is required." % rule_type) return f(self, *func_args, **func_kwargs) return wrapper return decorator def _require_sorting(f): @functools.wraps(f) def inner(self, *args, **kwargs): if not tutils.is_extension_enabled("sorting", "network"): self.skipTest('Sorting feature is required') return f(self, *args, **kwargs) return inner def _require_pagination(f): @functools.wraps(f) def inner(self, *args, **kwargs): if not tutils.is_extension_enabled("pagination", "network"): self.skipTest('Pagination feature is required') return f(self, *args, **kwargs) return inner class BaseSearchCriteriaTest(BaseNetworkTest): # This should be defined by subclasses to reflect resource name to test resource = None field = 'name' # NOTE(ihrachys): some names, like those starting with an underscore (_) # are sorted differently depending on whether the plugin implements native # sorting support, or not. So we avoid any such cases here, sticking to # alphanumeric. Also test a case when there are multiple resources with the # same name resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',) force_tenant_isolation = True list_kwargs = {} list_as_admin = False def assertSameOrder(self, original, actual): # gracefully handle iterators passed original = list(original) actual = list(actual) self.assertEqual(len(original), len(actual)) for expected, res in zip(original, actual): self.assertEqual(expected[self.field], res[self.field]) @utils.classproperty def plural_name(self): return '%ss' % self.resource @property def list_client(self): return self.admin_client if self.list_as_admin else self.client def list_method(self, *args, **kwargs): method = getattr(self.list_client, 'list_%s' % self.plural_name) kwargs.update(self.list_kwargs) return method(*args, **kwargs) def get_bare_url(self, url): base_url = self.client.base_url self.assertTrue(url.startswith(base_url)) return url[len(base_url):] @classmethod def _extract_resources(cls, body): return body[cls.plural_name] def _test_list_sorts(self, direction): sort_args = { 'sort_dir': direction, 'sort_key': self.field } body = self.list_method(**sort_args) resources = self._extract_resources(body) self.assertNotEmpty( resources, "%s list returned is empty" % self.resource) retrieved_names = [res[self.field] for res in resources] expected = sorted(retrieved_names) if direction == constants.SORT_DIRECTION_DESC: expected = list(reversed(expected)) self.assertEqual(expected, retrieved_names) @_require_sorting def _test_list_sorts_asc(self): self._test_list_sorts(constants.SORT_DIRECTION_ASC) @_require_sorting def _test_list_sorts_desc(self): self._test_list_sorts(constants.SORT_DIRECTION_DESC) @_require_pagination def _test_list_pagination(self): for limit in range(1, len(self.resource_names) + 1): pagination_args = { 'limit': limit, } body = self.list_method(**pagination_args) resources = self._extract_resources(body) self.assertEqual(limit, len(resources)) @_require_pagination def _test_list_no_pagination_limit_0(self): pagination_args = { 'limit': 0, } body = self.list_method(**pagination_args) resources = self._extract_resources(body) self.assertGreaterEqual(len(resources), len(self.resource_names)) def _test_list_pagination_iteratively(self, lister): # first, collect all resources for later comparison sort_args = { 'sort_dir': constants.SORT_DIRECTION_ASC, 'sort_key': self.field } body = self.list_method(**sort_args) expected_resources = self._extract_resources(body) self.assertNotEmpty(expected_resources) resources = lister( len(expected_resources), sort_args ) # finally, compare that the list retrieved in one go is identical to # the one containing pagination results self.assertSameOrder(expected_resources, resources) def _list_all_with_marker(self, niterations, sort_args): # paginate resources one by one, using last fetched resource as a # marker resources = [] for i in range(niterations): pagination_args = sort_args.copy() pagination_args['limit'] = 1 if resources: pagination_args['marker'] = resources[-1]['id'] body = self.list_method(**pagination_args) resources_ = self._extract_resources(body) self.assertEqual(1, len(resources_)) resources.extend(resources_) return resources @_require_pagination @_require_sorting def _test_list_pagination_with_marker(self): self._test_list_pagination_iteratively(self._list_all_with_marker) def _list_all_with_hrefs(self, niterations, sort_args): # paginate resources one by one, using next href links resources = [] prev_links = {} for i in range(niterations): if prev_links: uri = self.get_bare_url(prev_links['next']) else: sort_args.update(self.list_kwargs) uri = self.list_client.build_uri( self.plural_name, limit=1, **sort_args) prev_links, body = self.list_client.get_uri_with_links( self.plural_name, uri ) resources_ = self._extract_resources(body) self.assertEqual(1, len(resources_)) resources.extend(resources_) # The last element is empty and does not contain 'next' link uri = self.get_bare_url(prev_links['next']) prev_links, body = self.client.get_uri_with_links( self.plural_name, uri ) self.assertNotIn('next', prev_links) # Now walk backwards and compare results resources2 = [] for i in range(niterations): uri = self.get_bare_url(prev_links['previous']) prev_links, body = self.list_client.get_uri_with_links( self.plural_name, uri ) resources_ = self._extract_resources(body) self.assertEqual(1, len(resources_)) resources2.extend(resources_) self.assertSameOrder(resources, reversed(resources2)) return resources @_require_pagination @_require_sorting def _test_list_pagination_with_href_links(self): self._test_list_pagination_iteratively(self._list_all_with_hrefs) @_require_pagination @_require_sorting def _test_list_pagination_page_reverse_with_href_links( self, direction=constants.SORT_DIRECTION_ASC): pagination_args = { 'sort_dir': direction, 'sort_key': self.field, } body = self.list_method(**pagination_args) expected_resources = self._extract_resources(body) page_size = 2 pagination_args['limit'] = page_size prev_links = {} resources = [] num_resources = len(expected_resources) niterations = int(math.ceil(float(num_resources) / page_size)) for i in range(niterations): if prev_links: uri = self.get_bare_url(prev_links['previous']) else: pagination_args.update(self.list_kwargs) uri = self.list_client.build_uri( self.plural_name, page_reverse=True, **pagination_args) prev_links, body = self.list_client.get_uri_with_links( self.plural_name, uri ) resources_ = self._extract_resources(body) self.assertGreaterEqual(page_size, len(resources_)) resources.extend(reversed(resources_)) self.assertSameOrder(expected_resources, reversed(resources)) @_require_pagination @_require_sorting def _test_list_pagination_page_reverse_asc(self): self._test_list_pagination_page_reverse( direction=constants.SORT_DIRECTION_ASC) @_require_pagination @_require_sorting def _test_list_pagination_page_reverse_desc(self): self._test_list_pagination_page_reverse( direction=constants.SORT_DIRECTION_DESC) def _test_list_pagination_page_reverse(self, direction): pagination_args = { 'sort_dir': direction, 'sort_key': self.field, 'limit': 3, } body = self.list_method(**pagination_args) expected_resources = self._extract_resources(body) pagination_args['limit'] -= 1 pagination_args['marker'] = expected_resources[-1]['id'] pagination_args['page_reverse'] = True body = self.list_method(**pagination_args) self.assertSameOrder( # the last entry is not included in 2nd result when used as a # marker expected_resources[:-1], self._extract_resources(body)) def _test_list_validation_filters(self): validation_args = { 'unknown_filter': 'value', } body = self.list_method(**validation_args) resources = self._extract_resources(body) for resource in resources: self.assertIn(resource['name'], self.resource_names) neutron-12.1.1/neutron/tests/tempest/api/clients.py0000664000175000017500000000714413553660047022430 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest.lib.services.compute import keypairs_client from tempest.lib.services.compute import servers_client from tempest.lib.services.identity.v2 import tenants_client from tempest.lib.services.identity.v3 import projects_client from tempest import manager from neutron.tests.tempest import config from neutron.tests.tempest.services.network.json import network_client CONF = config.CONF class Manager(manager.Manager): """ Top level manager for OpenStack tempest clients """ default_params = { 'disable_ssl_certificate_validation': CONF.identity.disable_ssl_certificate_validation, 'ca_certs': CONF.identity.ca_certificates_file, 'trace_requests': CONF.debug.trace_requests } # NOTE: Tempest uses timeout values of compute API if project specific # timeout values don't exist. default_params_with_timeout_values = { 'build_interval': CONF.compute.build_interval, 'build_timeout': CONF.compute.build_timeout } default_params_with_timeout_values.update(default_params) def __init__(self, credentials=None, service=None): super(Manager, self).__init__(credentials=credentials) self._set_identity_clients() self.network_client = network_client.NetworkClientJSON( self.auth_provider, CONF.network.catalog_type, CONF.network.region or CONF.identity.region, endpoint_type=CONF.network.endpoint_type, build_interval=CONF.network.build_interval, build_timeout=CONF.network.build_timeout, **self.default_params) params = { 'service': CONF.compute.catalog_type, 'region': CONF.compute.region or CONF.identity.region, 'endpoint_type': CONF.compute.endpoint_type, 'build_interval': CONF.compute.build_interval, 'build_timeout': CONF.compute.build_timeout } params.update(self.default_params) self.servers_client = servers_client.ServersClient( self.auth_provider, enable_instance_password=CONF.compute_feature_enabled .enable_instance_password, **params) self.keypairs_client = keypairs_client.KeyPairsClient( self.auth_provider, **params) def _set_identity_clients(self): params = { 'service': CONF.identity.catalog_type, 'region': CONF.identity.region } params.update(self.default_params_with_timeout_values) params_v2_admin = params.copy() params_v2_admin['endpoint_type'] = CONF.identity.v2_admin_endpoint_type # Client uses admin endpoint type of Keystone API v2 self.tenants_client = tenants_client.TenantsClient(self.auth_provider, **params_v2_admin) # Client uses admin endpoint type of Keystone API v3 self.projects_client = projects_client.ProjectsClient( self.auth_provider, **params_v2_admin) neutron-12.1.1/neutron/tests/tempest/api/__init__.py0000664000175000017500000000000013553660047022506 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/common/0000775000175000017500000000000013553660156021127 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/tempest/common/tempest_fixtures.py0000664000175000017500000000144613553660047025117 0ustar zuulzuul00000000000000# Copyright 2013 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_concurrency.fixture import lockutils class LockFixture(lockutils.LockFixture): def __init__(self, name): super(LockFixture, self).__init__(name, 'tempest-') neutron-12.1.1/neutron/tests/tempest/common/ssh.py0000664000175000017500000000160513553660047022277 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest.lib.common import ssh from neutron.tests.tempest import config class Client(ssh.Client): def __init__(self, *args, **kwargs): if 'timeout' not in kwargs: kwargs['timeout'] = config.CONF.validation.ssh_timeout super(Client, self).__init__(*args, **kwargs) neutron-12.1.1/neutron/tests/tempest/common/__init__.py0000664000175000017500000000000013553660047023225 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/base.py0000664000175000017500000004665313553660047017457 0ustar zuulzuul00000000000000# Copyright 2010-2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Base test cases for all neutron tests. """ import abc import contextlib import functools import inspect import os import os.path import eventlet.timeout import fixtures import mock from neutron_lib.callbacks import manager as registry_manager from neutron_lib import fixture from oslo_concurrency.fixture import lockutils from oslo_config import cfg from oslo_db import exception as db_exceptions from oslo_db import options as db_options from oslo_messaging import conffixture as messaging_conffixture from oslo_utils import excutils from oslo_utils import fileutils from oslo_utils import strutils from oslotest import base import six from sqlalchemy import exc as sqlalchemy_exc import testtools from neutron._i18n import _ from neutron.agent.linux import external_process from neutron.api.rpc.callbacks.consumer import registry as rpc_consumer_reg from neutron.api.rpc.callbacks.producer import registry as rpc_producer_reg from neutron.common import config from neutron.common import rpc as n_rpc from neutron.conf.agent import common as agent_config from neutron.db import _model_query as model_query from neutron.db import _resource_extend as resource_extend from neutron.db import agentschedulers_db from neutron.db import api as db_api from neutron import manager from neutron import policy from neutron.quota import resource_registry from neutron.tests import fake_notifier from neutron.tests import post_mortem_debug from neutron.tests import tools CONF = cfg.CONF CONF.import_opt('state_path', 'neutron.conf.common') ROOTDIR = os.path.dirname(__file__) ETCDIR = os.path.join(ROOTDIR, 'etc') SUDO_CMD = 'sudo -n' def etcdir(*p): return os.path.join(ETCDIR, *p) def fake_use_fatal_exceptions(*args): return True def bool_from_env(key, strict=False, default=False): value = os.environ.get(key) return strutils.bool_from_string(value, strict=strict, default=default) def setup_test_logging(config_opts, log_dir, log_file_path_template): # Have each test log into its own log file config_opts.set_override('debug', True) fileutils.ensure_tree(log_dir, mode=0o755) log_file = sanitize_log_path( os.path.join(log_dir, log_file_path_template)) config_opts.set_override('log_file', log_file) config.setup_logging() def sanitize_log_path(path): # Sanitize the string so that its log path is shell friendly replace_map = {' ': '-', '(': '_', ')': '_'} for s, r in replace_map.items(): path = path.replace(s, r) return path def unstable_test(reason): def decor(f): @functools.wraps(f) def inner(self, *args, **kwargs): try: return f(self, *args, **kwargs) except Exception as e: msg = ("%s was marked as unstable because of %s, " "failure was: %s") % (self.id(), reason, e) raise self.skipTest(msg) return inner return decor def skip_if_timeout(reason): def decor(f): @functools.wraps(f) def inner(self, *args, **kwargs): try: return f(self, *args, **kwargs) except fixtures.TimeoutException: msg = ("Timeout raised for test %s, skipping it " "because of: %s") % (self.id(), reason) raise self.skipTest(msg) except (sqlalchemy_exc.InterfaceError, db_exceptions.DBConnectionError): # In case of db tests very often TimeoutException is reason of # some sqlalchemy InterfaceError exception and that is final # raised exception which needs to be handled msg = ("DB connection broken in test %s. It is very likely " "that this happend because of test timeout. " "Skipping test because of: %s") % (self.id(), reason) raise self.skipTest(msg) return inner return decor def set_timeout(timeout): """Timeout decorator for test methods. Use this decorator for tests that are expected to pass in very specific amount of time, not common for all other tests. It can have either big or small value. """ def decor(f): @functools.wraps(f) def inner(self, *args, **kwargs): self.useFixture(fixtures.Timeout(timeout, gentle=True)) return f(self, *args, **kwargs) return inner return decor def get_rootwrap_cmd(): return os.environ.get('OS_ROOTWRAP_CMD', SUDO_CMD) def get_rootwrap_daemon_cmd(): return os.environ.get('OS_ROOTWRAP_DAEMON_CMD') class AttributeDict(dict): """ Provide attribute access (dict.key) to dictionary values. """ def __getattr__(self, name): """Allow attribute access for all keys in the dict.""" if name in self: return self[name] raise AttributeError(_("Unknown attribute '%s'.") % name) def _catch_timeout(f): @functools.wraps(f) def func(self, *args, **kwargs): try: return f(self, *args, **kwargs) except eventlet.Timeout as e: self.fail('Execution of this test timed out: %s' % e) return func class _CatchTimeoutMetaclass(abc.ABCMeta): def __init__(cls, name, bases, dct): super(_CatchTimeoutMetaclass, cls).__init__(name, bases, dct) for name, method in inspect.getmembers( # NOTE(ihrachys): we should use isroutine because it will catch # both unbound methods (python2) and functions (python3) cls, predicate=inspect.isroutine): if name.startswith('test_'): setattr(cls, name, _catch_timeout(method)) # Test worker cannot survive eventlet's Timeout exception, which effectively # kills the whole worker, with all test cases scheduled to it. This metaclass # makes all test cases convert Timeout exceptions into unittest friendly # failure mode (self.fail). @six.add_metaclass(_CatchTimeoutMetaclass) class DietTestCase(base.BaseTestCase): """Same great taste, less filling. BaseTestCase is responsible for doing lots of plugin-centric setup that not all tests require (or can tolerate). This class provides only functionality that is common across all tests. """ def setUp(self): super(DietTestCase, self).setUp() # FIXME(amuller): this must be called in the Neutron unit tests base # class. Moving this may cause non-deterministic failures. Bug #1489098 # for more info. db_options.set_defaults(cfg.CONF, connection='sqlite://') # Configure this first to ensure pm debugging support for setUp() debugger = os.environ.get('OS_POST_MORTEM_DEBUGGER') if debugger: self.addOnException(post_mortem_debug.get_exception_handler( debugger)) # Make sure we see all relevant deprecation warnings when running tests self.useFixture(tools.WarningsFixture()) # NOTE(ihrachys): oslotest already sets stopall for cleanup, but it # does it using six.moves.mock (the library was moved into # unittest.mock in Python 3.4). So until we switch to six.moves.mock # everywhere in unit tests, we can't remove this setup. The base class # is used in 3party projects, so we would need to switch all of them to # six before removing the cleanup callback from here. self.addCleanup(mock.patch.stopall) self.addCleanup(self.reset_model_query_hooks) self.addCleanup(self.reset_resource_extend_functions) self.addOnException(self.check_for_systemexit) self.orig_pid = os.getpid() tools.reset_random_seed() @staticmethod def reset_model_query_hooks(): model_query._model_query_hooks = {} @staticmethod def reset_resource_extend_functions(): resource_extend._resource_extend_functions = {} def addOnException(self, handler): def safe_handler(*args, **kwargs): try: return handler(*args, **kwargs) except Exception: with excutils.save_and_reraise_exception(reraise=False) as ctx: self.addDetail('failure in exception handler %s' % handler, testtools.content.TracebackContent( (ctx.type_, ctx.value, ctx.tb), self)) return super(DietTestCase, self).addOnException(safe_handler) def check_for_systemexit(self, exc_info): if isinstance(exc_info[1], SystemExit): if os.getpid() != self.orig_pid: # Subprocess - let it just exit raise # This makes sys.exit(0) still a failure self.force_failure = True @contextlib.contextmanager def assert_max_execution_time(self, max_execution_time=5): with eventlet.Timeout(max_execution_time, False): yield return self.fail('Execution of this test timed out') def assertOrderedEqual(self, expected, actual): expect_val = self.sort_dict_lists(expected) actual_val = self.sort_dict_lists(actual) self.assertEqual(expect_val, actual_val) def sort_dict_lists(self, dic): for key, value in dic.items(): if isinstance(value, list): dic[key] = sorted(value) elif isinstance(value, dict): dic[key] = self.sort_dict_lists(value) return dic def assertDictSupersetOf(self, expected_subset, actual_superset): """Checks that actual dict contains the expected dict. After checking that the arguments are of the right type, this checks that each item in expected_subset is in, and matches, what is in actual_superset. Separate tests are done, so that detailed info can be reported upon failure. """ if not isinstance(expected_subset, dict): self.fail("expected_subset (%s) is not an instance of dict" % type(expected_subset)) if not isinstance(actual_superset, dict): self.fail("actual_superset (%s) is not an instance of dict" % type(actual_superset)) for k, v in expected_subset.items(): self.assertIn(k, actual_superset) self.assertEqual(v, actual_superset[k], "Key %(key)s expected: %(exp)r, actual %(act)r" % {'key': k, 'exp': v, 'act': actual_superset[k]}) class ProcessMonitorFixture(fixtures.Fixture): """Test fixture to capture and cleanup any spawn process monitor.""" def _setUp(self): self.old_callable = ( external_process.ProcessMonitor._spawn_checking_thread) p = mock.patch("neutron.agent.linux.external_process.ProcessMonitor." "_spawn_checking_thread", new=lambda x: self.record_calls(x)) p.start() self.instances = [] self.addCleanup(self.stop) def stop(self): for instance in self.instances: instance.stop() def record_calls(self, instance): self.old_callable(instance) self.instances.append(instance) class BaseTestCase(DietTestCase): @staticmethod def config_parse(conf=None, args=None): """Create the default configurations.""" # neutron.conf includes rpc_backend which needs to be cleaned up if args is None: args = [] args += ['--config-file', etcdir('neutron.conf')] if conf is None: config.init(args=args) else: conf(args) def setUp(self): super(BaseTestCase, self).setUp() self.useFixture(lockutils.ExternalLockFixture()) self.useFixture(fixture.APIDefinitionFixture()) cfg.CONF.set_override('state_path', self.get_default_temp_dir().path) self.addCleanup(CONF.reset) self.useFixture(ProcessMonitorFixture()) self.useFixture(fixtures.MonkeyPatch( 'neutron_lib.exceptions.NeutronException.use_fatal_exceptions', fake_use_fatal_exceptions)) self.useFixture(fixtures.MonkeyPatch( 'oslo_config.cfg.find_config_files', lambda project=None, prog=None, extension=None: [])) self.setup_rpc_mocks() self.setup_config() self._callback_manager = registry_manager.CallbacksManager() self.useFixture(fixture.CallbackRegistryFixture( callback_manager=self._callback_manager)) # Give a private copy of the directory to each test. self.useFixture(fixture.PluginDirectoryFixture()) policy.init() self.addCleanup(policy.reset) self.addCleanup(resource_registry.unregister_all_resources) self.addCleanup(db_api.sqla_remove_all) self.addCleanup(rpc_consumer_reg.clear) self.addCleanup(rpc_producer_reg.clear) def get_new_temp_dir(self): """Create a new temporary directory. :returns: fixtures.TempDir """ return self.useFixture(fixtures.TempDir()) def get_default_temp_dir(self): """Create a default temporary directory. Returns the same directory during the whole test case. :returns: fixtures.TempDir """ if not hasattr(self, '_temp_dir'): self._temp_dir = self.get_new_temp_dir() return self._temp_dir def get_temp_file_path(self, filename, root=None): """Returns an absolute path for a temporary file. If root is None, the file is created in default temporary directory. It also creates the directory if it's not initialized yet. If root is not None, the file is created inside the directory passed as root= argument. :param filename: filename :type filename: string :param root: temporary directory to create a new file in :type root: fixtures.TempDir :returns: absolute file path string """ root = root or self.get_default_temp_dir() return root.join(filename) def setup_rpc_mocks(self): # don't actually start RPC listeners when testing mock.patch( 'neutron.common.rpc.Connection.consume_in_threads', return_value=[]).start() self.useFixture(fixtures.MonkeyPatch( 'oslo_messaging.Notifier', fake_notifier.FakeNotifier)) self.messaging_conf = messaging_conffixture.ConfFixture(CONF) self.messaging_conf.transport_driver = 'fake' # NOTE(russellb) We want all calls to return immediately. self.messaging_conf.response_timeout = 0 self.useFixture(self.messaging_conf) self.addCleanup(n_rpc.clear_extra_exmods) n_rpc.add_extra_exmods('neutron.test') self.addCleanup(n_rpc.cleanup) n_rpc.init(CONF) def setup_config(self, args=None): """Tests that need a non-default config can override this method.""" self.config_parse(args=args) def config(self, **kw): """Override some configuration values. The keyword arguments are the names of configuration options to override and their values. If a group argument is supplied, the overrides are applied to the specified configuration option group. All overrides are automatically cleared at the end of the current test by the fixtures cleanup process. """ group = kw.pop('group', None) for k, v in kw.items(): CONF.set_override(k, v, group) def setup_coreplugin(self, core_plugin=None, load_plugins=True): cp = PluginFixture(core_plugin) self.useFixture(cp) self.patched_dhcp_periodic = cp.patched_dhcp_periodic self.patched_default_svc_plugins = cp.patched_default_svc_plugins if load_plugins: manager.init() def setup_notification_driver(self, notification_driver=None): self.addCleanup(fake_notifier.reset) if notification_driver is None: notification_driver = [fake_notifier.__name__] cfg.CONF.set_override("notification_driver", notification_driver) def setup_rootwrap(self): agent_config.register_root_helper(cfg.CONF) self.config(group='AGENT', root_helper=os.environ.get('OS_ROOTWRAP_CMD', SUDO_CMD)) self.config(group='AGENT', root_helper_daemon=os.environ.get( 'OS_ROOTWRAP_DAEMON_CMD')) class PluginFixture(fixtures.Fixture): def __init__(self, core_plugin=None): super(PluginFixture, self).__init__() self.core_plugin = core_plugin def _setUp(self): # Do not load default service plugins in the testing framework # as all the mocking involved can cause havoc. self.default_svc_plugins_p = mock.patch( 'neutron.manager.NeutronManager._get_default_service_plugins') self.patched_default_svc_plugins = self.default_svc_plugins_p.start() self.dhcp_periodic_p = mock.patch( 'neutron.db.agentschedulers_db.DhcpAgentSchedulerDbMixin.' 'add_periodic_dhcp_agent_status_check') self.patched_dhcp_periodic = self.dhcp_periodic_p.start() self.agent_health_check_p = mock.patch( 'neutron.db.agentschedulers_db.DhcpAgentSchedulerDbMixin.' 'add_agent_status_check_worker') self.agent_health_check = self.agent_health_check_p.start() # Plugin cleanup should be triggered last so that # test-specific cleanup has a chance to release references. self.addCleanup(self.cleanup_core_plugin) if self.core_plugin is not None: cfg.CONF.set_override('core_plugin', self.core_plugin) def cleanup_core_plugin(self): """Ensure that the core plugin is deallocated.""" nm = manager.NeutronManager if not nm.has_instance(): return # TODO(marun) Fix plugins that do not properly initialize notifiers agentschedulers_db.AgentSchedulerDbMixin.agent_notifiers = {} nm.clear_instance() class Timeout(fixtures.Fixture): """Setup per test timeouts. In order to avoid test deadlocks we support setting up a test timeout parameter read from the environment. In almost all cases where the timeout is reached this means a deadlock. A scaling factor allows extremely long tests to specify they need more time. """ def __init__(self, timeout=None, scaling=1): super(Timeout, self).__init__() if timeout is None: timeout = os.environ.get('OS_TEST_TIMEOUT', 0) try: self.test_timeout = int(timeout) except ValueError: # If timeout value is invalid do not set a timeout. self.test_timeout = 0 if scaling >= 1: self.test_timeout *= scaling else: raise ValueError('scaling value must be >= 1') def setUp(self): super(Timeout, self).setUp() if self.test_timeout > 0: self.useFixture(fixtures.Timeout(self.test_timeout, gentle=True)) neutron-12.1.1/neutron/tests/tools.py0000664000175000017500000002126613553660047017676 0ustar zuulzuul00000000000000# Copyright (c) 2013 NEC Corporation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import os import platform import random import time import warnings import fixtures import mock import netaddr from neutron_lib import constants from neutron_lib.utils import helpers from neutron_lib.utils import net from oslo_utils import netutils from oslo_utils import timeutils import unittest2 from neutron.common import constants as n_const from neutron.services.logapi.common import constants as log_const class WarningsFixture(fixtures.Fixture): """Filters out warnings during test runs.""" warning_types = ( DeprecationWarning, PendingDeprecationWarning, ImportWarning ) def _setUp(self): self.addCleanup(warnings.resetwarnings) for wtype in self.warning_types: warnings.filterwarnings( "once", category=wtype, module='^neutron\\.') class OpenFixture(fixtures.Fixture): """Mock access to a specific file while preserving open for others.""" def __init__(self, filepath, contents=''): self.path = filepath self.contents = contents def _setUp(self): self.mock_open = mock.mock_open(read_data=self.contents) self._orig_open = open def replacement_open(name, *args, **kwargs): if name == self.path: return self.mock_open(name, *args, **kwargs) return self._orig_open(name, *args, **kwargs) self._patch = mock.patch('six.moves.builtins.open', new=replacement_open) self._patch.start() self.addCleanup(self._patch.stop) class SafeCleanupFixture(fixtures.Fixture): """Catch errors in daughter fixture cleanup.""" def __init__(self, fixture): self.fixture = fixture def _setUp(self): def cleanUp(): try: self.fixture.cleanUp() except Exception: pass self.fixture.setUp() self.addCleanup(cleanUp) def setup_mock_calls(mocked_call, expected_calls_and_values): """A convenient method to setup a sequence of mock calls. expected_calls_and_values is a list of (expected_call, return_value): expected_calls_and_values = [ (mock.call(["ovs-vsctl", self.TO, '--', "--may-exist", "add-port", self.BR_NAME, pname]), None), (mock.call(["ovs-vsctl", self.TO, "set", "Interface", pname, "type=gre"]), None), .... ] * expected_call should be mock.call(expected_arg, ....) * return_value is passed to side_effect of a mocked call. A return value or an exception can be specified. """ return_values = [call[1] for call in expected_calls_and_values] mocked_call.side_effect = return_values def verify_mock_calls(mocked_call, expected_calls_and_values, any_order=False): """A convenient method to setup a sequence of mock calls. expected_calls_and_values is a list of (expected_call, return_value): expected_calls_and_values = [ (mock.call(["ovs-vsctl", self.TO, '--', "--may-exist", "add-port", self.BR_NAME, pname]), None), (mock.call(["ovs-vsctl", self.TO, "set", "Interface", pname, "type=gre"]), None), .... ] * expected_call should be mock.call(expected_arg, ....) * return_value is passed to side_effect of a mocked call. A return value or an exception can be specified. """ expected_calls = [call[0] for call in expected_calls_and_values] mocked_call.assert_has_calls(expected_calls, any_order=any_order) def fail(msg=None): """Fail immediately, with the given message. This method is equivalent to TestCase.fail without requiring a testcase instance (usefully for reducing coupling). """ raise unittest2.TestCase.failureException(msg) class UnorderedList(list): """A list that is equals to any permutation of itself.""" def __eq__(self, other): if not isinstance(other, list): return False return (sorted(self, key=helpers.safe_sort_key) == sorted(other, key=helpers.safe_sort_key)) def __neq__(self, other): return not self == other def get_random_string_list(i=3, n=5): return [helpers.get_random_string(n) for _ in range(0, i)] def get_random_boolean(): return bool(random.getrandbits(1)) def get_random_datetime(start_time=None, end_time=None): start_time = start_time or timeutils.utcnow() end_time = end_time or (start_time + datetime.timedelta(days=1)) # calculate the seconds difference between start and end time delta_seconds_difference = int(timeutils.delta_seconds(start_time, end_time)) # get a random time_delta_seconds between 0 and # delta_seconds_difference random_time_delta = random.randint(0, delta_seconds_difference) # generate a random datetime between start and end time return start_time + datetime.timedelta(seconds=random_time_delta) def get_random_integer(range_begin=0, range_end=1000): return random.randint(range_begin, range_end) def get_random_prefixlen(version=4): maxlen = constants.IPv4_BITS if version == 6: maxlen = constants.IPv6_BITS return random.randint(0, maxlen) def get_random_port(start=n_const.PORT_RANGE_MIN): return random.randint(start, n_const.PORT_RANGE_MAX) def get_random_vlan(): return random.randint(constants.MIN_VLAN_TAG, constants.MAX_VLAN_TAG) def get_random_ip_version(): return random.choice(n_const.IP_ALLOWED_VERSIONS) def get_random_cidr(version=4): if version == 4: return '10.%d.%d.0/%d' % (random.randint(3, 254), random.randint(3, 254), 24) return '2001:db8:%x::/%d' % (random.getrandbits(16), 64) def get_random_EUI(): return netaddr.EUI( net.get_random_mac(['fe', '16', '3e', '00', '00', '00']) ) def get_random_ip_network(version=4): return netaddr.IPNetwork(get_random_cidr(version=version)) def get_random_ip_address(version=4): if version == 4: ip_string = '10.%d.%d.%d' % (random.randint(3, 254), random.randint(3, 254), random.randint(3, 254)) return netaddr.IPAddress(ip_string) else: ip = netutils.get_ipv6_addr_by_EUI64( '2001:db8::/64', net.get_random_mac(['fe', '16', '3e', '00', '00', '00']) ) return ip def get_random_router_status(): return random.choice(n_const.VALID_ROUTER_STATUS) def get_random_floatingip_status(): return random.choice(n_const.VALID_FLOATINGIP_STATUS) def get_random_flow_direction(): return random.choice(constants.VALID_DIRECTIONS) def get_random_ha_states(): return random.choice(n_const.VALID_HA_STATES) def get_random_ether_type(): return random.choice(n_const.VALID_ETHERTYPES) def get_random_ipam_status(): return random.choice(n_const.VALID_IPAM_ALLOCATION_STATUSES) def get_random_ip_protocol(): return random.choice(list(constants.IP_PROTOCOL_MAP.keys())) def get_random_port_binding_statuses(): return random.choice(n_const.PORT_BINDING_STATUSES) def is_bsd(): """Return True on BSD-based systems.""" system = platform.system() if system == 'Darwin': return True if 'bsd' in system.lower(): return True return False def reset_random_seed(): # reset random seed to make sure other processes extracting values from RNG # don't get the same results (useful especially when you then use the # random values to allocate system resources from global pool, like ports # to listen). Use both current time and pid to make sure no tests started # at the same time get the same values from RNG seed = time.time() + os.getpid() random.seed(seed) def get_random_ipv6_mode(): return random.choice(constants.IPV6_MODES) def get_random_security_event(): return random.choice(log_const.LOG_EVENTS) neutron-12.1.1/neutron/tests/__init__.py0000664000175000017500000000000013553660046020253 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/var/0000775000175000017500000000000013553660157016747 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/var/certandkey.pem0000664000175000017500000001175513553660046021611 0ustar zuulzuul00000000000000-----BEGIN CERTIFICATE----- MIIFLjCCAxYCAQEwDQYJKoZIhvcNAQEFBQAwYTELMAkGA1UEBhMCQVUxEzARBgNV BAgTClNvbWUtU3RhdGUxFTATBgNVBAoTDE9wZW5zdGFjayBDQTESMBAGA1UECxMJ R2xhbmNlIENBMRIwEAYDVQQDEwlHbGFuY2UgQ0EwHhcNMTIwMjA5MTcxMDUzWhcN MjIwMjA2MTcxMDUzWjBZMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0 ZTESMBAGA1UEChMJT3BlbnN0YWNrMQ8wDQYDVQQLEwZHbGFuY2UxEDAOBgNVBAMT BzAuMC4wLjAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXpUkQN6pu avo+gz3o1K4krVdPl1m7NjNJDyD/+ZH0EGNcEN7iag1qPE7JsjqGPNZsQK1dMoXb Sz+OSi9qvNeJnBcfwUx5qTAtwyAb9AxGkwuMafIU+lWbsclo+dPGsja01ywbXTCZ bF32iqnpOMYhfxWUdoQYiBkhxxhW9eMPKLS/KkP8/bx+Vaa2XJiAebqkd9nrksAA BeGc9mlafYBEmiChPdJEPw+1ePA4QVq9aPepDsqAKtGN8JLpmoC3BdxQQTbbwL3Q 8fTXK4tCNUaVk4AbDy/McFq6y0ocQoBPJjihOY35mWG/OLtcI99yPOpWGnps/5aG /64DDJ2D67Fnaj6gKHV+6TXFO8KZxlnxtgtiZDJBZkneTBt9ArSOv+l6NBsumRz0 iEJ4o4H1S2TSMnprAvX7WnGtc6Xi9gXahYcDHEelwwYzqAiTBv6hxSp4MZ2dNXa+ KzOitC7ZbV2qsg0au0wjfE/oSQ3NvsvUr8nOmfutJTvHRAwbC1v4G/tuAsO7O0w2 0u2B3u+pG06m5+rnEqp+rB9hmukRYTfgEFRRsVIvpFl/cwvPXKRcX03UIMx+lLr9 Ft+ep7YooBhY3wY2kwCxD4lRYNmbwsCIVywZt40f/4ad98TkufR9NhsfycxGeqbr mTMFlZ8TTlmP82iohekKCOvoyEuTIWL2+wIDAQABMA0GCSqGSIb3DQEBBQUAA4IC AQBMUBgV0R+Qltf4Du7u/8IFmGAoKR/mktB7R1gRRAqsvecUt7kIwBexGdavGg1y 0pU0+lgUZjJ20N1SlPD8gkNHfXE1fL6fmMjWz4dtYJjzRVhpufHPeBW4tl8DgHPN rBGAYQ+drDSXaEjiPQifuzKx8WS+DGA3ki4co5mPjVnVH1xvLIdFsk89z3b3YD1k yCJ/a9K36x6Z/c67JK7s6MWtrdRF9+MVnRKJ2PK4xznd1kBz16V+RA466wBDdARY vFbtkafbEqOb96QTonIZB7+fAldKDPZYnwPqasreLmaGOaM8sxtlPYAJ5bjDONbc AaXG8BMRQyO4FyH237otDKlxPyHOFV66BaffF5S8OlwIMiZoIvq+IcTZOdtDUSW2 KHNLfe5QEDZdKjWCBrfqAfvNuG13m03WqfmcMHl3o/KiPJlx8l9Z4QEzZ9xcyQGL cncgeHM9wJtzi2cD/rTDNFsx/gxvoyutRmno7I3NRbKmpsXF4StZioU3USRspB07 hYXOVnG3pS+PjVby7ThT3gvFHSocguOsxClx1epdUJAmJUbmM7NmOp5WVBVtMtC2 Su4NG/xJciXitKzw+btb7C7RjO6OEqv/1X/oBDzKBWQAwxUC+lqmnM7W6oqWJFEM YfTLnrjs7Hj6ThMGcEnfvc46dWK3dz0RjsQzUxugPuEkLA== -----END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- MIIJKAIBAAKCAgEA16VJEDeqbmr6PoM96NSuJK1XT5dZuzYzSQ8g//mR9BBjXBDe 4moNajxOybI6hjzWbECtXTKF20s/jkovarzXiZwXH8FMeakwLcMgG/QMRpMLjGny FPpVm7HJaPnTxrI2tNcsG10wmWxd9oqp6TjGIX8VlHaEGIgZIccYVvXjDyi0vypD /P28flWmtlyYgHm6pHfZ65LAAAXhnPZpWn2ARJogoT3SRD8PtXjwOEFavWj3qQ7K gCrRjfCS6ZqAtwXcUEE228C90PH01yuLQjVGlZOAGw8vzHBaustKHEKATyY4oTmN +Zlhvzi7XCPfcjzqVhp6bP+Whv+uAwydg+uxZ2o+oCh1fuk1xTvCmcZZ8bYLYmQy QWZJ3kwbfQK0jr/pejQbLpkc9IhCeKOB9Utk0jJ6awL1+1pxrXOl4vYF2oWHAxxH pcMGM6gIkwb+ocUqeDGdnTV2viszorQu2W1dqrINGrtMI3xP6EkNzb7L1K/Jzpn7 rSU7x0QMGwtb+Bv7bgLDuztMNtLtgd7vqRtOpufq5xKqfqwfYZrpEWE34BBUUbFS L6RZf3MLz1ykXF9N1CDMfpS6/Rbfnqe2KKAYWN8GNpMAsQ+JUWDZm8LAiFcsGbeN H/+GnffE5Ln0fTYbH8nMRnqm65kzBZWfE05Zj/NoqIXpCgjr6MhLkyFi9vsCAwEA AQKCAgAA96baQcWr9SLmQOR4NOwLEhQAMWefpWCZhU3amB4FgEVR1mmJjnw868RW t0v36jH0Dl44us9K6o2Ab+jCi9JTtbWM2Osk6JNkwSlVtsSPVH2KxbbmTTExH50N sYE3tPj12rlB7isXpRrOzlRwzWZmJBHOtrFlAsdKFYCQc03vdXlKGkBv1BuSXYP/ 8W5ltSYXMspxehkOZvhaIejbFREMPbzDvGlDER1a7Q320qQ7kUr7ISvbY1XJUzj1 f1HwgEA6w/AhED5Jv6wfgvx+8Yo9hYnflTPbsO1XRS4x7kJxGHTMlFuEsSF1ICYH Bcos0wUiGcBO2N6uAFuhe98BBn+nOwAPZYWwGkmVuK2psm2mXAHx94GT/XqgK/1r VWGSoOV7Fhjauc2Nv8/vJU18DXT3OY5hc4iXVeEBkuZwRb/NVUtnFoHxVO/Mp5Fh /W5KZaLWVrLghzvSQ/KUIM0k4lfKDZpY9ZpOdNgWDyZY8tNrXumUZZimzWdXZ9vR dBssmd8qEKs1AHGFnMDt56IjLGou6j0qnWsLdR1e/WEFsYzGXLVHCv6vXRNkbjqh WFw5nA+2Dw1YAsy+YkTfgx2pOe+exM/wxsVPa7tG9oZ374dywUi1k6VoHw5dkmJw 1hbXqSLZtx2N51G+SpGmNAV4vLUF0y3dy2wnrzFkFT4uxh1w8QKCAQEA+h6LwHTK hgcJx6CQQ6zYRqXo4wdvMooY1FcqJOq7LvJUA2CX5OOLs8qN1TyFrOCuAUTurOrM ABlQ0FpsIaP8TOGz72dHe2eLB+dD6Bqjn10sEFMn54zWd/w9ympQrO9jb5X3ViTh sCcdYyXVS9Hz8nzbbIF+DaKlxF2Hh71uRDxXpMPxRcGbOIuKZXUj6RkTIulzqT6o uawlegWxch05QSgzq/1ASxtjTzo4iuDCAii3N45xqxnB+fV9NXEt4R2oOGquBRPJ LxKcOnaQKBD0YNX4muTq+zPlv/kOb8/ys2WGWDUrNkpyJXqhTve4KONjqM7+iL/U 4WdJuiCjonzk/QKCAQEA3Lc+kNq35FNLxMcnCVcUgkmiCWZ4dyGZZPdqjOPww1+n bbudGPzY1nxOvE60dZM4or/tm6qlXYfb2UU3+OOJrK9s297EQybZ8DTZu2GHyitc NSFV3Gl4cgvKdbieGKkk9X2dV9xSNesNvX9lJEnQxuwHDTeo8ubLHtV88Ml1xokn 7W+IFiyEuUIL4e5/fadbrI3EwMrbCF4+9VcfABx4PTNMzdc8LsncCMXE+jFX8AWp TsT2JezTe5o2WpvBoKMAYhJQNQiaWATn00pDVY/70H1vK3ljomAa1IUdOr/AhAF7 3jL0MYMgXSHzXZOKAtc7yf+QfFWF1Ls8+sen1clJVwKCAQEAp59rB0r+Iz56RmgL 5t7ifs5XujbURemY5E2aN+18DuVmenD0uvfoO1DnJt4NtCNLWhxpXEdq+jH9H/VJ fG4a+ydT4IC1vjVRTrWlo9qeh4H4suQX3S1c2kKY4pvHf25blH/Lp9bFzbkZD8Ze IRcOxxb4MsrBwL+dGnGYD9dbG63ZCtoqSxaKQSX7VS1hKKmeUopj8ivFBdIht5oz JogBQ/J+Vqg9u1gagRFCrYgdXTcOOtRix0lW336vL+6u0ax/fXe5MjvlW3+8Zc3p pIBgVrlvh9ccx8crFTIDg9m4DJRgqaLQV+0ifI2np3WK3RQvSQWYPetZ7sm69ltD bvUGvQKCAQAz5CEhjUqOs8asjOXwnDiGKSmfbCgGWi/mPQUf+rcwN9z1P5a/uTKB utgIDbj/q401Nkp2vrgCNV7KxitSqKxFnTjKuKUL5KZ4gvRtyZBTR751/1BgcauP pJYE91K0GZBG5zGG5pWtd4XTd5Af5/rdycAeq2ddNEWtCiRFuBeohbaNbBtimzTZ GV4R0DDJKf+zoeEQMqEsZnwG0mTHceoS+WylOGU92teQeG7HI7K5C5uymTwFzpgq ByegRd5QFgKRDB0vWsZuyzh1xI/wHdnmOpdYcUGre0zTijhFB7ALWQ32P6SJv3ps av78kSNxZ4j3BM7DbJf6W8sKasZazOghAoIBAHekpBcLq9gRv2+NfLYxWN2sTZVB 1ldwioG7rWvk5YQR2akukecI3NRjtC5gG2vverawG852Y4+oLfgRMHxgp0qNStwX juTykzPkCwZn8AyR+avC3mkrtJyM3IigcYOu4/UoaRDFa0xvCC1EfumpnKXIpHag miSQZf2sVbgqb3/LWvHIg/ceOP9oGJve87/HVfQtBoLaIe5RXCWkqB7mcI/exvTS 8ShaW6v2Fe5Bzdvawj7sbsVYRWe93Aq2tmIgSX320D2RVepb6mjD4nr0IUaM3Yed TFT7e2ikWXyDLLgVkDTU4Qe8fr3ZKGfanCIDzvgNw6H1gRi+2WQgOmjilMQ= -----END RSA PRIVATE KEY----- neutron-12.1.1/neutron/tests/var/privatekey.key0000664000175000017500000000625313553660046021647 0ustar zuulzuul00000000000000-----BEGIN RSA PRIVATE KEY----- MIIJKAIBAAKCAgEA16VJEDeqbmr6PoM96NSuJK1XT5dZuzYzSQ8g//mR9BBjXBDe 4moNajxOybI6hjzWbECtXTKF20s/jkovarzXiZwXH8FMeakwLcMgG/QMRpMLjGny FPpVm7HJaPnTxrI2tNcsG10wmWxd9oqp6TjGIX8VlHaEGIgZIccYVvXjDyi0vypD /P28flWmtlyYgHm6pHfZ65LAAAXhnPZpWn2ARJogoT3SRD8PtXjwOEFavWj3qQ7K gCrRjfCS6ZqAtwXcUEE228C90PH01yuLQjVGlZOAGw8vzHBaustKHEKATyY4oTmN +Zlhvzi7XCPfcjzqVhp6bP+Whv+uAwydg+uxZ2o+oCh1fuk1xTvCmcZZ8bYLYmQy QWZJ3kwbfQK0jr/pejQbLpkc9IhCeKOB9Utk0jJ6awL1+1pxrXOl4vYF2oWHAxxH pcMGM6gIkwb+ocUqeDGdnTV2viszorQu2W1dqrINGrtMI3xP6EkNzb7L1K/Jzpn7 rSU7x0QMGwtb+Bv7bgLDuztMNtLtgd7vqRtOpufq5xKqfqwfYZrpEWE34BBUUbFS L6RZf3MLz1ykXF9N1CDMfpS6/Rbfnqe2KKAYWN8GNpMAsQ+JUWDZm8LAiFcsGbeN H/+GnffE5Ln0fTYbH8nMRnqm65kzBZWfE05Zj/NoqIXpCgjr6MhLkyFi9vsCAwEA AQKCAgAA96baQcWr9SLmQOR4NOwLEhQAMWefpWCZhU3amB4FgEVR1mmJjnw868RW t0v36jH0Dl44us9K6o2Ab+jCi9JTtbWM2Osk6JNkwSlVtsSPVH2KxbbmTTExH50N sYE3tPj12rlB7isXpRrOzlRwzWZmJBHOtrFlAsdKFYCQc03vdXlKGkBv1BuSXYP/ 8W5ltSYXMspxehkOZvhaIejbFREMPbzDvGlDER1a7Q320qQ7kUr7ISvbY1XJUzj1 f1HwgEA6w/AhED5Jv6wfgvx+8Yo9hYnflTPbsO1XRS4x7kJxGHTMlFuEsSF1ICYH Bcos0wUiGcBO2N6uAFuhe98BBn+nOwAPZYWwGkmVuK2psm2mXAHx94GT/XqgK/1r VWGSoOV7Fhjauc2Nv8/vJU18DXT3OY5hc4iXVeEBkuZwRb/NVUtnFoHxVO/Mp5Fh /W5KZaLWVrLghzvSQ/KUIM0k4lfKDZpY9ZpOdNgWDyZY8tNrXumUZZimzWdXZ9vR dBssmd8qEKs1AHGFnMDt56IjLGou6j0qnWsLdR1e/WEFsYzGXLVHCv6vXRNkbjqh WFw5nA+2Dw1YAsy+YkTfgx2pOe+exM/wxsVPa7tG9oZ374dywUi1k6VoHw5dkmJw 1hbXqSLZtx2N51G+SpGmNAV4vLUF0y3dy2wnrzFkFT4uxh1w8QKCAQEA+h6LwHTK hgcJx6CQQ6zYRqXo4wdvMooY1FcqJOq7LvJUA2CX5OOLs8qN1TyFrOCuAUTurOrM ABlQ0FpsIaP8TOGz72dHe2eLB+dD6Bqjn10sEFMn54zWd/w9ympQrO9jb5X3ViTh sCcdYyXVS9Hz8nzbbIF+DaKlxF2Hh71uRDxXpMPxRcGbOIuKZXUj6RkTIulzqT6o uawlegWxch05QSgzq/1ASxtjTzo4iuDCAii3N45xqxnB+fV9NXEt4R2oOGquBRPJ LxKcOnaQKBD0YNX4muTq+zPlv/kOb8/ys2WGWDUrNkpyJXqhTve4KONjqM7+iL/U 4WdJuiCjonzk/QKCAQEA3Lc+kNq35FNLxMcnCVcUgkmiCWZ4dyGZZPdqjOPww1+n bbudGPzY1nxOvE60dZM4or/tm6qlXYfb2UU3+OOJrK9s297EQybZ8DTZu2GHyitc NSFV3Gl4cgvKdbieGKkk9X2dV9xSNesNvX9lJEnQxuwHDTeo8ubLHtV88Ml1xokn 7W+IFiyEuUIL4e5/fadbrI3EwMrbCF4+9VcfABx4PTNMzdc8LsncCMXE+jFX8AWp TsT2JezTe5o2WpvBoKMAYhJQNQiaWATn00pDVY/70H1vK3ljomAa1IUdOr/AhAF7 3jL0MYMgXSHzXZOKAtc7yf+QfFWF1Ls8+sen1clJVwKCAQEAp59rB0r+Iz56RmgL 5t7ifs5XujbURemY5E2aN+18DuVmenD0uvfoO1DnJt4NtCNLWhxpXEdq+jH9H/VJ fG4a+ydT4IC1vjVRTrWlo9qeh4H4suQX3S1c2kKY4pvHf25blH/Lp9bFzbkZD8Ze IRcOxxb4MsrBwL+dGnGYD9dbG63ZCtoqSxaKQSX7VS1hKKmeUopj8ivFBdIht5oz JogBQ/J+Vqg9u1gagRFCrYgdXTcOOtRix0lW336vL+6u0ax/fXe5MjvlW3+8Zc3p pIBgVrlvh9ccx8crFTIDg9m4DJRgqaLQV+0ifI2np3WK3RQvSQWYPetZ7sm69ltD bvUGvQKCAQAz5CEhjUqOs8asjOXwnDiGKSmfbCgGWi/mPQUf+rcwN9z1P5a/uTKB utgIDbj/q401Nkp2vrgCNV7KxitSqKxFnTjKuKUL5KZ4gvRtyZBTR751/1BgcauP pJYE91K0GZBG5zGG5pWtd4XTd5Af5/rdycAeq2ddNEWtCiRFuBeohbaNbBtimzTZ GV4R0DDJKf+zoeEQMqEsZnwG0mTHceoS+WylOGU92teQeG7HI7K5C5uymTwFzpgq ByegRd5QFgKRDB0vWsZuyzh1xI/wHdnmOpdYcUGre0zTijhFB7ALWQ32P6SJv3ps av78kSNxZ4j3BM7DbJf6W8sKasZazOghAoIBAHekpBcLq9gRv2+NfLYxWN2sTZVB 1ldwioG7rWvk5YQR2akukecI3NRjtC5gG2vverawG852Y4+oLfgRMHxgp0qNStwX juTykzPkCwZn8AyR+avC3mkrtJyM3IigcYOu4/UoaRDFa0xvCC1EfumpnKXIpHag miSQZf2sVbgqb3/LWvHIg/ceOP9oGJve87/HVfQtBoLaIe5RXCWkqB7mcI/exvTS 8ShaW6v2Fe5Bzdvawj7sbsVYRWe93Aq2tmIgSX320D2RVepb6mjD4nr0IUaM3Yed TFT7e2ikWXyDLLgVkDTU4Qe8fr3ZKGfanCIDzvgNw6H1gRi+2WQgOmjilMQ= -----END RSA PRIVATE KEY----- neutron-12.1.1/neutron/tests/var/certificate.crt0000664000175000017500000000350213553660046021740 0ustar zuulzuul00000000000000-----BEGIN CERTIFICATE----- MIIFLjCCAxYCAQEwDQYJKoZIhvcNAQEFBQAwYTELMAkGA1UEBhMCQVUxEzARBgNV BAgTClNvbWUtU3RhdGUxFTATBgNVBAoTDE9wZW5zdGFjayBDQTESMBAGA1UECxMJ R2xhbmNlIENBMRIwEAYDVQQDEwlHbGFuY2UgQ0EwHhcNMTIwMjA5MTcxMDUzWhcN MjIwMjA2MTcxMDUzWjBZMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0 ZTESMBAGA1UEChMJT3BlbnN0YWNrMQ8wDQYDVQQLEwZHbGFuY2UxEDAOBgNVBAMT BzAuMC4wLjAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXpUkQN6pu avo+gz3o1K4krVdPl1m7NjNJDyD/+ZH0EGNcEN7iag1qPE7JsjqGPNZsQK1dMoXb Sz+OSi9qvNeJnBcfwUx5qTAtwyAb9AxGkwuMafIU+lWbsclo+dPGsja01ywbXTCZ bF32iqnpOMYhfxWUdoQYiBkhxxhW9eMPKLS/KkP8/bx+Vaa2XJiAebqkd9nrksAA BeGc9mlafYBEmiChPdJEPw+1ePA4QVq9aPepDsqAKtGN8JLpmoC3BdxQQTbbwL3Q 8fTXK4tCNUaVk4AbDy/McFq6y0ocQoBPJjihOY35mWG/OLtcI99yPOpWGnps/5aG /64DDJ2D67Fnaj6gKHV+6TXFO8KZxlnxtgtiZDJBZkneTBt9ArSOv+l6NBsumRz0 iEJ4o4H1S2TSMnprAvX7WnGtc6Xi9gXahYcDHEelwwYzqAiTBv6hxSp4MZ2dNXa+ KzOitC7ZbV2qsg0au0wjfE/oSQ3NvsvUr8nOmfutJTvHRAwbC1v4G/tuAsO7O0w2 0u2B3u+pG06m5+rnEqp+rB9hmukRYTfgEFRRsVIvpFl/cwvPXKRcX03UIMx+lLr9 Ft+ep7YooBhY3wY2kwCxD4lRYNmbwsCIVywZt40f/4ad98TkufR9NhsfycxGeqbr mTMFlZ8TTlmP82iohekKCOvoyEuTIWL2+wIDAQABMA0GCSqGSIb3DQEBBQUAA4IC AQBMUBgV0R+Qltf4Du7u/8IFmGAoKR/mktB7R1gRRAqsvecUt7kIwBexGdavGg1y 0pU0+lgUZjJ20N1SlPD8gkNHfXE1fL6fmMjWz4dtYJjzRVhpufHPeBW4tl8DgHPN rBGAYQ+drDSXaEjiPQifuzKx8WS+DGA3ki4co5mPjVnVH1xvLIdFsk89z3b3YD1k yCJ/a9K36x6Z/c67JK7s6MWtrdRF9+MVnRKJ2PK4xznd1kBz16V+RA466wBDdARY vFbtkafbEqOb96QTonIZB7+fAldKDPZYnwPqasreLmaGOaM8sxtlPYAJ5bjDONbc AaXG8BMRQyO4FyH237otDKlxPyHOFV66BaffF5S8OlwIMiZoIvq+IcTZOdtDUSW2 KHNLfe5QEDZdKjWCBrfqAfvNuG13m03WqfmcMHl3o/KiPJlx8l9Z4QEzZ9xcyQGL cncgeHM9wJtzi2cD/rTDNFsx/gxvoyutRmno7I3NRbKmpsXF4StZioU3USRspB07 hYXOVnG3pS+PjVby7ThT3gvFHSocguOsxClx1epdUJAmJUbmM7NmOp5WVBVtMtC2 Su4NG/xJciXitKzw+btb7C7RjO6OEqv/1X/oBDzKBWQAwxUC+lqmnM7W6oqWJFEM YfTLnrjs7Hj6ThMGcEnfvc46dWK3dz0RjsQzUxugPuEkLA== -----END CERTIFICATE----- neutron-12.1.1/neutron/tests/var/ca.crt0000664000175000017500000000415713553660046020050 0ustar zuulzuul00000000000000-----BEGIN CERTIFICATE----- MIIGDDCCA/SgAwIBAgIJAPSvwQYk4qI4MA0GCSqGSIb3DQEBBQUAMGExCzAJBgNV BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMRUwEwYDVQQKEwxPcGVuc3RhY2sg Q0ExEjAQBgNVBAsTCUdsYW5jZSBDQTESMBAGA1UEAxMJR2xhbmNlIENBMB4XDTEy MDIwOTE3MTAwMloXDTIyMDIwNjE3MTAwMlowYTELMAkGA1UEBhMCQVUxEzARBgNV BAgTClNvbWUtU3RhdGUxFTATBgNVBAoTDE9wZW5zdGFjayBDQTESMBAGA1UECxMJ R2xhbmNlIENBMRIwEAYDVQQDEwlHbGFuY2UgQ0EwggIiMA0GCSqGSIb3DQEBAQUA A4ICDwAwggIKAoICAQDmf+fapWfzy1Uylus0KGalw4X/5xZ+ltPVOr+IdCPbstvi RTC5g+O+TvXeOP32V/cnSY4ho/+f2q730za+ZA/cgWO252rcm3Q7KTJn3PoqzJvX /l3EXe3/TCrbzgZ7lW3QLTCTEE2eEzwYG3wfDTOyoBq+F6ct6ADh+86gmpbIRfYI N+ixB0hVyz9427PTof97fL7qxxkjAayB28OfwHrkEBl7iblNhUC0RoH+/H9r5GEl GnWiebxfNrONEHug6PHgiaGq7/Dj+u9bwr7J3/NoS84I08ajMnhlPZxZ8bS/O8If ceWGZv7clPozyhABT/otDfgVcNH1UdZ4zLlQwc1MuPYN7CwxrElxc8Quf94ttGjb tfGTl4RTXkDofYdG1qBWW962PsGl2tWmbYDXV0q5JhV/IwbrE1X9f+OksJQne1/+ dZDxMhdf2Q1V0P9hZZICu4+YhmTMs5Mc9myKVnzp4NYdX5fXoB/uNYph+G7xG5IK WLSODKhr1wFGTTcuaa8LhOH5UREVenGDJuc6DdgX9a9PzyJGIi2ngQ03TJIkCiU/ 4J/r/vsm81ezDiYZSp2j5JbME+ixW0GBLTUWpOIxUSHgUFwH5f7lQwbXWBOgwXQk BwpZTmdQx09MfalhBtWeu4/6BnOCOj7e/4+4J0eVxXST0AmVyv8YjJ2nz1F9oQID AQABo4HGMIHDMB0GA1UdDgQWBBTk7Krj4bEsTjHXaWEtI2GZ5ACQyTCBkwYDVR0j BIGLMIGIgBTk7Krj4bEsTjHXaWEtI2GZ5ACQyaFlpGMwYTELMAkGA1UEBhMCQVUx EzARBgNVBAgTClNvbWUtU3RhdGUxFTATBgNVBAoTDE9wZW5zdGFjayBDQTESMBAG A1UECxMJR2xhbmNlIENBMRIwEAYDVQQDEwlHbGFuY2UgQ0GCCQD0r8EGJOKiODAM BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQA8Zrss/MiwFHGmDlercE0h UvzA54n/EvKP9nP3jHM2qW/VPfKdnFw99nEPFLhb+lN553vdjOpCYFm+sW0Z5Mi4 qsFkk4AmXIIEFOPt6zKxMioLYDQ9Sw/BUv6EZGeANWr/bhmaE+dMcKJt5le/0jJm 2ahsVB9fbFu9jBFeYb7Ba/x2aLkEGMxaDLla+6EQhj148fTnS1wjmX9G2cNzJvj/ +C2EfKJIuDJDqw2oS2FGVpP37FA2Bz2vga0QatNneLkGKCFI3ZTenBznoN+fmurX TL3eJE4IFNrANCcdfMpdyLAtXz4KpjcehqpZMu70er3d30zbi1l0Ajz4dU+WKz/a NQES+vMkT2wqjXHVTjrNwodxw3oLK/EuTgwoxIHJuplx5E5Wrdx9g7Gl1PBIJL8V xiOYS5N7CakyALvdhP7cPubA2+TPAjNInxiAcmhdASS/Vrmpvrkat6XhGn8h9liv ysDOpMQmYQkmgZBpW8yBKK7JABGGsJADJ3E6J5MMWBX2RR4kFoqVGAzdOU3oyaTy I0kz5sfuahaWpdYJVlkO+esc0CRXw8fLDYivabK2tOgUEWeZsZGZ9uK6aV1VxTAY 9Guu3BJ4Rv/KP/hk7mP8rIeCwotV66/2H8nq72ImQhzSVyWcxbFf2rJiFQJ3BFwA WoRMgEwjGJWqzhJZUYpUAQ== -----END CERTIFICATE----- neutron-12.1.1/neutron/tests/functional/0000775000175000017500000000000013553660156020320 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/sanity/0000775000175000017500000000000013553660156021627 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/sanity/test_sanity.py0000664000175000017500000000571013553660047024551 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.cmd.sanity import checks from neutron.tests import base from neutron.tests.functional import base as functional_base class SanityTestCase(base.BaseTestCase): """Sanity checks that do not require root access. Tests that just call checks.some_function() are to ensure that neutron-sanity-check runs without throwing an exception, as in the case where someone modifies the API without updating the check script. """ def test_nova_notify_runs(self): checks.nova_notify_supported() def test_dnsmasq_version(self): checks.dnsmasq_version_supported() def test_dibbler_version(self): checks.dibbler_version_supported() def test_ipset_support(self): checks.ipset_supported() def test_ip6tables_support(self): checks.ip6tables_supported() class SanityTestCaseRoot(functional_base.BaseSudoTestCase): """Sanity checks that require root access. Tests that just call checks.some_function() are to ensure that neutron-sanity-check runs without throwing an exception, as in the case where someone modifies the API without updating the check script. """ def test_ovs_vxlan_support_runs(self): checks.ovs_vxlan_supported() def test_ovs_geneve_support_runs(self): checks.ovs_geneve_supported() def test_iproute2_vxlan_support_runs(self): checks.iproute2_vxlan_supported() def test_ovs_patch_support_runs(self): checks.patch_supported() def test_arp_responder_runs(self): checks.arp_responder_supported() def test_arp_header_match_runs(self): checks.arp_header_match_supported() def test_icmpv6_header_match_runs(self): checks.icmpv6_header_match_supported() def test_vf_management_runs(self): checks.vf_management_supported() def test_vf_extended_management_runs(self): checks.vf_extended_management_supported() def test_namespace_root_read_detection_runs(self): checks.netns_read_requires_helper() def test_ovsdb_native_supported_runs(self): checks.ovsdb_native_supported() def test_keepalived_ipv6_support(self): checks.keepalived_ipv6_supported() def test_bridge_firewalling_enabled(self): checks.bridge_firewalling_enabled() def test_ip_nonlocal_bind(self): checks.ip_nonlocal_bind() neutron-12.1.1/neutron/tests/functional/sanity/__init__.py0000664000175000017500000000000013553660046023724 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/0000775000175000017500000000000013553660156022143 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/l3_router/0000775000175000017500000000000013553660156024061 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py0000664000175000017500000030475413553660047031655 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import external_net as extnet_apidef from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib.exceptions import agent as agent_exc from neutron_lib import constants from neutron_lib import context from neutron.api.rpc.handlers import l3_rpc from neutron.common import constants as n_const from neutron.common import topics from neutron.tests.common import helpers from neutron.tests.unit.plugins.ml2 import base as ml2_test_base DEVICE_OWNER_COMPUTE = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' class L3DvrTestCaseBase(ml2_test_base.ML2TestFramework): def setUp(self): super(L3DvrTestCaseBase, self).setUp() self.l3_agent = helpers.register_l3_agent( host="host0", agent_mode=constants.L3_AGENT_MODE_DVR_SNAT) # register OVS agents to avoid time wasted on committing # port binding failures on every port update helpers.register_ovs_agent(host='host1') helpers.register_ovs_agent(host='host2') def _create_router(self, distributed=True, ha=False, admin_state_up=True): return (super(L3DvrTestCaseBase, self). _create_router(distributed=distributed, ha=ha, admin_state_up=admin_state_up)) class L3DvrTestCase(L3DvrTestCaseBase): def test_update_router_db_centralized_to_distributed(self): router = self._create_router(distributed=False) # router needs to be in admin state down in order to be upgraded to DVR self.l3_plugin.update_router( self.context, router['id'], {'router': {'admin_state_up': False}}) self.assertFalse(router['distributed']) self.l3_plugin.update_router( self.context, router['id'], {'router': {'distributed': True}}) router = self.l3_plugin.get_router(self.context, router['id']) self.assertTrue(router['distributed']) def test_get_device_owner_distributed_router_object(self): router = self._create_router() self.assertEqual( constants.DEVICE_OWNER_DVR_INTERFACE, self.l3_plugin._get_device_owner(self.context, router)) def test_get_device_owner_distributed_router_id(self): router = self._create_router() self.assertEqual( constants.DEVICE_OWNER_DVR_INTERFACE, self.l3_plugin._get_device_owner(self.context, router['id'])) def test_get_device_owner_centralized(self): router = self._create_router(distributed=False) self.assertEqual( constants.DEVICE_OWNER_ROUTER_INTF, self.l3_plugin._get_device_owner(self.context, router['id'])) def test_get_agent_gw_ports_exist_for_network_no_port(self): self.assertIsNone( self.l3_plugin._get_agent_gw_ports_exist_for_network( self.context, 'network_id', 'host', 'agent_id')) def test_csnat_ports_are_created_and_deleted_based_on_router_subnet(self): kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} net1 = self._make_network(self.fmt, 'net1', True) subnet1 = self._make_subnet( self.fmt, net1, '10.1.0.1', '10.1.0.0/24', enable_dhcp=True) subnet2 = self._make_subnet( self.fmt, net1, '10.2.0.1', '10.2.0.0/24', enable_dhcp=True) ext_net = self._make_network(self.fmt, 'ext_net', True, **kwargs) self._make_subnet( self.fmt, ext_net, '20.0.0.1', '20.0.0.0/24', enable_dhcp=True) # Create first router and add an interface router1 = self._create_router() ext_net_id = ext_net['network']['id'] net1_id = net1['network']['id'] # Set gateway to router self.l3_plugin._update_router_gw_info( self.context, router1['id'], {'network_id': ext_net_id}) # Now add router interface (subnet1) from net1 to router self.l3_plugin.add_router_interface( self.context, router1['id'], {'subnet_id': subnet1['subnet']['id']}) # Now add router interface (subnet2) from net1 to router self.l3_plugin.add_router_interface( self.context, router1['id'], {'subnet_id': subnet2['subnet']['id']}) # Now check the valid snat interfaces passed to the agent snat_router_intfs = self.l3_plugin._get_snat_sync_interfaces( self.context, [router1['id']]) self.assertEqual(2, len(snat_router_intfs[router1['id']])) # Also make sure that there are no csnat ports created and # left over. csnat_ports = self.core_plugin.get_ports( self.context, filters={ 'network_id': [net1_id], 'device_owner': [constants.DEVICE_OWNER_ROUTER_SNAT]}) self.assertEqual(2, len(csnat_ports)) # Now remove router interface (subnet1) from net1 to router self.l3_plugin.remove_router_interface( self.context, router1['id'], {'subnet_id': subnet1['subnet']['id']}) # Now remove router interface (subnet2) from net1 to router self.l3_plugin.remove_router_interface( self.context, router1['id'], {'subnet_id': subnet2['subnet']['id']}) snat_router_intfs = self.l3_plugin._get_snat_sync_interfaces( self.context, [router1['id']]) self.assertEqual(0, len(snat_router_intfs[router1['id']])) # Also make sure that there are no csnat ports created and # left over. csnat_ports = self.core_plugin.get_ports( self.context, filters={ 'network_id': [net1_id], 'device_owner': [constants.DEVICE_OWNER_ROUTER_SNAT]}) self.assertEqual(0, len(csnat_ports)) def _test_remove_router_interface_leaves_snat_intact(self, by_subnet): with self.subnet() as subnet1, \ self.subnet(cidr='20.0.0.0/24') as subnet2: kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} with self.network(**kwargs) as ext_net, \ self.subnet(network=ext_net, cidr='30.0.0.0/24'): router = self._create_router() self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet1['subnet']['id']}) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet2['subnet']['id']}) gw_info = {'network_id': ext_net['network']['id']} self.l3_plugin.update_router( self.context, router['id'], {'router': {l3_apidef.EXTERNAL_GW_INFO: gw_info}}) snat_router_intfs = self.l3_plugin._get_snat_sync_interfaces( self.context, [router['id']]) self.assertEqual( 2, len(snat_router_intfs[router['id']])) if by_subnet: self.l3_plugin.remove_router_interface( self.context, router['id'], {'subnet_id': subnet1['subnet']['id']}) else: port = self.core_plugin.get_ports( self.context, filters={ 'network_id': [subnet1['subnet']['network_id']], 'device_owner': [constants.DEVICE_OWNER_DVR_INTERFACE]})[0] self.l3_plugin.remove_router_interface( self.context, router['id'], {'port_id': port['id']}) self.assertEqual( 1, len(self.l3_plugin._get_snat_sync_interfaces( self.context, [router['id']]))) def test_remove_router_interface_by_subnet_leaves_snat_intact(self): self._test_remove_router_interface_leaves_snat_intact(by_subnet=True) def test_remove_router_interface_by_port_leaves_snat_intact(self): self._test_remove_router_interface_leaves_snat_intact( by_subnet=False) def setup_create_agent_gw_port_for_network(self, network=None): if not network: network = self._make_network(self.fmt, '', True) network_id = network['network']['id'] port = self.core_plugin.create_port( self.context, {'port': {'tenant_id': '', 'network_id': network_id, 'mac_address': constants.ATTR_NOT_SPECIFIED, 'fixed_ips': constants.ATTR_NOT_SPECIFIED, 'device_id': self.l3_agent['id'], 'device_owner': constants.DEVICE_OWNER_AGENT_GW, portbindings.HOST_ID: '', 'admin_state_up': True, 'name': ''}}) return network_id, port def test_get_agent_gw_port_for_network(self): network_id, port = ( self.setup_create_agent_gw_port_for_network()) self.assertEqual( port['id'], self.l3_plugin._get_agent_gw_ports_exist_for_network( self.context, network_id, None, self.l3_agent['id'])['id']) def test_delete_agent_gw_port_for_network(self): network_id, port = ( self.setup_create_agent_gw_port_for_network()) self.l3_plugin.delete_floatingip_agent_gateway_port( self.context, "", network_id) self.assertIsNone( self.l3_plugin._get_agent_gw_ports_exist_for_network( self.context, network_id, "", self.l3_agent['id'])) def test_get_fip_agent_gw_ports(self): self.setup_create_agent_gw_port_for_network() self.assertEqual( 1, len(self.l3_plugin._get_fip_agent_gw_ports( self.context, self.l3_agent['id']))) def test_process_routers(self): router = self._create_router() if not router.get('gw_port_id'): router['gw_port_id'] = 'fake_gw_id' self.l3_plugin._get_fip_agent_gw_ports = mock.Mock( return_value='fip_interface') self.l3_plugin._get_snat_sync_interfaces = mock.Mock( return_value={router['id']: 'snat_interface'}) result = self.l3_plugin._process_routers(self.context, [router], self.l3_agent) self.assertEqual( router['id'], result[router['id']]['id']) self.assertIn(n_const.FLOATINGIP_AGENT_INTF_KEY, result[router['id']]) self.l3_plugin._get_fip_agent_gw_ports.assert_called_once_with( self.context, self.l3_agent['id']) self.l3_plugin._get_snat_sync_interfaces.assert_called_once_with( self.context, [router['id']]) def test_agent_gw_port_delete_when_last_gateway_for_ext_net_removed(self): kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} net1 = self._make_network(self.fmt, 'net1', True) net2 = self._make_network(self.fmt, 'net2', True) subnet1 = self._make_subnet( self.fmt, net1, '10.1.0.1', '10.1.0.0/24', enable_dhcp=True) subnet2 = self._make_subnet( self.fmt, net2, '10.1.0.1', '10.1.0.0/24', enable_dhcp=True) ext_net = self._make_network(self.fmt, 'ext_net', True, **kwargs) self._make_subnet( self.fmt, ext_net, '20.0.0.1', '20.0.0.0/24', enable_dhcp=True) # Create first router and add an interface router1 = self._create_router() ext_net_id = ext_net['network']['id'] self.l3_plugin.add_router_interface( self.context, router1['id'], {'subnet_id': subnet1['subnet']['id']}) # Set gateway to first router self.l3_plugin._update_router_gw_info( self.context, router1['id'], {'network_id': ext_net_id}) # Create second router and add an interface router2 = self._create_router() self.l3_plugin.add_router_interface( self.context, router2['id'], {'subnet_id': subnet2['subnet']['id']}) # Set gateway to second router self.l3_plugin._update_router_gw_info( self.context, router2['id'], {'network_id': ext_net_id}) # Create an agent gateway port for the external network net_id, agent_gw_port = ( self.setup_create_agent_gw_port_for_network(network=ext_net)) # Check for agent gateway ports self.assertIsNotNone( self.l3_plugin._get_agent_gw_ports_exist_for_network( self.context, ext_net_id, "", self.l3_agent['id'])) self.l3_plugin._update_router_gw_info( self.context, router1['id'], {}) # Check for agent gateway port after deleting one of the gw self.assertIsNotNone( self.l3_plugin._get_agent_gw_ports_exist_for_network( self.context, ext_net_id, "", self.l3_agent['id'])) self.l3_plugin._update_router_gw_info( self.context, router2['id'], {}) # Check for agent gateway port after deleting last gw self.assertIsNone( self.l3_plugin._get_agent_gw_ports_exist_for_network( self.context, ext_net_id, "", self.l3_agent['id'])) def test_create_floating_ip_with_no_dvr_agents(self): self._test_create_floating_ip_agent_notification( test_agent_mode=None) def _test_create_floating_ip_agent_notification( self, dvr=True, test_agent_mode=constants.L3_AGENT_MODE_DVR): with self.subnet() as ext_subnet,\ self.subnet(cidr='20.0.0.0/24') as int_subnet,\ self.port(subnet=int_subnet, device_owner=DEVICE_OWNER_COMPUTE) as int_port: self.core_plugin.update_port( self.context, int_port['port']['id'], {'port': {portbindings.HOST_ID: 'host1'}}) # and create l3 agents on corresponding hosts if test_agent_mode is not None: helpers.register_l3_agent(host='host1', agent_mode=test_agent_mode) # make net external ext_net_id = ext_subnet['subnet']['network_id'] self._update('networks', ext_net_id, {'network': {extnet_apidef.EXTERNAL: True}}) router = self._create_router(distributed=dvr) self.l3_plugin.update_router( self.context, router['id'], {'router': { 'external_gateway_info': {'network_id': ext_net_id}}}) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': int_subnet['subnet']['id']}) if test_agent_mode is None: self.assertRaises( agent_exc.AgentNotFoundByTypeHost, self.l3_plugin.create_fip_agent_gw_port_if_not_exists, self.context, ext_net_id, 'host1') return floating_ip = {'floating_network_id': ext_net_id, 'router_id': router['id'], 'port_id': int_port['port']['id'], 'tenant_id': int_port['port']['tenant_id'], 'dns_name': '', 'dns_domain': ''} with mock.patch.object( self.l3_plugin, '_l3_rpc_notifier') as l3_notif: self.l3_plugin.create_floatingip( self.context, {'floatingip': floating_ip}) if dvr: if test_agent_mode == ( n_const.L3_AGENT_MODE_DVR_NO_EXTERNAL): if router['ha']: expected_calls = [ mock.call(self.context, [router['id']], 'host0'), mock.call(self.context, [router['id']], 'standby')] l3_notif.routers_updated_on_host.assert_has_calls( expected_calls, any_order=True) self.assertFalse(l3_notif.routers_updated.called) if not router['ha']: l3_notif.routers_updated_on_host.\ assert_called_once_with( self.context, [router['id']], 'host0') self.assertFalse(l3_notif.routers_updated.called) else: l3_notif.routers_updated_on_host.\ assert_called_once_with( self.context, [router['id']], 'host1') self.assertFalse(l3_notif.routers_updated.called) else: l3_notif.routers_updated.assert_called_once_with( self.context, [router['id']], None) self.assertFalse( l3_notif.routers_updated_on_host.called) def test_create_floating_ip_agent_notification(self): self._test_create_floating_ip_agent_notification() def test_create_floating_ip_agent_notification_for_dvr_no_external_agent( self): agent_mode = n_const.L3_AGENT_MODE_DVR_NO_EXTERNAL self._test_create_floating_ip_agent_notification( test_agent_mode=agent_mode) def test_create_floating_ip_agent_notification_non_dvr(self): self._test_create_floating_ip_agent_notification(dvr=False) def _test_update_floating_ip_agent_notification( self, dvr=True, test_agent_mode=constants.L3_AGENT_MODE_DVR): with self.subnet() as ext_subnet,\ self.subnet(cidr='20.0.0.0/24') as int_subnet1,\ self.subnet(cidr='30.0.0.0/24') as int_subnet2,\ self.port(subnet=int_subnet1, device_owner=DEVICE_OWNER_COMPUTE) as int_port1,\ self.port(subnet=int_subnet2, device_owner=DEVICE_OWNER_COMPUTE) as int_port2: # locate internal ports on different hosts self.core_plugin.update_port( self.context, int_port1['port']['id'], {'port': {portbindings.HOST_ID: 'host1'}}) self.core_plugin.update_port( self.context, int_port2['port']['id'], {'port': {portbindings.HOST_ID: 'host2'}}) # and create l3 agents on corresponding hosts helpers.register_l3_agent(host='host1', agent_mode=test_agent_mode) helpers.register_l3_agent(host='host2', agent_mode=test_agent_mode) # make net external ext_net_id = ext_subnet['subnet']['network_id'] self._update('networks', ext_net_id, {'network': {extnet_apidef.EXTERNAL: True}}) router1 = self._create_router(distributed=dvr) router2 = self._create_router(distributed=dvr) for router in (router1, router2): self.l3_plugin.update_router( self.context, router['id'], {'router': { 'external_gateway_info': {'network_id': ext_net_id}}}) self.l3_plugin.add_router_interface( self.context, router1['id'], {'subnet_id': int_subnet1['subnet']['id']}) self.l3_plugin.add_router_interface( self.context, router2['id'], {'subnet_id': int_subnet2['subnet']['id']}) floating_ip = {'floating_network_id': ext_net_id, 'router_id': router1['id'], 'port_id': int_port1['port']['id'], 'tenant_id': int_port1['port']['tenant_id'], 'dns_name': '', 'dns_domain': ''} floating_ip = self.l3_plugin.create_floatingip( self.context, {'floatingip': floating_ip}) with mock.patch.object( self.l3_plugin, '_l3_rpc_notifier') as l3_notif: updated_floating_ip = {'router_id': router2['id'], 'port_id': int_port2['port']['id']} self.l3_plugin.update_floatingip( self.context, floating_ip['id'], {'floatingip': updated_floating_ip}) if dvr: if test_agent_mode == ( n_const.L3_AGENT_MODE_DVR_NO_EXTERNAL): if router1['ha'] and router2['ha']: self.assertEqual( 4, l3_notif.routers_updated_on_host.call_count) expected_calls = [ mock.call(self.context, [router1['id']], 'host0'), mock.call(self.context, [router1['id']], 'standby'), mock.call(self.context, [router2['id']], 'host0'), mock.call(self.context, [router2['id']], 'standby')] l3_notif.routers_updated_on_host.assert_has_calls( expected_calls, any_order=True) self.assertFalse(l3_notif.routers_updated.called) else: self.assertEqual( 2, l3_notif.routers_updated_on_host.call_count) expected_calls = [ mock.call(self.context, [router1['id']], 'host0'), mock.call(self.context, [router2['id']], 'host0')] l3_notif.routers_updated_on_host.assert_has_calls( expected_calls) self.assertFalse(l3_notif.routers_updated.called) else: self.assertEqual( 2, l3_notif.routers_updated_on_host.call_count) expected_calls = [ mock.call(self.context, [router1['id']], 'host1'), mock.call(self.context, [router2['id']], 'host2')] l3_notif.routers_updated_on_host.assert_has_calls( expected_calls) self.assertFalse(l3_notif.routers_updated.called) else: self.assertEqual( 2, l3_notif.routers_updated.call_count) expected_calls = [ mock.call(self.context, [router1['id']], None), mock.call(self.context, [router2['id']], None)] l3_notif.routers_updated.assert_has_calls( expected_calls) self.assertFalse(l3_notif.routers_updated_on_host.called) def test_update_floating_ip_agent_notification(self): self._test_update_floating_ip_agent_notification() def test_update_floating_ip_agent_notification_with_dvr_no_external_agents( self): agent_mode = n_const.L3_AGENT_MODE_DVR_NO_EXTERNAL self._test_update_floating_ip_agent_notification( test_agent_mode=agent_mode) def test_update_floating_ip_agent_notification_non_dvr(self): self._test_update_floating_ip_agent_notification(dvr=False) def test_delete_floating_ip_with_no_agents(self): self._test_delete_floating_ip_agent_notification( test_agent_mode=None) def _test_delete_floating_ip_agent_notification( self, dvr=True, test_agent_mode=constants.L3_AGENT_MODE_DVR): with self.subnet() as ext_subnet,\ self.subnet(cidr='20.0.0.0/24') as int_subnet,\ self.port(subnet=int_subnet, device_owner=DEVICE_OWNER_COMPUTE) as int_port: self.core_plugin.update_port( self.context, int_port['port']['id'], {'port': {portbindings.HOST_ID: 'host1'}}) # and create l3 agents on corresponding hosts helpers.register_l3_agent(host='host1', agent_mode=test_agent_mode) # make net external ext_net_id = ext_subnet['subnet']['network_id'] self._update('networks', ext_net_id, {'network': {extnet_apidef.EXTERNAL: True}}) router = self._create_router(distributed=dvr) self.l3_plugin.update_router( self.context, router['id'], {'router': { 'external_gateway_info': {'network_id': ext_net_id}}}) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': int_subnet['subnet']['id']}) floating_ip = {'floating_network_id': ext_net_id, 'router_id': router['id'], 'port_id': int_port['port']['id'], 'tenant_id': int_port['port']['tenant_id'], 'dns_name': '', 'dns_domain': ''} floating_ip = self.l3_plugin.create_floatingip( self.context, {'floatingip': floating_ip}) if test_agent_mode is None: with mock.patch.object( self.l3_plugin, 'get_dvr_agent_on_host') as a_mock,\ mock.patch.object(self.l3_plugin, '_l3_rpc_notifier') as l3_notif: a_mock.return_value = None self.l3_plugin.delete_floatingip( self.context, floating_ip['id']) self.assertFalse( l3_notif.routers_updated_on_host.called) return with mock.patch.object( self.l3_plugin, '_l3_rpc_notifier') as l3_notif: self.l3_plugin.delete_floatingip( self.context, floating_ip['id']) if dvr: if test_agent_mode == ( n_const.L3_AGENT_MODE_DVR_NO_EXTERNAL): if router['ha']: expected_calls = [ mock.call(self.context, [router['id']], 'host0'), mock.call(self.context, [router['id']], 'standby')] l3_notif.routers_updated_on_host.assert_has_calls( expected_calls, any_order=True) self.assertFalse(l3_notif.routers_updated.called) else: l3_notif.routers_updated_on_host.\ assert_called_once_with( self.context, [router['id']], 'host0') self.assertFalse(l3_notif.routers_updated.called) if test_agent_mode == ( constants.L3_AGENT_MODE_DVR): l3_notif.routers_updated_on_host.\ assert_called_once_with( self.context, [router['id']], 'host1') self.assertFalse(l3_notif.routers_updated.called) else: l3_notif.routers_updated.assert_called_once_with( self.context, [router['id']], None) self.assertFalse( l3_notif.routers_updated_on_host.called) def test_delete_floating_ip_agent_notification(self): self._test_delete_floating_ip_agent_notification() def test_delete_floating_ip_agent_notification_with_dvr_no_external_agents( self): agent_mode = n_const.L3_AGENT_MODE_DVR_NO_EXTERNAL self._test_delete_floating_ip_agent_notification( test_agent_mode=agent_mode) def test_delete_floating_ip_agent_notification_non_dvr(self): self._test_delete_floating_ip_agent_notification(dvr=False) def test_router_with_ipv4_and_multiple_ipv6_on_same_network(self): kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} ext_net = self._make_network(self.fmt, '', True, **kwargs) self._make_subnet( self.fmt, ext_net, '10.0.0.1', '10.0.0.0/24', ip_version=4, enable_dhcp=True) self._make_subnet( self.fmt, ext_net, '2001:db8::1', '2001:db8::/64', ip_version=6, enable_dhcp=True) router1 = self._create_router() self.l3_plugin._update_router_gw_info( self.context, router1['id'], {'network_id': ext_net['network']['id']}) snat_router_intfs = self.l3_plugin._get_snat_sync_interfaces( self.context, [router1['id']]) self.assertEqual(0, len(snat_router_intfs[router1['id']])) private_net1 = self._make_network(self.fmt, 'net1', True) private_ipv6_subnet1 = self._make_subnet(self.fmt, private_net1, 'fd00::1', cidr='fd00::1/64', ip_version=6, ipv6_ra_mode='slaac', ipv6_address_mode='slaac') private_ipv6_subnet2 = self._make_subnet(self.fmt, private_net1, 'fd01::1', cidr='fd01::1/64', ip_version=6, ipv6_ra_mode='slaac', ipv6_address_mode='slaac') # Add the first IPv6 subnet to the router self.l3_plugin.add_router_interface( self.context, router1['id'], {'subnet_id': private_ipv6_subnet1['subnet']['id']}) # Check for the internal snat port interfaces snat_router_intfs = self.l3_plugin._get_snat_sync_interfaces( self.context, [router1['id']]) self.assertEqual(1, len(snat_router_intfs[router1['id']])) # Add the second IPv6 subnet to the router self.l3_plugin.add_router_interface( self.context, router1['id'], {'subnet_id': private_ipv6_subnet2['subnet']['id']}) # Check for the internal snat port interfaces snat_router_intfs = self.l3_plugin._get_snat_sync_interfaces( self.context, [router1['id']]) snat_intf_list = snat_router_intfs[router1['id']] fixed_ips = snat_intf_list[0]['fixed_ips'] self.assertEqual(1, len(snat_router_intfs[router1['id']])) self.assertEqual(2, len(fixed_ips)) # Now delete the router interface and it should update the # SNAT port with the right fixed_ips instead of deleting it. self.l3_plugin.remove_router_interface( self.context, router1['id'], {'subnet_id': private_ipv6_subnet2['subnet']['id']}) # Check for the internal snat port interfaces snat_router_intfs = self.l3_plugin._get_snat_sync_interfaces( self.context, [router1['id']]) snat_intf_list = snat_router_intfs[router1['id']] fixed_ips = snat_intf_list[0]['fixed_ips'] self.assertEqual(1, len(snat_router_intfs[router1['id']])) self.assertEqual(1, len(fixed_ips)) def test_unbound_allowed_addr_pairs_fip_with_multiple_active_vms(self): HOST1 = 'host1' helpers.register_l3_agent( host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR) HOST2 = 'host2' helpers.register_l3_agent( host=HOST2, agent_mode=constants.L3_AGENT_MODE_DVR) router = self._create_router(ha=False) private_net1 = self._make_network(self.fmt, 'net1', True) test_allocation_pools = [{'start': '10.1.0.2', 'end': '10.1.0.20'}] fixed_vrrp_ip = [{'ip_address': '10.1.0.201'}] kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} ext_net = self._make_network(self.fmt, '', True, **kwargs) self._make_subnet( self.fmt, ext_net, '10.20.0.1', '10.20.0.0/24', ip_version=4, enable_dhcp=True) self.l3_plugin.schedule_router(self.context, router['id'], candidates=[self.l3_agent]) # Set gateway to router self.l3_plugin._update_router_gw_info( self.context, router['id'], {'network_id': ext_net['network']['id']}) private_subnet1 = self._make_subnet( self.fmt, private_net1, '10.1.0.1', cidr='10.1.0.0/24', ip_version=4, allocation_pools=test_allocation_pools, enable_dhcp=True) vrrp_port = self._make_port( self.fmt, private_net1['network']['id'], device_owner='', fixed_ips=fixed_vrrp_ip) allowed_address_pairs = [ {'ip_address': '10.1.0.201', 'mac_address': vrrp_port['port']['mac_address']}] with self.port( subnet=private_subnet1, device_owner=DEVICE_OWNER_COMPUTE) as int_port1,\ self.port( subnet=private_subnet1, device_owner=DEVICE_OWNER_COMPUTE) as int_port2: self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': private_subnet1['subnet']['id']}) router_handle = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, self.l3_agent['host'], [router['id']])) self.assertEqual(self.l3_agent['host'], router_handle[0]['gw_port_host']) with mock.patch.object(self.l3_plugin, '_l3_rpc_notifier') as l3_notifier: vm_port1 = self.core_plugin.update_port( self.context, int_port1['port']['id'], {'port': {portbindings.HOST_ID: HOST1}}) vm_port2 = self.core_plugin.update_port( self.context, int_port2['port']['id'], {'port': {portbindings.HOST_ID: HOST2}}) vrrp_port_db = self.core_plugin.get_port( self.context, vrrp_port['port']['id']) # Make sure that the VRRP port is not bound to any host self.assertNotEqual(vrrp_port_db[portbindings.HOST_ID], HOST1) self.assertNotEqual(vrrp_port_db[portbindings.HOST_ID], HOST2) self.assertNotEqual( vrrp_port_db[portbindings.HOST_ID], self.l3_agent['host']) # Now update both the VM ports with the allowed_address_pair ip self.core_plugin.update_port( self.context, vm_port1['id'], {'port': { 'allowed_address_pairs': allowed_address_pairs}}) updated_vm_port1 = self.core_plugin.get_port( self.context, vm_port1['id']) expected_allowed_address_pairs1 = updated_vm_port1.get( 'allowed_address_pairs') self.assertEqual(expected_allowed_address_pairs1, allowed_address_pairs) self.core_plugin.update_port( self.context, vm_port2['id'], {'port': { 'allowed_address_pairs': allowed_address_pairs}}) updated_vm_port2 = self.core_plugin.get_port( self.context, vm_port2['id']) expected_allowed_address_pairs2 = updated_vm_port2.get( 'allowed_address_pairs') self.assertEqual(expected_allowed_address_pairs2, allowed_address_pairs) # Now let us assign the floatingip to the vrrp port that is # unbound to any host. floating_ip = {'floating_network_id': ext_net['network']['id'], 'router_id': router['id'], 'port_id': vrrp_port['port']['id'], 'tenant_id': vrrp_port['port']['tenant_id']} floating_ip = self.l3_plugin.create_floatingip( self.context, {'floatingip': floating_ip}) expected_routers_updated_calls = [ mock.call(self.context, mock.ANY, 'host0'), mock.call(self.context, mock.ANY, HOST1), mock.call(self.context, mock.ANY, HOST2)] l3_notifier.routers_updated_on_host.assert_has_calls( expected_routers_updated_calls, any_order=True) self.assertFalse(l3_notifier.routers_updated.called) router_info = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, self.l3_agent['host'], [router['id']])) floatingips = router_info[0][constants.FLOATINGIP_KEY] self.assertTrue(floatingips[0][n_const.DVR_SNAT_BOUND]) def test_dvr_process_floatingips_for_dvr_on_full_sync(self): HOST1 = 'host1' helpers.register_l3_agent( host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR) router = self._create_router(ha=False) private_net1 = self._make_network(self.fmt, 'net1', True) kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} ext_net = self._make_network(self.fmt, '', True, **kwargs) self._make_subnet( self.fmt, ext_net, '10.20.0.1', '10.20.0.0/24', ip_version=4, enable_dhcp=True) # Schedule the router to the dvr_snat node self.l3_plugin.schedule_router(self.context, router['id'], candidates=[self.l3_agent]) # Set gateway to router self.l3_plugin._update_router_gw_info( self.context, router['id'], {'network_id': ext_net['network']['id']}) private_subnet1 = self._make_subnet( self.fmt, private_net1, '10.1.0.1', cidr='10.1.0.0/24', ip_version=4, enable_dhcp=True) with self.port( subnet=private_subnet1, device_owner=DEVICE_OWNER_COMPUTE) as int_port1,\ self.port( subnet=private_subnet1) as int_port2: self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': private_subnet1['subnet']['id']}) router_handle = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, self.l3_agent['host'], [router['id']])) self.assertEqual(self.l3_agent['host'], router_handle[0]['gw_port_host']) with mock.patch.object(self.l3_plugin, '_l3_rpc_notifier') as l3_notifier: self.core_plugin.update_port( self.context, int_port1['port']['id'], {'port': {portbindings.HOST_ID: HOST1}}) # Now let us assign the floatingip to the bound port # and unbound port. fip1 = {'floating_network_id': ext_net['network']['id'], 'router_id': router['id'], 'port_id': int_port1['port']['id'], 'tenant_id': int_port1['port']['tenant_id']} self.l3_plugin.create_floatingip( self.context, {'floatingip': fip1}) expected_routers_updated_calls = [ mock.call(self.context, mock.ANY, HOST1)] l3_notifier.routers_updated_on_host.assert_has_calls( expected_routers_updated_calls) self.assertFalse(l3_notifier.routers_updated.called) fip2 = {'floating_network_id': ext_net['network']['id'], 'router_id': router['id'], 'port_id': int_port2['port']['id'], 'tenant_id': int_port2['port']['tenant_id']} self.l3_plugin.create_floatingip( self.context, {'floatingip': fip2}) router_info = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, self.l3_agent['host'], [router['id']])) floatingips = router_info[0][constants.FLOATINGIP_KEY] self.assertEqual(1, len(floatingips)) self.assertTrue(floatingips[0][n_const.DVR_SNAT_BOUND]) self.assertEqual(n_const.FLOATING_IP_HOST_NEEDS_BINDING, floatingips[0]['host']) router1_info = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, HOST1, [router['id']])) floatingips = router1_info[0][constants.FLOATINGIP_KEY] self.assertEqual(1, len(floatingips)) self.assertEqual(HOST1, floatingips[0]['host']) self.assertIsNone(floatingips[0]['dest_host']) def test_dvr_router_unbound_floating_ip_migrate_to_bound_host(self): HOST1 = 'host1' helpers.register_l3_agent( host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR) router = self._create_router(ha=False) private_net1 = self._make_network(self.fmt, 'net1', True) kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} ext_net = self._make_network(self.fmt, '', True, **kwargs) self._make_subnet( self.fmt, ext_net, '10.20.0.1', '10.20.0.0/24', ip_version=4, enable_dhcp=True) self.l3_plugin.schedule_router(self.context, router['id'], candidates=[self.l3_agent]) # Set gateway to router self.l3_plugin._update_router_gw_info( self.context, router['id'], {'network_id': ext_net['network']['id']}) private_subnet1 = self._make_subnet( self.fmt, private_net1, '10.1.0.1', cidr='10.1.0.0/24', ip_version=4, enable_dhcp=True) with self.port( subnet=private_subnet1, device_owner=DEVICE_OWNER_COMPUTE) as int_port1: self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': private_subnet1['subnet']['id']}) router_handle = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, self.l3_agent['host'], [router['id']])) self.assertEqual(self.l3_agent['host'], router_handle[0]['gw_port_host']) with mock.patch.object(self.l3_plugin, '_l3_rpc_notifier') as l3_notifier: # Next we can try to associate the floatingip to the # VM port floating_ip = {'floating_network_id': ext_net['network']['id'], 'router_id': router['id'], 'port_id': int_port1['port']['id'], 'tenant_id': int_port1['port']['tenant_id']} floating_ip = self.l3_plugin.create_floatingip( self.context, {'floatingip': floating_ip}) expected_routers_updated_calls = [ mock.call(self.context, mock.ANY, 'host0')] l3_notifier.routers_updated_on_host.assert_has_calls( expected_routers_updated_calls) self.assertFalse(l3_notifier.routers_updated.called) router_info = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, self.l3_agent['host'], [router['id']])) floatingips = router_info[0][constants.FLOATINGIP_KEY] self.assertTrue(floatingips[0][n_const.DVR_SNAT_BOUND]) # Now do the host binding to the fip port self.core_plugin.update_port( self.context, int_port1['port']['id'], {'port': {portbindings.HOST_ID: HOST1}}) expected_routers_updated_calls = [ mock.call(self.context, mock.ANY, 'host0'), mock.call(self.context, mock.ANY, HOST1)] l3_notifier.routers_updated_on_host.assert_has_calls( expected_routers_updated_calls) updated_router_info = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, HOST1, [router['id']])) floatingips = updated_router_info[0][constants.FLOATINGIP_KEY] self.assertFalse(floatingips[0].get(n_const.DVR_SNAT_BOUND)) self.assertEqual(HOST1, floatingips[0]['host']) def test_dvr_router_centralized_floating_ip(self): HOST1 = 'host1' helpers.register_l3_agent( host=HOST1, agent_mode=n_const.L3_AGENT_MODE_DVR_NO_EXTERNAL) router = self._create_router(ha=False) private_net1 = self._make_network(self.fmt, 'net1', True) kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} ext_net = self._make_network(self.fmt, '', True, **kwargs) self._make_subnet( self.fmt, ext_net, '10.20.0.1', '10.20.0.0/24', ip_version=4, enable_dhcp=True) self.l3_plugin.schedule_router(self.context, router['id'], candidates=[self.l3_agent]) # Set gateway to router self.l3_plugin._update_router_gw_info( self.context, router['id'], {'network_id': ext_net['network']['id']}) private_subnet1 = self._make_subnet( self.fmt, private_net1, '10.1.0.1', cidr='10.1.0.0/24', ip_version=4, enable_dhcp=True) with self.port( subnet=private_subnet1, device_owner=DEVICE_OWNER_COMPUTE) as int_port1: self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': private_subnet1['subnet']['id']}) router_handle = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, self.l3_agent['host'], [router['id']])) self.assertEqual(self.l3_agent['host'], router_handle[0]['gw_port_host']) with mock.patch.object(self.l3_plugin, '_l3_rpc_notifier') as l3_notifier: vm_port = self.core_plugin.update_port( self.context, int_port1['port']['id'], {'port': {portbindings.HOST_ID: HOST1}}) self.assertEqual( 1, l3_notifier.routers_updated_on_host.call_count) # Next we can try to associate the floatingip to the # VM port floating_ip = {'floating_network_id': ext_net['network']['id'], 'router_id': router['id'], 'port_id': vm_port['id'], 'tenant_id': vm_port['tenant_id']} floating_ip = self.l3_plugin.create_floatingip( self.context, {'floatingip': floating_ip}) expected_routers_updated_calls = [ mock.call(self.context, mock.ANY, HOST1), mock.call(self.context, mock.ANY, 'host0')] l3_notifier.routers_updated_on_host.assert_has_calls( expected_routers_updated_calls) self.assertFalse(l3_notifier.routers_updated.called) router_info = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, self.l3_agent['host'], [router['id']])) floatingips = router_info[0][constants.FLOATINGIP_KEY] self.assertTrue(floatingips[0][n_const.DVR_SNAT_BOUND]) # Test case to make sure when an agent in this case # dvr_no_external restarts and does a full sync, we need # to make sure that the returned router_info has # DVR_SNAT_BOUND flag enabled, otherwise the floating IP # state would error out. router_sync_info = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, HOST1, [router['id']])) floatingips = router_sync_info[0][constants.FLOATINGIP_KEY] self.assertTrue(floatingips[0][n_const.DVR_SNAT_BOUND]) def test_allowed_addr_pairs_delayed_fip_and_update_arp_entry(self): HOST1 = 'host1' helpers.register_l3_agent( host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR) HOST2 = 'host2' helpers.register_l3_agent( host=HOST2, agent_mode=constants.L3_AGENT_MODE_DVR) router = self._create_router(ha=False) private_net1 = self._make_network(self.fmt, 'net1', True) test_allocation_pools = [{'start': '10.1.0.2', 'end': '10.1.0.20'}] fixed_vrrp_ip = [{'ip_address': '10.1.0.201'}] kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} ext_net = self._make_network(self.fmt, '', True, **kwargs) self._make_subnet( self.fmt, ext_net, '10.20.0.1', '10.20.0.0/24', ip_version=4, enable_dhcp=True) self.l3_plugin.schedule_router(self.context, router['id'], candidates=[self.l3_agent]) # Set gateway to router self.l3_plugin._update_router_gw_info( self.context, router['id'], {'network_id': ext_net['network']['id']}) private_subnet1 = self._make_subnet( self.fmt, private_net1, '10.1.0.1', cidr='10.1.0.0/24', ip_version=4, allocation_pools=test_allocation_pools, enable_dhcp=True) vrrp_port = self._make_port( self.fmt, private_net1['network']['id'], fixed_ips=fixed_vrrp_ip) allowed_address_pairs = [ {'ip_address': '10.1.0.201', 'mac_address': vrrp_port['port']['mac_address']}] with self.port( subnet=private_subnet1, device_owner=DEVICE_OWNER_COMPUTE) as int_port,\ self.port(subnet=private_subnet1, device_owner=DEVICE_OWNER_COMPUTE) as int_port2: self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': private_subnet1['subnet']['id']}) router_handle = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, self.l3_agent['host'], [router['id']])) self.assertEqual(self.l3_agent['host'], router_handle[0]['gw_port_host']) with mock.patch.object(self.l3_plugin, '_l3_rpc_notifier') as l3_notifier: vm_port = self.core_plugin.update_port( self.context, int_port['port']['id'], {'port': {portbindings.HOST_ID: HOST1}}) vm_port_mac = vm_port['mac_address'] vm_port_fixed_ips = vm_port['fixed_ips'] vm_port_subnet_id = vm_port_fixed_ips[0]['subnet_id'] vm_arp_table = { 'ip_address': vm_port_fixed_ips[0]['ip_address'], 'mac_address': vm_port_mac, 'subnet_id': vm_port_subnet_id} vm_port2 = self.core_plugin.update_port( self.context, int_port2['port']['id'], {'port': {portbindings.HOST_ID: HOST2}}) # Now update the VM port with the allowed_address_pair self.core_plugin.update_port( self.context, vm_port['id'], {'port': { 'allowed_address_pairs': allowed_address_pairs}}) self.core_plugin.update_port( self.context, vm_port2['id'], {'port': { 'allowed_address_pairs': allowed_address_pairs}}) self.assertEqual( 2, l3_notifier.routers_updated_on_host.call_count) updated_vm_port1 = self.core_plugin.get_port( self.context, vm_port['id']) updated_vm_port2 = self.core_plugin.get_port( self.context, vm_port2['id']) expected_allowed_address_pairs = updated_vm_port1.get( 'allowed_address_pairs') self.assertEqual(expected_allowed_address_pairs, allowed_address_pairs) expected_allowed_address_pairs_2 = updated_vm_port2.get( 'allowed_address_pairs') self.assertEqual(expected_allowed_address_pairs_2, allowed_address_pairs) # Now the VRRP port is attached to the VM port. At this # point, the VRRP port should not have inherited the # port host bindings from the parent VM port. cur_vrrp_port_db = self.core_plugin.get_port( self.context, vrrp_port['port']['id']) self.assertNotEqual( cur_vrrp_port_db[portbindings.HOST_ID], HOST1) self.assertNotEqual( cur_vrrp_port_db[portbindings.HOST_ID], HOST2) # Next we can try to associate the floatingip to the # VRRP port that is already attached to the VM port floating_ip = {'floating_network_id': ext_net['network']['id'], 'router_id': router['id'], 'port_id': vrrp_port['port']['id'], 'tenant_id': vrrp_port['port']['tenant_id']} floating_ip = self.l3_plugin.create_floatingip( self.context, {'floatingip': floating_ip}) post_update_vrrp_port_db = self.core_plugin.get_port( self.context, vrrp_port['port']['id']) vrrp_port_fixed_ips = post_update_vrrp_port_db['fixed_ips'] vrrp_port_subnet_id = vrrp_port_fixed_ips[0]['subnet_id'] vrrp_arp_table1 = { 'ip_address': vrrp_port_fixed_ips[0]['ip_address'], 'mac_address': vm_port_mac, 'subnet_id': vrrp_port_subnet_id} expected_calls = [ mock.call(self.context, router['id'], vm_arp_table), mock.call(self.context, router['id'], vrrp_arp_table1)] l3_notifier.add_arp_entry.assert_has_calls( expected_calls) expected_routers_updated_calls = [ mock.call(self.context, mock.ANY, HOST1), mock.call(self.context, mock.ANY, HOST2), mock.call(self.context, mock.ANY, 'host0')] l3_notifier.routers_updated_on_host.assert_has_calls( expected_routers_updated_calls, any_order=True) self.assertFalse(l3_notifier.routers_updated.called) router_info = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, self.l3_agent['host'], [router['id']])) floatingips = router_info[0][constants.FLOATINGIP_KEY] self.assertTrue(floatingips[0][n_const.DVR_SNAT_BOUND]) def test_dvr_gateway_host_binding_is_set(self): router = self._create_router(ha=False) private_net1 = self._make_network(self.fmt, 'net1', True) kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} ext_net = self._make_network(self.fmt, '', True, **kwargs) self._make_subnet( self.fmt, ext_net, '10.20.0.1', '10.20.0.0/24', ip_version=4, enable_dhcp=True) self.l3_plugin.schedule_router(self.context, router['id'], candidates=[self.l3_agent]) # Set gateway to router self.l3_plugin._update_router_gw_info( self.context, router['id'], {'network_id': ext_net['network']['id']}) private_subnet1 = self._make_subnet( self.fmt, private_net1, '10.1.0.1', cidr='10.1.0.0/24', ip_version=4, enable_dhcp=True) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': private_subnet1['subnet']['id']}) # Check for the gw_port_host in the router dict to make # sure that the _build_routers_list in l3_dvr_db is called. router_handle = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, self.l3_agent['host'], [router['id']])) self.assertEqual(self.l3_agent['host'], router_handle[0]['gw_port_host']) def test_allowed_address_pairs_update_arp_entry(self): HOST1 = 'host1' helpers.register_l3_agent( host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR) router = self._create_router(ha=False) private_net1 = self._make_network(self.fmt, 'net1', True) test_allocation_pools = [{'start': '10.1.0.2', 'end': '10.1.0.20'}] fixed_vrrp_ip = [{'ip_address': '10.1.0.201'}] kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} ext_net = self._make_network(self.fmt, '', True, **kwargs) self._make_subnet( self.fmt, ext_net, '10.20.0.1', '10.20.0.0/24', ip_version=4, enable_dhcp=True) self.l3_plugin.schedule_router(self.context, router['id'], candidates=[self.l3_agent]) # Set gateway to router self.l3_plugin._update_router_gw_info( self.context, router['id'], {'network_id': ext_net['network']['id']}) private_subnet1 = self._make_subnet( self.fmt, private_net1, '10.1.0.1', cidr='10.1.0.0/24', ip_version=4, allocation_pools=test_allocation_pools, enable_dhcp=True) vrrp_port = self._make_port( self.fmt, private_net1['network']['id'], fixed_ips=fixed_vrrp_ip) allowed_address_pairs = [ {'ip_address': '10.1.0.201', 'mac_address': vrrp_port['port']['mac_address']}] with self.port( subnet=private_subnet1, device_owner=DEVICE_OWNER_COMPUTE) as int_port: self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': private_subnet1['subnet']['id']}) router_handle = ( self.l3_plugin.list_active_sync_routers_on_active_l3_agent( self.context, self.l3_agent['host'], [router['id']])) self.assertEqual(self.l3_agent['host'], router_handle[0]['gw_port_host']) with mock.patch.object(self.l3_plugin, '_l3_rpc_notifier') as l3_notifier: vm_port = self.core_plugin.update_port( self.context, int_port['port']['id'], {'port': {portbindings.HOST_ID: HOST1}}) vm_port_mac = vm_port['mac_address'] vm_port_fixed_ips = vm_port['fixed_ips'] vm_port_subnet_id = vm_port_fixed_ips[0]['subnet_id'] vm_arp_table = { 'ip_address': vm_port_fixed_ips[0]['ip_address'], 'mac_address': vm_port_mac, 'subnet_id': vm_port_subnet_id} self.assertEqual(1, l3_notifier.add_arp_entry.call_count) floating_ip = {'floating_network_id': ext_net['network']['id'], 'router_id': router['id'], 'port_id': vrrp_port['port']['id'], 'tenant_id': vrrp_port['port']['tenant_id']} floating_ip = self.l3_plugin.create_floatingip( self.context, {'floatingip': floating_ip}) vrrp_port_db = self.core_plugin.get_port( self.context, vrrp_port['port']['id']) self.assertNotEqual(vrrp_port_db[portbindings.HOST_ID], HOST1) # Now update the VM port with the allowed_address_pair self.core_plugin.update_port( self.context, vm_port['id'], {'port': { 'allowed_address_pairs': allowed_address_pairs}}) updated_vm_port = self.core_plugin.get_port( self.context, vm_port['id']) expected_allowed_address_pairs = updated_vm_port.get( 'allowed_address_pairs') self.assertEqual(expected_allowed_address_pairs, allowed_address_pairs) cur_vrrp_port_db = self.core_plugin.get_port( self.context, vrrp_port['port']['id']) vrrp_port_fixed_ips = cur_vrrp_port_db['fixed_ips'] vrrp_port_subnet_id = vrrp_port_fixed_ips[0]['subnet_id'] vrrp_arp_table1 = { 'ip_address': vrrp_port_fixed_ips[0]['ip_address'], 'mac_address': vm_port_mac, 'subnet_id': vrrp_port_subnet_id} expected_calls = [ mock.call(self.context, router['id'], vm_arp_table), mock.call(self.context, router['id'], vrrp_arp_table1)] l3_notifier.add_arp_entry.assert_has_calls( expected_calls) expected_routers_updated_calls = [ mock.call(self.context, mock.ANY, HOST1), mock.call(self.context, mock.ANY, 'host0')] l3_notifier.routers_updated_on_host.assert_has_calls( expected_routers_updated_calls) self.assertFalse(l3_notifier.routers_updated.called) def test_update_vm_port_host_router_update(self): # register l3 agents in dvr mode in addition to existing dvr_snat agent HOST1 = 'host1' helpers.register_l3_agent( host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR) HOST2 = 'host2' helpers.register_l3_agent( host=HOST2, agent_mode=constants.L3_AGENT_MODE_DVR) router = self._create_router() with self.subnet() as subnet: self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) with mock.patch.object(self.l3_plugin, '_l3_rpc_notifier') as l3_notifier,\ self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE) as port: self.l3_plugin.agent_notifiers[ constants.AGENT_TYPE_L3] = l3_notifier self.core_plugin.update_port( self.context, port['port']['id'], {'port': {portbindings.HOST_ID: HOST1}}) l3_notifier.routers_updated_on_host.assert_called_once_with( self.context, {router['id']}, HOST1) self.assertFalse(l3_notifier.routers_updated.called) # updating port's host (instance migration) l3_notifier.reset_mock() self.core_plugin.update_port( self.context, port['port']['id'], {'port': {portbindings.HOST_ID: HOST2}}) l3_notifier.routers_updated_on_host.assert_called_once_with( self.context, {router['id']}, HOST2) l3_notifier.router_removed_from_agent.assert_called_once_with( mock.ANY, router['id'], HOST1) def test_dvr_router_manual_rescheduling_removes_router(self): router = self._create_router() kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} with self.network(**kwargs) as ext_net,\ self.subnet(network=ext_net),\ self.subnet(cidr='20.0.0.0/24') as subnet,\ self.port(subnet=subnet): self.l3_plugin._update_router_gw_info( self.context, router['id'], {'network_id': ext_net['network']['id']}) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) self.l3_plugin.schedule_router(self.context, router['id'], candidates=[self.l3_agent]) # Now the VM should be also scheduled on the node notifier = self.l3_plugin.agent_notifiers[ constants.AGENT_TYPE_L3] with mock.patch.object( notifier, 'router_removed_from_agent') as rtr_remove_mock: self.l3_plugin.remove_router_from_l3_agent( self.context, self.l3_agent['id'], router['id']) rtr_remove_mock.assert_called_once_with( self.context, router['id'], self.l3_agent['host']) def test_dvr_router_manual_rescheduling_updates_router(self): router = self._create_router() kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} with self.network(**kwargs) as ext_net,\ self.subnet(network=ext_net),\ self.subnet(cidr='20.0.0.0/24') as subnet,\ self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE) as port: self.core_plugin.update_port( self.context, port['port']['id'], {'port': {'binding:host_id': self.l3_agent['host']}}) self.l3_plugin._update_router_gw_info( self.context, router['id'], {'network_id': ext_net['network']['id']}) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) self.l3_plugin.schedule_router(self.context, router['id'], candidates=[self.l3_agent]) # Now the VM should be also scheduled on the node notifier = self.l3_plugin.agent_notifiers[ constants.AGENT_TYPE_L3] with mock.patch.object( notifier, 'routers_updated_on_host') as rtr_update_mock: self.l3_plugin.remove_router_from_l3_agent( self.context, self.l3_agent['id'], router['id']) rtr_update_mock.assert_called_once_with( self.context, [router['id']], self.l3_agent['host']) def _test_router_remove_from_agent_on_vm_port_deletion( self, non_admin_port=False): # register l3 agent in dvr mode in addition to existing dvr_snat agent HOST = 'host1' non_admin_tenant = 'tenant1' helpers.register_l3_agent( host=HOST, agent_mode=constants.L3_AGENT_MODE_DVR) router = self._create_router() with self.network(shared=True) as net,\ self.subnet(network=net) as subnet,\ self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, tenant_id=non_admin_tenant, set_context=non_admin_port) as port: self.core_plugin.update_port( self.context, port['port']['id'], {'port': {portbindings.HOST_ID: HOST}}) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) with mock.patch.object(self.l3_plugin.l3_rpc_notifier, 'router_removed_from_agent') as remove_mock: ctx = context.Context( '', non_admin_tenant) if non_admin_port else self.context self._delete('ports', port['port']['id'], neutron_context=ctx) remove_mock.assert_called_once_with( mock.ANY, router['id'], HOST) def test_router_remove_from_agent_on_vm_port_deletion(self): self._test_router_remove_from_agent_on_vm_port_deletion() def test_admin_router_remove_from_agent_on_vm_port_deletion(self): self._test_router_remove_from_agent_on_vm_port_deletion( non_admin_port=True) def test_dvr_router_notifications_for_live_migration_with_fip(self): self._dvr_router_notifications_for_live_migration( with_floatingip=True) def test_dvr_router_notifications_for_live_migration_without_fip(self): self._dvr_router_notifications_for_live_migration() def _dvr_router_notifications_for_live_migration( self, with_floatingip=False): """Check the router notifications go to the right hosts with live migration without hostbinding on the port. """ # register l3 agents in dvr mode in addition to existing dvr_snat agent HOST1, HOST2 = 'host1', 'host2' for host in [HOST1, HOST2]: helpers.register_l3_agent( host=host, agent_mode=constants.L3_AGENT_MODE_DVR) router = self._create_router() arg_list = (portbindings.HOST_ID,) with self.subnet() as ext_subnet,\ self.subnet(cidr='20.0.0.0/24') as subnet1,\ self.port(subnet=subnet1, device_owner=DEVICE_OWNER_COMPUTE, arg_list=arg_list, **{portbindings.HOST_ID: HOST1}) as vm_port: # make net external ext_net_id = ext_subnet['subnet']['network_id'] self._update('networks', ext_net_id, {'network': {extnet_apidef.EXTERNAL: True}}) # add external gateway to router self.l3_plugin.update_router( self.context, router['id'], {'router': { 'external_gateway_info': {'network_id': ext_net_id}}}) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet1['subnet']['id']}) if with_floatingip: floating_ip = {'floating_network_id': ext_net_id, 'router_id': router['id'], 'port_id': vm_port['port']['id'], 'tenant_id': vm_port['port']['tenant_id'], 'dns_name': '', 'dns_domain': ''} floating_ip = self.l3_plugin.create_floatingip( self.context, {'floatingip': floating_ip}) with mock.patch.object(self.l3_plugin, '_l3_rpc_notifier') as l3_notifier,\ mock.patch.object( self.l3_plugin, 'create_fip_agent_gw_port_if_not_exists' ) as fip_agent: live_migration_port_profile = { 'migrating_to': HOST2 } # Update the VM Port with Migration porbinding Profile. # With this change, it should trigger a notification to # the Destination host to create a Router ahead of time # before the VM Port binding has changed to HOST2. updated_port = self.core_plugin.update_port( self.context, vm_port['port']['id'], {'port': { portbindings.PROFILE: live_migration_port_profile}}) # this will be called twice, once for port update, and once # for new binding l3_notifier.routers_updated_on_host.assert_any_call( self.context, {router['id']}, HOST2) # Check the port-binding is still with the old HOST1, but # the router update notification has been sent to the new # host 'HOST2' based on the live migration profile change. self.assertEqual(updated_port[portbindings.HOST_ID], HOST1) self.assertNotEqual(updated_port[portbindings.HOST_ID], HOST2) if with_floatingip: fip_agent.return_value = True # Since we have already created the floatingip for the # port, it should be creating the floatingip agent gw # port for the new host if it does not exist. fip_agent.assert_any_call( mock.ANY, floating_ip['floating_network_id'], HOST2) def test_router_notifications(self): """Check that notifications go to the right hosts in different conditions """ # register l3 agents in dvr mode in addition to existing dvr_snat agent HOST1, HOST2, HOST3 = 'host1', 'host2', 'host3' for host in [HOST1, HOST2, HOST3]: helpers.register_l3_agent( host=host, agent_mode=constants.L3_AGENT_MODE_DVR) router = self._create_router() arg_list = (portbindings.HOST_ID,) with self.subnet() as ext_subnet,\ self.subnet(cidr='20.0.0.0/24') as subnet1,\ self.subnet(cidr='30.0.0.0/24') as subnet2,\ self.subnet(cidr='40.0.0.0/24') as subnet3,\ self.port(subnet=subnet1, device_owner=DEVICE_OWNER_COMPUTE, arg_list=arg_list, **{portbindings.HOST_ID: HOST1}),\ self.port(subnet=subnet2, device_owner=constants.DEVICE_OWNER_DHCP, arg_list=arg_list, **{portbindings.HOST_ID: HOST2}),\ self.port(subnet=subnet3, device_owner=constants.DEVICE_OWNER_NEUTRON_PREFIX, arg_list=arg_list, **{portbindings.HOST_ID: HOST3}): # make net external ext_net_id = ext_subnet['subnet']['network_id'] self._update('networks', ext_net_id, {'network': {extnet_apidef.EXTERNAL: True}}) with mock.patch.object(self.l3_plugin.l3_rpc_notifier.client, 'prepare') as mock_prepare: # add external gateway to router self.l3_plugin.update_router( self.context, router['id'], {'router': { 'external_gateway_info': {'network_id': ext_net_id}}}) # router has no interfaces so notification goes # to only dvr_snat agent mock_prepare.assert_called_once_with( server=self.l3_agent['host'], topic=topics.L3_AGENT, version='1.1') mock_prepare.reset_mock() self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet1['subnet']['id']}) self.assertEqual(2, mock_prepare.call_count) expected = [mock.call(server=self.l3_agent['host'], topic=topics.L3_AGENT, version='1.1'), mock.call(server=HOST1, topic=topics.L3_AGENT, version='1.1')] mock_prepare.assert_has_calls(expected, any_order=True) mock_prepare.reset_mock() self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet2['subnet']['id']}) self.assertEqual(3, mock_prepare.call_count) expected = [mock.call(server=self.l3_agent['host'], topic=topics.L3_AGENT, version='1.1'), mock.call(server=HOST1, topic=topics.L3_AGENT, version='1.1'), mock.call(server=HOST2, topic=topics.L3_AGENT, version='1.1')] mock_prepare.assert_has_calls(expected, any_order=True) mock_prepare.reset_mock() self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet3['subnet']['id']}) # there are no dvr serviceable ports on HOST3, so notification # goes to the same hosts self.assertEqual(3, mock_prepare.call_count) expected = [mock.call(server=self.l3_agent['host'], topic=topics.L3_AGENT, version='1.1'), mock.call(server=HOST1, topic=topics.L3_AGENT, version='1.1'), mock.call(server=HOST2, topic=topics.L3_AGENT, version='1.1')] mock_prepare.assert_has_calls(expected, any_order=True) def test_router_is_not_removed_from_snat_agent_on_interface_removal(self): """Check that dvr router is not removed from l3 agent hosting SNAT for it on router interface removal """ router = self._create_router() kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} with self.subnet() as subnet,\ self.network(**kwargs) as ext_net,\ self.subnet(network=ext_net, cidr='20.0.0.0/24'): self.l3_plugin._update_router_gw_info( self.context, router['id'], {'network_id': ext_net['network']['id']}) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) agents = self.l3_plugin.list_l3_agents_hosting_router( self.context, router['id']) self.assertEqual(1, len(agents['agents'])) with mock.patch.object(self.l3_plugin, '_l3_rpc_notifier') as l3_notifier: self.l3_plugin.remove_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) agents = self.l3_plugin.list_l3_agents_hosting_router( self.context, router['id']) self.assertEqual(1, len(agents['agents'])) self.assertFalse(l3_notifier.router_removed_from_agent.called) def test_router_is_not_removed_from_snat_agent_on_dhcp_port_deletion(self): """Check that dvr router is not removed from l3 agent hosting SNAT for it on DHCP port removal """ router = self._create_router() kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} with self.network(**kwargs) as ext_net,\ self.subnet(network=ext_net),\ self.subnet(cidr='20.0.0.0/24') as subnet,\ self.port(subnet=subnet, device_owner=constants.DEVICE_OWNER_DHCP) as port: self.core_plugin.update_port( self.context, port['port']['id'], {'port': {'binding:host_id': self.l3_agent['host']}}) self.l3_plugin._update_router_gw_info( self.context, router['id'], {'network_id': ext_net['network']['id']}) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) # router should be scheduled to the dvr_snat l3 agent agents = self.l3_plugin.list_l3_agents_hosting_router( self.context, router['id']) self.assertEqual(1, len(agents['agents'])) self.assertEqual(self.l3_agent['id'], agents['agents'][0]['id']) notifier = self.l3_plugin.agent_notifiers[ constants.AGENT_TYPE_L3] with mock.patch.object( notifier, 'router_removed_from_agent') as remove_mock: self._delete('ports', port['port']['id']) # now when port is deleted the router still has external # gateway and should still be scheduled to the snat agent agents = self.l3_plugin.list_l3_agents_hosting_router( self.context, router['id']) self.assertEqual(1, len(agents['agents'])) self.assertEqual(self.l3_agent['id'], agents['agents'][0]['id']) self.assertFalse(remove_mock.called) def test__get_dvr_subnet_ids_on_host_query(self): with self.subnet(cidr='20.0.0.0/24') as subnet1,\ self.subnet(cidr='30.0.0.0/24') as subnet2,\ self.subnet(cidr='40.0.0.0/24') as subnet3,\ self.port(subnet=subnet1, device_owner=DEVICE_OWNER_COMPUTE) as p1,\ self.port(subnet=subnet2, device_owner=constants.DEVICE_OWNER_DHCP) as p2,\ self.port(subnet=subnet3, device_owner=constants.DEVICE_OWNER_NEUTRON_PREFIX)\ as p3,\ self.port(subnet=subnet3, device_owner=constants.DEVICE_OWNER_COMPUTE_PREFIX)\ as p4: host = 'host1' subnet_ids = [item[0] for item in self.l3_plugin._get_dvr_subnet_ids_on_host_query( self.context, host)] self.assertEqual([], subnet_ids) self.core_plugin.update_port( self.context, p1['port']['id'], {'port': {portbindings.HOST_ID: host}}) expected = {subnet1['subnet']['id']} subnet_ids = [item[0] for item in self.l3_plugin._get_dvr_subnet_ids_on_host_query( self.context, host)] self.assertEqual(expected, set(subnet_ids)) self.core_plugin.update_port( self.context, p2['port']['id'], {'port': {portbindings.HOST_ID: host}}) expected.add(subnet2['subnet']['id']) subnet_ids = [item[0] for item in self.l3_plugin._get_dvr_subnet_ids_on_host_query( self.context, host)] self.assertEqual(expected, set(subnet_ids)) self.core_plugin.update_port( self.context, p3['port']['id'], {'port': {portbindings.HOST_ID: host}}) # p3 is non dvr serviceable so no subnet3 expected subnet_ids = [item[0] for item in self.l3_plugin._get_dvr_subnet_ids_on_host_query( self.context, host)] self.assertEqual(expected, set(subnet_ids)) other_host = 'other' + host self.core_plugin.update_port( self.context, p4['port']['id'], {'port': {portbindings.HOST_ID: other_host}}) # p4 is on other host so no subnet3 expected subnet_ids = [item[0] for item in self.l3_plugin._get_dvr_subnet_ids_on_host_query( self.context, host)] self.assertEqual(expected, set(subnet_ids)) self.core_plugin.update_port( self.context, p4['port']['id'], {'port': {portbindings.HOST_ID: host}}) # finally p4 is on the right host so subnet3 is expected expected.add(subnet3['subnet']['id']) subnet_ids = [item[0] for item in self.l3_plugin._get_dvr_subnet_ids_on_host_query( self.context, host)] self.assertEqual(expected, set(subnet_ids)) def test__get_dvr_router_ids_for_host(self): router1 = self._create_router() router2 = self._create_router() host = 'host1' arg_list = (portbindings.HOST_ID,) with self.subnet(cidr='20.0.0.0/24') as subnet1,\ self.subnet(cidr='30.0.0.0/24') as subnet2,\ self.port(subnet=subnet1, device_owner=DEVICE_OWNER_COMPUTE, arg_list=arg_list, **{portbindings.HOST_ID: host}),\ self.port(subnet=subnet2, device_owner=constants.DEVICE_OWNER_DHCP, arg_list=arg_list, **{portbindings.HOST_ID: host}): router_ids = self.l3_plugin._get_dvr_router_ids_for_host( self.context, host) self.assertEqual([], router_ids) self.l3_plugin.add_router_interface( self.context, router1['id'], {'subnet_id': subnet1['subnet']['id']}) router_ids = self.l3_plugin._get_dvr_router_ids_for_host( self.context, host) expected = {router1['id']} self.assertEqual(expected, set(router_ids)) self.l3_plugin.add_router_interface( self.context, router2['id'], {'subnet_id': subnet2['subnet']['id']}) router_ids = self.l3_plugin._get_dvr_router_ids_for_host( self.context, host) expected.add(router2['id']) self.assertEqual(expected, set(router_ids)) def test__get_router_ids_for_agent(self): router1 = self._create_router() router2 = self._create_router() router3 = self._create_router() arg_list = (portbindings.HOST_ID,) host = self.l3_agent['host'] with self.subnet() as ext_subnet,\ self.subnet(cidr='20.0.0.0/24') as subnet1,\ self.subnet(cidr='30.0.0.0/24') as subnet2,\ self.port(subnet=subnet1, device_owner=DEVICE_OWNER_COMPUTE, arg_list=arg_list, **{portbindings.HOST_ID: host}),\ self.port(subnet=subnet2, device_owner=constants.DEVICE_OWNER_DHCP, arg_list=arg_list, **{portbindings.HOST_ID: host}): ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, []) self.assertEqual([], ids) ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, [router1['id'], router2['id']]) self.assertEqual([], ids) self.l3_plugin.add_router_interface( self.context, router1['id'], {'subnet_id': subnet1['subnet']['id']}) ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, []) self.assertEqual([router1['id']], ids) ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, [router1['id']]) self.assertEqual([router1['id']], ids) ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, [router1['id'], router2['id']]) self.assertEqual([router1['id']], ids) ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, [router2['id']]) self.assertEqual([], ids) self.l3_plugin.add_router_interface( self.context, router2['id'], {'subnet_id': subnet2['subnet']['id']}) ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, []) self.assertEqual({router1['id'], router2['id']}, set(ids)) ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, [router1['id']]) self.assertEqual([router1['id']], ids) ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, [router1['id'], router2['id']]) self.assertEqual({router1['id'], router2['id']}, set(ids)) ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, [router2['id']]) self.assertEqual([router2['id']], ids) # make net external ext_net_id = ext_subnet['subnet']['network_id'] self._update('networks', ext_net_id, {'network': {extnet_apidef.EXTERNAL: True}}) # add external gateway to router self.l3_plugin.update_router( self.context, router3['id'], {'router': { 'external_gateway_info': {'network_id': ext_net_id}}}) ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, []) self.assertEqual({router1['id'], router2['id'], router3['id']}, set(ids)) ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, [router3['id']]) self.assertEqual([router3['id']], ids) ids = self.l3_plugin._get_router_ids_for_agent( self.context, self.l3_agent, [router1['id'], router3['id']]) self.assertEqual({router1['id'], router3['id']}, set(ids)) def test_remove_router_interface(self): HOST1 = 'host1' helpers.register_l3_agent( host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR) router = self._create_router() arg_list = (portbindings.HOST_ID,) with self.subnet() as subnet,\ self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=arg_list, **{portbindings.HOST_ID: HOST1}): l3_notifier = mock.Mock() self.l3_plugin.l3_rpc_notifier = l3_notifier self.l3_plugin.agent_notifiers[ constants.AGENT_TYPE_L3] = l3_notifier self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) self.l3_plugin.schedule_router(self.context, router['id']) self.l3_plugin.remove_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) l3_notifier.router_removed_from_agent.assert_called_once_with( mock.ANY, router['id'], HOST1) def test_router_auto_scheduling(self): router = self._create_router() agents = self.l3_plugin.list_l3_agents_hosting_router( self.context, router['id']) # router is not scheduled yet self.assertEqual([], agents['agents']) l3_rpc_handler = l3_rpc.L3RpcCallback() # router should be auto scheduled once l3 agent requests router ids l3_rpc_handler.get_router_ids(self.context, self.l3_agent['host']) agents = self.l3_plugin.list_l3_agents_hosting_router( self.context, router['id']) self.assertEqual(1, len(agents['agents'])) self.assertEqual(self.l3_agent['id'], agents['agents'][0]['id']) def test_add_router_interface_by_subnet_notifications(self): notif_handler_before = mock.Mock() notif_handler_after = mock.Mock() registry.subscribe(notif_handler_before.callback, resources.ROUTER_INTERFACE, events.BEFORE_CREATE) registry.subscribe(notif_handler_after.callback, resources.ROUTER_INTERFACE, events.AFTER_CREATE) router = self._create_router() with self.network() as net, \ self.subnet(network=net) as subnet: interface_info = {'subnet_id': subnet['subnet']['id']} self.l3_plugin.add_router_interface( self.context, router['id'], interface_info) kwargs = {'context': self.context, 'router_id': router['id'], 'network_id': net['network']['id'], 'router_db': mock.ANY, 'port': mock.ANY, 'interface_info': interface_info} notif_handler_before.callback.assert_called_once_with( resources.ROUTER_INTERFACE, events.BEFORE_CREATE, mock.ANY, **kwargs) kwargs_after = {'cidrs': mock.ANY, 'context': mock.ANY, 'gateway_ips': mock.ANY, 'interface_info': mock.ANY, 'network_id': None, 'port': mock.ANY, 'new_interface': True, 'subnets': mock.ANY, 'port_id': mock.ANY, 'router_id': router['id']} notif_handler_after.callback.assert_called_once_with( resources.ROUTER_INTERFACE, events.AFTER_CREATE, mock.ANY, **kwargs_after) def test_add_router_interface_by_port_notifications(self): notif_handler_before = mock.Mock() notif_handler_after = mock.Mock() registry.subscribe(notif_handler_before.callback, resources.ROUTER_INTERFACE, events.BEFORE_CREATE) registry.subscribe(notif_handler_after.callback, resources.ROUTER_INTERFACE, events.AFTER_CREATE) router = self._create_router() with self.network() as net, \ self.subnet(network=net) as subnet, \ self.port(subnet=subnet) as port: interface_info = {'port_id': port['port']['id']} self.l3_plugin.add_router_interface( self.context, router['id'], interface_info) kwargs = {'context': self.context, 'router_id': router['id'], 'network_id': net['network']['id'], 'router_db': mock.ANY, 'port': mock.ANY, 'interface_info': interface_info} notif_handler_before.callback.assert_called_once_with( resources.ROUTER_INTERFACE, events.BEFORE_CREATE, mock.ANY, **kwargs) kwargs_after = {'cidrs': mock.ANY, 'context': mock.ANY, 'gateway_ips': mock.ANY, 'interface_info': mock.ANY, 'network_id': None, 'port': mock.ANY, 'new_interface': True, 'subnets': mock.ANY, 'port_id': port['port']['id'], 'router_id': router['id']} notif_handler_after.callback.assert_called_once_with( resources.ROUTER_INTERFACE, events.AFTER_CREATE, mock.ANY, **kwargs_after) class L3DvrTestCaseMigration(L3DvrTestCaseBase): def test_update_router_db_centralized_to_distributed_with_ports(self): with self.subnet() as subnet1: kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} with self.network(**kwargs) as ext_net, \ self.subnet(network=ext_net, cidr='30.0.0.0/24'): router = self._create_router(distributed=False) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet1['subnet']['id']}) self.l3_plugin._update_router_gw_info( self.context, router['id'], {'network_id': ext_net['network']['id']}) self.assertEqual( 0, len(self.l3_plugin._get_snat_sync_interfaces( self.context, [router['id']]))) # router needs to be in admin state down in order to be # upgraded to DVR self.l3_plugin.update_router( self.context, router['id'], {'router': {'admin_state_up': False}}) self.assertFalse(router['distributed']) self.l3_plugin.update_router( self.context, router['id'], {'router': {'distributed': True}}) router = self.l3_plugin.get_router(self.context, router['id']) self.assertTrue(router['distributed']) self.assertEqual( 1, len(self.l3_plugin._get_snat_sync_interfaces( self.context, [router['id']]))) neutron-12.1.1/neutron/tests/functional/services/l3_router/__init__.py0000664000175000017500000000000013553660046026156 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/l3_router/test_l3_dvr_ha_router_plugin.py0000664000175000017500000004656513553660047032330 0ustar zuulzuul00000000000000# Copyright (c) 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.api.definitions import external_net as extnet_apidef from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron.common import topics from neutron.tests.common import helpers from neutron.tests.functional.services.l3_router import \ test_l3_dvr_router_plugin DEVICE_OWNER_COMPUTE = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' class L3DvrHATestCase(test_l3_dvr_router_plugin.L3DvrTestCase): def setUp(self): super(L3DvrHATestCase, self).setUp() self.l3_agent_2 = helpers.register_l3_agent( host="standby", agent_mode=constants.L3_AGENT_MODE_DVR_SNAT) def _create_router(self, distributed=True, ha=True, admin_state_up=True): return (super(L3DvrHATestCase, self). _create_router(distributed=distributed, ha=ha, admin_state_up=admin_state_up)) def test_update_router_db_cvr_to_dvrha(self): router = self._create_router(distributed=False, ha=False, admin_state_up=False) self.l3_plugin.update_router( self.context, router['id'], {'router': {'distributed': True, 'ha': True}}) router = self.l3_plugin.get_router(self.context, router['id']) self.assertTrue(router['distributed']) self.assertTrue(router['ha']) def test_update_router_db_dvrha_to_cvr(self): router = self._create_router(distributed=True, ha=True, admin_state_up=False) self.l3_plugin.update_router( self.context, router['id'], {'router': {'distributed': False, 'ha': False}}) router = self.l3_plugin.get_router(self.context, router['id']) self.assertFalse(router['distributed']) self.assertFalse(router['ha']) def test_update_router_db_dvrha_to_dvr(self): router = self._create_router(distributed=True, ha=True, admin_state_up=False) self.l3_plugin.update_router( self.context, router['id'], {'router': {'admin_state_up': False}}) self.l3_plugin.update_router( self.context, router['id'], {'router': {'distributed': True, 'ha': False}}) router = self.l3_plugin.get_router(self.context, router['id']) self.assertTrue(router['distributed']) self.assertFalse(router['ha']) def test_update_router_db_dvrha_to_cvrha(self): router = self._create_router(distributed=True, ha=True, admin_state_up=False) self.l3_plugin.update_router( self.context, router['id'], {'router': {'distributed': False, 'ha': True}}) router = self.l3_plugin.get_router(self.context, router['id']) self.assertFalse(router['distributed']) self.assertTrue(router['ha']) def test_update_router_db_dvr_to_dvrha(self): router = self._create_router(distributed=True, ha=False, admin_state_up=False) self.l3_plugin.update_router( self.context, router['id'], {'router': {'distributed': True, 'ha': True}}) router = self.l3_plugin.get_router(self.context, router['id']) self.assertTrue(router['distributed']) self.assertTrue(router['ha']) def test_update_router_db_cvrha_to_dvrha(self): router = self._create_router(distributed=False, ha=True, admin_state_up=False) self.l3_plugin.update_router( self.context, router['id'], {'router': {'distributed': True, 'ha': True}}) router = self.l3_plugin.get_router(self.context, router['id']) self.assertTrue(router['distributed']) self.assertTrue(router['ha']) def _assert_router_is_hosted_on_both_dvr_snat_agents(self, router): agents = self.l3_plugin.list_l3_agents_hosting_router( self.context, router['id']) self.assertEqual(2, len(agents['agents'])) dvr_snat_agents = self.l3_plugin.get_ha_router_port_bindings( self.context, [router['id']]) dvr_snat_agent_ids = [a.l3_agent_id for a in dvr_snat_agents] self.assertIn(self.l3_agent['id'], dvr_snat_agent_ids) self.assertIn(self.l3_agent_2['id'], dvr_snat_agent_ids) def test_router_notifications(self): """Check that notifications go to the right hosts in different conditions """ # register l3 agents in dvr mode in addition to existing dvr_snat agent HOST1, HOST2, HOST3 = 'host1', 'host2', 'host3' for host in [HOST1, HOST2, HOST3]: helpers.register_l3_agent( host=host, agent_mode=constants.L3_AGENT_MODE_DVR) router = self._create_router(distributed=True, ha=True) arg_list = (portbindings.HOST_ID,) with self.subnet() as ext_subnet, \ self.subnet(cidr='20.0.0.0/24') as subnet1, \ self.subnet(cidr='30.0.0.0/24') as subnet2, \ self.subnet(cidr='40.0.0.0/24') as subnet3, \ self.port(subnet=subnet1, device_owner=DEVICE_OWNER_COMPUTE, arg_list=arg_list, **{portbindings.HOST_ID: HOST1}), \ self.port(subnet=subnet2, device_owner=constants.DEVICE_OWNER_DHCP, arg_list=arg_list, **{portbindings.HOST_ID: HOST2}), \ self.port(subnet=subnet3, device_owner=constants.DEVICE_OWNER_NEUTRON_PREFIX, arg_list=arg_list, **{portbindings.HOST_ID: HOST3}): # make net external ext_net_id = ext_subnet['subnet']['network_id'] self._update('networks', ext_net_id, {'network': {extnet_apidef.EXTERNAL: True}}) with mock.patch.object(self.l3_plugin.l3_rpc_notifier.client, 'prepare') as mock_prepare: # add external gateway to router self.l3_plugin.update_router( self.context, router['id'], {'router': { 'external_gateway_info': {'network_id': ext_net_id}}}) # router has no interfaces so notification goes # to only dvr_snat agents (self.l3_agent and self.l3_agent_2) self.assertEqual(2, mock_prepare.call_count) expected = [mock.call(server=self.l3_agent['host'], topic=topics.L3_AGENT, version='1.1'), mock.call(server=self.l3_agent_2['host'], topic=topics.L3_AGENT, version='1.1')] mock_prepare.assert_has_calls(expected, any_order=True) mock_prepare.reset_mock() self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet1['subnet']['id']}) self.assertEqual(3, mock_prepare.call_count) expected = [mock.call(server=self.l3_agent['host'], topic=topics.L3_AGENT, version='1.1'), mock.call(server=self.l3_agent_2['host'], topic=topics.L3_AGENT, version='1.1'), mock.call(server=HOST1, topic=topics.L3_AGENT, version='1.1')] mock_prepare.assert_has_calls(expected, any_order=True) mock_prepare.reset_mock() self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet2['subnet']['id']}) self.assertEqual(4, mock_prepare.call_count) expected = [mock.call(server=self.l3_agent['host'], topic=topics.L3_AGENT, version='1.1'), mock.call(server=self.l3_agent_2['host'], topic=topics.L3_AGENT, version='1.1'), mock.call(server=HOST1, topic=topics.L3_AGENT, version='1.1'), mock.call(server=HOST2, topic=topics.L3_AGENT, version='1.1')] mock_prepare.assert_has_calls(expected, any_order=True) mock_prepare.reset_mock() self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet3['subnet']['id']}) # there are no dvr serviceable ports on HOST3, so notification # goes to the same hosts self.assertEqual(4, mock_prepare.call_count) expected = [mock.call(server=self.l3_agent['host'], topic=topics.L3_AGENT, version='1.1'), mock.call(server=self.l3_agent_2['host'], topic=topics.L3_AGENT, version='1.1'), mock.call(server=HOST1, topic=topics.L3_AGENT, version='1.1'), mock.call(server=HOST2, topic=topics.L3_AGENT, version='1.1')] mock_prepare.assert_has_calls(expected, any_order=True) def test_router_is_not_removed_from_snat_agent_on_interface_removal(self): """Check that dvr router is not removed from dvr_snat l3 agents on router interface removal """ router = self._create_router(distributed=True, ha=True) kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} with self.subnet() as subnet, \ self.network(**kwargs) as ext_net, \ self.subnet(network=ext_net, cidr='20.0.0.0/24'): gw_info = {'network_id': ext_net['network']['id']} self.l3_plugin.update_router( self.context, router['id'], {'router': {l3_apidef.EXTERNAL_GW_INFO: gw_info}}) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) self._assert_router_is_hosted_on_both_dvr_snat_agents(router) with mock.patch.object(self.l3_plugin, '_l3_rpc_notifier') as l3_notifier: self.l3_plugin.remove_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) self._assert_router_is_hosted_on_both_dvr_snat_agents(router) self.assertFalse(l3_notifier.router_removed_from_agent.called) def test_router_is_not_removed_from_snat_agent_on_dhcp_port_deletion(self): """Check that dvr router is not removed from l3 agent hosting SNAT for it on DHCP port removal """ router = self._create_router(distributed=True, ha=True) kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} with self.network(**kwargs) as ext_net, \ self.subnet(network=ext_net), \ self.subnet(cidr='20.0.0.0/24') as subnet, \ self.port(subnet=subnet, device_owner=constants.DEVICE_OWNER_DHCP) as port: self.core_plugin.update_port( self.context, port['port']['id'], {'port': {'binding:host_id': self.l3_agent['host']}}) gw_info = {'network_id': ext_net['network']['id']} self.l3_plugin.update_router( self.context, router['id'], {'router': {l3_apidef.EXTERNAL_GW_INFO: gw_info}}) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) # router should be scheduled to both dvr_snat l3 agents self._assert_router_is_hosted_on_both_dvr_snat_agents(router) notifier = self.l3_plugin.agent_notifiers[ constants.AGENT_TYPE_L3] with mock.patch.object( notifier, 'router_removed_from_agent', side_effect=Exception("BOOOOOOM!")) as remove_mock: self._delete('ports', port['port']['id']) # now when port is deleted the router still has external # gateway and should still be scheduled to the snat agent remove_mock.assert_not_called() self._assert_router_is_hosted_on_both_dvr_snat_agents(router) def _get_ha_interface_list_for_router(self, router): return self.l3_plugin.get_ha_router_port_bindings(self.context, [router['id']]) def _delete_router(self, router): self.l3_plugin.delete_router(self.context, router['id']) def _check_dvr_ha_interfaces_presence(self, rtr, int_cnt): self.assertEqual(int_cnt, len(self._get_ha_interface_list_for_router(rtr))) def _create_external_network(self): kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), extnet_apidef.EXTERNAL: True} ext_net = self._make_network(self.fmt, 'ext_net', True, **kwargs) self._make_subnet( self.fmt, ext_net, '10.0.0.1', '10.0.0.0/24', ip_version=4, enable_dhcp=True) self._make_subnet( self.fmt, ext_net, '2001:db8::1', '2001:db8::/64', ip_version=6, enable_dhcp=True) return ext_net def _set_external_gateway(self, router, ext_net): gw_info = {'network_id': ext_net['network']['id']} self.l3_plugin.update_router( self.context, router['id'], {'router': {l3_apidef.EXTERNAL_GW_INFO: gw_info}}) def _clear_external_gateway(self, router): self.l3_plugin.update_router( self.context, router['id'], {'router': {l3_apidef.EXTERNAL_GW_INFO: {}}}) def _remove_interface_from_router(self, router, subnet): self.l3_plugin.remove_router_interface( self.context, router['id'], {'subnet_id': subnet['subnet']['id']}) def _check_snat_external_gateway_presence(self, ext_net, router, gw_count): ext_net_id = ext_net['network']['id'] gw_port = (self.l3_plugin._core_plugin. _get_router_gw_ports_by_network(self.context, ext_net_id)) self.assertEqual(gw_count, len(gw_port)) if gw_count > 1: self.assertEqual(router['id'], gw_port[0].device_id) def _check_snat_internal_gateways_presence(self, router, subnet, int_cnt): snat_router_intfs = self.l3_plugin._get_snat_sync_interfaces( self.context, [router['id']]) if int_cnt == 0: self.assertEqual(0, len(snat_router_intfs)) else: snat_interfaces = snat_router_intfs[router['id']] self.assertEqual(1, len(snat_interfaces)) self.assertEqual(subnet['subnet']['id'], snat_interfaces[0]['fixed_ips'][0]['subnet_id']) def _check_internal_subnet_interface_presence(self, router, subnet, int_cnt): router_ints = self.l3_plugin._get_sync_interfaces( self.context, [router['id']], device_owners=constants.ROUTER_INTERFACE_OWNERS) self.assertEqual(int_cnt, len(router_ints)) if int_cnt > 1: self.assertEqual(subnet['subnet']['id'], router_ints[0]['fixed_ips'][0]['subnet_id']) def _add_internal_subnet_to_router(self, router): int_net = self._make_network(self.fmt, 'int_net', True) int_subnet = self._make_subnet( self.fmt, int_net, '10.1.0.1', '10.1.0.0/24', enable_dhcp=True) self.l3_plugin.add_router_interface( self.context, router['id'], {'subnet_id': int_subnet['subnet']['id']}) return int_subnet def _create_dvrha_router(self): router = self._create_router(distributed=True, ha=True) self.assertTrue(router['distributed']) self.assertTrue(router['ha']) return router def test_dvr_ha_router_create_attach_internal_external_detach_delete(self): """DVRHA Attach internal subnet followed by attach external""" # create router router = self._create_dvrha_router() self._check_dvr_ha_interfaces_presence(router, 2) # add subnet interface to router int_subnet = self._add_internal_subnet_to_router(router) self._check_internal_subnet_interface_presence(router, int_subnet, 1) # set router external gateway ext_net = self._create_external_network() self._set_external_gateway(router, ext_net) self._check_dvr_ha_interfaces_presence(router, 2) self._check_snat_external_gateway_presence(ext_net, router, 1) self._check_internal_subnet_interface_presence(router, int_subnet, 1) self._check_snat_internal_gateways_presence(router, int_subnet, 1) # clear router external gateway self._clear_external_gateway(router) self._check_dvr_ha_interfaces_presence(router, 2) self._check_snat_external_gateway_presence(ext_net, router, 0) self._check_internal_subnet_interface_presence(router, int_subnet, 1) self._check_snat_internal_gateways_presence(router, int_subnet, 0) # remove subnet interface from router self._remove_interface_from_router(router, int_subnet) self._check_internal_subnet_interface_presence(router, int_subnet, 0) # delete router self._delete_router(router) self._check_dvr_ha_interfaces_presence(router, 0) def test_get_device_owner_centralized(self): self.skipTest('Valid for DVR-only routers') def test_update_router_db_centralized_to_distributed(self): self.skipTest('Valid for DVR-only routers') def test__get_router_ids_for_agent(self): self.skipTest('Valid for DVR-only routers') def test_router_auto_scheduling(self): self.skipTest('Valid for DVR-only routers') neutron-12.1.1/neutron/tests/functional/services/__init__.py0000664000175000017500000000000013553660046024240 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/trunk/0000775000175000017500000000000013553660156023306 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/trunk/rpc/0000775000175000017500000000000013553660156024072 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/trunk/rpc/__init__.py0000664000175000017500000000000013553660046026167 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/trunk/rpc/test_server.py0000664000175000017500000000271713553660047027017 0ustar zuulzuul00000000000000# (c) Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.services.trunk import constants from neutron.services.trunk import plugin as trunk_plugin from neutron.tests.common import helpers from neutron.tests.unit.plugins.ml2 import base as ml2_test_base class TrunkSkeletonTestCase(ml2_test_base.ML2TestFramework): def setUp(self): super(TrunkSkeletonTestCase, self).setUp() self.trunk_plugin = trunk_plugin.TrunkPlugin() def test__handle_port_binding_set_device_owner(self): helpers.register_ovs_agent(host=helpers.HOST) with self.port() as subport: port = ( self.trunk_plugin. _rpc_backend._skeleton._handle_port_binding( self.context, subport['port']['id'], mock.ANY, helpers.HOST)) self.assertEqual( constants.TRUNK_SUBPORT_OWNER, port['device_owner']) neutron-12.1.1/neutron/tests/functional/services/trunk/drivers/0000775000175000017500000000000013553660156024764 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/trunk/drivers/openvswitch/0000775000175000017500000000000013553660156027335 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/trunk/drivers/openvswitch/agent/0000775000175000017500000000000013553660156030433 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/tests/functional/services/trunk/drivers/openvswitch/agent/test_ovsdb_handler.pyneutron-12.1.1/neutron/tests/functional/services/trunk/drivers/openvswitch/agent/test_ovsdb_handler.0000664000175000017500000002113413553660047034305 0ustar zuulzuul00000000000000# Copyright (c) 2016 SUSE Linux Products GmbH # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as n_consts from neutron_lib.utils import helpers from neutron_lib.utils import net from oslo_utils import uuidutils from neutron.agent.common import ovs_lib from neutron.common import utils as common_utils from neutron.objects import trunk as trunk_obj from neutron.services.trunk.drivers.openvswitch.agent import ovsdb_handler from neutron.services.trunk.drivers.openvswitch.agent import trunk_manager from neutron.tests.functional.agent.l2 import base def generate_tap_device_name(): return n_consts.TAP_DEVICE_PREFIX + helpers.get_random_string( n_consts.DEVICE_NAME_MAX_LEN - len(n_consts.TAP_DEVICE_PREFIX)) class OVSDBHandlerTestCase(base.OVSAgentTestFramework): """Test functionality of OVSDBHandler. This suite aims for interaction between events coming from OVSDB monitor, agent and wiring ports via trunk bridge to integration bridge. """ def setUp(self): """Prepare resources. Set up trunk_dict representing incoming data from Neutron-server when fetching for trunk details. Another resource trunk_br represents the trunk bridge which its creation is simulated when creating a port in l2 agent framework. """ super(OVSDBHandlerTestCase, self).setUp() trunk_id = uuidutils.generate_uuid() self.trunk_dict = { 'id': trunk_id, 'mac_address': net.get_random_mac('fa:16:3e:00:00:00'.split(':')), 'sub_ports': []} self.trunk_port_name = generate_tap_device_name() self.trunk_br = trunk_manager.TrunkBridge(trunk_id) self.ovsdb_handler = self._prepare_mocked_ovsdb_handler() def _prepare_mocked_ovsdb_handler(self): handler = ovsdb_handler.OVSDBHandler( trunk_manager.TrunkManager(ovs_lib.OVSBridge(self.br_int))) mock.patch.object(handler, 'trunk_rpc').start() handler.trunk_rpc.get_trunk_details.side_effect = ( self._mock_get_trunk_details) handler.trunk_rpc.update_subport_bindings.side_effect = ( self._mock_update_subport_binding) return handler def _mock_get_trunk_details(self, context, parent_port_id): if parent_port_id == self.trunk_dict['port_id']: return trunk_obj.Trunk(**self.trunk_dict) def _mock_update_subport_binding(self, context, subports): return {self.trunk_dict['id']: [ {'id': subport['port_id'], 'mac_address': subport['mac_address']} for subport in subports] } def _plug_ports(self, network, ports, agent, bridge=None, namespace=None): # creates only the trunk, the sub_port will be plugged by the # trunk manager if not self.trunk_br.exists(): self.trunk_br.create() self.addCleanup(self.trunk_br.destroy) self.driver.plug( network['id'], self.trunk_dict['port_id'], self.trunk_port_name, self.trunk_dict['mac_address'], self.trunk_br.br_name) def _mock_get_events(self, agent, polling_manager, ports): get_events = polling_manager.get_events p_ids = [p['id'] for p in ports] def filter_events(): events = get_events() filtered_events = { 'added': [], 'removed': [] } for event_type in filtered_events: for dev in events[event_type]: iface_id = agent.int_br.portid_from_external_ids( dev.get('external_ids', [])) is_for_this_test = ( iface_id in p_ids or iface_id == self.trunk_dict['port_id'] or dev['name'] == self.trunk_br.br_name) if is_for_this_test: # if the event is not about a port that was created by # this test, we filter the event out. Since these tests # are not run in isolation processing all the events # might make some test fail ( e.g. the agent might keep # resycing because it keeps finding not ready ports # that are created by other tests) filtered_events[event_type].append(dev) return filtered_events mock.patch.object(polling_manager, 'get_events', side_effect=filter_events).start() def _fill_trunk_dict(self, num=3): ports = self.create_test_ports(amount=num) self.trunk_dict['port_id'] = ports[0]['id'] self.trunk_dict['sub_ports'] = [trunk_obj.SubPort( id=uuidutils.generate_uuid(), port_id=ports[i]['id'], mac_address=ports[i]['mac_address'], segmentation_id=i, trunk_id=self.trunk_dict['id']) for i in range(1, num)] return ports def _test_trunk_creation_helper(self, ports): self.setup_agent_and_ports(port_dicts=ports) self.wait_until_ports_state(self.ports, up=True) self.trunk_br.delete_port(self.trunk_port_name) self.wait_until_ports_state(self.ports, up=False) common_utils.wait_until_true(lambda: not self.trunk_br.bridge_exists(self.trunk_br.br_name)) def test_trunk_creation_with_subports(self): ports = self._fill_trunk_dict() self._test_trunk_creation_helper(ports[:1]) def test_trunk_creation_with_no_subports(self): ports = self.create_test_ports(amount=1) self.trunk_dict['port_id'] = ports[0]['id'] self._test_trunk_creation_helper(ports) def test_resync(self): ports = self._fill_trunk_dict() self.setup_agent_and_ports(port_dicts=ports) self.wait_until_ports_state(self.ports, up=True) self.agent.fullsync = True self.wait_until_ports_state(self.ports, up=True) def test_restart_subport_events(self): ports = self._fill_trunk_dict() self.setup_agent_and_ports(port_dicts=ports) self.wait_until_ports_state(self.ports, up=True) # restart and simulate a subport delete deleted_port = self.ports[2] deleted_sp = trunk_manager.SubPort( self.trunk_dict['id'], deleted_port['id']) self.stop_agent(self.agent, self.agent_thread) self.polling_manager.stop() self.trunk_dict['sub_ports'] = self.trunk_dict['sub_ports'][:1] self.setup_agent_and_ports(port_dicts=ports[:2]) # NOTE: the port_dicts passed in setup_agent_and_ports is stored in # self.ports so we are waiting here only for ports[:2] self.wait_until_ports_state(self.ports, up=True) common_utils.wait_until_true( lambda: (deleted_sp.patch_port_trunk_name not in self.trunk_br.get_port_name_list())) def test_cleanup_on_vm_delete(self): with mock.patch.object(self.ovsdb_handler, 'handle_trunk_remove'): br_int = ovs_lib.OVSBridge(self.br_int) ports = self._fill_trunk_dict() self.setup_agent_and_ports(port_dicts=ports[:1]) self.wait_until_ports_state(self.ports, up=True) self.trunk_br.delete_port(self.trunk_port_name) # We do not expect any instance port to show up on the trunk # bridge so we can set a much more aggressive timeout and # fail fast(er). self.ovsdb_handler.timeout = 1 self.ovsdb_handler.handle_trunk_add(self.trunk_br.br_name) # Check no resources are left behind. self.assertFalse(self.trunk_br.exists()) self.assertFalse(ovsdb_handler.bridge_has_service_port(br_int)) def test_do_not_delete_trunk_bridge_with_instance_ports(self): ports = self._fill_trunk_dict() self.setup_agent_and_ports(port_dicts=ports) self.wait_until_ports_state(self.ports, up=True) self.ovsdb_handler.handle_trunk_remove(self.trunk_br.br_name, ports.pop()) self.assertTrue(self.trunk_br.exists()) neutron-12.1.1/neutron/tests/functional/services/trunk/drivers/openvswitch/agent/__init__.py0000664000175000017500000000000013553660046032530 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/tests/functional/services/trunk/drivers/openvswitch/agent/test_trunk_manager.pyneutron-12.1.1/neutron/tests/functional/services/trunk/drivers/openvswitch/agent/test_trunk_manager.0000664000175000017500000002534413553660046034336 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.utils import net from oslo_log import log as logging from oslo_utils import uuidutils import testtools from neutron.services.trunk.drivers.openvswitch.agent import trunk_manager from neutron.services.trunk.drivers.openvswitch import utils from neutron.tests.common import conn_testers from neutron.tests.common import helpers from neutron.tests.common import net_helpers from neutron.tests.functional import base from neutron.tests.functional import constants as test_constants LOG = logging.getLogger(__name__) VLAN_RANGE = set(range(1, test_constants.VLAN_COUNT - 1)) class FakeOVSDBException(Exception): pass class TrunkParentPortTestCase(base.BaseSudoTestCase): def setUp(self): super(TrunkParentPortTestCase, self).setUp() trunk_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() port_mac = net.get_random_mac('fa:16:3e:00:00:00'.split(':')) self.trunk = trunk_manager.TrunkParentPort(trunk_id, port_id, port_mac) self.trunk.bridge = self.useFixture( net_helpers.OVSTrunkBridgeFixture( self.trunk.bridge.br_name)).bridge self.br_int = self.useFixture(net_helpers.OVSBridgeFixture()).bridge def test_plug(self): self.trunk.plug(self.br_int) self.assertIn(self.trunk.patch_port_trunk_name, self.trunk.bridge.get_port_name_list()) self.assertIn(self.trunk.patch_port_int_name, self.br_int.get_port_name_list()) def test_plug_failure_doesnt_create_ports(self): with mock.patch.object( self.trunk.bridge.ovsdb, 'db_set', side_effect=FakeOVSDBException): with testtools.ExpectedException(FakeOVSDBException): self.trunk.plug(self.br_int) self.assertNotIn(self.trunk.patch_port_trunk_name, self.trunk.bridge.get_port_name_list()) self.assertNotIn(self.trunk.patch_port_int_name, self.br_int.get_port_name_list()) def test_unplug(self): self.trunk.plug(self.br_int) self.trunk.unplug(self.br_int) self.assertFalse( self.trunk.bridge.bridge_exists(self.trunk.bridge.br_name)) self.assertNotIn(self.trunk.patch_port_int_name, self.br_int.get_port_name_list()) def test_unplug_failure_doesnt_delete_bridge(self): self.trunk.plug(self.br_int) with mock.patch.object( self.trunk.bridge.ovsdb, 'del_port', side_effect=FakeOVSDBException): with testtools.ExpectedException(FakeOVSDBException): self.trunk.unplug(self.br_int) self.assertTrue( self.trunk.bridge.bridge_exists(self.trunk.bridge.br_name)) self.assertIn(self.trunk.patch_port_trunk_name, self.trunk.bridge.get_port_name_list()) self.assertIn(self.trunk.patch_port_int_name, self.br_int.get_port_name_list()) class SubPortTestCase(base.BaseSudoTestCase): def setUp(self): super(SubPortTestCase, self).setUp() trunk_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() port_mac = net.get_random_mac('fa:16:3e:00:00:00'.split(':')) trunk_bridge_name = utils.gen_trunk_br_name(trunk_id) trunk_bridge = self.useFixture( net_helpers.OVSTrunkBridgeFixture(trunk_bridge_name)).bridge segmentation_id = helpers.get_not_used_vlan( trunk_bridge, VLAN_RANGE) self.subport = trunk_manager.SubPort( trunk_id, port_id, port_mac, segmentation_id) self.subport.bridge = trunk_bridge self.br_int = self.useFixture(net_helpers.OVSBridgeFixture()).bridge def test_plug(self): self.subport.plug(self.br_int) self.assertIn(self.subport.patch_port_trunk_name, self.subport.bridge.get_port_name_list()) self.assertIn(self.subport.patch_port_int_name, self.br_int.get_port_name_list()) self.assertEqual( self.subport.segmentation_id, self.subport.bridge.db_get_val( 'Port', self.subport.patch_port_trunk_name, 'tag')) def test_plug_failure_doesnt_create_ports(self): with mock.patch.object( self.subport.bridge.ovsdb, 'db_set', side_effect=FakeOVSDBException): with testtools.ExpectedException(FakeOVSDBException): self.subport.plug(self.br_int) self.assertNotIn(self.subport.patch_port_trunk_name, self.subport.bridge.get_port_name_list()) self.assertNotIn(self.subport.patch_port_int_name, self.br_int.get_port_name_list()) def test_unplug(self): self.subport.plug(self.br_int) self.subport.unplug(self.br_int) self.assertNotIn(self.subport.patch_port_trunk_name, self.subport.bridge.get_port_name_list()) self.assertNotIn(self.subport.patch_port_int_name, self.br_int.get_port_name_list()) def test_unplug_failure(self): self.subport.plug(self.br_int) with mock.patch.object( self.subport.bridge.ovsdb, 'del_port', side_effect=FakeOVSDBException): with testtools.ExpectedException(FakeOVSDBException): self.subport.unplug(self.br_int) self.assertIn(self.subport.patch_port_trunk_name, self.subport.bridge.get_port_name_list()) self.assertIn(self.subport.patch_port_int_name, self.br_int.get_port_name_list()) class TrunkManagerTestCase(base.BaseSudoTestCase): net1_cidr = '192.178.0.1/24' net2_cidr = '192.168.0.1/24' def setUp(self): super(TrunkManagerTestCase, self).setUp() trunk_id = uuidutils.generate_uuid() self.tester = self.useFixture( conn_testers.OVSTrunkConnectionTester( self.net1_cidr, utils.gen_trunk_br_name(trunk_id))) self.trunk_manager = trunk_manager.TrunkManager( self.tester.bridge) self.trunk = trunk_manager.TrunkParentPort( trunk_id, uuidutils.generate_uuid()) def test_connectivity(self): """Test connectivity with trunk and sub ports. In this test we create a vm that has a trunk on net1 and a vm peer on the same network. We check connectivity between the peer and the vm. We create a sub port on net2 and a peer, check connectivity again. """ vlan_net1 = helpers.get_not_used_vlan(self.tester.bridge, VLAN_RANGE) vlan_net2 = helpers.get_not_used_vlan(self.tester.bridge, VLAN_RANGE) trunk_mac = net.get_random_mac('fa:16:3e:00:00:00'.split(':')) sub_port_mac = net.get_random_mac('fa:16:3e:00:00:00'.split(':')) sub_port_segmentation_id = helpers.get_not_used_vlan( self.tester.bridge, VLAN_RANGE) LOG.debug("Using %(n1)d vlan tag as local vlan ID for net1 and %(n2)d " "for local vlan ID for net2", { 'n1': vlan_net1, 'n2': vlan_net2}) self.tester.set_peer_tag(vlan_net1) self.trunk_manager.create_trunk(self.trunk.trunk_id, self.trunk.port_id, trunk_mac) # tag the patch port, this should be done by the ovs agent but we mock # it for this test conn_testers.OVSBaseConnectionTester.set_tag( self.trunk.patch_port_int_name, self.tester.bridge, vlan_net1) self.tester.wait_for_connection(self.tester.INGRESS) self.tester.wait_for_connection(self.tester.EGRESS) self.tester.add_vlan_interface_and_peer(sub_port_segmentation_id, self.net2_cidr) conn_testers.OVSBaseConnectionTester.set_tag( self.tester._peer2.port.name, self.tester.bridge, vlan_net2) sub_port = trunk_manager.SubPort(self.trunk.trunk_id, uuidutils.generate_uuid(), sub_port_mac, sub_port_segmentation_id) self.trunk_manager.add_sub_port(sub_port.trunk_id, sub_port.port_id, sub_port.port_mac, sub_port.segmentation_id) # tag the patch port, this should be done by the ovs agent but we mock # it for this test conn_testers.OVSBaseConnectionTester.set_tag( sub_port.patch_port_int_name, self.tester.bridge, vlan_net2) self.tester.wait_for_sub_port_connectivity(self.tester.INGRESS) self.tester.wait_for_sub_port_connectivity(self.tester.EGRESS) self.trunk_manager.remove_sub_port(sub_port.trunk_id, sub_port.port_id) self.tester.wait_for_sub_port_no_connectivity(self.tester.INGRESS) self.tester.wait_for_sub_port_no_connectivity(self.tester.EGRESS) self.trunk_manager.remove_trunk(self.trunk.trunk_id, self.trunk.port_id) self.tester.wait_for_no_connection(self.tester.INGRESS) class TrunkManagerDisposeTrunkTestCase(base.BaseSudoTestCase): def setUp(self): super(TrunkManagerDisposeTrunkTestCase, self).setUp() trunk_id = uuidutils.generate_uuid() self.trunk = trunk_manager.TrunkParentPort( trunk_id, uuidutils.generate_uuid()) self.trunk.bridge = self.useFixture( net_helpers.OVSTrunkBridgeFixture( self.trunk.bridge.br_name)).bridge self.br_int = self.useFixture(net_helpers.OVSBridgeFixture()).bridge self.trunk_manager = trunk_manager.TrunkManager( self.br_int) def test_dispose_trunk(self): self.trunk.plug(self.br_int) self.trunk_manager.dispose_trunk(self.trunk.bridge) self.assertFalse( self.trunk.bridge.bridge_exists(self.trunk.bridge.br_name)) self.assertNotIn(self.trunk.patch_port_int_name, self.br_int.get_port_name_list()) neutron-12.1.1/neutron/tests/functional/services/trunk/drivers/openvswitch/__init__.py0000664000175000017500000000000013553660046031432 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/trunk/drivers/__init__.py0000664000175000017500000000000013553660046027061 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/trunk/test_plugin.py0000664000175000017500000000451313553660047026217 0ustar zuulzuul00000000000000# (c) Copyright 2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings as pb from neutron.services.trunk.drivers.openvswitch import utils from neutron.services.trunk import plugin as trunk_plugin from neutron.tests.common import helpers from neutron.tests.unit.plugins.ml2 import base as ml2_test_base class TestTrunkServicePlugin(ml2_test_base.ML2TestFramework): def setUp(self): super(TestTrunkServicePlugin, self).setUp() self.trunk_plugin = trunk_plugin.TrunkPlugin() def test_ovs_bridge_name_set_when_trunk_bound(self): helpers.register_ovs_agent(host=helpers.HOST) with self.port() as port: trunk_port_id = port['port']['id'] trunk_req = {'port_id': trunk_port_id, 'tenant_id': 'test_tenant', 'sub_ports': []} trunk_res = self.trunk_plugin.create_trunk(self.context, {'trunk': trunk_req}) port['port'][pb.HOST_ID] = helpers.HOST bound_port = self.core_plugin.update_port(self.context, trunk_port_id, port) self.assertEqual( utils.gen_trunk_br_name(trunk_res['id']), bound_port[pb.VIF_DETAILS][pb.VIF_DETAILS_BRIDGE_NAME]) def test_ovs_bridge_name_not_set_when_not_trunk(self): helpers.register_ovs_agent(host=helpers.HOST) with self.port() as port: port['port'][pb.HOST_ID] = helpers.HOST bound_port = self.core_plugin.update_port(self.context, port['port']['id'], port) self.assertIsNone( bound_port[pb.VIF_DETAILS].get(pb.VIF_DETAILS_BRIDGE_NAME)) neutron-12.1.1/neutron/tests/functional/services/trunk/__init__.py0000664000175000017500000000000013553660046025403 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/logapi/0000775000175000017500000000000013553660156023416 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/services/logapi/test_logging.py0000664000175000017500000001457613553660047026471 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import re import mock from neutron_lib import constants from neutron_lib import context as neutron_context from oslo_config import cfg from oslo_log import log as logging import testscenarios from neutron.agent import firewall from neutron.objects.logapi import logging_resource as log_object from neutron.plugins.ml2.drivers.openvswitch.agent import ( ovs_agent_extension_api as ovs_ext_api) from neutron.plugins.ml2.drivers.openvswitch.agent.common import ( constants as ovs_consts) from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import ( ovs_bridge) from neutron.services.logapi.drivers.openvswitch import ( ovs_firewall_log as ovs_fw_log) from neutron.tests.functional.agent import test_firewall LOG = logging.getLogger(__name__) load_tests = testscenarios.load_tests_apply_scenarios FAKE_LOG_ID = 'a2d72369-4246-4f19-bd3c-af51ec8d70cd' FAKE_PROJECT_ID = 'fake_project' log_object_dict = { 'id': FAKE_LOG_ID, 'resource_type': 'security_group', 'project_id': FAKE_PROJECT_ID, 'event': 'ALL' } FAKE_LOG_OBJECT = log_object.Log(**log_object_dict) class LoggingExtensionTestFramework(test_firewall.BaseFirewallTestCase): def setUp(self): super(LoggingExtensionTestFramework, self).setUp() cfg.CONF.set_override('extensions', ['log'], group='agent') self.context = neutron_context.get_admin_context_without_session() self._set_resource_rpc_mock() if self.firewall_name != 'openvswitch': self.skipTest("Logging extension doesn't support firewall driver" " %s at that time " % self.firewall_name) self.log_driver = self.initialize_ovs_fw_log() def initialize_ovs_fw_log(self): int_br = ovs_ext_api.OVSCookieBridge(ovs_bridge.OVSAgentBridge( self.tester.bridge.br_name)) mock.patch('ryu.base.app_manager.AppManager.get_instance').start() mock.patch( 'neutron.agent.ovsdb.impl_vsctl.OvsdbVsctl.transaction').start() log_driver = ovs_fw_log.OVSFirewallLoggingDriver(int_br) log_driver.initialize(self.resource_rpc) return log_driver def _set_resource_rpc_mock(self): self.log_info = [] def _get_sg_info_mock(context, **kwargs): return self.log_info self.resource_rpc = mock.patch( 'neutron.services.logapi.rpc.agent.LoggingApiStub').start() self.resource_rpc.get_sg_log_info_for_log_resources.side_effect = ( _get_sg_info_mock) def _set_ports_log(self, sg_rules): fake_sg_log_info = [ { 'id': FAKE_LOG_ID, 'ports_log': [ {'port_id': self.src_port_desc['device'], 'security_group_rules': sg_rules}], 'event': 'ALL', 'project_id': FAKE_PROJECT_ID }] self.log_info = fake_sg_log_info class TestLoggingExtension(LoggingExtensionTestFramework): ip_cidr = '192.168.0.1/24' def _is_log_flow_set(self, table, actions): flows = self.log_driver.int_br.br.dump_flows_for_table(table) pattern = re.compile( r"^.* table=%s.* actions=%s" % (table, actions) ) for flow in flows.splitlines(): if pattern.match(flow.strip()): return True return False def _assert_logging_flows_set(self): self.assertTrue(self._is_log_flow_set( table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE, actions=r"resubmit\(,%d\),CONTROLLER:65535" % ( ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE))) self.assertTrue(self._is_log_flow_set( table=ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE, actions="CONTROLLER:65535")) self.assertTrue(self._is_log_flow_set( table=ovs_consts.DROPPED_TRAFFIC_TABLE, actions="CONTROLLER:65535")) def _assert_logging_flows_not_set(self): self.assertFalse(self._is_log_flow_set( table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE, actions=r"resubmit\(,%d\),CONTROLLER:65535" % ( ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE))) self.assertFalse(self._is_log_flow_set( table=ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE, actions="CONTROLLER:65535")) self.assertFalse(self._is_log_flow_set( table=ovs_consts.DROPPED_TRAFFIC_TABLE, actions="CONTROLLER:65535")) def test_log_lifecycle(self): sg_rules = [{'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_ICMP, 'security_group_id': self.FAKE_SECURITY_GROUP_ID}, {'ethertype': constants.IPv4, 'direction': firewall.EGRESS_DIRECTION, 'security_group_id': self.FAKE_SECURITY_GROUP_ID}, {'ethertype': constants.IPv6, 'protocol': constants.PROTO_NAME_TCP, 'port_range_min': 22, 'port_range_max': 22, 'remote_group_id': 2, 'direction': firewall.EGRESS_DIRECTION, 'security_group_id': self.FAKE_SECURITY_GROUP_ID}, ] self.firewall.update_security_group_rules( self.FAKE_SECURITY_GROUP_ID, sg_rules) self.firewall.update_port_filter(self.src_port_desc) self._set_ports_log(sg_rules) # start log self.log_driver.start_logging( self.context, log_resources=[FAKE_LOG_OBJECT]) self._assert_logging_flows_set() # stop log self.log_driver.stop_logging( self.context, log_resources=[FAKE_LOG_OBJECT]) self._assert_logging_flows_not_set() neutron-12.1.1/neutron/tests/functional/services/logapi/__init__.py0000664000175000017500000000000013553660046025513 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/plugins/0000775000175000017500000000000013553660156022001 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/plugins/__init__.py0000664000175000017500000000000013553660046024076 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/plugins/ml2/0000775000175000017500000000000013553660156022473 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/plugins/ml2/drivers/0000775000175000017500000000000013553660156024151 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/plugins/ml2/drivers/macvtap/0000775000175000017500000000000013553660156025604 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/plugins/ml2/drivers/macvtap/agent/0000775000175000017500000000000013553660156026702 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000neutron-12.1.1/neutron/tests/functional/plugins/ml2/drivers/macvtap/agent/test_macvtap_neutron_agent.pyneutron-12.1.1/neutron/tests/functional/plugins/ml2/drivers/macvtap/agent/test_macvtap_neutron_agent0000664000175000017500000000300413553660046034242 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM Corp. # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron.plugins.ml2.drivers.macvtap.agent import macvtap_neutron_agent from neutron.tests.common import net_helpers from neutron.tests.functional import base as functional_base class MacvtapAgentTestCase(functional_base.BaseSudoTestCase): def setUp(self): super(MacvtapAgentTestCase, self).setUp() self.mgr = macvtap_neutron_agent.MacvtapManager({}) def test_get_all_devices(self): # Veth is simulating the hosts eth device. In this test it is used as # src_dev for the macvtap veth1, veth2 = self.useFixture(net_helpers.VethFixture()).ports macvtap = self.useFixture(net_helpers.MacvtapFixture( src_dev=veth1.name, mode='bridge', prefix=constants.MACVTAP_DEVICE_PREFIX)).ip_dev self.assertEqual(set([macvtap.link.address]), self.mgr.get_all_devices()) neutron-12.1.1/neutron/tests/functional/plugins/ml2/drivers/macvtap/agent/__init__.py0000664000175000017500000000000013553660046030777 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/plugins/ml2/drivers/macvtap/__init__.py0000664000175000017500000000000013553660046027701 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/plugins/ml2/drivers/__init__.py0000664000175000017500000000000013553660046026246 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/plugins/ml2/test_plugin.py0000664000175000017500000000665613553660046025415 0ustar zuulzuul00000000000000# Copyright (c) 2016 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib import context from neutron.db import agents_db from neutron.tests.common import helpers from neutron.tests.unit.plugins.ml2 import base as ml2_test_base DEVICE_OWNER_COMPUTE = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' class TestMl2PortBinding(ml2_test_base.ML2TestFramework, agents_db.AgentDbMixin): def setUp(self): super(TestMl2PortBinding, self).setUp() self.admin_context = context.get_admin_context() self.host_args = {portbindings.HOST_ID: helpers.HOST, 'admin_state_up': True} def test_port_bind_successfully(self): helpers.register_ovs_agent(host=helpers.HOST) with self.network() as network: with self.subnet(network=network) as subnet: with self.port( subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID, 'admin_state_up',), **self.host_args) as port: # Note: Port creation invokes _bind_port_if_needed(), # therefore it is all we need in order to test a successful # binding self.assertEqual(port['port']['binding:vif_type'], portbindings.VIF_TYPE_OVS) def test_port_bind_retry(self): agent = helpers.register_ovs_agent(host=helpers.HOST) helpers.kill_agent(agent_id=agent.id) with self.network() as network: with self.subnet(network=network) as subnet: with self.port( subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE, arg_list=(portbindings.HOST_ID, 'admin_state_up',), **self.host_args) as port: # Since the agent is dead, expect binding to fail self.assertEqual(port['port']['binding:vif_type'], portbindings.VIF_TYPE_BINDING_FAILED) helpers.revive_agent(agent.id) # When an agent starts, The RPC call get_device_details() # will invoke get_bound_port_context() which eventually use # _bind_port_if_needed() bound_context = self.plugin.get_bound_port_context( self.admin_context, port['port']['id'], helpers.HOST) # Since the agent is back online, expect binding to succeed self.assertEqual(bound_context.vif_type, portbindings.VIF_TYPE_OVS) self.assertEqual(bound_context.current['binding:vif_type'], portbindings.VIF_TYPE_OVS) neutron-12.1.1/neutron/tests/functional/plugins/ml2/__init__.py0000664000175000017500000000000013553660046024570 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/0000775000175000017500000000000013553660156021416 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/test_ovs_flows.py0000664000175000017500000005212613553660047025055 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import eventlet import fixtures import mock import testscenarios from neutron_lib import constants as n_const from oslo_config import cfg from oslo_serialization import jsonutils from oslo_utils import importutils from testtools.content import text_content from neutron.agent.common import ovs_lib from neutron.agent.common import utils from neutron.agent.linux import ip_lib from neutron.cmd.sanity import checks from neutron.common import utils as common_utils from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent \ import ovs_neutron_agent as ovsagt from neutron.tests.common import base as common_base from neutron.tests.common import helpers from neutron.tests.common import net_helpers from neutron.tests.functional.agent import test_ovs_lib from neutron.tests.functional import base from neutron.tests import tools OVS_TRACE_FINAL_FLOW = 'Final flow' OVS_TRACE_DATAPATH_ACTIONS = 'Datapath actions' cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.' 'common.config') class OVSAgentTestBase(test_ovs_lib.OVSBridgeTestBase, base.BaseSudoTestCase): scenarios = testscenarios.multiply_scenarios([ ('ofctl', {'main_module': ('neutron.plugins.ml2.drivers.openvswitch.' 'agent.openflow.ovs_ofctl.main')}), ('native', {'main_module': ('neutron.plugins.ml2.drivers.openvswitch.' 'agent.openflow.native.main')})], test_ovs_lib.OVSBridgeTestBase.scenarios) def setUp(self): super(OVSAgentTestBase, self).setUp() self.br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge self.of_interface_mod = importutils.import_module(self.main_module) self.br_int_cls = None self.br_tun_cls = None self.br_phys_cls = None self.br_int = None self.init_done = False self.init_done_ev = eventlet.event.Event() self.main_ev = eventlet.event.Event() self.addCleanup(self._kill_main) retry_count = 3 while True: cfg.CONF.set_override('of_listen_port', net_helpers.get_free_namespace_port( n_const.PROTO_NAME_TCP), group='OVS') self.of_interface_mod.init_config() self._main_thread = eventlet.spawn(self._kick_main) # Wait for _kick_main -> of_interface main -> _agent_main # NOTE(yamamoto): This complexity came from how "native" # of_interface runs its openflow controller. "native" # of_interface's main routine blocks while running the # embedded openflow controller. In that case, the agent # rpc_loop runs in another thread. However, for FT we # need to run setUp() and test_xxx() in the same thread. # So I made this run of_interface's main in a separate # thread instead. try: while not self.init_done: self.init_done_ev.wait() break except fixtures.TimeoutException: self._kill_main() retry_count -= 1 if retry_count < 0: raise Exception('port allocation failed') def _run_trace(self, brname, spec): required_keys = [OVS_TRACE_FINAL_FLOW, OVS_TRACE_DATAPATH_ACTIONS] t = utils.execute(["ovs-appctl", "ofproto/trace", brname, spec], run_as_root=True) trace = {} trace_lines = t.splitlines() for line in trace_lines: (l, sep, r) = line.partition(':') if not sep: continue elif l in required_keys: trace[l] = r for k in required_keys: if k not in trace: self.fail("%s not found in trace %s" % (k, trace_lines)) return trace def _kick_main(self): with mock.patch.object(ovsagt, 'main', self._agent_main): self.of_interface_mod.main() def _kill_main(self): self.main_ev.send() self._main_thread.wait() def _agent_main(self, bridge_classes): self.br_int_cls = bridge_classes['br_int'] self.br_phys_cls = bridge_classes['br_phys'] self.br_tun_cls = bridge_classes['br_tun'] self.br_int = self.br_int_cls(self.br.br_name) self.br_int.set_secure_mode() self.br_int.setup_controllers(cfg.CONF) self.br_int.setup_default_table() # signal to setUp() self.init_done = True self.init_done_ev.send() self.main_ev.wait() class ARPSpoofTestCase(OVSAgentTestBase): def setUp(self): # NOTE(kevinbenton): it would be way cooler to use scapy for # these but scapy requires the python process to be running as # root to bind to the ports. self.addOnException(self.collect_flows_and_ports) super(ARPSpoofTestCase, self).setUp() self.skip_without_arp_support() self.src_addr = '192.168.0.1' self.dst_addr = '192.168.0.2' self.src_namespace = self.useFixture( net_helpers.NamespaceFixture()).name self.dst_namespace = self.useFixture( net_helpers.NamespaceFixture()).name self.src_p = self.useFixture( net_helpers.OVSPortFixture(self.br, self.src_namespace)).port self.dst_p = self.useFixture( net_helpers.OVSPortFixture(self.br, self.dst_namespace)).port # wait to add IPs until after anti-spoof rules to ensure ARP doesn't # happen before def collect_flows_and_ports(self, exc_info): nicevif = lambda x: ['%s=%s' % (k, getattr(x, k)) for k in ['ofport', 'port_name', 'switch', 'vif_id', 'vif_mac']] nicedev = lambda x: ['%s=%s' % (k, getattr(x, k)) for k in ['name', 'namespace']] + x.addr.list() details = {'flows': self.br.dump_all_flows(), 'vifs': map(nicevif, self.br.get_vif_ports()), 'src_ip': self.src_addr, 'dest_ip': self.dst_addr, 'sourt_port': nicedev(self.src_p), 'dest_port': nicedev(self.dst_p)} self.addDetail('arp-test-state', text_content(jsonutils.dumps(details, indent=5))) @common_base.no_skip_on_missing_deps def skip_without_arp_support(self): if not checks.arp_header_match_supported(): self.skipTest("ARP header matching not supported") def test_arp_spoof_doesnt_block_normal_traffic(self): self._setup_arp_spoof_for_port(self.src_p.name, [self.src_addr]) self._setup_arp_spoof_for_port(self.dst_p.name, [self.dst_addr]) self.src_p.addr.add('%s/24' % self.src_addr) self.dst_p.addr.add('%s/24' % self.dst_addr) net_helpers.assert_ping(self.src_namespace, self.dst_addr) def test_mac_spoof_blocks_wrong_mac(self): self._setup_arp_spoof_for_port(self.src_p.name, [self.src_addr]) self._setup_arp_spoof_for_port(self.dst_p.name, [self.dst_addr]) self.src_p.addr.add('%s/24' % self.src_addr) self.dst_p.addr.add('%s/24' % self.dst_addr) net_helpers.assert_ping(self.src_namespace, self.dst_addr) # changing the allowed mac should stop the port from working self._setup_arp_spoof_for_port(self.src_p.name, [self.src_addr], mac='00:11:22:33:44:55') net_helpers.assert_no_ping(self.src_namespace, self.dst_addr) def test_arp_spoof_doesnt_block_ipv6(self): self.src_addr = '2000::1' self.dst_addr = '2000::2' self._setup_arp_spoof_for_port(self.src_p.name, [self.src_addr]) self._setup_arp_spoof_for_port(self.dst_p.name, [self.dst_addr]) self.src_p.addr.add('%s/64' % self.src_addr) self.dst_p.addr.add('%s/64' % self.dst_addr) # make sure the IPv6 addresses are ready before pinging self.src_p.addr.wait_until_address_ready(self.src_addr) self.dst_p.addr.wait_until_address_ready(self.dst_addr) net_helpers.assert_ping(self.src_namespace, self.dst_addr) def test_arp_spoof_blocks_response(self): # this will prevent the destination from responding to the ARP # request for it's own address self._setup_arp_spoof_for_port(self.dst_p.name, ['192.168.0.3']) self.src_p.addr.add('%s/24' % self.src_addr) self.dst_p.addr.add('%s/24' % self.dst_addr) net_helpers.assert_no_ping(self.src_namespace, self.dst_addr, count=2) def test_arp_spoof_blocks_icmpv6_neigh_advt(self): self.src_addr = '2000::1' self.dst_addr = '2000::2' # this will prevent the destination from responding (i.e., icmpv6 # neighbour advertisement) to the icmpv6 neighbour solicitation # request for it's own address (2000::2) as spoofing rules added # below only allow '2000::3'. self._setup_arp_spoof_for_port(self.dst_p.name, ['2000::3']) self.src_p.addr.add('%s/64' % self.src_addr) self.dst_p.addr.add('%s/64' % self.dst_addr) # make sure the IPv6 addresses are ready before pinging self.src_p.addr.wait_until_address_ready(self.src_addr) self.dst_p.addr.wait_until_address_ready(self.dst_addr) net_helpers.assert_no_ping(self.src_namespace, self.dst_addr, count=2) def test_arp_spoof_blocks_request(self): # this will prevent the source from sending an ARP # request with its own address self._setup_arp_spoof_for_port(self.src_p.name, ['192.168.0.3']) self.src_p.addr.add('%s/24' % self.src_addr) self.dst_p.addr.add('%s/24' % self.dst_addr) ns_ip_wrapper = ip_lib.IPWrapper(self.src_namespace) try: ns_ip_wrapper.netns.execute(['arping', '-I', self.src_p.name, '-c1', self.dst_addr]) tools.fail("arping should have failed. The arp request should " "have been blocked.") except RuntimeError: pass def test_arp_spoof_allowed_address_pairs(self): self._setup_arp_spoof_for_port(self.dst_p.name, ['192.168.0.3', self.dst_addr]) self.src_p.addr.add('%s/24' % self.src_addr) self.dst_p.addr.add('%s/24' % self.dst_addr) net_helpers.assert_ping(self.src_namespace, self.dst_addr) def test_arp_spoof_icmpv6_neigh_advt_allowed_address_pairs(self): self.src_addr = '2000::1' self.dst_addr = '2000::2' self._setup_arp_spoof_for_port(self.dst_p.name, ['2000::3', self.dst_addr]) self.src_p.addr.add('%s/64' % self.src_addr) self.dst_p.addr.add('%s/64' % self.dst_addr) # make sure the IPv6 addresses are ready before pinging self.src_p.addr.wait_until_address_ready(self.src_addr) self.dst_p.addr.wait_until_address_ready(self.dst_addr) net_helpers.assert_ping(self.src_namespace, self.dst_addr) def test_arp_spoof_allowed_address_pairs_0cidr(self): self._setup_arp_spoof_for_port(self.dst_p.name, ['9.9.9.9/0', '1.2.3.4']) self.src_p.addr.add('%s/24' % self.src_addr) self.dst_p.addr.add('%s/24' % self.dst_addr) net_helpers.assert_ping(self.src_namespace, self.dst_addr) def test_arp_spoof_disable_port_security(self): # block first and then disable port security to make sure old rules # are cleared self._setup_arp_spoof_for_port(self.dst_p.name, ['192.168.0.3']) self._setup_arp_spoof_for_port(self.dst_p.name, ['192.168.0.3'], psec=False) self.src_p.addr.add('%s/24' % self.src_addr) self.dst_p.addr.add('%s/24' % self.dst_addr) net_helpers.assert_ping(self.src_namespace, self.dst_addr) def test_arp_spoof_disable_network_port(self): # block first and then disable port security to make sure old rules # are cleared self._setup_arp_spoof_for_port(self.dst_p.name, ['192.168.0.3']) self._setup_arp_spoof_for_port( self.dst_p.name, ['192.168.0.3'], device_owner=n_const.DEVICE_OWNER_ROUTER_GW) self.src_p.addr.add('%s/24' % self.src_addr) self.dst_p.addr.add('%s/24' % self.dst_addr) net_helpers.assert_ping(self.src_namespace, self.dst_addr) def _setup_arp_spoof_for_port(self, port, addrs, psec=True, device_owner='nobody', mac=None): vif = next( vif for vif in self.br.get_vif_ports() if vif.port_name == port) ip_addr = addrs.pop() details = {'port_security_enabled': psec, 'fixed_ips': [{'ip_address': ip_addr}], 'device_owner': device_owner, 'allowed_address_pairs': [ dict(ip_address=ip) for ip in addrs]} if mac: vif.vif_mac = mac ovsagt.OVSNeutronAgent.setup_arp_spoofing_protection( self.br_int, vif, details) class CanaryTableTestCase(OVSAgentTestBase): def test_canary_table(self): self.br_int.uninstall_flows(cookie=ovs_lib.COOKIE_ANY) self.assertEqual(constants.OVS_RESTARTED, self.br_int.check_canary_table()) self.br_int.setup_canary_table() self.assertEqual(constants.OVS_NORMAL, self.br_int.check_canary_table()) class DeleteFlowsTestCase(OVSAgentTestBase): def test_delete_flows_bridge_cookie_only(self): PORT = 1 self.br_int.add_flow(in_port=PORT, ip=True, nw_dst="1.1.1.1", actions="output:11") self.br_int.add_flow(in_port=PORT, ip=True, nw_dst="2.2.2.2", cookie=42, actions="output:42") # delete (should only delete flows with the bridge cookie) self.br_int.delete_flows(in_port=PORT) flows = self.br_int.dump_flows_for(in_port=PORT, cookie=self.br_int._default_cookie) flows42 = self.br_int.dump_flows_for(in_port=PORT, cookie=42) # check that only flows with cookie 42 remain self.assertFalse(flows) self.assertTrue(flows42) def test_delete_flows_all(self): PORT = 1 self.br_int.add_flow(in_port=PORT, ip=True, nw_dst="1.1.1.1", actions="output:11") self.br_int.add_flow(in_port=PORT, ip=True, nw_dst="2.2.2.2", cookie=42, actions="output:42") # delete both flows self.br_int.delete_flows(in_port=PORT, cookie=ovs_lib.COOKIE_ANY) # check that no flow remains flows = self.br_int.dump_flows_for(in_port=PORT) self.assertFalse(flows) class OVSFlowTestCase(OVSAgentTestBase): """Tests defined in this class use ovs-appctl ofproto/trace commands, which simulate processing of imaginary packets, to check desired actions are correctly set up by OVS flows. In this way, subtle variations in flows between of_interface drivers are absorbed and the same tests work against those drivers. """ def setUp(self): cfg.CONF.set_override('enable_distributed_routing', True, group='AGENT') super(OVSFlowTestCase, self).setUp() self.phys_br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge self.br_phys = self.br_phys_cls(self.phys_br.br_name) self.br_phys.set_secure_mode() self.br_phys.setup_controllers(cfg.CONF) self.router_addr = '192.168.0.1/24' self.namespace = self.useFixture( net_helpers.NamespaceFixture()).name self.phys_p = self.useFixture( net_helpers.OVSPortFixture(self.br_phys, self.namespace)).port self.tun_br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge self.br_tun = self.br_tun_cls(self.tun_br.br_name) self.br_tun.set_secure_mode() self.br_tun.setup_controllers(cfg.CONF) self.tun_p = self.br_tun.add_patch_port( common_utils.get_rand_device_name( prefix=cfg.CONF.OVS.tun_peer_patch_port), common_utils.get_rand_device_name( prefix=cfg.CONF.OVS.int_peer_patch_port)) self.br_tun.setup_default_table(self.tun_p, True) def test_provision_local_vlan(self): kwargs = {'port': 123, 'lvid': 888, 'segmentation_id': 777} self.br_phys.provision_local_vlan(distributed=False, **kwargs) trace = self._run_trace(self.phys_br.br_name, "in_port=%(port)d,dl_src=12:34:56:78:aa:bb," "dl_dst=24:12:56:78:aa:bb,dl_type=0x0800," "nw_src=192.168.0.1,nw_dst=192.168.0.2," "nw_proto=1,nw_tos=0,nw_ttl=128," "icmp_type=8,icmp_code=0,dl_vlan=%(lvid)d" % kwargs) self.assertIn("dl_vlan=%(segmentation_id)d" % kwargs, trace["Final flow"]) def test_install_dvr_to_src_mac(self): other_dvr_mac = 'fa:16:3f:01:de:ad' other_dvr_port = 333 kwargs = {'vlan_tag': 888, 'gateway_mac': '12:34:56:78:aa:bb', 'dst_mac': '12:34:56:78:cc:dd', 'dst_port': 123} self.br_int.install_dvr_to_src_mac(network_type='vlan', **kwargs) self.br_int.add_dvr_mac_vlan(mac=other_dvr_mac, port=other_dvr_port) trace = self._run_trace(self.br.br_name, "in_port=%d," % other_dvr_port + "dl_src=" + other_dvr_mac + "," + "dl_dst=%(dst_mac)s,dl_type=0x0800," "nw_src=192.168.0.1,nw_dst=192.168.0.2," "nw_proto=1,nw_tos=0,nw_ttl=128," "icmp_type=8,icmp_code=0," "dl_vlan=%(vlan_tag)d" % kwargs) self.assertIn("vlan_tci=0x0000", trace["Final flow"]) self.assertIn(("dl_src=%(gateway_mac)s" % kwargs), trace["Final flow"]) @helpers.skip_if_ovs_older_than("2.5.1") def test_install_flood_to_tun(self): attrs = { 'remote_ip': self.get_test_net_address(1), 'local_ip': self.get_test_net_address(2), } kwargs = {'vlan': 777, 'tun_id': 888} port_name = common_utils.get_rand_device_name(net_helpers.PORT_PREFIX) ofport = self.br_tun.add_tunnel_port(port_name, attrs['remote_ip'], attrs['local_ip']) self.br_tun.install_flood_to_tun(ports=[ofport], **kwargs) test_packet = ("icmp,in_port=%d," % self.tun_p + "dl_src=12:34:56:ab:cd:ef,dl_dst=12:34:56:78:cc:dd," "nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_ecn=0," "nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0," "dl_vlan=%(vlan)d,dl_vlan_pcp=0" % kwargs) trace = self._run_trace(self.tun_br.br_name, test_packet) self.assertIn(("tun_id=0x%(tun_id)x" % kwargs), trace["Final flow"]) self.assertIn("vlan_tci=0x0000,", trace["Final flow"]) self.br_tun.delete_flood_to_tun(kwargs['vlan']) trace = self._run_trace(self.tun_br.br_name, test_packet) self.assertEqual(" unchanged", trace["Final flow"]) self.assertIn("drop", trace["Datapath actions"]) def test_install_instructions_str(self): kwargs = {'in_port': 345, 'vlan_tci': 0x1123} dst_p = self.useFixture( net_helpers.OVSPortFixture(self.br_tun, self.namespace)).port dst_ofp = self.br_tun.get_port_ofport(dst_p.name) self.br_tun.install_instructions("pop_vlan,output:%d" % dst_ofp, priority=10, **kwargs) trace = self._run_trace(self.br_tun.br_name, "in_port=%(in_port)d,dl_src=12:34:56:78:aa:bb," "dl_dst=24:12:56:78:aa:bb,dl_type=0x0800," "nw_src=192.168.0.1,nw_dst=192.168.0.2," "nw_proto=1,nw_tos=0,nw_ttl=128," "icmp_type=8,icmp_code=0,vlan_tci=%(vlan_tci)d" % kwargs) self.assertIn("pop_vlan,", trace["Datapath actions"]) neutron-12.1.1/neutron/tests/functional/agent/windows/0000775000175000017500000000000013553660156023110 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/windows/test_ip_lib.py0000664000175000017500000000320213553660047025753 0ustar zuulzuul00000000000000# Copyright 2016 Cloudbase Solutions. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.windows import ip_lib from neutron.tests import base WRONG_IP = '0.0.0.0' TEST_IP = '127.0.0.1' TEST_MAC = '00:00:00:00:00:00' class IpLibTestCase(base.BaseTestCase): def test_ipwrapper_get_device_by_ip_None(self): self.assertIsNone(ip_lib.IPWrapper().get_device_by_ip(WRONG_IP)) def test_ipwrapper_get_device_by_ip(self): ip_dev = ip_lib.IPWrapper().get_device_by_ip(TEST_IP) self.assertEqual('lo', ip_dev.name) def test_device_has_ip(self): not_a_device = ip_lib.IPDevice('#!#._not_a_device_bleargh!!@@@') self.assertFalse(not_a_device.device_has_ip(TEST_IP)) def test_ip_link_read_mac_address(self): ip_dev = ip_lib.IPWrapper().get_device_by_ip(TEST_IP) self.assertEqual([TEST_MAC], ip_lib.IPLink(ip_dev).address) def test_ip_link_read_mac_address_wrong(self): not_a_device = ip_lib.IPDevice('#!#._not_a_device_bleargh!!@@@') mac_addr = ip_lib.IPLink(not_a_device).address self.assertFalse(mac_addr) neutron-12.1.1/neutron/tests/functional/agent/windows/__init__.py0000664000175000017500000000000013553660046025205 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/linux/0000775000175000017500000000000013553660156022555 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/linux/openvswitch_firewall/0000775000175000017500000000000013553660156027013 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/linux/openvswitch_firewall/test_iptables.py0000664000175000017500000001050413553660047032226 0ustar zuulzuul00000000000000# Copyright 2017 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from oslo_utils import uuidutils from neutron.agent import firewall from neutron.agent.linux import iptables_firewall import neutron.agent.linux.openvswitch_firewall.firewall as ovs_fw_mod import neutron.agent.linux.openvswitch_firewall.iptables as iptables_helper from neutron.tests.common import conn_testers from neutron.tests.common import net_helpers from neutron.tests.functional.agent import test_firewall from neutron.tests.functional import base class TestHelper(base.BaseSudoTestCase): def setUp(self): super(TestHelper, self).setUp() self.bridge = self.useFixture(net_helpers.OVSBridgeFixture()).bridge self.namespace = self.useFixture(net_helpers.NamespaceFixture()).name self.iptables_firewall = ( iptables_firewall.OVSHybridIptablesFirewallDriver(self.namespace)) def add_sg_rules(self, port, security_group_rules): """Add security group rules to given port. Method creates a security group for isolated firewall use. Adds passed rules to it and calls to prepare_port_filter() to the firewall driver. Method returns port description. """ sg_id = uuidutils.generate_uuid() self.iptables_firewall.update_security_group_rules( sg_id, security_group_rules) description = { 'admin_state_up': True, 'device': port.port_id, 'device_owner': test_firewall.DEVICE_OWNER_COMPUTE, 'fixed_ips': ['192.168.0.1'], 'mac_address': port.port.link.address, 'port_security_enabled': True, 'security_groups': [sg_id], 'status': 'ACTIVE', 'network_id': uuidutils.generate_uuid()} self.iptables_firewall.prepare_port_filter(description) return description def _set_vlan_tag_on_port(self, port, tag): qvo_dev_name = iptables_helper.get_device_port_name(port.port_id) conn_testers.OVSBaseConnectionTester.set_tag( qvo_dev_name, self.bridge, tag) def _prepare_port_and_description(self, security_group_rules): hybrid_port = self.useFixture( net_helpers.OVSPortFixture( self.bridge, self.namespace, hybrid_plug=True)) self._set_vlan_tag_on_port(hybrid_port, 1) description = self.add_sg_rules(hybrid_port, security_group_rules) return hybrid_port, description def _check_no_iptables_rules_for_port(self, port): tap_name = self.iptables_firewall._get_device_name( {'device': port.port_id}) iptables_rules = ( self.iptables_firewall.iptables.get_rules_for_table('filter')) for line in iptables_rules: if tap_name in line: raise Exception("port %s still has iptables rules in %s" % ( tap_name, line)) def test_migration(self): sg_rules = [{'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_ICMP}, {'ethertype': constants.IPv4, 'direction': firewall.EGRESS_DIRECTION}] port, desc = self._prepare_port_and_description(sg_rules) ovs_firewall = ovs_fw_mod.OVSFirewallDriver(self.bridge) # Check that iptables driver was set and replace it with the one that # has access to namespace if isinstance( ovs_firewall.iptables_helper.iptables_driver, iptables_firewall.OVSHybridIptablesFirewallDriver): ovs_firewall.iptables_helper.iptables_driver = ( self.iptables_firewall) ovs_firewall.prepare_port_filter(desc) self._check_no_iptables_rules_for_port(port) neutron-12.1.1/neutron/tests/functional/agent/linux/openvswitch_firewall/__init__.py0000664000175000017500000000000013553660046031110 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/linux/openvswitch_firewall/test_firewall.py0000664000175000017500000000444413553660046032235 0ustar zuulzuul00000000000000# Copyright 2016, Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import testtools from neutron.agent.linux.openvswitch_firewall import exceptions from neutron.agent.linux.openvswitch_firewall import firewall from neutron.tests.common import net_helpers from neutron.tests.functional import base class TestGetTagFromOtherConfig(base.BaseSudoTestCase): def setUp(self): super(TestGetTagFromOtherConfig, self).setUp() self.bridge = self.useFixture(net_helpers.OVSBridgeFixture()).bridge def set_port_tag(self, port_name, tag): self.bridge.set_db_attribute( 'Port', port_name, 'other_config', {'tag': str(tag)}) def test_correct_tag_is_returned(self): port_number = 42 port = self.useFixture(net_helpers.OVSPortFixture(self.bridge)).port self.set_port_tag(port.name, port_number) observed = firewall.get_tag_from_other_config(self.bridge, port.name) self.assertEqual(port_number, observed) def test_not_existing_name_raises_exception(self): with testtools.ExpectedException(exceptions.OVSFWTagNotFound): firewall.get_tag_from_other_config(self.bridge, 'foo') def test_bad_tag_value_raises_exception(self): port = self.useFixture(net_helpers.OVSPortFixture(self.bridge)).port self.set_port_tag(port.name, 'foo') with testtools.ExpectedException(exceptions.OVSFWTagNotFound): firewall.get_tag_from_other_config(self.bridge, port.name) def test_no_value_set_for_other_config_raises_exception(self): port = self.useFixture(net_helpers.OVSPortFixture(self.bridge)).port with testtools.ExpectedException(exceptions.OVSFWTagNotFound): firewall.get_tag_from_other_config(self.bridge, port.name) neutron-12.1.1/neutron/tests/functional/agent/linux/test_linuxbridge_arp_protect.py0000664000175000017500000001566513553660046031117 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron_lib.utils import net from neutron.plugins.ml2.drivers.linuxbridge.agent import arp_protect from neutron.tests.common import machine_fixtures from neutron.tests.common import net_helpers from neutron.tests.functional import base as functional_base no_arping = net_helpers.assert_no_arping arping = net_helpers.assert_arping class LinuxBridgeARPSpoofTestCase(functional_base.BaseSudoTestCase): def setUp(self): super(LinuxBridgeARPSpoofTestCase, self).setUp() lbfixture = self.useFixture(net_helpers.LinuxBridgeFixture()) self.addCleanup(setattr, arp_protect, 'NAMESPACE', None) arp_protect.NAMESPACE = lbfixture.namespace bridge = lbfixture.bridge self.source, self.destination, self.observer = self.useFixture( machine_fixtures.PeerMachines(bridge, amount=3)).machines self.addCleanup(self._ensure_rules_cleaned) def _ensure_rules_cleaned(self): rules = [r for r in arp_protect.ebtables(['-L']).splitlines() if r and 'Bridge' not in r] self.assertEqual([], rules, 'Test leaked ebtables rules') def _add_arp_protection(self, machine, addresses, extra_port_dict=None): port_dict = {'fixed_ips': [{'ip_address': a} for a in addresses], 'device_owner': 'nobody', 'mac_address': machine.port.link.address} if extra_port_dict: port_dict.update(extra_port_dict) name = net_helpers.VethFixture.get_peer_name(machine.port.name) arp_protect.setup_arp_spoofing_protection(name, port_dict) self.addCleanup(arp_protect.delete_arp_spoofing_protection, [name]) def test_arp_no_protection(self): arping(self.source.namespace, self.destination.ip) arping(self.destination.namespace, self.source.ip) def test_arp_correct_protection(self): self._add_arp_protection(self.source, [self.source.ip]) self._add_arp_protection(self.destination, [self.destination.ip]) arping(self.source.namespace, self.destination.ip) arping(self.destination.namespace, self.source.ip) def test_arp_correct_protection_allowed_address_pairs(self): smac = self.source.port.link.address port = {'mac_address': '00:11:22:33:44:55', 'allowed_address_pairs': [{'mac_address': smac, 'ip_address': self.source.ip}]} # make sure a large number of allowed address pairs works for i in range(100000): port['allowed_address_pairs'].append( {'mac_address': net.get_random_mac( 'fa:16:3e:00:00:00'.split(':')), 'ip_address': '10.10.10.10'}) self._add_arp_protection(self.source, ['1.2.2.2'], port) self._add_arp_protection(self.destination, [self.destination.ip]) arping(self.source.namespace, self.destination.ip) arping(self.destination.namespace, self.source.ip) def test_arp_fails_incorrect_protection(self): self._add_arp_protection(self.source, ['1.1.1.1']) self._add_arp_protection(self.destination, ['2.2.2.2']) no_arping(self.source.namespace, self.destination.ip) no_arping(self.destination.namespace, self.source.ip) def test_arp_fails_incorrect_mac_protection(self): # a bad mac filter on the source will prevent any traffic from it self._add_arp_protection(self.source, [self.source.ip], {'mac_address': '00:11:22:33:44:55'}) no_arping(self.source.namespace, self.destination.ip) no_arping(self.destination.namespace, self.source.ip) # correcting it should make it work self._add_arp_protection(self.source, [self.source.ip]) arping(self.source.namespace, self.destination.ip) def test_arp_protection_removal(self): self._add_arp_protection(self.source, ['1.1.1.1']) self._add_arp_protection(self.destination, ['2.2.2.2']) no_arping(self.observer.namespace, self.destination.ip) no_arping(self.observer.namespace, self.source.ip) name = net_helpers.VethFixture.get_peer_name(self.source.port.name) arp_protect.delete_arp_spoofing_protection([name]) # spoofing should have been removed from source, but not dest arping(self.observer.namespace, self.source.ip) no_arping(self.observer.namespace, self.destination.ip) def test_arp_protection_update(self): self._add_arp_protection(self.source, ['1.1.1.1']) self._add_arp_protection(self.destination, ['2.2.2.2']) no_arping(self.observer.namespace, self.destination.ip) no_arping(self.observer.namespace, self.source.ip) self._add_arp_protection(self.source, ['192.0.0.0/1']) # spoofing should have been updated on source, but not dest arping(self.observer.namespace, self.source.ip) no_arping(self.observer.namespace, self.destination.ip) def test_arp_protection_port_security_disabled(self): self._add_arp_protection(self.source, ['1.1.1.1']) no_arping(self.observer.namespace, self.source.ip) self._add_arp_protection(self.source, ['1.1.1.1'], {'port_security_enabled': False}) arping(self.observer.namespace, self.source.ip) def test_arp_protection_network_owner(self): self._add_arp_protection(self.source, ['1.1.1.1']) no_arping(self.observer.namespace, self.source.ip) self._add_arp_protection(self.source, ['1.1.1.1'], {'device_owner': constants.DEVICE_OWNER_ROUTER_GW}) arping(self.observer.namespace, self.source.ip) def test_arp_protection_dead_reference_removal(self): self._add_arp_protection(self.source, ['1.1.1.1']) self._add_arp_protection(self.destination, ['2.2.2.2']) no_arping(self.observer.namespace, self.destination.ip) no_arping(self.observer.namespace, self.source.ip) name = net_helpers.VethFixture.get_peer_name(self.source.port.name) # This should remove all arp protect rules that aren't source port arp_protect.delete_unreferenced_arp_protection([name]) no_arping(self.observer.namespace, self.source.ip) arping(self.observer.namespace, self.destination.ip) neutron-12.1.1/neutron/tests/functional/agent/linux/simple_daemon.py0000664000175000017500000000336313553660046025746 0ustar zuulzuul00000000000000# Copyright 2014 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import time from oslo_config import cfg from neutron.agent.linux import daemon def main(): class SimpleDaemon(daemon.Daemon): """The purpose of this daemon is to serve as an example, and also as a dummy daemon, which can be invoked by functional testing, it does nothing but setting the pid file, and staying detached in the background. """ def run(self): while True: time.sleep(10) opts = [ cfg.StrOpt('uuid', help='uuid provided from the command line ' 'so external_process can track us via /proc/' 'cmdline interface.', required=True), cfg.StrOpt('pid_file', help='Location of pid file of this process.', required=True) ] cfg.CONF.register_cli_opts(opts) # Don't get the default configuration file cfg.CONF(project='neutron', default_config_files=[]) simple_daemon = SimpleDaemon(cfg.CONF.pid_file, uuid=cfg.CONF.uuid) simple_daemon.start() if __name__ == "__main__": main() neutron-12.1.1/neutron/tests/functional/agent/linux/bin/0000775000175000017500000000000013553660156023325 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/linux/bin/__init__.py0000664000175000017500000000000013553660046025422 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/linux/bin/ipt_binname.py0000775000175000017500000000232013553660047026163 0ustar zuulzuul00000000000000#! /usr/bin/env python # Copyright (C) 2014 VA Linux Systems Japan K.K. # Copyright (C) 2014 YAMAMOTO Takashi # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from __future__ import print_function import sys import eventlet def print_binary_name(): # NOTE(yamamoto): Don't move this import to module-level. # The aim is to test importing from eventlet non-main thread. # See Bug #1367075 for details. from neutron.agent.linux import iptables_manager print(iptables_manager.binary_name) if __name__ == "__main__": if 'spawn' in sys.argv: eventlet.spawn(print_binary_name).wait() else: print_binary_name() neutron-12.1.1/neutron/tests/functional/agent/linux/test_ip_monitor.py0000664000175000017500000000506413553660047026351 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.linux import async_process from neutron.agent.linux import ip_monitor from neutron.tests.functional.agent.linux import test_ip_lib class TestIPMonitor(test_ip_lib.IpLibTestFramework): def setUp(self): super(TestIPMonitor, self).setUp() attr = self.generate_device_details() self.device = self.manage_device(attr) self.monitor = ip_monitor.IPMonitor(attr.namespace) self.addCleanup(self._safe_stop_monitor) def _safe_stop_monitor(self): try: self.monitor.stop() except async_process.AsyncProcessException: pass def test_ip_monitor_lifecycle(self): self.assertFalse(self.monitor.is_active()) self.monitor.start() self.assertTrue(self.monitor.is_active()) self.monitor.stop() self.assertFalse(self.monitor.is_active()) def test_ip_monitor_events(self): self.monitor.start() cidr = '169.254.128.1/24' self.device.addr.add(cidr) self._assert_event(expected_name=self.device.name, expected_cidr=cidr, expected_added=True, event=ip_monitor.IPMonitorEvent.from_text( next(self.monitor.iter_stdout(block=True)))) self.device.addr.delete(cidr) self._assert_event(expected_name=self.device.name, expected_cidr=cidr, expected_added=False, event=ip_monitor.IPMonitorEvent.from_text( next(self.monitor.iter_stdout(block=True)))) def _assert_event(self, expected_name, expected_cidr, expected_added, event): self.assertEqual(expected_name, event.interface) self.assertEqual(expected_added, event.added) self.assertEqual(expected_cidr, event.cidr) neutron-12.1.1/neutron/tests/functional/agent/linux/test_interface.py0000664000175000017500000001302313553660047026124 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools from neutron_lib.utils import net from oslo_config import cfg from oslo_utils import uuidutils import testtools from neutron.agent.linux import interface from neutron.agent.linux import ip_lib from neutron.common import exceptions from neutron.common import utils from neutron.conf.agent import common as config from neutron.tests.common import net_helpers from neutron.tests.functional.agent.linux import base as linux_base from neutron.tests.functional import base class InterfaceDriverTestCaseMixin(object): def _test_mtu_set_after_action(self, device_name, br_name, namespace, action=None): mac_address = net.get_random_mac('fa:16:3e:00:00:00'.split(':')) plug = functools.partial( self.interface.plug, network_id=uuidutils.generate_uuid(), port_id=uuidutils.generate_uuid(), device_name=device_name, mac_address=mac_address, bridge=self.bridge_name, namespace=namespace) plug(mtu=1500) self.assertTrue(ip_lib.device_exists(device_name, namespace)) action = action or plug for mtu in (1450, 1500, 9000, 9000, 1450): action(mtu=mtu) self.assertEqual( mtu, ip_lib.IPDevice(device_name, namespace=namespace).link.mtu) def test_plug_multiple_calls_update_mtu(self): device_name = utils.get_rand_name() namespace = self.useFixture(net_helpers.NamespaceFixture()).name self._test_mtu_set_after_action( device_name, self.bridge_name, namespace) def test_set_mtu(self): device_name = utils.get_rand_name() namespace = self.useFixture(net_helpers.NamespaceFixture()).name self._test_mtu_set_after_action( device_name, self.bridge_name, namespace, functools.partial( self.interface.set_mtu, device_name=device_name, namespace=namespace)) class OVSInterfaceDriverTestCase(linux_base.BaseOVSLinuxTestCase, InterfaceDriverTestCaseMixin): def setUp(self): super(OVSInterfaceDriverTestCase, self).setUp() conf = cfg.ConfigOpts() config.register_interface_opts(conf) self.interface = interface.OVSInterfaceDriver(conf) self.bridge = self.useFixture(net_helpers.OVSBridgeFixture()).bridge @property def bridge_name(self): return self.bridge.br_name def test_plug_checks_if_bridge_exists(self): with testtools.ExpectedException(exceptions.BridgeDoesNotExist): self.interface.plug(network_id=42, port_id=71, device_name='not_a_device', mac_address='', bridge='not_a_bridge', namespace='not_a_namespace') def test_plug_succeeds(self): device_name = utils.get_rand_name() mac_address = net.get_random_mac('fa:16:3e:00:00:00'.split(':')) namespace = self.useFixture(net_helpers.NamespaceFixture()).name self.assertFalse(self.bridge.get_port_name_list()) self.interface.plug(network_id=uuidutils.generate_uuid(), port_id=uuidutils.generate_uuid(), device_name=device_name, mac_address=mac_address, bridge=self.bridge.br_name, namespace=namespace) self.assertIn(device_name, self.bridge.get_port_name_list()) self.assertTrue(ip_lib.device_exists(device_name, namespace)) def test_plug_with_namespace_sets_mtu_higher_than_bridge(self): # First, add a new linuxbridge port with reduced MTU to OVS bridge lb_bridge = self.useFixture( net_helpers.LinuxBridgeFixture()).bridge lb_bridge_port = self.useFixture( net_helpers.LinuxBridgePortFixture(lb_bridge)) lb_bridge_port.port.link.set_mtu(1400) self.bridge.add_port(lb_bridge_port.port.name) device_name = utils.get_rand_name() namespace = self.useFixture(net_helpers.NamespaceFixture()).name # Now plug a device with intended MTU that is higher than for the port # above and validate that its MTU is not reduced to the least MTU on # the bridge self._test_mtu_set_after_action( device_name, self.bridge_name, namespace) class BridgeInterfaceDriverTestCase(base.BaseSudoTestCase, InterfaceDriverTestCaseMixin): def setUp(self): super(BridgeInterfaceDriverTestCase, self).setUp() conf = cfg.ConfigOpts() config.register_interface_opts(conf) self.interface = interface.BridgeInterfaceDriver(conf) self.bridge = self.useFixture(net_helpers.LinuxBridgeFixture()).bridge @property def bridge_name(self): return self.bridge.name neutron-12.1.1/neutron/tests/functional/agent/linux/test_bridge_lib.py0000664000175000017500000000647113553660047026257 0ustar zuulzuul00000000000000# Copyright (c) 2015 Thales Services SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_utils import uuidutils from neutron.agent.linux import bridge_lib from neutron.tests.common import net_helpers from neutron.tests.functional import base class BridgeLibTestCase(base.BaseSudoTestCase): def setUp(self): super(BridgeLibTestCase, self).setUp() self.bridge, self.port_fixture = self.create_bridge_port_fixture() def create_bridge_port_fixture(self): bridge = self.useFixture( net_helpers.LinuxBridgeFixture(namespace=None)).bridge port_fixture = self.useFixture( net_helpers.LinuxBridgePortFixture( bridge, port_id=uuidutils.generate_uuid())) return bridge, port_fixture def test_is_bridged_interface(self): self.assertTrue( bridge_lib.is_bridged_interface(self.port_fixture.br_port.name)) def test_is_not_bridged_interface(self): self.assertFalse( bridge_lib.is_bridged_interface(self.port_fixture.port.name)) def test_get_bridge_names(self): self.assertIn(self.bridge.name, bridge_lib.get_bridge_names()) def test_get_interface_ifindex(self): port = self.port_fixture.br_port t1 = bridge_lib.get_interface_ifindex(str(port)) self.port_fixture.veth_fixture.destroy() self.port_fixture.veth_fixture._setUp() t2 = bridge_lib.get_interface_ifindex(str(port)) self.assertIsNotNone(t1) self.assertIsNotNone(t2) self.assertGreaterEqual(t2, t1) def test_get_interface_bridge(self): bridge = bridge_lib.BridgeDevice.get_interface_bridge( self.port_fixture.br_port.name) self.assertEqual(self.bridge.name, bridge.name) def test_get_interface_no_bridge(self): bridge = bridge_lib.BridgeDevice.get_interface_bridge( self.port_fixture.port.name) self.assertIsNone(bridge) def test_get_interfaces(self): self.assertEqual( [self.port_fixture.br_port.name], self.bridge.get_interfaces()) def test_get_interfaces_no_bridge(self): bridge = bridge_lib.BridgeDevice('--fake--') self.assertEqual([], bridge.get_interfaces()) def test_disable_ipv6(self): sysfs_path = ("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % self.bridge.name) # first, make sure it's enabled with open(sysfs_path, 'r') as sysfs_disable_ipv6_file: sysfs_disable_ipv6 = sysfs_disable_ipv6_file.read() self.assertEqual("0\n", sysfs_disable_ipv6) self.assertEqual(0, self.bridge.disable_ipv6()) with open(sysfs_path, 'r') as sysfs_disable_ipv6_file: sysfs_disable_ipv6 = sysfs_disable_ipv6_file.read() self.assertEqual("1\n", sysfs_disable_ipv6) neutron-12.1.1/neutron/tests/functional/agent/linux/test_iptables.py0000664000175000017500000002004513553660046025770 0ustar zuulzuul00000000000000# Copyright (c) 2014 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os.path from neutron_lib import constants import testtools from neutron.agent.linux import iptables_manager from neutron.agent.linux import utils from neutron.tests import base from neutron.tests.common import machine_fixtures from neutron.tests.common import net_helpers from neutron.tests.functional.agent.linux import base as linux_base from neutron.tests.functional.agent.linux.bin import ipt_binname from neutron.tests.functional import base as functional_base class IptablesManagerTestCase(functional_base.BaseSudoTestCase): DIRECTION_CHAIN_MAPPER = {'ingress': 'INPUT', 'egress': 'OUTPUT'} PROTOCOL_BLOCK_RULE = '-p %s -j DROP' PROTOCOL_PORT_BLOCK_RULE = ('-p %(protocol)s -m %(protocol)s ' '--dport %(port)d -j DROP') def setUp(self): super(IptablesManagerTestCase, self).setUp() bridge = self.useFixture(net_helpers.VethBridgeFixture()).bridge self.client, self.server = self.useFixture( machine_fixtures.PeerMachines(bridge)).machines self.client_fw, self.server_fw = self.create_firewalls() # The port is used in isolated namespace that precludes possibility of # port conflicts self.port = net_helpers.get_free_namespace_port( constants.PROTO_NAME_TCP, self.server.namespace) def create_firewalls(self): client_iptables = iptables_manager.IptablesManager( namespace=self.client.namespace) server_iptables = iptables_manager.IptablesManager( namespace=self.server.namespace) return client_iptables, server_iptables def filter_add_rule(self, fw_manager, address, direction, protocol, port): self._ipv4_filter_execute(fw_manager, 'add_rule', direction, protocol, port) def filter_remove_rule(self, fw_manager, address, direction, protocol, port): self._ipv4_filter_execute(fw_manager, 'remove_rule', direction, protocol, port) def _ipv4_filter_execute(self, fw_manager, method, direction, protocol, port): chain, rule = self._get_chain_and_rule(direction, protocol, port) method = getattr(fw_manager.ipv4['filter'], method) method(chain, rule) fw_manager.apply() def _get_chain_and_rule(self, direction, protocol, port): chain = self.DIRECTION_CHAIN_MAPPER[direction] if port: rule = self.PROTOCOL_PORT_BLOCK_RULE % {'protocol': protocol, 'port': port} else: rule = self.PROTOCOL_BLOCK_RULE % protocol return chain, rule def _test_with_nc(self, fw_manager, direction, port, protocol): netcat = net_helpers.NetcatTester( self.client.namespace, self.server.namespace, self.server.ip, self.port, protocol) self.addCleanup(netcat.stop_processes) filter_params = 'direction %s, port %s and protocol %s' % ( direction, port, protocol) self.assertTrue(netcat.test_connectivity(), 'Failed connectivity check before applying a filter ' 'with %s' % filter_params) # REVISIT(jlibosva): Make sure we have ASSURED conntrack entry for # given connection self.filter_add_rule( fw_manager, self.server.ip, direction, protocol, port) with testtools.ExpectedException( RuntimeError, msg='Wrongfully passed a connectivity check after applying ' 'a filter with %s' % filter_params): netcat.test_connectivity() self.filter_remove_rule( fw_manager, self.server.ip, direction, protocol, port) # With TCP packets will get through after firewall was removed, so we # would get old data on socket and with UDP process died, so we need to # respawn processes to have clean sockets self.assertTrue(netcat.test_connectivity(True), 'Failed connectivity check after removing a filter ' 'with %s' % filter_params) def test_icmp(self): self.client.assert_ping(self.server.ip) self.server_fw.ipv4['filter'].add_rule('INPUT', linux_base.ICMP_BLOCK_RULE) self.server_fw.apply() self.client.assert_no_ping(self.server.ip) self.server_fw.ipv4['filter'].remove_rule('INPUT', linux_base.ICMP_BLOCK_RULE) self.server_fw.apply() self.client.assert_ping(self.server.ip) def test_mangle_icmp(self): self.client.assert_ping(self.server.ip) self.server_fw.ipv4['mangle'].add_rule('INPUT', linux_base.ICMP_MARK_RULE) self.server_fw.ipv4['filter'].add_rule('INPUT', linux_base.MARKED_BLOCK_RULE) self.server_fw.apply() self.client.assert_no_ping(self.server.ip) self.server_fw.ipv4['mangle'].remove_rule('INPUT', linux_base.ICMP_MARK_RULE) self.server_fw.ipv4['filter'].remove_rule('INPUT', linux_base.MARKED_BLOCK_RULE) self.server_fw.apply() self.client.assert_ping(self.server.ip) def test_tcp_input_port(self): self._test_with_nc(self.server_fw, 'ingress', self.port, protocol=net_helpers.NetcatTester.TCP) def test_tcp_output_port(self): self._test_with_nc(self.client_fw, 'egress', self.port, protocol=net_helpers.NetcatTester.TCP) def test_tcp_input(self): self._test_with_nc(self.server_fw, 'ingress', port=None, protocol=net_helpers.NetcatTester.TCP) def test_tcp_output(self): self._test_with_nc(self.client_fw, 'egress', port=None, protocol=net_helpers.NetcatTester.TCP) def test_udp_input_port(self): self._test_with_nc(self.server_fw, 'ingress', self.port, protocol=net_helpers.NetcatTester.UDP) def test_udp_output_port(self): self._test_with_nc(self.client_fw, 'egress', self.port, protocol=net_helpers.NetcatTester.UDP) def test_udp_input(self): self._test_with_nc(self.server_fw, 'ingress', port=None, protocol=net_helpers.NetcatTester.UDP) def test_udp_output(self): self._test_with_nc(self.client_fw, 'egress', port=None, protocol=net_helpers.NetcatTester.UDP) class IptablesManagerNonRootTestCase(base.BaseTestCase): @staticmethod def _normalize_module_name(name): for suf in ['.pyc', '.pyo']: if name.endswith(suf): return name[:-len(suf)] + '.py' return name def _test_binary_name(self, module, *extra_options): executable = self._normalize_module_name(module.__file__) expected = os.path.basename(executable)[:16] observed = utils.execute([executable] + list(extra_options)).rstrip() self.assertEqual(expected, observed) def test_binary_name(self): self._test_binary_name(ipt_binname) def test_binary_name_eventlet_spawn(self): self._test_binary_name(ipt_binname, 'spawn') neutron-12.1.1/neutron/tests/functional/agent/linux/base.py0000664000175000017500000000343213553660047024042 0ustar zuulzuul00000000000000# Copyright 2014 Cisco Systems, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import testscenarios from neutron.tests.common.exclusive_resources import ip_address from neutron.tests.functional import base MARK_VALUE = '0x1' MARK_MASK = '0xffffffff' ICMP_MARK_RULE = ('-j MARK --set-xmark %(value)s/%(mask)s' % {'value': MARK_VALUE, 'mask': MARK_MASK}) MARKED_BLOCK_RULE = '-m mark --mark %s -j DROP' % MARK_VALUE ICMP_BLOCK_RULE = '-p icmp -j DROP' # Regarding MRO, it goes BaseOVSLinuxTestCase, WithScenarios, # BaseSudoTestCase, ..., UnitTest, object. setUp is not defined in # WithScenarios, so it will correctly be found in BaseSudoTestCase. class BaseOVSLinuxTestCase(testscenarios.WithScenarios, base.BaseSudoTestCase): scenarios = [ ('vsctl', dict(ovsdb_interface='vsctl')), ('native', dict(ovsdb_interface='native')), ] def setUp(self): super(BaseOVSLinuxTestCase, self).setUp() self.config(group='OVS', ovsdb_interface=self.ovsdb_interface) def get_test_net_address(self, block): """Return exclusive address based on RFC 5737. :param block: One of constants 1, 2 or 3 """ return str(self.useFixture( ip_address.get_test_net_address_fixture(block)).address) neutron-12.1.1/neutron/tests/functional/agent/linux/test_ip_lib.py0000664000175000017500000003302313553660047025424 0ustar zuulzuul00000000000000# Copyright (c) 2014 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import netaddr from neutron_lib import constants from neutron_lib.utils import net from oslo_config import cfg from oslo_log import log as logging from oslo_utils import importutils from oslo_utils import uuidutils import testtools from neutron.agent.linux import ip_lib from neutron.common import utils from neutron.conf.agent import common as config from neutron.tests.common import net_helpers from neutron.tests.functional import base as functional_base LOG = logging.getLogger(__name__) Device = collections.namedtuple('Device', 'name ip_cidrs mac_address namespace') WRONG_IP = '0.0.0.0' TEST_IP = '240.0.0.1' TEST_IP_NEIGH = '240.0.0.2' TEST_IP_SECONDARY = '240.0.0.3' class IpLibTestFramework(functional_base.BaseSudoTestCase): def setUp(self): super(IpLibTestFramework, self).setUp() self._configure() def _configure(self): config.register_interface_driver_opts_helper(cfg.CONF) cfg.CONF.set_override( 'interface_driver', 'neutron.agent.linux.interface.OVSInterfaceDriver') config.register_interface_opts() self.driver = importutils.import_object(cfg.CONF.interface_driver, cfg.CONF) def generate_device_details(self, name=None, ip_cidrs=None, mac_address=None, namespace=None): return Device(name or utils.get_rand_name(), ip_cidrs or ["%s/24" % TEST_IP], mac_address or net.get_random_mac('fa:16:3e:00:00:00'.split(':')), namespace or utils.get_rand_name()) def _safe_delete_device(self, device): try: device.link.delete() except RuntimeError: LOG.debug('Could not delete %s, was it already deleted?', device) def manage_device(self, attr): """Create a tuntap with the specified attributes. The device is cleaned up at the end of the test. :param attr: A Device namedtuple :return: A tuntap ip_lib.IPDevice """ ip = ip_lib.IPWrapper(namespace=attr.namespace) if attr.namespace: ip.netns.add(attr.namespace) self.addCleanup(ip.netns.delete, attr.namespace) tap_device = ip.add_tuntap(attr.name) self.addCleanup(self._safe_delete_device, tap_device) tap_device.link.set_address(attr.mac_address) self.driver.init_l3(attr.name, attr.ip_cidrs, namespace=attr.namespace) tap_device.link.set_up() return tap_device class IpLibTestCase(IpLibTestFramework): def test_device_exists(self): attr = self.generate_device_details() self.assertFalse( ip_lib.device_exists(attr.name, namespace=attr.namespace)) device = self.manage_device(attr) self.assertTrue( ip_lib.device_exists(device.name, namespace=attr.namespace)) self.assertFalse( ip_lib.device_exists(attr.name, namespace='wrong_namespace')) device.link.delete() self.assertFalse( ip_lib.device_exists(attr.name, namespace=attr.namespace)) def test_ipdevice_exists(self): attr = self.generate_device_details() device = self.manage_device(attr) self.assertTrue(device.exists()) device.link.delete() self.assertFalse(device.exists()) def test_vxlan_exists(self): attr = self.generate_device_details() ip = ip_lib.IPWrapper(namespace=attr.namespace) ip.netns.add(attr.namespace) self.addCleanup(ip.netns.delete, attr.namespace) self.assertFalse(ip_lib.vxlan_in_use(9999, namespace=attr.namespace)) device = ip.add_vxlan(attr.name, 9999) self.addCleanup(self._safe_delete_device, device) self.assertTrue(ip_lib.vxlan_in_use(9999, namespace=attr.namespace)) device.link.delete() self.assertFalse(ip_lib.vxlan_in_use(9999, namespace=attr.namespace)) def test_ipwrapper_get_device_by_ip_None(self): ip_wrapper = ip_lib.IPWrapper(namespace=None) self.assertIsNone(ip_wrapper.get_device_by_ip(ip=None)) def test_ipwrapper_get_device_by_ip(self): # We need to pass both IP and cidr values to get_device_by_ip() # to make sure it filters correctly. test_ip = "%s/24" % TEST_IP test_ip_secondary = "%s/24" % TEST_IP_SECONDARY attr = self.generate_device_details( ip_cidrs=[test_ip, test_ip_secondary] ) self.manage_device(attr) ip_wrapper = ip_lib.IPWrapper(namespace=attr.namespace) self.assertEqual(attr.name, ip_wrapper.get_device_by_ip(TEST_IP).name) self.assertEqual(attr.name, ip_wrapper.get_device_by_ip(TEST_IP_SECONDARY).name) self.assertIsNone(ip_wrapper.get_device_by_ip(TEST_IP_NEIGH)) # this is in the same subnet, so will match if we pass as cidr test_ip_neigh = "%s/24" % TEST_IP_NEIGH self.assertEqual(attr.name, ip_wrapper.get_device_by_ip(test_ip_neigh).name) self.assertIsNone(ip_wrapper.get_device_by_ip(WRONG_IP)) def test_device_exists_with_ips_and_mac(self): attr = self.generate_device_details() device = self.manage_device(attr) self.assertTrue( ip_lib.device_exists_with_ips_and_mac(*attr)) wrong_ip_cidr = '10.0.0.1/8' wrong_mac_address = 'aa:aa:aa:aa:aa:aa' attr = self.generate_device_details(name='wrong_name') self.assertFalse( ip_lib.device_exists_with_ips_and_mac(*attr)) attr = self.generate_device_details(ip_cidrs=[wrong_ip_cidr]) self.assertFalse(ip_lib.device_exists_with_ips_and_mac(*attr)) attr = self.generate_device_details(mac_address=wrong_mac_address) self.assertFalse(ip_lib.device_exists_with_ips_and_mac(*attr)) attr = self.generate_device_details(namespace='wrong_namespace') self.assertFalse(ip_lib.device_exists_with_ips_and_mac(*attr)) device.link.delete() def test_get_device_mac(self): attr = self.generate_device_details() device = self.manage_device(attr) mac_address = ip_lib.get_device_mac(attr.name, namespace=attr.namespace) self.assertEqual(attr.mac_address, mac_address) device.link.delete() def test_get_device_mac_too_long_name(self): name = utils.get_rand_name( max_length=constants.DEVICE_NAME_MAX_LEN + 5) attr = self.generate_device_details(name=name) device = self.manage_device(attr) mac_address = ip_lib.get_device_mac(attr.name, namespace=attr.namespace) self.assertEqual(attr.mac_address, mac_address) device.link.delete() def test_get_routing_table(self): attr = self.generate_device_details( ip_cidrs=["%s/24" % TEST_IP, "fd00::1/64"] ) device = self.manage_device(attr) device_ip = attr.ip_cidrs[0].split('/')[0] destination = '8.8.8.0/24' device.route.add_route(destination, device_ip) destination6 = 'fd01::/64' device.route.add_route(destination6, "fd00::2") expected_routes = [{'nexthop': device_ip, 'device': attr.name, 'destination': destination, 'scope': 'universe'}, {'nexthop': None, 'device': attr.name, 'destination': str( netaddr.IPNetwork(attr.ip_cidrs[0]).cidr), 'scope': 'link'}] routes = ip_lib.get_routing_table(4, namespace=attr.namespace) self.assertItemsEqual(expected_routes, routes) self.assertIsInstance(routes, list) expected_routes6 = [{'nexthop': "fd00::2", 'device': attr.name, 'destination': destination6, 'scope': 'universe'}, {'nexthop': None, 'device': attr.name, 'destination': str( netaddr.IPNetwork(attr.ip_cidrs[1]).cidr), 'scope': 'universe'}] routes6 = ip_lib.get_routing_table(6, namespace=attr.namespace) self.assertItemsEqual(expected_routes6, routes6) self.assertIsInstance(routes6, list) def test_get_routing_table_no_namespace(self): with testtools.ExpectedException(ip_lib.NetworkNamespaceNotFound): ip_lib.get_routing_table(4, namespace="nonexistent-netns") def test_get_neigh_entries(self): attr = self.generate_device_details( ip_cidrs=["%s/24" % TEST_IP, "fd00::1/64"] ) mac_address = net.get_random_mac('fa:16:3e:00:00:00'.split(':')) device = self.manage_device(attr) device.neigh.add(TEST_IP_NEIGH, mac_address) expected_neighs = [{'dst': TEST_IP_NEIGH, 'lladdr': mac_address, 'device': attr.name}] neighs = device.neigh.dump(4) self.assertItemsEqual(expected_neighs, neighs) self.assertIsInstance(neighs, list) device.neigh.delete(TEST_IP_NEIGH, mac_address) neighs = device.neigh.dump(4, dst=TEST_IP_NEIGH, lladdr=mac_address) self.assertEqual([], neighs) def test_get_neigh_entries_no_namespace(self): with testtools.ExpectedException(ip_lib.NetworkNamespaceNotFound): ip_lib.dump_neigh_entries(4, namespace="nonexistent-netns") def test_get_neigh_entries_no_interface(self): attr = self.generate_device_details( ip_cidrs=["%s/24" % TEST_IP, "fd00::1/64"] ) self.manage_device(attr) with testtools.ExpectedException(ip_lib.NetworkInterfaceNotFound): ip_lib.dump_neigh_entries(4, device="nosuchdevice", namespace=attr.namespace) def test_delete_neigh_entries(self): attr = self.generate_device_details( ip_cidrs=["%s/24" % TEST_IP, "fd00::1/64"] ) mac_address = net.get_random_mac('fa:16:3e:00:00:00'.split(':')) device = self.manage_device(attr) # trying to delete a non-existent entry shouldn't raise an error device.neigh.delete(TEST_IP_NEIGH, mac_address) def _check_for_device_name(self, ip, name, should_exist): exist = any(d for d in ip.get_devices() if d.name == name) self.assertEqual(should_exist, exist) def test_dummy_exists(self): namespace = self.useFixture(net_helpers.NamespaceFixture()) dev_name = utils.get_rand_name() device = namespace.ip_wrapper.add_dummy(dev_name) self.addCleanup(self._safe_delete_device, device) self._check_for_device_name(namespace.ip_wrapper, dev_name, True) device.link.delete() self._check_for_device_name(namespace.ip_wrapper, dev_name, False) class TestSetIpNonlocalBind(functional_base.BaseSudoTestCase): def test_assigned_value(self): namespace = self.useFixture(net_helpers.NamespaceFixture()) for expected in (0, 1): failed = ip_lib.set_ip_nonlocal_bind(expected, namespace.name) try: observed = ip_lib.get_ip_nonlocal_bind(namespace.name) except RuntimeError as rte: stat_message = ( 'cannot stat /proc/sys/net/ipv4/ip_nonlocal_bind') if stat_message in str(rte): raise self.skipException( "This kernel doesn't support %s in network " "namespaces." % ip_lib.IP_NONLOCAL_BIND) raise self.assertFalse(failed) self.assertEqual(expected, observed) class NamespaceTestCase(functional_base.BaseSudoTestCase): def setUp(self): super(NamespaceTestCase, self).setUp() self.namespace = 'test_ns_' + uuidutils.generate_uuid() ip_lib.create_network_namespace(self.namespace) self.addCleanup(self._delete_namespace) def _delete_namespace(self): ip_lib.delete_network_namespace(self.namespace) def test_network_namespace_exists_ns_exists(self): self.assertTrue(ip_lib.network_namespace_exists(self.namespace)) def test_network_namespace_exists_ns_doesnt_exists(self): self.assertFalse(ip_lib.network_namespace_exists('another_ns')) def test_network_namespace_exists_ns_exists_try_is_ready(self): self.assertTrue(ip_lib.network_namespace_exists(self.namespace, try_is_ready=True)) def test_network_namespace_exists_ns_doesnt_exists_try_is_ready(self): self.assertFalse(ip_lib.network_namespace_exists('another_ns', try_is_ready=True)) neutron-12.1.1/neutron/tests/functional/agent/linux/test_utils.py0000664000175000017500000000704013553660047025326 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools from neutron.agent.linux import async_process from neutron.agent.linux import utils from neutron.common import utils as common_utils from neutron.tests.functional.agent.linux import test_async_process from neutron.tests.functional import base as functional_base class TestPIDHelpers(test_async_process.AsyncProcessTestFramework): def test_get_cmdline_from_pid_and_pid_invoked_with_cmdline(self): cmd = ['tail', '-f', self.test_file_path] proc = async_process.AsyncProcess(cmd) proc.start(block=True) self.addCleanup(proc.stop) pid = proc.pid self.assertEqual(cmd, utils.get_cmdline_from_pid(pid)) self.assertTrue(utils.pid_invoked_with_cmdline(pid, cmd)) self.assertEqual([], utils.get_cmdline_from_pid(-1)) class TestGetRootHelperChildPid(functional_base.BaseSudoTestCase): def _addcleanup_sleep_process(self, parent_pid): sleep_pid = utils.execute( ['ps', '--ppid', parent_pid, '-o', 'pid=']).strip() self.addCleanup( utils.execute, ['kill', '-9', sleep_pid], check_exit_code=False, run_as_root=True) def test_get_root_helper_child_pid_returns_first_child(self): """Test that the first child, not lowest child pid is returned. Test creates following process tree: sudo + | +--rootwrap + | +--bash+ | +--sleep 100 and tests that pid of `bash' command is returned. """ def wait_for_sleep_is_spawned(parent_pid): proc_tree = utils.execute( ['pstree', parent_pid], check_exit_code=False) processes = [command.strip() for command in proc_tree.split('---') if command] if processes: return 'sleep' == processes[-1] cmd = ['bash', '-c', '(sleep 100)'] proc = async_process.AsyncProcess(cmd, run_as_root=True) proc.start() # root helpers spawn their child processes asynchronously, and we # don't want to use proc.start(block=True) as that uses # get_root_helper_child_pid (The method under test) internally. sudo_pid = proc._process.pid common_utils.wait_until_true( functools.partial( wait_for_sleep_is_spawned, sudo_pid), sleep=0.1) child_pid = utils.get_root_helper_child_pid( sudo_pid, cmd, run_as_root=True) self.assertIsNotNone( child_pid, "get_root_helper_child_pid is expected to return the pid of the " "bash process") self._addcleanup_sleep_process(child_pid) with open('/proc/%s/cmdline' % child_pid, 'r') as f_proc_cmdline: cmdline = f_proc_cmdline.readline().split('\0')[0] self.assertIn('bash', cmdline) neutron-12.1.1/neutron/tests/functional/agent/linux/test_process_monitor.py0000664000175000017500000000735613553660047027425 0ustar zuulzuul00000000000000# Copyright 2014 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from oslo_config import cfg from six import moves from neutron.agent.linux import external_process from neutron.common import utils from neutron.tests import base from neutron.tests.functional.agent.linux import simple_daemon UUID_FORMAT = "test-uuid-%d" SERVICE_NAME = "service" class BaseTestProcessMonitor(base.BaseTestCase): def setUp(self): super(BaseTestProcessMonitor, self).setUp() cfg.CONF.set_override('check_child_processes_interval', 1, 'AGENT') self._child_processes = [] self._process_monitor = None self.create_child_processes_manager('respawn') self.addCleanup(self.cleanup_spawned_children) def create_child_processes_manager(self, action): cfg.CONF.set_override('check_child_processes_action', action, 'AGENT') self._process_monitor = self.build_process_monitor() def build_process_monitor(self): return external_process.ProcessMonitor( config=cfg.CONF, resource_type='test') def _make_cmdline_callback(self, uuid): def _cmdline_callback(pidfile): cmdline = ["python", simple_daemon.__file__, "--uuid=%s" % uuid, "--pid_file=%s" % pidfile] return cmdline return _cmdline_callback def spawn_n_children(self, n, service=None): self._child_processes = [] for child_number in moves.range(n): uuid = self._child_uuid(child_number) _callback = self._make_cmdline_callback(uuid) pm = external_process.ProcessManager( conf=cfg.CONF, uuid=uuid, default_cmd_callback=_callback, service=service) pm.enable() self._process_monitor.register(uuid, SERVICE_NAME, pm) self._child_processes.append(pm) @staticmethod def _child_uuid(child_number): return UUID_FORMAT % child_number def _kill_last_child(self): self._child_processes[-1].disable() def wait_for_all_children_spawned(self): def all_children_active(): return all(pm.active for pm in self._child_processes) for pm in self._child_processes: directory = os.path.dirname(pm.get_pid_file_name()) self.assertEqual(0o755, os.stat(directory).st_mode & 0o777) # we need to allow extra_time for the check process to happen # and properly execute action over the gone processes under # high load conditions max_wait_time = ( cfg.CONF.AGENT.check_child_processes_interval + 5) utils.wait_until_true( all_children_active, timeout=max_wait_time, sleep=0.01, exception=RuntimeError('Not all children (re)spawned.')) def cleanup_spawned_children(self): self._process_monitor.stop() for pm in self._child_processes: pm.disable() class TestProcessMonitor(BaseTestProcessMonitor): def test_respawn_handler(self): self.spawn_n_children(2) self.wait_for_all_children_spawned() self._kill_last_child() self.wait_for_all_children_spawned() neutron-12.1.1/neutron/tests/functional/agent/linux/test_async_process.py0000664000175000017500000000555113553660047027046 0ustar zuulzuul00000000000000# Copyright 2013 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import eventlet import six from neutron._i18n import _ from neutron.agent.linux import async_process from neutron.agent.linux import utils from neutron.common import utils as common_utils from neutron.tests import base class AsyncProcessTestFramework(base.BaseTestCase): def setUp(self): super(AsyncProcessTestFramework, self).setUp() self.test_file_path = self.get_temp_file_path('test_async_process.tmp') self.data = [six.text_type(x) for x in range(4)] with open(self.test_file_path, 'w') as f: f.writelines('%s\n' % item for item in self.data) def _check_stdout(self, proc): # Ensure that all the output from the file is read output = [] while output != self.data: new_output = list(proc.iter_stdout()) if new_output: output += new_output eventlet.sleep(0.01) class TestAsyncProcess(AsyncProcessTestFramework): def _safe_stop(self, proc): try: proc.stop() except async_process.AsyncProcessException: pass def test_stopping_async_process_lifecycle(self): proc = async_process.AsyncProcess(['tail', '-f', self.test_file_path]) self.addCleanup(self._safe_stop, proc) proc.start(block=True) self._check_stdout(proc) proc.stop(block=True) # Ensure that the process and greenthreads have stopped proc._process.wait() self.assertEqual(proc._process.returncode, -9) for watcher in proc._watchers: watcher.wait() def test_async_process_respawns(self): proc = async_process.AsyncProcess(['tail', '-f', self.test_file_path], respawn_interval=0) self.addCleanup(self._safe_stop, proc) proc.start() # Ensure that the same output is read twice self._check_stdout(proc) pid = proc.pid utils.execute(['kill', '-9', pid]) common_utils.wait_until_true( lambda: proc.is_active() and pid != proc.pid, timeout=5, sleep=0.01, exception=RuntimeError(_("Async process didn't respawn"))) self._check_stdout(proc) neutron-12.1.1/neutron/tests/functional/agent/linux/test_tc_lib.py0000664000175000017500000000543613553660047025431 0ustar zuulzuul00000000000000# Copyright (c) 2016 OVH SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.linux import ip_lib from neutron.agent.linux import tc_lib from neutron.tests.functional import base as functional_base TEST_HZ_VALUE = 250 LATENCY = 50 BW_LIMIT = 1024 BURST = 512 BASE_DEV_NAME = "test_tap" class TcLibTestCase(functional_base.BaseSudoTestCase): def create_device(self, name): """Create a tuntap with the specified name. The device is cleaned up at the end of the test. """ ip = ip_lib.IPWrapper() tap_device = ip.add_tuntap(name) self.addCleanup(tap_device.link.delete) tap_device.link.set_up() def test_filters_bandwidth_limit(self): device_name = "%s_filters" % BASE_DEV_NAME self.create_device(device_name) tc = tc_lib.TcCommand(device_name, TEST_HZ_VALUE) tc.set_filters_bw_limit(BW_LIMIT, BURST) bw_limit, burst = tc.get_filters_bw_limits() self.assertEqual(BW_LIMIT, bw_limit) self.assertEqual(BURST, burst) new_bw_limit = BW_LIMIT + 500 new_burst = BURST + 50 tc.update_filters_bw_limit(new_bw_limit, new_burst) bw_limit, burst = tc.get_filters_bw_limits() self.assertEqual(new_bw_limit, bw_limit) self.assertEqual(new_burst, burst) tc.delete_filters_bw_limit() bw_limit, burst = tc.get_filters_bw_limits() self.assertIsNone(bw_limit) self.assertIsNone(burst) def test_tbf_bandwidth_limit(self): device_name = "%s_tbf" % BASE_DEV_NAME self.create_device(device_name) tc = tc_lib.TcCommand(device_name, TEST_HZ_VALUE) tc.set_tbf_bw_limit(BW_LIMIT, BURST, LATENCY) bw_limit, burst = tc.get_tbf_bw_limits() self.assertEqual(BW_LIMIT, bw_limit) self.assertEqual(BURST, burst) new_bw_limit = BW_LIMIT + 500 new_burst = BURST + 50 tc.update_tbf_bw_limit(new_bw_limit, new_burst, LATENCY) bw_limit, burst = tc.get_tbf_bw_limits() self.assertEqual(new_bw_limit, bw_limit) self.assertEqual(new_burst, burst) tc.delete_tbf_bw_limit() bw_limit, burst = tc.get_tbf_bw_limits() self.assertIsNone(bw_limit) self.assertIsNone(burst) neutron-12.1.1/neutron/tests/functional/agent/linux/helpers.py0000664000175000017500000000552013553660047024572 0ustar zuulzuul00000000000000# Copyright (c) 2014 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import multiprocessing import os import time import fixtures from neutron.agent.linux import utils from neutron.tests import tools class RecursivePermDirFixture(fixtures.Fixture): """Ensure at least perms permissions on directory and ancestors.""" def __init__(self, directory, perms): super(RecursivePermDirFixture, self).__init__() self.directory = directory self.least_perms = perms def _setUp(self): previous_directory = None current_directory = self.directory while previous_directory != current_directory: perms = os.stat(current_directory).st_mode if perms & self.least_perms != self.least_perms: os.chmod(current_directory, perms | self.least_perms) previous_directory = current_directory current_directory = os.path.dirname(current_directory) class AdminDirFixture(fixtures.Fixture): """Handle directory create/delete with admin permissions required""" def __init__(self, directory): super(AdminDirFixture, self).__init__() self.directory = directory def _setUp(self): # NOTE(cbrandily): Ensure we will not delete a directory existing # before test run during cleanup. if os.path.exists(self.directory): tools.fail('%s already exists' % self.directory) create_cmd = ['mkdir', '-p', self.directory] delete_cmd = ['rm', '-r', self.directory] utils.execute(create_cmd, run_as_root=True) self.addCleanup(utils.execute, delete_cmd, run_as_root=True) class SleepyProcessFixture(fixtures.Fixture): """ Process fixture that performs time.sleep for the given number of seconds. """ def __init__(self, timeout=60): super(SleepyProcessFixture, self).__init__() self.timeout = timeout @staticmethod def yawn(seconds): time.sleep(seconds) def _setUp(self): self.process = multiprocessing.Process(target=self.yawn, args=[self.timeout]) self.process.start() self.addCleanup(self.destroy) def destroy(self): self.process.terminate() @property def pid(self): return self.process.pid neutron-12.1.1/neutron/tests/functional/agent/linux/__init__.py0000664000175000017500000000000013553660046024652 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/linux/test_ipset.py0000664000175000017500000000762613553660046025323 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.linux import ip_lib from neutron.agent.linux import ipset_manager from neutron.agent.linux import iptables_manager from neutron.common import utils from neutron.tests.common import machine_fixtures from neutron.tests.common import net_helpers from neutron.tests.functional.agent.linux import base from neutron.tests.functional import base as functional_base MAX_IPSET_NAME_LENGTH = 28 IPSET_ETHERTYPE = 'IPv4' UNRELATED_IP = '1.1.1.1' class IpsetBase(functional_base.BaseSudoTestCase): def setUp(self): super(IpsetBase, self).setUp() bridge = self.useFixture(net_helpers.VethBridgeFixture()).bridge self.source, self.destination = self.useFixture( machine_fixtures.PeerMachines(bridge)).machines self.ipset_name = utils.get_rand_name(MAX_IPSET_NAME_LENGTH, 'set-') self.icmp_accept_rule = ('-p icmp -m set --match-set %s src -j ACCEPT' % self.ipset_name) self.ipset = self._create_ipset_manager_and_set( ip_lib.IPWrapper(self.destination.namespace), self.ipset_name) self.addCleanup(self.ipset._destroy, self.ipset_name) self.dst_iptables = iptables_manager.IptablesManager( namespace=self.destination.namespace) self._add_iptables_ipset_rules() self.addCleanup(self._remove_iptables_ipset_rules) def _create_ipset_manager_and_set(self, dst_ns, set_name): ipset = ipset_manager.IpsetManager( namespace=dst_ns.namespace) ipset._create_set(set_name, IPSET_ETHERTYPE) return ipset def _remove_iptables_ipset_rules(self): self.dst_iptables.ipv4['filter'].remove_rule( 'INPUT', base.ICMP_BLOCK_RULE) self.dst_iptables.ipv4['filter'].remove_rule( 'INPUT', self.icmp_accept_rule) self.dst_iptables.apply() def _add_iptables_ipset_rules(self): self.dst_iptables.ipv4['filter'].add_rule( 'INPUT', self.icmp_accept_rule) self.dst_iptables.ipv4['filter'].add_rule( 'INPUT', base.ICMP_BLOCK_RULE) self.dst_iptables.apply() class IpsetManagerTestCase(IpsetBase): def test_add_member_allows_ping(self): self.source.assert_no_ping(self.destination.ip) self.ipset._add_member_to_set(self.ipset_name, self.source.ip) self.source.assert_ping(self.destination.ip) def test_del_member_denies_ping(self): self.ipset._add_member_to_set(self.ipset_name, self.source.ip) self.source.assert_ping(self.destination.ip) self.ipset._del_member_from_set(self.ipset_name, self.source.ip) self.source.assert_no_ping(self.destination.ip) def test_refresh_ipset_allows_ping(self): self.ipset._refresh_set( self.ipset_name, [UNRELATED_IP], IPSET_ETHERTYPE) self.source.assert_no_ping(self.destination.ip) self.ipset._refresh_set( self.ipset_name, [UNRELATED_IP, self.source.ip], IPSET_ETHERTYPE) self.source.assert_ping(self.destination.ip) self.ipset._refresh_set( self.ipset_name, [self.source.ip, UNRELATED_IP], IPSET_ETHERTYPE) self.source.assert_ping(self.destination.ip) def test_destroy_ipset_set(self): self._remove_iptables_ipset_rules() self.ipset._destroy(self.ipset_name) neutron-12.1.1/neutron/tests/functional/agent/linux/test_netlink_lib.py0000664000175000017500000001264513553660046026466 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.linux import utils as linux_utils from neutron.privileged.agent.linux import netlink_lib as nl_lib from neutron.tests.functional import base as functional_base class NetlinkLibTestCase(functional_base.BaseSudoTestCase): """Functional test for netlink_lib: List, delete, flush conntrack entries. For each function, first we add a specific namespace, then create real conntrack entries. netlink_lib function will do list, delete and flush these entries. This class will test this netlink_lib function work as expected. """ def _create_entries(self, zone): conntrack_cmds = ( ['conntrack', '-I', '-p', 'tcp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--sport', '1', '--dport', '2', '--state', 'ESTABLISHED', '--timeout', '1234', '-w', zone], ['conntrack', '-I', '-p', 'udp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--sport', '4', '--dport', '5', '--timeout', '1234', '-w', zone], ['conntrack', '-I', '-p', 'icmp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--icmp-type', '8', '--icmp-code', '0', '--icmp-id', '3333', '--timeout', '1234', '-w', zone], ) for cmd in conntrack_cmds: try: linux_utils.execute(cmd, run_as_root=True, check_exit_code=True, extra_ok_codes=[1]) except RuntimeError: raise Exception('Error while creating entry') def _delete_entry(self, delete_entries, remain_entries, zone): nl_lib.delete_entries(entries=delete_entries) entries_list = nl_lib.list_entries(zone=zone) for delete_entry in delete_entries: self.assertNotIn(delete_entry, entries_list) for remain_entry in remain_entries: self.assertIn(remain_entry, entries_list) @staticmethod def _find_unused_zone_id(start, end): """Find unused zone ID starting from a specified ID""" while start <= end: cmd = ['conntrack', '-L', '-w', start] try: current_entries = linux_utils.execute(cmd, run_as_root=True, check_exit_code=True, extra_ok_codes=[1]) except RuntimeError: raise Exception('Error while listing entries') if not current_entries: return start start += 1 raise Exception("Can not find usable zone_id") def test_list_entries(self): _zone = self._find_unused_zone_id(10, 30) self._create_entries(zone=_zone) expected = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333, _zone), (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2', _zone), (4, 'udp', 4, 5, '1.1.1.1', '2.2.2.2', _zone) ) entries_list = nl_lib.list_entries(zone=_zone) for entry in expected: self.assertIn(entry, entries_list) def test_delete_icmp_entry(self): _zone = self._find_unused_zone_id(31, 50) self._create_entries(zone=_zone) icmp_entry = [(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333, _zone)] remain_entries = ( (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2', _zone), (4, 'udp', 4, 5, '1.1.1.1', '2.2.2.2', _zone), ) self._delete_entry(icmp_entry, remain_entries, _zone) def test_delete_tcp_entry(self): _zone = self._find_unused_zone_id(51, 70) self._create_entries(zone=_zone) tcp_entry = [(4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2', _zone)] remain_entries = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333, _zone), (4, 'udp', 4, 5, '1.1.1.1', '2.2.2.2', _zone) ) self._delete_entry(tcp_entry, remain_entries, _zone) def test_delete_udp_entry(self): _zone = self._find_unused_zone_id(71, 90) self._create_entries(zone=_zone) udp_entry = [(4, 'udp', 4, 5, '1.1.1.1', '2.2.2.2', _zone)] remain_entries = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333, _zone), (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2', _zone) ) self._delete_entry(udp_entry, remain_entries, _zone) def test_delete_multiple_entries(self): _zone = self._find_unused_zone_id(91, 110) self._create_entries(zone=_zone) delete_entries = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333, _zone), (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2', _zone), (4, 'udp', 4, 5, '1.1.1.1', '2.2.2.2', _zone) ) remain_entries = () self._delete_entry(delete_entries, remain_entries, _zone) neutron-12.1.1/neutron/tests/functional/agent/linux/test_dhcp.py0000664000175000017500000000756013553660047025113 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg from neutron.agent.linux import dhcp from neutron.agent.linux import ip_lib from neutron.common import utils as common_utils from neutron.conf.agent import common as config from neutron.conf.agent import dhcp as dhcp_conf from neutron.conf import common as common_conf from neutron.tests import base as tests_base from neutron.tests.common import net_helpers from neutron.tests.functional import base as functional_base class TestDhcp(functional_base.BaseSudoTestCase): def setUp(self): super(TestDhcp, self).setUp() conf = cfg.ConfigOpts() config.register_interface_driver_opts_helper(conf) config.register_interface_opts(conf) conf.register_opts(common_conf.core_opts) conf.register_opts(dhcp_conf.DHCP_AGENT_OPTS) conf.set_override('interface_driver', 'openvswitch') conf.set_override('host', 'foo-host') self.conf = conf br_int = self.useFixture(net_helpers.OVSBridgeFixture()).bridge self.conf.set_override('ovs_integration_bridge', br_int.br_name) def test_cleanup_stale_devices(self): plugin = mock.MagicMock() dev_mgr = dhcp.DeviceManager(self.conf, plugin) network = { 'id': 'foo_id', 'tenant_id': 'foo_tenant', 'namespace': 'qdhcp-foo_id', 'ports': [], 'subnets': [tests_base.AttributeDict({'id': 'subnet_foo_id', 'enable_dhcp': True, 'ipv6_address_mode': None, 'ipv6_ra_mode': None, 'cidr': '10.0.0.0/24', 'ip_version': 4, 'gateway_ip': '10.0.0.1'})]} dhcp_port = { 'id': 'foo_port_id', 'mac_address': '10:22:33:44:55:67', 'fixed_ips': [tests_base.AttributeDict( {'subnet_id': 'subnet_foo_id', 'ip_address': '10.0.0.1'})] } plugin.create_dhcp_port.return_value = tests_base.AttributeDict( dhcp_port) dev_mgr.driver.plug("foo_id", "foo_id2", "tapfoo_id2", "10:22:33:44:55:68", namespace="qdhcp-foo_id") dev_mgr.driver.plug("foo_id", "foo_id3", "tapfoo_id3", "10:22:33:44:55:69", namespace="qdhcp-foo_id") ipw = ip_lib.IPWrapper(namespace="qdhcp-foo_id") devices = ipw.get_devices() self.addCleanup(ipw.netns.delete, 'qdhcp-foo_id') self.assertEqual(sorted(["tapfoo_id2", "tapfoo_id3"]), sorted(map(str, devices))) # setting up dhcp for the network dev_mgr.setup(tests_base.AttributeDict(network)) common_utils.wait_until_true( lambda: 1 == len(ipw.get_devices()), timeout=5, sleep=0.1, exception=RuntimeError("only one non-loopback device must remain")) devices = ipw.get_devices() self.assertEqual("tapfoo_port_id", devices[0].name) neutron-12.1.1/neutron/tests/functional/agent/linux/test_ovsdb_monitor.py0000664000175000017500000001347613553660047027064 0ustar zuulzuul00000000000000# Copyright 2013 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Tests in this module will be skipped unless: - ovsdb-client is installed - ovsdb-client can be invoked password-less via the configured root helper - sudo testing is enabled (see neutron.tests.functional.base for details) """ from oslo_config import cfg from neutron.agent.common import ovs_lib from neutron.agent.linux import ovsdb_monitor from neutron.common import utils from neutron.tests import base from neutron.tests.common import net_helpers from neutron.tests.functional.agent.linux import base as linux_base class BaseMonitorTest(linux_base.BaseOVSLinuxTestCase): def setUp(self): super(BaseMonitorTest, self).setUp() rootwrap_not_configured = (cfg.CONF.AGENT.root_helper == base.SUDO_CMD) if rootwrap_not_configured: # The monitor tests require a nested invocation that has # to be emulated by double sudo if rootwrap is not # configured. self.config(group='AGENT', root_helper=" ".join([base.SUDO_CMD] * 2)) self._check_test_requirements() # ovsdb-client monitor needs to have a bridge to make any output self.useFixture(net_helpers.OVSBridgeFixture()) def _check_test_requirements(self): self.check_command(['ovsdb-client', 'list-dbs'], 'Exit code: 1', 'password-less sudo not granted for ovsdb-client', run_as_root=True) class TestOvsdbMonitor(BaseMonitorTest): def setUp(self): super(TestOvsdbMonitor, self).setUp() self.monitor = ovsdb_monitor.OvsdbMonitor('Bridge') self.addCleanup(self.monitor.stop) self.monitor.start() def collect_monitor_output(self): output = list(self.monitor.iter_stdout()) if output: # Output[0] is header row with spaces for column separation. # Use 'other_config' as an indication of the table header. self.assertIn('other_config', output[0]) return True def test_monitor_generates_initial_output(self): utils.wait_until_true(self.collect_monitor_output, timeout=30) class TestSimpleInterfaceMonitor(BaseMonitorTest): def setUp(self): super(TestSimpleInterfaceMonitor, self).setUp() self.monitor = ovsdb_monitor.SimpleInterfaceMonitor() self.addCleanup(self.monitor.stop) self.monitor.start(block=True, timeout=60) def test_has_updates(self): utils.wait_until_true(lambda: self.monitor.has_updates) # clear the event list self.monitor.get_events() self.useFixture(net_helpers.OVSPortFixture()) # has_updates after port addition should become True utils.wait_until_true(lambda: self.monitor.has_updates is True) def _expected_devices_events(self, devices, state): """Helper to check that events are received for expected devices. :param devices: The list of expected devices. WARNING: This list is modified by this method :param state: The state of the devices (added or removed) """ events = self.monitor.get_events() event_devices = [ (dev['name'], dev['external_ids']) for dev in events.get(state)] for dev in event_devices: if dev[0] in devices: devices.remove(dev[0]) self.assertEqual(dev[1].get('iface-status'), 'active') if not devices: return True def test_get_events(self): utils.wait_until_true(lambda: self.monitor.has_updates) devices = self.monitor.get_events() self.assertTrue(devices.get('added'), 'Initial call should always be true') br = self.useFixture(net_helpers.OVSBridgeFixture()) p1 = self.useFixture(net_helpers.OVSPortFixture(br.bridge)) p2 = self.useFixture(net_helpers.OVSPortFixture(br.bridge)) added_devices = [p1.port.name, p2.port.name] utils.wait_until_true( lambda: self._expected_devices_events(added_devices, 'added')) br.bridge.delete_port(p1.port.name) br.bridge.delete_port(p2.port.name) removed_devices = [p1.port.name, p2.port.name] utils.wait_until_true( lambda: self._expected_devices_events(removed_devices, 'removed')) # restart self.monitor.stop(block=True) self.monitor.start(block=True, timeout=60) try: utils.wait_until_true( lambda: self.monitor.get_events().get('added')) except utils.WaitTimeout: raise AssertionError('Initial call should always be true') def test_get_events_includes_ofport(self): utils.wait_until_true(lambda: self.monitor.has_updates) self.monitor.get_events() # clear initial events br = self.useFixture(net_helpers.OVSBridgeFixture()) p1 = self.useFixture(net_helpers.OVSPortFixture(br.bridge)) def p1_event_has_ofport(): if not self.monitor.has_updates: return for e in self.monitor.new_events['added']: if (e['name'] == p1.port.name and e['ofport'] != ovs_lib.UNASSIGNED_OFPORT): return True utils.wait_until_true(p1_event_has_ofport) neutron-12.1.1/neutron/tests/functional/agent/linux/test_keepalived.py0000664000175000017500000000772113553660047026305 0ustar zuulzuul00000000000000# Copyright (c) 2014 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from neutron._i18n import _ from neutron.agent.linux import external_process from neutron.agent.linux import keepalived from neutron.agent.linux import utils from neutron.common import utils as common_utils from neutron.tests.functional.agent.linux import helpers from neutron.tests.functional import base from neutron.tests.unit.agent.linux import test_keepalived class KeepalivedManagerTestCase(base.BaseLoggingTestCase, test_keepalived.KeepalivedConfBaseMixin): def setUp(self): super(KeepalivedManagerTestCase, self).setUp() cfg.CONF.set_override('check_child_processes_interval', 1, 'AGENT') self.expected_config = self._get_config() self.process_monitor = external_process.ProcessMonitor(cfg.CONF, 'router') self.manager = keepalived.KeepalivedManager( 'router1', self.expected_config, self.process_monitor, conf_path=cfg.CONF.state_path) self.addCleanup(self.manager.disable) def _spawn_keepalived(self, keepalived_manager): keepalived_manager.spawn() process = keepalived_manager.get_process() common_utils.wait_until_true( lambda: process.active, timeout=5, sleep=0.01, exception=RuntimeError(_("Keepalived didn't spawn"))) return process def test_keepalived_spawn(self): self._spawn_keepalived(self.manager) self.assertEqual(self.expected_config.get_config_str(), self.manager.get_conf_on_disk()) def _test_keepalived_respawns(self, normal_exit=True): process = self._spawn_keepalived(self.manager) pid = process.pid exit_code = '-15' if normal_exit else '-9' # Exit the process, and see that when it comes back # It's indeed a different process utils.execute(['kill', exit_code, pid]) common_utils.wait_until_true( lambda: process.active and pid != process.pid, timeout=5, sleep=0.01, exception=RuntimeError(_("Keepalived didn't respawn"))) def test_keepalived_respawns(self): self._test_keepalived_respawns() def test_keepalived_respawn_with_unexpected_exit(self): self._test_keepalived_respawns(False) def _test_keepalived_spawns_conflicting_pid(self, process, pid_file): # Test the situation when keepalived PID file contains PID of an # existing non-keepalived process. This situation can happen e.g. # after hard node reset. spawn_process = helpers.SleepyProcessFixture() self.useFixture(spawn_process) with open(pid_file, "w") as f_pid_file: f_pid_file.write("%s" % spawn_process.pid) self._spawn_keepalived(self.manager) def test_keepalived_spawns_conflicting_pid_base_process(self): process = self.manager.get_process() pid_file = process.get_pid_file_name() self._test_keepalived_spawns_conflicting_pid(process, pid_file) def test_keepalived_spawns_conflicting_pid_vrrp_subprocess(self): process = self.manager.get_process() pid_file = process.get_pid_file_name() self._test_keepalived_spawns_conflicting_pid( process, self.manager.get_vrrp_pid_file_name(pid_file)) neutron-12.1.1/neutron/tests/functional/agent/linux/test_l3_tc_lib.py0000664000175000017500000001460213553660047026022 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as common_constants from oslo_utils import uuidutils from neutron.agent.l3 import namespaces from neutron.agent.linux import ip_lib from neutron.agent.linux import l3_tc_lib from neutron.common import exceptions from neutron.tests.functional import base as functional_base RATE_LIMIT = 1024 BURST_LIMIT = 512 DEV_NAME = "test_device" class TcLibTestCase(functional_base.BaseSudoTestCase): def create_tc_wrapper_with_namespace_and_device(self): ns_name = uuidutils.generate_uuid() namespace = namespaces.Namespace( ns_name, None, mock.Mock(), False) namespace.create() self.addCleanup(namespace.delete) ip_wrapper = ip_lib.IPWrapper(namespace=ns_name) tc_device = ip_wrapper.add_tuntap(DEV_NAME) tc_device.link.set_up() return l3_tc_lib.FloatingIPTcCommand( DEV_NAME, namespace=ns_name) def test_clear_all_filters(self): ip_addr = "2.2.2.2" l3_tc = self.create_tc_wrapper_with_namespace_and_device() l3_tc.set_ip_rate_limit(common_constants.INGRESS_DIRECTION, ip_addr, RATE_LIMIT, BURST_LIMIT) l3_tc.set_ip_rate_limit(common_constants.EGRESS_DIRECTION, ip_addr, RATE_LIMIT, BURST_LIMIT) l3_tc.clear_all_filters(common_constants.INGRESS_DIRECTION) self.assertRaises(exceptions.FilterIDForIPNotFound, l3_tc.get_filter_id_for_ip, common_constants.INGRESS_DIRECTION, ip_addr) l3_tc.clear_all_filters(common_constants.EGRESS_DIRECTION) self.assertRaises(exceptions.FilterIDForIPNotFound, l3_tc.get_filter_id_for_ip, common_constants.EGRESS_DIRECTION, ip_addr) def test_get_filter_id_for_ip(self): ip_addr = "3.3.3.3" l3_tc = self.create_tc_wrapper_with_namespace_and_device() l3_tc.set_ip_rate_limit(common_constants.INGRESS_DIRECTION, ip_addr, RATE_LIMIT, BURST_LIMIT) l3_tc.set_ip_rate_limit(common_constants.EGRESS_DIRECTION, ip_addr, RATE_LIMIT, BURST_LIMIT) self.assertIsNotNone( l3_tc.get_filter_id_for_ip(common_constants.INGRESS_DIRECTION, ip_addr)) self.assertIsNotNone( l3_tc.get_filter_id_for_ip(common_constants.EGRESS_DIRECTION, ip_addr)) # testing: IP filter does not exist self.assertRaises(exceptions.FilterIDForIPNotFound, l3_tc.get_filter_id_for_ip, common_constants.EGRESS_DIRECTION, '33.33.33.33') def test_get_existing_filter_ids(self): ip_addr = "4.4.4.4" l3_tc = self.create_tc_wrapper_with_namespace_and_device() l3_tc.set_ip_rate_limit(common_constants.INGRESS_DIRECTION, ip_addr, RATE_LIMIT, BURST_LIMIT) l3_tc.set_ip_rate_limit(common_constants.EGRESS_DIRECTION, ip_addr, RATE_LIMIT, BURST_LIMIT) filter_ids = l3_tc.get_existing_filter_ids( common_constants.INGRESS_DIRECTION) self.assertNotEqual(0, len(filter_ids)) filter_ids = l3_tc.get_existing_filter_ids( common_constants.EGRESS_DIRECTION) self.assertNotEqual(0, len(filter_ids)) def test_delete_filter_ids(self): ip_addr1 = "5.5.5.5" ip_addr2 = "6.6.6.6" l3_tc = self.create_tc_wrapper_with_namespace_and_device() l3_tc.set_ip_rate_limit(common_constants.INGRESS_DIRECTION, ip_addr1, RATE_LIMIT, BURST_LIMIT) l3_tc.set_ip_rate_limit(common_constants.INGRESS_DIRECTION, ip_addr2, RATE_LIMIT, BURST_LIMIT) filter_ids = l3_tc.get_existing_filter_ids( common_constants.INGRESS_DIRECTION) self.assertEqual(2, len(filter_ids)) l3_tc.delete_filter_ids(common_constants.INGRESS_DIRECTION, filter_ids) filter_ids = l3_tc.get_existing_filter_ids( common_constants.INGRESS_DIRECTION) self.assertEqual(0, len(filter_ids)) def test_set_ip_rate_limit(self): ip_addr = "7.7.7.7" l3_tc = self.create_tc_wrapper_with_namespace_and_device() # Set it multiple times l3_tc.set_ip_rate_limit(common_constants.INGRESS_DIRECTION, ip_addr, RATE_LIMIT, BURST_LIMIT) l3_tc.set_ip_rate_limit(common_constants.INGRESS_DIRECTION, ip_addr, RATE_LIMIT, BURST_LIMIT) l3_tc.set_ip_rate_limit(common_constants.INGRESS_DIRECTION, ip_addr, RATE_LIMIT, BURST_LIMIT) # Get only one and no exception filter_id = l3_tc.get_filter_id_for_ip( common_constants.INGRESS_DIRECTION, ip_addr) self.assertIsNotNone(filter_id) def test_clear_ip_rate_limit(self): ip_addr = "8.8.8.8" l3_tc = self.create_tc_wrapper_with_namespace_and_device() l3_tc.set_ip_rate_limit(common_constants.INGRESS_DIRECTION, ip_addr, RATE_LIMIT, BURST_LIMIT) filter_id = l3_tc.get_filter_id_for_ip( common_constants.INGRESS_DIRECTION, ip_addr) self.assertIsNotNone(filter_id) filter_id = l3_tc.clear_ip_rate_limit( common_constants.INGRESS_DIRECTION, ip_addr) self.assertIsNone(filter_id) # testing: IP filter does not exist l3_tc.clear_ip_rate_limit( common_constants.INGRESS_DIRECTION, "88.88.88.88") neutron-12.1.1/neutron/tests/functional/agent/test_dhcp_agent.py0000664000175000017500000004155013553660047025127 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import os.path import eventlet import fixtures import mock import netaddr from neutron_lib import constants as lib_const from oslo_config import fixture as fixture_config from oslo_utils import uuidutils from neutron.agent.common import ovs_lib from neutron.agent.dhcp import agent from neutron.agent import dhcp_agent from neutron.agent.linux import dhcp from neutron.agent.linux import external_process from neutron.agent.linux import interface from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.common import utils as common_utils from neutron.conf.agent import common as config from neutron.tests.common import net_helpers from neutron.tests.functional.agent.linux import helpers from neutron.tests.functional import base class DHCPAgentOVSTestFramework(base.BaseSudoTestCase): _DHCP_PORT_MAC_ADDRESS = netaddr.EUI("24:77:03:7d:00:4c") _DHCP_PORT_MAC_ADDRESS.dialect = netaddr.mac_unix _TENANT_PORT_MAC_ADDRESS = netaddr.EUI("24:77:03:7d:00:3a") _TENANT_PORT_MAC_ADDRESS.dialect = netaddr.mac_unix _IP_ADDRS = { 4: {'addr': '192.168.10.11', 'cidr': '192.168.10.0/24', 'gateway': '192.168.10.1'}, 6: {'addr': '2001:db8:0:1::c0a8:a0b', 'cidr': '2001:db8:0:1::c0a8:a00/120', 'gateway': '2001:db8:0:1::c0a8:a01'}, } def setUp(self): super(DHCPAgentOVSTestFramework, self).setUp() config.setup_logging() self.conf_fixture = self.useFixture(fixture_config.Config()) self.conf = self.conf_fixture.conf dhcp_agent.register_options(self.conf) # NOTE(cbrandily): TempDir fixture creates a folder with 0o700 # permissions but agent dir must be readable by dnsmasq user (nobody) agent_config_dir = self.useFixture(fixtures.TempDir()).path self.useFixture( helpers.RecursivePermDirFixture(agent_config_dir, 0o555)) self.conf.set_override("dhcp_confs", agent_config_dir) self.conf.set_override( 'interface_driver', 'neutron.agent.linux.interface.OVSInterfaceDriver') self.conf.set_override('report_interval', 0, 'AGENT') br_int = self.useFixture(net_helpers.OVSBridgeFixture()).bridge self.conf.set_override('ovs_integration_bridge', br_int.br_name) self.mock_plugin_api = mock.patch( 'neutron.agent.dhcp.agent.DhcpPluginApi').start().return_value mock.patch('neutron.agent.rpc.PluginReportStateAPI').start() self.agent = agent.DhcpAgentWithStateReport('localhost') self.ovs_driver = interface.OVSInterfaceDriver(self.conf) self.conf.set_override('check_child_processes_interval', 1, 'AGENT') def network_dict_for_dhcp(self, dhcp_enabled=True, ip_version=4, prefix_override=None): net_id = uuidutils.generate_uuid() subnet_dict = self.create_subnet_dict( net_id, dhcp_enabled, ip_version, prefix_override) port_dict = self.create_port_dict( net_id, subnet_dict.id, mac_address=str(self._DHCP_PORT_MAC_ADDRESS), ip_version=ip_version) port_dict.device_id = common_utils.get_dhcp_agent_device_id( net_id, self.conf.host) net_dict = self.create_network_dict( net_id, [subnet_dict], [port_dict]) return net_dict def create_subnet_dict(self, net_id, dhcp_enabled=True, ip_version=4, prefix_override=None): cidr = self._IP_ADDRS[ip_version]['cidr'] if prefix_override is not None: cidr = '/'.join((cidr.split('/')[0], str(prefix_override))) sn_dict = dhcp.DictModel({ "id": uuidutils.generate_uuid(), "network_id": net_id, "ip_version": ip_version, "cidr": cidr, "gateway_ip": (self. _IP_ADDRS[ip_version]['gateway']), "enable_dhcp": dhcp_enabled, "dns_nameservers": [], "host_routes": [], "ipv6_ra_mode": None, "ipv6_address_mode": None}) if ip_version == 6: sn_dict['ipv6_address_mode'] = lib_const.DHCPV6_STATEFUL return sn_dict def create_port_dict(self, network_id, subnet_id, mac_address, ip_version=4, ip_address=None): ip_address = (self._IP_ADDRS[ip_version]['addr'] if not ip_address else ip_address) port_dict = dhcp.DictModel({ "id": uuidutils.generate_uuid(), "name": "foo", "mac_address": mac_address, "network_id": network_id, "admin_state_up": True, "device_id": uuidutils.generate_uuid(), "device_owner": "foo", "fixed_ips": [{"subnet_id": subnet_id, "ip_address": ip_address}], }) return port_dict def create_network_dict(self, net_id, subnets=None, ports=None, non_local_subnets=None): subnets = [] if not subnets else subnets ports = [] if not ports else ports non_local_subnets = [] if not non_local_subnets else non_local_subnets net_dict = dhcp.NetModel(d={ "id": net_id, "subnets": subnets, "non_local_subnets": non_local_subnets, "ports": ports, "admin_state_up": True, "tenant_id": uuidutils.generate_uuid(), }) return net_dict def get_interface_name(self, network, port): device_manager = dhcp.DeviceManager(conf=self.conf, plugin=mock.Mock()) return device_manager.get_interface_name(network, port) def configure_dhcp_for_network(self, network, dhcp_enabled=True): self.agent.configure_dhcp_for_network(network) self.addCleanup(self._cleanup_network, network, dhcp_enabled) def _cleanup_network(self, network, dhcp_enabled): self.mock_plugin_api.release_dhcp_port.return_value = None if dhcp_enabled: self.agent.call_driver('disable', network) def assert_dhcp_resources(self, network, dhcp_enabled): ovs = ovs_lib.BaseOVS() port = network.ports[0] iface_name = self.get_interface_name(network, port) self.assertEqual(dhcp_enabled, ovs.port_exists(iface_name)) self.assert_dhcp_namespace(network.namespace, dhcp_enabled) self.assert_accept_ra_disabled(network.namespace) self.assert_dhcp_device(network.namespace, iface_name, dhcp_enabled) def assert_dhcp_namespace(self, namespace, dhcp_enabled): self.assertEqual(dhcp_enabled, ip_lib.network_namespace_exists(namespace)) def assert_accept_ra_disabled(self, namespace): actual = ip_lib.IPWrapper(namespace=namespace).netns.execute( ['sysctl', '-b', 'net.ipv6.conf.default.accept_ra']) self.assertEqual('0', actual) def assert_dhcp_device(self, namespace, dhcp_iface_name, dhcp_enabled): dev = ip_lib.IPDevice(dhcp_iface_name, namespace) self.assertEqual(dhcp_enabled, ip_lib.device_exists( dhcp_iface_name, namespace)) if dhcp_enabled: self.assertEqual(self._DHCP_PORT_MAC_ADDRESS, dev.link.address) def _plug_port_for_dhcp_request(self, network, port): namespace = network.namespace vif_name = self.get_interface_name(network.id, port) self.ovs_driver.plug(network.id, port.id, vif_name, port.mac_address, self.conf['ovs_integration_bridge'], namespace=namespace) def _ip_list_for_vif(self, vif_name, namespace): ip_device = ip_lib.IPDevice(vif_name, namespace) return ip_device.addr.list(ip_version=4) def _get_network_port_for_allocation_test(self): network = self.network_dict_for_dhcp() ip_addr = netaddr.IPNetwork(network.subnets[0].cidr)[1] port = self.create_port_dict( network.id, network.subnets[0].id, mac_address=str(self._TENANT_PORT_MAC_ADDRESS), ip_address=str(ip_addr)) return network, port def assert_good_allocation_for_port(self, network, port): vif_name = self.get_interface_name(network.id, port) self._run_dhclient(vif_name, network) predicate = lambda: len( self._ip_list_for_vif(vif_name, network.namespace)) common_utils.wait_until_true(predicate, 10) ip_list = self._ip_list_for_vif(vif_name, network.namespace) cidr = ip_list[0].get('cidr') ip_addr = str(netaddr.IPNetwork(cidr).ip) self.assertEqual(port.fixed_ips[0].ip_address, ip_addr) def assert_bad_allocation_for_port(self, network, port): vif_name = self.get_interface_name(network.id, port) self._run_dhclient(vif_name, network) # we need wait some time (10 seconds is enough) and check # that dhclient not configured ip-address for interface eventlet.sleep(10) ip_list = self._ip_list_for_vif(vif_name, network.namespace) self.assertEqual([], ip_list) def _run_dhclient(self, vif_name, network): # NOTE: Before run dhclient we should create resolv.conf file # in namespace, where we will run dhclient for testing address # allocation for port, otherwise, dhclient will override # system /etc/resolv.conf # By default, folder for dhcp-agent's namespace doesn't exist # that's why we use AdminDirFixture for create directory # with admin permissions in /etc/netns/ and touch resolv.conf in it. etc_dir = '/etc/netns/%s' % network.namespace self.useFixture(helpers.AdminDirFixture(etc_dir)) cmd = ['touch', os.path.join(etc_dir, 'resolv.conf')] utils.execute(cmd, run_as_root=True) dhclient_cmd = ['dhclient', '--no-pid', '-d', '-1', vif_name] proc = net_helpers.RootHelperProcess( cmd=dhclient_cmd, namespace=network.namespace) self.addCleanup(proc.wait) self.addCleanup(proc.kill) def _get_metadata_proxy_process(self, network): return external_process.ProcessManager( self.conf, network.id, network.namespace) class DHCPAgentOVSTestCase(DHCPAgentOVSTestFramework): def test_create_subnet_with_dhcp(self): dhcp_enabled = True for version in [4, 6]: network = self.network_dict_for_dhcp( dhcp_enabled, ip_version=version) self.configure_dhcp_for_network(network=network, dhcp_enabled=dhcp_enabled) self.assert_dhcp_resources(network, dhcp_enabled) def test_create_subnet_with_non64_ipv6_cidrs(self): # the agent should not throw exceptions on weird prefixes dhcp_enabled = True version = 6 for i in (0, 1, 41, 81, 121, 127, 128): network = self.network_dict_for_dhcp( dhcp_enabled, ip_version=version, prefix_override=i) self.configure_dhcp_for_network(network=network, dhcp_enabled=dhcp_enabled) self.assertFalse(self.agent.needs_resync_reasons[network.id], msg="prefix size of %s triggered resync" % i) def test_agent_mtu_set_on_interface_driver(self): network = self.network_dict_for_dhcp() network["mtu"] = 789 self.configure_dhcp_for_network(network=network) port = network.ports[0] iface_name = self.get_interface_name(network, port) dev = ip_lib.IPDevice(iface_name, network.namespace) self.assertEqual(789, dev.link.mtu) def test_good_address_allocation(self): network, port = self._get_network_port_for_allocation_test() network.ports.append(port) self.configure_dhcp_for_network(network=network) self._plug_port_for_dhcp_request(network, port) self.assert_good_allocation_for_port(network, port) def test_bad_address_allocation(self): network, port = self._get_network_port_for_allocation_test() network.ports.append(port) self.configure_dhcp_for_network(network=network) bad_mac_address = netaddr.EUI(self._TENANT_PORT_MAC_ADDRESS.value + 1) bad_mac_address.dialect = netaddr.mac_unix port.mac_address = str(bad_mac_address) self._plug_port_for_dhcp_request(network, port) self.assert_bad_allocation_for_port(network, port) def _spawn_network_metadata_proxy(self): network = self.network_dict_for_dhcp() self.conf.set_override('enable_isolated_metadata', True) self.addCleanup(self.agent.disable_isolated_metadata_proxy, network) self.configure_dhcp_for_network(network=network) pm = self._get_metadata_proxy_process(network) common_utils.wait_until_true( lambda: pm.active, timeout=5, sleep=0.01, exception=RuntimeError("Metadata proxy didn't spawn")) return (pm, network) def test_metadata_proxy_respawned(self): pm, network = self._spawn_network_metadata_proxy() old_pid = pm.pid utils.execute(['kill', '-9', old_pid], run_as_root=True) common_utils.wait_until_true( lambda: pm.active and pm.pid != old_pid, timeout=5, sleep=0.1, exception=RuntimeError("Metadata proxy didn't respawn")) def test_stale_metadata_proxy_killed(self): pm, network = self._spawn_network_metadata_proxy() self.conf.set_override('enable_isolated_metadata', False) self.configure_dhcp_for_network(network=network) common_utils.wait_until_true( lambda: not pm.active, timeout=5, sleep=0.1, exception=RuntimeError("Stale metadata proxy didn't get killed")) def _test_metadata_proxy_spawn_kill_with_subnet_create_delete(self): network = self.network_dict_for_dhcp(ip_version=6) self.configure_dhcp_for_network(network=network) pm = self._get_metadata_proxy_process(network) # A newly created network with ipv6 subnet will not have metadata proxy self.assertFalse(pm.active) new_network = copy.deepcopy(network) dhcp_enabled_ipv4_subnet = self.create_subnet_dict(network.id) new_network.subnets.append(dhcp_enabled_ipv4_subnet) self.mock_plugin_api.get_network_info.return_value = new_network self.agent.refresh_dhcp_helper(network.id) # Metadata proxy should be spawned for the newly added subnet common_utils.wait_until_true( lambda: pm.active, timeout=5, sleep=0.1, exception=RuntimeError("Metadata proxy didn't spawn")) self.mock_plugin_api.get_network_info.return_value = network self.agent.refresh_dhcp_helper(network.id) # Metadata proxy should be killed because network doesn't need it. common_utils.wait_until_true( lambda: not pm.active, timeout=5, sleep=0.1, exception=RuntimeError("Metadata proxy didn't get killed")) def test_enable_isolated_metadata_for_subnet_create_delete(self): self.conf.set_override('force_metadata', False) self.conf.set_override('enable_isolated_metadata', True) self._test_metadata_proxy_spawn_kill_with_subnet_create_delete() def test_force_metadata_for_subnet_create_delete(self): self.conf.set_override('force_metadata', True) self.conf.set_override('enable_isolated_metadata', False) self._test_metadata_proxy_spawn_kill_with_subnet_create_delete() def test_notify_port_ready_after_enable_dhcp(self): network = self.network_dict_for_dhcp() dhcp_port = self.create_port_dict( network.id, network.subnets[0].id, '24:77:03:7d:00:4d', ip_address='192.168.10.11') dhcp_port.device_owner = lib_const.DEVICE_OWNER_DHCP network.ports.append(dhcp_port) self.agent.start_ready_ports_loop() self.configure_dhcp_for_network(network) ports_to_send = {p.id for p in network.ports} common_utils.wait_until_true( lambda: self.mock_plugin_api.dhcp_ready_on_ports.called, timeout=1, sleep=0.1, exception=RuntimeError("'dhcp_ready_on_ports' not be called")) self.mock_plugin_api.dhcp_ready_on_ports.assert_called_with( ports_to_send) neutron-12.1.1/neutron/tests/functional/agent/test_l2_lb_agent.py0000664000175000017500000001457513553660046025211 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg from oslo_utils import uuidutils import testtools from neutron.agent.linux import ip_lib from neutron.objects import trunk from neutron.plugins.ml2.drivers.linuxbridge.agent import \ linuxbridge_neutron_agent from neutron.services.trunk.drivers.linuxbridge.agent import trunk_plumber from neutron.tests.functional.agent.linux import test_ip_lib lba = linuxbridge_neutron_agent class LinuxBridgeAgentTests(test_ip_lib.IpLibTestFramework): def setUp(self): super(LinuxBridgeAgentTests, self).setUp() agent_rpc = ('neutron.agent.rpc.PluginApi') mock.patch(agent_rpc).start() mock.patch('neutron.agent.rpc.PluginReportStateAPI').start() cfg.CONF.set_override('enable_vxlan', False, 'VXLAN') def test_validate_interface_mappings(self): mappings = {'physnet1': 'int1', 'physnet2': 'int2'} with testtools.ExpectedException(SystemExit): lba.LinuxBridgeManager({}, mappings) self.manage_device( self.generate_device_details()._replace(namespace=None, name='int1')) with testtools.ExpectedException(SystemExit): lba.LinuxBridgeManager({}, mappings) self.manage_device( self.generate_device_details()._replace(namespace=None, name='int2')) lba.LinuxBridgeManager({}, mappings) def test_validate_bridge_mappings(self): mappings = {'physnet1': 'br-eth1'} with testtools.ExpectedException(SystemExit): lba.LinuxBridgeManager(mappings, {}) self.manage_device( self.generate_device_details()._replace(namespace=None, name='br-eth1')) lba.LinuxBridgeManager(mappings, {}) def test_vlan_subinterfaces(self): attr = self.generate_device_details() device = self.manage_device(attr) devname = device.name plumber = trunk_plumber.Plumber(namespace=attr.namespace) for i in range(20): subname = 'vtest-%s' % i plumber._create_vlan_subint(devname, subname, i) # ensure no addresses were assigned (e.g. ipv6) vlan_int = ip_lib.IPDevice(subname, namespace=attr.namespace) self.assertFalse(vlan_int.addr.list()) children = plumber._get_vlan_children(devname) expected = {('vtest-%s' % i, i) for i in range(20)} self.assertEqual(expected, children) # delete one plumber._safe_delete_device('vtest-19') children = plumber._get_vlan_children(devname) expected = {('vtest-%s' % i, i) for i in range(19)} self.assertEqual(expected, children) # ensure they are removed by parent removal self._safe_delete_device(device) self.assertFalse(plumber._get_vlan_children(devname)) def test_vlan_QinQ_subinterfaces(self): # the trunk model does not support this right now, but this is to # ensure the plumber on the agent side doesn't explode in their # presence in case an operator does something fancy or we have a # race where a trunk's parent port is converted to a subport while # the agent is offline. attr = self.generate_device_details() device = self.manage_device(attr) devname = device.name plumber = trunk_plumber.Plumber(namespace=attr.namespace) for i in range(20): plumber._create_vlan_subint(devname, 'vtest-%s' % i, i) plumber._create_vlan_subint('vtest-%s' % i, 'qinq-%s' % i, 2) top_level = {('vtest-%s' % i, i) for i in range(20)} for i in range(20): # as we iterate, we delete a vlan from each dev and ensure it # didn't break the top-level vlans self.assertEqual({('qinq-%s' % i, 2)}, plumber._get_vlan_children('vtest-%s' % i)) plumber._safe_delete_device('qinq-%s' % i) self.assertEqual(set(), plumber._get_vlan_children('vtest-%i' % i)) self.assertEqual(top_level, plumber._get_vlan_children(devname)) def test_ensure_trunk_subports(self): attr = self.generate_device_details() device = self.manage_device(attr) devname = device.name plumber = trunk_plumber.Plumber(namespace=attr.namespace) plumber._trunk_device_name = lambda x: devname trunk_obj = self._gen_trunk() plumber.ensure_trunk_subports(trunk_obj) # ensure no mutation the second time with mock.patch.object(plumber, '_safe_delete_device', side_effect=RuntimeError()): plumber.ensure_trunk_subports(trunk_obj) while trunk_obj.sub_ports: # drain down the sub-ports and make sure it keeps # them equal trunk_obj.sub_ports.pop() plumber.ensure_trunk_subports(trunk_obj) expected = {(plumber._get_tap_device_name(sp.port_id), sp.segmentation_id) for sp in trunk_obj.sub_ports} wired = plumber._get_vlan_children(devname) self.assertEqual(expected, wired) def _gen_trunk(self): trunk_obj = trunk.Trunk(id=uuidutils.generate_uuid(), port_id=uuidutils.generate_uuid(), project_id=uuidutils.generate_uuid()) subports = [trunk.SubPort(id=uuidutils.generate_uuid(), port_id=uuidutils.generate_uuid(), segmentation_type='vlan', trunk_id=trunk_obj.id, segmentation_id=i) for i in range(20, 40)] trunk_obj.sub_ports = subports return trunk_obj neutron-12.1.1/neutron/tests/functional/agent/__init__.py0000664000175000017500000000000013553660046023513 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/test_firewall.py0000664000175000017500000011300513553660047024633 0ustar zuulzuul00000000000000# Copyright 2015 Intel Corporation. # Copyright 2015 Isaku Yamahata # # Copyright 2015 Red Hat, Inc. # All Rights Reserved. # # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import functools import netaddr from neutron_lib import constants from oslo_config import cfg from oslo_log import log as logging from oslo_utils import uuidutils import testscenarios from neutron.agent import firewall from neutron.agent.linux import iptables_firewall from neutron.agent.linux import openvswitch_firewall from neutron.agent.linux.openvswitch_firewall import constants as ovsfw_consts from neutron.cmd.sanity import checks from neutron.common import constants as n_const from neutron.conf.agent import securitygroups_rpc as security_config from neutron.tests.common import conn_testers from neutron.tests.common import helpers from neutron.tests.functional.agent.linux import base as linux_base from neutron.tests.functional import base from neutron.tests.functional import constants as test_constants LOG = logging.getLogger(__name__) load_tests = testscenarios.load_tests_apply_scenarios reverse_direction = { conn_testers.ConnectionTester.INGRESS: conn_testers.ConnectionTester.EGRESS, conn_testers.ConnectionTester.EGRESS: conn_testers.ConnectionTester.INGRESS} reverse_transport_protocol = { conn_testers.ConnectionTester.TCP: conn_testers.ConnectionTester.UDP, conn_testers.ConnectionTester.UDP: conn_testers.ConnectionTester.TCP} DEVICE_OWNER_COMPUTE = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' def skip_if_firewall(firewall_name): def outter(f): @functools.wraps(f) def wrap(self, *args, **kwargs): if self.firewall_name == firewall_name: self.skipTest("This test doesn't use %s firewall" % firewall_name) return f(self, *args, **kwargs) return wrap return outter def _add_rule(sg_rules, base, port_range_min=None, port_range_max=None): rule = copy.copy(base) if port_range_min: rule['port_range_min'] = port_range_min if port_range_max: rule['port_range_max'] = port_range_max sg_rules.append(rule) class BaseFirewallTestCase(base.BaseSudoTestCase): FAKE_SECURITY_GROUP_ID = uuidutils.generate_uuid() MAC_SPOOFED = "fa:16:3e:9a:2f:48" scenarios_iptables = testscenarios.multiply_scenarios( [('IptablesFirewallDriver', {'initialize': 'initialize_iptables', 'firewall_name': 'iptables'})], [('with ipset', {'enable_ipset': True}), ('without ipset', {'enable_ipset': False})]) scenarios_ovs_fw_interfaces = testscenarios.multiply_scenarios( [('OVS Firewall Driver', {'initialize': 'initialize_ovs', 'firewall_name': 'openvswitch'})], linux_base.BaseOVSLinuxTestCase.scenarios) scenarios = scenarios_iptables + scenarios_ovs_fw_interfaces ip_cidr = None vlan_range = set(range(1, test_constants.VLAN_COUNT - 1)) def setUp(self): security_config.register_securitygroups_opts() self.net_id = uuidutils.generate_uuid() super(BaseFirewallTestCase, self).setUp() self.tester, self.firewall = getattr(self, self.initialize)() if self.firewall_name == "openvswitch": self.assign_vlan_to_peers() self.src_port_desc = self._create_port_description( self.tester.vm_port_id, [self.tester.vm_ip_address], self.tester.vm_mac_address, [self.FAKE_SECURITY_GROUP_ID], self.net_id) # FIXME(jlibosva): We should consider to call prepare_port_filter with # deferred bridge depending on its performance self.firewall.prepare_port_filter(self.src_port_desc) # Traffic coming from patch-port is always VLAN tagged self.tester.set_peer_port_as_patch_port() def initialize_iptables(self): cfg.CONF.set_override('enable_ipset', self.enable_ipset, 'SECURITYGROUP') br_name = ('brq' + self.net_id)[:n_const.LINUX_DEV_LEN] tester = self.useFixture( conn_testers.LinuxBridgeConnectionTester(self.ip_cidr, bridge_name=br_name)) firewall_drv = iptables_firewall.IptablesFirewallDriver( namespace=tester.bridge_namespace) return tester, firewall_drv def initialize_ovs(self): self.config(group='OVS', ovsdb_interface=self.ovsdb_interface) # Tests for ovs requires kernel >= 4.3 and OVS >= 2.5 if not checks.ovs_conntrack_supported(): self.skipTest("Open vSwitch with conntrack is not installed " "on this machine. To run tests for OVS/CT firewall," " please meet the requirements (kernel>=4.3, " "OVS>=2.5). More info at " "https://github.com/openvswitch/ovs/blob/master/" "FAQ.md") tester = self.useFixture( conn_testers.OVSConnectionTester(self.ip_cidr)) firewall_drv = openvswitch_firewall.OVSFirewallDriver(tester.bridge) return tester, firewall_drv def assign_vlan_to_peers(self): vlan = helpers.get_not_used_vlan(self.firewall.int_br.br, self.vlan_range) LOG.debug("Using %d vlan tag for this test", vlan) self.tester.set_vm_tag(vlan) self.tester.set_peer_tag(vlan) @staticmethod def _create_port_description(port_id, ip_addresses, mac_address, sg_ids, net_id): return {'admin_state_up': True, 'device': port_id, 'device_owner': DEVICE_OWNER_COMPUTE, 'fixed_ips': ip_addresses, 'mac_address': mac_address, 'port_security_enabled': True, 'security_groups': sg_ids, 'status': 'ACTIVE', 'network_id': net_id} def _apply_security_group_rules(self, sg_id, sg_rules): with self.firewall.defer_apply(): self.firewall.update_security_group_rules(sg_id, sg_rules) self.firewall.update_port_filter(self.src_port_desc) def _apply_security_group_members(self, sg_id, members): with self.firewall.defer_apply(): self.firewall.update_security_group_members(sg_id, members) self.firewall.update_port_filter(self.src_port_desc) class FirewallTestCase(BaseFirewallTestCase): ip_cidr = '192.168.0.1/24' @skip_if_firewall('openvswitch') def test_rule_application_converges(self): sg_rules = [{'ethertype': 'IPv4', 'direction': 'egress'}, {'ethertype': 'IPv6', 'direction': 'egress'}, {'ethertype': 'IPv4', 'direction': 'ingress', 'source_ip_prefix': '0.0.0.0/0', 'protocol': 'icmp'}, {'ethertype': 'IPv6', 'direction': 'ingress', 'source_ip_prefix': '0::0/0', 'protocol': 'ipv6-icmp'}] # make sure port ranges converge on all protocols with and without # port ranges (prevents regression of bug 1502924) for proto in ('tcp', 'udp', 'icmp'): for version in ('IPv4', 'IPv6'): if proto == 'icmp' and version == 'IPv6': proto = 'ipv6-icmp' base = {'ethertype': version, 'direction': 'ingress', 'protocol': proto} sg_rules.append(copy.copy(base)) _add_rule(sg_rules, base, port_range_min=50, port_range_max=50) _add_rule(sg_rules, base, port_range_max=55) _add_rule(sg_rules, base, port_range_min=60, port_range_max=60) _add_rule(sg_rules, base, port_range_max=65) # add some single-host rules to prevent regression of bug 1502917 sg_rules.append({'ethertype': 'IPv4', 'direction': 'ingress', 'source_ip_prefix': '77.77.77.77/32'}) sg_rules.append({'ethertype': 'IPv6', 'direction': 'ingress', 'source_ip_prefix': 'fe80::1/128'}) self.firewall.update_security_group_rules( self.FAKE_SECURITY_GROUP_ID, sg_rules) self.firewall.prepare_port_filter(self.src_port_desc) # after one prepare call, another apply should be a NOOP self.assertEqual([], self.firewall.iptables._apply()) orig_sg_rules = copy.copy(sg_rules) for proto in ('tcp', 'udp', 'icmp'): for version in ('IPv4', 'IPv6'): if proto == 'icmp' and version == 'IPv6': proto = 'ipv6-icmp' # make sure firewall is in converged state self.firewall.update_security_group_rules( self.FAKE_SECURITY_GROUP_ID, orig_sg_rules) self.firewall.update_port_filter(self.src_port_desc) sg_rules = copy.copy(orig_sg_rules) # remove one rule and add another to make sure it results in # exactly one delete and insert sg_rules.pop(0 if version == 'IPv4' else 1) sg_rules.append({'ethertype': version, 'direction': 'egress', 'protocol': proto}) self.firewall.update_security_group_rules( self.FAKE_SECURITY_GROUP_ID, sg_rules) result = self.firewall.update_port_filter(self.src_port_desc) deletes = [r for r in result if r.startswith('-D ')] creates = [r for r in result if r.startswith('-I ')] self.assertEqual(1, len(deletes)) self.assertEqual(1, len(creates)) # quick sanity check to make sure the insert was for the # correct proto self.assertIn('-p %s' % proto, creates[0]) # another apply should be a NOOP if the right rule was removed # and the new one was inserted in the correct position self.assertEqual([], self.firewall.iptables._apply()) @skip_if_firewall('openvswitch') def test_rule_ordering_correct(self): sg_rules = [ {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'tcp', 'port_range_min': i, 'port_range_max': i} for i in range(50, 61) ] self.firewall.update_security_group_rules( self.FAKE_SECURITY_GROUP_ID, sg_rules) self.firewall.prepare_port_filter(self.src_port_desc) self._assert_sg_out_tcp_rules_appear_in_order(sg_rules) # remove a rule and add a new one sg_rules.pop(5) sg_rules.insert(8, {'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'tcp', 'port_range_min': 400, 'port_range_max': 400}) self.firewall.update_security_group_rules( self.FAKE_SECURITY_GROUP_ID, sg_rules) self.firewall.prepare_port_filter(self.src_port_desc) self._assert_sg_out_tcp_rules_appear_in_order(sg_rules) # reverse all of the rules (requires lots of deletes and inserts) sg_rules = list(reversed(sg_rules)) self.firewall.update_security_group_rules( self.FAKE_SECURITY_GROUP_ID, sg_rules) self.firewall.prepare_port_filter(self.src_port_desc) self._assert_sg_out_tcp_rules_appear_in_order(sg_rules) def _assert_sg_out_tcp_rules_appear_in_order(self, sg_rules): outgoing_rule_pref = '-A %s-o%s' % (self.firewall.iptables.wrap_name, self.src_port_desc['device'][3:13]) rules = [ r for r in self.firewall.iptables.get_rules_for_table('filter') if r.startswith(outgoing_rule_pref) ] # we want to ensure the rules went in in the same order we sent indexes = [rules.index('%s -p tcp -m tcp --dport %s -j RETURN' % (outgoing_rule_pref, i['port_range_min'])) for i in sg_rules] # all indexes should be in order with no unexpected rules in between self.assertEqual(list(range(indexes[0], indexes[-1] + 1)), indexes) def test_ingress_icmp_secgroup(self): # update the sg_group to make ping pass sg_rules = [{'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_ICMP}, {'ethertype': constants.IPv4, 'direction': firewall.EGRESS_DIRECTION}] self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) def test_mac_spoofing(self): sg_rules = [{'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_ICMP}, {'ethertype': constants.IPv4, 'direction': firewall.EGRESS_DIRECTION}] self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.vm_mac_address = self.MAC_SPOOFED self.tester.flush_arp_tables() self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) @skip_if_firewall('openvswitch') def test_mac_spoofing_works_without_port_security_enabled(self): self.src_port_desc['port_security_enabled'] = False self.firewall.update_port_filter(self.src_port_desc) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.vm_mac_address = self.MAC_SPOOFED self.tester.flush_arp_tables() self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) def test_port_security_enabled_set_to_false(self): self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.src_port_desc['port_security_enabled'] = False self.firewall.update_port_filter(self.src_port_desc) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) def test_dhcp_requests_from_vm(self): # DHCPv4 uses source port 67, destination port 68 self.tester.assert_connection(direction=self.tester.EGRESS, protocol=self.tester.UDP, src_port=68, dst_port=67) def test_dhcp_server_forbidden_on_vm(self): self.tester.assert_no_connection(direction=self.tester.EGRESS, protocol=self.tester.UDP, src_port=67, dst_port=68) self.tester.assert_no_connection(direction=self.tester.INGRESS, protocol=self.tester.UDP, src_port=68, dst_port=67) def test_ip_spoofing(self): sg_rules = [{'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_ICMP}] self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) not_allowed_ip = "%s/24" % ( netaddr.IPAddress(self.tester.vm_ip_address) + 1) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.vm_ip_cidr = not_allowed_ip self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) self.tester.assert_no_connection(protocol=self.tester.UDP, src_port=68, dst_port=67, direction=self.tester.EGRESS) @skip_if_firewall('openvswitch') def test_ip_spoofing_works_without_port_security_enabled(self): self.src_port_desc['port_security_enabled'] = False self.firewall.update_port_filter(self.src_port_desc) sg_rules = [{'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_ICMP}] self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) not_allowed_ip = "%s/24" % ( netaddr.IPAddress(self.tester.vm_ip_address) + 1) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.vm_ip_cidr = not_allowed_ip self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) def test_allowed_address_pairs(self): sg_rules = [{'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_ICMP}, {'ethertype': constants.IPv4, 'direction': firewall.EGRESS_DIRECTION}] self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) port_mac = self.tester.vm_mac_address allowed_ip = netaddr.IPAddress(self.tester.vm_ip_address) + 1 not_allowed_ip = "%s/24" % (allowed_ip + 1) allowed_mac = 'fa:16:3e:8c:84:13' not_allowed_mac = 'fa:16:3e:8c:84:14' self.src_port_desc['allowed_address_pairs'] = [ {'mac_address': port_mac, 'ip_address': "%s/32" % allowed_ip}, {'mac_address': allowed_mac, 'ip_address': "%s/32" % allowed_ip}] allowed_ip = "%s/24" % allowed_ip self.firewall.update_port_filter(self.src_port_desc) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) self.tester.vm_ip_cidr = allowed_ip self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) self.tester.vm_ip_cidr = not_allowed_ip self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) self.tester.vm_mac_address = allowed_mac self.tester.vm_ip_cidr = allowed_ip self.tester.flush_arp_tables() self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) self.tester.vm_mac_address = allowed_mac self.tester.vm_ip_cidr = not_allowed_ip self.tester.flush_arp_tables() self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) self.tester.vm_mac_address = not_allowed_mac self.tester.vm_ip_cidr = allowed_ip self.tester.flush_arp_tables() self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) def test_arp_is_allowed(self): self.tester.assert_connection(protocol=self.tester.ARP, direction=self.tester.EGRESS) self.tester.assert_connection(protocol=self.tester.ARP, direction=self.tester.INGRESS) def _test_rule(self, direction, protocol): sg_rules = [{'ethertype': constants.IPv4, 'direction': direction, 'protocol': protocol}] self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) not_allowed_direction = reverse_direction[direction] not_allowed_protocol = reverse_transport_protocol[protocol] self.tester.assert_connection(protocol=protocol, direction=direction) self.tester.assert_no_connection(protocol=not_allowed_protocol, direction=direction) self.tester.assert_no_connection(protocol=protocol, direction=not_allowed_direction) def test_ingress_tcp_rule(self): self._test_rule(self.tester.INGRESS, self.tester.TCP) def test_next_port_closed(self): # https://bugs.launchpad.net/neutron/+bug/1611991 was caused by wrong # masking in rules which allow traffic to a port with even port number port = 42 for direction in (self.tester.EGRESS, self.tester.INGRESS): sg_rules = [{'ethertype': constants.IPv4, 'direction': direction, 'protocol': constants.PROTO_NAME_TCP, 'source_port_range_min': port, 'source_port_range_max': port}] self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.assert_connection(protocol=self.tester.TCP, direction=direction, src_port=port) self.tester.assert_no_connection(protocol=self.tester.TCP, direction=direction, src_port=port + 1) def test_ingress_udp_rule(self): self._test_rule(self.tester.INGRESS, self.tester.UDP) def test_egress_tcp_rule(self): self._test_rule(self.tester.EGRESS, self.tester.TCP) def test_egress_udp_rule(self): self._test_rule(self.tester.EGRESS, self.tester.UDP) def test_connection_with_destination_port_range(self): port_min = 12345 port_max = 12346 sg_rules = [{'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_TCP, 'port_range_min': port_min, 'port_range_max': port_max}] self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.assert_connection(protocol=self.tester.TCP, direction=self.tester.INGRESS, dst_port=port_min) self.tester.assert_connection(protocol=self.tester.TCP, direction=self.tester.INGRESS, dst_port=port_max) self.tester.assert_no_connection(protocol=self.tester.TCP, direction=self.tester.INGRESS, dst_port=port_min - 1) self.tester.assert_no_connection(protocol=self.tester.TCP, direction=self.tester.INGRESS, dst_port=port_max + 1) def test_connection_with_source_port_range(self): source_port_min = 12345 source_port_max = 12346 sg_rules = [{'ethertype': constants.IPv4, 'direction': firewall.EGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_TCP, 'source_port_range_min': source_port_min, 'source_port_range_max': source_port_max}] self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.assert_connection(protocol=self.tester.TCP, direction=self.tester.EGRESS, src_port=source_port_min) self.tester.assert_connection(protocol=self.tester.TCP, direction=self.tester.EGRESS, src_port=source_port_max) self.tester.assert_no_connection(protocol=self.tester.TCP, direction=self.tester.EGRESS, src_port=source_port_min - 1) self.tester.assert_no_connection(protocol=self.tester.TCP, direction=self.tester.EGRESS, src_port=source_port_max + 1) @skip_if_firewall('iptables') def test_established_connection_is_cut(self): port = 12345 sg_rules = [{'ethertype': constants.IPv4, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_TCP, 'port_range_min': port, 'port_range_max': port}] connection = {'protocol': self.tester.TCP, 'direction': self.tester.INGRESS, 'dst_port': port} self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.establish_connection(**connection) self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, list()) self.tester.assert_no_established_connection(**connection) def test_preventing_firewall_blink(self): direction = self.tester.INGRESS sg_rules = [{'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'tcp'}] self.tester.start_sending_icmp(direction) self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, {}) self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.stop_sending_icmp(direction) packets_sent = self.tester.get_sent_icmp_packets(direction) packets_received = self.tester.get_received_icmp_packets(direction) self.assertGreater(packets_sent, 0) self.assertEqual(packets_received, 0) def test_remote_security_groups(self): self.tester.set_peer_port_as_vm_port() remote_sg_id = 'remote_sg_id' peer_port_desc = self._create_port_description( self.tester.peer_port_id, [self.tester.peer_ip_address], self.tester.peer_mac_address, [remote_sg_id], self.net_id) vm_sg_members = {'IPv4': [self.tester.peer_ip_address]} peer_sg_rules = [{'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'icmp'}] self.firewall.update_security_group_rules(remote_sg_id, peer_sg_rules) self.firewall.update_security_group_members(remote_sg_id, vm_sg_members) self.firewall.prepare_port_filter(peer_port_desc) vm_sg_rules = [{'ethertype': 'IPv4', 'direction': 'ingress', 'protocol': 'icmp', 'remote_group_id': remote_sg_id}] self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, vm_sg_rules) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.assert_no_connection(protocol=self.tester.TCP, direction=self.tester.INGRESS) self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) def test_related_connection(self): """Test ICMP net unreachable packets get back When destination address of ip traffic is not reachable, ICMP packets are returned. This packets are marked as RELATED traffic by conntrack and this test case validates such packets are not dropped by the firewall as ingress ICMP packets are not allowed in this test case. The used address below 1.2.3.4 is outside of subnet that is used in tester object. """ # Enable ip forwarding on the interface in order to reply with # destination net unreachable self.tester._peer.execute([ 'sysctl', '-w', 'net.ipv4.conf.%s.forwarding=1' % self.tester._peer.port.name]) self.tester.set_vm_default_gateway(self.tester.peer_ip_address) vm_sg_rules = [{'ethertype': 'IPv4', 'direction': 'egress', 'protocol': 'icmp'}] self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, vm_sg_rules) self.tester.assert_net_unreachable(self.tester.EGRESS, '1.2.3.4') @skip_if_firewall('iptables') def test_tracked_connection(self): # put an openflow rule to perform a CT lookup and hence packet will # carry conntrack information self.tester.bridge.add_flow( table=0, priority=200, dl_type="0x0800", ct_state=ovsfw_consts.OF_STATE_NOT_TRACKED, actions="ct(table=0)" ) # update the sg_group to make ping pass sg_rules = [{'ethertype': constants.IPv4, 'direction': constants.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_ICMP}, {'ethertype': constants.IPv4, 'direction': constants.EGRESS_DIRECTION}] self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) class FirewallTestCaseIPv6(BaseFirewallTestCase): scenarios = BaseFirewallTestCase.scenarios_ovs_fw_interfaces ip_cidr = '2001:db8:aaaa::1/64' def test_icmp_from_specific_address(self): sg_rules = [{'ethertype': constants.IPv6, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_ICMP, 'source_ip_prefix': self.tester.peer_ip_address}] self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) def test_icmp_to_specific_address(self): sg_rules = [{'ethertype': constants.IPv6, 'direction': firewall.EGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_ICMP, 'destination_ip_prefix': self.tester.peer_ip_address}] self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) def test_tcp_from_specific_address(self): sg_rules = [{'ethertype': constants.IPv6, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_TCP, 'source_ip_prefix': self.tester.peer_ip_address}] self.tester.assert_no_connection(protocol=self.tester.TCP, direction=self.tester.INGRESS) self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.assert_connection(protocol=self.tester.TCP, direction=self.tester.INGRESS) self.tester.assert_no_connection(protocol=self.tester.UDP, direction=self.tester.INGRESS) self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) def test_tcp_to_specific_address(self): sg_rules = [{'ethertype': constants.IPv6, 'direction': firewall.EGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_TCP, 'destination_ip_prefix': self.tester.peer_ip_address}] self.tester.assert_no_connection(protocol=self.tester.TCP, direction=self.tester.EGRESS) self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.assert_connection(protocol=self.tester.TCP, direction=self.tester.EGRESS) self.tester.assert_no_connection(protocol=self.tester.UDP, direction=self.tester.EGRESS) self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) def test_udp_from_specific_address(self): sg_rules = [{'ethertype': constants.IPv6, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_UDP, 'source_ip_prefix': self.tester.peer_ip_address}] self.tester.assert_no_connection(protocol=self.tester.UDP, direction=self.tester.INGRESS) self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.assert_connection(protocol=self.tester.UDP, direction=self.tester.INGRESS) self.tester.assert_no_connection(protocol=self.tester.TCP, direction=self.tester.INGRESS) self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) def test_udp_to_specific_address(self): sg_rules = [{'ethertype': constants.IPv6, 'direction': firewall.EGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_UDP, 'destination_ip_prefix': self.tester.peer_ip_address}] self.tester.assert_no_connection(protocol=self.tester.UDP, direction=self.tester.EGRESS) self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) self.tester.assert_connection(protocol=self.tester.UDP, direction=self.tester.EGRESS) self.tester.assert_no_connection(protocol=self.tester.TCP, direction=self.tester.EGRESS) self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) @skip_if_firewall('openvswitch') def test_ip_spoofing(self): sg_rules = [{'ethertype': constants.IPv6, 'direction': firewall.INGRESS_DIRECTION, 'protocol': constants.PROTO_NAME_ICMP}] self._apply_security_group_rules(self.FAKE_SECURITY_GROUP_ID, sg_rules) not_allowed_ip = "%s/64" % ( netaddr.IPAddress(self.tester.vm_ip_address) + 1) self.tester.assert_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.vm_ip_cidr = not_allowed_ip self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.INGRESS) self.tester.assert_no_connection(protocol=self.tester.ICMP, direction=self.tester.EGRESS) self.tester.assert_no_connection(protocol=self.tester.UDP, src_port=546, dst_port=547, direction=self.tester.EGRESS) neutron-12.1.1/neutron/tests/functional/agent/test_l2_ovs_agent.py0000664000175000017500000003464613553660047025425 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # Copyright (c) 2015 SUSE Linux Products GmbH # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import time import mock from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron.common import utils from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.tests.common import net_helpers from neutron.tests.functional.agent.l2 import base class TestOVSAgent(base.OVSAgentTestFramework): def test_port_creation_and_deletion(self): self.setup_agent_and_ports( port_dicts=self.create_test_ports()) self.wait_until_ports_state(self.ports, up=True) for port in self.ports: self.agent.int_br.delete_port(port['vif_name']) self.wait_until_ports_state(self.ports, up=False) def test_no_stale_flows_after_port_delete(self): def find_drop_flow(ofport, flows): for flow in flows.split("\n"): if "in_port=%d" % ofport in flow and "actions=drop" in flow: return True return False def num_ports_with_drop_flows(ofports, flows): count = 0 for ofport in ofports: if find_drop_flow(ofport, flows): count = count + 1 return count # setup self.setup_agent_and_ports( port_dicts=self.create_test_ports()) self.wait_until_ports_state(self.ports, up=True) # call port_delete first for port in self.ports: self.agent.port_delete([], port_id=port['id']) portnames = [port["vif_name"] for port in self.ports] ofports = [port.ofport for port in self.agent.int_br.get_vif_ports() if port.port_name in portnames] #wait until ports are marked dead, with drop flow utils.wait_until_true( lambda: num_ports_with_drop_flows( ofports, self.agent.int_br.dump_flows( constants.LOCAL_SWITCHING )) == len(ofports)) #delete the ports on bridge for port in self.ports: self.agent.int_br.delete_port(port['vif_name']) self.wait_until_ports_state(self.ports, up=False) #verify no stale drop flows self.assertEqual(0, num_ports_with_drop_flows( ofports, self.agent.int_br.dump_flows( constants.LOCAL_SWITCHING ) )) def _check_datapath_type_netdev(self, expected, default=False): if not default: self.config.set_override('datapath_type', expected, "OVS") agent = self.create_agent() self.start_agent(agent) for br_name in (getattr(self, br) for br in ('br_int', 'br_tun', 'br_phys')): actual = self.ovs.db_get_val('Bridge', br_name, 'datapath_type') self.assertEqual(expected, actual) def test_datapath_type_change(self): self._check_datapath_type_netdev('system') self._check_datapath_type_netdev('netdev') def test_datapath_type_netdev(self): self._check_datapath_type_netdev( constants.OVS_DATAPATH_NETDEV) def test_datapath_type_system(self): self._check_datapath_type_netdev( constants.OVS_DATAPATH_SYSTEM) def test_datapath_type_default(self): self._check_datapath_type_netdev( constants.OVS_DATAPATH_SYSTEM, default=True) def test_resync_devices_set_up_after_exception(self): self.setup_agent_and_ports( port_dicts=self.create_test_ports(), trigger_resync=True) self.wait_until_ports_state(self.ports, up=True) def test_reprocess_port_when_ovs_restarts(self): self.setup_agent_and_ports( port_dicts=self.create_test_ports()) self.wait_until_ports_state(self.ports, up=True) self.agent.check_ovs_status.return_value = constants.OVS_RESTARTED # OVS restarted, the agent should reprocess all the ports self.agent.plugin_rpc.update_device_list.reset_mock() self.wait_until_ports_state(self.ports, up=True) def test_resync_dev_up_after_failure(self): self.setup_agent_and_ports( port_dicts=self.create_test_ports(), failed_dev_up=True) # in the RPC mock the first port fails and should # be re-synced expected_ports = self.ports + [self.ports[0]] self.wait_until_ports_state(expected_ports, up=True) def test_resync_dev_down_after_failure(self): self.setup_agent_and_ports( port_dicts=self.create_test_ports(), failed_dev_down=True) self.wait_until_ports_state(self.ports, up=True) for port in self.ports: self.agent.int_br.delete_port(port['vif_name']) # in the RPC mock the first port fails and should # be re-synced expected_ports = self.ports + [self.ports[0]] self.wait_until_ports_state(expected_ports, up=False) def test_ancillary_port_creation_and_deletion(self): external_bridge = self.useFixture( net_helpers.OVSBridgeFixture()).bridge self.setup_agent_and_ports( port_dicts=self.create_test_ports(), ancillary_bridge=external_bridge) self.wait_until_ports_state(self.ports, up=True) for port in self.ports: external_bridge.delete_port(port['vif_name']) self.wait_until_ports_state(self.ports, up=False) def test_resync_ancillary_devices(self): external_bridge = self.useFixture( net_helpers.OVSBridgeFixture()).bridge self.setup_agent_and_ports( port_dicts=self.create_test_ports(), ancillary_bridge=external_bridge, trigger_resync=True) self.wait_until_ports_state(self.ports, up=True) def test_resync_ancillary_dev_up_after_failure(self): external_bridge = self.useFixture( net_helpers.OVSBridgeFixture()).bridge self.setup_agent_and_ports( port_dicts=self.create_test_ports(), ancillary_bridge=external_bridge, failed_dev_up=True) # in the RPC mock the first port fails and should # be re-synced expected_ports = self.ports + [self.ports[0]] self.wait_until_ports_state(expected_ports, up=True) def test_resync_ancillary_dev_down_after_failure(self): external_bridge = self.useFixture( net_helpers.OVSBridgeFixture()).bridge self.setup_agent_and_ports( port_dicts=self.create_test_ports(), ancillary_bridge=external_bridge, failed_dev_down=True) self.wait_until_ports_state(self.ports, up=True) for port in self.ports: external_bridge.delete_port(port['vif_name']) # in the RPC mock the first port fails and should # be re-synced expected_ports = self.ports + [self.ports[0]] self.wait_until_ports_state(expected_ports, up=False) def test_port_vlan_tags(self): self.setup_agent_and_ports( port_dicts=self.create_test_ports(), trigger_resync=True) self.wait_until_ports_state(self.ports, up=True) self.assert_vlan_tags(self.ports, self.agent) def _test_assert_bridges_ports_vxlan(self, local_ip=None): agent = self.create_agent(local_ip=local_ip) self.assertTrue(self.ovs.bridge_exists(self.br_int)) self.assertTrue(self.ovs.bridge_exists(self.br_tun)) self.assert_bridge_ports() self.assert_patch_ports(agent) def test_assert_bridges_ports_vxlan_ipv4(self): self._test_assert_bridges_ports_vxlan() def test_assert_bridges_ports_vxlan_ipv6(self): self._test_assert_bridges_ports_vxlan(local_ip='2001:db8:100::1') def test_assert_bridges_ports_no_tunnel(self): self.create_agent(create_tunnels=False) self.assertTrue(self.ovs.bridge_exists(self.br_int)) self.assertFalse(self.ovs.bridge_exists(self.br_tun)) def test_assert_pings_during_br_int_setup_not_lost(self): self.setup_agent_and_ports(port_dicts=self.create_test_ports(), create_tunnels=False) self.wait_until_ports_state(self.ports, up=True) ips = [port['fixed_ips'][0]['ip_address'] for port in self.ports] with net_helpers.async_ping(self.namespace, ips) as done: while not done(): self.agent.setup_integration_br() time.sleep(0.25) def test_assert_br_int_patch_port_ofports_dont_change(self): # When the integration bridge is setup, it should reuse the existing # patch ports between br-int and br-tun. self.setup_agent_and_ports(port_dicts=[], create_tunnels=True) patch_int_ofport_before = self.agent.patch_int_ofport patch_tun_ofport_before = self.agent.patch_tun_ofport self.stop_agent(self.agent, self.agent_thread) self.setup_agent_and_ports(port_dicts=[], create_tunnels=True) self.assertEqual(patch_int_ofport_before, self.agent.patch_int_ofport) self.assertEqual(patch_tun_ofport_before, self.agent.patch_tun_ofport) def test_assert_br_phys_patch_port_ofports_dont_change(self): # When the integration bridge is setup, it should reuse the existing # patch ports between br-int and br-phys. self.setup_agent_and_ports(port_dicts=[]) patch_int_ofport_before = self.agent.int_ofports['physnet'] patch_phys_ofport_before = self.agent.phys_ofports['physnet'] self.stop_agent(self.agent, self.agent_thread) self.setup_agent_and_ports(port_dicts=[]) self.assertEqual(patch_int_ofport_before, self.agent.int_ofports['physnet']) self.assertEqual(patch_phys_ofport_before, self.agent.phys_ofports['physnet']) def test_assert_pings_during_br_phys_setup_not_lost_in_vlan_to_flat(self): provider_net = self._create_test_network_dict() provider_net['network_type'] = 'flat' self._test_assert_pings_during_br_phys_setup_not_lost(provider_net) def test_assert_pings_during_br_phys_setup_not_lost_in_vlan_to_vlan(self): provider_net = self._create_test_network_dict() provider_net['network_type'] = 'vlan' provider_net['segmentation_id'] = 876 self._test_assert_pings_during_br_phys_setup_not_lost(provider_net) def _test_assert_pings_during_br_phys_setup_not_lost(self, provider_net): # Separate namespace is needed when pinging from one port to another, # otherwise Linux ping uses loopback instead for sending and receiving # ping, hence ignoring flow setup. ns_phys = self.useFixture(net_helpers.NamespaceFixture()).name ports = self.create_test_ports(amount=2) port_int = ports[0] port_phys = ports[1] ip_int = port_int['fixed_ips'][0]['ip_address'] ip_phys = port_phys['fixed_ips'][0]['ip_address'] self.setup_agent_and_ports(port_dicts=[port_int], create_tunnels=False, network=provider_net) self.plug_ports_to_phys_br(provider_net, [port_phys], namespace=ns_phys) # The OVS agent doesn't monitor the physical bridges, no notification # is sent when a port is up on a physical bridge, hence waiting only # for the ports connected to br-int self.wait_until_ports_state([port_int], up=True) # sanity pings before we start net_helpers.assert_ping(ns_phys, ip_int) net_helpers.assert_ping(self.namespace, ip_phys) with net_helpers.async_ping(ns_phys, [ip_int]) as done: while not done(): self.agent.setup_physical_bridges(self.agent.bridge_mappings) time.sleep(0.25) with net_helpers.async_ping(self.namespace, [ip_phys]) as done: while not done(): self.agent.setup_physical_bridges(self.agent.bridge_mappings) time.sleep(0.25) def test_noresync_after_port_gone(self): '''This will test the scenario where a port is removed after listing it but before getting vif info about it. ''' self.ports = self.create_test_ports(amount=2) self.agent = self.create_agent(create_tunnels=False) self.network = self._create_test_network_dict() self._plug_ports(self.network, self.ports, self.agent) self.start_agent(self.agent, ports=self.ports, unplug_ports=[self.ports[1]]) self.wait_until_ports_state([self.ports[0]], up=True) self.assertRaises( utils.WaitTimeout, self.wait_until_ports_state, [self.ports[1]], up=True, timeout=10) def test_ovs_restarted_event(self): callback = mock.Mock() self.setup_agent_and_ports( port_dicts=self.create_test_ports()) registry.subscribe(callback, resources.AGENT, events.OVS_RESTARTED) self.agent.check_ovs_status.return_value = constants.OVS_RESTARTED utils.wait_until_true(lambda: callback.call_count, timeout=10) callback.assert_called_with(resources.AGENT, events.OVS_RESTARTED, mock.ANY) class TestOVSAgentExtensionConfig(base.OVSAgentTestFramework): def setUp(self): super(TestOVSAgentExtensionConfig, self).setUp() self.config.set_override('extensions', ['qos'], 'agent') self.agent = self.create_agent(create_tunnels=False) def test_report_loaded_extension(self): self.agent._report_state() agent_state = self.agent.state_rpc.report_state.call_args[0][1] self.assertEqual(['qos'], agent_state['configurations']['extensions']) neutron-12.1.1/neutron/tests/functional/agent/test_ovs_lib.py0000664000175000017500000007231013553660047024466 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import uuid import mock from neutron_lib import constants as const from oslo_config import cfg from ovsdbapp.backend.ovs_idl import idlutils from neutron.agent.common import ovs_lib from neutron.agent.linux import ip_lib from neutron.common import utils from neutron.plugins.ml2.drivers.openvswitch.agent.common import ( constants as agent_const) from neutron.tests.common.exclusive_resources import port from neutron.tests.common import net_helpers from neutron.tests.functional.agent.linux import base class OVSBridgeTestBase(base.BaseOVSLinuxTestCase): # TODO(twilson) So far, only ovsdb-related tests are written. It would be # good to also add the openflow-related functions def setUp(self): super(OVSBridgeTestBase, self).setUp() self.ovs = ovs_lib.BaseOVS() self.br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge def create_ovs_port(self, *interface_attrs): # Convert ((a, b), (c, d)) to {a: b, c: d} and add 'type' by default attrs = collections.OrderedDict(interface_attrs) attrs.setdefault('type', 'internal') port_name = utils.get_rand_device_name(net_helpers.PORT_PREFIX) return (port_name, self.br.add_port(port_name, *attrs.items())) def create_ovs_vif_port(self, iface_id=None, mac=None, iface_field='iface-id'): if iface_id is None: iface_id = utils.get_rand_name() if mac is None: mac = utils.get_rand_name() attrs = ('external_ids', {iface_field: iface_id, 'attached-mac': mac}) port_name, ofport = self.create_ovs_port(attrs) return ovs_lib.VifPort(port_name, ofport, iface_id, mac, self.br) class OVSBridgeTestCase(OVSBridgeTestBase): def test_port_lifecycle(self): (port_name, ofport) = self.create_ovs_port(('type', 'internal')) # ofport should always be an integer string with value -1 or > 0. self.assertTrue(int(ofport)) self.assertTrue(int(self.br.get_port_ofport(port_name))) self.assertTrue(self.br.port_exists(port_name)) self.assertEqual(self.br.br_name, self.br.get_bridge_for_iface(port_name)) self.br.delete_port(port_name) self.assertFalse(self.br.port_exists(port_name)) def test_duplicate_port_may_exist_false(self): port_name, ofport = self.create_ovs_port(('type', 'internal')) cmd = self.br.ovsdb.add_port(self.br.br_name, port_name, may_exist=False) self.assertRaises(RuntimeError, cmd.execute, check_error=True) def test_delete_port_if_exists_false(self): cmd = self.br.ovsdb.del_port('nonexistantport', if_exists=False) self.assertRaises(RuntimeError, cmd.execute, check_error=True) def test_replace_port(self): port_name = utils.get_rand_device_name(net_helpers.PORT_PREFIX) self.br.replace_port(port_name, ('type', 'internal')) self.assertTrue(self.br.port_exists(port_name)) self.assertEqual('internal', self.br.db_get_val('Interface', port_name, 'type')) self.br.replace_port(port_name, ('type', 'internal'), ('external_ids', {'test': 'test'})) self.assertTrue(self.br.port_exists(port_name)) self.assertEqual('test', self.br.db_get_val('Interface', port_name, 'external_ids')['test']) self.assertEqual(agent_const.DEAD_VLAN_TAG, self.br.db_get_val('Port', port_name, 'tag')) def test_attribute_lifecycle(self): (port_name, ofport) = self.create_ovs_port() tag = 42 self.ovs.set_db_attribute('Port', port_name, 'tag', tag) self.assertEqual(tag, self.ovs.db_get_val('Port', port_name, 'tag')) self.assertEqual(tag, self.br.get_port_tag_dict()[port_name]) self.ovs.clear_db_attribute('Port', port_name, 'tag') self.assertEqual([], self.ovs.db_get_val('Port', port_name, 'tag')) self.assertEqual([], self.br.get_port_tag_dict()[port_name]) def test_attribute_map_handling(self): (pname, ofport) = self.create_ovs_port() expected = {'a': 'b'} self.ovs.set_db_attribute('Port', pname, 'other_config', expected) self.assertEqual(expected, self.ovs.db_get_val('Port', pname, 'other_config')) other = {'c': 'd'} expected.update(other) self.ovs.set_db_attribute('Port', pname, 'other_config', other) self.assertEqual(expected, self.ovs.db_get_val('Port', pname, 'other_config')) other = {'a': 'x'} expected.update(other) self.ovs.set_db_attribute('Port', pname, 'other_config', other) self.assertEqual(expected, self.ovs.db_get_val('Port', pname, 'other_config')) def test_get_bridge_external_bridge_id(self): self.ovs.set_db_attribute('Bridge', self.br.br_name, 'external_ids', {'bridge-id': self.br.br_name}) self.assertEqual( self.br.br_name, self.ovs.get_bridge_external_bridge_id(self.br.br_name)) def test_controller_lifecycle(self): controllers = {'tcp:127.0.0.1:6633', 'tcp:172.17.16.10:55'} self.br.set_controller(controllers) self.assertSetEqual(controllers, set(self.br.get_controller())) self.br.del_controller() self.assertEqual([], self.br.get_controller()) def test_non_index_queries(self): controllers = ['tcp:127.0.0.1:6633'] self.br.set_controller(controllers) cmd = self.br.ovsdb.db_set('Controller', self.br.br_name, ('connection_mode', 'out-of-band')) cmd.execute(check_error=True) self.assertEqual('out-of-band', self.br.db_get_val('Controller', self.br.br_name, 'connection_mode')) def test_set_fail_mode_secure(self): self.br.set_secure_mode() self._assert_br_fail_mode(ovs_lib.FAILMODE_SECURE) def test_set_fail_mode_standalone(self): self.br.set_standalone_mode() self._assert_br_fail_mode(ovs_lib.FAILMODE_STANDALONE) def _assert_br_fail_mode(self, fail_mode): self.assertEqual( self.br.db_get_val('Bridge', self.br.br_name, 'fail_mode'), fail_mode) def test_add_protocols_start_with_one(self): self.br.set_db_attribute('Bridge', self.br.br_name, 'protocols', ['OpenFlow10'], check_error=True) self.br.add_protocols('OpenFlow13') self.assertEqual( self.br.db_get_val('Bridge', self.br.br_name, 'protocols'), ['OpenFlow10', 'OpenFlow13']) def test_add_protocols_start_with_two_add_two(self): self.br.set_db_attribute('Bridge', self.br.br_name, 'protocols', ['OpenFlow10', 'OpenFlow12'], check_error=True) self.br.add_protocols('OpenFlow13', 'OpenFlow14') self.assertEqual( self.br.db_get_val('Bridge', self.br.br_name, 'protocols'), ['OpenFlow10', 'OpenFlow12', 'OpenFlow13', 'OpenFlow14']) def test_add_protocols_add_existing(self): self.br.set_db_attribute('Bridge', self.br.br_name, 'protocols', ['OpenFlow10', 'OpenFlow12', 'OpenFlow13'], check_error=True) self.br.add_protocols('OpenFlow13') self.assertEqual( self.br.db_get_val('Bridge', self.br.br_name, 'protocols'), ['OpenFlow10', 'OpenFlow12', 'OpenFlow13']) def test_get_datapath_id(self): brdev = ip_lib.IPDevice(self.br.br_name) dpid = brdev.link.attributes['link/ether'].replace(':', '') self.br.set_db_attribute('Bridge', self.br.br_name, 'datapath_id', dpid) self.assertIn(dpid, self.br.get_datapath_id()) def _test_add_tunnel_port(self, attrs): port_name = utils.get_rand_device_name(net_helpers.PORT_PREFIX) self.br.add_tunnel_port(port_name, attrs['remote_ip'], attrs['local_ip']) self.assertEqual('gre', self.ovs.db_get_val('Interface', port_name, 'type')) options = self.ovs.db_get_val('Interface', port_name, 'options') for attr, val in attrs.items(): self.assertEqual(val, options[attr]) def test_add_tunnel_port_ipv4(self): attrs = { 'remote_ip': self.get_test_net_address(1), 'local_ip': self.get_test_net_address(2), } self._test_add_tunnel_port(attrs) def test_add_tunnel_port_ipv6(self): attrs = { 'remote_ip': '2001:db8:200::1', 'local_ip': '2001:db8:100::1', } self._test_add_tunnel_port(attrs) def test_add_tunnel_port_custom_port(self): port_name = utils.get_rand_device_name(net_helpers.PORT_PREFIX) self.br.add_tunnel_port( port_name, self.get_test_net_address(1), self.get_test_net_address(2), tunnel_type=const.TYPE_VXLAN, vxlan_udp_port=12345) options = self.ovs.db_get_val('Interface', port_name, 'options') self.assertEqual("12345", options['dst_port']) def test_add_tunnel_port_tos(self): attrs = { 'remote_ip': self.get_test_net_address(1), 'local_ip': self.get_test_net_address(2), 'tos': 'inherit', } port_name = utils.get_rand_device_name(net_helpers.PORT_PREFIX) self.br.add_tunnel_port(port_name, attrs['remote_ip'], attrs['local_ip'], tos=attrs['tos']) self.assertEqual('gre', self.ovs.db_get_val('Interface', port_name, 'type')) options = self.ovs.db_get_val('Interface', port_name, 'options') for attr, val in attrs.items(): self.assertEqual(val, options[attr]) def test_add_patch_port(self): local = utils.get_rand_device_name(net_helpers.PORT_PREFIX) peer = 'remotepeer' self.br.add_patch_port(local, peer) self.assertEqual(self.ovs.db_get_val('Interface', local, 'type'), 'patch') options = self.ovs.db_get_val('Interface', local, 'options') self.assertEqual(peer, options['peer']) def test_get_port_name_list(self): # Note that ovs-vsctl's list-ports does not include the port created # with the same name as the bridge ports = {self.create_ovs_port()[0] for i in range(5)} self.assertSetEqual(ports, set(self.br.get_port_name_list())) def test_get_iface_name_list(self): ifaces = {self.create_ovs_port()[0] for i in range(5)} self.assertSetEqual(ifaces, set(self.br.get_iface_name_list())) def test_get_port_stats(self): # Nothing seems to use this function? (port_name, ofport) = self.create_ovs_port() stats = set(self.br.get_port_stats(port_name).keys()) self.assertTrue(set(['rx_packets', 'tx_packets']).issubset(stats)) def test_get_vif_ports(self): for i in range(2): self.create_ovs_port() vif_ports = [self.create_ovs_vif_port() for i in range(3)] ports = self.br.get_vif_ports() self.assertEqual(3, len(ports)) self.assertTrue(all([isinstance(x, ovs_lib.VifPort) for x in ports])) self.assertEqual(sorted([x.port_name for x in vif_ports]), sorted([x.port_name for x in ports])) def test_get_vif_ports_with_bond(self): for i in range(2): self.create_ovs_port() vif_ports = [self.create_ovs_vif_port() for i in range(3)] # bond ports don't have records in the Interface table but they do in # the Port table orig = self.br.get_port_name_list new_port_name_list = lambda: orig() + ['bondport'] mock.patch.object(self.br, 'get_port_name_list', new=new_port_name_list).start() ports = self.br.get_vif_ports() self.assertEqual(3, len(ports)) self.assertTrue(all([isinstance(x, ovs_lib.VifPort) for x in ports])) self.assertEqual(sorted([x.port_name for x in vif_ports]), sorted([x.port_name for x in ports])) def test_get_vif_port_set(self): for i in range(2): self.create_ovs_port() vif_ports = [self.create_ovs_vif_port() for i in range(2)] ports = self.br.get_vif_port_set() expected = set([x.vif_id for x in vif_ports]) self.assertEqual(expected, ports) def test_get_vif_port_set_with_missing_port(self): self.create_ovs_port() vif_ports = [self.create_ovs_vif_port()] # return an extra port to make sure the db list ignores it orig = self.br.get_port_name_list new_port_name_list = lambda: orig() + ['anotherport'] mock.patch.object(self.br, 'get_port_name_list', new=new_port_name_list).start() ports = self.br.get_vif_port_set() expected = set([vif_ports[0].vif_id]) self.assertEqual(expected, ports) def test_get_vif_port_set_on_empty_bridge_returns_empty_set(self): # Create a port on self.br self.create_ovs_vif_port() # Create another, empty bridge br_2 = self.useFixture(net_helpers.OVSBridgeFixture()).bridge # Assert that get_vif_port_set on an empty bridge returns an empty set, # and does not return the other bridge's ports. self.assertEqual(set(), br_2.get_vif_port_set()) def test_get_ports_attributes(self): port_names = [self.create_ovs_port()[0], self.create_ovs_port()[0]] db_ports = self.br.get_ports_attributes('Interface', columns=['name']) db_ports_names = [p['name'] for p in db_ports] self.assertEqual(sorted(port_names), sorted(db_ports_names)) def test_get_port_tag_dict(self): # Simple case tested in port test_set_get_clear_db_val pass def test_get_vif_port_by_id(self): for i in range(2): self.create_ovs_port() vif_ports = [self.create_ovs_vif_port() for i in range(3)] for vif in vif_ports: self.assertEqual(self.br.get_vif_port_by_id(vif.vif_id).vif_id, vif.vif_id) def test_get_vifs_by_ids(self): for i in range(2): self.create_ovs_port() vif_ports = [self.create_ovs_vif_port() for i in range(3)] by_id = self.br.get_vifs_by_ids([v.vif_id for v in vif_ports]) # convert to str for comparison of VifPorts by_id = {vid: str(vport) for vid, vport in by_id.items()} self.assertEqual({v.vif_id: str(v) for v in vif_ports}, by_id) def test_delete_ports(self): # TODO(twilson) I intensely dislike the current delete_ports function # as the default behavior is really delete_vif_ports(), then it acts # more like a delete_ports() seems like it should if all_ports=True is # passed # Create 2 non-vif ports and 2 vif ports nonvifs = {self.create_ovs_port()[0] for i in range(2)} vifs = {self.create_ovs_vif_port().port_name for i in range(2)} self.assertSetEqual(nonvifs.union(vifs), set(self.br.get_port_name_list())) self.br.delete_ports() self.assertSetEqual(nonvifs, set(self.br.get_port_name_list())) self.br.delete_ports(all_ports=True) self.assertEqual(len(self.br.get_port_name_list()), 0) def test_set_controller_connection_mode(self): controllers = ['tcp:192.0.2.0:6633'] self._set_controllers_connection_mode(controllers) def test_set_multi_controllers_connection_mode(self): controllers = ['tcp:192.0.2.0:6633', 'tcp:192.0.2.1:55'] self._set_controllers_connection_mode(controllers) def _set_controllers_connection_mode(self, controllers): self.br.set_controller(controllers) self.assertEqual(sorted(controllers), sorted(self.br.get_controller())) self.br.set_controllers_connection_mode('out-of-band') self._assert_controllers_connection_mode('out-of-band') self.br.del_controller() self.assertEqual([], self.br.get_controller()) def _assert_controllers_connection_mode(self, connection_mode): controllers = self.br.db_get_val('Bridge', self.br.br_name, 'controller') controllers = [controllers] if isinstance( controllers, uuid.UUID) else controllers for controller in controllers: self.assertEqual(connection_mode, self.br.db_get_val('Controller', controller, 'connection_mode')) def test_egress_bw_limit(self): port_name, _ = self.create_ovs_port() self.br.create_egress_bw_limit_for_port(port_name, 700, 70) max_rate, burst = self.br.get_egress_bw_limit_for_port(port_name) self.assertEqual(700, max_rate) self.assertEqual(70, burst) self.br.delete_egress_bw_limit_for_port(port_name) max_rate, burst = self.br.get_egress_bw_limit_for_port(port_name) self.assertIsNone(max_rate) self.assertIsNone(burst) def test_ingress_bw_limit(self): port_name, _ = self.create_ovs_port() self.br.update_ingress_bw_limit_for_port(port_name, 700, 70) max_rate, burst = self.br.get_ingress_bw_limit_for_port(port_name) self.assertEqual(700, max_rate) self.assertEqual(70, burst) self.br.update_ingress_bw_limit_for_port(port_name, 750, 100) max_rate, burst = self.br.get_ingress_bw_limit_for_port(port_name) self.assertEqual(750, max_rate) self.assertEqual(100, burst) self.br.delete_ingress_bw_limit_for_port(port_name) max_rate, burst = self.br.get_ingress_bw_limit_for_port(port_name) self.assertIsNone(max_rate) self.assertIsNone(burst) def test_ingress_bw_limit_dpdk_port(self): port_name, _ = self.create_ovs_port( ('type', agent_const.OVS_DPDK_VHOST_USER)) self.br.update_ingress_bw_limit_for_port(port_name, 700, 70) max_rate, burst = self.br.get_ingress_bw_limit_for_dpdk_port( port_name) self.assertEqual(700, max_rate) self.assertEqual(70, burst) self.br.update_ingress_bw_limit_for_port(port_name, 750, 100) max_rate, burst = self.br.get_ingress_bw_limit_for_dpdk_port( port_name) self.assertEqual(750, max_rate) self.assertEqual(100, burst) self.br.delete_ingress_bw_limit_for_port(port_name) max_rate, burst = self.br.get_ingress_bw_limit_for_dpdk_port( port_name) self.assertIsNone(max_rate) self.assertIsNone(burst) def test_db_create_references(self): with self.ovs.ovsdb.transaction(check_error=True) as txn: queue = txn.add(self.ovs.ovsdb.db_create("Queue", other_config={'a': '1'})) qos = txn.add(self.ovs.ovsdb.db_create("QoS", queues={0: queue})) txn.add(self.ovs.ovsdb.db_set("Port", self.br.br_name, ('qos', qos))) def cleanup(): with self.ovs.ovsdb.transaction() as t: t.add(self.ovs.ovsdb.db_destroy("QoS", qos.result)) t.add(self.ovs.ovsdb.db_destroy("Queue", queue.result)) t.add(self.ovs.ovsdb.db_clear("Port", self.br.br_name, 'qos')) self.addCleanup(cleanup) val = self.ovs.ovsdb.db_get("Port", self.br.br_name, 'qos').execute() self.assertEqual(qos.result, val) def test_db_add_set(self): protocols = ["OpenFlow10", "OpenFlow11"] self.br.ovsdb.db_add("Bridge", self.br.br_name, "protocols", *protocols).execute(check_error=True) self.assertEqual(protocols, self.br.db_get_val('Bridge', self.br.br_name, "protocols")) def test_db_add_map(self): key = "testdata" data = {key: "testvalue"} self.br.ovsdb.db_add("Bridge", self.br.br_name, "external_ids", data).execute(check_error=True) self.assertEqual(data, self.br.db_get_val('Bridge', self.br.br_name, 'external_ids')) self.br.ovsdb.db_add("Bridge", self.br.br_name, "external_ids", {key: "newdata"}).execute(check_error=True) self.assertEqual(data, self.br.db_get_val('Bridge', self.br.br_name, 'external_ids')) def test_db_add_map_multiple_one_dict(self): data = {"one": "1", "two": "2", "three": "3"} self.br.ovsdb.db_add("Bridge", self.br.br_name, "external_ids", data).execute(check_error=True) self.assertEqual(data, self.br.db_get_val('Bridge', self.br.br_name, 'external_ids')) def test_db_add_map_multiple_dicts(self): data = ({"one": "1"}, {"two": "2"}, {"three": "3"}) self.br.ovsdb.db_add("Bridge", self.br.br_name, "external_ids", *data).execute(check_error=True) combined = {k: v for a in data for k, v in a.items()} self.assertEqual(combined, self.br.db_get_val('Bridge', self.br.br_name, 'external_ids')) def test_db_add_ref(self): ovsdb = self.ovs.ovsdb brname = utils.get_rand_name(prefix=net_helpers.BR_PREFIX) br = ovs_lib.OVSBridge(brname) # doesn't create self.addCleanup(br.destroy) with ovsdb.transaction(check_error=True) as txn: br = txn.add(ovsdb.db_create('Bridge', name=brname)) txn.add(ovsdb.db_add('Open_vSwitch', '.', 'bridges', br)) self.assertIn(brname, self.ovs.get_bridges()) def test_db_add_to_new_object(self): ovsdb = self.ovs.ovsdb brname = utils.get_rand_name(prefix=net_helpers.BR_PREFIX) br = ovs_lib.OVSBridge(brname) # doesn't create self.addCleanup(br.destroy) with ovsdb.transaction(check_error=True) as txn: txn.add(ovsdb.add_br(brname)) txn.add(ovsdb.db_add('Bridge', brname, 'protocols', 'OpenFlow10')) def test_cascading_del_in_txn(self): ovsdb = self.ovs.ovsdb port_name, _ = self.create_ovs_port() def del_port_mod_iface(): with ovsdb.transaction(check_error=True) as txn: txn.add(ovsdb.del_port(port_name, self.br.br_name, if_exists=False)) txn.add(ovsdb.db_set('Interface', port_name, ('type', 'internal'))) # native gives a more specific exception than vsctl self.assertRaises((RuntimeError, idlutils.RowNotFound), del_port_mod_iface) def test_delete_flows_all(self): self.br.add_flow(in_port=1, actions="output:2") self.br.delete_flows(cookie=ovs_lib.COOKIE_ANY) self.assertEqual([], self.br.dump_all_flows()) def test_delete_flows_strict(self): self.br.delete_flows(cookie=ovs_lib.COOKIE_ANY) # remove NORMAL action self.br.add_flow(in_port=1, actions="output:2") self.br.add_flow(in_port=1, priority=100, actions="output:3") self.assertEqual(2, len(self.br.dump_all_flows())) self.br.delete_flows(in_port=1, priority=100, strict=True) self.assertEqual(1, len(self.br.dump_all_flows())) class OVSLibTestCase(base.BaseOVSLinuxTestCase): def setUp(self): super(OVSLibTestCase, self).setUp() self.ovs = ovs_lib.BaseOVS() def test_add_manager_appends(self): port1 = self.useFixture(port.ExclusivePort(const.PROTO_NAME_TCP, start=net_helpers.OVS_MANAGER_TEST_PORT_FIRST, end=net_helpers.OVS_MANAGER_TEST_PORT_LAST)).port port2 = self.useFixture(port.ExclusivePort(const.PROTO_NAME_TCP, start=net_helpers.OVS_MANAGER_TEST_PORT_FIRST, end=net_helpers.OVS_MANAGER_TEST_PORT_LAST)).port manager_list = ["ptcp:%s:127.0.0.1" % port1, "ptcp:%s:127.0.0.1" % port2] # Verify that add_manager does not override the existing manager expected_manager_list = list() for conn_uri in manager_list: self.ovs.add_manager(conn_uri) self.addCleanup(self.ovs.remove_manager, conn_uri) self.assertIn(conn_uri, self.ovs.get_manager()) expected_manager_list.append(conn_uri) # Verify that switch is configured with both the managers for manager_uri in expected_manager_list: self.assertIn(manager_uri, manager_list) def test_add_manager_lifecycle_baseovs(self): port1 = self.useFixture(port.ExclusivePort(const.PROTO_NAME_TCP, start=net_helpers.OVS_MANAGER_TEST_PORT_FIRST, end=net_helpers.OVS_MANAGER_TEST_PORT_LAST)).port conn_uri = "ptcp:%s:127.0.0.1" % port1 self.addCleanup(self.ovs.remove_manager, conn_uri) self.ovs.add_manager(conn_uri) self.assertIn(conn_uri, self.ovs.get_manager()) self.assertEqual(self.ovs.db_get_val('Manager', conn_uri, 'inactivity_probe'), self.ovs.vsctl_timeout * 1000) self.ovs.remove_manager(conn_uri) self.assertNotIn(conn_uri, self.ovs.get_manager()) def test_bridge_lifecycle_baseovs(self): name = utils.get_rand_name(prefix=net_helpers.BR_PREFIX) self.addCleanup(self.ovs.delete_bridge, name) br = self.ovs.add_bridge(name) self.assertEqual(br.br_name, name) self.assertTrue(self.ovs.bridge_exists(name)) self.ovs.delete_bridge(name) self.assertFalse(self.ovs.bridge_exists(name)) def test_get_bridges(self): bridges = { self.useFixture(net_helpers.OVSBridgeFixture()).bridge.br_name for i in range(5)} self.assertTrue(set(self.ovs.get_bridges()).issuperset(bridges)) def test_bridge_lifecycle_ovsbridge(self): name = utils.get_rand_name(prefix=net_helpers.BR_PREFIX) mac_table_size = 12345 cfg.CONF.set_override( 'bridge_mac_table_size', mac_table_size, group='OVS') br = ovs_lib.OVSBridge(name) self.assertEqual(br.br_name, name) # Make sure that instantiating an OVSBridge does not actually create self.assertFalse(self.ovs.bridge_exists(name)) self.addCleanup(self.ovs.delete_bridge, name) br.create() self.assertTrue(self.ovs.bridge_exists(name)) br_other_config = self.ovs.ovsdb.db_find( 'Bridge', ('name', '=', name), columns=['other_config'] ).execute()[0]['other_config'] self.assertEqual(str(mac_table_size), br_other_config['mac-table-size']) br.destroy() self.assertFalse(self.ovs.bridge_exists(name)) def test_db_find_column_type_list(self): """Fixate output for vsctl/native ovsdb_interface. Makes sure that db_find search queries give the same result for both implementations. """ bridge_name = utils.get_rand_name(prefix=net_helpers.BR_PREFIX) self.addCleanup(self.ovs.delete_bridge, bridge_name) br = self.ovs.add_bridge(bridge_name) port_name = utils.get_rand_name(prefix=net_helpers.PORT_PREFIX) br.add_port(port_name) self.ovs.set_db_attribute('Port', port_name, 'tag', 42) # wrap list/find in transaction so we get a single isolated snapshot with self.ovs.ovsdb.transaction(check_error=True) as txn: tags = txn.add(self.ovs.ovsdb.db_list('Port', columns=['tag'])) len_0_list = txn.add(self.ovs.ovsdb.db_find( 'Port', ('tag', '!=', []), columns=['tag'])) single_value = txn.add(self.ovs.ovsdb.db_find( 'Port', ('tag', '=', 42), columns=['tag'])) # Make sure that there is data to query. # It should be, but let's be a little paranoid here as otherwise # the test has no sense tags_present = [t for t in tags.result if t['tag'] != []] self.assertTrue(tags_present) tags_42 = [t for t in tags_present if t['tag'] == 42] self.assertEqual(tags_42, single_value.result) self.assertItemsEqual(len_0_list.result, tags_present) neutron-12.1.1/neutron/tests/functional/agent/l2/0000775000175000017500000000000013553660156021733 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/l2/base.py0000664000175000017500000004335513553660047023230 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # Copyright (c) 2015 SUSE Linux Products GmbH # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import random import eventlet import mock from neutron_lib import constants as n_const from neutron_lib.utils import net from oslo_config import cfg from oslo_utils import uuidutils from neutron.agent.common import ovs_lib from neutron.agent.l2 import l2_agent_extensions_manager as ext_manager from neutron.agent.linux import interface from neutron.agent.linux import polling from neutron.common import utils from neutron.conf.agent import common as agent_config from neutron.conf import common as common_config from neutron.conf.plugins.ml2.drivers import agent from neutron.conf.plugins.ml2.drivers import ovs_conf from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import br_int from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import br_phys from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl \ import br_tun from neutron.plugins.ml2.drivers.openvswitch.agent import ovs_neutron_agent \ as ovs_agent from neutron.tests.common import net_helpers from neutron.tests.functional.agent.linux import base class OVSAgentTestFramework(base.BaseOVSLinuxTestCase): def setUp(self): super(OVSAgentTestFramework, self).setUp() agent_rpc = ('neutron.plugins.ml2.drivers.openvswitch.agent.' 'ovs_neutron_agent.OVSPluginApi') mock.patch(agent_rpc).start() mock.patch('neutron.agent.rpc.PluginReportStateAPI').start() self.br_int = utils.get_rand_name(n_const.DEVICE_NAME_MAX_LEN, prefix='br-int') self.br_tun = utils.get_rand_name(n_const.DEVICE_NAME_MAX_LEN, prefix='br-tun') self.br_phys = utils.get_rand_name(n_const.DEVICE_NAME_MAX_LEN, prefix='br-phys') patch_name_len = n_const.DEVICE_NAME_MAX_LEN - len("-patch-tun") self.patch_tun = "%s-patch-tun" % self.br_int[patch_name_len:] self.patch_int = "%s-patch-int" % self.br_tun[patch_name_len:] self.ovs = ovs_lib.BaseOVS() self.config = self._configure_agent() self.driver = interface.OVSInterfaceDriver(self.config) self.namespace = self.useFixture(net_helpers.NamespaceFixture()).name def _get_config_opts(self): config = cfg.ConfigOpts() config.register_opts(common_config.core_opts) agent.register_agent_opts(config) ovs_conf.register_ovs_agent_opts(config) agent_config.register_interface_opts(config) agent_config.register_interface_driver_opts_helper(config) agent_config.register_agent_state_opts_helper(config) ext_manager.register_opts(config) return config def _configure_agent(self): config = self._get_config_opts() config.set_override( 'interface_driver', 'neutron.agent.linux.interface.OVSInterfaceDriver') config.set_override('integration_bridge', self.br_int, "OVS") config.set_override('ovs_integration_bridge', self.br_int) config.set_override('tunnel_bridge', self.br_tun, "OVS") config.set_override('int_peer_patch_port', self.patch_tun, "OVS") config.set_override('tun_peer_patch_port', self.patch_int, "OVS") config.set_override('host', 'ovs-agent') return config def _bridge_classes(self): return { 'br_int': br_int.OVSIntegrationBridge, 'br_phys': br_phys.OVSPhysicalBridge, 'br_tun': br_tun.OVSTunnelBridge } def create_agent(self, create_tunnels=True, ancillary_bridge=None, local_ip='192.168.10.1'): if create_tunnels: tunnel_types = [n_const.TYPE_VXLAN] else: tunnel_types = None bridge_mappings = ['physnet:%s' % self.br_phys] self.config.set_override('tunnel_types', tunnel_types, "AGENT") self.config.set_override('polling_interval', 1, "AGENT") self.config.set_override('local_ip', local_ip, "OVS") self.config.set_override('bridge_mappings', bridge_mappings, "OVS") # Physical bridges should be created prior to running self._bridge_classes()['br_phys'](self.br_phys).create() ext_mgr = ext_manager.L2AgentExtensionsManager(self.config) agent = ovs_agent.OVSNeutronAgent(self._bridge_classes(), ext_mgr, self.config) self.addCleanup(self.ovs.delete_bridge, self.br_int) if tunnel_types: self.addCleanup(self.ovs.delete_bridge, self.br_tun) self.addCleanup(self.ovs.delete_bridge, self.br_phys) agent.sg_agent = mock.Mock() agent.ancillary_brs = [] if ancillary_bridge: agent.ancillary_brs.append(ancillary_bridge) return agent def _mock_get_events(self, agent, polling_manager, ports): get_events = polling_manager.get_events p_ids = [p['id'] for p in ports] def filter_events(): events = get_events() filtered_ports = [] for dev in events['added']: iface_id = agent.int_br.portid_from_external_ids( dev.get('external_ids', [])) if iface_id in p_ids: # if the event is not about a port that was created by # this test, we filter the event out. Since these tests are # not run in isolation processing all the events might make # some test fail ( e.g. the agent might keep resycing # because it keeps finding not ready ports that are created # by other tests) filtered_ports.append(dev) return {'added': filtered_ports, 'removed': events['removed']} polling_manager.get_events = mock.Mock(side_effect=filter_events) def stop_agent(self, agent, rpc_loop_thread): agent.run_daemon_loop = False rpc_loop_thread.wait() def start_agent(self, agent, ports=None, unplug_ports=None): if unplug_ports is None: unplug_ports = [] if ports is None: ports = [] self.setup_agent_rpc_mocks(agent, unplug_ports) polling_manager = polling.InterfacePollingMinimizer() self._mock_get_events(agent, polling_manager, ports) self.addCleanup(polling_manager.stop) polling_manager.start() utils.wait_until_true( polling_manager._monitor.is_active) agent.check_ovs_status = mock.Mock( return_value=constants.OVS_NORMAL) self.agent_thread = eventlet.spawn(agent.rpc_loop, polling_manager) self.addCleanup(self.stop_agent, agent, self.agent_thread) return polling_manager def _create_test_port_dict(self): return {'id': uuidutils.generate_uuid(), 'mac_address': net.get_random_mac( 'fa:16:3e:00:00:00'.split(':')), 'fixed_ips': [{ 'ip_address': '10.%d.%d.%d' % ( random.randint(3, 254), random.randint(3, 254), random.randint(3, 254))}], 'vif_name': utils.get_rand_name( self.driver.DEV_NAME_LEN, self.driver.DEV_NAME_PREFIX)} def _create_test_network_dict(self): return {'id': uuidutils.generate_uuid(), 'tenant_id': uuidutils.generate_uuid()} def _plug_ports(self, network, ports, agent, bridge=None, namespace=None): if namespace is None: namespace = self.namespace for port in ports: bridge = bridge or agent.int_br self.driver.plug( network.get('id'), port.get('id'), port.get('vif_name'), port.get('mac_address'), bridge.br_name, namespace=namespace) ip_cidrs = ["%s/8" % (port.get('fixed_ips')[0][ 'ip_address'])] self.driver.init_l3(port.get('vif_name'), ip_cidrs, namespace=namespace) def _unplug_ports(self, ports, agent): for port in ports: self.driver.unplug( port.get('vif_name'), agent.int_br.br_name, self.namespace) def _get_device_details(self, port, network): dev = {'device': port['id'], 'port_id': port['id'], 'network_id': network['id'], 'network_type': network.get('network_type', 'vlan'), 'physical_network': network.get('physical_network', 'physnet'), 'segmentation_id': network.get('segmentation_id', 1), 'fixed_ips': port['fixed_ips'], 'device_owner': n_const.DEVICE_OWNER_COMPUTE_PREFIX, 'admin_state_up': True} return dev def assert_bridge(self, br, exists=True): self.assertEqual(exists, self.ovs.bridge_exists(br)) def assert_patch_ports(self, agent): def get_peer(port): return agent.int_br.db_get_val( 'Interface', port, 'options', check_error=True) utils.wait_until_true( lambda: get_peer(self.patch_int) == {'peer': self.patch_tun}) utils.wait_until_true( lambda: get_peer(self.patch_tun) == {'peer': self.patch_int}) def assert_bridge_ports(self): for port in [self.patch_tun, self.patch_int]: self.assertTrue(self.ovs.port_exists(port)) def assert_vlan_tags(self, ports, agent): for port in ports: res = agent.int_br.db_get_val('Port', port.get('vif_name'), 'tag') self.assertTrue(res) def _expected_plugin_rpc_call(self, call, expected_devices, is_up=True): """Helper to check expected rpc call are received :param call: The call to check :param expected_devices: The device for which call is expected :param is_up: True if expected_devices are devices that are set up, False if expected_devices are devices that are set down """ if is_up: rpc_devices = [ dev for args in call.call_args_list for dev in args[0][1]] else: rpc_devices = [ dev for args in call.call_args_list for dev in args[0][2]] for dev in rpc_devices: if dev in expected_devices: expected_devices.remove(dev) # reset mock otherwise if the mock is called again the same call param # will be processed again call.reset_mock() return not expected_devices def create_test_ports(self, amount=3, **kwargs): ports = [] for x in range(amount): ports.append(self._create_test_port_dict(**kwargs)) return ports def _mock_update_device(self, context, devices_up, devices_down, agent_id, host=None, agent_restarted=False): dev_up = [] dev_down = [] for port in self.ports: if devices_up and port['id'] in devices_up: dev_up.append(port['id']) if devices_down and port['id'] in devices_down: dev_down.append({'device': port['id'], 'exists': True}) return {'devices_up': dev_up, 'failed_devices_up': [], 'devices_down': dev_down, 'failed_devices_down': []} def setup_agent_rpc_mocks(self, agent, unplug_ports): def mock_device_details(context, devices, agent_id, host=None): details = [] for port in self.ports: if port['id'] in devices: dev = self._get_device_details( port, self.network) details.append(dev) ports_to_unplug = [x for x in unplug_ports if x['id'] in devices] if ports_to_unplug: self._unplug_ports(ports_to_unplug, self.agent) return {'devices': details, 'failed_devices': []} (agent.plugin_rpc.get_devices_details_list_and_failed_devices. side_effect) = mock_device_details agent.plugin_rpc.update_device_list.side_effect = ( self._mock_update_device) def _prepare_resync_trigger(self, agent): def mock_device_raise_exception(context, devices_up, devices_down, agent_id, host=None): agent.plugin_rpc.update_device_list.side_effect = ( self._mock_update_device) raise Exception('Exception to trigger resync') self.agent.plugin_rpc.update_device_list.side_effect = ( mock_device_raise_exception) def _prepare_failed_dev_up_trigger(self, agent): def mock_failed_devices_up(context, devices_up, devices_down, agent_id, host=None, agent_restarted=False): failed_devices = [] devices = list(devices_up) # first port fails if self.ports[0]['id'] in devices_up: # reassign side_effect so that next RPC call will succeed agent.plugin_rpc.update_device_list.side_effect = ( self._mock_update_device) devices.remove(self.ports[0]['id']) failed_devices.append(self.ports[0]['id']) return {'devices_up': devices, 'failed_devices_up': failed_devices, 'devices_down': [], 'failed_devices_down': []} self.agent.plugin_rpc.update_device_list.side_effect = ( mock_failed_devices_up) def _prepare_failed_dev_down_trigger(self, agent): def mock_failed_devices_down(context, devices_up, devices_down, agent_id, host=None, agent_restarted=False): # first port fails failed_port_id = self.ports[0]['id'] failed_devices_down = [] dev_down = [ {'device': p['id'], 'exists': True} for p in self.ports if p['id'] in devices_down and ( p['id'] != failed_port_id)] # check if it's the call to set devices down and if the device # that is supposed to fail is in the call then modify the # side_effect so that next RPC call will succeed. if devices_down and failed_port_id in devices_down: agent.plugin_rpc.update_device_list.side_effect = ( self._mock_update_device) failed_devices_down.append(failed_port_id) return {'devices_up': devices_up, 'failed_devices_up': [], 'devices_down': dev_down, 'failed_devices_down': failed_devices_down} self.agent.plugin_rpc.update_device_list.side_effect = ( mock_failed_devices_down) def wait_until_ports_state(self, ports, up, timeout=60): port_ids = [p['id'] for p in ports] utils.wait_until_true( lambda: self._expected_plugin_rpc_call( self.agent.plugin_rpc.update_device_list, port_ids, up), timeout=timeout) def setup_agent_and_ports(self, port_dicts, create_tunnels=True, ancillary_bridge=None, trigger_resync=False, failed_dev_up=False, failed_dev_down=False, network=None): self.ports = port_dicts self.agent = self.create_agent(create_tunnels=create_tunnels, ancillary_bridge=ancillary_bridge) self.polling_manager = self.start_agent(self.agent, ports=self.ports) self.network = network or self._create_test_network_dict() if trigger_resync: self._prepare_resync_trigger(self.agent) elif failed_dev_up: self._prepare_failed_dev_up_trigger(self.agent) elif failed_dev_down: self._prepare_failed_dev_down_trigger(self.agent) self._plug_ports(self.network, self.ports, self.agent, bridge=ancillary_bridge) def plug_ports_to_phys_br(self, network, ports, namespace=None): physical_network = network.get('physical_network', 'physnet') phys_segmentation_id = network.get('segmentation_id', None) network_type = network.get('network_type', 'flat') phys_br = self.agent.phys_brs[physical_network] self._plug_ports(network, ports, self.agent, bridge=phys_br, namespace=namespace) if network_type == 'flat': # NOTE(slaweq): for OVS implementations remove the DEAD VLAN tag # on ports that belongs to flat network. DEAD VLAN tag is added # to each newly created port. This is related to lp#1767422 for port in ports: phys_br.clear_db_attribute("Port", port['vif_name'], "tag") elif phys_segmentation_id and network_type == 'vlan': for port in ports: phys_br.set_db_attribute( "Port", port['vif_name'], "tag", phys_segmentation_id) neutron-12.1.1/neutron/tests/functional/agent/l2/extensions/0000775000175000017500000000000013553660156024132 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/l2/extensions/__init__.py0000664000175000017500000000000013553660046026227 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/l2/extensions/test_ovs_agent_qos_extension.py0000664000175000017500000003555213553660047032517 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import mock from neutron_lib import constants from oslo_utils import uuidutils import testscenarios from neutron.api.rpc.callbacks.consumer import registry as consumer_reg from neutron.api.rpc.callbacks import events from neutron.api.rpc.callbacks import resources from neutron.objects.qos import policy from neutron.objects.qos import rule from neutron.tests.common.agents import l2_extensions from neutron.tests.functional.agent.l2 import base from neutron.tests.functional.agent.linux import base as linux_base load_tests = testscenarios.load_tests_apply_scenarios TEST_POLICY_ID1 = "a2d72369-4246-4f19-bd3c-af51ec8d70cd" TEST_POLICY_ID2 = "46ebaec0-0570-43ac-82f6-60d2b03168c5" TEST_DSCP_MARK_1 = 14 TEST_DSCP_MARK_2 = 30 class OVSAgentQoSExtensionTestFramework(base.OVSAgentTestFramework): test_dscp_marking_rule_1 = rule.QosDscpMarkingRule( context=None, qos_policy_id=TEST_POLICY_ID1, id="9f126d84-551a-4dcf-bb01-0e9c0df0c793", dscp_mark=TEST_DSCP_MARK_1) test_dscp_marking_rule_2 = rule.QosDscpMarkingRule( context=None, qos_policy_id=TEST_POLICY_ID2, id="7f126d84-551a-4dcf-bb01-0e9c0df0c793", dscp_mark=TEST_DSCP_MARK_2) test_bw_limit_rule_1 = rule.QosBandwidthLimitRule( context=None, qos_policy_id=TEST_POLICY_ID1, id="5f126d84-551a-4dcf-bb01-0e9c0df0c793", max_kbps=1000, max_burst_kbps=10) test_bw_limit_rule_2 = rule.QosBandwidthLimitRule( context=None, qos_policy_id=TEST_POLICY_ID2, id="fa9128d9-44af-49b2-99bb-96548378ad42", max_kbps=900, max_burst_kbps=9) def setUp(self): super(OVSAgentQoSExtensionTestFramework, self).setUp() self.config.set_override('extensions', ['qos'], 'agent') self._set_pull_mock() self.set_test_qos_rules(TEST_POLICY_ID1, [self.test_bw_limit_rule_1, self.test_dscp_marking_rule_1]) self.set_test_qos_rules(TEST_POLICY_ID2, [self.test_bw_limit_rule_2, self.test_dscp_marking_rule_2]) def _set_pull_mock(self): self.qos_policies = {} def _pull_mock(context, resource_type, resource_id): return self.qos_policies[resource_id] self.pull = mock.patch( 'neutron.api.rpc.handlers.resources_rpc.' 'ResourcesPullRpcApi.pull').start() self.pull.side_effect = _pull_mock def set_test_qos_rules(self, policy_id, policy_rules): """This function sets the policy test rules to be exposed.""" qos_policy = policy.QosPolicy( context=None, project_id=uuidutils.generate_uuid(), id=policy_id, name="Test Policy Name", description="This is a policy for testing purposes", shared=False, rules=policy_rules) qos_policy.obj_reset_changes() self.qos_policies[policy_id] = qos_policy def _create_test_port_dict(self, policy_id=None): port_dict = super(OVSAgentQoSExtensionTestFramework, self)._create_test_port_dict() port_dict['qos_policy_id'] = policy_id port_dict['network_qos_policy_id'] = None return port_dict def _get_device_details(self, port, network): dev = super(OVSAgentQoSExtensionTestFramework, self)._get_device_details(port, network) dev['qos_policy_id'] = port['qos_policy_id'] return dev def _assert_bandwidth_limit_rule_is_set(self, port, rule): if rule.direction == constants.INGRESS_DIRECTION: max_rate, burst = ( self.agent.int_br.get_ingress_bw_limit_for_port( port['vif_name'])) else: max_rate, burst = ( self.agent.int_br.get_egress_bw_limit_for_port( port['vif_name'])) self.assertEqual(max_rate, rule.max_kbps) self.assertEqual(burst, rule.max_burst_kbps) def _assert_bandwidth_limit_rule_not_set(self, port, rule_direction): if rule_direction == constants.INGRESS_DIRECTION: max_rate, burst = ( self.agent.int_br.get_ingress_bw_limit_for_port( port['vif_name'])) else: max_rate, burst = ( self.agent.int_br.get_egress_bw_limit_for_port( port['vif_name'])) self.assertIsNone(max_rate) self.assertIsNone(burst) def wait_until_bandwidth_limit_rule_applied(self, port, rule): if rule and rule.direction == constants.INGRESS_DIRECTION: l2_extensions.wait_until_ingress_bandwidth_limit_rule_applied( self.agent.int_br, port['vif_name'], rule) else: l2_extensions.wait_until_egress_bandwidth_limit_rule_applied( self.agent.int_br, port['vif_name'], rule) def _assert_dscp_marking_rule_is_set(self, port, dscp_rule): port_num = self.agent.int_br._get_port_val(port['vif_name'], 'ofport') flows = self.agent.int_br.dump_flows_for(table='0', in_port=str(port_num)) tos_mark = l2_extensions.extract_mod_nw_tos_action(flows) self.assertEqual(dscp_rule.dscp_mark << 2, tos_mark) def _assert_dscp_marking_rule_not_set(self, port): port_num = self.agent.int_br._get_port_val(port['vif_name'], 'ofport') flows = self.agent.int_br.dump_flows_for(table='0', in_port=str(port_num)) tos_mark = l2_extensions.extract_mod_nw_tos_action(flows) self.assertIsNone(tos_mark) def wait_until_dscp_marking_rule_applied(self, port, dscp_mark): l2_extensions.wait_until_dscp_marking_rule_applied_ovs( self.agent.int_br, port['vif_name'], dscp_mark) def _create_port_with_qos(self): port_dict = self._create_test_port_dict() port_dict['qos_policy_id'] = TEST_POLICY_ID1 self.setup_agent_and_ports([port_dict]) self.wait_until_ports_state(self.ports, up=True) self.wait_until_bandwidth_limit_rule_applied(port_dict, self.test_bw_limit_rule_1) return port_dict class TestOVSAgentQosExtension(OVSAgentQoSExtensionTestFramework): interface_scenarios = linux_base.BaseOVSLinuxTestCase.scenarios direction_scenarios = [ ('ingress', {'direction': constants.INGRESS_DIRECTION}), ('egress', {'direction': constants.EGRESS_DIRECTION}) ] scenarios = testscenarios.multiply_scenarios( interface_scenarios, direction_scenarios) def setUp(self): super(TestOVSAgentQosExtension, self).setUp() self.test_bw_limit_rule_1.direction = self.direction self.test_bw_limit_rule_2.direction = self.direction @property def reverse_direction(self): if self.direction == constants.INGRESS_DIRECTION: return constants.EGRESS_DIRECTION elif self.direction == constants.EGRESS_DIRECTION: return constants.INGRESS_DIRECTION def test_port_creation_with_bandwidth_limit(self): """Make sure bandwidth limit rules are set in low level to ports.""" self.setup_agent_and_ports( port_dicts=self.create_test_ports(amount=1, policy_id=TEST_POLICY_ID1)) self.wait_until_ports_state(self.ports, up=True) for port in self.ports: self._assert_bandwidth_limit_rule_is_set( port, self.test_bw_limit_rule_1) def test_port_creation_with_bandwidth_limits_both_directions(self): """Make sure bandwidth limit rules are set in low level to ports. This test is checking applying rules for both possible directions at once """ reverse_direction_bw_limit_rule = copy.deepcopy( self.test_bw_limit_rule_1) reverse_direction_bw_limit_rule.direction = self.reverse_direction self.qos_policies[TEST_POLICY_ID1].rules.append( reverse_direction_bw_limit_rule) self.setup_agent_and_ports( port_dicts=self.create_test_ports(amount=1, policy_id=TEST_POLICY_ID1)) self.wait_until_ports_state(self.ports, up=True) for port in self.ports: self._assert_bandwidth_limit_rule_is_set( port, self.test_bw_limit_rule_1) self._assert_bandwidth_limit_rule_is_set( port, reverse_direction_bw_limit_rule) def test_port_creation_with_different_bandwidth_limits(self): """Make sure different types of policies end on the right ports.""" port_dicts = self.create_test_ports(amount=3) port_dicts[0]['qos_policy_id'] = TEST_POLICY_ID1 port_dicts[1]['qos_policy_id'] = TEST_POLICY_ID2 self.setup_agent_and_ports(port_dicts) self.wait_until_ports_state(self.ports, up=True) self._assert_bandwidth_limit_rule_is_set(self.ports[0], self.test_bw_limit_rule_1) self._assert_bandwidth_limit_rule_is_set(self.ports[1], self.test_bw_limit_rule_2) self._assert_bandwidth_limit_rule_not_set(self.ports[2], self.direction) def test_port_creation_with_dscp_marking(self): """Make sure dscp marking rules are set in low level to ports.""" self.setup_agent_and_ports( port_dicts=self.create_test_ports(amount=1, policy_id=TEST_POLICY_ID1)) self.wait_until_ports_state(self.ports, up=True) for port in self.ports: self._assert_dscp_marking_rule_is_set( port, self.test_dscp_marking_rule_1) def test_port_creation_with_different_dscp_markings(self): """Make sure different types of policies end on the right ports.""" port_dicts = self.create_test_ports(amount=3) port_dicts[0]['qos_policy_id'] = TEST_POLICY_ID1 port_dicts[1]['qos_policy_id'] = TEST_POLICY_ID2 self.setup_agent_and_ports(port_dicts) self.wait_until_ports_state(self.ports, up=True) self._assert_dscp_marking_rule_is_set(self.ports[0], self.test_dscp_marking_rule_1) self._assert_dscp_marking_rule_is_set(self.ports[1], self.test_dscp_marking_rule_2) self._assert_dscp_marking_rule_not_set(self.ports[2]) def test_simple_port_policy_update(self): self.setup_agent_and_ports( port_dicts=self.create_test_ports(amount=1, policy_id=TEST_POLICY_ID1)) self.wait_until_ports_state(self.ports, up=True) self._assert_dscp_marking_rule_is_set(self.ports[0], self.test_dscp_marking_rule_1) policy_copy = copy.deepcopy(self.qos_policies[TEST_POLICY_ID1]) policy_copy.rules[0].max_kbps = 500 policy_copy.rules[0].max_burst_kbps = 5 policy_copy.rules[1].dscp_mark = TEST_DSCP_MARK_2 context = mock.Mock() consumer_reg.push(context, resources.QOS_POLICY, [policy_copy], events.UPDATED) self.wait_until_bandwidth_limit_rule_applied(self.ports[0], policy_copy.rules[0]) self._assert_bandwidth_limit_rule_is_set(self.ports[0], policy_copy.rules[0]) self._assert_dscp_marking_rule_is_set(self.ports[0], self.test_dscp_marking_rule_2) def test_simple_port_policy_update_change_bw_limit_direction(self): self.setup_agent_and_ports( port_dicts=self.create_test_ports(amount=1, policy_id=TEST_POLICY_ID1)) self.wait_until_ports_state(self.ports, up=True) self._assert_bandwidth_limit_rule_is_set(self.ports[0], self.test_bw_limit_rule_1) self._assert_bandwidth_limit_rule_not_set(self.ports[0], self.reverse_direction) policy_copy = copy.deepcopy(self.qos_policies[TEST_POLICY_ID1]) policy_copy.rules[0].direction = self.reverse_direction context = mock.Mock() consumer_reg.push(context, resources.QOS_POLICY, [policy_copy], events.UPDATED) self.wait_until_bandwidth_limit_rule_applied(self.ports[0], policy_copy.rules[0]) self._assert_bandwidth_limit_rule_not_set(self.ports[0], self.direction) self._assert_bandwidth_limit_rule_is_set(self.ports[0], policy_copy.rules[0]) def test_port_qos_disassociation(self): """Test that qos_policy_id set to None will remove all qos rules from given port. """ port_dict = self._create_port_with_qos() port_dict['qos_policy_id'] = None self.agent.port_update(None, port=port_dict) self.wait_until_bandwidth_limit_rule_applied(port_dict, None) def test_port_qos_update_policy_id(self): """Test that change of qos policy id on given port refreshes all its rules. """ port_dict = self._create_port_with_qos() port_dict['qos_policy_id'] = TEST_POLICY_ID2 self.agent.port_update(None, port=port_dict) self.wait_until_bandwidth_limit_rule_applied(port_dict, self.test_bw_limit_rule_2) def test_policy_rule_delete(self): port_dict = self._create_port_with_qos() policy_copy = copy.deepcopy(self.qos_policies[TEST_POLICY_ID1]) policy_copy.rules = list() context = mock.Mock() consumer_reg.push(context, resources.QOS_POLICY, [policy_copy], events.UPDATED) self.wait_until_bandwidth_limit_rule_applied(port_dict, None) neutron-12.1.1/neutron/tests/functional/agent/l2/__init__.py0000664000175000017500000000000013553660046024030 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/l3/0000775000175000017500000000000013553660156021734 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/l3/test_legacy_router.py0000664000175000017500000004641713553660047026224 0ustar zuulzuul00000000000000# Copyright (c) 2014 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import mock from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as lib_constants from neutron.agent.l3 import namespace_manager from neutron.agent.l3 import namespaces from neutron.agent.linux import ip_lib from neutron.common import utils from neutron.tests.common import machine_fixtures from neutron.tests.common import net_helpers from neutron.tests.functional.agent.l3 import framework class L3AgentTestCase(framework.L3AgentTestFramework): def _test_agent_notifications_for_router_events(self, enable_ha=False): """Test notifications for router create, update, and delete. Make sure that when the agent sends notifications of router events for router create, update, and delete, that the correct handler is called with the right resource, event, and router information. """ event_handler = mock.Mock() registry.subscribe(event_handler, resources.ROUTER, events.BEFORE_CREATE) registry.subscribe(event_handler, resources.ROUTER, events.AFTER_CREATE) registry.subscribe(event_handler, resources.ROUTER, events.BEFORE_UPDATE) registry.subscribe(event_handler, resources.ROUTER, events.AFTER_UPDATE) registry.subscribe(event_handler, resources.ROUTER, events.BEFORE_DELETE) registry.subscribe(event_handler, resources.ROUTER, events.AFTER_DELETE) router_info = self.generate_router_info(enable_ha=enable_ha) router = self.manage_router(self.agent, router_info) with mock.patch.object(self.agent, 'check_ha_state_for_router') as check: self.agent._process_updated_router(router.router) self._delete_router(self.agent, router.router_id) if enable_ha: check.assert_called_once_with(router.router_id, None) expected_calls = [ mock.call('router', 'before_create', self.agent, router=router), mock.call('router', 'after_create', self.agent, router=router), mock.call('router', 'before_update', self.agent, router=router), mock.call('router', 'after_update', self.agent, router=router), mock.call('router', 'before_delete', self.agent, router=router), mock.call('router', 'after_delete', self.agent, router=router)] event_handler.assert_has_calls(expected_calls) def test_agent_notifications_for_router_events(self): self._test_agent_notifications_for_router_events() def test_agent_notifications_for_router_events_ha(self): self._test_agent_notifications_for_router_events(enable_ha=True) def test_legacy_router_update_floatingip_statuses(self): self._test_update_floatingip_statuses( self.generate_router_info(enable_ha=False)) def test_legacy_router_lifecycle(self): self._router_lifecycle(enable_ha=False, dual_stack=True) def test_legacy_router_lifecycle_with_no_gateway_subnet(self): self.agent.conf.set_override('ipv6_gateway', 'fe80::f816:3eff:fe2e:1') self._router_lifecycle(enable_ha=False, dual_stack=True, v6_ext_gw_with_sub=False) def test_legacy_router_gateway_update_to_none(self): router_info = self.generate_router_info(False) router = self.manage_router(self.agent, router_info) gw_port = router.get_ex_gw_port() interface_name = router.get_external_device_name(gw_port['id']) device = ip_lib.IPDevice(interface_name, namespace=router.ns_name) self.assertIn('gateway', device.route.get_gateway()) # Make this copy, so that the agent will think there is change in # external gateway port. router.ex_gw_port = copy.deepcopy(router.ex_gw_port) for subnet in gw_port['subnets']: subnet['gateway_ip'] = None router.process() self.assertIsNone(device.route.get_gateway()) def _make_bridge(self): bridge = framework.get_ovs_bridge(utils.get_rand_name()) bridge.create() self.addCleanup(bridge.destroy) return bridge def test_external_network_bridge_change(self): bridge1, bridge2 = self._make_bridge(), self._make_bridge() self.agent.conf.set_override('external_network_bridge', bridge1.br_name) router_info = self.generate_router_info(False) router = self.manage_router(self.agent, router_info) gw_port = router.router['gw_port'] gw_inf_name = router.get_external_device_name(gw_port['id']) self.assertIn(gw_inf_name, [v.port_name for v in bridge1.get_vif_ports()]) # changeing the external_network_bridge should have no impact since # the interface exists. self.agent.conf.set_override('external_network_bridge', bridge2.br_name) self.manage_router(self.agent, router_info) self.assertIn(gw_inf_name, [v.port_name for v in bridge1.get_vif_ports()]) self.assertNotIn(gw_inf_name, [v.port_name for v in bridge2.get_vif_ports()]) namespaces.Namespace.delete(router.router_namespace) self.manage_router(self.agent, router_info) self.assertIn(gw_inf_name, [v.port_name for v in bridge2.get_vif_ports()]) self.assertNotIn(gw_inf_name, [v.port_name for v in bridge1.get_vif_ports()]) def test_legacy_router_ns_rebuild(self): router_info = self.generate_router_info(False) router = self.manage_router(self.agent, router_info) gw_port = router.router['gw_port'] gw_inf_name = router.get_external_device_name(gw_port['id']) gw_device = ip_lib.IPDevice(gw_inf_name, namespace=router.ns_name) router_ports = [gw_device] for i_port in router_info.get(lib_constants.INTERFACE_KEY, []): interface_name = router.get_internal_device_name(i_port['id']) router_ports.append( ip_lib.IPDevice(interface_name, namespace=router.ns_name)) namespaces.Namespace.delete(router.router_namespace) # l3 agent should be able to rebuild the ns when it is deleted self.manage_router(self.agent, router_info) # Assert the router ports are there in namespace self.assertTrue(all([port.exists() for port in router_ports])) self._delete_router(self.agent, router.router_id) def test_conntrack_disassociate_fip_legacy_router(self): self._test_conntrack_disassociate_fip(ha=False) def _test_periodic_sync_routers_task(self, routers_to_keep, routers_deleted, routers_deleted_during_resync): ns_names_to_retrieve = set() deleted_routers_info = [] for r in routers_to_keep: ri = self.manage_router(self.agent, r) ns_names_to_retrieve.add(ri.ns_name) for r in routers_deleted + routers_deleted_during_resync: ri = self.manage_router(self.agent, r) deleted_routers_info.append(ri) ns_names_to_retrieve.add(ri.ns_name) mocked_get_router_ids = self.mock_plugin_api.get_router_ids mocked_get_router_ids.return_value = [r['id'] for r in routers_to_keep + routers_deleted_during_resync] mocked_get_routers = self.mock_plugin_api.get_routers mocked_get_routers.return_value = (routers_to_keep + routers_deleted_during_resync) # clear agent router_info as it will be after restart self.agent.router_info = {} # Synchronize the agent with the plug-in with mock.patch.object(namespace_manager.NamespaceManager, 'list_all', return_value=ns_names_to_retrieve): self.agent.periodic_sync_routers_task(self.agent.context) # Mock the plugin RPC API so a known external network id is returned # when the router updates are processed by the agent external_network_id = framework._uuid() self.mock_plugin_api.get_external_network_id.return_value = ( external_network_id) # Plug external_gateway_info in the routers that are not going to be # deleted by the agent when it processes the updates. Otherwise, # _process_router_if_compatible in the agent fails for r in routers_to_keep: r['external_gateway_info'] = {'network_id': external_network_id} # while sync updates are still in the queue, higher priority # router_deleted events may be added there as well for r in routers_deleted_during_resync: self.agent.router_deleted(self.agent.context, r['id']) # make sure all events are processed while not self.agent._queue._queue.empty(): self.agent._process_router_update() for r in routers_to_keep: self.assertIn(r['id'], self.agent.router_info) self.assertTrue(self._namespace_exists(namespaces.NS_PREFIX + r['id'])) for ri in deleted_routers_info: self.assertNotIn(ri.router_id, self.agent.router_info) self._assert_router_does_not_exist(ri) def test_periodic_sync_routers_task(self): routers_to_keep = [] for i in range(2): routers_to_keep.append(self.generate_router_info(False)) self._test_periodic_sync_routers_task(routers_to_keep, routers_deleted=[], routers_deleted_during_resync=[]) def test_periodic_sync_routers_task_routers_deleted_while_agent_down(self): routers_to_keep = [] routers_deleted = [] for i in range(2): routers_to_keep.append(self.generate_router_info(False)) for i in range(2): routers_deleted.append(self.generate_router_info(False)) self._test_periodic_sync_routers_task(routers_to_keep, routers_deleted, routers_deleted_during_resync=[]) def test_periodic_sync_routers_task_routers_deleted_while_agent_sync(self): routers_to_keep = [] routers_deleted_during_resync = [] for i in range(2): routers_to_keep.append(self.generate_router_info(False)) for i in range(2): routers_deleted_during_resync.append( self.generate_router_info(False)) self._test_periodic_sync_routers_task( routers_to_keep, routers_deleted=[], routers_deleted_during_resync=routers_deleted_during_resync) def _setup_fip_with_fixed_ip_from_same_subnet(self, enable_snat): """Setup 2 FakeMachines from same subnet, one with floatingip associated. """ router_info = self.generate_router_info(enable_ha=False, enable_snat=enable_snat) router = self.manage_router(self.agent, router_info) router_ip_cidr = self._port_first_ip_cidr(router.internal_ports[0]) router_ip = router_ip_cidr.partition('/')[0] br_int = framework.get_ovs_bridge( self.agent.conf.ovs_integration_bridge) src_machine, dst_machine = self.useFixture( machine_fixtures.PeerMachines( br_int, net_helpers.increment_ip_cidr(router_ip_cidr), router_ip)).machines dst_fip = '19.4.4.10' router.router[lib_constants.FLOATINGIP_KEY] = [] self._add_fip(router, dst_fip, fixed_address=dst_machine.ip) router.process() return src_machine, dst_machine, dst_fip def test_fip_connection_from_same_subnet(self): '''Test connection to floatingip which is associated with fixed_ip on the same subnet of the source fixed_ip. In other words it confirms that return packets surely go through the router. ''' src_machine, dst_machine, dst_fip = ( self._setup_fip_with_fixed_ip_from_same_subnet(enable_snat=True)) protocol_port = net_helpers.get_free_namespace_port( lib_constants.PROTO_NAME_TCP, dst_machine.namespace) # client sends to fip netcat = net_helpers.NetcatTester( src_machine.namespace, dst_machine.namespace, dst_fip, protocol_port, protocol=net_helpers.NetcatTester.TCP) self.addCleanup(netcat.stop_processes) self.assertTrue(netcat.test_connectivity()) def test_ping_floatingip_reply_with_floatingip(self): src_machine, _, dst_fip = ( self._setup_fip_with_fixed_ip_from_same_subnet(enable_snat=False)) # Verify that the ping replys with fip ns_ip_wrapper = ip_lib.IPWrapper(src_machine.namespace) result = ns_ip_wrapper.netns.execute( ['ping', '-c', 1, '-W', 5, dst_fip]) self._assert_ping_reply_from_expected_address(result, dst_fip) def _setup_address_scope(self, internal_address_scope1, internal_address_scope2, gw_address_scope=None): router_info = self.generate_router_info(enable_ha=False, num_internal_ports=2) address_scope1 = { str(lib_constants.IP_VERSION_4): internal_address_scope1} address_scope2 = { str(lib_constants.IP_VERSION_4): internal_address_scope2} if gw_address_scope: router_info['gw_port']['address_scopes'] = { str(lib_constants.IP_VERSION_4): gw_address_scope} router_info[lib_constants.INTERFACE_KEY][0]['address_scopes'] = ( address_scope1) router_info[lib_constants.INTERFACE_KEY][1]['address_scopes'] = ( address_scope2) router = self.manage_router(self.agent, router_info) router_ip_cidr1 = self._port_first_ip_cidr(router.internal_ports[0]) router_ip1 = router_ip_cidr1.partition('/')[0] router_ip_cidr2 = self._port_first_ip_cidr(router.internal_ports[1]) router_ip2 = router_ip_cidr2.partition('/')[0] br_int = framework.get_ovs_bridge( self.agent.conf.ovs_integration_bridge) test_machine1 = self.useFixture( machine_fixtures.FakeMachine( br_int, net_helpers.increment_ip_cidr(router_ip_cidr1), router_ip1)) test_machine2 = self.useFixture( machine_fixtures.FakeMachine( br_int, net_helpers.increment_ip_cidr(router_ip_cidr2), router_ip2)) return test_machine1, test_machine2, router def test_connection_from_same_address_scope(self): test_machine1, test_machine2, _ = self._setup_address_scope( 'scope1', 'scope1') # Internal networks that are in the same address scope can connected # each other net_helpers.assert_ping(test_machine1.namespace, test_machine2.ip) net_helpers.assert_ping(test_machine2.namespace, test_machine1.ip) def test_connection_from_diff_address_scope(self): test_machine1, test_machine2, _ = self._setup_address_scope( 'scope1', 'scope2') # Internal networks that are not in the same address scope should # not reach each other test_machine1.assert_no_ping(test_machine2.ip) test_machine2.assert_no_ping(test_machine1.ip) def test_fip_connection_for_address_scope(self): (machine_same_scope, machine_diff_scope, router) = self._setup_address_scope('scope1', 'scope2', 'scope1') router.router[lib_constants.FLOATINGIP_KEY] = [] fip_same_scope = '19.4.4.10' self._add_fip(router, fip_same_scope, fixed_address=machine_same_scope.ip, fixed_ip_address_scope='scope1') fip_diff_scope = '19.4.4.11' self._add_fip(router, fip_diff_scope, fixed_address=machine_diff_scope.ip, fixed_ip_address_scope='scope2') router.process() br_ex = framework.get_ovs_bridge( self.agent.conf.external_network_bridge) src_machine = self.useFixture( machine_fixtures.FakeMachine(br_ex, '19.4.4.12/24')) # Floating ip should work no matter of address scope net_helpers.assert_ping(src_machine.namespace, fip_same_scope) net_helpers.assert_ping(src_machine.namespace, fip_diff_scope) def test_direct_route_for_address_scope(self): (machine_same_scope, machine_diff_scope, router) = self._setup_address_scope('scope1', 'scope2', 'scope1') gw_port = router.get_ex_gw_port() gw_ip = self._port_first_ip_cidr(gw_port).partition('/')[0] br_ex = framework.get_ovs_bridge( self.agent.conf.external_network_bridge) src_machine = self.useFixture( machine_fixtures.FakeMachine(br_ex, '19.4.4.12/24', gw_ip)) # For the internal networks that are in the same address scope as # external network, they can directly route to external network net_helpers.assert_ping(src_machine.namespace, machine_same_scope.ip) # For the internal networks that are not in the same address scope as # external networks. SNAT will be used. Direct route will not work # here. src_machine.assert_no_ping(machine_diff_scope.ip) def test_connection_from_diff_address_scope_with_fip(self): (machine_same_scope, machine_diff_scope, router) = self._setup_address_scope('scope1', 'scope2', 'scope1') router.router[lib_constants.FLOATINGIP_KEY] = [] fip = '19.4.4.11' self._add_fip(router, fip, fixed_address=machine_diff_scope.ip, fixed_ip_address_scope='scope2') router.process() # For the internal networks that are in the same address scope as # external network, they should be able to reach the floating ip net_helpers.assert_ping(machine_same_scope.namespace, fip) # For the port with fip, it should be able to reach the internal # networks that are in the same address scope as external network net_helpers.assert_ping(machine_diff_scope.namespace, machine_same_scope.ip) neutron-12.1.1/neutron/tests/functional/agent/l3/test_ha_router.py0000664000175000017500000005250313553660047025341 0ustar zuulzuul00000000000000# Copyright (c) 2014 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import mock from neutron_lib import constants import testtools from neutron.agent.l3 import agent as neutron_l3_agent from neutron.agent.linux import ip_lib from neutron.common import ipv6_utils from neutron.common import utils as common_utils from neutron.tests.common import l3_test_common from neutron.tests.common import net_helpers from neutron.tests.functional.agent.l3 import framework class L3HATestCase(framework.L3AgentTestFramework): def test_ha_router_update_floatingip_statuses(self): self._test_update_floatingip_statuses( self.generate_router_info(enable_ha=True)) def test_keepalived_state_change_notification(self): enqueue_mock = mock.patch.object( self.agent, 'enqueue_state_change').start() router_info = self.generate_router_info(enable_ha=True) router = self.manage_router(self.agent, router_info) common_utils.wait_until_true(lambda: router.ha_state == 'master') self.fail_ha_router(router) common_utils.wait_until_true(lambda: router.ha_state == 'backup') common_utils.wait_until_true(lambda: enqueue_mock.call_count == 3) calls = [args[0] for args in enqueue_mock.call_args_list] self.assertEqual((router.router_id, 'backup'), calls[0]) self.assertEqual((router.router_id, 'master'), calls[1]) self.assertEqual((router.router_id, 'backup'), calls[2]) def _expected_rpc_report(self, expected): calls = (args[0][1] for args in self.agent.plugin_rpc.update_ha_routers_states.call_args_list) # Get the last state reported for each router actual_router_states = {} for call in calls: for router_id, state in call.items(): actual_router_states[router_id] = state return actual_router_states == expected def test_keepalived_state_change_bulk_rpc(self): router_info = self.generate_router_info(enable_ha=True) router1 = self.manage_router(self.agent, router_info) self.fail_ha_router(router1) router_info = self.generate_router_info(enable_ha=True) router2 = self.manage_router(self.agent, router_info) common_utils.wait_until_true(lambda: router1.ha_state == 'backup') common_utils.wait_until_true(lambda: router2.ha_state == 'master') common_utils.wait_until_true( lambda: self._expected_rpc_report( {router1.router_id: 'standby', router2.router_id: 'active'})) def test_ha_router_lifecycle(self): router_info = self._router_lifecycle(enable_ha=True) # ensure everything was cleaned up self._router_lifecycle(enable_ha=True, router_info=router_info) def test_conntrack_disassociate_fip_ha_router(self): self._test_conntrack_disassociate_fip(ha=True) def test_ipv6_ha_router_lifecycle(self): self._router_lifecycle(enable_ha=True, ip_version=6) def test_ipv6_ha_router_lifecycle_with_no_gw_subnet(self): self.agent.conf.set_override('ipv6_gateway', 'fe80::f816:3eff:fe2e:1') self._router_lifecycle(enable_ha=True, ip_version=6, v6_ext_gw_with_sub=False) def test_ipv6_ha_router_lifecycle_with_no_gw_subnet_for_router_advts(self): # Verify that router gw interface is configured to receive Router # Advts from upstream router when no external gateway is configured. self._router_lifecycle(enable_ha=True, dual_stack=True, v6_ext_gw_with_sub=False) def _test_ipv6_router_advts_and_fwd_helper(self, state, enable_v6_gw, expected_ra, expected_forwarding): # Schedule router to l3 agent, and then add router gateway. Verify # that router gw interface is configured to receive Router Advts and # IPv6 forwarding is enabled. router_info = l3_test_common.prepare_router_data( enable_snat=True, enable_ha=True, dual_stack=True, enable_gw=False) router = self.manage_router(self.agent, router_info) common_utils.wait_until_true(lambda: router.ha_state == 'master') if state == 'backup': self.fail_ha_router(router) common_utils.wait_until_true(lambda: router.ha_state == 'backup') _ext_dev_name, ex_port = l3_test_common.prepare_ext_gw_test( mock.Mock(), router, dual_stack=enable_v6_gw) router_info['gw_port'] = ex_port router.process() self._assert_ipv6_accept_ra(router, expected_ra) # As router is going first to master and than to backup mode, # ipv6_forwarding should be enabled on "all" interface always after # that transition self._assert_ipv6_forwarding(router, expected_forwarding, True) @testtools.skipUnless(ipv6_utils.is_enabled_and_bind_by_default(), "IPv6 is not enabled") def test_ipv6_router_advts_and_fwd_after_router_state_change_master(self): # Check that RA and forwarding are enabled when there's no IPv6 # gateway. self._test_ipv6_router_advts_and_fwd_helper('master', enable_v6_gw=False, expected_ra=True, expected_forwarding=True) # Check that RA is disabled and forwarding is enabled when an IPv6 # gateway is configured. self._test_ipv6_router_advts_and_fwd_helper('master', enable_v6_gw=True, expected_ra=False, expected_forwarding=True) @testtools.skipUnless(ipv6_utils.is_enabled_and_bind_by_default(), "IPv6 is not enabled") def test_ipv6_router_advts_and_fwd_after_router_state_change_backup(self): # Check that both RA and forwarding are disabled on backup instances self._test_ipv6_router_advts_and_fwd_helper('backup', enable_v6_gw=False, expected_ra=False, expected_forwarding=False) self._test_ipv6_router_advts_and_fwd_helper('backup', enable_v6_gw=True, expected_ra=False, expected_forwarding=False) def test_keepalived_configuration(self): router_info = self.generate_router_info(enable_ha=True) router = self.manage_router(self.agent, router_info) expected = self.get_expected_keepalive_configuration(router) self.assertEqual(expected, router.keepalived_manager.get_conf_on_disk()) # Add a new FIP and change the GW IP address router.router = copy.deepcopy(router.router) existing_fip = '19.4.4.2' new_fip = '19.4.4.3' self._add_fip(router, new_fip) subnet_id = framework._uuid() fixed_ips = [{'ip_address': '19.4.4.10', 'prefixlen': 24, 'subnet_id': subnet_id}] subnets = [{'id': subnet_id, 'cidr': '19.4.4.0/24', 'gateway_ip': '19.4.4.5'}] router.router['gw_port']['subnets'] = subnets router.router['gw_port']['fixed_ips'] = fixed_ips router.process() # Get the updated configuration and assert that both FIPs are in, # and that the GW IP address was updated. new_config = router.keepalived_manager.config.get_config_str() old_gw = '0.0.0.0/0 via 19.4.4.1' new_gw = '0.0.0.0/0 via 19.4.4.5' old_external_device_ip = '19.4.4.4' new_external_device_ip = '19.4.4.10' self.assertIn(existing_fip, new_config) self.assertIn(new_fip, new_config) self.assertNotIn(old_gw, new_config) self.assertIn(new_gw, new_config) external_port = router.get_ex_gw_port() external_device_name = router.get_external_device_name( external_port['id']) self.assertNotIn('%s/24 dev %s' % (old_external_device_ip, external_device_name), new_config) self.assertIn('%s/24 dev %s' % (new_external_device_ip, external_device_name), new_config) def test_ha_router_conf_on_restarted_agent(self): router_info = self.generate_router_info(enable_ha=True) router1 = self.manage_router(self.agent, router_info) self._add_fip(router1, '192.168.111.12') restarted_agent = neutron_l3_agent.L3NATAgentWithStateReport( self.agent.host, self.agent.conf) self.manage_router(restarted_agent, router1.router) common_utils.wait_until_true( lambda: self.floating_ips_configured(router1)) self.assertIn( router1._get_primary_vip(), self._get_addresses_on_device( router1.ns_name, router1.get_ha_device_name())) def test_ha_router_ipv6_radvd_status(self): router_info = self.generate_router_info(ip_version=6, enable_ha=True) router1 = self.manage_router(self.agent, router_info) common_utils.wait_until_true(lambda: router1.ha_state == 'master') common_utils.wait_until_true(lambda: router1.radvd.enabled) def _check_lla_status(router, expected): internal_devices = router.router[constants.INTERFACE_KEY] for device in internal_devices: lladdr = ip_lib.get_ipv6_lladdr(device['mac_address']) exists = ip_lib.device_exists_with_ips_and_mac( router.get_internal_device_name(device['id']), [lladdr], device['mac_address'], router.ns_name) self.assertEqual(expected, exists) _check_lla_status(router1, True) device_name = router1.get_ha_device_name() ha_device = ip_lib.IPDevice(device_name, namespace=router1.ns_name) ha_device.link.set_down() common_utils.wait_until_true(lambda: router1.ha_state == 'backup') common_utils.wait_until_true( lambda: not router1.radvd.enabled, timeout=10) _check_lla_status(router1, False) def test_ha_router_process_ipv6_subnets_to_existing_port(self): router_info = self.generate_router_info(enable_ha=True, ip_version=6) router = self.manage_router(self.agent, router_info) def verify_ip_in_keepalived_config(router, iface): config = router.keepalived_manager.config.get_config_str() ip_cidrs = common_utils.fixed_ip_cidrs(iface['fixed_ips']) for ip_addr in ip_cidrs: self.assertIn(ip_addr, config) interface_id = router.router[constants.INTERFACE_KEY][0]['id'] slaac = constants.IPV6_SLAAC slaac_mode = {'ra_mode': slaac, 'address_mode': slaac} # Add a second IPv6 subnet to the router internal interface. self._add_internal_interface_by_subnet(router.router, count=1, ip_version=6, ipv6_subnet_modes=[slaac_mode], interface_id=interface_id) router.process() common_utils.wait_until_true(lambda: router.ha_state == 'master') # Verify that router internal interface is present and is configured # with IP address from both the subnets. internal_iface = router.router[constants.INTERFACE_KEY][0] self.assertEqual(2, len(internal_iface['fixed_ips'])) self._assert_internal_devices(router) # Verify that keepalived config is properly updated. verify_ip_in_keepalived_config(router, internal_iface) # Remove one subnet from the router internal iface interfaces = copy.deepcopy(router.router.get( constants.INTERFACE_KEY, [])) fixed_ips, subnets = [], [] fixed_ips.append(interfaces[0]['fixed_ips'][0]) subnets.append(interfaces[0]['subnets'][0]) interfaces[0].update({'fixed_ips': fixed_ips, 'subnets': subnets}) router.router[constants.INTERFACE_KEY] = interfaces router.process() # Verify that router internal interface has a single ipaddress internal_iface = router.router[constants.INTERFACE_KEY][0] self.assertEqual(1, len(internal_iface['fixed_ips'])) self._assert_internal_devices(router) # Verify that keepalived config is properly updated. verify_ip_in_keepalived_config(router, internal_iface) def test_delete_external_gateway_on_standby_router(self): router_info = self.generate_router_info(enable_ha=True) router = self.manage_router(self.agent, router_info) self.fail_ha_router(router) common_utils.wait_until_true(lambda: router.ha_state == 'backup') # The purpose of the test is to simply make sure no exception is raised port = router.get_ex_gw_port() interface_name = router.get_external_device_name(port['id']) router.external_gateway_removed(port, interface_name) def test_removing_floatingip_immediately(self): router_info = self.generate_router_info(enable_ha=True) router = self.manage_router(self.agent, router_info) ex_gw_port = router.get_ex_gw_port() interface_name = router.get_external_device_interface_name(ex_gw_port) common_utils.wait_until_true(lambda: router.ha_state == 'master') self._add_fip(router, '172.168.1.20', fixed_address='10.0.0.3') router.process() router.router[constants.FLOATINGIP_KEY] = [] # The purpose of the test is to simply make sure no exception is raised # Because router.process will consume the FloatingIpSetupException, # call the configure_fip_addresses directly here router.configure_fip_addresses(interface_name) def test_ha_port_status_update(self): router_info = self.generate_router_info(enable_ha=True) router_info[constants.HA_INTERFACE_KEY]['status'] = ( constants.PORT_STATUS_DOWN) router1 = self.manage_router(self.agent, router_info) common_utils.wait_until_true(lambda: router1.ha_state == 'backup') router1.router[constants.HA_INTERFACE_KEY]['status'] = ( constants.PORT_STATUS_ACTIVE) self.agent._process_updated_router(router1.router) common_utils.wait_until_true(lambda: router1.ha_state == 'master') def test_ha_router_namespace_has_ip_nonlocal_bind_disabled(self): router_info = self.generate_router_info(enable_ha=True) router = self.manage_router(self.agent, router_info) try: ip_nonlocal_bind_value = ip_lib.get_ip_nonlocal_bind( router.router_namespace.name) except RuntimeError as rte: stat_message = 'cannot stat /proc/sys/net/ipv4/ip_nonlocal_bind' if stat_message in str(rte): raise self.skipException( "This kernel doesn't support %s in network namespaces." % ( ip_lib.IP_NONLOCAL_BIND)) raise self.assertEqual(0, ip_nonlocal_bind_value) @testtools.skipUnless(ipv6_utils.is_enabled_and_bind_by_default(), "IPv6 is not enabled") def test_ha_router_namespace_has_ipv6_forwarding_disabled(self): router_info = self.generate_router_info(enable_ha=True) router_info[constants.HA_INTERFACE_KEY]['status'] = ( constants.PORT_STATUS_DOWN) router = self.manage_router(self.agent, router_info) external_port = router.get_ex_gw_port() external_device_name = router.get_external_device_name( external_port['id']) common_utils.wait_until_true(lambda: router.ha_state == 'backup') self._wait_until_ipv6_forwarding_has_state( router.ns_name, external_device_name, 0) router.router[constants.HA_INTERFACE_KEY]['status'] = ( constants.PORT_STATUS_ACTIVE) self.agent._process_updated_router(router.router) common_utils.wait_until_true(lambda: router.ha_state == 'master') self._wait_until_ipv6_forwarding_has_state( router.ns_name, external_device_name, 1) @testtools.skipUnless(ipv6_utils.is_enabled_and_bind_by_default(), "IPv6 is not enabled") def test_ha_router_without_gw_ipv6_forwarding_state(self): router_info = self.generate_router_info( enable_ha=True, enable_gw=False) router_info[constants.HA_INTERFACE_KEY]['status'] = ( constants.PORT_STATUS_DOWN) router = self.manage_router(self.agent, router_info) common_utils.wait_until_true(lambda: router.ha_state == 'backup') self._wait_until_ipv6_forwarding_has_state(router.ns_name, 'all', 0) router.router[constants.HA_INTERFACE_KEY]['status'] = ( constants.PORT_STATUS_ACTIVE) self.agent._process_updated_router(router.router) common_utils.wait_until_true(lambda: router.ha_state == 'master') self._wait_until_ipv6_forwarding_has_state(router.ns_name, 'all', 1) class L3HATestFailover(framework.L3AgentTestFramework): def setUp(self): super(L3HATestFailover, self).setUp() conf = self._configure_agent('agent2') self.failover_agent = neutron_l3_agent.L3NATAgentWithStateReport( 'agent2', conf) br_int_1 = self._get_agent_ovs_integration_bridge(self.agent) br_int_2 = self._get_agent_ovs_integration_bridge(self.failover_agent) veth1, veth2 = self.useFixture(net_helpers.VethFixture()).ports veth1.link.set_up() veth2.link.set_up() br_int_1.add_port(veth1.name) br_int_2.add_port(veth2.name) def test_ha_router_failover(self): router1, router2 = self.create_ha_routers() master_router, slave_router = self._get_master_and_slave_routers( router1, router2) self._assert_ipv6_accept_ra(master_router, True) self._assert_ipv6_forwarding(master_router, True, True) self._assert_ipv6_accept_ra(slave_router, False) self._assert_ipv6_forwarding(slave_router, False, False) self.fail_ha_router(router1) # NOTE: passing slave_router as first argument, because we expect # that this router should be the master new_master, new_slave = self._get_master_and_slave_routers( slave_router, master_router) self.assertEqual(master_router, new_slave) self.assertEqual(slave_router, new_master) self._assert_ipv6_accept_ra(new_master, True) self._assert_ipv6_forwarding(new_master, True, True) self._assert_ipv6_accept_ra(new_slave, False) # after transition from master -> slave, 'all' IPv6 forwarding should # be enabled self._assert_ipv6_forwarding(new_slave, False, True) def test_ha_router_lost_gw_connection(self): self.agent.conf.set_override( 'ha_vrrp_health_check_interval', 5) self.failover_agent.conf.set_override( 'ha_vrrp_health_check_interval', 5) router1, router2 = self.create_ha_routers() master_router, slave_router = self._get_master_and_slave_routers( router1, router2) self.fail_gw_router_port(master_router) # NOTE: passing slave_router as first argument, because we expect # that this router should be the master new_master, new_slave = self._get_master_and_slave_routers( slave_router, master_router) self.assertEqual(master_router, new_slave) self.assertEqual(slave_router, new_master) def test_both_ha_router_lost_gw_connection(self): self.agent.conf.set_override( 'ha_vrrp_health_check_interval', 5) self.failover_agent.conf.set_override( 'ha_vrrp_health_check_interval', 5) router1, router2 = self.create_ha_routers() master_router, slave_router = self._get_master_and_slave_routers( router1, router2) self.fail_gw_router_port(master_router) self.fail_gw_router_port(slave_router) common_utils.wait_until_true( lambda: master_router.ha_state == 'master') common_utils.wait_until_true( lambda: slave_router.ha_state == 'master') self.restore_gw_router_port(master_router) new_master, new_slave = self._get_master_and_slave_routers( master_router, slave_router) self.assertEqual(master_router, new_master) self.assertEqual(slave_router, new_slave) class LinuxBridgeL3HATestCase(L3HATestCase): INTERFACE_DRIVER = 'neutron.agent.linux.interface.BridgeInterfaceDriver' neutron-12.1.1/neutron/tests/functional/agent/l3/framework.py0000664000175000017500000007337013553660047024314 0ustar zuulzuul00000000000000# Copyright (c) 2014 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import functools import textwrap import mock import netaddr from neutron_lib import constants from oslo_config import cfg from oslo_log import log as logging from oslo_utils import uuidutils import testtools from neutron.agent.common import ovs_lib from neutron.agent.l3 import agent as neutron_l3_agent from neutron.agent.l3 import namespaces from neutron.agent.l3 import router_info as l3_router_info from neutron.agent import l3_agent as l3_agent_main from neutron.agent.linux import external_process from neutron.agent.linux import interface from neutron.agent.linux import ip_lib from neutron.agent.linux import keepalived from neutron.common import constants as n_const from neutron.common import utils as common_utils from neutron.conf.agent import common as agent_config from neutron.conf import common as common_config from neutron.tests.common import l3_test_common from neutron.tests.common import net_helpers from neutron.tests.functional import base _uuid = uuidutils.generate_uuid OVS_INTERFACE_DRIVER = 'neutron.agent.linux.interface.OVSInterfaceDriver' def get_ovs_bridge(br_name): return ovs_lib.OVSBridge(br_name) class L3AgentTestFramework(base.BaseSudoTestCase): INTERFACE_DRIVER = OVS_INTERFACE_DRIVER NESTED_NAMESPACE_SEPARATOR = '@' def setUp(self): super(L3AgentTestFramework, self).setUp() self.mock_plugin_api = mock.patch( 'neutron.agent.l3.agent.L3PluginApi').start().return_value mock.patch('neutron.agent.rpc.PluginReportStateAPI').start() self.conf = self._configure_agent('agent1') self.agent = neutron_l3_agent.L3NATAgentWithStateReport('agent1', self.conf) def _get_config_opts(self): config = cfg.ConfigOpts() config.register_opts(common_config.core_opts) config.register_opts(common_config.core_cli_opts) logging.register_options(config) agent_config.register_process_monitor_opts(config) agent_config.register_root_helper(config) return config def _configure_agent(self, host, agent_mode='dvr_snat'): conf = self._get_config_opts() l3_agent_main.register_opts(conf) conf.set_override('interface_driver', self.INTERFACE_DRIVER) br_int = self.useFixture(net_helpers.OVSBridgeFixture()).bridge br_ex = self.useFixture(net_helpers.OVSBridgeFixture()).bridge conf.set_override('ovs_integration_bridge', br_int.br_name) conf.set_override('external_network_bridge', br_ex.br_name) temp_dir = self.get_new_temp_dir() get_temp_file_path = functools.partial(self.get_temp_file_path, root=temp_dir) conf.set_override('state_path', temp_dir.path) conf.set_override('log_file', get_temp_file_path('log_file')) conf.set_override('metadata_proxy_socket', get_temp_file_path('metadata_proxy')) conf.set_override('ha_confs_path', get_temp_file_path('ha_confs')) conf.set_override('external_pids', get_temp_file_path('external/pids')) conf.set_override('host', host) conf.set_override('agent_mode', agent_mode) return conf def _get_agent_ovs_integration_bridge(self, agent): return get_ovs_bridge(agent.conf.ovs_integration_bridge) def generate_router_info(self, enable_ha, ip_version=4, extra_routes=True, enable_fip=True, enable_snat=True, num_internal_ports=1, dual_stack=False, enable_gw=True, v6_ext_gw_with_sub=True, qos_policy_id=None): if ip_version == 6 and not dual_stack: enable_snat = False enable_fip = False extra_routes = False return l3_test_common.prepare_router_data(ip_version=ip_version, enable_snat=enable_snat, num_internal_ports=( num_internal_ports), enable_floating_ip=enable_fip, enable_ha=enable_ha, extra_routes=extra_routes, dual_stack=dual_stack, enable_gw=enable_gw, v6_ext_gw_with_sub=( v6_ext_gw_with_sub), qos_policy_id=qos_policy_id) def _test_conntrack_disassociate_fip(self, ha): '''Test that conntrack immediately drops stateful connection that uses floating IP once it's disassociated. ''' router_info = self.generate_router_info(enable_ha=ha) router = self.manage_router(self.agent, router_info) port = net_helpers.get_free_namespace_port( constants.PROTO_NAME_TCP, router.ns_name) client_address = '19.4.4.3' server_address = '35.4.0.4' def clean_fips(router): router.router[constants.FLOATINGIP_KEY] = [] clean_fips(router) self._add_fip(router, client_address, fixed_address=server_address) router.process() router_ns = ip_lib.IPWrapper(namespace=router.ns_name) netcat = net_helpers.NetcatTester( router.ns_name, router.ns_name, client_address, port, protocol=net_helpers.NetcatTester.TCP) self.addCleanup(netcat.stop_processes) def assert_num_of_conntrack_rules(n): out = router_ns.netns.execute(["conntrack", "-L", "--orig-src", client_address]) self.assertEqual( n, len([line for line in out.strip().split('\n') if line])) if ha: common_utils.wait_until_true(lambda: router.ha_state == 'master') with self.assert_max_execution_time(100): assert_num_of_conntrack_rules(0) self.assertTrue(netcat.test_connectivity()) assert_num_of_conntrack_rules(1) clean_fips(router) router.process() assert_num_of_conntrack_rules(0) with testtools.ExpectedException(RuntimeError): netcat.test_connectivity() def _test_update_floatingip_statuses(self, router_info): router = self.manage_router(self.agent, router_info) rpc = self.agent.plugin_rpc.update_floatingip_statuses self.assertTrue(rpc.called) # Assert that every defined FIP is updated via RPC expected_fips = set([ (fip['id'], constants.FLOATINGIP_STATUS_ACTIVE) for fip in router.router[constants.FLOATINGIP_KEY]]) call = [args[0] for args in rpc.call_args_list][0] actual_fips = set( [(fip_id, status) for fip_id, status in call[2].items()]) self.assertEqual(expected_fips, actual_fips) def _gateway_check(self, gateway_ip, external_device): expected_gateway = gateway_ip ip_vers = netaddr.IPAddress(expected_gateway).version existing_gateway = (external_device.route.get_gateway( ip_version=ip_vers).get('gateway')) self.assertEqual(expected_gateway, existing_gateway) def _assert_ha_device(self, router): def ha_router_dev_name_getter(not_used): return router.get_ha_device_name() self.assertTrue(self.device_exists_with_ips_and_mac( router.router[constants.HA_INTERFACE_KEY], ha_router_dev_name_getter, router.ns_name)) def _assert_gateway(self, router, v6_ext_gw_with_sub=True): external_port = router.get_ex_gw_port() external_device_name = router.get_external_device_name( external_port['id']) external_device = ip_lib.IPDevice(external_device_name, namespace=router.ns_name) for subnet in external_port['subnets']: self._gateway_check(subnet['gateway_ip'], external_device) if not v6_ext_gw_with_sub: self._gateway_check(self.agent.conf.ipv6_gateway, external_device) def _check_external_device(self, router): external_port = router.get_ex_gw_port() return (self.device_exists_with_ips_and_mac( external_port, router.get_external_device_name, router.ns_name)) def _assert_external_device(self, router): self.assertTrue(self._check_external_device(router)) def _wait_until_ipv6_accept_ra_has_state( self, ns_name, device_name, enabled): ip_wrapper = ip_lib.IPWrapper(namespace=ns_name) def _ipv6_accept_ra_state(): ra_state = ip_wrapper.netns.execute(['sysctl', '-b', 'net.ipv6.conf.%s.accept_ra' % device_name]) return ( enabled == (int(ra_state) != n_const.ACCEPT_RA_DISABLED)) common_utils.wait_until_true(_ipv6_accept_ra_state) def _assert_ipv6_accept_ra(self, router, enabled=True): external_port = router.get_ex_gw_port() external_device_name = router.get_external_device_name( external_port['id']) self._wait_until_ipv6_accept_ra_has_state( router.ns_name, external_device_name, enabled) def _wait_until_ipv6_forwarding_has_state(self, ns_name, dev_name, state): def _ipv6_forwarding_has_state(): return ip_lib.get_ipv6_forwarding( device=dev_name, namespace=ns_name) == state common_utils.wait_until_true(_ipv6_forwarding_has_state) def _assert_ipv6_forwarding(self, router, enabled=True, all_enabled=True): external_port = router.get_ex_gw_port() external_device_name = router.get_external_device_name( external_port['id']) self._wait_until_ipv6_forwarding_has_state( router.ns_name, external_device_name, int(enabled)) self._wait_until_ipv6_forwarding_has_state( router.ns_name, 'all', int(all_enabled)) def _router_lifecycle(self, enable_ha, ip_version=4, dual_stack=False, v6_ext_gw_with_sub=True, router_info=None): router_info = router_info or self.generate_router_info( enable_ha, ip_version, dual_stack=dual_stack, v6_ext_gw_with_sub=(v6_ext_gw_with_sub)) return_copy = copy.deepcopy(router_info) router = self.manage_router(self.agent, router_info) # Add multiple-IPv6-prefix internal router port slaac = constants.IPV6_SLAAC slaac_mode = {'ra_mode': slaac, 'address_mode': slaac} subnet_modes = [slaac_mode] * 2 self._add_internal_interface_by_subnet(router.router, count=2, ip_version=6, ipv6_subnet_modes=subnet_modes) router.process() if enable_ha: common_utils.wait_until_true(lambda: router.ha_state == 'master') # Keepalived notifies of a state transition when it starts, # not when it ends. Thus, we have to wait until keepalived finishes # configuring everything. We verify this by waiting until the last # device has an IP address. device = router.router[constants.INTERFACE_KEY][-1] device_exists = functools.partial( self.device_exists_with_ips_and_mac, device, router.get_internal_device_name, router.ns_name) common_utils.wait_until_true(device_exists) self.assertTrue(self._namespace_exists(router.ns_name)) common_utils.wait_until_true( lambda: self._metadata_proxy_exists(self.agent.conf, router)) self._assert_internal_devices(router) self._assert_external_device(router) if not (enable_ha and (ip_version == 6 or dual_stack)): # Note(SridharG): enable the assert_gateway for IPv6 once # keepalived on Ubuntu14.04 (i.e., check-neutron-dsvm-functional # platform) is updated to 1.2.10 (or above). # For more details: https://review.openstack.org/#/c/151284/ self._assert_gateway(router, v6_ext_gw_with_sub) self.assertTrue(self.floating_ips_configured(router)) self._assert_snat_chains(router) self._assert_floating_ip_chains(router) self._assert_iptables_rules_converged(router) self._assert_extra_routes(router) ip_versions = [4, 6] if (ip_version == 6 or dual_stack) else [4] self._assert_onlink_subnet_routes(router, ip_versions) self._assert_metadata_chains(router) # Verify router gateway interface is configured to receive Router Advts # when IPv6 is enabled and no IPv6 gateway is configured. if router.use_ipv6 and not v6_ext_gw_with_sub: if not self.agent.conf.ipv6_gateway: self._assert_ipv6_accept_ra(router) if enable_ha: self._assert_ha_device(router) common_utils.wait_until_true( lambda: router.keepalived_manager.get_process().active, timeout=15) self._delete_router(self.agent, router.router_id) self._assert_interfaces_deleted_from_ovs() self._assert_router_does_not_exist(router) if enable_ha: common_utils.wait_until_true( lambda: not router.keepalived_manager.get_process().active, timeout=15) return return_copy def manage_router(self, agent, router): self.addCleanup(agent._safe_router_removed, router['id']) # NOTE(mangelajo): Neutron functional for l3 don't rely on openvswitch # agent tagging ports, and all ports remain untagged # during test execution. # Workaround related to lp#1767422 plugs new ports as # dead vlan (4095) to avoid issues, we need to remove # such tag during functional l3 testing. original_plug_new = interface.OVSInterfaceDriver.plug_new def new_ovs_plug(self, *args, **kwargs): original_plug_new(self, *args, **kwargs) bridge = (kwargs.get('bridge') or args[4] or self.conf.ovs_integration_bridge) device_name = kwargs.get('device_name') or args[2] ovsbr = ovs_lib.OVSBridge(bridge) ovsbr.clear_db_attribute('Port', device_name, 'tag') with mock.patch(OVS_INTERFACE_DRIVER + '.plug_new', autospec=True) as ( ovs_plug): ovs_plug.side_effect = new_ovs_plug agent._process_added_router(router) return agent.router_info[router['id']] def _delete_router(self, agent, router_id): agent._router_removed(router_id) def _add_fip(self, router, fip_address, fixed_address='10.0.0.2', host=None, fixed_ip_address_scope=None): fip = {'id': _uuid(), 'port_id': _uuid(), 'floating_ip_address': fip_address, 'fixed_ip_address': fixed_address, 'host': host, 'fixed_ip_address_scope': fixed_ip_address_scope} router.router[constants.FLOATINGIP_KEY].append(fip) def _add_internal_interface_by_subnet(self, router, count=1, ip_version=4, ipv6_subnet_modes=None, interface_id=None): return l3_test_common.router_append_subnet(router, count, ip_version, ipv6_subnet_modes, interface_id) def _namespace_exists(self, namespace): return ip_lib.network_namespace_exists(namespace) def _metadata_proxy_exists(self, conf, router): pm = external_process.ProcessManager( conf, router.router_id, router.ns_name) return pm.active def device_exists_with_ips_and_mac(self, expected_device, name_getter, namespace): ip_cidrs = common_utils.fixed_ip_cidrs(expected_device['fixed_ips']) return ip_lib.device_exists_with_ips_and_mac( name_getter(expected_device['id']), ip_cidrs, expected_device['mac_address'], namespace) @staticmethod def _port_first_ip_cidr(port): fixed_ip = port['fixed_ips'][0] return common_utils.ip_to_cidr(fixed_ip['ip_address'], fixed_ip['prefixlen']) def get_device_mtu(self, target_device, name_getter, namespace): device = ip_lib.IPDevice(name_getter(target_device), namespace) return device.link.mtu def get_expected_keepalive_configuration(self, router): ha_device_name = router.get_ha_device_name() external_port = router.get_ex_gw_port() ex_port_ipv6 = ip_lib.get_ipv6_lladdr(external_port['mac_address']) ex_device_name = router.get_external_device_name( external_port['id']) external_device_cidr = self._port_first_ip_cidr(external_port) internal_port = router.router[constants.INTERFACE_KEY][0] int_port_ipv6 = ip_lib.get_ipv6_lladdr(internal_port['mac_address']) internal_device_name = router.get_internal_device_name( internal_port['id']) internal_device_cidr = self._port_first_ip_cidr(internal_port) floating_ip_cidr = common_utils.ip_to_cidr( router.get_floating_ips()[0]['floating_ip_address']) default_gateway_ip = external_port['subnets'][0].get('gateway_ip') extra_subnet_cidr = external_port['extra_subnets'][0].get('cidr') return textwrap.dedent("""\ global_defs { notification_email_from %(email_from)s router_id %(router_id)s } vrrp_instance VR_1 { state BACKUP interface %(ha_device_name)s virtual_router_id 1 priority 50 garp_master_delay 60 nopreempt advert_int 2 track_interface { %(ha_device_name)s } virtual_ipaddress { 169.254.0.1/24 dev %(ha_device_name)s } virtual_ipaddress_excluded { %(floating_ip_cidr)s dev %(ex_device_name)s %(external_device_cidr)s dev %(ex_device_name)s %(internal_device_cidr)s dev %(internal_device_name)s %(ex_port_ipv6)s dev %(ex_device_name)s scope link %(int_port_ipv6)s dev %(internal_device_name)s scope link } virtual_routes { 0.0.0.0/0 via %(default_gateway_ip)s dev %(ex_device_name)s 8.8.8.0/24 via 19.4.4.4 %(extra_subnet_cidr)s dev %(ex_device_name)s scope link } }""") % { 'email_from': keepalived.KEEPALIVED_EMAIL_FROM, 'router_id': keepalived.KEEPALIVED_ROUTER_ID, 'ha_device_name': ha_device_name, 'ex_device_name': ex_device_name, 'external_device_cidr': external_device_cidr, 'internal_device_name': internal_device_name, 'internal_device_cidr': internal_device_cidr, 'floating_ip_cidr': floating_ip_cidr, 'default_gateway_ip': default_gateway_ip, 'int_port_ipv6': int_port_ipv6, 'ex_port_ipv6': ex_port_ipv6, 'extra_subnet_cidr': extra_subnet_cidr, } def _get_rule(self, iptables_manager, table, chain, predicate): rules = iptables_manager.get_chain(table, chain) result = next(rule for rule in rules if predicate(rule)) return result def _assert_router_does_not_exist(self, router): # If the namespace assertion succeeds # then the devices and iptable rules have also been deleted, # so there's no need to check that explicitly. self.assertFalse(self._namespace_exists(router.ns_name)) common_utils.wait_until_true( lambda: not self._metadata_proxy_exists(self.agent.conf, router)) def _assert_snat_chains(self, router): self.assertFalse(router.iptables_manager.is_chain_empty( 'nat', 'snat')) self.assertFalse(router.iptables_manager.is_chain_empty( 'nat', 'POSTROUTING')) def _assert_floating_ip_chains(self, router, snat_bound_fip=False): if snat_bound_fip: self.assertFalse(router.snat_iptables_manager.is_chain_empty( 'nat', 'float-snat')) self.assertFalse(router.iptables_manager.is_chain_empty( 'nat', 'float-snat')) def _assert_iptables_rules_converged(self, router): # if your code is failing on this line, it means you are not generating # your iptables rules in the same format that iptables-save returns # them. run iptables-save to see the format they should be in self.assertFalse(router.iptables_manager.apply()) def _assert_metadata_chains(self, router): metadata_port_filter = lambda rule: ( str(self.agent.conf.metadata_port) in rule.rule) self.assertTrue(self._get_rule(router.iptables_manager, 'nat', 'PREROUTING', metadata_port_filter)) self.assertTrue(self._get_rule(router.iptables_manager, 'filter', 'INPUT', metadata_port_filter)) def _assert_internal_devices(self, router): internal_devices = router.router[constants.INTERFACE_KEY] self.assertGreater(len(internal_devices), 0) for device in internal_devices: self.assertTrue(self.device_exists_with_ips_and_mac( device, router.get_internal_device_name, router.ns_name)) def _assert_extra_routes(self, router, namespace=None): if namespace is None: namespace = router.ns_name routes = ip_lib.get_routing_table(4, namespace=namespace) routes = [{'nexthop': route['nexthop'], 'destination': route['destination']} for route in routes] for extra_route in router.router['routes']: self.assertIn(extra_route, routes) def _assert_onlink_subnet_routes( self, router, ip_versions, namespace=None): ns_name = namespace or router.ns_name routes = [] for ip_version in ip_versions: _routes = ip_lib.get_routing_table(ip_version, namespace=ns_name) routes.extend(_routes) routes = set(route['destination'] for route in routes) extra_subnets = router.get_ex_gw_port()['extra_subnets'] for extra_subnet in (route['cidr'] for route in extra_subnets): self.assertIn(extra_subnet, routes) def _assert_interfaces_deleted_from_ovs(self): def assert_ovs_bridge_empty(bridge_name): bridge = ovs_lib.OVSBridge(bridge_name) self.assertFalse(bridge.get_port_name_list()) assert_ovs_bridge_empty(self.agent.conf.ovs_integration_bridge) assert_ovs_bridge_empty(self.agent.conf.external_network_bridge) def floating_ips_configured(self, router): floating_ips = router.router[constants.FLOATINGIP_KEY] external_port = router.get_ex_gw_port() return len(floating_ips) and all( ip_lib.device_exists_with_ips_and_mac( router.get_external_device_name(external_port['id']), ['%s/32' % fip['floating_ip_address']], external_port['mac_address'], namespace=router.ns_name) for fip in floating_ips) def _create_router(self, router_info, agent): ns_name = "%s%s%s" % ( 'qrouter-' + router_info['id'], self.NESTED_NAMESPACE_SEPARATOR, agent.host) ext_name = "qg-%s-%s" % (agent.host, _uuid()[-4:]) int_name = "qr-%s-%s" % (agent.host, _uuid()[-4:]) get_ns_name = mock.patch.object( namespaces.RouterNamespace, '_get_ns_name').start() get_ns_name.return_value = ns_name get_ext_name = mock.patch.object(l3_router_info.RouterInfo, 'get_external_device_name').start() get_ext_name.return_value = ext_name get_int_name = mock.patch.object(l3_router_info.RouterInfo, 'get_internal_device_name').start() get_int_name.return_value = int_name router = self.manage_router(agent, router_info) router_ext_name = mock.patch.object(router, 'get_external_device_name').start() router_ext_name.return_value = get_ext_name.return_value router_int_name = mock.patch.object(router, 'get_internal_device_name').start() router_int_name.return_value = get_int_name.return_value return router def create_ha_routers(self): router_info = self.generate_router_info(enable_ha=True) router1 = self._create_router(router_info, self.agent) self._add_fip(router1, '192.168.111.12') r1_br = ip_lib.IPDevice(router1.driver.conf.external_network_bridge) r1_br.addr.add('19.4.4.1/24') r1_br.link.set_up() router_info_2 = copy.deepcopy(router_info) router_info_2[constants.HA_INTERFACE_KEY] = ( l3_test_common.get_ha_interface(ip='169.254.192.2', mac='22:22:22:22:22:22')) router2 = self._create_router(router_info_2, self.failover_agent) r2_br = ip_lib.IPDevice(router2.driver.conf.external_network_bridge) r2_br.addr.add('19.4.4.1/24') r2_br.link.set_up() return (router1, router2) def _get_master_and_slave_routers(self, router1, router2, check_external_device=True): try: common_utils.wait_until_true( lambda: router1.ha_state == 'master') if check_external_device: common_utils.wait_until_true( lambda: self._check_external_device(router1)) master_router = router1 slave_router = router2 except common_utils.WaitTimeout: common_utils.wait_until_true( lambda: router2.ha_state == 'master') if check_external_device: common_utils.wait_until_true( lambda: self._check_external_device(router2)) master_router = router2 slave_router = router1 common_utils.wait_until_true( lambda: master_router.ha_state == 'master') if check_external_device: common_utils.wait_until_true( lambda: self._check_external_device(master_router)) common_utils.wait_until_true( lambda: slave_router.ha_state == 'backup') return master_router, slave_router def fail_ha_router(self, router): device_name = router.get_ha_device_name() ha_device = ip_lib.IPDevice(device_name, router.ha_namespace) ha_device.link.set_down() @staticmethod def fail_gw_router_port(router): r_br = ip_lib.IPDevice(router.driver.conf.external_network_bridge) r_br.link.set_down() @staticmethod def restore_gw_router_port(router): r_br = ip_lib.IPDevice(router.driver.conf.external_network_bridge) r_br.link.set_up() @classmethod def _get_addresses_on_device(cls, namespace, interface): return [address['cidr'] for address in ip_lib.IPDevice(interface, namespace=namespace).addr.list()] def _assert_no_ip_addresses_on_interface(self, namespace, interface): self.assertEqual( [], self._get_addresses_on_device(namespace, interface)) def _assert_ip_addresses_on_interface(self, namespace, interface, ip_addresses): for ip_address in ip_addresses: self._assert_ip_address_on_interface(namespace, interface, ip_address) def _assert_ip_address_not_on_interface( self, namespace, interface, ip_address): self.assertNotIn( ip_address, self._get_addresses_on_device(namespace, interface)) def _assert_ip_address_on_interface(self, namespace, interface, ip_address): self.assertIn( ip_address, self._get_addresses_on_device(namespace, interface)) def _assert_ping_reply_from_expected_address( self, ping_result, expected_address): ping_results = ping_result.split('\n') self.assertGreater( len(ping_results), 1, "The result from ping should be multiple lines") self.assertIn( expected_address, ping_results[1], ("Expect to see %s in the reply of ping, but failed" % expected_address)) neutron-12.1.1/neutron/tests/functional/agent/l3/test_keepalived_state_change.py0000664000175000017500000001434613553660046030171 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import os import eventlet import mock import netaddr from oslo_config import fixture as fixture_config from oslo_utils import uuidutils from neutron.agent.l3 import keepalived_state_change from neutron.agent.linux import ip_lib from neutron.common import utils from neutron.conf.agent.l3 import keepalived as kd from neutron.tests.common import machine_fixtures as mf from neutron.tests.common import net_helpers from neutron.tests.functional import base def has_expected_arp_entry(device_name, namespace, ip, mac): ip_version = utils.get_ip_version(ip) entry = ip_lib.dump_neigh_entries(ip_version, device_name, namespace, dst=ip, lladdr=mac) return entry != [] class TestKeepalivedStateChange(base.BaseSudoTestCase): def setUp(self): super(TestKeepalivedStateChange, self).setUp() self.conf_fixture = self.useFixture(fixture_config.Config()) kd.register_l3_agent_keepalived_opts(self.conf_fixture) self.router_id = uuidutils.generate_uuid() self.conf_dir = self.get_default_temp_dir().path self.cidr = '169.254.128.1/24' self.interface_name = utils.get_rand_name() self.monitor = keepalived_state_change.MonitorDaemon( self.get_temp_file_path('monitor.pid'), self.router_id, 1, 2, utils.get_rand_name(), self.conf_dir, self.interface_name, self.cidr) mock.patch.object(self.monitor, 'notify_agent').start() self.line = '1: %s inet %s' % (self.interface_name, self.cidr) def test_parse_and_handle_event_wrong_device_completes_without_error(self): self.monitor.parse_and_handle_event( '1: wrong_device inet wrong_cidr') def _get_state(self): with open(os.path.join(self.monitor.conf_dir, 'state')) as state_file: return state_file.read() def test_parse_and_handle_event_writes_to_file(self): self.monitor.parse_and_handle_event('Deleted %s' % self.line) self.assertEqual('backup', self._get_state()) self.monitor.parse_and_handle_event(self.line) self.assertEqual('master', self._get_state()) def test_parse_and_handle_event_fails_writing_state(self): with mock.patch.object( self.monitor, 'write_state_change', side_effect=OSError): self.monitor.parse_and_handle_event(self.line) def test_parse_and_handle_event_fails_notifying_agent(self): with mock.patch.object( self.monitor, 'notify_agent', side_effect=Exception): self.monitor.parse_and_handle_event(self.line) def test_handle_initial_state_backup(self): ip = ip_lib.IPWrapper(namespace=self.monitor.namespace) ip.netns.add(self.monitor.namespace) self.addCleanup(ip.netns.delete, self.monitor.namespace) ip.add_dummy(self.interface_name) with mock.patch.object( self.monitor, 'write_state_change') as write_state_change,\ mock.patch.object( self.monitor, 'notify_agent') as notify_agent: self.monitor.handle_initial_state() write_state_change.assert_not_called() notify_agent.assert_not_called() def test_handle_initial_state_master(self): ip = ip_lib.IPWrapper(namespace=self.monitor.namespace) ip.netns.add(self.monitor.namespace) self.addCleanup(ip.netns.delete, self.monitor.namespace) ha_interface = ip.add_dummy(self.interface_name) ha_interface.addr.add(self.cidr) self.monitor.handle_initial_state() self.assertEqual('master', self._get_state()) class TestMonitorDaemon(base.BaseSudoTestCase): def setUp(self): super(TestMonitorDaemon, self).setUp() bridge = self.useFixture(net_helpers.OVSBridgeFixture()).bridge self.machines = self.useFixture(mf.PeerMachines(bridge)) self.router, self.peer = self.machines.machines[:2] conf_dir = self.get_default_temp_dir().path monitor = keepalived_state_change.MonitorDaemon( self.get_temp_file_path('monitor.pid'), uuidutils.generate_uuid(), 1, 2, self.router.namespace, conf_dir, 'foo-iface', self.machines.ip_cidr ) eventlet.spawn_n(monitor.run, run_as_root=True) monitor_started = functools.partial( lambda mon: mon.monitor is not None, monitor) utils.wait_until_true(monitor_started) self.addCleanup(monitor.monitor.stop) def test_new_fip_sends_garp(self): next_ip_cidr = net_helpers.increment_ip_cidr(self.machines.ip_cidr, 2) expected_ip = str(netaddr.IPNetwork(next_ip_cidr).ip) # Create incomplete ARP entry self.peer.assert_no_ping(expected_ip) has_entry = has_expected_arp_entry( self.peer.port.name, self.peer.namespace, expected_ip, self.router.port.link.address) self.assertFalse(has_entry) self.router.port.addr.add(next_ip_cidr) has_arp_entry_predicate = functools.partial( has_expected_arp_entry, self.peer.port.name, self.peer.namespace, expected_ip, self.router.port.link.address, ) exc = RuntimeError( "No ARP entry in %s namespace containing IP address %s and MAC " "address %s" % ( self.peer.namespace, expected_ip, self.router.port.link.address)) utils.wait_until_true( has_arp_entry_predicate, exception=exc) neutron-12.1.1/neutron/tests/functional/agent/l3/extensions/0000775000175000017500000000000013553660156024133 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/l3/extensions/test_fip_qos_extension.py0000664000175000017500000002612113553660047031301 0ustar zuulzuul00000000000000# Copyright 2017 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants from oslo_utils import uuidutils from neutron.agent.l3 import agent as neutron_l3_agent from neutron.agent.l3.extensions import fip_qos from neutron.agent.linux import ip_lib from neutron.common import constants as n_const from neutron.common import exceptions from neutron.common import utils as common_utils from neutron.objects.qos import policy from neutron.objects.qos import rule from neutron.tests.functional.agent.l3 import framework from neutron.tests.functional.agent.l3 import test_dvr_router _uuid = uuidutils.generate_uuid TEST_POLICY_ID1 = _uuid() TEST_POLICY_ID2 = _uuid() TEST_POLICY_ID3 = _uuid() class L3AgentFipQoSExtensionTestFramework(framework.L3AgentTestFramework): test_bw_limit_rule_1 = rule.QosBandwidthLimitRule( context=None, qos_policy_id=TEST_POLICY_ID1, id=_uuid(), max_kbps=111, max_burst_kbps=222, direction=constants.INGRESS_DIRECTION) test_bw_limit_rule_2 = rule.QosBandwidthLimitRule( context=None, qos_policy_id=TEST_POLICY_ID1, id=_uuid(), max_kbps=333, max_burst_kbps=444, direction=constants.EGRESS_DIRECTION) test_bw_limit_rule_3 = rule.QosBandwidthLimitRule( context=None, qos_policy_id=TEST_POLICY_ID2, id=_uuid(), max_kbps=555, max_burst_kbps=666, direction=constants.INGRESS_DIRECTION) test_bw_limit_rule_4 = rule.QosBandwidthLimitRule( context=None, qos_policy_id=TEST_POLICY_ID3, id=_uuid(), max_kbps=777, max_burst_kbps=888, direction=constants.EGRESS_DIRECTION) def setUp(self): super(L3AgentFipQoSExtensionTestFramework, self).setUp() self.conf.set_override('extensions', ['fip_qos'], 'agent') self.agent = neutron_l3_agent.L3NATAgentWithStateReport('agent1', self.conf) self._set_pull_mock() self.set_test_qos_rules(TEST_POLICY_ID1, [self.test_bw_limit_rule_1, self.test_bw_limit_rule_2]) self.set_test_qos_rules(TEST_POLICY_ID2, [self.test_bw_limit_rule_3]) self.set_test_qos_rules(TEST_POLICY_ID3, [self.test_bw_limit_rule_4]) self.fip_qos_ext = fip_qos.FipQosAgentExtension() def _set_pull_mock(self): self.qos_policies = {} def _pull_mock(context, resource_type, resource_id): return self.qos_policies[resource_id] self.pull = mock.patch( 'neutron.api.rpc.handlers.resources_rpc.' 'ResourcesPullRpcApi.pull').start() self.pull.side_effect = _pull_mock def set_test_qos_rules(self, policy_id, policy_rules): """This function sets the policy test rules to be exposed.""" qos_policy = policy.QosPolicy( context=None, project_id=_uuid(), id=policy_id, name="Test Policy Name", description="This is a policy for testing purposes", shared=False, rules=policy_rules) qos_policy.obj_reset_changes() self.qos_policies[policy_id] = qos_policy def _assert_bandwidth_limit_rule_is_set(self, router, ip, rule): device = self.fip_qos_ext._get_rate_limit_ip_device(router) tc_wrapper = self.fip_qos_ext._get_tc_wrapper(device) def get_filter_id(): try: return tc_wrapper.get_filter_id_for_ip(rule.direction, ip) except exceptions.FilterIDForIPNotFound: pass common_utils.wait_until_true(get_filter_id) def _assert_bandwidth_limit_rule_not_set(self, router, ip, rule, dvr_no_external=False): device = self.fip_qos_ext._get_rate_limit_ip_device(router) if dvr_no_external: self.assertIsNone(device) else: tc_wrapper = self.fip_qos_ext._get_tc_wrapper(device) filter_id = tc_wrapper.get_filter_id_for_ip(rule.direction, ip) self.assertIsNone(filter_id) class TestL3AgentFipQosExtension(L3AgentFipQoSExtensionTestFramework): def _test_centralized_routers(self, enable_ha=False, ingress=True, egress=True): qos_policy_id = TEST_POLICY_ID1 if ingress and not egress: qos_policy_id = TEST_POLICY_ID2 elif egress and not ingress: qos_policy_id = TEST_POLICY_ID3 router_info = self.generate_router_info( enable_ha=enable_ha, qos_policy_id=qos_policy_id) ri = self.manage_router(self.agent, router_info) if qos_policy_id == TEST_POLICY_ID1: self._assert_bandwidth_limit_rule_is_set( ri, '19.4.4.2', self.test_bw_limit_rule_1) self._assert_bandwidth_limit_rule_is_set( ri, '19.4.4.2', self.test_bw_limit_rule_2) elif qos_policy_id == TEST_POLICY_ID2: self._assert_bandwidth_limit_rule_is_set( ri, '19.4.4.2', self.test_bw_limit_rule_3) elif qos_policy_id == TEST_POLICY_ID3: self._assert_bandwidth_limit_rule_is_set( ri, '19.4.4.2', self.test_bw_limit_rule_4) def test_legacy_router_fip_qos(self): self._test_centralized_routers() def test_legacy_router_fip_qos_ingress(self): self._test_centralized_routers(ingress=True, egress=False) def test_legacy_router_fip_qos_egress(self): self._test_centralized_routers(ingress=False, egress=True) def test_ha_router_fip_qos(self): self._test_centralized_routers(enable_ha=True) def test_ha_router_fip_qos_ingress(self): self._test_centralized_routers(enable_ha=True, ingress=True, egress=False) def test_ha_router_fip_qos_egress(self): self._test_centralized_routers(enable_ha=True, ingress=False, egress=True) class TestL3AgentFipQosExtensionDVR( test_dvr_router.TestDvrRouter, L3AgentFipQoSExtensionTestFramework): def test_dvr_local_router_no_fip(self): self.agent.conf.agent_mode = constants.L3_AGENT_MODE_DVR router_info = self.generate_dvr_router_info( enable_floating_ip=False) ri = self.manage_router(self.agent, router_info) self._assert_bandwidth_limit_rule_not_set( ri, '19.4.4.2', self.test_bw_limit_rule_1) self._assert_bandwidth_limit_rule_not_set( ri, '19.4.4.2', self.test_bw_limit_rule_2) def _test_dvr_fip_qos(self, enable_ha=False): self.agent.conf.agent_mode = constants.L3_AGENT_MODE_DVR router_info = self.generate_dvr_router_info( enable_ha=enable_ha, enable_gw=True, qos_policy_id=TEST_POLICY_ID1) ri = self.manage_router(self.agent, router_info) self._assert_bandwidth_limit_rule_is_set( ri, '19.4.4.2', self.test_bw_limit_rule_1) self._assert_bandwidth_limit_rule_is_set( ri, '19.4.4.2', self.test_bw_limit_rule_2) def test_dvr_local_router_fip_qos(self): self._test_dvr_fip_qos() def test_ha_dvr_local_router_fip_qos(self): self._test_dvr_fip_qos(enable_ha=True) def _test_agent_mode_dvr_no_external(self, enable_ha=False): self.agent.conf.agent_mode = n_const.L3_AGENT_MODE_DVR_NO_EXTERNAL router_info = self.generate_dvr_router_info( enable_ha=enable_ha, enable_floating_ip=True, enable_centralized_fip=True, enable_snat=True, snat_bound_fip=True, qos_policy_id=TEST_POLICY_ID1) ri = self.manage_router(self.agent, router_info) self._assert_bandwidth_limit_rule_not_set( ri, '19.4.4.2', self.test_bw_limit_rule_1, dvr_no_external=True) self._assert_bandwidth_limit_rule_not_set( ri, '19.4.4.2', self.test_bw_limit_rule_2, dvr_no_external=True) def test_dvr_no_external_no_qos(self): self._test_agent_mode_dvr_no_external() def test_ha_dvr_no_external_no_qos(self): self._test_agent_mode_dvr_no_external(enable_ha=True) def _test_dvr_fip_snat_bound_agent_mode_dvr_snat(self, enable_ha=False): self.agent.conf.agent_mode = constants.L3_AGENT_MODE_DVR_SNAT router_info = self.generate_dvr_router_info( enable_ha=enable_ha, snat_bound_fip=True, enable_gw=True, qos_policy_id=TEST_POLICY_ID1) ri = self.manage_router(self.agent, router_info) self._assert_bandwidth_limit_rule_is_set( ri, '19.4.4.2', self.test_bw_limit_rule_1) self._assert_bandwidth_limit_rule_is_set( ri, '19.4.4.2', self.test_bw_limit_rule_2) def test_dvr_dvr_fip_snat_qos(self): self._test_dvr_fip_snat_bound_agent_mode_dvr_snat() def test_ha_dvr_dvr_fip_snat_qos(self): self._test_dvr_fip_snat_bound_agent_mode_dvr_snat(enable_ha=True) def _assert_dvr_snat_qrouter_ns_rule_is_set(self, device, ip, rule): tc_wrapper = self.fip_qos_ext._get_tc_wrapper(device) def get_filter_id(): try: return tc_wrapper.get_filter_id_for_ip(rule.direction, ip) except exceptions.FilterIDForIPNotFound: pass common_utils.wait_until_true(get_filter_id) def test_dvr_snat_qos_rules_set_in_qrouter(self): self.agent.conf.agent_mode = constants.L3_AGENT_MODE_DVR_SNAT router_info = self.generate_dvr_router_info( enable_snat=True, enable_gw=True, enable_floating_ip=True, qos_policy_id=TEST_POLICY_ID1) ri = self.manage_router(self.agent, router_info) gw_port = ri.get_ex_gw_port() rfp_dev_name = ri.get_external_device_interface_name(gw_port) if ri.router_namespace.exists(): dvr_fip_device = ip_lib.IPDevice( rfp_dev_name, namespace=ri.ns_name) self._assert_dvr_snat_qrouter_ns_rule_is_set( dvr_fip_device, '19.4.4.2', self.test_bw_limit_rule_1) self._assert_dvr_snat_qrouter_ns_rule_is_set( dvr_fip_device, '19.4.4.2', self.test_bw_limit_rule_2) class LinuxBridgeL3AgentFipQosExtensionTestCase(TestL3AgentFipQosExtension): INTERFACE_DRIVER = 'neutron.agent.linux.interface.BridgeInterfaceDriver' neutron-12.1.1/neutron/tests/functional/agent/l3/extensions/__init__.py0000664000175000017500000000000013553660046026230 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/l3/test_dvr_router.py0000664000175000017500000031323513553660047025546 0ustar zuulzuul00000000000000# Copyright (c) 2014 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import functools import mock import netaddr from neutron_lib.api.definitions import portbindings from neutron_lib import constants as lib_constants import six import testtools from neutron.agent.l3 import agent as neutron_l3_agent from neutron.agent.l3 import dvr_edge_ha_router as dvr_ha_router from neutron.agent.l3 import dvr_edge_router from neutron.agent.l3 import dvr_fip_ns from neutron.agent.l3 import dvr_local_router from neutron.agent.l3 import dvr_snat_ns from neutron.agent.l3 import namespaces from neutron.agent.linux import ip_lib from neutron.agent.linux import iptables_manager from neutron.common import constants as n_const from neutron.common import exceptions as n_exc from neutron.common import utils from neutron.tests.common import l3_test_common from neutron.tests.common import machine_fixtures from neutron.tests.common import net_helpers from neutron.tests.functional.agent.l3 import framework DEVICE_OWNER_COMPUTE = lib_constants.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' class TestDvrRouter(framework.L3AgentTestFramework): def manage_router(self, agent, router): def _safe_fipnamespace_delete_on_ext_net(ext_net_id): try: agent.fipnamespace_delete_on_ext_net(None, ext_net_id) except RuntimeError: pass if router['gw_port']: self.addCleanup( _safe_fipnamespace_delete_on_ext_net, router['gw_port']['network_id']) return super(TestDvrRouter, self).manage_router(agent, router) def test_dvr_update_floatingip_statuses(self): self.agent.conf.agent_mode = 'dvr' self._test_update_floatingip_statuses(self.generate_dvr_router_info()) def test_dvr_router_lifecycle_without_ha_without_snat_with_fips(self): self._dvr_router_lifecycle(enable_ha=False, enable_snat=False) def test_dvr_router_lifecycle_without_ha_with_snat_with_fips(self): self._dvr_router_lifecycle(enable_ha=False, enable_snat=True) def test_dvr_router_lifecycle_ha_with_snat_with_fips(self): self._dvr_router_lifecycle(enable_ha=True, enable_snat=True) def test_dvr_lifecycle_no_ha_with_snat_with_fips_with_cent_fips(self): self._dvr_router_lifecycle(enable_ha=False, enable_snat=True, snat_bound_fip=True) def test_dvr_lifecycle_ha_with_snat_with_fips_with_cent_fips(self): self._dvr_router_lifecycle(enable_ha=True, enable_snat=True, snat_bound_fip=True) def _helper_create_dvr_router_fips_for_ext_network( self, agent_mode, **dvr_router_kwargs): self.agent.conf.agent_mode = agent_mode router_info = self.generate_dvr_router_info(**dvr_router_kwargs) self.mock_plugin_api.get_external_network_id.return_value = ( router_info['_floatingips'][0]['floating_network_id']) router = self.manage_router(self.agent, router_info) fip_ns = router.fip_ns.get_name() return router, fip_ns def _validate_fips_for_external_network(self, router, fip_ns): self.assertTrue(self._namespace_exists(router.ns_name)) self.assertTrue(self._namespace_exists(fip_ns)) self._assert_dvr_floating_ips(router) self._assert_snat_namespace_does_not_exist(router) def test_dvr_gateway_move_does_not_remove_redirect_rules(self): """Test to validate snat redirect rules not cleared with snat move.""" self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info(enable_snat=True) router1 = self.manage_router(self.agent, router_info) router1.router['gw_port_host'] = "" self.agent._process_updated_router(router1.router) router_updated = self.agent.router_info[router1.router['id']] self.assertTrue(self._namespace_exists(router_updated.ns_name)) ns_ipr = ip_lib.IPRule(namespace=router1.ns_name) ip4_rules_list = ns_ipr.rule.list_rules(lib_constants.IP_VERSION_4) self.assertEqual(6, len(ip4_rules_list)) # IPRule list should have 6 entries. # Three entries from 'default', 'main' and 'local' table. # One rule for the floatingip. # The remaining 2 is for the two router interfaces(csnat ports). default_rules_list_count = 0 interface_rules_list_count = 0 for ip_rule in ip4_rules_list: tbl_index = ip_rule['table'] if tbl_index in ['local', 'default', 'main', str(dvr_fip_ns.FIP_RT_TBL)]: default_rules_list_count = default_rules_list_count + 1 else: interface_rules_list_count = interface_rules_list_count + 1 self.assertEqual(4, default_rules_list_count) self.assertEqual(2, interface_rules_list_count) def test_dvr_update_gateway_port_no_fip_fg_port_recovers_itself_with_fpr( self): self.agent.conf.agent_mode = 'dvr' # Create the router with external net router_info = self.generate_dvr_router_info() external_gw_port = router_info['gw_port'] ext_net_id = router_info['_floatingips'][0]['floating_network_id'] self.mock_plugin_api.get_external_network_id.return_value = ext_net_id router = self.manage_router(self.agent, router_info) fg_port = router.fip_ns.agent_gateway_port fg_port_name = router.fip_ns.get_ext_device_name(fg_port['id']) fg_device = ip_lib.IPDevice(fg_port_name, namespace=router.fip_ns.name) fip_2_rtr_name = router.fip_ns.get_int_device_name(router.router_id) fpr_device = ip_lib.IPDevice(fip_2_rtr_name, namespace=router.fip_ns.name) # Now validate if the gateway is properly configured. rtr_2_fip, fip_2_rtr = router.rtr_fip_subnet.get_pair() tbl_index = router._get_snat_idx(fip_2_rtr) tbl_filter = ['table', tbl_index] self.assertIn('gateway', fg_device.route.get_gateway( filters=tbl_filter)) self._validate_fips_for_external_network( router, router.fip_ns.get_name()) # Now delete the fg- port that was created ext_net_bridge = self.agent.conf.external_network_bridge router.fip_ns.driver.unplug(fg_port_name, bridge=ext_net_bridge, namespace=router.fip_ns.name, prefix=dvr_fip_ns.FIP_EXT_DEV_PREFIX) # Now check if the fg- port is missing. self.assertFalse(fg_device.exists()) fpr_device.link.set_down() # Now change the gateway ip for the router and do an update. router.ex_gw_port = copy.deepcopy(router.ex_gw_port) new_fg_port = copy.deepcopy(fg_port) for subnet in new_fg_port['subnets']: subnet['gateway_ip'] = '19.4.4.2' router.router[n_const.FLOATINGIP_AGENT_INTF_KEY] = [new_fg_port] self.assertRaises(n_exc.FloatingIpSetupException, self.agent._process_updated_router, router.router) self.agent._process_updated_router(router.router) self.assertTrue(fg_device.exists()) self.assertTrue(fpr_device.exists()) updated_route = fg_device.route.list_routes( ip_version=lib_constants.IP_VERSION_4, table=tbl_index) expected_route = [{'cidr': '0.0.0.0/0', 'dev': fg_port_name, 'table': tbl_index, u'via': u'19.4.4.2'}] self.assertEqual(expected_route, updated_route) self._validate_fips_for_external_network( router, router.fip_ns.get_name()) self._delete_router(self.agent, router.router_id) self._assert_fip_namespace_deleted(external_gw_port) def test_dvr_update_gateway_port_with_no_gw_port_in_namespace(self): self.agent.conf.agent_mode = 'dvr' # Create the router with external net router_info = self.generate_dvr_router_info() external_gw_port = router_info['gw_port'] ext_net_id = router_info['_floatingips'][0]['floating_network_id'] self.mock_plugin_api.get_external_network_id.return_value = ext_net_id router = self.manage_router(self.agent, router_info) fg_port = router.fip_ns.agent_gateway_port fg_port_name = router.fip_ns.get_ext_device_name(fg_port['id']) fg_device = ip_lib.IPDevice(fg_port_name, namespace=router.fip_ns.name) # Now validate if the gateway is properly configured. rtr_2_fip, fip_2_rtr = router.rtr_fip_subnet.get_pair() tbl_index = router._get_snat_idx(fip_2_rtr) tbl_filter = ['table', tbl_index] self.assertIn('gateway', fg_device.route.get_gateway( filters=tbl_filter)) self._validate_fips_for_external_network( router, router.fip_ns.get_name()) # Now delete the fg- port that was created ext_net_bridge = self.agent.conf.external_network_bridge router.fip_ns.driver.unplug(fg_port_name, bridge=ext_net_bridge, namespace=router.fip_ns.name, prefix=dvr_fip_ns.FIP_EXT_DEV_PREFIX) # Now check if the fg- port is missing. self.assertFalse(fg_device.exists()) # Now change the gateway ip for the router and do an update. router.ex_gw_port = copy.deepcopy(router.ex_gw_port) new_fg_port = copy.deepcopy(fg_port) for subnet in new_fg_port['subnets']: subnet['gateway_ip'] = '19.4.4.2' router.router[n_const.FLOATINGIP_AGENT_INTF_KEY] = [new_fg_port] self.assertRaises(n_exc.FloatingIpSetupException, self.manage_router, self.agent, router.router) router = self.manage_router(self.agent, router.router) self.assertTrue(fg_device.exists()) updated_route = fg_device.route.list_routes( ip_version=lib_constants.IP_VERSION_4, table=tbl_index) expected_route = [{'cidr': '0.0.0.0/0', 'dev': fg_port_name, 'table': tbl_index, u'via': u'19.4.4.2'}] self.assertEqual(expected_route, updated_route) self._validate_fips_for_external_network( router, router.fip_ns.get_name()) self._delete_router(self.agent, router.router_id) self._assert_fip_namespace_deleted(external_gw_port) @mock.patch.object(dvr_fip_ns.FipNamespace, 'subscribe') def test_dvr_process_fips_with_no_gw_port_in_namespace( self, fip_subscribe): self.agent.conf.agent_mode = 'dvr' # Create the router with external net router_info = self.generate_dvr_router_info() external_gw_port = router_info['gw_port'] ext_net_id = router_info['_floatingips'][0]['floating_network_id'] self.mock_plugin_api.get_external_network_id.return_value = ext_net_id # Create the fip namespace up front fip_ns = dvr_fip_ns.FipNamespace(ext_net_id, self.agent.conf, self.agent.driver, self.agent.use_ipv6) fip_ns.create() # Create the router with the fip, this shouldn't allow the # update_gateway_port to be called without the fg- port fip_subscribe.return_value = False fip_ns.agent_gateway_port = ( router_info[n_const.FLOATINGIP_AGENT_INTF_KEY]) # This will raise the exception and will also clear # subscription for the ext_net_id self.assertRaises(n_exc.FloatingIpSetupException, self.manage_router, self.agent, router_info) fip_subscribe.return_value = True self.manage_router(self.agent, router_info) # Now update the router again router = self.manage_router(self.agent, router_info) fg_port = router.fip_ns.agent_gateway_port fg_port_name = router.fip_ns.get_ext_device_name(fg_port['id']) fg_device = ip_lib.IPDevice(fg_port_name, namespace=router.fip_ns.name) rtr_2_fip, fip_2_rtr = router.rtr_fip_subnet.get_pair() tbl_index = router._get_snat_idx(fip_2_rtr) tbl_filter = ['table', tbl_index] # Now validate if the gateway is properly configured. self.assertIn('gateway', fg_device.route.get_gateway( filters=tbl_filter)) self._validate_fips_for_external_network( router, router.fip_ns.get_name()) self._delete_router(self.agent, router.router_id) self._assert_fip_namespace_deleted(external_gw_port) def test_dvr_router_fips_stale_gw_port(self): self.agent.conf.agent_mode = 'dvr' # Create the router with external net dvr_router_kwargs = {'ip_address': '19.4.4.3', 'subnet_cidr': '19.4.4.0/24', 'gateway_ip': '19.4.4.1', 'gateway_mac': 'ca:fe:de:ab:cd:ef'} router_info = self.generate_dvr_router_info(**dvr_router_kwargs) external_gw_port = router_info['gw_port'] ext_net_id = router_info['_floatingips'][0]['floating_network_id'] self.mock_plugin_api.get_external_network_id.return_value(ext_net_id) # Create the fip namespace up front stale_fip_ns = dvr_fip_ns.FipNamespace(ext_net_id, self.agent.conf, self.agent.driver, self.agent.use_ipv6) stale_fip_ns.create() # Add a stale fg port to the namespace fixed_ip = external_gw_port['fixed_ips'][0] float_subnet = external_gw_port['subnets'][0] fip_gw_port_ip = str(netaddr.IPAddress(fixed_ip['ip_address']) + 10) prefixlen = netaddr.IPNetwork(float_subnet['cidr']).prefixlen stale_agent_gw_port = { 'subnets': [{'cidr': float_subnet['cidr'], 'gateway_ip': float_subnet['gateway_ip'], 'id': fixed_ip['subnet_id']}], 'network_id': external_gw_port['network_id'], 'device_owner': lib_constants.DEVICE_OWNER_AGENT_GW, 'mac_address': 'fa:16:3e:80:8f:89', portbindings.HOST_ID: self.agent.conf.host, 'fixed_ips': [{'subnet_id': fixed_ip['subnet_id'], 'ip_address': fip_gw_port_ip, 'prefixlen': prefixlen}], 'id': framework._uuid(), 'device_id': framework._uuid()} stale_fip_ns.create_or_update_gateway_port(stale_agent_gw_port) stale_dev_exists = self.device_exists_with_ips_and_mac( stale_agent_gw_port, stale_fip_ns.get_ext_device_name, stale_fip_ns.get_name()) self.assertTrue(stale_dev_exists) # Create the router, this shouldn't allow the duplicate port to stay router = self.manage_router(self.agent, router_info) # Assert the device no longer exists stale_dev_exists = self.device_exists_with_ips_and_mac( stale_agent_gw_port, stale_fip_ns.get_ext_device_name, stale_fip_ns.get_name()) self.assertFalse(stale_dev_exists) # Validate things are looking good and clean up self._validate_fips_for_external_network( router, router.fip_ns.get_name()) ext_gateway_port = router_info['gw_port'] self._delete_router(self.agent, router.router_id) self._assert_fip_namespace_deleted(ext_gateway_port) def test_dvr_router_gateway_redirect_cleanup_on_agent_restart(self): """Test to validate the router namespace gateway redirect rule cleanup. This test checks for the non existence of the gateway redirect rules in the router namespace after the agent restarts while the gateway is removed for the router. """ self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info() router1 = self.manage_router(self.agent, router_info) self._assert_snat_namespace_exists(router1) self.assertTrue(self._namespace_exists(router1.ns_name)) restarted_agent = neutron_l3_agent.L3NATAgentWithStateReport( self.agent.host, self.agent.conf) router1.router['gw_port'] = "" router1.router['gw_port_host'] = "" router1.router['external_gateway_info'] = "" restarted_router = self.manage_router(restarted_agent, router1.router) self.assertTrue(self._namespace_exists(restarted_router.ns_name)) ns_ipr = ip_lib.IPRule(namespace=router1.ns_name) ip4_rules_list = ns_ipr.rule.list_rules(lib_constants.IP_VERSION_4) ip6_rules_list = ns_ipr.rule.list_rules(lib_constants.IP_VERSION_6) # Just make sure the basic set of rules are there in the router # namespace self.assertEqual(3, len(ip4_rules_list)) self.assertEqual(2, len(ip6_rules_list)) def test_dvr_unused_snat_ns_deleted_when_agent_restarts_after_move(self): """Test to validate the stale snat namespace delete with snat move. This test validates the stale snat namespace cleanup when the agent restarts after the gateway port has been moved from the agent. """ self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info() router1 = self.manage_router(self.agent, router_info) self._assert_snat_namespace_exists(router1) restarted_agent = neutron_l3_agent.L3NATAgentWithStateReport( self.agent.host, self.agent.conf) router1.router['gw_port_host'] = "my-new-host" restarted_router = self.manage_router(restarted_agent, router1.router) self._assert_snat_namespace_does_not_exist(restarted_router) def test_dvr_router_fips_for_multiple_ext_networks(self): agent_mode = 'dvr' # Create the first router fip with external net1 dvr_router1_kwargs = {'ip_address': '19.4.4.3', 'subnet_cidr': '19.4.4.0/24', 'gateway_ip': '19.4.4.1', 'gateway_mac': 'ca:fe:de:ab:cd:ef'} router1, fip1_ns = ( self._helper_create_dvr_router_fips_for_ext_network( agent_mode, **dvr_router1_kwargs)) # Validate the fip with external net1 self._validate_fips_for_external_network(router1, fip1_ns) # Create the second router fip with external net2 dvr_router2_kwargs = {'ip_address': '19.4.5.3', 'subnet_cidr': '19.4.5.0/24', 'gateway_ip': '19.4.5.1', 'gateway_mac': 'ca:fe:de:ab:cd:fe'} router2, fip2_ns = ( self._helper_create_dvr_router_fips_for_ext_network( agent_mode, **dvr_router2_kwargs)) # Validate the fip with external net2 self._validate_fips_for_external_network(router2, fip2_ns) def _dvr_router_lifecycle(self, enable_ha=False, enable_snat=False, custom_mtu=2000, ip_version=lib_constants.IP_VERSION_4, dual_stack=False, snat_bound_fip=False): '''Test dvr router lifecycle :param enable_ha: sets the ha value for the router. :param enable_snat: the value of enable_snat is used to set the agent_mode. ''' # The value of agent_mode can be dvr, dvr_snat, or legacy. # Since by definition this is a dvr (distributed = true) # only dvr and dvr_snat are applicable self.agent.conf.agent_mode = 'dvr_snat' if enable_snat else 'dvr' # We get the router info particular to a dvr router router_info = self.generate_dvr_router_info( enable_ha, enable_snat, extra_routes=True, snat_bound_fip=snat_bound_fip) for key in ('_interfaces', '_snat_router_interfaces', '_floatingip_agent_interfaces'): for port in router_info[key]: port['mtu'] = custom_mtu router_info['gw_port']['mtu'] = custom_mtu if enable_ha: router_info['_ha_interface']['mtu'] = custom_mtu # We need to mock the get_agent_gateway_port return value # because the whole L3PluginApi is mocked and we need the port # gateway_port information before the l3_agent will create it. # The port returned needs to have the same information as # router_info['gw_port'] fip_agent_gw_port = self._get_fip_agent_gw_port_for_router( router_info['gw_port']) self.mock_plugin_api.get_agent_gateway_port.return_value = ( fip_agent_gw_port) # We also need to mock the get_external_network_id method to # get the correct fip namespace. self.mock_plugin_api.get_external_network_id.return_value = ( router_info['_floatingips'][0]['floating_network_id']) # With all that set we can now ask the l3_agent to # manage the router (create it, create namespaces, # attach interfaces, etc...) router = self.manage_router(self.agent, router_info) if enable_ha: port = router.get_ex_gw_port() interface_name = router.get_external_device_name(port['id']) self._assert_no_ip_addresses_on_interface(router.ha_namespace, interface_name) utils.wait_until_true(lambda: router.ha_state == 'master') # Keepalived notifies of a state transition when it starts, # not when it ends. Thus, we have to wait until keepalived finishes # configuring everything. We verify this by waiting until the last # device has an IP address. device = router.router[lib_constants.INTERFACE_KEY][-1] device_exists = functools.partial( self.device_exists_with_ips_and_mac, device, router.get_internal_device_name, router.ns_name) utils.wait_until_true(device_exists) name = router.get_internal_device_name(device['id']) self.assertEqual(custom_mtu, ip_lib.IPDevice(name, router.ns_name).link.mtu) ext_gateway_port = router_info['gw_port'] self.assertTrue(self._namespace_exists(router.ns_name)) utils.wait_until_true( lambda: self._metadata_proxy_exists(self.agent.conf, router)) self._assert_internal_devices(router) self._assert_dvr_external_device(router) self._assert_dvr_gateway(router) self._assert_dvr_floating_ips(router, snat_bound_fip=snat_bound_fip) self._assert_snat_chains(router) self._assert_floating_ip_chains(router, snat_bound_fip=snat_bound_fip) self._assert_metadata_chains(router) self._assert_rfp_fpr_mtu(router, custom_mtu) if enable_snat: ip_versions = [4, 6] if (ip_version == 6 or dual_stack) else [4] snat_ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name( router.router_id) self._assert_onlink_subnet_routes( router, ip_versions, snat_ns_name) self._assert_extra_routes(router, namespace=snat_ns_name) # During normal operation, a router-gateway-clear followed by # a router delete results in two notifications to the agent. This # code flow simulates the exceptional case where the notification of # the clearing of the gateway hast been missed, so we are checking # that the L3 agent is robust enough to handle that case and delete # the router correctly. self._delete_router(self.agent, router.router_id) self._assert_fip_namespace_deleted(ext_gateway_port) self._assert_router_does_not_exist(router) self._assert_snat_namespace_does_not_exist(router) def generate_dvr_router_info(self, enable_ha=False, enable_snat=False, enable_gw=True, snat_bound_fip=False, agent=None, extra_routes=False, enable_floating_ip=True, enable_centralized_fip=False, vrrp_id=None, **kwargs): if not agent: agent = self.agent router = l3_test_common.prepare_router_data( enable_snat=enable_snat, enable_floating_ip=enable_floating_ip, enable_ha=enable_ha, extra_routes=extra_routes, num_internal_ports=2, enable_gw=enable_gw, snat_bound_fip=snat_bound_fip, vrrp_id=vrrp_id, **kwargs) internal_ports = router.get(lib_constants.INTERFACE_KEY, []) router['distributed'] = True router['gw_port_host'] = agent.conf.host if enable_floating_ip: for floating_ip in router[lib_constants.FLOATINGIP_KEY]: floating_ip['host'] = agent.conf.host if enable_floating_ip and enable_centralized_fip: # For centralizing the fip, we are emulating the legacy # router behavior were the fip dict does not contain any # host information. router[lib_constants.FLOATINGIP_KEY][0]['host'] = None # In order to test the mixed dvr_snat and compute scenario, we create # two floating IPs, one is distributed, another is centralized. # The distributed floating IP should have the host, which was # just set to None above, then we set it back. The centralized # floating IP has host None, and this IP will be used to test # migration from centralized to distributed. if snat_bound_fip: router[lib_constants.FLOATINGIP_KEY][0]['host'] = agent.conf.host router[lib_constants.FLOATINGIP_KEY][1][ lib_constants.DVR_SNAT_BOUND] = True router[lib_constants.FLOATINGIP_KEY][1]['host'] = None if enable_gw: external_gw_port = router['gw_port'] router['gw_port'][portbindings.HOST_ID] = agent.conf.host self._add_snat_port_info_to_router(router, internal_ports) # FIP has a dependency on external gateway. So we need to create # the snat_port info and fip_agent_gw_port_info irrespective of # the agent type the dvr supports. The namespace creation is # dependent on the agent_type. if enable_floating_ip: for index, floating_ip in enumerate(router['_floatingips']): floating_ip['floating_network_id'] = ( external_gw_port['network_id']) floating_ip['port_id'] = internal_ports[index]['id'] floating_ip['status'] = 'ACTIVE' self._add_fip_agent_gw_port_info_to_router(router, external_gw_port) return router def _get_fip_agent_gw_port_for_router( self, external_gw_port): # Add fip agent gateway port information to the router_info if external_gw_port: # Get values from external gateway port fixed_ip = external_gw_port['fixed_ips'][0] float_subnet = external_gw_port['subnets'][0] port_ip = fixed_ip['ip_address'] # Pick an ip address which is not the same as port_ip fip_gw_port_ip = str(netaddr.IPAddress(port_ip) + 5) # Add floatingip agent gateway port info to router prefixlen = netaddr.IPNetwork(float_subnet['cidr']).prefixlen fip_agent_gw_port_info = { 'subnets': [ {'cidr': float_subnet['cidr'], 'gateway_ip': float_subnet['gateway_ip'], 'id': fixed_ip['subnet_id']}], 'extra_subnets': external_gw_port['extra_subnets'], 'network_id': external_gw_port['network_id'], 'device_owner': lib_constants.DEVICE_OWNER_AGENT_GW, 'mac_address': 'fa:16:3e:80:8d:89', portbindings.HOST_ID: self.agent.conf.host, 'fixed_ips': [{'subnet_id': fixed_ip['subnet_id'], 'ip_address': fip_gw_port_ip, 'prefixlen': prefixlen}], 'id': framework._uuid(), 'device_id': framework._uuid() } return fip_agent_gw_port_info def _add_fip_agent_gw_port_info_to_router(self, router, external_gw_port): # Add fip agent gateway port information to the router_info fip_gw_port_list = router.get( n_const.FLOATINGIP_AGENT_INTF_KEY, []) if not fip_gw_port_list and external_gw_port: # Get values from external gateway port fixed_ip = external_gw_port['fixed_ips'][0] float_subnet = external_gw_port['subnets'][0] port_ip = fixed_ip['ip_address'] # Pick an ip address which is not the same as port_ip fip_gw_port_ip = str(netaddr.IPAddress(port_ip) + 5) # Add floatingip agent gateway port info to router prefixlen = netaddr.IPNetwork(float_subnet['cidr']).prefixlen router[n_const.FLOATINGIP_AGENT_INTF_KEY] = [ {'subnets': [ {'cidr': float_subnet['cidr'], 'gateway_ip': float_subnet['gateway_ip'], 'id': fixed_ip['subnet_id']}], 'extra_subnets': external_gw_port['extra_subnets'], 'network_id': external_gw_port['network_id'], 'device_owner': lib_constants.DEVICE_OWNER_AGENT_GW, 'mac_address': 'fa:16:3e:80:8d:89', portbindings.HOST_ID: self.agent.conf.host, 'fixed_ips': [{'subnet_id': fixed_ip['subnet_id'], 'ip_address': fip_gw_port_ip, 'prefixlen': prefixlen}], 'id': framework._uuid(), 'device_id': framework._uuid()} ] def _add_snat_port_info_to_router(self, router, internal_ports): # Add snat port information to the router snat_port_list = router.get(n_const.SNAT_ROUTER_INTF_KEY, []) if not snat_port_list and internal_ports: router[n_const.SNAT_ROUTER_INTF_KEY] = [] for port in internal_ports: # Get values from internal port fixed_ip = port['fixed_ips'][0] snat_subnet = port['subnets'][0] port_ip = fixed_ip['ip_address'] # Pick an ip address which is not the same as port_ip snat_ip = str(netaddr.IPAddress(port_ip) + 5) # Add the info to router as the first snat port # in the list of snat ports prefixlen = netaddr.IPNetwork(snat_subnet['cidr']).prefixlen snat_router_port = { 'subnets': [ {'cidr': snat_subnet['cidr'], 'gateway_ip': snat_subnet['gateway_ip'], 'id': fixed_ip['subnet_id']}], 'network_id': port['network_id'], 'device_owner': lib_constants.DEVICE_OWNER_ROUTER_SNAT, 'mac_address': 'fa:16:3e:80:8d:89', 'fixed_ips': [{'subnet_id': fixed_ip['subnet_id'], 'ip_address': snat_ip, 'prefixlen': prefixlen}], 'id': framework._uuid(), 'device_id': framework._uuid()} # Get the address scope if there is any if 'address_scopes' in port: snat_router_port['address_scopes'] = port['address_scopes'] router[n_const.SNAT_ROUTER_INTF_KEY].append( snat_router_port) def _assert_dvr_external_device(self, router): external_port = router.get_ex_gw_port() snat_ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name( router.router_id) # if the agent is in dvr_snat mode, then we have to check # that the correct ports and ip addresses exist in the # snat_ns_name namespace if self.agent.conf.agent_mode == 'dvr_snat': device_exists = functools.partial( self.device_exists_with_ips_and_mac, external_port, router.get_external_device_name, snat_ns_name) utils.wait_until_true(device_exists) # if the agent is in dvr mode then the snat_ns_name namespace # should not be present at all: elif self.agent.conf.agent_mode == 'dvr': self.assertFalse( self._namespace_exists(snat_ns_name), "namespace %s was found but agent is in dvr mode not dvr_snat" % (str(snat_ns_name)) ) # if the agent is anything else the test is misconfigured # we force a test failure with message else: self.fail("Agent not configured for dvr or dvr_snat") def _assert_dvr_gateway(self, router): gateway_expected_in_snat_namespace = ( self.agent.conf.agent_mode == 'dvr_snat' ) if gateway_expected_in_snat_namespace: self._assert_dvr_snat_gateway(router) self._assert_removal_of_already_deleted_gateway_device(router) snat_namespace_should_not_exist = ( self.agent.conf.agent_mode == 'dvr' ) if snat_namespace_should_not_exist: self._assert_snat_namespace_does_not_exist(router) def _assert_dvr_snat_gateway(self, router): namespace = dvr_snat_ns.SnatNamespace.get_snat_ns_name( router.router_id) external_port = router.get_ex_gw_port() external_device_name = router.get_external_device_name( external_port['id']) external_device = ip_lib.IPDevice(external_device_name, namespace=namespace) existing_gateway = ( external_device.route.get_gateway().get('gateway')) expected_gateway = external_port['subnets'][0]['gateway_ip'] self.assertEqual(expected_gateway, existing_gateway) def _assert_removal_of_already_deleted_gateway_device(self, router): namespace = dvr_snat_ns.SnatNamespace.get_snat_ns_name( router.router_id) device = ip_lib.IPDevice("fakedevice", namespace=namespace) # Assert that no exception is thrown for this case self.assertIsNone(router._delete_gateway_device_if_exists( device, "192.168.0.1", 0)) def _assert_snat_namespace_does_not_exist(self, router): namespace = dvr_snat_ns.SnatNamespace.get_snat_ns_name( router.router_id) self.assertFalse(self._namespace_exists(namespace)) def _assert_dvr_floating_ips(self, router, snat_bound_fip=False): # in the fip namespace: # Check that the fg- (floatingip_agent_gateway) # is created with the ip address of the external gateway port floating_ips = router.router[lib_constants.FLOATINGIP_KEY] self.assertTrue(floating_ips) # We need to fetch the floatingip agent gateway port info # from the router_info floating_agent_gw_port = ( router.router[n_const.FLOATINGIP_AGENT_INTF_KEY]) self.assertTrue(floating_agent_gw_port) external_gw_port = floating_agent_gw_port[0] fip_ns = self.agent.get_fip_ns(floating_ips[0]['floating_network_id']) fip_ns_name = fip_ns.get_name() fg_port_created_successfully = ip_lib.device_exists_with_ips_and_mac( fip_ns.get_ext_device_name(external_gw_port['id']), [self._port_first_ip_cidr(external_gw_port)], external_gw_port['mac_address'], namespace=fip_ns_name) self.assertTrue(fg_port_created_successfully) # Check fpr-router device has been created device_name = fip_ns.get_int_device_name(router.router_id) fpr_router_device_created_successfully = ip_lib.device_exists( device_name, namespace=fip_ns_name) self.assertTrue(fpr_router_device_created_successfully) # In the router namespace # Check rfp- is created correctly for fip in floating_ips: device_name = fip_ns.get_rtr_ext_device_name(router.router_id) self.assertTrue(ip_lib.device_exists( device_name, namespace=router.ns_name)) # In the router namespace, check the iptables rules are set # correctly for fip in floating_ips: expected_rules = router.floating_forward_rules(fip) if fip.get(lib_constants.DVR_SNAT_BOUND): iptables_mgr = router.snat_iptables_manager else: iptables_mgr = router.iptables_manager self._assert_iptables_rules_exist( iptables_mgr, 'nat', expected_rules) def test_dvr_router_with_ha_for_fip_disassociation(self): """Test to validate the fip rules are deleted in dvr_snat_ha router. This test validates the fip rules are getting deleted in a router namespace when the router has ha and snat enabled after the floatingip is disassociated. """ self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info( enable_snat=True, enable_ha=True, enable_gw=True) fip_agent_gw_port = router_info[n_const.FLOATINGIP_AGENT_INTF_KEY] self.mock_plugin_api.get_agent_gateway_port.return_value = ( fip_agent_gw_port[0]) router1 = self.manage_router(self.agent, router_info) fip_ns_name = router1.fip_ns.get_name() self.assertTrue(self._namespace_exists(router1.ns_name)) self.assertTrue(self._namespace_exists(fip_ns_name)) self._assert_snat_namespace_exists(router1) ns_ipr = ip_lib.IPRule(namespace=router1.ns_name) ip4_rules_list_with_fip = ns_ipr.rule.list_rules( lib_constants.IP_VERSION_4) # The rules_list should have 6 entries: # 3 default rules (local, main and default) # 1 Fip forward rule # 2 interface rules to redirect to snat self.assertEqual(6, len(ip4_rules_list_with_fip)) rfp_device_name = router1.fip_ns.get_rtr_ext_device_name( router1.router_id) rfp_device = ip_lib.IPDevice(rfp_device_name, namespace=router1.ns_name) rtr_2_fip, fip_2_rtr = router1.rtr_fip_subnet.get_pair() self._assert_default_gateway( fip_2_rtr, rfp_device, rfp_device_name) router1.router[lib_constants.FLOATINGIP_KEY] = [] self.agent._process_updated_router(router1.router) router_updated = self.agent.router_info[router1.router['id']] self.assertTrue(self._namespace_exists(router_updated.ns_name)) self._assert_snat_namespace_exists(router1) ip4_rules_list = ns_ipr.rule.list_rules(lib_constants.IP_VERSION_4) self.assertEqual(5, len(ip4_rules_list)) interface_rules_list_count = 0 fip_rule_count = 0 for ip_rule in ip4_rules_list: tbl_index = ip_rule['table'] if tbl_index not in ['local', 'default', 'main']: interface_rules_list_count += 1 if tbl_index == dvr_fip_ns.FIP_RT_TBL: fip_rule_count += 1 self.assertEqual(2, interface_rules_list_count) self.assertEqual(0, fip_rule_count) def _assert_default_gateway(self, fip_2_rtr, rfp_device, device_name): expected_gateway = [{'dev': device_name, 'cidr': '0.0.0.0/0', 'via': str(fip_2_rtr.ip), 'table': dvr_fip_ns.FIP_RT_TBL}] self.assertEqual(expected_gateway, rfp_device.route.list_routes( ip_version=lib_constants.IP_VERSION_4, table=dvr_fip_ns.FIP_RT_TBL, via=str(fip_2_rtr.ip))) def test_dvr_router_rem_fips_on_restarted_agent(self): self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info() router1 = self.manage_router(self.agent, router_info) fip_ns = router1.fip_ns.get_name() self.assertTrue(self._namespace_exists(fip_ns)) restarted_agent = neutron_l3_agent.L3NATAgentWithStateReport( self.agent.host, self.agent.conf) router1.router[lib_constants.FLOATINGIP_KEY] = [] self.manage_router(restarted_agent, router1.router) self._assert_dvr_snat_gateway(router1) self.assertTrue(self._namespace_exists(fip_ns)) def test_dvr_router_update_on_restarted_agent_sets_rtr_fip_connect(self): self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info() router1 = self.manage_router(self.agent, router_info) self.assertTrue(router1.rtr_fip_connect) fip_ns = router1.fip_ns.get_name() self.assertTrue(self._namespace_exists(fip_ns)) restarted_agent = neutron_l3_agent.L3NATAgentWithStateReport( self.agent.host, self.agent.conf) router_updated = self.manage_router(restarted_agent, router1.router) self.assertTrue(router_updated.rtr_fip_connect) def test_dvr_router_add_fips_on_restarted_agent(self): self.agent.conf.agent_mode = 'dvr' router_info = self.generate_dvr_router_info() router = self.manage_router(self.agent, router_info) floating_ips = router.router[lib_constants.FLOATINGIP_KEY] router_ns = router.ns_name fip_rule_prio_1 = self._get_fixed_ip_rule_priority( router_ns, floating_ips[0]['fixed_ip_address']) restarted_agent = neutron_l3_agent.L3NATAgent( self.agent.host, self.agent.conf) floating_ips[0]['floating_ip_address'] = '21.4.4.2' floating_ips[0]['fixed_ip_address'] = '10.0.0.2' self.manage_router(restarted_agent, router_info) fip_rule_prio_2 = self._get_fixed_ip_rule_priority( router_ns, floating_ips[0]['fixed_ip_address']) self.assertNotEqual(fip_rule_prio_1, fip_rule_prio_2) def test_dvr_router_floating_ip_moved(self): self.agent.conf.agent_mode = 'dvr' router_info = self.generate_dvr_router_info() router = self.manage_router(self.agent, router_info) floating_ips = router.router[lib_constants.FLOATINGIP_KEY] router_ns = router.ns_name fixed_ip = floating_ips[0]['fixed_ip_address'] self.assertTrue(self._fixed_ip_rule_exists(router_ns, fixed_ip)) # Floating IP reassigned to another fixed IP new_fixed_ip = '10.0.0.2' self.assertNotEqual(new_fixed_ip, fixed_ip) floating_ips[0]['fixed_ip_address'] = new_fixed_ip self.agent._process_updated_router(router.router) self.assertFalse(self._fixed_ip_rule_exists(router_ns, fixed_ip)) self.assertTrue(self._fixed_ip_rule_exists(router_ns, new_fixed_ip)) def _assert_iptables_rules_exist( self, router_iptables_manager, table_name, expected_rules): rules = router_iptables_manager.get_rules_for_table(table_name) for rule in expected_rules: self.assertIn( str(iptables_manager.IptablesRule(rule[0], rule[1])), rules) return True def _assert_iptables_rules_not_exist( self, router_iptables_manager, table_name, expected_rules): rules = router_iptables_manager.get_rules_for_table(table_name) for rule in expected_rules: self.assertNotIn( str(iptables_manager.IptablesRule(rule[0], rule[1])), rules) return True def test_prevent_snat_rule_exist_on_restarted_agent(self): self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info() router = self.manage_router(self.agent, router_info) ext_port = router.get_ex_gw_port() rfp_devicename = router.get_external_device_interface_name(ext_port) prevent_snat_rule = router._prevent_snat_for_internal_traffic_rule( rfp_devicename) self._assert_iptables_rules_exist( router.iptables_manager, 'nat', [prevent_snat_rule]) restarted_agent = neutron_l3_agent.L3NATAgentWithStateReport( self.agent.host, self.agent.conf) restarted_router = self.manage_router(restarted_agent, router_info) self._assert_iptables_rules_exist( restarted_router.iptables_manager, 'nat', [prevent_snat_rule]) def _get_fixed_ip_rule_priority(self, namespace, fip): iprule = ip_lib.IPRule(namespace) lines = iprule.rule._as_root([4], ['show']).splitlines() for line in lines: if fip in line: info = iprule.rule._parse_line(4, line) return info['priority'] def _fixed_ip_rule_exists(self, namespace, ip): iprule = ip_lib.IPRule(namespace) lines = iprule.rule._as_root([4], ['show']).splitlines() for line in lines: if ip in line: info = iprule.rule._parse_line(4, line) if info['from'] == ip: return True return False def test_dvr_router_add_internal_network_set_arp_cache(self): # Check that, when the router is set up and there are # existing ports on the uplinked subnet, the ARP # cache is properly populated. self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info(enable_snat=True) expected_neighbor = '35.4.1.10' port_data = { 'fixed_ips': [{'ip_address': expected_neighbor}], 'mac_address': 'fa:3e:aa:bb:cc:dd', 'device_owner': DEVICE_OWNER_COMPUTE } self.agent.plugin_rpc.get_ports_by_subnet.return_value = [port_data] router1 = self.manage_router(self.agent, router_info) internal_device = router1.get_internal_device_name( router_info['_interfaces'][0]['id']) neighbor = ip_lib.dump_neigh_entries(4, internal_device, router1.ns_name, dst=expected_neighbor) self.assertNotEqual([], neighbor) self.assertEqual(expected_neighbor, neighbor[0]['dst']) def _assert_rfp_fpr_mtu(self, router, expected_mtu=1500): dev_mtu = self.get_device_mtu( router.router_id, router.fip_ns.get_rtr_ext_device_name, router.ns_name) self.assertEqual(expected_mtu, dev_mtu) dev_mtu = self.get_device_mtu( router.router_id, router.fip_ns.get_int_device_name, router.fip_ns.get_name()) self.assertEqual(expected_mtu, dev_mtu) def test_dvr_router_fip_agent_mismatch(self): """Test to validate the floatingip agent mismatch. This test validates the condition where floatingip agent gateway port host mismatches with the agent and so the binding will not be there. """ self.agent.conf.agent_mode = 'dvr' router_info = self.generate_dvr_router_info() floating_ip = router_info['_floatingips'][0] floating_ip['host'] = 'my_new_host' # In this case the floatingip binding is different and so it # should not create the floatingip namespace on the given agent. # This is also like there is no current binding. router1 = self.manage_router(self.agent, router_info) fip_ns = router1.fip_ns.get_name() self.assertTrue(self._namespace_exists(router1.ns_name)) # FIP Namespace creation does not depend on the floatingip's # anymore and will be created on each agent when there is # a valid gateway. self.assertTrue(self._namespace_exists(fip_ns)) self._assert_snat_namespace_does_not_exist(router1) def test_dvr_router_fip_create_for_migrating_port(self): """Test to validate the floatingip create on port migrate. This test validates the condition where floatingip host mismatches with the agent, but the 'dest_host' variable matches with the agent host, due to port pre-migrate phase. """ self.agent.conf.agent_mode = 'dvr' router_info = self.generate_dvr_router_info() floating_ip = router_info['_floatingips'][0] floating_ip['host'] = 'my_new_host' floating_ip['dest_host'] = self.agent.host # Now we have the floatingip 'host' pointing to host that # does not match to the 'agent.host' and the floatingip # 'dest_host' matches with the agent.host in the case # of live migration due to the port_profile update from # nova. router1 = self.manage_router(self.agent, router_info) fip_ns = router1.fip_ns.get_name() self.assertTrue(self._namespace_exists(router1.ns_name)) self.assertTrue(self._namespace_exists(fip_ns)) def test_dvr_router_fip_late_binding(self): """Test to validate the floatingip migration or latebinding. This test validates the condition where floatingip private port changes while migration or when the private port host binding is done later after floatingip association. """ self.agent.conf.agent_mode = 'dvr' router_info = self.generate_dvr_router_info() fip_agent_gw_port = router_info[n_const.FLOATINGIP_AGENT_INTF_KEY] # Now let us not pass the FLOATINGIP_AGENT_INTF_KEY, to emulate # that the server did not create the port, since there was no valid # host binding. router_info[n_const.FLOATINGIP_AGENT_INTF_KEY] = [] self.mock_plugin_api.get_agent_gateway_port.return_value = ( fip_agent_gw_port[0]) router1 = self.manage_router(self.agent, router_info) fip_ns = router1.fip_ns.get_name() self.assertTrue(self._namespace_exists(router1.ns_name)) self.assertTrue(self._namespace_exists(fip_ns)) self._assert_snat_namespace_does_not_exist(router1) def test_dvr_router_fip_namespace_create_without_floatingip(self): """Test to validate the floatingip namespace creation without fip. This test validates the condition where floatingip namespace gets created on the agent when the gateway is added and without floatingip configured for the router. """ self.agent.conf.agent_mode = 'dvr' router_info = self.generate_dvr_router_info(enable_floating_ip=False) fip_agent_gw_port = self._get_fip_agent_gw_port_for_router( router_info['gw_port']) self.mock_plugin_api.get_agent_gateway_port.return_value = ( fip_agent_gw_port) router1 = self.manage_router(self.agent, router_info) fip_ns = router1.fip_ns.get_name() self.assertTrue(self._namespace_exists(router1.ns_name)) self.assertTrue(self._namespace_exists(fip_ns)) self.assertTrue(router1.rtr_fip_connect) self._assert_snat_namespace_does_not_exist(router1) def _assert_snat_namespace_exists(self, router): namespace = dvr_snat_ns.SnatNamespace.get_snat_ns_name( router.router_id) self.assertTrue(self._namespace_exists(namespace)) def _get_dvr_snat_namespace_device_status( self, router, internal_dev_name=None): """Function returns the internal and external device status.""" snat_ns = dvr_snat_ns.SnatNamespace.get_snat_ns_name( router.router_id) external_port = router.get_ex_gw_port() external_device_name = router.get_external_device_name( external_port['id']) qg_device_created_successfully = ip_lib.device_exists( external_device_name, namespace=snat_ns) sg_device_created_successfully = ip_lib.device_exists( internal_dev_name, namespace=snat_ns) return qg_device_created_successfully, sg_device_created_successfully def test_snat_bound_floating_ip(self): """Test to validate the snat bound floatingip lifecycle.""" self.agent.conf.agent_mode = lib_constants.L3_AGENT_MODE_DVR_SNAT router_info = self.generate_dvr_router_info(snat_bound_fip=True) router1 = self.manage_router(self.agent, router_info) snat_bound_floatingips = router_info[lib_constants.FLOATINGIP_KEY] self._assert_snat_namespace_exists(router1) # In the snat namespace, check the iptables rules are set correctly for fip in snat_bound_floatingips: expected_rules = router1.floating_forward_rules(fip) if fip.get(lib_constants.DVR_SNAT_BOUND): self._assert_iptables_rules_exist( router1.snat_iptables_manager, 'nat', expected_rules) def test_floating_ip_migrate_when_unbound_port_is_bound_to_a_host(self): """Test to check floating ips migrate from unbound to bound host.""" self.agent.conf.agent_mode = lib_constants.L3_AGENT_MODE_DVR_SNAT router_info = self.generate_dvr_router_info( enable_floating_ip=True, enable_centralized_fip=True, enable_snat=True, snat_bound_fip=True) router1 = self.manage_router(self.agent, router_info) floatingips = router_info[lib_constants.FLOATINGIP_KEY] distributed_fip = floatingips[0] centralized_floatingip = floatingips[1] # For private ports hosted in dvr_no_fip agent, the floatingip # dict will contain the fip['host'] key, but the value will always # be None to emulate the legacy router. self.assertIsNone(centralized_floatingip['host']) self.assertTrue(self._namespace_exists(router1.ns_name)) fip_ns = router1.fip_ns.get_name() self.assertTrue(self._namespace_exists(fip_ns)) self._assert_snat_namespace_exists(router1) # If fips are centralized then, the DNAT rules are only # configured in the SNAT Namespace and not in the router-ns. expected_rules = router1.floating_forward_rules(distributed_fip) self.assertTrue(self._assert_iptables_rules_exist( router1.iptables_manager, 'nat', expected_rules)) expected_rules = router1._centralized_floating_forward_rules( centralized_floatingip['floating_ip_address'], centralized_floatingip['fixed_ip_address']) self.assertTrue(self._assert_iptables_rules_exist( router1.snat_iptables_manager, 'nat', expected_rules)) qrouter_ns = router1.ns_name fixed_ip_dist = distributed_fip['fixed_ip_address'] snat_ns = router1.snat_namespace.name fixed_ip_cent = centralized_floatingip['fixed_ip_address'] self.assertFalse(self._fixed_ip_rule_exists(qrouter_ns, fixed_ip_cent)) self.assertTrue(self._fixed_ip_rule_exists(qrouter_ns, fixed_ip_dist)) self.assertFalse(self._fixed_ip_rule_exists(snat_ns, fixed_ip_dist)) self.assertFalse(self._fixed_ip_rule_exists(snat_ns, fixed_ip_cent)) # Now let us edit the centralized floatingIP info with 'host' # and remove the 'dvr_snat_bound' router1.router[lib_constants.FLOATINGIP_KEY][1]['host'] = ( self.agent.conf.host) del router1.router[lib_constants.FLOATINGIP_KEY][1]['dvr_snat_bound'] self.agent._process_updated_router(router1.router) router_updated = self.agent.router_info[router_info['id']] qrouter_ns = router_updated.ns_name fixed_ip_dist = distributed_fip['fixed_ip_address'] self._assert_snat_namespace_exists(router_updated) snat_ns = router_updated.snat_namespace.name fixed_ip_cent = centralized_floatingip['fixed_ip_address'] router_updated.get_centralized_fip_cidr_set = mock.Mock( return_value=set(["19.4.4.3/32"])) self.assertTrue(self._assert_iptables_rules_not_exist( router_updated.snat_iptables_manager, 'nat', expected_rules)) port = router_updated.get_ex_gw_port() interface_name = router_updated.get_external_device_name(port['id']) self._assert_ip_address_not_on_interface( snat_ns, interface_name, centralized_floatingip['floating_ip_address']) self.assertTrue(self._fixed_ip_rule_exists(qrouter_ns, fixed_ip_dist)) self.assertFalse(self._fixed_ip_rule_exists(snat_ns, fixed_ip_dist)) self.assertTrue(self._fixed_ip_rule_exists(qrouter_ns, fixed_ip_cent)) self.assertFalse(self._fixed_ip_rule_exists(snat_ns, fixed_ip_cent)) self.assertTrue(self._namespace_exists(fip_ns)) def _test_get_centralized_fip_cidr_set(self, router_info, expected_result_empty): self.agent.conf.agent_mode = lib_constants.L3_AGENT_MODE_DVR_SNAT self.manage_router(self.agent, router_info) router = self.agent.router_info[router_info['id']] centralized_fips = router.get_centralized_fip_cidr_set() if expected_result_empty: self.assertEqual(set([]), centralized_fips) else: self.assertNotEqual(set([]), centralized_fips) def test_get_centralized_fip_cidr_set(self): router_info = self.generate_dvr_router_info( enable_floating_ip=True, enable_centralized_fip=True, enable_snat=True, snat_bound_fip=True) self._test_get_centralized_fip_cidr_set(router_info, False) def test_get_centralized_fip_cidr_set_not_snat_host(self): router_info = self.generate_dvr_router_info( enable_floating_ip=True, enable_centralized_fip=True, enable_snat=True, snat_bound_fip=True) router_info['gw_port_host'] = 'some-other-host' self._test_get_centralized_fip_cidr_set(router_info, True) def test_get_centralized_fip_cidr_set_no_ex_gw_port(self): self.agent.conf.agent_mode = lib_constants.L3_AGENT_MODE_DVR_SNAT router_info = self.generate_dvr_router_info( enable_floating_ip=True, enable_centralized_fip=True, enable_snat=True, snat_bound_fip=True) router_info['gw_port'] = {} self._test_get_centralized_fip_cidr_set(router_info, True) def test_floating_ip_not_deployed_on_dvr_no_external_agent(self): """Test to check floating ips not configured for dvr_no_external.""" self.agent.conf.agent_mode = n_const.L3_AGENT_MODE_DVR_NO_EXTERNAL router_info = self.generate_dvr_router_info( enable_floating_ip=True, enable_centralized_fip=True) router1 = self.manage_router(self.agent, router_info) centralized_floatingips = router_info[lib_constants.FLOATINGIP_KEY] # For private ports hosted in dvr_no_fip agent, the floatingip # dict will contain the fip['host'] key, but the value will always # be None to emulate the legacy router. self.assertIsNone(centralized_floatingips[0]['host']) self.assertTrue(self._namespace_exists(router1.ns_name)) fip_ns = router1.fip_ns.get_name() self.assertFalse(self._namespace_exists(fip_ns)) # If fips are centralized then, the DNAT rules are only # configured in the SNAT Namespace and not in the router-ns. for fip in centralized_floatingips: expected_rules = router1.floating_forward_rules(fip) self.assertEqual(0, len(expected_rules)) def test_floating_ip_create_does_not_raise_keyerror_on_missing_host(self): """Test to check floating ips configure does not raise Keyerror.""" self.agent.conf.agent_mode = 'dvr' router_info = self.generate_dvr_router_info( enable_floating_ip=True) del router_info[lib_constants.FLOATINGIP_KEY][0]['host'] centralized_floatingips = router_info[lib_constants.FLOATINGIP_KEY][0] self.assertIsNone(centralized_floatingips.get('host')) # No Keyerror should be raised when calling manage_router self.manage_router(self.agent, router_info) def test_dvr_router_snat_namespace_with_interface_remove(self): """Test to validate the snat namespace with interface remove. This test validates the snat namespace for all the external and internal devices. It also validates if the internal device corresponding to the router interface is removed when the router interface is deleted. """ self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info() snat_internal_port = router_info[n_const.SNAT_ROUTER_INTF_KEY] router1 = self.manage_router(self.agent, router_info) csnat_internal_port = ( router1.router[n_const.SNAT_ROUTER_INTF_KEY]) # Now save the internal device name to verify later internal_device_name = router1._get_snat_int_device_name( csnat_internal_port[0]['id']) self._assert_snat_namespace_exists(router1) qg_device, sg_device = self._get_dvr_snat_namespace_device_status( router1, internal_dev_name=internal_device_name) self.assertTrue(qg_device) self.assertTrue(sg_device) self.assertEqual(router1.snat_ports, snat_internal_port) # Now let us not pass INTERFACE_KEY, to emulate # the interface has been removed. router1.router[lib_constants.INTERFACE_KEY] = [] # Now let us not pass the SNAT_ROUTER_INTF_KEY, to emulate # that the server did not send it, since the interface has been # removed. router1.router[n_const.SNAT_ROUTER_INTF_KEY] = [] self.agent._process_updated_router(router1.router) router_updated = self.agent.router_info[router_info['id']] self._assert_snat_namespace_exists(router_updated) qg_device, sg_device = self._get_dvr_snat_namespace_device_status( router_updated, internal_dev_name=internal_device_name) self.assertFalse(sg_device) self.assertTrue(qg_device) def _mocked_dvr_ha_router(self, agent, enable_ha=True, enable_gw=True, enable_centralized_fip=False, snat_bound_fip=False, vrrp_id=None, **kwargs): r_info = self.generate_dvr_router_info( enable_ha=enable_ha, enable_snat=True, agent=agent, enable_gw=enable_gw, enable_centralized_fip=enable_centralized_fip, snat_bound_fip=snat_bound_fip, vrrp_id=vrrp_id, **kwargs) r_snat_ns_name = namespaces.build_ns_name(dvr_snat_ns.SNAT_NS_PREFIX, r_info['id']) mocked_r_snat_ns_name = r_snat_ns_name + '@' + agent.host r_ns_name = namespaces.build_ns_name(namespaces.NS_PREFIX, r_info['id']) mocked_r_ns_name = r_ns_name + '@' + agent.host return r_info, mocked_r_ns_name, mocked_r_snat_ns_name def _setup_dvr_ha_agents(self): self.agent.conf.agent_mode = 'dvr_snat' conf = self._configure_agent('agent2') self.failover_agent = neutron_l3_agent.L3NATAgentWithStateReport( 'agent2', conf) self.failover_agent.conf.agent_mode = 'dvr_snat' def _setup_dvr_ha_bridges(self): br_int_1 = self._get_agent_ovs_integration_bridge(self.agent) br_int_2 = self._get_agent_ovs_integration_bridge(self.failover_agent) veth1, veth2 = self.useFixture(net_helpers.VethFixture()).ports veth1.link.set_up() veth2.link.set_up() br_int_1.add_port(veth1.name) br_int_2.add_port(veth2.name) def _create_dvr_ha_router(self, agent, enable_gw=True, enable_centralized_fip=False, snat_bound_fip=False, ha_interface=True, vrrp_id=None, **kwargs): get_ns_name = mock.patch.object(namespaces.RouterNamespace, '_get_ns_name').start() get_snat_ns_name = mock.patch.object(dvr_snat_ns.SnatNamespace, 'get_snat_ns_name').start() (r_info, mocked_r_ns_name, mocked_r_snat_ns_name) = self._mocked_dvr_ha_router( agent, ha_interface, enable_gw, enable_centralized_fip, snat_bound_fip, vrrp_id=vrrp_id, **kwargs) if not ha_interface: r_info['ha'] = True get_ns_name.return_value = mocked_r_ns_name get_snat_ns_name.return_value = mocked_r_snat_ns_name router = self.manage_router(agent, r_info) return router def _assert_ip_addresses_in_dvr_ha_snat_namespace_with_fip(self, router): namespace = router.ha_namespace ex_gw_port = router.get_ex_gw_port() snat_ports = router.get_snat_interfaces() if not snat_ports: return if router.is_router_master(): centralized_floatingips = ( router.router[lib_constants.FLOATINGIP_KEY]) for fip in centralized_floatingips: expected_rules = router.floating_forward_rules(fip) self.assertFalse(self._assert_iptables_rules_exist( router.snat_iptables_manager, 'nat', expected_rules)) snat_port = snat_ports[0] ex_gw_port_name = router.get_external_device_name( ex_gw_port['id']) snat_port_name = router._get_snat_int_device_name( snat_port['id']) ex_gw_port_cidrs = utils.fixed_ip_cidrs(ex_gw_port["fixed_ips"]) snat_port_cidrs = utils.fixed_ip_cidrs(snat_port["fixed_ips"]) self._assert_ip_addresses_on_interface(namespace, ex_gw_port_name, ex_gw_port_cidrs) self._assert_ip_addresses_on_interface(namespace, snat_port_name, snat_port_cidrs) def _assert_no_ip_addresses_in_dvr_ha_snat_namespace_with_fip(self, router): namespace = router.ha_namespace ex_gw_port = router.get_ex_gw_port() snat_ports = router.get_snat_interfaces() if not snat_ports: return snat_port = snat_ports[0] ex_gw_port_name = router.get_external_device_name( ex_gw_port['id']) snat_port_name = router._get_snat_int_device_name( snat_port['id']) self._assert_no_ip_addresses_on_interface(namespace, snat_port_name) self._assert_no_ip_addresses_on_interface(namespace, ex_gw_port_name) def _assert_ip_addresses_in_dvr_ha_snat_namespace(self, router): namespace = router.ha_namespace ex_gw_port = router.get_ex_gw_port() snat_ports = router.get_snat_interfaces() if not snat_ports: return snat_port = snat_ports[0] ex_gw_port_name = router.get_external_device_name( ex_gw_port['id']) snat_port_name = router._get_snat_int_device_name( snat_port['id']) ip = ex_gw_port["fixed_ips"][0]['ip_address'] prefix_len = ex_gw_port["fixed_ips"][0]['prefixlen'] ex_gw_port_cidr = ip + "/" + str(prefix_len) ip = snat_port["fixed_ips"][0]['ip_address'] prefix_len = snat_port["fixed_ips"][0]['prefixlen'] snat_port_cidr = ip + "/" + str(prefix_len) self._assert_ip_address_on_interface(namespace, ex_gw_port_name, ex_gw_port_cidr) self._assert_ip_address_on_interface(namespace, snat_port_name, snat_port_cidr) def _assert_no_ip_addresses_in_dvr_ha_snat_namespace(self, router): namespace = router.ha_namespace ex_gw_port = router.get_ex_gw_port() snat_ports = router.get_snat_interfaces() if not snat_ports: return snat_port = snat_ports[0] ex_gw_port_name = router.get_external_device_name( ex_gw_port['id']) snat_port_name = router._get_snat_int_device_name( snat_port['id']) self._assert_no_ip_addresses_on_interface(namespace, snat_port_name) self._assert_no_ip_addresses_on_interface(namespace, ex_gw_port_name) @mock.patch.object(dvr_local_router.DvrLocalRouter, 'connect_rtr_2_fip') @mock.patch.object( dvr_ha_router.DvrEdgeHaRouter, 'get_centralized_fip_cidr_set') def test_dvr_ha_router_with_centralized_fip_calls_keepalived_cidr( self, connect_rtr_2_fip_mock, fip_cidr_centralized_mock): self._setup_dvr_ha_agents() self._setup_dvr_ha_bridges() router1 = self._create_dvr_ha_router( self.agent, enable_gw=True, enable_centralized_fip=True, snat_bound_fip=True) self.assertTrue(fip_cidr_centralized_mock.called) restarted_agent = neutron_l3_agent.L3NATAgentWithStateReport( self.agent.host, self.agent.conf) self.manage_router(restarted_agent, router1.router) self.assertTrue(fip_cidr_centralized_mock.called) @mock.patch.object(dvr_local_router.DvrLocalRouter, 'connect_rtr_2_fip') @mock.patch.object( dvr_edge_router.DvrEdgeRouter, 'get_centralized_fip_cidr_set') def test_dvr_router_with_centralized_fip_calls_keepalived_cidr( self, connect_rtr_2_fip_mock, fip_cidr_centralized_mock): router_info = self.generate_dvr_router_info( enable_gw=True, enable_centralized_fip=True, snat_bound_fip=True) router1 = self.manage_router(self.agent, router_info) self.assertTrue(fip_cidr_centralized_mock.called) restarted_agent = neutron_l3_agent.L3NATAgentWithStateReport( self.agent.host, self.agent.conf) self.manage_router(restarted_agent, router1.router) self.assertTrue(fip_cidr_centralized_mock.called) def test_dvr_ha_router_unbound_from_agents(self): self._setup_dvr_ha_agents() self._setup_dvr_ha_bridges() router1 = self._create_dvr_ha_router( self.agent, enable_gw=True, vrrp_id=14, ha_port_ip="169.254.192.106", ha_port_mac="12:34:56:78:3a:aa") router2 = self._create_dvr_ha_router( self.failover_agent, enable_gw=True, vrrp_id=14, ha_port_ip="169.254.192.107", ha_port_mac="12:34:56:78:3a:bb") master, backup = self._get_master_and_slave_routers( router1, router2, check_external_device=False) self._assert_ip_addresses_in_dvr_ha_snat_namespace(master) self._assert_no_ip_addresses_in_dvr_ha_snat_namespace(backup) master_ha_device = master.get_ha_device_name() backup_ha_device = backup.get_ha_device_name() self.assertTrue( ip_lib.device_exists(master_ha_device, master.ha_namespace)) self.assertTrue( ip_lib.device_exists(backup_ha_device, backup.ha_namespace)) new_master_router = copy.deepcopy(master.router) new_master_router['_ha_interface'] = None self.agent._process_updated_router(new_master_router) router_updated = self.agent.router_info[master.router_id] self.assertTrue(self._namespace_exists(router_updated.ns_name)) self._assert_snat_namespace_exists(router_updated) snat_namespace_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name( router_updated.router_id) self.assertFalse( ip_lib.device_exists(master_ha_device, snat_namespace_name)) utils.wait_until_true(lambda: backup.ha_state == 'master') self._assert_ip_addresses_in_dvr_ha_snat_namespace(backup) self.assertTrue( ip_lib.device_exists(backup_ha_device, backup.ha_namespace)) def _test_dvr_ha_router_failover_with_gw_and_fip(self, enable_gw, enable_centralized_fip, snat_bound_fip, vrrp_id=None): self._setup_dvr_ha_agents() self._setup_dvr_ha_bridges() router1 = self._create_dvr_ha_router( self.agent, enable_gw=enable_gw, enable_centralized_fip=enable_centralized_fip, snat_bound_fip=snat_bound_fip, vrrp_id=vrrp_id, ha_port_ip="169.254.192.100", ha_port_mac="12:34:56:78:2b:aa") router2 = self._create_dvr_ha_router( self.failover_agent, enable_gw=enable_gw, enable_centralized_fip=enable_centralized_fip, snat_bound_fip=snat_bound_fip, vrrp_id=vrrp_id, ha_port_ip="169.254.192.101", ha_port_mac="12:34:56:78:2b:bb") master, backup = self._get_master_and_slave_routers( router1, router2, check_external_device=False) self._assert_ip_addresses_in_dvr_ha_snat_namespace_with_fip(master) self._assert_no_ip_addresses_in_dvr_ha_snat_namespace_with_fip(backup) self.fail_ha_router(master) utils.wait_until_true(lambda: backup.ha_state == 'master') utils.wait_until_true(lambda: master.ha_state == 'backup') self._assert_ip_addresses_in_dvr_ha_snat_namespace_with_fip(backup) self._assert_no_ip_addresses_in_dvr_ha_snat_namespace_with_fip(master) def _test_dvr_ha_router_failover(self, enable_gw, vrrp_id=None): self._setup_dvr_ha_agents() self._setup_dvr_ha_bridges() router1 = self._create_dvr_ha_router( self.agent, enable_gw=enable_gw, vrrp_id=vrrp_id, ha_port_ip="169.254.192.102", ha_port_mac="12:34:56:78:2b:cc") router2 = self._create_dvr_ha_router( self.failover_agent, enable_gw, vrrp_id=vrrp_id, ha_port_ip="169.254.192.103", ha_port_mac="12:34:56:78:2b:dd") master, backup = self._get_master_and_slave_routers( router1, router2, check_external_device=False) self._assert_ip_addresses_in_dvr_ha_snat_namespace(master) self._assert_no_ip_addresses_in_dvr_ha_snat_namespace(backup) self.fail_ha_router(master) utils.wait_until_true(lambda: backup.ha_state == 'master') utils.wait_until_true(lambda: master.ha_state == 'backup') self._assert_ip_addresses_in_dvr_ha_snat_namespace(backup) self._assert_no_ip_addresses_in_dvr_ha_snat_namespace(master) def test_dvr_ha_router_failover_with_gw(self): self._test_dvr_ha_router_failover(enable_gw=True, vrrp_id=10) def test_dvr_ha_router_failover_with_gw_and_floatingip(self): self._test_dvr_ha_router_failover_with_gw_and_fip( enable_gw=True, enable_centralized_fip=True, snat_bound_fip=True, vrrp_id=11) def test_dvr_ha_router_failover_without_gw(self): self._test_dvr_ha_router_failover(enable_gw=False, vrrp_id=12) def test_dvr_non_ha_router_update(self): self._setup_dvr_ha_agents() self._setup_dvr_ha_bridges() router1 = self._create_dvr_ha_router( self.agent, vrrp_id=13, ha_port_ip="169.254.192.104", ha_port_mac="12:34:56:78:2b:ee") router2 = self._create_dvr_ha_router( self.failover_agent, ha_interface=False, vrrp_id=13, ha_port_ip="169.254.192.105", ha_port_mac="12:34:56:78:2b:ff") r1_chsfr = mock.patch.object(self.agent, 'check_ha_state_for_router').start() r2_chsfr = mock.patch.object(self.failover_agent, 'check_ha_state_for_router').start() utils.wait_until_true(lambda: router1.ha_state == 'master') self.agent._process_updated_router(router1.router) self.assertTrue(r1_chsfr.called) self.failover_agent._process_updated_router(router2.router) self.assertFalse(r2_chsfr.called) def _setup_dvr_router_static_routes( self, router_namespace=True, check_fpr_int_rule_delete=False, enable_ha=False): """Test to validate the extra routes on dvr routers.""" self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info( enable_snat=True, enable_ha=enable_ha) router1 = self.manage_router(self.agent, router_info) self.assertTrue(self._namespace_exists(router1.ns_name)) self._assert_snat_namespace_exists(router1) fip_ns_name = router1.fip_ns.get_name() self.assertTrue(self._namespace_exists(fip_ns_name)) snat_ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name( router1.router_id) if router_namespace: router1.router['routes'] = [{'destination': '8.8.4.0/24', 'nexthop': '35.4.0.20'}] else: router1.router['routes'] = [{'destination': '8.8.4.0/24', 'nexthop': '19.4.4.10'}] self.agent._process_updated_router(router1.router) router_updated = self.agent.router_info[router_info['id']] if router_namespace: self._assert_extra_routes(router_updated) if not enable_ha: self._assert_extra_routes(router_updated, namespace=snat_ns_name) else: rtr_2_fip, fip_2_rtr = router_updated.rtr_fip_subnet.get_pair() # Now get the table index based on the fpr-interface ip. router_fip_table_idx = router_updated._get_snat_idx(fip_2_rtr) self._assert_extra_routes_for_fipns( router_updated, router_fip_table_idx) self._assert_extra_routes(router_updated, namespace=snat_ns_name) if check_fpr_int_rule_delete: router_updated.router[lib_constants.FLOATINGIP_KEY] = [] router_updated.router['gw_port'] = "" router_updated.router['gw_port_host'] = "" router_updated.router['external_gateway_info'] = "" self.agent._process_updated_router(router_updated.router) new_router_info = self.agent.router_info[router_updated.router_id] self.assertTrue(self._namespace_exists(fip_ns_name)) self._assert_extra_routes_for_fipns( new_router_info, router_fip_table_idx, check_fpr_int_rule_delete=check_fpr_int_rule_delete) def _assert_extra_routes_for_fipns(self, router, router_fip_table_idx, check_fpr_int_rule_delete=False): fip_ns_name = router.fip_ns.get_name() self.assertTrue(self._namespace_exists(fip_ns_name)) fg_port = router.fip_ns.agent_gateway_port fg_port_name = router.fip_ns.get_ext_device_name(fg_port['id']) fip_ns_int_name = router.fip_ns.get_int_device_name(router.router_id) fg_device = ip_lib.IPDevice(fg_port_name, namespace=fip_ns_name) tbl_filter = ['table', router_fip_table_idx] if not check_fpr_int_rule_delete: self.assertIn('gateway', fg_device.route.get_gateway( filters=tbl_filter)) else: self.assertIsNone(fg_device.route.get_gateway(filters=tbl_filter)) ip_rule = ip_lib.IPRule(namespace=fip_ns_name) ext_net_fw_rules_list = ip_rule.rule.list_rules( lib_constants.IP_VERSION_4) if not check_fpr_int_rule_delete: # When floatingip are associated, make sure that the # corresponding rules and routes in route table are created # for the router. expected_rule = {u'from': '0.0.0.0/0', u'iif': fip_ns_int_name, 'priority': str(router_fip_table_idx), 'table': str(router_fip_table_idx), 'type': 'unicast'} for rule in ext_net_fw_rules_list: rule_tbl = rule['table'] if rule_tbl in ['default', 'local', 'main']: continue if rule_tbl == str(router_fip_table_idx): self.assertEqual(expected_rule, rule) # Now check the routes in the table. destination = router.router['routes'][0]['destination'] next_hop = router.router['routes'][0]['nexthop'] actual_routes = fg_device.route.list_routes( ip_version=lib_constants.IP_VERSION_4, table=router_fip_table_idx, via=str(next_hop)) expected_extra_route = [{'cidr': six.u(destination), 'dev': fg_port_name, 'table': router_fip_table_idx, 'via': next_hop}] self.assertEqual(expected_extra_route, actual_routes) else: # When floatingip are deleted or disassociated, make sure that the # corresponding rules and routes are cleared from the table # corresponding to the router. self.assertEqual(3, len(ext_net_fw_rules_list)) rule_exist = False for rule in ext_net_fw_rules_list: rule_tbl = rule['table'] if rule_tbl not in ['default', 'local', 'main']: rule_exist = True self.assertFalse(rule_exist) tbl_routes = fg_device.route.list_routes( ip_version=lib_constants.IP_VERSION_4, table=router_fip_table_idx) self.assertEqual([], tbl_routes) def test_dvr_router_static_routes_in_fip_and_snat_namespace(self): self._setup_dvr_router_static_routes(router_namespace=False) def test_dvr_router_static_routes_in_snat_namespace_and_router_namespace( self): self._setup_dvr_router_static_routes() def test_dvr_ha_rtr_static_routes_in_rtr_namespace(self): self._setup_dvr_router_static_routes(enable_ha=True) def test_dvr_router_rule_and_route_table_cleared_when_fip_removed( self): self._setup_dvr_router_static_routes( router_namespace=False, check_fpr_int_rule_delete=True) def _assert_fip_namespace_interface_static_routes( self, address_scopes, fpr_device, router_info, rtr_2_fip, fpr_device_name): fixed_ips_1 = router_info[lib_constants.INTERFACE_KEY][0]['fixed_ips'] fixed_ips_2 = router_info[lib_constants.INTERFACE_KEY][1]['fixed_ips'] actual_routes = fpr_device.route.list_routes( ip_version=lib_constants.IP_VERSION_4, table='main', via=str(rtr_2_fip.ip)) if not address_scopes: self.assertEqual([], actual_routes) if address_scopes: cidr1 = ( str(fixed_ips_1[0]['ip_address']) + '/' + str(fixed_ips_1[0]['prefixlen'])) cidr2 = ( str(fixed_ips_2[0]['ip_address']) + '/' + str(fixed_ips_2[0]['prefixlen'])) net_addr_1 = netaddr.IPNetwork(cidr1).network net_addr_2 = netaddr.IPNetwork(cidr2).network route_cidr_1 = ( str(net_addr_1) + '/' + str(fixed_ips_1[0]['prefixlen'])) route_cidr_2 = ( str(net_addr_2) + '/' + str(fixed_ips_2[0]['prefixlen'])) expected_routes = [{'dev': fpr_device_name, 'cidr': six.u(route_cidr_1), 'via': str(rtr_2_fip.ip), 'table': 'main'}, {'dev': fpr_device_name, 'cidr': six.u(route_cidr_2), 'via': str(rtr_2_fip.ip), 'table': 'main'}] # Comparing the static routes for both internal interfaces on the # main table. self.assertEqual(expected_routes, actual_routes) else: self.assertEqual([], actual_routes) def _assert_interface_rules_on_gateway_remove( self, router, agent, address_scopes, agent_gw_port, rfp_device, fpr_device, no_external=False): router.router[n_const.SNAT_ROUTER_INTF_KEY] = [] router.router['gw_port'] = "" router.router['gw_port_host'] = "" self.agent._process_updated_router(router.router) router_updated = self.agent.router_info[router.router['id']] self.assertTrue(self._namespace_exists(router_updated.ns_name)) if not no_external: self.assertFalse(rfp_device.exists()) self.assertFalse(fpr_device.exists()) self._assert_fip_namespace_deleted( agent_gw_port, assert_ovs_interface=False) if not address_scopes or no_external: ns_ipr = ip_lib.IPRule(namespace=router_updated.ns_name) ip4_rules_list = ns_ipr.rule.list_rules(lib_constants.IP_VERSION_4) ip6_rules_list = ns_ipr.rule.list_rules(lib_constants.IP_VERSION_6) self.assertEqual(3, len(ip4_rules_list)) self.assertEqual(2, len(ip6_rules_list)) def _setup_dvr_router_for_fast_path_exit(self, address_scopes=True): """Test to validate the fip and router namespace routes. This test validates the fip and router namespace routes that are based on the address scopes. If the address scopes of internal network and external network matches, the traffic will be forwarded to the fip namespace and the reverse traffic to the private network is forwarded to the router namespace. """ self.agent.conf.agent_mode = 'dvr' router_info = self.generate_dvr_router_info( enable_snat=True, enable_gw=True, enable_floating_ip=True) router_info[lib_constants.FLOATINGIP_KEY] = [] if address_scopes: address_scope1 = { str(lib_constants.IP_VERSION_4): 'scope1'} address_scope2 = { str(lib_constants.IP_VERSION_4): 'scope1'} else: address_scope1 = { str(lib_constants.IP_VERSION_4): 'scope2'} address_scope2 = { str(lib_constants.IP_VERSION_4): 'scope2'} router_info['gw_port']['address_scopes'] = { str(lib_constants.IP_VERSION_4): 'scope1'} router_info[lib_constants.INTERFACE_KEY][0]['address_scopes'] = ( address_scope1) router_info[lib_constants.INTERFACE_KEY][1]['address_scopes'] = ( address_scope2) # should have the same address_scopes as gw_port fip_agent_gw_ports = router_info[n_const.FLOATINGIP_AGENT_INTF_KEY] fip_agent_gw_ports[0]['address_scopes'] = ( router_info['gw_port']['address_scopes']) self.mock_plugin_api.get_agent_gateway_port.return_value = ( fip_agent_gw_ports[0]) router1 = self.manage_router(self.agent, router_info) fip_ns_name = router1.fip_ns.get_name() self.assertTrue(self._namespace_exists(router1.ns_name)) self.assertTrue(self._namespace_exists(fip_ns_name)) # Check the router namespace for default route. rfp_device_name = router1.fip_ns.get_rtr_ext_device_name( router1.router_id) rfp_device = ip_lib.IPDevice(rfp_device_name, namespace=router1.ns_name) fpr_device_name = router1.fip_ns.get_int_device_name(router1.router_id) fpr_device = ip_lib.IPDevice(fpr_device_name, namespace=fip_ns_name) rtr_2_fip, fip_2_rtr = router1.rtr_fip_subnet.get_pair() self._assert_default_gateway( fip_2_rtr, rfp_device, rfp_device_name) # Check if any snat redirect rules in the router namespace exist. ns_ipr = ip_lib.IPRule(namespace=router1.ns_name) ip4_rules_list = ns_ipr.rule.list_rules(lib_constants.IP_VERSION_4) ip6_rules_list = ns_ipr.rule.list_rules(lib_constants.IP_VERSION_6) # Just make sure the basic set of rules are there in the router # namespace self.assertEqual(5, len(ip4_rules_list)) self.assertEqual(2, len(ip6_rules_list)) # Now check the fip namespace static routes for reaching the private # network. self._assert_fip_namespace_interface_static_routes( address_scopes, fpr_device, router_info, rtr_2_fip, fpr_device_name) # Now remove the gateway and validate if the respective interface # routes in router namespace is deleted respectively. self. _assert_interface_rules_on_gateway_remove( router1, self.agent, address_scopes, fip_agent_gw_ports[0], rfp_device, fpr_device) def test_dvr_fip_and_router_namespace_rules_with_address_scopes_match( self): self._setup_dvr_router_for_fast_path_exit(address_scopes=True) def test_dvr_fip_and_router_namespace_rules_with_address_scopes_mismatch( self): self._setup_dvr_router_for_fast_path_exit(address_scopes=False) @mock.patch.object(dvr_local_router.DvrLocalRouter, '_add_interface_routing_rule_to_router_ns') @mock.patch.object(dvr_local_router.DvrLocalRouter, '_add_interface_route_to_fip_ns') def test_dvr_no_external_router_namespace_rules_with_address_scopes_match( self, mock_add_interface_route_rule, mock_add_fip_interface_route_rule): """Test to validate the router namespace routes. This test validates the router namespace routes that are based on the address scopes. If the address scopes of internal network and external network matches, the traffic will be forwarded to SNAT namespace for agents that don't have external connectivity or configured as DVR_NO_EXTERNAL. """ self.agent.conf.agent_mode = ( lib_constants.L3_AGENT_MODE_DVR_NO_EXTERNAL) router_info = self.generate_dvr_router_info( enable_snat=True, enable_gw=True, enable_floating_ip=True) router_info[lib_constants.FLOATINGIP_KEY] = [] address_scope1 = { str(lib_constants.IP_VERSION_4): 'scope1'} address_scope2 = { str(lib_constants.IP_VERSION_4): 'scope1'} router_info['gw_port']['address_scopes'] = { str(lib_constants.IP_VERSION_4): 'scope1'} router_info[lib_constants.INTERFACE_KEY][0]['address_scopes'] = ( address_scope1) router_info[lib_constants.INTERFACE_KEY][1]['address_scopes'] = ( address_scope2) router1 = self.manage_router(self.agent, router_info) self.assertTrue(self._namespace_exists(router1.ns_name)) self.assertFalse(mock_add_interface_route_rule.called) self.assertFalse(mock_add_fip_interface_route_rule.called) # Check if any snat redirect rules in the router namespace exist. ns_ipr = ip_lib.IPRule(namespace=router1.ns_name) ip4_rules_list = ns_ipr.rule.list_rules(lib_constants.IP_VERSION_4) ip6_rules_list = ns_ipr.rule.list_rules(lib_constants.IP_VERSION_6) # Just make sure the basic set of rules are there in the router # namespace self.assertEqual(5, len(ip4_rules_list)) self.assertEqual(2, len(ip6_rules_list)) # Now remove the gateway and validate if the respective interface # routes in router namespace is deleted respectively. self. _assert_interface_rules_on_gateway_remove( router1, self.agent, True, mock.ANY, mock.ANY, mock.ANY, True) def test_dvr_router_gateway_update_to_none(self): self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info(enable_snat=True) router = self.manage_router(self.agent, router_info) gw_port = router.get_ex_gw_port() ex_gw_port_name = router.get_external_device_name(gw_port['id']) ex_gw_device = ip_lib.IPDevice(ex_gw_port_name, namespace=router.snat_namespace.name) fg_port = router.fip_ns.agent_gateway_port fg_port_name = router.fip_ns.get_ext_device_name(fg_port['id']) fg_device = ip_lib.IPDevice(fg_port_name, namespace=router.fip_ns.name) rtr_2_fip, fip_2_rtr = router.rtr_fip_subnet.get_pair() tbl_index = router._get_snat_idx(fip_2_rtr) self.assertIn('gateway', ex_gw_device.route.get_gateway()) tbl_filter = ['table', tbl_index] self.assertIn('gateway', fg_device.route.get_gateway( filters=tbl_filter)) # Make this copy to make agent think gw_port changed. router.ex_gw_port = copy.deepcopy(router.ex_gw_port) for subnet in gw_port['subnets']: subnet['gateway_ip'] = None new_fg_port = copy.deepcopy(fg_port) for subnet in new_fg_port['subnets']: subnet['gateway_ip'] = None router.router[n_const.FLOATINGIP_AGENT_INTF_KEY] = [new_fg_port] router.process() self.assertIsNone(ex_gw_device.route.get_gateway()) self.assertIsNone(fg_device.route.get_gateway()) def _assert_fip_namespace_deleted( self, ext_gateway_port, assert_ovs_interface=True): ext_net_id = ext_gateway_port['network_id'] fip_ns = self.agent.get_fip_ns(ext_net_id) fip_ns.unsubscribe = mock.Mock() self.agent.fipnamespace_delete_on_ext_net( self.agent.context, ext_net_id) if assert_ovs_interface: self._assert_interfaces_deleted_from_ovs() fip_ns_name = fip_ns.get_name() self.assertFalse(self._namespace_exists(fip_ns_name)) self.assertTrue(fip_ns.destroyed) self.assertTrue(fip_ns.unsubscribe.called) def _setup_address_scope(self, internal_address_scope1, internal_address_scope2, gw_address_scope=None): router_info = self.generate_dvr_router_info(enable_snat=True) address_scope1 = { str(lib_constants.IP_VERSION_4): internal_address_scope1} address_scope2 = { str(lib_constants.IP_VERSION_4): internal_address_scope2} if gw_address_scope: router_info['gw_port']['address_scopes'] = { str(lib_constants.IP_VERSION_4): gw_address_scope} fip_agent_gw_ports = router_info[ n_const.FLOATINGIP_AGENT_INTF_KEY] fip_agent_gw_ports[0]['address_scopes'] = router_info['gw_port'][ 'address_scopes'] router_info[lib_constants.INTERFACE_KEY][0]['address_scopes'] = ( address_scope1) router_info[lib_constants.INTERFACE_KEY][1]['address_scopes'] = ( address_scope2) # Renew the address scope router_info[n_const.SNAT_ROUTER_INTF_KEY] = [] self._add_snat_port_info_to_router( router_info, router_info[lib_constants.INTERFACE_KEY]) router = self.manage_router(self.agent, router_info) router_ip_cidr1 = self._port_first_ip_cidr(router.internal_ports[0]) router_ip1 = router_ip_cidr1.partition('/')[0] router_ip_cidr2 = self._port_first_ip_cidr(router.internal_ports[1]) router_ip2 = router_ip_cidr2.partition('/')[0] br_int = framework.get_ovs_bridge( self.agent.conf.ovs_integration_bridge) test_machine1 = self.useFixture( machine_fixtures.FakeMachine( br_int, net_helpers.increment_ip_cidr(router_ip_cidr1, 10), router_ip1)) test_machine2 = self.useFixture( machine_fixtures.FakeMachine( br_int, net_helpers.increment_ip_cidr(router_ip_cidr2, 10), router_ip2)) return test_machine1, test_machine2, router def test_connection_from_same_address_scope(self): self.agent.conf.agent_mode = 'dvr_snat' test_machine1, test_machine2, _ = self._setup_address_scope( 'scope1', 'scope1') # Internal networks that are in the same address scope can connected # each other net_helpers.assert_ping(test_machine1.namespace, test_machine2.ip) net_helpers.assert_ping(test_machine2.namespace, test_machine1.ip) def test_connection_from_diff_address_scope(self): self.agent.conf.agent_mode = 'dvr_snat' test_machine1, test_machine2, _ = self._setup_address_scope( 'scope1', 'scope2') # Internal networks that are not in the same address scope should # not reach each other test_machine1.assert_no_ping(test_machine2.ip) test_machine2.assert_no_ping(test_machine1.ip) @testtools.skip('bug/1543885') def test_fip_connection_for_address_scope(self): self.agent.conf.agent_mode = 'dvr_snat' (machine_same_scope, machine_diff_scope, router) = self._setup_address_scope('scope1', 'scope2', 'scope1') router.router[lib_constants.FLOATINGIP_KEY] = [] fip_same_scope = '19.4.4.10' self._add_fip(router, fip_same_scope, fixed_address=machine_same_scope.ip, host=self.agent.conf.host, fixed_ip_address_scope='scope1') fip_diff_scope = '19.4.4.11' self._add_fip(router, fip_diff_scope, fixed_address=machine_diff_scope.ip, host=self.agent.conf.host, fixed_ip_address_scope='scope2') router.process() br_ex = framework.get_ovs_bridge( self.agent.conf.external_network_bridge) src_machine = self.useFixture( machine_fixtures.FakeMachine(br_ex, '19.4.4.12/24')) # Floating ip should work no matter of address scope net_helpers.assert_ping(src_machine.namespace, fip_same_scope) net_helpers.assert_ping(src_machine.namespace, fip_diff_scope) def test_direct_route_for_address_scope(self): self.agent.conf.agent_mode = 'dvr_snat' (machine_same_scope, machine_diff_scope, router) = self._setup_address_scope('scope1', 'scope2', 'scope1') gw_port = router.get_ex_gw_port() gw_ip = self._port_first_ip_cidr(gw_port).partition('/')[0] br_ex = framework.get_ovs_bridge( self.agent.conf.external_network_bridge) src_machine = self.useFixture( machine_fixtures.FakeMachine(br_ex, '19.4.4.12/24', gw_ip)) # For the internal networks that are in the same address scope as # external network, they can directly route to external network net_helpers.assert_ping(src_machine.namespace, machine_same_scope.ip) # For the internal networks that are not in the same address scope as # external networks. SNAT will be used. Direct route will not work # here. src_machine.assert_no_ping(machine_diff_scope.ip) def test_dvr_snat_namespace_has_ip_nonlocal_bind_disabled(self): self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info( enable_ha=True, enable_snat=True) router = self.manage_router(self.agent, router_info) try: ip_nonlocal_bind_value = ip_lib.get_ip_nonlocal_bind( router.snat_namespace.name) except RuntimeError as rte: stat_message = 'cannot stat /proc/sys/net/ipv4/ip_nonlocal_bind' if stat_message in str(rte): raise self.skipException( "This kernel doesn't support %s in network namespaces." % ( ip_lib.IP_NONLOCAL_BIND)) raise self.assertEqual(0, ip_nonlocal_bind_value) def test_dvr_router_fip_namespace_routes(self): """Test to validate the floatingip namespace subnets routes.""" self.agent.conf.agent_mode = 'dvr' router_info = self.generate_dvr_router_info(enable_floating_ip=False) fip_agent_gw_port = self._get_fip_agent_gw_port_for_router( router_info['gw_port']) self.mock_plugin_api.get_agent_gateway_port.return_value = ( fip_agent_gw_port) router1 = self.manage_router(self.agent, router_info) fip_namespace = router1.fip_ns.get_name() ip_wrapper = ip_lib.IPWrapper(namespace=fip_namespace) interfaces = ip_wrapper.get_devices() fg_interface_name = next( interface.name for interface in interfaces if interface.name.startswith(dvr_fip_ns.FIP_EXT_DEV_PREFIX)) ip_device = ip_lib.IPDevice(fg_interface_name, namespace=fip_namespace) routes = ip_device.route.list_onlink_routes(lib_constants.IP_VERSION_4) self.assertGreater(len(routes), 0) self.assertEqual(len(fip_agent_gw_port['extra_subnets']), len(routes)) extra_subnet_cidr = set(extra_subnet['cidr'] for extra_subnet in fip_agent_gw_port['extra_subnets']) routes_cidr = set(route['cidr'] for route in routes) self.assertEqual(extra_subnet_cidr, routes_cidr) neutron-12.1.1/neutron/tests/functional/agent/l3/test_metadata_proxy.py0000664000175000017500000001740113553660047026370 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import os.path import time import fixtures from oslo_config import cfg import webob import webob.dec import webob.exc from neutron.agent.linux import dhcp from neutron.agent.linux import external_process from neutron.agent.linux import utils from neutron.tests.common import machine_fixtures from neutron.tests.common import net_helpers from neutron.tests.functional.agent.l3 import framework from neutron.tests.functional.agent.linux import helpers from neutron.tests.functional.agent.linux import simple_daemon METADATA_REQUEST_TIMEOUT = 60 METADATA_REQUEST_SLEEP = 5 class MetadataFakeProxyHandler(object): def __init__(self, status): self.status = status @webob.dec.wsgify() def __call__(self, req): return webob.Response(status=self.status) class MetadataL3AgentTestCase(framework.L3AgentTestFramework): SOCKET_MODE = 0o644 def _create_metadata_fake_server(self, status): server = utils.UnixDomainWSGIServer('metadata-fake-server') self.addCleanup(server.stop) # NOTE(cbrandily): TempDir fixture creates a folder with 0o700 # permissions but metadata_proxy_socket folder must be readable by all # users self.useFixture( helpers.RecursivePermDirFixture( os.path.dirname(self.agent.conf.metadata_proxy_socket), 0o555)) server.start(MetadataFakeProxyHandler(status), self.agent.conf.metadata_proxy_socket, workers=0, backlog=4096, mode=self.SOCKET_MODE) def _query_metadata_proxy(self, machine): url = 'http://%(host)s:%(port)s' % {'host': dhcp.METADATA_DEFAULT_IP, 'port': dhcp.METADATA_PORT} cmd = 'curl', '--max-time', METADATA_REQUEST_TIMEOUT, '-D-', url i = 0 CONNECTION_REFUSED_TIMEOUT = METADATA_REQUEST_TIMEOUT // 2 while i <= CONNECTION_REFUSED_TIMEOUT: try: raw_headers = machine.execute(cmd) break except RuntimeError as e: if 'Connection refused' in str(e): time.sleep(METADATA_REQUEST_SLEEP) i += METADATA_REQUEST_SLEEP else: self.fail('metadata proxy unreachable ' 'on %s before timeout' % url) if i > CONNECTION_REFUSED_TIMEOUT: self.fail('Timed out waiting metadata proxy to become available') return raw_headers.splitlines()[0] def test_access_to_metadata_proxy(self): """Test access to the l3-agent metadata proxy. The test creates: * A l3-agent metadata service: * A router (which creates a metadata proxy in the router namespace), * A fake metadata server * A "client" namespace (simulating a vm) with a port on router internal subnet. The test queries from the "client" namespace the metadata proxy on http://169.254.169.254 and asserts that the metadata proxy added the X-Forwarded-For and X-Neutron-Router-Id headers to the request and forwarded the http request to the fake metadata server and the response to the "client" namespace. """ router_info = self.generate_router_info(enable_ha=False) router = self.manage_router(self.agent, router_info) self._create_metadata_fake_server(webob.exc.HTTPOk.code) # Create and configure client namespace router_ip_cidr = self._port_first_ip_cidr(router.internal_ports[0]) br_int = framework.get_ovs_bridge( self.agent.conf.ovs_integration_bridge) machine = self.useFixture( machine_fixtures.FakeMachine( br_int, net_helpers.increment_ip_cidr(router_ip_cidr), router_ip_cidr.partition('/')[0])) # Query metadata proxy firstline = self._query_metadata_proxy(machine) # Check status code self.assertIn(str(webob.exc.HTTPOk.code), firstline.split()) @staticmethod def _make_cmdline_callback(uuid): def _cmdline_callback(pidfile): cmdline = ["python", simple_daemon.__file__, "--uuid=%s" % uuid, "--pid_file=%s" % pidfile] return cmdline return _cmdline_callback def test_haproxy_migration_path(self): """Test the migration path for haproxy. This test will launch the simple_daemon Python process before spawning haproxy. When launching haproxy, it will be detected and killed, as it's running on the same pidfile and with the router uuid in its cmdline. """ # Make sure that external_pids configuration option is the same for # simple_daemon and haproxy so that both work on the same pid_file. get_temp_file_path = functools.partial( self.get_temp_file_path, root=self.useFixture(fixtures.TempDir())) cfg.CONF.set_override('external_pids', get_temp_file_path('external/pids')) self.agent.conf.set_override('external_pids', get_temp_file_path('external/pids')) router_info = self.generate_router_info(enable_ha=False) # Spawn the simple_daemon process in the background using the generated # router uuid. We are not registering it within ProcessMonitor so that # it doesn't get respawned once killed. _callback = self._make_cmdline_callback(router_info['id']) pm = external_process.ProcessManager( conf=cfg.CONF, uuid=router_info['id'], default_cmd_callback=_callback) pm.enable() self.addCleanup(pm.disable) # Make sure that simple_daemon is running self.assertIn('simple_daemon', pm.cmdline) # Create the router. This is expected to launch haproxy after killing # the simple_daemon process. self.manage_router(self.agent, router_info) # Make sure that it was killed and replaced by haproxy self.assertNotIn('simple_daemon', pm.cmdline) self.assertIn('haproxy', pm.cmdline) class UnprivilegedUserMetadataL3AgentTestCase(MetadataL3AgentTestCase): """Test metadata proxy with least privileged user. The least privileged user has uid=65534 and is commonly named 'nobody' but not always, that's why we use its uid. """ SOCKET_MODE = 0o664 def setUp(self): super(UnprivilegedUserMetadataL3AgentTestCase, self).setUp() self.agent.conf.set_override('metadata_proxy_user', '65534') class UnprivilegedUserGroupMetadataL3AgentTestCase(MetadataL3AgentTestCase): """Test metadata proxy with least privileged user/group. The least privileged user has uid=65534 and is commonly named 'nobody' but not always, that's why we use its uid. Its group has gid=65534 and is commonly named 'nobody' or 'nogroup', that's why we use its gid. """ SOCKET_MODE = 0o666 def setUp(self): super(UnprivilegedUserGroupMetadataL3AgentTestCase, self).setUp() self.agent.conf.set_override('metadata_proxy_user', '65534') self.agent.conf.set_override('metadata_proxy_group', '65534') neutron-12.1.1/neutron/tests/functional/agent/l3/__init__.py0000664000175000017500000000000013553660046024031 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/agent/l3/test_namespace_manager.py0000664000175000017500000000677713553660046027012 0ustar zuulzuul00000000000000# Copyright (c) 2015 Rackspace # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg from oslo_utils import uuidutils from neutron.agent.l3 import dvr_snat_ns from neutron.agent.l3 import namespace_manager from neutron.agent.l3 import namespaces from neutron.agent.linux import ip_lib from neutron.tests.functional import base _uuid = uuidutils.generate_uuid class NamespaceManagerTestFramework(base.BaseSudoTestCase): def setUp(self): super(NamespaceManagerTestFramework, self).setUp() self.agent_conf = cfg.CONF self.metadata_driver_mock = mock.Mock() self.namespace_manager = namespace_manager.NamespaceManager( self.agent_conf, driver=None, metadata_driver=self.metadata_driver_mock) def _create_namespace(self, router_id, ns_class): namespace = ns_class(router_id, self.agent_conf, driver=None, use_ipv6=False) namespace.create() self.addCleanup(self._delete_namespace, namespace) return namespace.name def _delete_namespace(self, namespace): try: namespace.delete() except RuntimeError as e: # If the namespace didn't exist when delete was attempted, mission # accomplished. Otherwise, re-raise the exception if 'No such file or directory' not in str(e): raise e def _namespace_exists(self, namespace): return ip_lib.network_namespace_exists(namespace) class NamespaceManagerTestCase(NamespaceManagerTestFramework): def test_namespace_manager(self): router_id = _uuid() router_id_to_delete = _uuid() to_keep = set() to_delete = set() to_retrieve = set() to_keep.add(self._create_namespace(router_id, namespaces.RouterNamespace)) to_keep.add(self._create_namespace(router_id, dvr_snat_ns.SnatNamespace)) to_delete.add(self._create_namespace(router_id_to_delete, dvr_snat_ns.SnatNamespace)) to_retrieve = to_keep | to_delete with mock.patch.object(namespace_manager.NamespaceManager, 'list_all', return_value=to_retrieve): with self.namespace_manager as ns_manager: for ns_name in to_keep: id_to_keep = ns_manager.get_prefix_and_id(ns_name)[1] ns_manager.keep_router(id_to_keep) for ns_name in to_keep: self.assertTrue(self._namespace_exists(ns_name)) for ns_name in to_delete: (self.metadata_driver_mock.destroy_monitored_metadata_proxy. assert_called_once_with(mock.ANY, router_id_to_delete, self.agent_conf, ns_name)) self.assertFalse(self._namespace_exists(ns_name)) neutron-12.1.1/neutron/tests/functional/requirements.txt0000664000175000017500000000041413553660046023601 0ustar zuulzuul00000000000000# Additional requirements for functional tests # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. psycopg2 neutron-12.1.1/neutron/tests/functional/constants.py0000664000175000017500000000111713553660046022704 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. VLAN_COUNT = 4096 neutron-12.1.1/neutron/tests/functional/base.py0000664000175000017500000000675413553660047021617 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import mock from oslo_config import cfg from neutron.agent.linux import utils from neutron.conf.agent import common as config from neutron.conf.agent import ovs_conf from neutron.tests import base from neutron.tests.common import base as common_base from neutron.tests.common import helpers # This is the directory from which infra fetches log files for functional tests DEFAULT_LOG_DIR = os.path.join(helpers.get_test_log_path(), 'dsvm-functional-logs') def config_decorator(method_to_decorate, config_tuples): def wrapper(*args, **kwargs): method_to_decorate(*args, **kwargs) for config_tuple in config_tuples: cfg.CONF.set_override(*config_tuple) return wrapper class BaseLoggingTestCase(base.BaseTestCase): def setUp(self): super(BaseLoggingTestCase, self).setUp() base.setup_test_logging( cfg.CONF, DEFAULT_LOG_DIR, "%s.txt" % self.id()) class BaseSudoTestCase(BaseLoggingTestCase): """ Base class for tests requiring invocation of commands via a root helper. This class skips (during setUp) its tests unless sudo is enabled, ie: OS_SUDO_TESTING is set to '1' or 'True' in the test execution environment. This is intended to allow developers to run the functional suite (e.g. tox -e functional) without test failures if sudo invocations are not allowed. Running sudo tests in the upstream gate jobs (*-neutron-dsvm-functional) requires the additional step of setting OS_ROOTWRAP_CMD to the rootwrap command configured by devstack, e.g. sudo /usr/local/bin/neutron-rootwrap /etc/neutron/rootwrap.conf Gate jobs do not allow invocations of sudo without rootwrap to ensure that rootwrap configuration gets as much testing as possible. """ def setUp(self): super(BaseSudoTestCase, self).setUp() if not base.bool_from_env('OS_SUDO_TESTING'): self.skipTest('Testing with sudo is not enabled') self.setup_rootwrap() config.setup_privsep() self._override_default_config() @common_base.no_skip_on_missing_deps def check_command(self, cmd, error_text, skip_msg, run_as_root=False): try: utils.execute(cmd, run_as_root=run_as_root) except RuntimeError as e: if error_text in str(e): self.skipTest(skip_msg) raise @staticmethod def _override_default_config(): # NOTE(ralonsoh): once https://review.openstack.org/#/c/641681/ is # merged, we should increase the default value of those new parameters. ovs_agent_opts = [('ovsdb_timeout', 30, 'OVS')] ovs_agent_decorator = config_decorator( ovs_conf.register_ovs_agent_opts, ovs_agent_opts) mock.patch.object(ovs_conf, 'register_ovs_agent_opts', new=ovs_agent_decorator).start() neutron-12.1.1/neutron/tests/functional/cmd/0000775000175000017500000000000013553660156021063 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/cmd/test_ovs_cleanup.py0000664000175000017500000000557013553660047025020 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from neutron.cmd import ovs_cleanup from neutron.common import utils from neutron.conf.agent import cmd from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants from neutron.tests.common import net_helpers from neutron.tests.functional.agent.linux import base class TestOVSCLIConfig(base.BaseOVSLinuxTestCase): def setup_config(self, args=None): self.conf = ovs_cleanup.setup_conf() super(TestOVSCLIConfig, self).setup_config(args=args) def test_ovs_opts_registration(self): self.assertFalse(self.conf.ovs_all_ports) # to unregister opts self.conf.reset() self.conf.unregister_opts(cmd.ovs_opts) def test_do_main_default_options(self): int_br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge ext_br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge self.conf.set_override("ovs_integration_bridge", int_br.br_name) self.conf.set_override("external_network_bridge", ext_br.br_name) self.conf.set_override("ovs_all_ports", False) noskip = collections.defaultdict(list) skip = collections.defaultdict(list) # add two vifs, one skipped, and a non-vif port to int_br and ext_br for br in (int_br, ext_br): for collection in (noskip, skip): collection[br].append( self.useFixture(net_helpers.OVSPortFixture(br)).port.name) # set skippable vif to be skipped br.ovsdb.db_set( 'Interface', skip[br][0], ('external_ids', {constants.SKIP_CLEANUP: "True"}) ).execute(check_error=True) device_name = utils.get_rand_name() skip[br].append(device_name) br.add_port(device_name, ('type', 'internal')) # sanity check for collection in (noskip, skip): for bridge, ports in collection.items(): port_list = bridge.get_port_name_list() for port in ports: self.assertIn(port, port_list) ovs_cleanup.do_main(self.conf) for br in (int_br, ext_br): ports = br.get_port_name_list() for vif in noskip[br]: self.assertNotIn(vif, ports) for port in skip[br]: self.assertIn(port, ports) neutron-12.1.1/neutron/tests/functional/cmd/process_spawn.py0000664000175000017500000001366413553660047024334 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import random import signal import socket import sys import time from neutron_lib import constants as n_const from oslo_config import cfg from neutron.agent.linux import daemon UNIX_FAMILY = 'UNIX' OPTS = [ cfg.IntOpt('num_children', short='n', default=0, help='Number of children to spawn', required=False), cfg.StrOpt('family', short='f', default=n_const.IPv4, choices=[n_const.IPv4, n_const.IPv6, UNIX_FAMILY], help='Listen socket family (%(v4)s, %(v6)s or %(unix)s)' % { 'v4': n_const.IPv4, 'v6': n_const.IPv6, 'unix': UNIX_FAMILY }, required=False), cfg.StrOpt('proto', short='p', default=n_const.PROTO_NAME_TCP, choices=[n_const.PROTO_NAME_TCP, n_const.PROTO_NAME_UDP], help='Protocol (%(tcp)s or %(udp)s)' % { 'tcp': n_const.PROTO_NAME_TCP, 'udp': n_const.PROTO_NAME_UDP }, required=False), cfg.BoolOpt('parent_listen', short='pl', default=True, help='Parent process must listen too', required=False), cfg.BoolOpt('ignore_sigterm', short='i', default=False, help='Ignore SIGTERM', required=False) ] class ProcessSpawn(daemon.Daemon): """ This class is part of the functional test of the netns_cleanup module. It allows spawning processes that listen on random ports either on tcp(6), udp(6) or unix sockets. Also it allows handling or ignoring SIGTERM to check whether the cleanup works as expected by getting rid of the spawned processes. """ MAX_BIND_RETRIES = 64 DCT_FAMILY = { n_const.IPv4: socket.AF_INET, n_const.IPv6: socket.AF_INET6, UNIX_FAMILY: socket.AF_UNIX } DCT_PROTO = { n_const.PROTO_NAME_TCP: socket.SOCK_STREAM, n_const.PROTO_NAME_UDP: socket.SOCK_DGRAM, } def __init__(self, pidfile=None, family=n_const.IPv4, proto=n_const.PROTO_NAME_TCP, ignore_sigterm=False, num_children=0, parent_must_listen=True): self.family = family self.proto = proto self.ignore_sigterm = ignore_sigterm self.num_children = num_children self.listen_socket = None self.parent_must_listen = parent_must_listen self.child_pids = [] super(ProcessSpawn, self).__init__(pidfile) def start_listening(self): socket_family = self.DCT_FAMILY[self.family] socket_type = self.DCT_PROTO[self.proto] self.listen_socket = socket.socket(socket_family, socket_type) # Set a different seed per process to increase randomness random.seed(os.getpid()) # Try to listen in a random port which is not currently in use retries = 0 while retries < ProcessSpawn.MAX_BIND_RETRIES: # NOTE(dalvarez): not finding a free port on a freshly created # namespace is very unlikely but if problems show up, retries can # be increased to avoid tests failing try: if self.family == UNIX_FAMILY: self.listen_socket.bind('') else: # Pick a non privileged port port = random.randint(1024, 65535) self.listen_socket.bind(('', port)) except socket.error: retries += 1 else: if n_const.PROTO_NAME_TCP in self.proto: self.listen_socket.listen(0) break def do_sleep(self): while True: time.sleep(10) def run(self): # Spawn as many children as requested children = [] while len(children) != self.num_children: child_pid = os.fork() if child_pid == 0: # Listen and do nothing else self.start_listening() self.do_sleep() return children.append(child_pid) # Install a SIGTERM handler if requested handler = ( signal.SIG_IGN if self.ignore_sigterm else self.sigterm_handler) signal.signal(signal.SIGTERM, handler) self.child_pids = children if self.parent_must_listen: self.start_listening() self.do_sleep() def sigterm_handler(self, signum, frame): if self.listen_socket: self.listen_socket.close() for child in self.child_pids: try: os.kill(child, signal.SIGTERM) except OSError: pass sys.exit(0) def main(): cfg.CONF.register_cli_opts(OPTS) cfg.CONF(project='neutron', default_config_files=[]) proc_spawn = ProcessSpawn(num_children=cfg.CONF.num_children, family=cfg.CONF.family, proto=cfg.CONF.proto, parent_must_listen=cfg.CONF.parent_listen, ignore_sigterm=cfg.CONF.ignore_sigterm) proc_spawn.start() if __name__ == "__main__": main() neutron-12.1.1/neutron/tests/functional/cmd/test_ipset_cleanup.py0000664000175000017500000000230713553660047025330 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.linux import ipset_manager from neutron.cmd import ipset_cleanup from neutron.conf.agent import cmd from neutron.tests import base class TestIPSetCLIConfig(base.BaseTestCase): def setup_config(self, args=None): self.conf = ipset_cleanup.setup_conf() super(TestIPSetCLIConfig, self).setup_config(args=args) def test_ipset_opts_registration(self): self.assertFalse(self.conf.allsets) self.assertFalse(self.conf.force) self.assertEqual(ipset_manager.NET_PREFIX, self.conf.prefix) # to unregister opts self.conf.reset() self.conf.unregister_opts(cmd.ip_opts) neutron-12.1.1/neutron/tests/functional/cmd/__init__.py0000664000175000017500000000000013553660046023160 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/cmd/test_linuxbridge_cleanup.py0000664000175000017500000000643313553660047026524 0ustar zuulzuul00000000000000# Copyright (c) 2015 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import fixtures import mock from neutron_lib import constants from neutron.agent.linux import ip_lib from neutron.plugins.ml2.drivers.linuxbridge.agent import \ linuxbridge_neutron_agent as lb_agent from neutron.tests.common import config_fixtures from neutron.tests.common import net_helpers from neutron.tests.functional import base from neutron.tests import tools class LinuxbridgeCleanupTest(base.BaseSudoTestCase): def _test_linuxbridge_cleanup(self, bridge_exists, callback): br_fixture = self.useFixture( tools.SafeCleanupFixture( net_helpers.LinuxBridgeFixture( prefix=lb_agent.BRIDGE_NAME_PREFIX))).fixture config = callback(br_fixture) config.update({'VXLAN': {'enable_vxlan': 'False'}}) temp_dir = self.useFixture(fixtures.TempDir()).path conf = self.useFixture(config_fixtures.ConfigFileFixture( base_filename='neutron.conf', config=config, temp_dir=temp_dir)) cmd = 'neutron-linuxbridge-cleanup', '--config-file', conf.filename ip_wrapper = ip_lib.IPWrapper(br_fixture.namespace) ip_wrapper.netns.execute(cmd) self.assertEqual(bridge_exists, ip_lib.device_exists( br_fixture.bridge.name, br_fixture.namespace)) def test_cleanup_empty_bridge(self): def callback(br_fixture): return config_fixtures.ConfigDict() self._test_linuxbridge_cleanup(False, callback) def test_no_cleanup_bridge_with_tap(self): def callback(br_fixture): # TODO(cbrandily): refactor net_helpers to avoid mocking it mock.patch.object( net_helpers, 'VETH0_PREFIX', new_callable=mock.PropertyMock( return_value=constants.TAP_DEVICE_PREFIX + '0')).start() mock.patch.object( net_helpers, 'VETH1_PREFIX', new_callable=mock.PropertyMock( return_value=constants.TAP_DEVICE_PREFIX + '1')).start() self.useFixture( tools.SafeCleanupFixture( net_helpers.LinuxBridgePortFixture( br_fixture.bridge, br_fixture.namespace))) return config_fixtures.ConfigDict() self._test_linuxbridge_cleanup(True, callback) def test_no_cleanup_bridge_in_bridge_mappings(self): def callback(br_fixture): br_name = br_fixture.bridge.name conf = config_fixtures.ConfigDict() conf.update( {'LINUX_BRIDGE': {'bridge_mappings': 'physnet:%s' % br_name}}) return conf self._test_linuxbridge_cleanup(True, callback) neutron-12.1.1/neutron/tests/functional/cmd/test_netns_cleanup.py0000664000175000017500000001457413553660047025344 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import mock from neutron_lib import constants as n_const from neutron.agent.l3 import namespaces from neutron.agent.linux import dhcp from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.cmd import netns_cleanup from neutron.common import utils as common_utils from neutron.conf.agent import cmd from neutron.tests import base as basetest from neutron.tests.common import net_helpers from neutron.tests.functional import base from neutron.tests.functional.cmd import process_spawn GET_NAMESPACES = 'neutron.agent.linux.ip_lib.list_network_namespaces' TEST_INTERFACE_DRIVER = 'neutron.agent.linux.interface.OVSInterfaceDriver' NUM_SUBPROCESSES = 6 class NetnsCleanupTest(base.BaseSudoTestCase): def setUp(self): super(NetnsCleanupTest, self).setUp() self.get_namespaces_p = mock.patch(GET_NAMESPACES) self.get_namespaces = self.get_namespaces_p.start() def setup_config(self, args=None): if args is None: args = [] # force option enabled to make sure non-empty namespaces are # cleaned up and deleted args.append('--force') self.conf = netns_cleanup.setup_conf() self.conf.set_override('interface_driver', TEST_INTERFACE_DRIVER) self.config_parse(conf=self.conf, args=args) def test_cleanup_network_namespaces_cleans_dhcp_and_l3_namespaces(self): dhcp_namespace = self.useFixture( net_helpers.NamespaceFixture(dhcp.NS_PREFIX)).name l3_namespace = self.useFixture( net_helpers.NamespaceFixture(namespaces.NS_PREFIX)).name bridge = self.useFixture( net_helpers.VethPortFixture(namespace=dhcp_namespace)).bridge self.useFixture( net_helpers.VethPortFixture(bridge, l3_namespace)) # we scope the get_namespaces to our own ones not to affect other # tests, as otherwise cleanup will kill them all self.get_namespaces.return_value = [l3_namespace, dhcp_namespace] # launch processes in each namespace to make sure they're # killed during cleanup procs_launched = self._launch_processes([l3_namespace, dhcp_namespace]) self.assertIsNot(procs_launched, 0) common_utils.wait_until_true( lambda: self._get_num_spawned_procs() == procs_launched, timeout=15, exception=Exception("Didn't spawn expected number of processes")) netns_cleanup.cleanup_network_namespaces(self.conf) self.get_namespaces_p.stop() namespaces_now = ip_lib.IPWrapper.get_namespaces() procs_after = self._get_num_spawned_procs() self.assertEqual(procs_after, 0) self.assertNotIn(l3_namespace, namespaces_now) self.assertNotIn(dhcp_namespace, namespaces_now) @staticmethod def _launch_processes(namespaces): """ Launch processes in the specified namespaces. This function will spawn processes inside the given namespaces: - 6 processes listening on tcp ports (parent + 5 children) - 1 process + 5 subprocesses listening on unix sockets - 1 process + 5 subprocesses listening on udp6 sockets First two sets of processes will process SIGTERM so when the parent gets killed, it will kill all spawned children The last set of processes will ignore SIGTERM. This will allow us to test the cleanup functionality which will issue a SIGKILL to all remaining processes after the SIGTERM attempt """ commands = [['python', process_spawn.__file__, '-n', NUM_SUBPROCESSES, '-f', n_const.IPv4, '-p', n_const.PROTO_NAME_TCP, '--noignore_sigterm', '--parent_listen'], ['python', process_spawn.__file__, '-n', NUM_SUBPROCESSES, '-f', process_spawn.UNIX_FAMILY, '-p', n_const.PROTO_NAME_TCP, '--noignore_sigterm', '--noparent_listen'], ['python', process_spawn.__file__, '-n', NUM_SUBPROCESSES, '-f', n_const.IPv4, '-p', n_const.PROTO_NAME_UDP, '--ignore_sigterm', '--noparent_listen']] proc_count = 0 for netns in namespaces: ip = ip_lib.IPWrapper(namespace=netns) for command in commands: # The total amount of processes per command is # the process itself plus the number of subprocesses spawned by # it proc_count += (1 + NUM_SUBPROCESSES) # We need to pass the PATH env variable so that python # interpreter runs under the same virtual environment. # Otherwise, it won't find the necessary packages such as # oslo_config ip.netns.execute(command, addl_env={'PATH': os.environ.get('PATH')}) return proc_count @staticmethod def _get_num_spawned_procs(): cmd = ['ps', '-f', '-u', 'root'] out = utils.execute(cmd, run_as_root=True) return sum([1 for line in out.splitlines() if 'process_spawn' in line]) class TestNETNSCLIConfig(basetest.BaseTestCase): def setup_config(self, args=None): self.conf = netns_cleanup.setup_conf() super(TestNETNSCLIConfig, self).setup_config(args=args) def test_netns_opts_registration(self): self.assertFalse(self.conf.force) self.assertIsNone(self.conf.get('agent_type')) # to unregister opts self.conf.reset() self.conf.unregister_opts(cmd.netns_opts) neutron-12.1.1/neutron/tests/functional/scheduler/0000775000175000017500000000000013553660156022276 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/scheduler/test_l3_agent_scheduler.py0000664000175000017500000007530413553660047027451 0ustar zuulzuul00000000000000# Copyright (c) 2015 Hewlett-Packard Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import random from neutron_lib import constants from neutron_lib import context from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_utils import uuidutils import testscenarios from neutron.objects import network as net_obj from neutron.scheduler import l3_agent_scheduler from neutron.services.l3_router import l3_router_plugin from neutron.tests.common import helpers from neutron.tests.unit.db import test_db_base_plugin_v2 _uuid = uuidutils.generate_uuid PLUGIN_NAME = 'neutron.plugins.ml2.plugin.Ml2Plugin' # Required to generate tests from scenarios. Not compatible with nose. load_tests = testscenarios.load_tests_apply_scenarios class L3SchedulerBaseTest(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): """Base class for functional test of L3 schedulers. Provides basic setup and utility functions. """ def setUp(self): super(L3SchedulerBaseTest, self).setUp(PLUGIN_NAME) self.l3_plugin = l3_router_plugin.L3RouterPlugin() directory.add_plugin(plugin_constants.L3, self.l3_plugin) self.adminContext = context.get_admin_context() self.adminContext.tenant_id = _uuid() def _create_l3_agent(self, host, context, agent_mode='legacy', state=True, ext_net_id=''): agent = helpers.register_l3_agent(host, agent_mode, ext_net_id=ext_net_id) helpers.set_agent_admin_state(agent.id, state) return agent def _create_router(self, name): router = {'name': name, 'admin_state_up': True, 'tenant_id': self.adminContext.tenant_id} return self.l3_plugin.create_router( self.adminContext, {'router': router}) def _create_legacy_agents(self, agent_count, down_agent_count): # Creates legacy l3 agents and sets admin state based on # down agent count. self.hosts = ['host-%s' % i for i in range(agent_count)] self.l3_agents = [self._create_l3_agent(self.hosts[i], self.adminContext, 'legacy', (i >= down_agent_count)) for i in range(agent_count)] def _create_routers(self, scheduled_router_count, expected_scheduled_router_count): routers = [] if (scheduled_router_count + expected_scheduled_router_count): for i in range(scheduled_router_count + expected_scheduled_router_count): router = self._create_router('schd_rtr' + str(i)) routers.append(router) else: # create at least one router to test scheduling routers.append(self._create_router('schd_rtr0')) return routers def _pre_scheduler_routers(self, scheduler, count): hosting_agents = [] # schedule routers before calling schedule: for i in range(count): router = self.routers[i] agent = random.choice(self.l3_agents) scheduler.bind_router(self.l3_plugin, self.adminContext, router['id'], agent.id) hosting_agents.append(agent) return hosting_agents def _test_auto_schedule(self, expected_count): router_ids = [rtr['id'] for rtr in self.routers] hosting_before = self.l3_plugin.get_l3_agents_hosting_routers( self.adminContext, router_ids) # Try scheduling on each host for host in self.hosts: self.scheduler.auto_schedule_routers( self.l3_plugin, self.adminContext, host) hosting_after = self.l3_plugin.get_l3_agents_hosting_routers( self.adminContext, router_ids) if expected_count: self.assertNotEqual(hosting_before, hosting_after, 'Failed to schedule agent') else: self.assertEqual(hosting_before, hosting_after, 'Agent scheduled, not expected') class L3ChanceSchedulerTestCase(L3SchedulerBaseTest): """Test various scenarios for chance scheduler. agent_count Number of l3 agents (also number of hosts). down_agent_count Number of l3 agents which are down. scheduled_router_count Number of routers that have been previously scheduled. expected_scheduled_router_count Number of newly scheduled routers. """ scenarios = [ ('No routers scheduled if no agents are present', dict(agent_count=0, down_agent_count=0, scheduled_router_count=0, expected_scheduled_router_count=0)), ('No routers scheduled if it is already hosted', dict(agent_count=1, down_agent_count=0, scheduled_router_count=1, expected_scheduled_router_count=0)), ('No routers scheduled if all agents are down', dict(agent_count=2, down_agent_count=2, scheduled_router_count=0, expected_scheduled_router_count=0)), ('Router scheduled to the agent if router is not yet hosted', dict(agent_count=1, down_agent_count=0, scheduled_router_count=0, expected_scheduled_router_count=1)), ('Router scheduled to the agent even if it already hosts a router', dict(agent_count=1, down_agent_count=0, scheduled_router_count=1, expected_scheduled_router_count=1)), ] def setUp(self): super(L3ChanceSchedulerTestCase, self).setUp() self._create_legacy_agents(self.agent_count, self.down_agent_count) self.routers = self._create_routers(self.scheduled_router_count, self.expected_scheduled_router_count) self.scheduler = l3_agent_scheduler.ChanceScheduler() def test_chance_schedule_router(self): # Pre schedule routers self._pre_scheduler_routers(self.scheduler, self.scheduled_router_count) # schedule: actual_scheduled_agent = self.scheduler.schedule( self.l3_plugin, self.adminContext, self.routers[-1]['id']) if self.expected_scheduled_router_count: self.assertIsNotNone(actual_scheduled_agent, message='Failed to schedule agent') else: self.assertIsNone(actual_scheduled_agent, message='Agent scheduled but not expected') def test_auto_schedule_routers(self): # Pre schedule routers self._pre_scheduler_routers(self.scheduler, self.scheduled_router_count) # The test self._test_auto_schedule(self.expected_scheduled_router_count) class L3LeastRoutersSchedulerTestCase(L3SchedulerBaseTest): """Test various scenarios for least router scheduler. agent_count Number of l3 agents (also number of hosts). down_agent_count Number of l3 agents which are down. scheduled_router_count Number of routers that have been previously scheduled expected_scheduled_router_count Number of newly scheduled routers """ scenarios = [ ('No routers scheduled if no agents are present', dict(agent_count=0, down_agent_count=0, scheduled_router_count=0, expected_scheduled_router_count=0)), ('No routers scheduled if it is already hosted', dict(agent_count=1, down_agent_count=0, scheduled_router_count=1, expected_scheduled_router_count=1)), ('No routers scheduled if all agents are down', dict(agent_count=2, down_agent_count=2, scheduled_router_count=0, expected_scheduled_router_count=0)), ('Router scheduled to the agent if router is not yet hosted', dict(agent_count=1, down_agent_count=0, scheduled_router_count=0, expected_scheduled_router_count=1)), ('Router scheduled to the agent even if it already hosts a router', dict(agent_count=1, down_agent_count=0, scheduled_router_count=1, expected_scheduled_router_count=1)), ('Router is scheduled to agent hosting least routers', dict(agent_count=2, down_agent_count=0, scheduled_router_count=1, expected_scheduled_router_count=1)), ] def setUp(self): super(L3LeastRoutersSchedulerTestCase, self).setUp() self._create_legacy_agents(self.agent_count, self.down_agent_count) self.routers = self._create_routers(self.scheduled_router_count, self.expected_scheduled_router_count) self.scheduler = l3_agent_scheduler.LeastRoutersScheduler() def test_least_routers_schedule(self): # Pre schedule routers hosting_agents = self._pre_scheduler_routers(self.scheduler, self.scheduled_router_count) actual_scheduled_agent = self.scheduler.schedule( self.l3_plugin, self.adminContext, self.routers[-1]['id']) if self.expected_scheduled_router_count: # For case where there is just one agent: if self.agent_count == 1: self.assertEqual(actual_scheduled_agent.id, self.l3_agents[0].id) else: self.assertNotIn(actual_scheduled_agent.id, [x.id for x in hosting_agents], message='The expected agent was not scheduled') else: self.assertIsNone(actual_scheduled_agent, message='Expected no agent to be scheduled,' ' but it got scheduled') def test_auto_schedule_routers(self): # Pre schedule routers self._pre_scheduler_routers(self.scheduler, self.scheduled_router_count) # The test self._test_auto_schedule(self.expected_scheduled_router_count) class L3AZSchedulerBaseTest(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): def setUp(self): super(L3AZSchedulerBaseTest, self).setUp(plugin='ml2') self.l3_plugin = l3_router_plugin.L3RouterPlugin() directory.add_plugin(plugin_constants.L3, self.l3_plugin) self.l3_plugin.router_scheduler = None directory.add_plugin(plugin_constants.L3, self.l3_plugin) self.adminContext = context.get_admin_context() self.adminContext.tenant_id = '_func_test_tenant_' def _create_l3_agent(self, host, context, agent_mode='legacy', plugin=None, state=True, az='nova'): agent = helpers.register_l3_agent(host, agent_mode, az=az) helpers.set_agent_admin_state(agent.id, state) return agent def _create_legacy_agents(self, agent_count, down_agent_count, az): # Creates legacy l3 agents and sets admin state based on # down agent count. hosts = ['%s-host-%s' % (az, i) for i in range(agent_count)] l3_agents = [ self._create_l3_agent(hosts[i], self.adminContext, 'legacy', self.l3_plugin, (i >= down_agent_count), az=az) for i in range(agent_count)] return l3_agents def _create_router(self, az_hints, ha): router = {'name': 'router1', 'admin_state_up': True, 'availability_zone_hints': az_hints, 'tenant_id': self._tenant_id} if ha: router['ha'] = True return self.l3_plugin.create_router( self.adminContext, {'router': router}) class L3AZLeastRoutersSchedulerTestCase(L3AZSchedulerBaseTest): """Test various scenarios for AZ router scheduler. az_count Number of AZs. router_az_hints Number of AZs in availability_zone_hints of the router. agent_count[each az] Number of l3 agents (also number of hosts). max_l3_agents_per_router Maximum number of agents on which a router will be scheduled. 0 means test for regular router. down_agent_count[each az] Number of l3 agents which are down. expected_scheduled_agent_count[each az] Number of newly scheduled l3 agents. """ scenarios = [ ('Regular router, Scheduled specified AZ', dict(az_count=2, router_az_hints=1, agent_count=[1, 1], max_l3_agents_per_router=0, down_agent_count=[0, 0], expected_scheduled_agent_count=[1, 0])), ('HA router, Scheduled specified AZs', dict(az_count=3, router_az_hints=2, agent_count=[1, 1, 1], max_l3_agents_per_router=2, down_agent_count=[0, 0, 0], expected_scheduled_agent_count=[1, 1, 0])), ('HA router, max_l3_agents_per_routers > az_hints', dict(az_count=2, router_az_hints=2, agent_count=[2, 1], max_l3_agents_per_router=3, down_agent_count=[0, 0], expected_scheduled_agent_count=[2, 1])), ] def setUp(self): super(L3AZLeastRoutersSchedulerTestCase, self).setUp() self.scheduler = l3_agent_scheduler.AZLeastRoutersScheduler() self.l3_plugin.router_scheduler = self.scheduler def test_schedule_router(self): ha = False if self.max_l3_agents_per_router: self.config(max_l3_agents_per_router=self.max_l3_agents_per_router) ha = True # create l3 agents for i in range(self.az_count): az = 'az%s' % i self._create_legacy_agents(self.agent_count[i], self.down_agent_count[i], az) # create router. # note that ha-router needs enough agents beforehand. az_hints = ['az%s' % i for i in range(self.router_az_hints)] router = self._create_router(az_hints, ha) self.scheduler.schedule(self.l3_plugin, self.adminContext, router['id']) # schedule returns only one agent. so get all agents scheduled. scheduled_agents = self.l3_plugin.get_l3_agents_hosting_routers( self.adminContext, [router['id']]) scheduled_azs = collections.defaultdict(int) for agent in scheduled_agents: scheduled_azs[agent['availability_zone']] += 1 for i in range(self.az_count): self.assertEqual(self.expected_scheduled_agent_count[i], scheduled_azs.get('az%s' % i, 0)) class L3AZAutoScheduleTestCaseBase(L3AZSchedulerBaseTest): """Test various scenarios for AZ router scheduler. az_count Number of AZs. router_az_hints Number of AZs in availability_zone_hints of the router. agent_az AZ of newly activated l3 agent. agent_count[each az] Number of l3 agents (also number of hosts). max_l3_agents_per_router Maximum number of agents on which a router will be scheduled. 0 means test for regular router. down_agent_count[each az] Number of l3 agents which are down. scheduled_agent_count[each az] Number of l3 agents that have been previously scheduled expected_scheduled_agent_count[each az] Number of newly scheduled l3 agents """ scenarios = [ ('Regular router, not scheduled, agent in specified AZ activated', dict(az_count=2, router_az_hints=1, agent_az='az0', agent_count=[1, 1], max_l3_agents_per_router=0, down_agent_count=[1, 1], scheduled_agent_count=[0, 0], expected_scheduled_agent_count=[1, 0])), ('Regular router, not scheduled, agent not in specified AZ activated', dict(az_count=2, router_az_hints=1, agent_az='az1', agent_count=[1, 1], max_l3_agents_per_router=0, down_agent_count=[1, 1], scheduled_agent_count=[0, 0], expected_scheduled_agent_count=[0, 0])), ('HA router, not scheduled, agent in specified AZ activated', dict(az_count=3, router_az_hints=2, agent_az='az1', agent_count=[1, 1, 1], max_l3_agents_per_router=2, down_agent_count=[0, 1, 0], scheduled_agent_count=[0, 0, 0], expected_scheduled_agent_count=[0, 1, 0])), ('HA router, not scheduled, agent not in specified AZ activated', dict(az_count=3, router_az_hints=2, agent_az='az2', agent_count=[1, 1, 1], max_l3_agents_per_router=2, down_agent_count=[0, 0, 1], scheduled_agent_count=[0, 0, 0], expected_scheduled_agent_count=[0, 0, 0])), ] def test_auto_schedule_router(self): scheduler = l3_agent_scheduler.AZLeastRoutersScheduler() ha = False if self.max_l3_agents_per_router: self.config(max_l3_agents_per_router=self.max_l3_agents_per_router) ha = True # create l3 agents l3_agents = {} for i in range(self.az_count): az = 'az%s' % i l3_agents[az] = self._create_legacy_agents( self.agent_count[i], self.down_agent_count[i], az) # create router. # note that ha-router needs enough agents beforehand. az_hints = ['az%s' % i for i in range(self.router_az_hints)] router = self._create_router(az_hints, ha) # schedule some agents before calling auto schedule for i in range(self.az_count): az = 'az%s' % i for j in range(self.scheduled_agent_count[i]): agent = l3_agents[az][j + self.down_agent_count[i]] scheduler.bind_router(self.l3_plugin, self.adminContext, router['id'], agent.id) # activate down agent and call auto_schedule_routers activate_agent = l3_agents[self.agent_az][0] helpers.set_agent_admin_state(activate_agent['id'], admin_state_up=True) scheduler.auto_schedule_routers(self.l3_plugin, self.adminContext, activate_agent['host']) scheduled_agents = self.l3_plugin.get_l3_agents_hosting_routers( self.adminContext, [router['id']]) scheduled_azs = collections.defaultdict(int) for agent in scheduled_agents: scheduled_azs[agent['availability_zone']] += 1 for i in range(self.az_count): self.assertEqual(self.expected_scheduled_agent_count[i], scheduled_azs.get('az%s' % i, 0)) class L3DVRSchedulerBaseTest(L3SchedulerBaseTest): """Base class for functional test of DVR L3 schedulers. Provides basic setup and utility functions. """ def setUp(self): super(L3DVRSchedulerBaseTest, self).setUp() self.default_ext_net_id = _uuid() self.default_ext_subnet_id = _uuid() self.router_ext_net_id = _uuid() self.router_ext_subnet_id = _uuid() def _create_router(self, name, distributed, ext_net_id=None): router = {'name': name, 'admin_state_up': True, 'tenant_id': self.adminContext.tenant_id, 'distributed': distributed} if ext_net_id: router['external_gateway_info'] = {'network_id': ext_net_id} return self.l3_plugin.create_router(self.adminContext, {'router': router}) def _create_network(self, net_id, name=None, external=False): network_dict = {'tenant_id': self.adminContext.tenant_id, 'id': net_id, 'name': name, 'admin_state_up': True, 'shared': False, 'status': constants.NET_STATUS_ACTIVE} network = self.plugin.create_network(self.adminContext, {'network': network_dict}) if external: network = net_obj.ExternalNetwork( self.adminContext, network_id=net_id) network.create() return network def _create_subnet(self, sub_id, network_id, cidr, gw_ip, name='test_sub'): subnet = {'tenant_id': self.adminContext.tenant_id, 'id': sub_id, 'name': name, 'network_id': network_id, 'ip_version': 4, 'cidr': cidr, 'enable_dhcp': False, 'gateway_ip': gw_ip, 'shared': False, 'allocation_pools': constants.ATTR_NOT_SPECIFIED, 'dns_nameservers': constants.ATTR_NOT_SPECIFIED, 'host_routes': constants.ATTR_NOT_SPECIFIED} return self.plugin.create_subnet(self.adminContext, {'subnet': subnet}) class L3DVRSchedulerTestCase(L3DVRSchedulerBaseTest): """Test various scenarios for L3 DVR schedulers: agent_mode L3 agent mode. second_agent_mode Second L3 agent mode for scenarios with two agents. agent_has_ext_network Is there external network on the host. router_is_distributed Is router distributed. router_already_hosted Is router already hosted. router_has_ext_gw Does router have external gateway. router_agent_have_same_ext_net Do router and agent have the same external network. expected_router_scheduled To verify do we expect router to get scheduled. """ def get_scenario(agent_mode=constants.L3_AGENT_MODE_DVR_SNAT, second_agent_mode=None, agent_has_ext_network=False, router_is_distributed=False, router_already_hosted=False, router_has_ext_gw=False, router_agent_have_same_ext_net=False, expected_router_scheduled=False): return dict(agent_mode=agent_mode, second_agent_mode=second_agent_mode, agent_has_ext_network=agent_has_ext_network, router_is_distributed=router_is_distributed, router_already_hosted=router_already_hosted, router_has_ext_gw=router_has_ext_gw, router_agent_have_same_ext_net=router_agent_have_same_ext_net, expected_router_scheduled=expected_router_scheduled) scenarios = [ ('Legacy router not scheduled on dvr agent', get_scenario(agent_mode=constants.L3_AGENT_MODE_DVR)), ('Legacy router scheduled on dvr_snat agent', get_scenario(expected_router_scheduled=True)), ('Distributed router not scheduled on legacy agent', get_scenario(agent_mode=constants.L3_AGENT_MODE_LEGACY, router_is_distributed=True)), ('Distributed router not scheduled on dvr agent', get_scenario(agent_mode=constants.L3_AGENT_MODE_DVR, router_is_distributed=True)), ('Distributed router scheduled on dvr_snat agent', get_scenario(router_is_distributed=True, expected_router_scheduled=True)), ('Already hosted legacy router not scheduled on dvr agent', get_scenario(agent_mode=constants.L3_AGENT_MODE_DVR, router_already_hosted=True)), ('Already hosted legacy router not scheduled on dvr_snat agent', get_scenario(router_already_hosted=True)), ('Already hosted distributed router not scheduled on legacy agent', get_scenario(agent_mode=constants.L3_AGENT_MODE_LEGACY, router_already_hosted=True, router_is_distributed=True)), ('Already hosted distributed router not scheduled on dvr agent', get_scenario(agent_mode=constants.L3_AGENT_MODE_DVR, router_is_distributed=True, router_already_hosted=True)), ('Already hosted distributed router not scheduled on dvr_snat agent', get_scenario(router_is_distributed=True, router_already_hosted=True)), ('Already hosted legacy router not scheduled on additional dvr agent', get_scenario(agent_mode=constants.L3_AGENT_MODE_LEGACY, second_agent_mode=constants.L3_AGENT_MODE_DVR_SNAT, router_already_hosted=True)), ('Distributed router not scheduled if it is on a different ' 'external network than the dvr_snat agent', get_scenario(agent_has_ext_network=True, router_is_distributed=True, router_has_ext_gw=True, router_agent_have_same_ext_net=False)), ] def setUp(self): super(L3DVRSchedulerTestCase, self).setUp() agent_cnt = 2 if self.second_agent_mode else 1 # create hosts for each agent self.hosts = ['host-%s' % i for i in range(agent_cnt)] # create default external network self._create_network(self.default_ext_net_id, name='_test-ext-net', external=True) self._create_subnet(self.default_ext_subnet_id, self.default_ext_net_id, '10.10.9.0/24', '10.10.9.1', '_test-ext-net-subnet') if self.router_has_ext_gw and not self.router_agent_have_same_ext_net: # for the test cases in which router and agent are not on same # external network, we create an external network for router self._create_network(self.router_ext_net_id, name='_test-ext-net2', external=True) self._create_subnet(self.router_ext_subnet_id, self.router_ext_net_id, '10.10.8.0/24', '10.10.8.1', '_test-ext-net2-subnet') # create agents: self.l3_agents = [self._create_l3_agent(self.hosts[0], self.adminContext, self.agent_mode, True, self.default_ext_net_id if self.agent_has_ext_network else '')] if self.second_agent_mode: self.l3_agents.append(self._create_l3_agent(self.hosts[1], self.adminContext, self.second_agent_mode, True, self.default_ext_net_id if self.agent_has_ext_network else '')) # The router to schedule: self.router_to_schedule = self._create_router_to_schedule() def _create_router_to_schedule(self): router_to_schedule = None if self.router_has_ext_gw: if self.router_agent_have_same_ext_net: router_to_schedule = self._create_router('schd_rtr', self.router_is_distributed, self.default_ext_net_id) else: router_to_schedule = self._create_router('schd_rtr', self.router_is_distributed, self.router_ext_net_id) else: router_to_schedule = self._create_router('schd_rtr', self.router_is_distributed) return router_to_schedule def _test_schedule_router(self): if self.router_already_hosted: self.scheduler.bind_router(self.l3_plugin, self.adminContext, self.router_to_schedule['id'], self.l3_agents[0].id) # schedule: actual_scheduled_agent = self.scheduler.schedule( self.l3_plugin, self.adminContext, self.router_to_schedule['id']) # check for router scheduling: self.assertEqual(self.expected_router_scheduled, bool(actual_scheduled_agent), message='Failed to schedule agent') def _test_auto_schedule_routers(self): if self.router_already_hosted: self.scheduler.bind_router(self.l3_plugin, self.adminContext, self.router_to_schedule['id'], self.l3_agents[0].id) # schedule: hosting_before = self.l3_plugin.get_l3_agents_hosting_routers( self.adminContext, [self.router_to_schedule['id']]) for host in self.hosts: self.scheduler.auto_schedule_routers( self.l3_plugin, self.adminContext, host) hosting_after = self.l3_plugin.get_l3_agents_hosting_routers( self.adminContext, [self.router_to_schedule['id']]) if self.router_already_hosted: self.assertEqual(hosting_before, hosting_after, 'Agent pre scheduled, yet no binding found!') elif self.expected_router_scheduled: self.assertNotEqual(hosting_before, hosting_after, 'Agent not scheduled, not expected') else: self.assertEqual(hosting_before, hosting_after, 'Agent scheduled, not expected') def test_least_routers_schedule_router(self): self.scheduler = l3_agent_scheduler.LeastRoutersScheduler() self._test_schedule_router() def test_least_routers_auto_schedule_routers(self): self.scheduler = l3_agent_scheduler.LeastRoutersScheduler() self._test_auto_schedule_routers() def test_chance_schedule_router(self): self.scheduler = l3_agent_scheduler.ChanceScheduler() self._test_schedule_router() def test_chance_auto_schedule_routers(self): self.scheduler = l3_agent_scheduler.ChanceScheduler() self._test_auto_schedule_routers() neutron-12.1.1/neutron/tests/functional/scheduler/test_dhcp_agent_scheduler.py0000664000175000017500000006336013553660047030050 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from operator import attrgetter from neutron_lib.api.definitions import provider_net as providernet from neutron_lib import constants from neutron_lib import context from oslo_utils import uuidutils import testscenarios from neutron.db import agents_db from neutron.db import agentschedulers_db from neutron.db import common_db_mixin from neutron.objects import network from neutron.scheduler import dhcp_agent_scheduler from neutron.tests.common import helpers from neutron.tests.unit.plugins.ml2 import test_plugin from neutron.tests.unit.scheduler import (test_dhcp_agent_scheduler as test_dhcp_sch) # Required to generate tests from scenarios. Not compatible with nose. load_tests = testscenarios.load_tests_apply_scenarios class BaseTestScheduleNetwork(object): """Base class which defines scenarios for schedulers. agent_count Number of dhcp agents (also number of hosts). max_agents_per_network Maximum DHCP Agents that can be scheduled for a network. scheduled_agent_count Number of agents the network has previously scheduled down_agent_count Number of dhcp agents which are down expected_scheduled_agent_count Number of scheduled agents the schedule() should return or 'None' if the schedule() cannot schedule the network. """ scenarios = [ ('No agents scheduled if no agents are present', dict(agent_count=0, max_agents_per_network=1, scheduled_agent_count=0, down_agent_count=0, expected_scheduled_agent_count=None)), ('No agents scheduled if network already hosted and' ' max_agents_per_network reached', dict(agent_count=1, max_agents_per_network=1, scheduled_agent_count=1, down_agent_count=0, expected_scheduled_agent_count=None)), ('No agents scheduled if all agents are down', dict(agent_count=2, max_agents_per_network=1, scheduled_agent_count=0, down_agent_count=2, expected_scheduled_agent_count=None)), ('Agent scheduled to the network if network is not yet hosted', dict(agent_count=1, max_agents_per_network=1, scheduled_agent_count=0, down_agent_count=0, expected_scheduled_agent_count=1)), ('Additional Agents scheduled to the network if max_agents_per_network' ' is not yet reached', dict(agent_count=3, max_agents_per_network=3, scheduled_agent_count=1, down_agent_count=0, expected_scheduled_agent_count=2)), ('No agent scheduled if agent is dead', dict(agent_count=3, max_agents_per_network=3, scheduled_agent_count=1, down_agent_count=1, expected_scheduled_agent_count=1)), ] class TestChanceScheduleNetwork(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, agentschedulers_db.DhcpAgentSchedulerDbMixin, agents_db.AgentDbMixin, common_db_mixin.CommonDbMixin, BaseTestScheduleNetwork): """Test various scenarios for ChanceScheduler.schedule.""" def test_schedule_network(self): self.config(dhcp_agents_per_network=self.max_agents_per_network) scheduler = dhcp_agent_scheduler.ChanceScheduler() # create dhcp agents hosts = ['host-%s' % i for i in range(self.agent_count)] dhcp_agents = self._create_and_set_agents_down( hosts, down_agent_count=self.down_agent_count) active_agents = dhcp_agents[self.down_agent_count:] # schedule some agents before calling schedule if self.scheduled_agent_count: # schedule the network schedule_agents = active_agents[:self.scheduled_agent_count] scheduler.resource_filter.bind(self.ctx, schedule_agents, self.network_id) actual_scheduled_agents = scheduler.schedule(self, self.ctx, self.network) if self.expected_scheduled_agent_count: self.assertEqual(self.expected_scheduled_agent_count, len(actual_scheduled_agents)) hosted_agents = self.list_dhcp_agents_hosting_network( self.ctx, self.network_id) self.assertEqual(self.scheduled_agent_count + len(actual_scheduled_agents), len(hosted_agents['agents'])) else: self.assertEqual([], actual_scheduled_agents) class TestWeightScheduleNetwork(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, agentschedulers_db.DhcpAgentSchedulerDbMixin, agents_db.AgentDbMixin, common_db_mixin.CommonDbMixin, BaseTestScheduleNetwork): """Test various scenarios for WeightScheduler.schedule.""" def test_weight_schedule_network(self): self.config(dhcp_agents_per_network=self.max_agents_per_network) scheduler = dhcp_agent_scheduler.WeightScheduler() # create dhcp agents hosts = ['host-%s' % i for i in range(self.agent_count)] dhcp_agents = self._create_and_set_agents_down( hosts, down_agent_count=self.down_agent_count) active_agents = dhcp_agents[self.down_agent_count:] unscheduled_active_agents = list(active_agents) # schedule some agents before calling schedule if self.scheduled_agent_count: # schedule the network schedule_agents = active_agents[:self.scheduled_agent_count] scheduler.resource_filter.bind(self.ctx, schedule_agents, self.network_id) for agent in schedule_agents: unscheduled_active_agents.remove(agent) actual_scheduled_agents = scheduler.schedule(self, self.ctx, self.network) if self.expected_scheduled_agent_count: sorted_unscheduled_active_agents = sorted( unscheduled_active_agents, key=attrgetter('load'))[0:self.expected_scheduled_agent_count] self.assertItemsEqual( (agent['id'] for agent in actual_scheduled_agents), (agent['id'] for agent in sorted_unscheduled_active_agents)) self.assertEqual(self.expected_scheduled_agent_count, len(actual_scheduled_agents)) hosted_agents = self.list_dhcp_agents_hosting_network( self.ctx, self.network_id) self.assertEqual(self.scheduled_agent_count + len(actual_scheduled_agents), len(hosted_agents['agents'])) else: self.assertEqual([], actual_scheduled_agents) class TestAutoSchedule(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, agentschedulers_db.DhcpAgentSchedulerDbMixin, agents_db.AgentDbMixin, common_db_mixin.CommonDbMixin): """Test various scenarios for ChanceScheduler.auto_schedule_networks. Below is the brief description of the scenario variables -------------------------------------------------------- agent_count number of DHCP agents (also number of hosts). max_agents_per_network Maximum DHCP Agents that can be scheduled for a network. network_count Number of networks. networks_with_dhcp_disabled List of networks with dhcp disabled hosted_networks A mapping of agent id to the ids of the networks that they should be initially hosting. expected_auto_schedule_return_value Expected return value of 'auto_schedule_networks'. expected_hosted_networks This stores the expected networks that should have been scheduled (or that could have already been scheduled) for each agent after the 'auto_schedule_networks' function is called. no_network_with_az_match If this parameter is True, there is no unscheduled network with availability_zone_hints matches to an availability_zone of agents to be scheduled. The default is False. """ scenarios = [ ('Agent scheduled to the network if network is not yet hosted', dict(agent_count=1, max_agents_per_network=1, network_count=1, networks_with_dhcp_disabled=[], hosted_networks={}, expected_auto_schedule_return_value=True, expected_hosted_networks={'agent-0': ['network-0']})), ('No agent scheduled if no networks are present', dict(agent_count=1, max_agents_per_network=1, network_count=0, networks_with_dhcp_disabled=[], hosted_networks={}, expected_auto_schedule_return_value=False, expected_hosted_networks={'agent-0': []})), ('Agents scheduled to the networks if networks are not yet hosted', dict(agent_count=2, max_agents_per_network=3, network_count=2, networks_with_dhcp_disabled=[], hosted_networks={}, expected_auto_schedule_return_value=True, expected_hosted_networks={'agent-0': ['network-0', 'network-1'], 'agent-1': ['network-0', 'network-1']})), ('No new agents scheduled if networks are already hosted', dict(agent_count=2, max_agents_per_network=3, network_count=2, networks_with_dhcp_disabled=[], hosted_networks={'agent-0': ['network-0', 'network-1'], 'agent-1': ['network-0', 'network-1']}, expected_auto_schedule_return_value=True, expected_hosted_networks={'agent-0': ['network-0', 'network-1'], 'agent-1': ['network-0', 'network-1']})), ('Additional agents scheduled to the networks if' ' max_agents_per_network is not yet reached', dict(agent_count=4, max_agents_per_network=3, network_count=4, networks_with_dhcp_disabled=[], hosted_networks={'agent-0': ['network-0', 'network-1'], 'agent-1': ['network-0'], 'agent-2': ['network-2'], 'agent-3': ['network-0', 'network-2']}, expected_auto_schedule_return_value=True, expected_hosted_networks={'agent-0': ['network-0', 'network-1', 'network-2', 'network-3'], 'agent-1': ['network-0', 'network-1', 'network-2', 'network-3'], 'agent-2': ['network-1', 'network-2', 'network-3'], 'agent-3': ['network-0', 'network-1', 'network-2', 'network-3']})), ('No agents scheduled if networks already hosted and' ' max_agents_per_network reached', dict(agent_count=4, max_agents_per_network=1, network_count=4, networks_with_dhcp_disabled=[], hosted_networks={'agent-0': ['network-0'], 'agent-1': ['network-2'], 'agent-2': ['network-1'], 'agent-3': ['network-3']}, expected_auto_schedule_return_value=True, expected_hosted_networks={'agent-0': ['network-0'], 'agent-1': ['network-2'], 'agent-2': ['network-1'], 'agent-3': ['network-3']})), ('No agents scheduled to the network with dhcp disabled', dict(agent_count=2, max_agents_per_network=3, network_count=2, networks_with_dhcp_disabled=['network-1'], hosted_networks={}, expected_auto_schedule_return_value=True, expected_hosted_networks={'agent-0': ['network-0'], 'agent-1': ['network-0']})), ('No agents scheduled if all networks have dhcp disabled', dict(agent_count=2, max_agents_per_network=3, network_count=2, networks_with_dhcp_disabled=['network-0', 'network-1'], hosted_networks={}, expected_auto_schedule_return_value=False, expected_hosted_networks={'agent-0': [], 'agent-1': []})), ('No agents scheduled if unscheduled network does not match AZ', dict(agent_count=1, max_agents_per_network=1, network_count=1, networks_with_dhcp_disabled=[], hosted_networks={}, expected_auto_schedule_return_value=True, expected_hosted_networks={'agent-0': []}, no_network_with_az_match=True)), ] def _strip_host_index(self, name): """Strips the host index. Eg. if name = '2-agent-3', then 'agent-3' is returned. """ return name[name.find('-') + 1:] def _extract_index(self, name): """Extracts the index number and returns. Eg. if name = '2-agent-3', then 3 is returned """ return int(name.split('-')[-1]) def get_subnets(self, context, fields=None): subnets = [] for net in self._networks: enable_dhcp = (self._strip_host_index(net['name']) not in self.networks_with_dhcp_disabled) subnets.append({'network_id': net.id, 'enable_dhcp': enable_dhcp, 'segment_id': None}) return subnets def get_network(self, context, net_id): az_hints = [] if getattr(self, 'no_network_with_az_match', False): az_hints = ['not-match'] return {'availability_zone_hints': az_hints} def _get_hosted_networks_on_dhcp_agent(self, agent_id): binding_objs = network.NetworkDhcpAgentBinding.get_objects( self.ctx, dhcp_agent_id=agent_id) return [item.network_id for item in binding_objs] def _test_auto_schedule(self, host_index): self.config(dhcp_agents_per_network=self.max_agents_per_network) scheduler = dhcp_agent_scheduler.ChanceScheduler() self.ctx = context.get_admin_context() msg = 'host_index = %s' % host_index # create dhcp agents hosts = ['%s-agent-%s' % (host_index, i) for i in range(self.agent_count)] dhcp_agents = self._create_and_set_agents_down(hosts) # create networks self._networks = [ network.Network( self.ctx, id=uuidutils.generate_uuid(), name='%s-network-%s' % (host_index, i)) for i in range(self.network_count) ] for i in range(len(self._networks)): self._networks[i].create() network_ids = [net.id for net in self._networks] # pre schedule the networks to the agents defined in # self.hosted_networks before calling auto_schedule_network for agent, networks in self.hosted_networks.items(): agent_index = self._extract_index(agent) for net in networks: net_index = self._extract_index(net) scheduler.resource_filter.bind(self.ctx, [dhcp_agents[agent_index]], network_ids[net_index]) retval = scheduler.auto_schedule_networks(self, self.ctx, hosts[host_index]) self.assertEqual(self.expected_auto_schedule_return_value, retval, message=msg) agent_id = dhcp_agents[host_index].id hosted_networks = self._get_hosted_networks_on_dhcp_agent(agent_id) hosted_net_names = [ self._strip_host_index(net['name']) for net in network.Network.get_objects( self.ctx, id=hosted_networks) ] expected_hosted_networks = self.expected_hosted_networks['agent-%s' % host_index] self.assertItemsEqual(hosted_net_names, expected_hosted_networks, msg) def test_auto_schedule(self): for i in range(self.agent_count): self._test_auto_schedule(i) class TestAZAwareWeightScheduler(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, agentschedulers_db.DhcpAgentSchedulerDbMixin, agents_db.AgentDbMixin, common_db_mixin.CommonDbMixin): """Test various scenarios for AZAwareWeightScheduler.schedule. az_count Number of AZs. network_az_hints Number of AZs in availability_zone_hints of the network. agent_count[each az] Number of dhcp agents (also number of hosts). max_agents_per_network Maximum DHCP Agents that can be scheduled for a network. scheduled_agent_count[each az] Number of agents the network has previously scheduled down_agent_count[each az] Number of dhcp agents which are down expected_scheduled_agent_count[each az] Number of scheduled agents the schedule() should return or 'None' if the schedule() cannot schedule the network. """ scenarios = [ ('Single hint, Single agent, Scheduled an agent of the specified AZ', dict(az_count=2, network_az_hints=1, agent_count=[1, 1], max_agents_per_network=1, scheduled_agent_count=[0, 0], down_agent_count=[0, 0], expected_scheduled_agent_count=[1, 0])), ('Multi hints, Multi agents Scheduled agents of the specified AZs', dict(az_count=3, network_az_hints=2, agent_count=[1, 1, 1], max_agents_per_network=2, scheduled_agent_count=[0, 0, 0], down_agent_count=[0, 0, 0], expected_scheduled_agent_count=[1, 1, 0])), ('Single hint, Multi agents, Scheduled agents of the specified AZ', dict(az_count=2, network_az_hints=1, agent_count=[2, 1], max_agents_per_network=2, scheduled_agent_count=[0, 0], down_agent_count=[0, 0], expected_scheduled_agent_count=[2, 0])), ('Multi hints, Multi agents, Only single AZ available', dict(az_count=2, network_az_hints=2, agent_count=[2, 1], max_agents_per_network=2, scheduled_agent_count=[0, 0], down_agent_count=[0, 1], expected_scheduled_agent_count=[2, 0])), ('Multi hints, Multi agents, Not enough agents', dict(az_count=3, network_az_hints=3, agent_count=[1, 1, 1], max_agents_per_network=3, scheduled_agent_count=[0, 0, 0], down_agent_count=[0, 1, 0], expected_scheduled_agent_count=[1, 0, 1])), ('Multi hints, Multi agents, Partially scheduled, Another AZ selected', dict(az_count=3, network_az_hints=2, agent_count=[1, 1, 1], max_agents_per_network=2, scheduled_agent_count=[1, 0, 0], down_agent_count=[0, 0, 0], expected_scheduled_agent_count=[0, 1, 0])), ('No hint, Scheduled independent to AZ', dict(az_count=3, network_az_hints=0, agent_count=[1, 1, 1], max_agents_per_network=3, scheduled_agent_count=[0, 0, 0], down_agent_count=[0, 0, 0], expected_scheduled_agent_count=[1, 1, 1])), ] def _set_network_az_hints(self): self.network['availability_zone_hints'] = [] for i in range(self.network_az_hints): self.network['availability_zone_hints'].append('az%s' % i) def test_schedule_network(self): self.config(dhcp_agents_per_network=self.max_agents_per_network) scheduler = dhcp_agent_scheduler.AZAwareWeightScheduler() self._set_network_az_hints() # create dhcp agents for i in range(self.az_count): az = 'az%s' % i hosts = ['%s-host-%s' % (az, j) for j in range(self.agent_count[i])] dhcp_agents = self._create_and_set_agents_down( hosts, down_agent_count=self.down_agent_count[i], az=az) active_agents = dhcp_agents[self.down_agent_count[i]:] # schedule some agents before calling schedule if self.scheduled_agent_count[i]: # schedule the network schedule_agents = active_agents[:self.scheduled_agent_count[i]] scheduler.resource_filter.bind( self.ctx, schedule_agents, self.network_id) actual_scheduled_agents = scheduler.schedule(self, self.ctx, self.network) scheduled_azs = collections.defaultdict(int) for agent in actual_scheduled_agents: scheduled_azs[agent['availability_zone']] += 1 hosted_agents = self.list_dhcp_agents_hosting_network( self.ctx, self.network_id) hosted_azs = collections.defaultdict(int) for agent in hosted_agents['agents']: hosted_azs[agent['availability_zone']] += 1 for i in range(self.az_count): self.assertEqual(self.expected_scheduled_agent_count[i], scheduled_azs.get('az%s' % i, 0)) self.assertEqual(self.scheduled_agent_count[i] + scheduled_azs.get('az%s' % i, 0), hosted_azs.get('az%s' % i, 0)) class TestDHCPSchedulerWithNetworkAccessibility( test_plugin.Ml2PluginV2TestCase): _mechanism_drivers = ['openvswitch'] def test_dhcp_scheduler_filters_hosts_without_network_access(self): dhcp_agent1 = helpers.register_dhcp_agent(host='host1') dhcp_agent2 = helpers.register_dhcp_agent(host='host2') dhcp_agent3 = helpers.register_dhcp_agent(host='host3') dhcp_agents = [dhcp_agent1, dhcp_agent2, dhcp_agent3] helpers.register_ovs_agent( host='host1', bridge_mappings={'physnet1': 'br-eth-1'}) helpers.register_ovs_agent( host='host2', bridge_mappings={'physnet2': 'br-eth-1'}) helpers.register_ovs_agent( host='host3', bridge_mappings={'physnet2': 'br-eth-1'}) admin_context = context.get_admin_context() net = self.driver.create_network( admin_context, {'network': {'name': 'net1', providernet.NETWORK_TYPE: 'vlan', providernet.PHYSICAL_NETWORK: 'physnet1', providernet.SEGMENTATION_ID: 1, 'tenant_id': 'tenant_one', 'admin_state_up': True, 'shared': True}}) self.driver.create_subnet( admin_context, {'subnet': {'name': 'name', 'ip_version': 4, 'network_id': net['id'], 'cidr': '10.0.0.0/24', 'gateway_ip': constants.ATTR_NOT_SPECIFIED, 'allocation_pools': constants.ATTR_NOT_SPECIFIED, 'dns_nameservers': constants.ATTR_NOT_SPECIFIED, 'host_routes': constants.ATTR_NOT_SPECIFIED, 'tenant_id': 'tenant_one', 'enable_dhcp': True}}) self.plugin.schedule_network(admin_context, net) dhcp_agents = self.driver.get_dhcp_agents_hosting_networks( admin_context, [net['id']]) self.assertEqual(1, len(dhcp_agents)) self.assertEqual('host1', dhcp_agents[0]['host']) neutron-12.1.1/neutron/tests/functional/scheduler/__init__.py0000664000175000017500000000000013553660046024373 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/__init__.py0000664000175000017500000000202213553660046022423 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ In order to save gate resources, test paths that have similar environmental requirements to the functional path are marked for discovery. """ import os.path from neutron.common import eventlet_utils eventlet_utils.monkey_patch() def load_tests(loader, tests, pattern): this_dir = os.path.dirname(__file__) new_tests = loader.discover(start_dir=this_dir, pattern=pattern) tests.addTests(new_tests) return tests neutron-12.1.1/neutron/tests/functional/pecan_wsgi/0000775000175000017500000000000013553660156022437 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/pecan_wsgi/utils.py0000664000175000017500000001605213553660047024154 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import extensions as api_extensions from neutron.api import extensions from neutron.api.v2 import base from neutron.pecan_wsgi import controllers from neutron.pecan_wsgi.controllers import utils as pecan_utils class FakeSingularCollectionExtension(api_extensions.ExtensionDescriptor): COLLECTION = 'topologies' RESOURCE = 'topology' RAM = { COLLECTION: { 'fake': {'is_visible': True} } } @classmethod def get_name(cls): return "" @classmethod def get_alias(cls): return "fake-sc" @classmethod def get_description(cls): return "" @classmethod def get_updated(cls): return "2099-07-23T10:00:00-00:00" def get_extended_resources(self, version): if version == "2.0": return self.RAM else: return {} def get_pecan_controllers(self): ctrllr = controllers.CollectionsController( self.RESOURCE, self.RESOURCE) return [pecan_utils.PecanResourceExtension(self.RESOURCE, ctrllr)] class FakeSingularCollectionPlugin(object): supported_extension_aliases = ['fake-sc'] def get_topology(self, context, id_, fields=None): return {'fake': id_} def get_topologies(self, context, filters=None, fields=None): return [{'fake': 'fake'}] def create_network(context, plugin): return plugin.create_network( context, {'network': {'name': 'pecannet', 'tenant_id': 'tenid', 'shared': False, 'admin_state_up': True, 'status': 'ACTIVE'}}) def create_subnet(context, plugin, network_id): return plugin.create_subnet( context, {'subnet': {'tenant_id': 'tenid', 'network_id': network_id, 'name': 'pecansub', 'ip_version': 4, 'cidr': '10.20.30.0/24', 'gateway_ip': '10.20.30.1', 'enable_dhcp': True, 'allocation_pools': [ {'start': '10.20.30.2', 'end': '10.20.30.254'}], 'dns_nameservers': [], 'host_routes': []}}) def create_router(context, l3_plugin): return l3_plugin.create_router( context, {'router': {'name': 'pecanrtr', 'tenant_id': 'tenid', 'admin_state_up': True}}) class FakeExtension(api_extensions.ExtensionDescriptor): HYPHENATED_RESOURCE = 'meh_meh' HYPHENATED_COLLECTION = HYPHENATED_RESOURCE + 's' FAKE_PARENT_SUBRESOURCE_COLLECTION = 'fake_duplicates' FAKE_SUB_RESOURCE_COLLECTION = 'fake_subresources' RESOURCE_ATTRIBUTE_MAP = { 'meh_mehs': { 'fake': {'is_visible': True} }, 'fake_duplicates': { 'fake': {'is_visible': True} } } SUB_RESOURCE_ATTRIBUTE_MAP = { 'fake_subresources': { 'parent': { 'collection_name': ( 'meh_mehs'), 'member_name': 'meh_meh'}, 'parameters': {'foo': {'is_visible': True}, 'bar': {'is_visible': True} } }, 'fake_duplicates': { 'parent': { 'collection_name': ( 'meh_mehs'), 'member_name': 'meh_meh'}, 'parameters': {'fake': {'is_visible': True} } } } @classmethod def get_name(cls): return "fake-ext" @classmethod def get_alias(cls): return "fake-ext" @classmethod def get_description(cls): return "" @classmethod def get_updated(cls): return "meh" def get_resources(self): """Returns Ext Resources.""" resources = [] fake_plugin = FakePlugin() for collection_name in self.RESOURCE_ATTRIBUTE_MAP: resource_name = collection_name[:-1] params = self.RESOURCE_ATTRIBUTE_MAP.get(collection_name, {}) member_actions = {'put_meh': 'PUT', 'boo_meh': 'GET'} if collection_name == self.HYPHENATED_COLLECTION: collection_name = collection_name.replace('_', '-') controller = base.create_resource( collection_name, resource_name, fake_plugin, params, allow_bulk=True, allow_pagination=True, allow_sorting=True, member_actions=member_actions) resource = extensions.ResourceExtension( collection_name, controller, attr_map=params) resources.append(resource) for collection_name in self.SUB_RESOURCE_ATTRIBUTE_MAP: resource_name = collection_name[:-1] parent = self.SUB_RESOURCE_ATTRIBUTE_MAP[collection_name].get( 'parent') params = self.SUB_RESOURCE_ATTRIBUTE_MAP[collection_name].get( 'parameters') controller = base.create_resource(collection_name, resource_name, fake_plugin, params, allow_bulk=True, parent=parent) resource = extensions.ResourceExtension( collection_name, controller, parent, path_prefix="", attr_map=params) resources.append(resource) return resources def get_extended_resources(self, version): if version == "2.0": return self.RESOURCE_ATTRIBUTE_MAP else: return {} class FakePlugin(object): PLUGIN_TYPE = 'fake-ext-plugin' supported_extension_aliases = ['fake-ext'] @classmethod def get_plugin_type(cls): return cls.PLUGIN_TYPE def get_meh_meh(self, context, id_, fields=None): return {'fake': id_} def get_meh_mehs(self, context, filters=None, fields=None): return [{'fake': 'fake'}] def get_fake_duplicate(self, context, id_, fields=None): return {'fake': id_} def get_fake_duplicates(self, context, filters=None, fields=None): return [{'fake': 'fakeduplicates'}] def get_meh_meh_fake_duplicates(self, context, id_, fields=None, filters=None): return [{'fake': id_}] def get_meh_meh_fake_subresources(self, context, id_, fields=None, filters=None): return {'foo': id_} def put_meh(self, context, id_, data): return {'poo_yah': id_} def boo_meh(self, context, id_): return {'boo_yah': id_} neutron-12.1.1/neutron/tests/functional/pecan_wsgi/test_functional.py0000664000175000017500000001377113553660047026222 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import mock from neutron_lib import context from neutron_lib import exceptions as n_exc from neutron_lib.plugins import constants from oslo_config import cfg from oslo_middleware import base from oslo_service import wsgi from oslo_utils import uuidutils import testtools import webob.dec import webtest from neutron.api import extensions as exts from neutron import manager from neutron import tests from neutron.tests.unit import testlib_api class InjectContext(base.ConfigurableMiddleware): @webob.dec.wsgify def __call__(self, req): user_id = req.headers.get('X_USER_ID', '') # Determine the tenant tenant_id = req.headers.get('X_PROJECT_ID') # Suck out the roles roles = [r.strip() for r in req.headers.get('X_ROLES', '').split(',')] # Human-friendly names tenant_name = req.headers.get('X_PROJECT_NAME') user_name = req.headers.get('X_USER_NAME') # Create a context with the authentication data ctx = context.Context(user_id, tenant_id, roles=roles, user_name=user_name, tenant_name=tenant_name) req.environ['neutron.context'] = ctx return self.application def create_test_app(): paste_config_loc = os.path.join(os.path.dirname(tests.__file__), 'etc', 'api-paste.ini') paste_config_loc = os.path.abspath(paste_config_loc) cfg.CONF.set_override('api_paste_config', paste_config_loc) loader = wsgi.Loader(cfg.CONF) app = loader.load_app('neutron') app = InjectContext(app) return webtest.TestApp(app) class PecanFunctionalTest(testlib_api.SqlTestCase): def setUp(self, service_plugins=None, extensions=None): self.setup_coreplugin('ml2', load_plugins=False) super(PecanFunctionalTest, self).setUp() self.addCleanup(exts.PluginAwareExtensionManager.clear_instance) self.set_config_overrides() manager.init() ext_mgr = exts.PluginAwareExtensionManager.get_instance() if extensions: ext_mgr.extensions = extensions if service_plugins: service_plugins[constants.CORE] = ext_mgr.plugins.get( constants.CORE) ext_mgr.plugins = service_plugins self.app = create_test_app() def set_config_overrides(self): cfg.CONF.set_override('auth_strategy', 'noauth') def do_request(self, url, tenant_id=None, admin=False, expect_errors=False): if admin: if not tenant_id: tenant_id = 'admin' headers = {'X-Tenant-Id': tenant_id, 'X-Roles': 'admin'} else: headers = {'X-Tenant-ID': tenant_id or ''} return self.app.get(url, headers=headers, expect_errors=expect_errors) class TestErrors(PecanFunctionalTest): def test_404(self): response = self.app.get('/assert_called_once', expect_errors=True) self.assertEqual(response.status_int, 404) def test_bad_method(self): response = self.app.patch('/v2.0/ports/44.json', expect_errors=True) self.assertEqual(response.status_int, 405) class TestRequestID(PecanFunctionalTest): def test_request_id(self): response = self.app.get('/v2.0/') self.assertIn('x-openstack-request-id', response.headers) self.assertTrue( response.headers['x-openstack-request-id'].startswith('req-')) id_part = response.headers['x-openstack-request-id'].split('req-')[1] self.assertTrue(uuidutils.is_uuid_like(id_part)) class TestKeystoneAuth(PecanFunctionalTest): def set_config_overrides(self): # default auth strategy is keystone so we pass pass def test_auth_enforced(self): response = self.app.get('/v2.0/', expect_errors=True) self.assertEqual(response.status_int, 401) class TestInvalidAuth(PecanFunctionalTest): def setup_app(self): # disable normal app setup since it will fail pass def test_invalid_auth_strategy(self): cfg.CONF.set_override('auth_strategy', 'badvalue') # NOTE(blogan): the auth.pipeline_factory will throw a KeyError # with a bad value because that value is not the paste config. # This KeyError is translated to a LookupError, which the oslo wsgi # code translates into PasteAppNotFound. with testtools.ExpectedException(wsgi.PasteAppNotFound): create_test_app() class TestExceptionTranslationHook(PecanFunctionalTest): def test_neutron_nonfound_to_webob_exception(self): # this endpoint raises a Neutron notfound exception. make sure it gets # translated into a 404 error with mock.patch( 'neutron.pecan_wsgi.controllers.resource.' 'CollectionsController.get', side_effect=n_exc.NotFound() ): response = self.app.get('/v2.0/ports.json', expect_errors=True) self.assertEqual(response.status_int, 404) def test_unexpected_exception(self): with mock.patch( 'neutron.pecan_wsgi.controllers.resource.' 'CollectionsController.get', side_effect=ValueError('secretpassword') ): response = self.app.get('/v2.0/ports.json', expect_errors=True) self.assertNotIn(response.body, 'secretpassword') self.assertEqual(response.status_int, 500) neutron-12.1.1/neutron/tests/functional/pecan_wsgi/test_hooks.py0000664000175000017500000005333213553660047025200 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib.callbacks import events from neutron_lib import context from neutron_lib.db import constants as db_const from neutron_lib.plugins import directory from oslo_config import cfg from oslo_policy import policy as oslo_policy from oslo_serialization import jsonutils from neutron.api.v2 import attributes from neutron.db.quota import driver as quota_driver from neutron import manager from neutron.pecan_wsgi.controllers import resource from neutron import policy from neutron.tests.functional.pecan_wsgi import test_functional class TestOwnershipHook(test_functional.PecanFunctionalTest): def test_network_ownership_check(self): net_response = self.app.post_json( '/v2.0/networks.json', params={'network': {'name': 'meh'}}, headers={'X-Project-Id': 'tenid'}) network_id = jsonutils.loads(net_response.body)['network']['id'] port_response = self.app.post_json( '/v2.0/ports.json', params={'port': {'network_id': network_id, 'admin_state_up': True}}, headers={'X-Project-Id': 'tenid'}) self.assertEqual(201, port_response.status_int) class TestQueryParamatersHook(test_functional.PecanFunctionalTest): def test_if_match_on_update(self): net_response = jsonutils.loads(self.app.post_json( '/v2.0/networks.json', params={'network': {'name': 'meh'}}, headers={'X-Project-Id': 'tenid'}).body) network_id = net_response['network']['id'] response = self.app.put_json('/v2.0/networks/%s.json' % network_id, params={'network': {'name': 'cat'}}, headers={'X-Project-Id': 'tenid', 'If-Match': 'revision_number=0'}, expect_errors=True) # revision plugin not supported by default, so badrequest self.assertEqual(400, response.status_int) class TestQueryParamatersHookWithRevision(test_functional.PecanFunctionalTest): def setUp(self): cfg.CONF.set_override('service_plugins', ['revisions']) super(TestQueryParamatersHookWithRevision, self).setUp() def test_if_match_on_update(self): net_response = jsonutils.loads(self.app.post_json( '/v2.0/networks.json', params={'network': {'name': 'meh'}}, headers={'X-Project-Id': 'tenid'}).body) network_id = net_response['network']['id'] rev = net_response['network']['revision_number'] stale = rev - 1 response = self.app.put_json( '/v2.0/networks/%s.json' % network_id, params={'network': {'name': 'cat'}}, headers={'X-Project-Id': 'tenid', 'If-Match': 'revision_number=%s' % stale}, expect_errors=True) self.assertEqual(412, response.status_int) self.app.put_json('/v2.0/networks/%s.json' % network_id, params={'network': {'name': 'cat'}}, headers={'X-Project-Id': 'tenid', 'If-Match': 'revision_number=%s' % rev}) class TestQuotaEnforcementHook(test_functional.PecanFunctionalTest): def test_quota_enforcement_single(self): ctx = context.get_admin_context() quota_driver.DbQuotaDriver.update_quota_limit( ctx, 'tenid', 'network', 1) # There is enough headroom for creating a network response = self.app.post_json( '/v2.0/networks.json', params={'network': {'name': 'meh'}}, headers={'X-Project-Id': 'tenid'}) self.assertEqual(response.status_int, 201) # But a second request will fail response = self.app.post_json( '/v2.0/networks.json', params={'network': {'name': 'meh-2'}}, headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(response.status_int, 409) def test_quota_enforcement_bulk_request(self): ctx = context.get_admin_context() quota_driver.DbQuotaDriver.update_quota_limit( ctx, 'tenid', 'network', 3) # There is enough headroom for a bulk request creating 2 networks response = self.app.post_json( '/v2.0/networks.json', params={'networks': [ {'name': 'meh1'}, {'name': 'meh2'}]}, headers={'X-Project-Id': 'tenid'}) self.assertEqual(response.status_int, 201) # But it won't be possible to create 2 more networks... response = self.app.post_json( '/v2.0/networks.json', params={'networks': [ {'name': 'meh3'}, {'name': 'meh4'}]}, headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(response.status_int, 409) class TestPolicyEnforcementHook(test_functional.PecanFunctionalTest): FAKE_RESOURCE = { 'mehs': { 'id': {'allow_post': False, 'allow_put': False, 'is_visible': True, 'primary_key': True}, 'attr': {'allow_post': True, 'allow_put': True, 'is_visible': True, 'default': ''}, 'restricted_attr': {'allow_post': True, 'allow_put': True, 'is_visible': True, 'default': ''}, 'tenant_id': {'allow_post': True, 'allow_put': False, 'required_by_policy': True, 'validate': {'type:string': db_const.PROJECT_ID_FIELD_SIZE}, 'is_visible': True} } } def setUp(self): # Create a controller for a fake resource. This will make the tests # independent from the evolution of the API (so if one changes the API # or the default policies there won't be any risk of breaking these # tests, or at least I hope so) super(TestPolicyEnforcementHook, self).setUp() self.mock_plugin = mock.Mock() attributes.RESOURCE_ATTRIBUTE_MAP.update(self.FAKE_RESOURCE) manager.NeutronManager.set_plugin_for_resource('mehs', self.mock_plugin) fake_controller = resource.CollectionsController('mehs', 'meh') manager.NeutronManager.set_controller_for_resource( 'mehs', fake_controller) # Inject policies for the fake resource policy.init() policy._ENFORCER.set_rules( oslo_policy.Rules.from_dict( {'create_meh': '', 'update_meh': 'rule:admin_only', 'delete_meh': 'rule:admin_only', 'get_meh': 'rule:admin_only or field:mehs:id=xxx', 'get_meh:restricted_attr': 'rule:admin_only'}), overwrite=False) def test_before_on_create_authorized(self): # Mock a return value for an hypothetical create operation self.mock_plugin.create_meh.return_value = { 'id': 'xxx', 'attr': 'meh', 'restricted_attr': '', 'tenant_id': 'tenid'} response = self.app.post_json('/v2.0/mehs.json', params={'meh': {'attr': 'meh'}}, headers={'X-Project-Id': 'tenid'}) # We expect this operation to succeed self.assertEqual(201, response.status_int) self.assertEqual(0, self.mock_plugin.get_meh.call_count) self.assertEqual(1, self.mock_plugin.create_meh.call_count) def test_before_on_put_not_authorized(self): # The policy hook here should load the resource, and therefore we must # mock a get response self.mock_plugin.get_meh.return_value = { 'id': 'xxx', 'attr': 'meh', 'restricted_attr': '', 'tenant_id': 'tenid'} # The policy engine should trigger an exception in 'before', and the # plugin method should not be called at all response = self.app.put_json('/v2.0/mehs/xxx.json', params={'meh': {'attr': 'meh'}}, headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(403, response.status_int) self.assertEqual(1, self.mock_plugin.get_meh.call_count) self.assertEqual(0, self.mock_plugin.update_meh.call_count) def test_before_on_put_not_found_when_not_authorized_to_get(self): # the user won't even have permission to view this resource # so the error on unauthorized updates should be translated into # a 404 self.mock_plugin.get_meh.return_value = { 'id': 'yyy', 'attr': 'meh', 'restricted_attr': '', 'tenant_id': 'tenid'} response = self.app.put_json('/v2.0/mehs/yyy.json', params={'meh': {'attr': 'meh'}}, headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(404, response.status_int) self.assertEqual(1, self.mock_plugin.get_meh.call_count) self.assertEqual(0, self.mock_plugin.update_meh.call_count) def test_before_on_delete_not_authorized(self): # The policy hook here should load the resource, and therefore we must # mock a get response self.mock_plugin.delete_meh.return_value = None self.mock_plugin.get_meh.return_value = { 'id': 'xxx', 'attr': 'meh', 'restricted_attr': '', 'tenant_id': 'tenid'} # The policy engine should trigger an exception in 'before', and the # plugin method should not be called response = self.app.delete_json('/v2.0/mehs/xxx.json', headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(403, response.status_int) self.assertEqual(1, self.mock_plugin.get_meh.call_count) self.assertEqual(0, self.mock_plugin.delete_meh.call_count) def test_after_on_get_not_found(self): # The GET test policy will deny access to anything whose id is not # 'xxx', so the following request should be forbidden and presented # to the user as an HTTPNotFound self.mock_plugin.get_meh.return_value = { 'id': 'yyy', 'attr': 'meh', 'restricted_attr': '', 'tenant_id': 'tenid'} # The policy engine should trigger an exception in 'after', and the # plugin method should be called response = self.app.get('/v2.0/mehs/yyy.json', headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(404, response.status_int) self.assertEqual(1, self.mock_plugin.get_meh.call_count) def test_after_on_get_excludes_admin_attribute(self): self.mock_plugin.get_meh.return_value = { 'id': 'xxx', 'attr': 'meh', 'restricted_attr': '', 'tenant_id': 'tenid'} response = self.app.get('/v2.0/mehs/xxx.json', headers={'X-Project-Id': 'tenid'}) self.assertEqual(200, response.status_int) json_response = jsonutils.loads(response.body) self.assertNotIn('restricted_attr', json_response['meh']) def test_after_on_list_excludes_admin_attribute(self): self.mock_plugin.get_mehs.return_value = [{ 'id': 'xxx', 'attr': 'meh', 'restricted_attr': '', 'tenant_id': 'tenid'}] response = self.app.get('/v2.0/mehs', headers={'X-Project-Id': 'tenid'}) self.assertEqual(200, response.status_int) json_response = jsonutils.loads(response.body) self.assertNotIn('restricted_attr', json_response['mehs'][0]) def test_after_inits_policy(self): self.mock_plugin.get_mehs.return_value = [{ 'id': 'xxx', 'attr': 'meh', 'restricted_attr': '', 'tenant_id': 'tenid'}] policy.reset() response = self.app.get('/v2.0/mehs', headers={'X-Project-Id': 'tenid'}) self.assertEqual(200, response.status_int) class TestMetricsNotifierHook(test_functional.PecanFunctionalTest): def setUp(self): patcher = mock.patch('neutron.pecan_wsgi.hooks.notifier.NotifierHook.' '_notifier') self.mock_notifier = patcher.start().info super(TestMetricsNotifierHook, self).setUp() def test_post_put_delete_triggers_notification(self): req_headers = {'X-Project-Id': 'tenid', 'X-Roles': 'admin'} payload = {'network': {'name': 'meh'}} response = self.app.post_json( '/v2.0/networks.json', params=payload, headers=req_headers) self.assertEqual(201, response.status_int) json_body = jsonutils.loads(response.body) self.assertEqual( [mock.call(mock.ANY, 'network.create.start', payload), mock.call(mock.ANY, 'network.create.end', json_body)], self.mock_notifier.mock_calls) self.mock_notifier.reset_mock() network_id = json_body['network']['id'] payload = {'network': {'name': 'meh-2'}} response = self.app.put_json( '/v2.0/networks/%s.json' % network_id, params=payload, headers=req_headers) self.assertEqual(200, response.status_int) json_body = jsonutils.loads(response.body) # id should be in payload sent to notifier payload['id'] = network_id self.assertEqual( [mock.call(mock.ANY, 'network.update.start', payload), mock.call(mock.ANY, 'network.update.end', json_body)], self.mock_notifier.mock_calls) self.mock_notifier.reset_mock() before_payload = {'network_id': network_id} after_payload = before_payload.copy() after_payload['network'] = directory.get_plugin().get_network( context.get_admin_context(), network_id) response = self.app.delete( '/v2.0/networks/%s.json' % network_id, headers=req_headers) self.assertEqual(204, response.status_int) self.assertEqual( [mock.call(mock.ANY, 'network.delete.start', before_payload), mock.call(mock.ANY, 'network.delete.end', after_payload)], self.mock_notifier.mock_calls) def test_bulk_create_triggers_notification(self): req_headers = {'X-Project-Id': 'tenid', 'X-Roles': 'admin'} payload = {'networks': [{'name': 'meh_1'}, {'name': 'meh_2'}]} response = self.app.post_json( '/v2.0/networks.json', params=payload, headers=req_headers) self.assertEqual(201, response.status_int) json_body = jsonutils.loads(response.body) self.assertEqual(2, self.mock_notifier.call_count) self.mock_notifier.assert_has_calls( [mock.call(mock.ANY, 'network.create.start', payload), mock.call(mock.ANY, 'network.create.end', json_body)]) def test_bad_create_doesnt_emit_end(self): req_headers = {'X-Project-Id': 'tenid', 'X-Roles': 'admin'} payload = {'network': {'name': 'meh'}} plugin = directory.get_plugin() with mock.patch.object(plugin, 'create_network', side_effect=ValueError): response = self.app.post_json( '/v2.0/networks.json', params=payload, headers=req_headers, expect_errors=True) self.assertEqual(500, response.status_int) self.assertEqual( [mock.call(mock.ANY, 'network.create.start', mock.ANY)], self.mock_notifier.mock_calls) def test_bad_update_doesnt_emit_end(self): req_headers = {'X-Project-Id': 'tenid', 'X-Roles': 'admin'} payload = {'network': {'name': 'meh'}} response = self.app.post_json( '/v2.0/networks.json', params=payload, headers=req_headers, expect_errors=True) self.assertEqual(201, response.status_int) json_body = jsonutils.loads(response.body) self.mock_notifier.reset_mock() plugin = directory.get_plugin() with mock.patch.object(plugin, 'update_network', side_effect=ValueError): response = self.app.put_json( '/v2.0/networks/%s.json' % json_body['network']['id'], params=payload, headers=req_headers, expect_errors=True) self.assertEqual(500, response.status_int) self.assertEqual( [mock.call(mock.ANY, 'network.update.start', mock.ANY)], self.mock_notifier.mock_calls) def test_bad_delete_doesnt_emit_end(self): req_headers = {'X-Project-Id': 'tenid', 'X-Roles': 'admin'} payload = {'network': {'name': 'meh'}} response = self.app.post_json( '/v2.0/networks.json', params=payload, headers=req_headers, expect_errors=True) self.assertEqual(201, response.status_int) json_body = jsonutils.loads(response.body) self.mock_notifier.reset_mock() plugin = directory.get_plugin() with mock.patch.object(plugin, 'delete_network', side_effect=ValueError): response = self.app.delete( '/v2.0/networks/%s.json' % json_body['network']['id'], headers=req_headers, expect_errors=True) self.assertEqual(500, response.status_int) self.assertEqual( [mock.call(mock.ANY, 'network.delete.start', mock.ANY)], self.mock_notifier.mock_calls) class TestCallbackRegistryNotifier(test_functional.PecanFunctionalTest): def setUp(self): super(TestCallbackRegistryNotifier, self).setUp() patcher = mock.patch('neutron.pecan_wsgi.hooks.notifier.registry') self.mock_notifier = patcher.start().publish def _create(self, bulk=False): if bulk: body = {'networks': [{'name': 'meh-1'}, {'name': 'meh-2'}]} else: body = {'network': {'name': 'meh-1'}} response = self.app.post_json( '/v2.0/networks.json', params=body, headers={'X-Project-Id': 'tenid'}) return response.json def test_create(self): self._create() self.mock_notifier.assert_called_once_with( 'network', events.BEFORE_RESPONSE, mock.ANY, payload=mock.ANY) payload = self.mock_notifier.call_args[1]['payload'] self.assertEqual('network.create.end', payload.method_name) self.assertEqual('create_network', payload.action) self.assertEqual('networks', payload.collection_name) actual = payload.latest_state self.assertEqual('meh-1', actual['network']['name']) def test_create_bulk(self): self._create(bulk=True) self.mock_notifier.assert_called_once_with( 'network', events.BEFORE_RESPONSE, mock.ANY, payload=mock.ANY) payload = self.mock_notifier.call_args[1]['payload'] self.assertEqual('network.create.end', payload.method_name) self.assertEqual('create_network', payload.action) self.assertEqual('networks', payload.collection_name) actual = payload.latest_state self.assertEqual(2, len(actual['networks'])) self.assertEqual('meh-1', actual['networks'][0]['name']) self.assertEqual('meh-2', actual['networks'][1]['name']) def test_update(self): network_id = self._create()['network']['id'] self.mock_notifier.reset_mock() self.app.put_json('/v2.0/networks/%s.json' % network_id, params={'network': {'name': 'new-meh'}}, headers={'X-Project-Id': 'tenid'}) self.mock_notifier.assert_called_once_with( 'network', events.BEFORE_RESPONSE, mock.ANY, payload=mock.ANY) payload = self.mock_notifier.call_args[1]['payload'] self.assertEqual('network.update.end', payload.method_name) self.assertEqual('update_network', payload.action) self.assertEqual('networks', payload.collection_name) actual_new = payload.latest_state self.assertEqual('new-meh', actual_new['network']['name']) actual_original = payload.states[0] self.assertEqual(network_id, actual_original['id']) def test_delete(self): network_id = self._create()['network']['id'] self.mock_notifier.reset_mock() self.app.delete( '/v2.0/networks/%s.json' % network_id, headers={'X-Project-Id': 'tenid'}) self.mock_notifier.assert_called_once_with( 'network', events.BEFORE_RESPONSE, mock.ANY, payload=mock.ANY) payload = self.mock_notifier.call_args[1]['payload'] self.assertEqual('network.delete.end', payload.method_name) self.assertEqual('delete_network', payload.action) self.assertEqual('networks', payload.collection_name) actual = payload.latest_state self.assertEqual(network_id, actual['network']['id']) neutron-12.1.1/neutron/tests/functional/pecan_wsgi/config.py0000664000175000017500000000165113553660046024257 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # use main app settings except for the port number so testing doesn't need to # listen on the main neutron port app = { 'root': 'neutron.pecan_wsgi.controllers.root.RootController', 'modules': ['neutron.pecan_wsgi'], 'errors': { 400: '/error', '__force_dict__': True } } neutron-12.1.1/neutron/tests/functional/pecan_wsgi/test_controllers.py0000664000175000017500000014206713553660047026427 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron_lib import constants as n_const from neutron_lib import context from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_db import exception as db_exc from oslo_policy import policy as oslo_policy from oslo_serialization import jsonutils from oslo_utils import uuidutils import pecan from pecan import request from neutron.api import extensions from neutron.conf import quota as qconf from neutron import manager from neutron.pecan_wsgi.controllers import root as controllers from neutron.pecan_wsgi.controllers import utils as controller_utils from neutron import policy from neutron.tests.common import helpers from neutron.tests.functional.pecan_wsgi import test_functional from neutron.tests.functional.pecan_wsgi import utils as pecan_utils from neutron.tests.unit import dummy_plugin _SERVICE_PLUGIN_RESOURCE = 'serviceplugin' _SERVICE_PLUGIN_COLLECTION = _SERVICE_PLUGIN_RESOURCE + 's' _SERVICE_PLUGIN_INDEX_BODY = {_SERVICE_PLUGIN_COLLECTION: []} class FakeServicePluginController(controller_utils.NeutronPecanController): resource = _SERVICE_PLUGIN_RESOURCE collection = _SERVICE_PLUGIN_COLLECTION @pecan.expose(generic=True, content_type='application/json', template='json') def index(self): return _SERVICE_PLUGIN_INDEX_BODY class TestRootController(test_functional.PecanFunctionalTest): """Test version listing on root URI.""" base_url = '/' def setUp(self): super(TestRootController, self).setUp() self.setup_service_plugin() self.plugin = directory.get_plugin() self.ctx = context.get_admin_context() def setup_service_plugin(self): manager.NeutronManager.set_controller_for_resource( _SERVICE_PLUGIN_COLLECTION, FakeServicePluginController(_SERVICE_PLUGIN_COLLECTION, _SERVICE_PLUGIN_RESOURCE)) def _test_method_returns_code(self, method, code=200): api_method = getattr(self.app, method) response = api_method(self.base_url, expect_errors=True) self.assertEqual(response.status_int, code) def test_get(self): response = self.app.get(self.base_url) self.assertEqual(response.status_int, 200) json_body = jsonutils.loads(response.body) versions = json_body.get('versions') self.assertEqual(1, len(versions)) for (attr, value) in controllers.V2Controller.version_info.items(): self.assertIn(attr, versions[0]) self.assertEqual(value, versions[0][attr]) def test_methods(self): self._test_method_returns_code('post', 405) self._test_method_returns_code('patch', 405) self._test_method_returns_code('delete', 405) self._test_method_returns_code('head', 405) self._test_method_returns_code('put', 405) class TestV2Controller(TestRootController): base_url = '/v2.0/' def test_get(self): """Verify current version info are returned.""" response = self.app.get(self.base_url) self.assertEqual(response.status_int, 200) json_body = jsonutils.loads(response.body) self.assertIn('resources', json_body) self.assertIsInstance(json_body['resources'], list) for r in json_body['resources']: self.assertIn("links", r) self.assertIn("name", r) self.assertIn("collection", r) self.assertIn(self.base_url, r['links'][0]['href']) def test_get_no_trailing_slash(self): response = self.app.get(self.base_url[:-1], expect_errors=True) self.assertEqual(response.status_int, 404) def test_routing_successs(self): """Test dispatch to controller for existing resource.""" response = self.app.get('%sports.json' % self.base_url) self.assertEqual(response.status_int, 200) def test_routing_failure(self): """Test dispatch to controller for non-existing resource.""" response = self.app.get('%sidonotexist.json' % self.base_url, expect_errors=True) self.assertEqual(response.status_int, 404) def test_methods(self): self._test_method_returns_code('post', 405) self._test_method_returns_code('put', 405) self._test_method_returns_code('patch', 405) self._test_method_returns_code('delete', 405) self._test_method_returns_code('head', 405) self._test_method_returns_code('delete', 405) class TestExtensionsController(TestRootController): """Test extension listing and detail reporting.""" base_url = '/v2.0/extensions' def _get_supported_extensions(self): ext_mgr = extensions.PluginAwareExtensionManager.get_instance() return ext_mgr.get_supported_extension_aliases() def test_index(self): response = self.app.get(self.base_url) self.assertEqual(response.status_int, 200) json_body = jsonutils.loads(response.body) returned_aliases = [ext['alias'] for ext in json_body['extensions']] supported_extensions = self._get_supported_extensions() self.assertEqual(supported_extensions, set(returned_aliases)) def test_get(self): # Fetch any extension supported by plugins test_alias = self._get_supported_extensions().pop() response = self.app.get('%s/%s' % (self.base_url, test_alias)) self.assertEqual(response.status_int, 200) json_body = jsonutils.loads(response.body) self.assertEqual(test_alias, json_body['extension']['alias']) def test_methods(self): self._test_method_returns_code('post', 404) self._test_method_returns_code('put', 404) self._test_method_returns_code('patch', 404) self._test_method_returns_code('delete', 404) self._test_method_returns_code('head', 404) self._test_method_returns_code('delete', 404) class TestQuotasController(test_functional.PecanFunctionalTest): """Test quota management API controller.""" base_url = '/v2.0/quotas' default_expected_limits = { 'network': qconf.DEFAULT_QUOTA_NETWORK, 'port': qconf.DEFAULT_QUOTA_PORT, 'subnet': qconf.DEFAULT_QUOTA_SUBNET} def _verify_limits(self, response, limits): for resource, limit in limits.items(): self.assertEqual(limit, response['quota'][resource]) def _verify_default_limits(self, response): self._verify_limits(response, self.default_expected_limits) def _verify_after_update(self, response, updated_limits): expected_limits = self.default_expected_limits.copy() expected_limits.update(updated_limits) self._verify_limits(response, expected_limits) def test_index_admin(self): # NOTE(salv-orlando): The quota controller has an hardcoded check for # admin-ness for this operation, which is supposed to return quotas for # all tenants. Such check is "vestigial" from the home-grown WSGI and # shall be removed response = self.app.get('%s.json' % self.base_url, headers={'X-Project-Id': 'admin', 'X-Roles': 'admin'}) self.assertEqual(200, response.status_int) def test_index(self): response = self.app.get('%s.json' % self.base_url, expect_errors=True) self.assertEqual(403, response.status_int) def test_get_admin(self): response = self.app.get('%s/foo.json' % self.base_url, headers={'X-Project-Id': 'admin', 'X-Roles': 'admin'}) self.assertEqual(200, response.status_int) # As quota limits have not been updated, expect default values json_body = jsonutils.loads(response.body) self._verify_default_limits(json_body) def test_get(self): # It is not ok to access another tenant's limits url = '%s/foo.json' % self.base_url response = self.app.get(url, expect_errors=True) self.assertEqual(403, response.status_int) # It is however ok to retrieve your own limits response = self.app.get(url, headers={'X-Project-Id': 'foo'}) self.assertEqual(200, response.status_int) json_body = jsonutils.loads(response.body) self._verify_default_limits(json_body) def test_put_get_delete(self): # PUT and DELETE actions are in the same test as a meaningful DELETE # test would require a put anyway url = '%s/foo.json' % self.base_url response = self.app.put_json(url, params={'quota': {'network': 99}}, headers={'X-Project-Id': 'admin', 'X-Roles': 'admin'}) self.assertEqual(200, response.status_int) json_body = jsonutils.loads(response.body) self._verify_after_update(json_body, {'network': 99}) response = self.app.get(url, headers={'X-Project-Id': 'foo'}) self.assertEqual(200, response.status_int) json_body = jsonutils.loads(response.body) self._verify_after_update(json_body, {'network': 99}) response = self.app.delete(url, headers={'X-Project-Id': 'admin', 'X-Roles': 'admin'}) self.assertEqual(204, response.status_int) self.assertFalse(response.body) # As DELETE does not return a body we need another GET response = self.app.get(url, headers={'X-Project-Id': 'foo'}) self.assertEqual(200, response.status_int) json_body = jsonutils.loads(response.body) self._verify_default_limits(json_body) def test_update_list_delete(self): # PUT and DELETE actions are in the same test as a meaningful DELETE # test would require a put anyway url = '%s/foo.json' % self.base_url response = self.app.put_json(url, params={'quota': {'network': 99}}, headers={'X-Project-Id': 'admin', 'X-Roles': 'admin'}) self.assertEqual(200, response.status_int) json_body = jsonutils.loads(response.body) self._verify_after_update(json_body, {'network': 99}) response = self.app.get(self.base_url, headers={'X-Project-Id': 'admin', 'X-Roles': 'admin'}) self.assertEqual(200, response.status_int) json_body = jsonutils.loads(response.body) found = False for qs in json_body['quotas']: if qs['tenant_id'] == 'foo': found = True self.assertTrue(found) response = self.app.delete(url, headers={'X-Project-Id': 'admin', 'X-Roles': 'admin'}) self.assertEqual(204, response.status_int) self.assertFalse(response.body) response = self.app.get(self.base_url, headers={'X-Project-Id': 'admin', 'X-Roles': 'admin'}) self.assertEqual(200, response.status_int) json_body = jsonutils.loads(response.body) for qs in json_body['quotas']: self.assertNotEqual('foo', qs['tenant_id']) def test_quotas_get_defaults(self): response = self.app.get('%s/foo/default.json' % self.base_url, headers={'X-Project-Id': 'admin', 'X-Roles': 'admin'}) self.assertEqual(200, response.status_int) # As quota limits have not been updated, expect default values json_body = jsonutils.loads(response.body) self._verify_default_limits(json_body) def test_get_tenant_info(self): response = self.app.get('%s/tenant.json' % self.base_url, headers={'X-Project-Id': 'admin', 'X-Roles': 'admin'}) self.assertEqual(200, response.status_int) json_body = jsonutils.loads(response.body) self.assertEqual('admin', json_body['tenant']['tenant_id']) class TestResourceController(TestRootController): """Test generic controller""" # TODO(salv-orlando): This test case must not explicitly test the 'port' # resource. Also it should validate correct plugin/resource association base_url = '/v2.0' def setUp(self): super(TestResourceController, self).setUp() policy.init() self.addCleanup(policy.reset) self._gen_port() def _gen_port(self): network_id = self.plugin.create_network(context.get_admin_context(), { 'network': {'name': 'pecannet', 'tenant_id': 'tenid', 'shared': False, 'admin_state_up': True, 'status': 'ACTIVE'}})['id'] self.port = self.plugin.create_port(context.get_admin_context(), { 'port': {'tenant_id': 'tenid', 'network_id': network_id, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, 'mac_address': '00:11:22:33:44:55', 'admin_state_up': True, 'device_id': 'FF', 'device_owner': 'pecan', 'name': 'pecan'}}) def test_get(self): response = self.app.get('/v2.0/ports.json') self.assertEqual(response.status_int, 200) def _check_item(self, expected, item): for attribute in expected: self.assertIn(attribute, item) self.assertEqual(len(expected), len(item)) def _test_get_collection_with_fields_selector(self, fields=None): fields = fields or [] query_params = ['fields=%s' % field for field in fields] url = '/v2.0/ports.json' if query_params: url = '%s?%s' % (url, '&'.join(query_params)) list_resp = self.app.get(url, headers={'X-Project-Id': 'tenid'}) self.assertEqual(200, list_resp.status_int) for item in jsonutils.loads(list_resp.body).get('ports', []): for field in fields: self.assertIn(field, item) if fields: self.assertEqual(len(fields), len(item)) else: for field in ('id', 'name', 'device_owner'): self.assertIn(field, item) def test_get_collection_with_multiple_fields_selector(self): self._test_get_collection_with_fields_selector(fields=['id', 'name']) def test_get_collection_with_single_fields_selector(self): self._test_get_collection_with_fields_selector(fields=['name']) def test_get_collection_without_fields_selector(self): self._test_get_collection_with_fields_selector(fields=[]) def test_project_id_in_mandatory_fields(self): # ports only specifies that tenant_id is mandatory, but project_id # should still be passed to the plugin. mock_get = mock.patch.object(self.plugin, 'get_ports', return_value=[]).start() self.app.get( '/v2.0/ports.json?fields=id', headers={'X-Project-Id': 'tenid'} ) self.assertIn('project_id', mock_get.mock_calls[-1][2]['fields']) def test_get_item_with_fields_selector(self): item_resp = self.app.get( '/v2.0/ports/%s.json?fields=id&fields=name' % self.port['id'], headers={'X-Project-Id': 'tenid'}) self.assertEqual(200, item_resp.status_int) self._check_item(['id', 'name'], jsonutils.loads(item_resp.body)['port']) # Explicitly require an attribute which is also 'required_by_policy'. # The attribute should not be stripped while generating the response item_resp = self.app.get( '/v2.0/ports/%s.json?fields=id&fields=tenant_id' % self.port['id'], headers={'X-Project-Id': 'tenid'}) self.assertEqual(200, item_resp.status_int) self._check_item(['id', 'tenant_id'], jsonutils.loads(item_resp.body)['port']) def test_duped_and_empty_fields_stripped(self): mock_get = mock.patch.object(self.plugin, 'get_ports', return_value=[]).start() self.app.get( '/v2.0/ports.json?fields=id&fields=name&fields=&fields=name', headers={'X-Project-Id': 'tenid'} ) received = mock_get.mock_calls[-1][2]['fields'] self.assertNotIn('', received) self.assertEqual(len(received), len(set(received))) def test_post(self): response = self.app.post_json( '/v2.0/ports.json', params={'port': {'network_id': self.port['network_id'], 'admin_state_up': True, 'tenant_id': 'tenid'}}, headers={'X-Project-Id': 'tenid'}) self.assertEqual(response.status_int, 201) def test_post_with_retry(self): self._create_failed = False orig = self.plugin.create_port def new_create(*args, **kwargs): if not self._create_failed: self._create_failed = True raise db_exc.RetryRequest(ValueError()) return orig(*args, **kwargs) with mock.patch.object(self.plugin, 'create_port', new=new_create): response = self.app.post_json( '/v2.0/ports.json', params={'port': {'network_id': self.port['network_id'], 'admin_state_up': True, 'tenant_id': 'tenid'}}, headers={'X-Project-Id': 'tenid'}) self.assertEqual(201, response.status_int) def test_put(self): response = self.app.put_json('/v2.0/ports/%s.json' % self.port['id'], params={'port': {'name': 'test'}}, headers={'X-Project-Id': 'tenid'}) self.assertEqual(response.status_int, 200) json_body = jsonutils.loads(response.body) self.assertEqual(1, len(json_body)) self.assertIn('port', json_body) self.assertEqual('test', json_body['port']['name']) self.assertEqual('tenid', json_body['port']['tenant_id']) def test_delete(self): response = self.app.delete('/v2.0/ports/%s.json' % self.port['id'], headers={'X-Project-Id': 'tenid'}) self.assertEqual(response.status_int, 204) self.assertFalse(response.body) def test_delete_disallows_body(self): response = self.app.delete_json( '/v2.0/ports/%s.json' % self.port['id'], params={'port': {'name': 'test'}}, headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(response.status_int, 400) def test_plugin_initialized(self): self.assertIsNotNone(manager.NeutronManager._instance) def test_methods(self): self._test_method_returns_code('post', 405) self._test_method_returns_code('put', 405) self._test_method_returns_code('patch', 405) self._test_method_returns_code('delete', 405) self._test_method_returns_code('head', 405) self._test_method_returns_code('delete', 405) def test_post_with_empty_body(self): response = self.app.post_json( '/v2.0/ports.json', headers={'X-Project-Id': 'tenid'}, params={}, expect_errors=True) self.assertEqual(response.status_int, 400) def test_post_with_unsupported_json_type(self): response = self.app.post_json( '/v2.0/ports.json', headers={'X-Project-Id': 'tenid'}, params=[1, 2, 3], expect_errors=True) self.assertEqual(response.status_int, 400) def test_bulk_create(self): response = self.app.post_json( '/v2.0/ports.json', params={'ports': [{'network_id': self.port['network_id'], 'admin_state_up': True, 'tenant_id': 'tenid'}, {'network_id': self.port['network_id'], 'admin_state_up': True, 'tenant_id': 'tenid'}] }, headers={'X-Project-Id': 'tenid'}) self.assertEqual(response.status_int, 201) json_body = jsonutils.loads(response.body) self.assertIn('ports', json_body) self.assertEqual(2, len(json_body['ports'])) def test_emulated_bulk_create(self): self.plugin._FORCE_EMULATED_BULK = True response = self.app.post_json( '/v2.0/ports.json', params={'ports': [{'network_id': self.port['network_id'], 'admin_state_up': True, 'tenant_id': 'tenid'}, {'network_id': self.port['network_id'], 'admin_state_up': True, 'tenant_id': 'tenid'}] }, headers={'X-Project-Id': 'tenid'}) self.assertEqual(response.status_int, 201) json_body = jsonutils.loads(response.body) self.assertIn('ports', json_body) self.assertEqual(2, len(json_body['ports'])) def test_emulated_bulk_create_rollback(self): self.plugin._FORCE_EMULATED_BULK = True response = self.app.post_json( '/v2.0/ports.json', params={'ports': [{'network_id': self.port['network_id'], 'admin_state_up': True, 'tenant_id': 'tenid'}, {'network_id': self.port['network_id'], 'admin_state_up': True, 'tenant_id': 'tenid'}, {'network_id': 'bad_net_id', 'admin_state_up': True, 'tenant_id': 'tenid'}] }, headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(response.status_int, 400) response = self.app.get( '/v2.0/ports.json', headers={'X-Project-Id': 'tenid'}) # all ports should be rolled back from above so we are just left # with the one created in setup self.assertEqual(1, len(jsonutils.loads(response.body)['ports'])) def test_bulk_create_one_item(self): response = self.app.post_json( '/v2.0/ports.json', params={'ports': [{'network_id': self.port['network_id'], 'admin_state_up': True, 'tenant_id': 'tenid'}] }, headers={'X-Project-Id': 'tenid'}) self.assertEqual(response.status_int, 201) json_body = jsonutils.loads(response.body) self.assertIn('ports', json_body) self.assertEqual(1, len(json_body['ports'])) class TestPaginationAndSorting(test_functional.PecanFunctionalTest): RESOURCE_COUNT = 6 def setUp(self): super(TestPaginationAndSorting, self).setUp() policy.init() self.addCleanup(policy.reset) self.plugin = directory.get_plugin() self.ctx = context.get_admin_context() self._create_networks(self.RESOURCE_COUNT) self.networks = self._get_collection()['networks'] def _create_networks(self, count=1): network_ids = [] for index in range(count): network = {'name': 'pecannet-%d' % index, 'tenant_id': 'tenid', 'shared': False, 'admin_state_up': True, 'status': 'ACTIVE'} network_id = self.plugin.create_network( self.ctx, {'network': network})['id'] network_ids.append(network_id) return network_ids def _get_collection(self, collection=None, limit=None, marker=None, fields=None, page_reverse=False, sort_key=None, sort_dir=None): collection = collection or 'networks' fields = fields or [] query_params = [] if limit: query_params.append('limit=%d' % limit) if marker: query_params.append('marker=%s' % marker) if page_reverse: query_params.append('page_reverse=True') if sort_key: query_params.append('sort_key=%s' % sort_key) if sort_dir: query_params.append('sort_dir=%s' % sort_dir) query_params.extend(['%s%s' % ('fields=', field) for field in fields]) url = '/v2.0/%s.json' % collection if query_params: url = '%s?%s' % (url, '&'.join(query_params)) list_resp = self.app.get(url, headers={'X-Project-Id': 'tenid'}) self.assertEqual(200, list_resp.status_int) return list_resp.json def _test_get_collection_with_pagination(self, expected_list, collection=None, limit=None, marker=None, fields=None, page_reverse=False, sort_key=None, sort_dir=None): expected_list = expected_list or [] collection = collection or 'networks' list_resp = self._get_collection(collection=collection, limit=limit, marker=marker, fields=fields, page_reverse=page_reverse, sort_key=sort_key, sort_dir=sort_dir) if limit and marker: links_key = '%s_links' % collection self.assertIn(links_key, list_resp) if not fields or 'id' in fields: list_resp_ids = [item['id'] for item in list_resp[collection]] self.assertEqual(expected_list, list_resp_ids) if fields: for item in list_resp[collection]: for field in fields: self.assertIn(field, item) def test_get_collection_with_pagination_limit(self): self._test_get_collection_with_pagination([self.networks[0]['id']], limit=1) def test_get_collection_with_pagination_fields_no_pk(self): self._test_get_collection_with_pagination([self.networks[0]['id']], limit=1, fields=['name']) def test_get_collection_with_pagination_limit_over_count(self): expected_ids = [network['id'] for network in self.networks] self._test_get_collection_with_pagination( expected_ids, limit=self.RESOURCE_COUNT + 1) def test_get_collection_with_pagination_marker(self): marker = self.networks[2]['id'] expected_ids = [network['id'] for network in self.networks[3:]] self._test_get_collection_with_pagination(expected_ids, limit=3, marker=marker) def test_get_collection_with_pagination_marker_without_limit(self): marker = self.networks[2]['id'] expected_ids = [network['id'] for network in self.networks] self._test_get_collection_with_pagination(expected_ids, marker=marker) def test_get_collection_with_pagination_and_fields(self): expected_ids = [network['id'] for network in self.networks[:2]] self._test_get_collection_with_pagination( expected_ids, limit=2, fields=['id', 'name']) def test_get_collection_with_pagination_page_reverse(self): marker = self.networks[2]['id'] expected_ids = [network['id'] for network in self.networks[:2]] self._test_get_collection_with_pagination(expected_ids, limit=3, marker=marker, page_reverse=True) def test_get_collection_with_sorting_desc(self): nets = sorted(self.networks, key=lambda net: net['name'], reverse=True) expected_ids = [network['id'] for network in nets] self._test_get_collection_with_pagination(expected_ids, sort_key='name', sort_dir='desc') def test_get_collection_with_sorting_asc(self): nets = sorted(self.networks, key=lambda net: net['name']) expected_ids = [network['id'] for network in nets] self._test_get_collection_with_pagination(expected_ids, sort_key='name', sort_dir='asc') class TestRequestProcessing(TestRootController): def setUp(self): super(TestRequestProcessing, self).setUp() mock.patch('neutron.pecan_wsgi.hooks.notifier.registry').start() # request.context is thread-local storage so it has to be accessed by # the controller. We can capture it into a list here to assert on after # the request finishes. def capture_request_details(*args, **kwargs): self.captured_context = request.context self.request_params = kwargs mock.patch('neutron.pecan_wsgi.controllers.resource.' 'CollectionsController.get', side_effect=capture_request_details).start() mock.patch('neutron.pecan_wsgi.controllers.resource.' 'CollectionsController.create', side_effect=capture_request_details).start() mock.patch('neutron.pecan_wsgi.controllers.resource.' 'ItemController.get', side_effect=capture_request_details).start() # TODO(kevinbenton): add context tests for X-Roles etc def test_context_set_in_request(self): self.app.get('/v2.0/ports.json', headers={'X-Project-Id': 'tenant_id'}) self.assertEqual('tenant_id', self.captured_context['neutron_context'].tenant_id) def test_core_resource_identified(self): self.app.get('/v2.0/ports.json') self.assertEqual('port', self.captured_context['resource']) self.assertEqual('ports', self.captured_context['collection']) def test_lookup_identifies_resource_id(self): # We now this will return a 404 but that's not the point as it is # mocked self.app.get('/v2.0/ports/reina.json') self.assertEqual('port', self.captured_context['resource']) self.assertEqual('ports', self.captured_context['collection']) self.assertEqual('reina', self.captured_context['resource_id']) def test_resource_processing_post(self): self.app.post_json( '/v2.0/networks.json', params={'network': {'name': 'the_net', 'admin_state_up': True}}, headers={'X-Project-Id': 'tenid'}) self.assertEqual('network', self.captured_context['resource']) self.assertEqual('networks', self.captured_context['collection']) resources = self.captured_context['resources'] is_bulk = self.captured_context['is_bulk'] self.assertEqual(1, len(resources)) self.assertEqual('the_net', resources[0]['name']) self.assertTrue(resources[0]['admin_state_up']) self.assertFalse(is_bulk) def test_resource_processing_post_bulk(self): self.app.post_json( '/v2.0/networks.json', params={'networks': [{'name': 'the_net_1', 'admin_state_up': True}, {'name': 'the_net_2', 'admin_state_up': False}]}, headers={'X-Project-Id': 'tenid'}) resources = self.captured_context['resources'] is_bulk = self.captured_context['is_bulk'] self.assertEqual(2, len(resources)) self.assertTrue(resources[0]['admin_state_up']) self.assertEqual('the_net_1', resources[0]['name']) self.assertFalse(resources[1]['admin_state_up']) self.assertEqual('the_net_2', resources[1]['name']) self.assertTrue(is_bulk) def test_resource_processing_post_bulk_one_item(self): self.app.post_json( '/v2.0/networks.json', params={'networks': [{'name': 'the_net_1', 'admin_state_up': True}]}, headers={'X-Project-Id': 'tenid'}) resources = self.captured_context['resources'] is_bulk = self.captured_context['is_bulk'] self.assertEqual(1, len(resources)) self.assertTrue(is_bulk) def test_resource_processing_post_unknown_attribute_returns_400(self): response = self.app.post_json( '/v2.0/networks.json', params={'network': {'name': 'the_net', 'alien': 'E.T.', 'admin_state_up': True}}, headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(400, response.status_int) def test_resource_processing_post_validation_error_returns_400(self): response = self.app.post_json( '/v2.0/networks.json', params={'network': {'name': 'the_net', 'admin_state_up': 'invalid_value'}}, headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(400, response.status_int) def test_service_plugin_identified(self): # TODO(kevinbenton): fix the unit test setup to include an l3 plugin self.skipTest("A dummy l3 plugin needs to be setup") self.app.get('/v2.0/routers.json') self.assertEqual('router', self.req_stash['resource_type']) # make sure the core plugin was identified as the handler for ports self.assertEqual( directory.get_plugin(plugin_constants.L3), self.req_stash['plugin']) def test_service_plugin_uri(self): nm = manager.NeutronManager.get_instance() nm.path_prefix_resource_mappings[dummy_plugin.RESOURCE_NAME] = [ _SERVICE_PLUGIN_COLLECTION] response = self.do_request('/v2.0/dummy/serviceplugins.json') self.assertEqual(200, response.status_int) self.assertEqual(_SERVICE_PLUGIN_INDEX_BODY, response.json_body) class TestRouterController(TestResourceController): """Specialized tests for the router resource controller This test class adds tests specific for the router controller in order to verify the 'member_action' functionality, which this controller uses for adding and removing router interfaces. """ def setUp(self): cfg.CONF.set_override( 'service_plugins', ['neutron.services.l3_router.l3_router_plugin.L3RouterPlugin', 'neutron.services.flavors.flavors_plugin.FlavorsPlugin']) super(TestRouterController, self).setUp() policy.init() self.addCleanup(policy.reset) plugin = directory.get_plugin() ctx = context.get_admin_context() l3_plugin = directory.get_plugin(plugin_constants.L3) network_id = pecan_utils.create_network(ctx, plugin)['id'] self.subnet = pecan_utils.create_subnet(ctx, plugin, network_id) self.router = pecan_utils.create_router(ctx, l3_plugin) def test_member_actions_processing(self): response = self.app.put_json( '/v2.0/routers/%s/add_router_interface.json' % self.router['id'], params={'subnet_id': self.subnet['id']}, headers={'X-Project-Id': 'tenid'}) self.assertEqual(200, response.status_int) def test_non_existing_member_action_returns_404(self): response = self.app.put_json( '/v2.0/routers/%s/do_meh.json' % self.router['id'], params={'subnet_id': 'doesitevenmatter'}, headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(404, response.status_int) def test_unsupported_method_member_action(self): response = self.app.post_json( '/v2.0/routers/%s/add_router_interface.json' % self.router['id'], params={'subnet_id': self.subnet['id']}, headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(405, response.status_int) response = self.app.get( '/v2.0/routers/%s/add_router_interface.json' % self.router['id'], headers={'X-Project-Id': 'tenid'}, expect_errors=True) self.assertEqual(405, response.status_int) class TestDHCPAgentShimControllers(test_functional.PecanFunctionalTest): def setUp(self): super(TestDHCPAgentShimControllers, self).setUp() policy.init() policy._ENFORCER.set_rules( oslo_policy.Rules.from_dict( {'get_dhcp-agents': 'role:admin', 'get_dhcp-networks': 'role:admin', 'create_dhcp-networks': 'role:admin', 'delete_dhcp-networks': 'role:admin'}), overwrite=False) plugin = directory.get_plugin() ctx = context.get_admin_context() self.network = pecan_utils.create_network(ctx, plugin) self.agent = helpers.register_dhcp_agent() # NOTE(blogan): Not sending notifications because this test is for # testing the shim controllers plugin.agent_notifiers[n_const.AGENT_TYPE_DHCP] = None def test_list_dhcp_agents_hosting_network(self): response = self.app.get( '/v2.0/networks/%s/dhcp-agents.json' % self.network['id'], headers={'X-Roles': 'admin'}) self.assertEqual(200, response.status_int) def test_list_networks_on_dhcp_agent(self): response = self.app.get( '/v2.0/agents/%s/dhcp-networks.json' % self.agent.id, headers={'X-Project-Id': 'tenid', 'X-Roles': 'admin'}) self.assertEqual(200, response.status_int) def test_add_remove_dhcp_agent(self): headers = {'X-Project-Id': 'tenid', 'X-Roles': 'admin'} self.app.post_json( '/v2.0/agents/%s/dhcp-networks.json' % self.agent.id, headers=headers, params={'network_id': self.network['id']}) response = self.app.get( '/v2.0/networks/%s/dhcp-agents.json' % self.network['id'], headers=headers) self.assertIn(self.agent.id, [a['id'] for a in response.json['agents']]) self.app.delete('/v2.0/agents/%(a)s/dhcp-networks/%(n)s.json' % { 'a': self.agent.id, 'n': self.network['id']}, headers=headers) response = self.app.get( '/v2.0/networks/%s/dhcp-agents.json' % self.network['id'], headers=headers) self.assertNotIn(self.agent.id, [a['id'] for a in response.json['agents']]) class TestL3AgentShimControllers(test_functional.PecanFunctionalTest): def setUp(self): cfg.CONF.set_override( 'service_plugins', ['neutron.services.l3_router.l3_router_plugin.L3RouterPlugin', 'neutron.services.flavors.flavors_plugin.FlavorsPlugin']) super(TestL3AgentShimControllers, self).setUp() policy.init() policy._ENFORCER.set_rules( oslo_policy.Rules.from_dict( {'get_l3-agents': 'role:admin', 'get_l3-routers': 'role:admin'}), overwrite=False) ctx = context.get_admin_context() l3_plugin = directory.get_plugin(plugin_constants.L3) self.router = pecan_utils.create_router(ctx, l3_plugin) self.agent = helpers.register_l3_agent() # NOTE(blogan): Not sending notifications because this test is for # testing the shim controllers l3_plugin.agent_notifiers[n_const.AGENT_TYPE_L3] = None def test_list_l3_agents_hosting_router(self): response = self.app.get( '/v2.0/routers/%s/l3-agents.json' % self.router['id'], headers={'X-Roles': 'admin'}) self.assertEqual(200, response.status_int) def test_list_routers_on_l3_agent(self): response = self.app.get( '/v2.0/agents/%s/l3-routers.json' % self.agent.id, headers={'X-Roles': 'admin'}) self.assertEqual(200, response.status_int) def test_add_remove_l3_agent(self): headers = {'X-Project-Id': 'tenid', 'X-Roles': 'admin'} response = self.app.post_json( '/v2.0/agents/%s/l3-routers.json' % self.agent.id, headers=headers, params={'router_id': self.router['id']}) self.assertEqual(201, response.status_int) response = self.app.get( '/v2.0/routers/%s/l3-agents.json' % self.router['id'], headers=headers) self.assertIn(self.agent.id, [a['id'] for a in response.json['agents']]) response = self.app.delete( '/v2.0/agents/%(a)s/l3-routers/%(n)s.json' % { 'a': self.agent.id, 'n': self.router['id']}, headers=headers) self.assertEqual(204, response.status_int) self.assertFalse(response.body) response = self.app.get( '/v2.0/routers/%s/l3-agents.json' % self.router['id'], headers=headers) self.assertNotIn(self.agent.id, [a['id'] for a in response.json['agents']]) class TestShimControllers(test_functional.PecanFunctionalTest): def setUp(self): fake_ext = pecan_utils.FakeExtension() fake_plugin = pecan_utils.FakePlugin() plugins = {pecan_utils.FakePlugin.PLUGIN_TYPE: fake_plugin} new_extensions = {fake_ext.get_alias(): fake_ext} super(TestShimControllers, self).setUp( service_plugins=plugins, extensions=new_extensions) policy.init() policy._ENFORCER.set_rules( oslo_policy.Rules.from_dict( {'get_meh_meh': '', 'get_meh_mehs': '', 'get_fake_subresources': ''}), overwrite=False) self.addCleanup(policy.reset) def test_hyphenated_resource_controller_not_shimmed(self): collection = pecan_utils.FakeExtension.HYPHENATED_COLLECTION.replace( '_', '-') resource = pecan_utils.FakeExtension.HYPHENATED_RESOURCE url = '/v2.0/{}/something.json'.format(collection) resp = self.app.get(url) self.assertEqual(200, resp.status_int) self.assertEqual({resource: {'fake': 'something'}}, resp.json) def test_hyphenated_collection_controller_not_shimmed(self): body_collection = pecan_utils.FakeExtension.HYPHENATED_COLLECTION uri_collection = body_collection.replace('_', '-') url = '/v2.0/{}.json'.format(uri_collection) resp = self.app.get(url) self.assertEqual(200, resp.status_int) self.assertEqual({body_collection: [{'fake': 'fake'}]}, resp.json) def test_hyphenated_collection_subresource_controller_not_shimmed(self): body_collection = pecan_utils.FakeExtension.HYPHENATED_COLLECTION uri_collection = body_collection.replace('_', '-') # there is only one subresource so far sub_resource_collection = ( pecan_utils.FakeExtension.FAKE_SUB_RESOURCE_COLLECTION) temp_id = uuidutils.generate_uuid() url = '/v2.0/{0}/{1}/{2}'.format( uri_collection, temp_id, sub_resource_collection.replace('_', '-')) resp = self.app.get(url) self.assertEqual(200, resp.status_int) self.assertEqual({sub_resource_collection: {'foo': temp_id}}, resp.json) class TestMemberActionController(test_functional.PecanFunctionalTest): def setUp(self): fake_ext = pecan_utils.FakeExtension() fake_plugin = pecan_utils.FakePlugin() plugins = {pecan_utils.FakePlugin.PLUGIN_TYPE: fake_plugin} new_extensions = {fake_ext.get_alias(): fake_ext} super(TestMemberActionController, self).setUp( service_plugins=plugins, extensions=new_extensions) hyphen_collection = pecan_utils.FakeExtension.HYPHENATED_COLLECTION self.collection = hyphen_collection.replace('_', '-') def test_get_member_action_controller(self): url = '/v2.0/{}/something/boo_meh.json'.format(self.collection) resp = self.app.get(url) self.assertEqual(200, resp.status_int) self.assertEqual({'boo_yah': 'something'}, resp.json) def test_put_member_action_controller(self): url = '/v2.0/{}/something/put_meh.json'.format(self.collection) resp = self.app.put_json(url, params={'it_matters_not': 'ok'}) self.assertEqual(200, resp.status_int) self.assertEqual({'poo_yah': 'something'}, resp.json) def test_get_member_action_does_not_exist(self): url = '/v2.0/{}/something/are_you_still_there.json'.format( self.collection) resp = self.app.get(url, expect_errors=True) self.assertEqual(404, resp.status_int) def test_put_member_action_does_not_exist(self): url = '/v2.0/{}/something/are_you_still_there.json'.format( self.collection) resp = self.app.put_json(url, params={'it_matters_not': 'ok'}, expect_errors=True) self.assertEqual(404, resp.status_int) def test_put_on_get_member_action(self): url = '/v2.0/{}/something/boo_meh.json'.format(self.collection) resp = self.app.put_json(url, params={'it_matters_not': 'ok'}, expect_errors=True) self.assertEqual(405, resp.status_int) def test_get_on_put_member_action(self): url = '/v2.0/{}/something/put_meh.json'.format(self.collection) resp = self.app.get(url, expect_errors=True) self.assertEqual(405, resp.status_int) class TestParentSubresourceController(test_functional.PecanFunctionalTest): def setUp(self): fake_ext = pecan_utils.FakeExtension() fake_plugin = pecan_utils.FakePlugin() plugins = {pecan_utils.FakePlugin.PLUGIN_TYPE: fake_plugin} new_extensions = {fake_ext.get_alias(): fake_ext} super(TestParentSubresourceController, self).setUp( service_plugins=plugins, extensions=new_extensions) policy.init() policy._ENFORCER.set_rules( oslo_policy.Rules.from_dict( {'get_fake_duplicate': '', 'get_meh_meh_fake_duplicate': ''}), overwrite=False) self.addCleanup(policy.reset) hyphen_collection = pecan_utils.FakeExtension.HYPHENATED_COLLECTION self.collection = hyphen_collection.replace('_', '-') self.fake_collection = (pecan_utils.FakeExtension. FAKE_PARENT_SUBRESOURCE_COLLECTION) def test_get_duplicate_parent_resource(self): url = '/v2.0/{}'.format(self.fake_collection) resp = self.app.get(url) self.assertEqual(200, resp.status_int) self.assertEqual({'fake_duplicates': [{'fake': 'fakeduplicates'}]}, resp.json) def test_get_duplicate_parent_resource_item(self): url = '/v2.0/{}/something'.format(self.fake_collection) resp = self.app.get(url) self.assertEqual(200, resp.status_int) self.assertEqual({'fake_duplicate': {'fake': 'something'}}, resp.json) def test_get_parent_resource_and_duplicate_subresources(self): url = '/v2.0/{0}/something/{1}'.format(self.collection, self.fake_collection) resp = self.app.get(url) self.assertEqual(200, resp.status_int) self.assertEqual({'fake_duplicates': [{'fake': 'something'}]}, resp.json) def test_get_child_resource_policy_check(self): policy.reset() policy.init() policy._ENFORCER.set_rules( oslo_policy.Rules.from_dict( {'get_meh_meh_fake_duplicate': ''} ) ) url = '/v2.0/{0}/something/{1}'.format(self.collection, self.fake_collection) resp = self.app.get(url) self.assertEqual(200, resp.status_int) self.assertEqual({'fake_duplicates': [{'fake': 'something'}]}, resp.json) class TestExcludeAttributePolicy(test_functional.PecanFunctionalTest): def setUp(self): super(TestExcludeAttributePolicy, self).setUp() policy.init() self.addCleanup(policy.reset) plugin = directory.get_plugin() ctx = context.get_admin_context() self.network_id = pecan_utils.create_network(ctx, plugin)['id'] mock.patch('neutron.pecan_wsgi.controllers.resource.' 'CollectionsController.get').start() def test_get_networks(self): response = self.app.get('/v2.0/networks/%s.json' % self.network_id, headers={'X-Project-Id': 'tenid'}) json_body = jsonutils.loads(response.body) self.assertEqual(response.status_int, 200) self.assertEqual('tenid', json_body['network']['project_id']) self.assertEqual('tenid', json_body['network']['tenant_id']) neutron-12.1.1/neutron/tests/functional/pecan_wsgi/__init__.py0000664000175000017500000000217013553660047024547 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from pecan import set_config from pecan.testing import load_test_app import unittest2 __all__ = ['FunctionalTest'] class FunctionalTest(unittest2.TestCase): """ Used for functional tests where you need to test your literal application and its integration with the framework. """ def setUp(self): self.app = load_test_app(os.path.join( os.path.dirname(__file__), 'config.py' )) self.addCleanup(set_config, {}, overwrite=True) neutron-12.1.1/neutron/tests/functional/api/0000775000175000017500000000000013553660156021071 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/api/test_policies.py0000664000175000017500000000714613553660047024320 0ustar zuulzuul00000000000000# Copyright (c) 2014 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os.path from neutron_lib import context from neutron_lib import fixture from neutron.api import extensions from neutron.api.v2 import attributes from neutron import policy from neutron.tests import base TEST_PATH = os.path.dirname(os.path.abspath(__file__)) class APIPolicyTestCase(base.BaseTestCase): """ Tests for REST API policy checks. Ideally this would be done against an environment with an instantiated plugin, but there appears to be problems with instantiating a plugin against an sqlite environment and as yet, there is no precedent for running a functional test against an actual database backend. """ api_version = "2.0" def setUp(self): super(APIPolicyTestCase, self).setUp() self.useFixture(fixture.APIDefinitionFixture()) self.extension_path = os.path.abspath(os.path.join( TEST_PATH, "../../../extensions")) self.addCleanup(policy.reset) def _network_definition(self): return {'name': 'test_network', 'ports': [], 'subnets': [], 'status': 'up', 'admin_state_up': True, 'shared': False, 'tenant_id': 'admin', 'id': 'test_network', 'router:external': True} def _check_external_router_policy(self, context): return policy.check(context, 'get_network', self._network_definition()) def test_premature_loading(self): """ Verifies that loading policies by way of admin context before populating extensions and extending the resource map results in networks with router:external is true being invisible to regular tenants. """ extension_manager = extensions.ExtensionManager(self.extension_path) admin_context = context.get_admin_context() tenant_context = context.Context('test_user', 'test_tenant_id', False) extension_manager.extend_resources(self.api_version, attributes.RESOURCE_ATTRIBUTE_MAP) self.assertTrue(self._check_external_router_policy(admin_context)) self.assertFalse(self._check_external_router_policy(tenant_context)) def test_proper_load_order(self): """ Verifies that loading policies by way of admin context after populating extensions and extending the resource map results in networks with router:external are visible to regular tenants. """ policy.reset() extension_manager = extensions.ExtensionManager(self.extension_path) extension_manager.extend_resources(self.api_version, attributes.RESOURCE_ATTRIBUTE_MAP) policy.init() admin_context = context.get_admin_context() tenant_context = context.Context('test_user', 'test_tenant_id', False) self.assertTrue(self._check_external_router_policy(admin_context)) self.assertTrue(self._check_external_router_policy(tenant_context)) neutron-12.1.1/neutron/tests/functional/api/__init__.py0000664000175000017500000000000013553660046023166 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/tests/0000775000175000017500000000000013553660156021462 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/tests/__init__.py0000664000175000017500000000000013553660046023557 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/tests/common/0000775000175000017500000000000013553660156022752 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/tests/common/exclusive_resources/0000775000175000017500000000000013553660156027053 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/tests/common/exclusive_resources/test_resource_allocator.py0000664000175000017500000000425413553660046034356 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from neutron_lib.utils import helpers import testtools from neutron.tests.common.exclusive_resources import resource_allocator from neutron.tests.functional import base def safe_remove_file(file_path): try: os.remove(file_path) except OSError: pass class TestResourceAllocator(base.BaseLoggingTestCase): def setUp(self): super(TestResourceAllocator, self).setUp() self.ra = resource_allocator.ResourceAllocator( helpers.get_random_string(6), lambda: 42) self.addCleanup(safe_remove_file, self.ra._state_file_path) def test_allocate_and_release(self): # Assert that we can allocate a resource resource = self.ra.allocate() self.assertEqual('42', resource) # Assert that we cannot allocate any more resources, since we're # using an allocator that always returns the same value with testtools.ExpectedException(ValueError): self.ra.allocate() # Assert that releasing the resource and allocating again works self.ra.release(resource) resource = self.ra.allocate() self.assertEqual('42', resource) def test_file_manipulation(self): # The file should not be created until the first allocation self.assertFalse(os.path.exists(self.ra._state_file_path)) resource = self.ra.allocate() self.assertTrue(os.path.exists(self.ra._state_file_path)) # Releasing the last resource should delete the file self.ra.release(resource) self.assertFalse(os.path.exists(self.ra._state_file_path)) neutron-12.1.1/neutron/tests/functional/tests/common/exclusive_resources/test_ip_network.py0000664000175000017500000000236213553660046032646 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron.tests.common.exclusive_resources import ip_network from neutron.tests.functional import base class TestExclusiveIPNetwork(base.BaseLoggingTestCase): def test_ip_network(self): network_1 = self.useFixture( ip_network.ExclusiveIPNetwork( '240.0.0.1', '240.255.255.254', '24')).network network_2 = self.useFixture( ip_network.ExclusiveIPNetwork( '240.0.0.1', '240.255.255.254', '24')).network self.assertIsInstance(network_1, netaddr.IPNetwork) self.assertEqual(network_1.cidr, network_1) self.assertNotEqual(network_1, network_2) neutron-12.1.1/neutron/tests/functional/tests/common/exclusive_resources/test_ip_address.py0000664000175000017500000000220013553660046032571 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron.tests.common.exclusive_resources import ip_address from neutron.tests.functional import base class TestExclusiveIPAddress(base.BaseLoggingTestCase): def test_ip_address(self): address_1 = self.useFixture( ip_address.ExclusiveIPAddress('10.0.0.1', '10.0.0.2')).address address_2 = self.useFixture( ip_address.ExclusiveIPAddress('10.0.0.1', '10.0.0.2')).address self.assertIsInstance(address_1, netaddr.IPAddress) self.assertNotEqual(address_1, address_2) neutron-12.1.1/neutron/tests/functional/tests/common/exclusive_resources/__init__.py0000664000175000017500000000000013553660046031150 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/tests/common/exclusive_resources/test_port.py0000664000175000017500000000211213553660046031442 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron.tests.common.exclusive_resources import port from neutron.tests.functional import base class TestExclusivePort(base.BaseSudoTestCase): def test_port(self): port_1 = self.useFixture(port.ExclusivePort( constants.PROTO_NAME_TCP)).port port_2 = self.useFixture(port.ExclusivePort( constants.PROTO_NAME_TCP)).port self.assertIsInstance(port_1, str) self.assertNotEqual(port_1, port_2) neutron-12.1.1/neutron/tests/functional/tests/common/__init__.py0000664000175000017500000000000013553660046025047 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/db/0000775000175000017500000000000013553660156020705 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/db/test_migrations.py0000664000175000017500000006227413553660047024504 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from contextlib import contextmanager import subprocess from alembic.ddl import base as alembic_ddl from alembic import script as alembic_script from oslo_config import cfg from oslo_config import fixture as config_fixture from oslo_db.sqlalchemy import test_migrations from oslotest import base as oslotest_base import six import sqlalchemy from sqlalchemy import event # noqa from sqlalchemy.sql import ddl as sqla_ddl from neutron.db import migration as migration_root from neutron.db.migration.alembic_migrations import external from neutron.db.migration import cli as migration from neutron.db.migration.models import head as head_models from neutron.tests import base as test_base from neutron.tests.unit import testlib_api cfg.CONF.import_opt('core_plugin', 'neutron.conf.common') CREATION_OPERATIONS = { 'sqla': (sqla_ddl.CreateIndex, sqla_ddl.CreateTable, sqla_ddl.CreateColumn, ), 'alembic': (alembic_ddl.AddColumn, ) } DROP_OPERATIONS = { 'sqla': (sqla_ddl.DropConstraint, sqla_ddl.DropIndex, sqla_ddl.DropTable, ), 'alembic': (alembic_ddl.DropColumn, ) } def upgrade(engine, alembic_config, branch_name='heads'): cfg.CONF.set_override('connection', engine.url, group='database') migration.do_alembic_command(alembic_config, 'upgrade', branch_name) class _TestModelsMigrations(test_migrations.ModelsMigrationsSync): '''Test for checking of equality models state and migrations. For the opportunistic testing you need to set up a db named 'openstack_citest' with user 'openstack_citest' and password 'openstack_citest' on localhost. The test will then use that db and user/password combo to run the tests. For PostgreSQL on Ubuntu this can be done with the following commands:: sudo -u postgres psql postgres=# create user openstack_citest with createdb login password 'openstack_citest'; postgres=# create database openstack_citest with owner openstack_citest; For MySQL on Ubuntu this can be done with the following commands:: mysql -u root >create database openstack_citest; >grant all privileges on openstack_citest.* to openstack_citest@localhost identified by 'openstack_citest'; Output is a list that contains information about differences between db and models. Output example:: [('add_table', Table('bat', MetaData(bind=None), Column('info', String(), table=), schema=None)), ('remove_table', Table(u'bar', MetaData(bind=None), Column(u'data', VARCHAR(), table=), schema=None)), ('add_column', None, 'foo', Column('data', Integer(), table=)), ('remove_column', None, 'foo', Column(u'old_data', VARCHAR(), table=None)), [('modify_nullable', None, 'foo', u'x', {'existing_server_default': None, 'existing_type': INTEGER()}, True, False)]] * ``remove_*`` means that there is extra table/column/constraint in db; * ``add_*`` means that it is missing in db; * ``modify_*`` means that on column in db is set wrong type/nullable/server_default. Element contains information: - what should be modified, - schema, - table, - column, - existing correct column parameters, - right value, - wrong value. This class also contains tests for branches, like that correct operations are used in contract and expand branches. ''' BUILD_SCHEMA = False TIMEOUT_SCALING_FACTOR = 4 def setUp(self): super(_TestModelsMigrations, self).setUp() self.cfg = self.useFixture(config_fixture.Config()) self.cfg.config(core_plugin='ml2') self.alembic_config = migration.get_neutron_config() self.alembic_config.neutron_config = cfg.CONF # Migration tests can take a long time self.useFixture(test_base.Timeout(scaling=self.TIMEOUT_SCALING_FACTOR)) def db_sync(self, engine): upgrade(engine, self.alembic_config) def get_engine(self): return self.engine def get_metadata(self): return head_models.get_metadata() def include_object(self, object_, name, type_, reflected, compare_to): if type_ == 'table' and (name == 'alembic_version' or name in external.TABLES): return False return super(_TestModelsMigrations, self).include_object( object_, name, type_, reflected, compare_to) def filter_metadata_diff(self, diff): return list(filter(self.remove_unrelated_errors, diff)) # Remove some difference that are not mistakes just specific of # dialects, etc def remove_unrelated_errors(self, element): insp = sqlalchemy.engine.reflection.Inspector.from_engine( self.get_engine()) dialect = self.get_engine().dialect.name if isinstance(element, tuple): if dialect == 'mysql' and element[0] == 'remove_index': table_name = element[1].table.name for fk in insp.get_foreign_keys(table_name): if fk['name'] == element[1].name: return False cols = [c.name for c in element[1].expressions] for col in cols: if col in insp.get_pk_constraint( table_name)['constrained_columns']: return False else: for modified, _, table, column, _, _, new in element: if modified == 'modify_default' and dialect == 'mysql': constrained = insp.get_pk_constraint(table) if column in constrained['constrained_columns']: return False return True def test_upgrade_expand_branch(self): # Verify that "command neutron-db-manage upgrade --expand" works # without errors. Check this for both MySQL and PostgreSQL. upgrade(self.engine, self.alembic_config, branch_name='%s@head' % migration.EXPAND_BRANCH) def test_upgrade_contract_branch(self): # Verify that "command neutron-db-manage upgrade --contract" works # without errors. Check this for both MySQL and PostgreSQL. upgrade(self.engine, self.alembic_config, branch_name='%s@head' % migration.CONTRACT_BRANCH) @contextmanager def _listener(self, engine, listener_func): try: event.listen(engine, 'before_execute', listener_func) yield finally: event.remove(engine, 'before_execute', listener_func) def test_branches(self): drop_exceptions = collections.defaultdict(list) creation_exceptions = collections.defaultdict(list) def find_migration_exceptions(): # Due to some misunderstandings and some conscious decisions, # there may be some expand migrations which drop elements and # some contract migrations which create elements. These excepted # elements must be returned by a method in the script itself. # The names of the method must be 'contract_creation_exceptions' # or 'expand_drop_exceptions'. The methods must have a docstring # explaining the reason for the exception. # # Here we build lists of the excepted elements and verify that # they are documented. script = alembic_script.ScriptDirectory.from_config( self.alembic_config) for m in list(script.walk_revisions(base='base', head='heads')): branches = m.branch_labels or [] if migration.CONTRACT_BRANCH in branches: method_name = 'contract_creation_exceptions' exceptions_dict = creation_exceptions elif migration.EXPAND_BRANCH in branches: method_name = 'expand_drop_exceptions' exceptions_dict = drop_exceptions else: continue get_excepted_elements = getattr(m.module, method_name, None) if not get_excepted_elements: continue explanation = getattr(get_excepted_elements, '__doc__', "") if len(explanation) < 1: self.fail("%s() requires docstring with explanation" % '.'.join([m.module.__name__, get_excepted_elements.__name__])) for sa_type, elements in get_excepted_elements().items(): exceptions_dict[sa_type].extend(elements) def is_excepted_sqla(clauseelement, exceptions): """Identify excepted operations that are allowed for the branch.""" element = clauseelement.element element_name = element.name if isinstance(element, sqlalchemy.Index): element_name = element.table.name for sa_type_, excepted_names in exceptions.items(): if isinstance(element, sa_type_): if element_name in excepted_names: return True def is_excepted_alembic(clauseelement, exceptions): """Identify excepted operations that are allowed for the branch.""" # For alembic the clause is AddColumn or DropColumn column = clauseelement.column.name table = clauseelement.column.table.name element_name = '.'.join([table, column]) for alembic_type, excepted_names in exceptions.items(): if alembic_type == sqlalchemy.Column: if element_name in excepted_names: return True def is_allowed(clauseelement, exceptions, disallowed_ops): if (isinstance(clauseelement, disallowed_ops['sqla']) and hasattr(clauseelement, 'element')): return is_excepted_sqla(clauseelement, exceptions) if isinstance(clauseelement, disallowed_ops['alembic']): return is_excepted_alembic(clauseelement, exceptions) return True def check_expand_branch(conn, clauseelement, multiparams, params): if not is_allowed(clauseelement, drop_exceptions, DROP_OPERATIONS): self.fail("Migration in expand branch contains drop command") def check_contract_branch(conn, clauseelement, multiparams, params): if not is_allowed(clauseelement, creation_exceptions, CREATION_OPERATIONS): self.fail("Migration in contract branch contains create " "command") find_migration_exceptions() engine = self.engine cfg.CONF.set_override('connection', engine.url, group='database') with engine.begin() as connection: self.alembic_config.attributes['connection'] = connection # upgrade to latest release first; --expand users are expected to # apply all alembic scripts from previous releases before applying # the new ones for release in migration_root.NEUTRON_MILESTONES: release_revisions = migration._find_milestone_revisions( self.alembic_config, release) for rev in release_revisions: migration.do_alembic_command( self.alembic_config, 'upgrade', rev[0]) with self._listener(engine, check_expand_branch): migration.do_alembic_command( self.alembic_config, 'upgrade', '%s@head' % migration.EXPAND_BRANCH) with self._listener(engine, check_contract_branch): migration.do_alembic_command( self.alembic_config, 'upgrade', '%s@head' % migration.CONTRACT_BRANCH) def _test_has_offline_migrations(self, revision, expected): engine = self.get_engine() cfg.CONF.set_override('connection', engine.url, group='database') migration.do_alembic_command(self.alembic_config, 'upgrade', revision) self.assertEqual(expected, migration.has_offline_migrations(self.alembic_config, 'unused')) def test_has_offline_migrations_pending_contract_scripts(self): self._test_has_offline_migrations('kilo', True) def test_has_offline_migrations_all_heads_upgraded(self): self._test_has_offline_migrations('heads', False) # NOTE(ihrachys): if this test fails for you, it probably means that you # attempt to add an unsafe contract migration script, that is in # contradiction to blueprint online-upgrades # TODO(ihrachys): revisit later in Pike+ where some contract scripts may be # safe again def test_forbid_offline_migrations_starting_newton(self): engine = self.get_engine() cfg.CONF.set_override('connection', engine.url, group='database') # the following revisions are Newton heads for revision in ('5cd92597d11d', '5c85685d616d'): migration.do_alembic_command( self.alembic_config, 'upgrade', revision) self.assertFalse(migration.has_offline_migrations( self.alembic_config, 'unused'), msg='Offline contract migration scripts are forbidden for Ocata+') class TestModelsMigrationsMysql(testlib_api.MySQLTestCaseMixin, _TestModelsMigrations, testlib_api.SqlTestCaseLight): @test_base.skip_if_timeout("bug 1687027") def test_check_mysql_engine(self): engine = self.get_engine() cfg.CONF.set_override('connection', engine.url, group='database') with engine.begin() as connection: self.alembic_config.attributes['connection'] = connection migration.do_alembic_command(self.alembic_config, 'upgrade', 'heads') insp = sqlalchemy.engine.reflection.Inspector.from_engine(engine) # Test that table creation on MySQL only builds InnoDB tables tables = insp.get_table_names() self.assertGreater(len(tables), 0, "No tables found. Wrong schema?") res = [table for table in tables if insp.get_table_options(table)['mysql_engine'] != 'InnoDB' and table != 'alembic_version'] self.assertEqual(0, len(res), "%s non InnoDB tables created" % res) @test_base.skip_if_timeout("bug 1687027") def test_upgrade_expand_branch(self): super(TestModelsMigrationsMysql, self).test_upgrade_expand_branch() @test_base.skip_if_timeout("bug 1687027") def test_upgrade_contract_branch(self): super(TestModelsMigrationsMysql, self).test_upgrade_contract_branch() @test_base.skip_if_timeout("bug 1687027") def test_branches(self): super(TestModelsMigrationsMysql, self).test_branches() @test_base.skip_if_timeout("bug 1687027") def test_has_offline_migrations_pending_contract_scripts(self): super(TestModelsMigrationsMysql, self).test_has_offline_migrations_pending_contract_scripts() @test_base.skip_if_timeout("bug 1687027") def test_has_offline_migrations_all_heads_upgraded(self): super(TestModelsMigrationsMysql, self).test_has_offline_migrations_all_heads_upgraded() @test_base.skip_if_timeout("bug 1687027") def test_models_sync(self): super(TestModelsMigrationsMysql, self).test_models_sync() class TestModelsMigrationsPsql(testlib_api.PostgreSQLTestCaseMixin, _TestModelsMigrations, testlib_api.SqlTestCaseLight): pass class TestSanityCheck(testlib_api.SqlTestCaseLight): BUILD_SCHEMA = False def setUp(self): super(TestSanityCheck, self).setUp() self.alembic_config = migration.get_neutron_config() self.alembic_config.neutron_config = cfg.CONF def _drop_table(self, table): with self.engine.begin() as conn: table.drop(conn) def test_check_sanity_1df244e556f5(self): ha_router_agent_port_bindings = sqlalchemy.Table( 'ha_router_agent_port_bindings', sqlalchemy.MetaData(), sqlalchemy.Column('port_id', sqlalchemy.String(36)), sqlalchemy.Column('router_id', sqlalchemy.String(36)), sqlalchemy.Column('l3_agent_id', sqlalchemy.String(36))) with self.engine.connect() as conn: ha_router_agent_port_bindings.create(conn) self.addCleanup(self._drop_table, ha_router_agent_port_bindings) conn.execute(ha_router_agent_port_bindings.insert(), [ {'port_id': '1234', 'router_id': '12345', 'l3_agent_id': '123'}, {'port_id': '12343', 'router_id': '12345', 'l3_agent_id': '123'} ]) script_dir = alembic_script.ScriptDirectory.from_config( self.alembic_config) script = script_dir.get_revision("1df244e556f5").module self.assertRaises(script.DuplicateL3HARouterAgentPortBinding, script.check_sanity, conn) def test_check_sanity_030a959ceafa(self): routerports = sqlalchemy.Table( 'routerports', sqlalchemy.MetaData(), sqlalchemy.Column('router_id', sqlalchemy.String(36)), sqlalchemy.Column('port_id', sqlalchemy.String(36)), sqlalchemy.Column('port_type', sqlalchemy.String(255))) with self.engine.connect() as conn: routerports.create(conn) self.addCleanup(self._drop_table, routerports) conn.execute(routerports.insert(), [ {'router_id': '1234', 'port_id': '12345', 'port_type': '123'}, {'router_id': '12343', 'port_id': '12345', 'port_type': '1232'} ]) script_dir = alembic_script.ScriptDirectory.from_config( self.alembic_config) script = script_dir.get_revision("030a959ceafa").module self.assertRaises(script.DuplicatePortRecordinRouterPortdatabase, script.check_sanity, conn) def test_check_sanity_6b461a21bcfc_dup_on_fixed_ip(self): floatingips = sqlalchemy.Table( 'floatingips', sqlalchemy.MetaData(), sqlalchemy.Column('floating_network_id', sqlalchemy.String(36)), sqlalchemy.Column('fixed_port_id', sqlalchemy.String(36)), sqlalchemy.Column('fixed_ip_address', sqlalchemy.String(64))) with self.engine.connect() as conn: floatingips.create(conn) self.addCleanup(self._drop_table, floatingips) conn.execute(floatingips.insert(), [ {'floating_network_id': '12345', 'fixed_port_id': '1234567', 'fixed_ip_address': '12345678'}, {'floating_network_id': '12345', 'fixed_port_id': '1234567', 'fixed_ip_address': '12345678'} ]) script_dir = alembic_script.ScriptDirectory.from_config( self.alembic_config) script = script_dir.get_revision("6b461a21bcfc").module self.assertRaises(script.DuplicateFloatingIPforOneFixedIP, script.check_sanity, conn) def test_check_sanity_6b461a21bcfc_dup_on_no_fixed_ip(self): floatingips = sqlalchemy.Table( 'floatingips', sqlalchemy.MetaData(), sqlalchemy.Column('floating_network_id', sqlalchemy.String(36)), sqlalchemy.Column('fixed_port_id', sqlalchemy.String(36)), sqlalchemy.Column('fixed_ip_address', sqlalchemy.String(64))) with self.engine.connect() as conn: floatingips.create(conn) self.addCleanup(self._drop_table, floatingips) conn.execute(floatingips.insert(), [ {'floating_network_id': '12345', 'fixed_port_id': '1234567', 'fixed_ip_address': None}, {'floating_network_id': '12345', 'fixed_port_id': '1234567', 'fixed_ip_address': None} ]) script_dir = alembic_script.ScriptDirectory.from_config( self.alembic_config) script = script_dir.get_revision("6b461a21bcfc").module self.assertIsNone(script.check_sanity(conn)) class TestWalkDowngrade(oslotest_base.BaseTestCase): def setUp(self): super(TestWalkDowngrade, self).setUp() self.alembic_config = migration.get_neutron_config() self.alembic_config.neutron_config = cfg.CONF def test_no_downgrade(self): script_dir = alembic_script.ScriptDirectory.from_config( self.alembic_config) versions = [v for v in script_dir.walk_revisions(base='base', head='heads')] failed_revisions = [] for version in versions: if hasattr(version.module, 'downgrade'): failed_revisions.append(version.revision) if failed_revisions: self.fail('Migrations %s have downgrade' % failed_revisions) return True class _TestWalkMigrations(object): '''This will add framework for testing schema migration for different backends. ''' BUILD_SCHEMA = False def execute_cmd(self, cmd=None): proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) output = proc.communicate()[0] self.assertEqual(0, proc.returncode, 'Command failed with ' 'output:\n%s' % output) def _get_alembic_config(self, uri): db_config = migration.get_neutron_config() self.script_dir = alembic_script.ScriptDirectory.from_config(db_config) db_config.neutron_config = cfg.CONF db_config.neutron_config.set_override('connection', six.text_type(uri), group='database') return db_config def _revisions(self): """Provides revisions and its parent revisions. :return: List of tuples. Every tuple contains revision and its parent revision. """ revisions = list(self.script_dir.walk_revisions("base", "heads")) revisions = list(reversed(revisions)) for rev in revisions: # Destination, current yield rev.revision, rev.down_revision def _migrate_up(self, config, engine, dest, curr, with_data=False): if with_data: data = None pre_upgrade = getattr( self, "_pre_upgrade_%s" % dest, None) if pre_upgrade: data = pre_upgrade(engine) migration.do_alembic_command(config, 'upgrade', dest) if with_data: check = getattr(self, "_check_%s" % dest, None) if check and data: check(engine, data) def test_walk_versions(self): """Test migrations ability to upgrade and downgrade. """ engine = self.engine config = self._get_alembic_config(engine.url) revisions = self._revisions() for dest, curr in revisions: self._migrate_up(config, engine, dest, curr, with_data=True) class TestWalkMigrationsMysql(testlib_api.MySQLTestCaseMixin, _TestWalkMigrations, testlib_api.SqlTestCaseLight): # NOTE(slaweq): this workaround is taken from Manila patch: # https://review.openstack.org/#/c/291397/ # Set 5 minutes timeout for case of running it on # very slow nodes/VMs. Note, that this test becomes slower with each # addition of new DB migration. On fast nodes it can take about 5-10 # secs having Mitaka set of migrations. 'pymysql' works much slower # on slow nodes than 'psycopg2' and because of that this increased # timeout is required only when for testing with 'mysql' backend. @test_base.set_timeout(600) @test_base.skip_if_timeout("bug 1687027") def test_walk_versions(self): super(TestWalkMigrationsMysql, self).test_walk_versions() class TestWalkMigrationsPsql(testlib_api.PostgreSQLTestCaseMixin, _TestWalkMigrations, testlib_api.SqlTestCaseLight): pass neutron-12.1.1/neutron/tests/functional/db/test_ipam.py0000664000175000017500000001420713553660047023247 0ustar zuulzuul00000000000000# Copyright 2015 SUSE Linux Products GmbH # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron_lib import constants from neutron_lib import context from neutron_lib import exceptions as n_exc from oslo_config import cfg from oslo_utils import uuidutils import testtools from neutron.db import db_base_plugin_v2 as base_plugin from neutron.objects import ports as port_obj from neutron.objects import subnet as subnet_obj from neutron.tests.unit import testlib_api # required in order for testresources to optimize same-backend # tests together load_tests = testlib_api.module_load_tests # FIXME(zzzeek): needs to be provided by oslo.db, current version # is not working # load_tests = test_base.optimize_db_test_loader(__file__) class IpamTestCase(testlib_api.SqlTestCase): """ Base class for tests that aim to test ip allocation. """ def setUp(self): super(IpamTestCase, self).setUp() cfg.CONF.set_override('notify_nova_on_port_status_changes', False) DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' self.setup_coreplugin(DB_PLUGIN_KLASS) self.plugin = base_plugin.NeutronDbPluginV2() self.cxt = context.Context(user_id=None, tenant_id=None, is_admin=True, overwrite=False) self.tenant_id = uuidutils.generate_uuid() self.network_id = uuidutils.generate_uuid() self.subnet_id = uuidutils.generate_uuid() self.port_id = uuidutils.generate_uuid() self._create_network() self._create_subnet() def result_set_to_dicts(self, resultset, keys): dicts = [] for item in resultset: item_dict = dict((x, item[x]) for x in keys) dicts.append(item_dict) return dicts def assert_ip_alloc_matches(self, expected): result_set = port_obj.IPAllocation.get_objects(self.cxt) keys = ['port_id', 'ip_address', 'subnet_id', 'network_id'] actual = self.result_set_to_dicts(result_set, keys) self.assertEqual(expected, actual) def assert_ip_alloc_pool_matches(self, expected): result_set = subnet_obj.IPAllocationPool.get_objects(self.cxt) keys = ['start', 'end', 'subnet_id'] actual = self.result_set_to_dicts(result_set, keys) self.assertEqual(expected, actual) def _create_network(self): network = {'tenant_id': self.tenant_id, 'id': self.network_id, 'name': 'test-net', 'admin_state_up': True, 'shared': False, 'status': constants.NET_STATUS_ACTIVE} return self.plugin.create_network(self.cxt, {'network': network}) def _create_subnet(self): subnet = {'tenant_id': self.tenant_id, 'id': self.subnet_id, 'name': 'test_sub', 'network_id': self.network_id, 'ip_version': 4, 'cidr': '10.10.10.0/29', 'enable_dhcp': False, 'gateway_ip': '10.10.10.1', 'shared': False, 'allocation_pools': constants.ATTR_NOT_SPECIFIED, 'dns_nameservers': constants.ATTR_NOT_SPECIFIED, 'host_routes': constants.ATTR_NOT_SPECIFIED} return self.plugin.create_subnet(self.cxt, {'subnet': subnet}) def _create_port(self, port_id, fixed_ips=None): port_fixed_ips = (fixed_ips if fixed_ips else constants.ATTR_NOT_SPECIFIED) port = {'tenant_id': self.tenant_id, 'name': 'test_port', 'id': port_id, 'network_id': self.network_id, 'mac_address': constants.ATTR_NOT_SPECIFIED, 'admin_state_up': True, 'status': constants.PORT_STATUS_ACTIVE, 'device_id': 'test_dev_id', 'device_owner': constants.DEVICE_OWNER_COMPUTE_PREFIX, 'fixed_ips': port_fixed_ips} self.plugin.create_port(self.cxt, {'port': port}) def test_allocate_fixed_ip(self): fixed_ip = [{'ip_address': "10.10.10.3", 'subnet_id': self.subnet_id}] self._create_port(self.port_id, fixed_ip) ip_alloc_expected = [{'port_id': self.port_id, 'ip_address': netaddr.IPAddress( fixed_ip[0].get('ip_address')), 'subnet_id': self.subnet_id, 'network_id': self.network_id}] ip_alloc_pool_expected = [{'start': netaddr.IPAddress('10.10.10.2'), 'end': netaddr.IPAddress('10.10.10.6'), 'subnet_id': self.subnet_id}] self.assert_ip_alloc_matches(ip_alloc_expected) self.assert_ip_alloc_pool_matches(ip_alloc_pool_expected) def test_allocate_ip_exausted_pool(self): # available from .2 up to .6 -> 5 for i in range(1, 6): self._create_port(uuidutils.generate_uuid()) ip_alloc_pool_expected = [{'start': netaddr.IPAddress('10.10.10.2'), 'end': netaddr.IPAddress('10.10.10.6'), 'subnet_id': self.subnet_id}] self.assert_ip_alloc_pool_matches(ip_alloc_pool_expected) with testtools.ExpectedException(n_exc.IpAddressGenerationFailure): self._create_port(self.port_id) class TestIpamMySql(testlib_api.MySQLTestCaseMixin, IpamTestCase): pass class TestIpamPsql(testlib_api.PostgreSQLTestCaseMixin, IpamTestCase): pass neutron-12.1.1/neutron/tests/functional/db/migrations/0000775000175000017500000000000013553660156023061 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/db/migrations/test_97c25b0d2353_add_name_desc.py0000664000175000017500000000571313553660046030760 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_db.sqlalchemy import utils as db_utils from oslo_utils import uuidutils from neutron.tests.functional.db import test_migrations def _create_record_with_sa(engine, resource_type, attributes): """Create a record with standard attributes.""" sa_table = db_utils.get_table(engine, 'standardattributes') sa_record = engine.execute(sa_table.insert().values( {'resource_type': resource_type})) attributes['standard_attr_id'] = sa_record.inserted_primary_key[0] resource_table = db_utils.get_table(engine, resource_type) engine.execute(resource_table.insert().values(attributes)) class NetworkSegmentNameAndDescriptionMixin(object): """Validates migration that adds name and description .""" def _pre_upgrade_97c25b0d2353(self, engine): # Create a network for segments to belong to net_id = uuidutils.generate_uuid() _create_record_with_sa(engine, 'networks', { 'id': net_id, 'name': '97c25b0d2353'}) # Create some segments with old model ns_table = db_utils.get_table(engine, 'networksegments') for s in range(5): engine.execute(ns_table.insert().values({ 'id': uuidutils.generate_uuid(), 'network_id': net_id, 'network_type': 'flat'})) return True # Return True so check function is invoked after migrate def _check_97c25b0d2353(self, engine, data): ns_table = db_utils.get_table(engine, 'networksegments') sa_table = db_utils.get_table(engine, 'standardattributes') for segment in engine.execute(ns_table.select()).fetchall(): # Ensure a stdattr record was created for this old segment standard_id = segment.standard_attr_id rows = engine.execute(sa_table.select().where( sa_table.c.id == standard_id)).fetchall() self.assertEqual(1, len(rows)) # Ensure this old segment can now be named engine.execute(ns_table.update().values(name='Zeus').where( ns_table.c.standard_attr_id == standard_id)) class TestNetworkSegmentNameDescMySql(NetworkSegmentNameAndDescriptionMixin, test_migrations.TestWalkMigrationsMysql): pass class TestNetworkSegmentNameDescPsql(NetworkSegmentNameAndDescriptionMixin, test_migrations.TestWalkMigrationsPsql): pass neutron-12.1.1/neutron/tests/functional/db/migrations/__init__.py0000664000175000017500000000000013553660046025156 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000neutron-12.1.1/neutron/tests/functional/db/migrations/test_b12a3ef66e62_add_standardattr_to_qos_policies.pyneutron-12.1.1/neutron/tests/functional/db/migrations/test_b12a3ef66e62_add_standardattr_to_qos_poli0000664000175000017500000000445213553660046033654 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from oslo_db.sqlalchemy import utils as db_utils from oslo_utils import uuidutils from neutron.tests.functional.db import test_migrations class QosStandardAttrMixin(object): """Validates qos standard attr migration.""" def _create_qos_pol(self, pol_id, description): otable = db_utils.get_table(self.engine, 'qos_policies') values = {'id': pol_id, 'description': description} self.engine.execute(otable.insert().values(values)) def _create_policies_with_descriptions(self, engine): for i in range(10): pol_id = uuidutils.generate_uuid() self._create_qos_pol(pol_id, 'description-%s' % pol_id) def _pre_upgrade_b12a3ef66e62(self, engine): self._create_policies_with_descriptions(engine) return True # return True so check function is invoked after migrate def _check_b12a3ef66e62(self, engine, data): qp = db_utils.get_table(engine, 'qos_policies') sa = db_utils.get_table(engine, 'standardattributes') for qos_pol in engine.execute(qp.select()).fetchall(): # ensure standard attributes model was created standard_id = qos_pol.standard_attr_id rows = engine.execute( sa.select().where(sa.c.id == standard_id)).fetchall() self.assertEqual(1, len(rows)) # ensure description got moved over self.assertEqual('description-%s' % qos_pol.id, rows[0].description) class TestQosStandardAttrMysql(QosStandardAttrMixin, test_migrations.TestWalkMigrationsMysql): pass class TestQosStandardAttrPsql(QosStandardAttrMixin, test_migrations.TestWalkMigrationsPsql): pass ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/tests/functional/db/migrations/test_a8b517cff8ab_add_routerport_bindings_for_ha.pyneutron-12.1.1/neutron/tests/functional/db/migrations/test_a8b517cff8ab_add_routerport_bindings_for_0000664000175000017500000001136713553660046034031 0ustar zuulzuul00000000000000# Copyright 2016 Business Cat is Very Serious # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from neutron_lib import constants from oslo_db.sqlalchemy import utils as db_utils from oslo_utils import uuidutils from neutron.tests.functional.db import test_migrations class HARouterPortMigrationMixin(object): """Validates HA port to router port migration.""" def _create_so(self, o_type, values): """create standard attr object.""" stan = db_utils.get_table(self.engine, 'standardattributes') # find next available id taking into account existing records rec_ids = [r.id for r in self.engine.execute(stan.select()).fetchall()] next_id = max([0] + rec_ids) + 1 self.engine.execute(stan.insert().values({'id': next_id, 'resource_type': o_type})) values['standard_attr_id'] = next_id return self._create_rec(o_type, values) def _create_rec(self, o_type, values): otable = db_utils.get_table(self.engine, o_type) self.engine.execute(otable.insert().values(values)) def _make_router_agents_and_ports(self, router_id, network_id, add_binding): self._create_so('routers', {'id': router_id}) # each router gets a couple of agents for _ in range(2): port_id = uuidutils.generate_uuid() self._create_so('ports', {'id': port_id, 'network_id': network_id, 'mac_address': port_id[0:31], 'admin_state_up': True, 'device_id': router_id, 'device_owner': 'network', 'status': 'ACTIVE'}) agent_id = uuidutils.generate_uuid() timestamp = '2000-04-06T14:34:23' self._create_rec('agents', {'id': agent_id, 'topic': 'x', 'agent_type': 'L3', 'binary': 'x', 'host': agent_id, 'created_at': timestamp, 'started_at': timestamp, 'heartbeat_timestamp': timestamp, 'configurations': ''}) self._create_rec('ha_router_agent_port_bindings', {'port_id': port_id, 'router_id': router_id, 'l3_agent_id': agent_id}) if add_binding: ptype = constants.DEVICE_OWNER_ROUTER_HA_INTF self._create_rec('routerports', {'router_id': router_id, 'port_id': port_id, 'port_type': ptype}) def _create_ha_routers_with_ports(self, engine): network_id = uuidutils.generate_uuid() self._create_so('networks', {'id': network_id}) unpatched_router_ids = [uuidutils.generate_uuid() for i in range(10)] for rid in unpatched_router_ids: self._make_router_agents_and_ports(rid, network_id, False) # make half of the routers already have routerport bindings to simulate # a back-port of Ifd3e007aaf2a2ed8123275aa3a9f540838e3c003 patched_router_ids = [uuidutils.generate_uuid() for i in range(10)] for rid in patched_router_ids: self._make_router_agents_and_ports(rid, network_id, True) def _pre_upgrade_a8b517cff8ab(self, engine): self._create_ha_routers_with_ports(engine) return True # return True so check function is invoked after migrate def _check_a8b517cff8ab(self, engine, data): rp = db_utils.get_table(engine, 'routerports') # just ensuring the correct count of routerport records is enough. # 20 routers * 2 ports per router self.assertEqual(40, len(engine.execute(rp.select()).fetchall())) class TestHARouterPortMigrationMysql(HARouterPortMigrationMixin, test_migrations.TestWalkMigrationsMysql): pass class TestHARouterPortMigrationPsql(HARouterPortMigrationMixin, test_migrations.TestWalkMigrationsPsql): pass ././@LongLink0000000000000000000000000000016500000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/tests/functional/db/migrations/test_2e0d7a8a1586_add_binding_index_to_routerl3agentbinding.pyneutron-12.1.1/neutron/tests/functional/db/migrations/test_2e0d7a8a1586_add_binding_index_to_routerl0000664000175000017500000000726013553660046033554 0ustar zuulzuul00000000000000# Copyright 2016 Business Cat is Very Serious 13.37 # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import collections from oslo_db.sqlalchemy import utils as db_utils from oslo_utils import uuidutils from neutron.tests.functional.db import test_migrations class HARouterPortMigrationMixin(object): """Validates binding_index for RouterL3AgentBinding migration.""" def _create_so(self, o_type, values): """create standard attr object.""" stan = db_utils.get_table(self.engine, 'standardattributes') # find next available id taking into account existing records rec_ids = [r.id for r in self.engine.execute(stan.select()).fetchall()] next_id = max([0] + rec_ids) + 1 self.engine.execute(stan.insert().values({'id': next_id, 'resource_type': o_type})) values['standard_attr_id'] = next_id return self._create_rec(o_type, values) def _create_rec(self, o_type, values): otable = db_utils.get_table(self.engine, o_type) self.engine.execute(otable.insert().values(values)) def _make_router_agents_and_bindings(self, router_id): self._create_so('routers', {'id': router_id}) # each router gets a couple of agents for _ in range(2): agent_id = uuidutils.generate_uuid() timestamp = '2000-04-06T14:34:23' self._create_rec('agents', {'id': agent_id, 'topic': 'x', 'agent_type': 'L3', 'binary': 'x', 'host': agent_id, 'created_at': timestamp, 'started_at': timestamp, 'heartbeat_timestamp': timestamp, 'configurations': ''}) self._create_rec('routerl3agentbindings', {'router_id': router_id, 'l3_agent_id': agent_id}) def _create_ha_routers(self, engine): for rid in [uuidutils.generate_uuid() for i in range(10)]: self._make_router_agents_and_bindings(rid) def _pre_upgrade_2e0d7a8a1586(self, engine): self._create_ha_routers(engine) return True # return True so check function is invoked after migrate def _check_2e0d7a8a1586(self, engine, data): bindings_table = db_utils.get_table(engine, 'routerl3agentbindings') rows = engine.execute(bindings_table.select()).fetchall() routers_to_bindings = collections.defaultdict(list) for router_id, agent_id, binding_index in rows: routers_to_bindings[router_id].append(binding_index) for binding_indices in routers_to_bindings.values(): self.assertEqual(list(range(1, 3)), sorted(binding_indices)) class TestHARouterPortMigrationMysql(HARouterPortMigrationMixin, test_migrations.TestWalkMigrationsMysql): pass class TestHARouterPortMigrationPsql(HARouterPortMigrationMixin, test_migrations.TestWalkMigrationsPsql): pass neutron-12.1.1/neutron/tests/functional/db/migrations/test_3b935b28e7a0_migrate_to_pluggable_ipam.py0000664000175000017500000001433713553660047033501 0ustar zuulzuul00000000000000# Copyright 2016 Infoblox Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from oslo_db.sqlalchemy import utils as db_utils from oslo_utils import uuidutils from neutron.tests.functional.db import test_migrations class MigrationToPluggableIpamMixin(object): """Validates data migration to Pluggable IPAM.""" _standard_attribute_id = 0 def _gen_attr_id(self, engine, type): self._standard_attribute_id += 1 standardattributes = db_utils.get_table(engine, 'standardattributes') engine.execute(standardattributes.insert().values({ 'id': self._standard_attribute_id, 'resource_type': type})) return self._standard_attribute_id def _create_subnets(self, engine, data): """Create subnets and saves subnet id in data""" networks = db_utils.get_table(engine, 'networks') subnets = db_utils.get_table(engine, 'subnets') pools = db_utils.get_table(engine, 'ipallocationpools') allocations = db_utils.get_table(engine, 'ipallocations') for cidr in data: ip_version = 6 if ':' in cidr else 4 # Save generated id in incoming dict to simplify validations network_id = uuidutils.generate_uuid() network_dict = dict( id=network_id, standard_attr_id=self._gen_attr_id(engine, 'networks')) engine.execute(networks.insert().values(network_dict)) data[cidr]['id'] = uuidutils.generate_uuid() subnet_dict = dict(id=data[cidr]['id'], cidr=cidr, ip_version=ip_version, standard_attr_id=self._gen_attr_id(engine, 'subnets')) engine.execute(subnets.insert().values(subnet_dict)) if data[cidr].get('pools'): for pool in data[cidr]['pools']: pool_dict = dict(id=uuidutils.generate_uuid(), first_ip=pool['first_ip'], last_ip=pool['last_ip'], subnet_id=data[cidr]['id']) engine.execute(pools.insert().values(pool_dict)) if data[cidr].get('allocations'): for ip in data[cidr]['allocations']: ip_dict = dict(ip_address=ip, subnet_id=data[cidr]['id'], network_id=network_id) engine.execute(allocations.insert().values(ip_dict)) def _pre_upgrade_3b935b28e7a0(self, engine): data = { '172.23.0.0/16': { 'pools': [{'first_ip': '172.23.0.2', 'last_ip': '172.23.255.254'}], 'allocations': ('172.23.0.2', '172.23.245.2')}, '192.168.40.0/24': { 'pools': [{'first_ip': '192.168.40.2', 'last_ip': '192.168.40.100'}, {'first_ip': '192.168.40.105', 'last_ip': '192.168.40.150'}, {'first_ip': '192.168.40.155', 'last_ip': '192.168.40.157'}, ], 'allocations': ('192.168.40.2', '192.168.40.3', '192.168.40.15', '192.168.40.60')}, 'fafc:babc::/64': { 'pools': [{'first_ip': 'fafc:babc::2', 'last_ip': 'fafc:babc::6:fe00', }], 'allocations': ('fafc:babc::3',)}} self._create_subnets(engine, data) return data def _check_3b935b28e7a0(self, engine, data): subnets = db_utils.get_table(engine, 'ipamsubnets') pools = db_utils.get_table(engine, 'ipamallocationpools') allocations = db_utils.get_table(engine, 'ipamallocations') ipam_subnets = engine.execute(subnets.select()).fetchall() # Count of ipam subnets should match count of usual subnets self.assertEqual(len(data), len(ipam_subnets)) neutron_to_ipam_id = {subnet.neutron_subnet_id: subnet.id for subnet in ipam_subnets} for cidr in data: self.assertIn(data[cidr]['id'], neutron_to_ipam_id) ipam_subnet_id = neutron_to_ipam_id[data[cidr]['id']] # Validate ip allocations are migrated correctly ipam_allocations = engine.execute(allocations.select().where( allocations.c.ipam_subnet_id == ipam_subnet_id)).fetchall() for ipam_allocation in ipam_allocations: self.assertIn(ipam_allocation.ip_address, data[cidr]['allocations']) self.assertEqual(len(data[cidr]['allocations']), len(ipam_allocations)) # Validate allocation pools are migrated correctly ipam_pools = engine.execute(pools.select().where( pools.c.ipam_subnet_id == ipam_subnet_id)).fetchall() # Covert to dict for easier lookup pool_dict = {pool.first_ip: pool.last_ip for pool in ipam_pools} for p in data[cidr]['pools']: self.assertIn(p['first_ip'], pool_dict) self.assertEqual(p['last_ip'], pool_dict[p['first_ip']]) self.assertEqual(len(data[cidr]['pools']), len(ipam_pools)) class TestMigrationToPluggableIpamMysql(MigrationToPluggableIpamMixin, test_migrations.TestWalkMigrationsMysql): pass class TestMigrationToPluggableIpamPsql(MigrationToPluggableIpamMixin, test_migrations.TestWalkMigrationsPsql): pass neutron-12.1.1/neutron/tests/functional/db/__init__.py0000664000175000017500000000000013553660046023002 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/db/test_models.py0000664000175000017500000000232413553660047023601 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sqlalchemy from neutron.tests import base class TestDBCreation(base.BaseTestCase): """Check database schema can be created without conflicts. For each test case is created a SQLite memory database. """ def setUp(self): super(TestDBCreation, self).setUp() self.engine = sqlalchemy.create_engine('sqlite://') def _test_creation(self, module): metadata = module.get_metadata() metadata.create_all(self.engine) def test_head_creation(self): from neutron.db.migration.models import head self._test_creation(head) neutron-12.1.1/neutron/tests/functional/common/0000775000175000017500000000000013553660156021610 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/common/test_utils.py0000664000175000017500000000170613553660047024364 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import testtools from neutron.common import utils from neutron.tests import base class TestWaitUntilTrue(base.BaseTestCase): def test_wait_until_true_predicate_succeeds(self): utils.wait_until_true(lambda: True) def test_wait_until_true_predicate_fails(self): with testtools.ExpectedException(utils.WaitTimeout): utils.wait_until_true(lambda: False, 2) neutron-12.1.1/neutron/tests/functional/common/__init__.py0000664000175000017500000000000013553660046023705 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/functional/test_service.py0000664000175000017500000000305413553660047023372 0ustar zuulzuul00000000000000# Copyright 2014 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_concurrency import processutils from oslo_config import cfg from oslo_service import service from neutron import service as neutron_service from neutron.tests import base from neutron.tests.functional import test_server class TestService(base.BaseTestCase): def test_api_workers_default(self): self.assertEqual(processutils.get_worker_count(), neutron_service._get_api_workers()) def test_api_workers_from_config(self): cfg.CONF.set_override('api_workers', 1234) self.assertEqual(1234, neutron_service._get_api_workers()) class TestServiceRestart(test_server.TestNeutronServer): def _start_service(self, host, binary, topic, manager, workers, *args, **kwargs): server = neutron_service.Service(host, binary, topic, manager, *args, **kwargs) service.launch(cfg.CONF, server, workers).wait() neutron-12.1.1/neutron/tests/functional/test_server.py0000664000175000017500000002334013553660047023240 0ustar zuulzuul00000000000000# Copyright 2015 Mirantis Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import signal import socket import time import traceback import httplib2 import mock from neutron_lib import worker as neutron_worker from oslo_config import cfg import psutil from neutron.common import utils from neutron import manager from neutron import service from neutron.tests import base from neutron import wsgi CONF = cfg.CONF # This message will be written to temporary file each time # start method is called. FAKE_START_MSG = b"start" TARGET_PLUGIN = 'neutron.plugins.ml2.plugin.Ml2Plugin' class TestNeutronServer(base.BaseTestCase): def setUp(self): super(TestNeutronServer, self).setUp() self.service_pid = None self.workers = None self.temp_file = self.get_temp_file_path("test_server.tmp") self.health_checker = self._check_active self.pipein, self.pipeout = os.pipe() self.addCleanup(self._destroy_workers) def _destroy_workers(self): if self.service_pid: # Make sure all processes are stopped os.kill(self.service_pid, signal.SIGKILL) def _start_server(self, callback, workers): """Run a given service. :param callback: callback that will start the required service :param workers: number of service workers :returns: list of spawned workers' pids """ self.workers = workers # Fork a new process in which server will be started pid = os.fork() if pid == 0: status = 0 try: callback(workers) except SystemExit as exc: status = exc.code except BaseException: traceback.print_exc() status = 2 # Really exit os._exit(status) self.service_pid = pid # If number of workers is 1 it is assumed that we run # a service in the current process. if self.workers > 1: # Wait at most 10 seconds to spawn workers condition = lambda: self.workers == len(self._get_workers()) utils.wait_until_true( condition, timeout=10, sleep=0.1, exception=RuntimeError( "Failed to start %d workers." % self.workers)) workers = self._get_workers() self.assertEqual(len(workers), self.workers) return workers # Wait for a service to start. utils.wait_until_true(self.health_checker, timeout=10, sleep=0.1, exception=RuntimeError( "Failed to start service.")) return [self.service_pid] def _get_workers(self): """Get the list of processes in which WSGI server is running.""" def safe_ppid(proc): try: return proc.ppid() except psutil.NoSuchProcess: return None if self.workers > 1: return [proc.pid for proc in psutil.process_iter() if safe_ppid(proc) == self.service_pid] else: return [proc.pid for proc in psutil.process_iter() if proc.pid == self.service_pid] def _check_active(self): """Dummy service activity check.""" time.sleep(5) return True def _fake_start(self): with open(self.temp_file, 'ab') as f: f.write(FAKE_START_MSG) def _test_restart_service_on_sighup(self, service, workers=1): """Test that a service correctly (re)starts on receiving SIGHUP. 1. Start a service with a given number of workers. 2. Send SIGHUP to the service. 3. Wait for workers (if any) to (re)start. """ self._start_server(callback=service, workers=workers) os.kill(self.service_pid, signal.SIGHUP) expected_msg = FAKE_START_MSG * workers * 2 # Wait for temp file to be created and its size reaching the expected # value expected_size = len(expected_msg) condition = lambda: (os.path.isfile(self.temp_file) and os.stat(self.temp_file).st_size == expected_size) utils.wait_until_true( condition, timeout=5, sleep=0.1, exception=RuntimeError( "Timed out waiting for file %(filename)s to be created and " "its size become equal to %(size)s." % {'filename': self.temp_file, 'size': expected_size})) # Verify that start has been called twice for each worker (one for # initial start, and the second one on SIGHUP after children were # terminated). with open(self.temp_file, 'rb') as f: res = f.readline() self.assertEqual(expected_msg, res) class TestWsgiServer(TestNeutronServer): """Tests for neutron.wsgi.Server.""" def setUp(self): super(TestWsgiServer, self).setUp() self.health_checker = self._check_active self.port = None @staticmethod def application(environ, start_response): """A primitive test application.""" response_body = 'Response' status = '200 OK' response_headers = [('Content-Type', 'text/plain'), ('Content-Length', str(len(response_body)))] start_response(status, response_headers) return [response_body] def _check_active(self): """Check a wsgi service is active by making a GET request.""" port = int(os.read(self.pipein, 5)) conn = httplib2.HTTPConnectionWithTimeout("localhost", port) try: conn.request("GET", "/") resp = conn.getresponse() return resp.status == 200 except socket.error: return False def _run_wsgi(self, workers=1): """Start WSGI server with a test application.""" # Mock start method to check that children are started again on # receiving SIGHUP. with mock.patch("neutron.wsgi.WorkerService.start") as start_method: start_method.side_effect = self._fake_start server = wsgi.Server("Test") server.start(self.application, 0, "0.0.0.0", workers=workers) # Memorize a port that was chosen for the service self.port = server.port os.write(self.pipeout, bytes(self.port)) server.wait() def test_restart_wsgi_on_sighup_multiple_workers(self): self._test_restart_service_on_sighup(service=self._run_wsgi, workers=2) class TestRPCServer(TestNeutronServer): """Tests for neutron RPC server.""" def setUp(self): super(TestRPCServer, self).setUp() self.setup_coreplugin('ml2', load_plugins=False) self._plugin_patcher = mock.patch(TARGET_PLUGIN, autospec=True) self.plugin = self._plugin_patcher.start() self.plugin.return_value.rpc_workers_supported = True def _serve_rpc(self, workers=1): """Start RPC server with a given number of workers.""" # Mock start method to check that children are started again on # receiving SIGHUP. with mock.patch("neutron.service.RpcWorker.start") as start_method: with mock.patch( "neutron_lib.plugins.directory.get_plugin" ) as get_plugin: start_method.side_effect = self._fake_start get_plugin.return_value = self.plugin CONF.set_override("rpc_workers", workers) # not interested in state report workers specifically CONF.set_override("rpc_state_report_workers", 0) rpc_workers_launcher = service.start_rpc_workers() rpc_workers_launcher.wait() def test_restart_rpc_on_sighup_multiple_workers(self): self._test_restart_service_on_sighup(service=self._serve_rpc, workers=2) class TestPluginWorker(TestNeutronServer): """Ensure that a plugin returning Workers spawns workers""" def setUp(self): super(TestPluginWorker, self).setUp() self.setup_coreplugin('ml2', load_plugins=False) self._plugin_patcher = mock.patch(TARGET_PLUGIN, autospec=True) self.plugin = self._plugin_patcher.start() manager.init() def _start_plugin(self, workers=1): with mock.patch('neutron_lib.plugins.directory.get_plugin') as gp: gp.return_value = self.plugin plugin_workers_launcher = service.start_plugins_workers() plugin_workers_launcher.wait() def test_start(self): class FakeWorker(neutron_worker.BaseWorker): def start(self): pass def wait(self): pass def stop(self): pass def reset(self): pass # Make both ABC happy and ensure 'self' is correct FakeWorker.start = self._fake_start workers = [FakeWorker()] self.plugin.return_value.get_workers.return_value = workers self._test_restart_service_on_sighup(service=self._start_plugin, workers=len(workers)) neutron-12.1.1/neutron/tests/common/0000775000175000017500000000000013553660156017446 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/common/agents/0000775000175000017500000000000013553660156020727 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/common/agents/l2_extensions.py0000664000175000017500000001147413553660047024103 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import re import signal from oslo_log import log as logging from neutron.agent.linux import async_process from neutron.agent.linux import iptables_manager from neutron.common import utils as common_utils LOG = logging.getLogger(__name__) class TcpdumpException(Exception): pass def extract_mod_nw_tos_action(flows): tos_mark = None if flows: flow_list = flows.splitlines() for flow in flow_list: if 'mod_nw_tos' in flow: actions = flow.partition('actions=')[2] after_mod = actions.partition('mod_nw_tos:')[2] tos_mark = int(after_mod.partition(',')[0]) return tos_mark def extract_dscp_value_from_iptables_rules(rules): pattern = (r"^-A neutron-linuxbri-qos-.* -j DSCP " "--set-dscp (?P0x[A-Fa-f0-9]+)$") for rule in rules: m = re.match(pattern, rule) if m: return int(m.group("dscp_value"), 16) def wait_until_bandwidth_limit_rule_applied(check_function, port_vif, rule): def _bandwidth_limit_rule_applied(): bw_rule = check_function(port_vif) expected = None, None if rule: expected = rule.max_kbps, rule.max_burst_kbps return bw_rule == expected common_utils.wait_until_true(_bandwidth_limit_rule_applied) def wait_until_egress_bandwidth_limit_rule_applied(bridge, port_vif, rule): wait_until_bandwidth_limit_rule_applied( bridge.get_egress_bw_limit_for_port, port_vif, rule) def wait_until_ingress_bandwidth_limit_rule_applied(bridge, port_vif, rule): wait_until_bandwidth_limit_rule_applied( bridge.get_ingress_bw_limit_for_port, port_vif, rule) def wait_until_dscp_marking_rule_applied_ovs(bridge, port_vif, rule): def _dscp_marking_rule_applied(): port_num = bridge.get_port_ofport(port_vif) flows = bridge.dump_flows_for(table='0', in_port=str(port_num)) dscp_mark = extract_mod_nw_tos_action(flows) expected = None if rule: expected = rule << 2 return dscp_mark == expected common_utils.wait_until_true(_dscp_marking_rule_applied) def wait_until_dscp_marking_rule_applied_linuxbridge( namespace, port_vif, expected_rule): iptables = iptables_manager.IptablesManager( namespace=namespace) def _dscp_marking_rule_applied(): mangle_rules = iptables.get_rules_for_table("mangle") dscp_mark = extract_dscp_value_from_iptables_rules(mangle_rules) return dscp_mark == expected_rule common_utils.wait_until_true(_dscp_marking_rule_applied) def wait_for_dscp_marked_packet(sender_vm, receiver_vm, dscp_mark): cmd = [ "tcpdump", "-i", receiver_vm.port.name, "-nlt", "src", sender_vm.ip, 'and', 'dst', receiver_vm.ip] if dscp_mark: cmd += ["and", "(ip[1] & 0xfc == %s)" % (dscp_mark << 2)] tcpdump_async = async_process.AsyncProcess(cmd, run_as_root=True, namespace=receiver_vm.namespace) tcpdump_async.start() sender_vm.block_until_ping(receiver_vm.ip) try: tcpdump_async.stop(kill_signal=signal.SIGINT) except async_process.AsyncProcessException: # If it was already stopped than we don't care about it pass pattern = r"(?P^\d+) packets received by filter" for line in tcpdump_async.iter_stderr(): # TODO(slaweq): Debug logging added to help troubleshooting bug # https://bugs.launchpad.net/neutron/+bug/1733649 # once it will be closed this log can be removed LOG.debug("Tcpdump error output line: %s", line) m = re.match(pattern, line) if m and int(m.group("packets_count")) != 0: return # TODO(slaweq): Debug logging added to help troubleshooting bug # https://bugs.launchpad.net/neutron/+bug/1733649 # once it will be closed this log can be removed for line in tcpdump_async.iter_stdout(): LOG.debug("Tcpdump output line: %s", line) raise TcpdumpException( "No packets marked with DSCP = %(dscp_mark)s received from %(src)s " "to %(dst)s" % {'dscp_mark': dscp_mark, 'src': sender_vm.ip, 'dst': receiver_vm.ip}) neutron-12.1.1/neutron/tests/common/agents/__init__.py0000664000175000017500000000000013553660046023024 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/common/agents/l3_agent.py0000775000175000017500000000717413553660047023010 0ustar zuulzuul00000000000000#!/usr/bin/env python # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys import types import mock from oslo_config import cfg from neutron.agent.l3 import agent from neutron.agent.l3 import namespaces from neutron.agent import l3_agent from neutron.common import constants class L3NATAgentForTest(agent.L3NATAgentWithStateReport): def __init__(self, host, conf=None): ns_suffix = '@%s' % cfg.CONF.test_namespace_suffix # Mock out building of namespace names orig_build_ns_name = namespaces.build_ns_name def build_ns_name(prefix, identifier): return "%s%s" % (orig_build_ns_name(prefix, identifier), ns_suffix) build_ns = mock.patch.object(namespaces, 'build_ns_name').start() build_ns.side_effect = build_ns_name # Mock the parsing prefix from namespace names orig_get_prefix = namespaces.get_prefix_from_ns_name def get_prefix_from_ns_name(ns_name): if ns_name.endswith(ns_suffix): return orig_get_prefix(ns_name[:-len(ns_suffix)]) parse_prefix = mock.patch.object(namespaces, 'get_prefix_from_ns_name').start() parse_prefix.side_effect = get_prefix_from_ns_name # Mock the parsing id from namespace names orig_get_id = namespaces.get_id_from_ns_name def get_id_from_ns_name(ns_name): if ns_name.endswith(ns_suffix): return orig_get_id(ns_name[:-len(ns_suffix)]) parse_id = mock.patch.object(namespaces, 'get_id_from_ns_name').start() parse_id.side_effect = get_id_from_ns_name super(L3NATAgentForTest, self).__init__(host, conf) def _create_router(self, router_id, router): """Create a router with suffix added to the router namespace name. This is needed to be able to run two agents serving the same router on the same node. """ router = ( super(L3NATAgentForTest, self)._create_router(router_id, router)) router.get_internal_device_name = types.MethodType( get_internal_device_name, router) router.get_external_device_name = types.MethodType( get_external_device_name, router) return router def _append_suffix(dev_name): # If dev_name = 'xyz123' and the suffix is 'hostB' then the result # will be 'xy_stB' return '%s_%s' % (dev_name[:-4], cfg.CONF.test_namespace_suffix[-3:]) def get_internal_device_name(ri, port_id): return _append_suffix( (namespaces.INTERNAL_DEV_PREFIX + port_id) [:constants.LINUX_DEV_LEN]) def get_external_device_name(ri, port_id): return _append_suffix( (namespaces.EXTERNAL_DEV_PREFIX + port_id) [:constants.LINUX_DEV_LEN]) OPTS = [ cfg.StrOpt('test_namespace_suffix', default='testprefix', help="Suffix to append to all namespace names."), ] def register_opts(conf): conf.register_opts(OPTS) def main(manager='neutron.tests.common.agents.l3_agent.L3NATAgentForTest'): register_opts(cfg.CONF) l3_agent.main(manager=manager) if __name__ == "__main__": sys.exit(main()) neutron-12.1.1/neutron/tests/common/agents/ovs_agent.py0000775000175000017500000000334413553660047023274 0ustar zuulzuul00000000000000#!/usr/bin/env python # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import hashlib import sys from neutron_lib import constants as n_const from oslo_utils import encodeutils from neutron.cmd.eventlet.plugins.ovs_neutron_agent import main as _main from neutron.plugins.ml2.drivers.openvswitch.agent import ovs_neutron_agent def get_tunnel_name_full(cls, network_type, local_ip, remote_ip): network_type = network_type[:3] # Remove length of network_type and two dashes hashlen = (n_const.DEVICE_NAME_MAX_LEN - len(network_type) - 2) // 2 remote_tunnel_hash = cls.get_tunnel_hash(remote_ip, hashlen) if not remote_tunnel_hash: return None remote_tunnel_hash = encodeutils.to_utf8(remote_tunnel_hash) remote_ip_hash = hashlib.sha1(remote_tunnel_hash).hexdigest()[:hashlen] local_tunnel_hash = cls.get_tunnel_hash(local_ip, hashlen) local_tunnel_hash = encodeutils.to_utf8(local_tunnel_hash) source_ip_hash = hashlib.sha1(local_tunnel_hash).hexdigest()[:hashlen] return '%s-%s-%s' % (network_type, source_ip_hash, remote_ip_hash) ovs_neutron_agent.OVSNeutronAgent.get_tunnel_name = get_tunnel_name_full def main(): _main() if __name__ == "__main__": sys.exit(main()) neutron-12.1.1/neutron/tests/common/conn_testers.py0000664000175000017500000005464113553660047022537 0ustar zuulzuul00000000000000# All Rights Reserved. # # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import fixtures import netaddr from neutron_lib import constants from oslo_utils import uuidutils from neutron.agent import firewall from neutron.common import constants as n_consts from neutron.common import utils as common_utils from neutron.plugins.ml2.drivers.openvswitch.agent.common import ( constants as ovs_consts) from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import ( br_int) from neutron.tests.common import machine_fixtures from neutron.tests.common import net_helpers # NOTE: IPv6 uses NDP for obtaining destination endpoints link address that # extends round-trip packet time in ICMP tests. The timeout value should be # sufficient for correct scenarios but not too high because of negative # tests. ICMP_VERSION_TIMEOUTS = { constants.IP_VERSION_4: 1, constants.IP_VERSION_6: 2, } class ConnectionTesterException(Exception): pass def _validate_direction(f): @functools.wraps(f) def wrap(self, direction, *args, **kwargs): if direction not in (firewall.INGRESS_DIRECTION, firewall.EGRESS_DIRECTION): raise ConnectionTesterException('Unknown direction %s' % direction) return f(self, direction, *args, **kwargs) return wrap def _get_packets_sent_received(src_namespace, dst_ip, count): pinger = net_helpers.Pinger(src_namespace, dst_ip, count=count) pinger.start() pinger.wait() return pinger.sent, pinger.received def all_replied(src_ns, dst_ip, count): sent, received = _get_packets_sent_received(src_ns, dst_ip, count) return sent == received def all_lost(src_ns, dst_ip, count): sent, received = _get_packets_sent_received(src_ns, dst_ip, count) return received == 0 class ConnectionTester(fixtures.Fixture): """Base class for testers This class implements API for various methods for testing connectivity. The concrete implementation relies on how encapsulated resources are configured. That means child classes should define resources by themselves (e.g. endpoints connected through linux bridge or ovs bridge). """ UDP = net_helpers.NetcatTester.UDP TCP = net_helpers.NetcatTester.TCP ICMP = constants.PROTO_NAME_ICMP ARP = n_consts.ETHERTYPE_NAME_ARP INGRESS = firewall.INGRESS_DIRECTION EGRESS = firewall.EGRESS_DIRECTION def __init__(self, ip_cidr): self.ip_cidr = ip_cidr self.icmp_count = 3 self.connectivity_timeout = 12 def _setUp(self): self._protocol_to_method = { self.UDP: self._test_transport_connectivity, self.TCP: self._test_transport_connectivity, self.ICMP: self._test_icmp_connectivity, self.ARP: self._test_arp_connectivity} self._nc_testers = {} self._pingers = {} self.addCleanup(self.cleanup) def cleanup(self): for nc in self._nc_testers.values(): nc.stop_processes() for pinger in self._pingers.values(): pinger.stop() @property def vm_namespace(self): return self._vm.namespace @property def vm_ip_address(self): return self._vm.ip @property def vm_ip_cidr(self): return self._vm.ip_cidr @vm_ip_cidr.setter def vm_ip_cidr(self, ip_cidr): self._vm.ip_cidr = ip_cidr @property def vm_mac_address(self): return self._vm.port.link.address @vm_mac_address.setter def vm_mac_address(self, mac_address): self._vm.mac_address = mac_address @property def peer_mac_address(self): return self._peer.port.link.address @peer_mac_address.setter def peer_mac_address(self, mac_address): self._peer.mac_address = mac_address @property def peer_namespace(self): return self._peer.namespace @property def peer_ip_address(self): return self._peer.ip def set_vm_default_gateway(self, default_gw): self._vm.set_default_gateway(default_gw) def flush_arp_tables(self): """Flush arptables in all used namespaces""" for machine in (self._peer, self._vm): machine.port.neigh.flush(4, 'all') def _test_transport_connectivity(self, direction, protocol, src_port, dst_port): nc_tester = self._create_nc_tester(direction, protocol, src_port, dst_port) try: nc_tester.test_connectivity() except RuntimeError as exc: nc_tester.stop_processes() raise ConnectionTesterException( "%s connection over %s protocol with %s source port and " "%s destination port can't be established: %s" % ( direction, protocol, src_port, dst_port, exc)) @_validate_direction def _get_namespace_and_address(self, direction): if direction == self.INGRESS: return self.peer_namespace, self.vm_ip_address return self.vm_namespace, self.peer_ip_address def _test_icmp_connectivity(self, direction, protocol, src_port, dst_port): src_namespace, ip_address = self._get_namespace_and_address(direction) ip_version = common_utils.get_ip_version(ip_address) icmp_timeout = ICMP_VERSION_TIMEOUTS[ip_version] try: net_helpers.assert_ping(src_namespace, ip_address, timeout=icmp_timeout) except RuntimeError: raise ConnectionTesterException( "ICMP packets can't get from %s namespace to %s address" % ( src_namespace, ip_address)) def _test_arp_connectivity(self, direction, protocol, src_port, dst_port): src_namespace, ip_address = self._get_namespace_and_address(direction) try: net_helpers.assert_arping(src_namespace, ip_address) except RuntimeError: raise ConnectionTesterException( "ARP queries to %s address have no response from %s namespace" % (ip_address, src_namespace)) @_validate_direction def assert_connection(self, direction, protocol, src_port=None, dst_port=None): testing_method = self._protocol_to_method[protocol] testing_method(direction, protocol, src_port, dst_port) def assert_no_connection(self, direction, protocol, src_port=None, dst_port=None): try: self.assert_connection(direction, protocol, src_port, dst_port) except ConnectionTesterException: pass else: dst_port_info = str() src_port_info = str() if dst_port is not None: dst_port_info = " and destination port %d" % dst_port if src_port is not None: src_port_info = " and source port %d" % src_port raise ConnectionTesterException("%s connection with protocol %s, " "source port %s, destination " "port %s was established but it " "shouldn't be possible" % ( direction, protocol, src_port_info, dst_port_info)) @_validate_direction def assert_established_connection(self, direction, protocol, src_port=None, dst_port=None): nc_params = (direction, protocol, src_port, dst_port) nc_tester = self._nc_testers.get(nc_params) if nc_tester: if nc_tester.is_established: try: nc_tester.test_connectivity() except RuntimeError: raise ConnectionTesterException( "Established %s connection with protocol %s, source " "port %s and destination port %s can no longer " "communicate") else: nc_tester.stop_processes() raise ConnectionTesterException( '%s connection with protocol %s, source port %s and ' 'destination port %s is not established' % nc_params) else: raise ConnectionTesterException( "Attempting to test established %s connection with protocol %s" ", source port %s and destination port %s that hasn't been " "established yet by calling establish_connection()" % nc_params) def assert_no_established_connection(self, direction, protocol, src_port=None, dst_port=None): try: self.assert_established_connection(direction, protocol, src_port, dst_port) except ConnectionTesterException: pass else: raise ConnectionTesterException( 'Established %s connection with protocol %s, source port %s, ' 'destination port %s can still send packets through' % ( direction, protocol, src_port, dst_port)) @_validate_direction def establish_connection(self, direction, protocol, src_port=None, dst_port=None): nc_tester = self._create_nc_tester(direction, protocol, src_port, dst_port) nc_tester.establish_connection() self.addCleanup(nc_tester.stop_processes) def _create_nc_tester(self, direction, protocol, src_port, dst_port): """Create netcat tester If there already exists a netcat tester that has established connection, exception is raised. """ nc_key = (direction, protocol, src_port, dst_port) nc_tester = self._nc_testers.get(nc_key) if nc_tester and nc_tester.is_established: raise ConnectionTesterException( '%s connection using %s protocol, source port %s and ' 'destination port %s is already established' % ( direction, protocol, src_port, dst_port)) if direction == self.INGRESS: client_ns = self.peer_namespace server_ns = self.vm_namespace server_addr = self.vm_ip_address else: client_ns = self.vm_namespace server_ns = self.peer_namespace server_addr = self.peer_ip_address server_port = dst_port or net_helpers.get_free_namespace_port( protocol, server_ns) nc_tester = net_helpers.NetcatTester(client_namespace=client_ns, server_namespace=server_ns, address=server_addr, protocol=protocol, src_port=src_port, dst_port=server_port) self._nc_testers[nc_key] = nc_tester return nc_tester def _get_pinger(self, direction): try: pinger = self._pingers[direction] except KeyError: src_namespace, dst_address = self._get_namespace_and_address( direction) pinger = net_helpers.Pinger( src_namespace, dst_address, interval=0.3) self._pingers[direction] = pinger return pinger def start_sending_icmp(self, direction): pinger = self._get_pinger(direction) pinger.start() def stop_sending_icmp(self, direction): pinger = self._get_pinger(direction) pinger.stop() def get_sent_icmp_packets(self, direction): pinger = self._get_pinger(direction) return pinger.sent def get_received_icmp_packets(self, direction): pinger = self._get_pinger(direction) return pinger.received def assert_net_unreachable(self, direction, destination): src_namespace, dst_address = self._get_namespace_and_address( direction) pinger = net_helpers.Pinger(src_namespace, destination, count=5) pinger.start() pinger.wait() if not pinger.destination_unreachable: raise ConnectionTesterException( 'No Host Destination Unreachable packets were received when ' 'sending icmp packets to %s' % destination) def wait_for_connection(self, direction): src_ns, dst_ip = self._get_namespace_and_address( direction) all_replied_predicate = functools.partial( all_replied, src_ns, dst_ip, count=self.icmp_count) common_utils.wait_until_true( all_replied_predicate, timeout=self.connectivity_timeout, exception=ConnectionTesterException( "Not all ICMP packets replied from %s namespace to %s " "address." % self._get_namespace_and_address(direction))) def wait_for_no_connection(self, direction): src_ns, dst_ip = self._get_namespace_and_address( direction) all_lost_predicate = functools.partial( all_lost, src_ns, dst_ip, count=self.icmp_count) common_utils.wait_until_true( all_lost_predicate, timeout=self.connectivity_timeout, exception=ConnectionTesterException( "At least one packet got reply from %s namespace to %s " "address." % self._get_namespace_and_address(direction))) def set_peer_port_as_patch_port(self): pass def set_peer_port_as_vm_port(self): pass class OVSBaseConnectionTester(ConnectionTester): @property def peer_port_id(self): return self._peer.port.id @property def vm_port_id(self): return self._vm.port.id @staticmethod def set_tag(port_name, bridge, tag): ovsdb = bridge.ovsdb with ovsdb.transaction() as txn: txn.add(ovsdb.db_set('Port', port_name, ('tag', tag))) txn.add( ovsdb.db_add( 'Port', port_name, 'other_config', {'tag': str(tag)})) class OVSConnectionTester(OVSBaseConnectionTester): """Tester with OVS bridge in the middle The endpoints are created as OVS ports attached to the OVS bridge. NOTE: The OVS ports are connected from the namespace. This connection is currently not supported in OVS and may lead to unpredicted behavior: https://bugzilla.redhat.com/show_bug.cgi?id=1160340 """ def _setUp(self): super(OVSConnectionTester, self)._setUp() br_name = self.useFixture( net_helpers.OVSBridgeFixture()).bridge.br_name self.bridge = br_int.OVSIntegrationBridge(br_name) self.bridge.setup_default_table() machines = self.useFixture( machine_fixtures.PeerMachines( self.bridge, self.ip_cidr)).machines self._peer = machines[0] self._vm = machines[1] self._set_port_attrs(self._peer.port) self._set_port_attrs(self._vm.port) def _set_port_attrs(self, port): port.id = uuidutils.generate_uuid() attrs = [('type', 'internal'), ('external_ids', { 'iface-id': port.id, 'iface-status': 'active', 'attached-mac': port.link.address})] for column, value in attrs: self.bridge.set_db_attribute('Interface', port.name, column, value) def set_vm_tag(self, tag): self.set_tag(self._vm.port.name, self.bridge, tag) self._vm.port.vlan_tag = tag def set_peer_tag(self, tag): self.set_tag(self._peer.port.name, self.bridge, tag) self._peer.port.vlan_tag = tag def set_peer_port_as_patch_port(self): """As packets coming from tunneling bridges are always tagged with local VLAN tag, this flows will simulate the behavior. """ self.bridge.add_flow( table=ovs_consts.LOCAL_SWITCHING, priority=110, vlan_tci=0, in_port=self.bridge.get_port_ofport(self._peer.port.name), actions='mod_vlan_vid:0x%x,' 'resubmit(,%d)' % ( self._peer.port.vlan_tag, ovs_consts.LOCAL_SWITCHING) ) self.bridge.add_flow( table=ovs_consts.TRANSIENT_TABLE, priority=4, dl_vlan='0x%x' % self._peer.port.vlan_tag, actions='strip_vlan,normal' ) def set_peer_port_as_vm_port(self): """Remove flows simulating traffic from tunneling bridges. This method is opposite to set_peer_port_as_patch_port(). """ self.bridge.delete_flows( table=ovs_consts.LOCAL_SWITCHING, vlan_tci=0, in_port=self.bridge.get_port_ofport(self._peer.port.name), ) self.bridge.delete_flows( table=ovs_consts.TRANSIENT_TABLE, dl_vlan='0x%x' % self._peer.port.vlan_tag, ) class OVSTrunkConnectionTester(OVSBaseConnectionTester): """Tester with OVS bridge and a trunk bridge Two endpoints: one is a VM that is connected to a port associated with a trunk (the port is created on the trunk bridge), the other is a VM on the same network (the port is on the integration bridge). NOTE: The OVS ports are connected from the namespace. This connection is currently not supported in OVS and may lead to unpredicted behavior: https://bugzilla.redhat.com/show_bug.cgi?id=1160340 """ def __init__(self, ip_cidr, br_trunk_name): super(OVSTrunkConnectionTester, self).__init__(ip_cidr) self._br_trunk_name = br_trunk_name def _setUp(self): super(OVSTrunkConnectionTester, self)._setUp() self.bridge = self.useFixture( net_helpers.OVSBridgeFixture()).bridge self.br_trunk = self.useFixture( net_helpers.OVSTrunkBridgeFixture(self._br_trunk_name)).bridge self._peer = self.useFixture(machine_fixtures.FakeMachine( self.bridge, self.ip_cidr)) ip_cidr = net_helpers.increment_ip_cidr(self.ip_cidr, 1) self._vm = self.useFixture(machine_fixtures.FakeMachine( self.br_trunk, ip_cidr)) def add_vlan_interface_and_peer(self, vlan, ip_cidr): """Create a sub_port and a peer We create a sub_port that uses vlan as segmentation ID. In the vm namespace we create a vlan subinterface on the same vlan. A peer on the same network is created. When pinging from the peer to the sub_port packets will be tagged using the internal vlan ID of the network. The sub_port will remove that vlan tag and push the vlan specified in the segmentation ID. The packets will finally reach the vlan subinterface in the vm namespace. """ network = netaddr.IPNetwork(ip_cidr) net_helpers.create_vlan_interface( self._vm.namespace, self._vm.port.name, self.vm_mac_address, network, vlan) self._ip_vlan = str(network.ip) ip_cidr = net_helpers.increment_ip_cidr(ip_cidr, 1) self._peer2 = self.useFixture(machine_fixtures.FakeMachine( self.bridge, ip_cidr)) def set_vm_tag(self, tag): self.set_tag(self._vm.port.name, self.br_trunk, tag) def set_peer_tag(self, tag): self.set_tag(self._peer.port.name, self.bridge, tag) def _get_subport_namespace_and_address(self, direction): if direction == self.INGRESS: return self._peer2.namespace, self._ip_vlan return self._vm.namespace, self._peer2.ip def wait_for_sub_port_connectivity(self, direction): src_ns, dst_ip = self._get_subport_namespace_and_address( direction) all_replied_predicate = functools.partial( all_replied, src_ns, dst_ip, count=self.icmp_count) common_utils.wait_until_true( all_replied_predicate, timeout=self.connectivity_timeout, exception=ConnectionTesterException( "ICMP traffic from %s namespace to subport with address %s " "can't get through." % (src_ns, dst_ip))) def wait_for_sub_port_no_connectivity(self, direction): src_ns, dst_ip = self._get_subport_namespace_and_address( direction) all_lost_predicate = functools.partial( all_lost, src_ns, dst_ip, count=self.icmp_count) common_utils.wait_until_true( all_lost_predicate, timeout=self.connectivity_timeout, exception=ConnectionTesterException( "ICMP traffic from %s namespace to subport with address %s " "can still get through." % (src_ns, dst_ip))) class LinuxBridgeConnectionTester(ConnectionTester): """Tester with linux bridge in the middle Both endpoints are placed in their separated namespace connected to bridge's namespace via veth pair. """ def __init__(self, *args, **kwargs): self.bridge_name = kwargs.pop('bridge_name', None) super(LinuxBridgeConnectionTester, self).__init__(*args, **kwargs) def _setUp(self): super(LinuxBridgeConnectionTester, self)._setUp() bridge_args = {} if self.bridge_name: bridge_args = {'prefix': self.bridge_name, 'prefix_is_full_name': True} self.bridge = self.useFixture( net_helpers.LinuxBridgeFixture(**bridge_args)).bridge machines = self.useFixture( machine_fixtures.PeerMachines( self.bridge, self.ip_cidr)).machines self._peer = machines[0] self._vm = machines[1] @property def bridge_namespace(self): return self.bridge.namespace @property def vm_port_id(self): return net_helpers.VethFixture.get_peer_name(self._vm.port.name) @property def peer_port_id(self): return net_helpers.VethFixture.get_peer_name(self._peer.port.name) def flush_arp_tables(self): self.bridge.neigh.flush(4, 'all') super(LinuxBridgeConnectionTester, self).flush_arp_tables() neutron-12.1.1/neutron/tests/common/l3_test_common.py0000664000175000017500000003411213553660047022745 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import netaddr from neutron_lib import constants as lib_constants from neutron_lib.services.qos import constants as qos_consts from oslo_utils import uuidutils from six import moves from neutron.common import ipv6_utils _uuid = uuidutils.generate_uuid class FakeDev(object): def __init__(self, name): self.name = name def get_ha_interface(ip='169.254.192.1', mac='12:34:56:78:2b:5d'): subnet_id = _uuid() return {'admin_state_up': True, 'device_id': _uuid(), 'device_owner': lib_constants.DEVICE_OWNER_ROUTER_HA_INTF, 'fixed_ips': [{'ip_address': ip, 'prefixlen': 18, 'subnet_id': subnet_id}], 'id': _uuid(), 'mac_address': mac, 'name': u'L3 HA Admin port 0', 'mtu': 1500, 'network_id': _uuid(), 'status': u'ACTIVE', 'subnets': [{'cidr': '169.254.192.0/18', 'gateway_ip': '169.254.255.254', 'id': subnet_id}], 'tenant_id': '', 'agent_id': _uuid(), 'agent_host': 'aaa', 'priority': 1} def prepare_router_data(ip_version=4, enable_snat=None, num_internal_ports=1, enable_floating_ip=False, enable_ha=False, extra_routes=False, dual_stack=False, enable_gw=True, v6_ext_gw_with_sub=True, snat_bound_fip=False, vrrp_id=None, **kwargs): fixed_ips = [] subnets = [] gateway_mac = kwargs.get('gateway_mac', 'ca:fe:de:ad:be:ee') extra_subnets = [] for loop_version in (4, 6): if loop_version == 4 and (ip_version == 4 or dual_stack): ip_address = kwargs.get('ip_address', '19.4.4.4') prefixlen = 24 subnet_cidr = kwargs.get('subnet_cidr', '19.4.4.0/24') gateway_ip = kwargs.get('gateway_ip', '19.4.4.1') _extra_subnet = {'cidr': '9.4.5.0/24'} elif (loop_version == 6 and (ip_version == 6 or dual_stack) and v6_ext_gw_with_sub): ip_address = kwargs.get('ip_address', 'fd00::4') prefixlen = 64 subnet_cidr = kwargs.get('subnet_cidr', 'fd00::/64') gateway_ip = kwargs.get('gateway_ip', 'fd00::1') _extra_subnet = {'cidr': 'fd01::/64'} else: continue subnet_id = _uuid() fixed_ips.append({'ip_address': ip_address, 'subnet_id': subnet_id, 'prefixlen': prefixlen}) subnets.append({'id': subnet_id, 'cidr': subnet_cidr, 'gateway_ip': gateway_ip}) extra_subnets.append(_extra_subnet) if not fixed_ips and v6_ext_gw_with_sub: raise ValueError("Invalid ip_version: %s" % ip_version) router_id = _uuid() ex_gw_port = {} if enable_gw: ex_gw_port = {'id': _uuid(), 'mac_address': gateway_mac, 'mtu': 1500, 'network_id': _uuid(), 'fixed_ips': fixed_ips, 'subnets': subnets, 'extra_subnets': extra_subnets} routes = [] if extra_routes: routes = [{'destination': '8.8.8.0/24', 'nexthop': '19.4.4.4'}] router = { 'id': router_id, 'distributed': False, lib_constants.INTERFACE_KEY: [], 'routes': routes, 'gw_port': ex_gw_port} router_fips = router.get(lib_constants.FLOATINGIP_KEY, []) if enable_floating_ip: fip = {'id': _uuid(), 'port_id': _uuid(), 'status': 'DOWN', 'floating_ip_address': '19.4.4.2', 'fixed_ip_address': '10.0.0.1'} qos_policy_id = kwargs.get(qos_consts.QOS_POLICY_ID) if qos_policy_id: fip[qos_consts.QOS_POLICY_ID] = qos_policy_id router_fips.append(fip) if snat_bound_fip: fip = {'id': _uuid(), 'port_id': _uuid(), 'status': 'DOWN', 'floating_ip_address': '19.4.4.3', 'fixed_ip_address': '10.0.0.2'} qos_policy_id = kwargs.get(qos_consts.QOS_POLICY_ID) if qos_policy_id: fip[qos_consts.QOS_POLICY_ID] = qos_policy_id router_fips.append(fip) router[lib_constants.FLOATINGIP_KEY] = router_fips router_append_interface(router, count=num_internal_ports, ip_version=ip_version, dual_stack=dual_stack) if enable_ha: ha_port_ip = kwargs.get('ha_port_ip', '169.254.192.1') ha_port_mac = kwargs.get('ha_port_mac', '12:34:56:78:2b:aa') router['ha'] = True router['ha_vr_id'] = vrrp_id or 1 router[lib_constants.HA_INTERFACE_KEY] = ( get_ha_interface(ip=ha_port_ip, mac=ha_port_mac)) if enable_snat is not None: router['enable_snat'] = enable_snat return router def get_subnet_id(port): return port['fixed_ips'][0]['subnet_id'] def router_append_interface(router, count=1, ip_version=4, ra_mode=None, addr_mode=None, dual_stack=False, same_port=False): interfaces = router[lib_constants.INTERFACE_KEY] current = sum( [netaddr.IPNetwork(subnet['cidr']).version == ip_version for p in interfaces for subnet in p['subnets']]) # If dual_stack=True, create IPv4 and IPv6 subnets on each port # If same_port=True, create ip_version number of subnets on a single port # Else create just an ip_version subnet on each port if dual_stack: ip_versions = [4, 6] elif same_port: ip_versions = [ip_version] * count count = 1 else: ip_versions = [ip_version] mac_address = netaddr.EUI('ca:fe:de:ad:be:ef') mac_address.dialect = netaddr.mac_unix for i in range(current, current + count): fixed_ips = [] subnets = [] for loop_version in ip_versions: if loop_version == 4 and (ip_version == 4 or dual_stack): ip_pool = '35.4.%i.4' cidr_pool = '35.4.%i.0/24' prefixlen = 24 gw_pool = '35.4.%i.1' elif loop_version == 6 and (ip_version == 6 or dual_stack): ip_pool = 'fd01:%x:1::6' cidr_pool = 'fd01:%x:1::/64' prefixlen = 64 gw_pool = 'fd01:%x:1::1' else: continue subnet_id = _uuid() fixed_ips.append({'ip_address': ip_pool % i, 'subnet_id': subnet_id, 'prefixlen': prefixlen}) subnets.append({'id': subnet_id, 'cidr': cidr_pool % i, 'gateway_ip': gw_pool % i, 'ipv6_ra_mode': ra_mode, 'ipv6_address_mode': addr_mode}) if not fixed_ips: raise ValueError("Invalid ip_version: %s" % ip_version) interfaces.append( {'id': _uuid(), 'mtu': 1500, 'network_id': _uuid(), 'admin_state_up': True, 'fixed_ips': fixed_ips, 'mac_address': str(mac_address), 'subnets': subnets}) mac_address.value += 1 def router_append_subnet(router, count=1, ip_version=4, ipv6_subnet_modes=None, interface_id=None, dns_nameservers=None, network_mtu=0): if ip_version == 6: subnet_mode_none = {'ra_mode': None, 'address_mode': None} if not ipv6_subnet_modes: ipv6_subnet_modes = [subnet_mode_none] * count elif len(ipv6_subnet_modes) != count: ipv6_subnet_modes.extend([subnet_mode_none for i in moves.range(len(ipv6_subnet_modes), count)]) if ip_version == 4: ip_pool = '35.4.%i.4' cidr_pool = '35.4.%i.0/24' prefixlen = 24 gw_pool = '35.4.%i.1' elif ip_version == 6: ip_pool = 'fd01:%x::6' cidr_pool = 'fd01:%x::/64' prefixlen = 64 gw_pool = 'fd01:%x::1' else: raise ValueError("Invalid ip_version: %s" % ip_version) interfaces = copy.deepcopy(router.get(lib_constants.INTERFACE_KEY, [])) if interface_id: try: interface = next(i for i in interfaces if i['id'] == interface_id) except StopIteration: raise ValueError("interface_id not found") fixed_ips, subnets = interface['fixed_ips'], interface['subnets'] else: interface = None fixed_ips, subnets = [], [] num_existing_subnets = len(subnets) for i in moves.range(count): subnet_id = _uuid() fixed_ips.append( {'ip_address': ip_pool % (i + num_existing_subnets), 'subnet_id': subnet_id, 'prefixlen': prefixlen}) subnets.append( {'id': subnet_id, 'cidr': cidr_pool % (i + num_existing_subnets), 'gateway_ip': gw_pool % (i + num_existing_subnets), 'dns_nameservers': dns_nameservers, 'ipv6_ra_mode': ipv6_subnet_modes[i]['ra_mode'], 'ipv6_address_mode': ipv6_subnet_modes[i]['address_mode']}) if interface: # Update old interface index = interfaces.index(interface) interfaces[index].update({'fixed_ips': fixed_ips, 'subnets': subnets}) else: # New interface appended to interfaces list mac_address = netaddr.EUI('ca:fe:de:ad:be:ef') mac_address.dialect = netaddr.mac_unix interfaces.append( {'id': _uuid(), 'mtu': network_mtu, 'network_id': _uuid(), 'admin_state_up': True, 'mac_address': str(mac_address), 'fixed_ips': fixed_ips, 'subnets': subnets}) router[lib_constants.INTERFACE_KEY] = interfaces def router_append_pd_enabled_subnet(router, count=1, prefix=None): if not prefix: prefix = lib_constants.PROVISIONAL_IPV6_PD_PREFIX interfaces = router[lib_constants.INTERFACE_KEY] current = sum(netaddr.IPNetwork(subnet['cidr']).version == 6 for p in interfaces for subnet in p['subnets']) mac_address = netaddr.EUI('ca:fe:de:ad:be:ef') mac_address.dialect = netaddr.mac_unix pd_intfs = [] for i in range(current, current + count): subnet_id = _uuid() intf = {'id': _uuid(), 'mtu': 1500, 'network_id': _uuid(), 'admin_state_up': True, 'fixed_ips': [{'ip_address': '::1', 'prefixlen': 64, 'subnet_id': subnet_id}], 'mac_address': str(mac_address), 'subnets': [{'id': subnet_id, 'cidr': prefix, 'gateway_ip': '::1', 'ipv6_ra_mode': lib_constants.IPV6_SLAAC, 'subnetpool_id': lib_constants.IPV6_PD_POOL_ID}]} interfaces.append(intf) pd_intfs.append(intf) mac_address.value += 1 def get_unassigned_pd_interfaces(router): pd_intfs = [] for intf in router[lib_constants.INTERFACE_KEY]: for subnet in intf['subnets']: if (ipv6_utils.is_ipv6_pd_enabled(subnet) and subnet['cidr'] == lib_constants.PROVISIONAL_IPV6_PD_PREFIX): pd_intfs.append(intf) return pd_intfs def get_assigned_pd_interfaces(router): pd_intfs = [] for intf in router[lib_constants.INTERFACE_KEY]: for subnet in intf['subnets']: if (ipv6_utils.is_ipv6_pd_enabled(subnet) and subnet['cidr'] != lib_constants.PROVISIONAL_IPV6_PD_PREFIX): pd_intfs.append(intf) return pd_intfs def assign_prefix_for_pd_interfaces(router): pd_intfs = [] for ifno, intf in enumerate(router[lib_constants.INTERFACE_KEY]): for subnet in intf['subnets']: if (ipv6_utils.is_ipv6_pd_enabled(subnet) and subnet['cidr'] == lib_constants.PROVISIONAL_IPV6_PD_PREFIX): subnet['cidr'] = "2001:db8:%d::/64" % ifno pd_intfs.append(intf) return pd_intfs def prepare_ext_gw_test(context, ri, dual_stack=False): subnet_id = _uuid() fixed_ips = [{'subnet_id': subnet_id, 'ip_address': '20.0.0.30', 'prefixlen': 24}] subnets = [{'id': subnet_id, 'cidr': '20.0.0.0/24', 'gateway_ip': '20.0.0.1'}] if dual_stack: subnet_id_v6 = _uuid() fixed_ips.append({'subnet_id': subnet_id_v6, 'ip_address': '2001:192:168:100::2', 'prefixlen': 64}) subnets.append({'id': subnet_id_v6, 'cidr': '2001:192:168:100::/64', 'gateway_ip': '2001:192:168:100::1'}) ex_gw_port = {'fixed_ips': fixed_ips, 'subnets': subnets, 'extra_subnets': [{'cidr': '172.16.0.0/24'}], 'id': _uuid(), 'mtu': 1500, 'network_id': _uuid(), 'mac_address': 'ca:fe:de:ad:be:ef'} interface_name = ri.get_external_device_name(ex_gw_port['id']) context.device_exists.return_value = True return interface_name, ex_gw_port neutron-12.1.1/neutron/tests/common/base.py0000664000175000017500000000510613553660046020732 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import functools from neutron_lib import constants as n_const import testtools.testcase import unittest2.case from neutron.common import utils from neutron.tests import base from neutron.tests import tools def create_resource(prefix, creation_func, *args, **kwargs): """Create a new resource that does not already exist. If prefix isn't 'max_length' in size, a random suffix is concatenated to ensure it is random. Otherwise, 'prefix' is used as is. :param prefix: The prefix for a randomly generated name :param creation_func: A function taking the name of the resource to be created as it's first argument. An error is assumed to indicate a name collision. :param *args *kwargs: These will be passed to the create function. """ # Don't generate a random name if prefix is already full-length. if len(prefix) == n_const.DEVICE_NAME_MAX_LEN: return creation_func(prefix, *args, **kwargs) while True: name = utils.get_rand_name( max_length=n_const.DEVICE_NAME_MAX_LEN, prefix=prefix) try: return creation_func(name, *args, **kwargs) except RuntimeError: pass def no_skip_on_missing_deps(wrapped): """Do not allow a method/test to skip on missing dependencies. This decorator raises an error if a skip is raised by wrapped method when OS_FAIL_ON_MISSING_DEPS is evaluated to True. This decorator should be used only for missing dependencies (including missing system requirements). """ @functools.wraps(wrapped) def wrapper(*args, **kwargs): try: return wrapped(*args, **kwargs) except (testtools.TestCase.skipException, unittest2.case.SkipTest) as e: if base.bool_from_env('OS_FAIL_ON_MISSING_DEPS'): tools.fail( '%s cannot be skipped because OS_FAIL_ON_MISSING_DEPS ' 'is enabled, skip reason: %s' % (wrapped.__name__, e)) raise return wrapper neutron-12.1.1/neutron/tests/common/net_helpers.py0000664000175000017500000010254013553660047022331 0ustar zuulzuul00000000000000# Copyright (c) 2015 Thales Services SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import abc from concurrent import futures import contextlib import os import random import re import select import shlex import signal import subprocess import time import fixtures import netaddr from neutron_lib import constants as n_const from oslo_config import cfg from oslo_log import log as logging from oslo_utils import uuidutils import six from neutron.agent.common import ovs_lib from neutron.agent.linux import bridge_lib from neutron.agent.linux import interface from neutron.agent.linux import ip_lib from neutron.agent.linux import iptables_firewall from neutron.agent.linux import utils from neutron.common import utils as common_utils from neutron.conf.agent import common as config from neutron.db import db_base_plugin_common from neutron.plugins.ml2.drivers.linuxbridge.agent import \ linuxbridge_neutron_agent as linuxbridge_agent from neutron.tests.common import base as common_base from neutron.tests import tools LOG = logging.getLogger(__name__) UNDEFINED = object() NS_PREFIX = 'test-' BR_PREFIX = 'test-br' PORT_PREFIX = 'port' VETH0_PREFIX = 'test-veth0' VETH1_PREFIX = 'test-veth1' PATCH_PREFIX = 'patch' MACVTAP_PREFIX = 'macvtap' # port name should be shorter than DEVICE_NAME_MAX_LEN because if this # port is used to provide vlan connection between two linuxbridge # agents then place for vlan ID is also required, Vlan ID can take max 4 digits # and there is also additional "." in device name so it will in overall gives # DEVICE_NAME_MAX_LEN = 15 chars LB_DEVICE_NAME_MAX_LEN = 10 SS_SOURCE_PORT_PATTERN = re.compile( r'^.*\s+\d+\s+.*:(?P\d+)\s+[^\s]+:.*') READ_TIMEOUT = int( os.environ.get('OS_TEST_READ_TIMEOUT', 5)) CHILD_PROCESS_TIMEOUT = int( os.environ.get('OS_TEST_CHILD_PROCESS_TIMEOUT', 20)) CHILD_PROCESS_SLEEP = float( os.environ.get('OS_TEST_CHILD_PROCESS_SLEEP', 0.5)) TRANSPORT_PROTOCOLS = (n_const.PROTO_NAME_TCP, n_const.PROTO_NAME_UDP, n_const.PROTO_NAME_SCTP) OVS_MANAGER_TEST_PORT_FIRST = 6610 OVS_MANAGER_TEST_PORT_LAST = 6639 def increment_ip_cidr(ip_cidr, offset=1): """Increment ip_cidr offset times. example: increment_ip_cidr("1.2.3.4/24", 2) ==> "1.2.3.6/24" """ net0 = netaddr.IPNetwork(ip_cidr) net = netaddr.IPNetwork(ip_cidr) net.value += offset if not net0.network < net.ip < net0[-1]: tools.fail( 'Incorrect ip_cidr,offset tuple (%s,%s): "incremented" ip_cidr is ' 'outside ip_cidr' % (ip_cidr, offset)) return str(net) def set_namespace_gateway(port_dev, gateway_ip): """Set gateway for the namespace associated to the port.""" if not port_dev.namespace: tools.fail('tests should not change test machine gateway') port_dev.route.add_gateway(gateway_ip) def assert_ping(src_namespace, dst_ip, timeout=1, count=3): ipversion = netaddr.IPAddress(dst_ip).version ping_command = 'ping' if ipversion == 4 else 'ping6' ns_ip_wrapper = ip_lib.IPWrapper(src_namespace) ns_ip_wrapper.netns.execute([ping_command, '-c', count, '-W', timeout, dst_ip]) def assert_async_ping(src_namespace, dst_ip, timeout=1, count=1, interval=1): ipversion = netaddr.IPAddress(dst_ip).version ping_command = 'ping' if ipversion == 4 else 'ping6' ns_ip_wrapper = ip_lib.IPWrapper(src_namespace) # See bug 1588731 for explanation why using -c count ping option # cannot be used and it needs to be done using the following workaround. for _index in range(count): start_time = time.time() ns_ip_wrapper.netns.execute([ping_command, '-c', '1', '-W', timeout, dst_ip]) end_time = time.time() diff = end_time - start_time if 0 < diff < interval: # wait at most "interval" seconds between individual pings time.sleep(interval - diff) @contextlib.contextmanager def async_ping(namespace, ips, timeout=1, count=10): with futures.ThreadPoolExecutor(max_workers=len(ips)) as executor: fs = [executor.submit(assert_async_ping, namespace, ip, count=count, timeout=timeout) for ip in ips] yield lambda: all(f.done() for f in fs) futures.wait(fs) for f in fs: f.result() def assert_no_ping(src_namespace, dst_ip, timeout=1, count=1): try: assert_ping(src_namespace, dst_ip, timeout, count) except RuntimeError: pass else: tools.fail("destination ip %(destination)s is replying to ping from " "namespace %(ns)s, but it shouldn't" % {'ns': src_namespace, 'destination': dst_ip}) def assert_arping(src_namespace, dst_ip, source=None, timeout=1, count=1): """Send arp request using arping executable. NOTE: ARP protocol is used in IPv4 only. IPv6 uses Neighbour Discovery Protocol instead. """ ns_ip_wrapper = ip_lib.IPWrapper(src_namespace) arping_cmd = ['arping', '-c', count, '-w', timeout] if source: arping_cmd.extend(['-s', source]) arping_cmd.append(dst_ip) ns_ip_wrapper.netns.execute(arping_cmd) def assert_no_arping(src_namespace, dst_ip, source=None, timeout=1, count=1): try: assert_arping(src_namespace, dst_ip, source, timeout, count) except RuntimeError: pass else: tools.fail("destination ip %(destination)s is replying to arp from " "namespace %(ns)s, but it shouldn't" % {'ns': src_namespace, 'destination': dst_ip}) def _get_source_ports_from_ss_output(output): ports = set() for line in output.splitlines(): match = SS_SOURCE_PORT_PATTERN.match(line) if match: ports.add(int(match.group('port'))) return ports def get_unused_port(used, start=1024, end=None): if end is None: port_range = utils.execute( ['sysctl', '-n', 'net.ipv4.ip_local_port_range'], run_as_root=True) end = int(port_range.split()[0]) - 1 candidates = set(range(start, end + 1)) return random.choice(list(candidates - used)) def get_free_namespace_port(protocol, namespace=None, start=1024, end=None): """Return an unused port from given namespace WARNING: This function returns a port that is free at the execution time of this function. If this port is used later for binding then there is a potential danger that port will be no longer free. It's up to the programmer to handle error if port is already in use. :param protocol: Return free port for given protocol. Supported protocols are 'tcp' and 'udp'. :param namespace: Namespace in which free port has to be returned. :param start: The starting port number. :param end: The ending port number (free port that is returned would be between (start, end) values. """ if protocol == n_const.PROTO_NAME_TCP: param = '-tna' elif protocol == n_const.PROTO_NAME_UDP: param = '-una' else: raise ValueError("Unsupported protocol %s" % protocol) ip_wrapper = ip_lib.IPWrapper(namespace=namespace) output = ip_wrapper.netns.execute(['ss', param], run_as_root=True) used_ports = _get_source_ports_from_ss_output(output) return get_unused_port(used_ports, start, end) def create_patch_ports(source, destination): """Hook up two OVS bridges. The result is two patch ports, each end connected to a bridge. The two patch port names will start with 'patch-', followed by identical four characters. For example patch-xyzw-fedora, and patch-xyzw-ubuntu, where fedora and ubuntu are random strings. :param source: Instance of OVSBridge :param destination: Instance of OVSBridge """ common = common_utils.get_rand_name(max_length=4, prefix='') prefix = '%s-%s-' % (PATCH_PREFIX, common) source_name = common_utils.get_rand_device_name(prefix=prefix) destination_name = common_utils.get_rand_device_name(prefix=prefix) source.add_patch_port(source_name, destination_name) destination.add_patch_port(destination_name, source_name) def create_vlan_interface( namespace, port_name, mac_address, ip_address, vlan_tag): """Create a VLAN interface in namespace with IP address. :param namespace: Namespace in which VLAN interface should be created. :param port_name: Name of the port to which VLAN should be added. :param ip_address: IPNetwork instance containing the VLAN interface IP address. :param vlan_tag: VLAN tag for VLAN interface. """ ip_wrap = ip_lib.IPWrapper(namespace) dev_name = "%s.%d" % (port_name, vlan_tag) ip_wrap.add_vlan(dev_name, port_name, vlan_tag) dev = ip_wrap.device(dev_name) dev.addr.add(str(ip_address)) dev.link.set_address(mac_address) dev.link.set_up() return dev class RootHelperProcess(subprocess.Popen): def __init__(self, cmd, *args, **kwargs): for arg in ('stdin', 'stdout', 'stderr'): kwargs.setdefault(arg, subprocess.PIPE) kwargs.setdefault('universal_newlines', True) self.namespace = kwargs.pop('namespace', None) self.cmd = cmd if self.namespace is not None: cmd = ['ip', 'netns', 'exec', self.namespace] + cmd root_helper = config.get_root_helper(utils.cfg.CONF) cmd = shlex.split(root_helper) + cmd self.child_pid = None LOG.debug("Spawning process %s", cmd) super(RootHelperProcess, self).__init__(cmd, *args, **kwargs) self._wait_for_child_process() def kill(self, sig=signal.SIGKILL): pid = self.child_pid or str(self.pid) utils.execute(['kill', '-%d' % sig, pid], run_as_root=True) def read_stdout(self, timeout=None): return self._read_stream(self.stdout, timeout) @staticmethod def _read_stream(stream, timeout): if timeout: rready, _wready, _xready = select.select([stream], [], [], timeout) if not rready: raise RuntimeError('No output in %.2f seconds' % timeout) return stream.readline() def writeline(self, data): self.stdin.write(data + os.linesep) self.stdin.flush() def _wait_for_child_process(self, timeout=CHILD_PROCESS_TIMEOUT, sleep=CHILD_PROCESS_SLEEP): def child_is_running(): child_pid = utils.get_root_helper_child_pid( self.pid, self.cmd, run_as_root=True) if utils.pid_invoked_with_cmdline(child_pid, self.cmd): return True common_utils.wait_until_true( child_is_running, timeout, exception=RuntimeError("Process %s hasn't been spawned " "in %d seconds" % (self.cmd, timeout))) self.child_pid = utils.get_root_helper_child_pid( self.pid, self.cmd, run_as_root=True) @property def is_running(self): return self.poll() is None class Pinger(object): """Class for sending ICMP packets asynchronously The aim is to keep sending ICMP packets on background while executing other code. After background 'ping' command is stopped, statistics are available. Difference to assert_(no_)ping() functions located in this module is that these methods send given count of ICMP packets while they wait for the exit code of 'ping' command. >>> pinger = Pinger('pinger_test', '192.168.0.2') >>> pinger.start(); time.sleep(5); pinger.stop() >>> pinger.sent, pinger.received 7 7 """ stats_pattern = re.compile( r'^(?P\d+) packets transmitted,.*(?P\d+) received.*$') unreachable_pattern = re.compile( r'.* Destination .* Unreachable') TIMEOUT = 15 def __init__(self, namespace, address, count=None, timeout=1, interval=None): self.proc = None self.namespace = namespace self.address = address self.count = count self.timeout = timeout self.destination_unreachable = False self.sent = 0 self.received = 0 self.interval = interval def _wait_for_death(self): is_dead = lambda: self.proc.poll() is not None common_utils.wait_until_true( is_dead, timeout=self.TIMEOUT, exception=RuntimeError( "Ping command hasn't ended after %d seconds." % self.TIMEOUT)) def _parse_stats(self): for line in self.proc.stdout: if (not self.destination_unreachable and self.unreachable_pattern.match(line)): self.destination_unreachable = True continue result = self.stats_pattern.match(line) if result: self.sent = int(result.group('trans')) self.received = int(result.group('recv')) break else: raise RuntimeError("Didn't find ping statistics.") def start(self): if self.proc and self.proc.is_running: raise RuntimeError("This pinger has already a running process") ip_version = common_utils.get_ip_version(self.address) ping_exec = 'ping' if ip_version == 4 else 'ping6' cmd = [ping_exec, self.address, '-W', str(self.timeout)] if self.count: cmd.extend(['-c', str(self.count)]) if self.interval: cmd.extend(['-i', str(self.interval)]) self.proc = RootHelperProcess(cmd, namespace=self.namespace) def stop(self): if self.proc and self.proc.is_running: self.proc.kill(signal.SIGINT) self._wait_for_death() self._parse_stats() def wait(self): if self.count: self._wait_for_death() self._parse_stats() else: raise RuntimeError("Pinger is running infinitely, use stop() " "first") class NetcatTester(object): TCP = n_const.PROTO_NAME_TCP UDP = n_const.PROTO_NAME_UDP SCTP = n_const.PROTO_NAME_SCTP VERSION_TO_ALL_ADDRESS = { 4: '0.0.0.0', 6: '::', } def __init__(self, client_namespace, server_namespace, address, dst_port, protocol, server_address=None, src_port=None): """ Tool for testing connectivity on transport layer using netcat executable. The processes are spawned lazily. :param client_namespace: Namespace in which netcat process that connects to other netcat will be spawned :param server_namespace: Namespace in which listening netcat process will be spawned :param address: Server address from client point of view :param dst_port: Port on which netcat listens :param protocol: Transport protocol, either 'tcp', 'udp' or 'sctp' :param server_address: Address in server namespace on which netcat should listen :param src_port: Source port of netcat process spawned in client namespace - packet will have src_port in TCP/UDP header with this value """ self.client_namespace = client_namespace self.server_namespace = server_namespace self._client_process = None self._server_process = None self.address = address self.dst_port = str(dst_port) self.src_port = str(src_port) if src_port else None if protocol not in TRANSPORT_PROTOCOLS: raise ValueError("Unsupported protocol %s" % protocol) self.protocol = protocol ip_version = netaddr.IPAddress(address).version self.server_address = ( server_address or self.VERSION_TO_ALL_ADDRESS[ip_version]) @property def client_process(self): if not self._client_process: self.establish_connection() return self._client_process @property def server_process(self): if not self._server_process: self._spawn_server_process() return self._server_process def _spawn_server_process(self): self._server_process = self._spawn_nc_in_namespace( self.server_namespace, address=self.server_address, listen=True) @property def is_established(self): return bool(self._client_process and not self._client_process.poll()) def establish_connection(self): if self.is_established: raise RuntimeError('%(proto)s connection to %(ip_addr)s is already' ' established' % {'proto': self.protocol, 'ip_addr': self.address}) if not self._server_process: self._spawn_server_process() self._client_process = self._spawn_nc_in_namespace( self.client_namespace, address=self.address) if self.protocol == self.UDP: # Create an ASSURED entry in conntrack table for UDP packets, # that requires 3-way communication # 1st transmission creates UNREPLIED # 2nd transmission removes UNREPLIED # 3rd transmission creates ASSURED data = 'foo' self.client_process.writeline(data) self.server_process.read_stdout(READ_TIMEOUT) self.server_process.writeline(data) self.client_process.read_stdout(READ_TIMEOUT) self.client_process.writeline(data) self.server_process.read_stdout(READ_TIMEOUT) def test_connectivity(self, respawn=False): testing_string = uuidutils.generate_uuid() if respawn: self.stop_processes() self.client_process.writeline(testing_string) message = self.server_process.read_stdout(READ_TIMEOUT).strip() self.server_process.writeline(message) message = self.client_process.read_stdout(READ_TIMEOUT).strip() return message == testing_string def test_no_connectivity(self, respawn=False): try: return not self.test_connectivity(respawn) except RuntimeError: return True def _spawn_nc_in_namespace(self, namespace, address, listen=False): cmd = ['ncat', address, self.dst_port] if self.protocol == self.UDP: cmd.append('-u') elif self.protocol == self.SCTP: cmd.append('--sctp') if listen: cmd.append('-l') if self.protocol in (self.TCP, self.SCTP): cmd.append('-k') else: cmd.extend(['-w', '20']) if self.src_port: cmd.extend(['-p', self.src_port]) proc = RootHelperProcess(cmd, namespace=namespace) return proc def stop_processes(self): for proc_attr in ('_client_process', '_server_process'): proc = getattr(self, proc_attr) if proc: if proc.poll() is None: proc.kill() proc.wait() setattr(self, proc_attr, None) class NamespaceFixture(fixtures.Fixture): """Create a namespace. :ivar ip_wrapper: created namespace :type ip_wrapper: IPWrapper :ivar name: created namespace name :type name: str """ def __init__(self, prefix=NS_PREFIX): super(NamespaceFixture, self).__init__() self.prefix = prefix def _setUp(self): ip = ip_lib.IPWrapper() self.name = self.prefix + uuidutils.generate_uuid() self.ip_wrapper = ip.ensure_namespace(self.name) self.addCleanup(self.destroy) def destroy(self): if self.ip_wrapper.netns.exists(self.name): self.ip_wrapper.netns.delete(self.name) class VethFixture(fixtures.Fixture): """Create a veth. :ivar ports: created veth ports :type ports: tuple of 2 IPDevice """ def _setUp(self): ip_wrapper = ip_lib.IPWrapper() self.ports = common_base.create_resource( VETH0_PREFIX, lambda name: ip_wrapper.add_veth(name, self.get_peer_name(name))) self.addCleanup(self.destroy) def destroy(self): for port in self.ports: ip_wrapper = ip_lib.IPWrapper(port.namespace) if (ip_wrapper.netns.exists(port.namespace) or port.namespace is None): try: ip_wrapper.del_veth(port.name) break except RuntimeError: # NOTE(cbrandily): It seems a veth is automagically deleted # when a namespace owning a veth endpoint is deleted. pass @staticmethod def get_peer_name(name): if name.startswith(VETH0_PREFIX): return name.replace(VETH0_PREFIX, VETH1_PREFIX) elif name.startswith(VETH1_PREFIX): return name.replace(VETH1_PREFIX, VETH0_PREFIX) else: tools.fail('%s is not a valid VethFixture veth endpoint' % name) class NamedVethFixture(VethFixture): """Create a veth with at least one specified name of a device :ivar ports: created veth ports :type ports: tuple of 2 IPDevice """ def __init__(self, veth0_prefix=VETH0_PREFIX, veth1_prefix=VETH1_PREFIX): super(NamedVethFixture, self).__init__() self.veth0_name = self.get_veth_name(veth0_prefix) self.veth1_name = self.get_veth_name(veth1_prefix) def _setUp(self): ip_wrapper = ip_lib.IPWrapper() self.ports = ip_wrapper.add_veth(self.veth0_name, self.veth1_name) self.addCleanup(self.destroy) @staticmethod def get_veth_name(name): if name.startswith(VETH0_PREFIX): return common_utils.get_rand_device_name(VETH0_PREFIX) if name.startswith(VETH1_PREFIX): return common_utils.get_rand_device_name(VETH1_PREFIX) return name class MacvtapFixture(fixtures.Fixture): """Create a macvtap. :param src_dev: source device for macvtap :type src_dev: IPDevice :param mode: mode of macvtap :type mode: string :ivar ip_dev: created macvtap :type ip_dev: IPDevice """ def __init__(self, src_dev=None, mode=None, prefix=MACVTAP_PREFIX): super(MacvtapFixture, self).__init__() self.src_dev = src_dev self.mode = mode self.prefix = prefix def _setUp(self): ip_wrapper = ip_lib.IPWrapper() self.ip_dev = common_base.create_resource( self.prefix, ip_wrapper.add_macvtap, self.src_dev, mode=self.mode) self.addCleanup(self.destroy) def destroy(self): if (ip_lib.network_namespace_exists(self.ip_dev.namespace) or self.ip_dev.namespace is None): try: self.ip_dev.link.delete() except RuntimeError: pass @six.add_metaclass(abc.ABCMeta) class PortFixture(fixtures.Fixture): """Create a port. :ivar port: created port :type port: IPDevice :ivar bridge: port bridge """ def __init__(self, bridge=None, namespace=None, mac=None, port_id=None): super(PortFixture, self).__init__() self.bridge = bridge self.namespace = namespace self.mac = ( mac or db_base_plugin_common.DbBasePluginCommon._generate_mac()) self.port_id = port_id or uuidutils.generate_uuid() @abc.abstractmethod def _create_bridge_fixture(self): pass @abc.abstractmethod def _setUp(self): super(PortFixture, self)._setUp() if not self.bridge: self.bridge = self.useFixture(self._create_bridge_fixture()).bridge @classmethod def get(cls, bridge, namespace=None, mac=None, port_id=None, hybrid_plug=False): """Deduce PortFixture class from bridge type and instantiate it.""" if isinstance(bridge, ovs_lib.OVSBridge): return OVSPortFixture(bridge, namespace, mac, port_id, hybrid_plug) if isinstance(bridge, bridge_lib.BridgeDevice): return LinuxBridgePortFixture(bridge, namespace, mac, port_id) if isinstance(bridge, VethBridge): return VethPortFixture(bridge, namespace) tools.fail('Unexpected bridge type: %s' % type(bridge)) class OVSBridgeFixture(fixtures.Fixture): """Create an OVS bridge. :ivar prefix: bridge name prefix :type prefix: str :ivar bridge: created bridge :type bridge: OVSBridge """ def __init__(self, prefix=BR_PREFIX): super(OVSBridgeFixture, self).__init__() self.prefix = prefix def _setUp(self): ovs = ovs_lib.BaseOVS() self.bridge = common_base.create_resource(self.prefix, ovs.add_bridge) self.addCleanup(self.bridge.destroy) class OVSTrunkBridgeFixture(OVSBridgeFixture): """This bridge doesn't generate the name.""" def _setUp(self): ovs = ovs_lib.BaseOVS() self.bridge = ovs.add_bridge(self.prefix) self.addCleanup(self.bridge.destroy) class OVSPortFixture(PortFixture): NIC_NAME_LEN = 14 def __init__(self, bridge=None, namespace=None, mac=None, port_id=None, hybrid_plug=False): super(OVSPortFixture, self).__init__(bridge, namespace, mac, port_id) self.hybrid_plug = hybrid_plug self.vlan_tag = None def _create_bridge_fixture(self): return OVSBridgeFixture() def _setUp(self): super(OVSPortFixture, self)._setUp() # because in some tests this port can be used to providing connection # between linuxbridge agents and vlan_id can be also added to this # device name it has to be max LB_DEVICE_NAME_MAX_LEN long port_name = common_utils.get_rand_name( LB_DEVICE_NAME_MAX_LEN, PORT_PREFIX ) if self.hybrid_plug: self.hybrid_plug_port(port_name) else: self.plug_port(port_name) def plug_port(self, port_name): # TODO(jlibosva): Don't use interface driver for fullstack fake # machines as the port should be treated by OVS agent and not by # external party interface_config = cfg.ConfigOpts() config.register_interface_opts(interface_config) ovs_interface = interface.OVSInterfaceDriver(interface_config) ovs_interface.plug_new( None, self.port_id, port_name, self.mac, bridge=self.bridge.br_name, namespace=self.namespace) # NOTE(mangelajo): for OVS implementations remove the DEAD VLAN tag # on ports that we intend to use as fake vm interfaces, they # need to be flat. This is related to lp#1767422 self.bridge.clear_db_attribute("Port", port_name, "tag") self.addCleanup(self.bridge.delete_port, port_name) self.port = ip_lib.IPDevice(port_name, self.namespace) def hybrid_plug_port(self, port_name): """Plug port with linux bridge in the middle. """ ip_wrapper = ip_lib.IPWrapper(self.namespace) qvb_name, qvo_name = self._get_veth_pair_names(self.port_id) qvb, qvo = self.useFixture(NamedVethFixture(qvb_name, qvo_name)).ports qvb.link.set_up() qvo.link.set_up() qbr_name = self._get_br_name(self.port_id) self.qbr = self.useFixture( LinuxBridgeFixture(qbr_name, namespace=None, prefix_is_full_name=True)).bridge self.qbr.link.set_up() self.qbr.setfd(0) self.qbr.disable_stp() self.qbr.addif(qvb_name) qvo_attrs = ('external_ids', {'iface-id': self.port_id, 'iface-status': 'active', 'attached-mac': self.mac}) self.bridge.add_port(qvo_name, qvo_attrs) # NOTE(jlibosva): Create fake vm port, instead of tap device, we use # veth pair here in order to be able to attach it to linux bridge in # root namespace. Name with tap is in root namespace and its peer is in # the namespace hybrid_port_name = iptables_firewall.get_hybrid_port_name(self.port_id) bridge_port, self.port = self.useFixture( NamedVethFixture(hybrid_port_name)).ports self.addCleanup(self.port.link.delete) ip_wrapper.add_device_to_namespace(self.port) bridge_port.link.set_up() self.qbr.addif(bridge_port) self.port.link.set_address(self.mac) self.port.link.set_up() # NOTE(jlibosva): Methods below are taken from nova.virt.libvirt.vif def _get_br_name(self, iface_id): return ("qbr" + iface_id)[:self.NIC_NAME_LEN] def _get_veth_pair_names(self, iface_id): return (("qvb%s" % iface_id)[:self.NIC_NAME_LEN], ("qvo%s" % iface_id)[:self.NIC_NAME_LEN]) class LinuxBridgeFixture(fixtures.Fixture): """Create a linux bridge. :ivar bridge: created bridge :type bridge: BridgeDevice :ivar namespace: created bridge namespace :type namespace: str """ def __init__(self, prefix=BR_PREFIX, namespace=UNDEFINED, prefix_is_full_name=False): super(LinuxBridgeFixture, self).__init__() self.prefix = prefix self.prefix_is_full_name = prefix_is_full_name self.namespace = namespace def _setUp(self): if self.namespace is UNDEFINED: self.namespace = self.useFixture(NamespaceFixture()).name self.bridge = self._create_bridge() self.addCleanup(self.safe_delete) self.bridge.link.set_up() self.addCleanup(self.safe_set_down) def safe_set_down(self): try: self.bridge.link.set_down() except RuntimeError: pass def safe_delete(self): try: self.bridge.delbr() except RuntimeError: pass def _create_bridge(self): if self.prefix_is_full_name: return bridge_lib.BridgeDevice.addbr( name=self.prefix, namespace=self.namespace ) else: return common_base.create_resource( self.prefix, bridge_lib.BridgeDevice.addbr, namespace=self.namespace) class LinuxBridgePortFixture(PortFixture): """Create a linux bridge port. :ivar port: created port :type port: IPDevice :ivar br_port: bridge side veth peer port :type br_port: IPDevice """ def __init__(self, bridge, namespace=None, mac=None, port_id=None): super(LinuxBridgePortFixture, self).__init__( bridge, namespace, mac, port_id) # we need to override port_id value here because in Port() class it is # always generated as random. In LinuxBridgePortFixture we need to have # it empty if it was not give because then proper veth_pair will be # created (for example in some functional tests) self.port_id = port_id def _create_bridge_fixture(self): return LinuxBridgeFixture() def _setUp(self): super(LinuxBridgePortFixture, self)._setUp() br_port_name = self._get_port_name() if br_port_name: self.veth_fixture = self.useFixture( NamedVethFixture(veth0_prefix=br_port_name)) else: self.veth_fixture = self.useFixture(VethFixture()) self.br_port, self.port = self.veth_fixture.ports if self.mac: self.port.link.set_address(self.mac) # bridge side br_ip_wrapper = ip_lib.IPWrapper(self.bridge.namespace) br_ip_wrapper.add_device_to_namespace(self.br_port) self.bridge.addif(self.br_port) self.br_port.link.set_up() # port side ns_ip_wrapper = ip_lib.IPWrapper(self.namespace) ns_ip_wrapper.add_device_to_namespace(self.port) self.port.link.set_up() def _get_port_name(self): if self.port_id: return linuxbridge_agent.LinuxBridgeManager.get_tap_device_name( self.port_id) return None class VethBridge(object): def __init__(self, ports): self.ports = ports self.unallocated_ports = list(self.ports) def allocate_port(self): try: return self.unallocated_ports.pop() except IndexError: tools.fail('All FakeBridge ports (%s) are already allocated.' % len(self.ports)) class VethBridgeFixture(fixtures.Fixture): """Simulate a bridge with a veth. :ivar bridge: created bridge :type bridge: FakeBridge """ def _setUp(self): ports = self.useFixture(VethFixture()).ports self.bridge = VethBridge(ports) class VethPortFixture(PortFixture): """Create a veth bridge port. :ivar port: created port :type port: IPDevice """ def _create_bridge_fixture(self): return VethBridgeFixture() def _setUp(self): super(VethPortFixture, self)._setUp() self.port = self.bridge.allocate_port() ns_ip_wrapper = ip_lib.IPWrapper(self.namespace) ns_ip_wrapper.add_device_to_namespace(self.port) self.port.link.set_up() neutron-12.1.1/neutron/tests/common/exclusive_resources/0000775000175000017500000000000013553660156023547 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/common/exclusive_resources/resource_allocator.py0000664000175000017500000001122013553660046030002 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import fixtures from neutron_lib.utils import runtime from oslo_log import log as logging from oslo_utils import fileutils LOG = logging.getLogger(__name__) MAX_ATTEMPTS = 100 TMP_DIR = '/tmp/neutron_exclusive_resources/' class ExclusiveResource(fixtures.Fixture): def __init__(self, resource_name, allocator_function, validator=None): self.ra = ResourceAllocator( resource_name, allocator_function, validator) def _setUp(self): self.resource = self.ra.allocate() self.addCleanup(self.ra.release, self.resource) class ResourceAllocator(object): """ResourceAllocator persists cross-process allocations of a resource. Allocations are persisted to a file determined by the 'resource_name', and are allocated via an allocator_function. The public interface (allocate and release) are guarded by a file lock. The intention is to allow atomic, cross-process allocation of shared resources such as ports and IP addresses. For usages of this class, please see ExclusiveIPAddress and its functional tests. Note that this class doesn't maintain in-memory state, and multiple instances of it may be initialized and used. A pool of resources is identified solely by the 'resource_name' argument. """ def __init__(self, resource_name, allocator_function, validator=None): """Initialize a resource allocator. :param resource_name: A unique identifier for a pool of resources. :param allocator_function: A function with no parameters that generates a resource. :param validator: An optional function that accepts a resource and an existing pool and returns if the generated resource is valid. """ def is_valid(new_resource, allocated_resources): return new_resource not in allocated_resources self._allocator_function = allocator_function self._state_file_path = os.path.join(TMP_DIR, resource_name) self._validator = validator if validator else is_valid self._resource_name = resource_name @runtime.synchronized('resource_allocator', external=True, lock_path='/tmp') def allocate(self): allocations = self._get_allocations() for i in range(MAX_ATTEMPTS): resource = str(self._allocator_function()) if self._validator(resource, allocations): allocations.add(resource) self._write_allocations(allocations) LOG.debug('Allocated exclusive resource %s of type %s. ' 'The allocations are now: %s', resource, self._resource_name, allocations) return resource raise ValueError( 'Could not allocate a new resource of type %s from pool %s' % (self._resource_name, allocations)) @runtime.synchronized('resource_allocator', external=True, lock_path='/tmp') def release(self, resource): allocations = self._get_allocations() allocations.remove(resource) if allocations: self._write_allocations(allocations) else: # Clean up the file if we're releasing the last allocation os.remove(self._state_file_path) LOG.debug('Released exclusive resource %s of type %s. The allocations ' 'are now: %s', resource, self._resource_name, allocations) def _get_allocations(self): fileutils.ensure_tree(TMP_DIR, mode=0o755) try: with open(self._state_file_path, 'r') as allocations_file: contents = allocations_file.read() except IOError: contents = None # If the file was empty, we want to return an empty set, not {''} return set(contents.split(',')) if contents else set() def _write_allocations(self, allocations): with open(self._state_file_path, 'w') as allocations_file: allocations_file.write(','.join(allocations)) neutron-12.1.1/neutron/tests/common/exclusive_resources/port.py0000664000175000017500000000236713553660046025113 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools from neutron.tests.common.exclusive_resources import resource_allocator from neutron.tests.common import net_helpers class ExclusivePort(resource_allocator.ExclusiveResource): """Allocate a unique port for a specific protocol. :ivar port: allocated port :type port: int """ def __init__(self, protocol, start=1024, end=None): super(ExclusivePort, self).__init__( 'ports', functools.partial(net_helpers.get_free_namespace_port, protocol, start=start, end=end)) def _setUp(self): super(ExclusivePort, self)._setUp() self.port = self.resource neutron-12.1.1/neutron/tests/common/exclusive_resources/__init__.py0000664000175000017500000000000013553660046025644 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/common/exclusive_resources/ip_address.py0000664000175000017500000000355113553660046026240 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import random import netaddr from neutron.tests.common.exclusive_resources import resource_allocator TEST_NET_RANGE = { 1: ('192.0.2.1', '192.0.2.254'), 2: ('198.51.100.1', '198.51.100.254'), 3: ('203.0.113.1', '203.0.113.254'), } def get_test_net_address_fixture(test_net_number): """Return exclusive ip address on the system based on RFC 5737. :param block: One of following constants: 1, 2, 3 https://tools.ietf.org/html/rfc5737 """ try: net_range = TEST_NET_RANGE[test_net_number] except KeyError: raise ValueError("Unknown constant for TEST-NET: %d" % test_net_number) return ExclusiveIPAddress(*net_range) def get_random_ip(low, high): parent_range = netaddr.IPRange(low, high) return str(random.choice(parent_range)) class ExclusiveIPAddress(resource_allocator.ExclusiveResource): """Allocate a unique ip address. :ivar address: allocated ip address :type address: netaddr.IPAddress """ def __init__(self, low, high): super(ExclusiveIPAddress, self).__init__( 'ip_addresses', functools.partial(get_random_ip, low, high)) def _setUp(self): super(ExclusiveIPAddress, self)._setUp() self.address = netaddr.IPAddress(self.resource) neutron-12.1.1/neutron/tests/common/exclusive_resources/ip_network.py0000664000175000017500000000324713553660046026306 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import netaddr from neutron.tests.common.exclusive_resources import ip_address from neutron.tests.common.exclusive_resources import resource_allocator def _get_random_network(low, high, netmask): ip = ip_address.get_random_ip(low, high) return str(netaddr.IPNetwork("%s/%s" % (ip, netmask)).cidr) class ExclusiveIPNetwork(resource_allocator.ExclusiveResource): """Allocate a non-overlapping ip network. :ivar network: allocated ip network :type network: netaddr.IPNetwork """ def __init__(self, low, high, netmask): super(ExclusiveIPNetwork, self).__init__( 'ip_networks', functools.partial(_get_random_network, low, high, netmask), self.is_valid) def _setUp(self): super(ExclusiveIPNetwork, self)._setUp() self.network = netaddr.IPNetwork(self.resource) def is_valid(self, new_resource, allocated_resources): new_ipset = netaddr.IPSet([new_resource]) allocated_ipset = netaddr.IPSet(allocated_resources) return new_ipset.isdisjoint(allocated_ipset) neutron-12.1.1/neutron/tests/common/helpers.py0000664000175000017500000002125713553660047021470 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime from distutils import version import functools import os import random from neutron_lib import constants from neutron_lib import context from oslo_utils import timeutils import six import testtools import neutron from neutron.agent.common import ovs_lib from neutron.common import topics from neutron.db import agents_db from neutron.db import common_db_mixin HOST = 'localhost' DEFAULT_AZ = 'nova' def find_file(filename, path): """Find a file with name 'filename' located in 'path'.""" for root, _, files in os.walk(path): if filename in files: return os.path.abspath(os.path.join(root, filename)) def find_sample_file(filename): """Find a file with name 'filename' located in the sample directory.""" return find_file( filename, path=os.path.join(neutron.__path__[0], '..', 'etc')) def get_test_log_path(): return os.environ.get('OS_LOG_PATH', '/tmp') class FakePlugin(common_db_mixin.CommonDbMixin, agents_db.AgentDbMixin): pass def _get_l3_agent_dict(host, agent_mode, internal_only=True, ext_net_id='', ext_bridge='', az=DEFAULT_AZ): return { 'agent_type': constants.AGENT_TYPE_L3, 'binary': 'neutron-l3-agent', 'host': host, 'topic': topics.L3_AGENT, 'availability_zone': az, 'configurations': {'agent_mode': agent_mode, 'handle_internal_only_routers': internal_only, 'external_network_bridge': ext_bridge, 'gateway_external_network_id': ext_net_id}} def _register_agent(agent, plugin=None): if not plugin: plugin = FakePlugin() admin_context = context.get_admin_context() plugin.create_or_update_agent(admin_context, agent) return plugin._get_agent_by_type_and_host( admin_context, agent['agent_type'], agent['host']) def register_l3_agent(host=HOST, agent_mode=constants.L3_AGENT_MODE_LEGACY, internal_only=True, ext_net_id='', ext_bridge='', az=DEFAULT_AZ): agent = _get_l3_agent_dict(host, agent_mode, internal_only, ext_net_id, ext_bridge, az) return _register_agent(agent) def _get_dhcp_agent_dict(host, networks=0, az=DEFAULT_AZ): agent = { 'binary': 'neutron-dhcp-agent', 'host': host, 'topic': topics.DHCP_AGENT, 'agent_type': constants.AGENT_TYPE_DHCP, 'availability_zone': az, 'configurations': {'dhcp_driver': 'dhcp_driver', 'networks': networks}} return agent def register_dhcp_agent(host=HOST, networks=0, admin_state_up=True, alive=True, az=DEFAULT_AZ): agent = _register_agent( _get_dhcp_agent_dict(host, networks, az=az)) if not admin_state_up: set_agent_admin_state(agent['id']) if not alive: kill_agent(agent['id']) return FakePlugin()._get_agent_by_type_and_host( context.get_admin_context(), agent['agent_type'], agent['host']) def kill_agent(agent_id): hour_ago = timeutils.utcnow() - datetime.timedelta(hours=1) FakePlugin().update_agent( context.get_admin_context(), agent_id, {'agent': { 'started_at': hour_ago, 'heartbeat_timestamp': hour_ago}}) def revive_agent(agent_id): now = timeutils.utcnow() FakePlugin().update_agent( context.get_admin_context(), agent_id, {'agent': {'started_at': now, 'heartbeat_timestamp': now}}) def set_agent_admin_state(agent_id, admin_state_up=False): FakePlugin().update_agent( context.get_admin_context(), agent_id, {'agent': {'admin_state_up': admin_state_up}}) def _get_l2_agent_dict(host, agent_type, binary, tunnel_types=None, tunneling_ip='20.0.0.1', interface_mappings=None, bridge_mappings=None, l2pop_network_types=None, device_mappings=None, start_flag=True): agent = { 'binary': binary, 'host': host, 'topic': constants.L2_AGENT_TOPIC, 'configurations': {}, 'agent_type': agent_type, 'tunnel_type': [], 'start_flag': start_flag} if tunnel_types is not None: agent['configurations']['tunneling_ip'] = tunneling_ip agent['configurations']['tunnel_types'] = tunnel_types if bridge_mappings is not None: agent['configurations']['bridge_mappings'] = bridge_mappings if interface_mappings is not None: agent['configurations']['interface_mappings'] = interface_mappings if l2pop_network_types is not None: agent['configurations']['l2pop_network_types'] = l2pop_network_types if device_mappings is not None: agent['configurations']['device_mappings'] = device_mappings return agent def register_ovs_agent(host=HOST, agent_type=constants.AGENT_TYPE_OVS, binary='neutron-openvswitch-agent', tunnel_types=['vxlan'], tunneling_ip='20.0.0.1', interface_mappings=None, bridge_mappings=None, l2pop_network_types=None, plugin=None, start_flag=True): agent = _get_l2_agent_dict(host, agent_type, binary, tunnel_types, tunneling_ip, interface_mappings, bridge_mappings, l2pop_network_types, start_flag=start_flag) return _register_agent(agent, plugin) def register_linuxbridge_agent(host=HOST, agent_type=constants.AGENT_TYPE_LINUXBRIDGE, binary='neutron-linuxbridge-agent', tunnel_types=['vxlan'], tunneling_ip='20.0.0.1', interface_mappings=None, bridge_mappings=None, plugin=None): agent = _get_l2_agent_dict(host, agent_type, binary, tunnel_types, tunneling_ip=tunneling_ip, interface_mappings=interface_mappings, bridge_mappings=bridge_mappings) return _register_agent(agent, plugin) def register_macvtap_agent(host=HOST, agent_type=constants.AGENT_TYPE_MACVTAP, binary='neutron-macvtap-agent', interface_mappings=None, plugin=None): agent = _get_l2_agent_dict(host, agent_type, binary, interface_mappings=interface_mappings) return _register_agent(agent, plugin) def register_sriovnicswitch_agent(host=HOST, agent_type=constants.AGENT_TYPE_NIC_SWITCH, binary='neutron-sriov-nic-agent', device_mappings=None, plugin=None): agent = _get_l2_agent_dict(host, agent_type, binary, device_mappings=device_mappings) return _register_agent(agent, plugin) def requires_py2(testcase): return testtools.skipUnless(six.PY2, "requires python 2.x")(testcase) def requires_py3(testcase): return testtools.skipUnless(six.PY3, "requires python 3.x")(testcase) def get_not_used_vlan(bridge, vlan_range): port_vlans = bridge.ovsdb.db_find( 'Port', ('tag', '!=', []), columns=['tag']).execute() used_vlan_tags = {val['tag'] for val in port_vlans} available_vlans = vlan_range - used_vlan_tags return random.choice(list(available_vlans)) def skip_if_ovs_older_than(ovs_version): """Decorator for test method to skip if OVS version doesn't meet minimal requirement. """ def skip_if_bad_ovs(f): @functools.wraps(f) def check_ovs_and_skip(test): ovs = ovs_lib.BaseOVS() current_ovs_version = version.StrictVersion( ovs.config['ovs_version']) if current_ovs_version < version.StrictVersion(ovs_version): test.skip("This test requires OVS version %s or higher." % ovs_version) return f(test) return check_ovs_and_skip return skip_if_bad_ovs neutron-12.1.1/neutron/tests/common/machine_fixtures.py0000664000175000017500000001232613553660046023357 0ustar zuulzuul00000000000000# Copyright (c) 2015 Thales Services SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import functools import fixtures from neutron.agent.linux import ip_lib from neutron.common import utils from neutron.tests.common import net_helpers class FakeMachineException(Exception): pass class FakeMachineBase(fixtures.Fixture): """Create a fake machine. :ivar bridge: bridge on which the fake machine is bound :ivar ip_cidr: fake machine ip_cidr :type ip_cidr: str :ivar ip: fake machine ip :type ip: str :ivar gateway_ip: fake machine gateway ip :type gateway_ip: str :ivar namespace: namespace emulating the machine :type namespace: str :ivar port: port binding the namespace to the bridge :type port: IPDevice """ def __init__(self): self.port = None def _setUp(self): ns_fixture = self.useFixture( net_helpers.NamespaceFixture()) self.namespace = ns_fixture.name def execute(self, *args, **kwargs): ns_ip_wrapper = ip_lib.IPWrapper(self.namespace) return ns_ip_wrapper.netns.execute(*args, **kwargs) def ping_predicate(self, dst_ip): try: self.assert_ping(dst_ip) except RuntimeError: return False return True def block_until_ping(self, dst_ip): predicate = functools.partial(self.ping_predicate, dst_ip) utils.wait_until_true( predicate, exception=FakeMachineException( "No ICMP reply obtained from IP address %s" % dst_ip) ) def block_until_no_ping(self, dst_ip): predicate = functools.partial( lambda ip: not self.ping_predicate(ip), dst_ip) utils.wait_until_true( predicate, exception=FakeMachineException( "ICMP packets still pass to %s IP address." % dst_ip) ) def assert_ping(self, dst_ip): net_helpers.assert_ping(self.namespace, dst_ip) def assert_no_ping(self, dst_ip): net_helpers.assert_no_ping(self.namespace, dst_ip) @property def ip(self): raise NotImplementedError() @property def ip_cidr(self): raise NotImplementedError() @property def mac_address(self): return self.port.link.address class FakeMachine(FakeMachineBase): def __init__(self, bridge, ip_cidr, gateway_ip=None, ipv6_cidr=None): super(FakeMachine, self).__init__() self.bridge = bridge self._ip_cidr = ip_cidr self._ipv6_cidr = ipv6_cidr self.gateway_ip = gateway_ip def _setUp(self): super(FakeMachine, self)._setUp() self.port = self.useFixture( net_helpers.PortFixture.get(self.bridge, self.namespace)).port self.port.addr.add(self._ip_cidr) if self.gateway_ip: net_helpers.set_namespace_gateway(self.port, self.gateway_ip) @property def ip(self): return self._ip_cidr.partition('/')[0] @property def ip_cidr(self): return self._ip_cidr @ip_cidr.setter def ip_cidr(self, ip_cidr): self.port.addr.add(ip_cidr) self.port.addr.delete(self._ip_cidr) self._ip_cidr = ip_cidr @property def ipv6(self): return self._ipv6_cidr.partition('/')[0] @property def ipv6_cidr(self): return self._ipv6_cidr @ipv6_cidr.setter def ipv6_cidr(self, ipv6_cidr): if self._ipv6_cidr: self.port.addr.delete(self._ipv6_cidr) self.port.addr.add(ipv6_cidr) self._ipv6_cidr = ipv6_cidr @FakeMachineBase.mac_address.setter def mac_address(self, mac_address): self.port.link.set_down() self.port.link.set_address(mac_address) self.port.link.set_up() def set_default_gateway(self, default_gw): self.port.route.add_gateway(default_gw) class PeerMachines(fixtures.Fixture): """Create 'amount' peered machines on an ip_cidr. :ivar bridge: bridge on which peer machines are bound :ivar ip_cidr: ip_cidr on which peer machines have ips :type ip_cidr: str :ivar machines: fake machines :type machines: FakeMachine list """ CIDR = '192.168.0.1/24' def __init__(self, bridge, ip_cidr=None, gateway_ip=None, amount=2): super(PeerMachines, self).__init__() self.bridge = bridge self.ip_cidr = ip_cidr or self.CIDR self.gateway_ip = gateway_ip self.amount = amount def _setUp(self): self.machines = [] for index in range(self.amount): ip_cidr = net_helpers.increment_ip_cidr(self.ip_cidr, index) self.machines.append( self.useFixture( FakeMachine(self.bridge, ip_cidr, self.gateway_ip))) neutron-12.1.1/neutron/tests/common/config_fixtures.py0000664000175000017500000000467413553660047023230 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os.path import fixtures import six from neutron.tests import base class ConfigDict(base.AttributeDict): def update(self, other): self.convert_to_attr_dict(other) super(ConfigDict, self).update(other) def convert_to_attr_dict(self, other): """Convert nested dicts to AttributeDict. :param other: dictionary to be directly modified. """ for key, value in other.items(): if isinstance(value, dict): if not isinstance(value, base.AttributeDict): other[key] = base.AttributeDict(value) self.convert_to_attr_dict(value) class ConfigFileFixture(fixtures.Fixture): """A fixture that knows how to translate configurations to files. :param base_filename: the filename to use on disk. :param config: a ConfigDict instance. :param temp_dir: an existing temporary directory to use for storage. """ def __init__(self, base_filename, config, temp_dir): super(ConfigFileFixture, self).__init__() self.base_filename = base_filename self.config = config self.temp_dir = temp_dir def _setUp(self): config_parser = self.dict_to_config_parser(self.config) # Need to randomly generate a unique folder to put the file in self.filename = os.path.join(self.temp_dir, self.base_filename) with open(self.filename, 'w') as f: config_parser.write(f) f.flush() def dict_to_config_parser(self, config_dict): config_parser = six.moves.configparser.ConfigParser() for section, section_dict in config_dict.items(): if section != 'DEFAULT': config_parser.add_section(section) for option, value in section_dict.items(): config_parser.set(section, option, value) return config_parser neutron-12.1.1/neutron/tests/common/__init__.py0000664000175000017500000000115413553660046021556 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.common import eventlet_utils eventlet_utils.monkey_patch() neutron-12.1.1/neutron/tests/post_mortem_debug.py0000664000175000017500000001021513553660046022243 0ustar zuulzuul00000000000000# Copyright 2013 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import traceback def get_exception_handler(debugger_name): debugger = _get_debugger(debugger_name) return functools.partial(_exception_handler, debugger) def _get_debugger(debugger_name): try: debugger = __import__(debugger_name) except ImportError: raise ValueError("can't import %s module as a post mortem debugger" % debugger_name) if 'post_mortem' in dir(debugger): return debugger else: raise ValueError("%s is not a supported post mortem debugger" % debugger_name) def _exception_handler(debugger, exc_info): """Exception handler enabling post-mortem debugging. A class extending testtools.TestCase can add this handler in setUp(): self.addOnException(post_mortem_debug.exception_handler) When an exception occurs, the user will be dropped into a debugger session in the execution environment of the failure. Frames associated with the testing framework are excluded so that the post-mortem session for an assertion failure will start at the assertion call (e.g. self.assertTrue) rather than the framework code that raises the failure exception (e.g. the assertTrue method). """ tb = exc_info[2] ignored_traceback = get_ignored_traceback(tb) if ignored_traceback: tb = FilteredTraceback(tb, ignored_traceback) traceback.print_exception(exc_info[0], exc_info[1], tb) debugger.post_mortem(tb) def get_ignored_traceback(tb): """Retrieve the first traceback of an ignored trailing chain. Given an initial traceback, find the first traceback of a trailing chain of tracebacks that should be ignored. The criteria for whether a traceback should be ignored is whether its frame's globals include the __unittest marker variable. This criteria is culled from: unittest.TestResult._is_relevant_tb_level For example: tb.tb_next => tb0.tb_next => tb1.tb_next - If no tracebacks were to be ignored, None would be returned. - If only tb1 was to be ignored, tb1 would be returned. - If tb0 and tb1 were to be ignored, tb0 would be returned. - If either of only tb or only tb0 was to be ignored, None would be returned because neither tb or tb0 would be part of a trailing chain of ignored tracebacks. """ # Turn the traceback chain into a list tb_list = [] while tb: tb_list.append(tb) tb = tb.tb_next # Find all members of an ignored trailing chain ignored_tracebacks = [] for tb in reversed(tb_list): if '__unittest' in tb.tb_frame.f_globals: ignored_tracebacks.append(tb) else: break # Return the first member of the ignored trailing chain if ignored_tracebacks: return ignored_tracebacks[-1] class FilteredTraceback(object): """Wraps a traceback to filter unwanted frames.""" def __init__(self, tb, filtered_traceback): """Constructor. :param tb: The start of the traceback chain to filter. :param filtered_traceback: The first traceback of a trailing chain that is to be filtered. """ self._tb = tb self.tb_lasti = self._tb.tb_lasti self.tb_lineno = self._tb.tb_lineno self.tb_frame = self._tb.tb_frame self._filtered_traceback = filtered_traceback @property def tb_next(self): tb_next = self._tb.tb_next if tb_next and tb_next != self._filtered_traceback: return FilteredTraceback(tb_next, self._filtered_traceback) neutron-12.1.1/neutron/tests/etc/0000775000175000017500000000000013553660156016731 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/etc/policy.json0000664000175000017500000002740513553660047021132 0ustar zuulzuul00000000000000{ "context_is_admin": "role:admin", "owner": "tenant_id:%(tenant_id)s", "admin_or_owner": "rule:context_is_admin or rule:owner", "context_is_advsvc": "role:advsvc", "admin_or_network_owner": "rule:context_is_admin or tenant_id:%(network:tenant_id)s", "admin_owner_or_network_owner": "rule:owner or rule:admin_or_network_owner", "admin_only": "rule:context_is_admin", "regular_user": "", "admin_or_data_plane_int": "rule:context_is_admin or role:data_plane_integrator", "shared": "field:networks:shared=True", "shared_subnetpools": "field:subnetpools:shared=True", "shared_address_scopes": "field:address_scopes:shared=True", "external": "field:networks:router:external=True", "default": "rule:admin_or_owner", "create_subnet": "rule:admin_or_network_owner", "create_subnet:segment_id": "rule:admin_only", "create_subnet:service_types": "rule:admin_only", "get_subnet": "rule:admin_or_owner or rule:shared", "get_subnet:segment_id": "rule:admin_only", "update_subnet": "rule:admin_or_network_owner", "update_subnet:service_types": "rule:admin_only", "delete_subnet": "rule:admin_or_network_owner", "create_subnetpool": "", "create_subnetpool:shared": "rule:admin_only", "create_subnetpool:is_default": "rule:admin_only", "get_subnetpool": "rule:admin_or_owner or rule:shared_subnetpools", "update_subnetpool": "rule:admin_or_owner", "update_subnetpool:is_default": "rule:admin_only", "delete_subnetpool": "rule:admin_or_owner", "create_address_scope": "", "create_address_scope:shared": "rule:admin_only", "get_address_scope": "rule:admin_or_owner or rule:shared_address_scopes", "update_address_scope": "rule:admin_or_owner", "update_address_scope:shared": "rule:admin_only", "delete_address_scope": "rule:admin_or_owner", "create_network": "", "get_network": "rule:admin_or_owner or rule:shared or rule:external or rule:context_is_advsvc", "get_network:router:external": "rule:regular_user", "get_network:segments": "rule:admin_only", "get_network:provider:network_type": "rule:admin_only", "get_network:provider:physical_network": "rule:admin_only", "get_network:provider:segmentation_id": "rule:admin_only", "get_network:queue_id": "rule:admin_only", "get_network_ip_availabilities": "rule:admin_only", "get_network_ip_availability": "rule:admin_only", "create_network:shared": "rule:admin_only", "create_network:router:external": "rule:admin_only", "create_network:is_default": "rule:admin_only", "create_network:segments": "rule:admin_only", "create_network:provider:network_type": "rule:admin_only", "create_network:provider:physical_network": "rule:admin_only", "create_network:provider:segmentation_id": "rule:admin_only", "update_network": "rule:admin_or_owner", "update_network:segments": "rule:admin_only", "update_network:shared": "rule:admin_only", "update_network:provider:network_type": "rule:admin_only", "update_network:provider:physical_network": "rule:admin_only", "update_network:provider:segmentation_id": "rule:admin_only", "update_network:router:external": "rule:admin_only", "delete_network": "rule:admin_or_owner", "create_segment": "rule:admin_only", "get_segment": "rule:admin_only", "update_segment": "rule:admin_only", "delete_segment": "rule:admin_only", "network_device": "field:port:device_owner=~^network:", "create_port": "", "create_port:device_owner": "not rule:network_device or rule:context_is_advsvc or rule:admin_or_network_owner", "create_port:mac_address": "rule:context_is_advsvc or rule:admin_or_network_owner", "create_port:fixed_ips": "rule:context_is_advsvc or rule:admin_or_network_owner or rule:shared", "create_port:fixed_ips:ip_address": "rule:context_is_advsvc or rule:admin_or_network_owner", "create_port:fixed_ips:subnet_id": "rule:context_is_advsvc or rule:admin_or_network_owner or rule:shared", "create_port:port_security_enabled": "rule:context_is_advsvc or rule:admin_or_network_owner", "create_port:binding:host_id": "rule:admin_only", "create_port:binding:profile": "rule:admin_only", "create_port:mac_learning_enabled": "rule:context_is_advsvc or rule:admin_or_network_owner", "create_port:allowed_address_pairs": "rule:admin_or_network_owner", "create_port:allowed_address_pairs:mac_address": "rule:admin_or_network_owner", "create_port:allowed_address_pairs:ip_address": "rule:admin_or_network_owner", "get_port": "rule:context_is_advsvc or rule:admin_owner_or_network_owner", "get_port:queue_id": "rule:admin_only", "get_port:binding:vif_type": "rule:admin_only", "get_port:binding:vif_details": "rule:admin_only", "get_port:binding:host_id": "rule:admin_only", "get_port:binding:profile": "rule:admin_only", "update_port": "rule:admin_or_owner or rule:context_is_advsvc", "update_port:device_owner": "not rule:network_device or rule:context_is_advsvc or rule:admin_or_network_owner", "update_port:mac_address": "rule:admin_only or rule:context_is_advsvc", "update_port:fixed_ips": "rule:context_is_advsvc or rule:admin_or_network_owner or rule:shared", "update_port:fixed_ips:ip_address": "rule:context_is_advsvc or rule:admin_or_network_owner", "update_port:fixed_ips:subnet_id": "rule:context_is_advsvc or rule:admin_or_network_owner or rule:shared", "update_port:port_security_enabled": "rule:context_is_advsvc or rule:admin_or_network_owner", "update_port:binding:host_id": "rule:admin_only", "update_port:binding:profile": "rule:admin_only", "update_port:mac_learning_enabled": "rule:context_is_advsvc or rule:admin_or_network_owner", "update_port:allowed_address_pairs": "rule:admin_or_network_owner", "update_port:allowed_address_pairs:mac_address": "rule:admin_or_network_owner", "update_port:allowed_address_pairs:ip_address": "rule:admin_or_network_owner", "update_port:data_plane_status": "rule:admin_or_data_plane_int", "delete_port": "rule:context_is_advsvc or rule:admin_owner_or_network_owner", "get_router:ha": "rule:admin_only", "create_router": "rule:regular_user", "create_router:external_gateway_info": "rule:admin_or_owner", "create_router:external_gateway_info:network_id": "rule:admin_or_owner", "create_router:external_gateway_info:enable_snat": "rule:admin_only", "create_router:distributed": "rule:admin_only", "create_router:ha": "rule:admin_only", "get_router": "rule:admin_or_owner", "get_router:distributed": "rule:admin_only", "update_router": "rule:admin_or_owner", "update_router:external_gateway_info": "rule:admin_or_owner", "update_router:external_gateway_info:network_id": "rule:admin_or_owner", "update_router:external_gateway_info:enable_snat": "rule:admin_only", "update_router:distributed": "rule:admin_only", "update_router:ha": "rule:admin_only", "delete_router": "rule:admin_or_owner", "add_router_interface": "rule:admin_or_owner", "remove_router_interface": "rule:admin_or_owner", "create_router:external_gateway_info:external_fixed_ips": "rule:admin_only", "update_router:external_gateway_info:external_fixed_ips": "rule:admin_only", "create_qos_queue": "rule:admin_only", "get_qos_queue": "rule:admin_only", "update_agent": "rule:admin_only", "delete_agent": "rule:admin_only", "get_agent": "rule:admin_only", "create_dhcp-network": "rule:admin_only", "delete_dhcp-network": "rule:admin_only", "get_dhcp-networks": "rule:admin_only", "create_l3-router": "rule:admin_only", "delete_l3-router": "rule:admin_only", "get_l3-routers": "rule:admin_only", "get_dhcp-agents": "rule:admin_only", "get_l3-agents": "rule:admin_only", "get_loadbalancer-agent": "rule:admin_only", "get_loadbalancer-pools": "rule:admin_only", "get_agent-loadbalancers": "rule:admin_only", "get_loadbalancer-hosting-agent": "rule:admin_only", "create_floatingip": "rule:regular_user", "create_floatingip:floating_ip_address": "rule:admin_only", "update_floatingip": "rule:admin_or_owner", "delete_floatingip": "rule:admin_or_owner", "get_floatingip": "rule:admin_or_owner", "create_network_profile": "rule:admin_only", "update_network_profile": "rule:admin_only", "delete_network_profile": "rule:admin_only", "get_network_profiles": "", "get_network_profile": "", "update_policy_profiles": "rule:admin_only", "get_policy_profiles": "", "get_policy_profile": "", "create_metering_label": "rule:admin_only", "delete_metering_label": "rule:admin_only", "get_metering_label": "rule:admin_only", "create_metering_label_rule": "rule:admin_only", "delete_metering_label_rule": "rule:admin_only", "get_metering_label_rule": "rule:admin_only", "get_service_provider": "rule:regular_user", "get_lsn": "rule:admin_only", "create_lsn": "rule:admin_only", "create_flavor": "rule:admin_only", "update_flavor": "rule:admin_only", "delete_flavor": "rule:admin_only", "get_flavors": "rule:regular_user", "get_flavor": "rule:regular_user", "create_service_profile": "rule:admin_only", "update_service_profile": "rule:admin_only", "delete_service_profile": "rule:admin_only", "get_service_profiles": "rule:admin_only", "get_service_profile": "rule:admin_only", "get_policy": "rule:regular_user", "create_policy": "rule:admin_only", "update_policy": "rule:admin_only", "delete_policy": "rule:admin_only", "get_policy_bandwidth_limit_rule": "rule:regular_user", "create_policy_bandwidth_limit_rule": "rule:admin_only", "delete_policy_bandwidth_limit_rule": "rule:admin_only", "update_policy_bandwidth_limit_rule": "rule:admin_only", "get_policy_dscp_marking_rule": "rule:regular_user", "create_policy_dscp_marking_rule": "rule:admin_only", "delete_policy_dscp_marking_rule": "rule:admin_only", "update_policy_dscp_marking_rule": "rule:admin_only", "get_rule_type": "rule:regular_user", "get_policy_minimum_bandwidth_rule": "rule:regular_user", "create_policy_minimum_bandwidth_rule": "rule:admin_only", "delete_policy_minimum_bandwidth_rule": "rule:admin_only", "update_policy_minimum_bandwidth_rule": "rule:admin_only", "restrict_wildcard": "(not field:rbac_policy:target_tenant=*) or rule:admin_only", "create_rbac_policy": "", "create_rbac_policy:target_tenant": "rule:restrict_wildcard", "update_rbac_policy": "rule:admin_or_owner", "update_rbac_policy:target_tenant": "rule:restrict_wildcard and rule:admin_or_owner", "get_rbac_policy": "rule:admin_or_owner", "delete_rbac_policy": "rule:admin_or_owner", "create_flavor_service_profile": "rule:admin_only", "delete_flavor_service_profile": "rule:admin_only", "get_flavor_service_profile": "rule:regular_user", "get_auto_allocated_topology": "rule:admin_or_owner", "create_trunk": "rule:regular_user", "get_trunk": "rule:admin_or_owner", "delete_trunk": "rule:admin_or_owner", "get_subports": "", "add_subports": "rule:admin_or_owner", "remove_subports": "rule:admin_or_owner", "get_security_groups": "rule:admin_or_owner", "get_security_group": "rule:admin_or_owner", "create_security_group": "rule:admin_or_owner", "update_security_group": "rule:admin_or_owner", "delete_security_group": "rule:admin_or_owner", "get_security_group_rules": "rule:admin_or_owner", "get_security_group_rule": "rule:admin_or_owner", "create_security_group_rule": "rule:admin_or_owner", "delete_security_group_rule": "rule:admin_or_owner", "get_loggable_resources": "rule:admin_only", "create_log": "rule:admin_only", "update_log": "rule:admin_only", "delete_log": "rule:admin_only", "get_logs": "rule:admin_only", "get_log": "rule:admin_only" } neutron-12.1.1/neutron/tests/etc/neutron_test2.conf.example0000664000175000017500000000005113553660046024037 0ustar zuulzuul00000000000000[service_providers] service_provider=zzz neutron-12.1.1/neutron/tests/etc/neutron_test.conf0000664000175000017500000000007613553660046022332 0ustar zuulzuul00000000000000[service_providers] service_provider=foo service_provider=bar neutron-12.1.1/neutron/tests/etc/api-paste.ini0000664000175000017500000000266713553660046021326 0ustar zuulzuul00000000000000[composite:neutron] use = egg:Paste#urlmap /: neutronversions_composite /v2.0: neutronapi_v2_0 [composite:neutronapi_v2_0] use = call:neutron.auth:pipeline_factory noauth = cors http_proxy_to_wsgi request_id catch_errors extensions neutronapiapp_v2_0 keystone = cors http_proxy_to_wsgi request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0 [composite:neutronversions_composite] use = call:neutron.auth:pipeline_factory noauth = cors http_proxy_to_wsgi neutronversions keystone = cors http_proxy_to_wsgi neutronversions [filter:request_id] paste.filter_factory = oslo_middleware:RequestId.factory [filter:catch_errors] paste.filter_factory = oslo_middleware:CatchErrors.factory [filter:cors] paste.filter_factory = oslo_middleware.cors:filter_factory oslo_config_project = neutron [filter:http_proxy_to_wsgi] paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory [filter:keystonecontext] paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory [filter:authtoken] paste.filter_factory = keystonemiddleware.auth_token:filter_factory [filter:extensions] paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory [app:neutronversions] paste.app_factory = neutron.pecan_wsgi.app:versions_factory [app:neutronapiapp_v2_0] paste.app_factory = neutron.api.v2.router:APIRouter.factory [filter:osprofiler] paste.filter_factory = osprofiler.web:WsgiMiddleware.factory neutron-12.1.1/neutron/tests/etc/api-paste.ini.test0000664000175000017500000000044213553660046022271 0ustar zuulzuul00000000000000[pipeline:extensions_app_with_filter] pipeline = extensions extensions_test_app [filter:extensions] paste.filter_factory = neutron.common.extensions:plugin_aware_extension_middleware_factory [app:extensions_test_app] paste.app_factory = neutron.tests.unit.api.test_extensions:app_factory neutron-12.1.1/neutron/tests/etc/neutron.conf0000664000175000017500000000075513553660047021300 0ustar zuulzuul00000000000000[DEFAULT] # Show more verbose log output (sets DEBUG log level output) default_log_levels = neutron=DEBUG # Show debugging output in logs (sets DEBUG log level output) debug = False # Address to bind the API server bind_host = 0.0.0.0 # Port the bind the API server to bind_port = 9696 # Paste configuration file api_paste_config = api-paste.ini.test # The messaging module to use, defaults to kombu. rpc_backend = fake lock_path = $state_path/lock [database] connection = 'sqlite://' neutron-12.1.1/neutron/tests/fullstack/0000775000175000017500000000000013553660156020146 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/fullstack/test_dhcp_agent.py0000664000175000017500000001576113553660047023664 0ustar zuulzuul00000000000000# Copyright 2016 OVH SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import random from neutron_lib import constants from oslo_utils import uuidutils from neutron.agent.linux import ip_lib from neutron.common import utils as common_utils from neutron.tests.fullstack import base from neutron.tests.fullstack.cmd import dhcp_agent as cmd from neutron.tests.fullstack.resources import environment from neutron.tests.fullstack.resources import machine from neutron.tests.unit import testlib_api load_tests = testlib_api.module_load_tests class BaseDhcpAgentTest(base.BaseFullStackTestCase): scenarios = [ (constants.AGENT_TYPE_OVS, {'l2_agent_type': constants.AGENT_TYPE_OVS}), (constants.AGENT_TYPE_LINUXBRIDGE, {'l2_agent_type': constants.AGENT_TYPE_LINUXBRIDGE}) ] def setUp(self): host_descriptions = [ environment.HostDescription( dhcp_agent=True, l2_agent_type=self.l2_agent_type ) for _ in range(self.number_of_hosts)] env = environment.Environment( environment.EnvironmentDescription( l2_pop=False, arp_responder=False, agent_down_time=self.agent_down_time), host_descriptions) super(BaseDhcpAgentTest, self).setUp(env) self.project_id = uuidutils.generate_uuid() self._create_network_subnet_and_vm() def _spawn_vm(self): host = random.choice(self.environment.hosts) vm = self.useFixture( machine.FakeFullstackMachine( host, self.network['id'], self.project_id, self.safe_client, use_dhcp=True)) vm.block_until_boot() return vm def _create_network_subnet_and_vm(self): self.network = self.safe_client.create_network(self.project_id) self.subnet = self.safe_client.create_subnet( self.project_id, self.network['id'], cidr='10.0.0.0/24', gateway_ip='10.0.0.1', name='subnet-test', enable_dhcp=True) self.vm = self._spawn_vm() class TestDhcpAgentNoHA(BaseDhcpAgentTest): number_of_hosts = 1 agent_down_time = 60 def test_dhcp_assignment(self): # First check if network was scheduled to one DHCP agent dhcp_agents = self.client.list_dhcp_agent_hosting_networks( self.network['id']) self.assertEqual(1, len(dhcp_agents['agents'])) # And check if IP and gateway config is fine on FakeMachine self.vm.block_until_dhcp_config_done() def test_mtu_update(self): # The test case needs access to devices in nested namespaces. ip_lib # doesn't support it, and it's probably unsafe to touch the library for # testing matters. # TODO(jlibosva) revisit when ip_lib supports nested namespaces if self.environment.hosts[0].dhcp_agent.namespace is not None: self.skip("ip_lib doesn't support nested namespaces") self.vm.block_until_dhcp_config_done() namespace = cmd._get_namespace_name( self.network['id'], suffix=self.environment.hosts[0].dhcp_agent.get_namespace_suffix()) self.assert_namespace_exists(namespace) ip = ip_lib.IPWrapper(namespace) devices = ip.get_devices() self.assertEqual(1, len(devices)) dhcp_dev = devices[0] mtu = dhcp_dev.link.mtu self.assertEqual(1450, mtu) mtu -= 1 self.safe_client.update_network(self.network['id'], mtu=mtu) common_utils.wait_until_true(lambda: dhcp_dev.link.mtu == mtu) class TestDhcpAgentHA(BaseDhcpAgentTest): number_of_hosts = 2 agent_down_time = 30 def _wait_until_network_rescheduled(self, old_agent): def _agent_rescheduled(): network_agents = self.client.list_dhcp_agent_hosting_networks( self.network['id'])['agents'] if network_agents: return network_agents[0]['id'] != old_agent['id'] return False common_utils.wait_until_true(_agent_rescheduled) def _kill_dhcp_agent(self, agent): for host in self.environment.hosts: hostname = host.dhcp_agent.get_agent_hostname() if hostname == agent['host']: host.dhcp_agent.kill() self._wait_until_agent_down(agent['id']) break def _add_network_to_new_agent(self): dhcp_agents = self.client.list_agents( agent_type=constants.AGENT_TYPE_DHCP)['agents'] dhcp_agents_ids = [agent['id'] for agent in dhcp_agents] current_agents = self.client.list_dhcp_agent_hosting_networks( self.network['id'])['agents'] current_agents_ids = [agent['id'] for agent in current_agents] new_agents_ids = list(set(dhcp_agents_ids) - set(current_agents_ids)) if new_agents_ids: new_agent_id = random.choice(new_agents_ids) self.client.add_network_to_dhcp_agent( new_agent_id, {'network_id': self.network['id']}) def test_reschedule_network_on_new_agent(self): network_dhcp_agents = self.client.list_dhcp_agent_hosting_networks( self.network['id'])['agents'] self.assertEqual(1, len(network_dhcp_agents)) self._kill_dhcp_agent(network_dhcp_agents[0]) self._wait_until_network_rescheduled(network_dhcp_agents[0]) # ensure that only one agent is handling DHCP for this network new_network_dhcp_agents = self.client.list_dhcp_agent_hosting_networks( self.network['id'])['agents'] self.assertEqual(1, len(new_network_dhcp_agents)) # check if new vm will get IP from new DHCP agent new_vm = self._spawn_vm() new_vm.block_until_dhcp_config_done() def test_multiple_agents_for_network(self): network_dhcp_agents = self.client.list_dhcp_agent_hosting_networks( self.network['id'])['agents'] self.assertEqual(1, len(network_dhcp_agents)) self._add_network_to_new_agent() # ensure that two agents are handling DHCP for this network network_dhcp_agents = self.client.list_dhcp_agent_hosting_networks( self.network['id'])['agents'] self.assertEqual(2, len(network_dhcp_agents)) self._kill_dhcp_agent(network_dhcp_agents[0]) # check if new vm will get IP from DHCP agent which is still alive new_vm = self._spawn_vm() new_vm.block_until_dhcp_config_done() neutron-12.1.1/neutron/tests/fullstack/test_connectivity.py0000664000175000017500000002174513553660047024305 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import signal from neutron_lib import constants from oslo_log import log as logging from oslo_utils import uuidutils import testscenarios from neutron.common import utils as common_utils from neutron.tests.common import net_helpers from neutron.tests.fullstack import base from neutron.tests.fullstack.resources import config from neutron.tests.fullstack.resources import environment from neutron.tests.fullstack.resources import machine from neutron.tests.fullstack import utils from neutron.tests.unit import testlib_api load_tests = testlib_api.module_load_tests SEGMENTATION_ID = 1234 LOG = logging.getLogger(__name__) class BaseConnectivitySameNetworkTest(base.BaseFullStackTestCase): of_interface = None arp_responder = False use_dhcp = True num_hosts = 3 def setUp(self): host_descriptions = [ # There's value in enabling L3 agents registration when l2pop # is enabled, because l2pop code makes assumptions about the # agent types present on machines. environment.HostDescription( l3_agent=self.l2_pop, of_interface=self.of_interface, l2_agent_type=self.l2_agent_type, dhcp_agent=self.use_dhcp, ) for _ in range(self.num_hosts)] env = environment.Environment( environment.EnvironmentDescription( network_type=self.network_type, l2_pop=self.l2_pop, arp_responder=self.arp_responder), host_descriptions) super(BaseConnectivitySameNetworkTest, self).setUp(env) def _prepare_network(self, tenant_uuid): net_args = {'network_type': self.network_type} if self.network_type in ['flat', 'vlan']: net_args['physical_network'] = config.PHYSICAL_NETWORK_NAME if self.network_type in ['vlan', 'gre', 'vxlan']: net_args['segmentation_id'] = SEGMENTATION_ID network = self.safe_client.create_network(tenant_uuid, **net_args) self.safe_client.create_subnet( tenant_uuid, network['id'], '20.0.0.0/24', enable_dhcp=self.use_dhcp) return network def _prepare_vms_in_net(self, tenant_uuid, network): vms = machine.FakeFullstackMachinesList( self.useFixture( machine.FakeFullstackMachine( host, network['id'], tenant_uuid, self.safe_client, use_dhcp=self.use_dhcp)) for host in self.environment.hosts) vms.block_until_all_boot() return vms def _prepare_vms_in_single_network(self): tenant_uuid = uuidutils.generate_uuid() network = self._prepare_network(tenant_uuid) return self._prepare_vms_in_net(tenant_uuid, network) def _test_connectivity(self): vms = self._prepare_vms_in_single_network() vms.ping_all() class TestOvsConnectivitySameNetwork(BaseConnectivitySameNetworkTest): l2_agent_type = constants.AGENT_TYPE_OVS network_scenarios = [ ('VXLAN', {'network_type': 'vxlan', 'l2_pop': False}), ('GRE-l2pop-arp_responder', {'network_type': 'gre', 'l2_pop': True, 'arp_responder': True}), ('VLANs', {'network_type': 'vlan', 'l2_pop': False})] scenarios = testscenarios.multiply_scenarios( network_scenarios, utils.get_ovs_interface_scenarios()) def test_connectivity(self): self._test_connectivity() class TestOvsConnectivitySameNetworkOnOvsBridgeControllerStop( BaseConnectivitySameNetworkTest): num_hosts = 2 l2_agent_type = constants.AGENT_TYPE_OVS network_scenarios = [ ('VXLAN', {'network_type': 'vxlan', 'l2_pop': False}), ('GRE and l2pop', {'network_type': 'gre', 'l2_pop': True}), ('VLANs', {'network_type': 'vlan', 'l2_pop': False})] # Do not test for CLI ofctl interface as controller is irrelevant for CLI scenarios = testscenarios.multiply_scenarios( network_scenarios, [(m, v) for (m, v) in utils.get_ovs_interface_scenarios() if v['of_interface'] != 'ovs-ofctl']) def _test_controller_timeout_does_not_break_connectivity(self, kill_signal=None): # Environment preparation is effectively the same as connectivity test vms = self._prepare_vms_in_single_network() vms.ping_all() ns0 = vms[0].namespace ip1 = vms[1].ip LOG.debug("Stopping agents (hence also OVS bridge controllers)") for host in self.environment.hosts: if kill_signal is not None: host.l2_agent.stop(kill_signal=kill_signal) else: host.l2_agent.stop() # Ping to make sure that 3 x 5 seconds is overcame even under a high # load. The time was chosen to match three times inactivity_probe time, # which is the time after which the OVS vswitchd # treats the controller as dead and starts managing the bridge # by itself when the fail type settings is not set to secure (see # ovs-vsctl man page for further details) with net_helpers.async_ping(ns0, [ip1], timeout=2, count=25) as done: common_utils.wait_until_true( done, exception=RuntimeError("Networking interrupted after " "controllers have vanished")) def test_controller_timeout_does_not_break_connectivity_sigterm(self): self._test_controller_timeout_does_not_break_connectivity() def test_controller_timeout_does_not_break_connectivity_sigkill(self): self._test_controller_timeout_does_not_break_connectivity( signal.SIGKILL) class TestLinuxBridgeConnectivitySameNetwork(BaseConnectivitySameNetworkTest): l2_agent_type = constants.AGENT_TYPE_LINUXBRIDGE scenarios = [ ('VXLAN', {'network_type': 'vxlan', 'l2_pop': False}), ('VLANs', {'network_type': 'vlan', 'l2_pop': False}), ('VXLAN and l2pop', {'network_type': 'vxlan', 'l2_pop': True}) ] def test_connectivity(self): self._test_connectivity() class TestConnectivitySameNetworkNoDhcp(BaseConnectivitySameNetworkTest): scenarios = [ (constants.AGENT_TYPE_OVS, {'l2_agent_type': constants.AGENT_TYPE_OVS}), (constants.AGENT_TYPE_LINUXBRIDGE, {'l2_agent_type': constants.AGENT_TYPE_LINUXBRIDGE}) ] use_dhcp = False network_type = 'vxlan' l2_pop = False of_interface = 'native' def test_connectivity(self): self._test_connectivity() class TestUninterruptedConnectivityOnL2AgentRestart( BaseConnectivitySameNetworkTest): num_hosts = 2 ovs_agent_scenario = [('OVS', {'l2_agent_type': constants.AGENT_TYPE_OVS})] lb_agent_scenario = [('LB', {'l2_agent_type': constants.AGENT_TYPE_LINUXBRIDGE})] network_scenarios = [ ('Flat network', {'network_type': 'flat', 'l2_pop': False}), ('VLANs', {'network_type': 'vlan', 'l2_pop': False}), ('VXLAN', {'network_type': 'vxlan', 'l2_pop': False}), ] scenarios = ( testscenarios.multiply_scenarios(ovs_agent_scenario, network_scenarios, utils.get_ovs_interface_scenarios()) + testscenarios.multiply_scenarios(lb_agent_scenario, network_scenarios) ) def test_l2_agent_restart(self, agent_restart_timeout=20): # Environment preparation is effectively the same as connectivity test vms = self._prepare_vms_in_single_network() vms.ping_all() ns0 = vms[0].namespace ip1 = vms[1].ip agents = [host.l2_agent for host in self.environment.hosts] # Restart agents on all nodes simultaneously while pinging across # the hosts. The ping has to cross int and phys bridges and travels # via central bridge as the vms are on separate hosts. self._assert_ping_during_agents_restart( agents, ns0, [ip1], restart_timeout=agent_restart_timeout, ping_timeout=2, count=agent_restart_timeout) neutron-12.1.1/neutron/tests/fullstack/utils.py0000664000175000017500000000134613553660047021663 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. def get_ovs_interface_scenarios(): return [ ('openflow-cli', {'of_interface': 'ovs-ofctl'}), ('openflow-native', {'of_interface': 'native'}), ] neutron-12.1.1/neutron/tests/fullstack/test_ports_rebind.py0000664000175000017500000001505313553660046024253 0ustar zuulzuul00000000000000# Copyright 2018 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib import constants from oslo_utils import uuidutils from neutron.common import utils as common_utils from neutron.tests.common.exclusive_resources import ip_network from neutron.tests.fullstack import base from neutron.tests.fullstack.resources import environment from neutron.tests.fullstack.resources import machine from neutron.tests.unit import testlib_api load_tests = testlib_api.module_load_tests class TestPortsRebind(base.BaseFullStackTestCase): scenarios = [ ('Open vSwitch Agent', {'l2_agent_type': constants.AGENT_TYPE_OVS}), ('Linux Bridge Agent', { 'l2_agent_type': constants.AGENT_TYPE_LINUXBRIDGE})] def setUp(self): host_descriptions = [ environment.HostDescription( l2_agent_type=self.l2_agent_type, l3_agent=self.use_l3_agent)] env = environment.Environment( environment.EnvironmentDescription( agent_down_time=10), host_descriptions) super(TestPortsRebind, self).setUp(env) self.l2_agent_process = self.environment.hosts[0].l2_agent self.l2_agent = self.safe_client.client.list_agents( agent_type=self.l2_agent_type)['agents'][0] self.tenant_id = uuidutils.generate_uuid() self.network = self.safe_client.create_network(self.tenant_id) self.subnet = self.safe_client.create_subnet( self.tenant_id, self.network['id'], '20.0.0.0/24') def _ensure_port_bound(self, port_id): def port_bound(): port = self.safe_client.client.show_port(port_id)['port'] return ( port[portbindings.VIF_TYPE] not in [portbindings.VIF_TYPE_UNBOUND, portbindings.VIF_TYPE_BINDING_FAILED]) common_utils.wait_until_true(port_bound) def _ensure_port_binding_failed(self, port_id): def port_binding_failed(): port = self.safe_client.client.show_port(port_id)['port'] return (port[portbindings.VIF_TYPE] == portbindings.VIF_TYPE_BINDING_FAILED) common_utils.wait_until_true(port_binding_failed) class TestVMPortRebind(TestPortsRebind): use_l3_agent = False def test_vm_port_rebound_when_L2_agent_revived(self): """Test scenario 1. Create port which will be properly bound to host 2. Stop L2 agent and wait until it will be DEAD 3. Create another port - it should have "binding_failed" 4. Turn on L2 agent 5. Port from p.3 should be bound properly after L2 agent will be UP """ vm_1 = self.useFixture( machine.FakeFullstackMachine( self.environment.hosts[0], self.network['id'], self.tenant_id, self.safe_client)) vm_1.block_until_boot() self._ensure_port_bound(vm_1.neutron_port['id']) vm_1_port = self.safe_client.client.show_port( vm_1.neutron_port['id'])['port'] self.l2_agent_process = self.environment.hosts[0].l2_agent self.l2_agent = self.safe_client.client.list_agents( agent_type=self.l2_agent_type)['agents'][0] self.l2_agent_process.stop() self._wait_until_agent_down(self.l2_agent['id']) vm_2 = self.useFixture( machine.FakeFullstackMachine( self.environment.hosts[0], self.network['id'], self.tenant_id, self.safe_client)) self._ensure_port_binding_failed(vm_2.neutron_port['id']) vm_2_port = self.safe_client.client.show_port( vm_2.neutron_port['id'])['port'] # check if vm_1 port is still bound as it was before self._ensure_port_bound(vm_1.neutron_port['id']) # and that revision number of vm_1's port wasn't changed self.assertEqual( vm_1_port['revision_number'], self.safe_client.client.show_port( vm_1_port['id'])['port']['revision_number']) self.l2_agent_process.start() self._wait_until_agent_up(self.l2_agent['id']) self._ensure_port_bound(vm_2_port['id']) class TestRouterPortRebind(TestPortsRebind): use_l3_agent = True def setUp(self): super(TestRouterPortRebind, self).setUp() self.tenant_id = uuidutils.generate_uuid() self.ext_net = self.safe_client.create_network( self.tenant_id, external=True) ext_cidr = self.useFixture( ip_network.ExclusiveIPNetwork( "240.0.0.0", "240.255.255.255", "24")).network self.safe_client.create_subnet( self.tenant_id, self.ext_net['id'], ext_cidr) self.router = self.safe_client.create_router( self.tenant_id, external_network=self.ext_net['id']) def test_vm_port_rebound_when_L2_agent_revived(self): """Test scenario 1. Ensure that router gateway port is bound properly 2. Stop L2 agent and wait until it will be DEAD 3. Create router interface and check that it's port is "binding_failed" 4. Turn on L2 agent 5. Router's port created in p.3 should be now bound properly """ if self.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE: self.skipTest("Bug 1798085") gw_port = self.safe_client.client.list_ports( device_id=self.router['id'], device_owner=constants.DEVICE_OWNER_ROUTER_GW)['ports'][0] self._ensure_port_bound(gw_port['id']) self.l2_agent_process.stop() self._wait_until_agent_down(self.l2_agent['id']) router_interface_info = self.safe_client.add_router_interface( self.router['id'], self.subnet['id']) self._ensure_port_binding_failed(router_interface_info['port_id']) self.l2_agent_process.start() self._wait_until_agent_up(self.l2_agent['id']) self._ensure_port_bound(router_interface_info['port_id']) neutron-12.1.1/neutron/tests/fullstack/test_port_shut_down.py0000664000175000017500000000746413553660046024646 0ustar zuulzuul00000000000000# Copyright 2017 - Nokia # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools from neutron.common import utils from neutron.tests.fullstack import base from neutron.tests.fullstack.resources import environment from neutron.tests.unit import testlib_api from neutron_lib import constants from oslo_utils import uuidutils load_tests = testlib_api.module_load_tests class PortShutDownTest(base.BaseFullStackTestCase): # This is a test to confirm the port status # on shutting down the port administratively. # The port status should no longer be ACTIVE # and go to DOWN use_dhcp = True l2_pop = False arp_responder = False num_hosts = 1 scenarios = [ (constants.AGENT_TYPE_LINUXBRIDGE, {'l2_agent_type': constants.AGENT_TYPE_LINUXBRIDGE}), (constants.AGENT_TYPE_OVS, {'l2_agent_type': constants.AGENT_TYPE_OVS}) ] def setUp(self): host_descriptions = [ environment.HostDescription( l2_agent_type=self.l2_agent_type, dhcp_agent=self.use_dhcp, ) for _ in range(self.num_hosts)] env = environment.Environment( environment.EnvironmentDescription( l2_pop=self.l2_pop, arp_responder=self.arp_responder), host_descriptions) super(PortShutDownTest, self).setUp(env) def _create_external_network_and_subnet(self, tenant_id): # This test is not exclusive for the external networks. # It is only used here to implicitly create a dhcp port # on the network creation. network = self.safe_client.create_network( tenant_id, name='test-public', external=True, network_type='local') self.safe_client.create_subnet(tenant_id, network['id'], '240.0.0.0/8', gateway_ip='240.0.0.2') return network def _get_network_dhcp_ports(self, network_id): return self.client.list_ports(network_id=network_id, device_owner=constants.DEVICE_OWNER_DHCP)['ports'] def _is_port_active(self, port_id): port = self.client.show_port(port_id)['port'] return port['status'] == constants.PORT_STATUS_ACTIVE def _is_port_down(self, port_id): port = self.client.show_port(port_id)['port'] return port['status'] == constants.PORT_STATUS_DOWN def test_port_shut_down(self): tenant_id = uuidutils.generate_uuid() # Create an external network network = self._create_external_network_and_subnet(tenant_id) # Check if the DHCP port is created port_created = functools.partial(self._get_network_dhcp_ports, network['id']) utils.wait_until_true(port_created) # Get the DHCP port port = self._get_network_dhcp_ports(network['id'])[0] # Wait till the changes are reflected to DB port_status_active_predicate = functools.partial( self._is_port_active, port['id']) utils.wait_until_true(port_status_active_predicate) # Shut down the port self.safe_client.update_port(port['id'], admin_state_up=False) port_status_down_predicate = functools.partial( self._is_port_down, port['id']) utils.wait_until_true(port_status_down_predicate) neutron-12.1.1/neutron/tests/fullstack/base.py0000664000175000017500000001142313553660047021432 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from concurrent import futures import os from oslo_config import cfg from oslo_log import log as logging from neutron.agent.linux import ip_lib from neutron.common import utils as common_utils from neutron.conf.agent import common as config from neutron.tests import base as tests_base from neutron.tests.common import helpers from neutron.tests.common import net_helpers from neutron.tests.fullstack.resources import client as client_resource from neutron.tests import tools from neutron.tests.unit import testlib_api # This is the directory from which infra fetches log files for fullstack tests DEFAULT_LOG_DIR = os.path.join(helpers.get_test_log_path(), 'dsvm-fullstack-logs') ROOTDIR = os.path.dirname(__file__) LOG = logging.getLogger(__name__) class BaseFullStackTestCase(testlib_api.MySQLTestCaseMixin, testlib_api.SqlTestCase): """Base test class for full-stack tests.""" BUILD_WITH_MIGRATIONS = True def setUp(self, environment): super(BaseFullStackTestCase, self).setUp() tests_base.setup_test_logging( cfg.CONF, DEFAULT_LOG_DIR, '%s.txt' % self.get_name()) # NOTE(zzzeek): the opportunistic DB fixtures have built for # us a per-test (or per-process) database. Set the URL of this # database in CONF as the full stack tests need to actually run a # neutron server against this database. _orig_db_url = cfg.CONF.database.connection cfg.CONF.set_override( 'connection', str(self.engine.url), group='database') self.addCleanup( cfg.CONF.set_override, "connection", _orig_db_url, group="database" ) # NOTE(ihrachys): seed should be reset before environment fixture below # since the latter starts services that may rely on generated port # numbers tools.reset_random_seed() # configure test runner to use rootwrap self.setup_rootwrap() config.setup_privsep() self.environment = environment self.environment.test_name = self.get_name() self.useFixture(self.environment) self.client = self.environment.neutron_server.client self.safe_client = self.useFixture( client_resource.ClientFixture(self.client)) def get_name(self): class_name, test_name = self.id().split(".")[-2:] return "%s.%s" % (class_name, test_name) def _wait_until_agent_up(self, agent_id): def _agent_up(): agent = self.client.show_agent(agent_id)['agent'] return agent.get('alive') common_utils.wait_until_true(_agent_up) def _wait_until_agent_down(self, agent_id): def _agent_down(): agent = self.client.show_agent(agent_id)['agent'] return not agent.get('alive') common_utils.wait_until_true(_agent_down) def _assert_ping_during_agents_restart( self, agents, src_namespace, ips, restart_timeout=10, ping_timeout=1, count=10): with net_helpers.async_ping( src_namespace, ips, timeout=ping_timeout, count=count) as done: LOG.debug("Restarting agents") executor = futures.ThreadPoolExecutor(max_workers=len(agents)) restarts = [agent.restart(executor=executor) for agent in agents] futures.wait(restarts, timeout=restart_timeout) self.assertTrue(all([r.done() for r in restarts])) LOG.debug("Restarting agents - done") # It is necessary to give agents time to initialize # because some crucial steps (e.g. setting up bridge flows) # happen only after RPC is established common_utils.wait_until_true( done, timeout=count * (ping_timeout + 1), exception=RuntimeError("Could not ping the other VM, L2 agent " "restart leads to network disruption")) def assert_namespace_exists(self, ns_name): common_utils.wait_until_true( lambda: ip_lib.network_namespace_exists(ns_name, try_is_ready=True)) neutron-12.1.1/neutron/tests/fullstack/test_trunk.py0000664000175000017500000002743113553660046022727 0ustar zuulzuul00000000000000# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import netaddr from neutron_lib import constants from oslo_utils import uuidutils from neutron.common import utils from neutron.services.trunk.drivers.openvswitch.agent import ovsdb_handler from neutron.services.trunk.drivers.openvswitch.agent import trunk_manager from neutron.services.trunk.drivers.openvswitch import utils as trunk_ovs_utils from neutron.tests.fullstack import base from neutron.tests.fullstack.resources import environment from neutron.tests.fullstack.resources import machine def trunk_bridge_does_not_exist(trunk_id): """Return true if trunk bridge for given ID does not exists.""" bridge = trunk_manager.TrunkBridge(trunk_id) return not bridge.exists() def make_ip_network(port, network): """Make an IPNetwork object from port and network. Function returns IPNetwork object containing fixed IP address from port dictionary with prefixlen from network object. :param port: Port dictionary returned by Neutron API :param network: IPNetwork object in which the port's IP will be assigned. """ ip_address = netaddr.IPAddress( port['fixed_ips'][0]['ip_address']) return netaddr.IPNetwork( (ip_address.value, network.prefixlen)) class TrunkTestException(Exception): pass class Network(object): """A helper class to keep persistent info about assigned addresses.""" def __init__(self, prefix, network_cidr, tag=None): self.prefix = prefix self.network = netaddr.IPNetwork(network_cidr) self.neutron_network = None self.neutron_subnet = None self.tag = tag # Currently, only vlan is supported. Pass via __init__ once more are # supported. self.segmentation_type = 'vlan' @property def cidr(self): return str(self.network.cidr) @property def gateway(self): """Return lowest possible IP in the given subnet.""" return str(netaddr.IPAddress(self.network.first + 1)) @property def id(self): return self.neutron_network['id'] @property def name(self): return "%s-network" % self.prefix @property def subnet_name(self): return "%s-subnet" % self.prefix class TestTrunkPlugin(base.BaseFullStackTestCase): def setUp(self): host_desc = [environment.HostDescription( l3_agent=False, l2_agent_type=constants.AGENT_TYPE_OVS)] env_desc = environment.EnvironmentDescription(service_plugins='trunk') env = environment.Environment(env_desc, host_desc) super(TestTrunkPlugin, self).setUp(env) self.tenant_id = uuidutils.generate_uuid() self.trunk_network = Network('trunk', '10.0.0.0/24') self.vlan1_network = Network('vlan1', '192.168.0.0/24', tag=10) self.vlan2_network = Network('vlan2', '192.168.1.0/24', tag=20) self.host = self.environment.hosts[0] for network in ( self.trunk_network, self.vlan1_network, self.vlan2_network): self.create_network_and_subnet(network) def create_network_and_subnet(self, network): """Create network and subnet resources in Neutron based on network object. The resource names will be -network and -subnet, where prefix is taken from network object. :param network: Network object from this module. """ network.neutron_network = self.safe_client.create_network( self.tenant_id, network.name) network.neutron_subnet = self.safe_client.create_subnet( self.tenant_id, network.id, cidr=network.cidr, gateway_ip=network.gateway, name=network.subnet_name, enable_dhcp=False) def create_vlan_aware_vm(self, trunk_network, vlan_networks): """Create a fake machine with one untagged port and subports according vlan_networks parameter. :param trunk_network: Instance of Network where trunk port should be created. :param vlan_networks: List of Network instances where subports should be created. """ trunk_parent_port = self.safe_client.create_port( self.tenant_id, trunk_network.id) vlan_subports = [ self.safe_client.create_port(self.tenant_id, vlan_network.id, mac_address=trunk_parent_port['mac_address']) for vlan_network in vlan_networks] trunk = self.safe_client.create_trunk( self.tenant_id, name='mytrunk', port_id=trunk_parent_port['id'], sub_ports=[ {'port_id': vlan_subport['id'], 'segmentation_type': 'vlan', 'segmentation_id': vlan_network.tag} for vlan_subport, vlan_network in zip(vlan_subports, vlan_networks) ], ) vm = self.useFixture( machine.FakeFullstackTrunkMachine( trunk, self.host, trunk_network.id, self.tenant_id, self.safe_client, neutron_port=trunk_parent_port, bridge_name=trunk_ovs_utils.gen_trunk_br_name(trunk['id']))) for port, vlan_network in zip(vlan_subports, vlan_networks): ip_network = make_ip_network(port, vlan_network.network) vm.add_vlan_interface( port['mac_address'], ip_network, vlan_network.tag) vm.block_until_boot() return vm def create_vm_in_network(self, network): """Create a fake machine in given network.""" return self.useFixture( machine.FakeFullstackMachine( self.host, network.id, self.tenant_id, self.safe_client ) ) def add_subport_to_vm(self, vm, subport_network): """Add subport from subport_network to given vm. :param vm: FakeFullstackMachine instance to with subport should be added. :param subport_network: Network object representing network containing port for subport. """ subport = self.safe_client.create_port( self.tenant_id, subport_network.id, mac_address=vm.neutron_port['mac_address']) subport_spec = { 'port_id': subport['id'], 'segmentation_type': subport_network.segmentation_type, 'segmentation_id': subport_network.tag } self.safe_client.trunk_add_subports( self.tenant_id, vm.trunk['id'], [subport_spec]) ip_network = make_ip_network(subport, subport_network.network) vm.add_vlan_interface( subport['mac_address'], ip_network, subport_network.tag) # NOTE(slaweq): As is described in bug # https://bugs.launchpad.net/neutron/+bug/1687709 when more than one # different ovs-agent with enabled trunk driver is running at a time it # might lead to race contitions between them. # Because of that ovs_agent used for fullstack tests is monkeypatched and # loads trunk driver only if trunk service plugin is enabled. # That makes restriction that only a single set of tests with trunk-enabled # services will run at the same time. def test_trunk_lifecycle(self): """Test life-cycle of a fake VM with trunk port. This test uses 4 fake machines: - vlan_aware_vm (A) that is at the beginning connected to a trunk network and a vlan1 network. - trunk_network_vm (B) that is connected to the trunk network. - vlan1_network_vm (C) that is connected to the vlan1 network. - vlan2_network_vm (D) that is connected to a vlan2 network. Scenario steps: - all the vms from above are created - A can talk with B (over the trunk network) - A can talk with C (over the vlan1 network) - A can not talk with D (no leg on the vlan2 network) - subport from the vlan2 network is added to A - A can now talk with D (over the vlan2 network) - subport from the vlan1 network is removed from A - A can talk with B (over the trunk network) - A can not talk with C (no leg on the vlan1 network) - A can talk with D (over the vlan2 network) - A is deleted which leads to removal of trunk bridge - no leftovers like patch ports to the trunk bridge should remain on an integration bridge """ vlan_aware_vm = self.create_vlan_aware_vm( self.trunk_network, [self.vlan1_network] ) trunk_id = vlan_aware_vm.trunk['id'] # Create helper vms with different networks trunk_network_vm = self.create_vm_in_network(self.trunk_network) vlan1_network_vm = self.create_vm_in_network(self.vlan1_network) vlan2_network_vm = self.create_vm_in_network(self.vlan2_network) for vm in trunk_network_vm, vlan1_network_vm, vlan2_network_vm: vm.block_until_boot() # Test connectivity to trunk and subport vlan_aware_vm.block_until_ping(trunk_network_vm.ip) vlan_aware_vm.block_until_ping(vlan1_network_vm.ip) # Subport for vlan2 hasn't been added yet vlan_aware_vm.block_until_no_ping(vlan2_network_vm.ip) # Add another subport and test self.add_subport_to_vm(vlan_aware_vm, self.vlan2_network) vlan_aware_vm.block_until_ping(vlan2_network_vm.ip) # Remove the first subport self.safe_client.trunk_remove_subports( self.tenant_id, trunk_id, [vlan_aware_vm.trunk['sub_ports'][0]]) # vlan1_network_vm now shouldn't be able to talk to vlan_aware_vm vlan_aware_vm.block_until_no_ping(vlan1_network_vm.ip) # but trunk and vlan2 should be able to ping vlan_aware_vm.block_until_ping(trunk_network_vm.ip) vlan_aware_vm.block_until_ping(vlan2_network_vm.ip) # Delete vm and check that patch ports and trunk bridge are gone vlan_aware_vm.destroy() bridge_doesnt_exist_predicate = functools.partial( trunk_bridge_does_not_exist, trunk_id) utils.wait_until_true( bridge_doesnt_exist_predicate, exception=TrunkTestException( 'Trunk bridge with ID %s has not been removed' % trunk_id) ) integration_bridge = self.host.get_bridge(None) no_patch_ports_predicate = functools.partial( lambda bridge: not ovsdb_handler.bridge_has_service_port(bridge), integration_bridge, ) try: utils.wait_until_true(no_patch_ports_predicate) except utils.WaitTimeout: # Create exception object after timeout to provide up-to-date list # of interfaces raise TrunkTestException( "Integration bridge %s still has following ports while some of" " them are patch ports for trunk that were supposed to be " "removed: %s" % ( integration_bridge.br_name, integration_bridge.get_iface_name_list() ) ) neutron-12.1.1/neutron/tests/fullstack/cmd/0000775000175000017500000000000013553660156020711 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/fullstack/cmd/dhcp_agent.py0000775000175000017500000000474413553660047023372 0ustar zuulzuul00000000000000#!/usr/bin/env python # Copyright 2016 OVH SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import os import sys from oslo_config import cfg from oslo_utils import uuidutils from neutron.agent.linux import dhcp as linux_dhcp from neutron.cmd.eventlet.agents import dhcp as dhcp_agent OPTS = [ cfg.StrOpt('test_namespace_suffix', default='testprefix', help="Suffix to append to all DHCP namespace names."), ] def _get_namespace_name(id_, suffix=None): suffix = suffix or cfg.CONF.test_namespace_suffix return "%s%s%s" % (linux_dhcp.NS_PREFIX, id_, suffix) def NetModel_init(self, d): super(linux_dhcp.NetModel, self).__init__(d) self._ns_name = _get_namespace_name(self.id) @classmethod def existing_dhcp_networks(cls, conf): """Return a list of existing networks ids that we have configs for.""" confs_dir = cls.get_confs_dir(conf) networks = [] try: for c in os.listdir(confs_dir): c = c.replace(cfg.CONF.test_namespace_suffix, "") if uuidutils.is_uuid_like(c): networks.append(c) except OSError: pass return networks def monkeypatch_dhcplocalprocess_init(): original_init = linux_dhcp.DhcpLocalProcess.__init__ def new_init(self, conf, network, process_monitor, version=None, plugin=None): network_copy = copy.deepcopy(network) network_copy.id = "%s%s" % (network.id, cfg.CONF.test_namespace_suffix) original_init( self, conf, network_copy, process_monitor, version, plugin) self.network = network linux_dhcp.DhcpLocalProcess.__init__ = new_init def monkeypatch_linux_dhcp(): linux_dhcp.NetModel.__init__ = NetModel_init linux_dhcp.Dnsmasq.existing_dhcp_networks = existing_dhcp_networks monkeypatch_dhcplocalprocess_init() def main(): cfg.CONF.register_opts(OPTS) monkeypatch_linux_dhcp() dhcp_agent.main() if __name__ == "__main__": sys.exit(main()) neutron-12.1.1/neutron/tests/fullstack/cmd/__init__.py0000664000175000017500000000000013553660047023007 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/fullstack/cmd/l3_agent.py0000775000175000017500000000146213553660047022764 0ustar zuulzuul00000000000000#!/usr/bin/env python # Copyright 2017 Eayun, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from neutron.common import eventlet_utils from neutron.tests.common.agents import l3_agent eventlet_utils.monkey_patch() if __name__ == "__main__": sys.exit(l3_agent.main()) neutron-12.1.1/neutron/tests/fullstack/cmd/ovs_agent.py0000775000175000017500000000320513553660047023252 0ustar zuulzuul00000000000000#!/usr/bin/env python # Copyright 2017 OVH SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from oslo_config import cfg from neutron.services.trunk.drivers.openvswitch.agent \ import driver as trunk_driver from neutron.tests.common.agents import ovs_agent def monkeypatch_init_handler(): original_handler = trunk_driver.init_handler def new_init_handler(resource, event, trigger, payload=None): # NOTE(slaweq): make this setup conditional based on server-side # capabilities for fullstack tests we can assume that server-side # and agent-side conf are in sync if "trunk" not in cfg.CONF.service_plugins: return original_handler(resource, event, trigger, payload) trunk_driver.init_handler = new_init_handler def main(): # TODO(slaweq): this monkepatch will not be necessary when # https://review.openstack.org/#/c/506722/ will be merged and ovsdb-server # ovs-vswitchd processes for each test will be isolated in separate # namespace monkeypatch_init_handler() ovs_agent.main() if __name__ == "__main__": sys.exit(main()) neutron-12.1.1/neutron/tests/fullstack/test_l3_agent.py0000664000175000017500000003563213553660047023263 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import os import time import netaddr from oslo_utils import uuidutils from neutron.agent.l3 import ha_router from neutron.agent.l3 import namespaces from neutron.agent.linux import ip_lib from neutron.common import utils as common_utils from neutron.tests.common.exclusive_resources import ip_network from neutron.tests.common import machine_fixtures from neutron.tests.fullstack import base from neutron.tests.fullstack.resources import environment from neutron.tests.fullstack.resources import machine from neutron.tests.unit import testlib_api load_tests = testlib_api.module_load_tests class TestL3Agent(base.BaseFullStackTestCase): def _create_external_network_and_subnet(self, tenant_id): network = self.safe_client.create_network( tenant_id, name='public', external=True) cidr = self.useFixture( ip_network.ExclusiveIPNetwork( "240.0.0.0", "240.255.255.255", "24")).network subnet = self.safe_client.create_subnet(tenant_id, network['id'], cidr) return network, subnet def block_until_port_status_active(self, port_id): def is_port_status_active(): port = self.client.show_port(port_id) return port['port']['status'] == 'ACTIVE' common_utils.wait_until_true(lambda: is_port_status_active(), sleep=1) def _create_and_attach_subnet( self, tenant_id, subnet_cidr, network_id, router_id): subnet = self.safe_client.create_subnet( tenant_id, network_id, subnet_cidr) router_interface_info = self.safe_client.add_router_interface( router_id, subnet['id']) self.block_until_port_status_active( router_interface_info['port_id']) def _boot_fake_vm_in_network(self, host, tenant_id, network_id, wait=True): vm = self.useFixture( machine.FakeFullstackMachine( host, network_id, tenant_id, self.safe_client, use_dhcp=True)) if wait: vm.block_until_boot() return vm def _create_net_subnet_and_vm(self, tenant_id, subnet_cidrs, host, router): network = self.safe_client.create_network(tenant_id) for cidr in subnet_cidrs: self._create_and_attach_subnet( tenant_id, cidr, network['id'], router['id']) return self._boot_fake_vm_in_network(host, tenant_id, network['id']) class TestLegacyL3Agent(TestL3Agent): def setUp(self): host_descriptions = [ environment.HostDescription(l3_agent=True, dhcp_agent=True), environment.HostDescription()] env = environment.Environment( environment.EnvironmentDescription( network_type='vlan', l2_pop=False), host_descriptions) super(TestLegacyL3Agent, self).setUp(env) def _get_namespace(self, router_id): return namespaces.build_ns_name(namespaces.NS_PREFIX, router_id) def test_namespace_exists(self): tenant_id = uuidutils.generate_uuid() router = self.safe_client.create_router(tenant_id) network = self.safe_client.create_network(tenant_id) subnet = self.safe_client.create_subnet( tenant_id, network['id'], '20.0.0.0/24', gateway_ip='20.0.0.1') self.safe_client.add_router_interface(router['id'], subnet['id']) namespace = "%s@%s" % ( self._get_namespace(router['id']), self.environment.hosts[0].l3_agent.get_namespace_suffix(), ) self.assert_namespace_exists(namespace) def test_mtu_update(self): tenant_id = uuidutils.generate_uuid() router = self.safe_client.create_router(tenant_id) network = self.safe_client.create_network(tenant_id) subnet = self.safe_client.create_subnet( tenant_id, network['id'], '20.0.0.0/24', gateway_ip='20.0.0.1') self.safe_client.add_router_interface(router['id'], subnet['id']) namespace = "%s@%s" % ( self._get_namespace(router['id']), self.environment.hosts[0].l3_agent.get_namespace_suffix(), ) self.assert_namespace_exists(namespace) ip = ip_lib.IPWrapper(namespace) common_utils.wait_until_true(lambda: ip.get_devices()) devices = ip.get_devices() self.assertEqual(1, len(devices)) ri_dev = devices[0] mtu = ri_dev.link.mtu self.assertEqual(1500, mtu) mtu -= 1 network = self.safe_client.update_network(network['id'], mtu=mtu) common_utils.wait_until_true(lambda: ri_dev.link.mtu == mtu) def test_east_west_traffic(self): tenant_id = uuidutils.generate_uuid() router = self.safe_client.create_router(tenant_id) vm1 = self._create_net_subnet_and_vm( tenant_id, ['20.0.0.0/24', '2001:db8:aaaa::/64'], self.environment.hosts[0], router) vm2 = self._create_net_subnet_and_vm( tenant_id, ['21.0.0.0/24', '2001:db8:bbbb::/64'], self.environment.hosts[1], router) vm1.block_until_ping(vm2.ip) # Verify ping6 from vm2 to vm1 IPv6 Address vm2.block_until_ping(vm1.ipv6) def test_north_south_traffic(self): # This function creates an external network which is connected to # central_external_bridge and spawns an external_vm on it. # The external_vm is configured with the gateway_ip (both v4 & v6 # addresses) of external subnet. Later, it creates a tenant router, # a tenant network and two tenant subnets (v4 and v6). The tenant # router is associated with tenant network and external network to # provide north-south connectivity to the VMs. # We validate the following in this testcase. # 1. SNAT support: using ping from tenant VM to external_vm # 2. Floating IP support: using ping from external_vm to VM floating ip # 3. IPv6 ext connectivity: using ping6 from tenant vm to external_vm. tenant_id = uuidutils.generate_uuid() ext_net, ext_sub = self._create_external_network_and_subnet(tenant_id) external_vm = self.useFixture( machine_fixtures.FakeMachine( self.environment.central_external_bridge, common_utils.ip_to_cidr(ext_sub['gateway_ip'], 24))) # Create an IPv6 subnet in the external network v6network = self.useFixture( ip_network.ExclusiveIPNetwork( "2001:db8:1234::1", "2001:db8:1234::10", "64")).network ext_v6sub = self.safe_client.create_subnet( tenant_id, ext_net['id'], v6network) router = self.safe_client.create_router(tenant_id, external_network=ext_net['id']) # Configure the gateway_ip of external v6subnet on the external_vm. external_vm.ipv6_cidr = common_utils.ip_to_cidr( ext_v6sub['gateway_ip'], 64) # Configure an IPv6 downstream route to the v6Address of router gw port for fixed_ip in router['external_gateway_info']['external_fixed_ips']: if netaddr.IPNetwork(fixed_ip['ip_address']).version == 6: external_vm.set_default_gateway(fixed_ip['ip_address']) vm = self._create_net_subnet_and_vm( tenant_id, ['20.0.0.0/24', '2001:db8:aaaa::/64'], self.environment.hosts[1], router) # ping external vm to test snat vm.block_until_ping(external_vm.ip) fip = self.safe_client.create_floatingip( tenant_id, ext_net['id'], vm.ip, vm.neutron_port['id']) # ping floating ip from external vm external_vm.block_until_ping(fip['floating_ip_address']) # Verify VM is able to reach the router interface. vm.block_until_ping(vm.gateway_ipv6) # Verify north-south connectivity using ping6 to external_vm. vm.block_until_ping(external_vm.ipv6) # Now let's remove and create again phys bridge and check connectivity # once again br_phys = self.environment.hosts[0].br_phys br_phys.destroy() br_phys.create() self.environment.hosts[0].connect_to_internal_network_via_vlans( br_phys) # ping floating ip from external vm external_vm.block_until_ping(fip['floating_ip_address']) # Verify VM is able to reach the router interface. vm.block_until_ping(vm.gateway_ipv6) # Verify north-south connectivity using ping6 to external_vm. vm.block_until_ping(external_vm.ipv6) class TestHAL3Agent(TestL3Agent): def setUp(self): host_descriptions = [ environment.HostDescription(l3_agent=True, dhcp_agent=True) for _ in range(2)] env = environment.Environment( environment.EnvironmentDescription( network_type='vxlan', l2_pop=True), host_descriptions) super(TestHAL3Agent, self).setUp(env) def _is_ha_router_active_on_one_agent(self, router_id): agents = self.client.list_l3_agent_hosting_routers(router_id) return ( agents['agents'][0]['ha_state'] != agents['agents'][1]['ha_state']) def test_ha_router(self): # TODO(amuller): Test external connectivity before and after a # failover, see: https://review.openstack.org/#/c/196393/ tenant_id = uuidutils.generate_uuid() router = self.safe_client.create_router(tenant_id, ha=True) common_utils.wait_until_true( lambda: len(self.client.list_l3_agent_hosting_routers( router['id'])['agents']) == 2, timeout=90) common_utils.wait_until_true( functools.partial( self._is_ha_router_active_on_one_agent, router['id']), timeout=90) def _get_keepalived_state(self, keepalived_state_file): with open(keepalived_state_file, "r") as fd: return fd.read() def _get_state_file_for_master_agent(self, router_id): for host in self.environment.hosts: keepalived_state_file = os.path.join( host.neutron_config.state_path, "ha_confs", router_id, "state") if self._get_keepalived_state(keepalived_state_file) == "master": return keepalived_state_file def _get_l3_agents_with_ha_state(self, l3_agents, router_id, ha_state): found_agents = [] agents_hosting_router = self.client.list_l3_agent_hosting_routers( router_id)['agents'] for agent in l3_agents: agent_host = agent.neutron_cfg_fixture.get_host() for agent_hosting_router in agents_hosting_router: if (agent_hosting_router['host'] == agent_host and agent_hosting_router['ha_state'] == ha_state): found_agents.append(agent) break return found_agents def test_keepalived_multiple_sighups_does_not_forfeit_mastership(self): """Setup a complete "Neutron stack" - both an internal and an external network+subnet, and a router connected to both. """ tenant_id = uuidutils.generate_uuid() ext_net, ext_sub = self._create_external_network_and_subnet(tenant_id) router = self.safe_client.create_router(tenant_id, ha=True, external_network=ext_net['id']) common_utils.wait_until_true( lambda: len(self.client.list_l3_agent_hosting_routers( router['id'])['agents']) == 2, timeout=90) common_utils.wait_until_true( functools.partial( self._is_ha_router_active_on_one_agent, router['id']), timeout=90) keepalived_state_file = self._get_state_file_for_master_agent( router['id']) self.assertIsNotNone(keepalived_state_file) network = self.safe_client.create_network(tenant_id) self._create_and_attach_subnet( tenant_id, '13.37.0.0/24', network['id'], router['id']) # Create 10 fake VMs, each with a floating ip. Each floating ip # association should send a SIGHUP to the keepalived's parent process, # unless the Throttler works. host = self.environment.hosts[0] vms = [self._boot_fake_vm_in_network(host, tenant_id, network['id'], wait=False) for i in range(10)] for vm in vms: self.safe_client.create_floatingip( tenant_id, ext_net['id'], vm.ip, vm.neutron_port['id']) # Check that the keepalived's state file has not changed and is still # master. This will indicate that the Throttler works. We want to check # for ha_vrrp_advert_int (the default is 2 seconds), plus a bit more. time_to_stop = (time.time() + (common_utils.DEFAULT_THROTTLER_VALUE * ha_router.THROTTLER_MULTIPLIER * 1.3)) while True: if time.time() > time_to_stop: break self.assertEqual( "master", self._get_keepalived_state(keepalived_state_file)) def test_ha_router_restart_standby_agents_no_packet_lost(self): tenant_id = uuidutils.generate_uuid() ext_net, ext_sub = self._create_external_network_and_subnet(tenant_id) router = self.safe_client.create_router(tenant_id, ha=True, external_network=ext_net['id']) external_vm = self.useFixture( machine_fixtures.FakeMachine( self.environment.central_external_bridge, common_utils.ip_to_cidr(ext_sub['gateway_ip'], 24))) common_utils.wait_until_true( lambda: len(self.client.list_l3_agent_hosting_routers( router['id'])['agents']) == 2, timeout=90) common_utils.wait_until_true( functools.partial( self._is_ha_router_active_on_one_agent, router['id']), timeout=90) router_ip = router['external_gateway_info'][ 'external_fixed_ips'][0]['ip_address'] l3_agents = [host.agents['l3'] for host in self.environment.hosts] l3_standby_agents = self._get_l3_agents_with_ha_state( l3_agents, router['id'], 'standby') self._assert_ping_during_agents_restart( l3_standby_agents, external_vm.namespace, [router_ip], count=60) neutron-12.1.1/neutron/tests/fullstack/resources/0000775000175000017500000000000013553660156022160 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/fullstack/resources/environment.py0000664000175000017500000003777613553660047025121 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import fixtures from neutron_lib import constants from neutronclient.common import exceptions as nc_exc from oslo_config import cfg from neutron.agent.linux import ip_lib from neutron.common import constants as common_const from neutron.common import utils as common_utils from neutron.plugins.ml2.drivers.linuxbridge.agent import \ linuxbridge_neutron_agent as lb_agent from neutron.tests.common.exclusive_resources import ip_address from neutron.tests.common.exclusive_resources import ip_network from neutron.tests.common import net_helpers from neutron.tests.fullstack.resources import config from neutron.tests.fullstack.resources import process class EnvironmentDescription(object): """A set of characteristics of an environment setup. Does the setup, as a whole, support tunneling? How about l2pop? """ def __init__(self, network_type='vxlan', l2_pop=True, qos=False, mech_drivers='openvswitch,linuxbridge', service_plugins='router', arp_responder=False, agent_down_time=75, router_scheduler=None, global_mtu=common_const.DEFAULT_NETWORK_MTU): self.network_type = network_type self.l2_pop = l2_pop self.qos = qos self.network_range = None self.mech_drivers = mech_drivers self.arp_responder = arp_responder self.agent_down_time = agent_down_time self.router_scheduler = router_scheduler self.global_mtu = global_mtu self.service_plugins = service_plugins if self.qos: self.service_plugins += ',qos' @property def tunneling_enabled(self): return self.network_type in ('vxlan', 'gre') class HostDescription(object): """A set of characteristics of an environment Host. What agents should the host spawn? What mode should each agent operate under? """ def __init__(self, l3_agent=False, dhcp_agent=False, of_interface='ovs-ofctl', l2_agent_type=constants.AGENT_TYPE_OVS, firewall_driver='noop', availability_zone=None, l3_agent_mode=None): self.l2_agent_type = l2_agent_type self.l3_agent = l3_agent self.dhcp_agent = dhcp_agent self.of_interface = of_interface self.firewall_driver = firewall_driver self.availability_zone = availability_zone self.l3_agent_mode = l3_agent_mode class Host(fixtures.Fixture): """The Host class models a physical host running agents, all reporting with the same hostname. OpenStack installers or administrators connect compute nodes to the physical tenant network by connecting the provider bridges to their respective physical NICs. Or, if using tunneling, by configuring an IP address on the appropriate physical NIC. The Host class does the same with the connect_* methods. TODO(amuller): Add start/stop/restart methods that will start/stop/restart all of the agents on this host. Add a kill method that stops all agents and disconnects the host from other hosts. """ def __init__(self, env_desc, host_desc, test_name, neutron_config, central_data_bridge, central_external_bridge): self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_config = neutron_config self.central_data_bridge = central_data_bridge self.central_external_bridge = central_external_bridge self.host_namespace = None self.agents = {} # we need to cache already created "per network" bridges if linuxbridge # agent is used on host: self.network_bridges = {} def _setUp(self): self.local_ip = self.allocate_local_ip() if self.host_desc.l2_agent_type == constants.AGENT_TYPE_OVS: self.setup_host_with_ovs_agent() elif self.host_desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE: self.setup_host_with_linuxbridge_agent() if self.host_desc.l3_agent: self.l3_agent = self.useFixture( process.L3AgentFixture( self.env_desc, self.host_desc, self.test_name, self.neutron_config, self.l3_agent_cfg_fixture)) if self.host_desc.dhcp_agent: self.dhcp_agent = self.useFixture( process.DhcpAgentFixture( self.env_desc, self.host_desc, self.test_name, self.neutron_config, self.dhcp_agent_cfg_fixture, namespace=self.host_namespace)) def setup_host_with_ovs_agent(self): agent_cfg_fixture = config.OVSConfigFixture( self.env_desc, self.host_desc, self.neutron_config.temp_dir, self.local_ip) self.useFixture(agent_cfg_fixture) if self.env_desc.tunneling_enabled: self.useFixture( net_helpers.OVSBridgeFixture( agent_cfg_fixture.get_br_tun_name())).bridge self.connect_to_internal_network_via_tunneling() else: self.br_phys = self.useFixture( net_helpers.OVSBridgeFixture( agent_cfg_fixture.get_br_phys_name())).bridge self.connect_to_internal_network_via_vlans(self.br_phys) self.ovs_agent = self.useFixture( process.OVSAgentFixture( self.env_desc, self.host_desc, self.test_name, self.neutron_config, agent_cfg_fixture)) if self.host_desc.l3_agent: self.l3_agent_cfg_fixture = self.useFixture( config.L3ConfigFixture( self.env_desc, self.host_desc, self.neutron_config.temp_dir, self.ovs_agent.agent_cfg_fixture.get_br_int_name())) br_ex = self.useFixture( net_helpers.OVSBridgeFixture( self.l3_agent_cfg_fixture.get_external_bridge())).bridge self.connect_to_external_network(br_ex) if self.host_desc.dhcp_agent: self.dhcp_agent_cfg_fixture = self.useFixture( config.DhcpConfigFixture( self.env_desc, self.host_desc, self.neutron_config.temp_dir, self.ovs_agent.agent_cfg_fixture.get_br_int_name())) def setup_host_with_linuxbridge_agent(self): #First we need to provide connectivity for agent to prepare proper #bridge mappings in agent's config: self.host_namespace = self.useFixture( net_helpers.NamespaceFixture(prefix="host-") ).name self.connect_namespace_to_control_network() agent_cfg_fixture = config.LinuxBridgeConfigFixture( self.env_desc, self.host_desc, self.neutron_config.temp_dir, self.local_ip, physical_device_name=self.host_port.name ) self.useFixture(agent_cfg_fixture) self.linuxbridge_agent = self.useFixture( process.LinuxBridgeAgentFixture( self.env_desc, self.host_desc, self.test_name, self.neutron_config, agent_cfg_fixture, namespace=self.host_namespace ) ) if self.host_desc.l3_agent: self.l3_agent_cfg_fixture = self.useFixture( config.L3ConfigFixture( self.env_desc, self.host_desc, self.neutron_config.temp_dir)) if self.host_desc.dhcp_agent: self.dhcp_agent_cfg_fixture = self.useFixture( config.DhcpConfigFixture( self.env_desc, self.host_desc, self.neutron_config.temp_dir)) def _connect_ovs_port(self, cidr_address): ovs_device = self.useFixture( net_helpers.OVSPortFixture( bridge=self.central_data_bridge, namespace=self.host_namespace)).port # NOTE: This sets an IP address on the host's root namespace # which is cleaned up when the device is deleted. ovs_device.addr.add(cidr_address) return ovs_device def connect_namespace_to_control_network(self): self.host_port = self._connect_ovs_port( common_utils.ip_to_cidr(self.local_ip, 24) ) self.host_port.link.set_up() def connect_to_internal_network_via_tunneling(self): veth_1, veth_2 = self.useFixture( net_helpers.VethFixture()).ports # NOTE: This sets an IP address on the host's root namespace # which is cleaned up when the device is deleted. veth_1.addr.add(common_utils.ip_to_cidr(self.local_ip, 32)) veth_1.link.set_up() veth_2.link.set_up() def connect_to_internal_network_via_vlans(self, host_data_bridge): # If using VLANs as a segmentation device, it's needed to connect # a provider bridge to a centralized, shared bridge. net_helpers.create_patch_ports( self.central_data_bridge, host_data_bridge) def connect_to_external_network(self, host_external_bridge): net_helpers.create_patch_ports( self.central_external_bridge, host_external_bridge) def allocate_local_ip(self): if not self.env_desc.network_range: return str(self.useFixture( ip_address.ExclusiveIPAddress( '240.0.0.1', '240.255.255.254')).address) return str(self.useFixture( ip_address.ExclusiveIPAddress( str(self.env_desc.network_range[2]), str(self.env_desc.network_range[-2]))).address) def get_bridge(self, network_id): if "ovs" in self.agents.keys(): return self.ovs_agent.br_int elif "linuxbridge" in self.agents.keys(): bridge = self.network_bridges.get(network_id, None) if not bridge: br_prefix = lb_agent.LinuxBridgeManager.get_bridge_name( network_id) bridge = self.useFixture( net_helpers.LinuxBridgeFixture( prefix=br_prefix, namespace=self.host_namespace, prefix_is_full_name=True)).bridge self.network_bridges[network_id] = bridge return bridge @property def hostname(self): return self.neutron_config.config.DEFAULT.host @property def l3_agent(self): return self.agents['l3'] @l3_agent.setter def l3_agent(self, agent): self.agents['l3'] = agent @property def dhcp_agent(self): return self.agents['dhcp'] @dhcp_agent.setter def dhcp_agent(self, agent): self.agents['dhcp'] = agent @property def ovs_agent(self): return self.agents['ovs'] @ovs_agent.setter def ovs_agent(self, agent): self.agents['ovs'] = agent @property def linuxbridge_agent(self): return self.agents['linuxbridge'] @linuxbridge_agent.setter def linuxbridge_agent(self, agent): self.agents['linuxbridge'] = agent @property def l2_agent(self): if self.host_desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE: return self.linuxbridge_agent elif self.host_desc.l2_agent_type == constants.AGENT_TYPE_OVS: return self.ovs_agent class Environment(fixtures.Fixture): """Represents a deployment topology. Environment is a collection of hosts. It starts a Neutron server and a parametrized number of Hosts, each a collection of agents. The Environment accepts a collection of HostDescription, each describing the type of Host to create. """ def __init__(self, env_desc, hosts_desc): """ :param env_desc: An EnvironmentDescription instance. :param hosts_desc: A list of HostDescription instances. """ super(Environment, self).__init__() self.env_desc = env_desc self.hosts_desc = hosts_desc self.hosts = [] def wait_until_env_is_up(self): common_utils.wait_until_true( self._processes_are_ready, timeout=180, sleep=10) def _processes_are_ready(self): try: running_agents = self.neutron_server.client.list_agents()['agents'] agents_count = sum(len(host.agents) for host in self.hosts) return len(running_agents) == agents_count except nc_exc.NeutronClientException: return False def _create_host(self, host_desc): temp_dir = self.useFixture(fixtures.TempDir()).path neutron_config = config.NeutronConfigFixture( self.env_desc, host_desc, temp_dir, cfg.CONF.database.connection, self.rabbitmq_environment) self.useFixture(neutron_config) return self.useFixture( Host(self.env_desc, host_desc, self.test_name, neutron_config, self.central_data_bridge, self.central_external_bridge)) def _setUp(self): self.temp_dir = self.useFixture(fixtures.TempDir()).path #we need this bridge before rabbit and neutron service will start self.central_data_bridge = self.useFixture( net_helpers.OVSBridgeFixture('cnt-data')).bridge self.central_external_bridge = self.useFixture( net_helpers.OVSBridgeFixture('cnt-ex')).bridge #Get rabbitmq address (and cnt-data network) rabbitmq_ip_address = self._configure_port_for_rabbitmq() self.rabbitmq_environment = self.useFixture( process.RabbitmqEnvironmentFixture(host=rabbitmq_ip_address) ) plugin_cfg_fixture = self.useFixture( config.ML2ConfigFixture( self.env_desc, self.hosts_desc, self.temp_dir, self.env_desc.network_type)) neutron_cfg_fixture = self.useFixture( config.NeutronConfigFixture( self.env_desc, None, self.temp_dir, cfg.CONF.database.connection, self.rabbitmq_environment)) self.neutron_server = self.useFixture( process.NeutronServerFixture( self.env_desc, None, self.test_name, neutron_cfg_fixture, plugin_cfg_fixture)) self.hosts = [self._create_host(desc) for desc in self.hosts_desc] self.wait_until_env_is_up() def _configure_port_for_rabbitmq(self): self.env_desc.network_range = self._get_network_range() if not self.env_desc.network_range: return "127.0.0.1" rabbitmq_ip = str(self.env_desc.network_range[1]) rabbitmq_port = ip_lib.IPDevice(self.central_data_bridge.br_name) rabbitmq_port.addr.add(common_utils.ip_to_cidr(rabbitmq_ip, 24)) rabbitmq_port.link.set_up() return rabbitmq_ip def _get_network_range(self): #NOTE(slaweq): We need to choose IP address on which rabbitmq will be # available because LinuxBridge agents are spawned in their own # namespaces and need to know where the rabbitmq server is listening. # For ovs agent it is not necessary because agents are spawned in # globalscope together with rabbitmq server so default localhost # address is fine for them for desc in self.hosts_desc: if desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE: return self.useFixture( ip_network.ExclusiveIPNetwork( "240.0.0.0", "240.255.255.255", "24")).network neutron-12.1.1/neutron/tests/fullstack/resources/process.py0000664000175000017500000003075213553660047024216 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime from distutils import spawn import os import re import signal import fixtures from neutronclient.common import exceptions as nc_exc from neutronclient.v2_0 import client from oslo_log import log as logging from oslo_utils import fileutils from neutron.agent.linux import async_process from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.common import utils as common_utils from neutron.tests import base from neutron.tests.common import net_helpers from neutron.tests.fullstack import base as fullstack_base LOG = logging.getLogger(__name__) class ProcessFixture(fixtures.Fixture): def __init__(self, test_name, process_name, exec_name, config_filenames, namespace=None, kill_signal=signal.SIGKILL): super(ProcessFixture, self).__init__() self.test_name = test_name self.process_name = process_name self.exec_name = exec_name self.config_filenames = config_filenames self.process = None self.kill_signal = kill_signal self.namespace = namespace def _setUp(self): self.start() self.addCleanup(self.stop) def start(self): test_name = base.sanitize_log_path(self.test_name) log_dir = os.path.join(fullstack_base.DEFAULT_LOG_DIR, test_name) fileutils.ensure_tree(log_dir, mode=0o755) timestamp = datetime.datetime.now().strftime("%Y-%m-%d--%H-%M-%S-%f") log_file = "%s--%s.log" % (self.process_name, timestamp) run_as_root = bool(self.namespace) exec_name = (self.exec_name if run_as_root else spawn.find_executable(self.exec_name)) cmd = [exec_name, '--log-dir', log_dir, '--log-file', log_file] for filename in self.config_filenames: cmd += ['--config-file', filename] self.process = async_process.AsyncProcess( cmd, run_as_root=run_as_root, namespace=self.namespace ) self.process.start(block=True) LOG.debug("Process started: %s", self.process_name) def stop(self, kill_signal=None): kill_signal = kill_signal or self.kill_signal try: self.process.stop( block=True, kill_signal=kill_signal, kill_timeout=15) except async_process.AsyncProcessException as e: if "Process is not running" not in str(e): raise LOG.debug("Process stopped: %s", self.process_name) def restart(self, executor=None): def _restart(): self.stop() self.start() LOG.debug("Restarting process: %s", self.process_name) if executor is None: _restart() else: return executor.submit(_restart) class RabbitmqEnvironmentFixture(fixtures.Fixture): def __init__(self, host="127.0.0.1"): super(RabbitmqEnvironmentFixture, self).__init__() self.host = host def _setUp(self): self.user = common_utils.get_rand_name(prefix='user') self.password = common_utils.get_rand_name(prefix='pass') self.vhost = common_utils.get_rand_name(prefix='vhost') self._execute('add_user', self.user, self.password) self.addCleanup(self._execute, 'delete_user', self.user) self._execute('add_vhost', self.vhost) self.addCleanup(self._execute, 'delete_vhost', self.vhost) self._execute('set_permissions', '-p', self.vhost, self.user, '.*', '.*', '.*') def _execute(self, *args): cmd = ['rabbitmqctl'] cmd.extend(args) utils.execute(cmd, run_as_root=True) class ServiceFixture(fixtures.Fixture): def restart(self, executor=None): return self.process_fixture.restart(executor=executor) def start(self): return self.process_fixture.start() def stop(self, kill_signal=None): return self.process_fixture.stop(kill_signal=kill_signal) class NeutronServerFixture(ServiceFixture): NEUTRON_SERVER = "neutron-server" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, plugin_cfg_fixture, service_cfg_fixtures=None): super(NeutronServerFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.plugin_cfg_fixture = plugin_cfg_fixture self.service_cfg_fixtures = service_cfg_fixtures def _setUp(self): config_filenames = [self.neutron_cfg_fixture.filename, self.plugin_cfg_fixture.filename] if self.service_cfg_fixtures: config_filenames.extend( [scf.filename for scf in self.service_cfg_fixtures]) self.process_fixture = self.useFixture(ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_SERVER, exec_name=self.NEUTRON_SERVER, config_filenames=config_filenames, kill_signal=signal.SIGTERM)) common_utils.wait_until_true(self.server_is_live) def server_is_live(self): try: self.client.list_networks() return True except nc_exc.NeutronClientException: return False @property def client(self): url = ("http://127.0.0.1:%s" % self.neutron_cfg_fixture.config.DEFAULT.bind_port) return client.Client(auth_strategy="noauth", endpoint_url=url) class OVSAgentFixture(ServiceFixture): NEUTRON_OVS_AGENT = "neutron-openvswitch-agent" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, agent_cfg_fixture): super(OVSAgentFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.neutron_config = self.neutron_cfg_fixture.config self.agent_cfg_fixture = agent_cfg_fixture self.agent_config = agent_cfg_fixture.config def _setUp(self): self.br_int = self.useFixture( net_helpers.OVSBridgeFixture( self.agent_cfg_fixture.get_br_int_name())).bridge config_filenames = [self.neutron_cfg_fixture.filename, self.agent_cfg_fixture.filename] self.process_fixture = self.useFixture(ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_OVS_AGENT, exec_name=spawn.find_executable( 'ovs_agent.py', path=os.path.join(fullstack_base.ROOTDIR, 'cmd')), config_filenames=config_filenames, kill_signal=signal.SIGTERM)) class LinuxBridgeAgentFixture(ServiceFixture): NEUTRON_LINUXBRIDGE_AGENT = "neutron-linuxbridge-agent" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, agent_cfg_fixture, namespace=None): super(LinuxBridgeAgentFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.neutron_config = self.neutron_cfg_fixture.config self.agent_cfg_fixture = agent_cfg_fixture self.agent_config = agent_cfg_fixture.config self.namespace = namespace def _setUp(self): config_filenames = [self.neutron_cfg_fixture.filename, self.agent_cfg_fixture.filename] self.process_fixture = self.useFixture( ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_LINUXBRIDGE_AGENT, exec_name=self.NEUTRON_LINUXBRIDGE_AGENT, config_filenames=config_filenames, namespace=self.namespace ) ) class L3AgentFixture(ServiceFixture): NEUTRON_L3_AGENT = "neutron-l3-agent" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, l3_agent_cfg_fixture, namespace=None): super(L3AgentFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.l3_agent_cfg_fixture = l3_agent_cfg_fixture self.namespace = namespace def _setUp(self): self.plugin_config = self.l3_agent_cfg_fixture.config config_filenames = [self.neutron_cfg_fixture.filename, self.l3_agent_cfg_fixture.filename] # if we execute in namespace as root, then allow rootwrap to find the # executable, otherwise construct full path ourselves if self.namespace: exec_name = 'l3_agent.py' else: exec_name = spawn.find_executable( 'l3_agent.py', path=os.path.join(fullstack_base.ROOTDIR, 'cmd')) self.process_fixture = self.useFixture( ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_L3_AGENT, exec_name=exec_name, config_filenames=config_filenames, namespace=self.namespace ) ) def get_namespace_suffix(self): return self.plugin_config.DEFAULT.test_namespace_suffix class DhcpAgentFixture(fixtures.Fixture): NEUTRON_DHCP_AGENT = "neutron-dhcp-agent" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, agent_cfg_fixture, namespace=None): super(DhcpAgentFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.agent_cfg_fixture = agent_cfg_fixture self.namespace = namespace def _setUp(self): self.plugin_config = self.agent_cfg_fixture.config config_filenames = [self.neutron_cfg_fixture.filename, self.agent_cfg_fixture.filename] # if we execute in namespace as root, then allow rootwrap to find the # executable, otherwise construct full path ourselves if self.namespace: exec_name = 'dhcp_agent.py' else: exec_name = spawn.find_executable( 'dhcp_agent.py', path=os.path.join(fullstack_base.ROOTDIR, 'cmd')) self.process_fixture = self.useFixture( ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_DHCP_AGENT, exec_name=exec_name, config_filenames=config_filenames, namespace=self.namespace ) ) self.dhcp_namespace_pattern = re.compile( r"qdhcp-[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}%s" % self.get_namespace_suffix()) self.addCleanup(self.clean_dhcp_namespaces) def get_agent_hostname(self): return self.neutron_cfg_fixture.config['DEFAULT']['host'] def get_namespace_suffix(self): return self.plugin_config.DEFAULT.test_namespace_suffix def kill(self): self.process_fixture.stop() self.clean_dhcp_namespaces() def clean_dhcp_namespaces(self): """Delete all DHCP namespaces created by DHCP agent. In some tests for DHCP agent HA agents are killed when handling DHCP service for network(s). In such case DHCP namespace is not deleted by DHCP agent and such namespaces are found and deleted using agent's namespace suffix. """ for namespace in ip_lib.list_network_namespaces(): if self.dhcp_namespace_pattern.match(namespace): try: ip_lib.delete_network_namespace(namespace) except RuntimeError: # Continue cleaning even if namespace deletions fails pass neutron-12.1.1/neutron/tests/fullstack/resources/machine.py0000664000175000017500000002022013553660047024131 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from distutils import spawn import itertools import netaddr from neutron_lib.api.definitions import portbindings as pbs from neutron_lib import constants from neutron.agent.linux import async_process from neutron.agent.linux import ip_lib from neutron.common import utils from neutron.tests.common import machine_fixtures from neutron.tests.common import net_helpers FULLSTACK_DHCLIENT_SCRIPT = 'fullstack-dhclient-script' class FakeFullstackMachinesList(list): """A list of items implementing the FakeFullstackMachine interface.""" def block_until_all_boot(self): for vm in self: vm.block_until_boot() def ping_all(self): # Generate an iterable of all unique pairs. For example: # itertools.combinations(range(3), 2) results in: # ((0, 1), (0, 2), (1, 2)) for vm_1, vm_2 in itertools.combinations(self, 2): vm_1.block_until_ping(vm_2.ip) class FakeFullstackMachine(machine_fixtures.FakeMachineBase): NO_RESOLV_CONF_DHCLIENT_SCRIPT_PATH = ( spawn.find_executable(FULLSTACK_DHCLIENT_SCRIPT)) def __init__(self, host, network_id, tenant_id, safe_client, neutron_port=None, bridge_name=None, use_dhcp=False): super(FakeFullstackMachine, self).__init__() self.host = host self.tenant_id = tenant_id self.network_id = network_id self.safe_client = safe_client self.neutron_port = neutron_port self.bridge_name = bridge_name self.use_dhcp = use_dhcp self.dhclient_async = None def _setUp(self): super(FakeFullstackMachine, self)._setUp() self.bridge = self._get_bridge() if not self.neutron_port: self.neutron_port = self.safe_client.create_port( network_id=self.network_id, tenant_id=self.tenant_id, hostname=self.host.hostname) mac_address = self.neutron_port['mac_address'] hybrid_plug = self.neutron_port[pbs.VIF_DETAILS].get( pbs.OVS_HYBRID_PLUG, False) self.bind_port_if_needed() self.port = self.useFixture( net_helpers.PortFixture.get( self.bridge, self.namespace, mac_address, self.neutron_port['id'], hybrid_plug)).port for fixed_ip in self.neutron_port['fixed_ips']: self._configure_ipaddress(fixed_ip) def bind_port_if_needed(self): if self.neutron_port[pbs.VIF_TYPE] == pbs.VIF_TYPE_UNBOUND: self.safe_client.client.update_port( self.neutron_port['id'], {'port': {pbs.HOST_ID: self.host.hostname}}) self.addCleanup(self.safe_client.client.update_port, self.neutron_port['id'], {'port': {pbs.HOST_ID: ''}}) def _get_bridge(self): if self.bridge_name is None: return self.host.get_bridge(self.network_id) agent_type = self.host.host_desc.l2_agent_type if agent_type == constants.AGENT_TYPE_OVS: new_bridge = self.useFixture( net_helpers.OVSTrunkBridgeFixture(self.bridge_name)).bridge else: raise NotImplementedError( "Support for %s agent is not implemented." % agent_type) return new_bridge def _configure_ipaddress(self, fixed_ip): subnet_id = fixed_ip['subnet_id'] subnet = self.safe_client.client.show_subnet(subnet_id) if (netaddr.IPAddress(fixed_ip['ip_address']).version == constants.IP_VERSION_6): # v6Address/default_route is auto-configured. self._ipv6 = fixed_ip['ip_address'] self.gateway_ipv6 = subnet['subnet']['gateway_ip'] else: self._ip = fixed_ip['ip_address'] prefixlen = netaddr.IPNetwork(subnet['subnet']['cidr']).prefixlen self._ip_cidr = '%s/%s' % (self._ip, prefixlen) self.gateway_ip = subnet['subnet']['gateway_ip'] if self.use_dhcp: self._configure_ipaddress_via_dhcp() else: self._configure_static_ipaddress() def _configure_static_ipaddress(self): self.port.addr.add(self.ip_cidr) if self.gateway_ip: net_helpers.set_namespace_gateway(self.port, self.gateway_ip) def _configure_ipaddress_via_dhcp(self): self._start_async_dhclient() self.addCleanup(self._stop_async_dhclient) def _start_async_dhclient(self): cmd = ["dhclient", '-sf', self.NO_RESOLV_CONF_DHCLIENT_SCRIPT_PATH, '--no-pid', '-d', self.port.name] self.dhclient_async = async_process.AsyncProcess( cmd, run_as_root=True, respawn_interval=5, namespace=self.namespace) self.dhclient_async.start() def _stop_async_dhclient(self): if not self.dhclient_async: return try: self.dhclient_async.stop() except async_process.AsyncProcessException: # If it was already stopped than we don't care about it pass @property def ipv6(self): return self._ipv6 @property def ip(self): return self._ip @property def ip_cidr(self): return self._ip_cidr def ip_configured(self): for port_ip in self.port.addr.list(): if port_ip.get('cidr') == self.ip_cidr: return True return False def gateway_configured(self): gateway_info = self.port.route.get_gateway() if not gateway_info: return False return gateway_info.get('gateway') == self.gateway_ip def block_until_boot(self): utils.wait_until_true( lambda: (self.safe_client.client.show_port(self.neutron_port['id']) ['port']['status'] == 'ACTIVE'), sleep=3) def block_until_dhcp_config_done(self): utils.wait_until_true( lambda: self.ip_configured() and self.gateway_configured(), exception=machine_fixtures.FakeMachineException( "Address %s or gateway %s not configured properly on " "port %s" % (self.ip_cidr, self.gateway_ip, self.port.name) ) ) def destroy(self): """Destroy this fake machine. This should simulate deletion of a vm. It doesn't call cleanUp(). """ self.safe_client.client.update_port( self.neutron_port['id'], {'port': {pbs.HOST_ID: ''}} ) # All associated vlan interfaces are deleted too # If VM is connected to Linuxbridge it hasn't got "delete_port" method # and it is not necessary to delete tap port connected to this bridge. # It is veth pair and will be removed together with VM namespace if hasattr(self.bridge, "delete_port"): self.bridge.delete_port(self.port.name) ip_lib.delete_network_namespace(self.namespace) class FakeFullstackTrunkMachine(FakeFullstackMachine): def __init__(self, trunk, *args, **kwargs): super(FakeFullstackTrunkMachine, self).__init__(*args, **kwargs) self.trunk = trunk def add_vlan_interface(self, mac_address, ip_address, segmentation_id): """Add VLAN interface to VM's namespace. :param mac_address: MAC address to be set on VLAN interface. :param ip_address: The IPNetwork instance containing IP address assigned to the interface. :param segmentation_id: VLAN tag added to the interface. """ net_helpers.create_vlan_interface( self.namespace, self.port.name, mac_address, ip_address, segmentation_id) neutron-12.1.1/neutron/tests/fullstack/resources/config.py0000664000175000017500000003277713553660047024016 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import shutil import tempfile import fixtures from neutron_lib import constants from neutron.common import utils from neutron.plugins.ml2.extensions import qos as qos_ext from neutron.tests.common import config_fixtures from neutron.tests.common.exclusive_resources import port from neutron.tests.common import helpers as c_helpers PHYSICAL_NETWORK_NAME = "physnet1" class ConfigFixture(fixtures.Fixture): """A fixture that holds an actual Neutron configuration. Note that 'self.config' is intended to only be updated once, during the constructor, so if this fixture is re-used (setUp is called twice), then the dynamic configuration values won't change. The correct usage is initializing a new instance of the class. """ def __init__(self, env_desc, host_desc, temp_dir, base_filename): super(ConfigFixture, self).__init__() self.config = config_fixtures.ConfigDict() self.env_desc = env_desc self.host_desc = host_desc self.temp_dir = temp_dir self.base_filename = base_filename def _setUp(self): cfg_fixture = config_fixtures.ConfigFileFixture( self.base_filename, self.config, self.temp_dir) self.useFixture(cfg_fixture) self.filename = cfg_fixture.filename def _generate_namespace_suffix(self): return utils.get_rand_name(prefix='test') class NeutronConfigFixture(ConfigFixture): def __init__(self, env_desc, host_desc, temp_dir, connection, rabbitmq_environment): super(NeutronConfigFixture, self).__init__( env_desc, host_desc, temp_dir, base_filename='neutron.conf') self.config.update({ 'DEFAULT': { 'host': self._generate_host(), 'state_path': self._generate_state_path(self.temp_dir), 'api_paste_config': self._generate_api_paste(), 'core_plugin': 'ml2', 'service_plugins': env_desc.service_plugins, 'auth_strategy': 'noauth', 'debug': 'True', 'global_physnet_mtu': env_desc.global_mtu, 'agent_down_time': str(env_desc.agent_down_time), 'transport_url': 'rabbit://%(user)s:%(password)s@%(host)s:5672/%(vhost)s' % {'user': rabbitmq_environment.user, 'password': rabbitmq_environment.password, 'host': rabbitmq_environment.host, 'vhost': rabbitmq_environment.vhost}, }, 'database': { 'connection': connection, }, 'oslo_concurrency': { 'lock_path': '$state_path/lock', }, 'oslo_policy': { 'policy_file': self._generate_policy_json(), }, 'agent': { 'report_interval': str(env_desc.agent_down_time / 2.0), 'log_agent_heartbeats': 'True', }, }) # Set root_helper/root_helper_daemon only when env var is set root_helper = os.environ.get('OS_ROOTWRAP_CMD') if root_helper: self.config['agent']['root_helper'] = root_helper root_helper_daemon = os.environ.get('OS_ROOTWRAP_DAEMON_CMD') if root_helper_daemon: self.config['agent']['root_helper_daemon'] = root_helper_daemon if env_desc.router_scheduler: self.config['DEFAULT']['router_scheduler_driver'] = ( env_desc.router_scheduler) def _setUp(self): self.config['DEFAULT'].update({ 'bind_port': self.useFixture( port.ExclusivePort(constants.PROTO_NAME_TCP)).port }) super(NeutronConfigFixture, self)._setUp() def _generate_host(self): return utils.get_rand_name(prefix='host-') def _generate_state_path(self, temp_dir): # Assume that temp_dir will be removed by the caller self.state_path = tempfile.mkdtemp(prefix='state_path', dir=temp_dir) return self.state_path def _generate_api_paste(self): return c_helpers.find_sample_file('api-paste.ini') def _generate_policy_json(self): return c_helpers.find_sample_file('policy.json') def get_host(self): return self.config['DEFAULT']['host'] class ML2ConfigFixture(ConfigFixture): def __init__(self, env_desc, host_desc, temp_dir, tenant_network_types): super(ML2ConfigFixture, self).__init__( env_desc, host_desc, temp_dir, base_filename='ml2_conf.ini') mechanism_drivers = self.env_desc.mech_drivers if self.env_desc.l2_pop: mechanism_drivers += ',l2population' self.config.update({ 'ml2': { 'tenant_network_types': tenant_network_types, 'mechanism_drivers': mechanism_drivers, }, 'ml2_type_vlan': { 'network_vlan_ranges': PHYSICAL_NETWORK_NAME + ':1000:2999', }, 'ml2_type_gre': { 'tunnel_id_ranges': '1:1000', }, 'ml2_type_vxlan': { 'vni_ranges': '1001:2000', }, }) extension_drivers = ['port_security'] if env_desc.qos: extension_drivers.append(qos_ext.QOS_EXT_DRIVER_ALIAS) self.config['ml2']['extension_drivers'] = ','.join(extension_drivers) class OVSConfigFixture(ConfigFixture): def __init__(self, env_desc, host_desc, temp_dir, local_ip): super(OVSConfigFixture, self).__init__( env_desc, host_desc, temp_dir, base_filename='openvswitch_agent.ini') self.tunneling_enabled = self.env_desc.tunneling_enabled self.config.update({ 'ovs': { 'local_ip': local_ip, 'integration_bridge': self._generate_integration_bridge(), 'of_interface': host_desc.of_interface, }, 'securitygroup': { 'firewall_driver': host_desc.firewall_driver, }, 'agent': { 'l2_population': str(self.env_desc.l2_pop), 'arp_responder': str(self.env_desc.arp_responder), } }) if self.tunneling_enabled: self.config['agent'].update({ 'tunnel_types': self.env_desc.network_type}) self.config['ovs'].update({ 'tunnel_bridge': self._generate_tunnel_bridge(), 'int_peer_patch_port': self._generate_int_peer(), 'tun_peer_patch_port': self._generate_tun_peer()}) else: self.config['ovs']['bridge_mappings'] = ( self._generate_bridge_mappings()) if env_desc.qos: self.config['agent']['extensions'] = 'qos' def _setUp(self): if self.config['ovs']['of_interface'] == 'native': self.config['ovs'].update({ 'of_listen_port': self.useFixture( port.ExclusivePort(constants.PROTO_NAME_TCP)).port }) super(OVSConfigFixture, self)._setUp() def _generate_bridge_mappings(self): return '%s:%s' % (PHYSICAL_NETWORK_NAME, utils.get_rand_device_name(prefix='br-eth')) def _generate_integration_bridge(self): return utils.get_rand_device_name(prefix='br-int') def _generate_tunnel_bridge(self): return utils.get_rand_device_name(prefix='br-tun') def _generate_int_peer(self): return utils.get_rand_device_name(prefix='patch-tun') def _generate_tun_peer(self): return utils.get_rand_device_name(prefix='patch-int') def get_br_int_name(self): return self.config.ovs.integration_bridge def get_br_phys_name(self): return self.config.ovs.bridge_mappings.split(':')[1] def get_br_tun_name(self): return self.config.ovs.tunnel_bridge class LinuxBridgeConfigFixture(ConfigFixture): def __init__(self, env_desc, host_desc, temp_dir, local_ip, physical_device_name): super(LinuxBridgeConfigFixture, self).__init__( env_desc, host_desc, temp_dir, base_filename="linuxbridge_agent.ini" ) self.config.update({ 'VXLAN': { 'enable_vxlan': str(self.env_desc.tunneling_enabled), 'local_ip': local_ip, 'l2_population': str(self.env_desc.l2_pop), }, 'securitygroup': { 'firewall_driver': host_desc.firewall_driver, } }) if env_desc.qos: self.config.update({ 'AGENT': { 'extensions': 'qos' } }) if self.env_desc.tunneling_enabled: self.config.update({ 'LINUX_BRIDGE': { 'bridge_mappings': self._generate_bridge_mappings( physical_device_name ) } }) else: self.config.update({ 'LINUX_BRIDGE': { 'physical_interface_mappings': self._generate_bridge_mappings( physical_device_name ) } }) def _generate_bridge_mappings(self, device_name): return '%s:%s' % (PHYSICAL_NETWORK_NAME, device_name) class L3ConfigFixture(ConfigFixture): def __init__(self, env_desc, host_desc, temp_dir, integration_bridge=None): super(L3ConfigFixture, self).__init__( env_desc, host_desc, temp_dir, base_filename='l3_agent.ini') if host_desc.l2_agent_type == constants.AGENT_TYPE_OVS: self._prepare_config_with_ovs_agent(integration_bridge) elif host_desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE: self._prepare_config_with_linuxbridge_agent() if host_desc.l3_agent_mode: self.config['DEFAULT'].update({ 'agent_mode': host_desc.l3_agent_mode}) self.config['DEFAULT'].update({ 'debug': 'True', 'test_namespace_suffix': self._generate_namespace_suffix(), }) if host_desc.availability_zone: self.config['agent'].update({ 'availability_zone': host_desc.availability_zone }) def _prepare_config_with_ovs_agent(self, integration_bridge): self.config.update({ 'DEFAULT': { 'interface_driver': ('neutron.agent.linux.interface.' 'OVSInterfaceDriver'), 'ovs_integration_bridge': integration_bridge, 'external_network_bridge': self._generate_external_bridge(), } }) def _prepare_config_with_linuxbridge_agent(self): self.config.update({ 'DEFAULT': { 'interface_driver': ('neutron.agent.linux.interface.' 'BridgeInterfaceDriver'), } }) def _generate_external_bridge(self): return utils.get_rand_device_name(prefix='br-ex') def get_external_bridge(self): return self.config.DEFAULT.external_network_bridge class DhcpConfigFixture(ConfigFixture): def __init__(self, env_desc, host_desc, temp_dir, integration_bridge=None): super(DhcpConfigFixture, self).__init__( env_desc, host_desc, temp_dir, base_filename='dhcp_agent.ini') if host_desc.l2_agent_type == constants.AGENT_TYPE_OVS: self._prepare_config_with_ovs_agent(integration_bridge) elif host_desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE: self._prepare_config_with_linuxbridge_agent() self.config['DEFAULT'].update({ 'debug': 'True', 'dhcp_confs': self._generate_dhcp_path(), 'test_namespace_suffix': self._generate_namespace_suffix() }) if host_desc.availability_zone: self.config['agent'].update({ 'availability_zone': host_desc.availability_zone }) def _setUp(self): super(DhcpConfigFixture, self)._setUp() self.addCleanup(self._clean_dhcp_path) def _prepare_config_with_ovs_agent(self, integration_bridge): self.config.update({ 'DEFAULT': { 'interface_driver': 'openvswitch', 'ovs_integration_bridge': integration_bridge, } }) def _prepare_config_with_linuxbridge_agent(self): self.config.update({ 'DEFAULT': { 'interface_driver': 'linuxbridge', } }) def _generate_dhcp_path(self): # NOTE(slaweq): dhcp_conf path needs to be directory with read # permission for everyone, otherwise dnsmasq process will not be able # to read his configs self.dhcp_path = tempfile.mkdtemp(prefix="dhcp_configs_", dir="/tmp/") os.chmod(self.dhcp_path, 0o755) return self.dhcp_path def _clean_dhcp_path(self): shutil.rmtree(self.dhcp_path, ignore_errors=True) neutron-12.1.1/neutron/tests/fullstack/resources/__init__.py0000664000175000017500000000000013553660046024255 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/tests/fullstack/resources/client.py0000664000175000017500000002562613553660047024022 0ustar zuulzuul00000000000000# Copyright (c) 2015 Thales Services SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import functools import fixtures import netaddr from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutronclient.common import exceptions from neutron.common import utils def _safe_method(f): @functools.wraps(f) def delete(*args, **kwargs): try: return f(*args, **kwargs) except exceptions.NotFound: pass return delete class ClientFixture(fixtures.Fixture): """Manage and cleanup neutron resources.""" def __init__(self, client): super(ClientFixture, self).__init__() self.client = client def _create_resource(self, resource_type, spec): create = getattr(self.client, 'create_%s' % resource_type) delete = getattr(self.client, 'delete_%s' % resource_type) body = {resource_type: spec} resp = create(body=body) data = resp[resource_type] self.addCleanup(_safe_method(delete), data['id']) return data def _update_resource(self, resource_type, id, spec): update = getattr(self.client, 'update_%s' % resource_type) body = {resource_type: spec} resp = update(id, body=body) return resp[resource_type] def _delete_resource(self, resource_type, id): delete = getattr(self.client, 'delete_%s' % resource_type) return delete(id) def create_router(self, tenant_id, name=None, ha=False, external_network=None): resource_type = 'router' name = name or utils.get_rand_name(prefix=resource_type) spec = {'tenant_id': tenant_id, 'name': name, 'ha': ha} if external_network: spec['external_gateway_info'] = {"network_id": external_network} return self._create_resource(resource_type, spec) def create_network(self, tenant_id, name=None, external=False, network_type=None, segmentation_id=None, physical_network=None, mtu=None): resource_type = 'network' name = name or utils.get_rand_name(prefix=resource_type) spec = {'tenant_id': tenant_id, 'name': name} spec['router:external'] = external if segmentation_id is not None: spec['provider:segmentation_id'] = segmentation_id if network_type is not None: spec['provider:network_type'] = network_type if physical_network is not None: spec['provider:physical_network'] = physical_network if mtu is not None: spec['mtu'] = mtu return self._create_resource(resource_type, spec) def update_network(self, id, **kwargs): return self._update_resource('network', id, kwargs) def delete_network(self, id): return self._delete_resource('network', id) def create_subnet(self, tenant_id, network_id, cidr, gateway_ip=None, name=None, enable_dhcp=True, ipv6_address_mode='slaac', ipv6_ra_mode='slaac'): resource_type = 'subnet' name = name or utils.get_rand_name(prefix=resource_type) ip_version = netaddr.IPNetwork(cidr).version spec = {'tenant_id': tenant_id, 'network_id': network_id, 'name': name, 'cidr': cidr, 'enable_dhcp': enable_dhcp, 'ip_version': ip_version} if ip_version == constants.IP_VERSION_6: spec['ipv6_address_mode'] = ipv6_address_mode spec['ipv6_ra_mode'] = ipv6_ra_mode if gateway_ip: spec['gateway_ip'] = gateway_ip return self._create_resource(resource_type, spec) def create_port(self, tenant_id, network_id, hostname=None, qos_policy_id=None, security_groups=None, **kwargs): spec = { 'network_id': network_id, 'tenant_id': tenant_id, } spec.update(kwargs) if hostname is not None: spec[portbindings.HOST_ID] = hostname if qos_policy_id: spec['qos_policy_id'] = qos_policy_id if security_groups: spec['security_groups'] = security_groups return self._create_resource('port', spec) def update_port(self, port_id, **kwargs): return self._update_resource('port', port_id, kwargs) def create_floatingip(self, tenant_id, floating_network_id, fixed_ip_address, port_id): spec = { 'floating_network_id': floating_network_id, 'tenant_id': tenant_id, 'fixed_ip_address': fixed_ip_address, 'port_id': port_id } return self._create_resource('floatingip', spec) def add_router_interface(self, router_id, subnet_id): body = {'subnet_id': subnet_id} router_interface_info = self.client.add_interface_router( router=router_id, body=body) self.addCleanup(_safe_method(self.client.remove_interface_router), router=router_id, body=body) return router_interface_info def create_qos_policy(self, tenant_id, name, description, shared, is_default): policy = self.client.create_qos_policy( body={'policy': {'name': name, 'description': description, 'shared': shared, 'tenant_id': tenant_id, 'is_default': is_default}}) def detach_and_delete_policy(): qos_policy_id = policy['policy']['id'] ports_with_policy = self.client.list_ports( qos_policy_id=qos_policy_id)['ports'] for port in ports_with_policy: self.client.update_port( port['id'], body={'port': {'qos_policy_id': None}}) self.client.delete_qos_policy(qos_policy_id) # NOTE: We'll need to add support for detaching from network once # create_network() supports qos_policy_id. self.addCleanup(_safe_method(detach_and_delete_policy)) return policy['policy'] def create_bandwidth_limit_rule(self, tenant_id, qos_policy_id, limit=None, burst=None, direction=None): rule = {'tenant_id': tenant_id} if limit: rule['max_kbps'] = limit if burst: rule['max_burst_kbps'] = burst if direction: rule['direction'] = direction rule = self.client.create_bandwidth_limit_rule( policy=qos_policy_id, body={'bandwidth_limit_rule': rule}) self.addCleanup(_safe_method(self.client.delete_bandwidth_limit_rule), rule['bandwidth_limit_rule']['id'], qos_policy_id) return rule['bandwidth_limit_rule'] def create_dscp_marking_rule(self, tenant_id, qos_policy_id, dscp_mark=0): rule = {'tenant_id': tenant_id} if dscp_mark: rule['dscp_mark'] = dscp_mark rule = self.client.create_dscp_marking_rule( policy=qos_policy_id, body={'dscp_marking_rule': rule}) self.addCleanup(_safe_method(self.client.delete_dscp_marking_rule), rule['dscp_marking_rule']['id'], qos_policy_id) return rule['dscp_marking_rule'] def create_trunk(self, tenant_id, port_id, name=None, admin_state_up=None, sub_ports=None): """Create a trunk via API. :param tenant_id: ID of the tenant. :param port_id: Parent port of trunk. :param name: Name of the trunk. :param admin_state_up: Admin state of the trunk. :param sub_ports: List of subport dictionaries in format {'port_id': , 'segmentation_type': 'vlan', 'segmentation_id': } :return: Dictionary with trunk's data returned from Neutron API. """ spec = { 'port_id': port_id, 'tenant_id': tenant_id, } if name is not None: spec['name'] = name if sub_ports is not None: spec['sub_ports'] = sub_ports if admin_state_up is not None: spec['admin_state_up'] = admin_state_up trunk = self.client.create_trunk({'trunk': spec})['trunk'] if sub_ports: self.addCleanup( _safe_method(self.trunk_remove_subports), tenant_id, trunk['id'], trunk['sub_ports']) self.addCleanup(_safe_method(self.client.delete_trunk), trunk['id']) return trunk def trunk_add_subports(self, tenant_id, trunk_id, sub_ports): """Add subports to the trunk. :param tenant_id: ID of the tenant. :param trunk_id: ID of the trunk. :param sub_ports: List of subport dictionaries to be added in format {'port_id': , 'segmentation_type': 'vlan', 'segmentation_id': } """ spec = { 'tenant_id': tenant_id, 'sub_ports': sub_ports, } trunk = self.client.trunk_add_subports(trunk_id, spec) sub_ports_to_remove = [ sub_port for sub_port in trunk['sub_ports'] if sub_port in sub_ports] self.addCleanup( _safe_method(self.trunk_remove_subports), tenant_id, trunk_id, sub_ports_to_remove) def trunk_remove_subports(self, tenant_id, trunk_id, sub_ports): """Remove subports from the trunk. :param trunk_id: ID of the trunk. :param sub_ports: List of subport port IDs. """ spec = { 'tenant_id': tenant_id, 'sub_ports': sub_ports, } return self.client.trunk_remove_subports(trunk_id, spec) def create_security_group(self, tenant_id, name=None): resource_type = 'security_group' name = name or utils.get_rand_name(prefix=resource_type) spec = {'tenant_id': tenant_id, 'name': name} return self._create_resource(resource_type, spec) def create_security_group_rule(self, tenant_id, security_group_id, **kwargs): resource_type = 'security_group_rule' spec = {'tenant_id': tenant_id, 'security_group_id': security_group_id} spec.update(kwargs) return self._create_resource(resource_type, spec) neutron-12.1.1/neutron/tests/fullstack/__init__.py0000664000175000017500000000115413553660046022256 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.common import eventlet_utils eventlet_utils.monkey_patch() neutron-12.1.1/neutron/tests/fullstack/test_mtu.py0000664000175000017500000000452413553660046022367 0ustar zuulzuul00000000000000# Copyright 2017 NEC India # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from neutronclient.common import exceptions from oslo_utils import uuidutils from neutron.tests.fullstack import base from neutron.tests.fullstack.resources import environment from neutron.tests.unit import testlib_api load_tests = testlib_api.module_load_tests class MTUNetworkTestSetup(base.BaseFullStackTestCase): host_desc = [] # No need to register agents for this test case def setUp(self): env = environment.Environment( environment.EnvironmentDescription(), self.host_desc) super(MTUNetworkTestSetup, self).setUp(env) self.tenant_id = uuidutils.generate_uuid() def _restart_neutron_server(self, global_mtu): env = environment.Environment( environment.EnvironmentDescription(global_mtu=global_mtu), self.host_desc) env.test_name = self.get_name() self.useFixture(env) env.neutron_server.restart() class TestMTUScenarios(MTUNetworkTestSetup): def test_mtu_update_delete_network(self): network = self.safe_client.create_network(self.tenant_id, name='mtu-test-network', mtu=1450) self.safe_client.update_network(network['id'], mtu=9000) res = self.safe_client.delete_network(network['id']) self.assertEqual((), res) def test_global_physnet_mtu_update_delete_network(self): network = self.safe_client.create_network(self.tenant_id, name='mtu-test-network', mtu=1450) self._restart_neutron_server(1400) res = self.safe_client.delete_network(network['id']) self.assertEqual((), res) neutron-12.1.1/neutron/tests/fullstack/test_securitygroup.py0000664000175000017500000004003413553660047024503 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from oslo_utils import uuidutils from neutron.cmd.sanity import checks from neutron.common import utils as common_utils from neutron.tests import base as tests_base from neutron.tests.common import net_helpers from neutron.tests.fullstack import base from neutron.tests.fullstack.resources import environment from neutron.tests.fullstack.resources import machine from neutron.tests.unit import testlib_api load_tests = testlib_api.module_load_tests class OVSVersionChecker(object): conntrack_supported = None @classmethod def supports_ovsfirewall(cls): if cls.conntrack_supported is None: cls.conntrack_supported = checks.ovs_conntrack_supported() return cls.conntrack_supported class BaseSecurityGroupsSameNetworkTest(base.BaseFullStackTestCase): of_interface = None def setUp(self): host_descriptions = [ environment.HostDescription( of_interface=self.of_interface, l2_agent_type=self.l2_agent_type, firewall_driver=self.firewall_driver, dhcp_agent=True) for _ in range(self.num_hosts)] env = environment.Environment( environment.EnvironmentDescription( network_type=self.network_type), host_descriptions) super(BaseSecurityGroupsSameNetworkTest, self).setUp(env) if (self.firewall_driver == 'openvswitch' and not OVSVersionChecker.supports_ovsfirewall()): self.skipTest("Open vSwitch firewall_driver doesn't work " "with this version of ovs.") def assert_connection(self, *args, **kwargs): netcat = net_helpers.NetcatTester(*args, **kwargs) def test_connectivity(): try: return netcat.test_connectivity() except RuntimeError: return False try: common_utils.wait_until_true(test_connectivity) finally: netcat.stop_processes() def assert_no_connection(self, *args, **kwargs): netcat = net_helpers.NetcatTester(*args, **kwargs) try: common_utils.wait_until_true(netcat.test_no_connectivity) finally: netcat.stop_processes() class TestSecurityGroupsSameNetwork(BaseSecurityGroupsSameNetworkTest): network_type = 'vxlan' scenarios = [ # The iptables_hybrid driver lacks isolation between agents and # because of that using only one host is enough ('ovs-hybrid', { 'firewall_driver': 'iptables_hybrid', 'of_interface': 'native', 'l2_agent_type': constants.AGENT_TYPE_OVS, 'num_hosts': 1}), ('ovs-openflow-cli', { 'firewall_driver': 'openvswitch', 'of_interface': 'ovs-ofctl', 'l2_agent_type': constants.AGENT_TYPE_OVS, 'num_hosts': 2}), ('ovs-openflow-native', { 'firewall_driver': 'openvswitch', 'of_interface': 'native', 'l2_agent_type': constants.AGENT_TYPE_OVS, 'num_hosts': 2}), ('linuxbridge-iptables', { 'firewall_driver': 'iptables', 'l2_agent_type': constants.AGENT_TYPE_LINUXBRIDGE, 'num_hosts': 2})] # NOTE(toshii): As a firewall_driver can interfere with others, # the recommended way to add test is to expand this method, not # adding another. @tests_base.unstable_test("bug 1744402") def test_securitygroup(self): """Tests if a security group rules are working, by confirming that 0. traffic is allowed when port security is disabled, 1. connection from outside of allowed security group is blocked 2. connection from allowed security group is permitted 3. traffic not explicitly allowed (eg. ICMP) is blocked, 4. a security group update takes effect, 5. a remote security group member addition works, and 6. an established connection stops by deleting a SG rule. 7. multiple overlapping remote rules work, 8. test other protocol functionality by using SCTP protocol 9. test two vms with same mac on the same host in different networks """ index_to_sg = [0, 0, 1, 2] if self.firewall_driver == 'iptables_hybrid': # The iptables_hybrid driver lacks isolation between agents index_to_host = [0] * 4 else: index_to_host = [0, 1, 1, 0] tenant_uuid = uuidutils.generate_uuid() network = self.safe_client.create_network(tenant_uuid) self.safe_client.create_subnet( tenant_uuid, network['id'], '20.0.0.0/24') sgs = [self.safe_client.create_security_group(tenant_uuid) for i in range(3)] ports = [ self.safe_client.create_port(tenant_uuid, network['id'], self.environment.hosts[host].hostname, security_groups=[], port_security_enabled=False) for host in index_to_host] self.safe_client.create_security_group_rule( tenant_uuid, sgs[0]['id'], remote_group_id=sgs[0]['id'], direction='ingress', ethertype=constants.IPv4, protocol=constants.PROTO_NAME_TCP, port_range_min=3333, port_range_max=3333) vms = [ self.useFixture( machine.FakeFullstackMachine( self.environment.hosts[host], network['id'], tenant_uuid, self.safe_client, neutron_port=ports[port], use_dhcp=True)) for port, host in enumerate(index_to_host)] for vm in vms: vm.block_until_boot() vm.block_until_dhcp_config_done() # 0. check that traffic is allowed when port security is disabled self.assert_connection( vms[1].namespace, vms[0].namespace, vms[0].ip, 3333, net_helpers.NetcatTester.TCP) self.assert_connection( vms[2].namespace, vms[0].namespace, vms[0].ip, 3333, net_helpers.NetcatTester.TCP) vms[0].block_until_ping(vms[1].ip) vms[0].block_until_ping(vms[2].ip) vms[1].block_until_ping(vms[2].ip) # Apply security groups to the ports for port, sg in zip(ports, index_to_sg): self.safe_client.client.update_port( port['id'], body={'port': {'port_security_enabled': True, 'security_groups': [sgs[sg]['id']]}}) # 1. connection from outside of allowed security group is blocked netcat = net_helpers.NetcatTester( vms[2].namespace, vms[0].namespace, vms[0].ip, 3333, net_helpers.NetcatTester.TCP) # Wait until port update takes effect on the ports common_utils.wait_until_true( netcat.test_no_connectivity, exception=AssertionError( "Still can connect to the VM from different host.") ) netcat.stop_processes() # 2. check if connection from allowed security group is permitted self.assert_connection( vms[1].namespace, vms[0].namespace, vms[0].ip, 3333, net_helpers.NetcatTester.TCP) # 3. check if traffic not explicitly allowed (eg. ICMP) is blocked vms[0].block_until_no_ping(vms[1].ip) vms[0].block_until_no_ping(vms[2].ip) vms[1].block_until_no_ping(vms[2].ip) # 4. check if a security group update takes effect self.assert_no_connection( vms[1].namespace, vms[0].namespace, vms[0].ip, 3344, net_helpers.NetcatTester.TCP) self.safe_client.create_security_group_rule( tenant_uuid, sgs[0]['id'], remote_group_id=sgs[0]['id'], direction='ingress', ethertype=constants.IPv4, protocol=constants.PROTO_NAME_TCP, port_range_min=3344, port_range_max=3344) self.assert_connection( vms[1].namespace, vms[0].namespace, vms[0].ip, 3344, net_helpers.NetcatTester.TCP) # 5. check if a remote security group member addition works rule2 = self.safe_client.create_security_group_rule( tenant_uuid, sgs[0]['id'], remote_group_id=sgs[1]['id'], direction='ingress', ethertype=constants.IPv4, protocol=constants.PROTO_NAME_TCP, port_range_min=3355, port_range_max=3355) self.assert_connection( vms[2].namespace, vms[0].namespace, vms[0].ip, 3355, net_helpers.NetcatTester.TCP) # 6. check if an established connection stops by deleting # the supporting SG rule. index_to_host.append(index_to_host[2]) index_to_sg.append(1) ports.append( self.safe_client.create_port(tenant_uuid, network['id'], self.environment.hosts[ index_to_host[-1]].hostname, security_groups=[sgs[1]['id']])) vms.append( self.useFixture( machine.FakeFullstackMachine( self.environment.hosts[index_to_host[-1]], network['id'], tenant_uuid, self.safe_client, neutron_port=ports[-1], use_dhcp=True))) self.assertEqual(5, len(vms)) vms[4].block_until_boot() netcat = net_helpers.NetcatTester(vms[4].namespace, vms[0].namespace, vms[0].ip, 3355, net_helpers.NetcatTester.TCP) self.addCleanup(netcat.stop_processes) self.assertTrue(netcat.test_connectivity()) self.client.delete_security_group_rule(rule2['id']) common_utils.wait_until_true(lambda: netcat.test_no_connectivity(), sleep=8) netcat.stop_processes() # 7. check if multiple overlapping remote rules work self.safe_client.create_security_group_rule( tenant_uuid, sgs[0]['id'], remote_group_id=sgs[1]['id'], direction='ingress', ethertype=constants.IPv4, protocol=constants.PROTO_NAME_TCP, port_range_min=3333, port_range_max=3333) self.safe_client.create_security_group_rule( tenant_uuid, sgs[0]['id'], remote_group_id=sgs[2]['id'], direction='ingress', ethertype=constants.IPv4) for i in range(2): self.assert_connection( vms[0].namespace, vms[1].namespace, vms[1].ip, 3333, net_helpers.NetcatTester.TCP) self.assert_connection( vms[2].namespace, vms[1].namespace, vms[1].ip, 3333, net_helpers.NetcatTester.TCP) self.assert_connection( vms[3].namespace, vms[0].namespace, vms[0].ip, 8080, net_helpers.NetcatTester.TCP) # 8. check SCTP is supported by security group self.assert_no_connection( vms[1].namespace, vms[0].namespace, vms[0].ip, 3366, net_helpers.NetcatTester.SCTP) self.safe_client.create_security_group_rule( tenant_uuid, sgs[0]['id'], remote_group_id=sgs[0]['id'], direction='ingress', ethertype=constants.IPv4, protocol=constants.PROTO_NUM_SCTP, port_range_min=3366, port_range_max=3366) self.assert_connection( vms[1].namespace, vms[0].namespace, vms[0].ip, 3366, net_helpers.NetcatTester.SCTP) # 9. test two vms with same mac on the same host in different networks self._test_overlapping_mac_addresses() def _create_vm_on_host( self, project_id, network_id, sg_id, host, mac_address=None): if mac_address: port = self.safe_client.create_port( project_id, network_id, host.hostname, security_groups=[sg_id], mac_address=mac_address) else: port = self.safe_client.create_port( project_id, network_id, host.hostname, security_groups=[sg_id]) return self.useFixture( machine.FakeFullstackMachine( host, network_id, project_id, self.safe_client, neutron_port=port)) def _create_three_vms_first_has_static_mac( self, project_id, allowed_port, subnet_cidr): """Create three vms. First VM has a static mac and is placed on first host. Second VM is placed on the first host and third VM is placed on second host. """ network = self.safe_client.create_network(project_id) self.safe_client.create_subnet( project_id, network['id'], subnet_cidr) sg = self.safe_client.create_security_group(project_id) self.safe_client.create_security_group_rule( project_id, sg['id'], direction='ingress', ethertype=constants.IPv4, protocol=constants.PROTO_NAME_TCP, port_range_min=allowed_port, port_range_max=allowed_port) vms = [self._create_vm_on_host( project_id, network['id'], sg['id'], self.environment.hosts[0], mac_address="fa:16:3e:de:ad:fe")] if self.firewall_driver == 'iptables_hybrid': # iptables lack isolation between agents, use only a single host vms.extend([ self._create_vm_on_host( project_id, network['id'], sg['id'], self.environment.hosts[0]) for _ in range(2)]) else: vms.extend([ self._create_vm_on_host( project_id, network['id'], sg['id'], host) for host in self.environment.hosts[:2]]) map(lambda vm: vm.block_until_boot(), vms) return vms def verify_connectivity_between_vms(self, src_vm, dst_vm, protocol, port): self.assert_connection( src_vm.namespace, dst_vm.namespace, dst_vm.ip, port, protocol) def verify_no_connectivity_between_vms( self, src_vm, dst_vm, protocol, port): self.assert_no_connection( src_vm.namespace, dst_vm.namespace, dst_vm.ip, port, protocol) def _test_overlapping_mac_addresses(self): project1 = uuidutils.generate_uuid() p1_allowed = 4444 project2 = uuidutils.generate_uuid() p2_allowed = 4445 p1_vms = self._create_three_vms_first_has_static_mac( project1, p1_allowed, '20.0.2.0/24') p2_vms = self._create_three_vms_first_has_static_mac( project2, p2_allowed, '20.0.3.0/24') have_connectivity = [ (p1_vms[0], p1_vms[1], p1_allowed), (p1_vms[1], p1_vms[2], p1_allowed), (p2_vms[0], p2_vms[1], p2_allowed), (p2_vms[1], p2_vms[2], p2_allowed), ] for vm1, vm2, port in have_connectivity: self.verify_connectivity_between_vms( vm1, vm2, net_helpers.NetcatTester.TCP, port) self.verify_connectivity_between_vms( vm2, vm1, net_helpers.NetcatTester.TCP, port) self.verify_no_connectivity_between_vms( vm1, vm2, net_helpers.NetcatTester.TCP, port + 1) self.verify_no_connectivity_between_vms( vm2, vm1, net_helpers.NetcatTester.TCP, port + 1) neutron-12.1.1/neutron/tests/fullstack/README0000664000175000017500000000003713553660046021024 0ustar zuulzuul00000000000000Please see neutron/TESTING.rst.neutron-12.1.1/neutron/tests/fullstack/test_qos.py0000664000175000017500000005003113553660047022357 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools from neutron_lib import constants from neutron_lib.services.qos import constants as qos_consts from neutronclient.common import exceptions from oslo_utils import uuidutils import testscenarios from neutron.agent.common import ovs_lib from neutron.agent.linux import tc_lib from neutron.common import utils from neutron.tests import base as tests_base from neutron.tests.common.agents import l2_extensions from neutron.tests.fullstack import base from neutron.tests.fullstack.resources import environment from neutron.tests.fullstack.resources import machine from neutron.tests.fullstack import utils as fullstack_utils from neutron.tests.unit import testlib_api from neutron.conf.plugins.ml2.drivers import linuxbridge as \ linuxbridge_agent_config from neutron.plugins.ml2.drivers.linuxbridge.agent import \ linuxbridge_neutron_agent as linuxbridge_agent from neutron.services.qos.drivers.openvswitch import driver as ovs_drv load_tests = testlib_api.module_load_tests BANDWIDTH_BURST = 100 BANDWIDTH_LIMIT = 500 DSCP_MARK = 16 class BaseQoSRuleTestCase(object): of_interface = None number_of_hosts = 1 @property def reverse_direction(self): if self.direction == constants.INGRESS_DIRECTION: return constants.EGRESS_DIRECTION elif self.direction == constants.EGRESS_DIRECTION: return constants.INGRESS_DIRECTION def setUp(self): host_desc = [ environment.HostDescription( l3_agent=False, of_interface=self.of_interface, l2_agent_type=self.l2_agent_type ) for _ in range(self.number_of_hosts)] env_desc = environment.EnvironmentDescription( agent_down_time=10, qos=True) env = environment.Environment(env_desc, host_desc) super(BaseQoSRuleTestCase, self).setUp(env) self.tenant_id = uuidutils.generate_uuid() self.network = self.safe_client.create_network(self.tenant_id, 'network-test') self.subnet = self.safe_client.create_subnet( self.tenant_id, self.network['id'], cidr='10.0.0.0/24', gateway_ip='10.0.0.1', name='subnet-test', enable_dhcp=False) def _create_qos_policy(self): return self.safe_client.create_qos_policy( self.tenant_id, 'fs_policy', 'Fullstack testing policy', shared='False', is_default='False') def _prepare_vm_with_qos_policy(self, rule_add_functions): qos_policy = self._create_qos_policy() qos_policy_id = qos_policy['id'] port = self.safe_client.create_port( self.tenant_id, self.network['id'], self.environment.hosts[0].hostname, qos_policy_id) for rule_add in rule_add_functions: rule_add(qos_policy) vm = self.useFixture( machine.FakeFullstackMachine( self.environment.hosts[0], self.network['id'], self.tenant_id, self.safe_client, neutron_port=port)) return vm, qos_policy class _TestBwLimitQoS(BaseQoSRuleTestCase): number_of_hosts = 1 @staticmethod def _get_expected_egress_burst_value(limit): return int( limit * qos_consts.DEFAULT_BURST_RATE ) def _wait_for_bw_rule_removed(self, vm, direction): # No values are provided when port doesn't have qos policy self._wait_for_bw_rule_applied(vm, None, None, direction) def _add_bw_limit_rule(self, limit, burst, direction, qos_policy): qos_policy_id = qos_policy['id'] rule = self.safe_client.create_bandwidth_limit_rule( self.tenant_id, qos_policy_id, limit, burst, direction) # Make it consistent with GET reply rule['type'] = qos_consts.RULE_TYPE_BANDWIDTH_LIMIT rule['qos_policy_id'] = qos_policy_id qos_policy['rules'].append(rule) def test_bw_limit_qos_policy_rule_lifecycle(self): new_limit = BANDWIDTH_LIMIT + 100 # Create port with qos policy attached vm, qos_policy = self._prepare_vm_with_qos_policy( [functools.partial( self._add_bw_limit_rule, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.direction)]) bw_rule = qos_policy['rules'][0] self._wait_for_bw_rule_applied( vm, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.direction) qos_policy_id = qos_policy['id'] self.client.delete_bandwidth_limit_rule(bw_rule['id'], qos_policy_id) self._wait_for_bw_rule_removed(vm, self.direction) # Create new rule with no given burst value, in such case ovs and lb # agent should apply burst value as # bandwidth_limit * qos_consts.DEFAULT_BURST_RATE new_expected_burst = self._get_expected_burst_value(new_limit, self.direction) new_rule = self.safe_client.create_bandwidth_limit_rule( self.tenant_id, qos_policy_id, new_limit, direction=self.direction) self._wait_for_bw_rule_applied( vm, new_limit, new_expected_burst, self.direction) # Update qos policy rule id self.client.update_bandwidth_limit_rule( new_rule['id'], qos_policy_id, body={'bandwidth_limit_rule': {'max_kbps': BANDWIDTH_LIMIT, 'max_burst_kbps': BANDWIDTH_BURST}}) self._wait_for_bw_rule_applied( vm, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.direction) # Remove qos policy from port self.client.update_port( vm.neutron_port['id'], body={'port': {'qos_policy_id': None}}) self._wait_for_bw_rule_removed(vm, self.direction) def test_bw_limit_direction_change(self): # Create port with qos policy attached, with rule self.direction vm, qos_policy = self._prepare_vm_with_qos_policy( [functools.partial( self._add_bw_limit_rule, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.direction)]) bw_rule = qos_policy['rules'][0] self._wait_for_bw_rule_applied( vm, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.direction) # Update rule by changing direction to opposite then it was before self.client.update_bandwidth_limit_rule( bw_rule['id'], qos_policy['id'], body={'bandwidth_limit_rule': { 'direction': self.reverse_direction}}) self._wait_for_bw_rule_removed(vm, self.direction) self._wait_for_bw_rule_applied( vm, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.reverse_direction) def test_bw_limit_qos_l2_agent_restart(self): self.l2_agent_process = self.environment.hosts[0].l2_agent self.l2_agent = self.safe_client.client.list_agents( agent_type=self.l2_agent_type)['agents'][0] # Create port with qos policy attached, with different direction vm, qos_policy = self._prepare_vm_with_qos_policy( [functools.partial( self._add_bw_limit_rule, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.direction), functools.partial( self._add_bw_limit_rule, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.reverse_direction)]) bw_rule_1 = qos_policy['rules'][0] bw_rule_2 = qos_policy['rules'][1] qos_policy_id = qos_policy['id'] self._wait_for_bw_rule_applied( vm, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.direction) self._wait_for_bw_rule_applied( vm, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.reverse_direction) # Stop l2_agent and clear these rules self.l2_agent_process.stop() self._wait_until_agent_down(self.l2_agent['id']) self.client.delete_bandwidth_limit_rule(bw_rule_1['id'], qos_policy_id) self.client.delete_bandwidth_limit_rule(bw_rule_2['id'], qos_policy_id) # Start l2_agent to check if these rules is cleared self.l2_agent_process.start() self._wait_until_agent_up(self.l2_agent['id']) self._wait_for_bw_rule_applied( vm, None, None, self.direction) self._wait_for_bw_rule_applied( vm, None, None, self.reverse_direction) class TestBwLimitQoSOvs(_TestBwLimitQoS, base.BaseFullStackTestCase): l2_agent_type = constants.AGENT_TYPE_OVS direction_scenarios = [ ('ingress', {'direction': constants.INGRESS_DIRECTION}), ('egress', {'direction': constants.EGRESS_DIRECTION}) ] scenarios = testscenarios.multiply_scenarios( direction_scenarios, fullstack_utils.get_ovs_interface_scenarios()) @staticmethod def _get_expected_burst_value(limit, direction): # For egress bandwidth limit this value should be calculated as # bandwidth_limit * qos_consts.DEFAULT_BURST_RATE if direction == constants.EGRESS_DIRECTION: return TestBwLimitQoSOvs._get_expected_egress_burst_value(limit) else: return 0 def _wait_for_bw_rule_applied(self, vm, limit, burst, direction): if direction == constants.EGRESS_DIRECTION: utils.wait_until_true( lambda: vm.bridge.get_egress_bw_limit_for_port( vm.port.name) == (limit, burst)) elif direction == constants.INGRESS_DIRECTION: utils.wait_until_true( lambda: vm.bridge.get_ingress_bw_limit_for_port( vm.port.name) == (limit, burst)) def test_bw_limit_qos_port_removed(self): """Test if rate limit config is properly removed when whole port is removed. """ # Create port with qos policy attached vm, qos_policy = self._prepare_vm_with_qos_policy( [functools.partial( self._add_bw_limit_rule, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.direction)]) self._wait_for_bw_rule_applied( vm, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.direction) # Delete port with qos policy attached vm.destroy() self._wait_for_bw_rule_removed(vm, self.direction) self.assertIsNone(vm.bridge.find_qos(vm.port.name)) self.assertIsNone(vm.bridge.find_queue(vm.port.name, ovs_lib.QOS_DEFAULT_QUEUE)) class TestBwLimitQoSLinuxbridge(_TestBwLimitQoS, base.BaseFullStackTestCase): l2_agent_type = constants.AGENT_TYPE_LINUXBRIDGE scenarios = [ ('egress', {'direction': constants.EGRESS_DIRECTION}), ('ingress', {'direction': constants.INGRESS_DIRECTION}), ] @staticmethod def _get_expected_burst_value(limit, direction): # For egress bandwidth limit this value should be calculated as # bandwidth_limit * qos_consts.DEFAULT_BURST_RATE if direction == constants.EGRESS_DIRECTION: return TestBwLimitQoSLinuxbridge._get_expected_egress_burst_value( limit) else: return TestBwLimitQoSLinuxbridge._get_expected_ingress_burst_value( limit) @staticmethod def _get_expected_ingress_burst_value(limit): # calculate expected burst in same way as it's done in tc_lib but # burst value = 0 so it's always value calculated from kernel's hz # value # as in tc_lib.bits_to_kilobits result is rounded up that even # 1 bit gives 1 kbit same should be added here to expected burst # value return int( float(limit) / float(linuxbridge_agent_config.DEFAULT_KERNEL_HZ_VALUE) + 1) def _wait_for_bw_rule_applied(self, vm, limit, burst, direction): port_name = linuxbridge_agent.LinuxBridgeManager.get_tap_device_name( vm.neutron_port['id']) tc = tc_lib.TcCommand( port_name, linuxbridge_agent_config.DEFAULT_KERNEL_HZ_VALUE, namespace=vm.host.host_namespace ) if direction == constants.EGRESS_DIRECTION: utils.wait_until_true( lambda: tc.get_filters_bw_limits() == (limit, burst)) elif direction == constants.INGRESS_DIRECTION: utils.wait_until_true( lambda: tc.get_tbf_bw_limits() == (limit, burst)) class _TestDscpMarkingQoS(BaseQoSRuleTestCase): number_of_hosts = 2 def _wait_for_dscp_marking_rule_removed(self, vm): self._wait_for_dscp_marking_rule_applied(vm, None) def _add_dscp_rule(self, dscp_mark, qos_policy): qos_policy_id = qos_policy['id'] rule = self.safe_client.create_dscp_marking_rule( self.tenant_id, qos_policy_id, dscp_mark) # Make it consistent with GET reply rule['type'] = qos_consts.RULE_TYPE_DSCP_MARKING rule['qos_policy_id'] = qos_policy_id qos_policy['rules'].append(rule) def test_dscp_qos_policy_rule_lifecycle(self): new_dscp_mark = DSCP_MARK + 8 # Create port with qos policy attached vm, qos_policy = self._prepare_vm_with_qos_policy( [functools.partial(self._add_dscp_rule, DSCP_MARK)]) dscp_rule = qos_policy['rules'][0] self._wait_for_dscp_marking_rule_applied(vm, DSCP_MARK) qos_policy_id = qos_policy['id'] self.client.delete_dscp_marking_rule(dscp_rule['id'], qos_policy_id) self._wait_for_dscp_marking_rule_removed(vm) # Create new rule new_rule = self.safe_client.create_dscp_marking_rule( self.tenant_id, qos_policy_id, new_dscp_mark) self._wait_for_dscp_marking_rule_applied(vm, new_dscp_mark) # Update qos policy rule id self.client.update_dscp_marking_rule( new_rule['id'], qos_policy_id, body={'dscp_marking_rule': {'dscp_mark': DSCP_MARK}}) self._wait_for_dscp_marking_rule_applied(vm, DSCP_MARK) # Remove qos policy from port self.client.update_port( vm.neutron_port['id'], body={'port': {'qos_policy_id': None}}) self._wait_for_dscp_marking_rule_removed(vm) @tests_base.unstable_test("bug 1733649") def test_dscp_marking_packets(self): # Create port (vm) which will be used to received and test packets receiver_port = self.safe_client.create_port( self.tenant_id, self.network['id'], self.environment.hosts[1].hostname) receiver = self.useFixture( machine.FakeFullstackMachine( self.environment.hosts[1], self.network['id'], self.tenant_id, self.safe_client, neutron_port=receiver_port)) # Create port with qos policy attached sender, qos_policy = self._prepare_vm_with_qos_policy( [functools.partial(self._add_dscp_rule, DSCP_MARK)]) sender.block_until_boot() receiver.block_until_boot() self._wait_for_dscp_marking_rule_applied(sender, DSCP_MARK) l2_extensions.wait_for_dscp_marked_packet( sender, receiver, DSCP_MARK) def test_dscp_marking_clean_port_removed(self): """Test if DSCP marking OpenFlow/iptables rules are removed when whole port is removed. """ # Create port with qos policy attached vm, qos_policy = self._prepare_vm_with_qos_policy( [functools.partial(self._add_dscp_rule, DSCP_MARK)]) self._wait_for_dscp_marking_rule_applied(vm, DSCP_MARK) # Delete port with qos policy attached vm.destroy() self._wait_for_dscp_marking_rule_removed(vm) class TestDscpMarkingQoSOvs(_TestDscpMarkingQoS, base.BaseFullStackTestCase): scenarios = fullstack_utils.get_ovs_interface_scenarios() l2_agent_type = constants.AGENT_TYPE_OVS def _wait_for_dscp_marking_rule_applied(self, vm, dscp_mark): l2_extensions.wait_until_dscp_marking_rule_applied_ovs( vm.bridge, vm.port.name, dscp_mark) class TestDscpMarkingQoSLinuxbridge(_TestDscpMarkingQoS, base.BaseFullStackTestCase): l2_agent_type = constants.AGENT_TYPE_LINUXBRIDGE def _wait_for_dscp_marking_rule_applied(self, vm, dscp_mark): l2_extensions.wait_until_dscp_marking_rule_applied_linuxbridge( vm.host.host_namespace, vm.port.name, dscp_mark) class TestQoSWithL2Population(base.BaseFullStackTestCase): def setUp(self): host_desc = [] # No need to register agents for this test case env_desc = environment.EnvironmentDescription(qos=True, l2_pop=True) env = environment.Environment(env_desc, host_desc) super(TestQoSWithL2Population, self).setUp(env) def test_supported_qos_rule_types(self): res = self.client.list_qos_rule_types() rule_types = {t['type'] for t in res['rule_types']} expected_rules = set(ovs_drv.SUPPORTED_RULES) self.assertEqual(expected_rules, rule_types) class TestQoSPolicyIsDefault(base.BaseFullStackTestCase): NAME = 'fs_policy' DESCRIPTION = 'Fullstack testing policy' SHARED = True def setUp(self): host_desc = [] # No need to register agents for this test case env_desc = environment.EnvironmentDescription(qos=True) env = environment.Environment(env_desc, host_desc) super(TestQoSPolicyIsDefault, self).setUp(env) def _create_qos_policy(self, project_id, is_default): return self.safe_client.create_qos_policy( project_id, self.NAME, self.DESCRIPTION, shared=self.SHARED, is_default=is_default) def _update_qos_policy(self, qos_policy_id, is_default): return self.client.update_qos_policy( qos_policy_id, body={'policy': {'is_default': is_default}}) def test_create_one_default_qos_policy_per_project(self): project_ids = [uuidutils.generate_uuid(), uuidutils.generate_uuid()] for project_id in project_ids: qos_policy = self._create_qos_policy(project_id, True) self.assertTrue(qos_policy['is_default']) self.assertEqual(project_id, qos_policy['project_id']) qos_policy = self._create_qos_policy(project_id, False) self.assertFalse(qos_policy['is_default']) self.assertEqual(project_id, qos_policy['project_id']) def test_create_two_default_qos_policies_per_project(self): project_id = uuidutils.generate_uuid() qos_policy = self._create_qos_policy(project_id, True) self.assertTrue(qos_policy['is_default']) self.assertEqual(project_id, qos_policy['project_id']) self.assertRaises(exceptions.Conflict, self._create_qos_policy, project_id, True) def test_update_default_status(self): project_ids = [uuidutils.generate_uuid(), uuidutils.generate_uuid()] for project_id in project_ids: qos_policy = self._create_qos_policy(project_id, True) self.assertTrue(qos_policy['is_default']) qos_policy = self._update_qos_policy(qos_policy['id'], False) self.assertFalse(qos_policy['policy']['is_default']) def test_update_default_status_conflict(self): project_id = uuidutils.generate_uuid() qos_policy_1 = self._create_qos_policy(project_id, True) self.assertTrue(qos_policy_1['is_default']) qos_policy_2 = self._create_qos_policy(project_id, False) self.assertFalse(qos_policy_2['is_default']) self.assertRaises(exceptions.Conflict, self._update_qos_policy, qos_policy_2['id'], True) neutron-12.1.1/neutron/db/0000775000175000017500000000000013553660156015401 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/portbindings_db.py0000664000175000017500000001056013553660047021123 0ustar zuulzuul00000000000000# Copyright 2013 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import portbindings from neutron_lib.api import validators from neutron_lib.plugins import directory from neutron.db import _model_query as model_query from neutron.db import _resource_extend as resource_extend from neutron.db import api as db_api from neutron.db.models import portbinding as pmodels from neutron.db import models_v2 from neutron.db import portbindings_base def _port_model_hook(context, original_model, query): query = query.outerjoin( pmodels.PortBindingPort, (original_model.id == pmodels.PortBindingPort.port_id)) return query def _port_result_filter_hook(query, filters): values = filters and filters.get(portbindings.HOST_ID, []) if not values: return query query = query.filter(pmodels.PortBindingPort.host.in_(values)) return query @resource_extend.has_resource_extenders class PortBindingMixin(portbindings_base.PortBindingBaseMixin): def __new__(cls, *args, **kwargs): model_query.register_hook( models_v2.Port, "portbindings_port", query_hook=_port_model_hook, filter_hook=None, result_filters=_port_result_filter_hook) return super(PortBindingMixin, cls).__new__(cls, *args, **kwargs) def _process_portbindings_create_and_update(self, context, port_data, port): binding_profile = port.get(portbindings.PROFILE) binding_profile_set = validators.is_attr_set(binding_profile) if not binding_profile_set and binding_profile is not None: del port[portbindings.PROFILE] binding_vnic = port.get(portbindings.VNIC_TYPE) binding_vnic_set = validators.is_attr_set(binding_vnic) if not binding_vnic_set and binding_vnic is not None: del port[portbindings.VNIC_TYPE] # REVISIT(irenab) Add support for vnic_type for plugins that # can handle more than one type. # Currently implemented for ML2 plugin that does not use # PortBindingMixin. host = port_data.get(portbindings.HOST_ID) host_set = validators.is_attr_set(host) with db_api.context_manager.writer.using(context): bind_port = context.session.query( pmodels.PortBindingPort).filter_by(port_id=port['id']).first() if host_set: if not bind_port: context.session.add( pmodels.PortBindingPort(port_id=port['id'], host=host)) else: bind_port.host = host else: host = bind_port.host if bind_port else None self._extend_port_dict_binding_host(port, host) def get_port_host(self, context, port_id): with db_api.context_manager.reader.using(context): bind_port = ( context.session.query(pmodels.PortBindingPort.host). filter_by(port_id=port_id). first() ) return bind_port.host if bind_port else None def _extend_port_dict_binding_host(self, port_res, host): super(PortBindingMixin, self).extend_port_dict_binding( port_res, None) port_res[portbindings.HOST_ID] = host def extend_port_dict_binding(self, port_res, port_db): host = port_db.portbinding.host if port_db.portbinding else None self._extend_port_dict_binding_host(port_res, host) @staticmethod @resource_extend.extends([port_def.COLLECTION_NAME]) def _extend_port_dict_binding(port_res, port_db): plugin = directory.get_plugin() if not isinstance(plugin, PortBindingMixin): return plugin.extend_port_dict_binding(port_res, port_db) neutron-12.1.1/neutron/db/subnet_service_type_db_models.py0000664000175000017500000000252213553660047024044 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company, LP # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # TODO(ihrachys): consider renaming the module since now it does not contain # any models at all from neutron_lib.api.definitions import subnet as subnet_def from neutron.db import _resource_extend as resource_extend @resource_extend.has_resource_extenders class SubnetServiceTypeMixin(object): """Mixin class to extend subnet with service type attribute""" @staticmethod @resource_extend.extends([subnet_def.COLLECTION_NAME]) def _extend_subnet_service_types(subnet_res, subnet_db): subnet_res['service_types'] = [service_type['service_type'] for service_type in subnet_db.service_types] neutron-12.1.1/neutron/db/port_security/0000775000175000017500000000000013553660156020314 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/port_security/models.py0000664000175000017500000000374713553660046022162 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db import models_v2 class PortSecurityBinding(model_base.BASEV2): port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True) port_security_enabled = sa.Column(sa.Boolean(), nullable=False) # Add a relationship to the Port model in order to be to able to # instruct SQLAlchemy to eagerly load port security binding port = orm.relationship( models_v2.Port, load_on_pending=True, backref=orm.backref("port_security", uselist=False, cascade='delete', lazy='joined')) revises_on_change = ('port',) class NetworkSecurityBinding(model_base.BASEV2): network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', ondelete="CASCADE"), primary_key=True) port_security_enabled = sa.Column(sa.Boolean(), nullable=False) # Add a relationship to the Port model in order to be able to instruct # SQLAlchemy to eagerly load default port security setting for ports # on this network network = orm.relationship( models_v2.Network, load_on_pending=True, backref=orm.backref("port_security", uselist=False, cascade='delete', lazy='joined')) revises_on_change = ('network',) neutron-12.1.1/neutron/db/port_security/__init__.py0000664000175000017500000000000013553660046022411 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/extraroute_db.py0000664000175000017500000001365613553660047020634 0ustar zuulzuul00000000000000# Copyright 2013, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.exceptions import extraroute as xroute_exc from neutron_lib.utils import helpers from oslo_config import cfg from oslo_log import log as logging from neutron._i18n import _ from neutron.common import utils from neutron.conf.db import extraroute_db from neutron.db import _resource_extend as resource_extend from neutron.db import l3_db from neutron.objects import router as l3_obj LOG = logging.getLogger(__name__) extraroute_db.register_db_extraroute_opts() @resource_extend.has_resource_extenders class ExtraRoute_dbonly_mixin(l3_db.L3_NAT_dbonly_mixin): """Mixin class to support extra route configuration on router.""" @staticmethod @resource_extend.extends([l3_apidef.ROUTERS]) def _extend_router_dict_extraroute(router_res, router_db): router_res['routes'] = (ExtraRoute_dbonly_mixin. _make_extra_route_list( router_db['route_list'] )) def update_router(self, context, id, router): r = router['router'] if 'routes' in r: with context.session.begin(subtransactions=True): #check if route exists and have permission to access router_db = self._get_router(context, id) self._update_extra_routes(context, router_db, r['routes']) # NOTE(yamamoto): expire to ensure the following update_router # see the effects of the above _update_extra_routes. context.session.expire(router_db, attribute_names=['route_list']) return super(ExtraRoute_dbonly_mixin, self).update_router( context, id, router) def _validate_routes_nexthop(self, cidrs, ips, routes, nexthop): #Note(nati): Nexthop should be connected, # so we need to check # nexthop belongs to one of cidrs of the router ports if not netaddr.all_matching_cidrs(nexthop, cidrs): raise xroute_exc.InvalidRoutes( routes=routes, reason=_('the nexthop is not connected with router')) #Note(nati) nexthop should not be same as fixed_ips if nexthop in ips: raise xroute_exc.InvalidRoutes( routes=routes, reason=_('the nexthop is used by router')) def _validate_routes(self, context, router_id, routes): if len(routes) > cfg.CONF.max_routes: raise xroute_exc.RoutesExhausted( router_id=router_id, quota=cfg.CONF.max_routes) context = context.elevated() filters = {'device_id': [router_id]} ports = self._core_plugin.get_ports(context, filters) cidrs = [] ips = [] for port in ports: for ip in port['fixed_ips']: cidrs.append(self._core_plugin.get_subnet( context, ip['subnet_id'])['cidr']) ips.append(ip['ip_address']) for route in routes: self._validate_routes_nexthop( cidrs, ips, routes, route['nexthop']) def _update_extra_routes(self, context, router, routes): self._validate_routes(context, router['id'], routes) old_routes = self._get_extra_routes_by_router_id(context, router['id']) added, removed = helpers.diff_list_of_dict(old_routes, routes) LOG.debug('Added routes are %s', added) for route in added: l3_obj.RouterRoute( context, router_id=router['id'], destination=utils.AuthenticIPNetwork(route['destination']), nexthop=netaddr.IPAddress(route['nexthop'])).create() LOG.debug('Removed routes are %s', removed) for route in removed: l3_obj.RouterRoute.get_object( context, router_id=router['id'], destination=route['destination'], nexthop=route['nexthop']).delete() @staticmethod def _make_extra_route_list(extra_routes): # NOTE(yamamoto): the extra_routes argument is either object or db row return [{'destination': str(route['destination']), 'nexthop': str(route['nexthop'])} for route in extra_routes] def _get_extra_routes_by_router_id(self, context, id): router_objs = l3_obj.RouterRoute.get_objects(context, router_id=id) return self._make_extra_route_list(router_objs) def _confirm_router_interface_not_in_use(self, context, router_id, subnet_id): super(ExtraRoute_dbonly_mixin, self)._confirm_router_interface_not_in_use( context, router_id, subnet_id) subnet = self._core_plugin.get_subnet(context, subnet_id) subnet_cidr = netaddr.IPNetwork(subnet['cidr']) extra_routes = self._get_extra_routes_by_router_id(context, router_id) for route in extra_routes: if netaddr.all_matching_cidrs(route['nexthop'], [subnet_cidr]): raise xroute_exc.RouterInterfaceInUseByRoute( router_id=router_id, subnet_id=subnet_id) class ExtraRoute_db_mixin(ExtraRoute_dbonly_mixin, l3_db.L3_NAT_db_mixin): """Mixin class to support extra route configuration on router with rpc.""" pass neutron-12.1.1/neutron/db/l3_hamode_db.py0000664000175000017500000010352713553660047020262 0ustar zuulzuul00000000000000# Copyright (C) 2014 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import functools import random import netaddr from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import provider_net as providernet from neutron_lib.api import validators from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import l3 as l3_exc from neutron_lib.exceptions import l3_ext_ha_mode as l3ha_exc from neutron_lib.objects import exceptions as obj_base from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import helpers as log_helpers from oslo_log import log as logging from oslo_utils import excutils import six import sqlalchemy as sa from sqlalchemy import exc as sql_exc from sqlalchemy import orm from neutron._i18n import _ from neutron.common import constants as n_const from neutron.common import utils as n_utils from neutron.conf.db import l3_hamode_db from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.db.availability_zone import router as router_az_db from neutron.db import l3_dvr_db from neutron.db.l3_dvr_db import is_distributed_router from neutron.db.models import l3ha as l3ha_model from neutron.objects import base from neutron.objects import l3_hamode from neutron.objects import router as l3_obj from neutron.plugins.common import utils as p_utils VR_ID_RANGE = set(range(1, 255)) MAX_ALLOCATION_TRIES = 10 UNLIMITED_AGENTS_PER_ROUTER = 0 LOG = logging.getLogger(__name__) l3_hamode_db.register_db_l3_hamode_opts() @registry.has_registry_receivers class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin, router_az_db.RouterAvailabilityZoneMixin): """Mixin class to add high availability capability to routers.""" def _verify_configuration(self): self.ha_cidr = cfg.CONF.l3_ha_net_cidr try: net = netaddr.IPNetwork(self.ha_cidr) except netaddr.AddrFormatError: raise l3ha_exc.HANetworkCIDRNotValid(cidr=self.ha_cidr) if ('/' not in self.ha_cidr or net.network != net.ip): raise l3ha_exc.HANetworkCIDRNotValid(cidr=self.ha_cidr) self._check_num_agents_per_router() def _check_num_agents_per_router(self): max_agents = cfg.CONF.max_l3_agents_per_router if max_agents != UNLIMITED_AGENTS_PER_ROUTER and max_agents < 1: raise l3ha_exc.HAMaximumAgentsNumberNotValid(max_agents=max_agents) def __new__(cls, *args, **kwargs): inst = super(L3_HA_NAT_db_mixin, cls).__new__(cls, *args, **kwargs) inst._verify_configuration() return inst def get_ha_network(self, context, tenant_id): pager = base.Pager(limit=1) results = l3_hamode.L3HARouterNetwork.get_objects( context, _pager=pager, project_id=tenant_id) return results.pop() if results else None def _get_allocated_vr_id(self, context, network_id): vr_id_objs = l3_hamode.L3HARouterVRIdAllocation.get_objects( context, network_id=network_id) allocated_vr_ids = set(a.vr_id for a in vr_id_objs) - set([0]) return allocated_vr_ids def _get_vr_id(self, context, network_id): allocated_vr_ids = self._get_allocated_vr_id(context, network_id) available_vr_ids = VR_ID_RANGE - allocated_vr_ids if not available_vr_ids: return None return random.choice(list(available_vr_ids)) @db_api.retry_if_session_inactive() def _ensure_vr_id(self, context, router_db, ha_network): router_id = router_db.id network_id = ha_network.network_id # TODO(kevinbenton): let decorator handle duplicate retry # like in review.openstack.org/#/c/367179/1/neutron/db/l3_hamode_db.py for count in range(MAX_ALLOCATION_TRIES): try: # NOTE(kevinbenton): we disallow subtransactions because the # retry logic will bust any parent transactions with context.session.begin(): if router_db.extra_attributes.ha_vr_id: LOG.debug( "Router %(router_id)s has already been " "allocated a ha_vr_id %(ha_vr_id)d!", {'router_id': router_id, 'ha_vr_id': router_db.extra_attributes.ha_vr_id}) return vr_id = self._get_vr_id(context, network_id) if vr_id is None: raise l3ha_exc.NoVRIDAvailable(router_id=router_id) allocation = l3_hamode.L3HARouterVRIdAllocation( context, network_id=network_id, vr_id=vr_id) allocation.create() router_db.extra_attributes.ha_vr_id = allocation.vr_id LOG.debug( "Router %(router_id)s has been allocated a ha_vr_id " "%(ha_vr_id)d.", {'router_id': router_id, 'ha_vr_id': allocation.vr_id}) return allocation.vr_id except obj_base.NeutronDbObjectDuplicateEntry: LOG.info("Attempt %(count)s to allocate a VRID in the " "network %(network)s for the router %(router)s", {'count': count, 'network': network_id, 'router': router_id}) raise l3ha_exc.MaxVRIDAllocationTriesReached( network_id=network_id, router_id=router_id, max_tries=MAX_ALLOCATION_TRIES) @db_api.retry_if_session_inactive() def _delete_vr_id_allocation(self, context, ha_network, vr_id): l3_hamode.L3HARouterVRIdAllocation.delete_objects( context, network_id=ha_network.network_id, vr_id=vr_id) def _create_ha_subnet(self, context, network_id, tenant_id): args = {'network_id': network_id, 'tenant_id': '', 'name': n_const.HA_SUBNET_NAME % tenant_id, 'ip_version': 4, 'cidr': cfg.CONF.l3_ha_net_cidr, 'enable_dhcp': False, 'gateway_ip': None} return p_utils.create_subnet(self._core_plugin, context, {'subnet': args}) def _create_ha_network_tenant_binding(self, context, tenant_id, network_id): ha_network = l3_hamode.L3HARouterNetwork( context, project_id=tenant_id, network_id=network_id) ha_network.create() # we need to check if someone else just inserted at exactly the # same time as us because there is no constrain in L3HARouterNetwork # that prevents multiple networks per tenant if l3_hamode.L3HARouterNetwork.count( context, project_id=tenant_id) > 1: # we need to throw an error so our network is deleted # and the process is started over where the existing # network will be selected. raise db_exc.DBDuplicateEntry(columns=['tenant_id']) return ha_network def _add_ha_network_settings(self, network): if cfg.CONF.l3_ha_network_type: network[providernet.NETWORK_TYPE] = cfg.CONF.l3_ha_network_type if cfg.CONF.l3_ha_network_physical_name: network[providernet.PHYSICAL_NETWORK] = ( cfg.CONF.l3_ha_network_physical_name) def _create_ha_network(self, context, tenant_id): admin_ctx = context.elevated() args = {'network': {'name': n_const.HA_NETWORK_NAME % tenant_id, 'tenant_id': '', 'shared': False, 'admin_state_up': True}} self._add_ha_network_settings(args['network']) creation = functools.partial(p_utils.create_network, self._core_plugin, admin_ctx, args) content = functools.partial(self._create_ha_network_tenant_binding, admin_ctx, tenant_id) deletion = functools.partial(self._core_plugin.delete_network, admin_ctx) network, ha_network = db_utils.safe_creation( context, creation, deletion, content, transaction=False) try: self._create_ha_subnet(admin_ctx, network['id'], tenant_id) except Exception: with excutils.save_and_reraise_exception(): self._core_plugin.delete_network(admin_ctx, network['id']) return ha_network def get_number_of_agents_for_scheduling(self, context): """Return number of agents on which the router will be scheduled.""" num_agents = len(self.get_l3_agents(context, active=True, filters={'agent_modes': [constants.L3_AGENT_MODE_LEGACY, constants.L3_AGENT_MODE_DVR_SNAT]})) max_agents = cfg.CONF.max_l3_agents_per_router if max_agents: if max_agents > num_agents: LOG.info("Number of active agents lower than " "max_l3_agents_per_router. L3 agents " "available: %s", num_agents) else: num_agents = max_agents return num_agents @db_api.retry_if_session_inactive() def _create_ha_port_binding(self, context, router_id, port_id): try: with context.session.begin(): l3_obj.RouterPort( context, port_id=port_id, router_id=router_id, port_type=constants.DEVICE_OWNER_ROUTER_HA_INTF).create() portbinding = l3ha_model.L3HARouterAgentPortBinding( port_id=port_id, router_id=router_id) context.session.add(portbinding) return portbinding except db_exc.DBReferenceError as e: with excutils.save_and_reraise_exception() as ctxt: if isinstance(e.inner_exception, sql_exc.IntegrityError): ctxt.reraise = False LOG.debug( 'Failed to create HA router agent PortBinding, ' 'Router %s has already been removed ' 'by concurrent operation', router_id) raise l3_exc.RouterNotFound(router_id=router_id) def add_ha_port(self, context, router_id, network_id, tenant_id): # NOTE(kevinbenton): we have to block any ongoing transactions because # our exception handling will try to delete the port using the normal # core plugin API. If this function is called inside of a transaction # the exception will mangle the state, cause the delete call to fail, # and end up relying on the DB rollback to remove the port instead of # proper delete_port call. if context.session.is_active: raise RuntimeError(_('add_ha_port cannot be called inside of a ' 'transaction.')) args = {'tenant_id': '', 'network_id': network_id, 'admin_state_up': True, 'device_id': router_id, 'device_owner': constants.DEVICE_OWNER_ROUTER_HA_INTF, 'name': n_const.HA_PORT_NAME % tenant_id} creation = functools.partial(p_utils.create_port, self._core_plugin, context, {'port': args}) content = functools.partial(self._create_ha_port_binding, context, router_id) deletion = functools.partial(self._core_plugin.delete_port, context, l3_port_check=False) port, bindings = db_utils.safe_creation(context, creation, deletion, content, transaction=False) return bindings def _delete_ha_interfaces(self, context, router_id): admin_ctx = context.elevated() device_filter = {'device_id': [router_id], 'device_owner': [constants.DEVICE_OWNER_ROUTER_HA_INTF]} ports = self._core_plugin.get_ports(admin_ctx, filters=device_filter) for port in ports: self._core_plugin.delete_port(admin_ctx, port['id'], l3_port_check=False) def delete_ha_interfaces_on_host(self, context, router_id, host): admin_ctx = context.elevated() port_ids = (binding.port_id for binding in self.get_ha_router_port_bindings(admin_ctx, [router_id], host)) for port_id in port_ids: self._core_plugin.delete_port(admin_ctx, port_id, l3_port_check=False) def _notify_router_updated(self, context, router_id): self.l3_rpc_notifier.routers_updated( context, [router_id], shuffle_agents=True) @classmethod def _is_ha(cls, router): ha = router.get('ha') if not validators.is_attr_set(ha): ha = cfg.CONF.l3_ha return ha def _get_device_owner(self, context, router=None): """Get device_owner for the specified router.""" router_is_uuid = isinstance(router, six.string_types) if router_is_uuid: router = self._get_router(context, router) if is_ha_router(router) and not is_distributed_router(router): return constants.DEVICE_OWNER_HA_REPLICATED_INT return super(L3_HA_NAT_db_mixin, self)._get_device_owner(context, router) @n_utils.transaction_guard def _ensure_vr_id_and_network(self, context, router_db): """Attach vr_id to router while tolerating network deletes.""" creator = functools.partial(self._ensure_vr_id, context, router_db) dep_getter = functools.partial(self.get_ha_network, context, router_db.tenant_id) dep_creator = functools.partial(self._create_ha_network, context, router_db.tenant_id) dep_deleter = functools.partial(self._delete_ha_network, context) dep_id_attr = 'network_id' return n_utils.create_object_with_dependency( creator, dep_getter, dep_creator, dep_id_attr, dep_deleter)[1] @registry.receives(resources.ROUTER, [events.BEFORE_CREATE]) @db_api.retry_if_session_inactive() def _before_router_create(self, resource, event, trigger, context, router, **kwargs): """Event handler to create HA resources before router creation.""" if not self._is_ha(router): return # ensure the HA network exists before we start router creation so # we can provide meaningful errors back to the user if no network # can be allocated if not self.get_ha_network(context, router['tenant_id']): self._create_ha_network(context, router['tenant_id']) @registry.receives(resources.ROUTER, [events.PRECOMMIT_CREATE]) def _precommit_router_create(self, resource, event, trigger, context, router, router_db, **kwargs): """Event handler to set ha flag and status on creation.""" is_ha = self._is_ha(router) router['ha'] = is_ha self.set_extra_attr_value(context, router_db, 'ha', is_ha) if not is_ha: return # This will throw an exception if there aren't enough agents to # handle this HA router self.get_number_of_agents_for_scheduling(context) ha_net = self.get_ha_network(context, router['tenant_id']) if not ha_net: # net was deleted, throw a retry to start over to create another raise db_exc.RetryRequest( l3ha_exc.HANetworkConcurrentDeletion( tenant_id=router['tenant_id'])) @registry.receives(resources.ROUTER, [events.AFTER_CREATE]) def _after_router_create(self, resource, event, trigger, context, router_id, router, router_db, **kwargs): if not router['ha']: return try: self.schedule_router(context, router_id) router['ha_vr_id'] = router_db.extra_attributes.ha_vr_id self._notify_router_updated(context, router_id) except Exception as e: with excutils.save_and_reraise_exception() as ctx: if isinstance(e, l3ha_exc.NoVRIDAvailable): ctx.reraise = False LOG.warning("No more VRIDs for router: %s", e) else: LOG.exception("Failed to schedule HA router %s.", router_id) router['status'] = self._update_router_db( context, router_id, {'status': constants.ERROR})['status'] @registry.receives(resources.ROUTER, [events.PRECOMMIT_UPDATE]) def _validate_migration(self, resource, event, trigger, payload=None): """Event handler on precommit update to validate migration.""" original_ha_state = payload.states[0]['ha'] requested_ha_state = payload.request_body.get('ha') ha_changed = (requested_ha_state is not None and requested_ha_state != original_ha_state) if not ha_changed: return if payload.desired_state.admin_state_up: msg = _('Cannot change HA attribute of active routers. Please ' 'set router admin_state_up to False prior to upgrade') raise n_exc.BadRequest(resource='router', msg=msg) if requested_ha_state: # This will throw HANotEnoughAvailableAgents if there aren't # enough l3 agents to handle this router. self.get_number_of_agents_for_scheduling(payload.context) else: ha_network = self.get_ha_network(payload.context, payload.desired_state.tenant_id) self._delete_vr_id_allocation( payload.context, ha_network, payload.desired_state.extra_attributes.ha_vr_id) payload.desired_state.extra_attributes.ha_vr_id = None if (payload.request_body.get('distributed') or payload.states[0]['distributed']): self.set_extra_attr_value(payload.context, payload.desired_state, 'ha', requested_ha_state) return if requested_ha_state: self._migrate_router_ports( payload.context, payload.desired_state, old_owner=constants.DEVICE_OWNER_ROUTER_INTF, new_owner=constants.DEVICE_OWNER_HA_REPLICATED_INT) else: self._migrate_router_ports( payload.context, payload.desired_state, old_owner=constants.DEVICE_OWNER_HA_REPLICATED_INT, new_owner=constants.DEVICE_OWNER_ROUTER_INTF) self.set_extra_attr_value( payload.context, payload.desired_state, 'ha', requested_ha_state) @registry.receives(resources.ROUTER, [events.AFTER_UPDATE]) def _reconfigure_ha_resources(self, resource, event, trigger, context, router_id, old_router, router, router_db, **kwargs): """Event handler to react to changes after HA flag has been updated.""" ha_changed = old_router['ha'] != router['ha'] if not ha_changed: return requested_ha_state = router['ha'] # The HA attribute has changed. First unbind the router from agents # to force a proper re-scheduling to agents. # TODO(jschwarz): This will have to be more selective to get HA + DVR # working (Only unbind from dvr_snat nodes). self._unbind_ha_router(context, router_id) if not requested_ha_state: self._delete_ha_interfaces(context, router_db.id) # always attempt to cleanup the network as the router is # deleted. the core plugin will stop us if its in use ha_network = self.get_ha_network(context, router_db.tenant_id) if ha_network: self.safe_delete_ha_network(context, ha_network, router_db.tenant_id) self.schedule_router(context, router_id) self._notify_router_updated(context, router_db.id) def _delete_ha_network(self, context, net): admin_ctx = context.elevated() self._core_plugin.delete_network(admin_ctx, net.network_id) def safe_delete_ha_network(self, context, ha_network, tenant_id): try: # reference the attr inside the try block before we attempt # to delete the network and potentially invalidate the # relationship net_id = ha_network.network_id self._delete_ha_network(context, ha_network) except (n_exc.NetworkNotFound, orm.exc.ObjectDeletedError): LOG.debug( "HA network for tenant %s was already deleted.", tenant_id) except sa.exc.InvalidRequestError: LOG.info("HA network %s can not be deleted.", net_id) except n_exc.NetworkInUse: # network is still in use, this is normal so we don't # log anything pass else: LOG.info("HA network %(network)s was deleted as " "no HA routers are present in tenant " "%(tenant)s.", {'network': net_id, 'tenant': tenant_id}) @registry.receives(resources.ROUTER, [events.PRECOMMIT_DELETE]) def _release_router_vr_id(self, resource, event, trigger, context, router_db, **kwargs): """Event handler for removal of VRID during router delete.""" if router_db.extra_attributes.ha: ha_network = self.get_ha_network(context, router_db.tenant_id) if ha_network: self._delete_vr_id_allocation( context, ha_network, router_db.extra_attributes.ha_vr_id) @registry.receives(resources.ROUTER, [events.AFTER_DELETE]) @db_api.retry_if_session_inactive() def _cleanup_ha_network(self, resource, event, trigger, context, router_id, original, **kwargs): """Event handler to attempt HA network deletion after router delete.""" if not original['ha']: return ha_network = self.get_ha_network(context, original['tenant_id']) if not ha_network: return # always attempt to cleanup the network as the router is # deleted. the core plugin will stop us if its in use self.safe_delete_ha_network(context, ha_network, original['tenant_id']) def _unbind_ha_router(self, context, router_id): for agent in self.get_l3_agents_hosting_routers(context, [router_id]): self.remove_router_from_l3_agent(context, agent['id'], router_id) def get_ha_router_port_bindings(self, context, router_ids, host=None): if not router_ids: return [] return ( l3_hamode.L3HARouterAgentPortBinding.get_l3ha_filter_host_router( context, router_ids, host)) @staticmethod def _check_router_agent_ha_binding(context, router_id, agent_id): return l3_hamode.L3HARouterAgentPortBinding.objects_exist( context, router_id=router_id, l3_agent_id=agent_id) def _get_bindings_and_update_router_state_for_dead_agents(self, context, router_id): """Return bindings. In case if dead agents were detected update router states on this agent. """ with context.session.begin(subtransactions=True): bindings = self.get_ha_router_port_bindings(context, [router_id]) dead_agents = [] active = [binding for binding in bindings if binding.state == n_const.HA_ROUTER_STATE_ACTIVE] # Check dead agents only if we have more then one active agent if len(active) > 1: dead_agents = [binding.agent for binding in active if not (binding.agent.is_active and binding.agent.admin_state_up)] for dead_agent in dead_agents: self.update_routers_states( context, {router_id: n_const.HA_ROUTER_STATE_STANDBY}, dead_agent.host) if dead_agents: return self.get_ha_router_port_bindings(context, [router_id]) return bindings def get_l3_bindings_hosting_router_with_ha_states( self, context, router_id): """Return a list of [(agent, ha_state), ...].""" bindings = self._get_bindings_and_update_router_state_for_dead_agents( context, router_id) return [(binding.agent, binding.state) for binding in bindings if binding.agent is not None] def get_active_host_for_ha_router(self, context, router_id): bindings = self.get_l3_bindings_hosting_router_with_ha_states( context, router_id) # TODO(amuller): In case we have two or more actives, this method # needs to return the last agent to become active. This requires # timestamps for state changes. Otherwise, if a host goes down # and another takes over, we'll have two actives. In this case, # if an interface is added to a router, its binding might be wrong # and l2pop would not work correctly. return next( (agent.host for agent, state in bindings if state == n_const.HA_ROUTER_STATE_ACTIVE), None) @log_helpers.log_method_call def _process_sync_ha_data(self, context, routers, host, is_any_dvr_agent): routers_dict = dict((router['id'], router) for router in routers) bindings = self.get_ha_router_port_bindings(context, routers_dict.keys(), host) for binding in bindings: port = binding.port if not port: # Filter the HA router has no ha port here LOG.info("HA router %s is missing HA router port " "bindings. Skipping it.", binding.router_id) routers_dict.pop(binding.router_id) continue port_dict = self._core_plugin._make_port_dict(port) router = routers_dict.get(binding.router_id) router[constants.HA_INTERFACE_KEY] = port_dict router[n_const.HA_ROUTER_STATE_KEY] = binding.state interfaces = [] for router in routers_dict.values(): interface = router.get(constants.HA_INTERFACE_KEY) if interface: interfaces.append(interface) self._populate_mtu_and_subnets_for_ports(context, interfaces) # If this is a DVR+HA router, then we want to always return it even # though it's missing the '_ha_interface' key. The agent will have # to figure out what kind of router setup is needed. return [r for r in list(routers_dict.values()) if (is_any_dvr_agent or not r.get('ha') or r.get(constants.HA_INTERFACE_KEY))] @log_helpers.log_method_call def get_ha_sync_data_for_host(self, context, host, agent, router_ids=None, active=None): agent_mode = self._get_agent_mode(agent) dvr_agent_mode = ( agent_mode in [constants.L3_AGENT_MODE_DVR_SNAT, constants.L3_AGENT_MODE_DVR, n_const.L3_AGENT_MODE_DVR_NO_EXTERNAL]) if (dvr_agent_mode and n_utils.is_extension_supported( self, constants.L3_DISTRIBUTED_EXT_ALIAS)): # DVR has to be handled differently sync_data = self._get_dvr_sync_data(context, host, agent, router_ids, active) else: sync_data = super(L3_HA_NAT_db_mixin, self).get_sync_data(context, router_ids, active) return self._process_sync_ha_data( context, sync_data, host, dvr_agent_mode) @classmethod def _set_router_states(cls, context, bindings, states): for binding in bindings: try: with context.session.begin(subtransactions=True): binding.state = states[binding.router_id] except (orm.exc.StaleDataError, orm.exc.ObjectDeletedError): # Take concurrently deleted routers in to account pass @db_api.retry_if_session_inactive() def update_routers_states(self, context, states, host): """Receive dict of router ID to state and update them all.""" bindings = self.get_ha_router_port_bindings( context, router_ids=states.keys(), host=host) self._set_router_states(context, bindings, states) self._update_router_port_bindings(context, states, host) def _update_router_port_bindings(self, context, states, host): admin_ctx = context.elevated() device_filter = {'device_id': list(states.keys()), 'device_owner': [constants.DEVICE_OWNER_HA_REPLICATED_INT, constants.DEVICE_OWNER_ROUTER_SNAT, constants.DEVICE_OWNER_ROUTER_GW]} ports = self._core_plugin.get_ports(admin_ctx, filters=device_filter) active_ports = (port for port in ports if states[port['device_id']] == n_const.HA_ROUTER_STATE_ACTIVE) for port in active_ports: try: self._core_plugin.update_port( admin_ctx, port['id'], {port_def.RESOURCE_NAME: {portbindings.HOST_ID: host}}) except (orm.exc.StaleDataError, orm.exc.ObjectDeletedError, n_exc.PortNotFound): # Take concurrently deleted interfaces in to account pass def _get_gateway_port_host(self, context, router, gw_ports): if not router.get('ha'): return super(L3_HA_NAT_db_mixin, self)._get_gateway_port_host( context, router, gw_ports) gw_port_id = router['gw_port_id'] gateway_port = gw_ports.get(gw_port_id) if not gw_port_id or not gateway_port: return gateway_port_status = gateway_port['status'] gateway_port_binding_host = gateway_port[portbindings.HOST_ID] admin_ctx = context.elevated() router_id = router['id'] ha_bindings = self.get_l3_bindings_hosting_router_with_ha_states( admin_ctx, router_id) LOG.debug("HA router %(router_id)s gateway port %(gw_port_id)s " "binding host: %(host)s, status: %(status)s", {"router_id": router_id, "gw_port_id": gateway_port['id'], "host": gateway_port_binding_host, "status": gateway_port_status}) for ha_binding_agent, ha_binding_state in ha_bindings: if ha_binding_state != n_const.HA_ROUTER_STATE_ACTIVE: continue # For create router gateway, the gateway port may not be ACTIVE # yet, so we return 'master' host directly. if gateway_port_status != constants.PORT_STATUS_ACTIVE: return ha_binding_agent.host # Do not let the original 'master' (current is backup) host, # override the gateway port binding host. if (gateway_port_status == constants.PORT_STATUS_ACTIVE and ha_binding_agent.host == gateway_port_binding_host): return ha_binding_agent.host LOG.debug("No gateway port host retrieved. HA router %(router_id)s " "gateway port %(gw_port_id)s " "binding host: %(host)s, status: %(status)s, " "router HA bindings: %(ha_bindings)s", {"router_id": router_id, "gw_port_id": gateway_port['id'], "host": gateway_port_binding_host, "status": gateway_port_status, "ha_bindings": ha_bindings}) def is_ha_router(router): """Return True if router to be handled is ha.""" try: # See if router is a DB object first requested_router_type = router.extra_attributes.ha except AttributeError: # if not, try to see if it is a request body requested_router_type = router.get('ha') if validators.is_attr_set(requested_router_type): return requested_router_type return cfg.CONF.l3_ha def is_ha_router_port(context, device_owner, router_id): if device_owner == constants.DEVICE_OWNER_HA_REPLICATED_INT: return True elif device_owner == constants.DEVICE_OWNER_ROUTER_SNAT: return l3_obj.RouterExtraAttributes.objects_exist( context, router_id=router_id, ha=True) else: return False neutron-12.1.1/neutron/db/models/0000775000175000017500000000000013553660156016664 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/models/securitygroup.py0000664000175000017500000001023013553660047022155 0ustar zuulzuul00000000000000# Copyright 2012 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db import models_v2 from neutron.db import standard_attr from neutron.extensions import securitygroup as sg class SecurityGroup(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a v2 neutron security group.""" name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE)) api_collections = [sg.SECURITYGROUPS] collection_resource_map = {sg.SECURITYGROUPS: 'security_group'} tag_support = True class DefaultSecurityGroup(model_base.BASEV2, model_base.HasProjectPrimaryKey): __tablename__ = 'default_security_group' security_group_id = sa.Column(sa.String(36), sa.ForeignKey("securitygroups.id", ondelete="CASCADE"), nullable=False) security_group = orm.relationship( SecurityGroup, lazy='joined', backref=orm.backref('default_security_group', cascade='all,delete'), primaryjoin="SecurityGroup.id==DefaultSecurityGroup.security_group_id", ) class SecurityGroupPortBinding(model_base.BASEV2): """Represents binding between neutron ports and security profiles.""" port_id = sa.Column(sa.String(36), sa.ForeignKey("ports.id", ondelete='CASCADE'), primary_key=True) security_group_id = sa.Column(sa.String(36), sa.ForeignKey("securitygroups.id"), primary_key=True) revises_on_change = ('ports', ) # Add a relationship to the Port model in order to instruct SQLAlchemy to # eagerly load security group bindings ports = orm.relationship( models_v2.Port, load_on_pending=True, backref=orm.backref("security_groups", lazy='joined', cascade='delete')) class SecurityGroupRule(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a v2 neutron security group rule.""" security_group_id = sa.Column(sa.String(36), sa.ForeignKey("securitygroups.id", ondelete="CASCADE"), nullable=False) remote_group_id = sa.Column(sa.String(36), sa.ForeignKey("securitygroups.id", ondelete="CASCADE"), nullable=True) revises_on_change = ('security_group', ) direction = sa.Column(sa.Enum('ingress', 'egress', name='securitygrouprules_direction')) ethertype = sa.Column(sa.String(40)) protocol = sa.Column(sa.String(40)) port_range_min = sa.Column(sa.Integer) port_range_max = sa.Column(sa.Integer) remote_ip_prefix = sa.Column(sa.String(255)) security_group = orm.relationship( SecurityGroup, load_on_pending=True, backref=orm.backref('rules', cascade='all,delete', lazy='dynamic'), primaryjoin="SecurityGroup.id==SecurityGroupRule.security_group_id") source_group = orm.relationship( SecurityGroup, backref=orm.backref('source_rules', cascade='all,delete'), primaryjoin="SecurityGroup.id==SecurityGroupRule.remote_group_id") api_collections = [sg.SECURITYGROUPRULES] neutron-12.1.1/neutron/db/models/flavor.py0000664000175000017500000000436113553660047020532 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm class Flavor(model_base.BASEV2, model_base.HasId): name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE)) description = sa.Column(sa.String(db_const.LONG_DESCRIPTION_FIELD_SIZE)) enabled = sa.Column(sa.Boolean, nullable=False, default=True, server_default=sa.sql.true()) # Make it True for multi-type flavors service_type = sa.Column(sa.String(36), nullable=True) service_profiles = orm.relationship("FlavorServiceProfileBinding", cascade="all, delete-orphan", lazy="subquery") class ServiceProfile(model_base.BASEV2, model_base.HasId): description = sa.Column(sa.String(db_const.LONG_DESCRIPTION_FIELD_SIZE)) driver = sa.Column(sa.String(1024), nullable=False) enabled = sa.Column(sa.Boolean, nullable=False, default=True, server_default=sa.sql.true()) metainfo = sa.Column(sa.String(4096)) flavors = orm.relationship("FlavorServiceProfileBinding") class FlavorServiceProfileBinding(model_base.BASEV2): flavor_id = sa.Column(sa.String(36), sa.ForeignKey("flavors.id", ondelete="CASCADE"), nullable=False, primary_key=True) flavor = orm.relationship(Flavor) service_profile_id = sa.Column(sa.String(36), sa.ForeignKey("serviceprofiles.id", ondelete="CASCADE"), nullable=False, primary_key=True) service_profile = orm.relationship(ServiceProfile) neutron-12.1.1/neutron/db/models/plugins/0000775000175000017500000000000013553660156020345 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/models/plugins/__init__.py0000664000175000017500000000000013553660046022442 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/models/plugins/ml2/0000775000175000017500000000000013553660156021037 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/models/plugins/ml2/vxlanallocation.py0000664000175000017500000000314313553660047024607 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import sql class VxlanAllocation(model_base.BASEV2): __tablename__ = 'ml2_vxlan_allocations' vxlan_vni = sa.Column(sa.Integer, nullable=False, primary_key=True, autoincrement=False) allocated = sa.Column(sa.Boolean, nullable=False, default=False, server_default=sql.false(), index=True) class VxlanEndpoints(model_base.BASEV2): """Represents tunnel endpoint in RPC mode.""" __tablename__ = 'ml2_vxlan_endpoints' __table_args__ = ( sa.UniqueConstraint('host', name='unique_ml2_vxlan_endpoints0host'), model_base.BASEV2.__table_args__ ) ip_address = sa.Column(sa.String(64), primary_key=True) udp_port = sa.Column(sa.Integer, nullable=False) host = sa.Column(sa.String(255), nullable=True) def __repr__(self): return "" % self.ip_address neutron-12.1.1/neutron/db/models/plugins/ml2/vlanallocation.py0000664000175000017500000000332213553660047024416 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa class VlanAllocation(model_base.BASEV2): """Represent allocation state of a vlan_id on a physical network. If allocated is False, the vlan_id on the physical_network is available for allocation to a tenant network. If allocated is True, the vlan_id on the physical_network is in use, either as a tenant or provider network. When an allocation is released, if the vlan_id for the physical_network is inside the pool described by VlanTypeDriver.network_vlan_ranges, then allocated is set to False. If it is outside the pool, the record is deleted. """ __tablename__ = 'ml2_vlan_allocations' __table_args__ = ( sa.Index('ix_ml2_vlan_allocations_physical_network_allocated', 'physical_network', 'allocated'), model_base.BASEV2.__table_args__,) physical_network = sa.Column(sa.String(64), nullable=False, primary_key=True) vlan_id = sa.Column(sa.Integer, nullable=False, primary_key=True, autoincrement=False) allocated = sa.Column(sa.Boolean, nullable=False) neutron-12.1.1/neutron/db/models/plugins/ml2/flatallocation.py0000664000175000017500000000200713553660046024402 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa class FlatAllocation(model_base.BASEV2): """Represent persistent allocation state of a physical network. If a record exists for a physical network, then that physical network has been allocated as a flat network. """ __tablename__ = 'ml2_flat_allocations' physical_network = sa.Column(sa.String(64), nullable=False, primary_key=True) neutron-12.1.1/neutron/db/models/plugins/ml2/gre_allocation_endpoints.py0000664000175000017500000000303413553660047026455 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import sql class GreAllocation(model_base.BASEV2): __tablename__ = 'ml2_gre_allocations' gre_id = sa.Column(sa.Integer, nullable=False, primary_key=True, autoincrement=False) allocated = sa.Column(sa.Boolean, nullable=False, default=False, server_default=sql.false(), index=True) class GreEndpoints(model_base.BASEV2): """Represents tunnel endpoint in RPC mode.""" __tablename__ = 'ml2_gre_endpoints' __table_args__ = ( sa.UniqueConstraint('host', name='unique_ml2_gre_endpoints0host'), model_base.BASEV2.__table_args__ ) ip_address = sa.Column(sa.String(64), primary_key=True) host = sa.Column(sa.String(255), nullable=True) def __repr__(self): return "" % self.ip_address neutron-12.1.1/neutron/db/models/plugins/ml2/geneveallocation.py0000664000175000017500000000276113553660047024735 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sqlalchemy as sa from sqlalchemy import sql from neutron_lib.db import model_base class GeneveAllocation(model_base.BASEV2): __tablename__ = 'ml2_geneve_allocations' geneve_vni = sa.Column(sa.Integer, nullable=False, primary_key=True, autoincrement=False) allocated = sa.Column(sa.Boolean, nullable=False, default=False, server_default=sql.false(), index=True) class GeneveEndpoints(model_base.BASEV2): """Represents tunnel endpoint in RPC mode.""" __tablename__ = 'ml2_geneve_endpoints' __table_args__ = ( sa.UniqueConstraint('host', name='unique_ml2_geneve_endpoints0host'), model_base.BASEV2.__table_args__ ) ip_address = sa.Column(sa.String(64), primary_key=True) host = sa.Column(sa.String(255), nullable=True) def __repr__(self): return "" % self.ip_address neutron-12.1.1/neutron/db/models/plugins/ml2/__init__.py0000664000175000017500000000000013553660046023134 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/models/l3_attrs.py0000664000175000017500000000416513553660046020775 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm class RouterExtraAttributes(model_base.BASEV2): """Additional attributes for a Virtual Router.""" # NOTE(armando-migliaccio): this model can be a good place to # add extension attributes to a Router model. Each case needs # to be individually examined, however 'distributed' and other # simple ones fit the pattern well. __tablename__ = "router_extra_attributes" router_id = sa.Column(sa.String(36), sa.ForeignKey('routers.id', ondelete="CASCADE"), primary_key=True) # Whether the router is a legacy (centralized) or a distributed one distributed = sa.Column(sa.Boolean, default=False, server_default=sa.sql.false(), nullable=False) # Whether the router is to be considered a 'service' router service_router = sa.Column(sa.Boolean, default=False, server_default=sa.sql.false(), nullable=False) ha = sa.Column(sa.Boolean, default=False, server_default=sa.sql.false(), nullable=False) ha_vr_id = sa.Column(sa.Integer()) # Availability Zone support availability_zone_hints = sa.Column(sa.String(255)) router = orm.relationship( 'Router', load_on_pending=True, backref=orm.backref("extra_attributes", lazy='joined', uselist=False, cascade='delete')) revises_on_change = ('router', ) neutron-12.1.1/neutron/db/models/loggingapi.py0000664000175000017500000000267113553660046021362 0ustar zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # All rights reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa from neutron.db import standard_attr class Log(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents neutron logging resource database""" __tablename__ = 'logs' name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE)) resource_type = sa.Column(sa.String(36), nullable=False) resource_id = sa.Column(sa.String(db_const.UUID_FIELD_SIZE), nullable=True, index=True) event = sa.Column(sa.String(255), nullable=False) target_id = sa.Column(sa.String(db_const.UUID_FIELD_SIZE), nullable=True, index=True) enabled = sa.Column(sa.Boolean()) api_collections = ['logs'] neutron-12.1.1/neutron/db/models/l3.py0000664000175000017500000001257213553660047017562 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db.models import l3agent as rb_model from neutron.db import models_v2 from neutron.db import standard_attr class RouterPort(model_base.BASEV2): router_id = sa.Column( sa.String(36), sa.ForeignKey('routers.id', ondelete="CASCADE"), primary_key=True) port_id = sa.Column( sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True, unique=True) revises_on_change = ('router', ) # The port_type attribute is redundant as the port table already specifies # it in DEVICE_OWNER.However, this redundancy enables more efficient # queries on router ports, and also prevents potential error-prone # conditions which might originate from users altering the DEVICE_OWNER # property of router ports. port_type = sa.Column(sa.String(db_const.DEVICE_OWNER_FIELD_SIZE)) port = orm.relationship( models_v2.Port, backref=orm.backref('routerport', uselist=False, cascade="all,delete"), lazy='joined') class Router(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a v2 neutron router.""" name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE)) status = sa.Column(sa.String(16)) admin_state_up = sa.Column(sa.Boolean) gw_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id')) gw_port = orm.relationship(models_v2.Port, lazy='joined') flavor_id = sa.Column(sa.String(36), sa.ForeignKey("flavors.id"), nullable=True) attached_ports = orm.relationship( RouterPort, backref=orm.backref('router', load_on_pending=True), lazy='subquery') l3_agents = orm.relationship( 'Agent', lazy='subquery', viewonly=True, secondary=rb_model.RouterL3AgentBinding.__table__) api_collections = [l3_apidef.ROUTERS] collection_resource_map = {l3_apidef.ROUTERS: l3_apidef.ROUTER} tag_support = True class FloatingIP(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a floating IP address. This IP address may or may not be allocated to a tenant, and may or may not be associated with an internal port/ip address/router. """ floating_ip_address = sa.Column(sa.String(64), nullable=False) floating_network_id = sa.Column(sa.String(36), nullable=False) floating_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), nullable=False) # The ORM-level "delete" cascade relationship between port and floating_ip # is required for causing the in-Python event "after_delete" that needs for # proper quota management in case when cascade removal of the floating_ip # happens after removal of the floating_port port = orm.relationship(models_v2.Port, backref=orm.backref('floating_ips', cascade='all,delete-orphan'), foreign_keys='FloatingIP.floating_port_id') fixed_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id')) fixed_ip_address = sa.Column(sa.String(64)) router_id = sa.Column(sa.String(36), sa.ForeignKey('routers.id')) # Additional attribute for keeping track of the router where the floating # ip was associated in order to be able to ensure consistency even if an # asynchronous backend is unavailable when the floating IP is disassociated last_known_router_id = sa.Column(sa.String(36)) status = sa.Column(sa.String(16)) router = orm.relationship(Router, backref='floating_ips') __table_args__ = ( sa.UniqueConstraint( floating_network_id, fixed_port_id, fixed_ip_address, name=('uniq_floatingips0floatingnetworkid' '0fixedportid0fixedipaddress')), model_base.BASEV2.__table_args__,) api_collections = [l3_apidef.FLOATINGIPS] collection_resource_map = {l3_apidef.FLOATINGIPS: l3_apidef.FLOATINGIP} tag_support = True class RouterRoute(model_base.BASEV2, models_v2.Route): router_id = sa.Column(sa.String(36), sa.ForeignKey('routers.id', ondelete="CASCADE"), primary_key=True) router = orm.relationship(Router, load_on_pending=True, backref=orm.backref("route_list", lazy='subquery', cascade='delete')) revises_on_change = ('router', ) neutron-12.1.1/neutron/db/models/dvr.py0000664000175000017500000000201513553660046020025 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett-Packard Development Company, L.P. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa class DistributedVirtualRouterMacAddress(model_base.BASEV2): """Represents a v2 neutron distributed virtual router mac address.""" __tablename__ = 'dvr_host_macs' host = sa.Column(sa.String(255), primary_key=True, nullable=False) mac_address = sa.Column(sa.String(32), nullable=False, unique=True) neutron-12.1.1/neutron/db/models/agent.py0000664000175000017500000000510013553660047020327 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import sql from neutron.agent.common import utils class Agent(model_base.BASEV2, model_base.HasId): """Represents agents running in neutron deployments.""" __table_args__ = ( sa.UniqueConstraint('agent_type', 'host', name='uniq_agents0agent_type0host'), model_base.BASEV2.__table_args__ ) # L3 agent, DHCP agent, OVS agent, LinuxBridge agent_type = sa.Column(sa.String(255), nullable=False) binary = sa.Column(sa.String(255), nullable=False) # TOPIC is a fanout exchange topic topic = sa.Column(sa.String(255), nullable=False) # TOPIC.host is a target topic host = sa.Column(sa.String(255), nullable=False) availability_zone = sa.Column(sa.String(255)) admin_state_up = sa.Column(sa.Boolean, default=True, server_default=sql.true(), nullable=False) # the time when first report came from agents created_at = sa.Column(sa.DateTime, nullable=False) # the time when first report came after agents start started_at = sa.Column(sa.DateTime, nullable=False) # updated when agents report heartbeat_timestamp = sa.Column(sa.DateTime, nullable=False) # description is note for admin user description = sa.Column(sa.String(db_const.DESCRIPTION_FIELD_SIZE)) # configurations: a json dict string, I think 4095 is enough configurations = sa.Column(sa.String(4095), nullable=False) # resource_versions: json dict, 8191 allows for ~256 resource versions # assuming ~32byte length "'name': 'ver'," # the whole row limit is 65535 bytes in mysql resource_versions = sa.Column(sa.String(8191)) # load - number of resources hosted by the agent load = sa.Column(sa.Integer, server_default='0', nullable=False) @property def is_active(self): return not utils.is_agent_down(self.heartbeat_timestamp) neutron-12.1.1/neutron/db/models/subnet_service_type.py0000664000175000017500000000337613553660046023326 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company, LP # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db import models_v2 class SubnetServiceType(model_base.BASEV2): """Subnet Service Types table""" __tablename__ = "subnet_service_types" subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id', ondelete="CASCADE")) # Service types must be valid device owners, therefore share max length service_type = sa.Column(sa.String( length=db_const.DEVICE_OWNER_FIELD_SIZE)) subnet = orm.relationship(models_v2.Subnet, load_on_pending=True, backref=orm.backref('service_types', lazy='subquery', cascade='all, delete-orphan', uselist=True)) __table_args__ = ( sa.PrimaryKeyConstraint('subnet_id', 'service_type'), model_base.BASEV2.__table_args__ ) revises_on_change = ('subnet', ) neutron-12.1.1/neutron/db/models/provisioning_block.py0000664000175000017500000000222713553660046023137 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from neutron.db import standard_attr class ProvisioningBlock(model_base.BASEV2): # the standard attr id of the thing we want to block standard_attr_id = ( sa.Column(sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), sa.ForeignKey(standard_attr.StandardAttribute.id, ondelete="CASCADE"), primary_key=True)) # the entity that wants to block the status change (e.g. L2 Agent) entity = sa.Column(sa.String(255), nullable=False, primary_key=True) neutron-12.1.1/neutron/db/models/data_plane_status.py0000664000175000017500000000247713553660046022741 0ustar zuulzuul00000000000000# Copyright (c) 2017 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db import models_v2 class PortDataPlaneStatus(model_base.BASEV2): __tablename__ = 'portdataplanestatuses' port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True, index=True) data_plane_status = sa.Column(sa.String(16), nullable=True) port = orm.relationship( models_v2.Port, load_on_pending=True, backref=orm.backref("data_plane_status", lazy='joined', uselist=False, cascade='delete')) revises_on_change = ('port', ) neutron-12.1.1/neutron/db/models/servicetype.py0000664000175000017500000000212313553660046021574 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa class ProviderResourceAssociation(model_base.BASEV2): provider_name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE), nullable=False, primary_key=True) # should be manually deleted on resource deletion resource_id = sa.Column(sa.String(36), nullable=False, primary_key=True, unique=True) neutron-12.1.1/neutron/db/models/l3ha.py0000664000175000017500000000630613553660047020071 0ustar zuulzuul00000000000000# Copyright (C) 2014 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.common import constants as n_const from neutron.db.models import agent as agent_model from neutron.db import models_v2 class L3HARouterAgentPortBinding(model_base.BASEV2): """Represent agent binding state of a HA router port. A HA Router has one HA port per agent on which it is spawned. This binding table stores which port is used for a HA router by a L3 agent. """ __tablename__ = 'ha_router_agent_port_bindings' __table_args__ = ( sa.UniqueConstraint( 'router_id', 'l3_agent_id', name='uniq_ha_router_agent_port_bindings0port_id0l3_agent_id'), model_base.BASEV2.__table_args__ ) port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete='CASCADE'), nullable=False, primary_key=True) port = orm.relationship(models_v2.Port) router_id = sa.Column(sa.String(36), sa.ForeignKey('routers.id', ondelete='CASCADE'), nullable=False) l3_agent_id = sa.Column(sa.String(36), sa.ForeignKey("agents.id", ondelete='CASCADE')) agent = orm.relationship(agent_model.Agent) state = sa.Column(sa.Enum(n_const.HA_ROUTER_STATE_ACTIVE, n_const.HA_ROUTER_STATE_STANDBY, name='l3_ha_states'), default=n_const.HA_ROUTER_STATE_STANDBY, server_default=n_const.HA_ROUTER_STATE_STANDBY) class L3HARouterNetwork(model_base.BASEV2, model_base.HasProjectPrimaryKey): """Host HA network for a tenant. One HA Network is used per tenant, all HA router ports are created on this network. """ __tablename__ = 'ha_router_networks' network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', ondelete="CASCADE"), nullable=False, primary_key=True) network = orm.relationship(models_v2.Network) class L3HARouterVRIdAllocation(model_base.BASEV2): """VRID allocation per HA network. Keep a track of the VRID allocations per HA network. """ __tablename__ = 'ha_router_vrid_allocations' network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', ondelete="CASCADE"), nullable=False, primary_key=True) vr_id = sa.Column(sa.Integer(), nullable=False, primary_key=True) neutron-12.1.1/neutron/db/models/portbinding.py0000664000175000017500000000234713553660046021561 0ustar zuulzuul00000000000000# Copyright 2013 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db import models_v2 class PortBindingPort(model_base.BASEV2): port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True) host = sa.Column(sa.String(255), nullable=False) port = orm.relationship( models_v2.Port, load_on_pending=True, backref=orm.backref("portbinding", lazy='joined', uselist=False, cascade='delete')) revises_on_change = ('port', ) neutron-12.1.1/neutron/db/models/l3agent.py0000664000175000017500000000305213553660046020571 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db.models import agent as agent_model LOWEST_BINDING_INDEX = 1 class RouterL3AgentBinding(model_base.BASEV2): """Represents binding between neutron routers and L3 agents.""" __table_args__ = ( sa.UniqueConstraint( 'router_id', 'binding_index', name='uniq_router_l3_agent_binding0router_id0binding_index0'), model_base.BASEV2.__table_args__ ) router_id = sa.Column(sa.String(36), sa.ForeignKey("routers.id", ondelete='CASCADE'), primary_key=True) l3_agent = orm.relation(agent_model.Agent) l3_agent_id = sa.Column(sa.String(36), sa.ForeignKey("agents.id", ondelete='CASCADE'), primary_key=True) binding_index = sa.Column(sa.Integer, nullable=False, server_default=str(LOWEST_BINDING_INDEX)) neutron-12.1.1/neutron/db/models/metering.py0000664000175000017500000000371113553660046021050 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from sqlalchemy import sql from neutron.db.models import l3 as l3_models class MeteringLabelRule(model_base.BASEV2, model_base.HasId): direction = sa.Column(sa.Enum('ingress', 'egress', name='meteringlabels_direction')) remote_ip_prefix = sa.Column(sa.String(64)) metering_label_id = sa.Column(sa.String(36), sa.ForeignKey("meteringlabels.id", ondelete="CASCADE"), nullable=False) excluded = sa.Column(sa.Boolean, default=False, server_default=sql.false()) class MeteringLabel(model_base.BASEV2, model_base.HasId, model_base.HasProject): name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE)) description = sa.Column(sa.String(db_const.LONG_DESCRIPTION_FIELD_SIZE)) rules = orm.relationship(MeteringLabelRule, backref="label", cascade="delete", lazy="subquery") routers = orm.relationship( l3_models.Router, primaryjoin="MeteringLabel.tenant_id==Router.tenant_id", foreign_keys='MeteringLabel.tenant_id', lazy='subquery', uselist=True) shared = sa.Column(sa.Boolean, default=False, server_default=sql.false()) neutron-12.1.1/neutron/db/models/dns.py0000664000175000017500000001032313553660046020017 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import constants from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db.models import l3 as l3_models from neutron.db import models_v2 class NetworkDNSDomain(model_base.BASEV2): network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', ondelete="CASCADE"), primary_key=True, index=True) dns_domain = sa.Column(sa.String(255), nullable=False) # Add a relationship to the Network model in order to instruct # SQLAlchemy to eagerly load this association network = orm.relationship(models_v2.Network, load_on_pending=True, backref=orm.backref("dns_domain", lazy='joined', uselist=False, cascade='delete')) revises_on_change = ('network', ) class FloatingIPDNS(model_base.BASEV2): __tablename__ = 'floatingipdnses' floatingip_id = sa.Column(sa.String(36), sa.ForeignKey('floatingips.id', ondelete="CASCADE"), primary_key=True, index=True) dns_name = sa.Column(sa.String(255), nullable=False) dns_domain = sa.Column(sa.String(255), nullable=False) published_dns_name = sa.Column(sa.String(255), nullable=False) published_dns_domain = sa.Column(sa.String(255), nullable=False) # Add a relationship to the FloatingIP model in order to instruct # SQLAlchemy to eagerly load this association floatingip = orm.relationship(l3_models.FloatingIP, load_on_pending=True, backref=orm.backref("dns", lazy='joined', uselist=False, cascade='delete')) revises_on_change = ('floatingip', ) class PortDNS(model_base.BASEV2): __tablename__ = 'portdnses' port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True, index=True) current_dns_name = sa.Column(sa.String(255), nullable=False) current_dns_domain = sa.Column(sa.String(255), nullable=False) previous_dns_name = sa.Column(sa.String(255), nullable=False) previous_dns_domain = sa.Column(sa.String(255), nullable=False) dns_name = sa.Column(sa.String(255), nullable=False) dns_domain = sa.Column(sa.String(constants.FQDN_FIELD_SIZE), nullable=False, server_default='') # Add a relationship to the Port model in order to instruct # SQLAlchemy to eagerly load this association port = orm.relationship(models_v2.Port, load_on_pending=True, backref=orm.backref("dns", lazy='joined', uselist=False, cascade='delete')) revises_on_change = ('port', ) neutron-12.1.1/neutron/db/models/external_net.py0000664000175000017500000000275713553660046021737 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from sqlalchemy import sql from neutron.db import models_v2 class ExternalNetwork(model_base.BASEV2): network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', ondelete="CASCADE"), primary_key=True) # introduced by auto-allocated-topology extension is_default = sa.Column(sa.Boolean(), nullable=False, server_default=sql.false()) # Add a relationship to the Network model in order to instruct # SQLAlchemy to eagerly load this association network = orm.relationship( models_v2.Network, load_on_pending=True, backref=orm.backref("external", lazy='joined', uselist=False, cascade='delete')) revises_on_change = ('network', ) neutron-12.1.1/neutron/db/models/__init__.py0000664000175000017500000000000013553660046020761 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/models/segment.py0000664000175000017500000000623313553660046020702 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development, LP # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db import models_v2 from neutron.db import standard_attr from neutron.extensions import segment # Some standalone plugins need a DB table to store provider # network information. Initially there was no such table, # but in Mitaka the ML2 NetworkSegment table was promoted here. class NetworkSegment(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId): """Represent persistent state of a network segment. A network segment is a portion of a neutron network with a specific physical realization. A neutron network can consist of one or more segments. """ network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', ondelete="CASCADE"), nullable=False) network_type = sa.Column(sa.String(32), nullable=False) physical_network = sa.Column(sa.String(64)) segmentation_id = sa.Column(sa.Integer) is_dynamic = sa.Column(sa.Boolean, default=False, nullable=False, server_default=sa.sql.false()) segment_index = sa.Column(sa.Integer, nullable=False, server_default='0') name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE), nullable=True) network = orm.relationship(models_v2.Network, backref=orm.backref("segments", lazy='subquery', cascade='delete')) api_collections = [segment.SEGMENTS] class SegmentHostMapping(model_base.BASEV2): segment_id = sa.Column(sa.String(36), sa.ForeignKey('networksegments.id', ondelete="CASCADE"), primary_key=True, index=True, nullable=False) host = sa.Column(sa.String(255), primary_key=True, index=True, nullable=False) # Add a relationship to the NetworkSegment model in order to instruct # SQLAlchemy to eagerly load this association network_segment = orm.relationship( NetworkSegment, load_on_pending=True, backref=orm.backref("segment_host_mapping", lazy='subquery', cascade='delete')) revises_on_change = ('network_segment', ) neutron-12.1.1/neutron/db/models/tag.py0000664000175000017500000000231013553660046020003 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db import standard_attr class Tag(model_base.BASEV2): standard_attr_id = sa.Column( sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), sa.ForeignKey(standard_attr.StandardAttribute.id, ondelete="CASCADE"), nullable=False, primary_key=True) tag = sa.Column(sa.String(60), nullable=False, primary_key=True) standard_attr = orm.relationship( 'StandardAttribute', load_on_pending=True, backref=orm.backref('tags', lazy='subquery', viewonly=True)) revises_on_change = ('standard_attr', ) neutron-12.1.1/neutron/db/models/README0000664000175000017500000000052213553660046017541 0ustar zuulzuul00000000000000This directory is designed to contain all SQLAlchemy models shipped with core Neutron. * The expected directory structure is flat, except for the ML2 plugins. All ML2 plugin models should fall under the plugins subdirectory (i.e. plugins/ml2/gre_allocation). * Module names should use singular forms for nouns (port.py, not ports.py). neutron-12.1.1/neutron/db/models/address_scope.py0000664000175000017500000000201113553660046022044 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa class AddressScope(model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a neutron address scope.""" __tablename__ = "address_scopes" name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE), nullable=False) shared = sa.Column(sa.Boolean, nullable=False) ip_version = sa.Column(sa.Integer(), nullable=False) neutron-12.1.1/neutron/db/models/allowed_address_pair.py0000664000175000017500000000237413553660046023411 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db import models_v2 class AllowedAddressPair(model_base.BASEV2): port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True) mac_address = sa.Column(sa.String(32), nullable=False, primary_key=True) ip_address = sa.Column(sa.String(64), nullable=False, primary_key=True) port = orm.relationship( models_v2.Port, load_on_pending=True, backref=orm.backref("allowed_address_pairs", lazy="subquery", cascade="delete")) revises_on_change = ('port', ) neutron-12.1.1/neutron/db/dns_db.py0000664000175000017500000002674613553660047017222 0ustar zuulzuul00000000000000# Copyright (c) 2016 IBM # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import dns as dns_apidef from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.api import validators from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import dns as dns_exc from oslo_config import cfg from oslo_log import log as logging from neutron._i18n import _ from neutron.common import utils from neutron.db import _resource_extend as resource_extend from neutron.objects import floatingip as fip_obj from neutron.objects import network from neutron.objects import ports as port_obj from neutron.services.externaldns import driver LOG = logging.getLogger(__name__) class DNSActionsData(object): def __init__(self, current_dns_name=None, current_dns_domain=None, previous_dns_name=None, previous_dns_domain=None): self.current_dns_name = current_dns_name self.current_dns_domain = current_dns_domain self.previous_dns_name = previous_dns_name self.previous_dns_domain = previous_dns_domain @resource_extend.has_resource_extenders class DNSDbMixin(object): """Mixin class to add DNS methods to db_base_plugin_v2.""" _dns_driver = None @property def dns_driver(self): if self._dns_driver: return self._dns_driver if not cfg.CONF.external_dns_driver: return try: self._dns_driver = driver.ExternalDNSService.get_instance() LOG.debug("External DNS driver loaded: %s", cfg.CONF.external_dns_driver) return self._dns_driver except ImportError: LOG.exception("ImportError exception occurred while loading " "the external DNS service driver") raise dns_exc.ExternalDNSDriverNotFound( driver=cfg.CONF.external_dns_driver) @staticmethod @resource_extend.extends([l3_apidef.FLOATINGIPS]) def _extend_floatingip_dict_dns(floatingip_res, floatingip_db): floatingip_res['dns_domain'] = '' floatingip_res['dns_name'] = '' if floatingip_db.dns: floatingip_res['dns_domain'] = floatingip_db.dns['dns_domain'] floatingip_res['dns_name'] = floatingip_db.dns['dns_name'] return floatingip_res def _process_dns_floatingip_create_precommit(self, context, floatingip_data, req_data): # expects to be called within a plugin's session dns_domain = req_data.get(dns_apidef.DNSDOMAIN) if not validators.is_attr_set(dns_domain): return if not self.dns_driver: return dns_name = req_data[dns_apidef.DNSNAME] self._validate_floatingip_dns(dns_name, dns_domain) current_dns_name, current_dns_domain = ( self._get_requested_state_for_external_dns_service_create( context, floatingip_data, req_data)) dns_actions_data = None if current_dns_name and current_dns_domain: fip_obj.FloatingIPDNS(context, floatingip_id=floatingip_data['id'], dns_name=req_data[dns_apidef.DNSNAME], dns_domain=req_data[dns_apidef.DNSDOMAIN], published_dns_name=current_dns_name, published_dns_domain=current_dns_domain).create() dns_actions_data = DNSActionsData( current_dns_name=current_dns_name, current_dns_domain=current_dns_domain) floatingip_data['dns_name'] = dns_name floatingip_data['dns_domain'] = dns_domain return dns_actions_data def _process_dns_floatingip_create_postcommit(self, context, floatingip_data, dns_actions_data): if not dns_actions_data: return self._add_ips_to_external_dns_service( context, dns_actions_data.current_dns_domain, dns_actions_data.current_dns_name, [floatingip_data['floating_ip_address']]) def _process_dns_floatingip_update_precommit(self, context, floatingip_data): # expects to be called within a plugin's session if not utils.is_extension_supported(self._core_plugin, dns_apidef.ALIAS): return if not self.dns_driver: return dns_data_db = fip_obj.FloatingIPDNS.get_object( context, floatingip_id=floatingip_data['id']) if dns_data_db and dns_data_db['dns_name']: # dns_name and dns_domain assigned for floating ip. It doesn't # matter whether they are defined for internal port return current_dns_name, current_dns_domain = ( self._get_requested_state_for_external_dns_service_update( context, floatingip_data)) if dns_data_db: if (dns_data_db['published_dns_name'] != current_dns_name or dns_data_db['published_dns_domain'] != current_dns_domain): dns_actions_data = DNSActionsData( previous_dns_name=dns_data_db['published_dns_name'], previous_dns_domain=dns_data_db['published_dns_domain']) if current_dns_name and current_dns_domain: dns_data_db['published_dns_name'] = current_dns_name dns_data_db['published_dns_domain'] = current_dns_domain dns_actions_data.current_dns_name = current_dns_name dns_actions_data.current_dns_domain = current_dns_domain else: dns_data_db.delete() return dns_actions_data else: return if current_dns_name and current_dns_domain: fip_obj.FloatingIPDNS(context, floatingip_id=floatingip_data['id'], dns_name='', dns_domain='', published_dns_name=current_dns_name, published_dns_domain=current_dns_domain).create() return DNSActionsData(current_dns_name=current_dns_name, current_dns_domain=current_dns_domain) def _process_dns_floatingip_update_postcommit(self, context, floatingip_data, dns_actions_data): if not dns_actions_data: return if dns_actions_data.previous_dns_name: self._delete_floatingip_from_external_dns_service( context, dns_actions_data.previous_dns_domain, dns_actions_data.previous_dns_name, [floatingip_data['floating_ip_address']]) if dns_actions_data.current_dns_name: self._add_ips_to_external_dns_service( context, dns_actions_data.current_dns_domain, dns_actions_data.current_dns_name, [floatingip_data['floating_ip_address']]) def _process_dns_floatingip_delete(self, context, floatingip_data): if not utils.is_extension_supported(self._core_plugin, dns_apidef.ALIAS): return dns_data_db = fip_obj.FloatingIPDNS.get_object(context, floatingip_id=floatingip_data['id']) if dns_data_db: self._delete_floatingip_from_external_dns_service( context, dns_data_db['published_dns_domain'], dns_data_db['published_dns_name'], [floatingip_data['floating_ip_address']]) def _validate_floatingip_dns(self, dns_name, dns_domain): if dns_domain and not dns_name: msg = _("dns_domain cannot be specified without a dns_name") raise n_exc.BadRequest(resource='floatingip', msg=msg) if dns_name and not dns_domain: msg = _("dns_name cannot be specified without a dns_domain") raise n_exc.BadRequest(resource='floatingip', msg=msg) def _get_internal_port_dns_data(self, context, floatingip_data): port_dns = port_obj.PortDNS.get_object( context, port_id=floatingip_data['port_id']) if not (port_dns and port_dns['dns_name']): return None, None net_dns = network.NetworkDNSDomain.get_net_dns_from_port( context=context, port_id=floatingip_data['port_id']) if not net_dns: return port_dns['dns_name'], None return port_dns['dns_name'], net_dns['dns_domain'] def _delete_floatingip_from_external_dns_service(self, context, dns_domain, dns_name, records): ips = [str(r) for r in records] try: self.dns_driver.delete_record_set(context, dns_domain, dns_name, ips) except (dns_exc.DNSDomainNotFound, dns_exc.DuplicateRecordSet) as e: LOG.exception("Error deleting Floating IP data from external " "DNS service. Name: '%(name)s'. Domain: " "'%(domain)s'. IP addresses '%(ips)s'. DNS " "service driver message '%(message)s'", {"name": dns_name, "domain": dns_domain, "message": e.msg, "ips": ', '.join(ips)}) def _get_requested_state_for_external_dns_service_create(self, context, floatingip_data, req_data): fip_dns_name = req_data[dns_apidef.DNSNAME] if fip_dns_name: return fip_dns_name, req_data[dns_apidef.DNSDOMAIN] if floatingip_data['port_id']: return self._get_internal_port_dns_data(context, floatingip_data) return None, None def _get_requested_state_for_external_dns_service_update(self, context, floatingip_data): if floatingip_data['port_id']: return self._get_internal_port_dns_data(context, floatingip_data) return None, None def _add_ips_to_external_dns_service(self, context, dns_domain, dns_name, records): ips = [str(r) for r in records] try: self.dns_driver.create_record_set(context, dns_domain, dns_name, ips) except (dns_exc.DNSDomainNotFound, dns_exc.DuplicateRecordSet) as e: LOG.exception("Error publishing floating IP data in external " "DNS service. Name: '%(name)s'. Domain: " "'%(domain)s'. DNS service driver message " "'%(message)s'", {"name": dns_name, "domain": dns_domain, "message": e.msg}) neutron-12.1.1/neutron/db/standardattrdescription_db.py0000664000175000017500000000216413553660047023361 0ustar zuulzuul00000000000000# All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.db import _resource_extend as resource_extend from neutron.db import standard_attr @resource_extend.has_resource_extenders class StandardAttrDescriptionMixin(object): supported_extension_aliases = ['standard-attr-description'] @staticmethod @resource_extend.extends( list(standard_attr.get_standard_attr_resource_model_map())) def _extend_standard_attr_description(res, db_object): if not hasattr(db_object, 'description'): return res['description'] = db_object.description neutron-12.1.1/neutron/db/data_plane_status_db.py0000664000175000017500000000405013553660047022111 0ustar zuulzuul00000000000000# Copyright (c) 2017 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import data_plane_status as dps_lib from neutron.objects.port.extensions import data_plane_status as dps_obj class DataPlaneStatusMixin(object): """Mixin class to add data plane status to a port""" def _process_create_port_data_plane_status(self, context, data, res): obj = dps_obj.PortDataPlaneStatus(context, port_id=res['id'], data_plane_status=data[dps_lib.DATA_PLANE_STATUS]) obj.create() res[dps_lib.DATA_PLANE_STATUS] = data[dps_lib.DATA_PLANE_STATUS] def _process_update_port_data_plane_status(self, context, data, res): if dps_lib.DATA_PLANE_STATUS not in data: return obj = dps_obj.PortDataPlaneStatus.get_object(context, port_id=res['id']) if obj: obj.data_plane_status = data[dps_lib.DATA_PLANE_STATUS] obj.update() res[dps_lib.DATA_PLANE_STATUS] = data[dps_lib.DATA_PLANE_STATUS] else: self._process_create_port_data_plane_status(context, data, res) @staticmethod def _extend_port_data_plane_status(port_res, port_db): port_res[dps_lib.DATA_PLANE_STATUS] = None if port_db.get(dps_lib.DATA_PLANE_STATUS): port_res[dps_lib.DATA_PLANE_STATUS] = ( port_db[dps_lib.DATA_PLANE_STATUS].data_plane_status) neutron-12.1.1/neutron/db/l3_fip_qos.py0000664000175000017500000000576013553660047020020 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.services.qos import constants as qos_consts from neutron.common import exceptions as n_exc from neutron.db import _resource_extend as resource_extend from neutron.objects.db import api as obj_db_api from neutron.objects.qos import policy as policy_object @resource_extend.has_resource_extenders class FloatingQoSDbMixin(object): """Mixin class to enable floating IP's QoS extra attributes.""" @staticmethod @resource_extend.extends([l3_apidef.FLOATINGIPS]) def _extend_extra_fip_dict(fip_res, fip_db): if fip_db.get('qos_policy_binding'): fip_res[qos_consts.QOS_POLICY_ID] = ( fip_db.qos_policy_binding.policy_id) else: fip_res[qos_consts.QOS_POLICY_ID] = None return fip_res def _get_policy_obj(self, context, policy_id): obj = policy_object.QosPolicy.get_object(context, id=policy_id) if obj is None: raise n_exc.QosPolicyNotFound(policy_id=policy_id) return obj def _create_fip_qos_db(self, context, fip_id, policy_id): policy = self._get_policy_obj(context, policy_id) policy.attach_floatingip(fip_id) binding_db_obj = obj_db_api.get_object(policy, context, fip_id=fip_id) return binding_db_obj def _delete_fip_qos_db(self, context, fip_id, policy_id): policy = self._get_policy_obj(context, policy_id) policy.detach_floatingip(fip_id) def _process_extra_fip_qos_create(self, context, fip_id, fip): qos_policy_id = fip.get(qos_consts.QOS_POLICY_ID) if not qos_policy_id: return self._create_fip_qos_db(context, fip_id, qos_policy_id) def _process_extra_fip_qos_update( self, context, floatingip_obj, fip, old_floatingip): if qos_consts.QOS_POLICY_ID not in fip: # No qos_policy_id in API input, do nothing return new_qos_policy_id = fip.get(qos_consts.QOS_POLICY_ID) old_qos_policy_id = old_floatingip.get(qos_consts.QOS_POLICY_ID) if old_qos_policy_id == new_qos_policy_id: return if old_qos_policy_id: self._delete_fip_qos_db(context, floatingip_obj['id'], old_qos_policy_id) if not new_qos_policy_id: return self._create_fip_qos_db( context, floatingip_obj['id'], new_qos_policy_id) neutron-12.1.1/neutron/db/db_base_plugin_v2.py0000664000175000017500000020770413553660047021330 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import netaddr from neutron_lib.api.definitions import ip_allocation as ipalloc_apidef from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import subnetpool as subnetpool_def from neutron_lib.api import validators from neutron_lib.callbacks import events from neutron_lib.callbacks import exceptions from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context as ctx from neutron_lib import exceptions as exc from neutron_lib.exceptions import l3 as l3_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_db import exception as os_db_exc from oslo_log import log as logging from oslo_utils import excutils from oslo_utils import uuidutils from sqlalchemy import and_ from sqlalchemy import exc as sql_exc from sqlalchemy import not_ from neutron._i18n import _ from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api from neutron.common import exceptions as n_exc from neutron.common import ipv6_utils from neutron.common import utils from neutron.db import _model_query as model_query from neutron.db import _resource_extend as resource_extend from neutron.db import _utils as ndb_utils from neutron.db import api as db_api from neutron.db import db_base_plugin_common from neutron.db import ipam_pluggable_backend from neutron.db import models_v2 from neutron.db import rbac_db_mixin as rbac_mixin from neutron.db import rbac_db_models as rbac_db from neutron.db import standardattrdescription_db as stattr_db from neutron import ipam from neutron.ipam import exceptions as ipam_exc from neutron.ipam import subnet_alloc from neutron import neutron_plugin_base_v2 from neutron.objects import base as base_obj from neutron.objects import ports as port_obj from neutron.objects import subnet as subnet_obj from neutron.objects import subnetpool as subnetpool_obj LOG = logging.getLogger(__name__) # Ports with the following 'device_owner' values will not prevent # network deletion. If delete_network() finds that all ports on a # network have these owners, it will explicitly delete each port # and allow network deletion to continue. Similarly, if delete_subnet() # finds out that all existing IP Allocations are associated with ports # with these owners, it will allow subnet deletion to proceed with the # IP allocations being cleaned up by cascade. AUTO_DELETE_PORT_OWNERS = [constants.DEVICE_OWNER_DHCP] def _check_subnet_not_used(context, subnet_id): try: kwargs = {'context': context, 'subnet_id': subnet_id} registry.notify( resources.SUBNET, events.BEFORE_DELETE, None, **kwargs) except exceptions.CallbackFailure as e: raise exc.SubnetInUse(subnet_id=subnet_id, reason=e) def _update_subnetpool_dict(orig_pool, new_pool): updated = dict((k, v) for k, v in orig_pool.to_dict().items() if k not in orig_pool.synthetic_fields) new_pool = new_pool.copy() new_prefixes = new_pool.pop('prefixes', constants.ATTR_NOT_SPECIFIED) for k, v in new_pool.items(): if k not in orig_pool.fields_no_update: updated[k] = v if new_prefixes is not constants.ATTR_NOT_SPECIFIED: orig_ip_set = netaddr.IPSet(orig_pool.prefixes) new_ip_set = netaddr.IPSet(new_prefixes) if not orig_ip_set.issubset(new_ip_set): msg = _("Existing prefixes must be " "a subset of the new prefixes") raise n_exc.IllegalSubnetPoolPrefixUpdate(msg=msg) new_ip_set.compact() updated['prefixes'] = [str(prefix.cidr) for prefix in new_ip_set.iter_cidrs()] else: updated['prefixes'] = [str(prefix) for prefix in orig_pool.prefixes] return updated def _port_filter_hook(context, original_model, conditions): # Apply the port filter only in non-admin and non-advsvc context if ndb_utils.model_query_scope_is_project(context, original_model): conditions |= (models_v2.Port.network_id.in_( context.session.query(models_v2.Network.id). filter(context.project_id == models_v2.Network.project_id). subquery())) return conditions @registry.has_registry_receivers class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon, neutron_plugin_base_v2.NeutronPluginBaseV2, rbac_mixin.RbacPluginMixin, stattr_db.StandardAttrDescriptionMixin): """V2 Neutron plugin interface implementation using SQLAlchemy models. Whenever a non-read call happens the plugin will call an event handler class method (e.g., network_created()). The result is that this class can be sub-classed by other classes that add custom behaviors on certain events. """ # This attribute specifies whether the plugin supports or not # bulk/pagination/sorting operations. Name mangling is used in # order to ensure it is qualified by class __native_bulk_support = True __native_pagination_support = True __native_sorting_support = True def has_native_datastore(self): return True def __new__(cls, *args, **kwargs): model_query.register_hook( models_v2.Port, "port", query_hook=None, filter_hook=_port_filter_hook, result_filters=None) return super(NeutronDbPluginV2, cls).__new__(cls, *args, **kwargs) def __init__(self): self.set_ipam_backend() if cfg.CONF.notify_nova_on_port_status_changes: # Import nova conditionally to support the use case of Neutron # being used outside of an OpenStack context. from neutron.notifiers import nova # NOTE(arosen) These event listeners are here to hook into when # port status changes and notify nova about their change. self.nova_notifier = nova.Notifier.get_instance() db_api.sqla_listen(models_v2.Port, 'after_insert', self.nova_notifier.send_port_status) db_api.sqla_listen(models_v2.Port, 'after_update', self.nova_notifier.send_port_status) db_api.sqla_listen(models_v2.Port.status, 'set', self.nova_notifier.record_port_status_changed) @registry.receives(rbac_mixin.RBAC_POLICY, [events.BEFORE_CREATE, events.BEFORE_UPDATE, events.BEFORE_DELETE]) @db_api.retry_if_session_inactive() def validate_network_rbac_policy_change(self, resource, event, trigger, context, object_type, policy, **kwargs): """Validates network RBAC policy changes. On creation, verify that the creator is an admin or that it owns the network it is sharing. On update and delete, make sure the tenant losing access does not have resources that depend on that access. """ if object_type != 'network' or policy['action'] != 'access_as_shared': # we only care about shared network policies return # The object a policy targets cannot be changed so we can look # at the original network for the update event as well. net = self._get_network(context, policy['object_id']) if event in (events.BEFORE_CREATE, events.BEFORE_UPDATE): # we still have to verify that the caller owns the network because # _get_network will succeed on a shared network if not context.is_admin and net['tenant_id'] != context.tenant_id: msg = _("Only admins can manipulate policies on networks " "they do not own") raise exc.InvalidInput(error_message=msg) tenant_to_check = None self_sharing = policy['target_tenant'] == net['tenant_id'] if self_sharing: return if event == events.BEFORE_UPDATE: new_tenant = kwargs['policy_update']['target_tenant'] if policy['target_tenant'] != new_tenant: tenant_to_check = policy['target_tenant'] if event == events.BEFORE_DELETE: tenant_to_check = policy['target_tenant'] if tenant_to_check: self.ensure_no_tenant_ports_on_network(net['id'], net['tenant_id'], tenant_to_check) def ensure_no_tenant_ports_on_network(self, network_id, net_tenant_id, tenant_id): ctx_admin = ctx.get_admin_context() rb_model = rbac_db.NetworkRBAC other_rbac_entries = model_query.query_with_hooks( ctx_admin, rb_model).filter( and_(rb_model.object_id == network_id, rb_model.action == 'access_as_shared')) ports = model_query.query_with_hooks(ctx_admin, models_v2.Port).filter( models_v2.Port.network_id == network_id) if tenant_id == '*': # for the wildcard we need to get all of the rbac entries to # see if any allow the remaining ports on the network. other_rbac_entries = other_rbac_entries.filter( rb_model.target_tenant != tenant_id) # any port with another RBAC entry covering it or one belonging to # the same tenant as the network owner is ok allowed_tenants = [entry['target_tenant'] for entry in other_rbac_entries] allowed_tenants.append(net_tenant_id) ports = ports.filter( ~models_v2.Port.tenant_id.in_(allowed_tenants)) else: # if there is a wildcard rule, we can return early because it # allows any ports query = other_rbac_entries.filter(rb_model.target_tenant == '*') if query.count(): return ports = ports.filter(models_v2.Port.tenant_id == tenant_id) if ports.count(): raise n_exc.InvalidSharedSetting(network=network_id) def set_ipam_backend(self): self.ipam = ipam_pluggable_backend.IpamPluggableBackend() def _validate_host_route(self, route, ip_version): try: netaddr.IPNetwork(route['destination']) netaddr.IPAddress(route['nexthop']) except netaddr.core.AddrFormatError: err_msg = _("Invalid route: %s") % route raise exc.InvalidInput(error_message=err_msg) except ValueError: # netaddr.IPAddress would raise this err_msg = _("Invalid route: %s") % route raise exc.InvalidInput(error_message=err_msg) self._validate_ip_version(ip_version, route['nexthop'], 'nexthop') self._validate_ip_version(ip_version, route['destination'], 'destination') def _validate_shared_update(self, context, id, original, updated): # The only case that needs to be validated is when 'shared' # goes from True to False if updated['shared'] == original.shared or updated['shared']: return ports = model_query.query_with_hooks( context, models_v2.Port).filter(models_v2.Port.network_id == id) ports = ports.filter(not_(models_v2.Port.device_owner.startswith( constants.DEVICE_OWNER_NETWORK_PREFIX))) subnets = subnet_obj.Subnet.get_objects(context, network_id=id) tenant_ids = set([port['tenant_id'] for port in ports] + [subnet['tenant_id'] for subnet in subnets]) # raise if multiple tenants found or if the only tenant found # is not the owner of the network if (len(tenant_ids) > 1 or len(tenant_ids) == 1 and original.tenant_id not in tenant_ids): self._validate_projects_have_access_to_network( original, tenant_ids) def _validate_projects_have_access_to_network(self, network, project_ids): ctx_admin = ctx.get_admin_context() rb_model = rbac_db.NetworkRBAC other_rbac_entries = model_query.query_with_hooks( ctx_admin, rb_model).filter( and_(rb_model.object_id == network.id, rb_model.action == 'access_as_shared', rb_model.target_tenant != "*")) allowed_projects = {entry['target_tenant'] for entry in other_rbac_entries} allowed_projects.add(network.project_id) if project_ids - allowed_projects: raise n_exc.InvalidSharedSetting(network=network.name) def _validate_ipv6_attributes(self, subnet, cur_subnet): if cur_subnet: self._validate_ipv6_update_dhcp(subnet, cur_subnet) return ra_mode_set = validators.is_attr_set(subnet.get('ipv6_ra_mode')) address_mode_set = validators.is_attr_set( subnet.get('ipv6_address_mode')) self._validate_ipv6_dhcp(ra_mode_set, address_mode_set, subnet['enable_dhcp']) if ra_mode_set and address_mode_set: self._validate_ipv6_combination(subnet['ipv6_ra_mode'], subnet['ipv6_address_mode']) if address_mode_set or ra_mode_set: self._validate_eui64_applicable(subnet) def _validate_eui64_applicable(self, subnet): # Per RFC 4862, section 5.5.3, prefix length and interface # id together should be equal to 128. Currently neutron supports # EUI64 interface id only, thus limiting the prefix # length to be 64 only. if ipv6_utils.is_auto_address_subnet(subnet): if netaddr.IPNetwork(subnet['cidr']).prefixlen != 64: msg = _('Invalid CIDR %s for IPv6 address mode. ' 'OpenStack uses the EUI-64 address format, ' 'which requires the prefix to be /64') raise exc.InvalidInput( error_message=(msg % subnet['cidr'])) def _validate_ipv6_combination(self, ra_mode, address_mode): if ra_mode != address_mode: msg = _("ipv6_ra_mode set to '%(ra_mode)s' with ipv6_address_mode " "set to '%(addr_mode)s' is not valid. " "If both attributes are set, they must be the same value" ) % {'ra_mode': ra_mode, 'addr_mode': address_mode} raise exc.InvalidInput(error_message=msg) def _validate_ipv6_dhcp(self, ra_mode_set, address_mode_set, enable_dhcp): if (ra_mode_set or address_mode_set) and not enable_dhcp: msg = _("ipv6_ra_mode or ipv6_address_mode cannot be set when " "enable_dhcp is set to False") raise exc.InvalidInput(error_message=msg) def _validate_ipv6_update_dhcp(self, subnet, cur_subnet): if ('enable_dhcp' in subnet and not subnet['enable_dhcp']): msg = _("Cannot disable enable_dhcp with " "ipv6 attributes set") ra_mode_set = validators.is_attr_set(subnet.get('ipv6_ra_mode')) address_mode_set = validators.is_attr_set( subnet.get('ipv6_address_mode')) if ra_mode_set or address_mode_set: raise exc.InvalidInput(error_message=msg) old_ra_mode_set = validators.is_attr_set( cur_subnet.get('ipv6_ra_mode')) old_address_mode_set = validators.is_attr_set( cur_subnet.get('ipv6_address_mode')) if old_ra_mode_set or old_address_mode_set: raise exc.InvalidInput(error_message=msg) def _create_bulk(self, resource, context, request_items): objects = [] collection = "%ss" % resource items = request_items[collection] try: with db_api.context_manager.writer.using(context): for item in items: obj_creator = getattr(self, 'create_%s' % resource) objects.append(obj_creator(context, item)) except Exception: with excutils.save_and_reraise_exception(): LOG.error("An exception occurred while creating " "the %(resource)s:%(item)s", {'resource': resource, 'item': item}) return objects @db_api.retry_if_session_inactive() def create_network_bulk(self, context, networks): return self._create_bulk('network', context, networks) @db_api.retry_if_session_inactive() def create_network(self, context, network): """Handle creation of a single network.""" net_db = self.create_network_db(context, network) return self._make_network_dict(net_db, process_extensions=False, context=context) def create_network_db(self, context, network): # single request processing n = network['network'] with db_api.context_manager.writer.using(context): args = {'tenant_id': n['tenant_id'], 'id': n.get('id') or uuidutils.generate_uuid(), 'name': n['name'], 'mtu': n.get('mtu'), 'admin_state_up': n['admin_state_up'], 'status': n.get('status', constants.NET_STATUS_ACTIVE), 'description': n.get('description')} network = models_v2.Network(**args) if n['shared']: entry = rbac_db.NetworkRBAC( network=network, action='access_as_shared', target_tenant='*', tenant_id=network['tenant_id']) context.session.add(entry) context.session.add(network) return network @db_api.retry_if_session_inactive() def update_network(self, context, id, network): n = network['network'] with db_api.context_manager.writer.using(context): network = self._get_network(context, id) # validate 'shared' parameter if 'shared' in n: entry = None for item in network.rbac_entries: if (item.action == 'access_as_shared' and item.target_tenant == '*'): entry = item break setattr(network, 'shared', True if entry else False) self._validate_shared_update(context, id, network, n) update_shared = n.pop('shared') if update_shared and not entry: entry = rbac_db.NetworkRBAC( network=network, action='access_as_shared', target_tenant='*', tenant_id=network['tenant_id']) context.session.add(entry) elif not update_shared and entry: network.rbac_entries.remove(entry) # The filter call removes attributes from the body received from # the API that are logically tied to network resources but are # stored in other database tables handled by extensions network.update( ndb_utils.filter_non_model_columns(n, models_v2.Network)) return self._make_network_dict(network, context=context) def _ensure_network_not_in_use(self, context, net_id): non_auto_ports = context.session.query( models_v2.Port.id).filter_by(network_id=net_id).filter( ~models_v2.Port.device_owner.in_(AUTO_DELETE_PORT_OWNERS)) if non_auto_ports.count(): raise exc.NetworkInUse(net_id=net_id) @db_api.retry_if_session_inactive() def delete_network(self, context, id): registry.notify(resources.NETWORK, events.BEFORE_DELETE, self, context=context, network_id=id) self._ensure_network_not_in_use(context, id) with db_api.context_manager.reader.using(context): auto_delete_port_ids = [p.id for p in context.session.query( models_v2.Port.id).filter_by(network_id=id).filter( models_v2.Port.device_owner.in_(AUTO_DELETE_PORT_OWNERS))] for port_id in auto_delete_port_ids: try: self.delete_port(context.elevated(), port_id) except exc.PortNotFound: # Don't raise if something else concurrently deleted the port LOG.debug("Ignoring PortNotFound when deleting port '%s'. " "The port has already been deleted.", port_id) # clean up subnets subnets = self._get_subnets_by_network(context, id) with db_api.exc_to_retry(os_db_exc.DBReferenceError): # retry reference errors so we can check the port type and # cleanup if a network-owned port snuck in without failing for subnet in subnets: self._delete_subnet(context, subnet) with db_api.context_manager.writer.using(context): network_db = self._get_network(context, id) network = self._make_network_dict(network_db, context=context) registry.notify(resources.NETWORK, events.PRECOMMIT_DELETE, self, context=context, network_id=id) # We expire network_db here because precommit deletion # might have left the relationship stale, for example, # if we deleted a segment. context.session.expire(network_db) network_db = self._get_network(context, id) context.session.delete(network_db) registry.notify(resources.NETWORK, events.AFTER_DELETE, self, context=context, network=network) @db_api.retry_if_session_inactive() def get_network(self, context, id, fields=None): network = self._get_network(context, id) return self._make_network_dict(network, fields, context=context) @db_api.retry_if_session_inactive() def _get_networks(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): marker_obj = ndb_utils.get_marker_obj(self, context, 'network', limit, marker) return model_query.get_collection( context, models_v2.Network, # if caller needs postprocessing, it should implement it explicitly dict_func=None, filters=filters, fields=fields, sorts=sorts, limit=limit, marker_obj=marker_obj, page_reverse=page_reverse) @db_api.retry_if_session_inactive() def get_networks(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): make_network_dict = functools.partial(self._make_network_dict, context=context) return [ make_network_dict(net, fields) for net in self._get_networks( context, filters=filters, fields=fields, sorts=sorts, limit=limit, marker=marker, page_reverse=page_reverse) ] @db_api.retry_if_session_inactive() def get_networks_count(self, context, filters=None): return model_query.get_collection_count(context, models_v2.Network, filters=filters) @db_api.retry_if_session_inactive() def create_subnet_bulk(self, context, subnets): return self._create_bulk('subnet', context, subnets) def _validate_ip_version(self, ip_version, addr, name): """Check IP field of a subnet match specified ip version.""" ip = netaddr.IPNetwork(addr) if ip.version != ip_version: data = {'name': name, 'addr': addr, 'ip_version': ip_version} msg = _("%(name)s '%(addr)s' does not match " "the ip_version '%(ip_version)s'") % data raise exc.InvalidInput(error_message=msg) def _validate_subnet(self, context, s, cur_subnet=None): """Validate a subnet spec.""" # This method will validate attributes which may change during # create_subnet() and update_subnet(). # The method requires the subnet spec 's' has 'ip_version' field. # If 's' dict does not have 'ip_version' field in an API call # (e.g., update_subnet()), you need to set 'ip_version' field # before calling this method. ip_ver = s['ip_version'] if validators.is_attr_set(s.get('cidr')): self._validate_ip_version(ip_ver, s['cidr'], 'cidr') # TODO(watanabe.isao): After we found a way to avoid the re-sync # from the agent side, this restriction could be removed. if cur_subnet: dhcp_was_enabled = cur_subnet.enable_dhcp else: dhcp_was_enabled = False if s.get('enable_dhcp') and not dhcp_was_enabled: subnet_prefixlen = netaddr.IPNetwork(s['cidr']).prefixlen error_message = _("Subnet has a prefix length that is " "incompatible with DHCP service enabled") if ((ip_ver == 4 and subnet_prefixlen > 30) or (ip_ver == 6 and subnet_prefixlen > 126)): raise exc.InvalidInput(error_message=error_message) net = netaddr.IPNetwork(s['cidr']) if net.is_multicast(): error_message = _("Multicast IP subnet is not supported " "if enable_dhcp is True") raise exc.InvalidInput(error_message=error_message) elif net.is_loopback(): error_message = _("Loopback IP subnet is not supported " "if enable_dhcp is True") raise exc.InvalidInput(error_message=error_message) if validators.is_attr_set(s.get('gateway_ip')): self._validate_ip_version(ip_ver, s['gateway_ip'], 'gateway_ip') is_gateway_not_valid = ( ipam.utils.check_gateway_invalid_in_subnet( s['cidr'], s['gateway_ip'])) if is_gateway_not_valid: error_message = _("Gateway is not valid on subnet") raise exc.InvalidInput(error_message=error_message) # Ensure the gateway IP is not assigned to any port # skip this check in case of create (s parameter won't have id) # NOTE(salv-orlando): There is slight chance of a race, when # a subnet-update and a router-interface-add operation are # executed concurrently if cur_subnet and not ipv6_utils.is_ipv6_pd_enabled(s): with db_api.context_manager.reader.using(context): # TODO(electrocucaracha): Look a solution for Join in OVO ipal = models_v2.IPAllocation alloc_qry = context.session.query(ipal.port_id) alloc_qry = alloc_qry.join("port", "routerport") gateway_ip = str(cur_subnet['gateway_ip']) allocated = alloc_qry.filter( ipal.ip_address == gateway_ip, ipal.subnet_id == cur_subnet['id']).first() if allocated and allocated.port_id: raise n_exc.GatewayIpInUse( ip_address=gateway_ip, port_id=allocated.port_id) if validators.is_attr_set(s.get('dns_nameservers')): if len(s['dns_nameservers']) > cfg.CONF.max_dns_nameservers: raise n_exc.DNSNameServersExhausted( subnet_id=s.get('id', _('new subnet')), quota=cfg.CONF.max_dns_nameservers) for dns in s['dns_nameservers']: try: netaddr.IPAddress(dns) except Exception: raise exc.InvalidInput( error_message=(_("Error parsing dns address %s") % dns)) self._validate_ip_version(ip_ver, dns, 'dns_nameserver') if validators.is_attr_set(s.get('host_routes')): if len(s['host_routes']) > cfg.CONF.max_subnet_host_routes: raise n_exc.HostRoutesExhausted( subnet_id=s.get('id', _('new subnet')), quota=cfg.CONF.max_subnet_host_routes) # check if the routes are all valid for rt in s['host_routes']: self._validate_host_route(rt, ip_ver) if ip_ver == 4: if validators.is_attr_set(s.get('ipv6_ra_mode')): raise exc.InvalidInput( error_message=(_("ipv6_ra_mode is not valid when " "ip_version is 4"))) if validators.is_attr_set(s.get('ipv6_address_mode')): raise exc.InvalidInput( error_message=(_("ipv6_address_mode is not valid when " "ip_version is 4"))) if ip_ver == 6: self._validate_ipv6_attributes(s, cur_subnet) def _validate_subnet_for_pd(self, subnet): """Validates that subnet parameters are correct for IPv6 PD""" if (subnet.get('ip_version') != constants.IP_VERSION_6): reason = _("Prefix Delegation can only be used with IPv6 " "subnets.") raise exc.BadRequest(resource='subnets', msg=reason) mode_list = [constants.IPV6_SLAAC, constants.DHCPV6_STATELESS] ra_mode = subnet.get('ipv6_ra_mode') if ra_mode not in mode_list: reason = _("IPv6 RA Mode must be SLAAC or Stateless for " "Prefix Delegation.") raise exc.BadRequest(resource='subnets', msg=reason) address_mode = subnet.get('ipv6_address_mode') if address_mode not in mode_list: reason = _("IPv6 Address Mode must be SLAAC or Stateless for " "Prefix Delegation.") raise exc.BadRequest(resource='subnets', msg=reason) def _update_router_gw_ports(self, context, network, subnet): l3plugin = directory.get_plugin(plugin_constants.L3) if l3plugin: gw_ports = self._get_router_gw_ports_by_network(context, network['id']) router_ids = [p.device_id for p in gw_ports] for id in router_ids: try: self._update_router_gw_port(context, id, network, subnet) except l3_exc.RouterNotFound: LOG.debug("Router %(id)s was concurrently deleted while " "updating GW port for subnet %(s)s", {'id': id, 's': subnet}) def _update_router_gw_port(self, context, router_id, network, subnet): l3plugin = directory.get_plugin(plugin_constants.L3) ctx_admin = context.elevated() ext_subnets_dict = {s['id']: s for s in network['subnets']} router = l3plugin.get_router(ctx_admin, router_id) external_gateway_info = router['external_gateway_info'] # Get all stateful (i.e. non-SLAAC/DHCPv6-stateless) fixed ips fips = [f for f in external_gateway_info['external_fixed_ips'] if not ipv6_utils.is_auto_address_subnet( ext_subnets_dict[f['subnet_id']])] num_fips = len(fips) # Don't add the fixed IP to the port if it already # has a stateful fixed IP of the same IP version if num_fips > 1: return if num_fips == 1 and netaddr.IPAddress( fips[0]['ip_address']).version == subnet['ip_version']: return external_gateway_info['external_fixed_ips'].append( {'subnet_id': subnet['id']}) info = {'router': {'external_gateway_info': external_gateway_info}} l3plugin.update_router(context, router_id, info) @db_api.retry_if_session_inactive() def _create_subnet_postcommit(self, context, result, network=None, ipam_subnet=None): if not network: network = self._get_network(context, result['network_id']) if not ipam_subnet: ipam_subnet = self.ipam.get_subnet(context, result['id']) if hasattr(network, 'external') and network.external: self._update_router_gw_ports(context, network, result) # If this subnet supports auto-addressing, then update any # internal ports on the network with addresses for this subnet. if ipv6_utils.is_auto_address_subnet(result): updated_ports = self.ipam.add_auto_addrs_on_network_ports(context, result, ipam_subnet) for port_id in updated_ports: port_info = {'port': {'id': port_id}} try: self.update_port(context, port_id, port_info) except exc.PortNotFound: LOG.debug("Port %(p)s concurrently deleted while adding " "address for new subnet %(s)s.", {'p': port_id, 's': result}) def _get_subnetpool_id(self, context, subnet): """Return the subnetpool id for this request :param subnet: The subnet dict from the request """ use_default_subnetpool = subnet.get('use_default_subnetpool') if use_default_subnetpool == constants.ATTR_NOT_SPECIFIED: use_default_subnetpool = False subnetpool_id = subnet.get('subnetpool_id') if subnetpool_id == constants.ATTR_NOT_SPECIFIED: subnetpool_id = None if use_default_subnetpool and subnetpool_id: msg = _('subnetpool_id and use_default_subnetpool cannot both be ' 'specified') raise exc.BadRequest(resource='subnets', msg=msg) if subnetpool_id: return subnetpool_id if not use_default_subnetpool: return cidr = subnet.get('cidr') if validators.is_attr_set(cidr): ip_version = netaddr.IPNetwork(cidr).version else: ip_version = subnet.get('ip_version') if not validators.is_attr_set(ip_version): msg = _('ip_version must be specified in the absence of ' 'cidr and subnetpool_id') raise exc.BadRequest(resource='subnets', msg=msg) if ip_version == 6 and cfg.CONF.ipv6_pd_enabled: return constants.IPV6_PD_POOL_ID subnetpool = self.get_default_subnetpool(context, ip_version) if subnetpool: return subnetpool['id'] msg = _('No default subnetpool found for IPv%s') % ip_version raise exc.BadRequest(resource='subnets', msg=msg) @db_api.retry_if_session_inactive() def create_subnet(self, context, subnet): result, net, ipam_sub = self._create_subnet_precommit(context, subnet) self._create_subnet_postcommit(context, result, net, ipam_sub) return result def _create_subnet_precommit(self, context, subnet): """Creates subnet in DB, returns result, network, and ipam_subnet.""" s = subnet['subnet'] cidr = s.get('cidr', constants.ATTR_NOT_SPECIFIED) prefixlen = s.get('prefixlen', constants.ATTR_NOT_SPECIFIED) has_cidr = validators.is_attr_set(cidr) has_prefixlen = validators.is_attr_set(prefixlen) if has_cidr and has_prefixlen: msg = _('cidr and prefixlen must not be supplied together') raise exc.BadRequest(resource='subnets', msg=msg) if has_cidr: # turn the CIDR into a proper subnet net = netaddr.IPNetwork(s['cidr']) subnet['subnet']['cidr'] = '%s/%s' % (net.network, net.prefixlen) subnetpool_id = self._get_subnetpool_id(context, s) if not subnetpool_id and not has_cidr: msg = _('a subnetpool must be specified in the absence of a cidr') raise exc.BadRequest(resource='subnets', msg=msg) if subnetpool_id: self.ipam.validate_pools_with_subnetpool(s) if subnetpool_id == constants.IPV6_PD_POOL_ID: if has_cidr: # We do not currently support requesting a specific # cidr with IPv6 prefix delegation. Set the subnetpool_id # to None and allow the request to continue as normal. subnetpool_id = None self._validate_subnet(context, s) else: prefix = constants.PROVISIONAL_IPV6_PD_PREFIX subnet['subnet']['cidr'] = prefix self._validate_subnet_for_pd(s) else: if not has_cidr: msg = _('A cidr must be specified in the absence of a ' 'subnet pool') raise exc.BadRequest(resource='subnets', msg=msg) self._validate_subnet(context, s) with db_api.context_manager.writer.using(context): network = self._get_network(context, subnet['subnet']['network_id']) subnet, ipam_subnet = self.ipam.allocate_subnet(context, network, subnet['subnet'], subnetpool_id) result = self._make_subnet_dict(subnet, context=context) return result, network, ipam_subnet def _update_allocation_pools(self, subnet): """Gets new allocation pools and formats them correctly""" allocation_pools = self.ipam.generate_pools(subnet['cidr'], subnet['gateway_ip']) return [{'start': str(netaddr.IPAddress(p.first, subnet['ip_version'])), 'end': str(netaddr.IPAddress(p.last, subnet['ip_version']))} for p in allocation_pools] @db_api.retry_if_session_inactive() def update_subnet(self, context, id, subnet): """Update the subnet with new info. The change however will not be realized until the client renew the dns lease or we support gratuitous DHCP offers """ result, orig = self._update_subnet_precommit(context, id, subnet) return self._update_subnet_postcommit(context, orig, result) def _update_subnet_precommit(self, context, id, subnet): """All subnet update operations safe to enclose in a transaction. :param context: neutron api request context :param id: subnet id :param subnet: API request dictionary """ s = subnet['subnet'] new_cidr = s.get('cidr') subnet_obj = self._get_subnet_object(context, id) orig = self._make_subnet_dict(subnet_obj, fields=None, context=context) # Fill 'ip_version' and 'allocation_pools' fields with the current # value since _validate_subnet() expects subnet spec has 'ip_version' # and 'allocation_pools' fields. s['ip_version'] = subnet_obj.ip_version s['cidr'] = subnet_obj.cidr s['id'] = subnet_obj.id s['project_id'] = subnet_obj.project_id s['tenant_id'] = subnet_obj.project_id s['subnetpool_id'] = subnet_obj.subnetpool_id self._validate_subnet(context, s, cur_subnet=subnet_obj) db_pools = [netaddr.IPRange(p.start, p.end) for p in subnet_obj.allocation_pools] if new_cidr and ipv6_utils.is_ipv6_pd_enabled(s): # This is an ipv6 prefix delegation-enabled subnet being given an # updated cidr by the process_prefix_update RPC s['cidr'] = netaddr.IPNetwork(new_cidr, s['ip_version']) # Update gateway_ip and allocation pools based on new cidr s['gateway_ip'] = utils.get_first_host_ip( s['cidr'], s['ip_version']) s['allocation_pools'] = self._update_allocation_pools(s) range_pools = None if s.get('allocation_pools') is not None: # Convert allocation pools to IPRange to simplify future checks range_pools = self.ipam.pools_to_ip_range(s['allocation_pools']) self.ipam.validate_allocation_pools(range_pools, s['cidr']) s['allocation_pools'] = range_pools # If either gateway_ip or allocation_pools were specified subnet_gateway = (subnet_obj.gateway_ip if subnet_obj.gateway_ip else None) gateway_ip = s.get('gateway_ip', subnet_gateway) gateway_ip_changed = gateway_ip != subnet_gateway if gateway_ip_changed or s.get('allocation_pools') is not None: pools = range_pools if range_pools is not None else db_pools if gateway_ip: self.ipam.validate_gw_out_of_pools(gateway_ip, pools) kwargs = {'context': context, 'original_subnet': orig, 'request': s} registry.notify(resources.SUBNET, events.BEFORE_UPDATE, self, **kwargs) with db_api.context_manager.writer.using(context): subnet, changes = self.ipam.update_db_subnet(context, id, s, db_pools) return self._make_subnet_dict(subnet, context=context), orig @property def l3_rpc_notifier(self): if not hasattr(self, '_l3_rpc_notifier'): self._l3_rpc_notifier = l3_rpc_agent_api.L3AgentNotifyAPI() return self._l3_rpc_notifier @l3_rpc_notifier.setter def l3_rpc_notifier(self, value): self._l3_rpc_notifier = value def _update_subnet_postcommit(self, context, orig, result): """Subnet update operations that happen after transaction completes. :param context: neutron api request context :param orig: subnet dictionary representing state before update :param result: subnet dictionary representing state after update """ update_ports_needed = (result['cidr'] != orig['cidr'] and ipv6_utils.is_ipv6_pd_enabled(result)) if update_ports_needed: # Find ports that have not yet been updated # with an IP address by Prefix Delegation, and update them filters = {'fixed_ips': {'subnet_id': [result['id']]}} ports = self.get_ports(context, filters=filters) routers = [] for port in ports: for ip in port['fixed_ips']: if ip['subnet_id'] == result['id']: if (port['device_owner'] in constants.ROUTER_INTERFACE_OWNERS): routers.append(port['device_id']) ip['ip_address'] = result['gateway_ip'] else: # We remove ip_address and pass only PD subnet_id # in port's fixed_ip for port_update. Later, IPAM # drivers will allocate eui64 address with new # prefix when they find PD subnet_id in port's # fixed_ip. ip.pop('ip_address', None) self.update_port(context, port['id'], {'port': port}) # Send router_update to l3_agent if routers: self.l3_rpc_notifier.routers_updated(context, routers) kwargs = {'context': context, 'subnet': result, 'original_subnet': orig} registry.notify(resources.SUBNET, events.AFTER_UPDATE, self, **kwargs) return result @db_api.context_manager.reader def _subnet_get_user_allocation(self, context, subnet_id): """Check if there are any user ports on subnet and return first.""" return port_obj.IPAllocation.get_alloc_by_subnet_id( context, subnet_id, AUTO_DELETE_PORT_OWNERS) @db_api.context_manager.reader def _subnet_check_ip_allocations_internal_router_ports(self, context, subnet_id): # Do not delete the subnet if IP allocations for internal # router ports still exist allocs = port_obj.IPAllocation.get_alloc_by_subnet_id( context, subnet_id, constants.ROUTER_INTERFACE_OWNERS, False) if allocs: LOG.debug("Subnet %s still has internal router ports, " "cannot delete", subnet_id) raise exc.SubnetInUse(subnet_id=subnet_id) @db_api.retry_if_session_inactive() def _remove_subnet_from_port(self, context, sub_id, port_id, auto_subnet): try: fixed = [f for f in self.get_port(context, port_id)['fixed_ips'] if f['subnet_id'] != sub_id] if auto_subnet: # special flag to avoid re-allocation on auto subnets fixed.append({'subnet_id': sub_id, 'delete_subnet': True}) data = {port_def.RESOURCE_NAME: {'fixed_ips': fixed}} self.update_port(context, port_id, data) except exc.PortNotFound: # port is gone return except exc.SubnetNotFound as e: # another subnet in the fixed ips was concurrently removed. retry raise os_db_exc.RetryRequest(e) def _ensure_no_user_ports_on_subnet(self, context, id): alloc = self._subnet_get_user_allocation(context, id) if alloc: LOG.info("Found port (%(port_id)s, %(ip)s) having IP " "allocation on subnet " "%(subnet)s, cannot delete", {'ip': alloc.ip_address, 'port_id': alloc.port_id, 'subnet': id}) raise exc.SubnetInUse(subnet_id=id) @db_api.retry_if_session_inactive() def _remove_subnet_ip_allocations_from_ports(self, context, id): # Do not allow a subnet to be deleted if a router is attached to it self._subnet_check_ip_allocations_internal_router_ports( context, id) subnet = self._get_subnet_object(context, id) is_auto_addr_subnet = ipv6_utils.is_auto_address_subnet(subnet) if not is_auto_addr_subnet: # we only automatically remove IP addresses from user ports if # the IPs come from auto allocation subnets. self._ensure_no_user_ports_on_subnet(context, id) net_allocs = (context.session.query(models_v2.IPAllocation.port_id). filter_by(subnet_id=id)) port_ids_on_net = [ipal.port_id for ipal in net_allocs] for port_id in port_ids_on_net: self._remove_subnet_from_port(context, id, port_id, auto_subnet=is_auto_addr_subnet) @db_api.retry_if_session_inactive() def delete_subnet(self, context, id): LOG.debug("Deleting subnet %s", id) # Make sure the subnet isn't used by other resources _check_subnet_not_used(context, id) subnet = self._get_subnet_object(context, id) self._remove_subnet_ip_allocations_from_ports(context, id) self._delete_subnet(context, subnet) def _delete_subnet(self, context, subnet): with db_api.exc_to_retry(sql_exc.IntegrityError), \ db_api.context_manager.writer.using(context): registry.notify(resources.SUBNET, events.PRECOMMIT_DELETE, self, context=context, subnet_id=subnet.id) subnet.delete() # Delete related ipam subnet manually, # since there is no FK relationship self.ipam.delete_subnet(context, subnet.id) registry.notify(resources.SUBNET, events.AFTER_DELETE, self, context=context, subnet=subnet.to_dict()) @db_api.retry_if_session_inactive() def get_subnet(self, context, id, fields=None): subnet_obj = self._get_subnet_object(context, id) return self._make_subnet_dict(subnet_obj, fields, context=context) @db_api.retry_if_session_inactive() def get_subnets(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): subnet_objs = self._get_subnets(context, filters, fields, sorts, limit, marker, page_reverse) return [ self._make_subnet_dict(subnet_object, fields, context) for subnet_object in subnet_objs ] @db_api.retry_if_session_inactive() def get_subnets_count(self, context, filters=None): filters = filters or {} return subnet_obj.Subnet.count(context, validate_filters=False, **filters) @db_api.retry_if_session_inactive() def get_subnets_by_network(self, context, network_id): return [self._make_subnet_dict(subnet_obj) for subnet_obj in self._get_subnets_by_network(context, network_id)] def _validate_address_scope_id(self, context, address_scope_id, subnetpool_id, sp_prefixes, ip_version): """Validate the address scope before associating. Subnetpool can associate with an address scope if - the tenant user is the owner of both the subnetpool and address scope - the admin is associating the subnetpool with the shared address scope - there is no prefix conflict with the existing subnetpools associated with the address scope. - the address family of the subnetpool and address scope are the same """ if not validators.is_attr_set(address_scope_id): return if not self.is_address_scope_owned_by_tenant(context, address_scope_id): raise n_exc.IllegalSubnetPoolAssociationToAddressScope( subnetpool_id=subnetpool_id, address_scope_id=address_scope_id) as_ip_version = self.get_ip_version_for_address_scope(context, address_scope_id) if ip_version != as_ip_version: raise n_exc.IllegalSubnetPoolIpVersionAssociationToAddressScope( subnetpool_id=subnetpool_id, address_scope_id=address_scope_id, ip_version=as_ip_version) subnetpools = subnetpool_obj.SubnetPool.get_objects( context, address_scope_id=address_scope_id) new_set = netaddr.IPSet(sp_prefixes) for sp in subnetpools: if sp.id == subnetpool_id: continue sp_set = netaddr.IPSet(sp.prefixes) if sp_set.intersection(new_set): raise n_exc.AddressScopePrefixConflict() def _check_subnetpool_update_allowed(self, context, subnetpool_id, address_scope_id): """Check if the subnetpool can be updated or not. If the subnetpool is associated to a shared address scope not owned by the tenant, then the subnetpool cannot be updated. """ if not self.is_address_scope_owned_by_tenant(context, address_scope_id): msg = _("subnetpool %(subnetpool_id)s cannot be updated when" " associated with shared address scope " "%(address_scope_id)s") % { 'subnetpool_id': subnetpool_id, 'address_scope_id': address_scope_id} raise n_exc.IllegalSubnetPoolUpdate(reason=msg) def _check_default_subnetpool_exists(self, context, ip_version): """Check if a default already exists for the given IP version. There can only be one default subnetpool for each IP family. Raise an InvalidInput error if a default has already been set. """ if self.get_default_subnetpool(context, ip_version): msg = _("A default subnetpool for this IP family has already " "been set. Only one default may exist per IP family") raise exc.InvalidInput(error_message=msg) @db_api.retry_if_session_inactive() def create_subnetpool(self, context, subnetpool): sp = subnetpool['subnetpool'] sp_reader = subnet_alloc.SubnetPoolReader(sp) if sp_reader.is_default: self._check_default_subnetpool_exists(context, sp_reader.ip_version) self._validate_address_scope_id(context, sp_reader.address_scope_id, id, sp_reader.prefixes, sp_reader.ip_version) pool_args = {'project_id': sp['tenant_id'], 'id': sp_reader.id, 'name': sp_reader.name, 'ip_version': sp_reader.ip_version, 'default_prefixlen': sp_reader.default_prefixlen, 'min_prefixlen': sp_reader.min_prefixlen, 'max_prefixlen': sp_reader.max_prefixlen, 'is_default': sp_reader.is_default, 'shared': sp_reader.shared, 'default_quota': sp_reader.default_quota, 'address_scope_id': sp_reader.address_scope_id, 'description': sp_reader.description, 'prefixes': sp_reader.prefixes} subnetpool = subnetpool_obj.SubnetPool(context, **pool_args) subnetpool.create() return self._make_subnetpool_dict(subnetpool.db_obj) @db_api.retry_if_session_inactive() def update_subnetpool(self, context, id, subnetpool): new_sp = subnetpool['subnetpool'] with db_api.context_manager.writer.using(context): orig_sp = self._get_subnetpool(context, id=id) updated = _update_subnetpool_dict(orig_sp, new_sp) reader = subnet_alloc.SubnetPoolReader(updated) if reader.is_default and not orig_sp.is_default: self._check_default_subnetpool_exists(context, reader.ip_version) if orig_sp.address_scope_id: self._check_subnetpool_update_allowed(context, id, orig_sp.address_scope_id) self._validate_address_scope_id(context, reader.address_scope_id, id, reader.prefixes, reader.ip_version) address_scope_changed = ( orig_sp.address_scope_id != reader.address_scope_id) orig_sp.update_fields(reader.subnetpool) orig_sp.update() if address_scope_changed: # Notify about the update of subnetpool's address scope kwargs = {'context': context, 'subnetpool_id': id} registry.notify(resources.SUBNETPOOL_ADDRESS_SCOPE, events.AFTER_UPDATE, self.update_subnetpool, **kwargs) for key in ['min_prefixlen', 'max_prefixlen', 'default_prefixlen']: updated['key'] = str(updated[key]) resource_extend.apply_funcs(subnetpool_def.COLLECTION_NAME, updated, orig_sp.db_obj) return updated @db_api.retry_if_session_inactive() def get_subnetpool(self, context, id, fields=None): subnetpool = self._get_subnetpool(context, id) return self._make_subnetpool_dict(subnetpool.db_obj, fields) @db_api.retry_if_session_inactive() def get_subnetpools(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pager = base_obj.Pager(sorts, limit, page_reverse, marker) filters = filters or {} subnetpools = subnetpool_obj.SubnetPool.get_objects( context, _pager=pager, validate_filters=False, **filters) return [ self._make_subnetpool_dict(pool.db_obj, fields) for pool in subnetpools ] @db_api.retry_if_session_inactive() def get_default_subnetpool(self, context, ip_version): """Retrieve the default subnetpool for the given IP version.""" filters = {'is_default': True, 'ip_version': ip_version} subnetpool = self.get_subnetpools(context, filters=filters) if subnetpool: return subnetpool[0] @db_api.retry_if_session_inactive() def delete_subnetpool(self, context, id): with db_api.context_manager.writer.using(context): subnetpool = self._get_subnetpool(context, id=id) if subnet_obj.Subnet.objects_exist(context, subnetpool_id=id): reason = _("Subnet pool has existing allocations") raise n_exc.SubnetPoolDeleteError(reason=reason) subnetpool.delete() def _check_mac_addr_update(self, context, port, new_mac, device_owner): if (device_owner and device_owner.startswith(constants.DEVICE_OWNER_NETWORK_PREFIX)): raise n_exc.UnsupportedPortDeviceOwner( op=_("mac address update"), port_id=id, device_owner=device_owner) @db_api.retry_if_session_inactive() def create_port_bulk(self, context, ports): return self._create_bulk('port', context, ports) def _create_db_port_obj(self, context, port_data): mac_address = port_data.pop('mac_address', None) if mac_address: if self._is_mac_in_use(context, port_data['network_id'], mac_address): raise exc.MacAddressInUse(net_id=port_data['network_id'], mac=mac_address) else: mac_address = self._generate_mac() db_port = models_v2.Port(mac_address=mac_address, **port_data) context.session.add(db_port) return db_port @db_api.retry_if_session_inactive() def create_port(self, context, port): db_port = self.create_port_db(context, port) return self._make_port_dict(db_port, process_extensions=False) def create_port_db(self, context, port): p = port['port'] port_id = p.get('id') or uuidutils.generate_uuid() network_id = p['network_id'] if p.get('device_owner'): self._enforce_device_owner_not_router_intf_or_device_id( context, p.get('device_owner'), p.get('device_id'), p['tenant_id']) port_data = dict(tenant_id=p['tenant_id'], name=p['name'], id=port_id, network_id=network_id, admin_state_up=p['admin_state_up'], status=p.get('status', constants.PORT_STATUS_ACTIVE), device_id=p['device_id'], device_owner=p['device_owner'], description=p.get('description')) if p.get('mac_address') is not constants.ATTR_NOT_SPECIFIED: port_data['mac_address'] = p.get('mac_address') with db_api.context_manager.writer.using(context): # Ensure that the network exists. self._get_network(context, network_id) # Create the port db_port = self._create_db_port_obj(context, port_data) p['mac_address'] = db_port['mac_address'] try: self.ipam.allocate_ips_for_port_and_store( context, port, port_id) db_port['ip_allocation'] = (ipalloc_apidef. IP_ALLOCATION_IMMEDIATE) except ipam_exc.DeferIpam: db_port['ip_allocation'] = (ipalloc_apidef. IP_ALLOCATION_DEFERRED) fixed_ips = p['fixed_ips'] if validators.is_attr_set(fixed_ips) and not fixed_ips: # [] was passed explicitly as fixed_ips. An unaddressed port. db_port['ip_allocation'] = ipalloc_apidef.IP_ALLOCATION_NONE return db_port def _validate_port_for_update(self, context, db_port, new_port, new_mac): changed_owner = 'device_owner' in new_port current_owner = (new_port.get('device_owner') or db_port['device_owner']) changed_device_id = new_port.get('device_id') != db_port['device_id'] current_device_id = new_port.get('device_id') or db_port['device_id'] if current_owner and changed_device_id or changed_owner: self._enforce_device_owner_not_router_intf_or_device_id( context, current_owner, current_device_id, db_port['tenant_id']) if new_mac and new_mac != db_port['mac_address']: self._check_mac_addr_update(context, db_port, new_mac, current_owner) @db_api.retry_if_session_inactive() def update_port(self, context, id, port): new_port = port['port'] with db_api.context_manager.writer.using(context): db_port = self._get_port(context, id) new_mac = new_port.get('mac_address') self._validate_port_for_update(context, db_port, new_port, new_mac) # Note: _make_port_dict is called here to load extension data # (specifically host binding). The IPAM plugin is separate from # the core plugin, so extensions are not loaded. # # The IPAM code could cheat and get it directly from db_port but it # would have to know about the implementation (remember ml2 has its # own port binding schema that differs from the generic one) # # This code could extract just the port binding host here and pass # that in. The problem is that db_base_plugin_common shouldn't # know anything about port binding. This compromise sends IPAM a # port_dict with all of the extension data loaded. try: self.ipam.update_port( context, old_port_db=db_port, old_port=self._make_port_dict(db_port), new_port=new_port) except ipam_exc.IpAddressAllocationNotFound as e: # If a port update and a subnet delete interleave, there is a # chance that the IPAM update operation raises this exception. # Rather than throwing that up to the user under some sort of # conflict, bubble up a retry instead that should bring things # back to sanity. raise os_db_exc.RetryRequest(e) return self._make_port_dict(db_port) @db_api.retry_if_session_inactive() def delete_port(self, context, id): with db_api.context_manager.writer.using(context): self.ipam.delete_port(context, id) def delete_ports_by_device_id(self, context, device_id, network_id=None): with db_api.context_manager.reader.using(context): query = (context.session.query(models_v2.Port.id) .enable_eagerloads(False) .filter(models_v2.Port.device_id == device_id)) if network_id: query = query.filter(models_v2.Port.network_id == network_id) port_ids = [p[0] for p in query] for port_id in port_ids: try: self.delete_port(context, port_id) except exc.PortNotFound: # Don't raise if something else concurrently deleted the port LOG.debug("Ignoring PortNotFound when deleting port '%s'. " "The port has already been deleted.", port_id) @db_api.retry_if_session_inactive() @db_api.context_manager.reader def get_port(self, context, id, fields=None): port = self._get_port(context, id) return self._make_port_dict(port, fields) def _get_ports_query(self, context, filters=None, *args, **kwargs): Port = models_v2.Port IPAllocation = models_v2.IPAllocation limit = kwargs.pop('limit', None) filters = filters or {} fixed_ips = filters.pop('fixed_ips', {}) query = model_query.get_collection_query(context, Port, filters=filters, *args, **kwargs) ip_addresses = fixed_ips.get('ip_address') subnet_ids = fixed_ips.get('subnet_id') if ip_addresses: query = query.filter( Port.fixed_ips.any(IPAllocation.ip_address.in_(ip_addresses))) if subnet_ids: query = query.filter( Port.fixed_ips.any(IPAllocation.subnet_id.in_(subnet_ids))) if limit: query = query.limit(limit) return query @db_api.retry_if_session_inactive() def get_ports(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): marker_obj = ndb_utils.get_marker_obj(self, context, 'port', limit, marker) query = self._get_ports_query(context, filters=filters, sorts=sorts, limit=limit, marker_obj=marker_obj, page_reverse=page_reverse) items = [self._make_port_dict(c, fields) for c in query] if limit and page_reverse: items.reverse() return items @db_api.retry_if_session_inactive() def get_ports_count(self, context, filters=None): return self._get_ports_query(context, filters).count() def _enforce_device_owner_not_router_intf_or_device_id(self, context, device_owner, device_id, tenant_id): """Prevent tenants from replacing the device id of router ports with a router uuid belonging to another tenant. """ if device_owner not in constants.ROUTER_INTERFACE_OWNERS: return if not context.is_admin: # check to make sure device_id does not match another tenants # router. if device_id: if hasattr(self, 'get_router'): try: ctx_admin = context.elevated() router = self.get_router(ctx_admin, device_id) except l3_exc.RouterNotFound: return else: l3plugin = directory.get_plugin(plugin_constants.L3) if l3plugin: try: ctx_admin = context.elevated() router = l3plugin.get_router(ctx_admin, device_id) except l3_exc.RouterNotFound: return else: # raise as extension doesn't support L3 anyways. raise n_exc.DeviceIDNotOwnedByTenant( device_id=device_id) if tenant_id != router['tenant_id']: raise n_exc.DeviceIDNotOwnedByTenant(device_id=device_id) neutron-12.1.1/neutron/db/vlantransparent_db.py0000664000175000017500000000232213553660047021640 0ustar zuulzuul00000000000000# Copyright (c) 2015 Cisco Systems, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import network as net_def from neutron.db import _resource_extend as resource_extend from neutron.extensions import vlantransparent @resource_extend.has_resource_extenders class Vlantransparent_db_mixin(object): """Mixin class to add vlan transparent methods to db_base_plugin_v2.""" @staticmethod @resource_extend.extends([net_def.COLLECTION_NAME]) def _extend_network_dict_vlan_transparent(network_res, network_db): network_res[vlantransparent.VLANTRANSPARENT] = ( network_db.vlan_transparent) return network_res neutron-12.1.1/neutron/db/dvr_mac_db.py0000664000175000017500000001721613553660047020041 0ustar zuulzuul00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import exceptions as n_exc from neutron_lib.objects import exceptions from neutron_lib.plugins import directory from neutron_lib.utils import net from oslo_config import cfg from oslo_log import helpers as log_helpers from oslo_log import log as logging from sqlalchemy import or_ from neutron.common import utils from neutron.conf.db import dvr_mac_db from neutron.db import api as db_api from neutron.db import models_v2 from neutron.extensions import dvr as ext_dvr from neutron.objects import router LOG = logging.getLogger(__name__) dvr_mac_db.register_db_dvr_mac_opts() @registry.has_registry_receivers class DVRDbMixin(ext_dvr.DVRMacAddressPluginBase): """Mixin class to add dvr mac address to db_plugin_base_v2.""" @property def plugin(self): try: if self._plugin is not None: return self._plugin except AttributeError: pass self._plugin = directory.get_plugin() return self._plugin @staticmethod @registry.receives(resources.AGENT, [events.BEFORE_DELETE]) @db_api.retry_if_session_inactive() def _delete_mac_associated_with_agent(resource, event, trigger, context, agent, **kwargs): host = agent['host'] plugin = directory.get_plugin() if [a for a in plugin.get_agents(context, filters={'host': [host]}) if a['id'] != agent['id']]: # there are still agents on this host, don't mess with the mac # entry until they are all deleted. return if not router.DVRMacAddress.delete_objects(context, host=host): return # notify remaining agents so they cleanup flows dvr_macs = plugin.get_dvr_mac_address_list(context) plugin.notifier.dvr_mac_address_update(context, dvr_macs) @db_api.context_manager.reader def _get_dvr_mac_address_by_host(self, context, host): dvr_obj = router.DVRMacAddress.get_object(context, host=host) if not dvr_obj: raise ext_dvr.DVRMacAddressNotFound(host=host) return self._make_dvr_mac_address_dict(dvr_obj) @utils.transaction_guard @db_api.retry_if_session_inactive() def _create_dvr_mac_address_retry(self, context, host, base_mac): with db_api.context_manager.writer.using(context): mac_address = net.get_random_mac(base_mac) dvr_mac_binding = router.DVRMacAddress( context, host=host, mac_address=netaddr.EUI(mac_address)) dvr_mac_binding.create() LOG.debug("Generated DVR mac for host %(host)s " "is %(mac_address)s", {'host': host, 'mac_address': mac_address}) dvr_macs = self.get_dvr_mac_address_list(context) # TODO(vivek): improve scalability of this fanout by # sending a single mac address rather than the entire set self.notifier.dvr_mac_address_update(context, dvr_macs) return self._make_dvr_mac_address_dict(dvr_mac_binding) def _create_dvr_mac_address(self, context, host): """Create DVR mac address for a given host.""" base_mac = cfg.CONF.dvr_base_mac.split(':') try: return self._create_dvr_mac_address_retry(context, host, base_mac) except exceptions.NeutronDbObjectDuplicateEntry: LOG.error("MAC generation error after %s attempts", db_api.MAX_RETRIES) raise n_exc.HostMacAddressGenerationFailure(host=host) @db_api.context_manager.reader def get_dvr_mac_address_list(self, context): return [ dvr_mac.to_dict() for dvr_mac in router.DVRMacAddress.get_objects(context) ] def get_dvr_mac_address_by_host(self, context, host): """Determine the MAC for the DVR port associated to host.""" if not host: return try: return self._get_dvr_mac_address_by_host(context, host) except ext_dvr.DVRMacAddressNotFound: return self._create_dvr_mac_address(context, host) def _make_dvr_mac_address_dict(self, dvr_mac_entry, fields=None): return {'host': dvr_mac_entry['host'], 'mac_address': str(dvr_mac_entry['mac_address'])} @log_helpers.log_method_call @db_api.retry_if_session_inactive() def get_ports_on_host_by_subnet(self, context, host, subnet): """Returns DVR serviced ports on a given subnet in the input host This method returns ports that need to be serviced by DVR. :param context: rpc request context :param host: host id to match and extract ports of interest :param subnet: subnet id to match and extract ports of interest :returns: list -- Ports on the given subnet in the input host """ filters = {'fixed_ips': {'subnet_id': [subnet]}, portbindings.HOST_ID: [host]} ports_query = self.plugin._get_ports_query(context, filters=filters) owner_filter = or_( models_v2.Port.device_owner.startswith( constants.DEVICE_OWNER_COMPUTE_PREFIX), models_v2.Port.device_owner.in_( utils.get_other_dvr_serviced_device_owners())) ports_query = ports_query.filter(owner_filter) ports = [ self.plugin._make_port_dict(port, process_extensions=False) for port in ports_query.all() ] LOG.debug("Returning list of dvr serviced ports on host %(host)s" " for subnet %(subnet)s ports %(ports)s", {'host': host, 'subnet': subnet, 'ports': ports}) return ports @log_helpers.log_method_call @db_api.retry_if_session_inactive() def get_subnet_for_dvr(self, context, subnet, fixed_ips=None): if fixed_ips: subnet_data = fixed_ips[0]['subnet_id'] else: subnet_data = subnet try: subnet_info = self.plugin.get_subnet( context, subnet_data) except n_exc.SubnetNotFound: return {} else: # retrieve the gateway port on this subnet if fixed_ips: ip_address = fixed_ips[0]['ip_address'] else: ip_address = subnet_info['gateway_ip'] filter = {'fixed_ips': {'subnet_id': [subnet], 'ip_address': [ip_address]}} internal_gateway_ports = self.plugin.get_ports( context, filters=filter) if not internal_gateway_ports: LOG.error("Could not retrieve gateway port " "for subnet %s", subnet_info) return {} internal_port = internal_gateway_ports[0] subnet_info['gateway_mac'] = internal_port['mac_address'] return subnet_info neutron-12.1.1/neutron/db/l3_gwmode_db.py0000664000175000017500000000664513553660047020312 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from neutron_lib.api.definitions import l3 as l3_apidef from oslo_config import cfg import sqlalchemy as sa from sqlalchemy import sql from neutron.conf.db import l3_gwmode_db from neutron.db import _resource_extend as resource_extend from neutron.db import l3_db from neutron.db.models import l3 as l3_models l3_gwmode_db.register_db_l3_gwmode_opts() # Modify the Router Data Model adding the enable_snat attribute setattr(l3_models.Router, 'enable_snat', sa.Column(sa.Boolean, default=True, server_default=sql.true(), nullable=False)) @resource_extend.has_resource_extenders class L3_NAT_dbonly_mixin(l3_db.L3_NAT_dbonly_mixin): """Mixin class to add configurable gateway modes.""" @staticmethod @resource_extend.extends([l3_apidef.ROUTERS]) def _extend_router_dict_gw_mode(router_res, router_db): if router_db.gw_port_id: nw_id = router_db.gw_port['network_id'] router_res[l3_apidef.EXTERNAL_GW_INFO] = { 'network_id': nw_id, 'enable_snat': router_db.enable_snat, 'external_fixed_ips': [ {'subnet_id': ip["subnet_id"], 'ip_address': ip["ip_address"]} for ip in router_db.gw_port['fixed_ips'] ] } def _update_router_gw_info(self, context, router_id, info, router=None): # Load the router only if necessary if not router: router = self._get_router(context, router_id) with context.session.begin(subtransactions=True): router.enable_snat = self._get_enable_snat(info) # Calls superclass, pass router db object for avoiding re-loading super(L3_NAT_dbonly_mixin, self)._update_router_gw_info( context, router_id, info, router=router) # Returning the router might come back useful if this # method is overridden in child classes return router @staticmethod def _get_enable_snat(info): if info and 'enable_snat' in info: return info['enable_snat'] # if enable_snat is not specified then use the default value return cfg.CONF.enable_snat_by_default def _build_routers_list(self, context, routers, gw_ports): routers = super(L3_NAT_dbonly_mixin, self)._build_routers_list( context, routers, gw_ports) for rtr in routers: gw_port_id = rtr['gw_port_id'] # Collect gw ports only if available if gw_port_id and gw_ports.get(gw_port_id): rtr['gw_port'] = gw_ports[gw_port_id] # Add enable_snat key rtr['enable_snat'] = rtr[ l3_apidef.EXTERNAL_GW_INFO]['enable_snat'] return routers class L3_NAT_db_mixin(L3_NAT_dbonly_mixin, l3_db.L3_NAT_db_mixin): pass neutron-12.1.1/neutron/db/api.py0000664000175000017500000002354713553660047016536 0ustar zuulzuul00000000000000# Copyright 2011 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib import copy import weakref from neutron_lib.db import api from neutron_lib import exceptions from neutron_lib.objects import exceptions as obj_exc from oslo_config import cfg from oslo_db import api as oslo_db_api from oslo_db import exception as db_exc from oslo_log import log as logging from oslo_utils import excutils from osprofiler import opts as profiler_opts import osprofiler.sqlalchemy from pecan import util as p_util import six import sqlalchemy from sqlalchemy import event # noqa from sqlalchemy import exc as sql_exc from sqlalchemy import orm from sqlalchemy.orm import exc def set_hook(engine): if (profiler_opts.is_trace_enabled() and profiler_opts.is_db_trace_enabled()): osprofiler.sqlalchemy.add_tracing(sqlalchemy, engine, 'neutron.db') context_manager = api.get_context_manager() # TODO(ihrachys) the hook assumes options defined by osprofiler, and the only # public function that is provided by osprofiler that will register them is # set_defaults, that's why we call it here even though we don't need to change # defaults profiler_opts.set_defaults(cfg.CONF) context_manager.append_on_engine_create(set_hook) MAX_RETRIES = 10 LOG = logging.getLogger(__name__) def is_retriable(e): if getattr(e, '_RETRY_EXCEEDED', False): return False if _is_nested_instance(e, (db_exc.DBDeadlock, exc.StaleDataError, db_exc.DBConnectionError, db_exc.DBDuplicateEntry, db_exc.RetryRequest, obj_exc.NeutronDbObjectDuplicateEntry)): return True # looking savepoints mangled by deadlocks. see bug/1590298 for details. return _is_nested_instance(e, db_exc.DBError) and '1305' in str(e) _retry_db_errors = oslo_db_api.wrap_db_retry( max_retries=MAX_RETRIES, retry_interval=0.1, inc_retry_interval=True, exception_checker=is_retriable ) def _tag_retriables_as_unretriable(f): """Puts a flag on retriable exceptions so is_retriable returns False. This decorator can be used outside of a retry decorator to prevent decorators higher up from retrying again. """ @six.wraps(f) def wrapped(*args, **kwargs): try: return f(*args, **kwargs) except Exception as e: with excutils.save_and_reraise_exception(): if is_retriable(e): setattr(e, '_RETRY_EXCEEDED', True) return wrapped def _copy_if_lds(item): """Deepcopy lists/dicts/sets, leave everything else alone.""" return copy.deepcopy(item) if isinstance(item, (list, dict, set)) else item def retry_db_errors(f): """Nesting-safe retry decorator with auto-arg-copy and logging. Retry decorator for all functions which do not accept a context as an argument. If the function accepts a context, use 'retry_if_session_inactive' below. If retriable errors are retried and exceed the count, they will be tagged with a flag so is_retriable will no longer recognize them as retriable. This prevents multiple applications of this decorator (and/or the one below) from retrying the same exception. """ @_tag_retriables_as_unretriable @_retry_db_errors @six.wraps(f) def wrapped(*args, **kwargs): try: # copy mutable args and kwargs to make retries safe. this doesn't # prevent mutations of complex objects like the context or 'self' dup_args = [_copy_if_lds(a) for a in args] dup_kwargs = {k: _copy_if_lds(v) for k, v in kwargs.items()} return f(*dup_args, **dup_kwargs) except Exception as e: with excutils.save_and_reraise_exception(): if is_retriable(e): LOG.debug("Retry wrapper got retriable exception: %s", e) return wrapped def retry_if_session_inactive(context_var_name='context'): """Retries only if the session in the context is inactive. Calls a retry_db_errors wrapped version of the function if the context's session passed in is inactive, otherwise it just calls the function directly. This is useful to avoid retrying things inside of a transaction which is ineffective for DB races/errors. This should be used in all cases where retries are desired and the method accepts a context. """ def decorator(f): try: # NOTE(kevinbenton): we use pecan's util function here because it # deals with the horrors of finding args of already decorated # functions ctx_arg_index = p_util.getargspec(f).args.index(context_var_name) except ValueError: raise RuntimeError("Could not find position of var %s" % context_var_name) f_with_retry = retry_db_errors(f) @six.wraps(f) def wrapped(*args, **kwargs): # only use retry wrapper if we aren't nested in an active # transaction if context_var_name in kwargs: context = kwargs[context_var_name] else: context = args[ctx_arg_index] method = f if context.session.is_active else f_with_retry return method(*args, **kwargs) return wrapped return decorator def _is_nested_instance(e, etypes): """Check if exception or its inner excepts are an instance of etypes.""" if isinstance(e, etypes): return True if isinstance(e, exceptions.MultipleExceptions): return any(_is_nested_instance(i, etypes) for i in e.inner_exceptions) if isinstance(e, db_exc.DBError): return _is_nested_instance(e.inner_exception, etypes) return False @contextlib.contextmanager def exc_to_retry(etypes): try: yield except Exception as e: with excutils.save_and_reraise_exception() as ctx: if _is_nested_instance(e, etypes): ctx.reraise = False raise db_exc.RetryRequest(e) def get_reader_session(): """Helper to get reader session""" return context_manager.reader.get_sessionmaker()() def get_writer_session(): """Helper to get writer session""" return context_manager.writer.get_sessionmaker()() @contextlib.contextmanager def autonested_transaction(sess): """This is a convenience method to not bother with 'nested' parameter.""" if sess.is_active: session_context = sess.begin(nested=True) else: session_context = sess.begin(subtransactions=True) with session_context as tx: yield tx _REGISTERED_SQLA_EVENTS = [] def sqla_listen(*args): """Wrapper to track subscribers for test teardowns. SQLAlchemy has no "unsubscribe all" option for its event listener framework so we need to keep track of the subscribers by having them call through here for test teardowns. """ event.listen(*args) _REGISTERED_SQLA_EVENTS.append(args) def sqla_remove(*args): event.remove(*args) _REGISTERED_SQLA_EVENTS.remove(args) def sqla_remove_all(): for args in _REGISTERED_SQLA_EVENTS: try: event.remove(*args) except sql_exc.InvalidRequestError: # already removed pass del _REGISTERED_SQLA_EVENTS[:] @event.listens_for(orm.session.Session, "after_flush") def add_to_rel_load_list(session, flush_context=None): # keep track of new items to load relationships on during commit session.info.setdefault('_load_rels', weakref.WeakSet()).update( session.new) @event.listens_for(orm.session.Session, "before_commit") def load_one_to_manys(session): # TODO(kevinbenton): we should be able to remove this after we # have eliminated all places where related objects are constructed # using a key rather than a relationship. # capture any new objects if session.new: session.flush() if session.transaction.nested: # wait until final commit return for new_object in session.info.pop('_load_rels', []): if new_object not in session: # don't load detached objects because that brings them back into # session continue state = sqlalchemy.inspect(new_object) # set up relationship loading so that we can call lazy # loaders on the object even though the ".key" is not set up yet # (normally happens by in after_flush_postexec, but we're trying # to do this more succinctly). in this context this is only # setting a simple flag on the object's state. session.enable_relationship_loading(new_object) # look for eager relationships and do normal load. # For relationships where the related object is also # in the session these lazy loads will pull from the # identity map and not emit SELECT. Otherwise, we are still # local in the transaction so a normal SELECT load will work fine. for relationship_attr in state.mapper.relationships: if relationship_attr.lazy not in ('joined', 'subquery'): # we only want to automatically load relationships that would # automatically load during a lookup operation continue if relationship_attr.key not in state.dict: getattr(new_object, relationship_attr.key) assert relationship_attr.key in state.dict neutron-12.1.1/neutron/db/l3_hascheduler_db.py0000664000175000017500000000577213553660047021317 0ustar zuulzuul00000000000000# Copyright (C) 2014 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from neutron.db import l3_agentschedulers_db as l3_sch_db from neutron.objects import agent as ag_obj class L3_HA_scheduler_db_mixin(l3_sch_db.AZL3AgentSchedulerDbMixin): def get_l3_agents_ordered_by_num_routers(self, context, agent_ids): if not agent_ids: return [] return ag_obj.Agent.get_l3_agents_ordered_by_num_routers( context, agent_ids) def _get_agents_dict_for_router(self, agents_and_states): agents = [] for agent, ha_state in agents_and_states: l3_agent_dict = self._make_agent_dict(agent) l3_agent_dict['ha_state'] = ha_state agents.append(l3_agent_dict) return {'agents': agents} def list_l3_agents_hosting_router(self, context, router_id): with context.session.begin(subtransactions=True): router_db = self._get_router(context, router_id) if router_db.extra_attributes.ha: agents = self.get_l3_bindings_hosting_router_with_ha_states( context, router_id) else: agents = self._get_l3_agents_hosting_routers( context, [router_id]) agents = [(agent, None) for agent in agents] return self._get_agents_dict_for_router(agents) def _notify_l3_agent_ha_port_update(resource, event, trigger, **kwargs): new_port = kwargs.get('port') original_port = kwargs.get('original_port') context = kwargs.get('context') host = new_port[portbindings.HOST_ID] if new_port and original_port and host: new_device_owner = new_port.get('device_owner', '') if (new_device_owner == constants.DEVICE_OWNER_ROUTER_HA_INTF and new_port['status'] == constants.PORT_STATUS_ACTIVE and original_port['status'] != new_port['status']): l3plugin = directory.get_plugin(plugin_constants.L3) l3plugin.l3_rpc_notifier.routers_updated_on_host( context, [new_port['device_id']], host) def subscribe(): registry.subscribe( _notify_l3_agent_ha_port_update, resources.PORT, events.AFTER_UPDATE) neutron-12.1.1/neutron/db/quota/0000775000175000017500000000000013553660156016532 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/quota/api.py0000664000175000017500000002206413553660047017660 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import datetime from neutron.db import api as db_api from neutron.objects import quota as quota_obj # Wrapper for utcnow - needed for mocking it in unit tests def utcnow(): return datetime.datetime.utcnow() class QuotaUsageInfo(collections.namedtuple( 'QuotaUsageInfo', ['resource', 'tenant_id', 'used', 'dirty'])): """Information about resource quota usage.""" class ReservationInfo(collections.namedtuple( 'ReservationInfo', ['reservation_id', 'tenant_id', 'expiration', 'deltas'])): """Information about a resource reservation.""" @db_api.retry_if_session_inactive() def get_quota_usage_by_resource_and_tenant(context, resource, tenant_id): """Return usage info for a given resource and tenant. :param context: Request context :param resource: Name of the resource :param tenant_id: Tenant identifier :returns: a QuotaUsageInfo instance """ result = quota_obj.QuotaUsage.get_object_dirty_protected( context, resource=resource, project_id=tenant_id) if not result: return return QuotaUsageInfo(result.resource, result.project_id, result.in_use, result.dirty) @db_api.retry_if_session_inactive() def get_quota_usage_by_resource(context, resource): objs = quota_obj.QuotaUsage.get_objects(context, resource=resource) return [QuotaUsageInfo(item.resource, item.project_id, item.in_use, item.dirty) for item in objs] @db_api.retry_if_session_inactive() def get_quota_usage_by_tenant_id(context, tenant_id): objs = quota_obj.QuotaUsage.get_objects(context, project_id=tenant_id) return [QuotaUsageInfo(item.resource, tenant_id, item.in_use, item.dirty) for item in objs] @db_api.retry_if_session_inactive() def set_quota_usage(context, resource, tenant_id, in_use=None, delta=False): """Set resource quota usage. :param context: instance of neutron context with db session :param resource: name of the resource for which usage is being set :param tenant_id: identifier of the tenant for which quota usage is being set :param in_use: integer specifying the new quantity of used resources, or a delta to apply to current used resource :param delta: Specifies whether in_use is an absolute number or a delta (default to False) """ with db_api.context_manager.writer.using(context): usage_data = quota_obj.QuotaUsage.get_object( context, resource=resource, project_id=tenant_id) if not usage_data: # Must create entry usage_data = quota_obj.QuotaUsage( context, resource=resource, project_id=tenant_id) usage_data.create() # Perform explicit comparison with None as 0 is a valid value if in_use is not None: if delta: in_use = usage_data.in_use + in_use usage_data.in_use = in_use # After an explicit update the dirty bit should always be reset usage_data.dirty = False usage_data.update() return QuotaUsageInfo(usage_data.resource, usage_data.project_id, usage_data.in_use, usage_data.dirty) @db_api.retry_if_session_inactive() @db_api.context_manager.writer def set_quota_usage_dirty(context, resource, tenant_id, dirty=True): """Set quota usage dirty bit for a given resource and tenant. :param resource: a resource for which quota usage if tracked :param tenant_id: tenant identifier :param dirty: the desired value for the dirty bit (defaults to True) :returns: 1 if the quota usage data were updated, 0 otherwise. """ obj = quota_obj.QuotaUsage.get_object( context, resource=resource, project_id=tenant_id) if obj: obj.dirty = dirty obj.update() return 1 return 0 @db_api.retry_if_session_inactive() @db_api.context_manager.writer def set_resources_quota_usage_dirty(context, resources, tenant_id, dirty=True): """Set quota usage dirty bit for a given tenant and multiple resources. :param resources: list of resource for which the dirty bit is going to be set :param tenant_id: tenant identifier :param dirty: the desired value for the dirty bit (defaults to True) :returns: the number of records for which the bit was actually set. """ filters = {'project_id': tenant_id} if resources: filters['resource'] = resources objs = quota_obj.QuotaUsage.get_objects(context, **filters) for obj in objs: obj.dirty = dirty obj.update() return len(objs) @db_api.retry_if_session_inactive() @db_api.context_manager.writer def set_all_quota_usage_dirty(context, resource, dirty=True): """Set the dirty bit on quota usage for all tenants. :param resource: the resource for which the dirty bit should be set :returns: the number of tenants for which the dirty bit was actually updated """ # TODO(manjeets) consider squashing this method with # set_resources_quota_usage_dirty objs = quota_obj.QuotaUsage.get_objects(context, resource=resource) for obj in objs: obj.dirty = dirty obj.update() return len(objs) @db_api.retry_if_session_inactive() def create_reservation(context, tenant_id, deltas, expiration=None): # This method is usually called from within another transaction. # Consider using begin_nested expiration = expiration or (utcnow() + datetime.timedelta(0, 120)) delta_objs = [] for (resource, delta) in deltas.items(): delta_objs.append(quota_obj.ResourceDelta( context, resource=resource, amount=delta)) reserv_obj = quota_obj.Reservation( context, project_id=tenant_id, expiration=expiration, resource_deltas=delta_objs) reserv_obj.create() return ReservationInfo(reserv_obj['id'], reserv_obj['project_id'], reserv_obj['expiration'], dict((delta.resource, delta.amount) for delta in reserv_obj.resource_deltas)) @db_api.retry_if_session_inactive() def get_reservation(context, reservation_id): reserv_obj = quota_obj.Reservation.get_object(context, id=reservation_id) if not reserv_obj: return return ReservationInfo(reserv_obj['id'], reserv_obj['project_id'], reserv_obj['expiration'], dict((delta.resource, delta.amount) for delta in reserv_obj.resource_deltas)) @db_api.retry_if_session_inactive() @db_api.context_manager.writer def remove_reservation(context, reservation_id, set_dirty=False): reservation = quota_obj.Reservation.get_object(context, id=reservation_id) if not reservation: # TODO(salv-orlando): Raise here and then handle the exception? return tenant_id = reservation.project_id resources = [delta.resource for delta in reservation.resource_deltas] reservation.delete() if set_dirty: # quota_usage for all resource involved in this reservation must # be marked as dirty set_resources_quota_usage_dirty(context, resources, tenant_id) return 1 @db_api.retry_if_session_inactive() def get_reservations_for_resources(context, tenant_id, resources, expired=False): """Retrieve total amount of reservations for specified resources. :param context: Neutron context with db session :param tenant_id: Tenant identifier :param resources: Resources for which reserved amounts should be fetched :param expired: False to fetch active reservations, True to fetch expired reservations (defaults to False) :returns: a dictionary mapping resources with corresponding deltas """ # NOTE(manjeets) we are using utcnow() here because it # can be mocked easily where as datetime is built in type # mock.path does not allow mocking built in types. return quota_obj.Reservation.get_total_reservations_map( context, utcnow(), tenant_id, resources, expired) @db_api.retry_if_session_inactive() @db_api.context_manager.writer def remove_expired_reservations(context, tenant_id=None): return quota_obj.Reservation.delete_expired(context, utcnow(), tenant_id) neutron-12.1.1/neutron/db/quota/models.py0000664000175000017500000000457613553660046020401 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from sqlalchemy import sql class ResourceDelta(model_base.BASEV2): resource = sa.Column(sa.String(255), primary_key=True) reservation_id = sa.Column(sa.String(36), sa.ForeignKey('reservations.id', ondelete='CASCADE'), primary_key=True, nullable=False) # Requested amount of resource amount = sa.Column(sa.Integer) class Reservation(model_base.BASEV2, model_base.HasId, model_base.HasProjectNoIndex): expiration = sa.Column(sa.DateTime()) resource_deltas = orm.relationship(ResourceDelta, backref='reservation', lazy="joined", cascade='all, delete-orphan') class Quota(model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represent a single quota override for a tenant. If there is no row for a given tenant id and resource, then the default for the deployment is used. """ resource = sa.Column(sa.String(255)) limit = sa.Column(sa.Integer) class QuotaUsage(model_base.BASEV2, model_base.HasProjectPrimaryKeyIndex): """Represents the current usage for a given resource.""" resource = sa.Column(sa.String(255), nullable=False, primary_key=True, index=True) dirty = sa.Column(sa.Boolean, nullable=False, server_default=sql.false()) in_use = sa.Column(sa.Integer, nullable=False, server_default="0") reserved = sa.Column(sa.Integer, nullable=False, server_default="0") neutron-12.1.1/neutron/db/quota/__init__.py0000664000175000017500000000000013553660046020627 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/quota/driver.py0000664000175000017500000003431313553660047020402 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api import attributes from neutron_lib import exceptions from neutron_lib.plugins import constants from neutron_lib.plugins import directory from oslo_log import log from neutron.common import exceptions as n_exc from neutron.db import api as db_api from neutron.db.quota import api as quota_api from neutron.objects import quota as quota_obj from neutron.quota import resource as res LOG = log.getLogger(__name__) class DbQuotaDriver(object): """Driver to perform necessary checks to enforce quotas and obtain quota information. The default driver utilizes the local database. """ @staticmethod def get_default_quotas(context, resources, tenant_id): """Given a list of resources, retrieve the default quotas set for a tenant. :param context: The request context, for access checks. :param resources: A dictionary of the registered resource keys. :param tenant_id: The ID of the tenant to return default quotas for. :return: dict from resource name to dict of name and limit """ # Currently the tenant_id parameter is unused, since all tenants # share the same default values. This may change in the future so # we include tenant-id to remain backwards compatible. return dict((key, resource.default) for key, resource in resources.items()) @staticmethod @db_api.retry_if_session_inactive() def get_tenant_quotas(context, resources, tenant_id): """Given a list of resources, retrieve the quotas for the given tenant. If no limits are found for the specified tenant, the operation returns the default limits. :param context: The request context, for access checks. :param resources: A dictionary of the registered resource keys. :param tenant_id: The ID of the tenant to return quotas for. :return: dict from resource name to dict of name and limit """ # init with defaults tenant_quota = dict((key, resource.default) for key, resource in resources.items()) # update with tenant specific limits quota_objs = quota_obj.Quota.get_objects(context, project_id=tenant_id) for item in quota_objs: tenant_quota[item['resource']] = item['limit'] return tenant_quota @staticmethod @db_api.retry_if_session_inactive() def get_detailed_tenant_quotas(context, resources, tenant_id): """Given a list of resources and a sepecific tenant, retrieve the detailed quotas (limit, used, reserved). :param context: The request context, for access checks. :param resources: A dictionary of the registered resource keys. :return dict: mapping resource name in dict to its corresponding limit used and reserved. Reserved currently returns default value of 0 """ res_reserve_info = quota_api.get_reservations_for_resources( context, tenant_id, resources.keys()) tenant_quota_ext = {} for key, resource in resources.items(): if isinstance(resource, res.TrackedResource): used = resource.count_used(context, tenant_id, resync_usage=False) else: # NOTE(ihrachys) .count won't use the plugin we pass, but we # pass it regardless to keep the quota driver API intact plugins = directory.get_plugins() plugin = plugins.get(key, plugins[constants.CORE]) used = resource.count(context, plugin, tenant_id) tenant_quota_ext[key] = { 'limit': resource.default, 'used': used, 'reserved': res_reserve_info.get(key, 0), } #update with specific tenant limits quota_objs = quota_obj.Quota.get_objects(context, project_id=tenant_id) for item in quota_objs: tenant_quota_ext[item['resource']]['limit'] = item['limit'] return tenant_quota_ext @staticmethod @db_api.retry_if_session_inactive() def delete_tenant_quota(context, tenant_id): """Delete the quota entries for a given tenant_id. After deletion, this tenant will use default quota values in conf. Raise a "not found" error if the quota for the given tenant was never defined. """ if quota_obj.Quota.delete_objects( context, project_id=tenant_id) < 1: # No record deleted means the quota was not found raise n_exc.TenantQuotaNotFound(tenant_id=tenant_id) @staticmethod @db_api.retry_if_session_inactive() def get_all_quotas(context, resources): """Given a list of resources, retrieve the quotas for the all tenants. :param context: The request context, for access checks. :param resources: A dictionary of the registered resource keys. :return: quotas list of dict of tenant_id:, resourcekey1: resourcekey2: ... """ tenant_default = dict((key, resource.default) for key, resource in resources.items()) all_tenant_quotas = {} for quota in quota_obj.Quota.get_objects(context): tenant_id = quota['project_id'] # avoid setdefault() because only want to copy when actually # required tenant_quota = all_tenant_quotas.get(tenant_id) if tenant_quota is None: tenant_quota = tenant_default.copy() tenant_quota['tenant_id'] = tenant_id attributes.populate_project_info(tenant_quota) all_tenant_quotas[tenant_id] = tenant_quota tenant_quota[quota['resource']] = quota['limit'] # Convert values to a list to as caller expect an indexable iterable, # where python3's dict_values does not support indexing return list(all_tenant_quotas.values()) @staticmethod @db_api.retry_if_session_inactive() def update_quota_limit(context, tenant_id, resource, limit): tenant_quotas = quota_obj.Quota.get_objects( context, project_id=tenant_id, resource=resource) if tenant_quotas: tenant_quotas[0].limit = limit tenant_quotas[0].update() else: quota_obj.Quota(context, project_id=tenant_id, resource=resource, limit=limit).create() def _get_quotas(self, context, tenant_id, resources): """Retrieves the quotas for specific resources. A helper method which retrieves the quotas for the specific resources identified by keys, and which apply to the current context. :param context: The request context, for access checks. :param tenant_id: the tenant_id to check quota. :param resources: A dictionary of the registered resources. """ # Grab and return the quotas (without usages) quotas = DbQuotaDriver.get_tenant_quotas( context, resources, tenant_id) return dict((k, v) for k, v in quotas.items()) def _handle_expired_reservations(self, context, tenant_id): LOG.debug("Deleting expired reservations for tenant:%s", tenant_id) # Delete expired reservations (we don't want them to accrue # in the database) quota_api.remove_expired_reservations( context, tenant_id=tenant_id) @db_api.retry_if_session_inactive() def make_reservation(self, context, tenant_id, resources, deltas, plugin): # Lock current reservation table # NOTE(salv-orlando): This routine uses DB write locks. # These locks are acquired by the count() method invoked on resources. # Please put your shotguns aside. # A non locking algorithm for handling reservation is feasible, however # it will require two database writes even in cases when there are not # concurrent reservations. # For this reason it might be advisable to handle contention using # this kind of locks and paying the cost of a write set certification # failure when a MySQL Galera cluster is employed. Also, this class of # locks should be ok to use when support for sending "hotspot" writes # to a single node will be available. requested_resources = deltas.keys() with db_api.context_manager.writer.using(context): # get_tenant_quotes needs in input a dictionary mapping resource # name to BaseResosurce instances so that the default quota can be # retrieved current_limits = self.get_tenant_quotas( context, resources, tenant_id) unlimited_resources = set([resource for (resource, limit) in current_limits.items() if limit < 0]) # Do not even bother counting resources and calculating headroom # for resources with unlimited quota LOG.debug("Resources %s have unlimited quota limit. It is not " "required to calculate headroom ", ",".join(unlimited_resources)) requested_resources = (set(requested_resources) - unlimited_resources) # Gather current usage information # TODO(salv-orlando): calling count() for every resource triggers # multiple queries on quota usage. This should be improved, however # this is not an urgent matter as the REST API currently only # allows allocation of a resource at a time # NOTE: pass plugin too for compatibility with CountableResource # instances current_usages = dict( (resource, resources[resource].count( context, plugin, tenant_id, resync_usage=False)) for resource in requested_resources) # Adjust for expired reservations. Apparently it is cheaper than # querying every time for active reservations and counting overall # quantity of resources reserved expired_deltas = quota_api.get_reservations_for_resources( context, tenant_id, requested_resources, expired=True) # Verify that the request can be accepted with current limits resources_over_limit = [] for resource in requested_resources: expired_reservations = expired_deltas.get(resource, 0) total_usage = current_usages[resource] - expired_reservations res_headroom = current_limits[resource] - total_usage LOG.debug(("Attempting to reserve %(delta)d items for " "resource %(resource)s. Total usage: %(total)d; " "quota limit: %(limit)d; headroom:%(headroom)d"), {'resource': resource, 'delta': deltas[resource], 'total': total_usage, 'limit': current_limits[resource], 'headroom': res_headroom}) if res_headroom < deltas[resource]: resources_over_limit.append(resource) if expired_reservations: self._handle_expired_reservations(context, tenant_id) if resources_over_limit: raise exceptions.OverQuota(overs=sorted(resources_over_limit)) # Success, store the reservation # TODO(salv-orlando): Make expiration time configurable return quota_api.create_reservation( context, tenant_id, deltas) def commit_reservation(self, context, reservation_id): # Do not mark resource usage as dirty. If a reservation is committed, # then the relevant resources have been created. Usage data for these # resources has therefore already been marked dirty. quota_api.remove_reservation(context, reservation_id, set_dirty=False) def cancel_reservation(self, context, reservation_id): # Mark resource usage as dirty so the next time both actual resources # used and reserved will be recalculated quota_api.remove_reservation(context, reservation_id, set_dirty=True) def limit_check(self, context, tenant_id, resources, values): """Check simple quota limits. For limits--those quotas for which there is no usage synchronization function--this method checks that a set of proposed values are permitted by the limit restriction. If any of the proposed values is over the defined quota, an OverQuota exception will be raised with the sorted list of the resources which are too high. Otherwise, the method returns nothing. :param context: The request context, for access checks. :param tenant_id: The tenant_id to check the quota. :param resources: A dictionary of the registered resources. :param values: A dictionary of the values to check against the quota. """ # Ensure no value is less than zero unders = [key for key, val in values.items() if val < 0] if unders: raise n_exc.InvalidQuotaValue(unders=sorted(unders)) # Get the applicable quotas quotas = self._get_quotas(context, tenant_id, resources) # Check the quotas and construct a list of the resources that # would be put over limit by the desired values overs = [key for key, val in values.items() if 0 <= quotas[key] < val] if overs: raise exceptions.OverQuota(overs=sorted(overs)) neutron-12.1.1/neutron/db/portsecurity_db.py0000664000175000017500000000547613553660047021207 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import network as net_def from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import port_security as psec from neutron_lib.api import validators from neutron_lib.plugins import directory from neutron_lib.utils import net from neutron.db import _resource_extend as resource_extend from neutron.db import portsecurity_db_common @resource_extend.has_resource_extenders class PortSecurityDbMixin(portsecurity_db_common.PortSecurityDbCommon): @staticmethod @resource_extend.extends([net_def.COLLECTION_NAME, port_def.COLLECTION_NAME]) def _extend_port_security_dict(response_data, db_data): plugin = directory.get_plugin() if ('port-security' in getattr(plugin, 'supported_extension_aliases', [])): super(PortSecurityDbMixin, plugin)._extend_port_security_dict( response_data, db_data) def _determine_port_security_and_has_ip(self, context, port): """Returns a tuple of booleans (port_security_enabled, has_ip). Port_security is the value associated with the port if one is present otherwise the value associated with the network is returned. has_ip is if the port is associated with an ip or not. """ has_ip = self._ip_on_port(port) # we don't apply security groups for dhcp, router if port.get('device_owner') and net.is_port_trusted(port): return (False, has_ip) if validators.is_attr_set(port.get(psec.PORTSECURITY)): port_security_enabled = port[psec.PORTSECURITY] # If port has an ip and security_groups are passed in # conveniently set port_security_enabled to true this way # user doesn't also have to pass in port_security_enabled=True # when creating ports. elif has_ip and validators.is_attr_set(port.get('security_groups')): port_security_enabled = True else: port_security_enabled = self._get_network_security_binding( context, port['network_id']) return (port_security_enabled, has_ip) def _ip_on_port(self, port): return bool(port.get('fixed_ips')) neutron-12.1.1/neutron/db/common_db_mixin.py0000664000175000017500000000754213553660047021123 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import weakref from neutron.db import _model_query from neutron.db import _resource_extend from neutron.db import _utils as ndb_utils # TODO(HenryG): Remove these when available in neutron-lib safe_creation = ndb_utils.safe_creation model_query_scope = ndb_utils.model_query_scope_is_project model_query = ndb_utils.model_query resource_fields = ndb_utils.resource_fields # TODO(HenryG): Deprecate and schedule for removal class CommonDbMixin(object): """Deprecated.""" @staticmethod def register_model_query_hook(model, name, query_hook, filter_hook, result_filters=None): _model_query.register_hook( model, name, query_hook, filter_hook, result_filters=result_filters) @staticmethod def register_dict_extend_funcs(resource, funcs): _resource_extend.register_funcs(resource, funcs) @property # TODO(HenryG): Remove; used only by vmware-nsx. def safe_reference(self): return weakref.proxy(self) @staticmethod def model_query_scope(context, model): return ndb_utils.model_query_scope_is_project(context, model) @staticmethod def _model_query(context, model): return _model_query.query_with_hooks(context, model) @staticmethod def _fields(resource, fields): return ndb_utils.resource_fields(resource, fields) @staticmethod def _get_by_id(context, model, id): return _model_query.get_by_id(context, model, id) @staticmethod def _apply_filters_to_query(query, model, filters, context=None): return _model_query.apply_filters(query, model, filters, context) @staticmethod def _apply_dict_extend_functions(resource_type, response, db_object): _resource_extend.apply_funcs(resource_type, response, db_object) @staticmethod def _get_collection_query(context, model, filters=None, sorts=None, limit=None, marker_obj=None, page_reverse=False): return _model_query.get_collection_query(context, model, filters, sorts, limit, marker_obj, page_reverse) @staticmethod def _get_collection(context, model, dict_func, filters=None, fields=None, sorts=None, limit=None, marker_obj=None, page_reverse=False): return _model_query.get_collection(context, model, dict_func, filters, fields, sorts, limit, marker_obj, page_reverse) @staticmethod def _get_collection_count(context, model, filters=None): return _model_query.get_collection_count(context, model, filters) # TODO(HenryG): Remove this when available in neutron-lib def _get_marker_obj(self, context, resource, limit, marker): return ndb_utils.get_marker_obj(self, context, resource, limit, marker) @staticmethod def _filter_non_model_columns(data, model): return ndb_utils.filter_non_model_columns(data, model) neutron-12.1.1/neutron/db/l3_dvr_ha_scheduler_db.py0000664000175000017500000000367613553660046022331 0ustar zuulzuul00000000000000# Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import neutron.db.l3_dvrscheduler_db as l3agent_dvr_sch_db import neutron.db.l3_hascheduler_db as l3_ha_sch_db class L3_DVR_HA_scheduler_db_mixin(l3agent_dvr_sch_db.L3_DVRsch_db_mixin, l3_ha_sch_db.L3_HA_scheduler_db_mixin): def get_dvr_routers_to_remove(self, context, port_id, get_related_hosts_info=True): """Returns info about which routers should be removed In case dvr serviceable port was deleted we need to check if any dvr routers should be removed from l3 agent on port's host """ remove_router_info = super( L3_DVR_HA_scheduler_db_mixin, self).get_dvr_routers_to_remove( context, port_id, get_related_hosts_info) # Process the router information which was returned to make # sure we don't delete routers which have dvrhs snat bindings. processed_remove_router_info = [] for router_info in remove_router_info: router_id = router_info['router_id'] agent_id = router_info['agent_id'] if not self._check_router_agent_ha_binding( context, router_id, agent_id): processed_remove_router_info.append(router_info) return processed_remove_router_info neutron-12.1.1/neutron/db/qos/0000775000175000017500000000000013553660156016203 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/qos/models.py0000664000175000017500000001614413553660047020045 0ustar zuulzuul00000000000000# Copyright 2015 Huawei Technologies India Pvt Ltd, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa from neutron.db.models import l3 from neutron.db import models_v2 from neutron.db import rbac_db_models from neutron.db import standard_attr class QosPolicy(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, model_base.HasProject): __tablename__ = 'qos_policies' name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE)) rbac_entries = sa.orm.relationship(rbac_db_models.QosPolicyRBAC, backref='qos_policy', lazy='subquery', cascade='all, delete, delete-orphan') api_collections = ['policies'] collection_resource_map = {'policies': 'policy'} tag_support = True class QosNetworkPolicyBinding(model_base.BASEV2): __tablename__ = 'qos_network_policy_bindings' policy_id = sa.Column(sa.String(36), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False, primary_key=True) network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', ondelete='CASCADE'), nullable=False, unique=True, primary_key=True) revises_on_change = ('network', ) network = sa.orm.relationship( models_v2.Network, load_on_pending=True, backref=sa.orm.backref("qos_policy_binding", uselist=False, cascade='delete', lazy='joined')) class QosFIPPolicyBinding(model_base.BASEV2): __tablename__ = 'qos_fip_policy_bindings' policy_id = sa.Column(sa.String(db_const.UUID_FIELD_SIZE), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False, primary_key=True) fip_id = sa.Column(sa.String(db_const.UUID_FIELD_SIZE), sa.ForeignKey('floatingips.id', ondelete='CASCADE'), nullable=False, unique=True, primary_key=True) revises_on_change = ('floatingip', ) floatingip = sa.orm.relationship( l3.FloatingIP, load_on_pending=True, backref=sa.orm.backref("qos_policy_binding", uselist=False, cascade='delete', lazy='joined')) class QosPortPolicyBinding(model_base.BASEV2): __tablename__ = 'qos_port_policy_bindings' policy_id = sa.Column(sa.String(36), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False, primary_key=True) port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete='CASCADE'), nullable=False, unique=True, primary_key=True) revises_on_change = ('port', ) port = sa.orm.relationship( models_v2.Port, load_on_pending=True, backref=sa.orm.backref("qos_policy_binding", uselist=False, cascade='delete', lazy='joined')) class QosPolicyDefault(model_base.BASEV2, model_base.HasProjectPrimaryKeyIndex): __tablename__ = 'qos_policies_default' qos_policy_id = sa.Column(sa.String(36), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False) revises_on_change = ('qos_policy',) qos_policy = sa.orm.relationship(QosPolicy, load_on_pending=True) class QosBandwidthLimitRule(model_base.HasId, model_base.BASEV2): __tablename__ = 'qos_bandwidth_limit_rules' qos_policy_id = sa.Column(sa.String(36), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False) max_kbps = sa.Column(sa.Integer) max_burst_kbps = sa.Column(sa.Integer) revises_on_change = ('qos_policy', ) qos_policy = sa.orm.relationship(QosPolicy, load_on_pending=True) direction = sa.Column(sa.Enum(constants.EGRESS_DIRECTION, constants.INGRESS_DIRECTION, name="directions"), default=constants.EGRESS_DIRECTION, server_default=constants.EGRESS_DIRECTION, nullable=False) __table_args__ = ( sa.UniqueConstraint( qos_policy_id, direction, name="qos_bandwidth_rules0qos_policy_id0direction"), model_base.BASEV2.__table_args__ ) class QosDscpMarkingRule(model_base.HasId, model_base.BASEV2): __tablename__ = 'qos_dscp_marking_rules' qos_policy_id = sa.Column(sa.String(36), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False, unique=True) dscp_mark = sa.Column(sa.Integer) revises_on_change = ('qos_policy', ) qos_policy = sa.orm.relationship(QosPolicy, load_on_pending=True) class QosMinimumBandwidthRule(model_base.HasId, model_base.BASEV2): __tablename__ = 'qos_minimum_bandwidth_rules' qos_policy_id = sa.Column(sa.String(36), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False, index=True) min_kbps = sa.Column(sa.Integer) direction = sa.Column(sa.Enum(constants.EGRESS_DIRECTION, constants.INGRESS_DIRECTION, name='directions'), nullable=False, server_default=constants.EGRESS_DIRECTION) revises_on_change = ('qos_policy', ) qos_policy = sa.orm.relationship(QosPolicy, load_on_pending=True) __table_args__ = ( sa.UniqueConstraint( qos_policy_id, direction, name='qos_minimum_bandwidth_rules0qos_policy_id0direction'), model_base.BASEV2.__table_args__ ) neutron-12.1.1/neutron/db/qos/__init__.py0000664000175000017500000000000013553660046020300 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/db_base_plugin_common.py0000664000175000017500000003451513553660047022267 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import netaddr from neutron_lib.api.definitions import network as net_def from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import subnet as subnet_def from neutron_lib.api.definitions import subnetpool as subnetpool_def from neutron_lib.api import validators from neutron_lib import constants from neutron_lib import exceptions as n_exc from neutron_lib.utils import net from oslo_config import cfg from oslo_log import log as logging from sqlalchemy.orm import exc from neutron.common import constants as n_const from neutron.common import exceptions from neutron.db import _model_query as model_query from neutron.db import _resource_extend as resource_extend from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.db import common_db_mixin from neutron.db import models_v2 from neutron.objects import base as base_obj from neutron.objects import ports as port_obj from neutron.objects import subnet as subnet_obj from neutron.objects import subnetpool as subnetpool_obj LOG = logging.getLogger(__name__) def convert_result_to_dict(f): @functools.wraps(f) def inner(*args, **kwargs): result = f(*args, **kwargs) if result is None: return None elif isinstance(result, list): return [r.to_dict() for r in result] else: return result.to_dict() return inner def filter_fields(f): @functools.wraps(f) def inner_filter(*args, **kwargs): result = f(*args, **kwargs) fields = kwargs.get('fields') if not fields: try: pos = f.__code__.co_varnames.index('fields') fields = args[pos] except (IndexError, ValueError): return result do_filter = lambda d: {k: v for k, v in d.items() if k in fields} if isinstance(result, list): return [do_filter(obj) for obj in result] else: return do_filter(result) return inner_filter class DbBasePluginCommon(common_db_mixin.CommonDbMixin): """Stores getters and helper methods for db_base_plugin_v2 All private getters and simple helpers like _make_*_dict were moved from db_base_plugin_v2. More complicated logic and public methods left in db_base_plugin_v2. Main purpose of this class is to make getters accessible for Ipam backends. """ @staticmethod def _generate_mac(): return net.get_random_mac(cfg.CONF.base_mac.split(':')) @db_api.context_manager.reader def _is_mac_in_use(self, context, network_id, mac_address): return port_obj.Port.objects_exist(context, network_id=network_id, mac_address=mac_address) @staticmethod def _delete_ip_allocation(context, network_id, subnet_id, ip_address): # Delete the IP address from the IPAllocate table LOG.debug("Delete allocated IP %(ip_address)s " "(%(network_id)s/%(subnet_id)s)", {'ip_address': ip_address, 'network_id': network_id, 'subnet_id': subnet_id}) port_obj.IPAllocation.delete_objects( context, network_id=network_id, ip_address=ip_address, subnet_id=subnet_id) @staticmethod @db_api.context_manager.writer def _store_ip_allocation(context, ip_address, network_id, subnet_id, port_id): LOG.debug("Allocated IP %(ip_address)s " "(%(network_id)s/%(subnet_id)s/%(port_id)s)", {'ip_address': ip_address, 'network_id': network_id, 'subnet_id': subnet_id, 'port_id': port_id}) allocated = port_obj.IPAllocation( context, network_id=network_id, port_id=port_id, ip_address=ip_address, subnet_id=subnet_id) # NOTE(lujinluo): Add IPAllocations obj to the port fixed_ips # in Port OVO integration, i.e. the same way we did in # Ib32509d974c8654131112234bcf19d6eae8f7cca allocated.create() def _make_subnet_dict(self, subnet, fields=None, context=None): res = {'id': subnet['id'], 'name': subnet['name'], 'tenant_id': subnet['tenant_id'], 'network_id': subnet['network_id'], 'ip_version': subnet['ip_version'], 'subnetpool_id': subnet['subnetpool_id'], 'enable_dhcp': subnet['enable_dhcp'], 'ipv6_ra_mode': subnet['ipv6_ra_mode'], 'ipv6_address_mode': subnet['ipv6_address_mode'], } res['gateway_ip'] = str( subnet['gateway_ip']) if subnet['gateway_ip'] else None # TODO(korzen) this method can get subnet as DB object or Subnet OVO, # so temporary workaround will be to fill in the fields in separate # ways. After converting all code pieces to use Subnet OVO, the latter # 'else' can be deleted if isinstance(subnet, subnet_obj.Subnet): res['cidr'] = str(subnet.cidr) res['allocation_pools'] = [{'start': str(pool.start), 'end': str(pool.end)} for pool in subnet.allocation_pools] res['host_routes'] = [{'destination': str(route.destination), 'nexthop': str(route.nexthop)} for route in subnet.host_routes] res['dns_nameservers'] = [str(dns.address) for dns in subnet.dns_nameservers] res['shared'] = subnet.shared # Call auxiliary extend functions, if any resource_extend.apply_funcs(subnet_def.COLLECTION_NAME, res, subnet.db_obj) else: res['cidr'] = subnet['cidr'] res['allocation_pools'] = [{'start': pool['first_ip'], 'end': pool['last_ip']} for pool in subnet['allocation_pools']] res['host_routes'] = [{'destination': route['destination'], 'nexthop': route['nexthop']} for route in subnet['routes']] res['dns_nameservers'] = [dns['address'] for dns in subnet['dns_nameservers']] # The shared attribute for a subnet is the same # as its parent network res['shared'] = self._is_network_shared(context, subnet.rbac_entries) # Call auxiliary extend functions, if any resource_extend.apply_funcs(subnet_def.COLLECTION_NAME, res, subnet) return db_utils.resource_fields(res, fields) def _make_subnetpool_dict(self, subnetpool, fields=None): default_prefixlen = str(subnetpool['default_prefixlen']) min_prefixlen = str(subnetpool['min_prefixlen']) max_prefixlen = str(subnetpool['max_prefixlen']) res = {'id': subnetpool['id'], 'name': subnetpool['name'], 'tenant_id': subnetpool['tenant_id'], 'default_prefixlen': default_prefixlen, 'min_prefixlen': min_prefixlen, 'max_prefixlen': max_prefixlen, 'is_default': subnetpool['is_default'], 'shared': subnetpool['shared'], 'prefixes': [prefix.cidr for prefix in subnetpool['prefixes']], 'ip_version': subnetpool['ip_version'], 'default_quota': subnetpool['default_quota'], 'address_scope_id': subnetpool['address_scope_id']} resource_extend.apply_funcs( subnetpool_def.COLLECTION_NAME, res, subnetpool) return db_utils.resource_fields(res, fields) def _make_port_dict(self, port, fields=None, process_extensions=True): res = {"id": port["id"], 'name': port['name'], "network_id": port["network_id"], 'tenant_id': port['tenant_id'], "mac_address": port["mac_address"], "admin_state_up": port["admin_state_up"], "status": port["status"], "fixed_ips": [{'subnet_id': ip["subnet_id"], 'ip_address': ip["ip_address"]} for ip in port["fixed_ips"]], "device_id": port["device_id"], "device_owner": port["device_owner"]} # Call auxiliary extend functions, if any if process_extensions: resource_extend.apply_funcs(port_def.COLLECTION_NAME, res, port) return db_utils.resource_fields(res, fields) def _get_network(self, context, id): try: network = model_query.get_by_id(context, models_v2.Network, id) except exc.NoResultFound: raise n_exc.NetworkNotFound(net_id=id) return network def _get_subnet(self, context, id): # TODO(slaweq): remove this method when all will be switched to use OVO # objects only try: subnet = model_query.get_by_id(context, models_v2.Subnet, id) except exc.NoResultFound: raise n_exc.SubnetNotFound(subnet_id=id) return subnet def _get_subnet_object(self, context, id): subnet = subnet_obj.Subnet.get_object(context, id=id) if not subnet: raise n_exc.SubnetNotFound(subnet_id=id) return subnet def _get_subnetpool(self, context, id): subnetpool = subnetpool_obj.SubnetPool.get_object( context, id=id) if not subnetpool: raise exceptions.SubnetPoolNotFound(subnetpool_id=id) return subnetpool def _get_port(self, context, id): try: port = model_query.get_by_id(context, models_v2.Port, id) except exc.NoResultFound: raise n_exc.PortNotFound(port_id=id) return port def _get_route_by_subnet(self, context, subnet_id): return subnet_obj.Route.get_objects(context, subnet_id=subnet_id) def _get_router_gw_ports_by_network(self, context, network_id): return port_obj.Port.get_objects( context, network_id=network_id, device_owner=constants.DEVICE_OWNER_ROUTER_GW) @db_api.context_manager.reader def _get_subnets_by_network(self, context, network_id): return subnet_obj.Subnet.get_objects(context, network_id=network_id) @db_api.context_manager.reader def _get_subnets_by_subnetpool(self, context, subnetpool_id): return subnet_obj.Subnet.get_objects(context, subnetpool_id=subnetpool_id) def _get_subnets(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pager = base_obj.Pager(sorts, limit, page_reverse, marker) filters = filters or {} # turn the CIDRs into a proper subnets if filters.get('cidr'): filters.update( {'cidr': [netaddr.IPNetwork(x).cidr for x in filters['cidr']]}) return subnet_obj.Subnet.get_objects(context, _pager=pager, validate_filters=False, **filters) def _make_network_dict(self, network, fields=None, process_extensions=True, context=None): res = {'id': network['id'], 'name': network['name'], 'tenant_id': network['tenant_id'], 'admin_state_up': network['admin_state_up'], 'mtu': network.get('mtu', n_const.DEFAULT_NETWORK_MTU), 'status': network['status'], 'subnets': [subnet['id'] for subnet in network['subnets']]} res['shared'] = self._is_network_shared(context, network.rbac_entries) # Call auxiliary extend functions, if any if process_extensions: resource_extend.apply_funcs(net_def.COLLECTION_NAME, res, network) return db_utils.resource_fields(res, fields) def _is_network_shared(self, context, rbac_entries): # The shared attribute for a network now reflects if the network # is shared to the calling tenant via an RBAC entry. matches = ('*',) + ((context.tenant_id,) if context else ()) for entry in rbac_entries: if (entry.action == 'access_as_shared' and entry.target_tenant in matches): return True return False def _make_subnet_args(self, detail, subnet, subnetpool_id): args = {'project_id': detail.tenant_id, 'id': detail.subnet_id, 'name': subnet['name'], 'network_id': subnet['network_id'], 'ip_version': subnet['ip_version'], 'cidr': detail.subnet_cidr, 'subnetpool_id': subnetpool_id, 'enable_dhcp': subnet['enable_dhcp'], 'gateway_ip': detail.gateway_ip, 'description': subnet.get('description')} if subnet['ip_version'] == 6 and subnet['enable_dhcp']: if validators.is_attr_set(subnet['ipv6_ra_mode']): args['ipv6_ra_mode'] = subnet['ipv6_ra_mode'] if validators.is_attr_set(subnet['ipv6_address_mode']): args['ipv6_address_mode'] = subnet['ipv6_address_mode'] return args def _make_fixed_ip_dict(self, ips): # Excludes from dict all keys except subnet_id and ip_address return [{'subnet_id': ip["subnet_id"], 'ip_address': ip["ip_address"]} for ip in ips] neutron-12.1.1/neutron/db/ipam_pluggable_backend.py0000664000175000017500000006367013553660047022405 0ustar zuulzuul00000000000000# Copyright (c) 2015 Infoblox Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import netaddr from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib import exceptions as n_exc from oslo_db import exception as db_exc from oslo_log import log as logging from oslo_utils import excutils from sqlalchemy import and_ from neutron.common import ipv6_utils from neutron.db import api as db_api from neutron.db import ipam_backend_mixin from neutron.db import models_v2 from neutron.ipam import driver from neutron.ipam import exceptions as ipam_exc from neutron.objects import ports as port_obj from neutron.objects import subnet as obj_subnet LOG = logging.getLogger(__name__) class IpamPluggableBackend(ipam_backend_mixin.IpamBackendMixin): def _get_failed_ips(self, all_ips, success_ips): ips_list = (ip_dict['ip_address'] for ip_dict in success_ips) return (ip_dict['ip_address'] for ip_dict in all_ips if ip_dict['ip_address'] not in ips_list) def _safe_rollback(self, func, *args, **kwargs): """Calls rollback actions and catch all exceptions. All exceptions are catched and logged here to prevent rewriting original exception that triggered rollback action. """ try: func(*args, **kwargs) except Exception as e: LOG.warning("Revert failed with: %s", e) def _ipam_deallocate_ips(self, context, ipam_driver, port, ips, revert_on_fail=True): """Deallocate set of ips over IPAM. If any single ip deallocation fails, tries to allocate deallocated ip addresses with fixed ip request """ deallocated = [] try: for ip in ips: try: ipam_subnet = ipam_driver.get_subnet(ip['subnet_id']) ipam_subnet.deallocate(ip['ip_address']) deallocated.append(ip) except n_exc.SubnetNotFound: LOG.debug("Subnet was not found on ip deallocation: %s", ip) except Exception: with excutils.save_and_reraise_exception(): if not ipam_driver.needs_rollback(): return LOG.debug("An exception occurred during IP deallocation.") if revert_on_fail and deallocated: LOG.debug("Reverting deallocation") # In case of deadlock allocate fails with db error # and rewrites original exception preventing db_retry # wrappers from restarting entire api request. self._safe_rollback(self._ipam_allocate_ips, context, ipam_driver, port, deallocated, revert_on_fail=False) elif not revert_on_fail and ips: addresses = ', '.join(self._get_failed_ips(ips, deallocated)) LOG.error("IP deallocation failed on " "external system for %s", addresses) return deallocated def _ipam_allocate_ips(self, context, ipam_driver, port, ips, revert_on_fail=True): """Allocate set of ips over IPAM. If any single ip allocation fails, tries to deallocate all allocated ip addresses. """ allocated = [] # we need to start with entries that asked for a specific IP in case # those IPs happen to be next in the line for allocation for ones that # didn't ask for a specific IP ips.sort(key=lambda x: 'ip_address' not in x) try: for ip in ips: # By default IP info is dict, used to allocate single ip # from single subnet. # IP info can be list, used to allocate single ip from # multiple subnets ip_list = [ip] if isinstance(ip, dict) else ip subnets = [ip_dict['subnet_id'] for ip_dict in ip_list] try: factory = ipam_driver.get_address_request_factory() ip_request = factory.get_request(context, port, ip_list[0]) ipam_allocator = ipam_driver.get_allocator(subnets) ip_address, subnet_id = ipam_allocator.allocate(ip_request) except ipam_exc.IpAddressGenerationFailureAllSubnets: raise n_exc.IpAddressGenerationFailure( net_id=port['network_id']) allocated.append({'ip_address': ip_address, 'subnet_id': subnet_id}) except Exception: with excutils.save_and_reraise_exception(): if not ipam_driver.needs_rollback(): return LOG.debug("An exception occurred during IP allocation.") if revert_on_fail and allocated: LOG.debug("Reverting allocation") # In case of deadlock deallocation fails with db error # and rewrites original exception preventing db_retry # wrappers from restarting entire api request. self._safe_rollback(self._ipam_deallocate_ips, context, ipam_driver, port, allocated, revert_on_fail=False) elif not revert_on_fail and ips: addresses = ', '.join(self._get_failed_ips(ips, allocated)) LOG.error("IP allocation failed on " "external system for %s", addresses) return allocated def _ipam_update_allocation_pools(self, context, ipam_driver, subnet): factory = ipam_driver.get_subnet_request_factory() subnet_request = factory.get_request(context, subnet, None) ipam_driver.update_subnet(subnet_request) def delete_subnet(self, context, subnet_id): ipam_driver = driver.Pool.get_instance(None, context) ipam_driver.remove_subnet(subnet_id) def get_subnet(self, context, subnet_id): ipam_driver = driver.Pool.get_instance(None, context) return ipam_driver.get_subnet(subnet_id) def allocate_ips_for_port_and_store(self, context, port, port_id): # Make a copy of port dict to prevent changing # incoming dict by adding 'id' to it. # Deepcopy doesn't work correctly in this case, because copy of # ATTR_NOT_SPECIFIED object happens. Address of copied object doesn't # match original object, so 'is' check fails port_copy = {'port': port['port'].copy()} port_copy['port']['id'] = port_id network_id = port_copy['port']['network_id'] ips = [] try: ips = self._allocate_ips_for_port(context, port_copy) for ip in ips: ip_address = ip['ip_address'] subnet_id = ip['subnet_id'] IpamPluggableBackend._store_ip_allocation( context, ip_address, network_id, subnet_id, port_id) return ips except Exception: with excutils.save_and_reraise_exception(): if ips: ipam_driver = driver.Pool.get_instance(None, context) if not ipam_driver.needs_rollback(): return LOG.debug("An exception occurred during port creation. " "Reverting IP allocation") self._safe_rollback(self._ipam_deallocate_ips, context, ipam_driver, port_copy['port'], ips, revert_on_fail=False) def _allocate_ips_for_port(self, context, port): """Allocate IP addresses for the port. IPAM version. If port['fixed_ips'] is set to 'ATTR_NOT_SPECIFIED', allocate IP addresses for the port. If port['fixed_ips'] contains an IP address or a subnet_id then allocate an IP address accordingly. """ p = port['port'] fixed_configured = p['fixed_ips'] is not constants.ATTR_NOT_SPECIFIED subnets = self._ipam_get_subnets(context, network_id=p['network_id'], host=p.get(portbindings.HOST_ID), service_type=p.get('device_owner'), fixed_configured=fixed_configured) v4, v6_stateful, v6_stateless = self._classify_subnets( context, subnets) if fixed_configured: ips = self._test_fixed_ips_for_port(context, p["network_id"], p['fixed_ips'], p['device_owner'], subnets) else: ips = [] version_subnets = [v4, v6_stateful] for subnets in version_subnets: if subnets: ips.append([{'subnet_id': s['id']} for s in subnets]) ips.extend(self._get_auto_address_ips(v6_stateless, p)) ipam_driver = driver.Pool.get_instance(None, context) return self._ipam_allocate_ips(context, ipam_driver, p, ips) def _get_auto_address_ips(self, v6_stateless_subnets, port, exclude_subnet_ids=None): exclude_subnet_ids = exclude_subnet_ids or [] ips = [] is_router_port = ( port['device_owner'] in constants.ROUTER_INTERFACE_OWNERS_SNAT) if not is_router_port: for subnet in v6_stateless_subnets: if subnet['id'] not in exclude_subnet_ids: # IP addresses for IPv6 SLAAC and DHCPv6-stateless subnets # are implicitly included. ips.append({'subnet_id': subnet['id'], 'subnet_cidr': subnet['cidr'], 'eui64_address': True, 'mac': port['mac_address']}) return ips def _test_fixed_ips_for_port(self, context, network_id, fixed_ips, device_owner, subnets): """Test fixed IPs for port. Check that configured subnets are valid prior to allocating any IPs. Include the subnet_id in the result if only an IP address is configured. :raises: InvalidInput, IpAddressInUse, InvalidIpForNetwork, InvalidIpForSubnet """ fixed_ip_list = [] for fixed in fixed_ips: fixed['device_owner'] = device_owner subnet = self._get_subnet_for_fixed_ip(context, fixed, subnets) is_auto_addr_subnet = ipv6_utils.is_auto_address_subnet(subnet) if ('ip_address' in fixed and subnet['cidr'] != constants.PROVISIONAL_IPV6_PD_PREFIX): if (is_auto_addr_subnet and device_owner not in constants.ROUTER_INTERFACE_OWNERS): raise ipam_exc.AllocationOnAutoAddressSubnet( ip=fixed['ip_address'], subnet_id=subnet['id']) fixed_ip_list.append({'subnet_id': subnet['id'], 'ip_address': fixed['ip_address']}) else: # A scan for auto-address subnets on the network is done # separately so that all such subnets (not just those # listed explicitly here by subnet ID) are associated # with the port. if (device_owner in constants.ROUTER_INTERFACE_OWNERS_SNAT or not is_auto_addr_subnet): fixed_ip_list.append({'subnet_id': subnet['id']}) return fixed_ip_list def _update_ips_for_port(self, context, port, host, original_ips, new_ips, mac): """Add or remove IPs from the port. IPAM version""" added = [] removed = [] changes = self._get_changed_ips_for_port( context, original_ips, new_ips, port['device_owner']) try: subnets = self._ipam_get_subnets( context, network_id=port['network_id'], host=host, service_type=port.get('device_owner'), fixed_configured=True) except ipam_exc.DeferIpam: subnets = [] # Check if the IP's to add are OK to_add = self._test_fixed_ips_for_port( context, port['network_id'], changes.add, port['device_owner'], subnets) if port['device_owner'] not in constants.ROUTER_INTERFACE_OWNERS: to_add += self._update_ips_for_pd_subnet( context, subnets, changes.add, mac) ipam_driver = driver.Pool.get_instance(None, context) if changes.remove: removed = self._ipam_deallocate_ips(context, ipam_driver, port, changes.remove) v6_stateless = self._classify_subnets( context, subnets)[2] handled_subnet_ids = [ip['subnet_id'] for ip in to_add + changes.original + changes.remove] to_add.extend(self._get_auto_address_ips( v6_stateless, port, handled_subnet_ids)) if to_add: added = self._ipam_allocate_ips(context, ipam_driver, port, to_add) return self.Changes(add=added, original=changes.original, remove=removed) @db_api.context_manager.writer def save_allocation_pools(self, context, subnet, allocation_pools): for pool in allocation_pools: first_ip = str(netaddr.IPAddress(pool.first, pool.version)) last_ip = str(netaddr.IPAddress(pool.last, pool.version)) obj_subnet.IPAllocationPool( context, subnet_id=subnet['id'], start=first_ip, end=last_ip).create() def update_port_with_ips(self, context, host, db_port, new_port, new_mac): changes = self.Changes(add=[], original=[], remove=[]) auto_assign_subnets = [] if new_mac: original = self._make_port_dict(db_port, process_extensions=False) if original.get('mac_address') != new_mac: original_ips = original.get('fixed_ips', []) # NOTE(hjensas): Only set the default for 'fixed_ips' in # new_port if the original port or new_port actually have IPs. # Setting the default to [] breaks deferred IP allocation. # See Bug: https://bugs.launchpad.net/neutron/+bug/1811905 if original_ips or new_port.get('fixed_ips'): new_ips = new_port.setdefault('fixed_ips', original_ips) new_ips_subnets = [new_ip['subnet_id'] for new_ip in new_ips] for orig_ip in original_ips: if ipv6_utils.is_eui64_address(orig_ip.get('ip_address')): subnet_to_delete = {} subnet_to_delete['subnet_id'] = orig_ip['subnet_id'] subnet_to_delete['delete_subnet'] = True auto_assign_subnets.append(subnet_to_delete) try: i = new_ips_subnets.index(orig_ip['subnet_id']) new_ips[i] = subnet_to_delete except ValueError: new_ips.append(subnet_to_delete) if 'fixed_ips' in new_port: original = self._make_port_dict(db_port, process_extensions=False) changes = self._update_ips_for_port(context, db_port, host, original["fixed_ips"], new_port['fixed_ips'], new_mac) try: # Expire the fixed_ips of db_port in current transaction, because # it will be changed in the following operation and the latest # data is expected. context.session.expire(db_port, ['fixed_ips']) # Check if the IPs need to be updated network_id = db_port['network_id'] for ip in changes.remove: self._delete_ip_allocation(context, network_id, ip['subnet_id'], ip['ip_address']) for ip in changes.add: self._store_ip_allocation( context, ip['ip_address'], network_id, ip['subnet_id'], db_port.id) self._update_db_port(context, db_port, new_port, network_id, new_mac) if auto_assign_subnets: port_copy = copy.deepcopy(original) port_copy.update(new_port) port_copy['fixed_ips'] = auto_assign_subnets self.allocate_ips_for_port_and_store(context, {'port': port_copy}, port_copy['id']) getattr(db_port, 'fixed_ips') # refresh relationship before return except Exception: with excutils.save_and_reraise_exception(): if 'fixed_ips' in new_port: ipam_driver = driver.Pool.get_instance(None, context) if not ipam_driver.needs_rollback(): return LOG.debug("An exception occurred during port update.") if changes.add: LOG.debug("Reverting IP allocation.") self._safe_rollback(self._ipam_deallocate_ips, context, ipam_driver, db_port, changes.add, revert_on_fail=False) if changes.remove: LOG.debug("Reverting IP deallocation.") self._safe_rollback(self._ipam_allocate_ips, context, ipam_driver, db_port, changes.remove, revert_on_fail=False) return changes def delete_port(self, context, id): # Get fixed_ips list before port deletion port = self._get_port(context, id) ipam_driver = driver.Pool.get_instance(None, context) super(IpamPluggableBackend, self).delete_port(context, id) # Deallocating ips via IPAM after port is deleted locally. # So no need to do rollback actions on remote server # in case of fail to delete port locally self._ipam_deallocate_ips(context, ipam_driver, port, port['fixed_ips']) def update_db_subnet(self, context, id, s, old_pools): # 'allocation_pools' is removed from 's' in # _update_subnet_allocation_pools (ipam_backend_mixin), # so create unchanged copy for ipam driver subnet_copy = copy.deepcopy(s) subnet, changes = super(IpamPluggableBackend, self).update_db_subnet( context, id, s, old_pools) ipam_driver = driver.Pool.get_instance(None, context) # Set old allocation pools if no new pools are provided by user. # Passing old pools allows to call ipam driver on each subnet update # even if allocation pools are not changed. So custom ipam drivers # are able to track other fields changes on subnet update. if 'allocation_pools' not in subnet_copy: subnet_copy['allocation_pools'] = old_pools self._ipam_update_allocation_pools(context, ipam_driver, subnet_copy) return subnet, changes def add_auto_addrs_on_network_ports(self, context, subnet, ipam_subnet): """For an auto-address subnet, add addrs for ports on the net.""" # TODO(ataraday): switched for writer when flush_on_subtransaction # will be available for neutron with context.session.begin(subtransactions=True): network_id = subnet['network_id'] port_qry = context.session.query(models_v2.Port) ports = port_qry.filter( and_(models_v2.Port.network_id == network_id, ~models_v2.Port.device_owner.in_( constants.ROUTER_INTERFACE_OWNERS_SNAT))) updated_ports = [] ipam_driver = driver.Pool.get_instance(None, context) factory = ipam_driver.get_address_request_factory() for port in ports: ip = {'subnet_id': subnet['id'], 'subnet_cidr': subnet['cidr'], 'eui64_address': True, 'mac': port['mac_address']} ip_request = factory.get_request(context, port, ip) try: ip_address = ipam_subnet.allocate(ip_request) allocated = port_obj.IPAllocation( context, network_id=network_id, port_id=port['id'], ip_address=ip_address, subnet_id=subnet['id']) # Do the insertion of each IP allocation entry within # the context of a nested transaction, so that the entry # is rolled back independently of other entries whenever # the corresponding port has been deleted; since OVO # already opens a nested transaction, we don't need to do # it explicitly here. allocated.create() updated_ports.append(port['id']) except db_exc.DBReferenceError: LOG.debug("Port %s was deleted while updating it with an " "IPv6 auto-address. Ignoring.", port['id']) LOG.debug("Reverting IP allocation for %s", ip_address) # Do not fail if reverting allocation was unsuccessful try: ipam_subnet.deallocate(ip_address) except Exception: LOG.debug("Reverting IP allocation failed for %s", ip_address) except ipam_exc.IpAddressAlreadyAllocated: LOG.debug("Port %s got IPv6 auto-address in a concurrent " "create or update port request. Ignoring.", port['id']) return updated_ports def allocate_subnet(self, context, network, subnet, subnetpool_id): subnetpool = None if subnetpool_id and not subnetpool_id == constants.IPV6_PD_POOL_ID: subnetpool = self._get_subnetpool(context, id=subnetpool_id) self._validate_ip_version_with_subnetpool(subnet, subnetpool) # gateway_ip and allocation pools should be validated or generated # only for specific request if subnet['cidr'] is not constants.ATTR_NOT_SPECIFIED: subnet['gateway_ip'] = self._gateway_ip_str(subnet, subnet['cidr']) subnet['allocation_pools'] = self._prepare_allocation_pools( subnet['allocation_pools'], subnet['cidr'], subnet['gateway_ip']) ipam_driver = driver.Pool.get_instance(subnetpool, context) subnet_factory = ipam_driver.get_subnet_request_factory() subnet_request = subnet_factory.get_request(context, subnet, subnetpool) ipam_subnet = ipam_driver.allocate_subnet(subnet_request) # get updated details with actually allocated subnet subnet_request = ipam_subnet.get_details() try: subnet = self._save_subnet(context, network, self._make_subnet_args( subnet_request, subnet, subnetpool_id), subnet['dns_nameservers'], subnet['host_routes'], subnet_request) except Exception: # Note(pbondar): Third-party ipam servers can't rely # on transaction rollback, so explicit rollback call needed. # IPAM part rolled back in exception handling # and subnet part is rolled back by transaction rollback. with excutils.save_and_reraise_exception(): if not ipam_driver.needs_rollback(): return LOG.debug("An exception occurred during subnet creation. " "Reverting subnet allocation.") self._safe_rollback(self.delete_subnet, context, subnet_request.subnet_id) return subnet, ipam_subnet neutron-12.1.1/neutron/db/address_scope_db.py0000664000175000017500000001413513553660047021241 0ustar zuulzuul00000000000000# Copyright (c) 2015 Huawei Technologies Co.,LTD. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import address_scope as apidef from neutron_lib.api.definitions import network as net_def from neutron_lib import constants from neutron_lib.exceptions import address_scope as api_err from oslo_utils import uuidutils from neutron._i18n import _ from neutron.db import _resource_extend as resource_extend from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.extensions import address_scope as ext_address_scope from neutron.objects import address_scope as obj_addr_scope from neutron.objects import base as base_obj from neutron.objects import subnetpool as subnetpool_obj @resource_extend.has_resource_extenders class AddressScopeDbMixin(ext_address_scope.AddressScopePluginBase): """Mixin class to add address scope to db_base_plugin_v2.""" __native_bulk_support = True @staticmethod def _make_address_scope_dict(address_scope, fields=None): res = {'id': address_scope['id'], 'name': address_scope['name'], 'tenant_id': address_scope['tenant_id'], 'shared': address_scope['shared'], 'ip_version': address_scope['ip_version']} return db_utils.resource_fields(res, fields) def _get_address_scope(self, context, id): obj = obj_addr_scope.AddressScope.get_object(context, id=id) if obj is None: raise api_err.AddressScopeNotFound(address_scope_id=id) return obj def is_address_scope_owned_by_tenant(self, context, id): """Check if address scope id is owned by the tenant or not. AddressScopeNotFound is raised if the - address scope id doesn't exist or - if the (unshared) address scope id is not owned by this tenant. @return Returns true if the user is admin or tenant is owner Returns false if the address scope id is shared and not owned by the tenant. """ address_scope = self._get_address_scope(context, id) return context.is_admin or ( address_scope.tenant_id == context.tenant_id) def get_ip_version_for_address_scope(self, context, id): address_scope = self._get_address_scope(context, id) return address_scope.ip_version def create_address_scope(self, context, address_scope): """Create an address scope.""" a_s = address_scope['address_scope'] address_scope_id = a_s.get('id') or uuidutils.generate_uuid() pool_args = {'project_id': a_s['tenant_id'], 'id': address_scope_id, 'name': a_s['name'], 'shared': a_s['shared'], 'ip_version': a_s['ip_version']} address_scope = obj_addr_scope.AddressScope(context, **pool_args) address_scope.create() return self._make_address_scope_dict(address_scope) def update_address_scope(self, context, id, address_scope): a_s = address_scope['address_scope'] address_scope = self._get_address_scope(context, id) if address_scope.shared and not a_s.get('shared', True): reason = _("Shared address scope can't be unshared") raise api_err.AddressScopeUpdateError( address_scope_id=id, reason=reason) address_scope.update_fields(a_s) address_scope.update() return self._make_address_scope_dict(address_scope) def get_address_scope(self, context, id, fields=None): address_scope = self._get_address_scope(context, id) return self._make_address_scope_dict(address_scope, fields) def get_address_scopes(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pager = base_obj.Pager(sorts, limit, page_reverse, marker) address_scopes = obj_addr_scope.AddressScope.get_objects( context, _pager=pager, **filters) return [ self._make_address_scope_dict(addr_scope, fields) for addr_scope in address_scopes ] def get_address_scopes_count(self, context, filters=None): return obj_addr_scope.AddressScope.count(context, **filters) def delete_address_scope(self, context, id): with db_api.context_manager.writer.using(context): if subnetpool_obj.SubnetPool.get_objects(context, address_scope_id=id): raise api_err.AddressScopeInUse(address_scope_id=id) address_scope = self._get_address_scope(context, id) address_scope.delete() @staticmethod @resource_extend.extends([net_def.COLLECTION_NAME]) def _extend_network_dict_address_scope(network_res, network_db): network_res[apidef.IPV4_ADDRESS_SCOPE] = None network_res[apidef.IPV6_ADDRESS_SCOPE] = None subnetpools = {subnet.subnetpool for subnet in network_db.subnets if subnet.subnetpool} for subnetpool in subnetpools: # A network will be constrained to only one subnetpool per address # family. Retrieve the address scope of subnetpools as the address # scopes of network. as_id = subnetpool[apidef.ADDRESS_SCOPE_ID] if subnetpool['ip_version'] == constants.IP_VERSION_4: network_res[apidef.IPV4_ADDRESS_SCOPE] = as_id if subnetpool['ip_version'] == constants.IP_VERSION_6: network_res[apidef.IPV6_ADDRESS_SCOPE] = as_id return network_res neutron-12.1.1/neutron/db/securitygroups_rpc_base.py0000664000175000017500000004507213553660047022727 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as const from neutron_lib.utils import helpers from neutron._i18n import _ from neutron.db import api as db_api from neutron.db.models import allowed_address_pair as aap_models from neutron.db.models import securitygroup as sg_models from neutron.db import models_v2 from neutron.db import securitygroups_db as sg_db from neutron.extensions import securitygroup as ext_sg DIRECTION_IP_PREFIX = {'ingress': 'source_ip_prefix', 'egress': 'dest_ip_prefix'} DHCP_RULE_PORT = {4: (67, 68, const.IPv4), 6: (547, 546, const.IPv6)} @registry.has_registry_receivers class SecurityGroupServerNotifierRpcMixin(sg_db.SecurityGroupDbMixin): """Mixin class to add agent-based security group implementation.""" @registry.receives(resources.PORT, [events.AFTER_CREATE, events.AFTER_UPDATE, events.AFTER_DELETE]) def notify_sg_on_port_change(self, resource, event, trigger, context, port, *args, **kwargs): """Trigger notification to other SG members on port changes.""" self.notify_security_groups_member_updated(context, port) def create_security_group_rule(self, context, security_group_rule): rule = super(SecurityGroupServerNotifierRpcMixin, self).create_security_group_rule(context, security_group_rule) sgids = [rule['security_group_id']] self.notifier.security_groups_rule_updated(context, sgids) return rule def create_security_group_rule_bulk(self, context, security_group_rules): rules = super(SecurityGroupServerNotifierRpcMixin, self).create_security_group_rule_bulk_native( context, security_group_rules) sgids = set([r['security_group_id'] for r in rules]) self.notifier.security_groups_rule_updated(context, list(sgids)) return rules def delete_security_group_rule(self, context, sgrid): rule = self.get_security_group_rule(context, sgrid) super(SecurityGroupServerNotifierRpcMixin, self).delete_security_group_rule(context, sgrid) self.notifier.security_groups_rule_updated(context, [rule['security_group_id']]) def check_and_notify_security_group_member_changed( self, context, original_port, updated_port): sg_change = not helpers.compare_elements( original_port.get(ext_sg.SECURITYGROUPS), updated_port.get(ext_sg.SECURITYGROUPS)) if sg_change: self.notify_security_groups_member_updated_bulk( context, [original_port, updated_port]) elif original_port['fixed_ips'] != updated_port['fixed_ips']: self.notify_security_groups_member_updated(context, updated_port) def is_security_group_member_updated(self, context, original_port, updated_port): """Check security group member updated or not. This method returns a flag which indicates request notification is required and does not perform notification itself. It is because another changes for the port may require notification. """ need_notify = False if (original_port['fixed_ips'] != updated_port['fixed_ips'] or original_port['mac_address'] != updated_port['mac_address'] or not helpers.compare_elements( original_port.get(ext_sg.SECURITYGROUPS), updated_port.get(ext_sg.SECURITYGROUPS))): need_notify = True return need_notify def notify_security_groups_member_updated_bulk(self, context, ports): """Notify update event of security group members for ports. The agent setups the iptables rule to allow ingress packet from the dhcp server (as a part of provider rules), so we need to notify an update of dhcp server ip address to the plugin agent. """ sec_groups = set() for port in ports: # NOTE (Swami): ROUTER_INTERFACE_OWNERS check is required # since it includes the legacy router interface device owners # and DVR router interface device owners. if (port['device_owner'] not in [const.DEVICE_OWNER_DHCP, const.ROUTER_INTERFACE_OWNERS]): sec_groups |= set(port.get(ext_sg.SECURITYGROUPS)) if sec_groups: self.notifier.security_groups_member_updated( context, list(sec_groups)) def notify_security_groups_member_updated(self, context, port): self.notify_security_groups_member_updated_bulk(context, [port]) class SecurityGroupInfoAPIMixin(object): """API for retrieving security group info for SG agent code.""" def get_port_from_device(self, context, device): """Get port dict from device name on an agent. Subclass must provide this method or get_ports_from_devices. :param device: device name which identifies a port on the agent side. What is specified in "device" depends on a plugin agent implementation. For example, it is a port ID in OVS agent and netdev name in Linux Bridge agent. :return: port dict returned by DB plugin get_port(). In addition, it must contain the following fields in the port dict returned. - device - security_groups - security_group_rules, - security_group_source_groups - fixed_ips """ raise NotImplementedError(_("%s must implement get_port_from_device " "or get_ports_from_devices.") % self.__class__.__name__) def get_ports_from_devices(self, context, devices): """Bulk method of get_port_from_device. Subclasses may override this to provide better performance for DB queries, backend calls, etc. """ return [self.get_port_from_device(context, device) for device in devices] def security_group_info_for_ports(self, context, ports): sg_info = {'devices': ports, 'security_groups': {}, 'sg_member_ips': {}} rules_in_db = self._select_rules_for_ports(context, ports) remote_security_group_info = {} for (port_id, rule_in_db) in rules_in_db: remote_gid = rule_in_db.get('remote_group_id') security_group_id = rule_in_db.get('security_group_id') ethertype = rule_in_db['ethertype'] if ('security_group_source_groups' not in sg_info['devices'][port_id]): sg_info['devices'][port_id][ 'security_group_source_groups'] = [] if remote_gid: if (remote_gid not in sg_info['devices'][port_id][ 'security_group_source_groups']): sg_info['devices'][port_id][ 'security_group_source_groups'].append(remote_gid) if remote_gid not in remote_security_group_info: remote_security_group_info[remote_gid] = {} if ethertype not in remote_security_group_info[remote_gid]: # this set will be serialized into a list by rpc code remote_security_group_info[remote_gid][ethertype] = set() direction = rule_in_db['direction'] rule_dict = { 'direction': direction, 'ethertype': ethertype} for key in ('protocol', 'port_range_min', 'port_range_max', 'remote_ip_prefix', 'remote_group_id'): if rule_in_db.get(key) is not None: if key == 'remote_ip_prefix': direction_ip_prefix = DIRECTION_IP_PREFIX[direction] rule_dict[direction_ip_prefix] = rule_in_db[key] continue rule_dict[key] = rule_in_db[key] if security_group_id not in sg_info['security_groups']: sg_info['security_groups'][security_group_id] = [] if rule_dict not in sg_info['security_groups'][security_group_id]: sg_info['security_groups'][security_group_id].append( rule_dict) # Update the security groups info if they don't have any rules sg_ids = self._select_sg_ids_for_ports(context, ports) for (sg_id, ) in sg_ids: if sg_id not in sg_info['security_groups']: sg_info['security_groups'][sg_id] = [] sg_info['sg_member_ips'] = remote_security_group_info # the provider rules do not belong to any security group, so these # rules still reside in sg_info['devices'] [port_id] self._apply_provider_rule(context, sg_info['devices']) return self._get_security_group_member_ips(context, sg_info) def _get_security_group_member_ips(self, context, sg_info): ips = self._select_ips_for_remote_group( context, sg_info['sg_member_ips'].keys()) for sg_id, member_ips in ips.items(): for ip in member_ips: ethertype = 'IPv%d' % netaddr.IPNetwork(ip).version if ethertype in sg_info['sg_member_ips'][sg_id]: sg_info['sg_member_ips'][sg_id][ethertype].add(ip) return sg_info def _select_remote_group_ids(self, ports): remote_group_ids = [] for port in ports.values(): for rule in port.get('security_group_rules'): remote_group_id = rule.get('remote_group_id') if remote_group_id: remote_group_ids.append(remote_group_id) return remote_group_ids def _convert_remote_group_id_to_ip_prefix(self, context, ports): remote_group_ids = self._select_remote_group_ids(ports) ips = self._select_ips_for_remote_group(context, remote_group_ids) for port in ports.values(): updated_rule = [] for rule in port.get('security_group_rules'): remote_group_id = rule.get('remote_group_id') direction = rule.get('direction') direction_ip_prefix = DIRECTION_IP_PREFIX[direction] if not remote_group_id: updated_rule.append(rule) continue port['security_group_source_groups'].append(remote_group_id) base_rule = rule for ip in ips[remote_group_id]: if ip in port.get('fixed_ips', []): continue ip_rule = base_rule.copy() version = netaddr.IPNetwork(ip).version ethertype = 'IPv%s' % version if base_rule['ethertype'] != ethertype: continue ip_rule[direction_ip_prefix] = str( netaddr.IPNetwork(ip).cidr) updated_rule.append(ip_rule) port['security_group_rules'] = updated_rule return ports def _add_ingress_dhcp_rule(self, port): for ip_version in (4, 6): # only allow DHCP servers to talk to the appropriate IP address # to avoid getting leases that don't match the Neutron IPs prefix = '32' if ip_version == 4 else '128' dests = ['%s/%s' % (ip, prefix) for ip in port['fixed_ips'] if netaddr.IPNetwork(ip).version == ip_version] if ip_version == 4: # v4 dhcp servers can also talk to broadcast dests.append('255.255.255.255/32') elif ip_version == 6: # v6 dhcp responses can target link-local addresses dests.append('fe80::/64') source_port, dest_port, ethertype = DHCP_RULE_PORT[ip_version] for dest in dests: dhcp_rule = {'direction': 'ingress', 'ethertype': ethertype, 'protocol': 'udp', 'port_range_min': dest_port, 'port_range_max': dest_port, 'source_port_range_min': source_port, 'source_port_range_max': source_port, 'dest_ip_prefix': dest} port['security_group_rules'].append(dhcp_rule) def _add_ingress_ra_rule(self, port): has_v6 = [ip for ip in port['fixed_ips'] if netaddr.IPNetwork(ip).version == 6] if not has_v6: return ra_rule = {'direction': 'ingress', 'ethertype': const.IPv6, 'protocol': const.PROTO_NAME_IPV6_ICMP, 'source_port_range_min': const.ICMPV6_TYPE_RA} port['security_group_rules'].append(ra_rule) def _apply_provider_rule(self, context, ports): for port in ports.values(): self._add_ingress_ra_rule(port) self._add_ingress_dhcp_rule(port) def security_group_rules_for_ports(self, context, ports): rules_in_db = self._select_rules_for_ports(context, ports) for (port_id, rule_in_db) in rules_in_db: port = ports[port_id] direction = rule_in_db['direction'] rule_dict = { 'security_group_id': rule_in_db['security_group_id'], 'direction': direction, 'ethertype': rule_in_db['ethertype'], } for key in ('protocol', 'port_range_min', 'port_range_max', 'remote_ip_prefix', 'remote_group_id'): if rule_in_db.get(key) is not None: if key == 'remote_ip_prefix': direction_ip_prefix = DIRECTION_IP_PREFIX[direction] rule_dict[direction_ip_prefix] = rule_in_db[key] continue rule_dict[key] = rule_in_db[key] port['security_group_rules'].append(rule_dict) self._apply_provider_rule(context, ports) return self._convert_remote_group_id_to_ip_prefix(context, ports) def _select_ips_for_remote_group(self, context, remote_group_ids): """Get all IP addresses (including allowed addr pairs) for each sg. Return dict of lists of IPs keyed by group_id. """ raise NotImplementedError() def _select_rules_for_ports(self, context, ports): """Get all security group rules associated with a list of ports. Return list of tuples of (port_id, sg_rule) """ raise NotImplementedError() def _select_sg_ids_for_ports(self, context, ports): """Return security group IDs for a list of ports. Return list of tuples with a single element of sg_id. """ raise NotImplementedError() class SecurityGroupServerRpcMixin(SecurityGroupInfoAPIMixin, SecurityGroupServerNotifierRpcMixin): """Server-side RPC mixin using DB for SG notifications and responses.""" @db_api.retry_if_session_inactive() def _select_sg_ids_for_ports(self, context, ports): if not ports: return [] sg_binding_port = sg_models.SecurityGroupPortBinding.port_id sg_binding_sgid = sg_models.SecurityGroupPortBinding.security_group_id query = context.session.query(sg_binding_sgid) query = query.filter(sg_binding_port.in_(ports.keys())) return query.all() @db_api.retry_if_session_inactive() def _select_rules_for_ports(self, context, ports): if not ports: return [] sg_binding_port = sg_models.SecurityGroupPortBinding.port_id sg_binding_sgid = sg_models.SecurityGroupPortBinding.security_group_id sgr_sgid = sg_models.SecurityGroupRule.security_group_id query = context.session.query(sg_binding_port, sg_models.SecurityGroupRule) query = query.join(sg_models.SecurityGroupRule, sgr_sgid == sg_binding_sgid) query = query.filter(sg_binding_port.in_(ports.keys())) return query.all() @db_api.retry_if_session_inactive() def _select_ips_for_remote_group(self, context, remote_group_ids): ips_by_group = {} if not remote_group_ids: return ips_by_group for remote_group_id in remote_group_ids: ips_by_group[remote_group_id] = set() ip_port = models_v2.IPAllocation.port_id sg_binding_port = sg_models.SecurityGroupPortBinding.port_id sg_binding_sgid = sg_models.SecurityGroupPortBinding.security_group_id # Join the security group binding table directly to the IP allocation # table instead of via the Port table skip an unnecessary intermediary query = context.session.query(sg_binding_sgid, models_v2.IPAllocation.ip_address, aap_models.AllowedAddressPair.ip_address) query = query.join(models_v2.IPAllocation, ip_port == sg_binding_port) # Outerjoin because address pairs may be null and we still want the # IP for the port. query = query.outerjoin( aap_models.AllowedAddressPair, sg_binding_port == aap_models.AllowedAddressPair.port_id) query = query.filter(sg_binding_sgid.in_(remote_group_ids)) # Each allowed address pair IP record for a port beyond the 1st # will have a duplicate regular IP in the query response since # the relationship is 1-to-many. Dedup with a set for security_group_id, ip_address, allowed_addr_ip in query: ips_by_group[security_group_id].add(ip_address) if allowed_addr_ip: ips_by_group[security_group_id].add(allowed_addr_ip) return ips_by_group neutron-12.1.1/neutron/db/rbac_db_mixin.py0000664000175000017500000001476413553660047020546 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events from neutron_lib.callbacks import exceptions as c_exc from neutron_lib.callbacks import registry from neutron_lib import exceptions as n_exc from oslo_db import exception as db_exc from sqlalchemy.orm import exc from neutron.db import _model_query as model_query from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.db import common_db_mixin from neutron.db import rbac_db_models as models from neutron.extensions import rbac as ext_rbac # resource name using in callbacks RBAC_POLICY = 'rbac-policy' class RbacPluginMixin(common_db_mixin.CommonDbMixin): """Plugin mixin that implements the RBAC DB operations.""" object_type_cache = {} supported_extension_aliases = ['rbac-policies'] @db_api.retry_if_session_inactive() def create_rbac_policy(self, context, rbac_policy): e = rbac_policy['rbac_policy'] try: registry.notify(RBAC_POLICY, events.BEFORE_CREATE, self, context=context, object_type=e['object_type'], policy=e) except c_exc.CallbackFailure as e: raise n_exc.InvalidInput(error_message=e) dbmodel = models.get_type_model_map()[e['object_type']] try: with context.session.begin(subtransactions=True): db_entry = dbmodel(object_id=e['object_id'], target_tenant=e['target_tenant'], action=e['action'], tenant_id=e['tenant_id']) context.session.add(db_entry) except db_exc.DBDuplicateEntry: raise ext_rbac.DuplicateRbacPolicy() return self._make_rbac_policy_dict(db_entry) @staticmethod def _make_rbac_policy_dict(db_entry, fields=None): res = {f: db_entry[f] for f in ('id', 'tenant_id', 'target_tenant', 'action', 'object_id')} res['object_type'] = db_entry.object_type return db_utils.resource_fields(res, fields) @db_api.retry_if_session_inactive() def update_rbac_policy(self, context, id, rbac_policy): pol = rbac_policy['rbac_policy'] entry = self._get_rbac_policy(context, id) object_type = entry['object_type'] try: registry.notify(RBAC_POLICY, events.BEFORE_UPDATE, self, context=context, policy=entry, object_type=object_type, policy_update=pol) except c_exc.CallbackFailure as ex: raise ext_rbac.RbacPolicyInUse(object_id=entry['object_id'], details=ex) with context.session.begin(subtransactions=True): entry.update(pol) return self._make_rbac_policy_dict(entry) @db_api.retry_if_session_inactive() def delete_rbac_policy(self, context, id): entry = self._get_rbac_policy(context, id) object_type = entry['object_type'] try: registry.notify(RBAC_POLICY, events.BEFORE_DELETE, self, context=context, object_type=object_type, policy=entry) except c_exc.CallbackFailure as ex: raise ext_rbac.RbacPolicyInUse(object_id=entry['object_id'], details=ex) with context.session.begin(subtransactions=True): context.session.delete(entry) registry.notify(RBAC_POLICY, events.AFTER_DELETE, self, context=context, object_type=object_type, policy=entry) self.object_type_cache.pop(id, None) def _get_rbac_policy(self, context, id): object_type = self._get_object_type(context, id) dbmodel = models.get_type_model_map()[object_type] try: return model_query.query_with_hooks( context, dbmodel).filter(dbmodel.id == id).one() except exc.NoResultFound: raise ext_rbac.RbacPolicyNotFound(id=id, object_type=object_type) @db_api.retry_if_session_inactive() def get_rbac_policy(self, context, id, fields=None): return self._make_rbac_policy_dict( self._get_rbac_policy(context, id), fields=fields) @db_api.retry_if_session_inactive() def get_rbac_policies(self, context, filters=None, fields=None, sorts=None, limit=None, page_reverse=False): filters = filters or {} object_type_filters = filters.pop('object_type', None) models_to_query = [ m for t, m in models.get_type_model_map().items() if object_type_filters is None or t in object_type_filters ] collections = [model_query.get_collection( context, model, self._make_rbac_policy_dict, filters=filters, fields=fields, sorts=sorts, limit=limit, page_reverse=page_reverse) for model in models_to_query] # NOTE(kevinbenton): we don't have to worry about pagination, # limits, or page_reverse currently because allow_pagination is # set to False in 'neutron.extensions.rbac' return [item for c in collections for item in c] def _get_object_type(self, context, entry_id): """Scans all RBAC tables for an ID to figure out the type. This will be an expensive operation as the number of RBAC tables grows. The result is cached since object types cannot be updated for a policy. """ if entry_id in self.object_type_cache: return self.object_type_cache[entry_id] for otype, model in models.get_type_model_map().items(): if (context.session.query(model.id). filter(model.id == entry_id).first()): self.object_type_cache[entry_id] = otype return otype raise ext_rbac.RbacPolicyNotFound(id=entry_id, object_type='unknown') neutron-12.1.1/neutron/db/portsecurity_db_common.py0000664000175000017500000001000013553660047022532 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import port_security as psec from neutron.db import _utils as db_utils from neutron.objects import network from neutron.objects.port.extensions import port_security as p_ps class PortSecurityDbCommon(object): """Mixin class to add port security.""" @staticmethod def _extend_port_security_dict(response_data, db_data): if db_data.get('port_security') is None: response_data[psec.PORTSECURITY] = psec.DEFAULT_PORT_SECURITY else: response_data[psec.PORTSECURITY] = ( db_data['port_security'][psec.PORTSECURITY]) def _process_port_security_create( self, context, obj_cls, res_name, req, res): obj = obj_cls( context, id=res['id'], port_security_enabled=req[psec.PORTSECURITY] ) obj.create() res[psec.PORTSECURITY] = req[psec.PORTSECURITY] return self._make_port_security_dict(obj, res_name) def _process_port_port_security_create( self, context, port_req, port_res): self._process_port_security_create( context, p_ps.PortSecurity, 'port', port_req, port_res) def _process_network_port_security_create( self, context, network_req, network_res): self._process_port_security_create( context, network.NetworkPortSecurity, 'network', network_req, network_res) def _get_security_binding(self, context, obj_cls, res_id): obj = obj_cls.get_object(context, id=res_id) # NOTE(ihrachys) the resource may have been created before port # security extension was enabled; return default value return obj.port_security_enabled if obj else psec.DEFAULT_PORT_SECURITY def _get_network_security_binding(self, context, network_id): return self._get_security_binding( context, network.NetworkPortSecurity, network_id) def _get_port_security_binding(self, context, port_id): return self._get_security_binding(context, p_ps.PortSecurity, port_id) def _process_port_port_security_update( self, context, port_req, port_res): self._process_port_security_update( context, p_ps.PortSecurity, 'port', port_req, port_res) def _process_network_port_security_update( self, context, network_req, network_res): self._process_port_security_update( context, network.NetworkPortSecurity, 'network', network_req, network_res) def _process_port_security_update( self, context, obj_cls, res_name, req, res): if psec.PORTSECURITY not in req: return port_security_enabled = req[psec.PORTSECURITY] obj = obj_cls.get_object(context, id=res['id']) if obj: obj.port_security_enabled = port_security_enabled obj.update() res[psec.PORTSECURITY] = port_security_enabled else: # NOTE(ihrachys) the resource may have been created before port # security extension was enabled; create the binding model self._process_port_security_create( context, obj_cls, res_name, req, res) @staticmethod def _make_port_security_dict(res, res_name, fields=None): res_ = {'%s_id' % res_name: res.id, psec.PORTSECURITY: res.port_security_enabled} return db_utils.resource_fields(res_, fields) neutron-12.1.1/neutron/db/network_dhcp_agent_binding/0000775000175000017500000000000013553660156022740 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/network_dhcp_agent_binding/models.py0000664000175000017500000000240313553660046024572 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db.models import agent as agent_model class NetworkDhcpAgentBinding(model_base.BASEV2): """Represents binding between neutron networks and DHCP agents.""" network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id", ondelete='CASCADE'), primary_key=True) dhcp_agent = orm.relation(agent_model.Agent, lazy='subquery') dhcp_agent_id = sa.Column(sa.String(36), sa.ForeignKey("agents.id", ondelete='CASCADE'), primary_key=True) neutron-12.1.1/neutron/db/network_dhcp_agent_binding/__init__.py0000664000175000017500000000000013553660046025035 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/standard_attr.py0000664000175000017500000002146413553660047020613 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.db import constants as db_const from neutron_lib.db import model_base from oslo_utils import timeutils import sqlalchemy as sa from sqlalchemy import event # noqa from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext import declarative from sqlalchemy.orm import session as se from neutron._i18n import _ from neutron.db import sqlalchemytypes class StandardAttribute(model_base.BASEV2): """Common table to associate all Neutron API resources. By having Neutron objects related to this table, we can associate new tables that apply to many Neutron objects (e.g. timestamps, rbac entries) to this table to avoid schema duplication while maintaining referential integrity. NOTE(kevinbenton): This table should not have more columns added to it unless we are absolutely certain the new column will have a value for every single type of Neutron resource. Otherwise this table will be filled with NULL entries for combinations that don't make sense. Additionally, by keeping this table small we can ensure that performance isn't adversely impacted for queries on objects. """ # sqlite doesn't support auto increment on big integers so we use big int # for everything but sqlite id = sa.Column(sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), primary_key=True, autoincrement=True) # NOTE(kevinbenton): this column is redundant information, but it allows # operators/devs to look at the contents of this table and know which table # the corresponding object is in. # 255 was selected as a max just because it's the varchar ceiling in mysql # before a 2-byte prefix is required. We shouldn't get anywhere near this # limit with our table names... resource_type = sa.Column(sa.String(255), nullable=False) description = sa.Column(sa.String(db_const.DESCRIPTION_FIELD_SIZE)) revision_number = sa.Column( sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), server_default='0', nullable=False) created_at = sa.Column(sqlalchemytypes.TruncatedDateTime, default=timeutils.utcnow) updated_at = sa.Column(sqlalchemytypes.TruncatedDateTime, onupdate=timeutils.utcnow) __mapper_args__ = { # see http://docs.sqlalchemy.org/en/latest/orm/versioning.html for # details about how this works "version_id_col": revision_number, "version_id_generator": False # revision plugin increments manually } def bump_revision(self): if self.revision_number is None: # this is a brand new object uncommitted so we don't bump now return self.revision_number += 1 class HasStandardAttributes(object): @classmethod def get_api_collections(cls): """Define the API collection this object will appear under. This should return a list of API collections that the object will be exposed under. Most should be exposed in just one collection (e.g. the network model is just exposed under 'networks'). This is used by the standard attr extensions to discover which resources need to be extended with the standard attr fields (e.g. created_at/updated_at/etc). """ # NOTE(kevinbenton): can't use abc because the metaclass conflicts # with the declarative base others inherit from. if hasattr(cls, 'api_collections'): return cls.api_collections raise NotImplementedError("%s must define api_collections" % cls) @classmethod def get_collection_resource_map(cls): try: return cls.collection_resource_map except AttributeError: raise NotImplementedError("%s must define " "collection_resource_map" % cls) @classmethod def validate_tag_support(cls): return getattr(cls, 'tag_support', False) @declarative.declared_attr def standard_attr_id(cls): return sa.Column( sa.BigInteger().with_variant(sa.Integer(), 'sqlite'), sa.ForeignKey(StandardAttribute.id, ondelete="CASCADE"), unique=True, nullable=False ) # NOTE(kevinbenton): we have to disable the following pylint check because # it thinks we are overriding this method in the __init__ method. #pylint: disable=method-hidden @declarative.declared_attr def standard_attr(cls): return sa.orm.relationship(StandardAttribute, lazy='joined', cascade='all, delete-orphan', single_parent=True, uselist=False) def __init__(self, *args, **kwargs): standard_attr_keys = ['description', 'created_at', 'updated_at', 'revision_number'] standard_attr_kwargs = {} for key in standard_attr_keys: if key in kwargs: standard_attr_kwargs[key] = kwargs.pop(key) super(HasStandardAttributes, self).__init__(*args, **kwargs) # here we automatically create the related standard attribute object self.standard_attr = StandardAttribute( resource_type=self.__tablename__, **standard_attr_kwargs) @declarative.declared_attr def description(cls): return association_proxy('standard_attr', 'description') @declarative.declared_attr def created_at(cls): return association_proxy('standard_attr', 'created_at') @declarative.declared_attr def updated_at(cls): return association_proxy('standard_attr', 'updated_at') def update(self, new_dict): # ignore the timestamps if they were passed in. For example, this # happens if code calls update_port with modified results of get_port new_dict.pop('created_at', None) new_dict.pop('updated_at', None) super(HasStandardAttributes, self).update(new_dict) @declarative.declared_attr def revision_number(cls): return association_proxy('standard_attr', 'revision_number') def bump_revision(self): # SQLAlchemy will bump the version for us automatically if the # standard attr record is being modified, but we must call this # for all other modifications or when relevant children are being # modified (e.g. fixed_ips change should bump port revision) self.standard_attr.bump_revision() def get_standard_attr_resource_model_map(): rs_map = {} for subclass in HasStandardAttributes.__subclasses__(): for resource in subclass.get_api_collections(): if resource in rs_map: raise RuntimeError("Model %(sub)s tried to register for " "API resource %(res)s which conflicts " "with model %(other)s." % dict(sub=subclass, other=rs_map[resource], res=resource)) rs_map[resource] = subclass return rs_map def get_tag_resource_parent_map(): parent_map = {} for subclass in HasStandardAttributes.__subclasses__(): if subclass.validate_tag_support(): for collection, resource in (subclass.get_collection_resource_map() .items()): if collection in parent_map: msg = (_("API parent %(collection)s/%(resource)s for " "model %(subclass)s is already registered.") % dict(collection=collection, resource=resource, subclass=subclass)) raise RuntimeError(msg) parent_map[collection] = resource return parent_map @event.listens_for(se.Session, 'after_bulk_delete') def throw_exception_on_bulk_delete_of_listened_for_objects(delete_context): if hasattr(delete_context.mapper.class_, 'revises_on_change'): raise RuntimeError("%s may not be deleted in bulk because it " "bumps the revision of other resources via " "SQLAlchemy event handlers, which are not " "compatible with bulk deletes." % delete_context.mapper.class_) neutron-12.1.1/neutron/db/portbindings_base.py0000664000175000017500000000306713553660047021454 0ustar zuulzuul00000000000000# Copyright 2013 UnitedStack Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import port as port_def from neutron_lib.plugins import directory from neutron.db import _resource_extend as resource_extend @resource_extend.has_resource_extenders class PortBindingBaseMixin(object): # Initialized by core plugin or ml2 mechanism driver(s) base_binding_dict = None def _process_portbindings_create_and_update(self, context, port_data, port): self.extend_port_dict_binding(port, None) def extend_port_dict_binding(self, port_res, port_db): if self.base_binding_dict: port_res.update(self.base_binding_dict) @staticmethod @resource_extend.extends([port_def.COLLECTION_NAME]) def _extend_port_dict_binding(port_res, port_db): plugin = directory.get_plugin() if not isinstance(plugin, PortBindingBaseMixin): return plugin.extend_port_dict_binding(port_res, port_db) neutron-12.1.1/neutron/db/__init__.py0000664000175000017500000000000013553660046017476 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/l3_agentschedulers_db.py0000664000175000017500000005452113553660047022204 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants from neutron_lib.exceptions import agent as agent_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import log as logging import oslo_messaging from neutron.agent.common import utils as agent_utils from neutron.common import constants as l_consts from neutron.common import utils as n_utils from neutron.conf.db import l3_agentschedulers_db from neutron.db import agentschedulers_db from neutron.db.models import l3agent as rb_model from neutron.extensions import l3agentscheduler from neutron.extensions import router_availability_zone as router_az from neutron.objects import agent as ag_obj from neutron.objects import base as base_obj from neutron.objects import l3agent as rb_obj from neutron.objects import router as l3_objs LOG = logging.getLogger(__name__) l3_agentschedulers_db.register_db_l3agentschedulers_opts() class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase, agentschedulers_db.AgentSchedulerDbMixin): """Mixin class to add l3 agent scheduler extension to plugins using the l3 agent for routing. """ router_scheduler = None def add_periodic_l3_agent_status_check(self): if not cfg.CONF.allow_automatic_l3agent_failover: LOG.info("Skipping period L3 agent status check because " "automatic router rescheduling is disabled.") return self.add_agent_status_check_worker( self.reschedule_routers_from_down_agents) def reschedule_routers_from_down_agents(self): """Reschedule routers from down l3 agents if admin state is up.""" self.reschedule_resources_from_down_agents( agent_type='L3', get_down_bindings=self.get_down_router_bindings, agent_id_attr='l3_agent_id', resource_id_attr='router_id', resource_name='router', reschedule_resource=self.reschedule_router, rescheduling_failed=l3agentscheduler.RouterReschedulingFailed) def get_down_router_bindings(self, context, agent_dead_limit): cutoff = self.get_cutoff_time(agent_dead_limit) return rb_obj.RouterL3AgentBinding.get_down_router_bindings( context, cutoff) def _get_agent_mode(self, agent_db): agent_conf = self.get_configuration_dict(agent_db) return agent_conf.get(constants.L3_AGENT_MODE, constants.L3_AGENT_MODE_LEGACY) def validate_agent_router_combination(self, context, agent, router): """Validate if the router can be correctly assigned to the agent. :raises: RouterL3AgentMismatch if attempting to assign DVR router to legacy agent. :raises: InvalidL3Agent if attempting to assign router to an unsuitable agent (disabled, type != L3, incompatible configuration) :raises: DVRL3CannotAssignToDvrAgent if attempting to assign a router to an agent in 'dvr' mode. """ if agent['agent_type'] != constants.AGENT_TYPE_L3: raise l3agentscheduler.InvalidL3Agent(id=agent['id']) agent_mode = self._get_agent_mode(agent) if agent_mode in [constants.L3_AGENT_MODE_DVR, l_consts.L3_AGENT_MODE_DVR_NO_EXTERNAL]: raise l3agentscheduler.DVRL3CannotAssignToDvrAgent() if (agent_mode == constants.L3_AGENT_MODE_LEGACY and router.get('distributed')): raise l3agentscheduler.RouterL3AgentMismatch( router_id=router['id'], agent_id=agent['id']) is_suitable_agent = ( agentschedulers_db.services_available(agent['admin_state_up']) and self.get_l3_agent_candidates(context, router, [agent], ignore_admin_state=True)) if not is_suitable_agent: raise l3agentscheduler.InvalidL3Agent(id=agent['id']) def check_agent_router_scheduling_needed(self, context, agent, router): """Check if the router scheduling is needed. :raises: RouterHostedByL3Agent if router is already assigned to a different agent. :returns: True if scheduling is needed, otherwise False """ router_id = router['id'] agent_id = agent['id'] bindings = rb_obj.RouterL3AgentBinding.get_objects(context, router_id=router_id) if not bindings: return True for binding in bindings: if binding.l3_agent_id == agent_id: # router already bound to the agent we need return False if router.get('ha'): return True # legacy router case: router is already bound to some agent raise l3agentscheduler.RouterHostedByL3Agent( router_id=router_id, agent_id=bindings[0].l3_agent_id) def create_router_to_agent_binding(self, context, agent, router): """Create router to agent binding.""" router_id = router['id'] agent_id = agent['id'] if self.router_scheduler: plugin = directory.get_plugin(plugin_constants.L3) try: if router.get('ha'): self.router_scheduler.create_ha_port_and_bind( plugin, context, router['id'], router['tenant_id'], agent, is_manual_scheduling=True) else: self.router_scheduler.bind_router( plugin, context, router_id, agent.id) except db_exc.DBError: raise l3agentscheduler.RouterSchedulingFailed( router_id=router_id, agent_id=agent_id) def add_router_to_l3_agent(self, context, agent_id, router_id): """Add a l3 agent to host a router.""" if not self.router_supports_scheduling(context, router_id): raise l3agentscheduler.RouterDoesntSupportScheduling( router_id=router_id) with context.session.begin(subtransactions=True): router = self.get_router(context, router_id) agent = self._get_agent(context, agent_id) self.validate_agent_router_combination(context, agent, router) if not self.check_agent_router_scheduling_needed( context, agent, router): return self.create_router_to_agent_binding(context, agent, router) l3_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_L3) if l3_notifier: l3_notifier.router_added_to_agent( context, [router_id], agent.host) def remove_router_from_l3_agent(self, context, agent_id, router_id): """Remove the router from l3 agent. After removal, the router will be non-hosted until there is update which leads to re-schedule or be added to another agent manually. """ agent = self._get_agent(context, agent_id) agent_mode = self._get_agent_mode(agent) if agent_mode in [constants.L3_AGENT_MODE_DVR, l_consts.L3_AGENT_MODE_DVR_NO_EXTERNAL]: raise l3agentscheduler.DVRL3CannotRemoveFromDvrAgent() self._unbind_router(context, router_id, agent_id) router = self.get_router(context, router_id) plugin = directory.get_plugin(plugin_constants.L3) if router.get('ha'): plugin.delete_ha_interfaces_on_host(context, router_id, agent.host) # NOTE(Swami): Need to verify if there are DVR serviceable # ports owned by this agent. If owned by this agent, then # the routers should be retained. This flag will be used # to check if there are valid routers in this agent. retain_router = False if router.get('distributed'): subnet_ids = plugin.get_subnet_ids_on_router(context, router_id) if subnet_ids and agent.host: retain_router = plugin._check_dvr_serviceable_ports_on_host( context, agent.host, subnet_ids) l3_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_L3) if retain_router and l3_notifier: l3_notifier.routers_updated_on_host( context, [router_id], agent.host) elif l3_notifier: l3_notifier.router_removed_from_agent( context, router_id, agent.host) def _unbind_router(self, context, router_id, agent_id): rb_obj.RouterL3AgentBinding.delete_objects( context, router_id=router_id, l3_agent_id=agent_id) def _unschedule_router(self, context, router_id, agents_ids): with context.session.begin(subtransactions=True): for agent_id in agents_ids: self._unbind_router(context, router_id, agent_id) def reschedule_router(self, context, router_id, candidates=None): """Reschedule router to (a) new l3 agent(s) Remove the router from the agent(s) currently hosting it and schedule it again """ cur_agents = self.list_l3_agents_hosting_router( context, router_id)['agents'] with context.session.begin(subtransactions=True): cur_agents_ids = [agent['id'] for agent in cur_agents] self._unschedule_router(context, router_id, cur_agents_ids) self.schedule_router(context, router_id, candidates=candidates) new_agents = self.list_l3_agents_hosting_router( context, router_id)['agents'] if not new_agents: raise l3agentscheduler.RouterReschedulingFailed( router_id=router_id) self._notify_agents_router_rescheduled(context, router_id, cur_agents, new_agents) def _notify_agents_router_rescheduled(self, context, router_id, old_agents, new_agents): l3_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_L3) if not l3_notifier: return old_hosts = [agent['host'] for agent in old_agents] new_hosts = [agent['host'] for agent in new_agents] for host in set(old_hosts) - set(new_hosts): l3_notifier.router_removed_from_agent( context, router_id, host) for agent in new_agents: try: l3_notifier.router_added_to_agent( context, [router_id], agent['host']) except oslo_messaging.MessagingException: self._unbind_router(context, router_id, agent['id']) raise l3agentscheduler.RouterReschedulingFailed( router_id=router_id) def list_routers_on_l3_agent(self, context, agent_id): binding_objs = rb_obj.RouterL3AgentBinding.get_objects( context, l3_agent_id=agent_id) router_ids = [item.router_id for item in binding_objs] if router_ids: return {'routers': self.get_routers(context, filters={'id': router_ids})} else: # Exception will be thrown if the requested agent does not exist. self._get_agent(context, agent_id) return {'routers': []} def _get_active_l3_agent_routers_sync_data(self, context, host, agent, router_ids): if n_utils.is_extension_supported(self, constants.L3_HA_MODE_EXT_ALIAS): return self.get_ha_sync_data_for_host(context, host, agent, router_ids=router_ids, active=True) return self.get_sync_data(context, router_ids=router_ids, active=True) def list_router_ids_on_host(self, context, host, router_ids=None): try: agent = self._get_agent_by_type_and_host( context, constants.AGENT_TYPE_L3, host) except agent_exc.AgentNotFoundByTypeHost: return [] if not agentschedulers_db.services_available(agent.admin_state_up): return [] return self._get_router_ids_for_agent(context, agent, router_ids) def _get_router_ids_for_agent(self, context, agent, router_ids): """Get IDs of routers that the agent should host Overridden for DVR to handle agents in 'dvr' mode which have no explicit bindings with routers """ filters = {'l3_agent_id': agent.id} if router_ids: filters['router_id'] = router_ids bindings = rb_obj.RouterL3AgentBinding.get_objects(context, **filters) return [item.router_id for item in bindings] def list_active_sync_routers_on_active_l3_agent( self, context, host, router_ids): agent = self._get_agent_by_type_and_host( context, constants.AGENT_TYPE_L3, host) if not agentschedulers_db.services_available(agent.admin_state_up): LOG.info("Agent has its services disabled. Returning " "no active routers. Agent: %s", agent) return [] scheduled_router_ids = self._get_router_ids_for_agent( context, agent, router_ids) diff = set(router_ids or []) - set(scheduled_router_ids or []) if diff: LOG.debug("Agent requested router IDs not scheduled to it. " "Scheduled: %(sched)s. Unscheduled: %(diff)s. " "Agent: %(agent)s.", {'sched': scheduled_router_ids, 'diff': diff, 'agent': agent}) if scheduled_router_ids: return self._get_active_l3_agent_routers_sync_data( context, host, agent, scheduled_router_ids) return [] def get_l3_agents_hosting_routers(self, context, router_ids, admin_state_up=None, active=None): if not router_ids: return [] record_objs = rb_obj.RouterL3AgentBinding.get_objects( context, router_id=router_ids) if admin_state_up is not None: l3_agents = ag_obj.Agent.get_objects(context, id=[obj.l3_agent_id for obj in record_objs], admin_state_up=admin_state_up) else: l3_agents = [ ag_obj.Agent.get_object(context, id=obj.l3_agent_id) for obj in record_objs ] if active is not None: l3_agents = [l3_agent for l3_agent in l3_agents if not agent_utils.is_agent_down( l3_agent['heartbeat_timestamp'])] return l3_agents def _get_l3_agents_hosting_routers(self, context, router_ids): if not router_ids: return [] return ( rb_obj.RouterL3AgentBinding.get_l3_agents_by_router_ids( context, router_ids)) def list_l3_agents_hosting_router(self, context, router_id): with context.session.begin(subtransactions=True): agents = self._get_l3_agents_hosting_routers( context, [router_id]) return {'agents': [self._make_agent_dict(agent) for agent in agents]} def get_routers_l3_agents_count(self, context): """Return a map between routers and agent counts for all routers.""" # TODO(sshank): This portion needs Router OVO integration when it is # merged. l3_model_list = l3_objs.RouterExtraAttributes.get_router_agents_count( context) return [(self._make_router_dict(router_model), agent_count if agent_count else 0) for router_model, agent_count in l3_model_list] def get_l3_agents(self, context, active=None, filters=None): agent_filters = {'agent_type': constants.AGENT_TYPE_L3} if active is not None: agent_filters['admin_state_up'] = active config_filters = [] if filters: for key, value in filters.items(): column = ag_obj.Agent.fields.get(key, None) if column: if not value: return [] agent_modes = filters.pop('agent_modes', []) if agent_modes: config_filters = set('\"agent_mode\": \"%s\"' % agent_mode for agent_mode in agent_modes) agent_filters.update(filters) agent_objs = [] if config_filters: for conf_filter in config_filters: agent_objs.extend(ag_obj.Agent.get_objects_by_agent_mode( context, conf_filter, **agent_filters)) else: agent_objs = ag_obj.Agent.get_objects(context, **agent_filters) return [l3_agent for l3_agent in agent_objs if agentschedulers_db.AgentSchedulerDbMixin.is_eligible_agent( active, l3_agent)] def get_l3_agent_candidates(self, context, sync_router, l3_agents, ignore_admin_state=False): """Get the valid l3 agents for the router from a list of l3_agents. It will not return agents in 'dvr' mode or in 'dvr_no_external' mode for a dvr router as dvr routers are not explicitly scheduled to l3 agents on compute nodes """ candidates = [] is_router_distributed = sync_router.get('distributed', False) for l3_agent in l3_agents: if not ignore_admin_state and not l3_agent.admin_state_up: # ignore_admin_state True comes from manual scheduling # where admin_state_up judgement is already done. continue agent_conf = self.get_configuration_dict(l3_agent) agent_mode = agent_conf.get(constants.L3_AGENT_MODE, constants.L3_AGENT_MODE_LEGACY) if (agent_mode == constants.L3_AGENT_MODE_DVR or agent_mode == l_consts.L3_AGENT_MODE_DVR_NO_EXTERNAL or (agent_mode == constants.L3_AGENT_MODE_LEGACY and is_router_distributed)): continue router_id = agent_conf.get('router_id', None) if router_id and router_id != sync_router['id']: continue handle_internal_only_routers = agent_conf.get( 'handle_internal_only_routers', True) gateway_external_network_id = agent_conf.get( 'gateway_external_network_id', None) ex_net_id = (sync_router['external_gateway_info'] or {}).get( 'network_id') if ((not ex_net_id and not handle_internal_only_routers) or (ex_net_id and gateway_external_network_id and ex_net_id != gateway_external_network_id)): continue candidates.append(l3_agent) return candidates def auto_schedule_routers(self, context, host): if self.router_scheduler: self.router_scheduler.auto_schedule_routers(self, context, host) def schedule_router(self, context, router, candidates=None): if self.router_scheduler: return self.router_scheduler.schedule( self, context, router, candidates=candidates) def schedule_routers(self, context, routers): """Schedule the routers to l3 agents.""" for router in routers: self.schedule_router(context, router, candidates=None) def get_l3_agent_with_min_routers(self, context, agent_ids): if not agent_ids: return None agents = ag_obj.Agent.get_l3_agent_with_min_routers( context, agent_ids) return agents def get_hosts_to_notify(self, context, router_id): """Returns all hosts to send notification about router update""" state = agentschedulers_db.get_admin_state_up_filter() agents = self.get_l3_agents_hosting_routers( context, [router_id], admin_state_up=state, active=True) return [a.host for a in agents] def get_vacant_binding_index(self, context, router_id, is_manual_scheduling=False): """Return a vacant binding_index to use and whether or not it exists. Each RouterL3AgentBinding has a binding_index which is unique per router_id, and when creating a single binding we require to find a 'vacant' binding_index which isn't yet used - for example if we have bindings with indices 1 and 3, then clearly binding_index == 2 is free. :returns: binding_index. """ num_agents = self.get_number_of_agents_for_scheduling(context) pager = base_obj.Pager(sorts=[('binding_index', True)]) bindings = rb_obj.RouterL3AgentBinding.get_objects( context, _pager=pager, router_id=router_id) binding_indices = [b.binding_index for b in bindings] all_indicies = set(range(rb_model.LOWEST_BINDING_INDEX, num_agents + 1)) open_slots = sorted(list(all_indicies - set(binding_indices))) if open_slots: return open_slots[0] # Last chance: if this is a manual scheduling, we're gonna allow # creation of a binding_index even if it will exceed # max_l3_agents_per_router. if is_manual_scheduling: return max(all_indicies) + 1 return -1 class AZL3AgentSchedulerDbMixin(L3AgentSchedulerDbMixin, router_az.RouterAvailabilityZonePluginBase): """Mixin class to add availability_zone supported l3 agent scheduler.""" def get_router_availability_zones(self, router): return list({agent.availability_zone for agent in router.l3_agents}) neutron-12.1.1/neutron/db/migration/0000775000175000017500000000000013553660156017372 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/migration/models/0000775000175000017500000000000013553660156020655 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/migration/models/head.py0000664000175000017500000000346613553660046022137 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ The module provides all database models at current HEAD. Its purpose is to create comparable metadata with current database schema. Based on this comparison database can be healed with healing migration. """ import os.path from neutron_lib.db import model_base from neutron.common import utils from neutron.db import agentschedulers_db # noqa from neutron.db.extra_dhcp_opt import models as edo_models # noqa from neutron.db import l3_dvrscheduler_db # noqa from neutron.db import l3_gwmode_db # noqa from neutron.db import models from neutron.db import models_v2 # noqa from neutron.db.port_security import models as ps_models # noqa from neutron.db.qos import models as qos_models # noqa from neutron.db.quota import models as quota_models # noqa from neutron.db import rbac_db_models # noqa from neutron.ipam.drivers.neutrondb_ipam import db_models # noqa from neutron.plugins.ml2 import models as ml2_models # noqa from neutron.services.auto_allocate import models as aa_models # noqa from neutron.services.trunk import models as trunk_models # noqa utils.import_modules_recursively(os.path.dirname(models.__file__)) def get_metadata(): return model_base.BASEV2.metadata neutron-12.1.1/neutron/db/migration/models/__init__.py0000664000175000017500000000000013553660046022752 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/migration/autogen.py0000664000175000017500000000752113553660047021412 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from alembic.operations import ops from alembic.util import Dispatcher from alembic.util import rev_id as new_rev_id from neutron.db.migration import cli _ec_dispatcher = Dispatcher() def process_revision_directives(context, revision, directives): directives[:] = [ directive for directive in _assign_directives(context, directives) ] def _assign_directives(context, directives, phase=None): for directive in directives: decider = _ec_dispatcher.dispatch(directive) if phase is None: phases = cli.MIGRATION_BRANCHES else: phases = (phase,) for phase in phases: decided = decider(context, directive, phase) if decided: yield decided @_ec_dispatcher.dispatch_for(ops.MigrationScript) def _migration_script_ops(context, directive, phase): """Generate a new ops.MigrationScript() for a given phase. E.g. given an ops.MigrationScript() directive from a vanilla autogenerate and an expand/contract phase name, produce a new ops.MigrationScript() which contains only those sub-directives appropriate to "expand" or "contract". Also ensure that the branch directory exists and that the correct branch labels/depends_on/head revision are set up. """ version_path = cli._get_version_branch_path( context.config, release=cli.CURRENT_RELEASE, branch=phase) autogen_kwargs = {} cli._check_bootstrap_new_branch(phase, version_path, autogen_kwargs) op = ops.MigrationScript( new_rev_id(), ops.UpgradeOps(ops=[ d for d in _assign_directives( context, directive.upgrade_ops.ops, phase) ]), ops.DowngradeOps(ops=[]), message=directive.message, **autogen_kwargs ) if not op.upgrade_ops.is_empty(): return op @_ec_dispatcher.dispatch_for(ops.AddConstraintOp) @_ec_dispatcher.dispatch_for(ops.CreateIndexOp) @_ec_dispatcher.dispatch_for(ops.CreateTableOp) @_ec_dispatcher.dispatch_for(ops.AddColumnOp) def _expands(context, directive, phase): if phase == 'expand': return directive else: return None @_ec_dispatcher.dispatch_for(ops.DropConstraintOp) @_ec_dispatcher.dispatch_for(ops.DropIndexOp) @_ec_dispatcher.dispatch_for(ops.DropTableOp) @_ec_dispatcher.dispatch_for(ops.DropColumnOp) def _contracts(context, directive, phase): if phase == 'contract': return directive else: return None @_ec_dispatcher.dispatch_for(ops.AlterColumnOp) def _alter_column(context, directive, phase): is_expand = phase == 'expand' if is_expand and ( directive.modify_nullable is True ): return directive elif not is_expand and ( directive.modify_nullable is False ): return directive else: raise NotImplementedError( "Don't know if operation is an expand or " "contract at the moment: %s" % directive) @_ec_dispatcher.dispatch_for(ops.ModifyTableOps) def _modify_table_ops(context, directive, phase): op = ops.ModifyTableOps( directive.table_name, ops=[ d for d in _assign_directives(context, directive.ops, phase) ], schema=directive.schema) if not op.is_empty(): return op neutron-12.1.1/neutron/db/migration/alembic.ini0000664000175000017500000000177513553660046021477 0ustar zuulzuul00000000000000# A generic, single database configuration. [alembic] # path to migration scripts script_location = %(here)s/alembic_migrations # template used to generate migration files # file_template = %%(rev)s_%%(slug)s # set to 'true' to run the environment during # the 'revision' command, regardless of autogenerate # revision_environment = false # default to an empty string because the Neutron migration cli will # extract the correct value and set it programmatically before alembic is fully # invoked. sqlalchemy.url = # Logging configuration [loggers] keys = root,sqlalchemy,alembic [handlers] keys = console [formatters] keys = generic [logger_root] level = WARN handlers = console qualname = [logger_sqlalchemy] level = WARN handlers = qualname = sqlalchemy.engine [logger_alembic] level = INFO handlers = qualname = alembic [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(levelname)-5.5s [%(name)s] %(message)s datefmt = %H:%M:%S neutron-12.1.1/neutron/db/migration/__init__.py0000664000175000017500000002103313553660047021501 0ustar zuulzuul00000000000000# Copyright 2012 New Dream Network, LLC (DreamHost) # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib import functools from alembic import context from alembic import op import sqlalchemy as sa from sqlalchemy.engine import reflection from neutron._i18n import _ # Neutron milestones for upgrade aliases LIBERTY = 'liberty' MITAKA = 'mitaka' NEWTON = 'newton' OCATA = 'ocata' PIKE = 'pike' QUEENS = 'queens' NEUTRON_MILESTONES = [ # earlier milestones were not tagged LIBERTY, MITAKA, NEWTON, OCATA, PIKE, QUEENS, # Do not add the milestone until the end of the release ] def skip_if_offline(func): """Decorator for skipping migrations in offline mode.""" @functools.wraps(func) def decorator(*args, **kwargs): if context.is_offline_mode(): return return func(*args, **kwargs) return decorator def raise_if_offline(func): """Decorator for raising if a function is called in offline mode.""" @functools.wraps(func) def decorator(*args, **kwargs): if context.is_offline_mode(): raise RuntimeError(_("%s cannot be called while in offline mode") % func.__name__) return func(*args, **kwargs) return decorator @raise_if_offline def schema_has_table(table_name): """Check whether the specified table exists in the current schema. This method cannot be executed in offline mode. """ bind = op.get_bind() insp = sa.engine.reflection.Inspector.from_engine(bind) return table_name in insp.get_table_names() @raise_if_offline def schema_has_column(table_name, column_name): """Check whether the specified column exists in the current schema. This method cannot be executed in offline mode. """ bind = op.get_bind() insp = sa.engine.reflection.Inspector.from_engine(bind) # first check that the table exists if not schema_has_table(table_name): return # check whether column_name exists in table columns return column_name in [column['name'] for column in insp.get_columns(table_name)] @raise_if_offline def alter_column_if_exists(table_name, column_name, **kwargs): """Alter a column only if it exists in the schema.""" if schema_has_column(table_name, column_name): op.alter_column(table_name, column_name, **kwargs) @raise_if_offline def drop_table_if_exists(table_name): if schema_has_table(table_name): op.drop_table(table_name) @raise_if_offline def rename_table_if_exists(old_table_name, new_table_name): if schema_has_table(old_table_name): op.rename_table(old_table_name, new_table_name) def alter_enum_add_value(table, column, enum, nullable): '''If we need to expand Enum values for some column - for PostgreSQL this can be done with ALTER TYPE function. For MySQL, it can be done with ordinary alembic alter_column function. :param table:table name :param column: column name :param enum: sqlalchemy Enum with updated values :param nullable: existing nullable for column. ''' bind = op.get_bind() engine = bind.engine if engine.name == 'postgresql': values = {'name': enum.name, 'values': ", ".join("'" + i + "'" for i in enum.enums), 'column': column, 'table': table} op.execute("ALTER TYPE %(name)s rename to old_%(name)s" % values) op.execute("CREATE TYPE %(name)s AS enum (%(values)s)" % values) op.execute("ALTER TABLE %(table)s ALTER COLUMN %(column)s TYPE " "%(name)s USING %(column)s::text::%(name)s " % values) op.execute("DROP TYPE old_%(name)s" % values) else: op.alter_column(table, column, type_=enum, existing_nullable=nullable) def alter_enum(table, column, enum_type, nullable, do_drop=True, do_rename=True, do_create=True): """Alter a enum type column. Set the do_xx parameters only when the modified enum type is used by multiple columns. Else don't provide these parameters. :param do_drop: set to False when modified column is not the last one use this enum :param do_rename: set to False when modified column is not the first one use this enum :param do_create: set to False when modified column is not the first one use this enum """ bind = op.get_bind() engine = bind.engine if engine.name == 'postgresql': values = {'table': table, 'column': column, 'name': enum_type.name} if do_rename: op.execute("ALTER TYPE %(name)s RENAME TO old_%(name)s" % values) if do_create: enum_type.create(bind, checkfirst=False) op.execute("ALTER TABLE %(table)s RENAME COLUMN %(column)s TO " "old_%(column)s" % values) op.add_column(table, sa.Column(column, enum_type, nullable=nullable)) op.execute("UPDATE %(table)s SET %(column)s = " # nosec "old_%(column)s::text::%(name)s" % values) op.execute("ALTER TABLE %(table)s DROP COLUMN old_%(column)s" % values) if do_drop: op.execute("DROP TYPE old_%(name)s" % values) else: op.alter_column(table, column, type_=enum_type, existing_nullable=nullable) def create_table_if_not_exist_psql(table_name, values): if op.get_bind().engine.dialect.server_version_info < (9, 1, 0): op.execute("CREATE LANGUAGE plpgsql") op.execute("CREATE OR REPLACE FUNCTION execute(TEXT) RETURNS VOID AS $$" "BEGIN EXECUTE $1; END;" "$$ LANGUAGE plpgsql STRICT;") op.execute("CREATE OR REPLACE FUNCTION table_exist(TEXT) RETURNS bool as " "$$ SELECT exists(select 1 from pg_class where relname=$1);" "$$ language sql STRICT;") op.execute("SELECT execute($$CREATE TABLE %(name)s %(columns)s $$) " "WHERE NOT table_exist(%(name)r);" % {'name': table_name, 'columns': values}) def get_unique_constraints_map(table): inspector = reflection.Inspector.from_engine(op.get_bind()) return { tuple(sorted(cons['column_names'])): cons['name'] for cons in inspector.get_unique_constraints(table) } def remove_fk_unique_constraints(table, foreign_keys): unique_constraints_map = get_unique_constraints_map(table) for fk in foreign_keys: constraint_name = unique_constraints_map.get( tuple(sorted(fk['constrained_columns']))) if constraint_name: op.drop_constraint( constraint_name=constraint_name, table_name=table, type_="unique" ) def remove_foreign_keys(table, foreign_keys): for fk in foreign_keys: op.drop_constraint( constraint_name=fk['name'], table_name=table, type_='foreignkey' ) def create_foreign_keys(table, foreign_keys): for fk in foreign_keys: op.create_foreign_key( constraint_name=fk['name'], source_table=table, referent_table=fk['referred_table'], local_cols=fk['constrained_columns'], remote_cols=fk['referred_columns'], ondelete=fk['options'].get('ondelete') ) @contextlib.contextmanager def remove_fks_from_table(table, remove_unique_constraints=False): try: inspector = reflection.Inspector.from_engine(op.get_bind()) foreign_keys = inspector.get_foreign_keys(table) remove_foreign_keys(table, foreign_keys) if remove_unique_constraints: remove_fk_unique_constraints(table, foreign_keys) yield finally: create_foreign_keys(table, foreign_keys) def pk_on_alembic_version_table(): inspector = reflection.Inspector.from_engine(op.get_bind()) pk = inspector.get_pk_constraint('alembic_version') if not pk['constrained_columns']: op.create_primary_key(op.f('pk_alembic_version'), 'alembic_version', ['version_num']) neutron-12.1.1/neutron/db/migration/alembic_migrations/0000775000175000017500000000000013553660156023222 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/vpn_init_ops.py0000664000175000017500000001425213553660046026305 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial schema operations for IPSEC VPN service plugin from alembic import op import sqlalchemy as sa auth_algorithms = sa.Enum('sha1', name='vpn_auth_algorithms') encryption_algorithms = sa.Enum('3des', 'aes-128', 'aes-256', 'aes-192', name='vpn_encrypt_algorithms') encapsulation_modes = sa.Enum('tunnel', 'transport', name='ipsec_encapsulations') lifetime_unit_types = sa.Enum('seconds', 'kilobytes', name='vpn_lifetime_units') transform_protocols = sa.Enum('esp', 'ah', 'ah-esp', name='ipsec_transform_protocols') pfs_types = sa.Enum('group2', 'group5', 'group14', name='vpn_pfs') phase1_negotiation_modes = sa.Enum('main', name='ike_phase1_mode') ike_versions = sa.Enum('v1', 'v2', name='ike_versions') initiator_types = sa.Enum('bi-directional', 'response-only', name='vpn_initiators') dpd_actions = sa.Enum('hold', 'clear', 'restart', 'disabled', 'restart-by-peer', name='vpn_dpd_actions') def upgrade(): op.create_table( 'ipsecpolicies', sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('description', sa.String(length=255), nullable=True), sa.Column('transform_protocol', transform_protocols, nullable=False), sa.Column('auth_algorithm', auth_algorithms, nullable=False), sa.Column('encryption_algorithm', encryption_algorithms, nullable=False), sa.Column('encapsulation_mode', encapsulation_modes, nullable=False), sa.Column('lifetime_units', lifetime_unit_types, nullable=False), sa.Column('lifetime_value', sa.Integer(), nullable=False), sa.Column('pfs', pfs_types, nullable=False), sa.PrimaryKeyConstraint('id')) op.create_table( 'ikepolicies', sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('description', sa.String(length=255), nullable=True), sa.Column('auth_algorithm', auth_algorithms, nullable=False), sa.Column('encryption_algorithm', encryption_algorithms, nullable=False), sa.Column('phase1_negotiation_mode', phase1_negotiation_modes, nullable=False), sa.Column('lifetime_units', lifetime_unit_types, nullable=False), sa.Column('lifetime_value', sa.Integer(), nullable=False), sa.Column('ike_version', ike_versions, nullable=False), sa.Column('pfs', pfs_types, nullable=False), sa.PrimaryKeyConstraint('id')) op.create_table( 'vpnservices', sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('description', sa.String(length=255), nullable=True), sa.Column('status', sa.String(length=16), nullable=False), sa.Column('admin_state_up', sa.Boolean(), nullable=False), sa.Column('subnet_id', sa.String(length=36), nullable=False), sa.Column('router_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['subnet_id'], ['subnets.id'], ), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ), sa.PrimaryKeyConstraint('id')) op.create_table( 'ipsec_site_connections', sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('description', sa.String(length=255), nullable=True), sa.Column('peer_address', sa.String(length=255), nullable=False), sa.Column('peer_id', sa.String(length=255), nullable=False), sa.Column('route_mode', sa.String(length=8), nullable=False), sa.Column('mtu', sa.Integer(), nullable=False), sa.Column('initiator', initiator_types, nullable=False), sa.Column('auth_mode', sa.String(length=16), nullable=False), sa.Column('psk', sa.String(length=255), nullable=False), sa.Column('dpd_action', dpd_actions, nullable=False), sa.Column('dpd_interval', sa.Integer(), nullable=False), sa.Column('dpd_timeout', sa.Integer(), nullable=False), sa.Column('status', sa.String(length=16), nullable=False), sa.Column('admin_state_up', sa.Boolean(), nullable=False), sa.Column('vpnservice_id', sa.String(length=36), nullable=False), sa.Column('ipsecpolicy_id', sa.String(length=36), nullable=False), sa.Column('ikepolicy_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['vpnservice_id'], ['vpnservices.id'], ), sa.ForeignKeyConstraint(['ipsecpolicy_id'], ['ipsecpolicies.id'], ), sa.ForeignKeyConstraint(['ikepolicy_id'], ['ikepolicies.id'], ), sa.PrimaryKeyConstraint('id')) op.create_table( 'ipsecpeercidrs', sa.Column('cidr', sa.String(length=32), nullable=False), sa.Column('ipsec_site_connection_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['ipsec_site_connection_id'], ['ipsec_site_connections.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('cidr', 'ipsec_site_connection_id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/script.py.mako0000664000175000017500000000202713553660047026026 0ustar zuulzuul00000000000000# Copyright ${create_date.year} OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """${message} Revision ID: ${up_revision} Revises: ${down_revision} Create Date: ${create_date} """ # revision identifiers, used by Alembic. revision = ${repr(up_revision)} down_revision = ${repr(down_revision)} % if branch_labels: branch_labels = ${repr(branch_labels)} % endif from alembic import op import sqlalchemy as sa ${imports if imports else ""} def upgrade(): ${upgrades if upgrades else "pass"} neutron-12.1.1/neutron/db/migration/alembic_migrations/l3_init_ops.py0000664000175000017500000001553513553660046026025 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for l3 extension from alembic import op import sqlalchemy as sa l3_ha_states = sa.Enum('active', 'standby', name='l3_ha_states') def create_routerroutes(): op.create_table( 'routerroutes', sa.Column('destination', sa.String(length=64), nullable=False), sa.Column('nexthop', sa.String(length=64), nullable=False), sa.Column('router_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('destination', 'nexthop', 'router_id')) def upgrade(): op.create_table( 'externalnetworks', sa.Column('network_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id')) op.create_table( 'routers', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('status', sa.String(length=16), nullable=True), sa.Column('admin_state_up', sa.Boolean(), nullable=True), sa.Column('gw_port_id', sa.String(length=36), nullable=True), sa.Column('enable_snat', sa.Boolean(), nullable=False, server_default=sa.sql.true()), sa.ForeignKeyConstraint(['gw_port_id'], ['ports.id'], ), sa.PrimaryKeyConstraint('id')) op.create_table( 'floatingips', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('floating_ip_address', sa.String(length=64), nullable=False), sa.Column('floating_network_id', sa.String(length=36), nullable=False), sa.Column('floating_port_id', sa.String(length=36), nullable=False), sa.Column('fixed_port_id', sa.String(length=36), nullable=True), sa.Column('fixed_ip_address', sa.String(length=64), nullable=True), sa.Column('router_id', sa.String(length=36), nullable=True), sa.Column('last_known_router_id', sa.String(length=36), nullable=True), sa.Column('status', sa.String(length=16), nullable=True), sa.ForeignKeyConstraint(['fixed_port_id'], ['ports.id'], ), sa.ForeignKeyConstraint(['floating_port_id'], ['ports.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ), sa.PrimaryKeyConstraint('id')) create_routerroutes() op.create_table( 'routerl3agentbindings', sa.Column('router_id', sa.String(length=36), nullable=True), sa.Column('l3_agent_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['l3_agent_id'], ['agents.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('router_id', 'l3_agent_id')) op.create_table( 'router_extra_attributes', sa.Column('router_id', sa.String(length=36), nullable=False), sa.Column('distributed', sa.Boolean(), nullable=False, server_default=sa.sql.false()), sa.Column('service_router', sa.Boolean(), nullable=False, server_default=sa.sql.false()), sa.Column('ha', sa.Boolean(), nullable=False, server_default=sa.sql.false()), sa.Column('ha_vr_id', sa.Integer()), sa.ForeignKeyConstraint( ['router_id'], ['routers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('router_id') ) op.create_table('ha_router_agent_port_bindings', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('router_id', sa.String(length=36), nullable=False), sa.Column('l3_agent_id', sa.String(length=36), nullable=True), sa.Column('state', l3_ha_states, server_default='standby'), sa.PrimaryKeyConstraint('port_id'), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['l3_agent_id'], ['agents.id'], ondelete='CASCADE')) op.create_table('ha_router_networks', sa.Column('tenant_id', sa.String(length=255), nullable=False, primary_key=True), sa.Column('network_id', sa.String(length=36), nullable=False, primary_key=True), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE')) op.create_table('ha_router_vrid_allocations', sa.Column('network_id', sa.String(length=36), nullable=False, primary_key=True), sa.Column('vr_id', sa.Integer(), nullable=False, primary_key=True), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE')) op.create_table( 'routerports', sa.Column('router_id', sa.String(length=36), nullable=False), sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('port_type', sa.String(length=255)), sa.PrimaryKeyConstraint('router_id', 'port_id'), sa.ForeignKeyConstraint( ['router_id'], ['routers.id'], ondelete='CASCADE' ), sa.ForeignKeyConstraint( ['port_id'], ['ports.id'], ondelete='CASCADE' ), ) neutron-12.1.1/neutron/db/migration/alembic_migrations/cisco_init_ops.py0000664000175000017500000003663613553660047026615 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial schema operations for cisco plugin from alembic import op import sqlalchemy as sa segment_type = sa.Enum('vlan', 'overlay', 'trunk', 'multi-segment', name='segment_type') profile_type = sa.Enum('network', 'policy', name='profile_type') network_profile_type = sa.Enum('vlan', 'vxlan', name='network_profile_type') def upgrade(): op.create_table( 'cisco_policy_profiles', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'cisco_network_profiles', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('segment_type', segment_type, nullable=False), sa.Column('sub_type', sa.String(length=255), nullable=True), sa.Column('segment_range', sa.String(length=255), nullable=True), sa.Column('multicast_ip_index', sa.Integer(), nullable=True, server_default='0'), sa.Column('multicast_ip_range', sa.String(length=255), nullable=True), sa.Column('physical_network', sa.String(length=255), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'cisco_n1kv_vxlan_allocations', sa.Column('vxlan_id', sa.Integer(), autoincrement=False, nullable=False), sa.Column('allocated', sa.Boolean(), nullable=False, server_default=sa.sql.false()), sa.Column('network_profile_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['network_profile_id'], ['cisco_network_profiles.id'], ondelete='CASCADE', name='cisco_n1kv_vxlan_allocations_ibfk_1'), sa.PrimaryKeyConstraint('vxlan_id')) op.create_table( 'cisco_n1kv_vlan_allocations', sa.Column('physical_network', sa.String(length=64), nullable=False), sa.Column('vlan_id', sa.Integer(), autoincrement=False, nullable=False), sa.Column('allocated', sa.Boolean(), autoincrement=False, nullable=False, server_default=sa.sql.false()), sa.Column('network_profile_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('physical_network', 'vlan_id'), sa.ForeignKeyConstraint(['network_profile_id'], ['cisco_network_profiles.id'], ondelete='CASCADE', name='cisco_n1kv_vlan_allocations_ibfk_1')) op.create_table( 'cisco_credentials', sa.Column('credential_id', sa.String(length=255), nullable=True), sa.Column('credential_name', sa.String(length=255), nullable=False), sa.Column('user_name', sa.String(length=255), nullable=True), sa.Column('password', sa.String(length=255), nullable=True), sa.Column('type', sa.String(length=255), nullable=True), sa.PrimaryKeyConstraint('credential_name')) op.create_table( 'cisco_qos_policies', sa.Column('qos_id', sa.String(length=255), nullable=True), sa.Column('tenant_id', sa.String(length=255), nullable=False), sa.Column('qos_name', sa.String(length=255), nullable=False), sa.Column('qos_desc', sa.String(length=255), nullable=True), sa.PrimaryKeyConstraint('tenant_id', 'qos_name')) op.create_table( 'cisco_n1kv_profile_bindings', sa.Column('profile_type', profile_type, nullable=True), sa.Column('tenant_id', sa.String(length=36), nullable=False, server_default='TENANT_ID_NOT_SET'), sa.Column('profile_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('tenant_id', 'profile_id')) op.create_table( 'cisco_n1kv_vmnetworks', sa.Column('name', sa.String(length=80), nullable=False), sa.Column('profile_id', sa.String(length=36), nullable=True), sa.Column('network_id', sa.String(length=36), nullable=True), sa.Column('port_count', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['profile_id'], ['cisco_policy_profiles.id'], ), sa.PrimaryKeyConstraint('name')) op.create_table( 'cisco_n1kv_trunk_segments', sa.Column('trunk_segment_id', sa.String(length=36), nullable=False), sa.Column('segment_id', sa.String(length=36), nullable=False), sa.Column('dot1qtag', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['trunk_segment_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('trunk_segment_id', 'segment_id', 'dot1qtag')) op.create_table( 'cisco_provider_networks', sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('network_type', sa.String(length=255), nullable=False), sa.Column('segmentation_id', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id')) op.create_table( 'cisco_n1kv_multi_segments', sa.Column('multi_segment_id', sa.String(length=36), nullable=False), sa.Column('segment1_id', sa.String(length=36), nullable=False), sa.Column('segment2_id', sa.String(length=36), nullable=False), sa.Column('encap_profile_name', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['multi_segment_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('multi_segment_id', 'segment1_id', 'segment2_id')) op.create_table( 'cisco_n1kv_network_bindings', sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('network_type', sa.String(length=32), nullable=False), sa.Column('physical_network', sa.String(length=64), nullable=True), sa.Column('segmentation_id', sa.Integer(), nullable=True), sa.Column('multicast_ip', sa.String(length=32), nullable=True), sa.Column('profile_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['profile_id'], ['cisco_network_profiles.id']), sa.PrimaryKeyConstraint('network_id')) op.create_table( 'cisco_n1kv_port_bindings', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('profile_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['profile_id'], ['cisco_policy_profiles.id']), sa.PrimaryKeyConstraint('port_id')) op.create_table( 'cisco_csr_identifier_map', sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('ipsec_site_conn_id', sa.String(length=36), primary_key=True), sa.Column('csr_tunnel_id', sa.Integer(), nullable=False), sa.Column('csr_ike_policy_id', sa.Integer(), nullable=False), sa.Column('csr_ipsec_policy_id', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(['ipsec_site_conn_id'], ['ipsec_site_connections.id'], ondelete='CASCADE') ) op.create_table( 'cisco_ml2_apic_host_links', sa.Column('host', sa.String(length=255), nullable=False), sa.Column('ifname', sa.String(length=64), nullable=False), sa.Column('ifmac', sa.String(length=32), nullable=True), sa.Column('swid', sa.String(length=32), nullable=False), sa.Column('module', sa.String(length=32), nullable=False), sa.Column('port', sa.String(length=32), nullable=False), sa.PrimaryKeyConstraint('host', 'ifname')) op.create_table( 'cisco_ml2_apic_names', sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.Column('neutron_type', sa.String(length=32), nullable=False), sa.Column('apic_name', sa.String(length=255), nullable=False), sa.PrimaryKeyConstraint('neutron_id', 'neutron_type')) op.create_table( 'cisco_ml2_apic_contracts', sa.Column('tenant_id', sa.String(length=255), index=True), sa.Column('router_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['router_id'], ['routers.id']), sa.PrimaryKeyConstraint('router_id')) op.create_table('cisco_hosting_devices', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('complementary_id', sa.String(length=36), nullable=True), sa.Column('device_id', sa.String(length=255), nullable=True), sa.Column('admin_state_up', sa.Boolean(), nullable=False), sa.Column('management_port_id', sa.String(length=36), nullable=True), sa.Column('protocol_port', sa.Integer(), nullable=True), sa.Column('cfg_agent_id', sa.String(length=36), nullable=True), sa.Column('created_at', sa.DateTime(), nullable=False), sa.Column('status', sa.String(length=16), nullable=True), sa.ForeignKeyConstraint(['cfg_agent_id'], ['agents.id'], ), sa.ForeignKeyConstraint(['management_port_id'], ['ports.id'], ondelete='SET NULL'), sa.PrimaryKeyConstraint('id') ) op.create_table('cisco_port_mappings', sa.Column('logical_resource_id', sa.String(length=36), nullable=False), sa.Column('logical_port_id', sa.String(length=36), nullable=False), sa.Column('port_type', sa.String(length=32), nullable=True), sa.Column('network_type', sa.String(length=32), nullable=True), sa.Column('hosting_port_id', sa.String(length=36), nullable=True), sa.Column('segmentation_id', sa.Integer(), autoincrement=False, nullable=True), sa.ForeignKeyConstraint(['hosting_port_id'], ['ports.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['logical_port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('logical_resource_id', 'logical_port_id') ) op.create_table('cisco_router_mappings', sa.Column('router_id', sa.String(length=36), nullable=False), sa.Column('auto_schedule', sa.Boolean(), nullable=False), sa.Column('hosting_device_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['hosting_device_id'], ['cisco_hosting_devices.id'], ondelete='SET NULL'), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('router_id') ) op.create_table( 'cisco_ml2_n1kv_policy_profiles', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=False), sa.Column('vsm_ip', sa.String(length=16), nullable=False), sa.PrimaryKeyConstraint('id', 'vsm_ip'), ) op.create_table( 'cisco_ml2_n1kv_network_profiles', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=False), sa.Column('segment_type', network_profile_type, nullable=False), sa.Column('segment_range', sa.String(length=255), nullable=True), sa.Column('multicast_ip_index', sa.Integer(), nullable=True), sa.Column('multicast_ip_range', sa.String(length=255), nullable=True), sa.Column('sub_type', sa.String(length=255), nullable=True), sa.Column('physical_network', sa.String(length=255), nullable=True), sa.PrimaryKeyConstraint('id'), ) op.create_table( 'cisco_ml2_n1kv_port_bindings', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('profile_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('port_id'), ) op.create_table( 'cisco_ml2_n1kv_network_bindings', sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('network_type', sa.String(length=32), nullable=False), sa.Column('segmentation_id', sa.Integer(), autoincrement=False), sa.Column('profile_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['profile_id'], ['cisco_ml2_n1kv_network_profiles.id']), sa.PrimaryKeyConstraint('network_id') ) op.create_table( 'cisco_ml2_n1kv_vxlan_allocations', sa.Column('vxlan_id', sa.Integer(), autoincrement=False, nullable=False), sa.Column('allocated', sa.Boolean(), nullable=False), sa.Column('network_profile_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['network_profile_id'], ['cisco_ml2_n1kv_network_profiles.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('vxlan_id') ) op.create_table( 'cisco_ml2_n1kv_vlan_allocations', sa.Column('physical_network', sa.String(length=64), nullable=False), sa.Column('vlan_id', sa.Integer(), autoincrement=False, nullable=False), sa.Column('allocated', sa.Boolean(), autoincrement=False, nullable=False), sa.Column('network_profile_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['network_profile_id'], ['cisco_ml2_n1kv_network_profiles.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('physical_network', 'vlan_id') ) op.create_table( 'cisco_ml2_n1kv_profile_bindings', sa.Column('profile_type', profile_type, nullable=True), sa.Column('tenant_id', sa.String(length=36), nullable=False, server_default='tenant_id_not_set'), sa.Column('profile_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('tenant_id', 'profile_id') ) op.create_table( 'ml2_ucsm_port_profiles', sa.Column('vlan_id', sa.Integer(), nullable=False), sa.Column('profile_id', sa.String(length=64), nullable=False), sa.Column('created_on_ucs', sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint('vlan_id') ) neutron-12.1.1/neutron/db/migration/alembic_migrations/dvr_init_opts.py0000664000175000017500000000507313553660046026462 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for dvr from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'dvr_host_macs', sa.Column('host', sa.String(length=255), nullable=False), sa.Column('mac_address', sa.String(length=32), nullable=False, unique=True), sa.PrimaryKeyConstraint('host') ) op.create_table( 'ml2_dvr_port_bindings', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('host', sa.String(length=255), nullable=False), sa.Column('router_id', sa.String(length=36), nullable=True), sa.Column('vif_type', sa.String(length=64), nullable=False), sa.Column('vif_details', sa.String(length=4095), nullable=False, server_default=''), sa.Column('vnic_type', sa.String(length=64), nullable=False, server_default='normal'), sa.Column('profile', sa.String(length=4095), nullable=False, server_default=''), sa.Column(u'status', sa.String(16), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('port_id', 'host') ) op.create_table( 'csnat_l3_agent_bindings', sa.Column('router_id', sa.String(length=36), nullable=False), sa.Column('l3_agent_id', sa.String(length=36), nullable=False), sa.Column('host_id', sa.String(length=255), nullable=True), sa.Column('csnat_gw_port_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['l3_agent_id'], ['agents.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['csnat_gw_port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('router_id', 'l3_agent_id') ) neutron-12.1.1/neutron/db/migration/alembic_migrations/nuage_init_opts.py0000664000175000017500000000667013553660046026772 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for Nuage plugin from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'nuage_net_partitions', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=64), nullable=True), sa.Column('l3dom_tmplt_id', sa.String(length=36), nullable=True), sa.Column('l2dom_tmplt_id', sa.String(length=36), nullable=True), sa.Column('isolated_zone', sa.String(length=64), nullable=True), sa.Column('shared_zone', sa.String(length=64), nullable=True), sa.PrimaryKeyConstraint('id'), ) op.create_table( 'nuage_subnet_l2dom_mapping', sa.Column('subnet_id', sa.String(length=36), nullable=False), sa.Column('net_partition_id', sa.String(length=36), nullable=True), sa.Column('nuage_subnet_id', sa.String(length=36), nullable=True, unique=True), sa.Column('nuage_l2dom_tmplt_id', sa.String(length=36), nullable=True), sa.Column('nuage_user_id', sa.String(length=36), nullable=True), sa.Column('nuage_group_id', sa.String(length=36), nullable=True), sa.Column('nuage_managed_subnet', sa.Boolean(), nullable=True), sa.ForeignKeyConstraint(['subnet_id'], ['subnets.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['net_partition_id'], ['nuage_net_partitions.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('subnet_id'), ) op.create_table( 'nuage_net_partition_router_mapping', sa.Column('net_partition_id', sa.String(length=36), nullable=False), sa.Column('router_id', sa.String(length=36), nullable=False), sa.Column('nuage_router_id', sa.String(length=36), nullable=True, unique=True), sa.Column('nuage_rtr_rd', sa.String(length=36), nullable=True), sa.Column('nuage_rtr_rt', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['net_partition_id'], ['nuage_net_partitions.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('net_partition_id', 'router_id'), ) op.create_table( 'nuage_provider_net_bindings', sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('network_type', sa.String(length=32), nullable=False), sa.Column('physical_network', sa.String(length=64), nullable=False), sa.Column('vlan_id', sa.Integer(), nullable=False), sa.ForeignKeyConstraint( ['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id') ) neutron-12.1.1/neutron/db/migration/alembic_migrations/other_plugins_init_ops.py0000664000175000017500000000473213553660046030366 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for plugins: # bigswitch # metaplugin from alembic import op import sqlalchemy as sa def upgrade(): # metaplugin op.create_table( 'networkflavors', sa.Column('flavor', sa.String(length=255), nullable=True), sa.Column('network_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id')) op.create_table( 'routerflavors', sa.Column('flavor', sa.String(length=255), nullable=True), sa.Column('router_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('router_id')) # big switch op.create_table( 'routerrules', sa.Column('id', sa.Integer(), nullable=False), sa.Column('source', sa.String(length=64), nullable=False), sa.Column('destination', sa.String(length=64), nullable=False), sa.Column('action', sa.String(length=10), nullable=False), sa.Column('router_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id')) op.create_table( 'nexthops', sa.Column('rule_id', sa.Integer(), nullable=False), sa.Column('nexthop', sa.String(length=64), nullable=False), sa.ForeignKeyConstraint(['rule_id'], ['routerrules.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('rule_id', 'nexthop')) op.create_table( 'consistencyhashes', sa.Column('hash_id', sa.String(255), primary_key=True), sa.Column('hash', sa.String(255), nullable=False) ) neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/0000775000175000017500000000000013553660156025072 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/0000775000175000017500000000000013553660156026022 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/0000775000175000017500000000000013553660156027301 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000017000000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/349b6fd605a6_add_dns_domain_to_portdnses.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/349b6fd605a6_add_dns_dom0000664000175000017500000000221213553660047033276 0ustar zuulzuul00000000000000# Copyright 2017 IBM # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add dns_domain to portdnses Revision ID: 349b6fd605a6 Revises: c8c222d42aa9 Create Date: 2017-04-15 00:22:47.618593 """ # revision identifiers, used by Alembic. revision = '349b6fd605a6' down_revision = 'c8c222d42aa9' from alembic import op from neutron_lib.db import constants import sqlalchemy as sa def upgrade(): op.add_column('portdnses', sa.Column('dns_domain', sa.String(length=constants.FQDN_FIELD_SIZE), nullable=False, server_default='')) ././@LongLink0000000000000000000000000000017200000000000011215 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/804a3c76314c_add_data_plane_status_to_port.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/804a3c76314c_add_data_pl0000664000175000017500000000243413553660047033174 0ustar zuulzuul00000000000000# Copyright 2017 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add data_plane_status to Port Revision ID: 804a3c76314c Revises: a9c43481023c Create Date: 2017-01-17 13:51:45.737987 """ # revision identifiers, used by Alembic. revision = '804a3c76314c' down_revision = 'a9c43481023c' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table('portdataplanestatuses', sa.Column('port_id', sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True, index=True), sa.Column('data_plane_status', sa.String(length=16), nullable=True)) ././@LongLink0000000000000000000000000000016100000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/7d32f979895f_add_mtu_for_networks.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/7d32f979895f_add_mtu_for0000664000175000017500000000223613553660047033303 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add mtu for networks Revision ID: 7d32f979895f Revises: c8c222d42aa9 Create Date: 2017-07-13 19:25:29.204547 """ from alembic import op import sqlalchemy as sa from neutron.db import migration # revision identifiers, used by Alembic. revision = '7d32f979895f' down_revision = '349b6fd605a6' # require the migration rule that dropped the mtu column in the past depends_on = ('b67e765a3524',) neutron_milestone = [migration.PIKE] def upgrade(): op.add_column('networks', sa.Column('mtu', sa.Integer(), nullable=True)) ././@LongLink0000000000000000000000000000017300000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/62c781cb6192_add_qos_policies_default_table.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/62c781cb6192_add_qos_pol0000664000175000017500000000244713553660047033257 0ustar zuulzuul00000000000000# Copyright 2017 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add is default to qos policies Revision ID: 62c781cb6192 Revises: 2b42d90729da Create Date: 2017-02-07 13:28:35.894357 """ # revision identifiers, used by Alembic. revision = '62c781cb6192' down_revision = '2b42d90729da' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'qos_policies_default', sa.Column('qos_policy_id', sa.String(length=36), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False), sa.Column('project_id', sa.String(length=255), nullable=False, index=True, primary_key=True), ) ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/c8c222d42aa9_logging_api.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/c8c222d42aa9_logging_api0000664000175000017500000000415113553660047033373 0ustar zuulzuul00000000000000# Copyright 2017 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """logging api Revision ID: c8c222d42aa9 Revises: 62c781cb6192 Create Date: 2017-05-30 11:51:08.173604 """ # revision identifiers, used by Alembic. revision = 'c8c222d42aa9' down_revision = '62c781cb6192' from alembic import op import sqlalchemy as sa from neutron_lib.db import constants as db_const def upgrade(): op.create_table( 'logs', sa.Column('project_id', sa.String(length=db_const.PROJECT_ID_FIELD_SIZE), nullable=True, index=True), sa.Column('id', sa.String(length=db_const.UUID_FIELD_SIZE), nullable=False), sa.Column('standard_attr_id', sa.BigInteger(), nullable=False), sa.Column('name', sa.String(length=db_const.NAME_FIELD_SIZE), nullable=True), sa.Column('resource_type', sa.String(length=36), nullable=False), sa.Column('resource_id', sa.String(length=db_const.UUID_FIELD_SIZE), nullable=True, index=True), sa.Column('target_id', sa.String(length=db_const.UUID_FIELD_SIZE), nullable=True, index=True), sa.Column('event', sa.String(length=255), nullable=False), sa.Column('enabled', sa.Boolean(), nullable=True), sa.PrimaryKeyConstraint('id'), sa.ForeignKeyConstraint(['standard_attr_id'], ['standardattributes.id'], ondelete='CASCADE'), sa.UniqueConstraint('standard_attr_id')) ././@LongLink0000000000000000000000000000020500000000000011212 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/2b42d90729da_qos_add_direction_to_bw_limit_rule_table.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/pike/expand/2b42d90729da_qos_add_dir0000664000175000017500000000522213553660047033311 0ustar zuulzuul00000000000000# Copyright 2017 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """qos add direction to bw_limit_rule table Revision ID: 2b42d90729da Revises: 804a3c76314c Create Date: 2017-04-03 20:56:00.169599 """ # revision identifiers, used by Alembic. revision = '2b42d90729da' down_revision = '804a3c76314c' from alembic import op from neutron_lib import constants import sqlalchemy as sa from neutron.db import migration policies_table_name = "qos_policies" bw_limit_table_name = "qos_bandwidth_limit_rules" direction_enum = sa.Enum( constants.EGRESS_DIRECTION, constants.INGRESS_DIRECTION, name="directions" ) def upgrade(): if op.get_context().bind.dialect.name == 'postgresql': direction_enum.create(op.get_bind(), checkfirst=True) with migration.remove_fks_from_table(bw_limit_table_name, remove_unique_constraints=True): op.add_column(bw_limit_table_name, sa.Column("direction", direction_enum, server_default=constants.EGRESS_DIRECTION, nullable=False)) op.create_unique_constraint( op.f('qos_bandwidth_rules0qos_policy_id0direction'), bw_limit_table_name, ['qos_policy_id', 'direction']) def expand_drop_exceptions(): """ Drop the existing QoS policy foreign key uniq constraint and then replace it with new unique constraint for pair (policy_id, direction). As names of constraints are different in MySQL and PGSQL there is need to add both variants to drop exceptions. """ # TODO(slaweq): replace hardcoded constaints names with names get directly # from database model after bug # https://bugs.launchpad.net/neutron/+bug/1685352 will be closed return { sa.ForeignKeyConstraint: [ "qos_bandwidth_limit_rules_ibfk_1", # MySQL name "qos_bandwidth_limit_rules_qos_policy_id_fkey" # PGSQL name ], sa.UniqueConstraint: [ "qos_policy_id", # MySQL name "qos_bandwidth_limit_rules_qos_policy_id_key" # PGSQL name ] } neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/0000775000175000017500000000000013553660156026544 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/0000775000175000017500000000000013553660156030361 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016400000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/2a16083502f3_metaplugin_removal.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/2a16083502f3_metapl0000664000175000017500000000165613553660047033241 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Metaplugin removal Revision ID: 2a16083502f3 Revises: 5498d17be016 Create Date: 2015-06-16 09:11:10.488566 """ # revision identifiers, used by Alembic. revision = '2a16083502f3' down_revision = '5498d17be016' from alembic import op def upgrade(): op.drop_table('networkflavors') op.drop_table('routerflavors') ././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/30018084ec99_initial.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/30018084ec99_initia0000664000175000017500000000160013553660047033236 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Initial no-op Liberty contract rule. Revision ID: 30018084ec99 Revises: None Create Date: 2015-06-22 00:00:00.000000 """ from neutron.db.migration import cli # revision identifiers, used by Alembic. revision = '30018084ec99' down_revision = 'kilo' branch_labels = (cli.CONTRACT_BRANCH,) def upgrade(): pass ././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/4ffceebfada_rbac_network.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/4ffceebfada_rbac_ne0000664000175000017500000000467413553660047034101 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """network_rbac Revision ID: 4ffceebfada Revises: 30018084ec99 Create Date: 2015-06-14 13:12:04.012457 """ # revision identifiers, used by Alembic. revision = '4ffceebfada' down_revision = '30018084ec99' depends_on = ('8675309a5c4f',) from alembic import op from oslo_utils import uuidutils import sqlalchemy as sa # A simple model of the networks table with only the fields needed for # the migration. network = sa.Table('networks', sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255)), sa.Column('shared', sa.Boolean(), nullable=False)) networkrbacs = sa.Table( 'networkrbacs', sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('object_id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('target_tenant', sa.String(length=255), nullable=False), sa.Column('action', sa.String(length=255), nullable=False)) def upgrade(): op.bulk_insert(networkrbacs, get_values()) op.drop_column('networks', 'shared') # the shared column on subnets was just an internal representation of the # shared status of the network it was related to. This is now handled by # other logic so we just drop it. op.drop_column('subnets', 'shared') def get_values(): session = sa.orm.Session(bind=op.get_bind()) values = [] for row in session.query(network).filter(network.c.shared).all(): values.append({'id': uuidutils.generate_uuid(), 'object_id': row[0], 'tenant_id': row[1], 'target_tenant': '*', 'action': 'access_as_shared'}) # this commit appears to be necessary to allow further operations session.commit() return values ././@LongLink0000000000000000000000000000017200000000000011215 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/2e5352a0ad4d_add_missing_foreign_keys.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/2e5352a0ad4d_add_mi0000664000175000017500000000244413553660047033407 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add missing foreign keys Revision ID: 2e5352a0ad4d Revises: 2a16083502f3 Create Date: 2015-08-20 12:43:09.110427 """ # revision identifiers, used by Alembic. revision = '2e5352a0ad4d' down_revision = '2a16083502f3' from alembic import op from sqlalchemy.engine import reflection from neutron.db import migration TABLE_NAME = 'flavorserviceprofilebindings' def upgrade(): inspector = reflection.Inspector.from_engine(op.get_bind()) fk_constraints = inspector.get_foreign_keys(TABLE_NAME) for fk in fk_constraints: fk['options']['ondelete'] = 'CASCADE' migration.remove_foreign_keys(TABLE_NAME, fk_constraints) migration.create_foreign_keys(TABLE_NAME, fk_constraints) ././@LongLink0000000000000000000000000000017000000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/5498d17be016_drop_legacy_ovs_and_lb.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/5498d17be016_drop_l0000664000175000017500000000215313553660047033326 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Drop legacy OVS and LB plugin tables Revision ID: 5498d17be016 Revises: 4ffceebfada Create Date: 2015-06-25 14:08:30.984419 """ # revision identifiers, used by Alembic. revision = '5498d17be016' down_revision = '4ffceebfada' from alembic import op def upgrade(): op.drop_table('ovs_network_bindings') op.drop_table('ovs_vlan_allocations') op.drop_table('network_bindings') op.drop_table('ovs_tunnel_allocations') op.drop_table('network_states') op.drop_table('ovs_tunnel_endpoints') ././@LongLink0000000000000000000000000000017400000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/11926bcfe72d_add_geneve_ml2_type_driver.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/11926bcfe72d_add_ge0000664000175000017500000000361113553660047033406 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add geneve ml2 type driver Revision ID: 11926bcfe72d Revises: 2e5352a0ad4d Create Date: 2015-08-27 19:56:16.356522 """ # revision identifiers, used by Alembic. revision = '11926bcfe72d' down_revision = '2e5352a0ad4d' from alembic import op import sqlalchemy as sa def contract_creation_exceptions(): """These elements were created by mistake in the contract branch.""" return { sa.Table: ['ml2_geneve_allocations', 'ml2_geneve_endpoints'], sa.Index: ['ml2_geneve_allocations'] } def upgrade(): op.create_table( 'ml2_geneve_allocations', sa.Column('geneve_vni', sa.Integer(), autoincrement=False, nullable=False), sa.Column('allocated', sa.Boolean(), server_default=sa.sql.false(), nullable=False), sa.PrimaryKeyConstraint('geneve_vni'), ) op.create_index(op.f('ix_ml2_geneve_allocations_allocated'), 'ml2_geneve_allocations', ['allocated'], unique=False) op.create_table( 'ml2_geneve_endpoints', sa.Column('ip_address', sa.String(length=64), nullable=False), sa.Column('host', sa.String(length=255), nullable=True), sa.PrimaryKeyConstraint('ip_address'), sa.UniqueConstraint('host', name='unique_ml2_geneve_endpoints0host'), ) ././@LongLink0000000000000000000000000000017600000000000011221 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/4af11ca47297_drop_cisco_monolithic_tables.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/contract/4af11ca47297_drop_c0000664000175000017500000000307513553660047033373 0ustar zuulzuul00000000000000# Copyright 2015 Cisco Systems, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Drop cisco monolithic tables Revision ID: 4af11ca47297 Revises: 11926bcfe72d Create Date: 2015-08-13 08:01:19.709839 """ from alembic import op from neutron.db import migration # revision identifiers, used by Alembic. revision = '4af11ca47297' down_revision = '11926bcfe72d' # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.LIBERTY] def upgrade(): op.drop_table('cisco_n1kv_port_bindings') op.drop_table('cisco_n1kv_network_bindings') op.drop_table('cisco_n1kv_multi_segments') op.drop_table('cisco_provider_networks') op.drop_table('cisco_n1kv_trunk_segments') op.drop_table('cisco_n1kv_vmnetworks') op.drop_table('cisco_n1kv_profile_bindings') op.drop_table('cisco_qos_policies') op.drop_table('cisco_credentials') op.drop_table('cisco_n1kv_vlan_allocations') op.drop_table('cisco_n1kv_vxlan_allocations') op.drop_table('cisco_network_profiles') op.drop_table('cisco_policy_profiles') neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/0000775000175000017500000000000013553660156030023 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000020300000000000011210 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/1b4c6e320f79_address_scope_support_in_subnetpool.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/1b4c6e320f79_address_0000664000175000017500000000211613553660047033350 0ustar zuulzuul00000000000000# Copyright 2015 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """address scope support in subnetpool Revision ID: 1b4c6e320f79 Revises: 1c844d1677f7 Create Date: 2015-07-03 09:48:39.491058 """ # revision identifiers, used by Alembic. revision = '1b4c6e320f79' down_revision = '1c844d1677f7' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column('subnetpools', sa.Column('address_scope_id', sa.String(length=36), nullable=True)) ././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/26c371498592_subnetpool_hash.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/26c371498592_subnetpo0000664000175000017500000000176613553660047033230 0ustar zuulzuul00000000000000# Copyright (c) 2015 Thales Services SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """subnetpool hash Revision ID: 26c371498592 Revises: 45f955889773 Create Date: 2015-06-02 21:18:19.942076 """ # revision identifiers, used by Alembic. revision = '26c371498592' down_revision = '45f955889773' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column( 'subnetpools', sa.Column('hash', sa.String(36), nullable=False, server_default='')) ././@LongLink0000000000000000000000000000016100000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/9859ac9c136_quota_reservations.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/9859ac9c136_quota_res0000664000175000017500000000311513553660047033437 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """quota_reservations Revision ID: 9859ac9c136 Revises: 48153cb5f051 Create Date: 2015-03-11 06:40:56.775075 """ # revision identifiers, used by Alembic. revision = '9859ac9c136' down_revision = '48153cb5f051' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'reservations', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('expiration', sa.DateTime(), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'resourcedeltas', sa.Column('resource', sa.String(length=255), nullable=False), sa.Column('reservation_id', sa.String(length=36), nullable=False), sa.Column('amount', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['reservation_id'], ['reservations.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('resource', 'reservation_id')) ././@LongLink0000000000000000000000000000016100000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/354db87e3225_nsxv_vdr_metadata.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/354db87e3225_nsxv_vdr0000664000175000017500000000277213553660047033367 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """nsxv_vdr_metadata.py Revision ID: 354db87e3225 Revises: kilo Create Date: 2015-04-19 14:59:15.102609 """ from alembic import op import sqlalchemy as sa from neutron.db.migration import cli # revision identifiers, used by Alembic. revision = '354db87e3225' down_revision = 'kilo' branch_labels = (cli.EXPAND_BRANCH,) def upgrade(): op.create_table( 'nsxv_vdr_dhcp_bindings', sa.Column('vdr_router_id', sa.String(length=36), nullable=False), sa.Column('dhcp_router_id', sa.String(length=36), nullable=False), sa.Column('dhcp_edge_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('vdr_router_id'), sa.UniqueConstraint( 'dhcp_router_id', name='unique_nsxv_vdr_dhcp_bindings0dhcp_router_id'), sa.UniqueConstraint( 'dhcp_edge_id', name='unique_nsxv_vdr_dhcp_bindings0dhcp_edge_id')) ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/8675309a5c4f_rbac_network.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/8675309a5c4f_rbac_net0000664000175000017500000000316113553660047033273 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """network_rbac Revision ID: 8675309a5c4f Revises: 313373c0ffee Create Date: 2015-06-14 13:12:04.012457 """ # revision identifiers, used by Alembic. revision = '8675309a5c4f' down_revision = '313373c0ffee' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'networkrbacs', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('object_id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('target_tenant', sa.String(length=255), nullable=False), sa.Column('action', sa.String(length=255), nullable=False), sa.ForeignKeyConstraint(['object_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint( 'action', 'object_id', 'target_tenant', name='uniq_networkrbacs0tenant_target0object_id0action')) ././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/599c6a226151_neutrodb_ipam.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/599c6a226151_neutrodb0000664000175000017500000000551113553660047033252 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """neutrodb_ipam Revision ID: 599c6a226151 Revises: 354db87e3225 Create Date: 2015-03-08 18:12:08.962378 """ # revision identifiers, used by Alembic. revision = '599c6a226151' down_revision = '354db87e3225' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'ipamsubnets', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('neutron_subnet_id', sa.String(length=36), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'ipamallocations', sa.Column('ip_address', sa.String(length=64), nullable=False), sa.Column('status', sa.String(length=36), nullable=True), sa.Column('ipam_subnet_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['ipam_subnet_id'], ['ipamsubnets.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('ip_address', 'ipam_subnet_id')) op.create_table( 'ipamallocationpools', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('ipam_subnet_id', sa.String(length=36), nullable=False), sa.Column('first_ip', sa.String(length=64), nullable=False), sa.Column('last_ip', sa.String(length=64), nullable=False), sa.ForeignKeyConstraint(['ipam_subnet_id'], ['ipamsubnets.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id')) op.create_table( 'ipamavailabilityranges', sa.Column('allocation_pool_id', sa.String(length=36), nullable=False), sa.Column('first_ip', sa.String(length=64), nullable=False), sa.Column('last_ip', sa.String(length=64), nullable=False), sa.ForeignKeyConstraint(['allocation_pool_id'], ['ipamallocationpools.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('allocation_pool_id', 'first_ip', 'last_ip'), sa.Index('ix_ipamavailabilityranges_first_ip_allocation_pool_id', 'first_ip', 'allocation_pool_id'), sa.Index('ix_ipamavailabilityranges_last_ip_allocation_pool_id', 'last_ip', 'allocation_pool_id')) ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/48153cb5f051_qos_db_changes.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/48153cb5f051_qos_db_c0000664000175000017500000000461013553660047033251 0ustar zuulzuul00000000000000# Copyright 2015 Huawei Technologies India Pvt Ltd, Inc # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """qos db changes Revision ID: 48153cb5f051 Revises: 1b4c6e320f79 Create Date: 2015-06-24 17:03:34.965101 """ # revision identifiers, used by Alembic. revision = '48153cb5f051' down_revision = '1b4c6e320f79' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'qos_policies', sa.Column('id', sa.String(length=36), primary_key=True), sa.Column('name', sa.String(length=255)), sa.Column('description', sa.String(length=255)), sa.Column('shared', sa.Boolean(), nullable=False), sa.Column('tenant_id', sa.String(length=255), index=True)) op.create_table( 'qos_network_policy_bindings', sa.Column('policy_id', sa.String(length=36), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False), sa.Column('network_id', sa.String(length=36), sa.ForeignKey('networks.id', ondelete='CASCADE'), nullable=False, unique=True)) op.create_table( 'qos_port_policy_bindings', sa.Column('policy_id', sa.String(length=36), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False), sa.Column('port_id', sa.String(length=36), sa.ForeignKey('ports.id', ondelete='CASCADE'), nullable=False, unique=True)) op.create_table( 'qos_bandwidth_limit_rules', sa.Column('id', sa.String(length=36), primary_key=True), sa.Column('qos_policy_id', sa.String(length=36), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False, unique=True), sa.Column('max_kbps', sa.Integer()), sa.Column('max_burst_kbps', sa.Integer())) ././@LongLink0000000000000000000000000000016400000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/34af2b5c5a59_add_dns_name_to_port.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/34af2b5c5a59_add_dns_0000664000175000017500000000231613553660047033377 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add dns_name to Port Revision ID: 34af2b5c5a59 Revises: 9859ac9c136 Create Date: 2015-08-23 00:22:47.618593 """ from alembic import op from neutron_lib.db import constants import sqlalchemy as sa from neutron.db import migration # revision identifiers, used by Alembic. revision = '34af2b5c5a59' down_revision = '9859ac9c136' # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.LIBERTY] def upgrade(): op.add_column('ports', sa.Column('dns_name', sa.String(length=constants.FQDN_FIELD_SIZE), nullable=True)) ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/31337ec0ffee_flavors.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/31337ec0ffee_flavors.0000664000175000017500000000412113553660047033453 0ustar zuulzuul00000000000000# Copyright 2014-2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Flavor framework Revision ID: 313373c0ffee Revises: 52c5312f6baf Create Date: 2014-07-17 03:00:00.00 """ # revision identifiers, used by Alembic. revision = '313373c0ffee' down_revision = '52c5312f6baf' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'flavors', sa.Column('id', sa.String(36)), sa.Column('name', sa.String(255)), sa.Column('description', sa.String(1024)), sa.Column('enabled', sa.Boolean, nullable=False, server_default=sa.sql.true()), sa.Column('service_type', sa.String(36), nullable=True), sa.PrimaryKeyConstraint('id') ) op.create_table( 'serviceprofiles', sa.Column('id', sa.String(36)), sa.Column('description', sa.String(1024)), sa.Column('driver', sa.String(1024), nullable=False), sa.Column('enabled', sa.Boolean, nullable=False, server_default=sa.sql.true()), sa.Column('metainfo', sa.String(4096)), sa.PrimaryKeyConstraint('id') ) op.create_table( 'flavorserviceprofilebindings', sa.Column('service_profile_id', sa.String(36), nullable=False), sa.Column('flavor_id', sa.String(36), nullable=False), sa.ForeignKeyConstraint(['service_profile_id'], ['serviceprofiles.id']), sa.ForeignKeyConstraint(['flavor_id'], ['flavors.id']), sa.PrimaryKeyConstraint('service_profile_id', 'flavor_id') ) ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/45f955889773_quota_usage.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/45f955889773_quota_us0000664000175000017500000000273013553660047033242 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """quota_usage Revision ID: 45f955889773 Revises: 8675309a5c4f Create Date: 2015-04-17 08:09:37.611546 """ # revision identifiers, used by Alembic. revision = '45f955889773' down_revision = '8675309a5c4f' from alembic import op import sqlalchemy as sa from sqlalchemy import sql def upgrade(): op.create_table( 'quotausages', sa.Column('tenant_id', sa.String(length=255), nullable=False, primary_key=True, index=True), sa.Column('resource', sa.String(length=255), nullable=False, primary_key=True, index=True), sa.Column('dirty', sa.Boolean(), nullable=False, server_default=sql.false()), sa.Column('in_use', sa.Integer(), nullable=False, server_default='0'), sa.Column('reserved', sa.Integer(), nullable=False, server_default='0')) ././@LongLink0000000000000000000000000000016500000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/1c844d1677f7_dns_nameservers_order.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/1c844d1677f7_dns_name0000664000175000017500000000204013553660047033276 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add order to dnsnameservers Revision ID: 1c844d1677f7 Revises: 26c371498592 Create Date: 2015-07-21 22:59:03.383850 """ # revision identifiers, used by Alembic. revision = '1c844d1677f7' down_revision = '26c371498592' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column('dnsnameservers', sa.Column('order', sa.Integer(), server_default='0', nullable=False)) ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/52c5312f6baf_address_scopes.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/liberty/expand/52c5312f6baf_address_0000664000175000017500000000226613553660047033430 0ustar zuulzuul00000000000000# Copyright (c) 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Initial operations in support of address scopes """ # revision identifiers, used by Alembic. revision = '52c5312f6baf' down_revision = '599c6a226151' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'address_scopes', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=False), sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('shared', sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint('id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/kilo_initial.py0000664000175000017500000000554613553660047030124 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """kilo_initial Revision ID: kilo Revises: None """ # revision identifiers, used by Alembic. revision = 'kilo' down_revision = None from neutron.db import migration from neutron.db.migration.alembic_migrations import agent_init_ops from neutron.db.migration.alembic_migrations import brocade_init_ops from neutron.db.migration.alembic_migrations import cisco_init_ops from neutron.db.migration.alembic_migrations import core_init_ops from neutron.db.migration.alembic_migrations import dvr_init_opts from neutron.db.migration.alembic_migrations import firewall_init_ops from neutron.db.migration.alembic_migrations import l3_init_ops from neutron.db.migration.alembic_migrations import lb_init_ops from neutron.db.migration.alembic_migrations import loadbalancer_init_ops from neutron.db.migration.alembic_migrations import metering_init_ops from neutron.db.migration.alembic_migrations import ml2_init_ops from neutron.db.migration.alembic_migrations import nec_init_ops from neutron.db.migration.alembic_migrations import nsxv_initial_opts from neutron.db.migration.alembic_migrations import nuage_init_opts from neutron.db.migration.alembic_migrations import other_extensions_init_ops from neutron.db.migration.alembic_migrations import other_plugins_init_ops from neutron.db.migration.alembic_migrations import ovs_init_ops from neutron.db.migration.alembic_migrations import portsec_init_ops from neutron.db.migration.alembic_migrations import secgroup_init_ops from neutron.db.migration.alembic_migrations import vmware_init_ops from neutron.db.migration.alembic_migrations import vpn_init_ops def upgrade(): migration.pk_on_alembic_version_table() agent_init_ops.upgrade() core_init_ops.upgrade() l3_init_ops.upgrade() secgroup_init_ops.upgrade() portsec_init_ops.upgrade() other_extensions_init_ops.upgrade() lb_init_ops.upgrade() ovs_init_ops.upgrade() ml2_init_ops.upgrade() dvr_init_opts.upgrade() firewall_init_ops.upgrade() loadbalancer_init_ops.upgrade() vpn_init_ops.upgrade() metering_init_ops.upgrade() brocade_init_ops.upgrade() cisco_init_ops.upgrade() nec_init_ops.upgrade() other_plugins_init_ops.upgrade() vmware_init_ops.upgrade() nuage_init_opts.upgrade() nsxv_initial_opts.upgrade() neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/queens/0000775000175000017500000000000013553660156026372 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/queens/expand/0000775000175000017500000000000013553660156027651 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/queens/expand/594422d373ee_fip_qos.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/queens/expand/594422d373ee_fip_qos.p0000664000175000017500000000271513553660047033242 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """fip qos Revision ID: 594422d373ee Revises: 7d32f979895f Create Date: 2016-04-26 17:16:10.323756 """ # revision identifiers, used by Alembic. revision = '594422d373ee' down_revision = '7d32f979895f' from alembic import op import sqlalchemy as sa from neutron_lib.db import constants as db_const from neutron.db import migration # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.QUEENS] def upgrade(): op.create_table( 'qos_fip_policy_bindings', sa.Column('policy_id', sa.String(length=db_const.UUID_FIELD_SIZE), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False), sa.Column('fip_id', sa.String(length=db_const.UUID_FIELD_SIZE), sa.ForeignKey('floatingips.id', ondelete='CASCADE'), nullable=False, unique=True)) neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD0000664000175000017500000000001513553660047026550 0ustar zuulzuul00000000000000594422d373ee neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/CONTRACT_HEAD0000664000175000017500000000001513553660046027005 0ustar zuulzuul000000000000005c85685d616d neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/0000775000175000017500000000000013553660156026404 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/0000775000175000017500000000000013553660156030221 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000017100000000000011214 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/7d9d8eeec6ad_rename_tenant_to_project.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/7d9d8eeec6ad_rename_0000664000175000017500000000762513553660047033717 0ustar zuulzuul00000000000000# Copyright 2016 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """rename tenant to project Revision ID: 7d9d8eeec6ad Create Date: 2016-06-29 19:42:17.862721 """ # revision identifiers, used by Alembic. revision = '7d9d8eeec6ad' down_revision = 'a84ccf28f06a' depends_on = ('5abc0278ca73',) from alembic import op import sqlalchemy as sa _INSPECTOR = None def get_inspector(): """Reuse inspector""" global _INSPECTOR if _INSPECTOR: return _INSPECTOR else: bind = op.get_bind() _INSPECTOR = sa.engine.reflection.Inspector.from_engine(bind) return _INSPECTOR def get_tables(): """ Returns hardcoded list of tables which have ``tenant_id`` column. DB head can be changed. To prevent possible problems, when models will be updated, return hardcoded list of tables, up-to-date for this day. Output retrieved by using: >>> metadata = head.get_metadata() >>> all_tables = metadata.sorted_tables >>> tenant_tables = [] >>> for table in all_tables: ... for column in table.columns: ... if column.name == 'tenant_id': ... tenant_tables.append((table, column)) """ tables = [ 'address_scopes', 'floatingips', 'meteringlabels', 'networkrbacs', 'networks', 'ports', 'qos_policies', 'qospolicyrbacs', 'quotas', 'reservations', 'routers', 'securitygrouprules', 'securitygroups', 'subnetpools', 'subnets', 'trunks', 'auto_allocated_topologies', 'default_security_group', 'ha_router_networks', 'quotausages', ] return tables def get_columns(table): """Returns list of columns for given table.""" inspector = get_inspector() return inspector.get_columns(table) def get_data(): """Returns combined list of tuples: [(table, column)]. List is built, based on retrieved tables, where column with name ``tenant_id`` exists. """ output = [] tables = get_tables() for table in tables: columns = get_columns(table) for column in columns: if column['name'] == 'tenant_id': output.append((table, column)) return output def alter_column(table, column): old_name = 'tenant_id' new_name = 'project_id' op.alter_column( table_name=table, column_name=old_name, new_column_name=new_name, existing_type=column['type'], existing_nullable=column['nullable'] ) def recreate_index(index, table_name): old_name = index['name'] new_name = old_name.replace('tenant', 'project') op.drop_index(op.f(old_name), table_name) op.create_index(new_name, table_name, ['project_id']) def upgrade(): inspector = get_inspector() data = get_data() for table, column in data: alter_column(table, column) indexes = inspector.get_indexes(table) for index in indexes: if 'tenant_id' in index['name']: recreate_index(index, table) def contract_creation_exceptions(): """Special migration for the blueprint to support Keystone V3. We drop all tenant_id columns and create project_id columns instead. """ return { sa.Column: ['.'.join([table, 'project_id']) for table in get_tables()], sa.Index: get_tables() } ././@LongLink0000000000000000000000000000020100000000000011206 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/b12a3ef66e62_add_standardattr_to_qos_policies.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/b12a3ef66e62_add_sta0000664000175000017500000000617313553660047033445 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add standardattr to qos policies Revision ID: b12a3ef66e62 Revises: 3b935b28e7a0 Create Date: 2016-08-18 14:10:30.021055 """ # revision identifiers, used by Alembic. revision = 'b12a3ef66e62' down_revision = '3b935b28e7a0' depends_on = ('67daae611b6e',) from alembic import op import sqlalchemy as sa # basic model of the tables with required field for migration TABLE = 'qos_policies' TABLE_MODEL = sa.Table(TABLE, sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('description', sa.String(length=255), nullable=True), sa.Column('standard_attr_id', sa.BigInteger(), nullable=True)) standardattrs = sa.Table( 'standardattributes', sa.MetaData(), sa.Column('id', sa.BigInteger(), primary_key=True, autoincrement=True), sa.Column('resource_type', sa.String(length=255), nullable=False), sa.Column('description', sa.String(length=255), nullable=True)) def upgrade(): generate_records_for_existing() # add the constraint now that everything is populated on that table op.alter_column(TABLE, 'standard_attr_id', nullable=False, existing_type=sa.BigInteger(), existing_nullable=True, existing_server_default=False) op.create_unique_constraint( constraint_name='uniq_%s0standard_attr_id' % TABLE, table_name=TABLE, columns=['standard_attr_id']) op.drop_column(TABLE, 'description') op.create_foreign_key( constraint_name=None, source_table=TABLE, referent_table='standardattributes', local_cols=['standard_attr_id'], remote_cols=['id'], ondelete='CASCADE') def generate_records_for_existing(): session = sa.orm.Session(bind=op.get_bind()) values = [] with session.begin(subtransactions=True): for row in session.query(TABLE_MODEL): # NOTE(kevinbenton): without this disabled, pylint complains # about a missing 'dml' argument. #pylint: disable=no-value-for-parameter res = session.execute( standardattrs.insert().values(resource_type=TABLE, description=row[1]) ) session.execute( TABLE_MODEL.update().values( standard_attr_id=res.inserted_primary_key[0]).where( TABLE_MODEL.c.id == row[0]) ) # this commit is necessary to allow further operations session.commit() return values ././@LongLink0000000000000000000000000000017100000000000011214 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/8fd3918ef6f4_add_segment_host_mapping.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/8fd3918ef6f4_add_seg0000664000175000017500000000346213553660047033457 0ustar zuulzuul00000000000000# Copyright 2016 IBM # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add segment_host_mapping table. Revision ID: 8fd3918ef6f4 Revises: c879c5e1ee90 Create Date: 2016-02-25 00:22:47.618593 """ # revision identifiers, used by Alembic. revision = '8fd3918ef6f4' down_revision = 'c879c5e1ee90' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table('segmenthostmappings', sa.Column('segment_id', sa.String(length=36), index=True, nullable=False), sa.Column('host', sa.String(255), index=True, nullable=False), sa.PrimaryKeyConstraint('segment_id', 'host'), sa.ForeignKeyConstraint(['segment_id'], ['networksegments.id'], ondelete='CASCADE')) def contract_creation_exceptions(): """ Return create exceptions. These elements depend on the networksegments table which was renamed in the contract branch. """ return { sa.Table: ['segmenthostmappings'], sa.Index: ['segmenthostmappings'] } ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/97c25b0d2353_add_name_desc.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/97c25b0d2353_add_nam0000664000175000017500000000723213553660047033266 0ustar zuulzuul00000000000000# Copyright 2016 NEC Technologies Limited # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add Name and Description to the networksegments table """ # revision identifiers, used by Alembic. revision = '97c25b0d2353' down_revision = 'b12a3ef66e62' depends_on = ('89ab9a816d70',) # As this script depends on another migration which was a contract script, # therefore the following column addition ( which should have been in an # expand phase ) is also submitted in the contract phase. For information # about the expand and contract scripts and how the depends_on works, please # refer from alembic import op import sqlalchemy as sa TBL = 'networksegments' TBL_MODEL = sa.Table(TBL, sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('standard_attr_id', sa.BigInteger(), nullable=True)) standardattrs = sa.Table( 'standardattributes', sa.MetaData(), sa.Column('id', sa.BigInteger(), primary_key=True, autoincrement=True), sa.Column('resource_type', sa.String(length=255), nullable=False)) def update_existing_records(): session = sa.orm.Session(bind=op.get_bind()) values = [] with session.begin(subtransactions=True): for row in session.query(TBL_MODEL): # NOTE from kevinbenton: without this disabled, pylint complains # about a missing 'dml' argument. #pylint: disable=no-value-for-parameter res = session.execute( standardattrs.insert().values(resource_type=TBL) ) session.execute( TBL_MODEL.update().values( standard_attr_id=res.inserted_primary_key[0]).where( TBL_MODEL.c.id == row[0]) ) # this commit is necessary to allow further operations session.commit() return values def upgrade(): op.add_column(TBL, sa.Column('standard_attr_id', sa.BigInteger(), nullable=True)) op.add_column(TBL, sa.Column('name', sa.String(255), nullable=True)) update_existing_records() op.alter_column(TBL, 'standard_attr_id', nullable=False, existing_type=sa.BigInteger(), existing_nullable=True, existing_server_default=False) # add the constraint now that everything is populated on that table op.create_foreign_key( constraint_name=None, source_table=TBL, referent_table='standardattributes', local_cols=['standard_attr_id'], remote_cols=['id'], ondelete='CASCADE') op.create_unique_constraint( constraint_name='uniq_%s0standard_attr_id' % TBL, table_name=TBL, columns=['standard_attr_id']) def contract_creation_exceptions(): """ Return create exceptions. These elements depend on the networksegments table which are added in the contract branch. """ return { sa.Column: ['networksegments.name', 'networksegments.standard_attr_id'], } ././@LongLink0000000000000000000000000000020000000000000011205 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/b67e765a3524_remove_mtu_column_from_networks.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/b67e765a3524_remove_0000664000175000017500000000156313553660047033350 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Remove mtu column from networks. Revision ID: b67e765a3524 Revises: 4bcd4df1f426 Create Date: 2016-07-17 02:07:36.625196 """ # revision identifiers, used by Alembic. revision = 'b67e765a3524' down_revision = '4bcd4df1f426' from alembic import op def upgrade(): op.drop_column('networks', 'mtu') ././@LongLink0000000000000000000000000000017300000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/5c85685d616d_remove_availability_ranges.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/5c85685d616d_remove_0000664000175000017500000000162713553660047033361 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Remove availability ranges.""" from alembic import op from neutron.db import migration revision = '5c85685d616d' down_revision = '2e0d7a8a1586' # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.NEWTON] def upgrade(): op.drop_table('ipavailabilityranges') op.drop_table('ipamavailabilityranges') ././@LongLink0000000000000000000000000000017700000000000011222 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/a8b517cff8ab_add_routerport_bindings_for_ha.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/a8b517cff8ab_add_rou0000664000175000017500000000525613553660047033631 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Add routerport bindings for L3 HA Revision ID: a8b517cff8ab Revises: a8b517cff8ab Create Date: 2016-07-18 14:31:45.725516 """ # revision identifiers, used by Alembic. revision = 'a8b517cff8ab' down_revision = '7d9d8eeec6ad' from alembic import op from neutron_lib import constants as lib_const import sqlalchemy as sa from neutron.common import constants HA_AGENT_BINDINGS = 'ha_router_agent_port_bindings' ROUTER_PORTS = 'routerports' def upgrade(): ha_bindings = sa.Table( HA_AGENT_BINDINGS, sa.MetaData(), sa.Column('port_id', sa.String(36)), sa.Column('router_id', sa.String(36)), sa.Column('l3_agent_id', sa.String(36)), sa.Column('state', sa.Enum(constants.HA_ROUTER_STATE_ACTIVE, constants.HA_ROUTER_STATE_STANDBY, name='l3_ha_states')) ) router_ports = sa.Table(ROUTER_PORTS, sa.MetaData(), sa.Column('router_id', sa.String(36)), sa.Column('port_id', sa.String(36)), sa.Column('port_type', sa.String(255))) session = sa.orm.Session(bind=op.get_bind()) with session.begin(subtransactions=True): router_port_tuples = set() for ha_bind in session.query(ha_bindings): router_port_tuples.add((ha_bind.router_id, ha_bind.port_id)) # we have to remove any from the bulk insert that may already exist # as a result of Ifd3e007aaf2a2ed8123275aa3a9f540838e3c003 being # back-ported for router_port in session.query(router_ports).filter( router_ports.c.port_type == lib_const.DEVICE_OWNER_ROUTER_HA_INTF): router_port_tuples.discard((router_port.router_id, router_port.port_id)) new_records = [dict(router_id=router_id, port_id=port_id, port_type=lib_const.DEVICE_OWNER_ROUTER_HA_INTF) for router_id, port_id in router_port_tuples] op.bulk_insert(router_ports, new_records) session.commit() ././@LongLink0000000000000000000000000000017400000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/89ab9a816d70_rename_ml2_network_segments.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/89ab9a816d70_rename_0000664000175000017500000000343413553660047033404 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Rename ml2_network_segments table Revision ID: 89ab9a816d70 Revises: 7bbb25278f53 Create Date: 2016-03-22 00:22:47.618593 """ # revision identifiers, used by Alembic. revision = '89ab9a816d70' down_revision = '7bbb25278f53' from alembic import op from sqlalchemy.engine import reflection TABLE_NAME = 'ml2_port_binding_levels' OLD_REFERRED_TABLE_NAME = 'ml2_network_segments' NEW_REFERRED_TABLE_NAME = 'networksegments' def upgrade(): fk_name = delete_foreign_key_constraint() op.rename_table(OLD_REFERRED_TABLE_NAME, NEW_REFERRED_TABLE_NAME) op.create_foreign_key( constraint_name=fk_name, source_table=TABLE_NAME, referent_table=NEW_REFERRED_TABLE_NAME, local_cols=['segment_id'], remote_cols=['id'], ondelete="SET NULL" ) def delete_foreign_key_constraint(): inspector = reflection.Inspector.from_engine(op.get_bind()) fk_constraints = inspector.get_foreign_keys(TABLE_NAME) for fk in fk_constraints: if fk['referred_table'] == OLD_REFERRED_TABLE_NAME: op.drop_constraint( constraint_name=fk['name'], table_name=TABLE_NAME, type_='foreignkey' ) return fk['name'] ././@LongLink0000000000000000000000000000017300000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/a84ccf28f06a_migrate_dns_name_from_port.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/a84ccf28f06a_migrate0000664000175000017500000000520413553660047033553 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """migrate dns name from port""" # revision identifiers, used by Alembic. revision = 'a84ccf28f06a' down_revision = 'b67e765a3524' depends_on = ('a963b38d82f4',) from alembic import op from neutron_lib.db import constants import sqlalchemy as sa ports = sa.Table( 'ports', sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('dns_name', sa.String(length=constants.FQDN_FIELD_SIZE), nullable=True)) portdnses = sa.Table('portdnses', sa.MetaData(), sa.Column('port_id', sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True, index=True), sa.Column('dns_name', sa.String(length=255), nullable=False), sa.Column('current_dns_name', sa.String(255), nullable=False), sa.Column('current_dns_domain', sa.String(255), nullable=False), sa.Column('previous_dns_name', sa.String(255), nullable=False), sa.Column('previous_dns_domain', sa.String(255), nullable=False)) def migrate_records_for_existing(): session = sa.orm.Session(bind=op.get_bind()) with session.begin(subtransactions=True): for row in session.query(ports): if row[1]: res = session.execute(portdnses.update().values( dns_name=row[1]).where(portdnses.c.port_id == row[0])) if res.rowcount == 0: session.execute(portdnses.insert().values( port_id=row[0], current_dns_name='', current_dns_domain='', previous_dns_name='', previous_dns_domain='', dns_name=row[1])) session.commit() def upgrade(): migrate_records_for_existing() op.drop_column('ports', 'dns_name') ././@LongLink0000000000000000000000000000017600000000000011221 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/7bbb25278f53_device_owner_ha_replicate_int.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/7bbb25278f53_device_0000664000175000017500000000503013553660047033361 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """device_owner_ha_replicate_int Revision ID: 7bbb25278f53 Revises: 4ffceebfcdc Create Date: 2016-03-22 10:00:43.245503 """ # revision identifiers, used by Alembic. revision = '7bbb25278f53' down_revision = '4ffceebfcdc' from alembic import op from neutron_lib import constants import sqlalchemy as sa ROUTER_ATTR_TABLE = 'router_extra_attributes' ROUTER_PORTS_TABLE = 'routerports' PORTS_TABLE = 'ports' def upgrade(): update_device_owner_ha_replicated_interface() def update_device_owner_ha_replicated_interface(): router_attr_table = sa.Table(ROUTER_ATTR_TABLE, sa.MetaData(), sa.Column('router_id', sa.String(36)), sa.Column('ha', sa.Boolean),) routerports = sa.Table(ROUTER_PORTS_TABLE, sa.MetaData(), sa.Column('router_id', sa.String(36)), sa.Column('port_type', sa.String(255))) ports = sa.Table(PORTS_TABLE, sa.MetaData(), sa.Column('device_owner', sa.String(255)), sa.Column('device_id', sa.String(255))) session = sa.orm.Session(bind=op.get_bind()) with session.begin(subtransactions=True): for router_attr in session.query( router_attr_table).filter(router_attr_table.c.ha): session.execute(routerports.update().values( port_type=constants.DEVICE_OWNER_HA_REPLICATED_INT).where( routerports.c.router_id == router_attr.router_id).where( routerports.c.port_type == constants.DEVICE_OWNER_ROUTER_INTF)) session.execute(ports.update().values( device_owner=constants.DEVICE_OWNER_HA_REPLICATED_INT).where( ports.c.device_id == router_attr.router_id).where( ports.c.device_owner == constants.DEVICE_OWNER_ROUTER_INTF)) session.commit() ././@LongLink0000000000000000000000000000021200000000000011210 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/2e0d7a8a1586_add_binding_index_to_routerl3agentbinding.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/2e0d7a8a1586_add_bin0000664000175000017500000000471413553660047033350 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add binding index to RouterL3AgentBinding Revision ID: 2e0d7a8a1586 Revises: 97c25b0d2353 Create Date: 2016-09-01 14:01:57.263289 """ # revision identifiers, used by Alembic. revision = '2e0d7a8a1586' down_revision = '97c25b0d2353' from collections import defaultdict from alembic import op import sqlalchemy as sa ROUTER_L3_AGENT_BINDING = 'routerl3agentbindings' def contract_creation_exceptions(): """Add a new binding_index to ensure that no over-creation of the bindings is possible. """ return { sa.Column: ['%s.binding_index' % ROUTER_L3_AGENT_BINDING] } def upgrade(): op.add_column(ROUTER_L3_AGENT_BINDING, sa.Column('binding_index', sa.Integer(), nullable=False, server_default='1')) bindings_table = sa.Table( ROUTER_L3_AGENT_BINDING, sa.MetaData(), sa.Column('router_id', sa.String(36)), sa.Column('l3_agent_id', sa.String(36)), sa.Column('binding_index', sa.Integer, nullable=False, server_default='1'), ) routers_to_bindings = defaultdict(list) session = sa.orm.Session(bind=op.get_bind()) with session.begin(subtransactions=True): for result in session.query(bindings_table): routers_to_bindings[result.router_id].append(result) for bindings in routers_to_bindings.values(): for index, result in enumerate(bindings): session.execute(bindings_table.update().values( binding_index=index + 1).where( bindings_table.c.router_id == result.router_id).where( bindings_table.c.l3_agent_id == result.l3_agent_id)) session.commit() op.create_unique_constraint( 'uniq_router_l3_agent_binding0router_id0binding_index0', ROUTER_L3_AGENT_BINDING, ['router_id', 'binding_index']) ././@LongLink0000000000000000000000000000017200000000000011215 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/3b935b28e7a0_migrate_to_pluggable_ipam.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/3b935b28e7a0_migrate0000664000175000017500000001372213553660047033416 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """migrate to pluggable ipam """ # revision identifiers, used by Alembic. revision = '3b935b28e7a0' down_revision = 'a8b517cff8ab' from alembic import op from oslo_utils import uuidutils import sqlalchemy as sa from neutron.common import constants as const # A simple models for tables with only the fields needed for the migration. neutron_subnet = sa.Table('subnets', sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False)) ipam_subnet = sa.Table('ipamsubnets', sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('neutron_subnet_id', sa.String(length=36), nullable=True)) ip_allocation_pool = sa.Table('ipallocationpools', sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('subnet_id', sa.String(length=36), sa.ForeignKey('subnets.id', ondelete="CASCADE"), nullable=False), sa.Column('first_ip', sa.String(length=64), nullable=False), sa.Column('last_ip', sa.String(length=64), nullable=False)) ipam_allocation_pool = sa.Table('ipamallocationpools', sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('ipam_subnet_id', sa.String(length=36), sa.ForeignKey('ipamsubnets.id', ondelete="CASCADE"), nullable=False), sa.Column('first_ip', sa.String(length=64), nullable=False), sa.Column('last_ip', sa.String(length=64), nullable=False)) ip_allocation = sa.Table('ipallocations', sa.MetaData(), sa.Column('ip_address', sa.String(length=64), nullable=False), sa.Column('subnet_id', sa.String(length=36), sa.ForeignKey('subnets.id', ondelete="CASCADE"))) ipam_allocation = sa.Table('ipamallocations', sa.MetaData(), sa.Column('ip_address', sa.String(length=64), nullable=False, primary_key=True), sa.Column('ipam_subnet_id', sa.String(length=36), sa.ForeignKey('subnets.id', ondelete="CASCADE"), primary_key=True), sa.Column('status', sa.String(length=36))) def upgrade(): """Migrate data to pluggable ipam reference driver. Tables 'subnets', 'ipallocationpools' and 'ipallocations' are API exposed and always contain up to date data independently from the ipam driver in use, so they can be used as a reliable source of data. This migration cleans up tables for reference ipam driver and rebuilds them from API exposed tables. So this migration will work correctly for both types of users: - Who used build-in ipam implementation; Their ipam data will be migrated to reference ipam driver tables, and reference ipam driver becomes default driver. - Who switched to reference ipam before Newton; Existent reference ipam driver tables are cleaned up and all ipam data is regenerated from API exposed tables. All existent subnets and ports are still usable after upgrade. """ session = sa.orm.Session(bind=op.get_bind()) # Make sure destination tables are clean session.execute(ipam_subnet.delete()) session.execute(ipam_allocation_pool.delete()) session.execute(ipam_allocation.delete()) map_neutron_id_to_ipam = {} subnet_values = [] for subnet_id, in session.query(neutron_subnet): ipam_id = uuidutils.generate_uuid() map_neutron_id_to_ipam[subnet_id] = ipam_id subnet_values.append(dict( id=ipam_id, neutron_subnet_id=subnet_id)) op.bulk_insert(ipam_subnet, subnet_values) ipam_pool_values = [] pools = session.query(ip_allocation_pool) for pool in pools: new_pool_id = uuidutils.generate_uuid() ipam_pool_values.append(dict( id=new_pool_id, ipam_subnet_id=map_neutron_id_to_ipam[pool.subnet_id], first_ip=pool.first_ip, last_ip=pool.last_ip)) op.bulk_insert(ipam_allocation_pool, ipam_pool_values) ipam_allocation_values = [] for ip_alloc in session.query(ip_allocation): ipam_allocation_values.append(dict( ip_address=ip_alloc.ip_address, status=const.IPAM_ALLOCATION_STATUS_ALLOCATED, ipam_subnet_id=map_neutron_id_to_ipam[ip_alloc.subnet_id])) op.bulk_insert(ipam_allocation, ipam_allocation_values) session.commit() ././@LongLink0000000000000000000000000000017500000000000011220 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/4bcd4df1f426_rename_ml2_dvr_port_bindings.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/4bcd4df1f426_rename_0000664000175000017500000000204513553660047033527 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Rename ml2_dvr_port_bindings Revision ID: 4bcd4df1f426 Revises: 8fd3918ef6f4 Create Date: 2016-06-02 14:06:04.112998 """ # revision identifiers, used by Alembic. revision = '4bcd4df1f426' down_revision = '8fd3918ef6f4' from alembic import op OLD_REFERRED_TABLE_NAME = 'ml2_dvr_port_bindings' NEW_REFERRED_TABLE_NAME = 'ml2_distributed_port_bindings' def upgrade(): op.rename_table(OLD_REFERRED_TABLE_NAME, NEW_REFERRED_TABLE_NAME) ././@LongLink0000000000000000000000000000017100000000000011214 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/c879c5e1ee90_add_segment_id_to_subnet.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/contract/c879c5e1ee90_add_seg0000664000175000017500000000260113553660047033443 0ustar zuulzuul00000000000000# Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add segment_id to subnet """ # revision identifiers, used by Alembic. revision = 'c879c5e1ee90' down_revision = '89ab9a816d70' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column('subnets', sa.Column('segment_id', sa.String(length=36), nullable=True)) op.create_foreign_key( None, 'subnets', 'networksegments', ['segment_id'], ['id']) def contract_creation_exceptions(): """The networksegments table was renamed in the contract branch. Because the column being added has a foreign key dependency on a column in a table that was renamed in the contract branch, this column must also be added in the contract branch. """ return { sa.Column: ['subnets.segment_id'] } neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/0000775000175000017500000000000013553660156027663 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000017400000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/5abc0278ca73_add_support_for_vlan_trunking.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/5abc0278ca73_add_suppo0000664000175000017500000000467313553660047033466 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add support for VLAN trunking""" revision = '5abc0278ca73' down_revision = '45f8dd33480b' from alembic import op import sqlalchemy as sa from sqlalchemy import sql def upgrade(): op.create_table('trunks', sa.Column('admin_state_up', sa.Boolean(), nullable=False, server_default=sql.true()), sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('status', sa.String(length=16), nullable=False, server_default='ACTIVE'), sa.Column('standard_attr_id', sa.BigInteger(), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['standard_attr_id'], ['standardattributes.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('port_id'), sa.UniqueConstraint('standard_attr_id') ) op.create_table('subports', sa.Column('port_id', sa.String(length=36)), sa.Column('trunk_id', sa.String(length=36), nullable=False), sa.Column('segmentation_type', sa.String(length=32), nullable=False), sa.Column('segmentation_id', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['trunk_id'], ['trunks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('port_id'), sa.UniqueConstraint('trunk_id', 'segmentation_type', 'segmentation_id', name='uniq_subport0trunk_id0segmentation_type0segmentation_id') ) ././@LongLink0000000000000000000000000000020100000000000011206 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/6b461a21bcfc_uniq_floatingips0floating_network_.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/6b461a21bcfc_uniq_floa0000664000175000017500000000471113553660047033531 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """uniq_floatingips0floating_network_id0fixed_port_id0fixed_ip_addr Revision ID: 6b461a21bcfc Revises: 67daae611b6e Create Date: 2016-06-03 16:00:38.273324 """ # revision identifiers, used by Alembic. revision = '6b461a21bcfc' down_revision = '67daae611b6e' from alembic import op from neutron_lib import exceptions import sqlalchemy as sa from neutron._i18n import _ floatingips = sa.Table( 'floatingips', sa.MetaData(), sa.Column('floating_network_id', sa.String(36)), sa.Column('fixed_port_id', sa.String(36)), sa.Column('fixed_ip_address', sa.String(64))) class DuplicateFloatingIPforOneFixedIP(exceptions.Conflict): message = _("Duplicate Floating IPs were created for fixed IP " "addresse(s) %(fixed_ip_address)s. Database cannot " "be upgraded. Please remove all duplicate Floating " "IPs before upgrading the database.") def upgrade(): op.create_unique_constraint( 'uniq_floatingips0floatingnetworkid0fixedportid0fixedipaddress', 'floatingips', ['floating_network_id', 'fixed_port_id', 'fixed_ip_address']) def check_sanity(connection): res = get_duplicate_floating_ip_for_one_fixed_ip(connection) if res: raise DuplicateFloatingIPforOneFixedIP(fixed_ip_address=",".join(res)) def get_duplicate_floating_ip_for_one_fixed_ip(connection): insp = sa.engine.reflection.Inspector.from_engine(connection) if 'floatingips' not in insp.get_table_names(): return [] session = sa.orm.Session(bind=connection.connect()) query = (session.query(floatingips.c.fixed_ip_address) .group_by(floatingips.c.floating_network_id, floatingips.c.fixed_port_id, floatingips.c.fixed_ip_address) .having(sa.func.count() > 1)).all() return [q[0] for q in query if q[0] is not None] ././@LongLink0000000000000000000000000000017200000000000011215 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/d3435b514502_add_device_id_index_to_port.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/d3435b514502_add_devic0000664000175000017500000000170013553660047033152 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add device_id index to Port Revision ID: d3435b514502 Revises: 5abc0278ca73 Create Date: 2016-04-25 22:13:16.676761 """ # revision identifiers, used by Alembic. revision = 'd3435b514502' down_revision = '5abc0278ca73' from alembic import op def upgrade(): op.create_index('ix_ports_device_id', 'ports', ['device_id'], unique=False) ././@LongLink0000000000000000000000000000016700000000000011221 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/a5648cfeeadf_add_subnet_service_types.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/a5648cfeeadf_add_subne0000664000175000017500000000240213553660047033654 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company, LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add support for Subnet Service Types Revision ID: a5648cfeeadf Revises: 030a959ceafa Create Date: 2016-03-15 18:00:00.190173 """ # revision identifiers, used by Alembic. revision = 'a5648cfeeadf' down_revision = '030a959ceafa' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table('subnet_service_types', sa.Column('subnet_id', sa.String(length=36)), sa.Column('service_type', sa.String(length=255)), sa.ForeignKeyConstraint(['subnet_id'], ['subnets.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('subnet_id', 'service_type') ) ././@LongLink0000000000000000000000000000016300000000000011215 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/c415aab1c048_add_revisions_column.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/c415aab1c048_add_revis0000664000175000017500000000203513553660047033425 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add revisions table Revision ID: c415aab1c048 Revises: 30107ab6a3ee Create Date: 2016-04-11 03:16:24.742290 """ # revision identifiers, used by Alembic. revision = 'c415aab1c048' down_revision = '30107ab6a3ee' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column( 'standardattributes', sa.Column('revision_number', sa.BigInteger(), nullable=False, server_default='0')) ././@LongLink0000000000000000000000000000016700000000000011221 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/3d0e74aa7d37_add_flavor_id_to_routers.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/3d0e74aa7d37_add_flavo0000664000175000017500000000212613553660047033422 0ustar zuulzuul00000000000000# Copyright 2016 Mirantis # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add flavor_id to Router Revision ID: 3d0e74aa7d37 Revises: a963b38d82f4 Create Date: 2016-05-05 00:22:47.618593 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = '3d0e74aa7d37' down_revision = 'a963b38d82f4' def upgrade(): op.add_column('routers', sa.Column('flavor_id', sa.String(length=36), sa.ForeignKey('flavors.id'), nullable=True)) ././@LongLink0000000000000000000000000000020000000000000011205 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/67daae611b6e_add_standard_attr_to_qos_policies.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/67daae611b6e_add_stand0000664000175000017500000000172213553660047033504 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add standardattr to qos policies Revision ID: 67daae611b6e Revises: a5648cfeeadf Create Date: 2016-08-18 14:10:30.021015 """ revision = '67daae611b6e' down_revision = '0f5bef0f87d4' from alembic import op import sqlalchemy as sa TABLE = 'qos_policies' def upgrade(): op.add_column(TABLE, sa.Column('standard_attr_id', sa.BigInteger(), nullable=True)) ././@LongLink0000000000000000000000000000017000000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/5cd92597d11d_add_ip_allocation_to_port.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/5cd92597d11d_add_ip_al0000664000175000017500000000204713553660047033331 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add ip_allocation to port """ from alembic import op import sqlalchemy as sa from neutron.db import migration # revision identifiers, used by Alembic. revision = '5cd92597d11d' down_revision = '6b461a21bcfc' # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.NEWTON] def upgrade(): op.add_column('ports', sa.Column('ip_allocation', sa.String(length=16), nullable=True)) ././@LongLink0000000000000000000000000000016300000000000011215 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/45f8dd33480b_qos_dscp_db_addition.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/45f8dd33480b_qos_dscp_0000664000175000017500000000233113553660047033400 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """qos dscp db addition Revision ID: 45f8dd33480b Revises: 0e66c5227a8a Create Date: 2015-12-03 07:16:24.742290 """ # revision identifiers, used by Alembic. revision = '45f8dd33480b' down_revision = '0e66c5227a8a' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'qos_dscp_marking_rules', sa.Column('id', sa.String(length=36), primary_key=True), sa.Column('qos_policy_id', sa.String(length=36), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False, unique=True), sa.Column('dscp_mark', sa.Integer())) ././@LongLink0000000000000000000000000000017600000000000011221 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/0f5bef0f87d4_add_qos_minimum_bandwidth_rules.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/0f5bef0f87d4_add_qos_m0000664000175000017500000000326013553660047033520 0ustar zuulzuul00000000000000# Copyright 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add_qos_minimum_bandwidth_rules Revision ID: 0f5bef0f87d4 Revises: a5648cfeeadf Create Date: 2016-07-29 14:33:37.243487 """ # revision identifiers, used by Alembic. revision = '0f5bef0f87d4' down_revision = 'a5648cfeeadf' from alembic import op from neutron_lib import constants import sqlalchemy as sa def upgrade(): op.create_table( 'qos_minimum_bandwidth_rules', sa.Column('id', sa.String(length=36), primary_key=True), sa.Column('qos_policy_id', sa.String(length=36), sa.ForeignKey('qos_policies.id', ondelete='CASCADE'), nullable=False, index=True), sa.Column('min_kbps', sa.Integer()), sa.Column('direction', sa.Enum(constants.EGRESS_DIRECTION, constants.INGRESS_DIRECTION, name='directions'), nullable=False, server_default=constants.EGRESS_DIRECTION), sa.UniqueConstraint('qos_policy_id', 'direction', name='qos_minimum_bandwidth_rules0qos_policy_id0direction') ) ././@LongLink0000000000000000000000000000016200000000000011214 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/30107ab6a3ee_provisioning_blocks.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/30107ab6a3ee_provision0000664000175000017500000000233513553660047033521 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """provisioning_blocks.py Revision ID: 30107ab6a3ee Revises: d3435b514502 Create Date: 2016-04-15 05:59:59.000001 """ # revision identifiers, used by Alembic. revision = '30107ab6a3ee' down_revision = 'd3435b514502' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'provisioningblocks', sa.Column('standard_attr_id', sa.BigInteger(), sa.ForeignKey('standardattributes.id', ondelete='CASCADE'), nullable=False, primary_key=True), sa.Column('entity', sa.String(length=255), nullable=False, primary_key=True), ) ././@LongLink0000000000000000000000000000017000000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/a963b38d82f4_add_dns_name_to_portdnses.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/a963b38d82f4_add_dns_n0000664000175000017500000000163213553660047033347 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add dns name to portdnses""" # revision identifiers, used by Alembic. revision = 'a963b38d82f4' down_revision = 'c415aab1c048' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column('portdnses', sa.Column('dns_name', sa.String(length=255), nullable=False)) ././@LongLink0000000000000000000000000000016700000000000011221 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/030a959ceafa_uniq_routerports0port_id.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/newton/expand/030a959ceafa_uniq_rout0000664000175000017500000000421613553660047033607 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """uniq_routerports0port_id Revision ID: 030a959ceafa Revises: 3d0e74aa7d37 Create Date: 2016-06-21 11:33:13.043879 """ # revision identifiers, used by Alembic. revision = '030a959ceafa' down_revision = '3d0e74aa7d37' from alembic import op from neutron_lib import exceptions import sqlalchemy as sa from neutron._i18n import _ routerports = sa.Table( 'routerports', sa.MetaData(), sa.Column('router_id', sa.String(36)), sa.Column('port_id', sa.String(36)), sa.Column('port_type', sa.String(255))) class DuplicatePortRecordinRouterPortdatabase(exceptions.Conflict): message = _("Duplicate port(s) %(port_id)s records exist in routerports " "database. Database cannot be upgraded. Please remove all " "duplicated records before upgrading the database.") def upgrade(): op.create_unique_constraint( 'uniq_routerports0port_id', 'routerports', ['port_id']) def check_sanity(connection): res = get_duplicate_port_records_in_routerport_database(connection) if res: raise DuplicatePortRecordinRouterPortdatabase(port_id=",".join(res)) def get_duplicate_port_records_in_routerport_database(connection): insp = sa.engine.reflection.Inspector.from_engine(connection) if 'routerports' not in insp.get_table_names(): return [] session = sa.orm.Session(bind=connection.connect()) query = (session.query(routerports.c.port_id) .group_by(routerports.c.port_id) .having(sa.func.count() > 1)).all() return [q[0] for q in query] neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/ocata/0000775000175000017500000000000013553660156026161 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/ocata/expand/0000775000175000017500000000000013553660156027440 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016600000000000011220 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/ocata/expand/a9c43481023c_extend_ml2_port_bindings.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/ocata/expand/a9c43481023c_extend_ml20000664000175000017500000000446313553660047033162 0ustar zuulzuul00000000000000# Copyright 2016 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """extend_pk_with_host_and_add_status_to_ml2_port_binding Revision ID: a9c43481023c Revises: 5cd92597d11d Create Date: 2016-11-22 11:48:43.479552 """ # revision identifiers, used by Alembic. revision = 'a9c43481023c' down_revision = '929c968efe70' from alembic import op from neutron_lib import constants import sqlalchemy as sa from sqlalchemy.engine.reflection import Inspector as insp from neutron.db import migration MYSQL_ENGINE = 'mysql' ML2_PORT_BINDING = 'ml2_port_bindings' neutron_milestone = [migration.OCATA] def upgrade(): bind = op.get_bind() engine = bind.engine op.add_column(ML2_PORT_BINDING, sa.Column('status', sa.String(length=16), nullable=False, server_default=constants.ACTIVE)) if (engine.name == MYSQL_ENGINE): op.execute("ALTER TABLE ml2_port_bindings DROP PRIMARY KEY," "ADD PRIMARY KEY(port_id, host);") else: inspector = insp.from_engine(bind) pk_constraint = inspector.get_pk_constraint(ML2_PORT_BINDING) op.drop_constraint(pk_constraint.get('name'), ML2_PORT_BINDING, type_='primary') op.create_primary_key(op.f('pk_ml2_port_bindings'), ML2_PORT_BINDING, ['port_id', 'host']) def expand_drop_exceptions(): """ Drop the existing primary key constraint and then extend it to include host as the primary key to support multiple bindings for the same port. This is needed to use drop in expand migration to pass test_branches. It is safe to recreate primary key in expand as it is backward compatible. """ return { sa.Constraint: ['ml2_port_bindings_pkey'] } ././@LongLink0000000000000000000000000000016200000000000011214 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/ocata/expand/929c968efe70_add_pk_version_table.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/ocata/expand/929c968efe70_add_pk_ver0000664000175000017500000000164013553660047033325 0ustar zuulzuul00000000000000# Copyright 2017 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add_pk_version_table Revision ID: 929c968efe70 Revises: 5cd92597d11d Create Date: 2017-01-12 07:17:33.677770 """ # revision identifiers, used by Alembic. revision = '929c968efe70' down_revision = '5cd92597d11d' from neutron.db import migration def upgrade(): migration.pk_on_alembic_version_table() neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/0000775000175000017500000000000013553660156026340 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/0000775000175000017500000000000013553660156030155 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/4ffceebfcdc_standard_desc.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/4ffceebfcdc_standard0000664000175000017500000000422613553660047034101 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """standard_desc Revision ID: 4ffceebfcdc Revises: 5ffceebfada Create Date: 2016-02-10 23:12:04.012457 """ from alembic import op import sqlalchemy as sa from neutron.db import migration # revision identifiers, used by Alembic. revision = '4ffceebfcdc' down_revision = '5ffceebfada' depends_on = ('0e66c5227a8a',) neutron_milestone = [migration.MITAKA] # A simple model of the security groups table with only the fields needed for # the migration. securitygroups = sa.Table('securitygroups', sa.MetaData(), sa.Column('standard_attr_id', sa.BigInteger(), nullable=False), sa.Column('description', sa.String(length=255))) standardattr = sa.Table( 'standardattributes', sa.MetaData(), sa.Column('id', sa.BigInteger(), primary_key=True, autoincrement=True), sa.Column('description', sa.String(length=255))) def upgrade(): migrate_values() op.drop_column('securitygroups', 'description') def migrate_values(): session = sa.orm.Session(bind=op.get_bind()) values = [] for row in session.query(securitygroups): values.append({'id': row[0], 'description': row[1]}) with session.begin(subtransactions=True): for value in values: session.execute( standardattr.update().values( description=value['description']).where( standardattr.c.id == value['id'])) # this commit appears to be necessary to allow further operations session.commit() ././@LongLink0000000000000000000000000000017200000000000011215 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/2b4c2465d44b_dvr_sheduling_refactoring.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/2b4c2465d44b_dvr_she0000664000175000017500000000534213553660047033346 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """DVR sheduling refactoring Revision ID: 2b4c2465d44b Revises: 8a6d8bdae39 Create Date: 2015-12-23 07:39:49.062767 """ # revision identifiers, used by Alembic. revision = '2b4c2465d44b' down_revision = '8a6d8bdae39' from alembic import op import sqlalchemy as sa ROUTER_ATTR_TABLE = 'router_extra_attributes' ROUTER_BINDING_TABLE = 'routerl3agentbindings' CSNAT_BINDING_TABLE = 'csnat_l3_agent_bindings' def upgrade(): transfer_snat_bindings() op.drop_table(CSNAT_BINDING_TABLE) def transfer_snat_bindings(): router_attr_table = sa.Table(ROUTER_ATTR_TABLE, sa.MetaData(), sa.Column('router_id', sa.String(36)), sa.Column('distributed', sa.Boolean),) csnat_binding = sa.Table(CSNAT_BINDING_TABLE, sa.MetaData(), sa.Column('router_id', sa.String(36)), sa.Column('l3_agent_id', sa.String(36))) router_binding = sa.Table(ROUTER_BINDING_TABLE, sa.MetaData(), sa.Column('router_id', sa.String(36)), sa.Column('l3_agent_id', sa.String(36))) session = sa.orm.Session(bind=op.get_bind()) with session.begin(subtransactions=True): # first delete all bindings for dvr routers from # routerl3agentbindings as this might be bindings with l3 agents # on compute nodes for router_attr in session.query( router_attr_table).filter(router_attr_table.c.distributed): session.execute(router_binding.delete( router_binding.c.router_id == router_attr.router_id)) # now routerl3agentbindings will only contain bindings for snat # portion of the router for csnat_binding in session.query(csnat_binding): session.execute( router_binding.insert().values( router_id=csnat_binding.router_id, l3_agent_id=csnat_binding.l3_agent_id)) # this commit is necessary to allow further operations session.commit() ././@LongLink0000000000000000000000000000016500000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/5ffceebfada_rbac_network_external.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/5ffceebfada_rbac_net0000664000175000017500000000461013553660047034050 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """network_rbac_external Revision ID: 5ffceebfada Revises: c6c112992c9 Create Date: 2015-06-14 13:12:04.012457 """ # revision identifiers, used by Alembic. revision = '5ffceebfada' down_revision = 'c6c112992c9' depends_on = () from alembic import op from oslo_utils import uuidutils import sqlalchemy as sa # A simple model of the external network table with only the fields needed for # the migration. external = sa.Table('externalnetworks', sa.MetaData(), sa.Column('network_id', sa.String(length=36), nullable=False)) network = sa.Table('networks', sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255))) networkrbacs = sa.Table( 'networkrbacs', sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('object_id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('target_tenant', sa.String(length=255), nullable=False), sa.Column('action', sa.String(length=255), nullable=False)) def upgrade(): op.bulk_insert(networkrbacs, get_values()) def get_values(): session = sa.orm.Session(bind=op.get_bind()) values = [] net_to_tenant_id = {} for row in session.query(network).all(): net_to_tenant_id[row[0]] = row[1] for row in session.query(external).all(): values.append({'id': uuidutils.generate_uuid(), 'object_id': row[0], 'tenant_id': net_to_tenant_id[row[0]], 'target_tenant': '*', 'action': 'access_as_external'}) # this commit appears to be necessary to allow further operations session.commit() return values ././@LongLink0000000000000000000000000000017700000000000011222 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/8a6d8bdae39_migrate_neutron_resources_table.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/8a6d8bdae39_migrate_0000664000175000017500000000643013553660047033573 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """standardattributes migration Revision ID: 8a6d8bdae39 Revises: 1b294093239c Create Date: 2015-09-10 03:12:04.012457 """ # revision identifiers, used by Alembic. revision = '8a6d8bdae39' down_revision = '1b294093239c' depends_on = ('32e5974ada25',) from alembic import op import sqlalchemy as sa # basic model of the tables with required field for migration TABLES = ('ports', 'networks', 'subnets', 'subnetpools', 'securitygroups', 'floatingips', 'routers', 'securitygrouprules') TABLE_MODELS = [ (table, sa.Table(table, sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('standard_attr_id', sa.BigInteger(), nullable=True))) for table in TABLES ] standardattrs = sa.Table( 'standardattributes', sa.MetaData(), sa.Column('id', sa.BigInteger(), primary_key=True, autoincrement=True), sa.Column('resource_type', sa.String(length=255), nullable=False)) def upgrade(): generate_records_for_existing() for table, model in TABLE_MODELS: # add constraint(s) now that everything is populated on that table. # note that some MariaDB versions will *not* allow the ALTER to # NOT NULL on a column that has an FK constraint, so we set NOT NULL # first, then the FK constraint. op.alter_column(table, 'standard_attr_id', nullable=False, existing_type=sa.BigInteger(), existing_nullable=True, existing_server_default=False) op.create_foreign_key( constraint_name=None, source_table=table, referent_table='standardattributes', local_cols=['standard_attr_id'], remote_cols=['id'], ondelete='CASCADE') op.create_unique_constraint( constraint_name='uniq_%s0standard_attr_id' % table, table_name=table, columns=['standard_attr_id']) def generate_records_for_existing(): session = sa.orm.Session(bind=op.get_bind()) values = [] with session.begin(subtransactions=True): for table, model in TABLE_MODELS: for row in session.query(model): # NOTE(kevinbenton): without this disabled, pylint complains # about a missing 'dml' argument. #pylint: disable=no-value-for-parameter res = session.execute( standardattrs.insert().values(resource_type=table)) session.execute( model.update().values( standard_attr_id=res.inserted_primary_key[0]).where( model.c.id == row[0])) # this commit is necessary to allow further operations session.commit() return values ././@LongLink0000000000000000000000000000016600000000000011220 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/1b294093239c_remove_embrane_plugin.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/1b294093239c_remove_0000664000175000017500000000155513553660047033217 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Drop embrane plugin table Revision ID: 1b294093239c Revises: 4af11ca47297 Create Date: 2015-10-09 14:07:59.968597 """ # revision identifiers, used by Alembic. revision = '1b294093239c' down_revision = '4af11ca47297' from alembic import op def upgrade(): op.drop_table('embrane_pool_port') ././@LongLink0000000000000000000000000000016700000000000011221 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/e3278ee65050_drop_nec_plugin_tables.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/e3278ee65050_drop_ne0000664000175000017500000000222413553660047033267 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Drop NEC plugin tables Revision ID: e3278ee65050 Revises: 2b4c2465d44b Create Date: 2016-02-15 18:50:56.870043 """ # revision identifiers, used by Alembic. revision = 'e3278ee65050' down_revision = '2b4c2465d44b' from alembic import op def upgrade(): op.drop_table('ofcnetworkmappings') op.drop_table('ofcportmappings') op.drop_table('ofcroutermappings') op.drop_table('ofcfiltermappings') op.drop_table('ofctenantmappings') op.drop_table('portinfos') op.drop_table('routerproviders') op.drop_table('packetfilters') ././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/c6c112992c9_rbac_qos_policy.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/contract/c6c112992c9_rbac_qos0000664000175000017500000000437413553660047033357 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """rbac_qos_policy Revision ID: c6c112992c9 Revises: 8a6d8bdae39 Create Date: 2015-11-25 18:45:03.831359 """ from alembic import op from oslo_utils import uuidutils import sqlalchemy as sa from neutron.db import rbac_db_models # revision identifiers, used by Alembic. revision = 'c6c112992c9' down_revision = 'e3278ee65050' depends_on = ('15e43b934f81',) qos_rbacs = sa.Table( 'qospolicyrbacs', sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('target_tenant', sa.String(length=255), nullable=False), sa.Column('action', sa.String(length=255), nullable=False), sa.Column('object_id', sa.String(length=36), nullable=False)) # A simple model of the qos_policies table with only the fields needed for # the migration. qos_policy = sa.Table('qos_policies', sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255)), sa.Column('shared', sa.Boolean(), nullable=False)) def upgrade(): op.bulk_insert(qos_rbacs, get_values()) op.drop_column('qos_policies', 'shared') def get_values(): session = sa.orm.Session(bind=op.get_bind()) values = [] for row in session.query(qos_policy).filter(qos_policy.c.shared).all(): values.append({'id': uuidutils.generate_uuid(), 'object_id': row[0], 'tenant_id': row[1], 'target_tenant': '*', 'action': rbac_db_models.ACCESS_SHARED}) session.commit() return values neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/0000775000175000017500000000000013553660156027617 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000017500000000000011220 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/c3a73f615e4_add_ip_version_to_address_scope.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/c3a73f615e4_add_ip_ver0000664000175000017500000000172013553660047033370 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add ip_version to AddressScope Revision ID: c3a73f615e4 Revises: 13cfb89f881a Create Date: 2015-10-08 17:34:32.231256 """ # revision identifiers, used by Alembic. revision = 'c3a73f615e4' down_revision = 'dce3ec7a25c9' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column('address_scopes', sa.Column('ip_version', sa.Integer(), nullable=False)) ././@LongLink0000000000000000000000000000016600000000000011220 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/19f26505c74f_auto_allocated_topology.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/19f26505c74f_auto_allo0000664000175000017500000000320513553660047033265 0ustar zuulzuul00000000000000# Copyright 2015-2016 Hewlett Packard Enterprise Development Company, LP # # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """ Auto Allocated Topology - aka Get-Me-A-Network Revision ID: 19f26505c74f Revises: 1df244e556f5 Create Date: 2015-11-20 11:27:53.419742 """ from alembic import op import sqlalchemy as sa from sqlalchemy import sql # revision identifiers, used by Alembic. revision = '19f26505c74f' down_revision = '1df244e556f5' def upgrade(): op.create_table( 'auto_allocated_topologies', sa.Column('tenant_id', sa.String(length=255), primary_key=True), sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('router_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='SET NULL'), ) op.add_column('externalnetworks', sa.Column('is_default', sa.Boolean(), nullable=False, server_default=sql.false())) ././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/ec7fcfbf72ee_network_az.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/ec7fcfbf72ee_network_a0000664000175000017500000000172113553660047033743 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add network availability zone Revision ID: ec7fcfbf72ee Revises: 32e5974ada25 Create Date: 2015-09-17 09:21:51.257579 """ # revision identifiers, used by Alembic. revision = 'ec7fcfbf72ee' down_revision = '32e5974ada25' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column('networks', sa.Column('availability_zone_hints', sa.String(length=255))) ././@LongLink0000000000000000000000000000017000000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/b4caf27aae4_add_bgp_dragent_model_data.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/b4caf27aae4_add_bgp_dr0000664000175000017500000000264113553660047033554 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add_bgp_dragent_model_data Revision ID: b4caf27aae4 Revises: 15be7321482 Create Date: 2015-08-20 17:05:31.038704 """ # revision identifiers, used by Alembic. revision = 'b4caf27aae4' down_revision = '15be73214821' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'bgp_speaker_dragent_bindings', sa.Column('agent_id', sa.String(length=36), primary_key=True), sa.Column('bgp_speaker_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['agent_id'], ['agents.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['bgp_speaker_id'], ['bgp_speakers.id'], ondelete='CASCADE'), ) ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/15e43b934f81_rbac_qos_policy.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/15e43b934f81_rbac_qos_0000664000175000017500000000366213553660047033242 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """rbac_qos_policy Revision ID: 15e43b934f81 Revises: 1df244e556f5 Create Date: 2015-11-25 18:45:03.819115 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = '15e43b934f81' down_revision = 'b4caf27aae4' def upgrade(): op.create_table('qospolicyrbacs', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('target_tenant', sa.String(length=255), nullable=False), sa.Column('action', sa.String(length=255), nullable=False), sa.Column('object_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['object_id'], ['qos_policies.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('target_tenant', 'object_id', 'action')) op.create_index(op.f('ix_qospolicyrbacs_tenant_id'), 'qospolicyrbacs', ['tenant_id'], unique=False) ././@LongLink0000000000000000000000000000020700000000000011214 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/1df244e556f5_add_unique_ha_router_agent_port_bindings.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/1df244e556f5_add_uniqu0000664000175000017500000000462313553660047033343 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add_unique_ha_router_agent_port_bindings Revision ID: 1df244e556f5 Revises: 34af2b5c5a59 Create Date: 2015-10-02 18:06:01.696742 """ # revision identifiers, used by Alembic. revision = '1df244e556f5' down_revision = '659bf3d90664' from alembic import op from neutron_lib import exceptions import sqlalchemy as sa from neutron._i18n import _ UNIQUE_NAME = 'uniq_ha_router_agent_port_bindings0port_id0l3_agent_id' TABLE_NAME = 'ha_router_agent_port_bindings' ha_router_agent_port_bindings = sa.Table( 'ha_router_agent_port_bindings', sa.MetaData(), sa.Column('port_id', sa.String(36)), sa.Column('router_id', sa.String(36)), sa.Column('l3_agent_id', sa.String(36))) class DuplicateL3HARouterAgentPortBinding(exceptions.Conflict): message = _("Duplicate L3HARouterAgentPortBinding is created for " "router(s) %(router)s. Database cannot be upgraded. Please, " "remove all duplicates before upgrading the database.") def upgrade(): op.create_unique_constraint(UNIQUE_NAME, TABLE_NAME, ['router_id', 'l3_agent_id']) def check_sanity(connection): res = get_duplicate_l3_ha_port_bindings(connection) if res: raise DuplicateL3HARouterAgentPortBinding(router=", ".join(res)) def get_duplicate_l3_ha_port_bindings(connection): insp = sa.engine.reflection.Inspector.from_engine(connection) if 'ha_router_agent_port_bindings' not in insp.get_table_names(): return {} session = sa.orm.Session(bind=connection.connect()) query = (session.query(ha_router_agent_port_bindings.c.router_id) .group_by(ha_router_agent_port_bindings.c.router_id, ha_router_agent_port_bindings.c.l3_agent_id) .having(sa.func.count() > 1)).all() return [q[0] for q in query] ././@LongLink0000000000000000000000000000020700000000000011214 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/31ed664953e6_add_resource_versions_row_to_agent_table.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/31ed664953e6_add_resou0000664000175000017500000000177213553660047033265 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add resource_versions row to agent table Revision ID: 31ed664953e6 Revises: c3a73f615e4 Create Date: 2016-01-15 13:41:30.016915 """ # revision identifiers, used by Alembic. revision = '31ed664953e6' down_revision = '15e43b934f81' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column('agents', sa.Column('resource_versions', sa.String(length=8191))) ././@LongLink0000000000000000000000000000017000000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/0e66c5227a8a_add_desc_to_standard_attr.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/0e66c5227a8a_add_desc_0000664000175000017500000000212113553660047033237 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add desc to standard attr table Revision ID: 0e66c5227a8a Revises: 3894bccad37f Create Date: 2016-02-02 10:50:34.238563 """ from alembic import op import sqlalchemy as sa from neutron.db import migration # revision identifiers, used by Alembic. revision = '0e66c5227a8a' down_revision = '3894bccad37f' neutron_milestone = [migration.MITAKA] def upgrade(): op.add_column('standardattributes', sa.Column('description', sa.String(length=255), nullable=True)) ././@LongLink0000000000000000000000000000022100000000000011210 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/659bf3d90664_add_attributes_to_support_external_dns_integration.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/659bf3d90664_add_attri0000664000175000017500000000745613553660047033263 0ustar zuulzuul00000000000000# Copyright 2016 IBM # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add tables and attributes to support external DNS integration Revision ID: 659bf3d90664 Revises: c3a73f615e4 Create Date: 2015-09-11 00:22:47.618593 """ # revision identifiers, used by Alembic. revision = '659bf3d90664' down_revision = 'c3a73f615e4' from alembic import op from neutron_lib.db import constants import sqlalchemy as sa def upgrade(): op.create_table('networkdnsdomains', sa.Column('network_id', sa.String(length=36), nullable=False, index=True), sa.Column('dns_domain', sa.String( length=constants.FQDN_FIELD_SIZE), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id')) op.create_table('floatingipdnses', sa.Column('floatingip_id', sa.String(length=36), nullable=False, index=True), sa.Column('dns_name', sa.String( length=constants.FQDN_FIELD_SIZE), nullable=False), sa.Column('dns_domain', sa.String( length=constants.FQDN_FIELD_SIZE), nullable=False), sa.Column('published_dns_name', sa.String(length=constants.FQDN_FIELD_SIZE), nullable=False), sa.Column('published_dns_domain', sa.String(length=constants.FQDN_FIELD_SIZE), nullable=False), sa.ForeignKeyConstraint(['floatingip_id'], ['floatingips.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('floatingip_id')) op.create_table('portdnses', sa.Column('port_id', sa.String(length=36), nullable=False, index=True), sa.Column('current_dns_name', sa.String(length=constants.FQDN_FIELD_SIZE), nullable=False), sa.Column('current_dns_domain', sa.String(length=constants.FQDN_FIELD_SIZE), nullable=False), sa.Column('previous_dns_name', sa.String(length=constants.FQDN_FIELD_SIZE), nullable=False), sa.Column('previous_dns_domain', sa.String(length=constants.FQDN_FIELD_SIZE), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('port_id')) ././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/59cb5b6cf4d_availability_zone.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/59cb5b6cf4d_availabili0000664000175000017500000000167713553660047033551 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add availability zone Revision ID: 59cb5b6cf4d Revises: 34af2b5c5a59 Create Date: 2015-01-20 14:38:47.156574 """ # revision identifiers, used by Alembic. revision = '59cb5b6cf4d' down_revision = '34af2b5c5a59' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column('agents', sa.Column('availability_zone', sa.String(length=255))) ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/dce3ec7a25c9_router_az.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/dce3ec7a25c9_router_az0000664000175000017500000000173713553660047033624 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add router availability zone Revision ID: dce3ec7a25c9 Revises: ec7fcfbf72ee Create Date: 2015-09-17 09:36:17.468901 """ # revision identifiers, used by Alembic. revision = 'dce3ec7a25c9' down_revision = 'ec7fcfbf72ee' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column('router_extra_attributes', sa.Column('availability_zone_hints', sa.String(length=255))) ././@LongLink0000000000000000000000000000017200000000000011215 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/32e5974ada25_add_neutron_resources_table.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/32e5974ada25_add_neutr0000664000175000017500000000255613553660047033335 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add standard attribute table Revision ID: 32e5974ada25 Revises: 13cfb89f881a Create Date: 2015-09-10 00:22:47.618593 """ # revision identifiers, used by Alembic. revision = '32e5974ada25' down_revision = '13cfb89f881a' from alembic import op import sqlalchemy as sa TABLES = ('ports', 'networks', 'subnets', 'subnetpools', 'securitygroups', 'floatingips', 'routers', 'securitygrouprules') def upgrade(): op.create_table( 'standardattributes', sa.Column('id', sa.BigInteger(), autoincrement=True), sa.Column('resource_type', sa.String(length=255), nullable=False), sa.PrimaryKeyConstraint('id') ) for table in TABLES: op.add_column(table, sa.Column('standard_attr_id', sa.BigInteger(), nullable=True)) ././@LongLink0000000000000000000000000000017600000000000011221 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/3894bccad37f_add_timestamp_to_base_resources.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/3894bccad37f_add_times0000664000175000017500000000210513553660047033454 0ustar zuulzuul00000000000000# Copyright 2015 HuaWei Technologies. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add_timestamp_to_base_resources Revision ID: 3894bccad37f Revises: 2f9e956e7532 Create Date: 2016-03-01 04:19:58.852612 """ # revision identifiers, used by Alembic. revision = '3894bccad37f' down_revision = '2f9e956e7532' from alembic import op import sqlalchemy as sa def upgrade(): for column_name in ['created_at', 'updated_at']: op.add_column( 'standardattributes', sa.Column(column_name, sa.DateTime(), nullable=True) ) ././@LongLink0000000000000000000000000000017300000000000011216 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/13cfb89f881a_add_is_default_to_subnetpool.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/13cfb89f881a_add_is_de0000664000175000017500000000216513553660047033347 0ustar zuulzuul00000000000000# Copyright 2015 Cisco Systems # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add is_default to subnetpool Revision ID: 13cfb89f881a Revises: 59cb5b6cf4d Create Date: 2015-09-30 15:58:31.170153 """ # revision identifiers, used by Alembic. revision = '13cfb89f881a' down_revision = '59cb5b6cf4d' from alembic import op import sqlalchemy as sa from sqlalchemy import sql def upgrade(): op.add_column('subnetpools', sa.Column('is_default', sa.Boolean(), server_default=sql.false(), nullable=False)) ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/2f9e956e7532_tag_support.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/2f9e956e7532_tag_suppo0000664000175000017500000000223213553660047033320 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """tag support Revision ID: 2f9e956e7532 Revises: 31ed664953e6 Create Date: 2016-01-21 08:11:49.604182 """ # revision identifiers, used by Alembic. revision = '2f9e956e7532' down_revision = '31ed664953e6' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'tags', sa.Column('standard_attr_id', sa.BigInteger(), sa.ForeignKey('standardattributes.id', ondelete='CASCADE'), nullable=False, primary_key=True), sa.Column('tag', sa.String(length=60), nullable=False, primary_key=True) ) ././@LongLink0000000000000000000000000000016100000000000011213 Lustar 00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/15be73214821_add_bgp_model_data.pyneutron-12.1.1/neutron/db/migration/alembic_migrations/versions/mitaka/expand/15be73214821_add_bgp_m0000664000175000017500000000730513553660047033112 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add dynamic routing model data Revision ID: 15be73214821 Create Date: 2015-07-29 13:16:08.604175 """ # revision identifiers, used by Alembic. revision = '15be73214821' down_revision = '19f26505c74f' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'bgp_speakers', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=False), sa.Column('local_as', sa.Integer, nullable=False, autoincrement=False), sa.Column('ip_version', sa.Integer, nullable=False, autoincrement=False), sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('advertise_floating_ip_host_routes', sa.Boolean(), nullable=False), sa.Column('advertise_tenant_networks', sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint('id') ) op.create_table( 'bgp_peers', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=False), sa.Column('auth_type', sa.String(length=16), nullable=False), sa.Column('password', sa.String(length=255), nullable=True), sa.Column('peer_ip', sa.String(length=64), nullable=False), sa.Column('remote_as', sa.Integer, nullable=False, autoincrement=False), sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.PrimaryKeyConstraint('id') ) op.create_table( 'bgp_speaker_network_bindings', sa.Column('bgp_speaker_id', sa.String(length=36), nullable=False), sa.Column('network_id', sa.String(length=36), nullable=True), sa.Column('ip_version', sa.Integer, nullable=False, autoincrement=False), sa.ForeignKeyConstraint(['bgp_speaker_id'], ['bgp_speakers.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id', 'bgp_speaker_id', 'ip_version') ) op.create_table( 'bgp_speaker_peer_bindings', sa.Column('bgp_speaker_id', sa.String(length=36), nullable=False), sa.Column('bgp_peer_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['bgp_speaker_id'], ['bgp_speakers.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['bgp_peer_id'], ['bgp_peers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('bgp_speaker_id', 'bgp_peer_id') ) neutron-12.1.1/neutron/db/migration/alembic_migrations/versions/README0000664000175000017500000000024313553660046025747 0ustar zuulzuul00000000000000This directory contains the migration scripts for the Neutron project. Please see the README in neutron/db/migration on how to use and generate new migrations. neutron-12.1.1/neutron/db/migration/alembic_migrations/other_extensions_init_ops.py0000664000175000017500000001043513553660046031101 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for extensions: # allowedaddresspairs # extradhcpopts # portbindings # quotas # routedserviceinsertion # servicetype from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'providerresourceassociations', sa.Column('provider_name', sa.String(length=255), nullable=False), sa.Column('resource_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('provider_name', 'resource_id'), sa.UniqueConstraint('resource_id')) op.create_table( 'quotas', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('resource', sa.String(length=255), nullable=True), sa.Column('limit', sa.Integer(), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'allowedaddresspairs', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('mac_address', sa.String(length=32), nullable=False), sa.Column('ip_address', sa.String(length=64), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('port_id', 'mac_address', 'ip_address')) op.create_table( 'portbindingports', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('host', sa.String(length=255), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('port_id')) op.create_table( 'extradhcpopts', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('opt_name', sa.String(length=64), nullable=False), sa.Column('opt_value', sa.String(length=255), nullable=False), sa.Column('ip_version', sa.Integer(), server_default='4', nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint( 'port_id', 'opt_name', 'ip_version', name='uniq_extradhcpopts0portid0optname0ipversion')) op.create_table('subnetpools', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('ip_version', sa.Integer(), nullable=False), sa.Column('default_prefixlen', sa.Integer(), nullable=False), sa.Column('min_prefixlen', sa.Integer(), nullable=False), sa.Column('max_prefixlen', sa.Integer(), nullable=False), sa.Column('shared', sa.Boolean(), nullable=False), sa.Column('default_quota', sa.Integer(), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table('subnetpoolprefixes', sa.Column('cidr', sa.String(length=64), nullable=False), sa.Column('subnetpool_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['subnetpool_id'], ['subnetpools.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('cidr', 'subnetpool_id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/ovs_init_ops.py0000664000175000017500000000414213553660046026306 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for the OVS plugin from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'ovs_tunnel_endpoints', sa.Column('ip_address', sa.String(length=64), nullable=False), sa.Column('id', sa.Integer(), nullable=False), sa.PrimaryKeyConstraint('ip_address'), sa.UniqueConstraint('id', name='uniq_ovs_tunnel_endpoints0id')) op.create_table( 'ovs_tunnel_allocations', sa.Column('tunnel_id', sa.Integer(), autoincrement=False, nullable=False), sa.Column('allocated', sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint('tunnel_id')) op.create_table( 'ovs_vlan_allocations', sa.Column('physical_network', sa.String(length=64), nullable=False), sa.Column('vlan_id', sa.Integer(), autoincrement=False, nullable=False), sa.Column('allocated', sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint('physical_network', 'vlan_id')) op.create_table( 'ovs_network_bindings', sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('network_type', sa.String(length=32), nullable=False), sa.Column('physical_network', sa.String(length=64), nullable=True), sa.Column('segmentation_id', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/external.py0000664000175000017500000001002713553660047025415 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # These tables are in the neutron database, but their models have moved # to separate repositories. We skip the migration checks for these tables. VPNAAS_TABLES = ['vpnservices', 'ipsecpolicies', 'ipsecpeercidrs', 'ipsec_site_connections', 'cisco_csr_identifier_map', 'ikepolicies'] LBAAS_TABLES = ['vips', 'sessionpersistences', 'pools', 'healthmonitors', 'poolstatisticss', 'members', 'poolloadbalanceragentbindings', 'poolmonitorassociations'] FWAAS_TABLES = ['firewall_rules', 'firewalls', 'firewall_policies'] # Arista ML2 driver Models moved to openstack/networking-arista REPO_ARISTA_TABLES = [ 'arista_provisioned_nets', 'arista_provisioned_vms', 'arista_provisioned_tenants', ] # BGP models in openstack/neutron-dynamic-routing REPO_NEUTRON_DYNAMIC_ROUTING_TABLES = [ 'bgp_speakers', 'bgp_peers', 'bgp_speaker_network_bindings', 'bgp_speaker_peer_bindings', 'bgp_speaker_dragent_bindings', ] # Models moved to openstack/networking-cisco REPO_CISCO_TABLES = [ 'cisco_ml2_apic_contracts', 'cisco_ml2_apic_names', 'cisco_ml2_apic_host_links', 'cisco_ml2_n1kv_policy_profiles', 'cisco_ml2_n1kv_network_profiles', 'cisco_ml2_n1kv_port_bindings', 'cisco_ml2_n1kv_network_bindings', 'cisco_ml2_n1kv_vxlan_allocations', 'cisco_ml2_n1kv_vlan_allocations', 'cisco_ml2_n1kv_profile_bindings', 'cisco_ml2_nexusport_bindings', 'cisco_ml2_nexus_nve', 'ml2_nexus_vxlan_allocations', 'ml2_nexus_vxlan_mcast_groups', 'ml2_ucsm_port_profiles', 'cisco_hosting_devices', 'cisco_port_mappings', 'cisco_router_mappings', ] # VMware-NSX models moved to openstack/vmware-nsx REPO_VMWARE_TABLES = [ 'tz_network_bindings', 'neutron_nsx_network_mappings', 'neutron_nsx_security_group_mappings', 'neutron_nsx_port_mappings', 'neutron_nsx_router_mappings', 'multi_provider_networks', 'networkconnections', 'networkgatewaydevicereferences', 'networkgatewaydevices', 'networkgateways', 'maclearningstates', 'qosqueues', 'portqueuemappings', 'networkqueuemappings', 'lsn_port', 'lsn', 'nsxv_router_bindings', 'nsxv_edge_vnic_bindings', 'nsxv_edge_dhcp_static_bindings', 'nsxv_internal_networks', 'nsxv_internal_edges', 'nsxv_security_group_section_mappings', 'nsxv_rule_mappings', 'nsxv_port_vnic_mappings', 'nsxv_router_ext_attributes', 'nsxv_tz_network_bindings', 'nsxv_port_index_mappings', 'nsxv_firewall_rule_bindings', 'nsxv_spoofguard_policy_network_mappings', 'nsxv_vdr_dhcp_bindings', 'vcns_router_bindings', ] # Brocade models are in openstack/networking-brocade REPO_BROCADE_TABLES = [ 'brocadenetworks', 'brocadeports', 'ml2_brocadenetworks', 'ml2_brocadeports', ] # BigSwitch models are in openstack/networking-bigswitch REPO_BIGSWITCH_TABLES = [ 'consistencyhashes', 'routerrules', 'nexthops', ] # Nuage models are in github.com/nuagenetworks/nuage-openstack-neutron REPO_NUAGE_TABLES = [ 'nuage_net_partitions', 'nuage_net_partition_router_mapping', 'nuage_provider_net_bindings', 'nuage_subnet_l2dom_mapping', ] TABLES = (FWAAS_TABLES + LBAAS_TABLES + VPNAAS_TABLES + REPO_ARISTA_TABLES + REPO_NEUTRON_DYNAMIC_ROUTING_TABLES + REPO_CISCO_TABLES + REPO_VMWARE_TABLES + REPO_BROCADE_TABLES + REPO_BIGSWITCH_TABLES + REPO_NUAGE_TABLES) neutron-12.1.1/neutron/db/migration/alembic_migrations/brocade_init_ops.py0000664000175000017500000000511213553660046027074 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for the Mellanox plugin from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'brocadenetworks', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('vlan', sa.String(length=10), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'brocadeports', sa.Column('port_id', sa.String(length=36), nullable=False, server_default=''), sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('admin_state_up', sa.Boolean(), nullable=False), sa.Column('physical_interface', sa.String(length=36), nullable=True), sa.Column('vlan_id', sa.String(length=36), nullable=True), sa.Column('tenant_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['network_id'], ['brocadenetworks.id'], ), sa.PrimaryKeyConstraint('port_id')) op.create_table( 'ml2_brocadenetworks', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('vlan', sa.String(length=10), nullable=True), sa.Column('segment_id', sa.String(length=36), nullable=True), sa.Column('network_type', sa.String(length=10), nullable=True), sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'ml2_brocadeports', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('admin_state_up', sa.Boolean(), nullable=False), sa.Column('physical_interface', sa.String(length=36), nullable=True), sa.Column('vlan_id', sa.String(length=36), nullable=True), sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.PrimaryKeyConstraint('id'), sa.ForeignKeyConstraint(['network_id'], ['ml2_brocadenetworks.id'])) neutron-12.1.1/neutron/db/migration/alembic_migrations/core_init_ops.py0000664000175000017500000001547413553660047026442 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for core resources from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'networks', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('status', sa.String(length=16), nullable=True), sa.Column('admin_state_up', sa.Boolean(), nullable=True), sa.Column('shared', sa.Boolean(), nullable=True), sa.Column('mtu', sa.Integer(), nullable=True), sa.Column('vlan_transparent', sa.Boolean(), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'ports', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('mac_address', sa.String(length=32), nullable=False), sa.Column('admin_state_up', sa.Boolean(), nullable=False), sa.Column('status', sa.String(length=16), nullable=False), sa.Column('device_id', sa.String(length=255), nullable=False), sa.Column('device_owner', sa.String(length=255), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id']), sa.UniqueConstraint('network_id', 'mac_address', name='uniq_ports0network_id0mac_address'), sa.PrimaryKeyConstraint('id'), sa.Index(op.f('ix_ports_network_id_device_owner'), 'network_id', 'device_owner'), sa.Index(op.f('ix_ports_network_id_mac_address'), 'network_id', 'mac_address')) op.create_table( 'subnets', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('network_id', sa.String(length=36), nullable=True), sa.Column('ip_version', sa.Integer(), nullable=False), sa.Column('cidr', sa.String(length=64), nullable=False), sa.Column('gateway_ip', sa.String(length=64), nullable=True), sa.Column('enable_dhcp', sa.Boolean(), nullable=True), sa.Column('shared', sa.Boolean(), nullable=True), sa.Column('ipv6_ra_mode', sa.Enum('slaac', 'dhcpv6-stateful', 'dhcpv6-stateless', name='ipv6_ra_modes'), nullable=True), sa.Column('ipv6_address_mode', sa.Enum('slaac', 'dhcpv6-stateful', 'dhcpv6-stateless', name='ipv6_address_modes'), nullable=True), sa.Column('subnetpool_id', sa.String(length=36), nullable=True, index=True), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ), sa.PrimaryKeyConstraint('id')) op.create_table( 'dnsnameservers', sa.Column('address', sa.String(length=128), nullable=False), sa.Column('subnet_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['subnet_id'], ['subnets.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('address', 'subnet_id')) op.create_table( 'ipallocationpools', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('subnet_id', sa.String(length=36), nullable=True), sa.Column('first_ip', sa.String(length=64), nullable=False), sa.Column('last_ip', sa.String(length=64), nullable=False), sa.ForeignKeyConstraint(['subnet_id'], ['subnets.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id')) op.create_table( 'subnetroutes', sa.Column('destination', sa.String(length=64), nullable=False), sa.Column('nexthop', sa.String(length=64), nullable=False), sa.Column('subnet_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['subnet_id'], ['subnets.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('destination', 'nexthop', 'subnet_id')) op.create_table( 'ipallocations', sa.Column('port_id', sa.String(length=36), nullable=True), sa.Column('ip_address', sa.String(length=64), nullable=False), sa.Column('subnet_id', sa.String(length=36), nullable=False), sa.Column('network_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['subnet_id'], ['subnets.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('ip_address', 'subnet_id', 'network_id')) op.create_table( 'ipavailabilityranges', sa.Column('allocation_pool_id', sa.String(length=36), nullable=False), sa.Column('first_ip', sa.String(length=64), nullable=False), sa.Column('last_ip', sa.String(length=64), nullable=False), sa.ForeignKeyConstraint(['allocation_pool_id'], ['ipallocationpools.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('allocation_pool_id', 'first_ip', 'last_ip'), sa.UniqueConstraint( 'first_ip', 'allocation_pool_id', name='uniq_ipavailabilityranges0first_ip0allocation_pool_id'), sa.UniqueConstraint( 'last_ip', 'allocation_pool_id', name='uniq_ipavailabilityranges0last_ip0allocation_pool_id')) op.create_table( 'networkdhcpagentbindings', sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('dhcp_agent_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['dhcp_agent_id'], ['agents.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id', 'dhcp_agent_id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/firewall_init_ops.py0000664000175000017500000000672013553660046027310 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial schema operations for firewall service plugin from alembic import op import sqlalchemy as sa action_types = sa.Enum('allow', 'deny', name='firewallrules_action') def upgrade(): op.create_table( 'firewall_policies', sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('description', sa.String(length=1024), nullable=True), sa.Column('shared', sa.Boolean(), nullable=True), sa.Column('audited', sa.Boolean(), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'firewalls', sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('description', sa.String(length=1024), nullable=True), sa.Column('shared', sa.Boolean(), nullable=True), sa.Column('admin_state_up', sa.Boolean(), nullable=True), sa.Column('status', sa.String(length=16), nullable=True), sa.Column('firewall_policy_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['firewall_policy_id'], ['firewall_policies.id'], name='firewalls_ibfk_1'), sa.PrimaryKeyConstraint('id')) op.create_table( 'firewall_rules', sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('description', sa.String(length=1024), nullable=True), sa.Column('firewall_policy_id', sa.String(length=36), nullable=True), sa.Column('shared', sa.Boolean(), nullable=True), sa.Column('protocol', sa.String(length=40), nullable=True), sa.Column('ip_version', sa.Integer(), nullable=False), sa.Column('source_ip_address', sa.String(length=46), nullable=True), sa.Column('destination_ip_address', sa.String(length=46), nullable=True), sa.Column('source_port_range_min', sa.Integer(), nullable=True), sa.Column('source_port_range_max', sa.Integer(), nullable=True), sa.Column('destination_port_range_min', sa.Integer(), nullable=True), sa.Column('destination_port_range_max', sa.Integer(), nullable=True), sa.Column('action', action_types, nullable=True), sa.Column('enabled', sa.Boolean(), nullable=True), sa.Column('position', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['firewall_policy_id'], ['firewall_policies.id'], name='firewall_rules_ibfk_1'), sa.PrimaryKeyConstraint('id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/secgroup_init_ops.py0000664000175000017500000000612713553660046027333 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for security group extension from alembic import op import sqlalchemy as sa rule_direction_enum = sa.Enum('ingress', 'egress', name='securitygrouprules_direction') def upgrade(): op.create_table( 'securitygroups', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('description', sa.String(length=255), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'securitygrouprules', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('security_group_id', sa.String(length=36), nullable=False), sa.Column('remote_group_id', sa.String(length=36), nullable=True), sa.Column('direction', rule_direction_enum, nullable=True), sa.Column('ethertype', sa.String(length=40), nullable=True), sa.Column('protocol', sa.String(length=40), nullable=True), sa.Column('port_range_min', sa.Integer(), nullable=True), sa.Column('port_range_max', sa.Integer(), nullable=True), sa.Column('remote_ip_prefix', sa.String(length=255), nullable=True), sa.ForeignKeyConstraint(['security_group_id'], ['securitygroups.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['remote_group_id'], ['securitygroups.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id')) op.create_table( 'securitygroupportbindings', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('security_group_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['security_group_id'], ['securitygroups.id']), sa.PrimaryKeyConstraint('port_id', 'security_group_id')) op.create_table( 'default_security_group', sa.Column('tenant_id', sa.String(length=255), nullable=False), sa.Column('security_group_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('tenant_id'), sa.ForeignKeyConstraint(['security_group_id'], ['securitygroups.id'], ondelete="CASCADE")) neutron-12.1.1/neutron/db/migration/alembic_migrations/metering_init_ops.py0000664000175000017500000000374513553660046027321 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for the metering service plugin from alembic import op import sqlalchemy as sa direction = sa.Enum('ingress', 'egress', name='meteringlabels_direction') def create_meteringlabels(): op.create_table( 'meteringlabels', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('description', sa.String(length=1024), nullable=True), sa.Column('shared', sa.Boolean(), server_default=sa.sql.false(), nullable=True), sa.PrimaryKeyConstraint('id')) def upgrade(): create_meteringlabels() op.create_table( 'meteringlabelrules', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('direction', direction, nullable=True), sa.Column('remote_ip_prefix', sa.String(length=64), nullable=True), sa.Column('metering_label_id', sa.String(length=36), nullable=False), sa.Column('excluded', sa.Boolean(), nullable=True, server_default=sa.sql.false()), sa.ForeignKeyConstraint(['metering_label_id'], ['meteringlabels.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/nec_init_ops.py0000664000175000017500000001044713553660046026251 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for NEC plugin from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'ofcportmappings', sa.Column('ofc_id', sa.String(length=255), nullable=False), sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('neutron_id'), sa.UniqueConstraint('ofc_id')) op.create_table( 'ofcroutermappings', sa.Column('ofc_id', sa.String(length=255), nullable=False), sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('neutron_id'), sa.UniqueConstraint('ofc_id')) op.create_table( 'routerproviders', sa.Column('provider', sa.String(length=255), nullable=True), sa.Column('router_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('router_id')) op.create_table( 'ofctenantmappings', sa.Column('ofc_id', sa.String(length=255), nullable=False), sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('neutron_id'), sa.UniqueConstraint('ofc_id')) op.create_table( 'ofcfiltermappings', sa.Column('ofc_id', sa.String(length=255), nullable=False), sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('neutron_id'), sa.UniqueConstraint('ofc_id')) op.create_table( 'ofcnetworkmappings', sa.Column('ofc_id', sa.String(length=255), nullable=False), sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('neutron_id'), sa.UniqueConstraint('ofc_id')) op.create_table( 'packetfilters', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('priority', sa.Integer(), nullable=False), sa.Column('action', sa.String(length=16), nullable=False), sa.Column('in_port', sa.String(length=36), nullable=True), sa.Column('src_mac', sa.String(length=32), nullable=False), sa.Column('dst_mac', sa.String(length=32), nullable=False), sa.Column('eth_type', sa.Integer(), nullable=False), sa.Column('src_cidr', sa.String(length=64), nullable=False), sa.Column('dst_cidr', sa.String(length=64), nullable=False), sa.Column('protocol', sa.String(length=16), nullable=False), sa.Column('src_port', sa.Integer(), nullable=False), sa.Column('dst_port', sa.Integer(), nullable=False), sa.Column('admin_state_up', sa.Boolean(), nullable=False), sa.Column('status', sa.String(length=16), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['in_port'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id')) op.create_table( 'portinfos', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('datapath_id', sa.String(length=36), nullable=False), sa.Column('port_no', sa.Integer(), nullable=False), sa.Column('vlan_id', sa.Integer(), nullable=False), sa.Column('mac', sa.String(length=32), nullable=False), sa.ForeignKeyConstraint(['id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/__init__.py0000664000175000017500000000000013553660046025317 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/migration/alembic_migrations/portsec_init_ops.py0000664000175000017500000000265413553660046027164 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for the port security extension from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'networksecuritybindings', sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('port_security_enabled', sa.Boolean(), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id')) op.create_table( 'portsecuritybindings', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('port_security_enabled', sa.Boolean(), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('port_id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/lb_init_ops.py0000664000175000017500000000274013553660046026076 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for the port security extension from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'network_states', sa.Column('physical_network', sa.String(length=64), nullable=False), sa.Column('vlan_id', sa.Integer(), autoincrement=False, nullable=False), sa.Column('allocated', sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint('physical_network', 'vlan_id')) op.create_table( 'network_bindings', sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('physical_network', sa.String(length=64), nullable=True), sa.Column('vlan_id', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/loadbalancer_init_ops.py0000664000175000017500000001561113553660046030111 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial schema operations for the load balancer service plugin from alembic import op import sqlalchemy as sa protocols = sa.Enum('HTTP', 'HTTPS', 'TCP', name='lb_protocols') session_persistence_type = sa.Enum('SOURCE_IP', 'HTTP_COOKIE', 'APP_COOKIE', name='sesssionpersistences_type') lb_methods = sa.Enum('ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP', name='pools_lb_method') health_monitor_type = sa.Enum('PING', 'TCP', 'HTTP', 'HTTPS', name='healthmontiors_type') def upgrade(): op.create_table( 'healthmonitors', sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('type', health_monitor_type, nullable=False), sa.Column('delay', sa.Integer(), nullable=False), sa.Column('timeout', sa.Integer(), nullable=False), sa.Column('max_retries', sa.Integer(), nullable=False), sa.Column('http_method', sa.String(length=16), nullable=True), sa.Column('url_path', sa.String(length=255), nullable=True), sa.Column('expected_codes', sa.String(length=64), nullable=True), sa.Column('admin_state_up', sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint('id')) op.create_table( 'vips', sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('status', sa.String(length=16), nullable=False), sa.Column('status_description', sa.String(length=255), nullable=True), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('description', sa.String(length=255), nullable=True), sa.Column('port_id', sa.String(length=36), nullable=True), sa.Column('protocol_port', sa.Integer(), nullable=False), sa.Column('protocol', protocols, nullable=False), sa.Column('pool_id', sa.String(length=36), nullable=False), sa.Column('admin_state_up', sa.Boolean(), nullable=False), sa.Column('connection_limit', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('pool_id')) op.create_table( 'pools', sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('status', sa.String(length=16), nullable=False), sa.Column('status_description', sa.String(length=255), nullable=True), sa.Column('vip_id', sa.String(length=36), nullable=True), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('description', sa.String(length=255), nullable=True), sa.Column('subnet_id', sa.String(length=36), nullable=False), sa.Column('protocol', protocols, nullable=False), sa.Column('lb_method', lb_methods, nullable=False), sa.Column('admin_state_up', sa.Boolean(), nullable=False), sa.ForeignKeyConstraint(['vip_id'], ['vips.id'], ), sa.PrimaryKeyConstraint('id')) op.create_table( 'sessionpersistences', sa.Column('vip_id', sa.String(length=36), nullable=False), sa.Column('type', session_persistence_type, nullable=False), sa.Column('cookie_name', sa.String(length=1024), nullable=True), sa.ForeignKeyConstraint(['vip_id'], ['vips.id'], ), sa.PrimaryKeyConstraint('vip_id')) op.create_table( 'poolloadbalanceragentbindings', sa.Column('pool_id', sa.String(length=36), nullable=False), sa.Column('agent_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['pool_id'], ['pools.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['agent_id'], ['agents.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('pool_id')) op.create_table( 'members', sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('status', sa.String(length=16), nullable=False), sa.Column('status_description', sa.String(length=255), nullable=True), sa.Column('pool_id', sa.String(length=36), nullable=False), sa.Column('address', sa.String(length=64), nullable=False), sa.Column('protocol_port', sa.Integer(), nullable=False), sa.Column('weight', sa.Integer(), nullable=False), sa.Column('admin_state_up', sa.Boolean(), nullable=False), sa.ForeignKeyConstraint(['pool_id'], ['pools.id'], ), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('pool_id', 'address', 'protocol_port', name='uniq_member0pool_id0address0port')) op.create_table( 'poolmonitorassociations', sa.Column('status', sa.String(length=16), nullable=False), sa.Column('status_description', sa.String(length=255), nullable=True), sa.Column('pool_id', sa.String(length=36), nullable=False), sa.Column('monitor_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['pool_id'], ['pools.id'], ), sa.ForeignKeyConstraint(['monitor_id'], ['healthmonitors.id'], ), sa.PrimaryKeyConstraint('pool_id', 'monitor_id')) op.create_table( 'poolstatisticss', sa.Column('pool_id', sa.String(length=36), nullable=False), sa.Column('bytes_in', sa.BigInteger(), nullable=False), sa.Column('bytes_out', sa.BigInteger(), nullable=False), sa.Column('active_connections', sa.BigInteger(), nullable=False), sa.Column('total_connections', sa.BigInteger(), nullable=False), sa.ForeignKeyConstraint(['pool_id'], ['pools.id'], ), sa.PrimaryKeyConstraint('pool_id')) op.create_table( u'embrane_pool_port', sa.Column(u'pool_id', sa.String(length=36), nullable=False), sa.Column(u'port_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['pool_id'], [u'pools.id'], name=u'embrane_pool_port_ibfk_1'), sa.ForeignKeyConstraint(['port_id'], [u'ports.id'], name=u'embrane_pool_port_ibfk_2'), sa.PrimaryKeyConstraint(u'pool_id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/agent_init_ops.py0000664000175000017500000000357613553660046026607 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for agent management extension # This module only manages the 'agents' table. Binding tables are created # in the modules for relevant resources from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'agents', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('agent_type', sa.String(length=255), nullable=False), sa.Column('binary', sa.String(length=255), nullable=False), sa.Column('topic', sa.String(length=255), nullable=False), sa.Column('host', sa.String(length=255), nullable=False), sa.Column('admin_state_up', sa.Boolean(), nullable=False, server_default=sa.sql.true()), sa.Column('created_at', sa.DateTime(), nullable=False), sa.Column('started_at', sa.DateTime(), nullable=False), sa.Column('heartbeat_timestamp', sa.DateTime(), nullable=False), sa.Column('description', sa.String(length=255), nullable=True), sa.Column('configurations', sa.String(length=4095), nullable=False), sa.Column('load', sa.Integer(), server_default='0', nullable=False), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('agent_type', 'host', name='uniq_agents0agent_type0host')) neutron-12.1.1/neutron/db/migration/alembic_migrations/ml2_init_ops.py0000664000175000017500000001762313553660046026201 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial operations for ML2 plugin and drivers from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'ml2_vlan_allocations', sa.Column('physical_network', sa.String(length=64), nullable=False), sa.Column('vlan_id', sa.Integer(), autoincrement=False, nullable=False), sa.Column('allocated', sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint('physical_network', 'vlan_id'), sa.Index(op.f('ix_ml2_vlan_allocations_physical_network_allocated'), 'physical_network', 'allocated')) op.create_table( 'ml2_vxlan_endpoints', sa.Column('ip_address', sa.String(length=64), nullable=False), sa.Column('udp_port', sa.Integer(), autoincrement=False, nullable=False), sa.Column('host', sa.String(length=255), nullable=True), sa.UniqueConstraint('host', name='unique_ml2_vxlan_endpoints0host'), sa.PrimaryKeyConstraint('ip_address')) op.create_table( 'ml2_gre_endpoints', sa.Column('ip_address', sa.String(length=64), nullable=False), sa.Column('host', sa.String(length=255), nullable=True), sa.UniqueConstraint('host', name='unique_ml2_gre_endpoints0host'), sa.PrimaryKeyConstraint('ip_address')) op.create_table( 'ml2_vxlan_allocations', sa.Column('vxlan_vni', sa.Integer(), autoincrement=False, nullable=False), sa.Column('allocated', sa.Boolean(), nullable=False, server_default=sa.sql.false(), index=True), sa.PrimaryKeyConstraint('vxlan_vni')) op.create_table( 'ml2_gre_allocations', sa.Column('gre_id', sa.Integer(), autoincrement=False, nullable=False), sa.Column('allocated', sa.Boolean(), nullable=False, server_default=sa.sql.false(), index=True), sa.PrimaryKeyConstraint('gre_id')) op.create_table( 'ml2_flat_allocations', sa.Column('physical_network', sa.String(length=64), nullable=False), sa.PrimaryKeyConstraint('physical_network')) op.create_table( 'ml2_network_segments', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('network_type', sa.String(length=32), nullable=False), sa.Column('physical_network', sa.String(length=64), nullable=True), sa.Column('segmentation_id', sa.Integer(), nullable=True), sa.Column('is_dynamic', sa.Boolean(), nullable=False, server_default=sa.sql.false()), sa.Column('segment_index', sa.Integer(), nullable=False, server_default='0'), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id')) op.create_table( 'ml2_port_bindings', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('host', sa.String(length=255), nullable=False, server_default=''), sa.Column('vif_type', sa.String(length=64), nullable=False), sa.Column('vnic_type', sa.String(length=64), nullable=False, server_default='normal'), sa.Column('profile', sa.String(length=4095), nullable=False, server_default=''), sa.Column('vif_details', sa.String(length=4095), nullable=False, server_default=''), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('port_id')) op.create_table( 'ml2_port_binding_levels', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('host', sa.String(length=255), nullable=False), sa.Column('level', sa.Integer(), autoincrement=False, nullable=False), sa.Column('driver', sa.String(length=64), nullable=True), sa.Column('segment_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['segment_id'], ['ml2_network_segments.id'], ondelete='SET NULL'), sa.PrimaryKeyConstraint('port_id', 'host', 'level') ) op.create_table( 'cisco_ml2_nexusport_bindings', sa.Column('binding_id', sa.Integer(), nullable=False), sa.Column('port_id', sa.String(length=255), nullable=True), sa.Column('vlan_id', sa.Integer(), autoincrement=False, nullable=False), sa.Column('switch_ip', sa.String(length=255), nullable=True), sa.Column('instance_id', sa.String(length=255), nullable=True), sa.Column('vni', sa.Integer(), nullable=True), sa.Column('is_provider_vlan', sa.Boolean(), nullable=False, server_default=sa.sql.false()), sa.PrimaryKeyConstraint('binding_id'), ) op.create_table( 'arista_provisioned_nets', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('network_id', sa.String(length=36), nullable=True), sa.Column('segmentation_id', sa.Integer(), autoincrement=False, nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'arista_provisioned_vms', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('vm_id', sa.String(length=255), nullable=True), sa.Column('host_id', sa.String(length=255), nullable=True), sa.Column('port_id', sa.String(length=36), nullable=True), sa.Column('network_id', sa.String(length=36), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'arista_provisioned_tenants', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('id')) op.create_table( 'ml2_nexus_vxlan_allocations', sa.Column('vxlan_vni', sa.Integer(), nullable=False, autoincrement=False), sa.Column('allocated', sa.Boolean(), nullable=False, server_default=sa.sql.false()), sa.PrimaryKeyConstraint('vxlan_vni') ) op.create_table( 'ml2_nexus_vxlan_mcast_groups', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('mcast_group', sa.String(length=64), nullable=False), sa.Column('associated_vni', sa.Integer(), nullable=False), sa.PrimaryKeyConstraint('id'), sa.ForeignKeyConstraint(['associated_vni'], ['ml2_nexus_vxlan_allocations.vxlan_vni'], ondelete='CASCADE') ) op.create_table( 'cisco_ml2_nexus_nve', sa.Column('vni', sa.Integer(), nullable=False), sa.Column('switch_ip', sa.String(length=255), nullable=True), sa.Column('device_id', sa.String(length=255), nullable=True), sa.Column('mcast_group', sa.String(length=255), nullable=True), sa.PrimaryKeyConstraint('vni', 'switch_ip', 'device_id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/env.py0000664000175000017500000000713613553660046024371 0ustar zuulzuul00000000000000# Copyright 2012 New Dream Network, LLC (DreamHost) # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from alembic import context from neutron_lib.db import model_base from oslo_config import cfg import sqlalchemy as sa from sqlalchemy import event # noqa from neutron.db.migration.alembic_migrations import external from neutron.db.migration import autogen from neutron.db.migration.connection import DBConnection from neutron.db.migration.models import head # noqa try: # NOTE(mriedem): This is to register the DB2 alembic code which # is an optional runtime dependency. from ibm_db_alembic.ibm_db import IbmDbImpl # noqa # pylint: disable=unused-import except ImportError: pass MYSQL_ENGINE = None # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config neutron_config = config.neutron_config # set the target for 'autogenerate' support target_metadata = model_base.BASEV2.metadata def set_mysql_engine(): try: mysql_engine = neutron_config.command.mysql_engine except cfg.NoSuchOptError: mysql_engine = None global MYSQL_ENGINE MYSQL_ENGINE = (mysql_engine or model_base.BASEV2.__table_args__['mysql_engine']) def include_object(object_, name, type_, reflected, compare_to): if type_ == 'table' and name in external.TABLES: return False elif type_ == 'index' and reflected and name.startswith("idx_autoinc_"): # skip indexes created by SQLAlchemy autoincrement=True # on composite PK integer columns return False else: return True def run_migrations_offline(): """Run migrations in 'offline' mode. This configures the context with either a URL or an Engine. Calls to context.execute() here emit the given string to the script output. """ set_mysql_engine() kwargs = dict() if neutron_config.database.connection: kwargs['url'] = neutron_config.database.connection else: kwargs['dialect_name'] = neutron_config.database.engine kwargs['include_object'] = include_object context.configure(**kwargs) with context.begin_transaction(): context.run_migrations() @event.listens_for(sa.Table, 'after_parent_attach') def set_storage_engine(target, parent): if MYSQL_ENGINE: target.kwargs['mysql_engine'] = MYSQL_ENGINE def run_migrations_online(): """Run migrations in 'online' mode. In this scenario we need to create an Engine and associate a connection with the context. """ set_mysql_engine() connection = config.attributes.get('connection') with DBConnection(neutron_config.database.connection, connection) as conn: context.configure( connection=conn, target_metadata=target_metadata, include_object=include_object, process_revision_directives=autogen.process_revision_directives ) with context.begin_transaction(): context.run_migrations() if context.is_offline_mode(): run_migrations_offline() else: run_migrations_online() neutron-12.1.1/neutron/db/migration/alembic_migrations/nsxv_initial_opts.py0000664000175000017500000001542513553660046027355 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from alembic import op import sqlalchemy as sa appliance_sizes_enum = sa.Enum('compact', 'large', 'xlarge', 'quadlarge', name='nsxv_router_bindings_appliance_size') edge_types_enum = sa.Enum('service', 'vdr', name='nsxv_router_bindings_edge_type') internal_network_purpose_enum = sa.Enum('inter_edge_net', name='nsxv_internal_networks_purpose') internal_edge_purpose_enum = sa.Enum('inter_edge_net', name='nsxv_internal_edges_purpose') tz_binding_type_enum = sa.Enum('flat', 'vlan', 'portgroup', name='nsxv_tz_network_bindings_binding_type') router_types_enum = sa.Enum('shared', 'exclusive', name='nsxv_router_type') def upgrade(): op.create_table( 'nsxv_router_bindings', sa.Column('status', sa.String(length=16), nullable=False), sa.Column('status_description', sa.String(length=255), nullable=True), sa.Column('router_id', sa.String(length=36), nullable=False), sa.Column('edge_id', sa.String(length=36), nullable=True), sa.Column('lswitch_id', sa.String(length=36), nullable=True), sa.Column('appliance_size', appliance_sizes_enum, nullable=True), sa.Column('edge_type', edge_types_enum, nullable=True), sa.PrimaryKeyConstraint('router_id')) op.create_table( 'nsxv_internal_networks', sa.Column('network_purpose', internal_network_purpose_enum, nullable=False), sa.Column('network_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_purpose')) op.create_table( 'nsxv_internal_edges', sa.Column('ext_ip_address', sa.String(length=64), nullable=False), sa.Column('router_id', sa.String(length=36), nullable=True), sa.Column('purpose', internal_edge_purpose_enum, nullable=True), sa.PrimaryKeyConstraint('ext_ip_address')) op.create_table( 'nsxv_firewall_rule_bindings', sa.Column('rule_id', sa.String(length=36), nullable=False), sa.Column('edge_id', sa.String(length=36), nullable=False), sa.Column('rule_vse_id', sa.String(length=36), nullable=True), sa.PrimaryKeyConstraint('rule_id', 'edge_id')) op.create_table( 'nsxv_edge_dhcp_static_bindings', sa.Column('edge_id', sa.String(length=36), nullable=False), sa.Column('mac_address', sa.String(length=32), nullable=False), sa.Column('binding_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('edge_id', 'mac_address')) op.create_table( 'nsxv_edge_vnic_bindings', sa.Column('edge_id', sa.String(length=36), nullable=False), sa.Column('vnic_index', sa.Integer(), nullable=False), sa.Column('tunnel_index', sa.Integer(), nullable=False), sa.Column('network_id', sa.String(length=36), nullable=True), sa.PrimaryKeyConstraint('edge_id', 'vnic_index', 'tunnel_index')) op.create_table( 'nsxv_spoofguard_policy_network_mappings', sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('policy_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id')) op.create_table( 'nsxv_security_group_section_mappings', sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.Column('ip_section_id', sa.String(length=100), nullable=True), sa.ForeignKeyConstraint(['neutron_id'], ['securitygroups.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('neutron_id')) op.create_table( 'nsxv_tz_network_bindings', sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('binding_type', tz_binding_type_enum, nullable=False), sa.Column('phy_uuid', sa.String(length=36), nullable=True), sa.Column('vlan_id', sa.Integer(), autoincrement=False, nullable=True), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id', 'binding_type', 'phy_uuid', 'vlan_id')) op.create_table( 'nsxv_port_vnic_mappings', sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.Column('nsx_id', sa.String(length=42), nullable=False), sa.ForeignKeyConstraint(['neutron_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('neutron_id', 'nsx_id')) op.create_table( 'nsxv_port_index_mappings', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('device_id', sa.String(length=255), nullable=False), sa.Column('index', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('port_id'), sa.UniqueConstraint('device_id', 'index')) op.create_table( 'nsxv_rule_mappings', sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.Column('nsx_rule_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['neutron_id'], ['securitygrouprules.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('neutron_id', 'nsx_rule_id')) op.create_table( 'nsxv_router_ext_attributes', sa.Column('router_id', sa.String(length=36), nullable=False), sa.Column('distributed', sa.Boolean(), nullable=False), sa.Column('router_type', router_types_enum, default='exclusive', nullable=False), sa.Column('service_router', sa.Boolean(), nullable=False), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('router_id')) neutron-12.1.1/neutron/db/migration/alembic_migrations/vmware_init_ops.py0000664000175000017500000002220013553660046026773 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Initial schema operations for VMware plugins from alembic import op import sqlalchemy as sa net_binding_type = sa.Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext', name='tz_network_bindings_binding_type') l2gw_segmentation_type = sa.Enum('flat', 'vlan', name='networkconnections_segmentation_type') qos_marking = sa.Enum('untrusted', 'trusted', name='qosqueues_qos_marking') def upgrade(): op.create_table( 'tz_network_bindings', sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('binding_type', net_binding_type, nullable=False), sa.Column('phy_uuid', sa.String(length=36), nullable=True), sa.Column('vlan_id', sa.Integer(), autoincrement=False, nullable=True), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id', 'binding_type', 'phy_uuid', 'vlan_id')) op.create_table( 'multi_provider_networks', sa.Column('network_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id')) op.create_table( 'vcns_router_bindings', sa.Column('status', sa.String(length=16), nullable=False), sa.Column('status_description', sa.String(length=255), nullable=True), sa.Column('router_id', sa.String(length=36), nullable=False), sa.Column('edge_id', sa.String(length=16), nullable=True), sa.Column('lswitch_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('router_id')) op.create_table( 'networkgateways', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('tenant_id', sa.String(length=36), nullable=True), sa.Column('default', sa.Boolean(), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'networkconnections', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('network_gateway_id', sa.String(length=36), nullable=True), sa.Column('network_id', sa.String(length=36), nullable=True), sa.Column('segmentation_type', l2gw_segmentation_type, nullable=True), sa.Column('segmentation_id', sa.Integer(), nullable=True), sa.Column('port_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['network_gateway_id'], ['networkgateways.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('port_id'), sa.UniqueConstraint('network_gateway_id', 'segmentation_type', 'segmentation_id')) op.create_table( 'qosqueues', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('default', sa.Boolean(), nullable=True, server_default=sa.sql.false()), sa.Column('min', sa.Integer(), nullable=False), sa.Column('max', sa.Integer(), nullable=True), sa.Column('qos_marking', qos_marking, nullable=True), sa.Column('dscp', sa.Integer(), nullable=True), sa.PrimaryKeyConstraint('id')) op.create_table( 'networkqueuemappings', sa.Column('network_id', sa.String(length=36), nullable=False), sa.Column('queue_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['queue_id'], ['qosqueues.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('network_id')) op.create_table( 'portqueuemappings', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('queue_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['queue_id'], ['qosqueues.id'], ), sa.PrimaryKeyConstraint('port_id', 'queue_id')) op.create_table( 'maclearningstates', sa.Column('port_id', sa.String(length=36), nullable=False), sa.Column('mac_learning_enabled', sa.Boolean(), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('port_id')) op.create_table('neutron_nsx_port_mappings', sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.Column('nsx_port_id', sa.String(length=36), nullable=False), sa.Column('nsx_switch_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['neutron_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('neutron_id')) op.create_table( 'lsn', sa.Column('net_id', sa.String(length=36), nullable=False), sa.Column('lsn_id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('lsn_id')) op.create_table( 'lsn_port', sa.Column('lsn_port_id', sa.String(length=36), nullable=False), sa.Column('lsn_id', sa.String(length=36), nullable=False), sa.Column('sub_id', sa.String(length=36), nullable=False, unique=True), sa.Column('mac_addr', sa.String(length=32), nullable=False, unique=True), sa.ForeignKeyConstraint(['lsn_id'], ['lsn.lsn_id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('lsn_port_id')) op.create_table( 'neutron_nsx_network_mappings', sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.Column('nsx_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['neutron_id'], ['networks.id'], ondelete='CASCADE'), # There might be multiple switches for a neutron network sa.PrimaryKeyConstraint('neutron_id', 'nsx_id'), ) op.create_table( 'neutron_nsx_router_mappings', sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.Column('nsx_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['neutron_id'], ['routers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('neutron_id'), ) op.create_table( 'neutron_nsx_security_group_mappings', sa.Column('neutron_id', sa.String(length=36), nullable=False), sa.Column('nsx_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['neutron_id'], ['securitygroups.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('neutron_id', 'nsx_id')) op.create_table( 'networkgatewaydevicereferences', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('network_gateway_id', sa.String(length=36), nullable=True), sa.Column('interface_name', sa.String(length=64), nullable=True), sa.ForeignKeyConstraint(['network_gateway_id'], ['networkgateways.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id', 'network_gateway_id', 'interface_name')) op.create_table( 'networkgatewaydevices', sa.Column('tenant_id', sa.String(length=255), nullable=True, index=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('nsx_id', sa.String(length=36), nullable=True), sa.Column('name', sa.String(length=255), nullable=True), sa.Column('connector_type', sa.String(length=10), nullable=True), sa.Column('connector_ip', sa.String(length=64), nullable=True), sa.Column('status', sa.String(length=16), nullable=True), sa.PrimaryKeyConstraint('id')) neutron-12.1.1/neutron/db/migration/connection.py0000664000175000017500000000273613553660046022111 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_db.sqlalchemy import session class DBConnection(object): """Context manager class which handles a DB connection. An existing connection can be passed as a parameter. When nested block is complete the new connection will be closed. This class is not thread safe. """ def __init__(self, connection_url, connection=None): self.connection = connection self.connection_url = connection_url self.new_engine = False def __enter__(self): self.new_engine = self.connection is None if self.new_engine: self.engine = session.create_engine(self.connection_url) self.connection = self.engine.connect() return self.connection def __exit__(self, type, value, traceback): if self.new_engine: try: self.connection.close() finally: self.engine.dispose() neutron-12.1.1/neutron/db/migration/README0000664000175000017500000000021513553660046020246 0ustar zuulzuul00000000000000See doc/source/contributor/alembic_migrations.rst Rendered at https://docs.openstack.org/neutron/latest/contributor/alembic_migrations.html neutron-12.1.1/neutron/db/migration/cli.py0000664000175000017500000005571113553660047020523 0ustar zuulzuul00000000000000# Copyright 2012 New Dream Network, LLC (DreamHost) # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from logging import config as logging_config import os from alembic import command as alembic_command from alembic import config as alembic_config from alembic import environment from alembic import migration as alembic_migration from alembic import script as alembic_script from alembic import util as alembic_util from oslo_config import cfg from oslo_utils import fileutils from oslo_utils import importutils import six from neutron._i18n import _ from neutron.conf.db import migration_cli from neutron.db import migration from neutron.db.migration.connection import DBConnection HEAD_FILENAME = 'HEAD' HEADS_FILENAME = 'HEADS' CONTRACT_HEAD_FILENAME = 'CONTRACT_HEAD' EXPAND_HEAD_FILENAME = 'EXPAND_HEAD' CURRENT_RELEASE = migration.QUEENS RELEASES = ( migration.LIBERTY, migration.MITAKA, migration.NEWTON, migration.OCATA, migration.PIKE, migration.QUEENS, ) EXPAND_BRANCH = 'expand' CONTRACT_BRANCH = 'contract' MIGRATION_BRANCHES = (EXPAND_BRANCH, CONTRACT_BRANCH) neutron_alembic_ini = os.path.join(os.path.dirname(__file__), 'alembic.ini') CONF = cfg.ConfigOpts() migration_cli.register_db_cli_opts(CONF) def do_alembic_command(config, cmd, revision=None, desc=None, **kwargs): args = [] if revision: args.append(revision) project = config.get_main_option('neutron_project') if desc: alembic_util.msg(_('Running %(cmd)s (%(desc)s) for %(project)s ...') % {'cmd': cmd, 'desc': desc, 'project': project}) else: alembic_util.msg(_('Running %(cmd)s for %(project)s ...') % {'cmd': cmd, 'project': project}) try: getattr(alembic_command, cmd)(config, *args, **kwargs) except alembic_util.CommandError as e: alembic_util.err(six.text_type(e)) alembic_util.msg(_('OK')) def _get_alembic_entrypoint(project): if project not in migration_cli.migration_entrypoints: alembic_util.err(_('Sub-project %s not installed.') % project) return migration_cli.migration_entrypoints[project] def do_generic_show(config, cmd): kwargs = {'verbose': CONF.command.verbose} do_alembic_command(config, cmd, **kwargs) def do_check_migration(config, cmd): do_alembic_command(config, 'branches') validate_revisions(config) validate_head_files(config) def add_alembic_subparser(sub, cmd): return sub.add_parser(cmd, help=getattr(alembic_command, cmd).__doc__) def add_branch_options(parser): group = parser.add_mutually_exclusive_group() group.add_argument('--expand', action='store_true') group.add_argument('--contract', action='store_true') return group def _find_milestone_revisions(config, milestone, branch=None): """Return the revision(s) for a given milestone.""" script = alembic_script.ScriptDirectory.from_config(config) return [ (m.revision, label) for m in _get_revisions(script) for label in (m.branch_labels or [None]) if milestone in getattr(m.module, 'neutron_milestone', []) and (branch is None or branch in m.branch_labels) ] def do_upgrade(config, cmd): branch = None if ((CONF.command.revision or CONF.command.delta) and (CONF.command.expand or CONF.command.contract)): raise SystemExit(_( 'Phase upgrade options do not accept revision specification')) if CONF.command.expand: branch = EXPAND_BRANCH revision = _get_branch_head(EXPAND_BRANCH) elif CONF.command.contract: branch = CONTRACT_BRANCH revision = _get_branch_head(CONTRACT_BRANCH) elif not CONF.command.revision and not CONF.command.delta: raise SystemExit(_('You must provide a revision or relative delta')) else: revision = CONF.command.revision or '' if '-' in revision: raise SystemExit(_('Negative relative revision (downgrade) not ' 'supported')) delta = CONF.command.delta if delta: if '+' in revision: raise SystemExit(_('Use either --delta or relative revision, ' 'not both')) if delta < 0: raise SystemExit(_('Negative delta (downgrade) not supported')) revision = '%s+%d' % (revision, delta) # leave branchless 'head' revision request backward compatible by # applying all heads in all available branches. if revision == 'head': revision = 'heads' if revision in migration.NEUTRON_MILESTONES: expand_revisions = _find_milestone_revisions(config, revision, EXPAND_BRANCH) contract_revisions = _find_milestone_revisions(config, revision, CONTRACT_BRANCH) # Expand revisions must be run before contract revisions revisions = expand_revisions + contract_revisions else: revisions = [(revision, branch)] for revision, branch in revisions: if not CONF.command.sql: run_sanity_checks(config, revision) do_alembic_command(config, cmd, revision=revision, desc=branch, sql=CONF.command.sql) def no_downgrade(config, cmd): raise SystemExit(_("Downgrade no longer supported")) def do_stamp(config, cmd): do_alembic_command(config, cmd, revision=CONF.command.revision, sql=CONF.command.sql) def _get_branch_head(branch): '''Get the latest @head specification for a branch.''' return '%s@head' % branch def _check_bootstrap_new_branch(branch, version_path, addn_kwargs): addn_kwargs['version_path'] = version_path addn_kwargs['head'] = _get_branch_head(branch) if not os.path.exists(version_path): # Bootstrap initial directory structure fileutils.ensure_tree(version_path, mode=0o755) def do_revision(config, cmd): kwargs = { 'message': CONF.command.message, 'autogenerate': CONF.command.autogenerate, 'sql': CONF.command.sql, } branches = [] if CONF.command.expand: kwargs['head'] = 'expand@head' branches.append(EXPAND_BRANCH) elif CONF.command.contract: kwargs['head'] = 'contract@head' branches.append(CONTRACT_BRANCH) else: branches = MIGRATION_BRANCHES if not CONF.command.autogenerate: for branch in branches: args = copy.copy(kwargs) version_path = _get_version_branch_path( config, release=CURRENT_RELEASE, branch=branch) _check_bootstrap_new_branch(branch, version_path, args) do_alembic_command(config, cmd, **args) else: # autogeneration code will take care of enforcing proper directories do_alembic_command(config, cmd, **kwargs) update_head_files(config) def _get_release_labels(labels): result = set() for label in labels: # release labels were introduced Liberty for a short time and dropped # in that same release cycle result.add('%s_%s' % (migration.LIBERTY, label)) return result def _compare_labels(revision, expected_labels): # validate that the script has expected labels only bad_labels = revision.branch_labels - expected_labels if bad_labels: # NOTE(ihrachyshka): this hack is temporary to accommodate those # projects that already initialized their branches with liberty_* # labels. Let's notify them about the deprecation for now and drop it # later. bad_labels_with_release = (revision.branch_labels - _get_release_labels(expected_labels)) if not bad_labels_with_release: alembic_util.warn( _('Release aware branch labels (%s) are deprecated. ' 'Please switch to expand@ and contract@ ' 'labels.') % bad_labels) return script_name = os.path.basename(revision.path) alembic_util.err( _('Unexpected label for script %(script_name)s: %(labels)s') % {'script_name': script_name, 'labels': bad_labels} ) def _validate_single_revision_labels(script_dir, revision, label=None): expected_labels = set() if label is not None: expected_labels.add(label) _compare_labels(revision, expected_labels) # if it's not the root element of the branch, expect the parent of the # script to have the same label if revision.down_revision is not None: down_revision = script_dir.get_revision(revision.down_revision) _compare_labels(down_revision, expected_labels) def _validate_revision(script_dir, revision): for branch in MIGRATION_BRANCHES: if branch in revision.path: _validate_single_revision_labels( script_dir, revision, label=branch) return # validate script from branchless part of migration rules _validate_single_revision_labels(script_dir, revision) def validate_revisions(config): script_dir = alembic_script.ScriptDirectory.from_config(config) revisions = _get_revisions(script_dir) for revision in revisions: _validate_revision(script_dir, revision) branchpoints = _get_branch_points(script_dir) if len(branchpoints) > 1: branchpoints = ', '.join(p.revision for p in branchpoints) alembic_util.err( _('Unexpected number of alembic branch points: %(branchpoints)s') % {'branchpoints': branchpoints} ) def _get_revisions(script): return list(script.walk_revisions(base='base', head='heads')) def _get_branch_points(script): branchpoints = [] for revision in _get_revisions(script): if revision.is_branch_point: branchpoints.append(revision) return branchpoints def _get_heads_map(config): script = alembic_script.ScriptDirectory.from_config(config) heads = script.get_heads() head_map = {} for head in heads: if CONTRACT_BRANCH in script.get_revision(head).branch_labels: head_map[CONTRACT_BRANCH] = head else: head_map[EXPAND_BRANCH] = head return head_map def _check_head(branch_name, head_file, head): try: with open(head_file) as file_: observed_head = file_.read().strip() except IOError: pass else: if observed_head != head: alembic_util.err( _('%(branch)s HEAD file does not match migration timeline ' 'head, expected: %(head)s') % {'branch': branch_name.title(), 'head': head}) def validate_head_files(config): '''Check that HEAD files contain the latest head for the branch.''' contract_head = _get_contract_head_file_path(config) expand_head = _get_expand_head_file_path(config) if not os.path.exists(contract_head) or not os.path.exists(expand_head): alembic_util.warn(_("Repository does not contain HEAD files for " "contract and expand branches.")) return head_map = _get_heads_map(config) _check_head(CONTRACT_BRANCH, contract_head, head_map[CONTRACT_BRANCH]) _check_head(EXPAND_BRANCH, expand_head, head_map[EXPAND_BRANCH]) def update_head_files(config): '''Update HEAD files with the latest branch heads.''' head_map = _get_heads_map(config) contract_head = _get_contract_head_file_path(config) expand_head = _get_expand_head_file_path(config) with open(contract_head, 'w+') as f: f.write(head_map[CONTRACT_BRANCH] + '\n') with open(expand_head, 'w+') as f: f.write(head_map[EXPAND_BRANCH] + '\n') old_head_file = _get_head_file_path(config) old_heads_file = _get_heads_file_path(config) for file_ in (old_head_file, old_heads_file): fileutils.delete_if_exists(file_) def _get_current_database_heads(config): with DBConnection(config.neutron_config.database.connection) as conn: opts = { 'version_table': get_alembic_version_table(config) } context = alembic_migration.MigrationContext.configure( conn, opts=opts) return context.get_current_heads() def has_offline_migrations(config, cmd): heads_map = _get_heads_map(config) if heads_map[CONTRACT_BRANCH] not in _get_current_database_heads(config): # If there is at least one contract revision not applied to database, # it means we should shut down all neutron-server instances before # proceeding with upgrade. project = config.get_main_option('neutron_project') alembic_util.msg(_('Need to apply migrations from %(project)s ' 'contract branch. This will require all Neutron ' 'server instances to be shutdown before ' 'proceeding with the upgrade.') % {"project": project}) return True return False def add_command_parsers(subparsers): for name in ['current', 'history', 'branches', 'heads']: parser = add_alembic_subparser(subparsers, name) parser.set_defaults(func=do_generic_show) parser.add_argument('--verbose', action='store_true', help='Display more verbose output for the ' 'specified command') help_text = (getattr(alembic_command, 'branches').__doc__ + ' and validate head file') parser = subparsers.add_parser('check_migration', help=help_text) parser.set_defaults(func=do_check_migration) parser = add_alembic_subparser(subparsers, 'upgrade') parser.add_argument('--delta', type=int) parser.add_argument('--sql', action='store_true') parser.add_argument('revision', nargs='?') parser.add_argument('--mysql-engine', default='', help='Change MySQL storage engine of current ' 'existing tables') add_branch_options(parser) parser.set_defaults(func=do_upgrade) parser = subparsers.add_parser('downgrade', help="(No longer supported)") parser.add_argument('None', nargs='?', help="Downgrade not supported") parser.set_defaults(func=no_downgrade) parser = add_alembic_subparser(subparsers, 'stamp') parser.add_argument('--sql', action='store_true') parser.add_argument('revision') parser.set_defaults(func=do_stamp) parser = add_alembic_subparser(subparsers, 'revision') parser.add_argument('-m', '--message') parser.add_argument('--sql', action='store_true') group = add_branch_options(parser) group.add_argument('--autogenerate', action='store_true') parser.set_defaults(func=do_revision) parser = subparsers.add_parser( 'has_offline_migrations', help='Determine whether there are pending migration scripts that ' 'require full shutdown for all services that directly access ' 'database.') parser.set_defaults(func=has_offline_migrations) command_opt = cfg.SubCommandOpt('command', title='Command', help=_('Available commands'), handler=add_command_parsers) CONF.register_cli_opt(command_opt) def _get_project_base(config): '''Return the base python namespace name for a project.''' script_location = config.get_main_option('script_location') return script_location.split(':')[0].split('.')[0] def _get_package_root_dir(config): root_module = importutils.try_import(_get_project_base(config)) if not root_module: project = config.get_main_option('neutron_project') alembic_util.err(_("Failed to locate source for %s.") % project) # The root_module.__file__ property is a path like # '/opt/stack/networking-foo/networking_foo/__init__.py' # We return just # '/opt/stack/networking-foo' return os.path.dirname(os.path.dirname(root_module.__file__)) def _get_root_versions_dir(config): '''Return root directory that contains all migration rules.''' root_dir = _get_package_root_dir(config) script_location = config.get_main_option('script_location') # Script location is something like: # 'project_base.db.migration:alembic_migrations' # Convert it to: # 'project_base/db/migration/alembic_migrations/versions' part1, part2 = script_location.split(':') parts = part1.split('.') + part2.split('.') + ['versions'] # Return the absolute path to the versions dir return os.path.join(root_dir, *parts) def _get_head_file_path(config): '''Return the path of the file that contains single head.''' return os.path.join( _get_root_versions_dir(config), HEAD_FILENAME) def _get_heads_file_path(config): ''' Return the path of the file that was once used to maintain the list of latest heads. ''' return os.path.join( _get_root_versions_dir(config), HEADS_FILENAME) def _get_contract_head_file_path(config): ''' Return the path of the file that is used to maintain contract head ''' return os.path.join( _get_root_versions_dir(config), CONTRACT_HEAD_FILENAME) def _get_expand_head_file_path(config): ''' Return the path of the file that is used to maintain expand head ''' return os.path.join( _get_root_versions_dir(config), EXPAND_HEAD_FILENAME) def _get_version_branch_path(config, release=None, branch=None): version_path = _get_root_versions_dir(config) if branch and release: return os.path.join(version_path, release, branch) return version_path def _set_version_locations(config): '''Make alembic see all revisions in all migration branches.''' split_branches = False version_paths = [_get_version_branch_path(config)] for release in RELEASES: for branch in MIGRATION_BRANCHES: version_path = _get_version_branch_path(config, release, branch) if split_branches or os.path.exists(version_path): split_branches = True version_paths.append(version_path) config.set_main_option('version_locations', ' '.join(version_paths)) def _get_installed_entrypoint(subproject): '''Get the entrypoint for the subproject, which must be installed.''' if subproject not in migration_cli.migration_entrypoints: alembic_util.err(_('Package %s not installed') % subproject) return migration_cli.migration_entrypoints[subproject] def _get_subproject_script_location(subproject): '''Get the script location for the installed subproject.''' entrypoint = _get_installed_entrypoint(subproject) return ':'.join([entrypoint.module_name, entrypoint.attrs[0]]) def _get_subproject_base(subproject): '''Get the import base name for the installed subproject.''' entrypoint = _get_installed_entrypoint(subproject) return entrypoint.module_name.split('.')[0] def get_alembic_version_table(config): script_dir = alembic_script.ScriptDirectory.from_config(config) alembic_version_table = [None] def alembic_version_table_from_env(rev, context): alembic_version_table[0] = context.version_table return [] with environment.EnvironmentContext(config, script_dir, fn=alembic_version_table_from_env): script_dir.run_env() return alembic_version_table[0] def get_alembic_configs(): '''Return a list of alembic configs, one per project. ''' # Get the script locations for the specified or installed projects. # Which projects to get script locations for is determined by the CLI # options as follows: # --subproject P # only subproject P (where P can be neutron) # (none specified) # neutron and all installed subprojects script_locations = {} if CONF.subproject: script_location = _get_subproject_script_location(CONF.subproject) script_locations[CONF.subproject] = script_location else: for subproject in migration_cli.migration_entrypoints: script_locations[subproject] = _get_subproject_script_location( subproject) # Return a list of alembic configs from the projects in the # script_locations dict. If neutron is in the list it is first. configs = [] project_seq = sorted(script_locations.keys()) # Core neutron must be the first project if there is more than one if len(project_seq) > 1 and 'neutron' in project_seq: project_seq.insert(0, project_seq.pop(project_seq.index('neutron'))) for project in project_seq: config = alembic_config.Config(neutron_alembic_ini) config.set_main_option('neutron_project', project) script_location = script_locations[project] config.set_main_option('script_location', script_location) _set_version_locations(config) config.neutron_config = CONF configs.append(config) return configs def get_neutron_config(): # Neutron's alembic config is always the first one return get_alembic_configs()[0] def run_sanity_checks(config, revision): script_dir = alembic_script.ScriptDirectory.from_config(config) def check_sanity(rev, context): # TODO(ihrachyshka): here we use internal API for alembic; we may need # alembic to expose implicit_base= argument into public # iterate_revisions() call for script in script_dir.revision_map.iterate_revisions( revision, rev, implicit_base=True): if hasattr(script.module, 'check_sanity'): script.module.check_sanity(context.connection) return [] with environment.EnvironmentContext(config, script_dir, fn=check_sanity, starting_rev=None, destination_rev=revision): script_dir.run_env() def get_engine_config(): return [obj for obj in migration_cli.DB_OPTS if obj.name == 'engine'] def main(): # Interpret the config file for Python logging. # This line sets up loggers basically. logging_config.fileConfig(neutron_alembic_ini) CONF(project='neutron') return_val = False for config in get_alembic_configs(): #TODO(gongysh) enable logging return_val |= bool(CONF.command.func(config, CONF.command.name)) if CONF.command.name == 'has_offline_migrations' and not return_val: alembic_util.msg(_('No offline migrations pending.')) return return_val neutron-12.1.1/neutron/db/tag_db.py0000664000175000017500000000632513553660047017200 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from sqlalchemy.orm import aliased from neutron.db.models import tag as tag_model def _get_tag_list(tag_strings): tags = set() for tag_str in tag_strings: tags |= set(tag_str.split(',')) return list(tags) def apply_tag_filters(model, query, filters): """Apply tag filters There are four types of filter: `tags` -- One or more strings that will be used to filter results in an AND expression: T1 AND T2 `tags-any` -- One or more strings that will be used to filter results in an OR expression: T1 OR T2 `not-tags` -- One or more strings that will be used to filter results in a NOT AND expression: NOT (T1 AND T2) `not-tags-any` -- One or more strings that will be used to filter results in a NOT OR expression: NOT (T1 OR T2) Note: tag values can be specified comma separated string. for example, 'GET /v2.0/networks?tags-any=red,blue' is equivalent to 'GET /v2.0/networks?tags-any=red&tags-any=blue' it means 'red' or 'blue'. """ if 'tags' in filters: tags = _get_tag_list(filters.pop('tags')) first_tag = tags.pop(0) query = query.join(tag_model.Tag, model.standard_attr_id == tag_model.Tag.standard_attr_id) query = query.filter(tag_model.Tag.tag == first_tag) for tag in tags: tag_alias = aliased(tag_model.Tag) query = query.join(tag_alias, model.standard_attr_id == tag_alias.standard_attr_id) query = query.filter(tag_alias.tag == tag) if 'tags-any' in filters: tags = _get_tag_list(filters.pop('tags-any')) query = query.join(tag_model.Tag, model.standard_attr_id == tag_model.Tag.standard_attr_id) query = query.filter(tag_model.Tag.tag.in_(tags)) if 'not-tags' in filters: tags = _get_tag_list(filters.pop('not-tags')) first_tag = tags.pop(0) subq = query.session.query(tag_model.Tag.standard_attr_id) subq = subq.filter(tag_model.Tag.tag == first_tag) for tag in tags: tag_alias = aliased(tag_model.Tag) subq = subq.join(tag_alias, tag_model.Tag.standard_attr_id == tag_alias.standard_attr_id) subq = subq.filter(tag_alias.tag == tag) query = query.filter(~model.standard_attr_id.in_(subq)) if 'not-tags-any' in filters: tags = _get_tag_list(filters.pop('not-tags-any')) subq = query.session.query(tag_model.Tag.standard_attr_id) subq = subq.filter(tag_model.Tag.tag.in_(tags)) query = query.filter(~model.standard_attr_id.in_(subq)) return query neutron-12.1.1/neutron/db/servicetype_db.py0000664000175000017500000001030513553660047020760 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from itertools import chain from oslo_log import log as logging from neutron.objects import servicetype as servicetype_obj from neutron.services import provider_configuration as pconf LOG = logging.getLogger(__name__) class ServiceTypeManager(object): """Manage service type objects in Neutron.""" _instance = None @classmethod def get_instance(cls): if cls._instance is None: cls._instance = cls() return cls._instance def __init__(self): self.config = {} def add_provider_configuration(self, service_type, configuration): """Add or update the provider configuration for the service type.""" LOG.debug('Adding provider configuration for service %s', service_type) self.config.update({service_type: configuration}) def get_service_providers(self, context, filters=None, fields=None): if filters and 'service_type' in filters: return list( chain.from_iterable(self.config[svc_type]. get_service_providers(filters, fields) for svc_type in filters['service_type'] if svc_type in self.config) ) return list( chain.from_iterable( self.config[p].get_service_providers(filters, fields) for p in self.config) ) def get_default_service_provider(self, context, service_type): """Return the default provider for a given service type.""" filters = {'service_type': [service_type], 'default': [True]} providers = self.get_service_providers(context, filters=filters) # By construction we expect at most a single item in provider if not providers: raise pconf.DefaultServiceProviderNotFound( service_type=service_type ) return providers[0] def get_provider_names_by_resource_ids(self, context, resource_ids): objs = servicetype_obj.ProviderResourceAssociation.get_objects( context, resource_id=resource_ids) return {rec.resource_id: rec.provider_name for rec in objs} def add_resource_association(self, context, service_type, provider_name, resource_id): r = self.get_service_providers(context, filters={'service_type': [service_type], 'name': [provider_name]}) if not r: raise pconf.ServiceProviderNotFound(provider=provider_name, service_type=service_type) # we don't actually need service type for association. # resource_id is unique and belongs to specific service # which knows its type servicetype_obj.ProviderResourceAssociation( context, provider_name=provider_name, resource_id=resource_id).create() # NOTE(blogan): the ProviderResourceAssociation relationship will not # be populated if a resource was created before this. The expire_all # will force the session to go retrieve the new data when that # resource will be read again. It has been suggested that we can # crawl through everything in the mapper to find the resource with # the ID that matches resource_id and expire that one, but we can # just start with this. context.session.expire_all() def del_resource_associations(self, context, resource_ids): if not resource_ids: return servicetype_obj.ProviderResourceAssociation.delete_objects( context, resource_id=resource_ids) neutron-12.1.1/neutron/db/_resource_extend.py0000664000175000017500000001171513553660047021314 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ NOTE: This module shall not be used by external projects. It will be moved to neutron-lib in due course, and then it can be used from there. """ import collections import inspect from neutron.common import utils # This dictionary will store methods for extending API resources. # Extensions can add their own methods by invoking register_funcs(). _resource_extend_functions = { # : [, , ...], # : [, , ...], # ... } # This dictionary will store @extends decorated methods with a list of # resources that each method will extend on class initialization. _DECORATED_EXTEND_METHODS = collections.defaultdict(list) def register_funcs(resource, funcs): """Add functions to extend a resource. :param resource: A resource collection name. :type resource: str :param funcs: A list of functions. :type funcs: list of callable These functions take a resource dict and a resource object and update the resource dict with extension data (possibly retrieved from the resource db object). def _extend_foo_with_bar(foo_res, foo_db): foo_res['bar'] = foo_db.bar_info # example return foo_res """ funcs = [utils.make_weak_ref(f) if callable(f) else f for f in funcs] _resource_extend_functions.setdefault(resource, []).extend(funcs) def get_funcs(resource): """Retrieve a list of functions extending a resource. :param resource: A resource collection name. :type resource: str :return: A list (possibly empty) of functions extending resource. :rtype: list of callable """ return _resource_extend_functions.get(resource, []) def apply_funcs(resource_type, response, db_object): for func in get_funcs(resource_type): resolved_func = utils.resolve_ref(func) if resolved_func: resolved_func(response, db_object) def extends(resources): """Use to decorate methods on classes before initialization. Any classes that use this must themselves be decorated with the @has_resource_extenders decorator to setup the __new__ method to actually register the instance methods after initialization. :param resources: Resource collection names. The decorated method will be registered with each resource as an extend function. :type resources: list of str """ def decorator(method): _DECORATED_EXTEND_METHODS[method].extend(resources) return method return decorator def has_resource_extenders(klass): """Decorator to setup __new__ method in classes to extend resources. Any method decorated with @extends above is an unbound method on a class. This decorator sets up the class __new__ method to add the bound method to _resource_extend_functions after object instantiation. """ orig_new = klass.__new__ new_inherited = '__new__' not in klass.__dict__ @staticmethod def replacement_new(cls, *args, **kwargs): if new_inherited: # class didn't define __new__ so we need to call inherited __new__ super_new = super(klass, cls).__new__ if super_new is object.__new__: # object.__new__ doesn't accept args nor kwargs instance = super_new(cls) else: instance = super_new(cls, *args, **kwargs) else: instance = orig_new(cls, *args, **kwargs) if getattr(instance, '_DECORATED_METHODS_REGISTERED', False): # Avoid running this logic twice for classes inheriting other # classes with this same decorator. Only one needs to execute # to subscribe all decorated methods. return instance for name, unbound_method in inspect.getmembers(cls): if (not inspect.ismethod(unbound_method) and not inspect.isfunction(unbound_method)): continue # Handle py27/py34 difference method = getattr(unbound_method, 'im_func', unbound_method) if method not in _DECORATED_EXTEND_METHODS: continue for resource in _DECORATED_EXTEND_METHODS[method]: # Register the bound method for the resourse register_funcs(resource, [method]) setattr(instance, '_DECORATED_METHODS_REGISTERED', True) return instance klass.__new__ = replacement_new return klass neutron-12.1.1/neutron/db/extradhcpopt_db.py0000664000175000017500000001335213553660047021130 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import extra_dhcp_opt as edo_ext from neutron_lib.api.definitions import port as port_def from neutron.db import _resource_extend as resource_extend from neutron.db import api as db_api from neutron.objects.port.extensions import extra_dhcp_opt as obj_extra_dhcp @resource_extend.has_resource_extenders class ExtraDhcpOptMixin(object): """Mixin class to add extra options to the DHCP opts file and associate them to a port. """ def _is_valid_opt_value(self, opt_name, opt_value): # If the dhcp opt is blank-able, it shouldn't be saved to the DB in # case that the value is None if opt_name in edo_ext.VALID_BLANK_EXTRA_DHCP_OPTS: return opt_value is not None # Otherwise, it shouldn't be saved to the DB in case that the value # is None or empty return bool(opt_value) def _process_port_create_extra_dhcp_opts(self, context, port, extra_dhcp_opts): if not extra_dhcp_opts: return port with db_api.context_manager.writer.using(context): for dopt in extra_dhcp_opts: if self._is_valid_opt_value(dopt['opt_name'], dopt['opt_value']): ip_version = dopt.get('ip_version', 4) extra_dhcp_obj = obj_extra_dhcp.ExtraDhcpOpt( context, port_id=port['id'], opt_name=dopt['opt_name'], opt_value=dopt['opt_value'], ip_version=ip_version) extra_dhcp_obj.create() return self._extend_port_extra_dhcp_opts_dict(context, port) def _extend_port_extra_dhcp_opts_dict(self, context, port): port[edo_ext.EXTRADHCPOPTS] = self._get_port_extra_dhcp_opts_binding( context, port['id']) def _get_port_extra_dhcp_opts_binding(self, context, port_id): opts = obj_extra_dhcp.ExtraDhcpOpt.get_objects( context, port_id=port_id) # TODO(mhickey): When port serilization is available then # the object list should be returned instead return [{'opt_name': r.opt_name, 'opt_value': r.opt_value, 'ip_version': r.ip_version} for r in opts] def _update_extra_dhcp_opts_on_port(self, context, id, port, updated_port=None): # It is not necessary to update in a transaction, because # its called from within one from ovs_neutron_plugin. dopts = port['port'].get(edo_ext.EXTRADHCPOPTS) if dopts: opts = obj_extra_dhcp.ExtraDhcpOpt.get_objects( context, port_id=id) # if there are currently no dhcp_options associated to # this port, Then just insert the new ones and be done. with db_api.context_manager.writer.using(context): for upd_rec in dopts: for opt in opts: if (opt['opt_name'] == upd_rec['opt_name'] and opt['ip_version'] == upd_rec.get( 'ip_version', 4)): # to handle deleting of a opt from the port. if upd_rec['opt_value'] is None: opt.delete() else: if (self._is_valid_opt_value( opt['opt_name'], upd_rec['opt_value']) and opt['opt_value'] != upd_rec['opt_value']): opt['opt_value'] = upd_rec['opt_value'] opt.update() break else: if self._is_valid_opt_value( upd_rec['opt_name'], upd_rec['opt_value']): ip_version = upd_rec.get('ip_version', 4) extra_dhcp_obj = obj_extra_dhcp.ExtraDhcpOpt( context, port_id=id, opt_name=upd_rec['opt_name'], opt_value=upd_rec['opt_value'], ip_version=ip_version) extra_dhcp_obj.create() if updated_port: edolist = self._get_port_extra_dhcp_opts_binding(context, id) updated_port[edo_ext.EXTRADHCPOPTS] = edolist return bool(dopts) @staticmethod @resource_extend.extends([port_def.COLLECTION_NAME]) def _extend_port_dict_extra_dhcp_opt(res, port): res[edo_ext.EXTRADHCPOPTS] = [{'opt_name': dho.opt_name, 'opt_value': dho.opt_value, 'ip_version': dho.ip_version} for dho in port.dhcp_opts] return res neutron-12.1.1/neutron/db/l3_attrs_db.py0000664000175000017500000000556613553660047020166 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.api.validators import availability_zone as az_validator from oslo_config import cfg from neutron._i18n import _ from neutron.db import _resource_extend as resource_extend from neutron.db.models import l3_attrs def get_attr_info(): """Returns api visible attr names and their default values.""" return {'distributed': {'default': cfg.CONF.router_distributed}, 'ha': {'default': cfg.CONF.l3_ha}, 'ha_vr_id': {'default': 0}, 'availability_zone_hints': { 'default': '[]', 'transform_to_db': az_validator.convert_az_list_to_string, 'transform_from_db': az_validator.convert_az_string_to_list} } @resource_extend.has_resource_extenders class ExtraAttributesMixin(object): """Mixin class to enable router's extra attributes.""" @staticmethod @resource_extend.extends([l3_apidef.ROUTERS]) def _extend_extra_router_dict(router_res, router_db): extra_attrs = router_db['extra_attributes'] or {} for name, info in get_attr_info().items(): from_db = info.get('transform_from_db', lambda x: x) router_res[name] = from_db(extra_attrs.get(name, info['default'])) def _ensure_extra_attr_model(self, context, router_db): if not router_db['extra_attributes']: kwargs = {k: v['default'] for k, v in get_attr_info().items()} kwargs['router_id'] = router_db['id'] new = l3_attrs.RouterExtraAttributes(**kwargs) context.session.add(new) router_db['extra_attributes'] = new def set_extra_attr_value(self, context, router_db, key, value): # set a single value explicitly with context.session.begin(subtransactions=True): if key in get_attr_info(): info = get_attr_info()[key] to_db = info.get('transform_to_db', lambda x: x) self._ensure_extra_attr_model(context, router_db) router_db['extra_attributes'].update({key: to_db(value)}) return raise RuntimeError(_("Tried to set a key '%s' that doesn't exist " "in the extra attributes table.") % key) neutron-12.1.1/neutron/db/metering/0000775000175000017500000000000013553660156017213 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/metering/metering_rpc.py0000664000175000017500000000412013553660047022237 0ustar zuulzuul00000000000000# Copyright (C) 2014 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import constants as consts from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_log import log as logging import oslo_messaging from neutron.common import utils LOG = logging.getLogger(__name__) class MeteringRpcCallbacks(object): target = oslo_messaging.Target(version='1.0') def __init__(self, meter_plugin): self.meter_plugin = meter_plugin def get_sync_data_metering(self, context, **kwargs): l3_plugin = directory.get_plugin(plugin_constants.L3) if not l3_plugin: return metering_data = self.meter_plugin.get_sync_data_metering(context) host = kwargs.get('host') if not utils.is_extension_supported( l3_plugin, consts.L3_AGENT_SCHEDULER_EXT_ALIAS) or not host: return metering_data else: agents = l3_plugin.get_l3_agents(context, filters={'host': [host]}) if not agents: LOG.error('Unable to find agent on host %s.', host) return router_ids = [] for agent in agents: routers = l3_plugin.list_routers_on_l3_agent(context, agent.id) router_ids += [router['id'] for router in routers['routers']] if not router_ids: return else: return [ router for router in metering_data if router['id'] in router_ids ] neutron-12.1.1/neutron/db/metering/__init__.py0000664000175000017500000000000013553660046021310 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/metering/metering_db.py0000664000175000017500000002340413553660047022046 0ustar zuulzuul00000000000000# Copyright (C) 2013 eNovance SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron_lib.exceptions import metering as metering_exc from oslo_db import exception as db_exc from oslo_utils import uuidutils from neutron.api.rpc.agentnotifiers import metering_rpc_agent_api from neutron.common import constants from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.db import common_db_mixin as base_db from neutron.db import l3_dvr_db from neutron.db.models import metering as metering_models from neutron.extensions import metering from neutron.objects import base as base_obj from neutron.objects import metering as metering_objs from neutron.objects import router as l3_obj class MeteringDbMixin(metering.MeteringPluginBase, base_db.CommonDbMixin): def __init__(self): self.meter_rpc = metering_rpc_agent_api.MeteringAgentNotifyAPI() @staticmethod def _make_metering_label_dict(metering_label, fields=None): res = {'id': metering_label['id'], 'name': metering_label['name'], 'description': metering_label['description'], 'shared': metering_label['shared'], 'tenant_id': metering_label['tenant_id']} return db_utils.resource_fields(res, fields) def create_metering_label(self, context, metering_label): m = metering_label['metering_label'] metering_obj = metering_objs.MeteringLabel( context, id=uuidutils.generate_uuid(), description=m['description'], project_id=m['tenant_id'], name=m['name'], shared=m['shared']) metering_obj.create() return self._make_metering_label_dict(metering_obj) def _get_metering_label(self, context, label_id): metering_label = metering_objs.MeteringLabel.get_object(context, id=label_id) if not metering_label: raise metering_exc.MeteringLabelNotFound(label_id=label_id) return metering_label def delete_metering_label(self, context, label_id): deleted = metering_objs.MeteringLabel.delete_objects( context, id=label_id) if not deleted: raise metering_exc.MeteringLabelNotFound(label_id=label_id) def get_metering_label(self, context, label_id, fields=None): return self._make_metering_label_dict( self._get_metering_label(context, label_id), fields) def get_metering_labels(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): filters = filters or {} pager = base_obj.Pager(sorts, limit, page_reverse, marker) metering_labels = metering_objs.MeteringLabel.get_objects(context, _pager=pager, **filters) return [self._make_metering_label_dict(ml) for ml in metering_labels] @staticmethod def _make_metering_label_rule_dict(metering_label_rule, fields=None): res = {'id': metering_label_rule['id'], 'metering_label_id': metering_label_rule['metering_label_id'], 'direction': metering_label_rule['direction'], 'remote_ip_prefix': metering_label_rule['remote_ip_prefix'], 'excluded': metering_label_rule['excluded']} return db_utils.resource_fields(res, fields) def get_metering_label_rules(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): filters = filters or {} pager = base_obj.Pager(sorts, limit, page_reverse, marker) metering_label_rules = metering_objs.MeteringLabelRule.get_objects( context, _pager=pager, **filters) return [self._make_metering_label_rule_dict(mlr) for mlr in metering_label_rules] def _get_metering_label_rule(self, context, rule_id): metering_label_rule = metering_objs.MeteringLabelRule.get_object( context, id=rule_id) if not metering_label_rule: raise metering_exc.MeteringLabelRuleNotFound(rule_id=rule_id) return metering_label_rule def get_metering_label_rule(self, context, rule_id, fields=None): return self._make_metering_label_rule_dict( self._get_metering_label_rule(context, rule_id), fields) def _validate_cidr(self, context, label_id, remote_ip_prefix, direction, excluded): r_ips = self.get_metering_label_rules(context, filters={'metering_label_id': [label_id], 'direction': [direction], 'excluded': [excluded]}, fields=['remote_ip_prefix']) cidrs = [r['remote_ip_prefix'] for r in r_ips] new_cidr_ipset = netaddr.IPSet([remote_ip_prefix]) if (netaddr.IPSet(cidrs) & new_cidr_ipset): raise metering_exc.MeteringLabelRuleOverlaps( remote_ip_prefix=remote_ip_prefix) def create_metering_label_rule(self, context, metering_label_rule): m = metering_label_rule['metering_label_rule'] try: with db_api.context_manager.writer.using(context): label_id = m['metering_label_id'] ip_prefix = m['remote_ip_prefix'] direction = m['direction'] excluded = m['excluded'] self._validate_cidr(context, label_id, ip_prefix, direction, excluded) rule = metering_objs.MeteringLabelRule( context, id=uuidutils.generate_uuid(), metering_label_id=label_id, direction=direction, excluded=m['excluded'], remote_ip_prefix=netaddr.IPNetwork(ip_prefix)) rule.create() except db_exc.DBReferenceError: raise metering_exc.MeteringLabelNotFound(label_id=label_id) return self._make_metering_label_rule_dict(rule) def delete_metering_label_rule(self, context, rule_id): with db_api.context_manager.writer.using(context): rule = self._get_metering_label_rule(context, rule_id) rule.delete() return self._make_metering_label_rule_dict(rule) def _get_metering_rules_dict(self, metering_label): rules = [] for rule in metering_label.rules: rule_dict = self._make_metering_label_rule_dict(rule) rules.append(rule_dict) return rules def _make_router_dict(self, router): distributed = l3_dvr_db.is_distributed_router(router) res = {'id': router['id'], 'name': router['name'], 'tenant_id': router['tenant_id'], 'admin_state_up': router['admin_state_up'], 'status': router['status'], 'gw_port_id': router['gw_port_id'], 'distributed': distributed, constants.METERING_LABEL_KEY: []} return res def _process_sync_metering_data(self, context, labels): routers = None routers_dict = {} for label in labels: if label.shared: if not routers: routers = l3_obj.Router.get_objects(context) else: routers = label.routers for router in routers: if not router['admin_state_up']: continue router_dict = routers_dict.get( router['id'], self._make_router_dict(router)) rules = self._get_metering_rules_dict(label) data = {'id': label['id'], 'rules': rules} router_dict[constants.METERING_LABEL_KEY].append(data) routers_dict[router['id']] = router_dict return list(routers_dict.values()) def get_sync_data_for_rule(self, context, rule): label = context.session.query( metering_models.MeteringLabel).get( rule['metering_label_id']) if label.shared: routers = l3_obj.Router.get_objects(context) else: routers = label.routers routers_dict = {} for router in routers: router_dict = routers_dict.get(router['id'], self._make_router_dict(router)) data = {'id': label['id'], 'rule': rule} router_dict[constants.METERING_LABEL_KEY].append(data) routers_dict[router['id']] = router_dict return list(routers_dict.values()) def get_sync_data_metering(self, context, label_id=None): labels = context.session.query(metering_models.MeteringLabel) if label_id: labels = labels.filter( metering_models.MeteringLabel.id == label_id) return self._process_sync_metering_data(context, labels) neutron-12.1.1/neutron/db/external_net_db.py0000664000175000017500000002637113553660047021120 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import external_net as extnet_apidef from neutron_lib.api.definitions import network as net_def from neutron_lib.api import validators from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import external_net as extnet_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from sqlalchemy.sql import expression as expr from neutron._i18n import _ from neutron.db import _model_query as model_query from neutron.db import _resource_extend as resource_extend from neutron.db import _utils as db_utils from neutron.db.models import l3 as l3_models from neutron.db import models_v2 from neutron.db import rbac_db_models as rbac_db from neutron.extensions import rbac as rbac_ext from neutron.objects import network as net_obj DEVICE_OWNER_ROUTER_GW = constants.DEVICE_OWNER_ROUTER_GW def _network_filter_hook(context, original_model, conditions): if conditions is not None and not hasattr(conditions, '__iter__'): conditions = (conditions, ) # Apply the external network filter only in non-admin and non-advsvc # context if db_utils.model_query_scope_is_project(context, original_model): # the table will already be joined to the rbac entries for the # shared check so we don't need to worry about ensuring that rbac_model = original_model.rbac_entries.property.mapper.class_ tenant_allowed = ( (rbac_model.action == 'access_as_external') & (rbac_model.target_tenant == context.tenant_id) | (rbac_model.target_tenant == '*')) conditions = expr.or_(tenant_allowed, *conditions) return conditions def _network_result_filter_hook(query, filters): vals = filters and filters.get(extnet_apidef.EXTERNAL, []) if not vals: return query if vals[0]: return query.filter(models_v2.Network.external.has()) return query.filter(~models_v2.Network.external.has()) @resource_extend.has_resource_extenders @registry.has_registry_receivers class External_net_db_mixin(object): """Mixin class to add external network methods to db_base_plugin_v2.""" def __new__(cls, *args, **kwargs): model_query.register_hook( models_v2.Network, "external_net", query_hook=None, filter_hook=_network_filter_hook, result_filters=_network_result_filter_hook) return super(External_net_db_mixin, cls).__new__(cls, *args, **kwargs) def _network_is_external(self, context, net_id): return net_obj.ExternalNetwork.objects_exist( context, network_id=net_id) @staticmethod @resource_extend.extends([net_def.COLLECTION_NAME]) def _extend_network_dict_l3(network_res, network_db): # Comparing with None for converting uuid into bool network_res[extnet_apidef.EXTERNAL] = network_db.external is not None return network_res def _process_l3_create(self, context, net_data, req_data): external = req_data.get(extnet_apidef.EXTERNAL) external_set = validators.is_attr_set(external) if not external_set: return if external: net_obj.ExternalNetwork( context, network_id=net_data['id']).create() context.session.add(rbac_db.NetworkRBAC( object_id=net_data['id'], action='access_as_external', target_tenant='*', tenant_id=net_data['tenant_id'])) net_data[extnet_apidef.EXTERNAL] = external def _process_l3_update(self, context, net_data, req_data, allow_all=True): new_value = req_data.get(extnet_apidef.EXTERNAL) net_id = net_data['id'] if not validators.is_attr_set(new_value): return if net_data.get(extnet_apidef.EXTERNAL) == new_value: return if new_value: net_obj.ExternalNetwork( context, network_id=net_id).create() net_data[extnet_apidef.EXTERNAL] = True if allow_all: context.session.add(rbac_db.NetworkRBAC( object_id=net_id, action='access_as_external', target_tenant='*', tenant_id=net_data['tenant_id'])) else: # must make sure we do not have any external gateway ports # (and thus, possible floating IPs) on this network before # allow it to be update to external=False if context.session.query(models_v2.Port.id).filter_by( device_owner=constants.DEVICE_OWNER_ROUTER_GW, network_id=net_data['id']).first(): raise extnet_exc.ExternalNetworkInUse(net_id=net_id) net_obj.ExternalNetwork.delete_objects( context, network_id=net_id) for rbdb in (context.session.query(rbac_db.NetworkRBAC).filter_by( object_id=net_id, action='access_as_external')): context.session.delete(rbdb) net_data[extnet_apidef.EXTERNAL] = False def _process_l3_delete(self, context, network_id): l3plugin = directory.get_plugin(plugin_constants.L3) if l3plugin: l3plugin.delete_disassociated_floatingips(context, network_id) def get_external_network_id(self, context): nets = self.get_networks(context, {extnet_apidef.EXTERNAL: [True]}) if len(nets) > 1: raise n_exc.TooManyExternalNetworks() else: return nets[0]['id'] if nets else None @registry.receives('rbac-policy', [events.BEFORE_CREATE]) def _process_ext_policy_create(self, resource, event, trigger, context, object_type, policy, **kwargs): if (object_type != 'network' or policy['action'] != 'access_as_external'): return net = self.get_network(context, policy['object_id']) if not context.is_admin and net['tenant_id'] != context.tenant_id: msg = _("Only admins can manipulate policies on networks they " "do not own") raise n_exc.InvalidInput(error_message=msg) if not self._network_is_external(context, policy['object_id']): # we automatically convert the network into an external network self._process_l3_update(context, net, {extnet_apidef.EXTERNAL: True}, allow_all=False) @registry.receives('rbac-policy', [events.AFTER_DELETE]) def _process_ext_policy_delete(self, resource, event, trigger, context, object_type, policy, **kwargs): if (object_type != 'network' or policy['action'] != 'access_as_external'): return # If the network still have rbac policies, we should not # update external attribute. if context.session.query(rbac_db.NetworkRBAC.object_id).filter( rbac_db.NetworkRBAC.object_id == policy['object_id'], rbac_db.NetworkRBAC.action == 'access_as_external').count(): return net = self.get_network(context, policy['object_id']) self._process_l3_update(context, net, {extnet_apidef.EXTERNAL: False}) @registry.receives('rbac-policy', (events.BEFORE_UPDATE, events.BEFORE_DELETE)) def _validate_ext_not_in_use_by_tenant(self, resource, event, trigger, context, object_type, policy, **kwargs): if (object_type != 'network' or policy['action'] != 'access_as_external'): return new_tenant = None if event == events.BEFORE_UPDATE: new_tenant = kwargs['policy_update']['target_tenant'] if new_tenant == policy['target_tenant']: # nothing to validate if the tenant didn't change return ports = context.session.query(models_v2.Port.id).filter_by( device_owner=DEVICE_OWNER_ROUTER_GW, network_id=policy['object_id']) router = context.session.query(l3_models.Router).filter( l3_models.Router.gw_port_id.in_(ports)) rbac = rbac_db.NetworkRBAC if policy['target_tenant'] != '*': router = router.filter( l3_models.Router.tenant_id == policy['target_tenant']) # if there is a wildcard entry we can safely proceed without the # router lookup because they will have access either way if context.session.query(rbac_db.NetworkRBAC.object_id).filter( rbac.object_id == policy['object_id'], rbac.action == 'access_as_external', rbac.target_tenant == '*').count(): return else: # deleting the wildcard is okay as long as the tenants with # attached routers have their own entries and the network is # not the default external network. if net_obj.ExternalNetwork.objects_exist( context, network_id=policy['object_id'], is_default=True): msg = _("Default external networks must be shared to " "everyone.") raise rbac_ext.RbacPolicyInUse(object_id=policy['object_id'], details=msg) tenants_with_entries = ( context.session.query(rbac.target_tenant). filter(rbac.object_id == policy['object_id'], rbac.action == 'access_as_external', rbac.target_tenant != '*')) router = router.filter( ~l3_models.Router.tenant_id.in_(tenants_with_entries)) if new_tenant: # if this is an update we also need to ignore any router # interfaces that belong to the new target. router = router.filter( l3_models.Router.tenant_id != new_tenant) if router.count(): msg = _("There are routers attached to this network that " "depend on this policy for access.") raise rbac_ext.RbacPolicyInUse(object_id=policy['object_id'], details=msg) @registry.receives(resources.NETWORK, [events.BEFORE_DELETE]) def _before_network_delete_handler(self, resource, event, trigger, context, network_id, **kwargs): self._process_l3_delete(context, network_id) neutron-12.1.1/neutron/db/allowedaddresspairs_db.py0000664000175000017500000001454713553660047022466 0ustar zuulzuul00000000000000# Copyright 2013 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef from neutron_lib.api.definitions import port as port_def from neutron_lib.api import validators from neutron_lib.exceptions import allowedaddresspairs as addr_exc from neutron_lib.objects import exceptions from neutron.common import utils from neutron.db import _resource_extend as resource_extend from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.objects.port.extensions import (allowedaddresspairs as obj_addr_pair) @resource_extend.has_resource_extenders class AllowedAddressPairsMixin(object): """Mixin class for allowed address pairs.""" def _process_create_allowed_address_pairs(self, context, port, allowed_address_pairs): if not validators.is_attr_set(allowed_address_pairs): return [] try: with db_api.context_manager.writer.using(context): for address_pair in allowed_address_pairs: # use port.mac_address if no mac address in address pair if 'mac_address' not in address_pair: address_pair['mac_address'] = port['mac_address'] # retain string format as passed through API mac_address = utils.AuthenticEUI( address_pair['mac_address']) ip_address = utils.AuthenticIPNetwork( address_pair['ip_address']) pair_obj = obj_addr_pair.AllowedAddressPair( context, port_id=port['id'], mac_address=mac_address, ip_address=ip_address) pair_obj.create() except exceptions.NeutronDbObjectDuplicateEntry: raise addr_exc.DuplicateAddressPairInRequest( mac_address=address_pair['mac_address'], ip_address=address_pair['ip_address']) return allowed_address_pairs def get_allowed_address_pairs(self, context, port_id): pairs = obj_addr_pair.AllowedAddressPair.get_objects( context, port_id=port_id) return [self._make_allowed_address_pairs_dict(pair.db_obj) for pair in pairs] @staticmethod @resource_extend.extends([port_def.COLLECTION_NAME]) def _extend_port_dict_allowed_address_pairs(port_res, port_db): # If port_db is provided, allowed address pairs will be accessed via # sqlalchemy models. As they're loaded together with ports this # will not cause an extra query. allowed_address_pairs = [ AllowedAddressPairsMixin._make_allowed_address_pairs_dict( address_pair) for address_pair in port_db.allowed_address_pairs] port_res[addr_apidef.ADDRESS_PAIRS] = allowed_address_pairs return port_res def _delete_allowed_address_pairs(self, context, id): obj_addr_pair.AllowedAddressPair.delete_objects( context, port_id=id) @staticmethod def _make_allowed_address_pairs_dict(allowed_address_pairs, fields=None): res = {'mac_address': allowed_address_pairs['mac_address'], 'ip_address': allowed_address_pairs['ip_address']} return db_utils.resource_fields(res, fields) def _has_address_pairs(self, port): return (validators.is_attr_set(port['port'][addr_apidef.ADDRESS_PAIRS]) and port['port'][addr_apidef.ADDRESS_PAIRS] != []) def _check_update_has_allowed_address_pairs(self, port): """Determine if request has an allowed address pair. Return True if the port parameter has a non-empty 'allowed_address_pairs' attribute. Otherwise returns False. """ return (addr_apidef.ADDRESS_PAIRS in port['port'] and self._has_address_pairs(port)) def _check_update_deletes_allowed_address_pairs(self, port): """Determine if request deletes address pair. Return True if port has an allowed address pair and its value is either [] or not is_attr_set, otherwise return False """ return (addr_apidef.ADDRESS_PAIRS in port['port'] and not self._has_address_pairs(port)) def is_address_pairs_attribute_updated(self, port, update_attrs): """Check if the address pairs attribute is being updated. Returns True if there is an update. This can be used to decide if a port update notification should be sent to agents or third party controllers. """ new_pairs = update_attrs.get(addr_apidef.ADDRESS_PAIRS) if new_pairs is None: return False old_pairs = port.get(addr_apidef.ADDRESS_PAIRS) # Missing or unchanged address pairs in attributes mean no update return new_pairs != old_pairs def update_address_pairs_on_port(self, context, port_id, port, original_port, updated_port): """Update allowed address pairs on port. Returns True if an update notification is required. Notification is not done here because other changes on the port may need notification. This method is expected to be called within a transaction. """ new_pairs = port['port'].get(addr_apidef.ADDRESS_PAIRS) if self.is_address_pairs_attribute_updated(original_port, port['port']): updated_port[addr_apidef.ADDRESS_PAIRS] = new_pairs self._delete_allowed_address_pairs(context, port_id) self._process_create_allowed_address_pairs( context, updated_port, new_pairs) return True return False neutron-12.1.1/neutron/db/l3_dvr_db.py0000664000175000017500000016364413553660047017626 0ustar zuulzuul00000000000000# Copyright (c) 2014 OpenStack Foundation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import netaddr from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.api.definitions import portbindings from neutron_lib.api import validators from neutron_lib.callbacks import events from neutron_lib.callbacks import exceptions from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as const from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import agent as agent_exc from neutron_lib.exceptions import l3 as l3_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_concurrency import lockutils from oslo_config import cfg from oslo_log import helpers as log_helper from oslo_log import log as logging from oslo_utils import excutils import six from neutron._i18n import _ from neutron.common import constants as l3_const from neutron.common import utils as n_utils from neutron.conf.db import l3_dvr_db from neutron.db import api as db_api from neutron.db import l3_attrs_db from neutron.db import l3_db from neutron.db.models import allowed_address_pair as aap_models from neutron.db import models_v2 from neutron.ipam import utils as ipam_utils from neutron.objects import agent as ag_obj from neutron.objects import base as base_obj from neutron.objects import l3agent as rb_obj from neutron.objects import router as l3_obj from neutron.plugins.common import utils as p_utils LOG = logging.getLogger(__name__) l3_dvr_db.register_db_l3_dvr_opts() @registry.has_registry_receivers class DVRResourceOperationHandler(object): """Contains callbacks for DVR operations. This can be implemented as a mixin or can be intantiated as a stand-alone object. Either way, it will subscribe itself to the relevant L3 events and use the plugin directory to find the L3 plugin to make calls to it as necessary. """ related_dvr_router_hosts = {} related_dvr_router_routers = {} @property def l3plugin(self): return directory.get_plugin(plugin_constants.L3) @registry.receives(resources.ROUTER, [events.PRECOMMIT_CREATE]) def _set_distributed_flag(self, resource, event, trigger, context, router, router_db, **kwargs): """Event handler to set distributed flag on creation.""" dist = is_distributed_router(router) router['distributed'] = dist self.l3plugin.set_extra_attr_value(context, router_db, 'distributed', dist) def _validate_router_migration(self, context, router_db, router_res): """Allow transition only when admin_state_up=False""" original_distributed_state = router_db.extra_attributes.distributed requested_distributed_state = router_res.get('distributed', None) distributed_changed = ( requested_distributed_state is not None and requested_distributed_state != original_distributed_state) if not distributed_changed: return False if router_db.admin_state_up: msg = _("Cannot change the 'distributed' attribute of active " "routers. Please set router admin_state_up to False " "prior to upgrade") raise n_exc.BadRequest(resource='router', msg=msg) # Notify advanced services of the imminent state transition # for the router. try: kwargs = {'context': context, 'router': router_db} registry.notify( resources.ROUTER, events.BEFORE_UPDATE, self, **kwargs) except exceptions.CallbackFailure as e: # NOTE(armax): preserve old check's behavior if len(e.errors) == 1: raise e.errors[0].error raise l3_exc.RouterInUse(router_id=router_db['id'], reason=e) return True @registry.receives(resources.ROUTER, [events.PRECOMMIT_UPDATE]) def _handle_distributed_migration(self, resource, event, trigger, payload=None): """Event handler for router update migration to distributed.""" if not self._validate_router_migration( payload.context, payload.desired_state, payload.request_body): return migrating_to_distributed = ( not payload.desired_state.extra_attributes.distributed and payload.request_body.get('distributed') is True) if migrating_to_distributed: if payload.states[0]['ha']: old_owner = const.DEVICE_OWNER_HA_REPLICATED_INT else: old_owner = const.DEVICE_OWNER_ROUTER_INTF self.l3plugin._migrate_router_ports( payload.context, payload.desired_state, old_owner=old_owner, new_owner=const.DEVICE_OWNER_DVR_INTERFACE) else: if payload.request_body.get('ha'): new_owner = const.DEVICE_OWNER_HA_REPLICATED_INT else: new_owner = const.DEVICE_OWNER_ROUTER_INTF self.l3plugin._migrate_router_ports( payload.context, payload.desired_state, old_owner=const.DEVICE_OWNER_DVR_INTERFACE, new_owner=new_owner) cur_agents = self.l3plugin.list_l3_agents_hosting_router( payload.context, payload.resource_id)['agents'] for agent in cur_agents: self.l3plugin._unbind_router( payload.context, payload.resource_id, agent['id']) self.l3plugin.set_extra_attr_value( payload.context, payload.desired_state, 'distributed', migrating_to_distributed) @registry.receives(resources.ROUTER, [events.AFTER_UPDATE]) def _delete_snat_interfaces_after_change(self, resource, event, trigger, context, router_id, router, request_attrs, router_db, **kwargs): if (router.get(l3_apidef.EXTERNAL_GW_INFO) and not router['distributed']): old_router = kwargs['old_router'] if old_router and old_router['distributed']: self.delete_csnat_router_interface_ports( context.elevated(), router_db) @registry.receives(resources.ROUTER, [events.AFTER_CREATE, events.AFTER_UPDATE]) def _create_snat_interfaces_after_change(self, resource, event, trigger, context, router_id, router, request_attrs, router_db, **kwargs): if (not router.get(l3_apidef.EXTERNAL_GW_INFO) or not router['distributed']): # we don't care if it's not distributed or not attached to an # external network return if event == events.AFTER_UPDATE: # after an update, we check to see if it was a migration or a # gateway attachment old_router = kwargs['old_router'] do_create = (not old_router['distributed'] or not old_router.get(l3_apidef.EXTERNAL_GW_INFO)) if not do_create: return if not self._create_snat_intf_ports_if_not_exists( context.elevated(), router_db): LOG.debug("SNAT interface ports not created: %s", router_db['id']) return router_db def _get_snat_interface_ports_for_router(self, context, router_id): """Return all existing snat_router_interface ports.""" objs = l3_obj.RouterPort.get_objects( context, router_id=router_id, port_type=const.DEVICE_OWNER_ROUTER_SNAT) # TODO(lujinluo): Need Port as synthetic field ports = [self.l3plugin._core_plugin._make_port_dict(rp.db_obj.port) for rp in objs] return ports def _add_csnat_router_interface_port( self, context, router, network_id, subnets, do_pop=True): """Add SNAT interface to the specified router and subnet.""" port_data = {'tenant_id': '', 'network_id': network_id, 'fixed_ips': subnets, 'device_id': router.id, 'device_owner': const.DEVICE_OWNER_ROUTER_SNAT, 'admin_state_up': True, 'name': ''} snat_port = p_utils.create_port(self._core_plugin, context, {'port': port_data}) if not snat_port: msg = _("Unable to create the SNAT Interface Port") raise n_exc.BadRequest(resource='router', msg=msg) with p_utils.delete_port_on_error( self.l3plugin._core_plugin, context.elevated(), snat_port['id']): l3_obj.RouterPort( context, port_id=snat_port['id'], router_id=router.id, port_type=const.DEVICE_OWNER_ROUTER_SNAT ).create() if do_pop: return self.l3plugin._populate_mtu_and_subnets_for_ports( context, [snat_port]) return snat_port def _create_snat_intf_ports_if_not_exists(self, context, router): """Function to return the snat interface port list. This function will return the snat interface port list if it exists. If the port does not exist it will create new ports and then return the list. """ port_list = self._get_snat_interface_ports_for_router( context, router.id) if port_list: self._populate_mtu_and_subnets_for_ports(context, port_list) return port_list port_list = [] int_ports = ( rp.port for rp in router.attached_ports if rp.port_type == const.DEVICE_OWNER_DVR_INTERFACE ) LOG.info('SNAT interface port list does not exist,' ' so create one: %s', port_list) v6_subnets = [] network = None for intf in int_ports: if intf.fixed_ips: # Passing the subnet for the port to make sure the IP's # are assigned on the right subnet if multiple subnet # exists for fixed_ip in intf['fixed_ips']: ip_version = n_utils.get_ip_version( fixed_ip.get('ip_address')) if ip_version == const.IP_VERSION_4: snat_port = self._add_csnat_router_interface_port( context, router, intf['network_id'], [{'subnet_id': fixed_ip['subnet_id']}], do_pop=False) port_list.append(snat_port) else: v6_subnets.append( {"subnet_id": fixed_ip['subnet_id']}) network = intf['network_id'] if v6_subnets: snat_port = self._add_csnat_router_interface_port( context, router, network, v6_subnets, do_pop=False) port_list.append(snat_port) if port_list: self.l3plugin._populate_mtu_and_subnets_for_ports( context, port_list) return port_list @registry.receives(resources.ROUTER_GATEWAY, [events.AFTER_DELETE]) def _delete_dvr_internal_ports(self, event, trigger, resource, context, router, network_id, new_network_id, **kwargs): """ GW port AFTER_DELETE event handler to cleanup DVR ports. This event is emitted when a router gateway port is being deleted, so go ahead and delete the csnat ports and the floatingip agent gateway port associated with the dvr router. """ if not is_distributed_router(router): return if not new_network_id: self.delete_csnat_router_interface_ports(context.elevated(), router) # NOTE(Swami): Delete the Floatingip agent gateway port # on all hosts when it is the last gateway port in the # given external network. filters = {'network_id': [network_id], 'device_owner': [const.DEVICE_OWNER_ROUTER_GW]} ext_net_gw_ports = self._core_plugin.get_ports( context.elevated(), filters) if not ext_net_gw_ports: self.delete_floatingip_agent_gateway_port( context.elevated(), None, network_id) # Send the information to all the L3 Agent hosts # to clean up the fip namespace as it is no longer required. self.l3plugin.l3_rpc_notifier.delete_fipnamespace_for_ext_net( context, network_id) def delete_floatingip_agent_gateway_port( self, context, host_id, ext_net_id): """Function to delete FIP gateway port with given ext_net_id.""" # delete any fip agent gw port device_filter = {'device_owner': [const.DEVICE_OWNER_AGENT_GW], 'network_id': [ext_net_id]} ports = self._core_plugin.get_ports(context, filters=device_filter) for p in ports: if not host_id or p[portbindings.HOST_ID] == host_id: self._core_plugin.ipam.delete_port(context, p['id']) if host_id: return def _get_ports_for_allowed_address_pair_ip( self, context, network_id, fixed_ip): """Return all active ports associated with the allowed_addr_pair ip.""" query = context.session.query( models_v2.Port).filter( models_v2.Port.id == aap_models.AllowedAddressPair.port_id, aap_models.AllowedAddressPair.ip_address == fixed_ip, models_v2.Port.network_id == network_id, models_v2.Port.admin_state_up == True) # noqa return query.all() @registry.receives(resources.FLOATING_IP, [events.AFTER_UPDATE]) def _create_dvr_floating_gw_port(self, resource, event, trigger, context, router_id, fixed_port_id, floating_ip_id, floating_network_id, fixed_ip_address, **kwargs): """Create floating agent gw port for DVR. Floating IP Agent gateway port will be created when a floatingIP association happens. """ associate_fip = fixed_port_id and floating_ip_id if associate_fip and router_id: admin_ctx = context.elevated() router_dict = self.get_router(admin_ctx, router_id) # Check if distributed router and then create the # FloatingIP agent gateway port if router_dict.get('distributed'): hostid = self._get_dvr_service_port_hostid(context, fixed_port_id) if hostid: # FIXME (Swami): This FIP Agent Gateway port should be # created only once and there should not be a duplicate # for the same host. Until we find a good solution for # augmenting multiple server requests we should use the # existing flow. fip_agent_port = ( self.create_fip_agent_gw_port_if_not_exists( admin_ctx, floating_network_id, hostid)) LOG.debug("FIP Agent gateway port: %s", fip_agent_port) else: # If not hostid check if the fixed ip provided has to # deal with allowed_address_pairs for a given service # port. Get the port_dict, inherit the service port host # and device owner(if it does not exist). port = self._core_plugin.get_port( admin_ctx, fixed_port_id) allowed_device_owners = ( n_utils.get_dvr_allowed_address_pair_device_owners()) # NOTE: We just need to deal with ports that do not # have a device_owner and ports that are owned by the # dvr service ports except for the compute port and # dhcp port. if (port['device_owner'] == "" or port['device_owner'] in allowed_device_owners): addr_pair_active_service_port_list = ( self._get_ports_for_allowed_address_pair_ip( admin_ctx, port['network_id'], fixed_ip_address)) if not addr_pair_active_service_port_list: return self._inherit_service_port_and_arp_update( context, addr_pair_active_service_port_list[0]) def _inherit_service_port_and_arp_update( self, context, service_port): """Function inherits port host bindings for allowed_address_pair.""" service_port_dict = self.l3plugin._core_plugin._make_port_dict( service_port) address_pair_list = service_port_dict.get('allowed_address_pairs') for address_pair in address_pair_list: self.update_arp_entry_for_dvr_service_port(context, service_port_dict) @registry.receives(resources.ROUTER_INTERFACE, [events.BEFORE_CREATE]) @db_api.retry_if_session_inactive() def _add_csnat_on_interface_create(self, resource, event, trigger, context, router_db, port, **kwargs): """Event handler to for csnat port creation on interface creation.""" if not router_db.extra_attributes.distributed or not router_db.gw_port: return admin_context = context.elevated() self._add_csnat_router_interface_port( admin_context, router_db, port['network_id'], [{'subnet_id': port['fixed_ips'][-1]['subnet_id']}]) @registry.receives(resources.ROUTER_INTERFACE, [events.AFTER_CREATE]) @db_api.retry_if_session_inactive() def _update_snat_v6_addrs_after_intf_update(self, resource, event, triger, context, subnets, port, router_id, new_interface, **kwargs): if new_interface: # _add_csnat_on_interface_create handler deals with new ports return # if not a new interface, the interface was added to a new subnet, # which is the first in this list subnet = subnets[0] if not subnet or subnet['ip_version'] != 6: return # NOTE: For IPv6 additional subnets added to the same # network we need to update the CSNAT port with respective # IPv6 subnet # Add new prefix to an existing ipv6 csnat port with the # same network id if one exists admin_ctx = context.elevated() router = self.l3plugin._get_router(admin_ctx, router_id) cs_port = self._find_v6_router_port_by_network_and_device_owner( router, subnet['network_id'], const.DEVICE_OWNER_ROUTER_SNAT) if not cs_port: return new_fixed_ip = {'subnet_id': subnet['id']} fixed_ips = list(cs_port['fixed_ips']) fixed_ips.append(new_fixed_ip) try: updated_port = self._core_plugin.update_port( admin_ctx, cs_port['id'], {'port': {'fixed_ips': fixed_ips}}) except Exception: with excutils.save_and_reraise_exception(): # we need to try to undo the updated router # interface from above so it's not out of sync # with the csnat port. # TODO(kevinbenton): switch to taskflow to manage # these rollbacks. @db_api.retry_db_errors def revert(): # TODO(kevinbenton): even though we get the # port each time, there is a potential race # where we update the port with stale IPs if # another interface operation is occurring at # the same time. This can be fixed in the # future with a compare-and-swap style update # using the revision number of the port. p = self._core_plugin.get_port(admin_ctx, port['id']) rollback_fixed_ips = [ip for ip in p['fixed_ips'] if ip['subnet_id'] != subnet['id']] upd = {'port': {'fixed_ips': rollback_fixed_ips}} self._core_plugin.update_port(admin_ctx, port['id'], upd) try: revert() except Exception: LOG.exception("Failed to revert change " "to router port %s.", port['id']) LOG.debug("CSNAT port updated for IPv6 subnet: %s", updated_port) def _find_v6_router_port_by_network_and_device_owner( self, router, net_id, device_owner): for port in router.attached_ports: p = port['port'] if (p['network_id'] == net_id and p['device_owner'] == device_owner and self.l3plugin._port_has_ipv6_address(p)): return self.l3plugin._core_plugin._make_port_dict(p) def _check_for_multiprefix_csnat_port_and_update( self, context, router, network_id, subnet_id): """Checks if the csnat port contains multiple ipv6 prefixes. If the csnat port contains multiple ipv6 prefixes for the given network when a router interface is deleted, make sure we don't delete the port when a single subnet is deleted and just update it with the right fixed_ip. This function returns true if it is a multiprefix port. """ if router.gw_port: # If router has a gateway port, check if it has IPV6 subnet cs_port = ( self._find_v6_router_port_by_network_and_device_owner( router, network_id, const.DEVICE_OWNER_ROUTER_SNAT)) if cs_port: fixed_ips = ( [fixedip for fixedip in cs_port['fixed_ips'] if fixedip['subnet_id'] != subnet_id]) if len(fixed_ips) == len(cs_port['fixed_ips']): # The subnet being detached from router is not part of # ipv6 router port. No need to update the multiprefix. return False if fixed_ips: # multiple prefix port - delete prefix from port self.l3plugin._core_plugin.update_port( context.elevated(), cs_port['id'], {'port': {'fixed_ips': fixed_ips}}) return True return False @registry.receives(resources.ROUTER_INTERFACE, [events.BEFORE_DELETE]) def _cache_related_dvr_routers_info_before_interface_removal( self, resource, event, trigger, context, **kwargs): router_id = kwargs.get("router_id") subnet_id = kwargs.get("subnet_id") router = self.l3plugin._get_router(context, router_id) if not router.extra_attributes.distributed: return cache_key = (router_id, subnet_id) try: existing_hosts = self.related_dvr_router_hosts[cache_key] except KeyError: existing_hosts = set() other_hosts = set(self._get_other_dvr_hosts(context, router_id)) self.related_dvr_router_hosts[cache_key] = existing_hosts | other_hosts try: existing_routers = self.related_dvr_router_routers[cache_key] except KeyError: existing_routers = set() other_routers = set(self._get_other_dvr_router_ids_connected_router( context, router_id)) self.related_dvr_router_routers[cache_key] = ( existing_routers | other_routers) @registry.receives(resources.ROUTER_INTERFACE, [events.AFTER_DELETE]) @db_api.retry_if_session_inactive() def _cleanup_after_interface_removal(self, resource, event, trigger, context, port, interface_info, router_id, **kwargs): """Handler to cleanup distributed resources after intf removal.""" router = self.l3plugin._get_router(context, router_id) if not router.extra_attributes.distributed: return plugin = directory.get_plugin(plugin_constants.L3) # we calculate which hosts to notify by checking the hosts for # the removed port's subnets and then subtract out any hosts still # hosting the router for the remaining interfaces router_hosts_for_removed = plugin._get_dvr_hosts_for_subnets( context, subnet_ids={ip['subnet_id'] for ip in port['fixed_ips']}) router_hosts_after = plugin._get_dvr_hosts_for_router( context, router_id) removed_hosts = set(router_hosts_for_removed) - set(router_hosts_after) if removed_hosts: # Get hosts where this router is placed as "related" to other dvr # routers and don't remove it from such hosts related_hosts = self._get_other_dvr_hosts(context, router_id) agents = plugin.get_l3_agents( context, filters={'host': removed_hosts}) bindings = rb_obj.RouterL3AgentBinding.get_objects( context, router_id=router_id) snat_binding = bindings.pop() if bindings else None connected_dvr_routers = set( self.l3plugin._get_other_dvr_router_ids_connected_router( context, router_id)) for agent in agents: is_this_snat_agent = ( snat_binding and snat_binding.l3_agent_id == agent['id']) if (not is_this_snat_agent and agent['host'] not in related_hosts): self.l3plugin.l3_rpc_notifier.router_removed_from_agent( context, router_id, agent['host']) for connected_router_id in connected_dvr_routers: connected_router_hosts = set( self.l3plugin._get_dvr_hosts_for_router( context, connected_router_id)) connected_router_hosts |= set( self._get_other_dvr_hosts( context, connected_router_id)) if agent['host'] not in connected_router_hosts: self.l3plugin.l3_rpc_notifier.\ router_removed_from_agent( context, connected_router_id, agent['host']) # if subnet_id not in interface_info, request was to remove by port sub_id = (interface_info.get('subnet_id') or port['fixed_ips'][0]['subnet_id']) self._cleanup_related_hosts_after_interface_removal( context, router_id, sub_id) self._cleanup_related_routers_after_interface_removal( context, router_id, sub_id) is_multiple_prefix_csport = ( self._check_for_multiprefix_csnat_port_and_update( context, router, port['network_id'], sub_id)) if not is_multiple_prefix_csport: # Single prefix port - go ahead and delete the port self.delete_csnat_router_interface_ports( context.elevated(), router, subnet_id=sub_id) def _cleanup_related_hosts_after_interface_removal( self, context, router_id, subnet_id): router_hosts = self.l3plugin._get_dvr_hosts_for_router( context, router_id) cache_key = (router_id, subnet_id) related_dvr_router_hosts_before = self.related_dvr_router_hosts.pop( cache_key, set()) related_dvr_router_hosts_after = set(self._get_other_dvr_hosts( context, router_id)) related_dvr_router_hosts_before -= set(router_hosts) related_removed_hosts = ( related_dvr_router_hosts_before - related_dvr_router_hosts_after) if related_removed_hosts: agents = self.l3plugin.get_l3_agents( context, filters={'host': related_removed_hosts}) bindings = rb_obj.RouterL3AgentBinding.get_objects( context, router_id=router_id) snat_binding = bindings.pop() if bindings else None for agent in agents: is_this_snat_agent = ( snat_binding and snat_binding.l3_agent_id == agent['id']) if not is_this_snat_agent: self.l3plugin.l3_rpc_notifier.router_removed_from_agent( context, router_id, agent['host']) def _cleanup_related_routers_after_interface_removal( self, context, router_id, subnet_id): router_hosts = self.l3plugin._get_dvr_hosts_for_router( context, router_id) cache_key = (router_id, subnet_id) related_dvr_routers_before = self.related_dvr_router_routers.pop( cache_key, set()) related_dvr_routers_after = set( self._get_other_dvr_router_ids_connected_router( context, router_id)) related_routers_to_remove = ( related_dvr_routers_before - related_dvr_routers_after) for related_router in related_routers_to_remove: related_router_hosts = self.l3plugin._get_dvr_hosts_for_router( context, related_router) hosts_to_remove = set(router_hosts) - set(related_router_hosts) for host in hosts_to_remove: self.l3plugin.l3_rpc_notifier.router_removed_from_agent( context, related_router, host) def delete_csnat_router_interface_ports(self, context, router, subnet_id=None): # Each csnat router interface port is associated # with a subnet, so we need to pass the subnet id to # delete the right ports. filters = {'device_owner': [const.DEVICE_OWNER_ROUTER_SNAT], 'device_id': [router.id]} c_snat_ports = self.l3plugin._core_plugin.get_ports( context, filters=filters ) for p in c_snat_ports: if subnet_id is None or not p['fixed_ips']: if not p['fixed_ips']: LOG.info("CSNAT port has no IPs: %s", p) self.l3plugin._core_plugin.delete_port(context, p['id'], l3_port_check=False) else: if p['fixed_ips'][0]['subnet_id'] == subnet_id: LOG.debug("Subnet matches: %s", subnet_id) self.l3plugin._core_plugin.delete_port(context, p['id'], l3_port_check=False) class _DVRAgentInterfaceMixin(object): """Contains calls made by the DVR scheduler and RPC interface. Must be instantiated as a mixin with the L3 plugin. """ def _get_snat_sync_interfaces(self, context, router_ids): """Query router interfaces that relate to list of router_ids.""" if not router_ids: return [] objs = l3_obj.RouterPort.get_objects( context, router_id=router_ids, port_type=const.DEVICE_OWNER_ROUTER_SNAT) interfaces = collections.defaultdict(list) for rp in objs: # TODO(lujinluo): Need Port as synthetic field interfaces[rp.router_id].append( self._core_plugin._make_port_dict(rp.db_obj.port)) LOG.debug("Return the SNAT ports: %s", interfaces) return interfaces def _get_gateway_port_host(self, context, router, gw_ports): binding_objs = rb_obj.RouterL3AgentBinding.get_objects( context, router_id=[router['id']]) if not binding_objs: return gw_port_id = router['gw_port_id'] # Collect gw ports only if available if gw_port_id and gw_ports.get(gw_port_id): l3_agent = ag_obj.Agent.get_object(context, id=binding_objs[0].l3_agent_id) return l3_agent.host def _build_routers_list(self, context, routers, gw_ports): # Perform a single query up front for all routers routers = super(_DVRAgentInterfaceMixin, self)._build_routers_list( context, routers, gw_ports) if not routers: return [] for router in routers: gw_port_host = self._get_gateway_port_host( context, router, gw_ports) LOG.debug("Set router %s gateway port host: %s", router['id'], gw_port_host) router['gw_port_host'] = gw_port_host return routers def _process_routers(self, context, routers, agent): routers_dict = {} snat_intfs_by_router_id = self._get_snat_sync_interfaces( context, [r['id'] for r in routers]) fip_agent_gw_ports = None LOG.debug("FIP Agent: %s ", agent.id) for router in routers: routers_dict[router['id']] = router if router['gw_port_id']: snat_router_intfs = snat_intfs_by_router_id[router['id']] LOG.debug("SNAT ports returned: %s ", snat_router_intfs) router[l3_const.SNAT_ROUTER_INTF_KEY] = snat_router_intfs if not fip_agent_gw_ports: fip_agent_gw_ports = self._get_fip_agent_gw_ports( context, agent.id) LOG.debug("FIP Agent ports: %s", fip_agent_gw_ports) router[l3_const.FLOATINGIP_AGENT_INTF_KEY] = ( fip_agent_gw_ports) return routers_dict @staticmethod def _get_floating_ip_host(floating_ip): """Function to return floating IP host binding state By default, floating IPs are not bound to any host. Instead of using the empty string to denote this use a constant. """ return floating_ip.get('host', l3_const.FLOATING_IP_HOST_UNBOUND) def _process_floating_ips_dvr(self, context, routers_dict, floating_ips, host, agent): LOG.debug("FIP Agent : %s ", agent.id) for floating_ip in floating_ips: router = routers_dict.get(floating_ip['router_id']) if router: if router['distributed']: if self._skip_floating_ip_for_mismatched_agent_or_host( floating_ip, agent, host): continue router_floatingips = router.get(const.FLOATINGIP_KEY, []) router_floatingips.append(floating_ip) router[const.FLOATINGIP_KEY] = router_floatingips def _skip_floating_ip_for_mismatched_agent_or_host(self, floating_ip, agent, host): """Function to check if floating IP processing can be skipped.""" fip_host = self._get_floating_ip_host(floating_ip) # Skip if it is unbound if fip_host == l3_const.FLOATING_IP_HOST_UNBOUND: return True # Skip if the given agent is in the wrong mode - SNAT bound # requires DVR_SNAT agent. agent_mode = self._get_agent_mode(agent) if (agent_mode in [const.L3_AGENT_MODE_LEGACY, const.L3_AGENT_MODE_DVR] and floating_ip.get(l3_const.DVR_SNAT_BOUND)): return True # Skip if it is bound, but not to the given host fip_dest_host = floating_ip.get('dest_host') if (fip_host != l3_const.FLOATING_IP_HOST_NEEDS_BINDING and fip_host != host and fip_dest_host is None): return True # not being skipped, log host LOG.debug("Floating IP host: %s", fip_host) return False def _get_fip_agent_gw_ports(self, context, fip_agent_id): """Return list of floating agent gateway ports for the agent.""" if not fip_agent_id: return [] filters = {'device_id': [fip_agent_id], 'device_owner': [const.DEVICE_OWNER_AGENT_GW]} ports = self._core_plugin.get_ports(context.elevated(), filters) LOG.debug("Return the FIP ports: %s ", ports) return ports @log_helper.log_method_call def _get_dvr_sync_data(self, context, host, agent, router_ids=None, active=None): routers, interfaces, floating_ips = self._get_router_info_list( context, router_ids=router_ids, active=active, device_owners=const.ROUTER_INTERFACE_OWNERS) dvr_router_ids = set(router['id'] for router in routers if is_distributed_router(router)) floating_ip_port_ids = [fip['port_id'] for fip in floating_ips if fip['router_id'] in dvr_router_ids] if floating_ip_port_ids: port_filter = {'id': floating_ip_port_ids} ports = self._core_plugin.get_ports(context, port_filter) port_dict = {} for port in ports: # Make sure that we check for cases were the port # might be in a pre-live migration state or also # check for the portbinding profile 'migrating_to' # key for the host. port_profile = port.get(portbindings.PROFILE) port_in_migration = ( port_profile and port_profile.get('migrating_to') == host) # All unbound ports with floatingip irrespective of # the device owner should be included as valid ports # and updated. if (port_in_migration or self._is_unbound_port(port)): port_dict.update({port['id']: port}) continue port_host = port[portbindings.HOST_ID] if port_host: l3_agent_on_host = self.get_l3_agents( context, filters={'host': [port_host]}) l3_agent_mode = '' if len(l3_agent_on_host): l3_agent_mode = self._get_agent_mode( l3_agent_on_host[0]) requesting_agent_mode = self._get_agent_mode(agent) # Consider the ports where the portbinding host and # request host match. if port_host == host: # Check for agent type before adding the port_dict. # For VMs that are hosted on the dvr_no_external # agent and if the request is coming from the same # agent on re-syncs then we need to add the appropriate # port['agent'] before updating the dict. if (l3_agent_mode == ( l3_const.L3_AGENT_MODE_DVR_NO_EXTERNAL) and requesting_agent_mode == ( l3_const.L3_AGENT_MODE_DVR_NO_EXTERNAL)): port['agent'] = ( l3_const.L3_AGENT_MODE_DVR_NO_EXTERNAL) port_dict.update({port['id']: port}) # Consider the ports where the portbinding host and # request host does not match. else: # If the agent requesting is dvr_snat but # the portbinding host resides in dvr_no_external # agent then include the port. if (l3_agent_mode == ( l3_const.L3_AGENT_MODE_DVR_NO_EXTERNAL) and requesting_agent_mode == ( const.L3_AGENT_MODE_DVR_SNAT)): port['agent'] = ( l3_const.L3_AGENT_MODE_DVR_NO_EXTERNAL) port_dict.update({port['id']: port}) # Add the port binding host to the floatingip dictionary for fip in floating_ips: # Assume no host binding required fip['host'] = l3_const.FLOATING_IP_HOST_UNBOUND vm_port = port_dict.get(fip['port_id'], None) if vm_port: # Default value if host port-binding required fip['host'] = l3_const.FLOATING_IP_HOST_NEEDS_BINDING port_host = vm_port[portbindings.HOST_ID] if port_host: fip['dest_host'] = ( self._get_dvr_migrating_service_port_hostid( context, fip['port_id'], port=vm_port)) vm_port_agent_mode = vm_port.get('agent', None) if vm_port_agent_mode != ( l3_const.L3_AGENT_MODE_DVR_NO_EXTERNAL): # For floatingip configured on ports that do not # reside on a 'dvr_no_external' agent, add the # fip host binding, else it will be created # in the 'dvr_snat' agent. fip['host'] = port_host # Handle the case were there is no host binding # for the private ports that are associated with # floating ip. if fip['host'] == l3_const.FLOATING_IP_HOST_NEEDS_BINDING: fip[l3_const.DVR_SNAT_BOUND] = True routers_dict = self._process_routers(context, routers, agent) self._process_floating_ips_dvr(context, routers_dict, floating_ips, host, agent) ports_to_populate = [] for router in routers_dict.values(): if router.get('gw_port'): ports_to_populate.append(router['gw_port']) if router.get(l3_const.FLOATINGIP_AGENT_INTF_KEY): ports_to_populate += router[l3_const.FLOATINGIP_AGENT_INTF_KEY] if router.get(l3_const.SNAT_ROUTER_INTF_KEY): ports_to_populate += router[l3_const.SNAT_ROUTER_INTF_KEY] ports_to_populate += interfaces self._populate_mtu_and_subnets_for_ports(context, ports_to_populate) self._process_interfaces(routers_dict, interfaces) return list(routers_dict.values()) def _is_unbound_port(self, port): """Check for port-bindings irrespective of device_owner.""" return not port[portbindings.HOST_ID] def _get_dvr_service_port_hostid(self, context, port_id, port=None): """Returns the portbinding host_id for dvr service port.""" port_db = port or self._core_plugin.get_port(context, port_id) return port_db[portbindings.HOST_ID] or None def _get_dvr_migrating_service_port_hostid( self, context, port_id, port=None): """Returns the migrating host_id from the migrating profile.""" port_db = port or self._core_plugin.get_port(context, port_id) port_profile = port_db.get(portbindings.PROFILE) port_dest_host = None if port_profile: port_dest_host = port_profile.get('migrating_to') return port_dest_host def _get_agent_gw_ports_exist_for_network( self, context, network_id, host, agent_id): """Return agent gw port if exist, or None otherwise.""" if not network_id: LOG.debug("Network not specified") return filters = { 'network_id': [network_id], 'device_id': [agent_id], 'device_owner': [const.DEVICE_OWNER_AGENT_GW] } ports = self._core_plugin.get_ports(context, filters) if ports: return ports[0] def check_for_fip_and_create_agent_gw_port_on_host_if_not_exists( self, context, port, host): """Create fip agent_gw_port on host if not exists""" fips = self._get_floatingips_by_port_id(context, port['id']) if not fips: return fip = fips[0] network_id = fip.get('floating_network_id') agent_gw_port = self.create_fip_agent_gw_port_if_not_exists( context.elevated(), network_id, host) LOG.debug("Port-in-Migration: Floatingip Agent Gateway port " "%(gw)s created for the future host: %(dest_host)s", {'gw': agent_gw_port, 'dest_host': host}) def create_fip_agent_gw_port_if_not_exists( self, context, network_id, host): # TODO(slaweq): add proper constraint on database level to avoid # creation of duplicated Floating IP gateway ports for same network and # same L3 agent. When this will be done, we can get rid of this lock. try: l3_agent_db = self._get_agent_by_type_and_host( context, const.AGENT_TYPE_L3, host) except agent_exc.AgentNotFoundByTypeHost( agent_type=const.AGENT_TYPE_L3, host=host): LOG.warning("%(ag)s agent not found for the given host: %(host)s", {'ag': const.AGENT_TYPE_L3, 'host': host}) return l3_agent_mode = self._get_agent_mode(l3_agent_db) if l3_agent_mode == l3_const.L3_AGENT_MODE_DVR_NO_EXTERNAL: return if not l3_agent_db: return lock_name = 'fip-gw-lock-' + network_id + '-' + host with lockutils.lock(lock_name, external=True): return self._create_fip_agent_gw_port_if_not_exists( context, network_id, host, l3_agent_db) def _create_fip_agent_gw_port_if_not_exists(self, context, network_id, host, l3_agent_db): """Function to return the FIP Agent GW port. This function will create a FIP Agent GW port if required. If the port already exists, it will return the existing port and will not create a new one. """ LOG.debug("Agent ID exists: %s", l3_agent_db['id']) f_port = self._get_agent_gw_ports_exist_for_network( context, network_id, host, l3_agent_db['id']) if not f_port: LOG.info('Agent Gateway port does not exist,' ' so create one: %s', f_port) port_data = {'tenant_id': '', 'network_id': network_id, 'device_id': l3_agent_db['id'], 'device_owner': const.DEVICE_OWNER_AGENT_GW, portbindings.HOST_ID: host, 'admin_state_up': True, 'name': ''} agent_port = p_utils.create_port(self._core_plugin, context, {'port': port_data}) if agent_port: self._populate_mtu_and_subnets_for_ports(context, [agent_port]) return agent_port msg = _("Unable to create the Agent Gateway Port") raise n_exc.BadRequest(resource='router', msg=msg) else: self._populate_mtu_and_subnets_for_ports(context, [f_port]) return f_port def _generate_arp_table_and_notify_agent( self, context, fixed_ip, mac_address, notifier): """Generates the arp table entry and notifies the l3 agent.""" ip_address = fixed_ip['ip_address'] subnet = fixed_ip['subnet_id'] arp_table = {'ip_address': ip_address, 'mac_address': mac_address, 'subnet_id': subnet} filters = {'fixed_ips': {'subnet_id': [subnet]}, 'device_owner': [const.DEVICE_OWNER_DVR_INTERFACE]} ports = self._core_plugin.get_ports(context, filters=filters) routers = [port['device_id'] for port in ports] for router_id in routers: notifier(context, router_id, arp_table) def _get_subnet_id_for_given_fixed_ip( self, context, fixed_ip, port_dict): """Returns the subnet_id that matches the fixedip on a network.""" filters = {'network_id': [port_dict['network_id']]} subnets = self._core_plugin.get_subnets(context, filters) for subnet in subnets: if ipam_utils.check_subnet_ip(subnet['cidr'], fixed_ip): return subnet['id'] def _get_allowed_address_pair_fixed_ips(self, context, port_dict): """Returns all fixed_ips associated with the allowed_address_pair.""" aa_pair_fixed_ips = [] if port_dict.get('allowed_address_pairs'): for address_pair in port_dict['allowed_address_pairs']: aap_ip_cidr = address_pair['ip_address'].split("/") if len(aap_ip_cidr) == 1 or int(aap_ip_cidr[1]) == 32: subnet_id = self._get_subnet_id_for_given_fixed_ip( context, aap_ip_cidr[0], port_dict) if subnet_id is not None: fixed_ip = {'subnet_id': subnet_id, 'ip_address': aap_ip_cidr[0]} aa_pair_fixed_ips.append(fixed_ip) else: LOG.debug("Subnet does not match for the given " "fixed_ip %s for arp update", aap_ip_cidr[0]) return aa_pair_fixed_ips def update_arp_entry_for_dvr_service_port(self, context, port_dict): """Notify L3 agents of ARP table entry for dvr service port. When a dvr service port goes up, look for the DVR router on the port's subnet, and send the ARP details to all L3 agents hosting the router to add it. If there are any allowed_address_pairs associated with the port those fixed_ips should also be updated in the ARP table. """ fixed_ips = port_dict['fixed_ips'] if not fixed_ips: return allowed_address_pair_fixed_ips = ( self._get_allowed_address_pair_fixed_ips(context, port_dict)) changed_fixed_ips = fixed_ips + allowed_address_pair_fixed_ips for fixed_ip in changed_fixed_ips: self._generate_arp_table_and_notify_agent( context, fixed_ip, port_dict['mac_address'], self.l3_rpc_notifier.add_arp_entry) def delete_arp_entry_for_dvr_service_port( self, context, port_dict, fixed_ips_to_delete=None): """Notify L3 agents of ARP table entry for dvr service port. When a dvr service port goes down, look for the DVR router on the port's subnet, and send the ARP details to all L3 agents hosting the router to delete it. If there are any allowed_address_pairs associated with the port, those fixed_ips should be removed from the ARP table. """ fixed_ips = port_dict['fixed_ips'] if not fixed_ips: return if not fixed_ips_to_delete: allowed_address_pair_fixed_ips = ( self._get_allowed_address_pair_fixed_ips(context, port_dict)) fixed_ips_to_delete = fixed_ips + allowed_address_pair_fixed_ips for fixed_ip in fixed_ips_to_delete: self._generate_arp_table_and_notify_agent( context, fixed_ip, port_dict['mac_address'], self.l3_rpc_notifier.del_arp_entry) def _get_address_pair_active_port_with_fip( self, context, port_dict, port_addr_pair_ip): port_valid_state = (port_dict['admin_state_up'] or (port_dict['status'] == const.PORT_STATUS_ACTIVE)) if not port_valid_state: return fips = l3_obj.FloatingIP.get_objects( context, _pager=base_obj.Pager(limit=1), fixed_ip_address=netaddr.IPAddress(port_addr_pair_ip)) return self._core_plugin.get_port( context, fips[0].fixed_port_id) if fips else None class L3_NAT_with_dvr_db_mixin(_DVRAgentInterfaceMixin, DVRResourceOperationHandler, l3_attrs_db.ExtraAttributesMixin, l3_db.L3_NAT_db_mixin): """Mixin class to enable DVR support.""" router_device_owners = ( l3_db.L3_NAT_db_mixin.router_device_owners + (const.DEVICE_OWNER_DVR_INTERFACE, const.DEVICE_OWNER_ROUTER_SNAT, const.DEVICE_OWNER_AGENT_GW)) def _get_device_owner(self, context, router=None): """Get device_owner for the specified router.""" router_is_uuid = isinstance(router, six.string_types) if router_is_uuid: router = self._get_router(context, router) if is_distributed_router(router): return const.DEVICE_OWNER_DVR_INTERFACE return super(L3_NAT_with_dvr_db_mixin, self)._get_device_owner(context, router) @db_api.retry_if_session_inactive() def create_floatingip(self, context, floatingip, initial_status=const.FLOATINGIP_STATUS_ACTIVE): floating_ip = self._create_floatingip( context, floatingip, initial_status) self._notify_floating_ip_change(context, floating_ip) return floating_ip def get_dvr_agent_on_host(self, context, fip_host): agent_filters = {'host': [fip_host]} return self.get_l3_agents(context, filters=agent_filters) def _notify_floating_ip_change(self, context, floating_ip): router_id = floating_ip['router_id'] fixed_port_id = floating_ip['port_id'] # we need to notify agents only in case Floating IP is associated if not router_id or not fixed_port_id: return try: # using admin context as router may belong to admin tenant router = self._get_router(context.elevated(), router_id) except l3_exc.RouterNotFound: LOG.warning("Router %s was not found. " "Skipping agent notification.", router_id) return if is_distributed_router(router): host = self._get_dvr_service_port_hostid(context, fixed_port_id) dest_host = self._get_dvr_migrating_service_port_hostid( context, fixed_port_id) if host is not None: l3_agent_on_host = self.get_dvr_agent_on_host( context, host) if not l3_agent_on_host: LOG.warning("No valid L3 agent found for the given host: " "%s", host) return agent_mode = self._get_agent_mode(l3_agent_on_host[0]) if agent_mode == l3_const.L3_AGENT_MODE_DVR_NO_EXTERNAL: # If the agent hosting the fixed port is in # 'dvr_no_external' mode, then set the host to None, # since we would be centralizing the floatingip for # those fixed_ports. host = None if host is not None: self.l3_rpc_notifier.routers_updated_on_host( context, [router_id], host) if dest_host and dest_host != host: self.l3_rpc_notifier.routers_updated_on_host( context, [router_id], dest_host) else: centralized_agent_list = self.list_l3_agents_hosting_router( context, router_id)['agents'] for agent in centralized_agent_list: self.l3_rpc_notifier.routers_updated_on_host( context, [router_id], agent['host']) else: self.notify_router_updated(context, router_id) @db_api.retry_if_session_inactive() def update_floatingip(self, context, id, floatingip): old_floatingip, floatingip = self._update_floatingip( context, id, floatingip) self._notify_floating_ip_change(context, old_floatingip) if (floatingip['router_id'] != old_floatingip['router_id'] or floatingip['port_id'] != old_floatingip['port_id']): self._notify_floating_ip_change(context, floatingip) return floatingip @db_api.retry_if_session_inactive() def delete_floatingip(self, context, id): floating_ip = self._delete_floatingip(context, id) self._notify_floating_ip_change(context, floating_ip) @db_api.retry_if_session_inactive() def is_router_distributed(self, context, router_id): if router_id: return is_distributed_router( self.get_router(context.elevated(), router_id)) return False def is_distributed_router(router): """Return True if router to be handled is distributed.""" try: # See if router is a DB object first requested_router_type = router.extra_attributes.distributed except AttributeError: # if not, try to see if it is a request body requested_router_type = router.get('distributed') if validators.is_attr_set(requested_router_type): return requested_router_type return cfg.CONF.router_distributed neutron-12.1.1/neutron/db/_model_query.py0000664000175000017500000003115613553660047020444 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ NOTE: This module shall not be used by external projects. It will be moved to neutron-lib in due course, and then it can be used from there. """ from neutron_lib.api import attributes from neutron_lib.db import utils as db_utils from oslo_db.sqlalchemy import utils as sa_utils from sqlalchemy import sql, or_, and_ from sqlalchemy.ext import associationproxy from neutron.common import utils from neutron.db import _utils as ndb_utils from neutron.objects import utils as obj_utils # Classes implementing extensions will register hooks into this dictionary # for "augmenting" the "core way" of building a query for retrieving objects # from a model class. Hooks are registered by invoking register_hook(). _model_query_hooks = { # model1 : { # hook1: { # 'query': query_hook, # 'filter': filter_hook, # 'result_filters': result_filters # }, # hook2: { # 'query': query_hook, # 'filter': filter_hook, # 'result_filters': result_filters # }, # ... # }, # model2 : { # hook1: { # 'query': query_hook, # 'filter': filter_hook, # 'result_filters': result_filters # }, # hook2: { # 'query': query_hook, # 'filter': filter_hook, # 'result_filters': result_filters # }, # ... # }, # ... } def register_hook(model, name, query_hook, filter_hook, result_filters=None): """Register a hook to be invoked when a query is executed. :param model: The DB Model that the hook applies to. :type model: sqlalchemy orm model :param name: A name for the hook. :type name: str :param query_hook: The method to be called to augment the query. :type query_hook: callable or None :param filter_hook: A method to be called to augment the query filter. :type filter_hook: callable or None :param result_filters: A Method to be called to filter the query result. :type result_filters: callable or None Adds the hook components to the _model_query_hooks dict. Models are the keys of this dict, whereas the value is another dict mapping hook names to callables performing the hook. Each hook has three components: "query", used to build the query expression "filter", used to build the filter expression "result_filters", used for final filtering on the query result Query hooks take as input the query being built and return a transformed query expression. def mymodel_query_hook(context, original_model, query): augmented_query = ... return augmented_query Filter hooks take as input the filter expression being built and return a transformed filter expression def mymodel_filter_hook(context, original_model, filters): refined_filters = ... return refined_filters Result filter hooks take as input the query expression and the filter expression, and return a final transformed query expression. def mymodel_result_filter_hook(query, filters): final_filters = ... return query.filter(final_filters) """ if callable(query_hook): query_hook = utils.make_weak_ref(query_hook) if callable(filter_hook): filter_hook = utils.make_weak_ref(filter_hook) if callable(result_filters): result_filters = utils.make_weak_ref(result_filters) _model_query_hooks.setdefault(model, {})[name] = { 'query': query_hook, 'filter': filter_hook, 'result_filters': result_filters } def get_hooks(model): """Retrieve the model query hooks for a model. :param model: The DB Model to look up for query hooks. :type model: sqlalchemy orm model :return: list of hooks :rtype: list of dict of callable """ return _model_query_hooks.get(model, {}).values() def query_with_hooks(context, model): query = context.session.query(model) # define basic filter condition for model query query_filter = None if ndb_utils.model_query_scope_is_project(context, model): if hasattr(model, 'rbac_entries'): query = query.outerjoin(model.rbac_entries) rbac_model = model.rbac_entries.property.mapper.class_ query_filter = ( (model.tenant_id == context.tenant_id) | ((rbac_model.action == 'access_as_shared') & ((rbac_model.target_tenant == context.tenant_id) | (rbac_model.target_tenant == '*')))) elif hasattr(model, 'shared'): query_filter = ((model.tenant_id == context.tenant_id) | (model.shared == sql.true())) else: query_filter = (model.tenant_id == context.tenant_id) # Execute query hooks registered from mixins and plugins for hook in get_hooks(model): query_hook = utils.resolve_ref(hook.get('query')) if query_hook: query = query_hook(context, model, query) filter_hook = utils.resolve_ref(hook.get('filter')) if filter_hook: query_filter = filter_hook(context, model, query_filter) # NOTE(salvatore-orlando): 'if query_filter' will try to evaluate the # condition, raising an exception if query_filter is not None: query = query.filter(query_filter) return query def get_by_id(context, model, object_id): query = query_with_hooks(context=context, model=model) return query.filter(model.id == object_id).one() def apply_filters(query, model, filters, context=None): if filters: for key, value in filters.items(): column = getattr(model, key, None) # NOTE(kevinbenton): if column is a hybrid property that # references another expression, attempting to convert to # a boolean will fail so we must compare to None. # See "An Important Expression Language Gotcha" in: # docs.sqlalchemy.org/en/rel_0_9/changelog/migration_06.html if column is not None: if not value: query = query.filter(sql.false()) return query if isinstance(column, associationproxy.AssociationProxy): # association proxies don't support in_ so we have to # do multiple equals matches query = query.filter( or_(*[column == v for v in value])) elif isinstance(value, obj_utils.StringMatchingFilterObj): if value.is_contains: query = query.filter( column.contains(value.contains)) elif value.is_starts: query = query.filter( column.startswith(value.starts)) elif value.is_ends: query = query.filter( column.endswith(value.ends)) elif None in value: # in_() operator does not support NULL element so we have # to do multiple equals matches query = query.filter( or_(*[column == v for v in value])) else: query = query.filter(column.in_(value)) elif key == 'shared' and hasattr(model, 'rbac_entries'): # translate a filter on shared into a query against the # object's rbac entries rbac = model.rbac_entries.property.mapper.class_ matches = [rbac.target_tenant == '*'] if context: matches.append(rbac.target_tenant == context.tenant_id) # any 'access_as_shared' records that match the # wildcard or requesting tenant is_shared = and_(rbac.action == 'access_as_shared', or_(*matches)) if not value[0]: # NOTE(kevinbenton): we need to find objects that don't # have an entry that matches the criteria above so # we use a subquery to exclude them. # We can't just filter the inverse of the query above # because that will still give us a network shared to # our tenant (or wildcard) if it's shared to another # tenant. # This is the column joining the table to rbac via # the object_id. We can't just use model.id because # subnets join on network.id so we have to inspect the # relationship. join_cols = model.rbac_entries.property.local_columns oid_col = list(join_cols)[0] is_shared = ~oid_col.in_( query.session.query(rbac.object_id).filter(is_shared) ) elif (not context or not ndb_utils.model_query_scope_is_project(context, model)): # we only want to join if we aren't using the subquery # and if we aren't already joined because this is a # scoped query query = query.outerjoin(model.rbac_entries) query = query.filter(is_shared) for hook in get_hooks(model): result_filter = utils.resolve_ref(hook.get('result_filters', None)) if result_filter: query = result_filter(query, filters) return query def get_collection_query(context, model, filters=None, sorts=None, limit=None, marker_obj=None, page_reverse=False): collection = query_with_hooks(context, model) collection = apply_filters(collection, model, filters, context) if sorts: sort_keys = db_utils.get_and_validate_sort_keys(sorts, model) sort_dirs = db_utils.get_sort_dirs(sorts, page_reverse) # we always want deterministic results for sorted queries # so add unique keys to limit queries when present. # (http://docs.sqlalchemy.org/en/latest/orm/ # loading_relationships.html#subqueryload-ordering) # (http://docs.sqlalchemy.org/en/latest/faq/ # ormconfiguration.html#faq-subqueryload-limit-sort) for k in _unique_keys(model): if k not in sort_keys: sort_keys.append(k) sort_dirs.append('asc') collection = sa_utils.paginate_query(collection, model, limit, marker=marker_obj, sort_keys=sort_keys, sort_dirs=sort_dirs) return collection def _unique_keys(model): # just grab first set of unique keys and use them. # if model has no unqiue sets, 'paginate_query' will # warn if sorting is unstable uk_sets = sa_utils.get_unique_keys(model) return uk_sets[0] if uk_sets else [] def get_collection(context, model, dict_func, filters=None, fields=None, sorts=None, limit=None, marker_obj=None, page_reverse=False): query = get_collection_query(context, model, filters=filters, sorts=sorts, limit=limit, marker_obj=marker_obj, page_reverse=page_reverse) items = [ attributes.populate_project_info( dict_func(c, fields) if dict_func else c) for c in query ] if limit and page_reverse: items.reverse() return items def get_collection_count(context, model, filters=None): return get_collection_query(context, model, filters).count() neutron-12.1.1/neutron/db/ipam_backend_mixin.py0000664000175000017500000011007513553660047021557 0ustar zuulzuul00000000000000# Copyright (c) 2015 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import copy import itertools import netaddr from neutron_lib.api.definitions import ip_allocation as ipalloc_apidef from neutron_lib.api.definitions import portbindings from neutron_lib.api import validators from neutron_lib import constants as const from neutron_lib import exceptions as exc from oslo_config import cfg from oslo_log import log as logging from sqlalchemy import and_, or_ from sqlalchemy.orm import exc as orm_exc from neutron._i18n import _ from neutron.common import exceptions as n_exc from neutron.common import ipv6_utils from neutron.common import utils as common_utils from neutron.db import _model_query as model_query from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.db import db_base_plugin_common from neutron.db.models import segment as segment_model from neutron.db.models import subnet_service_type as sst_model from neutron.db import models_v2 from neutron.extensions import segment from neutron.ipam import exceptions as ipam_exceptions from neutron.ipam import utils as ipam_utils from neutron.objects import network as network_obj from neutron.objects import subnet as subnet_obj from neutron.services.segments import exceptions as segment_exc LOG = logging.getLogger(__name__) class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon): """Contains IPAM specific code which is common for both backends. """ # Tracks changes in ip allocation for port using namedtuple Changes = collections.namedtuple('Changes', 'add original remove') @staticmethod def _gateway_ip_str(subnet, cidr_net): if subnet.get('gateway_ip') is const.ATTR_NOT_SPECIFIED: if subnet.get('version') == const.IP_VERSION_6: return str(netaddr.IPNetwork(cidr_net).network) else: return str(netaddr.IPNetwork(cidr_net).network + 1) return subnet.get('gateway_ip') @staticmethod def pools_to_ip_range(ip_pools): ip_range_pools = [] for ip_pool in ip_pools: try: ip_range_pools.append(netaddr.IPRange(ip_pool['start'], ip_pool['end'])) except netaddr.AddrFormatError: LOG.info("Found invalid IP address in pool: " "%(start)s - %(end)s:", {'start': ip_pool['start'], 'end': ip_pool['end']}) raise n_exc.InvalidAllocationPool(pool=ip_pool) return ip_range_pools def delete_subnet(self, context, subnet_id): pass def validate_pools_with_subnetpool(self, subnet): """Verifies that allocation pools are set correctly Allocation pools can be set for specific subnet request only """ has_allocpool = validators.is_attr_set(subnet['allocation_pools']) is_any_subnetpool_request = not validators.is_attr_set(subnet['cidr']) if is_any_subnetpool_request and has_allocpool: reason = _("allocation_pools allowed only " "for specific subnet requests.") raise exc.BadRequest(resource='subnets', msg=reason) def _validate_ip_version_with_subnetpool(self, subnet, subnetpool): """Validates ip version for subnet_pool and requested subnet""" ip_version = subnet.get('ip_version') has_ip_version = validators.is_attr_set(ip_version) if has_ip_version and ip_version != subnetpool.ip_version: args = {'req_ver': str(subnet['ip_version']), 'pool_ver': str(subnetpool.ip_version)} reason = _("Cannot allocate IPv%(req_ver)s subnet from " "IPv%(pool_ver)s subnet pool") % args raise exc.BadRequest(resource='subnets', msg=reason) def _update_db_port(self, context, db_port, new_port, network_id, new_mac): # Remove all attributes in new_port which are not in the port DB model # and then update the port if (new_mac and new_mac != db_port.mac_address and self._is_mac_in_use(context, network_id, new_mac)): raise exc.MacAddressInUse(net_id=network_id, mac=new_mac) db_port.update(db_utils.filter_non_model_columns(new_port, models_v2.Port)) def _update_subnet_host_routes(self, context, id, s): def _combine(ht): return "{}_{}".format(ht['destination'], ht['nexthop']) old_route_list = self._get_route_by_subnet(context, id) new_route_set = set([_combine(route) for route in s['host_routes']]) old_route_set = set([_combine(route) for route in old_route_list]) for route_str in old_route_set - new_route_set: for route in old_route_list: if _combine(route) == route_str: route.delete() for route_str in new_route_set - old_route_set: route = subnet_obj.Route(context, destination=common_utils.AuthenticIPNetwork( route_str.partition("_")[0]), nexthop=netaddr.IPAddress(route_str.partition("_")[2]), subnet_id=id) route.create() # Gather host routes for result new_routes = [] for route_str in new_route_set: new_routes.append( {'destination': route_str.partition("_")[0], 'nexthop': route_str.partition("_")[2]}) del s["host_routes"] return new_routes def _update_subnet_dns_nameservers(self, context, id, s): new_dns_addr_list = s["dns_nameservers"] # NOTE(changzhi) delete all dns nameservers from db # when update subnet's DNS nameservers. And store new # nameservers with order one by one. subnet_obj.DNSNameServer.delete_objects(context, subnet_id=id) for order, server in enumerate(new_dns_addr_list): dns = subnet_obj.DNSNameServer(context, address=server, order=order, subnet_id=id) dns.create() del s["dns_nameservers"] return new_dns_addr_list @db_api.context_manager.writer def _update_subnet_allocation_pools(self, context, subnet_id, s): subnet_obj.IPAllocationPool.delete_objects(context, subnet_id=subnet_id) pools = [(netaddr.IPAddress(p.first, p.version).format(), netaddr.IPAddress(p.last, p.version).format()) for p in s['allocation_pools']] for p in pools: subnet_obj.IPAllocationPool(context, start=p[0], end=p[1], subnet_id=subnet_id).create() # Gather new pools for result result_pools = [{'start': p[0], 'end': p[1]} for p in pools] del s['allocation_pools'] return result_pools def _update_subnet_service_types(self, context, subnet_id, s): subnet_obj.SubnetServiceType.delete_objects(context, subnet_id=subnet_id) updated_types = s.pop('service_types') for service_type in updated_types: new_type = subnet_obj.SubnetServiceType(context, subnet_id=subnet_id, service_type=service_type) new_type.create() return updated_types def update_db_subnet(self, context, subnet_id, s, oldpools): changes = {} if "dns_nameservers" in s: changes['dns_nameservers'] = ( self._update_subnet_dns_nameservers(context, subnet_id, s)) if "host_routes" in s: changes['host_routes'] = self._update_subnet_host_routes( context, subnet_id, s) if "allocation_pools" in s: changes['allocation_pools'] = ( self._update_subnet_allocation_pools(context, subnet_id, s)) if "service_types" in s: changes['service_types'] = ( self._update_subnet_service_types(context, subnet_id, s)) subnet_obj = self._get_subnet_object(context, subnet_id) subnet_obj.update_fields(s) subnet_obj.update() return subnet_obj, changes def _validate_subnet_cidr(self, context, network, new_subnet_cidr): """Validate the CIDR for a subnet. Verifies the specified CIDR does not overlap with the ones defined for the other subnets specified for this network, or with any other CIDR if overlapping IPs are disabled. Does not apply to subnets with temporary IPv6 Prefix Delegation CIDRs (::/64). """ new_subnet_ipset = netaddr.IPSet([new_subnet_cidr]) # Disallow subnets with prefix length 0 as they will lead to # dnsmasq failures (see bug 1362651). # This is not a discrimination against /0 subnets. # A /0 subnet is conceptually possible but hardly a practical # scenario for neutron's use cases. for cidr in new_subnet_ipset.iter_cidrs(): if cidr.prefixlen == 0: err_msg = _("0 is not allowed as CIDR prefix length") raise exc.InvalidInput(error_message=err_msg) if cfg.CONF.allow_overlapping_ips: subnet_list = network.subnets else: subnet_list = self._get_subnets(context) for subnet in subnet_list: if ((netaddr.IPSet([subnet.cidr]) & new_subnet_ipset) and str(subnet.cidr) != const.PROVISIONAL_IPV6_PD_PREFIX): # don't give out details of the overlapping subnet err_msg = ("Requested subnet with cidr: %(cidr)s for " "network: %(network_id)s overlaps with another " "subnet" % {'cidr': new_subnet_cidr, 'network_id': network.id}) LOG.info("Validation for CIDR: %(new_cidr)s failed - " "overlaps with subnet %(subnet_id)s " "(CIDR: %(cidr)s)", {'new_cidr': new_subnet_cidr, 'subnet_id': subnet.id, 'cidr': subnet.cidr}) raise exc.InvalidInput(error_message=err_msg) def _validate_network_subnetpools(self, network, new_subnetpool_id, ip_version): """Validate all subnets on the given network have been allocated from the same subnet pool as new_subnetpool_id """ for subnet in network.subnets: if (subnet.ip_version == ip_version and new_subnetpool_id != subnet.subnetpool_id): raise n_exc.NetworkSubnetPoolAffinityError() def validate_allocation_pools(self, ip_pools, subnet_cidr): """Validate IP allocation pools. Verify start and end address for each allocation pool are valid, ie: constituted by valid and appropriately ordered IP addresses. Also, verify pools do not overlap among themselves. Finally, verify that each range fall within the subnet's CIDR. """ subnet = netaddr.IPNetwork(subnet_cidr) subnet_first_ip = netaddr.IPAddress(subnet.first + 1) # last address is broadcast in v4 subnet_last_ip = netaddr.IPAddress(subnet.last - (subnet.version == 4)) LOG.debug("Performing IP validity checks on allocation pools") ip_sets = [] for ip_pool in ip_pools: start_ip = netaddr.IPAddress(ip_pool.first, ip_pool.version) end_ip = netaddr.IPAddress(ip_pool.last, ip_pool.version) if (start_ip.version != subnet.version or end_ip.version != subnet.version): LOG.info("Specified IP addresses do not match " "the subnet IP version") raise n_exc.InvalidAllocationPool(pool=ip_pool) if start_ip < subnet_first_ip or end_ip > subnet_last_ip: LOG.info("Found pool larger than subnet " "CIDR:%(start)s - %(end)s", {'start': start_ip, 'end': end_ip}) raise n_exc.OutOfBoundsAllocationPool( pool=ip_pool, subnet_cidr=subnet_cidr) # Valid allocation pool # Create an IPSet for it for easily verifying overlaps ip_sets.append(netaddr.IPSet(ip_pool.cidrs())) LOG.debug("Checking for overlaps among allocation pools " "and gateway ip") ip_ranges = ip_pools[:] # Use integer cursors as an efficient way for implementing # comparison and avoiding comparing the same pair twice for l_cursor in range(len(ip_sets)): for r_cursor in range(l_cursor + 1, len(ip_sets)): if ip_sets[l_cursor] & ip_sets[r_cursor]: l_range = ip_ranges[l_cursor] r_range = ip_ranges[r_cursor] LOG.info("Found overlapping ranges: %(l_range)s and " "%(r_range)s", {'l_range': l_range, 'r_range': r_range}) raise n_exc.OverlappingAllocationPools( pool_1=l_range, pool_2=r_range, subnet_cidr=subnet_cidr) def _validate_segment(self, context, network_id, segment_id): query = context.session.query(models_v2.Subnet.segment_id) query = query.filter(models_v2.Subnet.network_id == network_id) associated_segments = set(row.segment_id for row in query) if None in associated_segments and len(associated_segments) > 1: raise segment_exc.SubnetsNotAllAssociatedWithSegments( network_id=network_id) if segment_id: segment = network_obj.NetworkSegment.get_object(context, id=segment_id) if segment.network_id != network_id: raise segment_exc.NetworkIdsDontMatch( subnet_network=network_id, segment_id=segment_id) if segment.is_dynamic: raise segment_exc.SubnetCantAssociateToDynamicSegment() def _get_subnet_for_fixed_ip(self, context, fixed, subnets): # Subnets are all the subnets belonging to the same network. if not subnets: msg = _('IP allocation requires subnets for network') raise exc.InvalidInput(error_message=msg) if 'subnet_id' in fixed: def get_matching_subnet(): for subnet in subnets: if subnet['id'] == fixed['subnet_id']: return subnet subnet = get_matching_subnet() if not subnet: subnet_obj = self._get_subnet_object(context, fixed['subnet_id']) msg = (_("Failed to create port on network %(network_id)s" ", because fixed_ips included invalid subnet " "%(subnet_id)s") % {'network_id': subnet_obj.network_id, 'subnet_id': fixed['subnet_id']}) raise exc.InvalidInput(error_message=msg) # Ensure that the IP is valid on the subnet if ('ip_address' in fixed and not ipam_utils.check_subnet_ip(subnet['cidr'], fixed['ip_address'], fixed['device_owner'])): raise exc.InvalidIpForSubnet(ip_address=fixed['ip_address']) return subnet if 'ip_address' not in fixed: msg = _('IP allocation requires subnet_id or ip_address') raise exc.InvalidInput(error_message=msg) for subnet in subnets: if ipam_utils.check_subnet_ip(subnet['cidr'], fixed['ip_address'], fixed['device_owner']): return subnet raise exc.InvalidIpForNetwork(ip_address=fixed['ip_address']) def generate_pools(self, cidr, gateway_ip): return ipam_utils.generate_pools(cidr, gateway_ip) def _prepare_allocation_pools(self, allocation_pools, cidr, gateway_ip): """Returns allocation pools represented as list of IPRanges""" if not validators.is_attr_set(allocation_pools): return self.generate_pools(cidr, gateway_ip) ip_range_pools = self.pools_to_ip_range(allocation_pools) self.validate_allocation_pools(ip_range_pools, cidr) if gateway_ip: self.validate_gw_out_of_pools(gateway_ip, ip_range_pools) return ip_range_pools def validate_gw_out_of_pools(self, gateway_ip, pools): for pool_range in pools: if netaddr.IPAddress(gateway_ip) in pool_range: raise n_exc.GatewayConflictWithAllocationPools( pool=pool_range, ip_address=gateway_ip) def _is_ip_required_by_subnet(self, context, subnet_id, device_owner): # For ports that are not router ports, retain any automatic # (non-optional, e.g. IPv6 SLAAC) addresses. # NOTE: Need to check the SNAT ports for DVR routers here since # they consume an IP. if device_owner in const.ROUTER_INTERFACE_OWNERS_SNAT: return True subnet_obj = self._get_subnet_object(context, subnet_id) return not (ipv6_utils.is_auto_address_subnet(subnet_obj) and not ipv6_utils.is_ipv6_pd_enabled(subnet_obj)) def _get_changed_ips_for_port(self, context, original_ips, new_ips, device_owner): """Calculate changes in IPs for the port.""" # Collect auto addressed subnet ids that has to be removed on update delete_subnet_ids = set(ip['subnet_id'] for ip in new_ips if ip.get('delete_subnet')) ips = [ip for ip in new_ips if ip.get('subnet_id') not in delete_subnet_ids] add_ips, prev_ips, remove_candidates = [], [], [] # Consider fixed_ips that specify a specific address first to see if # they already existed in original_ips or are completely new. orig_by_ip = {ip['ip_address']: ip for ip in original_ips} for ip in ips: if 'ip_address' not in ip: continue original = orig_by_ip.pop(ip['ip_address'], None) if original: prev_ips.append(original) else: add_ips.append(ip) # Consider fixed_ips that don't specify ip_address. Try to match them # up with originals to see if they can be reused. Create a new map of # the remaining, unmatched originals for this step. orig_by_subnet = collections.defaultdict(list) for ip in orig_by_ip.values(): orig_by_subnet[ip['subnet_id']].append(ip) for ip in ips: if 'ip_address' in ip: continue orig = orig_by_subnet.get(ip['subnet_id']) if not orig: add_ips.append(ip) continue # Try to match this new request up with an existing IP orig_ip = orig.pop() if ipv6_utils.is_eui64_address(orig_ip['ip_address']): # In case of EUI64 address, the prefix may have changed so # we want to make sure IPAM gets a chance to re-allocate # it. This is safe in general because EUI-64 addresses # always come out the same given the prefix doesn't change. add_ips.append(ip) remove_candidates.append(orig_ip) else: # Reuse the existing address on this subnet. prev_ips.append(orig_ip) # Iterate through any unclaimed original ips (orig_by_subnet) *and* the # remove_candidates with this compound chain. maybe_remove = itertools.chain( itertools.chain.from_iterable(orig_by_subnet.values()), remove_candidates) # Mark ip for removing if it is not found in new_ips # and subnet requires ip to be set manually. # For auto addressed subnet leave ip unchanged # unless it is explicitly marked for delete. remove_ips = [] for ip in maybe_remove: subnet_id = ip['subnet_id'] ip_required = self._is_ip_required_by_subnet(context, subnet_id, device_owner) if ip_required or subnet_id in delete_subnet_ids: remove_ips.append(ip) else: prev_ips.append(ip) return self.Changes(add=add_ips, original=prev_ips, remove=remove_ips) def delete_port(self, context, port_id): query = (context.session.query(models_v2.Port). enable_eagerloads(False).filter_by(id=port_id)) # Use of the ORM mapper is needed for ensuring appropriate resource # tracking; otherwise SQL Alchemy events won't be triggered. # For more info check 'caveats' in doc/source/devref/quota.rst try: context.session.delete(query.first()) except orm_exc.UnmappedInstanceError: LOG.debug("Port %s was not found and therefore no delete " "operation was performed", port_id) def _save_subnet(self, context, network, subnet_args, dns_nameservers, host_routes, subnet_request): self._validate_subnet_cidr(context, network, subnet_args['cidr']) self._validate_network_subnetpools(network, subnet_args['subnetpool_id'], subnet_args['ip_version']) service_types = subnet_args.pop('service_types', []) segment_id = subnet_args.get('segment_id') if segment_id: # TODO(slaweq): integrate check if segment exists in # self._validate_segment() method segment = network_obj.NetworkSegment.get_object(context, id=segment_id) if not segment: raise segment_exc.SegmentNotFound(segment_id=segment_id) subnet = subnet_obj.Subnet(context, **subnet_args) subnet.create() # TODO(slaweq): when check is segment exists will be integrated in # self._validate_segment() method, it should be moved to be done before # subnet object is created self._validate_segment(context, network['id'], segment_id) # NOTE(changzhi) Store DNS nameservers with order into DB one # by one when create subnet with DNS nameservers if validators.is_attr_set(dns_nameservers): for order, server in enumerate(dns_nameservers): dns = subnet_obj.DNSNameServer(context, address=server, order=order, subnet_id=subnet.id) dns.create() if validators.is_attr_set(host_routes): for rt in host_routes: route = subnet_obj.Route( context, subnet_id=subnet.id, destination=common_utils.AuthenticIPNetwork( rt['destination']), nexthop=netaddr.IPAddress(rt['nexthop'])) route.create() if validators.is_attr_set(service_types): for service_type in service_types: service_type_obj = subnet_obj.SubnetServiceType( context, subnet_id=subnet.id, service_type=service_type) service_type_obj.create() self.save_allocation_pools(context, subnet, subnet_request.allocation_pools) return subnet_obj.Subnet.get_object(context, id=subnet.id) def _classify_subnets(self, context, subnets): """Split into v4, v6 stateless and v6 stateful subnets""" v4, v6_stateful, v6_stateless = [], [], [] for subnet in subnets: if subnet['ip_version'] == 4: v4.append(subnet) elif not ipv6_utils.is_auto_address_subnet(subnet): v6_stateful.append(subnet) else: v6_stateless.append(subnet) return v4, v6_stateful, v6_stateless def _update_ips_for_pd_subnet(self, context, subnets, fixed_ips, mac_address=None): fixed_ip_list = [] subnet_set = {fixed['subnet_id'] for fixed in fixed_ips if 'subnet_id' in fixed} pd_subnets = [s for s in subnets if (s['id'] in subnet_set and ipv6_utils.is_ipv6_pd_enabled(s))] for subnet in pd_subnets: # Already checked subnet validity in _get_subnet_for_fixed_ip if mac_address: fixed_ip_list.append({'subnet_id': subnet['id'], 'subnet_cidr': subnet['cidr'], 'eui64_address': True, 'mac': mac_address}) else: fixed_ip_list.append({'subnet_id': subnet['id']}) return fixed_ip_list def _query_subnets_on_network(self, context, network_id): query = model_query.get_collection_query(context, models_v2.Subnet) return query.filter(models_v2.Subnet.network_id == network_id) def _query_filter_service_subnets(self, query, service_type): # TODO(korzen) use SubnetServiceType OVO here Subnet = models_v2.Subnet ServiceType = sst_model.SubnetServiceType query = query.add_entity(ServiceType) query = query.outerjoin(ServiceType) query = query.filter(or_( ServiceType.service_type.is_(None), ServiceType.service_type == service_type, # Allow DHCP ports to be created on subnets of any # service type when DHCP is enabled on the subnet. and_(Subnet.enable_dhcp.is_(True), service_type == const.DEVICE_OWNER_DHCP))) return query.from_self(Subnet) @staticmethod def _query_filter_by_segment_host_mapping(query, host): """Excludes subnets on segments not reachable by the host The query gets two kinds of subnets: those that are on segments that the host can reach and those that are not on segments at all (assumed reachable by all hosts). Hence, subnets on segments that the host *cannot* reach are excluded. """ Subnet = models_v2.Subnet SegmentHostMapping = segment_model.SegmentHostMapping # A host has been provided. Consider these two scenarios # 1. Not a routed network: subnets are not on segments # 2. Is a routed network: only subnets on segments mapped to host # The following join query returns results for either. The two are # guaranteed to be mutually exclusive when subnets are created. query = query.add_entity(SegmentHostMapping) query = query.outerjoin( SegmentHostMapping, and_(Subnet.segment_id == SegmentHostMapping.segment_id, SegmentHostMapping.host == host)) # Essentially "segment_id IS NULL XNOR host IS NULL" query = query.filter(or_(and_(Subnet.segment_id.isnot(None), SegmentHostMapping.host.isnot(None)), and_(Subnet.segment_id.is_(None), SegmentHostMapping.host.is_(None)))) return query @staticmethod def _query_exclude_subnets_on_segments(query): """Excludes all subnets associated with segments For the case where the host is not known, we don't consider any subnets that are on segments. But, we still consider subnets that are not associated with any segment (i.e. for non-routed networks). """ return query.filter(models_v2.Subnet.segment_id.is_(None)) @staticmethod def is_host_set(host): """Utility to tell if the host is set in the port binding""" # This seems redundant, but its not. Host is unset if its None, '', # or ATTR_NOT_SPECIFIED due to differences in host binding # implementations. return host and validators.is_attr_set(host) def _ipam_get_subnets(self, context, network_id, host, service_type=None, fixed_configured=False): """Return eligible subnets If no eligible subnets are found, determine why and potentially raise an appropriate error. """ subnets = self._find_candidate_subnets(context, network_id, host, service_type, fixed_configured) if subnets: subnet_dicts = [self._make_subnet_dict(subnet, context=context) for subnet in subnets] # Give priority to subnets with service_types return sorted( subnet_dicts, key=lambda subnet: not subnet.get('service_types')) # Determine why we found no subnets to raise the right error query = self._query_subnets_on_network(context, network_id) if self.is_host_set(host): # Empty because host isn't mapped to a segment with a subnet? s_query = query.filter(models_v2.Subnet.segment_id.isnot(None)) if s_query.limit(1).count() != 0: # It is a routed network but no subnets found for host raise segment_exc.HostNotConnectedToAnySegment( host=host, network_id=network_id) if not query.limit(1).count(): # Network has *no* subnets of any kind. This isn't an error. return [] # Does filtering ineligible service subnets makes the list empty? query = self._query_filter_service_subnets(query, service_type) if query.limit(1).count(): # No, must be a deferred IP port because there are matching # subnets. Happens on routed networks when host isn't known. raise ipam_exceptions.DeferIpam() raise ipam_exceptions.IpAddressGenerationFailureNoMatchingSubnet( network_id=network_id, service_type=service_type) def _find_candidate_subnets(self, context, network_id, host, service_type, fixed_configured): """Find canditate subnets for the network, host, and service_type""" query = self._query_subnets_on_network(context, network_id) query = self._query_filter_service_subnets(query, service_type) # Select candidate subnets and return them if not self.is_host_set(host): if fixed_configured: # If fixed_ips in request and host is not known all subnets on # the network are candidates. Host/Segment will be validated # on port update with binding:host_id set. Allocation _cannot_ # be deferred as requested fixed_ips would then be lost. return query.all() # If the host isn't known, we can't allocate on a routed network. # So, exclude any subnets attached to segments. return self._query_exclude_subnets_on_segments(query).all() # The host is known. Consider both routed and non-routed networks results = self._query_filter_by_segment_host_mapping(query, host).all() # For now, we're using a simplifying assumption that a host will only # touch one segment in a given routed network. Raise exception # otherwise. This restriction may be relaxed as use cases for multiple # mappings are understood. segment_ids = {subnet.segment_id for subnet, mapping in results if mapping} if 1 < len(segment_ids): raise segment_exc.HostConnectedToMultipleSegments( host=host, network_id=network_id) return [subnet for subnet, _mapping in results] def _make_subnet_args(self, detail, subnet, subnetpool_id): args = super(IpamBackendMixin, self)._make_subnet_args( detail, subnet, subnetpool_id) if validators.is_attr_set(subnet.get(segment.SEGMENT_ID)): args['segment_id'] = subnet[segment.SEGMENT_ID] if validators.is_attr_set(subnet.get('service_types')): args['service_types'] = subnet['service_types'] return args def update_port(self, context, old_port_db, old_port, new_port): """Update the port IPs Updates the port's IPs based on any new fixed_ips passed in or if deferred IP allocation is in effect because allocation requires host binding information that wasn't provided until port update. :param old_port_db: The port database record :param old_port: A port dict created by calling _make_port_dict. This must be called before calling this method in order to load data from extensions, specifically host binding. :param new_port: The new port data passed through the API. """ old_host = old_port.get(portbindings.HOST_ID) new_host = new_port.get(portbindings.HOST_ID) host = new_host if validators.is_attr_set(new_host) else old_host changes = self.update_port_with_ips(context, host, old_port_db, new_port, new_port.get('mac_address')) fixed_ips_requested = validators.is_attr_set(new_port.get('fixed_ips')) old_ips = old_port.get('fixed_ips') deferred_ip_allocation = ( old_port.get('ip_allocation') == ipalloc_apidef.IP_ALLOCATION_DEFERRED and host and not old_host and not old_ips and not fixed_ips_requested) if not deferred_ip_allocation: # Check that any existing IPs are valid on the new segment new_host_requested = host and host != old_host if old_ips and new_host_requested and not fixed_ips_requested: valid_subnets = self._ipam_get_subnets( context, old_port['network_id'], host, service_type=old_port.get('device_owner')) valid_subnet_ids = {s['id'] for s in valid_subnets} for fixed_ip in old_ips: if fixed_ip['subnet_id'] not in valid_subnet_ids: raise segment_exc.HostNotCompatibleWithFixedIps( host=host, port_id=old_port['id']) return changes # Allocate as if this were the port create. port_copy = copy.deepcopy(old_port) port_copy['fixed_ips'] = const.ATTR_NOT_SPECIFIED port_copy.update(new_port) context.session.expire(old_port_db, ['fixed_ips']) ips = self.allocate_ips_for_port_and_store( context, {'port': port_copy}, port_copy['id']) getattr(old_port_db, 'fixed_ips') # refresh relationship before return return self.Changes(add=ips, original=[], remove=[]) neutron-12.1.1/neutron/db/sqlalchemytypes.py0000664000175000017500000000541113553660047021202 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Custom SQLAlchemy types.""" import netaddr from sqlalchemy import types from neutron._i18n import _ class IPAddress(types.TypeDecorator): impl = types.String(64) def process_result_value(self, value, dialect): return netaddr.IPAddress(value) def process_bind_param(self, value, dialect): if not isinstance(value, netaddr.IPAddress): raise AttributeError(_("Received type '%(type)s' and value " "'%(value)s'. Expecting netaddr.IPAddress " "type.") % {'type': type(value), 'value': value}) return str(value) class CIDR(types.TypeDecorator): impl = types.String(64) def process_result_value(self, value, dialect): return netaddr.IPNetwork(value) def process_bind_param(self, value, dialect): if not isinstance(value, netaddr.IPNetwork): raise AttributeError(_("Received type '%(type)s' and value " "'%(value)s'. Expecting netaddr.IPNetwork " "type.") % {'type': type(value), 'value': value}) return str(value) class MACAddress(types.TypeDecorator): impl = types.String(64) def process_result_value(self, value, dialect): return netaddr.EUI(value) def process_bind_param(self, value, dialect): if not isinstance(value, netaddr.EUI): raise AttributeError(_("Received type '%(type)s' and value " "'%(value)s'. Expecting netaddr.EUI " "type.") % {'type': type(value), 'value': value}) return str(value) class TruncatedDateTime(types.TypeDecorator): """Truncates microseconds. Use this for datetime fields so we don't have to worry about DB-specific behavior when it comes to rounding/truncating microseconds off of timestamps. """ impl = types.DateTime def process_bind_param(self, value, dialect): return value.replace(microsecond=0) if value else value process_result_value = process_bind_param neutron-12.1.1/neutron/db/l3_db.py0000664000175000017500000027565313553660047016757 0ustar zuulzuul00000000000000# Copyright 2012 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import random import netaddr from neutron_lib.api.definitions import external_net as extnet_apidef from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.api.definitions import portbindings as pb from neutron_lib.api import validators from neutron_lib.callbacks import events from neutron_lib.callbacks import exceptions from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context as n_ctx from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import l3 as l3_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from neutron_lib.services import base as base_services from oslo_log import log as logging from oslo_utils import uuidutils from sqlalchemy import orm from sqlalchemy.orm import exc from neutron._i18n import _ from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api from neutron.common import ipv6_utils from neutron.common import rpc as n_rpc from neutron.common import utils from neutron.db import _model_query as model_query from neutron.db import _resource_extend as resource_extend from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.db import common_db_mixin from neutron.db.models import l3 as l3_models from neutron.db import models_v2 from neutron.db import standardattrdescription_db as st_attr from neutron.extensions import l3 from neutron.extensions import qos_fip from neutron.objects import base as base_obj from neutron.objects import ports as port_obj from neutron.objects import router as l3_obj from neutron.plugins.common import utils as p_utils from neutron import worker as neutron_worker LOG = logging.getLogger(__name__) DEVICE_OWNER_HA_REPLICATED_INT = constants.DEVICE_OWNER_HA_REPLICATED_INT DEVICE_OWNER_ROUTER_INTF = constants.DEVICE_OWNER_ROUTER_INTF DEVICE_OWNER_ROUTER_GW = constants.DEVICE_OWNER_ROUTER_GW DEVICE_OWNER_FLOATINGIP = constants.DEVICE_OWNER_FLOATINGIP EXTERNAL_GW_INFO = l3_apidef.EXTERNAL_GW_INFO # Maps API field to DB column # API parameter name and Database column names may differ. # Useful to keep the filtering between API and Database. API_TO_DB_COLUMN_MAP = {'port_id': 'fixed_port_id'} CORE_ROUTER_ATTRS = ('id', 'name', 'tenant_id', 'admin_state_up', 'status') def can_port_be_bound_to_virtual_bridge(port): """Returns if port can be bound to a virtual bridge (e.g.: LB, OVS) :param port: (dict) A port dictionary. :returns: True if the port VNIC type is 'normal'; False in any other case. """ return port[pb.VNIC_TYPE] == pb.VNIC_NORMAL @registry.has_registry_receivers class L3_NAT_dbonly_mixin(l3.RouterPluginBase, base_services.WorkerBase, st_attr.StandardAttrDescriptionMixin): """Mixin class to add L3/NAT router methods to db_base_plugin_v2.""" router_device_owners = ( DEVICE_OWNER_HA_REPLICATED_INT, DEVICE_OWNER_ROUTER_INTF, DEVICE_OWNER_ROUTER_GW, DEVICE_OWNER_FLOATINGIP ) _dns_integration = None _fip_qos = None def __new__(cls, *args, **kwargs): inst = super(L3_NAT_dbonly_mixin, cls).__new__(cls, *args, **kwargs) inst._start_janitor() return inst @staticmethod @registry.receives(resources.PORT, [events.BEFORE_DELETE]) def _prevent_l3_port_delete_callback(resource, event, trigger, **kwargs): context = kwargs['context'] port_id = kwargs['port_id'] port_check = kwargs['port_check'] l3plugin = directory.get_plugin(plugin_constants.L3) if l3plugin and port_check: l3plugin.prevent_l3_port_deletion(context, port_id) @property def _is_dns_integration_supported(self): if self._dns_integration is None: self._dns_integration = ( utils.is_extension_supported(self._core_plugin, 'dns-integration') or utils.is_extension_supported(self._core_plugin, 'dns-domain-ports')) return self._dns_integration @property def _is_fip_qos_supported(self): if self._fip_qos is None: # Check L3 service plugin self._fip_qos = utils.is_extension_supported( self, qos_fip.FIP_QOS_ALIAS) return self._fip_qos @property def _core_plugin(self): return directory.get_plugin() def _start_janitor(self): """Starts the periodic job that cleans up broken complex resources. This job will look for things like floating IP ports without an associated floating IP and delete them 5 minutes after detection. """ interval = 60 * 5 # only every 5 minutes. cleanups should be rare initial_delay = random.randint(0, interval) # splay multiple servers janitor = neutron_worker.PeriodicWorker(self._clean_garbage, interval, initial_delay) self.add_worker(janitor) def _clean_garbage(self): if not hasattr(self, '_candidate_broken_fip_ports'): self._candidate_broken_fip_ports = set() context = n_ctx.get_admin_context() candidates = self._get_dead_floating_port_candidates(context) # just because a port is in 'candidates' doesn't necessarily mean # it's broken, we could have just caught it before it was updated. # We confirm by waiting until the next call of this function to see # if it persists. to_cleanup = candidates & self._candidate_broken_fip_ports self._candidate_broken_fip_ports = candidates - to_cleanup for port_id in to_cleanup: # ensure it wasn't just a failure to update device_id before we # delete it try: self._fix_or_kill_floating_port(context, port_id) except Exception: LOG.exception("Error cleaning up floating IP port: %s", port_id) def _fix_or_kill_floating_port(self, context, port_id): pager = base_obj.Pager(limit=1) fips = l3_obj.FloatingIP.get_objects( context, _pager=pager, floating_port_id=port_id) if fips: LOG.warning("Found incorrect device_id on floating port " "%(pid)s, correcting to %(fip)s.", {'pid': port_id, 'fip': fips[0].id}) self._core_plugin.update_port( context, port_id, {'port': {'device_id': fips[0].id}}) else: LOG.warning("Found floating IP port %s without floating IP, " "deleting.", port_id) self._core_plugin.delete_port( context, port_id, l3_port_check=False) registry.notify(resources.FLOATING_IP, events.AFTER_DELETE, self, **fips[0]) def _get_dead_floating_port_candidates(self, context): filters = {'device_id': ['PENDING'], 'device_owner': [DEVICE_OWNER_FLOATINGIP]} return {p['id'] for p in self._core_plugin.get_ports(context, filters)} def _get_router(self, context, router_id): try: router = model_query.get_by_id( context, l3_models.Router, router_id) except exc.NoResultFound: raise l3_exc.RouterNotFound(router_id=router_id) return router def _make_router_dict(self, router, fields=None, process_extensions=True): res = dict((key, router[key]) for key in CORE_ROUTER_ATTRS) if router['gw_port_id']: ext_gw_info = { 'network_id': router.gw_port['network_id'], 'external_fixed_ips': [{'subnet_id': ip["subnet_id"], 'ip_address': ip["ip_address"]} for ip in router.gw_port['fixed_ips']]} else: ext_gw_info = None res.update({ EXTERNAL_GW_INFO: ext_gw_info, 'gw_port_id': router['gw_port_id'], }) # NOTE(salv-orlando): The following assumes this mixin is used in a # class inheriting from CommonDbMixin, which is true for all existing # plugins. if process_extensions: resource_extend.apply_funcs(l3_apidef.ROUTERS, res, router) return db_utils.resource_fields(res, fields) def _create_router_db(self, context, router, tenant_id): """Create the DB object.""" router.setdefault('id', uuidutils.generate_uuid()) router['tenant_id'] = tenant_id registry.notify(resources.ROUTER, events.BEFORE_CREATE, self, context=context, router=router) with context.session.begin(subtransactions=True): # pre-generate id so it will be available when # configuring external gw port router_db = l3_models.Router( id=router['id'], tenant_id=router['tenant_id'], name=router['name'], admin_state_up=router['admin_state_up'], status=constants.ACTIVE, description=router.get('description')) context.session.add(router_db) registry.notify(resources.ROUTER, events.PRECOMMIT_CREATE, self, context=context, router=router, router_id=router['id'], router_db=router_db) return router_db def _update_gw_for_create_router(self, context, gw_info, router_id): if gw_info: router_db = self._get_router(context, router_id) self._update_router_gw_info(context, router_id, gw_info, router=router_db) @db_api.retry_if_session_inactive() def create_router(self, context, router): r = router['router'] gw_info = r.pop(EXTERNAL_GW_INFO, None) create = functools.partial(self._create_router_db, context, r, r['tenant_id']) delete = functools.partial(self.delete_router, context) update_gw = functools.partial(self._update_gw_for_create_router, context, gw_info) router_db, _unused = common_db_mixin.safe_creation(context, create, delete, update_gw, transaction=False) new_router = self._make_router_dict(router_db) registry.notify(resources.ROUTER, events.AFTER_CREATE, self, context=context, router_id=router_db.id, router=new_router, request_attrs=r, router_db=router_db) return new_router def _update_router_db(self, context, router_id, data): """Update the DB object.""" with context.session.begin(subtransactions=True): router_db = self._get_router(context, router_id) old_router = self._make_router_dict(router_db) if data: router_db.update(data) registry.publish(resources.ROUTER, events.PRECOMMIT_UPDATE, self, payload=events.DBEventPayload( context, request_body=data, states=(old_router,), resource_id=router_id, desired_state=router_db)) return router_db @db_api.retry_if_session_inactive() def update_router(self, context, id, router): r = router['router'] gw_info = r.pop(EXTERNAL_GW_INFO, constants.ATTR_NOT_SPECIFIED) original = self.get_router(context, id) # check whether router needs and can be rescheduled to the proper # l3 agent (associated with given external network); # do check before update in DB as an exception will be raised # in case no proper l3 agent found if gw_info != constants.ATTR_NOT_SPECIFIED: candidates = self._check_router_needs_rescheduling( context, id, gw_info) # Update the gateway outside of the DB update since it involves L2 # calls that don't make sense to rollback and may cause deadlocks # in a transaction. self._update_router_gw_info(context, id, gw_info) else: candidates = None router_db = self._update_router_db(context, id, r) if candidates: l3_plugin = directory.get_plugin(plugin_constants.L3) l3_plugin.reschedule_router(context, id, candidates) updated = self._make_router_dict(router_db) registry.notify(resources.ROUTER, events.AFTER_UPDATE, self, context=context, router_id=id, old_router=original, router=updated, request_attrs=r, router_db=router_db) return updated def _check_router_needs_rescheduling(self, context, router_id, gw_info): """Checks whether router's l3 agent can handle the given network When external_network_bridge is set, each L3 agent can be associated with at most one external network. If router's new external gateway is on other network then the router needs to be rescheduled to the proper l3 agent. If external_network_bridge is not set then the agent can support multiple external networks and rescheduling is not needed :return: list of candidate agents if rescheduling needed, None otherwise; raises exception if there is no eligible l3 agent associated with target external network """ # TODO(obondarev): rethink placement of this func as l3 db manager is # not really a proper place for agent scheduling stuff network_id = gw_info.get('network_id') if gw_info else None if not network_id: return nets = self._core_plugin.get_networks( context, {extnet_apidef.EXTERNAL: [True]}) # nothing to do if there is only one external network if len(nets) <= 1: return # first get plugin supporting l3 agent scheduling # (either l3 service plugin or core_plugin) l3_plugin = directory.get_plugin(plugin_constants.L3) if (not utils.is_extension_supported( l3_plugin, constants.L3_AGENT_SCHEDULER_EXT_ALIAS) or l3_plugin.router_scheduler is None): # that might mean that we are dealing with non-agent-based # implementation of l3 services return if not l3_plugin.router_supports_scheduling(context, router_id): return cur_agents = l3_plugin.list_l3_agents_hosting_router( context, router_id)['agents'] for agent in cur_agents: ext_net_id = agent['configurations'].get( 'gateway_external_network_id') ext_bridge = agent['configurations'].get( 'external_network_bridge', '') if (ext_net_id == network_id or (not ext_net_id and not ext_bridge)): return # otherwise find l3 agent with matching gateway_external_network_id active_agents = l3_plugin.get_l3_agents(context, active=True) router = { 'id': router_id, 'external_gateway_info': {'network_id': network_id} } candidates = l3_plugin.get_l3_agent_candidates(context, router, active_agents) if not candidates: msg = (_('No eligible l3 agent associated with external network ' '%s found') % network_id) raise n_exc.BadRequest(resource='router', msg=msg) return candidates def _create_router_gw_port(self, context, router, network_id, ext_ips): # Port has no 'tenant-id', as it is hidden from user port_data = {'tenant_id': '', # intentionally not set 'network_id': network_id, 'fixed_ips': ext_ips or constants.ATTR_NOT_SPECIFIED, 'device_id': router['id'], 'device_owner': DEVICE_OWNER_ROUTER_GW, 'admin_state_up': True, 'name': ''} gw_port = p_utils.create_port(self._core_plugin, context.elevated(), {'port': port_data}) if not gw_port['fixed_ips']: LOG.debug('No IPs available for external network %s', network_id) with p_utils.delete_port_on_error(self._core_plugin, context.elevated(), gw_port['id']): with context.session.begin(subtransactions=True): router.gw_port = self._core_plugin._get_port( context.elevated(), gw_port['id']) router_port = l3_obj.RouterPort( context, router_id=router.id, port_id=gw_port['id'], port_type=DEVICE_OWNER_ROUTER_GW ) context.session.add(router) router_port.create() def _validate_gw_info(self, context, gw_port, info, ext_ips): network_id = info['network_id'] if info else None if network_id: network_db = self._core_plugin._get_network(context, network_id) if not network_db.external: msg = _("Network %s is not an external network") % network_id raise n_exc.BadRequest(resource='router', msg=msg) if ext_ips: subnets = self._core_plugin.get_subnets_by_network(context, network_id) for s in subnets: if not s['gateway_ip']: continue for ext_ip in ext_ips: if ext_ip.get('ip_address') == s['gateway_ip']: msg = _("External IP %s is the same as the " "gateway IP") % ext_ip.get('ip_address') raise n_exc.BadRequest(resource='router', msg=msg) return network_id # NOTE(yamamoto): This method is an override point for plugins # inheriting this class. Do not optimize this out. def router_gw_port_has_floating_ips(self, context, router_id): """Return True if the router's gateway port is serving floating IPs.""" return bool(self.get_floatingips_count(context, {'router_id': [router_id]})) def _delete_current_gw_port(self, context, router_id, router, new_network_id): """Delete gw port if attached to an old network.""" port_requires_deletion = ( router.gw_port and router.gw_port['network_id'] != new_network_id) if not port_requires_deletion: return admin_ctx = context.elevated() old_network_id = router.gw_port['network_id'] if self.router_gw_port_has_floating_ips(admin_ctx, router_id): raise l3_exc.RouterExternalGatewayInUseByFloatingIp( router_id=router_id, net_id=router.gw_port['network_id']) gw_ips = [x['ip_address'] for x in router.gw_port['fixed_ips']] gw_port_id = router.gw_port['id'] self._delete_router_gw_port_db(context, router) self._core_plugin.delete_port( admin_ctx, gw_port_id, l3_port_check=False) with context.session.begin(subtransactions=True): context.session.refresh(router) registry.notify(resources.ROUTER_GATEWAY, events.AFTER_DELETE, self, router_id=router_id, context=context, router=router, network_id=old_network_id, new_network_id=new_network_id, gateway_ips=gw_ips) def _delete_router_gw_port_db(self, context, router): with context.session.begin(subtransactions=True): router.gw_port = None if router not in context.session: context.session.add(router) try: kwargs = {'context': context, 'router_id': router.id} registry.notify( resources.ROUTER_GATEWAY, events.BEFORE_DELETE, self, **kwargs) except exceptions.CallbackFailure as e: # NOTE(armax): preserve old check's behavior if len(e.errors) == 1: raise e.errors[0].error raise l3_exc.RouterInUse(router_id=router.id, reason=e) def _create_gw_port(self, context, router_id, router, new_network_id, ext_ips): new_valid_gw_port_attachment = ( new_network_id and (not router.gw_port or router.gw_port['network_id'] != new_network_id)) if new_valid_gw_port_attachment: subnets = self._core_plugin.get_subnets_by_network(context, new_network_id) try: kwargs = {'context': context, 'router_id': router_id, 'network_id': new_network_id, 'subnets': subnets} registry.notify( resources.ROUTER_GATEWAY, events.BEFORE_CREATE, self, **kwargs) except exceptions.CallbackFailure as e: # raise the underlying exception raise e.errors[0].error self._check_for_dup_router_subnets(context, router, new_network_id, subnets, include_gateway=True) self._create_router_gw_port(context, router, new_network_id, ext_ips) registry.notify(resources.ROUTER_GATEWAY, events.AFTER_CREATE, self._create_gw_port, gw_ips=ext_ips, network_id=new_network_id, router_id=router_id) def _update_current_gw_port(self, context, router_id, router, ext_ips): self._core_plugin.update_port(context, router.gw_port['id'], {'port': {'fixed_ips': ext_ips}}) context.session.expire(router.gw_port) def _update_router_gw_info(self, context, router_id, info, router=None): # TODO(salvatore-orlando): guarantee atomic behavior also across # operations that span beyond the model classes handled by this # class (e.g.: delete_port) router = router or self._get_router(context, router_id) gw_port = router.gw_port ext_ips = info.get('external_fixed_ips') if info else [] ext_ip_change = self._check_for_external_ip_change( context, gw_port, ext_ips) network_id = self._validate_gw_info(context, gw_port, info, ext_ips) if gw_port and ext_ip_change and gw_port['network_id'] == network_id: self._update_current_gw_port(context, router_id, router, ext_ips) else: self._delete_current_gw_port(context, router_id, router, network_id) self._create_gw_port(context, router_id, router, network_id, ext_ips) def _check_for_external_ip_change(self, context, gw_port, ext_ips): # determine if new external IPs differ from the existing fixed_ips if not ext_ips: # no external_fixed_ips were included return False if not gw_port: return True subnet_ids = set(ip['subnet_id'] for ip in gw_port['fixed_ips']) new_subnet_ids = set(f['subnet_id'] for f in ext_ips if f.get('subnet_id')) subnet_change = not new_subnet_ids == subnet_ids if subnet_change: return True ip_addresses = set(ip['ip_address'] for ip in gw_port['fixed_ips']) new_ip_addresses = set(f['ip_address'] for f in ext_ips if f.get('ip_address')) ip_address_change = not ip_addresses == new_ip_addresses return ip_address_change def _ensure_router_not_in_use(self, context, router_id): """Ensure that no internal network interface is attached to the router. """ router = self._get_router(context, router_id) device_owner = self._get_device_owner(context, router) if any(rp.port_type == device_owner for rp in router.attached_ports): raise l3_exc.RouterInUse(router_id=router_id) return router @db_api.retry_if_session_inactive() def delete_router(self, context, id): registry.notify(resources.ROUTER, events.BEFORE_DELETE, self, context=context, router_id=id) #TODO(nati) Refactor here when we have router insertion model router = self._ensure_router_not_in_use(context, id) original = self._make_router_dict(router) self._delete_current_gw_port(context, id, router, None) with context.session.begin(subtransactions=True): context.session.refresh(router) router_ports = router.attached_ports for rp in router_ports: self._core_plugin.delete_port(context.elevated(), rp.port.id, l3_port_check=False) with context.session.begin(subtransactions=True): context.session.refresh(router) registry.notify(resources.ROUTER, events.PRECOMMIT_DELETE, self, context=context, router_db=router, router_id=id) # we bump the revision even though we are about to delete to throw # staledataerror if something snuck in with a new interface router.bump_revision() context.session.flush() context.session.delete(router) registry.notify(resources.ROUTER, events.AFTER_DELETE, self, context=context, router_id=id, original=original) @db_api.retry_if_session_inactive() def get_router(self, context, id, fields=None): router = self._get_router(context, id) return self._make_router_dict(router, fields) @db_api.retry_if_session_inactive() def get_routers(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): marker_obj = db_utils.get_marker_obj(self, context, 'router', limit, marker) return model_query.get_collection(context, l3_models.Router, self._make_router_dict, filters=filters, fields=fields, sorts=sorts, limit=limit, marker_obj=marker_obj, page_reverse=page_reverse) @db_api.retry_if_session_inactive() def get_routers_count(self, context, filters=None): return model_query.get_collection_count(context, l3_models.Router, filters=filters) def _check_for_dup_router_subnets(self, context, router, network_id, new_subnets, include_gateway=False): # It's possible these ports are on the same network, but # different subnets. new_subnet_ids = {s['id'] for s in new_subnets} router_subnets = [] for p in (rp.port for rp in router.attached_ports): for ip in p['fixed_ips']: if ip['subnet_id'] in new_subnet_ids: msg = (_("Router already has a port on subnet %s") % ip['subnet_id']) raise n_exc.BadRequest(resource='router', msg=msg) gw_owner = (p.get('device_owner') == DEVICE_OWNER_ROUTER_GW) if include_gateway == gw_owner: router_subnets.append(ip['subnet_id']) # Ignore temporary Prefix Delegation CIDRs new_subnets = [s for s in new_subnets if s['cidr'] != constants.PROVISIONAL_IPV6_PD_PREFIX] id_filter = {'id': router_subnets} subnets = self._core_plugin.get_subnets(context.elevated(), filters=id_filter) for sub in subnets: cidr = sub['cidr'] ipnet = netaddr.IPNetwork(cidr) for s in new_subnets: new_cidr = s['cidr'] new_ipnet = netaddr.IPNetwork(new_cidr) match1 = netaddr.all_matching_cidrs(new_ipnet, [cidr]) match2 = netaddr.all_matching_cidrs(ipnet, [new_cidr]) if match1 or match2: data = {'subnet_cidr': new_cidr, 'subnet_id': s['id'], 'cidr': cidr, 'sub_id': sub['id']} msg = (_("Cidr %(subnet_cidr)s of subnet " "%(subnet_id)s overlaps with cidr %(cidr)s " "of subnet %(sub_id)s") % data) raise n_exc.BadRequest(resource='router', msg=msg) def _get_device_owner(self, context, router=None): """Get device_owner for the specified router.""" # NOTE(armando-migliaccio): in the base case this is invariant return DEVICE_OWNER_ROUTER_INTF def _validate_interface_info(self, interface_info, for_removal=False): port_id_specified = interface_info and 'port_id' in interface_info subnet_id_specified = interface_info and 'subnet_id' in interface_info if not (port_id_specified or subnet_id_specified): msg = _("Either subnet_id or port_id must be specified") raise n_exc.BadRequest(resource='router', msg=msg) for key in ('port_id', 'subnet_id'): if key not in interface_info: continue err = validators.validate_uuid(interface_info[key]) if err: raise n_exc.BadRequest(resource='router', msg=err) if not for_removal: if port_id_specified and subnet_id_specified: msg = _("Cannot specify both subnet-id and port-id") raise n_exc.BadRequest(resource='router', msg=msg) return port_id_specified, subnet_id_specified def _check_router_port(self, context, port_id, device_id): """Check that a port is available for an attachment to a router :param context: The context of the request. :param port_id: The port to be attached. :param device_id: This method will check that device_id corresponds to the device_id of the port. It raises PortInUse exception if it doesn't. :returns: The port description returned by the core plugin. :raises: PortInUse if the device_id is not the same as the port's one. :raises: BadRequest if the port has no fixed IP. """ port = self._core_plugin.get_port(context, port_id) if port['device_id'] != device_id: raise n_exc.PortInUse(net_id=port['network_id'], port_id=port['id'], device_id=port['device_id']) if not port['fixed_ips']: msg = _('Router port must have at least one fixed IP') raise n_exc.BadRequest(resource='router', msg=msg) return port def _validate_port_in_range_or_admin(self, context, subnets, port): if context.is_admin: return subnets_by_id = {} for s in subnets: addr_set = netaddr.IPSet() for range in s['allocation_pools']: addr_set.add(netaddr.IPRange(netaddr.IPAddress(range['start']), netaddr.IPAddress(range['end']))) subnets_by_id[s['id']] = (addr_set, s['project_id'],) for subnet_id, ip in [(fix_ip['subnet_id'], fix_ip['ip_address'],) for fix_ip in port['fixed_ips']]: if (ip not in subnets_by_id[subnet_id][0] and context.project_id != subnets_by_id[subnet_id][1]): msg = (_('Cannot add interface to router because specified ' 'port %(port)s has an IP address out of the ' 'allocation pool of subnet %(subnet)s, which is not ' 'owned by the project making the request') % {'port': port['id'], 'subnet': subnet_id}) raise n_exc.BadRequest(resource='router', msg=msg) def _validate_router_port_info(self, context, router, port_id): with db_api.autonested_transaction(context.session): # check again within transaction to mitigate race port = self._check_router_port(context, port_id, router.id) # Only allow one router port with IPv6 subnets per network id if self._port_has_ipv6_address(port): for existing_port in (rp.port for rp in router.attached_ports): if (existing_port['network_id'] == port['network_id'] and self._port_has_ipv6_address(existing_port)): msg = _("Cannot have multiple router ports with the " "same network id if both contain IPv6 " "subnets. Existing port %(p)s has IPv6 " "subnet(s) and network id %(nid)s") raise n_exc.BadRequest(resource='router', msg=msg % { 'p': existing_port['id'], 'nid': existing_port['network_id']}) fixed_ips = [ip for ip in port['fixed_ips']] subnets = [] for fixed_ip in fixed_ips: subnet = self._core_plugin.get_subnet(context, fixed_ip['subnet_id']) subnets.append(subnet) if subnets: self._check_for_dup_router_subnets(context, router, port['network_id'], subnets) # Keep the restriction against multiple IPv4 subnets if len([s for s in subnets if s['ip_version'] == 4]) > 1: msg = _("Cannot have multiple " "IPv4 subnets on router port") raise n_exc.BadRequest(resource='router', msg=msg) self._validate_port_in_range_or_admin(context, subnets, port) return port, subnets def _notify_attaching_interface(self, context, router_db, port, interface_info): """Notify third party code that an interface is being attached to a router :param context: The context of the request. :param router_db: The router db object having an interface attached. :param port: The port object being attached to the router. :param interface_info: The requested interface attachment info passed to add_router_interface. :raises: RouterInterfaceAttachmentConflict if a third party code prevent the port to be attach to the router. """ try: registry.notify(resources.ROUTER_INTERFACE, events.BEFORE_CREATE, self, context=context, router_db=router_db, port=port, interface_info=interface_info, router_id=router_db.id, network_id=port['network_id']) except exceptions.CallbackFailure as e: # raise the underlying exception reason = (_('cannot perform router interface attachment ' 'due to %(reason)s') % {'reason': e}) raise l3_exc.RouterInterfaceAttachmentConflict(reason=reason) def _add_interface_by_port(self, context, router, port_id, owner): # Update owner before actual process in order to avoid the # case where a port might get attached to a router without the # owner successfully updating due to an unavailable backend. self._core_plugin.update_port( context, port_id, {'port': {'device_id': router.id, 'device_owner': owner}}) return self._validate_router_port_info(context, router, port_id) def _port_has_ipv6_address(self, port): for fixed_ip in port['fixed_ips']: if netaddr.IPNetwork(fixed_ip['ip_address']).version == 6: return True def _find_ipv6_router_port_by_network(self, context, router, net_id): router_dev_owner = self._get_device_owner(context, router) for port in router.attached_ports: p = port['port'] if p['device_owner'] != router_dev_owner: # we don't want any special purpose internal ports continue if p['network_id'] == net_id and self._port_has_ipv6_address(p): return port def _add_interface_by_subnet(self, context, router, subnet_id, owner): subnet = self._core_plugin.get_subnet(context, subnet_id) if not subnet['gateway_ip']: msg = _('Subnet for router interface must have a gateway IP') raise n_exc.BadRequest(resource='router', msg=msg) if subnet['project_id'] != context.project_id and not context.is_admin: msg = (_('Cannot add interface to router because subnet %s is not ' 'owned by project making the request') % subnet_id) raise n_exc.BadRequest(resource='router', msg=msg) if (subnet['ip_version'] == 6 and subnet['ipv6_ra_mode'] is None and subnet['ipv6_address_mode'] is not None): msg = (_('IPv6 subnet %s configured to receive RAs from an ' 'external router cannot be added to Neutron Router.') % subnet['id']) raise n_exc.BadRequest(resource='router', msg=msg) self._check_for_dup_router_subnets(context, router, subnet['network_id'], [subnet]) fixed_ip = {'ip_address': subnet['gateway_ip'], 'subnet_id': subnet['id']} if (subnet['ip_version'] == 6 and not ipv6_utils.is_ipv6_pd_enabled(subnet)): # Add new prefix to an existing ipv6 port with the same network id # if one exists port = self._find_ipv6_router_port_by_network(context, router, subnet['network_id']) if port: fixed_ips = list(map(dict, port['port']['fixed_ips'])) fixed_ips.append(fixed_ip) return self._core_plugin.update_port(context, port['port_id'], {'port': {'fixed_ips': fixed_ips}}), [subnet], False port_data = {'tenant_id': router.tenant_id, 'network_id': subnet['network_id'], 'fixed_ips': [fixed_ip], 'admin_state_up': True, 'device_id': router.id, 'device_owner': owner, 'name': ''} return p_utils.create_port(self._core_plugin, context, {'port': port_data}), [subnet], True @staticmethod def _make_router_interface_info( router_id, tenant_id, port_id, network_id, subnet_id, subnet_ids): return { 'id': router_id, 'tenant_id': tenant_id, 'port_id': port_id, 'network_id': network_id, 'subnet_id': subnet_id, # deprecated by IPv6 multi-prefix 'subnet_ids': subnet_ids } @db_api.retry_if_session_inactive() def add_router_interface(self, context, router_id, interface_info=None): router = self._get_router(context, router_id) add_by_port, add_by_sub = self._validate_interface_info(interface_info) device_owner = self._get_device_owner(context, router_id) # This should be True unless adding an IPv6 prefix to an existing port new_router_intf = True cleanup_port = False if add_by_port: port_id = interface_info['port_id'] port = self._check_router_port(context, port_id, '') revert_value = {'device_id': '', 'device_owner': port['device_owner']} with p_utils.update_port_on_error( self._core_plugin, context, port_id, revert_value): port, subnets = self._add_interface_by_port( context, router, port_id, device_owner) # add_by_subnet is not used here, because the validation logic of # _validate_interface_info ensures that either of add_by_* is True. else: port, subnets, new_router_intf = self._add_interface_by_subnet( context, router, interface_info['subnet_id'], device_owner) cleanup_port = new_router_intf # only cleanup port we created revert_value = {'device_id': '', 'device_owner': port['device_owner']} if cleanup_port: mgr = p_utils.delete_port_on_error( self._core_plugin, context, port['id']) else: mgr = p_utils.update_port_on_error( self._core_plugin, context, port['id'], revert_value) if new_router_intf: with mgr: self._notify_attaching_interface(context, router_db=router, port=port, interface_info=interface_info) self._add_router_port( context, port['id'], router.id, device_owner) gw_ips = [] gw_network_id = None if router.gw_port: gw_network_id = router.gw_port.network_id gw_ips = [x['ip_address'] for x in router.gw_port.fixed_ips] registry.notify(resources.ROUTER_INTERFACE, events.AFTER_CREATE, self, context=context, network_id=gw_network_id, gateway_ips=gw_ips, cidrs=[x['cidr'] for x in subnets], subnets=subnets, port_id=port['id'], router_id=router_id, port=port, new_interface=new_router_intf, interface_info=interface_info) with context.session.begin(subtransactions=True): context.session.refresh(router) return self._make_router_interface_info( router.id, port['tenant_id'], port['id'], port['network_id'], subnets[-1]['id'], [subnet['id'] for subnet in subnets]) @db_api.retry_if_session_inactive() def _add_router_port(self, context, port_id, router_id, device_owner): l3_obj.RouterPort( context, port_id=port_id, router_id=router_id, port_type=device_owner ).create() # Update owner after actual process again in order to # make sure the records in routerports table and ports # table are consistent. self._core_plugin.update_port( context, port_id, {'port': {'device_id': router_id, 'device_owner': device_owner}}) def _confirm_router_interface_not_in_use(self, context, router_id, subnet_id): subnet = self._core_plugin.get_subnet(context, subnet_id) subnet_cidr = netaddr.IPNetwork(subnet['cidr']) try: kwargs = {'context': context, 'router_id': router_id, 'subnet_id': subnet_id} registry.notify( resources.ROUTER_INTERFACE, events.BEFORE_DELETE, self, **kwargs) except exceptions.CallbackFailure as e: # NOTE(armax): preserve old check's behavior if len(e.errors) == 1: raise e.errors[0].error raise l3_exc.RouterInUse(router_id=router_id, reason=e) fip_objs = l3_obj.FloatingIP.get_objects(context, router_id=router_id) for fip_obj in fip_objs: if fip_obj.fixed_ip_address in subnet_cidr: raise l3_exc.RouterInterfaceInUseByFloatingIP( router_id=router_id, subnet_id=subnet_id) def _remove_interface_by_port(self, context, router_id, port_id, subnet_id, owner): obj = l3_obj.RouterPort.get_object( context, port_id=port_id, router_id=router_id, port_type=owner ) if obj: try: port = self._core_plugin.get_port(context, obj.port_id) except n_exc.PortNotFound: raise l3_exc.RouterInterfaceNotFound( router_id=router_id, port_id=port_id) else: raise l3_exc.RouterInterfaceNotFound( router_id=router_id, port_id=port_id) port_subnet_ids = [fixed_ip['subnet_id'] for fixed_ip in port['fixed_ips']] if subnet_id and subnet_id not in port_subnet_ids: raise n_exc.SubnetMismatchForPort( port_id=port_id, subnet_id=subnet_id) subnets = [self._core_plugin.get_subnet(context, port_subnet_id) for port_subnet_id in port_subnet_ids] for port_subnet_id in port_subnet_ids: self._confirm_router_interface_not_in_use( context, router_id, port_subnet_id) self._core_plugin.delete_port(context, port['id'], l3_port_check=False) return (port, subnets) def _remove_interface_by_subnet(self, context, router_id, subnet_id, owner): self._confirm_router_interface_not_in_use( context, router_id, subnet_id) subnet = self._core_plugin.get_subnet(context, subnet_id) try: ports = port_obj.Port.get_ports_by_router( context, router_id, owner, subnet) for p in ports: try: p = self._core_plugin.get_port(context, p.id) except n_exc.PortNotFound: continue port_subnets = [fip['subnet_id'] for fip in p['fixed_ips']] if subnet_id in port_subnets and len(port_subnets) > 1: # multiple prefix port - delete prefix from port fixed_ips = [dict(fip) for fip in p['fixed_ips'] if fip['subnet_id'] != subnet_id] self._core_plugin.update_port(context, p['id'], {'port': {'fixed_ips': fixed_ips}}) return (p, [subnet]) elif subnet_id in port_subnets: # only one subnet on port - delete the port self._core_plugin.delete_port(context, p['id'], l3_port_check=False) return (p, [subnet]) except exc.NoResultFound: pass raise l3_exc.RouterInterfaceNotFoundForSubnet( router_id=router_id, subnet_id=subnet_id) @db_api.retry_if_session_inactive() def remove_router_interface(self, context, router_id, interface_info): remove_by_port, remove_by_subnet = ( self._validate_interface_info(interface_info, for_removal=True) ) port_id = interface_info.get('port_id') subnet_id = interface_info.get('subnet_id') device_owner = self._get_device_owner(context, router_id) if remove_by_port: port, subnets = self._remove_interface_by_port(context, router_id, port_id, subnet_id, device_owner) # remove_by_subnet is not used here, because the validation logic of # _validate_interface_info ensures that at least one of remote_by_* # is True. else: port, subnets = self._remove_interface_by_subnet( context, router_id, subnet_id, device_owner) gw_network_id = None gw_ips = [] router = self._get_router(context, router_id) if router.gw_port: gw_network_id = router.gw_port.network_id gw_ips = [x['ip_address'] for x in router.gw_port.fixed_ips] registry.notify(resources.ROUTER_INTERFACE, events.AFTER_DELETE, self, context=context, cidrs=[x['cidr'] for x in subnets], network_id=gw_network_id, gateway_ips=gw_ips, port=port, router_id=router_id, interface_info=interface_info) with context.session.begin(subtransactions=True): context.session.refresh(router) return self._make_router_interface_info(router_id, port['tenant_id'], port['id'], port['network_id'], subnets[0]['id'], [subnet['id'] for subnet in subnets]) def _get_floatingip(self, context, id): floatingip = l3_obj.FloatingIP.get_object(context, id=id) if not floatingip: raise l3_exc.FloatingIPNotFound(floatingip_id=id) return floatingip def _make_floatingip_dict(self, floatingip, fields=None, process_extensions=True): floating_ip_address = (str(floatingip.floating_ip_address) if floatingip.floating_ip_address else None) fixed_ip_address = (str(floatingip.fixed_ip_address) if floatingip.fixed_ip_address else None) res = {'id': floatingip.id, 'tenant_id': floatingip.project_id, 'floating_ip_address': floating_ip_address, 'floating_network_id': floatingip.floating_network_id, 'router_id': floatingip.router_id, 'port_id': floatingip.fixed_port_id, 'fixed_ip_address': fixed_ip_address, 'status': floatingip.status} # NOTE(mlavalle): The following assumes this mixin is used in a # class inheriting from CommonDbMixin, which is true for all existing # plugins. # TODO(lujinluo): Change floatingip.db_obj to floatingip once all # codes are migrated to use Floating IP OVO object. if process_extensions: resource_extend.apply_funcs( l3_apidef.FLOATINGIPS, res, floatingip.db_obj) return db_utils.resource_fields(res, fields) def _get_router_for_floatingip(self, context, internal_port, internal_subnet_id, external_network_id): subnet = self._core_plugin.get_subnet(context, internal_subnet_id) return self.get_router_for_floatingip(context, internal_port, subnet, external_network_id) # NOTE(yamamoto): This method is an override point for plugins # inheriting this class. Do not optimize this out. def get_router_for_floatingip(self, context, internal_port, internal_subnet, external_network_id): """Find a router to handle the floating-ip association. :param internal_port: The port for the fixed-ip. :param internal_subnet: The subnet for the fixed-ip. :param external_network_id: The external network for floating-ip. :raises: ExternalGatewayForFloatingIPNotFound if no suitable router is found. """ # Find routers(with router_id and interface address) that # connect given internal subnet and the external network. # Among them, if the router's interface address matches # with subnet's gateway-ip, return that router. # Otherwise return the first router. RouterPort = l3_models.RouterPort gw_port = orm.aliased(models_v2.Port, name="gw_port") # TODO(lujinluo): Need IPAllocation and Port object routerport_qry = context.session.query( RouterPort.router_id, models_v2.IPAllocation.ip_address).join( models_v2.Port, models_v2.IPAllocation).filter( models_v2.Port.network_id == internal_port['network_id'], RouterPort.port_type.in_(constants.ROUTER_INTERFACE_OWNERS), models_v2.IPAllocation.subnet_id == internal_subnet['id'] ).join(gw_port, gw_port.device_id == RouterPort.router_id).filter( gw_port.network_id == external_network_id, gw_port.device_owner == DEVICE_OWNER_ROUTER_GW ).distinct() first_router_id = None for router_id, interface_ip in routerport_qry: if interface_ip == internal_subnet['gateway_ip']: return router_id if not first_router_id: first_router_id = router_id if first_router_id: return first_router_id raise l3_exc.ExternalGatewayForFloatingIPNotFound( subnet_id=internal_subnet['id'], external_network_id=external_network_id, port_id=internal_port['id']) def _port_ipv4_fixed_ips(self, port): return [ip for ip in port['fixed_ips'] if netaddr.IPAddress(ip['ip_address']).version == 4] def _internal_fip_assoc_data(self, context, fip, tenant_id): """Retrieve internal port data for floating IP. Retrieve information concerning the internal port where the floating IP should be associated to. """ internal_port = self._core_plugin.get_port(context, fip['port_id']) if internal_port['tenant_id'] != tenant_id and not context.is_admin: port_id = fip['port_id'] msg = (_('Cannot process floating IP association with ' 'Port %s, since that port is owned by a ' 'different tenant') % port_id) raise n_exc.BadRequest(resource='floatingip', msg=msg) internal_subnet_id = None if not utils.is_fip_serviced(internal_port.get('device_owner')): msg = _('Port %(id)s is unable to be assigned a floating IP') raise n_exc.BadRequest(resource='floatingip', msg=msg) if fip.get('fixed_ip_address'): internal_ip_address = fip['fixed_ip_address'] if netaddr.IPAddress(internal_ip_address).version != 4: msg = (_('Cannot process floating IP association with %s, ' 'since that is not an IPv4 address') % internal_ip_address) raise n_exc.BadRequest(resource='floatingip', msg=msg) for ip in internal_port['fixed_ips']: if ip['ip_address'] == internal_ip_address: internal_subnet_id = ip['subnet_id'] if not internal_subnet_id: msg = (_('Port %(id)s does not have fixed ip %(address)s') % {'id': internal_port['id'], 'address': internal_ip_address}) raise n_exc.BadRequest(resource='floatingip', msg=msg) else: ipv4_fixed_ips = self._port_ipv4_fixed_ips(internal_port) if not ipv4_fixed_ips: msg = (_('Cannot add floating IP to port %s that has ' 'no fixed IPv4 addresses') % internal_port['id']) raise n_exc.BadRequest(resource='floatingip', msg=msg) if len(ipv4_fixed_ips) > 1: msg = (_('Port %s has multiple fixed IPv4 addresses. Must ' 'provide a specific IPv4 address when assigning a ' 'floating IP') % internal_port['id']) raise n_exc.BadRequest(resource='floatingip', msg=msg) internal_ip_address = ipv4_fixed_ips[0]['ip_address'] internal_subnet_id = ipv4_fixed_ips[0]['subnet_id'] return internal_port, internal_subnet_id, internal_ip_address def _get_assoc_data(self, context, fip, floatingip_obj): """Determine/extract data associated with the internal port. When a floating IP is associated with an internal port, we need to extract/determine some data associated with the internal port, including the internal_ip_address, and router_id. The confirmation of the internal port whether owned by the tenant who owns the floating IP will be confirmed by _get_router_for_floatingip. """ (internal_port, internal_subnet_id, internal_ip_address) = self._internal_fip_assoc_data( context, fip, floatingip_obj.project_id) router_id = self._get_router_for_floatingip( context, internal_port, internal_subnet_id, floatingip_obj.floating_network_id) if self.is_router_distributed(context, router_id): if not can_port_be_bound_to_virtual_bridge(internal_port): msg = _('Port VNIC type is not valid to associate a FIP in ' 'DVR mode') raise n_exc.BadRequest(resource='floatingip', msg=msg) return (fip['port_id'], internal_ip_address, router_id) def _check_and_get_fip_assoc(self, context, fip, floatingip_obj): port_id = internal_ip_address = router_id = None if fip.get('fixed_ip_address') and not fip.get('port_id'): msg = _("fixed_ip_address cannot be specified without a port_id") raise n_exc.BadRequest(resource='floatingip', msg=msg) if fip.get('port_id'): port_id, internal_ip_address, router_id = self._get_assoc_data( context, fip, floatingip_obj) if port_id == floatingip_obj.fixed_port_id: # Floating IP association is not changed. return port_id, internal_ip_address, router_id fip_exists = l3_obj.FloatingIP.objects_exist( context, fixed_port_id=fip['port_id'], floating_network_id=floatingip_obj.floating_network_id, fixed_ip_address=netaddr.IPAddress(internal_ip_address)) if fip_exists: floating_ip_address = (str(floatingip_obj.floating_ip_address) if floatingip_obj.floating_ip_address else None) raise l3_exc.FloatingIPPortAlreadyAssociated( port_id=fip['port_id'], fip_id=floatingip_obj.id, floating_ip_address=floating_ip_address, fixed_ip=internal_ip_address, net_id=floatingip_obj.floating_network_id) if fip and 'port_id' not in fip and floatingip_obj.fixed_port_id: # NOTE(liuyulong): without the fix of bug #1610045 here could # also let floating IP can be dissociated with an empty # updating dict. fip['port_id'] = floatingip_obj.fixed_port_id port_id, internal_ip_address, router_id = self._get_assoc_data( context, fip, floatingip_obj) # After all upper conditions, if updating API dict is submitted with # {'port_id': null}, then the floating IP cloud also be dissociated. return port_id, internal_ip_address, router_id def _update_fip_assoc(self, context, fip, floatingip_obj, external_port): previous_router_id = floatingip_obj.router_id port_id, internal_ip_address, router_id = ( self._check_and_get_fip_assoc(context, fip, floatingip_obj)) floatingip_obj.fixed_ip_address = ( netaddr.IPAddress(internal_ip_address) if internal_ip_address else None) floatingip_obj.fixed_port_id = port_id floatingip_obj.router_id = router_id floatingip_obj.last_known_router_id = previous_router_id if 'description' in fip: floatingip_obj.description = fip['description'] floating_ip_address = (str(floatingip_obj.floating_ip_address) if floatingip_obj.floating_ip_address else None) return {'fixed_ip_address': internal_ip_address, 'fixed_port_id': port_id, 'router_id': router_id, 'last_known_router_id': previous_router_id, 'floating_ip_address': floating_ip_address, 'floating_network_id': floatingip_obj.floating_network_id, 'floating_ip_id': floatingip_obj.id, 'context': context} def _is_ipv4_network(self, context, net_id): net = self._core_plugin._get_network(context, net_id) return any(s.ip_version == 4 for s in net.subnets) def _create_floatingip(self, context, floatingip, initial_status=constants.FLOATINGIP_STATUS_ACTIVE): fip = floatingip['floatingip'] fip_id = uuidutils.generate_uuid() f_net_id = fip['floating_network_id'] if not self._core_plugin._network_is_external(context, f_net_id): msg = _("Network %s is not a valid external network") % f_net_id raise n_exc.BadRequest(resource='floatingip', msg=msg) if not self._is_ipv4_network(context, f_net_id): msg = _("Network %s does not contain any IPv4 subnet") % f_net_id raise n_exc.BadRequest(resource='floatingip', msg=msg) # This external port is never exposed to the tenant. # it is used purely for internal system and admin use when # managing floating IPs. port = {'tenant_id': '', # tenant intentionally not set 'network_id': f_net_id, 'admin_state_up': True, 'device_id': 'PENDING', 'device_owner': DEVICE_OWNER_FLOATINGIP, 'status': constants.PORT_STATUS_NOTAPPLICABLE, 'name': ''} # Both subnet_id and floating_ip_address are accepted, if # floating_ip_address is not in the subnet, # InvalidIpForSubnet exception will be raised. fixed_ip = {} if validators.is_attr_set(fip.get('subnet_id')): fixed_ip['subnet_id'] = fip['subnet_id'] if validators.is_attr_set(fip.get('floating_ip_address')): fixed_ip['ip_address'] = fip['floating_ip_address'] if fixed_ip: port['fixed_ips'] = [fixed_ip] # 'status' in port dict could not be updated by default, use # check_allow_post to stop the verification of system external_port = p_utils.create_port(self._core_plugin, context.elevated(), {'port': port}, check_allow_post=False) with p_utils.delete_port_on_error(self._core_plugin, context.elevated(), external_port['id']),\ context.session.begin(subtransactions=True): # Ensure IPv4 addresses are allocated on external port external_ipv4_ips = self._port_ipv4_fixed_ips(external_port) if not external_ipv4_ips: raise n_exc.ExternalIpAddressExhausted(net_id=f_net_id) floating_fixed_ip = external_ipv4_ips[0] floating_ip_address = floating_fixed_ip['ip_address'] floatingip_obj = l3_obj.FloatingIP( context, id=fip_id, project_id=fip['tenant_id'], status=initial_status, floating_network_id=fip['floating_network_id'], floating_ip_address=floating_ip_address, floating_port_id=external_port['id'], description=fip.get('description')) # Update association with internal port # and define external IP address assoc_result = self._update_fip_assoc( context, fip, floatingip_obj, external_port) floatingip_obj.create() floatingip_dict = self._make_floatingip_dict( floatingip_obj, process_extensions=False) if self._is_dns_integration_supported: dns_data = self._process_dns_floatingip_create_precommit( context, floatingip_dict, fip) if self._is_fip_qos_supported: self._process_extra_fip_qos_create(context, fip_id, fip) floatingip_obj = l3_obj.FloatingIP.get_object( context, id=floatingip_obj.id) floatingip_db = floatingip_obj.db_obj registry.notify(resources.FLOATING_IP, events.PRECOMMIT_CREATE, self, context=context, floatingip=fip, floatingip_id=fip_id, floatingip_db=floatingip_db) self._core_plugin.update_port(context.elevated(), external_port['id'], {'port': {'device_id': fip_id}}) registry.notify(resources.FLOATING_IP, events.AFTER_UPDATE, self._update_fip_assoc, **assoc_result) if self._is_dns_integration_supported: self._process_dns_floatingip_create_postcommit(context, floatingip_dict, dns_data) # TODO(lujinluo): Change floatingip_db to floatingip_obj once all # codes are migrated to use Floating IP OVO object. resource_extend.apply_funcs(l3_apidef.FLOATINGIPS, floatingip_dict, floatingip_db) return floatingip_dict @db_api.retry_if_session_inactive() def create_floatingip(self, context, floatingip, initial_status=constants.FLOATINGIP_STATUS_ACTIVE): return self._create_floatingip(context, floatingip, initial_status) def _update_floatingip(self, context, id, floatingip): fip = floatingip['floatingip'] with context.session.begin(subtransactions=True): floatingip_obj = self._get_floatingip(context, id) old_floatingip = self._make_floatingip_dict(floatingip_obj) fip_port_id = floatingip_obj.floating_port_id assoc_result = self._update_fip_assoc( context, fip, floatingip_obj, self._core_plugin.get_port(context.elevated(), fip_port_id)) floatingip_obj.update() floatingip_dict = self._make_floatingip_dict(floatingip_obj) if self._is_dns_integration_supported: dns_data = self._process_dns_floatingip_update_precommit( context, floatingip_dict) if self._is_fip_qos_supported: self._process_extra_fip_qos_update(context, floatingip_obj, fip, old_floatingip) floatingip_obj = l3_obj.FloatingIP.get_object( context, id=floatingip_obj.id) floatingip_db = floatingip_obj.db_obj registry.notify(resources.FLOATING_IP, events.PRECOMMIT_UPDATE, self, floatingip=floatingip, floatingip_db=floatingip_db, old_floatingip=old_floatingip, **assoc_result) registry.notify(resources.FLOATING_IP, events.AFTER_UPDATE, self._update_fip_assoc, **assoc_result) if self._is_dns_integration_supported: self._process_dns_floatingip_update_postcommit(context, floatingip_dict, dns_data) # TODO(lujinluo): Change floatingip_db to floatingip_obj once all # codes are migrated to use Floating IP OVO object. resource_extend.apply_funcs(l3_apidef.FLOATINGIPS, floatingip_dict, floatingip_db) return old_floatingip, floatingip_dict def _floatingips_to_router_ids(self, floatingips): return list(set([floatingip['router_id'] for floatingip in floatingips if floatingip['router_id']])) @db_api.retry_if_session_inactive() def update_floatingip(self, context, id, floatingip): _old_floatingip, floatingip = self._update_floatingip( context, id, floatingip) return floatingip @db_api.retry_if_session_inactive() def update_floatingip_status(self, context, floatingip_id, status): """Update operational status for floating IP in neutron DB.""" return l3_obj.FloatingIP.update_object( context, {'status': status}, id=floatingip_id) @registry.receives(resources.PORT, [events.PRECOMMIT_DELETE]) def _precommit_delete_port_callback( self, resource, event, trigger, **kwargs): if (kwargs['port']['device_owner'] == constants.DEVICE_OWNER_FLOATINGIP): registry.notify(resources.FLOATING_IP, events.PRECOMMIT_DELETE, self, **kwargs) def _delete_floatingip(self, context, id): floatingip = self._get_floatingip(context, id) floatingip_dict = self._make_floatingip_dict(floatingip) if self._is_dns_integration_supported: self._process_dns_floatingip_delete(context, floatingip_dict) # Foreign key cascade will take care of the removal of the # floating IP record once the port is deleted. We can't start # a transaction first to remove it ourselves because the delete_port # method will yield in its post-commit activities. self._core_plugin.delete_port(context.elevated(), floatingip.floating_port_id, l3_port_check=False) registry.notify(resources.FLOATING_IP, events.AFTER_DELETE, self, **floatingip_dict) return floatingip_dict @db_api.retry_if_session_inactive() def delete_floatingip(self, context, id): self._delete_floatingip(context, id) @db_api.retry_if_session_inactive() def get_floatingip(self, context, id, fields=None): floatingip = self._get_floatingip(context, id) return self._make_floatingip_dict(floatingip, fields) @db_api.retry_if_session_inactive() def get_floatingips(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pager = base_obj.Pager(sorts, limit, page_reverse, marker) filters = filters or {} for key, val in API_TO_DB_COLUMN_MAP.items(): if key in filters: filters[val] = filters.pop(key) floatingip_objs = l3_obj.FloatingIP.get_objects( context, _pager=pager, validate_filters=False, **filters) floatingip_dicts = [ self._make_floatingip_dict(floatingip_obj, fields) for floatingip_obj in floatingip_objs ] return floatingip_dicts @db_api.retry_if_session_inactive() def delete_disassociated_floatingips(self, context, network_id): fip_objs = l3_obj.FloatingIP.get_objects( context, floating_network_id=network_id, router_id=None, fixed_port_id=None) for fip in fip_objs: self.delete_floatingip(context, fip.id) @db_api.retry_if_session_inactive() def get_floatingips_count(self, context, filters=None): filters = filters or {} return l3_obj.FloatingIP.count(context, **filters) def _router_exists(self, context, router_id): try: self.get_router(context.elevated(), router_id) return True except l3_exc.RouterNotFound: return False def prevent_l3_port_deletion(self, context, port_id): """Checks to make sure a port is allowed to be deleted. Raises an exception if this is not the case. This should be called by any plugin when the API requests the deletion of a port, since some ports for L3 are not intended to be deleted directly via a DELETE to /ports, but rather via other API calls that perform the proper deletion checks. """ try: port = self._core_plugin.get_port(context, port_id) except n_exc.PortNotFound: # non-existent ports don't need to be protected from deletion return if port['device_owner'] not in self.router_device_owners: return # Raise port in use only if the port has IP addresses # Otherwise it's a stale port that can be removed fixed_ips = port['fixed_ips'] if not fixed_ips: LOG.debug("Port %(port_id)s has owner %(port_owner)s, but " "no IP address, so it can be deleted", {'port_id': port['id'], 'port_owner': port['device_owner']}) return # NOTE(kevinbenton): we also check to make sure that the # router still exists. It's possible for HA router interfaces # to remain after the router is deleted if they encounter an # error during deletion. # Elevated context in case router is owned by another tenant if port['device_owner'] == DEVICE_OWNER_FLOATINGIP: if not l3_obj.FloatingIP.objects_exist( context, id=port['device_id']): LOG.debug("Floating IP %(f_id)s corresponding to port " "%(port_id)s no longer exists, allowing deletion.", {'f_id': port['device_id'], 'port_id': port['id']}) return elif not self._router_exists(context, port['device_id']): LOG.debug("Router %(router_id)s corresponding to port " "%(port_id)s no longer exists, allowing deletion.", {'router_id': port['device_id'], 'port_id': port['id']}) return reason = _('has device owner %s') % port['device_owner'] raise n_exc.ServicePortInUse(port_id=port['id'], reason=reason) @db_api.retry_if_session_inactive() def disassociate_floatingips(self, context, port_id, do_notify=True): """Disassociate all floating IPs linked to specific port. @param port_id: ID of the port to disassociate floating IPs. @param do_notify: whether we should notify routers right away. This parameter is ignored. @return: set of router-ids that require notification updates """ with context.session.begin(subtransactions=True): floating_ip_objs = l3_obj.FloatingIP.get_objects( context, fixed_port_id=port_id) router_ids = {fip.router_id for fip in floating_ip_objs} old_fips = {fip.id: fip.to_dict() for fip in floating_ip_objs} values = {'fixed_port_id': None, 'fixed_ip_address': None, 'router_id': None} l3_obj.FloatingIP.update_objects( context, values, fixed_port_id=port_id) for fip in floating_ip_objs: registry.notify(resources.FLOATING_IP, events.PRECOMMIT_UPDATE, self, floatingip={l3_apidef.FLOATINGIP: values}, floatingip_db=fip, old_floatingip=old_fips[fip.id], router_ids=router_ids) for fip in floating_ip_objs: assoc_result = { 'fixed_ip_address': None, 'fixed_port_id': None, 'router_id': None, 'floating_ip_address': fip.floating_ip_address, 'floating_network_id': fip.floating_network_id, 'floating_ip_id': fip.id, 'context': context, 'router_ids': router_ids, } registry.notify(resources.FLOATING_IP, events.AFTER_UPDATE, self, **assoc_result) return router_ids def _get_floatingips_by_port_id(self, context, port_id): """Helper function to retrieve the fips associated with a port_id.""" return l3_obj.FloatingIP.get_objects(context, fixed_port_id=port_id) def _build_routers_list(self, context, routers, gw_ports): """Subclasses can override this to add extra gateway info""" return routers def _make_router_dict_with_gw_port(self, router, fields): result = self._make_router_dict(router, fields) if router.get('gw_port'): result['gw_port'] = self._core_plugin._make_port_dict( router['gw_port']) return result def _get_sync_routers(self, context, router_ids=None, active=None): """Query routers and their gw ports for l3 agent. Query routers with the router_ids. The gateway ports, if any, will be queried too. l3 agent has an option to deal with only one router id. In addition, when we need to notify the agent the data about only one router (when modification of router, its interfaces, gw_port and floatingips), we will have router_ids. @param router_ids: the list of router ids which we want to query. if it is None, all of routers will be queried. @return: a list of dicted routers with dicted gw_port populated if any """ filters = {'id': router_ids} if router_ids else {} if active is not None: filters['admin_state_up'] = [active] router_dicts = model_query.get_collection( context, l3_models.Router, self._make_router_dict_with_gw_port, filters=filters) if not router_dicts: return [] gw_ports = dict((r['gw_port']['id'], r['gw_port']) for r in router_dicts if r.get('gw_port')) return self._build_routers_list(context, router_dicts, gw_ports) def _make_floatingip_dict_with_scope(self, floatingip_obj, scope_id): d = self._make_floatingip_dict(floatingip_obj) d['fixed_ip_address_scope'] = scope_id return d def _get_sync_floating_ips(self, context, router_ids): """Query floating_ips that relate to list of router_ids with scope. This is different than the regular get_floatingips in that it finds the address scope of the fixed IP. The router needs to know this to distinguish it from other scopes. There are a few redirections to go through to discover the address scope from the floating ip. """ if not router_ids: return [] return [ self._make_floatingip_dict_with_scope(*scoped_fip) for scoped_fip in l3_obj.FloatingIP.get_scoped_floating_ips( context, router_ids) ] def _get_sync_interfaces(self, context, router_ids, device_owners=None): """Query router interfaces that relate to list of router_ids.""" device_owners = device_owners or [DEVICE_OWNER_ROUTER_INTF, DEVICE_OWNER_HA_REPLICATED_INT] if not router_ids: return [] # TODO(lujinluo): Need Port as synthetic field objs = l3_obj.RouterPort.get_objects( context, router_id=router_ids, port_type=list(device_owners)) interfaces = [self._core_plugin._make_port_dict(rp.db_obj.port) for rp in objs] return interfaces @staticmethod def _each_port_having_fixed_ips(ports): for port in ports or []: fixed_ips = port.get('fixed_ips', []) if not fixed_ips: # Skip ports without IPs, which can occur if a subnet # attached to a router is deleted LOG.info("Skipping port %s as no IP is configure on " "it", port['id']) continue yield port def _get_subnets_by_network_list(self, context, network_ids): if not network_ids: return {} query = context.session.query(models_v2.Subnet, models_v2.SubnetPool.address_scope_id) query = query.outerjoin( models_v2.SubnetPool, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id) query = query.filter(models_v2.Subnet.network_id.in_(network_ids)) fields = ['id', 'cidr', 'gateway_ip', 'dns_nameservers', 'network_id', 'ipv6_ra_mode', 'subnetpool_id'] def make_subnet_dict_with_scope(row): subnet_db, address_scope_id = row subnet = self._core_plugin._make_subnet_dict( subnet_db, fields, context=context) subnet['address_scope_id'] = address_scope_id return subnet subnets_by_network = dict((id, []) for id in network_ids) for subnet in (make_subnet_dict_with_scope(row) for row in query): subnets_by_network[subnet['network_id']].append(subnet) return subnets_by_network def _get_mtus_by_network_list(self, context, network_ids): if not network_ids: return {} filters = {'id': network_ids} fields = ['id', 'mtu'] networks = self._core_plugin.get_networks(context, filters=filters, fields=fields) mtus_by_network = dict((network['id'], network.get('mtu', 0)) for network in networks) return mtus_by_network def _populate_mtu_and_subnets_for_ports(self, context, ports): """Populate ports with subnets. These ports already have fixed_ips populated. """ network_ids = [p['network_id'] for p in self._each_port_having_fixed_ips(ports)] mtus_by_network = self._get_mtus_by_network_list(context, network_ids) subnets_by_network = self._get_subnets_by_network_list( context, network_ids) for port in self._each_port_having_fixed_ips(ports): port['subnets'] = [] port['extra_subnets'] = [] port['address_scopes'] = {constants.IP_VERSION_4: None, constants.IP_VERSION_6: None} scopes = {} for subnet in subnets_by_network[port['network_id']]: scope = subnet['address_scope_id'] cidr = netaddr.IPNetwork(subnet['cidr']) scopes[cidr.version] = scope # If this subnet is used by the port (has a matching entry # in the port's fixed_ips), then add this subnet to the # port's subnets list, and populate the fixed_ips entry # entry with the subnet's prefix length. subnet_info = {'id': subnet['id'], 'cidr': subnet['cidr'], 'gateway_ip': subnet['gateway_ip'], 'dns_nameservers': subnet['dns_nameservers'], 'ipv6_ra_mode': subnet['ipv6_ra_mode'], 'subnetpool_id': subnet['subnetpool_id']} for fixed_ip in port['fixed_ips']: if fixed_ip['subnet_id'] == subnet['id']: port['subnets'].append(subnet_info) prefixlen = cidr.prefixlen fixed_ip['prefixlen'] = prefixlen break else: # This subnet is not used by the port. port['extra_subnets'].append(subnet_info) port['address_scopes'].update(scopes) port['mtu'] = mtus_by_network.get(port['network_id'], 0) def _process_floating_ips(self, context, routers_dict, floating_ips): for floating_ip in floating_ips: router = routers_dict.get(floating_ip['router_id']) if router: router_floatingips = router.get(constants.FLOATINGIP_KEY, []) router_floatingips.append(floating_ip) router[constants.FLOATINGIP_KEY] = router_floatingips def _process_interfaces(self, routers_dict, interfaces): for interface in interfaces: router = routers_dict.get(interface['device_id']) if router: router_interfaces = router.get(constants.INTERFACE_KEY, []) router_interfaces.append(interface) router[constants.INTERFACE_KEY] = router_interfaces def _get_router_info_list(self, context, router_ids=None, active=None, device_owners=None): """Query routers and their related floating_ips, interfaces.""" with context.session.begin(subtransactions=True): routers = self._get_sync_routers(context, router_ids=router_ids, active=active) router_ids = [router['id'] for router in routers] interfaces = self._get_sync_interfaces( context, router_ids, device_owners) floating_ips = self._get_sync_floating_ips(context, router_ids) return (routers, interfaces, floating_ips) def get_sync_data(self, context, router_ids=None, active=None): routers, interfaces, floating_ips = self._get_router_info_list( context, router_ids=router_ids, active=active) ports_to_populate = [router['gw_port'] for router in routers if router.get('gw_port')] + interfaces self._populate_mtu_and_subnets_for_ports(context, ports_to_populate) routers_dict = dict((router['id'], router) for router in routers) self._process_floating_ips(context, routers_dict, floating_ips) self._process_interfaces(routers_dict, interfaces) return list(routers_dict.values()) def is_router_distributed(self, context, router_id): """Returns if a router is distributed or not If DVR extension is not enabled, no router will be distributed. This function is overridden in L3_NAT_with_dvr_db_mixin in case the DVR extension is loaded. """ return False @registry.has_registry_receivers class L3RpcNotifierMixin(object): """Mixin class to add rpc notifier attribute to db_base_plugin_v2.""" @staticmethod @registry.receives(resources.PORT, [events.AFTER_DELETE]) def _notify_routers_callback(resource, event, trigger, **kwargs): context = kwargs['context'] router_ids = kwargs['router_ids'] l3plugin = directory.get_plugin(plugin_constants.L3) if l3plugin: l3plugin.notify_routers_updated(context, router_ids) else: LOG.debug('%s not configured', plugin_constants.L3) @staticmethod @registry.receives(resources.SUBNET, [events.AFTER_UPDATE]) def _notify_subnet_gateway_ip_update(resource, event, trigger, **kwargs): l3plugin = directory.get_plugin(plugin_constants.L3) if not l3plugin: return context = kwargs['context'] orig = kwargs['original_subnet'] updated = kwargs['subnet'] if orig['gateway_ip'] == updated['gateway_ip']: return network_id = updated['network_id'] subnet_id = updated['id'] query = context.session.query(models_v2.Port.device_id).filter_by( network_id=network_id, device_owner=DEVICE_OWNER_ROUTER_GW) query = query.join(models_v2.Port.fixed_ips).filter( models_v2.IPAllocation.subnet_id == subnet_id) router_ids = set(port.device_id for port in query) for router_id in router_ids: l3plugin.notify_router_updated(context, router_id) @staticmethod @registry.receives(resources.SUBNETPOOL_ADDRESS_SCOPE, [events.AFTER_UPDATE]) def _notify_subnetpool_address_scope_update(resource, event, trigger, **kwargs): context = kwargs['context'] subnetpool_id = kwargs['subnetpool_id'] router_ids = l3_obj.RouterPort.get_router_ids_by_subnetpool( context, subnetpool_id) l3plugin = directory.get_plugin(plugin_constants.L3) if l3plugin: l3plugin.notify_routers_updated(context, router_ids) else: LOG.debug('%s not configured', plugin_constants.L3) @property def l3_rpc_notifier(self): if not hasattr(self, '_l3_rpc_notifier'): self._l3_rpc_notifier = l3_rpc_agent_api.L3AgentNotifyAPI() return self._l3_rpc_notifier @l3_rpc_notifier.setter def l3_rpc_notifier(self, value): self._l3_rpc_notifier = value def notify_router_updated(self, context, router_id, operation=None): if router_id: self.l3_rpc_notifier.routers_updated( context, [router_id], operation) def notify_routers_updated(self, context, router_ids, operation=None, data=None): if router_ids: self.l3_rpc_notifier.routers_updated( context, router_ids, operation, data) def notify_router_deleted(self, context, router_id): self.l3_rpc_notifier.router_deleted(context, router_id) class L3_NAT_db_mixin(L3_NAT_dbonly_mixin, L3RpcNotifierMixin): """Mixin class to add rpc notifier methods to db_base_plugin_v2.""" def create_router(self, context, router): router_dict = super(L3_NAT_db_mixin, self).create_router(context, router) if router_dict.get('external_gateway_info'): self.notify_router_updated(context, router_dict['id'], None) return router_dict def update_router(self, context, id, router): router_dict = super(L3_NAT_db_mixin, self).update_router(context, id, router) self.notify_router_updated(context, router_dict['id'], None) return router_dict def delete_router(self, context, id): super(L3_NAT_db_mixin, self).delete_router(context, id) self.notify_router_deleted(context, id) def notify_router_interface_action( self, context, router_interface_info, action): l3_method = '%s_router_interface' % action super(L3_NAT_db_mixin, self).notify_routers_updated( context, [router_interface_info['id']], l3_method, {'subnet_id': router_interface_info['subnet_id']}) mapping = {'add': 'create', 'remove': 'delete'} notifier = n_rpc.get_notifier('network') router_event = 'router.interface.%s' % mapping[action] notifier.info(context, router_event, {'router_interface': router_interface_info}) def add_router_interface(self, context, router_id, interface_info=None): router_interface_info = super( L3_NAT_db_mixin, self).add_router_interface( context, router_id, interface_info) self.notify_router_interface_action( context, router_interface_info, 'add') return router_interface_info def remove_router_interface(self, context, router_id, interface_info): router_interface_info = super( L3_NAT_db_mixin, self).remove_router_interface( context, router_id, interface_info) self.notify_router_interface_action( context, router_interface_info, 'remove') return router_interface_info def create_floatingip(self, context, floatingip, initial_status=constants.FLOATINGIP_STATUS_ACTIVE): floatingip_dict = super(L3_NAT_db_mixin, self).create_floatingip( context, floatingip, initial_status) router_id = floatingip_dict['router_id'] self.notify_router_updated(context, router_id, 'create_floatingip') return floatingip_dict def update_floatingip(self, context, id, floatingip): old_floatingip, floatingip = self._update_floatingip( context, id, floatingip) router_ids = self._floatingips_to_router_ids( [old_floatingip, floatingip]) super(L3_NAT_db_mixin, self).notify_routers_updated( context, router_ids, 'update_floatingip', {}) return floatingip def delete_floatingip(self, context, id): floating_ip = self._delete_floatingip(context, id) self.notify_router_updated(context, floating_ip['router_id'], 'delete_floatingip') def disassociate_floatingips(self, context, port_id, do_notify=True): """Disassociate all floating IPs linked to specific port. @param port_id: ID of the port to disassociate floating IPs. @param do_notify: whether we should notify routers right away. @return: set of router-ids that require notification updates if do_notify is False, otherwise None. """ router_ids = super(L3_NAT_db_mixin, self).disassociate_floatingips( context, port_id, do_notify) if do_notify: self.notify_routers_updated(context, router_ids) # since caller assumes that we handled notifications on its # behalf, return nothing return return router_ids def notify_routers_updated(self, context, router_ids): super(L3_NAT_db_mixin, self).notify_routers_updated( context, list(router_ids), 'disassociate_floatingips', {}) def _migrate_router_ports( self, context, router_db, old_owner, new_owner): """Update the model to support the dvr case of a router.""" for rp in router_db.attached_ports: if rp.port_type == old_owner: rp.port_type = new_owner rp.port.device_owner = new_owner neutron-12.1.1/neutron/db/allowed_address_pairs/0000775000175000017500000000000013553660156021733 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/allowed_address_pairs/__init__.py0000664000175000017500000000000013553660046024030 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/models_v2.py0000664000175000017500000003025213553660047017646 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import network as net_def from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import subnet as subnet_def from neutron_lib.api.definitions import subnetpool as subnetpool_def from neutron_lib import constants from neutron_lib.db import constants as db_const from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from sqlalchemy import sql from neutron.db.network_dhcp_agent_binding import models as ndab_model from neutron.db import rbac_db_models from neutron.db import standard_attr class IPAllocationPool(model_base.BASEV2, model_base.HasId): """Representation of an allocation pool in a Neutron subnet.""" subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id', ondelete="CASCADE"), nullable=True) first_ip = sa.Column(sa.String(64), nullable=False) last_ip = sa.Column(sa.String(64), nullable=False) def __repr__(self): return "%s - %s" % (self.first_ip, self.last_ip) class IPAllocation(model_base.BASEV2): """Internal representation of allocated IP addresses in a Neutron subnet. """ port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), nullable=True) ip_address = sa.Column(sa.String(64), nullable=False, primary_key=True) subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id', ondelete="CASCADE"), nullable=False, primary_key=True) network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id", ondelete="CASCADE"), nullable=False, primary_key=True) revises_on_change = ('port', ) class Route(object): """mixin of a route.""" destination = sa.Column(sa.String(64), nullable=False, primary_key=True) nexthop = sa.Column(sa.String(64), nullable=False, primary_key=True) class SubnetRoute(model_base.BASEV2, Route): subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id', ondelete="CASCADE"), primary_key=True) class Port(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a port on a Neutron v2 network.""" name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE)) network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id"), nullable=False) fixed_ips = orm.relationship(IPAllocation, backref=orm.backref('port', load_on_pending=True), lazy='subquery', cascade='all, delete-orphan', order_by=(IPAllocation.ip_address, IPAllocation.subnet_id)) mac_address = sa.Column(sa.String(32), nullable=False) admin_state_up = sa.Column(sa.Boolean(), nullable=False) status = sa.Column(sa.String(16), nullable=False) device_id = sa.Column(sa.String(db_const.DEVICE_ID_FIELD_SIZE), nullable=False) device_owner = sa.Column(sa.String(db_const.DEVICE_OWNER_FIELD_SIZE), nullable=False) ip_allocation = sa.Column(sa.String(16)) __table_args__ = ( sa.Index( 'ix_ports_network_id_mac_address', 'network_id', 'mac_address'), sa.Index( 'ix_ports_network_id_device_owner', 'network_id', 'device_owner'), sa.Index('ix_ports_device_id', 'device_id'), sa.UniqueConstraint( network_id, mac_address, name='uniq_ports0network_id0mac_address'), model_base.BASEV2.__table_args__ ) api_collections = [port_def.COLLECTION_NAME] collection_resource_map = {port_def.COLLECTION_NAME: port_def.RESOURCE_NAME} tag_support = True def __init__(self, id=None, tenant_id=None, project_id=None, name=None, network_id=None, mac_address=None, admin_state_up=None, status=None, device_id=None, device_owner=None, fixed_ips=None, **kwargs): super(Port, self).__init__(**kwargs) self.id = id self.project_id = project_id or tenant_id self.name = name self.network_id = network_id self.mac_address = mac_address self.admin_state_up = admin_state_up self.device_owner = device_owner self.device_id = device_id # Since this is a relationship only set it if one is passed in. if fixed_ips: self.fixed_ips = fixed_ips # NOTE(arosen): status must be set last as an event is triggered on! self.status = status class DNSNameServer(model_base.BASEV2): """Internal representation of a DNS nameserver.""" address = sa.Column(sa.String(128), nullable=False, primary_key=True) subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id', ondelete="CASCADE"), primary_key=True) order = sa.Column(sa.Integer, nullable=False, server_default='0') class Subnet(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a neutron subnet. When a subnet is created the first and last entries will be created. These are used for the IP allocation. """ name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE)) network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id')) # Added by the segments service plugin segment_id = sa.Column(sa.String(36), sa.ForeignKey('networksegments.id')) subnetpool_id = sa.Column(sa.String(36), index=True) # NOTE: Explicitly specify join conditions for the relationship because # subnetpool_id in subnet might be 'prefix_delegation' when the IPv6 Prefix # Delegation is enabled subnetpool = orm.relationship( 'SubnetPool', lazy='joined', foreign_keys='Subnet.subnetpool_id', primaryjoin='Subnet.subnetpool_id==SubnetPool.id') ip_version = sa.Column(sa.Integer, nullable=False) cidr = sa.Column(sa.String(64), nullable=False) gateway_ip = sa.Column(sa.String(64)) network_standard_attr = orm.relationship( 'StandardAttribute', lazy='subquery', viewonly=True, secondary='networks', uselist=False, load_on_pending=True) revises_on_change = ('network_standard_attr', ) allocation_pools = orm.relationship(IPAllocationPool, backref='subnet', lazy="subquery", cascade='delete') enable_dhcp = sa.Column(sa.Boolean()) dns_nameservers = orm.relationship(DNSNameServer, backref='subnet', cascade='all, delete, delete-orphan', order_by=DNSNameServer.order, lazy='subquery') routes = orm.relationship(SubnetRoute, backref='subnet', cascade='all, delete, delete-orphan', lazy='subquery') ipv6_ra_mode = sa.Column(sa.Enum(constants.IPV6_SLAAC, constants.DHCPV6_STATEFUL, constants.DHCPV6_STATELESS, name='ipv6_ra_modes'), nullable=True) ipv6_address_mode = sa.Column(sa.Enum(constants.IPV6_SLAAC, constants.DHCPV6_STATEFUL, constants.DHCPV6_STATELESS, name='ipv6_address_modes'), nullable=True) # subnets don't have their own rbac_entries, they just inherit from # the network rbac entries rbac_entries = orm.relationship( rbac_db_models.NetworkRBAC, lazy='subquery', uselist=True, foreign_keys='Subnet.network_id', primaryjoin='Subnet.network_id==NetworkRBAC.object_id') api_collections = [subnet_def.COLLECTION_NAME] collection_resource_map = {subnet_def.COLLECTION_NAME: subnet_def.RESOURCE_NAME} tag_support = True class SubnetPoolPrefix(model_base.BASEV2): """Represents a neutron subnet pool prefix """ __tablename__ = 'subnetpoolprefixes' cidr = sa.Column(sa.String(64), nullable=False, primary_key=True) subnetpool_id = sa.Column(sa.String(36), sa.ForeignKey('subnetpools.id', ondelete='CASCADE'), nullable=False, primary_key=True) class SubnetPool(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a neutron subnet pool. """ name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE)) ip_version = sa.Column(sa.Integer, nullable=False) default_prefixlen = sa.Column(sa.Integer, nullable=False) min_prefixlen = sa.Column(sa.Integer, nullable=False) max_prefixlen = sa.Column(sa.Integer, nullable=False) shared = sa.Column(sa.Boolean, nullable=False) is_default = sa.Column(sa.Boolean, nullable=False, server_default=sql.false()) default_quota = sa.Column(sa.Integer, nullable=True) hash = sa.Column(sa.String(36), nullable=False, server_default='') address_scope_id = sa.Column(sa.String(36), nullable=True) prefixes = orm.relationship(SubnetPoolPrefix, backref='subnetpools', cascade='all, delete, delete-orphan', lazy='subquery') api_collections = [subnetpool_def.COLLECTION_NAME] collection_resource_map = {subnetpool_def.COLLECTION_NAME: subnetpool_def.RESOURCE_NAME} tag_support = True class Network(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a v2 neutron network.""" name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE)) subnets = orm.relationship( Subnet, lazy="subquery") status = sa.Column(sa.String(16)) admin_state_up = sa.Column(sa.Boolean) vlan_transparent = sa.Column(sa.Boolean, nullable=True) rbac_entries = orm.relationship(rbac_db_models.NetworkRBAC, backref=orm.backref('network', load_on_pending=True), lazy='subquery', cascade='all, delete, delete-orphan') availability_zone_hints = sa.Column(sa.String(255)) # TODO(ihrachys) provide data migration path to fill in mtus for existing # networks in Queens when all controllers run Pike+ code mtu = sa.Column(sa.Integer, nullable=True) dhcp_agents = orm.relationship( 'Agent', lazy='subquery', viewonly=True, secondary=ndab_model.NetworkDhcpAgentBinding.__table__) api_collections = [net_def.COLLECTION_NAME] collection_resource_map = {net_def.COLLECTION_NAME: net_def.RESOURCE_NAME} tag_support = True neutron-12.1.1/neutron/db/quota_db.py0000664000175000017500000000153113553660047017550 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys from neutron.db.quota import driver # noqa # This module has been preserved for backward compatibility, and will be # deprecated in the future sys.modules[__name__] = sys.modules['neutron.db.quota.driver'] neutron-12.1.1/neutron/db/agents_db.py0000664000175000017500000005241113553660047017703 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime from eventlet import greenthread from neutron_lib.agent import constants as agent_consts from neutron_lib.api import converters from neutron_lib.api.definitions import agent as agent_apidef from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context from neutron_lib.exceptions import agent as agent_exc from neutron_lib.exceptions import availability_zone as az_exc from neutron_lib.plugins import directory from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_serialization import jsonutils from oslo_utils import importutils from oslo_utils import timeutils from neutron.agent.common import utils from neutron.api.rpc.callbacks import version_manager from neutron.common import constants as n_const from neutron.conf.agent.database import agents_db from neutron.db import _model_query as model_query from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.db.models import agent as agent_model from neutron.extensions import agent as ext_agent from neutron.extensions import availability_zone as az_ext from neutron.objects import agent as agent_obj LOG = logging.getLogger(__name__) agents_db.register_db_agents_opts() # this is the ratio from agent_down_time to the time we use to consider # the agents down for considering their resource versions in the # version_manager callback DOWNTIME_VERSIONS_RATIO = 2 def get_availability_zones_by_agent_type(context, agent_type, availability_zones): """Get list of availability zones based on agent type""" agents = agent_obj.Agent._get_agents_by_availability_zones_and_agent_type( context, agent_type=agent_type, availability_zones=availability_zones) return set(agent.availability_zone for agent in agents) class AgentAvailabilityZoneMixin(az_ext.AvailabilityZonePluginBase): """Mixin class to add availability_zone extension to AgentDbMixin.""" def _list_availability_zones(self, context, filters=None): result = {} filters = filters or {} agents = agent_obj.Agent.get_objects(context, **filters) for agent in agents: if not agent.availability_zone: continue if agent.agent_type == constants.AGENT_TYPE_DHCP: resource = 'network' elif agent.agent_type == constants.AGENT_TYPE_L3: resource = 'router' else: continue key = (agent.availability_zone, resource) result[key] = agent.admin_state_up or result.get(key, False) return result @db_api.retry_if_session_inactive() def get_availability_zones(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """Return a list of availability zones.""" # NOTE(hichihara): 'tenant_id' is dummy for policy check. # it is not visible via API. return [{'state': 'available' if v else 'unavailable', 'name': k[0], 'resource': k[1], 'tenant_id': context.tenant_id} for k, v in self._list_availability_zones( context, filters).items()] @db_api.retry_if_session_inactive() def validate_availability_zones(self, context, resource_type, availability_zones): """Verify that the availability zones exist.""" if not availability_zones: return if resource_type == 'network': agent_type = constants.AGENT_TYPE_DHCP elif resource_type == 'router': agent_type = constants.AGENT_TYPE_L3 else: return azs = get_availability_zones_by_agent_type( context, agent_type, availability_zones) diff = set(availability_zones) - set(azs) if diff: raise az_exc.AvailabilityZoneNotFound(availability_zone=diff.pop()) class AgentDbMixin(ext_agent.AgentPluginBase, AgentAvailabilityZoneMixin): """Mixin class to add agent extension to db_base_plugin_v2.""" def _get_agent(self, context, id): agent = agent_obj.Agent.get_object(context, id=id) if not agent: raise agent_exc.AgentNotFound(id=id) return agent @db_api.retry_if_session_inactive() def get_enabled_agent_on_host(self, context, agent_type, host): """Return agent of agent_type for the specified host.""" agent = agent_obj.Agent.get_object(context, agent_type=agent_type, host=host, admin_state_up=True) if not agent: LOG.debug('No enabled %(agent_type)s agent on host ' '%(host)s', {'agent_type': agent_type, 'host': host}) return if utils.is_agent_down(agent.heartbeat_timestamp): LOG.warning('%(agent_type)s agent %(agent_id)s is not active', {'agent_type': agent_type, 'agent_id': agent.id}) return agent @staticmethod def is_agent_considered_for_versions(agent_dict): return not timeutils.is_older_than(agent_dict['heartbeat_timestamp'], cfg.CONF.agent_down_time * DOWNTIME_VERSIONS_RATIO) def get_configuration_dict(self, agent_db): return self._get_dict(agent_db, 'configurations') def _get_dict(self, agent_db, dict_name, ignore_missing=False): json_value = None try: json_value = getattr(agent_db, dict_name) # TODO(tuanvu): after all agent_db is converted to agent_obj, # we no longer need this. # Without this check, some unit tests will fail # because some of json_values are dict already if not isinstance(json_value, dict): conf = jsonutils.loads(json_value) else: conf = json_value except Exception: if json_value or not ignore_missing: msg = ('Dictionary %(dict_name)s for agent %(agent_type)s ' 'on host %(host)s is invalid.') LOG.warning(msg, {'dict_name': dict_name, 'agent_type': agent_db.agent_type, 'host': agent_db.host}) conf = {} return conf def _get_agent_load(self, agent): configs = agent.get('configurations', {}) load_type = None load = 0 if(agent['agent_type'] == constants.AGENT_TYPE_DHCP): load_type = cfg.CONF.dhcp_load_type if load_type: load = int(configs.get(load_type, 0)) return load def _make_agent_dict(self, agent, fields=None): attr = agent_apidef.RESOURCE_ATTRIBUTE_MAP.get( agent_apidef.COLLECTION_NAME) res = dict((k, agent[k]) for k in attr if k not in ['alive', 'configurations']) res['alive'] = not utils.is_agent_down( res['heartbeat_timestamp'] ) res['configurations'] = self._get_dict(agent, 'configurations') res['resource_versions'] = self._get_dict(agent, 'resource_versions', ignore_missing=True) res['availability_zone'] = agent['availability_zone'] return db_utils.resource_fields(res, fields) @db_api.retry_if_session_inactive() def delete_agent(self, context, id): agent = self._get_agent(context, id) registry.notify(resources.AGENT, events.BEFORE_DELETE, self, context=context, agent=agent) agent.delete() @db_api.retry_if_session_inactive() def update_agent(self, context, id, agent): agent_data = agent['agent'] with context.session.begin(subtransactions=True): agent = self._get_agent(context, id) agent.update_fields(agent_data) agent.update() return self._make_agent_dict(agent) @db_api.retry_if_session_inactive() def get_agents_db(self, context, filters=None): # TODO(annp): keep this method for backward compatibility, # will need to clean it up later query = model_query.get_collection_query(context, agent_model.Agent, filters=filters) return query.all() @db_api.retry_if_session_inactive() def get_agent_objects(self, context, filters=None): filters = filters or {} return agent_obj.Agent.get_objects(context, **filters) @db_api.retry_if_session_inactive() def get_agents(self, context, filters=None, fields=None): filters = filters or {} alive = filters and filters.pop('alive', None) agents = agent_obj.Agent.get_objects(context, **filters) if alive: alive = converters.convert_to_boolean(alive[0]) agents = [agent for agent in agents if agent.is_active == alive] return [self._make_agent_dict(agent, fields=fields) for agent in agents] @db_api.retry_db_errors def agent_health_check(self): """Scan agents and log if some are considered dead.""" agents = self.get_agents(context.get_admin_context(), filters={'admin_state_up': [True]}) dead_agents = [agent for agent in agents if not agent['alive']] if dead_agents: data = '%20s %20s %s\n' % ('Type', 'Last heartbeat', "host") data += '\n'.join(['%20s %20s %s' % (agent['agent_type'], agent['heartbeat_timestamp'], agent['host']) for agent in dead_agents]) LOG.warning("Agent healthcheck: found %(count)s dead agents " "out of %(total)s:\n%(data)s", {'count': len(dead_agents), 'total': len(agents), 'data': data}) else: LOG.debug("Agent healthcheck: found %s active agents", len(agents)) def _get_agent_by_type_and_host(self, context, agent_type, host): agent_objs = agent_obj.Agent.get_objects(context, agent_type=agent_type, host=host) if not agent_objs: raise agent_exc.AgentNotFoundByTypeHost(agent_type=agent_type, host=host) if len(agent_objs) > 1: raise agent_exc.MultipleAgentFoundByTypeHost( agent_type=agent_type, host=host) return agent_objs[0] @db_api.retry_if_session_inactive() def get_agent(self, context, id, fields=None): agent = self._get_agent(context, id) return self._make_agent_dict(agent, fields) @db_api.retry_if_session_inactive() def filter_hosts_with_network_access( self, context, network_id, candidate_hosts): """Filter hosts with access to network_id. This method returns a subset of candidate_hosts with the ones with network access to network_id. A plugin can overload this method to define its own host network_id based filter. """ return candidate_hosts def _log_heartbeat(self, state, agent_db, agent_conf): if agent_conf.get('log_agent_heartbeats'): delta = timeutils.utcnow() - agent_db.heartbeat_timestamp LOG.info("Heartbeat received from %(type)s agent on " "host %(host)s, uuid %(uuid)s after %(delta)s", {'type': agent_db.agent_type, 'host': agent_db.host, 'uuid': state.get('uuid'), 'delta': delta}) @db_api.retry_if_session_inactive() def create_or_update_agent(self, context, agent_state): """Registers new agent in the database or updates existing. Returns tuple of agent status and state. Status is from server point of view: alive, new or revived. It could be used by agent to do some sync with the server if needed. """ status = agent_consts.AGENT_ALIVE with context.session.begin(subtransactions=True): res_keys = ['agent_type', 'binary', 'host', 'topic'] res = dict((k, agent_state[k]) for k in res_keys) if 'availability_zone' in agent_state: res['availability_zone'] = agent_state['availability_zone'] configurations_dict = agent_state.get('configurations', {}) res['configurations'] = jsonutils.dumps(configurations_dict) resource_versions_dict = agent_state.get('resource_versions') if resource_versions_dict: res['resource_versions'] = jsonutils.dumps( resource_versions_dict) res['load'] = self._get_agent_load(agent_state) current_time = timeutils.utcnow() try: agent = self._get_agent_by_type_and_host( context, agent_state['agent_type'], agent_state['host']) if not agent.is_active: status = agent_consts.AGENT_REVIVED if 'resource_versions' not in agent_state: # updating agent_state with resource_versions taken # from db so that # _update_local_agent_resource_versions() will call # version_manager and bring it up to date agent_state['resource_versions'] = self._get_dict( agent, 'resource_versions', ignore_missing=True) res['heartbeat_timestamp'] = current_time if agent_state.get('start_flag'): res['started_at'] = current_time greenthread.sleep(0) self._log_heartbeat(agent_state, agent, configurations_dict) agent.update_fields(res) agent.update() event_type = events.AFTER_UPDATE except agent_exc.AgentNotFoundByTypeHost: greenthread.sleep(0) res['created_at'] = current_time res['started_at'] = current_time res['heartbeat_timestamp'] = current_time res['admin_state_up'] = cfg.CONF.enable_new_agents agent = agent_obj.Agent(context=context, **res) greenthread.sleep(0) agent.create() event_type = events.AFTER_CREATE self._log_heartbeat(agent_state, agent, configurations_dict) status = agent_consts.AGENT_NEW greenthread.sleep(0) agent_state['agent_status'] = status agent_state['admin_state_up'] = agent.admin_state_up registry.notify(resources.AGENT, event_type, self, context=context, host=agent_state['host'], plugin=self, agent=agent_state) return status, agent_state def _get_agents_considered_for_versions(self): up_agents = self.get_agents(context.get_admin_context(), filters={'admin_state_up': [True]}) return filter(self.is_agent_considered_for_versions, up_agents) def get_agents_resource_versions(self, tracker): """Get the known agent resource versions and update the tracker. This function looks up into the database and updates every agent resource versions. This method is called from version_manager when the cached information has passed TTL. :param tracker: receives a version_manager.ResourceConsumerTracker """ for agent in self._get_agents_considered_for_versions(): resource_versions = agent.get('resource_versions', {}) consumer = version_manager.AgentConsumer( agent_type=agent['agent_type'], host=agent['host']) tracker.set_versions(consumer, resource_versions) class AgentExtRpcCallback(object): """Processes the rpc report in plugin implementations. This class implements the server side of an rpc interface. The client side can be found in neutron.agent.rpc.PluginReportStateAPI. For more information on changing rpc interfaces, see doc/source/contributor/internals/rpc_api.rst. API version history: 1.0 - Initial version. 1.1 - report_state now returns agent state. """ target = oslo_messaging.Target(version='1.1', namespace=n_const.RPC_NAMESPACE_STATE) START_TIME = timeutils.utcnow() def __init__(self, plugin=None): super(AgentExtRpcCallback, self).__init__() self.plugin = plugin #TODO(ajo): fix the resources circular dependency issue by dynamically # registering object types in the RPC callbacks api resources_rpc = importutils.import_module( 'neutron.api.rpc.handlers.resources_rpc') # Initialize RPC api directed to other neutron-servers self.server_versions_rpc = resources_rpc.ResourcesPushToServersRpcApi() @db_api.retry_if_session_inactive() def report_state(self, context, **kwargs): """Report state from agent to server. Returns - agent's status: AGENT_NEW, AGENT_REVIVED, AGENT_ALIVE """ time = kwargs['time'] time = timeutils.parse_strtime(time) agent_state = kwargs['agent_state']['agent_state'] self._check_clock_sync_on_agent_start(agent_state, time) if self.START_TIME > time: time_agent = datetime.datetime.isoformat(time) time_server = datetime.datetime.isoformat(self.START_TIME) log_dict = {'agent_time': time_agent, 'server_time': time_server} LOG.debug("Stale message received with timestamp: %(agent_time)s. " "Skipping processing because it's older than the " "server start timestamp: %(server_time)s", log_dict) return if not self.plugin: self.plugin = directory.get_plugin() agent_status, agent_state = self.plugin.create_or_update_agent( context, agent_state) self._update_local_agent_resource_versions(context, agent_state) return agent_status def _update_local_agent_resource_versions(self, context, agent_state): resource_versions_dict = agent_state.get('resource_versions') if not resource_versions_dict: return version_manager.update_versions( version_manager.AgentConsumer(agent_type=agent_state['agent_type'], host=agent_state['host']), resource_versions_dict) # report other neutron-servers about this quickly self.server_versions_rpc.report_agent_resource_versions( context, agent_state['agent_type'], agent_state['host'], resource_versions_dict) def _check_clock_sync_on_agent_start(self, agent_state, agent_time): """Checks if the server and the agent times are in sync. Method checks if the agent time is in sync with the server time on start up. Ignores it, on subsequent re-connects. """ if agent_state.get('start_flag'): time_server_now = timeutils.utcnow() diff = abs(timeutils.delta_seconds(time_server_now, agent_time)) if diff > cfg.CONF.agent_down_time: agent_name = agent_state['agent_type'] time_agent = datetime.datetime.isoformat(agent_time) host = agent_state['host'] log_dict = {'host': host, 'agent_name': agent_name, 'agent_time': time_agent, 'threshold': cfg.CONF.agent_down_time, 'serv_time': (datetime.datetime.isoformat (time_server_now)), 'diff': diff} LOG.error("Message received from the host: %(host)s " "during the registration of %(agent_name)s has " "a timestamp: %(agent_time)s. This differs from " "the current server timestamp: %(serv_time)s by " "%(diff)s seconds, which is more than the " "threshold agent down" "time: %(threshold)s.", log_dict) neutron-12.1.1/neutron/db/rbac_db_models.py0000664000175000017500000000722313553660047020675 0ustar zuulzuul00000000000000# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron_lib.db import constants as db_const from neutron_lib.db import model_base from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory import sqlalchemy as sa from sqlalchemy.ext import declarative from sqlalchemy.orm import validates from neutron._i18n import _ ACCESS_SHARED = 'access_as_shared' ACCESS_EXTERNAL = 'access_as_external' class InvalidActionForType(n_exc.InvalidInput): message = _("Invalid action '%(action)s' for object type " "'%(object_type)s'. Valid actions: %(valid_actions)s") class RBACColumns(model_base.HasId, model_base.HasProject): """Mixin that object-specific RBAC tables should inherit. All RBAC tables should inherit directly from this one because the RBAC code uses the __subclasses__() method to discover the RBAC types. """ # the target_tenant is the subject that the policy will affect. this may # also be a wildcard '*' to indicate all tenants or it may be a role if # neutron gets better integration with keystone target_tenant = sa.Column(sa.String(db_const.PROJECT_ID_FIELD_SIZE), nullable=False) action = sa.Column(sa.String(255), nullable=False) @abc.abstractproperty def object_type(self): # this determines the name that users will use in the API # to reference the type. sub-classes should set their own pass @declarative.declared_attr def __table_args__(cls): return ( sa.UniqueConstraint('target_tenant', 'object_id', 'action'), model_base.BASEV2.__table_args__ ) @validates('action') def _validate_action(self, key, action): if action not in self.get_valid_actions(): raise InvalidActionForType( action=action, object_type=self.object_type, valid_actions=self.get_valid_actions()) return action @abc.abstractmethod def get_valid_actions(self): # object table needs to override this to return an interable # with the valid actions rbac entries pass def get_type_model_map(): return {table.object_type: table for table in RBACColumns.__subclasses__()} def _object_id_column(foreign_key): return sa.Column(sa.String(36), sa.ForeignKey(foreign_key, ondelete="CASCADE"), nullable=False) class NetworkRBAC(RBACColumns, model_base.BASEV2): """RBAC table for networks.""" object_id = _object_id_column('networks.id') object_type = 'network' revises_on_change = ('network', ) def get_valid_actions(self): actions = (ACCESS_SHARED,) pl = directory.get_plugin() if 'external-net' in pl.supported_extension_aliases: actions += (ACCESS_EXTERNAL,) return actions class QosPolicyRBAC(RBACColumns, model_base.BASEV2): """RBAC table for qos policies.""" object_id = _object_id_column('qos_policies.id') object_type = 'qos_policy' def get_valid_actions(self): return (ACCESS_SHARED,) neutron-12.1.1/neutron/db/availability_zone/0000775000175000017500000000000013553660156021106 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/availability_zone/network.py0000664000175000017500000000275613553660047023162 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import availability_zone as az_def from neutron_lib.api.definitions import network as net_def from neutron_lib.api.validators import availability_zone as az_validator from neutron_lib.plugins import directory from neutron.db import _resource_extend as resource_extend from neutron.extensions import network_availability_zone as net_az @resource_extend.has_resource_extenders class NetworkAvailabilityZoneMixin(net_az.NetworkAvailabilityZonePluginBase): """Mixin class to enable network's availability zone attributes.""" @staticmethod @resource_extend.extends([net_def.COLLECTION_NAME]) def _extend_availability_zone(net_res, net_db): net_res[az_def.AZ_HINTS] = az_validator.convert_az_string_to_list( net_db[az_def.AZ_HINTS]) plugin = directory.get_plugin() net_res[az_def.COLLECTION_NAME] = ( plugin.get_network_availability_zones(net_db)) neutron-12.1.1/neutron/db/availability_zone/__init__.py0000664000175000017500000000000013553660046023203 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/availability_zone/router.py0000664000175000017500000000422013553660047022775 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import availability_zone as az_def from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib.plugins import constants from neutron_lib.plugins import directory from neutron.common import utils from neutron.db import _resource_extend as resource_extend from neutron.db import l3_attrs_db @resource_extend.has_resource_extenders @registry.has_registry_receivers class RouterAvailabilityZoneMixin(l3_attrs_db.ExtraAttributesMixin): """Mixin class to enable router's availability zone attributes.""" @staticmethod @resource_extend.extends([l3_apidef.ROUTERS]) def _add_az_to_response(router_res, router_db): l3_plugin = directory.get_plugin(constants.L3) if not utils.is_extension_supported(l3_plugin, 'router_availability_zone'): return router_res['availability_zones'] = ( l3_plugin.get_router_availability_zones(router_db)) @registry.receives(resources.ROUTER, [events.PRECOMMIT_CREATE]) def _process_az_request(self, resource, event, trigger, context, router, router_db, **kwargs): if az_def.AZ_HINTS in router: self.validate_availability_zones(context, 'router', router[az_def.AZ_HINTS]) self.set_extra_attr_value(context, router_db, az_def.AZ_HINTS, router[az_def.AZ_HINTS]) neutron-12.1.1/neutron/db/segments_db.py0000664000175000017500000001160413553660047020246 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from oslo_log import log as logging from oslo_utils import uuidutils from neutron.db import api as db_api from neutron.db.models import segment as segments_model from neutron.objects import base as base_obj from neutron.objects import network as network_obj LOG = logging.getLogger(__name__) NETWORK_TYPE = segments_model.NetworkSegment.network_type.name PHYSICAL_NETWORK = segments_model.NetworkSegment.physical_network.name SEGMENTATION_ID = segments_model.NetworkSegment.segmentation_id.name NETWORK_ID = segments_model.NetworkSegment.network_id.name def _make_segment_dict(obj): """Make a segment dictionary out of an object.""" return {'id': obj.id, NETWORK_TYPE: obj.network_type, PHYSICAL_NETWORK: obj.physical_network, SEGMENTATION_ID: obj.segmentation_id, NETWORK_ID: obj.network_id} def add_network_segment(context, network_id, segment, segment_index=0, is_dynamic=False): with db_api.context_manager.writer.using(context): netseg_obj = network_obj.NetworkSegment( context, id=uuidutils.generate_uuid(), network_id=network_id, network_type=segment.get(NETWORK_TYPE), physical_network=segment.get(PHYSICAL_NETWORK), segmentation_id=segment.get(SEGMENTATION_ID), segment_index=segment_index, is_dynamic=is_dynamic) netseg_obj.create() registry.notify(resources.SEGMENT, events.PRECOMMIT_CREATE, trigger=add_network_segment, context=context, segment=netseg_obj) segment['id'] = netseg_obj.id LOG.info("Added segment %(id)s of type %(network_type)s for network " "%(network_id)s", {'id': netseg_obj.id, 'network_type': netseg_obj.network_type, 'network_id': netseg_obj.network_id}) def get_network_segments(context, network_id, filter_dynamic=False): return get_networks_segments( context, [network_id], filter_dynamic)[network_id] def get_networks_segments(context, network_ids, filter_dynamic=False): if not network_ids: return {} with db_api.context_manager.reader.using(context): filters = { 'network_id': network_ids, } if filter_dynamic is not None: filters['is_dynamic'] = filter_dynamic objs = network_obj.NetworkSegment.get_objects(context, **filters) result = {net_id: [] for net_id in network_ids} for record in objs: result[record.network_id].append(_make_segment_dict(record)) return result def get_segment_by_id(context, segment_id): with db_api.context_manager.reader.using(context): net_obj = network_obj.NetworkSegment.get_object(context, id=segment_id) if net_obj: return _make_segment_dict(net_obj) def get_dynamic_segment(context, network_id, physical_network=None, segmentation_id=None): """Return a dynamic segment for the filters provided if one exists.""" with db_api.context_manager.reader.using(context): filters = { 'network_id': network_id, 'is_dynamic': True, } if physical_network: filters['physical_network'] = physical_network if segmentation_id: filters['segmentation_id'] = segmentation_id pager = base_obj.Pager(limit=1) objs = network_obj.NetworkSegment.get_objects( context, _pager=pager, **filters) if objs: return _make_segment_dict(objs[0]) else: LOG.debug("No dynamic segment found for " "Network:%(network_id)s, " "Physical network:%(physnet)s, " "segmentation_id:%(segmentation_id)s", {'network_id': network_id, 'physnet': physical_network, 'segmentation_id': segmentation_id}) def delete_network_segment(context, segment_id): """Release a dynamic segment for the params provided if one exists.""" with db_api.context_manager.writer.using(context): network_obj.NetworkSegment.delete_objects(context, id=segment_id) neutron-12.1.1/neutron/db/provisioning_blocks.py0000664000175000017500000001763413553660047022050 0ustar zuulzuul00000000000000# Copyright 2016 Mirantis, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from oslo_log import log as logging from neutron.db import api as db_api from neutron.db import models_v2 from neutron.objects import provisioning_blocks as pb_obj LOG = logging.getLogger(__name__) PROVISIONING_COMPLETE = 'provisioning_complete' # identifiers for the various entities that participate in provisioning DHCP_ENTITY = 'DHCP' L2_AGENT_ENTITY = 'L2' # TODO(sshank): Change to object later on when complete integration of Port # OVO is complete. Currently 'extend_port_dict' in ext_test fails when changed # to OVO here. _RESOURCE_TO_MODEL_MAP = {resources.PORT: models_v2.Port} def add_model_for_resource(resource, model): """Adds a mapping between a callback resource and a DB model.""" _RESOURCE_TO_MODEL_MAP[resource] = model @db_api.retry_if_session_inactive() def add_provisioning_component(context, object_id, object_type, entity): """Adds a provisioning block by an entity to a given object. Adds a provisioning block to the DB for object_id with an identifier of the entity that is doing the provisioning. While an object has these provisioning blocks present, this module will not emit any callback events indicating that provisioning has completed. Any logic that depends on multiple disjoint components may use these blocks and subscribe to the PROVISIONING_COMPLETE event to know when all components have completed. :param context: neutron api request context :param object_id: ID of object that has been provisioned :param object_type: callback resource type of the object :param entity: The entity that has provisioned the object """ log_dict = {'entity': entity, 'oid': object_id, 'otype': object_type} # we get an object's ID, so we need to convert that into a standard attr id standard_attr_id = _get_standard_attr_id(context, object_id, object_type) if not standard_attr_id: return if pb_obj.ProvisioningBlock.objects_exist( context, standard_attr_id=standard_attr_id, entity=entity): # an entry could be leftover from a previous transition that hasn't # yet been provisioned. (e.g. multiple updates in a short period) LOG.debug("Ignored duplicate provisioning block setup for %(otype)s " "%(oid)s by entity %(entity)s.", log_dict) return pb_obj.ProvisioningBlock( context, standard_attr_id=standard_attr_id, entity=entity).create() LOG.debug("Transition to ACTIVE for %(otype)s object %(oid)s " "will not be triggered until provisioned by entity %(entity)s.", log_dict) @db_api.retry_if_session_inactive() def remove_provisioning_component(context, object_id, object_type, entity, standard_attr_id=None): """Remove a provisioning block for an object without triggering a callback. Removes a provisioning block without triggering a callback. A user of this module should call this when a block is no longer correct. If the block has been satisfied, the 'provisioning_complete' method should be called. :param context: neutron api request context :param object_id: ID of object that has been provisioned :param object_type: callback resource type of the object :param entity: The entity that has provisioned the object :param standard_attr_id: Optional ID to pass to the function to avoid the extra DB lookup to translate the object_id into the standard_attr_id. :return: boolean indicating whether or not a record was deleted """ standard_attr_id = standard_attr_id or _get_standard_attr_id( context, object_id, object_type) if not standard_attr_id: return False if pb_obj.ProvisioningBlock.delete_objects( context, standard_attr_id=standard_attr_id, entity=entity): return True else: return False @db_api.retry_if_session_inactive() def provisioning_complete(context, object_id, object_type, entity): """Mark that the provisioning for object_id has been completed by entity. Marks that an entity has finished provisioning an object. If there are no remaining provisioning components, a callback will be triggered indicating that provisioning has been completed for the object. Subscribers to this callback must be idempotent because it may be called multiple times in high availability deployments. :param context: neutron api request context :param object_id: ID of object that has been provisioned :param object_type: callback resource type of the object :param entity: The entity that has provisioned the object """ log_dict = {'oid': object_id, 'entity': entity, 'otype': object_type} # this can't be called in a transaction to avoid REPEATABLE READ # tricking us into thinking there are remaining provisioning components if context.session.is_active: raise RuntimeError("Must not be called in a transaction") standard_attr_id = _get_standard_attr_id(context, object_id, object_type) if not standard_attr_id: return if remove_provisioning_component(context, object_id, object_type, entity, standard_attr_id): LOG.debug("Provisioning for %(otype)s %(oid)s completed by entity " "%(entity)s.", log_dict) # now with that committed, check if any records are left. if None, emit # an event that provisioning is complete. if not pb_obj.ProvisioningBlock.objects_exist( context, standard_attr_id=standard_attr_id): LOG.debug("Provisioning complete for %(otype)s %(oid)s triggered by " "entity %(entity)s.", log_dict) registry.notify(object_type, PROVISIONING_COMPLETE, 'neutron.db.provisioning_blocks', context=context, object_id=object_id) @db_api.retry_if_session_inactive() def is_object_blocked(context, object_id, object_type): """Return boolean indicating if object has a provisioning block. :param context: neutron api request context :param object_id: ID of object that has been provisioned :param object_type: callback resource type of the object """ standard_attr_id = _get_standard_attr_id(context, object_id, object_type) if not standard_attr_id: # object doesn't exist so it has no blocks return False return pb_obj.ProvisioningBlock.objects_exist( context, standard_attr_id=standard_attr_id) def _get_standard_attr_id(context, object_id, object_type): model = _RESOURCE_TO_MODEL_MAP.get(object_type) if not model: raise RuntimeError("Could not find model for %s. If you are " "adding provisioning blocks for a new resource " "you must call add_model_for_resource during " "initialization for your type." % object_type) obj = (context.session.query(model.standard_attr_id). enable_eagerloads(False). filter_by(id=object_id).first()) if not obj: # concurrent delete LOG.debug("Could not find standard attr ID for object %s.", object_id) return return obj.standard_attr_id neutron-12.1.1/neutron/db/l3_dvrscheduler_db.py0000664000175000017500000007012313553660047021512 0ustar zuulzuul00000000000000# (c) Copyright 2014 Hewlett-Packard Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as n_const from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_log import log as logging from sqlalchemy import or_ from neutron.common import constants as l3_consts from neutron.common import utils as n_utils from neutron.db import agentschedulers_db from neutron.db import l3_agentschedulers_db as l3agent_sch_db from neutron.db.models import l3 as l3_models from neutron.db import models_v2 from neutron.objects import l3agent as rb_obj from neutron.plugins.ml2 import db as ml2_db from neutron.plugins.ml2 import models as ml2_models LOG = logging.getLogger(__name__) class L3_DVRsch_db_mixin(l3agent_sch_db.L3AgentSchedulerDbMixin): """Mixin class for L3 DVR scheduler. DVR currently supports the following use cases: - East/West (E/W) traffic between VMs: this is handled in a distributed manner across Compute Nodes without a centralized element. This includes E/W traffic between VMs on the same Compute Node. - North/South traffic for Floating IPs (FIP N/S): this is supported on the distributed routers on Compute Nodes when there is external network connectivity and on centralized nodes when the port is not bound or when the agent is configured as 'dvr_no_external'. - North/South traffic for SNAT (SNAT N/S): this is supported via a centralized element that handles the SNAT traffic. To support these use cases, DVR routers rely on an L3 agent that runs on a central node (also known as Network Node or Service Node), as well as, L3 agents that run individually on each Compute Node of an OpenStack cloud. Each L3 agent creates namespaces to route traffic according to the use cases outlined above. The mechanism adopted for creating and managing these namespaces is via (Router, Agent) binding and Scheduling in general. The main difference between distributed routers and centralized ones is that in the distributed case, multiple bindings will exist, one for each of the agents participating in the routed topology for the specific router. These bindings are created in the following circumstances: - A subnet is added to a router via router-interface-add, and that subnet has running VM's deployed in it. A binding will be created between the router and any L3 agent whose Compute Node is hosting the VM(s). - An external gateway is set to a router via router-gateway-set. A binding will be created between the router and the L3 agent running centrally on the Network Node. Therefore, any time a router operation occurs (create, update or delete), scheduling will determine whether the router needs to be associated to an L3 agent, just like a regular centralized router, with the difference that, in the distributed case, the bindings required are established based on the state of the router and the Compute Nodes. """ def dvr_handle_new_service_port(self, context, port, dest_host=None, unbound_migrate=False): """Handle new dvr service port creation. When a new dvr service port is created, this function will schedule a dvr router to new compute node if needed and notify l3 agent on that node. The 'dest_host' will provide the destination host of the port in case of service port migration. If an unbound port migrates and becomes a bound port, send notification to the snat_agents and to the bound host. """ port_host = dest_host or port[portbindings.HOST_ID] l3_agent_on_host = (self.get_l3_agents( context, filters={'host': [port_host]}) or [None])[0] if not l3_agent_on_host: return if dest_host: # Make sure we create the floatingip agent gateway port # for the destination node if fip is associated with this # fixed port l3plugin = directory.get_plugin(plugin_constants.L3) ( l3plugin. check_for_fip_and_create_agent_gw_port_on_host_if_not_exists( context, port, dest_host)) subnet_ids = [ip['subnet_id'] for ip in port['fixed_ips']] router_ids = self.get_dvr_routers_by_subnet_ids(context, subnet_ids) if not router_ids: return agent_port_host_match = False if unbound_migrate: # This might be a case were it is migrating from unbound # to a bound port. # In that case please forward the notification to the # snat_nodes hosting the routers. # Make a call here to notify the snat nodes. snat_agent_list = self.get_dvr_snat_agent_list(context) for agent in snat_agent_list: LOG.debug('DVR: Handle new unbound migration port, ' 'host %(host)s, router_ids %(router_ids)s', {'host': agent.host, 'router_ids': router_ids}) self.l3_rpc_notifier.routers_updated_on_host( context, router_ids, agent.host) if agent.host == port_host: agent_port_host_match = True if not agent_port_host_match: connected_router_ids = set(router_ids) for router_id in router_ids: connected_router_ids.update( self._get_other_dvr_router_ids_connected_router( context, router_id)) LOG.debug('DVR: Handle new service port, host %(host)s, ' 'router ids %(router_ids)s', {'host': port_host, 'router_ids': connected_router_ids}) self.l3_rpc_notifier.routers_updated_on_host( context, connected_router_ids, port_host) def get_dvr_snat_agent_list(self, context): agent_filters = {'agent_modes': [n_const.L3_AGENT_MODE_DVR_SNAT]} state = agentschedulers_db.get_admin_state_up_filter() return self.get_l3_agents(context, active=state, filters=agent_filters) def get_dvr_routers_by_subnet_ids(self, context, subnet_ids): """Gets the dvr routers on vmport subnets.""" if not subnet_ids: return set() router_ids = set() filter_sub = {'fixed_ips': {'subnet_id': subnet_ids}, 'device_owner': [n_const.DEVICE_OWNER_DVR_INTERFACE]} subnet_ports = self._core_plugin.get_ports( context, filters=filter_sub) for subnet_port in subnet_ports: router_ids.add(subnet_port['device_id']) return router_ids def get_subnet_ids_on_router(self, context, router_id): """Return subnet IDs for interfaces attached to the given router.""" subnet_ids = set() filter_rtr = {'device_id': [router_id]} int_ports = self._core_plugin.get_ports(context, filters=filter_rtr) for int_port in int_ports: int_ips = int_port['fixed_ips'] if int_ips: int_subnet = int_ips[0]['subnet_id'] subnet_ids.add(int_subnet) else: LOG.debug('DVR: Could not find a subnet id ' 'for router %s', router_id) return subnet_ids def get_dvr_routers_to_remove(self, context, deleted_port, get_related_hosts_info=True): """Returns info about which routers should be removed In case dvr serviceable port was deleted we need to check if any dvr routers should be removed from l3 agent on port's host """ if not n_utils.is_dvr_serviced(deleted_port['device_owner']): return [] admin_context = context.elevated() port_host = deleted_port[portbindings.HOST_ID] subnet_ids = [ip['subnet_id'] for ip in deleted_port['fixed_ips']] router_ids = self.get_dvr_routers_by_subnet_ids(admin_context, subnet_ids) if not router_ids: LOG.debug('No DVR routers for this DVR port %(port)s ' 'on host %(host)s', {'port': deleted_port['id'], 'host': port_host}) return [] agent = self._get_agent_by_type_and_host( context, n_const.AGENT_TYPE_L3, port_host) removed_router_info = [] # NOTE(Swami): If host has any serviceable ports, # we should not remove the router namespace of the # port as well as the connected routers namespace. # After all serviceable ports in the host for the # connected routers are deleted, then we can remove # the router namespace. host_has_serviceable_port = False for router_id in router_ids: if rb_obj.RouterL3AgentBinding.objects_exist(context, router_id=router_id, l3_agent_id=agent.id): # not removing from the agent hosting SNAT for the router continue if self._check_for_rtr_serviceable_ports( admin_context, router_id, port_host): # once we found a serviceable port there is no need to # check further host_has_serviceable_port = True break self._unbind_dvr_port_before_delete(context, router_id, port_host) info = {'router_id': router_id, 'host': port_host, 'agent_id': str(agent.id)} removed_router_info.append(info) # Now collect the connected router info as well to remove # it from the agent, only if there is not a serviceable port. if not host_has_serviceable_port: related_router_ids = set() for router_id in router_ids: connected_dvr_router_ids = set( self._get_other_dvr_router_ids_connected_router( context, router_id)) related_router_ids |= connected_dvr_router_ids related_router_ids = [r_id for r_id in related_router_ids if r_id not in list(router_ids)] for router_id in related_router_ids: if self._check_for_rtr_serviceable_ports( admin_context, router_id, port_host): # once we found a serviceable port there is no need to # check further host_has_serviceable_port = True break self._unbind_dvr_port_before_delete(context, router_id, port_host) info = {'router_id': router_id, 'host': port_host, 'agent_id': str(agent.id)} removed_router_info.append(info) LOG.debug("Router info to be deleted: %s", removed_router_info) return removed_router_info def _check_for_rtr_serviceable_ports( self, admin_context, router_id, port_host): subnet_ids = self.get_subnet_ids_on_router(admin_context, router_id) return self._check_dvr_serviceable_ports_on_host( admin_context, port_host, subnet_ids) def _unbind_dvr_port_before_delete( self, context, router_id, port_host): filter_rtr = {'device_id': [router_id], 'device_owner': [n_const.DEVICE_OWNER_DVR_INTERFACE]} int_ports = self._core_plugin.get_ports( context.elevated(), filters=filter_rtr) for port in int_ports: dvr_binding = (ml2_db. get_distributed_port_binding_by_host( context, port['id'], port_host)) if dvr_binding: # unbind this port from router dvr_binding['router_id'] = None dvr_binding.update(dvr_binding) def _get_active_l3_agent_routers_sync_data(self, context, host, agent, router_ids): if n_utils.is_extension_supported(self, n_const.L3_HA_MODE_EXT_ALIAS): return self.get_ha_sync_data_for_host(context, host, agent, router_ids=router_ids, active=True) return self._get_dvr_sync_data(context, host, agent, router_ids=router_ids, active=True) def get_hosts_to_notify(self, context, router_id): """Returns all hosts to send notification about router update""" hosts = super(L3_DVRsch_db_mixin, self).get_hosts_to_notify( context, router_id) router = self.get_router(context.elevated(), router_id) if router.get('distributed', False): dvr_hosts = self._get_dvr_hosts_for_router(context, router_id) dvr_hosts = set(dvr_hosts) - set(hosts) dvr_hosts |= self._get_other_dvr_hosts(context, router_id) state = agentschedulers_db.get_admin_state_up_filter() agents = self.get_l3_agents(context, active=state, filters={'host': dvr_hosts}) hosts += [a.host for a in agents] return hosts def _get_dvr_hosts_for_router(self, context, router_id): """Get a list of hosts where specified DVR router should be hosted It will first get IDs of all subnets connected to the router and then get a set of hosts where all dvr serviceable ports on those subnets are bound """ subnet_ids = self.get_subnet_ids_on_router(context, router_id) hosts = self._get_dvr_hosts_for_subnets(context, subnet_ids) LOG.debug('Hosts for router %s: %s', router_id, hosts) return hosts def _get_other_dvr_hosts(self, context, router_id): """Get a list of hosts where specified DVR router should be hosted It will search DVR hosts based on other dvr routers connected to the router. """ dvr_hosts = set() connected_dvr_routers = ( self._get_other_dvr_router_ids_connected_router( context, router_id)) for dvr_router in connected_dvr_routers: dvr_hosts |= set( self._get_dvr_hosts_for_router(context, dvr_router)) LOG.debug('Hosts for other DVR routers connected to router ' '%(router_id)s: %(dvr_hosts)s', {'router_id': router_id, 'dvr_hosts': dvr_hosts}) return dvr_hosts def _get_dvr_hosts_for_subnets(self, context, subnet_ids): """Get a list of hosts with DVR servicable ports on subnet_ids.""" Binding = ml2_models.PortBinding Port = models_v2.Port IPAllocation = models_v2.IPAllocation query = context.session.query(Binding.host).distinct() query = query.join(Binding.port) query = query.join(Port.fixed_ips) query = query.filter(IPAllocation.subnet_id.in_(subnet_ids)) owner_filter = or_( Port.device_owner.startswith(n_const.DEVICE_OWNER_COMPUTE_PREFIX), Port.device_owner.in_( n_utils.get_other_dvr_serviced_device_owners())) query = query.filter(owner_filter) hosts = [item[0] for item in query if item[0] != ''] return hosts def _get_dvr_subnet_ids_on_host_query(self, context, host): query = context.session.query( models_v2.IPAllocation.subnet_id).distinct() query = query.join(models_v2.IPAllocation.port) query = query.join(models_v2.Port.port_binding) query = query.filter(ml2_models.PortBinding.host == host) owner_filter = or_( models_v2.Port.device_owner.startswith( n_const.DEVICE_OWNER_COMPUTE_PREFIX), models_v2.Port.device_owner.in_( n_utils.get_other_dvr_serviced_device_owners())) query = query.filter(owner_filter) return query def _get_dvr_router_ids_for_host(self, context, host): subnet_ids_on_host_query = self._get_dvr_subnet_ids_on_host_query( context, host) query = context.session.query(models_v2.Port.device_id).distinct() query = query.filter( models_v2.Port.device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE) query = query.join(models_v2.Port.fixed_ips) query = query.filter( models_v2.IPAllocation.subnet_id.in_(subnet_ids_on_host_query)) router_ids = [item[0] for item in query] LOG.debug('DVR routers on host %s: %s', host, router_ids) return router_ids def _get_other_dvr_router_ids_connected_router(self, context, router_id): # TODO(slaweq): move this method to RouterPort OVO object subnet_ids = self.get_subnet_ids_on_router(context, router_id) RouterPort = l3_models.RouterPort query = context.elevated().session.query(RouterPort.router_id) query = query.join(models_v2.Port) query = query.join( models_v2.Subnet, models_v2.Subnet.network_id == models_v2.Port.network_id) query = query.filter( models_v2.Subnet.id.in_(subnet_ids), RouterPort.port_type == n_const.DEVICE_OWNER_DVR_INTERFACE ).distinct() query = query.filter(RouterPort.router_id != router_id) return [item[0] for item in query] def _get_router_ids_for_agent(self, context, agent_db, router_ids): result_set = set(super(L3_DVRsch_db_mixin, self)._get_router_ids_for_agent( context, agent_db, router_ids)) router_ids = set(router_ids or []) if router_ids and result_set == router_ids: # no need for extra dvr checks if requested routers are # explicitly scheduled to the agent return list(result_set) # dvr routers are not explicitly scheduled to agents on hosts with # dvr serviceable ports, so need special handling if (self._get_agent_mode(agent_db) in [n_const.L3_AGENT_MODE_DVR, l3_consts.L3_AGENT_MODE_DVR_NO_EXTERNAL, n_const.L3_AGENT_MODE_DVR_SNAT]): dvr_routers = self._get_dvr_router_ids_for_host(context, agent_db['host']) if not router_ids: result_set |= set(dvr_routers) else: for router_id in (router_ids - result_set): subnet_ids = self.get_subnet_ids_on_router( context, router_id) if (subnet_ids and self._check_dvr_serviceable_ports_on_host( context, agent_db['host'], list(subnet_ids))): result_set.add(router_id) for dvr_router in dvr_routers: result_set |= set( self._get_other_dvr_router_ids_connected_router( context, dvr_router)) return list(result_set) def _check_dvr_serviceable_ports_on_host(self, context, host, subnet_ids): """Check for existence of dvr serviceable ports on host :param context: request context :param host: host to look ports on :param subnet_ids: IDs of subnets to look ports on :return: return True if dvr serviceable port exists on host, otherwise return False """ # db query will return ports for all subnets if subnet_ids is empty, # so need to check first if not subnet_ids: return False # The port binding profile filter for host performs a "contains" # operation. This produces a LIKE expression targeting a sub-string # match: column LIKE '%' || || '%'. # Add quotes to force an exact match of the host name in the port # binding profile dictionary. profile_host = "\"%s\"" % host Binding = ml2_models.PortBinding IPAllocation = models_v2.IPAllocation Port = models_v2.Port query = context.session.query(Binding) query = query.join(Binding.port) query = query.join(Port.fixed_ips) query = query.filter( IPAllocation.subnet_id.in_(subnet_ids)) device_filter = or_( models_v2.Port.device_owner.startswith( n_const.DEVICE_OWNER_COMPUTE_PREFIX), models_v2.Port.device_owner.in_( n_utils.get_other_dvr_serviced_device_owners())) query = query.filter(device_filter) host_filter = or_( ml2_models.PortBinding.host == host, ml2_models.PortBinding.profile.contains(profile_host)) query = query.filter(host_filter) return query.first() is not None def _dvr_handle_unbound_allowed_addr_pair_add( plugin, context, port, allowed_address_pair): plugin.update_arp_entry_for_dvr_service_port(context, port) def _dvr_handle_unbound_allowed_addr_pair_del( plugin, context, port, allowed_address_pair): aa_fixed_ips = plugin._get_allowed_address_pair_fixed_ips(context, port) if aa_fixed_ips: plugin.delete_arp_entry_for_dvr_service_port( context, port, fixed_ips_to_delete=aa_fixed_ips) def _notify_l3_agent_new_port(resource, event, trigger, **kwargs): LOG.debug('Received %(resource)s %(event)s', { 'resource': resource, 'event': event}) port = kwargs.get('port') if not port: return if n_utils.is_dvr_serviced(port['device_owner']): l3plugin = directory.get_plugin(plugin_constants.L3) context = kwargs['context'] l3plugin.dvr_handle_new_service_port(context, port) l3plugin.update_arp_entry_for_dvr_service_port(context, port) def _notify_port_delete(event, resource, trigger, **kwargs): context = kwargs['context'] port = kwargs['port'] get_related_hosts_info = kwargs.get("get_related_hosts_info", True) l3plugin = directory.get_plugin(plugin_constants.L3) if port: port_host = port.get(portbindings.HOST_ID) allowed_address_pairs_list = port.get('allowed_address_pairs') if allowed_address_pairs_list and port_host: for address_pair in allowed_address_pairs_list: _dvr_handle_unbound_allowed_addr_pair_del( l3plugin, context, port, address_pair) l3plugin.delete_arp_entry_for_dvr_service_port(context, port) removed_routers = l3plugin.get_dvr_routers_to_remove( context, port, get_related_hosts_info) for info in removed_routers: l3plugin.l3_rpc_notifier.router_removed_from_agent( context, info['router_id'], info['host']) def _notify_l3_agent_port_update(resource, event, trigger, **kwargs): new_port = kwargs.get('port') original_port = kwargs.get('original_port') if new_port and original_port: l3plugin = directory.get_plugin(plugin_constants.L3) context = kwargs['context'] is_bound_port_moved = ( original_port[portbindings.HOST_ID] and original_port[portbindings.HOST_ID] != new_port[portbindings.HOST_ID]) if is_bound_port_moved: removed_routers = l3plugin.get_dvr_routers_to_remove( context, original_port, get_related_hosts_info=False) if removed_routers: removed_router_args = { 'context': context, 'port': original_port, 'removed_routers': removed_routers, 'get_related_hosts_info': False, } _notify_port_delete( event, resource, trigger, **removed_router_args) fips = l3plugin._get_floatingips_by_port_id( context, port_id=original_port['id']) fip = fips[0] if fips else None if fip and not (removed_routers and fip['router_id'] in removed_routers): l3plugin.l3_rpc_notifier.routers_updated_on_host( context, [fip['router_id']], original_port[portbindings.HOST_ID]) is_new_port_binding_changed = ( new_port[portbindings.HOST_ID] and (original_port[portbindings.HOST_ID] != new_port[portbindings.HOST_ID])) dest_host = None new_port_profile = new_port.get(portbindings.PROFILE) if new_port_profile: dest_host = new_port_profile.get('migrating_to') # This check is required to prevent an arp update # of the allowed_address_pair port. if new_port_profile.get('original_owner'): return # If dest_host is set, then the port profile has changed # and this port is in migration. The call below will # pre-create the router on the new host # If the original_port is None, then it is a migration # from unbound to bound. if (is_new_port_binding_changed or dest_host): if (not original_port[portbindings.HOST_ID] and not original_port['device_owner']): l3plugin.dvr_handle_new_service_port(context, new_port, unbound_migrate=True) else: l3plugin.dvr_handle_new_service_port(context, new_port, dest_host=dest_host) l3plugin.update_arp_entry_for_dvr_service_port( context, new_port) return # Check for allowed_address_pairs and port state new_port_host = new_port.get(portbindings.HOST_ID) allowed_address_pairs_list = new_port.get('allowed_address_pairs') if allowed_address_pairs_list and new_port_host: new_port_state = new_port.get('admin_state_up') original_port_state = original_port.get('admin_state_up') if new_port_state: # Case were we activate the port from inactive state, # or the same port has additional address_pairs added. for address_pair in allowed_address_pairs_list: _dvr_handle_unbound_allowed_addr_pair_add( l3plugin, context, new_port, address_pair) return elif original_port_state: # Case were we deactivate the port from active state. for address_pair in allowed_address_pairs_list: _dvr_handle_unbound_allowed_addr_pair_del( l3plugin, context, original_port, address_pair) return is_fixed_ips_changed = ( 'fixed_ips' in new_port and 'fixed_ips' in original_port and new_port['fixed_ips'] != original_port['fixed_ips']) if kwargs.get('mac_address_updated') or is_fixed_ips_changed: l3plugin.update_arp_entry_for_dvr_service_port( context, new_port) def subscribe(): registry.subscribe( _notify_l3_agent_port_update, resources.PORT, events.AFTER_UPDATE) registry.subscribe( _notify_l3_agent_new_port, resources.PORT, events.AFTER_CREATE) registry.subscribe( _notify_port_delete, resources.PORT, events.AFTER_DELETE) neutron-12.1.1/neutron/db/extra_dhcp_opt/0000775000175000017500000000000013553660156020404 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/extra_dhcp_opt/models.py0000664000175000017500000000370113553660046022240 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm from neutron.db import models_v2 class ExtraDhcpOpt(model_base.BASEV2, model_base.HasId): """Represent a generic concept of extra options associated to a port. Each port may have none to many dhcp opts associated to it that can define specifically different or extra options to DHCP clients. These will be written to the /opts files, and each option's tag will be referenced in the /host file. """ port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete="CASCADE"), nullable=False) opt_name = sa.Column(sa.String(64), nullable=False) opt_value = sa.Column(sa.String(255), nullable=False) ip_version = sa.Column(sa.Integer, server_default='4', nullable=False) __table_args__ = (sa.UniqueConstraint( 'port_id', 'opt_name', 'ip_version', name='uniq_extradhcpopts0portid0optname0ipversion'), model_base.BASEV2.__table_args__,) # Add a relationship to the Port model in order to instruct SQLAlchemy to # eagerly load extra_dhcp_opts bindings ports = orm.relationship( models_v2.Port, load_on_pending=True, backref=orm.backref("dhcp_opts", lazy='subquery', cascade='delete')) revises_on_change = ('ports', ) neutron-12.1.1/neutron/db/extra_dhcp_opt/__init__.py0000664000175000017500000000000013553660046022501 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/db/securitygroups_db.py0000664000175000017500000011503613553660047021534 0ustar zuulzuul00000000000000# Copyright 2012 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron_lib.api.definitions import port as port_def from neutron_lib.api import validators from neutron_lib.callbacks import events from neutron_lib.callbacks import exceptions from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants from neutron_lib import context as context_lib from neutron_lib import exceptions as n_exc from neutron_lib.utils import helpers from neutron_lib.utils import net from oslo_utils import uuidutils import six from sqlalchemy.orm import scoped_session from neutron._i18n import _ from neutron.common import constants as n_const from neutron.common import utils from neutron.db import _model_query as model_query from neutron.db import _resource_extend as resource_extend from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.db.models import securitygroup as sg_models from neutron.extensions import securitygroup as ext_sg from neutron.objects import base as base_obj from neutron.objects import securitygroup as sg_obj @resource_extend.has_resource_extenders @registry.has_registry_receivers class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase): """Mixin class to add security group to db_base_plugin_v2.""" __native_bulk_support = True def create_security_group_bulk(self, context, security_groups): return self._create_bulk('security_group', context, security_groups) def _registry_notify(self, res, event, id=None, exc_cls=None, **kwargs): # NOTE(armax): a callback exception here will prevent the request # from being processed. This is a hook point for backend's validation; # we raise to propagate the reason for the failure. try: if 'payload' in kwargs: # TODO(boden): remove shim once all callbacks use payloads registry.publish(res, event, self, payload=kwargs['payload']) else: registry.notify(res, event, self, **kwargs) except exceptions.CallbackFailure as e: if exc_cls: reason = (_('cannot perform %(event)s due to %(reason)s') % {'event': event, 'reason': e}) raise exc_cls(reason=reason, id=id) @db_api.retry_if_session_inactive() def create_security_group(self, context, security_group, default_sg=False): """Create security group. If default_sg is true that means we are a default security group for a given tenant if it does not exist. """ s = security_group['security_group'] kwargs = { 'context': context, 'security_group': s, 'is_default': default_sg, } self._registry_notify(resources.SECURITY_GROUP, events.BEFORE_CREATE, exc_cls=ext_sg.SecurityGroupConflict, **kwargs) tenant_id = s['tenant_id'] if not default_sg: self._ensure_default_security_group(context, tenant_id) else: existing_def_sg_id = self._get_default_sg_id(context, tenant_id) if existing_def_sg_id is not None: # default already exists, return it return self.get_security_group(context, existing_def_sg_id) with db_api.context_manager.writer.using(context): sg = sg_obj.SecurityGroup( context, id=s.get('id') or uuidutils.generate_uuid(), description=s['description'], project_id=tenant_id, name=s['name'], is_default=default_sg) sg.create() for ethertype in ext_sg.sg_supported_ethertypes: if default_sg: # Allow intercommunication ingress_rule = sg_obj.SecurityGroupRule( context, id=uuidutils.generate_uuid(), project_id=tenant_id, security_group_id=sg.id, direction='ingress', ethertype=ethertype, remote_group_id=sg.id) ingress_rule.create() sg.rules.append(ingress_rule) egress_rule = sg_obj.SecurityGroupRule( context, id=uuidutils.generate_uuid(), project_id=tenant_id, security_group_id=sg.id, direction='egress', ethertype=ethertype) egress_rule.create() sg.rules.append(egress_rule) sg.obj_reset_changes(['rules']) # fetch sg from db to load the sg rules with sg model. # NOTE(yamamoto): Adding rules above bumps the revision # of the SG. It would add SG object to the session. # Expunge it to ensure the following get_object doesn't # use the instance. context.session.expunge(model_query.get_by_id( context, sg_models.SecurityGroup, sg.id)) sg = sg_obj.SecurityGroup.get_object(context, id=sg.id) secgroup_dict = self._make_security_group_dict(sg) kwargs['security_group'] = secgroup_dict self._registry_notify(resources.SECURITY_GROUP, events.PRECOMMIT_CREATE, exc_cls=ext_sg.SecurityGroupConflict, **kwargs) registry.notify(resources.SECURITY_GROUP, events.AFTER_CREATE, self, **kwargs) return secgroup_dict @db_api.retry_if_session_inactive() def get_security_groups(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False, default_sg=False): # If default_sg is True do not call _ensure_default_security_group() # so this can be done recursively. Context.tenant_id is checked # because all the unit tests do not explicitly set the context on # GETS. TODO(arosen) context handling can probably be improved here. filters = filters or {} if not default_sg and context.tenant_id: tenant_id = filters.get('tenant_id') if tenant_id: tenant_id = tenant_id[0] else: tenant_id = context.tenant_id self._ensure_default_security_group(context, tenant_id) pager = base_obj.Pager( sorts=sorts, limit=limit, marker=marker, page_reverse=page_reverse) sg_objs = sg_obj.SecurityGroup.get_objects( context, _pager=pager, validate_filters=False, fields=fields, **filters) return [self._make_security_group_dict(obj, fields) for obj in sg_objs] @db_api.retry_if_session_inactive() def get_security_groups_count(self, context, filters=None): filters = filters or {} return sg_obj.SecurityGroup.count( context, validate_filters=False, **filters) @db_api.retry_if_session_inactive() def get_security_group(self, context, id, fields=None, tenant_id=None): """Tenant id is given to handle the case when creating a security group rule on behalf of another use. """ if tenant_id: tmp_context_tenant_id = context.tenant_id context.tenant_id = tenant_id try: with db_api.context_manager.reader.using(context): ret = self._make_security_group_dict(self._get_security_group( context, id, fields=fields), fields) if (fields is None or len(fields) == 0 or 'security_group_rules' in fields): rules = self.get_security_group_rules( context_lib.get_admin_context(), {'security_group_id': [id]}) ret['security_group_rules'] = rules finally: if tenant_id: context.tenant_id = tmp_context_tenant_id return ret def _get_security_group(self, context, id, fields=None): sg = sg_obj.SecurityGroup.get_object(context, fields=fields, id=id) if sg is None: raise ext_sg.SecurityGroupNotFound(id=id) return sg def _check_security_group(self, context, id, tenant_id=None): if tenant_id: tmp_context_tenant_id = context.tenant_id context.tenant_id = tenant_id try: if not sg_obj.SecurityGroup.objects_exist(context, id=id): raise ext_sg.SecurityGroupNotFound(id=id) finally: if tenant_id: context.tenant_id = tmp_context_tenant_id @db_api.retry_if_session_inactive() def delete_security_group(self, context, id): filters = {'security_group_id': [id]} with db_api.context_manager.reader.using(context): ports = self._get_port_security_group_bindings(context, filters) if ports: raise ext_sg.SecurityGroupInUse(id=id) # confirm security group exists sg = self._get_security_group(context, id, fields=['id', 'name']) if sg['name'] == 'default' and not context.is_admin: raise ext_sg.SecurityGroupCannotRemoveDefault() kwargs = { 'context': context, 'security_group_id': id, 'security_group': sg, } self._registry_notify(resources.SECURITY_GROUP, events.BEFORE_DELETE, exc_cls=ext_sg.SecurityGroupInUse, id=id, **kwargs) with db_api.context_manager.writer.using(context): # pass security_group_rule_ids to ensure # consistency with deleted rules # get security_group_bindings and security_group one more time # so that they will be attached for session where sg will be # deleted ports = self._get_port_security_group_bindings(context, filters) sg = self._get_security_group(context, id) kwargs['security_group_rule_ids'] = [r['id'] for r in sg.rules] kwargs['security_group'] = self._make_security_group_dict(sg) self._registry_notify(resources.SECURITY_GROUP, events.PRECOMMIT_DELETE, exc_cls=ext_sg.SecurityGroupInUse, id=id, **kwargs) sg.delete() kwargs.pop('security_group') registry.notify(resources.SECURITY_GROUP, events.AFTER_DELETE, self, **kwargs) @db_api.retry_if_session_inactive() def update_security_group(self, context, id, security_group): s = security_group['security_group'] kwargs = { 'context': context, 'security_group_id': id, 'security_group': s, } self._registry_notify(resources.SECURITY_GROUP, events.BEFORE_UPDATE, exc_cls=ext_sg.SecurityGroupConflict, **kwargs) with db_api.context_manager.writer.using(context): sg = self._get_security_group(context, id) if sg.name == 'default' and 'name' in s: raise ext_sg.SecurityGroupCannotUpdateDefault() sg_dict = self._make_security_group_dict(sg) kwargs['original_security_group'] = sg_dict sg.update_fields(s) sg.update() sg_dict = self._make_security_group_dict(sg) kwargs['security_group'] = sg_dict self._registry_notify( resources.SECURITY_GROUP, events.PRECOMMIT_UPDATE, exc_cls=ext_sg.SecurityGroupConflict, payload=events.DBEventPayload( context, request_body=s, states=(kwargs['original_security_group'],), resource_id=id, desired_state=sg_dict)) registry.notify(resources.SECURITY_GROUP, events.AFTER_UPDATE, self, **kwargs) return sg_dict def _make_security_group_dict(self, security_group, fields=None): res = {'id': security_group['id'], 'name': security_group['name'], 'tenant_id': security_group['tenant_id'], 'description': security_group['description']} if security_group.rules: res['security_group_rules'] = [ self._make_security_group_rule_dict(r.db_obj) for r in security_group.rules ] else: res['security_group_rules'] = [] resource_extend.apply_funcs(ext_sg.SECURITYGROUPS, res, security_group.db_obj) return db_utils.resource_fields(res, fields) @staticmethod def _make_security_group_binding_dict(security_group, fields=None): res = {'port_id': security_group['port_id'], 'security_group_id': security_group['security_group_id']} return db_utils.resource_fields(res, fields) @db_api.retry_if_session_inactive() def _create_port_security_group_binding(self, context, port_id, security_group_id): with db_api.context_manager.writer.using(context): db = sg_models.SecurityGroupPortBinding(port_id=port_id, security_group_id=security_group_id) context.session.add(db) def _get_port_security_group_bindings(self, context, filters=None, fields=None): return model_query.get_collection( context, sg_models.SecurityGroupPortBinding, self._make_security_group_binding_dict, filters=filters, fields=fields) @db_api.retry_if_session_inactive() def _delete_port_security_group_bindings(self, context, port_id): with db_api.context_manager.writer.using(context): query = model_query.query_with_hooks( context, sg_models.SecurityGroupPortBinding) bindings = query.filter( sg_models.SecurityGroupPortBinding.port_id == port_id) for binding in bindings: context.session.delete(binding) @db_api.retry_if_session_inactive() def create_security_group_rule_bulk(self, context, security_group_rules): return self._create_bulk('security_group_rule', context, security_group_rules) @db_api.retry_if_session_inactive() def create_security_group_rule_bulk_native(self, context, security_group_rules): rules = security_group_rules['security_group_rules'] scoped_session(context.session) security_group_id = self._validate_security_group_rules( context, security_group_rules) with db_api.context_manager.writer.using(context): self._check_for_duplicate_rules(context, security_group_id, rules) ret = [] for rule_dict in rules: res_rule_dict = self._create_security_group_rule( context, rule_dict, validate=False) ret.append(res_rule_dict) for rdict in ret: registry.notify( resources.SECURITY_GROUP_RULE, events.AFTER_CREATE, self, context=context, security_group_rule=rdict) return ret @db_api.retry_if_session_inactive() def create_security_group_rule(self, context, security_group_rule): res = self._create_security_group_rule(context, security_group_rule) registry.notify( resources.SECURITY_GROUP_RULE, events.AFTER_CREATE, self, context=context, security_group_rule=res) return res def _create_security_group_rule(self, context, security_group_rule, validate=True): if validate: sg_id = self._validate_security_group_rule(context, security_group_rule) rule_dict = security_group_rule['security_group_rule'] remote_ip_prefix = rule_dict.get('remote_ip_prefix') if remote_ip_prefix: remote_ip_prefix = utils.AuthenticIPNetwork(remote_ip_prefix) protocol = rule_dict.get('protocol') if protocol: # object expects strings only protocol = six.text_type(protocol) args = { 'id': (rule_dict.get('id') or uuidutils.generate_uuid()), 'project_id': rule_dict['tenant_id'], 'security_group_id': rule_dict['security_group_id'], 'direction': rule_dict['direction'], 'remote_group_id': rule_dict.get('remote_group_id'), 'ethertype': rule_dict['ethertype'], 'protocol': protocol, 'remote_ip_prefix': remote_ip_prefix, 'description': rule_dict.get('description'), } port_range_min = self._safe_int(rule_dict['port_range_min']) if port_range_min is not None: args['port_range_min'] = port_range_min port_range_max = self._safe_int(rule_dict['port_range_max']) if port_range_max is not None: args['port_range_max'] = port_range_max kwargs = { 'context': context, 'security_group_rule': args } self._registry_notify(resources.SECURITY_GROUP_RULE, events.BEFORE_CREATE, exc_cls=ext_sg.SecurityGroupConflict, **kwargs) with db_api.context_manager.writer.using(context): if validate: self._check_for_duplicate_rules(context, sg_id, [security_group_rule]) sg_rule = sg_obj.SecurityGroupRule(context, **args) sg_rule.create() # fetch sg_rule from db to load the sg rules with sg model # otherwise a DetachedInstanceError can occur for model extensions sg_rule = sg_obj.SecurityGroupRule.get_object(context, id=sg_rule.id) res_rule_dict = self._make_security_group_rule_dict(sg_rule.db_obj) kwargs['security_group_rule'] = res_rule_dict self._registry_notify(resources.SECURITY_GROUP_RULE, events.PRECOMMIT_CREATE, exc_cls=ext_sg.SecurityGroupConflict, **kwargs) return res_rule_dict def _get_ip_proto_number(self, protocol): if protocol is None: return # According to bug 1381379, protocol is always set to string to avoid # problems with comparing int and string in PostgreSQL. Here this # string is converted to int to give an opportunity to use it as # before. if protocol in n_const.IP_PROTOCOL_NAME_ALIASES: protocol = n_const.IP_PROTOCOL_NAME_ALIASES[protocol] return int(constants.IP_PROTOCOL_MAP.get(protocol, protocol)) def _get_ip_proto_name_and_num(self, protocol): if protocol is None: return protocol = str(protocol) if protocol in constants.IP_PROTOCOL_MAP: return [protocol, str(constants.IP_PROTOCOL_MAP.get(protocol))] elif protocol in n_const.IP_PROTOCOL_NUM_TO_NAME_MAP: return [n_const.IP_PROTOCOL_NUM_TO_NAME_MAP.get(protocol), protocol] return [protocol, protocol] def _safe_int(self, port_range): if port_range is None: return try: return int(port_range) except (ValueError, TypeError): msg = "port range must be an integer" raise n_exc.InvalidInput(error_message=msg) def _validate_port_range(self, rule): """Check that port_range is valid.""" if (rule['port_range_min'] is None and rule['port_range_max'] is None): return if not rule['protocol']: raise ext_sg.SecurityGroupProtocolRequiredWithPorts() ip_proto = self._get_ip_proto_number(rule['protocol']) # Not all firewall_driver support all these protocols, # but being strict here doesn't hurt. if ip_proto in [constants.PROTO_NUM_DCCP, constants.PROTO_NUM_SCTP, constants.PROTO_NUM_TCP, constants.PROTO_NUM_UDP, constants.PROTO_NUM_UDPLITE]: if rule['port_range_min'] == 0 or rule['port_range_max'] == 0: raise ext_sg.SecurityGroupInvalidPortValue(port=0) elif (rule['port_range_min'] is not None and rule['port_range_max'] is not None and rule['port_range_min'] <= rule['port_range_max']): pass else: raise ext_sg.SecurityGroupInvalidPortRange() elif ip_proto in [constants.PROTO_NUM_ICMP, constants.PROTO_NUM_IPV6_ICMP]: for attr, field in [('port_range_min', 'type'), ('port_range_max', 'code')]: if rule[attr] is not None and not (0 <= rule[attr] <= 255): raise ext_sg.SecurityGroupInvalidIcmpValue( field=field, attr=attr, value=rule[attr]) if (rule['port_range_min'] is None and rule['port_range_max'] is not None): raise ext_sg.SecurityGroupMissingIcmpType( value=rule['port_range_max']) else: # Only the protocols above support port ranges, raise otherwise. # When min/max are the same it is just a single port. if (rule['port_range_min'] is not None and rule['port_range_max'] is not None and rule['port_range_min'] != rule['port_range_max']): raise ext_sg.SecurityGroupInvalidProtocolForPortRange( protocol=ip_proto) def _validate_ethertype_and_protocol(self, rule): """Check if given ethertype and protocol are valid or not""" if rule['protocol'] in [constants.PROTO_NAME_IPV6_ENCAP, constants.PROTO_NAME_IPV6_FRAG, constants.PROTO_NAME_IPV6_ICMP, constants.PROTO_NAME_IPV6_ICMP_LEGACY, constants.PROTO_NAME_IPV6_NONXT, constants.PROTO_NAME_IPV6_OPTS, constants.PROTO_NAME_IPV6_ROUTE, str(constants.PROTO_NUM_IPV6_ENCAP), str(constants.PROTO_NUM_IPV6_FRAG), str(constants.PROTO_NUM_IPV6_ICMP), str(constants.PROTO_NUM_IPV6_NONXT), str(constants.PROTO_NUM_IPV6_OPTS), str(constants.PROTO_NUM_IPV6_ROUTE)]: if rule['ethertype'] == constants.IPv4: raise ext_sg.SecurityGroupEthertypeConflictWithProtocol( ethertype=rule['ethertype'], protocol=rule['protocol']) def _validate_single_tenant_and_group(self, security_group_rules): """Check that all rules belong to the same security group and tenant """ sg_groups = set() tenants = set() for rule_dict in security_group_rules['security_group_rules']: rule = rule_dict['security_group_rule'] sg_groups.add(rule['security_group_id']) if len(sg_groups) > 1: raise ext_sg.SecurityGroupNotSingleGroupRules() tenants.add(rule['tenant_id']) if len(tenants) > 1: raise ext_sg.SecurityGroupRulesNotSingleTenant() return sg_groups.pop() def _validate_security_group_rule(self, context, security_group_rule): rule = security_group_rule['security_group_rule'] self._validate_port_range(rule) self._validate_ip_prefix(rule) self._validate_ethertype_and_protocol(rule) if rule['remote_ip_prefix'] and rule['remote_group_id']: raise ext_sg.SecurityGroupRemoteGroupAndRemoteIpPrefix() remote_group_id = rule['remote_group_id'] # Check that remote_group_id exists for tenant if remote_group_id: self._check_security_group(context, remote_group_id, tenant_id=rule['tenant_id']) security_group_id = rule['security_group_id'] # Confirm that the tenant has permission # to add rules to this security group. self._check_security_group(context, security_group_id, tenant_id=rule['tenant_id']) return security_group_id def _validate_security_group_rules(self, context, security_group_rules): sg_id = self._validate_single_tenant_and_group(security_group_rules) for rule in security_group_rules['security_group_rules']: self._validate_security_group_rule(context, rule) return sg_id def _make_security_group_rule_dict(self, security_group_rule, fields=None): res = {'id': security_group_rule['id'], 'tenant_id': security_group_rule['tenant_id'], 'security_group_id': security_group_rule['security_group_id'], 'ethertype': security_group_rule['ethertype'], 'direction': security_group_rule['direction'], 'protocol': security_group_rule['protocol'], 'port_range_min': security_group_rule['port_range_min'], 'port_range_max': security_group_rule['port_range_max'], 'remote_ip_prefix': security_group_rule['remote_ip_prefix'], 'remote_group_id': security_group_rule['remote_group_id']} resource_extend.apply_funcs(ext_sg.SECURITYGROUPRULES, res, security_group_rule) return db_utils.resource_fields(res, fields) def _make_security_group_rule_filter_dict(self, security_group_rule): sgr = security_group_rule['security_group_rule'] res = {'tenant_id': [sgr['tenant_id']], 'security_group_id': [sgr['security_group_id']], 'direction': [sgr['direction']]} include_if_present = ['protocol', 'port_range_max', 'port_range_min', 'ethertype', 'remote_group_id'] for key in include_if_present: value = sgr.get(key) if value: res[key] = [value] # protocol field will get corresponding name and number value = sgr.get('protocol') if value: res['protocol'] = self._get_ip_proto_name_and_num(value) return res def _rule_to_key(self, rule): def _normalize_rule_value(key, value): # This string is used as a placeholder for str(None), but shorter. none_char = '+' if key == 'remote_ip_prefix': all_address = ['0.0.0.0/0', '::/0', None] if value in all_address: return none_char elif value is None: return none_char elif key == 'protocol': return str(self._get_ip_proto_name_and_num(value)) return str(value) comparison_keys = [ 'direction', 'ethertype', 'port_range_max', 'port_range_min', 'protocol', 'remote_group_id', 'remote_ip_prefix', 'security_group_id' ] return '_'.join([_normalize_rule_value(x, rule.get(x)) for x in comparison_keys]) def _check_for_duplicate_rules(self, context, security_group_id, new_security_group_rules): # First up, check for any duplicates in the new rules. new_rules_set = set() for i in new_security_group_rules: rule_key = self._rule_to_key(i['security_group_rule']) if rule_key in new_rules_set: raise ext_sg.DuplicateSecurityGroupRuleInPost(rule=i) new_rules_set.add(rule_key) # Now, let's make sure none of the new rules conflict with # existing rules; note that we do *not* store the db rules # in the set, as we assume they were already checked, # when added. sg = self.get_security_group(context, security_group_id) if sg: for i in sg['security_group_rules']: rule_key = self._rule_to_key(i) if rule_key in new_rules_set: raise ext_sg.SecurityGroupRuleExists(rule_id=i.get('id')) def _validate_ip_prefix(self, rule): """Check that a valid cidr was specified as remote_ip_prefix No need to check that it is in fact an IP address as this is already validated by attribute validators. Check that rule ethertype is consistent with remote_ip_prefix ip type. Add mask to ip_prefix if absent (192.168.1.10 -> 192.168.1.10/32). """ input_prefix = rule['remote_ip_prefix'] if input_prefix: addr = netaddr.IPNetwork(input_prefix) # set input_prefix to always include the netmask: rule['remote_ip_prefix'] = str(addr) # check consistency of ethertype with addr version if rule['ethertype'] != "IPv%d" % (addr.version): raise ext_sg.SecurityGroupRuleParameterConflict( ethertype=rule['ethertype'], cidr=input_prefix) @db_api.retry_if_session_inactive() def get_security_group_rules(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): filters = filters or {} pager = base_obj.Pager( sorts=sorts, marker=marker, limit=limit, page_reverse=page_reverse) rule_objs = sg_obj.SecurityGroupRule.get_objects( context, _pager=pager, validate_filters=False, **filters ) return [ self._make_security_group_rule_dict(obj.db_obj, fields) for obj in rule_objs ] @db_api.retry_if_session_inactive() def get_security_group_rules_count(self, context, filters=None): filters = filters or {} return sg_obj.SecurityGroupRule.count( context, validate_filters=False, **filters) @db_api.retry_if_session_inactive() def get_security_group_rule(self, context, id, fields=None): security_group_rule = self._get_security_group_rule(context, id) return self._make_security_group_rule_dict( security_group_rule.db_obj, fields) def _get_security_group_rule(self, context, id): sgr = sg_obj.SecurityGroupRule.get_object(context, id=id) if sgr is None: raise ext_sg.SecurityGroupRuleNotFound(id=id) return sgr @db_api.retry_if_session_inactive() def delete_security_group_rule(self, context, id): kwargs = { 'context': context, 'security_group_rule_id': id } self._registry_notify(resources.SECURITY_GROUP_RULE, events.BEFORE_DELETE, id=id, exc_cls=ext_sg.SecurityGroupRuleInUse, **kwargs) with db_api.context_manager.writer.using(context): sgr = self._get_security_group_rule(context, id) kwargs['security_group_id'] = sgr['security_group_id'] self._registry_notify(resources.SECURITY_GROUP_RULE, events.PRECOMMIT_DELETE, exc_cls=ext_sg.SecurityGroupRuleInUse, id=id, **kwargs) sgr.delete() registry.notify( resources.SECURITY_GROUP_RULE, events.AFTER_DELETE, self, **kwargs) @staticmethod @resource_extend.extends([port_def.COLLECTION_NAME]) def _extend_port_dict_security_group(port_res, port_db): # Security group bindings will be retrieved from the SQLAlchemy # model. As they're loaded eagerly with ports because of the # joined load they will not cause an extra query. security_group_ids = [sec_group_mapping['security_group_id'] for sec_group_mapping in port_db.security_groups] port_res[ext_sg.SECURITYGROUPS] = security_group_ids return port_res def _process_port_create_security_group(self, context, port, security_group_ids): if validators.is_attr_set(security_group_ids): for security_group_id in security_group_ids: self._create_port_security_group_binding(context, port['id'], security_group_id) # Convert to list as a set might be passed here and # this has to be serialized port[ext_sg.SECURITYGROUPS] = (security_group_ids and list(security_group_ids) or []) def _get_default_sg_id(self, context, tenant_id): default_group = sg_obj.DefaultSecurityGroup.get_object( context, project_id=tenant_id, ) if default_group: return default_group.security_group_id @registry.receives(resources.PORT, [events.BEFORE_CREATE, events.BEFORE_UPDATE]) @registry.receives(resources.NETWORK, [events.BEFORE_CREATE]) def _ensure_default_security_group_handler(self, resource, event, trigger, context, **kwargs): if event == events.BEFORE_UPDATE: tenant_id = kwargs['original_' + resource]['tenant_id'] else: tenant_id = kwargs[resource]['tenant_id'] self._ensure_default_security_group(context, tenant_id) def _ensure_default_security_group(self, context, tenant_id): """Create a default security group if one doesn't exist. :returns: the default security group id for given tenant. """ default_group_id = self._get_default_sg_id(context, tenant_id) if default_group_id: return default_group_id security_group = { 'security_group': {'name': 'default', 'tenant_id': tenant_id, 'description': _('Default security group')} } return self.create_security_group(context, security_group, default_sg=True)['id'] def _get_security_groups_on_port(self, context, port): """Check that all security groups on port belong to tenant. :returns: all security groups IDs on port belonging to tenant. """ port = port['port'] if not validators.is_attr_set(port.get(ext_sg.SECURITYGROUPS)): return if port.get('device_owner') and net.is_port_trusted(port): return port_sg = port.get(ext_sg.SECURITYGROUPS, []) filters = {'id': port_sg} tenant_id = port.get('tenant_id') if tenant_id: filters['tenant_id'] = [tenant_id] valid_groups = set(g['id'] for g in self.get_security_groups(context, fields=['id'], filters=filters)) requested_groups = set(port_sg) port_sg_missing = requested_groups - valid_groups if port_sg_missing: raise ext_sg.SecurityGroupNotFound(id=', '.join(port_sg_missing)) return list(requested_groups) def _ensure_default_security_group_on_port(self, context, port): # we don't apply security groups for dhcp, router port = port['port'] if port.get('device_owner') and net.is_port_trusted(port): return if not validators.is_attr_set(port.get(ext_sg.SECURITYGROUPS)): default_sg = self._ensure_default_security_group(context, port['tenant_id']) port[ext_sg.SECURITYGROUPS] = [default_sg] def _check_update_deletes_security_groups(self, port): """Return True if port has as a security group and it's value is either [] or not is_attr_set, otherwise return False """ if (ext_sg.SECURITYGROUPS in port['port'] and not (validators.is_attr_set(port['port'][ext_sg.SECURITYGROUPS]) and port['port'][ext_sg.SECURITYGROUPS] != [])): return True return False def _check_update_has_security_groups(self, port): """Return True if port has security_groups attribute set and its not empty, or False otherwise. This method is called both for port create and port update. """ if (ext_sg.SECURITYGROUPS in port['port'] and (validators.is_attr_set(port['port'][ext_sg.SECURITYGROUPS]) and port['port'][ext_sg.SECURITYGROUPS] != [])): return True return False def update_security_group_on_port(self, context, id, port, original_port, updated_port): """Update security groups on port. This method returns a flag which indicates request notification is required and does not perform notification itself. It is because another changes for the port may require notification. """ need_notify = False port_updates = port['port'] if (ext_sg.SECURITYGROUPS in port_updates and not helpers.compare_elements( original_port.get(ext_sg.SECURITYGROUPS), port_updates[ext_sg.SECURITYGROUPS])): # delete the port binding and read it with the new rules port_updates[ext_sg.SECURITYGROUPS] = ( self._get_security_groups_on_port(context, port)) self._delete_port_security_group_bindings(context, id) self._process_port_create_security_group( context, updated_port, port_updates[ext_sg.SECURITYGROUPS]) need_notify = True else: updated_port[ext_sg.SECURITYGROUPS] = ( original_port[ext_sg.SECURITYGROUPS]) return need_notify neutron-12.1.1/neutron/db/agentschedulers_db.py0000664000175000017500000005430613553660047021607 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import random import time from neutron_lib import constants from neutron_lib import context as ncontext from neutron_lib.exceptions import agent as agent_exc from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_utils import timeutils from sqlalchemy.orm import exc from neutron.agent.common import utils as agent_utils from neutron.common import utils from neutron.conf.agent.database import agentschedulers_db from neutron.db import agents_db from neutron.db.availability_zone import network as network_az from neutron.extensions import dhcpagentscheduler from neutron.objects import network from neutron import worker as neutron_worker LOG = logging.getLogger(__name__) agentschedulers_db.register_db_agentschedulers_opts() class AgentSchedulerDbMixin(agents_db.AgentDbMixin): """Common class for agent scheduler mixins.""" # agent notifiers to handle agent update operations; # should be updated by plugins; agent_notifiers = { constants.AGENT_TYPE_DHCP: None, constants.AGENT_TYPE_L3: None, constants.AGENT_TYPE_LOADBALANCER: None, } @staticmethod def is_eligible_agent(active, agent): if active is None: # filtering by activeness is disabled, all agents are eligible return True else: # note(rpodolyaka): original behaviour is saved here: if active # filter is set, only agents which are 'up' # (i.e. have a recent heartbeat timestamp) # are eligible, even if active is False return not agent_utils.is_agent_down( agent['heartbeat_timestamp']) def update_agent(self, context, id, agent): original_agent = self.get_agent(context, id) result = super(AgentSchedulerDbMixin, self).update_agent( context, id, agent) agent_data = agent['agent'] agent_notifier = self.agent_notifiers.get(original_agent['agent_type']) if (agent_notifier and 'admin_state_up' in agent_data and original_agent['admin_state_up'] != agent_data['admin_state_up']): agent_notifier.agent_updated(context, agent_data['admin_state_up'], original_agent['host']) return result def add_agent_status_check_worker(self, function): # TODO(enikanorov): make interval configurable rather than computed interval = max(cfg.CONF.agent_down_time // 2, 1) # add random initial delay to allow agents to check in after the # neutron server first starts. random to offset multiple servers initial_delay = random.randint(interval, interval * 2) check_worker = neutron_worker.PeriodicWorker(function, interval, initial_delay) self.add_worker(check_worker) def agent_dead_limit_seconds(self): return cfg.CONF.agent_down_time * 2 def wait_down_agents(self, agent_type, agent_dead_limit): """Gives chance for agents to send a heartbeat.""" # check for an abrupt clock change since last check. if a change is # detected, sleep for a while to let the agents check in. tdelta = timeutils.utcnow() - getattr(self, '_clock_jump_canary', timeutils.utcnow()) if tdelta.total_seconds() > cfg.CONF.agent_down_time: LOG.warning("Time since last %s agent reschedule check has " "exceeded the interval between checks. Waiting " "before check to allow agents to send a heartbeat " "in case there was a clock adjustment.", agent_type) time.sleep(agent_dead_limit) self._clock_jump_canary = timeutils.utcnow() def get_cutoff_time(self, agent_dead_limit): cutoff = timeutils.utcnow() - datetime.timedelta( seconds=agent_dead_limit) return cutoff def reschedule_resources_from_down_agents(self, agent_type, get_down_bindings, agent_id_attr, resource_id_attr, resource_name, reschedule_resource, rescheduling_failed): """Reschedule resources from down neutron agents if admin state is up. """ agent_dead_limit = self.agent_dead_limit_seconds() self.wait_down_agents(agent_type, agent_dead_limit) context = ncontext.get_admin_context() try: down_bindings = get_down_bindings(context, agent_dead_limit) agents_back_online = set() for binding in down_bindings: binding_agent_id = getattr(binding, agent_id_attr) binding_resource_id = getattr(binding, resource_id_attr) if binding_agent_id in agents_back_online: continue else: # we need new context to make sure we use different DB # transaction - otherwise we may fetch same agent record # each time due to REPEATABLE_READ isolation level context = ncontext.get_admin_context() agent = self._get_agent(context, binding_agent_id) if agent.is_active: agents_back_online.add(binding_agent_id) continue LOG.warning( "Rescheduling %(resource_name)s %(resource)s from agent " "%(agent)s because the agent did not report to the server " "in the last %(dead_time)s seconds.", {'resource_name': resource_name, 'resource': binding_resource_id, 'agent': binding_agent_id, 'dead_time': agent_dead_limit}) try: reschedule_resource(context, binding_resource_id) except (rescheduling_failed, oslo_messaging.RemoteError): # Catch individual rescheduling errors here # so one broken one doesn't stop the iteration. LOG.exception("Failed to reschedule %(resource_name)s " "%(resource)s", {'resource_name': resource_name, 'resource': binding_resource_id}) except Exception: # we want to be thorough and catch whatever is raised # to avoid loop abortion LOG.exception("Exception encountered during %(resource_name)s " "rescheduling.", {'resource_name': resource_name}) class DhcpAgentSchedulerDbMixin(dhcpagentscheduler .DhcpAgentSchedulerPluginBase, AgentSchedulerDbMixin): """Mixin class to add DHCP agent scheduler extension to db_base_plugin_v2. """ network_scheduler = None def add_periodic_dhcp_agent_status_check(self): if not cfg.CONF.allow_automatic_dhcp_failover: LOG.info("Skipping periodic DHCP agent status check because " "automatic network rescheduling is disabled.") return self.add_agent_status_check_worker( self.remove_networks_from_down_agents ) def is_eligible_agent(self, context, active, agent): # eligible agent is active or starting up return (AgentSchedulerDbMixin.is_eligible_agent(active, agent) or self.agent_starting_up(context, agent)) def agent_starting_up(self, context, agent): """Check if agent was just started. Method returns True if agent is in its 'starting up' period. Return value depends on amount of networks assigned to the agent. It doesn't look at latest heartbeat timestamp as it is assumed that this method is called for agents that are considered dead. """ agent_dead_limit = datetime.timedelta( seconds=self.agent_dead_limit_seconds()) network_count = network.NetworkDhcpAgentBinding.count( context, dhcp_agent_id=agent['id']) # amount of networks assigned to agent affect amount of time we give # it so startup. Tests show that it's more or less sage to assume # that DHCP agent processes each network in less than 2 seconds. # So, give it this additional time for each of the networks. additional_time = datetime.timedelta(seconds=2 * network_count) LOG.debug("Checking if agent starts up and giving it additional %s", additional_time) agent_expected_up = (agent['started_at'] + agent_dead_limit + additional_time) return agent_expected_up > timeutils.utcnow() def _schedule_network(self, context, network_id, dhcp_notifier): LOG.info("Scheduling unhosted network %s", network_id) try: # TODO(enikanorov): have to issue redundant db query # to satisfy scheduling interface network = self.get_network(context, network_id) agents = self.schedule_network(context, network) if not agents: LOG.info("Failed to schedule network %s, " "no eligible agents or it might be " "already scheduled by another server", network_id) return if not dhcp_notifier: return for agent in agents: LOG.info("Adding network %(net)s to agent " "%(agent)s on host %(host)s", {'net': network_id, 'agent': agent.id, 'host': agent.host}) dhcp_notifier.network_added_to_agent( context, network_id, agent.host) except Exception: # catching any exception during scheduling # so if _schedule_network is invoked in the loop it could # continue in any case LOG.exception("Failed to schedule network %s", network_id) def _filter_bindings(self, context, bindings): """Skip bindings for which the agent is dead, but starting up.""" # to save few db calls: store already checked agents in dict # id -> is_agent_starting_up checked_agents = {} for binding in bindings: try: agent_id = binding.db_obj.dhcp_agent['id'] if agent_id not in checked_agents: if self.agent_starting_up(context, binding.db_obj.dhcp_agent): # When agent starts and it has many networks to process # it may fail to send state reports in defined interval # The server will consider it dead and try to remove # networks from it. checked_agents[agent_id] = True LOG.debug("Agent %s is starting up, skipping", agent_id) else: checked_agents[agent_id] = False if not checked_agents[agent_id]: yield binding except exc.ObjectDeletedError: # we're not within a transaction, so object can be lost # because underlying row is removed, just ignore this issue LOG.debug("binding was removed concurrently, skipping it") def remove_networks_from_down_agents(self): """Remove networks from down DHCP agents if admin state is up. Reschedule them if configured so. """ agent_dead_limit = self.agent_dead_limit_seconds() self.wait_down_agents('DHCP', agent_dead_limit) cutoff = self.get_cutoff_time(agent_dead_limit) context = ncontext.get_admin_context() try: down_bindings = network.NetworkDhcpAgentBinding.get_down_bindings( context, cutoff) dhcp_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_DHCP) dead_bindings = [b for b in self._filter_bindings(context, down_bindings)] agents = self.get_agent_objects( context, {'agent_type': [constants.AGENT_TYPE_DHCP]}) if not agents: # No agents configured so nothing to do. return active_agents = [agent for agent in agents if self.is_eligible_agent(context, True, agent)] if not active_agents: LOG.warning("No DHCP agents available, " "skipping rescheduling") return for binding in dead_bindings: LOG.warning("Removing network %(network)s from agent " "%(agent)s because the agent did not report " "to the server in the last %(dead_time)s " "seconds.", {'network': binding.network_id, 'agent': binding.dhcp_agent_id, 'dead_time': agent_dead_limit}) # save binding object to avoid ObjectDeletedError # in case binding is concurrently deleted from the DB saved_binding = {'net': binding.network_id, 'agent': binding.dhcp_agent_id} try: # do not notify agent if it considered dead # so when it is restarted it won't see network delete # notifications on its queue self.remove_network_from_dhcp_agent(context, binding.dhcp_agent_id, binding.network_id, notify=False) except dhcpagentscheduler.NetworkNotHostedByDhcpAgent: # measures against concurrent operation LOG.debug("Network %(net)s already removed from DHCP " "agent %(agent)s", saved_binding) # still continue and allow concurrent scheduling attempt except Exception: LOG.exception("Unexpected exception occurred while " "removing network %(net)s from agent " "%(agent)s", saved_binding) if cfg.CONF.network_auto_schedule: self._schedule_network( context, saved_binding['net'], dhcp_notifier) except Exception: # we want to be thorough and catch whatever is raised # to avoid loop abortion LOG.exception("Exception encountered during network " "rescheduling") def get_dhcp_agents_hosting_networks( self, context, network_ids, active=None, admin_state_up=None, hosts=None): if not network_ids: return [] # get all the NDAB objects, which will also fetch (from DB) # the related dhcp_agent objects because of the synthetic field bindings = network.NetworkDhcpAgentBinding.get_objects( context, network_id=network_ids) # get the already fetched dhcp_agent objects agent_objs = [binding.db_obj.dhcp_agent for binding in bindings] # filter the dhcp_agent objects on admin_state_up if admin_state_up is not None: agent_objs = [agent for agent in agent_objs if agent.admin_state_up == admin_state_up] # filter the dhcp_agent objects on hosts if hosts: agent_objs = [agent for agent in agent_objs if agent.host in hosts] # finally filter if the agents are eligible return [agent for agent in agent_objs if self.is_eligible_agent(context, active, agent)] def add_network_to_dhcp_agent(self, context, id, network_id): self._get_network(context, network_id) with context.session.begin(subtransactions=True): agent_db = self._get_agent(context, id) if (agent_db['agent_type'] != constants.AGENT_TYPE_DHCP or not services_available(agent_db['admin_state_up'])): raise dhcpagentscheduler.InvalidDHCPAgent(id=id) dhcp_agents = self.get_dhcp_agents_hosting_networks( context, [network_id]) for dhcp_agent in dhcp_agents: if id == dhcp_agent.id: raise dhcpagentscheduler.NetworkHostedByDHCPAgent( network_id=network_id, agent_id=id) network.NetworkDhcpAgentBinding(context, dhcp_agent_id=id, network_id=network_id).create() dhcp_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_DHCP) if dhcp_notifier: dhcp_notifier.network_added_to_agent( context, network_id, agent_db.host) def remove_network_from_dhcp_agent(self, context, id, network_id, notify=True): agent = self._get_agent(context, id) binding_obj = network.NetworkDhcpAgentBinding.get_object( context, network_id=network_id, dhcp_agent_id=id) if not binding_obj: raise dhcpagentscheduler.NetworkNotHostedByDhcpAgent( network_id=network_id, agent_id=id) # reserve the port, so the ip is reused on a subsequent add device_id = utils.get_dhcp_agent_device_id(network_id, agent['host']) filters = dict(device_id=[device_id]) ports = self.get_ports(context, filters=filters) # NOTE(kevinbenton): there should only ever be one port per # DHCP agent per network so we don't have to worry about one # update_port passing and another failing for port in ports: port['device_id'] = constants.DEVICE_ID_RESERVED_DHCP_PORT self.update_port(context, port['id'], dict(port=port)) binding_obj.delete() if not notify: return dhcp_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_DHCP) if dhcp_notifier: dhcp_notifier.network_removed_from_agent( context, network_id, agent.host) def list_networks_on_dhcp_agent(self, context, id): objs = network.NetworkDhcpAgentBinding.get_objects(context, dhcp_agent_id=id) net_ids = [item.network_id for item in objs] if net_ids: return {'networks': self.get_networks(context, filters={'id': net_ids})} else: # Exception will be thrown if the requested agent does not exist. self._get_agent(context, id) return {'networks': []} def list_active_networks_on_active_dhcp_agent(self, context, host): try: agent = self._get_agent_by_type_and_host( context, constants.AGENT_TYPE_DHCP, host) except agent_exc.AgentNotFoundByTypeHost: LOG.debug("DHCP Agent not found on host %s", host) return [] if not services_available(agent.admin_state_up): return [] query = network.NetworkDhcpAgentBinding.get_objects( context, dhcp_agent_id=agent.id) net_ids = [item.network_id for item in query] if net_ids: return self.get_networks( context, filters={'id': net_ids, 'admin_state_up': [True]} ) else: return [] def list_dhcp_agents_hosting_network(self, context, network_id): dhcp_agents = self.get_dhcp_agents_hosting_networks( context, [network_id]) agent_ids = [dhcp_agent.id for dhcp_agent in dhcp_agents] if agent_ids: return { 'agents': self.get_agents(context, filters={'id': agent_ids})} else: return {'agents': []} def schedule_network(self, context, created_network): if self.network_scheduler: return self.network_scheduler.schedule( self, context, created_network) def auto_schedule_networks(self, context, host): if self.network_scheduler: self.network_scheduler.auto_schedule_networks(self, context, host) class AZDhcpAgentSchedulerDbMixin(DhcpAgentSchedulerDbMixin, network_az.NetworkAvailabilityZoneMixin): """Mixin class to add availability_zone supported DHCP agent scheduler.""" def get_network_availability_zones(self, network): zones = {agent.availability_zone for agent in network.dhcp_agents} return list(zones) # helper functions for readability. def services_available(admin_state_up): if cfg.CONF.enable_services_on_agents_with_admin_state_down: # Services are available regardless admin_state_up return True return admin_state_up def get_admin_state_up_filter(): if cfg.CONF.enable_services_on_agents_with_admin_state_down: # Avoid filtering on admin_state_up at all return None # Filters on admin_state_up is True return True neutron-12.1.1/neutron/db/_utils.py0000664000175000017500000001156013553660047017254 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ NOTE: This module shall not be used by external projects. It will be moved to neutron-lib in due course, and then it can be used from there. """ import contextlib from neutron_lib.api import attributes from oslo_log import log as logging from oslo_utils import excutils from sqlalchemy.ext import associationproxy LOG = logging.getLogger(__name__) @contextlib.contextmanager def _noop_context_manager(): yield def safe_creation(context, create_fn, delete_fn, create_bindings, transaction=True): '''This function wraps logic of object creation in safe atomic way. In case of exception, object is deleted. More information when this method could be used can be found in developer guide - Effective Neutron: Database interaction section. https://docs.openstack.org/neutron/latest/contributor/effective_neutron.html :param context: context :param create_fn: function without arguments that is called to create object and returns this object. :param delete_fn: function that is called to delete an object. It is called with object's id field as an argument. :param create_bindings: function that is called to create bindings for an object. It is called with object's id field as an argument. :param transaction: if true the whole operation will be wrapped in a transaction. if false, no transaction will be used. ''' cm = (context.session.begin(subtransactions=True) if transaction else _noop_context_manager()) with cm: obj = create_fn() try: value = create_bindings(obj['id']) except Exception: with excutils.save_and_reraise_exception(): try: delete_fn(obj['id']) except Exception as e: LOG.error("Cannot clean up created object %(obj)s. " "Exception: %(exc)s", {'obj': obj['id'], 'exc': e}) return obj, value def model_query_scope_is_project(context, model): # Unless a context has 'admin' or 'advanced-service' rights the # query will be scoped to a single project_id return ((not context.is_admin and hasattr(model, 'project_id')) and (not context.is_advsvc and hasattr(model, 'project_id'))) def model_query(context, model): query = context.session.query(model) # define basic filter condition for model query query_filter = None if model_query_scope_is_project(context, model): query_filter = (model.tenant_id == context.tenant_id) if query_filter is not None: query = query.filter(query_filter) return query # NOTE: This used to be CommonDbMixin._fields() def resource_fields(resource, fields): """Return only the resource items that are in fields. :param resource: a resource dictionary :type resource: dict :param fields: a list of fields to select from the resource :type fields: list """ if fields: resource = {key: item for key, item in resource.items() if key in fields} return attributes.populate_project_info(resource) # NOTE: This used to be CommonDbMixin._filter_non_model_columns def filter_non_model_columns(data, model): """Return the attributes from data which are model columns. Return a new dict with items from data that whose keys are columns in the model or are association proxies of the model. """ columns = [c.name for c in model.__table__.columns] return dict((k, v) for (k, v) in data.items() if k in columns or isinstance(getattr(model, k, None), associationproxy.AssociationProxy)) # NOTE: This used to be CommonDbMixin._get_marker_obj def get_marker_obj(plugin, context, resource, limit, marker): """Retrieve a resource marker object. This function is used to invoke: plugin._get_(context, marker) It is used for pagination. :param plugin: The plugin processing the request. :param context: The request context. :param resource: The resource name. :param limit: Indicates if pagination is in effect. :param marker: The id of the marker object. """ if limit and marker: return getattr(plugin, '_get_%s' % resource)(context, marker) neutron-12.1.1/neutron/db/flavors_db.py0000664000175000017500000002544113553660047020101 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.exceptions import flavors as flav_exc from oslo_db import exception as db_exc from oslo_log import log as logging from neutron.db import _utils as db_utils from neutron.db import api as db_api from neutron.db import common_db_mixin from neutron.db import servicetype_db as sdb from neutron.objects import base as base_obj from neutron.objects import flavor as obj_flavor LOG = logging.getLogger(__name__) class FlavorsDbMixin(common_db_mixin.CommonDbMixin): """Class to support flavors and service profiles.""" def _get_flavor(self, context, flavor_id): flavor = obj_flavor.Flavor.get_object(context, id=flavor_id) if not flavor: raise flav_exc.FlavorNotFound(flavor_id=flavor_id) return flavor def _get_service_profile(self, context, sp_id): service_profile = obj_flavor.ServiceProfile.get_object( context, id=sp_id) if not service_profile: raise flav_exc.ServiceProfileNotFound(sp_id=sp_id) return service_profile @staticmethod def _make_flavor_dict(flavor_obj, fields=None): res = {'id': flavor_obj['id'], 'name': flavor_obj['name'], 'description': flavor_obj['description'], 'service_type': flavor_obj['service_type'], 'enabled': flavor_obj['enabled'], 'service_profiles': list(flavor_obj['service_profile_ids'])} return db_utils.resource_fields(res, fields) @staticmethod def _make_service_profile_dict(sp_obj, fields=None): res = {'id': sp_obj['id'], 'description': sp_obj['description'], 'driver': sp_obj['driver'], 'enabled': sp_obj['enabled'], 'metainfo': sp_obj['metainfo'], 'flavors': list(sp_obj['flavor_ids'])} return db_utils.resource_fields(res, fields) def _ensure_flavor_not_in_use(self, context, flavor_id): """Checks that flavor is not associated with service instance.""" # Future TODO(enikanorov): check that there is no binding to # instances. Shall address in future upon getting the right # flavor supported driver # NOTE(kevinbenton): sqlalchemy utils has a cool dependent # objects function we can use to quickly query all tables # that have a foreign key ref to flavors. Or we could replace # the call to this with callback events. pass def _ensure_service_profile_not_in_use(self, context, sp_id): """Ensures no current bindings to flavors exist.""" if obj_flavor.FlavorServiceProfileBinding.objects_exist( context, service_profile_id=sp_id): raise flav_exc.ServiceProfileInUse(sp_id=sp_id) def _validate_driver(self, context, driver): """Confirms a non-empty driver is a valid provider.""" service_type_manager = sdb.ServiceTypeManager.get_instance() providers = service_type_manager.get_service_providers( context, filters={'driver': driver}) if not providers: raise flav_exc.ServiceProfileDriverNotFound(driver=driver) def create_flavor(self, context, flavor): fl = flavor['flavor'] obj = obj_flavor.Flavor( context, name=fl['name'], description=fl['description'], service_type=fl['service_type'], enabled=fl['enabled']) obj.create() return self._make_flavor_dict(obj) def update_flavor(self, context, flavor_id, flavor): with db_api.context_manager.writer.using(context): self._ensure_flavor_not_in_use(context, flavor_id) fl_obj = self._get_flavor(context, flavor_id) fl_obj.update_fields(flavor['flavor']) fl_obj.update() return self._make_flavor_dict(fl_obj) def get_flavor(self, context, flavor_id, fields=None): fl = self._get_flavor(context, flavor_id) return self._make_flavor_dict(fl, fields) def delete_flavor(self, context, flavor_id): # NOTE(kevinbenton): we need to fix _ensure_flavor_not_in_use, # but the fix is non-trivial since multiple services can use # flavors so for now we just capture the foreign key violation # to detect if it's in use. try: with db_api.context_manager.writer.using(context): self._ensure_flavor_not_in_use(context, flavor_id) self._get_flavor(context, flavor_id).delete() except db_exc.DBReferenceError: raise flav_exc.FlavorInUse(flavor_id=flavor_id) def get_flavors(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pager = base_obj.Pager(sorts, limit, page_reverse, marker) filters = filters or {} flavor_objs = obj_flavor.Flavor.get_objects(context, _pager=pager, **filters) return [self._make_flavor_dict(flavor_object, fields) for flavor_object in flavor_objs] def create_flavor_service_profile(self, context, service_profile, flavor_id): sp = service_profile['service_profile'] with db_api.context_manager.writer.using(context): if obj_flavor.FlavorServiceProfileBinding.objects_exist( context, service_profile_id=sp['id'], flavor_id=flavor_id): raise flav_exc.FlavorServiceProfileBindingExists( sp_id=sp['id'], fl_id=flavor_id) obj_flavor.FlavorServiceProfileBinding( context, service_profile_id=sp['id'], flavor_id=flavor_id).create() fl_obj = self._get_flavor(context, flavor_id) return self._make_flavor_dict(fl_obj) def delete_flavor_service_profile(self, context, service_profile_id, flavor_id): if (obj_flavor.FlavorServiceProfileBinding.delete_objects( context, service_profile_id=service_profile_id, flavor_id=flavor_id) == 0): raise flav_exc.FlavorServiceProfileBindingNotFound( sp_id=service_profile_id, fl_id=flavor_id) @staticmethod def get_flavor_service_profile(context, service_profile_id, flavor_id, fields=None): if not obj_flavor.FlavorServiceProfileBinding.objects_exist( context, service_profile_id=service_profile_id, flavor_id=flavor_id): raise flav_exc.FlavorServiceProfileBindingNotFound( sp_id=service_profile_id, fl_id=flavor_id) res = {'service_profile_id': service_profile_id, 'flavor_id': flavor_id} return db_utils.resource_fields(res, fields) def create_service_profile(self, context, service_profile): sp = service_profile['service_profile'] if sp['driver']: self._validate_driver(context, sp['driver']) else: if not sp['metainfo']: raise flav_exc.ServiceProfileEmpty() obj = obj_flavor.ServiceProfile( context, description=sp['description'], driver=sp['driver'], enabled=sp['enabled'], metainfo=sp['metainfo']) obj.create() return self._make_service_profile_dict(obj) def update_service_profile(self, context, service_profile_id, service_profile): sp = service_profile['service_profile'] if sp.get('driver'): self._validate_driver(context, sp['driver']) with db_api.context_manager.writer.using(context): self._ensure_service_profile_not_in_use(context, service_profile_id) sp_obj = self._get_service_profile(context, service_profile_id) sp_obj.update_fields(sp) sp_obj.update() return self._make_service_profile_dict(sp_obj) def get_service_profile(self, context, sp_id, fields=None): sp_db = self._get_service_profile(context, sp_id) return self._make_service_profile_dict(sp_db, fields) def delete_service_profile(self, context, sp_id): with db_api.context_manager.writer.using(context): self._ensure_service_profile_not_in_use(context, sp_id) self._get_service_profile(context, sp_id).delete() def get_service_profiles(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): pager = base_obj.Pager(sorts, limit, page_reverse, marker) filters = filters or {} sp_objs = obj_flavor.ServiceProfile.get_objects(context, _pager=pager, **filters) return [self._make_service_profile_dict(sp_obj, fields) for sp_obj in sp_objs] def get_flavor_next_provider(self, context, flavor_id, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): """From flavor, choose service profile and find provider for driver.""" objs = obj_flavor.FlavorServiceProfileBinding.get_objects(context, flavor_id=flavor_id) if not objs: raise flav_exc.FlavorServiceProfileBindingNotFound( sp_id='', fl_id=flavor_id) # Get the service profile from the first binding # TODO(jwarendt) Should become a scheduling framework instead sp_obj = self._get_service_profile(context, objs[0].service_profile_id) if not sp_obj.enabled: raise flav_exc.ServiceProfileDisabled() LOG.debug("Found driver %s.", sp_obj.driver) service_type_manager = sdb.ServiceTypeManager.get_instance() providers = service_type_manager.get_service_providers( context, filters={'driver': sp_obj.driver}) if not providers: raise flav_exc.ServiceProfileDriverNotFound( driver=sp_obj.driver) LOG.debug("Found providers %s.", providers) res = {'driver': sp_obj.driver, 'provider': providers[0].get('name')} return [db_utils.resource_fields(res, fields)] neutron-12.1.1/neutron/db/network_ip_availability_db.py0000664000175000017500000001637013553660047023341 0ustar zuulzuul00000000000000# Copyright 2016 GoDaddy. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import netaddr import six from sqlalchemy import func from neutron.db import api as db_api import neutron.db.models_v2 as mod NETWORK_ID = 'network_id' NETWORK_NAME = 'network_name' SUBNET_ID = 'subnet_id' SUBNET_NAME = 'subnet_name' SUPPORTED_FILTERS = { NETWORK_ID: mod.Network.id, NETWORK_NAME: mod.Network.name, 'tenant_id': mod.Network.tenant_id, 'project_id': mod.Network.project_id, 'ip_version': mod.Subnet.ip_version, } SUPPORTED_FILTER_KEYS = six.viewkeys(SUPPORTED_FILTERS) class IpAvailabilityMixin(object): """Mixin class to query for IP availability.""" # Columns common to all queries common_columns = [ mod.Network.id.label(NETWORK_ID), mod.Subnet.id.label(SUBNET_ID), mod.Subnet.cidr, mod.Subnet.ip_version ] # Columns for the network/subnet and used_ip counts network_used_ips_columns = list(common_columns) network_used_ips_columns.append(mod.Network.name.label(NETWORK_NAME)) network_used_ips_columns.append(mod.Network.tenant_id) network_used_ips_columns.append(mod.Subnet.name.label(SUBNET_NAME)) # Aggregate query computed column network_used_ips_computed_columns = [ func.count(mod.IPAllocation.subnet_id).label('used_ips')] # Columns for total_ips query total_ips_columns = list(common_columns) total_ips_columns.append(mod.IPAllocationPool.first_ip) total_ips_columns.append(mod.IPAllocationPool.last_ip) @classmethod def get_network_ip_availabilities(cls, context, filters=None): """Get IP availability stats on a per subnet basis. Returns a list of network summaries which internally contains a list of subnet summaries. The used_ip and total_ip counts are returned at both levels. """ # Fetch total_ips by subnet subnet_total_ips_dict = cls._generate_subnet_total_ips_dict(context, filters) # Query network/subnet data along with used IP counts record_and_count_query = cls._build_network_used_ip_query(context, filters) # Assemble results result_dict = {} for row in record_and_count_query: cls._add_result(row, result_dict, subnet_total_ips_dict.get(row.subnet_id, 0)) # Convert result back into the list it expects net_ip_availabilities = list(six.viewvalues(result_dict)) return net_ip_availabilities @classmethod @db_api.context_manager.reader def _build_network_used_ip_query(cls, context, filters): # Generate a query to gather network/subnet/used_ips. # Ensure query is tolerant of missing child table data (outerjoins) # Process these outerjoin columns assuming their values may be None query = context.session.query() query = query.add_columns(*cls.network_used_ips_columns) query = query.add_columns(*cls.network_used_ips_computed_columns) query = query.outerjoin(mod.Subnet, mod.Network.id == mod.Subnet.network_id) query = query.outerjoin(mod.IPAllocation, mod.Subnet.id == mod.IPAllocation.subnet_id) query = query.group_by(*cls.network_used_ips_columns) return cls._adjust_query_for_filters(query, filters) @classmethod @db_api.context_manager.reader def _build_total_ips_query(cls, context, filters): query = context.session.query() query = query.add_columns(*cls.total_ips_columns) query = query.outerjoin(mod.Subnet, mod.Network.id == mod.Subnet.network_id) query = query.outerjoin( mod.IPAllocationPool, mod.Subnet.id == mod.IPAllocationPool.subnet_id) return cls._adjust_query_for_filters(query, filters) @classmethod def _generate_subnet_total_ips_dict(cls, context, filters): """Generates a dict whose key=subnet_id, value=total_ips in subnet""" # Query to get total_ips counts total_ips_query = cls._build_total_ips_query(context, filters) subnet_totals_dict = {} for row in total_ips_query: # Skip networks without subnets if not row.subnet_id: continue # Add IPAllocationPool data if row.last_ip: pool_total = netaddr.IPRange( netaddr.IPAddress(row.first_ip), netaddr.IPAddress(row.last_ip)).size cur_total = subnet_totals_dict.get(row.subnet_id, 0) subnet_totals_dict[row.subnet_id] = cur_total + pool_total else: subnet_totals_dict[row.subnet_id] = netaddr.IPNetwork( row.cidr, version=row.ip_version).size return subnet_totals_dict @classmethod def _adjust_query_for_filters(cls, query, filters): # The intersect of sets gets us applicable filter keys (others ignored) common_keys = six.viewkeys(filters) & SUPPORTED_FILTER_KEYS for key in common_keys: filter_vals = filters[key] if filter_vals: query = query.filter(SUPPORTED_FILTERS[key].in_(filter_vals)) return query @classmethod def _add_result(cls, db_row, result_dict, subnet_total_ips): # Find network in results. Create and add if missing if db_row.network_id in result_dict: network = result_dict[db_row.network_id] else: network = {NETWORK_ID: db_row.network_id, NETWORK_NAME: db_row.network_name, 'tenant_id': db_row.tenant_id, 'project_id': db_row.tenant_id, 'subnet_ip_availability': [], 'used_ips': 0, 'total_ips': 0} result_dict[db_row.network_id] = network # Only add subnet data if outerjoin rows have it if db_row.subnet_id: cls._add_subnet_data_to_net(db_row, network, subnet_total_ips) @classmethod def _add_subnet_data_to_net(cls, db_row, network_dict, subnet_total_ips): subnet = { SUBNET_ID: db_row.subnet_id, 'ip_version': db_row.ip_version, 'cidr': db_row.cidr, SUBNET_NAME: db_row.subnet_name, 'used_ips': db_row.used_ips if db_row.used_ips else 0, 'total_ips': subnet_total_ips } # Attach subnet result and rollup subnet sums into the parent network_dict['subnet_ip_availability'].append(subnet) network_dict['total_ips'] += subnet['total_ips'] network_dict['used_ips'] += subnet['used_ips'] neutron-12.1.1/neutron/common/0000775000175000017500000000000013553660156016304 5ustar zuulzuul00000000000000neutron-12.1.1/neutron/common/exceptions.py0000664000175000017500000003074313553660047021045 0ustar zuulzuul00000000000000# Copyright 2011 VMware, Inc # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions as e from neutron._i18n import _ class SubnetPoolNotFound(e.NotFound): message = _("Subnet pool %(subnetpool_id)s could not be found.") class QosPolicyNotFound(e.NotFound): message = _("QoS policy %(policy_id)s could not be found.") class QosRuleNotFound(e.NotFound): message = _("QoS rule %(rule_id)s for policy %(policy_id)s " "could not be found.") class QoSPolicyDefaultAlreadyExists(e.Conflict): message = _("A default QoS policy exists for project %(project_id)s.") class PortQosBindingNotFound(e.NotFound): message = _("QoS binding for port %(port_id)s and policy %(policy_id)s " "could not be found.") class PortQosBindingError(e.NeutronException): message = _("QoS binding for port %(port_id)s and policy %(policy_id)s " "could not be created: %(db_error)s.") class NetworkQosBindingNotFound(e.NotFound): message = _("QoS binding for network %(net_id)s and policy %(policy_id)s " "could not be found.") class FloatingIPQosBindingNotFound(e.NotFound): message = _("QoS binding for floating IP %(fip_id)s and policy " "%(policy_id)s could not be found.") class FloatingIPQosBindingError(e.NeutronException): message = _("QoS binding for floating IP %(fip_id)s and policy " "%(policy_id)s could not be created: %(db_error)s.") class NetworkQosBindingError(e.NeutronException): message = _("QoS binding for network %(net_id)s and policy %(policy_id)s " "could not be created: %(db_error)s.") class PlacementEndpointNotFound(e.NotFound): message = _("Placement API endpoint not found") class PlacementResourceProviderNotFound(e.NotFound): message = _("Placement resource provider not found %(resource_provider)s.") class PlacementInventoryNotFound(e.NotFound): message = _("Placement inventory not found for resource provider " "%(resource_provider)s, resource class %(resource_class)s.") class PlacementAggregateNotFound(e.NotFound): message = _("Aggregate not found for resource provider " "%(resource_provider)s.") class PolicyRemoveAuthorizationError(e.NotAuthorized): message = _("Failed to remove provided policy %(policy_id)s " "because you are not authorized.") class StateInvalid(e.BadRequest): message = _("Unsupported port state: %(port_state)s.") class QosPolicyInUse(e.InUse): message = _("QoS Policy %(policy_id)s is used by " "%(object_type)s %(object_id)s.") class DhcpPortInUse(e.InUse): message = _("Port %(port_id)s is already acquired by another DHCP agent") class HostRoutesExhausted(e.BadRequest): # NOTE(xchenum): probably make sense to use quota exceeded exception? message = _("Unable to complete operation for %(subnet_id)s. " "The number of host routes exceeds the limit %(quota)s.") class DNSNameServersExhausted(e.BadRequest): # NOTE(xchenum): probably make sense to use quota exceeded exception? message = _("Unable to complete operation for %(subnet_id)s. " "The number of DNS nameservers exceeds the limit %(quota)s.") class FlatNetworkInUse(e.InUse): message = _("Unable to create the flat network. " "Physical network %(physical_network)s is in use.") class TenantNetworksDisabled(e.ServiceUnavailable): # NOTE(vvargaszte): May be removed in the future as it is not used in # Neutron, only in the Neutron plugin of OpenContrail. message = _("Tenant network creation is not enabled.") class NoNetworkFoundInMaximumAllowedAttempts(e.ServiceUnavailable): message = _("Unable to create the network. " "No available network found in maximum allowed attempts.") class MalformedRequestBody(e.BadRequest): message = _("Malformed request body: %(reason)s.") class InvalidAllocationPool(e.BadRequest): message = _("The allocation pool %(pool)s is not valid.") class QosRuleNotSupported(e.Conflict): message = _("Rule %(rule_type)s is not supported by port %(port_id)s") class UnsupportedPortDeviceOwner(e.Conflict): message = _("Operation %(op)s is not supported for device_owner " "%(device_owner)s on port %(port_id)s.") class OverlappingAllocationPools(e.Conflict): message = _("Found overlapping allocation pools: " "%(pool_1)s %(pool_2)s for subnet %(subnet_cidr)s.") class PlacementInventoryUpdateConflict(e.Conflict): message = _("Placement inventory update conflict for resource provider " "%(resource_provider)s, resource class %(resource_class)s.") class OutOfBoundsAllocationPool(e.BadRequest): message = _("The allocation pool %(pool)s spans " "beyond the subnet cidr %(subnet_cidr)s.") class MacAddressGenerationFailure(e.ServiceUnavailable): message = _("Unable to generate unique mac on network %(net_id)s.") class BridgeDoesNotExist(e.NeutronException): message = _("Bridge %(bridge)s does not exist.") class QuotaResourceUnknown(e.NotFound): message = _("Unknown quota resources %(unknown)s.") class QuotaMissingTenant(e.BadRequest): message = _("Tenant-id was missing from quota request.") class InvalidQuotaValue(e.Conflict): message = _("Change would make usage less than 0 for the following " "resources: %(unders)s.") class InvalidSharedSetting(e.Conflict): message = _("Unable to reconfigure sharing settings for network " "%(network)s. Multiple tenants are using it.") class QoSRuleParameterConflict(e.Conflict): message = _("Unable to add the rule with value %(rule_value)s to the " "policy %(policy_id)s as the existing rule of type " "%(existing_rule)s restricts the bandwidth to " "%(existing_value)s.") class QoSRulesConflict(e.Conflict): message = _("Rule %(new_rule_type)s conflicts with " "rule %(rule_id)s which already exists in " "QoS Policy %(policy_id)s.") class ExtensionsNotFound(e.NotFound): message = _("Extensions not found: %(extensions)s.") class GatewayConflictWithAllocationPools(e.InUse): message = _("Gateway ip %(ip_address)s conflicts with " "allocation pool %(pool)s.") class GatewayIpInUse(e.InUse): message = _("Current gateway ip %(ip_address)s already in use " "by port %(port_id)s. Unable to update.") class NetworkVlanRangeError(e.NeutronException): message = _("Invalid network VLAN range: '%(vlan_range)s' - '%(error)s'.") def __init__(self, **kwargs): # Convert vlan_range tuple to 'start:end' format for display if isinstance(kwargs['vlan_range'], tuple): kwargs['vlan_range'] = "%d:%d" % kwargs['vlan_range'] super(NetworkVlanRangeError, self).__init__(**kwargs) class PhysicalNetworkNameError(e.NeutronException): message = _("Empty physical network name.") class NetworkVxlanPortRangeError(e.NeutronException): message = _("Invalid network VXLAN port range: '%(vxlan_range)s'.") class VxlanNetworkUnsupported(e.NeutronException): message = _("VXLAN network unsupported.") class DuplicatedExtension(e.NeutronException): message = _("Found duplicate extension: %(alias)s.") class DriverCallError(e.MultipleExceptions): def __init__(self, exc_list=None): super(DriverCallError, self).__init__(exc_list or []) class DeviceIDNotOwnedByTenant(e.Conflict): message = _("The following device_id %(device_id)s is not owned by your " "tenant or matches another tenants router.") class InvalidCIDR(e.BadRequest): message = _("Invalid CIDR %(input)s given as IP prefix.") class RouterNotCompatibleWithAgent(e.NeutronException): message = _("Router '%(router_id)s' is not compatible with this agent.") class FailToDropPrivilegesExit(SystemExit): """Exit exception raised when a drop privileges action fails.""" code = 99 class FloatingIpSetupException(e.NeutronException): def __init__(self, message=None): self.message = message super(FloatingIpSetupException, self).__init__() class IpTablesApplyException(e.NeutronException): def __init__(self, message=None): self.message = message super(IpTablesApplyException, self).__init__() class NetworkIdOrRouterIdRequiredError(e.NeutronException): message = _('Both network_id and router_id are None. ' 'One must be provided.') class AbortSyncRouters(e.NeutronException): message = _("Aborting periodic_sync_routers_task due to an error.") class EmptySubnetPoolPrefixList(e.BadRequest): message = _("Empty subnet pool prefix list.") class PrefixVersionMismatch(e.BadRequest): message = _("Cannot mix IPv4 and IPv6 prefixes in a subnet pool.") class UnsupportedMinSubnetPoolPrefix(e.BadRequest): message = _("Prefix '%(prefix)s' not supported in IPv%(version)s pool.") class IllegalSubnetPoolPrefixBounds(e.BadRequest): message = _("Illegal prefix bounds: %(prefix_type)s=%(prefixlen)s, " "%(base_prefix_type)s=%(base_prefixlen)s.") class IllegalSubnetPoolPrefixUpdate(e.BadRequest): message = _("Illegal update to prefixes: %(msg)s.") class SubnetAllocationError(e.NeutronException): message = _("Failed to allocate subnet: %(reason)s.") class AddressScopePrefixConflict(e.Conflict): message = _("Failed to associate address scope: subnetpools " "within an address scope must have unique prefixes.") class IllegalSubnetPoolAssociationToAddressScope(e.BadRequest): message = _("Illegal subnetpool association: subnetpool %(subnetpool_id)s " "cannot be associated with address scope " "%(address_scope_id)s.") class IllegalSubnetPoolIpVersionAssociationToAddressScope(e.BadRequest): message = _("Illegal subnetpool association: subnetpool %(subnetpool_id)s " "cannot associate with address scope %(address_scope_id)s " "because subnetpool ip_version is not %(ip_version)s.") class IllegalSubnetPoolUpdate(e.BadRequest): message = _("Illegal subnetpool update : %(reason)s.") class MinPrefixSubnetAllocationError(e.BadRequest): message = _("Unable to allocate subnet with prefix length %(prefixlen)s, " "minimum allowed prefix is %(min_prefixlen)s.") class MaxPrefixSubnetAllocationError(e.BadRequest): message = _("Unable to allocate subnet with prefix length %(prefixlen)s, " "maximum allowed prefix is %(max_prefixlen)s.") class SubnetPoolDeleteError(e.BadRequest): message = _("Unable to delete subnet pool: %(reason)s.") class SubnetPoolQuotaExceeded(e.OverQuota): message = _("Per-tenant subnet pool prefix quota exceeded.") class NetworkSubnetPoolAffinityError(e.BadRequest): message = _("Subnets hosted on the same network must be allocated from " "the same subnet pool.") class ObjectActionError(e.NeutronException): message = _('Object action %(action)s failed because: %(reason)s.') class CTZoneExhaustedError(e.NeutronException): message = _("IPtables conntrack zones exhausted, iptables rules cannot " "be applied.") class TenantQuotaNotFound(e.NotFound): message = _("Quota for tenant %(tenant_id)s could not be found.") class TenantIdProjectIdFilterConflict(e.BadRequest): message = _("Both tenant_id and project_id passed as filters.") class MultipleFilterIDForIPFound(e.Conflict): message = _("Multiple filter IDs for IP %(ip)s found.") class FilterIDForIPNotFound(e.NotFound): message = _("Filter ID for IP %(ip)s could not be found.") class FailedToAddQdiscToDevice(e.NeutronException): message = _("Failed to add %(direction)s qdisc " "to device %(device)s.") class ProcessExecutionError(RuntimeError): def __init__(self, message, returncode): super(ProcessExecutionError, self).__init__(message) self.returncode = returncode neutron-12.1.1/neutron/common/test_lib.py0000664000175000017500000000405313553660046020463 0ustar zuulzuul00000000000000# Copyright (c) 2010 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Colorizer Code is borrowed from Twisted: # Copyright (c) 2001-2010 Twisted Matrix Laboratories. # # 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. # describes parameters used by different unit/functional tests # a plugin-specific testing mechanism should import this dictionary # and override the values in it if needed (e.g., run_tests.py in # neutron/plugins/openvswitch/ ) test_config = {} neutron-12.1.1/neutron/common/eventlet_utils.py0000664000175000017500000000257713553660046021735 0ustar zuulzuul00000000000000# Copyright (c) 2015 Cloudbase Solutions. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import eventlet from oslo_utils import importutils def monkey_patch(): # NOTE(slaweq): to workaround issue with import cycles in # eventlet < 0.22.0; # This issue is fixed in eventlet with patch # https://github.com/eventlet/eventlet/commit/b756447bab51046dfc6f1e0e299cc997ab343701 # For details please check https://bugs.launchpad.net/neutron/+bug/1745013 eventlet.hubs.get_hub() if os.name != 'nt': eventlet.monkey_patch() p_c_e = importutils.import_module('pyroute2.config.asyncio') p_c_e.asyncio_config() else: # eventlet monkey patching the os module causes subprocess.Popen to # fail on Windows when using pipes due to missing non-blocking IO # support. eventlet.monkey_patch(os=False) neutron-12.1.1/neutron/common/utils.py0000664000175000017500000007166313553660047020032 0ustar zuulzuul00000000000000# Copyright 2011, VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Borrowed from nova code base, more utilities will be added/borrowed as and # when needed. """Utilities and helper functions.""" import functools import importlib import os import os.path import random import re import signal import sys import threading import time import uuid import weakref import eventlet from eventlet.green import subprocess import netaddr from neutron_lib import constants as n_const from neutron_lib.utils import helpers from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import log as logging from oslo_utils import excutils import pkg_resources import six import neutron from neutron._i18n import _ from neutron.db import api as db_api TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" LOG = logging.getLogger(__name__) DEFAULT_THROTTLER_VALUE = 2 _SEPARATOR_REGEX = re.compile(r'[/\\]+') class WaitTimeout(Exception): """Default exception coming from wait_until_true() function.""" class LockWithTimer(object): def __init__(self, threshold): self._threshold = threshold self.timestamp = 0 self._lock = threading.Lock() def acquire(self): return self._lock.acquire(False) def release(self): return self._lock.release() def time_to_wait(self): return self.timestamp - time.time() + self._threshold # REVISIT(jlibosva): Some parts of throttler may be similar to what # neutron.notifiers.batch_notifier.BatchNotifier does. They # could be refactored and unified. def throttler(threshold=DEFAULT_THROTTLER_VALUE): """Throttle number of calls to a function to only once per 'threshold'. """ def decorator(f): lock_with_timer = LockWithTimer(threshold) @functools.wraps(f) def wrapper(*args, **kwargs): if lock_with_timer.acquire(): try: fname = f.__name__ time_to_wait = lock_with_timer.time_to_wait() if time_to_wait > 0: LOG.debug("Call of function %s scheduled, sleeping " "%.1f seconds", fname, time_to_wait) # Decorated function has been called recently, wait. eventlet.sleep(time_to_wait) lock_with_timer.timestamp = time.time() finally: lock_with_timer.release() LOG.debug("Calling throttled function %s", fname) return f(*args, **kwargs) return wrapper return decorator def _subprocess_setup(): # Python installs a SIGPIPE handler by default. This is usually not what # non-Python subprocesses expect. signal.signal(signal.SIGPIPE, signal.SIG_DFL) def subprocess_popen(args, stdin=None, stdout=None, stderr=None, shell=False, env=None, preexec_fn=_subprocess_setup, close_fds=True): return subprocess.Popen(args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr, preexec_fn=preexec_fn, close_fds=close_fds, env=env) def get_first_host_ip(net, ip_version): return str(netaddr.IPAddress(net.first + 1, ip_version)) def is_extension_supported(plugin, ext_alias): return ext_alias in getattr( plugin, "supported_extension_aliases", []) def log_opt_values(log): cfg.CONF.log_opt_values(log, logging.DEBUG) def get_dhcp_agent_device_id(network_id, host): # Split host so as to always use only the hostname and # not the domain name. This will guarantee consistency # whether a local hostname or an fqdn is passed in. local_hostname = host.split('.')[0] host_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, str(local_hostname)) return 'dhcp%s-%s' % (host_uuid, network_id) class exception_logger(object): """Wrap a function and log raised exception :param logger: the logger to log the exception default is LOG.exception :returns: origin value if no exception raised; re-raise the exception if any occurred """ def __init__(self, logger=None): self.logger = logger def __call__(self, func): if self.logger is None: LOG = logging.getLogger(func.__module__) self.logger = LOG.exception def call(*args, **kwargs): try: return func(*args, **kwargs) except Exception as e: with excutils.save_and_reraise_exception(): self.logger(e) return call def get_other_dvr_serviced_device_owners(): """Return device_owner names for ports that should be serviced by DVR This doesn't return DEVICE_OWNER_COMPUTE_PREFIX since it is a prefix, not a complete device_owner name, so should be handled separately (see is_dvr_serviced() below) """ return [n_const.DEVICE_OWNER_LOADBALANCER, n_const.DEVICE_OWNER_LOADBALANCERV2, n_const.DEVICE_OWNER_DHCP] def get_dvr_allowed_address_pair_device_owners(): """Return device_owner names for allowed_addr_pair ports serviced by DVR This just returns the device owners that are used by the allowed_address_pair ports. Right now only the device_owners shown below are used by the allowed_address_pair ports. Later if other device owners are used for allowed_address_pairs those device_owners should be added to the list below. """ # TODO(Swami): Convert these methods to constants. # Add the constants variable to the neutron-lib return [n_const.DEVICE_OWNER_LOADBALANCER, n_const.DEVICE_OWNER_LOADBALANCERV2] def is_dvr_serviced(device_owner): """Check if the port need to be serviced by DVR Helper function to check the device owners of the ports in the compute and service node to make sure if they are required for DVR or any service directly or indirectly associated with DVR. """ return (device_owner.startswith(n_const.DEVICE_OWNER_COMPUTE_PREFIX) or device_owner in get_other_dvr_serviced_device_owners()) def is_fip_serviced(device_owner): """Check if the port can be assigned a floating IP Helper function to check the device owner of a port can be assigned a floating IP. """ return device_owner != n_const.DEVICE_OWNER_DHCP def ip_to_cidr(ip, prefix=None): """Convert an ip with no prefix to cidr notation :param ip: An ipv4 or ipv6 address. Convertable to netaddr.IPNetwork. :param prefix: Optional prefix. If None, the default 32 will be used for ipv4 and 128 for ipv6. """ net = netaddr.IPNetwork(ip) if prefix is not None: # Can't pass ip and prefix separately. Must concatenate strings. net = netaddr.IPNetwork(str(net.ip) + '/' + str(prefix)) return str(net) def cidr_to_ip(ip_cidr): """Strip the cidr notation from an ip cidr or ip :param ip_cidr: An ipv4 or ipv6 address, with or without cidr notation """ net = netaddr.IPNetwork(ip_cidr) return str(net.ip) def fixed_ip_cidrs(fixed_ips): """Create a list of a port's fixed IPs in cidr notation. :param fixed_ips: A neutron port's fixed_ips dictionary """ return [ip_to_cidr(fixed_ip['ip_address'], fixed_ip.get('prefixlen')) for fixed_ip in fixed_ips] def is_cidr_host(cidr): """Determines if the cidr passed in represents a single host network :param cidr: Either an ipv4 or ipv6 cidr. :returns: True if the cidr is /32 for ipv4 or /128 for ipv6. :raises ValueError: raises if cidr does not contain a '/'. This disallows plain IP addresses specifically to avoid ambiguity. """ if '/' not in str(cidr): raise ValueError(_("cidr doesn't contain a '/'")) net = netaddr.IPNetwork(cidr) if net.version == 4: return net.prefixlen == n_const.IPv4_BITS return net.prefixlen == n_const.IPv6_BITS def get_ip_version(ip_or_cidr): return netaddr.IPNetwork(ip_or_cidr).version def ip_version_from_int(ip_version_int): if ip_version_int == 4: return n_const.IPv4 if ip_version_int == 6: return n_const.IPv6 raise ValueError(_('Illegal IP version number')) def is_version_greater_equal(version1, version2): """Returns True if version1 is greater or equal than version2 else False""" return (pkg_resources.parse_version(version1) >= pkg_resources.parse_version(version2)) class DelayedStringRenderer(object): """Takes a callable and its args and calls when __str__ is called Useful for when an argument to a logging statement is expensive to create. This will prevent the callable from being called if it's never converted to a string. """ def __init__(self, function, *args, **kwargs): self.function = function self.args = args self.kwargs = kwargs def __str__(self): return str(self.function(*self.args, **self.kwargs)) def _hex_format(port, mask=0): def hex_str(num): return format(num, '#06x') if mask > 0: return "%s/%s" % (hex_str(port), hex_str(0xffff & ~mask)) return hex_str(port) def _gen_rules_port_min(port_min, top_bit): """ Encode a port range range(port_min, (port_min | (top_bit - 1)) + 1) into a set of bit value/masks. """ # Processing starts with setting up mask and top_bit variables to their # maximum. Top_bit has the form (1000000) with '1' pointing to the register # being processed, while mask has the form (0111111) with '1' showing # possible range to be covered. # With each rule generation cycle, mask and top_bit are bit shifted to the # right. When top_bit reaches 0 it means that last register was processed. # Let port_min be n bits long, top_bit = 1 << k, 0<=k<=n-1. # Each cycle step checks the following conditions: # 1). port & mask == 0 # This means that remaining bits k..1 are equal to '0' and can be # covered by a single port/mask rule. # If condition 1 doesn't fit, then both top_bit and mask are bit # shifted to the right and condition 2 is checked: # 2). port & top_bit == 0 # This means that kth port bit is equal to '0'. By setting it to '1' # and masking other (k-1) bits all ports in range # [P, P + 2^(k-1)-1] are guaranteed to be covered. # Let p_k be equal to port first (n-k) bits with rest set to 0. # Then P = p_k | top_bit. # Correctness proof: # The remaining range to be encoded in a cycle is calculated as follows: # R = [port_min, port_min | mask]. # If condition 1 holds, then a rule that covers R is generated and the job # is done. # If condition 2 holds, then the rule emitted will cover 2^(k-1) values # from the range. Remaining range R will shrink by 2^(k-1). # If condition 2 doesn't hold, then even after top_bit/mask shift in next # iteration the value of R won't change. # Full cycle example for range [40, 64): # port=0101000, top_bit=1000000, k=6 # * step 1, k=6, R=[40, 63] # top_bit=1000000, mask=0111111 -> condition 1 doesn't hold, shifting # mask/top_bit # top_bit=0100000, mask=0011111 -> condition 2 doesn't hold # * step 2, k=5, R=[40, 63] # top_bit=0100000, mask=0011111 -> condition 1 doesn't hold, shifting # mask/top_bit # top_bit=0010000, mask=0001111 -> condition 2 holds -> 011xxxx or # 0x0030/fff0 # * step 3, k=4, R=[40, 47] # top_bit=0010000, mask=0001111 -> condition 1 doesn't hold, shifting # mask/top_bit # top_bit=0001000, mask=0000111 -> condition 2 doesn't hold # * step 4, k=3, R=[40, 47] # top_bit=0001000, mask=0000111 -> condition 1 holds -> 0101xxx or # 0x0028/fff8 # rules=[0x0030/fff0, 0x0028/fff8] rules = [] mask = top_bit - 1 while True: if (port_min & mask) == 0: # greedy matched a streak of '0' in port_min rules.append(_hex_format(port_min, mask)) break top_bit >>= 1 mask >>= 1 if (port_min & top_bit) == 0: # matched next '0' in port_min to substitute for '1' in resulting # rule rules.append(_hex_format(port_min & ~mask | top_bit, mask)) return rules def _gen_rules_port_max(port_max, top_bit): """ Encode a port range range(port_max & ~(top_bit - 1), port_max + 1) into a set of bit value/masks. """ # Processing starts with setting up mask and top_bit variables to their # maximum. Top_bit has the form (1000000) with '1' pointing to the register # being processed, while mask has the form (0111111) with '1' showing # possible range to be covered. # With each rule generation cycle, mask and top_bit are bit shifted to the # right. When top_bit reaches 0 it means that last register was processed. # Let port_max be n bits long, top_bit = 1 << k, 0<=k<=n-1. # Each cycle step checks the following conditions: # 1). port & mask == mask # This means that remaining bits k..1 are equal to '1' and can be # covered by a single port/mask rule. # If condition 1 doesn't fit, then both top_bit and mask are bit # shifted to the right and condition 2 is checked: # 2). port & top_bit == top_bit # This means that kth port bit is equal to '1'. By setting it to '0' # and masking other (k-1) bits all ports in range # [P, P + 2^(k-1)-1] are guaranteed to be covered. # Let p_k be equal to port first (n-k) bits with rest set to 0. # Then P = p_k | ~top_bit. # Correctness proof: # The remaining range to be encoded in a cycle is calculated as follows: # R = [port_max & ~mask, port_max]. # If condition 1 holds, then a rule that covers R is generated and the job # is done. # If condition 2 holds, then the rule emitted will cover 2^(k-1) values # from the range. Remaining range R will shrink by 2^(k-1). # If condition 2 doesn't hold, then even after top_bit/mask shift in next # iteration the value of R won't change. # Full cycle example for range [64, 105]: # port=1101001, top_bit=1000000, k=6 # * step 1, k=6, R=[64, 105] # top_bit=1000000, mask=0111111 -> condition 1 doesn't hold, shifting # mask/top_bit # top_bit=0100000, mask=0011111 -> condition 2 holds -> 10xxxxx or # 0x0040/ffe0 # * step 2, k=5, R=[96, 105] # top_bit=0100000, mask=0011111 -> condition 1 doesn't hold, shifting # mask/top_bit # top_bit=0010000, mask=0001111 -> condition 2 doesn't hold # * step 3, k=4, R=[96, 105] # top_bit=0010000, mask=0001111 -> condition 1 doesn't hold, shifting # mask/top_bit # top_bit=0001000, mask=0000111 -> condition 2 holds -> 1100xxx or # 0x0060/fff8 # * step 4, k=3, R=[104, 105] # top_bit=0001000, mask=0000111 -> condition 1 doesn't hold, shifting # mask/top_bit # top_bit=0000100, mask=0000011 -> condition 2 doesn't hold # * step 5, k=2, R=[104, 105] # top_bit=0000100, mask=0000011 -> condition 1 doesn't hold, shifting # mask/top_bit # top_bit=0000010, mask=0000001 -> condition 2 doesn't hold # * step 6, k=1, R=[104, 105] # top_bit=0000010, mask=0000001 -> condition 1 holds -> 1101001 or # 0x0068 # rules=[0x0040/ffe0, 0x0060/fff8, 0x0068] rules = [] mask = top_bit - 1 while True: if (port_max & mask) == mask: # greedy matched a streak of '1' in port_max rules.append(_hex_format(port_max & ~mask, mask)) break top_bit >>= 1 mask >>= 1 if (port_max & top_bit) == top_bit: # matched next '1' in port_max to substitute for '0' in resulting # rule rules.append(_hex_format(port_max & ~mask & ~top_bit, mask)) return rules def port_rule_masking(port_min, port_max): """Translate a range [port_min, port_max] into a set of bitwise matches. Each match has the form 'port/mask'. The port and mask are 16-bit numbers written in hexadecimal prefixed by 0x. Each 1-bit in mask requires that the corresponding bit in port must match. Each 0-bit in mask causes the corresponding bit to be ignored. """ # Let binary representation of port_min and port_max be n bits long and # have first m bits in common, 0 <= m <= n. # If remaining (n - m) bits of given ports define 2^(n-m) values, then # [port_min, port_max] range is covered by a single rule. # For example: # n = 6 # port_min = 16 (binary 010000) # port_max = 23 (binary 010111) # Ports have m=3 bits in common with the remaining (n-m)=3 bits # covering range [0, 2^3), which equals to a single 010xxx rule. The algo # will return [0x0010/fff8]. # Else [port_min, port_max] range will be split into 2: range [port_min, T) # and [T, port_max]. Let p_m be the common part of port_min and port_max # with other (n-m) bits set to 0. Then T = p_m | 1 << (n-m-1). # For example: # n = 7 # port_min = 40 (binary 0101000) # port_max = 105 (binary 1101001) # Ports have m=0 bits in common, p_m=000000. Then T=1000000 and the # initial range [40, 105] is divided into [40, 64) and [64, 105]. # Each of the ranges will be processed separately, then the generated rules # will be merged. # Check port_max >= port_min. if port_max < port_min: raise ValueError(_("'port_max' is smaller than 'port_min'")) bitdiff = port_min ^ port_max if bitdiff == 0: # port_min == port_max return [_hex_format(port_min)] # for python3.x, bit_length could be used here top_bit = 1 while top_bit <= bitdiff: top_bit <<= 1 if (port_min & (top_bit - 1) == 0 and port_max & (top_bit - 1) == top_bit - 1): # special case, range of 2^k ports is covered return [_hex_format(port_min, top_bit - 1)] top_bit >>= 1 rules = [] rules.extend(_gen_rules_port_min(port_min, top_bit)) rules.extend(_gen_rules_port_max(port_max, top_bit)) return rules def create_object_with_dependency(creator, dep_getter, dep_creator, dep_id_attr, dep_deleter): """Creates an object that binds to a dependency while handling races. creator is a function that expected to take the result of either dep_getter or dep_creator. The result of dep_getter and dep_creator must have an attribute of dep_id_attr be used to determine if the dependency changed during object creation. dep_deleter will be called with a the result of dep_creator if the creator function fails due to a non-dependency reason or the retries are exceeded. dep_getter should return None if the dependency does not exist. dep_creator can raise a DBDuplicateEntry to indicate that a concurrent create of the dependency occurred and the process will restart to get the concurrently created one. This function will return both the created object and the dependency it used/created. This function protects against all of the cases where the dependency can be concurrently removed by catching exceptions and restarting the process of creating the dependency if one no longer exists. It will give up after neutron.db.api.MAX_RETRIES and raise the exception it encounters after that. """ result, dependency, dep_id, made_locally = None, None, None, False for attempts in range(1, db_api.MAX_RETRIES + 1): # we go to max + 1 here so the exception handlers can raise their # errors at the end try: dependency = dep_getter() if not dependency: dependency = dep_creator() made_locally = True dep_id = getattr(dependency, dep_id_attr) except db_exc.DBDuplicateEntry: # dependency was concurrently created. with excutils.save_and_reraise_exception() as ctx: if attempts < db_api.MAX_RETRIES: # sleep for a random time between 0 and 1 second to # make sure a concurrent worker doesn't retry again # at exactly the same time time.sleep(random.uniform(0, 1)) ctx.reraise = False continue try: result = creator(dependency) break except Exception: with excutils.save_and_reraise_exception() as ctx: # check if dependency we tried to use was removed during # object creation if attempts < db_api.MAX_RETRIES: dependency = dep_getter() if not dependency or dep_id != getattr(dependency, dep_id_attr): ctx.reraise = False continue # we have exceeded retries or have encountered a non-dependency # related failure so we try to clean up the dependency if we # created it before re-raising if made_locally and dependency: try: dep_deleter(dependency) except Exception: LOG.exception("Failed cleaning up dependency %s", dep_id) return result, dependency def transaction_guard(f): """Ensures that the context passed in is not in a transaction. Various Neutron methods modifying resources have assumptions that they will not be called inside of a transaction because they perform operations that expect all data to be committed to the database (e.g. ML2 postcommit calls) and/or they have side effects on external systems. So calling them in a transaction can lead to consistency errors on failures since the side effect will not be reverted on a DB rollback. If you receive this error, you must alter your code to handle the fact that the thing you are calling can have side effects so using transactions to undo on failures is not possible. """ @functools.wraps(f) def inner(self, context, *args, **kwargs): # FIXME(kevinbenton): get rid of all uses of this flag if (context.session.is_active and getattr(context, 'GUARD_TRANSACTION', True)): raise RuntimeError(_("Method %s cannot be called within a " "transaction.") % f) return f(self, context, *args, **kwargs) return inner def wait_until_true(predicate, timeout=60, sleep=1, exception=None): """ Wait until callable predicate is evaluated as True :param predicate: Callable deciding whether waiting should continue. Best practice is to instantiate predicate with functools.partial() :param timeout: Timeout in seconds how long should function wait. :param sleep: Polling interval for results in seconds. :param exception: Exception instance to raise on timeout. If None is passed (default) then WaitTimeout exception is raised. """ try: with eventlet.Timeout(timeout): while not predicate(): eventlet.sleep(sleep) except eventlet.Timeout: if exception is not None: #pylint: disable=raising-bad-type raise exception raise WaitTimeout("Timed out after %d seconds" % timeout) class _AuthenticBase(object): def __init__(self, addr, **kwargs): super(_AuthenticBase, self).__init__(addr, **kwargs) self._initial_value = addr def __str__(self): if isinstance(self._initial_value, six.string_types): return self._initial_value return super(_AuthenticBase, self).__str__() # NOTE(ihrachys): override deepcopy because netaddr.* classes are # slot-based and hence would not copy _initial_value def __deepcopy__(self, memo): return self.__class__(self._initial_value) class AuthenticEUI(_AuthenticBase, netaddr.EUI): ''' This class retains the format of the MAC address string passed during initialization. This is useful when we want to make sure that we retain the format passed by a user through API. ''' class AuthenticIPNetwork(_AuthenticBase, netaddr.IPNetwork): ''' This class retains the format of the IP network string passed during initialization. This is useful when we want to make sure that we retain the format passed by a user through API. ''' class classproperty(object): def __init__(self, f): self.func = f def __get__(self, obj, owner): return self.func(owner) _NO_ARGS_MARKER = object() def attach_exc_details(e, msg, args=_NO_ARGS_MARKER): e._error_context_msg = msg e._error_context_args = args def extract_exc_details(e): for attr in ('_error_context_msg', '_error_context_args'): if not hasattr(e, attr): return u'No details.' details = e._error_context_msg args = e._error_context_args if args is _NO_ARGS_MARKER: return details return details % args def import_modules_recursively(topdir): '''Import and return all modules below the topdir directory.''' topdir = _SEPARATOR_REGEX.sub('/', topdir) modules = [] for root, dirs, files in os.walk(topdir): for file_ in files: if file_[-3:] != '.py': continue module = file_[:-3] if module == '__init__': continue import_base = _SEPARATOR_REGEX.sub('.', root) # NOTE(ihrachys): in Python3, or when we are not located in the # directory containing neutron code, __file__ is absolute, so we # should truncate it to exclude PYTHONPATH prefix prefixlen = len(os.path.dirname(neutron.__file__)) import_base = 'neutron' + import_base[prefixlen:] module = '.'.join([import_base, module]) if module not in sys.modules: importlib.import_module(module) modules.append(module) return modules def get_rand_name(max_length=None, prefix='test'): """Return a random string. The string will start with 'prefix' and will be exactly 'max_length'. If 'max_length' is None, then exactly 8 random characters, each hexadecimal, will be added. In case len(prefix) <= len(max_length), ValueError will be raised to indicate the problem. """ return get_related_rand_names([prefix], max_length)[0] def get_rand_device_name(prefix='test'): return get_rand_name( max_length=n_const.DEVICE_NAME_MAX_LEN, prefix=prefix) def get_related_rand_names(prefixes, max_length=None): """Returns a list of the prefixes with the same random characters appended :param prefixes: A list of prefix strings :param max_length: The maximum length of each returned string :returns: A list with each prefix appended with the same random characters """ if max_length: length = max_length - max(len(p) for p in prefixes) if length <= 0: raise ValueError( _("'max_length' must be longer than all prefixes")) else: length = 8 rndchrs = helpers.get_random_string(length) return [p + rndchrs for p in prefixes] def get_related_rand_device_names(prefixes): return get_related_rand_names(prefixes, max_length=n_const.DEVICE_NAME_MAX_LEN) try: # PY3 weak_method = weakref.WeakMethod except AttributeError: # PY2 import weakrefmethod weak_method = weakrefmethod.WeakMethod def make_weak_ref(f): """Make a weak reference to a function accounting for bound methods.""" return weak_method(f) if hasattr(f, '__self__') else weakref.ref(f) def resolve_ref(ref): """Handles dereference of weakref.""" if isinstance(ref, weakref.ref): ref = ref() return ref def bytes_to_bits(value): return value * 8 def bits_to_kilobits(value, base): #NOTE(slaweq): round up that even 1 bit will give 1 kbit as a result return int((value + (base - 1)) / base) neutron-12.1.1/neutron/common/constants.py0000664000175000017500000002316313553660047020676 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib import constants as lib_constants ROUTER_PORT_OWNERS = lib_constants.ROUTER_INTERFACE_OWNERS_SNAT + \ (lib_constants.DEVICE_OWNER_ROUTER_GW,) ROUTER_STATUS_ACTIVE = 'ACTIVE' ROUTER_STATUS_ALLOCATING = 'ALLOCATING' ROUTER_STATUS_ERROR = 'ERROR' VALID_ROUTER_STATUS = (ROUTER_STATUS_ACTIVE, ROUTER_STATUS_ALLOCATING, ROUTER_STATUS_ERROR) HA_ROUTER_STATE_KEY = '_ha_state' METERING_LABEL_KEY = '_metering_labels' FLOATINGIP_AGENT_INTF_KEY = '_floatingip_agent_interfaces' SNAT_ROUTER_INTF_KEY = '_snat_router_interfaces' DVR_SNAT_BOUND = 'dvr_snat_bound' L3_AGENT_MODE_DVR_NO_EXTERNAL = 'dvr_no_external' HA_NETWORK_NAME = 'HA network tenant %s' HA_SUBNET_NAME = 'HA subnet tenant %s' HA_PORT_NAME = 'HA port tenant %s' HA_ROUTER_STATE_ACTIVE = 'active' HA_ROUTER_STATE_STANDBY = 'standby' VALID_HA_STATES = (HA_ROUTER_STATE_ACTIVE, HA_ROUTER_STATE_STANDBY) PAGINATION_INFINITE = 'infinite' SORT_DIRECTION_ASC = 'asc' SORT_DIRECTION_DESC = 'desc' ETHERTYPE_NAME_ARP = 'arp' ETHERTYPE_ARP = 0x0806 ETHERTYPE_IP = 0x0800 ETHERTYPE_IPV6 = 0x86DD IP_PROTOCOL_NAME_ALIASES = {lib_constants.PROTO_NAME_IPV6_ICMP_LEGACY: lib_constants.PROTO_NAME_IPV6_ICMP} IP_PROTOCOL_NUM_TO_NAME_MAP = { str(v): k for k, v in lib_constants.IP_PROTOCOL_MAP.items()} # When using iptables-save we specify '-p {proto}', # but sometimes those values are not identical. This is a map # of known protocol numbers that require a name to be used and # protocol names that require a different name to be used, # because that is how iptables-save will display them. # # This is how the list was created, so there is a possibility # it will need to be updated in the future: # # $ for num in {0..255}; do iptables -A INPUT -p $num; done # $ iptables-save # # These cases are special, and were found by inspection: # - 'ipv6-encap' uses 'ipv6' # - 'icmpv6' uses 'ipv6-icmp' # - 'pgm' uses '113' instead of its name # - protocol '0' uses no -p argument IPTABLES_PROTOCOL_NAME_MAP = {lib_constants.PROTO_NAME_IPV6_ENCAP: 'ipv6', lib_constants.PROTO_NAME_IPV6_ICMP_LEGACY: 'ipv6-icmp', lib_constants.PROTO_NAME_PGM: '113', '0': None, '1': 'icmp', '2': 'igmp', '3': 'ggp', '4': 'ipencap', '5': 'st', '6': 'tcp', '8': 'egp', '9': 'igp', '12': 'pup', '17': 'udp', '20': 'hmp', '22': 'xns-idp', '27': 'rdp', '29': 'iso-tp4', '33': 'dccp', '36': 'xtp', '37': 'ddp', '38': 'idpr-cmtp', '41': 'ipv6', '43': 'ipv6-route', '44': 'ipv6-frag', '45': 'idrp', '46': 'rsvp', '47': 'gre', '50': 'esp', '51': 'ah', '57': 'skip', '58': 'ipv6-icmp', '59': 'ipv6-nonxt', '60': 'ipv6-opts', '73': 'rspf', '81': 'vmtp', '88': 'eigrp', '89': 'ospf', '93': 'ax.25', '94': 'ipip', '97': 'etherip', '98': 'encap', '103': 'pim', '108': 'ipcomp', '112': 'vrrp', '115': 'l2tp', '124': 'isis', '132': 'sctp', '133': 'fc', '135': 'mobility-header', '136': 'udplite', '137': 'mpls-in-ip', '138': 'manet', '139': 'hip', '140': 'shim6', '141': 'wesp', '142': 'rohc'} # Timeout in seconds for getting an IPv6 LLA LLA_TASK_TIMEOUT = 40 # length of all device prefixes (e.g. qvo, tap, qvb) LINUX_DEV_PREFIX_LEN = 3 # must be shorter than linux IFNAMSIZ (which is 16) LINUX_DEV_LEN = 14 # Possible prefixes to partial port IDs in interface names used by the OVS, # Linux Bridge, and IVS VIF drivers in Nova and the neutron agents. See the # 'get_ovs_interfaceid' method in Nova (nova/virt/libvirt/vif.py) for details. INTERFACE_PREFIXES = (lib_constants.TAP_DEVICE_PREFIX, lib_constants.VETH_DEVICE_PREFIX, lib_constants.SNAT_INT_DEV_PREFIX) ATTRIBUTES_TO_UPDATE = 'attributes_to_update' # TODO(amuller): Re-define the RPC namespaces once Oslo messaging supports # Targets with multiple namespaces. Neutron will then implement callbacks # for its RPC clients in order to support rolling upgrades. # RPC Interface for agents to call DHCP API implemented on the plugin side RPC_NAMESPACE_DHCP_PLUGIN = None # RPC interface for the metadata service to get info from the plugin side RPC_NAMESPACE_METADATA = None # RPC interface for agent to plugin security group API RPC_NAMESPACE_SECGROUP = None # RPC interface for agent to plugin DVR api RPC_NAMESPACE_DVR = None # RPC interface for reporting state back to the plugin RPC_NAMESPACE_STATE = None # RPC interface for agent to plugin resources API RPC_NAMESPACE_RESOURCES = None # Default network MTU value when not configured DEFAULT_NETWORK_MTU = 1500 IPV6_MIN_MTU = 1280 ROUTER_MARK_MASK = "0xffff" VALID_ETHERTYPES = (lib_constants.IPv4, lib_constants.IPv6) IP_ALLOWED_VERSIONS = [lib_constants.IP_VERSION_4, lib_constants.IP_VERSION_6] PORT_RANGE_MIN = 1 PORT_RANGE_MAX = 65535 # TODO(bence romsics): move this to neutron_lib.constants DHCPV6_CLIENT_PORT = 546 # Configuration values for accept_ra sysctl, copied from linux kernel # networking (netdev) tree, file Documentation/networking/ip-sysctl.txt # # Possible values are: # 0 Do not accept Router Advertisements. # 1 Accept Router Advertisements if forwarding is disabled. # 2 Overrule forwarding behaviour. Accept Router Advertisements # even if forwarding is enabled. ACCEPT_RA_DISABLED = 0 ACCEPT_RA_WITHOUT_FORWARDING = 1 ACCEPT_RA_WITH_FORWARDING = 2 # Some components communicate using private address ranges, define # them all here. These address ranges should not cause any issues # even if they overlap since they are used in disjoint namespaces, # but for now they are unique. # We define the metadata cidr since it falls in the range. PRIVATE_CIDR_RANGE = '169.254.0.0/16' DVR_FIP_LL_CIDR = '169.254.64.0/18' L3_HA_NET_CIDR = '169.254.192.0/18' METADATA_CIDR = '169.254.169.254/32' # The only defined IpamAllocation status at this stage is 'ALLOCATED'. # More states will be available in the future - e.g.: RECYCLABLE IPAM_ALLOCATION_STATUS_ALLOCATED = 'ALLOCATED' VALID_IPAM_ALLOCATION_STATUSES = (IPAM_ALLOCATION_STATUS_ALLOCATED,) # Port binding states for Live Migration PORT_BINDING_STATUSES = (lib_constants.ACTIVE, lib_constants.INACTIVE) VALID_FLOATINGIP_STATUS = (lib_constants.FLOATINGIP_STATUS_ACTIVE, lib_constants.FLOATINGIP_STATUS_DOWN, lib_constants.FLOATINGIP_STATUS_ERROR) # Floating IP host binding states FLOATING_IP_HOST_UNBOUND = "FLOATING_IP_HOST_UNBOUND" FLOATING_IP_HOST_NEEDS_BINDING = "FLOATING_IP_HOST_NEEDS_BINDING" # Possible types of values (e.g. in QoS rule types) VALUES_TYPE_CHOICES = "choices" VALUES_TYPE_RANGE = "range" # Units base SI_BASE = 1000 IEC_BASE = 1024 # Number of resources for neutron agent side functions to deal # with large sets. # Setting this value does not count on special conditions, it is just a human # countable or scalable number. [1] gives us the method to test the scale # issue. And we have tested the value of 1000, 500, 200, 100. But for 100, # ovs-agent will have a lower timeout probability. And according to the # testing result, step size 100 can indeed cost about 10% much more time # than 500/1000. But such extra time looks inevitably needed to be sacrificed # for the restart success rate. # [1] http://paste.openstack.org/show/745685/ AGENT_RES_PROCESSING_STEP = 100 # Number of resources for neutron to divide the large RPC # call data sets. RPC_RES_PROCESSING_STEP = 20 # IPtables version to support --random-fully option. # Do not move this constant to neutron-lib, since it is temporary IPTABLES_RANDOM_FULLY_VERSION = '1.6.2' neutron-12.1.1/neutron/common/config.py0000664000175000017500000001125013553660047020121 0ustar zuulzuul00000000000000# Copyright 2011 VMware, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Routines for configuring Neutron """ import sys from keystoneauth1 import loading as ks_loading from neutron_lib.api import validators from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_middleware import cors from oslo_service import wsgi from neutron._i18n import _ from neutron.conf import common as common_config from neutron import policy from neutron import version LOG = logging.getLogger(__name__) # Jam here any extra log level default you care about. This helps keep # Neutron logs lean. EXTRA_LOG_LEVEL_DEFAULTS = [ 'OFPHandler=INFO', 'OfctlService=INFO', 'ryu.base.app_manager=INFO', 'ryu.controller.controller=INFO', 'ovsdbapp.backend.ovs_idl.vlog=INFO' ] # Register the configuration options common_config.register_core_common_config_opts() # Ensure that the control exchange is set correctly oslo_messaging.set_transport_defaults(control_exchange='neutron') NOVA_CONF_SECTION = 'nova' ks_loading.register_auth_conf_options(cfg.CONF, NOVA_CONF_SECTION) ks_loading.register_session_conf_options(cfg.CONF, NOVA_CONF_SECTION) # Register the nova configuration options common_config.register_nova_opts() ks_loading.register_auth_conf_options(cfg.CONF, common_config.PLACEMENT_CONF_SECTION) ks_loading.register_session_conf_options(cfg.CONF, common_config.PLACEMENT_CONF_SECTION) # Register the placement configuration options common_config.register_placement_opts() logging.register_options(cfg.CONF) def init(args, **kwargs): cfg.CONF(args=args, project='neutron', version='%%(prog)s %s' % version.version_info.release_string(), **kwargs) # FIXME(ihrachys): if import is put in global, circular import # failure occurs from neutron.common import rpc as n_rpc n_rpc.init(cfg.CONF) # Validate that the base_mac is of the correct format msg = validators.validate_regex(cfg.CONF.base_mac, validators.MAC_PATTERN) if msg: msg = _("Base MAC: %s") % msg raise Exception(msg) def setup_logging(): """Sets up the logging options for a log with supplied name.""" product_name = "neutron" # We use the oslo.log default log levels and add only the extra levels # that Neutron needs. logging.set_defaults(default_log_levels=logging.get_default_log_levels() + EXTRA_LOG_LEVEL_DEFAULTS) logging.setup(cfg.CONF, product_name) LOG.info("Logging enabled!") LOG.info("%(prog)s version %(version)s", {'prog': sys.argv[0], 'version': version.version_info.release_string()}) LOG.debug("command line: %s", " ".join(sys.argv)) def reset_service(): # Reset worker in case SIGHUP is called. # Note that this is called only in case a service is running in # daemon mode. setup_logging() set_config_defaults() policy.refresh() def load_paste_app(app_name): """Builds and returns a WSGI app from a paste config file. :param app_name: Name of the application to load """ loader = wsgi.Loader(cfg.CONF) app = loader.load_app(app_name) return app def set_config_defaults(): """This method updates all configuration default values.""" set_cors_middleware_defaults() def set_cors_middleware_defaults(): """Update default configuration options for oslo.middleware.""" cors.set_defaults( allow_headers=['X-Auth-Token', 'X-Identity-Status', 'X-Roles', 'X-Service-Catalog', 'X-User-Id', 'X-Tenant-Id', 'X-OpenStack-Request-ID'], expose_headers=['X-Auth-Token', 'X-Subject-Token', 'X-Service-Token', 'X-OpenStack-Request-ID', 'OpenStack-Volume-microversion'], allow_methods=['GET', 'PUT', 'POST', 'DELETE', 'PATCH'] ) neutron-12.1.1/neutron/common/cache_utils.py0000664000175000017500000001020313553660046021133 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import functools from neutron_lib.utils import helpers from oslo_cache import core as cache from oslo_config import cfg from oslo_log import log as logging from oslo_utils import reflection from neutron._i18n import _ LOG = logging.getLogger(__name__) def register_oslo_configs(conf): cache.configure(conf) def get_cache(conf): """Used to get cache client""" if conf.cache.enabled: return _get_cache_region(conf) else: return False def _get_cache_region(conf): region = cache.create_region() cache.configure_cache_region(conf, region) return region def _get_memory_cache_region(expiration_time=5): conf = cfg.ConfigOpts() register_oslo_configs(conf) cache_conf_dict = { 'enabled': True, 'backend': 'oslo_cache.dict', 'expiration_time': expiration_time, } for k, v in cache_conf_dict.items(): conf.set_override(k, v, group='cache') return _get_cache_region(conf) class cache_method_results(object): """This decorator is intended for object methods only.""" def __init__(self, func): self.func = func functools.update_wrapper(self, func) self._first_call = True self._not_cached = cache.NO_VALUE def _get_from_cache(self, target_self, *args, **kwargs): target_self_cls_name = reflection.get_class_name(target_self, fully_qualified=False) func_name = "%(module)s.%(class)s.%(func_name)s" % { 'module': target_self.__module__, 'class': target_self_cls_name, 'func_name': self.func.__name__, } key = (func_name,) + args if kwargs: key += helpers.dict2tuple(kwargs) # oslo.cache expects a string or a buffer key = str(key) try: item = target_self._cache.get(key) except TypeError: LOG.debug("Method %(func_name)s cannot be cached due to " "unhashable parameters: args: %(args)s, kwargs: " "%(kwargs)s", {'func_name': func_name, 'args': args, 'kwargs': kwargs}) return self.func(target_self, *args, **kwargs) if item is self._not_cached: item = self.func(target_self, *args, **kwargs) target_self._cache.set(key, item) return item def __call__(self, target_self, *args, **kwargs): target_self_cls_name = reflection.get_class_name(target_self, fully_qualified=False) if not hasattr(target_self, '_cache'): raise NotImplementedError( _("Instance of class %(module)s.%(class)s must contain _cache " "attribute") % { 'module': target_self.__module__, 'class': target_self_cls_name}) if not target_self._cache: if self._first_call: LOG.debug("Instance of class %(module)s.%(class)s doesn't " "contain attribute _cache therefore results " "cannot be cached for %(func_name)s.", {'module': target_self.__module__, 'class': target_self_cls_name, 'func_name': self.func.__name__}) self._first_call = False return self.func(target_self, *args, **kwargs) return self._get_from_cache(target_self, *args, **kwargs) def __get__(self, obj, objtype): return functools.partial(self.__call__, obj) neutron-12.1.1/neutron/common/profiler.py0000664000175000017500000000361613553660046020504 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import context from oslo_config import cfg from oslo_log import log as logging import osprofiler.initializer from osprofiler import opts as profiler_opts import osprofiler.web CONF = cfg.CONF profiler_opts.set_defaults(CONF) LOG = logging.getLogger(__name__) def setup(name, host='0.0.0.0'): # nosec """Setup OSprofiler notifier and enable profiling. :param name: name of the service, that will be profiled :param host: host (either host name or host address) the service will be running on. By default host will be set to 0.0.0.0, but more specified host name / address usage is highly recommended. """ if CONF.profiler.enabled: osprofiler.initializer.init_from_conf( conf=CONF, context=context.get_admin_context().to_dict(), project="neutron", service=name, host=host ) LOG.info("OSProfiler is enabled.\n" "Traces provided from the profiler " "can only be subscribed to using the same HMAC keys that " "are configured in Neutron's configuration file " "under the [profiler] section.\n To disable OSprofiler " "set in /etc/neutron/neutron.conf:\n" "[profiler]\n" "enabled=false") neutron-12.1.1/neutron/common/ipv6_utils.py0000664000175000017500000000537513553660047020773 0ustar zuulzuul00000000000000# Copyright 2013 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ IPv6-related utilities and helper functions. """ import os import netaddr from neutron_lib import constants as const from oslo_log import log LOG = log.getLogger(__name__) _IS_IPV6_ENABLED = None def is_enabled_and_bind_by_default(): """Check if host has the IPv6 support and is configured to bind IPv6 address to new interfaces by default. """ global _IS_IPV6_ENABLED if _IS_IPV6_ENABLED is None: disabled_ipv6_path = "/proc/sys/net/ipv6/conf/default/disable_ipv6" if os.path.exists(disabled_ipv6_path): with open(disabled_ipv6_path, 'r') as f: disabled = f.read().strip() _IS_IPV6_ENABLED = disabled == "0" else: _IS_IPV6_ENABLED = False if not _IS_IPV6_ENABLED: LOG.info("IPv6 not present or configured not to bind to new " "interfaces on this system. Please ensure IPv6 is " "enabled and /proc/sys/net/ipv6/conf/default/" "disable_ipv6 is set to 0 to enable IPv6.") return _IS_IPV6_ENABLED def is_auto_address_subnet(subnet): """Check if subnet is an auto address subnet.""" modes = [const.IPV6_SLAAC, const.DHCPV6_STATELESS] return (subnet['ipv6_address_mode'] in modes or subnet['ipv6_ra_mode'] in modes) def is_eui64_address(ip_address): """Check if ip address is EUI64.""" ip = netaddr.IPAddress(ip_address) # '0xfffe' addition is used to build EUI-64 from MAC (RFC4291) # Look for it in the middle of the EUI-64 part of address return ip.version == 6 and not ((ip & 0xffff000000) ^ 0xfffe000000) def is_ipv6_pd_enabled(subnet): """Returns True if the subnetpool_id of the given subnet is equal to constants.IPV6_PD_POOL_ID """ return subnet.get('subnetpool_id') == const.IPV6_PD_POOL_ID def valid_ipv6_url(host, port): """Given a host and a port returns a valid URL RFC2732 https://tools.ietf.org/html/rfc2732 square brackets always required in ipv6 URI. """ if netaddr.valid_ipv6(host): uri = '[%s]:%s' % (host, port) else: uri = '%s:%s' % (host, port) return uri neutron-12.1.1/neutron/common/_deprecate.py0000664000175000017500000001454713553660047020763 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. """ Provide a deprecation method for globals. NOTE: This module may be a candidate for adoption by debtcollector. """ import inspect import sys import debtcollector from neutron._i18n import _ class _MovedGlobals(object): """Override a module to deprecate moved globals. This class is used when globals (attributes of a module) need to be marked as deprecated. It can be used in either or both of two ways: 1. By specifying a default new module, all accesses to a global in the source module will emit a warning if the global does not exist in the source module and it does exist in the new module. This way is intended to be used when many globals are moved from one module to another. 2. By explicitly deprecating individual globals with the _moved_global() function, see below. This class must be called from the last line in a module, as follows: ``_deprecate._MovedGlobals(default_new_module)`` or ``_deprecate._MovedGlobals()`` Args: :param default_new_module: The default new location for moved globals :type default_new_module: module or None Attributes: :ivar _mg__my_globals: The current vars() of the source module :type _mg__my_globals: dict :ivar _mg__default_new_mod: The default location for moved globals :type _mg__default_new_mod: module or None :ivar _mg__old_ref: The original reference to the source module :type _mg__old_ref: module :cvar _mg__moves: Moves (and renames) not involving default_new_module :type _mg__moves: dict NOTE: An instance of _MovedGlobals overrides the module it is called from, so instance and class variables appear in the module namespace. To prevent collisions with existing globals, the instance and class variable names here are prefixed with ``_mg__``. """ # Here we store individual moves and renames. This is a dict where # key = (old_module, old_name) # value = (new_module, new_name) # If new_module is the same as old_module then it is a rename in place. _mg__moves = {} def __init__(self, default_new_module=None): # To avoid infinite recursion at inspect.getsourcelines() below we # must initialize self._mg__my_globals early here. self._mg__my_globals = {} self._mg__default_new_mod = default_new_module caller_frame = inspect.stack()[1][0] caller_line = inspect.getframeinfo(caller_frame).lineno source_module = inspect.getmodule(caller_frame) src_mod_last_line = len(inspect.getsourcelines(source_module)[0]) if caller_line < src_mod_last_line: raise SystemExit(_("_MovedGlobals() not called from last " "line in %s") % source_module.__file__) self._mg__my_globals = vars(source_module) # When we return from here we override the sys.modules[] entry # for the source module with this instance. We must keep a # reference to the original module to prevent it from being # garbage collected. self._mg__old_ref = source_module sys.modules[source_module.__name__] = self def __getattr__(self, name): value = self._mg__my_globals.get(name) if not name.startswith("__") and not inspect.ismodule(value): old_module = self._mg__old_ref specified_move = self._mg__moves.get((old_module, name)) if specified_move: new_module, new_name = specified_move else: new_module, new_name = self._mg__default_new_mod, name if new_module and new_name in vars(new_module): old_location = '%s.%s' % (old_module.__name__, name) new_location = '%s.%s' % (new_module.__name__, new_name) changed = 'renamed' if old_module == new_module else 'moved' debtcollector.deprecate( old_location, message='%s to %s' % (changed, new_location), stacklevel=4) return vars(new_module)[new_name] try: return self._mg__my_globals[name] except KeyError: raise AttributeError( _("'module' object has no attribute '%s'") % name) def __setattr__(self, name, val): if name.startswith('_mg__'): return super(_MovedGlobals, self).__setattr__(name, val) self._mg__my_globals[name] = val def __delattr__(self, name): if name not in self._mg__my_globals: raise AttributeError( _("'module' object has no attribute '%s'") % name) self._mg__my_globals.pop(name) def _moved_global(old_name, new_module=None, new_name=None): """Deprecate a single attribute in a module. This function is used to move an attribute to a module that differs from _mg__default_new_mod in _MovedGlobals. It also handles renames. NOTE: This function has no effect if _MovedGlobals() is not called at the end of the module containing the attribute. [TODO(HenryG): Figure out a way of asserting on this.] :param old_name: The name of the attribute that was moved/renamed. :type old_name: str :param new_module: The new module where the attribute is now. :type new_module: module :param new_name: The new name of the attribute. :type new_name: str """ assert new_module or new_name # One or both must be new if isinstance(new_module, _MovedGlobals): # The new module has been shimmed, get the original new_module = new_module._mg__old_ref old_module = inspect.getmodule(inspect.stack()[1][0]) # caller's module new_module = new_module or old_module new_name = new_name or old_name _MovedGlobals._mg__moves[(old_module, old_name)] = (new_module, new_name) neutron-12.1.1/neutron/common/__init__.py0000664000175000017500000000000013553660046020401 0ustar zuulzuul00000000000000neutron-12.1.1/neutron/common/rpc.py0000664000175000017500000003063513553660047017450 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # Copyright (c) 2014 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import random import time from neutron_lib import context from neutron_lib import exceptions as lib_exceptions from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_messaging.rpc import dispatcher from oslo_messaging import serializer as om_serializer from oslo_service import service from oslo_utils import excutils from osprofiler import profiler from neutron.common import exceptions LOG = logging.getLogger(__name__) TRANSPORT = None NOTIFICATION_TRANSPORT = None NOTIFIER = None ALLOWED_EXMODS = [ exceptions.__name__, lib_exceptions.__name__, ] EXTRA_EXMODS = [] # NOTE(salv-orlando): I am afraid this is a global variable. While not ideal, # they're however widely used throughout the code base. It should be set to # true if the RPC server is not running in the current process space. This # will prevent get_connection from creating connections to the AMQP server RPC_DISABLED = False def init(conf): global TRANSPORT, NOTIFICATION_TRANSPORT, NOTIFIER exmods = get_allowed_exmods() TRANSPORT = oslo_messaging.get_rpc_transport(conf, allowed_remote_exmods=exmods) NOTIFICATION_TRANSPORT = oslo_messaging.get_notification_transport( conf, allowed_remote_exmods=exmods) serializer = RequestContextSerializer() NOTIFIER = oslo_messaging.Notifier(NOTIFICATION_TRANSPORT, serializer=serializer) def cleanup(): global TRANSPORT, NOTIFICATION_TRANSPORT, NOTIFIER assert TRANSPORT is not None assert NOTIFICATION_TRANSPORT is not None assert NOTIFIER is not None TRANSPORT.cleanup() NOTIFICATION_TRANSPORT.cleanup() _BackingOffContextWrapper.reset_timeouts() TRANSPORT = NOTIFICATION_TRANSPORT = NOTIFIER = None def add_extra_exmods(*args): EXTRA_EXMODS.extend(args) def clear_extra_exmods(): del EXTRA_EXMODS[:] def get_allowed_exmods(): return ALLOWED_EXMODS + EXTRA_EXMODS def _get_default_method_timeout(): return TRANSPORT.conf.rpc_response_timeout def _get_default_method_timeouts(): return collections.defaultdict(_get_default_method_timeout) class _ContextWrapper(object): def __init__(self, original_context): self._original_context = original_context def __getattr__(self, name): return getattr(self._original_context, name) def cast(self, ctxt, method, **kwargs): try: self._original_context.cast(ctxt, method, **kwargs) except Exception as e: # TODO(kevinbenton): make catch specific to missing exchange once # bug/1705351 is resolved on the oslo.messaging side; if # oslo.messaging auto-creates the exchange, then just remove the # code completely LOG.debug("Ignored exception during cast: %e", e) class _BackingOffContextWrapper(_ContextWrapper): """Wraps oslo messaging contexts to set the timeout for calls. This intercepts RPC calls and sets the timeout value to the globally adapting value for each method. An oslo messaging timeout results in a doubling of the timeout value for the method on which it timed out. There currently is no logic to reduce the timeout since busy Neutron servers are more frequently the cause of timeouts rather than lost messages. """ _METHOD_TIMEOUTS = _get_default_method_timeouts() _max_timeout = None @classmethod def reset_timeouts(cls): # restore the original default timeout factory cls._METHOD_TIMEOUTS = _get_default_method_timeouts() cls._max_timeout = None @classmethod def get_max_timeout(cls): return cls._max_timeout or _get_default_method_timeout() * 10 @classmethod def set_max_timeout(cls, max_timeout): if max_timeout < cls.get_max_timeout(): cls._METHOD_TIMEOUTS = collections.defaultdict( lambda: max_timeout, **{ k: min(v, max_timeout) for k, v in cls._METHOD_TIMEOUTS.items() }) cls._max_timeout = max_timeout def call(self, ctxt, method, **kwargs): # two methods with the same name in different namespaces should # be tracked independently if self._original_context.target.namespace: scoped_method = '%s.%s' % (self._original_context.target.namespace, method) else: scoped_method = method # set the timeout from the global method timeout tracker for this # method self._original_context.timeout = self._METHOD_TIMEOUTS[scoped_method] try: return self._original_context.call(ctxt, method, **kwargs) except oslo_messaging.MessagingTimeout: with excutils.save_and_reraise_exception(): wait = random.uniform( 0, min(self._METHOD_TIMEOUTS[scoped_method], TRANSPORT.conf.rpc_response_timeout) ) LOG.error("Timeout in RPC method %(method)s. Waiting for " "%(wait)s seconds before next attempt. If the " "server is not down, consider increasing the " "rpc_response_timeout option as Neutron " "server(s) may be overloaded and unable to " "respond quickly enough.", {'wait': int(round(wait)), 'method': scoped_method}) new_timeout = min( self._original_context.timeout * 2, self.get_max_timeout()) if new_timeout > self._METHOD_TIMEOUTS[scoped_method]: LOG.warning("Increasing timeout for %(method)s calls " "to %(new)s seconds. Restart the agent to " "restore it to the default value.", {'method': scoped_method, 'new': new_timeout}) self._METHOD_TIMEOUTS[scoped_method] = new_timeout time.sleep(wait) class BackingOffClient(oslo_messaging.RPCClient): """An oslo messaging RPC Client that implements a timeout backoff. This has all of the same interfaces as oslo_messaging.RPCClient but if the timeout parameter is not specified, the _BackingOffContextWrapper returned will track when call timeout exceptions occur and exponentially increase the timeout for the given call method. """ def prepare(self, *args, **kwargs): ctx = super(BackingOffClient, self).prepare(*args, **kwargs) # don't back off contexts that explicitly set a timeout if 'timeout' in kwargs: return _ContextWrapper(ctx) return _BackingOffContextWrapper(ctx) @staticmethod def set_max_timeout(max_timeout): '''Set RPC timeout ceiling for all backing-off RPC clients.''' _BackingOffContextWrapper.set_max_timeout(max_timeout) def get_client(target, version_cap=None, serializer=None): assert TRANSPORT is not None serializer = RequestContextSerializer(serializer) return BackingOffClient(TRANSPORT, target, version_cap=version_cap, serializer=serializer) def get_server(target, endpoints, serializer=None): assert TRANSPORT is not None serializer = RequestContextSerializer(serializer) access_policy = dispatcher.DefaultRPCAccessPolicy return oslo_messaging.get_rpc_server(TRANSPORT, target, endpoints, 'eventlet', serializer, access_policy=access_policy) def get_notifier(service=None, host=None, publisher_id=None): assert NOTIFIER is not None if not publisher_id: publisher_id = "%s.%s" % (service, host or cfg.CONF.host) return NOTIFIER.prepare(publisher_id=publisher_id) class RequestContextSerializer(om_serializer.Serializer): """This serializer is used to convert RPC common context into Neutron Context. """ def __init__(self, base=None): super(RequestContextSerializer, self).__init__() self._base = base def serialize_entity(self, ctxt, entity): if not self._base: return entity return self._base.serialize_entity(ctxt, entity) def deserialize_entity(self, ctxt, entity): if not self._base: return entity return self._base.deserialize_entity(ctxt, entity) def serialize_context(self, ctxt): _context = ctxt.to_dict() prof = profiler.get() if prof: trace_info = { "hmac_key": prof.hmac_key, "base_id": prof.get_base_id(), "parent_id": prof.get_id() } _context['trace_info'] = trace_info return _context def deserialize_context(self, ctxt): rpc_ctxt_dict = ctxt.copy() trace_info = rpc_ctxt_dict.pop("trace_info", None) if trace_info: profiler.init(**trace_info) return context.Context.from_dict(rpc_ctxt_dict) @profiler.trace_cls("rpc") class Service(service.Service): """Service object for binaries running on hosts. A service enables rpc by listening to queues based on topic and host. """ def __init__(self, host, topic, manager=None, serializer=None): super(Service, self).__init__() self.host = host self.topic = topic self.serializer = serializer if manager is None: self.manager = self else: self.manager = manager def start(self): super(Service, self).start() self.conn = create_connection() LOG.debug("Creating Consumer connection for Service %s", self.topic) endpoints = [self.manager] self.conn.create_consumer(self.topic, endpoints) # Hook to allow the manager to do other initializations after # the rpc connection is created. if callable(getattr(self.manager, 'initialize_service_hook', None)): self.manager.initialize_service_hook(self) # Consume from all consumers in threads self.conn.consume_in_threads() def stop(self): # Try to shut the connection down, but if we get any sort of # errors, go ahead and ignore them.. as we're shutting down anyway try: self.conn.close() except Exception: # nosec pass super(Service, self).stop() class Connection(object): def __init__(self): super(Connection, self).__init__() self.servers = [] def create_consumer(self, topic, endpoints, fanout=False): target = oslo_messaging.Target( topic=topic, server=cfg.CONF.host, fanout=fanout) server = get_server(target, endpoints) self.servers.append(server) def consume_in_threads(self): for server in self.servers: server.start() return self.servers def close(self): for server in self.servers: server.stop() for server in self.servers: server.wait() class VoidConnection(object): def create_consumer(self, topic, endpoints, fanout=False): pass def consume_in_threads(self): pass def close(self): pass # functions def create_connection(): # NOTE(salv-orlando): This is a clever interpretation of the factory design # patter aimed at preventing plugins from initializing RPC servers upon # initialization when they are running in the REST over HTTP API server. # The educated reader will perfectly be able that this a fairly dirty hack # to avoid having to change the initialization process of every plugin. if RPC_DISABLED: return VoidConnection() return Connection() neutron-12.1.1/neutron/common/topics.py0000664000175000017500000000360713553660047020164 0ustar zuulzuul00000000000000# Copyright (c) 2012 OpenStack Foundation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. NETWORK = 'network' SUBNET = 'subnet' PORT = 'port' SECURITY_GROUP = 'security_group' L2POPULATION = 'l2population' DVR = 'dvr' RESOURCES = 'resources' CREATE = 'create' DELETE = 'delete' UPDATE = 'update' AGENT = 'q-agent-notifier' PLUGIN = 'q-plugin' SERVER_RESOURCE_VERSIONS = 'q-server-resource-versions' L3PLUGIN = 'q-l3-plugin' REPORTS = 'q-reports-plugin' DHCP = 'q-dhcp-notifer' METERING_PLUGIN = 'q-metering-plugin' L3_AGENT = 'l3_agent' DHCP_AGENT = 'dhcp_agent' METERING_AGENT = 'metering_agent' RESOURCE_TOPIC_PATTERN = "neutron-vo-%(resource_type)s-%(version)s" def get_topic_name(prefix, table, operation, host=None): """Create a topic name. The topic name needs to be synced between the agent and the plugin. The plugin will send a fanout message to all of the listening agents so that the agents in turn can perform their updates accordingly. :param prefix: Common prefix for the plugin/agent message queues. :param table: The table in question (NETWORK, SUBNET, PORT). :param operation: The operation that invokes notification (CREATE, DELETE, UPDATE) :param host: Add host to the topic :returns: The topic name. """ if host: return '%s-%s-%s.%s' % (prefix, table, operation, host) return '%s-%s-%s' % (prefix, table, operation) neutron-12.1.1/neutron/_i18n.py0000664000175000017500000000203113553660046016276 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import oslo_i18n DOMAIN = "neutron" _translators = oslo_i18n.TranslatorFactory(domain=DOMAIN) # The primary translation function using the well-known name "_" _ = _translators.primary # The contextual translation function using the name "_C" _C = _translators.contextual_form # The plural translation function using the name "_P" _P = _translators.plural_form def get_available_languages(): return oslo_i18n.get_available_languages(DOMAIN) neutron-12.1.1/.pylintrc0000664000175000017500000000673213553660047015176 0ustar zuulzuul00000000000000# The format of this file isn't really documented; just use --generate-rcfile [MASTER] # Add to the black list. It should be a base name, not a # path. You may set this option multiple times. ignore=.git,tests [MESSAGES CONTROL] # NOTE(gus): This is a long list. A number of these are important and # should be re-enabled once the offending code is fixed (or marked # with a local disable) disable= # "F" Fatal errors that prevent further processing import-error, # "I" Informational noise locally-disabled, # "E" Error for important programming issues (likely bugs) access-member-before-definition, no-member, no-method-argument, no-self-argument, not-an-iterable, # "W" Warnings for stylistic problems or minor programming issues abstract-method, arguments-differ, attribute-defined-outside-init, bad-builtin, bad-indentation, broad-except, dangerous-default-value, deprecated-lambda, expression-not-assigned, fixme, global-statement, literal-comparison, no-init, non-parent-init-called, not-callable, protected-access, redefined-builtin, redefined-outer-name, signature-differs, star-args, super-init-not-called, super-on-old-class, unpacking-non-sequence, unused-argument, unused-import, unused-variable, useless-super-delegation, # TODO(dougwig) - disable nonstandard-exception while we have neutron_lib shims nonstandard-exception, # "C" Coding convention violations bad-continuation, consider-iterating-dictionary, consider-using-enumerate, invalid-name, len-as-condition, misplaced-comparison-constant, missing-docstring, singleton-comparison, superfluous-parens, ungrouped-imports, wrong-import-order, # "R" Refactor recommendations abstract-class-little-used, abstract-class-not-used, consider-merging-isinstance, consider-using-ternary, duplicate-code, interface-not-implemented, no-else-return, no-self-use, redefined-argument-from-local, simplifiable-if-statement, too-few-public-methods, too-many-ancestors, too-many-arguments, too-many-branches, too-many-instance-attributes, too-many-lines, too-many-locals, too-many-nested-blocks, too-many-public-methods, too-many-return-statements, too-many-statements [BASIC] # Variable names can be 1 to 31 characters long, with lowercase and underscores variable-rgx=[a-z_][a-z0-9_]{0,30}$ # Argument names can be 2 to 31 characters long, with lowercase and underscores argument-rgx=[a-z_][a-z0-9_]{1,30}$ # Method names should be at least 3 characters long # and be lowercased with underscores method-rgx=([a-z_][a-z0-9_]{2,}|setUp|tearDown)$ # Module names matching neutron-* are ok (files in bin/) module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(neutron-[a-z0-9_-]+))$ # Don't require docstrings on tests. no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$ [FORMAT] # Maximum number of characters on a single line. max-line-length=79 [VARIABLES] # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. # _ is used by our localization additional-builtins=_ [CLASSES] # List of interface methods to ignore, separated by a comma. ignore-iface-methods= [IMPORTS] # Deprecated modules which should not be used, separated by a comma deprecated-modules= # should use oslo_serialization.jsonutils json [TYPECHECK] # List of module names for which member attributes should not be checked ignored-modules=six.moves,_MovedItems [REPORTS] # Tells whether to display a full report or only the messages reports=no neutron-12.1.1/etc/0000775000175000017500000000000013553660156014075 5ustar zuulzuul00000000000000neutron-12.1.1/etc/rootwrap.conf0000664000175000017500000000225313553660047016622 0ustar zuulzuul00000000000000# Configuration for neutron-rootwrap # This file should be owned by (and only-writeable by) the root user [DEFAULT] # List of directories to load filter definitions from (separated by ','). # These directories MUST all be only writeable by root ! filters_path=/etc/neutron/rootwrap.d,/usr/share/neutron/rootwrap # List of directories to search executables in, in case filters do not # explicitely specify a full path (separated by ',') # If not specified, defaults to system PATH environment variable. # These directories MUST all be only writeable by root ! exec_dirs=/sbin,/usr/sbin,/bin,/usr/bin,/usr/local/bin,/usr/local/sbin # Enable logging to syslog # Default value is False use_syslog=False # Which syslog facility to use. # Valid values include auth, authpriv, syslog, local0, local1... # Default value is 'syslog' syslog_log_facility=syslog # Which messages to log. # INFO means log all usage # ERROR means only log unsuccessful attempts syslog_log_level=ERROR [xenapi] # XenAPI configuration is only required by the L2 agent if it is to # target a XenServer/XCP compute host's dom0. xenapi_connection_url= xenapi_connection_username=root xenapi_connection_password= neutron-12.1.1/etc/policy.json0000664000175000017500000002740513553660047016276 0ustar zuulzuul00000000000000{ "context_is_admin": "role:admin", "owner": "tenant_id:%(tenant_id)s", "admin_or_owner": "rule:context_is_admin or rule:owner", "context_is_advsvc": "role:advsvc", "admin_or_network_owner": "rule:context_is_admin or tenant_id:%(network:tenant_id)s", "admin_owner_or_network_owner": "rule:owner or rule:admin_or_network_owner", "admin_only": "rule:context_is_admin", "regular_user": "", "admin_or_data_plane_int": "rule:context_is_admin or role:data_plane_integrator", "shared": "field:networks:shared=True", "shared_subnetpools": "field:subnetpools:shared=True", "shared_address_scopes": "field:address_scopes:shared=True", "external": "field:networks:router:external=True", "default": "rule:admin_or_owner", "create_subnet": "rule:admin_or_network_owner", "create_subnet:segment_id": "rule:admin_only", "create_subnet:service_types": "rule:admin_only", "get_subnet": "rule:admin_or_owner or rule:shared", "get_subnet:segment_id": "rule:admin_only", "update_subnet": "rule:admin_or_network_owner", "update_subnet:service_types": "rule:admin_only", "delete_subnet": "rule:admin_or_network_owner", "create_subnetpool": "", "create_subnetpool:shared": "rule:admin_only", "create_subnetpool:is_default": "rule:admin_only", "get_subnetpool": "rule:admin_or_owner or rule:shared_subnetpools", "update_subnetpool": "rule:admin_or_owner", "update_subnetpool:is_default": "rule:admin_only", "delete_subnetpool": "rule:admin_or_owner", "create_address_scope": "", "create_address_scope:shared": "rule:admin_only", "get_address_scope": "rule:admin_or_owner or rule:shared_address_scopes", "update_address_scope": "rule:admin_or_owner", "update_address_scope:shared": "rule:admin_only", "delete_address_scope": "rule:admin_or_owner", "create_network": "", "get_network": "rule:admin_or_owner or rule:shared or rule:external or rule:context_is_advsvc", "get_network:router:external": "rule:regular_user", "get_network:segments": "rule:admin_only", "get_network:provider:network_type": "rule:admin_only", "get_network:provider:physical_network": "rule:admin_only", "get_network:provider:segmentation_id": "rule:admin_only", "get_network:queue_id": "rule:admin_only", "get_network_ip_availabilities": "rule:admin_only", "get_network_ip_availability": "rule:admin_only", "create_network:shared": "rule:admin_only", "create_network:router:external": "rule:admin_only", "create_network:is_default": "rule:admin_only", "create_network:segments": "rule:admin_only", "create_network:provider:network_type": "rule:admin_only", "create_network:provider:physical_network": "rule:admin_only", "create_network:provider:segmentation_id": "rule:admin_only", "update_network": "rule:admin_or_owner", "update_network:segments": "rule:admin_only", "update_network:shared": "rule:admin_only", "update_network:provider:network_type": "rule:admin_only", "update_network:provider:physical_network": "rule:admin_only", "update_network:provider:segmentation_id": "rule:admin_only", "update_network:router:external": "rule:admin_only", "delete_network": "rule:admin_or_owner", "create_segment": "rule:admin_only", "get_segment": "rule:admin_only", "update_segment": "rule:admin_only", "delete_segment": "rule:admin_only", "network_device": "field:port:device_owner=~^network:", "create_port": "", "create_port:device_owner": "not rule:network_device or rule:context_is_advsvc or rule:admin_or_network_owner", "create_port:mac_address": "rule:context_is_advsvc or rule:admin_or_network_owner", "create_port:fixed_ips": "rule:context_is_advsvc or rule:admin_or_network_owner or rule:shared", "create_port:fixed_ips:ip_address": "rule:context_is_advsvc or rule:admin_or_network_owner", "create_port:fixed_ips:subnet_id": "rule:context_is_advsvc or rule:admin_or_network_owner or rule:shared", "create_port:port_security_enabled": "rule:context_is_advsvc or rule:admin_or_network_owner", "create_port:binding:host_id": "rule:admin_only", "create_port:binding:profile": "rule:admin_only", "create_port:mac_learning_enabled": "rule:context_is_advsvc or rule:admin_or_network_owner", "create_port:allowed_address_pairs": "rule:admin_or_network_owner", "create_port:allowed_address_pairs:mac_address": "rule:admin_or_network_owner", "create_port:allowed_address_pairs:ip_address": "rule:admin_or_network_owner", "get_port": "rule:context_is_advsvc or rule:admin_owner_or_network_owner", "get_port:queue_id": "rule:admin_only", "get_port:binding:vif_type": "rule:admin_only", "get_port:binding:vif_details": "rule:admin_only", "get_port:binding:host_id": "rule:admin_only", "get_port:binding:profile": "rule:admin_only", "update_port": "rule:admin_or_owner or rule:context_is_advsvc", "update_port:device_owner": "not rule:network_device or rule:context_is_advsvc or rule:admin_or_network_owner", "update_port:mac_address": "rule:admin_only or rule:context_is_advsvc", "update_port:fixed_ips": "rule:context_is_advsvc or rule:admin_or_network_owner or rule:shared", "update_port:fixed_ips:ip_address": "rule:context_is_advsvc or rule:admin_or_network_owner", "update_port:fixed_ips:subnet_id": "rule:context_is_advsvc or rule:admin_or_network_owner or rule:shared", "update_port:port_security_enabled": "rule:context_is_advsvc or rule:admin_or_network_owner", "update_port:binding:host_id": "rule:admin_only", "update_port:binding:profile": "rule:admin_only", "update_port:mac_learning_enabled": "rule:context_is_advsvc or rule:admin_or_network_owner", "update_port:allowed_address_pairs": "rule:admin_or_network_owner", "update_port:allowed_address_pairs:mac_address": "rule:admin_or_network_owner", "update_port:allowed_address_pairs:ip_address": "rule:admin_or_network_owner", "update_port:data_plane_status": "rule:admin_or_data_plane_int", "delete_port": "rule:context_is_advsvc or rule:admin_owner_or_network_owner", "get_router:ha": "rule:admin_only", "create_router": "rule:regular_user", "create_router:external_gateway_info": "rule:admin_or_owner", "create_router:external_gateway_info:network_id": "rule:admin_or_owner", "create_router:external_gateway_info:enable_snat": "rule:admin_only", "create_router:distributed": "rule:admin_only", "create_router:ha": "rule:admin_only", "get_router": "rule:admin_or_owner", "get_router:distributed": "rule:admin_only", "update_router": "rule:admin_or_owner", "update_router:external_gateway_info": "rule:admin_or_owner", "update_router:external_gateway_info:network_id": "rule:admin_or_owner", "update_router:external_gateway_info:enable_snat": "rule:admin_only", "update_router:distributed": "rule:admin_only", "update_router:ha": "rule:admin_only", "delete_router": "rule:admin_or_owner", "add_router_interface": "rule:admin_or_owner", "remove_router_interface": "rule:admin_or_owner", "create_router:external_gateway_info:external_fixed_ips": "rule:admin_only", "update_router:external_gateway_info:external_fixed_ips": "rule:admin_only", "create_qos_queue": "rule:admin_only", "get_qos_queue": "rule:admin_only", "update_agent": "rule:admin_only", "delete_agent": "rule:admin_only", "get_agent": "rule:admin_only", "create_dhcp-network": "rule:admin_only", "delete_dhcp-network": "rule:admin_only", "get_dhcp-networks": "rule:admin_only", "create_l3-router": "rule:admin_only", "delete_l3-router": "rule:admin_only", "get_l3-routers": "rule:admin_only", "get_dhcp-agents": "rule:admin_only", "get_l3-agents": "rule:admin_only", "get_loadbalancer-agent": "rule:admin_only", "get_loadbalancer-pools": "rule:admin_only", "get_agent-loadbalancers": "rule:admin_only", "get_loadbalancer-hosting-agent": "rule:admin_only", "create_floatingip": "rule:regular_user", "create_floatingip:floating_ip_address": "rule:admin_only", "update_floatingip": "rule:admin_or_owner", "delete_floatingip": "rule:admin_or_owner", "get_floatingip": "rule:admin_or_owner", "create_network_profile": "rule:admin_only", "update_network_profile": "rule:admin_only", "delete_network_profile": "rule:admin_only", "get_network_profiles": "", "get_network_profile": "", "update_policy_profiles": "rule:admin_only", "get_policy_profiles": "", "get_policy_profile": "", "create_metering_label": "rule:admin_only", "delete_metering_label": "rule:admin_only", "get_metering_label": "rule:admin_only", "create_metering_label_rule": "rule:admin_only", "delete_metering_label_rule": "rule:admin_only", "get_metering_label_rule": "rule:admin_only", "get_service_provider": "rule:regular_user", "get_lsn": "rule:admin_only", "create_lsn": "rule:admin_only", "create_flavor": "rule:admin_only", "update_flavor": "rule:admin_only", "delete_flavor": "rule:admin_only", "get_flavors": "rule:regular_user", "get_flavor": "rule:regular_user", "create_service_profile": "rule:admin_only", "update_service_profile": "rule:admin_only", "delete_service_profile": "rule:admin_only", "get_service_profiles": "rule:admin_only", "get_service_profile": "rule:admin_only", "get_policy": "rule:regular_user", "create_policy": "rule:admin_only", "update_policy": "rule:admin_only", "delete_policy": "rule:admin_only", "get_policy_bandwidth_limit_rule": "rule:regular_user", "create_policy_bandwidth_limit_rule": "rule:admin_only", "delete_policy_bandwidth_limit_rule": "rule:admin_only", "update_policy_bandwidth_limit_rule": "rule:admin_only", "get_policy_dscp_marking_rule": "rule:regular_user", "create_policy_dscp_marking_rule": "rule:admin_only", "delete_policy_dscp_marking_rule": "rule:admin_only", "update_policy_dscp_marking_rule": "rule:admin_only", "get_rule_type": "rule:regular_user", "get_policy_minimum_bandwidth_rule": "rule:regular_user", "create_policy_minimum_bandwidth_rule": "rule:admin_only", "delete_policy_minimum_bandwidth_rule": "rule:admin_only", "update_policy_minimum_bandwidth_rule": "rule:admin_only", "restrict_wildcard": "(not field:rbac_policy:target_tenant=*) or rule:admin_only", "create_rbac_policy": "", "create_rbac_policy:target_tenant": "rule:restrict_wildcard", "update_rbac_policy": "rule:admin_or_owner", "update_rbac_policy:target_tenant": "rule:restrict_wildcard and rule:admin_or_owner", "get_rbac_policy": "rule:admin_or_owner", "delete_rbac_policy": "rule:admin_or_owner", "create_flavor_service_profile": "rule:admin_only", "delete_flavor_service_profile": "rule:admin_only", "get_flavor_service_profile": "rule:regular_user", "get_auto_allocated_topology": "rule:admin_or_owner", "create_trunk": "rule:regular_user", "get_trunk": "rule:admin_or_owner", "delete_trunk": "rule:admin_or_owner", "get_subports": "", "add_subports": "rule:admin_or_owner", "remove_subports": "rule:admin_or_owner", "get_security_groups": "rule:admin_or_owner", "get_security_group": "rule:admin_or_owner", "create_security_group": "rule:admin_or_owner", "update_security_group": "rule:admin_or_owner", "delete_security_group": "rule:admin_or_owner", "get_security_group_rules": "rule:admin_or_owner", "get_security_group_rule": "rule:admin_or_owner", "create_security_group_rule": "rule:admin_or_owner", "delete_security_group_rule": "rule:admin_or_owner", "get_loggable_resources": "rule:admin_only", "create_log": "rule:admin_only", "update_log": "rule:admin_only", "delete_log": "rule:admin_only", "get_logs": "rule:admin_only", "get_log": "rule:admin_only" } neutron-12.1.1/etc/README.txt0000664000175000017500000000046113553660046015572 0ustar zuulzuul00000000000000To generate the sample neutron configuration files, run the following command from the top level of the neutron directory: tox -e genconfig If a 'tox' environment is unavailable, then you can run the following script instead to generate the configuration files: ./tools/generate_config_file_samples.sh neutron-12.1.1/etc/oslo-config-generator/0000775000175000017500000000000013553660156020300 5ustar zuulzuul00000000000000neutron-12.1.1/etc/oslo-config-generator/macvtap_agent.ini0000664000175000017500000000022513553660046023607 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/neutron/plugins/ml2/macvtap_agent.ini.sample wrap_width = 79 namespace = neutron.ml2.macvtap.agent namespace = oslo.log neutron-12.1.1/etc/oslo-config-generator/metadata_agent.ini0000664000175000017500000000022513553660046023734 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/metadata_agent.ini.sample wrap_width = 79 namespace = neutron.metadata.agent namespace = oslo.log namespace = oslo.cacheneutron-12.1.1/etc/oslo-config-generator/dhcp_agent.ini0000664000175000017500000000026313553660046023074 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/dhcp_agent.ini.sample wrap_width = 79 namespace = neutron.az.agent namespace = neutron.base.agent namespace = neutron.dhcp.agent namespace = oslo.log neutron-12.1.1/etc/oslo-config-generator/linuxbridge_agent.ini0000664000175000017500000000023513553660046024471 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/neutron/plugins/ml2/linuxbridge_agent.ini.sample wrap_width = 79 namespace = neutron.ml2.linuxbridge.agent namespace = oslo.log neutron-12.1.1/etc/oslo-config-generator/metering_agent.ini0000664000175000017500000000023613553660046023770 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/metering_agent.ini.sample wrap_width = 79 namespace = neutron.base.agent namespace = neutron.metering.agent namespace = oslo.log neutron-12.1.1/etc/oslo-config-generator/openvswitch_agent.ini0000664000175000017500000000026413553660046024530 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/neutron/plugins/ml2/openvswitch_agent.ini.sample wrap_width = 79 namespace = neutron.ml2.ovs.agent namespace = neutron.ml2.xenapi namespace = oslo.log neutron-12.1.1/etc/oslo-config-generator/neutron.conf0000664000175000017500000000075613553660047022650 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/neutron.conf.sample wrap_width = 79 namespace = neutron namespace = neutron.agent namespace = neutron.db namespace = neutron.extensions namespace = nova.auth namespace = oslo.log namespace = oslo.db namespace = oslo.policy namespace = oslo.concurrency namespace = oslo.messaging namespace = oslo.middleware.cors namespace = oslo.middleware.http_proxy_to_wsgi namespace = oslo.service.sslutils namespace = oslo.service.wsgi namespace = keystonemiddleware.auth_token neutron-12.1.1/etc/oslo-config-generator/l3_agent.ini0000664000175000017500000000025713553660046022477 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/l3_agent.ini.sample wrap_width = 79 namespace = neutron.az.agent namespace = neutron.base.agent namespace = neutron.l3.agent namespace = oslo.log neutron-12.1.1/etc/oslo-config-generator/sriov_agent.ini0000664000175000017500000000022113553660046023312 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/neutron/plugins/ml2/sriov_agent.ini.sample wrap_width = 79 namespace = neutron.ml2.sriov.agent namespace = oslo.log neutron-12.1.1/etc/oslo-config-generator/ml2_conf.ini0000664000175000017500000000020213553660046022470 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/neutron/plugins/ml2/ml2_conf.ini.sample wrap_width = 79 namespace = neutron.ml2 namespace = oslo.log neutron-12.1.1/etc/api-paste.ini0000664000175000017500000000266713553660047016473 0ustar zuulzuul00000000000000[composite:neutron] use = egg:Paste#urlmap /: neutronversions_composite /v2.0: neutronapi_v2_0 [composite:neutronapi_v2_0] use = call:neutron.auth:pipeline_factory noauth = cors http_proxy_to_wsgi request_id catch_errors extensions neutronapiapp_v2_0 keystone = cors http_proxy_to_wsgi request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0 [composite:neutronversions_composite] use = call:neutron.auth:pipeline_factory noauth = cors http_proxy_to_wsgi neutronversions keystone = cors http_proxy_to_wsgi neutronversions [filter:request_id] paste.filter_factory = oslo_middleware:RequestId.factory [filter:catch_errors] paste.filter_factory = oslo_middleware:CatchErrors.factory [filter:cors] paste.filter_factory = oslo_middleware.cors:filter_factory oslo_config_project = neutron [filter:http_proxy_to_wsgi] paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory [filter:keystonecontext] paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory [filter:authtoken] paste.filter_factory = keystonemiddleware.auth_token:filter_factory [filter:extensions] paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory [app:neutronversions] paste.app_factory = neutron.pecan_wsgi.app:versions_factory [app:neutronapiapp_v2_0] paste.app_factory = neutron.api.v2.router:APIRouter.factory [filter:osprofiler] paste.filter_factory = osprofiler.web:WsgiMiddleware.factory neutron-12.1.1/etc/neutron/0000775000175000017500000000000013553660156015567 5ustar zuulzuul00000000000000neutron-12.1.1/etc/neutron/rootwrap.d/0000775000175000017500000000000013553660156017666 5ustar zuulzuul00000000000000neutron-12.1.1/etc/neutron/rootwrap.d/dhcp.filters0000664000175000017500000000267113553660047022203 0ustar zuulzuul00000000000000# neutron-rootwrap command filters for nodes on which neutron is # expected to control network # # This file should be owned by (and only-writeable by) the root user # format seems to be # cmd-name: filter-name, raw-command, user, args [Filters] # dhcp-agent dnsmasq: CommandFilter, dnsmasq, root # dhcp-agent uses kill as well, that's handled by the generic KillFilter # it looks like these are the only signals needed, per # neutron/agent/linux/dhcp.py kill_dnsmasq: KillFilter, root, /sbin/dnsmasq, -9, -HUP, -15 kill_dnsmasq_usr: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP, -15 ovs-vsctl: CommandFilter, ovs-vsctl, root ivs-ctl: CommandFilter, ivs-ctl, root mm-ctl: CommandFilter, mm-ctl, root dhcp_release: CommandFilter, dhcp_release, root dhcp_release6: CommandFilter, dhcp_release6, root # haproxy haproxy: RegExpFilter, haproxy, root, haproxy, -f, .* kill_haproxy: KillFilter, root, haproxy, -15, -9, -HUP # RHEL invocation of the metadata proxy will report /usr/bin/python # TODO(dalvarez): Remove kill_metadata* filters in Q release since # neutron-ns-metadata-proxy is now replaced by haproxy. We keep them for now # for the migration process kill_metadata: KillFilter, root, python, -9 kill_metadata7: KillFilter, root, python2.7, -9 kill_metadata35: KillFilter, root, python3.5, -9 # ip_lib ip: IpFilter, ip, root find: RegExpFilter, find, root, find, /sys/class/net, -maxdepth, 1, -type, l, -printf, %.* ip_exec: IpNetnsExecFilter, ip, root neutron-12.1.1/etc/neutron/rootwrap.d/linuxbridge-plugin.filters0000664000175000017500000000231213553660047025065 0ustar zuulzuul00000000000000# neutron-rootwrap command filters for nodes on which neutron is # expected to control network # # This file should be owned by (and only-writeable by) the root user # format seems to be # cmd-name: filter-name, raw-command, user, args [Filters] # linuxbridge-agent # unclear whether both variants are necessary, but I'm transliterating # from the old mechanism brctl: CommandFilter, brctl, root bridge: CommandFilter, bridge, root sysctl: CommandFilter, sysctl, root # ip_lib ip: IpFilter, ip, root find: RegExpFilter, find, root, find, /sys/class/net, -maxdepth, 1, -type, l, -printf, %.* ip_exec: IpNetnsExecFilter, ip, root # tc commands needed for QoS support tc_replace_tbf: RegExpFilter, tc, root, tc, qdisc, replace, dev, .+, root, tbf, rate, .+, latency, .+, burst, .+ tc_add_ingress: RegExpFilter, tc, root, tc, qdisc, add, dev, .+, ingress, handle, .+ tc_delete: RegExpFilter, tc, root, tc, qdisc, del, dev, .+, .+ tc_show_qdisc: RegExpFilter, tc, root, tc, qdisc, show, dev, .+ tc_show_filters: RegExpFilter, tc, root, tc, filter, show, dev, .+, parent, .+ tc_add_filter: RegExpFilter, tc, root, tc, filter, add, dev, .+, parent, .+, protocol, all, prio, .+, basic, police, rate, .+, burst, .+, mtu, .+, drop neutron-12.1.1/etc/neutron/rootwrap.d/debug.filters0000664000175000017500000000131113553660047022341 0ustar zuulzuul00000000000000# neutron-rootwrap command filters for nodes on which neutron is # expected to control network # # This file should be owned by (and only-writeable by) the root user # format seems to be # cmd-name: filter-name, raw-command, user, args [Filters] # This is needed because we should ping # from inside a namespace which requires root # _alt variants allow to match -c and -w in any order # (used by NeutronDebugAgent.ping_all) ping: RegExpFilter, ping, root, ping, -w, \d+, -c, \d+, [0-9\.]+ ping_alt: RegExpFilter, ping, root, ping, -c, \d+, -w, \d+, [0-9\.]+ ping6: RegExpFilter, ping6, root, ping6, -w, \d+, -c, \d+, [0-9A-Fa-f:]+ ping6_alt: RegExpFilter, ping6, root, ping6, -c, \d+, -w, \d+, [0-9A-Fa-f:]+neutron-12.1.1/etc/neutron/rootwrap.d/dibbler.filters0000664000175000017500000000111313553660047022656 0ustar zuulzuul00000000000000# neutron-rootwrap command filters for nodes on which neutron is # expected to control network # # This file should be owned by (and only-writeable by) the root user # format seems to be # cmd-name: filter-name, raw-command, user, args [Filters] # Filters for the dibbler-based reference implementation of the pluggable # Prefix Delegation driver. Other implementations using an alternative agent # should include a similar filter in this folder. # prefix_delegation_agent dibbler-client: CommandFilter, dibbler-client, root kill_dibbler-client: KillFilter, root, dibbler-client, -9 neutron-12.1.1/etc/neutron/rootwrap.d/ebtables.filters0000664000175000017500000000044113553660046023036 0ustar zuulzuul00000000000000# neutron-rootwrap command filters for nodes on which neutron is # expected to control network # # This file should be owned by (and only-writeable by) the root user # format seems to be # cmd-name: filter-name, raw-command, user, args [Filters] ebtables: CommandFilter, ebtables, root neutron-12.1.1/etc/neutron/rootwrap.d/iptables-firewall.filters0000664000175000017500000000137613553660047024674 0ustar zuulzuul00000000000000# neutron-rootwrap command filters for nodes on which neutron is # expected to control network # # This file should be owned by (and only-writeable by) the root user # format seems to be # cmd-name: filter-name, raw-command, user, args [Filters] # neutron/agent/linux/iptables_firewall.py # "iptables-save", ... iptables-save: CommandFilter, iptables-save, root iptables-restore: CommandFilter, iptables-restore, root ip6tables-save: CommandFilter, ip6tables-save, root ip6tables-restore: CommandFilter, ip6tables-restore, root # neutron/agent/linux/iptables_firewall.py # "iptables", "-A", ... iptables: CommandFilter, iptables, root ip6tables: CommandFilter, ip6tables, root # neutron/agent/linux/ip_conntrack.py conntrack: CommandFilter, conntrack, root neutron-12.1.1/etc/neutron/rootwrap.d/ipset-firewall.filters0000664000175000017500000000053413553660046024207 0ustar zuulzuul00000000000000# neutron-rootwrap command filters for nodes on which neutron is # expected to control network # # This file should be owned by (and only-writeable by) the root user # format seems to be # cmd-name: filter-name, raw-command, user, args [Filters] # neutron/agent/linux/iptables_firewall.py # "ipset", "-A", ... ipset: CommandFilter, ipset, root neutron-12.1.1/etc/neutron/rootwrap.d/openvswitch-plugin.filters0000664000175000017500000000151513553660046025125 0ustar zuulzuul00000000000000# neutron-rootwrap command filters for nodes on which neutron is # expected to control network # # This file should be owned by (and only-writeable by) the root user # format seems to be # cmd-name: filter-name, raw-command, user, args [Filters] # openvswitch-agent # unclear whether both variants are necessary, but I'm transliterating # from the old mechanism ovs-vsctl: CommandFilter, ovs-vsctl, root # NOTE(yamamoto): of_interface=native doesn't use ovs-ofctl ovs-ofctl: CommandFilter, ovs-ofctl, root kill_ovsdb_client: KillFilter, root, /usr/bin/ovsdb-client, -9 ovsdb-client: CommandFilter, ovsdb-client, root # ip_lib ip: IpFilter, ip, root find: RegExpFilter, find, root, find, /sys/class/net, -maxdepth, 1, -type, l, -printf, %.* ip_exec: IpNetnsExecFilter, ip, root # needed for FDB extension bridge: CommandFilter, bridge, root neutron-12.1.1/etc/neutron/rootwrap.d/privsep.filters0000664000175000017500000000225613553660047022754 0ustar zuulzuul00000000000000# Command filters to allow privsep daemon to be started via rootwrap. # # This file should be owned by (and only-writeable by) the root user [Filters] # By installing the following, the local admin is asserting that: # # 1. The python module load path used by privsep-helper # command as root (as started by sudo/rootwrap) is trusted. # 2. Any oslo.config files matching the --config-file # arguments below are trusted. # 3. Users allowed to run sudo/rootwrap with this configuration(*) are # also allowed to invoke python "entrypoint" functions from # --privsep_context with the additional (possibly root) privileges # configured for that context. # # (*) ie: the user is allowed by /etc/sudoers to run rootwrap as root # # In particular, the oslo.config and python module path must not # be writeable by the unprivileged user. # oslo.privsep default neutron context privsep: PathFilter, privsep-helper, root, --config-file, /etc, --privsep_context, neutron.privileged.default, --privsep_sock_path, / # NOTE: A second `--config-file` arg can also be added above. Since # many neutron components are installed like that (eg: by devstack). # Adjust to suit local requirements. neutron-12.1.1/etc/neutron/rootwrap.d/netns-cleanup.filters0000664000175000017500000000045713553660046024040 0ustar zuulzuul00000000000000# neutron-rootwrap command filters for nodes on which neutron is # expected to control network # # This file should be owned by (and only-writeable by) the root user # format seems to be # cmd-name: filter-name, raw-command, user, args [Filters] # netns-cleanup netstat: CommandFilter, netstat, root neutron-12.1.1/etc/neutron/rootwrap.d/l3.filters0000664000175000017500000000664113553660047021604 0ustar zuulzuul00000000000000# neutron-rootwrap command filters for nodes on which neutron is # expected to control network # # This file should be owned by (and only-writeable by) the root user # format seems to be # cmd-name: filter-name, raw-command, user, args [Filters] # arping arping: CommandFilter, arping, root # l3_agent sysctl: CommandFilter, sysctl, root route: CommandFilter, route, root radvd: CommandFilter, radvd, root # haproxy haproxy: RegExpFilter, haproxy, root, haproxy, -f, .* kill_haproxy: KillFilter, root, haproxy, -15, -9, -HUP # RHEL invocation of the metadata proxy will report /usr/bin/python # TODO(dalvarez): Remove kill_metadata* filters in Q release since # neutron-ns-metadata-proxy is now replaced by haproxy. We keep them for now # for the migration process kill_metadata: KillFilter, root, python, -15, -9 kill_metadata7: KillFilter, root, python2.7, -15, -9 kill_metadata35: KillFilter, root, python3.5, -15, -9 kill_radvd_usr: KillFilter, root, /usr/sbin/radvd, -15, -9, -HUP kill_radvd: KillFilter, root, /sbin/radvd, -15, -9, -HUP # ip_lib ip: IpFilter, ip, root find: RegExpFilter, find, root, find, /sys/class/net, -maxdepth, 1, -type, l, -printf, %.* ip_exec: IpNetnsExecFilter, ip, root # l3_tc_lib l3_tc_show_qdisc: RegExpFilter, tc, root, tc, qdisc, show, dev, .+ l3_tc_add_qdisc_ingress: RegExpFilter, tc, root, tc, qdisc, add, dev, .+, ingress l3_tc_add_qdisc_egress: RegExpFilter, tc, root, tc, qdisc, add, dev, .+, root, handle, 1:, htb l3_tc_show_filters: RegExpFilter, tc, root, tc, -p, -s, -d, filter, show, dev, .+, parent, .+, prio, 1 l3_tc_delete_filters: RegExpFilter, tc, root, tc, filter, del, dev, .+, parent, .+, prio, 1, handle, .+, u32 l3_tc_add_filter_ingress: RegExpFilter, tc, root, tc, filter, add, dev, .+, parent, .+, protocol, ip, prio, 1, u32, match, ip, dst, .+, police, rate, .+, burst, .+, mtu, 64kb, drop, flowid, :1 l3_tc_add_filter_egress: RegExpFilter, tc, root, tc, filter, add, dev, .+, parent, .+, protocol, ip, prio, 1, u32, match, ip, src, .+, police, rate, .+, burst, .+, mtu, 64kb, drop, flowid, :1 # For ip monitor kill_ip_monitor: KillFilter, root, ip, -9 # ovs_lib (if OVSInterfaceDriver is used) ovs-vsctl: CommandFilter, ovs-vsctl, root # iptables_manager iptables-save: CommandFilter, iptables-save, root iptables-restore: CommandFilter, iptables-restore, root ip6tables-save: CommandFilter, ip6tables-save, root ip6tables-restore: CommandFilter, ip6tables-restore, root # Keepalived keepalived: CommandFilter, keepalived, root kill_keepalived: KillFilter, root, keepalived, -HUP, -15, -9 # l3 agent to delete floatingip's conntrack state conntrack: CommandFilter, conntrack, root # keepalived state change monitor keepalived_state_change: CommandFilter, neutron-keepalived-state-change, root # The following filters are used to kill the keepalived state change monitor. # Since the monitor runs as a Python script, the system reports that the # command of the process to be killed is python. # TODO(mlavalle) These kill filters will be updated once we come up with a # mechanism to kill using the name of the script being executed by Python kill_keepalived_monitor_py: KillFilter, root, python, -15 kill_keepalived_monitor_py27: KillFilter, root, python2.7, -15 kill_keepalived_monitor_py3: KillFilter, root, python3, -15 kill_keepalived_monitor_py35: KillFilter, root, python3.5, -15 kill_keepalived_monitor_py36: KillFilter, root, python3.6, -15 kill_keepalived_monitor_py37: KillFilter, root, python3.7, -15 neutron-12.1.1/etc/neutron/plugins/0000775000175000017500000000000013553660156017250 5ustar zuulzuul00000000000000neutron-12.1.1/etc/neutron/plugins/ml2/0000775000175000017500000000000013553660156017742 5ustar zuulzuul00000000000000neutron-12.1.1/etc/neutron/plugins/ml2/.placeholder0000664000175000017500000000000013553660046022211 0ustar zuulzuul00000000000000neutron-12.1.1/.testr.conf0000664000175000017500000000062713553660047015414 0ustar zuulzuul00000000000000[DEFAULT] test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ OS_LOG_CAPTURE=${OS_LOG_CAPTURE:-1} \ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-160} \ ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./neutron/tests/unit} $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list neutron-12.1.1/releasenotes/0000775000175000017500000000000013553660156016013 5ustar zuulzuul00000000000000neutron-12.1.1/releasenotes/source/0000775000175000017500000000000013553660157017314 5ustar zuulzuul00000000000000neutron-12.1.1/releasenotes/source/README.rst0000664000175000017500000000263113553660046021002 0ustar zuulzuul00000000000000=========================== Neutron Release Notes Howto =========================== Release notes are a new feature for documenting new features in OpenStack projects. Background on the process, tooling, and methodology is documented in a `mailing list post by Doug Hellmann `_. Writing release notes --------------------- For information on how to create release notes, please consult the `reno documentation `__. Please keep the following in your mind when you write release notes. * **Avoid using "prelude" section** for individual release notes. "prelude" section is for general comments about the release. * **Use one entry per section** (like "feature" or "upgrade"). All entries which belong to a same release will be merged and rendered, so there is less meaning to use multiple entries by a single topic. Maintaining release notes ------------------------- .. warning:: Avoid modifying an existing release note file even though it is related to your change. If you modify a release note file of a past release, the whole content will be shown in a latest release. The only allowed case is to update a release note in a same release. If you need to update a release note of a past release, edit a corresponding release note file in a stable branch directly. neutron-12.1.1/releasenotes/source/_templates/0000775000175000017500000000000013553660157021451 5ustar zuulzuul00000000000000neutron-12.1.1/releasenotes/source/_templates/.placeholder0000664000175000017500000000000013553660046023717 0ustar zuulzuul00000000000000neutron-12.1.1/releasenotes/source/conf.py0000664000175000017500000002150213553660046020610 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # Neutron Release Notes documentation build configuration file, created by # sphinx-quickstart on Tue Nov 3 17:40:50 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # openstackdocstheme options repository_name = 'openstack/neutron' bug_project = 'neutron' bug_tag = 'doc' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Neutron Release Notes' copyright = u'2015, Neutron Developers' # Release notes are version independent # The short X.Y version. # The full version, including alpha/beta/rc tags. release = '' # The short X.Y version. version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%Y-%m-%d %H:%M' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. html_use_index = False # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'NeutronReleaseNotesdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'NeutronReleaseNotes.tex', u'Neutron Release Notes Documentation', u'Neutron Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'neutronreleasenotes', u'Neutron Release Notes Documentation', [u'Neutron Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'NeutronReleaseNotes', u'Neutron Release Notes Documentation', u'Neutron Developers', 'NeutronReleaseNotes', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] neutron-12.1.1/releasenotes/source/pike.rst0000664000175000017500000000032513553660046020773 0ustar zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike :ignore-notes: vlan-aware-vms-aka-trunk-3341cc75ba1bf5b4.yaml neutron-12.1.1/releasenotes/source/ocata.rst0000664000175000017500000000035313553660047021134 0ustar zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata :ignore-notes: deprecate-SRIOV-physical_device_mappings-67dd3317181eb513 neutron-12.1.1/releasenotes/source/_static/0000775000175000017500000000000013553660157020742 5ustar zuulzuul00000000000000neutron-12.1.1/releasenotes/source/_static/.placeholder0000664000175000017500000000000013553660046023210 0ustar zuulzuul00000000000000neutron-12.1.1/releasenotes/source/liberty.rst0000664000175000017500000000022213553660047021512 0ustar zuulzuul00000000000000============================== Liberty Series Release Notes ============================== .. release-notes:: :branch: origin/stable/liberty neutron-12.1.1/releasenotes/source/newton.rst0000664000175000017500000000026613553660047021362 0ustar zuulzuul00000000000000=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: origin/stable/newton :earliest-version: 9.0.0 neutron-12.1.1/releasenotes/source/mitaka.rst0000664000175000017500000000023213553660047021307 0ustar zuulzuul00000000000000=================================== Mitaka Series Release Notes =================================== .. release-notes:: :branch: origin/stable/mitaka neutron-12.1.1/releasenotes/source/index.rst0000664000175000017500000000142713553660047021157 0ustar zuulzuul00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ======================= Neutron Release Notes ======================= .. toctree:: :maxdepth: 1 unreleased pike ocata newton mitaka liberty .. toctree:: :maxdepth: 1 README.rst neutron-12.1.1/releasenotes/source/unreleased.rst0000664000175000017500000000015313553660046022171 0ustar zuulzuul00000000000000============================ Current Series Release Notes ============================ .. release-notes:: neutron-12.1.1/releasenotes/notes/0000775000175000017500000000000013553660157017144 5ustar zuulzuul00000000000000neutron-12.1.1/releasenotes/notes/advertise_mtu_by_default-d8b0b056a74517b8.yaml0000664000175000017500000000140313553660046027227 0ustar zuulzuul00000000000000--- features: - By default, the DHCP agent provides a network MTU value to instances using the corresponding DHCP option if core plugin calculates the value. For ML2 plugin, calculation mechanism is enabled by setting [ml2] path_mtu option to a value greater than zero. upgrade: - To disable, use [DEFAULT] advertise_mtu = False. other: - For overlay networks managed by ML2 core plugin, the calculation algorithm subtracts the overlay protocol overhead from the value of [ml2] path_mtu. The DHCP agent provides the resulting (smaller) MTU to instances using overlay networks. - The [DEFAULT] advertise_mtu option must contain a consistent value on all hosts running the DHCP agent. - Typical networks can use [ml2] path_mtu = 1500. neutron-12.1.1/releasenotes/notes/add-dscp-for-tunneling-03e28fe7c2f34e86.yaml0000664000175000017500000000137013553660046026520 0ustar zuulzuul00000000000000--- features: - The DSCP value for outer headers in openvswitch overlay tunnel ports can now be set through a configuration option ``dscp`` for both OVS and linuxbridge agents. - DSCP can also be inherited from the inner header through a new boolean configuration option ``dscp_inherit`` for both openvswitch and linuxbridge. If this option is set to true, then the value of ``dscp`` will be ignored. deprecations: - the ``tos`` configuration option in vxlan group for linuxbridge is deprecated and replaced with the more precise option ``dscp``. The TOS value is made of DSCP and ECN bits. It is not possible to set the ECN value through the TOS value, and ECN is always inherited from the inner in case of tunneling.neutron-12.1.1/releasenotes/notes/add-net-mtu-writable-api-extension-f7038f85f3494a74.yaml0000664000175000017500000000142713553660046030636 0ustar zuulzuul00000000000000--- features: - | The new ``net-mtu-writable`` extension API definition has been added. The new extension indicates that the network ``mtu`` attribute is writeable. Plugins supporting the new extension are expected to also support ``net-mtu``. The first plugin that gets support for the new extension is ``ml2``. other: - | Changing MTU configuration options (``global_physnet_mtu``, ``physical_network_mtus``, and ``path_mtu``) and restarting ``neutron-serer`` no longer affects existing networks' MTUs. Nevertheless, new networks will use new option values for MTU calculation. To reflect configuration changes for existing networks, one may use the new ``net-mtu-writable`` API extension to update ``mtu`` attribute for those networks. neutron-12.1.1/releasenotes/notes/mtu-selection-and-advertisement-ab29f9ec43140224.yaml0000664000175000017500000000057413553660046030361 0ustar zuulzuul00000000000000--- prelude: > Support for MTU selection and advertisement. features: - When advertise_mtu is set in the config, Neutron supports advertising the LinkMTU using Router Advertisements. other: - For details please read `Blueprint mtu-selection-and-advertisement `_. neutron-12.1.1/releasenotes/notes/oslo-reports-166a169037bf64f2.yaml0000664000175000017500000000032313553660046024552 0ustar zuulzuul00000000000000--- prelude: > Neutron is integrated with Guru Meditation Reports library. features: - Neutron services should respond to SIGUSR2 signal by dumping valuable debug information to standard error output. neutron-12.1.1/releasenotes/notes/project_id-d5ea7a42be428230.yaml0000664000175000017500000000060613553660046024355 0ustar zuulzuul00000000000000--- features: - The Networking API now supports the 'project_id' field in requests and responses, for compatibility with the Identity (Keystone) API V3. A new API extension, 'project-id', has been added to allow API users to detect if the 'project_id' field is supported. Note that the 'tenant_id' field is still supported, and the two fields are functionally equivalent. neutron-12.1.1/releasenotes/notes/add-availability-zone-4440cf00be7c54ba.yaml0000664000175000017500000000121113553660046026450 0ustar zuulzuul00000000000000--- prelude: > DHCP and L3 Agent scheduling is availability zone aware. features: - A DHCP agent is assigned to an availability zone; the network will be hosted by the DHCP agent with availability zone specified by the user. - An L3 agent is assigned to an availability zone; the router will be hosted by the L3 agent with availability zone specified by the user. This supports the use of availability zones with HA routers. DVR isn't supported now because L3HA and DVR integration isn't finished. other: - Please read the `OpenStack Networking Guide `_. neutron-12.1.1/releasenotes/notes/path-mtu-back-to-zero-e4f9e8bdd8317ad4.yaml0000664000175000017500000000061513553660046026451 0ustar zuulzuul00000000000000--- upgrade: - In case you rely on the default ML2 path_mtu value of 1500 to cap MTU used for new network resources, please set it explicitly in your ml2_conf.ini file. fixes: - The default value for ML2 path_mtu option is changed from 1500 to 0, effectively disabling its participation in network MTU calculation unless it's overridden in the ml2_conf.ini configuration file. neutron-12.1.1/releasenotes/notes/fix-security-group-protocol-by-numbers-48afb97ede961716.yaml0000664000175000017500000000044713553660046031775 0ustar zuulzuul00000000000000--- fixes: - | Adding security group rules by protocol number is documented, but somehow was broken without being noticed in one of the last couple of releases. This is now fixed. For more information see bug `1716045 `_. neutron-12.1.1/releasenotes/notes/add-osprofiler-support-7fc2de3001187075.yaml0000664000175000017500000000360013553660046026520 0ustar zuulzuul00000000000000--- fixes: - Missing OSprofiler support was added. This cross-project profiling library allows to trace various OpenStack requests through all OpenStack services that support it. To initiate OpenStack request tracing `--profile ` option needs to be added to the CLI command. This key needs to present one of the secret keys defined in neutron.conf configuration file with `hmac_keys` option under the `[profiler]` configuration section. To enable or disable Neutron profiling the appropriate `enabled` option under the same section needs to be set either to `True` or `False`. By default Neutron will trace all API and RPC requests, but there is an opportunity to trace DB requests as well. For this purpose `trace_sqlalchemy` option needs to be set to `True`. As a prerequisite OSprofiler library and its storage backend needs to be installed to the environment. If so (and if profiling is enabled in neutron.conf) the trace can be generated via command - `$ neutron --profile SECRET_KEY `. At the end of output there will be message with , and to plot nice HTML graphs the following command should be used - `$ osprofiler trace show --html --out result.html` upgrade: - OSprofiler support was introduced. To allow its usage the api-paste.ini file needs to be modified to contain osprofiler middleware. Also `[profiler]` section needs to be added to the neutron.conf file with `enabled`, `hmac_keys` and `trace_sqlalchemy` flags defined. security: - OSprofiler support requires passing of trace information between various OpenStack services. This information is securely signed by one of HMAC keys, defined in neutron.conf configuration file. To allow cross-project tracing user should use the key, that is common among all OpenStack services he or she wants to trace. neutron-12.1.1/releasenotes/notes/Ingress-bandwidth-limit-in-openvswitch-agent-51cda9bb6b511885.yaml0000664000175000017500000000013313553660046032775 0ustar zuulzuul00000000000000--- features: - The openvswitch L2 agent now supports bi-directional bandwidth limiting. ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000neutron-12.1.1/releasenotes/notes/iptables-fail-on-missing-sysctl-bridge-firewalling-912f157b5671363f.yamlneutron-12.1.1/releasenotes/notes/iptables-fail-on-missing-sysctl-bridge-firewalling-912f157b56713630000664000175000017500000000136513553660046032575 0ustar zuulzuul00000000000000--- deprecations: - The ``iptables`` firewall driver will no longer enable bridge firewalling in next versions of Neutron. If your distribution overrides the default value for any of relevant sysctl settings (``net.bridge.bridge-nf-call-arptables``, ``net.bridge.bridge-nf-call-ip6tables``, and ``net.bridge.bridge-nf-call-iptables``) then make sure you set them back to upstream kernel default (``1``) using /etc/sysctl.conf or /etc/sysctl.d/* configuration files. upgrades: - On newer Linux kernels (3.18+) you will need to load the ``br_netfilter`` kernel module before starting an Open vSwitch or Linuxbridge agent using ``iptables`` firewall driver. Otherwise the agent will fail to start. neutron-12.1.1/releasenotes/notes/dnsmasq_dns_servers-d729c04887ce67b4.yaml0000664000175000017500000000021213553660046026257 0ustar zuulzuul00000000000000--- upgrade: - The configuration option dnsmasq_dns_server was deprecated in the kilo cycle. This value is no longer supported. neutron-12.1.1/releasenotes/notes/remove-network_device_mtu-option-a1a96e99dc7f0a02.yaml0000664000175000017500000000052613553660046031023 0ustar zuulzuul00000000000000--- upgrade: - The network_device_mtu option is removed. Existing users of the option are advised to adopt new configuration options to accommodate for their underlying physical infrastructure. The relevant options are global_physnet_mtu for all plugins, and also path_mtu and physical_network_mtus for ML2. neutron-12.1.1/releasenotes/notes/end-to-end-mtu-00345fc4282cb8fb.yaml0000664000175000017500000000171713553660046024777 0ustar zuulzuul00000000000000--- features: - Use the value of the network 'mtu' attribute for the MTU of virtual network interfaces such as veth pairs, patch ports, and tap devices involving a particular network. - Enable end-to-end support for arbitrary MTUs including jumbo frames between instances and provider networks by moving MTU disparities between flat or VLAN networks and overlay networks from layer-2 devices to layer-3 devices that support path MTU discovery (PMTUD). upgrade: - Does not change MTU for existing virtual network interfaces. - Actions that create virtual network interfaces on an existing network with the 'mtu' attribute containing a value greater than zero could cause issues for network traffic traversing existing and new virtual network interfaces. fixes: - Explicitly configure MTU of virtual network interfaces rather than using default values or incorrect values that do not account for overlay protocol overhead. neutron-12.1.1/releasenotes/notes/remove-subnetpool-config-b15dbe59237aee7e.yaml0000664000175000017500000000033213553660046027334 0ustar zuulzuul00000000000000--- upgrade: - The configuration options for ``default_ipv4_subnet_pool`` and ``default_ipv6_subnet_pool`` have been removed. Please use the ``is_default`` option of the create/update subnetpool API instead. neutron-12.1.1/releasenotes/notes/fail-on-missing-extensions-bc332124b780875b.yaml0000664000175000017500000000024513553660046027267 0ustar zuulzuul00000000000000--- fixes: - The server will fail to start if any of the declared required extensions, as needed by core and service plugins, are not properly configured. neutron-12.1.1/releasenotes/notes/bgp-support-ef361825ca63f28b.yaml0000664000175000017500000000231013553660046024523 0ustar zuulzuul00000000000000--- prelude: > Announcement of tenant prefixes and host routes for floating IP's via BGP is supported features: - Announcement of tenant subnets via BGP using centralized Neutron router gateway port as the next-hop - Announcement of floating IP host routes via BGP using the centralized Neutron router gateway port as the next-hop - Announcement of floating IP host routes via BGP using the floating IP agent gateway as the next-hop when the floating IP is associated through a distributed router issues: - When using DVR, if a floating IP is associated to a fixed IP direct access to the fixed IP is not possible when traffic is sent from outside of a Neutron tenant network (north-south traffic). Traffic sent between tenant networks (east-west traffic) is not affected. When using a distributed router, the floating IP will mask the fixed IP making it inaccessible, even though the tenant subnet is being announced as accessible through the centralized SNAT router. In such a case, traffic sent to the instance should be directed to the floating IP. This is a limitation of the Neutron L3 agent when using DVR and will be addressed in a future release. neutron-12.1.1/releasenotes/notes/deprecate_prevent_arp_spoofing_option-a09e673fc8f9fee4.yaml0000664000175000017500000000102713553660046032262 0ustar zuulzuul00000000000000--- deprecations: - The option ``[AGENT] prevent_arp_spoofing`` has been deprecated and will be removed in Ocata release. ARP spoofing protection should always be enabled unless its explicitly disabled via the port security extension via the API. The primary reason it was a config option was because it was merged at the end of Kilo development cycle so it was not considered stable. It has been enabled by default since Liberty and is considered stable and there is no reason to keep this configurable. neutron-12.1.1/releasenotes/notes/add-timestamp-fields-f9ab949fc88f05f6.yaml0000664000175000017500000000061313553660046026347 0ustar zuulzuul00000000000000--- prelude: > Timestamp fields have been added to neutron core resources. features: - Add timestamp fields ``created_at``, ``updated_at`` into neutron core resources for example networks, subnets, ports and subnetpools. - These resources can now be queried by ``changed-since``, which returns the resources changed after a specific time string like ``YYYY-MM-DDTHH:MM:SS``. neutron-12.1.1/releasenotes/notes/bump-default-quotas-810570badb378c50.yaml0000664000175000017500000000041313553660046026047 0ustar zuulzuul00000000000000--- upgrade: - | Default quotas were bumped for the following resources: networks (from 10 to 100), subnets (from 10 to 100), ports (from 50 to 500). If you want to stick to old values, consider explicitly setting them in the ``neutron.conf`` file. neutron-12.1.1/releasenotes/notes/extend-quota-api-2df3b84309664234.yaml0000664000175000017500000000035213553660046025211 0ustar zuulzuul00000000000000--- features: - | Implements a new extension, ``quota_details`` which extends existing quota API to show detailed information for a specified tenant. The new API shows details such as ``limits``, ``used``, ``reserved``. neutron-12.1.1/releasenotes/notes/qos-min-egress-bw-rule-b1c80f5675a4c1c3.yaml0000664000175000017500000000062513553660046026463 0ustar zuulzuul00000000000000--- features: - Users can now apply a QoS rule to a port or network to setup the minimum egress bandwidth per queue and port. The minimum egress bandwidth rule is applied to each port individually. other: - At the time of writing, Neutron bandwidth booking is not integrated with Compute scheduler, which means that minimal bandwidth is not guaranteed but provided as best effort. neutron-12.1.1/releasenotes/notes/deprecate-force_gateway_on_subnet-376855c4e66f4e11.yaml0000664000175000017500000000017013553660046030740 0ustar zuulzuul00000000000000--- deprecations: - The 'force_gateway_on_subnet' option is deprecated and will be removed in the 'Newton' cycle. neutron-12.1.1/releasenotes/notes/dvr-fip-namespace-on-all-nodes-c4da7ccd60ee62f5.yaml0000664000175000017500000000100013553660046030240 0ustar zuulzuul00000000000000--- features: - Proactively create DVR floating IP namespace on all compute nodes when a gateway is configured. issues: - Creating DVR floating IP namespace on all nodes proactively might consume public IP Address, but by using subnet service-types as explained in `the networking guide `__ consumers can use the private IPs for floating IP agent gateway ports and need not consume any public IP addresses. neutron-12.1.1/releasenotes/notes/add-dns-domain-to-ports-f71359d75909a2d5.yaml0000664000175000017500000000034313553660046026463 0ustar zuulzuul00000000000000--- features: - Ports have now a ``dns_domain`` attribute. A port's ``dns_domain`` attribute has precedence over the network's ``dns_domain`` from the point of view of publishing it to the external DNS service. neutron-12.1.1/releasenotes/notes/enable-bridge-command-openvswitch-agent-d07c0b59ea9f864f.yaml0000664000175000017500000000047213553660046032077 0ustar zuulzuul00000000000000--- fixes: - | The Openvswitch agent has an extension called ``fdb`` that uses the Linux ``bridge`` command. The ``bridge`` command has been added to the rootwrap openvswitch-plugin.filters file. For more information, see bug: `1730407 `_ neutron-12.1.1/releasenotes/notes/macvtap_assigned_vf_check-f4d07660ffd82a24.yaml0000664000175000017500000000012313553660046027400 0ustar zuulzuul00000000000000--- fixes: - Fix SR-IOV agent macvtap assigned VF check when linux kernel < 3.13 neutron-12.1.1/releasenotes/notes/l2_adjacency-e6e54e5ff9aad9b7.yaml0000664000175000017500000000043013553660046025015 0ustar zuulzuul00000000000000--- features: - | The new l2_adjacency extension adds an l2_adjacency field to the network, to indicate whether or not there is guaranteed L2 adjacency between the ports on that Network. Routed network implementations would typically set l2_adjacency to False. neutron-12.1.1/releasenotes/notes/add_is_default_to_qos_policies-f7c6bbac08d474d5.yaml0000664000175000017500000000026513553660046030611 0ustar zuulzuul00000000000000--- features: - Add 'default' behaviour to QoS policies Neutron now supports having a default QoS policy in a project, assigned automatically to all new networks created. neutron-12.1.1/releasenotes/notes/dvr-support-live-migration-b818b12bd9cbb518.yaml0000664000175000017500000000101613553660046027545 0ustar zuulzuul00000000000000--- prelude: > Improve DVR's resiliency during Nova VM live migration events. fixes: - Create DVR router namespaces pro-actively on the destination node during live migration events. This helps minimize packet loss to floating IP traffic. issues: - More synchronization between Nova and Neutron is needed to properly handle live migration failures on either side. For instance, if live migration is reverted or canceled, some dangling Neutron resources may be left on the destination host. neutron-12.1.1/releasenotes/notes/custom_ethertypes-eae3fcab3293e3a1.yaml0000664000175000017500000000074213553660046026240 0ustar zuulzuul00000000000000--- security: - | The OVS Firewall blocks traffic that does not have either the IPv4 or IPv6 ethertypes at present. This is a behavior change compared to the iptables_hybrid firewall, which only operates on IP packets and thus does not address other ethertypes. There is now a configuration option in the neutron openvswitch agent configuration file for permitted ethertypes and then ensures that the requested ethertypes are permitted on initialization. neutron-12.1.1/releasenotes/notes/precise-agent-state-transfer-67c771cb1ee04dd0.yaml0000664000175000017500000000272613553660047030013 0ustar zuulzuul00000000000000--- critical: - | The neutron-openvswitch-agent can sometimes spend too much time handling a large number of ports, exceeding its timeout value, ``agent_boot_time``, for L2 population. Because of this, some flow update operations will not be triggerred, resulting in lost flows during agent restart, especially for host-to-host vxlan tunnel flows, causing the original tunnel flows to be treated as stale due to the different cookie IDs. The agent's first RPC loop will also do a stale flow clean-up procedure and delete them, leading to a loss of connectivity. Please ensure that all neutron-server and neutron-openvswitch-agent binaries are upgraded for the changes to take effect, after which the L2 population ``agent_boot_time`` config option will no longer be used. fixes: - | The neutron-openvswitch-agent was changed to notify the neutron-server in its first RPC loop that it has restarted. This signals neutron-server to provide updated L2 population information to correctly program FDB entries, ensuring connectivity to instances is not interrupted. This fixes the following bugs: `1794991 `_, `1799178 `_, `1813703 `_, `1813714 `_, `1813715 `_. neutron-12.1.1/releasenotes/notes/firewall_driver_not_needed_on_server-4159669ad834dea6.yaml0000664000175000017500000000072113553660046031624 0ustar zuulzuul00000000000000--- prelude: > The Neutron server no longer needs to be configured with a firewall driver and it can support mixed environments of hybrid iptables firewalls and the pure OVS firewall. features: - The Neutron server now learns the appropriate firewall wiring behavior from each OVS agent so it no longer needs to be configured with the firewall_driver. This means it also supports multiple agents with different types of firewalls. neutron-12.1.1/releasenotes/notes/sriov_show_l2_agent_extensions-ca852e155a529e99.yaml0000664000175000017500000000013013553660046030423 0ustar zuulzuul00000000000000--- fixes: - Loaded agent extensions of SR-IOV agent are now shown in agent state API.neutron-12.1.1/releasenotes/notes/ipv6_first_ip_address_valid-cd94b47bdcc642cf.yaml0000664000175000017500000000036713553660046030141 0ustar zuulzuul00000000000000--- upgrade: - | The first address in an IPv6 network is now a valid, usable IP for routers. It had previously been reserved, but now can be assigned to a router so that an IPv6 address ending in "::" could be a valid default route. neutron-12.1.1/releasenotes/notes/default-subnetpool-semantics-1cdc5cdde2be88c2.yaml0000664000175000017500000000230113553660046030334 0ustar zuulzuul00000000000000--- features: - The subnet API now includes a new use_default_subnetpool attribute. This attribute can be specified on creating a subnet in lieu of a subnetpool_id. The two are mutually exclusive. If it is specified as True, the default subnet pool for the requested ip_version will be looked up and used. If no default exists, an error will be returned. deprecations: - The default_subnet_pools option is now deprecated and will be removed in the Newton release. The same functionality is now provided by setting is_default attribute on subnetpools to True using the API or client. fixes: - Before Mitaka, when a default subnetpool was defined in the configuration, a request to create a subnet would fall back to using it if no specific subnet pool was specified. This behavior broke the semantics of subnet create calls in this scenario and is now considered an API bug. This bug has been fixed so that there is no automatic fallback with the presence of a default subnet pool. Workflows which depended on this new behavior will have to be modified to set the new use_default_subnetpool attribute when creating a subnet. neutron-12.1.1/releasenotes/notes/add-minimum-bandwidth-support-sriov-63664b89f4dd1c1b.yaml0000664000175000017500000000052613553660046031267 0ustar zuulzuul00000000000000--- features: - SR-IOV now supports egress minimum bandwidth configuration. other: - In order to use QoS egress minimum bandwidth limit feature, 'ip-link' must support the extended VF management parameter ``min_tx_rate``. Minimum version of ``ip-link`` supporting this parameter is ``iproute2-ss140804``, git tag ``v3.16.0``. neutron-12.1.1/releasenotes/notes/hyperv-security-group-driver-fdbe0c0c292a1505.yaml0000664000175000017500000000127313553660046030112 0ustar zuulzuul00000000000000--- prelude: > Hyper-V Neutron Agent has been fully decomposed from Neutron. Therefore, the `neutron.plugins.hyperv.agent.security_groups_driver.HyperVSecurityGroupsDriver` firewall driver has been deleted. Update the `neutron_hyperv_agent.conf` / `neutron_ovs_agent.conf` files on the Hyper-V nodes to use `hyperv.neutron.security_groups_driver.HyperVSecurityGroupsDriver`, which is the networking_hyperv security groups driver. upgrade: - Update the `neutron_hyperv_agent.conf` / `neutron_ovs_agent.conf` files on the Hyper-V nodes to use `hyperv.neutron.security_groups_driver.HyperVSecurityGroupsDriver`, which is the networking_hyperv security groups driver. neutron-12.1.1/releasenotes/notes/deprecate-send_arp_for_ha-0281853632f58e8d.yaml0000664000175000017500000000036713553660046027105 0ustar zuulzuul00000000000000--- deprecations: - The L3 agent ``send_arp_for_ha configuration`` option is deprecated and will be removed in Pike. The functionality will remain, and the agent will send three gratuitious ARPs whenever a new floating IP is configured. neutron-12.1.1/releasenotes/notes/remove-force_gateway_on_subnet-77cb79f0b35d0c6d.yaml0000664000175000017500000000063113553660046030514 0ustar zuulzuul00000000000000--- fixes: - Fixes Bug 1548193, removing 'force_gateway_on_subnet' configuration option. This will always allow adding gateway outside the subnet, and gateway cannot be forced onto the subnet range. other: - The configuration option 'force_gateway_on_subnet' is removed. This will always allow adding gateway outside the subnet, and gateway cannot be forced onto the subnet range. neutron-12.1.1/releasenotes/notes/fdb_population-70d751c8c2e4395f.yaml0000664000175000017500000000125213553660046025177 0ustar zuulzuul00000000000000--- fixes: - In order to fix the communication issues between SR-IOV instances and regular instances the FDB population extension is added to the OVS or linuxbridge agent. the cause was that messages from SR-IOV direct port instance to normal port instances located on the same hypervisor were sent directly to the wire because the FDB table was not yet updated. FDB population extension tracks instances boot/delete operations using the handle_port delete_port extension interface messages and update the hypervisor's FDB table accordingly. Please note this L2 agent extension doesn't support allowed address pairs extension. neutron-12.1.1/releasenotes/notes/new-vif-type-for-pf-passthrough-33ec560b9b5d246f.yaml0000664000175000017500000000036713553660046030337 0ustar zuulzuul00000000000000--- features: - SriovNicSwitchMechanismDriver driver now exposes a new VIF type 'hostdev_physical' for ports with vnic type 'direct-physical' (used for SR-IOV PF passthrough). This will enable Nova to provision PFs as Neutron ports. neutron-12.1.1/releasenotes/notes/web_framework_deprecation-f984b83a1366c5b1.yaml0000664000175000017500000000041113553660046027371 0ustar zuulzuul00000000000000--- deprecations: - | The web_framework option has been deprecated and will be removed during Queens. This option was just added to make the transition to pecan easier so there is no reason operators should be using the non-default option anyway. neutron-12.1.1/releasenotes/notes/config-wsgi-pool-size-a4c06753b79fee6d.yaml0000664000175000017500000000104413553660046026465 0ustar zuulzuul00000000000000--- prelude: > Support configuration of greenthreads pool for WSGI. other: - Operators may want to tune the ``max_overflow`` and ``wsgi_default_pool_size`` configuration options according to the investigations outlined in this `mailing list post `_. The default value of ``wsgi_default_pool_size`` inherits from that of oslo.config, which is currently 100. This is a change in default from the previous Neutron-specific value of 1000. neutron-12.1.1/releasenotes/notes/ovsdb_timeout_override_for_ovs_cleanup_tool-e6ed6db258d0819e.yaml0000664000175000017500000000131413553660046033416 0ustar zuulzuul00000000000000--- fixes: - | Fixes bug `1763604 `_. Override default value of ``ovsdb_timeout`` config option in ``neutron-ovs-cleanup`` script. The default value is 10 seconds, but that is not enough for the ``neutron-ovs-cleanup`` script when there are many ports to remove from a single bridge, for example, 5000. Because of that, we now override the default value for the config option to be 600 seconds (10 minutes). issues: - | In the case when the number of ports to clean up in a single bridge is larger than about 10000, it might require an increase in the ``ovsdb_timeout`` config option to some value higher than 600 seconds. neutron-12.1.1/releasenotes/notes/deprecate-of_interface-driver-option-1968f8bf6fcd1a38.yaml0000664000175000017500000000036213553660046031516 0ustar zuulzuul00000000000000--- deprecations: - The of_interface Open vSwitch agent configuration option is deprecated and will be removed in the future. After option removal, the current default driver (native) will be the only supported of_interface driver. neutron-12.1.1/releasenotes/notes/l3-agent-extensions-b348ff26aec0fe88.yaml0000664000175000017500000000051713553660046026225 0ustar zuulzuul00000000000000--- features: - The neutron L3 agent now has the ability to load agent extensions, which allows other services to integrate without additional agent changes. An API for exposing the l3 agent's router info data to the extensions is also provided so that extensions can remain consistent with router state. ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000neutron-12.1.1/releasenotes/notes/vxlan-multicast-groups-distribution-linuxbridge-9337019c961c01a7.yamlneutron-12.1.1/releasenotes/notes/vxlan-multicast-groups-distribution-linuxbridge-9337019c961c01a7.y0000664000175000017500000000121613553660046033026 0ustar zuulzuul00000000000000--- features: - Enable creation of VXLANs with different multicast addresses in linuxbridge agent allocated by VNI-address mappings. A new config option ``multicast_ranges`` was introduced. other: - Example configuration of ``multicast_ranges`` in ml2_conf.ini under the ``[vxlan]`` config. section ``multicast_ranges = 224.0.0.10:10:90,225.0.0.15:100:900``. For VNI between 10 and 90, the multicast address 224.0.0.0.10 will be used, and for 100 through 900 225.0.0.15 will be used. Other VNI values will get standard ``vxlan_group`` address. For more info see RFE https://bugs.launchpad.net/neutron/+bug/1579068 neutron-12.1.1/releasenotes/notes/physical_network-aware-dhcp-scheduling-94e9fadc7c7c5fec.yaml0000664000175000017500000000053313553660046032304 0ustar zuulzuul00000000000000--- prelude: > Schedule networks on dhcp-agents with access to network features: - DHCP schedulers use "filter_host_with_network_access" plugin method to filter hosts with access to dhcp network. Plugins can overload it to define their own filtering logic. In particular, ML2 plugin delegates the filtering to mechanism drivers. neutron-12.1.1/releasenotes/notes/correlate-address-scope-with-network-ea16e16b0154ac21.yaml0000664000175000017500000000016213553660046031367 0ustar zuulzuul00000000000000--- features: - Add derived attributes to the network to tell users which address scopes the network is in. neutron-12.1.1/releasenotes/notes/oslo.messaging.notify.drivers-abb0d17b9e1bd470.yaml0000664000175000017500000000106013553660046030276 0ustar zuulzuul00000000000000--- upgrade: - Obsolete ``oslo.messaging.notify.drivers`` entrypoints that were left in tree for backwards compatibility with pre-Icehouse releases have been removed. Those are ``neutron.openstack.common.notifier.log_notifier``, ``neutron.openstack.common.notifier.no_op_notifier``, ``neutron.openstack.common.notifier.test_notifier``, ``neutron.openstack.common.notifier.rpc_notifier2``, ``neutron.openstack.common.notifier.rpc_notifier``. Use values provided by ``oslo.messaging`` library to configure notification drivers. neutron-12.1.1/releasenotes/notes/l3-agent-extensions-ha-state-change-f50ae363a53b0f18.yaml0000664000175000017500000000015513553660046030770 0ustar zuulzuul00000000000000--- features: - | A new method ``ha_state_change`` has been added to ``L3AgentExtensionsManager``. neutron-12.1.1/releasenotes/notes/deprecated-driver-e368e0befc9bee4c.yaml0000664000175000017500000000057313553660046026147 0ustar zuulzuul00000000000000--- prelude: > OFAgent is decomposed and deprecated in the Mitaka cycle. other: - The Openflow Agent(OFAgent) mechanism driver is decomposed completely from neutron tree in the Mitaka. The OFAgent driver and its agent also are deprecated in favor of OpenvSwitch mechanism driver with "native" of_interface in the Mitaka and will be removed in the next release. neutron-12.1.1/releasenotes/notes/ip-substring-port-filtering-f5c3d89c4a91e867.yaml0000664000175000017500000000012713553660046027655 0ustar zuulzuul00000000000000--- features: - | Support substring matching when filtering ports by IP address. neutron-12.1.1/releasenotes/notes/.placeholder0000664000175000017500000000000013553660046021412 0ustar zuulzuul00000000000000neutron-12.1.1/releasenotes/notes/noneutronversions-fbbdb98f350767d8.yaml0000664000175000017500000000055313553660046026164 0ustar zuulzuul00000000000000--- deprecations: - | The api-paste entrypoint ``neutron.api.versions:Versions.factory`` has been deprecated and will be removed in the Rocky release. Please update your api-paste.ini file to use the one that ships with Queens or update any references to the Versions factory to point to ``neutron.pecan_wsgi.app:versions_factory`` instead. neutron-12.1.1/releasenotes/notes/use-pyroute2-in-ip-lib-558bfea8f14d1fea.yaml0000664000175000017500000000021613553660046026626 0ustar zuulzuul00000000000000--- features: - Initial support for ``oslo.privsep`` has been added. Most external commands are still executed using ``oslo.rootwrap``. neutron-12.1.1/releasenotes/notes/direct-physical-vnic-878d15bdb758b70e.yaml0000664000175000017500000000027313553660046026300 0ustar zuulzuul00000000000000--- prelude: > Add new VNIC type for SR-IOV physical functions. features: - Neutron now supports creation of ports for exposing physical functions as network devices to guests. neutron-12.1.1/releasenotes/notes/conditional_updates-10b9aa66fd144217.yaml0000664000175000017500000000071213553660046026201 0ustar zuulzuul00000000000000--- features: - | The Neutron API now supports conditional updates to resources with the 'revision_number' attribute by setting the desired revision number in an HTTP If-Match header. This allows clients to ensure that a resource hasn't been modified since it was retrieved by the client. Support for conditional updates on the server can be checked for by looking for the 'revision-if-match' extension in the supported extensions. neutron-12.1.1/releasenotes/notes/remove-quota_items-d50b4672dd31ea3e.yaml0000664000175000017500000000037013553660046026137 0ustar zuulzuul00000000000000--- prelude: > Remove 'quota_items' configuration option from neutron.conf file. This option was deprecated since Liberty release and has no effect now. upgrade: - Remove 'quota_items' configuration option from neutron.conf file. neutron-12.1.1/releasenotes/notes/deprecate_neutron_debug-a578e0adfc9cff4c.yaml0000664000175000017500000000061113553660046027423 0ustar zuulzuul00000000000000--- deprecations: - The tool neutron-debug is now deprecated, to be replaced with a new set of troubleshooting and diagnostic tools. There is no plan for removal in the immediate term, and not until comparable tools will be adequate enough to supplant neutron-debug altogether. For more information, please see https://blueprints.launchpad.net/neutron/+spec/troubleshooting neutron-12.1.1/releasenotes/notes/remove-send-arp-for-ha-c1b4a926b8e52b8e.yaml0000664000175000017500000000025213553660046026500 0ustar zuulzuul00000000000000--- upgrade: - | The ``send_arp_for_ha`` configuration option is removed. Neutron now always sends three gratuitous ARP requests on address assigned to a port. neutron-12.1.1/releasenotes/notes/enable-sorting-pagination-754390289d3311fa.yaml0000664000175000017500000000012113553660046027056 0ustar zuulzuul00000000000000--- upgrade: - API sorting and pagination features are now enabled by default. neutron-12.1.1/releasenotes/notes/deprecate-gateway_external_network_id-f5c4071cd06714b0.yaml0000664000175000017500000000027313553660046031673 0ustar zuulzuul00000000000000--- deprecations: - The ``gateway_external_network_id`` L3 agent option is deprecated and will be removed in next releases, with ``external_network_bridge`` that it depends on. neutron-12.1.1/releasenotes/notes/macvtap-l2-agent-2b551d8ec341196d.yaml0000664000175000017500000000177513553660046025235 0ustar zuulzuul00000000000000--- prelude: > Adding MacVtap ML2 driver and L2 Agent as new vswitch choice features: - Libvirt qemu/kvm instances can now be attached via MacVtap in bridge mode to a network. VLAN and FLAT attachments are supported. Other attachmentes than compute are not supported. issues: - To ensure any kind of migration works between all compute nodes, make sure that the same physical_interface_mappings is configured on each MacVtap compute node. Having different mappings could cause live migration to fail (if the configured physical network interface does not exist on the target host), or even worse, result in an instance placed on the wrong physical network (if the physical network interface exists on the target host, but is used by another physical network or not used at all by OpenStack). Such an instance does not have access to its configured networks anymore. It then has layer 2 connectivity to either another OpenStack network, or one of the hosts networks.neutron-12.1.1/releasenotes/notes/dhcp-ipv6-address-update-ff18d1eb0c196bce.yaml0000664000175000017500000000116713553660046027170 0ustar zuulzuul00000000000000--- fixes: - There is a race condition when adding ports in DHCP namespaces where an IPv6 address could be dynamically created via SLAAC from a Router Advertisement sent from the L3 agent, leading to a failure to start the DHCP agent. This bug has been fixed, but care must be taken on an upgrade dealing with any potentially stale dynamic addresses. For more information, see bug `1627902 `_. upgrade: - On upgrade, IPv6 addresses in DHCP namespaces that have been created dynamically via SLAAC will be removed, and static IPv6 addresses will be added instead. neutron-12.1.1/releasenotes/notes/1500-default-mtu-b0d6e4ab193b62a4.yaml0000664000175000017500000000314313553660046025121 0ustar zuulzuul00000000000000--- prelude: > The ML2 plug-in supports calculating the MTU for instances using overlay networks by subtracting the overlay protocol overhead from the value of 'path_mtu', ideally the physical (underlying) network MTU, and providing the smaller value to instances via DHCP. Prior to Mitaka, 'path_mtu' defaults to 0 which disables this feature. In Mitaka, 'path_mtu' defaults to 1500, a typical MTU for physical networks, to improve the "out of box" experience for typical deployments. features: - In Mitaka, the combination of 'path_mtu' defaulting to 1500 and 'advertise_mtu' defaulting to True provides a value of MTU accounting for any overlay protocol overhead on the network to instances using DHCP. For example, an instance attaching to a VXLAN network receives a 1450 MTU from DHCP accounting for 50 bytes of overhead from the VXLAN overlay protocol if using IPv4 endpoints. issues: - The combination of 'path_mtu' and 'advertise_mtu' only adjusts the MTU for instances rather than all virtual network components between instances and provider/public networks. In particular, setting 'path_mtu' to a value greater than 1500 can cause packet loss even if the physical network supports it. Also, the calculation does not consider additional overhead from IPv6 endpoints. upgrade: - Operators using the ML2 plug-in with 'path_mtu' defaulting to 0 may need to perform a database migration to update the MTU for existing networks and possibly disable existing workarounds for MTU problems such as increasing the physical network MTU to 1550. neutron-12.1.1/releasenotes/notes/clear-allowed-address-pairs-with-none-4757bcca78076c9e.yaml0000664000175000017500000000045513553660046031443 0ustar zuulzuul00000000000000--- prelude: > Allowed address pairs can now be cleared by passing None in addition to an empty list. This is to make it possible to use the --action=clear option with the neutron client. neutron port-update --allowed-address-pairs action=clear fixes: - Fixes bug 1537734 neutron-12.1.1/releasenotes/notes/remove-min-l3-agents-per-router-27aef7d91dec0348.yaml0000664000175000017500000000107013553660046030300 0ustar zuulzuul00000000000000--- upgrade: - The ``min_l3_agents_per_router`` configuration option was deprecated in Newton cycle and removed in Ocata. HA routers no longer require a minimal number of L3 agents to be created, although obviously they require at least two L3 agents to provide HA guarantees. The rationale for the removal of the option is the case a router was created just when an agent was not operational. The creation of the router will now succeed, and when a second agent resumes operation the router will be scheduled to it providing HA. neutron-12.1.1/releasenotes/notes/add-get-me-a-network-56321aeef5389001.yaml0000664000175000017500000000054513553660046025713 0ustar zuulzuul00000000000000--- prelude: > The "get-me-a-network" feature simplifies the process for launching an instance with basic network connectivity (via an externally connected private tenant network). features: - Once Nova takes advantage of the "get-me-a-network" feature, a user can launch an instance without explicitly provisioning network resources. neutron-12.1.1/releasenotes/notes/security-group-port-range-check-73114bdcde459e53.yaml0000664000175000017500000000030613553660046030372 0ustar zuulzuul00000000000000--- fixes: - In security group rules API, API level validation for port_range values has been performed only against TCP and UDP. Now it is performed against DCCP, SCTP and UDP-Lite, too. neutron-12.1.1/releasenotes/notes/deprecate_max_fixed_ips_per_port-5e80518cbf25cfd6.yaml0000664000175000017500000000115513553660046031076 0ustar zuulzuul00000000000000--- prelude: > max_fixed_ips_per_port has been deprecated and will be removed in the Newton or Ocata cycle depending on when all identified usecases of the options are satisfied via another quota system. deprecations: - max_fixed_ips_per_port has been deprecated and will be removed in the Newton or Ocata cycle depending on when all identified usecases of the options are satisfied via another quota system. If you depend on this configuration option to stop tenants from consuming IP addresses, please leave a comment on the `bug report `_. neutron-12.1.1/releasenotes/notes/sriov-agent-num-vf-0-0c06424247e7efe0.yaml0000664000175000017500000000006413553660046025757 0ustar zuulzuul00000000000000--- fixes: - Allow SR-IOV agent to run with 0 vfs neutron-12.1.1/releasenotes/notes/QoS-ingress-bandwidth-limit-54cea12dbea71172.yaml0000664000175000017500000000035613553660046027541 0ustar zuulzuul00000000000000--- features: - The QoS service plugin now supports new attribute in ``qos_bandwidth_limit_rule``. This new parameter is called ``direction`` and allows to specify direction of traffic for which the limit should be applied. neutron-12.1.1/releasenotes/notes/segment_mtu_to_global_physnet_mtu-9cee5ff09557edeb.yaml0000664000175000017500000000102613553660046031512 0ustar zuulzuul00000000000000--- deprecations: - The 'segment_mtu' option of the ML2 configuration has been deprecated and replaced with the 'global_physnet_mtu' option in the main Neutron configuration. This option is meant to be used by all plugins for an operator to reference their physical network's MTU, regardless of the backend plugin. Plugins should access this config option via the 'get_deployment_physnet_mtu' method added to neutron.plugins.common.utils to avoid being broken on any potential renames in the future. neutron-12.1.1/releasenotes/notes/switching-to-haproxy-for-metadata-proxy-9d8f7549fadf9182.yaml0000664000175000017500000000107213553660046032123 0ustar zuulzuul00000000000000--- features: - In order to reduce metadata proxy memory footprint, ``haproxy`` is now used as a replacement for ``neutron-ns-metadata-proxy`` Python implementation. upgrade: - Since ``haproxy`` was not used before by ``neutron-l3-agent`` and ``neutron-dhcp-agent``, rootwrap filters for both agents have to be copied over when upgrading. - To upgrade to the ``haproxy`` based metadata proxy, ``neutron-l3-agent`` and ``neutron-dhcp-agent`` have to be restarted. On startup, old proxy processes will be detected and replaced with ``haproxy``. neutron-12.1.1/releasenotes/notes/change-of-default-timeout-b09d11683526e27d.yaml0000664000175000017500000000043613553660046027043 0ustar zuulzuul00000000000000--- other: - | In order to improve heavy load ovs agent restart success rate, instead a retry or fullsync, the native driver ``of_connect_timeout`` and ``of_request_timeout`` are now set to 300s. The value does not have side effect for the regular pressure ovs agent. neutron-12.1.1/releasenotes/notes/access_as_external_rbac-455dc74b9fa22761.yaml0000664000175000017500000000156713553660046027014 0ustar zuulzuul00000000000000--- prelude: > External networks can now be controlled using the RBAC framework that was added in Liberty. This allows networks to be made available to specific tenants (as opposed to all tenants) to be used as an external gateway for routers and floating IPs. features: - External networks can now be controlled using the RBAC framework that was added in Liberty. This allows networks to be made available to specific tenants (as opposed to all tenants) to be used as an external gateway for routers and floating IPs. By default this feature will also allow regular tenants to make their networks available as external networks to other individual tenants (or even themselves), but they are prevented from using the wildcard to share to all tenants. This behavior can be adjusted via policy.json by the operator if desired. neutron-12.1.1/releasenotes/notes/advanced_image-8abff2ca91de7f6c.yaml0000664000175000017500000000075113553660046025462 0ustar zuulzuul00000000000000--- features: - | Some scenario tests require advanced ``Glance`` images (for example, ``Ubuntu`` or ``CentOS``) in order to pass. They are now skipped by default. If you need to execute those tests, please configure ``tempest.conf`` to use an advanced image, and set ``image_is_advanced`` in ``neutron_plugin_options`` section of ``tempest.conf`` file to ``True``. The first scenario test case that requires the new option set to execute is ``test_trunk``. neutron-12.1.1/releasenotes/notes/trunk_inherit-455dc74b9fa22dad.yaml0000664000175000017500000000057713553660046025277 0ustar zuulzuul00000000000000--- features: - Subport segmentation details can now accept ``inherit`` as segmentation type during a trunk creation/update request. The trunk plugin will determine the segmentation type and ID and replace them with those of the network to which the port is connected. Only single-segment VLAN networks are set to have expected and correct results at this point. neutron-12.1.1/releasenotes/notes/add-dhcp_release6-ff1b8d62fd7fe76d.yaml0000664000175000017500000000165313553660046025741 0ustar zuulzuul00000000000000--- prelude: > - Call dhcp_release6 command line utility when releasing unused IPv6 leases for DHCPv6 stateful subnets. dhcp_release6 first appeared in dnsmasq 2.76 upgrade: - A version of dnsmasq that includes dhcp_release6 should be installed on systems running the DHCP agent. Failure to do this could cause DHCPv6 stateful addressing to not function properly. - The rootwrap filters file dhcp.filters must be updated to include dhcp_release6, otherwise trying to run the utility will result in a NoFilterMatched exception. issues: - Absence of dhcp_release6 when DHCPv6 stateful addressing is in use may lead to bug `1521666 `_. Neutron supports dhcp_release6 now, but if the tool is not available this leads to increased log warnings. Read bug report `1622002 `_ for more details.neutron-12.1.1/releasenotes/notes/deprecate-allow-sorting-allow-pagination-4549c92a74cfe15d.yaml0000664000175000017500000000022713553660046032251 0ustar zuulzuul00000000000000--- deprecations: - The ``allow_sorting`` and ``allow_pagination`` configuration options are deprecated and will be removed in a future release. neutron-12.1.1/releasenotes/notes/rename-tenant-to-project-b19a4068f8625969.yaml0000664000175000017500000000022413553660046026664 0ustar zuulzuul00000000000000--- upgrade: - | tenant_id column has been renamed to project_id. This database migration is required to be applied as offline migration. ././@LongLink0000000000000000000000000000016100000000000011213 Lustar 00000000000000neutron-12.1.1/releasenotes/notes/terminate-macvtap-agt-when-interface_mapping-not-present-3109faf3b44d366a.yamlneutron-12.1.1/releasenotes/notes/terminate-macvtap-agt-when-interface_mapping-not-present-3109faf3b0000664000175000017500000000030613553660046033435 0ustar zuulzuul00000000000000--- upgrade: - After upgrade, a macvtap agent without physical_interface_mappings configured can not be started. Specify a valid mapping to be able to start and use the macvtap agent. neutron-12.1.1/releasenotes/notes/add-conntrack-workers-89d303e9ec3b4963.yaml0000664000175000017500000000075013553660046026402 0ustar zuulzuul00000000000000--- prelude: > In order to reduce the time spent processing security group updates in the L2 agent, conntrack deletion is now performed in a set of worker threads instead of the main agent thread, so it can return to processing other events quickly. upgrade: - | On an upgrade, conntrack entries will now be cleaned-up in a worker thread, instead of in the calling thread. fixes: - | Fixes bug `1745468 `_. neutron-12.1.1/releasenotes/notes/qos-drivers-refactor-16ece9984958f8a4.yaml0000664000175000017500000000073113553660046026277 0ustar zuulzuul00000000000000--- features: - The QoS driver architecture has been refactored to overcome several previous limitations, the main one was the coupling of QoS details into the mechanism drivers, and the next one was the need of configuration knobs to enable each specific notification driver, that will be handled automatically from now on. deprecations: - | notification_drivers from [qos] section has been deprecated. It will be removed in a future release. neutron-12.1.1/releasenotes/notes/of_interface-native-by-default-0c07bdbd7365230a.yaml0000664000175000017500000000130313553660046030162 0ustar zuulzuul00000000000000--- prelude: > Prior to Newton, the neutron-openvswitch-agent used 'ovs-ofctl' of_interface driver by default. In Newton, 'of_interface' defaults to 'native'. This mostly eliminates spawning ovs-ofctl and improves performance a little. upgrade: - To retain the old default for neutron-openvswitch-agent, use 'of_interface = ovs-ofctl' in the '[ovs]' section of your openvswitch agent configuration file. - By default, the native interface will have the Ryu controller listen on 127.0.0.1:6633. The listen address can be configured with of_listen_address and of_listen_port options. Ensure that the controller has permission to listen at the configured address. neutron-12.1.1/releasenotes/notes/default-local-dns-a1c3fa1451f228fa.yaml0000664000175000017500000000230313553660046025601 0ustar zuulzuul00000000000000--- fixes: - Prior to Mitaka, name resolution in instances requires specifying DNS resolvers via the 'dnsmasq_dns_servers' option in the DHCP agent configuration file or via neutron subnet options. In this case, the data plane must provide connectivity between instances and upstream DNS resolvers. Omitting both of these methods causes the dnsmasq service to offer the IP address on which it resides to instances for name resolution. However, the static dnsmasq '--no-resolv' process argument prevents name resolution via dnsmasq, leaving instances without name resolution. Mitaka introduces the 'dnsmasq_local_resolv' option, default value False to preserve backward-compatibility, that enables the dnsmasq service to provide name resolution for instances via DNS resolvers on the host running the DHCP agent. In this case, the data plane must provide connectivity between the host and upstream DNS resolvers rather than between the instances and upstream DNS resolvers. Specifying DNS resolvers via the 'dnsmasq_dns_servers' option in the DHCP agent configuration overrides the 'dnsmasq_local_resolv' option for all subnets using the DHCP agent. neutron-12.1.1/releasenotes/notes/network_ip_availability-d64bd7032b3c15ee.yaml0000664000175000017500000000064613553660046027233 0ustar zuulzuul00000000000000--- prelude: > Neutron now provides network IP availability information. features: - A new API endpoint /v2.0/network-ip-availabilities that allows an admin to quickly get counts of used_ips and total_ips for network(s) is available. New endpoint allows filtering by network_id, network_name, tenant_id, and ip_version. Response returns network and nested subnet data that includes used and total IPs. neutron-12.1.1/releasenotes/notes/add-port-data-plane-status-12726c964210b374.yaml0000664000175000017500000000140613553660046026775 0ustar zuulzuul00000000000000--- features: - Add ``data_plane_status`` attribute to port resources to represent the status of the underlying data plane. This attribute is to be managed by entities outside of the Networking service, while the ``status`` attribute is managed by the Networking service. Both status attributes are independent from one another. Third parties can report via Neutron API issues in the underlying data plane affecting connectivity from/to Neutron ports. Attribute can take values ``None`` (default), ``ACTIVE`` or ``DOWN``, and is readable by users and writable by admins and users granted the ``data-plane-integrator`` role. Append ``data_plane_status`` to ``[ml2] extension_drivers`` config option to load the extension driver. ././@LongLink0000000000000000000000000000016100000000000011213 Lustar 00000000000000neutron-12.1.1/releasenotes/notes/dvr-configure-centralized-floatingip-with-new-agent-type-05361f1f78853cf7.yamlneutron-12.1.1/releasenotes/notes/dvr-configure-centralized-floatingip-with-new-agent-type-05361f1f70000664000175000017500000000162313553660046033247 0ustar zuulzuul00000000000000--- prelude: > A new agent_mode(``dvr_no_external``) for DVR routers has been added to allow the server to configure Floating IPs associated with DVR at the centralized node. features: - | A new DVR agent type ``dvr_no_external`` has been introduced with this release. This agent type allows the Floating IPs (DNAT/North-South routing) to be centralized while the East/West routing is still distributed. issues: - | There can be a mixture of ``dvr`` agents and ``dvr_no_external`` agents. But please avoid any VM with Floating IP migration between a ``dvr`` agent and a ``dvr_no_external`` agent. All VM ports with Floating IPs should be migrated to same agent_mode. This would be one of the restrictions. upgrade: - | A new DVR agent mode of ``dvr_no_external`` was added. Changing between this mode and ``dvr`` is a disruptive operation to the dataplane. neutron-12.1.1/releasenotes/notes/vlan-aware-vms-aka-trunk-3341cc75ba1bf5b4.yaml0000664000175000017500000000302013553660046027034 0ustar zuulzuul00000000000000--- prelude: > The "vlan-aware-vms" feature allows Nova users to launch VMs on a single port (trunk parent port) that connects multiple Neutron logical networks together. features: - The feature "vlan-aware-vms" is available. To enable it, a service plugin named 'trunk' must be added to the option ``service_plugins`` in your neutron.conf. The plugin exposes two new extensions ``trunk`` and ``trunk_details``. The plugin can work with multiple backends and in particular Neutron has support for `ML2/openvswitch `_ and ML2/linuxbridge. Even though Neutron API compatibility should be preserved for ports associated to trunks, since this is the first release where the feature is available, it is reasonable to expect possible functionality gaps for one or both drivers. These will be filled over time as being reported. The CLI is available via openstackclient, and python-neutronclient 5.1.0 or above. For more details, please check the networking guide. security: - | When working with the ML2/openvswitch driver, the "vlan-aware-vms" feature has the following limitations: * security groups do not work in conjunction with the iptables-based firewall driver. * if security groups are desired, the use of the stateful OVS firewall is required, however that prevents the use of the DPDK datapath for OVS versions 2.5 or lower. neutron-12.1.1/releasenotes/notes/add-port-rebinding-chance-33178b9abacf5804.yaml0000664000175000017500000000030213553660046027124 0ustar zuulzuul00000000000000--- prelude: > ML2: ports can now recover from binding failed state. features: - Ports that failed to bind when an L2 agent was offline can now recover after the agent is back online. ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000neutron-12.1.1/releasenotes/notes/fix-update-port-fixed-ips-on-routed-provider-networks-c54a54844d9a3926.yamlneutron-12.1.1/releasenotes/notes/fix-update-port-fixed-ips-on-routed-provider-networks-c54a54844d9a0000664000175000017500000000035213553660046033157 0ustar zuulzuul00000000000000--- fixes: - | When updating the fixed-ips of a port residing on a routed provider network the port update would always fail if *host* was not set. See bug: `1844124 `_. neutron-12.1.1/releasenotes/notes/ovs-ct-firewall-driver-52a70a6a16d06f59.yaml0000664000175000017500000000063113553660046026465 0ustar zuulzuul00000000000000--- features: - New security groups firewall driver is introduced. It's based on OpenFlow using connection tracking. issues: - OVS firewall driver doesn't work well with other features using openflow. other: - OVS firewall driver requires OVS 2.5 version or higher with linux kernel 4.3 or higher. More info at `OVS github page `_. neutron-12.1.1/releasenotes/notes/ovsdb-native-by-default-38835d6963592396.yaml0000664000175000017500000000173513553660046026354 0ustar zuulzuul00000000000000--- prelude: > Prior to Newton, the default option for 'ovsdb_interface' was 'vsctl'. In Newton 'ovsdb_interface' defaults to 'native'. This change switches the way of communication with OVSDB from the ovs-vsctl tool to Open vSwitch python api to improve out-of-the-box performance for typical deployments. upgrade: - To keep the old default value use 'ovsdb_interface = vsctl' in '[ovs]' section of openvswitch_agent.ini (common path '/etc/neutron/plugins/ml2/openvswitch_agent.ini') if there is a separate openvswitch agent configuration file; otherwise apply changes mentioned above to ml2_conf.ini (common path '/etc/neutron/plugins/ml2/ml2_conf.ini'). - The native interface configures ovsdb-server to listen for connections on 127.0.0.1:6640 by default. The address can be configured with the ovsdb_connection config option. Ensure that ovsdb-server has permissions to listen on the configured address. neutron-12.1.1/releasenotes/notes/dvr-ovs-agent-6052a8d60fddde22.yaml0000664000175000017500000000055713553660046025020 0ustar zuulzuul00000000000000--- prelude: > An OVS agent configured to run in DVR mode will fail to start if it cannot get proper DVR configuration values from the server on start-up. The agent will no longer fallback to non-DVR mode, since it may lead to inconsistency in the DVR-enabled cluster as the Neutron server does not distinguish between DVR and non-DVR OVS agents. neutron-12.1.1/releasenotes/notes/sending-garp-for-l3-ha-c118871833ad8743.yaml0000664000175000017500000000177713553660046026110 0ustar zuulzuul00000000000000--- issues: - In kernels < 3.19 ``net.ipv4.ip_nonlocal_bind`` sysctl option was not isolated to network namespace scope. L3 HA sets this option to zero to avoid sending gratuitous ARPs for IP addresses that were removed while processing. If this happens, then gratuitous ARPs will be sent. It may populate ARP cache tables of peer machines with wrong MAC addresses. fixes: - Versions of ``keepalived`` < 1.2.20 don't send gratuitous ARPs when keepalived process receives a ``SIGHUP`` signal. These versions are not packaged in some Linux distributions like Red Hat Enterprise Linux 7, CentOS 7, or Ubuntu Xenial. Not sending gratuitous ARPs may lead to peer ARP cache tables containing wrong entries about floating IP addresses until those entries are invalidated. To fix that scenario, Neutron now sends gratuitous ARPs for all new IP addresses that appear on non-HA interfaces in router namespaces. This behavior simulates behavior of new versions of ``keepalived``. neutron-12.1.1/releasenotes/notes/deprecate-neutron-rootwrap-xen-dom0-124ee3647beecc17.yaml0000664000175000017500000000026613553660046031251 0ustar zuulzuul00000000000000--- deprecations: - Now that rootwrap daemon mode is supported for XenServer, the ``neutron-rootwrap-xen-dom0`` script is deprecated and will be removed in a next release. neutron-12.1.1/releasenotes/notes/l3-agent-api-get-router-info-93c316a792a9d87f.yaml0000664000175000017500000000014213553660046027403 0ustar zuulzuul00000000000000--- features: - A new method ``get_router_info`` has been added to ``L3AgentExtensionAPI``. neutron-12.1.1/releasenotes/notes/remove-allow-pagination-allow-sorting-ff23ca5ccb3007b9.yaml0000664000175000017500000000030313553660046031724 0ustar zuulzuul00000000000000--- other: - The ``allow_pagination`` and ``allow_sorting`` configuration options are now removed. Now, sorting and pagination are always enabled for plugins that support the features. neutron-12.1.1/releasenotes/notes/ovs-mac-table-size-config-option-d255d5208650f34b.yaml0000664000175000017500000000116413553660046030254 0ustar zuulzuul00000000000000--- features: - | A new config option ``bridge_mac_table_size`` has been added for Neutron OVS agent. This value will be set on every Open vSwitch bridge managed by the openvswitch-neutron-agent in ``other_config:mac-table-size`` column in ovsdb. Default value for this new option is set to 50000 and it should be enough for most systems. More details about this option can be found in `Open vSwitch documentation `_ For more information see bug `1775797 `_. neutron-12.1.1/releasenotes/notes/set-of-default-qos-burst-value-0790773703fa08fc.yaml0000664000175000017500000000027413553660046027776 0ustar zuulzuul00000000000000--- prelude: > By default, the QoS driver for the Open vSwitch and Linuxbridge agents calculates the burst value as 80% of the available bandwidth. fixes: - Fixes bug 1572670neutron-12.1.1/releasenotes/notes/ovs-make-inactivity-probe-configurable-39d669014d961c5c.yaml0000664000175000017500000000046213553660046031563 0ustar zuulzuul00000000000000--- other: - | A new option ``[ovs] of_inactivity_probe`` has been added to allow changing the inactivity probe interval when using the OVS ML2 agent with the native OpenFlow driver. Operators can increase this if they are experiencing OpenFlow timeouts. The default value is 10 seconds. neutron-12.1.1/releasenotes/notes/vhost-user-reconnect-7650134520022e7d.yaml0000664000175000017500000000130113553660046026017 0ustar zuulzuul00000000000000--- features: - vhost-user reconnect is a mechanism which allows a vhost-user frontend to reconnect to a vhost-user backend in the event the backend terminates either as a result of a graceful shutdown or a crash. This allows a VM utilising a vhost-user interface to reconnect automatically to the backend e.g. Open vSwitch without requiring the VM to reboot. In this release, support was added to the neutron Open vSwitch agent and ``ml2`` driver for vhost-user reconnect. other: - vhost-user reconnect requires dpdk 16.07 and qemu 2.7 and openvswitch 2.6 to function. if an older qemu is used, reconnect will not be available but vhost-user will still function. neutron-12.1.1/releasenotes/notes/qos-rule-type-details-api-call-27d792980235aec4.yaml0000664000175000017500000000037213553660046027744 0ustar zuulzuul00000000000000--- features: - | New API to get details of supported rule types. The QoS service plugin can now expose details about supported QoS rule types in Neutron deployment. The new API call is allowed only for users with admin priviliges. neutron-12.1.1/releasenotes/notes/ovs_hardware_offload_support-798d3896ab2c4b1d.yaml0000664000175000017500000000045113553660046030225 0ustar zuulzuul00000000000000--- features: - The ``openvswitch`` mechanism driver now supports hardware offload via SR-IOV. It allows binding direct (SR-IOV) ports. Using ``openvswitch`` 2.8.0 and 'Linux Kernel' 4.8 allows to control the SR-IOV VF via OpenFlow control plane and gain accelerated 'Open vSwitch'. neutron-12.1.1/releasenotes/notes/add-port-ip-allocation-attr-294a580641998240.yaml0000664000175000017500000000165513553660046027114 0ustar zuulzuul00000000000000--- prelude: > Add ip_allocation attribute to port resources features: - The port resource now has an ip_allocation attribute. The value of this attribute will be set to 'immediate', 'deferred', or 'none' at the time the port is created. It will not be changed when the port is updated. 'immediate' means that the port is expected to have an IP address and Neutron attempted IP allocation on port creation. 'deferred' means that the port is expected to have an IP address but Neutron deferred IP allocation until a port update provides the host to which the port will be bound. 'none' means that the port was created explicitly with no addresses by passing [] in fixed_ips when creating it. upgrade: - All existing ports are considered to have 'immediate' IP allocation. Any ports that do not have this attribute should also be considered to have immediate IP allocation. neutron-12.1.1/releasenotes/notes/dvr-ha-support-cc67e84d9380cd0b.yaml0000664000175000017500000000112713553660046025223 0ustar zuulzuul00000000000000--- prelude: > High Availability (HA) of SNAT service is supported for Distributed Virtual Routers (DVRs). features: - High Availability support for SNAT services on Distributed Virtual Routers. Routers can now be created with the flags distributed=True and ha=True. The created routers will provide Distributed Virtual Routing as well as SNAT high availability on the l3 agents configured for dvr_snat mode. issues: - Only creation of dvr/ha routers is currently supported. Upgrade from other types of routers to dvr/ha router is not supported on this release. neutron-12.1.1/releasenotes/notes/fix-mtu-for-existing-networks-5a476cde9bc46a53.yaml0000664000175000017500000000113113553660046030201 0ustar zuulzuul00000000000000--- features: - net-mtu extension now recalculates network MTU on each network access, not just on creation. It now allows operators to tweak MTU related configuration options and see them applied to all network resources right after controller restart, both old and new. upgrade: - Existing networks with MTU values that don't reflect configuration will receive new MTU values after controller upgrade. Note that to propagate new correct MTU values to your backend, you may need to resync all agents that set up ports, as well as re-attach VIFs to affected instances. neutron-12.1.1/releasenotes/notes/deprecate-ovsdb-interface-b7e7cc5b036e9ef9.yaml0000664000175000017500000000031213553660046027420 0ustar zuulzuul00000000000000--- deprecations: - | The ``ovsdb_interface`` configuration option is now deprecated. In future releases, the value of the option will be ignored. The ``native`` driver will then be used. neutron-12.1.1/releasenotes/notes/oslo-cache-cache-url-deprecated-16cd3d335c5962eb.yaml0000664000175000017500000000071113553660046030215 0ustar zuulzuul00000000000000--- features: - Neutron switched to using oslo.cache library to cache port state in metadata agent. With it, more caching backends are now available, including Memcached and Mongo. More details in oslo.cache documentation. deprecations: - The cache_url configuration option is deprecated as of Newton, and will be removed in Ocata. Please configure metadata cache using [cache] group, setting enable = True and configuring your backend. neutron-12.1.1/releasenotes/notes/designate-driver-keystonev3-8e70d152e84388e0.yaml0000664000175000017500000000050413553660046027456 0ustar zuulzuul00000000000000--- features: - Designate driver can now use Keystone v3 authentication options. "The ``[designate]`` section now accepts the ``auth_type`` option, as well as other ``keystoneauth`` options (e.g. ``auth_url``, ``username``, ``user_domain_name``, ``password``, ``project_name``, ``project_domain_name``)." neutron-12.1.1/releasenotes/notes/hyperv-neutron-agent-decomposition-ae6a052aeb48c6ac.yaml0000664000175000017500000000071613553660046031426 0ustar zuulzuul00000000000000--- upgrade: - The Hyper-V Neutron Agent has been fully decomposed from Neutron. The `neutron.plugins.hyperv.agent.security_groups_driver.HyperVSecurityGroupsDriver` firewall driver has been deprecated and will be removed in the Ocata release. Update the `neutron_hyperv_agent.conf` files on the Hyper-V nodes to use `hyperv.neutron.security_groups_driver.HyperVSecurityGroupsDriver`, which is the networking_hyperv security groups driver. neutron-12.1.1/releasenotes/notes/oslo-messaging-notifier-queue-d94677076a1db261.yaml0000664000175000017500000000054213553660046027774 0ustar zuulzuul00000000000000--- features: - The RPC and notification queues have been separated into different queues. Specify the transport_url to be used for notifications within the [oslo_messaging_notifications] section of the configuration file. If no transport_url is specified in [oslo_messaging_notifications], the transport_url used for RPC will be used. neutron-12.1.1/releasenotes/notes/deprecate-min-l3-agents-per-router-15ddaa4c178b23df.yaml0000664000175000017500000000027113553660046031004 0ustar zuulzuul00000000000000--- deprecations: - The option min_l3_agents_per_router is deprecated and will be removed for the Ocata release where the scheduling of new HA routers will always be allowed. neutron-12.1.1/releasenotes/notes/bug-1311040-dhcp-no-dns-09291c23e2ce800a.yaml0000664000175000017500000000132713553660046025646 0ustar zuulzuul00000000000000--- prelude: > DNS server assignment can now be disabled in replies sent from the DHCP agent. features: - | It is now possible to instruct the DHCP agent not to supply any DNS server address to their clients by setting the ``dns_nameservers`` attribute for the corresponding subnet to ``0.0.0.0`` or ``::``, for IPv4 or IPv6 subnets (respectively). upgrade: - | The functionality when a subnet has its DNS server set to ``0.0.0.0`` or ``::`` has been changed with this release. The old behaviour was that each DHCP agent would supply only its own IP address as the DNS server to its clients. The new behaviour is that the DHCP agent will not supply any DNS server IP address at all. neutron-12.1.1/releasenotes/notes/overlay_ip_version-ml2-e6438b570844ef5c.yaml0000664000175000017500000000137613553660046026612 0ustar zuulzuul00000000000000--- prelude: > Properly calculate overlay (tunnel) protocol overhead for environments using IPv4 or IPv6 endpoints. The ML2 plug-in configuration file contains a new configuration option, 'overlay_ip_version', in the '[ml2]' section that indicates the IP version of all overlay network endpoints. Use '4' for IPv4 and '6' for IPv6. Defaults to '4'. Additionally, all layer-2 agents must use the same IP version for endpoints. upgrade: - Define the 'overlay_ip_version' option and value appropriate for the environment. Only required if not using the Default of '4'. other: - The value of the 'overlay_ip_version' option adds either 20 bytes for IPv4 or 40 bytes for IPv6 to determine the total tunnel overhead amount. neutron-12.1.1/releasenotes/notes/dscp-qos-77ea9b27d3762e48.yaml0000664000175000017500000000072613553660046023737 0ustar zuulzuul00000000000000--- prelude: > A new rule has been added to the API that allows for tagging traffic with DSCP values. This is currently supported by the Open vSwitch QoS driver. features: - Neutron can apply a QoS rule to ports that mark outgoing traffic's type of service packet header field. - The Open vSwitch Neutron agent has been extended to mark the Type of Service IP header field of packets egressing from the VM when the QoS rule has been applied. neutron-12.1.1/releasenotes/notes/dhcp-domain-removed-cc5bc6e2129fdf7f.yaml0000664000175000017500000000026713553660046026317 0ustar zuulzuul00000000000000upgrade: - The ``dhcp_domain`` DHCP agent configuration option was deprecated in Liberty cycle, and now is no longer used. The ``dns_domain`` option should be used instead. neutron-12.1.1/releasenotes/notes/ib-dhcp-allocation-fix-a4ebe8b55bb2c065.yaml0000664000175000017500000000054313553660046026612 0ustar zuulzuul00000000000000--- fixes: - | For Infiniband support, Ironic needs to send the 'client-id' DHCP option as a number in order for IP address assignment to work. This is now supported in Neutron, and can be specified as option number 61 as defined in RFC 4776. For more information see bug `1770932 `_neutron-12.1.1/releasenotes/notes/change-oslo-db-defaults-f94df09c30767f95.yaml0000664000175000017500000000105013553660046026571 0ustar zuulzuul00000000000000--- upgrade: - | Previously, ``neutron-server`` was using configuration values for ``oslo.db`` that were different from library defaults. Specifically, it used the following values when they were not overridden in configuration files: ``max_pool_size`` = 10, ``max_overflow`` = 20, ``pool_timeout`` = 10. In this release, ``neutron-server`` instead relies on default values defined by the library itself. If you rely on old default values, you may need to adjust your configuration files to explicitly set the new values. neutron-12.1.1/releasenotes/notes/sriov_allow_use_many_nics_for_one_physnet-3570aa67a60ce6c4.yaml0000664000175000017500000000052413553660046032772 0ustar zuulzuul00000000000000--- prelude: > Several NICs per physical network can be used with SR-IOV. fixes: - The 'physical_device_mappings' of sriov_nic configuration now can accept more than one NIC per physical network. For example, if 'physnet2' is connected to enp1s0f0 and enp1s0f1, 'physnet2:enp1s0f0,physnet2:enp1s0f1' will be a valid option. neutron-12.1.1/releasenotes/notes/remove-advertise_mtu-28933264714453c4.yaml0000664000175000017500000000025513553660046026036 0ustar zuulzuul00000000000000--- upgrade: - The ``advertise_mtu`` option is removed. Now Neutron always uses all available means to advertise MTUs to instances (including DHCPv4 and IPv6 RA). neutron-12.1.1/releasenotes/notes/pluggable-ipam-is-default-15c2ee15dc5b4a7b.yaml0000664000175000017500000000111413553660046027307 0ustar zuulzuul00000000000000--- prelude: > The internal pluggable IPAM implementation -- added in the Liberty release -- is now the default for both old and new deployments. Old deployments are unconditionally switched to pluggable IPAM during upgrade. Old non-pluggable IPAM is deprecated and removed from code base. upgrade: - During upgrade 'internal' ipam driver becomes default for 'ipam_driver' config option and data is migrated to new tables using alembic migration. deprecations: - The non-pluggable ipam implementatios is deprecated and will be removed in Newton release cycle. neutron-12.1.1/releasenotes/notes/add-tags-to-core-resources-b05330a129900609.yaml0000664000175000017500000000035713553660046026773 0ustar zuulzuul00000000000000--- prelude: > Add tag mechanism for network resources features: - Users can set tags on their network resources. - Networks can be filtered by tags. The supported filters are 'tags', 'tags-any', 'not-tags' and 'not-tags-any'. neutron-12.1.1/releasenotes/notes/deprecate-supported_pci_vendor_devs-12279b70a1f1fe8e.yaml0000664000175000017500000000047713553660046031461 0ustar zuulzuul00000000000000--- deprecations: - The 'supported_pci_vendor_devs' option is deprecated in Newton and will be removed in Ocata. The validation of supported pci vendors is done in nova-scheduler through the pci_passthrough_whitelist option when it selects a suitable hypervisor, hence the option is considered redundant. neutron-12.1.1/releasenotes/notes/remove-driver-60eb7e26d95f7322.yaml0000664000175000017500000000037213553660046024761 0ustar zuulzuul00000000000000--- prelude: > OFAgent has been removed in the Newton cycle. other: - The Openflow Agent(OFAgent) mechanism driver and its agent have been removed in favor of OpenvSwitch mechanism driver with "native" of_interface in the Newton cycle. ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-12.1.1/releasenotes/notes/metering-iptables-driver-load-interface-driver-ca397f1db40ec643.yamlneutron-12.1.1/releasenotes/notes/metering-iptables-driver-load-interface-driver-ca397f1db40ec643.ya0000664000175000017500000000045113553660046033036 0ustar zuulzuul00000000000000--- other: - | The metering agent iptables driver can now load its interface driver by using a stevedore alias in the ``metering_agent.ini`` file. For example, ``interface_driver = openvswitch`` instead of ``interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver`` neutron-12.1.1/releasenotes/notes/rm-notify-entry-points-aa442134a780469a.yaml0000664000175000017500000000054113553660046026467 0ustar zuulzuul00000000000000--- prelude: > oslo.messaging.notify.drivers entry points are deprecated other: - The oslo.messaging.notify.drivers entry points that were left in tree for backward compatibility with Icehouse are deprecated and will be removed after liberty-eol. Configure notifications using the oslo_messaging configuration options in neutron.conf. neutron-12.1.1/releasenotes/notes/keepalived-state-change-server-threads-9ed775e7533dd1a0.yaml0000664000175000017500000000101113553660046031647 0ustar zuulzuul00000000000000--- upgrade: - A new option ``ha_keepalived_state_change_server_threads`` has been added to configure the number of concurrent threads spawned for keepalived server connection requests. Higher values increase the CPU load on the agent nodes. The default value is half of the number of CPUs present on the node. This allows operators to tune the number of threads to suit their environment. With more threads, simultaneous requests for multiple HA routers state change can be handled faster. neutron-12.1.1/releasenotes/notes/linuxbridge_vxlan_arp_responder-e9ea91552e1b62a7.yaml0000664000175000017500000000211313553660046030710 0ustar zuulzuul00000000000000--- upgrade: - When using ML2 and the Linux Bridge agent, the default value for the ARP Responder under L2Population has changed. The responder is now disabled to improve compatibility with the allowed-address-pair extension and to match the default behavior of the ML2 OVS agent. The logical network will now utilize traditional flood and learn through the overlay. When upgrading, existing vxlan devices will retain their old setup and be unimpacted by changes to this flag. To apply this to older devices created with the Liberty agent, the vxlan device must be removed and then the Mitaka agent restarted. The agent will recreate the vxlan devices with the current settings upon restart. To maintain pre-Mitaka behavior, enable the arp_responder in the Linux Bridge agent VXLAN config file prior to starting the updated agent. fixes: - The Linuxbridge agent now supports the ability to toggle the local ARP responder when L2Population is enabled. This ensures compatibility with the allowed-address-pairs extension. closes bug 1445089 neutron-12.1.1/releasenotes/notes/use-keystoneauth-24f309566001a16b.yaml0000664000175000017500000000020113553660046025314 0ustar zuulzuul00000000000000--- upgrade: - Neutron depends on keystoneauth instead of keystoneclient. features: - Neutron can interact with keystone v3. neutron-12.1.1/releasenotes/notes/fip-qos-52926bce81c3f8bb.yaml0000664000175000017500000000020113553660046023674 0ustar zuulzuul00000000000000--- features: - Implementation of floating IP QoS. A new parameter ``qos_policy_id`` was added to floating IP related API. neutron-12.1.1/releasenotes/notes/add-wsgi-script-support-e611fa5b5c2043a5.yaml0000664000175000017500000000021513553660046026735 0ustar zuulzuul00000000000000--- features: - Neutron API can now be managed by a ``mod_wsgi`` compatible web server (e.g. ``apache2`` (``httpd``), ``nginx``, etc.) neutron-12.1.1/releasenotes/notes/timestamp_format_change-73eda78566b4690b.yaml0000664000175000017500000000115313553660046027055 0ustar zuulzuul00000000000000--- features: - The ``created_at`` and ``updated_at`` resource fields now include a timezone indicator at the end. Because this is a change in field format, the old ``timestamp_core`` extension has been removed and replaced with a ``standard-attr-timestamp`` extension. upgrade: - The ``timestamp_core`` extension has been removed and replaced with the ``standard-attr-timestamp`` extension. Resources will still have timestamps in the ``created_at`` and ``updated_at`` fields, but timestamps will have time zone info appended to the end to be consistent with other OpenStack projects. neutron-12.1.1/releasenotes/notes/QoS-for-linuxbridge-agent-bdb13515aac4e555.yaml0000664000175000017500000000115713553660046027206 0ustar zuulzuul00000000000000--- prelude: > The LinuxBridge agent now supports QoS bandwidth limiting. features: - The LinuxBridge agent can now configure basic bandwidth limiting QoS rules set for ports and networks. It introduces two new config options for LinuxBridge agent. First is 'kernel_hz' option which is value of host kernel HZ setting. It is necessary for proper calculation of minimum burst value in tbf qdisc setting. Second is 'tbf_latency' which is value of latency to be configured in tc-tbf setting. Details about this option can be found in `tc-tbf manual `_. neutron-12.1.1/releasenotes/notes/sorting-pagination-extensions-e66e99e2a8f5e563.yaml0000664000175000017500000000041613553660046030302 0ustar zuulzuul00000000000000--- features: - New API extensions, 'sorting' and 'pagination', have been added to allow API users to detect if sorting and pagination features are enabled. These features are controlled by ``allow_sorting`` and ``allow_pagination`` configuration options. neutron-12.1.1/releasenotes/notes/remove_max_fixed_ips_per_port-64f1fb36748d5756.yaml0000664000175000017500000000020113553660046030221 0ustar zuulzuul00000000000000--- upgrade: - The ``max_fixed_ips_per_port`` configuration option was deprecated in the Newton cycle and removed in Pike. neutron-12.1.1/releasenotes/notes/deprecate-router_id-34aca9ea5ee9e789.yaml0000664000175000017500000000013713553660046026351 0ustar zuulzuul00000000000000--- upgrade: - The router_id option is deprecated and will be removed in the Newton release. neutron-12.1.1/releasenotes/notes/rename-ovs-vsctl-timeout-9df1967c47f394c0.yaml0000664000175000017500000000034713553660046027100 0ustar zuulzuul00000000000000--- deprecations: - | The ``ovs_vsctl_timeout`` option is renamed into ``ovsdb_timeout`` to reflect that it's not specific to ``vsctl`` implementation of ``ovsdb_interface``. It is also moved under ``[OVS]`` section. neutron-12.1.1/releasenotes/notes/1500-default-segment-mtu-54e2cf6aea9602d5.yaml0000664000175000017500000000174113553660046026577 0ustar zuulzuul00000000000000--- prelude: > The ML2 plug-in supports calculating the MTU for networks that are realized as flat or VLAN networks, by consulting the 'segment_mtu' option. Prior to Mitaka, 'segment_mtu' defaults to 0 which disables this feature. This creates slightly confusing API results when querying Neutron networks, since the plugins that support the MTU API extension would return networks with the MTU equal to zero. Networks with an MTU of zero make little sense, since nothing could ever be transmitted. In Mitaka, 'segment_mtu' now defaults to 1500 which is the standard MTU for Ethernet networks in order to improve the "out of box" experience for typical deployments. features: - In Mitaka, queries to the Networking API for network objects will now return network objects that contain a sane MTU value. upgrade: - Operators using the ML2 plug-in with existing data may need to perform a database migration to update the MTU for existing networks neutron-12.1.1/releasenotes/notes/enhance-tags-1f8915fe3e074069.yaml0000664000175000017500000000015013553660046024446 0ustar zuulzuul00000000000000--- features: - Resource tag mechanism now supports subnet, port, subnetpool and router resources.neutron-12.1.1/releasenotes/notes/add-tag-all-standardattr-resources-6f757cb39cc1dcfe.yaml0000664000175000017500000000071113553660046031242 0ustar zuulzuul00000000000000--- features: - The resource tag mechanism is refactored so that the tag support for new resources can be supported easily. The resources with tag support are network, subnet, port, subnetpool, trunk, floatingip, policy, security_group, and router. deprecations: - Users can use 'tagging' extension instead of the 'tag' extension and 'tag-ext' extension. Those extensions are now deprecated and will be removed in the Queens release.././@LongLink0000000000000000000000000000017400000000000011217 Lustar 00000000000000neutron-12.1.1/releasenotes/notes/fix-deferred-alloction-when-new-mac-in-same-request-as-binding-data-2a01c1ed1a8eff66.yamlneutron-12.1.1/releasenotes/notes/fix-deferred-alloction-when-new-mac-in-same-request-as-binding-dat0000664000175000017500000000076713553660046033504 0ustar zuulzuul00000000000000--- fixes: - | Fixes an issue causing IP allocation on port update to fail when the initial IP allocation was deferred due to lack of binding info. If both the port mac_address and binding info (binding_host_id) were updated in the same request, the fixed_ips field was added to the request internally. The code to complete the deferred allocation failed to execute in that case. (For more information see bug `1811905 `_.) neutron-12.1.1/releasenotes/notes/deprecate-implicit-service-providers-loading-703f984b90351bf0.yaml0000664000175000017500000000053613553660046032737 0ustar zuulzuul00000000000000--- deprecations: - Neutron controller service currently allows to load ``service_providers`` options from some files that are not passed to it via --config-dir or --config-file CLI options. This behaviour is now deprecated and will be disabled in Ocata. Current users are advised to switch to aforementioned CLI options. neutron-12.1.1/releasenotes/notes/ovs-ipv6-tunnel-endpoints-f41b4954a04c43f6.yaml0000664000175000017500000000055413553660046027161 0ustar zuulzuul00000000000000--- prelude: > Support for IPv6 addresses as tunnel endpoints in OVS. features: - The local_ip value in ml2_conf.ini can now be set to an IPv6 address configured on the system. other: - Requires OVS 2.5+ version or higher with linux kernel 4.3 or higher. More info at `OVS github page `_. neutron-12.1.1/releasenotes/notes/deprecate-advertise-mtu-51e3f78475a14efc.yaml0000664000175000017500000000106713553660046027000 0ustar zuulzuul00000000000000--- deprecations: - The 'advertise_mtu' option is deprecated and will be removed in Ocata. There should be no use case to disable the feature, hence the option is considered redundant. DHCP and L3 agents will continue advertising MTU values to instances. Other plugins not using those agents are also encouraged to advertise MTU to instances. The actual implementation of MTU advertisement depends on the plugin in use, but it's assumed that at least DHCP option for IPv4 clients and Router Advertisements for IPv6 clients is supported. neutron-12.1.1/releasenotes/notes/add-enable-dvr-knob-636268f775bb4569.yaml0000664000175000017500000000073213553660046025544 0ustar zuulzuul00000000000000--- features: - | Allow to configure ``router`` service plugin without ``dvr`` API extension loaded and exposed. To achieve that, set the new ``enable_dvr`` option to ``False`` in ``neutron.conf`` file. upgrade: - | Consider setting ``enable_dvr`` to ``False`` in ``neutron.conf`` file if your setup doesn't support DVR. This will make Neutron stop advertising support for the ``dvr`` API extension via its ``/v2.0/extensions`` API endpoint. neutron-12.1.1/releasenotes/notes/fip-janitor-53f0d42a7471c5ed.yaml0000664000175000017500000000065313553660046024465 0ustar zuulzuul00000000000000--- other: - Due to changes in internal L3 logic, a server crash/backend failure during FIP creation may leave dangling ports attached on external networks. These ports can be identified by a ``PENDING`` ``device_id`` parameter. While those ports can also be removed by admins, the ``neutron-server`` service will now also trigger periodic (approximately once in 10 minutes) cleanup to address the issue. neutron-12.1.1/releasenotes/notes/netns_cleanup_kill_procs-af88d8c47c07dd9c.yaml0000664000175000017500000000063713553660046027506 0ustar zuulzuul00000000000000--- features: - A new mechanism has been added to the ``neutron-netns-cleanup`` tool that allows to kill processes listening on any ``Unix`` or network socket within a namespace. The new mechanism will try to kill those processes gracefully using the ``SIGTERM`` signal and, if they refuse to die, then the ``SIGKILL`` signal will be sent to each remaining process to ensure a proper cleanup. neutron-12.1.1/releasenotes/notes/qos-for-router-gateway-02340f7aa8be3b0d.yaml0000664000175000017500000000031213553660046026641 0ustar zuulzuul00000000000000--- features: - | Network QoS policies are now supported for network:router_gateway ports. Neutron QoS policies set on an external network now apply to external router ports (DVR or not). neutron-12.1.1/releasenotes/notes/add-keepalived-vrrp-healt-check-f23ed7c853151484.yaml0000664000175000017500000000103513553660046030102 0ustar zuulzuul00000000000000--- features: - Keepalived VRRP health check functionality to enable verification of connectivity from the "master" router to all gateways. Activation of this feature enables gateway connectivity validation and rescheduling of the "master" router to another node when connectivity is lost. If all routers lose connectivity to the gateways, the election process will be repeated round-robin until one of the routers restores its gateway connection. In the mean time, all of the routers will be reported as "master". neutron-12.1.1/releasenotes/notes/removed_prevent_arp_spoofing-b49e91a92a93e3e1.yaml0000664000175000017500000000032513553660046030221 0ustar zuulzuul00000000000000--- upgrade: - | The deprecated ``prevent_arp_spoofing`` option has been removed and the default behavior is to always prevent ARP spoofing unless port security is disabled on the port (or network). neutron-12.1.1/releasenotes/notes/dnsmasq-local-service-c8eaa91894a7d6d4.yaml0000664000175000017500000000057013553660046026533 0ustar zuulzuul00000000000000--- fixes: - | Fixes bug `1501206 `_. This ensures that DHCP agent instances running dnsmasq as a DNS server can no longer be exploited as DNS amplifiers when the tenant network is using publicly routed IP addresses by adding an option that will allow them to only serve DNS requests from local networks. neutron-12.1.1/releasenotes/notes/Adds-http_proxy_to_wsgi-middleware-24e8271cbd94ffdf.yaml0000664000175000017500000000107113553660046031351 0ustar zuulzuul00000000000000--- features: - Middleware was added to parse the ``X-Forwarded-Proto`` HTTP header or the Proxy protocol in order to help Neutron respond with the correct URL references when it's put behind a TLS proxy such as ``haproxy``. This adds ``http_proxy_to_wsgi`` middleware to the pipeline. This middleware is disabled by default, but can be enabled via a configuration option in the ``[oslo_middleware]`` group. upgrade: - The ``api-paste.ini`` configuration file for the paste pipeline was updated to add the ``http_proxy_to_wsgi`` middleware. ././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000neutron-12.1.1/releasenotes/notes/allow-non-admins-to-define-external-extra-routes-0d541fc356a5c546.yamlneutron-12.1.1/releasenotes/notes/allow-non-admins-to-define-external-extra-routes-0d541fc356a5c546.0000664000175000017500000000011313553660046032576 0ustar zuulzuul00000000000000--- features: - Allow non-admin users to define "external" extra-routes. neutron-12.1.1/releasenotes/notes/add-integration-with-external-dns-f56ec8a4993b1fc4.yaml0000664000175000017500000000125413553660046030763 0ustar zuulzuul00000000000000--- prelude: > Support integration with external DNS service. features: - Floating IPs can have dns_name and dns_domain attributes associated with them - Ports can have a dns_name attribute associated with them. The network where a port is created can have a dns_domain associated with it - Floating IPs and ports will be published in an external DNS service if they have dns_name and dns_domain attributes associated with them. - The reference driver integrates neutron with designate - Drivers for other DNSaaS can be implemented - Driver is configured in the default section of neutron.conf using parameter 'external_dns_driver' neutron-12.1.1/releasenotes/notes/add-designate-driver-ssl-options-169c299c96f2aff0.yaml0000664000175000017500000000130413553660046030530 0ustar zuulzuul00000000000000--- prelude: > Add options to designate external dns driver of neutron for SSL based connections. This makes it possible to use neutron with designate in scenario where endpoints are SSL based. Users can specify to skip cert validation or specify path to a valid cert in [designate] section of neutron.conf file. features: - Two new options are added to `[designate]` section to support SSL. - First option `insecure` allows to skip SSL validation when creating a keystone session to initate a designate client. Default value is False, which means to always verify connection. - Second option `ca_cert` allows setting path to a valid cert file. Default is None. neutron-12.1.1/releasenotes/notes/ingress-bandwidth-limit-in-linuxbridge-agent-50a2dad610401474.yaml0000664000175000017500000000022613553660046032620 0ustar zuulzuul00000000000000--- features: - Linuxbridge L2 agent supports ingress bandwidth limit. The linuxbridge L2 agent now supports bi-directional bandwidth limiting. neutron-12.1.1/releasenotes/notes/deprecate-ivs-interface-driver-b68e06a470c65ccb.yaml0000664000175000017500000000016313553660046030301 0ustar zuulzuul00000000000000--- deprecations: - | The ``ivs`` interface driver is deprecated in Queens and will be removed in Rocky. neutron-12.1.1/releasenotes/notes/add-subnet-service-types-bc81f6df9834f96e.yaml0000664000175000017500000000120013553660046027167 0ustar zuulzuul00000000000000--- features: - Subnets now have a new property 'service_types'. This is a list of port device owners, such that only ports with a matching device owner will be given an IP from this subnet. If no matching service subnet exists for the given device owner, or no service subnets have been defined on the network, the port will be assigned an IP from a subnet with no service-types. This preserves backwards compatibility with older deployments. upgrade: - A new table 'subnet_service_types' has been added to cater for this feature. It uses the ID field from the 'subnets' table as a foreign key. neutron-12.1.1/releasenotes/notes/add-ip-protocols-in-sg-60467a073e771aee.yaml0000664000175000017500000000040113553660046026351 0ustar zuulzuul00000000000000--- prelude: > Add popular IP protocols to the security group code. End-users can specify protocol names instead of protocol numbers in both RESTful API and python-neutronclient CLI. upgrade: - Add popular IP protocols to security group code. neutron-12.1.1/releasenotes/notes/Dscp-marking-for-linuxbridge-agent-e765d0d934fa4017.yaml0000664000175000017500000000011613553660046030673 0ustar zuulzuul00000000000000--- features: - The Linux Bridge agent now supports QoS DSCP marking rules. neutron-12.1.1/releasenotes/notes/dvr_handle_unbound_floatingip_port-f12ae806b8be2065.yaml0000664000175000017500000000151213553660046031354 0ustar zuulzuul00000000000000--- features: - | Floating IPs associated with an unbound port with DVR routers will not be distributed, but will be centralized and implemented in the SNAT namespace of the Network node or ``dvr_snat`` node. Floating IPs associated with allowed_address_pair port IP and are bound to multiple active VMs with DVR routers will be implemented in the SNAT namespace in the Network node or ``dvr_snat`` node. This will address VRRP use cases. More information about this is captured in `bug 1583694 `__. issues: - | While the bound port Floating IPs are distributed, the unbound port Floating IPs are centralized. fixes: - | Allows the unbound port Floating IPs to be configured properly with DVR routers irrespective of its device_owner. neutron-12.1.1/releasenotes/notes/remove-router_id-b3732089f8f1faa1.yaml0000664000175000017500000000057013553660046025533 0ustar zuulzuul00000000000000--- prelude: > Remove 'router_id' configuration option from the l3_agent.ini file. 'router_id' option has been defined in order to associate an l3-agent to a specific router when use_namespaces=False. It was deprecated after use_namespaces was removed in Mitaka release. upgrade: - Remove 'router_id' configuration option from the l3_agent.ini file. neutron-12.1.1/releasenotes/notes/dhcp-lease-time-5c504c3730a4f9ea.yaml0000664000175000017500000000027213553660046025177 0ustar zuulzuul00000000000000--- upgrade: - The configuration option dhcp_lease_time was deprecated in the Havana cycle. This option is no longer supported. The option was replaced by dhcp_lease_duration. neutron-12.1.1/releasenotes/notes/metering-driver-stevedore-alias-2c4fdb0556205a3a.yaml0000664000175000017500000000041613553660046030414 0ustar zuulzuul00000000000000--- features: - The metering agent driver can now be specified with a stevedore alias in the ``metering_agent.ini`` file. For example, ``driver = iptables`` instead of ``driver = neutron.services.metering.iptables.iptables_driver:IptablesMeteringDriver``. neutron-12.1.1/releasenotes/notes/allow_port_create_update_shared_owners-2a57b1c72d91ace2.yaml0000664000175000017500000000036013553660046032302 0ustar zuulzuul00000000000000--- features: - | Tenants who can access shared networks, can now create/update ports on a specified subnet instead of the default subnet. This is now the default behavior and can be changed by modifying policy.json file. neutron-12.1.1/releasenotes/notes/change_external_network_bridge_default-5de3a0c19182eb70.yaml0000664000175000017500000000162613553660046032157 0ustar zuulzuul00000000000000--- prelude: > The default value for 'external_network_bridge' in the L3 agent is now ''. upgrade: - The default value for 'external_network_bridge' has been changed to '' since that is the preferred way to configure the L3 agent and will be the only way in future releases. If you have not explicitly set this value and you use the L3 agent, you will need to set this value to 'br-ex' to match the old default. If you are using 'br-ex', you should switch to '', ensure your external network has a flat segment and ensure your L2 agent has a bridge_mapping entry between the external network's flat segment physnet and 'br-ex' to get the same connectivity. If the external network did not already have the flat segment, you will need to detach all routers from the external networks, delete the incorrect segment type, add the flat segment, and re-attach the routers. neutron-12.1.1/releasenotes/notes/web_framework_removed-6e4c5c7ca506523a.yaml0000664000175000017500000000030313553660046026604 0ustar zuulzuul00000000000000--- upgrade: - | The web_framework option has been removed. This should have no impact on operators/users since it was just an option used for development of the new web framework. neutron-12.1.1/releasenotes/notes/config-file-generation-2eafc6602d57178e.yaml0000664000175000017500000000042413553660046026561 0ustar zuulzuul00000000000000--- prelude: > Core configuration files are automatically generated. features: - Neutron no longer includes static example configuration files. Instead, use tools/generate_config_file_samples.sh to generate them. The files are generated with a .sample extension. neutron-12.1.1/releasenotes/notes/deprecate-network-device-mtu-59b78264c9974808.yaml0000664000175000017500000000034313553660046027460 0ustar zuulzuul00000000000000--- deprecations: - The 'network_device_mtu' option is deprecated and will be removed in the 'Newton' cycle. Please use the system-wide segment_mtu setting which the agents will take into account when wiring VIFs.neutron-12.1.1/releasenotes/notes/add_dhcp_dnsmasq_t1t2_options-3cef427d8109c165.yaml0000664000175000017500000000105713553660046030071 0ustar zuulzuul00000000000000--- features: - | Allow configuration of DHCP renewal (T1) and rebinding (T2) timers in ``neutron-dhcp-agent``. By allowing these timers to be set (options 58 and 59 as per RFC2132) in ``dnsmasq`` it allows users to change other parameters, like MTU, on instances without having to wait for the lease time to expire. The advantage of changing T1 over the lease time is that if the DHCP server becomes unreachable within the lease time, instances will not drop their IP addresses and it will not cause a dataplane disruption. neutron-12.1.1/releasenotes/notes/fix-ovsdb-ssl-connection-4058caf4fdcb33ab.yaml0000664000175000017500000000053313553660046027307 0ustar zuulzuul00000000000000--- features: - | Neutron agents now support SSL connections to OVSDB server. To enable an SSL based connection, use an ``ssl`` prefixed URI for the ``ovsdb_connection`` setting. When using SSL it is also required to set new ``ovs`` group options which include ``ssl_key_file``, ``ssl_cert_file``, and ``ssl_ca_cert_file``. neutron-12.1.1/releasenotes/notes/add-rbac-qos-8b1154ee756c66df.yaml0000664000175000017500000000021013553660046024502 0ustar zuulzuul00000000000000--- prelude: > RBAC support for QoS policies features: - Neutron now supports sharing of QoS policies between a subset of tenants. neutron-12.1.1/releasenotes/notes/add-placement-api-configuration-options-f1611d0909bf6166.yaml0000664000175000017500000000122413553660046031704 0ustar zuulzuul00000000000000--- features: - Add a new configuration section, ``[placement]``, with two new options that allow to make ``segments`` plugin to use the ``Compute`` placement ReST API. This API allows to influence node placement of instances based on availability of IPv4 addresses in routed networks. The first option, `region_name`, indicates the placement region to use. This option is useful if keystone manages more than one region. The second option, `endpoint_type`, determines the type of a placement endpoint to use. This endpoint will be looked up in the keystone catalog and should be one of ``public``, ``internal`` or ``admin``. neutron-12.1.1/releasenotes/notes/linuxbridge-vxlan-udp-ports-73b260efefa15a46.yaml0000664000175000017500000000051513553660046027714 0ustar zuulzuul00000000000000--- features: - UDP ports used by VXLAN in the LinuxBridge agent can be configured now with the VXLAN.udp_srcport_min, VXLAN.udp_srcport_max and VXLAN.udp_dstport config options. To use the IANA assigned port number, set VXLAN.udp_dstport to 4789. The default is not changed from the Linux kernel default 8472. neutron-12.1.1/releasenotes/notes/common-agent-extension-api-3fd06ff67329200a.yaml0000664000175000017500000000075613553660046027334 0ustar zuulzuul00000000000000--- features: - | L2 agents based on ``ML2`` ``_common_agent`` have now the L2 extension API available. This API can be used by L2 extension drivers to request resources from the L2 agent. It is used, for example, to pass an instance of the ``IptablesManager`` to the ``Linuxbridge`` L2 agent ``QoS extension driver``. fixes: - | Fixes bug 1736674, security group rules are now properly applied by ``Linuxbridge L2 agent`` with ``QoS extension driver`` enabled. neutron-12.1.1/releasenotes/notes/network_link_prefix-e3fe37e37ea275b7.yaml0000664000175000017500000000045313553660046026420 0ustar zuulzuul00000000000000--- features: - | A new ``network_link_prefix`` configuration option is introduced that allows to alter the domain returned in the URLs included in the API responses. It behaves the same way as the ``compute_link_prefix`` and ``glance_link_prefix`` options do for Nova and Glance. neutron-12.1.1/releasenotes/notes/404-for-quota-tenant-2c09c16759269b21.yaml0000664000175000017500000000023213553660046025533 0ustar zuulzuul00000000000000--- features: - | Return code for `quota delete` for a tenant whose quota has not been previously defined has been changed from 204 to 404. neutron-12.1.1/releasenotes/notes/linuxbridge-agent-extensions-66bdf9feee25ef99.yaml0000664000175000017500000000033313553660046030320 0ustar zuulzuul00000000000000--- prelude: > The Linuxbridge agent now supports l2 agent extensions. features: - The Linuxbridge agent can now be extended by 3rd parties using a pluggable mechanism. fixes: - partially closes bug 1468803 neutron-12.1.1/releasenotes/notes/add-standard-attr-descriptions-1ba0d7a454c3fd8f.yaml0000664000175000017500000000051013553660046030373 0ustar zuulzuul00000000000000--- prelude: > Add description field to security group rules, networks, ports, routers, floating IPs, and subnet pools. features: - Security group rules, networks, ports, routers, floating IPs, and subnet pools may now contain an optional description which allows users to easily store details about entities. ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-12.1.1/releasenotes/notes/advertisement-intervals-for-radvd-configurable-6d85b5fdd97a2742.yamlneutron-12.1.1/releasenotes/notes/advertisement-intervals-for-radvd-configurable-6d85b5fdd97a2742.ya0000664000175000017500000000103313553660046033121 0ustar zuulzuul00000000000000--- fixes: - Prior to Mitaka, the settings that control the frequency of router advertisements transmitted by the radvd daemon were not able to be adjusted. Larger deployments may wish to decrease the frequency in which radvd sends multicast traffic. The 'min_rtr_adv_interval' and 'max_rtr_adv_interval' settings in the L3 agent configuration file map directly to the 'MinRtrAdvInterval' and 'MaxRtrAdvInterval' in the generated radvd.conf file. Consult the manpage for radvd.conf for more detailed information. neutron-12.1.1/releasenotes/notes/rename-to-nova-metadata-ip-685fd81618c16d9d.yaml0000664000175000017500000000041213553660046027213 0ustar zuulzuul00000000000000deprecations: - | The ``nova_metadata_ip`` option is deprecated and will be removed in Queens. It is deprecated in favor of the new ``nova_metadata_host`` option because it reflects better that the option accepts an IP address and also a DNS name.

7wgãð>Ôý¢"Ùcì0¬Kž€sôé.-нú$Œæq§šÅž û—w~ˆz¤ ^iûW øñ¢U¢å´ÚÍšKA ÂÒéÓª¾V˜ú-)éØO$Þæ²3˜úíN…ÕâZ}){ ØRMÒPFäð²RU‚j©Ä'kCYÛ[°–bn]2”Q÷QŸõdÚÞíPê« m?˜!jµ£W­œƒÏ¦7Ù³ŒÅÆÒšÓ}z,ˆ o÷&°ÍWãgë¿ÙÕ¶`FôÜ%zý*''ëèþQ¨QjgÇgÂëD¼L.è'¬Ü|€vR‰Gƒz~º»K§Ïb^;^m§qðií§ž:CNo2ûÂSH¿Z­ †Ó<…Í/êv6£ó¢ðäìQP‘ã *LkŠÅÈ{é﬑ު?Uyª.“»&÷W_燆ÏÌPƆë)_9®hÀÊhr†òÂ¥ÎQQ׎õˆA+/ЩÕÏDIVV«Rœ¢šiàÄËÝtX|8ž³ôÁ rÓÑX\‰iUEš¢Ê0ÉoŸíÓ‰$è;|ÔÁXÈ(ÎJLL’Ü¿ÿÔÐ={’‚nž|3æ.Ñ(|ÜÒ[=óž‘*¿f ¡ÙWµÑËÕ îÙ^ødņÖEÅ¥ÛžžÓë³yÑ`Î(—™Úùíõã<Çý@Zw“^ŸÕϳä1wsu6 •·»›Àåyy¸‘§» 1 Ä`0 :½Þ×òâ©ÓC’vÅS<™Sûwß$“TyÂái³i=*›'—Í_h_I—*kEŠl§&g(ýD `e"üõè£Iø‚WDÑ@ýi1û¿ŠdìjjAEŠ¡\wÃ1jß·PŒî…â7¯€é…¯Q¤ÁÅú’¸3½z=û{}·”9}!æÑÔ²=3æ,è (Ñòvaê§î.¸‚¥¢!KxHKzaêõâÿýå[Zª_1ÝÅxËïãÆƒ¥:Cw»‹1,¤¥*,¸%…5'^{¸¹H„µì¿#š.çäSzV¥^ΦSçÓÂOžO}¼¸¸téJ@7︑Ä5µðßϵ³ײ|åtEŠj¨&g(cpŸ(ëãüoò¶²V4P_@jãßHo|³¬þÛÀ~¡VØ/êën8N½CoMìÓë5ùÍ…¥¬ÁJKÃ*PÈ}æáçÿZàºuÖhñ³}äbH/ŠÐ‘¡J4¶… Ü3ní±`öMô„S[Cõ“mÁ¶¨f®âsæeƒ¢- ÇRôFÃÎLU–ŽðûœÔªÇ& nðF²¬ßV~4¼wGõÚÝñ§¿9o{±N‡LQhÜRìÝ9‚zthEͽ=­†»T£Œý£uiB#ûGñÇ’ú|j& '“R;&$¥tÔëŒ/Íx3&Ž¥.ïï?Ö>—+·YY+P4pãhR†òüež-I_З] ˜¾Ò¹9{«ôº|ó7®e¥EÕhàø‹c;,Š?¤5 ¿Cç Äé;ª¹D9ÔD4Pæ±]˜Ô·Ç6½‘–ÃPm…ñ‹¡3 .§;Ý«×mcc7ÕEÓæ,ìJFý­0tÇ•¦çÞ„ä'Òû˜yòtu6¶D°]K_o‰»˜ùŠ*ÀkUÙ§ ù…%¾9ù¾9yEÁWlÌ)@ڽƢHã÷$o/3£Y]šèp×0nyÍ®xu€× ~]#…~`틆íé:Ê4jÔBZÆ ìª*,.¡=ñg€¡>Õ4w_”r?˜óiT —jgp8* R4Ð5Ф eA_4¸?Ó &ÐÞÍV¬äøOÛ(›, …µHOp߀bVŠ¡ÌÊPDÒ@ؾ¸]ìQRZô¸ïàð.† dØp¦w÷%^^¾¯øoÙrÝ@¯'£çŽDá» F]þÛÄ –>Ô%2DhØœZúzI‹›‹s]<£Ò5ÀñpÍAÃÖØ¤Sx0ý륩 §¨‹~¬®wWÞ·3/ªó)—Ù`vý;þô%¥†žŒŽY&¸ o-=»QÐ Z]yJŠj¨&e(Ãc7TÖ ¦·°×CE¡¶Ê†²(ùÿ4Æ!Ú¥4Âa4Ðj÷î,4fÒ™^=î‡}û e?É»,ÒŒ¼Üì[ÏôíùD侃*6˜©ÈRu;ïUª×@ÑÖÕÍÕIªéLQ!B3ϲŒÐ/¬ão¯/U`$;d×ZÍËíC{ªÿÜqˆ¶8ñ¸X,>8-zî¿ÉxWd8ämSÕ4à_ÅvÓ“(yê¤êT¢j‹ÝêU*R4p ¸8¹n‘OÁôêÀÞKĆý$wFY[]‘±qß ×΀eü*'@Ñ`\”ØK.÷ïߌ÷¿¼h‘pÄ/¦w3ø¿Àæ^QOL‹^¸WóøCh`·¶dm#Yn²®? °!?yT?úçSwªútŽpE,άR}^âôèùw×_«”š 4\ 4CyÉ’`wx_¢øVÁ1ø‰þ»îmSZÞØ4pø¹vÉø¿Lâ~ÁSèQœ´scë£Òëi bÏž´Èqw0<ZfyÉâ´+úâ#ÿ÷ÀCÏçå–ÁìÄ{íZ>7u½9íÇ$¦Hã× 鱉7SôpØÜÇ(~„wùÛgµIR_J XGMfÄÌ2ØH6õW¤SÓ§_*´Ž •R XGðü”KB@•D7%ÿVÖŠ*Ó@Ûý—“‹{¼Ë÷¶A¥¦ÍºµÚݦÝûà-nýòC·Òó÷VuŽ©ìre_Ð@h€½úð8õíÃzlÝ‹€¿½Ó£ß•r 4î+]T4pÃh2†2ò[¡ÖœR€¢kk@EæÿKƒ(v³vñJySmwíJ‡wyòá6¯.:F¿7¼= ëÓI`rÛVˆ×S¤ÉkR4î¦nôâcTŒ$¡tÿô9ó™]GEŠ®£¦c( bY3̉¼OY+¨o ¨2' 5‹ùÿµ¾Û¥Ôïø˜¡é·6ªÏ±e Ccéž1ý <ÆŽßp¥…vÕ8E?~»¦¥óö7LŸ3Ä® P*S4Ð5Ðd ed0j-ß# gäme­hÀQ4€|§-ÚÒÆb[ÙT4P¥ØH&môóñtí‘ñšm«¾ŸE1TFbߨêÚ6D@„®Éj¸».Qzç&Çœ ¨é¥n»Ò”K ØN[–çRÞeÓÿêý†P[¿ú÷—>.9püé‹´ïhbÀ‘ÓÉ ‚ºE<÷`äkÓ¿yÿ5è WƳ" HOÎŽyÐ`4Þz÷¨¾2Utº ¾©cv¥-ûŽŠJt³Ð%)I-î¨V+ªÒh^?½nì÷ÐoÎë 'Ÿiöv _ÑÃÕÙ€l˜äîâ"¸¹9«TðBÈh4R©^/––êE%¥b~a©P\ZªÆnÒQ ]ÒïÐÃÉq÷A·;;5ûCɲX‹›d…SëËP–½È\¿óƒ/½þœ«›Ç?ƒý}Ä{F÷'äóq«‰N_d.ËIcÝt­æ‚• EVЀ+ûʤHlj|lRÿ.¼§Î§Òòµ{ü/ŠâÊG^žõÕ»1 öV(ÞåWµ]Jx~Þ¼€âbú824@8»TªTÒh5à⬡¡½;hÖ쎟ø‚ö}Ÿ´/f7ÚÎZ©cœ´¥D—;#Å0ÿÆA‚ [ú‰a!-UÁ-}(¨…7Eàçzv?Ãfˆໜ‚"ʼ’GÙy”š‘­¹–Õ%éÒåÎ0¢Ÿ*1ä”N›ó›“Jóö§s_?Z]—ØÃýâÛxëõù¾¥•¯¨]UNTJN- ¥hYN z5U«gI5Š¼Þ ¬æÒ:â ötqŠ^—ûŸåyÉo÷é.>4a°Ê”Fzƒ ›Ì-V eÖ‚"Žª7‹'²îèäª{‡P3‚úëÕ;ÄýÇÏ.|ä•ÙÂW‹æ±±ÌØ$ÅX®Zus¤¨PŒQ«¯‡ÆRŒd‡¹+ »!Ý;´¡¿vÅk u£Ð“volÛúsæß¤3æ¯T"(*"©àÛQ§ˆ ÁÍÅÙ*Ï#gÎD² ii_Î`g²¨Nº”I‡Î;ï8tjR~añ]Ó£çÍZ3ûîñÓÚmù¸î•(¸«;#¹Lx¢Cs/Í,’Îd %ªÎ4ˆíKBŒžðtx·Ï!°å¸¨¢©«ƒiµ–O¶U«C—nñZ¶[;e#ÙõŽG§ßêîÙì-6’Ÿ¸s¨UþÉ*ë…ÁPŽOV!s•"ŠUàR6‹Îh²¹À² þ•ž·_ˆåy÷Î|ñäòOßÿ ‡ùA1á>*^¤üv ÌÐ.ò7êK4MÕÒWÉDì7¥4"4À—¡°Å¨ÉPÅIÁ°¥BgàBaKb[îà‹!Xƒ‘  ŒaüáaW]†z.ÃA› %Í…ÇØùá ƒ…ÖÍív÷ªÒRZÆé©ùiÃ>Ú¼ÿøB¤%»¦N§g¦$8ÕÆ€æÞBp ¡¥¯¹»¹’‡›3¹»:ó12DÒ è•Pv^¡æJnA0–Àäô¬¾ù…%ÏŠ.‰uÓçÌÛ†×ÐjFµúsí‰v묃UdoC™d®Ó¥uëö~þÁ¡ï‡øJžd[êÅÉ©n¡Ó•Ã0lY§R¶¢ºh ¨ü›ŽÜœ,¬æºvk0ƒ#\ʼ"¦ ôY·nƒº>¼+—°u®LÃ]Gwõuب+y/tç›z´«¯&(õ6B hÔjÃPRjðo„Ý“º4svLŠ&at¤R«Š ˆF— rw12mžF ³^]`Žq(æ—ŠlP—ªï$xy¸ž›:JííYTŒÜ>ŽO8˜pÞP\ª›Ð),PÝ)<„:†> ƒ®Ó]dûL…p:—šI‰ÉNq§ÎKË̹E¯3~0cÎÍZS÷ö­iÒˆÞêËÀIÇ%$Óö¸“=€—þ¿ºø> y5sywñ+¯4ªÿ—ªthOCÙìMFc<¼›û=Ö¥m¨híÀ½Ê:ÊLµ+¦Šñœè1Ó¢#íŠ(p4 ZðyXòVm³ÌÏáÑ3ÉÌ¡ú19°O`T«9ûä@«]AÀ.¬÷V´7”P)™Ù˜’7‚‘°yµn’V»Â9Erz~nÉ[€“4jácàç¦Oç0jîíY™A\£:ÜÝ\jtž½NbcÙÖÒÜÇ‹né׉u‚Á·î?á}àø¹9ù¹º*|ué¼èÿÚº õ]¾½ eØeo²ëÍcoï Ú”Lg/qqnFú".=¿(ƒšyÛ«j¥E5Ö@fa9ê¡™‹}ì!~€6ÎÿÖ{êû×_ïBceÊ8Å«\ã;gû‹Œ…½1 졯í+SjhR8”pAê¯Z£ÙÚÐ;ÎÙ*S §~¶6¬Sxxû^Àõ¶¨³qÜÐõaÍö·‡c‹0 A…?®ßÛâLrúWÓæÄÜFê–,ÕNgnþF)¶ÿ1©M6”ÙëÖ2$t€'ÙnJõó 3ו•Ûd1éf(ާ´|‘ŠÙŸ ñuÈÓe®‹ŸC~}Z´‰ŸüŒòKÅ>V:*R¤†E ˜ìïçxA|ì8DÓ¯Ô°#ÕŸVRª£I)ÕŸt£ÖlÏuª²éaîGJ†mÙÚô Þ¼ÿ„¸ÝmŸiß8mÓÙ¸p@fõ´Ã§™G«—‹4Ý£É6®µéÜ‚^yxœêÎ[ú0ÌûnÒgîž¶p!’ª4N±—¡Ìõ˜=ÊNÎ.!^n."ó»ÚKš{Gš«ºœ{Ƽ­l(p œÍ.G;DøØÏÂÏ!?*µ¦5tÁ1\¹½ÆGQ¿Ã·CÊð…VúûyÙ½­çS.ÓŒy_IË3ï|Co/YIÇ›ÆQ¦JX½õ %[ÉP>šx‰>^¾žtú²¯ÆZö¶ºöÈýxç«ß¯*ÉxèÅ}wÕ¾ª~°_\bu[Õ‰7¸¿¼Y7XRõ—¯Þvrò Ô‚Ñøvõg:öÑiÑóþNãÏ»·k¥™óøµµšc7¼·nô€.ôì½# @ºúïð?Û(,özr= óà—°‹ÚIãïãíaW…úy·5ÿ;^În0,s”Æ¡Seù¸7¾öz4Mºãçi®ð‹ŸQ~VíÛS3”¿Õi@¥|æVŒÛ©®¶J=1i(Íœr …øûÒ÷kv{­-½:¶¡÷ÿq¨¬øßÐúÂú;Ã÷áÚ;O™V닟61¥–õV%Æ ëFXöEÌœMõЫT9ý͘‰ˆC~·Oçpš1y„à*4Eì£dR¦©c¨ð‘2nFô¼)ö©Õ¾µØf$ººlóÂ^*®ÏY­R»»:Ù˜û YJ€oóϳ)ÛÍÛöÚ`ïþ‘@Áã˜A„Ü>¦ªmô,¿(ϯĴ4µžNŒÅ =î¦nµª“§ ™§¶í¬MÛêëÜÝËŽN-íñh–÷”ŸGÔ{bWÌϪüÜ*8år5Õï–JHF42]É-ª¯2wžaÌêïGû¥Øçh@×HI/—sòiÁ²ß)+·€zw £;†õ¢Ü‚búð»µtÿ¸Ä/T–µ»ã‘Ný,Í~|­‚':öD<´zŠ mIŒD§.¤ÓŠu{èŸOÝ)=ëhöýš¿%ãXtÇð^Ô­]+ÚràmÚ{Œrò‹¨3ÜŠ±„yf¯'p€Ó°>è×ͨg‡6äêr혜š™Cÿûs]H»,õyêØè³/<Ý$oòûè“n´×O ÿïwzhüMÔcàIx›¿þ}'Ý:¸ îÑž˜1à½ÿ­¥éw—²µý¼a¿4îqú"°Œ§®yÜeŒð®C tsÏŽôúÞ­](Ý=¡<riè—¯ý›x3tuã,nGÎ\¤¯ÜjÄXºÏÝÏ틪Ԧ4åŸoøo8àLÜ ÚÞX;¸g{$¬9lijÿ úôCcé—Ü{y¸~ùò‚±ýÝó­üûÀCaâ=¼’—„‘ɲlº>–x‘Þ^º’ž{÷ôü¢o)úÓŸéÀñ$›ÖY›Â/e\¡¾_O/.þž^Xü-½÷ÍLÃÕÜ[Rqš4=+¯FXE›KÓµLz^SaïOûþ°vOM/¹¡óì5ÅÊÔC {’˧šonm_CÙ¤(é¹”ŸS{ 7tšÒÅ‚Q”­ì¼úgd:—zYR½«s¹çnÓÞ£ÄÚ‡ö¤í±'éЩ ’Aïæê$Æò½bÈFh¹Dkv¦É£úÓ#·–¨º˜U T§#6ºÁÍL ô ŒS$A ûo@üBæôÀ,œ½lÒð>ô`QAmFl“«¸îzìÀ.p\«¼æ›?v·ûùûÆPHK“÷œúF…KeOÞ[j³—‡+ŒyÁŒ©FP,éàuŽGßYNžK¥|‘³þ·-±´<]=õÂÂ}>~ö}÷×.|d´‘ô((ûsú^úËfÞ·³UŒd$ª OØ  'ª]Æ¿ÿÒK 7Á@¾á£(6»Ü ˜Û,Õ¦lÛQm›ã ºÙ±J»Ue—¡ì™’½Êj|Mó¶]…éà‚n2×yúÂó¶­6’Ó²$#”½/ÑOL”<$=:´¢/ÙB ã²Uµµ*÷í‡0HëéÇÆcJu$u'ÃÛ«æDê§IÿØ~ ­©H‰jxòÞ#‰OÖñ³’w¾†—Õé4{O±î»¤#™.ÌGE¡Íì?à—á˸bËg¶NúS.²¾D'M—z!íŠõ ¯a‰Œg]ôß?éóaØ6§®íL^b¾¼Gû64²gɘåcÇÊÆ¸ÝÚƒ‡õ¼dô¦gåJÁiýºFžìY= Ê);¨ë5­8 cšæ»Gö¡~]"iDßNœPA:½Ê=;¶f\b<ê1šŒë­‰8C|â°Þ´ißqJ»œ‹ÿøòW'—H¼˜pòrw¡ÝÚÒ…Ô,À- ¨Æ6ô™Z‘…=œ:Ÿ&ms†öîH çÒ¤¶ðþv­¤¾î>|F2´Gö‚ž¢$¯û. øG©ÎÀ¼µð˜÷–ô!ˆ?ùhÏ'?l€·¹DÓ%ï¯Ë:'¿òF‘ èòï^ÞÎC–j_άKYŽr (ížä÷*Œ(R?àÇÁ¼˜4kþò¯Ÿ¦Ö©V{¹­,_¼ö0Î+UFdÈ-˜Ö[/;˜ð õîøH¥çYkçš]ñ îv—<2ßáÝ#ûÑé ô×ÎxŠŠ •Sö2ßuK_É»’©ºÞÃ8„4íȈëöJÆ'-€G=,ßýµ›ü0y A4G๎Ĵãm7wÇ {õôãß®•¦$'b*”…§9Xæ±;†H°‰AxȃŒÁÞ‹æÍ<é.LÿñËçŸ_þF~Þ4Sˆ,ŸàúLÿ…à#@ž&•¦T1Ëð’݇NÓ½càEJ•Mr ŸØšþ„±Î˜2~Á éÕ]#E%¥tÞšw<+GÑß®mC¥óø%Æž©crvÒpf%j…éankeS¨Ð÷‰õÎ÷ã¯]‡0ébè}"¦uù^Uœb÷ÌÝ×´Éš;¾‹/57,ìÚ©`óAÛoX>«¶¯M©¡ÆXª}ýüŒ9óü}øt/pšò}²»ðsÃ0«)£ûQ‹,²qÊÆ±,^ÈpV‚p–þ]"è—û$Cö Wœài¦Xî»u Ø›@†t—<¦Ò²?ü\²Èu•í–V+Ÿ«äf8CÛxa2àšC#¶¾ÁPÞ–ådRªÔ§ÿ®Þ!ïBú_+fƒVÔÕ¼OÞ`ÈÅθSä„gè^€zýßGÉÄXŸ.küá͸<Îòµltï8˜ ¥–˧¹¼i^¯ÞGœèB6ÌÍj±Áï‘õ{ŽaœŒ7èõz†Þ——Ìþ´E8ä©3çÏo^Z`‰lÅáŠÔ—øý›–•«BÖ–…õÕ[ÖkO£µÞ_ÀÃ&Âb&QfÎ)JJ)m¡d¦KêØ&H2¼,Ë·#ÝòHfö˜ü°~d$ÞCx×áSÒ´%_³-öÅ›öÈ„!x‘ôÄ g?e\Âø?~Y õ(=tÛ i f:¡ŠÂx6KÏÅITŠ3uö­€<‚€Ž#WE™³Q|èÔy©¨¤K—¥A>þôÉûÌß ¹`#Ùrš”=<˜:eÜO¥2-OuS§ü‚Ý— ãŒ{äVUÑä…tFpOçˆ`É@fï²,¿m9 È ž,çýOÞ9T:\Ùª|ëýÛ¿vâE‰éâ^´/-Æ ²¡\qŠU¾ÆëKyFÚt¶ÜPžÜÙ~l0•ôG~N+9¤ìªo ް¸YÙêCnÇs2ÐŠÊ ×ªâ/˜ºµ•à ŸêW_àöó‡1²‹Qàœ€C b¿xM‡jf#Kç²ùÞ1ýñ}FüE¹h`–Mþ£îMïýcªy鎱²2éH`vnÂ3^ Ç`ÌôaŒ%l+<åŽÝ¯A€_0~7 ¿<±$BÂÁo< Ô¯,ùTEc›gذ–=V€H0Ž—!mY9&ƒ‘½Óü<›µÔ R3%°GX¤7ƸíJî<$•Í|cá`Fž ã eìåå}ƒ[¶èòxœdé,ýöòp3{‘»`ñàÉóÒ—€qŽ·»µk-_Ý6ÄïѸ}µªnÎ[úG <Öy¸9Ðä9ºBcÚô9óÖMŸ3U«Ýlú2¨®zÌÝ[%ys2³ó´…³Y'¤¥Ÿ‰à@79øeÁ²?¤%7¿Ø„©+;»u  3Ç?Ù(gã[ž • äÁ1€O$IÆ/{ºïǃþø›{Ð`¨ý}½$X{:8Ûw0‘gSˆ§¶Ú¶ò§“xQ¾ ot‹J£Åå:åuuS§|°xz˜>(JÔRQØÈ?y.E2Ò¹Qól$³wŠ¥sDóKzÚ¯c Y,§PYKÞbžB•NÀKϘåt±|ÜÖë¸4=ýx¬Ü›üHW[WY›òêÙ­MÃë¹kŸËÂ[`œêBoV_z‰Š AÀª‘‚1Î#8Ž%î²UÛ¥@g¦\ã·€æ&ü±ÜNö(ólc†ßúâ)°¯ã$9üqÌ|ÎóÁ´qsÏò%µ^ßæ 'Mù«?¸§ß5Œ&2ð¬çÞýF‚—±‚Û3 1†Ã½üÁró ³Ìö!3{°™w,C–{G÷¶¹¹{{ÿÛ5ØÜ›&{]aØÝã‡Hïƒ {ŽÖä’«ÎagCÑþõÒTMô·3CˆF¿K5î:7-zîÌ+V4¸/ôů¼R¶žØ½ñg½ªË6ûñë¦l¯¦Ø3(¼b›j\_ñºê~3Ôqög?þï·m•£ÜÙËÌÿØAÝès¼hÙK3 øí®mMS¤–S¨–øI.Dþ˜¨IýæJ­¼Qjé­ÍåS©·„;ÑÀÐzÄ'›‚lâ9µ²ªUqKbf/zú­cA™6ÞOUEãÒåçç‹ÙTZ4lW<ö4¸–-…¡ lÈÈÞd>Ö£Ckiáø7—rö fÎàEØã…™/\1ÞÉÌœyé)e/´e|CÅöÈeUÖöJôêÕÎ0Æ3=·Å²}÷®aŒK&Ûr<¾¼ÈÔÂçp»§Ý5\Â$óøcé(©Øg.§¢^yÆîÓ×’«¨óšËáåö¡=ÕTå@xö?ÙxøÔŒéÚ˜G—h£÷×¹ðz¹PXz9çÇ`]á IG9(üõGÆS ‹‰­Åžíãàú–p”Ybão´~ðo,ú‚ñdD\ØO¿x{Ö&üßÚé“åF{Róë¯o¥Ô¼¬sæàî/Òñs¿VpáúiËctÿèa”Z×›ÇÜ^d|úò×­4¼šd¶eÿ iêðÅûÇ\¥/Æ÷ÄW. ÌK|hXzâe²çè {Û°NãÁYŽü¾ªj~ô‹ £Ÿ6ìE{Θ½ÉŒ޸ùuG0Ý߈ÊfaOKGxE¸½ìçúبü89ÆÑq*¾6éR†äia  åÔé#úÂË’HòÚé™uÈS”r0"×Ë/7æÍ/,–"×Û‡Í^%gµÔ>>Çr u2‚½<Ü%Lx·®'–S¬ÀÃÁ†üⲦ¼¾¡Ê’Œ¸ƒV|Îk¯”ÕH5À/¢§µïÞg0èŽ|øý:$Q[óEhMµ™àT)´ÁA¾Ãzwº¦xK#ôšƒ;*ƒ±ÉF²ÅiVݬªm7òm‰I¶jcëP”cQÝ÷kÿ[¸çÉÙ1èêY‡.¶f.ûiÃ>é£éæ²wg]ʪí5üÁSÛ ðëèóGÌe@I8ffæ=·HˆtÊÌuˆ'rÅGå0`òåêÖÿD¼;™ø± ®¯m+žÏ\åoϘ¤æH†Rá#KªÃÛ1ó;iÆ› Ž>¥wßçÚÙ‡+^×—»9r/jÙv6ˆÇ \ÃÇä HÉx ¤6÷évÕá^`¨`á€Æm/þúOIǯ`Jôô…4 RÕªtq5ªšb­æ’Zú×®"úët9äâµ›Ü(ÀÓº†x­¤œÜ 4ð™öÕT _#ðaþîÿ0ÆïêˆÂ©öQavl:¼©²ÖÛÚ”ÛÄØsž¸] ˆ l‚†c$[Þ3¦eGΧ+6H°Ëc¶Ü®mPøõô™‚ñhb²Ä`ÅŽ'˜ç€Ó¿ã%–&†+Z¾W°^Yp½5õÀ‘l#0áÂg§¨gÞ3’ã“: â>àß°f]õ]Ö ¿™mÚÂX3g¯+¶F9$•Ágl>ðü«vïÜ®ÓKŽÅÏú“ƒ ßÒÚ=o˜<Œîò9së{ö8£œøŠP þ"}jþ{¸Ó‘ÒTbe™¢Ø3Ã_pheǦ4%Ê/´Šm3+¦–<ÅZT\Bœ<ÀR*NZ«ë6õ} š¼×¹M¢Êã©Ø%?o‘¸U¶ÈÒTÙjMê4 –O±ÖäšêÎá ½n-¤Ž–c×îîB¯®9wuuå×õ'š‰?‘xøë÷ðÇ” L3ÀIªüz¬Á³Ë±¥fhù ¢nµ^oìË”q<‹Å¬ Š(¨‹8Hë?+·ŠprܰmP—ú­q |ó{µsD(âU"©=âkj“àzm`Œ2;bÀÖ"¿¿9g•dyç«?¤xŸGe< l=ÿÖNŸ$âóñáôj‰x Q¢ø' ûÄo Œ~WÉQvÐÌÅ/Þ#y‚ùø>̨þgå6$¿-¦Ùp”½þñ zjò-RÜÐËï/—œoÁ-½Á9^@LËL2Í}¼è¤›z´“â¸,[ ¾þç×­âá3t Šë¹D;똭ë´GùMz!+¶gûûA"ŽöûBÚ•xi ýoí0–¿ÃC¸|šUÖŒO«NÙ e¸2#™ÀgŽÄ¶…p½Ö2’¹}\^E#™÷W6uÊûoT8ÃÖüñ uñ<Ä|Ò–RÕªå9•mßÈkÅò² Ez}cm?_°È¸ä×êÙH®ØNåwÃÑÀÚWÒŸýè£!†´Üň˜±ýàIÕ^UdzHŠ(¨ØÈDŠláRf޳yiµO‡Ï´3¥s+ë Å'Ž'¥< v(÷P=ØA]Õïc ©iP¸ Ï Tû%™›"%½)ûűA•AŠ* |· X— “9¿ÙP¶·ðŒÑ# `ÐRçkQÿ{·ÁõUg»Ù¢>‡+sxïYäääN;½'µ-ýÊqúÏêQ4°ë34¨ËLLÚ>¸ŠS±º”…8œ‚¼AÝÛ·¦TÐFq†AàO´¶|´'N­&*bf‹Å» )·¤<ÎᎎÎ4w¸‡åZ“2”s T¦Ÿ{ާ'ž}&zî{]ÓÃ[:…«˜æ‘d¦‰Ê®Wö)°Ôc¨Á˯†'4XOWžÀ±,;úö§³fñŒØ;Ó£çÇcp]B"…rvCv¤ØJjêP©I€~Uð¤Êê¨.`ÝV}½^¹e 5¼î¯wnC9Þä e¾Qƒ»½@>ž­éÏݯ ³“‰RÉp>~ö73`*Øì~r€Ø|$ÊP¤î`ï/Ž(§³ ³Elªþªæ=Ó×fö³Í ÁU)?šŒ>‰™s}ü)í‚yH¿ö 8‡o‹?,Eûl¸mp7õ°>×ÓÕ‡‚ ÍâDJLõÆñÀ¤ˆãh€¶À1m€ç³ÁÊ3´óºõâçò '”ž!È1 ®j¶Ö–Z¯,(ÜZúÜîë¬W ®¯ÌSm»þ‹†.®IóUöÑ%âNztÜð¼ô4¾œ{†¾[7…–o¸ƒýRx@IDATL[Íû• E×ÓÀ‘t=½´¶€&.Ï%K#¹·Š¾ºÃK1’¯§@åx5ð¹öDÄŒ¼dPµè)*‰³ ¨X@˜ƒÍ„ƒ„gÌûŠÎ§Ö,c 'Î`êªÂ¢b8&¬×0[ðÆÊJcZ,|xÈ?ýºk»P5¼°QÏΟ-¯§ƒöþÉèyÏ‹zñ « `ÌpÌÌ»5Ñí¶>ŒdVQeAáÖ Ðçò¹_Õ¬W ®çkl-ü<Ÿ<›Útã.[×e¯ò²…¦[úv¤‡Æ®¤Ø“ßЖƒ mj‚fqªk^|<ÛP—È»ˆjö@+¢hÀR— ôÇ)­9¯ÏWäì®È!^ï6âU ÏIa*JEŽm†ÆXÖYç7s•3<±²àúŠõXë7gêË)(Ä+O0%«°VÁõXŽb(_£|zux‘²chsì|:–ô°M&Gvþ9 ’Áxæ–>©]èHP¹ÝBÁ-z €ÍD5wMqÊŽF­“™ÚzNG›Ïêè²ìU†‚Ü{ kë§ü4êèÜS ø Šãƒþ~ —¹½›ö˜hü@%y Y@€Ú*Sù`º«/ÙJÌôóÆýH‚t‚^t<ñùùc90w¿t󋨲ð!›1$€å·-± „L_»^¢Ódïá”U‘7–Óµí:”€˜…Ž¥sÑrPÔêm‡hîÓwJe1 dÞ¿WÓ· ’X8rŸ9çãÀ¨Ã†P_¤¸î‰ n#ó¶Ïþä'p‡Ð$ÐbÅü{±ŽåŒ|ÌNàíé*ÑxJ¸pMÝ“Gõ#ΨøçŽÃRYL½É •?K«Ç?æhµ`ÿLµì÷Œ9ó¦À“ù2˜_hò¨þ牨 _lí}óýª »ª‚ë+œf•ŸHz;¨ V.™½Í*…:@!Š¡\ÅMðt  ƒ?¤!=^¦}ÇÿMñ‰?ƒº³L’‘}‚xÙuä$¸p§VþýÀ?àn÷ _7T#Žž¹w”Ä‘¾f×a$c…ì¥ìƒ MæåTöœ™s hÁ8;pÚÒÂiwû?¾G‡6<„eá/æÂe™å7x9ÉÑhÐuµôñÄø®‘ÚÀlI¨ë‘ 7I Dfy_Ë £Ð$I˜ÿ–ë·¬Iè¿«wHɰà–ô=ŒïmH,1vPWÓEô—©6YÔ¢Pþâs öY6E$ñÍV~Æ»Gö¯ŸxËÆ4Ñí¤|ÿpùz$áÉÉuFcRƒb(_çnrÊë‘}ߦá½fQBò:ÿBgÃ0ÊypuúBbj9^XT‚ž“.Ò¢'²Üu§ æÝ0ÐFâˆÃ}èJíUþT®ä\#ÉÐÓÑ4¼ÅÀ'¦ë¨øê˜¼«.ädï 'ßމƵs&Ogå~_¥ å‡Í40cvÌýFÒ/kîí¥žq×Uh€¯ÍêªiÁLSÅ ŒX2³ $/°Z¥¦ÈV&¯pP _³‡˜ÏaïpÌÓãÌÔXlø²°qÙe6¤Ù8eãŸÛÓçSùcÀll³G𧥑ô€x:Z6€'ÝÖÛœûz8ê=GÎPïNá4lKál —2³Íe¿žT¬{Œbf9b8 K—¶¡tÉ’ÑPæ@KµÑ˜,m8è­v³æ¢nGT蔽§ŠØ_üዠĆ’R}ºÚ(ÞòÅÜ—3íß ÛÕ¨Ê5Ô-{ˆ;µ™ -:}‘Üwæâ&JJÝ)¥Â¶,Æ(ê)%3NZäýìuôëù+`ÍK ïvx°•`YGõ¹>Ÿc £ÀgÃ8Ã(mç_?Ȩ¹»Šú‡hhH¤çm£!oWå~Öç}lŠu# Ö4$3ú¢ ,<>qˆÍ°™µÕmëÀr& æPçÔ½ÕQtUä] /òãçÀDd”xä9/ ¿¸pö2o‹M  CºlU'lV%Œ•…!!œɲíò±š¬9©TE±¬›=Ì %Y°ìòÓÔ¸;“œ!‚W9ãÓ¹ÑçÊëx[o½5Ì0ã͹ðÜsR3Eì¨~žÿÚO«·aËC’‘Û?›ûÆ;6Á.U)†rÔì¤q£ö­ÇJ _žSŒé¿”œ±Ÿ.fÀtÜékJe¯ó…ô½Ò"äTÚ¾1EØY{zPhËÞJ ¬®3 Œt0Õ@‡)fv 6óK¯}ÁUÖ„/õÔPÏ €©àŽ+S“²ÏN˜=÷q_ôí&p&0Ná(RÛ$F–ü±çR2i ^Àr6²ÍûŽc¼×<)7¤Wâ´öŒ5æý‘¡ÔØãªÄ Ÿda¸¿àÙ(æDPÉéYò!é7·#ãÊõÑœ–›…!²\&›¡"–bY·+Òþ²gš³º9²0s0Úz¨i­#·“Û¹8-zÞ·ÈÒ:ãä¹TñìŠØ^œ4Ð!Crú5<ùߺûºN{ÿ¥—8›k£ÅP¶Â-õö¥nmï‘.®¸4‡.e¤K—ã(-+žR.sàFÚ55é Åt13VZäƒnþÀ;÷¡–}±î Ïs‚òA^>OY×\Ìe›¢—–X3¤¢&ÂÞᨖjê⯦®Xºh¨¥‡â1®‰î”sl¯ésæD ñçl0>vÇ6l_©jÈÊ)”jb£ò2 »' ¾g“Ó®èîáð§~QÄF´Ì[‘7¶²æ¶k$A7v:£ž¼ê´NHÒ OvTd(5CvC‰ëÌ\ö¶Øt%·€88‹©¿âÂéŒ;´ B a•”^UVÅÝÛ·¢q Ä̃{´GÜ‹ŽÒa”ƒ³¸â©õú;îäyP÷•8©ÔªÿÖkCjX¹—æµÜÝ O—¯ïúäÃU ¶«á•ÊiµÕ@2‚l×ì>"î?z–?ÌS`Ÿ<»4fÖÊÚ–ÓÎW eÜ-WgoD𓹸‚ÂtJÑœŠ%-ë²ÉÅS^aŠ|ؼ.(J§çþ”ÞéìäAáA7£¬<A$Ž5 šî@¹%FÚqAOÛ’t´ 颯]ß[ìë&À(ÖPgÆ[h( †qh3Å(v Ûª4ÅB//Zä‘—[ús@ oÕCão‚ÜxŒdîf×¶!.ùí%+É4_‡õ’X'ø®ß¯ý›ò ŠÈËÝMJ6ÐÜ4ëμ±ß¯ù›^Xü-A/Ä»ÖÉÀ4yì‘fhÇOë÷IA…œÑS–)£ûÓ~ÝJ~·NÚ5 k¤D±ÅFSôÆÇ?JFô³÷ŽFº­Úr^ûh…dì²á^0îšû²jëAúqý^éãfô€.g(ƒ•CybjÖf¢ÙÕuÉ!޽ûÚkyÓ´‹GŠ†â Ÿ®ØÐ™JîÕÁ¡îѾ†Þ†'9“Ì•Fà‘a«òIEŠê –j§›¾jz'«iÿ ®Ó¢çnáòAp?Œ×•TnXx$ã¹±ÀžõÃîÛuzéÁ±øÙt¥°8 †óa`™Á«|°Ø«˜5*Ó {˜Û…Ž¢(p9ûz…UvJ“Ü— ãÕ ¥´1QX…ž* šõâŠÏÃnð3„‚=Å]ü5èÙ´â÷¾YCñ'ýþ‚ (ÎÁsÏUY[YàŒ}Óâs¢Ÿ¸]ñ¯ÿÀ=+wÏ\{\eo±ygÙ{o+£Ä‚^jÄË D@c%áŸ+–Í¿˜$á£+òñrï³ü8©®••ÍûòÀôáæê$ VuN}ìgÆe ¹î½ øú¡>ÚP×:9°ï’n竤ßBЧ¦T¤ŠñëŽÜZ×>Õ×uÌ:sê|š”dçÀñ³†âPK*žßYãõùÇÚç®Oª¯Æ[¹^Å£le…Ö¦8wW?Іç‹,WN˜°Îéûè<0͹åCÒ:-ë(<ÒGiÇá€iîC]#'S§° ð<{^u^Sø¡_ÛFx=Ž”ãðWeû¹ Ô˜âžN`¥PS'xŽ9ˆ"Š¢¦i—¸“!ãåþ]"µ‘Ì÷¦*#™Uf$ó~v®×„—˜2ª“ª0Ö••]];«ªÃËÃñRسq´bÝ=³|1wÖ ÊU5ß!÷kµÃõhØ|¤q_nÐ^Üú±‡N¹ƒ;Ûj@Ï ·´Ï‡%PÅŸ¹Ö+ïjñóöR$rð« 0øÞžÔ |ß,zx‹¯ŸWPebIJ½lHÍÈÁ[Rà>ÎÇÇçÏx®¾»¥[»S¦L©Òyb¯¾Ø»ÅP¶·Æ¯Sgä¥g{vìeå&ÒéäM žÛDçÓöÀÃQ8ÂÁƒ¼¬ß÷ðÑSh@ÔSÀÔ…\§††˜¹‹¿9TBß)¡Ê˜)xš¤[ Zb¢ŠdQð+¢h ±h@eÈv÷›PDÑ€55ØŠXPT*: šÇá1¿>fÍš•[±,NãŽâž¶pa´P ¸öûÎ¥dõA¢¡¹·§tƒš°à.ëàÌ­Á7Îð„‹W(An‡O%‹'’.1K‹àææbxó‰ÛÕŒ·¯/áY–zenóêư^ãdô<„õ6Â"†6ß¿tútÉðXZÝÅø˜bA8øÍõkAý:óòè•À×|q+9û3ñÌ4t,{òk:˜ð­”^{`—gÀÕîà=«}ó^±ì` -?ZLEåß æ‚ú…bbg†Èr`ŽQ4Ð5€‰”Á..N†ˆêQ4`% lÚwœö; ûX5ûÓ¹¯µR±õZÌÒ×_ÏA>àåií»F}É„Ë9·ýŸ8%BoZeôõö4ú5ÓÀˆüdÆšn..äê¢ÁÚI‚Ù0õŸ^Y^•R6’ËäÀ Ët—2² 0’ËÒ\%2ýœaÛT-¨w—ë¾›÷ŸßݧŽí¯îÕ1Lšñ°—RØx?xò­ÿû¨L2`§P½©ů@£¿`T!ï°¡TÔ%Q  ?«ÕN©>"Õ^w zCÙnÆõšÂ\ÌÚÜ*-Œo>zv%2® ô¬cÒ¥¢ˆ¤g~Äò<Ò÷KIR$C’Š¥±E´d •rz< ျIìÓÁ…˜ºME]‚(†5swo©ò1ØØïµ½úwŸ7ì3â_jÕÒ˜Ù‹ìU¯=ëùLûj*êû²l¡iÚ…­½±¼§½A؃ÿFÖ ¯`V˜¤Kt")…Ž'¥J€-†á~T%¨ßX3ëϲò/ÜH=MíZÅPn wœñÍ};=&-çRwÑ®øˆ×&á]þJ^O£ûÎ5ó=7Ä®ÆëxΦBbŠ7K逴ÐÓ{»Òضª±< l+hìDƒb#7ö›l¿þ±AõÅOaRœ‹ÆûaûÕ\¿5-Õ¾~-àå'Ë–¼úÎ;^¹:?J7Œ±™A›áƒMàU*D¬59jç‹ïk_*'á¶,Äbû3í§ÁÛÝkÆ[ónµàóHå>ä÷íqj''!¸…·êï§jáëE>^îÒÂ,/®ÎÎÀç«ÍY(¹8æ¶æÓb¸r +§}Ï‘¼Ú…Å`úå\}fv¾ 發Ú*á¢A~!•úç/þ9k«E“”ÍZj@1”k©0G<½Mà â…9™w"È/ñâ©™ÌÝüËÖi’¡ÌsC¢–+Љôþî"ú.¾„,}ÈÌP1³¯+ srÄ[¡´IÑ€Í5 ˆ2)ç«Ø{t½€4›7F© AkàÒ€¶bƒ1[ÇUÂÈßh:LUÝ8¦šÃ1^¬&exïßPào/hß÷)ÒŒÑëõýÏ¥fõ¼–Õ Xæò4–µ¬ecI§ú ¼+Oo«2Š»?›­xk©ËªNo°†òù”ËtùÅ/çä#Z³Jñ•ÅbiTUÕ鯲Ÿ#­}Àé‡LPQ ÎoÔ‹¦ŒøšNž_ƒ¿9æ$' ø}>u7Ý9ìKj0ÀởxÅ@ÓÏ¿*1ˆ‡“@/t£ûººHdÎÜuÓ…Y9VŒé™šŠÈtg¾ÿðBWºÀ‡ˆ¬n*÷ >ûiTÓ&xµž>rú"õèк>›¢ÔÝ€5À™Õ>þa#`·âi'7aøÇo¼q¥w§Á4ýí‹Ùh,Ó÷^|ï=·ÒÜâV¥ R Æf åôD,¥'ðUjx£%³ã>rÀ ù°ËCrÉ<H’«*¥ ‡Ý`úßÚ  e¦0ဃMÈÄÄÆ1O?z¹¹’hN\)©©É•Ü:)“ò¥Š ì½a4 áþˆ¾·ÐãÑÖ¸…ƒõÂÙ—o¸Ÿn´˜¢Â'9¬ªö_ÒÓÌ?ó)·¤ü“gD¸͹.Z»ó0m>pAÊý/°¸ÿˆæ–Œæà Ñ·“”Š×ao²Ò°Ò€“Êw­Þx%ca3Ê 0ÿ†´Ù4/Þq0–¯ýŽL1Q­vöñ¬W3š¦&£×e©ŸÐ^q0 4ëò(²Â|ûçnÐ¥ ˜-ÆŒ¢ŽaUri:˜žmÚÆ+HJCtëZ ƒi+ É©cÐÈ>s©sØôÛŽgà]N• V«w<\S2 êú¬MÛT—Âÿ8UJol,$æGfq‡9f„×À{œL"ãÖÈ|ÿoUî¿YÅ–÷ÿ—MûiËþãtÿ¸Rö0óIÊF£ÑÀgÚ™ùOFÏ{7á\Êü}ÇÎg!SDÑ@M4Àpåk÷Ê*AX§òPß÷Ù¯*žäš(O9§Éj AÊö¥Ÿ7î§@¤)qç iÑdoXegâýžZIËÙ‹™´rë!úü§Í4qhº¥_/zpôJúyë#üŸ.ß·É4vÀ;•W/û–ÅÓ¢œÎ$þ*ú|œ;µó@ms„~Ûgºÿ£”û/ëH^_sÿ·¢O~ØHwÝÒ‡Fö’OSÖHž~nfÞ÷ퟻ:µ ðS"©"ŠªÓ;™–ü´Ùx.å²+y^zö[Ú7p )¢h@Ñ@upèi;†ælØs„~Ú°èBè黇)FruwÇø#bæäa’¾Vn9#3´-hêÈŸ(,pˆùê¸SßÓŽCï™×çÆïð$[ÉíýTôÝ$wŠô¥ös?”û_³;$Ý<'¬/~nøù)ƒ¸Õ¬å¬¡žªU‘Ó݈‚/úhùzAŠ(¨LÀWÐ:8´K~5\H»\¨RÓ¤/çFÏÑj#¹2})û TÔ€ÃÊürçéöŸ7@Ö¹PºÿÖ~HÓ¬ðëW¼•ýf=±¾Xo«·F– ó€s¡»‡/CB’ÉæK8 ö±¤Uæßõ±q8Õ@³·e@ˆš¾¹Ãƒš»¥vsû•û/k§fkËûÏÏ?Gб\3Ý5¤³>yí$²g½’SPüÎWèS29FHEå8}!æ~ù›ñÌÈêtú?4Fê²äŸÑ̾ ˆ¢E5Ô€ÃÊÌbñí_»¥éöÉ#{×°;Êi–`½ø5£Ÿ`,å!Ÿ»H·n2Ü|ÚŸ»^¦”ÌCæßöÜHËiæ_ùæ$"íš«éñîä,¤ör»n£ÜÿºÝùþós$³ÂÔ­$å*GÕÀsgíTkhd^AQö‚ÿûݰ÷ÈGmªÒ.;j 53‡¾Zµ]\üõ_”v%/â&,™3ñÓyÑçìØ ¥*EBi(#ËÄnq%·&é®x’ëø¯ÆžÅ;†v—Rlr€WIi)Œe#M¸écjáÓ^*•Ó_ÿ´åq3•\«ªõeÅȾ=ã<â´Ô,œrúÓ[ÝɳÜNn/§Uî­Uk¾@¾ÿü1[ ?WŠ4> |®þ[p¡:qÿ²ß¶Ó²•ÛÄ\<;Š4= $€ùÓ³X)î=’X ,rŒ»kÇ/bfýÞô´¡ôXÑ€u4àp†2Os~ò-`n`v%pïÆn4ëõø÷±$*\)ŒPµÊî¶ Œ!¾RáEé´¼Ëö”Ï÷щLS¶='B~4ÖƒÝIj·“Û«Üÿ¿#òýç牟+‚qã:uľˆŽ¾H­Zܬ"a˜0to~ñ‹aãÞcÄ,Š4n =2í>|š.[m|ï›5tìÌ¥ËH@ñ–Æ]ÕŠ±ÈeÔc[ Jï ØPÇzÁ^¯¤‹’7ñÖA]lØuë}&9ƒBü}—c¤ÿ;Xb.(zˆõ PIðö&s;ó K$óIxãð©‹’Û½}¨MzÉ÷ùºT鹊håOjµ‚õ·‰¢ë¹Ð¥Ó§sÖ˜§¢ßù x>úqýÞÑëö5ÜvS7õ€®‘˜™s¸á¾ž5Öp«G*dŠ;yËyJ8ŸŽï_£ R gT*Õâ@uð×Zí£Å ·wJË 8–j䔽ÉG“¥d"Ì“l ùk×x¨M<ÞL¥ cvÂn~×åsì­[¶z'M%WJ ž„sitêB:Õ‡¡Ìzää,l,·jNNNN’±ÔÊ¿?uo;•þ^RÃú}oÒ6 ýµ†¾µôÄåÌÛ®dDb³ôÒÐäÎÎhB¥:—HíäöZëþK•ýIÏÊ£}»Þr—yûñ‰7!Ý·uþçÌ…Ö`#.á‚M eùþósÕ&¸î/üެ`E¥8ÈóäœùC›°ð»¿vøeã>ã€níT7÷lyÓL’£w¸kÚžè.JnéÛÌÑ›[«ö—è¤ÄYžî®€Áybñ’2lªÕ哼üN)Õë)¯ ˜’ӮХŒ+”œž…åŠ!óJžôµ«Q©NŠ‚ø£ QýºD;;¶VPNV4 h Fp@CÙ@—³ó¥Œ{lÔZK¼=Ýè±ÛQêå\Ú°÷8­ßsœ¸µ¿µŠ‡á©¢·žï^Ö£'2r²†5¸ºº NRö¶a=_GÊë?¥Ì}Y¹giϱ/i`—§­¦—ŠmH,¥íçM馸 9CÝ%#yTa,ë¤öq;¹½Ö¼ÿr;üšyÐ3S†K?ù ¯°ˆîÞKúÝÒ×K>­Q­YœÁ’Ÿ+Ö³F£V åFu‡+ïÌ—sgmÅ‘ÓçÌX¬ÓÏØ{â^`ÿ#[ù‹7÷ì ôìІ\œê u„S+oÝ‚O F£°ïX’øêÃãÕ—ÝFd—]lª–‚W³‘FƒÑ èôFŽ£«úŒãµFHBœÉAPíÕhT¿}¦}ã´eʶ¢EÖ×€C’ »àyv~¡”–ÚšÝUËƤü¼—‚6-Æ¡Ž‘ cßÑ$êHÄ*$jè(y›9ËÝF@åæS«_ºsDOCýgÕºwt_ ƒg–ekì): _ï}·&ÜÜ¢"‚¨ ¨”V¬ßGI)—)$ÀZÀ@³ölþ¼9–R2r¨¹Ý1¬'µ ô£cgS*måµuÙn†4ßy…ÅT\R"A\‘šõíæêKCz¼BëöFKÅîŠÿºFÜEžîÖ÷¬rƽ…;ŠÌÍ¿¯«+uÓ…^¾ïÌÌÀíãvr{m!x¹H÷“Ëvsu–<6|YxsÝžôêC£¥ßœñîÃå›è®½¨ ¿nŽ#_/7écë 4a¸_·ôëˆòü$cÿ¯×ÒøÁÝh^òˆÈ\ÆcE™¡8ŽŸÃÿÔIbLa;x®ïÞCjƒT™ÿpšw~®XÏ|ßÙ«¬HÓÐÀ’¹³v£§»ŸZ°àC¡áᤋ™OŸ¹ÞîõNcÛVBçˆ!*"˜Bñ\_’€Ó# 1„1/^HÍœÕ:jã—{¶¤ß»‰ÉéóbOœ£^ÛÔWó¬ZoJF6­Ý}Ĩğ¢«0],ñðœp@Ïš‹‚P‚ Ÿb‘TŰ”‹±´ÔµŸ×ñŸ{®³fÕV)…)P4P•ÌP¥àè“‹ ñtÒ³%(‚3°º¥˜ò? F ètzËÉ/Œs©YÀtî£>è= ¤­± ´lÕ.úÇý£È #ÖáSÉfC™ð° RyÌ.Àå±l;EI(‡Smbšmóþ“˜Z+7–Þ Ï®=~Ç`ú;>©§Òó÷ÞRi{ªºyµÙÏúÌ-*¢lrѳôêð ¿HË:J:}íƒWyx“á\›:®wîÊ“¥t1Ïļàç.ÐsýLÆ0·ƒÛÃíâöéôjænÿÍb^÷òÄ xqßSn ðI¤þQá€ØôB2”c´aß ztü é8Ÿû >~FôíHÎðÚ®ÜrˆZØîÕ±52!ÓŠ ˆq÷l˜¯ÜG{Žœ¥a}:H×Úúß~®,ï»­ëTÊw, |þÆœªø^fÌ™“Q4Ž?u>ý¶“I©]~Ý´_ðòp3`üS‡‡´”àÁ-}ȳO¶†0CÃɤ:‘tÉÇôåæ­¹,öpÛ)„j‘“JSbüí½ýü©ßü±«fÕ‘¡þ¶hŽÝÊ<~ö-Ev<À*Î9©f|2ûµTÎË.»5B©HÑ€¢ZiÀþÖHÍc|² £lÖUœVçÝðâ-_».ã•™]@#ûu‚WÍ4³U é®QÀ·’Êg#ÆÃÍY2†x‡;¶ÿ½r!õ'õ„ÑÆíí ­ËÄT6C9& ëqM»xú°G»V4°[¤tŒSK³¡ÅR6ãÝ6gêÝ© }ùëvBâéxÅöH;­ð‡ Rf½=¸²Î¯:¤Ç«ô㦇¥ZžþŽu{Ó²Ö…"|Wî y¢—+y¹Ò½–ï;·‹Û'ðVè²Õ‹`Ï.°dÁ0^»û(úP^MTD îÞVÚ±ïè9i’¡|¯—;um,뀙‹ø3—ìf(s¥²ž-ï»ÔåO“Óó/£Ó¼¼ñü¼y……4:¿°hìÞ#gÇüÆ4]†ƒÞžî`šÕ!0š[šÄ˜ZO7Œekwgr‚Ã…Ÿž±Ð[kZô”WD—sò¥±2óJ®´ÎÈÎgŒ- cÐZU>7 *aÃ÷%÷ùª’%<†¤Ø[Ãî~ÜÁ8m޼ѥ%º­üomÛÇî*ðÜЄƒïVo‹£Ý‡N‹ˆº;*¨F~¢}%½¡õCi¯¢¦¨‡1”Yù2ô‚_ä¶pÄÌÒ)<¼=Ü®ª¢# Y²1mØÂGþIÁeÛŒíÙ.T2ŽÎ§eÑ™ ä /q¤,Û̆^2¼Ö½;…™Ë`6ŒI©ÒofÇàóW¬?`>ÎXJV®ù·e{Ì;otuêáå—™</ÉIH˜[93;^í|Š;ý-õï|ÃëÍgs¹bòÌz: 4¥s¹§J¾ïÒK–½·6ºÿµíð‘×\ÒÒÛ¼ÏÐ ö6ó½Ä‹]’Ѐòÿwó éó’Wù㶘¯GºD» ·³â}·k”ÊRΞ††}S¶ÐŒ˜˜±Xìj….¹E]òÎu‡'´#þLS@zø ~„©5Vû­Q«àÑÁ匂‡‚zÓ𮑱S¦L1üù®ú1\o È@jÍ”¥céìÙ)+&OžÔfß’Ÿ7!¸¯•x׈ÞBŒwG—¤K™uÛθ¼ÄR(h^:h±ÂJáèwNiŸ¢r 8Œ¡Ì/qÓ‚ÆÙÀPò€dJ5þ4”Y-`ü2e—,ÙyÒ¦' É0>ùXbŠd(÷,óBËçòš=ÕÉÌ^Y²adË"Ð< ØЖ ,–í±<~#Ûüù9?ĶËj6é\f@è×yq¦>–}Ç—Q¿NO”‚«¥}7ògY\9[ѽQðH9™Þ©r¸=Ü.nŸm>“®ßz5¬]n'ea,3Ãq*Êõ(¶ÎS™¸¸hàs£×SÙaûì“î÷µ÷Ý>•+µ4 HœÌD<®‘Û¬ÕŠªtZä/ŒÍõF}s ÍaH7÷/Hï”þaÁhpW‹FÂbHhÑyþŸT„RÄ£¥«5b¢§‡óÙů¼R> –ü%Ö>ð())\,×…ëþ5òo^÷JL˜Ú3ñ”Û:’¶c”øç™d50ËBÐÞE!€cPE€±–ðÖ{âÏS/çàu Âwòµ«“Ӽ羖ì(íTÚ¡h@Ñ@Í4Pù[½f×Zý,³Ñdõ’kW`û6ÀŸ¦‡Î ¸/€¶!`¡­M3’<õ·ûp"¥c*qò¨Þ•ÎXºøÓ—¨¨8hð¼Ï^0´Y¸6Ü9Xp‚¿<0™ÏCGÔkKaý²ëæ±ÙH¶¬¯Kø$Úzð]"¦ƒÇ8ØíMbeyJ¶“²´ÿ’ » û“ì^îMæËï{YûØj®al&W½ï8'; î±µš$iî8tšúûÎXvf¡àÙ{ kUÖµ½êTêiÐj‘2“ˆ§ÄLÓbØØ7>ðnüC=Šÿ*ÉÓ ·@PѽO,û³VYàJK‹>FqRD-¾UÏøh‚µDåödêèÑ™é£ê}î4d?ðmÿá½|û(˜1ZbæÆÐ»c˜º=+µEL‰Wy,ʵ©ðs|1ý  t—éè7OœK1–À» &OؘſÕjŸåK´3ómÚ¥pEŠl¦‡2”¹—ü"¯oa.Ý À oAÞj0aø6s§ûÆö{I]ÝچЪ­‡$H†”bƒºFs'ôÃ&x—=$œê)àUY¸œAM÷ú‚)…=Ð0±-H;lôG6”*Ó3ó'wœB»Ž|"Õ~$ñG«Ê+O”c“‡‡;‘¿ÇµÞŸêÚe#U\S,ß'æ¹^Þíß·¦›ºG’wúÕk.ªáŽN0”9°sÝnÐBm; O½@C{µ³«¡ÌM­ì¾×° ÊiŠÌØ7!àEÁhüFkÓÔ ¤Ie|¯U)ûÍ'Õ`ãÏEê ¢Ñ8Ù|ª ž6è¥ärj(ÌLŸöÒÔ›„!k7¬*?hµ›g_w+(,}dG\¨m±'=¹P@ê#Bý5~ͨ%/£yœötw3C¤ÌõÕ`ƒ9yFðrN–Êæ:ãJÇ»2„ˆgJÒ\Ç™Ø^=¬ÔÎë¾P0È5ЮrŠ¢Ç×€ÃʶP3 T•åøä >˺ÃHâ…IW'ËCˆw¦ù3ï¸j>ï<{§yŸ¿Ÿ—D3võõ]ÍÇ9µð+Ž–¼Í¼SÎæWU{ÌÚx£Käd³¡|*y#•dƒÏøjxHmšÀŸ=¿íB–;:\íM–÷×Çzêè>×T; ”mœŒ†ß|Ì=î¦ò{ÆÁ—–Âl¼Èbyÿyß#ãʇ¤õp0\ðÂYù ˜MiÅr¯ºHù¡hÀ4 jµªû?_4ŠÏÉ. Œ}'€¤×}UÊÙÚ4uÇ;-¼r—?“¯A9ËÆ½¢ß$ÿæ5¡‰}z< gr™â<é—V;œ§©Vñ²bÅ õæø3=ŒFÃPÙÉ9y¾; \ˆÎ-°c‚Ø›ÑÍÅId6Œã°iáGieÅK«’± ¸Xd¶¢â½º"öõëpáE`­V‹@ÄXQ¥9¸Dûúy¹•ÊZÑ€¢Æ£‡1”ÕÓUÑH®í­¿Þõ²\Ûr­u~E½û5 §½èbf,`::–ôõîðp«û;YO©ùjŽ——Ïו•ÏØÏÑѼ¼ÇçL[²ÄÉ)år8²€¶ƒ÷;!·~¥¥zÀ%|ÁÆáƒáfðKÞ`˜ÞìF’;Éf.ÄÞlœŸ-1y,±¦4Œbç\œœ’šÓ«—Ê (\"Š 4r 8Œ¡Ìzæ—·ò·í#[d=W¥ë.‘wK†2·$áü_7d(s&>Y&´w&µôZ’÷˜Ö–íqäÍÕkd¿d]7²n)ݱ±öOn‘V¤[ÿó´ lÌÞá^µûøt9¶ª†íøó]ÍM$ž*óär0Þs7?VŒ "Ÿ—÷àíðoÿ-[jŒõ]:}:§M([äb¬°~Í e(E(P4ÐP4p-X´¡´\i§Í4оÕsÙÒö"ðìš`uóñëmlM2¥«æóFF\íM¾ÞµÊqEŠê_nmûÿì]`TUÖ>oZ2é”]¤&Q¤Xè`Ų®½­uWt]wEw]W×µîúïêºTnEDH¤)¤PShé3™™7ï?çMî›!¦Ì$3“Ir®÷¾÷n9÷»ó2ß;ïÜs@ql9“$Ãsi«‹¯m IÞýá0“¢ÈoâC±K›+ÁªióåëÏ´ -mjxÕ¨M–õFɵy¢~E>fF €0Q ¸íµëHs<†úvÙæ:mjÑTòNÉZ$>ò<¦åÉ6B {v⹊ӎ$hjáD³ÞûÒהζ¾Šv°pï°ÍPµ•¢ôž†úÀ°%¿E³ë”¢|Úï‡í… ÕãsŒ#À&ÊD·÷ݿ煚ôyG¿Òʾ6tk“Ç÷14hváK\—`‚‡@ö¬„ËÑÿïרIîN£"1®AŸgW &ù-•býRÓ0ìïQÑ^§HÎxÀrD‹üàøñè.NºQKzÝ‹¢Ì9#À0ÁD€‰r0ÑnGcõOvåCÅ[Z$ù¶£.ßÉÔxBŠ©E}p#F€>Y³É«ÅG¸¡AõˆöÈÇÑRbrƪ’O[*¢,Ô9ö7±½úǵӛ§-_m¨?{MÕíH¨#èôí©™¹ß6TÏ1Œ#h˜(ûˆ0…Ÿ¦" ¥ŸÑOò»òºÔîΑé…^ïòÎPVuj¬>oj‡KÝD9­‡‡—¦v‡†KàZ .p׿±DÁD lì2ŸgBÕýÛÌÄ¥èþíE4·¿?#³=7cuñ¶ÖL`Ý’§îÅ>…ÏD›Ñh¼£!ó eî\ ’yŸ Û°6Y€Á9#ÀòzìÙS°Óè@ž’9Ì=ºÇÀ¥G¡}nLƒ¢Pˆå·Vm†¹¥£ïeáÕÈ]•Œì?\ª­pŸmŸ%ÎI]φ£ÇÉÛÀ±¹0 ×E^Oæp… §-®=íqáô‰ -¢¼ƒŠl̦ ñ¨Þ2ê!.*&ŒÃúªçú‡„Þû,ž¼s6 ¿œEp{v? "âŠàØP|ŽU nîžõÊ;x×^å!ã÷Q&㜡˶ê ð³çͽe‡õ¯¢_$ÈO_ò m¯8öÌ \ŽÚä>t ”KS{¼°Ý³ —F€Bc´Cm Œ³Ràw×]W^8Ê«¬ðÙ–ÝŠH(ž¸}Vƒ$¹ÑFíøBrühMú"$ʾ¤%äÖÔ•F$þ’TŠkm™ÇF™áÁ_] 7Í:zÆÇÂJŒÆg±ºÝÙÕ—mxÿdXxǬIrýº|Ì´'ö^žÜíäñš/Q{ë&É|Ü=>â¢Ö’dÂA–­¯!W#ç!ùÝÒwè³áƒ>4—pÈ”_“Ö­óÙý\c}óyF€`|E Sk” ¬¨ˆpÔ$ǪŸÃ%§ kï!Ã=E¹»5„©°ÃX훳.Kÿ÷%̾`$ KíÕ|øE&„äÄ®Ð=&RÃÚÃç?ì]ùÇ0º5Öèñß'ÞsåDµLaŒwåÅèlz •<@ g¬5‘Bîn¢\rºñˆ†ÄÝ[êA”BÓÛúnÕÞ„¡VyûÏG èd¤bÔÄå¶Cø8]Q[wÂͳǡù‰Mý.ÌÇPãôÐDf8Ë7æªßzà 34?àÇŽ—à NaËIû¬7èÔç…¯ôT%|¼!аN7 ›}٤ђԵ!ù#pÐGòj›}5Ùþb0 ²ñ˜ô»çI bœÖ¥µ‹õ7"ùQ׋SÒéïvõîŸH ÓÒÆ8Çøºº6ÎÔ  së$âÖŒ#Àx@§×(iÙd–^ÃçþtΑª¢GD÷À‘RXDhÄÀd5¸·zžˆ]£´iû~(,>ÓÆ ƒþH®r~r‘lº¶sÿø1ïÜ3w\}qšJ¸®½$b"Ãa+ð}…Åx>.{¬Ýü#œ,¯¢f!•ºÅ Ôä9UQ •½),wå~]Cókf³ËêÚÓÑÚÍ» WB°VÖXá‹­{á'4§¹xìHè­®;­? 5}üu.˜ ¸‰î©ò(=]©AóÙ–]`Dòýè-Óa`J¢zþúéçÔµËQCXßvÙx$ê±êwLkÈF ˆdÎI¸Dõ‘\G’ñY‰¬ôÛ´5%ùƒ$¾(¢§NcéåéóîF’ü;1} hÒ/3³XsÎ0Œ@[ Re´[£ΪŽ6N#‘Mña$»•–ZèŽÚ½Áu¤†Æ&"uå…ÃÐÔÂE’Ñnî ‘òHØÆp)b Žž"X”ò°ÜmcPc= w‚zNì}¨­Ú/ â»DªŸ„®Ñðãc0)mZ/ÿÔÚýŠðUw‘«8ÖM¥.Ñ)Ú岪è-ub;ïHïÁr·"ªo3öÉbÝ]9*z!ë_m­…¨9¦±h~餑8¶ÂlÁUM±D]¡¬²F}¸¹ãò Ðv;ö‰Ç££Z5ZÿË'‚p“RÑfy>4‘›´ÒñûFšå(³ Ò†¦À›Ë7©¶ò]bÝo$´ŽüT ¹˜P³-°öS·ÜM;F kvÒ=Šì|ÿª©¿ø×¡Ã8_ßÏõá°ƒå |²Œ£óøç&/!6é÷¸ã¡~5õ8ìØD§ÝzvÑ ylí,F€*!C”=I[d¸ •–ˆóGöW5ÂD–V£9ÄëŸl‚'ÐU¤!hrÑPr¢ƒÑ#¥eHtúj—“âTM1Ô'_ßçÂ?æCщrˆ4Ã@$Ì²Ó ùÇN¨diÇ~7±:^XrÚ_GÝ$P퉻8'r£ÁŒ¦)‰PUSN§*ªBl”ë¡AÔi,?äA”Sb'× †f •®ŽÆú÷Çù.ÑðÈMSUs‰=Åðöš-›:‡HV»ïÛ£[ƒ$™.*vEÛMF³J´Ô“èÒZoÞ™§ÓvÀh$Ä”È\ƒ¸>üµI’ÎQÛ¢Sg´§óþLÕVè“à’•úmwŽÇ}….äU"ÇòíßœN§æY¿‡õÜÑ+Žúm×ܺÅú›Šs&!$YÑþ–ô»ŽÕ4†Œ"×Þ×ÐÁ†úýÜÜ[nVcuù<#À0ÁB dˆ²ç„c" ÊbEÛ^›J"<¯ªLö¥CPË»mO!Ÿt“t²!n(‘æ±{\šT¸¼fPÒ2Štvÿžðuæ>Õ FßžÝážôÁ*9!¿GÔ,ŽCéç-ª4'«¬VˆP]¢ú4V—¨•(S#Ò*{C”K«`“]Ú÷.f "1*Ÿ/ÉŒ& Å•– ­?‘Æ!ø&¼_Bm¯ Ê y¶ó ·”N¡)F2>Ø‘G”*$£"MÎ o¯þA}³0aô \a¨a¦tš]ЃU0’ºþx?ÅD¸ÞlcL#4Èš››mùæC|V›"$Äïÿ6³Ù|é°ehæ§ôÕRs²Õa}ÁÝôÒ´ŽMîã3KHÞMùù?ÿFœÅ¿¬M`pÎ0mŠ@㪾6‹HKJRÔºjgK.EeM-ÐÆ«]øjœ6_aŽs‘ æïß+A5™8ŠšeÒî?ìö±K®Äj ÒF½T$Êf$S" CMöÐyhöAæbÜ@He‘ðìiTÉ:aLo…³©¦Ö;_ʧ­.’Líâ#¼ûŠ ™('9ƒ±þ„=­}>šIІM2µIF;eo¹ŒÂºM¹ûÕ¤MÛ€Ýá¶ËÞ¼=H#=vX? R-HwŸ¤niƒ¯2÷ªæ4æ>´ƒdëO÷•·ëHy¸ï¶A`çÌÄT¨©¦pÔIÆ/Äݺ›'ú“$ÓìjV·ÉÀ4¹x¬©Yçåï¿åróKp¸_tÜò¦êó5F€`‚…@Hi”YJ@Mmz‰ 29ºn] ÉB 2}ˆô NAÏ㇫­¼ï¼á©@¾“_úàkÕ¾yÌ>ª—jÛ-6 È.ôÍ›ÔWí:Ô"O3¦ž{–:ÆG_åÀ›ŸnV¯…ã\oœq®ÚÆ›q}­“‹› M bÌF´ÁÅýìôŸ—dÙæÖzZ¬§½ºÌƒ(dž5O”…,$ÉGr’¼$w ׿¼Ê/¾ÿzÑ¡§‹xÕ¦¸!ÿØMz*nâ\ùÍؾÿ°ª…î‡:E"ûôuèj¼§!'r|㌱èc¹;Ü0},|ôuoJôfb0zÒðòÙE áuN÷ÝOt_ ¬½nÌ;9szL¨u:?Á?8n'ß’îI "²Ðß\³D‹âôðr¡oÚäÂ5¾ò[MIzYÚ¸18›T´A¹À0Œ@Ä Q?àDÈõÖàä.›_ ´AΓ€4<–}ôÆ©6¤ |bŸ¨D2.ºÿ q¨j Ü8¬èþÈ®+ W5‹¯}ô \uщ›ýÈ&•"ö­ÿ~7LA/Dšnš5N}]OQþ"ÂÃF’?òÜ.ÊWš’PN8 ̵ 5P0›ÜV‹Í;¢\îA”ãš±ö2ˆuòÅ›uªÜZ2{iÊô…6ÛÕOõ¿çœÕÒ‡¤€]–ϰe&rüÝŽ<øýMÓ .&BÝ,øßµ[Ѭç J”éû<ÿüÞÔEx 7‰ïNý[ìZÿbšàÓº·~dî!TÈž•t Ú#¿†ˆê^kIV´»9muñþ–‘L.jíÖÄ;%|ø}iú<ÇwMS1z"š.¦:ø÷ Æd4ÿ³©ú|``"мº/ˆÒ6Q¯9=œ•hϹâ›íê+ñ ŠáóPn’ìjêDbŒ.–TÿºåU5ªŸå=ùEª¯f"„"a%ÒìyN\óGN¯õ—oÜ&½=£`@Wfòé[‡1áÝ\ óÐ(×Ö–7W]½^^+~&â¼Ð({®;ÉGr’¼$7ÉOóÕD¶êd®S?á&&(«¶¨æȳJÉi5¨‰g="È$É„Ý?tÑýä˺{ÊÉåö‰…£Îœ•°¿‹oáú°ý*Æ?8ÓV—ø$JH’ßÄ»?VEL‚ýqÆžMš\P=|ゞ0DRÞî½e‹w6^¢ çŒ#À_þÂp°¦ºšEú1'¢dF-ëÈ”.ðÃã°ìËl>h›ê#T®qºnjä`ðÒ$‡øj¿›jzL—}™%èM¡o´&$K&$f&“IÅ—p˜7%“A'~_ñM±7UU»&6òÑ SÃ{!µºB±î$ŸKN$Ë(Dùi×£¹B{Igõëá°ÁWÛöÁéÊjˆÁðØÓϦÚ+stßÐúŸ; ^½Ÿè¾òv݃)'åvÏ••õê»èšmŽGïÛÃѳÅðOö8ç·"¹]HN¯ëÐ)¡—‹ó:bij€º#ê«=ü[ Aÿ\Sõù#À0ÁF dˆ2Mœ´säe‚ˆRºÌêa…O` ‡ެS`îÅéªg‚`ƒÔ’ñÈ=}Ú"‘&‘ÈåÎG!>Ì]Ðþ:, ?HB W—pöôܘœgøMFM¹WÉ£áæRýuwɆr[ ÚV«ÎÖmmWëO>±é»)L=×`B¤zùºîMõÏ×B/íÙÛb9¾ IòH!)š@|jì ×ç˜ÛM¸è‡|íKæ^ÿ}©Ö•$½8cc³vÜHAÙC› öÉÎÎk¤*ŸfF M1¢ì2½ "ŽÄ.܃zÆÝnGïGÕÈg—M0›å6Y?J6©d®@šÄnF$DËp0#–„'áJø’fÑÓ ´Ô$¤€#Þ$'F-I’š'×ÂôÂsÝI^‹%å·£g›J–)ò¯¿@¶á\¬)½IèbR­{ÃýóÙÐF ëÒıVYþI²ö”Žú‡ÅŽú÷’ÂQ76{Åj}¯©&x÷ÿgèùTp4‘òÓÓ+N»ºéÛ(’A÷lÕù#À0m‚@HeÒ>3¢©¥ÈHÜ…ÑÓ$Å€IW…e•ðÚ'ߪÑóFiƒæg¹MVÇËAÉO.¹#/´qψ¦ÇIF+Ä™õu8bàÄ’ð$\ _ñ ¾¹!<5Ê þúz“ìø¾¹>fdô¶8í׋zzƒîQæœ`PB äØ%½†§MGd§JP2»Ñý–‹,¹\Ç%`”±J« U¸·Ì†®ÙNY«5]§‡ylPp¶?¦cŠï©•›*Xme¸;ܲšHH± }@IDATb´9 µ?Íìzk C2k0àÌc17ëe0ãj’¦Ød S5ÇD’cc£!IS ‘eÄ“p%|½3» ]ìÚÈaÆ­ÜT!:ÌMÀ+mžúåÆ[5·î.ó +Ð¥ZW¹,HÀ-N=8¬68i­Â÷˜ræ’Œ¢:ê,J¿nè–ÎÛd©<¢U5G÷ÒÊ.³oš±×¾ ~u¢ð&:œÞ ¸ŽZ»îž÷ïöÍ=;ém|Ûs™èí‘÷˜˜=bMQ¾8¨Iò?ño®ú‡¿—?¡ÉÅ›3¹ Y,NÛ|üÓåzò–¤}·ån ”ŒÜ/#À0­A 䈲¦]DBGnÖˆ$ òë"Shša ï ’×î‡Ã¡’h"Òä¯8Øéè·ïiC&ÿú­ÜTÁ‰ZÝÌ=ï`ä>´%ÀäˆM…ý¯iªI£×/ÂE‡Ú^"ÀFô¸A6É‘‘fU£H$9.6V-“Ùy•ðV›LƒÚl•ÚØ&“w £=BVWÙêX¨ÖKÃo×ÝZkŇ'˜pÝ£Šºö-]÷ …vÈ.r©¼ÃlÞ2*¢6.úšïp?áz\ôgqÍbýí¸oaJ]ŸN|ØoÖËÕ-º`t|Mr»0åBê¿úS.î‹`"rD™&'qºþT"]ä—´hª¶ÍȃƒµÊª +’&ÙéÒ8;‘8Qj)qRûø#ȉD[oÓ˜³®Â´ÿP;že5{!9>ÍÛæªk7ªL#Tm«Žl»QØ!VmSÑÜ‚4ɪ酦MövŸK”Z»›(‡½#ÊQå*·åF³s 溮aûIÄ76Rš>؉]|»<¢’«Úúf'è‡ D)zÝý *w@2g%ÍÛÿð/žºŽ†ÂýKÓÌ,Þ]pÇã_¼hîc¯µ>/Tø½\:}¾Ã+­0’äñot](");5kû„Š»fF UøÆ Z5”o‰4ñ£$´g´ Ü\…¡FÖŠDÙJ›½ê´ÊÙ©UÚEߤs×.3º‰r—8í7Ë]¡‘R—¸áPcŸ yG7¨5ŸXýûŒÁ͉îÑ4ÕN \HCìÖ*º¼…i'Ó ²S¦™\ž„«/©ÊRªU7‡uÕÊMâ<ˆòññSÚT ÷µ`¬»¨Öí¶€ƒ½PÒ]“×ýn»Ei¶„¦ñZòeíµF-,cÝ[(7 0H0¥¬9IIŠó)¼³\7³$Yt Ü‘öÞX` TE„´v‰îßh:¡™\˜ãû ÉEa³cŸ;6¦ÌQ{¨(éu¬M`pÎ0!‰@ÈeAˆÜ©eÔ¤¹4¦&ˆ@;L"ÉêF?› Í/P£\gÇ,´ÉÁ²Â(3¹‰g|÷n>-ò]oD;ë=Pe)ÁvV(8þLõH³}Ô)µ ûLíȬ‚$Èi•iãdW—&™°ô%•UÔªÇE§hå¦ ½bܘ«DO©ø‹ŽŽ¼JÁX÷÷w[á´úH€p|йë‚Hè³ˆÅö"ùºö¢/¹X:Q ×ݹ¸npPƒˆÌN$‚z¥xüD{äƒþòQ+åG ti¿XÿŽ5YOô†›'ßRhõf|$É÷âßhU£€ßç}©Û²—ã2ošrF€`Ú%Ê„áÆŒ4TZeÒ$ÛpcÙ(“3i”Éæˆ² ËÁ@ô”GˆæÄ¤xŸ‡œ5ñXóýÃØNAÒ¼ÊmÙ¨DxZj¼;ÂFý ŸcÒ(6¤U&_Äô@AäØ¨Ú®º<\øªIv¬@YÕaMˆ.Ñ}´rSrO–%AIÚ˜ã/:‘åÞ1ÞoV äºï=!ÆÒjÐ×Y‘Üs®Ý ºÌ/ššSC׺xX¢´díê³¹sÁY÷æ¤àëÁF {N¯5–+ðÏÄ016þØ:ýÕH’OˆsÎ×,5 ‡ýi”CM’"ýuÆÃö¼÷ð¸qf[mÍïD]¤gñû,8¿8Í9#À0!…@He‘<"j7"ƒh‚¡dÜÀç"É® Â3F0‰r,ùäªKݺvE¯ón]/€Ó5¿‚ÜýøÖÓ®ƒoÀˆÁA¤9¡É>J.l\öÛD–õˆfµŒZ`×dg\¬¨.Bûo›z5Ò܉·÷6Ø)±z$Ê®r‡Ê>e!ŽÝ_ëns(ðê†JÐEºˆñ¹½ô07-J çsãGKÖÞç±A0Ö½%rq›À!=§Ç4Åi{É©f—…߃Ó҇ΓntÝd^ëyíKpsHÞ»ÈlU›%ü”iΘðÀF­NS»½æ6l«þa#Mxjtœë^Sø#À0mŒ@» Ê„"DžÈÌ€´¦´yOx»ðÔ$“(Gzlæ£s-I—œ7ŠÊ6¡öö6¯‚-ûž…+&¼ÑdW‚0Q.>„Øä%Î5ÙI3O{š]D¥4SûÌË}(o;ê&Êç÷>óº·G4­ûÒ-8f‹þÌ“w‹'§Ä@LdË_û¢u‹–ZºöZ^Jb})÷÷º{) W Y³UœòÓø¢¬î‰\²âÃðcV½«Éd+ˆÉšÿ4Ýáu#Ö(zÓ¯'Oöލ+iiÆ|EžOoÎ(á·x‰´Ñ»¶uãqÆ0Œ@› Ðnˆ²@G"”< ²8V/éŸpã[² nY ‡K'>ï~~56WàÈñMpàØJÞŸŽO‚4Q Kãµ}¿RVY¨5êÝW+{Sèía§|5Ê­Mb~-]÷Üb¼¿I¥ÑµF])ÝM­ cºh©åk¯uáu!Ðëîµ \1`ì¸!1Ò~ þOq*sµA$é°NÑ]Ž$9[;¤ÂúE†ÉøþŽl“Õ„ßÁy3®ýY7—ç)2QTÛ-$É¥Æ0³Û·bsù:#À0mˆ€ÇO}JÑŠ¡jE­jŠÞÙ´DšÏ–¦”cáܳoC—qÿT»ØóÐk2DE$µ´ËV·;íA”½ÝÈ'í[§ÃËýï­Ê—u¯E“‹Ç7XA©{¸šb€+ÏòpY!„ö1¯ëNmÕšµ÷qX®ÞÁØ931Õv È?²ÐÞÒ“ð7z³4w̲¢ãÁžþ†ââ,öòÿ V[ýk‡/1ÖÎXà|Õ[9”… uù«–?¢m°–t/ôÞ‚¯w81Œ#Ðp³™v lGq¨ 4·ä¿xí ÚtÊE'¶kãw¤•½) ìê~hø±ÔÿDÙD~° YwiµÉäâ©I-3‘ýqÎ ¬Ù=§à®€,O’ŒÆUOOzñ˜eÅA'É4Ï{Å+Hr]ÆSœ@‡“·ù2ÿüÕŸ^Ží‡P|À-3˜^ñ¥=×eF -`¢Ü–è×Û ‡™ç=g]jêü£açêÕ Î¡¢Èpô„ÛãTïøtŸ€DY„²>Ža¢`€¶H9hrñöwÔ“ß7«9ÚB“h  "²Ç:$Éu»‚%+ Ýœ¾¦ä`nÚó”]Á]‡öm׉s’¢»cò‚êbqìMŽæ#ZØJ$Ì/wÛºµÂ›v\‡`P@€‰r(¬‚‡ ½2àœ³Ü ›¯²ž‚ªŸ~—${˜Îôœ/í¹.#À0¡€åPX…dhkŒÃ¥neÉÒ’4"Á†ºoØþ“2TÖº|¨¶¤/_Ú°É…/hqÝ`# ,œdÈš™°DqÂr¼#\áœAªÕIº[3V—Þ—þF¶=Ø2yŽ÷ÙÓÙ’¢üÕ}NzfêÃŽïÝÇÞ•0XêBQãïý793ó°8æœ`ö‚å^©¶2Á8Q¾ª-.…V˜)âã|óx! Ã`,CãÝ8­2›\ˆà<ÔØ>«WrvæžHçi²IRnÚ;?muñÿiçÚ¨°ûÃa&Y±¿‹Úd}/+"c“¾ŠS1z"jÊ/¡vh›,ƒžñµ®Ï0Œ@( ÀD9V¡ÚÊãçCë5‰úõ¸Ë./ÚI pH_ä^QÆ&mÎC œ™‰;{.’äó5Ù$i¥Á`“¶&øAD4<  ÷R@a R#)ÞGßó膢¦>í>VþÝëöýîc.1Œ#Ð~`¢âkÕ&?Z§¡28e†VnIá’T7QÞPhGëƒô5*›\4 _hC(àšZüÑ))Ÿ¡ë·xU´ûE-ò£é«Š/½¢°¬ ÅÓ†^·Ä0 µÀZô=Hó§?Rû“VÁËB^ú¨Øx@·Ð†§¼lÊÕF€9˜(‡Ü’üR ¦L0ð µ7èÄ©î#Ë2´æsªâØ…ý‘Ù}'¶ª¿è69 ç„VYeØ|ÐÚªþ<ç&æL9áÀ&¿üîð™¶E`ïåÉݲ³^Y‹Zä§ð+ªþ½Å÷3EzþBtý¶ÍðRÛ'оçtÊäŠNȸnú#ž®á¼“ïC4oVþ¢Õ–àõþÙÙ‡´c.0Œ#ÐÎp¶3Á;“¸Â㿟]¥¼#á‹­ïAmí8U^ eU5`³Ù5Áî‚ï(UZŠ¡¼êVµa¸)^~“ïÔkqv…ºcÐJËVJ°-Öí £^Uï‘m„™ ]c"A›ÿÉ1"¡ ýk=z~$ñM®²g'ž[ew|ˆ÷£+ªŽQö6„é•놯,* À-îCTÿ÷Q;pEßÃ?Õ>÷W1æJœïhjH~“%C˜‡ †ÏÝqF€`Ú&Êm¾Þ Œ‘ñÆ ¼ ¾ÊÜ Õ5çÃÁõøC´¢ÍáaFÿ,e”9è#¾*Åç=c$èã&Çþè“Ä*¯²À‘âÓPi±âÀyº08¨ë)©aö½ª¹Öé\ĹÅÂsCF dÍJü-F¤[‚MUÛ#|®SôLZø„?IË–?úNsX»Xw7j¯UÔè{ø}Ú’‰IÞÊO‰~ðÌK©[·†Ô[6.1Œ#àþaWÞŵZˆ™ì:p6l å•}`PJ"ŒÜ†ôMs˜©…½vœf–Zì+,œŸƒñà~ˆ9zvüœÃR“Áh4F8"Ìœ@#°ÿú1ååÿBây•{,é”"I7d¬*^‹ïTܧC ´f©i8ØíK…(:IúÇô¾Eßm Vúk$ÇCéµÉå&“™81Œ#Ю`¢¡?¶ªíñ—ÛvÊ ¹Ô-~sIôKîÂR_4zX=¸·ú)8zV|³ÞZ¹¦Ÿ7 &§ “É0A%Ëd–Á‰ÙsF IþoÛ¢ü¾mI?7}Õ±Câ\¨äY¯÷Œ()+ú_Æ„×É´C O}à€Ï"*iiÆ|űP4T@z¾÷–-§Ä1çŒ#À´WXÍ¢+§‘ä­»aù×90¼2ÜsÕ$&Éͬ=DÜ;w’Š×ÚÍ»€2¬µµ¨4s¨„+'FÀßdÏJºÅé”~ð$ɨ}Ùlî~A(’dšiyñß1«ÓCµAo¼vÆj[‚M>8oǹ÷SÛJÒ‰Øðˆ¿µ¤nÃ0Œ@¨!ÀåP[‘:yÈÜâÇý‡aÅÆ\1 \?ýœ•4ôÄ2õ.¼ÐËÝç[÷A7Üì7bkŸ’m¹õz·½tèIϵ'Ïíe.¶Ø_v*Î[…ÜøÒ¢ 7íÝž¶ºä€Ð4Ñ]·D-þqË ºû¦Î³ísð%?½-Iú±¡L’³^O3"I~厡٠Åþ¥ëÝî™ùV:~þùÑèÅüQÑ I÷ŸûmÜhÇœ3Œ#ÐÞ`rˆ­ i“lÚþ3 NIb›äV®Ù,Ž9û¹g÷U7õ Ô(Kl‚ÑJl;có}sGWÊe¯ÈN=<ˆ$YñëôšZ¼)΄j~¼,÷Y´ÒϨ“Ϧ“ ׌_p¢ÅfÖšÑñº» RA÷¯P;ËÅ0Œ@K`rKP P¡M.8z*ª¬ª‡ Õ©º%ÕV®Vk-ØìvÖ*wªo€&›;;)£J>‹fn’,ÁO£a\{ ÉkŸÓOǸy 4$ôÈ´öqìk޶É] -äµ$éJÙÙv_ûáúŒ#À„2L”Chu\DY†½ÇÐŽT?É!$žßE9~º JOµX™åµ<äošðÌC¢Læv•(Ëj”C¯;ኼ/%ôj1_v:7£6¶¿„$ýŸ©‹”6zÅÑíÚ¹-¬!¢ÈÎÿ ÉÞ äçVϘïl•g ›Íº±‰uMYÚÛoÖ¥ÿ Ñé³XŒ#À´&Ê-†Îÿ ÉìB–e8Y^¥FÜó6˜ÈºïwÁ¢ÿ|æ•@´ÞWXìU]W:p5º6‡ÖíÛöÀg?ìÖŽU £0‚!Eò#Wqî›p&¼91M!°{nß$$ÉŸ¡W‹ÅH’E”½rtývmÆê’[G¾Sâ{œç¦ À5EY¨“í–wQþxµ{ Žš"¢nnÍPI¨M¾_ôÚé?J ò %áœ`: L”Ch)NHàÊ«-jXê@ˆöó¡XûýèºÉ>‰ ¿³f š”X´z×\’¿š·w1æ»#øÕ⇈2áLxsbC gvÒt‹Å²C®_¢Õ‘¤-z£n”Ëõ›v6¤ ëž{êè™c²*¤2Zç_ñ}•'[#´ì´=†Ú䈺>sR3s>iMÜ–`PE€7ó…ÈÊÙ}HÓiC­kúûmIÚSP[væÃYý’à›œýf2À¸ýq#[?(,:‰ÁKrU{Ýgÿ³÷I„Ë'VÍ>ÞEÇË¡[\$\6i´êNÆÏÚSÛvBÑÉrˆ S·É_”>"# Ï=;V~»†öM‚éçŸ }™ ûÇÍr€2ôTÏáF'xkÕ÷ª6ùÍ›ÔÒÞ4 V~³SÕê^yÑuª¹‚ú«Ì½ª}vïÄ.pÅ…£¡[l”ŠË¢·?ƒYãG 7}ªf˜°LÃÈ{á&UÉ×,T„§¥Ú©z½ Í’„³ÀœÜÅqb»ç3YkN<‹o~‡RÂTÁ‰a¨ŸMOú„´p£ûµˆh¢ùºç ãNù !jÂÿ<}¾ãqÜ’ù]œ±ªäí‰$¯_ÓUqÊÿÃWõ/‰ÏƒßLK¹ò/­^PÅñöa¢~ o•‹¡}81Œ#Ð1`¢"ë*´›*GnQÓ™:î,> Y%•ä—97²ÅFš!6*Lêv.±k Ô &’:vX?´ã5AÚÐ8†šåÓåÕªšÚì1áÔ5jQ+’Í.«ZÝiã†i€)]0z ’Ý®ÐIyb÷ø I» tÏxמŸ^HÜÉe[ý”»ïDâøsQ»¡"ã?f¿^ºz™ÜšyäsÎ Ää&ѺYüƒ(sÎ0Œ@GD eï÷;"!0'A܈е6õBÍ.¥H3šK`" pC)7ØÑ¸~‘­]¦ÍoE§* ObW0£iÚïvA$ºÇN­sÎ×êQaš\ˆTb2í8X|b”Óæ¹ìË›T†u“º»d¦ú=ëÊt¥/tS»è•è¾N¦Í©±ñhž¤µG#&ÉÔ Ïï¿~@LyYåkøÝ¸NLã7Ö‚槯*ù»8מòµKt¿SœÊ¥BfEÒÝ|ÑC–£â¸Å¹l{ï£: µôy¿ÌÜV™q´XnÈ0Œ@`¢$ ½†Èœ?’ƒjx“Ȇ™ÒhvAfõ™Yä=¡F¼õÒó¡oiõ zõ7S=¤èwE'ÊaÁÓÐvد~ìýoh÷ØH؉AAD:^V©£‹dòrN¢~C9á+> ]çs ¬KÇ–•W¾‡ïú‰™#IÞ+é•kÓV–îçÚS¾n‰aœ¢È‹…Ì8Ÿ¿ÍX ¯Ç-ÍóÇŒ¹À©ÈWQ{ ÉÇ[Ú·cF ½ À¦íe¥ü$gB—( {ß2ÔÔ’ÙEŸ¤nªÖ™6Ñ‘¹™Sì;X¢ŽF„òûy>´jÑ%ڵɽ1QHƒ…&¸aH5‹ð4›èŽ›òhÓ\þ1ôeŒãÖ ”’¨zûønGžêï[܈H¦$'FÀß( ê²f%þ^q(ßá—ÑM’%éMÐéÓÛ+I^ûRT<Ú%ˆ·®k—«[Sú }¤µøáý*9Áù‚Ö$ý¯æöLí˜ Œ#ÀtP¼S;vÐɇҴêÇ@ÉvVjÜwžù7z½ÀÐηÎ9n˜>> }Ï¢g JÝã¢`𨗒-¯dSü)zµ ’KîÔHë|ïÜIj½úÿŒÞ>ø" þôúJµÝ£ÙSŠ7Á¸á©°êÛðé7;à‰ÛgÑ|zà˜=a$lÌú ëìP7þjÚXU3HlÙ÷äƒA`û¬^ÉÙY¯¾k¡ ¿Þe¨"½#}uñGâ\{ËÉ_òºÅO’¿ä^$;j}Oïvõn[kç’Ψ›±4êÿXÂuÆßS™#À0&Ê!´ÂDÚZBÜÈ“}(‘w áa‚ŽÉÅ¢û¯ ¢š(ðÆ}WORµºäé‚m®›ÃtßæŠ>+\®-CòLZd2¹ ÷j§*ªá¹ÿ~û•þbê§oÏnðÈMSU7pdzQ?]:q$Ê9 OKª)Ç3Ï=£ÊxôÂA+z¼²Q"éžs s7ÏG™Ï !ÖL/Z‚µÏrƒB gfâµÅþ Þi®Ý§(ÊÍ ~•±êØ¡ÖGaÖ-yê üz_BÍð–Qt ÿõ%¿µ´zN¥“&EUV–=MÖj’`qrfæa×ÿË0Œ@ÇFà—l¦cÏ—gW‡€5¼õ“ Èžçkq`E•£ßʰ+ÏZ;Í7šJ ‘dQßä…hO’,ÚqδÝs{u­±Ø_‘åѽ$I÷tZøOJËZç BôÙV9úKžŠ]êPDt)…¢å0 5ÌÃR{ÂÉò*8]Q#öIÁÇ_çEá›2iìÜD H"ä¤ã1’ß½.û0öýöšàî«&BLd8|·#öÃÕg@e>ú*Îîß#(L^s°±ØpÞr(µS¶½‚=h!¨qAwtúG­<– @.…;^Zó|Ø Éa#“ × *Ò÷ÊÇü9ßÂsFs8œ×ièIzv§ÁF€è¬0Q‘•÷$mdÿKç‚‘6fÿ¤CZa2¹èëOm³Ëpå…Ã`Ô —)e‘Ê*kTâ|ÇåÀ€^ñ0°O<ã£â2ä==ºÅ@LD8 @sJD¾)íCmõÐ~Iß%Rý$t†ƒIiªÙ¥ZÇßÿÐC€¾ÛkOÜý=&÷ç?~œ“Xë„—1õU¢W|èqâSÛÒØ~ÑüûZq¾£åþe©>±Ir,Í ™r¡)"êZéjÿ…Ý&ÛçüôQš;8|øø7ð}×Ѱäù0Œ#à+L”}E,õ#ÃMp¨´<#ÜvéxÕô‚ˆïkŸ| +6n‡ë§Ÿ£=M.J‡Ð¦™Rrwõ·Í)LÐ%V3…A}aù†\øáÇ|U+i²Æ(iìÚDŸ‚ûÝÄúx™›„74^kÏUTY!ÚÄN^Z‹c[´Ïš™t“UV–¢™EWm|5µî¦´5E›JµÓ±PSuâ?8¯³Ô¹I`1€áò‹ï«ô«{™‚sF]¦,"p‰U “tD,yNŒ#ÀøŠe_ p}ÒpÆD„A•ÅŠ¶½6•€xHµ{ W’Ôm”O1Ù7”H Léšb$#±—e'TU[µªg÷ï _gîƒý‡KÑ Fw¸'}°jò Çùt:7"¦Ÿw¶V?±Êj…ø3›]h?÷sE§M~ír§xv÷È›‚ú¡Ž‚Ús^ •×,Ñý£ã]!®IŠîŽ)Ø·‹cäÇÒÒ"jœÍ†YÚÿû܃þè›û`F ½#À*¶ZA"Éô!ÂJ{âö>ÌîñÓ•p¤ô´ªùÝSP =qcŸ7) Í*¢ÐoÊݤ޴ýز֔\ÉÕ A¥z©H”ÍH¦E†$:÷§Ã‡$š4ÌÅ'+T3qÝß9áHxv‹ C×w:Ú¤¤fÅýµeáBúE¾Iò.\7’,å£ë‹ÒW—ÜÙHòúņiH’ÿ"Åoíßf<"¿+Žý•[$yš°ô¢þðÏOqLTœFšý5÷Ã0Œ@{E€5Ê!²r‚$ãÚöFCD¸Q%“¹jó—Øä™ÙŸ?²?ŒÕßë®§Ž+¿ÙÛ÷†á’¡_rw­m7´u&»à7WlB’ª¨uâ˜A0õܳ`Öøá¸/Þüt³z-=bÜ8ãÜ€mæËýé˜ ÄEA‡ròØA8 Ì5¡¹ÐædÍî9$+ó•¡ ~‘ÑtI÷Ç´UÇjÚ\È ðùâ°vÅö?JUfà÷u£9cÂ|€~ýÈØ±½jµØo]ÒÁc 7ÖJŒÅ9#À0í&Ê!´H¤íÔ«DNC{uƒìÅP€›â< ¨¿ÄMNˆƒE÷kotÑ-mà›øÄÅúçÎ9«/¤I»,«®áD=Ò,¿öÑ7pÕEc`äÀÞ*þaW>¬ÿ~7L{DšÃà¦YãTs «ÍŽa*qíý™~?,Þ1hÙi eׇðæ( '²²÷>Nù(Q˜&z´P@º-cuñ6í\/|ñz—X{yÙ*œ¦êÙIòa¼zòä~ßá[k·.Á—-*¤ä¤Î¼ü?¹£ƒ#ÌÓcFÀ{˜)xU@k í&‘8"t#R{@D˜V|³½E¸M*D :§(~aÆ3Ÿ·œ¨A–ð=,¿¤è¢Äü¿©™¹ßŠcÎF€`\ƒ(ÿâ×!;,V»Î{èyI„é¹ð°0èÛ£+œ(¯†ެ˜{q˜ŒŸiÀÈ=}Ú"‘&™HòÎG!)B„3˜ÍáNÄ•ð%2›^¸WM`Y–ëÛÿþâÞu·h])gN 'OÔü5§ZHFÔ"× 3|<-íî;£fsÝýmN§ó·YtmünÞ[&Žý™ç/ú=jïUìñði%<âaöÏ}1Œ#ÐQQþVvkíé²òj&Êõ!S"pFÔÀ±‹ŒŒ€á}Àáp`0Š|W—M›åz¢´ÛC²I&s Ò$'†ËÐ+ÎŒ8š! ±$< WÂW˜^´Û‰úYð²ŠjÅn³éÐÏcPwû¯S^^µÈ)Ëwá÷ƒ²$}eRàΫKòaÕÂŒÚ]¢‡‹ d§üŠùhúùIxÄ ‘¸ÖÚm¬z‹ÏgÍIšYVVñv º"£ŽðÙ° ýöÎËXUBž.:eZ÷Bx_§­öcœ¼‰ÀoenB\›ðï­…ÿ“ˆ×Ù‚ÃÖÔÌíol£€ÿ¥çF€*Á"ÊâÇ—rçáý?mOî×ÿšñÕøØ³Sƒ:áPŒÌh3ÙÓ’Ôn·£wú¡¿á8t—¼ Ž”ª„6ÁE!ñ‹‰2cý`-eè Hîç*ª,L¤Võ“lD‚ÜÝ䀄ÚxG¨Ä866bbðCdñ$\ _6»p¯#݇ÂøHþòèy¯º+µ¢„.ߺƒ"¿¨ÈÎ_yvƒDp˜ÂïÉøä`‘çùÎTVÃSWX‰ Ç×Í»Do¿4ý®À¸ÁËK}ât^Lc!þ2þ¹ùMÀygZHž+#ÀtX‚Á®<xÉ}ƒücæ–ç\4µõaËá@Èž—– ŠC×%^ꗹݯQþDל3Œ#ÐQQ&¬ˆÆ©$sòê8t`ÿgüáêý‡Ša`Ÿ$<ÅI àÒ*ë!L1ébɤ=&»Z2à if³ºÕÖ¢ÆÙÑíd$ÊNü¸êR€Žžè‚y® ¼ô:"ɨ‰W±!“ ´IFs Ò$«¦š6™7ñy~7èþÛuàˆtì`Á§xžîMqŸ¶êÑ+gVòȬÙI¯"Aç9.ÛÛf³ñÁaËŽÜÚsÜP,¯{Nÿ >’\ê–MwçôùŽ-îc?—$ùiÅ ul¥£Ñ1qòóÜ#À0`e¡¢`ú!¶ÑgóÚ•Ÿ÷é?`Ê{ëˆyô–ÙºöâÍ!Xß"Dü()tIFghnaµXÁZG”i³Ÿ,QvÒ+ô`‰Øæã\HCLæ„yµ ïdfA6ÞfõC&tpääB€¼ƒàýçDwå›V­XgéÞdYÜ·>ÁåÚ¬Wù” öûº]´HRbOÚÊ¢õ>uØA+¯Y¬¿µøhÓÓIKfÌ—ßÖŽý\ÈË•²r·èýœÿ–#ð 48gF q‚A”itÒN ’\‹åÚššÊªœÍßÖM¼ø¾·W}§Ü~ÅD6Á ¤ê’ DîÔ2’e*QŽ0›U’Lýl6zÅ@2šhxåŽÌ—ë”ÉÚ…¥¦ˆ{dVAøYVÝÁádW—&™°ääB€î»cÇË`çÖͯVV–QØbº7YöY£œ33ñÚò²Š¥È°{ŒoZÈ,I0Ÿî½ìˆEœïÌùšç —H²üªx¤Å¯äªéóþô(Ì_X”¹sõyù?Ó&Jõ)×dmÿ¬\Ú<ȉ`F ‚I”I£L?ÂVüЦ%÷»or»Ä'&ë ø”g—X³ŒhÔ%"uÂiB…ë8"ƒdraÃðÏ.m²¬j”Ñ/ªªQî ZeÂFý Ò(6¤U&Éô@A䨍j™].X“,¾Ux¢&™HrÖÞéàÏ{ßÛ¶á‹­x•îIº7é¥{Õk¢¼cNÏÁ6§üYQ.ÂvIúÒ`4Ü;jùÑŸ=NvêâgKLgËNûGH’Õ¿½ø5ÎÑK ×IÒB¯ñöÀ¼ü÷⟆1Ôï‹^oºÏ×>¸>#À0`eRžÐ¯?ôƒ\Ÿpü„}½|Ùzü#NšŽ9E'O+×N=WÇ6ˈ†G"’G¤PÜGdí”U‚Œ¦´ÙOlø#2¥ÎB”i®.l\öÛD–õˆfµŒZ`Gu9¡c´I&s Ò$ìÛýá—¿Oª¤M¦{’îMºGé^ O,6œL²]wNÑæ>v§¼¿tªk3ª‰_ÕcøóPÚê’nÙ9Ï®!¢‡ÃfYƒ³!§Ã:£yÖÔKû€¤Â´´h˜õgÑ9j÷ÿÒwÛ¶qÌ9#À0Œ@Ó›( 2ý0“Oúq5|½bÙú“%ÅÅé&_÷ü;ëcÏÐKÉ–* Ì~–ëÖˆ2‘?"~df@ZSÚ¼'Ì-ˆ ‚,òº¦2#<(Q.>„ØÜ'ÎuÈÉû8)ò“L.àÈ»mÜs:앹›¿ýwÎwI“\‰ºéã©Qn’(gÏJœ]µçŒGu¸GE>Òý=R÷Ä•?Q¿œêølIb¤Ã^º ûÐ)üÚV(ãÌiÖÔ5>F¿„TbŽ£îí'é—Ô‰Ä#À0Œ€‹(“(¤î$;e²ƒ$ :õR7û¨vs;¶lÊùy{ÎþqSgL–eÇüAA²£D›Ã”¸ØH)Üh IãÒI8 ‘–¾³^ƒ–×'Åõƒ&H DdØ3Õ?ö¼ˆòY¶ÅÚ{ ß`‘ÂÄSL &‚ß 4‹uTüyß×[Ö¯ù¼¦¦ª •×}*0§{’îMºG5Ƚ,©¯Ã¡¼ˆ® ç Iƪ®„+±YÒnÖ+Ù P"NsŽ(ÊBݺ%Oþuôib…ëçÎ|Èöc ÊËsnÔ¼JŒ¡×KwK™ÙôÆ€#À0Œ€—“(“âIØ)9¦ñ‚é¨$Úb©¶£vy žÿ|ÐðÑ}S bŽŠêf6ÇôÒ@‡\š*„ÊÙ¹7_”;CÞÅjÑæ~:ÜÜ©æNë{VŸÐ^{‡ì°ÖZ,–ꪓ‡~þiÏO;shHkL —4¾D”‰0S™Ì.„}²‡’ÏbÚ=w˜ÉZsb’äÇ‘ù™]gÑNÃfwØ~3zuñÿу­8Ϲµ‹ŸzæhgÐûÇŒùŽÏµã×Õf«ÑBb£ ûŸý2s¿ ÀPÜ%#À0`eR%Ę“æJdOMçéG8ìçs÷àç',©¦%ÑÆuÿ>43áe!Æ{ÿXºT”;C¾°w²6÷WíTs§õýõ"I›ˆ­½ ¬t¿ÑGì û‹qU݇4ÉD’›Ô&çÌJ¼¨ÆrüXo°°\ÆQÉ‹X´¥÷Ä‚¿=û×·È–€Ó/X·D÷šH= ]‘¤gÑ Ü›Úq€ µµ5î]ÝKGÑpk^€†ânF€èЛ(˜ôÃ-^ÿѺ8¦qÒvQ¦~dšAò‘晈r{ø%>rvšƒ6Ó©SÍÝcÞ¢jó÷‘d2§ {Žî/"ÊDŒ‰,×Ô}èÞ£ët/j)는ŠÍº½Y\«ÄrâÐK÷|ÕoÚ"Ïó\>µ‹ô—áÒ&×%éC$ÉÁ‚Àþ)ËK5Cq*7h£‚î7ý³³éí'F€`h ¢,~Àé‡Ù³L?ÖôízÃÀ\Ýè‡y}¢Ø_°éx+Ú¶»¦]Ñ»„GêTs÷˜·(†Êü鞢$î-A”ɬB<ŒY&ÒLÇt^Ø%«mÉÌÂRsü~¥Öò'¼ƒ5áW®HºÇÓ̼*-[&ß©^W9÷D`Ýs†ñhüžoþHèsS ÍSNŽsÚa}]È‚[]ÿÛ?'gµ8æœ`FÀ7Î`:¾5mUmúA¦pÏs"ÎôÃMšdO’,l™‰ ‡2IFñ ˜þé,)ÝÔy¤N5wy‹b(ÍŸî+ñ¡ûÌ“,Ó}Fä˜rúÐ5M“ŒACæX,ÇŸÇÆð¼–སS¾²¸`™vž ¿D€|%;œöUx…úé¯Öþ]Ô¥“o)¤‡“€¦ÓŽÚçpå{Ñ ¨ù/1™Ì¿ è€Ü9#À0¶"ÊVúd™´ZôN29"ÇB+êDù$ÊÚiRtSç‘:ÕÜ=æ-Š¡4º§(‰ûKeA˜Å±¸ÙsF(Né4³¸P½#]í‘ãI{Iº'}uñƺSœ5À/šûØj­ä>N­&A±A ›:y^å‰&šùåR^Æè‹Ùy‡Ö™¤»·÷–-§´c.0Œ#ÀøŒ@[e˜~Ô…VKØR1vÉžšäP'Ê´9ªÓ¤¨3m”;ÕÜXäPš¿ Ê”‹‘bñç`Çå‰ ýÏhÓz;º¤DOã?O¦¥ý‡´p#Ý—œšA`ýÒ˜®¶ÚÊÏñdªŠÝ Þ0mêÃÖ‚fš¶úrñ”)‘Õ'Jÿ):Â7q˜jçŒ#À´P ÊBzñãM?æ”< ²8V/„ð?dûÙiRØ™ž:ÕÜXäP›?ÝO"‰{K»Ü½YNþÖfs>Ž'c0^H$½n6>1lÙ‘S°--85‹@Öë=#JË‹V#IB•ñÖ¨Õ)ú˦=lßÑlc?T¨:qüY|&ê«v%I'•°ˆ{ýÐ-wÁ0Œ@§G ”ˆrýÅøÅ{ý !xLšñN“ôgåN5÷¹ÝÌ?{VÂå¸Yo Þ`ýëÍc½Î¤(myÑÞzçù° 6l˜d(Ùö͇Xe\]5'†ˆüõ´yŽ M4óÛ¥¼´´ñ 8îÏ:èÍú·ý¿ÿ¾ÔopGŒ#ÀtbB™(wâeá©3þG ÷²äQ²ÃñFÕ›äÙ;Ù!KzÝCi+‹Ö{žç²wX¶}ûÖœ©Õ–¤fÌ“?ÒŽXÀÀ"f[­å-$ɪY>»®îŸ½ýÝÉ]3Œ#Щ`¢Ü©–›'ÛøqNBb­,=-;ì· ‰…f‡Œö¸QZ˜–>ä5¶CnÙ7cÍbÝ3hÛ}‹ÖZ’þ2s“‚³%ÕÚ,O¡ÉÅ@ í’ËM&å7A˜aF “ ÀD¹“,4O³ó!°ÿþaå…•ZÊcH¦¢Õm}ªíȪ^1)aOŽXsè4¬.î|àøaÆkë I~ÔÝ•ô/$Ét¶t }Ôùhfñ fr!I÷Þ²ýh`GåÞF€è\0Qî\ëͳí$dÎJºª¬ r1(J?Ï)£ÖqµQ§Ÿ7r屟<ÏsÙ7Ö-Ò݃&,ZdB4yX9½ï•wËÇôñóÏ®°V½ƒoT¸®_¤fåþË·YpmF€`æ`¢ÜB|hGäÌNšˆîYEqž{†Ø’´tʃé+K¾8ã<øŒÀÚÅúp¾,4ô¨¢ß`ŽO¹FºzYÐ6tVX«_B’¬>á‚Óa†°[}ž7`F€h&ÊÍBÄÐG gVòHìÏÈNçtOiQÓxéOvú ;íy˾#°f±þJ$Éo!IF~ªš±ü`Žì>'Q÷„´yc®ÀðØ7‹c^w¯­[ˆcÎF€`ü‡eÿaÉ=1AG`çÌÄT›v*öëpp•¼¹„¬(/ë æ§G¯(, –I@Ðâ€ë¦ÉŠü?Ò’R‚ífcìôÉ÷¯ –…ii=N™¼l¨ =–ü·_fÎâ˜sF€`ÿ"ÀDÙ¿xroŒ@P O6§ô¸MQîÂWðFmP‰¢\Jo‡ëõO ÿôØaí<Z…ÀÚņ‰2ÈŸ`'&µ# öIá‘S&?P†!ÁI¸qPÊKõ¨ÍîF#âÛ‚CÈÙï Îè< #À0&ÊsÝyÖíý׈)¯¨šW++) DzNµ‹ŸêôÒcc>-ÞãyžË­C`ýRã9èzT³Ú“á†ð‹/z êxëzö­uAú˜ûP†©Ô _8u:éÆ~™Ùå¾õµF€`|A€‰²/hq]F  Woù•w——Wþ5‹Ýë‰ñ-’¦GÒV•üPï<¶uKL#œû:$¨ÑÔz·8f2†]|у– ºa+ÐLE¾Ì0ÁG@Y¸P—53é¦ò‚ÊŸ ¼àI’‘´íI73cMéD&Éþ_›5KMÃÑöû+4méJ½£÷¸Ñh¼øâ­ùþ­ñ•´4£Ã.¿‹k¯j´Ñäb{jÿl¼_aF€ð¬Qö’Ü#àgrf&ÎÉÊzõ¯,dj4ÝI’ t üq̪’ÿ!iò¼â®Ã¥V!P§Iþ qwiï%(½aê%Úö¶ªã4ÎçŸQŽÑÔެƒt=z0±µ +nÂ0Œ#à#L”}Œ«3F gN ²,?#ƒrž'AFR\ŠjÍ¿@^¯¥½‘MÑõ-J§ì_%ɨI$Q.—t†)Ó¶çüŒÑÙ9_Œ‹ÈIÙšË6èÎF€0L”'ûå@IDAT 0wÏx‹@öÌ8%çH’/:£•ȉŸ3‡w_:lÙntEVrÆe>ðõI2>˜”©$yž=Ó£x×ÓɱccÊìµoã+ÕD”>OÍÊù;? y‡×bFÀ0QöŠÜ#Ð ˆ +’s¡S‘/×6lX™T£”®ÆåJ IÌ»æŒ 7Ož¼Ñ!εEN?óÒG¿ß¥êø’TfÒ]-­[WÛòð˜Œ#À0#ÀD¹qløJ'F sVÒ$PœÈŠ}š ÃüXÊÂ@!‹ñn°:§¾8HŽ¿ÐI°hÌê’¯èÚš è7SH!°n‰a’Ín_ŽD4ŽÃr(’îî ä‚¶_¯cF^޲ýQM’žKÍÞÎ\ \`F ´`¢ZëÁÒ´»çöêj©qÜ‘eùö^´?î}†ÊQéÖGF~Ѩ•ÇrÛ@<ÒKÖ.Ò_ïtÊoau“Ú]­é@õ´Žõ^vÐj3F³Ëð6~¿TÆŽÚíÏSS> ÙÛ:.wÎ0Œ#Ðr˜(·;nÙÎØ13ñlôãvÅ~Ú‹šQÓç‘$+™ƒÁø\úŠÃy¸‚ äÇœÁ5¬#¡p̆™SØC‚…?¾‹ÝRõ)~Ï¢Tø$)Ï`ŽºVZ¶ Ä81Œ#À„*L”CueX®€  ,\¨ËÍ|u–,Á¸Aï"u‚Œä¸é5£IùÇÈåÅ¥‚;õ6L2X3¿}Õ©(·‹N‘)ÿ¨×‡Ïœò°å°8×–9™ôäçïßVô¯“£Ê¨S.Mùî»Óm)Í0Œ#Ð<L”›Çˆktö_? ¦¢¬òÖ¬ÌWïG­^ê™Úc|.AŽºÍænï[¶›†p q¾|9º›%ó›Ðý›ë‡ä•àKïråÔNU„ŠøyùûŸE’<Å%–nLÉܱ;Täc9F€`G€‰rãØð•€ÀöË“ÉvÇýåå7#Aq½övÏËô–ƒNÿRúªcß¹N»¯r)dXû¼qtmMmÚKÑ„DS™ÄØÑw¦ß•m×εq!/mÔ¯ÐÜ8^Åï⿇N^w]¿­9!EæÀ0Œ@A€‰rYÈÎ2ìى碊˜´Ç×àkí3´Ç„jw€¢{ÝÑíô^r$ª³¬“¿æ¹~‘þ§âxû ¯ëÓ)éÐyžü×P4c86òqtkq›6 ë—™O4™¸À0Œ#à5L”½†Š+¶Âö‰ñ]N§2œä8S{,Uãn®$=¼‘þiÉV—œì½¢­ÖËãf½Þ3¢´¼øYqÞ©õ'A™¤ÓýjÆÑßu,lþOI9½ßwû~¯ýDN®Ι¨Ý¯”ÿôÚm÷cþ?‡lôïAï§±¹°ýô^§é~E>ÔaÔ8¸ÿ¬«ët_'—/™ùœC7ˆ 0&PC°P®!U››©<‰‰ñå2u«îϹëð`׊ü¾³õ¸4«EŽ™ùnŽ®°» Á —mäe£Œ÷„ØoG‡Ôz[¿~Mu¿6f¸¨›×0¹Çít_Ö`ùr~ÂR#ÅaL€ ” åòqã\•@`Õ¸ž¦_¿51ñÏ›a5nR¸HËz,ŧX{}»oEáýü»vøa¢ó¢#óÜ5 ôHŠ$LývÓ%£Bkê·@û°±ï’K¢2Àüͪµ/E†C—·X¾|Wp:ÞfL€ 0šK€…rÍ»ÙòÄk[7Þœ›ñ€ú6Xâ¬ó ¼™‡^A ' ©ÞŽÖê~ÜyÖ&ž{¶FŽôñ8­ë@ÚªS£ ’­·ó`E¦ÍI­Ûœ:ºË |Ç/åĤP×_ïHIÙL~Ô}¬H©;4íÌp±úÄ´ˆkeL€ 0ª ÀB¹*¨r™ì¼¾Eľ\ÿ|úãVå˹;ç­€4’b7”ÒÇ©}ÐóÛ}ò 8P þQ{ÌÖõ@ÚÊ÷0•Zß ^Á¿Âqëeêó„È?‚v†ÒfJÊ–WÑö€›¾øxOJ#ÄmaL€ T+”ï;¡aƒ€¡µ’ª9DVLå )[)ßfìd·0ð£6`zËJîÛ‡ÃùÔqO$UCµ¥ªS¹ÉUW5;×PêÖ}9¾ëa'Ì4Ú˜>+Û_+Mý·Oï?JÇ´÷om¿¡_ …ÝêDÿ’¬¿ÍßfaàG5mÆ_ˆíÍ17”Æ¿8>ï¶ý¯?ÇÊÿ„¦ËN‡ówŽC‹º}Ш̿;ÚÚ»ç(ß#ì¶ M>Û.qõÛß¼Á˜`µ†@ÊÏ{á»ý»ïÃW‰ÖM½.®*ÜŸmFéY2ÌðÁ¨sbC³ŒÝçUg |Ž03Ë¥r]‘x$ óÌ^Ã/ÇæF¿6yòùhK,­ž–%^Ù¼³PÆ-IW4ý^pj]¸V3ˆÛŸ!’? oðE࣠³<…“þ-zhRxzxæHø>h?Ó‡ÉL3BK—Ná=¡ãß@û«ZÇžàèÂm昱ʧ¢hüÇÓø+¡MŠsŽîñÜAãRaöK®3¶oûãm”]ì†áI‚ÇÃãø€È+¡ø»özkß^7JÓ|Þ>±ÐæOÚ­XOD80&À˜@í#Pc„ò°øq—ìõï|éãZfìTíl­2¶I·îÅcü“6XâÐët‹1mErýNMwÄ´z>+"ãÛGÅ}bÂ\!+­}]¯P¦S+®l2F*qƒ2uësÒ…+‚ Øˆý†9vûfÏμý¥ú() ý”åÎxKSZóF®­ª¥shâÜ$ÃdîI?þ>.öëÄN½GÓþ÷ê;Äùrçô„±pa8ñaîÄ&Q†y0Aéúý4–v‹ -—Jv×àG½›Bñ"v;íurßž—Á’L/æ«b¹¨]»Sî¨ ß`L lj„PÆEÿ!<ÉŸX/÷8kç"Ñ,k›o‚Æ7 ┣i‘{£š‹%-69ÑàÛ;òñwŸ{æe$5°”Ú­!¨èb7“®jÑ!75SøÂs„áÒ©ô„cÔ¸”‡¡Ññ“öAßòÍyL¢Ê~<èp¸ž‹Ñö«îîïDCçvÿ ‘ÁÍ‚héZC‹<äj-Öæ^Ù4Ýl2çÍ(ˆåÉAI«}“f´ÐÕ騸­]9/ó`?>x”ñfM™ôA|ôæKÜr滋È?\‘Ñ×È3Bö…C›7¯™`L üÖòQ¥9ežH“Ú§nцlþ‘\¥Öô‰Ï5[f8Ú§n•—/ÜþhüÃè]ÜÉòZ!¹öò&í¯lòxâ“L÷%'&#O$C“2–Â/¤&®‘Í[4ë÷ÝûÊñaj'µ×Eí§~´pmçFNw@$×ÆÛ…Ÿs£¦9Z¸ÖkðžDç’ThÜ UQªŸ?LŠ­?ûí¼G0â²­ ™ã ïrù£æ5E$§ôíѾö³À3Üê‡)aòâÖK–µûÅk&À˜¨BÙ¢,ïzÜ3ˆ,ÉÉê¿æTûž¦¹Óô ‹W›Á*¹n‡goºï?›>}ý¥ÐúRX™\1V iÚÆÔÅõ°¦Ýàªo‘vi… Ý4ñµ¦ÄçŽzâÛÈÿ‚Z¹ÞË¢q¦8Ú}©Ëå~"YõøŒÇPJœÒ/òxýCíòwˆóèÏwžóÎ1FÿÒ”W–4?ÿ<Й›øËßáA¾zv^ Þa©iŠü‘9vtȯ“ûô馔>äò_–»Ž°‹Z-ýïØC~ô¸L€ 0ŠY¡Ÿ>–äúym•ÒqQ›ßßêmçö1&À˜@åU¡¬õåHMjÍÉ'™,¤ÊN€¸½k¡öí)×5í~Áy÷B(¿„RȪHKŸå•WÄõ0¥ÿjì¹Úð½­Ú ‚‰cçišúÉÇÊ߯’Ï…ªƒ@ð!~>^¾ú\DD~ÓoF Y¥ˆ­½{ämVÎ_kÜQT–ˆÈ¨È»cv ~q¯bpÉg™8íDI“°Ø7J+¹Ý2ÛÙÅ=§ßç_‹Ÿÿ%H#r\ë¶§¾Ê_×+ÀÖçz³@$·¢48²qƒx9Dòªâòp<`L€ ÔN‹[uOûçýõÃ|°Mi 8'ŽÓÆ5ºöêkÏ»æðÂÈ–Q΋]».Õ¤ ø×b‰c!`=Cs„Ïì5ó¯ÔàýU´°&_tÍ?N—š£MÇ¡âÀQbÚ¸¦t^}ôêóËQb‰®7Ç«1qZóÈ©ûîñ‹IýÂç qõÒ¢Òs`L€ Ôn¡&”-«¢Ë6ŠæI®Ýø«§wàHÓ¨~Ñ9ÕÏÔœøˆÀ[üd) L‡€Mˆa9qßDÉ:s:ÏÚ”Q=­³j±Æ[dM¯ß¸É…Ô^š'¹ÛPk«Gküé¼B'±ÐS…€¾-mÇi>dÓ<8â@ÚÞG¹1Dr +ŽŸ•ÓõÐeûÖÕ47 »ŒNÏ8:¿óæ—RÇÔ+7¶M\=ßNÃk&À˜8¹„¤PÆ´¡/îä©´#‘æYŽðgḚ̈h§×P" ¶[—%’å(Ño4)¾éÕ÷ÔEÒ³"SØ—·ª¾¿¶P¦iì"4§³U˜ÌÂW÷rY(WÂÐ<Ëà uWGÓîÑ8—Z(ÿüF£èܬÃ÷a>äÿ _Ãàœ¹ªNv¤Ì¸ið£Æ,$œ‘®F…=}úD¦g¤ÎBßΠ†ãÀƒ{µº£íŠ53kTG¸±L€ 0&P©BM(Óãwf»héˤm•D RϹaQ"ï6ær¶)äWÿ\¼ŸÃç‰ãïöWRMå*&oÜó-Ê—³q¸Lc‘\.”EgO-KkÔ {I(ïã¾Ô7ûÕú±Â›:2'ëÐÃxQ¿@ÉRlÿ3÷ïV߀}Sž‚H®¹,Éé©ßCŸè…ÔFàÓÔ~ó`L€ œ”BQ(;5!cܦŸ…R%’a†_ì o´ñ¶¥Gþ…ba!¿ãP¹¡vбH®n‡Ô¢]ÒÇ㕈'W(8—8î?NŠˆËÕ½w«œ£÷"mA?v)¶!û³Mêôüï7ég×x—Ì“\'==uDò™6kMÊQí’VMµóš 0&ÀN^¡$”IÑâ€-‹¤*8& é ë1‰Qw²,ÚÌKýy*;Øm öXb™Ü§+».|-¬óªØqŸ=Ñy2{½þÜ«òÇ"€ ²UiÚ³‘}ÏùðüóÉE'I ‹Ø_7ò_Ü›2}íöãnâáv‰«Oèg¿í¶ðš 0&ÀNÌnAS(r`L€ 0&`%¡LâØZ¾[ÍãSùò-µ$”‚­ŠÄ:XU~Å%—÷üv9Ø¢\2°òîÍ?¯,Þ³žwî”Æþ´Ô[À;ªp™x×ó'|rúKÿ3v¦”ø3Ï(œ¤ÆþN9ýô&Ùº÷G¡Tꀘ˜íex»¤ÕoרNqÙ`L J„’P¦Ú¢©J:Ë…ZÈzks¦u(„àö”è? ­©mÀtnQSÑžlTG]®)½uá»# B¬ª84盃ñá t0 ?â©©Ý-²Ý;ÏìçóåB$‹N”ÙÀrG»ÄU™#™`Là¤&jB™#TÄÛq Gt¬¨Óëlán'R‰œ¿67OEháÂÌÍ©H1Á¢´"åTvÞPmW‘ý”Ò!êÇ.ê5ï/2lû¶bÒ„*ΰ¡ûÊ?½uŒãÐiQNuZÍ\ƒÏÅ¿©É†µ?«&OóVDßQxq¯•×güKr{+ó$kšö϶+V~HÄL€ 0&À‚„¢Pj^é7#ÛŸ*:?÷‘ØøØ-";ưㄘnýEÖÖ ÂÌ.(ghsïÓ"<®­H[¹DHGÕ¡tDF‹æ7ß' ¼Bhaá"sãj±û£WEÖfúrpYBMqi){;Ï»m©Ø»å[±ù×çŽ Ds„‰-Ïÿúé¸i‹KЬÓÕ¢ëùω]|.Â"h‘ª $È;Ÿû”ˆëüÂ-rÒwˆ]ë?ÉIoT´Â£°¤~×Þ½ìQ_£õÐ)+ÚŸóoïׯnø1àªu~B|š]Ý‘ ˜`L€ M ”sW›%Y:ÃDûQ/аú‹¦RŠXét‰Ø§‹}_½#v½ÿ¢ÈNþ£¹Ê—D‹ˆaš‹ä ˆ \#”¡‹æ7Þ]¾ÂŽÍUmÜ­ÚŠ©öú¶>Ot:kt1Í)]t£6ŠÃ»~,Š;Ö}TºLåH¥”!¼{ÅÊïï?½ÝSì\ÿ‰8åÌQ""¦E™KÃ4ô\Ÿü,5K»6qcëæ—=jÞ•'’Ë\TÊÜ¿G¿áÿ/îÙ"Ÿ¥–×vX¹†ErIn,`L ú T´|}Éü^v‹báêZ-üöˆðÖDžDææubßo‰œɢØW…%Nyr D§_¬q¥•,¶M¯û·pÆÖék–‰ÓŸFfš ²r·m.¸X4ºè±mò¢ñÿÒåqÿz@4t½ØøÄ­Öºñå7 W݆"cC¢Ø÷õ»°ú⋾á-Ú‰–ÿ~\Dµ;Uøî»?}C¤%þbY¤[ #"ÛvÞ½;ÄŽ·Ÿ;ÆRì?|"ù~«ú“ºlhqÛÃßeÜÈgðS.cö*I^©mjÜîbѪۭâà¶¢mŸ{„îÍ€˜ý¯%hë5ë+º œ+p}qÞí¿Âª¼Pü±p´ˆª×At½à9Û¨«ÈJ݆¸1"ußJ8iâ¼Û–ˆU³‡‰NÆ ][Ë¢Û¸ÍE*cõœ»EÆ¡¢ëE/І-Θ=BøkØ´t<\%2…æ Ï~R4m7FLSìÙ:KlZ<Ί?õhÚ~°0 ¯Ø¾æ]±må±Ó÷¦¬œ€~hÇ"ÑqÀãÂ鎢Œ^GýMÖ }Ѥ']ˆ¿üBkñÆÖÞ½û(ÝGó$7¢nB çàEÅ«1\Ÿºw 0&2BÉ¢\©P\»ÍoiùónŸš Â6±D0Yc.™kÕµûãWÅ_¯=em»ê7­áJqxÁ×"å…ÿˆˆV,QL;©¬fÿ¸[Ôé1@ì…ØÎÞ±Uœó©•ïàìÿ‰ïM´¶}‡÷ *sóÓÄæv‹fÿ7ÌŠ'ës‡Ñ¯WL±ã­ âÐ_‹Ü]Û¬}­î+ŒìL±eÜ‘³3Y´‚˜>^ˆî¿Ø|~¼´'ã~‡3R4lužhÞù:±qñ3"íÀZqê¹O[® Ù»Äѽ¿‹Ü̽bí¼‡ÄŽµïYˆº^ø‚ðCP¯˜y³È<¼Iœ6p|Yoû\ñ>ö§‰MKÆ["˜ü’IHSYGS„iøDÆþubDóÚ‰f¯‚¾Ì*£ã™‰æ†ˆíkß—&ˆÔ=¿[ñ­ºÞ,·¹é[—¿dY¹#ëØFÏ@õÖFݦ½DË®·X"ïæo,a^0ÅñA ªã§ª=)’ûõºHHãç€H" Ÿk¿ŒErícî `L ª „šE¹Rûkd¥‹Óò_ïm~ÓËm!û¯MV=ÙÉŠÜÝÛ¬í:½Ï¾CûÄÑe?Z¿ÓV-uϸPì›ù¾õÛ÷‡õ#¯†Os¶õ;Ó›k­sv%¬Æd!¦@¢;c}¢hq-4MDwêa¹NlŸH_ý›•†þ8 œ£Oé&’_|DèiGÄá…ßZVî°ÆqÂw`w ]ðFý³‹ºýΛŸüwp4oA`ËoÅ¡‹-Awêõ¢~‹3Å”ù""9º~GqtÏr+—+¼®¨×´·X9{¨ðå»7Îý†|*"b[ŠœŒ¼q QL–c;ø}iŸ{4PÅo[ý–eŽŒm!û§hØf Øõçç"®ÓµxÙï;‘œøšÝZ7„ûÆ~X½Ihç-[-qlA¶3<¦Ñi–ò–ß'ÙѼ.†@Jï7˜†I3YÐvÈ”|XŽËÛ&&å ºÉ˜`L€ ”L V åà—úüéG…Ä‹pô(½¨Óý áª×Pt~>h–(L0k‡ ¼@g‹d;®ðš,Øõ΄ âXéøx™æÀ¶Ã²fSÚ¬B~̱]ñA0¤ms¯'P‰û¸‹%”£;÷­GŒÛßxZdmYÈÃEH…%™‚/爵v:™.ØŠoÐb€u\t¿èoªÃzÓ°s@(ÚÃd ÁÕÖÞgE½fýDnÖ>Ý B:‡€[¸"àÊs ÏÇ.BjNÑ î ô¾¢yÇ<×ÚY?oB;½^ú¿K úf§\)ú^õeù>¼s‰½›×ARúö¸.¯ *ïd—b§Ó¥ j³,éÏ d¼É˜`Là¸jµP.Ë4jFN¦ðÞ'Öß{u‘Д×[d¼Ùþ4ÑtÈí– EÆÚå¢ñàE‹;GᩯzFš•,¼Yk‘•‘'Þ(ÂÈ·N“ÛY·K mNíÙšíâȸ\r8.SÏ9nJ û²¬t¿Ãí¢° µo¬ =ï Bq¶éùoÛ¸«XôÁY–_òé×}a%5á{l"od¶²*SG¼Ol_ÿؼtB}Åý áM/óu:+³vœ#X($¥<-åÛ¯'š¦ ?ìü ÿwºµX¶|—Ãk&À˜`¥%P´yµ´¹khº\¼4'p59­·pD×±¬ºi+‰°Æ-ð’ÞÍ‚æ*vÖm È‚[Ú@>ÐüðS&׉ç_•—Ž¡™[ÖY¾Ò$¤ÝÍ[‹XX¯#Út™›Ö –î¦×ÞeåÑÜá"¶¬›…‚»iKqʘ7,wެ­ˆ¨ŽÝ­…\78”@ö‘­‚Ü#ÈêKVÚÔ}I–Õ¹}ßû-w zù®Qëe*8<¦¹ðfÀa¥Ã?ú\̯Ü/ÿÐŽÅ¢é)—‰†·1 :‹Æí.±öíO™ kò5‚,Úda¦}‘uÚòÑFXT#ø7_#ÈbíŽn"Úõ½W8\pí¨ºYV 4 †üØyæ™)³¾žûÒ`‘ü›ÛqN‹å,’kÈ0r3™`!Gà¤Ê,¼æÎ€Å÷ÑãùÂ0È¿xf¢h~Ã=¢ç‡KD÷i?ˆØ¾ç–zÀÒVþŠ™.’Äi“fˆSŸûPZðU /Õ÷×ëc!n»‰.¯|%ÚãÅ>w“ÖÎ)/>*"Z¶]ߘ%z~´T´¼VhˆëàP¾Ò$Üëž~¾è4þ½ÀÛõo1œž·K&°Û|‘qx£xÇrÑã’W- ðªÙÃEtƒŽ˜áb©¸äžÍ˜»øiRpJ*uÇÚ¬™4.¹{£8åŒGĶUðWÎþò^Ü'úùXœuó<ѳ\PظäË‚ÝoÈ'bн)‚¬Ðá1Íòså­håÖÝïçݺDœûrѨ͘+z‚ ú8äØ{N¯F^_öOp”ºÖf‚‘û "ù–¿ývÄŽã5`L€ 0²(½(¦äañãÒ®é cÒºÁ¼XbïÿM+ß¡¾Wlý[lV Üb³jnªN 3ÿÅ<;¡³N}adeÂÏØgG•zMs—äË쌭 —‹¡ü]9è£"hŒªßu¸V$‹Ø??zå…Pþ>,‡±¤c!ÿúÚD™ÃÖÞ=Nܘ“¶¼ÇR`ÜÑ€XšþóG_iYçð©gG½[æ6•%Y“ý^ €C«èëwÊû<ú(M WTp¸"-˱Nu©¹„Ë«öQÄ¥À&|ë°tþ¼—I î<þ¯%YwŠ}9Íß›˜p5RWhÜíÚ*ñÜ·‹,ó:¥oßN¦ò϶vvfÜgNjwå5£¤Çó÷ÀÚ;y͘`L  jµòñ8˜Þ¢}XiŠò†’D2•©§§YtU ä"+ãH‹ùüåÈv9ʼndÚ_œÈU¦?ð¡]Î1kˆùâò“ö$‰Hé×ë\°û"¹>us$R‰Ú%­~C$­9I(p7™`L * œ”®U ”ËfL ê ¤ôî9Ô4Õø$ç‰dL*#”6¤ÝJˆdL€ 0&À*‰@¨Y”é¹³2”™åÕ\´]ÞÇù•„§öãÓÂàIbMa1&ÎùK(tÒn‹µÖ =ǯÂh›Ç¿’FÇ¯Ü Â’|BlÖ•Trõ£t&§§M6…yŸÕTKò^¥´+;¬\™T½­áÚ˜`L ¶%¡Lp+˜~ã@¶;š…’ ¤ÖY®(åË(Òï#À½ª)OÇÔïËõ¦æª:<þå¡YLž\‹RêƒvÃ=h_Hnîêß¿AJÆÑÏѸ  ”be¸æ·bÅÎ@o0&À˜¨$!ézá÷z÷äº"¥×鮤nžÜÅGâ™›,”B µÓ§¢¤Oá1*L€8O:¯*\Ø *`{¿]r ïïpµø[$ ñ™;,òlÉ'hP¸Z&À˜ÀI@ …²Ú·sûR˜»äŽ˜¶'ÁT}‰£Ås󯵍,‰öRõ•—¾»MjÚIíݯw*}nNY,âH<é¼B"â\£Â¶¾=¯ö›â7´¼5þ8JÓd=&À·šf¶È›·OŠ |"~H»ÄÕãkêxp»™`L æ%¡,–ŒCûö¶#¦•ÜÕ¼æÐ Á–?â¸3%y>šGó%ÛK0ïÙòàvØm3¨½üä!½õ‰l[¯›øYq>å}0ïíßÖ'§l™W‹ÇÑ༗:¥HqiâÌv‰«f…lùaL€ 0&P«„’P&°dí´ÄÒ¢™_Î0ußá%-š:>ÈÀ¡ìˆñ3|¹iKg»%èù‹-HCåƒ q·ÛHí5ü¹iks¯4uÅã_öÑÇ`ƒñÃp˜Î'”jã^d·¶öíy–ÈÍ^%”:ßN€™-fù_ëkÈ*Î 0&À˜@µ%¡l[ºèb®§¦ÊüsÕÊÉG#ˆE­.¢}ÊH€¸¿•KœI¾œô©AZH0g›96OX°Û`;Zaµ‘Ú»rÉâÓÍÆbuîµ<þåâFüþ\¹r2O("”ƽÈ%÷íñŽÊ…84­GI0%›°'…ù þu‘È8’ 0&Àª@( eê&YébNbÉûë¼ï—íLÞôarÝòÇ6ƒ[–A¥8/â¶uýšïVÿúË:d£ofÛ‹-–CÉ¢w»Ônjÿ.™˜óÅ–åR >’'âEÜèü¡ó(Ÿi¨{ CÛö¬›Ü»Çøá$X’ó¦­”òÔ䥒Ö$À¢Ì7KZ¼Á˜`ÕE ”æQ¦>Ûà邞‹%ç‡ÿ}ôÕ%×ßìO½ñhx]uÖÎEZ³¬;Ëõ±Jù$“»Y’·¬_ýÝÂo¾üZ,±&«2m_²à†’P¦öÆÛÔÖˆŸ¿ùâG%”]{^žn6TÝÝßi Û±‹CQÈ'™Ü-È’¼}óŸŸÎ›ñÉWHªãnuá¯~=éâ(áÖ>I±,Üé¾¾Åòå»q¼Á˜`L š „šP&« &?º¸ga ÇÅ~fïsèyÆYw|{ÊuuZeìPíl’­2¶ ·NFÒ“;Ð<É4ÍnA/îéÞÜÌÄs¾X·ü×õ cs¤GïÄ“~_â*Vº"Çí£‰´]û?Þ·÷`ßsÏÿ¿%Æ]±][UKçÙĹI„IÒý'w y’i 8šÝ‚^܃owÆêß~|oåâ…¿‚LÈŽ»òx´äo¿m˜Âƒ›!G`¥x­½pþG._NÇ)&À˜`'Œ@¨ eÛ²HyKa¸èÿ¾1)qëƒ.»DïÐqàŽÖ­bῨÂýÙ*ÒŸ)ݦ?O®@ŸùÎvE+ú˜”¦Ô}¹™ÛÖ­Y²lÞ쥹¹ÙÙ A 1ÌÈ_Óv°E9Ô„ò1ãŽöÒ1ª‘èß²fuÊ—\v–Þ©ÓÙÂ:ÄÐø‡É,.Ó¤KúNºñ§Ï|Ó éc"yãïÍØ¾uÝÂesgσ÷p Ùqßyf¸äY3?FσHÆ °G4!ïj›´z¦Á˜`L€ œ`¡&” ‡í§L¦b²€Ò”dmÒpñ?}ýùwØþñ”n=Û¶>¥Ó©Ñ1õܱNgäI÷?]Ç·Ö²fdg¦§nß¼ióÖ kè3¾¶ YŽI§å/éXOâJþÀ¡âv¦X¡Øq·÷Cü g}ñÓB!uèÒ£eëŽ:FFÇÖuGFÄ8'çøçJÏÉÜvtû–MnY·zX…ü¸oëÓsˆÏ§ÞÁ}Nýü±Åûzò·Ó} »ZØDx͘`¡@ …2™—l±G/’H¶­…¶˜òClÂ’‚}–ÅkJk§ÃæIˆq °Á6YI“E‘„rjþ6‰(Û?9ÏŒ‡ˆ ¥w´•¾kíÂMA2–mØæqÏ»ñ!7…÷矛ê÷¾l(uÚj¼¤‡s]>ÓîÊ«¤ÇCÇ0&À˜`!C …2Á±EY?mñ,¤(>‹åʵeqÆÚžÅÃ΃¨Zl¡k‹dº¹ ±Dll«"Y”É’L‚9”­ÉhžxÜmůkä¸oíÝû‚TÝûÜ,ZÙ]ƒHÞ×4oî¸j©HZeGóš 0&À˜@ÈU¡L€H4Ù/ó8°“$Ë e˺ˆ5õÃË'ƒHFw­`s!‘Lî¶U‘„2 cʶ¯2q£ýÄ1”ƒ=ÎÔF»ö {ÞÈÙ\B~Ü÷ôé™-ôç¤2îÃWöç&Dò§ZŒÑvájzÚÁ 0&À˜@He¡l‹IÁÛ$øHü‘H¶^ôú(¡¸(cm ă‚ÍÅLäVaßHX&aI¿)ÞöK¶ó"*$ƒÝ'÷c‡Ç;›QHû¶Þ½ÏÈúq”v´Ž7öiRÞƒˆ|ql÷8† 0&À˜@he¡L¤èúJb XØÖEzÉ/ ‹-’m_fȵY$£{V &öBŒ‚E1"qLkZh_¨[’ÑÄ@àq 8fÃs›QÈû¾K.‰Ê:¼ÿS˜ÀŠLçe~³4§{X»åË÷Û1¼fL€ 0&ÊB](ÛìHä‘0 µý·`l‹ä“ÕG9X4Ù‰ֶ@¦ý51ð¸;jöXÚlìq‰q‡yPÖ¡SÑÈ6y§,: e:ÎÞ:¬\ýþ±Ýá&À˜`¡K ¦e"h‹A[,“¥”„±=ëA°%ùd±(Û\ˆ -ÄÆ^ì8JS“{ÁÑ#ìñ¥µ=æ´¶ã)Mµø"7ÌQÆdCÿ ®¾ÈóÂ5׿ãV¬ØÏÛL€ 0&Àjš$”mž¶ Q@!X Û¿­'Ába›‹ý»¶­íþñ¸ç‰a{|m.öïj_'÷îyK¶0^F³*—ò0|‘‚/ò‡8Þ`L€ 0&PÃÔD¡\ñ  …Ä¿«…{µ`.¾’¸Ô#QnXÕS¾}dÙ±ó“ŠüId¤|°ÙâU‹ÏÍ{˜`L€ „>Ú ”CŸ2· Ô"–›…ÐÇ™¿ýÜG*Ì„œðu½íšC»»íŠ•?Øq¼fL€ 0&P“ °P®É£ÇmgÕH@ èLIO½¾ÈOÁœ_WÚfd)ýØ~-ªAã'›Î›Góws`L€ 0&P+°P®ÃÈ`UK`[¿Þ—¦d Ü9¸¦Ýõi•švVÛ¤¤Áñ¼Í˜`L 6`¡\F‘ûÀªˆ@rÿþ…á›dÆåÁUH)6×½¿wC\«#ÓƲH†ÃÛL€ 0&Pk°P®5CÉa•G ¹OŸ:˜²üIeøF ¥èã>VÀ‹zi˜hæ™vB{ "y¾Ïk&À˜`µ‘€ýŽÚØ7î`e$ <-¥wÏ¡J[ðU½‡m‘Œ7öL|8ä-ÌfqJû¤U“dRÍcÎ 0&À˜@­&ÀåZ=¼Ü9&P: ³W$÷é9$eÖL¦{ëœ ³Yü¢9ÄmW¬^ÏÛL€ 0&Àj;ʵ}„¹L ܧ×SHÖ"9š¦{ƒ/ò¨vI«g"yƒ 0&À˜ÀID€]/N¢Áæ®2›€%ûõ¾6¥O/²ÜÃÞ7‹,|ïr¬[·3‹d› ¯™`Làd$Àåš?ê'ìÞœXƒ‚#h»º6ƒ@ÛÁ¿«« 5ªÈ)yùÉÂ.èH¦”ÚB{±yRÒ¡Õ1n,`L€ 0* ÀB¹  Vq‘–0¾ëQO_©ÉA°üµšl&•ˆ©âz)þÓ#k©¡ƒÿïçcTq„)T¦0Õ^Èãí~¯wþ/OHB•fþÂÂ9ˆ? dÌ…|]JßžOâþ¦[Ð.Úd\ÿdL€ 0&@X(לã@æ‰0êË‘)TšÖŠYEz½fŒ7G†éþêw£!ÉžZ=t޽]]kŸÓef¸#T¶Û­iÎȧ‡=ñô>¯ßÿúöÄõo,\83í0°p>i-ÍÇÈâu·Ãýb‹ß?\]ãÆõ0&À˜¨)X(׌‘Òî|ìÉAš&߆@nÞöÐ~uÚî¢ýÁ=2Üï?.¡Bͺ9Èu¹Dr£æâ¸VMR6Iètf¯{›um?âÓ×_š‡†Ò4f¶`•vWy;Ôõׇmûkó ° ? r×J‘õ Ã^b\€ ÿ`L€ 0&P€ å8BîÙlµ~úAøŽ¾P?#M]¼a•hyôP-7äÚ\í ÂÍ‚è²g;-rg½†b^—ÞÍ:u¿ü×Ã?ùá¤ç^Eƒ|Xt,µÞº¼óÌq~ŸvwJòæa0£7.0Ș ùµp-l ädø`L€ 0" °P.KHDZ"ù®'<A$O))íØýØ`L€ ”— åò’«š|–5ùú¡#û MkBSÀq¨8p”˜6®á9—^Õoñ³– DÛO9$_ì#× S÷Ý„ÆÝbêæirÉ8HðKþ 4ñz»«æH‰ÏÍp`L€ 0&À* åJGZ¡-¡s1‰!š'¹B¥qf‹8’Ÿ…j×b "èë}¹Xì)ãBBdî=§W£œ,uÄñͺî€öäA©‰Ã\Ú-[¹ÕJ ù)G1&À˜¨,”+c¥BnX“[Ó÷Nò‰TTšg<•+̇BñŸ2‰eâMVå ™utˆ2äÍYÙêbhù¢ÎG|^Z~£¤ü¸}tùráBžð„ŒWʘ`'#¢.Ì'#‡Pé3 7§Ôd³èì,ÚæPI¢s³¥Ãå¤p¸±P¦c¿ÚïéÓ§aŽ2)¡®ÌÈH½S»EZžÁvm)ýR¨¹JjG*mVó¤$ú7&À˜`L š °P®fà%TGÏÐiqhRF» Ÿ©—«¬»ˆ§ÃQÉöÌÄ9X¦–µèÓ+GûëÛoû™ÒŒZç(½/*³:f²äE#F v}âv¸fð—óhxƒ 0&À˜À #ÀBù„¡/²bPK2¹›#+F@‘(¦cžD2-UbQ¶üsÍAÊ”ƒ“¿y ¦rkXœÇAÖ ©>A³>Ŭ;Ð&L€ 0&À˜@ˆ`¡"fˆ³š37tšU{Z.qµEr€7âþ6í–£»¬Æ¦¸,;Ëì“g5.¢X)ñ¨@ý†¡žãÔÔ¬Ö+Vo(G•œ… 0&À˜¨,”«rª°Å[²pÒ2 +²Í™Öeôéh]Ïíoš ³S¨)ß~="¼~ñr[búõ¾–7G)m>Ïw\fäœ 0&À˜À !ÀBù„`/±Òr‰·KäÁÊ,’wžÙ#Îï“`> bx€×—Óî®àB lY5MÌi»bõêûù`L€ 0&P#°P®Ãšt´l.\ýza‚5%¼ó~*;'4hUÉ.-äòò×™½Z+]v‡¥¸¬Ä=õtÌ,ת€wF0R³Õ8À™7˜`L€ Ô,”Cg+dIޏó&á>ût‘z烥ꑫG¡oI)·¸•‘¢ÎÏ ÒZa8(T®·Tõ–;>¬qÛ?DÄ W‰ôQO }ærE¯iØ0æžÆOkÖÙ-UWÝnÉ}{uƒ¥8¶€(.¢ $µü»~ŲT:ů헯ÞRDRŽbL€ 0&Àj0Ê¡5xùn%[>+Üd—SD}X¤?ô¤0²w—«84¥p:Eæó¯C$Ó·;ª.Èè(ýØHáhÞ¤B•Äxsš¿Ñ¾Í«­Âš4p:ëG;¡LxSö"ÍÄV}Ø›…$Ë¥Ð~5¥øU‹V¿µ]¸:µBáÌL€ 0&À˜@È`¡òCTŽÂ1¶î;“Eö[‰ˆ‡Ù°ð/Y.²ÿû™†!bžyLE8fÂh!üºH½ã!Ýa"rø¿DØ™ý…—켳~9_|gU9òNanÝ!d³F"üÒ D·_ˆð!—ZûêLy^xü"r>ÿFD=4\¸zÂ8kšÂ·ø oRš\ƒ¼¬Ey­&œëÚ$%í Ngm'&ÅL€ 0&À˜@í'ÀB¹±ï·"w櫇îKа>Ý…ï§%BOÙnÅ‘°5væ¹^¸ú÷‚x•ÐwA'b¡x÷YýaUþÖJ+#ÂEÚ]O •“çfaîÙGžºB_¿1@0÷ëÙø„‡&´&…¹m§p¡>Êaœ-TV–Èzá +??Õi@¨{—þnÅøW‹0ÔYX(*¨àFfXØž9©é/å˜æº%99ÉÉ!÷ z‘­ÅdËÙ™`L€ ÔF,”kã¨æ÷‰„°TZºááöÏ‚kø»ºjYsM¾5@IDAT~ÝçžاµlØÖÿØÉÈ  r©ˆºï.« óÈ¡5h Tfž …®ÆÖ¿,‘”E¸zuä«óJÂßÑßUr]aiŸ:´åCå ³ªêár™`L€ 0ÚA€…ríÇ"{a–v& ]‡_².r¿Ÿ/È ¢ÈàómGº‡\&´v­Eê]Z~ɱ/Œ% ¿Ìå |î—‡çaL€ 0šD€}”kÒhUr[ d»lòWÆ‹å ÅŠd*s8›©E—]\|yÛÁù˜`L€ 0&PQìzQQ‚œŸ 0&À˜`L V`¡ZÃJs£a‘´æP5ò[߬fÎUØKeL€ 0&P+°Pa ˆ6CÙ^‡3ð;tšXs[âu¸”®ëä\80çÂDø7`L€ 0&``¡‚‚é7d†G²€«Ä±É P~¯ßÚæÀ˜`L€ 0Ò`¡\:NÕ™ŠÝžl·[æº\ÕYo­­‹8Ïœì¬Cè$Ý€ØK­í3wŒ 0&À˜¨8ÊgX™%Xnÿ®K°!“5¯Ì²OÚ²ˆ#ñܹeÓj@ OWÛB™Ö˜`L€ 0&P$ÊEb9!‘ñ¶ð»¯Ö™ºÿðq­XÈUÂPGÃçM[·â·­(ÎÈ_¼+¡ .‚ 0&À˜¨…X(‡Ö ’µÓr‡ø4¥a¹³^ÃÐja k ñ³8nÛ6M×ó[,oL€ 0&À˜(’ å"±œHÛÂI"Nÿ囯>U~ßáy]z›~¦<#B܈Ÿéõ¦.=kÊðå/$˜‰³Í›˜`L€ 0&P å‚[Ò©kDdtðˆð:N§Ë-”ª”ñDù5"è¤qNNZNfæ‘í›7nܼnÕ_h8¹UŘD±mMNÅ6 eδҰ)ʆÅ'ÌQBu}+alËò–Áù˜`L€ „: [”­*ù .š‡I8wÚøø_B½Ó5 }ÁVe[üÚþËô"šwãê¤5X6bÛ²8cMâš vž¼_µó/ñ @¬h±}»ÉbL~È$Ši!L‚™­É€PÎsS©Kq˜ªŒò¸ &À˜`¡J R•Çó^ø}×&4S¥«Ñ™Ó=óCµÃ5¨]46¶eÙíÈü…,ÌQX"°Å™„2Ýð¿9¥¯”1E95!X¶E2¹SØÖdëEHü&¡LÇ"-$ mK²-²Å¡,†y¦E*ÿÁߤTu›;[tòxî =&À˜`µ’@¥X”éb9,~ÜPš-üßWJýCâJZ+‰U_§lHâ.x› ?É$ Ã°%”k³`¶-› Y“I([Öv¬I¼‘X¦5ñ¢xÛ/Ù΋(e!€óZÿ>òtÁr‹dPàÀ˜`µš@¥Š©acT¦š åÂÕèv¶,WÚ±C–b²“õ˜ÇöÚÉ´ŸÆÓ^°Y« ^{!¡,–éæ‚Ä1­i¡}dyæPNdI¶n‚…º^jò¡éãâ_.gQœ 0&À˜@!P©B™z'–Å‹0(¯ÇüÊ÷³Ïr¥ 4V¶`&Q,m‘Lû)Tú¸æRmË0 `ڶŲ-˜íßöþj|MjŒå“,Äkhs©‰GX$פÑã¶2&À˜@ET‰ ‚Æ%hÔ[˜>ª\0~Àò±©}?å‰'ŽV¤±œ×"@cf‹fÆ´ØqöxÚk+C-ýc eÛªLkÅöbÇ×ÒîWm·hžä¼)àÔ-p¹¸TJ±5ž0v^ÕÖÌ¥3&À˜U&¨è¿ÝþÝ÷ác$ÀÞ×ZŸ»Ý¯¤ÚƒJé%+•Dî.’0ûH•i ’Ù€‹O ãp‘VÕìfh´õ¥M©šÐ<ɸÛ…Cê•8WÜëì“\³Ç–[Ϙ`e'P-êâž±ú¦1Ík ¡Ü•Ò\À˜1ÊôYz4k;}Lhʸ'’B¬‰Ü&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&ÀN2”ú¨®¿Þ‘²cK;iÊfJˆ0©Ìô0ÍÜâ÷߇R;¹-LÀãùÙ¹O_r‡íú|úã§…*‘-·tˆMKÏî )£ž!¤ápˆ1­¢“Oym«7TÛ\™íºwLBkCˆ³§Žÿ¸2Ë­-eÝëy¶£Ï4»½õLü—µ¥OUÝ®/­ké×d+ÍP‘JŠ,· Û¾æáN»«º^.Ÿ 0CÀybªý»V5p ó¯¬ôëLÓøgròæØ­d2ýÍ5½bkïž[¥T³.ÇÛm–­üÓÚYÎ?ÃÆ$ܦ¤ºASòƒiãã?+\ŒRJ›ðê®î¬sÓkžûÓ §áßL 3*Ñm¤Šé2×\ !%”W_Ñ"ξ+!¯MKMï†cYB,"(aèhlJ†7ñŠ&¿á<ø_X]ùQ÷gY»ËñÇ>Ÿ(«T2«šTßNMûS9Š+S–añ ÿ“B®š–0æùâ2ú¥ÖKãEìg¡\$ÃP¤Rc å"øØQ&n¸@Hóv\.õ™ª‘0q.ÑNœ\9Â+:M\·OH1Ç¡iïýñpúŸÀ 0ZB@;‘ýØÚ»Ç5)G7†ñ?¥ÄhKtÑíQ°ÿaÃglHîÓó“gõj^tºRÄj¢3Ra ñxQ©‡=9á\¡ä`ü¼Ì Ët•†ãŠ'0,~œg蘄ŧ¨¼=Jó+¯´š_ÒÆ«:Ŭ¸²Éd]øSp÷à¢ÞDrážáFÔ}…)¦úŽŠí‰W6A7ˆ…Ó•ê7Î'dl©In:Q[WSÉÙCãÇ=WªüH$•øFJ¹¤Eœð¬Ã= }‡Ç{ö„7„P$Ž/®ï×ñÅµË ‹Ä9ò/$jTTBœSM±ÿÝ0~é8qí/]^Zߣ¨tǘ@Í#pB„2\,Â’{÷|¸¾‚n[ZltÑÇ?£›|¹jí¶Þ½•6_át°B}‹‹û©wkSÁw»3£`,ÿ*kR…—!}¹’Þÿ|'\œz—+s-Ì”tUãî™ÆÑU°t=ˆs$¬Ô]Tª2Í7’®h:wÃõ-ê—:_„r÷´qc>˜>>þ¹é ñ )ÇãfóÑ{Ÿ}¶Ad•üO„>:nôÒJ.¶z‹ó‹KqcQ¯z+åÚJC Ó‹ëÅõàWMN/Mú 4çè¦ú½óĵ÷Åñ&`5”@µ»^¨ÁƒÝ)É[¾ƒÈ¹¨ÜÌpq7¤ù]Jï·´[¹æó²–qž#¥˜ w;‘w¤ØsÏÕQ: ½« %†ÙñÒöãÇÃwââ°$ÂäÓi cß¡4Mš‘y8{µtiW Ýœlýc•btýÌ‹ºŸòò 7Ü`=¥ƒµµ=âÆ#Ë´! eÌWÒùê[ãžXAåXõèÏ> „y5~BâQ6Lõ¹R†];-áÑ->ÿ|ÌÑ ÿd<óƒÕ[ºPÆ\W¤ãÁ7F¶ü¸‡Æ'¼­ íð½å\ƒ4¹xÄþYœ³Ó“»õ-/J”Ž,MÊG½‰ozîͤº)Àåäne‚‡T­ðs”ŽÿL7ú7k_ü³×šÊ¼–ÃѤÑ(£5âàQã»SŸ3w¤çÕØ\=m6úÕÍòX•s4M|2m\üS”ߥeEé‹kÏð'Ç_hþÿb 뢞-”V:ä0ašSœNíÆ7=cVSÜ=cŸÅË|WsÈÓžcYŸïöŒïjøÕ—õb]}_x챌žñ=u¿¢Gó½Á .rn”3ê‘—=¥RéßB „åòc¡ÌÁµ¯K„‘@ßAûíq*‡ÇŸŠß΋ztf¹½¿*×IW69Ã4Ô\Ô[ÞzЯ‹s²ý‹W^ßt`ïû–·œ¼|æ'X?cøLïâ0“B“_â.·Î!:¾Ò¦'Œím3þôça… ëp]Ø´WiN1jš'>q„gB¿ß˜îrÿšç±]v{hL Ó|S¸®ôƒ¯âX;}\ü«´ÿóÏ?w,X»é)óÿÂXÆâ¼ùE cŽÕ¡¸ãŠ7Þo&ÜOñÃâÇJuÆÍ ÝØ‹»ãÇ 5”$aÖýÞ¦41å­qñoÒ> ÅAÞÞ¿ÿÞgæ*Œ•œ\”kÚß)k÷V§×½¬”‰ÿÅå 8ŸÂpܽ—Œ¦›Fu‹/_)œ‹ 0P Píåäý{þ‹"åÉ65¥œpŸø0¥wïÀÅÞu¼5.®nÍwq1½y䫯b;/È,ý&ì[ªœŽ}v­=\º„Ø üCŽhG[lOÃ?Á©#Ÿ}Öz ç0 pT•n|…õL)»(¡M„xzzÁº-wPœN7 ±R:¯”‘².èÙÒ4&äíb¯žp òŒN×­¢eãV°|OÁ¾:Q "û“H¦tÉ3QG3éÔ®t¹$ÒË&þlóv´B–ü^—’g‡¼½û÷ø7o‚Hö9Î ¡*ïÁz¨á?ú ÏJŒ–R{*ÌÖ Âp¾išóGzžoAip'áq›æ]Xv„ËîˆÞÑ2s„ç…¦¯>52#Ê}øe+©ˆrEõ‹¬yÌã÷Ò²*©=a c– î‡À/ê¡¥¹6`1X¤Áwµ—Ú#Úašêy1ˆÃ~ô%DòÝž m ’Eç6:¥ë, âP)T‡l=ó¾”'¯ßê2¥Œ7ÀäC‡Óyjcgk,ì2"Yªnuc]V§H^{y“v¦)¾G[Ê-’ýê4#G}¿áú.¥·HÛ™ ®/¡Ÿ¦OÉ‹VÍ„¡<¸é ÿå»´py%Å{õôoqNu“š¼W¸´3 –ךºZL/˜½éy‚ÞKÈÈÕõå•‘÷× '>R­ŸîžmB¬Ö Ön!Ar›Ò7¸œî.Bh¿â<-p –t\Q ¥oJWÚ㸤óÕé¨7}o°ü’Ža·³ÎERs®Àñtö=ž í¨ºy†H¾cm÷¡ (./˜×¡o–x‡à¾"yÿ.n,: MLƒˆc‰i;9þ/5ÝØx`üø&Ép1³On‘¼þQŒA¹Er0S\ëÆÀ}cDpo3&P³8«³¹)}{Ýñ-•Pw˜)ÌOŸ~úi –/O/my°zÊ8Wü‚=úø,ßþô!Èg½Ôñx—¦É‰¸`µz=(r¬Ãc½9Š,¿oÛñZae~/ÿ÷,¤™‹ºÎ°Ó¼éy”øKvz¸LÒ•Ã=&Ô›òÄGQï¿`…9ÍóøJó gò[Yþ̲ú:âçòý…¨9í§zžø‹Ò “`*µˆ¬Õ°Ò%Sä¢éãÇŒ³¶…ØÑŸ×}±GÝÛŠ²¾ÁFßü40–Šx\h'OKýÅáBáÁ…vx¾PÉóR¹QΛ©­”摉Ç¥¥å>`šþ(—,š©(W9¤‘i[d)]Qáx¬JjÏ[÷ß?–¿,Wþàz ‚~€(¾õ=ŸgO¸VHm,ŸN€µqxž€U°ÜÉ9Ô&Óoü v¾•0Æ~Dúç}ñã6y•H[Î`$™MéÀ¢½thWÀ*M‚Ô è»uƒåÐjØØg_é-¢]‘·ÓTõZy†ÒÉm¡5,´õ`£ øJ§,üpdi+»¬aÓ¦¹äÎÃW›Ò<⦅!üuq™s(¯¨ƒ4$>·Á:x!‰\žñ(—~ç‡=¶H¦ß/Ž•…ê*SÆØ J».‰UÚS :°›‹~¢§ûNè›×—÷ð8~üOk7ãñ|þë}úç°…@0™\Èë c·A¼lÅcW²˜[Bcpkº8&ÊO"ûÍåè0µš§‰[™8õ6ˆ:º«Ô€1pÍUÍßî1kϦҌ㫷¡«¯pL§áý ®.÷NyzÌL¸e—_Ú"™" etÅñuàͧF¯ƒP¤ƒðœ‡úQD¤3úã,=óÅ{< gLñÄ/K‡HÆø„;Ïo%Œ ä¡ zº“›eÔƒP^X`‡ëpŽYQ¥>®Jï‚eãœ,áœ/õùZ¨Pä›kq¢ß’¦ùø5Wj6âþ‡ÿ ÷ìÑ' Ä™¹wª'~ý0Ï‹ •?§ ŽéÁÅàÉÐ<%žûùäÖˆOÉÛWp ‚Ò›x*5efáf;xЂ’œ,›æk8Zœ•Ú[¥Ü†iÐM i•z"ã÷›=pþx×føØçz˜B? nj˦{Ƭ¬ò†p•B®W—â)ôþ©ãžXU)r!e&P¹ÿJ¨>M÷Ã…4𘴄¤eÞ‹Öˆm{Nh»pujY2ã1úûºß=Ìó\+ï߅¼ÓÅ|¸çÙÅÀ:ó˜®ôÇq‘ ñõƒË¥òëÆG¸LHç0œYÁ¸Àáš—‘¶gçæŸÑV©)NjسM: ·îÖcjJߨI™zÖ¹»ýã÷A€nµ<À¬MRº!fs¤¦¬Ûù-xÙÔÜ“eç·#»XK§Wèa”î"_âýGPÞ×]ʱÇþ®èŸ_žuI¬JÛžÂõ^Ü£ãòùk7y½‡2ॶ!`ó5YÓàŸù f¸f·±•F#÷¢n~Ÿ† üÐÉ¥p9 ^ˆ¿àsc¿Çs¾~L:ŠÖ,*‹ ¿1 ¿ÈâW-YÂ7™,¯Uœ>ÓxÓ9qü Å"øÔ’>¦>SPoi®KA‚X‡ðóý}†äíÄ1ƒ>nþè¸ÇØ}‰sä6ü\g˜[qïöná<”Vù.Èo¡9ÌÇ(Ž\t e8®Šï¼¢K:Žq§Yªó5PØß?àßÄtü[·žŠÜ|A·NK¬ÙìÜgL8½½Çç”ÏóÍ}†ÿZ<ý W¶ŒP’Ž%xÚ÷ÕñòÒ~šò6:BO)>W¦Þ çÎ"e\WDHåÒLñH}:™Nã‘x*º ×ø“™Ç‰è{°¨Òú1Ø·WaÑ*S^‡òß)KS…öÓ :©| ÿþGTT¾à¸ÝþÍpsPÂ\ŽNoxFo¦}CÇŽ¿qdY„ §Œƒ5èögįœŸâè$oèÔÄAû…µ@æ lKy§BÕ7žöL¼ýø¶\%¢ c]¡ãª4í6DnÁzHã‚ð#^ªW¡-wXÐä×pXžŒ¦¥!~ý(û‡ ¹:¸£t‘òú}]LGžÿgð¾"·ÚÈH—:š“­VÁý[¿XdºJŽL¼:nŽë‰@%m‡²v^ßâ¾–3vuÓUá*ñêïð}i1ÌólçéžÑíá.€Y3Dà7ü—ßÁ¬_À%æŸPç:UÑÿG^{êѽ¸¡ÍQº<e%Úåá¥\z¹Í ¥9®ì|•±.ÕùŠ· QDþß!²~Ô¢Ì#Y1xgá6ŸŽ8ç‹o¸ž€%Ì2„y)n¾/T¸J!Ðÿ<Ú ëñÅøù›] |½/ÆM…×߬Á6;®¸5΃õÓÆ5ôÉ„e0õÿ†‚ÕÓ<£ƒo˜‹ËZ»â1OrпãJï›n |!JÊä†çÍò-Çu` .âZ€Ã_œOõ~È[*¡\¸áô’ºÈô_£9Ãã¦zF(¼ÿDþƱü Îñ'² Ç«›¦pºÂËôcG/-ï¯*$hJ‹d×é§·Èõç’°¬²€‹Ò¥(¼LBÙjŒïB¬¾‹L«ƒ|% ´ûVÁšÕ޾ÆvHüÞøh¬Ë¸Ò2(ÈSøG´+r#¬Å¹xÙ¬#öm:vB?©ôû!ÜÁ!r;àªY¢½.^(ë?|쳪EƒDûM÷8güO{üãýºz o¹kêŒÿe¯>±½”þ^SÇùn±0]“ä§ _lyA÷N‹§bGœvÊ{ŒÍMax»Á!µ¿_îr:>~sÄÐøñOD;#¦e‰ÜÆ>Ýïg‹ˆk4+Pæq6^3fÿбÏÞ„Çäsá{»|ʸÑÄ J^½4øØ©ìÊ`‹>˜í? å.¨ì²©¼ »µ[ éÏàÿ°±Ïq;"·{õŒ»`Þ쯹ÄÙvÓž½8&þ×!ü¼9>~§½/xMVfÜ$½C/›{rüJ×p‰Ü}ð2 à ¤«¬ã<¸Ðb¶Ks¾B(,Æÿ‘±$fì/0›ÍWxyö%Çíd¤ëïq‘òU”óøí¯W-|+vº7†÷ ÜWíÿÅ4±@4}çÿ9Êo~9ÂóF¿àq $¬¥?ð$Ãrù©²îIëUbñf–: k.wŸ Qû~‰™Ž³SË4ÏÁuå@PyÇÉQ}»iŠÇê«­œ5ÑŽ‚§p,'½Z‘­Z„²ßðö©zZª\uHW£/…~àu©/²!HžÅ…è•=ú’Ã自‰t*ìzQR'{>‚—ãž4„1 ÖÛH¡ôlˆÁ›pÑûз áL{— ±ð ‡fᥒFjÇÁhXŒž€ïàtÁ6~üU˜Wb NÜyx,KždZ„¥BBSšMĬÜpLöëG¦Â²‚éëÄCsÒ‹S¥Êð{EêÕ=zÂ~—¯`…/ Tì¾o}¼ö¨:6áyÓ”_¡žlÌî1|:^¶sšj®_¨·ð yšm9öxnðáÉÁlôé&3B̵ë&K&ò^ ôä,=‹,t䂲.9—NÉŸ†ËN{¼õ[ãF/‚à~Bæ3ÌЋÄóñòTd?¾\Ç{YêÄ1Fuü-ÈÊ’ù8iil0=ܯžö /õšéNÜ Ö¤ãÆ©žÑ«›%€ãÇ¿‹s"ÖµëK*3ŽŒNÍðM‡Hœ'vðáJÁ'»‚EîM;ßñŽ+;]e¬Ks¾bYz¦q‹ÌôïÀÿ…tø^·¤ºáJñÞ?xIsh#ì¶4u6à ȻšãÿÎ2[TÓ>zÑÇÞ0&‹?Âÿº‡š†—'ÇÚyK»V-»3týèÛÈscióÕôtôYj|q¯qUö£Ò¶çämuW?Ô¶XA<`8‚ëKî§µ>L7ùE…’¦,œÿßÅ´ž÷à&³n¾¶`ÿ!<õ‚ûα¦ÝÄ4“÷âžóœ;G°NÚ#ÓÇŒÙK©­cJš>/šã| L£‰©C‡À äõ²LñHõØS‚Ã)øŸpçB¼ý’|Im ¼ÁÁšê±„)Mí´%ñ Å)©_x‡æ ár^7=ÿÅ»/¼®8«>¤ôé5'+„e)õI« <¬ìÚèÅ™‹NkuÔ`å)BTÞ?aBÃ×F>œßz)ÛØ¡9å94¬½ohü³WáŸç´æÎ³[ûÉÒ‹bïÑf±QŽÃôR¾2Öä~ Â ¯=7syʤ2Šþ‚Û\žr(OIí±|¿][ØÿÌË[Í$ c•—¬yå-£:󭸢ÉzˆÇ*}JƒoöýþÀ½Uݯ¼c9£NY2*P©Çó^xš8I7§%SÒqUR¾òì;ÞùJÜc4M¯è¬)4Å[=ÿ胶ºŠ›®Óðd Z7RoÄ;c"]Q½ á0_óÜŸ^¸L:gvë»¶Àxó°ÓUoŽ)RÛ˜~ÕŸ‡ÿ€nV-JÓ‡ ía—Óñ«ßðÇ èh:BšçœêÄìKñ¸OÅ\ûÓ1Kм¦âŒ]0.ýŽ÷f`f¡ vp!zíwÃøsn¿Çœþ‰ö|ûx¢1—ȧàß?Ù¥ÔG~—l¦›G‰ÃñÚ`—o¯­6™êMµÁ•ì.×¹ŠfÑyÓG¶ÍŸ…Šæ/–§ÅO?2 7§ÞHgÔÃÄÏ+²ë*¿»ÓålOî›Ö»þñ{ðÿRÇ‹ÌwÑw¨ 0p-C_h™i Çüw&„;_z ÿh'Àóåé c&RzbÃWCŒe*Þ-˜ Âå–©ññ»)é–!Ý8<-»œÜ£ðöþåÓÇ^Nù8T=j±(›RET¥ï—… ó*«>}\2)É_UØèB>½‚…Ó?Q@$S‘¾\S¿‰tլњà*¼±0%^èXÀ!ÿ‘j•øv½îy,ð_p[ʲ]eØõ•TY‹‘βxØé˳.ìþÿí |EöÇ«º{’@9”CYAÁ¿^x"Š‚xຊ®Ç*hAE L %“A0 (ëîz¡(¢€ˆ(.¨¨ ˆèÊéä3çtwý5I“k2If†@^ñúªzUýéô«W¯^ÕDFXË9É'ä):ä5 ‚’ßòþ`Õ¥ë K~ü&¿+¿kp±ªçÕ7šL Ä{‹d${i'0 ³˜e­U.ÅTý>S²ƒƒ¨×» kv!ü”¡l-›ú49b%+†U8°¡%­”î3˜ ™‡CËß߸los(imƒü——›M(³ #‚I5 «ˆöW+Ä#^ŽIN™‹ ¥i%·ðkÉ6°¦vf{ë'¤)²ì©Šg] á…¹T’áª?V$%%ÙþŽÃ° ‹¢ŒÞQ^)-/7‹r(•äP4ÙW¦P¢>C‹B±sïÛp)X†ž}$þ¶uÇK?<cÈBäK‹öa%É uç~ùA© oÔUPÐÂò[G$¤*ë)™ì}ƒ\EÔ2øPÌ}X?ýXw'ÃpXˆP? åj¤°Bßng‰t8æI«0\Ÿ–¹ Ù§pw˜™ê\. ;µ «X%õœ¡2±Ân›½ ¸ vã[¿!MkÊåÂÂs/ÎAGf4,íOCIþìøíÑ^8àyC⪷WÂÚÂQGÈšïvÖZœ¥ø9|)§ãAl!«¯"¿0+5¹¶†ìµ›Ÿ—ùï>ó!¯ãÄУZ‰@iB‹Ëo½±¦\O¦ž¼ ÃíCb¥‘r29¥H¼‚’,­Ú­ìÜ<.‹¨kºÖPm'‡ùÑqøwBJÚéÚæVÑ®³dû@צûÔR.¬b±›Û!¡ë±à* ñ˜ëȇqIãÇ|dzv«Ñ†RE«ìÕœ§ áxt»€5¶â åäqŽÖW®‡pDk ሺn‚ìù0ž‘ËÌ—ºI:9ðX”-¾.äe‰'y*^–—Á_Êó9ÉJ°L­ƒò-!­C9f-”õl"p¢ l~^N§©ßï†ß¨T C’àF¼õËç–ó®ª2‹)›áwÐröÙé¨}ˆPøÌ~Ž:å§\*qJ÷™ý´ÛØv»\­Væ‡ 4Ä£œgWc°¢ß€†}åÛ¸P…vÄ÷]uÈÕ:‘Ïöø(;]§ÃÏú Ìy¸®:m|YÒ~õ „EQþËúõ»·_ráü(/¬~+^õÒÀrR."pJ¿÷ ¡ºX/7nװ—i¨ê$¹DàX†ú…¬ “òý&î«=˜¬ÅÆÖ¬cQŽùÅ%L˜.¸Z}+'vÁ‚¹;!BëSZ ÓÝ!s⸵ÒÝÑcZâo@CXd Ùà*Ãuú¹«@C &uŒ\ÝBöŽ’·ª)UËÇÈëÿa9øå½77ÇýYB"LŸø6R‹ðø{æªDóÛñB=j OˆÐ|Km6…Rœœ ±*átDgØ n{  ¿ y3²RÇÉ-“aá$0 QdøÐX6}|§3ME=­ªz=Øán6÷ƒ[Ç‹þòG´lü4\¡þù»Œ5¹øôÉŠ®¯m*«3ž2„#BûíÀ¢-2„£7Ž;”§ðÝtD¤‹·lù2„£t›¬(„#F³±P– áxßë”YZÝŽ2*W´»P6aâä–R Óž´çÆcrü± ?¸3‚]#–fMm¿þ»ñÁ–Kòˆ@]&°î¶Óï–x%ØmÄ‹­(B°ÿë¶äÁ–Mòˆ@]&ÐiÊ÷«âìÚ ·‘ó%[Fu½5¹ ,PWoÛvš»ÈÁý-RUÈÁ@ê*›GúAï‹ø¢±¿Ð á«(}sé§g%%íñ(ÚeŠ6TÅ“B8–ùêÑaØeÉt{÷‹þŽõ8_ &_Äü¹Óº´^¿^†À©uòÄåeµa¿òdì]!wìÂÁÎ'åÊ éþb®Ÿ93²çgµ‰ùl·Ÿ¶u“\™øúÛÎøÛ^Ál!&ê¤voOµ:žò;aB/³¢—Z0ÛF²ˆ@( tzîûnÌ;†²¬zð’ÍS"´ ÿ÷øù[ƒ%“ä">aU”åmm¿ø,3?0¼”ó1eôÚ_oøº:Ȉ|€i‰y¿fŽž¼Á.‹a¹¼±â’J÷üÌ–Vä0/W0‚Âÿ†Wöáj$ ÞƒàéÛm9ö6˜ùäªJr…£B£èÌ€} 3mcë` Ÿ>75ùu»N¹•³báð¿ Óq‡žK„ú6žö«Eà»;Û´-*tËß°FjþÛýÒó{s}•Ç71ÐÆ`θ‚Ì“«ÄZæDå“¡¦°cÓŠžÙÕ&ª·îè8uÓPLž›¬aއÕåå`É#9D€„—@Ø|”íÛjÛ4/Tß“}®†ÛB&”ÕU’å’”X%ðï°Dï[/&\ƒYÉ=ÛhWwÊFÝ¡»ùïûúæ‘ûrX̲‚¨GÞ‚•z`™ND÷癡Ì7Dw]¿§]2Ö%êù+']‰6^­6`wraMö ù4ÀÌg³¡¼¯ò9E»§( ÞÞõ»æpôE§±Ö và÷¿^sD÷¯®’œ2étÚö# Ç'f(É÷€—œYO‰”#°eT—9ð¥M-w¡'ðwx,)É5GEˆ@"–¨¾÷ËW­2ÄÝwÿmÇŽ­ÏbÈøqßkïóÎÕ{:¬_¿&ð2Å9L{uVj∹ʷ,,´w@Í^rYì_°§ O©Æ+¿ßvøËrñ £Éå*ã“]ó¥•ÊwÉÜ`æ3Ýì~øa{|QaûÐnwñò–.SÝ}¤)Îí•çãRÒ!Æî·˜8Qå$ [mOn-ÚµaÃ_Ûôp»…PV»Ôän 8¾ÓPiòÐy‹6­vyá(e·Ø£ÃR&už:Ö3ÉPv*?Ú¸9#HW¡3Ù¿I7:|?â7Ü_Žê`ÉÛGð,µƒ’‰Yý9ˆŸ:2š S\ÊÖ†2¦ÆµÖÎYšcl†{Ã3Ê …ΜèôLÞ­LNÖ„¤oñŒË‰C}PïQ<Û¯ËðJ ã]· “¥a®DFi†h–ð‚î\‹Žò=˜Hå‚bƒ6.èÝ­ÓSä¶Tí_Â)S`óè.ã..¿Ýçð»+Žï[»Ãï(se()ÉÕ€FY‰@%v‹²äÀß|Óì°~ÃXþXv7ÌF®¾Ç”¬(-¢[M”dY|‰C)À{¡ÏK%ÞZå7ûz¿â%~¦}lo-nÉ­Þ|žó\ü^ÀòÛÚyä6Xù¤ï§TâyŒæYkׯ§Ÿ'•{ÂÇp}r[ÜÙ@vfót(ª¶§> ßÙµ¥yó—âù˜Ëp•«ùù]QÙƒÝßÿãÎóW_I–£ã·sµùbãˆßúJ´ÓÍ£\ƒ'òAù|dMt¾‡Kÿ‚"îB<Ù ¤’?iRc(D“Tÿ+sDuƒëw·7_gßmë'Í£ºf·íbü-]Q-˜¸'}’II®5ÊLê,¢(Û4:|ýíGí×mèÆ¥^l¯BiþþfoñrÅ{˜}ƒ—¿Î™Ú¡Ã7ßnûÕWìëAÛ «(«`¸Q¹xa6)+_±D,,¶Þ|ò:Bßå2“7öͬ|»¬Ïoƒu%à½UÈÉz̰²T¬0jŸ,0вWGVäWmç¡í©KàìþRÐ}Éž ¢gÁw}$ž§5x®ŠÊÞ1~O‡ñò_Œ<÷ñ6g¶¿dñÞÿ”Íðñηp&¾“_•ím< ¤[R ª²N*­žÎpÉm¸ÇÜÞY÷ÉɨžðLœm@ìÔ–vèF¶Ö®›=vìayÇý0Á03-+†ï€R»}åw[zT%'kbòJ9Ù¶ÃQÙ_¹Ý=í:|·"ÏèƒãÅV”º“En„qzjvß<´_? ü0ªóˆVq#ãÊeÕ› #ÉöŠHàý´¿óM½HF· ‰{Q¢sDàä$Ãé‰Mò%ŠÈ@ìž`ì;.¿ütn¶‚E)‚ íkÔè—³W­’/º&Ø—ÿ„¸‘]‰Êܱ&cícïVá ‹yóŸ„*Jç V>KÜB¹`…{σQ|IfZÊDz ñ)iÂJþSöıëðáƒiX3p¡í—0öÞíœÚ:¿ùûŸ¸Ãéò³.þ‡²çvŠ0ššL11uo·…¿ï:þ›(×7­!¬‡¡<\‰°?¹žù­VlÚz#„,ƒ;Æ Ä 7>íGtw;¢Kù³\a Vè˜ß ‹+ÄÁµb·eYwDz¬¾%U ¾Úv’g Ø_9ç²£pš(>f–Â[ó¶§-¬LÎP}vCÃ}0 y×#‡ ó¡Àw‚…y¹,_6q‹÷FÛú²£\D<õÉñ¦ïÊæ£ãúK~Ër¬gÒø%YÛçæ¥˜V´4’ÄF:~«ÉŠ{õ—&Ý98¹œpE¹,®ö_~)ßܵ{{—À1¬o¿Â­ƒÕ0Œöxeî´½[¡ý™׊“'Þ£ájÇ#•]ö9Ï6ùâõ¬haì½®•ÖêQ_Ù )®)PÖ ²ÓœÏxÏ[âN¨Å=q-¾ø„3(Þ™ÖÇC¼ùh§^èž½ÞÞVê¦ñcFžžÞ¢ ϼÒzDÈ‘¤"ÎoCD˜‡±»¬µ’ôé.+íüŽoŠø¤µÒÆc¥•#˜Tû¬%Ø]Œ[Ë"4Gïç“÷ÉòžÄ…l³7IeDåÊðLWÒgÞ“%;•É’<]D1×åì%³Ây‘Ô¸+JȆ"ñn–+削®Ó9"àK`}B‡Ã8.5Ü÷:í"pj8¡®u ¥ÂÔ· (ß'ý‹ÛÅ!ôÜ"¹/XÅúÊý¬Ô±ÒÒÔ$>eRWy¼ÇJëù&éì|ܽ¯?L[êú@¯EÇÂJܶµ#9AÖo'(Íý0Iª‘ýÁÐz6Âà ‚&)É6$Ú•@a¾u?\ÞÂonû,WÊÏò‰ˆ0ìz;ü–Þn‘šÖdì ]:.òýãYŠ¥Êgm[Ìž¥'æøk&Ö.A¤š8Ùq”ù¤µØûœV&‡‹ 0T¾NæOKkËòMr_&‹)?cëìâ#t‡…X‚ã¿Kvµ“çdçWú-Û×iKˆ õ—@³(Ÿ¨¯"3uÜ·PBæ›7"&¬ôëü1²E싲=î\ëFXÒ±ëy‘2®>Ä,còŤ¤(®©B‘/ïûñNÊ–IFÖ8æÎÍ€ò¼/ÇpíBýŘ’‰ýO—І„‡€+Š6з²©£GçÂzû~¾™{¯j±%°âžk7¬ØxPÃóµþÆã‚qÕ.÷g?q·õ1Û¹//ÞéB¿Ž½’•êœà+ËÞWªNã˜9Çßð›ÏÁºÍr´£Wb¢kNer0éoŽiYs ;IÇ Á³m‹2ž×w˜á^„ö\‚:ôL—sF^f¹™øòwä¸ÓÎâ†HÄ5Ïóo·ƒ¶D€"@ê=9‹ß¶\Ù0¤…I._iÛÛ²V§`ç³ë¡-8ÙHßx([s¦z-· )éWBqýç§ÂeÂëæ 'ÿAiÝî±üú¹Q]Ÿ¯Om.eËlȮό­Lä°ôôf¾×¤ÜÁú”–eã’ûæ¡}"@ˆ¨_À"õ.›®¢í1Ö ´:d{Ò~×uá‹›?ÿ/Q»Ý;û0!z0&:¢MM±„´ÉßËöƒ"”U‰qî/8ç¢.´—Ú@ˆ D .ÐNd£„ЕeSR`ÂeYî®v[J¿¹3…`ÖáC…K¦( Ë‘Ö7±p³7Ðm‚ó™s™(zBpÖŠCc(à›T…-Ìœè|'PµÉ‡ÀpÆÌuAŠr% Ŭ‹4±l^`­FR”+áäïô3ócÎ0ŠòsŠ~„|±¥òz,ü‡£‰ÙÊoéÙÊŒ†mÛÏÑo[a©¼t@ˆ D€À¶t‚Ò’g#;.›òôZ!¬—ñêö*É•5ºr$³Äƒ‚}¿ôåi±ànµ²¼eÏÇwýM°Â ‚‹¶L°—¸ÂS`µÞaYâ?ñÉ®¬²ùëóq¼Óµ¢>ßÿÉ|ïéÙjœ»(o³`â ÜGi%¹‚Ãsw–âÙc;·ÿ˜>Oƒå™ D€"àKà„(ÊLÑú0³h”ßK}È>Ê8ðr¿ì—·–~2»Eêʌš2%ƳÙrÎÎv¥ôÏNs¾œ5ÑùöÇ«\»†q1hðø´›ª’S®qNî%ëâúp¯§Ò=.@§–ált:³q_U*Èeï s{f™Ÿ¦ÏU){މ D€Ôgaw½XúŒv­%Ì÷aÙ¬ x(Ì7æçîoéÌsnî7¢òaã£G „#vD“XÇø²õIá¸d×LKŒÅµå°./U¸x'Ó•2×Î+ýfwkÖªLÕ3]Iï™<¹ÑÁ£îépßè åÛÏåŽhõ‰ÙIId™8§kSøB.D' q‡"~Jy9å3Þ™Ú1ž†"Wá^" g…àÚ̹©ã¾NHq »Jý­v;äet¸Œ´ër>ê{þÉiÓ;·;”þ̰¦ƒíåðGýòõévîs 0íü%²e»Î¹ï9WŸÊJMú"a|ZoÓt¿Ì9k&[e~®òxfY/hšrï=yƒ<7$%½§aY/)*š51Ùc}¬§u1ÝbaÓXG÷gÕÓ.4Üb*²_ +~.$-ÑbF=§?yHÊHp¦ßi1Ñ þ±¯Àíf*”óî!ý&¯Û "žàLËıÖ炎ñ¾÷aç¡-c[-̆/²tµ¨qÂoPÅw1/#[5ÇÅ›ÿ¬± *Hˆ Dà"V‹òÊi ÚÀãø-¼”k¥$Ûü!§—(Ü1Í>®h ]« ,f_I®¢ëŠÂ?Át&ë‡à|µ)øPß|9Öç7AÑ>×Òš},ÏCI^%¹×”Û~#äŸîγ^?^F´‚Sµeµ/”îG”(~ÛñkÇ÷4-R*_s®ÝÆ£ùEPºó¸efȪã}´ùæ„”IØ%¤ÂŽý!P.=í°ÏË­jšøEGa˜oc»ˆEó΂)S h>ýÑ÷[Úy=J²`Iœ+"´ˆNµÂ²¬ÃõÉm#š7Zƒ|O¢‡c1—ÊOkåªÕ¸¿Ã¦Á¼wä¿<Àmå[®…ëPôKƃõŒ¿@Iþœqþ“Æ=а8¸¹œ“gû@*¾² òÆâ\?!ÌÙhÿUMû¿–ÚeåÜ–éU’¹èŠNΤ$ÛdJoÓ²•áµU’}%J«ôäÝ}ÏÑ> D€úJ ¬åB£`ÈæA…m‰¡°R/è7Æø´"¹˜x64³ß*º&ÏAuÛ ‹f³áúÌØBÇÑŠ<–:DOëö‚ž¼ÑSÆAµ{=[Oȃ¥õ*(x½Më©ûE^OHv¹,!>•bXŽ·{ÊpÑ1²eã3ž1¢Ò Rsô1{÷YO~ü·‡i†pÿ0$#£éóㇼ÷ HÆáÒc2Ïã‹›¡Þ¸¹§®/ø<ÇØ yêÄ ¥¥ÚWÚ,WÊÏèdlƒµ²Î{e€ÿëå¾ùì}S¸¥’=Dq¨çdR˜8K¹m¡U¤ãdƒrjy¿‡.k·®~bþYKQTœ"@ˆÀIMFÈ0%Óªšðb¿bÉ䊇‹¡nƒrw^eu “uâ‚ï°‡ö¹Ê^„oñýÒÕÁÊ3¥{ÁÖLÝù•§<ç‘P–ó¹¢Ì²?PŸSTåFK‰\oסp¶ËÞ¯h å2‚íÜ· n(#Ïö3L*œ¤jl‚oÞ¬Tç è°à{|×út(Ñìv•+/úæ)»¯šÜ9Ž'¸’x\ä™BfȸÔÐé•wì¶—lˆàÚty­¢tÿ„n\X¸ÿèU¦eÝïHVð•~×ü¯»ÌmW‚Aï®çxaVH«¨œ,È@YߎÙº~]ÅKÎ΃"½Út›~ÝjÊÕQNL›{¾ÍûBuËèÔŒ•ìpÊ–ìj78Ùu8ë<™ê¦§w”QN¦6S[‰ á$૸„¬ÞtŽøõ—û‡¬æÜ¼›ueëÐXÄ/J–“Õ0Ì¿Ò÷º´êŠûà“̽!âZ)=–äXkÄëóÞp©xnóì2pøQºŽh ÛgOn³¯Ug»Ë½> âª‡Úi¶ž´E–KI»Wª±v‚Ž+àv‰ócPL7×f±éêçL=±-õc†]QF™eR)†•}%&'öBënGùž2 ý¦saÆù¼ .¾BǤÔwý˜>¹u¡»¨³¥²ÿùÖW龦 vˆƒùyâ[X¢GÍMsN­4o=½P rûãçâéü„¾Ó‹2^Šj?nPÁòñÛx©2:œùØì„oþ{˜û±¿rÁ¸†P†¯£3ùm–+yreòÜ\¹Q£åïç•ÊòÔçó¦)®Â³= ÖgtïD€Ê„Å¢üëÎ-ÃW6¦²Få¼þ/Ÿæ¸¥r6“Ðħ¤Ý'Ý)¤‚ Ù‹ÄνPÙѨ–¼–Kiå„’ð²eŠÇQî’ˆhþ[jÍù1^Ìë܆xv°3µ—ôÙ• ™ NIó( v¾ª¶ Ñ?ÁbZ€IrrÅ4(É—Âç·œÏÍ_‚b ‹-ÄXºk™ ÈL–¿°JàÀ¡úì†ñúÔæqãÓúJ«›GtCÇ—PrÈ{+U•Âá§Ìî…&}}·N«åµ6ʹ+‘÷ 쀥{™7¿¦J^mâœi㤻G¼ž~^‘ážÛöVÞ¦Åbo¾*vf$'ÿ!åïPÜ]2ÒFÙëÝetÚ*ü½„ᮺ…‡ïèL…+o îuø]wÁHÃRtʤ;THžÝwÑ¡\ÒJB,Ô°ÝåŠÁϸÒ:0Én ”Ä‘˜Í¦›¹Æ~¶sož%ŒÁ¿gŽ——õ'ŽÐÔ—0ÁON<[l‡}“ê:·QâvXòŽBøp—á‚ïpÑf¬8¤\ƒüœ˜®ü“3e Ì—¢ÔÉŠb^_ãl=é'\¾Vª;sÜÌ0¿GÙ3Ùâæì„·,hš›šô)ÜGž†Ëǧ¥h¹ú‘OTú{ÖýÃÿ>À:ø®¬Ôä!œá¤l—ó 嵐5fXzz³`µ¥"9YiÎ×2S“>«èÚIsÎÍnÆóÖô¤i/5”"PÏÀú„•ôFÁ%-´©ßÙwi|À›¦¿ZdÌa£  áóIIûüå«êš´J³Ý[ÅÆ¨¦Ž]Ê7¸ª²öu0á#22šûk Ü/21©¯é\WÊ=v¹`l¥;„ˆ0 };¶\éCãØÚ,;9y·}®&[é[ÍcE¡œŒX“òT¦riY\ºìœ[yŽÚ_µöŤxëQ’âS\è8^Yv,= “E; µMuðË¥e±Åe¤™÷‘ɰB÷…u¸ :^ß*¥;× Õ3Îq»ÍeQÇuÏ뉿۲åè:NsdWûf¢s¹1;Õ9S^G“L'@IÇ ž2!û/>‹1B”ŽÎr+[Fe±ÄåõÊb|—ŒLÙ"X ±ËýÅ]»4̇x #5…øS°£G…Â'£;þxzGQ¤ÅÊô-ü¬ÝíÎ&þ.ŒÆèÐyèœ<"…Q 8„¶”“`;àžF‡÷…¹©Î9vƒ+û0ö0& Ÿn9.W«@ <¢ã¤9å(%"@ˆ@½& …åîa K=TR¢´ÕZq+ya•²P}©,Ò'*UØeLb„[» ‰\_ª`fé‰9•‰‘Öb\«•’,eû†™«¬.:_w @Ù­és{£¼+‹Eí(¾;[œéPˆÉØâ,Š{bfGÞƒ’ÏeéÙÎÝÖË«1Áì‚Ùú¸-P¸ÊÐ…‘Q,ÞÆÂze6yÂ5:S[Áj—}í£[ØH(ê]UÝiîÑÉœ„s–Ç'–øH‡¦~î6Ý %±ÄÏ“ 9ÉXŒ† Æ·uÜI²U}°%»Ì«¨Ûr|c—C1Á¢•A˜æ{)F­^EìòÃÈç™ÛPw½@Æ]Ç[ËífÏ”Ä]¿AS›fâÏ¿À»»qËGšLµ Y^aº¯¢g´A·Cºwír§ B†²k¿ü;P2zcý .YoÉö@á~Ì,ý˜¥i M÷P¢“¡LÇd»’K ²¼ä¨M~¾µJû«PÀII¶ÁЖzM ,Š2VxÛ/'!Äé`UÖä×4ñpy¸¯ö«õáP,þ Û7AN‚N Pœö㉠©EŠí@`AŸn$c3“76˜Õ¿]'Ú÷\¶>j¿·|™ØâÅ+<š×håoÌrƇ+R"·5åâó"g–Œ–ãQ”årô‡Þ…vUfR`^WfÈÕ-KêŸ%Fn(9öKÜ®ùËÅø¶Ë—Ý*~b—Ww}Ž>l;òÈNi¡O§R†¨ü ¡4{ãüŽ]f†´,Fçà]Ëdwb¹©Éuçv‡ÅþQß“ÂæÎMM¶'éΊ–1è$8aeŸfO´…ŒJã»K7™ü\ë#°} “g'J¹”ˆ DN§á€€hÒo5¤ C°!¯#¤7à#_J†ŽOƒ’üx+íêQ>—h—”à!ÿ½cøýàF¾‹MC¼ —ˆç`í­¨l"QŒ,]¶tlqS˜]ðÌî3! >ìÇ~ûBÉö¸Dk _Ag ÝÝu…ÌqJ2®íƒ«ÅÊã%Š÷†§§·€ÂÞ>í«J_Ã<„’$'—"Ove,ñwåŠô"X‘!V%î'Æ·-ËÞV»‡GkÇ]-8Ÿ eÄyß2¬#|»‡ ßL;J†-ÛßvîDçB¸{Ínk¡Œ³î//]#D€ÔaS”oé–K¿ °°&Ë %"P¯¨"òiÜp^°oZ†\kßøÎW‚-×Wžœ`鈽Êœ[XÆg…î#{ äŽ„oí½Þ%ãQÀ£sþ,çý{üE_e÷›Ä:’ 0~‡#?D¬ôCXžÞ¥*ìIß|UÅ÷Í[Ûý@⮫ ÕÅpyØÁ޹ÃDFo'˜áZ!:*jqd Ù–3´VÁúÛTÖʘçvûà³= ¾ÙÙXYs:ûmßaÜÿ\[Ù²qŠ'Э8³åh|'‡ ãà¼@ËP>"@ˆÀ©Lï•ð¥¦G·²Šò¿ƒÏ`‹`ÕŠéóýÆ”_Õ®:ò¥5«~̶ÜTV6Ð|••/{¾:òdÜÖ+Û¶-òÎ`/+ ÇñzV´ ›UÁ%:uŠÈÈV†b©õÙA»=ΊB»rL‚;l‘VŠc’m\*JF-nH×çGf£åÂ>þÄø‹%î¯\M®Uw]®ÚHQŒgÖD¾]F†xkêNÚWÕß2;?m‰ DÀ?°*ʲ)˦jW Ó\e9ÊÓª¾ Ë×Êv9¿_ç?”šÈR¶dÉâob襰¾$Û×åLùÂ<¾Íž¶45Ë…ÆÊ gšOÊ­¬.»N¹ Dž\€=*SW#BÀ4X¤/j,MŸc!€a¾òä¾\8Qæ·q\íËp—½Nǧ.ôl%þ°qµ½C@S#^Ç|ö<,V0±M¯Äâ[œU¸zùÊ®OnkZâ^ŒSÿê{žöëqqãÃß÷…ZÞ­Á„2”äZR¤âD€"pJ»¢,éÝœh|¢h—@YöÎÒ®Õ"Ì„ßwÌ„[oýG©PPÉPUÇÿ-ªfÂl.{±ŠïhЀýKž÷øü ¶L˜Eå34Ÿ¿º|ë®JÞ`gZOØö¶ÉYúré\{ºô׳¯õ©³¯¼B£h¶¢h‰0Fúž§ýúA€sÝJJ°†b…;¹‚›×wµw¿US´žI æËÕ(CY‰ D€œòNˆ¢,©Þ<²p[ßÑVDÞ¿/w1E‹¿Îòa5›¯:":÷m¥Jå o'S½W~°ª9ž¤Õ'N›‘œü‡÷,ç¿YLœé=ÆN ùd™Êꪮ<ŒK&å&Sy,ÍBܤ±ˆm™q)iƒp'³RÇ~‡[Ríó´­’âÌùŽ1Ñ¡šŠk‡ª"€ßØ܃ǻþ ?äÛù™-®³kÀŒ½,ì?5søð"|8âвœV[PœR}'0 x‚«\½M~|’ý<4 ãSŽv‰ D€Ô?'Ìõ¢. †å:GZeí¶Àí¢€xc™ÚçÍgç¯jë_žy?(J¹]$$»®1M¦G7Pþf/ 0,=½âö„¬å )®#òƒz›#~ìŸX©ëƪÚ@׉ D€"@ü¨3eÿÍ ÍU˜^ß28éOÇëS›3wA?Å9^ÖŸìº;¦Yôûr•,ù†è®+˜á8ø‚+±ÜdAßVûæ«Lޘɓ:âîÑJéø ]vpJÆE–0fqÍqëŒä±^êÙII§‘Ona!ßÛZ»º5…‡ó¥BûD€"@ˆ¨zmQŽÐO‚æˆY¼Jò—ð‡Öe¤ ¹¸¨_Ë=XÐMb­,Ÿ¼fbœÉŠªŒa뛯2yP’ï„È%º>ÀÚ²Ì)8w63Ü_ÂR¼[~<í••S"D€"@ˆ ¡$ -¹º.Ju<® e*­(_ñ¹¥|ËóV”¯"y•¥sD€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ D€"@ˆ€Àÿ¦9\÷%·û0IEND®B`‚neutron-12.1.1/doc/source/admin/figures/lbaasv2-diagram.png0000664000175000017500000005344113553660046023572 0ustar zuulzuul00000000000000‰PNG  IHDRƒ„Mƒ*«jzTXtmxGraphModelÝYÝoâ0 ÿkxú ìñ¶ÛÇÃ&!1éî3šµ¹+ aÀýõsZ›~À6 ][ !Ô8ibÿü³kÓ{=ßÜ)¶ˆeÀãc›ûsà8£±¿F°Í¶E’P‰e¹`*þsZˆÒ•ø²´PKk±( g2IøL—dL)¹./{‘qùÔ éÄ\0±x_úK:ʤcg˜Ëï¹#:Ù^f3Ïlö/Tr•àyÇ}I?ÙôœÑ^©¡î €¨¤„mÌÕ|sÍc$a”¡qûÎìNIÅTäã`ÊܰÔ[²“`6¥Ò‘ eÂâ›\z•ÚÂÍŒ"=áÒ†K¾ú·_ø8úC3‰VÛ”š9³Á_®õýÍVZ‚(?÷AÊî‘ijÔ+Ùº”+5C2J3r4çž‘ôF„ãŽË9e` 2Õõüì$ª‹Z înÉq… „ö0Ìnû0Ê@_Xöˆ®˜ÀU+×õ€ç"âMz{eñ U},É‹Y2,ªZGBóé‚¥¦­!©•½€ûq¥9ê} ø>¡Ú¨l¨3Äñ:O06’ ­;dù7‰| ó"ïìúþ¨„þŽˆçàŒš‰'–š'†qÃôº Ä+\†æröÃâ1œ ß[ø½zšÐBÖv@Xg\ÉDà6;úV„ŬX",æ¨ãýáÛ_@XÔ¬a=žpÆN{CYÏï²X¾CÙD&ýæ(eÐIÎÓ³ªßI©+8Ú°,ÍÔç|îXh¶ŒÒS³=‚¦ýȵÉ­0–¥ÇŸèªÜk$ïJYå[MøU+d‹ te=x6ymºýqÕÿH?P·Ûµ ÷ê㨙Hÿ¸w:Zº>éi8-Ò? îR&8ÑÔºÔðô~ÍGú~ÕQ¤WéíF:ž]€á‘ÏŸ¹Zö çùC·‚Ä~g“!Ñ—˜%Š€V€ÀΤ”p­ D‘VØïºC¢š&Ú ,ýûÞ°Ãà }ûA‰Q‡Á±{Þ]xz!sDmBÇyÝ ]ùϳ‘‘T+ìž³\êX2Zvw65©]Ô)Ÿ¼rh€mg4ÈǰíP%Œ¸Ô¨„«uA#lÛ¯„{À¶a¥½ÿJ¶Á0E™Î^úº7oåU!Û IDATx^í´E¶þ÷ QP Œ8FĬQÁ0£¨€Š€AG„ˆŠŒ *AQP ( #Š9 Šx*‚ ˜žã  $Qïíòû?÷z/Õ·OWŸª¯¿^Ë5séßî®ïT§?•”””    LøÍ`&ugÒ$P)?ýéO¤N€sà3=¨"šÁ*ãæ$€N@Í Í®ÊÔW[fFq Ð Æ%ÇýH”ͨ°ÿ—õÅÖ—Ù‘@4ƒq¨q&@³,®ˆP_l}™ Ä!@3‡÷!`4 ÀâÒ b‹ËìH &šÁ˜à¸   DUö÷¼¨/¶¾ÌŽâ ŒCû0š`qi±Åev$“Í`LpÜP Ð ¢*Ë™Ale™ Ä'@3Ÿ÷$H4ƒ²–&E}±õev$‡Í`j܇€ Ð,‹ËËÄØâ2;ˆI€f0&8îF¨hQ•åeble™ Ä'@3Ÿ÷$H4ƒ²ò21¶¬ÌŽ "@3X>îLxhñ4ÍψúbëËìH šÁ8Ô¸  Y—÷ b‹ËìH &šÁ˜à¸   DUö÷¼¨/¶¾ÌŽâ ŒCû0š`qi±Åev$“Í`LpÜP Ð ¢*Ë™Ale™ Ä'@3Ÿ÷$H4ƒ²–&E}±õev$‡Í`j܇€ Ð,‹ËËÄØâ2;ˆI€f0&8îF¨hQ•åeble™ Ä'@3Ÿ÷$H4ƒ²ò21¶¬ÌŽ "@3X>îLxhñ4ÍψúbëËìH šÁ8Ô¸  Y—÷ b‹ËìH &šÁ˜à¸   DUö÷¼¨/¶¾ÌŽâ ŒCû0š`qi±Åev$“Í`LpÜP Ð ¢*Ë™Ale™ Ä'@3Ÿ÷$H4ƒ²–&E}±õev$‡Í`j܇€ ¸2 /½ô’ôêÕK.\è„ÞM7Ý$óçÏ—‰'–i_ûmÕª•Ô¬YÓü{ýúõ¥M›6rÛm·I:u6ËI'$:t3Ï<ÓIÌÅhÔ•¾ÅÈ…}’ $C€f0Žl…`¸2 Å4ƒù&ô?ÿù´oß^N9å¹òÊ+½5ƒ¿ýö›T«V-ñºr¥oâ²A ÔÐ ¦†š‘@\™… ™Áï¾ûNÎ=÷\™7ož1@;w–Aƒ`o½õ–Y÷ý÷ßKíÚµåÖ[o•-ZÈÚµk¥[·nòúë¯ËvÛm'M›6•Õ«WW83X~F²ÿþòí·ßÊwÞ)%%%Ò·o_™:uª¨;ꨣLÕ«W—ü™ÁÊâ¸ùæ›åÃ?”ŸþY-ZdbxüñÇeÇw”~øAzöì)sæÌ‘zõê‰nÛ²eKùñÇåüóÏ—¹sçš~.¸à3kªË¦›njLê¨Q£ä믿–ºuë&Z8®ôM4H6F$*šÁTq³3ðŸ€+³°!3¨FHM™š³•+WÊ!‡"cÆŒ‘ã?Þ˜¼‹.ºHÎ:ë,yðÁeèСòÉ'ŸÈ¸qãä3fȪU«äÐC•Ã;ÌjÕ`p 2lØ0iÛ¶­<ýôÓÆ|½óÎ;æµ+Úw¿~ýäôÓO/c+‹CÍé!CÌ%ê­·ÞÚ˜º-·ÜÒ´¯&o£6½„­Æï¸ãŽ™Ôö—/_.÷Þ{¯¨>è ƒŒÝwß};jr¯¿þzOÒ‹+}“Ž“í‘ ¤G€f0=Ö쉂 àÊ,lÈ n¿ýö2eÊcŠtQ³¤3j׬Ycî÷SS¥FJgÜtNÍZ³fͤOŸ>¥û,[¶¬B3غukÙd“MŒáT³©3~jÄrÿ¦fRgît9çœsL:{˜?3XYj§OŸnÌœ.cÇŽ5ÆrÒ¤I²ÓN;ÉOúÈ\¢ÖK½½{÷––1ƒ•Å¡fP/k¬ºäÿ­—xuÆ0—W® Ôøm¶Ùfæ±.ëÖ­“Ž;š¸Ô ¾ñƲ뮻:)Wú: –’ ¤B€f0Ìì„Â!àÊ,Øfõ>;½D«ËW\a ŸÞË·Ë.»È»ï¾+{î¹§¹‡®Q£Ffš'½4|ñÅ›}.½ôRs^EO—¿gP·Ó:5‚ 0fì®»î2³Ý»w7}ä›A½G±²86dÕ>üðÃ¥y©YÕ¶÷Þ{oyì±Çdÿý÷ÿCa¨Ts©ý¹X\éë"V¶I$šÁt8³†€+³°!3xÞyçɯ¿þjîÔK©|°Œ?^¶Új+󰈚@EÓËÇ7ÜpƒyHãŽ;î0³{/¿ü²¬X±ÂÃ#<ÒjÕøéƒ·Ür‹|õÕWæÕ1ºï%—\bf#O>ùdc4GŒQ:3¨¦­²8Ô|V63¨‰èåî &˜Íc=V–,Yb ¨^šÖûÕØªù=ãŒ3Ìý‘4ƒÁ* ”`Ð ÂHÉDH .Í >I«³où‹š"5w:{—{š¸GÆœéÒ¥K™5k–l±ÅÆêÃz9÷™gž1•Ìž=[vØa9úè£Í=…÷ÜsO™öË¿gP/I«é=z´™™Óýµ½/Qg&õ>A}Xg õ¿Ü{+‹C×WfÕØj.úij¾ßPï'ÔËÛú4±^ŠÖW3xâ‰'šû#kÔ¨A3˜L³ * ¬,nJY àÊ f]9RßTbŒ$.šÁty³7ðžÍ‚÷ õ-w&H4ƒ²2)ˆO€f!>»ö¤¾!¨ÄI ]4ƒéòfo$à=šï%*(@ê[>îLh!eeR$ŸÍB|v!ìI}CP‰1’@ºhÓåÍÞHÀ{4 ÞKTP€Ô· |Ü™ Ð BÊʤH >š…øìBØ“ú† c$t Ð ¦Ë›½‘€÷h¼—¨ ©oAø¸3 @ „”•I‘@|4 ñÙ…°'õ A%ÆHé L—7{#ï Ð,x/QARß‚ðqg€$@3)+“"øhâ³ aOê‚JŒ‘Ò%@3˜.oöFÞ Yð^¢‚¤¾áãÎ$I€fRV&Eñ Ð,ÄgžÔ7•# ¤K€f0]Þì¼'@³à½DH} ÂÇI’Í ¤¬LŠâ YˆÏ.„=©o*1FH—Í`º¼Ù xO€fÁ{‰ ú„;“$šAHY™ Ä'@³Ÿ]{RßTbŒ$.šÁty³7ðžÍ‚÷ õ-w&H4ƒ²2)ˆO€f!>»ö¤¾!¨ÄI ]4ƒéòfo$à=šï%*(@ê[>îLh!eeR$ŸÍB|v!ìI}CP‰1’@ºhÓåÍÞHÀ{4 ÞKTP€Ô· |Ü™ Ð BÊʤH >š…øìBØ“ú† c$t Ð ¦Ë›½‘€÷h¼—¨ ©oAø¸3 @ „”•I‘@|j¸`())ÁNÙ‘ T‰Í`•pqc 4œ)CS”ù T•Í`U‰q{ (4ƒPr2 hc@ã.$@8hq´d&$@ñÐ ÆãƽH€@Ð ‚É4H€b ŒŽ;’  DP‘9 B€f°zÜ—H x4ƒÁKÈH€ $@3X @îN$6šÁ°õcô$@… ,œ![ ˜Í`Àâ1t DÐ &‚‘ „J€f0Tå7 @Rh“"ÉvH€‚$@3¤l šH A4ƒ ÂdS$@á  O3FL$,šÁdy²5 ÀÐ &Ã%HœÍ`âHÙ @HhCR‹±’ ¸ @3è‚*Û$†Í`0R1P Ghe³$@a  C'FI$àŽÍ ;¶l™H 4ƒˆÄI€œ tŠ—“ øN€fÐw… €k4ƒ® ³} ¯ Ð z-ƒ#HÍ` Ù €¿hýÕ†‘‘ ¤C€f0Îì…HÀS4ƒž ðH€R#@3˜jvD$à#šAUaL$@i L“6û"ðŽÍ w’0  ” Ð ¦ œÝ‘ øE€fÐ/= @úhÓgÎI€<"@3è‘ …H (h‹‚’@<-Z´(™9sæŸâíͽ*"P¿~ý’+ViÊåqÔQG•̘1ƒÜSæÎîH "4ƒ¬ ˆg±‹¡nk™Bþ ôG FBV@­ˆ¸A XËÅ03A€f023I@Q”d¬eÖ øC€fÐ- X pµ"â`-"ÃÌšÁLÈÌ$QpEQ’y°–Y$àšA´`$$`%ÀÔŠˆB€µˆP 3h3!3“D!ÀEIæÁZf €?hýÑ‚‘€•P+"nÖr B1ÌL Ì„ÌL…P%™k™5@þ ôG FBV@­ˆ¸A XËÅ03A€f023I@Q”d¬eÖ øC€fÐ- X pµ"â`-"ÃÌšÁLÈÌ$QpEQ’y°–Y$àšA´`$$`%ÀÔŠˆB€µˆP 3h3!3“D!ÀEIæÁZf €?hýÑ‚‘€•P+"nÖr B1ÌL Ì„ÌL2TÓ§O—:ÈØ±c¥K—.’@'Mš$^x¡<úè£ÒªU«PÓcÜ&@3˜añ™ºwh½“„‘@Yµk×–êÕ«K:udùòåÒ AY³f¬_¿^Ö­[G\$þ° B&™Q4ƒži‡C`äÈ‘2xð`ùùçŸKƒ®Y³¦ 2DúõëN"Œ4óøÃ&ó%@ž ôT†EùtVpíÚµ¥ÿT«V­2“ „@€?lBP‰1f‘Í`UgÎÁÈD9+œ| 8ذHÀ?4ƒþiˆH B¹A”³‚, ð‡MÈê1vT4ƒ¨Ê2/8:ˆ4H†Ê{áÔÍVBüa“-½™­ÿhý׈’€! OwïÞ]&L˜ z©˜ „J€?lBUŽq£ DU6#yé»Ê¸`()) >AÖiðZ@¨Sk’Ü–Í ¬´ÙHŒ/®ÅÖE_”<°«-~vÔ7>;îéšA?t`1 ð$\ »¡è‹’G e“z˜Ô7uäì0a4ƒ eséàI8]Þi÷†¢/JiëJÔ7¥gehYAàI8hù¬Á£è‹’‡U°Œn@}3*kð(ú¢äa,£Pߌ ”6Í ˜YL…'alÕQôEÉ»ÚâgG}ã³ãž~ ôCF“OÂ1Á²о(yR6©‡I}SGÎ&@3˜0P6—.ž„Óåvo(ú¢ä‘¶þ¡ôG}CQŠqVF€fµ4ž„ƒ–Ï<о(yXËèÔ7£Â¥M3$fSáI[u}QòÀ®¶øÙQßøì¸§hýÐQÄ$À“pLp솢/J”MêaRßÔ‘³Ã„ Ð & ”Í¥K€'áty§Ýо(y¤­(ýQßP”bœ• dmM€'á å³¢/JVÁ2ºõͨð@iÓ ‰™ÅTxÆVE_”<°«-~vÔ7>;îéšA?t`1 ð$\ »¡è‹’G e“z˜Ô7uäì0a4ƒ eséàI8]Þi÷†¢/JiëJÔ7¥gehYAá$|ÄGH¯^½äÌ3Ï,Ãú†nO>ùD&NœX© .”o¾ùF´,.!èE—ò`FQ²âmBÐ7~vÜ3 h³ 2pŽ!œ„+dW¯^-¿üò‹lºé¦•*4fÌY·n\yå•NUüõ×_e£6rÚGœÆCÐ7J^!äÁ:¢$Í`|JÜÓg4ƒ>«ÃجBdóg_|ñE¹ä’KdíÚµR³fM5j”Ô®][N;í4©Q£†tíÚUFŒ!Æ “ûî»O4ïcŽ9FFm¶?ùä“åàƒ–7ÞxC–,Y";ì°ƒ<öØcÆà}öÙgÒ³gO3ÃX¯^=;v¬vØafß?üPæÍ›'mÚ´1mû¶„ of!äefuJ3¥Þ¹MxhÃÓŒç@d›4i"wÜq‡¹üþûïË­·Þ*&LÞ½{ËvÛmgf§N*ýû÷—Ù³gSסCiÑ¢…\xá…æÿûí·¢ƒuµjÕdß}÷•›nºIZ¶l)x œ{î¹Ò£Gy뭷䤓N’/¿üRî¼óN¹æškdΜ9Ò¸qc/ë*}£€ !(fuJ3¥Þ¹MxhÃÓŒšA5m»í¶›ôíÛWvÞyçÒ óÍ`·nÝd÷Ýw—+®¸Â¬æ™gDgg̘aÌàá‡.}úô1ëN9åiÛ¶­™=Ük¯½dåÊ•Æ$ê¢3ˆºŸÎ N›6Mžþyok*^yD1ƒ¬SšÁ(õÎmÂ#@3žfŒÐ .]ºÔÌÒM™2Eêׯ_:«—oO<ñD3‹§³‚ºè}~ 6”·ß~Û˜ÁãŽ;NºwïnÖåþÞÿý¥yóæfv1·¬ZµJÆg.ëeåûï¿ßÛš ÁDEBQÌ ë”f0J½s›ðÐ †§#4ƒù¢>ûì³æÉãåË—›KÀ¹ËÄjôô2ÝÅ_ü‡¨Ì ªAÔ}~øá‡?죗¢Õ\Nž<ÙÛš ÁDEBQÌ ë”f0J½s›ðÐ †§#3ƒú@ÇÑGmøPã·hÑ"ѽï¾ûÎûìJgÕ@6mÚT.½ôRéÔ©“,[¶L.ºè"³ï=÷ÜC3˜ÒÑ„`Y§•Kú¦Têì&P4ƒ ǰ'ÂIXg\ôr¬Æš[ô¾¿Í7ß¼ô=ƒj̆n^5³ñÆ›KÆú„ðôéÓåÔSO5÷ÿéå\ÝfÒ¤I²~ýzsáÝwß-Ûl³ÍÍ >M|Î9çÈW_}eîÔûõ½‡œLï(b²NÓ«6öDU'@3XufÜÃ#! ²á .}Qò®€R ˜ú¦šÝ8#@3è -NƒOÂiP.^(ú¢äQ¼Jð»gêë·>ŒÎN€fÐΈ[xL€'aÅI 4}QòH@RÈ&¨/¤¬™JŠf0Srã%Ë“0ž¦ù¡è‹’vµÅÏŽúÆgÇ=ý @3臌"&ž„c‚ d7}Qò¤lR“ú¦Žœ&L€f0a l.]®NÂ4¯s©^½ºIH_ݾ}{3fŒÔ­[·JI¾ôÒK²Ç{”yñs®~ýúÉ£>*%%%æ "úI:}šxÍš5æóqú:íO_)£W´èçé4Ö§žzªÌêŠÞ÷Þ{ïÉQGeÞ;¨ß>Ö§—uÑXëwŒuÑ×ÚèL dþþí·ß̓й§¡Ÿxâ yóÍ7ã³!˜®ô­’€ l\Hª£òÎ}Afë­·6ïŸÔ§Â«²¨Ö?ü°œqÆÜM?q¨/3衇Êl§ï½Ô:¾öÚkÍÓèZ—_~¹ùV¶¶}üñÇ›OVt|üüóÏæË:‹/.ÍC×ãL_~>qâÄ2}m¶Ùf2sæLÑwnVV‡ú-í˜ýôøÑÿrŒôõIO«V­Ìë—tÑW4é Øo»í6Ùe—]Ì¿=ùä“rõÕWË×_mþÖ¯óèSö;í´SUÐñVƒ*%Ä3G€f0s’c%\È »!å)°N?ýt9äCäúë¯QKœÔDªéÓ(Ñ÷ ê«b^{í53Piû{ï½· 2D®ºê*™7ožÿýïË‘G)j*õÒå5Šúš5ù‹Í æ¶]¸p¡iwíÚµæ¥/ÀÖõú}äÜ’Ÿ(]é¥ï$·)$ò:ê·«[·nm^+t 'D SÍþ|`êpCŸÔWiýi­–7ƒú>K5hZjõÛÙjâÔ°©áÊ}þPk·ü2kÖ,¹ùæ›EHä/63¨?LrKEu˜[§qèñ”Ÿ›/§Ö·.«W¯6æS75×jL8àyá…Œ \·nùö÷믿nÞ¿Y•¥}«Ò·%Wh]‘e»©pu®hÊÍ"¼ûî»æ…Ðçž{®1ljø:wî\:ƒ±é¦›šsÔ¨QföFg0¶Ýv[ó·Î¸å‘øßÿý_3S¡‹ÎH¼úê«föF¿'¬ƒ}Î@ê˧µÝÁƒÿë_ÿúW3#£ûä/išAí7ŸORâ»Ò7©ø¢¶SHé¨_¡Ñº=z´ùÆ´š¹ÓZojÌrë´F=öXyä‘Gäûï¿—fÍš™åÝ_ûÒ¶Õôå›Aý[tèýa ý©aªS§Žì·ß~¦©ë®»N>úè#óÌò‹Ä¿üå/rÞyç•Y•¦ÔŽuF\8­X±BÞzë-ϧŸ~Z“þ Ò~5Š*­Ù®}«Ô7&Ghe³épu®hzüñÇͬàܹsÍ`¨—¥Ô„­\¹ÒÌê%d½4µå–[J·nÝ̶Ÿž:sQ~fPg(ôeÐjõ{ÃjuQ/ëL‹J[l±…©ýü÷ÿ÷¾#üÓO?™Zg9Ê/i›Á|>I©ïJߤâ‹ÚN!yT¤ã\`n'1b„©/5[ú…™ Èa‡&Ÿþ¹<øàƒæååjÚ7nlfδ+›ìÚµ«üío3_·ÑmsfP/ñêÌ™šÉ[n¹¥Ô æç®_ÍÑ—£ë ÓN;íX´öÕ$î¾ûîE5ƒß~û­üùÏ=nt¶TåÕ¥Kc„5÷8K!úÆéû@Òh“&ÊöR%àê$\Þ ê¥ÚŽ;š{ô¦í·ß^¦L™"tÉW/¿é¬‚ ȴiÓJÍ_efP÷Ó™ Tó§mé@­9éß«V­2¾.:ê%¶©S§–á«ýèL¢Þ·U‘œ?¾¹70·è½]ú_þ·Š“¸L\žOREàJߤâ‹ÚN!yä›Aý¢3}úÍi½×T?_¨—RõIîžÎC=Ô˜2ýâŒÖGÎümÈ êe\ù{å•WŒÌ7ƒúïÚ¯^>îÝ»÷Ì þÒY¶ž={Êí·ß^zïiŽÎÂiŒ_~ùåpéq¦3˜:ë¿,]ºTt>ÉËÄjõ2°š§.úyF=f•Ó'Ÿ|b> ©÷ßæ÷EãBôÒ>·!×h]fûN ¸: —€D/-錇Îö©¹ÒËq:xän4×KÀz/×äÉ“ÔÏÏíºë®&÷ÊÌ œjîtFMÛÔKÁz_“Þ‹¥fðþçDÐE¿ «3’úIºüEo”×ûžô~®ŠÌ ÎéŒMnÑ™#ý; 3˜ÿ€My>I‰îJߤâ‹ÚN!y”€D àe—]ff•µÎô^Óü™á6mÚ˜Oê]Ÿ«™ÊÌ Îüé¥c Ô™»|3¨Ÿ2ÔA:»¨5_‘TjÞôò²>ø¡u¿è&­s½±ü¢Ç™šÄo¼±Ì*C j¡f0ÿ=Æô– OÌ•_Ôêq6nÜ8sìå~ˆEѸ}£´ÏmHÀ5šAׄپS®N•ÝË”KF5q:+¢‹~kXgÜtPS3¨ƒgî‰ÅÊÌ ^Ö[gTtѧ‚ÿñ˜K|ûì³¹$§Oþꢗ¥Õh\ù‹^æÒàu]EfP÷ÓïsKþÓĹKbfЕȮôuoeí’GE—‰sýh­¨aÒÙ·Ü“´jì´NÔ jê]*3ƒ:«§÷êýºèƒj$µvíÚ™§‡sOäþøãæ ûóÏ?ß+ý1¤Oë¢æM ªþHÊ_t[­Ì¯ÃÜú´ïÌëí·ß6³©M›6-ýgÕ§¡uöµü%í ÕL!ú¦]‹ì*"@3Ⱥš€«“°Í êå]½çHïåÓ›òõžªñãÇ›ËLåÍ ÎÜé½]zi/ÑKVü±¹«Ff×KXú †> O5ê¥À/¾øÂ´«÷æPK–,1ÿ^~ðÍõ‘ö=ƒ. É•¾.bue6dÕ¼è }ÅŠ>Ĥf½hþ IDAT_kBM¢Îæ›AÓÙ\ýÑ‘»¤\QÌå/ço“?3¨·Fè¥\­QuÓujJõ¶…üEkV@ÑHÊ/Å4ƒz{†>ͯ?êô!˜õë×›cX=5ÒzLF]Pê4j¾ÜÍ ž¦™ÊÈÕIØfÕê¬[îib}èC/óêRÞ êSÃ:c¨3,zãnÑ{uÖD ÑY4õ=ƒ:먗ît–EïÔ‡KÔLžuÖYe´½÷Þ{Í¥ãÜÓÈå…§ôçP(¤N7d5Ãü§‰uvOoYhÙ²¥y:=ß êI:Û§¯<Òû +[¢šA}¦Ö¯Þo§mêƒZ¿Ûl³MiÓz¹UªÒÛ*ZŠi5½,¬1ÿç?ÿ1³Ÿú£N5™¯ÊRˆ¾Ué‡Û’€+4ƒ®È²ÝTð$œ æ¢u‚¢/JE+Ï;¦¾ž Äð¬h­ˆ¸ÏxöYÂcCÑ%ÂÅlúbꚥ¬h³¤6`®< Šš—о(y`W[üì¨o|vÜÓ4ƒ~èÀ(bàI8&¸@vCÑ%@Ê&õ0©oêÈÙaÂhÊæÒ%À“pº¼Óî E_”<ÒÖ?”þ¨o(J1ÎÊÐ ²6‚&À“pÐòYƒGÑ%«`Ý€úfTx ´iÄÌb*< c«Ž¢/JØÕ?;êŸ÷ôƒÍ :0Š˜xŽ .ÝPôEÉ#²I=Lê›:rv˜0šÁ„²¹t ð$œ.ï´{CÑ%´õ¥?êŠRŒ³24ƒ¬  ð$´|ÖàQôEÉÃ*XF7 ¾(mšA 1³˜ OÂØª£è‹’vµÅÏŽúÆgÇ=ý @3臌"&ž„c‚ d7}Qò¤lR“ú¦Žœ&L€f0a l.]< §Ë;íÞPôEÉ#mýCéú†¢ã¬ŒÍ k#h< -Ÿ5x}Qò° –Ñ ¨oF…J›fHÌ,¦Â“0¶ê(ú¢ä]mñ³£¾ñÙqO?Ð ú¡£ˆI€'á˜àÙ E_”<)›Ô侩#g‡  L(›K—OÂéòN»7}QòH[ÿPú£¾¡(Å8+#@3ÈÚš€ž„¹`()) >AÖiðZ@¨Sk’Ü–Í ¬´L ‘g UÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³#ÀALPÐtX§ Â2-X4ƒ°Ò21DdUÅˉuЧ)3Â&@3ˆ­/³ œÀôéÓ¥C‡2vìXéÒ¥‹äÙI“&É…^(>ú¨´jÕ*ð,~èX§¡+Èø³N€f0ëÀü½'P»vm©^½ºÔ©SG–/_. 45kÖÈúõëeݺuÞÇϳA€uš ™%&šAL]™‘#GÊàÁƒåçŸ.ͪfÍš2dÈéׯP¦L%d¬ÓÕcìY'@3˜õ `þAÐYÁµk×–ÆZ«V­2‘ƒ„'À:…—˜ ‚ –iaÈŸuᬠ–¶HÙ°N‘Ôd.Y"@3˜%µ™kÐr³.œ ZFøàY§ð3A@4ƒ€¢2%L:ë2hÐ :t(ïÄ”"+Ö)„ŒL"ch3&8Ó —€>=ܽ{w™0a‚è¥b.$à#Ö©ª0&Ø0cõÝe\° ”””@&ÈÚ…”µLRµË:e†J€µªrÑãÖsl©D8áFO=[[" 9·lUiÅ٢苒k»N+ÊŽµ‹]õ9}i±u6Ù!Ìȹe 4­)¢è‹’‡U°Œn€¬/rn-×2iÓ f¨ fäÜ2T¢•¦Š¢/J¬IÎ ²°Ð bé¹Ál"äÜ2T¢4ƒ;hÈç!äÜ‚.º„‚§LdÍ Ìȹ…P[®cDÑ%×z‡Ú>²¾È¹…ZoIÆM3˜$MÏÛB>˜‘só¼¬R E_”˜‘s+fÍøÒ7о(yøR¾Å¬/rn¾ÕQ1â¡,õ"õ‰|0#çV¤rñª[}Qòðª8< Y_äÜ<*¡¢…B3X4ôéwŒ|0#ç–~¥ø×#о(yøW!~D„¬/rn~TOq£ ,.ÿT{G>˜‘sKµH<í E_”<<-“¢‡…¬/rnE/ ô@„´B@>˜‘sK«>|îE_”<|®•bƆ¬/rnŬ_ú¦ôE‰â@>˜‘sK¡4¼ïE_”<¼/˜"ˆ¬/rnE*¯º¥ôJ·Á Ìȹ¹­Š0ZGÑ%0ª&ý(‘õEÎ-ýJñ¯GšAÿ4qòÁŒœ›³‚¨a}Qò¨tR Y_äÜR-O;£ôTa!Ìȹ¹¨…ÐÚDÑ%Ðê'­x‘õEÎ-­úð¹šAŸÕI86äƒ9·„Ë ÈæPôEÉ#È"J!hd}‘sK¡4¼ï‚fÐ{‰’ ù`FÎ-¹ ·%}Qò·’ÜFެ/rnn«"ŒÖiÃÐ)‘(‘fäÜ?ðFPôEÉ#ðrr>²¾È¹9+ˆ€¦ H¬BCE>˜‘s+Tw„ýQôEÉ¡¦\䀬/rn.j!´6iCS¬€x‘fäÜ fW}Qò€)¬„AÖ9·„Ë ÈæR3ƒ”o¾ùF&NœXÔf›m&3gΔý÷ß?À“N:I:tè gžy¦¼ôÒK²Ç{ÈvÛmgþí¸ãŽ“îÝ»o°]kذaòÅ_ÈN;íTºí’%Kdûí·—~ýúɵ×^+¶n¸A>ùä“óÂ… MþGqD¬¶’Ø ù`FÉMëqäÈ‘R½zu#yýúõ¥}ûö2fÌ©[·n¬2¸é¦›dþüù8öb5V¤PôEɃuZñ€¢oEÙ¹ÌþÀð§’’’’ÐÅÖASÍ[óæÍ«dï½÷^9çœsdÀ€¥Ç€¾jæºvíÛ ®^½Z~ùåÙtÓMÍ`¾nÝ:¹òÊ+#§¿ýö›T«V-òö¶ ]êkëÛõz”ÜÊŸõÄé§Ÿ.‡rˆ\ýõ±0Ò ÆÂæd'ÖiåXY§NJ.±F]Önfþ`Ã¥àÕÌàgŸ}&={ö43hõêÕ“±cÇÊa‡&%%%Ò·o_™:uª¨A:ꨣÌ,‡ÎžäfÿýïËàÁƒeÛm·•Q£FÉ< {íµ—™-Ôvÿú׿Êc=&m´Q"¹"|ã7dÁ‚¥ëš5k&»îº«4jÔȘÁï¾ûNÎ=÷\™7ož1h;w–Aƒ™í7ß|sÓ§¶¿xñbùÇ?þ!ýû÷7fRguÛÓN;MjÔ¨aÌ¥ÎDê¾>ú¨Ùÿàƒ–Ûo¿Ý˜ÆvíÚI“&MdÒ¤I&Çã?>ˆƒ9± c6äòD3¤X»UtR|òÉ'åꫯ–wß}wƒuøá‡J¯^½dùòåR«V-SgmÛ¶²±¤p²ëT„uꤴœ7ê²v£˜Aúƒtü3ƒx 1\=zô·Þz˽/¿üR^xá3£öÎ;ïˆ¤Î’è  Î˜ä_&V¥*73¨Q÷U¦ëî¸ãiÙ²åÌ`íÚµåñÇ—þóŸærõçŸ.§œrŠœ|òÉffOÍ ²jJï¼óNY¹r¥‰AgûÔ¬5hÐÀ¬×í´ÏÆË÷ßo ^î2qïÞ½ÍåkÍ㡇2³<¯¾úªl¼ñÆæ·®»îºëäÔSO•¥K—Ê‹/¾(uêÔIôWvC† 1¦Y—¡C‡š¿u ýß5Õ'ô¥¢“¢Ö¦ÖËܹs+­ÃÖ­[›¿êª«¤S§Næ‡þÒZž2&mÑ¢E¢ƒŸІÌ`þ$•=P’3ƒgŸ}¶~øá¦m5jO=õ”ÜsÏ=¥fPÍ¢»ÜC&zYøý÷ß7³.jçÌ™#»ì²‹ =÷·^Þ«È ê,ŽÎj挩΂ê,ä×_mÌ ®×ÙѤ—sÒ±Vµ=”ÜÊߘ¯· è-ú#%÷£¥¢:<ÿüóMMém ¹¥M›6¦®~úé'šÁª”£íY§¬SG¥å¼Y—µ«ç=úƒßÍ`±ýA*fpCO«ÒË\?üðÊZgu–î®»î2÷üéÂz/_’fPÛRÓ©¦p„ 2{ölsé4fPgúôò°.W\q…Y§æ¶ªfPÿ>ûì#—_~¹ië¹çž3¹èlbÔ§ ãù.æ8ñ$¹Jn•].ɱҙÁŠêð¼óÎ33ƒ+V¬(Y×û^µ=½TÌ™Á$«-~[¬SÖiüê)îž.k×v™˜þ =Pt3¨YÓ¦MÍ¥S½çI/Ù^tÑE2~üxcÐr—|u6Ng;ôÒÙˆ#ÊÌ êeZý7,oªl3ƒZŒz?à5×\cŒÞ\PÆ ê`û믿šmô~@½„­±}ôÑ‘Ì`Ÿ>}Ì¥h}mÈ#ŒD3O¤÷b²N“®©´ÚsY»63Hž?ð êÓB: øÕW_™Ù }‚XÌÐYº³Î:KjÖ¬ifæôÒ°>u¬3…ú_î=ƒj¦t¦NäÐKÏU¹L¬Å¨O ëÓÈz©¸aÆeÌ @%÷4±^ÆÍÝefPï)Ô‡Cô¾Åûôib½aSùæ›Í{ähãÚ\ž¨âEo/›ÜPæ?¥©é­ úÀŸ&ާ…‹½X§eŸ&fº¨27mº¬Ý(fþ àÜ º)O¶Z.æªÄáb[äÜ\ð ­M}Qò­~ÒŠY_äÜÒªŸûÉéK3è³J ņ|0#ç–üA7ƒ¢/JA“Ãà‘õEÎÍaIÓ4Í`0R(òÁŒœ[áʇßо(y„_Qn2@Ö977ÕV«4ƒaéUP´È3rn‰²3о(y€”Uâi 닜[â…`ƒ4ƒŠ7däƒ9·¸z#퇢/JHµ•d.Èú"ç–d „ÚÍ`¨Êňù`FÎ-†Ôp» è‹’\%”²¾È¹%$ÐÍÐ -_Õ‚G>˜‘s«šÊ˜[£è‹’f•ž²¾È¹®|ø-Ð †¯aä fäÜ" ¼!о(y—ZA©!닜[A¢ƒìL3"d”4fäÜ¢h‹¾ о(y ×[ÜüõEÎ-®ÞHûÑ "©iÉù`FÎ-C%Ziª(ú¢äÁš¬˜²¾È¹±žEh3TÈ3rn*QšAŠ4äórnA]BÁÓ &2„ffäÜB¨-×1¢è‹’‡k½CmY_äÜB­·$ã¦L’¦çm!Ìȹy^V©„‡¢/J©ˆ`'Èú"ç`©%2Í`âHýmù`FÎÍߊJ/2}QòHOù°zBÖ9·°ªÌM´4ƒn¸zÙ*òÁŒœ›—Å”rP(ú¢ä‘²üÁt‡¬/rnÁ˜Ã@iÂõ­iäƒ97ßê¨ñ è‹’G1j „>‘õEÎ-„Úr#Í kµ|0#çæQ -}Qò(Z!xÞ1²¾È¹y^V©„G3˜ f?:A>˜‘só£zŠо(y·üíY_äÜü­¨ô"£LuÑ{B>˜‘s+záxо(yxP^†€¬/rn^SÊAÑ ¦ ¼˜Ý!Ìȹ³f|éE_”<|© ßâ@Ö97ßê¨ñÐ ƒz‘úD>˜‘s+R¹xÕ-о(yxUƒ¬/rn•PÑB¡,úô;F>˜‘sK¿RüëE_”<ü«?"BÖ97?ª§¸QÐ —ª½#Ìȹ¥Z$žv†¢/Jž–IÑÃBÖ9·¢ŽÐ z BZ! Ìȹ¥U>÷ƒ¢/J>×J1cCÖ9·bÖŒ/}ÿÁ úãpC ¤¤ÄMÃEnU ™ 6„Úebרf‡P§©ÄÚÍFíþ©µ‚-ú]|ñÅòÔSOÉÍ7ß,mÛ¶ÅW›Bà¯tá“`ÂK —à¬Y³¤K—.²hÑ"¸Ü¢$”Y3¨p¾üòK¹è¢‹dÕªUÆ6iÒ$ 3nCE#ÀA¶hèÙq°N«‹›À½÷Þ+wß}·Ìœ9³è±+€L›ÁôW^yÅ˜ÂæÍ›ËM7Ý$uëÖ-–ì—6H€ƒ, $¬ÓTbŒJàꫯ–Ï?ÿ\&Mš”i 4ƒyòOœ8QôòqÿþýÍ\HÀ7d}S„ñTD€uʺ@·nÝdûí·—¡C‡†®Ói+À;`ÀQc¨³„:ur*'ªà [ZܶXX§Å"Ï~£8æ˜cäÌ3Ï”®]»FÝz;šÁJä]ºt©™%üâ‹/Ìý„Íš5ƒ.&²aè”õ(Y§Y¯¿óßyçe„ rôÑGûhŠÑÑ Z`Ï;×ÜO¸ÓN;S¸ÕV[¥(»"²8Ȳ"B À: A¥ìÅøÕW_I£Fä_ÿú—4nÜ8{61Í`Ärx衇Œ)ìÞ½» 6,â^ÜŒ’%ÀA6YžlÍ Ö©®l5>×^{M:wîl^S­Zµø îI3XEa‡n̠Ϊ1äBià ›&mö—ë4.9îç‚ÀäÉ“åÎ;ï”W_}ÕEómÒ ÆqõêÕf–pΜ9æ!½• ¤A€ƒl”ÙG¡X§…äþI¸öÚkåÓO?•ûî»/©&!Û¡,@Öùó盇L6Þxc3S¨÷r!—8Ⱥ¤Ë¶“"À:MŠ$Û)„@=dë­·–k®¹¦f2±/Í`2?ýôÓf¦P?k§¦ ¸"ÀAÖY¶›$Öi’4ÙV-[¶”Ž;òv®ˆðh#‚Š²ÙØ±c)ÔKÇú¿\H id“&Êö\`º Ê6£Øu×]eܸqrì±ÇFÝ%óÛÑ :(½ti`¦M<»ý1BôÍ÷ßv!$9Í`«ÒÄ€Ì7u–°S§NUÙ•Û’€pe„@€u‚JáÇxÎ9çÈ–[n)ú1.… ,Œ_¬½—-[ff ¿øâ óäqóæÍcµÃ²G€ƒlö41cÖiˆª…sëÖ­åÔSO•ž={†¸§ÑÒ Q˜¹sçš—V뇳u¦p«­¶*b4ì:dCP‰1²NY. ì¾ûîrË-·H«V­\v“©¶i=û¡‡23…ÿõ_ÿÅénôð9²>«ÃØrX§¬¾ùæ3yòÁÈn»í梋̶I3è‘ôz#¬~GQg »wïîQd Åd}Q‚qlˆë”õ‘49sæÈÉ'Ÿ,úÚ¶Zµj%Ý|æÛ£ô¬V¯^mf ßxã c 9æÏ"d8Å$ÀA¶˜ôÙwT¬Ó¨¤¸]?ü°¹¿^ÇE.nÐ ºáZp« ,0¦pã76AãÆ n“ „O€ƒløf!ÖiTN'Çë®»NæÍ›'z;whݱM¤å§Ÿ~Ú]:tè úc K—.¥/ž4i’\xá…òè£òõ Ø%Dv¬Ó d &ȽöÚKn¼ñF9þøãƒ‰9ô@iTPŸ¦ÒYÂüјÂ}öÙ'À,rTµk×6^¯S§Ž,_¾\4h kÖ¬‘õë×˺uë¢6ÃíHÀ)Ö©S¼™h|éÒ¥æÕ1ï¼óŽì¹çž™ÈÙ—$i}Q"F3fÌ0¦ðC1¦°nݺ1Zá.¾Ðûe,?ÿüsi¨5kÖ”!C†H¿~ý|Ÿñe„ë4#B;Jó­·Þ2÷Æëd‡þðå’.šÁty;éí®»î2¦P~û˜ =9®]»¶41}ÏVþßx3£ °NCT­ø1ëí.£F’7ß|³øÁd4šA á(ãÇ7³„:uÊŒ©äϺpVõà+Ö©¯Êø—š@|ä‘Gü 2‘Ñ ‚‰¼lÙ23KøùçŸSؼys° ³›NnÖ…³‚Ù­2g† ’1öîÝÛ¼K÷úë¯÷#  GA3*¾N·«)Ô›qõ¥Õ 6Í4;ié¬Ë AƒdèС¼W0;²—)ë48ÉŠ°Þ¨O ŸþùE韖%@3^úÖv}/a·nÝdøðáàÙb§§Oë7«'L˜ z©˜ øH€uê£*~ÅÔ¤IÑ/‹´iÓÆ¯À2 Í`FÄ1b„\{íµf–°G0YëËm¹`()) >AÖiðZ@¨Sk’nðí·ßÊŽ;î(sçΕ½÷Þ»ÀÖ¸{’h“¤éy[«W¯6³„³gÏ6÷sÌ1žGl_:°3 y }Qò¹–\ÆN}ítõÝ­[·–Å‹ó5hv\©oA3˜:òâw¸`Ás?¡Þè­¦°qãÆÅ*f< ÇÈn(ú¢äHÙ¤&õÝ0òÇ\ôêÔÛo¿º6ì0šÁhœ ·š6mš1…z#¯^>ñROÂ¥Yšо(y`W[üì¨oåìFm®F=öØcñsOçh#ö¿ýî­^>ÖƒVÿ7¤…'áÔªz¬(ú¢äQu³±õ­Xgý~z5Ìw†¹øM€fÐo}R®OŸ>òä“OšYÂöíÛ§ÚwÜÎxŽK.ŒýPôEÉ#ŒªI?JêûGæíÚµ“–-[Ê\¾ ì±Êh«Œ {ý.¤Î®\¹ÒÜO¸Ï>ûx0OÂ^ËSpp(ú¢äQ°   Pß²Âî»ï¾2lØ0iÛ¶-¨âxiÑ âišHF3fÌ0÷|ðÁÆÖ«W/‘v“n„'ᤉúÕо(yøUþDC}×âûÚ¬\ZIDATï¿7:xíµ×¼ŸHð§züˆ„fмâî»ï6¦ðÊ+¯”x'OÂÞI’h@(ú¢ä‘¨¸@Q_‘yóæ™×•éÕ¥M7ÝHÝl¤B3˜ ÎràÀ2~üxs?açÎ n/©xNФŸí è‹’‡ŸURü¨²®¯Þk~õÕWË»ï¾[|1A,4ƒ±°es§eË–™û .\h.7oÞ¼è l'á_—St¨—X§•ƒpC¨Ó8iëÁ¬Y³dÊ”)qvç>ž ôDˆÂxóÍ7Í¥cý¬šÂ† -ü(ƒ,êI¸hÐ=êØ¦¿/¡Úâ´­÷%ƪ¾:9 ¹3&îå šAo¤/‡~ؘÂnݺÉðáË’€í$k[_” ÙibBÑ×§m}bÀØPQ ê{ÒI'I‹-ÌÀ%|4ƒákXô ô3C×\s™%ìÑ£GªñØN²¶õ©ËÎ'о¶8mëÇS%€¦ï C† æ}´©Šhg4ƒ ç[ØkÖ¬1¿õ³CzɱÇ›Jˆ¶“¬m}*A²gBÑ×§m½3€l8(ú®X±Â¼:F_=¶ÿþû§ÂޤC€f0ΙéeÁ‚æ!“Úµk›™ÂÆ;ÍÝv’µ­wwN }mqÚÖ;Éœ@Ð÷ƒ>¿ýíoæÕ1›m¶™S^l<}4ƒé3ÏDÓ¦M33…mÚ´1¦ÐÕS½¶“¬m}&ÄN2}mqÚÖK˜‰ÔB×÷©§ž’«®ºJÞÿýLè•Å$i³¨zŠ9ßrË-ÆŽ=ÚÌ&½ØN²¶õIÇÃöÒ%о¶8mëÓ¥ÊÞ’&²¾cÇŽ•—_~Y¦Nš4¶çšAÄ@¥OŸ>æ=T:Kؾ}ûÄRµdmë „ …@(úÚâ´­/ \všPõíÛ·¯üúë¯æ¼Í›Í ¶¾^e·xñb3K¸råJó‰~̼ÐÅv’µ­/´î_\¡èk‹Ó¶¾¸”Ù{¡BÔ÷ÔSO•#Ž8Bô‡<|4ƒø{—¡>‰¦—Œ:è ó‹³^½z±c´dmës»º§1vbÜ1qÅ|ù¸­mëY§‰—ƒ· ³NsPš6mj¾EÊ)§xˉ%K€f0Yžl­ î¾ûn3SxÅWˆ~û8ÎbDmëóYNÂqp;¨u`o)Þ¶þmëY§ñ¸‡¶WÔ:p•×O?ýd¾,5}út9ðÀ]uÃv=$@3è¡(Y IŸR»ãŽ;Ì,açΫ”¾íäi[ÏA¶J¸ƒÝ8j¸JÐÖ¿m=ëÔ•2~µµ\D=þ|9ôÐCEoçÙb‹-\tÁ6=&@3è±8Y mùòåf–páÂ…æ~B=)å/úé»§Ÿ~Z&Ož\æßm'OÛz²Ù¨²¨uàŠ†­ÛzÖ©+eüj7j$µ¾ ¬_¿~òá‡&Ý4Û „Í` Be%Ì7ß|ÓÜO¸Ã;˜™Â† šÔuæPÍâ¤I“äôÓO/Åa;yÚÖsÍFeE­W4lýÛÖ³N])ãW»Që ɨo»í6yþùçÍm.Ù%@3˜]í½Î\gÕüuíÚUôÛÇ£F’Ë/¿\ªU«&k×®•5j˜øm'OÛz²^—AbÁE­ƒÄ:,×­ÛzÖ©+eüj7j$õe—]&ú)Ñ[o½5©&ÙN h.+a9R®¾újiÙ²¥è[ðu9òÈ#eæÌ™4ƒY)‚òL{-²­ÛzšÁŠ €&¢ÖA©œvÚiÒ¬Y3¹ôÒK“hŽmN€f0p³þ¼yóÌ71õI7]6ß|s>|¸ôêÕ‹3ƒY(€rLs­(\[ÿ¶õ4ƒ AMD­ƒBS9øàƒÍ[:tèPhSÜ„Í ˆ¨iè»®ô“v?þøc™õ2ñ÷ßoÞQ¸¡WÂD=¹FÝ•3z^ÅÖ×Ö¿m=Í z…þž_Ô:ˆKcõêÕÒ¨Q#yöÙgÍ{^¹@é¹¥„/Wc5xJ@ïT3¨ÆOO’Õ«W—6ÚÈü·lÙ2iÒ¤‰èëh=У°\²¶TmýÛÖÓ Úc¬Zq²ýè£Dg-Z$þóŸã4Á}€ pfXÜÐS[±b…¬[·Î\Ö™ÁüÿôßöÜsO9ì°ÃhC:…ø]²Q·õo[O3…røÛD­ƒªfúÜsÏ™{,XPÕ]¹}FÐ fDhÔ4m'OÛz²¨•Q6¯¨uàŠ†­ÛzÖ©+eüj7jT%êqãÆ™×Æè¥a.$PšAÖFÐl'OÛz²AË9ø¨u¹Á*nhëß¶žuZEànµ¢¦§‰è•Ûo¿=ê.Ü.£h3*ê^xÁ|Ëý“O>ñ+!F šÁ`¤b °û̯€ šA)³™ˆmµ­ç ›º‰Z®hØú·­gºRƯvsupË-·È€äÇ”cŽ9FªU«&/¾ø¢_Á2(4ƒPrf/Û j[ÏA65µ\Ѱõo[Ï:u¥Œ_íæê`Ûm·•%K–˜àêÕ«g¾,rùå—KÛ¶mý ˜ÑÀ „‘2›‰ØQÛzŸÙʰaÃä‹/¾vÚ©T`$¶ß~{ÑwŒ]{íµ‰¿Ùf›Éüùóe»í¶K¤=߉Z®â¶õo[Ï:ý@êtæÌ™Ò¾}{Y±bE™r¬]»¶¹tüÃ?¸*S¶›a4ƒ!uÛ j[ïû {ï½÷Ê9çœc.å–1cÆÈ 7Ü ]»võÆ þöÛoæR–¯KÔ:p¿­ÛzÖi2f0„:=ñÄeÚ´i&áš5kšÿªW¯.-Z´ã?^zôèáªLÙn† Ð fX|„Ômƒ¨m½ïƒì7ß|#o¼ñ†,X° T®f͚ɮ»î*52fPï+Ò›ËçÎk }ÿX¯^½Ìö›o¾¹™]|â‰'äóÏ?—±cÇÊSO=%~ø¡Ô¨QCž{î9sJg\t&rüøñòÓO?É¥—^*}ûö5mèþ÷ÝwŸ(K½iôèÑf€j×®4iÒD&Mš$'N”6ÚH.¹äY»v­Y?jÔ(9ᄼ(³¨uà*X[ÿ¶õ¬ÓìÔéÖ[omfÿô]‚§œrŠ9æöØcW¥ÉvIÀ d!MÀ6ˆÚÖû>Èê¥!}§Ø?ÿùOsß: N>ùdÑ'Õ öéÓG–/_.:‹øÝwßÉA$S§N5_'hР¹×HÿS£wñÅË| »ì²‹}ôÑÒ³gOÑWX¨üûßÿ.úÓ… š}?ýôS™7ožôïß_fÏžmLc‡Ì Å…^(§žzª,]ºÔÜØ^§Nc õ³XGq„è;Òn½õV™0a‚õµ\këß¶žuš:Õc¼qãÆ®J‘í’@…hYA° ¢¶õ! ²zŸÐ²eËÌ hu–N/Ç®[·®Ô êÀñàƒŠÎêrÙe—ã6xð`cgÍšeÞOöòË/KïÞ½åã?6Ûé妽öÚ˘I5ƒ/¼ðBi:+¡3ƒÏ>û¬ì¾ûîrÅW˜}žyæs‰zƌƪñÌ}«eË–²Ûn»™ývÞyg¯ê*j¸ ÚÖ¿m=ë”uêª6Ù. pf5<Û j[ {öÙgËá‡.‹-’ýöÛÏ\æÕwåfs—yõ±.j;vì(7Ýt“1ƒo¿ý¶¹¤¬7¦ëå_ý[½”¬3„úoju0÷ J›6m䤓N23ŒsæÌ1æR—_ýU6lhÚP3غuëÒ{˜t–ðšk®‘)S¦HýúõMÿj}X¢Ö«XmýÛÖ³NY§®j“í’Í k x¶AÔ¶>„AVïå;òÈ#EM¡^vÕK¶C† )5ƒjèôKz¹üR3øÊ+¯ÈhšPó©÷ÿéÌ ^þÕËËå5ƒÇwœtïÞýët¿3Ï<Ó\¾öáÁ’¨uàꀰõo[Ï:ýÿfuêªJÙn– ð2q–ÕÈÝ6ˆÚÖ‡2Èê½|:릗kõ‘|3¨¦mÕªU2nÜ8cu›3Î8Cš6mZ¥™A}:YŸTþòË/Ô{upèС沰~ Aï;Ô‡CÔ˜æ›ÁÕ«W›{Õ”êëitSÍ©ÞÃH3(æá›’’’J8ÛzÖéÿ7ƒ¬S€7SðŽÍ w’0 ª° ¢¶õ¡ ²jªôE´j²ô2m¾Ô§‰õ^À×_ݘA}5…š:}Z8êÌ =}EŸ Ö÷›é}€çwžÁ3|øpóÄðúõëÍ=wß}·l³Í6eÌ n§—®u[aã76æUtña‰Z®bµõo[Ï:ýÝ ²N]U(ÛÍ:šÁ¬W@àùÛQÛzŸÙÀ¥ñ*ü¨uà*h[ÿ¶õ¬SWÊøÕnÔ:ð+jFƒ@€fAÅ ç`;yÚÖsÍFñD­W4lýÛÖ³N])ãW»QëÀ¯¨ šA3œƒíäi[ÏA6ŵ\Ѱõo[Ï:u¥Œ_íF­¿¢f4hTÌp¶“§m=ÙlOÔ:pEÃÖ¿m=ëÔ•2~µµüŠšÑ  DP1Ã9èÉÓ¶lè)N²6zë‹=ȲN1êÈuÅ®S×ù±} Ð ú« #K€@Ô“kÔí‰M€ïúF/êvE@Ì. @}€È&b Œ…;…B êÉ5êv¡äÍ8Ëð]ߨñEÝŽú‡I€ú†©BÔ4ƒ*2‡J D=¹FÝŽ¨Ã$໾Q㋺]˜*1jêË(šÁb‘g¿©ˆzrº]*A³“Ä ø®oÔø¢n—8@6˜ ê› fvRšA–4¨'רÛAÃNÎw}£Æu;`)¡S£¾ÐòzÍ ×ò0¸B D=¹FÝ®Ðx¸qø®oÔø¢nWÊìµPÔ·P‚Ü?.šÁ¸ä¸_¢ž\£nDÒ ò|×7j|Q·c „I€ú†©BÔ4ƒ*2‡J D=¹FÝŽ¨Ã$໾Q㋺]˜*1jêË(šÁb‘g¿©ˆzrº]*A³“Ä ø®oÔø¢n—8@6˜ ê› fvRšA–4=¹F]¢|©$j[ÜÎ/¾²¬S¿ê¥XÑø^§ÅâÂ~Ý tϘ=@€'áD* D}Qò(@Jè]©/´¼^'G3èµ< .-< §Eº8ý è‹’GqªÀÿ^©¯ÿ¡FH3ˆª,󪞄«„+¸QôEÉ#¸J)`ê›hvó4ƒ, ž„±ËE_”<°«-~vÔ7>;îYšÁÂøqo< ƒYI(ú¢ä]mñ³£¾ñÙqÏÂÐ Æ{ƒàIDHšAl!Á³ãy\`Ó£ôX†–ž„Óc]ŒžPôEÉ£5BŸÔ7•0c¤ÄÔ•YU‘OÂUØæ(ú¢äXù¤.õM 5;*G€f%A|€¾PY”<à .f‚Ô7&8îV0šÁ‚²zæ‚Má 3¬SìÕìê_%¼ iñ4eF$@$@$@$™Àÿý²£ ë·IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-lb-provider-overview.svg0000664000175000017500000007565313553660046026245 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-09-21 18:40:05 +0000Canvas 1Layer 1 Compute NodesLinux Bridge - Provider NetworksOverviewInternetProvider network Controller NodeSQLDatabaseMessageBusNetworkingManagementML2 Plug-inAPIManagement network10.0.0.0/24Interface 1Linux Bridge AgentInterface 1InstanceInterface 2BridgeBridgeFirewall Physical Network InfrastructureProvider networkAggregateMetadata AgentDHCP AgentMetadataProcessDHCP Namespace neutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-flowns2.png0000664000175000017500000031564713553660046026436 0ustar zuulzuul00000000000000‰PNG  IHDR)šûÍÉÅsRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì]|TÅÖŸ¹»›F•ÞÁ*O±€Øìõ=+ê³?%¡("*%ÙèJ6TŸ¢HIx–gûžè{vÅbC"‚"¡wHÝ{ï|ÿs“n6›d7Ù ›äÌï·{ï;sæÌÚ™3gæ ÁŽ`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`Ž€làü3ûŒ@îö=Ü&?PÐ×ãѶÌò¥¯ªð’jD`¸Ï’îÜlïâP•RrDæ´>¦8BSÚ‘"˜3aÂÎPaÙhPW†æš›•¾¤!ðÛÔxt7µ ×&¿>߇îÍâón¡•Ìô[´Ìì”2[äú3焾¡„±±pò«„0“DÒ®¾»w9ýër_[ü õ¢S”Po”èæóHÿ†ºðqGfø{R Y¡£pÉmRÏêÛûç!C†ÑàW×Õ; Óˆ&¥DÑîwwø¦vIóf?| ¤fÒ_¾(ºwúôvÝw_þþçîNßÔnÅzÉ0)ÿãûk¼äìÎI“Úë%²…î}s}÷m©Ž/»Í³07#ccua›â;ªóRÍ‘wOS̼癅”0Jh‹ø¼—Ð5¤øÁ¨)Ê=–X¼yÏ“èá=©ÙÙ¯6¦ŽÁÆ"ƒ|±O õf•!ó4)þ«)Ï¿æøÇ^»ÆŒ_Ä`”EHóù˜º˜Z¢ÔYÂðgÐOˆEËWmOõú_?§_Ÿ¡ÑVBñY¢—ÌBŠƒ…ˤ’3!Ƹ„PIU@! JŒÀP¥D†vx —áWT`>"”º^H}Ç]ÙÙ}ÍÈØ\[KºêBý&ò}„9§ªpìÏÄ#,¤Ä TfŽUœêÍ¢iÛ.tt1Hå“”b—Úÿ´€«n;âéÓ÷š"0ë Ÿ÷ëH9møEÊÈÌ) è!œ4—Rl‚ ø~ß ©:ü>Àü<;$–ʈ ÷€R—!ýuçô;üÄX¦4±*å'¨ÛgàãÏ)Ѧ Ð|Œ]]ŸÌ¥z³3•0›ÍógޝÏt9­¦‡ )1*s,óü­”tfŒR8°daÌ´9ן1"˜‹Ô)SZ©½úS8/7ò ¼?&8L8Ï¿p0°Ã”þ0fóÍ¥&§åL̼ ãó=•´Alî`‡ÅUwÉÃ,ÍK›Œ€ 7 '½bi4hBh܆ºqUšwÒ9þôÿEƒf84°äw+åÏà Ëaº  Õ%2Ǥ‰ %ŽèP;°TrÇß-=I©e\ü%Õ—“RGóçÏwÕ6ÿµG;¥[oÁï)^°_,Ÿ©n`@h¥áJš, ¿ÏwkQ®oüºšø¨ïJt.¥/wÔ”NMï}¾ù ‘òix›‡ºÔ-›F$WØç4 '<Ù‰Ð/œ°Õ…‘R»‡Þ+aÌ>yòAÕ… ç]8|¥fN9š¥ƒÃ¡‹0Ôæ-ím-ˆÇ¢>Œ:µEmù©Eš\LˆÙÕ„ÀßäðIÁ¬åÌð¨)<½ÇrÏ—¸´ÌÉòéX,%—c÷ÄÐÔ ÿÕP%ßn BJ¶‚ú>kýokžÄÌ`c¸Ñ¾GZèûhæ² < µ’–9éTSO Päø3GS€4oÖ°#)„öA(Íb¤zC(HN‘U·¶MôÂÅùÌÇ /%%Eëä¤YŠ‹ö x6ŸCâ×'5su(Î7ç í+ÑÏüåqKp|jã%Bý¬ßGûªd“._Ö„Cß2EG}V+äsÒ*7ÄÕ49Á4Uø]%¤617+ã…`^Ð罎ü÷'ÁË´à÷åøKWÚ¼¬ôí÷Ã}“1tmPýC/Ä÷šÔžËÉÊȵÃ9¯ÕÖG_CueÐeàqÆ/Ã%üžŽ~ù¹œl¯ŸÞ“ž1é¡Ìˆw$õs¨Ë€X®Ãó¡¹ss³Æï¤Å÷µG€5)µÇ®Ú˜è„z£òÈò—ê4ÖtÜ¿ˆÆÞûM4„—Ð\xN5ÅKƒgŽ„BÑCœCƒiÚÏPšYa”ìbû%thE ö–æp¨„¯²ýéJl‰O#ÎÑXJÈq Îp‘ÞôMêšÔp×Ó¤ü#áþ#¼S4Lã=tê—¡³ùÁRkÚJ;- §ø óMîUØóò“ú‰Ø€ò<~ÃÃU@|‚ø•:a¢IµÐ߃¯ÐYÿŽŠÁB>‰ô›fè`Ú)¡½ Hþe´qÚ| Ò“´|÷3f®9T¥AÁ ß óf ²ÃÆðZÚá¨#I#¼+Cœ†¼wÁo¥ ì¶«¥?±*~JëCÉR`ý”ëN nÏÒk a ž!õš`¥6¼WW·¤ò¼Nù@]¹†¦°ªìFNšÔ¼Ü‚7ÝjáúB˜JdÕ)ÛѳÓÑÄ»ÞC˜Ç·þÈÏ*äíU¤³­ë$ÔÉòþ•fÛyì·Qo¦B é_¯ Í[ñ8ÔW¨=:é‡soæ›)˜\Œ½ˆó°û³Ï'ž&¾’=ÛZ =JÛ+Q»KëAY}0x¿”¶'óR; ûJmù¿”ÞƒÆ-¶¿}%-Äkð|h’«ù·¶ÿ°Ìì!P–ƒö5Ho/Ê;ÌÄçÀî(Ó4s†fø_ ¥±­®>G_c§|…@IÂßíH÷§ÎžS§Øïáÿ¨0MZÊF_,?C?òxA&Fø;„4*Õ;._#GÀyŽQg”è¡d‚&\—8gZÔaìÜ[ò:›#7òÈÞãẦEF¨Ã|Ù7t“ö¹#|ÓÏöµœ´û³ï‚À0ˆŒªf#‘¦?—}l@7ri6)…ë®PñÑA¢‹’hÜï¶j•x[$»CL]Ÿ š}0Z¾œÔ¡å ”?Jƒf7u¿Æ„Pi„ø':Ç–˜mÞ-N–ªã»Š¶ìÞ„w­ÜòñY¾Œò7Õ÷P;¥>…°šÛ#N›ãó’&Ër$ðA(|ÉP2'5'ç¨Ü´4$‡z2ÉÆ4ÀA3ÖºU‹„ÉÓÆÛ[]jÑâ=7Ë;éÌ -á˜%?‹º² 7;ãŽêÒ~W¢£ã†0ޏÀÐr¢ó=iX\nÚ¦´ßÕ…÷êêféóQηŠk‘Ú£ûS,½+)41@*üÿÔ„/Å€fÓ ÆÏF]üT$Ëkæ9¶÷óMïÐI (_Û½·„êåù¨óÿ‡z{«]oK5¤ùo Í_ç&„ù7Ñ×)éIy4cܘôÜ8OcÐÎE?rT8üS‘ðU6á’zöÅÊ0ßVŸæfWÔV¢é£[‘ƒƒóÐÄ娷MŽ&áÕy–Ñ¢ïW(k£‹gúF‘–šÛit½ø ЃӮÈõ§¿jÓ%¡2oÒDïoBß2þ>û}­®>ØaB]Ó2ýÓQß±l-võô¹Öç¬S8J³$߸ƒ„õfî”>øÆ”—1-õíÝUÜvL4Yb%Ê%ý(Ñc2á" ål§€BѬŽEÊÿÒ½)Åñt†›ëËø=¹­(ùÑ$U::ÆÉhlaCr{Äé(Ù jÒ¯¿ÐQn èæ µXs'Z¥!v$ ÌfF×E"  ÷N%ÍÔ€’Ü­n³;zâfâÝ^/:•ÕôìÐYL~Rxþã|G4€Ákèp]®Îv¾za:ušˆõ’S@¡0ÈÛË U¾ê£­Û:¤B¼(?Ìõ§¿‰v¼òkuõ¡ Õi¢tÉ›s}÷n‹˜®T.Ä!µyéOÉèdÛ€oÒÎdè%×Uk˜ªibj„(á;Ø4`éTèò¿öLË›|ÑéG÷èü E–­¨3ëìnÿgå÷bå§d°áá%äï’ÚÁqè6ŸÑ‡«M×X:¬—ÏqKÏ1Àì)ôâ‰(ÇTeV@XY”êË%ÐÆ ïÀe1aƒå¸9d[Nu㽊º[”—\EK3´Üèäƒl­ÀáI˜æ¯“åýØù.Ô½ÐOG$¢¾¿¼¤^7g¡ÞÒòç‡s|Jëš#PW×øexGá¡w?üp²ãUD·ÒãN%ÞbøðÌI§×9V|išx—Ò†¶å,›ZbV§¢Á/NH–B¨í«Ëì÷t• ¶bp¦æ*Ræò.wŽu ú›™ž¾ؽLø3ƒ^—>VQœaÁ `³láFBã<8~…‰Ð%¹¾´g¸â”Ðì³ç§fúGUÛÇ9#ò}­ …Ý@ !É\*YSOØ.D1Ú¦Hõ¾.~X»¾µ°P­À˜ #= Üš|ªIœ¹Q+÷T÷G8c–žl¹dNpÌ€QÙŒ÷V¬º øœP[V4wùš³“Fu÷@zAâ@ùkUá0ˆ¯‡PRá5 CÌ?"-ÏØ@cm ž¯¤0Âà °ÔQçƒCÒz!ªi(c;Z~ ræ¡äõh pÕ;šUB_a`¤ IÚG4#¬>véÛÙþq?ãîXJoŠoow"¯gË€ø Ë1·C3õ…Œ6數‡þ'0õì+‚ߺ•¶eNVú§ä/{´T­Ûzðÿl ~–¯kš–;çÁôwƒÕhð^]ÝÂ@ùjÇdC”Ö$Ýæ[×õ¬{%žæÉã¼å¶aÚ*§È{UfG¦D;äýÿ‚ÃlÐýT‡°æ¥ ÷R"û†ˆíð}œ[¢G}Ï1ŒCZ†Ð.V|a<Ü«LABÊã”xQ‘øš%š˜x› ðù5¼O"á…„<Ú±ƒe×SÃö.$´aÕ–ú“ úâž$Àå<0îOäH…pÔàÔ–R#úJï««ŽÀXþ>ýQÔ‘Ýží™é¥KNŽ0ài°;´ËÖ[ÊT¢¸}Ë<ô­OÔ$¬:éð}ø`VÊî@ pP3€(;tüh_U;jD­-ð =%IZjתcDö†0-‹tuŸv9ÍD P Zôý¯·†¢ÒN”ÚÅ„zW•æ^]­wÒÞ]R9$ÒÝ\Ù9Ö¤5€C昈r8©†Ñ¹œ6Ô›ýÞöÃPúy'÷É ì¸Ÿþ´¶3Rk%Ïd¡~‡aVµKI³ªw¦a^‡sF^þaM½’àR=%!h)Z‡D÷”~.øÄ–LsŽ­îŽ6ïÕñóÒK/iÁy¢gÃ4°ã‘½NWOÆRÓ®‡€Hq…a˜ `ƒñSjfößíptïÕÕ­ç”5Á\oiØÊ—R¤ÂÌàßå^ÕÜ õ´^Ëý»[ª Žò) K‚MèzDÆ®ET—0øÕir‚Ý„³1¨ Œ{çïÈx xÜÈ.µz¼q¸XñÕYëò)R*œg’­˜•¶R—ÓÕcŠWJYÐ^#¡¥°H\JÏÅÛöž9# ñÙFÔÛÅ—Æ Áeku‚£&ÌMM‰Ýg!\uõÁ`”aia5àÒ*Ph^o¿ ¾Îõg~$ÜìÚÔæ 0¸WÙ8P¢ÿiœ0Þ98r,)7òh£¾ð”$Û³ujL!ô#ÝB¾€' e¨À Ô•Ô`|* a Áö¾[ªŠS7LH,§ëFgläïwzÒ¤ª2ŸÈ“¥m!#”ý1Kï0§£íÞ:f@c4!¯Å~dz]ɲΖ¸6Øð‘Ôäèïÿ^Íé#sÁ´"}Æà´˜´Á¿¹^o^¤´‚Ãϥä|ºÐ5Ú¼ͪ ŠÁy¢ç¹Yó«ŠCÿ<¿÷hVNÅ@©cUf]_¼?E¼™B£]>_}¶fÍè -ò«ÉA ùÍ #e¯Ã*µª4lÍßýª‰V8ïi€Ô„æCpáÜ 'µ@r…ôF1åë]âÕPæPÎCC+©ý׿VÖøÁÁj´ÇAØçÁdf0½“I´TTêJ[ù ÚtùÚþÁW䣗姪^ŽSùY]=§^”“•¾kR#‘f‚4ù5G›°îñ.î>}!%½ ÌÛã㦾ÊôÙ§¶°R[äê9^éábr/O›* %Á‘õ —œmСCÌŽ‡Ú`R•àö܆FµÃTæÌR£ÁÑêài–v:Bû®D*F5…E ÒÏåd»PñeÙSYgüŽÎWf&üga ò#°ÂjÛª¤5øã+¸©ÁôâìùàR~$:~ÛÅ?ï¹Yé_@»„óBPs…ÑÛæCULqOìØŠ l5¯$áõéj»“Û¦üo?ÕßA€Ý9óêPgt”¾+ûw»¾€0†•u%ma­ð.Fg÷ëýò¸ BöÑùzþ(I*%UK¾ X-”]•‚ƒÛS*l@€8A/ ðnᮀ/ lq¦ vîämåï@”Á•Û£²4†mEGÛ~¡9¾ÊÂ×£}"HX^µ¡Ù.Õ¤BÀ~Ø=ƒþ²—‘¯?Ú”àve”†¥ïi±‹,¤D Éú¡C†“4=± =I½ß%Ôç9ýì{„ Õï½è¨Vã\’»ÉŸÔÞØµ2œ:‰@Àx>Gt]šyà, {&:Ž=š[³!zWW‡:N²ÖÛ{¼¿|Õ„`zü“.AÇra°?=à ¡{©¿<޶îŽðÍjNƒKuÇê»Ü®t¤WŽC½“þŠ.PE´B½‹†iqh§FUB_‚²ý:øÒð™f<ðnóBx‡ª_4ƒ†Æë0 'Ýò';|¬y·¶+IZÅv«Æb¸íˆ´_¬ÖÈÔf®ì:'kÂ2Ôodq–ÎÖÇ,m€# ÙbØõ"Ç—þô'0ª·.)0ž5;§øtnˆƒDnÉX]“žÛP/¨¿é¡ˆÕ–/,‰¬%z{N§s^BÑžãËX|6AŸy<¤£3ÐŽ~ þ*:,÷_¥¸˜<ÜKšh:˵(6Mœ×˜ƒãg,·-w´Fò=ź­ƒK¤eqM¸é×.'küò¡™þ©0Ôò–†EjA®6|Í~`üZäõK ôü¯Ñ~žD?”ïæ÷¤ÜŸ¾Ö­JOIFÊYûýKï:¹&,Í3³7Û“+´ãr{;,£“–9éR´÷•n.Az BRòKÎE8–„·;¡,v̺]©ŸÄ!˜CÌ€úÚ²)àá3Z 2KÌÞ8z òü1v*-C¿³)u†žìœ­B³˜úΨ[êÛ‰kRœhÔpF‰]⸪~`À;G(í@C m¶×ÀôŸh@÷#ÅCa¨xUWO×»ñíf¿CÇø/tÂtø¤àÉ(¾S33†õèLÆÍœtæþ˜5Ý©f {‰ý£Áit¢ÁÆ ‘"«>Ì­&ÒÕ¼§<`v5Àb¤7†w~d稣pÂí58˜´ƒÜF}ñ9èкù]‚ßÛô¯b ÃÆ3!¾¬1¡óI4éHé!܉¨£©ÓÇõàzÁÿ)Õ¬lwAPšÑx¤Y+xü/ä0Øö¨ó­tM5xß ~h€AóhýÀg¥YèæÝÎ?ê&©ù÷–ò¬²1ˆ?F÷ðk…²z ‹»ÝWp±æ}nÖ„oí4¢óK~¦¥§ „ñ@‡Ð5s7?ñŸÁrŽ·7‡[‡€)ŒKõ±l2Ö6óîNñÎ@{Ãî"ñ°€1>¹ ÕJh6£>Áéêê“Eƒ¸ÍGðµ6|¡Î+œÙsÊîK”㡨“ÙP‡Ì€Ï1NúÈß{ö³”žJy#›/ÖD<¢|b‡w^I8ÀyN—",Nª%¡GMm2ÖîŠx3ºxºö·OÑvÆ«ë=‚‰¬b›¿ÀÌ|‘&|.áY‡òz ´O±ÊÚT³J…@E»ÿ>v¹åÙ¹¾Œ*'²uå©)ÆG¹³kˆÜ9iRû@¡ìÖ²¹¶:Ü#°b>«ã™–jô{O)ëœ Ž H:š¹èÐr›yR&ŸIáI¥‹ŽëøòužLƒžiÉåãŸVS§œ¢ ÷–3ú¼9Ô0¡âÖÕÒ^òë¯íв-ÓÞž—“ž¾‰ŒphHÞmþ¨“×KDGÍÉÒåÊ›õÀ}›Ãá?x·óPÝuD†¿»áÑZtVÚÛh« o-ùˆCLåÚÓª…Ú/m8R¾È ;»aßΗ¸«Â#\ê÷Šqêk²GäÈsI¨ï17ïé¤{D{·ÐöDÛõÁ¿…›'W=,¤T¿m@HÁŒOŸäñôšé·>T–hm=?°>~÷+fñQ³ •û1Œ#À„‡/÷„‡‡j @=L&+}¡pØY•€BY+Ò÷[šElydÇ0Œ#°ÅÀLÄ ZN€ºð,÷БäƒÏ[ -¨ôEc|Íx>–ƒR“óbÅ ÓeF€ˆ ^î‰ /Ý°Žª.4?† bÅ%u0úû¶%maìÖ†x-à· †·wZ_7n€yd–F€hŒ°ÒK•óT :Sa£¾ô2&ø"­Ân'Ñ Â žn‡}–ªîmß(;f¾R\ö`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€`F€8àð9)¼.C3ü7¢ÊÍöž¯¹ e7è¿ÐgÜ/Á—øp.ʹYÞ Ö¹)%ãM©.Ù´É8˜ö=Õ½Ã}"oÛi]´S>õùë±ÎÓPoÖbMÊçr²¼scVc¦oÕC©îŽýÃù`aMXø|O%mÐ×wÅWyó|¾[‹j ë÷Ѫ'ñ–¯hâ–š9éDáJø-×wï¶hÒ¥ oÙÝ-^êB4óÖPh¹ £ñÌç°ÌI§¦±Ÿl_‚ðôP%:Ò9Ðb@ï_›¼ø|J«é˪µ¡[—8ÈgŒv\$4R3ü?ã@µ#ª‹ƒƒÖ¾ÂGþV&Üwõ_|8Uv´ÐÄ£Â?IM좸KÆâ«>™øêñãÈÇ·8Э@ËÛv–a˜ 6ŠÏÎA÷ÃM#T¸T¯ÿ=`Ct*9MŠQ9þÌ™¨ôiû:¥S‰x =꣞×Ä~¨vP›zHé ÷ùOÒj©[s1'+ýÓ4ï´Þ¦(~8/w1^K*:ÚíO¨ËZ·ðÜU_*™¿:Ö“xÈWMåZ—÷4á@{þX¨ÂÙ 3¦.´ì¸ø„Æy˜ÔL‚€r Nªv£N(Ô…W]Éâι^ožޝ±G€…”hb¬Ä©i™þ 9+šdÓ¼Y·åéÙ'ƒæíѤ{@hIu—Ðd ;miªl|°P¸„ßöÓLµÓ¾¯ëÍõBªwr³2'ѺßêùBå=¶?} ¹À,ð&h-¾²ýêt•âgä53˜†2ÕwÁ~ ê9Fõ¼& ¢Ý ]Þ„‰ÅÚÙ',nÑÜÕbçÞ’…àÁ¡gœË->U†ìa s ²K}(ÑÎá9vêÔž¯šÊµ®ïIë94Ó?Fž%u¥EñGø²…ðú&&0sQît¹åfÃ0.Je…òeL>N?ºvõ )ÑDYŠùp'Èð¿>;Ûûg´H£5ü´¶F‹Þ¤ É»Îô¡q ™ÏÞy½ÿuúGãžf¥t7hRæT¢'UtB¯:ýgøî&-K¶Ó¯Ž÷Ûb‘¯:òT÷è1ªç51Ív@Ë€yú/×@›5“|dòt BkšvsNVÆ3e¼|ŽëüšøŠÖûhæÏæi÷^ã€çËæ%–×yY^Ò¢DÅÍöe|7Ì7½Û\ß}[ƒv¥>¥‘yǃ“é`«ïø6†°Ep=nwF `1 Š—„CëÍç@³œ¹ü8tR[Ñ_¾õÏtZ ·>ŒW æAÝx:Ó]h$¥K Jf!ܩ߃þåÎt°œ²³ÃDt´g8¥}¤ó¢ÚæyþŒQ>Õ—“¢Œ­YÒçAÓÐ ù*„ŸßÙ>ݹ¬„4?Ç»{5(1â~¤9ŸÎž’“í-×|THß›u(µà÷Ö¼lïDç»Hî‘›Á×y]ÜÞóŒìÏuè Ú'vhÕ¡íŽ;Ù7"Ÿ7%ŽÄ²M3àó ¾`œ3Ñû¥ƒøW粇â«+bTµˆ/6k.1Ë4U*0mޏ·Áÿ¤³wž?ó´á™“ûë¦ñ¤Ë#¯ŸëËøè+ÇJ©sÅaHo»Ú¹þ ÒšEÕ•§UE¹ ÷N=\Wù(ÿûœßÐûýW Cܯir:Êþ9›©aÞ¬AøÂó£é¹v¶Ü϶]®µªç™“Δ¦™‰úÓxP&_»EÂx'OU•¹‘$û¹ ŬPí˧/Q^°$cŽ˜2¥5hLý3áu4 ?¸¥v?-åçw£¾3cÑuèYë2÷ÑUºÔÊà°¡ž‡†‘Ÿ¾YÍKÐ^&¡½ ýíwÃ2³o1Lu7ÚÈ1Õµs;hS:}­šáÔW ÛéWZ¶LœøÐ}÷åï§ta¾¨¾˜èÐÖúR~¿Héº+'+})Q¦ú)[§¢ÏCv~Ë]Ró:1¦rD¹H6÷¤Š½úÈ6üb€—?häçú3? Z䬉D m™ÂUU7¨?˜9jT1õSJÊgœÂJMm‰èVå‚;ØbºÑ -Ùöàkì@ûa-4¡`  ›‹‹S3³ÿ^Ý´ ÿ5ô Cpap -ÃÐ<=ï-Š ½s üŸAg° ßiRóÓ þ%´?à÷×»}·¡°ä†û²iŒÎüÔ´û'—ÛtÜé{¬%Ò¹RSêw G³Hغ6èåÓHã&Øj¼ 5æØ_¦0÷мÊ«W#ìMs½ãx_~K-†÷à±»«§Ï”òµ¹‘²x>%OŸ4‚Ôè ™±Ô!‘…Nrò™Þ²É8„u)Cüw„wê‘”œ&ÕV”ÅÓt¸ïº À@@™‰0ÛÑ©þƒšŽßGäß?)˜iÍJ;g•BÏäÈpòR©Ûñø*h¦B@™‹ô£¦)³Â_8åÒÑ=öWÔ‡îø]ddz®†¸åÓex½ÓߤpÐ êwpÔf~‘Öóaþë¥2>ÀÀµuü.ü`È,[ë¢äë´ÌI'—ó[E™'iÚ¾ªÚå¡ëûŒPŸ Ô/êÃ?!„t1LsáÈ O;œ}U€ 2æoä×Ù“HnF=÷Zen q 7?.a «ªŸ©‰¶ÁdPÛ— Õ¶óòxJáã˜Æ$)ÔÿPöÃáÿ4?cöî*žV&ÄM$ùJË̾õ…l¤LLXRÃR¨üAºäF"š“ãA¿AK*×ÃÆëMÔ·{€qŠnš¦ÞŸ=˜ÂXå/û£<(» (Éh¯í L^îðfl §-Û4«êÊhý`wÜO7¢>ÎŽVí‚b?ô[æN·¢Ú€ü2ª¸£J­‰+ÑK’Ñé½­Åõ°;˜‘ê{èݪ¬ÍK­Æ÷LG#³øËÊ {ëÒ_c y³¦‹f¥§¿ ÿù˜%LÄà¿fnVÆ|âÔÌ)‹”ijùzÑð³–-°Ž :òkt‰ègh‰è _lî=!¥yH€ô_G"ÌQ˜Éå˜É¾2Ì;é+CoPÚ9~+m ޾Pd¸\ò‚¹½´fÒ Ÿ<ù #__ˆŽds«Ö‰—ûîR2`žöz¡Sî×Åsø©>_Ez˜žè$áäÓ€*ùI—óáÿ3fkQ§*ÖmyØ}ëĎ⡌ž…MÊ÷ÁþNšt_¼uÏp €qIwÿ¹Y¾ ~ÍçpÊúÛ¨‚§AvÚw?üprþŽüóý“ð»™fð³}#-Íü£¬?2dƇè¸Hêù½Ó§7Û³«x:ÊàÅy~o¹àcÇg7è‹›¦9õý$ ŠÈêZÕe²”çHaç–&ÞÎÍʘ`û¥ùü‹Ì€úJòJøÑŒÞrh—íD èB—T#m?,a×0•}fñ–=¿BÃöX’§eîLߨ=vºFšgܪîÑηã]õù“ª'ÅÕ«,,‘zÚ…V*nÄ}y>è…Ó…›/KëØ: åô4„WÚåZÿ²é©õ[¡™TǺ=Úq´$RæÿÄP¯ÿwHøSñ\>1BŸ‘æÛ9þŒt;~iyˆ¯B’ö·¼ÙE ¬²XxJ·g8úÜd¡>Rí’m{OCçܳû§œaT²Í ºižêô¾ÏÍÿ=¤úMRƒìwHó2Ìš?Dä¡%!!¥Ô™&*±‰â”z¨«0+xß! XÞsýé˜Éß”0¯-‹i]0˜¿?wbFHi*¸Œ|ãM¤ oÊÅÕªŸ„ùWÒ, „ŠVš¹3¸®¡Þ×Öeth~T+š »+ýh éÂ+”vÉ~¶mߎ¢sA.AxÜÙ(OÀÜI‚šH2¥ž§ð!“«¥g$õ|ïÞ’S0¸t†ôAš°rGÆŽÈ ÌÕÀN)ŸY[Â,órbe7nåyÆé7÷Œe¨÷Û èåô—záuÐêfsO¹ÐOï­¥%ÏÄûøš^سe7(;~­òcG®ÃÜÛÅ¢„¶‡6©ZØõ *òáäKé[¬rB6õŃéÁ÷: Òß9”R>iƒEÿ` ”r»Â*àtªmËa×ðÚRpÚU=ïÛ^0›ÚVr2´BìêÖ¤Äî\ßøuЈ¤C¥û®Ïc»é«á£÷³Öç¡{ÓÓ0«Ÿlû«B¬c}A{Ú~U]1ëyÔ zŸê›ÒCǹ¤¸g¸4©U÷g®¥Ù4TÛåÞ ~þWÝ_Aó° ïd5»Q”ÌÇÌsóS4éºøߘâÖñÁí9Èž±U MÒÑX±¹I™âptÝð²9øn ôÐÏFÏá •CP ¯×†"ÊñwÈmå3F›Fó–ZH‚pËÅår-2M]ÚZ4T,ˆ¥óPï0¨~ÚCêe~ê ær»ÞµÓ¾bÉq ü-÷—Úû˜aVU7ʃÑM8õš ‹¶Ë“¸ºBdÔqUÖuìÍÐÇ,söUÖ½&ÇèîV=InuòNyDÚ–#ªa´%'¿¡î±¤6ýÊX)µ«Ê½PÁØ/†°&%FàÒZðÈÌ)·—¨À7}×$ ¦E¨ìå©iJ[9¤nm):ŽoÊ_DpóEF¡’ûô¢Ó@ûR#o’H`¶óÌßÎËW…¿£SÖ<ž„ýBŠTË0C ­©¡­¹Øé.¤õIq7?­@ßGÆßîÚ[’‹+Ô±qÖ ²UÓ 8}–ë÷^e§bÙÈÂVÎl¯¨\¡¡X ¬zD…XMD"+—Eàí :Ÿ²í ¬y·ô¼ÛœÉEöñx)x¹S€ ûƒaA^=ÖTÏ17_›VaXî¬@\/ÃUÓ~«àÃS7nBÙŒS…±TR³kîJùÏ>3– ˜!ôÊHòc”6H-Hè·R•² &.Öíø ÎÚñZâ¦XP]ߊ'²«‘—¹þŒ§C½Ô/¦m9²¶’uP£±lîÇ„qH$ö+!‰±g­`MJ­¡«9⬬ñ?BM;ãøH tý*Äp{¾D÷•¯ ÆhÕ;,ëlÄÐKªÔ Î:ù†iÂ8 =áé°¢/WÓ»0£¦AL*ËåûÙ¾±´C¨Ô)ùèg ì¶®¤vE¿y L>pxW{‹|):_„vI`F< ñ¯Å6ÆóT-Ñj^n ¬Â"+ñF…`FѵH;êB7pZ óü`¬*¤­‡ÊemiÑÀÛe¨GßYËzà£l=µÒÉe)d•K=Ñb»ºzžÔ¾Ù Șë7TJO™7@Øü³³8¬|‰ R‡GUíÀ¤Ú[Ò.aEšê[˜¯6^…¥0¦KüA×HòCÆËÀÚ=sŵ]©öË<Ï~¶¯uÍŸM'œkp¾d‚ø¼ºY¹œÊ¢-ЭsCí– 'Íà01mË´¥`¾èK<áA›JJ®?ýÕPaد~ˆz§^?l7œT:»údãШ«¡’‡1£,טÐ>ü4ovŒO&C¥X‚sHžÇû¥›'@ñq"l˜eºb–ó$ú)Ø}s‰Ù\û´YQ‚k¿í‡¶g'Ð7tÌ”Ö)åR’§Ù[Ðn$CP¹ ³é§ˆa÷Í?±ãâR¥.@ڣݞ„Õ¦8Æ4ôÙè¨Vvvú/gøpï±³âÐ;vBàùÜæ?Üøá„kïÉø´mr3ÒZì’ž­87ëÅâ&ð¾=ÚsS`“­ëÅ·ÀúÝ´û³Çi¦{ެ8yì -D•†Ñáä%8L$åbº?À*”†üÞ::ia~ þáS)žä°N'H﫪ç´e|hfö­Xzz嵟— ‘`ÊÀí8¯æJ,mÞŽa4ñS};¨™ã椳!àvõ¸+/õX³fëܹX*×zåÒÖr°¡Ì ^ýвEâJ¡ùY„r¸ˆèk.׫8ŶN‡özP0ÇuÍ_0=z7_4áÁÀü8&c`§´Gz°|^Ñ? D•Ž7÷dË}qÈÍûØ8z»åJ sØì˜.LT|¡x¨Ê/–m9’¶Ìm17”˜…ò˜É_Jðqtw±f%µØ=³&%vØZ”­XÉÛÑÅV?±=o*¦v·a,¹Â¨/Œ€ù=¿'¡î/•†Ùg©s»þƒ0¯Áæc¾ÚØ•(½R§iê= Ðd¸¶Àië@Ú ÌÌ>¡w.Yq6M»o’S$6·–èÅqžÄ«˜ñ~åi¦î,³Œ…ŠOû;Aów,¿HÛ5+¾¬û-/ “¸`þjèªd%®×Ãh÷jðÿvÝS¨H4Pت=åWhêJå0; Ž­²îO‘” mmÇ@‡o‰6Rs[K=6.éz­¬N|UvŠ®ý*f×êêù¼¬Œÿ@pŽ&pm±’¿•b(®.y•ó๚˜«®Ô—ÞcËþM(ÇÁ»Sè6©íÆÌù&ìùyûw¤ÞG;¼oÞÁ²êÎk‘äqïB]] !í# ¯…Áó§è ¶jÂu+¥ëtuÍŸ“–}I¾ppâXH‚™àw õEè>…ð‘Vz~ŒŠah¬%É@òG¥ÌçLU¼ ÚË…h d|±‹e[ޤ-3nJq6òèÔ5¨3ÏWú)k»up4~ŽèçØÅ´0_Ï?¨u‹„-U}'„ŒRó÷ÚÌÊöÒGªç­ìîgÓg}4ÏÒˆ s5!£àâÄ"9gÂ„Š¶5Ä«íkúŠìÖ@^ÇÇýë°LQIè¬-ÝPñr¹„Êí7rÒ¤¶07ëRf±h6tÞ,mM·Q8'#cCMån~Èx^Ów´oçöþAƒ³^¨k,òi¾îðMí"Eò¾àsbl~çÏŸïZ´â—^"Y+8§wï-uí;bÙ–k[²Ë‚¯Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ#À0Œ@uÈê^FëÝðÌÉý Ó8ôz*©º ÑÑ¢ÍtF€`º" „Ø+•Ü:¸4×Â9Y–Õ•&ǯ;1R|¾§’òyw©îJt“R˜BÉÍR6 Ñ}ug)0Œ#À0ÑABJs)]0fuTJhBŠõ³íêéú¸ÏwkQtRa*‘"!%Õ›u™‡‚î!¥\€ßóZŠöÖœ vFÊ ‡gF€`ê á“'d˜+¥®ÇïL°×!í¡¹þÌwë‹Ng?QRR3ý£•)’Rý©tTN¶÷“ýÉñ#À0Œ#Ð0HËðŸa 1ÜþEjâÞÜ,ÁyãáÒͬ” (ê)ä…§Ã¥¹YcVG“>ÓbF€`ê eŸ~ðÇ€³¯yFš‡C«2fÀ ³w/ûäƒÏë+}NG`Õ-JŽ–x”’oƒàÿrü×`‰K|ìF€`†™æÍ~ƒÚÚ.⥟ú+O-I‘‘,èÌ£%ái (Ñ@•i0Œ#ÀÄÖ˜†± ¼üˆß¼²1/XkôGdF‰kRe±6½L:µE¾ž¿@(õ¨bgjÒu±Û#OVRfáLƒ½š’»*yºÿ;}S»Å"_¦¡æÞ;}z³XÐfšŒ#À„‹kRÂEŠÃÅ-tDõ†½yo‚Áînéé?Û?îg³ô1°GÏâvXæäã S‰€‚Oz½ÃÀ ÷î.ÊX£`œ F€h°Ò ‹™v"gn Êš¦¥ÍΪ  8ƒU¸š9éLiš™X êMKÚ—¯Ý"a|€#`'ñ9Ôã•Ôz˜¦‰p‡#üwR“ÿ¤åŽÔÌì¿ e¦‚xü6 )g;?çŽå¦›•ç7w§ÜMÏL¤w¶ì{ös—Ç5z®oÂ︷\ª7û>!Ì‹ññ²Ae^å—¡þ/ðm¬§ñnÎ0oÖPSééeq ðx €æ¶ÜlïYv„¡Þ¬s ldà3ŸÇ!Í­ˆûFw·tŸïÖ";LUWMªßM%³!¨<š9éÿr³Ò¿¨*¬íŸêËIQÆÖ,,>|zŸU8ôj~gwúôà%)`v»Ræ0„é¾VÓðXbÓr^‰®lŠ÷ç!\G|u¹KjÞ9YéŸ:Ãñ=#À4Nx¹§q–kÓÊ•¡NBž}tï'ÂÉø° ÿõR`Þ‰p~^ è­uQòuZ椓ƒhüÅâ!S™£0xÏ•š÷-!°ÌOófÍP¦zƒñ¤ÔFbÐ]çGÒ¼“®(§!e;)ÔÅûôüOH€mÌh¡‰‰8 êx3`|=Â7­SyXav†ÍoßýÏûï0˜…'²Û†t­­ùÖ[©Ó¤æ‡5ÃzÆ_Z†ÿ¼_ „riHù{ÞCóô¼°\4…L‘=ÚO†`ð³0Íù|ólÚ¡®ô^¶.¦¸BÚÓHó&àô&l[È d¿ìŒ¡m¬2ÍyÀû;Í%noó`Ts-Ó9ÃÑ}jNŽt—àöz`ö&0@8‘¢›æ‡©÷gÏÏŒ#Ðø`MJã+Ó&—#̰BàX3dÈÈÕ;²³Ø³«xºòÅy~ïßíÐ>߇ÏnÐ/6Msד x`Œ.uRÉCÜžƒzÌö´¶ÚßáÍZX¬ÄSŠáIÏ¡3}ãÖSÈ;{ìÅâ-»w(a\‰Çÿ•Ɔ¨ DK ÜÿÍñ{°ý†ù&¿ !e•®ß ?ú…íæe¥ŒÝ0…©F%“ y@IDATºÞ°Ó'à!±xËžéÌßœçϼ¬ŒèÓ¨¾F¾ž„uQŽ?ýíêƒP•œ›–îóßnèjI^` f«Š³Aÿu$ QÐDåÐD½2Ì;é+CoØi¦N™ÒJí Lg€ýí6½Ñ¾G^Êä¯C9VpjýÖ¡@ïX·G;n¶/㻲—O õú‡ð4Ï+DàF€ht`"ÃŽhàHq4)ÃÉÅÞ½%§(¡:cÜÞç¬C‹1 ƒâÀN9ØùËïØ ù?îÏ\‹ô~Çïs§€0sÔ¨bÐXé¦{…øxДç§-ó ÜÛÐx\èô¯ë}ɶ½§Aé-ÅSNZ*Yƒf¹“æ©Nÿ*î­~aŽÏû¹Ú,ä?#Í7)¤†§4¾º ù~ß! XÞsýéÐ~Èß”0¯%¹icוKºrKã•þÏðݽ Ë8o:ýèbâu¤qq(VàNyéO™åÁŒ#Ðh`MJ£-Ú¦“1 íèVŽ•:”¹<‰«ƒÃKM[¥LCº~Þ­q¼ÏsÜ[·0ö@ã@§NVpÐìGË žxHjãYì§iâWÓçû×åv6‡P|dcìR&Û´T!qð½huzÚ~á\=îÖ麾ãoJ7°ì£NÛ)&UŠš‡B)×ø¢á ùÄ(MÛå_EÞþD€ åz’däã'ç amð¬;òI¬TŽÎ°|Ï0 Rvù1÷@ÙL»ûÏŸ?ßÎ’&õ@%ð\*» £Ø 3t À…•–zèUøWòvF%­%´Í è¸1è"‰ýËK•"“ÛÖ„‚™R6£õ%#[•|òs:CȵÎçšîIƒ4ôþìaÂ0ßÞ¨ûGj‰îçJåh Ø…rJÀ߯SK¢¸f’QT™"‚JÁ³ ÷+Ê>_ñ=I¡'É­üù`,¤4º"mzÒ„Zb(qó{ßÿz3rÿdu˜ÂµL]˜²Âí¬V·ü0G×~«à…‡‚|½3ÈTœõ+u&”:˜¹ˆJFª©¾‡Ú©@¡'h É•¦´ÕÐ áÖ–Îõe|2P„žó&f¼ƒD/€×IF±z¯Rt©–AÎêYÉŸ<¤êk•u+ä !d‘Ù—Ýäg;`Љt-N;£ÕT çú3žvúó=#À4*ÍîšNÖ9§³{‡§1ô­ÆVàÓ¼ÓzW—¯¤öÍV`^Aå†Já”yË?;‹Ã* •Fî¡Æ%ÎXÃ|Ó;`9£/ñÏÊý%v ÕŠÎ@)÷à …°[)µÁ)1ŒŠö/nŽ–—ù* `t=—˜â a£Xèæ#$yT ¬ä Î#aÊ韚9åh(uŽÁNŸÈß­LˆÒsÿî'øÓɹаœåŒK÷ I;°ÎåÏ#ÃÏŒ@ÓA€5)M§¬mNi'J–$°-øe%Š¿Á™"Ó…K~åVÚÓ4ú`û* k]¯ÎÍšð-·bgÌ­Ø*ü ¶ÃnMê%C$$˜2p;â_‰%’}¾!%Ñ ËQ´Œ46-3{G‚ËýAÀ0Û›zñ ,oèÂí*·‘Âý!‚‚ c ò3M&¨•F±<Ë7‚VûݹG¶ø½¿ìÀvÞ{0ˆß£y<ñÎõÝ·%Í›ã“ÉÈ_‰æX*‘J7O€ì‰Bºfåfÿ~?¥ðîf¦§o®wƒ—gJcì×ë´jøO옺Té… æh·'aµiŽ1 }6ò¾²³ëÔQœYÙÞ?@ãYP]î%†j»AϾ¯Û–Òuü7÷dË}qˆÍûØ!4bÎr”ïÁPu¡Æ…3c|ŽÐ|Ë0Ö¤4ÂBmŠYÊ™˜ñ~’;áhäý]üî  ÝÔ¿6qPì‹¡¡€=D©Ã7|p®‰…ÀµÅJþ¦«’•¦WC°¹*'+6ÑuP;¹4×¥8ke|Q äOœKË0]±”qv®/}¥ZNÖøå8oe8xhæ"£KA¦Ê”nyD‚åv8ºZ¶7R»~ß¡~Ç¡nÖ’ ½ËñgLÅ™.·áÝF@}aÌï‘¿'!`ô—J« ìPøpݼlï³x‡è¾ûò“S$iŠÖ"Í×â†i¾Jö$žfÚi´sÊŽÓÕÓu8„Œg¡õšU(ÉS¦¾4÷a‰í;Œ}Í?~·–$@—ô#{ÎTÅ« ™YˆÝBcì0|eÆÀþéP-ó‰µê(j¨S2kI’£1uFàÎI“Ú«—çq߸J;pœÄGNšÔÆ¦æœ *Ú§8Õá§ÈÞƒö¡\¿W#Ûú¢žPä"ÍDUdiùcK »k‚§Ù^k{nUËüqæJ/M´ÜæÜ&mG)ýèbþA­[$l™6nÜ^Û?VWâ}k »û™ÇôY_3Î¥qoŸw;«ï¡VÎæ“Œ¢­ø¥—HÖ ÎéÝ{K8qì¸|e¢{ÑF4†ô¨°ì‹a2Lšh24# xp8>;F€hð¸WÅÈË=õ‡5§Ä0Œ#À0 ÀBJ`qPF€`F€¨?xwOýaÍ)5A¤ð¼.¥¾ÖqJD³Ì0Œ@í`!¥v¸q,F ,rücE@ú±cF€ˆ^î‰0Î0Œ#À0õƒ )õƒ3§Â0Œ#À0"ÀBJ„€qpF€`F€¨XH©œ9F€`F€ˆR"Œƒ3Œ#À0Œ@ý ÀBJýàÌ©0Œ#À0Œ@„°!`œ`F€`êRêgN…`F€`"D Áæ6òȘ"”;ú²FãäÝw?œ´'ißn·k´UþZÓ.ÿFS²œ‘ˆjûÙÔö Ã|¤[B·™>ß­ÔöÙ1#¤àÓØçm26<‰q·kßCºªþrˆ8ú°®2%)ÑÕ(J¢v™°³‚¢bñýê<ñÕk:ý°zýÔÂæ…wÝrŸwèÓÓý AÖį! +P’ ù9¿0©pžfÊ.}éÂå_»ú±¡Úþ4ô‘£oï»í‰)>jû ¹Ý7ž’âœÔ :ðýÏ8ëâ`Ù'>M×X8(w+%ÿݵCëæC¯¤]xj?Ù­ÃAÂãn02V,`)§I8:DÞ«“ø}ã¶”ü¢Àß=õôüoüeYÀ†ØaQGìþÇøûïv{žDù7ãò//v¾a¬>0¸íï+,ù{ÿ3íûæÓ¿D ±ÝÇ}ÉÖǸ÷ Ôƒñ¾D"Kñð€#{ico¾ÄÕ»G§z‚¦a&CøŒ»å×€#–.—gÚ-c½c~$’V¢!8â“øõÿ”Ê—C(:æñ@!°¿í÷B¿.ÿy{úƒwƒ—†Ôîtœn#ÏBŠ„Úò|hP¦c€R·_q¦LðÔYñÇE=Ö'‹póx']wÇ=‚: *TÞñ.¨ħ‡ø&þ¹ü;F œm_JmÚÍ÷¦_€h ¡Ý‡‘;Òˆ[!%5Õ—ìñxžèÖ±µ¸éÒÓâ}`˺C¸uißZ´lÝzÎQGØ LÚ‚J\ò[Æ”% ¿Äw·Ž).ÿx..æ- 6ÓµÃA*))eÞ9ç\Ý<Æm_ø1Oñƒ@¼V\Íh#ï4L³Ë5çèb Jí* ávÝ'i¦úuæHPIÄ yâ¶ÜËøK$~‰o. ÂŽˆjûמ¢ËTªs×cú܉èñÜî#ÌoJÄã`eÙ#¸=®ÑGÖM± Jݪ#áG86kÑb(%ã—€_<®S[å^Æ_2ñËå4Ø1µDÀnû‰ÉÉw€D¼¶ûZ掣5âQHÑn5îÓT°Í˜—y¢PËplÎåלrñªM¡ºH³½Ä2>ÛsùG¡ð™D“F€ÚRªãe7§ ¬MiÒµ¡af>Þ„k6íIL8ŸjÃ9( Õ8ãšp$<Ûtèx6XKÂ/ÞfUN-JñÉåg•ˆÙiØm¿yëVçÆa»o˜2Óõ‹@\ )€ $‹ƒÚêFšáØ"9Qinwd‘–|âmK²-¤_ÉÄ'ñËåßH+$g«Þ°Ú>úR©¹z"Ñxk÷õ†'Ôpˆ7!…øq MvÆQ÷ñÆ[Ã-epÞºU3éò¸;àÖ©I‰ŒK˽TÓD|¿ pfžˆ¨/•.ÙìØÔxi÷q‚³ÏÄ[e%~Üš-ð-¤¢XsO—Ôh+"©§¨³Š§õi«ÜËøJ$>¹üF„nóö=bÓ¶ÝƪßàÅ%±ò÷U&úþ—?‰Õn®ò=¿ˆjKš°Ú~¼µûÈ3Ã1šñ$¤Pb©ý•PQP2fýW¼úaÝ>¼eÇ^‘·egƒ® JYøÚ•½Ã'ªX× òrG\œÊø¬©ÆeÝÆíbXöÓbݦí•2ûÆ'ߊ×>®[=¯D´›âgÄä‹qÎÿ~c±€Ñ{•±~\³AÌüÏ{" ë!ü÷Åâ—?6…|ÇžµGƳöD ^Ú}í3Ã1›4(Ä“£†äÂ×&ôÀY “·>ýV´oÓRà€¤Jïއ%üQ™SGE¿xRK˽”'ð]!µá”OÝ8½õo§/µÜ~ù™¢]ëæâ·õÛÄKï}!Ž8¸‹8ñ¨CBfæø#zŠ£îù;w+$:±ô´ÚT¼µûXf˜i7âIH±gÔ´e.fBÊòUŠ—­ýzwï.ýA$%&ˆAŽg¸U¤ßÿº^¼ûùbý–¢{Ç63k󶊯~Z+ÿ~ ƒ¢gç¶VZT°:ø,½àw×uç¡Sí!Ú¶jkwÑ,9QwxOqË_OwmgñÕ¦e3qùàVgÐ ñÖ§ßYþÕ¥A^ûh™øòÇ5ŽŽW=@Ö½£ï“o~߯η\z†¸ôŒãÄÿÞÿZlݹÇzÅ?*sgºÆƒsòou2𠋇=ùEboA¡¶¦:¸;¿Ð lÂ…¨ÿ;vç[§sl×¥xiÑWb×Þ|ñʇ߈“ûR@¡´<ó5„ø!4ÁædàчZtˆ‡Ÿ×n$„ô?²§À™‚ü¶ïÞ©¸t||î­ÏD"“¿_x²Ø¶k¯Ø´}¿M Ñ\öóbì-‰›/9ÕŠ÷h‹Hpª§¶bå£ý9ÛY#Êg¥1#Ošçz8ÿ6è8q$ÔÒ‡@ó±tÅj«£=¦Ow‹‡?°î fuçœØ×æI¸].ѦU3á<—„r»÷ˆ>ФM€= $ÿªÒø³ÖþG,.‚–Æé~Xý§5ÛìØ¶… _§v­Ä7+ÿçŸ|´3X]ïãµ£ŠW¾êŠ÷_U¬Ž)ªë¤yè™w 4ìÚ´çžXµFoág+,r¤ ¡ežµ,'OZÃË/îo (äéºwîÉ·žG_¾8¢Wg´ÇÎV}·#¯‚ðÓ Ë«­š§à]é™IdxK®žÚŠÍJc¹Úm¬±ä‡óшG!¥^`' ¹æÍJÏb)Æ \Ú•ƒÅó˜ùMœ÷*–a×_t²µÌc½ ú#c\šé™¦i©®É`Ði4* êÌ{tjS¾S$V­Û„uý-˜•þ^þŽvlDÏÅn-z<¥†Âgts j¡ê`¨t¨;i÷Hp 8©W šVõÜa´Ž´Ü³BÇ?!Øü´‘C¯TNŸ7(¿wÞ¬Á2*¹XV%Ggz´mݺ§¿#éjiahy–ŒÖ[·hfM,ê§­”³Á7Œ#pˆ'!¥ê^0%xBg½W—v"ý… °F<÷öRqÐâqùY*qðÇÆmbÁgß‹»þ~žÕq~øÕO–ºÛiò* ²5¡9g”DÜ¥i–¶fPÿ#^ÿJéÅУ^q‘~–W¨:H¹£úf æô¼~Ë.º”; ~ùc£èÒþ ±pé÷â¸#zXöSåBÜÐò'i&IÐv:ê|(ךBrÛví=°œj¦Øû.Û4ßY¼‚Òqh·NXöégñàÂRi{@[±Yk Wnw¡›@âmýŸ~f&Mªi2ÝWPdÙ£P§›HÆzpÚ¶¿oØ*Èv…TÎ;vXþ!Ìv¬¥†%#Ë…aŠFËLß@Cëõ4C\ZrÇÞC|ñãoâÌ^i¶Hï‚…+`ÝþÊ0.·K©µèÄŽGž¢“³Q¡º±6o›õ£ûHÝa=:â<’-bãÖ]ÖRçkÖ—“Ð C<ƒ­Ä IuÝ9bóŽÝ0&ÿ±ü}ð Ù‘ÐNå¿þ)zt.µÙ üLP‹fI–‘.iaÁݹ5ùÇßòD~Q± ò>à·™ãêzj+Á,7ÜçÒ“ÎvÖpóœ7)B«šû3k˜†x;žzíl‘taVØA *Ûõs&4ÿ·às1ú¡çÅM0â#@²Cy0çU¨©`r¨ Ž1_\wáI–m í³Ò:”qƽ7”·‹X´•Q±L“Úþ÷+׬xæ‘É7 xi÷±ÌrÌiGqÜ‹9¯ =Û;Å)zwhXH`p (Ä~m”Ò¸¡Ó!á¨y }b‡#[œ‚AlSªš:åóA‚ ù“‘ùNlƒ¦íÉ+pîJ·mÊ¢Èm¥j\ù #Ð=J6†œqF Á"@Âÿm—!>_ñÎDY)’,»”óN®z+tƒÍ,3Î0U"ÀBJ•Ðð F€8ôÅdú±c¦‹@E£‹¦‹çœ`F€`â Râ¬@˜F ž /ˆÛ[äã‰/æ…`š,¤4ræ\2µB€¶å—}ï¯Vñ9#À0uA€mRê‚Çeš[ð‘ËyÿûÛlnBø£7ïØc'ôÕkÅð«ãCÝú*øF|ïçôãäó¥§5„8‹Œ#+X“+d™.#ÐH 6ÇÙËú ñ¬—Þ·rE[‚_ûèì¾Yw=!¬¸ÅÜ—?nÙaTû*Þ-Ç–avŒ#ÀÔRê‚Çeš§s¾ÚÝK\qö±_§³KÈÑk#†œ-N;¶õ¹ˆ5y[Äåƒû[a8² ÃYdX#ÀBJ¬fúŒ@G )±ôDåä²kN]&GÇÖÛ‡Ò7vè¶e‡¶jN‡G³cF n°R7ü86#Ðèø , èâKؘÐG7I8 vd¯Bߘúúçß-a…—z‚âgF€¨ l8[Ô8#ЄèŠN~òM|™ÛWᣃU¹+Ïî/þ÷Á×⿇÷êXU0ögF lXH *È4=¦Ü9ÄÊô¥øò°²ü»9§Â…~Nwìá=ñ¥äžÖG3ùc#vmv¾æ{F€`"F€…”ˆ!ãŒ@ÓC@†ù9n üõð¦‡ç˜`¢…Û¤D I¦Ã0å´kÝRœsbßòg¾aF 6°RÔ8#ÀT‹@‡6-ÄE§Sm~É0Œ@M°RBüž`F€`,¤Ø9QF€`F€¨ 6œ BHákjô³óÞö‹ôê4:¤{çs¤´8|ìˆvp–7—ì˯¶)D»Ü‰.{l £maûó~¿odwû;h!èÞù%÷4y!Åî˜LÓ´¡¢c¿^»AìØ“/vçXM«S(k’‰ nѪYŠhÓª™è{HWѳs[¡išÕ‰9;²¸¯1A»ü±a›øqMžØ±;_ìÚW JJû‹¾¶Ý`#):e¶ ¨DDaq‰HJt‹ƒZ4ÍËN˜mˆuØ.wnûQmÔTãéGZzí¦Ñú{Ï…OO¡ÉÎèñšG#5%Å^aªÊTëLÃXøÔCYË@×Ä…–hG4š¬BuN†aŠââ€øè›•âãoûò ­&¦Ë$Q¤% C¸¢R\.Q(’Ì­ŠÄ[‹WˆæÍ’ÅYýƒú)ÑáÛKTc"a!@u€‘¾Z)>øúg±7FeOÌ4´ò/(,?ü¶¿<ñÓšø^ÚEçv»DkœBÛ»[qôaÝD¿ÞݰÙUAƒ"Úõâ¶ømÁÄ5hÐe)=521Ñs‡Z'¼P 2ßLÖöH·(ŽŠ‰.ÍBÙR•hÍ4Ííz05ãÁMº®ÝöóºY¯¿þdrh ,1É,­?š¤b '@Àš9Ï_´Ì vz:ŠÍ)}Åv\uYú½’h…[DÛÀfѱd½xý“ï ­×w‚8ê°îÖù$¬°‹=T~X½^<÷Îçb´&õQö”«x/Ã0Ä;KVˆ…Ÿÿ$¨}èZ¢Øéj'v'·±Ú„a†ÚåÄH€ÐÝ|ϱõû5b)¾ˆÜ²yЏ|Ðqâ„¿l ÞñVŸ¹íǤmQ§E³9ÏuwÜs^‹V-gC8éÜÞ³Zuw/Ý¿ÈYÙÞ~ö­Ž²D%‰ÍúáâOý˜Ž[Äa“;ÝsÔ‡ŽK{ö‘© ÔÀ„v &%¤Ø3(]70ƒ..[)ÞþìGQàj!V5?^ìv·‹yQR¿9¡›õk¥o} ¿¹¯|".=½Ÿ8{à_ÍLY«»b°ëÀ¢/¯~ø­È·ÊþÔz){ÊU¼–¿¥U‚P2ç¥ÄÊß7ˆmž.b]‹>b¯«UX…áº8(°Eô*úUüûÍ%bݦíâ²AÇ[‚w>´2?®Ù 6nÝ%¶íÞ'tÝ .A!ìѱ­8¬{Ѷu +X-ÙåÎm?¬â 7­=¡q$áÆ1ãG%%7›ØÂµYõK|S´sÿAïcê üˆîžåô“Û<=ÅŠ¢K;¨”¯Þ2Ö›þô4ÿ $®ãÇZ•˜–Bl‰7!Åî¤øPZ1”¾ú ³ÅŸÑw?§'LmA¿æ‚#¡hYó3Ä‘ßB«²ÜZL=û„¾Âãá埚ы<„]}ñ£xåÃohÙ÷ªü ‡½ùEb_áÿ³wðQUYÿ¼7}Ò„ÐB•Þ› H± bYÅ]˺ºú) bC a7+Á†Ø ຺ºêŠeUiŠ"J•„^Co©Sß{ß9or'“d’Ly3™ ÷æ÷òÞ¼w˹ÿÛÎ=÷ÜsíàÆåNQ Ib,üm“Ê ì°ô#¦vA,^elNê[Bgû&X¶®ÌX·8{Ÿ@et”ù "8tV1tè[#JbÏ"×±Ms¸¨ïy^ Œ–Ì +wÞöƒ*Òú<3…DΦ[zì!dPþÑÚ¯ô5)êW}á5ÿŽL O˜£Ûh«‚ÏÝñØTáý™3ˆQ!b8£¢9âщðœaRHÌK”Ýြ`ÑêuÊO¤kI…˜#¢¡GÀü_ò =5×õ³TF…Öö¹Óªt¢ïW?mˆ‰²§œE«üeYÁå­ƒ°*o7ì8tu¯hÙ¾ª#Æà2íÁ2(UbÁ¡k—¹7d:Â÷+·€KgBSWÄ»J­ªžž, [n•Jpùó(8Ž„ÝWÀ’Õ[áÆËAÇÖéšé¶ð¶_¥„´úAË-Ä ˜¯»s•ÖÄ促2ÐòiÄ¥'ue€˜# 7£2ãæ‰“¶ÿ÷Í—¿Ç0Ĩx8âº"àßbs‚I¡NŠÄ¼$A)*.…¯Ù åb’*A‰•!i΀ÒølézèØ*CU¬¥A#ÖÖôc¯`é :`Ç)/Z£.ñÞ±ä"Uþ”ï­¸céã…kp×R ꘘá”>J,Á‰Êá m‰ÇI¦E*ê—š:„ ‹Œqî6wW°c†,¼û·L™º@gÈ@=-ש­ðê'KàÚá}àb”*Ò9@á,ò¶vqú‹€;LYY›¤·hñr2.ñÅŸç†x‡´ÅRº"¤6}«wï úäåýV„tÐή£ÒFš1S©ÂÈCAIÔK;xHņâí¸ƒ‡ÖÈwXz5ÈOmÄÒŒšh*Á]?¡® ÑKtýÜ…‡«?®Ûª*Éî0ÇVÙSî´.Ož%˜·x ¼ñép¸TA‰Ý`ø-i4Xû«ÌÈ cKUÂAÒ“æÎ°ÓÒ™wMvˆªñ1â‰Èµ0(þJô8êj­N¼Žëš«Ò®/~X«J?‰Ñ¥°rçmßÚ!¿#Ž“D¼F¼,CÆŒ™‚®9ê èb‰§¶\-½ÍóEt™½F]4±‚^¢Û?Ç\[Dü}ƒ#Ðè™ÏLÊ£‡R†"îµ; ÕÑP’ ¶t‰&Úeò+î” ©ÛíV·I÷_Vh'á‹eOkUþŒAùX‰á=dêk’F© INbÙÑî!b¦ àg,¯Å«¶¨ÛÄi×Q°Œ +wjK¼íkVê^) Ƙœšò¸‹‡ôAbÍMD›Õj¹i3áEÒŸF?æÅZ9„KO£.0ïL w-ØÑÕÞ¨“â„c†Öáâ±ðD٨؇´:‘n.M jV¨ìËÊí1]ö”ÓpËŸ1(¤¼jËØgî »-=QÎ_M}—µd…ôZöZ¢ÂÊÚoûáµ!ŸÐ¾RóEW\;HÐé›á6ã˜å|UÚD]Æ•7Ý>óAÒ.Mñ)ÐxxŒ¯ž+HDY‡Möh©g7T$ì#;(±êTÚÆ‚}GU;¡Ì c5o A«dE8ÖËžð ·üIzpèØi˜»ÅHYu¿¹KCÀ®Iš;pùÉfø·Š“4„ß)8Vî¼í‚VÀ~“B ³–ôV­Gâ í A´=mDcj³f—bÚD7gR¢]a¦×¨™êÐh'«¢´«§¨Ô†›æM3ÔfY¨ÁÉŽY»=SRæsÚ1k‘~c‹ƒÕSdŸq%|cÙ…Sþ”WR_¼*\Š;¬}c9«õÒ&aYí3u‚CÇÏÀ<²€Ž@%‹¬ÜyÛ¯æ`<ÐxÁôQÌ£©Z’Ek²ö`∪_¢huú,L˜IRõ¸U€£X£.,Úvé®`R¸ÌS†™ºugG+ŸÅ¸4A,ÑOùà.4X(B“÷ñPö”ËPÊŸ-oœ<[ p‹ýaTZu Ô'Ç·£%rt¦M4ÕÓbåNmˆ·}Íê¤×Aˤ3è3ÌBIWbÚh:ŸÄçD7×K‰éÒªI\£eR¨Óf"_êØHÃßåBÉ *æÅº#ÃX.§¤vÈl¹‡òÂ]pøÖ§Ó­Ù9LÁQ¼ïPÊŸòJõ|óÎC€‡®…gë$x’#‚-Ú6}üt±Êl¤¨>¦Ý·ÜyÛ׬hˆ¡‹$)*£¢uVƒàŒy&…huÛÑÍ–{bžn¤•;D Ñ2)TºLäK=unȵÄM¡SGKtÓEùà.4X <ãÉ[þ”Ow@]&´êªÕVâ˜ÀLñ,Û’DÄå¦%Ÿúwú°rçm_Ó¤ñ‚ù FÏ"ާÒÊhoÔã^ÜI€„6ÚÂb³)›hóV-ùˆKƒ{C9J7ÑÏòÒàDÅ 7•?‰7&%ˆò÷04hQ™”“EePŒ† ‹3*6<ÌÐ V<ëG]þT%)uÛMñ-wÞö5« İKì±IÅ “‚u‚h%ºYâ†vÍJ0N#j´L •‡·³ÂŸfVô;n1'ô‡4ÇÝ10Ã/ŽJÞƒ`åOyôH ÜxKµ$cÅ29-ûÔ°-R-¨Ÿ…Kv’ÅúÚDe¹ó¶2ø5úðñ8vøÒ_3wüML"-( ½U<1(˜CTíAe˜{®@}Z1ð"˜ò§üy–{$0àaz…~—$…,ÇNHO4 $Et{`í'ÞÊ?˜²o€RŽçžÑÞ°ñ$CE Ñ3)¡ÃÃqBA€zAý‹¢ó0)iJ‚tO«Œ²£½¬-ÿÓt°óæÿ“b˜ðm)\ÜÎÄÜ’ 'ÿ¾ô«‘ÅÆ?*Hß+(ª:|Î^ ÅÇ7«á: ¸ú^9·ÌW]žÔ›R`à>€ÌNWÕˆ÷êW }¿ `´4©ñ­Î%ßi¯3nþ1¢TŠ"šLüEž„ƒË-½Œp÷7¥*ñÉØï”:&.(ƒopvìëR±¶c;[u¨jc#?ïnpÀ²¿¦Ày(m¡Î,P·ý¤þ€Ÿgï¢É ¢Љáð”ç´>¿[ç™Y( åo &¼¦à7Ï„GJd—dGôÇ/¢±&ƒY[Wu6 µ*€ùÛ±ÐÑuÆó{jMou×=]×u5—èÿÜÉÑ€þá&Œ`†¯+â©®®ß…Å2<»—nŒèŒnÄJ‰\F‚/\†Ë ØíÒ™8tC!VÕ7Ö+`‘ÌÐu9ÎèÓÕ«®4jûF4ÿ¯Àg !× ëï_—Â߆We¸K ¼´²ÜÍfdèGwð0eÄàï>%í½Lðƚ଒–èR`[ 8$eká›uûáß{’@Ÿh=Â?4K†û‡TJyˆ€Ÿö¹°Œ°¼q7Ÿät€ËVŽÒr°µAæ#ÀÞ®zÛ§¸kËÿw;@sy˜ÿ-õ0/ß¡NpBÉDÚ~Ëó{wžyK£3è»O[’Ê΂Trlzò•-E8‚R@‰µ?âY"ÿŸ¡îÂØn&/“Bñ´Äø‚q]pö9íâdhÚ$’“뤉÷¥¦­;·ýçÕBÿGñ"9-.ªM{+GüeG² Ùi ­)^™yhÊ«}ºŸ×í‘Û®ÀŸ¡;Ï`eÇÁJï~£‡­g‹Ê½ÏŒ¸¶qä.Ô× ¦”$h©(U#© )”îE&çx)Íú(¾`™”ÚÒ¬þžj’¢ ¼Í`µZ %Ñ =[:`Ýb\ŠD}¬.SÜ-A!hýßLzß9g¡u²¦\`AåÙ$¸è=\ÀŒS™AÖ}5ñjÿNé›C Ç>8rªÛCÑéĈìö ¦í32'c¾ûdêàæÏKÀåsÖV(ùDÛß\°'Ÿý Òj»¯ÑNGb;Åøº1 êº[[½ôp]^þf/9 æÄ–ûçãàFÎøÎkPÔÓŒˆf“¡¸Ýgd”ÄTBKñ+«_"JZÓOÚõB®'Öbx¸57’Žj²'BÒ”cgJa˾ãpÐt^Ð £‘˜Žn¨ZŸ#F~â` ´Ãå-bæWrÁýß—zu¹z¢DeÅ¡Júâ«í{’T¤îb¢W.—;¢’”`ÚþŸ{šTI™¯íÊC·ŠiZå¿6\âå}é™]¸ÕØÉé=át᪰ÈNÉè 'þVhзܼí3$B¾×`FœvÓN©ñ>ä"Ю$+.§ótµècžîjôž³?-“â¯D-Üýœ©Vnõç¯!ßmz¤1ÁÈWâ´,‡d« ËÍÚÇpÙS~C-äQ@BiŠ­¾fàIÈ­{=óF-AŒb\&ÙÝñÌ:M SS2RgB&Åf“ŒF<¿‡Žðc¢¿6yÛ¯ ™ÐßÛËËN8•Á©˜C$Â!‰6•F[ù±'Å£žI¡Y»š$àìÌ›ºb·¾ª´!™i ^º#TöçL´Tþm3Ób¾ì©@B-¦PJz)½Û¥£Ô¨ Ú¢9üxt (ê_ºŒ¸ìÙ"&'X !Áª^ĬÕe½º“©®ü±vOwÞöëB*¨oØ;y–Mì(ÈÃÂ1w— "ˆ¦g¢h<¸{×ZFw4Óçi…@£fR¼îÏ ­™É¸OR‡³¯æ®Cá#¡ˆ6ÒŸhšlAª+¬%×è£eu #5QÕiˆå²§ÂТü³2R m³h‡údJ>ž\ ç~èWò3XPêÕ>Y‚&‰f•9±Z‘IÁ‹Î0"[0õ™ÄgåNmˆ·}Íj€—AÁå-ëVí–\Ž¢ƒî>ô>&Ñ&»g~ÿåÇíH /ý1I/'ª&–IñvR(.¦N‡¥{&šéNCIJ nkŒ5G4m횘½ô’n ËK¬Ñëô0ÜC*ûî­Ób¶ì ËpÊŸòJƒ1å“”Jû´miV=t¶m‚Nå›C: 9Zå+à¹Bé®Ã0 ä'è\¾Et@¥i’’ðœ:»*99™‹ºìCgQ^ksÕË·ýÚ ê½ïOFŸÔëàžÝKŽ»: 'ÝmƒŠ,ž‰&¢íðó½x÷ÍG4Èài„‰@í-=̈c!¸§Ó&æDçÝÐ2 w 4å<µãŽk¢"©4Ñ®žÉ*½D7]uuȱ€s,Óà[º·M*]žg£A;vÊžð ·ü‰ñ™Æ÷œßc‚þY©@gÇ´rîÁ%K!2†ŒK”Š¡wéoЙ-Ñ2´x¡g²5зôVô­ª’*Ÿ… #2()i‰Èœ c’’œ É)É”˜¨šû7"ƒB;|êk¾åÎvñ¶v ¢õ s‚wÚ*çþuÁü¥’Ë^”g¿Fv+¸œ#Žh!šd·ãÌ/ó¿ú–hÅ‹1W”îâFˤ°Ù”‡AÑ«Êv´–mD…ÔÖ 2$ÈÅЭ|}ÌÑB4un†»PÌD'*ªbmÏVKÊwÁ!P½XPŸ¡»4HÀ·[ù†à"‹°ïpËŸeª/TÇ-x²ÏïIÄ“»d&A'4YŸåÐ Q‡/Æe H–Nƒ@Ú¶~œˆ}y÷1Õ”nÐ.›L×~°Ê•–—ý«÷•Qq@[Ûv°âRNÆŸé> Ò HÕ9¡¥É ’Üh°Í„’“$HII‚Ô”HIE&%)¤“bFÅÙú¤(DDõr§¶ÄÛ~½ÅSŸ& ž|Úëî,//µ­_ñËGÅrl´õ_¡ê‹9߉¢iÓª•ÿ,-ÅS)=ô2F…å%)ó(µF Ö¶TT¢“è%ºµZî±;] ÚÑð*S•R/îU_GíWôÉH”åO9ðW:·LÅ;Ñ3 WöD›–åO7 ÌTg,²YeNЀ•Ê 0 z4q8ë‚z`™$@¢ä7Ö?O!( ÇùI¯€YDi£ÊH¥XÝ.(´—Á ’à±#œ4¶€R]*†«½‰šÑ~V3÷QÔ7)DƒlgÔíÄ-ÍNd˜SŸLƒ<‰Ihð§e2’B ?}ýŲSGœ8|ÔWHw%gv)mô›„æúí`"cD“ì Ð6cÚÅCJ²‚ 19øo!½h7Í8‰>Z£¤;ÑKtý¡0hŒ»†@ ™L—ØB92VÔ#Õy:*HÙŽì+$&$¨´gÑÔA{:gXP~Æ…¢gdˆq É¡ ÛO’•m "südI”ŒÉQ!QzªÞ 陳J‰µPÌMtªö T&%0ñv]Ž„§­¼Œ¶4QeW]Á¢ýѤZ”?e ®:Ðë@‚¾uàÀ‹eOöž"PöDG4ÊŸ Ì8Þ«Žý¦-»êr'¶»ÝŽW£ŽËAd¥–l«CCŽ˜¶ã‘â! 訽XÑä~’Å 6<Ä°Èæ†<öØ*7N I¢‚± ›¢€ApPÂ;Jd’I*ƒÖ“F4Ɔ„ãAŒˆÕbA¦„éŠTÜ+Ú©ujˆ^j·4Á f‰è ÆÕUîçbÛwÚlG?ÖÆ‚Ò×/…§AžIRˆ šju”âæÕ¿mÙ¹iãž!£Ç\èîÒeØqc§$¬AŠQ(SÌB‘`œž–u9Ü@IDAT …žÃqdŽŸ¬Ý’¡6$Jp;í¥E~Ûß´|iO“Á i0ìöËÅÍ,’ßÇtˆN_IЧ²‡C5´µ#WmDÇX‘Ü´éC›wÂù=;„»g&险‘$…Äü´NÏfô]§CK¤(o‚Qì{Á&ã)/8#t£¢ ‚p¸Î£óŠ¢tŒH/ÈhPÀ‚¦¾UÑmÚvîÒÙš˜œj²Z’ôúŠs0@8ÎíÆ©l9ÎeJ÷žÙ¿³`ûÎ-›`|Λ/nAƒËC¬(ßéÝIw—Q·âÝù}#z¹>J8 7PØXbR¼ÕOß~¹¹Sï¾§Öæïi‚LJh=T žN¥)ØQÉØ‹z ==˜§µ;쪢¡‘Î?Á›:n6»Ô¢lˆßAƒ:fê¤i‰‡$(Ä Ðnz¦™&Ñë+n‡ÄQ‘%©xóÚ•»0š 1‘'Ã<œèà Ëh »JÑ9ø’Ë‹îäpËŸˆ ´vHöJhàÖ®D³ü=ùõ,ªu¼Byœ$‡EeNƒÂ¤ ”W ǰbŒ ý¦v@Ì 1(v¼Êm6õ™–H#IœH’BÆÓ(Œ·m!óARªÓ”>í8") ý&=j”–N,°-÷•m%œÊÅòr®·}E–Ï®[þöŠöåÛÞB—µS’¦cB ë£CË*´ dØ•¿i7^{ñ™ü2ø¶#:(=ß4íó_è¹i’ÎCµþ¢^ò+Mͦ‹^øL]êfý^؉ó¢‡@,1)”kªpê@uòÈáO¶Âý;…ó²2ÃB„u˜&ųVO‘a?ªê{H™:KÏZ=êªÐŽT8d³Kò³B%9¡:‘tdHüîYWuPh'2'êrJ{”ʤ`‡—zp¹ÈŠŒ µ'2)ļ“Â:Ox’¢x¶DÓR-ëSB’+NqÙr}cÒBj&TFž.­\C•;ÑÏòÑÐmÿðþ½_#9Z¶{Æt‚–/óBï-x©º*xg’bTȱ0ž_Áý§tÈ ¬ß ¦ˆÒ´•Ù¤Ò7è&ßq¹ü×ÕÏMÒ×ùáK­[xäåC䟻8C –˜ª|¬¢»—ýå'c'L¼å“…«Òž¸óш‡†ã¨³"æ€ëHIÄì]«§Y")ÔV̤};ÞpÒõMÏÛq«RÏn#CÓr‰Âé"…è$zÃuN—ˆ4rÿºà›ÅÍ~èbÃ<ܤ Ïh ƉèRi$z³:vô'kQþD`CÕVߢ]þ,Ï”owëÐò Õkªcæ™ ”’Âq¢•.’l°gúFáœ.'êo¡4™’¢Ðo¶Í™˜æ(=bpÔ¥lcž‰éxvðT×5¡t"éªÜ)O”7L[”Û>në)Bcf_!)Z·{*pbÈQf¿‰Y ýbRTi Þ©ãeŒŠ…ÍÒc}ÑAiªÊ¼«ò¥ý£Ï×ïÕVþ¯Ê‹0¨[‹Ã¯á÷qxqgÄ“BÐQEWªÓ§—lßøû BÿAÏ~0…r÷ØaUnÖY >cçá‘h 2ŠŸ‰AQ;^TTEàz+¬óÆ~:hÇú^–6™ç¦Ž›uÖ´sÁ#÷(Òûp×á}‰$ÜŸ8 ëýéƒòòR£„.ÖaUŽ,¾£ÿì-÷ úD/Ñ-ޏô~-ÊŸ²ÄÊ!Zu ¡Ëß·i $GÌÕi&ù zÍê8}'ŒÈÑìŸýöøGEXl;Äà¸,n•9©Ôïòħ¨C “ÆP8’˜0É }#zM,\¤îÑ.wÊGC—=kûy«}»¤ä,kûZ¶{Æ(ƒàûLý mw&ÅW©¶:“JÎza–cR(_Œ9"FÅþô¿¤Åóþ!NÖ‹Ê+TȌ߽`¦¸iÌcòø»8B l&kK æ·…Fy¦ÁŠ*U:û/ß³")-í_ø|7| Êí× ‘¨PgÅDØžÙg{2IShFHëôž5vƒã:;ýa/îÛ‰#-A9JS½PÂÉ:n같 1&Ôy‡º‹Á1$A¡Njݶ½Âî­yÿÛ°âg2¯JWmÀx'| çXbR¼å^A§èF#Q4 «Eùcër¬Ôå§ú7Q¤íÌže#1ð&Y•ÆHz;¬xó‹õ^Dcq¬Î{˜T!Z̉oXžé~®´ýý;¶}²fÙ’ÕˆC¤Ú=1 Ôn}™¶ôB µl×1(tcÂ.| ÉQZ좴ébXJ›ú6º»nü»üúw3Åȡ܆¿‰iÁ ú c¦¸U‡óO’0xa8Qð°!6“"(ÂaLj@`ÉÕë‹UzªdÔ°Ê|üïO®¸ù6¢óö#§Î(7_>DÔBGÅÓYU®Õ«Ì v¸lžf˜L„.“B¹¦ŽûGïì’ÄéªÂ ºöï™UMá:Òá %’ ìÎÏûúǯ>[ˆqƺ½€åŽ4«3±ÿ‡ôÓÎV€kµ*ÂØS4`E¶°2VùSÞ"á(ÉíþÑÕÄø¶òë¹IQ"m'„&;Ô†éÎê³s¶†NE tȱ4£Â˜ö[ýnm–5¾üÄþîHÝìÓ HŸ/~Þ:`ôãå4n…ãhb¾.œxØÀ§²¨)ŒŸšû2]— oöö“Oja,Œ*4qà x¥àÕ¯&ƒF]6¼÷à îõúÔžZ+ƒztzuj–Œ×+)!†„¶aVŠÁ+%(¾0… ƱŽÊ·£¦N‹)Ó±÷ÁÄYÝ/ÙA¡íº´‹‡”de·«hÝòeŸlZù m9.Æ‹Êå4^§ðBkGj§Å¤)ø3&œßrGÊÒðJî3ô¢¾h$êQoHѲü)çL ‰:ò§›&•¡¹pE˜6wFö3AGÀ…@Ø’Ü­²È-»gÈåòU˜ò‚JÝ¿gªlÄÓ£wþÚeKVl]³ºà‚+¯º·Ó^†ƒq vJ’Ť¤¦$fÑÀUgHªÿ% ÖY±°Õ³÷ÁÞÉtfæs8–r$ÊŸ©^æÕ Zõò®þ;Øø¸ÿÈ P½œ«ÿ%Õêe]ýw(qRmÿŽ‚W.ün1úw½4T»gLë_ƒÂ²ªE?Mi0ÇÒc¿kÜ/{Èvàû™úA–– g=²6çØ·íMô’"-u(•õ8öÕHŒ¿Ð-* ŒËž~¼[ææf_©…40Ñš&‰üñJÆ+/’¬ÐZ`B×¾:g×¥§ÅšØÌh1§èõŽ.šäã ‡gÜ86[‘­´ô4vP;6o؇„ÓRiºCBŒ uVÔi“BL }#?¬Áǘqõ–;RJuÂйW¿vm;wíjILlb²XRÐøX覉c&ûœŽ@``Û·cÛ/¶••ž:°cûÖíyë÷`Èxm÷e:L_ß½ NÄ1¢RqM\\5E&f%(7>;÷{”³÷|'wZ› rÏ!! É >njîdœÎD­‹sfd/‰’šHüÏZú!愘bZè¢-n‘Ø‡ÑÆ…c³ b6袵Xb>HRBú<ÄÐEÌ 1+Ä´0…üƪãå«%ÃéŠk» ¶¨HûS¤E•!ì/u£‚Q¤05w8n©øW´{gFö‹Q!úOD&%'ç=óa÷¡í(È8+Ò‡ÎÍ™PýÄÍP`&ÚØÌš˜:ž.bPˆi!&EUã)i‘Mò„ñă£‹1(´„ä(ªÒ1þ&&…Ê‚.b^˜…utø*æ/÷˜+NPŒ!ÐÛ}T ^ö^;3*Ò® EZ5AŽK@Š´ãsæX׉•¨eÚRߺKNÎ4éã.Âh6 Ïž>™”á—sr§Þ„k¯Z „lÀ"&„”i‰Y!Æ„1(ô›ÞûcR4ËÆkŽa[½³"E0bF¨ñ°m‡ô›Þ3=_Ŭãå³Eà k@XÛm¬í>*І¢H‹úI„ìŸ"ðcqh37wÚâ¨ËÑVê0~Zîʬ¼Œú)Ÿ!ý$*TL$!aË´DŒ »3…¾ÓàÆ.|lÔŽ:*v±mwĈCBv§gúN—xs¼Üã­Ä8½‘F€µyº7Övi iGâfEZLM„^9Eç/a’ €ëÄû¨‡ò'A&ÍžýŠ?ü]dÐ\ÚàaTàEä6·  •5ÔQa³kbFèòeNƒBƒ9Íóå‰6¦þS'EŽ˜ß‹:.bVXƾ㫸t¼Üã²Ø8ÑBà\i÷‚¯2Ú@i=:(ð:†ê¶ 's¥¿h=Ed0§¥ÌÀ;¸M+ —}âõ‘h¿ÓÈŽ ÑÌ.bJèbïX~Ø?5ZÇ:+º³‹v±wVƬÌÏÕro,åÉóçZ» ¥CùS¤/MßêÙf¬ÜŠËé'µ?Ë(ïònAœq}€†ñ/oÑ4©ÉOw¶·³ot_0S¼_‘•×Ù;„‰W>.¿Å~ó»6p&Ey,ŽG€#g#B“êdwµ­3(®Ïñýz¼ºLî=´ºúÍ-ÒúCEÛw\qV[yqË|ÜÍÓUgªlI¦lªiá–eYQîæiáݹâlxøñÐŽG€#Ð@*û ze”,éÌ’"}‰Û“?ØöhÏgÉ*퀷7sEsÞƒçòÍ:W¤õEC›g.IÑG G€#Àà4z¼”?¼rÉBw7È EA7©`r¯gˆAé>+¿_8Ö:]¶«ªg—+ÒVG$üßQ‘¤Ü;íÙ’,]Žä¶ÅóZb¢Iá“Îcàp8-ÀåŒAcœûu¢nÑÛÓŸü]Ëøã%.”¢ÌT¸pêuâ ùôØE´wŸµù/’¢ÌÂ]?÷"Óò¥¿üp‹´þP ý]Ę”œœ÷Ì…®Âû‘} ºµ € Šp ™”Øhiè$óŽG€# IID&¥%öÛÍÑ4<ªdÀ!ì·_mehõFNÎUŒ™E"ýX‰³Ë̼—‘i£³ŠwäOìQ:r™¢?²nóL|w•N¯ËŸÜck]´r‹´u¡Ü·ˆ0)ã³§F2ÞÁJž…â±…x}$ZÅïÞ~òÉ3Á‘Ç}s8Ž@´¸÷ÙgÓärù*Ü–{+^Wà$óÒ0nnî´ÅѦ¥!Ò#¦dÙH˜þI8çáxf³èoõµJ[m ^@ì^c~¸EZ†DpwÍ™”ñÓrVdx—î¶ GþàœÙ˃#‰ûæp8XA`ÂÔÜáh/„Ì¿÷D˜ãΠÔ;ÏàpâêÇCúPê×ñ¬š—©ŸÊçRRD -Î>ºí±^Ùµ1(½^ÎëÐíå­ýÿ4OÑUO‘+ÒVG$øßšIRh‰ ö- Š<'wêM$& ž‚#Ààpb²È:!{ƧرÅî}̹²ôS_YtµùÔ»¼—ƒúãa„‹¶?Ö{’o®Hë‹FðÏšHRHI“~‡–xÀ~gP‚/‚#ÀàÄ2j¿Žý;Ò˜×;ý~,“Ú íõ›Ê稷cQ·¬z¢Ü"muD‚û­ “B»xT%YÔA™›3¡<8¸oŽG€#Àˆ¨ÇAB!KݽDGFf*_V„g½áòíöøÆ_rþ,Ò¢bí½þüòwUЄI¡mÆÈe/ä:(UÁå¿8Ž@cC@ÕQÁþ^5/ÑØ2D~ú¾¼7U5•/(½RMæ!Û®àc¦È¯ãÉÊVúQ^]ð‚þÂÊßüÉa3)d¨ÍcEøÈ_üG€#Àà4.pRúõûjÿ߸²Pnz¼˜ßÝ.•¬ÆÝNk¶=ÚëºÕžWÌv}qóØ®/æ}ÜåÅÍ?öžî\‘ÖÀžÃfRÈ’,j#;(%É}q8Ž@<#@ý=õû–Äã9+AÓŽ;yZ¸i‘"èC}”¨º:KŸ—¶·BKµxø ò(ê>ÅmËŸ¹ùóž¯lnÎuç>»Ñd‹LN¨ïÈtƒí‹üy=ŒÌ¿WE l&£kK–d¹¡¶ªÀò_ŽG ±" ö÷ØïcþÚ6Ö<Ö–¯m“º,†¾¾ú'´ Ù.;~A½KP²2rë#=¾.x¬ç|”´üËí†}ã⊴¾hÔÿ6“Bgñ©ûú“â>8ŽG ± @ý>õÿ%?Áä£àþn§˜ÿsƒÓ-|+ “(YAC¦>æ7¥'JœŽ3¿ìÎiõßÃfRPl•„?‹§~¬¹ŽG€#Ðh ~ŸúÿF“¡3b+Ý‚vÁ”_P‚ò©oÝfm¹lÞ¿ç¾ïÙ3W¤eHÔ}›I©;zþ•#pî 0.{úŠñÓfÜO975wõ„ìéwÅÍœVŽ@,!€6îÒ¼Ò¶ä.³6O•ùï 3\ýÓ(ÁMßzÎÚܦ:Ý\‘¶:"5ëk¾âo8¡#pÏ´g.Deºxzê¯xÆÇEUDŸÑâÀø6ÎÀÏ‘= ””rr1''¸34BI'Ø0˜§>‚¬dn|vî´æy©N'^1û驨xWÕ=4cFóòrù¨¨®ótöüª_Ãü%(}ñ°Û i3Uœ#ÐxЉÿ$ii—Yù{$Ógm~3wFÔé‡m›ÔíeOOîçRälÜñÓ^T`2šÙW7š"-Z¤ë²Ûס%ßtÜ1ÅiGô¸1ßÙx@ ='\’:v¹÷zÏ|[{ÌÅSw ¶J©N„<öžî\‘֚ϜI©‰ £àÑæÂ³÷MÍ­!â 'ú{¦=Ûì3„Gl†¾&%Ä’"ûŒØ¤SÅàÔ†@þä[ &÷¾sûä^·ûîú!ÿ´¹´dËh¥7-³YâE[íu°z<¤H+ˆÂ£ì½¬(ws‹´4ør«ü®)½~ªË%r¼_Hä¨Óq) F§âl£Š>O 2Úü–úÖOåäÜi§ð÷dO'+î§éÙár-ÁC-]8s9‰þòÐ[<ðìzúÆÜø©¹KQÎjš3}êpße'LÜÄcïäN}üŽÏ™cU¤Óqi4ZÑl‡qî@ÿóZ蟚黬„ñý¿n©Ï¾­Pšñ¤ó´‰nÊHÉxýÁ,]ßû¸i¹ÓA†KuÝ-³sžÜçûÍ÷Y”}hZ{2*?í™OæNjµïwÏÒMaÇϘÑÊ•Wñq^˜”ò«AozÈívÔ)S¼à:ñž2GÏq¿xBö3c(ì;ÓŸúY…•ôlÒ磿yïä>õ º¥ÈŒˆenûpúFÎíR®ÃxIY¸Öó%0rÉEÄ)¢~ ½;ìÞ9ýô4†ss§ÎšýtöÿæLÏþ;2@h€I¹ž¥ÍÂ#ÓÕ möniè|!*¿‚Ò›9ì›ïvù ÈvJ nó§ ëë—žñUËÜ \z½p72WÝ ];ž¨îÇ÷w0t—¹ÊG¡¹ÆKPª4wnîS_ÍÉö°"Â0Ut`”C'Æá»¾zƒpñ;Ó³Eó $ð2OÏûÒÀŸ9ÿt•‡ (÷¸°à‘Þ?û÷Uó-·H[ΤÔÄ„¿Ñ\eQë– 7Ü‹L€ܶ—k‹Öy²dJ.Úˆ:xÏ×bQ²‚C¦ ×y×ÜéOlFyÀQ¤‘,<¦yöË0ð2Sx™hG‘_ ãñ«ü™™ÞÊ}| K÷Ù¹O¡AØ­€|³ï{õY²srn¬±LBßP0¡Lø[î1›R™ûˆ‘ªÞÿ ¯·s²W!¿õ&Ò>uBÎ3Ýý{¥·Ó$]y^6'wÊNßøŒ:Ñç°3ÏÏ–°ñ­œ©}ýâî*‹ÄPú¾çÏŽ@MÓæe6MæOÿ¤¦ïªo¸"mU<¸NJU<ø/˜›óÄ´Ãñ”¬Àkxÿgæ qðG.lT8A–;У,Á ¨/ò,{K¸ƒ+´Jý¦·ÑNÁ8p¤°ãsžËR\®~:&Ë‚¢Çðߟ=½ý¹Óö"1 ¥ª…üâ·ŽHÏ—ôìÇá ®tªþ^oH«2€Wù®(íAA)ˆð;I-ª| ð‡AŸú”Û}úŠ[ÂeeØx¦FÈ én‹ôÔ0(eL6tž*«7LdH,X^»ä™HZJ¥Óemð¾‹ÞqÇàøGà÷ -Ëý ì-)Ò~?S|ûÒ#£Ni×£8Òñ«âîÉ™™!¹x†ž‚:dJGôÝ '^ÉØ¯‘”´[ïœøìÅç½ØëþdÎHù¥¶%ê*ÇÈ.I‰‘‚hÌd´Ðg¿‰ƒä*d9fß—óf"Žx.ßüʞƄ ‰ò.¼à{á²Ë]¨CñޝÏÈö ­¡7é¥n×9©¶¼¥¾ËO˜v‰S®!Ý ”PôÅÞˤ¨qU£Ç¿¤˜[Crðæßï«:²{`wpþüŸ£bï½>Ÿ~|+gb©"Š÷ C¸s':EѯԦ:ŽÞjÒmF¿5{_š4ÉŽØHÞpø€‡¢% v¿û–ú, O ²Ðn³pÂ×?æp"ƒ)Ò‚(øH;UEÚ Xjþ6ã2œø­Då(ö7 ü–fºÝC[è·—eÜмƒaebkýÆVÍtû†&§nBæIì –8ŽÁIÈsª~‹,†ï\’Ã…ÓXH£2§=w·Sq­w¹Ï>ƒÌˆoöDEÜ%Ž•zqå윩뽂x-°T²)B©Û> ã¾GÛosrhsjßfOÿdat™bÛ‡T4Œ•LŠ üŽÌ Jü8AɸvøùRë+Ìé©<2>{F.½4aÚs¿Í™þĦZÔòá§§~LÎÇÈô<#9”Jz™ÿàèÞÁZ± ì~ï?^LÇÎM‡¬ {…ÙUv!£b›;õ}ïKþÀà4¤H[~bwì·à¤ÅP¡H;àó’''ãÔ“,B‘ÔѼJÈÐí‚dÝ1l˵:S1Ãqwg(tõH;âî6Åé>ûWÜ`pùÛ9SQoÏãîÏy¾¥SvwAƒsm±G&!F9ÚwÙoM3o|ù‘GlÌ_4ï\’M´Ïá´ÞœþD>Àõ®ÞL$‰G(ô†58P–).•6ësÂòá”$ZvðºÙÙÙ…8ÖnAº”‹PÄ»„£S„op°.(ª>Êæ·r¦àÌ£Â)Âj¢GWWì?í¹^Ø)ôÁ>?2¯Üq€?MþZê[>B Ž,»ç©Ò£@Wóc²êÆ8à–QŸeE¾.ºÑ/]ÝPœ,9/õ’ž‹‘Q¹lâÔ\ÿŒ[õü7G€#1ü)Òæ9.ü û¦IŒ«á²ÄWtŒ¿ƒR/ FÁ­ yp¾õ™0EÄ%é²[ZtOγípB”3>ûé-—³P‘ä%Y~õ÷ÞÅëY–~+=U^‚R›dbÊóÏGõ¼&.I©·h¹­h¡ë<£Ð½ýO8{¿‡C¯ÄdvÎcÇq»ît\w}m‘8E|„ßË·<çãî7™¢ëe½:­X’·ý´"Ëâ@ú(ŠE¤×s?ä¡Q\*ƒ|´²5Õº˜Ñm6$|Wî.µà(;m3fïéž’jšU|Öqâ¶-Ä´ÖŒ»dÙÕG–Üo¡nFA Ý…ÿôõè3ÙvŸóÌMŠK^‡ú%´Ž|[ a™¿×Ÿzê!0 —j>𼫔xC·Þ {Þí’þZ\äøò¾œ‹ ?îrK(Šü4æ±êì(Ñ0C(uÝæàÜÙ„º5úM 8Úã¤ê|„U‡R¢F¿s8Ú#@&A²£B? Eðù HÅM:«P¤$ˆ§…“îvm[è  ·ù»OÕ†–Ït+ÊïÊ—´‡"Âe!È4l‡Tñ0Xų(F‘Á F(•›Â©•%0CJåô ‹JÏŸ–û÷–º g3iuÈ„KR‰{ÑuGŒ‚[l«K0ú9¹SŸGƒfwá,~¬äRVK.y3*Ûþ è‚"¢î„ÇÝxãê¨<†þº;AÙ‡FݼË1¢ˆË" ‚çB_Ñä+9“΢bírú¦/óB1¾øØce«@Ææöbœ_»\Ž#8‹ø E k â°páÜœ§ 0O`gó—qÓfü¥¬{gF6®I ‹ª‡ †î·ržÜ…ú$— \.yƒ:[ mÌènAÆ­ÊRÔÜ'ž(ÍÂ@d$ó‘‰ù¬8v`¸E¸tõHuøoŽG@;È:÷¸ìÜÿ€ËV(ËÊÛ&(½=C¿«7J?²Zëóš$ëŽ v9 Œ¢ ºšƒðú%²©þ$‹Çå6† Â¥‰¯ ÃÞ:ƒfú}*“b‹!Q< ™úíÐÍô#\šøºnDÂúžŠ ½¯vÿ²âžÜÜËÈ~ ãeåÔ,ÄHPLôÅÖHºsÇR~-s—¥¥&¿ðøã%µÅGbJ’O’¢im~‚yOžpÍh3¢OçC*3Làô Ý4KKQ~9çuYª.²çÍ›§[š·½XÄòKÏ;ïxŽG€#ÀàÔ…€[B{Q CÓõuùŠ›oMt=´*J­‰æLŠÖˆòø8ŽG€#Px`çeh£D²4) eS†±U'C²çA»ÿœIÑKG€#ÀàpêEé¦?¤¹þF½ GÈC™œ¦ÆŒÇ‘Ö: Τh(#Ààp8u" ¤›…ZíTÖ2?ºzã‰# tú¥ZÓÇ™­åñq8ŽG nÜ24A í³Ó1 ͽÀ·•ç¨Õù`¾r&%´¸_ŽG€#Àà„€rÌ&§„K°xz«¨7¬Vÿår*¬,¿MB†«Ød4Þ[«Ç0>p&% ðxPŽG€#Àà‹€"?wŸçƃOƒ ZÃÿ«Þ…Î<é}ß,k8\2.º ÿ‡÷Rªþ}oŒ¾§ú^ùˆ:“ú}àuA§Áûø ì‘NHþ¹l‚T&7Ñ¡¡—)oä<®¹> Q™”Àʃûâp8Ž€&¢8Ï©˜õœýŠ/­Å@hÚfìÛ0G§Ã€{‘™ Šì®oF‡Ë «÷íPðËÓ°~Á8hÚz´í}‡êg÷êW }¿ `´Ðò;=8¡a“N'¸eIæŒËžþ\$LãGÄÖ~àÙ¬Ýç½Óž Iî+P'K”–Èo&Õ ´cX"(ªÆõ~¨[ôöô'¯Ío<½çõ#žJ+2´6Öº´x¬±„Àܧ§~7>ûé•[ìWn¢?¨KC;ñ¸ÃÀû¡pÛgà´y5OlÚÖ~u3t‘[%»Í;^g®‡ýyÿVßnó5džw5ìEææÌ‘µPzz'21wÀ®Õ/U Wßžæ…ÐÅ´LÜê¸ö:>^è^ÑÃüµ¾pÁ|%&E˜4é%s±¹ô½^÷°[v·DAI²šåÔd«`6¸Ô'˜’­ðkw¹ä³ÅåJI¹]DLgL˜6ãˆ,+/µ2´z#'çN{Q6T^? ùM·ÕíE˜“IŒ ÜêãÊå¥ãšõ²|§k£ßŒsr´.„KjÖŽïYä ‘·ø!õ™tO|9!Ê‹x_ÙŠ÷C‹NW{Ø ÉÍB³Ãf@)}Ì߀I(…ÇÈÛ'LÍ]0gFö§ÞÈÃ|ˆ&…åÄ;˾Üf¶½#ÊBËîZ*ƒzt€^Z V³©q¨@‡YPaW™»r»6ï*„µù{2·ì:4ó¨tøaXÅ3TNº·· 5Žêá‚IQÅ÷Hˆá–û½Ò`0=ƒ Šr÷Ø‚ÑÀWvªP¤~Ö„9a•™w=‘s9¦ Œ ¯‘*ôs$Þ®ÛçH ðlŠÀÒ¼íÏ¢Žà%ý,_.ó°¸iÉÆQz R;°WµÞîüRš÷…ôv£ ©Y7UåØîï½þ­)pÙh÷w¨2x4H«®4Ô8ª‡k&…Ò4ôìy~JrjêÛ­›§)·_3,ÇêØœ¿ ûV©`0Þ?>Ç™æõ# ¡1ƒu»1ÀÊó ã§æCIöcŒ¿B–aSH±ŸÜ Éé=ë {ê௰ç÷·¡ß˜¹páÍßÃéÃk`ߦw½áR2zCñÉ|ïïPJ¤fd×ÄöúSO…¶]ÉOÂÑfR(=bµL½/1QV ó¦Ñçë¸ÅOÉDéaóåçë$Yn)5Àd£]'|sÊë‡/ü9,b¬n‡•¸ñ! ‚ü”Y(“º›~9s»Ö¾ ­ºý Œ éUâøí¿W¶å¯ònçÊ`Éìn°xvWØ€¶R$—MýÞ¤ÕHHëû7ý«ŠÿP~•º“:MÏï‰æ€DÒZÏ!…KBRÒ==;µV¸J(UAÛ0TTzƒŽÌR5„d‹×m‹•džÄHÝæeÁ¨‚ÀÏ<“Ž»z®ì`\‰ÖZ«^«â±žEG7Âá_A›n7×ãÓ󙌼É'ÈÈÛÎÕ³Àe#}’ÐÝI©”I)z< ùËÐc©2šLŠw–|éõ7¤¤ã6ㆠk¢Àß•®fþåÁÇ!Ѭ }^?ü®)1P·5Í,þpØu¦‰þ@Ø™ÉÿñqؽîõãÙøý}(Ey/äð,àvÇŲÊQCzÒ'ì÷h F¾³ds“Œæ—¸_ í h‘‡PYP™LFR ¶4…× ÊGá®Ûþ‰âoÏiPTôFð,¹Ä;ûà„»(‹âô×|Сe~¢Í¤<‹¨×g%YL ·ƒ¢eQ†•Y÷ÅXÚáÕPL ¯á#í®Û~(â¯ÎUh+å™úK‡YÞ0(SÒâгRKÈs\#ãwu+ÝÔÙZg(Zg‰bú(fAŸ‘š’À—z´.Í0ãÃãÄÓEÅd!ˆÊŠÊ,8Í¡§ÏëGèØñ Ѐu;긗ƎÀ²7Óme§n[0S|èÚDØfäËÑBkh¡/ˆÛìã!‰°Æv‹,ƒP.€ù¶œœ íúóh2)”)Íšt‚˜ˆgñp&%€Ц*:È‘Ê*ZR6Ê"¥÷õãØ©bÀ™Yó¥<Ťs8]°÷ðIèÚÎc­²:‘[÷Âɳ¥0¼—êŸâúwÖí¸Æ _2u’Ü®ûle'ï®!™ÅFʲíŒk•íŽpÚ˜%4Ñ`ŸâæîP¬°ªì6É&§È‚N¼nÎÓSvF‚øh0)ÄŒÐE³su ÂÂjpeê›_À©³%¨‡œh…ž[Ã-—Ÿx¸aØ8ì;íZ4³©ê!OaG…p­”•ÝéR"œ,K'¦êG]y¦º3¨{;¸nÔ€*Þæ/ß’,Á„.®ò>R?9Ïü U¢Cz\®³ž{·]u¡úÛ_ºù{û_ý ¯Lþ3ô5›?1)[÷itL aÑuÛ_ðwZÒY0Ëp© KÈnçÕXñÐ`|¥Ã¯_ý»©ñÐ?‡üùêò[:ŽH˜£³Šg+=ÅøÓ©¬-¿Q²Aš Ý-sž~*ô}Ôõäµf/UO€?ÓL™¡ŠHCŒJ»`ô9†õë { OÀçK×àvÅæ0¤WǰpKÌþüGx⎫!Ó»3êZ3éa ©œ¨Ì¢åb²~›ù;ÿpQäY:?DÝ}ýh–š»„Ï–¬†®í[ªç4ùñ ý»¶…žúgPüùoTï¦n7*yfjG ú’Ž/c¢† 7'¼i±6}ÔÄh‘µîË6_çRÄ_*»'yˆõc]¬KTlr2ìt^{œçëuLtcçN*°k‡®Î/Ñ`RØL™î*£ ’B%9Á Z¥«×¦`ÝÖ=^&eõ–=°`ÅF8[bƒö-›Á­c†BzZ2ØìNÈýç7êïî<»“Þýj9¤$šáÚýáõÿ.»Ã/¼pÃ8̸ÿàr»aÞâ5°aû~URsñ î0zˆÇJàÇ߯„ÖÍ› T§~Ù°&Þt tlQg¡Eò#Í0~ß«F[Ó8}ß´bª~›ÏO­ÜÆ ¹êØ´ã üü{ô>¯5,^¹¥jF9°«WBñ/¬3‰VÜ8švã¬Ù²¾ýe<}ïXX¾~;,]þå HI²ÂOë¶ÁOýNJ¨i8£I2de6…v-Óá‡5ù°÷Ðq•I!~Û´.ê×>]¼Z¥¥sÛXW«éèt"”–;àýo–Ã. Ó¥i´ÚçqN—¾ùy=l(8å'X1$zœrû•`µk­Ó,|¬Ý nÇœž PÛ’NER¨P Q÷ÚºÓJ€Jc¬oå>¾ ÏNäV, V”ßÕ©½a•ØÕô#„à6ȸQ“b·c(4Åe£fú½açò°«;œ”ÚC²î82ØåD8%wOºÛ¬Š ˆsMú¤'^Ïy°8ìÄê‰ L ‘à;Es†^Oö=ŸO•ÂÁ£§½È”¬¼÷õr I ‰Ïi!æ#gÂõªÎÁ)ôïtIÞ¸‹KË¥í C¦dPö°óÀQ¸—š¦$¨~–¯ß ›wÂ× ‡¢²røðÛ_¡_—,•é).³-¤$Zઋú@fÓ˜¾°òòæ1Â,=ºÇ\ý4ïT–ŠâÑ5vº\¸lRåÈÔþñÒÁXþUFapê2`QYÕ­‡6dlO•©I]Чü¸f+|¶t-†ÿ[¶Æ ëí—A¡Û÷…ã§‹Q’rì¨s2¸BH4lÛ{X­Ûƒñì¾]Ú‰3Å@õ—>Ô´ˆ©Ùõýº‘ý¡ i]øëf¬—FeÝÖ½ðû¶ýðÄW©ÒÆÙŸ/ƒÇþ:Feœ~X³­Ö:­F»ÿX]‹] 9e1@ K:Èп'À¼>fŠc7l›LU¯¦››;m÷øçž ¥ÒŒÝÎ îÙïêm ëum¿C²x¼fŸ7.Ň\½`»c”dWt‰ºÓîKõ¯„5®ÓIÌí×JN…Æ/…&Žè1e+tßxë­Ü©=ï#ÿ?¬ÌIëü—T‘iá$kó÷ªvÔI¹|¨Gº±zónu–{ûÕªÉ$XLðêÇ‹q8­Òkß2F3Ó6(!G:)Lr P4£nÞ4I½èýú‚ý˜^/Õ¯Ýá†ÜûÆ€É3:, QF1W?Ô óßFöƒn¸üB»•y»T†¢Oç6uƪ×éTiÌ‹|GO…Œ&IpÙùµŸÏ±è·<5>’ŠœÌHJü˜#fúú«±>õ1)¾®`ßaÔ¯iRžnêë]ŽAQ©‡ÚÌOëŒ4d ­˜ÔoÉÕW§UO±ù¯!êvl"Á© ¶¤óýLñ!äó»Ô3×XÒ L"2÷‰'Š˜û'L{îI1MÙãúÇÝΡF³Pînªß£OOƒY,Æœ’¢‡r9 ФLå´œ¥ÈŠH*ikQº±°Tj’³Õq)t7…f™ÞŒÉ:ÛM²•bQ¼Ð¨Ótë]B¢ÉtöÅÇóÌ¢‚F-¼ÑdR|( o÷E‰Æ—?®C‰I+a›TÏ—A« ³A/Ú4oª¾?…3]LŠ„"þÚž‰;P²²Åéë¶îóz£ Ìuj“K #«ï±Q?´ m OÝILðÔ-J7ü9뉯ëÔ¦¹º‡”°ÇU«",…yð–ÑêrÏi¬·³±ùdáJ7v¤7:<òÀûìû@KSûQùvhïó¼¯iÙhó®CêïnØ&>þþ7uÙªðøHMJP®@ê´7BþÀh„³¤LöçLbú¿u|΋‰nÛì0ê°«Ç0”d´ÂQÆ;^ãªnùòðÝ/‚høÃm¦tÆeç¶Ùá~W‰œ¡t3þ $뎔üY¹:{Â^×ù’¤ÝØáÜi]“€COÞL 1;sÉÊl¢.ñˆ|þòõ0°{[uÖH3×u[÷{³zì41¹äYÕg¥3G8[ÚaïØ–€hfK:â.«PˆQc—N K:ÁR<7gòI ónÅEj½ÿx1Ýdp 6Î>çñÇ‹=ú-Ucž;}ê¸ ÓžÙ~ÔÝ%爫«Õ"ËéúÝbšxô‚•B] ÃíÏ9A5$W&£>¤»dS’uØ pP¾ãÔ¹Ó#³¸*µýò߃6_Ô!ø^Á„¸ßKP‘uùïÛq‡Ï:¸ëºáªTeéê­¨„¸ —iZÁ’UùêòO‡Vª>Aëæi¸V¿º âZ”ŽØP©9êôi0!é )æZÌFUÿduþnU¿¥î :z²ˆ1!EÜØsªƒ•U´Ècé±{´Ò 9³%娧AýˆÇµÁ:Œ£zð*Ç9qŠËí¿ç78íû`þ dš;À — €œ9_Áb¬ƒl9Òë±âáè©"UWjÚ?Ù´ó Z«û©íw×v-Õ¥ÇÝÚ ™õm(¹I®PÎÍß]ˆz*…ííŒ@Z%fŽtªâ§N3ª¤n³Äù=N %{é©ÛqI‡ ¯i¶¤Nö+ïì—‡üFWáo&JbÞÓIŽ«|åAgßË@¿ÊŽ8ÝFEù ºMt( ª´|]±ìä7î†z-&¥¡òPº¤KrÃ%ƒ`Î?ª[’iéçO—3Øû†ûýßQCyðÆ ùglL9†6¤¾'†œ]1}Ê£pSŽßZ—»µ-õÕØ¾ûž{c.m.(Zýï§Ÿ¼Œ%Ó <(£ýwè磵wÌíññ3Ôþß µŸsL›ô/ïÑõÁ޵ýIùvíVúdÁwCèùõgZÞŸvz¶eóq¹n»?æûnÒaa/®ƒ_Óå'›tb¾Kq'`û¿cÒ† Änˆ àX}¾ÐØ$:l8¬ŠB@  ‚Ô~ÎáT ŸÌ*«¬æPù•´‘ã®ä°Cy8ÁVÏ´z;"M1éðGæ_í éÿ ^kÚ,ŽˆE¬Ë¬HJ¬ß!%ŸB ‡ÖŠ;9¸ÜV²Y,4ˆýh¦Ÿ¦ß"©&­F 1“70Ç`4¾X_àµV7®*hФ´6u‘B þ€o–Œªÿ½U=ŒWÂM:m=K'^1mË~)’Ò–h«¶ …€B MhÔ¤C´…m—/)“N›ÜŽ7¢HJ‹¡S*¢Àx*2fŒa†˜* …@ãÀ¤ðzïqV—ÜÆtêÌÒá„I碇}ó‚AOäÒi¼vuF[# HJ[#®ÚS4D¢•ù€šq™:U!Щ¨mÒar©fN@Pw–f_Ö_†=—?Þè7ÙøÈà]õŸ¡ö¶%š¤ìáÞˆïзGýì–KB¸­Ê/¤·æ® ç9%½* ŽŽòï¼ÃÏ3‚ž?a˜_É ¿X²‘Æq®ž/9"-â— 1 ä|?OÏy©nžÁµUQÄ)M5éµ®¯M{¤˜sÏœz–”.œöa–_óÀݧ°u¨nuh’¤1ùG¼íÌ‘:øJX…@S@Χ«82ÿýñÕÏ蹯َ­ÝFŸ Ç©‰„,Åv›™²  O®¥’²!î›ÒŽ:G!ÐQhŠIGÓŒ/LÔ÷eФsr›Û˜æ­ÑnÐtÝa1ZßÜð`î~ôÝð?ÁQ[ÃQè9æå¯¹«yGÁ$^åìð$…ƒïˆ ®s©Ós{‡¢Â†ß°…k¶Š/L¼äs{gÓEgdíKW ê‰—>¤+ÏË_¥8D Ü›£¡×?_Bå•NMÞGG®!|ÁVq(ó¡ýºÓ.:S$&ÌÛ±C—o¢}‡KE&d´ì·oÍYΉÙœÍ¶‚ò8dyvF2Ýzéy"ºg0üùRŠ1)F ìIWNêÚšËn!w/Nrx×Õ“…BC2„÷YmÇyêï9§ Zº~çÒáìÄY©üB ˆ|PƒûtãȰ^&,‡é‰Û/ù~Lðý¯VŪ'˜tæ»idM•óVþý—N|;yÛYˆÖ6!Çn€íþóÅëë­ ‰×®˜<–ºáBòúü'w”ª½3ï;AtÎ=}-^·Ã×Ï_£çOÊù{6Sáñ„måLbàÈxÎééîk¦ .âóøê-Î<Û‡3ÞÞÏiGìÅ “„,o>ëÛu”šh§ï_x}ºp8†$m½øš»®šD·°Z~Uþ.Σ²[C’8´Õ+;ƒM¤ásAiHq‘ú/.°YOD<¶[ÍTåt…úÙ›³£ ǞäãÁSøySE!À¤3çφs.-ºîŸÇ¦—ÔŠ ‹Y:÷ ™=¦ÿ,ð@ 4ÜsÍ忎µ%Ÿn}hÄ3[ò3Ÿ_$hëÃ#ÿ4SÓšÁðWþ}ÏL]‹1²a4bÿH\Ü3'ë»lÒš¿j ³ÆB؀°É d‘Å‹{kR6ï: ´(òhO&2A¹dâiÉо=D”©g ¹S¶§"…=Ïh>¿K²ƒ†È¡u[‹DdNœ³“•³Þ^ÀäyPdAŠ{hk&°Z~2çVAòAY.`ƒþ9™B]Ü+È£‚²rS¡ˆzÛeçÒiœÌ ² 4$ƒ8¨þ‹K øy9)>À ‡öíV§ŸÈ/…„•«9ñ%ÈÊŽ«ŠB ##“Î6<ϳtö³ûøKLLrÃúàíÏÙ¤3íâŸéC/~4ðRÐç$ì Þ<ý…]‡=?%|/×ef3O(ÝÇÖ‡G|Äu× ~.ïjyÞæ‡å뤽û̦‹ä>µn:¼¹GÂuø·lÖy÷ËïBªqy ùFÖl)Ú„ñÆK L,(=²ºˆµ‰SÚ[8J°Œ¶ÈüêyxxR=“› _Àçâ|ñ_° Ö†L¦ÿÎY&Æ!×ɦŸ ›ß-ãDÊ&"•¬]ÇIJ¼5gíÜW,LBe5B[‚zK+ªØÄsrrì?• 8®Jü!ËæœWg-f’RJOEé©It¤ ©•N.ȘüÑüÕ4ûÛõ”Û'ëäƒê—B  M:Ì!îc“Îô–štdWk<ΗXk2~ÌËúÀ5wi^ìçѵ¬yÿ“4d;öY úB_ûHÑòõýìígóžñÌÆÁyÜŠóTi{âÂÜ#aÐ3‹]ûœ e)-¯› G9IÚ2vFåä?yú)×£õ‰Öà§‚ìµìÐZ°·˜0EtÕæ]™þ( Ö0?‚ÒŠj–i;dUý² ;Øä”Å)Êxr¢ƒ5:Ú\¸_hT¤ƒÙéö'vûê»|*e­ËмìOããA¨~äuj¿H‚rª‚¬C[¨ŠB #  “Nƒý héd0=nÒÿÐ5ý'ò<øœ0yŠÞ_Ë}yŒ,djR5ö•ƒ!g.œgдÇc²"熈Ú××7óî+dUb©ÅXàW‚˜˜¡ƒòë».šÏk«ý%›`·ÐÃ7]DN—G8Ê ø? ˜Ž0}ÔÆ³6‹4_]û½ñTít ’‚}( É<ªþWpÜž~Î(…B f€I§¦úèͳñ «î¸eã#s{ÔxÜwñúÉ;«Ñ‹Ðà_r3ÎÉ¿v˜gÌ3Û¾Wð¼2ø™¼G)@[52Õµã7X¡:ÐVth’Òz>’%)ÁV§:³éÔpÚY“S_©MPÂÏso}mᜆö‡_¯¶ …@{"x}D’kYÚüñõ)_l(ðZ}²³¹|½W«1ù¦›^Ô5ßÜI ô?-œ¬Õ%!1ãꊣ3G>]yœØÐš‡sKøú+ë«_í‹”ñ:Ê÷"Ñae-HÈÌåÖTõ …€B ý€Iç‹§LS?ÿ³öÏÒÙÎΫ?eíIÈ·ƒ•Âåi±Z¬§?ªÏ”ºrÿK^k:Þü̦iáG-fÛßô€v;ÏÚ1oydè&®k{ñÚM jBŽ›qÞtk®;ÃëQÛ±À©?ýc_þ˜—ð†égżŒJÀ“àìIqtð»5NÒ²`;ü·Ü ëH÷} ïk,÷=ŠøãæŸxNÞni³á$¶Ã·´Îˆ\×|“ΉÀ„á {&h€üƒôî†/hOàž |^î3ysS-¶‡¾»o`ÅÆûîc“ÏwrÖgäéÂÚ#\GÃ>$&ÓKšß7#¼µû(’û÷HIeäà\t „òy–Uiy5«ª!û1…Þþ¡f s|x²rü”‡˜?´_v”æ‰<««=nÙoDkFÜ $ìDÌF+¯®3ØNÐÌ~ãôî{ zÓÔK$)–ÚpÓO1Ælµ~±èM­ßñĦVtªót*ÙÑó Ð÷üþyÿ~æwpE€³v!-‘6é´Ày¬)ž#Ÿ°åel&½ÜãÚÈ¿ïÚúððy&2¾à§ÀS¼ÿë1ëž.<äùÍ£·<0t-ï«S¶>0d7ï|±Îµ#¦P$%¦o.š`™¿Šs;ñì+dÆÀêÓlä2ØÈOõÇËi‰LFr’-PF&ÝEŸsî§Ä;M“K“8O”Õj ––ÔÝÜkÐo¿?@nvÚ^¸v+}»nUE©ÿ±Ô÷æbÕŒóAN@LŒ“&]îè=vø=V«ù^™Ít‹V°*4¹#bb÷‘5àÔ’u!Á`0sçã¿9äóéÿ[²eÏK³f½Š*’°4£ Í?µ»)?í¸I§ÁÀk‹åÅ p6”Kgø³y=­‰ÃÉ`k‚µ"ÇHÓELÍhøGÀï[š˜4"·ºjÓGüð¾ÊZ•ʼnËCÇ<.SîsycgjÚêܧóþF~œcoAªÄФÄÇ}T½h&¤7qšƒ79âokMÊÌYTìJGyíÓ"ëd.šI÷Rº·˜²<ûhÖ¢õL¶ÓõSÇqŠ…žbV´+Ñ,’œx½^¡5zïë5‚œµEÿÛ»ïQÄU®ß|ý½MMJIþ““n]ÍzOÓÊ2mÓ,š+rŒ7Øñ xtûri¯oTÖaðÇì½ï»±ÿÏîzãù?ÏåÓúd%¢EštœçÆÙµc®|d-‹è–“géÔoÒ‘ByuýY_ežóäÜÀdCÈË×c-‰ )¬98øéË««7_ÃùvÞ:ó¹½§—ùËŸ„V…Í<óȯ!ÊÍ–ýÿ¼Õ´¡ðkOG–m©uÇC@‘”(ݳoVná˜) Ÿî¥¦TµÍ@@j¾^™Ï1hÖQµ1‰¶'žM妌fÔÒòSA€Š-9bIñ•Ð g½òñ"š1q$Ïqr0e^šZÞJÝ+e¿}œ`ÓãñÐN!ñŲ|ªýÝ&ýo¯¾×E#b{¤öïQËþü>›=á·IÆb}¤õ3Ê0áxT “êiÞ€E+1÷¦®™º#ó“[}â±×žzÉó0Û%"ZaÒñyïå\:·²Q)™ JxßÄ,Ý`|áâ‡}_­NÅáÇÜîg±ßXèq~Îæšø¤{q"[+Ót-”OD3𠞀ýykùƒ=YåI {~ÓG~ý›M_½8³ñƒy?rtðÓ›nö™œõÎðA½ªt<¢ûÙÃxTÕpvâ…kéåç‹h¯È8Œ•ÿ»œ¹©ýãƒù" b¬,ሱ¯|¸€ÿí$Ÿ?˜› Éþ>å:d1YÎm…ö«›h«Ñ_ùha`À—’¾ýó£…œYy ÕpÖZ„éGCÔóÖœå²*µŽr þš£ø~<-1u§5 ç¶É]_w@ŒÖ$ž+䘵h8!O´5REöu»ÜnÑÎK7QI;ö¿­ú) ë©G¨Ýl?¸ÿ‘˜ <™cÎ×Îu¼bd‚RÏ%ÑÝ…6ÏMxÙÌfëŸnyäñ¸Eȇ÷|‹?;ÚœgLÓB³ttý~~4“eOüºÙÇŽUÏËY:A‚"6m=ç¾n“Ýx¹NÚ8ö7ù ®2é>f@As~oyhè >nö̦qø’ÿÀð¥iÆÔQFÍpæÖ{‡Å>pËûñÈ2l«tZ’òÐ^NØ6–“~ÂAfu1!Y´v› *{e‹ìÄÿ樰ë·ÑXŽ»ˆÕó|µJÜý܇gÛ'a÷#´¯¸T$ D ·½ºq6å¡B Ís—m Ie¶ˆÏûdAÜìfgM„ØG¶cU¢‡ÿ¼{ù~¯£sÊOËñ"­…ožühr@žÙ‹7ŠhÉ’¨4¯¦†ÏF¿%AÙÀÏñ¼ï¶ÆDÿÛ¢ï £Òê#xw ‚rù­wMw$&ÿšÉ>Öþ®f æ±ku-©mCÈb¶Ø~ÿý{¸è¸œÍz×äóùS†{8ðÚvÌ…éèb&'áDgË÷ù;æVýl9g ~0èsÒ‰ƒ×äß3¬Êd0°¬«Ø·ä>Mw”r{!M Î2jô‚O×ás*Ъl~hغеw4ëÁ—Þ#ÂkáþÃtÅä14fHšºjÊ8=¸7]rîi´fkÑ)¿t38K­ƒÇa¨²˜ÁÁ!=%‰ö*ã lVÎʼ;TGvz ÝxÉÙ„YªD Ô.——Þš·R˜x¶8NNC-¬òÔ’èý¯×R5kù }ƒÌ­-¨u¹ÙÄSΙµ?]œ'Ú‰¥þG«ï­Åî×ã½ µW¯A]ºvëö|2›xN³}>ˆŸâòè‚,ɆÔœšò·‘#Ï‚æò6ú¾¹tž2ü…M:ûùõW& ¹aÒ¤óuM½øgúÐížóøÈÒú‡ôxù+Õ­–©ì0{¿×è<›_œ'EÌÔzÞceʧaò¨ÍN€@£mtLm(ÚNMR$ú©ÉtÁ„¡ò§ZÇÐZA{UÎñoê¾#—ÁJlšÁš·æÊ-û:Ülæ©æ¥#ô?}o.VÍ8C;Þ™ )V£Ù”iÓ8™oŒ›¡ÜàÓìIðõgÎKº16L:1›/FP$…oœc§Ÿ3*Fn‰#RÀä!Í/¢¹x"%c}õøyôz0 ™ef‚%ûÑT“<×¢ÌîñzY£Ä™cb½´¶ïQìÈhRQ1Œ³æ‰y’b"ÿ³ò³D•UníͲJÓ w=ëÙÆÁhk¹£D=UµB …Äþ[«…S—)€€4{`àîHE’+ ô¡¹Eö;HTðùÜqúßÚ¾7«fœ/Í=lj dï ÅåOÚûÔÛ¦Kwì÷ òTv±•˜ ƃû(Œ©Qãs¤7뀄Ó5Ä–ýhê}çãZL›çˆ¡pEè0¥5}b'¥&EjSŒŒo‡!)΀½” Їñ ïGáRU+"ƒ€Ò¤ÔÂQ¾àånünm WÓc;üwkëVןy?[OÝNIJÀ¬¥üÍ­_^‡: U‰ÄsÜ\Z|~+ûÞâv¿0|€?ázÚøu±rF¸ü±"“’C!pJ:=I‘/s¼Èál¸çàQ1]µ´¢š-k8¨'ÔlÍwü[ ÁßRm6A„¿ïÝ-=ÿB‘–S>£­>Ø¡èã½Å#'ŸÍ– ¯ïhýDß[ŠY®ëȽ”½ ÝT§(bNKRðâÚí<ûÁËÑH·Ò·6¼Šg@!Ši ˜ áÐ|a#9É(ãìž.ú|ÉFJä(¶SÆär·!dµšB„%6 %…B q0â™8j²—É}ç,QðIÑ8Æ+ÿÓuö#RE! ^êINè*¿p?½÷õaï!ËCEÀ¯hÅ“0é^J÷S–gÍZ´^„j¿~ê8> §ˆÙÒÜxîæ5£Ã‘Ô ¤Ù5úꆺùÓ*zé¢DÊLÄ}rõ÷côKhíA?½´ŠÉnKsúÒœs›*âŒAVºg¼¦ÿ·œP¢'§8hr ±‚VîóÑã ª9K­F_2F×~PA¥‘d£ÑŸ¦ö;켺7<ì`K6»ç^AÆ?@‹ÞœD£…Lx€zä^EF³nŸEù ~A–„®tÞKhù{3¨ªt{Kši蚈÷§¡†Ô~…@kèTš¼ì@P0Ó2p"·/–åS1‰¶'Žn“œ. ?Å–±¤øJh3^ùx͘8’sú ÑE[½5A<_‹{‰Aîî1vZsÐGù‡}ô“¹Ud{Åß9ÆF] B£ð·U.zyF"½µÉEeÎÖi¤ì-‘?üZŠÖ3kKî?ÃF]é$(M¦ 0ÓE¬tå{åTáÑé¿W$ÓFXù¸‹>Üâ¢ϰӿ¨jM“!SWKúÞª†O}1îzørê³OqT3˜ià™PÁŠçØ® ì~RÏ!ß§usî"¿ÏIc.yœå{¨píßißæwhÐY?§µŸÝvŠ9tòˆì´¤+"Š@§!)xÙ x½>‘¼mþªÍ4oÅ*1÷ ¤Šo¬°Hª¶&ñ\R³Žµ*„ëËù㆒™Ãè+¢Ñç¼U•%Y5º~„…nŸxóŠÙOéxI°h4¶‡‰þ¸8¨9‘Ùy4´åyy}ñ 3G.#š½ “CˆÒlªpë´¯‚ ?ÏŽ.© P¢%èGú¯unZps L7ÒŽ£-Ó¦l+ñÑeoW‘‰ŸÁê#ƒ™ƒðÕŠÜÜ|<'p@¤ c=}íþæ_ËWÀgÍ뢀»šüÕÇÈ_YBNŠmQuÝ]ÆQ_­t`ÛÇâúì—Б¢Tº…ø½7ÿ¿Ôµß‚¤ìZûštëw”˜žKUG9¼‰* N„@GôPoÑí‘!Â7l+¢yßm%?al»Ù #Ȳ4{ñFÚ¸c RW•Ø@ _š MØ|¤î {Õ G…Õé³Áçó ;$#~øÿ  #mcÂÁÝen‡ÊÝú÷eIôÜ´ÊLÐèÍnqìPU€Ž¹tœ?ýö:²ÿ'vBG·„|O|¾2Y“C˜mi”Ôeøíª:H^×1JÎPQ±C©Nƒ@§x“HRÖ—WTѧ‹ó¨Æ$4(±r§¡ÍSUIï½–ú÷È޵˜õ£|Tš‡àÈy R§¢R/í<â¡ÂC.:T‚¼pÍ/}RŒTZ£S›6 tç7Œ´Ò‡[=ä Þ¹‡ÛšÀÚ•x)}¹ÿ» Í !³X«òà™v‘ÿêµõ.Öªœ pE|n¯T £JC$¤ô£êc»B‡ïœG£¦½HÃÏŠ4ƒ‰ºöšÌÊ›˜â\GjŸÐùjC!ÐYˆŸ7iw f´‚ ²Á.á<ÕN7û ´¯¥¶¸Ð¨l· GÕRZȾ2SÏ2ù¨)ʵÑ"áïq/›°Àô°—ɶVr\\RÑ»i4“2®´ tK4ÐAÖÔ.“ûš)'ÙHoçq^¶°‚s³ùšx)èÿZ6cÉrÍP6}¶Ñ —B›b`Bý§%5âô¿{+úŸËZ¨_NI¦ô.)”œ”Hv»Êäµåå¹7æÒæ‚¢Õÿ~úÉ˸– ^`Ÿ;Á¯Øyº#—ìI÷?ú¿¼ÂK³‹=©;;¸2tÝ¡‚ÏÉç­¦C®!gå>Ú“÷:eö›:îª<@¶¤¡ßjC!ÐYˆ{’Ô¢ýPª9îɪíûÅ,øƒÄZLeæ,ZšWHçÈN´ðMÑZý‚޵~6E?û'•Ÿ û°]É$Dì Pµ÷d­FSêlé9ÅÕʪg6ÏM£l´h·÷$-ÚÈb_Šâê¶“¯¥ýjêuè?L:²L`¡¯ ½´îP¸ü›5)w0iùÓ’àÀjžøé¿ìw$×®ªCdMÈ>©Ê’¢…„eÜåoQÙÁÕbÿÙ’²éèÞ¥¡ßjC!ÐYˆk’Ò¢ðTc—ËM»öá´õ*väÄìý-6çPZM1ífYöé.J¼:Ñzf[†Ñ_5ÀïræÞÁ<g¦û­·Û¬ª°Ó«o–·è>a8…&#'I£l›•Ò~:²Õ@û]-ªN˜:2‚“lõq“O.;†Â¤sç캳XzÃß8ÊJð=Y¼Ç[GÈá]M´d_SzQçҘܱ•§°s¶°‚äí| b’öñuÉÌè;Ž“òwžzrˆ˜2[J⇤Eã¦Tɧng°OQ8Ï&wFg\ó©Ø®*ÙFß}pÕ”ïMÛ»‘ÅÞ…*lކ(ªN…@L#×$¦ TkRxVOy•“|œ²O'„<™àösïœK•G·Æ+ª_ ˆs’¢óôÐ Iq³™§š„ºõââø 5.AR ?r µWiPš5"ІxåG3Äl†¨þ»’IM¡î÷qü‰¾ÒG]û6UŸÓÇvÛ¸™¹qZ{u»N»Ð¼wM2åb:î)´ßn¥9Úlk¹Õ w ÊŸ—:…³ì‡LRä-ÆcNPº84šØÛÄñdNv$nGÑc¶i=࣭K~GýÆü˜ƒµ½Ërêäq–Ö‘×âH§ŒÞçÑêOo¬sLíPtâ–¤À%è“âç³>1»ÇëeÍŠû]ös¶¯Ç%ä†&Hö%Z& ¨˜)SèM§¢ÄÔìO.{F³8rXÝ»5Ú¶­øc{›Ø ù©kM× m§7`*Üöpn ï¯3kÂ~ÿ£7©kLýýaú1BÂ7VœŸôü]^Ârª‚iÚSßÀÄUš‚Àá¯Ë©Š§æ(-z}â©NQÇq@ìØ­€_š{0Ð#>ö­¨­m/• ²£­)2nˆœ¢‹™2{0e—‰ ¶OÌ”Àþ!”ÃÄâz´Ç4¯Ñf¥6„O$„‰I!ÛwvjfC¡¡›^”í°“u×ÖÙ@ì°˜MFfC'œ;#ÛJdk3é>29ÑÜqÙ[BNïåjDÀV#×ë%}bñZ|¿Þ«[ð›ŽÝâÕ­º?à{É»Ò*É'#·$EjÀK'%ÄÉ}é_ ò˾œj°’Ú7DLÕE&!ˆÚò¾×Õ†ÚNƒQ+ Ó†Ä$–áx%Ø,œ…ºqMH,tÄp‘Íb=I”ð¾œt žõkeÒƒzc½´¶ïQì_?!ËÍqíR°?¦IŠKOÖY3[Û–T§?QÄNU­h1qKR€ˆÜ1àCߦ°¨[öÚC¤LjC‚$¤>mHó{ˆ©µ˜ã>ÆùH*Úï¶ý¯ÁhÞbLHÚjêëØÕÞÚæ÷¨îÉ+™t/Þ˜vœ†|&rót焺hÅ»ÙÈñv1Ýÿhõ½°òRWMõž­ytY´Ø$€Í£'hìïR|ÊΨƒ E ®I 0—ƒ|‡"(,w‰3@/¬`Lö¡)öpœ™tŽ£Ð’‚Ϩ½²ÓBæžVºýï’`¦Â£Î˜î$ûÞRÌšp¾tIJgûÖ=úö¿®Ø—K=ÍšpiÛŸÙXXmï΂URî¶—Bµ¨h9qORZMÓ¯4s »Ê@_M¿²á3KØ q5Oå4:8 ¬-À™`>G¤6qCz|ˆ˜! &FêÁA·Ï¢3 ÐX2SÉj1S–w_L“Èÿ™ôd΋ƒÇåoסL_O¶›9ë®ÓýTß›‹U3ξ&°iõŠã§\P¾×<*™IJLš|öúFñD"ϱ5‹çoc™ÃåoF·Õ© öC nIJ44'/MO$özny0F"Žþb¢ƒta§L~EMø¿ršÖßD?É1%Þ©o„HÝZžîKÙL6 éÅÄC/C³ZÚHµõ„j¾7FöÉš“FîÂbJñ•P,¦E€\i¬Ié“ɹp8W dfŽÒl¢R»ß²®l×﯌ÍþGªïQ|nÃxLºËÞÂ_s‡^]bîM¦¢(6ßüªK|½é°w€v`OÁl)/¯ÃûÑüJÕ 6F nI p 7õ´Öet¶‰ÎÌ1Ó މ[4*ÛH¯ÌH¤÷óÝ„À^{ØAµ’ƒ~}Ìq4ÎN—rØðO9SlKK›Fw¶Ò åvO¢~]de¿Uš‡´HN‡ehï®´yo) tæÑÚÄs Ic¥t? ¬É³zúer$WÌF:.wKù…÷u!T÷$3ç ˆvÖ&ÅNÿ#Ý÷(ÞSL³“S¥|K¿˜ýuÏ~ý¾·Ñ5#éÜ„— &íÔÓ´£(ÛIUût3±L€Ï]¾xö'ŸAV^¤ì­›.xRKê‡B ºtR#@óA½sŒ H0Ú&®¾c´]’';iÓa!*)>Qøê nÂù­)]ù«÷¢ßÃ,´%Õ\Ó ¥Fƒ=i;çñÝ'ü4¤f]kªŽøµCjÖRB ‚eð,$›™,‹²Ë~4µQy¾ì7ê²°©Ëb1QNB@´ƒöb¥D²ïQì“Ô@` Ç€/OMM•sí’Åÿ­dÒzו8'& dLV,ÿ¿ªªrL?†¼’¨È¾Ä„¬J…À©P$åTè„Ìéã·†E×ÝD;Jôëóôçï%Иn'”R›ø84º‘ýTXW¯JS/M¹nÊ5M:Y¤M<Ðc¶Z-Ô³k dsJ†w? «^EøŠoÏ‚ö!G†÷'D4PVjûÎX„¬²£Í-µû¬ÓJiuµúD{íÝÿhõ½a¬`4 ™;>­á#Ð@H‚‚¼bY¿lQ^Á¦ ŸíóÓV;¯Ó¡Åh¯‚¶!dÙµuÓ‡«¿ýf”“×’¨(MJ{Ý Õn³P$¥ YMìpÈéçws6XöA¤d«Fwœn££ìàŠòæ•I4"+HTvsâ9 +½ØD•Fv‚d¹¼Þ:ûÃNiò¦4{`À·Y­d³ÛhP÷Tê“fõèªo…J“+Œà‰ðÃ]¹HÈ‘íЩgšì,d„¬ÚÖ˜{Âû-ê¶Ú(“3:§›=íÚÿhö½¾[äòxuC¸%Ï^`µì1çØy|q-øôƒovlZ?›É¾¨æÎüAÚº ÍEÕw ÃÎÍ?úúÃw¿—‘·!7äW$…AP¥c pâó¿cÈÛ.Rvc‚‚rƒ¢¡àÍÈåï«ô.û¤  欰ç÷5S^±W! Q7žö[PÚ¾_êB¸öŸ×å.;V^ ›¯B¨ÕW˜>0Л9›4鄹]nLÃ1*,«¢Óª–R™)KÌúAêh& D,LµÍòì£4_±p¸îÎ3¸²R,›Y>ÈY!³4÷ÔêV£?ê·›mz}^êÊHÍ<βүÍúßV}¯œcÕœ"ÊÇiÐ[UðLâŽ' ( =°ë"òžyá§.8zèà‘±çN¾z‰ÿ‡É™æ½§iƒ–eÚµ8*ˆƒ‚iƘÅ'Y¿×U¹fñ—ooX¾ö<ȇŒ˜XC^È ùÑUERšp›‹ñ’(+Aã7Á©Æ‡™°$q4YªøÃ߯ø“`ë0;)ªÒdð⋳¦º¤ÒéÖj˜L8ؤ5%8XȾëJLH $›ôx½ÔS%$˜*iß17<Å”VÃ1JøÆyyÌqs"ÊHæyBHzDS5ÃBÀ½„'Õä¡®v“›+))‘Ä6d…ÌÁ>Áçª98œªßHLµà$»›cñx d`ÂÒþ·ußkã„çÏ“×í>ÀÇZ3@ãZ òR“€Ô ޥ†¼ï–mÚ±a}áS§ŸíËÍ=ç°e@ß=Ý¢Uë6­\3kžæßL®¸vA8~D»E 6Jóy\U»·o\´âË/¾u:«ã²U_cš©Ii \* ¶C@‘”&`]Ä~&kÂ$MôÍn/]3Ì"~§Ù5Ŧžg8S,Jož"Œ²›gü¨Ò$ðÒ”K`ïŽmë$+¯`?MÞ¯Iœê$˜Kà8 ¿ h)¼LPÂs"d:]TéòSkz|œ-òMÞÚ™aAÅ›.F1“ D³Î&CŽ­ÃæhL@œRR’(9™–²Bæ–˜z$õÇFŽÂëñR 9ùÑv8Ñîað*ž×úñ´½ú.1ÀÏÏôÓŠ÷íYÂ?ås~Js¶ñG—T¨ÐPÀðÒ¶p¹jü g}0!Ñ·†êÙ{Pî GbrªÕaO2™ì­cÝÜŠÏÇ”«†¹|Õ®²¢[·íØ´aï ÁK¤9 °€°@NÈ ¹ÕK‰AP¥ã  HJïœf‡dœ˜²ú§%NznZýçòDŽ« ÓϺCA3¼«‘v±ÿŠÛ'‡¹&6Ò9O“ƒÖøJõç­Z^0þüiå«ò “™¤´z¤ ixà0ãf–‚ÈÙdÂì%{}"û44 ‘L¥Ж$f³‰ýN`~²4( (©))bÄ3rZªE‘QSûír»˜¸ùÈÂÙ“8ýB$û YÚ£ï¬ù9bŽ¢—.üì£<þþ¼…ŸÖÔmùœ‚€˜à¥ ŸQI``VÈ\¿a'/»xçÊóx³Õr ½ð6¡-!ñ° ⢴( ‚*ERšxÏ^bÿ“×.Mf2⢒šyü:ÝûE•˜Áãåm¼1P}öæÓlôô²`À·à^õ#àE+ ¯Áô|{ vÌã d×îØsˆöÊnäòÆK2bÕ-¤'ï¾òá󳊕U¬M><`sÖX1XxÐFAÌ– Ð(˜mŒøÇ°V‡Û jQØ…M<РsOH‹Ò2‡ÙÚ2¶W¿!G{÷2àùÙT°O+9xàmþ)Ÿ1¼tñBÅ‹Uª¬Å—èüߟ˯dØ>.=x´Lÿþ´3 ‘òQ¦"2+H›}9aóN ø™˜ðhÆ¿QZCRp=ÈI±ðo•àï Cm[Üã Ñï7ú,ûÓ–}‡ L<Рìݹý?sßyf˜@ð\ÉÏ[$ Ô‰µ$ x·‚˜È/$iëûrâƒM,RvÙ¦$*’¬Èßòx«U§)b¸%)õÁmg¿3¿«T*š»êk»©û ›‰?Ä, M½$ÎË/W|‚E¦–&¾DçòþܣŇq¬ëŸ}cnÊð9ú¸aý´z´*Ž Q ¢0Ã@³GY9»%\ƒÒZ’‚¶ä‚ö¤#­ÜÇ}m³ÒýFgÐŽ\Ë~F£ïˆƒ‚iƘÅ'Ù€ÏW±q岿¯ZðÕ"nÏ–pMŠè!^¤Š|†%Q!)Á ä›­Ö¤ ´'´)¹稢èðÄ=I‘/G¬»$˜©ð¨SDü,¶äÄäÍC4R¼z²ÓBƒZL y¡ä˪r|ù†ÏŠ DÐܾ~íŽ3§MŸì÷ûÎåÁ(™ï©žd·ê©) šÍÌG"Tj’Ú¿›Û ž½ðRûwø±öÜ®ÝÏÚ¿["[í¾ÖþÝ’:å5H€ÈÄÔÆ²jìCT±§`ÛWËæ|>»ºº¢„Ïk8!’$àyF ''ò·8ЊÿІ,²=ù[­q…@\“¼ÅÂï |Á%sÒ#OÍòî!ÐcñNB63Õ§'ÛYê_ß±(k„eÂËVjSðõ)møhFޤée­ÊçüûËA#NïÓ{ÐàÁöÄÄ.V»=Åd4lÃUªÄ/LPÐÌãt•;kªJöìØ¶iëú5۹à ¸±'D‘ˆø} UÏÚ¸%)!‚Âß1pXÄ쬳9d½¿²X$•+7e´ÄMoI×ÒX“Ò‡³ôJyñ.ûÒôš:ì™áÚ©~'/вˆøÛóÖmæeÿ–*utZ^ƒmUâ<(xV°€ØÂ´‚g~'Ò¼£â„0ª(::qKRpc =‘ŽŠð9€£b÷$3sˆû5y´6é\ hø`oÿ‚´õ ³zúe&  );úщ  :(äo Bð+ˆfü ®^•€€|.@PਊçφtºQQqBU¸%)Rû€>8åq(úÜD9 NòUVКµ”Ÿ0.&î!dITР,ÄÍ€œÁÜ-_ö%&¾rÂÀ¾ ’­øÑï•j!ày@‘Ï…$)p¸–$D„¿±$DW^Ë›ª(¸%)¸bz)ò '"ª¨¢e¥4‡“ª=ü㤨êWÑÇèvÓ¨@ƒ‚’á=@9IÊJM½ /ä6±üèG'+P0…FR¥­ø âÛ]<rÁ3NTðŒ€˜`Ç@PTQ(:(qNR‚æ öˆîiã螈ðétÚ(Óá婦ü>cr0ºª’vØGR[û¨À&hP²:õL³#" )Ë ¹¡Iédæžð?%ùŒu´ãO„·«¶cùlH¢"ÉŠü-ÏVÿ+¸&)0“`G~äIqsL‘DÎ祮þjžçê¢#<üVµ”ÊLYbÖÏQsVÔ⨠ ¦gyöQš¯˜8!uwøXƒÂ‰æ8#n"Ë9!/ä–æžùtEFh Jò‹XúHgY¨˜ä‚Ö:Ê îdE’©MÁ„D.r'ƒEuW!Ÿt’bÑD‘‰ÜDn/BŸó²À×ÎîvR™—g1iqÀPçåXbnƒüZd B8~D’5ÃTίQXpRMêj70)± ÙD\Έ 9!/¢ bFÈ–*!?#€Lø¶8Aý·€ˆÈ¢H‰DB­qˆ@dFà¦8Î"4^&({.Ãg¹ÈÄ9[ºøkÈÉßê΀‘|:'™ Tq8ôÖ}AŽ¡‹T¨&-@‰fì{ æhL@JRR’(9™—¤D!'ä…ÜØÔÓØS¥§ÆRÇ …@G îIJÐäÃÚôœŒEf³ 6™Øèãv‰sN.—=R÷r ½`›&‚Ýf… ÊNР€ ¤¦¤ˆmÈ«´(‘B_Õ£P(¸')¸)’ŒXu é AM1´ð÷€IÅÊNªN§+è«Â™pý ¦y\PZ\šjd®£!˜#mµ(ìƒÂ&hP„¹‡µ=A-J§v˜¸«ÿ …€B s#Ð)H n1ˆ È¡H­SÅÔdöÿp1Iq¹ÝB›"²áú¡Dsâ¢Vü'Ûƒf&È!gÁsÃa  ŽC^U …€B@!Йè4$E±Íªlƒ¤8ìvAP„S­‡}QàT{ÜoEjQ¤‰¨9KÐå)B¨{D¾…)킨@›'Y“`· 2ª¢P( ÎŒ@§!)¸Éøå”^h*äôd/›y<ì<Ô¢øÙw%ÀÓIø³ITZò  M±ðDhRÐf0®Y$³Ð®gò( JKPV×( …@<"ЩHм  ""-ø¥rÇZé\+gµ–¤ Ý`›A?#˲"¶™¼H™¤Œj­P( ÎŽ@§$)¸éR«r L?p”1‘ÚINäº% Ú‘í Š G %ÁÙ>r_KêV×( …€B žè´$EÞTI@VP$A‘Ç[CPd’¨à·lOSk…€B@! P(êG Ó“”Ú°(Qõ[! P(타šçÚ>¸«V …€B@! hERHV( …€B }P$¥}pW­* …€B@!ÐФ4:¬P( …@û  HJûà®ZU( …€B Ii uX! P( öA@‘”öÁ]µªP( …@#(’Ò@ê°B@! P(타")탻jU! P( FP$¥€Ôa…€B@! P(Ú¿îEK(ç~*-¯¦cU5äöxkÑüŸH1h±˜)5ÑAé) 4¬õê–ÞüŠÔ …€B@! èDtz’‚‚^¯æ¯ÚBóWçSE•›“êd28É`*'Üyt²RÀ’Þ‚O@IDAT—B¾€>ýv%'ZéüñÃiÊØ!"sDQ•( …€B Žè´$ä$­É[s–²ÖÄMv{uMßÀëmd0¸¢r›9¹TU3Š>žï¦+ó醋ÏÚ•ðlÉQi\UªP( „@§$) '~€¾a‚­†Å|˜²3g“ÍVõ[ò“°A,.Wo*;6ƒ^zÏEWNËš•¡LŽ”›PÔo‚j@! P(:ФHí‰ÏçeÖ"& Ž|JOÿˆ Zë}Oš{ÇAв²^¦£G¯¤¿! èº`ü0AT”V¥Q4áêƒE–ðm¹O­ã=¬[ØÿvHm*NCR$AÿI^Á^š½8HPºf¼Û®÷äHÈPr}²€(+-™F 쩈JÝ»"I TM†›~ú‹1f«õ{LSz3Ãìf -±î%jO<# kTÉÌþ S”"= Ïû×S3Wsi‰ç›®úÖéè4$&”ªj'½ÿÕJ6ñ J¬Üqhs¼Å]éíyËi@NûÅXÈh4ÆŠxí)È ˆ‰qҤ˽ǿÇf·ÝèÙ¬mÒ“¶@j²C³™ÍÊNÖžw©Úvy½c5zeËÀ!¿½ë—Oòyý1–ê/¾òÊL'‹¤4,íp_T“ H"Ð)H  La擬‡ ŠÓé¢å›vŠYtã…tïuh†÷#&(ÕåÚà9Àó€çÏGvFJ–ÑdþìöÇ~óŸ‹çHù+ÕMýVt➤µ(>¡E)Ü[LÕ.%:6ÄìílU5^Úµÿk|bštÌ Á¤yG”ÜÿÈ6{“c‡ôÕ½ùãÀ^ÙÑiUÕàùøÙ-—ñ¼hšá™þb&ˆ ÞsЍÄÅVèlÄ5I iQ¼^r±Jx[Ñ!¨ qPbµ@6“Û²ëyXîN¨MÁ3)Êå·Þ5Ý‘˜ükpôÛ¯ù–þòðÿÙT÷Ï$esáA:wtn´Än—zñ<4‘„Rš{PújiØT£ vG î[ªÝEŠ]®šô/:ZQ@ ×þ‘&žö0=â¾°€—žúo:màõ4:÷Vzõ³i|L½Czš Xø™äü¶°ÜzÙÄv¹å·_qe¤&ÒÎ}%œ<ó;Ü·»_„£÷¦áÕOPê;?®öé"ê,ž±¸Ö ÇÕ=SéôÄ-I‰´æ$§ëXêÓíš³âñÐ8¬]hÿ‘5´`ÍïÅo˜fP6î|ŸÎy? ïwm*üHì‹Ä‘îO$dŠP $rD…-\’¤¼;ï;1ƒì†‹Ï¢ Û÷Ò·k¶ÒÈ9ôåòMd³ZhÒØÁ! Å«Ÿ,âô VºvêãJÎ)õÙâ ôÛ])Ì-_—OÝp!¥$9DRÌ…\~'%ØëÀžÙ%™ze§SŸî]雕ù´kßaAR ò ÛiâéƒéÝ/¿² êÝÞãm´c48ƒ›^›µˆ øšÞÝ2(3-)T¿‡³†Ïúv-­Ûº‡jx ¿ƒûÀ¨ôèM‘ƒ³t¿÷åJZ·­ˆL&#M7”¦ž1:{iÞ ¡íÔÄ^t÷K(·×E”¿ëcª¬.æc:%;º+’B©Á¸þ²µ˜ëÿ3 NXö>v@…l‚ÙVtºwM£yËóèôÁ½ì¤sjÿè’œ 46 áÅÌ>#õÌ‚?ËÑcU¡Ã¥l ’e4·9gÉ&J¨N6›}F Œ,´=“Æ ¦+¦œ<ÃI^ÛÖ§³t@‰¨è tXû[ÞœÊâ)žÙõ6[QsmýU”–ÜGOtdòZ£*ç¡zÏW;ë €#|©sB¬í8Ƶwí/ -"¢q3„Ð+‹ ö¦ƒGŽ P~aP{*¯éõÙKhìÐ~¬%¹€ŠKËéËù Ö~èh9kDJ„£î†{©;À6µ îÓ]˜i0¾-[Âb¨äïÜOÕ.7Mf§ØA,oE“åôÜ^ô]þNÚÆçC³MKmS’<7vÖ'9ÎÆŽXJ…€B AêÿÌkðôÎyÀë«a'Çbê’Üö^)@;øV*Ø?ŸtŽ‹qzîd1'ò@±Lë’ÔW¬K+v‹µú/þX‘·“Lw†:6óî+BÛMÙ?¬/må˜%¿ýç'<ËÇNçÉ¥¹KóÄ¥Ÿ/ZÏZ;]û½ñlf´ §Ö¾^MðeÉNO©S=f ¡ô`ÿ‘)ã†Ðùã‡Ö9§¡І€Œ Þ |_Î`§ÛÍ, ´,.·þòßy"fµ?ð9¹ô¼ÓÙx½ñùRúË[_Šcv›…î¾z u ó‡i¨Mµ_! P4ERšˆTqÙfÊêrÂyq`ÏitîiòôK+ZNŸ-}€ Ìw¢¶ì.#¨´¢Pø§4±zuZBà÷÷\U¯´w_=9´3i°ÈóÈ?¿Eþä¸&Iôà’›³][-Á?ÃK&ž&Ž_ÆAâ°È§VéØ*÷aÝ‹ý]Âë ?†íÚ2Ô·¯¿ûñ•ätyDC/'å|ú?sèÆKÎbÇÚ~‚ˆ|»v+}²`˜Í“è°Ñ®9_¤œpòôä» ³“UQ(E@‘”&¹4ïyºþ‚wiiÞÿRµó½ýÕ÷ùJ£vÚÈë Ά@UFƒ…Æ ¹ƒ¬ ÆOibõõœ¦B8ÔJÜí’¥½;NP œj᳂Ce< ºäX%mä¸+9™]N"#ˆµ¢ŠB@! ˆÊ'¥‰¨8²ž6ïþ„F 9‘E?‰ `ïÞSIÅv>÷SyR‹Ö%ÇvЪüW¨¤¼ E׫‹­AÀj1Ó/?—v³ï oÉqPÖPϬ.¬=™ÒšjÕµ …€B Y(MJ3àš³ügž½‰g÷`im è~Ê+ü€ Š? œì4zèu4¼ïeüÕšÕÚªÕõ &!0”§ cQE! P´q­IÁ”ÍK{Aܲv…yŸÿƒ¿¤|Í_ý$ýõÃñôÞü›„–Æçsµ¬bu•B@! P(:q«I©/¦ON €ÿ„s`¬Þ#]·òL¢Lê‘>…WcÆë¸€N…ûŠÅlâ( }fpŽ «¨WÖ>Gy-ÆêýTr) …@ˈ[’R6jUá¬;…³¾sÛsŸßŸL©ii<-õ”’l§Ç8ÜþYÇgj1-zcÁ»bIrt£ý¯¦a}¯ ô”í)ºj;Âü‡ã¥$Ø­bÊo„«VÕ) ˜G îIÊ sF6“üºƒg.ØxæBlšK ›?à «Y˜ªÌf;û¢\ÉÓ@ÿ‡gf?•÷ió®O„ H>]•LnYÞ‹béžqkW®fÞ<¥4Mž¢Ö„Ë×õ@•^‰­P(Z‡@\“”AaSQ¥'Z©ð¨“œÎ\JHØÐ:ä¢t5dCFùî]Yê>5hùƒÎâìÊX–¬§M…Ò–¢Ù¡ÌÌ8çïÇòõª™4 ç|aêŸ3ELÆqU:HøÎÜ„x$çO&òôTrå/–l¤qœPðK› k]Ó’hþÊÍt“N<=—g‡í¢›gœÓñ:¬$V(Lj[’"(ìªX¦•š`&³QçØ£b–¤@6+ß•ô‡޳²/áOm7Ö˜`¹`ÜLÚ±÷kÎô!ì›ÏZ8- ûDÖfdn¶YRh(Ï Þ÷jÎ ^—ÚŽmÄïª ÆŠìÄ|õ3zîÁë9ã¶—­ÝÆþIGh<•ŒÔDúÇ X{f樱è“…k©¤ìDžØî¡’N! PÔ@Ü’tÚ#“,&öG1™L”ÍDeoÅr¹z“ÍVT?*í´29(·[²WÊŽ~4T4ÍÈyU¦‰Åí©`²ò)Oþ€YºÄå)§µÛ^KZRÎþ+ÃÙ%%±gèµ» ä¼9çœ6H¸týNøwˆr²REÎ$øC¶d—ÛË„å0=qû¥")¡Çë£÷¿Z»R’) & Ððèׄ‹cù©}Y,^Ì”“f#³!@GËfP@7ÇL K)Ëd5é4¨[Ф ò˾4&¬Õ’L£9ÐM~Jw_¾˜Îq?!Csx)«ÜM‹×?CÿølzëËk…ã­ÛS~ŠÚŽ1lÖ3Òìì«Tå<áOÕ;;]H‹D€ˆ›t<úkJ¢=Æz¡ÄQ(ÍG nI  @XoäL9±ò‹ÞÊDÅΙ\{%›Èëͤ£G¯l>bQº²xX¦!ÙÉBFÈ ¹!?úÑÜ’šÔ›&žöÝ}ÅúÁ´÷E¤\˜ð²§x}±üzáƒÑ4kñ½¤Ó&&D*8F±°]°ç•sÖed>ÀY“‡öíVG¬ô”D‘Üoõ–Ý‚¬làöª( ŽŽ@§0÷`°·Y­dã$hv^2yZoµ§ŠÕ #:r¥g|DÍÛ.÷”j–¥Oš…zd$ 9!/ä†&åT枦Ý3sa™:áIÚ¶g.å³ÃmáEì ë—ûýn7Kl÷íkõÄó†_ÕT_{î ¯²4à(Ÿ“ËæœWg-f’RJOE霜ðHYEV¯: }45Íþv=åöQ‘‰ë¤v(¸&)0“`7›Í‚œ$$8ÈÍjq·ÛM9>/é';‡’·8ƒº¤~Öæ>*ðAÙ Zž)FÀZ”D–r‚LAniî‰Ä“…ä‡Cû\*–W)ÏúHø¯.ݪž§f›íºËîðÝõ‡W´|vÛ} ÓÛÞìÜ:Im´·^vn¨-?OG6÷Oêš–\'òi¹½iÔ Þ¬I ж¢ƒ´mwqèZµ¡P(:"€¤ȃ½Í<‰ LP(¬IE˜{BZ”Ö;Ì6&[íã LgaÊ™ó~UÍœ÷éu>çµçþcïÁØÜÄ8ÜÀˆ„>Ï–Fç1_Àûû¯¬ÒtíuÝìx÷±ÛªŽÔ®[ýV( …@sh ’RG&¯Ë]v¬¼ºÍH A9@qþ>>5™ý?\LR\LR¼LR|>k\Â$Ô‚" kš˜p ‡œmÓ|e»X`æÁqÈ×ÖåXEµî÷ùêŒãN³dyþøÇWÌãXu#““ï3Ié*eämÞ¯#oõóì¿ò¹¦^OìÑ÷óû¦¸å9j­P( æ Ж$¤D,Κê’J§[«á™6&mQNS¤0Q j4,ä°ÛANµpª ÷[‘$¥%ÊhkPdÛÍÈ·0å  ˆ ´)p’9Á~A‘˜` Ñÿ÷÷ÄëvàÖpŸê-¿¸SLK^µ`Á¤—ïX<]§ÀMlɺ„/7ÒÄW_ÆIñ.«Ü·óû¯¼£Ù凾åõVC;÷<ÊÓ³÷ÓÑò*:ÆqI<z¥A0š(;L'OMtpºƒÖ?‡zu akbmrZÑÁÊß¹ŸJË«éXU ¹÷¿5w”¾·¦§ºöG¿üã¿ßw!ÿA÷Ò5½;ã‘tªó›zŒŸÉJÖ\âoµˆµ³óþþ»_¬iêµê<…@GB ­H Þór ìݱm}¾ý¯Ë+ØO8ïH[ ürJ/ˆ€œž  ¥ Å/4)¬¦ITZ"'Ú [¶ IA›Ð¦`ÐI11 íJp&O{hPÐ/Ü î§V¼oÏþ)ïUƒ]žÝpñÙB»ÒVÚ3¾7” ­É[s–²ÖÄÝ&ý…¾‡=‘ÞÄ{ÌpË#OL«6WþÓ º÷¬Ü£÷/ÝF½*wiVŸÛáÙq›¬´'©/íì’›½'©×S<{~úßÏüá¿þ4s·‡¿cUâž{Î^Uê¼Ó û®ô{KÏâÑJŒç̉uSÀ«›ý¾€Ûd1øY•¡Á;úŸ,»óñ'?&³öÒ+3_Û i ’0Ú ‚Âk|ûöì˜ÇækwpÈï½²yWÛ©U9€‰Z 8Ê‚˜à%.H…í–9ðmÊq­ Ú”Ž´rKëÄu¸› öi%¼ÍõÉûÔ"¶}\Kòk®ç×zÙtN@ ÜÄh^ïKÖª Ã9‘‘žXã-ñ÷ÿ0̪ªÒÞýÏs–U8å)Q_ý]>}øÍ*Öd¦ìÌÙmÈñw8ó+;6ƒ^zÏEWNËš•¡‚¬F³óx¾áoõ “#ht,æ¶ë{÷=Џ‚0oûù¯~j4šÿ”æ*ÑÏÞû-u«>õ0&?4°l+í`BwZ’3)«ÌžñÙÿöÑþþWa¹ZôwE¬TÕ@àG¿üÃÄš’êwYÝÛ-½¦$SYdèQ¹2j“%àÁs'H3šrmTmI¤ÃŽ,*Jî›¶/¹×-~¯éV&+ÿînîqO}š·;g¾ì0‘ÏI?®ioÍK[ùUŽÁGý$÷÷JÞ··÷ó»|Køâ%/|1à'•ûv]Æ!~oâßÓx >w:ÙHÓ¯MLÒ¯ýñ¯\%e†9+Ó²7Ø„ q1@ðõjJpäSzzû¥D@—EZ„¿áÑDÐã‡EE«†~ƒ À¼…þÏZÄD©ûß–}oÂcÑÒSä@`ºåÑ'Aé¬@?oÏ×þ’mi-¾ŽI]±ã}ã·½.Ðw¦xööÇ~£ýß~-‰JË¿´Z,‘º0Üñø“gùþùIžcÚä=_SVõÁààÕ@cV¿‹¬<1¤‹³„Í'ÁbX—=–6f޹õ w4ß5óCuoàr~í_¬éú€÷pžàýô$ÝùÄoÙ÷I[Ƀ×7d´½óÊ̇Kh**»Û‚¤@p°yIP`èw×ÔTV­]ºðuÃyÜûúì%úíWžõ¯ÒX‰5ј¼­=ì‘´nÛú5O•–†é$÷*b_`Çgø¼Çu¾÷‡W»ê¾šÿa6Â2šQXÉ”‘ÒE¿ñ{—ÑnÏ?Ý:Ÿ£òžF‰ŽÈiÙ0PÃÄóá7A‚Ò5ã]Ù|»­‘3JÈQr}²€(‹ÃÝØ3¢DEøŸäì¥Ù‹ƒ¥½ûß}â•Å|ý½]h6[ÿ‚rþî9íú92ô¹Då©›~lëžùÃ\ÆÏЍDñˆdÕ÷üáé~—>–¿[Nc_¿d~_Z¹þ*ö+)Ôþ'ÝÚ;Þ3B“ÖÜššp`%x«µe=Î~çc¿[ðú'0ѳjŠõ.5‡ IžJŒÅ>kš‰*l)Ý't»äÿÙ»ø¨Ší=s÷n6Ð ]ºRlØåÀŠŠÏg/$€ O)It5›‚ ¨@‚Ýg±àiR±!½…*-tBzÙ»÷þ¿s“ ›„”Ýdfò»¹mÊ™oçÎ|sæÌLª_½»¹ž=5"Úñ†¢ícöW4íÊø¯N’B½sjsp^?{ݪëê7nò®²o˜ñè×óšÒ¨@†óÊ‘…Êšm{ù¡½»Þ_ùÓ<ÒzÐoC¿ýV#)ˆ«Ð¬¡ò¼ÿØ»ö8êÎã¾…ðdó;mÓØ\öÎÜoØá×Ã^ã>Ö¹Õm00^*u&ÃèÿÂ…†8HƒâKŽäqm̾Xøëв)lDh1?ÏhIƒB%#3›ÍYü'òÔ§òïͼ{ñ7¦Þ«µ[·«ê…„„Ì !Ò x1½ E Yø)[}ý´ýY}ûÞá’%s2}×Òù0/Ç Àxì3Î,­øòÄ ‹®§è†‹« †ÏA‚9»âȬ2Å=ë±gÛo-n0`ÌݳÇñµÐ²låZVi$[Mñoȶ7¼PÝÚ¨ÇC0yNô¬¸èÉîqz㺺H 1xú@HƒD… óÇa[úíœ lôqßyødŠñ¯[¯VjÂFéŸ7ŽlPhˆ‡4(vïøxÁ—ŸÒ0ý&ôÛÐoD¿•×{]ãžÌ£ ÇvîÜ(¶Ï=)ýÃé±¾\alÄ`{¯4… (]Z`Ý@XÚ4»ÁJû–ðªG 5Íb!#Q²A©©]¯KÍ|Dò4ûÁÜCjéš­ìÖkº›Cƒb²´pçz.†xr1µ~ùšm,-+ùÿŸOåß[y?6UxOõÕ¶ÿwã3†biF6(51ÄSZH–ë.·üÐñÞðwÁ–°IðKß´W:¥É!Ÿ—ö‰-s¹‰†Kï¨ekžÜ¬4O;ÀåçV–@‡ÓÑLfÀ¶3ºX“Ì#勸 _ˆ—Ý›ôÉM³XË1)­ƒBÓŒiÉêš–¶ñÏÕ3þZ¶øˆI¿îš¯“ORÒ #i[‰û­šZf=8´ÏÌæášc/_<Êée³M{¾6 f¬[Ûа܋1¢Ž"šRÏ4ÜAÆ¢4Í–fñ=„/:’‹äûe{)u.òq·iªˆÜ"ßy (´¢òo›wûlþ=÷ŠàTA¿Ô`Š‹ê®€À À!­1‹§:Œd+('îbfÑ~cpËáKÚK1äSmßvEe>ýGÄLèžçÌ] Iý«¯fØ„]TÎþ‰ü|¡yiæá)œdŸ¬pTAÎ ÖÚ›_ÌV7ïuwÖɬo#îHŒŒ¤®Ç]u‘œ>²u A4êµ[qÐÇN½¶á·•kw¬_»óš[û÷ÆâG½ÐbŽ77BlFX½ îoµV¬ëL‘JÇhûZÝ—jC£Å±H]Úþ]I‹Wÿôã™™id•Š# ý&ôÛxÔñUÈ8êÊII¹$µuøýÍ#mÞû-Û¼g.;qzGa<éèEü¶eºy4k؃ukw/»¨í] ´ ý¸_6áŸCÇYzfž9ÍØý¯]n`ÇOv`{!oÇ6á *ùkûTFÎ|-ІEÙòØžGYfŽæÓù÷dÞ+ƒW9ÃjQúÞóÀU\±4¦iƾê ÇÔä¦w?yåw'Q:ÕÃrØÇG~°gìñœyήìÛwεÔË=í#’[ŒnÇ70?W._Þú–[øþãï Dä¹CUÜGu’¢†ôq6…>t"(‚x˜&;;Ó ­Êx¾¨S÷KÛ¶éÔ¥K@pp[@@=Õ‚¤« yšæÌÍËÎIÍÎÊ8±gÒæíëÿ¦Öž ÉA¡¯‚®I]G¿Mµ õ s:2š½ºëPó8rrÈÊ×lë?óXÔŽÂ9¹‘Ññóš×Xû½AXîc[õÅIÔÑ…¦@‹²eÏAs¡¶€ßmPH^’”Û¶7™µiÞÈ´K¡!ŸŠjSD¾ó°ëw´hIûŽø|þ=•wÂÑKÎ]‹âß IÓ>x`Ð:(^J¯ÊÑB6ªdà°z7#²58èû•*Ç/#¨<vû2õ°såç6=/äÎsÔhIj›ëtj;KµÕçëšöŒ;g–#f‰§óP$…dwצˆÛ¼POžìl;6­ÛŠƒZ"4¦¶g—Ò•€ÐÞtPÅDª8—ȈÞ¡¯‚ŠOhQ G©®YÃ>=_f»-7 Ë΃K0ŒCY"2âb»pO‡Í/š•;XWhXš7¼ ~\ìäé ¬$‹uJ|Ù‘|%Ë\šß ‚A Ò:>•#).Ì:Í¡ž,óOñúrþ=•w/þ¾TÿP犴ÀŠª¶öwf0`ôÙz‰Œ+!£ž­XÚÈMÒ–Š:—ÒÕ‡¿>«3~y¯ý?³ÚHPf—ù“í®ßIËð ųZOR(_Ôhб+úPÄ=µ6Ô‚I!£ZªˆDQ¥@$Åg+Èæ‹N`K…*%œð%’B¤„ˆJVÁAØÓ{ú-|ÚqnÁì—>æ‘‹irÛöýÀ6ïþš}¡ð<‹‚½¤@4h%äŠ: £Ú8Ó‰EÜ(^_wžÈ»óH •"ª›l®Ûôü™^L³ÊQ“ŒŠE .›ä¯Õb•ñŒCÇï4Þ¬ÇѺ±/d«ô}&ªšá¼µž¤Pf¨Ö¥ÆÓ½!%ÒB&iPÜ iRˆ˜ˆ—ÒÂU„³;Q!œ©µ¢3ô®Ö÷ªÂ‚[³ë{‰ KÙÓïQë]0´ÚäðA´òtfjºo)zït¦! j©õê)£ÕG¦¬BvQŸz*uY(º~CxÆA 6,Ë[­y—ƒýÈé ;åi¡kš¤P~èW¢FSêÝÓGDUâÀeíé¹°5èDɧ³8_qˆg5(¢÷“V¸•uju ¯;tt;•±Åû‰z(èN@Ph³Kâ•Ó¤PXŠÓÎkUUX•¼{þ’¢õÍÆøÒu­p(CBn÷|P= ] À[„æÖ¾Ù<¥A…M ÍWØ:w]i~*ûÜHŠ]4œÔ’Sþ$(‡òžÝ+ myÃÖgxjWj‘Ã/Fµ¿¢’‹piUè¾Ö¸*æÝ‹ùuk£6Â]~/Â$£>| ò3|ßFì\ù ÷X®Ÿ­oÒëõ{bÇýVž0ñãK$¥¸ÜçmÃZy_5¼Ý@+Š "@£•žsfá/РT6ÖB¢â%‚bUaœ› DŠPÏ9OäÝsÒœ“Wz®’9¨¥F e;!»Ç#–V ¬¬™Š!’ú å{¾ ì »²UovÚ¿Œ³ùPoHX{ÞÀAÆ)(Ô'#ÿµÕ\«ÅÝãM—e/=´‡5½À|ü@ßÏ`¼ûw/uâºë÷°È»ñ®‚5hîdCîYÉ^xp;ûÏÙÀ›faóÇ@ÐÏ’X£°Nu"ÏçÎDÕlRÜПu†ýU±X_@ÛN¬Ókï±K?û•]üÁϬíâ˜b `jXCvÉW1ÿVíÏ-ŽôQkÀèÛ®“ŨÇäv/S©µ~#Ö9îCÖcÖ¢Â4hgjá2³O°Œì£Ìf«g>úcËLö̽°ÆaÙñÓIÂ[…ÎÇR¶±÷çbAX4 K;¢='eC•]‡¬g\?T™ˆh‚Àä`…¡l,…˜›GnÅö y´bBÅ]ƒëoeŠÕÆNþò“øÔ¯ ÂeîÚ‚/¼”Xü ;úý'¬ûÌùÌ¿u–³—ðR¡sܓѫÁ¦zVC¨+Ñq¬›S¡(Îö¼îmóÙàMh„Nº ¶åc6û‡ٺ&=Ùµ‡~©@ÈÒ½/Sm:°c?ü× ÐôÎGŠôD™¢¶mû¯eoXögø5³Xȯ‘QŽG⢿(’Xo<ðéWQ\"àã4¬‡ÕjAJަœ™tm÷,%m/Û“¼ü,éœÚÄšÖïzÖóÚú qý Ù±Óۊ؞к4Ûôcý¯™djW6ížcf/=ë04-§Y“úÕÖìV‹Üþm:²¬};¡E9ÛžÇÊÛvf™ILYœ'2Wz* ls¾ £UËOP£‰„榲.'¶2ÚIx/êO¸âeÊ$¸?|Êx «XzªLY`Ö!%‰Ý—ô¹¥IF²£Ÿ‰™àÑÊO’O”GF AÈ榆´ ?¹&h´/ëô([ôW ºÓgwGOcS7a£R€iÒŽ!swÝÚ dwÝð.ëÞþ>¶açæPxOä­~h[q+Ï% àÞ†åÞ_ÂÆÚ y™9Oc'~þ¾ð}üÚÂ[ÞË‹ÚÀ5Р4Ë:–´íÇþnv%s¢#TWV™*)^O–),LÇnÞ÷W §ê4´Ñ%¥WÙgr¸§²ÈÉpç ¡A-XZf²™_2¥až»¿b6k0#¹°à6Øç(–ŸÏ2ý†57Ÿ×…”—ƒÇþ,’•U°½Y½ù]Ö´ÁEìþÞƒ”\Àüžo‹’–•̳Ê:"ƒþokØ > ÆÎÐþænЕÂMùtÛºkßš'9îÂ--Ps}&œÊåh;í+Š£!Žf?×ð-Du!®+ìü6c™Û×®Å#ϱ ŽÝXRÔãEføäA›â×(ü,ÿå}pÒÖpヿœzþi ñÊäÿ¬¤"¢c—ÓÃDGÌMt–®â¼4±uˆ‘ž÷ÎßÍ®ztC“Ë\W%¯¶t=±±â!Dieª´ÈªZ¦ŠÇàÌbíSvX’^t?4*a…l JMJq¤å½D éèí„63ŸÒ –¦ º±«»eƒï\ÆÞ˜h>ÔçÖ6ü:óšüR˜ºâ(/ÁùwÏ“Ž1éÃ'6°=‡W ï×¾ªkù/̘/ò )Q4)cÓ;ezßÅvÆ gy'Š–¿†M…‘®n!ðúèÑé<Øú,ô±5ÅjѰœAe]Ieª¬¸¼Q¦ÃxkúGÆÇçW˜e PÎw’¤”(éíüEàTÚsz­4'Ж¼ùU÷Âã³…÷›À|üÓl÷¡eæu}sxdOŒòß0´]a~hˆ‡ìthƒÇ š÷b[öeÇNm-|_Ãc)#]éä&ïc¤ž®ÑÿÝÍZ<ü,;<'‘Yü133èÎÖ¬£0ç“›=»+-ãu7í¿ B Ã^#má‹“&±´Ü_CïvÓþÅìâck+ßâeê\y£LYÐq1ÓRµ±+7á+OÛÜ"‘—ºŒÀÉÔ]˜%êdÍ AÙôw–“—Z˜Ý\-ßN%7/­pA7šš¼Ú…ºâŽÁ`ø¢¶wÀüÆb϶lr¦qkÄh÷é­{ç±_7½if7$0Üœ~}4å i©+8x2Ùû’XýënÁ´%Œ"Áx¶ñmƒLû¦–OŽ*’ÌÚû/gÖ†M™ZŸeÿCÛZââ•—¡¡t9>‘¯Re¢á2.Á*3ê¸ý÷÷âúõmwÞºmâû­G?…ñÃ7>A‰ÁúÉ‘ªÚðÂÑOÈÿøðñ.â\;v°ó¬m…Ç¿gë ëys¸Áç‹Ô£D\ñ7fZ朌ÌÿøÄWÃâfYQƒ]ùÓaà).Ñ2ˆ3ý=•©½^Šp®á°ãE׳4G q…^r ;ôßi¥yñÉ磟ÊM‚qÂɉ‰Öë„€šž{?´ß´¥ÌMkÌ¡nÜçÇmC7áÅYa|ÈÏãß³^*ž•v¶úûo³õ… g ‹öwc½O(ì˜È”T–Ÿ´Üì»é^áêçÐ D4º÷´#ò¦èÚ+ ³OêØì‘èkºLeú±#AÍñ[¾õH† "‘$Å“hʸê,Ô '~£…ÆÊrßÿ2Œ­ÙöaY^j廳OÜÙÑ·,Åe圄få–’þO)>äcwRÿZY¡™6à÷0®OŠKä[±WT t–4ÓÇO±= »—¨×? j†ÌèÅòWæí3ƒŽg@GR¼Bª–qZtrH@ §k¹Cùb‹/‚¢¤r»§³S´&ôtì2>‰€ äï‡Iõ|Dš²Åp¹B™¿Ÿg+/"išïçßy/íÊ¿uæä¦dYƒ½ÞØW^Âü™Ö Ù—wªªñT&‹iBmÌ¥€ßßgå$ÁH>’3Èf5‡©JÒŠ”7VþªÂ\†oçß“y//F•ðG¤Ä<²³2OäXy®j«D4Õ„d#ó²³iu8!{õ$ŽTü­¶Ù0~½ ·ýTôÙ3lŠIb0 õ¨JÙ£ˆC>ï™Ï‹ù¥ÛÆ¥S`Ós1Ç9hÏ Îù×y®Ü'éùä„FD¤T«ÿgùóƒw1{ÃX¹©ñ%.Sûk«û;ü*sï!T5“fÆFÍöF>$Iñª2NŸA@4Òmš5@]ÆYvvgŸ‘­$AH>’³yƒ`h£ÏŒ’ü–õLä›âÀº¬a°Íçó塚—…K߉FžÎúIëqÁ÷cñ:_u$ÉxpÏ®¿ £»üÕ"ò Of€–ãÍêáÜ^dH¤`d|?\x¼gy6÷”ö›»pÁ-.øϯŒ›ÔÔý9]GFþíT,–¡¸,Ôˆ÷SÚ}H`ØC7®Æ+kòXÆ Sì/=‘Id®Zœ¡Z^É´…ð_Zõ®–ô<™«•ûï¦W"ZþQBlôhOÆï—ê~#¯%u †µdxÃà§`=‹YPПÍ&ÉgÃWÙ°^ ö«ÁdKÈ.òQ^¡… Kq gaAVع>Oä½¼UŸ{ïBxצ¿~ÛuEï>©»t혲hûžƒl†®å^óËÏ4-ävÏGµ<.ÂÀž ”ì76RÅnŽÄcŸº=0/Ÿí¿‹ \APÈ®µxÌÄyZûÏéKsDEèoS`w÷ìçh£~ßoòÒ“Ã3‹“(ò Ù{•¯{|½N|-jYDtÜË;\è°`šÿuØp+ÏV4šj÷4(œ­hõ®Óþ ,¨câb£bPçý=(•Ô¤xL•ï!@Z i:.jÝš”,'§ï ‰H.’¯mãP¦Â†DÈMy¨¨sÏ7Å¥b»öf *¾šOæ½¢XUÀ?µ &AÁ™ @µ{ö.ÜÒšöÁ %I&S¶¾/WÈîû-!®.G+×–DPª#ý„ØqñX”ű­QwöC‡{uš%SY—j c9ªg†³ó×< g4\H+ÜSÜÛve?t¼Oÿ¾ã},ÍVÿ³(ý±¹d´7 a!5)•-2œÏ# 4 ÔØS#Ý£]8Ûºÿ(;•rkÖ,uCé=¯êΜnXM¹lªÁ:…×c~~~¦Ì$»ÈGyeþE¾).??+kYߟÉHc'‘ÿpÊ¿'ó^^Œ*áOh ¨¡'‚B ’åý:Þ¢V\p˪–7…Þ³sޢнK*‘€'ƒ`³:™°›Cnêʾû®@^’›äyñd’2®J PÐÀÇDD96l–ðu§‡u9µ…wÃNÈ ²O”+Æ,5­ ¿†H„Ñ:}¯qÛî*Þ«)–Ò¢¶·»Ž69ËX3Ù`š©!a~ïM5ÊTm êñ[IR<©ŒÐ—Pl‚†žé@Ï^Ñ!œý²KažÈ7úÊgD%yòœMØ%-1,åoc6[>± Ù)uîù¦¸l *oëP•í>Ýħòïé¼W« ø' „ (4‘›••ž±î·•Ÿô¼±ïð­û}þù©â?V(¯W’%½òM«™‘ž~:ƒdÅAÄŠä—š€àK.1.zÎÐñã—.{Rî‘ ¶†Ù'\-Ó÷[še&³ÐÜ4䤟Kàr•¥Û걓ø}÷‡^` m»aîˆËú!m{&·`Í3U:{›\ƈ 1,ÖÞ=ŒUï`»ÌaW¤þ6Ã1:©ÒW2 $)•Nó(¢ggžiý‰§“®«\á‹a")þ6kÞ€HÍd[a-¥°† ¿©Q i¨‘ÎÌêÊÚÖ÷c-aúq€¿)+ÉLÚª ÷ˆ|Sœ8š„°Ì¼ vé±ãÈ£šË¿·ò^R˰²|@IDATÉÌÉsºnä×òÅ $J Pò31ÜC=­s‘MǺU+ÖÕoÜä;vQ¬m?ãÆýKxMiTHƒBewX¾oǶ/þ\¶ø9I^’›4)’¤_s3ÆŽ¥5ZžY±ë™ÚÃ'Ý* ÑUØeÅ×s)âT­[Tõƒ  õhZJöúí×e–)ã'³µÍ®4þ©×œ„Íž cØMOaz5q!IJM .ÓtG€ÈH——“{útj¦GH }PCoµZÍF:((uoÛ„išÆvíÊœZ#Ö ìÌß_ªã†ì0hèÅ J«zÖ¡Y( †|$# ’Y ÷TTž’ò›“ËrssYKÍÉ =›;ˆ9ÖLþ½™÷’°:–‰­M´ãnïÎ*wnïJ»¤0ÔÈÓ8!Rw“€mé·s “«°®=îLñ3®;°B G¸:Ù ÐiPönß2{ÉÜ/i˜‡ˆÉIò’Üb¸—Òù"d… }ß¶Û?ô?ì:|)†YZ¹ ^°RfK%cÊõoÛGtÏÃpûÄóòøwKÛÜrÅÚ¦W¸Ú¤íµ4ÇîÊM²Ž1«žWh˜Kd6ÍÊÒýBÙh^öÖk¯§ù…‚›0 Vì3cÇMñ‚By“$Åý–×>@NVæñôì\ž…F5CUqùµÂüÐàû#®à  4Ôy¬k›ÆÌ_ålûlrì)‚],8pÎIÐ\˜‹AV%ÙRÃÒZ 4Õ–f²«UÑY‡ú k JHH0 2e$YIæü>W(•–ï<§ÍÅZè:óKËa‡³åçßùǬ'o濺ò^|*GTžœ¹¹Ue ‚¤M * ÔÓU—~7gÁÉ£GŽ\~}¯èxo½Öéûö§’8ì˜M£ÑÏ;2l¤iÆ4‹‡Œd]y9éëWüüÑÚUËIƒ’Žƒd¤Ã]“R‚†(¤«nìö'èw£)ÙE¦e—&Ç;öÑÉv»qõaWüéþõÚd«ã†&—V¢Ð¼CÓAR íM`À¢aåÞ%ÐØ~ág ùîmû³i‰Ž¨Ò’¨öç’¤T;ä2ÁR ŠÓ<öïØ¾±ÅíØ´ë»ª[»R¼—ÿ1 —á,Ùe–‰†Ë„³ ÐP‡ù[Ùžãi0(mÇŽƒ4ÐL:…g1‹šŠ3µEžq´$½ +¾êXPÖAQ¹‹5 0XË0bR¯^ ÅAD2’¬$se†z„Ä¥å+€š^è}ˆ_;–¥±”Ü Øñïä¿&ò.0 3•#ÔÍüÈ}¿â¶ª 4GvÄ:HCaÅA¾i¬¸á·•kw¬_»óš[û÷Ö:vMëPPLÃß™e:3¸MwVœq"ò⎖ã§Õni¡6dˆky¹éû·m^úÛ‚eeeœ†ÿÔ‚ƒ¦Ù’œ$¯´GuÝÙíœÊè't&&Y;Ø ×±R·ð tOáã84&{ ÕØƒ vL·?CDÖ'$)>ù³œwBä\ß¼æ÷ÝWõ½-õ¯-{BARª\©jÐðë01#‚‚“騡öÃô­2³YJFKÉÖX®ÀœzVõÜï ¢) °`³hNêÙ,,$Àv'þ $¦…JX½zæ5 õÐŒœÊjQ„ÔçÊ7MM¶ªVéæ°fNeäâ@šmæò7É”ˆ«*çšÈ»»¼(Gà(Æ©?~»ÏEYs÷R‘k OC&Ä`‰˜AeÔ$0ÙÙ™NhU~ÄóEº_Ú¶M§.]‚ƒØê©Xo{ÀÁn+'÷Ô‰´ìÌŒ“ûw$mMÚ¸v¢¥^7Ò I!²B×4Ô#ìQ J>žHWç(ÐÄ,BF騕N’”Zù³Õ)¡E£!*j\ûw%-Æ"d÷íÜ„ulݬÊÎ×*X˜  #(¿žÎ_ì 30¬bƒQm@@k› 'lìR ’¢ãÈ÷‹F®R2Q G³mH 6³Z¡Õ1ÓóÏ·A&…4(æpO¡¥r³Å…,O¾³³sL[•@仡‡òMrÔtÞI*?›wäÇ“ÑT.aA?få~PŠ4ßðThSA¡øy!­EÛŽMë¶âHÂ5SÛ‚³ƒË ;!7":(M²5¡4‰ŒˆáÒ A‘Z€ ]íE@’”ÚûÛÕ%ÉEeK.UþÚ¯óXÒº}Ç›¿Xð{Ș'îPüÎ ¡V:ßÔ`A G hþ=LÑ… H뜒B†µ.‘† *¦{Z¤¡!’fÑŒÚ!;™ÀÀó azO²yÊÕT¾k:ïyNCùÑ1­'eÅwsç@*_tPy«ª£8ˆ£"î‰,FƒH ÕÒp:1$T‚‚hL'Òß ÉAi c^"*´ë1$½÷DžtêE@’”êÅ[¦v6TáÒ!*\RKça\=ûï•Ë?Szß<ä“VO¼±Ê•ûb¢æ“•|­† 0 Õæå噯¥:†…ÜIJE¹J¥Ñòô4ÄBC9DŒˆ¨ÐÐÉ9É_À-_ƒ"´gÃUñ'Õo’ÐòNå&ùøi¶õï?§ž>}‚n"ÀTÎD™Ãe¥ Dܯ‰90gýàlÕâ\œ¤T¦àðÉã_·^­xÊF…QseV" °K1É †wò J¾q­˜ S’Bøù í‘óÀ=‘•ü{lh¾¯JÛE©œÛ‰t¼o’„ð%Wy'â! -f¶hÎçß@¡] ò军Ê/: ²-†xˆœ‚"ÆðªòCS:äDš‚¨²"îÅû|ßÞøoðD{ÈQË8%*“ZT‘]."”g‰@% Š—*Wêý‰qu¡.·.ÿ~G¿â¦¾÷½ñé‚ÐnZWtmÇ»whQ¥uT„vQs¦ 4d(+†xˆ˜r"ÎÍ›h¨é,JOÒŠg·*þ)M"GÞÌ7ÉG鈳ȧ7òNë Ð4cšÅCF²ºæL_·ú—×®\¾éÓ0R¹ÚÑÐã‘Çœ(¨PZÂX–€%è ’B鉃Ò‡xFéxÛQçt·‘ñŸßT™¤¾}ŒM±”oý‚•òÎoDeî+ƒ€¨à…&…šª)Ôåʦ?VoÞ¹aýž«oéKs^Æ( Ÿ`3Âêq«µ*™‹’â÷E<—ãF4ÖÂkñ{ñ¼¦ÏÅóYü¾2òÏkñûÊÄ)ÂÐÖ ´21-ÔY¹Ë¥¥ÿ“´mùï çÓZ!§à´¼T–è ФPyó–$H9wr"îÍUøç.¿H¯ ÑU<(Õ÷®L­)”œû*Z†”*“L©\ˆõëâô,}’ýoù“–>%E=Pϧžo‘E²p¯çäd¹–ÏûzéòylE‡®·jÓ©s§ÀàÐ0[`@ˆªZ=²þD‰äO# iXC6;;-;#=eßΤm;7­ß IcB¤ÄW3«áíŽê{(õ¿·Ó’ñŸßx¤÷98:ö”Ù›ÑýÎo8e©Ç‰œÐPO0ŽPa8êá ñï ôŽüÁvW§ãVºójüÅÙÐð iKŠ/fFë…M ù\JWY"¢?a0´Û,GL«ÊÆ!ÃIʃ@•5)f" vRd”£WB\ô/åIXú‘”€€»6EhjŒÈ^…Tõ¤e1ÉÂÙ]ÓBd…œ“'ÿ×E¨<EØ2QÙZ"%r13BÉ Žêy¬Ü|>·Q^ˆ^F)(‚€G*õ‚ý’0,|š[_“h¤u¤“T*“B£BC8iVH“âÍE²½tµ¡E!‚â®EF×DRJZÌLœZEß1žh8ÿs°°æjËÎË®û¦°Rª:€G4)TP#¢c‘ùÌyü#±=9Y!Ô‰"Rí™ ©æÝ¯©§ì­E²ª=“2ÁJ! êQ.IZ61}ÎT^è9‘Òºˆ°¸”®2qrdtÜGÛGIP€‚t^GÀ#š!eDŒã?†nL…}Êfmü¸Ô¨dä¹’F…Ö˜ ¡:h¶8Áv_ƒ‚ʲGË3â“Î÷ ²!")îD…ˆ-:ÓAï¤ @¨ª# ŠÙeÆý\áÏ'ÆF¿YÕ8ex‰@yðx¥žOTØd(R6c •g¥Jy~é§ ¨Œ ²B¤Äœxr‘¬2D¯|¡ÚATY÷⽉^;E1mP{Òwå {Q”Úù;ÖV©=NR ýÜ‚Ó,LQkaŸ8>S•å:*µµ˜ø„ÜTVa!ÒB‡x&ʱ8û„ÀR¯ HŠÐ¦Ð™‰8Äs¯$~¾DJë äO36Â0ÏmX—o?ò>8ѳè|Á@æÓ7ðZ¥NÆ´‡œ‡†c¡·ç œm‰BŽídùQƒÉH” Û¤“T  -†/X:¿ð^^Ô]0œ\øÃcè¡îf´r`ƒ¡oN tÒ:(èD½ýV k‹w¤ J ü 2ÉêÃ3þr—îºx·IiŽj…Ö¼N" H|”ô‚­NöÑB3bÇþíCâIQ$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰@µ"À«5µr$¶f`›pž›×œs—ŸÂ•4){{þœUŽ Ò‹D@"P -³»úíß»ëCÕësƒ¹ü˜åØÿ½0æçv½˜Wy+H|')†aðµw6 ìaÓ÷ÜQâœQeº‰qežÅÂ>¸ô»#ÿ¸¿¯kבѱOù(ÿ{+*êh]Ë[Uóc·/Sh«žÐƒ­³ÇŒI­j|u5ü²É!²õÌÇ3"= ƒY‹ä•³tf°_Eù²u›.³»Ú’Wä}-»å‚©_Ï;6¥–‰_mâ¢n¹sõèÌØ±ëª-Q™D Š¨U _¥àkîh~Ëß·7{Ó`Æ…‘QBl¨`<¾˜úÅšÎÆ®¹½éGŒ[ÆB»r¢ïe>ŠˆŽm4¦‘'npN‡q±¶¹Úòc»ý‰œ2WñeD”ã~¤ý⬸è«ÊŠ œmFv–‘?’¤*#hÍuš%ò}%^I’R ŸÕSZœv&Ge¹2žÇ«Àb¯ÏÜ,7t]°ïŸ­šl×ïE×Çgö=ˆT»3ì7©ßÞômC× ‚RN!ThZžF¸Mkï ïUÎ0…Þ®†¢ía¤ï¹Â朼šì<ôW„=¡ôJ½0†Ê_X¸u#Òü´ò1øFH=ûà(ǵ¾!”B °p²_—ÓÚ¡5øž¢ð¬Üe€æºKÿhþëüûùÓ„Šø*r_w±/ÐÍ8ȹò*ç7 K-·6bðËqý˜®?ná–›fÄŽ# —pÿ¡u0æšÀ¢D{¤´+s²ÛtÆë €äY" 87ÕNRØ¡‰«*ÅÌzŒ6ã_­¹«éM=¿?úǹ³Z²éŽÑÛP™’ú³#ùˆŒŽ¨3ã&ô?ÃÓd¤ÓÓÊü.#Ãìq—hNc2¼]fp#ƒF ƒÔ ß´?½Éù 7¾éˆ™Eñ£±òCÚªß-ÌbGâg0}p¢#º_þ[Ɔ¼w+Tîãqßï“`#0ÖÐõ\pfššþ&î/…Öçäšž;îMœ„±ÁÑŽ÷˜ÂçrÃè ë¾JMtÄ\&‹s¾?ö×ë Æâ¹Î™ñ¢ªÓfÚÇþ#üEÆ8†ùñ´Æ³Mœ[^@z¿°O ÍÑRç£1뎊öhS²…}[¢Àa÷,GåƒÍž=Û²dÃŽ?!Ç È1RÄ |–¢—==Á1îk3.gÚDÆY?ä" Ù]§¨lT‚=z ù~Ê”€Œ“Yëyˆz¥‘¡½‹ú ÏM«·D|t&{¦È踙¸Tû^Ü)bРA4„w^¹…o´r¹r~ª A€!ŽKÓÓÓæ-[vSïÞ½—kâyYgî2Fáwœ]Œ äQ›ÄÎãè<6Ì>~±ÓéúÉßjíý¶}ôAçИø\º>Y_ED¦´òGþË[.ìvCIvÅÅá{¸ÁP>Ù|._$8bÞ§xð½op¾pVlôtº'Ga9ã~e6yÖkÑsóŸæÿ7ëC¿ØŸa{<8µÁ›%Eù`ækQ …ß—&N IIwNEÉì‡oØŠ4Z-ÿywܸ“1Ž8|W/àY.¾¾8§ ŸÈuöÔ 7ˆ8HÓ¢|oݸgbd¤“žGDÇBaï’ý݉Žì2øp\¶GM±×PØ ÷¼”·N |†>–YÕ{ícöSÜÒI| jîùûö&áÜs þLc_nx¤iPeãnŸØJ7„ßMq òEãÝß0\ï‚|jQÕ ›¨Wîbße5ˆ@µiR¶Üß58+ûÄø€< ¿¶Î>‘Æ”7bƒ¥tbw`ĈµÊÕœÃQ)ìF¥ñ™Š£=·(·'¼õ£xMÀ ü4Ôƒ!·mxtlR®Áö ‰ßÔâ##‹ÅµÇõ˜aÚhúp¡Rú’*à!1qæ#ñOw¤õØî¦yùzpÌø}ÌÐþ4«QxDWv$*ºÍèA9 ÂÍEeÔ•ËÜ’ ÄÓÉÖ¤^³·Ÿ}6WÄ_ò™¯JŒ+Þ Ž‰{“§Ä=zzѨü¦’¶ƒž;Ò‹¤†„…4%§Ñ 4,Ü•AÚ#ò²µPרëÏÛ§4˜jyJgÊ}Œë!ÖWß/?;~3šíïÆE³¦»z©VåâBœ{nptìuyNôê{‡p{ÑC|Aܼ8i’IF-šÅˆˆ‰ ¿Ûe x}_=:]ø9ŸÎ?½®þù­°mÖ¹0Â75jþë¶û¿”k÷ÒüÓï‘z:§1ºý¥ùáL9€òÙ¾àýû(o#pmjÝòÃ碼0Ón¥¬ò'ÂÄSb¹(xgžPÎßq»ŸíILn¶qž½g[?áÎ ƒcâoœ;nù|n©³Æ¸aµ©ÿ³‡ {ljjÎsºî¼Á:-7A3Ù^h&#£Ý0V íö^Þ ?yð›+¾\§¡^ÙnhZ\ï9äO?x}Òv.ü}jX¦3³§ÅÊŸÆ=”»øF6kVl”Ð*¾mbT4´˜S Ézu¾µ¾èØ| Íí]3cÇUZ MòH'ð6è±VËÉ:ñ4Z½¦ÞH é³ÛïìRÞ¸5 ÃNö!Î4¦‹XˆzõÛögÓ Ãsvª¹ríÂÂûü ÌZà‹ÝŸ½ãˆÙ‹ð»@z$FEF£9_3‰ cCǧ±ç;+{ß=Lá57:£_^x‹àú¶Íî÷¸îŽ^\}RO‹y½ÏE¥_àÏ=7A¤œ‰Z½è…›Æ’D2Ð8µ@u}‡H š•ï>²UØÈÝS{Ôfh¢gèY½©'ˆß6+–…ˆ{>*Bªh‰íôEÅû]º W7Èqlú+ã6ѽp(ˆ‹õP ÔéŸÞ¸]¸ çpÄ9L±Zî/µQqó_W/Ñ0Ey)oštŽ:WÜ“^|1 #‡õJ÷«ãqŠÞªÁŸè·jw\M÷i§sïÃo~„â犔¿ÒÊÅ)ÜИñ—£¿†ùcy(£Ð˜2Ó„Ê †<¿à†ËløMòkð{QKþVó#M…n'•‰2~ÜйYïà;éŽE¶®io‰ïÆÃ£É/È@:—än¡‹±¾ôŽë:_9æ§»è{ÊÒ²o·s˜¾³ûäFÀu¨RDÛÍç"úŽ—mÝÝæL%× (3€Å7Hg<ʯgüË+‰€o"Pm$"{‚ÐL#µpÈá\éXÕú= èбà[É–¢„†î¨ÝÞŠŒ3•îuê ucFE«ÒCnA%§³‡h˜CÏr=€G;gÚ£¡)Éq+â+bLxMË–yhÀIUï8³ábW”wÄ¡XøKøÑî^è¬p¨¶Ë犤ç$—i~tßߊ´ Îûquª»ß³®9†Îûq¿*ojû+f2}‹JúnªhÑäžâ Ù5`‰|æÛÔˆ¸.”Rù8ŠgÃrP\9sÖãJ UM)òü<ºY8Éz²{¡·²ŒßìÁùÓ:Pù+ÕÑoˆ¿=èÞw)Õ“Á;áÝNzŸ¯Aàs5'{ŒîAEcùÅS‘òWj¹ Há Y­5䇑H‚Õª¼ˆr¹Ç|YðOå–(o÷‘¦"õtî@|ÃGgÄF¯p÷ã~ ¿™î÷g]snC6²Ý¿“7‹r‹®Øþ>Ëÿ™ £Iðæ@à1÷ÿzt^IuŠù=1£/06¿@fAÄÑ`Eê!ÃøàÐC0ë!óº´:Áà4ý!êž—†8è”H'ðm µ7Å\{g«æ.W.©E½æ ÆjŒ}ä­Pý‰Êä.÷øÉž%×™×U·ä‡+×ý˜¬¯2Žè«û@Íû0 {ÏݱëÝèyÝèþlÉÆ¤¶è¡QÏw¨·áú& ;-v÷çëéö—Ž ×yùl’ðZ´P%Ÿ•Þc”¬(™P8_ ëÆ«:Ó3PU~K6 jË’]¿qÆ=ˆŠ5¤¹µÅ/ì ÿdº³e„=¾K¢}Üv‘zx7ƒxÞ‹ç%žUeD ÕHÁz2ë B§µg&—è¯?Ä•w¯9ê™óÜ®CKÏ‘Èg £ÏØíN.¾ÖИøëtÃu 2qÀé}Ø^}¡Ò×ó˜ÑK5Øãô®¼åOÄSÖåðY4ì“ЙAþÈ õtº«+.çˆp3bÇþ Ǧ,WÖ}xF “,‰÷=Cù ¿‘ª°ãÓíQëK Ï”&EÖ l´"ãTfÈaÍñ>K uìÊAƒ¸í˜çbúm ò} †xàLýèØ]šº·¿Ñ3r°çºZ\gxýùOJÿBøn¢#*ñ7uå_E$$ôº¥‡’o$5‡@µCÏ»ÜûY4¼›†jù/sêÃGÇ V2YN“<Íi‡æc'kÑxå´/¤^Ö]Æshp/÷ ,JjÜ1@åó:E?£¢ŠÅä><ì:Úë¿Ä¸I¾³2ëTÍp>Ž8' K›À›ÔKÎ=žq‰3…JœM!VòŒŠ}"’3 vYÔs4–íoè¹Wø¹Œ­dObFkð•8ß É’E` X’id~†çw+Ì´—1±@Eø?Ì”ŠAå»\4`}º·ÛŒÙ?Ë€åë1¢l–À}¹ZúS¾¹Cc×—WtZ‘6BE¾v.xòÊR3þŒžÞNØÒ7U&Iùœ‚u†IÖ.‡MÕˆ–ö’ý÷ð ýfšµRòº;MxuÜrh:Nçrã4¬K¦ÇEù(WùžË8#žuÐÒ´#æ ög“”tm žYñMq¸¡ZÀóËlªút‘—¼i¡F/MvÆ­qjƘ}ÛLþå°6©=çÎKgÆFͦèðͯ„ 1&ÔÚÛ©#Gfƒh¯ñ/&Á'¿ S¾~o P;h]BÏLÇù4Äófæl6Z5ü:q j†"Þi!a!ÖÁ)iÎ5ìÀ1|óldAìò$ð9 9ô¾Ã”Õ6ÞN-»WÓ(¨hïÄTß™Z榹6¡Ao†Ù?·¹W~ªåØvP/wM=,-ß qѤYxÇTð'AP–AKñ-*Î-" M{æÞÏîÎ5øîÜ£iéè…þìBƒ.üxòŒi¼“P=¾ ÏT§vê¤áÌ>ކj¦K±4é@Ò²ôMÖG1ÖŸ@ÏÉ`§¿. ªêô,ß)ß GŽYBùö(ôŒ ûlÖлQù:‘ç_siG E‰qõ•>4V]±=bVÕ«¨Ì¿z..cõç‘ãÞ-ï&’åHƒÈ§j­~ûuÜÐþwȹ#ËH×Nꆧ(|$ìM¢Üs˜Z Pñþ°É*bRžòçWiר âÑ_œ¬­:™§åíÄê)”·iÅýC‹ñ%4 U-uŸ]Ü_yîMÂÈïD¾Ò1CgÑ!Í‘Åì’ð­á1Óh¾‡=,ùBr†JÃ9F' åšëä¿™¾ßMsP›ß¡¡çdŒ2Ÿˆ©ÐSÙþã© (D€æÃp>†Þ—ב±9f‘)„‡ÎÒI|´Þwk4†h‚·Sâ-ZùõLüÛéíth›‡¹Ô ªjZPóg^ÔôÝWF5+ðR"¤Þ—_jÄ®}À(ý¯¦Qƒ‘ÓP+´.ÏðVM.u׆֠H2i‰€D Õb“¢èlYŒy×ñýÞ_Æ.ðлØÖ«$Sôëä7I¿§`«ÒOß)×R‰@q0ê}g±©k½ Æo½ž†·ó ã—”ƒ—5­µÜÑ”åSh½žFYé{íÖKÁ$º®Z®Â  U^KGF,Tjî!)ÿº½)ÔÓ†×z~ÆŸÂfƒTD  °ð õZMsýê5Q9K¯¯¶hzíȃU¶»òšŒ2b‰€D Î#P-šB³b>òš˜œ¡ÍõVü2^‰€¯!pËHçoèaìð–\ 6ï”Å[èÊx%ò"Pm$¥^Xè,Ì^ñÎ,ƒ¿ÛsΞÔòfZú“Ôvh&¦m÷F>@úºÅŠéèÒI$šE ÚHJÇÏv¥aѤ—<ž]Î[Ââ<¯ŒP"àãÜ6Jû„¢påQω˧x!×kZÏÉ)c’Hê:ÕFRÈËÿwäC•/<*z“yL±<Øe^RzEâ¤ýdh„Š„‘~%¾†€©M±ÚþaŸR ¬°ÌœýѦí…Z¬¬4ìöÙ~ö„À²üˆw´þ¸öĹ"ñÑÒùe¥9bÚ4vÆbÏÒI$Õ‰@µΊLíÑÁ–º7V^¼E<«Ì‚; ®üûŠÿùº¢á±Rê8,#}+6¼±¢akÂÿ»ãÊŠ®ÆZrÊ4kS¬Wº4m!ÖM©Z#]²•àÞ½_L¯Ð°ì˜¸A.ÝxÏjå½Ü÷­¡í€È8hcË-Xäé’¦ûF¾×+)cŸì*Ì ¾í1´XÝY®´´Ü=–'>,E¿Óßjí«å Àª³Ã±¹_*ÎVìœ5uVlÔ—îñÑF|Øçf³bðaX)Úc,÷4äµD@"P2ÕªI!:¾½+—5ou;zem¾W²´Oö6ÝP‚BQ`}„ûQò¡öñíÊLÈ^±Oj¢kLgùÀoá«"Ü6Òù'S­½@¼wVVFƒ…¡Á¡½*JP"bqXÿA ;a›ˆ3.2ÊÑ ZÓZ¨×÷˜åˆÁ>CÜÉžxâŒü+ÒN`sÊ÷±á÷t®³( –Ñç3‹û£ûÒÒr÷[žø†ÚWäçPÁRø{±¬ÿ5ñzKÈ }¢¹b¬[¤®lö.ðYîöH^J$Õ„@ y,]?x퀦?`º) íË“_T„Ø\˜0ªëœŽ”'Lq?èA]‹ jŒ¢÷ù(ÞÛ…ê¥!§@cÚ€ÃØPé'3•è{QÇ Ø¡x<ä¼hK°'âØŠÞÞ]ØDo1¶Hý:ÞŦ5xö*è°a˜[üÍîÓ£ó ´47U ¥Åƒ¦Ū¼c©ÆW©ªí‹ŸæÊÉÌ]Œ8ÚAî x>;þ¾Fä ²,.‚Œ[ü°“lá€"3ò|^!0`dÞ¦eï6¾,;ó¤åw84•¶ò€2z›ÆÜö¢+‘†ÊÆÝ•©Ÿ¿;z+Êïr÷çÐL`&þ…ØÂA1Ø'èÐ0R¢»¿Ÿ7íÂ>TÆþÇK&ÁšùZÔBh:?|Þ>¥AÁžP…ÞKK«Ð.ÊŸËÉÂVãŸQ8|¯‹Dø™ÑчG;\–Ãiõñì=÷$PY‡o¾pÿ*á_ž%ï#Píš÷,a]“y—ÞØ™qå~ôº¾Ç»4÷÷…ל%¡1ž¤Z­^ñãÑG*KPÌø8{œ3ËjŸ‹FþAÚ;‡žÓØ9ztã±;è}ÍÕhjü·+œ¿•hZû󯤛á¥6÷ºÁ¢Ú®D€†ªÚà!3>ƒ]‚2Ҋͺî1Úc5ý‹•ßìþ=}Å“z“ß²âQ,l½jµÜÑ\½¾=æmœÖ\yÿz{ܸãèY3ÿääb"(¦ioc¨ë“ÖN-Aš9GÏ¥;¿èýÌñŒþ/é/*Ö€ PvcPN×ðå¢Ïs@øÆw78ÌÚ¢M¿QzBe Åúnì˜-–4“ÅRinÊ~ñ ½¡} #­Ä½8ë\oŽ2\èÏ|΃9,e»¨+=­3þÎÙ¢âAêYÃÄöø. #é3í£L‚2Â>±%rökÕ(„KÚ£œY^Iª Ѥ¸çŽÏ™ãÂ=U_v»²é¯móKsÆu+wñ4+³îéñ¿ý)îa*{ýü”)§2û…«áÏÚÇ=‘ƒ1ó]/ï…øVqíî -ÆñüM¿F“jy=´3ï2PáÙ@$Þ~÷µ1ؽx ü²£/jëyº¿åè34þ=zbwÁ/m·^j<3ìQ)-SÕ¼Ÿ-7tö nߤgîÎnÿÐÿvð‚ Kà7Éþ{,9ÊЪüJd«² {üòºö#pÛóY‡‘ Юçæœn-]}htÝe9Ú8¬Ûžž‘´'}z½“aƒ…*£ps=§Õ?Ópæœe3£èF(Šn¡?¤<“¹x½Êv®øé«oÆ÷¹fÆØ±Eê2ŒÍ=––`áÆó"Ý-/quä¬ÈHgD´q¸È³D :¨q’âžYn·S¯oOÁáþÊ#×™)Ù÷@KÑüZ(KXcì=ŽÈWè-lfŽ…‰Ž̘尮ëwÛ‚ TšŠ²Õû[ƒ_ŽÛŠ~i'<Ù;ÝþRápS3¥ã2òGŽë¼Þ÷céÚµæ=þ¡IC5eÆâ‰Ðwcûuˆ¼©D)<¹d=ùÈÝ"Ë™¹˜¡™!À …JŽ|õ†¸y’N"pÒ®àÎ,ùO5œ¼¿Ú=Êü)¦óÂ3æ %:»L+üy¡¿|Ãbœí7ÿeÙÿÏŸn<Mª9ÔãQî±Ô÷ð¡þ83.f)=ˆ‰{ڡ퉯Yƒƒ‰ŽãÔS²3àŽš¼–xŸ")ÞÎ.z“[þ¸jQZ0æt2çò'M š™ íɺÁîÓøÉOµöy{Üèã䯹2nÅ!=î$6\`(ƲæJ‹»è¹pvû _f<##Có¥ü'ðfù$åßdë•/’)ì;ºßZÉ~t;†´%‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$‰€D@" H$ó ŸYqöõ[{—¢õ1t£‡ÁæXÝÕsž†_c7výݯ~øÒ‘ƒf×õ_'2:ö)ÿåoEE­ëy­hþhk#Úª'ô`ëìÄ1cR+þ|òÓ2C=²fóõç×cƒÊNØ\¯>vèvá| ßÕ•óå[^èF;ר¿)R¿¦…0´´¢eßÃ~bÆ«QßAfˆ.¯# ~Oùúú/U{äSkZÔøY–†¡ryW3±?qAu„ÍöLñ\`,Ù)‡2â•ÿÚ¿ />³¯2rcžöˆq…åwátk›«-?vß¾½2qŸ+ 6+¼i¿ˆ­Þ¯*Ë/–埑e$Á$)Å€ÊZcsf‰_)NüÛŠKæù´Ég®–š¤2¿ÿ›î½Íó)ÔþÅï)¿ÑÚÿ[úJ”šdò{þmâùrC×ç¢ö¼ºr£v’çÊÝŸ ¼TÚnÁeÅ£p5iõW¸ò=*ÒùØo8¤àÕdç¡¿ŠïÔZV<•ygáÖHóÓÊ„õ¥0 zöÁQŽk}I&)K>]¦lù7ʃQxÒø\¸àê¢ëlfòß›7t™¼ñòsù/é}ÎÑ´™:s½…½®v(Ì¢rÔÐÙÍsšæ–ä¿<Ï@xyzx>Yž0Õá§!‘tfiÁFru¤WÙ4°Cûâʆ•á$¾†@hR&$¨×ƒlÌCÏî¬íÚÏ O81~–rí”Ù-¬ÌÅöùtû3TáÌ=ñBhq¶ríØ#¸O8Wú•}?Ã1š´#tÔv7ÐÂ_j{&ê’üDØ»LÞ4Ew¹þS©|ÆEã«»LÞüøö»}QÞ8 vö~=3Q³ Â}SÞð%ù³Û %Ysܤpsv;']ïË>&Vq†FO쬱¼ËŠ<”7ZŒ@µ“”‰³¬Wkº¶˜V 7ƒÝ•“r蛄„ËüÛYÙ¸Hm íÀ:„ïHqDFÇÔ™qTÌŸ1CŸŒ^iO+ó»Œü ³Ç]¢9Éðvìf21h´0H zñMûó§1œ3_áÆ·31¢²fT‰ÒVýna;È•ŸÁôÁ‰Žè~”¹!/Çݪëúx\vÄû$EQÆB³” •¹ùžþ™ijú›¸¼mÙLOˆ÷¦£íxãösaoÐÃe#ГMMtÄœUIåûc?qݸ Ò@ħsf|§¨ê´™ö±ÿàÞt‘1Ž!è S<­ñ`ç–Þo¤êÎÑRçc” »Îø'Цd+ û\7X à°{–#ŠòÁfÏžmY²aÇŸ¿rŒ4#Å?à³”sez‚cÜ×f\δ‰ØMº, ÂÝuŠÊF%Ø£×ÿç§L È8™µž‡¨WÚ» ¥0<7 ¯Þ¢÷ÂQã7÷jß‹;E 4ˆ†ðÎ;wá›ÞFùy¦*G9GùdŸuzc“¾ã…î_•/®›PV¥¸å`YþÏU†EØÈ(G¯CšÃmg Îô/PÆr+{P” áÎÃì¯7Ó4§e 7nësŽ!&…¿“øZÔùïñ­Vð»Áww¤¬o8 ÿÏ(—î×?ÁñÒNJgHÌøKuÝEe“¾9˜Ï g9b wåÍ7ù-,óVå.¦éSÁUø>¶àÛú®oŽoº—íÒ¾ÑÈ—ãú¸\ÎE°3åãÁt}†ª*ÿšnZOiÑnìš® Xø°„×¢Ó³!ö¸n.§1·~¨µ'íî^V]GþK«'ñj?½N~£ y®,Õ:Ü3é“à&.]£ñ媔‚Ü¢R½í$_7¡²™§pÃía¤Ë»ár7ÝCÍŠÆ»?l ßøÔ¢ª6Q¯Ü9Ä>¾-ÊjxØ®rëun0üuÈÒ2ЇÅ•.ƒ£8„KÖWß ºÑQW¢6BÑ´ïÌJÁeüˆxf°`kK¨Ê'€ Ä!*hŠòݰ(G+T´¿"ò%Š?¿ˆYؤ5zÈËñƒ…4àáÌeØQ©õCûüÝqæû•‚òäÏUT¥?Wù}Èí.§+Qø2+?ƒ™xÅOõëŒü/‰Z<Â>±å´WF¤©Á·£Ì2¸2,ÈtE`ýÀ œqŒÍë÷Š8–nLº×MñÛ÷Þª¡ÿE÷¹ZÚ 1ݹŸaVåj•ºf¬|Æ߉Þ[\.ÀcÏ 'ü±£°¸Úl™Aï„+¬üOX¨õ?ðs>œ»¼±y0ˆb•J!N(| ºu}cóÅ…Ïʸ ÍÊ÷WÜeL§²$¯å+Ãù!ýš†þÁÕ"Ðø$•G¨Œõ¹¨u ÎrNgÞ+þµX­~7ò@~1gÊ,UQLMeùÒ<û»9×7\P.;¨\÷#¨Npêvdê6?n\ ÁM"]>Îd«°Ìk.h¢ŒïX ïj0eÊù«K6í|Bø,ëõk² þž‡ ©„Í•kWâgMuiìV¾éáw ÐuÃ$Sô\Ç{ÔQ©DPÎUבÿÒêIz'œüFò\ªU“âÌÉ¢_³ª|vXãùø÷Õ¯Ç=¥ývö»’Ÿ,¥Û»Cwé¬U®æŽv·M­÷™ð«=·(·£—aöÊè94/@öÐ /ð·mxtlR®Áö 7ÕÔâ##‹ÅµÇõ˜aÚhúqásþ2Ñ™5$&® XþIw¤­Øî¦yùzpÌø}ÌÐþDÓ,G¢‚ÙŒqyGAà¹Ñq½ah<÷…äñt²5©×ìígŸÍ-ðWʉ¯JŒ+^މ{­R‚¸‡%µÏTÒvÐ3à`Gz‘9šö iJN£‡fX¸+ƒ´Gädk!*¸×Ÿ·Oi0Õ>ò”Δû×?B>¬¯¾^~v*üf(Ê·¿½/¿çê¥Z•‹ qbì¹Áѱ×å9u’­°BÆõÞY±Ñ/àlº'M ¢ ‹f1"bâßJ—àõ¥Š5ßÇùõÿâ)I-rôÜ©žÍµá£Ú¡¢èiçæðF™Ñ‡«-Ç$kaFbÌÃ7òì®Þ·túÀn”GË]†á—Ê/ˆ­ë[ŸÅH§2F*ÄRÜ%謘néHÁûÂa¦r§Y컉ˆ‹+óåOÈãÒô‘ 釠}\<Ãy/]—[·€t v>)ÁóaÁãyÐò.„ö’löÞ£ge}£³ž}v<4™°2Ä÷Ia@l€‰œX0œ6q%†úxh>#ó ¾ÑßøOä_wºÊ¬ëàe>ù+©žF6z'¿QBA:O Pm$eü,ë庮 ò„ÐîqàæÜ嚈g½ÜŸ—u­iìsôÞ³¡8ˆà‹XˆeúÛcžM+ ÃÙ)ô@Þç_t%Í‚û³w1{Ñhïiè‘=•ô|Í$&ì…¡ãÿ¿½+“¢8÷]Ý={ÀrªÈ!b ‚F!æŸDÄDŒF BÔ$‚‘]À AÙ FwgÅ9vÌaâ1æ=}ðP#$>_Eì‚c—î®ü¿Þí¡gvΙe¯~¿™î®ã«ê×WõÕW_UUö2›×b„¹\BÅèïe7½¼^Ù[ïEóvÔ Ãˆ¥ªç/4 g¢Üg;Ï-W±:¾€B£e‹;Ø_wò#!ãp a´C× ?»ÌhÜ(  R¾èN羯òoÁ{×¶/Gøl]Àw½¦jL){£­¤QñZ´f£AÅnMi^šŸ-_´BJç—Op´Gšª>Œàº1e€Å©ªG\u/E>búç[psÁ“ú­T¾ò䃛o¡ àh몸Y¨ïefƒu+¤•âÚI¨ —¦é¬Ã‘sðzWeÞÜ`¨¯c:e®j?7-sêÞ5èÙ—õÌóüÖ%¬&˜g(ßÔ×'ÅÊ<üÒ&ËÙÎ÷††SìÙhþ)À ½ª˜Âq…'óÖ=Nœâ}àwVÀ_‹ï9ÂÖ¢#K¨;t¯{{ÒÏñÇ´¨-T/++| u뜖o¬bÉÂÏ5χ&rÌò²Â‰æ‰o’ãa™ »Á)[ȵﭙz=LA9¥½<ú/Ùð?›¶ùýÿ:ô `uÚgI{;µç0U8v§ù>U¢¦ÑÿY<ÄmëZßµM;Ä€y4ߤ†@H§©è©×­»Lcû+c3ÕœRÎÅKBBJô’FAKñ&F ?pÇ { ù|KS¶’?uä uÖkr—õú–”?†JÕVѺӸî?ÀTÎ¥®g#›³Ðç ílqÀk+î/sŒÛÜqÓ}OjsL¹|Ž÷ìS}oIˆª;/„c–L ©7ª¤N¾ÆŽX1¥>K#hjëêÌõ¹»>ŒÈE·þž¯)N†þ²ù³÷ä—úö¡lÃnmªu¸UC85RZ55«¶GÑ·ÎÆý„TòL†‡¡d¬E©l©”!=òK„G¥bBÀåOH€ÏZ×_†r] ždç¡bÿËZ–>ÿ5Ž]W"m]´2ý™GƒPðMj„t6©‘ŠžúÍÖo·è1Ò"/M•¨4tí÷JÀš6¹ÄW˜§çV7(M}š±º#©íÊ€ÓþDéHû‚Žþ·–)g ÃýZV—P¡ÆMz„Ð:¬ÅÔÊTl&÷ëzs÷Pieˆƒö¢ÅyÏ"C&‚æôFÕ¢O:ÿžÃjX)´¬¬è¯N¼t]¡Á´™œ]oß×ôÞOÊ‘iù/Ê2å»dObç#å{ F±kZ; %WË]Ó hÔ}ªØö26دáÏP •B2yÙÙ,ïŠagoÁêŸuÀòþüÒÅÙZ—ýÆ¡Ÿ¡S©±Q‰¾ íÈ;¹´âFaY/ÂÎeC&ðH´,Ç"p…z>ÃNXÄSEË… ª› s„Ô³^­ñÎþl´¾€)Â[Pú ]}‰ÒeªÓʸܞ¹¯.š5ëÙŠ8ÐÔ³=e›JžÉð°&´%†en€ÝVivQ¥5çüJù˼%K¥ Ñð&ÿ¸<šçÙ 2r§””]{·—ƒ´T{ 1H—oúWRÍPÏY[gÖöÑñšP.@H ­ Òqs²óh h8(  }ϼ3Më¼Lç‚N?£y´Žú¯Åhäú£áSLZlF‡Þ«¾WSPpÞ/K×~…i‹«ðü§%EE{ÿðkµ¯„4 4¶†‡{! ¬Cò,Úàœ4-{Æ«àw_Šü»  µ°÷N/ÏXÆû4!‹¡áY0öí•#{ T™ªÖÛ¡ò–e4¦pvctFÚa… fqy éz¢|…üZœú „S¬j±G!?­e{º_‡†1€w^ïÜ-Ê,¬(úQô©±Vra—åeE¯@~iY+gø|§‡ŸØ®ï¾]Lž‚mWWhÏR=A]hÆ\èßОUfcª½Eö”a¦ê0¾ùM ûþ ~çÁÏýÛ¡ÕhÀʶ»¨Ü©æ™(Ó”6†ü€*ð7X;ŒÃ» ÃÞH/å2Ð{Drñx”vù•ªrŸ%Å3ø&;òçù®&:º%_ÄBšNGcBÆÍXÏEÛ µr„·¸DÛ:'~¬ëIÍ£±€á°„@ÿ—yç«V±BÃÞ_$£™Ókœ>~üÓ!ö™ÈðÞE=Ewé§Q\ªô¡V·ßóÀéKæÏÙ}tö¹-Õü zd5‰®§*#> ·—i;ušÊ’Y¦?š Eá©–%¿ºÚ£ÔêA£ðÔK|rQúÀfÚYöœL¾5êãŠm³‡MŽ—í{cöXR^¼££ë0l`úÐS´|;Šo¦WTœ¦äåŒdÀž©2ÄâQ28®ól?…Œã}¿XáélëbåÃaŒ@4:DH©¨Qg£3Æ(=³®£„”̾Sgâ#0dáæíÁ޳ý1°Í£Ûîv[û)pJF€`RC C¦{°Ž5ê´GjÅ?šÒ֎ТÍ‘ïc‰€Ì8OAÊ|ÇBΛ`:=#¤¨"±U©ÁÕy¤VBNͤÌ×w™àj«ô½SbF Rwúrm É9ÝØ–>Ý$™#ÐYÀ*Œ×w]Ó2žGgÅ—ËÅ0RƧÖ÷Ïgô•¥foåžÑ<˜8#ÐI"ë9,®*K{±„øÇ;³Î?ít™ #À0I Ð!BŠ]M}$‰r%õÆ¢‚À†¤qdFà8F vöP¬ˆOeîÄâÌÑfÊŒ#À$†@‡ )E·ë!Lü9±b%KªZar)86#pü#¥I/ŒÒýé±µ[·ó›~ºL‘`äè0!…Š•­çLÃ*œýÉ1^l±¢ø6cm¼Xé§½ÒA' Úw;º¦åÛ|ou—hyÒ>‘Âp:ª~êk¤xäF´øÉøS¨,ñÒз‰õžîôñ0¡¸t["ùºévôýæ™Ãÿ‰MñŠÒ™/øÓÀ‘·¾U 27•”¦§“OâiæCåFŠ“H]Š”.’ ÞÆP¤º)~{ýå×DÞíxà“öâÄé:h“:ÖUÖè£q”ø Øü=åš™õý<ƒFOšôQS¼·À/ó°óiÏšòÒYî¸Ø±ò5U÷©–§'wþ±ÆWq—Í‚’ûϱdóÖlçÌÅÞ»ƒîÑ–Ø´ãdÏîžó\§°*Èï%œÔZH»Rºó£ûiÞÊÁ€ù4޵ÿKMYI±NBùͧñœƒ_OìÂZ^]Vü{'||@÷ôº)ÒÁ¨Ñp"n_Od>qãÀ÷Çi­'óÊ…ùÆt¾c‘&¥Õ>h0^ÎëÒkL" •Ož•hn FÝ)/=‚ûóú©C‚[B;aáW)š'ÂoMs ж3²ñÀ¡æ²ð4‘žqÞÌ¥†a<ƒB¼Ž­µçaÛê ,ü‡’ç¹eõÒŽš/Vºp:ös¯^š¢þû—ñ»ýŸ ò4á@ÀGqPëÕ5å%#5U… $ª(]Ï€=ŠÐ½(ÃWñûŽ;\íëKÃóˆE#°þ‡¡©!(— ø7裆///ý:Þ# vük’;ž)›ñÕWb`ÿˆ²ßçe·_g½§Vû}ý¼¯x,µ2 bÞž¨€ByAˆ{J‘æw¾–_¹ßã¹àΫ8¡Û³rÇIäž´$r^Áwø4<~ZùÄ£ñVk¿/§‹öCðÐÅÌTÍ-8'X—¨ìÑê8…Õ¥Púö÷Œ¼y|ÏPèì ~ÎB¸T ù®aî {”–\,œZb´ü'¯'2Ÿ¸±àûã Rž¢Éæ_4á¨Íè<|èèL4Ì÷Ÿ"¿zå?Þg(/ …/+¿{òÛ[PâéÄ÷†Ý ÒŽ_¤+vÐŒËr*›ÛÄâ—}®™RZù•6aašæÙªê9£UÙ„Áš¼.7·å@7:ƒ£§ÿ’fóu/Vºp:ôLš„ªò¢  Ð ‹­/QØÚÍï€ÐòIuù]Ûé¹êÞâ‘×ù-êæIMöq÷‡¥®M8,±GËÓÑÿX4ŽÆj¹ó(úÕe%×ãÉH4l‰·¨¶Ê;ç3Wü&LmÊA˜¡|O:U*A¸»ÁIGß÷?ZΪX˜8ñ'—únEýzãǯ³__¾\µs†Ý‚þhH¶¼*>Áî²£kg_°4©´"k%êý8·à>™2hЫjÊlÐ\fñG¼wT„g²êÉ^ž,|B´£ñÖÚ-Û/CðæGŠŠöP<âÈ„gÑ”L"u‰Ò‹Vÿ‰4`“ºxºÞíÔß–àT"êÜÓßgJâ놹X8¹£ÆãדOÜxðýñƒ5àÇÄÍÍo~gp¯q_ƒV%¬W›@! t«¤ê¹°(ߺ» à­@iB¢HE] >ÔÓÂ(ðhƒzÔ?ô®Þª¸½õÿ/./ýeÝ5¹´’¦P‚‚ ÎóRgš–YÕÊìÁ°ðêx釃 CjèáÑ›N ¦â´Lé9Zº`Üx7å{Èâ)š%¬þ?B;a!w4)GΠpÛ¾£tÁ0¨â  n¾µ9Ò  ¢ã¸%esß¡?äw¦ðXaùÞ…§”V\‚“uïÆ¥–ýŒCóèUô—R ¾‹®(£¼6fßîr¤Ýñ| &—4k(å$eà©0HkCi:“{oÎðÅÙY¹çBèX 㕠ߤ^UDQ^÷SÎÛvçëâÅ·…\!?›6¯ÒÖ”ä{œ‰Ï<¸Ÿ>j7+Gý¾ú?”OöÌtü½Ö”ÍÝœíÁî.— >‰Æ[º*H‹¬K¤u¯WÞì*RËmX]r‡G«ãõÆGƒp0êÞ«ñœþ+â· ØÀ>ëÐæ&—”?iŸùÈ÷B%Oû•›®s ''̹Æãד‰OLøz| pÌ„‚‡¶±/Ê7—åË¡ªÐG¨B¡á¤QØ«h™ÞÀùEtK!Èü4[ÉëádBñmÍmæ®…Z×Õ•è„Æ‘ 1Å[yÒ}¡¿>Êî¸cÑ€íÆD¡h¶mÊø¤Ö-îø(«¾¼¼èOh wí4}SÜa‰Þß~ÏÒ®èÃýîø16@ ѰÕ/ÞýT¯o8h]/ºéÿIqUKvGã2â†í@tT¶¦b—^{š" <>tðbHgk\ÜùÄ£áŽÛî{Ó¥eYct>ƒÔ®ÝÕ2Ût¥Ò]UeÐ?àÉi@ƒÄLZòfÔŸÇÃËŽ …7ÍÕRh³Üö,áé:ûó¦;ÎÙ±mΰiÙJŸ>ªªŽƒÉ"ðÐ Àïuü^ƒ³'÷Þ+TõÛÃϼ`à{s†U¾UÐ?®@í½œÊmOùÈ€1uæ·VÀlÖµ.¹âNÔ¿·—ŠF'QÿLòIxH›‡÷cÊê†5›¶=פa†ði¤ºN+Ò³PüÀ_˺õöaÊÀƒš^ ¸v”â÷h[Fa ð |õ¶v5¡üâñëÉÆ' @ÆQ: xvW˜؈’Ð/Ìmw(Ì?ùÇeÞÂb´²c—Qþ-KQ/Bw½ÚÝ F¢hÏùŽEC1†‚°åÈFƒÛÚ†YáÓD)îhVäëÓ¼÷?ƒ¹khlwdcÛP¯ݤ°RZ …²ô þ§4]_ESHäT±·ÐÒÉnh~í¼ªJJv"ì2 ‡ÍÇÍŠø nC ŠãР´©ºš²â'@ã 2 „}Âo÷7ž‚çùnº¥ïƒj(ø.šèn¶® óz³ÓØñÝݳ§»ÓDÂ$¿Ôw4=ïÕÜ;÷ïø‰)%>!!‘Ú#w\Üé‡ûMsú’යõ±Èmæ#ÆŠí) øKÃx‚ÿuFùxEUïOAÚÁɰK HåXƒßœJ…Ο„—Ùyîï2r§¹ýzwçjŠç^Siݵw×:'ÝÉÜÖ¸Ñ>ÕÚvhF`„¥X?ï¢wnfñÀçQK95åžxt#†Çàד™O"bÅž cªI96H¨+!=ŒA‡4=Ppî7}UÁqRÞDv4-ù?Uù#Ý#ÍÍIc»X˜`UŬ"êæü ^¯Áê•[±âbª“ž¯Ñ€6Å”èpÿàõb‚2‚#MèSñ­ ^ƒÛïÚÃ'”Ûä’Škij¦=9“­¶(ÀÔ™ºÒǪKNÓCÑöV¡prdÌOWgõÓíÞŠ!x7½e›ù4¶S89iX#ìUºw×zŽæÜñbñ+óI4Ù¿3 Ði¦{: Œ¥¾’O1m³ åÆ6 *Œþ n­wÊ"-õ¿afzš{ Ëê“÷<¦–ÌðùN?b©œ-sɘ"™jIù 40I¹,½Ç¿qp%Ê÷w%ÐÔ Ó2^Ú“!±cÿ—Ð+ÜU3?q· µqÿ‘Ÿ iû¦C†àèêÕ5ÞâÿS„v‹b«vSX9BׯSœ€Õ­û£4Êm0½¿~É NÎq1q"ò5)Ô¹Ê<¢<É#¦@G{á[=:™’6¥=|b¿´°Lub2/G{•@°[‘Þ˜ÊZJZJ«.uíž»õðÞÆ'ö7DÔ ±òÓ?=þͰPÓ\××ãà«N¯4ë|z iSðõ·ÐëóÓãß -x~z|9þÿw†€Žƒ¯·†€V‚¯B¯4m ¾þz}~zü«á %ÏO/‡ÿ}g8ˆ@ð€_áçàëóÓã_ -z~z|™þ{ ¾ÞZwgˆ@ð€_áöǾ1 ´NÇWB| ÁWB| ÁWB| ÁWB| ÁWB| ÁWB| ÁWB| ÁWB| ÁWB| ÁWB| ÁWB| ÁWB| ÁWB| ÁWB¸7ÔöÅ—_€ ¾ûöƒ@1·?þðý‹a W€qÄ’ƒà+Y¹pJ–½_IBЀµaYKð•Í„]HE–%_YE؀܄`9Gð•«„]¨E–c‚¯Ìv 5B°¾òWZ';.ÁW„]Iv<‚¯x °ã|À+=€íŸàë@^l¿_!ô Àh`û#øÚ9WF'ÛÁ×N ¼Àç`ã»3ýz€×äëâÓñÕ ÃÑý5&_;!ô ËÉÝŤã«†¦ûk:¾&ô ûÉãš㫠¸Ñù5Á×`„^ /ØvÝ‚8„^ ?y½v ¾:ˆ€r{m|uð3ä÷Úsûãß¿ 0ï»o¿1ÐñµQB¯Ð™¾vèøêÒýµ._#ô í’ó«KðÕÁ¬ ïWૃXIî¯ÁW“Ø@þ¯<ÁW“ Á׊„^ 6YÀ²_Mt`™Àr_v~-CðÕäÌOðÕ¤‘ÌKðÕd’ÌGðÕ$Að 1 3ó|5y€ äÓ|5i€LäÓ|5YB|ÈH#Ít_MR 3¹Â4_MN€_ ÐXs?ÁW“(DÎpÁWB|MH ¸FÞp;ÁW“ ÁW€Â4ÜÜFðÕäAð 7×|5éB|¨DÎuî ÉÄðõÇëýÃAv»ýñ‡ï_ Ã6‚¯@®Àh„báææ»o¿1 èøº‘Ð+‚+ÀüžH˜£ãëF‚¯sCÄä"Ð×0¶Q#bº¾^§ã+†"ŒkM]À ò^€åû*u “ޝèö å6(©¹ ”àš@Yj>ôD××Ët|ŠSèj:W›pqȵϠì^Lú¦ãëJº½ÂöM@D.”—¸Ð.u¢Òñõ2Áו_á<…`.˜×EbQÓ!á×óî °•‚0¢¹šˆ '0î>€X{9uˆOÇ×t{Å@1` P /®‘ôI ‡–éøzžàë ‚¯ŒF`?P .×JÆ ~C«„_çÝà˜@Z§õR Þú Àû?uˆAÇ×…t{e„Å€²\L€¶¸fÀD͆Vèø:Oðu!ÁWz¢hÐT ×N8¥VC+„__»70E€v×n\X€òë/ÌíÕi =:¾. Û+=,ÂÄäâ äá k¨ÑP‹Ž¯¯ ¾. øJ4 5}rös€=Ôg¨Aøõs÷†ú HÐ?_±û×QØ»¯T›º_¡ƒÅ€±ׄ\h€uk'¤Ú_ªË@·?þðý‹a8ï‹/¿24»xÀ -ðšk*О?ýôÓÍŸß¼1tCM†¾ûöƒpDðõ ÁWZ¢8À.¸€ë*Å€›Áy?1G :õr~ýäÞ@ûfXÃ×í`„z¡®c©^%ž£Æ8Ñ×¼w¬öq¬:æaÝ>T-ʸ3Ðö‚¨8ÀVêKŒ¸öA‹¾Œ‡÷ÓïbŽ€ý(ŽŽ¯` s:À0Òz­š‚_¾Ú«À8ûRuÈGÇ× ¾øò+ƒ@ÑEOA€œÔ èy{TƒŽ¯`‘`0:ÐÛšQè$ ŽU`¼ýªú ¤§ã+T\Øc¨I €èëØ·2 ß`ÿ‰Ž¯`1`p:Àu킈t’Ç*0îþUíÒÐñ .`Š0´L €(ëD7êÇ*`/ l#ø -øŒ,-¯Q`O c|…Ì •Å €¨Ô·hm]‚žè$ ŽUÀÞØæÞ€Å .9Ô»Þ?< ª®EÕŸß¼™ ÏM·MÿÖ’K!¿Ö^k/®+KŽûÜké}r¬¶0gZ8W8_ÑÃW­¶|…Ä °]‹{€O\ ÆÚä·´£åñý–Öp¶<ö©¹çÚЬýó[ÆfOÝìÚë½ôZR<Š÷œ²sfÏk¨ýü[ÏWPk¿«Öë ¾žñÅ—_V-B°öndi.›¯´rîêÓý€Òkô¢ÅN’{ê1>À\~ÜsÔk—¾–R¡×æTOÇjÊ9³å5¤¬ù:_Ñû¾WÖ¹3°ñ€ã?œ×|ÒtÍý¡Õ¹ÜóïŸó8-ñæÓTS ç:=:¼ªQIñœ5k:K»À–»Ôp[÷œs¯¥`¢cµ­9ç¸Áþ8%ø ;‹Ç,ïý¤©?¸ii>c\Œ+°”@<­talí÷Ïý»´6î-…8uãlwÎôz|¾b,j,°Ü½! mýÁ­¸mïŽQ€×õ2_Ë@ÊuzÕâרŸ¾¾s.FÕÍòŒ{É ^î×-ôõXíiÎ8_1ê^X}®|…•‹ äþÃÝÜ@T°¤ZK wçu%ì nyÝs;÷½×Å·Žûáßs0—¾†\Ç‚cµÝ9“ã5´|¾‚–öÄj+œóÅ—_Ý|÷í7ÃéË…–þ±}üõ¹ÿì}l Ýc¡%¬°U­ÚÓš \ ]&·¼î_GŠq<·#ÿN‘Õè¯!êù ì!-ÁW°°Á¥?ˆ—„[—`¢òarÖ®0š¬TáÖëÛׯ/÷¿·ðž¶ö^F¶r¬–~ìT¯aëá|öȰà+\X<, ¬ýCíÏ—îïÓ¦ó<ÀçÔØ°V¼6J€oÏWÕÓÞœñ¾Ð ÁW˜¡À@É?öç~NGXhŸð+Àr>dÀ¥5Fùk¸ç^{ÄšÙȵøÜ¿û¥ùm®D>Véç|…ý2ð‰à+X0X!×'“?/ð ôLí ë¼V3P×C=ºöïpíùS}]û¥Ÿm1À×c¨°Ô±ÚœIýœ¯À¾R|…£EÂB@Í?´ýÁñøD8ÀzêpuŒTË:÷».ƒ½?9‰¹©©¥Àçî Xè×µÂ@©°mŠN¹—~—Ú¿GÉ×0ÚÕñÞ?< €ÏïÀv%kžÐÃüö»ôŘpLÇW† ˜@ÏÎ…·¦À¥"À¥OL»öüK#Åãìý=jeîßíø?Kïwm\öÎÑRïoŽù=±ŸãùKAës ø…š€s:ŒÌ‡ðÓÙžq»ôsêKyÞ+Ç*€}6,!øŠ“<thMÑ(WiÉã¦zîœE²ÆÒU8¬ùºjÏ×(瞨s F¤¶Œ¬Dw×Që —POÔù8ʼ©ým^ê¶0Oí„Q ¾Òõ‰ÝÉ€Þåü#¿…ÂÞbÑÞß!wWÊQçè–qmµpX+8YûØ/Ñzô¹|¢Æà<|Nø«üXíyœÔ¾Tu±ÚF¨Ëm}Ÿ[˜3ꦴNð•.)¢0²‚_):.®}M­aƒõØ7÷€œ|È•š £Ñõ`=5F$øŠ“9!´úÕFµº¾ÒÿÜ^SûpÞÒ멲¤Î³¶Óe®«[ƽ·š•°h¼9S{ªÛ‚½7¾â$A¥.¶ ~¥c,×MÏ¿WŽ÷½ö±ßêW©)’À8Ôœ¯a4%¾µGý2Î{Xò±kÑõµ9“ê5l}?¯¸Dð•n( ´Eñ s ÈE-M‰pãššËt_5šòcnnû}Z˜3_ƒó£P/a$‚¯tqÒvâ 6Ÿ>(KMÀ9ØæR0ïZ-ë4@–²öuî±R>GéN—KóZ(ïÒ¿÷ü-HêªmÌ™¯¡‡óõÝ"S8ˆ¥÷ÂÒT„›û§Ûz.B›[@-S}ðýÆp®ö”ú±Ö>GÊÚWª \ÎZQ®:ßÈõ­ÜïY¯¿O cVâ5´z¾‚V©•0 _ }¢ƦN༠#Iتþ5xÖËûWJä0lª÷§…9ÓÂ\Và”à+!)šÐÂÔº KÏÎyMõB5C€6ÎÇ@,)ÂdiƼ•q1gò=ÆžŸo)lža_ý|ÅÉS<¨CíAêÚÓž.-„ár=_Î×·uìöŽyDº¾¶3g¶>ç+ˆH„ÞÝœ”(eôÂÒT¤U\3·€¼¦âû‡Páü ü¢D"g˜ìRÐ0Çóæx¾¿fþÒã_ w¶Þé2Êx÷t¬–˜3)Ï©CÀ5ÎW´Eð•0Lˆä\Ñ^ø ?áW€íz s̯F¤>BÏî QNÄÐâÏ‘¿b3uÆ G¥ßWs XBMÀ9€> ¾Ò<Ř—:ø5÷³>)k,)7G¢ûæ2‰Ú"02µz%øŠ“/씪Ëàô8:BEêRcpž /‚¯4K¡€ °í ¬ÿü!»õ1·üœÀm:µ;yFx/Í·vÆ5å{ÑÂûjnç¨5@?_i’B4• ªäì$©KeºÝÈc™+,ÞË8D=öS=ŽóŸð+pŽš#0"5z$øŠ“-¬t)ð•²;çž`Ùš×!$ÖþX.}ÜhïåÚ±>WK kÏ×VÎ=£Í-`9µGçUâ|¥) $Dq-üz-Duí>kr—º^zŽKÿ®Ûë¶÷4çXn}þ”c°õy÷„ÅOÇ^@±c?Çó§>›[À5H`4ê!ôæÞà ÛL«ká®sÅËKóªö{¹÷kéSý^Óý£†³¯7z:öK=¿¹l1Õ"ß?<€çR¨AÇWš 8@T©RÓãmyÌ”¯Cèëfø±ÌõºÍ­¶×Úçs (MM‰Z=|ÅIvJ¸Úû8)^‡ðXºñˆø³QŽó´­1me¾š[Àj“à+U),Ћ­Z÷þlªÇJùz|oKüLŠ÷$Jçߎ•ˆóªåc¿•n±æ@®ïPÓí?|ÿb^ûâ˯ BfŠ"ôìO?ýtõ>%BV×^‡ ×å±:ŸKã™{,k=w‰ç­9®£žƒz¯æ°Õû‡ƒ°‚k<—:H|ß}ûÍðcpoPƒ‚½k%P%ØÕÏxÖzîÏkžö7¦Î@4S½ÒE€åçL¨éÎ0:A.ˆAð•
]½Sÿ ‚¯8qüJ À9€¶ÝJQ "˜j™ï Cø¯ÿý‹Af}xûÎ M|¥¡W áWz Ô äÞKÇÆ¤îAt‚¯9Qy¸-íC„aÜ_`†î'ŸŸiƒ+q¿" ¤$øJVŠ @d¯´@Øèm?# ì!øJ6B¯@T~óÆ ¿~ 4AW`¤ýŽlêD&øJ¶#@4¯À9.£ŸÉOذú… ,pà+É)~ ½Pš°+Àù=’,pŽà+, ë+)»¬ß7 ÁÇî )éö ôL €­¦à†Ð+À¾½”ýTZêD%øŠ!À j¡¬! `¤uoHA¡ ?®m#ˆPnÏõáí;ƒƒÑñV`Žîcö`@~‚¯ì¦À ŒHm€a {2 ÁWvQØ`TÂöhÑÉ‘à+Nz©“ŒI˜Àž ¨Gðv~œßÆ"<oÿf}|e…€OÔLú'0?ôáÞ°–.£èoo÷áí;ƒéø hСW€~÷yöz—à+«(Üœ§† ÐA€qö}¨gà+Npð+áû? m‚¯&q =Œ»´„8_YD¡€^ :pØí|€Ä4ˆC¸€Óý¡="´Mð•«hÖS[hŸ@öŠà+)ÌŒ§÷kDºx°tß´Gð2Ñ\ = Ø?Bl‚¯œ¥ °ŸZ+@;„°„ø_€î +°w?iO m|e–騹Ô% €½%ôãÞpJlX`©oß iêüäØcª‹A=‚¯ÀЛ Ìß×.À/MÞ?<€÷*°e¯©æu¾òÝ^éq“´ý7º @éý¤ÚsªmAy‚¯@7  ¯¿ë]4 Gº¾ÔÝ_@޽§:”%øÊot{%Ò†·àB°e9÷ jVPŽà+ÐüÆàØi½ÀE¢Ñõ Ü>JîEÕ©  ÁW~¦Û+-mÖ˜«'¸È@ë„_Êì  ôžT] ò|ª/ø©×\l€±Ö~ú&øŠn¯§ø”tZ‹„¥º¾äYë öþTý ò|Š,è­Ð ú\× ¥}ªºä#ø:8Ý^ɹ€´N7XjÓõ Ý:­íWÕš ÁW é‚ ™n°¤"ü y¾L·WR-Ð=‚¥]_ö­ÓŒEðXM1 ,´¹.@„ý«z¤ugƤÛ+[áÃF¦FBj¶Ë×a°…±éø Xx6Ѐ¥t~…t_¤sKZ–;ÔS\¼€üë-c»3À¯éØçP_Qca M .¯±`_ Lt|ŒÂ)T€2tàÔT3R/‚}t|…Q¡W€üÔaXCó€ùµt|ˆ‚)Å!€:Žë2ºzÀúõzÚçªÁv:¾Â@ ¦â@Ôj¸DèŸÚl§ã+X$¨äP»Ñáί“pLÇ×Aè0]CâPËzl#ø .ˆE€˜Ôv8Ð̰&Z˜'ø:Ò1¸(е€1¨Áz‚¯ÐÁâgè“ÚÏØ45F^ÿàÁWÊE€q¨ôKíÖ¹7}ÓÀB@u¡oß º_ïà_!Å|ÐX47€1¨÷Àr:¾vLAÔÂ@¿t€ ×µ .Ññ¦{רõO“ƒ:,#ø 2‚ójzXË`‰{CÐ'âRØ`«CméÃÛwè’Ž¯Ð]9HE©?šÀÔuà:ÁW°`Ð!´ ÒšK ¾vÈ'ÿãpñ€ÜÔŸbQÏË_Á@ç|»š½®Q°†à+æ"µ¨KÑ ¾vÆ'þÛæÂµù`6@ûÔoà<ÁW(´YŒh‰zULšÖ"F'ø ™)ÚÐ*Ø¢|íˆOú·ÇE"PÇhš Ì|…L‹Ž…€HÔ³âЀ‘ ¾Bb.•tPrÍ€-_!!Ez Î´Jðµ¾Úª.0èz@}j4ðšà+X\`–ÚW»4C`T‚¯°ƒÂ?½ómGäX[`+ÁWØHQ€‘¨‡-|퀯´*O‘€©‹µ ¾Â ¾Ö €Ñ©µCSƒz |Nð, °ŠZP‹à+, ŸS3j| ÎWYå§€ó¦Ú™úkרCð.P|€ëÔÑêрѾŠõ°œzP‚à+ÌP¤€õÔÕ€Ü_óVy(ÎÀvêk@N‚¯pDQöSgHK½>¹7`q€Ô¦zÛ‡·ï DÓ·ƒ½x0ð«ÿþŸÿ3„ñŸøƒ°’à+ܽ@¯@)®ô°† Á,#øÊð„^ áW 'Wz]ׄ`λ31M_]Å~B¯Ÿ:Ú zÀZ0&ÁW†¥Øå¨Ç)0âÚÀç_’";”§.—o `‚?Œ¼Z>|e8ŠëPú°…°X_Š¢:Ô§N¬!äÖE€c‚¯ C1Ú¡^,!ÜÖG€S‚¯}ýñ£AXIÚ£n\"ÔÖI€9‚¯tOñbæÎ|¥kB¯Ð65¼t|[½zk&À%‚¯tKÁbPË€m„_ ¾Pð+0Þ®|¥KŠäº¬çƒ#Àh_éŽâ8Ä¥¾ãÚ–| æë ŠâŸ:¬ã$ÀH_é†b8Ä$¬,%øJ„^ /j~ÀÁWš$üºÞ×?€Aé ŒBð•ð¿ _êÀ1ÁWBSô€Øt§Ö|%,¡WÃÿ³wÿ°r]÷Ç_ViÜí¦YdÁÒ•!!"˜¥«¤ÔŠS§ b$)ÌŽPGÙ@ÆV©-k³Àz½@R¹ €AHP¥*¸ÙÝNMÚ—×#>>Οûçœ{~¿s>€0-‰ofν3÷¼™/Ï{À‰ð€ðįÀDøJJÞä€ñx_¾’Ž7·`LÂWÒðã`lÂWRñ¦6à}B—ð5‘~öÙÐß›Ù06á+)ˆ^€û¼gc¾’ø€5^¼xñêàù@n-Ñyó¸dzÿðå“§8ܹ`ëÙ³géncÔc…ã|‹çšç#q™øJh¢W A—cB_玩¤žÄ$| 5ˆDØ}>¯=· á+ay³ˆBü 1_HÏ_¤"Çç7PÏcK@DÞ¤ÖšÞW|ùä©…^yöìÙæ?+p€¸L|%Ñ+hr›¢ÙKá¬ç7´%| þb=‰8òÛ35¨CøJ(Þ”¢¶@;ÂWºâ/Ø‘ˆã ?S_ –Ç–€(¼ ”2½ßøòÉS jŠãÎ…®Ó?‹Î] rE~uÖwÏºÞ ¨:fΛãÖÓšöq®–~-`&|%Ñ+@]K§ÏÞÿï¶Z¥B¯µ_gËã[{?oݧk_ûôïÖ¬ÅÚÇT:®=}½%÷£Ä}ð\<ö¸f>¶zkñZÀ›„¯tÉÔW …ˆS_—Æ`—þ¬8«Ü/]˭ǬFP·ö¾ˆúrWëZç5®ôkç=²´fÚ+ГKAÓž8k«·Ùâ~,Ò1sì=­+ _è–¿xŒ¬dÌ% ˷έ™sÆsq´uulŽóØÐ’7€MS_ÏEP¥¬öžûwÉ¥x+Ê}Ïpì׬kë¯ëœ¹íÜã+õ¸"MˆáyxäùêØÔ#| kÓ_Àùä©…w)~=Â¥Û]uµ¼ß{×{É:Ô 1o}Ý[ÿ~ë1;ýûš¡õ’û æËw\3?#¿Æí}-à¶G–€VL{FÔ*\[™TC‰cvÔ}è5æ«qÎG8®ž/^ã2¾Ð=h¥E\Ujª¢à:ú|`Ùºí™*ê¸ÞúØ£¼ÆyÝ8ÆcK@ Þd€Ë¶†|%ƒÈµ÷áÜíOÿ¿U ØzZkÔã Ù™ø Àü…| •Ì?RûÜ}à-;¾@ÎçïÖ×8¯Ç¾r8o.£i¿Š±b+õcÛ¯ýY±r™çÑšã⸶} _ÂW†á/æ½ÂAyÂJ¯qÄ"|åPÞTFÕrê+°þùºvÊ+pŒÇ–€‘LAÿå“§hbŠèÎ…®Ó?«Ø kùùæùÑ?Ç`<&¾0$“˜ á+‡™¦(ŒNhÛ _Ž¿¨´¶äG°mŸbtˆé±%àÞD¸m íJG±"[ g^ãÆcâ+Còö€ÖL™ÏG`=á+Õyóà¼#¦» ÷€ž_3½ÆŒGø À°üÅ} *!×8J‡|çþ¬¿ýXl=Ž+Ô#|€†Äk°œð•ªLKhïÈ ²%&]šx»m ¬[ó·äÚ}\Gy>:÷Æ"|`hþ?AÉ©¯µÃ=bÿ#¿6Žk¤uõ0á+pDȶ& ›þÛ’!ÙÒ¯%^Û~¼¨û\l1õµÖqíõùØò5€ã_©Æ”€6®E´·B¯‡1Ø‘ñëÚ¥ÛhÀ]‹-¯Ý·kÿ޴׸ÏÅÇ5óó1êkÇxl Ýôù_>yj!€æ¦˜«T„uík­½é¿_Û•¼í2†nâ¼Ïŵυǵ×çcË×8Ú2ñ•*L{ئd|5zÈ•íñ;öÎÞkûî<“ð:V" Ûò5öÜîÈ1[«ãÅòuÜ2¡´õqíùùè90á+ÜùIV@,¥#¬­_oús{ƒ¹µ>Êco}ü·ÆÆ>ÇuÄçc«×8Úøµ_þóßÿ»exÛïÿÁ_„»O?üì³4ëç b £—Ož¦ ?z÷]Ø­ÖçR>Câœ/¾üÚ"4pmbeͬÕí¶¾íš÷=Ãý'æqíùù˜ùù¾×w¾ý-O®ŽÕ|ßÂû yüüoþrø5¾^ |ÝÎ@VÂW€™ð•# _ ákß„¯L„¯wwœ0d@lÂWR¾R”i@-ÂW¸Ç_ð€¸„¯¤ |¥S€š„¯¤ |€ü„+ˆIø @ ÂWŠ0ý¨Mø @ ÂW8ÃOº€x„¯¤ |e7S€#_HAø ø‰WPßw¾ý-‹,&| á+@r¦¨£¾²‹óEø @S&ÔK _à ?ù €èþí÷~Ï"þМ`XBø ”¿<ŒFø @Âàá+›½÷Õ§ñ—F€ _CÀ®™×_EÈ\"|€ÞûêS‹¿€ë$À9ÂWBõ€ë#ÀC-À8¾øòk‹°Ñ¯ÿíߦ¸Ÿ:T)<þÜ",4Å=Y®ÃpÄu`tÂW€Î‰] ž?|3QÂ\'~ÀµPð p"|èØr9…°X€ËįŒ| à5á+›¼÷Õ§¼@nX€ëNထ®{¼é‘%èƒèú1°§€·M!€Þ¯uœgâ+@r‚Wè׿šþ p™ °ôx]à:á+@b¢WèŸøඇ¡€Œ×/–¾$%z€qLñëD °ŒúõÈä#zF$|HFô ã:M~•ð Ñ+ ~F&|HFü ŒJø „i¯À脯 ™ú ŒHø €i¯ÂW€´L}F#|δW€™ð 1S_€‘_HAø Ø_~m~Eø @ ÂW€ä>üðC‹ á±%Èãg|ÿÿÿþ'[`ÂW€¯çþ¹èð °KÁë­ÿV ôHø ÔšèõÚŸÁ½¾³'x½õõD°@fÂW€@JG¯×¾¾ÈFø ÀšàõOðƒo~ÿã>*r›"X á+@cK£×ûÁë¹&‚z'|¸áÅ‹¯þ÷Ù³gñÂ1(jë”×%ÿè‘ð€fN1S ‚¨åëm­Úœ£%ïCécxÄmŒp¬pLÖ(¼Þús"X ÂWºp?v^_Ÿé÷ÖhÝšE¼oŽaŽc…c pÎÑ뵯#‚2¾Ð“MϯÇÃf}òWÇri¼ÞúÚ"X á+ÝE”èõÚm‰`€ „¯tMü:O¾õcÃß@Qƒ×[·/‚¢¾ÊÖˆOØy{]﯑X ¾¥ÑkëàõÖ}Á‘_èÂ)ä<ÀšŠùæÑç7Ä“iÊëÚû(‚Z¾Е‡“MaâWˆ#ó”×µ÷[ ´ |Ø©·)¯k‡8Ê#K@oÎM¾4–Þ9Ç 5S^{‰^/=¶½oZËÓ/€sL|€•Ά{~Ìü­`1ú°¿vÿkÞ÷V·ÁôøÎ=þéŸE|ì#«hëÝbÍK¿fD2â”×µÕ$X 4á+üÊ­8ëZLvúwkb®¥:·|íµ­Ä×»õß•Ýj­Ù–ÇSr=Gy>•8Gj«½Ï‘Öþè×”¯™ž£@4k¦¼ŽL ”&|€–Æd¥#ËK®e¼µõ¾Ÿþì±m´5«%êÔ×–çˆ×¨ÏÒ¯™{o×9”fÊëv"X „G–޳' ,ù5²Ý÷½·ÝjÍj»ôxŽÔXË£îËži¯™×üˆsOô ”4Å–k¦¼Š^—­ÑÞu:—5A2›ð®(„EýZÑï{¯ÑªçJ?ÇüÒý­ý8"®y‹c'zŽ`Êk]"X`Ç––«5a3ÚäÎ÷ýÚý_ú#í¯=þµk¶ô63ž“çs¤Ç[ó!ökJíû!zj¼ïþ:þø£Š»÷?ùØÂ@G„¯tgÏ ¿äÖŸ¿õï·ÆY§ß2lÜ–]Š2÷ºvÛ[Ölé¹ÓÕZçˆçHöcÕzÍzMÙûšYó¶FôßÿúïrÜѯáï˜üOcK£WÁk="Xàá+]Éô#Ò×ÄY­ÃÆ÷}kd·'j-ˆk95µå9²ç>Gxžm}Ì™_SJ½Þ‹^!§4Q&0 S^c*Á ` /á+ݨqí ©jÆeµÃÀRaÙ–ÈnïmŸns”®U<Úò9êuåÚã¹uŸ÷þùŒ¯)-'QCißûÞ÷†}ìŸWz쟋\àLyÍ¡Dk ,ä%|  ×"¨ˆácOa`d¦?â\ïï5åÒ}ö|;Ò€½LyÍK ã¾5ýÈ×ï~÷»Ã=îÒV†É…£8Ù4‘µÕgZçΑÑõÚÇêÚÚz=¶2åµ§c´5€}x>ˆ` ¦G– †)t½ÿ‹ý"P¥~ üµ?{ddwÔš–^7çýqçIöãÔúþߺýÞ^Sö¾.ì1Ž¢×>MÇëô«Ä9²f"0PŸ‰¯ˆ[ëIî'2Ã9òúqî D÷þyö+€‡Z¯{¦F= ¾ÿL‚€>_ r=Žà æçÁ¹¨RxÌëÏ¥õç˜óàš5“;MyíoD°Ðá+@B×ã‰ùŽ!2Ëõœh¿:Ghýš/ð.i1åµW=¬‘ò¾ t-G¬Dxºð® |—Ę^ÿÖ_'L}–ˆ2åuúÚ{Ë(z ƒE°‹ð`¡+A«©¯£®õ’µ§ì:_â&Q‚ׇ·“5~e®â¾, t²`–Q:¤tL¶ËÎq`©ˆÑkV#¯b¾< r…ËLPìG­‰˜Îz?Ç€Ø2¯™¦¾Š‚ϯ…Úzd €ÑM¡ëý_½¹ÿ™’¹~Íö¬Ûµ?'Ьw¼€q,^§€±uÐ=(°FÖgïMçìé°œðŽÐ–9’ %û:žÎ‘ºk+ÆlÏkŒaM8(æ\&ËDÚD°p¼Ç–踕–¦ðíaxµçÇoŸ‹¸zŒëJ¯ëÎ).¯U‰spÏš{MY·6¼ö'ü»áïãË'O(V˼N÷)r`zÿ¾ †ïVŸg{ŽíýóúýO>¶°ð€ðèŽÐà«òußûêS'°[æ)¯=Á®'‚€²„¯@*BW2ªbÕŽ /Ý÷VaãšÛ½,ŽÀ–>£#Žaì×”VF9G 4G/S^ÏÝ× S_ÏÁn;ÞçÖoÏóA ÀhY ²)t½ÿ z²&\‹8Ápíý_û.Ej{ÖBˆyì:Ô>Gèë5%Ûù k͔׌fÑèqž~q·ê|Ý{ü§çÇéŒ@ø „"t½»û“?þÝo~ÑkçµíÚ¿?*îºv;·â¹‡÷¿Tl·uÍjÝîèÁf¦s¤ô´Û’ÿÝ(¯)­« §51Ÿ‰£qˆ`×ÁÀr-Ð’)®wW^‰d]ûÑêkïûš9^òvG9V%Ži¦s¤ö±*½Nç¾¾ó4þù oM´×Kð:=ŽCÑÓc&ßm:§÷œ÷ŸGïò±… &¾‡y8ÍÕDWS]GS2pk1™±Õ4ÈR·»öëŒðãÒK?Æìçˆ×”þ‰a ‡£×^Ï}¦¿n?'L‚€7™ø TcšëLÜÊ}%¦¶ ÔZÝÿ½·;jé·Þ#¯·©¯ÏÈÁë(¦øÕ±ÛÎ$X˜ _€b„®3¡+·œ"³µAV”8mkL¶÷þo¹Ý·™ñ|,ý[œ#G«-„kÝ¿ì¯)G›ÖI\ ñˆ^ß||{¢ÆRq$ñÏ“ÇY @F¿öËþû· oûý?ø‹p÷釟}æ¾¼÷Õ§N„®wõ#×/¾üÚ‰6€[±Z‰@ëÜm” ¿®ÝÿšqÙ‘·Ûê1ŽpŽ;V9_Sˆéùóçbj}.å3&®yùä©Eàõ²-1ãµ5ŠÁšøï¼9G›“½–ýÒ?z÷] œÄÏÿæ/‡__€Å„®¦¹RGö­Õý?òv…‚Ž•õ(oiô*”,³F&ÁŽw.˜ @¯„¯ÀY"×™ÐÊ2åuùc_.nY#ì8çP‰ã|zÎ `ˆBø ¼"t ] S^×¹¿–Z#ì8çÒÞãl ,Q_`PBיЕQùQèÀ‘Ly-«Ö-2í~³ý8ˆ`ÈHø ƒºŠ\׋/,ÐŒ)¯ûÜQ­5ίɞàY ÀÑ„¯Ð)¡«Ð&¢W S^˱>yމ`ˆNø ¹Î„®ðÚµàõÙ³g¨Ê”WÈK @tÂWHHè:ºÂkK'»Š^€šLyÍmOäHŸD°D$|€„®3¡+\6­·âWÑ+P‹àú'‚ á+$t¹Bi¢W Ñ+ŽýØÇS ÀÑ„¯€ÐUè %œ›ú*xj¼’Í–@Ó¹»nD°Aø ¹Î„®P‡Ð8‚è•ÈöÄ—×¾V‹sùtûYžG"XŽ |€Ê„®3¡+ä'x%š’‘ëšÛª}~?|\ÓÿÏöœÁP‹ð ºÎ„®Çyþü¹E º¥Ñ«à5§#ÒîcÍ6ñX«t+€›ðvºŠ\ g¦¼²×–ó"SüY*‚½õ˜3N}½u>l=ÎÓë’ø`\ÂWXIè*t€Q˜òÊzšpº5‚]º½Ä¯çÖ¨ÇI·Ô!|€+D®3¡k;ßùö·î¾øòk À¡Ly¥–‘âÆ%¬ØóükÉ’u1õ`\ÂW¸Gè:ºäñüùs‹@Q¦¼By#Ø=ÁkoS_/½¾üÎoÿÖ«ßð‡äà ÂW†&t ]€S^{v™-J4u4ßz÷¿ž‚דO~úñ+o¾0¡«È5£ï|û[w_|ùµ… šVS^÷N¾ŒÈ$ÜËë!°åš‡Á+\"| kBW¡+@Ïž?nإŔ×^YŽÖÓÔWÑ+k_è†Èu&tí“©¯”)xíaê«è•V²Ç¯·‚×ÿñ?ÿ—ƒ À[„¯¤%t ]Ç!~î3퀭"NyÍ¿ ^a;Ñ+[ _HCè:º z`‹ˆÁkf™Öè\T|ºÿÙ§í–:v™×!ÛÔWÁ+{ _Kè*råm¦¾°ÅÒèµe<—eêkôÀpɶx ­ŽíÒÇzÿ¿ËÁf‰_E¯” | ¡«Ð•eį06Ó^X#Û”×ÈñkĨ0z yôš•º½ìlD‚WJ¾ЄÈu&te+ñ+ŒIô À¦¼fr [­—³Íy›)‚:õõZô*x` á+‡ºÎ„®”$~€±ˆ^X*Û”×s÷)r`xÿ¾ÕZ?‘kÌó4C)~5å€Z„¯T!t ]©Mü c½°”)¯Ç*ÁŠ\ó›ŽÙm¦¼P“ð€"„®"WÚ¿@¿¯,•}Êë¹û˜-.\ÁFxL-¦–^ºŒv†ó²åÔWS^8‚ð€M„®BWb™â׉ú!z`©^§¼fŒ_OL}ó|ëaÂpÆûÈu¼p$á+7‰\gBW20ýò¼°ToS^!*!óu¢WŽ&|à-BיЕ¬NÓ_'"XÈCð ÀR#¯™§¾’_ös¯öÔWÁ+­_ºþŠÐ•‰` 6±+k8åUüJ ½œsµâWÑ+- _$t¹2¦û,püuÒµ§/Ÿ< ôî»thÄà•ºÄÄÖe Á+_ tÐöšé:À¢WS_K®cO·SZçXÉc!z á+@gD®3Q®¡®Ia„)¯{âׇë#¢eÔçÑ‚W¢¾$'t‰Šˆx=u}`¯[Ó^…z·[£ûÿ¬çÖùÁ^¢W"¾$#t ‰ˆ~mu­ ¶O~ú“o~ÿÿøOC<æ5S_—FŸ=E°BWJ¼™ð 8¡«p€|×Y×.j»½NîGj½G°Kâ×­h¦vOäÚó”[ö»½ ^ˆ@ø ŒÐU,@îk®ë­Á>Trâi¤Ö$WŽ~í8Gô @ÂW€†D®3½\ƒ]Óˆ¦×öÜÔךqè‘löÈU¤›ûuâÁ+Ñ_$t‰‚èñzìúÀ‘þïÿûÿßüþ7þóZôgz‹`OñëÑ¡eÉV$Jk¢W2¾T$t èýÚìZ@K#G°­Ãѵ¬Ð•(¯d&|(Hè*ü`¼ë´k‘˜Ûލ•,D¯d'|ØAè*ö`ìk¶ë ‘‰`9gÉdZú$x ÂW€…D®3®á®‰ä³'‚À²”©·q‰^è‰ðà¡ëLÔ€ë¹ë#}YÁš y ^è‘ðàW„®3!®í®•ŒãÁ®;ÁBl¢Wz%|†%tîà:ïÚ ³µS`'"XZùñG}óû?ýÁ,È•çæ9‚W²¾ÃºŠupÍw€ÛD°ýºŒÖúú5CÔs÷¿ömfs-z¼Ð á+Ð%‘ëL €=€k*ì!‚e­ZÓXkG»Ù™ò ÀH„¯@„®3Qö®¯P‹–µJD°K‚ב§¾ ^‘ðHIè:â@ݽk-œ'‚e­-¬)¯ËŸSçˆ^è•ðHAè*¼€£÷ ®½°Œ–µnE°[‚ב¦¾ ^ðIè*¶€–{×aØFËZ#Ø=S^Gˆ_E¯ |¹Î6cáš eˆ`ãÙ–Öù¾µ&x€×„¯Àá„®3Q ÄÛO¸>@"Ø8¢Ç¯{õ8õUô o¾Õ ]gBˆ½·p­€cˆ`Û;…¡½°½Ä¯‚×»»ÿóóÿí À[„¯@qBWá dÛg¸v@"ضz`3½ÞŽ^ßÿäc' À „¯ÀnBW± dÞs¸Ž@ "ØvîOGí%‚Í:õUðjÊ+· _€UD®3 ô±qM€˜D°íôÁf‹_¯E¯#¯“¥Ñ«i¯c¾W ]g¢èo?âú9ˆ`ÛéqlD¦¼®›ò*z@ø ¼Aè:Â@ß{×zÈIÛNÖ6úÔWS^My`=á+ Nè*|€Ñö)®ýÐl;Ù"؈ñ«)¯¦¼°ð#t»ÀÈ{ûè“¶¬“`[¼ÎLy`á+tLä:¸€=Œ=ŒCË9¦¾Š^My  á+tDè:µ€ýŒý0Á#ˤ×Vñ«àufÊ+¥_ 1¡ëLÈö6ö À-"Øò²¯-‰^My <á+$"t®€}޽°Ÿv¿¬ÑëQS_¯‚Wê¾@`BW± ØóØGu‰`×éaÊkíøUô*z .á+!r TÀÈžhE{]ÑkM‚WÁ+Ǿ@#B×™(ì‡ì/€ˆD°¯õ¼–žúz-z!xˆ^8Šð"t Q€š{#{  l%ÃÓVLy¼p<á+T"tžÇî“ì=€#ì‰`³°=ª5˜òº{(ˆIËH¬èÕ”Wú%|`3‘ëL¤ØÚO@6"Xz`¯3S^è™ð€Å„®3a`?ho=ÁÒK+z5å€1_¸Hè:cˆ^`"X2F°‚×™)¯ŒBø À7„®â €£öˆö]Ÿ– ¬èÕ”WÆ#|˜ÐUpÐb¿hùˆ`‰Á ^¯ŒKø 0‘ëLdÐvÿh?ù‰`iÁ^‹^G^'¢WF&|è”Ðu&¬ˆ³—´7€þˆ`92‚5åUð á+@'„®31@Ì}¥}ô¯t+€Í§V+x-^¯ôNø ”ÐU<eißã)Áš›[©VôjÊ+<$|HBè*˜È¸ß´‡D°ì`Ï1åõòó z'|Hä:IäÞÚÏâ¼­ìD›Û–ö!S^Ï?¯`ÂW€„®3a@?{Q{;àšS`'"ØÜÖF°¦¼^~ÀH„¯ ]gb€>÷¥öyÀ"X&·"XS^/?o`4ÂW€]Å£ìQíû€=D°ŒÊ”WXNø PÐUð0â~Õ(IËLy€õ„¯;‰\g"€±÷¯öƒ@M"Xz#x€í„¯+ ]gÂ{Y{C ,Ù‰^`á+À B×™˜À¾Ö>ˆFK&‚W(Cø ð€ÐU¼`kßä#‚%2Ñ+”#|†'t,ØïÚC}Á…àʾC¹ÎD ö¿ö“À(D°´²4z¼À:ÂW kB×™0À^ØÞ@Ë1Ly€º„¯@W„®31€}±}&Àu"Xj0å꾩 ]ųG¶ïz&‚e/S^à8ÂW ¡«à€ã÷Ëö ÀHD°¬eÊ+Kø „%r‰ h¹¶F&‚åS^  á+†Ðu&, Ê^ÚÞà5,÷™ò í_€f„®31÷Õö©—‰`ÇeÊ+´'|#tcmß °œv ‚WˆCø T#t o¿m °¶O¢WˆEø !r‰ȼÿ¶Ÿ(G›Ÿàb¾›]gÂzÙ‹ÛÛÔ#‚ÍGô q _€E„®31=îËísŽ3E’Ÿ?{öê÷ÿò¯¿ÜüuD°u^ >á+p–ÐÕ‡ÿŒ±G·ïhç¿þæùæ÷"Øö–F¯‚WhKø ¼"tõ?ãí×íâÁ¶cÊ+ä"|€‰\g>ä`äý»ý0@\¥#Xìe¦¼@>ÂW€Ðuæƒ}ìåí²)Ášû6S^éÍ{_}j€a_ CB×™ó°¯·OèÉÁ¾óâÅ«ßÿìƒïoú"XS^ ;á+t@èêÃ{ìñí›ÈnšT÷òÉS  ¢Ì­ìd´Ö”WèƒðºúÀû}{h&÷Mìe¦¼@?„¯œÈuæCzìÿí§¸Nû6S^ ?ÂWFè:óÁ<¾°·`<ï}õéÝË'O-0z+xe´ë'ÀH„¯Ð˜ÐuæÃx|_`Ÿ @£E°¢Wè›ð&tõá;ñ=‚}7çôÁ ^‘i¯Àˆ„¯P™ÐÕîpô÷ öàd6X€ßK؛РS_¡­,¬)¯0&á+¬ tù0â}_aŸ@oįCÄÖ”W0í›ð®ºúð2|aßÀ"D°¦¼‚è@ø ÷]}`پ߰‡ g¦¾B\GG°¦¼'ÂW†%rùò~ÿa?Àį_ö“Ÿþä›ßð‡´ú~@Ï×E€Ñ _†Ðuæƒqèã{{{F"~…Ì€¼D¯p<ñ+ôKìÊÈ×6®¾@#>¼€~ˆ^ S $€ —k× _E¼Q@4ïôð¼zñÂßä½@ ¦¿ùÀrÂWà Þ ÓõÉ›@k¢WˆÅôW²^»XNø ó ?=^˼9Eô q `Èr­`=á+ Æ7÷Œt­ó†ØOÔôN­×Ú/,îìÆØïyO€h×&¶¾Â|#€k 7À>Ö]ïìú"€ ʵ€ý„¯Ð)ß´Àåk£7ÀÖ^í!ú € Õµ€r„¯ÐߤÀòë¥7ð\°w€­×M{I€Ü°q á+tÂ7å°ýúéÍì€=×TûI€¼îïå¼o@©k u _!9߀@¹ë©7$°gì'Æ%‚`ϵ€ã_!)ßl@½ë«7)°gö^{í)r{¸Ÿó> ¾çˆCø Éø¦Ž»Þzã{FÀž€sû:ï¿°ÇçÏžYHúú@ ÂWHÂ7ÏÐîúëM ì{Jî»´¿óþ µ½óâ…E€_׈Iø øÚ_‹½áý"Pâzm_ з-û=ïíÄx= á+æ›\ˆw]öF ö‹€}%%ÙŽçó¿þ;ç À,Ä$b×h¸v:À¾`DÂWÈÿZíz½"`_ p¼Ç–âðAä»vû±RØ+¥®óö–·™ø AÀ5œgàšÀuÂWÀ‡àZ—Î-çØ[ðšðóa¸¦ƒs x¸°8Oø ù\ÛÁ¹Ø,'|…F|pý^ã]ç±Oì ê¾B>°×{pÞöë _á`>¨×}8w®8_{K€Û„¯p P`Ø7l'|…ƒø`ìÀù”Ø?ØC#¾Â|öà¼ì%ö¾Be>„ì p>”!|…Š €½ÎÀ¾ á+TâƒÀǰ¿(Kø ˜ø‰ð*ða`¯€ãØk”'|…Â|ÈØ3àxöu_ a‚ã `ï°Žð òÁö‡õ_¡Q`€=&@]ÂW€ '€½À°w7ËMdg€¥TV¹ïæ’JÍ63a ãlðx.!„¸©©d=kÝÂ\F¶Šd $«%îsN÷ó°@ƶZêî*^»ßóu|Н €,c P?ÅWÈ€¢$2&ÀbН°&Ù Н°ÙÇ 9Н-fqÐ&Н°" Ç YН-g‘ÐН° YÇ Yš§ø @_Ö`r »4Gñ–äâ s@Н+RP  ®KÁÅWð { gAñ€"(¾¬À¤4@–hžâ+Täb ƒ@ZНKRH?S†â+@)%R|€Ì)$À%ÅW¨@ÙÙ€.yº±a'@&\Ÿ‚iНå¢ PÅWŠ ø 4ÆÄqÖ¡ø øå “à8äAñ Ã,îÈ—ßÝÀ—_ £žnlØ Eñh”©U¬Jñ€"(¾T` ]ãv§–ëS0›â+øÁŠ ø ÐqYàÿJñG»æó ºì?ýóÜÏýå×ÿÚAȯP“¦zKO76ìlŠ£ø À”ë ³¾F‰ù€¦(¾p¡Ja`Ñ÷) ¿ËS¬¾úý7;€Ê_:nÕÂÀ¢çR"@~€ò…Ûžž v4(,æS|è°˜¥ëž[‰ù€_:¨ÎÂÀ¢í) ¿“Â4«¯~ÿÍŽ€†…;<@‰_:¦éÒÀuÛW" çì*¿д°0 ¸žâ+@GäP¸î5) ¿@žÂ¨“Á Öm˜ú @UН-—ca`ÑëT"_åWºÆ´W¨Fñ ÅJ) \÷º•äWùºÁÔWhN¸³”Jñ …J- Ty/Šòk)ïEv *Ó^¡:ÅW€©£0ðãñO'þíÙ³eñ ä×YÙ5—üjm¦A µoÇÔWQ|h‰Ø¥ããŸzýk> ‡ÃÞóçO³xÏJòë¢ÏɯäÈ´WXŽâ+@ábž<9éõûý¥¾~D‰ùU~€u™ú õ wt€’)¾*va xüøÇ‹¿Ã$¬eÊ#¡D0¼¸§ì°÷âÅ¿³Ø7J¤ô§_~±2ò¿¯¿¶äWù€¬˜ö ËS|(PìÒÀÑá“•Š³„§ åQ !P" K”]Ë96аòë$ù S¡NƒF¶eê+ó(¾$vaàðÑãF^÷E‰àr”VïÅËgYì?%bRv-û¸)Àt;¿ŽnɯȮ¤ ‹t[Ši¯aA”Nñ uÜöQ”Ò@˜²5\ê;+Њí9†J]ȯ˓_‘YiòØÈ¤0ÍÔWfQ|È\ìÒÀþþá§Û†Âj?é{;:</_=Ïb+P•ò@;©¢@Îù5†ånɯȬ¤8fr)9 Ó¡N;Ö”bÚ+´…â+@¦böözUŠ®Ãá0b± º£Ã'Ÿ¶ßë½z­D@ÞÚl r̯³nɯò+òª\ ífê+m͹)„…lÐН™©ã¶°»»û]ÒÏ|ípâ6¶¯^¿ÈâX((tëX+¤Ë¯{Sù5ò+ò*r)]ÓôÔWåW&)¾d$vi`g{¯W}øUž5ØP"ÝŒöµ )tó˜+¤É¯ÓÉ4ÿ…[ò+ò*M[Ù RM{…6Q|È@ìÂÀÖöî—…€ZcýO/xÚ£‰IZJ4I‰ ÛÇ^Á Q~](ÿ€+¿"¯"›@<¦¾Ж\›J¸s´…â+@b±K·v*|Õœ’Àðüßûu•f—YWJÃaoüœoÞ¼Ìâø)´ ‰òëTdͽäº8ëæ˜_eWyÙÖ.šŸ nSù€@ñ ‘Ø…ÍÍ­^TZ­µÀš§ƒƒ£ñ[?=}™ÅqU$ä׊ùuU-ÜÊ1¿ZÄU>¥×îwåW€ò˜ö ñ(¾4,vaàŸ6¯- LÏÆºú(FG |ÿpf nI`ÙgÛß?Çéé«,޵A™ ˜<” ùµÆü:Nëœí:?¿¦%¿"«"ŸP*S_ zŽâQ|hPìÒÀƒû›‘živ½ ÷ÊV±¿8.7œ)P"³Î å@~m:¿Î[¸;©¦]¸%¿"«"ŸÀr”_ :Ó^i#ÅW€Ä. Ü»ÿ ÷‡ó?Ÿ›;Å5B/ Þ½Nl§Æ)\{{ÆÏÎ^gq>(òk¼íLÆ^ùU~m+¥W 7)¦¾ʯ”´WˆOñ F± Á½{jy­±KÍY­ÒpQ"MÒz«DÀ4e®;7LÕä×ê~øáþyÆ\!d¶áÖ‘ɯ€| PžÔ¥WÓ^i+ÅW€šD/ ܽ7ÕLžÿéÏjÔ`­s:Ö2ÛÙÛ=?>{û&‹sF‰hE~Œ©óòëŠRÄÞ8ùuýY´òkwY Å¼óBù€®2õ€Üó*PÅW€Èbîܹ»Ú”¬Þ烲®>*wºkývwÆûëí»Ó,Î#%‚f)PåQ,ä׈ùu"œôzi™‚­ü @©“Á ɶ•_È‘Ò+ÔKñ ’Ø…àÎ?î^>˜h45éuxþ|ý˜íعƒ­ÖŸx5õl§Èîîìwí»÷J€üºJ~]#‘ö’.ÜŠS!¿¶›Z,:?,Π˔_àKa´•â+@±K·o߉7éÊØ¬(v¶÷Ç;ôÝû³,Î5%HG±_«ç×bnÍ•ïÂ-ù€”RN} ”_È…i¯P?ÅW€5Ä. ÜúþöT)`ng5r™5ö­º.Þ7»«BÃçÛÙÙÞ†÷J­bŠòëzù5N8í5¾p«©üZó»èÍ+äʯr*@S”_èº\J¯¦½ÒvН+¨ã¶°·n}áY&nÛ3èõKñïW»ý©D(]ϯ¥.Üj,f°@L~…örW¸¤ü @Ê,š¥Wº@ñ`I±KßýýÖŒÀD3 JI`¢;Uˆñäñˤ¥ %‚QÁà矕€.å×9u*™Ö¹Œ«¤üZïk•_¨Cꩯò+)2(ÐÅW€ŠbnÞün…Kù³ Ãá°×8ò*ö󥑶аµµ7Þ~(|øð6‹óV‘â1Q _çäԩȺ~5þÂ-Jɯ²ëUæò•SéÕ´WºBñ ‚Ø¥7nŽO÷bß³uÞ-šöðáîø˜|øð.‹sY‘`šB²kµü%¥Z¸5C^“hsȯqÁj?×Xœ@L} +Lz…4_®»4ðí·7*[çU£4XSÜR–‘‡wÆ·”ýøQ (+¿Î[¸'IÊ©ò«ü @<£‚,uæÍ\˜öJ—(¾Ì»0ðÍß¾¹(4uñ¾µ€¼&^Õessg|Ô>~|ŸÅ9¯Dòë(¿æNbåWù  9L}1ý€:2&Îÿ`ïN£%9ë;AGê`ƒXÍ¢µTª’›ö­J{I%TÚ«Á ½´¯Iˆ–NÉHØ$@6þÖº§ÀÐÐsfpOÝÓò̸›6›Å>ØÛC#@ ý!¦âVeÞÌ[7##߈x#âyä´òæ™÷êlj_þCñ`…Ð¥·¿ís>cìà}èãø“ã¹%¸lÝzå¨ëûÈ£J@óùÕ·æÑ/nɯÌCù€.бôjÚ+}£ø °SèÂÀùç¿- ¾O·ÿ3˜R;»l …^à¸þ•ÊÚzÙ•£}õÈ£Ÿ‹â÷B‰º_ßvþÛK¶O§q+/¿–]tn=V~•_åW R~ D¦ŒÒ+}¤ø ô^èÂÀ[ßzþêÞÇÌOÇϹ½¬Üå…§•­"­¡ Pmå`yéu½žy\vé£ë~A‰ä×jòknQ5p±´Òž*ò«ü ˜¦¾f”_(›#x(¾½º4pÞyo­ö4­'hM–Bo¹éX«îBÛ]÷áŽAºôÜ/|ñóQüî(@»ókÉ8ÚÈ·B”fósXØüÚµ/n•]ü @Fù€6‹¹ôjÚ+}¥ø ôRèÂÀ¹çž› ¦/ÒÜ7 =Ak|óꪲÖ6uµ¦TÕë¹ô’ËGו@~-›_'ˬa³iè/n Ò•~…¶ü @L”_(šc¥ôJŸ)¾½º09çœsw\©ð{’@§mš.[Ͷ^zÉÖÑÛüÅ/>Åï˜Dž_#É•¹_Ü °_ÜŠ÷õȯýÛÔ×̰Ġ Àjyˆâ+СKgŸuö*£UsŽÒÇzjVèi±EÄ~jÖX׳šKv–2J ¿ææ×Ø¿¸i~ ”“¾qK~è§˯Ó_X™Û­¡Ï_Î ]Ø²å¬ ×óN)¤0Ö˜\ÞòOí- ´Iu%¢Û¬D0|Ü—¾¤Dýͯ³‚ßì/n…È©¹…Ø…ÓnqK~•_úKù€a.ŒÒ+(¾VÅiaÏܲeÆ#BÍšÞˆ­o‚VZ $1‡NœšuùE´iNWæâ‹·Ž>W_úÒ£Qün*@]ù5pNÍýâÖâëñÅ-äW€n‹uêkfXrP€è'¥WhÅW “B—Î|Ë™ ¿¯ð,²+V”Ó¨mÊj[…´æ½SA‘÷¢‹.›øù±Çâwµ‹E‚{ï½wêí·Þzkë™ö¼ÐÛV×zW.³Ž×Ðþü:=(†9 AÒá/n…ͯ]ûâV_ò«,@y1—_3¦¿ôK ¯À$ÅW SBÎØ|Æd‘4o²U€f@Þòêꬆž ÕSÀŽ•jz=!¶µŒE‚téÿûý/Dñ;Ü•"AV¾Ì+¥Ö±näתÂd˜rl[¿¸…üÚ/qT© å׌,@·µ©ôjÚ+,S|:¡ŠÓÂnÎJ¥ä¥Ý,Í-h ¬.òé²5mÞjEÞ‹.¼tt=†¬Aý²¢n•¥Ù*ŠÀM•‹Ú”_ó¿¸ö[\¾¸Õäzøâ–ü*¿tœé¯ÝÍym¢ô “v³ €¶ ]8íôÓ“Ó7oNÆÞN_]ü¨n±åU·žðÀm»ê¢ /I.¼`Ç¥+¿ã±PÔ´/ùµÊüDšL¦«-$ðz:GÖ–_º«-ï³R„Óß¡|úÑ¯Ø ä;¹ÚÍÄW µBLÜ´iÓB#£Òíÿ ¦Œ¹š¸½üÂÃNäÊ_øò­éöí´ý {äÓ][¶­ãåßÿƒ/ÖþûÞæÉYÙôÔ*˙Ӗ½èÄÖª¦¾†Ü ¯€üšŸ_'^=ù±ÊõLNw ±ž6åDäW€nËâßòÄ­ØVÓ_¦]}Ù™vÀ‚y®yØ•â+ÐJÁK§nûiì{Rá|¥±£÷!Ö“_T­ëd³tÓôRÈï¿xtý¾ü¥Ú~ï•úMÑ]óòkÉüØð·BÑÊ·lëjäW€¸µ­üšQ€h¶NïWz…|Н@«„. œrʩŇ¼N”ªÀ¶›ó’Ú ¾tÞ°D•M¾ü‡­úØ»¶Ýµôï;î¼£ôßåä×ü”×HNÇ4µÊ©Ìov!W~ˆS›Ê¯X€öd6 {_V]8ùä“w^Ë9”àà}^é ÈÁûjGÑ&ê]WÝt®÷¿ï¢Ñõ•%‚ai ¯n½õÖ]&–f?g·/bÚÔE—rûfm+€ü:o~ ›MkK’CpÞº_åW€vk[ù5£ wNks6ò)¾Ñ ]8ñ¤“xvµ3XC®§Xé ö’@»NŸÊ¤a‰à ƒÖïr_V"(;5‹þZ­„«T ô'¿6ýÅ­êòc7ê«ò«üJßÍÊ¥!¿X]ÕÆòkF ®\ÖöL ¬NñˆVèÂÀ 'œ vNˆJ“AÀ¬ù%±õXxåZgèÓÕŽSè‹i…qeËN[¯PS_SùµD~=q{~]8“M»áK¦¡OqP lCÍùu‘¿ Mä×¼üºxYv=Už¹ ‰õÍ»ÃÇ)ÀÀêÚZ~Í(À4—Áº’…Ùv³ €Ød«(½“N¹–ì(‚†”æ¬gñÅUºž<ƒNt¶5 ªJm7íàö"…ЦÊT›_?þøù5O©i’T„Éœõ„È©9Ë ¯Ã/°­9‘*óköÅ­¶gÏ>¬»nY&/“éË>ú¤íü³òE— 2— 1Q|¢ºðºqãÆ¥âÀP¶ÐL!6È*Óœ—º00ˆxi«¬g`=åÖ3X* ÌSzÍÊÙ)e‡§•%N‹w@_ç˯¡sj˜øØôÄ®§¶|TÓÒãÿ"Z¼(¿v+wÖ½üË“•`u]8ð¯ cõ%ûBžc1]ذaC2˜ãàjè³§Öu6Öl‚Ö´×™õBü ƒ¤Úé±CÙö§©ßúöøb鵉é®u¾jó^]©5ò+U/¸KªeK¯¦¾Àbº<ùuÓ`€®æš>æX _J…ž’uø‡'GyÄèç¼ÃiàñS•N¶*°žð ¯ÇÀt×Nlëúõõ•ú4õµ ™fÚ+ ¿Î—_£ÉŒMÄÇÀmÏ´¶Ó´3ìÖµÕµýoüÚUUOi ‘•å[hVŸK}žŽ´7³(½*½BHН@eBOÉÊJ‹K缽좼OªÛì‰ÅµtT§L­vEq¼žõë×.]êtÇwôæodÙR©B)@7ók#_ÜŠý bþâVµëÔŸ_# ¾òkøì)“±SØaZ©D!h: ·B•žcUY8ì°C“ÁÒy?“±ã¢9§c pîÓÉ%/ÿä°cÛ—·ž0ò¶;ôzƵéô©ý;Õë<ê. õ©ôÚGŠ€ü&ÎÎ{× _z9‹Ç뎋<¿Ö´yK¿V©üÚ'YÁv‘YÕ´W .ÃÁ-OäM3Ÿ95k=ç}Mó–—†=ОF¾¼f(ô±~Ý~K—ºõ½4┳&©´'¿–ΩnŸ‡ƒ„à$ðÂg.O~í϶ʯÍdϼç„ʤUgÛ˜²s•ûúL±€Xr©l ÕR|5œ–µÒ[Ï=?9ïÜó’sÏ9oîeVØ_MjêÉNbúÏ[ž W$LÊš_S“Tç)I8€ȯùù5­´û6?6ÿÅ-9ù•Å2°³@)Ðtª÷»e‘iY«9çìsG×ÿÝãÿ.È2'O“ú4­cë_rugƒ­`qÕíb1HÖí¿¦öµ* ¿6—C¯gryË»ÚØ»ü“ÄÚ?ëö7á•nPÊ€zd…ƒ[žxÂŽ ¶ü ÔÇÄW UÎÞrvrÖ™g%[¶_v|‚VuӧꚺÚ9Yê ¡e…W¥×x,rÊÙ¦'©®ÜNÓ^æÌ¯¥£iMù1 ¼ÆJé ±ÝϯJ¯MgϺòÞ¢Ûs.Ͷ+ïµÈÍP åäNè&_Æä&¶¨-gn]ÿã¯ýqîã²Ó´¦Œ¹š¸=€ð“º¦ç ²ÝE6vr,n`ƒD9¡ ¯íÕôT¨ì@¼ÉT€ü8¿š—ßê:õÀâëÉ]B«Æ»Êºò+²û|¹Yéª5,!˜þ @•y¨—â+Јƒ~]Ðå±ùŒÑõ¯ýû¯Í¿€œ"hˆãë¹EÕÀÅÒüžC•Vš3_¡Ba€&8€ȯåòkÓ_Ü ýE«ÐÛí‹[ý ¿Ê‘ÓÎ.P$_¶í,23Ô'+$(¿2_ÍÙÍ.ºfói›“Ó7žœ¶é´° ;¿lšs{éEYOˆ—óS€—°JÇ lù Ú ï Àë ¼ÆŠ×³ÿÚ5K—º) ̧Ìé\›8@¾Úvš Ps~Msr]àœZ(/¾¸‚w´+‡Q.É˯qfϼÇ(m.NŽ€zeådJh?_F¼óïªe=›6mšøù?ü¯ÿ!wT¥'€­páuMÐjÕÙe;§Ü„1…îp0 Ÿù5Ë®ÕdÓÀ“QÇ–Wi¦?3ÃÄK½Ö°Ó]«»¼ôlÔÑ®r=ò+UhC9·è4[ œaQÁXÊäH y&¾½rÊɧ$§œtrrò‰'­þÀ&ÎbZÝ@®àë©«[Û®A·^)YT©è„0îe×í—“Oœž_ÓÀ/ôòòžÖ¶Óò+åöxù5Ë®òk7rç<÷ÇÌ—Ý Š Ͳ#ÄÅÄW ·N:a¹<ð­o}kþäM]­p4jøé®9ëIÛzê×jçiž­k×îÛÈ+Q#;°¿ò`wÞä'¥RùuÑü(œ]^nmÑ©ꚺZWì|=òk_³çjË`þ}³ZÁÕäWh†é¯ÌÊŠ@|L|jw×¶»¢Û¦ã?>9~ãÆdㆣÛÂÒÍYÊØÑð4ÈZf¯'vƒA—×3¨|=M”LɪG'?9XȯÍä×Òù±ÂQ¯y95ôÔ×ÅÞ•i®·¿Wò«LZ6WæmK[ÎBmSÙb,P-S¼ ¡=_VØxÜÆdÃq’ Ç·|cî‘ôÀGØóúÖ“·¼ö–Èì·ß¾•è/ÅV€8óëq…òkÙ0ö‹[¯'_Ü*'Ë®ò+ÈÔ3%yˆŸâ+À*Ž;æØäØcŽIŽÙ~™O:Ç­åÖTHC'sßQn5•–muñד•^ëfJVµŠüŽý¹ø±ç×¾¸87òÅ­ÏVòkü¹­è´Ñ¦3ßÊílË´WÙÚE ÙOþƒöP|(èè£NŽ:ê¨ä¨#ªtüT˜^@ZáÂiÚ~ûí³t©“Â+9HвüZ2…†Ž¹_Ü ÜfMkkÇúâV‘×#¿¶[Ñ2¬Üîõ5½Ï€I ²ÅW vwÜyGòëgÿçÒ¥­Ž:òÈäÈ#ŽHŽ8üˆÉC÷ù?”zPV‘U†îÚ‰SÀj=ÓÚkÖÔ_È( ¿v ¿‘“_CäÇÀ8©ryå×>_iŠ/iUPŠí€x(¾Ú\"8âðÓ÷¢Ã=´ØÆš¥Ïúš{‡A¯Ó´«Ñ•êfJV3V;Hû|@~ma~­°µšÎ}GÉõ´4ìvý‹[òk{³ç¬i£Md¾Õ¶ÓtT nNƒ Ðþ,´ßsì ãåß|îo´ò5:V~ý¿ÿßÿgÎggGì«ßž÷’ëÉJËÂC,|¨Ù†µfÍÞµ¯SY€¢²‚¢ ¿ö ¿Ž…ÆÉÄ$œNÏ©I]_SjS~mǶʯÝ"ë¬nXš¸å‰'ì €È3Ð-Н@”ºP"8äMo]ÿ‹¿ø¯IºýŸAEÅ€‰[Óíë 8Ö)ôòšÑ¾BÚ}÷ndíJ”eÚ+ ¿v/¿É©Ñq ù•6åÍ•EÜiÅܺri¶n/S(ÁĕπnÚÍ.b×…ÓɾñoHÞø†7$oxýëw½sìܬiÎía䬇è4QpZظL;€ëAuûº›_ßð†×Oϯòèdì ›NÓ´ i·]…^ùµ{Ù3oÒ«ì·cß ÷‰¸À4ÃÓç*[Èa@uL|Z¥ “´^?Vøö_þe¡çäβ =A+©òԵ̲fß½Y¯Â@œê*4U^½^% @~­1¿Ž]HŒ“ãXSåWùµIm,uV™ •\€2L‚¨/oý¢ø ´VJ¿öµ?篾|ݨ ’®Ï©m¢4 0@(J§ý̯ßùÎ_ÍŸA‡S…Xù•þåΦ ¨yëÎn[$O[¦| Ý´²”¡ &Wý¤ø tBJ™×üö«G׿û½ï%ïCÇŸÏ•( 4cß}@~mg~}Ík~{âçù5pLm­î~qK~í¦"…ÒØË˜Mn_Ùò«)²Ðoаóg&€!ÅW s~ýì¯G‡˜ŸûÜßlíëxõ«^5ºþ×óý‰ûÒíÿ ¦ÔŸ]6ôÇuŠëjöÝgÏFÖ«4òkmùuâ;\Óóë|Ù4iø‹[ò«üJ bœúš™·üš·Ó^ ¿¦•:”a€¾ç!€<Н@w¥Iòì3¿ýøÜçµ·Dðʃ]ÿÁ~Xb_,X«<l¶Š´†.@µ•ƒå¥Wõzš( ( €üc~m×·úK~…e³Ê¯ÃÇä1å˜Çjå¥X kù`Н@çäõ—KiòÜç=·µ¯ï€Ö®ÿðGn¿MtBOÐê÷t¬¡}öVä×yÃi3_Ü ›_ÛúÅ-ùµ_V+tÆ>…´îí›5u¶l¹Õ´W`J#Ð^›üî,Lñè˜tâ_y÷?û̳y^K‹ë×í?ºþw÷ã]_màWã5ƒºª¬uM‘­òí³÷|>” ;ùõ™íùµíÙµH~kÏøâVè¯sõ…üJìfOÛúš(Fñè”Üó‘©·ß~Û‡'~^Ù+X*¤;îyÞîÏkåk_»vÍèúü÷«?¸Ó €¸¦Ë6Qh[aà›ßøŠ?^ȯ3òë.Ùuj»•_s¿¸ ¿úâ–ü*¿vSS…Ñå[¥W€ù(¾ð‘{~oÕûïþèŽBÁí·Ý>ýcŒŸyú™ÑM»·´D°f;£ëÿ?)µŒÐÓb‹¨«$PÇzöÞË”¬"”_å×6ç×@é4‰á‹[ò«üš™V挵”ÃÔ×á¾)» ¯å(¾­7«40îîÞ|(¯<0v°yxíé%‚L[Kûî½×èú?þäŸÆe9ñkŠQx@~•_‹äןlϯ1ë·äWùu¥ºÊ˜MNj­r™³J°Ê®‹S|z瞕傟~úéÑõÝwß½•¯}ü ö?ýÓ?/ï‚&¦cÕ4تÚ2Âä‹h¢4жÂ@Fé€>›§ô*¿&É^»ä×AsùµƒäWùµíb,‘*¶TOñhµ2ÅÌž{î•üô§ã§PM§\K¦ÞŸ]}ú©±ÁóÛY"ØcWŒ®ÿó¿ülæãÓ4Mµ´ â8Õë<ö2%«…Í_ýº?|ô’üA~Í©aók_Ü’_åWh3ÅW ßPÞQ"ر ÝŸÿüVî†W¼ü¥£ë?ûÙσOÐêÂ)`Ç ãëÙË”¬™LÈùµêüJqò«ü m§ø 0fÑÁÓO=5ºÞÖÁK_ú’ÑõŸÿüÇËOçÚk¯WÔ¾µ}?-¬iY°kj•_«É¯]üâÖž{ʯò+tƒâ+À̺@:ã¡ÓŸÿÔ°D°ýîç¿ %‚—¼äE£ëOþâWŸöT¯±i¢0éû”,¥(™oå׎™?k˯ò+tâ+ÐjþЇ“Üó‘rO®£›¹sOýjy’V[K/~Ñ &~þå/ŸjÙ+X¼kJV1J ¿¶+¿v÷‹[ò«ì ]¤ø ´Þíúpr÷åx(ùéO²ó§Y§‰5-+™õ¬]t¡Dyá —·ý­+ÁÎgÏ=_^û:^ë/ <µeKòüÇ÷G€Žçיϒ_£Q¾+¿ö#¿@_)¾­6< |ûmNîþèêå¬0Y* ¤¡·`¾»†~µT"ØñÀ¼à­}^4V"xòÝ)Áî±ÇËY¯)Y ´CV–h_~-måWùU~•_ Н@‹¥ÿÊÊ™=öÜ3÷Ë“²Æž˜4sbÓ•ëüÕ¯~5ºñ/lo‰àÅ//üªµ¯£‰Ò€)YJȯÁòkZÙÖ˯ò«ü*¿@£_ÖÊ;–ÿO?ýéÄ#ÒktšØ¢ëøÕ/µó¦7¾ð…/lå{ôâ- ~þä/\Ú ©£¢¼Ç/kd_™’¥0€üš›_ÓE×Po~mkv•_åWùÚAñh©´üýœ&6s¿üåòA÷¶ ^òâåí^. ’fæíN×Di@a@a€žå×tñšV¸y!òëxví^~‹ü*¿@)¾íVúý¬ÓÄ67íu–Ε`þ‹Æ·gW( чÒÀS[¶$Ïüq[)ôY¨7ÞÎñÅ/ùU~•_{“_ANúHñh¡´ü”«¦½_ÄìrÃ/~¹|ÐýE/|Q+ßÍ—¼dy»ç)„˜»Ç+^ÚÈk6%KaùµMùµø´WùU~•_«àËY0i7»hÂGîù½Å2ã4±ÕM{­h³”•†—¶ÊJ¿õ[ÙåÅ•¯«‰Ò@VPˆ¿ôjB}ɯiÃûT~•_«È®¾´Ýgâ+ИñòÀ‡?ô¯ ?¯ùiYuœ&vö´¬Õ±£<°ã/zá‹[ùù/üÿñd’zÿ^ñòf m£0ù”£A~íJ~-½ ùU~•_ANhâ+…â%‚tâ_¹÷'³ï¯dÂU°ÓĆÙìÿ?ùË'G }ñ‹Ú_"øÙÏ~^ໞHö/ÿ­F¶Ý„W…äWùU~-C~•_Çe…Æç?þ¸?¾(¾e¿ö–Ó^ÎÊÓÈÎ3Mk¨üÁ÷r“¬êö:çúÆüä/žÝøâ½¤•Ÿ³—¾ty»ÿ¿‚%‚&J¦dµ»ôªXÀjŸ @~•_å×ðùuò‹[ò«ü r*@>ÅW zãE‚Û?ôár IkØÐÚ¦e¥Aîò?ýô’––^6£Dðò—™’U„ÂÀtʯLûL„ͯ©ü:v*¿Ê¯ò«| (¾A…šš•çî{>2º~ûm+KŘç?&†iYs-!Èã2?ÿÅòA÷.”š¢0`Bòë|ùuVnëW~‡ü*¿Ê¯°Ì´€¾Q|ZëîÎ.,‰`ZVñE„™æZôáÓîþù“c%‚¿Ä­§…íniÀT-Æ? ò«ü*¿Ê¯ò)4Oñ®ê©YÓŒ—>tÛíS1kZÖÔ‡“Öü¤e·dÅÝy%‚¿xò¬O>ù?zûy7%«ûS²” Pzù5Îüºø$VùU~•_åSdU€6R|*ÑDy`èžÞ=ºþ¡[o/ø¬r“ªÂž&6]x+çyð¼e†%‚ÏZYXùs¦ëeS²úujXå‚þR$ù5ÞüZ. Ö›_ÓÖä×>aå×~åWdU€.S|*3<¨ØT sϽã%‚U³’èÓ+šy²ÙÀM‚ºZ&Pèoa@ùµŸï9 ¿6_+™¼Z[~-¶”òk—¿È%¿v3¿Ê¦ô™â+P¹ñƒŒÍ–î]¿m¢[n’UØiYs-!ðƒ[èõ7\»ôïxhÕg— ÚZ"pZX² úõ^ò«ü:ÿ:äWùU~•M‘Wª¦ø Ô*–ÁG'J·•_P€ôi¡¥A7¤Š^Á°@04«HÐ&¦d)½ŽS0èÇ{ Ð×üZ÷Ù ŠÞ/¿Ê¯ò«lм 0¤ø 4&žÁGG×o»¥X‰ è÷´æ×RÝibç%ãE‚•%‚•§’y‚–)Y ¯Ó 4+tó}ès~­;ÐV·]ý̯ ¯ý̯ʯò*@ß(¾Qˆ¦Dð±•%‚Gú=Mlô4±i„ïŒE…W…×"” ºõ^t>¿‚³¾Nµh~MåWùU~•K‘WJS|¢c‰ sÛ-·N> ¶ÓÄ[JuÓ^ë×d‘À”,¥y™þÚ÷ kùu—ìZ[6 ±¾úÏV ¿Ê¯r)2+@{(¾Q[y³Ù"Á½£ë·.,:í5 :íµœjŠ«Jv¥a‘ ê)Y ‹?­lО÷  ÊüKvms~ ’DÅYùU~m[Ö‘IåV€®R|Z%–"Á½ã%ØîZ"7í5-tÑiY1Æ—†f• B2%Ki ´•¨•âz?šÈ®òk¹üZG•_å×.g 9T~è"ÅW µ¢)Á~<¯D0kZÖl•×ð4±CY™ êò€Â€Â@]¸@~­‹ü*¿’—C•`ýP|:!ÆÁ-¼%÷q³æ\%eîoÑibËú—Ÿýsòò—¾"ȲœVaäWùU~•_ûÄY âO(FñèœXJûøÇF×o¹ù–ÊÖSÝibÓrO«XV*S"0%KiäWùU~•_Qº ½_N‹¦Dð‰•%‚ÙÓ°æ:@߃iYÓüËÏþitýå/ÝcÕÇ* ( €ü*¿Ê¯ò+Ð~Н@oÄX"øàÍ,¹”Y'›­¦IFúÞfÛõÏc%‚¡ÿþíï·ò³ª0ȯý˯mÍ®ò+P7ÅW —b)|ü]/„œ–UNêCÒ¥@~•_åW€|Н@ïÅX"¸ùæ›WyäìÓÌÎóèB¢ì¤ñnZ €ü*¿Ê¯³)¾Œ‰¥Dð‰O|bt=¯DPú€yZîéâk&‡Ò ¿Ê¯ò+@1Н9ZQ"˜q ¿ëÓ²ÚNaè~~ý€ü:¶QmO±ò+ÅW€b,d>ðE‚…›i¹§5¤­Å… ?ùõ¾å캲ÛÃüÚVò+ÅW€9­ 0ȯsæ×Žm•_åW ÅW€€â)Ü?º~Ó°D°ðib§ßÿà®_õ>+( òkÉü:“ü*¿}¤ø P‘XJ÷—nšcl‰‘Tã%‚Lø"AZvÓ¡4ȯò«ü –â+@ ¢)ÜŸW"˜5-«œ•E‚¾Päצók¹$+¿Ê¯@ü_jg‰àÆÉ;wé LÞð·÷ƒ÷¤ÉºµFµŸ›šª¥4ȯ1ä×dÕüºníÞXùh)ÅW€ÅS"øäèú°D0oqôGûý‰Ÿ×í_M6ÖÓÄ* òk{òëþîû»õàC£ëù%‚ê) ȯò+P5¥W Æ|¢ü ´â+‘–®)ü¼Ee) ȯò+Ðgʯ@Û(¾0!žÁãë×)¤¹?äR_Û’_eWh?Ó^ÂP| W,%‚‡V)”™–¥4 ¿Ê¯@”^€6äS_¶P| ¨K§e) ȯò+@»)¾0·K×\·úéd•äWùh‚i¯@›r‹©¯@(¾°XJê቟¯¹îê¥+ ôC^]™ßb̯Ãì*¿̦ø @0+ª7[$øt¥¯ €æɛ㉷ûéàË”_€2L}Ú@ñ€ÊÄR$ù:ˆC™|Y´+¿¡dÅÂR| m,Á* Ä'T–\­+¿ÄKñ€Úµ¡D 4—*scJ°ò+ÐН4*¶Â@|ểm+Áʯ@hÛ¶mKî¼óN;ˆ–â+V;x_õAõ&K íʦM¬ef”_ê§ø À’"êgM¿ ©ÎÒ@û²iÓÛÕd V~úLñ çÊ”ïB Va ;ù´Éí¬«+¿(¾ôVÈðm+Á* t;Ÿ6¹íU”`åW€eН=Te© ö¬Ò@²iÓ¯'D V~˜¤ø Ð#u— š*ÁÐÏ|úÁ›o]úw:vÛ'>qo¯oµ,Å)¾ô@ S´ê,ÁЯ|zóÍ·&ƒUîˤi’Üw_œ%XŠS|è¸Oë ?€|Ê>pK©ÇÞwßÇäa€R|è¨ ¯E¶Óù´ˆ›núàBÏ–`Ó4Mî¿ÿãQìY`6ÅW€ŽiKáµÈö;ð Ÿ®tã Hƒ°Û8^¢U‚  î½÷Þ]n»õÖ[m€¿_ ø Ð!m/½®özøOo¸þ¦åÒ4 Þ€Mv–`Ó¥$÷ò²0@d_: t¡àƒ7/3-»ýŸ¸7Š×èÀ?@¿òéu×ݘ ,¹fOOÓ©÷¬H½ËnºñæÑu%X ¦M§+ÃD;èöß‚¿ã¦buP|h¹Ð¥‚›o¾uæ}iš&÷Ý÷±(^³ÿÝͦ™ë®»aÅ-YIu×ìô[ùñ†›“aAö“Ü' ½2^dS`úü7ÐßBˆƒâ+@K….|à·”~¼,¡óé5×\WzÊkþ ×0n¼á£ëÙÙ¼/Š}.uÀ”¾ [¿×~§€6Q|h™Ð…‚¥Ó·.xÚØ¬»ã´±irÿýbß8èÐÎ|zõU×Î,¼fÙs0ˆgÜpýM£ëÂ5ýÛt×y\?–»•`€¶áV+Àïû]¤ø ±*¦h]ý–’5Ò¹žqÓ7®+Áȧ™Ë/¿2hµ¦id9³ãîü9xèºë†%Ø4ùÔ§>)­“)§•_M‰„îðû ´…â+@¤B— ®½ö†UËÁË«¸ñ†¬»£0ðÉî‹b;èP_6ÍlÝzå”PšL~í¨ë®[þ2š,Ð&yåWÀï3@_"ºTpÍ5×%1·n¼áKÿÎNaûÀƒJ°]ϧ[/»|¡çg¹1èp×ÁŽeV-o=×^›•`wÜñÐCÈÃ@+™ ~Ÿê¤ø ‰*¦h]}õµc?-Ïš¤ÏX­®¿iâç¼?Š÷Â@>]Ü¥—l]QXíÉx×9dgiXÚ3ÛwÍÃ+ÁqªkJäj똧”Wd[•ü¾?MïÓ¶n[Û•÷û܆òkÌï3íøûÛï#óQ|ˆ@èRÁUW^t V}²^ƒuýXöÁ†J°+ßþÙt~—\rÙª÷ç}1krºkˆ¢ìdÞŒÙ5×Ü0ÚÖ‡~PZoZÁhe±(d¡vže [¶è”·®Å©y×QdW¹_ݧu¼ß!¶¯îϵ¿#õ¼ÏEÖ3϶Œ¯?ÔïfèÒfÈ¿uþýmëï£Ò-Àâ_ºTpÅ•WïZ˜è´wª–,@ûòéE_²=}NæÏ4M“AÀoi…^^3V/ä^sÍõ£»þ´,ÐM¡ÊH‹,§l«®)¸ÓÖû{TÇäÐ&Þóº?×UŽÚ0õ5æ÷9Äk 9É:ôòºò)½t‹â+@ª˜¢uùW•òä(­ÀÂOպFËüÔ§>ÅûèÀ? Ÿ.»è‹K|ßjlÒkÒÖ¯kUïš«¯]øÓŸ’…€Nˆ¡tµr9ÊGáömUû3ô„àÐÛhÒk?Þçy5U”/³]úû«ô Ð=Н5 ]*غõŠå WcÖ*ËÙ*ÒÎ[¤2{Ýu7Ž^úC)Á4O/¼à¢å‚„Ò¼B¬zìÕW-—`?ý%X æ=½v^i¨l™hµå-ZB Uàjº°Ufß®|Nè2Û¬}Rt»ªÚÆPŸëºÄ:õ5ö÷™xÿþÆøû¨ô –â+@MB .»të§t+ L w ]?Ýu×^{ãhý=ô@ïµÿ@_òéûßa©dÙúj³98sõU׎®ú3ÉÂ@+¬,)þL{Ì"e¢Õ¦/ÎS¾ª{Šc]%©¼×Td¿®|~“}ËlW¬ŸëºÅ6‰´É÷yåòç-NöùïxÛðû¨ô žâ+@ B— .½dëÄÏ“eÖÅ—ê:„zŠìµ×Þ0ZÞÃ+ÁT•Mß÷¾÷ïH¡¬Ë3ÝuW]yíØ~K’Ï|öá(>kò0°š²ÅŸ²ÅÌiqâe“BíÛ:Ê”ó¾GuM8íÂg'¦ßXßç:÷sÑß§ÐËëÂßß&Þó²S‹˜Mñ B¡K_ri²Ûö ët? x%÷šk®]øá£ø<8èÏóÜNˆÄS[¶Ø €|ZÒïþîûrbhº=­íDCa+üËåÍØ]uå5£WòÙ†J°ò0tG%Æ.-nÅ4³ì{²üròfÕ¥Ü6~®c™úÚ¦÷9†mªby]£ô Ð=»ÙáetC– .ºè’äâ‹/]ºž>øž6p,0hn=Y öš«w\bøŒ„.Ÿ·¬ì:¼à}hs>}ï{Þ›¼÷½¿[I(­2žÖ–C#^ú•W^3ºÈÃ@(M•wBL,ò¼¢…²ºöC뉭(Xç¾ìËkïÓçÁûÜý¿¿mûQzXœ‰¯…>p{Ñ…{àØ´«ÉA¯N ›çš«¯]øÓŸŠâ3còU÷(S¶÷ý2 M§{Ï{Þ;CóÒfº¼Éᮡ3n;§»fûc‘/²]qÅrùõsŸ3 ˜­ Ó^]^LÓ!»0í5ä~ 94æ}ö7ùÙkÓûL;þþÖýùQz¨‡‰¯„žVtÁ&^pÑèç“^›˜îš48ÝuW_uÝÎ˵Q|ŽL¾j?D»ñ´=Ÿ†ôîw¿'ùßyo¹š“eCçÓ´þZÛÙª_ÐW\½t¹üò«åa`ªÐÓýºªêÉ’öw=ïI¥Ÿcïs¹}<ï{zy,¶Ï³Ï ¿7á(¾,¨Šƒ²¼ÿÂâÜHkkÇÆ;…6+¿f—«®T‚¥…Én½—ÞO ïùô]ïzwòîwÿÎΰXÓ‹H½MÉŠ¼Yùux‘‡€¬ÄSU9Jˆ˜?÷}þ\wí´óU¼Ï±üý.'ôòº¢Î×3«ô @Xϱ Ê }ö}ï»`t’Õ€g…­Í¢§Zc=“§¹].¿¦Ég>ëô¯¬NA²ÛïíS[¶Ø@¯²iæï|W‰šnOTƒÂ·—Ž»iÓVƒæÐɼÙd®.âò˯íïGùŒ< Еé£Už}Úé¶³ŸYþ¼Ïïâ©æ9y•ï7qÿ}ò>÷ë}ªâïoÓÿmõ¨†â+@ ¡KïýÝ÷+.³6Qh“•…ƒ«®¼fô6d>ÛPvåçÏÿ8(½öç=V€úOßñŽwæÇÐÐ_° ¥“‹nà›\½Î»³ ¹[·^5zoyä³Q|ÞeahŽ"Ïêû¦Šâª}N Ÿã6”¡NJ¯ÍP|˜CS´ÞûÞß °”åR@^Y |ÉuöñxT³­Wî,Âf>kl¯)½öïýV~ºšOßþ¶wÌ•s‹ªWŸôÚȶjŠ»užÍ ¤­[¯-ú‘G•` O”xˆE“SqûôûÞô~îòû<ÏtÓ"SVC/Å>Ÿö'@µ_ ]*xÏï¼wÊ«ée ÆjM]^š¦É õ#`›)ä^qÅr ösŸS‚í¥×þ¾ïʯ@—²iæm翽’ä™IöYë˲múX=[½õ²+Gÿ;ã‘G?' @)ï„QtR¦B']ù,ƒÿnPÅW€B— Þýî÷+äÜwZØ&]qÅÕ;ߟ$ùüç?Åg×ÿj(½zÿ•_.äÓ·žþÜYó¾£$…6e«ºZ×zâ(än½ìŠÑk|ô J°#eœúösÈ«÷®|–BP|ÈQÅ­w½ëÝ£ë“eåiWÕÎv]þI5¶—_~õhßþóŸ‰âóìÀJ¯´=ŸžwÞyùWOc >Co^×ÔUäÕµ½ìÒ+Fxô Ÿ—… P0¤MŸU…lðûÐÅW€)B— ÞùÎw-~d¾Ò¦ªl.¿üªQá‘G”`ÛLé•ñÏ‚©¯@óéyçžW2)V÷5­j×3.ŽÉ¨ÝÛÖÕ]zÉå?ዟoüwIª6mRf™2”ò”ýãgÙû\ß߉iû>o¿„^å7”_ª¥ø 0&t¡àoÇRáµØAüÅîç.¡U½ÖîÜ_ÍÖ­WÞœGùlŸyþ¡åW MùôœsÎÝ™¶ÒíÿÌŠc ™<µÁâwbºëôå¥Û40¶U™ýÒK¶Ž–ðÅ/>ÅïW_ò°/qÕKNºL±”_𢸠„/dÞþ¶w”òDo`¬”DNù ÒóÅö£ÌZÖÖ­WŽvÏ#*ÁÆNQ€¼Ï…Rs>=묳 Cs³gଘ_Tíó™ú›™/¹dëèzS%Ø®æaÙ5Î÷@n ¯ÕÔDM("¯Ü§ØʯMØÍ.ú.t©àüóß–¼ímoßõŽtù vˆÃÙi:ïa¨U­—]¹ýrÅÒ¥éß‹áhw>ÝrÖYIÈ šV˜qsדT·ârñÅ[Gy¸œ¬h9¼à= y‹–”¥ Ÿ†%ÊP…øÐËc¾¿×ö;@xН@o…>ˆùÖ·žŸ¼õü·ÍÿÄ&Ê•¶/-éuyçYÏe—^1ºÄð»¢kb>@»òé™gnI¶œ*˜æ‡ÈС´M §aË%ØËäá‚YDig†ô¾”SåÄÔiËéSµª}»È2LÈ÷³ò½èëû<ïß—Y½<ÃìK³ÂzŽ]ôQè–ç÷ÖÉ“²†>CëØòrO?bá9·ö÷¤«MÙuï(¿¦K¥å/|ñóQüîtåô¯Ðµlú–·œ¹3Ç-J'—·¼Ðë ºèF3á\tÑrùõ±Ç•‡Ç(MvÃð}|ªàh¿•§¾Vˆè†ìo{Þßô•û(ÏÄW WBOé9÷Üs“óÎ;¯Ü“ÏâÚ¢cïuM]M™în¥—^rùèÃïS_&Á*PÕOC:㌷ÌC+œîšÖ5:¶ÒÓ |H#”•`/ºèÒä¢ /&7•‰eÔnþïï+@÷”-9)Gͦ0Ïç¹Ê÷ÂûL—þîû<„¡ø ôBè‘gŸ}NrÎ9çîø¡Âæ@ZSk5mí`*E„K/Ùºt¹dû%†ß±¾”`!’ÐT>=}óædóæ3ªÉžca1DlÌÿ–i©ÝTmfÏʯÃK ¿×uf¹C®`ºE E IÕì›ûU8U¾ÞçêþUý÷ÍßßâŸeÿ­Xœâ+Ðy¡>ž}ÖÙó?)tY ¦RB•ƒ.¯gÐÈë¹dgö’†‹°1L¾€˜³ièÿ>ž~úéÅ2aZÝ)ªíš} ùy*l†«6†z’«ç“•_/¼à’¥K—~ÇWRˆìï5Àl¡'Oæ=/DoÚ2†ë›¶Þ¦Ëm**õã½èúû\ôw®©Çµùïo¬ï«¿]‹Q|:+ôÇ-[ÎJÎ:묰™N½ºÚ ¯È ­þ¸øâ­£K ¿‹m.Á:à @¨ÿ&†´é´MÉi§2’VÚ`M+®ÇN»ÕùêÏ`›*ÁV•}åÒþñž›2E­å®Ðe»ª—ÕeML}mê}^­¸‹ü®ø”§ø tN»3·l™ñˆtŽ[Ë+2é5ÈzB·c뚪UéqöAE[]ÏëÙQ€½,¹è¢Ë¢øý4 –.SNªÎ§§žº)Ù´iÓ3´ãóWõûÜ÷ÏKÛÿþư¯|ŽÊ{Ž]tIðÂë[ÎÜq46;è^ÑQÙ‰E‡^O…Û';Hnªl­{<™·2^~}ì±G£ø}ÝüÕ¯{+M 8õ”SŒ‡éöô0¨<ˆN¬§BYî´r¬ëüŽ0ÆË¯ðå/Í|ü]ÛîJî¸óŽÒBå\ÅG²ÏÀS3¿” Ð_Y¡(¯8”Ý^¶pºtµÚvV¹Þ*¶yxÛ<åÄ¢¯ãËŸµ•ÍêûLwù}nÛït_ÿþ¶}_°+ÅW B— ÎØ|Fîôü~@è²@ÎÒ—YÛZ¨¶`»\D¨­È[S÷aåëÙQ‚M—þï±ßÿB¿ÃJ°È§»:ùä“s‹¤“‘4p¸[^ˆš[ˆ ¼žöæÐ¦×S]( ÿz¦oë<ëyÿû.]ÿò>6q_Vx¿Cù”_V7«PTfy„Û·U”ˆg­_±µŸ½XÞç:J¾³Ö1ïïQèåùû[ý¾ò߀âv³ €6«â”é›7o]r\vì¨ëäòª;çl}³£ÚÔ˜ø…™á¢ /]ºö{ mýïØ‰'˜œtòIA³bà號qÓ°+rVª”•`‡—ñÒk L{€âB–¸·OªÜŸÞ«îüÞµá}öyó÷·êר°Pœ‰¯@k….ÆvúéÉ`ÕéXÓgO…?ëØz‚Œ»Z^Èä°ØgiõEœ§¹½èÂKFå’ßÿƒ/6ö;n:}ͦ™Nêm9ï]þó'oŸv[²êãf??L>bömcK[àù³·gÆú§-sÊB›(ÁÆP~U8`^U–ú’ÃdCúœOO8þ„åì7Ê”ƒ)5™š1³ŸwæÇdEö[™—Ö³ÛèçaÖeÔYqù1y9x,ã&csÅëŒ-´þáz’ñ<–¥WäðÁØë[~LR0Ç˦U䨩ÙtŽe&ónn¶ –cÓœV“c§i‹æØéÏ~V+¼®T¦üºHž•C©;oÊšÝÉ…;ì0o@$¶mÛf'4¬HKÁ(ü~mzŸ-Þyï»ÿûí}ö÷—òî¼óN;"õGÿúþÞï_Ö¨bŠÖ¦S7M½=o’V˜¡ZËS­&×vÚÕĤ׉í6é•"¦Ïºàý®ÿÁ—¿TÛï¾É¯ô!Ÿnܸ1Ùµ²pj’7-¶Âõ$•½œàñ¹®)¥µMwméÔÕº÷Û<¥×ÌûßwÑèú—ÿð±J_³Ò+„¡TÕÏýê}÷9Äû@·)¾­ºTpÊ)§,~ Ôœ÷!Žç×VHbû%l+aX‚Í&eÍ:èŸ6Söô°Ê¯t=Ÿnذ¡TŠ«4Ïå}a+xnÌ{=íÉE¶µ}Ì ¯ÓJ¯C&ÀÐÆ|Ò1Ç2…&¹©6M®g,+¦Õ†ÒœíV¥\f>à€ýƒ•^eZbb20ÐW&¾Ñ](8þøãKM|šžµ|NÕ¼ÛË*´žÒ Ï[^ØI¯Ù*¦÷ÂNªªvîÕòÒó_Oà5Ö´ž&¬Vx—M…ýò>æ½Ê§GuÔêù4'ªMFϰÓ] ÜbÑÁO8PeÆ%FåþAVxm#eFX‰¯@B— 6nÜ8ñs]%ÃÐCdóžXxZ`cÍÔjÒâåºÎô›xfMy]){lV~Í.c6 O<òÈjƒVèÀ›;-¶º„Øì©€‹:J¯¦¾@3L|ºP°aÆd0ÇÁæÉ©«IýÇ©CO¾J«+;V;uul=uMC­éuyºk&ô)c kùôˆ#Ÿ3Ÿ¦Ës*€© }Ç–8q5ëõÇÚi“õë×Ú ôJ6!ø©-[ì W_F„.{ܱ£ÓÆæ—ž6|)!o•¡Ë9Ië›V]•ܸ·µ‰bò¢¥×lêë·þãŸùƒ@'óéá‡6 W!¢b#·Â°<{VfÛ”7ûÙ•^ _Ú/½{ÌüOÊé„靿-úèÑ”×%¡ Ÿ¹Ë[ü {Þ®JK c?µwºk›T8ݵÁbìóÖ¥{ï½wÕûo½õV; ’QïÚv×Ò¿§•Þ=äà!«®É¨¡‡Èæ-<ÄÙ &_B…§^ ±\4ï®_×Ì„×*ÏZ¶švçwÚ @Ô_Ê….¼fŽ>ú¨…—þy¥  eBkOÓÉbñ¢j:Clµ¥Ð±©Zõ¼œh¦jpÀþ•-;+uebV^ù2té²ìz¦=¯ŽBh•ëUxö8%X Ô¿†þíý›¹òYˆœUiQµèü;lc›×¹ÜÖ‰\½¸õëö«}U^€â_J….½yä‘£°ƒêF£Îº9Ä¢ƒ¯'w§D?«®*B—؇9‹¨²ðÚEY¡²h³Šu÷É"ûyø\X TV}ë¹ç®ÿÑÿòG 䯱/8Õ5uµÂ¬ä [¶¯½g3hS®ðls.YáØÍ.ª•B–^?âðäˆ#˜ù¸tüi:yÏÂ*\t¡õT(ô$§ºŠÕ®gPû뉭 ¼~}}¥×ñ©yÔ«ê¢n˵̦JÊ@{ùïչ眛œsö9ÉÙg0g͈aâ\ZKXL;ð¥§näÝú׳ˆuûï§ô @+9M0@x&¾Á…žòzøá‡ýTÝè«0Kλ<†*Ìt×¼…D?Þµa¦Ë.í…§­]¿~míÛtÇwtzŸgKÓEÃìGï Ð&go9{âç¯|õñéÙ3ôÈÒÜå-ž'·;I‚ŽÍÍÌc™½µÓ]eæ"94Ϻý×Ô¾^è+_ÜÚ@ñ*déõ°ÃMEj?žWˆ­ðœ³…n/»ä*Ëʤ]ÑDá5ÓµÒkV¢¬r’è´e/Zܬªür?ÌZÖ¬í7݈Á™oÙ2ºþÇ_ûãà 2‰f‰Ó±Ù¤×A _ÒJ·ÞAÈÀ[SÜ-[ ÷E´)½7QxÍ(½@Ü_`æ-½O»²ôvÈ!‡+fæ\}Ð}by¡îçÕ ´åÓ–¼,Ј6Òo~[ׯ«¿ôªP@E »«’M}f9øà×%ï|Ç»‚.óŒÍgŒ®ýß-tœ ’k;—@Þ7³¢?™/€­ºw yó÷¡)¯tI65mÛ¶mv@ Н@‹Lz`3ÿöþÍÌÇW8Ü5Éô¼ošwײ‹Ë+ªFß aë×í×Èz• ˜G^auÞ²jÕÓxþä×ÐN?móèú7¾ùðAtúÖЋÎ_O…Bÿoƒj§®Öµž±é®5½žàý×*¼@S²/ì´ÁnvЄñ²ë¸·ž{~rÞ¹ç%çžsÞÜËLs»9ÀÁØ"ë)¿ð´ºEçü”&U¾ â³£’^›(½f¥‚> ¦2C.§-#Ô¤ÒЅЪ ¦e_·É®ÀÐæ¯~½ðcÏ;çüÚ¶kÓ©›’SO=59å”S §»0 nv€N+\Z,ÃuYVxUz Ë”HÂQ|VÕ´¬sÎ>79û¬s¶_ÎÞyKµUÐå›Ó°KNg®1ÈfÏùzlÝþ ¯´Kè²jUÅd€ÐN9ù”䔓NIN>ñäRa±Ú/8¥•%ÑÜ/l¹ÎŒ‹¦ ¯ò),óE Mžcmpö–³G×ÿÊã“wæœG5ÝþÏ èÁìÐ'lÍ[^…çˆ }nÛJ<_*KÖí¿¦‘õ*´GVþ Q2­¢D:¾]Jª@Ý~ó¹¿Ñø6œtÂI£ëÿû·¾0Žeæ‰ KWš=§//M·o÷ í…Ö6åàŶuíÚ}eÓ€9nV†jr=Už¹ Æõƶ @<²2ɶmÛì ÊœÐ&&¾µ»kÛ] =Ë™[’3ßrfò–3Þ’œ±ùŒbOš˜ºvkZd=IˆõL_^ðÉWi•ÙMÕjJ¥×¾OÑ =UÔs¯`è„ãOŽßx|²qÃÆ©A4Í «a³bdqÕMŽ%^YáUéµ;ùH6›/Ã(•,Nñ¨ÕÁ¿.ø27o>#Ù|úöËi›\R^Y ðA÷´áõV×@­A‹—Þä¶f…׺K¯NÛn‹œwp ^Yùuãq’ Çn˜#í&Ñbó75y;qkªð*Ÿv/wÆžKåb`5ʯ€l°ÅW SNßtzrÚ¦Ó’M›6ír_|ºk:û1!ÖzáãeJÛíœîZ[‘·âõì¿vÍÒ¥n tÍ¢g+¨ÓqÇ›»t9&PÞ­,†.òZj¿ýö]ºÈ§õR¾ˆ—‚ “”§ø ÔêïxWmëÚtê¦äÔSNMN9ù”UWíä«ê^_š³ðàƒcÇ®×Ue ü®,²Ç›*¼*½îjÚi_Ë”¦='Ô)e«>5­Sß4㘣NŽ>ê¨ä¨í—úBn¥á·žõ4˜áºj¿ýöYºÈ§ÍdϪȤUsM{ÚBÑEÊQ|z!+¿žrÒÉÉÉ'ž´øÂ&&_…#[!ã³vÕÈt×êVÚÄ”W…×n*{ ¾-ø«,ÝsÇw$¿~ö.]Úê¨#JŽ<âÈäÈÃȇfܺ¾°~ ÇŽQxE–˜MáAæ÷»è›“NX.¿~ë[ß ·àì8û`ên_Ú ðí¥×“Ö?m5[ßâ…AÒçfïÚµû6²^…ÚHXÄxùõ7Ÿû­| G~øèúþç¥Ãâdô "óÖem:©Æ™ƒ×¬Ù§‘õʧȚ@[eÅ“mÛ¶Ù@-¹  L|js×¶»¢Û¦ã?>9~ãÆdㆣÛ&'_>ˆ<¶¼JÏÖš·ž—C›î:\O¥WS´æ3m’è<Äë˜Nºè6Öµ1Nmó$ØÃ=49ôC“CÞ|ÈöË›ÃÄйï(·ô´Ò0ÝÎé®ÍäÝ]­Y³·|ÚâìÙDÖ ]U>ÚJ 7Š3ñ`§Ç-—_ÿÿó?ÍxtÞì©ÅgRM.aì§À㮚˜îZ×@ªj§wÅ1Uk¿ýLy…yå• tEualæÍozÓèú_üÅ­¬ÿ™{6ƒÐƒcÇâs] ®®)²1jªð 1gÍ,g*âó–QLªÈ]¢ø 0ÅqÇ;ºþ§ú§ //ô)ZóÊ¡Ûéöå jiÇÆyŠÖX¶ze¢‰Ò«RA÷M;(Ÿý\¤û´We N])Á¾ño]ÿoßþv=!·®0Ý|¡lúJ×ì»w#»@>í–¢¹3ÖÜçì@h °@ÈLÐEН3}ôÑ£ëöŸÿsUX'—zÐkeº¦]õaºëÐ~ûíSû: Â([*u}q³Êö'P¥®”`_ðÁ£ëÿý/ÿrΚ3Ý5ï‹\eãng3“CÛù°¡5ûî%Ÿv({ʤÕäM€E¬,«(Â󿀮R|jsÇw$þпZºÞÖÿGyäèúÿõ_þËòy­ÕqUž»5Äâ( ´Im§­Ò}X³fŸF^³ReÅP„(R@PØêÔ•ìë^ûÚÑõï|ç»|l9”ôZo-2Ýu”O^©(¿-’Ïb* Ê™@UY ý6mÚTú¹ßøÆ7ì@€_FtáÀÿ‡>ºþçþçs=wò ~u³c«]ϸ6Mªj×T­&J¯JýUf:XŒ®LyÚ”…Ûœ‡_óšW®÷{ßËÏžOaÐȶÚ=˜5Ø‹ØwŸ½Ùrù”X™† ÐŒÝì iÙÿᥭ=ôÐäCIyó!;nrPï¹­|Y°\~ýáþ6©"|ŽÅg¦ÈöMÙï´í³·Â+%sÔÇÄW º0 6óúƒN~Ýë’ƒ_ûÚÅTéä«Ô®fköÝkéR7¥‚ú-rÊÙ¦¤¯ÜÎX¦½*½mW4y=ó̳£K[­_·²~ÿý“uû¯¹7ÒÚòi›³õmë>{ïQ{é5˦òi<Ù³®¼·è66‘Kc:ë@_™ø ´N&Áf^7V~ýÎ_}7øòsiµj¼ë ér·‰²kF¡ ~MŸ:5;pûé[^nH“»ïùÈÔ{n¿íù9h©üºó®çíÞÎI°û¯ÝotýÇ?þûâ{lbº«ó„–^åÓÙ¾ù¯ø°Èér'@ƒ_VëJ ö5¿ýêÑõï~ï{ÉÄAü‰«éö«!îç”*=_l·Ë¬Eí» ¯tKü•^.øÈ=¿·êýwtG!ööÛn_%9¥É3O?3Ju»ïþ¼Vî‹5kö]ÿûøÇÅîö,;´½[fÞ{¯úK¯ ¯ ³Û´³ ÉtuQcÿb@Ÿìf]ñëg<;¼<óëÖ¾ŽW¿êUÉ«^ùÊä•Tø9i:ï4eß}öTzí¹2§sm¢È¹Úv6yеugÛ¬ô ´Á¬Ò븻?z÷”Œ7ý±O?ýÌèÒÞ¬´w²ÏÞ{%ûìµçÄ M‹ìJÉ ¯u—^³lªôoöÌ{LŸsVÞ>“=šaâ+Ð=;ƒ—_Ÿû¼ßlåKyåAŽ®ÿà?\x· ¦üb¸köüéÛ°“ªª{µ¼ôü׳˜¬ôZ7…×ö1Iª¥ -æ)½í¹ç^ÉOú“é¡7'=ýôÓ£ë»ï¾{+÷ÕÞ{-g§ŸþÓ?Ï•q)²÷hd½}+¼nþê×}Ø:š åO€æ˜ø tÆj=Ŭûì3Ï.]Úê€Ö'¬_Ÿ¬_·nì5§µìE3µ³ÏÞ{Ö^zmã-âStBX]ýMÚúêú®](˜=ýÔÓ£K[í¹Ç+’=^ñòí——%¯xùËZ°Åqp÷j ôjÊ+!rç<÷W™AåO€f)¾‘Nü+÷þížyæÙÑ¥­Ö¯Û?Y·ÿÚdÝÚµÓ_m:s7”V×!üAm+ªnÑûì½ÇRéµn ¯ñ›çÀ¾ƒêö@&›ú:-ÔÍŒw+°\‚}ªÕûãå/û­äe/Í./)„#Ž5É ¯M•^Û$+¼†(½Æ2íµL©Tæ*·Ÿ¨Çsì  >rÏG¦Þ~ûmžøyåað¥òkºãžçíþ¼V¾öµk׌®ÿøÇ¿ðòþöî%Æ®û>ð|IÈÆz–%RînÄŠÕݯ ÁÐ2``ÀÙq‚Àk/Úõ2EQ³jS­)9 c–†a'c &<ÀÙ   Ȧ½i‡±l §#Û-“z8»Ûý'uªN]Þ{ÏãþÏ9ÿÇçP*²ªîãNÝú“÷[¿Z»²|Nÿ®ƒ”fÓ†èun¥OЪáebs|b]x ¢U¯÷·ã×OÝsO¶÷öVüzãÆÿ?üÐ"×[ó„ל÷®KíIþrýºÃï—z?x.á+µo½ôŸv¾ÿòË·ƒØK/mþ€Vð/¿û—£?úT¦ì¿ù7ÿúèíÿúÿÿ·l Öp3W«2®ç³&hõâecó±)@Hé¶TeÂ}Lˆ`?ùy°ƒ{îÍ7‚=}úþ£·?ø°{ªíöýaܪšöG´Ž/}Óý9{vþèµöýi ?°UóÞ€åÝm €\uE¯m—_¾|pæÄËöÝùR±¿ûÝ¿ýÊÕ¿þWŸ=øWŸ}øà³Ÿ]{’½uWΣXBð*zíëecKÐçIóÔŸXŸëöy‰Y v×®¾uðÞ{ÿ¼uïºmo»ù:?ëàã>>ñ+W÷ßwÏÁ}ŸüªQ^E¯óïOS^ûNMMqOºi ìÔ·qŽë`<_jœꉓÑ@Ïèów¿ûÝÑÛŸúÔ§²¼ïí óW¿úõ Ï]­VwÅœ»uÜUä©Z“NwmMÕ:/+xíC𚯧¾ €<­.½ðâÁå—¾Õû3ÂþuÈÞuÏ›·Q;~Íul;~í3 6Ý}h¿íóÙ³ڟμ7-eÊëÒ{¾öÛ®?æíÚvYö¸ý_, ™öºÛ°‰Y¿û¸ÁÞ“g{æÌñÔ§_ÿæ}'Ó–^ƒ£/˺)‚¡0—K_<8söì­ÌÚ¥‰^ãM{íÚwÜG·"ØÛŸqï½÷f¹þ÷ÏÁöïÀΜ¼Îµ?µWMGÌ=œWȃð¨S„ç•oG°·/èS÷ä9ùê¡8ñû÷ß¿qu¸ëLÓ®f›îÚºS^»™ðÚm×D«Ô#̹nŸø(u ú«÷Þ; `·úJ³l‚[—ñч½}ï}ùG°7?ø(ÛýîÁ«ýi~Á립gøý®}Ý{Ò]·ÓÞ€@ø p°ð»'EåÁ´Èõš¢%zMQêÓhJÞ¡ÆÞ»®&¸Ž?<þª\#ØÓ§Úì‡Ûݸûà¾SdÏ<$xµ?-{¿¹iê«})P«¿þë¿¶-ÂW K/¾ðâÁ·^úÖ¸Ož£Íüä:>þè8Í9‚m¿쇽"Ø”ì",½ ^ë 6=©Ÿêø¦¾Œß®Æêl{×}n¶)#‚=¾Ý7n|°øíY"xµ?-gº-(Ý´ÿ¬1*â> vâ¾Ùºô‹—į׮¾uðÞ{ÿüÉïú¿”ìØ`ׇ–ÁÞ׊`?È.‚æìÙg¿NS´êœ¢5×ßK=Á¾ÏõŠ€¢¬v¿cÕãÇí]ç˜öÚ탣Ñûï»?ËCxúôñíŽÁöîÚ0åÕþt*9þp“}"@½„¯@–šç†/]|ñàòË»ã×¼·¢×Uì[0ì]nE°Çxï½÷fyÚ“`o~PN{æÌƒ‹\¯)Z^66 Fßzé?Ýúÿ‹/ü±wŽ}â¼×±êuA'ß_ZûÛßÞœôºzðûSûÓ*÷^m€M„¯@†V'þâ×àÌÙ³?úxÊëÚçÌÓl½öc}øÑÑÛ÷Þ—g{êþvûQÄK¾kÖ#µDôjŠ–¨€<4lÐ?‚]ul<÷öڱќ`ïÛíöö5Ýß©,ÏOúäí~ÿýx!ìÑ«àµì½iŸ 4õvòÃXu¾ÙÙöäû¯Þ{ïÄG¬b\Ã/"ØÕ'xß}÷eyŒNÝïÞ¸ùa·ùÌ™Ï,r½¢Á+yêÁN?íu5êý±ö®}oCßë»ùáÍ£‹;uÿ©lÏ8¾íï¿cÔe^íO1õ€Í„¯@fVãߟÀKÅ®^ćG£¹F°§OßîãvÞ)®»œyHðÚ‡ ¶ÛÁö›öcœÂÞuß²~Y7?¸yôþS÷ŸÎöüxàãÛþß{D°~æÓö§ö§ÀÂW O£Ÿ¤ïz©Øe§½îR\{ãƒQ—3™]"z5EKT@:Â÷¤ÿïÿߢ^æÉöÅ=·¶~è+±½ëèëÛòÁ7?8FsŽ`?ÓÁ.½ÚŸÖ¹?Ý5I5¼/õÛ@Ý„¯@FV3¼Tì^7/ÒEt|xÞßýYÍÓ§o÷Øv¬3=°È}6EKð @]¾õÒ·ŽÞ¾tñÅþŸ˜Õ+¬ö¼3ÃÞ~w£Áž.$‚µ7µ?e»]‘.u¾³j&`| Ø:^*vºi¯û™ª_(-‚ýíoo½}×]ÿsÝ"/ÜÑ«àUP@º¦˜úºÉå—×#Øeö®«DÃØÛuãæÉ©©§OvRÛŸÚŸÌ´Wá+°ˆ“/Û/‚]~Úë/Û=ñj×GÜŽ`oÄý÷ÊòÜøô§ow;‚íç®­køÐƒ‚×>DÔh®øµÑŽ`_¸xiÖ½ëè˘`ï:ôâú\V;„mG°§N}úèí›7[í¹njÚ¶i’jªa©©¯´ _Åõ‹`W'þ·õýÝïŸdÊU´—ŠsCÂo~xóèBOÝŸ¼ÿþÁ—ñЃŸ^ä¶›¢%( /sǯ—^¾|ôöq[ßÞuŠz£¸¶Ã×öÛbXûSûÓMæ ]— jSyM©ˆOø ì-f$0flÛø'àÇM³šwÚëÁè‰Y7?¸yô‡§îÏ÷å_xàø¶ÿ÷ìÑ«)Z¢òÞ×K°Á‰öùªÝ».a=†-)„¼Ú›–B@ @Cø $«Á^záÅq2Çè³MÌZEyÿÍnýîtÆìgvD°~FðÚ‡¨vO[*€ ^z奣·/îŒ`óÞ»öök[þ©'nýÿÚÕ·v~\;„Í5‚µ?µ?€R _(¦~iØË/}ëèíK×#ØþOšwξZlbÖ KˆòqÁŽƒÑR"Ø%˜¢%( Ü=ncÉöåV\|þb1{×ÑVû}@À6ºBØÜØŸÚŸ@É„¯@v.¿¼+‚mI`Úkÿ‹ˆ3͵ï‡oz÷›­öÔi'Z¦h‰ ¨Çú÷¼eCØ—Þ¾xáb•{רÚ!ìzÛžþ¤<ÖþÔþj |¢™zêë&íö…‹—6|D×´×Íjæc°{KÖÞÝŽ`ƒ&„]Ò?Èõ¥_÷eŠ– {߯¢ì•‘lA{×¹ïIŠ!¬àÕþj"|¢jžl\âÉÿ—^¾|ôö Ï_êùYã¦UÅ}©ØîÛ°yuûßöÛn|òdþ¦ð5§ X1^°ëûc:ìóÕî]§¹í–þá0ûSûS¨ð˜ÄÓ_Û^z¥Á¾0Í•Dx’~ñ†¬¦¼¡”š¢%*€¾>þÒ—,T¾n,Á¾rôöó!‚µwÝËù§ž8zûÚÕ·z퉧ÞÛŸÚŸ@­„¯ÀdRyÒÿ¥W^:zûâ‰vÜ4«¸³]BäÞïB›'ÿ»žøo‡°¹F°¦h BÈxÏO~â€,÷ï´#Øo>¿ÇÞµ{³™êÞ5¦vÛèÃÚŸÚŸq_Y$3ùêD{qüE›˜µÚïŠÆg¸Ñ¬?ñ?÷“þS2EKT¥í‡_ùöl¼i¯q÷¶Qö®3MˆíûCaµíOíM€)_Ù¥Á¾|â÷/t‡°«D×tº—Š~w½ l{úkêXÁ«¨Æ ÓrÙ·#Ø íI°ozW“}â*îõíé7ïÿúàÁ²?µ?"¾‹ZòrÑöÊq{ñÂó.qß—Š]E}©Ø£ÝCXS´Û„ ñžŸüÄB0Ù÷Þ%÷ÂWÚìs6ì#÷Ý»®æß»&°ñkÛ˜ÖþÔþ8Iø $%™i°WŽŸø¿#‚ð$}œçໄüJØõ6˜+†5EKTö·]yõÊÑÛëlœì{×´¦½nó›÷ut»zàŒý©ý)0‚ðHVŠìó½'Áî;íõ ê´×q¦ÉÎ?õĉß_»úÖÎ1ìÔñ«)Z‚‚¾L}¥ëü(m/ÜŽ`¿ùÜ7gÙ»–2íµëFýú“¶ñÓÿ|=ÛóÕþ˜“ðÈB*Oü¿ÒŽ`¿ygoÚëª×ûûNÌJ9ÂÆT{ðºþµŒ'zjØ ûÕoŸøýqwï:×>t)%ÝÁ+°á+d"Øoo‹`»¦½v›üÉðÕâ·`«ÂN¿zÙXQÁ>L} •½ðÒûávûÜsÏ-¸*«Ž?]9qìO€‚_¬¥Á^øæ…­×5ëê`Ìû+x©Øß¼ÿëƒx(ÊeÕ>åUPñ™ö ؼúê«GoŸŒ`ëÛ»îó7ûS€nÂW ©<éåÛWŽÞ¾ðÜ…É®gº—Š]û´‰…øµ1&‚5åUT“©¯´Ïûá“NF°ÏV¹w-Q콩ý©}&Œ%|Š”LûêzÛ=ÍuГô•NÌúÍû¿:qózàÌÖ¼ ¦"JÀ~¸Û«¯¾vôö³Ï>»ÀÞ5…Ílþ®ý)¤ý÷€Ú_â¥Á~ó¹o޼”®—H¦„Mõ©úævýºÂ?ýÏ׳»öÞ}KØÕ¸O[H®ù«  ¦¿–u,ì…—ß ŸŒ`Ÿ¼GüÅ/ÿéÄÇ=òûŸwpíOí' SÂW€Ryâÿµ×¶G°ÓL{M!5Í{Ú«¨ >¦¿–s Hk/üúëo½}+‚¼w]¼û‹ë'þä‘Ï=ZÝN×þìUr%|)Íö™â×=·üUP@û il^Ç €ô÷Âí6xúé§Gí%O†°«ƒG>÷oO¼ÿÚÕ·îøœóO=‘åÞÕþ4¯}‰ý#ÜIø A:lë%`›¶óÙöUÇŸn~ûÉÿ>Oú×FPÀ&"Øô ûïS–Ü¿ñÆqûôÓOmÞ宺wÇ?ÿÅ?ÞzûÖØmûá}ÌÇÚŸ‚ý+@)„¯‘%3ýªÁ>3`ìˆgÞןô®ÆÞ´Eˆ èÃ“ÕØOë7®½"Ø~{É“Ø]ñk´mõÄ]ûÓ¼÷Œ~` 8Iø 0¡t^v[Û5íõ¤_üòŸ>÷ûŸï¼¾XÓ¯r#(Hs?ÜŽ`ŸzêüÖÛ´þù»ÿxâ#þà‘gʬįp’ð`&iF°OŸ|çÏôßùÔˆ_ß³:xä÷Mj—š +*Èc?|õêµ£·OF°«ŽÍåíßüÓ»?»ãÃ>?2†jïjo e2íà6á+ÀÒ‰`ß8z»‰`‡>ùþî/®Ÿøý#Ÿ›&„]%z,Eùî‡OF°O޾œãöö®õóüûÅ6ºö§e2õÑ+À1á+ÀÂRŒ`Ÿ~:D°]ÏÀ¯6~H;„]…—‚ýÜ¿=ñþkWߺãsÎ?õD”û0g+((k?|õê›­ýé“#ö›Çï½þî™}ïjZ>ñkÝÇ€cÂW€„¤ò¤ÿo¼qâ÷O?ýÔ‰ß÷{’þöGýüÿxëÿëlÛ¦vøõùàqDeµ#ØóOÚd.±wµ?­‡øµÎcÀIÂW€D­?Ù¼l{õèí£¶ãIúõw‡vWüšAÀ2ûáE#ØkÇì“çÓz¥ûÓ:‰_ë:ÖÜIø ‰t¦ÁG°O=u~ËG­6þÑÏßýÇïÿƒGþ]´Û5å WA€½pðæµãW*8Á®öÜ“ûLûSš R[þ1àNÂW€ ¥òÄÿի׎ÞÞÁnžÚÿ§wvÇŸ~Ÿv‚úUT`/¼ÉÉö³í]íOi3ýµÌc ÀnÂW€Ì¥ÁžêÉÑ—s;†=®>ÿÈ¿ïüœ)¦½ ì…ûzóÚwŽÞ~¢#‚»wµ?eÓ_Ë:Žt¾$•'þ¯]}óèí[ì‰g÷û?Õ>òú»ÿ¥ÿG"*°ë­Vl aìíMéK›÷q ?á+@¡’Œ`ÏßžsBkÌË”·^z?Üa¿Ñ1 Öþ”Ú!¥6ýcÀpÂW€ $Á^;Ž`Ÿ<ÿÄ–ZµþÛÓȉ² A€ýð¾ÓŽ`ŸüÆÖ½«ý)±l ,±ËƾT&•'ýß¼öÖÑÛÛ#Øé‰ 쇗ð7Ûì´?eVbLr÷ã]³dçÏÿÏÿ×9ùòWÎ[€ _*–fÛÿå_W{\§ €t"Ø?Ÿô¾äBÜ@iß³²Ó¾pK:ìñä«'úD°«­¿ÙHð @ÊûaûS dÂV|ï;I 0Žð€;¤ò¤ÿ[;"Ø1Ó^E¯ä´¶?r%p€ý¾g bv¾°SŠlðÂöœö*x ÷ý°ý)*‘+ÌóýU pLø @oëO¨/ùÄÿwZ!ì7žüÆÖ½0Å^0…ÖÞ˜›ÈÒú>,†j%|`´TžøÿΛíö?Þú¿à€’÷Âö§À„®×÷j!,P á+Q¤Áþùd÷ –Þ ÛŸSº@YßË…°@©„¯D—ÚKÀƸ°ô^ØþˆMè u}¯Â¥¾0©#XASì+Çî‡íO€X„®`/ÐÁ9¾0›Ô#XA)í‡íO€Ä®@×A äFø À"R‹`EØ¥»c÷"X ÂW·d+8 bW öžB ¤Jø @RæŒ`E¯äNð ̱ÏÁ)¾¬©"XÁ+9»’“g/}÷èí×.Ý‚d¾ÿÀ)¾…¬à€\‰]ÉI;vÝöç"X€ü÷$"X`)ÂW²³°v…°‚Wr%x%7Û¢×]'‚È{Ÿ"€æ&| {ÂVJ#x%G}£×mŸ'€Èß"‚æ |H€Ø•\ ^w]Ž ïýŒ˜’ð`A‚WrÖ½>ñä“Go¿õ曣.W ïþF LAø °Á+9ë3åµ½®ÿ^ P×~G Ä$|˜‘à•Ü ™òÚõ1CØõëÁä·ÿÀ1_f x%wc¦¼ùø±¬ ¿ý؇ð`B‚WJcÊk—±¬)°ùî°ÀÂW€‰ˆ^ÉÝS^ûÁÔ³W¿C _"¼R‚¥¢×m×1$€]¿ý"X€ô÷MX /á+À‚îùÉO,0ÊÇ_ú’E€ ^)EWô:Gðºë:ÇF°X€ô÷QX ‹ð&t€ü °ÿRØWØ—À²¯”"•)¯CnÃÖX€<öUâW`á+ÌHäõ|`/,µß°€ù‰^)EŠS^ûÁ”»¿À›_ab°8–Ø؇Àô¯”"—)¯}4·sH»¾"X€´ö[âW`ð&"pÖ÷ÂûAûP(‡è•Rä:åµËØ)°í5À¤µïÀ á+D&pºö ÂûAûPÈ—à•R”4åuÈýÁš Þ>Lü ÂWˆHäôÝ3ˆììC ?¢WJQê”×>D°eìǰP7á+D pÆî„ö„sïYìA`Ñ+%¨iÊkÍ}À®¯£`¹½™øê%|…= €}÷ÂûA{PH—à•Rˆ^ûÝo,@^û4ñ+ÔIø {9±öÂûA€%ö1ö¡°›è•RtE¯µ¯]k16‚ÀÌ»_¿B}„¯0‚À˜j!<°'Ò z¥¦¼îglk ,À2û6,ÔCø  €©÷âWì?`Y¢WJ`Êk\1"Ø?xä÷-$ÀÄ{8ñ+ÔAø ‰äqŒì? <‚WJ`ÊëôšõÀ?÷—Go‹`¦Ûω_¡|ÂW@à€=!`e½RS^ç5v lÐD°X€iöuâW(›ðzòÿÀÜ{S·ì æô?½qðÅ/œ¶TIôJîj˜òÚ',]ò>Ž`M˜n'~…r _¡°ÔDü 0-Ñ+¹›zÊëÐI¦ˆ`RÚç‰_¡Lw[ØMô Ø‹àXµS_ &¢Wr7uôš“Tïk¸]cn[ˆ`›_Øï'™ø ÐAÔ¶÷ùâN[Š'‚ g]Ák+ —“òÔ×\âÞ±S`ÛÇûµË_wòŒÜ÷™ü e¾Â¦z©ìI>þÒ—,@$¢Wr¶Ä”×Tã×\'ÚŽ`ÛÇ^ 0|ÿ'~…r_v0í¨udê+¥½’«9§¼¦®¤û)‚˜w(~…2Üm `3“èö'z%W}¦¼Nƒ¦›–÷Ž=Žáüh~`?50ñ(žÈ™òZç};¶}Θ °{_hò+äí®_þìoV–áNôÇÏ$w›.&s[»þvÑÇß´W U^nÖ¾ÀþsYï<úxò·ñʹsÀÑ+9ê3åu CCÌ©Ôüî»ö"X€Íįäê¯þòõê×ÀÄWHDêS^Ãu§¿¶oƒI°ýÏ),Pá+P,Ó^ÉIêÑkªš´¦µÁÄÙ'šú yºë—?û›•e¸Óýñ3Éݦ ‡‡ÉÜ–Ç®¿]ì±÷r¶@ê¼Ü¬}!€ýçrÞyôñäoã•sç(€Oˆ^ÉIWôšZÔ™ÂÔלÖ+õc"‚j&~%7õ—¯W¿&¾Å½’ S^§ÑŽ@kY¿±S`Û硨uß(~…¼_€¢ˆ^ÉEnS^×o[êS_Íí¬) Á¶ÏI,ª»-äå~zÃ"@ÆB\˜sôšÓmlË%Ôâ85¿Æœ§}¦äÎNA^L|… @ÞD ¤®„à5g!~­y›û>46 ¨eù寜·á+À?y½’²>S3s 2ÃmQ¶ïg­SXS8ncAs. `€¥_Ø*üPпpÚBì¡ô)¯Câ×õû:v)q_cÈq0(‘©¯á+=Ó^IQ©S^Ç躟¦À¦wœD°@ª„¯YmÑ뮩¯Cï§6c:æˆ`Ü™ú é¾Y3í•ÔtE¯¦¼¿ lÇql+€b¾@µMyÝtßš02ÕûYKt<Çú ‰`Mrcê+¤Mø dË´WRaÊk]÷,°á+ŒTû”×9 ‰*Y†(‘©¯.á+%Ó^Yš)¯°ý¼,‹`€¾„¯0€)¯p0èk`l+€–fê+¤Iø dÇ´W–bÊ+]†Džµœ/c#XS`€M„¯Ðƒè5oSŸ¡“Lw}þTçOs©œŸ"X 7¦¾Bz„¯@VL{en]Ák zƾaiN·%v»~{ÃïS;O›Û3tm›¯I,ÔIø [˜òZ¯%£Û}#Ø”‚á>ö+€€º_`)¯uI9mn[ßóm×}Iqê뮯«Ü^`ÂW ?þÑ5‹ÀäLy-[®1e×ؾ÷+‡øuÓýÜuÿÂ׬©¯ÀÔ{Ð/å¼…€D_€É¼òÊ+·þÿüóÏ[ Ç ÇwÒû³ó€!jžòÚU’öñªa2êðý[ÿÿ“?ý3'TNø ®¨g(иµ·n˳1oGìã8ÇuÔvÜp|cÞãô5å”W/×¾¼æø•x,ÆÜ§œ¦¾ÿëÿòŽÞ¬øê&| :Ó¡7[ ŸÇýשù½õ¾n)Þ>Ç1¿ã†ãã~ذÉS^s™È™Z)ž~}S_ÛÁ+@Cø ‰2Ñ+µ·nËž·1o‡©^é7Û¾÷Ãc3L÷uæë«ßã¨h²œãë8B½û[¯ÿèšE ª9¢×\Ôr?ÉǶèõ/þ¯ÿÛâ‹íE¿ü•óp·%€:„'tš_ì^§]¿§ÿÚ¹}ŽŽ-à±Rþåû]ÑkAcÆ ©†¥±ï'yIqªn^E¯À.&¾B…LôÚ½.›þÜZ•sŒK(ã±ðýraJšòŽóJÝv}ÛS°nÉ)¯árS S ^×פ¤·¹/)¦)Ú¼¢W !|…Šy’€Ô¿O»í³—ó5ÖÛkeÿ Ξ°ïcò¶dh.ßc;@]úLy­Á’÷s‰t®û»ëzÚïK-‚ ·gés_ð !|…Êy‚瘉^Îy ¯S`ž½Ï¶¯9ß/ííÒ›wůÔaÉ)¯›®gÉðqîÀqè}òöÅ^÷±·5ÅvÉøUô:ÎW¿vñà/¾÷²… JÂWȈ‰^󬱉^óò= ÒÚÿ@­ß‹ìý!Ÿ}âØ¯Wßó ?þÑ5‹À(¦¼žÔ„–SÜï˜gjSQ§:ORž;‡mÑ«àu·½BŠç¥˜‹ð*a¢×ðµ¢\Îy€|þ~3VÝ¿ú;"@¹Ršòºéz—ŽÛ×?vJ 5—é¾½-1}&µ ;1.oÊÛ?æúS›åklºóeªãf¢W>©¾n’¿?ö¹œ¥Çb܇}nÿ¾×/~­ë|™j]—Wcÿ]§æ¯ûBR“ã”WÈ!x1õµÔèulWò9¾Íñ½¤énKì+<¡7ÅT¯}¦R-9'Æí_êú—^·© y)֚ΗC–¼_©=Þ,ù&z¦~,úØ’Ò䨮sÉý]éûÄ¿~³´×?æßíb=¦Æºî' Ûëä«Ï”×\¢Wqn=j™òº-z Ák­“^C@¹-¢Åæu¼Xž‰¯@rrŸ°c¢WÚR›êe¢×4kh¢Wüc ØÅ~ Ù÷:w=6åü¹±¾ç¤úAö‡iž/¤ó˜3××nMÑ+ùê½Ö ÜÏBÊÒŽYÉJòSˆ)…®°á+°—ØÓg¦x²nÎ'æbOhšê¥ÚS[7_?ŽYîëë˜è¤d߯ÿ®Ï;íµÏíj>fé˜qßû㚆^ÿ˜µÛôg¹MTŒ±Þ¹/©·]÷cʯßÔsö}Lµ/ &9G…C"Ööýl¿ckÚm9vMyˆIø Êq¢×®Ëžú>M½6»n¿‰^Ó‹Ô§z™è•ßúçö˜ÚçòœO`o˜Óãóïù¹Ü‡)ö,}?oÓu×ø}!…Éú¹îq—zU€˜ñn 9ö…ÝßëìyÊòÃ|ÿèí¿ý»¿Ïò>ô‰_w…¢9D°s†®¢Úy˜ò:ܦ©¯á÷áÏ€nÂWà–¥'z¹Oõªe¢×¶Ë4Õ+ýó%Åãf¢×t©s¬¿©¾Î9{ìåçßý¾óúkú;ÑRŸXú|ñX˜öcNSªs=¾ö¼eiG¯A;ÄË5‚ÝdHÈÙ|ìÒìÐø4Ç©µ53å5=ëAíÑ1)(ªÝvS¿¯»n÷·9×uèCø •ÉíIÐÜ'ì˜è•ç×ÈR÷ÝD¯8i&zíÿ=Aö…)>ìóø.œwÍaÊ¿¯Îµß­í1§Æè€ºäÁnšúºÏäÒ9§Àš°Zç×Õ:ÑëüºÇöÇ ‰7]nŒXrìåö¹ŸcïëT÷{®Û<ôúÖ?V äFø Iu¢×”·­}ùS>ag¢Wž–ŠGMô*ó±0Öùc¢°ôcÚÔ¯@œ=Ë’?¬cè|Éi¿šû×É’¯^’ócMŸóÎc(@yþÛ?¿wðÙ‡Ïv~\ë¥À¶ãט1iìVèZ'S^Ó2$tl|ŸÐ1|Ìúå‡ßïIN½Ž½¯)›}nó˜ëŠy\æv·%€:äøÒ~sMmĺ‘ÞãÒñxoí=)<Äœæ½ëóçŽñæ\Û)ÖÐ×Á¼çLÉÇkÈ:†u²Kþaj9S®YîçØÐó €¼„øµùÕ%„{ͯT…¨tʰ´¹ü¾×Ñþø©oiÚõ5#z]ƾ±c-÷s‰ušë6‡‹uÿj9Ÿ€2˜ø 1Ñ«&z-¿Þ9­¹‰^e=&™èx µoÇù)ž£9|0å€uíøµkl;äK}ìTJŒX…¹qíŠÄE¯yÊeÂg̨s®û;g@ºi*/@ „¯P±Tžð™jÂÎ’1 ‰^ù}-,¿–>Ñ«ïýk>®oü‘ÒD¯\bua PÛ÷!ÈuX£!ûÇaþs¿„óÇ9P7,Œ'xÍÇ®¨s[Ù'ÝVŽH7ÝŽ±1êÐû›Bôºí6ìs|ƬÏT× 0'á+TÊ>ñÉp¾PÚ9ê{Ø bÀ²ñkIçËØ2Æ{¬‹·^9ÿ`—ó€uc"Xì|ÞzóM‹Ñëtb‡™câÕ’×iýþ¦rîº Íû6Ýß¡!jßÝu9¾Be<áÛ¿6Lõš‰^iŸ÷€½€=K½ßOrYoÑ+14¬)°°û¼o¼î/vh8$t\2rœûº›û›zôóøÆS^×cóû°ò§æ‹8aÛ&Æ7›ËØg‚éé®i¬1osŒë1È‘ðàâví ýPPŠ"Øc×õõ^Wò”×®µøá¾/~­ÜØ@sÉ©©cnó˜Û;&6MÈðà@ô `¸›è(UÉ“`scÊk¿µ˜SJq`¬Û2Å}škbhû¶ïºÎ}ïcÌ5šë6·/cèõŒ½nñ,°$á+P5½êö|]ñ«}!ÔíG·@5ÆD°Øý™òÚo-ÈËÔQdŽÑå\·Y Ô@ø TÅD/†°/jÕD°¦ÀNË”×îµÈ5þ¦#|eoaâÁc×ß¶dÁD/úìí  /O=õô­ÿ_½ú†ÅˆlÌØ@Û­´ÐsŠ)¯¢×ô}õkï gWγTM;-l%†ž¢W`NÂW :&z”­ `ì4D°ãÔ6å5ؽ ^€}_* ]èòΣ[HÔk—¿~ðì¥ïöúXìôš¶+€ šà±¶Ö”ײעV_ýÚÅ;á÷^¶0LNøš™ð2U“»]ျþ¶ÌbHüÚÁNËØîûº.ÇÐsì”×]k!x†¾¥¿6ÆF°Øiˆ`My-}-j·iÚ+ÌEø @Ö†F¯m¦ÀN¯ÆÖ”×îµ¼–ç/¾÷²E`ÂWXóΣ[ÈÄ>Ñë:ìôÆD°9°¦¼–½@„¯Dþ!ø±ëo[`r}‚×v÷Ö›oº|ìôš¶”)°¦¼v¯…ൠ_ýÚEÓ]X”ð påÜ9‹@5º¢×M1^ŒV;1S`ƒ”"ØÒBϱѫ)¯u½°4á+´„W·Ò4tÊkŸÁš;½Ü"ØCÏ®èuè”ל×H“ð5Cá§ö/Z c¦¼ö!‚MWê¬)¯»×!çµÒ&|%ª0 á±ëo[`6c£×]—#‚MKÁv°AbNÀšòZöZé¾¥þàû·þ?EàØ„~CØ@;­¥§À–zŽòºk-¯ÀÔ„¯lõÅ/œ®êþ†Wµò3åËÜ4¬vsF°¦¼–½@^„¯Dþaø±ëo[²âŽøé @6®œ;gà)F°¦ÀNoÊÖ”×îµ¼5øòWÎ[H„ð€"‰`ëÔD°]lûÙv~”zšò ”@øš©ðÓû-@$áÕ¬€¼Œ™ò;€ šXpHˆ`Ó;?šs¤ÄÐSô ”BøÊ$Â??vým Ì¢ï”ϧÀM+€öüzŽ´•¼®Ÿ»]ëóZe¾PŒ}¦|Æ46‚56­s¤aÊkþk”Cø -_üÂéƒøé $¿g{ϸ«XåÁ²ï9bÊkþk”GøÊdÂ??vým @–üP¹¸rîœE€R‹`‡°vžs¤ïØ”™ò ”Nøš±ð™-ÀL{€ú4lŸÈ±‰S™4¬–¶±S^Ûçù:Á+"á+“2õHUjS`ƒ!¬)°4Lyj"|…5^ÒH}¯‚½!€½'@|"XrdÊ+P#á+Õ ¯Z•³+çÎ9ˆ0,9½šò äNøÊäÂ??výí¬n³É^صï¸mL;€ š€qHˆ`ËÔ½òˆ^€\_3~šÿÂá¡…¨€øâÊ}Ú+0¿&‚Íq lÐD°Ø|™ò |e&9N}€†©¯@m{v36H%‚56O¦¼’¢¯~íâísé{/[ Ç`6ÂWØBܤ¶7Ò#‚ej¦¼Î«‰þN¬WäpŽëXb(ïÜH•ð ?Ô²ç©Ex•ªÜ]9wÎI ‰ÁÆ`ƒ&‚À"Øô˜òÚo¦‚@Ó/O®Ž)À’„¯ÿ¸yáð0ùÛþ!ù±ëogµ¶â •= ö‡öyi"اÀM+€]ÆØ)¯ëçT[®S^—Ž^âWH‡ð:ˆ€¥÷"@\%L{ò1f lJk ìüLyí·¯]þº“*%|eV9N}€6?”ºÇ!/á•À€üˆ`ÙÅ”×ak±S_ ÂWèAÜ,µÁþÀ¾3.Ó^€T¤Á `l\c£×Ú¦¼&½Â×B„Ÿî¿px˜ÅmÍu꫸˜{ïý!€}'@š¶+€ šÐ1•)°v?]¡çÐ)¯AŽÑkŸø7…èÕÔWXžð7sí9°?°ïŒÏ´W u©M ÆF°Øn¦¼ö_‹Ô¦¼Š_`YÂW‘ëÔ×@ÜL½×ÀþÀ¾“]Â+€åË=‚5v7S^û­CZô ,OøZð-Ä Ä ÀT{ ìì;§aÚ+3l9ÆNy]?¶m¥Ny RŽ^M}€å_YLÎS_q{oAþÇÐþ°ç`jc"ØØlÐÄ™CØ æÖ”×~ëÐöì¥ï½½d׺®K1~Ýt;Û÷ƒy×|‰ußt{{ 4ÂW؃¸ˆµ§ ¬ciØs¦¥¤i¯á•¿M›ãØ ‰`K`My¶Û„6å °Kê /7}\ßrÌeßq¾n¸®}ãÌ¥?ÌÚìŸvÝæ]·'ÖmØ÷¸Ä"|-LøGÏ ‡‡ÙÜÞܧ¾â`ß}å[{DÀž€9Œ™¤Á–<Ö”×þkÑÇ’ñkŠS_û†—Û>W˜8ïº/Ÿ®ŸÃs\¯ó ˜ÃÝ–âð$`ï€ã ¤´±¹­¤i¯}„¶ùÕ%ĕͯØBÐÙü"D°Í¯ÜÅŽ^CðšcôÖ¡ÏZô=_Büº”mAß>êX1®s‰Û½k-çº=ûL{Í}ݧ>ÿD¯À\L|eq%L}m˜ìôÝ/Pç1·Oì7ÙGxÅ/€!R›;d lë$Ø>“M7ž5NyÝ´áÏšµø“?ý3_ÈÄ 'sœüºíþO}_R\÷”¢kÑ+0'ákÂ?~^8<´ ÀÛö8öŠ€½æôL{8ÖD°]lÐD‡S°ÁØ6õ6ö”× ×)¯C·mkñÃ|?¹ø5Ä}›Â¿TÒ]·a©`´©Lžúvˆ^T_IBIS_Û„ €¾ç†ý"` ÀR› ‰`SkÊkÿµØ¼v­GJ¶Å¯sØ'<s»7]î¦Ë(=|»îÍû§Ž¥ûÞŽ©¾æ&|…™ˆ` ¾ïùàÜXNiÓ^Ã+}Ä&‚Ô×~ë0v-r²ÔäÔ!×™ÒÄÚ%â±ë{Ýs;¿c­À¾„¯… ÿzáð0«Û\êÔ×MD @JR‹`‡°Áì¡§)¯ù¬Åi¬ð0å³kýºn÷¾ŸësbÝß¾ÇsŽãá+I©)~¦QÚ´W€ÚýøG×,̬‰`»Ø ‰S™ÌÁšòÚojZ êRZp¼Ë¶ÛlÚ+°¤»-¥(1z ¯ðlÇæñ…¶ùÕ%DŒÍ¯ØB8Ùü{ŽÄBÏØÑkˆt]ÿ¦ã¸ÏmÎ)8¶)û=K@ üP=”íµË_?xöÒw;?nŽ—¹¯];~ýìÃgw~l;rüÛ¿ûû¨·£V¾õ曽?oÌ92Å”×M1å5çõ˜SMb¸¯û¢û~>û+€© _IRøêÇ®¿m!€ªåúÊ^0•¾ñkC;½1lì6hbË!lŸsdŸ)¯Û”<åu×q/i-î¸ß{ycT) œ~Ýw­?óœçK¾.ü£è…ÃÃ,o»øèË´W¨Gˆ_ƒ!l ‚^Áæ8¶}Žüðß¿õÿ?ùÓ?ë}]ÛîߺCOS^ûY"~!2çù½ë<xs¾µR£WÓ^`·¡Ó_ÛD°Ó36H%‚í ^×/{Ó}YWÛ”×]ëQZðÊþúLùö‰1ÅšýÏc0ákL}J26xݤ‰`°ÓÈ=‚Ýu9Ûnû:S^ó^‹!–˜úZ«õõdγÎÛ8ǹ _IžøئÔi¯Àv]ÑëØÀÑØé¥Á `‡†ž¦¼æ¿c0ãˆR:&ãÎeç8*á+Y*9z ¯äÜiHôºþ{lZš¶+€ š˜r©)°5M6½Ö<嵯)&bš°Iéç8À6Â×J„$½px˜íí7õêÕ¼»¦P¶ß?tʧvZ©Mí«¶)¯»ÖÈ”× ÷}ËDLaàð5kÖmŒ]ëí8Lw¼æp·% ^² h˜ö õè3åuH´Ø|üÐÐ1l;„%®Á6¿º„زù5§]×Yò”×M_/%®ELsLw­5<³¦K ÌEøZÿX ”ÀÉ1ñêúç‰`›V;#Ø]‘gŽ¡g^ûD¯CÖ¢Y¶οVû\NìàxÓçæì %ýž% 'á´»þ¶…€Šýðß¿õÿ˜/wßüÞzóÍޟ׎_¯^}ÃÁ™@¿~öá³Û™1Ï#OÁë<¼<5 ç¼(˜š‰¯•)aê«iP¯Òÿ}Ð+w@·&z ¦šô9f l`ì´–˜[ZèiÊkyJ kûLWí *×ß?Õ„Õ1ë^b-p–bâ+YðCñÀ.í(/ִϱS`ƒ&~5víøµkì˜sÔ×~ë!xísêë¶Ë2a3Ÿc¸é²K䜦&|%Kḻþ¶…€JÔ½šö ý„àqŠÐ±Ëض=ýU;Ý9шqn”8åuÈù]òZ,eÊp²1$4ln‹0qþ5w~Äq·%¨O)ÿxjºÔi‰—¼o ‘`ókˆÁ6¿HçÜXÿ³MBäYbôºë<.m-J±+´ìŠÃûÛ3VÜvY1¯£od:Eлí2××tÈûKŰÀ”L|%k&¿@ùL{viÇ®IŸAòÅš4Ñà)°I°éœ]Qt®‘gŸèuèz^Ç‹9s×e ½Ž˜/IãþM==tªØ´öÈÓÔW`n&¾VÊ?¢9ðÊO@_¹N L‚MçÜhËyÊkìèÕ”×8bF—KN -}RiéÇoNbX`*&¾’=S_ LµD¯@|íÀqÈ´ÏX“`ÛQáØI°¦À.{n˜òšÿZÔ Æ”ÍZ#VkžæÚ¤ìË_9ï bâkÅJúÇTS€Mrk ì|çÆ:S^ó^‹ÔÅŽÇ^^ø¼}nËÜñæÐë›òö]»}ׄ¯ô"~ú½šö L«‰`»Ø ‰'c°Aj `ƒÚ"XS^û¯…àBø -âWØèUô Ì'×)°AÁ–Àšò:l-D¯ÀPÂWz«aêk ~€qD¯ËÉ5‚-m ìÑ«)¯' _¤¦ø5À@?¢×ÛL{R ‚]FWèYË”×>k!zö!|…L€n¢×ÛD¯@Šš¶+€ š3VÔÁšò:l-D¯À¾„¯ VËÔ׆ø¶½äaé)°A `ƒ”#XS^û¯…àˆEøÊ(âW@ôzÌ´W 'KG°c§ÀM»t;Å”× ÇèÕ”W`nÂWèIü ÇD¯ÇD¯@Îr`—œkÊkÿµ¼S¾2ZmS_ñ+µ¼K Ácj/w_Š1l¬6H=‚5åuØZˆ^€©_ùíÝOn×¹ÆáfƒkH2ðÌ ñ̉A$È"L1³»ÞI!€´f̈ØÞD_”t‹,6»º»ªëÏ÷ó<µÛ"«¥vÕ¯/"~€zˆ^ß²ö ëXsí³m»Æ l£ H‡°s^V^Ï? Á+07á+ŒÐÞàÀP Ñë[¢WˆA;¯1+°©"ر+°Ýkã’ëÂÊë°³½K¸zz|Ø9†·nnïµ­¾v‰_(èõ0á+À2¾|úàØüåþwÐ_/€ß©¶kÊ%ØÖÐöص±ÿ½´QêÑk©+¯ Ñ+P²úÙ!Æçï«?ákáëpâW(‹àµŸè`9ÂWZCã×–v~Ù#ØCáë©Ð³–•ׯ¿Ÿ8 Á+Pá+‘_…¯½„¯ãˆ_  ¢×~¢W€e _9DSÖ¶ñ÷¿ýõëüÓŸOþµV^_ˆ^Z_‰Dø*|í%|§æðµ%€ ;Ñk?Ñ+À²D¯œ26€mˆ`ç•9‚ícåõ…à¨ð•H„¯Â×^Â×ñįâWò½'|X–ð•!¬ÀÆUB[Kôjåà0á+‘_…¯½„¯—¿Š_ÈEðzšè`yÂWÆÁÆ•-‚µòúBð ÔLøJ$ÂWák/áëåįß`ˆNôzšè`ÂW¦ ‚ií¶q*‚µòúBô ÔNøJ$Â×ÍæÚeójˆ_ˆJôzšèrkƒ½¡ìÝÝ»ç×"ØéýòëoϯOE°ÝÐtʶ [÷ؾàuÿké²ò °‹¯=,¾NÃêë ñ+‘^Ï#zX—ÅWæb6®K°‡”¶òúõ÷XÑ+ÀÙ,¾‰ÅW‹¯Ì¬¹),~ý¦} €`m¢W vÝ oHkv~–`ý:]¥¯ûï€h,¾ö°ø:-ñëkâWÖ xÆÚ+Àú,¾²4K°q»;ek倖ÅW"±ø*|í%|žøõ-,K½#zˆCüÊZD°1À6.‰`­¼Ð%|%áëfsí2€õ4įÌIð:œèh´àÐöîîÝókìô~ùõ·ç×§"Øn¼znkå€}¢WˆÇâk‹¯ó°úÚO ÀÔD¯Ã‰^â±øJ$V`ãšb ¶´èÕÊ+À4„¯DcñUøÚKø:ñk?ñ+S¼Ž#zˆIøJT"ظ†F°V^8FøJ4Â×ÍæÚeÀÒš›Çâ×ÃÚXƽ,£‰`ïîÞ=¿ÁÎã—_ûúã9¬•W€|„¯¬Büzœ€¡¯—±ö \¢ ‡®ÀŠ`çÕ°!+°V^b»zz|Ø9†·nnïÂᧉ_8Fðz9Ñ+@l_>}p¤44‚m `çw,‚½ÐõãO?;Âùüñ}õg |í!|]Žøõ<Xö‰^/'zÈAüJv"ؘöØŒÑë©àµ!zOøJDÂ×ÍæÚeÀÚšËâ×ÓÚ‡X¯Ó½K醇C"Ø»»wϯE°ì³ò ÔJøJâ×ó `ê%xŽèXK#]ÁÒ²ò ÔNøJâ×aš‡âW€:^§%z"»Ûh#Xl}¬¼,çÇŸ~v”ð³þ P6ÁëôD¯95ÑÁ—OÅÁZ­‡•W€ÂWB±ú:Ž ,‚×yˆ^€ D°ì³ò ðšð•pįã `r¼ÎGô d$‚­›•W€õ4ÿÇ ®«§Ç‡cxëæöÞ!¬Lüz9,@‚×y‰^ÊñåÓ‡@õ†°ûD°¯ýá÷¿{õóüó_q~¿½¬JøJdŸ?¾¯þ ,¾–å×ËY€ˆMð:?Ñ+Pš±+°v VÛ©èUð ÔNøJhâ×i`b¼.Cô ”nlÛ° lV^b°ö ñ]==>ìÃ[7·÷!ñëôD°Ë¼.Gô P®/Ÿ>88aèl«¶ö¿ÿÝ«ŸÿãŸÿZï÷6+¯a_‰îóÇ÷ÕŸÅWRhoR `§c`9‚×e‰^€Úµ‘äÐÖìò¬¼Ä"z…,¾ö°ø—øuX€i‰]×!z¨ƒÕWÎ ì[k/¾ZyˆGøJ_-¾’PsãZü:½îƒ,Àx‚×õˆ^úu#Ê!¬ØéYyˆIô yX|íañ5>ñëü°ç¼®Kô P«¯0š—`×X|µò —ð•,,¾Z|%1˯ó³ pœØ5Ñ+Àxmh94€µ;œè .Ñ+äbñµ‡Å×<įËÀµ»Æ"z¨›ÕW˜G-+°K-¾ž ^¢W€õˆ^ÉÆâ«ÅW `ùuYV`€Z ^ã½Ì£a‰`­À¾eå`z_{X|ÍGüº.,P"±kL‚Wº¬¾ÂrJ[‚sñÕÊ+@Ö^ÉÈâ«ðµ—ð5'ñk "X 3±kl¢W¿ÂòJˆ`ç _­¼ä!|%#áëfsí2 $íMo캺‡D°@tB×¢WN±ú ë˶;åâ«•W€\D¯dfñUøÚKøZñkl"X`Ib×¼D¯œKü qdˆ`§_­¼ä#z%;áëfsí2 dÍ qñk\û›„°À”„®ù ^òêÆžC"Ø»»wϯ×Z‚=—•W€uX|íañµ<Ø|„°ÀBײˆ^Ëê+Ä5v¶1G{Éâ«è 'k¯”Àâ«ðµ—ðµLâ×üİ@CäZ6Ñ+—¿B|c#Ø)Ø1áë©àµ!zˆIôJ)„¯Â×^Â×r‰_Ë#†€²‰\ë"z`*âWÈc­vhøjå /Ñ+%¾ _{ _Ë&~-Ÿò¸ÖMð Àįϒì¹á«•W€ÜD¯”FøºÙ\» ¨Q{][®cÊD±ó¿Ù©—è€V `ïîÞ=¿¾t ¶ËÊ+@n¢W(“Å×_ë!~åq, 'je Ñ+s³ú ù͵{lñÕÊ+@~¢WJeñUøÚKøZ,sÑ`•5^X’øÊ1eÛ¾ZyÈOôJÉ„¯Â×^Â×:‰_`~¢WÖ ~…ò\ÁüÓŸOþ½¢W€øD¯”Nø*|í%|­—øæ!x`mâW(רöïûë×E¯e½Rá«ðµ—ð,LGô @âW(ÛØöÁ+@¢Wj!|¾ö¾Ò¿Àe¯D$~…:LÁŠ^r½Rá«ðµ—ð•., 'z 2ñ+Ôeh+xÈCôJm„¯Â×^ÂWö‰_à<‚W²¿BNE°¢W€|ã^%OäøÌæs`ñµ‡ÅW†°þ @¯”Èò+@L‚W°öÚ²ø pƒ€š4÷ÃÜ TVÄb~Fh];˜F{£ßú+¥»P“&®°þ °þg2€}_abÖ.(‘{^ÔHhà³ÏÕÓãÃÎ1vs{︘X2¼À7Ö_–!x…~Ÿ?¾w‹¯0; °dä¾¼&Àð™ ˆáÚÀ2š‡Ö_ˆNì ýÚÃú+À<Ÿ³Îañd%€¨Ü»€ó 3|¶ÖcñVÐ>@° ÀÚÄ®0ŽõW€i>O %|… `X‹à¦!€÷ù `¬­#€õùßȰ÷¢`Ÿ™€e\==>ìÃa7·÷ÕX`JbWXŽõW€×¯p¹Ïß;„ÿwí ¦öA„€K^`ymØ!€|.¼Ó³øz‚ÕW¢À0„àâÀµ¼Âô,¾¾°ø IX€à±+Äd¨ísÀœ„¯L÷á…€†àrÀ¥ÎX‚ð³ P/±+ä%€Jû\°¤«§Ç‡cèws{ïHE P6Á+”G d"v…å}þøÞ!t_Ï ~%+,@Ä®P,•àÖ#|}íÚ@¹Ú"X€|Ä®P§6(À‘>›D"|… t’ˆ`â»­nd"‚Öü,ÍÕÓãÃÎ1ws{ï(’`}bWà\X`NbWˆéóÇ÷aðõLâWJ'‚XŽØ¸”˜‚Øâ¾¾uí€ÆþÃ!,À´Ä®À”º‘Šû9 #‹¯g²øJÍD°Ã ]€5ˆ`€CÄ®“µ×įˆ_á!,À[BW ,ÔMì ù _»vÀPûq„°@„®@tû±‹êú³ T_°ø çÃ¥º¥Â@~bW(—µ×~Â×į0žÈBä ÔH ±‰\¡.Â×~׎XJß#A,°+À‹ý˜F ±þlà‹¯Y|…e‰b€K‰[¦#†€yˆ\.k¯Ç _G¿B,âX¨“  A,œGà œCøzܵ#²ó `]} 605‹¯#Y}¸œ8€lD­Àœ¬½žfñ€ÕŒ‰‡Ä²¬ùç뾊H €Y{=ÏÖ¸À2¾¬Èçù„¯.4€„¯+1Â9ŒðÕ‚ð`Æ7‡¾ºðR¾,Ìèæ8ÂW @ Â× ‰_€Sô†ã _HAø:16ÐGgxá+ÀD¯—¾º0R¾ÎDü ´t…Ó¾ÌHô:á«  áëÌįP/á´„¯.Z`úÁé _&&z‡ðÕ ‚ðuAâW(Ÿ^p>ÂW30༄¯.j`úÀù _.$z]†ðÕ\@¸á« I ¸,á«  ákâWÈGÿ·<á«‹H÷·á«70€Þo=ÂWoàL:¿u]==>ìC<7·÷‚¼Æ`ñÕ8BÓ‡Å׬¿À:D¯±X|õ¦ÐïÅ#|õæöèöb¾zz½¸®žvŽ!Ÿ›Û{‡¼ÆgñÕ› ª§ËËAøêMUÓãåqõôø°s ùÝÜÞ;@ðšÅWo>¨Žî.'‹¯²þ ‡ ^s¾Jü /¯e¾N @íD¯å¾VB @m¯å¾VF @é¯å¾VHü @‰¯å¾VL @ ¯õ¾ò•€l¯õ¾òŠ€è¯õ¾r€h¯_9I ÀZÄ®t _D ÀÜÄ®ô¾2š€©ˆ]9‡ð•Iˆ`JìÊPÂWf!„`ŸÐ•K _YŒ "Wæ |eu‚X€¼®,Iø @ [G@ÂWR¾‚ð€„¯¤°ýîû®‘5Í«ÅWR¾‚ð€„¯¤ð5|ýîû®µ­«ÅWR¾‚ð€žÃ×ï¾ÿáÊqI·qµø @ ÂWR¾«ðõ»ï¸r$D°ß¶Z| á+)¼ _÷'a`i‡šV‹¯¤p0|µú ÀZúZV‹¯¤ | …Þðµo"ær¬aµø @ GÃW«¯,åT»jñ€N†¯V_˜Û9ͪÅWR8+|µú À\ÎmU-¾ÂÙá«ÕW¦6¤QÝÎõ€c†¶©[G@ƒÃW«¯\jL“º]ê€ÆØuëèÈ`tøjõ€¡.iP·kýÂÔåÒötëÈàâðÕê+§LÑœn£|!”iªÖtí  S6¦Û¨_¹MÝ–n£ä3GSºu¬d0Køjõ ^sµ¤Ûl_0qÍÙn³~áÄ2w;ºÍþ °¾%šÑm)ßëXª½Úív‹~cÿýÏ¿wþõä·ô8ê¶ôo€é­Ñ„nkùF˜ÆZ-è¶¶o€ñÖl@·µ~ã ³vûyµÛíBÄÿóïË ž(c§[@ŸHg˜Å×.ë¯ëŠ8jºuPtEm9C.¾vYXFôñÒ­ C³~ñµËú+À´2”¦ _[X€Ëd ^[)Ã×–`˜ŒÁk+uøÚÀ—9xm¾v‰`¾)!ví*.|m `€Z•¼¶Š _»D°@éJ]»ª_÷ a€ìj]÷U¾îÂÑÕºî¾öÃk¹ö_Êcð!IEND®B`‚neutron-12.1.1/doc/source/admin/figures/deploy-ovs-selfservice-flowns1.svg0000664000175000017500000013356713553660046026661 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 18:09:40 +0000Canvas 1Layer 1Network NodeOpen vSwitch - Self-service NetworksNetwork Traffic Flow - North/South Scenario 1Provider network 1VLAN 101, 203.0.113.0/24Compute NodeInstanceLinux Bridgeqbr(1)(3)(2)VNI 101Provider networkAggregate OVS Tunnel Bridgebr-tunOVS Integration Bridgebr-int(4)(5)(8)OVS Integration Bridgebr-intOVS Tunnel Bridgebr-tun OVS Provider Bridgebr-providerRouter Namespaceqrouter(9)(20)(21)(18)(11)(13)(12)(16)(17)(15)(14)(19)(22)(10)(6)(7)(23)Self-service networkVNI 101, 192.168.1.0/24Overlay network10.0.1.0/24VNI 101VLAN 101 neutron-12.1.1/doc/source/admin/figures/scenario-classic-mt.svg0000664000175000017500000011251513553660046024507 0ustar zuulzuul00000000000000
Compute Node 1
[Not supported by viewer]
Network Node
[Not supported by viewer]
Interface 2
(unnumbered)
[Not supported by viewer]
Interface 3
(unnumbered)
[Not supported by viewer]
Internet
[Not supported by viewer]
Interface 2
(unnumbered)
[Not supported by viewer]
VLAN network
[Not supported by viewer]
External  Network
203.0.113.0/24
[Not supported by viewer]
Router
SNAT/DNAT
[Not supported by viewer]
Switch
[Not supported by viewer]
DHCP Service
[Not supported by viewer]
Instance
[Not supported by viewer]
Macvtap
[Not supported by viewer]
Compute Node 2
[Not supported by viewer]
Interface 2
(unnumbered)
[Not supported by viewer]
Instance
[Not supported by viewer]
Macvtap
[Not supported by viewer]
Compute Node X
[Not supported by viewer]
Interface 2
(unnumbered)
[Not supported by viewer]
Instance
[Not supported by viewer]
Macvtap
[Not supported by viewer]
General Architecture
[Not supported by viewer]
neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-dvr-flowns1.svg0000664000175000017500000013377213553660046025526 0ustar zuulzuul00000000000000 Produced by OmniGraffle 6.6.1 2016-10-06 17:55:27 +0000Canvas 1Layer 1Network NodeOpen vSwitch - High-availability with DVRNetwork Traffic Flow - North/South Scenario 1Provider network 1VLAN 101, 203.0.113.0/24Compute NodeInstanceLinux Bridgeqbr(1)(3)(2)VNI 101Provider networkAggregate OVS Tunnel Bridgebr-tunOVS Integration Bridgebr-int(4)(5)(8)OVS Integration Bridgebr-intOVS Tunnel Bridgebr-tun OVS Provider Bridgebr-providerSNAT Namespacesnat(9)(20)(21)(18)(11)(13)(12)(16)(17)(15)(14)(19)(22)(10)(6)(7)(23)Self-service networkVNI 101, 192.168.1.0/24Overlay network10.0.1.0/24VNI 101VLAN 101 neutron-12.1.1/doc/source/admin/figures/deploy-ovs-ha-dvr-compconn1.graffle0000664000175000017500000002104013553660046026621 0ustar zuulzuul00000000000000‹í]ksÛF–ýÿ ¬?íÖØP¿O¦dÙ{Æ‘5’ãì¤Tµ‘„1EÐ $Å“ÊßÛŸH(Aäu*–LvãÙ}Î}ßWýíªçÝ„É0ŠûyN}òÜ û¸õ/þòüçO?¾4ÏÿúóWÿõæãÁ§½õ½h˜zG?¿þðþÀ{þroo0è…{{o>½ñŽ>¼?ùäÁ1ööÞ>÷ž_¦éàû½½ÛÛ[?p£üN|å÷Ž’x&é·p°—0Áï¦Ýçpšüès—Ÿv£Núóï^} ¿ý°ßI£›ðCð-LÞ÷»áo¯öܧðeÔOË0ù¼Úÿ:žçŽ:A ‡üœy2)H’ÀýòÝ«ašÀ-ÿWèÇWýè"‰¯þGøíoIp~Þ Õ«½Ñ™ÑTYŸ¸|¦4effÈ«½ñ¡óK¸Nãý‡éäÔirî¿t¾¸Sö»pºÁeÔ™ Ýûh˜17¹Œß'/<òÇ ïw)á‚´”J)´0R¼ð´a¾1VÃGDÊäÌÝ‹;ØA/–{÷¢é¦¼3?~àlöÙçÃNÒo½p:r|;ù—pÈøËôÛ™¯óïß$Áíô²¦vøqöràaOžÒø·é/ùㆿÆñÕ ‹å èßÃItM—Éø9gyö-g3âÞõU¿]ôKǧåãg£OA1Ÿ„Aïk,ûe<þáí› ‹—Â/)}I‰Gõ÷œ}/¤÷'JWç'Åù?iêý^zp$AqÒ›h8èßN:A¯tfØ“ðÇë\yñ&¿ðcÔ ?}”&°Šc³ö&î\_…ý´¸A'’•žäha†Tlèù•VØ93[‡ÃæTXi´â”1)_xŠIŸrÛG!¥!Ôm.Jáý31¿}ªöÏt]ƒ°jŸRú)ü--Ïû×Û“ŠÑ½ø¶<ô8Fÿ +FÇýô}ÿ<^¼¹ÜˆŠ½õqöO‚þpœi:#[¬f²hg7b #¦¯ÒH5U8QºØó¨×›=õôëj¤X³—8 ˠ;û`=vßš9vñ)ÖPáæ€iö=ù·Pu¬é69MÒsz Ë"Êþê .€HØi'îÄûJ‘ÿcx}ÿš<ûýôVXzÖ;='§çý~v.ƒd¦Ä/²?ÿÃ:q/N`ÜŸO“° ûïô" þûå¬wÂÏì ÃóÏ)œÉ}N)ƒÙ§Ýð< Î4#ÏNAÒÍþäþ‘F—iØ?:€‚äÙ³ìb†Œx§sæ½ù|칇ôƒž×ÓÛ8ùòÇnÉÐ/°™Ë»pþmÍðÑÊ(D¸Ï‰&G «¤u(dn‡„¥’*Jl†B@WÍ-U€SV™^ñ3Ù$HÝ $îý-[ägU œø‚Ã]©ùežO¸¨ž@‰!LVMHª'h­¹±wlÒ8ø.ìÝ„)È«¡£¨ºH(«Ð½òk:ˆ’N¯èï œÅ—77¢âõnvüö¸4Vh-aåÍÜ}å{œÌÌ^£P\FŒ¦šWÌL*gæïSM¥‰Öʹ™so¶]0'ý09ºÑõü)JÚ/¬@‰<&BØq@k|ø˜Z©,·Êjîð‡ŸDI%¨Ö* BQé³§€?F R~¬ÔVˆðca­r±üŒòåIxŽîª0¤k£¯B¡Ÿ‚ àÃÙ×5­¿[¬}7£ø>2>F¼ØéŸ•J J¥$“?re€ôl1=¥i ^‡}Ðå.«ÖZQaX£Öf#‰š>®Õ™À!9ÆÒ™Õš3ÁAHZðrgï¾6¯-i?°ìNŸ}½øã.ÏýÈMJß)Aªa„À7Hn[InbÉMÆ)Ÿ¢Ÿ\rS ³/eÅjÈ¥LKa‘ÐÐÐÖ ´a“„Æ¥ð¹³Á’Q\ 4¥FÖ"M(,&¡ÚVGm µ5$7$·ÖÛ&ˆM: rÒÀWLùZS +ƨÒV¡ª¶ÌÆPUCU Ù Ùl[ØÌ1ì}1,SI¨uE}-¨¸ƒå§¥dŽÍ gÛZ´.JxïÒÔ BI¬b¶ AŠ'¨Ë„ Œª²U~ˆúKhkö@3Gº*¸ç(FéLtÝ,cc$瀥€mÂ’yTrÃã4‹Fœ ëûnaìgéŽÇÓgn%˜Û‚¹ ;À`¢Ìl4F†m22¬½±^À£`/Nò`/ÎÚ@S¢x? ;}öùÃþᣄŒ1n}.¹–:‹Ÿ¢ì…'­ô¥]‰YjÈ(Ê‘ Ï”¨]'—ÜÄT‹]2ËR-v±ZËÖÒ oŠ^$«I/’Ƹ6D&(E/Ü%D­Œ`tÎ)Ò Ò •cz1Ù²]äâ(Å£ËIÅ=Ýã°¸l¨}XÑU á¬">“YÔð ¯Ò¶!n2Ò'Üre¥á”Yc8p“d>¨;–0f‰ þÌTRùÊ:¿‘· (±²4ã=–OÕ¦ÖdÄÅÓp^iÃÈŒO­l…b ¹ÉЂjíÑÆ‡6>´ñÝeãËT7ø6ôX“Ž+& “„(£€¼$07¾4LiA˜Æ2•§rV4|N)1ÜÐÎÊ,Ø*Æ»­ÂúÀLT[áaDmÔ¡´ O§_wÛ“¦ã¤i ‘pP*ººû)“O\FµÙ¥—­ ž¹ Ba…tƒ¬gžNšRÔ¶SÔ‚<ÒVy¡.¿Á3 zÞaž‘ ”užð,¯;p˜°6k-à gú¹kw]-ë‰"x®Ã .¥b.ˆÖØš:È}œ[8 55Õ9©p¹ÛjÞ,Zán7ì'I\)‘*S‡ð"è|›~ô†Yå™ïfmÀ»ëÂí1ú êÕ»’_¢nA·Ìn•­Ê;pÂÚvê¢)bM XÇ9C¨Ï@î” 'ÿkîJ‡€h„aˆ0RºÏW5€aÛS2€ µqlylÆTP«h‚4VŽÊ&³ç!m\¨@ «ž¤ËÓǾ¦!eùå¾·¶Þ§F9°QhÜ|!e¡×…FÔ|EQN !‰ó·×ˆª©(¤\.¼X+"z‘>—‚;7lr&øÂÙ£ÜPJ,ˆMDè,R@ù\ƒDH¯œð•í ®´¬PÂ7 —M¤)ÌòõÌa­­ÔüJ9t™ÃšKA µF™š¾kê=gÚõù@LoV‚UÐSaý«°À½ð*uJ­|éÐö§†2]šÈ=ÛÊðüÌ»K–ÿXÒ£ELg²š=ZÅö ·½}ئågßï¼Ãà*‚N؂ȑ_Öy4¸»uèQ®ù*œÅó ÊΛŒPs߸2Ä×V†„kø ‰Ó!s®JÊ|W_q  Ä^h/À¹)»*5Ã$LØ‚£¿°Yhd›‡FÕ$4N|ˆ–*å47}'4rWꤺ3FR`$ÅV 㣥O2êS©aQpÎ$µ‚Á65Âw¿+-8¡T€ ² ¬›Rë;‹*Õ‚*ëJ¢¹¥s Es š[Ð܂斘[N÷?µÑÞ2„‘bpYRJ½¢Q}WSLŒÑSÑD!!ƒÔ‰Ô‰Ô‰ÔÙê| Sï8¾Nä ú5É.í‘IT;xs}Ô9µL K@­@¢„q*¨¤Úr“‘hUrQµH¢ ¨DEEEm‰z?ŸxG£zÞë$ê^´ˆCW¨µñ@<*ó©äTÐ)á¾”ó˜ø tNK8Õ’h. iÞ¤¶¥…°…òѽYT=‡w=\m¼ÊRâãâJ+÷ÈBTïGö«r¼©Ïñ¼‚㥮ïBŠÊèÿ£x¥QÜ/]*<j´‘Ï2Æ\Ôð<ÚºáÇq¸É_Ïäœl¹+s<}æV²p'×RIÍœ¼ÈæD€&d“{ø չα[Í…5(¸cÄœäÈ[ÁSÌêZôað³Ïö—ÓÌ/Òetk‹¤Æå© ­¤P.Α‹„ÏàÆ¤ë$™e_dä¢\\XSä¢T]ra¤¥Œåš +éC‘‹±>·TpØ ZI-’ ’ËS"—O×ý~ØóÞ¿y†a’úÒXk¤#&˜Îca)± Ó€´f¥â&gÚðºê 3”sسJim”úÁvˆ`R1W`Sfåæ‰ÒŸmž˜yŠ…:°½Ô¶—’R`ïDìˆ-¬V»¥½'}%Üw–6䯴/¥uÊõ¼·ÂH$·­$7Šä†ä†ä†äÖrk²ã=nCFTFl’jt¯²à@îÊí¼U0Gͤ˜ƒe RÌ0Ò¶Ô,(usާºfA Û‰f%0Ž5äáÌ‚œqŸrç)Êì Yð~Å'¸ÈŠÇ®,qhIù":[F„J€h.u™)á.[:ù8V‘šÜÐim» Ÿß{”лãÃìä›pÞué9±Üg™ºÚY¥2¶!)µ¾.õ[*Ë­²èVIí+W#“ ÁŒ¦$¿0í“ÙR*+ï0ÿ™|Ð&_R§£’v„­RÁ²*ßçA”tzBÁ£uºj!صŽHVV?š€éµì?s½ª Ü€G r+¡ ïW†¶Y ìh.·EN~,ASتæFè~ÝP²ß‹.úU"se«öŠ0 Ò-ºáyœÐ1’fÜ_㋌äbÇÇ›0éß¼~˜ÞÆÉ—Óomçºç6J©mž¥ê™g`{(M˜«æHkšg´„i`O1ô~£÷½ß÷jDΛt…WQàüg\ª'J­¨”SŸšˆØQjzêîä#ä£ࣇc¢êTšU˜£ŸR´±ÚÊTšvw°D~C~C~«Ô·‚´syú,½îo˜Þ`ù ¨ÌÀÍh!µÒHo[Ioé é é é­Eô€T›Þv¬^É×|/<©ÌÚ½ð¬ò à(H\XæzlªQÅ«&êcµ"VSØ.v´Ih«ÝdØ oVx%o/6 ½02¢6ŒºI`¼Ûfz„æ+öÎ)³Âå†*åÏkdˆŒˆŒÛŒD 26)2ªUƒÈÈõ(;O Mó”\„Œ’Bï…S¼¥Î‚Eµ: ‡™”pIåÂÞË’ PG­pf.¦¸ ªNçæŠ /U¢ÙMPn·1y-i•?Vãf!¹Š!àoiYVWúÖA„æŒæá@Kûf1rƒ#`8PÖc†³°qZ±qV ŒÇ®oÖ¨ân »f-qÙ>PìIÍDXþ SÌ(Ù™Œ©w.lWsÉ _M»Œ[±’¯L(ZPPeÛ!s#s£ß·n ÛD©à)íapӎОÂà& nB’C’kQpÓ£ë¤,¿œÁå·—+tsn*]Æ®Ð"GÊÝJÊ•H¹H¹H¹H¹H¹%Ê`ÜåVÙv·¥T2îrÆehÛEÛ.r0rpÛK20Lê¹#ºªØÚl𒬡ɭÏawkEµ…5ÂÌÝ‘Ž3ÁëÊ€2Ä-ç’׎ÖY êkΘaÊ•H¶†ïdœd»[¿aœä:q’c×›EF¶qdtåk×CÆbbWuò+gcÂc[%}ÄõðP#6Ї›ÏrMf92Ê|éBõ¹• ïMæÓTÅùS€CJ £†€ŽbçòTé]yD˜Žƒé8OVe“6óÑÀ‚þ-æÒ¨¼MIísÊœö¡V¼uX*3lîŸa#fØ`† Úr1æ%6Μ{‘d™Û˜f³¤tÔC¥Ùâ m8ÏžøLž*]^)¡îÌS…TqC-’h$*‘D‘D‘D‘D[@¢‹B€ÚH¡w…'=*áÓ‚%Gëkf¨¡ÒàWž5.äú…ÇØ¶tS]7Š0øÏ˜Q~g@X9"J«µ¼Ýo®¬¨ªÔï,?µ­*ÜTWâaäÄÞÒ¥ZÃ.tv ¦ÂÂ4óxë†Çi&3¬PÓ²uË-´ãé3·’—\ŸÊésÂþ·ìÛÞŽ¶°G´ÇMö…léM)ïó‡ýCÞÍ'ÇaVùM¸+ºŠœ0î³¼Y-¨JUˆ¯.ºX*³q'7:Õ±V&:ÕÑû³0ôû^)»•¨ðþÈ…õa´ò%—‚3Å`“*K®îo¸âèýAîÐpÕÃÕq|†‰w\…ÃAÐi‘Ñêk’]Ú#¬¨¾ÖŠ»pl+à6¤ ŠP>5’àË H¤Ž@¬&aï5ìUy´{ {UänÛj¯b¶){«k¯RŠqňPv¶D?”½ 6‡ï„²(<ZGóÕö'ì²mމæ¦9ЗÚeœ» ÓËö›æÓ.cÊw† kÑ™²‹š3—+Ë`µ™'ݽR˜­¬?ÞnHGýõcÌt_\ÅôëÍYƒ…]ÊuÓ^x IúŒ.80•‹vocaNÄÎviw7d>d>d¾UêwóM3 )™% À„4F`1Ñ-Tï$Ãb¢XLII®MÅD—d˜­c½$Ôg®0žÒ°î°Töΰ›@vCvCvCvk»-iS¸ŽîVU'¤’ÞÐ7·uôÆÑ7‡¾9¤7¤·¶X(›$6%`nk£²å¦Øv†Ø0è‰ ‰ ‰­UA'ñ†]nR2_;]Ž1K¸yé Œ:Ù²¨a4F`Ô RRßê,Ôd¼‰`¾œiÜ`­¨^®PµÛÆ|­QµCÕù ù­ ü–옷<õ¹„ØÍ—°†¯[ªÔß®N”JÕºë¨6ÌÆy[Q˪è.ÆQ÷FóÀ¸vm¿ªœmIFýc¨ÀŸ†/l˜W9»PÛïf+Ñ–Û²×€ÆR" Bã=¡qó½DÙÚÐHµñ]á ­(R3-ê'Õ®ä§ÑÜR¥˜´zaJXtÉa²Ë´bÂ*εY8Yß‚äôIépwwhWQ\}ªâ*CLn²Ÿi1Õo˜¬×W­¯\CS€Q!$Qª&3e}'™rM„Pf—>«3»Ø¨Qð:¢2Èj¾D1€ÙõX5ØÛ¡n*‹ b‹ˆi[)L¹ŠgRúÖum#^.@ ³Yç»ÌR7¦ŒÆ‚Ø Ä^,ˆ±ÑA‡±±êÓh‡Jð](©Ò‚J…–%ùnÕv¨F"‹ÞŸE©EEEEmK?ÔO×ý~Øk#ƒ.Iˆ Òú V ”0–PnÊÙ,«€¦¹›Keá^aO5 5‡˜:½!2d©‘šŠz`ª®ß$‚n¶š{¿å×~Ћ¯»å×ÄÞ±·ÒЬެÞpn€°>~Vºv0‚°Êw~/c6+»»‹*ö`]l]v<% ·õÀViÎ Už®®¶Š×%ÞHj9:Ÿu>Q¹Ù€%­[µó,‚À2>cÔ„ž¢Ö7„S"!Ö˜ÌÆfé Ëmé<÷h­ÛhU(ÁŽv.Û|s¯ý^tѯÚC¤jõ·Ø±­ž§ÁZ# Îý5þhqû.FFõ’ø&ꆉÂÖmœ|9Í ÊÛ¿€“]i¸Ü|±¡®\}æJH÷ƒ ‘‡²K%U”ØÌy®}2ëRWYÌâügòAKH<ˆ°¸Xª>/ÒÒ£¤Ó Û£¦ç}儤b.^ÉØš}å Qœ[8 55[ÌKm„[-Š=!=}eU¶ œ]§4µ•¾q~;a¤“IVFÓ,` s‹ýlw¾Ÿ­0Mõ³L×ìg+4p¬àpÕ†˜õûÙ’åŠ5v¥ÝÂ<$lKÛâ¶´Ÿß{”Чܙ¶²ú5v¦Ý‰J2Ra%¬$ƒÎ8¬$Óž"¡ëÔ“YV›ùÖºÀÂçŒÉ vÓUEB]"E‘Ý@‘Dv{:ì&0*¹¹¹ð qá(¦3¤ÁY/n¸dh‘ -êyÛX.›A ·!·!·µ¦bh ’'X~1azI$Y#}í² \;,_F\œUóV“±¬Fw$º#lÊ)MMw¤¬í~dè~ܵŠ[è}l±÷ñÈ«ý®G,ÜØ|M[±váFK}î4~*# ŠŠˆË'bùÄæJ©Ë¼ÚPvEÃ9%G–Ql{6™¸OÅúu»1› «~ïd:Y¹ã½b¹g ”êZ‘HéÊ¡Äha¹±šV8ë’OÕE¡-?­a1 «ã£ ŠÒÖ—{`ØJEöz §\P sõú­ù{æÖ4,f#W ³N -á°¬Fú(°0DÅOatƒ48}v”Äpؤž$ÄI{,V®Ø›çA+ã»Ühk¸‚ÿ™Rk n¨¥¦ÀZƒXkÉɧäóæÝÁ‘w\…ÃAÐiQÁ¯ÝËÎàAÊ îšË !ã2†S´Áþ wÙüïço@z žºÇ@¡Ç Q€Ü¼Ç@®]®ªr ãLfxèRE€ÜNÁ¦;ÒÕk”Tlæ Å¢FIŠùnIXÄ*jš]šÈÐãhvA³ š]ÐìÒ³ pîõo­kîðõ,yäÎE¿º i)§U¦*…ž«qš–yåŸ(qVvîFâDâDâDâ|ø. Ã4èwÂÇîá§ïÔ;­(R3-ÊõÅm¦àY‹õÅÆÅúâX_¼•õÅ=:©0þùÃþ¡G½ÿ¾î§ÁÅEØýŸÇ©4®¨ï:£gU´@ +¯1ä WgðŸÑ‹$žeRhOTÊå‚pµ”%„ÕZLOi°ÒxS•Æ©K ™wÔ?3(kA˜Æ2…íqêº[”/î»û¹B±×–åOóQíw)ÞÍÉm”v.½—Þ;ü2¸ ¢^põ¢ô›ß\zo>ŸÖ9ÃLwñÕ îƒ¶3ô‚~þÙï‡4º#?Ž "|: `.§˜HCòÚ$†ûs©4.‹B”5Q†jŶhmªEë&{µˆæ1tK |;Z†O”ÿ¿É,Ó]§¡wwÛ È™„-/hOÊ(5jZÌÍH®èíäy1òk¦Ï9î p§ÈqÈqÈqÈqä¸Ã‘eyc7uÃgGù[ugùbò*'ßž ‚\Ãä £ðMßÌ—A˜.œQ”À¿ã¤rú$ê²p¹Í5¾ºYÓáÕ6¨|t? ?Å+ŒŸÞ}vW×Q7~ˆ;_ÂÉÄJø˜ý9FgS¤_pš,V÷(¸'[«:È4g™+x"F&+ ÿG>Å'`æ*&!½y8oð-L¦gž„[¤wãwéýùH—VþïNãÑòð#øçªæÃ“^Ô ‡«]Æç(¼½ó°‹8\m|.^âûýè*HÃÒ*ØAÜ·(éÄ?Eý7Ñ0-l‡Ù.Ó±'!ì÷`®0æÄl_ßË.ómÿb¶(çøZºqZºò»îaÿb&øcÒFtLïÖÆÙl3Ðéü°4ˆV?¿Ùà¿,Ú=åÍ6 .ª1~»çä2œ‰HÊþdWüw£ó¨“]õ›™7=>(#T½¤ä%ãßõ=çÞŸÜ“++;ÐÌ^}ýS¦—á­÷׃[x>ʼn‡q®~K“}î!W—F†%³òÁ6½†÷ôe¸ÂX·oœÃ“×qšÆW? \Át=ÌDyŽwÞ‹ƒt~[~t~ O!+?û8‰þ÷ƒ\wÔ/,¼ò‰:@šÝʽÞOÿÓ}÷÷Þ¿~¡$üß×½_ß¿‹÷Å?_ÿóýÉÛËýË?IÏ~ù{¯sqÿî~ü™þzyöîso¾?x+?}bÿúë/‡dÿý/o÷O¾Â˜ëÝümùåÏÓû<‡*ËŽª`2Ò‡ÏT?Æ/±Â9æT¨ªs”œ>“¬%+}¦¥TšHáô]ñÂ3‚úÆX$ÑDÀ?à ·°bw<…•p섾M.…Oñ`‡ŸÇòìE|ì‡EÈ ÇaÐýØï}»^ŽãÛ¹`“%¢ -ˆ‘9ëð©Ô9>Ei ‚þM0œZòW g—á¤è\ÔÛwÞ“U$½l²\XZ͹?÷£¯×áÔаäî†oûi”,{ìŸW:‰ú Y/Fõƒë$'‘=ɲÌ^!²¿ýmôa³ý_þˆgdº),7ÉRMFÜ(0•gEQ)_xšüÚí`ζõ*M®Ã½¹ï¯{S†. €WtÌ|=M‰›ý~>cmÙÍî;égïB·½‹’,ŠP#–>/æÐkZÕßWó¨ ¢vïÒ¥·ÏüÑ/<®õüg´â‰ýÇWEIu¶"ûdÐç”áa%vTDwWì¦ï*?ýÏš(æ­³`VžéÏAvÚÏþŒ«H©>wneutron-12.1.1/doc/source/admin/figures/deploy-lb-selfservice-overview.png0000664000175000017500000054613313553660046026706 0ustar zuulzuul00000000000000‰PNG  IHDRàØ›€L}sRGB®Îé pHYsœœ&Í:4ÕiTXtXML:com.adobe.xmp 5 2 1 °ã2Ý@IDATxì|EûÇgöî’Ф÷&  "ˆ];ŠŠý EP’‹œäDý+¢ ¯½¼Š½£ˆAPA R”ªt’ÜÞîÿ÷l²ÉærIî’»ä’<óù$»·;õ»³3Ï>óÌŒì˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜0HæÀ˜'1®Ô>ªG¯ßÆÑv¥ËuGN8Óªqv¹Ïòz„=#Õ¹Ô_ùt]—c’í®)žŠ®ì±ò÷¹S§ðç—¯1H'pkf›<§‹]ˆ-Ϧ:·Ez~9L ¢PÇÙ1šGÀåúÚ¾[¬è`JÞÓ®·R‚„äÔ[u]káNžˆÿšâg\ZZs5O6°æ×&”œ¼Ö÷f$$x¬×+z>Î5³]®š7JŠè—ÒÝ“7GÕþ ÿýöˆ]ÝpÜLØH÷ïJU¼ûOðJoESt!ÅNisü=Ï5iO¨ò®ªúgˆ«1mEJ©[ã%a%Á™úª.ôAÂ+„Fÿ²DÎ=ÖìñI“²¬~kËyeêb¸˜íÅïmÝd[yïùζ­vðGiñ§’«ª× ¡ÏR¥âÄÔâwù¨=X¯=ϲN•dXÑIõ¨¼R¬CÁ{”Wøq³gGçî>ü<¤G|jêûII;Ë SSîçdkO ]¿Åš_•~lÝ#â“ܤ ýY(òyÑ®Ù[å Ö8¬çy^ÏH]IBzšáú(뽺xNõ)o÷áIºgïD¯Ð#†ðKⱦéñΔE™6ozÒçáä“§æÍA’ƒ ø¯’º|"º ÂKLm¾‰e$ÖE³=¢üÉm{Hh$á±T—“¥=Šç4b‡È<ž––ê‘o0&Pk °^k-ÌJàé{ïÍ…PDšÄ¦m<‰»dÉÚæ ýÝ¿¡†F4J袙z/΃…¦†`p%´¨7ûjQâ åy[ è/È-÷”³ûð< ÀÛ žßã|‘.”\E×ÛéR?ÌÏ„ žN c’Üí=º~µ”bë…½O8søðáP×ùuñA˜]½ý¬+éתz¤Q‡Pÿe”WÍIun©ªt9&À*N€ðгã5ŒLO†æg9¹†å<°ìêR>7?%é _ߣJ¢iÚ» o=-uî¿çë§¼ßéÓ“HðîTž¿ºp?!9ílMóŽ€à»G±ÛΜ皺ٷÜc\3º¶Sþ"|uMµÉ®ÂK*w¹¼Îß(m$×EÔ‰ñž5SUíy˜¥œár 2£|ëG¨çfi—á#°·pØ¢C7Çǘ@x(ቖce5›Ml#3ƒš]ŠüÜç›A(/Ð/MgR¦ Ø*R~âær-€".xçr½Cé’¦2þu¡i„×åÂ7Ý{Ö5u£Ë%µòÒ©TÞuÑ:?~¹¿¼tÊ»?yæÌôìÊógÞ'›gú3s$»ù`üWÆ/Õá@óY™2æQ—¯BÿBxßLõûI…×+qP‘úU•H¢ÒA‰s0õÇš`EÛkÖsjbf ÄçL ÜàVCÞ8I&P*Ò0z`ŽÎn4ÛåÚ€SDd—‹CÃôgO«†q]—«±ÊÄHØL_S‹»aÂq†Ðe#˜pd¶öSÅì;±.!É턹Á­RØœéîÄ·ýeqÿ×ûÚíö+H(#? ΔqÐ[ŽBù*Ã4Æ7ÜØ$wGÔ?Búžz±ò²§’’`2Sºét¿jØ€+ÊMþ4à嚌2Í„¶tÞüTçhklù\”Ÿ‘—Q£“gôSuïLõ» þ“BŽ——0õ?˜ÀšˆcNº;ùikx:OxÈ}¥¦‰‰×?IÀúMWħmmç>¼C]ºÉÙŠŒîî;3ÞõH¡ªn¤5åmøUˆÛ¥Ðó(^ÓÅÚëŸ5Ë5á ù;Þ•ÖC¨^ØÚÊÓÑñ·G>3qo…â°=PšPl†­ì1Þ™z?X<Ž43À&!Øø‚ÍûHgʘ5Îp;I˜ .÷i t+êÐ5àÚõõ ¸Ô¹ þ.-/O.—®dzÓF]ƒ°=O,âÉÃ3ØŠß …bÏÈH™ò›5 ³Ã“úÞ‰aHû”‚{Àÿ xçXýÒ¹Yg»rݽº­ýrõzH¯EØ~Hk3æ%Œ•šö$äþ娓wø†7£~g >œ¯ ÛäùîÄÍx‘®ßºˆú|<ºï©8y«ð*3‘îmíÝǸ\à ëV°e2óä{4Û#EÈg¥bû¯¦«?‚¥W8”>®Ä¿|ýLr¿@&L¨Cçù[Ý&Ð:2Ê™r쎦£~œKi ¾ð|xļGÇÑ®ÔÞ^U[v¿Íw'crd‘íšÑÅ«ªdïÞÇ#Ž#h×(Ry5=%)£ÈgÑY mÍHgê}¨—³$&a¢M¢ø ÝÈä§ ]}ieI‡ýú ×”­t3Áùh7Mä=ˆzr9ž{K”(Ä~üûgïµµ%Í äƒ¸0!>aU@€5àU™“ˆ èDºAÐ8Á77Æu©_!5çoBlŠûctdo¡ï¶áw¼æÉ]î«Uß–ùñyùÆiùÝ‘ü`¢^¡V8ªE#êœò ÄNp¦]gñ 9Z—yR¼ˆ0'KE¦—'|[ÖuŽN¨ÝW¤¾Òו ÷ãœÙÓ«y¡ç¾éïè´WE1„hÎ3Ê*%MÂ,æ &i^ñ„ïs‘Î/¸ùŽ¡ÿ½?Ó³ìÓbž-?èCC¨žß/>bäf4Fî0YTõ´tÙ vÕ_ɽMlÇÌ `6 Â÷O¸>×Ö¢£NGzðœ®Õ<Þ_!” 4ý†ç¨å ¦Tg\—àQVš¡È»îýQö6øÛEiÝ>ÈkòÿÄú²Ò7ïA~ 6êÏá7„UذÃ| uÏ\tÇ{`Ú^¬\¤!Ïô¤~Š{3!ص@šo!Ü{ƒ|ˆg X½Gu׌ŸŽfÁ‡YÇE«×?ƒ°3ÙãðѼÏø[©4û–ÊùÖûRS!4•tcÓÒš¢^ÀöÇ5°}M>Ìx‘çby¤{#“SoÄáWäéF¤wø3pA8½þúX…‰Ò(ËaÈ#v^ÊÔ_·GQÞh¡jÏ‘_Vß{ÁÔMÊ^x;ð<ñH<ÿ‚º€5yþzÖ5y~;ðlÎíœY²íÓåMô®!¯½UK|¸aùЋé>0§*r£’S‡Cø^çwâ?‚6+ñˆÈËI0wKÇÆûþF:à§Ü¶¦(•âg£\©'Aø^ˆJÖÙ®(˜Â÷XWZw]Ï[Ž:u'â?€úõ*êæÈ—å¾õ羋³ä_‘A BCˆ‘‘uÎ!]´Cg=U¶+ Í.©“>p$ï4ä=wz2I[ýDeS¥ ¡èLn†f B¤6oŒëÑ¥FG‰ˆ"í>:Ò´HÁ¤åèâÊ‹GþoD'ºDoßüeá!pwQEÞB_4j}W «h$¸Òzéªw:ò{ÂýUóÜÉߘñM~äÄ<]ýxgóšõ5Ýÿá^Cäë!hºRÌ{ξ/gÏ¡]¸×8Ê.Ÿ™ãJ**IàÕÕc/À¯bwˆþs]Îf8ú˜Ñtï[^]¦Ç§§ŸTÑ_ÌøJ;beQB²ÏNœ.=9ßJN»{^Jâ²Òü›×C•÷Œç,Ä9‹–ÕÄ‚+¯€ýŒԤ{ÌtÊ;’P›—彇÷8{l÷'] MX¨¾9˜ÛvÖK¬ñ:’7¿‡ ~ü/¦EÃ;¨Óýñ®'g«YxÆúÕXñ?¸ô]/æ4-iuPlÊ%¾«Â@»½ïÊ>Yˆ§Š…ücÚ œÿ†¯vÖ×/Þ£VOÞÁÃŽºxwºÛI†#!xx¬…ù›Ž•*“5"ë¹ÔiôGD7o8«. —svxSéÙ̶z+í<Ø:‚ºð âzʃŸp w`ðb·yTï`ë=¡KDz ê-«ðM~ 3 )ðñ¤wW¶î^,\@êЕy „“ðüz`Ô`) @ñÉ©wûŽ’K6òNùÑóôöùZa¾é=ûôTg1á›.È`÷¹n·Ûœ¦ðMþÉ,ƒDé~œtôux^}a–0ÂWø6üéâù|ÿÚm¾áŒßš0®+B/ðç×—qQU=!qÈ&ÞãäBá›n’Ôüȥߕ-ÅáÏA;C׉xÜ…:¹V¤rÍèäωka¨#6›!€ÃªC\`Mo—ºf=Æhß›x ×Bc|9>\¦:dz;eÈŽjÖ`©yœ]¸^_*â)«ðM÷ç$&SnÂûK#V“hz3\á1ȶ†Ö}G;ð%Â7ƒ¹ß 0w*ü0âÔõtTôîwdòfšþ¿Ã¿˜@õ`¼úŸç BØ„­˜öÇÌ–Ý&>¡st®Ík¡8’-5:ªÏÐ ^Ž!üÑ£½‚N0ZÚäí®þ > ½ÂÐP¾ñ‡¡×ˆ;¿[kB»ÎÂ-+REy˜„˲¼øÞƒçóéšb·ûbZ6\@BH‰pd®{ê¨[Û›o+y_üc\Ó¥±Æ¶åþtŽŠb–y_ å{:÷JýdóZ8ŽóœÎÌ6öÎW0_€„p>M×´ù;ÔíÛ`’“\ÊÄ¿ˆÈ{ qÎïà~‚êød÷½åM|U½ž p‘ø×s]SóŸ‹j[Û”U¸—‡KÇOxâ ˜zwHkåŠEÛ^Yþ:ê°|ŸKvé–ë|Ê"š€=¢sÇ™cUH *FÛî/9MÚ'FÜuåï~e®a’åÇŽékÐ/Bh@ç¨È'0üOC­Á;©Œõ„iØ({snD/ëÖ<úñ¥^›1=Éø ðM J±ÿâ{­¬ß†¶lÛh¸¤gî´)[0ù±„wê¸aºe£‚BG‚>´ÆßÐÇG¦wÇÕ¸QL Žß×’ghN×Б Š˜È× A5¯îƒ2 òqÚñt„·Ò "ñÓR ÄêËKëð¶õžïy=1†âõçG'»iº…r^ ³¢é°}¿lœkö§]÷¦p¡Ì»o>|—W6ZvòWCðúD×ô§ÀtžÅ|ÔÅçüÎ9ÐÉNËëùÿ|ÓÛ¡º‰7Æùõ¨c‡‘ßß­~P«Ë¬W_€°5Ã+òHÛh†…ÉÁ­Æ¹.^,ïÃÐà»f]'øWÛØ›Ê]þ±’e2óXÞ±aãèDŒB]…gr!&_ßå«™·†Wɘ2åP¼Ó 3:qΘ‡ÓèÃí.a2t5åýõîþÃâ5±Óª÷,kIï¢Ñþà™s°ÿÜ̧a¶¢.íH\éÓÜ;ló–Ï‘ö#ÀzAdóíÇÒÖ ^äU³ßB4' !pÏs;_󌛚?¥oÝÛõo(ìÒסŽ~ˆM°2æ>œøEyõÆ_|| TÖ€WiN'â çæ&Äý\¡ÆÉ_Ô$ðÀ´¹dP)ÔØù¨?½Fšt²Åþˆ·£ƒŠÆ€ø\ê|ýÅ×L`ÂV0nçÖèÔ© ÙSNG·Û_´ØµñUºyz:Ðp5ÁþXa6xôÆ­ìg/4Ã~÷ç¦Ö(Cþ¹.‡™¿¿®Ð¸€y‹× çïøÖ[o)6ÞöýójÚ4þ˺Fe‡ùÍWXÍb¸b—g#ß(¯~V®zÈe† eÞÍ8K;R6Ã\ÈîÀj@Ê\<LvÕS³i[!¨½ˆb[[ãÆ½Ž¿i¥ ̇à~q‡ÀUâ#ÚË2ëU”Ãñ2˜A€·¯›¸¤ÙË/Yóãïü›Õëi2¨ùÛÈ„»Ê–É_ü]#“ŒhÅÓ=°yÜ4ÉÀ»Žâb$GÁJ oÉ·áVUìš —ðÐŒ3À -x}HkÈGµ¨¿y:ªa‡ù ð{°‘5›Rhò±OüØŠ8ãƒkoYï¼"´üg.±JH[ñ}<(ùÅñšoà'*AfumIä¢Ü¨˜*†y½ÚB|`ü ³°›ý…ákL °<žç!"L›&t—+´YAïÚ˜zZ»·¨£µ¦@+@ów„—\t|Ñ‚ |Ь~BqÞZéþY¦¶.i´ýð#çfßxƒ-Ã8Û¾C=$IøíÍø!ŸÖC§(|'C‘¶K9b±ýEDòl®'ïY# <£sSÔ“\IE™Ø:ö‰}ĤgCØmbÆ_‘ãõ×_¯-Z:É7,ä¡­¾×‚ù=ÏåüšÎX©ƒV߸a'RøPæ½¼üZ¶‚U$Æ`¢ñƒznÇ3úq»È—ae‰þs\‰ë)-b{éùÁ¹Àýqã,ˆe jÍ3®wàÃë ¤q)FàÒ7£]î³°Çñx/²³£êh¾Oxö¼îk²ä7§•-“ßHK¹H#Z(ßó`{g®ÇCuüj”5××{8ëÒ&­¶ Ö`àø´ÝT7ɔ˘ßbŒT9S¢\7æ¡GN…)ÑŸG÷g‘ùÏë2ŠMÅä;¤û(„wLz,Ýy…ÒŒ>9Jûø ¤­ßT·‰ëaOv&¦Èòd=‡ùKµà£ëuÜ{=©Bï}`Œ‘?í5°?õsØ1È"@Ú+vL€ T€KCË Ýu\©Á¥Þ®´{´a„W÷¼ŽŽ"V—Êp*ÔÇ_#J SÑë;Z“RbœT² ¯´ËŸ¬)ɼ$®4íTA"méèÅÖ¾‰b6ZòQ…&v"ÖP¾>ÛêÉv˜0zcFRÒN«ÿ''N<æÛÀ«>MÌ²Þ öœ„BØè>îû7/%Éï<€`â²7úžüx3\(ónÆYÚ1زÑDc%icïÞ ÓÛÈyóm0ewÜ!g?–Mx:Zi!+p=ó_²‘„ù†Ìji/¾LYb³ù•~ä¨G¯·^4ÏG¹ÜgPdþ¶ieØ ?ks° á“X}ãM2å  ŽVÅÏårú‰YñůGί,þ(ž«ÈÎ;Ù´GÙmIùyÖ»æÝnû‚ ,(ôki™¹Âë!<‰nÙðCªûÁ¯¥SŠ×“ÀX¯iì»A$³’üjjÞí冩‚2Yó`l"%•1Æ5MÇ(—c½_t^Á:"EÅ¡HÕ¯PLf&`úç&ñÓÒ.A[Ð Rø'ÖÉ•1ޏOÀ܃ç0uw ‘'EÚ¿úOtðª5ñãh)K||^gÔ‡òƒ/]ÂwBy¤+‡b‡‚c3šx4>9 “CËw)‰X¥ˆÖ«GÍÞnå‡`L j °^µ¼9µZD@³ÉµŹîZ‹FaLÆ{ÔzÍzŽõ¾±¾®|bÚF¬»=îÑP<†„G“Ðîñx_+e% k4ÓhÚ‹äÚѱ‘ (bx²Iýò‹%¦»ZZÒêšG÷-— OaõÛ>ÿ‡ìŸüÈÉc\sêÓæ´­u¡'Ÿ›Ý–!êåF:Ó®ò¹mü¤ ~(.÷Bq Ïî¢Ò´Æ:κg.¥=ã'Öô"!}Ÿ•™OUÓzÒy¾Æ1ÿjº+ñO2ÏAŠiœ—í}qôŒ%Ìü|ÃYßѪ(“oúÆ ’ÖÙ֛äânßûô»ud…Çädh¶ý;E)Ðf{u£½Á’~†ù‰é;ÿ#A| áë´º‘ôÆ)q‹Íûæû¥£~ìƒöñ®ÔSÍët¤6 “Ng‘ :ó‚Õ|Åê/ØsZÒ£„ØAJÝ»€VG1ã ¶Ã_[Iu&0]ÉŸ´“ˆ,öÈÊç† I@—0ïçÒBa¢_* c–v¿2×3N\ˆ%çV S8+×sx5òñæ$îÅðk×,õèÕÐ"íƒ$³ ѹÖtŒP²½/£[а™ÌmÖu·Éñ Å0ñÍ;¼ËF¸¤5ÿçRÓ'a ð[Ì»è$É6»#¶ƒ3]ßK¢[4¸ß¼Š#i¬‘æñ«åOàñd 4}ð°g(Ò8 ßÁy¾éµ´½|‡¾l!Êz‰Ð=k<Ú~ÃK¦0ÂnóâY£+ÊÌùÓ“>3ÃÒ2xà3÷°}¶÷œ I÷˜²…1yøëƒýÔOu¨G{á|­.”GhíoÞyÈã/Ðôý-†¡±‡ÐÒQxr†àXì¿nc;—ž_¡‹„¼Sf´<­v]†üc7J± ªíM¸ÜÚÅÓ±±ÏCÓh—³ 3Ž“¨Xeª'ÛK£W¨G½k±Æ<- ·uœæ8tÄ’uƒò¤NÚÒ¬á‚9‡ý¼WSïÓ©¨/Pç*Ïžv D]|y|~KP7¨ÞüA°òzFŽz¨9~~A+õsFžGZQŽÛŽªY/áx™[éšúX¦¶¬î}мoÄ%ìÌ)ûŠm{`n#º¡>¿áoùÌJe†3` x rÕGY4Ù¾¥ýA(Åd ð8jü£Žk¡V~ aä!IèÚ,´µú²Æ ¢ÎCGøoêdþ‹Ž‚¶àNóÝL†üÆÚë…кB픑Éi|×ö,NE¼˜°Tøw:òt yùL‘¶ki#ësiñ{öÃØö[I¤pè ÇB@}KòÝ +E{ðùÖ_œ;Õ¥èèõ¶è UãC…6ÑÁòû5ä/šø7BÅ'¾šnlÄ1W‘ö3À)ü‰rÇê.N)ÍN¸þV^Þå/Í\“â-<;Ú=xc÷Mqý!¿W#?™X-¡£û%´ÜŸozÕžwdÈ&[1úòNÏÀýØÍ!~x´ÂÉ·6»œáJ*öQK¬ˆö-NÇ$Õé¨ë^’×Ì+íM°7f¶joŒ#²M¬:óNy$Sm÷áVªC4· å°G4Ýhþ 3”lÑEÓm‡5Ðw•·M| LBé6Ê9|¬[Œ¨·Ý_ýòM+Ël1žéÞ쮚Ík‹iXoC0&<¾<ý=.-­y.vW­ç™~ב4¢Jú£‘E5O´T¼²ž´Ù2çL›´»®¶Ã•DÉÁ«ˆ àUš“aL ˆð¡…=5Æáèô´ëÁíEwŠÎÈŽ>Ësô´¢ ½«°}qQŒ|Ƙ`L 2° Jd<Ψ30< ÃMI«0(Mø&XY¥O>,{ÆŽ 0&À˜@-"Àx-z˜\&Pа0†Þ–À…¶6Ÿî»¢™ó$8Ó®óêrLT<°™_ÊÅydL€ 0&(6A ”ûcL dŒíÎißB7Öç…™ÉVL˜Ú [,דºàÚ.,•6.݈ÍaØ1&À˜¨=X¯=Ï’KÂjZ»w§º+‡èÀ,«Âì(ªï‚†|—Еåzû¦e$$xjT¡8³L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ „”À=®™mÆ$¹Û‡4Ò0GŸœvf¼ëñfaN†£gL€ 0¼þɘ”ÀØ$wGÔgc÷Îó°¡ÐqF8)¢a]"ìŽq®)Øá32m„´Ã³ì¨ú³î䉑™KÎ`L vPjg±¸TL€ 0ðˆw¦\ á{.ä)b“„çJÅv–”Ê$]—=tÕ³:á¡ÔÁáÍEÅcw¹©º"&Jéx©â±pH&À˜¨Ö€W„‡aL N7{vtîîÃéRßg¯á,ׄƒV c\sê«êÏ„.Ú´qtïér ϳÞçs&À˜¨Ûìu»ø\z&À˜@ðòöŠ×…è¤(J¼¯ðM±=ë{4>É=f)ßíð®Ç¥g 1ÿ@êr{zªs¬oŠðû„ù®óÝÉ—Ó½xWz¬ðì‰4.†–¤¥bµM*ι)‰ßYÃ"Î0y@qˆ<Í#‚ÿRêoCß׿·Ìs%ý^Ì¿+­‡îÑÞD\Ió܉Sx]Ê—ç§8Ÿ5ý•—v‚3eÒ鳕kÌ0tD¾D>£ÓS’ΗȄyo¤3åM!”ÝóÝI÷š×øÈ˜¨ëØ¥®×.?`A€tyLNö¦OOZTZàŒTçR)d¦®‹s ü¬F¸»Æ»žll 3yæÌø=ñjºŸžîÐ={—áô¡ˆ!ËÞMz¬ªi_Ç?”:ˆüX܉0¹Â÷ûð·QQäD›´=‡ó®^U»Õâ/ÿTõÞ ÿݵúŠ)ÈŸˆt[šþI[Êø¿j‚ë‰&f¸Ñ®ÔÞˆwÊznÂC3À&ßsÍnˆ„k]ßl^ã#`L€ Áp®L€ 0à Í4 ¢e; ?º8¾ÀÓKЈ'S^‹ßÏ™ö Ãõz6õ]Ó·ï‰ÿ}ì¥ï³®¤_ ü=7ÒéÞ,4m&~ ¸†]$Ùlò’yÓŸøñN÷;Rè7éº>Õª†½ú͸þ~Æ”)‡L¿Öc@iKÛ—º¦)YjÎùû>…W=úÕø Y Á?Zí*\ú®çjGÎCYlºâ(õC…ü±cL€ Ô5¬¯kOœË˘@å è²%LFö”‘þ¯”¢5ùƒÉÆß†—Â<ä–bᤸ~~˜ë~p]‡ñÆMøÿ«Eø6¼C›¾'ýÈþܸPð×Ï›žT(|Óe©È— î0Ê™ 8ß%$§-t½3> Aß¼n=’vFÊ”ßß]Rxša!x_Œ m÷×B$€ç;MD~)Œy‰L€ 0&Àp®L€ 0à H±‚¬!X—æúöB?RBøÕÒG¹Ýmç9™÷¥¦¶ÌÎÖÃtäÓ´á] T׃íôŸæ5:B›M&Šw­5¾‘®Nꦖܼ"Z+‰_ïÐR·‘Æ—Ð „¿6Ú;÷>aQz¡Ïâ'¦x#Æ:ÞõHÝãék“âMêvþãïq¦t~Ƽ ù ¤ÉÚïâ˜ù`L€MP¸0&À‚%!ó–WA¨…R»h¡o<¸Ý à/ÌëѶ r=‡gk9òF\û¿c9ú äU%Ö†‰Šù5ã÷O˜(ùšyÍ8Â#tÛB‘{­×‘~–õ7»\RÃË—!Pv¹`òcs-Ó³l84ã/ >ÜëëßühÚ(ñ"îÉ<[ͪK±¿•½?ý½J¦gý‘<)®„­ûËÙêÑ>`0ËŒŸL€ 0&O€mÀ¹&0&À‚$ H}©¦‹ñÉ3®DÐýÇài^ ¦*Ê7æý§]÷†`üì \×ÄM«?œ;uêÓL[6B>6Ïô¢y­"G‡ÝöržÇ›´CÛp‘"6¨ÐX7—64ð¥»@ÓVê‰/½ÇtyTÍé-÷•0?ù˜Ö§˜¡¹ÿLhòâ,ýØf”Sq8¢X^:r¾Ã˜@%À6àuôÁs±™¨8ÖöþdåzØA»ýmåN+è^= ¦$]tr7_¡÷%h¦OKHrc£N÷¼hÍ ò¯ _D»lZ¯{>Ç•¸Ú÷åÈÇušWŽóŸÒ]‰ÅÌZ|ã 4m2Ÿ2~­"¼@ø>&4ïšqÙtù!V{9_ê†ý÷oϺ&ï2ïñ‘ 0&Àò °œk`L H¤íÅ’€£„W¼#<9¿B«=¦¿ØX§Ä+úd©Y“a™ÒÚák|M>ÚØ_îð¤fBÈ~ï®Á½»~^Ì&»¾#UõÜæ‘bq‚3m ¦ê`ùÂÜÎ0ÿ>öã6Læt‘Ý—éþÍcéÁƒJ[ùRÚm(ƒÛ8¶ÐÌ&Æ÷ LOêAÿòÅÒã;L€ 0ºK€5àu÷ÙsÉ™¨ŒéI_ÃãdD±A›½ Ëñ-‡]øÃ°‘^)ê)½i-pß$È>ë{¿;r ûš¯€NK*1ò4Ä÷‡®k¯jzîzøý\ÚD߸Êûk¯ÿ&á†økgû_yþƒI[QôE01¡‰¡ Ÿœ8ñ˜7mLûð%tÏ&e¡`nÞç#`L€ ÐŒvL€ 0&PiãÒÒšcÃ婤¤Ý•ެ ‚ ؾ\³®„ùì »uÛã+¬‡*ñTgÚþòÃט`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜`L€ 0&À˜¨Z²*’<£ŸWóAZu©·A¢ ª"]Nƒ 0àèB‘ºÜP[lŠíó¹)SWCÍõÍíTÍ}vœs&À˜@eTGß6Üåz!&Ó“yú}Bí¤šÐånà;èÑÊ€â°L€ „‡¡úÀÛà½m©ëBRlÇ{ûT[GÛg\®;r“jõÅÊíTõ±ç”™`‘B :ú¾°àñΔ‹u>:ðRÊ…ø{M‰U>™;uêHÍù`L t£gÌ8NËÖ.×uýü]‚è­ð=2ÃüEé¡jÖn§jÖóâÜ2&ÀÂM *û¾ àñÉîñº&—Rÿš´{ÓSK ŒãgL |’ÜçkB<N”Šx #Å9+|©UMÌÜNU gN… 0&PS „»ï³…L~§¦?)…|G8Z\™‘2qc(ã縘¨z«¾ûjËiƒoxYjÙ'@>ñ´ƒ­ZòÕŠªÏIhRäv*49&À˜@m&î¾/dpÎÕuù)"|7ÝtÌN`RÃŽ 0ÚB·Lp¦¾‰{^ïËj¢9 ·Sµ¥6r9˜`UC \}ŸŠìÓD&Ä3ŸÌN„£ù¾CA•ã`‘EÀx¯ñ~#Wào~Á{Y™,#7ÜN•‡o1&À˜€_áêûB"€Ój'Æ„KØ|g¸²ý–€/2&Pã ÐûFc½ïÆ*G5¨DÜNÕ ‡ÅYeL€ Dpô}!Ài©A|!,ä —T[8+L Lè=§÷ÝXb4Li„%Zn§Â‚•#eL€ Ô¡îû*-€Óæùë|Ë×êÂà22& ð×è½7Þÿ„Û©ð8‹L€ 0'ʾ¯Ò8ípI›ìÐ:ßγǘ@ˆÐûNï}Á·!Š5|Ñp;>¶3`L ®eßWiÐ;Ò—¼ÉN]©~\N& „ñ¾ã½7Þÿš„Û©šñœ8—L€ 0ˆ%ʾÏ^ÙRbkù6ˆcGeãáðL€ Ô,x÷é½§÷?â·Sÿˆ8ƒL€ 0A T}_¥p¬ûÝÄŽÖjœI&ÀBFï>½÷ôþG¼ãv*âg 0&P#„ªï«´^#hUq&'<ñD½¬ýYm6ŠÙõø¤IYUœ|X’™ä¾ ë¼OHOqöÃ$c“¥‘Δ¥Š”¯âÚ¼°$‘ŽLN½SêÚ(l:sFd‡³À˜@¨myâ«–¤ÐWý H=#Ýü\µd€e`<@PxKxÈ=T×ôîË:›ü:˜ëÅÎ{ÂFþ§(‡#ù׃a5Õq¹tÅå’Z y Ö„îØ ª¯5¾OÁïÅÖká8•œv.&Ñ-R,ËHqžg~XÓB£;ù9##ÕÙÏz½Òçºh‰8Š•»ÒqrL€ D4ênË«N¨ûŽx§{ú‹ m6å’yÓ“>÷-Ï}©©-³³µ]ŠM^•>Ýù‘ïýJý–z](­*fU@ “0« ›‘ŸD|rê;߅jXU¤¸Óæg*R®K¹¹oÔLLÞîRdªî-ã\3Û…;j‹_ç&$»ÇT[úœ0`µž@$´åU 9Á™rW¦ššŽ45¯>ïÇ‹ GÜ'¨éX‚'mÉ•º¦½a{V[[Ò$‹úGDÿn~ΤTz£’gôõjjí¾©èR,Ðu9cL’ûÃgSÛJ§Áw˜`Áˆ„¶<ø\W.”FW!†½•‹Å_hù&«]zäPN*îŽ÷烯1ºL€ð<}Í+œRÈímíݧZ„ïRcŽw¥ÇêÞ½)0¹» v‚‰Êz˜U,hmO|Ì>>É};°Ëd}G¼8¢>Â3H¢¿+BOžçNþŠåL©éêt:ÏõxÁìŃ8ÿ…9ÆÂ\ÜÆî¼-Ó›ú0¬·oBoÝ¢Q‹§ï½77мPÜ8ŠOxöÎD£~1LBZBh^m“ŠsnJâwføòòdúó=:ìö$Ç;HÅ*x¸w…ï}¿ƒ)4_wë°÷»î0sÿ ùOG9òüÅ û÷ ñ’À³/üì…ÿÚØÛ%º\wäøóÏטˆ|aoËíõÆêjÎl)ôAh?²Ðv|#íÍïâH¬îÉy mÎ@´ù ¥.—Û¶ñϺ¦n4©¡]_a“¶Iš¦µƒŸû Œè…vg#vÅz¦y³Mc\sê{<–ax; ;÷½i^§ã¨äÔ^MŸ0?ÕyJ|jjk‘­Ï×uqÒ=ˆøóç¹è2}Ç[ä?öœüùs°ÃÞ¬é2yŸœö¿Œ”Äüùó½629m€Ô´d°é¾Ëƒ¼­´‹¨)Ϻ\kõ[ÿ§p­?þ”¾Ìa¾OUsK´Ù– .B†>òB]èõöè–§»“ógúhÍŸ3` ° J°Ä|ü»\ ¢ð’’-ô|—kx‰ßÇ» ÿºgïr¡‰Û¨áTù©ˆa/7-Ó“úv1ÿR6CÜWˆ£žoá׿SÑÌ@ƒÙÌ+Ä'÷8S:“¯´­ÇõFX©Ï†é‹[—ʬü߲¦Í‚À âgp}2 ßAåň¬ìñéé”m|Ý"P&t÷c·ÄXUÓ¾Ž(uPah”«´<úñs¢%[*r<ÆËi˜Ø—b—‚)> &cc>:Å_›¸ /Æ|Ó#ÿúÅ"Å„$÷ ÈÿB4Ò6<“ñðû.ÌT3y3*_Xü› ÔUÒ–«Ç¾D»±U Û8´ã‹ð³®îyÂ÷bx¹R*“pü/„ɳUU}›l³-øNôêÞÿÓ¤ž„k/›¸ƒÚ+˜>>Ÿì&-³ál‹0zoMMÍkæQÓEsºG¿Ræ¡ýz} ™GþJýýAèý™îÜž“g?N2Vvh>‚ôZ¡iÿ%¾~¼»4*É}‹Ô½_¡üPˆûðçD«"oeBrš1·ŠŒw=ÙX?¦­Dvú—Ê=ð»Sõä-LJ ŠTä-Úõóê3]hõ°¸@"âLFÚÐ÷vbỈ'Ÿ…ŽkÀ+Ér§Xßa´BBpn‡ºa,Ó“ðE’å‹þ½QδŸÐ¸~”àL»,Ýø©éXØ”Š/ðDóZ‚Ëý¥æ?y„¼מ˜Ÿ’ø-Vëh À½Ñö¨žv=¸ÝôKGä¯4.½Û8N8×ú‘l^¬qú;×·ï‰ÔúØJßg]I¿øyn¤Ó½ ðLü.\I¤´<ù‹×¼–§æÕÃj$¯Cû| :Yñ®Ç¿Èp=ð¯yß÷hùây¤‘~Ô3á_žïvÞmƃFþ­,O:Ë"7nöìèÜ=‡Ô结¯.¸ó"ì(Wâ#êyßçW’ϘˆdUÑ–c¤t´ÏiÞF[‹¶ä?ŸE?Ö¼aðˆ&ô”êcÇãÚ ·âìõ;ÎrM8Xpí´¯*:F¹{jžkÒ‹ß2Oç$&ÐúN×…üg^JR¾§ T0í¹¿„ÐçÔËHHðŒv¹ïöªú²LÏú)ð7ÝŸ_ºF¶â‡æ>†¼¼‰v¸PÁâr}ýÊuéRhþg¡=‹„a´Ë5-¥Œê™ž2ÙäóþÈd÷ôƒ$´&h9 ôcTSìokwÞa‰~¾0">a!&€÷ž]¥xe oîÅ£_‡/ôÅáÛ6ϱü_ß7úÆ£Ûm/[¯Í›–´ éý«áËÜz½Ìs]:­Âw¾ßàóRVùæ-òW‹ðmxG§m±èGÂk±ð~óTÌG±RDõUÚ£Ñ×ê±'‹y(ñ#°òÉ£Ú¹ècx7Ã…ÑÉIñ±õZÞ¿Gú£h-ù Öëz=…ÊÅ8âbǘ@Í#Pm9FôÄSÃŒP·Éb£Ÿ}3®K‘ÛÞô[püÔ"|—vù,õ4Oî¿•út{^25£½žër®Àª$s YOJp¥õ*é-ÿÊ‘#yç ­ÑÓ(m¡s¹©è3ç u=cÌÃt¦hÿ¯@øuº»Pø6üGÙ”W œZŒJ¬¡ÑåžÔñúY9é ’‡œW²J"ÅÑÆÒ‚ø:oHTxÁ‡ ýn)~ñ%¯wõ½×0αÍz4Xæé¾ñËÒ3ÃÙÇ™ió¬+‚ÏKa`?'h<»@Ø®­ÎŸÖÛX›à·âÝŸEÉFóž¿<™÷Ê:f¸¦l…Æ9é³q| ë½.$&T « ´|ºðv4ÂÙ¦&¥04òÛÐ!:Ø&v¡°}åœaÞÀp(}„iH3?.󙨪¢-×í2­0ð!Ø {B›f7úóVÑ:ìõ Iómã¥Üjú1±qQÌ!¿!m{‚mÏÍüø;:ìUuÿP]õÂEï@˜ƒߺNÚ~asDöæ]©(ëu4º^U¥þñü¡¬òó¾yŒj³-o_ñ­7-FžßÆh@*Úð´že±´mFLœ2÷éÄÄ0LP5sÌǺLÀøB­Ë*]v»h>e Á¹Ô¸uÝã÷ž.hòdq-1<>öÀÙ~ýqqδ1Å[%3ly1ƒù;B›‡‘¿U˜tùh±?EN±ôjŒ,Ö•š'‘û\kmwB#"W@ìG“Ž |—´´ëVÖJ E¯Åxs}’º&ŠqèCù¼?»XQfØOÞ»Áù¾qðo&Àj*hËÓ§ÅóGÂ+òT×}¯AéR¢ÏöäåÕ')dtÝági¿ƒmÏK‹‡®?ë{TW”QnÏÞ©ºÇÂø<¯4ÿ2Z-Ù–ÛòÛqØ{›ýc Úüíõ&ä€>iŠ\0倉£SÄÊÎ0Gy<Ççf{7“]zQl|ÆBG€5à•dyA¯ã·}¹f=´¤úݘìñ8Ù¼•¥ÔWa8Í¿¦Bê €¯/3|(o†8/h´6¢±;6Ïôb(³é/.²Ñ›üÈÝyºçgz0 1T@h2­.ÀòAk¿Ú#!s´¶8*…”­ðyUxIÑ•ÐÃaW–Ïs%“• oò `5–@MhË1ê×Úð±lý|ºÉ| ½Ð§Óm±)¬ÒÏ|'eß‘Bó–ï1ÔíùüéIŸAÃü:ZÓ4o®¾È7=MØVa ¡e˸w Ø}Õ¸†B*\§²R{]Ì~øñæh¯mÆçHÁ`Ë‘‘”´A§ŒsÍNËõ~0ŸÂÒ·JšpKš0  ”øš:†:`øðá^̘~ˆ†¹Äö½Óaná#úÒåðp1&b9Á"ŸüÈÉМ‚Q¾*ºÌ™¤FCäy½íâ¼ \4{ý¢±Inÿg,0sR¦üöÀòc,°÷.*ÀòÙu ?´& ËO¹ü´ Š®àÌîø~³tŽ §ì˜¨-"§-/(&6^仚ÚÝdþ¦8lÆ2¤m†RŠm 5¦‚öìbë5:ǤLj£D¿Žö<:Ö6Š’\¡j˜¿³A‹‹i·j|h°Õ[-—óOuíV˜nk-ºæ›§ mÇ‹}7ùѼyú†­h9žvÝ{Yœ¾½é.ïß'úÆË¿™@e °¼²¾•-ñåL-õj¬Ì1%!9õ4¬µúšW(µÆXý“4•XÌp§U@D£ÆÑÿ‡™ÞWêê±…Xún¼ÝµQÓ<§h^õY4šµ¶ûߊd颓».]´fÝ~,¥w?àû‡Ã뻊o¼!ÏK}Gª<ê¹ ·.Æj S ­®ÖEng”ÿLt6 ï¹|óPÙß­mÝS3Õu×£‚½˜F:ÐòÍIun½ß+øx˜ŠÕdvDÛì ó¼zÓjêýÈ_±¥¼h•gj Œ½gàùå)ñÒÍÖUítL4:SHÛœŒ”)¿U¶\ž 0ª' my9¥ŽÁj" °ñÚÃv›²+C]‰ÕRî†ùºuÍpÄñ%Ý˰<áxÅf{_÷ÊFØíòAø;Î7~ï`•G°×Z}廸œ(Û“®‰ûEÚs²§F[;Zø—óó8ZíïèG_FÛº7JêoyET”&=wkš~-Lün3µÐX#}¦êñÞ~øPî»c\©*¾ǣzÏÁ>ÓÑ7ó °Hóv2qÁ„Ø%q"fOŽ7çDUצbtõßzÇEÿeæ“L TX’d¥¥®ÆD‘‘øZnÆ"Chê†ÂFù~4ÆäJêñI“²êÅÊ+pº ßOîN¯¦½/ÿŸqJšñ]‘,‘ööÇ“g¯<¡oƆ<åš²„:/S¦Rbäi†ÿ@Cøª¦ç®‡Vÿs¬ì2±"e $ŒÑ ëòn_m … ¦|mmGãCá}ÏÉõäeꚺqÅçß|ÐÇ6™¸ ¬‡a¢Ô^ö††ŸG§ÒOê 48온‰"¡-/‹›JmpãÕ¼ß픳ÐN½„MÀŠÈ)õÄ}P//…0û<÷ð_@IDAT¤×£nÒ4õ;è›÷*Âv‡oüv[ÔÔiº¶@?â9˜åÉF{*D¸Úsô•¯ ÏŸûæƒ~ÏOIzïhhžoÌÕåߪž÷ÚÖë…M^—ž’ôª†>60¯h0ú—.öK> /ö¿°Ý„r¯6ýÑ1Ðr@øö¢ßJÕ=ÞµG=YûTÍ»#qŠ]^þäĉŅzk|Î*H èó³‚À¦ë íæ@:²ËßÀÀ¶sû–¢Õ޲vF¤!Á½žÔöNé¾Ý Co”kF'E4ü—†"2ÔyY°`íË5ë:‰zJö…ݺí eù-“Õ_ å£å§v‰íÈ4øàkšµý¼&½û5)¯µ½ÞÔ¤òÑ&.ÕÙ–[Y¡Sûò4’HíÙ.ñH‡Vâ¬íe)mh‚º¢îoÞÌîÜbYÛÚmáùä™3dö4¡Á‹'ÕÑžMKkŠIïÚÜ©S‹ÛƒûdŽÌ9ã„¢Z{Ÿ{¾?Ë+q=àHk®zd#EÔße˜¡øF¿ë<ˆéO(#ffêüSaL ¨Iï~MÊkªB\Ô  IHv?Döʘ@„ª?a”0<Ž’ 0&À˜`L€ ”F€ðÒÈðu&À˜`L€ 0&¼ J r”L€ 0&ÀBI@¶[ìºmC(ã丘¨>,€W{N™ 0&À˜@@æ»? È#{bL F`”ñ˜8“L€ 0&À˜`µ… àµåIr9˜`L€ 0&ÀjÀkÄcâL2&À˜`L€ Ô,€×–'Éå`L€ 0&À˜¨X¯‰3ɘ`L€ 0&P[°^[ž$—ƒ 0&À˜`L F`¼F<&Î$`L€ 0&À˜@m!Àxmy’\&À˜`L€ 0A ÆnÄ3:yF?¯æÊu©·‘B4¨ÄC˜I]ˆ#R—;„®oµÙì ç¦L]Âè«-*~¶¡A_[ëGhèTM,F]öª—);ÔùvJˆ-6Åöymi§ª¦q*‘J€ßíÐ<™Â~ª¶5Jw¹^ˆÉôdÞ£(r¢ª©­¥”zƒØ­qÃXãpÔ9m~ŽÇ£<œ­ÉÎQÀܺSU½³æÔúÉ''æàõ@Ý®ŽŸmèŸSmª¡§¶å„ OÄŽ9:În·7Ú)…Û)K;•J픦éO´u´}ÆåºƒÚ)vL &àw; OɧŸªSíCÀã)ïòîx2eÛ^]Úê§ŸØEœÜµ­Œ‰¶…¡NÔ”(Žìœ\ñÛÆLñÓÿ´ú}ãö™Çê»oÄ$çÈsŽ‚hø‹hAœŸmت[­¨a£Úˆ1'¼wCŽÅ›¯h²M¯.m¸Êgì¯>†ö|<Þý;3ÜÉ_„öQplL ¤øÝ)ΑÕÙö¡ÒÂk¿ó/A8W-ùúE:†Ã¡‘ž ëò¥¶-×9l ré¹½e»Ç ‡½Æ|?„KaœÄxœqRyB§VbóÎc³r<7÷9÷¼¬_–~ûcLjÂùÙ>ư„«~TÅ»*(aÎ+u ö;§<4ÁîˆzíT·S%ŸœŸzw8+÷–Ó :‚þcEÉ|… T;~·«èÔ¤ö!Tý‰ñåQE|+’ŒÌÐħõì¤L¾ý [·­*O C|q…í´ž¥ÍæxtÄdçDÞ?úØ¢/ùHqül«áIÔ úQ t‚N’Þ'z¯ôžÑûFï·Sås,ª‡]OP;P‘Ô>•_öQ› ð»]O·®´‘,€Ë»¦¸†@óý:5ýîad”£Ò ûj¬RU—4q"^ÄÍáˆN»éžû/Eê$„ÓóŽ„NŽŸmÕU‡)Õ€úQ"ÏxÞ#zŸô~Ñ{ÆíTpOÉZ©§ö1DBû\AØwm#Àïv<ѺÐ>D¬ïªçp8žkײ±øÏ•ý¹Q®À AÜÚ4o,6n<÷¤“Îl„(L!¼±….?Ûб¬LL‘Z?*S¦* kßô^ÑûÕ®åq:·S£OÜ`¶#¨½§¶¡b±p(&2ün‡ eå#ªÍíC¤ àŠ·‰çÕ´67\|¦¾„ØO€¸ÝtÉYЦ‹V½/01Dã ç«ó¹ó³ þQ†%D„Ö°”5Ä‘ÒûCïQ4½Wô~q;UqÂTor¦Ú{j÷Su¶O/‡¬ øÝް§X›Û‡HlèHÛm³;lãOêÚN'[ v'@üˆc\ƒ£ i—¢ðW]öàül+þ(Ã2ÂêGXÊâH:Œ8é=ªGï·S•'lÖCj÷[uµO•/ÇP“ ð»¡O¯¶¶‘(€+·ÞûàéX'¶–¤‚]% pl~á57œ‰¨ªS Î϶’Ï2Á#¨~„£x¡Ž³PCVð>5çv*4ˆ‰#µûÔþ#ÆHì›BSPŽ%R ð»©OùªíC¤5rƨ#:jm²ƒu¾«½:xTM–¾‚­ÁN£ÚóYVˆ#ñlÒ¢å`ø‹Á_uhÁ#îٖŬ.Ý‹úQ[5d1ô>EJ;Uà•—G³Rû¿¬/ß%~·CI3 qÕÆö!ÒÒ6_‚N´Ãeun²³cïñö—+Åú-»„Í&EÇVÍÄ]WŸ/5ˆ5îϾ_#¾þi­8šƒûŠèÚ¾¥¸åÒ³E‹& ª÷ÞW«Äªµ›…{ìµa¨ŠÁE Ž¢A½hý°ÝÞ!É …vŸËÃŸŠ¿ªúzˆ˜g‹2³³ˆúaÉQÄžšu˜&3×Sð>Ñ{~t]% õíþþÜÜNˆŠðªlŸ*™{^à ð»á°6¶‘&€“FÞ&ÙÛËW«vþ“ïV‹<*¦Þy…È:–K›ÛÂ7ÕÑ¿ûU|¾ü7qÝàÓDïîÄ¡£ÇÄ‚/~¿ò™p¹VD9" «ÅÉ]{í-}SNB81¦2«ÂE̳­ŠÂÖ´4" ~Ôdùu8)Ææ°· n5!ã5%Ôîï?t¸5òKxU¶O5ç3<øÝׯZÛÚªt‘ä(?vEÈ1Gµvl›vü+ztj-Úb‡Éî[‰‹Ï:ÉàD&)_þð‡8ÿÔbÐé½DÓFõE—¶ÍÅÝל/ŽdKÝI< óBUw;Uá’Dh@âIí?²WÕíS„álUñn“9,É •q‡ªz+Eµ…­míC$©jIà6†t¡W«ðMµ«O÷öÐrÿ.¢¢ìbÐi= ·½ÿ÷àQC3Þ³3)iŠ\³Æ Dóãˆ{]Œ°3¼wÄÕ¾MKºæG, iæ) ú³…òÙ’™D ÃH„ùD¹ž÷ö×¢Al´¸å²sèg™î¯Í;E§ÖÍDL4Y„Ïmß³_|ôí/âÒsz“=~±„>Zò‹ðj^‘píÅ®Wõj¬U]ÔŠ¤WX‡˜Úͨ^‰‹Ã”A  ¨Êö©ŒÜð­:@ ,ï¶Ù?¿zÑQ¢eÓFb@¿ÄÙ½»"õgªº}÷áþïØÑúrÑÊ=rÛwïû{ûCo×¢‰¸zЩ¢W—¶"iÎ;bßÁ#èW„hT?NôîÖN`YTÃ4¶0!ËÉó|'~úã1ñÖK å¢åVXNCÝÇÖ¦ö!’pzøôjƒ8X\B Kµ(;Òë/:CÄÖ‹ï½Ê°õ¾qÈYâå‡anB®~,YrwqõbÄ‘ìüûÅïDÊ/ãÆž9unæoUe.,Ï–ìïÉ\謓Ž ë·‡‡êõBXÿJLq…hMûU»cèyáÿ ¨hÕZ?Êa5{ʯÃùïÞ£êWT3ð$ŸßþWuûž²p¬5…@XÞm˜Lˆq7\$²°XÃÚvˆW?ù^ìÞwÂs¿€¹p=ûE‚„0¹i;m²gºsOé&Î;õ±aÛnñîâŸÄ ½ï׳“y»ð˜›§ŠÕë·ˆv-›ˆÿ'ìxXúØZÔ>D’n~…J ‘T»NZÊ+Îë#úõè$Þø|…Èx÷k1-þjѸAœQ™d‘ uqw4;WôDÅTW ±3…ïBÞÈoUiÀCþlO„ æïáCéö+ûûE¿|ÍFñéÒ5Æ„Ù^]Ú“eív›xú/EN®G<ùúç°{RÄåç"Á¼è¡‘WÚ„·¿üIüò×1yÄeÐ,ÄŠ¯W®5-ÒLìB#ú¿Ï–cnÀ>q&æ^q~qZ¯ÎFú¯ã:5pû0ZòÝ/ëÅØ—Èןÿ`Œ–нw¯4&öÞzù9h·‰oWýeh1¾ÀL 4'1s>WrÛví 1øO4æ4×ÀŽö­šŠQ× *‘F°ª±~›Õêðo}_Œwˆ5àáy í¿•w¸Û§ð„c­)¬u-¤ï¶]±f¬¢;öä …Ñgè‹öë)H8Ä}ùãZüõn,AJAr-š•V‘k#:µifü­A²}ž?ü×õ[Ño8ÄeýO1>°Q_¡¦¼¬¾…L$_ýô{±m÷>c¡‰›.9Û0½%s'4ð×]x†øìûÕâÀá,Cf í¼ }ªo›zÏuE™®àYmjè«/’œù"DLžZã+óÎ¡ç ¯W3&b6mg˜+üñ÷öbyܳÿ°Ø{à°èØ&ȨØÍÈúAÏÜäLǪrfš!M€ë/¥ê+qbQÕfŠ„èɰó%ÙŽÖÆJ(VGÂÏ¥çœlüeãëðù—à‹ð&`æ ]×\ÐOÐ_ pUÁ=ìi©ÐØÕѤÙ.mi¹ó|G6ÔÇAè†H= áþüûßÀ=:·ݰÁ­óþ뺭˜ÅÞаۣÕnÈí‚–œ„tr»1rÒñE;·’†Á×ÑŽ©Ô@¾ÿÍJâË`·}¯—Âߥ­% VQ™‹‰£´AĜܵ}a˜0„ýÙ…)ß¡Ž6â9„eÂS¨)_Äs¾H"ÂTY[¾fƒ¡}nß2_Á‡v0ùX»i'LF¼Æ¼¿0dVRÞÄN2?!;ñžè×È‘òŠV£QÙs0‰³´¾%&:_þóņvÛš¾©+­¿²ú Óy•=»p俸dŽ‚‹“`â/<_ŸÁe…4–Xì×Gøöƒì²F^3šîOŬ׾“o¿,èÕ8|ã óïÆ…vàaN®0ú°?Û+Ï?“$ÿk6lÃpY7#aZ¹fÌ…ÿø'®u¹y±¶ú´s)íZJSdBÔ“XH3M“hi¦ºöqCÎ>Ùxþ´Mj9›.‘#m7MʤI’Ã.8Mü›=í8Ë?•çúœÐZƒXñèKŸ¦MçaÈ.÷v_í ›óóúž€¼Ù°–É Ô]töIâ§?7‰ùï~cLž¬™cÇÞC)kR¸9`yqÑü4š#E“õÉ\Ät´ßÉOl2ðÒúRhѪoŸ.[ùV§‹q±FŸiU8™ñùýõ±ÔçVÎÕžö!mÀ+÷lª!4iÌw_%¦% õ»Â¥â|ü½µè'ÌÈ¥[ÆäßÒÒ y4јVvº“…ɶ³u³üÉÄeMÈ2"æL€ ΩÏ}(^úx)VÎ:lŒz’b'G¦¯÷`)ÃX‰dÆó ç³ïÕ4ÿ'P÷#„l’Shò¥ÕQ›ñ×f2ÓÌ6Vkñ×·yJµ!ô4Ö¿÷ÑWÐgþ`XXãòwî¯õ篮^‹4 x} Œ†…qVåôgâCËõ™Kö™y¹6ûôG“Ph&7M¤5Ý CÎ,0’ÆÐ ]÷5W¡%éÏêh™Áä‘C ¡6ëG½¿%O?± ¤.…QЗôGÎêß×åu^ÒÃßj,!õÕOŠ4h6I;JÚüôw¾†ƒ¡Ñ7<ñ¿ZC€>(Íͤþ=˜…p~6:=Z]€Ìã6bäfLáÌ!eë¤âVÂ2-u6øŒžF]é s%Ú ê¯M»Œ ¾ª´4hÂ1­npïMÁ6´È´ÊœEæS´ÙÍA˜õÚçÆ„,úxeǘ@>@ÌPýù¡ÅÌ6ßdyR×¶X@`˜a« ³¡etM—:öZó´Ô#Í=ó7ÿŒF…鯼¾…æ¸M= +å ý´±P¾“%ò:føàbùð×ÇóP‡°ÄX‡~]+:­•êÏUÆ~-Ü;húæWÃV0‘‡lïȬ`3†É4]í#@C̦£!kÚ‚šž;™DÝ|éÙÆÚ÷K~^/ÈôjÐé½L¯ÅŽ4Úó†Žwc¾‚¹v0-?fºÒÒ õ|ÉY'WÑï²&d±N„Ø1ð¨L_U^®é[Šïòb+~?œù.žRÍúÅxÍz^œÛ:L€L\h“vþÜw舱)iP‚µ#¯ÃkTÑËšB£<§õê„õæWcøaÞ²DÙHKNöß´tæ$ÌM¡ ÉO½YÌ_ii˜Kg’ànN4¦€eMÈ*1ÿ`L Æྥz à!àN›·ü¼n‹±´#i]wd6õ &³ ó1aK¨nGl´ÿ̸nW.=¨õ"É…\ÃyuovŽÇC¿+»giAŒù‡ƒX?™m«LŽv[µv 4ªšažBiú³ÙJ&KBû럭oßm¬*p;\™Ku‘á_›fS¸ix–¶~%Ó—²ÖâM¸vxí³ïÅôùï‹Ó{uÁÆg‡e­Ýœ<®ªê13¯–c!w˵ˆ<¥5Éôççµ› 7Ù”ù¯8† iº`”Âtª×[ª¹ÐêõÛÄ·0=êmãi+ù<§§õ(ܼ§,s!ú¢ÿÄ|æ·Ã¤©=v)#[ÚÒL†(ÏNLV6ø4ô‰Öð0ƒzù“¥âБcÆrrtŸ6X¡‰¿ °¬ Òd:pÖw¾6¼äÊÚ:ÜŸÙÔÉ(#»(ñž`=ícáh§B–ãµShó!ÏÏ î5°Xœe&À˜@ ‘¦72¨y¼{Ê yû|Íc·Ãö-›Bëü/„©ß°¡ÅY"; ‚f¸’²·qyñŠm»ö ÷˜ë°íüPlËœ/lçÊÿ¿ç@þ¶â4i‰&HµmÞÄØ±ŽÖâ%­úãn£¯+¾KT§6ÍDâW‰;‡ž/VoØfiÖµvŸ¸ÿ&aþÒ½½5¹ ÎÎÒ=¹¹5Þ>ád࿬ÛjŒT€Ukÿ§ÂTȪ˧ыÒÌ…ò<ЙbÅoÿˆë.<4Å:Ê?;ZR|¥™ ѽ¾YeÞ3Æ]ÝÛÐ%A+C+Ëdˆv|ãó è÷çcKïï~Y/f½¾sºŠÁõXŒyÿ`çArK~Þ ~Û˜i¬@qåù}Å»‹W[ÞÓ½²¶'÷g6EaØ…€''÷@8Ú©ðå8òc¦vÊ«ªùC‡‘Ÿ]Î!`L Â"Q'AqÇ‘c¹2«—TÆ©šBó~ñ;š×>ýÞt.Çöß4©hÿ¡|møq°'ÓŽï×lÌOª@ìo…e 7ïØkf´å7í ×°~,ÌFC€#M¸¯ûêǵ†°D;Ï‘yI·-a®–ºožÇkLö<šcØ“Mz44²]Ú¶0Ì^>E<”7ò÷»¹‹oÚeý&ŽÄóXvÖ¿ðG%4ÿÊ ‘÷ºƒ©GõŠõ[vù[ mx?lHB¦)VWž¹ÐP2êÙQ pªðz5˜µäÇWš¹ŽnËnÃT‰&ÞÒ³ÍÎÉ3´Ö˜ ‘Ö›6Ì¡íçIÓÝ«s[h·{ÚmÚ¡ÓLÿ÷Û í|˦ 0zÒRЖâ?ÿUdVbnÞg÷îf˜2ц+¥™MY™ðyH˜ïŽNïS(Ú©äªDb¶SÔþ£8Å_èZP>.`LÀJ ’LP(_Fç¶{ûÖ¥ ›6½4gžÔÅšß ÎÂL$õ¹Eƒ¸z‚êQ×]`,ßE‘œÜµ­!L=œþ¾ˆ‰C±j ñ›n@¿â Wˆñ¿&þsŹb4ä/~¸TÜ÷Ø«ÆöÌžq¢Øs È&›¶y&³„‡æ¾gß$蛻̕¶¯ŸbWÃ>X"07 Á{ v¸#/”kíG˜BÈmÖýŠòiø+"ÌòÖ”#­dCf(« xÓ9 Ï=:µ6ÌIÌ2b.DšorõãòmÇs¡'W–¹P/¬„óõʵƒnK~^'Î*XS¾¬í¹›˜;µÅ92+‰Â’nm[gü¦q1ØfÜ£sÖoÝ3§=‚ìÚM·»_š®´­Ãy(“PXÖ÷F£÷©mçÿgï;À£:®¶ÏÝ"iÕiBtMôÞ1ŒMqÃ׸$v‚cvâ8¶ó;ù'±“8ŽK;q¾8ÅvŒ 6`0ÕD7EH€ Eˆ¢^·Üÿ¼³šÕJHBewµ+Íyž»·Oyïݹïœ9sNÿ»[ÚNyµÄ”¸l§Ðþs±%ÖTUT…€B@!Ðxü‰€ËWOøjÙ¡#G_Ú›šÙ‘ x=F! W!º±Ô' B?aï'ÐnÃÝ á¥ÀÏ.³–±öXNð{é‰;„FÜÝõ ®ŸÀÑè°@îºak8kúÜmÈï¯/v¿Í ¥H„<ék—qÔv{᡽» æ·W-.¼ež²†æú#¶ÇéßW¬ÝËîn.„gõ§ÖºŸÛµ]µáàÕBwÏ›:’þöù&¡}¿ží²Gð„Zˆ»ÉL_Ü6àÚùÙEfmfæ3¦Rß»+;uµïUû^GÀýÿ"þCø?M¼nnÿ¿"›ÛNy½Ô”Ú)–Ëhÿ¹ØîxP-TQ …@ãð7hgÅÇíâ¹³§dœÖÒY#èM‘ä»®<˜ß¹È·<_›|Ëãrd6Ö Òò8í@òœÙdª÷&s¶Äß.ðŽYéë9?[Õ"I8ð8Ö¿‡è8mg[j˜ŸÔ–Ƙ Õ¾û ™ áü¦½Gh{Á¹fÌ`ž%F-pÜ“&Cc¸ã·;õ8OÄ='4âp+x¡jnòjHj›M5t­:×,\íß-þKø_ù¢jViè&ÙN¡Ýçbtû@°«¢*­ˆ€?p©ñ@ãkÛúå²y:ü%6qÀZIÓnÀÏa³åïX³r§PYµ€<T‰9oŽ Ã2b@O6 ¡½b¯(8Ì…`Ws¡•[’ æB¤+Í…žyý1Ñ“d¥ **ì±ÿôÁúÍÿ}IϾù edŸw™ ½/Âs?õÇÙ“Én¡Q—÷6v}çœ Ô‡=«¼¹d=ñûèO®¥ËlJÕÙTvNž0›‚‰Ž" ÿ+¢}â”Å ÿ+ü¿T;Õ|¬e;…öí>§ÐíSó‘Pw*í 2AîÐ0¡ñ­¼|9·(-9ñÚØ ¿ÿ`ÕvýÑÛ¯m–) m¯Ü@ ÷ïHø ´´¨˜qÀ¬V, À9`4àµCuÿö™\üjùÑ}7¸vôêJ1 0ùp^Ÿ¹ÐcY¬?L¿c9°ë†éÒß¿H Éé„üê7Òj¤<ßxæ>¬\òâc·¹¶ásþ‡‹®¶íð¤f açé«…¯ËlÊ•°Úð®ö‰ÿ%ü¯ðÿ2\;çT;Õ<ˆe;…öí>§píSój®îR(Ú3þHÀ¡aB\¾míÊí:ü‹·¥e¤?xót &JF%|Ôö9¡?|pyÒö-I||€c)çøç€!à\Ö&ËÕÌ…êKæBu üÅÃ7캳Ï_f/9éÆkFÕ¸´%æBî Á\I€r?~µíºÌ¦®v:ßhðqµO¼-þSøu賂÷oWíT£±Þd;uæDÆ?ÑÞóÝí¦}jn%k–¼ÿñ¼{@9~ìýuŸ|ÓÕ>]:u…B@!ÐFðW.4àŒ1Hc07ΟN˜uý9ÇÄ©?|íÃuÑÃôÔÙëˆW‚ð‹ÜÞþsá ^0!Ìa³íÛºùã»¶%Waˆa]`‰Å]ä8ÒX™;e8aQÒn¼FûÄhñbBg÷Òùœœñ3fÝËíT”j§j¾'W¶S¶Âƒ{vþmïæ¯·ò•ª}ª —Úk#`Ä‹÷my¬­®ÝÝãbÛ}¿­Ö¹)õò7޲K;KØXB#Fn¤·Þ³ûèÔù7ÞÌ.õ®g¢ÅS°ëÑQaZˆÙÜîlÄË++)¿°L/*+Ç\³ÛmE'ÞºkýšÍee%’t0~XàLx×€²ÿæò*Qø u¶O\81¡;½û%ïOŸ2wÁ,þ?Îàv*²Ý·SV«Žˆ¡Z„v .Q³2Ò¾Þ¹võª’’BSí“¿¼Ýª-FË)6QLå¨Ë— J(¿¸”*yÞ‹†»6Zœÿ&ÀŒ ns£ÂB A‡ôë."T#H!*BÎZ?|zîZ&|Ô`+È57ÖŽ¯?ÿø3Þ_?zÜ Þ·„†w²„D™Læ`~ëÛ 78¦kEƒµÒh()Õ´²’‹ YY;”|‚±y ´Ü Û áø¸åWmÃüDÚ·‡&€««D!àQêmŸ8AιókemøjÞß0hƾ}ÅÇ[ÂÃ;[,Q&£ÉçCvå¦ð8‰@ˆ­8SnûdÍJ›ÍZQYV^PVZ|1+=-åhrâ1Î[µO>y*_!Ò ’½iïQJØ„ ŠÊÄúžHBÁ¨¯] ·”Åetš=sAI¸zÇAŠ ·ÐôÑ9à`phxñÃ(pÔšr|äÚ<ùæ:ÒŽÂâ½c¾r›‘m«¦FF.̵ڷmÈÏOåÓb+¯AÂáD pžÀQ‰B@!Ð2d{„T@6å¾ßµOFK¤{MóÜw¼¼-qù†Ù›Sí“—WÉûh½A¾7îI¥›“(¶S$=vý ê×£³o  ¹À;ؘÁ½ÄrâÌEZ±åýkåš?uÍâøAAAïcíMîÏ\6Þh´Ý·ñ¡Þx-&@ñº6oËDxп/\z÷¾½â# Æ!FLtíòtFeå]™¥¥09ÁGà³i÷-îå}% …@ópo“Ü·ý®}2†up¯å÷/mË6Fâ" 8Ú!ÙAQí“—ÀWÉúùÞJË7ï§‘nÑœq¤\%7Œ?:'O,šIK7&Òš)óÂN³' ¥`=ˆµá¦vEÂý™€ã)¢—îò arÆ„æÛ|K[qï¶L¾¹zBô›MßVXòôwºt\Æ2”5á}ßéÛóÑù‡ý‚¯ÀÇ,ÀMi¾% "í“1¢†6.ǃõo()`#´?X í’\«ö‰AQx@ó}(=›V$$ ò}ÿü‰W‰V*1:)¯µ1òÀA‰B  Úïg.Paq¹ðꨪ°ÂÀ?RJÊ+ ¸–sdïJ«UŒ2ï¶,¨oËÏ£Euë7ðã™ÇîãOÝ~q-z…ЀßÝ¢DÕÍ …€B@! Pˆ—ðÚqäÄYd~¾}!ø`=åqDM‚ÙtëEó&¥nó;ÎäæÓ[Ÿn¢§î™M=ºTÏùxõÃõ"åMÓGˆ{­6;­d€éÙ¹Â|&:2”Æ êEó¦ £µ;S(AÄÈbÏì6°gL4Ýâ:çÍ àÎx"rfykÀ9¢(pÞmYocO·ïž¤]ÜcüXVKsÐüÇkÛ3deÕZ! P(^BÀáÐɯİ ¤L„—÷R6u&ÂW¢(¾_,}÷æ©Û1‚û§Ôym]O»D‡Oœ£ë'á”ݨk¿a{EŠ‘MÕÇhÖŠOÙŸgž£r&þÈð*ሙ¼€€gàÝ–E™ ´É§kü™¦Ù 3Ò'œ˜0ö®æm²ªªR …€B@! ð2Ð cf¶²ÒFÁ쯺µæÃô -ûÕ(BÚ©ótÝúI©¨¬v„væB8<¨WãÌf²Ù¬ùñ„H_ð,+q0¶•b’+p–˜·U3ß ë‹§§òp!Ð?11+sܘ׸¹ø:tÇKúüùŸjk×" …€B@! P(šˆ€4A1lm ·ódE{ u¾4áÂO–­œÉ¬óHÖ¢Ãî»>))« OÖï¥ìÜËt1¿„æL"¼‘Ôw½Ç3®6¶Sùn&(Š€{ü òC;uùCÉ¥óòûË“2{¸pöû\²·ý£tª …€B@! ¤6Vpo? à°— 6SH‚‚;eñ3®˜„)Ï…‡SY#ÌI F]3fOÞŒ¥¨0‹¼Ý'ktk‚|;XûÍ®“øK[Õ€+pŸ¼V¾Ï$vÆ ¿—9;Úÿ;1sf ‡ÀòœZ+ …€B@!Ð0’¶¶þ€’ÒOS?öÒXm7¼ dœ¾Pï-a¬U¿‹£SNÞÏçä…¾e`Úí"ßõ¶ œP¼ <ÄúªÐ?&öï—èŒó¼ÞÝQ˜ÿX}תã …€B@! P4ŒHbkHQi{=É£äc§éo_l¡"Ö€/˜æt/ؘò`âe—áôùÆDv£xŽò KéTÎe:yöbcn÷Ù5®NN+á쳊rFŠ€ûmç›ožDñ;W¶šþ³³ãÆÕoæºPm( …€B@!à/ì;|’þúymøö0uŠ £ŸÜ?‡bØJcfÞ:ÿ®ÝM¿}°z®šœÙØtÔužC@Ù€{K¿L).nà{™™éÏs¯²7w(»–‘ý\Ð7ý²°ªP …€B@! ðCZKó (~öàÜéÁAs^yòö+®yöš÷EG„Ò÷n™*Ì;à^ÐÝùü©Ã ‹¿Ikâîm,Ú‡kw?×îÛÞÆ¯µÓw öû¾(ç©Ì?êw|æ]çzZŸ9óm-!ÁâÐ6E9L%omËRYG¹vŸ‚m÷}yZû@jƒŒ>EFe¦¸:îß­:¿cWOÂ7WÔþîø&WÏç‚o†;ùö|ÍO–'ç¶þ]D.>v<÷âxÍ Íeê݇ Z7M§ÆÅ4ÿÝð«;yªB1û<Çäú”µ¢âëÞü}"¡£°¸2cx‡÷…y¿á1üB÷Î,.XÄç]ÁzxÛïEþ!1AÎù³8¨Â_.,á ¥TÁ~Y¯ì~ø}µš^Àªî%VA<û=:<”:òpä°¸Ô§{gAÆ!o:¬M¼# Ú -n•ûþ ¿Ùì¶«6­Ž@c¿c­^PU…€‡$®ýà/Zìµ'Mf㙄Å2ÑÐ#BCì×R 1›Û={¹Õêà‰zQi¹Á`2þzñÿ¾”S^Vþö©})ï$$¬(åw^øýÊ3Æy›ã×þï®;~Ê«€ à ÞNß«Že¥„ýGi{rºYËx8‚#hµfP`ê3A·Š«|š'Ñ••ÓWÛPT„…fŽB³'Ä r®ˆ¸€Ê“?Ùm)¬†`PŸn×Tï©-…@ë#ÐØï—´ªõký2«(<@ pÃ÷žÿå\Cù=Ýáè>4®‡>aXÐC nÏCª¢ÓQZ^A‡2ÎÐÞÔÌ®)§_<}ü݆÷üã·_ÛÀ/ âÈÚCŒæ¿VØ+Æ„6”›±±cÇΰÿ&O¼DÞJCo«ÕJ©™gè‹o)Ÿg~#t.lÕâûvõÛa4oaRWºeº÷èÉó””–M_nÙO[¹“rï¼É)­g  uÝ«Ž5€mƒþñNž«’?y`žk[m(üFǸ¼ÝU¢hø;ÇP¯áÑ~ýcM3ü±[—(ýî&ÑÀÞ±8®¤ î„ФáqX´ô¬Ždµ;Æ¡ë_<ð“ŸýòÃ×ÿð_VÙsÏžüôñ£ÿͦ:Oà6Ms<Ã+¿$àRëˆXK»9ñ(­Ý™J±"éž&P¿Q%UÀ–oÌà^b9qæ"­Ør€þöùfZ8k G2&H¸Ò†7ûuQmP³¡S7*@c¾cœæ.IËÆ'®®Tø!þl¶!>|ü¿Ÿfòý§ñCúiÏ=t“‘É·Âè?E>Ï?|“x…XÂ^ºÿGÏ>Í¥Cs‰Uwjt}ÞɉûùOÉ%‘äÛjµQyEmÜ“Jkv¤Ðˆþ=èñ;g*ò}•†ÎÉ‹f ¼–oÚOw§:´ŸªW¦9§UÔÔÔ= "ÐÐwŒ“oÁS‰B  ðgnxè§/Ì3Œ¯0™Ô½ýZ-ÈÜž­Mÿž'àÜBÃ#µð»‹ðÝ!£Ȳ“¶)±1Án¯€KB¿˜Hò} ímØ}”F²)Åýó'’zþ{TÀ x· It(=[ðÆÝ­®rC@µAn`¨M…€/¨ï;Æe@ìuæ.¾„IåÀøëKl˜?ÿþ°ÐôìÚAðæéª·ÛŒ— ¸uïM1Ý{¾Ñ»÷ ŽœDpéï¹Ñ}W7™_È7ÌN*Øì¤ °˜¾Ú~ˆÍN"h‡ÆUÒt€[Wºðé†=T^nU$¼iª6¨ix©«^A ®ïgóYå/.ÖîL¡çÿ²Œ¶ìOwÃÆ«¿¥e›’jS;í|A¶1Ãú=ÉvÌÝØæÛ¨4ŸÍ{1OÆ3°¶»ëä s*–džÚd×õÓ˜NŽÀ<'tÛmÍKݳwÁôÄnw›ï2öê±=) 9ôî­3F+Íw3¡Æó_xí(Ê+*¥Mû |³’«" Ú «B¤.Pøº¾cœs/÷{åœÑh oö!öVæÀT.ƒ€?NÂD§Àòä°þ=t5á²eïl騆žBú÷8¥ÿKÎÏ·•8zý'Â@¿0²?‡¦=ÆÇ?kY.-¿Û©ý¶ íw ûõÞŸ~Fx;Q.[†-ðƒ×˜mÉÇxBæPž©‘ѨL¹®‚ªjƒ®:­ð%µ¿cœ7&cÊ ™p·ë·2¨wWʹ\( ÜUÏhî®C™ì^7ƒŠJÊ)®gv%;˜zwíH‡Oœ£]3iPŸ®´5ñY©2oò0žçÇ(4ÄL׎D£õõ·òòª­)…½†™¸Ÿ6jŸ(Î=‘C IÇèÜÅÂÈøìñƒiçõÊëéÆi#hó¾4ºÄq5Fò|«…3GówÂ@6VŠ}¾1‘Ò³/ð·ƒhh¿î4Úp 2Q¥ÕN«·deçØ©Ñ(.ÃMÓGPCeðÛ‡ÔJÇƟDhž}ÿɉ¬©ëÊ®ý¾wëOàÕW'ŽZçkæÝ2¯ Ù^^ü_&Þ6¡ ÕõYYãÆõ¯ï^_wi¿ÙÝ`9»TC˜a~³ûˆ¸®’Ë:O©ÇÏÒ­LŠqÝ'öÒÞçÙâ@l«¶r¥¹;õ$»¤Í¡»æL ë' e‡èRÇëãàuË·$S¯˜ôè­Ó™Hw£aâ¾¼ÂRZ¹õMѦ3aßwô>yNœ31 G˜ûL¢»®OŽe»:çÖïJ¥dÞŸ:ª?ÝÈõëÇà õ•AœT?5ðKq=‚ì°Ÿï…U;ÍC8Ï.=zÎä,?N;qÉêp¬—ÆVÝþæ¥ì™»œÜΓ/­ó“ãLÀÑ£†Ÿo%-G8ODµÛ튀7 © ઠj$uV!àkjÇ8Ì_ò© ܹb1›ŒTÁä¸1-ò°¸îBÛ¼’]ÄÖ%C˜ïß"ÂB¨?k¥eçÖh§o˜4DœŸÌî†ñ½D°5à1cÌ@*æofîå"‘ìQÖ˜éK]:„Q\NÓ1‚c„œ#Ÿ¸àtn™LtÍèÔ9ÚIÀqö'ëǤ}õdÂ}œ5ÞR®á<útëH#C©kçHJã$éXØShÒ¡×M¯¯ âd?ˆfmäZ|«$ÎX·eñ7tŒü¶ôA„KOÙAÈòßýk…‡ÓtëEÄC]¾i­ß•ÂÃ4雦µÙç ÿª–`½ (=¸$ Ê·Ù?éjÖnÄÛÎDü×­€3à|~³œ]p°pKˆ ²ã¡?áŒ'4! àÀÛ€ñD%u!àñ6¨®LÔ1…€B iÔõã`Xÿ¬›o¦¥ÛØ«ÝÉ`XHeå4êV9âx Ïgzó“B{m4Ö$–ëX›|Í.ºƒI¨Ah¬¡µ–“H˜J¢î£Ä:”ËÙ‡Ûõ̳éGG>ÀiI¹_,6¿3-Û¼ŸÞøøk=°Ý6{ “q§9"ˆµ”ØŽQ”}Þ¼«íÖ—ódÑS9—(2Ì"¾Í¡ü=±ÙTRVI=«Ê%ï½Zäuu­1Jn®‰ ®sǽ®ûù˜?p“fкqxy1<@;¿Ìi§Ð;ìîz^ûŸ¤PK°k¿-oDG…içΛb¸Ž¨pПr/~ýÇžÝЂð?YpbܸIýw·hhlL AÀ+ض­„—H/¯ÄsD0ž%eg÷†Ýs9´™”Ðîx¼ j è¤Ê¡Þ±ýºc|þR¡ÐÆVwÜóy¨}ÇÁtš9.ž‰ŒgÛ}˜va: È“ï!Pû;Æ9ÃøôHVèAóŒ(ÄPp4Fº²G¯É#úÓ:VúÁ¾[ÐðÐJÃþúÑ…Ói`¯Úy ƒ¾d;n÷饦*¢,óÁ\žÚ‚wÏÄË”‘q«§ã@IDATœ¥öù^];ÐSw_Çä<[x`‰Úk¡yS†‰Ë.Tiбs‘5]9èÎ`3þ܃ó„Ý÷ß¾Ø"Ž›Lááb¹ùçje×Õ^Çâòrêj©}ªMïûÇ……G!´ðs]¡f> ô@Gqoÿ‘“.ŽˆeLøâjEUÜu0ƒÖðÄ‚bîù åa£ûçO$=¿¨„–~½WøÈ°P;¤ÝtÍhö±|š6|›Â¢ËÔ‹ÿTó§y@óþÙ×{¸'yY¸‚›Ê¶U3Ç5Øy _ìcâhzÑAX0}$M=ˆ'\Ô3«îº x ÆP>€Ö´27×öR÷®Ëøó]€îÐìðÊçÏ 4³6›MxA±ZæO¯&#àlæI3<Ä'MP€y[Ö,4óqy­ jfy|~ÛòM‰l«z’^zâŽyƒ`þ哯顛§Ó„¡ýjœóÖʲ~—Ó¾ž0:F†Óõ< íd}²jk+[ì´øŽÙW\r™ÛðU[’hܾ%àVn·~úƧÌÓ^yê.¯ÿ¯`n€<{°=o{“Úß1®¿4AÁ·ZeìE`Ðnö‰í@»g±½õù&ÍUºaR<%§eѶӞ8´¯(e~Q™XG±}w^A íe[q!ͨÍ06cIJ˦ALäû2·¹WÌæ2Š`b‹ ˜t s—èˆ4~_«=fóÄálÒÞí7ÌQ b4:4„˜“ [ôSÌk†ôušš èC)l›>¸o,§B—y'lËë+C§¨p‘f]?À‘?IÔ1Ì,þ?À¸=|Ÿ|Þs¬ |·c(‘ÿFø3yTF0OâÄ’x$“ÆîSã/[ÀîÚÞ_µ¦³ÍÓc‹fÓî™ne¢ ÙÀ½Ö ùEôä=×Ó¼©Ã©G—Žb˜hɺ]Ô·['úѽ7ÐȽ©ST„¸Ã5câûÒ³ΧQƒ{ ò^Ê KË*裵»è†)Ãé·ß!ZŒÑ|•†ò‰¶øG®`¶h´Œyºþ‰L’a¹»µ|‚K' ç íUÏH–M­=ƒ€ìèo%õ"àµ6¨Þà<"¼þ“{}F¾%$<Jÿûý[éñ»æ|Ú£ ­O¾{ë5¬IœYßi¯?ÈJhÔap´jòšW2ªJtõ¶$þ–U‘4ofä·i×üŽq1}Æc$1Œ‰§Ð`³ »M Úò¹¬u–<÷Ƴ—*x>yý£ôÖg›iÛy7Wà…¤›…üãËôÂ;+k|˜¶lÚ{”^þ÷zùŸkØœ$„¦ w’läÕ;¶ýëËíÂ?yåI‡La} ÿß~ù÷•¬h<¶ãN*8wËŒQ"÷Vl§7–ld[s§ÙK}eÀ=õ ¸YI£H‹Y˜G2ývñúîi ÇýIÍr(~9±ö¨ÀÈc<Œ:˜{pûXËóÐMÓ±–âÙ½#Ãhl<svßGI<#xÞÔÂvü2Í\Ê/¡‰ÃûË[Ľ©lG>‡gKÁ‡ãº‰CØ­ž•'8ôš¸ÂÐ ´ICãzðŽIüYÊY3 i(™nKÖÌk« ß¼Öîʹ°c[ÏØlM§^Ü"tÎ4دçãkZ’GSïECä\HàâÀ$Á¦&¢®okôoœ˜· Cã€q]åÕ6È•KnàùõßWТë'KX‰Ð‘5ZgYI—gý{t¡¯E}»w¡2ö`ôÒ{+éþSD;‡êþsÅVŠb3(ŒþyÉ×âZ Ç_È+¤·>ÞH·\;šØËÅȘ F—¦®Ïö>A§/äñP}WúÅ;_Ðâ;gѲoöñ$´BúÝ“‹èÓõ»…bä;7Ni}µ5™ 3kØ]›»xj$rOÊqž ׃ͬ´'%Ó5Ê +·ìçoH•ò;lg™Uðpþ|U ¢Ï¸3" ó‚Ù†Ò “‡‹â5„íšíÐ^îbS…{æNáÜëÕÖ·ùU¬ñãúÊÿ­W?’|ó#.ú÷è@I™9ì]ê"Õç.w>+ëjËd&¾X¤€,¾í1©#•y“¥‹A‡‰Ê+OÞŽM!±l*⾎àC7MßÒræ°›Gy!?¹Žà@Ðf£C Áÿ2¸w {N/Ê€ÿ™”¾Ý;ÑóÍ%p÷ã8ß9Îã‹fŠ{t6# á ¤¡2ˆ jý¿´Sç©G„Q”ËÀîQ>”[b^ë–6³ë³žc#ëRõº4òŽF^† 3 ¯fdŸ/h|ÕPŠL^"0äòû­ l± õ†,˜6Šý\ö¡ÿ[¶™?._ÒIžìY|Ç,6e©¤ßücý‹?2 ܘ¦üñ?«é…·?§%ëvŠc6»]õtƒÏ7ûÓØ¿æy¶Ùrú†ò xæÏ\à|þüyl~êJV׫ÿÙ®ƒÞß„i¡˜ (ïçܾr@S+±n_5oRm½Ö5©~x1&ñJÏ…ì¯xÅæDavñ “] ?¯frÁ;†ká'XJaq©ðqÜ9:B(8>ßèÔd¼n·ø`z%ùƽÈ/™Iêöc¿ì›DöÆÐ™É>¦²ÈãO¿dööëÆ‹c(WQ©sHÿ4›ÿ}µ-Ytnâ p;“ÓÅ5øñÔH$ÒIaÍ| £c²Ÿ60ìãÎ\Ï=÷ð&EÓDy¿Çú¨ˆPnûÓ…Æðá›gÐÍ3ƈN:#†°Éß0œ1](”Éê vïçþ®×u_cŽIòݘk¯v øÞI¾åõðÞ"É·<†5ú4¸¶6É–×ÔwçQnI¾åõX×W÷k€Ûò„d âI©Ý#ÌÜ5‰©Ä¸­; ¨î긣ҺÛ^û3Ánû£µßŠ^ÕX6©­ a EÖ€×¶˜¬ùhfOB®ÞAï-ß"®ë˾/_øÞ-ÜØfÒ×ì¢ÛCiá¬ñÂþÃ8Ï>´@|xžû³“çÂÍÐtÆoO¼¸O±éJ¶¥‚4”¿¸À3?’`œYἜ۔Ÿ"ix¸E_´È¨-]ZýåôLžWMECE¾¯ U‹.Pø6 >¯µAÊ=@.‚víþNMóEü2a?·!W/üM+LNàUèõ׉KáœÇP¯!lf¸díNÚÂ&0˜OÍOà… $ýXVçN’R:DZ„…Ü¿ÚícOö9Œù6-Äÿ~~¥¬â‰‘ØÿÙ÷Ñ?¹=|ÿ«¬€X ÚJy\ÛØ¥`6Á<›}©'Ĩcï*e†¼¦¾õ vpL¶È“ð1ò)ÿƒ–ŽDîåò`2$L\¤Ÿf|7’Ó>¥D6'‰`­^ {¾šÅ,Ù}žÐÜËüǰ½øîÔãÂÕí.[ûZFç¢K‡ê€(òÚÚëØNQ<邸ñ祆;¹Ú×¶áý+¾cž¬«µ¼"/¿ ä îüžÉÌÏ„1ŒG°+øˆ¤ C>äïù»Ë¶ò{ß•íô{‹hÎõîÉòû[Zø¿ÃÕà~övrŒ'\Úì:u0²ßo ›ò-ÎXOì_i‚â^—üÂÝn³]p?ÈÛþFÀýKhHêwò-Ï×§…‘²¼N®+X3”Ï.|³àñ§Ælfw©/÷k<µÝ?2rkfQÞeþ&uä¸^Ç'ŽßOÒ>O¥Èé`&ùàôw¾>b[TØîÇó‡Ý]:wÞlºñ‡ñÀ±ÓbRÖ8žo D!ˆ æÁ+ÿùÊUô›¯ÓÐGºö³1wêHZ™DÏ¿õ™AœXåáäÔ¹‹"nÜÂ>û¾ù“Ù»Êrúš5âÒ ˆ{úžóò?W ñ v×v/Û€Ã9ôÕsk0ïç&ø ·7r܆TöØñÄH$¼Ÿ`b¿{;v“ûa†rï¼IL’mô&Ûº£¼ÐæÁÆûÆóÎ9Ä\¢7—lç,Ñð±;g7Š€_Ëšó×}K?þÓGô Oî„.%A/•XÊJK.•Uhp {|)R¡Ä1„‡‡…‰àqíéÎ#;a¦":_A™<2Ïè …óu‘ìã»=Žj ¼|!;¶(fñ—­´q@Äœ:q±’̬€\8<”¦t £ˆˆp xWà Å%ð–‚ggb­¨8ËÇ®ÞÈýx­¸ÎÂYci#îù›­€€C³úÌwæ5ªáõVQµ„[æ¸Ñ«XýòÐí…)Î ÀÃÙh¢Aì¦ þZä 6fÓ<”qp ¨…@€ “ˆúÌ"ÜGc÷î3 ,R@’±`ÒaíÿÃß^ÍŒ¸£o=÷€¼­Æº¡²àÃì^y£{¹•4Aàý#Wr’£'F"៼.Á¨*ìáŸ{ó3z঩ŒCœ Ù[öžcàõ$œ—üpÑu¢#í`ký$×p¯Ò¯-¼­ŒÔ›Ý=29äNŒ B'GvzZr~ýï†YIm¿ÜèHa&ì•¡±µ2ùF _Ä1†£d•Û©˜­°*yÔüry‰‹16¢ïè‘ ùC"›k›¸æQ¼¶ÙO~¾N\vn "«1„¾8n¦ü `ZÜ;‚"AÂOà |k›ŸàYpGV;:k»Û³ò‡j6» Š€7ºæÝ?ãwÝ0©y7{ñ.]Ó ¦_FîsÂ.ç%/f0Iƒpë&>Þ0-ùbÓ~1±>Ü¥lØ}˜ÊË­"‚X˜èz º¼^­íÚä»5ê]ŸæÑ›#‘˜ÛÂ_\ZAy<¢SÃ<‰´gLGÑ k ãMuE¾›ŠZ½×Kâ5<€ÙíÝ•1ñº¹{S3#™€W«aù¤K ÎDÑÁlÚ]Ø™¶“œ³‰Š Þ<Ê)’0 Â3È9–ÆŒÜ8Sj;¿À ¸ fšg¡ÁÝôE:Qž=˜ Áa´õ|•0ÑËó„ù ¼ ÔÖ~ ~ Ÿ~9á«eðjáþ̬j°UP÷©vc)wÒù­æ¾ª69wæÌ𘄄bO¤¨iÀïqö¤ͦ¿}¾…‡Ïl‚|Ã^Z(—0jý9’ÙÍ3F ·c®ãjC! ðK¼=‰ŽÇ# gз³Ïï£ì_9ˆGѺŠÈ~ ˆ*Or’o^Ñ»-+#}=…¹+=ÖÔvù(‰v°Dz¸ ˆ9:Tì!…Í'àÑ£Œµà¬!· ‡ Nî¼¶=qi>‚Ž(ð2rP-3+®bºÓ°¸`ú,Ý@û.:IøþK!ôôfýõ›¡\9ùÏ %ã´vñÜÙjÑIâ5žY@‹"àýø5/•YÓµ¯Ù| ’w8DXúvMÀ“˜€#˜’»Áè }¹å•°›1% …€B@!Ð&€6U’o4îcÍÔ¹CÂã™97jï¯ÜFß¿cfŠJr R)¶YŽmðP‹Eï ¶ï¯A¤ØF¼ÊN\j¿Û²-8F #„—Çde˜—px;yêÚ`Ô‡è;ÙŽž­ÅsÙÁàw–ÓŸç‡Ñ´^f‘Æ«¶ëgy~UZrâ/_Î-âƒè áY) ¸@¨þüü/èRU(zÌ„FT³»çNj0pD@CaÔ6]uàÿOMÿŒ]±¦þ\áç{ÎÄ!5nÑ¿§ àØ×oŸØN5Ω…€B@! H¤ È]ùc±]†Œ ûeQΙPûޔȵe[ôož^C‚)Ýå 3 &šR Ó“Jž„ìÔ~Û…œ\Â\’ð€Dª‘…6ba6 8p‚>¾ÑQ‡¹»Gš©_ýx})WêTÂîׯ*¦_\c¡âôÝú¾#'´3'2þ¹míÊíx6¼àA®8ƒÐ¦e»x‚×\v¿|s"éÙ/f¯·ÅJÇuŽÝž™{®Œ ÷Ο™0¡W½{³Ûb]¯V§˜ôÊ“ˆITSÂ,Aôû'ns¬ëyògΕ›j­P(þ‹Ì@꬈›m6¿žo³™íü!Œ:r¨ôxPp"»›vîRž~ÏÜɆºlÂÚ^²’ɦ(‚xóÄKLÒ”5a‚i/uEÇqp±ð>0rîcr¦¦õ6ÑÇwiñWÅt¶ˆ1ã§ñë­eԿ̨ëǽ¿î“az‚^'ŠÐõØÝàéÜË"2ì ±?‹¶vmÛoå2 æX®Û›ù_þ\fU6…€B@! P´AÀ÷þ(³ã?Ú†J&†%Ã¥¯ò/¾²ç«…£.]J?cÖ½¯}¸.jø€žú„aqÚˆ=\~Â¥6„æÐòÂŽ´ûé–Ä[®[Xf¿¾x@„¼Jlä¤Ly\V¢{¨ž~™^Þ¬çZ-âæã–a†Òð&ÞÂ!w 8žY@‹"àWy|9— 9²Ù)6uwj&Í/îÀèRA±ˆÌ&“(,.%žð+þtKÖí¢ñCúÒ×çpŹý2B^æ×kþ{|ÍSEçI0CQܯŸ˜*œB@! P´Œ±£mÓ?¯`Òlá Ö¥ÃñO.åýtOQ L¢ìÚ¶ÿXòþô)s̲Ûm3Ø+G$“H=¬GG…iíÔÉ8k¤6Ù®½_ëò6µ+I¸¬Tí}/·ZuDEÆFàÐ óB¦Z­¡ÅTÌÐøi÷yziΩ7½Ç—Ã>¿MØ£îþFÀÑ£Ñíº£…·ë|¡Qp_ÉaŽœv‚]Ñ–”QLÇÞÿê³ÚÑÜ:wID)›3i¨¯Š[o>ÑQç!1 ߌÝÖ5îaïµ\¦Ušvþhdj\äáü)« Yñ¿Œíç”x# b­ŒÄÙs)«” …@à" Ïœi:Q”ÿûô~ÈÀß"3ÌdØý"Gòóa3WXÛÊÊJ¬›V,]ÍÛÓ·Ï øxKxxÇ`‹%Êd4óñb×L›)¤+ëë8UíZÊÎt‡úÀ @4æ%֊ʲò‚²Òâ‹Yéi)G“™czÛûüÏ’ –Èx6Ђ»Çÿz௷\HÿÕµÿáÛÚ„ù ªïOÜEòV{.zD\¾V'à³' ¡…³Æ‰ˆnK¿ÞK¯}¸ž^æ`w…عç,eñ³è£µ;é7ÿX!"¡ÁT¥5ƒRä–è•ÎP²Îuu¡y¿bâ¡ããFåpƒË/~çS&ŒæÃI5oñÜam æ …EiRâ)àÓ¼wL”+¹ºpwT …€B  sà a™s—²qÈ|T,™IøªÏóòú"'߯0^°éƒ&X /ÁÇ%æ%·Åm¼†¸>h¡§6ã1M³ ò­[+ó ¾ýüÍÊ‹'/9/m׿’w cƒEØßóø–Ys³ŠsÞÿÑãÝy÷-Í<œA íø×ø?(<úÜ(؃· ñ'îÔZQqÃ¥µËÞGüA@ž‡³½×öäcì#b;9ý>WrZ)8Þ) ÿW¢¾Ý;Ó ß»…öΤÿ®ÙE¶‡rˆçñòRŸ®#ð,/-¹Ð¨ŒuJàëD/îۦð¶×x]屘”STFÑla—EJZ†p,fŸ´‘¡1-KHÝ­P(ÚgÇë\r)w5“*iÚ?ÎED?þ‡ý@ákD|èA¡GøRœ‡2ò’í"ßá#¯1lÖbÒ8ü#‹n«¼”·wùo™|7ھHlA¾1"Œ/FêKŠ'_Þðú¢N ~ö)ÝŽ çñø†¼~¨üÈOF,çk^ü‘€ë9Ù§vDvêô£CgˆÃÀ¶*ÈÐfç\æPÂÅ´~×!¡ÅîÚ1ŠýXšÅÄÌÄ#'ipŸn´÷ðIAQX8Ž?žEñ||äÀÞÔ1ò ð}ÙZŽ0%É:vô —/½\ê,’®v‘ÿkzð 4Àºc˜™2/•ÑÑ“çiÌà^Ì¥}&yDƒÝ&v&(íUk…€B@!àDàÄäÑ}ˬ¶õÜ.ªÆD{y@bò/ªöA¦A%I”¼”|C3íÐÃç•T#àÎA€3hÁAÂÑÙ‘kë©7(ùjÆœr­lk’ð¡dÓ—øëÁñ‡™Ç׬ø¯ñ@.œ=óiЦ=žž•Cµ}nú í—Ÿ¸£Á¬& íÇöÝý„}xmûî_-^HV?kÐà€£S­#ÀgkkYi_s ä‹.‰yÂÅ9ŒÉ™š­œ¨~áûŸ»fL„ª¯q‘‡v\ä››/à„ˆYXwáièi§Îó؋ԯ‡˜ í¡ÛW2À/íT‰‹¸Âä^bÞ¾PµU(Ú;'Çše³ë+ø» ,¸=ÄwIõ™4H…Ö’ J­7ˆ·$߆ü}zp÷þ‘ä[s8Îäí_{gáÞåYÈKI À= _wnœå¾8ðÙ¹C_O]hw8¾åÑüpîÅU–Ð’uýÆ5 פ´3¬.)€ß²â‹¥ìïÒÇë¾uÀ¤ÃŸ¥6ù–eE”§Ö$ßÀ ø9lÖ‚kVmäráÅ®ýrËâºÖZb"÷@5ÿRV®M–ÛÞXCë-ôÃ*œôw`-¸Q£å É5\=z#ÿ¶š&žÿŠ-Éd¢¡}º¸0ÞJ …@{B s쨻l:­s‘oÒò5ÝxÀýê#ßEi†e˜¢ 0L–¸ç×µôý¾!8<ÄF¼d[sÏ̽¼öõ>_¨–z1ø1>À˜[ØÚk`.‰:þɰTV=ÌÇ„ð‰yK^Kù­Üĵ?}‰´|Ñmùù‹'îyãì…|ú`Õv×CD[«ÌÀ ø%nKø¨´´/6†u°H.1¿¢ˆ<1å[y;B°÷ŠHm,¸3LmUĤ±{¨Cø__º‘c)i2K7& üFõéÀfSÁ®ècó&'¨nP(ˆ@Ƹ1OñÇîc.ºÓ~T£Ó&£>=nÿþmM¨¾—Pâû‰ï(ˆbÅ WN5uŽ]j0[ æ2Y²t›cvÖ?>ÊçñÝUË•¸ì¼%޼–ÜDh½yÿ 9úÌð/xØâwn'þOʬMív"6ý‰€¯/÷Î «¿Í>žöá¾#'´÷–mÑý]î/8/à–‘rà«ä[qÙDcQµvÑë,¶†‰˜UÂ­Ž—5ྗ x{š f¯'ÁA¼S‡Pu ¶ÑAžDúÑÚÝJ.ÈUÖxþÀë`ÆiÐ%Œzu‰¸_à,ýÔ_%uZ! P<`ç%v*ðgþŽUñí°Å4µÏÞ©-¬œ>èµÔYlý½’“¶ €…f0f±ù÷Ìã/NÍà´Åh¾Z׋øž\йi”Ü÷ÌðÿeÓ¡µâbv.ÁSîÞíÈ FÝìgù#ÇK ‚(zHë>ùï²SÇŽ,a2©¿òŸUØ4+©àó‡¯r¯ô”äU›¿üü‰%¯Ñ®À8ãå¯S‚Ì—œ‡ì&è‹ÁÖÍ+"MP@C˜x‡XBÈÂKHpÅ„¨“¹Rðw–n6á^)DI6ßo¶™àù¦o‡ Ô=Zà \/F” Jyت …@½ ÀNæ¸ÑÿáõŸË‹xôo‡94|z½{³å±æ®™|_ÇaWñ¸=Ü2ײØ3áÌ´gãO47MußՀͷÅqkÂÑÉaøõ]·ý›íÁýÏ^µ2þ4 Ó‰eµ­È"ì‚B6,]²bì53sSg|÷µ×E ÐSŸ0,NÁ~¹ýÅOøU‘öâðó Âo'˜pi·Y‹÷&lüüÐî°?“8ó6ðÄþöU|¬†ôÚuàLƸQÙüv÷âá'Ne°3|:Pã"íÀ$ÄÐl6 âJ\§ŠŠ žÈj¥.öv¸ZN—x¾ó»Ë¶²ÛÇ®ì¥7Å÷íªü„ó3€Ÿo¸„×L\…íüÀN&¼IáŒ%ðD‡øgà­D! P´UjØqÖS[d¹§×öíø¶Hùn|-¾9ùé~ùC_K½Ë®Û÷°êÜÄ3§.y-õGœð-N܇ ø+—pF¸ó Ú¿-aÏÑÄ}“ç.¸Án·Íd¢ÉDB°ëÑQaZˆÙÜîXE¹Õª#b(‚ìÀÏ7ˆwæ‘ÃۿݰfGyy)&4`†˜è€5w øU†}46CÑAÀ¹£/Üz‘€(ˆ bÛ*‡‡…1ù®d“+Ùlvr8 ¨/#KEåY t<û¼ šà‘á|}d¸…ÍVüíUjÞ„—/dÓþbî¬ðL~B¹K°zFSTdED„SXx˜À¸_L VÜ»ÏÅ=õSç.Rêñ3t¹ „ò‹K…Ç$÷ó¾Ø]“U§¼ö¡ïþ'²AÆÈKtD() §aq=¨w·N²8j­ð8õØé7ð‡ÚÒ¥ùm‘(òÝ"ø¶Á‹[xÒi†ÂZp&Š&1 ÐêBœäœMTLlˆRQ.NûXpèB;ÒÓÞxL¨±63±ƒÍ|X˜Eh¾A¾££¢Ä6ÌO€«Ò~{ï-Á;ˆ‘h»Ùõ'å1ñÜ'–æO¦L¥ª`¯6•ʦe›öQ¾#tÿ‚)4Œœ)Q´g€Ç:þ+Æ"-&uØiv6Š|7:¯Ý˜¸X³Æ¿v臺ƒv83ÑoüZʬ´g†oöZ¦LØ 8ª'É$´µ’TƒeIbŽã˜ø­·KCÎÛÒ_ÞÇZW•–™•n4ôæ’˜oˆŽóÉÅK½øè3#v~õàf÷!cÝ¡¿Î\`,+ÄÀ§¥iZfþJÀQ KJ€”û ßÐð‚€‡ðŽz@Sî—ZðÓ••«'†‡ýËG£ÃB§-ËË_Z‰/vËEâò ³àl éá —¶àÀ çoLBBqƸљÌlûób托Cø~¯ØsºB@*A(!RˋɃÂ=!Û1—3/ÇäL&à6Ö‚F ö@¾(n¸@£]­ývz¹ lèCC-bé ð®J<‹€ÔzcžÈ÷J&”#ô¤EsÆq‡Q™V4„6¢Û>qçL‚¯úÏ7îåKuºnâ05?¡!ÐÔ¹:Ø2lÈíüÅø„oöFÂv ·ôošï:ÓÆAE¾ë…ÆoN[ž¯¨([È„(”Û’ÑCßHYÈ…»Z€¥V/¿?pI.AÝ·A$A*A¾¡ÇŸ®.î7Zðõyù_]y/ûŸˆæ²vú^—.ƒÞÍÉÙÆÛÍà‘¸Hí´ì €„ƒŒcǥݷ¼—5JñUýÅ•Ék¯ðjÒmrpV;5¼AÊöÍ ßb‚fe¥s‚f•¸$àmÙ£‰‘G`C ótPàjP¸oäŽ ˆ7Žã<È7îQâ9$ùFGðPF6­ÚvPïûçOô\&m<%tR^k‰¾ø&‘G¢„9ŠzWÛøƒ÷`õ¾2ø‘ŽfÓ‹œ¤SÀ;}^Ÿ½ûS=‘"ßž@Ñûi|jàéøWSÞeCÀŸ 7ÿ9¯o!ô ‹ —îD„¤šowò •X†\xÓ?$$Q×>‰ 2?†Í‰Ž¸‘ øÒ–˜È¹“p`Ò5œk´æ›¯u Û~äLЛdÑG8×ÞýÅXºËfU. A2Ax*y"¦Sûmpž€!4à’„{·t­›:° ¿æÐ€'hÁáin³° wz‚NA€óÄ©ro­Ù" 8Ör•‹<Öì Äó!r`À k>%à"Ï*m8%Ì) åŤKi÷ Ò-‰·\‹Â¶Ñ9<ïÒ„3>ÀFNÊ”ÇÛhõ[½ZxÇ0ï ’É7&{ø¤ðv›f%ÍGøÁkL»nÄ„Lõ7˶~gTyY¯“ ó‘„Øúš£VëÝwíߎ9O-E¾[ a«% MoëŽJIÀ þËÑîiOÆŸmµ]%ã@Ñ€»WCK§Ú³š|ËküŽ|Ë‚iÆ?iŽ»°b0ܹu̘gf$%]–ç›±R$.rß#ë¸ 22W­(eâÊK·Ó'vê¹gÏ%$Þ„Däd"„EŠû¶<ÖV×ÀBŠÄEwpj¿mBûÉ¡ŠK+„WïæÚ>R‡‡”O6äÐÉ3(®WŒ0¯j5Wµl 檸¸§Hw|ôäésO$æåaÎS‹E‘ïCت ¤ý8>m𫇶0;¸–Õ’Fª°} ôR«ªÌl¦ àX˜Ôúb-M/ün="9y§Q3$¡µd/.= ú£-,¯{½«Ù¨šöâ‹ìb„\Zlö ŸkÁ몈§Sóë4·pš^°)Ì1Úøâ^ow2^Nê˜ç@'Oh¿9HT9GM;•ÚZ~¾=—KûM)¾oWgjæiçÜ·vûEEÕ¼>ÎUÚþ17åèsL¾ñ­o±(òÝbý#Mÿ‡« º¾Èµí‡m€û!¬õÉ`Ôßt;ûûÖ†ýº‹¦”´·‚ŠÌ[­­‰€“€Ûyò¥U˜Ÿ ØN8û^‡k%-G8"bè¥üâªù^Ñ)´¼ *…ÖF@?TVöçGÿ‹§ ¢È·§ôƒt:­äž¼°›eMøÈø7ŽôõƒRÕYEÀë„Å{ûuî¾”•f¹È?è½OœÈ¸Í{¹y*e ®«Ä7žPdnj­ð`~‚‰¿ðÂw˜E¥åÉáå•xÆ3¿¸Tà ¼•(j#P|æµ3çV×>ÞÜ}E¾›‹œÞ—öH|{oÛ$K§Ûl·Èm[+îã'¢­][Á³ß•Ùêºã)¹í¯k#»"t•M÷ýDLWÞjC!Њ`ò¯­Š€Ã½•=¡ó¤`%žCxVTr-¸n­Špë¹ÔUJm “‘="Š|{F?LD[é*”¦_çÚö³ EÀ[á˜C ç!øçf-8]“9i¬_ØUׇsq×€­ï:u\!ÐV€ù‰4A+LxAQÑ;O[â,1÷N.*ÕöŽ€"ßm÷ `·¼ÛdítÒ&Ém[+Þ O¤÷ޤ³l†ò¹ÌZ·Ú$·ýqÝ=1ñ"—WøWe«Ì°“ãÆuóÇrª2)¼‰€4A ü€£÷¬ÄóH®LP<­Jщ€"ßmûMH}zèa®a¨¥®wüêÑ~þXcEÀ[é©tÃ[®¬5íÞì)S:ºöýqCÓÒe±8 Ϲ­Ö ö€€ÔÆ‚sʃI¸¢ß^zòb´áJW£^ÊM%ÛÎPä»í?pöÆŠom¬©Áh-·ýi­x+=~û÷Ë.Ì‘=Ü-••å?h¥¢46Û y¡nÐÊmµV´\$œ©7´³ØWây€ªÄÚó©«Û3Š|·Ÿ§o ý˜¬­]ו\‚¡ÖN8z៫±Ðèß. u篣ҀW?8µÕŽİ5È÷…¼bʽì±ùgM~jßì=J9— Å}ÓÏÐc§›œFcoh |]`" Èw`>·æ–Ú¡Q¦¼× SœÜö§µšÂߊO£_—ØÏ2ÏŸýk|bøƒ#]ºlÃ[±hWd­(]¯w ø© Ï ð‡ÖS^A‰+1³ÉH/ýðVúzÏaaþòÀ‚É®s¾Ú!ÞðíaêF±")ùX¶ÐRÔÓWEPù(š€"ß͆.`o4èÚID„ðo/¬ˆ"à­øTà’ðø¸QïòÛñKñ’8ô'yíŸÜnd ¸‹+”V|oTÖ¾GÀ×Ù ÃúÒ´‘N¥Œvz÷õãÅ—Ä÷µo½}{ëÕTåì-¼H¾Ù7;®÷íê£j«>œìØyÛîûõÝÓèãºnȯæ,ZD£oôá…Š€û캲‚KÂÊrýÿ±:ÉÌŽÎfÀ%aÜîý뺶5kZFiÕ߃ËÙ¿5Ë‚¼ñavÿ8»o·vÙ¼¿$dÈÛîûÞλ=§/ß9çÚ»HDX‚©[ç虬ÜrPØžßqÝX:|âíM=I“†ÇÑÊ­hHßXºyÆHJ66"ºÃu£±—f4Ä4ƒóOÄ'•4Vg3‘bÝ¡ŸÓެò’’¯?ùÛûøn ·c!w6z¼Ñq ©+náȘ–¦Üë«k÷Ò ä—„v² Î . Ÿíµk×åni­S˜ˆÉãàü°Ù`†âuÂáô¿ì Š +%ì?ʺt*(.Ú¼K!|u»‰HXÕaUÀ!»Oç\¦¢²rújÛŠŠ°Ð̱CXk/ȹҌãM LÙœxŒ¶&9=öãŽÕ£ §_Q‘J«î˜=ŒFê%ÎíI=A Î#tû Þ‡ŽŸ¥™ãSkÔ3Ï\"³ÉD%å•4•Í[ÒO_¤Q|ïIîÌÞ2c”¸gH?§‹ùþ¬ß°û°‹ô_Qu@!à§x|Kâm檚 1qÁ¼„GF,æOS ·±zDhˆ#:2Ôb6W©Gü?-V¹ÕêÈ/,ÕYÁ`0ÿï~ñÒùв²·ïN~gûöÕÅ\lIÄ›TŽ[ìºA×*\Û~´¡¸< ¸$Ì;š]êã˜pJ—„ðƒ¢Õ*‚ð„â$àº}ŸÜ^ëîJâmµZyý }ñM"å3ñÜ'Vhéâûv%KpGó ÄÄÊ8,úÑ“ç))-›¾Ü²Ÿ¶r'åÞy“iø€žd0àû¡$ÐÑ¿wëP&ÏõI<“l)éY„ü/Ÿ&ÈC. Õ½ºÐɳ—ÈÀ¡7kÇûõèB+6'ÑYÖ —sè÷½bÄ=ëv¥<œ8X/ÅÑäÄhFœØcSušjK!àÇxˆ|ã…Gã Ž„Lð­ÿà†®=z½ÎÊ®Ãú÷Ô' ‹ãÎn-4$šq%ÍG@|¤JË+èÎíMÍŒIÉ8ýÛa3§<Þ{ì.yëOë8iX“4IÎZï07Õyõ¬öæ—Óãw*îqH›— \ò‡îçÝÂ%á«ÚÒ¥Îñ“æ%éñ»Ø~4½jiƒ€{E¤ÖÑò{sâQOÞî¹a“‡Î^É7PE'd ›!`9qæ"­Ør€þöùfZ8k Í™8Lp¥ ¬§Û9:Œ¤6º¡’›ŒÕßþà`E± Éóͽâì„}ì—ãS äí~Ý; ²žÂò.ÂÅ}§sóhó¾4¡mÇ5;dЗ[y:ŠâÞWà©ø'&ßÐz£÷zÏO?Ýñ…];èwß0‰çWĪ…‡_îȈ9-<¯EKÏÊ¡OÖïŽá Ë~öç?ÿÏ«/¿ÉÙYyi< ×1ñÒIÁù×/ ¸Ryø%jnrpIÈÿè\ÜÏTº$lnrÞ¹O3ÀÅ)yÅŠ$ßV¶q-¯¨ {RiÍŽ‚Fðñ;g*ò-ñ¯gÎÉ<¹x-ß´Ÿ6îN&<ÀUIÛF`›`NÄv&ÎüÿÍøÉ³E¥²ÆûRA1:wYL΄ÉVïØŽ<ù2‹ yWqM~Q™XƒÄà â^žÐ)D½:NÔ¯_#à!ò:‚|‡ð~ןz’É÷/Æé§=÷ÐMF&ß|X‰7ÆÏ?|“˜›ƒB~Ï$üÇœžI£9+7[Î!D¾‰Ý(Ÿófy››v£+ÓÜ Ô}C. YÓô®¼š'%À%¡_ O–8. ÄŠ´~rÛ“k˜Hò} íÛ ¥‘lJqÿü‰d®Ööy2϶–p^ÀmEBJÏ$¼­ÕSÕ§&Иϛ2Œ6ì:L¿|w%½ü¯µt„'XB:ðD]øð¶ñ¤¤1Nï*°ñÎ+*eBî4?‰gÓ.x>yý£ôÖg›…6ªfjO!àŸ |%i6éöU¬ð“íØ7Tóµ™iÏÆŸhb‰Á‰`Íwؼ{˜Õ±ËóLõGo¿VSß &¢Ù‚Ë50öLÂ_¾÷ž™ÇÉáÙ4Ž·ºsö Þ‚¢xíVe‚â5h›žp•KÂXnòG—„ÍœUF•Ίiº«wÙôšÖ}È7ÌN*Øì¤ °X¸K‹íA‹æŒ«ûu´A€[n^}ºak9cÉbÑ”MxƒˆùÇÉŸ=x¥ Jöà“]ÄÄK9ùÒu7fñ„K,Å¥l&“±ú[UÛ4åúIC‹“É@‹o»FhÏ夿ÉëûÙ¯gEeŠÒL0ì¶ðÐàä»)Å—ä»)÷¨k­@ï§–L7EvöùFñkh¿§ÝtË#ìj«Ë=s'”æ»5ž®3O`ÏN <ß ÈZç–èÈPƒÁhˆ­zVò¹Õ(LüëGFð(oqPÓ Ã#‡n«qí(îGý(¦ûüãÛÒ%¡ûùÖÚfGvÙ®¼=àŠæÒ“ùÒju° í O!<+yx_š (3”¦#+‰7SpáαSx0¿»DGOάéµöÝÀxöáAkßå®ròg@¾¸šGðt'eÑm•¹×½ó½ó_ü²ú{Ô² ap,ì½Ób6{€/ß”H½üúïš+­#~ñÎâ\êñj]—©NâêPÆÕ¯«óf>øõîJ;•SßéÇž<Ç#Ô­kJgbÐ á\0<§:ù«C·Ý+ ΞPÖû«ý7ÊXgdáÕºõ¸Â%¡ÍîZpMw#àä_àÒä~ÀÅW¸õ o³9ËŽŽ2Aiú#–„ŠY#ûÖ6pøè03Û+k””–ÕôÕW ”–Íæ'fêÆ¾ÿ³ÄüŠ Õv…€$ß6çÈ]½Ìzþ³_ÿôrÂ?Ïx©IÅZpîbÛ«f ¦d6ç® AäÚÒòƨ¥?KË6íój9‘8ˆþ»Ÿo¢|ÜÕÚÂx¹?¯ÏiÑgº‘}ÐUFƒñ#×¶n(5£>Y$¸$´“½ª7§Ý—=eÊs½víº,Ï·ÆÚâȶ;#VGÃl± 8çB¸ƒÿèÎf¶5jׯóX ï:s%GÀÀ#ßF&ÞXLlÿm2™¨k¨‘5HçÙÃÌEå2³ñP^q%ðƒ&nÜ€XÆÀ[IûF 6ù7TRܹ§R’V5NmÛxøÜIO^¼½bè·xïãûv%M<|‚0ÙpwJf’ï:˜Ak¶ä[å44®;G:žBç.Ð’µ»Ä±Ÿ¿ý9 ë߃îããYç.Ñg_ï¡ìó—©kÇHš:jÍï ¸…¼–¬Ý)uM5X£ìê€d°jGÏ=G4j`/º}öx±ý—O6 í÷KÖ“‰ÿ—/ÿÏ æS£ðÞÙ‘Ï«Fê²ßÀBΜ½6tM ülÇ'/šŸÕ9`Šã. {]kŽ4a »èóç·x–Š‹„ó?ÚYì+ñ<@UbíùÔÛvŠRë$ß& âÅL=;„P°Q£å ÉTiåÑ%MF¸­Ø’L¡Á&×Mtl€³Ä¼É ªÚµÉw·pÝz˜BìÞ²ƒ¤Î'š çFqDÛýGNºž×>Þ=¸¯k¬u~Õvš>f =¶h6ÉÍ£­IǨSTÄGG†Ñ÷L§Yœ$;2ÌBcâûÒ³ΧQƒ{ÓÒ¯÷²V½B¤ùßÕ;Ù¼Ó,ˆúÅü"Êa/ÅÄÿ¹ÞÝ:Ñâ;fÒÃ7O§½©'hÿÑ“ddÂ=aX?qÙm³Æ‰¼°ÓP>2M/®ëyFø Â|äÏæ'(¤"àÎgå·¿þæ’P{ñEv¡¡ í:íDA&D´X$1Tä»ÅP6˜€Â·Ax< ­>R ÞÁìY&˜I¸…gæ÷ŒÐèüåBZºÑûCÁ 0@O.Ý˜ÈøÑøþ1Ê0/pÞJÚ'u‘ïn‹ ƒ“HzaæàÕll6‡ ÊIUf(ÇOç’•M0þöÞ>Žâ|÷Фө7[–{orïl ¸S &´PBBÈ’|“öKH'„Š)Æ lãÞäÞmÉEîE½\ßÿûÌiN§S±ÊÝéNšWŸÕîm™™}fv÷™wÞyßžuÝïÂÆ;Iö˜Á½x€4´mÿ1JI´R*ïG¤HªÉÎHå削tÕ„!”™šH#ú÷ 7+µö9CÅe•t¾¸Œn¾jÒ›îºnJ½û»zâ0ê×=‹2R('+•öäŸ&w=ºxãöÎÎyáÂÆò©—h˜v ýýžaœÕµ";vW¨Ì SÖ­ÎF™ ´ºð\—„gO=Ëd—#;é=k\¾žÜÉE×xÖ™î5?qxð¶8ÖÈ™Qµ>[;ùs'Gî¹9°ÍV9u0Ë?qž_„)ǤDIû" MP@ãbc)®2yÉJ²P¥£‚vf“Ô¥iþÕãÄG±}Kù¹Có ò½“ ÆÐœdê&p¾Ð€+”ȯÃP”°1òÝ=)dúÂvééá{1ŒÍIð<~–íÁ ’m èx"~\óþæ¥ján¢Ä» ¥kw¥@VZ¢¸ÆåvQÇ×€ô¬!Ópë—žâ=Žý¥Ulž²òOœerÏ„½Š`§Þ˜4–Ocç‡h¿ —Çý8¢‹ßì†pñþLJ QžAKVð Aš„à’°`ìÈ¿ó³ú‘ƒ×%a»pnáç`ÎÑ=Æ,ïVëþã%ÔZYºn7­Ì;Hxxì–£ë$óôËŸR÷øï¿aŠÅ~ò\ ýñ­åô3('Ó«)À6v}øÉº=”·ÿ8k\Ô‹‡à®½,Wxb¨“ ÿøí+ŸRqi¥˜ –o¡!}ºÒ WŒ‚í ƒNÀµò%¶É›Ï‹QA&ömÁ=—Îò& †fÂñ¶ZãÉÎC»vö]ßÝåäg¡ZðsÅå4oÚ(eÞDÀÍ÷"6Û9˹]¬”Û;Kà \¯4Ai" u¨"Ðä[¢âæ¿Èý!]c.ÉÖhÃö{CîŸwE½üâx´ šî_}ë–zÇwàs ûï!}²é‰{¯¥rö«ÿý?¼%NËÎHë %Ô“‰5¾/eLì¥|±q/ž¹H¿zøVî›éÙW–ÊCõÖMåSïä ï¨3 S¤žûüξ7Ý)Ù„®Ÿ z¶!H0d]Ê”µÓ&q. yÄ]V†fСo“€ Ö.-K A;öxµÏòʳÅ웹yf‚Ðz—WÒ}×O¦Çïš%>üŸmØ+“ª·?´}kþ•lן6²Üž‚ÓõÎ ÖxÛøÙƒ×•|KœƒUÆÎ”Ž—€X»ÍpÖ%X­dM°Rbb‚ØÎI‰¥ñ.*bRùâ{«è¥ÅkÙCJ¡ò^ÓHàçxàS\VA¹ÝhX¯L#ð®Àmx+é<´#ùnWG³9ÈÚí‡Xy«Ó@6% ؉Ã^>»)¤YúîîšžLùÌK*«íì­„WV ¶ƒ¿mË6ìò%×-3•­qôùÆ=B9õ9§¥“¤‘”/Fžöò7šp)Y<™ÏãÁãg¨ŠóÁuå#¯ çÚî¦_1ðk´êàãÃÖ„3ÿÖæ¥4à­E.Œ×Á%aþØQ ¹w'<¢è^—„†±u³ÒéœÜÁ„®Mp™Nk×½Yc mZ>/ý»gŠdvò¯ãc)Iæ +÷ͽÌwÚˆ~Ýh Ï6oLâã¨G—T±ì=rš'©£ä¤kïUJ\¶q?}ÿž™bˆÇÞ\ÎZú14€'Íœ:_J+¶ C<ìhf ˆ‘ºtcû½{®›T'[åßÿïsš{ùHªÌ¦÷Wl§ÔD Oœ)£ý|moö—|Õ„Á\¯}^‹Õ ³x?ý74àN§³NtQOdOçªSh“Ó]lƒí#àl‹;÷òÍ"àyf›ë^]Ów×ûíâỳL„ô¬ßÿ°±M´êRìé0Á‚—£•‰ü“÷͡ūvÛ¼;çL§×Yã:h2 åLì6°{ª‰lr3ÿª1Mýç›÷³ö¾þdš:‰¨ACÀ§g¢/ÞÀFÞä½äœMTL˜¤i£®\o¬š©pj\‡UT\TÅMøµº…eµÞYz0ñ ·pŸ„Ò$+N£T‹‰R9*«•;¾9ùNINÛ0?w¥ýw µo~‘|ß4c¬tL¢üã÷ïöý¶ð¨î‹?þªï76æ\6B,0)±°¯|LR†$òsôä}× ­4®ƒ\7u$Íb‚.ßUbgÍ¿©£Ò”Ä·(PB3Û J Ü>k"yË­‰9.Måxm(»uÏsòÅÊ÷¼dÿÿ—[?ºQ( І´oxá¼´÷¦më•ǽ¼±¬µ8¶‡8ÿ߆³ 2/Íà9§×~Óë3PybÖnžIž;,‡Þ]¾•æMIÇΉ—K¿^s‹;nhµ¿yK};<™Æ>>â}„}­‚|]Á®¡Z*ÐÖß4}O¬4Qßné´—ƒ)ÀÕSsÄÂö€7ר»19ÿtýÑR£õÍA/8çH¢«ÇnõvD?ì–a>Ë4«9¤:lÃ㙄§{ܽ¦Çã=#á”7w×zŽøÉÞt¨óÇÇO&À‹# ²m706 Ú ¬í†ù4ß âµÚo5ù2ÔuIéwFòÝüaBÒÄL–„myc‚g2|ûŸÛù–Çc"T7•¼&”ëAÏl¿ƒß§W"~㸠ºñG¡Ì/Øi+lDC˜?8àø+Þ,ôoêóç?£-\XK…C˜w¤½^PjvµÝ¼NÚ-ü23{îNÖ:0±ÝÍ6Ù¹ p&ù¥’E²…_äÑmWmÒ¤vzÉlJ€ó`N’K Ü@ù ®[»3_h(6ì>B£‡ô‡?ør­ãýLîœ2¢¯Øöÿ—“éHƒ}VÖx@« $áñ?Wm‡JJ°÷þ®qOÈæ&6&à6&àN&à.¶—Ä„§öòqod­³”Ô«Ü ùZâ6ÌJ€—ô ›ïxžÌŒ¦'8•tùîõª»ì±à©$1Ù²F¡Á¯á?í{|èîPåŠtkßÌ¡H]¥T"Æ%¡A;ÇãïòÞÚUŽB˜Ø†Ià poÁ)Z0k¼,[³Ö§/”Ò>\O×NÉå =š¼fhŸn4{2Ü6.F~€ÃÏ+ʆôýåÊñƒè•ÙÝw®=íÕ½o.ãˆe¹lƒIOMð¿Ä·¨ðPaE@’KF±ÍuŽmø·Xù¶³í¿ÃáàvÀp-ñ'à5ߌ°”Ùh­ÕzgfÔº Uæ …‰‘G )ƒy ð ‡.A¼±ÇA¾q’Ž€"ß¿ŽC}‡–QsÍZaËoSzjÌÏBg°ÓW<؈†0½HqI¨é&žíwÊ6­íj.áÎá{ßød3Á+JßœLž-Þ°”3L†Ýnoç¶wf&ÈÿZ´VØÃßöQ6-dñä0¤ÕéÃùƒ`mæ¨fƒzu¡õ; ê$³v{>ûgñ]–Íù.eÛmØ›Ã<3Ìï¹vbÙ꾓ÕFÄ# 5·°­$“íÀñæºÅ<9QpHØ ¸µÖ¬#=­Ö„)”ÀJM¶¯}ýðÞÙ"”/ü¿öñFÚ´÷X=ã_æÀ<Æ îÉ®¤z6Z&u | ®@*A(aN-/&]J³Íw¸ ¸!¶Ö×/&=†C$ÇZ.ÀFNÊ”ûÂQ•Gd  ÈwdÔC4—.$OæÉQš°¥ã×ËÑ„Dë7£õžšc¿ÑdûbAÀ¹!¶‹KBÖ|_`ò-Œ4'Q‡€GzÕ™˜x7$ÝC%•ÕBzêb)!pÐÕ7tªÚeHb ² ñ'Þò·8Æs­çØ^‡K€…‰‹ü­Ö E¾Û^ßx—`2w{{iû´>…mönTabûPˆFnÝå¸;ïë#êN²j}òa¿Rð°CÞö ë»$¬ú§útÛSn~ lû]ÌôÂ{Û“Úü+;÷™C9D0&^~±i¿ðŽÈcs.&ìÁ;72óî#‚xÖt€0´ôJáD@‘ïà ýÒ«ióžúÞ]³i`/ïÄýà¤Üp*ûžæ¹J úoøŠÐîÝvÆE[íµAï<Žê§ÿhâúÐæÚÔVÑ…6O•z€KÂÚd´‡a‡]û;ô[¬Û*‘¹p§¼M\’テ‰`šÑQe:G'{àÆËèñ»fÒC󦆅|#a ›FH¬;*¶ê¾ ÈA@‘ïàÔÞß;£îõxc µ¸xîÊ‹ï,§’òªPgÕ¬ôKl:=¾¬’U~5£jnǦ¢—¾V¥c³ Ú“”¼…€EÊépI˜öôs¬…Îä¡©žGÏã²½¶òiT,àšAo5! ”X6Ý(+·îV¿Û€@GPë™U;¯!ÜÛ¼ºT! PÔA@‘ï:p´éÇöƒÇE¯k9Êåk­£;fOâÑ,¯þ´ƒÏ}²n§pÁ 7µ0}ìÁÑœ¿q땯_¯}¼ŽÍ/RVZ_7Yx ƒ9Ëÿýå]ºõê ´tÝŽÔ\Ic÷¦¯#&JÿéÍÏEDÌçÿ÷)™X#öëoßÚ¦ò·åb'{-ûÖGtªÜ;‰Ý¨»\Å›>øvÑáÞmI¼¯Uðv®€Öf—„MQ^¯{ôGävXÖ àR<†Vp™„ÿÚÂ*+l6ªf?ÊJÚŽp„§—¤øZwtmOU¥ P(F@‘ï†qií^h½GèAÃûu'—ÛE»Ÿð%õÁÊ<&ç&úÍ#óih_o‰¯Ý퀀"ßÁ}˾#¥釫¶Ó'kw s‘²*›0éÚ7›VlÙ'Þí«¶D¥8pôŒðÂôò’5¾BÅsôÙçJ(-Ù뎴l)‰ñq5÷já^;ýck­)ê#â(žïi¯,pX+啨n. 5ž„É,\ˆÖz ¸|óØlÚ¶ëL‹Aôìp¸ö>9µ/‰(¯®°ø8v†F÷͸Âä^bö¨  ‹€"ß¡©Z˜Ÿôî–ACúxÍK`¿ýéú]´uÿQš2rÍž2‚þÆ&ò{þšIÃ)·¿Wië¥wßýÊL¡Ýö/Ò€Àt%em¡“~¾²vèôÞfúÑT ½p4KÛú2)”ÖcWÂ%!Sª<†*‹Ã!\†¼lºÇãÓ€ó£Ü&h½½‘ñ®Ú²º["kÁ9²åû+·“ÃéùýtÄ €Û¢/·S|Œ‰†öÊôa,}TwÄ{V÷¤P„E¾Cƒùùâ2:Âf$³§äÒÓLjeÛeì•Í. ˆL—oÞGý»gÑå£QvF2™ù 雓E ¬Õþxíav‚ïÁîüæ™NcÂ&5Ÿ¡*6e‘„]$â.ºéÑO*ÙÉ·7£!™FúýÌâ@ÃNïUÊ. ÿX{áqIh4À¸W4½õ\jcAÀEï˜Љaòͤ±[¼‡Î•ÑÂϷȬԺ,ü '^x“žþ»¿~Ët:u¾„~ÌO¾ó»WéíeÅ| ÿ´Ú¶ZbiúØÁ|þ&zœÓ³Ù ô}‡˜|ß÷A9U:¼ì»K‚F/^—@l•Ú!%2Ç:$Ô¡»)vIø»$|6œ. upµ-Dk–BœÌ ÓõAxjCïØØ~áðK©ñÕü Úiçaîµ/ÝHó¯'&¡\®~ M:-Àm@–•zd& \/pÞMHM= £zuÕÄuêB@!ÐÉPä;´~»Ä(SG äÉ•ÙkÉqZ¾y/ý?v»n»ÃIw%­Ý~ˆú÷è"&Pþâ›7û<ŠYøûê&¶?þjd¾íª:¿oŸ5‘nš1–÷iaùîdòýU&ßÅÕÞÏNBŒF¿>‘²¬WOÜqï¬NSêØ?ÚÃ%¡î6ø4àü¸\j¦È¹uw•ÍéôýFÍHÄ8&Þq.!³ãbã(+Þ@éf‡ “Y¸‚‡ã.tìÊlãÝŸ?¿½‚ÝT¤Þ©14°[ŠÀ¸_hÀ2A±9œì]ÕUÝ@öuêªãj—B@!Ð Pä;2*ÝãñpTå*af²ïÈ):zê‚Øã_:ïZòí¤ém؈ÃóJ¨%|'ÆjôÒ 4(=ôy‡úÞšJ_iÀ›B'ŠŽÁ%¡Ã¦ÿÇ—¸N½. ûååí Õ-˜,–rg•×»$Lln>§û\Ii%HO “C³Ù,ˆ·ÕOv›ìv;9]NÊtW’™lt‘)ÿ‹ï­¢A½º°w”ž4¸w—V½Tš[Öh9~¾áj^cà’ ¶óÒMx'‰Kà‰ ð•&(÷VRV©;ív_§*ð¸ú­P($Š|K$Úw=r`O:s±Œ>ZÍvޥ唒hZëËÙ­`´l¾av"5ß ßÿž›@¹]:>=íøw-­°å ·KžÕÕÒ©®‘×§Ñ¥ï$ïTyµ]«b‚ —H/7pO›5à¼/ÁjeòíàÉ—Nr¹Üì‚ ¯ªÉb¯¦b§òÙ¾ D=øü¤ ›­t¾¦ŒðÄeÕ´“dˆÌ<ž•ë¦î)±”œ”H‰‰ dM° <+ð…‡àí/¨ ÔIuU%†Ð9’‹ÿij»;}öðħ¢ÒJ*a{M ‡[F•ׄ àŒŸ{5|ωleyIIŒ§t"Ö7§žG†pã®üŽŸ¾H{د2ž ¤·£¦îñpE³”Ùu‚­îš‰a%å x#½õ¬ñÚ»ÃQÿ ¨+ c[fø›V<fMNX¢Q"ßÐ|Ï ß{ª=qëwÙž‡1ïpº$Ôòòœ‡ÇŽr2óCÔ“>~Œ¶pa­Çüú÷-HÝÙÇ×$¥§? ‰‰Ãûú΂Y&aÂþ['“o·[’oi¦b#ÄÒÜUTÍœ¢Úc$¸h«à"Ôùûï`’?㎘ËÀž¤’gdM·EîääDJbžÎxWàÛù ê‚g¹k…‡lç$ÑÛ‘<Ú¹BØkÞœNÛeî£yû©”‰ê+‘G â(¶Ü~îZûlà™ ·T–Ùé8‹—s4ÖVnd|Æø¡4cün“kˆ Ô=ìrAºÛ»îCQ×Vf £ºÔ¶)o~_7œ[úÿr›¨ÿ+¹îgŒ£rŠ‚4ŒZÇß{à‚׿»ÄæýÌH³“ÎB¾Qêõw v—„‡ÇŒb—„úX?—„O‡ê5]¯àGG¸ ßÌE×™„ «7?!Òï muÿ»ýTußüÚõ?ß[ÿè¸xt]Íõ÷”T24?5uf´ ðO-ÿûõµÎ¶² ôë;¾·“ÆêGðƉÒýpIX0vä‹Ü¹ü nAw{¾Ã«pÒµ ¯¥«LÝž¦8Šm*ˆ£¨è\ùíy¿ÓÆŒÿÍ+KÖèÞ<­Ž)Š$á‚h² Ü«á¡x¶où4ïÍ;qi†"ÍVaGŒ@$7ðÈLs`^‚ \ ÷ÜQñÆ~oì£ì¤aëÚ•¯TU•s]’½f‘$\iÀx"É7:‚»Ò’Õ;»s΄ÎV»Bo×Rbíqk“£Êmf'(;È·ªû†j¹ñ}þõ¿hÅ6ê’šD¹z4ú¾j<%u$Ò€²ûW«ªèÝøÄxe»ü'ûùF°Î*Š€wÀš7ÇÐßí-ä. ù™ª”ðt‚ JS"MP@îl«—.^“˜šúo?ÈÝýž¹S5© Á”îò@¥‹BLxðj¿ÝB®³ö CI›*D´6ba/ŽÐ€oQ³è¨€t›…VÜëñø 4ß ß[öÑò÷î|Ûž73ÞÅÆkÔF+gŒº -VTVÓ;‚<& ÍwCçª}M#€ƒsÅåôúÒõôÔCóDg²é+"ã(ÞC(3LŽpJZ‡€¬ÿ79êbÿîìZÖ‚ù*krnëéWÙXíö½e´âH­¨IÝMô§9 „`;YßcýŽ“ùcF¾Ãyn§4B þµ`ß*sÁ ©qöx —ҀܤO!È^åÇÿ{ùÙ îF¼çôÅb}Á¬I†@›p¯¶—#e2©„¸ ÞÂæäÛ;QdÒY8îÕ«ÕöÚÇãced¢ ŒÄ6óÆ´Þ°ù†Ù 4ßù{v~°|ÑÂO89t¤ Çuƒ:B]¡Î” €ö³;Â`"a)»‚¼cæxev€Ss¢ã=oÚHáãDfMÉí·¹×·Çyh(kqY}ƒm¾¥ò =ÊíyÖ©ÿ-{Ù¥^®o”/Úï­³—¿¨J§o|\A»ÎbðÛ+sÆÒ¯¯²°ÛÜÎM¾†"à5¢£­ü]r3¿³pòäôX¿¾(˜÷YGn¼¤\p¡çr€ðÅ~òæ«o¿òšÓž S¾ùÜ«Ÿ¤ ïß]?¬¯–Û?Gø —ÚpJá …í™1é@©õ–Ä[®ƒy‘–ð€-x6ØÈI™r¿¹áç®áí.=.gù–U+ÞØ±~5Ü¢Q k,þpEÀAÄK“oLÞ°÷¨ðx›V%­GøÁsÈJv݈ ™ µãÖ§Ü+e@YQfU÷mÇWÖÿ¡~åØA>‚|ßµ=‡öKíÅÿÛä¿Ý~¥ }Î{ιè»Ëªèt¹HÕè¡qzl’%ô™GIŠ€GIEµ´˜Â%áØ‘[Y‡9†x‹ÃQ xP]Ö¸!Eã<.¥Çyxц!´­ì½šŒ›W|¶fï¦û§Ì¹n.»Ã»†Ib2¿xõöꑚdÑâØ¬"P_b¿ÏïH¿?J¿å½ÚœNQGd‡ñÑÜnWùÑý{W­ÿôãÕÕ•’t—òùXÊxA nPGò­É›J$^í·Kh¿ 8 TE•]xõÇÕºõÀ;Ê›ËÎÐÑ“ç©o¬ˆ5C@@1ò1gÊðÖß°º²¨ÿÇΰ‡œó4 W¶P*D£) ¾EXޱï{b’A¸`²äÓhø6ê@Ða~\¨òPa™‡ÒÈH Z9 š7¾Ý?&QŒbËÚÆ¾]ˆK܈"à—(š4ÃxvùËâBà’°Ž Ê¥mÀQ -8Œ“aè'Tº••ežÏÞyãmþýÑàQcöí7`l?ƒáê3ñM7ÅPyLìI—ÁP;ƒƒOTÒ4.·Ën¯®.­®¨(:vpÿþƒ»¶å+`^-7È6H8ÈwIÍ6ÌO¤ýwÿDð¶PðQÚoeãQ H îÝ¥…)©ÓB8Ï='¨W· Ÿ´¡sÛkŸl(£ªûàÖ‚¬ÿ}GN‰úù–D-¸9…&5´ ìå›÷ÓÊ­lšV^-ÚH{á Í^:ÕôxÒ9:ªMDk¾@•6m_{ˆ~²ÃBSG  écó\ÚyJ•ˆ+~é¶µg„Ü%¡¦yc 3B̦¡ÍnŽøkÁùæ‹ü‰¹}ÿö¼—Ÿ.Ü-.Öàæ—Z©Ç}ä¯'N?[“¸¼¦9yuæs$ÞX¤ý=:1 ÚÒäšoq¥ýfš/ùróäK§0?)æH‡¥c%mG8‚¬\,©ó;`rifÙPF”UÕ}Ûë]¦,ñ<],­ÏæüDbåõ_cT„Gn‚p WA¸j@òÂôÑš´–Mn¹j, ë›ÃÎj‰¸?¦a[ð\Ë^—„£ÿΑ%ÿ·l—„Ì}3+˜í5—€£( „rJ4ˆ¢ü rhK3™’ÆZ㯔¬|OyõbÞ_Ì‹"ß B Db òº’ÚopnðªšØã8êBIà#‹‰¿ÐrÁfy•’8¼¼’à!Èx–TT œ74 ‘$²  Œ(«’à"€ç©”= á‹‹õZ‹´6àÇè¡M|ξàáJQáòG§v«:A¸8ÓK‹×² ×0¶ù|I—¹µ)u¬-EÀ;V}Ö»sŒçEvIø$¥q]ëÓòÇŽÍí——·«Þ‰­Ø¡‘îÃðk¨%mIC>ÿmÀªõÈ™Ñ%Æœâ䗛ͣŸ[\\úaÍy’€Ë5ïVÒµÕR«ý‡y‰èäð$¦(øý8ò-¯åM%þ`ò¯«†€Ã½“=¡$Å·¤Ùû§¦¶B –'YÛyØ8Ë· ×^ûd@QV%ÁE˜br3x¤¶yÇ>òÍQPß_±Ub’À\b ·ßšï ÂôñÚÝ¢³=cüP[‹´‘¯KÜR««7H«¡‹Ž Cé’P'“6¯Â”˜–¶%=éê‰øL$&X­w°‚ùÉq›óåJ—ëé– o*¹ÀW.ÀVjÁA¶Ññ‘klã˜Ò|3 >¶Òü®0á% bcå¦ýg‰y¤|Œey¼îOU?5TmJGGWD9†›Yïs)mÀÿž¡ùÞu¨­Ü¦1ùÓŒm¸ž”A¸–mÜOéIV1°§¸$<'ß6ã¶ëÒRÒT/µ#ò0µ?¹Üú””l]ê oBøsÔyÖ ~øšaíZ™›;%Ù@CùÕËœª>³_ü'‹VÚo£"‚ÄW’pIÄåoy¼IvîS¤ùü€3;èÜ€„èî%Þ‘&²  ŒJBƒ€¬tËŒÑur}úåO©¨¬’î¿aŠÐ°×9ðÃÅ$õpá9.G×€#ÍûÙ’w:õݳR).&ø´QaÊgÞ+;âø=M~à›‡vý³”¼>&v\ò[£ÆnÛë’°-7 7„òzÖZ´v¦HÂU]þ§aŶüÚÑ;oÇ ™¶Z+ÚöÖÄ|eÖzøÖi4mÜ ÊÛwŒv:Ñ($¹ýr詯]ß ùnô¢=ÐÞ¸ûÃÒ^e?´}÷Ž«Q+e­ë§ë÷ø«Þöí׌#´—Ž"í…{CøÁü“C,¨­˜ÐB¨zÿû+<[LÕvßÀrCE¨³í×Å«pt€è¿úÑzÖúÃ{mðEa b,À e$O ÁïÊ«d* #l—„lßà{S°2 ÕmIŸ>Ý”_^ümyúÒ~K(Ô:‚À‡²v oÁ2R('3…ztIcÛÓCtìôE1üýþŠí”™DÅeU´q÷QúêÜɼ`ñªôÄ]3Ù†Ò@ù¬¹zŸ]¥UV;d¾–åGÿÔùRZ±åâ9´åF“ºe¤Ð=×Mf ï²ãÓ|NzŠ•æM-<„ãÎ%ÎáÈ«%y´G¹âã(;#Y,…g‹h˾ã¢È(ËÓ¬!‡¦ûcö"q‘5åO~u6-þr§˜£pËUcÄyŸmÜGëw°æÒ,F>üïwËÞ£´iÏQ:}±”¬¬u„àUãÓøa½EGï‹Í¨ªÚNzv¡›¯E–0iÍ%ÎXG’H?ðÔÖ@L½YÓ{ääÊç¥÷Lq›;¹c mð¶…unîÀº8[\Nï/ççšM7~ûò'4ˆëè¦+GFÍ–¬ÞI§Î—PFJ‚xæ'è'Òkê]’ýÎçyt¨ð<® íÓæ\6œ'E½´d+ÿÏE«Å;åÉ{g7™OÂ7ó‚0UÚm„ Ñྙ·ÕäiJÞ$<ï \Ê»âç . Óäï–®5?/(lXÞj^PQ:Ÿ¯ïŽüyXïL¿¾ÞhiYÔù ŽŒ>œ;ž VíjFî)nÑ8A°;KWOLY©‰l?é„\R—wù#Óšnb]TZEçøÃ-åÓõ»9´‘ž¼oŽ0mÁ~ៗ×ï.ßÊÑÍôÀ¼©lò,üËëÔ:|Àž{OÁ)a²b6eD__æèxýwñ:AŒçL.ö£MT0i†œ¾PÂQ÷qÈïntÍÄ!Â-¥š‰Û{+·Ó´±é‰{fñ+˜h“.ï²J½Ídloßͱ3LÐ7î>"/í”kÙ)€fÖ„@Lˆu1¤o6û?éÃsçá“4œG°ü¥±ºHMЧÞ9é””h¡Û¯GSFzIvB|, g£oÞz ë×Éø.Öª{mþ›z˜¸³ž“•º‰t¢ì8XH»Ÿ #³ñQ{ˆ"¡!/HSùˆZøŠ§Ó#b,úoaRQsº"àQSUÁ)(\²–c+RãŠÅî¬~°µ)Œ°÷ ë[o‚¢ëß­MÇðWmáÂÈ!$ ªÖ 0"°2ï“ïí´Ž'PfðŒä_îˆÊøM6O™:²=»Þv“†áòk§æÒˆ9tËUuíM¡}ëÇ‘é`×Ù—mÂáÛ\LÒ;v¦ˆ&²j‚%†ÆéŵRŸ=²/sµr±ï"éø„MOb¹žqýûKöŸ|ל‰4r Ðaøbûàó‚œÃÎݦŽîï;~”GQ0Ñm@,ŽèG]ÓÉÎÞ= ûž¦ÔÄx¶ïF)Lð±ñ®|ÄCëÜ"MP‚¡™w»<Âw êÑuût˨rcu‘lµPrBzÛ`§Q“û.<ßàŒa­[f²H6àÈ ÒX>â`kÿñˆb, “£LPZ ¢º.¢ðwIÈþ»ÖçÏŽI/<‹´H‚a‚rtü¨)$H,²öÛO/¶¨êd…@'@à§ |D_|ok£·û4ÕÊn,,úñ3Å6a€ÀÖ25YLµ¿A¾ÖîÌþv7°†s4m4î o–'~ã®=]TVçzßAµ2.c25{ò0‚w›yäïï­¦Ÿ±?4–Á}Ÿ€‡úïÆ¦KR`Æ$æL6KùhÍn6cJ¦#ìçþ.‡g/¬ÿÓ[+åéµþ©j÷tª-©V1A0Az{ta­¯› ¸#¼»à4aþ†6~ÒÒºØä û(ßOçK*ØÅû¬ƒÌ^ê]PÆ#'0i9væ"%1¹‡=v{‹MPŽLÕ›ó»Iæi6è/ÈmµV(j8ÏvÛ'ÎÓžL·—5\þZÍÚ³êo!L6ˆÖjþ¸B{¾zûa1Ì-Ï\»=_.˜šà-É|Ï®édµÄòį}ÂìÄÁZºýlg®$ü”WÙ…ùÏn6Á„YñÌoÇêR¥éÇ“ûàÆ¶ß0EÚägÇ ‚³ŽG?Æ é)ÌW`r"eXŸlá ~¢¡y‡]ùÑSäáN½nÁØkoæúAª}s¼“1ýÓoª.²xr6æàن٘‹µÇ\W0q°)Ç—ìêPÊ¥ÞÐxcÂ/"Q» &zKÉ´Ð§Îæ8ÙT©±|ä5­]Kâlœ[[žP^×*­e( ¤ÒpIhwhOr7“Û€×%a¿¼¼]-ÉI·ÓïUÔâ¶äqêð»L¨oøá^Ökóö¦ýkµ¤pê\…@B~½!ÙL¨a’0u”wÂUsnq›/,þrm?T(lN¥ §Lo)Ûï=rZØŠ‚tßÓ°z³êÝlWüOÄ„/j<* bÏ*üVF@ž± #5¨WWºžíù19¶9ö¼ðù½ð‹­BË}Õø!®Åw»yæ;kÑ?`9 WððÑŽoÍŸÎZÙlaö²lý^ZÂf/8>mÌÑ.Âxëí’UØX‹Ùåªí„¸ƒzve;krtoðÙjª.†ò$ÎM{ŽÑoþË^P¸mÀw8&ÕÂÇ8.Mbÿ=º¤úî ©wÁäÜ~ôÖg[è§_,ÚÅå£Ð6cÀeâäܾ¢-|Àï’Ÿ=x}“ùø2TM"ÐbÒÔdjê`Ô —„‡ÇŒ|— |; Íñ½ ÿ¶›+6Aa;p!LÄ[¤?Ùe‰¥¶Ê‘3Ä`0ÞÈ&ÇÑQâî™ì~Ìëmn씄'Ù;IcRÜPÛðo˜P‹ š¨[˜ª ˜Ót&h7–m-az‚và/Ͼö»¤ôv¹’}Îc©` <¿´9o¬<ѾÿoýúML2j÷8<žÙ;춸ÀûiN‡'ðšÆ~K58óŸ_~ãFß©À:°^«‹K}û¶éB+ë WLÓØœŒ›‡˜@éK˜7{àæ‚É‘'u7lçÆi#iΔa|¦VÓ™k<¤, &îÁ*S°ÒQñ‘;ÅvÅw†þ¢ˆ·?ѹíßñò¿s„Tw{ܶL6kðhÞ;ªN™Sa×îµ»ÝTyÜ}‹Ùlã<{ iLðœ´'!l¬.ý³Ã¶¼1iì] Ïoˆ|Ëc1ÜQó—¦òñ?¯%Û°ð‘8·'Ö-)skÏ­‹fkSQ×E%pIxxìÈ­¬„à ]º$ü]soÆ`b x¸®7¦þÔS†ü%ïûÛ¿ÀÚ?k–æ–@§P´¡lf€‰—_lÚOÅå•”Ä.Íæ\6L¸lmšêºèA`Îä¡´Šç¼¹l3Û{¨Kz"}ýæ+(ÝÏÅeôÜMóKªk.Ð<70Ë»Ïá ÙÝèx„Üf*×Ó‚7?u¦BàÒ(~iŒ:øÚ¹¿ù_ÜdK]²îÛ?f³Ç§ó¿ñ¶¾È“U.cc,¯ŠmõO!Á`È¿v‰à‚¶ h0G& -¸,ä§JœCžQ 3ˆÔrµð6Äéð¨sÃ#[siȯ‘8c,9†a&!:½Èf/Õru«Qˆ@CDáš1„¯$x ¨PŒ©Ö­^C¸/·–¥ä_x‹Quß2üšs6êß\ãß\žï»Ü×ÜuþرɺæZ éÚýºÇ9ÁûÙ‘áÚ\× †/‹íö×)<µ|gi)ìoºÞÕÜ Ôy 6  xÀë—Â%aÁØÑ÷èúÿá~tç^½×œ{së&fR Þ<ðÃcÆŒÕu÷å"}MsšÈø—æä¥ÎQDqLÂÊÊk}jGZù¢±<þÒ3+9â‹ne¯ÇÏ•F|9£­€°GÏLj<øKsî‡Í)µcãFMw3éæ9J·‡,l¹]çR&õÇyÞÒËšY{ið†í…|Рæ$Õ5€¯s•ú¡.[ê7•Z#—„<¶îUåéútÖä6§¸f­Ö !ÑiÖX¡¦y“i³Õ:“F¸@IDAT÷[½óòNËßj­ˆt “KOtª°Ù¨ÚîˆôbGEù€cEµÃ¢Ç Œ#µÐ¨”eUu¼ZõÏÏ“•½yÈg¬%©Ÿ?¾Ïiú¿‚q£ò9BÜr&Ýwan“_vÞ~‹ç#Ïê;w^Ÿ¾y;~ÚgÃö£~ÇÕ¦B ¬(V¸#33¸$d8¸$Rã’PþltíòÔNµfmBãSÇkR8~Ùèn¬‡¸M&hÔŒÏËmµVD:’0‡ÛLJOˆ…Ý(í?ªÔ£î€#ð\XG¯î´Ó±²ºZË`äÑÖ4d@4RU÷mE³îõ²þ»¥qÐüÕtvëžU÷ר””˜/‡ ¹yÿØ‘ÛÜŽ£¬èþ%×Kÿ³8íü¸>bŽOÌî¿uÇ&ÞË´§žºä÷Ê? µ­Ê%¨Faš­qIh0¸ìZ¸F4ߖܸvÚôoóWKLÖä×ë*Ö~o Æy(Pÿº<Ÿ‡•ö[‚¡ÖQ´ÞF&ÞXLlÿm2™¨K¼‘pˆö#'U˜î¶T"ð;xì ·%2éâ× cÍ´ÿ‚›^ØPM×¾^J7½UFϳё’&_5m)Æ%¯õoCº§sÝŸQuIÔ.}êXöʰŠgK>gÀ;PRª«úÜšžú· “ù+ÒSäq¦ß&ìËøÚ}³ºuë¿uÛ#ÑD¾å} Œüc_¹ŠæMI%UôÙÆ}òZw0”¼ƒUhÛn§e. ÝF¼–ãij̛ü*:¶{yh0M”OÓò{Ï·˜ò¢F1Ñ6XÕÕQ€ÔÄyÉ·‰bbbx1S÷Ô8:WYIï¯ÜÎé®â¢þfÃ|§›}¹âcMôÓ½)ß‘HëÏÇÑÆsrèµZMñýª!”a¤9ýcªl#8 ø¾Â ¢ìßš¯ê¾µÍFÖœÙ@³“ų…Î-ž5‰¹Ú&§Žnþðõh†ÿÆÍ/ålÞŒ •Q-ˆ^Ú5=Y,Õ6-Y½KD£-àNÊæ=Giâð¾´xÕÒ»«ÐŽcäà‹ÍûD@%„¿yÆhŸ/÷³EeÜ>wÐI°•ÊÑngOFm®¨œÞ]±•NŸ/¥ô+“ýѳªRÊõáª]”ò%ÆÇQnÿºzÂF÷G5ÐPøúÝË(”*Bû —„¬v:ÜY£Ý+?ÿà M•Äà6øù Ó5AÁ¬tVNþrÀO3>/Œšqÿ6Ï1Qf”÷ ¤uà¹Áó3¸‹U”~à+™0¿ùéf*à’‰3ÖYïȵۊ{ƒKB»C{’'Kšx. ûååíj()—G÷ ðsÒèKPÓõÇäç’5ëÿÊZ¹²¢¡ôÔ>…@;# ›)yœîs%¥•øíûàcbh6›ñ¶ZãÉn³“Ýn§î.'Ï‚¨dâ\q9Í›6ŠúäÔ ¶×η9ÙÃæäálq dÍgnï,žèÐ_i~ Klá©Û âíMÆj(»Ëå¢]ù'èߪ{Yk¯Qÿ0Û:ÇšïÉFêß5‰¸þ›jH­¸¬R/ò臙|W¤î{vöí§? µxO¯º|ô@6éJÉVOŠDƒÙôDJIE5uÍð™ÁS·šíb&ÑRºg¥ÊM±Î?qž?í:½ýYí¨ &|žæº¸jü`²±¦ý5V$dg$ ­:ÌZÛ_'á6þ€ø³ïuëKÍwßβ¡xôW$Z¬«õßnñݱKÂ3ûÇŒ|I³pÈQÄ`:â›<éŸ`5‘9†ä¯5I©Ù¹k̘a.ëüäÉ]jðüµ¡ójNoéÊ—9_ˆmÿß-MK¯ð!à´ÛO•WÛµ*&Øñlfñ’/k8YÎû¬V&ßv2sSŽÇC1e6A*_|o êÕ…=dôäfáÑÀ—x'Ý€Ÿg¸šƒ×L\e/¦¹Ý¨wT¬ V'p¾F& }xÛ“Œ7Ö†õʤ86“9p¶‚TÝ7ÜÀë?ƨQ¿TugÍwbbÂ%ÛžE<“¶ªJa&Ùp.áÙëïä5˜bµÄÒm<’Ò˜À4GJF²•vâ‘·9_R.¶Ø< f(ó%¾[üà±1^Ú÷56?Éa/+r+›²LÙÞùb+ýï“Môƒ{g±iP 5´?ðÚÖþ–>à3ãëv6Z›^´\§x´ÔTm9á~àûOÓ Ú,¦Þ½xÜ(›ƒÚx»jÏkõÖ_ޤôŠ2q=+ý¾úõ«ç ó2ä¥/ÝØí)é•ÞÞi2å~mö-+|k6^«(`qx•ØÍ1E“þûµkæžÖªßÒ+xœí4ÓîcL˜>{å…ß KŽEÈ[…ªºmçLá±µIééîb³Lz’‚!qLƒý7´uN&ßnöÅéaò ÁñÄ*«òPAáYA41ŠšÀÄ2‰½ÈŸL¯3¬1´\Æšº &P诃xuO2RßÌ$JMM¤¤$^@ÀOà |Méµd|6OàœÕJ׆µ>ÜRØ5aÁù2:¶ºèdtöºG½Ùj꿲¦þÙÓ uáØ”ÝSb(‘;^ÉÉÍkx1ŸèøÁý;9Yù~Ç:¬‚NX¯®©´~ïqÑ¡lO¤¹“¿zûaZ³#Ÿ'ev¡U[ “”ž]ÓÅ;)Öl¢•[е—å *09Á1|LÜœ;uYy²eÁ© 4˜ÓÚä Ïð ·‡ýºgŠ6 pÛ,à¥ø4ví |å¬ô#5EÀ#µfê—K{è¡§,î4íþ~}W7ºòw]·Û=‰öj-Æåô™ƒÔ¿´{jõèÆîeÅ“M¡æ<£Û•Ò³øÂå žWsN¼Ë‘Éçd6xN+v:LfOy¬E¯bcQƒ)þçýðçgìNçŸmÙý—•+a C%oEê’Nˆ€ø°ùÑû{Œ}qóž‚4&ྦྷÁ§e¢Ko9ä%fl¢bÂ$Muå¡Ü »›*œkÉÙ®²¨ŠÏõ%Õi …'9Œ,gÅi”j1QjB“m‹Ðz‚|§$'‹m˜ŸÀ»LSÚï¦@k)ÿ»7„7•–’ñKµ˜#õ`oÅ<¤^\íb{õj*)®fBÔTé;Þ167¤2›NE6›5Uñ”ŠóU:¥Ç›hþÈDJeïÐ|7· ð³¨{Üî²Ý[6Àö[¾×% €’f¥$°×žö÷Ûî¹WŒ${ {F§“¯ÌžHq5ZîÛ®Ǧ>ÛèÙ×–±}µîœ3MKRèî9鞈ùÛW>¸eðý ºk&í;zFŒP¹xg&wŽç\6\ol°@Ǩb*$ñCŒ÷(SðNAÂV m:†ûðÓY<ã_L¼»õ¹pVzò8õ;J‹s:kÇ£B[†HL]t:lð7+¼Ïåâÿ̢ɅL$ùö>딓JÛ ¼~àƒ5ßcΔᄥ!ÁÄK9ùÒÿ8ÌE°ØØ <›øËð~ÝKeµƒ  7Õ¯FyŸ¸{¦poˆóãxâ3ä&ö–‚£’Ä7µ_\ÔÆ^ðg)'1 „ gÞN¸×ìO¾KÚ˜MD^®xDV‹¯PøŠüÑÏ¿ËÁ)~—V^ª_³gõ(¾àýºùNëÜÜ ¡a§ŽaÑ S3hÙ°1Yžä”wïþÞ“?}õ÷¿ý#£x,—Z“Î ˜ºû¦@÷/½»ðÖo>rûŸlH}ò¾¹†Œ×%ïï÷„lnbcn«!_˜ —`0Sé ä»".ø˜Â¬xIï107 }<Û|bé Žƒ|Ëüåº12¾æ¸ƒl5q p®ô¦Ò\͸jáÆ×x6ÒÓ 4¼·–æ{hÅq~¼L±ä4ÇÒ+ãh¯#†~<#²¹3ÖX€ç~=—³|íÇK>çÜPkX$ Ç3A££e(Ûý8Q1~àÉ·? ð”ÒHâxÌŸ|ûkl¿ÿ9-ÙFÝb2.ÌÒº%²ëI~O È™Ä8”/Ôç*j„[Ÿ¾ ßüð©Ç˜|?3äô }ή-³Ûï‹Ñú´;ì•Ü9¡{Ö/7.ͧïËîþ«;}Âøúžùß°hÃ ï°µßæ“Ú4|Ø]%%*öæmz^?é¯,Y£?xó4_Ç·–t›¼D“‰†WÃCñ‹ ßb‚&ÏÀM:¯CÀ;²œ¡"1‚F U˜—ÀÇ3H8´à˜p Ò…ý8Ž-® ¥Ô%ãVŸkÃÖqy¨w±­Ú€¯êd5JŒÐÊ1Ò¬r#ýu››NU³©“ðíűtßÇnzâr¢Ûr½°À6€gïÔùÊ[½òõª*žLàU¨H¥ žUùÜúòoņLC®ë%ra9QD €‘½RiÃáóÂ&Ì;”´ é¾w¢Îï83×¼ÄÄ*YGXGµ(¹Õg¸÷ñÍ6ŒOƒ|ß°}Ch¿L‘‹C‹K†NНI á?›wß×-úÏßÙK¯i’ÒâtÕtÐÐËÅÞ¾nÙG’ÒR_åí{é=Òï™;U“špù1ÆZjÆàB$f¼£@ûípØÄBÞ´à’¬À–pIZðaé6 ­¸×ãI{hº‚AÆUà'£i¨ tË6Ñ”!&zi‡›ÞÚÏšÑDUìñökôùq~5#žµáÞO´£ ß[öÑïÞ±dû:ÑèU¤@™‚E’ð¶jÀ›Mä`R匫Gf2û¯ªñ¯«´…Àݨ[oÄNÈŒuQ*{my:äÍ Ää—^³ëÎÙT!I׺d?ß³çÀÇDÐÙ{nëK»YeP'EhЪáãnã¥ú“7_{oæü¯ÀHrÁé‹Åú‚Y“ 6á^Îg&•°Ä›MN¼äÛ;QSzJé,œñªÑj{íãŰ2k¹‘wˆ™í=àõF9.%Á"ã‚ ©6 àÆ3ñÖqÝ6ðä ÍAô“•Õt´”_É|îºB'Íý_=9ÕB#Š…Ù 4߇voÿpåï~ÁI‰ç‘×ЂcÏ(žÕ ¿Ë]nWu` .ÎÇ×ÑFGRšS ì–"¼!ÁK‹Š”šéÑOÓÍÊâI¹q±Þ fnÖT &bâ÷h ø¦3Œà£Š€G^åà­eÌÖçÁ ›oevÒºJn3÷l5üoÒô.“®½öëÇ_<ø<§„—5IÄ[—¸ºª£"€v:FJð¡ÇË>nÙÂÿ-sùôsž)WÜ÷Ü«Ÿ$ïß]?¬¯–Û?Gø —šP ˜S@Ë‹I—ÒîÛ_óÝ™¸O Z3J 'eÊýŒmÄI _qÔAK9gsÍTz'{íXUðpY×Xãùm`lw>øJ,½°ÁFÿÝn/äJö”ò“U”ê*£eU•;–/]¸kãºÝÜH䳈 nx&ñ[Žfâ™ †Èo‚î´Ù‹q!Ü:Á¸H(ÆPBGÙ·ò_¿*}À›Y¥ØÕl£‹±&¨™Å;1›ç†4Œ ©–p &·Ë?ðÁªóú… ãEÀÃv3³‚¶ÛÄZ´Gú²·5á²™¨5rlÂc~Júý|Ê?x‘x¤¦³‘+ÕîNŠ€$àRŽ>"ñÄl]½rÓþ¼-‡'ͺv¦ÛíšÎ^’øƒ¬'Zbõ”d«g®Æ-lþîȃ¬øKàoÿc‘¾Ý‡ ؃ÉÂqW å;Óé„;™\µ€ëLàL3VQ_sõ1QŠ¡ºžÉ‘jukÛåtê—¹Ìú6ãp­R³ŠFSlʤMÉ×Ä”æ”[‰ÖÁ¥,žCÀ‹¿<dÌG¾9m‚ýââý5¼á`\Ýx®‡ÕTN'Jì*Àb Œ',);¥š=ljb°1»%˜œ;"ÉúùDë?EÀ#«æðò1ÎÿÚ#XMЮ•´ÆQc÷„—ϾaüêO¯á¥æDjÂÛž‰J¡#!€vŽìL¡mƒù \ x-ÿíyû‹¹£úô0hˆ%!15ÖbI2™xVY'Íã1%9l¬Ód£o–ò˜¸“nƒ˜° Gð5$8™F;^fì2«*Ì1ç˜=ƒ¨Q'œÂžÍK¬½â‹úX+âºZ<º×Hge u§8W©=¥êxeZy~Uœ³ïˆÔcœÛm5{܉ÝãõMW·Äº“ñtŒN£±:˜º\F­ªº<µú˲ŠìiZÏñ#™_ñÄSlòäÛn·6ôüâß>W¾ó‹R. "Äá™Ä³‰gÏj[E’9¬1úå>~øà¶n}ú- Ä…Œ¼£\㜇,ö‚TnC ¶“a÷€E¶JŸÊ¶#OÀ>þ‚GÎÄwžÌk‹ÑM@Œ ÄÄHw¤-Ätîä‰Õœ‡ùgUÛŠ€GVu Ÿ˜x oèðóYÅ‹ÎÒ0Ž`zfN÷é|ˆ–)µ'xå£Ìé(é8È1$ˆÈ·|%9wÚµý/| ïQœ‡EžÇ›Gæg¤]—Ûw\æöýÌÙ¿„ûî3bLÖÛÒÒîO0}äû¤Ý¹ñ­¢¢E.i€‚B™2ÍiWÜ5Ô2äò±Y}²#cŸÏ7›)9öLR.–4wEÑ©ê#y»J7-ÚU¹u»‡S—PÄðLÿ C[bǧ™L½x¿¯3!Ï©òxÎØì›7•Wl»àr[ðÌñ³õš'eÒü×>úƒ%©+21%wÉív×ïÿb;¹ÿ·ÇþpûxÌOðl‚,ãº`žkA¾yíÚµiÝÁ WÍ,áà?Éþ¸‘× …µàÚXŒMKˆu»:ÕG<Ôµ<¦ MCûv²$1Yà<•D’€„øoã‰a ßþº@ÞáŸá.±±qã⯑=ÙÍ••¯±ëÅsŒKX䮌ŒQ3R’þó÷šhšc]yùÿ{õü…ua)@™8KNÑ™wž:Á§,‰Íè—1û[“b{¼Â””5ŽÚŽC|ROKÿIXnëzËÏ çò¿,ÝüÁš’õoÊsÚ²f·Ú‚””£¬ÖÙé1¦©l'ïëÈt¹Ÿ;ép,[ZRü鯲Êpiäå{W>[’€Ë&Ûé·zÜq`å´´¿û¾fŠý6?†ö¤ŸÈåþ×Àgv.0ÇØóè°ãò>Ú°™CþÈëêÊʲ²íëWÿËpù•ß ÄÅÇýH· Ä<üEο–“¶ã’˜íÚñë Ncêu¥¸?èj»Íà;fäO}‡ÿp·©V% WI¾%YjUJê¢NƒÈ^öþ$„$šoÿ§®S<Ã?íÞíÞ.fsãÀCzá/½Å›aù0>ݧ×ìáqq¿æüP\AzÙ—e•üã̹møIb¿p„N¾öøQ.Ó›–î#-Ys¿;͔ݦђtS9Œ¤ÑbãûÆöÈí›Õ#÷¾¬¹Ot–žþ´|çgŸ]Xú§£5§4{57--ûú´”ºšLóLš–x!7hG™Û½|¯­zÑó§Ïm¨t:ÃRoåÀs%ø™o²)ÊKìEf0ï‡\írxv|v÷÷>>ü_Þ]­þ2 oä)]Æå­Z¾)53k!ï»=0﫱7úȸtQ¨‚qy±om0.ÿ@Lg þmå’÷VÔÔ êH¾“Q Q+J9U‡¶XøÓ)>ÞᆞgŸW¤@¢„—¯…@S€œ `- ‚gN¶)© îðÏð”óHKü]†ç"§Î?1‰ ËäÂ÷|0ÛlzŠ+DàìÖéôªòÒ;Ÿ9yò@S• ǪOì c»ïu.ËëÉÃçXÒç|c¦1µëõš9îJ¾'sì@sFïi3¾öHÚ´û÷zl%K*oþèôkßÏoì>F¥¤Ä|?3}VvlÌqšv9ã#Û£ïî(í¾èr½ùjIÅ{ïŸ> Ï"í)òÝ+Ÿ/+¹à9“ÛžýOŒX?ý?GFŸºXñ+~ãÇÑ« ×õ²6üö6jÃQä%5àpu(F·>÷Í®¾eÚÙ­ ââýÂÅ«íUÁ¸€x@`ªƒMoð-#ñ_:l¾Ùôǃ@L§Žüã£×þó2' ÷“¢ŒíõOðöB¾á|Ñb½­¶áãjoÛÀI⬰n;ž)ù‘–$𴧆ÚT‡o[ÿêÙíþDv—ZÓN/®(}‰·122ÉÈÈÐ>ÊÉþ¹Eó Ç;ÚŸgsÌûñ±“'å¾hY—î^ZÆË«\ÞW»Ì|<>ù²ysÈb½IÓŒ×pcóysaö2Ô`Mš8rö’FÌÚíqÚßwœ/Xtì…q¯‹†‘c4Ý£i·sÃK ¼N«Ø¡ëot»^·{?¼HDŠà™‚`-<_r‘ûp­¼¯ÌCòÜÞw<ºû?AÖ†Ëçm“ˆ1²‚޵I8qòL™öUÄ•ˆ‹ÏñiÃåMˆÉû ZphÂy6rR¦Üì U6;Áí#¼pŒM÷¸Köæmþýºe-çáp?)òmÏŠ€·'ú çÝá?Ü ßvØö_¹„-S•Q‡B@’|°!í©Ã?ÃO fÌ4™¿£ë^<í¹WNž,ñšÿìßß43ÉúO¶a¾Û—ƒfXwJ3ÞøÀ]ž}QºqvÙ³Õ¼¼ÆÅmÐ÷×Ä2¯çhÈ·òýÎæç#ãl†1\‹‰ÎB~2á—kO^utµ!áðŠì¤²ºó7¹ztƒá ¦ýç°nø`n^^H;Gm€Ï“ùlÉß ®÷ýC7„@޼¥j9ª…2x8׺ýyy{¦Ì¹~¶Çí¾šIb2“È&qáBî$`å“Àß¾p$Û_ã˜1!ê(16ìŠÞ]zòHÁ’µ-YX^Αô8&/ ÷“œlû‰"àí‡}ÔçlìÑÌãGã5Oöe+H¯ÂóÉ¢L{"¹v¢¸lÍ" Q|õŠ~wœùvþXöcËšv19=ëE> ä%$rfæLkÅ…³ïpâ³EžÈEÓ>0&¦,¸råJhE;”øÝTŽ7°ŒxæŒÕIæº5}~ /‰KÌyw𵄥OI!]~|M:•WØ»ää?-Ós6o.ìPÀøÝLˆ´áþZpÉ}ļªªÜþù»oÀ&|ñÑãû÷0(×bMH±Ä¥˜Œ&ßÄZ¿bªÍ¦Ð9ª°Ía³#úè‰üCy»6¯ßË—ÀÜ 4ßx0"$Ì’¤âƒ7£[œú“{«Jd¹ÿŠ:‘Jîÿn³®7F®C­&ÍZ¼…’ÿò[ræí$Ϲó¤óðQH…{Ò–{o'Ëm7PÙ?'מÁÊ®M¸«*…@´ -UÁ¸QOÖ–WûC×eË𠉜¾|tfå…sðç=¾6íýúxX[¸0d¤¿6¯öÝÚùD×Ê£<»íCªL– ›³GƯê5‘òºæ’ÝTëÐäHJÂòʈ[zð€ÿ­ÜAÑÞîyçàã#ö·ï„6÷hÃAð`^ù–¿ñ‘Cgvúqû¶m.ãæ<ÂL…×ê[ ´@$¶x†A¬9ð•“`AÀám °ÇñC¾ù^„G¬•Dx€y ±¦–}—&üä{TöØOÉ]Õ:³Ióè\n=&ªxúÏL¾ñÌ„N´+%üà2v“æ¦mÊ«ãzfmJT]¬è,?f.ªó @H…Ùbýs¨î‰gŸªJǧìád€Ìƒã$ü¼ß–íOÑÖírW‡\NžlqØ«o&M¿ßí²_ÉlE³8«éŠãÄbc×ë+ûLÉÐì²IÝññZ3ÒG°íÃæ—ì3{'“ñ…:u\2dm¸$† |þÛ ÊýhÛŸ6` ‘ØJŽ °²“bߨ/5ßòZÞý¢4àÑ_‡õï€':¤üûyªúçkdY0´Œtr®ÙHU/³‡0·›ñ‚;ñ7?â>¥‹Jî{”´ØŠÿúÝ3yé9Ò¾øª~çC‘vü#÷“çðqÒ²3)nö ª~õŠ›7[KþÛÓdÿ|U¿ýYû:™G ç>ª‡ó¨úßoø4ì±W_Aq·ÝH†´rç¥ò_?ÏNÃ*û-wÌ#JJ"×ÖTùç—H/GÇ·V´„xN§Š*~ù%ýõwµÔ–B@!v<ºç‡2SžPõ·^kÖ„ÄþúÈøQ£Xë»”¿¸]‘Ûº™ø?Ìäû2ÿŽ¸ÎŸ0z¹=8ÕwpÇ#YД:7ªg-Âk‰dÿ÷·ß~aÑ Ô”™ §ÑiÈxµá z †þ$„„“3;µûQ¾ÿ¶ p• pö'áÀ¤k,8Ö¡4ß|?B—Ht°µ¡K&YyªßZ$ÌC¬•œû‘cÕr¬\K¦áƒ©ê¥7Ø|sØßkØsWþþo¤¥&“õ»_çP`›Ésú,’“)æî[I/.¦êÿ½GÎ]{IËL'Ë-×QÅs#ÏEþþ2‘w:Bö?'Íd&ë¿Kîýdÿl%™† $ë÷¾Aö/בã?È”ÓUoCZªØ_Íåp²9Œõáû(vÎ ²½½¸NmxΜ§Šßü‘ ÙAÑ€×I[ýP(šÀá1cfîžTs…]ÓÌ¿oþÕÍ?ù¸=ž÷ù-|Œ³šgÅÝÑgëöEÍO%zÎ<5vlFµî¾‹µÝè.k1 à'^A烷>a/õõhK´¼<ŸÀL…¼‰¥³“ñ kÃAü$ ‡Ä¼ f'rÁ¨*&mB°­äÒÈÆ-ñEû– p–Ûòø¥SŒÂ3ÂJkn‘ë7“mÑRqzìÌé3v9–¯!WÁ1±„Ù]è5A1OÍZëmä:Á3éyÁþØË&°|‰8W³ÄQé?'½Úknâ9u†_K:¹vï÷ÇöþÇüb7C]²Ès¤ÌœxÌŒ©¤WVRåïþ"®‘_ä鿀}í&‘†sËvŠá< ¸/µ¡P´+¹$¿œL _ê³y3¿‚+lâr;GÓ|…i´Œ`4ÜÃ7ÝÐ'/oMpsjßÔôùóGÍâIì÷W‘û~9šý8·(w<2ÿeïÉÿí—W×ÍI#Å$ã6Ãùë9ÝÛ'pvt͸ԆŸ¾PñK]Óc Œ|ωüÝj©ßp4yBAi« „[.¼©8@h†È×Ör¾r‘ûš‘Tôž¢xôÖÝ%K‚-E/-#-.Nþ¬»f[nsî¡©Ž½B*·øíÒ#ÛwžkïùöíôÛ€i‰õÛˆ4bBmwЄ!èßÁ¹»'@!zL yj,„¦ ƒ´ã&›íü”·q&’u'‚ié yæQˆüîÅ¿8ÙƒK1¥ž&}š:¶… Q#!hÌ}@æ0yëRÁ²1]ñÌøÀ÷€BÞ‹XêMßåZUTØþƒø6ïDi „+‹Šþþ:;›ÜñÀàTƒ€ÕÂ^Du_™YˆðSë[vW“Üé(Ò g¨ 쨹pØií§ ìdöë×—ŠÝp¶ZåKÉ*šï`W—ŠdCþ«°r%×YpK3>kû(ú£kCiÃëL~¼bÿE ôêAûÇ%M[IÙç¤LL[ÜF€fT“ƒÿ°‡'%-Ž/¸Ðûž«Ü.Ì™Œ‚Ñ $+·ˆÚƒ Áèöï.>å`—èLy”†ÜV¹"¦$ òùKË#Â@.Då šÂø"|Ýï*ÈŲ{¬\MôzˆÔIª“JDé½À~tÂ’¸ [û¢îúZ¦?ÝûþÔV­]/ä‹Ûj3ïÇ碰$¡oÂæ­›=m'-°STÈ–¢–µÏŲpÿ[`‡´ø‡ Η}¹QÅêb¿hOØ# 0O6}ÖvݺœòqÚ;RmÆFà`0 _Àì~Æ[‹Z|¿%ãhÞ_fålÃñ-SÈg‚ðÌþ‰]þçØO¾ÏPðÖxÂ5à*¢ p[Žx«ý'{pÔP»ª$ßT ú —ó*/»ªóî¶£Š|d³géÄ÷ #šâ\+ µ`fïá§â]øð]‚3æIˆk·¡!¬²WNü4GÀŽúâ~MJÉ7xƒ|×—v÷ïÕI¶ÊgæŸ{Ÿ'Mí ©;-£-|+1ñ“¸ôô êiØV¦¯ŒŒc¿qQ&™fšÖÁÏ4ã\îWbým#'àõW¶¼gÈ Þ¿® à›ËÃB;„JR"FÛW×S’2Öµq‘l<Ÿ‘y 73±Ç$çß&0ñŠ~w+Í9õC}ú4“eó(µs’À^Q÷ÝÝúû;§ Í/))ã„J«Ù6 2ðE~5®gýà¥ñõãL}%ãUiÃ™Ìæ´}û]úé‘]Ov>R?¤È{¡8ׂJÛ@„[ ²Å–S` â\Ä Ûc 3ç‘›–‹á¡½{i¢ñ÷áÑ-VÓPÙ&ß„Ç7â{P 5% "÷Gã§ÖÑùy¹hªòƒØ×­S·®ÄÙôv¹©éù–#PÈ4(ÒRœ?^í‹(Ji¿«\`'U» ìØ3ÆR¢{ýH·Å‡ŠÁŒ6m¿0Qü$!®íІf¶æHÆggåäÞì”™Š‹ÓEû'vsôFRÛZ?æÚðZ‡¼AWÈ ¸Åo1™²‹BB„œ,h´X4ØBÿjáXd0%E…§«jy«õëÉÊ/e?ÈèÛ³· ³»Ð÷1j»X{>ÔŒãþXœ*:63±çáŒ^=FaAüº-Yö4|‡#P°üvCÕTïˆÛ”¶„TÖî„JØÑ‰×·Þ˜æo*î´©ª<¸Be@桃7ãׯÑH¾¯Ç/c4A¾\@“íxb~  }&&g”ÈÔ-åÒ4´ƒ´ñ1dj£,úS#WìÆaªb3®A2εá íê­›þr^7¸WW+;y4kmXTÔ“Mb s6çuÕåLሊ+áÈþ½4h’KýU™=aÓ–TŒ¤ßs™½»÷EmÍHÌ4s¶R3•ód(aÿÉHìþ70a^€!ð‡22¯&ã[Ž€ß!P¶pÌÊB­Äî|íñ§v2»¢àÃ8ÿã$ß+ IwNáþ$a^Ùó¡b~\†@} ã\Î/g_#À ¸¯v­|…®Zúã®¶]ºæînÛ ¸{*'×ê­×©Gf3›.ìLÝEÓn;'¼k ñ©Û6a¢MH&&fõëÕÅÜ+ò½HÊQf,DÀ¿«qïj“¹ø’ñ¯pùì÷[oÞVo–Ï®$ž ^!P˜{z<’PúÚC†Úñqm‚‹Þ²v>ÁrTÁAú]`È--׬9§ž«Ë-’îp|$ÜCÚnƬ¥îž øðexwÿ |‚¾ÉˆóSßäu‰±¿“q® ¯Ë«§~×Í ¸väK}õg;sêÔ·b‹–ÿ>ÙZ+ý©¦úOK¿ÌÆM…£ûvÿŽ­¶9üT¬iëT(Ó®ÇÄëñ3õ³‡2ü IøÃ8@_gÿLÍX8Ábƒ h+ŽZqö^BBûŸšm¨S€òDšD€L022<«6Ndðª«×o ìü„$öžº^`‡´òH¼‡àK4úì¶ÝŽÇ'_—uµÝY °OE½0Ÿ¯ ^ žoý•Œ«ÚðË^ßõMà5£.°¤¬¢É=¥x~m4ÄĆØi ÷™´³ I\õÓ÷‹˜Åœ»¢s/Ù"ñ÷$wdF¸~²©äüÚ_ùË@Ómå§qÂÛ­€„ÄŸ¶uQBúÖáz£+⋨ÝÛW®0Ɔàñ™™23{¾˜xÉgíréùG@ ù…º€˜Ò¦ÙqM›æJ³hÂ39cž.æÃvâÛÝY—d$ݱ{NÉìÝ¿„ÙþDuŸ#ùÆû·5ÞßàÒð×Åßrk|BÚ¶ÿpò}Q‚ÞÞ#2¾ÿÙ®ßâòðw†„5n¢p7¾ø|2(ç¶•]q.ÎTTOíA›ñíÞØ>¥ý¬í*öv³ª-o÷¤Î›G…ôÄvÎBÅ #Ø,*Ë2yJù½ó;»b«-€GràÜŒ:ÞU5²tS[óòÎìÞšþÖ™Ð0XÖµ·ÓZÚ:ª'Ü¿´uÿ|YTT@“,Íe?"ℳŠ9îºb×nÉNHÛ2쎢$^…šòEøÙžêPò±ŒÉ3ŠÁzµâs3úõ(ï\MÈ·:Fµß‹çÔfàµü†°l™I=®i[ºÀ[‹¤Ä¾º¥ ÿm›¾u¼«Zôšêr&ž f ï+8 ô7`ÖCÀä©HèâÊå`‹ÂãºÀИ„ôm÷ħný]HNvûå¼\ÙüÀ)ü‰Œ“6_&é˜4ïG/.ª6|¬Sæ‰<\µª­K€úD܈(šÖ­Xº!¬QäçÐá!€þlØŽTAo³ó:mµ\C­!Í7‘ï=Í[ wnûeëºÕ;O‡áK@z}ß¼e5–»úè€î-L&áQ4‹,?Ï¡5 ùSfcÀ£‘ˆ§t3Û¤ºhXKñÀðøµf$^¨ Jñ‚Õd¶³Uiiý{÷´Ê¶Ñ™9Ùö¹Žý@m÷Y¼/¿ÄæOâ7mÙæÇ÷ëŠf*roÂÝ…ÏÎa(³ µu¤G ­ÀY'ÞTH>xþ¡ž'r ¦â ç3؉´áÜo¸*!¾­ 5àxCäã/¤¦Šx¼S!$Í,Äü/ÿ拲öïù É$û|À™lšy¨Âç³WË„×[ù{ñwbjKÜ’œö _ÂÙëËTB«õÛŽ£Ö/)>¡}+QDM MäTÞ/"’œ»¬²%ý`b÷å‡úô¼Jó«-BÑóË¿j3ol•à—üúÏ^Pàgöw›­XQ¨W·ÍèÓó›ÌV#)jFéÈœعï9Õåófúòo„‹Gm÷º·ðö~Û£L”VÚD÷»¿á 72>:&¦mÚ¶ÿãäÛ›ð~YDÆQÛ¼P5SÁkr¤–ÌTmøÄ®Ïqm¸÷eßJôX.0!Jl`ÕB‘›)ÄœY¤ÁϸbÑW?õºbpNÏ~þªÿàðø3§ØeÇ §³¹Ÿpˆü|“«AòvB.m%%›ÿXöÝŽëv:àXP†'áJøª&(¸ë»@¶âXúçôCÔAXí‹HthåÍÒÀàz›M¾þ`¯ë%ÿ—žþ›åÛæØFrÕÈC=@M5nV´Š¥})пçL·öîy7³ÉŸbÚJ,þ„›ãR·®u&¿'iXr²xè—‡Ò²ð&SÑ­X–_"Ê)@&ž˜o>m±yóÑÒH®ô.’öÊ4ã ±¥ Éϸ–4ã\®ýëG‹-Äg¥gaÜä”—p¶ø4)X×ø£_Ô„k)ÏzTç¹iÑÈh©ct‘Qø#-NdPPH£þ׿®uBûÁ:ƒ!ŒÔUA& )) 6«Ç²Ä:ü*˜$£Ci‘r«©$ÿÐþ}k6¬øumIIMä¡ihéº<‹¿\üǽبZpÜ­ÝÙ·gwf•_@5á$ã$o{@íÎj&ÂKmk¼Ø+ucçÑ3"m…Ö3¨Ÿ2gz’G+$ºQ½ËYÆ%M[I™æ¤LLÛÚ N™‘h³Yo@YÇ2ÅàMª,lS[õ»RO³¼s=u6kå)2ž %âZm+*j^\˜@÷%”Ñ„ó.v˜õz»æãòË5Y’(ýöÑ´Óª-Ô‰H²5—m¦‡ñÖ(äÛ­*fÁ/NÅxþ{I`óêjÅZEö²ízl[k­Ë¾"~Þ<ö…üÛWwL‡_evàUêó8ÑSJ?ò”bÍëi9F¹ ¬ØlËÏ4²âŽmªïûvÙ3vD’t˽qïk3o'“6zÈXek*~f`ö´É_h?oÉ„¾L ½°BÂ#pKK¡Ó9™û¶ëÚ#®u»CB# a:5? +X­¸fhQq~QÁ…¼¬ýûöܵ´[¤Ù&rMšnÒzá&N?ZŠžˆ™¡ 8>ê.ILL03ÛsØŒ‡°åä‡b© “µú‰|ü”é÷ãÌÿÏu¢®·?»d[íÃPž o2`Nòx²½åÁ3H64°Á&¢Moïô#òM„œVosÔ‚«$Ü+2Ųý! §›–n^Gí7p"ÜDÀU[p"æ4HSz•¼ã®öBV¿ž—Y­,µ}·•op/ˆÉq·ÜúI]ù)—<;ˆYN¯GÒ£kÙÁÛŸË÷×{GÞz`Vh‘zêF=—ô”^o˜ÙéÄ1î.´HU:º ÅÌgîYóç{±yב¶Mwnqü¤–éöãcq>NXþ´MZÚ õ|n¼¶žÂ—„7ò²«Žü!èä:}¨C(\«Ú*êaUìP–ÑuòÄÿ½ò•„ûôùìe2n—Ký.ÖÃøƒ ¸ükº¬ø¡wkÉí옥³`±˜^XðZ ÉžÆpÍÏÔ7o'^#kØ \Ž[ø üavÊ䑨¡ðéMD 4€`౯41“ˆ8‘n•|Ó1ZÚ$Z@IDAT'3”ŠÜk²Å²µÔk‹¶Žœl¿‰hÓ7?"á´¥c:ï777¶U ôéW€{ ‰Èà²S¥Z8„IOƧ§ÿSî¼ð…@Ÿ4ý[ýv¼½‡×d:àãæ¸T¼·˜•ª÷¦þžÇŸ½!$<â{$ßÂ-[7ÔçûΡûÞÛý¹G¶§YK¸#uÍÙ„3'£*ˆÇø2-,Aš‡_€Öx¯fKy!ùzQÒ-IÈ; ^sxYŦ{\AC(àÏ6È„3´ï¿åÓY¯,Ç>× óŒÛåßR¿Sìø-—¿‹ljñH"á¬à|Þ_¿ÿÉÞ/”dÞO¼zÁŒ›’ò:¡ íÁ¾É(® wñj¬:¹ò‰£IN?"ÝêV%ßDÀIžêwëu ò­þH®jÁ‰lÓM¬niŸâè¡î—!³wÏ;ñ¾zm®Û8vý>P|–Vât<ï‹}Ò|ƒåôlÃ\Ùðé9Ó’Hcá7Á[L‡Óý¦ïÒ¥_øÀ›†m**hòÀú¿%¾P–BNî’&×8“N¬ZøÖY„u¸,ü¼ðo£W®¤¯Yš ãÆ%Acá@¤élÓ[÷'qÍ·{â!MøíFØòŒ‘9™›vvúãE$kzf×jp…ŒãÀ³(ÔtzIbñš_BÅÓM¯ š#qÍ·ëâ"MøêÂñr¾•³nñ²;wn$KuÌv½ÀZÊá­ñ„ˆ× Ê48“† ¬9ëÈVÔk…7ì‚hD" ®£}3Ù5çá¼{Ð/ª«=Úo?µ¿*„áBpÒ€^ªæwý3ħnùN ‹è‚ø2~Y²›wá}v7NÞÜ>ÄÇ‘vÚW½£û˜ÌNè¾öGòí\è¹I/¾†nW_õ˜,IÍÈæ›“o÷&Ü?¸›âÚåã$Æ×EQßñˆ×þ'Z$ߨSÑÖHx½¹ÄÍ7'ßîÉžrvƒŽ­”Ëæ-º·‚Náϫ܄ê©)Øý™ØmDHXã&¨HÄ/ùð>XíÏ\*C™“ Ëÿ ·žMCO'1dóÍÉwMèVO¸u3þ‚R¤fô,ÅTôU¿Nä_y }{–:êÕ@$ßvc¡si¢zKXޤáK1H\Êý„{5ò»&—H%½%ÒCŠ~ªÖ[%aê£êm <(ÐVýÑ‹ŠúSÏQ¿q+W’9Í´cýúÍ7YM¯!ᾇ:…ÛpÜÌÎHìyoF¿c½å-…ü|ËEòXþ}2c7à@të>gZõ+¨Mõ<нEÚoú 4¼à„ˆpÏùL´4a•p\ß®sá¨ïNÆŠè§Õ \’OµBo'8á’ËÞCIѤUòs$¤åãXÔ;ø£çx=Ã+]ôGõ¦ÂX“eheÉ€hÝAà.=>áG^cNÇMÀ’>,“}Êß³9ŸÛ뜪&ÛPœ˜Ùü¡â'ë'ñóùç¬P–qÑžS裕žVšûœè{bÄhÅÐó‚Ћ#œI fÿÊ…ûZê5ІV¸ÄEv¨ʵۓƒzÝ9O€áy9þˆ@«õéãC#âÄÉé8:ý¹ÍjÄl¶ï3»ÿ/;1±lÒ¦?ö®NÛLƒ-ýH+§pÄ•À>IÙDrÂYÅܵ¸U¤Ú©!Mvw )2¡ç%…äbZ»¸ü=«3YËž©êË—ÖäïL\Jà ¸KpñÄí# ¬\iEÊIHƒqRn–Úb|¸=R Ö Gôj«žã[— ç% ôCÞp<¹„’§‰K_lc-ŽO¥×ùòTÊÕäWî+õ>ÓÚ5Àå_äA‚Ѹ˜Í,/[¼Çösfb÷©,9™?œGßq@à¸9›;)U¬ÝÉëËG Õúõgão¾u*ÀÉKJé âef]—Ù§ç•>o€ÿVPëÚ}¯îôð=!¦ïÙ§‹ê àÕ÷¡ð£OÑ/ŽâÇ£2«Ê,„CèÔçÁpE¿ª’¸s¾Öq¯¦‘µÚ"Îý®®¦9ÕG :=„uï'øŽ-˜E»«ÏàA¬Mb cÆ“°ëÉÛ€Ù¬s÷J,—µVq/WsùƒZk‡(($Öܬ| \8j{¢¶ýö8ìYõ2>í}wï[JÎÁ¡ôà¯OzAêâû¡qì•Ûù>Z[mÒZýÚVø Òw¯Ä>h,/’#Àð!9™žÄI‡z÷Øl“Ù¸‚L<’Ùäß3{<Œ6ãöI›ž×V¯J ÀñçQçôýÁxÓµ`Þ”Æ;nVTæ%¿Aɯ‚þ²üÄ# †…@ÄüwÀœ¶ŠÞÿ¤V-ðüÚ¶ùø)(ü`X÷@!"D|òäO ‚¹ÄæM¡ðÍ!ht)ÿèU0ý±Š.†à§Çƒ¾G…Œ›7¦Añ'_cÝ´Â6:àz%ïúˆ"À–qX)](PÎÞs+@XXÓ·Cáûó€å”ë¿„åAÁ´7 ìÃ×ÊŹvPn¦kY}ŸºLþžkêZ{ ,9Ù`lÝÂ{\ûwÀÉïæBáÐüŽ1Ðhàõ`+.‚¨!7ÁÑO^ƒóiÿ@Ôà› ÆéÂ"á¶ pdÎ+`+8á}®‚ÆCþgþøZ>€.CgAã–ƒ‹Û çð°oít°šKïãï„øÞƒ!8Z)k˯ãÀR’t>¡ïÿA@`œÉZ »V¾¨œw„êÌÑŽ–ž1SÑ)ÐÓ¸²ï({Ú¯·kÀë­hyÇ8Õ#—ºu±NÔ_‰&)'ÊRàÓÿ‹ÌÞ=&WŸ“ÇzÑh}b70\=Šÿ÷%Ød@Є‡;mÛ©Ó`ݹä3ç àÀôóoJ•AOŽVXù/Í[ÖQ~l´½)bÓ&šüœBŒ‹ç}röI0­ÝL³nKËø ?[¬XÏ!(xåm(œõ\y9 ,ÕXë:µ‡àg&€5ã±NËæ-@ä[l©œ7-û RÞ©M,†]ª™•Ož†‚ï‚l²ØÛÄwªF@$:æ¾'@.)†¬S  qS…\S޼´Õ`Í?y›þ†ÃïýG!åúFM 5š”äþñ#d¾ö,š§´…&CoS*!´[?h5ö%8·áO8ûÏr8½ìk%îô¯ßÀÑù¯+ûæÜSpüËwaÿÇh0 mø8åL¿¯‚ГAlÖäœ\¥Ù¤ /xå{ˆ„'2¯†’U4æbÓhU^L¿¯„|`……PøÚJ•F“º-µZk7)EXR·"iï % V‹ä[7°^€#³§+¹ MšCÌ=ÿFA P|x?0«Ì9' `OºßɶùÌI…`Ó‰ó[Ö@DÿkàäO ”xÑ`„ìÞƒ³k—+Çd—M¡øX†]ËMm Dæów¦BÌH4Á¯'!º+&$§?¶®WÒП„„<¤]WȘ5¬çÏBîÊ_ ÝËøâÝÛvÜžÎq§Ñ aÑçJØÿr©Ö1Žï—Gàdær8¼õåd‹ËîFÓ« {ïH„w)çΟ¯Mç*ûÛ\§ýÇ™e¿ƒ ÎDÓ º€`Xõùí`3*ÇEMñ ïýsÙ•cú;´u.^_"!YÎ?³· Çö,„˜Ž·ƒÕtÍUè+Že!ºõÕPR '3ð™!'ë/hФ=3íò7-°Œ„>˜¯ßýkg¢üôÅH¾W)œ€W ?Éh8 «Â#H `ûWøJ=Ççö#òš°aÃî–-354j¿§Ö™J¥Dª)Fe[ñOß­s)Yzö¢m-+(©u¬€[6o­˜­Ü1™–?þ¶[>{Ĩ(`¥Ÿ ¥ÆQ`;xX¾c&}Ï. a¾ðwR.žÆ „Ï(ʼøbd¹p„”=$4¢¾¤ðÐnýAÙ:¾úùŸ r R^] {䀡X…ˆß­E ÷%EûNù +؉‡ué­\smK¶K/ h6SéØZÿ{ d}ð_EkoÏÄw*EàBÎ6ûysq.Hº û±ãŽ ê 'SF6ë 1ío¶G5J°ïŸ=±ÙN¾í'vÈ´¤óàW ²y()<‰f/Íí¦$dSJúËß×Q­®CP4\>r‰½$ônßwÜ!Í{FêÖä2H¼ùSŠˆƒ]×üµÄ±Œ†¶Ï xC“8ï/G È_8KLž ¶9ø€Uš„Ý’‘“½øè€·áäÍR#áJòòSž!ÀLν߰âRäO~¬hFR. ¡¢À̪޺\¬ýÀ€®ÅøÖ÷ÈSŠÝwØkÿ±ÇÉùù o‹¶áÙ¦ËgÎBÞè§*ÄðCO ógƒ­¸,¹'açcÿª2‹l6W”p4»u”bJ’¿}#D»ZŽž¤¼p‘¹ cóÖP˜Ѿ›lÐ)ùIQÆe¿ª¿À6í á…·áøïÂÙ5˪JÆÏ; `³8'&[A¶š!kçg¨]žáPÂÅ]ÙRrñ ’½6hÝV}6P±ûîwÇwöTÓ9o‚/ø‚Õ’¯hÀW}:¨BLå‡L¶ÀùSÛàÌ‘U¨Íw.Oå%5Œ³¥Oí†ÑWÞKŽG „´4 NÀ|mÂ/>á\o2ÿš3xpé÷ìjòó(ï"`;š d×-6‰RÌO¬{C-yàÝ·)f'ÚôîîR¥T;—§LÀÔ÷ꆮ/ú†¶lÙ‰ñFŠÏp1º±b’"À¼!ë‹F?âÃ@0AŒŒà.]BݽÄÅÇAP». ã/Îo^…¦-!ú¦{|më"¢€4Îβ1§`A;p2!‰rKiV4(8°C±E'‚nˆi a¨m'¿áû¶ƒ5óÍnDÉCf.a=//ÍçðohÖ ÚMþ@1k)<¸[qAHnÉ„…×(ÌÃlôZÒ¨E?Ð#”¯"§2Cí÷mÕòrüj¡ƒÐ¨ŽhÞÆé¡1`*ÌAGHVÅKIdL{ÞÜ#ÿ€!¤™â3ÜÚB1I¡IŸ9™+†mz<¢¸ n¢hÐíËvh¢&ùׄ*eGÇ EúîŠÉøq8¯?ä4t„¿„Oxô[U|áBÞ 4SᣩŠI-l-Ò”‰–Ÿ¾!“S4ÖùÓß1¶%?ñÈæCàø‡›Kg›cZºÄð0ˆüî|àNPìÁË2›W®Ó/+ hÔHˆXð.½_ø-Ó¡èÓo1ý¬sD|þ>èûõr¶JžÎMÎüözé€~µWCdÿ¡ŠG’lôLs×£Ðãó5Ðmörë}¥Ó¥ŸO_‡žOÒà²7A§™Ÿ£Ç”ìym¨?üþ$Î]¡ó;?@NÈ$/*rq!dÎz[%@—~F?ßk¡zY!;_lj¶èôBÑot˜>ßþ ër‘ä9¦çûÕ#@ÞG²v|®ü/\=f‹âeïš©p!gô¹õ+¸þ±L ¶1´yõ9ÄÙþz2iס¯îvý'¡-h^²÷ýYÛ?…ö—?ƒG­‡Žƒ^F‹ƒ6çÖ¿¦Lð$ßW?¼šÄ+VŠjVed¾ßí‹`èø]ÐyÈ+8‰ôgؽzJ¹4üàRÊßE—Æó3Ž@E 3±ç³2“gÙ»/@ºQ2\×rÓ¦ÒöíïŒKš¶’Z9'eÊ`Úº$ÌCKü…á/ Íîò¹w:˜K:ݳqú.¡!ŠçGÞBp©­(yDq'Ð=ªëÁKòKˆ¡Á8Ñ3_1OpŒ#Â@¦:Ñ›Š/Â×ý®‚̰Ôù¯§ÅÓ7úK ¢}QyÍeÚ¯‡'%-Ž5Ÿé}ÓÁ‹$¶æìn¤@¢+‡)®së­°í¸«69qLï¸OÚt¹Ì´Äñ¼º¯ C”hîÄ,åM£ÔIäÅWaIÛÛá¹ý³·fàÛÐsFK×@9ù7 <Ñ{Pð<_A¡”+éñ>E›kÙzѼ„|{ë ¡`.>‡i˜ËõÓ=ªëÁŠ™iK}`˜‹Î^Rv@PNÔÌW¼©Ţ“ÜjÆç†aMáh8z>jÏï¼ö$f?‰?­ÉßÞ+/Œ'JY\n‡”ïp8ŽħmyÍQ÷ôÒ§<ƒ^%6ÓŠÜ~ýˆ„òPK(þ¶+, CÄÛ]òMÍ®’|S¤ÍròžJ&[)ç}D¾©j* €2 ?ßy$q‡|S9Õ‘oŠ·^È»„|Óy"Þ¾$ßTå qGòM±dgm.¾” —ÏYõQUä[)'ÿš‹ˆ÷^Jìé<¹2¬.¸K¾«+³>Çq^Ÿ¥ËûÆð4Gù$xI¸¬…$<ÏZ²'f’6˜ŽG€#ÀàpÜ@€p7@ãY8 „ÍÛæ# £jÂQ)w…ÙTü=zMÑ7$°¯¤rü5°î×Vw-c\Ö6¥µHêG(wÞZ \þ¾—†ãý¯5ù{µ÷œ€{N^G ~" pžQ{‡þ‡e0ëç,9¹¡ñ¥‡õòŸ43êà@+ÑX:ô[fã¦ÂÑÈÆõ²ÃµÕ)ÂpÌÍÉùÚãR×—µÕçê±_¹§r¾>+œæ_@œƒ®êT„¡‚åÑ£‹1Ý[ê}¦µk œüs,m…3ÖÖUwŒÇÔˆáG8Í<ô&Vž«¸Õªükì+ 8w-ž–#À°#ÐdíZ\òLw’ðSÊIÆ[l–%õtÉzUC ´"…yí¯?¯M¦¼{ÉI§ÀÀÿ\C€p#üpö]îêÅ?W ˜Š¹kú.µÚå ¶¢Æ6wMËÁ²W'äÁ=;ÂP¶˜ÎýóËO?a)toiñ¸Tþ6sîö’›e+ãòwGú„á‡ÚïN™‘h“m×cE­™Àb€PŸWÊ+àp\FG| ´J–$J¿}4íÅ4g V®´ë×od‰Õ´pB&ê/[ Ö/cÃÈu¡³åh<‘BQÉN‘ˆ;"Nƒ1l[ÿOúþ­é\?|ˆµm‡+3»7 Ãç 2™XHI‘`°Yñ°a“¤c´b(-²Co$èºñÂÑ}»_·lé/……há |/àð$\µN¾+½6ÿýûšÝ›6î½|Ø7Ûâ®=Ò:6œdo´± K`- Nö(K0‰zF«†Ò";$«Ù”dÏοÖ/_º¢¨¨ “¨äË_®§äŸ£o«È?_5ŒÂyA/˜œü-,€ÑŠ¡´ÈŽ*û¬ÛW¯ÿí׿‹‹ ‰xÓW“?6Ù;ÁgDrò|ãqËñÇA`OÒ@ŒŸ§e`Â)$àÙX)ÎG€# 1ð!‚<ïÛ¦¨ÂA€cxß¾ÓBßâýää‡I3Yc8Ô§× ²Íö+=p)1’ïÇÒ¶~XcF&—4m%?'eÊ`Úzˆlñ¦eðBð†¿ü…ã” Áø£8}û®=Û´nß±c`HH#C``¸NÒÖÒ‰¡æ’\ºœ_‘^É2 ŠÞ øâeµZLæâ’óÅEgŽØ·sïÖ´ýX‘mUóE$ŒÈ t‘ Š?h¿«½:öHlÛ®C—À ÆÆpN®:™×ÇÜPkAk1åkv¡.è¸Mè%F3Áj³–˜Š‹/äÙ¿o÷¾í陨8’³?_5>œ•¿Q*j" »RÒ*\0Éôbê÷eoBÙŸ/.(8›µïÞý;¶ÆNÑýí×ò÷Öxâõ‡]1ظëp3ðX|—ãïK1H\úÑ‹/ž£x8m#ð茑r‘|#j¯ïÃß ø}[< ì gZž‘Øs&còó”2…È;»¶Ù´é3y}‘Æ[L‡¶áQ n@‰ˆ!§_ þ ø£4ª†\Ñ’ã±Ož»X®ËáΨ¨«»o§Œg­¶Ýïž8é­%|ÿRiú‘ ¼DÉÆ—È6ýˆx'2¦’o±Ù¨ókàòhÃeƒ¢þØI†sïí-|ÅR±§ÓuÔvÔ×kÀ#ù‹¢(Üq{ q8ôU…tê¬ùéá[ ÉPmš;Ûz+o'^7A7%å)&Ã,T|ìDõÙ³S’V»#9ž‡#À¨;Ê^–¿À|1~rÊ•8¤¿‡û¿âý=qδ¤·kjY||Û—33÷#‡.ø¶ÚÌóÈ_]ÏLQˆPR †H†J0‰HW´à¸¥ç¬JÂ5C¾±M°ÃT´êòÐ`…€‡IR‡æF£ùDI ‘ao"ÔdV¢j¾ˆ€SDÀ‹Ê~DÌ)žpô— ÊœÚ«öµV¯^ôCõ¥—T^¡m·³OÅ¥>^nË_’$áÁä Ã.’ïœsðçâ5â|Y¶fõ%Ôgù{,#¯ðRòÍÞ@XºèQ³“ÇÓÕŽGÀ˜==iõ¸äÙÀrz“Ù[xŸCM$WÊ4ìÕkòüì®ÃßàC½{=Ž0‘¯AXˆp9ç‘oÒ€à¯2® "¾¯ ø´¡©¸'X;a;¥»¢":¼süäo¸ïIP „Š‹J¾È½ áC/(DÂiKÇt^µûVóâ)͵ur ëtR»0]O©ìJZwÚ²;­ÔT9ªÕÇk@í›Kò×étâ“#lÏEà UV¹çaÉ—HïÙlV75Ê_·j?TŒê£ü=–×8™(šo$ß³S&¬Gš.Aæpü9ø2ì‘㓦Ýçx¿ï®É¥mzzZ&š¢àä$ê?63óòž?ǯےåïx”µŸXUJ¦'Žä›4àD•Ôîj# Âdi½ž8\.…ôàS/´Œ0Q„‘ãLé¦-ý(Ο4ߨ\{¨³k౎Á£"™=êLOü™c£¯ÍÔ­Uþ*FõñPûF[º†©U>‚ƒ¤)Z¦‡a¨*¤óEÂÂë¦[­f*£>êú#\ê£ü=’—W8M¸Ì¶›Kf'¤ùæäÛ#™ðÌM"@÷5jÂG¡&¼6p.Þ÷jš˜âÔL`· ùî†>õ³0ïMvÐýFÑÀ«À¤É%r©j½‰x«ä[s6àØ68`*^Ô#(p"íGº~·5onýñÄ òLàIPÉ„Š:«ƒ°z¬Æ{R—òªý m­\}ë¯ ¦+ C¾…-6›ÍgJ4óß®å߸q4ctñ»Æ¸Y}M*2 ŸOûÌð¢Ù\¬b¥áy¡!jŸTlÔ{¾¾>\†Ì+\ñv«Ø|s³—…À3püÒ„£Møh¾ªì¾'B]eÒÒ,‡ûöœ€_V×âÓ5°;õî18.uëÊ*3ùg 64°¨Œ´`D¸é§j½iKAÝ–Õñÿ[ÇNî~°qãÍ}°)ºg£#¯Bþ…‡ÍR_Úª?ÂFý©ç<¬FSÙ©Oµr n¡Cí÷uhÚ¥g†op‡&´j)”6î¢üéX•?méXM£¥v»ÛêK¥òïÖ­©4yÄé¹H¸î¤Â)¡Õ&|2yv£§‹‹ÏÔ' ¨{jPûE[õWŸå¯öÛé­W8¹ÄϘËù„K§qç 9~‹€bž”²µÚOb'ª%àÔÉ6›¶¬?Ø«;ºèXfì]6bDO´§Áª¾džú¦’oµŸš"ßj£Œ’n¡(ËDÀ¡±¨¿7sÕ8¶êLE¨¸xPœßdUûJdƒ‚ׯ”.Æëõ²ÜˆŠÆ/SGæHÖ¬tÄ»´æºÿwl“ŠKÝ·Ê·-Pû©ÈáÂ’1óû¹"°{¨ZzgB™Ížõ畟9£I™y†(§ñS?‰:¡bBZd­èç[ø²b?æpê'ÊýŽ÷½rÿ;ÑE #—„äõ‚ ®‡2Lp"[}HB£Ö‡^:4÷Ã5rIèk’~:A¸:kÐ ²-ö´Žývˆëƒ\]éƒ×¯t(~‡ˆ´^ù[¸Áò‚¼<•weùô5ÀX²œõý|I`÷ã­…ÄíÑ$á㟷=ªa™U&GwÏ5hù×ôð˜€Ó —xQÉä绦ÊxEM( |òºëÈ6@@™+À¥)Œé­¦|Ò‚ó AXò`úrý/µiL©û|«ˆ|ÿ:kêüä÷€Ú*T\|E~¸4kRp&GYFÛ•2xZß PÑ E.d·ù¦&^ª»pów‘«|•‘oüL1››ÔþþV‹ÇÜß:ÌÛë{ÆNNy`Üä¿ú,:vÊôѸ¸Ì&ߣӰk ·„h†2]EA6ñ耴l;@€›¡h@Õ4›ŸTNGUE¾‡O²=Ê¿RÔ±p4Z='à≯¤”ßÇNžÆ&¼<ýúʪyrúô¦?þå”›+‹wæ\r2Óœ ña3Lz:Ó~Ç4¦¼2ð›4m ú‹Fåê¥ÉýGHîÓ.ñ𠃦X‚Ëmö°Ö™ÝQ Ž×I´ÅRroƒBƒæf(ŠC“¸ù‰Úåä[CÂð£¦h޼ùvN7U¶±'¾þz°ÓœL8>iÚ#Ç­Óç8™Ü’18~J 7Mð‰¹ÔRÒ‚ƒÈÞU3ÉLæ²VÁ¨ã-7C©cTS=7?©œ:Œâä»Á÷óª9÷¹…Å4Q-ÿ|‰ý³»·ªDíá-Þ*KSå°à3þ=9¥•¦ÚÅã5tÆÐø¥¤X)A¯C‰‰ý¼V8/È#ÍPÀ&\œðçQ©<³§póOô~~N¾½iC*Ñ;+a6$Ä\ì«(°Ã2¦# ÿϸ)¯|=gÚK)Í0†¢“©Éè-´'íÓè6ô—]Ë—’“.7}zs(bsqA“áh§‘‡¶Ë}©L&‹Ÿàª¤£1í¤9)SV¨õŒ}9å\RãeQ^Ÿ=mòêù IÓÛ˜ðŽ^ÐßýaÊó{èüØ)¯\%Èò¬³–eÁòSuð‚¯æÅ:7 ûɉ¢̲^Æôƒñmn&®’˜¢¦qÜbú$Õ_á¹¥s§'MuŒ«¸¯×é&[,¶!¸²ÄGwSÅøÊŽm7å7eúÆä ØþöˆÕ^ìãll¿¹Òr«‘Ceéù9çh½f͹Œ^Ý¿ÆÔ£)‡ l¤wêÞ ô®”o>æiîj¾†˜žÌPX ¼A}GC°¡ä %nåÖ¼†ˆ…–úÌÍO´$ ZPìR?ß4á’Û|kKN>i ÈùÀqOËæpO¬!¿ BÛd€= ËÿKN^PC?9e$Ž|Ëñ—4?…B"2=ö¸õ¸²Ø‘^Ìxþ3$Ž'ñüVQSè'˸8 @+ü /W‡ îC¢yÚUßçx—¨Ž$;vp·¸ýt~Âä”ûfû _ÎaOâ/ _"¬`N?啎yq¿3æ½É÷OXöAlÏ3¢(-«F9¤Âïxp¾…¾ýÌÊÒ8žA,°ß¸ÔùH–ïuŒ«lß•v£ýøsL–q™ma«(ÁØÇ¹ˆÃ³ø4]±ìšäP1=?v Ò‡jœŒy—/ÝÞ LÈÆ_ŒZßV7C©›ºŠáæ'u…|åõrò]9. è,)sNxÚ_®÷Áò#±œ3~¼åÑä”16+[{ܲÿÌ2µªlO¼û®Á”sáu`°dnÊu5ºhïŠzÞø¤W†ðÒK¿bþ…¨UžŠÄ6óãi“ªåṿI­?ýæ›…g ¯GB?Ï=ôïäB>L~¬Ôm$† yþ뮻ú…<ÓëXÞ·sS’ì¤79ùïϳ­kÖ öåm¬¿¿ãlnÔÀOÆeuoøxjÒoj}·Î˜i+´þ†õŸ 0Ü–<é.sÅ4ÍVs jð¿Â¯÷1™½=.yÖŠ9ÉÏTLGÇ®´{ÜÌ™á¬Àò"fû û8F-ï©ä·Z à‹‹=8#‡Ù)ŠìyøŽk´MOO;Ø«N¨e‰x-mùÊê~ŸºVŠÓ©³ð…±)]|Ѱš1#3r%e©Êüšsñ¾B ¢ùI"-¾ƒî„x¨}8ù®}̵Tc§iŠœ&ËÓv¡# `üQrÒâH&O~岪ê4ŸÉ„D·jgË x,PD8}–V•—Î#AþM=º=üf#:.8[r-n@¯›Žuë-ò9Å# ‘Q<îEé)]~¾ùrÔ87Gâÿ>«!9yˆËûkîûïÿÎŒSÏÓý9ÿùñÔÉ•’o$öŒH¬­Ð¶-„èƒnœ5iR¡cþªö0(˜ :=ºob`-~«ª´®´[(@ìDH‚4DZ¼·“ŸÎÃÎ,q<ç©ËâûU# ð‹tÂgöÆ’(ý†$_”‹äÕúø¶j½¡¨f(U§æ1¾F œù‰ ,rT„øºn^þE8ù¾ˆECÝ£1„ÆS<Å€pOt!¿^ñ’Œ“ÌjCS”ÊÝ¢ýu<)Ûà5ÔïV¬Xþ2 ¾uuUJ’ôÑôBkÉ•”³ÜŠñõs’_8‚‡±eâ¦Pd½’."Q’V(å1–@[Io8¨;ü ¢¸ŸmVk[‡ÓX8ÛZîØñ›€šüðeârAŸ+ù™³ŽÑÎìS›¯—°÷ã€(Ï% íf`+ÅNÒ¨X?b{Ôñœ§rp,‹ïW€ ‡ïÔX¼V®õ•ÊGÓ^LÛáÖqŸZßV7C©›Ú޹Äü„ý‹gm·¥!×ÇÉwC–þž+cŽ%ʘrñ´[{œ€»›{™Èôƒ‰â$”NXSCCîKÌ1dAPÜ¢æ÷]I_sü¡÷# ˆs««ýÃäÒJƒØ—’|v³È„Ÿ”î©!Û%ÑÉÉ‚üØ”™cÌÌ’n±æ½‚/&%xñÛÓ¹Òn$zYˆ %r Üœ·Bç¡~9°Ÿò’ìåñªÀkÝNBJ–K7ãæÓªS»ÓBßâýlë±'dïKž=`Nòø"÷K«ÿ9¹7mȘ›ŸÔ­TòÄëA{K8ù¶CÑPvpÌ’-§ßÃñêHŒ®E9S]w1àpw‘ó Ÿ!Hz I¤ ¬2Ú6££AÇ ÓoÂs…ÌÂÆ:ž®l'Lž@*z‰¯lôH¢Ø#YÅIœÂÖ÷S¦¢ü¥®…ƒÌJç¡;ÒRó<06 ÞŽŸèK„ßOiË&ßõhsh{‰yJ¹th7Îȶ'Sf æœ´þw£ÛÃûäP„}÷ƒi/ìa&rúÇȾÝ;®´[Çd|Él`‘ow,£ôK|µã9pAåòñ—õâR5NŒÝÅ7,5 [rá‰ÅÐ5Ø,§øª/4UE 6¥®!ÓË…ì6M4¬5‚›ŸÔ­°9ù®[üµR»2Và˜í錿±ec‰ÇÍãp!t½€÷^zé4.©þ4šS|Všû"ßø8yRÎø¤éÓÐØ{ºÌ3£Ÿí/‘41«ÜM¶û }0gÚ ;(Ž‹A†™’^¹Iÿ . ÈÖZ– \""³“•ûT‚¤|1ž_X>ð¯ÒúÞû¿ÿ32ýa4Mù ë=ZóE6ËYfw ùËÉÉ5{0QËsÜΙ6ù+,óÔ<¾3nÊÌ jûÓÔ´ß\j?ý¸ußÔRã$RÁþeÀ•v0=) qÿ»þ"ö5Û é–›m,*Û:ýY¬Ÿ4õöàŠì™øŽ[´Ù¾'#±ûI|Ik†÷DÔá¾=»cAUÏ/p«–ÒLäÜ””‰x¿…÷ VcׄW¨ˆöÆré"IÜJu@ù$®¢ù ÷~â˜+-”“ïJaip'IócÅ4©ÊħçL»¸ÆŠ§`p ¸§º™£ù‰d¥³hg§L~ðy‰âí6 Ûh³È;¼ÎCr’(0‘´xJÐIß`šÅhc½å[ò -EŠk=rÙ‡”~ ZT4Db~¢æA ‹é<oV¼¨¸;mò7HÐE¥üÝ&&dX™y/Ö;$áNÇ|²8¿«oò¶õ0špK®ÏXšR!ÿLÀþUøb€Ñ®´ÍE[šÏqÕ¢Lóq&[×c™h^3ªb›œ•CÅ|üØ„¿Õ\øò9DÝ÷Åvδ¤·éAŠ/¢·ƒ5g· ¯eî ¥jlj#†›ŸÔÊ—ÖÁÉ÷¥˜4Ä3ŠÍ·åôz+JÉwÒÛÞÄá¢êÕÍRÑžy%eEÍÒ`Úòà]È`¡µ02"4 çµçŸÏ¯¬tšàXxÁÒˆ4¼•Å»sî±W^‰Â‰ˆ²¿ùLv¶Ýèß\w6´¼ú²„£ä½&Œœ‘CMeÔ·xoÞû™½zŒ•Ñ<ú²³$!mÙ‚û4`û¯Ã hEÙX4±ZŽ¿/Å q©¿]ó> ǯ#E .H0:aó¶ù¾®“—ßú’ëRS÷œ¤¯B ‚دϒ“h¢Èƒ/àäÛ—èj¿lòó]êj݇¦'7Í7¶z,}=õvë9÷6¢¼<Ž@AÀ›<£_v8ïa?A‡¼“HÀ›×ŒÉÉóÇ-ÇÇ+Oâ—¡–X7¾§ðkM6>K¬ª†h¸Ž+öïj9ààÅMivdãÜ/ Þ©áæÖ›¦µ=w0rÈáeÊœ—]PÉç])µÇ¯7=Ô^Gp]èøU‡¦ÒÞfjë²­]²7ßu@=æÛú‰j¹C”Õ’qÁ6T8ˆä¶Ç‚whò¾·l¾+"ÇmÀ+"Â9ZG ~Öƒ™‰= ñ!L¶àÙ‰‰cÒÒ*]ýÔ›+{°ÎÂ2g=:eF¢M¶ÑBU­ñƒ¿Pü5ø°«Eìi•€7ÏË ²˜¤"½¡Æ¯F 8ˆË;ÐD-âHx›Óê>ßúN¾}ƒ«•zÛš†¤;K‡‹ìxÃÏwM}ç¼&„x Ó‹`ò¢8O`;ÔÜ艰«ºÏ·Z@€¼¡”…Ro(êßúôÍû®Qm¿ñåô_|Ç —é@¾¹ŸoßÁÌK®NÀ+…Ÿâpêì¶Åè …4àÿ9Šm=öweðP À²ÕjJd¹V|«õñmõp3”êññFlEóÙ"o”ËË(E€“o~%h î†P;Ò ’h'Ý>õb¢Þ`¸Ï´QhŽ^áC´ÓTÿi  dvYÉb2ûí“×’S±õ4¡‰OjÒ è‹•u¤¦‰`%îh®Á7´&‘Ь¬Š ¥ÞPæ74|Ù_ÅüJW¾$ï'‰?áÊ—¸:ž#ÀÉ·çò¼‹'àÞÅÓÒTÒ- |kPëÞ]3—eÖŒf¾‡到 Á¨×“&—(±Xä¼ E,¿¨DÄee§ŽŸ’rÒj±½-eïÍ™“\ŒÅq"î"¦¾LŽ7ÃI»@pE2_ÖÅËv2Ca%ðåÄ—Û¡‡÷ˆˆ[¹5Ïõ’xŽÊPÌOÔ€{?© "·Îqòíl<“àÜÇ×P<‘j2+Ñßóø³×…ED~ˆîך_ß‚õé]Û¶‚ŒŠçÁ}”—¢ì8x6ïÊlºóà±™bSñÿF?ÿò˜y¯Ný ‹–Ý/žçô.ì¼Z“ÅPuŸoµ™¡d$v߈«•öC?Õz¹nÖq-¸Ä£,¾“ºç_ö¢¸ù‰ Ov8ùö=ž×—p­ª/Ñ­ºlÒz+Ä·Æžyáéðˆïc¢#¢Ÿ}àx|äP¡_—x@ò]u <Æ%K”°%Œ›5o*éôKƼôß§± ’ÿÎ뢾IŒ_}òÕ’~R÷ùV; ^´Kæ‹òxM0½Ÿ(æ'^+½aÄÉwÔ»¿ôšðÚ—”jr¢Çª÷=9éic`pJïNqÂý:×Î@IDATsÝ$µ‹mVû-j`5ÆÏºI"ÌAœõÈ‹ÉDÂé^à$¼Ž¯\³@m‚ÈNÀU0´´5ÀwjsT3õ˜oÝG€{?q»Êrrò]*üœ–à¼ö¥A˜+äûÖ‡Ç ûA6æö«„=·6©-qÖ„9a/ŠÒ«M|鬛ߵ%€*êA;}²ËW‚ ̨îó­vàÞP¼/ îýÄ»˜ròí]vÀÐ"4ÐÀ¸ŸïÚß™ZH´ê(ˆbkLO²â÷†3Ày9 ‘î% á\CƒÛŠf(¶ p«›©é&‘ù ºr¼ˆ_|Ç%yUF¾ñ¹1gø$Û£¤Pq©0ž˜#PËp’Q;€Îd[Lä éuÑáÁ~o~RlrŽ¡iX­v絃¸µD„‰‚(4ǬÜÜ ü¼‘o;Gcpç.0oTÌËp G3| ÝåV! 87?q_øU‘ïa“l8ùvWž³öàÜ÷XÑVMP.‰RQ¯wš€9‘ ¦/€#'s}ßÚjeßþ¶žšõ%<=ë+Húà{X¶nGµ¹æ-þõsØŸu²ÚtÞŠÜ{ø”˜,.G2!3ª&(NËÈåÊx†J(çû›±’Jñ“ÚA€›¡x$ n~â|œ|»‡Ï¥-8¯yÎDêè‡$?:úiEPS ޏf=}7 êÙ~^™¹çí –ë™Él…mû³ eÓF°igf¹8_Xm6øø»¿ /¿È½â™ýe‰ßî!èQ.ôýM/@J@-V啚€oën†â¾¸ù‰{Øqòín<—ö ,¾E€È¶úSH8Zdx€¯LÛ mÚ ç Š¡Cëf0l`7ˆkÑÞùjÄ4 ‡×öSzwât¼ÿíð𿮀ÖÍ£`áŠM°e_èt\Ýç2¸®%ÝWËÖ+d97¯þÙ²y $´Œ.‡ÐõºÚ;Ç·€ŸþN«Rã¼uÿÐëaø îðÅÒupÏ ýA’J¹íÑ“gaùºí°;3Óè@‡ç[5‹‚ w“gÎÿ®ƒ£§r!ºQæñØ/2g!­ûCû¢æ}œ»P‰ÛÀ¿†ôIá½oþPÚòÖW¿§?~§½­Îì`ùª¬Ô-·#t8o¥aô¢rANÀ½…«Ë!3Ôä*¼iÈ e««7E+æ'À¢”©‹ïàEÏCÕpò]56<ÆÿàZ¾Ú‘™JæhëUÌ…ÃmCzó÷ß´³^úÏV¥G푌¯Ûž6[©[å´½‡Ád±(ä|uúØqð8ŒºùJ¸ùÊžðß©púÜ%ß…ÂøeõØ•y n¼¢;4‹ ¯¡CÇOÃêô}ðÕòu€.üW“¬4i½»·k]Z‚ÕfÅzÙÓ-^™¦ïOŒ€Ëâc”óco¿JÙ~¾t-õðä½×C‹&‘ðõòõö|¤mÿrÙZè×%þuU/åEaÛþ£ ïÓ9NIwÛDuË {wTy¹˜'÷|ý±kÀeYæÜS@k#?7Cq en~âlœ|»†O­}¼JµßÝ:m¡JêhëµÐ ÉmÏŽ±DºwÊV´Äº¶EM°Y9¦Ê¶ìÍ‚Þâ’ºóàQèÖ®%4 …ö±M¡YãpHÇx5”˜¬0éÁápMßË 8Рž.·ý5å¤-Ï/4Aÿ®ñåâÔƒ|$ó{‡[#Ñ–d·„M»2ÔhØ—uJi³Ñ ‡vØŽ¢³Ò¾Âbd"ÁÔ£z&1@ÿnm´å¹yùö¼=:´VÚG&0±¨Ñß“y\Ѭ·BS mš7Æ2›ÙÓ»¸ãU¹XwƒNŽ~ ìÍ8÷ƒ«›¡¸.$n~âfœ|»†Oíp”:‘“bæà•šÉü#mO ¶P! 4I’~d§Ý)®¤í>Í‘`Ï9÷ ¿l˜nÿ‘“q,Rw¶·áTn©œN´m †rY^&½ý íÒùp¯>y’ý Hs !QÞŒå“ÙÇ“÷^‡õ•j±Õ|©{)mY²z+,_»]1¹PT¢˜ˆé¾,¾9üº‡üo+Út"Úö>©¼D|ú˵( 7Çrò Qx)?k[ è>µûVõoýû'I/JÿîS½o=7CqMÄÜüÄy¼8ùv+žÒ¿àüÿÙ»ø*ªì}敼ôBH'„Ð[ -4iÒ,€ êZ÷¯‚ºêZàf¥Ù]Û*XÖ¾*iJSé!!ô–BM…ôž÷fþçÜ—yy ¤½2ïÝûËdæM¹sïw§|sîwϱ|{YÌšz&û"j¨™ÈïÖ¤£ð-j»e…y høfÃnC«p€Ÿ7ÓPSu5j5Œï 3ÇÇ7Y{²V›§oo’²xº¹š6¹£u|tl/ø%,ÇÑòÞ˜€“ü$"´³i=é·7î:„ÖöÓ0bP˜2b ¼&É;ʵ¨AÎòvÕ/ËǑԓuÛw$¸ÅZÓ|À¤\Šž¹iÏQØu( nžÐ´Ö›üq—É·|lKæ$óhŽ|·äxÚ‡Ž¢Œ…ä3ÇPC~:ëâeR"Þõ以95âµì-?šïi  55&ë7žŸ[¿mÑí8'—¡\<.?¹2Fœ|_¾Õ±hÚ|éXutÊÚì$· î¾~T{ÜñY »A¨OÏAÏ+kÿDwq)e…ŽF·‚<9'‚Jß Æºc<¤BçDAÙµæ2”+·—Ÿ4'ßÍc÷8&œ€;f»Â]×´ûšMNè4ñÄ ô¢£9™ÜIr®ÄË‚ËP®Øj\~Ò4<œ|7 _ëØp Šc·/¯G@1*ÉNNœ€+¦åê Êe(õX4^âò“ƈsòÝ4.|­ã#À ¸ã·1¯!G@H×€+£¥®XJ’¡È;à‡Ô,yÙÙçL~"IÆÀ‚p.~UmpîÄÉ·s·¿³×žpg¿xý9ö‚€¹p´—fiu9H†R—ÐÍ) Ê#ÿvæ9—Ÿ4l}N¾âÁ9œ€;_›ósìIò2L†Þ†²ÈPÐ!‘¡f(«_Z.?iˆ)'ß ñ࿜NÀ³Ýy­9v‡€$˜p€R»+ /P‹à2”†PqùI=œ|×cÁ—œNÀ»ýyí9vƒ€$©<å ”ÉË|®@¸ ¥A£5Ÿ€°ã(`ÐWçKœ|;_›ó7'àÍc÷p8VD@ef%‰[À­ˆ}GŸŠËPê½L~¢–¾«ßêá\üªœ½TqGLœ|;b«ò:YNÀ­rC¯7À–}Ç`ËÞ£PTZÁžç^n®àåé :­ó5eyqœ¹pJ+«àçm)àëåã‡ö…ñƒû€Fƒ6Tž…€$eHZX™%‰pE5ÞÕ K2”*xvö˜96Æ7r[jÑÕSìvÑ0[.;~|¬D¿öõŒå 0çäÛ‘WÁf8k³Ôí;ñQ´v¹v'””C¯nÁ0%¡ôŽ7ÉurûN à£+«kàøé\Øâü¸ylK:wLK€~Ý»(¸VÎWt´–É,E%·€;Ð%@2”ôø˜½H¾É .åùÔªhª ÉOöí;6ä‹Y-™|¡›vr€N¾ ylŠ'à6…¿e'ߌïï7%A°¿7w œE†â òN¾ãžäµ°-|¦mñ¿êÙ‰|¯üm/ èß2–“ï« F'Cœ¯ï7íƒM{Ž€(šœk\åh¾Ù¦¨„sòùºÊË|î 8‹7—Ÿpòí ÷#¯†ÍàÜæMÐ|ޤŸg–ïÑ]àŽ©CÁEËuÍÍ£U¿…p"¼··$áSçÀ€±Í±û»~'¾dpV.¶T¸¼Ì玀3xCiìý¥ß+£õŒµh’|ƒðáÔy†UçîHíÇëb_pn_ía*Mm­¾Z·‹ÉNfMŒ7­ç -G€p êä ßlÜ••5ÌÎIxËñ³úž‚ÖDÀ‘¸p ¸ÕÀò'tô <å'q«söXU뜡Yòý´a.'ßÖi~ÇB€p;mÏ-8\N3ˆ[¾ÛØFd ŸqÍ (D1[’Ž}Ôp9JÁ´Âa.jµI‚"ÐEJLäÏ'+ànÕS8º ÅAå'œ|[õ.á'sø ΚH"¹$o'|Àeûˆð#ÿ